diff options
Diffstat (limited to 'auth2-pubkey.c')
-rw-r--r-- | auth2-pubkey.c | 216 |
1 files changed, 196 insertions, 20 deletions
diff --git a/auth2-pubkey.c b/auth2-pubkey.c index d42ba14b8..f980b0dad 100644 --- a/auth2-pubkey.c +++ b/auth2-pubkey.c | |||
@@ -1,4 +1,4 @@ | |||
1 | /* $OpenBSD: auth2-pubkey.c,v 1.30 2011/09/25 05:44:47 djm Exp $ */ | 1 | /* $OpenBSD: auth2-pubkey.c,v 1.34 2013/02/14 21:35:59 djm Exp $ */ |
2 | /* | 2 | /* |
3 | * Copyright (c) 2000 Markus Friedl. All rights reserved. | 3 | * Copyright (c) 2000 Markus Friedl. All rights reserved. |
4 | * | 4 | * |
@@ -27,9 +27,15 @@ | |||
27 | 27 | ||
28 | #include <sys/types.h> | 28 | #include <sys/types.h> |
29 | #include <sys/stat.h> | 29 | #include <sys/stat.h> |
30 | #include <sys/wait.h> | ||
30 | 31 | ||
32 | #include <errno.h> | ||
31 | #include <fcntl.h> | 33 | #include <fcntl.h> |
34 | #ifdef HAVE_PATHS_H | ||
35 | # include <paths.h> | ||
36 | #endif | ||
32 | #include <pwd.h> | 37 | #include <pwd.h> |
38 | #include <signal.h> | ||
33 | #include <stdio.h> | 39 | #include <stdio.h> |
34 | #include <stdarg.h> | 40 | #include <stdarg.h> |
35 | #include <string.h> | 41 | #include <string.h> |
@@ -241,7 +247,7 @@ match_principals_file(char *file, struct passwd *pw, struct KeyCert *cert) | |||
241 | if (strcmp(cp, cert->principals[i]) == 0) { | 247 | if (strcmp(cp, cert->principals[i]) == 0) { |
242 | debug3("matched principal \"%.100s\" " | 248 | debug3("matched principal \"%.100s\" " |
243 | "from file \"%s\" on line %lu", | 249 | "from file \"%s\" on line %lu", |
244 | cert->principals[i], file, linenum); | 250 | cert->principals[i], file, linenum); |
245 | if (auth_parse_options(pw, line_opts, | 251 | if (auth_parse_options(pw, line_opts, |
246 | file, linenum) != 1) | 252 | file, linenum) != 1) |
247 | continue; | 253 | continue; |
@@ -254,31 +260,22 @@ match_principals_file(char *file, struct passwd *pw, struct KeyCert *cert) | |||
254 | fclose(f); | 260 | fclose(f); |
255 | restore_uid(); | 261 | restore_uid(); |
256 | return 0; | 262 | return 0; |
257 | } | 263 | } |
258 | 264 | ||
259 | /* return 1 if user allows given key */ | 265 | /* |
266 | * Checks whether key is allowed in authorized_keys-format file, | ||
267 | * returns 1 if the key is allowed or 0 otherwise. | ||
268 | */ | ||
260 | static int | 269 | static int |
261 | user_key_allowed2(struct passwd *pw, Key *key, char *file) | 270 | check_authkeys_file(FILE *f, char *file, Key* key, struct passwd *pw) |
262 | { | 271 | { |
263 | char line[SSH_MAX_PUBKEY_BYTES]; | 272 | char line[SSH_MAX_PUBKEY_BYTES]; |
264 | const char *reason; | 273 | const char *reason; |
265 | int found_key = 0; | 274 | int found_key = 0; |
266 | FILE *f; | ||
267 | u_long linenum = 0; | 275 | u_long linenum = 0; |
268 | Key *found; | 276 | Key *found; |
269 | char *fp; | 277 | char *fp; |
270 | 278 | ||
271 | /* Temporarily use the user's uid. */ | ||
272 | temporarily_use_uid(pw); | ||
273 | |||
274 | debug("trying public key file %s", file); | ||
275 | f = auth_openkeyfile(file, pw, options.strict_modes); | ||
276 | |||
277 | if (!f) { | ||
278 | restore_uid(); | ||
279 | return 0; | ||
280 | } | ||
281 | |||
282 | found_key = 0; | 279 | found_key = 0; |
283 | found = key_new(key_is_cert(key) ? KEY_UNSPEC : key->type); | 280 | found = key_new(key_is_cert(key) ? KEY_UNSPEC : key->type); |
284 | 281 | ||
@@ -373,8 +370,6 @@ user_key_allowed2(struct passwd *pw, Key *key, char *file) | |||
373 | break; | 370 | break; |
374 | } | 371 | } |
375 | } | 372 | } |
376 | restore_uid(); | ||
377 | fclose(f); | ||
378 | key_free(found); | 373 | key_free(found); |
379 | if (!found_key) | 374 | if (!found_key) |
380 | debug2("key not found"); | 375 | debug2("key not found"); |
@@ -437,7 +432,180 @@ user_cert_trusted_ca(struct passwd *pw, Key *key) | |||
437 | return ret; | 432 | return ret; |
438 | } | 433 | } |
439 | 434 | ||
440 | /* check whether given key is in .ssh/authorized_keys* */ | 435 | /* |
436 | * Checks whether key is allowed in file. | ||
437 | * returns 1 if the key is allowed or 0 otherwise. | ||
438 | */ | ||
439 | static int | ||
440 | user_key_allowed2(struct passwd *pw, Key *key, char *file) | ||
441 | { | ||
442 | FILE *f; | ||
443 | int found_key = 0; | ||
444 | |||
445 | /* Temporarily use the user's uid. */ | ||
446 | temporarily_use_uid(pw); | ||
447 | |||
448 | debug("trying public key file %s", file); | ||
449 | if ((f = auth_openkeyfile(file, pw, options.strict_modes)) != NULL) { | ||
450 | found_key = check_authkeys_file(f, file, key, pw); | ||
451 | fclose(f); | ||
452 | } | ||
453 | |||
454 | restore_uid(); | ||
455 | return found_key; | ||
456 | } | ||
457 | |||
458 | /* | ||
459 | * Checks whether key is allowed in output of command. | ||
460 | * returns 1 if the key is allowed or 0 otherwise. | ||
461 | */ | ||
462 | static int | ||
463 | user_key_command_allowed2(struct passwd *user_pw, Key *key) | ||
464 | { | ||
465 | FILE *f; | ||
466 | int ok, found_key = 0; | ||
467 | struct passwd *pw; | ||
468 | struct stat st; | ||
469 | int status, devnull, p[2], i; | ||
470 | pid_t pid; | ||
471 | char *username, errmsg[512]; | ||
472 | |||
473 | if (options.authorized_keys_command == NULL || | ||
474 | options.authorized_keys_command[0] != '/') | ||
475 | return 0; | ||
476 | |||
477 | if (options.authorized_keys_command_user == NULL) { | ||
478 | error("No user for AuthorizedKeysCommand specified, skipping"); | ||
479 | return 0; | ||
480 | } | ||
481 | |||
482 | username = percent_expand(options.authorized_keys_command_user, | ||
483 | "u", user_pw->pw_name, (char *)NULL); | ||
484 | pw = getpwnam(username); | ||
485 | if (pw == NULL) { | ||
486 | error("AuthorizedKeysCommandUser \"%s\" not found: %s", | ||
487 | username, strerror(errno)); | ||
488 | free(username); | ||
489 | return 0; | ||
490 | } | ||
491 | free(username); | ||
492 | |||
493 | temporarily_use_uid(pw); | ||
494 | |||
495 | if (stat(options.authorized_keys_command, &st) < 0) { | ||
496 | error("Could not stat AuthorizedKeysCommand \"%s\": %s", | ||
497 | options.authorized_keys_command, strerror(errno)); | ||
498 | goto out; | ||
499 | } | ||
500 | if (auth_secure_path(options.authorized_keys_command, &st, NULL, 0, | ||
501 | errmsg, sizeof(errmsg)) != 0) { | ||
502 | error("Unsafe AuthorizedKeysCommand: %s", errmsg); | ||
503 | goto out; | ||
504 | } | ||
505 | |||
506 | if (pipe(p) != 0) { | ||
507 | error("%s: pipe: %s", __func__, strerror(errno)); | ||
508 | goto out; | ||
509 | } | ||
510 | |||
511 | debug3("Running AuthorizedKeysCommand: \"%s %s\" as \"%s\"", | ||
512 | options.authorized_keys_command, user_pw->pw_name, pw->pw_name); | ||
513 | |||
514 | /* | ||
515 | * Don't want to call this in the child, where it can fatal() and | ||
516 | * run cleanup_exit() code. | ||
517 | */ | ||
518 | restore_uid(); | ||
519 | |||
520 | switch ((pid = fork())) { | ||
521 | case -1: /* error */ | ||
522 | error("%s: fork: %s", __func__, strerror(errno)); | ||
523 | close(p[0]); | ||
524 | close(p[1]); | ||
525 | return 0; | ||
526 | case 0: /* child */ | ||
527 | for (i = 0; i < NSIG; i++) | ||
528 | signal(i, SIG_DFL); | ||
529 | |||
530 | if ((devnull = open(_PATH_DEVNULL, O_RDWR)) == -1) { | ||
531 | error("%s: open %s: %s", __func__, _PATH_DEVNULL, | ||
532 | strerror(errno)); | ||
533 | _exit(1); | ||
534 | } | ||
535 | /* Keep stderr around a while longer to catch errors */ | ||
536 | if (dup2(devnull, STDIN_FILENO) == -1 || | ||
537 | dup2(p[1], STDOUT_FILENO) == -1) { | ||
538 | error("%s: dup2: %s", __func__, strerror(errno)); | ||
539 | _exit(1); | ||
540 | } | ||
541 | closefrom(STDERR_FILENO + 1); | ||
542 | |||
543 | /* Don't use permanently_set_uid() here to avoid fatal() */ | ||
544 | if (setresgid(pw->pw_gid, pw->pw_gid, pw->pw_gid) != 0) { | ||
545 | error("setresgid %u: %s", (u_int)pw->pw_gid, | ||
546 | strerror(errno)); | ||
547 | _exit(1); | ||
548 | } | ||
549 | if (setresuid(pw->pw_uid, pw->pw_uid, pw->pw_uid) != 0) { | ||
550 | error("setresuid %u: %s", (u_int)pw->pw_uid, | ||
551 | strerror(errno)); | ||
552 | _exit(1); | ||
553 | } | ||
554 | /* stdin is pointed to /dev/null at this point */ | ||
555 | if (dup2(STDIN_FILENO, STDERR_FILENO) == -1) { | ||
556 | error("%s: dup2: %s", __func__, strerror(errno)); | ||
557 | _exit(1); | ||
558 | } | ||
559 | |||
560 | execl(options.authorized_keys_command, | ||
561 | options.authorized_keys_command, user_pw->pw_name, NULL); | ||
562 | |||
563 | error("AuthorizedKeysCommand %s exec failed: %s", | ||
564 | options.authorized_keys_command, strerror(errno)); | ||
565 | _exit(127); | ||
566 | default: /* parent */ | ||
567 | break; | ||
568 | } | ||
569 | |||
570 | temporarily_use_uid(pw); | ||
571 | |||
572 | close(p[1]); | ||
573 | if ((f = fdopen(p[0], "r")) == NULL) { | ||
574 | error("%s: fdopen: %s", __func__, strerror(errno)); | ||
575 | close(p[0]); | ||
576 | /* Don't leave zombie child */ | ||
577 | kill(pid, SIGTERM); | ||
578 | while (waitpid(pid, NULL, 0) == -1 && errno == EINTR) | ||
579 | ; | ||
580 | goto out; | ||
581 | } | ||
582 | ok = check_authkeys_file(f, options.authorized_keys_command, key, pw); | ||
583 | fclose(f); | ||
584 | |||
585 | while (waitpid(pid, &status, 0) == -1) { | ||
586 | if (errno != EINTR) { | ||
587 | error("%s: waitpid: %s", __func__, strerror(errno)); | ||
588 | goto out; | ||
589 | } | ||
590 | } | ||
591 | if (WIFSIGNALED(status)) { | ||
592 | error("AuthorizedKeysCommand %s exited on signal %d", | ||
593 | options.authorized_keys_command, WTERMSIG(status)); | ||
594 | goto out; | ||
595 | } else if (WEXITSTATUS(status) != 0) { | ||
596 | error("AuthorizedKeysCommand %s returned status %d", | ||
597 | options.authorized_keys_command, WEXITSTATUS(status)); | ||
598 | goto out; | ||
599 | } | ||
600 | found_key = ok; | ||
601 | out: | ||
602 | restore_uid(); | ||
603 | return found_key; | ||
604 | } | ||
605 | |||
606 | /* | ||
607 | * Check whether key authenticates and authorises the user. | ||
608 | */ | ||
441 | int | 609 | int |
442 | user_key_allowed(struct passwd *pw, Key *key) | 610 | user_key_allowed(struct passwd *pw, Key *key) |
443 | { | 611 | { |
@@ -454,9 +622,17 @@ user_key_allowed(struct passwd *pw, Key *key) | |||
454 | if (success) | 622 | if (success) |
455 | return success; | 623 | return success; |
456 | 624 | ||
625 | success = user_key_command_allowed2(pw, key); | ||
626 | if (success > 0) | ||
627 | return success; | ||
628 | |||
457 | for (i = 0; !success && i < options.num_authkeys_files; i++) { | 629 | for (i = 0; !success && i < options.num_authkeys_files; i++) { |
630 | |||
631 | if (strcasecmp(options.authorized_keys_files[i], "none") == 0) | ||
632 | continue; | ||
458 | file = expand_authorized_keys( | 633 | file = expand_authorized_keys( |
459 | options.authorized_keys_files[i], pw); | 634 | options.authorized_keys_files[i], pw); |
635 | |||
460 | success = user_key_allowed2(pw, key, file); | 636 | success = user_key_allowed2(pw, key, file); |
461 | xfree(file); | 637 | xfree(file); |
462 | } | 638 | } |