From 85ac1baaa89d5c17e5d9c22d76753223d8c5eee8 Mon Sep 17 00:00:00 2001 From: Damien Miller Date: Mon, 23 May 2011 03:30:08 +0000 Subject: allow AuthorizedKeysFile to specify multiple files, separated by spaces. Bring back authorized_keys2 as a default search path (to avoid breaking existing users of this file), but override this in sshd_config so it will be no longer used on fresh installs. Maybe in 2015 we can remove it entierly :) feedback and ok markus@ dtucker@ --- usr.bin/ssh/auth-rsa.c | 70 ++++++++++++++++++++++++++-------------------- usr.bin/ssh/auth.c | 10 ++----- usr.bin/ssh/auth.h | 4 +-- usr.bin/ssh/auth2-pubkey.c | 13 +++++---- usr.bin/ssh/monitor.c | 9 ++++-- usr.bin/ssh/monitor_wrap.c | 9 ++++-- usr.bin/ssh/pathnames.h | 5 +++- usr.bin/ssh/servconf.c | 57 +++++++++++++++++++++++++++++-------- usr.bin/ssh/servconf.h | 8 ++++-- usr.bin/ssh/sshd.8 | 12 ++++---- usr.bin/ssh/sshd_config | 7 +++-- usr.bin/ssh/sshd_config.5 | 11 +++++--- 12 files changed, 139 insertions(+), 76 deletions(-) (limited to 'usr.bin/ssh') diff --git a/usr.bin/ssh/auth-rsa.c b/usr.bin/ssh/auth-rsa.c index bc382037000..d9fc56e4e7c 100644 --- a/usr.bin/ssh/auth-rsa.c +++ b/usr.bin/ssh/auth-rsa.c @@ -1,4 +1,4 @@ -/* $OpenBSD: auth-rsa.c,v 1.79 2010/12/03 23:55:27 djm Exp $ */ +/* $OpenBSD: auth-rsa.c,v 1.80 2011/05/23 03:30:07 djm Exp $ */ /* * Author: Tatu Ylonen * Copyright (c) 1995 Tatu Ylonen , Espoo, Finland @@ -157,44 +157,27 @@ auth_rsa_challenge_dialog(Key *key) return (success); } -/* - * check if there's user key matching client_n, - * return key if login is allowed, NULL otherwise - */ - -int -auth_rsa_key_allowed(struct passwd *pw, BIGNUM *client_n, Key **rkey) +static int +rsa_key_allowed_in_file(struct passwd *pw, char *file, + const BIGNUM *client_n, Key **rkey) { - char line[SSH_MAX_PUBKEY_BYTES], *file; + char line[SSH_MAX_PUBKEY_BYTES]; int allowed = 0; u_int bits; FILE *f; u_long linenum = 0; Key *key; - /* Temporarily use the user's uid. */ - temporarily_use_uid(pw); - - /* The authorized keys. */ - file = authorized_keys_file(pw); debug("trying public RSA key file %s", file); - f = auth_openkeyfile(file, pw, options.strict_modes); - if (!f) { - xfree(file); - restore_uid(); - return (0); - } - - /* Flag indicating whether the key is allowed. */ - allowed = 0; - - key = key_new(KEY_RSA1); + if ((f = auth_openkeyfile(file, pw, options.strict_modes)) == NULL) + return 0; /* * Go though the accepted keys, looking for the current key. If * found, perform a challenge-response dialog to verify that the * user really has the corresponding private key. */ + key = key_new(KEY_RSA1); while (read_keyfile_line(f, file, line, sizeof(line), &linenum) != -1) { char *cp; char *key_options; @@ -232,7 +215,10 @@ auth_rsa_key_allowed(struct passwd *pw, BIGNUM *client_n, Key **rkey) } /* cp now points to the comment part. */ - /* Check if the we have found the desired key (identified by its modulus). */ + /* + * Check if the we have found the desired key (identified + * by its modulus). + */ if (BN_cmp(key->rsa->n, client_n) != 0) continue; @@ -261,11 +247,7 @@ auth_rsa_key_allowed(struct passwd *pw, BIGNUM *client_n, Key **rkey) break; } - /* Restore the privileged uid. */ - restore_uid(); - /* Close the file. */ - xfree(file); fclose(f); /* return key if allowed */ @@ -273,7 +255,33 @@ auth_rsa_key_allowed(struct passwd *pw, BIGNUM *client_n, Key **rkey) *rkey = key; else key_free(key); - return (allowed); + + return allowed; +} + +/* + * check if there's user key matching client_n, + * return key if login is allowed, NULL otherwise + */ + +int +auth_rsa_key_allowed(struct passwd *pw, BIGNUM *client_n, Key **rkey) +{ + char *file; + u_int i, allowed = 0; + + temporarily_use_uid(pw); + + for (i = 0; !allowed && i < options.num_authkeys_files; i++) { + file = expand_authorized_keys( + options.authorized_keys_files[i], pw); + allowed = rsa_key_allowed_in_file(pw, file, client_n, rkey); + xfree(file); + } + + restore_uid(); + + return allowed; } /* diff --git a/usr.bin/ssh/auth.c b/usr.bin/ssh/auth.c index 3852843f4b7..9608b58c328 100644 --- a/usr.bin/ssh/auth.c +++ b/usr.bin/ssh/auth.c @@ -1,4 +1,4 @@ -/* $OpenBSD: auth.c,v 1.92 2011/05/11 04:47:06 djm Exp $ */ +/* $OpenBSD: auth.c,v 1.93 2011/05/23 03:30:07 djm Exp $ */ /* * Copyright (c) 2000 Markus Friedl. All rights reserved. * @@ -241,7 +241,7 @@ auth_root_allowed(char *method) * * This returns a buffer allocated by xmalloc. */ -static char * +char * expand_authorized_keys(const char *filename, struct passwd *pw) { char *file, ret[MAXPATHLEN]; @@ -264,12 +264,6 @@ expand_authorized_keys(const char *filename, struct passwd *pw) return (xstrdup(ret)); } -char * -authorized_keys_file(struct passwd *pw) -{ - return expand_authorized_keys(options.authorized_keys_file, pw); -} - char * authorized_principals_file(struct passwd *pw) { diff --git a/usr.bin/ssh/auth.h b/usr.bin/ssh/auth.h index d551097b8ff..5d63c575d31 100644 --- a/usr.bin/ssh/auth.h +++ b/usr.bin/ssh/auth.h @@ -1,4 +1,4 @@ -/* $OpenBSD: auth.h,v 1.68 2011/05/11 04:47:06 djm Exp $ */ +/* $OpenBSD: auth.h,v 1.69 2011/05/23 03:30:07 djm Exp $ */ /* * Copyright (c) 2000 Markus Friedl. All rights reserved. @@ -145,7 +145,7 @@ struct passwd * getpwnamallow(const char *user); char *get_challenge(Authctxt *); int verify_response(Authctxt *, const char *); -char *authorized_keys_file(struct passwd *); +char *expand_authorized_keys(const char *, struct passwd *pw); char *authorized_principals_file(struct passwd *); FILE *auth_openkeyfile(const char *, struct passwd *, int); diff --git a/usr.bin/ssh/auth2-pubkey.c b/usr.bin/ssh/auth2-pubkey.c index 20ad508a905..57cc295156b 100644 --- a/usr.bin/ssh/auth2-pubkey.c +++ b/usr.bin/ssh/auth2-pubkey.c @@ -1,4 +1,4 @@ -/* $OpenBSD: auth2-pubkey.c,v 1.28 2011/05/11 04:47:06 djm Exp $ */ +/* $OpenBSD: auth2-pubkey.c,v 1.29 2011/05/23 03:30:07 djm Exp $ */ /* * Copyright (c) 2000 Markus Friedl. All rights reserved. * @@ -435,7 +435,7 @@ user_cert_trusted_ca(struct passwd *pw, Key *key) int user_key_allowed(struct passwd *pw, Key *key) { - int success; + u_int success, i; char *file; if (auth_key_is_revoked(key)) @@ -447,9 +447,12 @@ user_key_allowed(struct passwd *pw, Key *key) if (success) return success; - file = authorized_keys_file(pw); - success = user_key_allowed2(pw, key, file); - xfree(file); + for (i = 0; !success && i < options.num_authkeys_files; i++) { + file = expand_authorized_keys( + options.authorized_keys_files[i], pw); + success = user_key_allowed2(pw, key, file); + xfree(file); + } return success; } diff --git a/usr.bin/ssh/monitor.c b/usr.bin/ssh/monitor.c index 0366da43c08..ca08555b9b0 100644 --- a/usr.bin/ssh/monitor.c +++ b/usr.bin/ssh/monitor.c @@ -1,4 +1,4 @@ -/* $OpenBSD: monitor.c,v 1.112 2011/05/20 03:25:45 djm Exp $ */ +/* $OpenBSD: monitor.c,v 1.113 2011/05/23 03:30:07 djm Exp $ */ /* * Copyright 2002 Niels Provos * Copyright 2002 Markus Friedl @@ -547,6 +547,7 @@ mm_answer_pwnamallow(int sock, Buffer *m) char *username; struct passwd *pwent; int allowed = 0; + u_int i; debug3("%s", __func__); @@ -589,9 +590,14 @@ mm_answer_pwnamallow(int sock, Buffer *m) if (options.x != NULL) \ buffer_put_cstring(m, options.x); \ } while (0) +#define M_CP_STRARRAYOPT(x, nx) do { \ + for (i = 0; i < options.nx; i++) \ + buffer_put_cstring(m, options.x[i]); \ + } while (0) /* See comment in servconf.h */ COPY_MATCH_STRING_OPTS(); #undef M_CP_STROPT +#undef M_CP_STRARRAYOPT debug3("%s: sending MONITOR_ANS_PWNAM: %d", __func__, allowed); mm_request_send(sock, MONITOR_ANS_PWNAM, m); @@ -605,7 +611,6 @@ mm_answer_pwnamallow(int sock, Buffer *m) monitor_permit(mon_dispatch, MONITOR_REQ_AUTH2_READ_BANNER, 1); } - return (0); } diff --git a/usr.bin/ssh/monitor_wrap.c b/usr.bin/ssh/monitor_wrap.c index 47f77667d13..5af3290d87b 100644 --- a/usr.bin/ssh/monitor_wrap.c +++ b/usr.bin/ssh/monitor_wrap.c @@ -1,4 +1,4 @@ -/* $OpenBSD: monitor_wrap.c,v 1.71 2011/05/20 03:25:45 djm Exp $ */ +/* $OpenBSD: monitor_wrap.c,v 1.72 2011/05/23 03:30:07 djm Exp $ */ /* * Copyright 2002 Niels Provos * Copyright 2002 Markus Friedl @@ -202,7 +202,7 @@ mm_getpwnamallow(const char *username) { Buffer m; struct passwd *pw; - u_int len; + u_int len, i; ServerOptions *newopts; debug3("%s entering", __func__); @@ -239,9 +239,14 @@ out: if (newopts->x != NULL) \ newopts->x = buffer_get_string(&m, NULL); \ } while (0) +#define M_CP_STRARRAYOPT(x, nx) do { \ + for (i = 0; i < newopts->nx; i++) \ + newopts->x[i] = buffer_get_string(&m, NULL); \ + } while (0) /* See comment in servconf.h */ COPY_MATCH_STRING_OPTS(); #undef M_CP_STROPT +#undef M_CP_STRARRAYOPT copy_set_server_options(&options, newopts, 1); xfree(newopts); diff --git a/usr.bin/ssh/pathnames.h b/usr.bin/ssh/pathnames.h index c7184eff3fb..1ce8fc5e5c0 100644 --- a/usr.bin/ssh/pathnames.h +++ b/usr.bin/ssh/pathnames.h @@ -1,4 +1,4 @@ -/* $OpenBSD: pathnames.h,v 1.21 2011/05/11 04:47:06 djm Exp $ */ +/* $OpenBSD: pathnames.h,v 1.22 2011/05/23 03:30:07 djm Exp $ */ /* * Author: Tatu Ylonen @@ -88,6 +88,9 @@ */ #define _PATH_SSH_USER_PERMITTED_KEYS ".ssh/authorized_keys" +/* backward compat for protocol v2 */ +#define _PATH_SSH_USER_PERMITTED_KEYS2 ".ssh/authorized_keys2" + /* * Per-user and system-wide ssh "rc" files. These files are executed with * /bin/sh before starting the shell or command if they exist. They will be diff --git a/usr.bin/ssh/servconf.c b/usr.bin/ssh/servconf.c index a2133e200a3..cb1db2de4c8 100644 --- a/usr.bin/ssh/servconf.c +++ b/usr.bin/ssh/servconf.c @@ -1,4 +1,4 @@ -/* $OpenBSD: servconf.c,v 1.218 2011/05/20 03:25:45 djm Exp $ */ +/* $OpenBSD: servconf.c,v 1.219 2011/05/23 03:30:07 djm Exp $ */ /* * Copyright (c) 1995 Tatu Ylonen , Espoo, Finland * All rights reserved @@ -119,7 +119,7 @@ initialize_server_options(ServerOptions *options) options->use_dns = -1; options->client_alive_interval = -1; options->client_alive_count_max = -1; - options->authorized_keys_file = NULL; + options->num_authkeys_files = 0; options->num_accept_env = 0; options->permit_tun = -1; options->num_permitted_opens = -1; @@ -249,8 +249,12 @@ fill_default_server_options(ServerOptions *options) options->client_alive_interval = 0; if (options->client_alive_count_max == -1) options->client_alive_count_max = 3; - if (options->authorized_keys_file == NULL) - options->authorized_keys_file = xstrdup(_PATH_SSH_USER_PERMITTED_KEYS); + if (options->num_authkeys_files == 0) { + options->authorized_keys_files[options->num_authkeys_files++] = + xstrdup(_PATH_SSH_USER_PERMITTED_KEYS); + options->authorized_keys_files[options->num_authkeys_files++] = + xstrdup(_PATH_SSH_USER_PERMITTED_KEYS2); + } if (options->permit_tun == -1) options->permit_tun = SSH_TUNMODE_NO; if (options->zero_knowledge_password_authentication == -1) @@ -286,7 +290,7 @@ typedef enum { sMaxStartups, sMaxAuthTries, sMaxSessions, sBanner, sUseDNS, sHostbasedAuthentication, sHostbasedUsesNameFromPacketOnly, sClientAliveInterval, - sClientAliveCountMax, sAuthorizedKeysFile, sAuthorizedKeysFile2, + sClientAliveCountMax, sAuthorizedKeysFile, sGssAuthentication, sGssCleanupCreds, sAcceptEnv, sPermitTunnel, sMatch, sPermitOpen, sForceCommand, sChrootDirectory, sUsePrivilegeSeparation, sAllowAgentForwarding, @@ -391,7 +395,7 @@ static struct { { "clientaliveinterval", sClientAliveInterval, SSHCFG_GLOBAL }, { "clientalivecountmax", sClientAliveCountMax, SSHCFG_GLOBAL }, { "authorizedkeysfile", sAuthorizedKeysFile, SSHCFG_ALL }, - { "authorizedkeysfile2", sAuthorizedKeysFile2, SSHCFG_ALL }, + { "authorizedkeysfile2", sDeprecated, SSHCFG_ALL }, { "useprivilegeseparation", sUsePrivilegeSeparation, SSHCFG_GLOBAL}, { "acceptenv", sAcceptEnv, SSHCFG_GLOBAL }, { "permittunnel", sPermitTunnel, SSHCFG_ALL }, @@ -1197,11 +1201,22 @@ process_server_config_line(ServerOptions *options, char *line, * AuthorizedKeysFile /etc/ssh_keys/%u */ case sAuthorizedKeysFile: - charptr = &options->authorized_keys_file; - goto parse_tilde_filename; + if (*activep && options->num_authkeys_files == 0) { + while ((arg = strdelim(&cp)) && *arg != '\0') { + if (options->num_authkeys_files >= + MAX_AUTHKEYS_FILES) + fatal("%s line %d: " + "too many authorized keys files.", + filename, linenum); + options->authorized_keys_files[ + options->num_authkeys_files++] = + tilde_expand_filename(arg, getuid()); + } + } + return 0; + case sAuthorizedPrincipalsFile: charptr = &options->authorized_principals_file; - parse_tilde_filename: arg = strdelim(&cp); if (!arg || *arg == '\0') fatal("%s line %d: missing file name.", @@ -1420,6 +1435,12 @@ parse_server_match_config(ServerOptions *options, const char *user, dst->n = src->n; \ } \ } while(0) +#define M_CP_STRARRAYOPT(n, num_n) do {\ + if (src->num_n != 0) { \ + for (dst->num_n = 0; dst->num_n < src->num_n; dst->num_n++) \ + dst->n[dst->num_n] = xstrdup(src->n[dst->num_n]); \ + } \ +} while(0) /* * Copy any supported values that are set. @@ -1464,12 +1485,14 @@ copy_set_server_options(ServerOptions *dst, ServerOptions *src, int preauth) */ if (preauth) return; + M_CP_STROPT(adm_forced_command); M_CP_STROPT(chroot_directory); } #undef M_CP_INTOPT #undef M_CP_STROPT +#undef M_CP_STRARRAYOPT void parse_server_config(ServerOptions *options, const char *filename, Buffer *conf, @@ -1583,7 +1606,18 @@ dump_cfg_strarray(ServerOpCodes code, u_int count, char **vals) u_int i; for (i = 0; i < count; i++) - printf("%s %s\n", lookup_opcode_name(code), vals[i]); + printf("%s %s\n", lookup_opcode_name(code), vals[i]); +} + +static void +dump_cfg_strarray_oneline(ServerOpCodes code, u_int count, char **vals) +{ + u_int i; + + printf("%s", lookup_opcode_name(code)); + for (i = 0; i < count; i++) + printf(" %s", vals[i]); + printf("\n"); } void @@ -1676,7 +1710,6 @@ dump_config(ServerOptions *o) dump_cfg_string(sCiphers, o->ciphers); dump_cfg_string(sMacs, o->macs); dump_cfg_string(sBanner, o->banner); - dump_cfg_string(sAuthorizedKeysFile, o->authorized_keys_file); dump_cfg_string(sForceCommand, o->adm_forced_command); dump_cfg_string(sChrootDirectory, o->chroot_directory); dump_cfg_string(sTrustedUserCAKeys, o->trusted_user_ca_keys); @@ -1689,6 +1722,8 @@ dump_config(ServerOptions *o) dump_cfg_string(sLogFacility, log_facility_name(o->log_facility)); /* string array arguments */ + dump_cfg_strarray_oneline(sAuthorizedKeysFile, o->num_authkeys_files, + o->authorized_keys_files); dump_cfg_strarray(sHostKeyFile, o->num_host_key_files, o->host_key_files); dump_cfg_strarray(sHostKeyFile, o->num_host_cert_files, diff --git a/usr.bin/ssh/servconf.h b/usr.bin/ssh/servconf.h index 04c5dead19d..84cc33c0c6a 100644 --- a/usr.bin/ssh/servconf.h +++ b/usr.bin/ssh/servconf.h @@ -1,4 +1,4 @@ -/* $OpenBSD: servconf.h,v 1.97 2011/05/20 03:25:45 djm Exp $ */ +/* $OpenBSD: servconf.h,v 1.98 2011/05/23 03:30:07 djm Exp $ */ /* * Author: Tatu Ylonen @@ -27,6 +27,7 @@ #define MAX_HOSTCERTS 256 /* Max # host certificates. */ #define MAX_ACCEPT_ENV 256 /* Max # of env vars. */ #define MAX_MATCH_GROUPS 256 /* Max # of groups for Match. */ +#define MAX_AUTHKEYS_FILES 256 /* Max # of authorized_keys files. */ /* permit_root_login */ #define PERMIT_NOT_SET -1 @@ -145,7 +146,8 @@ typedef struct { * disconnect the session */ - char *authorized_keys_file; /* File containing public keys */ + u_int num_authkeys_files; /* Files containing public keys */ + char *authorized_keys_files[MAX_AUTHKEYS_FILES]; char *adm_forced_command; @@ -169,8 +171,8 @@ typedef struct { M_CP_STROPT(banner); \ M_CP_STROPT(trusted_user_ca_keys); \ M_CP_STROPT(revoked_keys_file); \ - M_CP_STROPT(authorized_keys_file); \ M_CP_STROPT(authorized_principals_file); \ + M_CP_STRARRAYOPT(authorized_keys_files, num_authkeys_files); \ } while (0) void initialize_server_options(ServerOptions *); diff --git a/usr.bin/ssh/sshd.8 b/usr.bin/ssh/sshd.8 index 51dd2f01139..3ca4c409f29 100644 --- a/usr.bin/ssh/sshd.8 +++ b/usr.bin/ssh/sshd.8 @@ -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.8,v 1.260 2010/10/28 18:33:28 jmc Exp $ -.Dd $Mdocdate: October 28 2010 $ +.\" $OpenBSD: sshd.8,v 1.261 2011/05/23 03:30:07 djm Exp $ +.Dd $Mdocdate: May 23 2011 $ .Dt SSHD 8 .Os .Sh NAME @@ -435,10 +435,12 @@ is run, and if that does not exist either, xauth is used to add the cookie. .Sh AUTHORIZED_KEYS FILE FORMAT .Cm AuthorizedKeysFile -specifies the file containing public keys for +specifies the file or files containing public keys for public key authentication; -if none is specified, the default is -.Pa ~/.ssh/authorized_keys . +if none is specified, the default is both +.Pa ~/.ssh/authorized_keys +and +.Pa ~/.ssh/authorized_keys2 . Each line of the file contains one key (empty lines and lines starting with a .Ql # diff --git a/usr.bin/ssh/sshd_config b/usr.bin/ssh/sshd_config index 7d83b3c84b4..52fd8622fc2 100644 --- a/usr.bin/ssh/sshd_config +++ b/usr.bin/ssh/sshd_config @@ -1,4 +1,4 @@ -# $OpenBSD: sshd_config,v 1.83 2011/05/06 01:03:35 dtucker Exp $ +# $OpenBSD: sshd_config,v 1.84 2011/05/23 03:30:07 djm Exp $ # This is the sshd server system-wide configuration file. See # sshd_config(5) for more information. @@ -42,7 +42,10 @@ #RSAAuthentication yes #PubkeyAuthentication yes -#AuthorizedKeysFile .ssh/authorized_keys + +# The default is to check both .ssh/authorized_keys and .ssh/authorized_keys2 +# but this is overridden so installations will only check .ssh/authorized_keys +AuthorizedKeysFile .ssh/authorized_keys # For this to work you will also need host keys in /etc/ssh/ssh_known_hosts #RhostsRSAAuthentication no diff --git a/usr.bin/ssh/sshd_config.5 b/usr.bin/ssh/sshd_config.5 index a5dba2324f7..4f584996507 100644 --- a/usr.bin/ssh/sshd_config.5 +++ b/usr.bin/ssh/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.131 2010/12/08 04:02:47 djm Exp $ -.Dd $Mdocdate: December 8 2010 $ +.\" $OpenBSD: sshd_config.5,v 1.132 2011/05/23 03:30:07 djm Exp $ +.Dd $Mdocdate: May 23 2011 $ .Dt SSHD_CONFIG 5 .Os .Sh NAME @@ -168,8 +168,11 @@ After expansion, .Cm AuthorizedKeysFile is taken to be an absolute path or one relative to the user's home directory. -The default is -.Dq .ssh/authorized_keys . +The default is both +.Dq .ssh/authorized_keys +and +.Dq .ssh/authorized_keys2 . +Multiple files may be listed separated by whitespace. .It Cm AuthorizedPrincipalsFile Specifies a file that lists principal names that are accepted for certificate authentication. -- cgit v1.2.3