xref: /freebsd/crypto/openssh/regress/cert-userkey.sh (revision 2574974648c68c738aec3ff96644d888d7913a37)
1#	$OpenBSD: cert-userkey.sh,v 1.32 2026/02/11 22:58:23 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-//'`
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/^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			# Also test multiple RevokedKeys files.
230			echo "RevokedKeys /dev/null $OBJ/cert_user_key_revoked"
231			echo "PubkeyAcceptedAlgorithms ${t}"
232			echo "$extra_sshd"
233		) > $OBJ/sshd_proxy
234		cp $OBJ/cert_user_key_${ktype}.pub \
235		    $OBJ/cert_user_key_revoked
236		${SSH} -i $OBJ/cert_user_key_${ktype} \
237		    -F $OBJ/ssh_proxy somehost true >/dev/null 2>&1
238		if [ $? -eq 0 ]; then
239			fail "ssh cert connect succeeded unexpecedly"
240		fi
241		verbose "$tid: ${_prefix} revoked via KRL"
242		rm $OBJ/cert_user_key_revoked
243		${SSHKEYGEN} -kqf $OBJ/cert_user_key_revoked \
244		    $OBJ/cert_user_key_${ktype}.pub
245		${SSH} -i $OBJ/cert_user_key_${ktype} \
246		    -F $OBJ/ssh_proxy somehost true >/dev/null 2>&1
247		if [ $? -eq 0 ]; then
248			fail "ssh cert connect succeeded unexpecedly"
249		fi
250		verbose "$tid: ${_prefix} empty KRL"
251		${SSHKEYGEN} -kqf $OBJ/cert_user_key_revoked
252		${SSH} -i $OBJ/cert_user_key_${ktype} \
253		    -F $OBJ/ssh_proxy somehost true >/dev/null 2>&1
254		if [ $? -ne 0 ]; then
255			fail "ssh cert connect failed"
256		fi
257	done
258
259	# Revoked CA
260	verbose "$tid: ${ktype} $auth revoked CA key"
261	(
262		cat $OBJ/sshd_proxy_bak
263		echo "RevokedKeys $OBJ/user_ca_key.pub"
264		echo "PubkeyAcceptedAlgorithms ${t}"
265		echo "$extra_sshd"
266	) > $OBJ/sshd_proxy
267	${SSH} -i $OBJ/cert_user_key_${ktype} -F $OBJ/ssh_proxy \
268	    somehost true >/dev/null 2>&1
269	if [ $? -eq 0 ]; then
270		fail "ssh cert connect succeeded unexpecedly"
271	fi
272
273	verbose "$tid: $auth CA does not authenticate"
274	(
275		cat $OBJ/sshd_proxy_bak
276		echo "PubkeyAcceptedAlgorithms ${t}"
277		echo "$extra_sshd"
278	) > $OBJ/sshd_proxy
279	verbose "$tid: ensure CA key does not authenticate user"
280	${SSH} -i $OBJ/user_ca_key \
281	    -F $OBJ/ssh_proxy somehost true >/dev/null 2>&1
282	if [ $? -eq 0 ]; then
283		fail "ssh cert connect with CA key succeeded unexpectedly"
284	fi
285}
286
287basic_tests authorized_keys
288basic_tests TrustedUserCAKeys
289
290test_one() {
291	ident=$1
292	result=$2
293	sign_opts=$3
294	auth_choice=$4
295	auth_opt=$5
296
297	if test "x$auth_choice" = "x" ; then
298		auth_choice="authorized_keys TrustedUserCAKeys"
299	fi
300
301	for auth in $auth_choice ; do
302		for ktype in $rsa ed25519 ; do
303			cat $OBJ/sshd_proxy_bak > $OBJ/sshd_proxy
304			if test "x$auth" = "xauthorized_keys" ; then
305				# Add CA to authorized_keys
306				(
307					printf "cert-authority${auth_opt} "
308					cat $OBJ/user_ca_key.pub
309				) > $OBJ/authorized_keys_${USER}_X
310			else
311				echo > $OBJ/authorized_keys_${USER}_X
312				echo "TrustedUserCAKeys $OBJ/user_ca_key.pub" \
313				    >> $OBJ/sshd_proxy
314				echo "PubkeyAcceptedAlgorithms ${t}*" \
315				    >> $OBJ/sshd_proxy
316				if test "x$auth_opt" != "x" ; then
317					echo $auth_opt >> $OBJ/sshd_proxy
318				fi
319			fi
320
321			verbose "$tid: $ident auth $auth expect $result $ktype"
322			${SSHKEYGEN} -q -s $OBJ/user_ca_key \
323			    -I "regress user key for $USER" \
324			    $sign_opts $OBJ/cert_user_key_${ktype} ||
325				fail "couldn't sign cert_user_key_${ktype}"
326
327			${SSH} -i $OBJ/cert_user_key_${ktype} \
328			    -F $OBJ/ssh_proxy somehost true >/dev/null 2>&1
329			rc=$?
330			if [ "x$result" = "xsuccess" ] ; then
331				if [ $rc -ne 0 ]; then
332					fail "$ident failed unexpectedly"
333				fi
334			else
335				if [ $rc -eq 0 ]; then
336					fail "$ident succeeded unexpectedly"
337				fi
338			fi
339		done
340	done
341}
342
343test_one "correct principal"	success "-n ${USER}"
344test_one "correct principal"	success "-n ${USER},*"
345test_one "host-certificate"	failure "-n ${USER} -h"
346test_one "wrong principals"	failure "-n foo,*"
347test_one "cert not yet valid"	failure "-n ${USER} -V20300101:20320101"
348test_one "cert expired"		failure "-n ${USER} -V19800101:19900101"
349test_one "cert valid interval"	success "-n ${USER} -V-1w:+2w"
350test_one "wrong source-address"	failure "-n ${USER} -Osource-address=10.0.0.0/8"
351test_one "force-command"	failure "-n ${USER} -Oforce-command=false"
352test_one "empty principals"	failure "" 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