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