diff options
author | Damien Miller <djm@mindrot.org> | 2012-10-31 08:58:58 +1100 |
---|---|---|
committer | Damien Miller <djm@mindrot.org> | 2012-10-31 08:58:58 +1100 |
commit | 09d3e1251250dcf45e5434cd474430e4ec5e8639 (patch) | |
tree | 674ce6528821636740dce3a32ac1634b397643c9 | |
parent | 07daed505f1cd6a0beff4d060b588debcc1ca8c8 (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-- | ChangeLog | 10 | ||||
-rw-r--r-- | auth-rsa.c | 4 | ||||
-rw-r--r-- | auth.c | 53 | ||||
-rw-r--r-- | auth.h | 6 | ||||
-rw-r--r-- | auth2-pubkey.c | 206 | ||||
-rw-r--r-- | servconf.c | 30 | ||||
-rw-r--r-- | servconf.h | 4 | ||||
-rw-r--r-- | sshd.c | 11 | ||||
-rw-r--r-- | sshd_config | 5 | ||||
-rw-r--r-- | sshd_config.5 | 22 |
10 files changed, 308 insertions, 43 deletions
@@ -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 | ||
7 | 20121019 | 17 | 20121019 |
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); |
@@ -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 | */ |
425 | static int | 425 | int |
426 | secure_filename(FILE *f, const char *file, struct passwd *pw, | 426 | auth_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 | */ | ||
489 | static int | ||
490 | secure_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 | |||
482 | static FILE * | 505 | static FILE * |
483 | auth_openfile(const char *file, struct passwd *pw, int strict_modes, | 506 | auth_openfile(const char *file, struct passwd *pw, int strict_modes, |
484 | int log_missing, char *file_type) | 507 | int log_missing, char *file_type) |
@@ -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 *); | |||
120 | int hostbased_key_allowed(struct passwd *, const char *, char *, Key *); | 120 | int hostbased_key_allowed(struct passwd *, const char *, char *, Key *); |
121 | int user_key_allowed(struct passwd *, Key *); | 121 | int user_key_allowed(struct passwd *, Key *); |
122 | 122 | ||
123 | struct stat; | ||
124 | int auth_secure_path(const char *, struct stat *, const char *, uid_t, | ||
125 | char *, size_t); | ||
126 | |||
123 | #ifdef KRB5 | 127 | #ifdef KRB5 |
124 | int auth_krb5(Authctxt *authctxt, krb5_data *auth, char **client, krb5_data *); | 128 | int auth_krb5(Authctxt *authctxt, krb5_data *auth, char **client, krb5_data *); |
125 | int auth_krb5_tgt(Authctxt *authctxt, krb5_data *tgt); | 129 | int 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 | */ | ||
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 | } |
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; |
@@ -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 | |||
151 | in | 151 | in |
152 | .Xr ssh_config 5 | 152 | .Xr ssh_config 5 |
153 | for more information on patterns. | 153 | for more information on patterns. |
154 | .It Cm AuthorizedKeysCommand | ||
155 | Specifies a program to be used for lookup of the user's public keys. | ||
156 | The program will be invoked with a single argument of the username | ||
157 | being authenticated, and should produce on standard output zero or | ||
158 | more lines of authorized_keys output (see AUTHORIZED_KEYS in | ||
159 | .Xr sshd 8 ) | ||
160 | If a key supplied by AuthorizedKeysCommand does not successfully authenticate | ||
161 | and authorize the user then public key authentication continues using the usual | ||
162 | .Cm AuthorizedKeysFile | ||
163 | files. | ||
164 | By default, no AuthorizedKeysCommand is run. | ||
165 | .It Cm AuthorizedKeysCommandUser | ||
166 | Specifies the user under whose account the AuthorizedKeysCommand is run. | ||
167 | The default is the user being authenticated. | ||
168 | It is recommended to use a dedicated user that has no other role on the host | ||
169 | than running authorized keys commands. | ||
154 | .It Cm AuthorizedKeysFile | 170 | .It Cm AuthorizedKeysFile |
155 | Specifies the file that contains the public keys that can be used | 171 | Specifies the file that contains the public keys that can be used |
156 | for user authentication. | 172 | for 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 , |