1# $OpenBSD: cert-hostkey.sh,v 1.27 2021/09/30 05:26:26 dtucker 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 ktype in $PLAIN_TYPES ; do 135 verbose "$tid: host ${ktype} cert connect" 136 ( 137 cat $OBJ/sshd_proxy_bak 138 echo HostKey $OBJ/cert_host_key_${ktype} 139 echo HostCertificate $OBJ/cert_host_key_${ktype}-cert.pub 140 ) > $OBJ/sshd_proxy 141 142 # test name expect success 143 attempt_connect "$ktype basic connect" "yes" 144 attempt_connect "$ktype empty KRL" "yes" \ 145 -oRevokedHostKeys=$OBJ/host_krl_empty 146 attempt_connect "$ktype KRL w/ plain key revoked" "no" \ 147 -oRevokedHostKeys=$OBJ/host_krl_plain 148 attempt_connect "$ktype KRL w/ cert revoked" "no" \ 149 -oRevokedHostKeys=$OBJ/host_krl_cert 150 attempt_connect "$ktype KRL w/ CA revoked" "no" \ 151 -oRevokedHostKeys=$OBJ/host_krl_ca 152 attempt_connect "$ktype empty plaintext revocation" "yes" \ 153 -oRevokedHostKeys=$OBJ/host_revoked_empty 154 attempt_connect "$ktype plain key plaintext revocation" "no" \ 155 -oRevokedHostKeys=$OBJ/host_revoked_plain 156 attempt_connect "$ktype cert plaintext revocation" "no" \ 157 -oRevokedHostKeys=$OBJ/host_revoked_cert 158 attempt_connect "$ktype CA plaintext revocation" "no" \ 159 -oRevokedHostKeys=$OBJ/host_revoked_ca 160done 161 162# Revoked certificates with key present 163kh_ca host_ca_key.pub host_ca_key2.pub > $OBJ/known_hosts-cert.orig 164for ktype in $PLAIN_TYPES ; do 165 test -f "$OBJ/cert_host_key_${ktype}.pub" || fatal "no pubkey" 166 kh_revoke cert_host_key_${ktype}.pub >> $OBJ/known_hosts-cert.orig 167done 168cp $OBJ/known_hosts-cert.orig $OBJ/known_hosts-cert 169for ktype in $PLAIN_TYPES ; do 170 verbose "$tid: host ${ktype} revoked cert" 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 ) > $OBJ/sshd_proxy 176 177 cp $OBJ/known_hosts-cert.orig $OBJ/known_hosts-cert 178 ${SSH} -oUserKnownHostsFile=$OBJ/known_hosts-cert \ 179 -oGlobalKnownHostsFile=$OBJ/known_hosts-cert \ 180 -F $OBJ/ssh_proxy somehost true >/dev/null 2>&1 181 if [ $? -eq 0 ]; then 182 fail "ssh cert connect succeeded unexpectedly" 183 fi 184done 185 186# Revoked CA 187kh_ca host_ca_key.pub host_ca_key2.pub > $OBJ/known_hosts-cert.orig 188kh_revoke host_ca_key.pub host_ca_key2.pub >> $OBJ/known_hosts-cert.orig 189cp $OBJ/known_hosts-cert.orig $OBJ/known_hosts-cert 190for ktype in $PLAIN_TYPES ; do 191 verbose "$tid: host ${ktype} revoked cert" 192 ( 193 cat $OBJ/sshd_proxy_bak 194 echo HostKey $OBJ/cert_host_key_${ktype} 195 echo HostCertificate $OBJ/cert_host_key_${ktype}-cert.pub 196 ) > $OBJ/sshd_proxy 197 cp $OBJ/known_hosts-cert.orig $OBJ/known_hosts-cert 198 ${SSH} -oUserKnownHostsFile=$OBJ/known_hosts-cert \ 199 -oGlobalKnownHostsFile=$OBJ/known_hosts-cert \ 200 -F $OBJ/ssh_proxy somehost true >/dev/null 2>&1 201 if [ $? -eq 0 ]; then 202 fail "ssh cert connect succeeded unexpectedly" 203 fi 204done 205 206# Create a CA key and add it to known hosts 207kh_ca host_ca_key.pub host_ca_key2.pub > $OBJ/known_hosts-cert.orig 208cp $OBJ/known_hosts-cert.orig $OBJ/known_hosts-cert 209 210test_one() { 211 ident=$1 212 result=$2 213 sign_opts=$3 214 215 for kt in $PLAIN_TYPES; do 216 case $ktype in 217 rsa-sha2-*) tflag="-t $ktype"; ca="$OBJ/host_ca_key2" ;; 218 *) tflag=""; ca="$OBJ/host_ca_key" ;; 219 esac 220 ${SSHKEYGEN} -q -s $ca $tflag -I "regress host key for $USER" \ 221 $sign_opts $OBJ/cert_host_key_${kt} || 222 fatal "couldn't sign cert_host_key_${kt}" 223 ( 224 cat $OBJ/sshd_proxy_bak 225 echo HostKey $OBJ/cert_host_key_${kt} 226 echo HostCertificate $OBJ/cert_host_key_${kt}-cert.pub 227 ) > $OBJ/sshd_proxy 228 229 cp $OBJ/known_hosts-cert.orig $OBJ/known_hosts-cert 230 ${SSH} -oUserKnownHostsFile=$OBJ/known_hosts-cert \ 231 -oGlobalKnownHostsFile=$OBJ/known_hosts-cert \ 232 -F $OBJ/ssh_proxy somehost true >/dev/null 2>&1 233 rc=$? 234 if [ "x$result" = "xsuccess" ] ; then 235 if [ $rc -ne 0 ]; then 236 fail "ssh cert connect $ident failed unexpectedly" 237 fi 238 else 239 if [ $rc -eq 0 ]; then 240 fail "ssh cert connect $ident succeeded unexpectedly" 241 fi 242 fi 243 done 244} 245 246test_one "user-certificate" failure "-n $HOSTS" 247test_one "empty principals" success "-h" 248test_one "wrong principals" failure "-h -n foo" 249test_one "cert not yet valid" failure "-h -V20300101:20320101" 250test_one "cert expired" failure "-h -V19800101:19900101" 251test_one "cert valid interval" success "-h -V-1w:+2w" 252test_one "cert has constraints" failure "-h -Oforce-command=false" 253 254# Check downgrade of cert to raw key when no CA found 255for ktype in $PLAIN_TYPES ; do 256 rm -f $OBJ/known_hosts-cert $OBJ/cert_host_key* 257 verbose "$tid: host ${ktype} ${v} cert downgrade to raw key" 258 # Generate and sign a host key 259 ${SSHKEYGEN} -q -N '' -t ${ktype} -f $OBJ/cert_host_key_${ktype} || \ 260 fail "ssh-keygen of cert_host_key_${ktype} failed" 261 case $ktype in 262 rsa-sha2-*) tflag="-t $ktype"; ca="$OBJ/host_ca_key2" ;; 263 *) tflag=""; ca="$OBJ/host_ca_key" ;; 264 esac 265 ${SSHKEYGEN} -h -q $tflag -s $ca $tflag \ 266 -I "regress host key for $USER" \ 267 -n $HOSTS $OBJ/cert_host_key_${ktype} || 268 fatal "couldn't sign cert_host_key_${ktype}" 269 ( 270 printf "$HOSTS " 271 cat $OBJ/cert_host_key_${ktype}.pub 272 ) > $OBJ/known_hosts-cert 273 ( 274 cat $OBJ/sshd_proxy_bak 275 echo HostKey $OBJ/cert_host_key_${ktype} 276 echo HostCertificate $OBJ/cert_host_key_${ktype}-cert.pub 277 ) > $OBJ/sshd_proxy 278 279 ${SSH} -oUserKnownHostsFile=$OBJ/known_hosts-cert \ 280 -oGlobalKnownHostsFile=none -F $OBJ/ssh_proxy somehost true 281 if [ $? -ne 0 ]; then 282 fail "ssh cert connect failed" 283 fi 284 # Also check that it works when the known_hosts file is not in the 285 # first array position. 286 ${SSH} -oUserKnownHostsFile="/dev/null $OBJ/known_hosts-cert" \ 287 -oGlobalKnownHostsFile=none -F $OBJ/ssh_proxy somehost true 288 if [ $? -ne 0 ]; then 289 fail "ssh cert connect failed known_hosts 2nd" 290 fi 291done 292 293# Wrong certificate 294kh_ca host_ca_key.pub host_ca_key2.pub > $OBJ/known_hosts-cert.orig 295cp $OBJ/known_hosts-cert.orig $OBJ/known_hosts-cert 296for kt in $PLAIN_TYPES ; do 297 verbose "$tid: host ${kt} connect wrong cert" 298 rm -f $OBJ/cert_host_key* 299 # Self-sign key 300 ${SSHKEYGEN} -q -N '' -t ${kt} -f $OBJ/cert_host_key_${kt} || \ 301 fail "ssh-keygen of cert_host_key_${kt} failed" 302 case $kt in 303 rsa-sha2-*) tflag="-t $kt" ;; 304 *) tflag="" ;; 305 esac 306 ${SSHKEYGEN} $tflag -h -q -s $OBJ/cert_host_key_${kt} \ 307 -I "regress host key for $USER" \ 308 -n $HOSTS $OBJ/cert_host_key_${kt} || 309 fatal "couldn't sign cert_host_key_${kt}" 310 ( 311 cat $OBJ/sshd_proxy_bak 312 echo HostKey $OBJ/cert_host_key_${kt} 313 echo HostCertificate $OBJ/cert_host_key_${kt}-cert.pub 314 ) > $OBJ/sshd_proxy 315 316 cp $OBJ/known_hosts-cert.orig $OBJ/known_hosts-cert 317 ${SSH} -oUserKnownHostsFile=$OBJ/known_hosts-cert \ 318 -oGlobalKnownHostsFile=$OBJ/known_hosts-cert \ 319 -F $OBJ/ssh_proxy -q somehost true >/dev/null 2>&1 320 if [ $? -eq 0 ]; then 321 fail "ssh cert connect $ident succeeded unexpectedly" 322 fi 323done 324 325rm -f $OBJ/known_hosts-cert* $OBJ/host_ca_key* $OBJ/cert_host_key* 326