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