From 09d3e1251250dcf45e5434cd474430e4ec5e8639 Mon Sep 17 00:00:00 2001 From: Damien Miller Date: Wed, 31 Oct 2012 08:58:58 +1100 Subject: - 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@ --- auth2-pubkey.c | 206 +++++++++++++++++++++++++++++++++++++++++++++++++++------ 1 file changed, 186 insertions(+), 20 deletions(-) (limited to 'auth2-pubkey.c') 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 @@ -/* $OpenBSD: auth2-pubkey.c,v 1.30 2011/09/25 05:44:47 djm Exp $ */ +/* $OpenBSD: auth2-pubkey.c,v 1.31 2012/10/30 21:29:54 djm Exp $ */ /* * Copyright (c) 2000 Markus Friedl. All rights reserved. * @@ -27,9 +27,13 @@ #include #include +#include +#include #include +#include #include +#include #include #include #include @@ -240,7 +244,7 @@ match_principals_file(char *file, struct passwd *pw, struct KeyCert *cert) if (strcmp(cp, cert->principals[i]) == 0) { debug3("matched principal \"%.100s\" " "from file \"%s\" on line %lu", - cert->principals[i], file, linenum); + cert->principals[i], file, linenum); if (auth_parse_options(pw, line_opts, file, linenum) != 1) continue; @@ -253,31 +257,22 @@ match_principals_file(char *file, struct passwd *pw, struct KeyCert *cert) fclose(f); restore_uid(); return 0; -} +} -/* return 1 if user allows given key */ +/* + * Checks whether key is allowed in authorized_keys-format file, + * returns 1 if the key is allowed or 0 otherwise. + */ static int -user_key_allowed2(struct passwd *pw, Key *key, char *file) +check_authkeys_file(FILE *f, char *file, Key* key, struct passwd *pw) { char line[SSH_MAX_PUBKEY_BYTES]; const char *reason; int found_key = 0; - FILE *f; u_long linenum = 0; Key *found; char *fp; - /* Temporarily use the user's uid. */ - temporarily_use_uid(pw); - - debug("trying public key file %s", file); - f = auth_openkeyfile(file, pw, options.strict_modes); - - if (!f) { - restore_uid(); - return 0; - } - found_key = 0; found = key_new(key_is_cert(key) ? KEY_UNSPEC : key->type); @@ -370,8 +365,6 @@ user_key_allowed2(struct passwd *pw, Key *key, char *file) break; } } - restore_uid(); - fclose(f); key_free(found); if (!found_key) debug2("key not found"); @@ -433,7 +426,172 @@ user_cert_trusted_ca(struct passwd *pw, Key *key) return ret; } -/* check whether given key is in .ssh/authorized_keys* */ +/* + * Checks whether key is allowed in file. + * returns 1 if the key is allowed or 0 otherwise. + */ +static int +user_key_allowed2(struct passwd *pw, Key *key, char *file) +{ + FILE *f; + int found_key = 0; + + /* Temporarily use the user's uid. */ + temporarily_use_uid(pw); + + debug("trying public key file %s", file); + if ((f = auth_openkeyfile(file, pw, options.strict_modes)) != NULL) { + found_key = check_authkeys_file(f, file, key, pw); + fclose(f); + } + + restore_uid(); + return found_key; +} + +/* + * Checks whether key is allowed in output of command. + * returns 1 if the key is allowed or 0 otherwise. + */ +static int +user_key_command_allowed2(struct passwd *user_pw, Key *key) +{ + FILE *f; + int ok, found_key = 0; + struct passwd *pw; + struct stat st; + int status, devnull, p[2], i; + pid_t pid; + char errmsg[512]; + + if (options.authorized_keys_command == NULL || + options.authorized_keys_command[0] != '/') + return 0; + + /* If no user specified to run commands the default to target user */ + if (options.authorized_keys_command_user == NULL) + pw = user_pw; + else { + pw = getpwnam(options.authorized_keys_command_user); + if (pw == NULL) { + error("AuthorizedKeyCommandUser \"%s\" not found: %s", + options.authorized_keys_command, strerror(errno)); + return 0; + } + } + + temporarily_use_uid(pw); + + if (stat(options.authorized_keys_command, &st) < 0) { + error("Could not stat AuthorizedKeysCommand \"%s\": %s", + options.authorized_keys_command, strerror(errno)); + goto out; + } + if (auth_secure_path(options.authorized_keys_command, &st, NULL, 0, + errmsg, sizeof(errmsg)) != 0) { + error("Unsafe AuthorizedKeysCommand: %s", errmsg); + goto out; + } + + if (pipe(p) != 0) { + error("%s: pipe: %s", __func__, strerror(errno)); + goto out; + } + + debug3("Running AuthorizedKeysCommand: \"%s\" as \"%s\"", + options.authorized_keys_command, pw->pw_name); + + /* + * Don't want to call this in the child, where it can fatal() and + * run cleanup_exit() code. + */ + restore_uid(); + + switch ((pid = fork())) { + case -1: /* error */ + error("%s: fork: %s", __func__, strerror(errno)); + close(p[0]); + close(p[1]); + return 0; + case 0: /* child */ + for (i = 0; i < NSIG; i++) + signal(i, SIG_DFL); + + /* Don't use permanently_set_uid() here to avoid fatal() */ + if (setresgid(pw->pw_gid, pw->pw_gid, pw->pw_gid) != 0) { + error("setresgid %u: %s", (u_int)pw->pw_gid, + strerror(errno)); + _exit(1); + } + if (setresuid(pw->pw_uid, pw->pw_uid, pw->pw_uid) != 0) { + error("setresuid %u: %s", (u_int)pw->pw_uid, + strerror(errno)); + _exit(1); + } + + close(p[0]); + if ((devnull = open(_PATH_DEVNULL, O_RDWR)) == -1) { + error("%s: open %s: %s", __func__, _PATH_DEVNULL, + strerror(errno)); + _exit(1); + } + if (dup2(devnull, STDIN_FILENO) == -1 || + dup2(p[1], STDOUT_FILENO) == -1 || + dup2(devnull, STDERR_FILENO) == -1) { + error("%s: dup2: %s", __func__, strerror(errno)); + _exit(1); + } + closefrom(STDERR_FILENO + 1); + + execl(options.authorized_keys_command, + options.authorized_keys_command, pw->pw_name, NULL); + + error("AuthorizedKeysCommand %s exec failed: %s", + options.authorized_keys_command, strerror(errno)); + _exit(127); + default: /* parent */ + break; + } + + temporarily_use_uid(pw); + + close(p[1]); + if ((f = fdopen(p[0], "r")) == NULL) { + error("%s: fdopen: %s", __func__, strerror(errno)); + close(p[0]); + /* Don't leave zombie child */ + kill(pid, SIGTERM); + while (waitpid(pid, NULL, 0) == -1 && errno == EINTR) + ; + goto out; + } + ok = check_authkeys_file(f, options.authorized_keys_command, key, pw); + fclose(f); + + while (waitpid(pid, &status, 0) == -1) { + if (errno != EINTR) { + error("%s: waitpid: %s", __func__, strerror(errno)); + goto out; + } + } + if (WIFSIGNALED(status)) { + error("AuthorizedKeysCommand %s exited on signal %d", + options.authorized_keys_command, WTERMSIG(status)); + goto out; + } else if (WEXITSTATUS(status) != 0) { + error("AuthorizedKeysCommand %s returned status %d", + options.authorized_keys_command, WEXITSTATUS(status)); + goto out; + } + found_key = ok; + out: + restore_uid(); + return found_key; +} + +/* + * Check whether key authenticates and authorises the user. + */ int user_key_allowed(struct passwd *pw, Key *key) { @@ -449,9 +607,17 @@ user_key_allowed(struct passwd *pw, Key *key) if (success) return success; + success = user_key_command_allowed2(pw, key); + if (success > 0) + return success; + for (i = 0; !success && i < options.num_authkeys_files; i++) { + + if (strcasecmp(options.authorized_keys_files[i], "none") == 0) + continue; file = expand_authorized_keys( options.authorized_keys_files[i], pw); + success = user_key_allowed2(pw, key, file); xfree(file); } -- cgit v1.2.3 From d0d1099b3b8a766480ce6df215631bf0af6e6bcd Mon Sep 17 00:00:00 2001 From: Damien Miller Date: Sun, 4 Nov 2012 22:23:14 +1100 Subject: - djm@cvs.openbsd.org 2012/11/04 10:38:43 [auth2-pubkey.c sshd.c sshd_config.5] Remove default of AuthorizedCommandUser. Administrators are now expected to explicitly specify a user. feedback and ok markus@ --- ChangeLog | 4 ++++ auth2-pubkey.c | 30 +++++++++++++++++------------- sshd.c | 9 ++++++++- sshd_config.5 | 5 ++--- 4 files changed, 31 insertions(+), 17 deletions(-) (limited to 'auth2-pubkey.c') diff --git a/ChangeLog b/ChangeLog index f1d0580fc..120c132af 100644 --- a/ChangeLog +++ b/ChangeLog @@ -3,6 +3,10 @@ - jmc@cvs.openbsd.org 2012/10/31 08:04:50 [sshd_config.5] tweak previous; + - djm@cvs.openbsd.org 2012/11/04 10:38:43 + [auth2-pubkey.c sshd.c sshd_config.5] + Remove default of AuthorizedCommandUser. Administrators are now expected + to explicitly specify a user. feedback and ok markus@ 20121030 - (djm) OpenBSD CVS Sync diff --git a/auth2-pubkey.c b/auth2-pubkey.c index ec8f75d57..6a6217017 100644 --- a/auth2-pubkey.c +++ b/auth2-pubkey.c @@ -1,4 +1,4 @@ -/* $OpenBSD: auth2-pubkey.c,v 1.31 2012/10/30 21:29:54 djm Exp $ */ +/* $OpenBSD: auth2-pubkey.c,v 1.32 2012/11/04 10:38:43 djm Exp $ */ /* * Copyright (c) 2000 Markus Friedl. All rights reserved. * @@ -462,23 +462,27 @@ user_key_command_allowed2(struct passwd *user_pw, Key *key) struct stat st; int status, devnull, p[2], i; pid_t pid; - char errmsg[512]; + char *username, errmsg[512]; if (options.authorized_keys_command == NULL || options.authorized_keys_command[0] != '/') return 0; - /* If no user specified to run commands the default to target user */ - if (options.authorized_keys_command_user == NULL) - pw = user_pw; - else { - pw = getpwnam(options.authorized_keys_command_user); - if (pw == NULL) { - error("AuthorizedKeyCommandUser \"%s\" not found: %s", - options.authorized_keys_command, strerror(errno)); - return 0; - } + if (options.authorized_keys_command_user == NULL) { + error("No user for AuthorizedKeysCommand specified, skipping"); + return 0; + } + + username = percent_expand(options.authorized_keys_command_user, + "u", user_pw->pw_name, (char *)NULL); + pw = getpwnam(username); + if (pw == NULL) { + error("AuthorizedKeyCommandUser \"%s\" not found: %s", + options.authorized_keys_command, strerror(errno)); + free(username); + return 0; } + free(username); temporarily_use_uid(pw); @@ -517,6 +521,7 @@ user_key_command_allowed2(struct passwd *user_pw, Key *key) for (i = 0; i < NSIG; i++) signal(i, SIG_DFL); + closefrom(STDERR_FILENO + 1); /* Don't use permanently_set_uid() here to avoid fatal() */ if (setresgid(pw->pw_gid, pw->pw_gid, pw->pw_gid) != 0) { error("setresgid %u: %s", (u_int)pw->pw_gid, @@ -541,7 +546,6 @@ user_key_command_allowed2(struct passwd *user_pw, Key *key) error("%s: dup2: %s", __func__, strerror(errno)); _exit(1); } - closefrom(STDERR_FILENO + 1); execl(options.authorized_keys_command, options.authorized_keys_command, pw->pw_name, NULL); diff --git a/sshd.c b/sshd.c index eff0290b0..4ad1a4bd1 100644 --- a/sshd.c +++ b/sshd.c @@ -1,4 +1,4 @@ -/* $OpenBSD: sshd.c,v 1.394 2012/10/30 21:29:55 djm Exp $ */ +/* $OpenBSD: sshd.c,v 1.395 2012/11/04 10:38:43 djm Exp $ */ /* * Author: Tatu Ylonen * Copyright (c) 1995 Tatu Ylonen , Espoo, Finland @@ -1559,6 +1559,13 @@ main(int ac, char **av) if (options.challenge_response_authentication) options.kbd_interactive_authentication = 1; + /* Check that options are sensible */ + if (options.authorized_keys_command_user == NULL && + (options.authorized_keys_command != NULL && + strcasecmp(options.authorized_keys_command, "none") != 0)) + fatal("AuthorizedKeysCommand set without " + "AuthorizedKeysCommandUser"); + /* set default channel AF */ channel_set_af(options.address_family); diff --git a/sshd_config.5 b/sshd_config.5 index 3cc901a82..0fb0b837d 100644 --- a/sshd_config.5 +++ b/sshd_config.5 @@ -33,8 +33,8 @@ .\" (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF .\" THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. .\" -.\" $OpenBSD: sshd_config.5,v 1.147 2012/10/31 08:04:50 jmc Exp $ -.Dd $Mdocdate: October 31 2012 $ +.\" $OpenBSD: sshd_config.5,v 1.148 2012/11/04 10:38:43 djm Exp $ +.Dd $Mdocdate: November 4 2012 $ .Dt SSHD_CONFIG 5 .Os .Sh NAME @@ -166,7 +166,6 @@ files. By default, no AuthorizedKeysCommand is run. .It Cm AuthorizedKeysCommandUser Specifies the user under whose account the AuthorizedKeysCommand is run. -The default is the user being authenticated. It is recommended to use a dedicated user that has no other role on the host than running authorized keys commands. .It Cm AuthorizedKeysFile -- cgit v1.2.3 From 737f7aff365a47039fb48dcc06367e59863a4cee Mon Sep 17 00:00:00 2001 From: Darren Tucker Date: Mon, 5 Nov 2012 17:07:43 +1100 Subject: - (dtucker) [auth2-pubkey.c] wrap paths.h in an ifdef for platforms that don't have it. Spotted by tim@. --- ChangeLog | 2 ++ auth2-pubkey.c | 4 +++- 2 files changed, 5 insertions(+), 1 deletion(-) (limited to 'auth2-pubkey.c') diff --git a/ChangeLog b/ChangeLog index 8cc7c8e7b..ae5557001 100644 --- a/ChangeLog +++ b/ChangeLog @@ -4,6 +4,8 @@ openbsd-compat/openbsd-compat.h] Move the fallback code for setting uids and gids from uidswap.c to the compat library, which allows it to work with the new setresuid calls in auth2-pubkey. with tim@, ok djm@ + - (dtucker) [auth2-pubkey.c] wrap paths.h in an ifdef for platforms that + don't have it. Spotted by tim@. 20121104 - (djm) OpenBSD CVS Sync diff --git a/auth2-pubkey.c b/auth2-pubkey.c index 6a6217017..f9cc6c2c6 100644 --- a/auth2-pubkey.c +++ b/auth2-pubkey.c @@ -31,7 +31,9 @@ #include #include -#include +#ifdef HAVE_PATHS_H +# include +#endif #include #include #include -- cgit v1.2.3 From 1e85469fcb1e81e7f8f643eafd42eb6c123a8c13 Mon Sep 17 00:00:00 2001 From: Damien Miller Date: Wed, 14 Nov 2012 19:04:02 +1100 Subject: - djm@cvs.openbsd.org 2012/11/14 02:24:27 [auth2-pubkey.c] fix username passed to helper program prepare stdio fds before closefrom() spotted by landry@ --- ChangeLog | 9 +++++++++ auth2-pubkey.c | 32 ++++++++++++++++++-------------- 2 files changed, 27 insertions(+), 14 deletions(-) (limited to 'auth2-pubkey.c') diff --git a/ChangeLog b/ChangeLog index 24193233e..0c018c29e 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,12 @@ +20121114 + - (djm) OpenBSD CVS Sync + - djm@cvs.openbsd.org 2012/11/14 02:24:27 + [auth2-pubkey.c] + fix username passed to helper program + prepare stdio fds before closefrom() + spotted by landry@ + + 20121107 - (djm) OpenBSD CVS Sync - eric@cvs.openbsd.org 2011/11/28 08:46:27 diff --git a/auth2-pubkey.c b/auth2-pubkey.c index f9cc6c2c6..70d8996ea 100644 --- a/auth2-pubkey.c +++ b/auth2-pubkey.c @@ -1,4 +1,4 @@ -/* $OpenBSD: auth2-pubkey.c,v 1.32 2012/11/04 10:38:43 djm Exp $ */ +/* $OpenBSD: auth2-pubkey.c,v 1.33 2012/11/14 02:24:27 djm Exp $ */ /* * Copyright (c) 2000 Markus Friedl. All rights reserved. * @@ -504,8 +504,8 @@ user_key_command_allowed2(struct passwd *user_pw, Key *key) goto out; } - debug3("Running AuthorizedKeysCommand: \"%s\" as \"%s\"", - options.authorized_keys_command, pw->pw_name); + debug3("Running AuthorizedKeysCommand: \"%s %s\" as \"%s\"", + options.authorized_keys_command, user_pw->pw_name, pw->pw_name); /* * Don't want to call this in the child, where it can fatal() and @@ -523,7 +523,19 @@ user_key_command_allowed2(struct passwd *user_pw, Key *key) for (i = 0; i < NSIG; i++) signal(i, SIG_DFL); + if ((devnull = open(_PATH_DEVNULL, O_RDWR)) == -1) { + error("%s: open %s: %s", __func__, _PATH_DEVNULL, + strerror(errno)); + _exit(1); + } + /* Keep stderr around a while longer to catch errors */ + if (dup2(devnull, STDIN_FILENO) == -1 || + dup2(p[1], STDOUT_FILENO) == -1) { + error("%s: dup2: %s", __func__, strerror(errno)); + _exit(1); + } closefrom(STDERR_FILENO + 1); + /* Don't use permanently_set_uid() here to avoid fatal() */ if (setresgid(pw->pw_gid, pw->pw_gid, pw->pw_gid) != 0) { error("setresgid %u: %s", (u_int)pw->pw_gid, @@ -535,22 +547,14 @@ user_key_command_allowed2(struct passwd *user_pw, Key *key) strerror(errno)); _exit(1); } - - close(p[0]); - if ((devnull = open(_PATH_DEVNULL, O_RDWR)) == -1) { - error("%s: open %s: %s", __func__, _PATH_DEVNULL, - strerror(errno)); - _exit(1); - } - if (dup2(devnull, STDIN_FILENO) == -1 || - dup2(p[1], STDOUT_FILENO) == -1 || - dup2(devnull, STDERR_FILENO) == -1) { + /* stdin is pointed to /dev/null at this point */ + if (dup2(STDIN_FILENO, STDERR_FILENO) == -1) { error("%s: dup2: %s", __func__, strerror(errno)); _exit(1); } execl(options.authorized_keys_command, - options.authorized_keys_command, pw->pw_name, NULL); + options.authorized_keys_command, user_pw->pw_name, NULL); error("AuthorizedKeysCommand %s exec failed: %s", options.authorized_keys_command, strerror(errno)); -- cgit v1.2.3 From 4018dc04daa613d0e20df10947b85f154dc5765d Mon Sep 17 00:00:00 2001 From: Damien Miller Date: Fri, 15 Feb 2013 10:28:55 +1100 Subject: - djm@cvs.openbsd.org 2013/02/14 21:35:59 [auth2-pubkey.c] Correct error message that had a typo and was logging the wrong thing; patch from Petr Lautrbach --- ChangeLog | 5 +++++ auth2-pubkey.c | 6 +++--- 2 files changed, 8 insertions(+), 3 deletions(-) (limited to 'auth2-pubkey.c') diff --git a/ChangeLog b/ChangeLog index 997982e61..2a2584661 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,6 +1,11 @@ 20130215 - (djm) [contrib/suse/rc.sshd] Use SSHD_BIN consistently; bz#2056 from Iain Morgan + - (djm) OpenBSD CVS Sync + - djm@cvs.openbsd.org 2013/02/14 21:35:59 + [auth2-pubkey.c] + Correct error message that had a typo and was logging the wrong thing; + patch from Petr Lautrbach 20130214 - (djm) [regress/krl.sh] Don't use ecdsa keys in environment that lack ECC. diff --git a/auth2-pubkey.c b/auth2-pubkey.c index 70d8996ea..3ff6faa8b 100644 --- a/auth2-pubkey.c +++ b/auth2-pubkey.c @@ -1,4 +1,4 @@ -/* $OpenBSD: auth2-pubkey.c,v 1.33 2012/11/14 02:24:27 djm Exp $ */ +/* $OpenBSD: auth2-pubkey.c,v 1.34 2013/02/14 21:35:59 djm Exp $ */ /* * Copyright (c) 2000 Markus Friedl. All rights reserved. * @@ -479,8 +479,8 @@ user_key_command_allowed2(struct passwd *user_pw, Key *key) "u", user_pw->pw_name, (char *)NULL); pw = getpwnam(username); if (pw == NULL) { - error("AuthorizedKeyCommandUser \"%s\" not found: %s", - options.authorized_keys_command, strerror(errno)); + error("AuthorizedKeysCommandUser \"%s\" not found: %s", + username, strerror(errno)); free(username); return 0; } -- cgit v1.2.3