OpenBSD CVS Sync
   - djm@cvs.openbsd.org 2011/05/23 03:30:07
     [auth-rsa.c auth.c auth.h auth2-pubkey.c monitor.c monitor_wrap.c pathnames.h servconf.c servconf.h sshd.8 sshd_config sshd_config.5]
     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@
diff --git a/ChangeLog b/ChangeLog
index 1aac69c..4e08289 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,16 @@
+20110529
+ - (djm) OpenBSD CVS Sync
+   - djm@cvs.openbsd.org 2011/05/23 03:30:07
+     [auth-rsa.c auth.c auth.h auth2-pubkey.c monitor.c monitor_wrap.c]
+     [pathnames.h servconf.c servconf.h sshd.8 sshd_config sshd_config.5]
+     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@
+
 20110520
  - (djm) [session.c] call setexeccon() before executing passwd for pw
    changes; bz#1891 reported by jchadima AT redhat.com; ok dtucker@
diff --git a/auth-rsa.c b/auth-rsa.c
index 4edaab0..4ab46cd 100644
--- a/auth-rsa.c
+++ b/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 <ylo@cs.hut.fi>
  * Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland
@@ -160,44 +160,27 @@
 	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;
@@ -235,7 +218,10 @@
 		}
 		/* 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;
 
@@ -264,11 +250,7 @@
 		break;
 	}
 
-	/* Restore the privileged uid. */
-	restore_uid();
-
 	/* Close the file. */
-	xfree(file);
 	fclose(f);
 
 	/* return key if allowed */
@@ -276,7 +258,33 @@
 		*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/auth.c b/auth.c
index be78f1a..cac12b2 100644
--- a/auth.c
+++ b/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.
  *
@@ -331,7 +331,7 @@
  *
  * This returns a buffer allocated by xmalloc.
  */
-static char *
+char *
 expand_authorized_keys(const char *filename, struct passwd *pw)
 {
 	char *file, ret[MAXPATHLEN];
@@ -355,12 +355,6 @@
 }
 
 char *
-authorized_keys_file(struct passwd *pw)
-{
-	return expand_authorized_keys(options.authorized_keys_file, pw);
-}
-
-char *
 authorized_principals_file(struct passwd *pw)
 {
 	if (options.authorized_principals_file == NULL)
diff --git a/auth.h b/auth.h
index 2273958..0d786c4 100644
--- a/auth.h
+++ b/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.
@@ -168,7 +168,7 @@
 int	verify_response(Authctxt *, const char *);
 void	abandon_challenge_response(Authctxt *);
 
-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/auth2-pubkey.c b/auth2-pubkey.c
index a97509c..137887e 100644
--- a/auth2-pubkey.c
+++ b/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.
  *
@@ -436,7 +436,7 @@
 int
 user_key_allowed(struct passwd *pw, Key *key)
 {
-	int success;
+	u_int success, i;
 	char *file;
 
 	if (auth_key_is_revoked(key))
@@ -448,9 +448,12 @@
 	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/monitor.c b/monitor.c
index c3a4183..4479e0a 100644
--- a/monitor.c
+++ b/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 <provos@citi.umich.edu>
  * Copyright 2002 Markus Friedl <markus@openbsd.org>
@@ -632,6 +632,7 @@
 	char *username;
 	struct passwd *pwent;
 	int allowed = 0;
+	u_int i;
 
 	debug3("%s", __func__);
 
@@ -676,9 +677,14 @@
 		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);
@@ -691,7 +697,6 @@
 		monitor_permit(mon_dispatch, MONITOR_REQ_AUTHSERV, 1);
 		monitor_permit(mon_dispatch, MONITOR_REQ_AUTH2_READ_BANNER, 1);
 	}
-
 #ifdef USE_PAM
 	if (options.use_pam)
 		monitor_permit(mon_dispatch, MONITOR_REQ_PAM_START, 1);
diff --git a/monitor_wrap.c b/monitor_wrap.c
index d3f2740..7a90b3b 100644
--- a/monitor_wrap.c
+++ b/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 <provos@citi.umich.edu>
  * Copyright 2002 Markus Friedl <markus@openbsd.org>
@@ -211,7 +211,7 @@
 {
 	Buffer m;
 	struct passwd *pw;
-	u_int len;
+	u_int len, i;
 	ServerOptions *newopts;
 
 	debug3("%s entering", __func__);
@@ -250,9 +250,14 @@
 		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/pathnames.h b/pathnames.h
index 787bdb6..c3d9abf 100644
--- a/pathnames.h
+++ b/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 <ylo@cs.hut.fi>
@@ -96,6 +96,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/servconf.c b/servconf.c
index daed26a..74710c4 100644
--- a/servconf.c
+++ b/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 <ylo@cs.hut.fi>, Espoo, Finland
  *                    All rights reserved
@@ -126,7 +126,7 @@
 	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;
@@ -263,8 +263,12 @@
 		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)
@@ -430,6 +434,7 @@
 	{ "clientaliveinterval", sClientAliveInterval, SSHCFG_GLOBAL },
 	{ "clientalivecountmax", sClientAliveCountMax, SSHCFG_GLOBAL },
 	{ "authorizedkeysfile", sAuthorizedKeysFile, SSHCFG_ALL },
+	{ "authorizedkeysfile2", sDeprecated, SSHCFG_ALL },
 	{ "useprivilegeseparation", sUsePrivilegeSeparation, SSHCFG_GLOBAL},
 	{ "acceptenv", sAcceptEnv, SSHCFG_GLOBAL },
 	{ "permittunnel", sPermitTunnel, SSHCFG_ALL },
@@ -1241,11 +1246,22 @@
 	 * 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.",
@@ -1464,6 +1480,12 @@
 		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.
@@ -1508,12 +1530,14 @@
 	 */
 	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,
@@ -1627,7 +1651,18 @@
 	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
@@ -1725,7 +1760,6 @@
 	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);
@@ -1738,6 +1772,8 @@
 	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/servconf.h b/servconf.h
index 953ef86..31e621b 100644
--- a/servconf.h
+++ b/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 <ylo@cs.hut.fi>
@@ -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 @@
 					 * 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;
 
@@ -171,8 +173,8 @@
 		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/sshd.8 b/sshd.8
index 5503b13..8e007d1 100644
--- a/sshd.8
+++ b/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
@@ -462,10 +462,12 @@
 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/sshd_config b/sshd_config
index 9b0d9fa..473e866 100644
--- a/sshd_config
+++ b/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.
@@ -44,7 +44,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/sshd_config.5 b/sshd_config.5
index c3d6df3..b23e0f7 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.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 @@
 .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.