Restricting users using one port

Darren Tucker dtucker at zip.com.au
Mon Oct 17 00:08:57 EST 2011


On Sun, Oct 09, 2011 at 09:50:05PM +1100, Damien Miller wrote:
> On Sun, 9 Oct 2011, Alex Bligh wrote:
> > I have ssh running on port 22 and (say) port 33333. Port 22 is restricted at
> > layer 3 so not much can get to it. Port 33333 is open to the world.
> > 
> > I only want to allow one user to authenticated using port 33333, but
> > all users to authenticate using port 22.
[...]
> 
> At the moment, no. It might be possible to add more Match options to
> select using the local connection address and port. E.g.
[...]
> Darren wrote most of the Match code - what do you think, Darren?

(apologies if this is a duplicate, my previous reply seems to have been
eaten somewhere)

It's feasible.  The initial Match processing is done just after the
client sends the username so both the local address and port are known
and there should be no additional hooks needed.

I'd suggest calling them LocalAddress and LocalPort (or ServerAddress
and ServerPort) though.

Attached are two patches: openssh-match-struct.patch which moves the
items that are checked to a struct, and
openssh-match-localaddrport.patch which implements the requested
functionality.  (You only need the latter to try it, the former is just
for review).

-- 
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: auth.c
===================================================================
RCS file: /home/dtucker/openssh/cvs/openssh/auth.c,v
retrieving revision 1.149
diff -u -p -r1.149 auth.c
--- auth.c	29 May 2011 11:40:42 -0000	1.149
+++ auth.c	10 Oct 2011 04:44:52 -0000
@@ -544,9 +544,12 @@ getpwnamallow(const char *user)
 #endif
 #endif
 	struct passwd *pw;
+	extern ConnectionInfo connection_info;
 
-	parse_server_match_config(&options, user,
-	    get_canonical_hostname(options.use_dns), get_remote_ipaddr());
+	connection_info.user = user;
+	connection_info.host = get_canonical_hostname(options.use_dns);
+	connection_info.address = get_remote_ipaddr();
+	parse_server_match_config(&options, &connection_info);
 
 #if defined(_AIX) && defined(HAVE_SETAUTHDB)
 	aix_setauthdb(user);
Index: servconf.c
===================================================================
RCS file: /home/dtucker/openssh/cvs/openssh/servconf.c,v
retrieving revision 1.220
diff -u -p -r1.220 servconf.c
--- servconf.c	2 Oct 2011 07:57:38 -0000	1.220
+++ servconf.c	10 Oct 2011 05:40:13 -0000
@@ -598,19 +598,19 @@ out:
 }
 
 static int
-match_cfg_line(char **condition, int line, const char *user, const char *host,
-    const char *address)
+match_cfg_line(char **condition, int line, ConnectionInfo *ci)
 {
 	int result = 1;
 	char *arg, *attrib, *cp = *condition;
 	size_t len;
 
-	if (user == NULL)
+	if (ci == NULL)
 		debug3("checking syntax for 'Match %s'", cp);
 	else
 		debug3("checking match for '%s' user %s host %s addr %s", cp,
-		    user ? user : "(null)", host ? host : "(null)",
-		    address ? address : "(null)");
+		    ci->user ? ci->user : "(null)",
+		    ci->host ? ci->host : "(null)",
+		    ci->address ? ci->address : "(null)");
 
 	while ((attrib = strdelim(&cp)) && *attrib != '\0') {
 		if ((arg = strdelim(&cp)) == NULL || *arg == '\0') {
@@ -619,37 +619,45 @@ match_cfg_line(char **condition, int lin
 		}
 		len = strlen(arg);
 		if (strcasecmp(attrib, "user") == 0) {
-			if (!user) {
+			if (ci == NULL || ci->user == NULL) {
 				result = 0;
 				continue;
 			}
-			if (match_pattern_list(user, arg, len, 0) != 1)
+			if (match_pattern_list(ci->user, arg, len, 0) != 1)
 				result = 0;
 			else
 				debug("user %.100s matched 'User %.100s' at "
-				    "line %d", user, arg, line);
+				    "line %d", ci->user, arg, line);
 		} else if (strcasecmp(attrib, "group") == 0) {
-			switch (match_cfg_line_group(arg, line, user)) {
+			if (ci == NULL || ci->user == NULL) {
+				result = 0;
+				continue;
+			}
+			switch (match_cfg_line_group(arg, line, ci->user)) {
 			case -1:
 				return -1;
 			case 0:
 				result = 0;
 			}
 		} else if (strcasecmp(attrib, "host") == 0) {
-			if (!host) {
+			if (ci == NULL || ci->host == NULL) {
 				result = 0;
 				continue;
 			}
-			if (match_hostname(host, arg, len) != 1)
+			if (match_hostname(ci->host, arg, len) != 1)
 				result = 0;
 			else
 				debug("connection from %.100s matched 'Host "
-				    "%.100s' at line %d", host, arg, line);
+				    "%.100s' at line %d", ci->host, arg, line);
 		} else if (strcasecmp(attrib, "address") == 0) {
-			switch (addr_match_list(address, arg)) {
+			if (ci == NULL || ci->address == NULL) {
+				result = 0;
+				continue;
+			}
+			switch (addr_match_list(ci->address, arg)) {
 			case 1:
 				debug("connection from %.100s matched 'Address "
-				    "%.100s' at line %d", address, arg, line);
+				    "%.100s' at line %d", ci->address, arg, line);
 				break;
 			case 0:
 			case -1:
@@ -663,7 +671,7 @@ match_cfg_line(char **condition, int lin
 			return -1;
 		}
 	}
-	if (user != NULL)
+	if (ci != NULL)
 		debug3("match %sfound", result ? "" : "not ");
 	*condition = cp;
 	return result;
@@ -710,8 +718,8 @@ static const struct multistate multistat
 
 int
 process_server_config_line(ServerOptions *options, char *line,
-    const char *filename, int linenum, int *activep, const char *user,
-    const char *host, const char *address)
+    const char *filename, int linenum, int *activep,
+    ConnectionInfo *connectinfo)
 {
 	char *cp, **charptr, *arg, *p;
 	int cmdline = 0, *intptr, value, value2, n;
@@ -742,7 +750,7 @@ process_server_config_line(ServerOptions
 	if (*activep && opcode != sMatch)
 		debug3("%s:%d setting %s %s", filename, linenum, arg, cp);
 	if (*activep == 0 && !(flags & SSHCFG_MATCH)) {
-		if (user == NULL) {
+		if (connectinfo == 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 */
@@ -1313,7 +1321,7 @@ process_server_config_line(ServerOptions
 		if (cmdline)
 			fatal("Match directive not supported as a command-line "
 			   "option");
-		value = match_cfg_line(&cp, linenum, user, host, address);
+		value = match_cfg_line(&cp, linenum, connectinfo);
 		if (value < 0)
 			fatal("%s line %d: Bad Match condition", filename,
 			    linenum);
@@ -1451,16 +1459,48 @@ load_server_config(const char *filename,
 }
 
 void
-parse_server_match_config(ServerOptions *options, const char *user,
-    const char *host, const char *address)
+parse_server_match_config(ServerOptions *options, ConnectionInfo *connectinfo)
 {
 	ServerOptions mo;
 
 	initialize_server_options(&mo);
-	parse_server_config(&mo, "reprocess config", &cfg, user, host, address);
+	parse_server_config(&mo, "reprocess config", &cfg, connectinfo);
 	copy_set_server_options(options, &mo, 0);
 }
 
+int parse_server_match_testspec(ConnectionInfo *ci, char *spec)
+{
+	char *p;
+
+	while ((p = strsep(&spec, ",")) && *p != '\0') {
+		if (strncmp(p, "addr=", 5) == 0)
+			ci->address = xstrdup(p + 5);
+		else if (strncmp(p, "host=", 5) == 0)
+			ci->host = xstrdup(p + 5);
+		else if (strncmp(p, "user=", 5) == 0)
+			ci->user = xstrdup(p + 5);
+		else {
+			fprintf(stderr, "Invalid test mode specification %s\n",
+			   p);
+			return -1;
+		}
+	}
+	return 0;
+}
+
+/*
+ * returns 1 for a complete spec, 0 for partial spec and -1 for an
+ * empty spec.
+ */
+int server_match_spec_complete(ConnectionInfo *ci)
+{
+	if (ci->user && ci->host && ci->address)
+		return 1;	/* complete */
+	if (!ci->user && !ci->host && !ci->address)
+		return -1;	/* empty */
+	return 0;	/* partial */
+}
+
 /* Helper macros */
 #define M_CP_INTOPT(n) do {\
 	if (src->n != -1) \
@@ -1534,7 +1574,7 @@ copy_set_server_options(ServerOptions *d
 
 void
 parse_server_config(ServerOptions *options, const char *filename, Buffer *conf,
-    const char *user, const char *host, const char *address)
+    ConnectionInfo *connectinfo)
 {
 	int active, linenum, bad_options = 0;
 	char *cp, *obuf, *cbuf;
@@ -1542,11 +1582,11 @@ parse_server_config(ServerOptions *optio
 	debug2("%s: config %s len %d", __func__, filename, buffer_len(conf));
 
 	obuf = cbuf = xstrdup(buffer_ptr(conf));
-	active = user ? 0 : 1;
+	active = connectinfo ? 0 : 1;
 	linenum = 1;
 	while ((cp = strsep(&cbuf, "\n")) != NULL) {
 		if (process_server_config_line(options, cp, filename,
-		    linenum++, &active, user, host, address) != 0)
+		    linenum++, &active, connectinfo) != 0)
 			bad_options++;
 	}
 	xfree(obuf);
Index: servconf.h
===================================================================
RCS file: /home/dtucker/openssh/cvs/openssh/servconf.h,v
retrieving revision 1.91
diff -u -p -r1.91 servconf.h
--- servconf.h	22 Jun 2011 22:30:03 -0000	1.91
+++ servconf.h	10 Oct 2011 04:27:54 -0000
@@ -168,6 +168,15 @@ typedef struct {
 	char   *authorized_principals_file;
 }       ServerOptions;
 
+
+/* Information about the incoming connection as used by Match */
+typedef struct {
+	const char *user;
+	const char *host;	/* possibly resolved hostname */
+	const char *address; 	/* remote address */
+} ConnectionInfo;
+
+
 /*
  * These are string config options that must be copied between the
  * Match sub-config and the main config, and must be sent from the
@@ -185,12 +194,13 @@ typedef struct {
 void	 initialize_server_options(ServerOptions *);
 void	 fill_default_server_options(ServerOptions *);
 int	 process_server_config_line(ServerOptions *, char *, const char *, int,
-	     int *, const char *, const char *, const char *);
+	     int *, ConnectionInfo *);
 void	 load_server_config(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 *);
+	     ConnectionInfo *);
+void	 parse_server_match_config(ServerOptions *, ConnectionInfo *);
+int	 parse_server_match_testspec(ConnectionInfo *, char *);
+int	 server_match_spec_complete(ConnectionInfo *);
 void	 copy_set_server_options(ServerOptions *, ServerOptions *, int);
 void	 dump_config(ServerOptions *);
 char	*derelativise_path(const char *);
Index: sshd.c
===================================================================
RCS file: /home/dtucker/openssh/cvs/openssh/sshd.c,v
retrieving revision 1.410
diff -u -p -r1.410 sshd.c
--- sshd.c	2 Oct 2011 08:10:14 -0000	1.410
+++ sshd.c	10 Oct 2011 04:45:55 -0000
@@ -143,6 +143,9 @@ extern char *__progname;
 /* Server configuration options. */
 ServerOptions options;
 
+/* Connection information used by Match */
+ConnectionInfo connection_info;
+
 /* Name of the server configuration file. */
 char *config_file_name = _PATH_SERVER_CONFIG_FILE;
 
@@ -1320,9 +1323,8 @@ main(int ac, char **av)
 	int opt, i, j, on = 1;
 	int sock_in = -1, sock_out = -1, newsock = -1;
 	const char *remote_ip;
-	char *test_user = NULL, *test_host = NULL, *test_addr = NULL;
 	int remote_port;
-	char *line, *p, *cp;
+	char *line;
 	int config_s[2] = { -1 , -1 };
 	u_int64_t ibytes, obytes;
 	mode_t new_umask;
@@ -1449,20 +1451,9 @@ main(int ac, char **av)
 			test_flag = 2;
 			break;
 		case 'C':
-			cp = optarg;
-			while ((p = strsep(&cp, ",")) && *p != '\0') {
-				if (strncmp(p, "addr=", 5) == 0)
-					test_addr = xstrdup(p + 5);
-				else if (strncmp(p, "host=", 5) == 0)
-					test_host = xstrdup(p + 5);
-				else if (strncmp(p, "user=", 5) == 0)
-					test_user = xstrdup(p + 5);
-				else {
-					fprintf(stderr, "Invalid test "
-					    "mode specification %s\n", p);
-					exit(1);
-				}
-			}
+			if (parse_server_match_testspec(&connection_info,
+			    optarg) == -1)
+				exit(1);
 			break;
 		case 'u':
 			utmp_len = (u_int)strtonum(optarg, 0, MAXHOSTNAMELEN+1, NULL);
@@ -1474,7 +1465,7 @@ main(int ac, char **av)
 		case 'o':
 			line = xstrdup(optarg);
 			if (process_server_config_line(&options, line,
-			    "command-line", 0, NULL, NULL, NULL, NULL) != 0)
+			    "command-line", 0, NULL, NULL) != 0)
 				exit(1);
 			xfree(line);
 			break;
@@ -1530,13 +1521,10 @@ main(int ac, char **av)
 	 * the parameters we need.  If we're not doing an extended test,
 	 * do not silently ignore connection test params.
 	 */
-	if (test_flag >= 2 &&
-	   (test_user != NULL || test_host != NULL || test_addr != NULL)
-	    && (test_user == NULL || test_host == NULL || test_addr == NULL))
+	if (test_flag >= 2 && server_match_spec_complete(&connection_info) == 0)
 		fatal("user, host and addr are all required when testing "
 		   "Match configs");
-	if (test_flag < 2 && (test_user != NULL || test_host != NULL ||
-	    test_addr != NULL))
+	if (test_flag < 2 && server_match_spec_complete(&connection_info) >= 0)
 		fatal("Config test connection parameter (-C) provided without "
 		   "test mode (-T)");
 
@@ -1548,7 +1536,7 @@ main(int ac, char **av)
 		load_server_config(config_file_name, &cfg);
 
 	parse_server_config(&options, rexeced_flag ? "rexec" : config_file_name,
-	    &cfg, NULL, NULL, NULL);
+	    &cfg, NULL);
 
 	seed_rng();
 
@@ -1710,9 +1698,8 @@ main(int ac, char **av)
 	}
 
 	if (test_flag > 1) {
-		if (test_user != NULL && test_addr != NULL && test_host != NULL)
-			parse_server_match_config(&options, test_user,
-			    test_host, test_addr);
+		if (server_match_spec_complete(&connection_info) == 1)
+			parse_server_match_config(&options, &connection_info);
 		dump_config(&options);
 	}
 
Index: regress/cfgmatch.sh
===================================================================
RCS file: /home/dtucker/openssh/cvs/openssh/regress/cfgmatch.sh,v
retrieving revision 1.8
diff -u -p -r1.8 cfgmatch.sh
--- regress/cfgmatch.sh	17 Aug 2011 02:08:18 -0000	1.8
+++ regress/cfgmatch.sh	11 Oct 2011 00:47:48 -0000
@@ -52,6 +52,11 @@ echo "PermitOpen 127.0.0.1:1" >>$OBJ/ssh
 echo "Match Address 127.0.0.1" >>$OBJ/sshd_proxy
 echo "PermitOpen 127.0.0.1:$PORT" >>$OBJ/sshd_proxy
 
+# Plus some that won't match just to exercise the parsers
+echo "Match Group nosuchgroup" >>$OBJ/sshd_config
+echo "Match Host nosuchhost" >>$OBJ/sshd_config
+
+
 start_sshd
 
 #set -x
-------------- next part --------------
Index: auth.c
===================================================================
RCS file: /usr/local/src/security/openssh/cvs/openssh/auth.c,v
retrieving revision 1.149
diff -u -p -r1.149 auth.c
--- auth.c	29 May 2011 11:40:42 -0000	1.149
+++ auth.c	10 Oct 2011 05:45:34 -0000
@@ -544,9 +544,14 @@ getpwnamallow(const char *user)
 #endif
 #endif
 	struct passwd *pw;
+	extern ConnectionInfo connection_info;
 
-	parse_server_match_config(&options, user,
-	    get_canonical_hostname(options.use_dns), get_remote_ipaddr());
+	connection_info.user = user;
+	connection_info.host = get_canonical_hostname(options.use_dns);
+	connection_info.address = get_remote_ipaddr();
+	connection_info.laddress = get_local_ipaddr(packet_get_connection_in());
+	connection_info.lport = get_local_port();
+	parse_server_match_config(&options, &connection_info);
 
 #if defined(_AIX) && defined(HAVE_SETAUTHDB)
 	aix_setauthdb(user);
Index: servconf.c
===================================================================
RCS file: /usr/local/src/security/openssh/cvs/openssh/servconf.c,v
retrieving revision 1.220
diff -u -p -r1.220 servconf.c
--- servconf.c	2 Oct 2011 07:57:38 -0000	1.220
+++ servconf.c	10 Oct 2011 06:12:53 -0000
@@ -598,19 +598,20 @@ out:
 }
 
 static int
-match_cfg_line(char **condition, int line, const char *user, const char *host,
-    const char *address)
+match_cfg_line(char **condition, int line, ConnectionInfo *ci)
 {
-	int result = 1;
+	int result = 1, port;
 	char *arg, *attrib, *cp = *condition;
 	size_t len;
 
-	if (user == NULL)
+	if (ci == NULL)
 		debug3("checking syntax for 'Match %s'", cp);
 	else
-		debug3("checking match for '%s' user %s host %s addr %s", cp,
-		    user ? user : "(null)", host ? host : "(null)",
-		    address ? address : "(null)");
+		debug3("checking match for '%s' user %s host %s addr %s "
+		    "laddr %s lport %d", cp, ci->user ? ci->user : "(null)",
+		    ci->host ? ci->host : "(null)",
+		    ci->address ? ci->address : "(null)",
+		    ci->laddress ? ci->laddress : "(null)", ci->lport);
 
 	while ((attrib = strdelim(&cp)) && *attrib != '\0') {
 		if ((arg = strdelim(&cp)) == NULL || *arg == '\0') {
@@ -619,37 +620,45 @@ match_cfg_line(char **condition, int lin
 		}
 		len = strlen(arg);
 		if (strcasecmp(attrib, "user") == 0) {
-			if (!user) {
+			if (ci == NULL || ci->user == NULL) {
 				result = 0;
 				continue;
 			}
-			if (match_pattern_list(user, arg, len, 0) != 1)
+			if (match_pattern_list(ci->user, arg, len, 0) != 1)
 				result = 0;
 			else
 				debug("user %.100s matched 'User %.100s' at "
-				    "line %d", user, arg, line);
+				    "line %d", ci->user, arg, line);
 		} else if (strcasecmp(attrib, "group") == 0) {
-			switch (match_cfg_line_group(arg, line, user)) {
+			if (ci == NULL || ci->user == NULL) {
+				result = 0;
+				continue;
+			}
+			switch (match_cfg_line_group(arg, line, ci->user)) {
 			case -1:
 				return -1;
 			case 0:
 				result = 0;
 			}
 		} else if (strcasecmp(attrib, "host") == 0) {
-			if (!host) {
+			if (ci == NULL || ci->host == NULL) {
 				result = 0;
 				continue;
 			}
-			if (match_hostname(host, arg, len) != 1)
+			if (match_hostname(ci->host, arg, len) != 1)
 				result = 0;
 			else
 				debug("connection from %.100s matched 'Host "
-				    "%.100s' at line %d", host, arg, line);
+				    "%.100s' at line %d", ci->host, arg, line);
 		} else if (strcasecmp(attrib, "address") == 0) {
-			switch (addr_match_list(address, arg)) {
+			if (ci == NULL || ci->address == NULL) {
+				result = 0;
+				continue;
+			}
+			switch (addr_match_list(ci->address, arg)) {
 			case 1:
 				debug("connection from %.100s matched 'Address "
-				    "%.100s' at line %d", address, arg, line);
+				    "%.100s' at line %d", ci->address, arg, line);
 				break;
 			case 0:
 			case -1:
@@ -658,12 +667,42 @@ match_cfg_line(char **condition, int lin
 			case -2:
 				return -1;
 			}
+		} else if (strcasecmp(attrib, "localaddress") == 0){
+			if (ci == NULL || ci->laddress == NULL) {
+				result = 0;
+				continue;
+			}
+			switch (addr_match_list(ci->laddress, arg)) {
+			case 1:
+				debug("connection from %.100s matched "
+				    "'LocalAddress %.100s' at line %d",
+				    ci->laddress, arg, line);
+				break;
+			case 0:
+			case -1:
+				result = 0;
+				break;
+			case -2:
+				return -1;
+			}
+		} else if (strcasecmp(attrib, "localport") == 0) {
+			if ((port = a2port(arg)) == -1) {
+				error("Invalid LocalPort '%s' on Match line",
+				    arg);
+				return -1;
+			}
+			if (ci == NULL) {
+				result = 0;
+				continue;
+			}
+			if (a2port(arg) == ci->lport)
+				result = 1;
 		} else {
 			error("Unsupported Match attribute %s", attrib);
 			return -1;
 		}
 	}
-	if (user != NULL)
+	if (ci != NULL)
 		debug3("match %sfound", result ? "" : "not ");
 	*condition = cp;
 	return result;
@@ -710,8 +749,8 @@ static const struct multistate multistat
 
 int
 process_server_config_line(ServerOptions *options, char *line,
-    const char *filename, int linenum, int *activep, const char *user,
-    const char *host, const char *address)
+    const char *filename, int linenum, int *activep,
+    ConnectionInfo *connectinfo)
 {
 	char *cp, **charptr, *arg, *p;
 	int cmdline = 0, *intptr, value, value2, n;
@@ -742,7 +781,7 @@ process_server_config_line(ServerOptions
 	if (*activep && opcode != sMatch)
 		debug3("%s:%d setting %s %s", filename, linenum, arg, cp);
 	if (*activep == 0 && !(flags & SSHCFG_MATCH)) {
-		if (user == NULL) {
+		if (connectinfo == 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 */
@@ -1313,7 +1352,7 @@ process_server_config_line(ServerOptions
 		if (cmdline)
 			fatal("Match directive not supported as a command-line "
 			   "option");
-		value = match_cfg_line(&cp, linenum, user, host, address);
+		value = match_cfg_line(&cp, linenum, connectinfo);
 		if (value < 0)
 			fatal("%s line %d: Bad Match condition", filename,
 			    linenum);
@@ -1451,16 +1490,57 @@ load_server_config(const char *filename,
 }
 
 void
-parse_server_match_config(ServerOptions *options, const char *user,
-    const char *host, const char *address)
+parse_server_match_config(ServerOptions *options, ConnectionInfo *connectinfo)
 {
 	ServerOptions mo;
 
 	initialize_server_options(&mo);
-	parse_server_config(&mo, "reprocess config", &cfg, user, host, address);
+	parse_server_config(&mo, "reprocess config", &cfg, connectinfo);
 	copy_set_server_options(options, &mo, 0);
 }
 
+int parse_server_match_testspec(ConnectionInfo *ci, char *spec)
+{
+	char *p;
+
+	while ((p = strsep(&spec, ",")) && *p != '\0') {
+		if (strncmp(p, "addr=", 5) == 0)
+			ci->address = xstrdup(p + 5);
+		else if (strncmp(p, "host=", 5) == 0)
+			ci->host = xstrdup(p + 5);
+		else if (strncmp(p, "user=", 5) == 0)
+			ci->user = xstrdup(p + 5);
+		else if (strncmp(p, "laddr=", 6) == 0)
+			ci->laddress = xstrdup(p + 6);
+		else if (strncmp(p, "lport=", 6) == 0)
+			ci->lport = a2port(p + 6);
+			if (ci->lport == -1) {
+				fprintf(stderr, "Invalid port '%s' in test mode"
+				   " specification %s\n", p+6, p);
+				return -1;
+			}
+		else {
+			fprintf(stderr, "Invalid test mode specification %s\n",
+			   p);
+			return -1;
+		}
+	}
+	return 0;
+}
+
+/*
+ * returns 1 for a complete spec, 0 for partial spec and -1 for an
+ * empty spec.
+ */
+int server_match_spec_complete(ConnectionInfo *ci)
+{
+	if (ci->user && ci->host && ci->address)
+		return 1;	/* complete */
+	if (!ci->user && !ci->host && !ci->address)
+		return -1;	/* empty */
+	return 0;	/* partial */
+}
+
 /* Helper macros */
 #define M_CP_INTOPT(n) do {\
 	if (src->n != -1) \
@@ -1534,7 +1614,7 @@ copy_set_server_options(ServerOptions *d
 
 void
 parse_server_config(ServerOptions *options, const char *filename, Buffer *conf,
-    const char *user, const char *host, const char *address)
+    ConnectionInfo *connectinfo)
 {
 	int active, linenum, bad_options = 0;
 	char *cp, *obuf, *cbuf;
@@ -1542,11 +1622,11 @@ parse_server_config(ServerOptions *optio
 	debug2("%s: config %s len %d", __func__, filename, buffer_len(conf));
 
 	obuf = cbuf = xstrdup(buffer_ptr(conf));
-	active = user ? 0 : 1;
+	active = connectinfo ? 0 : 1;
 	linenum = 1;
 	while ((cp = strsep(&cbuf, "\n")) != NULL) {
 		if (process_server_config_line(options, cp, filename,
-		    linenum++, &active, user, host, address) != 0)
+		    linenum++, &active, connectinfo) != 0)
 			bad_options++;
 	}
 	xfree(obuf);
Index: servconf.h
===================================================================
RCS file: /usr/local/src/security/openssh/cvs/openssh/servconf.h,v
retrieving revision 1.91
diff -u -p -r1.91 servconf.h
--- servconf.h	22 Jun 2011 22:30:03 -0000	1.91
+++ servconf.h	10 Oct 2011 06:05:56 -0000
@@ -168,6 +168,17 @@ typedef struct {
 	char   *authorized_principals_file;
 }       ServerOptions;
 
+
+/* Information about the incoming connection as used by Match */
+typedef struct {
+	const char *user;
+	const char *host;	/* possibly resolved hostname */
+	const char *address; 	/* remote address */
+	const char *laddress;	/* local address */
+	int lport;		/* local port */
+} ConnectionInfo;
+
+
 /*
  * These are string config options that must be copied between the
  * Match sub-config and the main config, and must be sent from the
@@ -185,12 +196,13 @@ typedef struct {
 void	 initialize_server_options(ServerOptions *);
 void	 fill_default_server_options(ServerOptions *);
 int	 process_server_config_line(ServerOptions *, char *, const char *, int,
-	     int *, const char *, const char *, const char *);
+	     int *, ConnectionInfo *);
 void	 load_server_config(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 *);
+	     ConnectionInfo *);
+void	 parse_server_match_config(ServerOptions *, ConnectionInfo *);
+int	 parse_server_match_testspec(ConnectionInfo *, char *);
+int	 server_match_spec_complete(ConnectionInfo *);
 void	 copy_set_server_options(ServerOptions *, ServerOptions *, int);
 void	 dump_config(ServerOptions *);
 char	*derelativise_path(const char *);
Index: sshd.8
===================================================================
RCS file: /usr/local/src/security/openssh/cvs/openssh/sshd.8,v
retrieving revision 1.225
diff -u -p -r1.225 sshd.8
--- sshd.8	2 Oct 2011 07:57:38 -0000	1.225
+++ sshd.8	10 Oct 2011 06:14:47 -0000
@@ -114,6 +114,8 @@ The connection parameters are supplied a
 The keywords are
 .Dq user ,
 .Dq host ,
+.Dq laddr ,
+.Dq lport ,
 and
 .Dq addr .
 All are required and may be supplied in any order, either with multiple
Index: sshd.c
===================================================================
RCS file: /usr/local/src/security/openssh/cvs/openssh/sshd.c,v
retrieving revision 1.410
diff -u -p -r1.410 sshd.c
--- sshd.c	2 Oct 2011 08:10:14 -0000	1.410
+++ sshd.c	10 Oct 2011 06:04:57 -0000
@@ -143,6 +143,9 @@ extern char *__progname;
 /* Server configuration options. */
 ServerOptions options;
 
+/* Connection information used by Match */
+ConnectionInfo connection_info;
+
 /* Name of the server configuration file. */
 char *config_file_name = _PATH_SERVER_CONFIG_FILE;
 
@@ -1320,9 +1323,8 @@ main(int ac, char **av)
 	int opt, i, j, on = 1;
 	int sock_in = -1, sock_out = -1, newsock = -1;
 	const char *remote_ip;
-	char *test_user = NULL, *test_host = NULL, *test_addr = NULL;
 	int remote_port;
-	char *line, *p, *cp;
+	char *line;
 	int config_s[2] = { -1 , -1 };
 	u_int64_t ibytes, obytes;
 	mode_t new_umask;
@@ -1449,20 +1451,9 @@ main(int ac, char **av)
 			test_flag = 2;
 			break;
 		case 'C':
-			cp = optarg;
-			while ((p = strsep(&cp, ",")) && *p != '\0') {
-				if (strncmp(p, "addr=", 5) == 0)
-					test_addr = xstrdup(p + 5);
-				else if (strncmp(p, "host=", 5) == 0)
-					test_host = xstrdup(p + 5);
-				else if (strncmp(p, "user=", 5) == 0)
-					test_user = xstrdup(p + 5);
-				else {
-					fprintf(stderr, "Invalid test "
-					    "mode specification %s\n", p);
-					exit(1);
-				}
-			}
+			if (parse_server_match_testspec(&connection_info,
+			    optarg) == -1)
+				exit(1);
 			break;
 		case 'u':
 			utmp_len = (u_int)strtonum(optarg, 0, MAXHOSTNAMELEN+1, NULL);
@@ -1474,7 +1465,7 @@ main(int ac, char **av)
 		case 'o':
 			line = xstrdup(optarg);
 			if (process_server_config_line(&options, line,
-			    "command-line", 0, NULL, NULL, NULL, NULL) != 0)
+			    "command-line", 0, NULL, NULL) != 0)
 				exit(1);
 			xfree(line);
 			break;
@@ -1530,13 +1521,10 @@ main(int ac, char **av)
 	 * the parameters we need.  If we're not doing an extended test,
 	 * do not silently ignore connection test params.
 	 */
-	if (test_flag >= 2 &&
-	   (test_user != NULL || test_host != NULL || test_addr != NULL)
-	    && (test_user == NULL || test_host == NULL || test_addr == NULL))
+	if (test_flag >= 2 && server_match_spec_complete(&connection_info) == 0)
 		fatal("user, host and addr are all required when testing "
 		   "Match configs");
-	if (test_flag < 2 && (test_user != NULL || test_host != NULL ||
-	    test_addr != NULL))
+	if (test_flag < 2 && server_match_spec_complete(&connection_info) >= 0)
 		fatal("Config test connection parameter (-C) provided without "
 		   "test mode (-T)");
 
@@ -1548,7 +1536,7 @@ main(int ac, char **av)
 		load_server_config(config_file_name, &cfg);
 
 	parse_server_config(&options, rexeced_flag ? "rexec" : config_file_name,
-	    &cfg, NULL, NULL, NULL);
+	    &cfg, NULL);
 
 	seed_rng();
 
@@ -1710,9 +1698,8 @@ main(int ac, char **av)
 	}
 
 	if (test_flag > 1) {
-		if (test_user != NULL && test_addr != NULL && test_host != NULL)
-			parse_server_match_config(&options, test_user,
-			    test_host, test_addr);
+		if (server_match_spec_complete(&connection_info) == 1)
+			parse_server_match_config(&options, &connection_info);
 		dump_config(&options);
 	}
 
Index: sshd_config.5
===================================================================
RCS file: /usr/local/src/security/openssh/cvs/openssh/sshd_config.5,v
retrieving revision 1.143
diff -u -p -r1.143 sshd_config.5
--- sshd_config.5	22 Sep 2011 11:37:13 -0000	1.143
+++ sshd_config.5	10 Oct 2011 06:15:33 -0000
@@ -675,6 +675,8 @@ The available criteria are
 .Cm User ,
 .Cm Group ,
 .Cm Host ,
+.Cm LocalAddress ,
+.Cm LocalPort ,
 and
 .Cm Address .
 The match patterns may consist of single entries or comma-separated


More information about the openssh-unix-dev mailing list