From: Darren Tucker
Date: 2019-04-18 19:42:28 EST
Subject: [PATCH] ssh-T

---
 regress/cfgmatch.sh | 47 +++++++++++++++++++++++++++++++++++++++++++--
 servconf.c          | 14 ++++++++------
 servconf.h          |  2 ++
 sshd.c              |  1 +
 4 files changed, 56 insertions(+), 8 deletions(-)

diff --git a/regress/cfgmatch.sh b/regress/cfgmatch.sh
index dd11e40..37fe6f8 100644
--- a/regress/cfgmatch.sh
+++ b/regress/cfgmatch.sh
@@ -51,9 +51,10 @@ echo "AuthorizedKeysFile /dev/null $OBJ/authorized_keys_%u" >>$OBJ/sshd_proxy
 echo "Match Address 127.0.0.1" >>$OBJ/sshd_proxy
 echo "PermitOpen 127.0.0.1:2 127.0.0.1:3 127.0.0.1:$PORT" >>$OBJ/sshd_proxy
 
-start_sshd
+${SUDO} ${SSHD} -f $OBJ/sshd_config -T >/dev/null || \
+    fail "config w/match fails config test"
 
-#set -x
+start_sshd
 
 # Test Match + PermitOpen in sshd_config.  This should be permitted
 trace "match permitopen localhost"
@@ -113,3 +114,45 @@ start_client -F $OBJ/ssh_proxy
 ${SSH} -q -p $fwdport -F $OBJ/ssh_config somehost true || \
     fail "nomatch override permitopen"
 stop_client
+
+
+# Test parsing of available Match criteria (with the exception of Group which
+# requires knowledge of actual group memberships user running the test).
+params="user:user:u1 host:host:h1 address:addr:1.2.3.4 \
+    localaddress:laddr:5.6.7.8 rdomain:rdomain:rdom1"
+cp $OBJ/sshd_proxy_bak $OBJ/sshd_config
+echo 'Banner /nomatch' >>$OBJ/sshd_config
+for i in $params; do
+	config=`echo $i | cut -f1 -d:`
+	criteria=`echo $i | cut -f2 -d:`
+	value=`echo $i | cut -f3 -d:`
+	cat >>$OBJ/sshd_config <<EOD
+	    Match $config $value
+	      Banner /$value
+EOD
+done
+${SUDO} ${SSHD} -f $OBJ/sshd_config -T >/dev/null || \
+    fail "validate config for w/out spec"
+
+# Test matching each criteria.
+for i in $params; do
+	testcriteria=`echo $i | cut -f2 -d:`
+	expected=/`echo $i | cut -f3 -d:`
+	spec=""
+	for j in $params; do
+		config=`echo $j | cut -f1 -d:`
+		criteria=`echo $j | cut -f2 -d:`
+		value=`echo $j | cut -f3 -d:`
+		if [ "$criteria" = "$testcriteria" ]; then
+			spec="$criteria=$value,$spec"
+		else
+			spec="$criteria=1$value,$spec"
+		fi
+	done
+	trace "test spec $spec"
+	result=`${SUDO} ${SSHD} -f $OBJ/sshd_config -T -C "$spec" | \
+	    awk '$1=="banner"{print $2}'`
+	if [ "$result" != "$expected" ]; then
+		fail "match $config expected $expected got $result"
+	fi
+done
diff --git a/servconf.c b/servconf.c
index 434f0bc..9f363c9 100644
--- a/servconf.c
+++ b/servconf.c
@@ -1075,7 +1075,7 @@ match_cfg_line(char **condition, int line, struct connection_info *ci)
 			return -1;
 		}
 		if (strcasecmp(attrib, "user") == 0) {
-			if (ci == NULL) {
+			if (ci == NULL || (ci->test && ci->user == NULL)) {
 				result = 0;
 				continue;
 			}
@@ -1087,7 +1087,7 @@ match_cfg_line(char **condition, int line, struct connection_info *ci)
 				debug("user %.100s matched 'User %.100s' at "
 				    "line %d", ci->user, arg, line);
 		} else if (strcasecmp(attrib, "group") == 0) {
-			if (ci == NULL) {
+			if (ci == NULL || (ci->test && ci->user == NULL)) {
 				result = 0;
 				continue;
 			}
@@ -1100,7 +1100,7 @@ match_cfg_line(char **condition, int line, struct connection_info *ci)
 				result = 0;
 			}
 		} else if (strcasecmp(attrib, "host") == 0) {
-			if (ci == NULL) {
+			if (ci == NULL || (ci->test && ci->host == NULL)) {
 				result = 0;
 				continue;
 			}
@@ -1112,7 +1112,7 @@ match_cfg_line(char **condition, int line, struct connection_info *ci)
 				debug("connection from %.100s matched 'Host "
 				    "%.100s' at line %d", ci->host, arg, line);
 		} else if (strcasecmp(attrib, "address") == 0) {
-			if (ci == NULL) {
+			if (ci == NULL || (ci->test && ci->address == NULL)) {
 				result = 0;
 				continue;
 			}
@@ -1131,7 +1131,7 @@ match_cfg_line(char **condition, int line, struct connection_info *ci)
 				return -1;
 			}
 		} else if (strcasecmp(attrib, "localaddress") == 0){
-			if (ci == NULL) {
+			if (ci == NULL || (ci->test && ci->laddress == NULL)) {
 				result = 0;
 				continue;
 			}
@@ -1157,7 +1157,7 @@ match_cfg_line(char **condition, int line, struct connection_info *ci)
 				    arg);
 				return -1;
 			}
-			if (ci == NULL) {
+			if (ci == NULL || (ci->test && ci->lport == -1)) {
 				result = 0;
 				continue;
 			}
@@ -1175,6 +1175,8 @@ match_cfg_line(char **condition, int line, struct connection_info *ci)
 				result = 0;
 				continue;
 			}
+			if (ci->rdomain == NULL)
+				match_test_missing_fatal("RDomain", "rdomain");
 			if (match_pattern_list(ci->rdomain, arg, 0) != 1)
 				result = 0;
 			else
diff --git a/servconf.h b/servconf.h
index fdbae24..381ed25 100644
--- a/servconf.h
+++ b/servconf.h
@@ -231,6 +231,8 @@ struct connection_info {
 	const char *laddress;	/* local address */
 	int lport;		/* local port */
 	const char *rdomain;	/* routing domain if available */
+	int test;		/* test mode, allow some attributes to be
+				   * unspecified */
 };
 
 
diff --git a/sshd.c b/sshd.c
index 6d081e4..a00b1af 100644
--- a/sshd.c
+++ b/sshd.c
@@ -2001,6 +2001,7 @@ main(int ac, char **av)
 		 */
 		if (connection_info == NULL)
 			connection_info = get_connection_info(0, 0);
+		connection_info->test = 1;
 		parse_server_match_config(&options, connection_info);
 		dump_config(&options);
 	}
-- 
2.19.1
