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