summaryrefslogtreecommitdiff
path: root/auth2-pubkey.c
diff options
context:
space:
mode:
authorDamien Miller <djm@mindrot.org>2012-10-31 08:58:58 +1100
committerDamien Miller <djm@mindrot.org>2012-10-31 08:58:58 +1100
commit09d3e1251250dcf45e5434cd474430e4ec5e8639 (patch)
tree674ce6528821636740dce3a32ac1634b397643c9 /auth2-pubkey.c
parent07daed505f1cd6a0beff4d060b588debcc1ca8c8 (diff)
- djm@cvs.openbsd.org 2012/10/30 21:29:55
[auth-rsa.c auth.c auth.h auth2-pubkey.c servconf.c servconf.h] [sshd.c sshd_config sshd_config.5] new sshd_config option AuthorizedKeysCommand to support fetching authorized_keys from a command in addition to (or instead of) from the filesystem. The command is run as the target server user unless another specified via a new AuthorizedKeysCommandUser option. patch originally by jchadima AT redhat.com, reworked by me; feedback and ok markus@
Diffstat (limited to 'auth2-pubkey.c')
-rw-r--r--auth2-pubkey.c206
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 */
259static int 266static int
260user_key_allowed2(struct passwd *pw, Key *key, char *file) 267check_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 */
433static int
434user_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 */
456static int
457user_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 */
437int 595int
438user_key_allowed(struct passwd *pw, Key *key) 596user_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 }