1# $OpenBSD: cert-userkey.sh,v 1.26 2021/02/25 03:27:34 djm Exp $ 2# Placed in the Public Domain. 3 4tid="certified user keys" 5 6rm -f $OBJ/authorized_keys_$USER $OBJ/user_ca_key* $OBJ/cert_user_key* 7cp $OBJ/sshd_proxy $OBJ/sshd_proxy_bak 8cp $OBJ/ssh_proxy $OBJ/ssh_proxy_bak 9 10PLAIN_TYPES=`$SSH -Q key-plain | maybe_filter_sk | sed 's/^ssh-dss/ssh-dsa/;s/^ssh-//'` 11EXTRA_TYPES="" 12rsa="" 13 14if echo "$PLAIN_TYPES" | grep '^rsa$' >/dev/null 2>&1 ; then 15 rsa=rsa 16 PLAIN_TYPES="$PLAIN_TYPES rsa-sha2-256 rsa-sha2-512" 17fi 18 19kname() { 20 case $1 in 21 rsa-sha2-*) n="$1" ;; 22 sk-ecdsa-*) n="sk-ecdsa" ;; 23 sk-ssh-ed25519*) n="sk-ssh-ed25519" ;; 24 # subshell because some seds will add a newline 25 *) n=$(echo $1 | sed 's/^dsa/ssh-dss/;s/^rsa/ssh-rsa/;s/^ed/ssh-ed/') ;; 26 esac 27 if [ -z "$rsa" ]; then 28 echo "$n*,ssh-ed25519*" 29 else 30 echo "$n*,ssh-rsa*,ssh-ed25519*" 31 fi 32} 33 34# Create a CA key 35if [ ! -z "$rsa" ]; then 36 catype=rsa 37else 38 catype=ed25519 39fi 40${SSHKEYGEN} -q -N '' -t $catype -f $OBJ/user_ca_key ||\ 41 fail "ssh-keygen of user_ca_key failed" 42 43# Generate and sign user keys 44for ktype in $PLAIN_TYPES $EXTRA_TYPES ; do 45 verbose "$tid: sign user ${ktype} cert" 46 ${SSHKEYGEN} -q -N '' -t ${ktype} \ 47 -f $OBJ/cert_user_key_${ktype} || \ 48 fatal "ssh-keygen of cert_user_key_${ktype} failed" 49 # Generate RSA/SHA2 certs for rsa-sha2* keys. 50 case $ktype in 51 rsa-sha2-*) tflag="-t $ktype" ;; 52 *) tflag="" ;; 53 esac 54 ${SSHKEYGEN} -q -s $OBJ/user_ca_key -z $$ \ 55 -I "regress user key for $USER" \ 56 -n ${USER},mekmitasdigoat $tflag $OBJ/cert_user_key_${ktype} || \ 57 fatal "couldn't sign cert_user_key_${ktype}" 58done 59 60# Test explicitly-specified principals 61for ktype in $EXTRA_TYPES $PLAIN_TYPES ; do 62 t=$(kname $ktype) 63 for privsep in yes ; do 64 _prefix="${ktype} privsep $privsep" 65 66 # Setup for AuthorizedPrincipalsFile 67 rm -f $OBJ/authorized_keys_$USER 68 ( 69 cat $OBJ/sshd_proxy_bak 70 echo "UsePrivilegeSeparation $privsep" 71 echo "AuthorizedPrincipalsFile " \ 72 "$OBJ/authorized_principals_%u" 73 echo "TrustedUserCAKeys $OBJ/user_ca_key.pub" 74 echo "PubkeyAcceptedAlgorithms ${t}" 75 ) > $OBJ/sshd_proxy 76 ( 77 cat $OBJ/ssh_proxy_bak 78 echo "PubkeyAcceptedAlgorithms ${t}" 79 ) > $OBJ/ssh_proxy 80 81 # Missing authorized_principals 82 verbose "$tid: ${_prefix} missing authorized_principals" 83 rm -f $OBJ/authorized_principals_$USER 84 ${SSH} -i $OBJ/cert_user_key_${ktype} \ 85 -F $OBJ/ssh_proxy somehost true >/dev/null 2>&1 86 if [ $? -eq 0 ]; then 87 fail "ssh cert connect succeeded unexpectedly" 88 fi 89 90 # Empty authorized_principals 91 verbose "$tid: ${_prefix} empty authorized_principals" 92 echo > $OBJ/authorized_principals_$USER 93 ${SSH} -i $OBJ/cert_user_key_${ktype} \ 94 -F $OBJ/ssh_proxy somehost true >/dev/null 2>&1 95 if [ $? -eq 0 ]; then 96 fail "ssh cert connect succeeded unexpectedly" 97 fi 98 99 # Wrong authorized_principals 100 verbose "$tid: ${_prefix} wrong authorized_principals" 101 echo gregorsamsa > $OBJ/authorized_principals_$USER 102 ${SSH} -i $OBJ/cert_user_key_${ktype} \ 103 -F $OBJ/ssh_proxy somehost true >/dev/null 2>&1 104 if [ $? -eq 0 ]; then 105 fail "ssh cert connect succeeded unexpectedly" 106 fi 107 108 # Correct authorized_principals 109 verbose "$tid: ${_prefix} correct authorized_principals" 110 echo mekmitasdigoat > $OBJ/authorized_principals_$USER 111 ${SSH} -i $OBJ/cert_user_key_${ktype} \ 112 -F $OBJ/ssh_proxy somehost true >/dev/null 2>&1 113 if [ $? -ne 0 ]; then 114 fail "ssh cert connect failed" 115 fi 116 117 # authorized_principals with bad key option 118 verbose "$tid: ${_prefix} authorized_principals bad key opt" 119 echo 'blah mekmitasdigoat' > $OBJ/authorized_principals_$USER 120 ${SSH} -i $OBJ/cert_user_key_${ktype} \ 121 -F $OBJ/ssh_proxy somehost true >/dev/null 2>&1 122 if [ $? -eq 0 ]; then 123 fail "ssh cert connect succeeded unexpectedly" 124 fi 125 126 # authorized_principals with command=false 127 verbose "$tid: ${_prefix} authorized_principals command=false" 128 echo 'command="false" mekmitasdigoat' > \ 129 $OBJ/authorized_principals_$USER 130 ${SSH} -i $OBJ/cert_user_key_${ktype} \ 131 -F $OBJ/ssh_proxy somehost true >/dev/null 2>&1 132 if [ $? -eq 0 ]; then 133 fail "ssh cert connect succeeded unexpectedly" 134 fi 135 136 137 # authorized_principals with command=true 138 verbose "$tid: ${_prefix} authorized_principals command=true" 139 echo 'command="true" mekmitasdigoat' > \ 140 $OBJ/authorized_principals_$USER 141 ${SSH} -i $OBJ/cert_user_key_${ktype} \ 142 -F $OBJ/ssh_proxy somehost false >/dev/null 2>&1 143 if [ $? -ne 0 ]; then 144 fail "ssh cert connect failed" 145 fi 146 147 # Setup for principals= key option 148 rm -f $OBJ/authorized_principals_$USER 149 ( 150 cat $OBJ/sshd_proxy_bak 151 echo "UsePrivilegeSeparation $privsep" 152 echo "PubkeyAcceptedAlgorithms ${t}" 153 ) > $OBJ/sshd_proxy 154 ( 155 cat $OBJ/ssh_proxy_bak 156 echo "PubkeyAcceptedAlgorithms ${t}" 157 ) > $OBJ/ssh_proxy 158 159 # Wrong principals list 160 verbose "$tid: ${_prefix} wrong principals key option" 161 ( 162 printf 'cert-authority,principals="gregorsamsa" ' 163 cat $OBJ/user_ca_key.pub 164 ) > $OBJ/authorized_keys_$USER 165 ${SSH} -i $OBJ/cert_user_key_${ktype} \ 166 -F $OBJ/ssh_proxy somehost true >/dev/null 2>&1 167 if [ $? -eq 0 ]; then 168 fail "ssh cert connect succeeded unexpectedly" 169 fi 170 171 # Correct principals list 172 verbose "$tid: ${_prefix} correct principals key option" 173 ( 174 printf 'cert-authority,principals="mekmitasdigoat" ' 175 cat $OBJ/user_ca_key.pub 176 ) > $OBJ/authorized_keys_$USER 177 ${SSH} -i $OBJ/cert_user_key_${ktype} \ 178 -F $OBJ/ssh_proxy somehost true >/dev/null 2>&1 179 if [ $? -ne 0 ]; then 180 fail "ssh cert connect failed" 181 fi 182 done 183done 184 185basic_tests() { 186 auth=$1 187 if test "x$auth" = "xauthorized_keys" ; then 188 # Add CA to authorized_keys 189 ( 190 printf 'cert-authority ' 191 cat $OBJ/user_ca_key.pub 192 ) > $OBJ/authorized_keys_$USER 193 else 194 echo > $OBJ/authorized_keys_$USER 195 extra_sshd="TrustedUserCAKeys $OBJ/user_ca_key.pub" 196 fi 197 198 for ktype in $PLAIN_TYPES ; do 199 t=$(kname $ktype) 200 for privsep in yes ; do 201 _prefix="${ktype} privsep $privsep $auth" 202 # Simple connect 203 verbose "$tid: ${_prefix} connect" 204 ( 205 cat $OBJ/sshd_proxy_bak 206 echo "UsePrivilegeSeparation $privsep" 207 echo "PubkeyAcceptedAlgorithms ${t}" 208 echo "$extra_sshd" 209 ) > $OBJ/sshd_proxy 210 ( 211 cat $OBJ/ssh_proxy_bak 212 echo "PubkeyAcceptedAlgorithms ${t}" 213 ) > $OBJ/ssh_proxy 214 215 ${SSH} -i $OBJ/cert_user_key_${ktype} \ 216 -F $OBJ/ssh_proxy somehost true 217 if [ $? -ne 0 ]; then 218 fail "ssh cert connect failed" 219 fi 220 221 # Revoked keys 222 verbose "$tid: ${_prefix} revoked key" 223 ( 224 cat $OBJ/sshd_proxy_bak 225 echo "UsePrivilegeSeparation $privsep" 226 echo "RevokedKeys $OBJ/cert_user_key_revoked" 227 echo "PubkeyAcceptedAlgorithms ${t}" 228 echo "$extra_sshd" 229 ) > $OBJ/sshd_proxy 230 cp $OBJ/cert_user_key_${ktype}.pub \ 231 $OBJ/cert_user_key_revoked 232 ${SSH} -i $OBJ/cert_user_key_${ktype} \ 233 -F $OBJ/ssh_proxy somehost true >/dev/null 2>&1 234 if [ $? -eq 0 ]; then 235 fail "ssh cert connect succeeded unexpecedly" 236 fi 237 verbose "$tid: ${_prefix} revoked via KRL" 238 rm $OBJ/cert_user_key_revoked 239 ${SSHKEYGEN} -kqf $OBJ/cert_user_key_revoked \ 240 $OBJ/cert_user_key_${ktype}.pub 241 ${SSH} -i $OBJ/cert_user_key_${ktype} \ 242 -F $OBJ/ssh_proxy somehost true >/dev/null 2>&1 243 if [ $? -eq 0 ]; then 244 fail "ssh cert connect succeeded unexpecedly" 245 fi 246 verbose "$tid: ${_prefix} empty KRL" 247 ${SSHKEYGEN} -kqf $OBJ/cert_user_key_revoked 248 ${SSH} -i $OBJ/cert_user_key_${ktype} \ 249 -F $OBJ/ssh_proxy somehost true >/dev/null 2>&1 250 if [ $? -ne 0 ]; then 251 fail "ssh cert connect failed" 252 fi 253 done 254 255 # Revoked CA 256 verbose "$tid: ${ktype} $auth revoked CA key" 257 ( 258 cat $OBJ/sshd_proxy_bak 259 echo "RevokedKeys $OBJ/user_ca_key.pub" 260 echo "PubkeyAcceptedAlgorithms ${t}" 261 echo "$extra_sshd" 262 ) > $OBJ/sshd_proxy 263 ${SSH} -i $OBJ/cert_user_key_${ktype} -F $OBJ/ssh_proxy \ 264 somehost true >/dev/null 2>&1 265 if [ $? -eq 0 ]; then 266 fail "ssh cert connect succeeded unexpecedly" 267 fi 268 done 269 270 verbose "$tid: $auth CA does not authenticate" 271 ( 272 cat $OBJ/sshd_proxy_bak 273 echo "PubkeyAcceptedAlgorithms ${t}" 274 echo "$extra_sshd" 275 ) > $OBJ/sshd_proxy 276 verbose "$tid: ensure CA key does not authenticate user" 277 ${SSH} -i $OBJ/user_ca_key \ 278 -F $OBJ/ssh_proxy somehost true >/dev/null 2>&1 279 if [ $? -eq 0 ]; then 280 fail "ssh cert connect with CA key succeeded unexpectedly" 281 fi 282} 283 284basic_tests authorized_keys 285basic_tests TrustedUserCAKeys 286 287test_one() { 288 ident=$1 289 result=$2 290 sign_opts=$3 291 auth_choice=$4 292 auth_opt=$5 293 294 if test "x$auth_choice" = "x" ; then 295 auth_choice="authorized_keys TrustedUserCAKeys" 296 fi 297 298 for auth in $auth_choice ; do 299 for ktype in $rsa ed25519 ; do 300 cat $OBJ/sshd_proxy_bak > $OBJ/sshd_proxy 301 if test "x$auth" = "xauthorized_keys" ; then 302 # Add CA to authorized_keys 303 ( 304 printf "cert-authority${auth_opt} " 305 cat $OBJ/user_ca_key.pub 306 ) > $OBJ/authorized_keys_$USER 307 else 308 echo > $OBJ/authorized_keys_$USER 309 echo "TrustedUserCAKeys $OBJ/user_ca_key.pub" \ 310 >> $OBJ/sshd_proxy 311 echo "PubkeyAcceptedAlgorithms ${t}*" \ 312 >> $OBJ/sshd_proxy 313 if test "x$auth_opt" != "x" ; then 314 echo $auth_opt >> $OBJ/sshd_proxy 315 fi 316 fi 317 318 verbose "$tid: $ident auth $auth expect $result $ktype" 319 ${SSHKEYGEN} -q -s $OBJ/user_ca_key \ 320 -I "regress user key for $USER" \ 321 $sign_opts $OBJ/cert_user_key_${ktype} || 322 fail "couldn't sign cert_user_key_${ktype}" 323 324 ${SSH} -i $OBJ/cert_user_key_${ktype} \ 325 -F $OBJ/ssh_proxy somehost true >/dev/null 2>&1 326 rc=$? 327 if [ "x$result" = "xsuccess" ] ; then 328 if [ $rc -ne 0 ]; then 329 fail "$ident failed unexpectedly" 330 fi 331 else 332 if [ $rc -eq 0 ]; then 333 fail "$ident succeeded unexpectedly" 334 fi 335 fi 336 done 337 done 338} 339 340test_one "correct principal" success "-n ${USER}" 341test_one "host-certificate" failure "-n ${USER} -h" 342test_one "wrong principals" failure "-n foo" 343test_one "cert not yet valid" failure "-n ${USER} -V20300101:20320101" 344test_one "cert expired" failure "-n ${USER} -V19800101:19900101" 345test_one "cert valid interval" success "-n ${USER} -V-1w:+2w" 346test_one "wrong source-address" failure "-n ${USER} -Osource-address=10.0.0.0/8" 347test_one "force-command" failure "-n ${USER} -Oforce-command=false" 348 349# Behaviour is different here: TrustedUserCAKeys doesn't allow empty principals 350test_one "empty principals" success "" authorized_keys 351test_one "empty principals" failure "" TrustedUserCAKeys 352 353# Check explicitly-specified principals: an empty principals list in the cert 354# should always be refused. 355 356# AuthorizedPrincipalsFile 357rm -f $OBJ/authorized_keys_$USER 358echo mekmitasdigoat > $OBJ/authorized_principals_$USER 359test_one "AuthorizedPrincipalsFile principals" success "-n mekmitasdigoat" \ 360 TrustedUserCAKeys "AuthorizedPrincipalsFile $OBJ/authorized_principals_%u" 361test_one "AuthorizedPrincipalsFile no principals" failure "" \ 362 TrustedUserCAKeys "AuthorizedPrincipalsFile $OBJ/authorized_principals_%u" 363 364# principals= key option 365rm -f $OBJ/authorized_principals_$USER 366test_one "principals key option principals" success "-n mekmitasdigoat" \ 367 authorized_keys ',principals="mekmitasdigoat"' 368test_one "principals key option no principals" failure "" \ 369 authorized_keys ',principals="mekmitasdigoat"' 370 371# command= options vs. force-command in key 372test_one "force-command match true" success \ 373 "-n ${USER} -Oforce-command=true" \ 374 authorized_keys ',command="true"' 375test_one "force-command match true" failure \ 376 "-n ${USER} -Oforce-command=false" \ 377 authorized_keys ',command="false"' 378test_one "force-command mismatch 1" failure \ 379 "-n ${USER} -Oforce-command=false" \ 380 authorized_keys ',command="true"' 381test_one "force-command mismatch 2" failure \ 382 "-n ${USER} -Oforce-command=true" \ 383 authorized_keys ',command="false"' 384 385# Wrong certificate 386cat $OBJ/sshd_proxy_bak > $OBJ/sshd_proxy 387for ktype in $PLAIN_TYPES ; do 388 t=$(kname $ktype) 389 # Self-sign 390 ${SSHKEYGEN} -q -s $OBJ/cert_user_key_${ktype} -I \ 391 "regress user key for $USER" \ 392 -n $USER $OBJ/cert_user_key_${ktype} || 393 fatal "couldn't sign cert_user_key_${ktype}" 394 verbose "$tid: user ${ktype} connect wrong cert" 395 ${SSH} -i $OBJ/cert_user_key_${ktype} -F $OBJ/ssh_proxy \ 396 somehost true >/dev/null 2>&1 397 if [ $? -eq 0 ]; then 398 fail "ssh cert connect $ident succeeded unexpectedly" 399 fi 400done 401 402rm -f $OBJ/authorized_keys_$USER $OBJ/user_ca_key* $OBJ/cert_user_key* 403rm -f $OBJ/authorized_principals_$USER 404 405