1#	$OpenBSD: forward-control.sh,v 1.12 2023/07/28 05:33:15 djm Exp $
2#	Placed in the Public Domain.
3
4tid="sshd control of local and remote forwarding"
5
6LFWD_PORT=3320
7RFWD_PORT=3321
8CTL=$OBJ/ctl-sock
9WAIT_SECONDS=20
10
11wait_for_process_to_exit() {
12	_pid=$1
13	_n=0
14	while kill -0 $_pid 2>/dev/null ; do
15		test $_n -eq 1 && trace "waiting for $_pid to exit"
16		_n=`expr $_n + 1`
17		test $_n -ge $WAIT_SECONDS && return 1
18		sleep 1
19	done
20	return 0
21}
22
23mux_cmd() {
24	${SSH} -F $OBJ/ssh_proxy -S $CTL -O $1 host 2>&1
25}
26
27controlmaster_pid() {
28	mux_cmd check | cut -f2 -d= | cut -f1 -d')'
29}
30
31# usage: check_lfwd Y|N message
32check_lfwd() {
33	_expected=$1
34	_message=$2
35	${SSH} -F $OBJ/ssh_proxy \
36	    -L$LFWD_PORT:127.0.0.1:$PORT \
37	    -o ExitOnForwardFailure=yes \
38	    -MS $CTL -o ControlPersist=yes \
39	    -Nf host
40	mux_cmd check >/dev/null || fatal "check_lfwd ssh fail: $_message"
41	${SSH} -F $OBJ/ssh_config -p $LFWD_PORT \
42	    -oConnectionAttempts=10 host true >/dev/null 2>&1
43	_result=$?
44	_sshpid=`controlmaster_pid`
45	mux_cmd exit >/dev/null
46	wait_for_process_to_exit $_sshpid
47	if test "x$_expected" = "xY" -a $_result -ne 0 ; then
48		fail "check_lfwd failed (expecting success): $_message"
49	elif test "x$_expected" = "xN" -a $_result -eq 0 ; then
50		fail "check_lfwd succeeded (expecting failure): $_message"
51	elif test "x$_expected" != "xY" -a "x$_expected" != "xN" ; then
52		fatal "check_lfwd invalid argument \"$_expected\""
53	else
54		verbose "check_lfwd done (expecting $_expected): $_message"
55	fi
56}
57
58# usage: check_rfwd Y|N message
59check_rfwd() {
60	_expected=$1
61	_message=$2
62	${SSH} -F $OBJ/ssh_proxy \
63	    -R127.0.0.1:$RFWD_PORT:127.0.0.1:$PORT \
64	    -o ExitOnForwardFailure=yes \
65	    -MS $CTL -o ControlPersist=yes \
66	    -Nf host
67	mux_cmd check >/dev/null
68	_result=$?
69	_sshpid=`controlmaster_pid`
70	if test $_result -eq 0; then
71		${SSH} -F $OBJ/ssh_config -p $RFWD_PORT \
72		    -oConnectionAttempts=10 host true >/dev/null 2>&1
73		_result=$?
74		mux_cmd exit >/dev/null
75		wait_for_process_to_exit $_sshpid
76	fi
77	if test "x$_expected" = "xY" -a $_result -ne 0 ; then
78		fail "check_rfwd failed (expecting success): $_message"
79	elif test "x$_expected" = "xN" -a $_result -eq 0 ; then
80		fail "check_rfwd succeeded (expecting failure): $_message"
81	elif test "x$_expected" != "xY" -a "x$_expected" != "xN" ; then
82		fatal "check_rfwd invalid argument \"$_expected\""
83	else
84		verbose "check_rfwd done (expecting $_expected): $_message"
85	fi
86}
87
88start_sshd
89cp ${OBJ}/sshd_proxy ${OBJ}/sshd_proxy.bak
90cp ${OBJ}/authorized_keys_${USER} ${OBJ}/authorized_keys_${USER}.bak
91
92# Sanity check: ensure the default config allows forwarding
93check_lfwd Y "default configuration"
94check_rfwd Y "default configuration"
95
96# Usage: lperm_tests yes|local|remote|no Y|N Y|N Y|N Y|N Y|N Y|N
97lperm_tests() {
98	_tcpfwd=$1
99	_plain_lfwd=$2
100	_plain_rfwd=$3
101	_nopermit_lfwd=$4
102	_nopermit_rfwd=$5
103	_permit_lfwd=$6
104	_permit_rfwd=$7
105	_badfwd1=127.0.0.1:22
106	_badfwd2=127.0.0.2:22
107	_goodfwd=127.0.0.1:${PORT}
108	cp ${OBJ}/authorized_keys_${USER}.bak  ${OBJ}/authorized_keys_${USER}
109	_prefix="AllowTcpForwarding=$_tcpfwd"
110
111	# No PermitOpen
112	( cat ${OBJ}/sshd_proxy.bak ;
113	  echo "AllowTcpForwarding $_tcpfwd" ) \
114	    > ${OBJ}/sshd_proxy
115	check_lfwd $_plain_lfwd "$_prefix"
116	check_rfwd $_plain_rfwd "$_prefix"
117
118	# PermitOpen via sshd_config that doesn't match
119	( cat ${OBJ}/sshd_proxy.bak ;
120	  echo "AllowTcpForwarding $_tcpfwd" ;
121	  echo "PermitOpen $_badfwd1 $_badfwd2" ) \
122	    > ${OBJ}/sshd_proxy
123	check_lfwd $_nopermit_lfwd "$_prefix, !PermitOpen"
124	check_rfwd $_nopermit_rfwd "$_prefix, !PermitOpen"
125	# PermitOpen via sshd_config that does match
126	( cat ${OBJ}/sshd_proxy.bak ;
127	  echo "AllowTcpForwarding $_tcpfwd" ;
128	  echo "PermitOpen $_badfwd1 $_goodfwd $_badfwd2" ) \
129	    > ${OBJ}/sshd_proxy
130	check_lfwd $_plain_lfwd "$_prefix, PermitOpen"
131	check_rfwd $_plain_rfwd "$_prefix, PermitOpen"
132
133	# permitopen keys option.
134	# NB. permitopen via authorized_keys should have same
135	# success/fail as via sshd_config
136	# permitopen via authorized_keys that doesn't match
137	sed "s/^/permitopen=\"$_badfwd1\",permitopen=\"$_badfwd2\" /" \
138	    < ${OBJ}/authorized_keys_${USER}.bak \
139	    > ${OBJ}/authorized_keys_${USER} || fatal "sed 1 fail"
140	( cat ${OBJ}/sshd_proxy.bak ;
141	  echo "AllowTcpForwarding $_tcpfwd" ) \
142	    > ${OBJ}/sshd_proxy
143	check_lfwd $_nopermit_lfwd "$_prefix, !permitopen"
144	check_rfwd $_nopermit_rfwd "$_prefix, !permitopen"
145	# permitopen via authorized_keys that does match
146	sed "s/^/permitopen=\"$_badfwd1\",permitopen=\"$_goodfwd\" /" \
147	    < ${OBJ}/authorized_keys_${USER}.bak \
148	    > ${OBJ}/authorized_keys_${USER} || fatal "sed 2 fail"
149	( cat ${OBJ}/sshd_proxy.bak ;
150	  echo "AllowTcpForwarding $_tcpfwd" ) \
151	    > ${OBJ}/sshd_proxy
152	check_lfwd $_permit_lfwd "$_prefix, permitopen"
153	check_rfwd $_permit_rfwd "$_prefix, permitopen"
154
155	# Check port-forwarding flags in authorized_keys.
156	# These two should refuse all.
157	sed "s/^/no-port-forwarding /" \
158	    < ${OBJ}/authorized_keys_${USER}.bak \
159	    > ${OBJ}/authorized_keys_${USER} || fatal "sed 3 fail"
160	( cat ${OBJ}/sshd_proxy.bak ;
161	  echo "AllowTcpForwarding $_tcpfwd" ) \
162	    > ${OBJ}/sshd_proxy
163	check_lfwd N "$_prefix, no-port-forwarding"
164	check_rfwd N "$_prefix, no-port-forwarding"
165	sed "s/^/restrict /" \
166	    < ${OBJ}/authorized_keys_${USER}.bak \
167	    > ${OBJ}/authorized_keys_${USER} || fatal "sed 4 fail"
168	( cat ${OBJ}/sshd_proxy.bak ;
169	  echo "AllowTcpForwarding $_tcpfwd" ) \
170	    > ${OBJ}/sshd_proxy
171	check_lfwd N "$_prefix, restrict"
172	check_rfwd N "$_prefix, restrict"
173	# This should pass the same cases as _nopermit*
174	sed "s/^/restrict,port-forwarding /" \
175	    < ${OBJ}/authorized_keys_${USER}.bak \
176	    > ${OBJ}/authorized_keys_${USER} || fatal "sed 5 fail"
177	( cat ${OBJ}/sshd_proxy.bak ;
178	  echo "AllowTcpForwarding $_tcpfwd" ) \
179	    > ${OBJ}/sshd_proxy
180	check_lfwd $_plain_lfwd "$_prefix, restrict,port-forwarding"
181	check_rfwd $_plain_rfwd "$_prefix, restrict,port-forwarding"
182}
183
184#          permit-open      none          mismatch         match
185#   AllowTcpForwarding  local remote    local remote    local remote
186lperm_tests     yes     Y     Y         N     Y         Y     Y
187lperm_tests   local     Y     N         N     N         Y     N
188lperm_tests  remote     N     Y         N     Y         N     Y
189lperm_tests      no     N     N         N     N         N     N
190
191# Usage: rperm_tests yes|local|remote|no Y|N Y|N Y|N Y|N Y|N Y|N
192rperm_tests() {
193	_tcpfwd=$1
194	_plain_lfwd=$2
195	_plain_rfwd=$3
196	_nopermit_lfwd=$4
197	_nopermit_rfwd=$5
198	_permit_lfwd=$6
199	_permit_rfwd=$7
200	_badfwd1=127.0.0.1:22
201	_badfwd2=127.0.0.2:${RFWD_PORT}
202	_goodfwd=127.0.0.1:${RFWD_PORT}
203	cp ${OBJ}/authorized_keys_${USER}.bak  ${OBJ}/authorized_keys_${USER}
204	_prefix="AllowTcpForwarding=$_tcpfwd"
205
206	# PermitListen via sshd_config that doesn't match
207	( cat ${OBJ}/sshd_proxy.bak ;
208	  echo "AllowTcpForwarding $_tcpfwd" ;
209	  echo "PermitListen $_badfwd1 $_badfwd2" ) \
210	    > ${OBJ}/sshd_proxy
211	check_lfwd $_nopermit_lfwd "$_prefix, !PermitListen"
212	check_rfwd $_nopermit_rfwd "$_prefix, !PermitListen"
213	# PermitListen via sshd_config that does match
214	( cat ${OBJ}/sshd_proxy.bak ;
215	  echo "AllowTcpForwarding $_tcpfwd" ;
216	  echo "PermitListen $_badfwd1 $_goodfwd $_badfwd2" ) \
217	    > ${OBJ}/sshd_proxy
218	check_lfwd $_plain_lfwd "$_prefix, PermitListen"
219	check_rfwd $_plain_rfwd "$_prefix, PermitListen"
220}
221
222#   permit-remote-open      none          mismatch         match
223#   AllowTcpForwarding  local remote    local remote    local remote
224rperm_tests     yes     Y     Y         Y     N         Y     Y
225rperm_tests   local     Y     N         Y     N         Y     N
226rperm_tests  remote     N     Y         N     N         N     Y
227rperm_tests      no     N     N         N     N         N     N
228
229