summaryrefslogtreecommitdiff
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
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@
-rw-r--r--ChangeLog10
-rw-r--r--auth-rsa.c4
-rw-r--r--auth.c53
-rw-r--r--auth.h6
-rw-r--r--auth2-pubkey.c206
-rw-r--r--servconf.c30
-rw-r--r--servconf.h4
-rw-r--r--sshd.c11
-rw-r--r--sshd_config5
-rw-r--r--sshd_config.522
10 files changed, 308 insertions, 43 deletions
diff --git a/ChangeLog b/ChangeLog
index 3cd16e480..27ec898b8 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -3,6 +3,16 @@
3 - markus@cvs.openbsd.org 2012/10/05 12:34:39 3 - markus@cvs.openbsd.org 2012/10/05 12:34:39
4 [sftp.c] 4 [sftp.c]
5 fix signed vs unsigned warning; feedback & ok: djm@ 5 fix signed vs unsigned warning; feedback & ok: djm@
6 - djm@cvs.openbsd.org 2012/10/30 21:29:55
7 [auth-rsa.c auth.c auth.h auth2-pubkey.c servconf.c servconf.h]
8 [sshd.c sshd_config sshd_config.5]
9 new sshd_config option AuthorizedKeysCommand to support fetching
10 authorized_keys from a command in addition to (or instead of) from
11 the filesystem. The command is run as the target server user unless
12 another specified via a new AuthorizedKeysCommandUser option.
13
14 patch originally by jchadima AT redhat.com, reworked by me; feedback
15 and ok markus@
6 16
720121019 1720121019
8 - (tim) [buildpkg.sh.in] Double up on some backslashes so they end up in 18 - (tim) [buildpkg.sh.in] Double up on some backslashes so they end up in
diff --git a/auth-rsa.c b/auth-rsa.c
index 4ab46cd51..2c8a7cb35 100644
--- a/auth-rsa.c
+++ b/auth-rsa.c
@@ -1,4 +1,4 @@
1/* $OpenBSD: auth-rsa.c,v 1.80 2011/05/23 03:30:07 djm Exp $ */ 1/* $OpenBSD: auth-rsa.c,v 1.81 2012/10/30 21:29:54 djm Exp $ */
2/* 2/*
3 * Author: Tatu Ylonen <ylo@cs.hut.fi> 3 * Author: Tatu Ylonen <ylo@cs.hut.fi>
4 * Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland 4 * Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland
@@ -276,6 +276,8 @@ auth_rsa_key_allowed(struct passwd *pw, BIGNUM *client_n, Key **rkey)
276 temporarily_use_uid(pw); 276 temporarily_use_uid(pw);
277 277
278 for (i = 0; !allowed && i < options.num_authkeys_files; i++) { 278 for (i = 0; !allowed && i < options.num_authkeys_files; i++) {
279 if (strcasecmp(options.authorized_keys_files[i], "none") == 0)
280 continue;
279 file = expand_authorized_keys( 281 file = expand_authorized_keys(
280 options.authorized_keys_files[i], pw); 282 options.authorized_keys_files[i], pw);
281 allowed = rsa_key_allowed_in_file(pw, file, client_n, rkey); 283 allowed = rsa_key_allowed_in_file(pw, file, client_n, rkey);
diff --git a/auth.c b/auth.c
index a8cffd5c1..b5e1eefa0 100644
--- a/auth.c
+++ b/auth.c
@@ -1,4 +1,4 @@
1/* $OpenBSD: auth.c,v 1.96 2012/05/13 01:42:32 dtucker Exp $ */ 1/* $OpenBSD: auth.c,v 1.97 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 *
@@ -409,41 +409,42 @@ check_key_in_hostfiles(struct passwd *pw, Key *key, const char *host,
409 return host_status; 409 return host_status;
410} 410}
411 411
412
413/* 412/*
414 * Check a given file for security. This is defined as all components 413 * Check a given path for security. This is defined as all components
415 * of the path to the file must be owned by either the owner of 414 * of the path to the file must be owned by either the owner of
416 * of the file or root and no directories must be group or world writable. 415 * of the file or root and no directories must be group or world writable.
417 * 416 *
418 * XXX Should any specific check be done for sym links ? 417 * XXX Should any specific check be done for sym links ?
419 * 418 *
420 * Takes an open file descriptor, the file name, a uid and and 419 * Takes an the file name, its stat information (preferably from fstat() to
420 * avoid races), the uid of the expected owner, their home directory and an
421 * error buffer plus max size as arguments. 421 * error buffer plus max size as arguments.
422 * 422 *
423 * Returns 0 on success and -1 on failure 423 * Returns 0 on success and -1 on failure
424 */ 424 */
425static int 425int
426secure_filename(FILE *f, const char *file, struct passwd *pw, 426auth_secure_path(const char *name, struct stat *stp, const char *pw_dir,
427 char *err, size_t errlen) 427 uid_t uid, char *err, size_t errlen)
428{ 428{
429 uid_t uid = pw->pw_uid;
430 char buf[MAXPATHLEN], homedir[MAXPATHLEN]; 429 char buf[MAXPATHLEN], homedir[MAXPATHLEN];
431 char *cp; 430 char *cp;
432 int comparehome = 0; 431 int comparehome = 0;
433 struct stat st; 432 struct stat st;
434 433
435 if (realpath(file, buf) == NULL) { 434 if (realpath(name, buf) == NULL) {
436 snprintf(err, errlen, "realpath %s failed: %s", file, 435 snprintf(err, errlen, "realpath %s failed: %s", name,
437 strerror(errno)); 436 strerror(errno));
438 return -1; 437 return -1;
439 } 438 }
440 if (realpath(pw->pw_dir, homedir) != NULL) 439 if (pw_dir != NULL && realpath(pw_dir, homedir) != NULL)
441 comparehome = 1; 440 comparehome = 1;
442 441
443 /* check the open file to avoid races */ 442 if (!S_ISREG(stp->st_mode)) {
444 if (fstat(fileno(f), &st) < 0 || 443 snprintf(err, errlen, "%s is not a regular file", buf);
445 (st.st_uid != 0 && st.st_uid != uid) || 444 return -1;
446 (st.st_mode & 022) != 0) { 445 }
446 if ((stp->st_uid != 0 && stp->st_uid != uid) ||
447 (stp->st_mode & 022) != 0) {
447 snprintf(err, errlen, "bad ownership or modes for file %s", 448 snprintf(err, errlen, "bad ownership or modes for file %s",
448 buf); 449 buf);
449 return -1; 450 return -1;
@@ -479,6 +480,28 @@ secure_filename(FILE *f, const char *file, struct passwd *pw,
479 return 0; 480 return 0;
480} 481}
481 482
483/*
484 * Version of secure_path() that accepts an open file descriptor to
485 * avoid races.
486 *
487 * Returns 0 on success and -1 on failure
488 */
489static int
490secure_filename(FILE *f, const char *file, struct passwd *pw,
491 char *err, size_t errlen)
492{
493 char buf[MAXPATHLEN];
494 struct stat st;
495
496 /* check the open file to avoid races */
497 if (fstat(fileno(f), &st) < 0) {
498 snprintf(err, errlen, "cannot stat file %s: %s",
499 buf, strerror(errno));
500 return -1;
501 }
502 return auth_secure_path(file, &st, pw->pw_dir, pw->pw_uid, err, errlen);
503}
504
482static FILE * 505static FILE *
483auth_openfile(const char *file, struct passwd *pw, int strict_modes, 506auth_openfile(const char *file, struct passwd *pw, int strict_modes,
484 int log_missing, char *file_type) 507 int log_missing, char *file_type)
diff --git a/auth.h b/auth.h
index 0d786c4d5..063404167 100644
--- a/auth.h
+++ b/auth.h
@@ -1,4 +1,4 @@
1/* $OpenBSD: auth.h,v 1.69 2011/05/23 03:30:07 djm Exp $ */ 1/* $OpenBSD: auth.h,v 1.70 2012/10/30 21:29:54 djm Exp $ */
2 2
3/* 3/*
4 * Copyright (c) 2000 Markus Friedl. All rights reserved. 4 * Copyright (c) 2000 Markus Friedl. All rights reserved.
@@ -120,6 +120,10 @@ int auth_rhosts_rsa_key_allowed(struct passwd *, char *, char *, Key *);
120int hostbased_key_allowed(struct passwd *, const char *, char *, Key *); 120int hostbased_key_allowed(struct passwd *, const char *, char *, Key *);
121int user_key_allowed(struct passwd *, Key *); 121int user_key_allowed(struct passwd *, Key *);
122 122
123struct stat;
124int auth_secure_path(const char *, struct stat *, const char *, uid_t,
125 char *, size_t);
126
123#ifdef KRB5 127#ifdef KRB5
124int auth_krb5(Authctxt *authctxt, krb5_data *auth, char **client, krb5_data *); 128int auth_krb5(Authctxt *authctxt, krb5_data *auth, char **client, krb5_data *);
125int auth_krb5_tgt(Authctxt *authctxt, krb5_data *tgt); 129int auth_krb5_tgt(Authctxt *authctxt, krb5_data *tgt);
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 }
diff --git a/servconf.c b/servconf.c
index f4b7dd58b..8e69ea5ce 100644
--- a/servconf.c
+++ b/servconf.c
@@ -1,5 +1,5 @@
1 1
2/* $OpenBSD: servconf.c,v 1.230 2012/09/13 23:37:36 dtucker Exp $ */ 2/* $OpenBSD: servconf.c,v 1.231 2012/10/30 21:29:54 djm Exp $ */
3/* 3/*
4 * Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland 4 * Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland
5 * All rights reserved 5 * All rights reserved
@@ -135,6 +135,8 @@ initialize_server_options(ServerOptions *options)
135 options->num_permitted_opens = -1; 135 options->num_permitted_opens = -1;
136 options->adm_forced_command = NULL; 136 options->adm_forced_command = NULL;
137 options->chroot_directory = NULL; 137 options->chroot_directory = NULL;
138 options->authorized_keys_command = NULL;
139 options->authorized_keys_command_user = NULL;
138 options->zero_knowledge_password_authentication = -1; 140 options->zero_knowledge_password_authentication = -1;
139 options->revoked_keys_file = NULL; 141 options->revoked_keys_file = NULL;
140 options->trusted_user_ca_keys = NULL; 142 options->trusted_user_ca_keys = NULL;
@@ -329,6 +331,7 @@ typedef enum {
329 sZeroKnowledgePasswordAuthentication, sHostCertificate, 331 sZeroKnowledgePasswordAuthentication, sHostCertificate,
330 sRevokedKeys, sTrustedUserCAKeys, sAuthorizedPrincipalsFile, 332 sRevokedKeys, sTrustedUserCAKeys, sAuthorizedPrincipalsFile,
331 sKexAlgorithms, sIPQoS, sVersionAddendum, 333 sKexAlgorithms, sIPQoS, sVersionAddendum,
334 sAuthorizedKeysCommand, sAuthorizedKeysCommandUser,
332 sDeprecated, sUnsupported 335 sDeprecated, sUnsupported
333} ServerOpCodes; 336} ServerOpCodes;
334 337
@@ -453,6 +456,8 @@ static struct {
453 { "authorizedprincipalsfile", sAuthorizedPrincipalsFile, SSHCFG_ALL }, 456 { "authorizedprincipalsfile", sAuthorizedPrincipalsFile, SSHCFG_ALL },
454 { "kexalgorithms", sKexAlgorithms, SSHCFG_GLOBAL }, 457 { "kexalgorithms", sKexAlgorithms, SSHCFG_GLOBAL },
455 { "ipqos", sIPQoS, SSHCFG_ALL }, 458 { "ipqos", sIPQoS, SSHCFG_ALL },
459 { "authorizedkeyscommand", sAuthorizedKeysCommand, SSHCFG_ALL },
460 { "authorizedkeyscommanduser", sAuthorizedKeysCommandUser, SSHCFG_ALL },
456 { "versionaddendum", sVersionAddendum, SSHCFG_GLOBAL }, 461 { "versionaddendum", sVersionAddendum, SSHCFG_GLOBAL },
457 { NULL, sBadOption, 0 } 462 { NULL, sBadOption, 0 }
458}; 463};
@@ -1498,6 +1503,25 @@ process_server_config_line(ServerOptions *options, char *line,
1498 } 1503 }
1499 return 0; 1504 return 0;
1500 1505
1506 case sAuthorizedKeysCommand:
1507 len = strspn(cp, WHITESPACE);
1508 if (*activep && options->authorized_keys_command == NULL) {
1509 if (cp[len] != '/' && strcasecmp(cp + len, "none") != 0)
1510 fatal("%.200s line %d: AuthorizedKeysCommand "
1511 "must be an absolute path",
1512 filename, linenum);
1513 options->authorized_keys_command = xstrdup(cp + len);
1514 }
1515 return 0;
1516
1517 case sAuthorizedKeysCommandUser:
1518 charptr = &options->authorized_keys_command_user;
1519
1520 arg = strdelim(&cp);
1521 if (*activep && *charptr == NULL)
1522 *charptr = xstrdup(arg);
1523 break;
1524
1501 case sDeprecated: 1525 case sDeprecated:
1502 logit("%s line %d: Deprecated option %s", 1526 logit("%s line %d: Deprecated option %s",
1503 filename, linenum, arg); 1527 filename, linenum, arg);
@@ -1648,6 +1672,8 @@ copy_set_server_options(ServerOptions *dst, ServerOptions *src, int preauth)
1648 M_CP_INTOPT(hostbased_uses_name_from_packet_only); 1672 M_CP_INTOPT(hostbased_uses_name_from_packet_only);
1649 M_CP_INTOPT(kbd_interactive_authentication); 1673 M_CP_INTOPT(kbd_interactive_authentication);
1650 M_CP_INTOPT(zero_knowledge_password_authentication); 1674 M_CP_INTOPT(zero_knowledge_password_authentication);
1675 M_CP_STROPT(authorized_keys_command);
1676 M_CP_STROPT(authorized_keys_command_user);
1651 M_CP_INTOPT(permit_root_login); 1677 M_CP_INTOPT(permit_root_login);
1652 M_CP_INTOPT(permit_empty_passwd); 1678 M_CP_INTOPT(permit_empty_passwd);
1653 1679
@@ -1908,6 +1934,8 @@ dump_config(ServerOptions *o)
1908 dump_cfg_string(sAuthorizedPrincipalsFile, 1934 dump_cfg_string(sAuthorizedPrincipalsFile,
1909 o->authorized_principals_file); 1935 o->authorized_principals_file);
1910 dump_cfg_string(sVersionAddendum, o->version_addendum); 1936 dump_cfg_string(sVersionAddendum, o->version_addendum);
1937 dump_cfg_string(sAuthorizedKeysCommand, o->authorized_keys_command);
1938 dump_cfg_string(sAuthorizedKeysCommandUser, o->authorized_keys_command_user);
1911 1939
1912 /* string arguments requiring a lookup */ 1940 /* string arguments requiring a lookup */
1913 dump_cfg_string(sLogLevel, log_level_name(o->log_level)); 1941 dump_cfg_string(sLogLevel, log_level_name(o->log_level));
diff --git a/servconf.h b/servconf.h
index 096d596d7..0064c9bc5 100644
--- a/servconf.h
+++ b/servconf.h
@@ -1,4 +1,4 @@
1/* $OpenBSD: servconf.h,v 1.103 2012/07/10 02:19:15 djm Exp $ */ 1/* $OpenBSD: servconf.h,v 1.104 2012/10/30 21:29:55 djm Exp $ */
2 2
3/* 3/*
4 * Author: Tatu Ylonen <ylo@cs.hut.fi> 4 * Author: Tatu Ylonen <ylo@cs.hut.fi>
@@ -166,6 +166,8 @@ typedef struct {
166 char *revoked_keys_file; 166 char *revoked_keys_file;
167 char *trusted_user_ca_keys; 167 char *trusted_user_ca_keys;
168 char *authorized_principals_file; 168 char *authorized_principals_file;
169 char *authorized_keys_command;
170 char *authorized_keys_command_user;
169 171
170 char *version_addendum; /* Appended to SSH banner */ 172 char *version_addendum; /* Appended to SSH banner */
171} ServerOptions; 173} ServerOptions;
diff --git a/sshd.c b/sshd.c
index 9aff5e8af..eff0290b0 100644
--- a/sshd.c
+++ b/sshd.c
@@ -1,4 +1,4 @@
1/* $OpenBSD: sshd.c,v 1.393 2012/07/10 02:19:15 djm Exp $ */ 1/* $OpenBSD: sshd.c,v 1.394 2012/10/30 21:29:55 djm Exp $ */
2/* 2/*
3 * Author: Tatu Ylonen <ylo@cs.hut.fi> 3 * Author: Tatu Ylonen <ylo@cs.hut.fi>
4 * Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland 4 * Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland
@@ -359,6 +359,15 @@ grace_alarm_handler(int sig)
359 if (use_privsep && pmonitor != NULL && pmonitor->m_pid > 0) 359 if (use_privsep && pmonitor != NULL && pmonitor->m_pid > 0)
360 kill(pmonitor->m_pid, SIGALRM); 360 kill(pmonitor->m_pid, SIGALRM);
361 361
362 /*
363 * Try to kill any processes that we have spawned, E.g. authorized
364 * keys command helpers.
365 */
366 if (getpgid(0) == getpid()) {
367 signal(SIGTERM, SIG_IGN);
368 killpg(0, SIGTERM);
369 }
370
362 /* Log error and exit. */ 371 /* Log error and exit. */
363 sigdie("Timeout before authentication for %s", get_remote_ipaddr()); 372 sigdie("Timeout before authentication for %s", get_remote_ipaddr());
364} 373}
diff --git a/sshd_config b/sshd_config
index 9424ee2c6..3d35bef02 100644
--- a/sshd_config
+++ b/sshd_config
@@ -1,4 +1,4 @@
1# $OpenBSD: sshd_config,v 1.87 2012/07/10 02:19:15 djm Exp $ 1# $OpenBSD: sshd_config,v 1.88 2012/10/30 21:29:55 djm Exp $
2 2
3# This is the sshd server system-wide configuration file. See 3# This is the sshd server system-wide configuration file. See
4# sshd_config(5) for more information. 4# sshd_config(5) for more information.
@@ -51,6 +51,9 @@ AuthorizedKeysFile .ssh/authorized_keys
51 51
52#AuthorizedPrincipalsFile none 52#AuthorizedPrincipalsFile none
53 53
54#AuthorizedKeysCommand none
55#AuthorizedKeysCommandUser nobody
56
54# For this to work you will also need host keys in /etc/ssh/ssh_known_hosts 57# For this to work you will also need host keys in /etc/ssh/ssh_known_hosts
55#RhostsRSAAuthentication no 58#RhostsRSAAuthentication no
56# similar for protocol version 2 59# similar for protocol version 2
diff --git a/sshd_config.5 b/sshd_config.5
index 987558ae8..de8f0f825 100644
--- a/sshd_config.5
+++ b/sshd_config.5
@@ -33,8 +33,8 @@
33.\" (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 33.\" (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
34.\" THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 34.\" THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
35.\" 35.\"
36.\" $OpenBSD: sshd_config.5,v 1.145 2012/10/04 13:21:50 markus Exp $ 36.\" $OpenBSD: sshd_config.5,v 1.146 2012/10/30 21:29:55 djm Exp $
37.Dd $Mdocdate: October 4 2012 $ 37.Dd $Mdocdate: October 30 2012 $
38.Dt SSHD_CONFIG 5 38.Dt SSHD_CONFIG 5
39.Os 39.Os
40.Sh NAME 40.Sh NAME
@@ -151,6 +151,22 @@ See
151in 151in
152.Xr ssh_config 5 152.Xr ssh_config 5
153for more information on patterns. 153for more information on patterns.
154.It Cm AuthorizedKeysCommand
155Specifies a program to be used for lookup of the user's public keys.
156The program will be invoked with a single argument of the username
157being authenticated, and should produce on standard output zero or
158more lines of authorized_keys output (see AUTHORIZED_KEYS in
159.Xr sshd 8 )
160If a key supplied by AuthorizedKeysCommand does not successfully authenticate
161and authorize the user then public key authentication continues using the usual
162.Cm AuthorizedKeysFile
163files.
164By default, no AuthorizedKeysCommand is run.
165.It Cm AuthorizedKeysCommandUser
166Specifies the user under whose account the AuthorizedKeysCommand is run.
167The default is the user being authenticated.
168It is recommended to use a dedicated user that has no other role on the host
169than running authorized keys commands.
154.It Cm AuthorizedKeysFile 170.It Cm AuthorizedKeysFile
155Specifies the file that contains the public keys that can be used 171Specifies the file that contains the public keys that can be used
156for user authentication. 172for user authentication.
@@ -712,6 +728,8 @@ Available keywords are
712.Cm AllowTcpForwarding , 728.Cm AllowTcpForwarding ,
713.Cm AllowUsers , 729.Cm AllowUsers ,
714.Cm AuthorizedKeysFile , 730.Cm AuthorizedKeysFile ,
731.Cm AuthorizedKeysCommand ,
732.Cm AuthorizedKeysCommandUser ,
715.Cm AuthorizedPrincipalsFile , 733.Cm AuthorizedPrincipalsFile ,
716.Cm Banner , 734.Cm Banner ,
717.Cm ChrootDirectory , 735.Cm ChrootDirectory ,