1#	$OpenBSD: cert-hostkey.sh,v 1.25 2021/06/08 22:30:27 djm Exp $
2#	Placed in the Public Domain.
3
4tid="certified host keys"
5
6rm -f $OBJ/known_hosts-cert* $OBJ/host_ca_key* $OBJ/host_revoked_*
7rm -f $OBJ/cert_host_key* $OBJ/host_krl_*
8
9# Allow all hostkey/pubkey types, prefer certs for the client
10rsa=0
11types=""
12for i in `$SSH -Q key | maybe_filter_sk`; do
13	if [ -z "$types" ]; then
14		types="$i"
15		continue
16	fi
17	case "$i" in
18	# Special treatment for RSA keys.
19	*rsa*cert*)
20		types="rsa-sha2-256-cert-v01@openssh.com,$i,$types"
21		types="rsa-sha2-512-cert-v01@openssh.com,$types";;
22	*rsa*)
23		rsa=1
24		types="$types,rsa-sha2-512,rsa-sha2-256,$i";;
25	# Prefer certificate to plain keys.
26	*cert*)	types="$i,$types";;
27	*)	types="$types,$i";;
28	esac
29done
30(
31	echo "HostKeyAlgorithms ${types}"
32	echo "PubkeyAcceptedAlgorithms *"
33) >> $OBJ/ssh_proxy
34cp $OBJ/sshd_proxy $OBJ/sshd_proxy_bak
35(
36	echo "HostKeyAlgorithms *"
37	echo "PubkeyAcceptedAlgorithms *"
38) >> $OBJ/sshd_proxy_bak
39
40HOSTS='localhost-with-alias,127.0.0.1,::1'
41
42kh_ca() {
43	for k in "$@" ; do
44		printf "@cert-authority $HOSTS "
45		cat $OBJ/$k || fatal "couldn't cat $k"
46	done
47}
48kh_revoke() {
49	for k in "$@" ; do
50		printf "@revoked * "
51		cat $OBJ/$k || fatal "couldn't cat $k"
52	done
53}
54
55# Create a CA key and add it to known hosts. Ed25519 chosen for speed.
56# RSA for testing RSA/SHA2 signatures if supported.
57ktype2=ed25519
58[ "x$rsa" = "x1" ] && ktype2=rsa
59${SSHKEYGEN} -q -N '' -t ed25519  -f $OBJ/host_ca_key ||\
60	fail "ssh-keygen of host_ca_key failed"
61${SSHKEYGEN} -q -N '' -t $ktype2  -f $OBJ/host_ca_key2 ||\
62	fail "ssh-keygen of host_ca_key failed"
63
64kh_ca host_ca_key.pub host_ca_key2.pub > $OBJ/known_hosts-cert.orig
65cp $OBJ/known_hosts-cert.orig $OBJ/known_hosts-cert
66
67# Plain text revocation files
68touch $OBJ/host_revoked_empty
69touch $OBJ/host_revoked_plain
70touch $OBJ/host_revoked_cert
71cat $OBJ/host_ca_key.pub $OBJ/host_ca_key2.pub > $OBJ/host_revoked_ca
72
73PLAIN_TYPES=`echo "$SSH_KEYTYPES" | sed 's/^ssh-dss/ssh-dsa/g;s/^ssh-//'`
74
75if echo "$PLAIN_TYPES" | grep '^rsa$' >/dev/null 2>&1 ; then
76	PLAIN_TYPES="$PLAIN_TYPES rsa-sha2-256 rsa-sha2-512"
77fi
78
79# Prepare certificate, plain key and CA KRLs
80${SSHKEYGEN} -kf $OBJ/host_krl_empty || fatal "KRL init failed"
81${SSHKEYGEN} -kf $OBJ/host_krl_plain || fatal "KRL init failed"
82${SSHKEYGEN} -kf $OBJ/host_krl_cert || fatal "KRL init failed"
83${SSHKEYGEN} -kf $OBJ/host_krl_ca $OBJ/host_ca_key.pub $OBJ/host_ca_key2.pub \
84	|| fatal "KRL init failed"
85
86# Generate and sign host keys
87serial=1
88for ktype in $PLAIN_TYPES ; do
89	verbose "$tid: sign host ${ktype} cert"
90	# Generate and sign a host key
91	${SSHKEYGEN} -q -N '' -t ${ktype} \
92	    -f $OBJ/cert_host_key_${ktype} || \
93		fatal "ssh-keygen of cert_host_key_${ktype} failed"
94	${SSHKEYGEN} -ukf $OBJ/host_krl_plain \
95	    $OBJ/cert_host_key_${ktype}.pub || fatal "KRL update failed"
96	cat $OBJ/cert_host_key_${ktype}.pub >> $OBJ/host_revoked_plain
97	case $ktype in
98	rsa-sha2-*)	tflag="-t $ktype"; ca="$OBJ/host_ca_key2" ;;
99	*)		tflag=""; ca="$OBJ/host_ca_key" ;;
100	esac
101	${SSHKEYGEN} -h -q -s $ca -z $serial $tflag \
102	    -I "regress host key for $USER" \
103	    -n $HOSTS $OBJ/cert_host_key_${ktype} ||
104		fatal "couldn't sign cert_host_key_${ktype}"
105	${SSHKEYGEN} -ukf $OBJ/host_krl_cert \
106	    $OBJ/cert_host_key_${ktype}-cert.pub || \
107		fatal "KRL update failed"
108	cat $OBJ/cert_host_key_${ktype}-cert.pub >> $OBJ/host_revoked_cert
109	serial=`expr $serial + 1`
110done
111
112attempt_connect() {
113	_ident="$1"
114	_expect_success="$2"
115	shift; shift
116	verbose "$tid: $_ident expect success $_expect_success"
117	cp $OBJ/known_hosts-cert.orig $OBJ/known_hosts-cert
118	${SSH} -oUserKnownHostsFile=$OBJ/known_hosts-cert \
119	    -oGlobalKnownHostsFile=$OBJ/known_hosts-cert \
120	    "$@" -F $OBJ/ssh_proxy somehost true
121	_r=$?
122	if [ "x$_expect_success" = "xyes" ] ; then
123		if [ $_r -ne 0 ]; then
124			fail "ssh cert connect $_ident failed"
125		fi
126	else
127		if [ $_r -eq 0 ]; then
128			fail "ssh cert connect $_ident succeeded unexpectedly"
129		fi
130	fi
131}
132
133# Basic connect and revocation tests.
134for privsep in yes ; do
135	for ktype in $PLAIN_TYPES ; do
136		verbose "$tid: host ${ktype} cert connect privsep $privsep"
137		(
138			cat $OBJ/sshd_proxy_bak
139			echo HostKey $OBJ/cert_host_key_${ktype}
140			echo HostCertificate $OBJ/cert_host_key_${ktype}-cert.pub
141			echo UsePrivilegeSeparation $privsep
142		) > $OBJ/sshd_proxy
143
144		#               test name                         expect success
145		attempt_connect "$ktype basic connect"			"yes"
146		attempt_connect "$ktype empty KRL"			"yes" \
147		    -oRevokedHostKeys=$OBJ/host_krl_empty
148		attempt_connect "$ktype KRL w/ plain key revoked"	"no" \
149		    -oRevokedHostKeys=$OBJ/host_krl_plain
150		attempt_connect "$ktype KRL w/ cert revoked"		"no" \
151		    -oRevokedHostKeys=$OBJ/host_krl_cert
152		attempt_connect "$ktype KRL w/ CA revoked"		"no" \
153		    -oRevokedHostKeys=$OBJ/host_krl_ca
154		attempt_connect "$ktype empty plaintext revocation"	"yes" \
155		    -oRevokedHostKeys=$OBJ/host_revoked_empty
156		attempt_connect "$ktype plain key plaintext revocation"	"no" \
157		    -oRevokedHostKeys=$OBJ/host_revoked_plain
158		attempt_connect "$ktype cert plaintext revocation"	"no" \
159		    -oRevokedHostKeys=$OBJ/host_revoked_cert
160		attempt_connect "$ktype CA plaintext revocation"	"no" \
161		    -oRevokedHostKeys=$OBJ/host_revoked_ca
162	done
163done
164
165# Revoked certificates with key present
166kh_ca host_ca_key.pub host_ca_key2.pub > $OBJ/known_hosts-cert.orig
167for ktype in $PLAIN_TYPES ; do
168	test -f "$OBJ/cert_host_key_${ktype}.pub" || fatal "no pubkey"
169	kh_revoke cert_host_key_${ktype}.pub >> $OBJ/known_hosts-cert.orig
170done
171cp $OBJ/known_hosts-cert.orig $OBJ/known_hosts-cert
172for privsep in yes ; do
173	for ktype in $PLAIN_TYPES ; do
174		verbose "$tid: host ${ktype} revoked cert privsep $privsep"
175		(
176			cat $OBJ/sshd_proxy_bak
177			echo HostKey $OBJ/cert_host_key_${ktype}
178			echo HostCertificate $OBJ/cert_host_key_${ktype}-cert.pub
179			echo UsePrivilegeSeparation $privsep
180		) > $OBJ/sshd_proxy
181
182		cp $OBJ/known_hosts-cert.orig $OBJ/known_hosts-cert
183		${SSH} -oUserKnownHostsFile=$OBJ/known_hosts-cert \
184		    -oGlobalKnownHostsFile=$OBJ/known_hosts-cert \
185			-F $OBJ/ssh_proxy somehost true >/dev/null 2>&1
186		if [ $? -eq 0 ]; then
187			fail "ssh cert connect succeeded unexpectedly"
188		fi
189	done
190done
191
192# Revoked CA
193kh_ca host_ca_key.pub host_ca_key2.pub > $OBJ/known_hosts-cert.orig
194kh_revoke host_ca_key.pub host_ca_key2.pub >> $OBJ/known_hosts-cert.orig
195cp $OBJ/known_hosts-cert.orig $OBJ/known_hosts-cert
196for ktype in $PLAIN_TYPES ; do
197	verbose "$tid: host ${ktype} revoked cert"
198	(
199		cat $OBJ/sshd_proxy_bak
200		echo HostKey $OBJ/cert_host_key_${ktype}
201		echo HostCertificate $OBJ/cert_host_key_${ktype}-cert.pub
202	) > $OBJ/sshd_proxy
203	cp $OBJ/known_hosts-cert.orig $OBJ/known_hosts-cert
204	${SSH} -oUserKnownHostsFile=$OBJ/known_hosts-cert \
205	    -oGlobalKnownHostsFile=$OBJ/known_hosts-cert \
206		-F $OBJ/ssh_proxy somehost true >/dev/null 2>&1
207	if [ $? -eq 0 ]; then
208		fail "ssh cert connect succeeded unexpectedly"
209	fi
210done
211
212# Create a CA key and add it to known hosts
213kh_ca host_ca_key.pub host_ca_key2.pub > $OBJ/known_hosts-cert.orig
214cp $OBJ/known_hosts-cert.orig $OBJ/known_hosts-cert
215
216test_one() {
217	ident=$1
218	result=$2
219	sign_opts=$3
220
221	for kt in $PLAIN_TYPES; do
222		case $ktype in
223		rsa-sha2-*)	tflag="-t $ktype"; ca="$OBJ/host_ca_key2" ;;
224		*)		tflag=""; ca="$OBJ/host_ca_key" ;;
225		esac
226		${SSHKEYGEN} -q -s $ca $tflag -I "regress host key for $USER" \
227		    $sign_opts $OBJ/cert_host_key_${kt} ||
228			fatal "couldn't sign cert_host_key_${kt}"
229		(
230			cat $OBJ/sshd_proxy_bak
231			echo HostKey $OBJ/cert_host_key_${kt}
232			echo HostCertificate $OBJ/cert_host_key_${kt}-cert.pub
233		) > $OBJ/sshd_proxy
234
235		cp $OBJ/known_hosts-cert.orig $OBJ/known_hosts-cert
236		${SSH} -oUserKnownHostsFile=$OBJ/known_hosts-cert \
237		    -oGlobalKnownHostsFile=$OBJ/known_hosts-cert \
238		    -F $OBJ/ssh_proxy somehost true >/dev/null 2>&1
239		rc=$?
240		if [ "x$result" = "xsuccess" ] ; then
241			if [ $rc -ne 0 ]; then
242				fail "ssh cert connect $ident failed unexpectedly"
243			fi
244		else
245			if [ $rc -eq 0 ]; then
246				fail "ssh cert connect $ident succeeded unexpectedly"
247			fi
248		fi
249	done
250}
251
252test_one "user-certificate"	failure "-n $HOSTS"
253test_one "empty principals"	success "-h"
254test_one "wrong principals"	failure "-h -n foo"
255test_one "cert not yet valid"	failure "-h -V20300101:20320101"
256test_one "cert expired"		failure "-h -V19800101:19900101"
257test_one "cert valid interval"	success "-h -V-1w:+2w"
258test_one "cert has constraints"	failure "-h -Oforce-command=false"
259
260# Check downgrade of cert to raw key when no CA found
261for ktype in $PLAIN_TYPES ; do
262	rm -f $OBJ/known_hosts-cert $OBJ/cert_host_key*
263	verbose "$tid: host ${ktype} ${v} cert downgrade to raw key"
264	# Generate and sign a host key
265	${SSHKEYGEN} -q -N '' -t ${ktype} -f $OBJ/cert_host_key_${ktype} || \
266		fail "ssh-keygen of cert_host_key_${ktype} failed"
267	case $ktype in
268	rsa-sha2-*)	tflag="-t $ktype"; ca="$OBJ/host_ca_key2" ;;
269	*)		tflag=""; ca="$OBJ/host_ca_key" ;;
270	esac
271	${SSHKEYGEN} -h -q $tflag -s $ca $tflag \
272	    -I "regress host key for $USER" \
273	    -n $HOSTS $OBJ/cert_host_key_${ktype} ||
274		fatal "couldn't sign cert_host_key_${ktype}"
275	(
276		printf "$HOSTS "
277		cat $OBJ/cert_host_key_${ktype}.pub
278	) > $OBJ/known_hosts-cert
279	(
280		cat $OBJ/sshd_proxy_bak
281		echo HostKey $OBJ/cert_host_key_${ktype}
282		echo HostCertificate $OBJ/cert_host_key_${ktype}-cert.pub
283	) > $OBJ/sshd_proxy
284
285	${SSH} -oUserKnownHostsFile=$OBJ/known_hosts-cert \
286	    -oGlobalKnownHostsFile=none -F $OBJ/ssh_proxy somehost true
287	if [ $? -ne 0 ]; then
288		fail "ssh cert connect failed"
289	fi
290	# Also check that it works when the known_hosts file is not in the
291	# first array position.
292	${SSH} -oUserKnownHostsFile="/dev/null $OBJ/known_hosts-cert" \
293	    -oGlobalKnownHostsFile=none -F $OBJ/ssh_proxy somehost true
294	if [ $? -ne 0 ]; then
295		fail "ssh cert connect failed known_hosts 2nd"
296	fi
297done
298
299# Wrong certificate
300kh_ca host_ca_key.pub host_ca_key2.pub > $OBJ/known_hosts-cert.orig
301cp $OBJ/known_hosts-cert.orig $OBJ/known_hosts-cert
302for kt in $PLAIN_TYPES ; do
303	verbose "$tid: host ${kt} connect wrong cert"
304	rm -f $OBJ/cert_host_key*
305	# Self-sign key
306	${SSHKEYGEN} -q -N '' -t ${kt} -f $OBJ/cert_host_key_${kt} || \
307		fail "ssh-keygen of cert_host_key_${kt} failed"
308	case $kt in
309	rsa-sha2-*)	tflag="-t $kt" ;;
310	*)		tflag="" ;;
311	esac
312	${SSHKEYGEN} $tflag -h -q -s $OBJ/cert_host_key_${kt} \
313	    -I "regress host key for $USER" \
314	    -n $HOSTS $OBJ/cert_host_key_${kt} ||
315		fatal "couldn't sign cert_host_key_${kt}"
316	(
317		cat $OBJ/sshd_proxy_bak
318		echo HostKey $OBJ/cert_host_key_${kt}
319		echo HostCertificate $OBJ/cert_host_key_${kt}-cert.pub
320	) > $OBJ/sshd_proxy
321
322	cp $OBJ/known_hosts-cert.orig $OBJ/known_hosts-cert
323	${SSH} -oUserKnownHostsFile=$OBJ/known_hosts-cert \
324	    -oGlobalKnownHostsFile=$OBJ/known_hosts-cert \
325		-F $OBJ/ssh_proxy -q somehost true >/dev/null 2>&1
326	if [ $? -eq 0 ]; then
327		fail "ssh cert connect $ident succeeded unexpectedly"
328	fi
329done
330
331rm -f $OBJ/known_hosts-cert* $OBJ/host_ca_key* $OBJ/cert_host_key*
332