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