sshd config parser

Darren Tucker dtucker at zip.com.au
Mon Apr 3 23:22:41 EST 2006


Hi all.

New Match patch.  Old bugs out, new bugs in :-)  Feedback welcome.

-- 
Darren Tucker (dtucker at zip.com.au)
GPG key 8FF4FA69 / D9A3 86E9 7EEE AF4B B2D4  37C9 C982 80C7 8FF4 FA69
    Good judgement comes with experience. Unfortunately, the experience
usually comes from bad judgement.
-------------- next part --------------
Index: Makefile.in
===================================================================
RCS file: /usr/local/src/security/openssh/cvs/openssh_cvs/Makefile.in,v
retrieving revision 1.274
diff -u -p -r1.274 Makefile.in
--- Makefile.in	1 Jan 2006 08:47:05 -0000	1.274
+++ Makefile.in	2 Apr 2006 10:19:10 -0000
@@ -81,7 +81,7 @@ SSHDOBJS=sshd.o auth-rhosts.o auth-passw
 	auth.o auth1.o auth2.o auth-options.o session.o \
 	auth-chall.o auth2-chall.o groupaccess.o \
 	auth-skey.o auth-bsdauth.o auth2-hostbased.o auth2-kbdint.o \
-	auth2-none.o auth2-passwd.o auth2-pubkey.o \
+	auth2-none.o auth2-passwd.o auth2-pubkey.o cfgmatch.o \
 	monitor_mm.o monitor.o monitor_wrap.o kexdhs.o kexgexs.o \
 	auth-krb5.o \
 	auth2-gss.o gss-serv.o gss-serv-krb5.o \
Index: auth.c
===================================================================
RCS file: /usr/local/src/security/openssh/cvs/openssh_cvs/auth.c,v
retrieving revision 1.101
diff -u -p -r1.101 auth.c
--- auth.c	31 Aug 2005 16:59:49 -0000	1.101
+++ auth.c	2 Apr 2006 02:23:02 -0000
@@ -492,6 +492,9 @@ getpwnamallow(const char *user)
 #endif
 	struct passwd *pw;
 
+	parse_server_match_config(&options, user,
+	    get_canonical_hostname(options.use_dns), get_remote_ipaddr());
+
 	pw = getpwnam(user);
 	if (pw == NULL) {
 		logit("Invalid user %.100s from %.100s",
Index: cfgmatch.c
===================================================================
RCS file: cfgmatch.c
diff -N cfgmatch.c
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ cfgmatch.c	3 Apr 2006 10:23:55 -0000
@@ -0,0 +1,116 @@
+/*
+ * Copyright (c) 2006 Darren Tucker.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "includes.h"
+
+#include "match.h"
+#include "log.h"
+#include "misc.h"
+#include "canohost.h"
+#include "groupaccess.h"
+
+int
+match_cfg_line(char **condition, int line, const char *user, const char *host,
+    const char *address)
+{
+	int result = 1;
+	char *arg, *attrib, *cp = *condition, *grplist[1];
+	size_t len;
+	struct passwd *pw;
+
+	if (user == NULL)
+		debug3("checking syntax for 'Match %s'", cp);
+	else
+		debug3("checking match for '%s'", cp);
+
+	while ((attrib = strdelim(&cp)) && *attrib != '\0') {
+		if ((arg = strdelim(&cp)) == NULL || *arg == '\0') {
+			error("Missing Match criteria for %s", attrib);
+			return -1;
+		}
+		len = strlen(arg);
+		if (strcasecmp(attrib, "user") == 0) {
+			if (!user) {
+				result = 0;
+				continue;
+			}
+			if (match_pattern_list(user, arg, len, 0) != 1)
+				/* XXX what about negative match? */
+				result = 0;
+			else
+				debug("user %.100s matched 'User %.100s' at "
+				    "line %d", user, arg, line);
+		} else if (strcasecmp(attrib, "group") == 0) {
+			if (!user) {
+				result = 0;
+				continue;
+			}
+			grplist[0] = arg; /* XXX split on comma */
+			if ((pw = getpwnam(user)) == NULL) {
+				debug("Can't match group at line %d because "
+				    "user %.100s does not exist", line, user);
+				result = 0;
+			} else if (ga_init(pw->pw_name, pw->pw_gid) == 0) {
+				debug("Can't Match group because user %.100s "
+				    "not in any group at line %d", user,
+				    line);
+				result = 0;
+			} else if (ga_match(grplist, 1) != 1) {
+				debug("User %.100s does not match group "
+				    "%.100s at line %d", user, arg,
+				    line);
+				result = 0;
+			} else {
+				debug("User %.100s matched Group %.100s at "
+				    "line %d", user, arg, line);
+			}
+		} else if (strcasecmp(attrib, "host") == 0) {
+			if (!host) {
+				result = 0;
+				continue;
+			}
+			if (match_hostname(host, arg, len) != 1)
+				result = 0;
+			else
+				debug("connection from %.100s matched 'Host "
+				    "%.100s' at line %d", host, arg, line);
+		} else if (strcasecmp(attrib, "address") == 0) {
+			if (!address) {
+				result = 0;
+				continue;
+			}
+			if (match_hostname(address, arg, len) != 1)
+				result = 0;
+			else
+				debug("connection from %.100s matched 'Address "
+				    "%.100s' at line %d", address, arg, line);
+		} else {
+			error("Unsupported Match attribute %s", attrib);
+			return -1;
+		}
+	}
+	*condition = cp;
+	debug3("match_cfg_line: returning %d", result);
+	return result;
+}
Index: match.h
===================================================================
RCS file: /usr/local/src/security/openssh/cvs/openssh_cvs/match.h,v
retrieving revision 1.11
diff -u -p -r1.11 match.h
--- match.h	5 Mar 2002 01:42:43 -0000	1.11
+++ match.h	2 Apr 2006 01:27:11 -0000
@@ -20,5 +20,6 @@ int	 match_hostname(const char *, const 
 int	 match_host_and_ip(const char *, const char *, const char *);
 int	 match_user(const char *, const char *, const char *, const char *);
 char	*match_list(const char *, const char *, u_int *);
+int	 match_cfg_line(char **, int, const char *, const char *, const char *);
 
 #endif
Index: monitor.c
===================================================================
RCS file: /usr/local/src/security/openssh/cvs/openssh_cvs/monitor.c,v
retrieving revision 1.88
diff -u -p -r1.88 monitor.c
--- monitor.c	5 Nov 2005 04:07:05 -0000	1.88
+++ monitor.c	2 Apr 2006 01:08:28 -0000
@@ -612,6 +612,7 @@ mm_answer_pwnamallow(int sock, Buffer *m
 #endif
 	buffer_put_cstring(m, pwent->pw_dir);
 	buffer_put_cstring(m, pwent->pw_shell);
+	buffer_put_string(m, &options, sizeof(options));
 
  out:
 	debug3("%s: sending MONITOR_ANS_PWNAM: %d", __func__, allowed);
Index: monitor_wrap.c
===================================================================
RCS file: /usr/local/src/security/openssh/cvs/openssh_cvs/monitor_wrap.c,v
retrieving revision 1.54
diff -u -p -r1.54 monitor_wrap.c
--- monitor_wrap.c	29 Sep 2005 12:01:10 -0000	1.54
+++ monitor_wrap.c	3 Apr 2006 10:17:00 -0000
@@ -196,7 +196,8 @@ mm_getpwnamallow(const char *username)
 {
 	Buffer m;
 	struct passwd *pw;
-	u_int pwlen;
+	u_int len;
+	void *p;
 
 	debug3("%s entering", __func__);
 
@@ -212,8 +213,8 @@ mm_getpwnamallow(const char *username)
 		buffer_free(&m);
 		return (NULL);
 	}
-	pw = buffer_get_string(&m, &pwlen);
-	if (pwlen != sizeof(struct passwd))
+	pw = buffer_get_string(&m, &len);
+	if (len != sizeof(struct passwd))
 		fatal("%s: struct passwd size mismatch", __func__);
 	pw->pw_name = buffer_get_string(&m, NULL);
 	pw->pw_passwd = buffer_get_string(&m, NULL);
@@ -223,7 +224,28 @@ mm_getpwnamallow(const char *username)
 #endif
 	pw->pw_dir = buffer_get_string(&m, NULL);
 	pw->pw_shell = buffer_get_string(&m, NULL);
-	buffer_free(&m);
+	p = buffer_get_string(&m, &len);
+	if (len != sizeof(options))
+		fatal("%s: option block size mismatch", __func__);
+	memcpy(&options, p, len);
+	buffer_free(&m);
+
+	/* we don't use these options, so zero for safety */
+	options.num_ports = 0;
+	options.ports_from_cmdline = 0;
+	options.pid_file = NULL;
+	options.xauth_location = NULL;
+	options.ciphers = NULL;
+	options.num_allow_users = 0;
+	options.num_deny_users = 0;
+	options.num_allow_groups = 0;
+	options.num_deny_groups = 0;
+	options.macs = NULL;
+	options.num_subsystems = 0;
+	options.banner = NULL;
+	options.authorized_keys_file = NULL;
+	options.authorized_keys_file2 = NULL;
+	options.num_accept_env = 0;
 
 	return (pw);
 }
Index: servconf.c
===================================================================
RCS file: /usr/local/src/security/openssh/cvs/openssh_cvs/servconf.c,v
retrieving revision 1.136
diff -u -p -r1.136 servconf.c
--- servconf.c	13 Dec 2005 08:33:20 -0000	1.136
+++ servconf.c	3 Apr 2006 12:14:12 -0000
@@ -22,12 +22,14 @@ RCSID("$OpenBSD: servconf.c,v 1.146 2005
 #include "cipher.h"
 #include "kex.h"
 #include "mac.h"
+#include "match.h"
 
 static void add_listen_addr(ServerOptions *, char *, u_short);
 static void add_one_listen_addr(ServerOptions *, char *, u_short);
 
 /* Use of privilege separation or not */
 extern int use_privsep;
+extern Buffer cfg;
 
 /* Initializes the server options to their default values. */
 
@@ -102,9 +104,6 @@ initialize_server_options(ServerOptions 
 	options->authorized_keys_file2 = NULL;
 	options->num_accept_env = 0;
 	options->permit_tun = -1;
-
-	/* Needs to be accessable in many places */
-	use_privsep = -1;
 }
 
 void
@@ -274,110 +273,117 @@ typedef enum {
 	sHostbasedUsesNameFromPacketOnly, sClientAliveInterval,
 	sClientAliveCountMax, sAuthorizedKeysFile, sAuthorizedKeysFile2,
 	sGssAuthentication, sGssCleanupCreds, sAcceptEnv, sPermitTunnel,
+	sMatch,
 	sUsePrivilegeSeparation,
 	sDeprecated, sUnsupported
 } ServerOpCodes;
 
+#define SSHCFG_GLOBAL	0x01
+#define SSHCFG_MATCH	0x02
+#define SSHCFG_ALL	(SSHCFG_GLOBAL|SSHCFG_MATCH)
+
 /* Textual representation of the tokens. */
 static struct {
 	const char *name;
 	ServerOpCodes opcode;
+	u_int flags;
 } keywords[] = {
 	/* Portable-specific options */
 #ifdef USE_PAM
-	{ "usepam", sUsePAM },
+	{ "usepam", sUsePAM, SSHCFG_ALL },
 #else
-	{ "usepam", sUnsupported },
+	{ "usepam", sUnsupported, SSHCFG_ALL },
 #endif
-	{ "pamauthenticationviakbdint", sDeprecated },
+	{ "pamauthenticationviakbdint", sDeprecated, SSHCFG_GLOBAL },
 	/* Standard Options */
-	{ "port", sPort },
-	{ "hostkey", sHostKeyFile },
-	{ "hostdsakey", sHostKeyFile },					/* alias */
-	{ "pidfile", sPidFile },
-	{ "serverkeybits", sServerKeyBits },
-	{ "logingracetime", sLoginGraceTime },
-	{ "keyregenerationinterval", sKeyRegenerationTime },
-	{ "permitrootlogin", sPermitRootLogin },
-	{ "syslogfacility", sLogFacility },
-	{ "loglevel", sLogLevel },
-	{ "rhostsauthentication", sDeprecated },
-	{ "rhostsrsaauthentication", sRhostsRSAAuthentication },
-	{ "hostbasedauthentication", sHostbasedAuthentication },
-	{ "hostbasedusesnamefrompacketonly", sHostbasedUsesNameFromPacketOnly },
-	{ "rsaauthentication", sRSAAuthentication },
-	{ "pubkeyauthentication", sPubkeyAuthentication },
-	{ "dsaauthentication", sPubkeyAuthentication },			/* alias */
+	{ "port", sPort, SSHCFG_GLOBAL },
+	{ "hostkey", sHostKeyFile, SSHCFG_GLOBAL },
+	{ "hostdsakey", sHostKeyFile, SSHCFG_GLOBAL },		/* alias */
+	{ "pidfile", sPidFile, SSHCFG_GLOBAL },
+	{ "serverkeybits", sServerKeyBits, SSHCFG_GLOBAL },
+	{ "logingracetime", sLoginGraceTime, SSHCFG_ALL },
+	{ "keyregenerationinterval", sKeyRegenerationTime, SSHCFG_GLOBAL },
+	{ "permitrootlogin", sPermitRootLogin, SSHCFG_ALL },
+	{ "syslogfacility", sLogFacility, SSHCFG_ALL },
+	{ "loglevel", sLogLevel, SSHCFG_ALL },
+	{ "rhostsauthentication", sDeprecated, SSHCFG_GLOBAL },
+	{ "rhostsrsaauthentication", sRhostsRSAAuthentication, SSHCFG_ALL },
+	{ "hostbasedauthentication", sHostbasedAuthentication, SSHCFG_ALL },
+	{ "hostbasedusesnamefrompacketonly", sHostbasedUsesNameFromPacketOnly, SSHCFG_ALL },
+	{ "rsaauthentication", sRSAAuthentication, SSHCFG_ALL },
+	{ "pubkeyauthentication", sPubkeyAuthentication, SSHCFG_ALL },
+	{ "dsaauthentication", sPubkeyAuthentication, SSHCFG_ALL }, /* alias */
 #ifdef KRB5
-	{ "kerberosauthentication", sKerberosAuthentication },
-	{ "kerberosorlocalpasswd", sKerberosOrLocalPasswd },
-	{ "kerberosticketcleanup", sKerberosTicketCleanup },
+	{ "kerberosauthentication", sKerberosAuthentication, SSHCFG_ALL },
+	{ "kerberosorlocalpasswd", sKerberosOrLocalPasswd, SSHCFG_ALL },
+	{ "kerberosticketcleanup", sKerberosTicketCleanup, SSHCFG_ALL },
 #ifdef USE_AFS
-	{ "kerberosgetafstoken", sKerberosGetAFSToken },
+	{ "kerberosgetafstoken", sKerberosGetAFSToken, SSHCFG_ALL },
 #else
-	{ "kerberosgetafstoken", sUnsupported },
+	{ "kerberosgetafstoken", sUnsupported, SSHCFG_ALL },
 #endif
 #else
-	{ "kerberosauthentication", sUnsupported },
-	{ "kerberosorlocalpasswd", sUnsupported },
-	{ "kerberosticketcleanup", sUnsupported },
-	{ "kerberosgetafstoken", sUnsupported },
+	{ "kerberosauthentication", sUnsupported, SSHCFG_ALL },
+	{ "kerberosorlocalpasswd", sUnsupported, SSHCFG_ALL },
+	{ "kerberosticketcleanup", sUnsupported, SSHCFG_ALL },
+	{ "kerberosgetafstoken", sUnsupported, SSHCFG_ALL },
 #endif
-	{ "kerberostgtpassing", sUnsupported },
-	{ "afstokenpassing", sUnsupported },
+	{ "kerberostgtpassing", sUnsupported, SSHCFG_ALL },
+	{ "afstokenpassing", sUnsupported, SSHCFG_ALL },
 #ifdef GSSAPI
-	{ "gssapiauthentication", sGssAuthentication },
-	{ "gssapicleanupcredentials", sGssCleanupCreds },
+	{ "gssapiauthentication", sGssAuthentication, SSHCFG_ALL },
+	{ "gssapicleanupcredentials", sGssCleanupCreds, SSHCFG_ALL },
 #else
-	{ "gssapiauthentication", sUnsupported },
-	{ "gssapicleanupcredentials", sUnsupported },
+	{ "gssapiauthentication", sUnsupported, SSHCFG_ALL },
+	{ "gssapicleanupcredentials", sUnsupported, SSHCFG_ALL },
 #endif
-	{ "passwordauthentication", sPasswordAuthentication },
-	{ "kbdinteractiveauthentication", sKbdInteractiveAuthentication },
-	{ "challengeresponseauthentication", sChallengeResponseAuthentication },
-	{ "skeyauthentication", sChallengeResponseAuthentication }, /* alias */
-	{ "checkmail", sDeprecated },
-	{ "listenaddress", sListenAddress },
-	{ "addressfamily", sAddressFamily },
-	{ "printmotd", sPrintMotd },
-	{ "printlastlog", sPrintLastLog },
-	{ "ignorerhosts", sIgnoreRhosts },
-	{ "ignoreuserknownhosts", sIgnoreUserKnownHosts },
-	{ "x11forwarding", sX11Forwarding },
-	{ "x11displayoffset", sX11DisplayOffset },
-	{ "x11uselocalhost", sX11UseLocalhost },
-	{ "xauthlocation", sXAuthLocation },
-	{ "strictmodes", sStrictModes },
-	{ "permitemptypasswords", sEmptyPasswd },
-	{ "permituserenvironment", sPermitUserEnvironment },
-	{ "uselogin", sUseLogin },
-	{ "compression", sCompression },
-	{ "tcpkeepalive", sTCPKeepAlive },
-	{ "keepalive", sTCPKeepAlive },				/* obsolete alias */
-	{ "allowtcpforwarding", sAllowTcpForwarding },
-	{ "allowusers", sAllowUsers },
-	{ "denyusers", sDenyUsers },
-	{ "allowgroups", sAllowGroups },
-	{ "denygroups", sDenyGroups },
-	{ "ciphers", sCiphers },
-	{ "macs", sMacs },
-	{ "protocol", sProtocol },
-	{ "gatewayports", sGatewayPorts },
-	{ "subsystem", sSubsystem },
-	{ "maxstartups", sMaxStartups },
-	{ "maxauthtries", sMaxAuthTries },
-	{ "banner", sBanner },
-	{ "usedns", sUseDNS },
-	{ "verifyreversemapping", sDeprecated },
-	{ "reversemappingcheck", sDeprecated },
-	{ "clientaliveinterval", sClientAliveInterval },
-	{ "clientalivecountmax", sClientAliveCountMax },
-	{ "authorizedkeysfile", sAuthorizedKeysFile },
-	{ "authorizedkeysfile2", sAuthorizedKeysFile2 },
-	{ "useprivilegeseparation", sUsePrivilegeSeparation},
-	{ "acceptenv", sAcceptEnv },
-	{ "permittunnel", sPermitTunnel },
-	{ NULL, sBadOption }
+	{ "passwordauthentication", sPasswordAuthentication, SSHCFG_ALL },
+	{ "kbdinteractiveauthentication", sKbdInteractiveAuthentication, SSHCFG_ALL },
+	{ "challengeresponseauthentication", sChallengeResponseAuthentication, SSHCFG_ALL },
+	{ "skeyauthentication", sChallengeResponseAuthentication, SSHCFG_ALL }, /* alias */
+	{ "checkmail", sDeprecated, SSHCFG_GLOBAL },
+	{ "listenaddress", sListenAddress, SSHCFG_GLOBAL },
+	{ "addressfamily", sAddressFamily, SSHCFG_GLOBAL },
+	{ "printmotd", sPrintMotd, SSHCFG_ALL },
+	{ "printlastlog", sPrintLastLog, SSHCFG_ALL },
+	{ "ignorerhosts", sIgnoreRhosts, SSHCFG_ALL },
+	{ "ignoreuserknownhosts", sIgnoreUserKnownHosts, SSHCFG_ALL },
+	{ "x11forwarding", sX11Forwarding, SSHCFG_ALL },
+	{ "x11displayoffset", sX11DisplayOffset, SSHCFG_ALL },
+	{ "x11uselocalhost", sX11UseLocalhost, SSHCFG_ALL },
+	{ "xauthlocation", sXAuthLocation, SSHCFG_ALL },
+	{ "strictmodes", sStrictModes, SSHCFG_ALL },
+	{ "permitemptypasswords", sEmptyPasswd, SSHCFG_ALL },
+	{ "permituserenvironment", sPermitUserEnvironment, SSHCFG_ALL },
+	{ "uselogin", sUseLogin, SSHCFG_ALL },
+	{ "compression", sCompression, SSHCFG_GLOBAL },
+	{ "tcpkeepalive", sTCPKeepAlive, SSHCFG_GLOBAL },
+	{ "keepalive", sTCPKeepAlive, SSHCFG_GLOBAL },	/* obsolete alias */
+	{ "allowtcpforwarding", sAllowTcpForwarding, SSHCFG_ALL },
+	{ "allowusers", sAllowUsers, SSHCFG_GLOBAL },
+	{ "denyusers", sDenyUsers, SSHCFG_GLOBAL },
+	{ "allowgroups", sAllowGroups, SSHCFG_GLOBAL },
+	{ "denygroups", sDenyGroups, SSHCFG_GLOBAL },
+	{ "ciphers", sCiphers, SSHCFG_GLOBAL },
+	{ "macs", sMacs, SSHCFG_GLOBAL },
+	{ "protocol", sProtocol, SSHCFG_GLOBAL },
+	{ "gatewayports", sGatewayPorts, SSHCFG_ALL },
+	{ "subsystem", sSubsystem, SSHCFG_ALL },
+	{ "maxstartups", sMaxStartups, SSHCFG_GLOBAL },
+	{ "maxauthtries", sMaxAuthTries, SSHCFG_ALL },
+	{ "banner", sBanner, SSHCFG_ALL },
+	{ "usedns", sUseDNS, SSHCFG_GLOBAL },
+	{ "verifyreversemapping", sDeprecated, SSHCFG_GLOBAL },
+	{ "reversemappingcheck", sDeprecated, SSHCFG_GLOBAL },
+	{ "clientaliveinterval", sClientAliveInterval, SSHCFG_ALL },
+	{ "clientalivecountmax", sClientAliveCountMax, SSHCFG_ALL },
+	{ "authorizedkeysfile", sAuthorizedKeysFile, SSHCFG_ALL },
+	{ "authorizedkeysfile2", sAuthorizedKeysFile2, SSHCFG_ALL },
+	{ "useprivilegeseparation", sUsePrivilegeSeparation, SSHCFG_GLOBAL},
+	{ "acceptenv", sAcceptEnv, SSHCFG_ALL },
+	{ "permittunnel", sPermitTunnel, SSHCFG_ALL },
+	{ "match", sMatch, SSHCFG_ALL },
+	{ NULL, sBadOption, 0 }
 };
 
 /*
@@ -386,13 +392,15 @@ static struct {
 
 static ServerOpCodes
 parse_token(const char *cp, const char *filename,
-	    int linenum)
+	    int linenum, u_int *flags)
 {
 	u_int i;
 
 	for (i = 0; keywords[i].name; i++)
-		if (strcasecmp(cp, keywords[i].name) == 0)
+		if (strcasecmp(cp, keywords[i].name) == 0) {
+			*flags = keywords[i].flags;
 			return keywords[i].opcode;
+		}
 
 	error("%s: line %d: Bad configuration option: %s",
 	    filename, linenum, cp);
@@ -437,15 +445,49 @@ add_one_listen_addr(ServerOptions *optio
 	options->listen_addrs = aitop;
 }
 
+/*
+ * The strategy for the Match blocks is that the config file is parsed twice.
+ *
+ * The first time is at startup.  activep is initialized to 1 and the
+ * directives in the global context are processed and acted on.  Hitting a
+ * Match directive unsets activep and the directives inside the block are
+ * checked for syntax only.
+ *
+ * The second time is after a connection has been established but before
+ * authentication.  activep is initialized to 2 and global config directives
+ * are ignored since they have already been processed.  If the criteria in a
+ * Match block is met, activep is set and the subsequent directives
+ * processed and actioned until EOF or another Match block unsets it.  Any
+ * options set are copied into the main server config.
+ *
+ * Potential additions/improvements:
+ *  - Add Match support for pre-kex directives, eg Protocol, Ciphers.
+ *
+ *  - Add a Tag directive (idea from David Leonard) ala pf, eg:
+ *	Match Address 192.168.0.*
+ *		Tag trusted
+ *	Match Group wheel
+ *		Tag trusted
+ *	Match Tag trusted
+ *		AllowTcpForwarding yes
+ *		GatewayPorts clientspecified
+ *		[...]
+ *
+ *  - Add a PermittedChannelRequests directive
+ *	Match Group shell
+ *		PermittedChannelRequests session,forwarded-tcpip
+ */
+
 int
 process_server_config_line(ServerOptions *options, char *line,
-    const char *filename, int linenum)
+    const char *filename, int linenum, int *activep, const char *user,
+    const char *host, const char *address)
 {
 	char *cp, **charptr, *arg, *p;
-	int *intptr, value, n;
+	int cmdline = 0, *intptr, value, n;
 	ServerOpCodes opcode;
 	u_short port;
-	u_int i;
+	u_int i, flags = 0;
 
 	cp = line;
 	arg = strdelim(&cp);
@@ -456,7 +498,24 @@ process_server_config_line(ServerOptions
 		return 0;
 	intptr = NULL;
 	charptr = NULL;
-	opcode = parse_token(arg, filename, linenum);
+	opcode = parse_token(arg, filename, linenum, &flags);
+
+	if (activep == NULL) { /* We are processing a command line directive */
+		cmdline = 1;
+		activep = &cmdline;
+	}
+	debug3("match: line %s active %d flags %d", line, *activep, flags);
+	if (*activep == 0 && !(flags & SSHCFG_MATCH)) {
+		if (user == NULL) {
+			fatal("%s line %d: Directive '%s' is not allowed "
+			    "within a Match block", filename, linenum, arg);
+		} else { /* this is a directive we have already processed */
+			while (arg)
+				arg = strdelim(&cp);
+			return 0;
+		}
+	}
+
 	switch (opcode) {
 	/* Portable-specific options */
 	case sUsePAM:
@@ -494,7 +553,7 @@ parse_int:
 			fatal("%s line %d: missing integer value.",
 			    filename, linenum);
 		value = atoi(arg);
-		if (*intptr == -1)
+		if (*activep && *intptr == -1)
 			*intptr = value;
 		break;
 
@@ -574,7 +633,7 @@ parse_filename:
 		if (!arg || *arg == '\0')
 			fatal("%s line %d: missing file name.",
 			    filename, linenum);
-		if (*charptr == NULL) {
+		if (*activep && *charptr == NULL) {
 			*charptr = tilde_expand_filename(arg, getuid());
 			/* increase optional counter */
 			if (intptr != NULL)
@@ -625,7 +684,7 @@ parse_flag:
 		else
 			fatal("%s line %d: Bad yes/no argument: %s",
 				filename, linenum, arg);
-		if (*intptr == -1)
+		if (*activep && *intptr == -1)
 			*intptr = value;
 		break;
 
@@ -890,6 +949,10 @@ parse_flag:
 		if (!arg || *arg == '\0')
 			fatal("%s line %d: Missing subsystem name.",
 			    filename, linenum);
+		if (!*activep) {
+			arg = strdelim(&cp);
+			break;
+		}
 		for (i = 0; i < options->num_subsystems; i++)
 			if (strcmp(arg, options->subsystem_name[i]) == 0)
 				fatal("%s line %d: Subsystem '%s' already defined.",
@@ -961,6 +1024,8 @@ parse_flag:
 			if (options->num_accept_env >= MAX_ACCEPT_ENV)
 				fatal("%s line %d: too many allow env.",
 				    filename, linenum);
+			if (!*activep)
+				break;
 			options->accept_env[options->num_accept_env++] =
 			    xstrdup(arg);
 		}
@@ -988,6 +1053,17 @@ parse_flag:
 			*intptr = value;
 		break;
 
+	case sMatch:
+		if (cmdline)
+			fatal("Match directive not supported as a command-line "
+			   "option");
+		value = match_cfg_line(&cp, linenum, user, host, address);
+		if (value < 0)
+			fatal("%s line %d: Bad Match condition", filename,
+			    linenum);
+		*activep = value;
+		break;
+
 	case sDeprecated:
 		logit("%s line %d: Deprecated option %s",
 		    filename, linenum, arg);
@@ -1044,18 +1120,140 @@ load_server_config(const char *filename,
 }
 
 void
-parse_server_config(ServerOptions *options, const char *filename, Buffer *conf)
+parse_server_match_config(ServerOptions *options, const char *user,
+    const char *host, const char *address)
+{
+	u_int i;
+	ServerOptions mo;
+
+	initialize_server_options(&mo);
+	parse_server_config(&mo, "reprocess config", &cfg, user, host, address);
+
+	/* now copy any (supported) values set */
+	if (mo.use_pam != -1)
+		options->use_pam = mo.use_pam;
+	if (mo.num_accept_env > 0) {
+		for (i = 0; i < options->num_accept_env; i++)
+			xfree(options->accept_env[i]);
+		options->num_accept_env = 0;
+		for (i = 0; i < mo.num_accept_env; i++) {
+			if (options->num_accept_env >= MAX_ACCEPT_ENV)
+				fatal("Too many allow env in Match block.");
+			options->accept_env[options->num_accept_env++] =
+			    mo.accept_env[i];
+		}
+	}
+	if (mo.allow_tcp_forwarding != -1)
+		options->allow_tcp_forwarding = mo.allow_tcp_forwarding;
+	if (mo.authorized_keys_file != NULL) {
+		if (options->authorized_keys_file != NULL)
+			xfree(options->authorized_keys_file);
+		options->authorized_keys_file = mo.authorized_keys_file;
+	}
+	if (mo.authorized_keys_file2 != NULL) {
+		if (options->authorized_keys_file2 != NULL)
+			xfree(options->authorized_keys_file2);
+		options->authorized_keys_file2 = mo.authorized_keys_file2;
+	}
+	if (mo.banner != NULL) {
+		if (options->banner != NULL)
+			xfree(options->banner);
+		options->banner = mo.banner;
+	}
+	if (mo.password_authentication != -1)
+		options->password_authentication = mo.password_authentication;
+	if (mo.challenge_response_authentication != -1)
+		options->challenge_response_authentication =
+		    mo.challenge_response_authentication;
+	if (mo.client_alive_count_max != -1)
+		options->client_alive_count_max = mo.client_alive_count_max;
+	if (mo.client_alive_interval != -1)
+		options->client_alive_interval = mo.client_alive_interval;
+	if (mo.gateway_ports != -1)
+		options->gateway_ports = mo.gateway_ports;
+	if (mo.gss_authentication != -1)
+		options->gss_authentication = mo.gss_authentication;
+	if (mo.hostbased_authentication != -1)
+		options->hostbased_authentication = mo.hostbased_authentication;
+	if (mo.hostbased_uses_name_from_packet_only != -1)
+		options->hostbased_uses_name_from_packet_only =
+		    mo.hostbased_uses_name_from_packet_only;
+	if (mo.kerberos_authentication != -1)
+		options->kerberos_authentication = mo.kerberos_authentication;
+	if (mo.kerberos_or_local_passwd != -1)
+		options->kerberos_or_local_passwd = mo.kerberos_or_local_passwd;
+	if (mo.kerberos_ticket_cleanup != -1)
+		options->kerberos_ticket_cleanup = mo.kerberos_ticket_cleanup;
+	if (mo.kerberos_get_afs_token != -1)
+		options->kerberos_get_afs_token = mo.kerberos_get_afs_token;
+	if (mo.login_grace_time != -1)
+		options->login_grace_time = mo.login_grace_time;
+	if (mo.log_facility != -1)
+		options->log_facility = mo.log_facility;
+	if (mo.log_level != -1)
+		options->log_level = mo.log_level;
+	if (mo.permit_root_login != -1)
+		options->permit_root_login = mo.permit_root_login;
+	if (mo.max_authtries != -1)
+		options->max_authtries = mo.max_authtries;
+	if (mo.permit_empty_passwd != -1)
+		options->permit_empty_passwd = mo.permit_empty_passwd;
+	if (mo.permit_tun != -1)
+		options->permit_tun = mo.permit_tun;
+	if (mo.permit_user_env != -1)
+		options->permit_user_env = mo.permit_user_env;
+	if (mo.print_motd != -1)
+		options->print_motd = mo.print_motd;
+	if (mo.pubkey_authentication != -1)
+		options->pubkey_authentication = mo.pubkey_authentication;
+	if (mo.rsa_authentication != -1)
+		options->rsa_authentication = mo.rsa_authentication;
+	if (mo.strict_modes != -1)
+		options->strict_modes = mo.strict_modes;
+	if (mo.num_subsystems != 0) {  /* Not currently used */
+		for (i = 0; i < options->num_subsystems; i++) {
+			xfree(options->subsystem_name[i]);
+			xfree(options->subsystem_command[i]);
+		}
+		options->num_subsystems = 0;
+		for (i = 0; i < mo.num_subsystems; i++) {
+			options->subsystem_name[options->num_subsystems] =
+			    mo.subsystem_name[i];
+			options->subsystem_command[options->num_subsystems] =
+			    mo.subsystem_command[i];
+			options->num_subsystems++;
+		}
+	}
+	if (mo.use_login != -1)
+		options->use_login = mo.use_login;
+	if (mo.xauth_location != NULL) {
+		if (options->xauth_location != NULL)
+			xfree(options->xauth_location);
+		options->xauth_location = mo.xauth_location;
+	}
+	if (mo.x11_display_offset != -1)
+		options->x11_display_offset = mo.x11_display_offset;
+	if (mo.x11_forwarding != -1)
+		options->x11_forwarding = mo.x11_forwarding;
+	if (mo.x11_use_localhost != -1)
+		options->x11_use_localhost = mo.x11_use_localhost;
+}
+
+void
+parse_server_config(ServerOptions *options, const char *filename, Buffer *conf,
+    const char *user, const char *host, const char *address)
 {
-	int linenum, bad_options = 0;
+	int active, linenum, bad_options = 0;
 	char *cp, *obuf, *cbuf;
 
 	debug2("%s: config %s len %d", __func__, filename, buffer_len(conf));
 
 	obuf = cbuf = xstrdup(buffer_ptr(conf));
+	active = user ? 0 : 1;
 	linenum = 1;
 	while ((cp = strsep(&cbuf, "\n")) != NULL) {
 		if (process_server_config_line(options, cp, filename,
-		    linenum++) != 0)
+		    linenum++, &active, user, host, address) != 0)
 			bad_options++;
 	}
 	xfree(obuf);
Index: servconf.h
===================================================================
RCS file: /usr/local/src/security/openssh/cvs/openssh_cvs/servconf.h,v
retrieving revision 1.64
diff -u -p -r1.64 servconf.h
--- servconf.h	13 Dec 2005 08:29:03 -0000	1.64
+++ servconf.h	3 Apr 2006 11:31:16 -0000
@@ -141,8 +141,12 @@ typedef struct {
 
 void	 initialize_server_options(ServerOptions *);
 void	 fill_default_server_options(ServerOptions *);
-int	 process_server_config_line(ServerOptions *, char *, const char *, int);
+int	 process_server_config_line(ServerOptions *, char *, const char *, int,
+	     int *, const char *, const char *, const char *);
 void	 load_server_config(const char *, Buffer *);
-void	 parse_server_config(ServerOptions *, const char *, Buffer *);
+void	 parse_server_config(ServerOptions *, const char *, Buffer *,
+	     const char *, const char *, const char *);
+void	 parse_server_match_config(ServerOptions *, const char *, const char *,
+	     const char *);
 
 #endif				/* SERVCONF_H */
Index: sshd.c
===================================================================
RCS file: /usr/local/src/security/openssh/cvs/openssh_cvs/sshd.c,v
retrieving revision 1.320
diff -u -p -r1.320 sshd.c
--- sshd.c	24 Dec 2005 03:59:12 -0000	1.320
+++ sshd.c	3 Apr 2006 11:30:11 -0000
@@ -201,12 +201,15 @@ int *startup_pipes = NULL;
 int startup_pipe;		/* in child */
 
 /* variables used for privilege separation */
-int use_privsep;
+int use_privsep = -1;		/* Needs to be accessable in many places */
 struct monitor *pmonitor = NULL;
 
 /* global authentication context */
 Authctxt *the_authctxt = NULL;
 
+/* sshd_config buffer */
+Buffer cfg;
+
 /* message to be displayed after login */
 Buffer loginmsg;
 
@@ -892,7 +895,6 @@ main(int ac, char **av)
 	Key *key;
 	Authctxt *authctxt;
 	int ret, key_used = 0;
-	Buffer cfg;
 
 #ifdef HAVE_SECUREWARE
 	(void)set_auth_parameters(ac, av);
@@ -1011,7 +1013,7 @@ main(int ac, char **av)
 		case 'o':
 			line = xstrdup(optarg);
 			if (process_server_config_line(&options, line,
-			    "command-line", 0) != 0)
+			    "command-line", 0, NULL, NULL, NULL, NULL) != 0)
 				exit(1);
 			xfree(line);
 			break;
@@ -1069,11 +1071,8 @@ main(int ac, char **av)
 	else
 		load_server_config(config_file_name, &cfg);
 
-	parse_server_config(&options,
-	    rexeced_flag ? "rexec" : config_file_name, &cfg);
-
-	if (!rexec_flag)
-		buffer_free(&cfg);
+	parse_server_config(&options, rexeced_flag ? "rexec" : config_file_name,
+	    &cfg, NULL, NULL, NULL);
 
 	seed_rng();
 
Index: sshd_config.5
===================================================================
RCS file: /usr/local/src/security/openssh/cvs/openssh_cvs/sshd_config.5,v
retrieving revision 1.53
diff -u -p -r1.53 sshd_config.5
--- sshd_config.5	3 Jan 2006 07:47:31 -0000	1.53
+++ sshd_config.5	3 Apr 2006 11:38:30 -0000
@@ -433,6 +433,70 @@ for data integrity protection.
 Multiple algorithms must be comma-separated.
 The default is
 .Dq hmac-md5,hmac-sha1,hmac-ripemd160,hmac-sha1-96,hmac-md5-96 .
+.It Cm Match
+Introduces a conditional block.  Keywords on lines following a
+.Cm Match
+block are only applied if the criteria on the
+.Cm Match
+are satisfied.
+The the arguments to
+.Cm Match
+block are one or more criteria-pattern pairs.
+The available criteria are
+.Cm User ,
+.Cm Group ,
+.Cm Host ,
+and
+.Cm Address .
+Only a subset of keywords may be used on the lines following a
+.Cm Match
+keyword.
+Available keywords are
+.Cm AcceptEnv ,
+.Cm AllowTcpForwarding ,
+.Cm AuthorizedKeysFile ,
+.Cm AuthorizedKeysFile2 ,
+.Cm Banner ,
+.Cm ChallengeResponseAuthentication ,
+.Cm ChallengeResponseAuthentication ,
+.Cm ClientAliveCountMax ,
+.Cm ClientAliveInterval ,
+.Cm GatewayPorts ,
+.Cm GssAuthentication ,
+.Cm GssCleanupCreds ,
+.Cm HostbasedAuthentication ,
+.Cm HostbasedUsesNameFromPacketOnly ,
+.Cm IgnoreRhosts ,
+.Cm IgnoreUserKnownHosts ,
+.Cm KbdInteractiveAuthentication ,
+.Cm KerberosAuthentication ,
+.Cm KerberosGetAFSToken ,
+.Cm KerberosOrLocalPasswd ,
+.Cm KerberosTicketCleanup ,
+.Cm LogFacility ,
+.Cm LogLevel ,
+.Cm LoginGraceTime ,
+.Cm MaxAuthTries ,
+.Cm PasswordAuthentication ,
+.Cm PermitEmptyPasswd ,
+.Cm PermitRootLogin ,
+.Cm PermitTunnel ,
+.Cm PermitUserEnvironment ,
+.Cm PrintLastLog ,
+.Cm PrintMotd ,
+.Cm PubkeyAuthentication ,
+.Cm PubkeyAuthentication ,
+.Cm RSAAuthentication ,
+.Cm RhostsRSAAuthentication ,
+.Cm StrictModes ,
+.Cm Subsystem ,
+.Cm UseLogin ,
+.Cm UsePAM ,
+.Cm X11DisplayOffset ,
+.Cm X11Forwarding ,
+.Cm X11UseLocalhost ,
+and
+.Cm XAuthLocation .
 .It Cm MaxAuthTries
 Specifies the maximum number of authentication attempts permitted per
 connection.
@@ -827,6 +891,17 @@ Contains configuration data for
 This file should be writable by root only, but it is recommended
 (though not necessary) that it be world-readable.
 .El
+.Sh EXAMPLES
+To allow
+.Cm PasswordAuthentication
+only from the local private network:
+.Bd -literal -offset indent
+PasswordAuthentication no
+Match Address 192.168.0.*
+	PasswordAuthentication yes
+.Ed
+.Bl -tag -width Ds
+.Bl -tag -width Ds
 .Sh SEE ALSO
 .Xr sshd 8
 .Sh AUTHORS


More information about the openssh-unix-dev mailing list