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