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