diff options
Diffstat (limited to 'auth2-pubkey.c')
-rw-r--r-- | auth2-pubkey.c | 615 |
1 files changed, 402 insertions, 213 deletions
diff --git a/auth2-pubkey.c b/auth2-pubkey.c index 169839b01..8024b1d6a 100644 --- a/auth2-pubkey.c +++ b/auth2-pubkey.c | |||
@@ -1,4 +1,4 @@ | |||
1 | /* $OpenBSD: auth2-pubkey.c,v 1.71 2017/09/07 23:48:09 djm Exp $ */ | 1 | /* $OpenBSD: auth2-pubkey.c,v 1.77 2018/03/03 03:15:51 djm Exp $ */ |
2 | /* | 2 | /* |
3 | * Copyright (c) 2000 Markus Friedl. All rights reserved. | 3 | * Copyright (c) 2000 Markus Friedl. All rights reserved. |
4 | * | 4 | * |
@@ -73,42 +73,39 @@ extern ServerOptions options; | |||
73 | extern u_char *session_id2; | 73 | extern u_char *session_id2; |
74 | extern u_int session_id2_len; | 74 | extern u_int session_id2_len; |
75 | 75 | ||
76 | static char * | ||
77 | format_key(const struct sshkey *key) | ||
78 | { | ||
79 | char *ret, *fp = sshkey_fingerprint(key, | ||
80 | options.fingerprint_hash, SSH_FP_DEFAULT); | ||
81 | |||
82 | xasprintf(&ret, "%s %s", sshkey_type(key), fp); | ||
83 | free(fp); | ||
84 | return ret; | ||
85 | } | ||
86 | |||
76 | static int | 87 | static int |
77 | userauth_pubkey(struct ssh *ssh) | 88 | userauth_pubkey(struct ssh *ssh) |
78 | { | 89 | { |
79 | Authctxt *authctxt = ssh->authctxt; | 90 | Authctxt *authctxt = ssh->authctxt; |
91 | struct passwd *pw = authctxt->pw; | ||
80 | struct sshbuf *b; | 92 | struct sshbuf *b; |
81 | struct sshkey *key = NULL; | 93 | struct sshkey *key = NULL; |
82 | char *pkalg, *userstyle = NULL, *fp = NULL; | 94 | char *pkalg, *userstyle = NULL, *key_s = NULL, *ca_s = NULL; |
83 | u_char *pkblob, *sig, have_sig; | 95 | u_char *pkblob, *sig, have_sig; |
84 | size_t blen, slen; | 96 | size_t blen, slen; |
85 | int r, pktype; | 97 | int r, pktype; |
86 | int authenticated = 0; | 98 | int authenticated = 0; |
99 | struct sshauthopt *authopts = NULL; | ||
87 | 100 | ||
88 | if (!authctxt->valid) { | 101 | if (!authctxt->valid) { |
89 | debug2("%s: disabled because of invalid user", __func__); | 102 | debug2("%s: disabled because of invalid user", __func__); |
90 | return 0; | 103 | return 0; |
91 | } | 104 | } |
92 | if ((r = sshpkt_get_u8(ssh, &have_sig)) != 0) | 105 | if ((r = sshpkt_get_u8(ssh, &have_sig)) != 0 || |
93 | fatal("%s: sshpkt_get_u8 failed: %s", __func__, ssh_err(r)); | 106 | (r = sshpkt_get_cstring(ssh, &pkalg, NULL)) != 0 || |
94 | if (ssh->compat & SSH_BUG_PKAUTH) { | 107 | (r = sshpkt_get_string(ssh, &pkblob, &blen)) != 0) |
95 | debug2("%s: SSH_BUG_PKAUTH", __func__); | 108 | fatal("%s: parse request failed: %s", __func__, ssh_err(r)); |
96 | if ((b = sshbuf_new()) == NULL) | ||
97 | fatal("%s: sshbuf_new failed", __func__); | ||
98 | /* no explicit pkalg given */ | ||
99 | /* so we have to extract the pkalg from the pkblob */ | ||
100 | /* XXX use sshbuf_from() */ | ||
101 | if ((r = sshpkt_get_string(ssh, &pkblob, &blen)) != 0 || | ||
102 | (r = sshbuf_put(b, pkblob, blen)) != 0 || | ||
103 | (r = sshbuf_get_cstring(b, &pkalg, NULL)) != 0) | ||
104 | fatal("%s: failed: %s", __func__, ssh_err(r)); | ||
105 | sshbuf_free(b); | ||
106 | } else { | ||
107 | if ((r = sshpkt_get_cstring(ssh, &pkalg, NULL)) != 0 || | ||
108 | (r = sshpkt_get_string(ssh, &pkblob, &blen)) != 0) | ||
109 | fatal("%s: sshpkt_get_cstring failed: %s", | ||
110 | __func__, ssh_err(r)); | ||
111 | } | ||
112 | pktype = sshkey_type_from_name(pkalg); | 109 | pktype = sshkey_type_from_name(pkalg); |
113 | if (pktype == KEY_UNSPEC) { | 110 | if (pktype == KEY_UNSPEC) { |
114 | /* this is perfectly legal */ | 111 | /* this is perfectly legal */ |
@@ -135,7 +132,6 @@ userauth_pubkey(struct ssh *ssh) | |||
135 | "signature scheme"); | 132 | "signature scheme"); |
136 | goto done; | 133 | goto done; |
137 | } | 134 | } |
138 | fp = sshkey_fingerprint(key, options.fingerprint_hash, SSH_FP_DEFAULT); | ||
139 | if (auth2_key_already_used(authctxt, key)) { | 135 | if (auth2_key_already_used(authctxt, key)) { |
140 | logit("refusing previously-used %s key", sshkey_type(key)); | 136 | logit("refusing previously-used %s key", sshkey_type(key)); |
141 | goto done; | 137 | goto done; |
@@ -147,9 +143,15 @@ userauth_pubkey(struct ssh *ssh) | |||
147 | goto done; | 143 | goto done; |
148 | } | 144 | } |
149 | 145 | ||
146 | key_s = format_key(key); | ||
147 | if (sshkey_is_cert(key)) | ||
148 | ca_s = format_key(key->cert->signature_key); | ||
149 | |||
150 | if (have_sig) { | 150 | if (have_sig) { |
151 | debug3("%s: have signature for %s %s", | 151 | debug3("%s: have %s signature for %s%s%s", |
152 | __func__, sshkey_type(key), fp); | 152 | __func__, pkalg, key_s, |
153 | ca_s == NULL ? "" : " CA ", | ||
154 | ca_s == NULL ? "" : ca_s); | ||
153 | if ((r = sshpkt_get_string(ssh, &sig, &slen)) != 0 || | 155 | if ((r = sshpkt_get_string(ssh, &sig, &slen)) != 0 || |
154 | (r = sshpkt_get_end(ssh)) != 0) | 156 | (r = sshpkt_get_end(ssh)) != 0) |
155 | fatal("%s: %s", __func__, ssh_err(r)); | 157 | fatal("%s: %s", __func__, ssh_err(r)); |
@@ -172,22 +174,11 @@ userauth_pubkey(struct ssh *ssh) | |||
172 | authctxt->style ? authctxt->style : ""); | 174 | authctxt->style ? authctxt->style : ""); |
173 | if ((r = sshbuf_put_u8(b, SSH2_MSG_USERAUTH_REQUEST)) != 0 || | 175 | if ((r = sshbuf_put_u8(b, SSH2_MSG_USERAUTH_REQUEST)) != 0 || |
174 | (r = sshbuf_put_cstring(b, userstyle)) != 0 || | 176 | (r = sshbuf_put_cstring(b, userstyle)) != 0 || |
175 | (r = sshbuf_put_cstring(b, ssh->compat & SSH_BUG_PKSERVICE ? | 177 | (r = sshbuf_put_cstring(b, authctxt->service)) != 0 || |
176 | "ssh-userauth" : authctxt->service)) != 0) | 178 | (r = sshbuf_put_cstring(b, "publickey")) != 0 || |
177 | fatal("%s: build packet failed: %s", | 179 | (r = sshbuf_put_u8(b, have_sig)) != 0 || |
178 | __func__, ssh_err(r)); | 180 | (r = sshbuf_put_cstring(b, pkalg) != 0) || |
179 | if (ssh->compat & SSH_BUG_PKAUTH) { | 181 | (r = sshbuf_put_string(b, pkblob, blen)) != 0) |
180 | if ((r = sshbuf_put_u8(b, have_sig)) != 0) | ||
181 | fatal("%s: build packet failed: %s", | ||
182 | __func__, ssh_err(r)); | ||
183 | } else { | ||
184 | if ((r = sshbuf_put_cstring(b, "publickey")) != 0 || | ||
185 | (r = sshbuf_put_u8(b, have_sig)) != 0 || | ||
186 | (r = sshbuf_put_cstring(b, pkalg) != 0)) | ||
187 | fatal("%s: build packet failed: %s", | ||
188 | __func__, ssh_err(r)); | ||
189 | } | ||
190 | if ((r = sshbuf_put_string(b, pkblob, blen)) != 0) | ||
191 | fatal("%s: build packet failed: %s", | 182 | fatal("%s: build packet failed: %s", |
192 | __func__, ssh_err(r)); | 183 | __func__, ssh_err(r)); |
193 | #ifdef DEBUG_PK | 184 | #ifdef DEBUG_PK |
@@ -196,17 +187,20 @@ userauth_pubkey(struct ssh *ssh) | |||
196 | 187 | ||
197 | /* test for correct signature */ | 188 | /* test for correct signature */ |
198 | authenticated = 0; | 189 | authenticated = 0; |
199 | if (PRIVSEP(user_key_allowed(authctxt->pw, key, 1)) && | 190 | if (PRIVSEP(user_key_allowed(ssh, pw, key, 1, &authopts)) && |
200 | PRIVSEP(sshkey_verify(key, sig, slen, sshbuf_ptr(b), | 191 | PRIVSEP(sshkey_verify(key, sig, slen, sshbuf_ptr(b), |
201 | sshbuf_len(b), ssh->compat)) == 0) { | 192 | sshbuf_len(b), NULL, ssh->compat)) == 0) { |
202 | authenticated = 1; | 193 | authenticated = 1; |
203 | } | 194 | } |
204 | sshbuf_free(b); | 195 | sshbuf_free(b); |
205 | free(sig); | 196 | free(sig); |
206 | auth2_record_key(authctxt, authenticated, key); | 197 | auth2_record_key(authctxt, authenticated, key); |
207 | } else { | 198 | } else { |
208 | debug("%s: test whether pkalg/pkblob are acceptable for %s %s", | 199 | debug("%s: test pkalg %s pkblob %s%s%s", |
209 | __func__, sshkey_type(key), fp); | 200 | __func__, pkalg, key_s, |
201 | ca_s == NULL ? "" : " CA ", | ||
202 | ca_s == NULL ? "" : ca_s); | ||
203 | |||
210 | if ((r = sshpkt_get_end(ssh)) != 0) | 204 | if ((r = sshpkt_get_end(ssh)) != 0) |
211 | fatal("%s: %s", __func__, ssh_err(r)); | 205 | fatal("%s: %s", __func__, ssh_err(r)); |
212 | 206 | ||
@@ -218,7 +212,7 @@ userauth_pubkey(struct ssh *ssh) | |||
218 | * if a user is not allowed to login. is this an | 212 | * if a user is not allowed to login. is this an |
219 | * issue? -markus | 213 | * issue? -markus |
220 | */ | 214 | */ |
221 | if (PRIVSEP(user_key_allowed(authctxt->pw, key, 0))) { | 215 | if (PRIVSEP(user_key_allowed(ssh, pw, key, 0, NULL))) { |
222 | if ((r = sshpkt_start(ssh, SSH2_MSG_USERAUTH_PK_OK)) | 216 | if ((r = sshpkt_start(ssh, SSH2_MSG_USERAUTH_PK_OK)) |
223 | != 0 || | 217 | != 0 || |
224 | (r = sshpkt_put_cstring(ssh, pkalg)) != 0 || | 218 | (r = sshpkt_put_cstring(ssh, pkalg)) != 0 || |
@@ -229,15 +223,20 @@ userauth_pubkey(struct ssh *ssh) | |||
229 | authctxt->postponed = 1; | 223 | authctxt->postponed = 1; |
230 | } | 224 | } |
231 | } | 225 | } |
232 | if (authenticated != 1) | ||
233 | auth_clear_options(); | ||
234 | done: | 226 | done: |
227 | if (authenticated == 1 && auth_activate_options(ssh, authopts) != 0) { | ||
228 | debug("%s: key options inconsistent with existing", __func__); | ||
229 | authenticated = 0; | ||
230 | } | ||
235 | debug2("%s: authenticated %d pkalg %s", __func__, authenticated, pkalg); | 231 | debug2("%s: authenticated %d pkalg %s", __func__, authenticated, pkalg); |
232 | |||
233 | sshauthopt_free(authopts); | ||
236 | sshkey_free(key); | 234 | sshkey_free(key); |
237 | free(userstyle); | 235 | free(userstyle); |
238 | free(pkalg); | 236 | free(pkalg); |
239 | free(pkblob); | 237 | free(pkblob); |
240 | free(fp); | 238 | free(key_s); |
239 | free(ca_s); | ||
241 | return authenticated; | 240 | return authenticated; |
242 | } | 241 | } |
243 | 242 | ||
@@ -261,18 +260,77 @@ match_principals_option(const char *principal_list, struct sshkey_cert *cert) | |||
261 | return 0; | 260 | return 0; |
262 | } | 261 | } |
263 | 262 | ||
263 | /* | ||
264 | * Process a single authorized_principals format line. Returns 0 and sets | ||
265 | * authoptsp is principal is authorised, -1 otherwise. "loc" is used as a | ||
266 | * log preamble for file/line information. | ||
267 | */ | ||
268 | static int | ||
269 | check_principals_line(struct ssh *ssh, char *cp, const struct sshkey_cert *cert, | ||
270 | const char *loc, struct sshauthopt **authoptsp) | ||
271 | { | ||
272 | u_int i, found = 0; | ||
273 | char *ep, *line_opts; | ||
274 | const char *reason = NULL; | ||
275 | struct sshauthopt *opts = NULL; | ||
276 | |||
277 | if (authoptsp != NULL) | ||
278 | *authoptsp = NULL; | ||
279 | |||
280 | /* Trim trailing whitespace. */ | ||
281 | ep = cp + strlen(cp) - 1; | ||
282 | while (ep > cp && (*ep == '\n' || *ep == ' ' || *ep == '\t')) | ||
283 | *ep-- = '\0'; | ||
284 | |||
285 | /* | ||
286 | * If the line has internal whitespace then assume it has | ||
287 | * key options. | ||
288 | */ | ||
289 | line_opts = NULL; | ||
290 | if ((ep = strrchr(cp, ' ')) != NULL || | ||
291 | (ep = strrchr(cp, '\t')) != NULL) { | ||
292 | for (; *ep == ' ' || *ep == '\t'; ep++) | ||
293 | ; | ||
294 | line_opts = cp; | ||
295 | cp = ep; | ||
296 | } | ||
297 | if ((opts = sshauthopt_parse(line_opts, &reason)) == NULL) { | ||
298 | debug("%s: bad principals options: %s", loc, reason); | ||
299 | auth_debug_add("%s: bad principals options: %s", loc, reason); | ||
300 | return -1; | ||
301 | } | ||
302 | /* Check principals in cert against those on line */ | ||
303 | for (i = 0; i < cert->nprincipals; i++) { | ||
304 | if (strcmp(cp, cert->principals[i]) != 0) | ||
305 | continue; | ||
306 | debug3("%s: matched principal \"%.100s\"", | ||
307 | loc, cert->principals[i]); | ||
308 | found = 1; | ||
309 | } | ||
310 | if (found && authoptsp != NULL) { | ||
311 | *authoptsp = opts; | ||
312 | opts = NULL; | ||
313 | } | ||
314 | sshauthopt_free(opts); | ||
315 | return found ? 0 : -1; | ||
316 | } | ||
317 | |||
264 | static int | 318 | static int |
265 | process_principals(FILE *f, const char *file, struct passwd *pw, | 319 | process_principals(struct ssh *ssh, FILE *f, const char *file, |
266 | const struct sshkey_cert *cert) | 320 | const struct sshkey_cert *cert, struct sshauthopt **authoptsp) |
267 | { | 321 | { |
268 | char line[SSH_MAX_PUBKEY_BYTES], *cp, *ep, *line_opts; | 322 | char loc[256], line[SSH_MAX_PUBKEY_BYTES], *cp, *ep; |
269 | u_long linenum = 0; | 323 | u_long linenum = 0; |
270 | u_int i, found_principal = 0; | 324 | u_int found_principal = 0; |
325 | |||
326 | if (authoptsp != NULL) | ||
327 | *authoptsp = NULL; | ||
271 | 328 | ||
272 | while (read_keyfile_line(f, file, line, sizeof(line), &linenum) != -1) { | 329 | while (read_keyfile_line(f, file, line, sizeof(line), &linenum) != -1) { |
273 | /* Always consume entire input */ | 330 | /* Always consume entire input */ |
274 | if (found_principal) | 331 | if (found_principal) |
275 | continue; | 332 | continue; |
333 | |||
276 | /* Skip leading whitespace. */ | 334 | /* Skip leading whitespace. */ |
277 | for (cp = line; *cp == ' ' || *cp == '\t'; cp++) | 335 | for (cp = line; *cp == ' ' || *cp == '\t'; cp++) |
278 | ; | 336 | ; |
@@ -281,50 +339,33 @@ process_principals(FILE *f, const char *file, struct passwd *pw, | |||
281 | *ep = '\0'; | 339 | *ep = '\0'; |
282 | if (!*cp || *cp == '\n') | 340 | if (!*cp || *cp == '\n') |
283 | continue; | 341 | continue; |
284 | /* Trim trailing whitespace. */ | 342 | |
285 | ep = cp + strlen(cp) - 1; | 343 | snprintf(loc, sizeof(loc), "%.200s:%lu", file, linenum); |
286 | while (ep > cp && (*ep == '\n' || *ep == ' ' || *ep == '\t')) | 344 | if (check_principals_line(ssh, cp, cert, loc, authoptsp) == 0) |
287 | *ep-- = '\0'; | 345 | found_principal = 1; |
288 | /* | ||
289 | * If the line has internal whitespace then assume it has | ||
290 | * key options. | ||
291 | */ | ||
292 | line_opts = NULL; | ||
293 | if ((ep = strrchr(cp, ' ')) != NULL || | ||
294 | (ep = strrchr(cp, '\t')) != NULL) { | ||
295 | for (; *ep == ' ' || *ep == '\t'; ep++) | ||
296 | ; | ||
297 | line_opts = cp; | ||
298 | cp = ep; | ||
299 | } | ||
300 | for (i = 0; i < cert->nprincipals; i++) { | ||
301 | if (strcmp(cp, cert->principals[i]) == 0) { | ||
302 | debug3("%s:%lu: matched principal \"%.100s\"", | ||
303 | file, linenum, cert->principals[i]); | ||
304 | if (auth_parse_options(pw, line_opts, | ||
305 | file, linenum) != 1) | ||
306 | continue; | ||
307 | found_principal = 1; | ||
308 | continue; | ||
309 | } | ||
310 | } | ||
311 | } | 346 | } |
312 | return found_principal; | 347 | return found_principal; |
313 | } | 348 | } |
314 | 349 | ||
350 | /* XXX remove pw args here and elsewhere once ssh->authctxt is guaranteed */ | ||
351 | |||
315 | static int | 352 | static int |
316 | match_principals_file(char *file, struct passwd *pw, struct sshkey_cert *cert) | 353 | match_principals_file(struct ssh *ssh, struct passwd *pw, char *file, |
354 | struct sshkey_cert *cert, struct sshauthopt **authoptsp) | ||
317 | { | 355 | { |
318 | FILE *f; | 356 | FILE *f; |
319 | int success; | 357 | int success; |
320 | 358 | ||
359 | if (authoptsp != NULL) | ||
360 | *authoptsp = NULL; | ||
361 | |||
321 | temporarily_use_uid(pw); | 362 | temporarily_use_uid(pw); |
322 | debug("trying authorized principals file %s", file); | 363 | debug("trying authorized principals file %s", file); |
323 | if ((f = auth_openprincipals(file, pw, options.strict_modes)) == NULL) { | 364 | if ((f = auth_openprincipals(file, pw, options.strict_modes)) == NULL) { |
324 | restore_uid(); | 365 | restore_uid(); |
325 | return 0; | 366 | return 0; |
326 | } | 367 | } |
327 | success = process_principals(f, file, pw, cert); | 368 | success = process_principals(ssh, f, file, cert, authoptsp); |
328 | fclose(f); | 369 | fclose(f); |
329 | restore_uid(); | 370 | restore_uid(); |
330 | return success; | 371 | return success; |
@@ -335,12 +376,13 @@ match_principals_file(char *file, struct passwd *pw, struct sshkey_cert *cert) | |||
335 | * returns 1 if the principal is allowed or 0 otherwise. | 376 | * returns 1 if the principal is allowed or 0 otherwise. |
336 | */ | 377 | */ |
337 | static int | 378 | static int |
338 | match_principals_command(struct passwd *user_pw, const struct sshkey *key) | 379 | match_principals_command(struct ssh *ssh, struct passwd *user_pw, |
380 | const struct sshkey *key, struct sshauthopt **authoptsp) | ||
339 | { | 381 | { |
382 | struct passwd *runas_pw = NULL; | ||
340 | const struct sshkey_cert *cert = key->cert; | 383 | const struct sshkey_cert *cert = key->cert; |
341 | FILE *f = NULL; | 384 | FILE *f = NULL; |
342 | int r, ok, found_principal = 0; | 385 | int r, ok, found_principal = 0; |
343 | struct passwd *pw; | ||
344 | int i, ac = 0, uid_swapped = 0; | 386 | int i, ac = 0, uid_swapped = 0; |
345 | pid_t pid; | 387 | pid_t pid; |
346 | char *tmp, *username = NULL, *command = NULL, **av = NULL; | 388 | char *tmp, *username = NULL, *command = NULL, **av = NULL; |
@@ -348,6 +390,8 @@ match_principals_command(struct passwd *user_pw, const struct sshkey *key) | |||
348 | char serial_s[16]; | 390 | char serial_s[16]; |
349 | void (*osigchld)(int); | 391 | void (*osigchld)(int); |
350 | 392 | ||
393 | if (authoptsp != NULL) | ||
394 | *authoptsp = NULL; | ||
351 | if (options.authorized_principals_command == NULL) | 395 | if (options.authorized_principals_command == NULL) |
352 | return 0; | 396 | return 0; |
353 | if (options.authorized_principals_command_user == NULL) { | 397 | if (options.authorized_principals_command_user == NULL) { |
@@ -365,8 +409,8 @@ match_principals_command(struct passwd *user_pw, const struct sshkey *key) | |||
365 | /* Prepare and verify the user for the command */ | 409 | /* Prepare and verify the user for the command */ |
366 | username = percent_expand(options.authorized_principals_command_user, | 410 | username = percent_expand(options.authorized_principals_command_user, |
367 | "u", user_pw->pw_name, (char *)NULL); | 411 | "u", user_pw->pw_name, (char *)NULL); |
368 | pw = getpwnam(username); | 412 | runas_pw = getpwnam(username); |
369 | if (pw == NULL) { | 413 | if (runas_pw == NULL) { |
370 | error("AuthorizedPrincipalsCommandUser \"%s\" not found: %s", | 414 | error("AuthorizedPrincipalsCommandUser \"%s\" not found: %s", |
371 | username, strerror(errno)); | 415 | username, strerror(errno)); |
372 | goto out; | 416 | goto out; |
@@ -424,15 +468,15 @@ match_principals_command(struct passwd *user_pw, const struct sshkey *key) | |||
424 | /* Prepare a printable command for logs, etc. */ | 468 | /* Prepare a printable command for logs, etc. */ |
425 | command = argv_assemble(ac, av); | 469 | command = argv_assemble(ac, av); |
426 | 470 | ||
427 | if ((pid = subprocess("AuthorizedPrincipalsCommand", pw, command, | 471 | if ((pid = subprocess("AuthorizedPrincipalsCommand", runas_pw, command, |
428 | ac, av, &f, | 472 | ac, av, &f, |
429 | SSH_SUBPROCESS_STDOUT_CAPTURE|SSH_SUBPROCESS_STDERR_DISCARD)) == 0) | 473 | SSH_SUBPROCESS_STDOUT_CAPTURE|SSH_SUBPROCESS_STDERR_DISCARD)) == 0) |
430 | goto out; | 474 | goto out; |
431 | 475 | ||
432 | uid_swapped = 1; | 476 | uid_swapped = 1; |
433 | temporarily_use_uid(pw); | 477 | temporarily_use_uid(runas_pw); |
434 | 478 | ||
435 | ok = process_principals(f, "(command)", pw, cert); | 479 | ok = process_principals(ssh, f, "(command)", cert, authoptsp); |
436 | 480 | ||
437 | fclose(f); | 481 | fclose(f); |
438 | f = NULL; | 482 | f = NULL; |
@@ -459,132 +503,225 @@ match_principals_command(struct passwd *user_pw, const struct sshkey *key) | |||
459 | free(keytext); | 503 | free(keytext); |
460 | return found_principal; | 504 | return found_principal; |
461 | } | 505 | } |
506 | |||
507 | static void | ||
508 | skip_space(char **cpp) | ||
509 | { | ||
510 | char *cp; | ||
511 | |||
512 | for (cp = *cpp; *cp == ' ' || *cp == '\t'; cp++) | ||
513 | ; | ||
514 | *cpp = cp; | ||
515 | } | ||
516 | |||
517 | /* | ||
518 | * Advanced *cpp past the end of key options, defined as the first unquoted | ||
519 | * whitespace character. Returns 0 on success or -1 on failure (e.g. | ||
520 | * unterminated quotes). | ||
521 | */ | ||
522 | static int | ||
523 | advance_past_options(char **cpp) | ||
524 | { | ||
525 | char *cp = *cpp; | ||
526 | int quoted = 0; | ||
527 | |||
528 | for (; *cp && (quoted || (*cp != ' ' && *cp != '\t')); cp++) { | ||
529 | if (*cp == '\\' && cp[1] == '"') | ||
530 | cp++; /* Skip both */ | ||
531 | else if (*cp == '"') | ||
532 | quoted = !quoted; | ||
533 | } | ||
534 | *cpp = cp; | ||
535 | /* return failure for unterminated quotes */ | ||
536 | return (*cp == '\0' && quoted) ? -1 : 0; | ||
537 | } | ||
538 | |||
539 | /* | ||
540 | * Check a single line of an authorized_keys-format file. Returns 0 if key | ||
541 | * matches, -1 otherwise. Will return key/cert options via *authoptsp | ||
542 | * on success. "loc" is used as file/line location in log messages. | ||
543 | */ | ||
544 | static int | ||
545 | check_authkey_line(struct ssh *ssh, struct passwd *pw, struct sshkey *key, | ||
546 | char *cp, const char *loc, struct sshauthopt **authoptsp) | ||
547 | { | ||
548 | int want_keytype = sshkey_is_cert(key) ? KEY_UNSPEC : key->type; | ||
549 | struct sshkey *found = NULL; | ||
550 | struct sshauthopt *keyopts = NULL, *certopts = NULL, *finalopts = NULL; | ||
551 | char *key_options = NULL, *fp = NULL; | ||
552 | const char *reason = NULL; | ||
553 | int ret = -1; | ||
554 | |||
555 | if (authoptsp != NULL) | ||
556 | *authoptsp = NULL; | ||
557 | |||
558 | if ((found = sshkey_new(want_keytype)) == NULL) { | ||
559 | debug3("%s: keytype %d failed", __func__, want_keytype); | ||
560 | goto out; | ||
561 | } | ||
562 | |||
563 | /* XXX djm: peek at key type in line and skip if unwanted */ | ||
564 | |||
565 | if (sshkey_read(found, &cp) != 0) { | ||
566 | /* no key? check for options */ | ||
567 | debug2("%s: check options: '%s'", loc, cp); | ||
568 | key_options = cp; | ||
569 | if (advance_past_options(&cp) != 0) { | ||
570 | reason = "invalid key option string"; | ||
571 | goto fail_reason; | ||
572 | } | ||
573 | skip_space(&cp); | ||
574 | if (sshkey_read(found, &cp) != 0) { | ||
575 | /* still no key? advance to next line*/ | ||
576 | debug2("%s: advance: '%s'", loc, cp); | ||
577 | goto out; | ||
578 | } | ||
579 | } | ||
580 | /* Parse key options now; we need to know if this is a CA key */ | ||
581 | if ((keyopts = sshauthopt_parse(key_options, &reason)) == NULL) { | ||
582 | debug("%s: bad key options: %s", loc, reason); | ||
583 | auth_debug_add("%s: bad key options: %s", loc, reason); | ||
584 | goto out; | ||
585 | } | ||
586 | /* Ignore keys that don't match or incorrectly marked as CAs */ | ||
587 | if (sshkey_is_cert(key)) { | ||
588 | /* Certificate; check signature key against CA */ | ||
589 | if (!sshkey_equal(found, key->cert->signature_key) || | ||
590 | !keyopts->cert_authority) | ||
591 | goto out; | ||
592 | } else { | ||
593 | /* Plain key: check it against key found in file */ | ||
594 | if (!sshkey_equal(found, key) || keyopts->cert_authority) | ||
595 | goto out; | ||
596 | } | ||
597 | |||
598 | /* We have a candidate key, perform authorisation checks */ | ||
599 | if ((fp = sshkey_fingerprint(found, | ||
600 | options.fingerprint_hash, SSH_FP_DEFAULT)) == NULL) | ||
601 | fatal("%s: fingerprint failed", __func__); | ||
602 | |||
603 | debug("%s: matching %s found: %s %s", loc, | ||
604 | sshkey_is_cert(key) ? "CA" : "key", sshkey_type(found), fp); | ||
605 | |||
606 | if (auth_authorise_keyopts(ssh, pw, keyopts, | ||
607 | sshkey_is_cert(key), loc) != 0) { | ||
608 | reason = "Refused by key options"; | ||
609 | goto fail_reason; | ||
610 | } | ||
611 | /* That's all we need for plain keys. */ | ||
612 | if (!sshkey_is_cert(key)) { | ||
613 | verbose("Accepted key %s %s found at %s", | ||
614 | sshkey_type(found), fp, loc); | ||
615 | finalopts = keyopts; | ||
616 | keyopts = NULL; | ||
617 | goto success; | ||
618 | } | ||
619 | |||
620 | /* | ||
621 | * Additional authorisation for certificates. | ||
622 | */ | ||
623 | |||
624 | /* Parse and check options present in certificate */ | ||
625 | if ((certopts = sshauthopt_from_cert(key)) == NULL) { | ||
626 | reason = "Invalid certificate options"; | ||
627 | goto fail_reason; | ||
628 | } | ||
629 | if (auth_authorise_keyopts(ssh, pw, certopts, 0, loc) != 0) { | ||
630 | reason = "Refused by certificate options"; | ||
631 | goto fail_reason; | ||
632 | } | ||
633 | if ((finalopts = sshauthopt_merge(keyopts, certopts, &reason)) == NULL) | ||
634 | goto fail_reason; | ||
635 | |||
636 | /* | ||
637 | * If the user has specified a list of principals as | ||
638 | * a key option, then prefer that list to matching | ||
639 | * their username in the certificate principals list. | ||
640 | */ | ||
641 | if (keyopts->cert_principals != NULL && | ||
642 | !match_principals_option(keyopts->cert_principals, key->cert)) { | ||
643 | reason = "Certificate does not contain an authorized principal"; | ||
644 | goto fail_reason; | ||
645 | } | ||
646 | if (sshkey_cert_check_authority(key, 0, 0, | ||
647 | keyopts->cert_principals == NULL ? pw->pw_name : NULL, &reason) != 0) | ||
648 | goto fail_reason; | ||
649 | |||
650 | verbose("Accepted certificate ID \"%s\" (serial %llu) " | ||
651 | "signed by CA %s %s found at %s", | ||
652 | key->cert->key_id, | ||
653 | (unsigned long long)key->cert->serial, | ||
654 | sshkey_type(found), fp, loc); | ||
655 | |||
656 | success: | ||
657 | if (finalopts == NULL) | ||
658 | fatal("%s: internal error: missing options", __func__); | ||
659 | if (authoptsp != NULL) { | ||
660 | *authoptsp = finalopts; | ||
661 | finalopts = NULL; | ||
662 | } | ||
663 | /* success */ | ||
664 | ret = 0; | ||
665 | goto out; | ||
666 | |||
667 | fail_reason: | ||
668 | error("%s", reason); | ||
669 | auth_debug_add("%s", reason); | ||
670 | out: | ||
671 | free(fp); | ||
672 | sshauthopt_free(keyopts); | ||
673 | sshauthopt_free(certopts); | ||
674 | sshauthopt_free(finalopts); | ||
675 | sshkey_free(found); | ||
676 | return ret; | ||
677 | } | ||
678 | |||
462 | /* | 679 | /* |
463 | * Checks whether key is allowed in authorized_keys-format file, | 680 | * Checks whether key is allowed in authorized_keys-format file, |
464 | * returns 1 if the key is allowed or 0 otherwise. | 681 | * returns 1 if the key is allowed or 0 otherwise. |
465 | */ | 682 | */ |
466 | static int | 683 | static int |
467 | check_authkeys_file(FILE *f, char *file, struct sshkey *key, struct passwd *pw) | 684 | check_authkeys_file(struct ssh *ssh, struct passwd *pw, FILE *f, |
685 | char *file, struct sshkey *key, struct sshauthopt **authoptsp) | ||
468 | { | 686 | { |
469 | char line[SSH_MAX_PUBKEY_BYTES]; | 687 | char *cp, line[SSH_MAX_PUBKEY_BYTES], loc[256]; |
470 | int found_key = 0; | 688 | int found_key = 0; |
471 | u_long linenum = 0; | 689 | u_long linenum = 0; |
472 | struct sshkey *found = NULL; | ||
473 | 690 | ||
474 | while (read_keyfile_line(f, file, line, sizeof(line), &linenum) != -1) { | 691 | if (authoptsp != NULL) |
475 | char *cp, *key_options = NULL, *fp = NULL; | 692 | *authoptsp = NULL; |
476 | const char *reason = NULL; | ||
477 | 693 | ||
694 | while (read_keyfile_line(f, file, line, sizeof(line), &linenum) != -1) { | ||
478 | /* Always consume entire file */ | 695 | /* Always consume entire file */ |
479 | if (found_key) | 696 | if (found_key) |
480 | continue; | 697 | continue; |
481 | if (found != NULL) | ||
482 | sshkey_free(found); | ||
483 | found = sshkey_new(sshkey_is_cert(key) ? KEY_UNSPEC : key->type); | ||
484 | if (found == NULL) | ||
485 | goto done; | ||
486 | auth_clear_options(); | ||
487 | 698 | ||
488 | /* Skip leading whitespace, empty and comment lines. */ | 699 | /* Skip leading whitespace, empty and comment lines. */ |
489 | for (cp = line; *cp == ' ' || *cp == '\t'; cp++) | 700 | cp = line; |
490 | ; | 701 | skip_space(&cp); |
491 | if (!*cp || *cp == '\n' || *cp == '#') | 702 | if (!*cp || *cp == '\n' || *cp == '#') |
492 | continue; | 703 | continue; |
493 | 704 | snprintf(loc, sizeof(loc), "%.200s:%lu", file, linenum); | |
494 | if (sshkey_read(found, &cp) != 0) { | 705 | if (check_authkey_line(ssh, pw, key, cp, loc, authoptsp) == 0) |
495 | /* no key? check if there are options for this key */ | ||
496 | int quoted = 0; | ||
497 | debug2("user_key_allowed: check options: '%s'", cp); | ||
498 | key_options = cp; | ||
499 | for (; *cp && (quoted || (*cp != ' ' && *cp != '\t')); cp++) { | ||
500 | if (*cp == '\\' && cp[1] == '"') | ||
501 | cp++; /* Skip both */ | ||
502 | else if (*cp == '"') | ||
503 | quoted = !quoted; | ||
504 | } | ||
505 | /* Skip remaining whitespace. */ | ||
506 | for (; *cp == ' ' || *cp == '\t'; cp++) | ||
507 | ; | ||
508 | if (sshkey_read(found, &cp) != 0) { | ||
509 | debug2("user_key_allowed: advance: '%s'", cp); | ||
510 | /* still no key? advance to next line*/ | ||
511 | continue; | ||
512 | } | ||
513 | } | ||
514 | if (sshkey_is_cert(key)) { | ||
515 | if (!sshkey_equal(found, key->cert->signature_key)) | ||
516 | continue; | ||
517 | if (auth_parse_options(pw, key_options, file, | ||
518 | linenum) != 1) | ||
519 | continue; | ||
520 | if (!key_is_cert_authority) | ||
521 | continue; | ||
522 | if ((fp = sshkey_fingerprint(found, | ||
523 | options.fingerprint_hash, SSH_FP_DEFAULT)) == NULL) | ||
524 | continue; | ||
525 | debug("matching CA found: file %s, line %lu, %s %s", | ||
526 | file, linenum, sshkey_type(found), fp); | ||
527 | /* | ||
528 | * If the user has specified a list of principals as | ||
529 | * a key option, then prefer that list to matching | ||
530 | * their username in the certificate principals list. | ||
531 | */ | ||
532 | if (authorized_principals != NULL && | ||
533 | !match_principals_option(authorized_principals, | ||
534 | key->cert)) { | ||
535 | reason = "Certificate does not contain an " | ||
536 | "authorized principal"; | ||
537 | fail_reason: | ||
538 | free(fp); | ||
539 | error("%s", reason); | ||
540 | auth_debug_add("%s", reason); | ||
541 | continue; | ||
542 | } | ||
543 | if (sshkey_cert_check_authority(key, 0, 0, | ||
544 | authorized_principals == NULL ? pw->pw_name : NULL, | ||
545 | &reason) != 0) | ||
546 | goto fail_reason; | ||
547 | if (auth_cert_options(key, pw, &reason) != 0) | ||
548 | goto fail_reason; | ||
549 | verbose("Accepted certificate ID \"%s\" (serial %llu) " | ||
550 | "signed by %s CA %s via %s", key->cert->key_id, | ||
551 | (unsigned long long)key->cert->serial, | ||
552 | sshkey_type(found), fp, file); | ||
553 | free(fp); | ||
554 | found_key = 1; | ||
555 | break; | ||
556 | } else if (sshkey_equal(found, key)) { | ||
557 | if (auth_parse_options(pw, key_options, file, | ||
558 | linenum) != 1) | ||
559 | continue; | ||
560 | if (key_is_cert_authority) | ||
561 | continue; | ||
562 | if ((fp = sshkey_fingerprint(found, | ||
563 | options.fingerprint_hash, SSH_FP_DEFAULT)) == NULL) | ||
564 | continue; | ||
565 | debug("matching key found: file %s, line %lu %s %s", | ||
566 | file, linenum, sshkey_type(found), fp); | ||
567 | free(fp); | ||
568 | found_key = 1; | 706 | found_key = 1; |
569 | continue; | ||
570 | } | ||
571 | } | 707 | } |
572 | done: | ||
573 | if (found != NULL) | ||
574 | sshkey_free(found); | ||
575 | if (!found_key) | ||
576 | debug2("key not found"); | ||
577 | return found_key; | 708 | return found_key; |
578 | } | 709 | } |
579 | 710 | ||
580 | /* Authenticate a certificate key against TrustedUserCAKeys */ | 711 | /* Authenticate a certificate key against TrustedUserCAKeys */ |
581 | static int | 712 | static int |
582 | user_cert_trusted_ca(struct passwd *pw, struct sshkey *key) | 713 | user_cert_trusted_ca(struct ssh *ssh, struct passwd *pw, struct sshkey *key, |
714 | struct sshauthopt **authoptsp) | ||
583 | { | 715 | { |
584 | char *ca_fp, *principals_file = NULL; | 716 | char *ca_fp, *principals_file = NULL; |
585 | const char *reason; | 717 | const char *reason; |
718 | struct sshauthopt *principals_opts = NULL, *cert_opts = NULL; | ||
719 | struct sshauthopt *final_opts = NULL; | ||
586 | int r, ret = 0, found_principal = 0, use_authorized_principals; | 720 | int r, ret = 0, found_principal = 0, use_authorized_principals; |
587 | 721 | ||
722 | if (authoptsp != NULL) | ||
723 | *authoptsp = NULL; | ||
724 | |||
588 | if (!sshkey_is_cert(key) || options.trusted_user_ca_keys == NULL) | 725 | if (!sshkey_is_cert(key) || options.trusted_user_ca_keys == NULL) |
589 | return 0; | 726 | return 0; |
590 | 727 | ||
@@ -605,36 +742,69 @@ user_cert_trusted_ca(struct passwd *pw, struct sshkey *key) | |||
605 | * against the username. | 742 | * against the username. |
606 | */ | 743 | */ |
607 | if ((principals_file = authorized_principals_file(pw)) != NULL) { | 744 | if ((principals_file = authorized_principals_file(pw)) != NULL) { |
608 | if (match_principals_file(principals_file, pw, key->cert)) | 745 | if (match_principals_file(ssh, pw, principals_file, |
746 | key->cert, &principals_opts)) | ||
609 | found_principal = 1; | 747 | found_principal = 1; |
610 | } | 748 | } |
611 | /* Try querying command if specified */ | 749 | /* Try querying command if specified */ |
612 | if (!found_principal && match_principals_command(pw, key)) | 750 | if (!found_principal && match_principals_command(ssh, pw, key, |
751 | &principals_opts)) | ||
613 | found_principal = 1; | 752 | found_principal = 1; |
614 | /* If principals file or command is specified, then require a match */ | 753 | /* If principals file or command is specified, then require a match */ |
615 | use_authorized_principals = principals_file != NULL || | 754 | use_authorized_principals = principals_file != NULL || |
616 | options.authorized_principals_command != NULL; | 755 | options.authorized_principals_command != NULL; |
617 | if (!found_principal && use_authorized_principals) { | 756 | if (!found_principal && use_authorized_principals) { |
618 | reason = "Certificate does not contain an authorized principal"; | 757 | reason = "Certificate does not contain an authorized principal"; |
619 | fail_reason: | 758 | goto fail_reason; |
620 | error("%s", reason); | ||
621 | auth_debug_add("%s", reason); | ||
622 | goto out; | ||
623 | } | 759 | } |
760 | if (use_authorized_principals && principals_opts == NULL) | ||
761 | fatal("%s: internal error: missing principals_opts", __func__); | ||
624 | if (sshkey_cert_check_authority(key, 0, 1, | 762 | if (sshkey_cert_check_authority(key, 0, 1, |
625 | use_authorized_principals ? NULL : pw->pw_name, &reason) != 0) | 763 | use_authorized_principals ? NULL : pw->pw_name, &reason) != 0) |
626 | goto fail_reason; | 764 | goto fail_reason; |
627 | if (auth_cert_options(key, pw, &reason) != 0) | 765 | |
766 | /* Check authority from options in key and from principals file/cmd */ | ||
767 | if ((cert_opts = sshauthopt_from_cert(key)) == NULL) { | ||
768 | reason = "Invalid certificate options"; | ||
769 | goto fail_reason; | ||
770 | } | ||
771 | if (auth_authorise_keyopts(ssh, pw, cert_opts, 0, "cert") != 0) { | ||
772 | reason = "Refused by certificate options"; | ||
628 | goto fail_reason; | 773 | goto fail_reason; |
774 | } | ||
775 | if (principals_opts == NULL) { | ||
776 | final_opts = cert_opts; | ||
777 | cert_opts = NULL; | ||
778 | } else { | ||
779 | if (auth_authorise_keyopts(ssh, pw, principals_opts, 0, | ||
780 | "principals") != 0) { | ||
781 | reason = "Refused by certificate principals options"; | ||
782 | goto fail_reason; | ||
783 | } | ||
784 | if ((final_opts = sshauthopt_merge(principals_opts, | ||
785 | cert_opts, &reason)) == NULL) { | ||
786 | fail_reason: | ||
787 | error("%s", reason); | ||
788 | auth_debug_add("%s", reason); | ||
789 | goto out; | ||
790 | } | ||
791 | } | ||
629 | 792 | ||
793 | /* Success */ | ||
630 | verbose("Accepted certificate ID \"%s\" (serial %llu) signed by " | 794 | verbose("Accepted certificate ID \"%s\" (serial %llu) signed by " |
631 | "%s CA %s via %s", key->cert->key_id, | 795 | "%s CA %s via %s", key->cert->key_id, |
632 | (unsigned long long)key->cert->serial, | 796 | (unsigned long long)key->cert->serial, |
633 | sshkey_type(key->cert->signature_key), ca_fp, | 797 | sshkey_type(key->cert->signature_key), ca_fp, |
634 | options.trusted_user_ca_keys); | 798 | options.trusted_user_ca_keys); |
799 | if (authoptsp != NULL) { | ||
800 | *authoptsp = final_opts; | ||
801 | final_opts = NULL; | ||
802 | } | ||
635 | ret = 1; | 803 | ret = 1; |
636 | |||
637 | out: | 804 | out: |
805 | sshauthopt_free(principals_opts); | ||
806 | sshauthopt_free(cert_opts); | ||
807 | sshauthopt_free(final_opts); | ||
638 | free(principals_file); | 808 | free(principals_file); |
639 | free(ca_fp); | 809 | free(ca_fp); |
640 | return ret; | 810 | return ret; |
@@ -645,17 +815,22 @@ user_cert_trusted_ca(struct passwd *pw, struct sshkey *key) | |||
645 | * returns 1 if the key is allowed or 0 otherwise. | 815 | * returns 1 if the key is allowed or 0 otherwise. |
646 | */ | 816 | */ |
647 | static int | 817 | static int |
648 | user_key_allowed2(struct passwd *pw, struct sshkey *key, char *file) | 818 | user_key_allowed2(struct ssh *ssh, struct passwd *pw, struct sshkey *key, |
819 | char *file, struct sshauthopt **authoptsp) | ||
649 | { | 820 | { |
650 | FILE *f; | 821 | FILE *f; |
651 | int found_key = 0; | 822 | int found_key = 0; |
652 | 823 | ||
824 | if (authoptsp != NULL) | ||
825 | *authoptsp = NULL; | ||
826 | |||
653 | /* Temporarily use the user's uid. */ | 827 | /* Temporarily use the user's uid. */ |
654 | temporarily_use_uid(pw); | 828 | temporarily_use_uid(pw); |
655 | 829 | ||
656 | debug("trying public key file %s", file); | 830 | debug("trying public key file %s", file); |
657 | if ((f = auth_openkeyfile(file, pw, options.strict_modes)) != NULL) { | 831 | if ((f = auth_openkeyfile(file, pw, options.strict_modes)) != NULL) { |
658 | found_key = check_authkeys_file(f, file, key, pw); | 832 | found_key = check_authkeys_file(ssh, pw, f, file, |
833 | key, authoptsp); | ||
659 | fclose(f); | 834 | fclose(f); |
660 | } | 835 | } |
661 | 836 | ||
@@ -668,17 +843,20 @@ user_key_allowed2(struct passwd *pw, struct sshkey *key, char *file) | |||
668 | * returns 1 if the key is allowed or 0 otherwise. | 843 | * returns 1 if the key is allowed or 0 otherwise. |
669 | */ | 844 | */ |
670 | static int | 845 | static int |
671 | user_key_command_allowed2(struct passwd *user_pw, struct sshkey *key) | 846 | user_key_command_allowed2(struct ssh *ssh, struct passwd *user_pw, |
847 | struct sshkey *key, struct sshauthopt **authoptsp) | ||
672 | { | 848 | { |
849 | struct passwd *runas_pw = NULL; | ||
673 | FILE *f = NULL; | 850 | FILE *f = NULL; |
674 | int r, ok, found_key = 0; | 851 | int r, ok, found_key = 0; |
675 | struct passwd *pw; | ||
676 | int i, uid_swapped = 0, ac = 0; | 852 | int i, uid_swapped = 0, ac = 0; |
677 | pid_t pid; | 853 | pid_t pid; |
678 | char *username = NULL, *key_fp = NULL, *keytext = NULL; | 854 | char *username = NULL, *key_fp = NULL, *keytext = NULL; |
679 | char *tmp, *command = NULL, **av = NULL; | 855 | char *tmp, *command = NULL, **av = NULL; |
680 | void (*osigchld)(int); | 856 | void (*osigchld)(int); |
681 | 857 | ||
858 | if (authoptsp != NULL) | ||
859 | *authoptsp = NULL; | ||
682 | if (options.authorized_keys_command == NULL) | 860 | if (options.authorized_keys_command == NULL) |
683 | return 0; | 861 | return 0; |
684 | if (options.authorized_keys_command_user == NULL) { | 862 | if (options.authorized_keys_command_user == NULL) { |
@@ -695,8 +873,8 @@ user_key_command_allowed2(struct passwd *user_pw, struct sshkey *key) | |||
695 | /* Prepare and verify the user for the command */ | 873 | /* Prepare and verify the user for the command */ |
696 | username = percent_expand(options.authorized_keys_command_user, | 874 | username = percent_expand(options.authorized_keys_command_user, |
697 | "u", user_pw->pw_name, (char *)NULL); | 875 | "u", user_pw->pw_name, (char *)NULL); |
698 | pw = getpwnam(username); | 876 | runas_pw = getpwnam(username); |
699 | if (pw == NULL) { | 877 | if (runas_pw == NULL) { |
700 | error("AuthorizedKeysCommandUser \"%s\" not found: %s", | 878 | error("AuthorizedKeysCommandUser \"%s\" not found: %s", |
701 | username, strerror(errno)); | 879 | username, strerror(errno)); |
702 | goto out; | 880 | goto out; |
@@ -754,15 +932,16 @@ user_key_command_allowed2(struct passwd *user_pw, struct sshkey *key) | |||
754 | xasprintf(&command, "%s %s", av[0], av[1]); | 932 | xasprintf(&command, "%s %s", av[0], av[1]); |
755 | } | 933 | } |
756 | 934 | ||
757 | if ((pid = subprocess("AuthorizedKeysCommand", pw, command, | 935 | if ((pid = subprocess("AuthorizedKeysCommand", runas_pw, command, |
758 | ac, av, &f, | 936 | ac, av, &f, |
759 | SSH_SUBPROCESS_STDOUT_CAPTURE|SSH_SUBPROCESS_STDERR_DISCARD)) == 0) | 937 | SSH_SUBPROCESS_STDOUT_CAPTURE|SSH_SUBPROCESS_STDERR_DISCARD)) == 0) |
760 | goto out; | 938 | goto out; |
761 | 939 | ||
762 | uid_swapped = 1; | 940 | uid_swapped = 1; |
763 | temporarily_use_uid(pw); | 941 | temporarily_use_uid(runas_pw); |
764 | 942 | ||
765 | ok = check_authkeys_file(f, options.authorized_keys_command, key, pw); | 943 | ok = check_authkeys_file(ssh, user_pw, f, |
944 | options.authorized_keys_command, key, authoptsp); | ||
766 | 945 | ||
767 | fclose(f); | 946 | fclose(f); |
768 | f = NULL; | 947 | f = NULL; |
@@ -792,10 +971,14 @@ user_key_command_allowed2(struct passwd *user_pw, struct sshkey *key) | |||
792 | * Check whether key authenticates and authorises the user. | 971 | * Check whether key authenticates and authorises the user. |
793 | */ | 972 | */ |
794 | int | 973 | int |
795 | user_key_allowed(struct passwd *pw, struct sshkey *key, int auth_attempt) | 974 | user_key_allowed(struct ssh *ssh, struct passwd *pw, struct sshkey *key, |
975 | int auth_attempt, struct sshauthopt **authoptsp) | ||
796 | { | 976 | { |
797 | u_int success, i; | 977 | u_int success, i; |
798 | char *file; | 978 | char *file; |
979 | struct sshauthopt *opts = NULL; | ||
980 | if (authoptsp != NULL) | ||
981 | *authoptsp = NULL; | ||
799 | 982 | ||
800 | if (auth_key_is_revoked(key)) | 983 | if (auth_key_is_revoked(key)) |
801 | return 0; | 984 | return 0; |
@@ -803,25 +986,31 @@ user_key_allowed(struct passwd *pw, struct sshkey *key, int auth_attempt) | |||
803 | auth_key_is_revoked(key->cert->signature_key)) | 986 | auth_key_is_revoked(key->cert->signature_key)) |
804 | return 0; | 987 | return 0; |
805 | 988 | ||
806 | success = user_cert_trusted_ca(pw, key); | 989 | if ((success = user_cert_trusted_ca(ssh, pw, key, &opts)) != 0) |
807 | if (success) | 990 | goto out; |
808 | return success; | 991 | sshauthopt_free(opts); |
992 | opts = NULL; | ||
809 | 993 | ||
810 | success = user_key_command_allowed2(pw, key); | 994 | if ((success = user_key_command_allowed2(ssh, pw, key, &opts)) != 0) |
811 | if (success > 0) | 995 | goto out; |
812 | return success; | 996 | sshauthopt_free(opts); |
997 | opts = NULL; | ||
813 | 998 | ||
814 | for (i = 0; !success && i < options.num_authkeys_files; i++) { | 999 | for (i = 0; !success && i < options.num_authkeys_files; i++) { |
815 | |||
816 | if (strcasecmp(options.authorized_keys_files[i], "none") == 0) | 1000 | if (strcasecmp(options.authorized_keys_files[i], "none") == 0) |
817 | continue; | 1001 | continue; |
818 | file = expand_authorized_keys( | 1002 | file = expand_authorized_keys( |
819 | options.authorized_keys_files[i], pw); | 1003 | options.authorized_keys_files[i], pw); |
820 | 1004 | success = user_key_allowed2(ssh, pw, key, file, &opts); | |
821 | success = user_key_allowed2(pw, key, file); | ||
822 | free(file); | 1005 | free(file); |
823 | } | 1006 | } |
824 | 1007 | ||
1008 | out: | ||
1009 | if (success && authoptsp != NULL) { | ||
1010 | *authoptsp = opts; | ||
1011 | opts = NULL; | ||
1012 | } | ||
1013 | sshauthopt_free(opts); | ||
825 | return success; | 1014 | return success; |
826 | } | 1015 | } |
827 | 1016 | ||