xref: /openbsd-src/regress/usr.bin/ssh/cert-userkey.sh (revision e6fc4d34cb7c01d7fca4e95aeb6f7f74874bab2c)
1#	$OpenBSD: cert-userkey.sh,v 1.29 2024/12/06 16:25:58 djm Exp $
2#	Placed in the Public Domain.
3
4tid="certified user keys"
5
6rm -f $OBJ/authorized_keys_${USER}* $OBJ/user_ca_key* $OBJ/cert_user_key*
7rm -f $OBJ/authorized_principals*
8cp $OBJ/ssh_proxy $OBJ/ssh_proxy_bak
9
10grep -v AuthorizedKeysFile $OBJ/sshd_proxy > $OBJ/sshd_proxy_bak
11echo "AuthorizedKeysFile $OBJ/authorized_keys_%u_*" >> $OBJ/sshd_proxy_bak
12
13PLAIN_TYPES=`$SSH -Q key-plain | maybe_filter_sk | sed 's/^ssh-dss/ssh-dsa/;s/^ssh-//'`
14EXTRA_TYPES=""
15rsa=""
16
17if echo "$PLAIN_TYPES" | grep '^rsa$' >/dev/null 2>&1 ; then
18	rsa=rsa
19	PLAIN_TYPES="$PLAIN_TYPES rsa-sha2-256 rsa-sha2-512"
20fi
21
22kname() {
23	case $1 in
24	rsa-sha2-*) n="$1" ;;
25	sk-ecdsa-*) n="sk-ecdsa" ;;
26	sk-ssh-ed25519*) n="sk-ssh-ed25519" ;;
27	# subshell because some seds will add a newline
28	*) n=$(echo $1 | sed 's/^dsa/ssh-dss/;s/^rsa/ssh-rsa/;s/^ed/ssh-ed/') ;;
29	esac
30	if [ -z "$rsa" ]; then
31		echo "$n*,ssh-ed25519*"
32	else
33		echo "$n*,ssh-rsa*,ssh-ed25519*"
34	fi
35}
36
37# Create a CA key
38if [ ! -z "$rsa" ]; then
39	catype=rsa
40else
41	catype=ed25519
42fi
43${SSHKEYGEN} -q -N '' -t $catype  -f $OBJ/user_ca_key ||\
44	fail "ssh-keygen of user_ca_key failed"
45
46# Generate and sign user keys
47for ktype in $PLAIN_TYPES $EXTRA_TYPES ; do
48	verbose "$tid: sign user ${ktype} cert"
49	${SSHKEYGEN} -q -N '' -t ${ktype} \
50	    -f $OBJ/cert_user_key_${ktype} || \
51		fatal "ssh-keygen of cert_user_key_${ktype} failed"
52	# Generate RSA/SHA2 certs for rsa-sha2* keys.
53	case $ktype in
54	rsa-sha2-*)	tflag="-t $ktype" ;;
55	*)		tflag="" ;;
56	esac
57	${SSHKEYGEN} -q -s $OBJ/user_ca_key -z $$ \
58	    -I "regress user key for $USER" \
59	    -n ${USER},mekmitasdigoat $tflag $OBJ/cert_user_key_${ktype} || \
60		fatal "couldn't sign cert_user_key_${ktype}"
61done
62
63# Test explicitly-specified principals
64for ktype in $EXTRA_TYPES $PLAIN_TYPES ; do
65	t=$(kname $ktype)
66	_prefix="${ktype}"
67
68	# Setup for AuthorizedPrincipalsFile
69	rm -f $OBJ/authorized_keys_${USER}* $OBJ/authorized_principals_${USER}*
70	touch $OBJ/authorized_keys_${USER}_A
71	touch $OBJ/authorized_keys_${USER}_Z
72	touch $OBJ/authorized_principals_${USER}_A
73	touch $OBJ/authorized_principals_${USER}_Z
74	(
75		cat $OBJ/sshd_proxy_bak
76		echo "AuthorizedPrincipalsFile " \
77		    "$OBJ/authorized_principals_%u_*"
78		echo "TrustedUserCAKeys $OBJ/user_ca_key.pub"
79		echo "PubkeyAcceptedAlgorithms ${t}"
80	) > $OBJ/sshd_proxy
81	(
82		cat $OBJ/ssh_proxy_bak
83		echo "PubkeyAcceptedAlgorithms ${t}"
84	) > $OBJ/ssh_proxy
85
86	# Missing authorized_principals
87	verbose "$tid: ${_prefix} missing authorized_principals"
88	rm -f $OBJ/authorized_principals_${USER}_X
89	${SSH} -i $OBJ/cert_user_key_${ktype} \
90	    -F $OBJ/ssh_proxy somehost true >/dev/null 2>&1
91	if [ $? -eq 0 ]; then
92		fail "ssh cert connect succeeded unexpectedly"
93	fi
94
95	# Empty authorized_principals
96	verbose "$tid: ${_prefix} empty authorized_principals"
97	echo > $OBJ/authorized_principals_${USER}_X
98	${SSH} -i $OBJ/cert_user_key_${ktype} \
99	    -F $OBJ/ssh_proxy somehost true >/dev/null 2>&1
100	if [ $? -eq 0 ]; then
101		fail "ssh cert connect succeeded unexpectedly"
102	fi
103
104	# Wrong authorized_principals
105	verbose "$tid: ${_prefix} wrong authorized_principals"
106	echo gregorsamsa > $OBJ/authorized_principals_${USER}_X
107	${SSH} -i $OBJ/cert_user_key_${ktype} \
108	    -F $OBJ/ssh_proxy somehost true >/dev/null 2>&1
109	if [ $? -eq 0 ]; then
110		fail "ssh cert connect succeeded unexpectedly"
111	fi
112
113	# Correct authorized_principals
114	verbose "$tid: ${_prefix} correct authorized_principals"
115	echo mekmitasdigoat > $OBJ/authorized_principals_${USER}_X
116	${SSH} -i $OBJ/cert_user_key_${ktype} \
117	    -F $OBJ/ssh_proxy somehost true >/dev/null 2>&1
118	if [ $? -ne 0 ]; then
119		fail "ssh cert connect failed"
120	fi
121
122	# authorized_principals with bad key option
123	verbose "$tid: ${_prefix} authorized_principals bad key opt"
124	echo 'blah mekmitasdigoat' > $OBJ/authorized_principals_${USER}_X
125	${SSH} -i $OBJ/cert_user_key_${ktype} \
126	    -F $OBJ/ssh_proxy somehost true >/dev/null 2>&1
127	if [ $? -eq 0 ]; then
128		fail "ssh cert connect succeeded unexpectedly"
129	fi
130
131	# authorized_principals with command=false
132	verbose "$tid: ${_prefix} authorized_principals command=false"
133	echo 'command="false" mekmitasdigoat' > \
134	    $OBJ/authorized_principals_${USER}_X
135	${SSH} -i $OBJ/cert_user_key_${ktype} \
136	    -F $OBJ/ssh_proxy somehost true >/dev/null 2>&1
137	if [ $? -eq 0 ]; then
138		fail "ssh cert connect succeeded unexpectedly"
139	fi
140
141
142	# authorized_principals with command=true
143	verbose "$tid: ${_prefix} authorized_principals command=true"
144	echo 'command="true" mekmitasdigoat' > \
145	    $OBJ/authorized_principals_${USER}_X
146	${SSH} -i $OBJ/cert_user_key_${ktype} \
147	    -F $OBJ/ssh_proxy somehost false >/dev/null 2>&1
148	if [ $? -ne 0 ]; then
149		fail "ssh cert connect failed"
150	fi
151
152	# Setup for principals= key option
153	rm -f $OBJ/authorized_principals_${USER}_X
154	(
155		cat $OBJ/sshd_proxy_bak
156		echo "PubkeyAcceptedAlgorithms ${t}"
157	) > $OBJ/sshd_proxy
158	(
159		cat $OBJ/ssh_proxy_bak
160		echo "PubkeyAcceptedAlgorithms ${t}"
161	) > $OBJ/ssh_proxy
162
163	# Wrong principals list
164	verbose "$tid: ${_prefix} wrong principals key option"
165	(
166		printf 'cert-authority,principals="gregorsamsa" '
167		cat $OBJ/user_ca_key.pub
168	) > $OBJ/authorized_keys_${USER}_X
169	${SSH} -i $OBJ/cert_user_key_${ktype} \
170	    -F $OBJ/ssh_proxy somehost true >/dev/null 2>&1
171	if [ $? -eq 0 ]; then
172		fail "ssh cert connect succeeded unexpectedly"
173	fi
174
175	# Correct principals list
176	verbose "$tid: ${_prefix} correct principals key option"
177	(
178		printf 'cert-authority,principals="mekmitasdigoat" '
179		cat $OBJ/user_ca_key.pub
180	) > $OBJ/authorized_keys_${USER}_X
181	${SSH} -i $OBJ/cert_user_key_${ktype} \
182	    -F $OBJ/ssh_proxy somehost true >/dev/null 2>&1
183	if [ $? -ne 0 ]; then
184		fail "ssh cert connect failed"
185	fi
186done
187
188basic_tests() {
189	auth=$1
190	rm -f $OBJ/authorized_keys_${USER}*
191	touch $OBJ/authorized_keys_${USER}_A
192	touch $OBJ/authorized_keys_${USER}_Z
193	if test "x$auth" = "xauthorized_keys" ; then
194		# Add CA to authorized_keys
195		(
196			printf 'cert-authority '
197			cat $OBJ/user_ca_key.pub
198		) > $OBJ/authorized_keys_${USER}_X
199	else
200		echo > $OBJ/authorized_keys_${USER}_X
201		extra_sshd="TrustedUserCAKeys $OBJ/user_ca_key.pub"
202	fi
203
204	for ktype in $PLAIN_TYPES ; do
205		t=$(kname $ktype)
206		_prefix="${ktype} $auth"
207		# Simple connect
208		verbose "$tid: ${_prefix} connect"
209		(
210			cat $OBJ/sshd_proxy_bak
211			echo "PubkeyAcceptedAlgorithms ${t}"
212			echo "$extra_sshd"
213		) > $OBJ/sshd_proxy
214		(
215			cat $OBJ/ssh_proxy_bak
216			echo "PubkeyAcceptedAlgorithms ${t}"
217		) > $OBJ/ssh_proxy
218
219		${SSH} -i $OBJ/cert_user_key_${ktype} \
220		    -F $OBJ/ssh_proxy somehost true
221		if [ $? -ne 0 ]; then
222			fail "ssh cert connect failed"
223		fi
224
225		# Revoked keys
226		verbose "$tid: ${_prefix} revoked key"
227		(
228			cat $OBJ/sshd_proxy_bak
229			echo "RevokedKeys $OBJ/cert_user_key_revoked"
230			echo "PubkeyAcceptedAlgorithms ${t}"
231			echo "$extra_sshd"
232		) > $OBJ/sshd_proxy
233		cp $OBJ/cert_user_key_${ktype}.pub \
234		    $OBJ/cert_user_key_revoked
235		${SSH} -i $OBJ/cert_user_key_${ktype} \
236		    -F $OBJ/ssh_proxy somehost true >/dev/null 2>&1
237		if [ $? -eq 0 ]; then
238			fail "ssh cert connect succeeded unexpecedly"
239		fi
240		verbose "$tid: ${_prefix} revoked via KRL"
241		rm $OBJ/cert_user_key_revoked
242		${SSHKEYGEN} -kqf $OBJ/cert_user_key_revoked \
243		    $OBJ/cert_user_key_${ktype}.pub
244		${SSH} -i $OBJ/cert_user_key_${ktype} \
245		    -F $OBJ/ssh_proxy somehost true >/dev/null 2>&1
246		if [ $? -eq 0 ]; then
247			fail "ssh cert connect succeeded unexpecedly"
248		fi
249		verbose "$tid: ${_prefix} empty KRL"
250		${SSHKEYGEN} -kqf $OBJ/cert_user_key_revoked
251		${SSH} -i $OBJ/cert_user_key_${ktype} \
252		    -F $OBJ/ssh_proxy somehost true >/dev/null 2>&1
253		if [ $? -ne 0 ]; then
254			fail "ssh cert connect failed"
255		fi
256	done
257
258	# Revoked CA
259	verbose "$tid: ${ktype} $auth revoked CA key"
260	(
261		cat $OBJ/sshd_proxy_bak
262		echo "RevokedKeys $OBJ/user_ca_key.pub"
263		echo "PubkeyAcceptedAlgorithms ${t}"
264		echo "$extra_sshd"
265	) > $OBJ/sshd_proxy
266	${SSH} -i $OBJ/cert_user_key_${ktype} -F $OBJ/ssh_proxy \
267	    somehost true >/dev/null 2>&1
268	if [ $? -eq 0 ]; then
269		fail "ssh cert connect succeeded unexpecedly"
270	fi
271
272	verbose "$tid: $auth CA does not authenticate"
273	(
274		cat $OBJ/sshd_proxy_bak
275		echo "PubkeyAcceptedAlgorithms ${t}"
276		echo "$extra_sshd"
277	) > $OBJ/sshd_proxy
278	verbose "$tid: ensure CA key does not authenticate user"
279	${SSH} -i $OBJ/user_ca_key \
280	    -F $OBJ/ssh_proxy somehost true >/dev/null 2>&1
281	if [ $? -eq 0 ]; then
282		fail "ssh cert connect with CA key succeeded unexpectedly"
283	fi
284}
285
286basic_tests authorized_keys
287basic_tests TrustedUserCAKeys
288
289test_one() {
290	ident=$1
291	result=$2
292	sign_opts=$3
293	auth_choice=$4
294	auth_opt=$5
295
296	if test "x$auth_choice" = "x" ; then
297		auth_choice="authorized_keys TrustedUserCAKeys"
298	fi
299
300	for auth in $auth_choice ; do
301		for ktype in $rsa ed25519 ; do
302			cat $OBJ/sshd_proxy_bak > $OBJ/sshd_proxy
303			if test "x$auth" = "xauthorized_keys" ; then
304				# Add CA to authorized_keys
305				(
306					printf "cert-authority${auth_opt} "
307					cat $OBJ/user_ca_key.pub
308				) > $OBJ/authorized_keys_${USER}_X
309			else
310				echo > $OBJ/authorized_keys_${USER}_X
311				echo "TrustedUserCAKeys $OBJ/user_ca_key.pub" \
312				    >> $OBJ/sshd_proxy
313				echo "PubkeyAcceptedAlgorithms ${t}*" \
314				    >> $OBJ/sshd_proxy
315				if test "x$auth_opt" != "x" ; then
316					echo $auth_opt >> $OBJ/sshd_proxy
317				fi
318			fi
319
320			verbose "$tid: $ident auth $auth expect $result $ktype"
321			${SSHKEYGEN} -q -s $OBJ/user_ca_key \
322			    -I "regress user key for $USER" \
323			    $sign_opts $OBJ/cert_user_key_${ktype} ||
324				fail "couldn't sign cert_user_key_${ktype}"
325
326			${SSH} -i $OBJ/cert_user_key_${ktype} \
327			    -F $OBJ/ssh_proxy somehost true >/dev/null 2>&1
328			rc=$?
329			if [ "x$result" = "xsuccess" ] ; then
330				if [ $rc -ne 0 ]; then
331					fail "$ident failed unexpectedly"
332				fi
333			else
334				if [ $rc -eq 0 ]; then
335					fail "$ident succeeded unexpectedly"
336				fi
337			fi
338		done
339	done
340}
341
342test_one "correct principal"	success "-n ${USER}"
343test_one "host-certificate"	failure "-n ${USER} -h"
344test_one "wrong principals"	failure "-n foo"
345test_one "cert not yet valid"	failure "-n ${USER} -V20300101:20320101"
346test_one "cert expired"		failure "-n ${USER} -V19800101:19900101"
347test_one "cert valid interval"	success "-n ${USER} -V-1w:+2w"
348test_one "wrong source-address"	failure "-n ${USER} -Osource-address=10.0.0.0/8"
349test_one "force-command"	failure "-n ${USER} -Oforce-command=false"
350
351# Behaviour is different here: TrustedUserCAKeys doesn't allow empty principals
352test_one "empty principals"	success "" authorized_keys
353test_one "empty principals"	failure "" TrustedUserCAKeys
354
355# Check explicitly-specified principals: an empty principals list in the cert
356# should always be refused.
357
358# AuthorizedPrincipalsFile
359rm -f $OBJ/authorized_keys_${USER}_X
360echo mekmitasdigoat > $OBJ/authorized_principals_${USER}_X
361test_one "AuthorizedPrincipalsFile principals" success "-n mekmitasdigoat" \
362    TrustedUserCAKeys "AuthorizedPrincipalsFile $OBJ/authorized_principals_%u_*"
363test_one "AuthorizedPrincipalsFile no principals" failure "" \
364    TrustedUserCAKeys "AuthorizedPrincipalsFile $OBJ/authorized_principals_%u_*"
365
366# principals= key option
367rm -f $OBJ/authorized_principals_${USER}_X
368test_one "principals key option principals" success "-n mekmitasdigoat" \
369    authorized_keys ',principals="mekmitasdigoat"'
370test_one "principals key option no principals" failure "" \
371    authorized_keys ',principals="mekmitasdigoat"'
372
373# command= options vs. force-command in key
374test_one "force-command match true" success \
375    "-n ${USER} -Oforce-command=true" \
376    authorized_keys ',command="true"'
377test_one "force-command match true" failure \
378    "-n ${USER} -Oforce-command=false" \
379    authorized_keys ',command="false"'
380test_one "force-command mismatch 1" failure \
381    "-n ${USER} -Oforce-command=false" \
382    authorized_keys ',command="true"'
383test_one "force-command mismatch 2" failure \
384    "-n ${USER} -Oforce-command=true" \
385    authorized_keys ',command="false"'
386
387# Wrong certificate
388cat $OBJ/sshd_proxy_bak > $OBJ/sshd_proxy
389for ktype in $PLAIN_TYPES ; do
390	t=$(kname $ktype)
391	# Self-sign
392	${SSHKEYGEN} -q -s $OBJ/cert_user_key_${ktype} -I \
393	    "regress user key for $USER" \
394	    -n $USER $OBJ/cert_user_key_${ktype} ||
395		fatal "couldn't sign cert_user_key_${ktype}"
396	verbose "$tid: user ${ktype} connect wrong cert"
397	${SSH} -i $OBJ/cert_user_key_${ktype} -F $OBJ/ssh_proxy \
398	    somehost true >/dev/null 2>&1
399	if [ $? -eq 0 ]; then
400		fail "ssh cert connect $ident succeeded unexpectedly"
401	fi
402done
403
404rm -f $OBJ/authorized_keys_${USER}* $OBJ/user_ca_key* $OBJ/cert_user_key*
405rm -f $OBJ/authorized_principals*
406
407