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