]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - crypto/openssh/regress/agent-restrict.sh
ena: Upgrade ena-com to freebsd v2.7.0
[FreeBSD/FreeBSD.git] / crypto / openssh / regress / agent-restrict.sh
1 #       $OpenBSD: agent-restrict.sh,v 1.6 2023/03/01 09:29:32 dtucker Exp $
2 #       Placed in the Public Domain.
3
4 tid="agent restrictions"
5
6 SSH_AUTH_SOCK="$OBJ/agent.sock"
7 export SSH_AUTH_SOCK
8 rm -f $SSH_AUTH_SOCK $OBJ/agent.log $OBJ/host_[abcdex]* $OBJ/user_[abcdex]*
9 rm -f $OBJ/sshd_proxy_host* $OBJ/ssh_output* $OBJ/expect_*
10 rm -f $OBJ/ssh_proxy[._]* $OBJ/command
11
12 verbose "generate keys"
13 for h in a b c d e x ca ; do
14         $SSHKEYGEN -q -t ed25519 -C host_$h -N '' -f $OBJ/host_$h || \
15                 fatal "ssh-keygen hostkey failed"
16         $SSHKEYGEN -q -t ed25519 -C user_$h -N '' -f $OBJ/user_$h || \
17                 fatal "ssh-keygen userkey failed"
18 done
19
20 # Make some hostcerts
21 for h in d e ; do
22         id="host_$h"
23         $SSHKEYGEN -q -s $OBJ/host_ca -I $id -n $id -h $OBJ/host_${h}.pub || \
24                 fatal "ssh-keygen certify failed"
25 done
26
27 verbose "prepare client config"
28 egrep -vi '(identityfile|hostname|hostkeyalias|proxycommand)' \
29         $OBJ/ssh_proxy > $OBJ/ssh_proxy.bak
30 cat << _EOF > $OBJ/ssh_proxy
31 IdentitiesOnly yes
32 ForwardAgent yes
33 ExitOnForwardFailure yes
34 _EOF
35 cp $OBJ/ssh_proxy $OBJ/ssh_proxy_noid
36 for h in a b c d e ; do
37         cat << _EOF >> $OBJ/ssh_proxy
38 Host host_$h
39         Hostname host_$h
40         HostkeyAlias host_$h
41         IdentityFile $OBJ/user_$h
42         ProxyCommand ${SUDO} env SSH_SK_HELPER=\"$SSH_SK_HELPER\" ${OBJ}/sshd-log-wrapper.sh -i -f $OBJ/sshd_proxy_host_$h
43 _EOF
44         # Variant with no specified keys.
45         cat << _EOF >> $OBJ/ssh_proxy_noid
46 Host host_$h
47         Hostname host_$h
48         HostkeyAlias host_$h
49         ProxyCommand ${SUDO} env SSH_SK_HELPER=\"$SSH_SK_HELPER\" ${OBJ}/sshd-log-wrapper.sh -i -f $OBJ/sshd_proxy_host_$h
50 _EOF
51 done
52 cat $OBJ/ssh_proxy.bak >> $OBJ/ssh_proxy
53 cat $OBJ/ssh_proxy.bak >> $OBJ/ssh_proxy_noid
54
55 LC_ALL=C
56 export LC_ALL
57 echo "SetEnv LC_ALL=${LC_ALL}" >> sshd_proxy
58
59 verbose "prepare known_hosts"
60 rm -f $OBJ/known_hosts
61 for h in a b c x ; do
62         (printf "host_$h " ; cat $OBJ/host_${h}.pub) >> $OBJ/known_hosts
63 done
64 (printf "@cert-authority host_* " ; cat $OBJ/host_ca.pub) >> $OBJ/known_hosts
65
66 verbose "prepare server configs"
67 egrep -vi '(hostkey|pidfile)' $OBJ/sshd_proxy \
68         > $OBJ/sshd_proxy.bak
69 for h in a b c d e; do
70         cp $OBJ/sshd_proxy.bak $OBJ/sshd_proxy_host_$h
71         cat << _EOF >> $OBJ/sshd_proxy_host_$h
72 ExposeAuthInfo yes
73 PidFile none
74 Hostkey $OBJ/host_$h
75 _EOF
76 done
77 for h in d e ; do
78         echo "HostCertificate $OBJ/host_${h}-cert.pub" \
79                 >> $OBJ/sshd_proxy_host_$h
80 done
81 # Create authorized_keys with canned command.
82 reset_keys() {
83         _whichcmd="$1"
84         _command=""
85         case "$_whichcmd" in
86         authinfo)       _command="cat \$SSH_USER_AUTH" ;;
87         keylist)                _command="$SSHADD -L | cut -d' ' -f-2 | sort" ;;
88         *)              fatal "unsupported command $_whichcmd" ;;
89         esac
90         trace "reset keys"
91         >$OBJ/authorized_keys_$USER
92         for h in e d c b a; do
93                 (printf "%s" "restrict,agent-forwarding,command=\"$_command\" ";
94                  cat $OBJ/user_$h.pub) >> $OBJ/authorized_keys_$USER
95         done
96 }
97 # Prepare a key for comparison with ExposeAuthInfo/$SSH_USER_AUTH.
98 expect_key() {
99         _key="$OBJ/${1}.pub"
100         _file="$OBJ/$2"
101         (printf "publickey " ; cut -d' ' -f-2 $_key) > $_file
102 }
103 # Prepare expect_* files to compare against authinfo forced command to ensure
104 # keys used for authentication match.
105 reset_expect_keys() {
106         for u in a b c d e; do
107                 expect_key user_$u expect_$u
108         done
109 }
110 # ssh to host, expecting success and that output matched expectation for
111 # that host (expect_$h file).
112 expect_succeed() {
113         _id="$1"
114         _case="$2"
115         shift; shift; _extra="$@"
116         _host="host_$_id"
117         trace "connect $_host expect success"
118         rm -f $OBJ/ssh_output
119         ${SSH} $_extra -F $OBJ/ssh_proxy $_host true > $OBJ/ssh_output
120         _s=$?
121         test $_s -eq 0 || fail "host $_host $_case fail, exit status $_s"
122         diff $OBJ/ssh_output $OBJ/expect_${_id} ||
123                 fail "unexpected ssh output"
124 }
125 # ssh to host using explicit key, expecting success and that the key was
126 # actually used for authentication.
127 expect_succeed_key() {
128         _id="$1"
129         _key="$2"
130         _case="$3"
131         shift; shift; shift; _extra="$@"
132         _host="host_$_id"
133         trace "connect $_host expect success, with key $_key"
134         _keyfile="$OBJ/$_key"
135         rm -f $OBJ/ssh_output
136         ${SSH} $_extra -F $OBJ/ssh_proxy_noid \
137             -oIdentityFile=$_keyfile $_host true > $OBJ/ssh_output
138         _s=$?
139         test $_s -eq 0 || fail "host $_host $_key $_case fail, exit status $_s"
140         expect_key $_key expect_key
141         diff $OBJ/ssh_output $OBJ/expect_key ||
142                 fail "incorrect key used for authentication"
143 }
144 # ssh to a host, expecting it to fail.
145 expect_fail() {
146         _host="$1"
147         _case="$2"
148         shift; shift; _extra="$@"
149         trace "connect $_host expect failure"
150         ${SSH} $_extra -F $OBJ/ssh_proxy $_host true >/dev/null && \
151                 fail "host $_host $_case succeeded unexpectedly"
152 }
153 # ssh to a host using an explicit key, expecting it to fail.
154 expect_fail_key() {
155         _id="$1"
156         _key="$2"
157         _case="$3"
158         shift; shift; shift; _extra="$@"
159         _host="host_$_id"
160         trace "connect $_host expect failure, with key $_key"
161         _keyfile="$OBJ/$_key"
162         ${SSH} $_extra -F $OBJ/ssh_proxy_noid -oIdentityFile=$_keyfile \
163             $_host true > $OBJ/ssh_output && \
164                 fail "host $_host $_key $_case succeeded unexpectedly"
165 }
166 # Move the private key files out of the way to force use of agent-hosted keys.
167 hide_privatekeys() {
168         trace "hide private keys"
169         for u in a b c d e x; do
170                 mv $OBJ/user_$u $OBJ/user_x$u || fatal "hide privkey $u"
171         done
172 }
173 # Put the private key files back.
174 restore_privatekeys() {
175         trace "restore private keys"
176         for u in a b c d e x; do
177                 mv $OBJ/user_x$u $OBJ/user_$u || fatal "restore privkey $u"
178         done
179 }
180 clear_agent() {
181         ${SSHADD} -D > /dev/null 2>&1 || fatal "clear agent failed"
182 }
183
184 reset_keys authinfo
185 reset_expect_keys
186
187 verbose "authentication w/o agent"
188 for h in a b c d e ; do
189         expect_succeed $h "w/o agent"
190         wrongkey=user_e
191         test "$h" = "e" && wrongkey=user_a
192         expect_succeed_key $h $wrongkey "\"wrong\" key w/o agent"
193 done
194 hide_privatekeys
195 for h in a b c d e ; do
196         expect_fail $h "w/o agent"
197 done
198 restore_privatekeys
199
200 verbose "start agent"
201 ${SSHAGENT} ${EXTRA_AGENT_ARGS} -d -a $SSH_AUTH_SOCK > $OBJ/agent.log 2>&1 &
202 AGENT_PID=$!
203 trap "kill $AGENT_PID" EXIT
204 sleep 4 # Give it a chance to start
205 # Check that it's running.
206 ${SSHADD} -l > /dev/null 2>&1
207 if [ $? -ne 1 ]; then
208         fail "ssh-add -l did not fail with exit code 1"
209 fi
210
211 verbose "authentication with agent (no restrict)"
212 for u in a b c d e x; do
213         $SSHADD -q $OBJ/user_$u || fatal "add key $u unrestricted"
214 done
215 hide_privatekeys
216 for h in a b c d e ; do
217         expect_succeed $h "with agent"
218         wrongkey=user_e
219         test "$h" = "e" && wrongkey=user_a
220         expect_succeed_key $h $wrongkey "\"wrong\" key with agent"
221 done
222
223 verbose "unrestricted keylist"
224 reset_keys keylist
225 rm -f $OBJ/expect_list.pre
226 # List of keys from agent should contain everything.
227 for u in a b c d e x; do
228         cut -d " " -f-2 $OBJ/user_${u}.pub >> $OBJ/expect_list.pre
229 done
230 sort $OBJ/expect_list.pre > $OBJ/expect_list
231 for h in a b c d e; do
232         cp $OBJ/expect_list $OBJ/expect_$h
233         expect_succeed $h "unrestricted keylist"
234 done
235 restore_privatekeys
236
237 verbose "authentication with agent (basic restrict)"
238 reset_keys authinfo
239 reset_expect_keys
240 for h in a b c d e; do
241         $SSHADD -h host_$h -H $OBJ/known_hosts -q $OBJ/user_$h \
242                 || fatal "add key $u basic restrict"
243 done
244 # One more, unrestricted
245 $SSHADD -q $OBJ/user_x || fatal "add unrestricted key"
246 hide_privatekeys
247 # Authentication to host with expected key should work.
248 for h in a b c d e ; do
249         expect_succeed $h "with agent"
250 done
251 # Authentication to host with incorrect key should fail.
252 verbose "authentication with agent incorrect key (basic restrict)"
253 for h in a b c d e ; do
254         wrongkey=user_e
255         test "$h" = "e" && wrongkey=user_a
256         expect_fail_key $h $wrongkey "wrong key with agent (basic restrict)"
257 done
258
259 verbose "keylist (basic restrict)"
260 reset_keys keylist
261 # List from forwarded agent should contain only user_x - the unrestricted key.
262 cut -d " " -f-2 $OBJ/user_x.pub > $OBJ/expect_list
263 for h in a b c d e; do
264         cp $OBJ/expect_list $OBJ/expect_$h
265         expect_succeed $h "keylist (basic restrict)"
266 done
267 restore_privatekeys
268
269 verbose "username"
270 reset_keys authinfo
271 reset_expect_keys
272 for h in a b c d e; do
273         $SSHADD -h "${USER}@host_$h" -H $OBJ/known_hosts -q $OBJ/user_$h \
274                 || fatal "add key $u basic restrict"
275 done
276 hide_privatekeys
277 for h in a b c d e ; do
278         expect_succeed $h "wildcard user"
279 done
280 restore_privatekeys
281
282 verbose "username wildcard"
283 reset_keys authinfo
284 reset_expect_keys
285 for h in a b c d e; do
286         $SSHADD -h "*@host_$h" -H $OBJ/known_hosts -q $OBJ/user_$h \
287                 || fatal "add key $u basic restrict"
288 done
289 hide_privatekeys
290 for h in a b c d e ; do
291         expect_succeed $h "wildcard user"
292 done
293 restore_privatekeys
294
295 verbose "username incorrect"
296 reset_keys authinfo
297 reset_expect_keys
298 for h in a b c d e; do
299         $SSHADD -h "--BADUSER@host_$h" -H $OBJ/known_hosts -q $OBJ/user_$h \
300                 || fatal "add key $u basic restrict"
301 done
302 hide_privatekeys
303 for h in a b c d e ; do
304         expect_fail $h "incorrect user"
305 done
306 restore_privatekeys
307
308
309 verbose "agent restriction honours certificate principal"
310 reset_keys authinfo
311 reset_expect_keys
312 clear_agent
313 $SSHADD -h host_e -H $OBJ/known_hosts -q $OBJ/user_d || fatal "add key"
314 hide_privatekeys
315 expect_fail d "restricted agent w/ incorrect cert principal"
316 restore_privatekeys
317
318 # Prepares the script used to drive chained ssh connections for the
319 # multihop tests. Believe me, this is easier than getting the escaping
320 # right for 5 hops on the command-line...
321 prepare_multihop_script() {
322         MULTIHOP_RUN=$OBJ/command
323         cat << _EOF > $MULTIHOP_RUN
324 #!/bin/sh
325 #set -x
326 me="\$1" ; shift
327 next="\$1"
328 if test ! -z "\$me" ; then 
329         rm -f $OBJ/done
330         echo "HOSTNAME host_\$me"
331         echo "AUTHINFO"
332         cat \$SSH_USER_AUTH
333 fi
334 echo AGENT
335 $SSHADD -L | egrep "^ssh" | cut -d" " -f-2 | sort
336 if test -z "\$next" ; then 
337         touch $OBJ/done
338         echo "FINISH"
339         e=0
340 else
341         echo NEXT
342         ${SSH} -F $OBJ/ssh_proxy_noid -oIdentityFile=$OBJ/user_a \
343                 host_\$next $MULTIHOP_RUN "\$@"
344         e=\$?
345 fi
346 echo "COMPLETE \"\$me\""
347 if test ! -z "\$me" ; then 
348         if test ! -f $OBJ/done ; then
349                 echo "DONE MARKER MISSING"
350                 test \$e -eq 0 && e=63
351         fi
352 fi
353 exit \$e
354 _EOF
355         chmod u+x $MULTIHOP_RUN
356 }
357
358 # Prepare expected output for multihop tests at expect_a
359 prepare_multihop_expected() {
360         _keys="$1"
361         _hops="a b c d e"
362         test -z "$2" || _hops="$2"
363         _revhops=$(echo "$_hops" | rev)
364         _lasthop=$(echo "$_hops" | sed 's/.* //')
365
366         rm -f $OBJ/expect_keys
367         for h in a b c d e; do
368                 cut -d" " -f-2 $OBJ/user_${h}.pub >> $OBJ/expect_keys
369         done
370         rm -f $OBJ/expect_a
371         echo "AGENT" >> $OBJ/expect_a
372         test "x$_keys" = "xnone" || sort $OBJ/expect_keys >> $OBJ/expect_a
373         echo "NEXT" >> $OBJ/expect_a
374         for h in $_hops ; do 
375                 echo "HOSTNAME host_$h" >> $OBJ/expect_a
376                 echo "AUTHINFO" >> $OBJ/expect_a
377                 (printf "publickey " ; cut -d" " -f-2 $OBJ/user_a.pub) >> $OBJ/expect_a
378                 echo "AGENT" >> $OBJ/expect_a
379                 if test "x$_keys" = "xall" ; then
380                         sort $OBJ/expect_keys >> $OBJ/expect_a
381                 fi
382                 if test "x$h" != "x$_lasthop" ; then
383                         if test "x$_keys" = "xfiltered" ; then
384                                 cut -d" " -f-2 $OBJ/user_a.pub >> $OBJ/expect_a
385                         fi
386                         echo "NEXT" >> $OBJ/expect_a
387                 fi
388         done
389         echo "FINISH" >> $OBJ/expect_a
390         for h in $_revhops "" ; do 
391                 echo "COMPLETE \"$h\"" >> $OBJ/expect_a
392         done
393 }
394
395 prepare_multihop_script
396 cp $OBJ/user_a.pub $OBJ/authorized_keys_$USER # only one key used.
397
398 verbose "multihop without agent"
399 clear_agent
400 prepare_multihop_expected none
401 $MULTIHOP_RUN "" a b c d e > $OBJ/ssh_output || fail "multihop no agent ssh failed"
402 diff $OBJ/ssh_output $OBJ/expect_a || fail "unexpected ssh output"
403
404 verbose "multihop agent unrestricted"
405 clear_agent
406 $SSHADD -q $OBJ/user_[abcde]
407 prepare_multihop_expected all
408 $MULTIHOP_RUN "" a b c d e > $OBJ/ssh_output || fail "multihop no agent ssh failed"
409 diff $OBJ/ssh_output $OBJ/expect_a || fail "unexpected ssh output"
410
411 verbose "multihop restricted"
412 clear_agent
413 prepare_multihop_expected filtered
414 # Add user_a, with permission to connect through the whole chain.
415 $SSHADD -h host_a -h "host_a>host_b" -h "host_b>host_c" \
416         -h "host_c>host_d" -h "host_d>host_e" \
417         -H $OBJ/known_hosts -q $OBJ/user_a \
418         || fatal "add key user_a multihop"
419 # Add the other keys, bound to a unused host.
420 $SSHADD -q -h host_x -H $OBJ/known_hosts $OBJ/user_[bcde] || fail "add keys"
421 hide_privatekeys
422 $MULTIHOP_RUN "" a b c d e > $OBJ/ssh_output || fail "multihop ssh failed"
423 diff $OBJ/ssh_output $OBJ/expect_a || fail "unexpected ssh output"
424 restore_privatekeys
425
426 verbose "multihop username"
427 $SSHADD -h host_a -h "host_a>${USER}@host_b" -h "host_b>${USER}@host_c" \
428         -h "host_c>${USER}@host_d"  -h "host_d>${USER}@host_e" \
429         -H $OBJ/known_hosts -q $OBJ/user_a || fatal "add key user_a multihop"
430 hide_privatekeys
431 $MULTIHOP_RUN "" a b c d e > $OBJ/ssh_output || fail "multihop w/ user ssh failed"
432 diff $OBJ/ssh_output $OBJ/expect_a || fail "unexpected ssh output"
433 restore_privatekeys
434
435 verbose "multihop wildcard username"
436 $SSHADD -h host_a -h "host_a>*@host_b" -h "host_b>*@host_c" \
437         -h "host_c>*@host_d"  -h "host_d>*@host_e" \
438         -H $OBJ/known_hosts -q $OBJ/user_a || fatal "add key user_a multihop"
439 hide_privatekeys
440 $MULTIHOP_RUN "" a b c d e > $OBJ/ssh_output || fail "multihop w/ user ssh failed"
441 diff $OBJ/ssh_output $OBJ/expect_a || fail "unexpected ssh output"
442 restore_privatekeys
443
444 verbose "multihop wrong username"
445 $SSHADD -h host_a -h "host_a>*@host_b" -h "host_b>*@host_c" \
446         -h "host_c>--BADUSER@host_d"  -h "host_d>*@host_e" \
447         -H $OBJ/known_hosts -q $OBJ/user_a || fatal "add key user_a multihop"
448 hide_privatekeys
449 $MULTIHOP_RUN "" a b c d e > $OBJ/ssh_output && \
450         fail "multihop with wrong user succeeded unexpectedly"
451 restore_privatekeys
452
453 verbose "multihop cycle no agent"
454 clear_agent
455 prepare_multihop_expected none "a b a a c d e"
456 $MULTIHOP_RUN "" a b a a c d e > $OBJ/ssh_output || \
457         fail "multihop cycle no-agent fail"
458 diff $OBJ/ssh_output $OBJ/expect_a || fail "unexpected ssh output"
459
460 verbose "multihop cycle agent unrestricted"
461 clear_agent
462 $SSHADD -q $OBJ/user_[abcde] || fail "add keys"
463 prepare_multihop_expected all "a b a a c d e"
464 $MULTIHOP_RUN "" a b a a c d e > $OBJ/ssh_output || \
465         fail "multihop cycle agent ssh failed"
466 diff $OBJ/ssh_output $OBJ/expect_a || fail "unexpected ssh output"
467
468 verbose "multihop cycle restricted deny"
469 clear_agent
470 $SSHADD -q -h host_x -H $OBJ/known_hosts $OBJ/user_[bcde] || fail "add keys"
471 $SSHADD -h host_a -h "host_a>host_b" -h "host_b>host_c" \
472         -h "host_c>host_d" -h "host_d>host_e" \
473         -H $OBJ/known_hosts -q $OBJ/user_a \
474         || fatal "add key user_a multihop"
475 prepare_multihop_expected filtered "a b a a c d e"
476 hide_privatekeys
477 $MULTIHOP_RUN "" a b a a c d e > $OBJ/ssh_output && \
478         fail "multihop cycle restricted deny succeded unexpectedly"
479 restore_privatekeys
480
481 verbose "multihop cycle restricted allow"
482 clear_agent
483 $SSHADD -q -h host_x -H $OBJ/known_hosts $OBJ/user_[bcde] || fail "add keys"
484 $SSHADD -h host_a -h "host_a>host_b" -h "host_b>host_c" \
485         -h "host_c>host_d" -h "host_d>host_e" \
486         -h "host_b>host_a" -h "host_a>host_a" -h "host_a>host_c" \
487         -H $OBJ/known_hosts -q $OBJ/user_a \
488         || fatal "add key user_a multihop"
489 prepare_multihop_expected filtered "a b a a c d e"
490 hide_privatekeys
491 $MULTIHOP_RUN "" a b a a c d e > $OBJ/ssh_output || \
492         fail "multihop cycle restricted allow failed"
493 diff $OBJ/ssh_output $OBJ/expect_a || fail "unexpected ssh output"
494 restore_privatekeys
495