cert-hostkey.sh 10.1 KB
Newer Older
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
#	$OpenBSD: cert-hostkey.sh,v 1.14 2016/05/02 09:52:00 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
types=""
for i in `$SSH -Q key`; do
	if [ -z "$types" ]; then
		types="$i"
		continue
	fi
	case "$i" in
	*cert*)	types="$i,$types";;
	*)	types="$types,$i";;
	esac
done
(
	echo "HostKeyAlgorithms ${types}"
	echo "PubkeyAcceptedKeyTypes *"
) >> $OBJ/ssh_proxy
cp $OBJ/sshd_proxy $OBJ/sshd_proxy_bak
(
	echo "HostKeyAlgorithms *"
	echo "PubkeyAcceptedKeyTypes *"
) >> $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.
${SSHKEYGEN} -q -N '' -t ed25519  -f $OBJ/host_ca_key ||\
	fail "ssh-keygen of host_ca_key failed"
${SSHKEYGEN} -q -N '' -t rsa  -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=`$SSH -Q key-plain | 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} -2 -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 no ; 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 no ; 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} -2 -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} -2 -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 rsa ed25519 ; 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} -2 -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 -V20200101:20300101"
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} -2 -oUserKnownHostsFile=$OBJ/known_hosts-cert \
	    -oGlobalKnownHostsFile=$OBJ/known_hosts-cert \
		-F $OBJ/ssh_proxy somehost true
	if [ $? -ne 0 ]; then
		fail "ssh cert connect failed"
	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} -2 -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*