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