summaryrefslogtreecommitdiffstats
path: root/regress/cert-hostkey.sh
blob: de8652b0e5e274376cc0fda74383ed8f478b309b (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
#	$OpenBSD: cert-hostkey.sh,v 1.25 2021/06/08 22:30:27 djm Exp $
#	Placed in the Public Domain.

tid="certified host keys"

rm -f $OBJ/known_hosts-cert* $OBJ/host_ca_key* $OBJ/host_revoked_*
rm -f $OBJ/cert_host_key* $OBJ/host_krl_*

# Allow all hostkey/pubkey types, prefer certs for the client
rsa=0
types=""
for i in `$SSH -Q key | maybe_filter_sk`; do
	if [ -z "$types" ]; then
		types="$i"
		continue
	fi
	case "$i" in
	# Special treatment for RSA keys.
	*rsa*cert*)
		types="rsa-sha2-256-cert-v01@openssh.com,$i,$types"
		types="rsa-sha2-512-cert-v01@openssh.com,$types";;
	*rsa*)
		rsa=1
		types="$types,rsa-sha2-512,rsa-sha2-256,$i";;
	# Prefer certificate to plain keys.
	*cert*)	types="$i,$types";;
	*)	types="$types,$i";;
	esac
done
(
	echo "HostKeyAlgorithms ${types}"
	echo "PubkeyAcceptedAlgorithms *"
) >> $OBJ/ssh_proxy
cp $OBJ/sshd_proxy $OBJ/sshd_proxy_bak
(
	echo "HostKeyAlgorithms *"
	echo "PubkeyAcceptedAlgorithms *"
) >> $OBJ/sshd_proxy_bak

HOSTS='localhost-with-alias,127.0.0.1,::1'

kh_ca() {
	for k in "$@" ; do
		printf "@cert-authority $HOSTS "
		cat $OBJ/$k || fatal "couldn't cat $k"
	done
}
kh_revoke() {
	for k in "$@" ; do
		printf "@revoked * "
		cat $OBJ/$k || fatal "couldn't cat $k"
	done
}

# Create a CA key and add it to known hosts. Ed25519 chosen for speed.
# RSA for testing RSA/SHA2 signatures if supported.
ktype2=ed25519
[ "x$rsa" = "x1" ] && ktype2=rsa
${SSHKEYGEN} -q -N '' -t ed25519  -f $OBJ/host_ca_key ||\
	fail "ssh-keygen of host_ca_key failed"
${SSHKEYGEN} -q -N '' -t $ktype2  -f $OBJ/host_ca_key2 ||\
	fail "ssh-keygen of host_ca_key failed"

kh_ca host_ca_key.pub host_ca_key2.pub > $OBJ/known_hosts-cert.orig
cp $OBJ/known_hosts-cert.orig $OBJ/known_hosts-cert

# Plain text revocation files
touch $OBJ/host_revoked_empty
touch $OBJ/host_revoked_plain
touch $OBJ/host_revoked_cert
cat $OBJ/host_ca_key.pub $OBJ/host_ca_key2.pub > $OBJ/host_revoked_ca

PLAIN_TYPES=`echo "$SSH_KEYTYPES" | sed 's/^ssh-dss/ssh-dsa/g;s/^ssh-//'`

if echo "$PLAIN_TYPES" | grep '^rsa$' >/dev/null 2>&1 ; then
	PLAIN_TYPES="$PLAIN_TYPES rsa-sha2-256 rsa-sha2-512"
fi

# Prepare certificate, plain key and CA KRLs
${SSHKEYGEN} -kf $OBJ/host_krl_empty || fatal "KRL init failed"
${SSHKEYGEN} -kf $OBJ/host_krl_plain || fatal "KRL init failed"
${SSHKEYGEN} -kf $OBJ/host_krl_cert || fatal "KRL init failed"
${SSHKEYGEN} -kf $OBJ/host_krl_ca $OBJ/host_ca_key.pub $OBJ/host_ca_key2.pub \
	|| fatal "KRL init failed"

# Generate and sign host keys
serial=1
for ktype in $PLAIN_TYPES ; do
	verbose "$tid: sign host ${ktype} cert"
	# Generate and sign a host key
	${SSHKEYGEN} -q -N '' -t ${ktype} \
	    -f $OBJ/cert_host_key_${ktype} || \
		fatal "ssh-keygen of cert_host_key_${ktype} failed"
	${SSHKEYGEN} -ukf $OBJ/host_krl_plain \
	    $OBJ/cert_host_key_${ktype}.pub || fatal "KRL update failed"
	cat $OBJ/cert_host_key_${ktype}.pub >> $OBJ/host_revoked_plain
	case $ktype in
	rsa-sha2-*)	tflag="-t $ktype"; ca="$OBJ/host_ca_key2" ;;
	*)		tflag=""; ca="$OBJ/host_ca_key" ;;
	esac
	${SSHKEYGEN} -h -q -s $ca -z $serial $tflag \
	    -I "regress host key for $USER" \
	    -n $HOSTS $OBJ/cert_host_key_${ktype} ||
		fatal "couldn't sign cert_host_key_${ktype}"
	${SSHKEYGEN} -ukf $OBJ/host_krl_cert \
	    $OBJ/cert_host_key_${ktype}-cert.pub || \
		fatal "KRL update failed"
	cat $OBJ/cert_host_key_${ktype}-cert.pub >> $OBJ/host_revoked_cert
	serial=`expr $serial + 1`
done

attempt_connect() {
	_ident="$1"
	_expect_success="$2"
	shift; shift
	verbose "$tid: $_ident expect success $_expect_success"
	cp $OBJ/known_hosts-cert.orig $OBJ/known_hosts-cert
	${SSH} -oUserKnownHostsFile=$OBJ/known_hosts-cert \
	    -oGlobalKnownHostsFile=$OBJ/known_hosts-cert \
	    "$@" -F $OBJ/ssh_proxy somehost true
	_r=$?
	if [ "x$_expect_success" = "xyes" ] ; then
		if [ $_r -ne 0 ]; then
			fail "ssh cert connect $_ident failed"
		fi
	else
		if [ $_r -eq 0 ]; then
			fail "ssh cert connect $_ident succeeded unexpectedly"
		fi
	fi
}

# Basic connect and revocation tests.
for privsep in yes ; do
	for ktype in $PLAIN_TYPES ; do
		verbose "$tid: host ${ktype} cert connect privsep $privsep"
		(
			cat $OBJ/sshd_proxy_bak
			echo HostKey $OBJ/cert_host_key_${ktype}
			echo HostCertificate $OBJ/cert_host_key_${ktype}-cert.pub
			echo UsePrivilegeSeparation $privsep
		) > $OBJ/sshd_proxy

		#               test name                         expect success
		attempt_connect "$ktype basic connect"			"yes"
		attempt_connect "$ktype empty KRL"			"yes" \
		    -oRevokedHostKeys=$OBJ/host_krl_empty
		attempt_connect "$ktype KRL w/ plain key revoked"	"no" \
		    -oRevokedHostKeys=$OBJ/host_krl_plain
		attempt_connect "$ktype KRL w/ cert revoked"		"no" \
		    -oRevokedHostKeys=$OBJ/host_krl_cert
		attempt_connect "$ktype KRL w/ CA revoked"		"no" \
		    -oRevokedHostKeys=$OBJ/host_krl_ca
		attempt_connect "$ktype empty plaintext revocation"	"yes" \
		    -oRevokedHostKeys=$OBJ/host_revoked_empty
		attempt_connect "$ktype plain key plaintext revocation"	"no" \
		    -oRevokedHostKeys=$OBJ/host_revoked_plain
		attempt_connect "$ktype cert plaintext revocation"	"no" \
		    -oRevokedHostKeys=$OBJ/host_revoked_cert
		attempt_connect "$ktype CA plaintext revocation"	"no" \
		    -oRevokedHostKeys=$OBJ/host_revoked_ca
	done
done

# Revoked certificates with key present
kh_ca host_ca_key.pub host_ca_key2.pub > $OBJ/known_hosts-cert.orig
for ktype in $PLAIN_TYPES ; do
	test -f "$OBJ/cert_host_key_${ktype}.pub" || fatal "no pubkey"
	kh_revoke cert_host_key_${ktype}.pub >> $OBJ/known_hosts-cert.orig
done
cp $OBJ/known_hosts-cert.orig $OBJ/known_hosts-cert
for privsep in yes ; do
	for ktype in $PLAIN_TYPES ; do
		verbose "$tid: host ${ktype} revoked cert privsep $privsep"
		(
			cat $OBJ/sshd_proxy_bak
			echo HostKey $OBJ/cert_host_key_${ktype}
			echo HostCertificate $OBJ/cert_host_key_${ktype}-cert.pub
			echo UsePrivilegeSeparation $privsep
		) > $OBJ/sshd_proxy

		cp $OBJ/known_hosts-cert.orig $OBJ/known_hosts-cert
		${SSH} -oUserKnownHostsFile=$OBJ/known_hosts-cert \
		    -oGlobalKnownHostsFile=$OBJ/known_hosts-cert \
			-F $OBJ/ssh_proxy somehost true >/dev/null 2>&1
		if [ $? -eq 0 ]; then
			fail "ssh cert connect succeeded unexpectedly"
		fi
	done
done

# Revoked CA
kh_ca host_ca_key.pub host_ca_key2.pub > $OBJ/known_hosts-cert.orig
kh_revoke host_ca_key.pub host_ca_key2.pub >> $OBJ/known_hosts-cert.orig
cp $OBJ/known_hosts-cert.orig $OBJ/known_hosts-cert
for ktype in $PLAIN_TYPES ; do
	verbose "$tid: host ${ktype} revoked cert"
	(
		cat $OBJ/sshd_proxy_bak
		echo HostKey $OBJ/cert_host_key_${ktype}
		echo HostCertificate $OBJ/cert_host_key_${ktype}-cert.pub
	) > $OBJ/sshd_proxy
	cp $OBJ/known_hosts-cert.orig $OBJ/known_hosts-cert
	${SSH} -oUserKnownHostsFile=$OBJ/known_hosts-cert \
	    -oGlobalKnownHostsFile=$OBJ/known_hosts-cert \
		-F $OBJ/ssh_proxy somehost true >/dev/null 2>&1
	if [ $? -eq 0 ]; then
		fail "ssh cert connect succeeded unexpectedly"
	fi
done

# Create a CA key and add it to known hosts
kh_ca host_ca_key.pub host_ca_key2.pub > $OBJ/known_hosts-cert.orig
cp $OBJ/known_hosts-cert.orig $OBJ/known_hosts-cert

test_one() {
	ident=$1
	result=$2
	sign_opts=$3

	for kt in $PLAIN_TYPES; do
		case $ktype in
		rsa-sha2-*)	tflag="-t $ktype"; ca="$OBJ/host_ca_key2" ;;
		*)		tflag=""; ca="$OBJ/host_ca_key" ;;
		esac
		${SSHKEYGEN} -q -s $ca $tflag -I "regress host key for $USER" \
		    $sign_opts $OBJ/cert_host_key_${kt} ||
			fatal "couldn't sign cert_host_key_${kt}"
		(
			cat $OBJ/sshd_proxy_bak
			echo HostKey $OBJ/cert_host_key_${kt}
			echo HostCertificate $OBJ/cert_host_key_${kt}-cert.pub
		) > $OBJ/sshd_proxy

		cp $OBJ/known_hosts-cert.orig $OBJ/known_hosts-cert
		${SSH} -oUserKnownHostsFile=$OBJ/known_hosts-cert \
		    -oGlobalKnownHostsFile=$OBJ/known_hosts-cert \
		    -F $OBJ/ssh_proxy somehost true >/dev/null 2>&1
		rc=$?
		if [ "x$result" = "xsuccess" ] ; then
			if [ $rc -ne 0 ]; then
				fail "ssh cert connect $ident failed unexpectedly"
			fi
		else
			if [ $rc -eq 0 ]; then
				fail "ssh cert connect $ident succeeded unexpectedly"
			fi
		fi
	done
}

test_one "user-certificate"	failure "-n $HOSTS"
test_one "empty principals"	success "-h"
test_one "wrong principals"	failure "-h -n foo"
test_one "cert not yet valid"	failure "-h -V20300101:20320101"
test_one "cert expired"		failure "-h -V19800101:19900101"
test_one "cert valid interval"	success "-h -V-1w:+2w"
test_one "cert has constraints"	failure "-h -Oforce-command=false"

# Check downgrade of cert to raw key when no CA found
for ktype in $PLAIN_TYPES ; do
	rm -f $OBJ/known_hosts-cert $OBJ/cert_host_key*
	verbose "$tid: host ${ktype} ${v} cert downgrade to raw key"
	# Generate and sign a host key
	${SSHKEYGEN} -q -N '' -t ${ktype} -f $OBJ/cert_host_key_${ktype} || \
		fail "ssh-keygen of cert_host_key_${ktype} failed"
	case $ktype in
	rsa-sha2-*)	tflag="-t $ktype"; ca="$OBJ/host_ca_key2" ;;
	*)		tflag=""; ca="$OBJ/host_ca_key" ;;
	esac
	${SSHKEYGEN} -h -q $tflag -s $ca $tflag \
	    -I "regress host key for $USER" \
	    -n $HOSTS $OBJ/cert_host_key_${ktype} ||
		fatal "couldn't sign cert_host_key_${ktype}"
	(
		printf "$HOSTS "
		cat $OBJ/cert_host_key_${ktype}.pub
	) > $OBJ/known_hosts-cert
	(
		cat $OBJ/sshd_proxy_bak
		echo HostKey $OBJ/cert_host_key_${ktype}
		echo HostCertificate $OBJ/cert_host_key_${ktype}-cert.pub
	) > $OBJ/sshd_proxy

	${SSH} -oUserKnownHostsFile=$OBJ/known_hosts-cert \
	    -oGlobalKnownHostsFile=none -F $OBJ/ssh_proxy somehost true
	if [ $? -ne 0 ]; then
		fail "ssh cert connect failed"
	fi
	# Also check that it works when the known_hosts file is not in the
	# first array position.
	${SSH} -oUserKnownHostsFile="/dev/null $OBJ/known_hosts-cert" \
	    -oGlobalKnownHostsFile=none -F $OBJ/ssh_proxy somehost true
	if [ $? -ne 0 ]; then
		fail "ssh cert connect failed known_hosts 2nd"
	fi
done

# Wrong certificate
kh_ca host_ca_key.pub host_ca_key2.pub > $OBJ/known_hosts-cert.orig
cp $OBJ/known_hosts-cert.orig $OBJ/known_hosts-cert
for kt in $PLAIN_TYPES ; do
	verbose "$tid: host ${kt} connect wrong cert"
	rm -f $OBJ/cert_host_key*
	# Self-sign key
	${SSHKEYGEN} -q -N '' -t ${kt} -f $OBJ/cert_host_key_${kt} || \
		fail "ssh-keygen of cert_host_key_${kt} failed"
	case $kt in
	rsa-sha2-*)	tflag="-t $kt" ;;
	*)		tflag="" ;;
	esac
	${SSHKEYGEN} $tflag -h -q -s $OBJ/cert_host_key_${kt} \
	    -I "regress host key for $USER" \
	    -n $HOSTS $OBJ/cert_host_key_${kt} ||
		fatal "couldn't sign cert_host_key_${kt}"
	(
		cat $OBJ/sshd_proxy_bak
		echo HostKey $OBJ/cert_host_key_${kt}
		echo HostCertificate $OBJ/cert_host_key_${kt}-cert.pub
	) > $OBJ/sshd_proxy

	cp $OBJ/known_hosts-cert.orig $OBJ/known_hosts-cert
	${SSH} -oUserKnownHostsFile=$OBJ/known_hosts-cert \
	    -oGlobalKnownHostsFile=$OBJ/known_hosts-cert \
		-F $OBJ/ssh_proxy -q somehost true >/dev/null 2>&1
	if [ $? -eq 0 ]; then
		fail "ssh cert connect $ident succeeded unexpectedly"
	fi
done

rm -f $OBJ/known_hosts-cert* $OBJ/host_ca_key* $OBJ/cert_host_key*