1# 2# Copyright (c) 2025 Dag-Erling Smørgrav <des@FreeBSD.org> 3# 4# SPDX-License-Identifier: BSD-2-Clause 5# 6 7. $(atf_get_srcdir)/certctl.subr 8 9# Random sets of eight non-colliding names 10set1() 11{ 12 cat <<EOF 13AVOYKJHSLFHWPVQMKBHENUAHJTEGMCCB 0ca83bbe 14UYSYXKDNNJTYOQPBGIKQDHRJYZHTDPKK 0d9a6512 15LODHGFXMZYKGOKAYGWTMMYQJYHDATDDM 4e6219f5 16NBBTQHJLHKBFFFWJTHHSNKOQYMGLHLPW 5dd76abc 17BJFAQZXZHYQLIDDPCAQFPDMNXICUXBXW ad68573d 18IOKNTHVEVVIJMNMYAVILMEMQQWLVRESN b577803d 19BHGMAJJGNJPIVMHMFCUTJLGFROJICEKN c98a6338 20HCRFQMGDQJALMLUQNXMPGLXFLLJRODJW f50c6379 21EOF 22} 23 24set2() 25{ 26 cat <<EOF 27GOHKZTSKIPDSYNLMGYXGLROPTATELXIU 30789c88 28YOOTYHEGHZIYFXOBLNKENPSJUDGOPJJU 7fadbc13 29ETRINNYBGKIENAVGOKVJYFSSHFZIJZRH 8ed664af 30DBFGMFFMRNLPQLQPOLXOEUVLCRXLRSWT 8f34355e 31WFOPBQPLQFHDHZOUQFEIDGSYDUOTSNDQ ac0471df 32HMNETZMGNIWRGXQCVZXVZGWSGFBRRDQC b32f1472 33SHFYBXDVAUACBFPPAIGDAQIAGYOYGMQE baca75fa 34PCBGDNVPYCDGNRQSGRSLXFHYKXLAVLHW ddeeae01 35EOF 36} 37 38set3() 39{ 40 cat <<EOF 41NJWIRLPWAIICVJBKXXHFHLCPAERZATRL 000aa2e5 42RJAENDPOCZQEVCPFUWOWDXPCSMYJPVYC 021b95a3 43PQUQDSWHBNVLBTNBGONYRLGZZVEFXVLO 071e8c50 44VZEXRKJUPZSFBDWBOLUZXOGLNTEAPCZM 3af7bb9b 45ZXOWOXQTXNZMAMZIWVFDZDJEWOOAGAOH 48d5c7cc 46KQSFQYVJMFTMADIHJIWGSQISWKSHRYQO 509f5ba1 47AIECYSLWZOIEPJWWUTWSQXCNCIHHZHYI 8cb0c503 48RFHWDJZEPOFLMPGXAHVEJFHCDODAPVEV 9ae4e049 49EOF 50} 51 52# Random set of three colliding names 53collhash=f2888ce3 54coll() 55{ 56 cat <<EOF 57EJFTZEOANQLOYPEHWWXBWEWEFVKHMSNA $collhash 58LEMRWZAZLKZLPPSFLNLQZVGKKBEOFYWG $collhash 59ZWUPHYWKKTVEFBJOLLPDAIKGRDFVXZID $collhash 60EOF 61} 62 63sortfile() { 64 for filename; do 65 sort "${filename}" >"${filename}"- 66 mv "${filename}"- "${filename}" 67 done 68} 69 70certctl_setup() 71{ 72 export DESTDIR="$PWD" 73 74 # Create input directories 75 mkdir -p ${DESTDIR}${DISTBASE}/usr/share/certs/trusted 76 mkdir -p ${DESTDIR}${DISTBASE}/usr/share/certs/untrusted 77 mkdir -p ${DESTDIR}/usr/local/share/certs 78 79 # Do not create output directories; certctl will take care of it 80 #mkdir -p ${DESTDIR}${DISTBASE}/etc/ssl/certs 81 #mkdir -p ${DESTDIR}${DISTBASE}/etc/ssl/untrusted 82 83 # Generate a random key 84 keyname="testkey" 85 gen_key ${keyname} 86 87 # Generate certificates 88 :>metalog.expect 89 :>trusted.expect 90 :>untrusted.expect 91 metalog() { 92 echo ".${DISTBASE}$@ type=file" >>metalog.expect 93 } 94 trusted() { 95 local crtname=$1 96 local filename=$2 97 printf "%s\t%s\n" "${filename}" "${crtname}" >>trusted.expect 98 metalog "/etc/ssl/certs/${filename}" 99 } 100 untrusted() { 101 local crtname=$1 102 local filename=$2 103 printf "%s\t%s\n" "${filename}" "${crtname}" >>untrusted.expect 104 metalog "/etc/ssl/untrusted/${filename}" 105 } 106 set1 | while read crtname hash ; do 107 gen_crt ${crtname} ${keyname} 108 mv ${crtname}.crt ${DESTDIR}${DISTBASE}/usr/share/certs/trusted 109 trusted "${crtname}" "${hash}.0" 110 done 111 local c=0 112 coll | while read crtname hash ; do 113 gen_crt ${crtname} ${keyname} 114 mv ${crtname}.crt ${DESTDIR}${DISTBASE}/usr/share/certs/trusted 115 trusted "${crtname}" "${hash}.${c}" 116 c=$((c+1)) 117 done 118 set2 | while read crtname hash ; do 119 gen_crt ${crtname} ${keyname} 120 openssl x509 -in ${crtname}.crt 121 rm ${crtname}.crt 122 trusted "${crtname}" "${hash}.0" 123 done >usr/local/share/certs/bundle.crt 124 set3 | while read crtname hash ; do 125 gen_crt ${crtname} ${keyname} 126 mv ${crtname}.crt ${DESTDIR}${DISTBASE}/usr/share/certs/untrusted 127 untrusted "${crtname}" "${hash}.0" 128 done 129 metalog "/etc/ssl/cert.pem" 130 unset -f untrusted 131 unset -f trusted 132 unset -f metalog 133 sortfile *.expect 134} 135 136check_trusted() { 137 local crtname=$1 138 local subject="$(subject ${crtname})" 139 local c=${2:-1} 140 141 atf_check -e ignore -o match:"found: ${c}\$" \ 142 openssl storeutl -noout -subject "${subject}" \ 143 ${DESTDIR}${DISTBASE}/etc/ssl/certs 144 atf_check -e ignore -o not-match:"found: [1-9]" \ 145 openssl storeutl -noout -subject "${subject}" \ 146 ${DESTDIR}${DISTBASE}/etc/ssl/untrusted 147} 148 149check_untrusted() { 150 local crtname=$1 151 local subject="$(subject ${crtname})" 152 local c=${2:-1} 153 154 atf_check -e ignore -o not-match:"found: [1-9]" \ 155 openssl storeutl -noout -subject "${subject}" \ 156 ${DESTDIR}/${DISTBASE}/etc/ssl/certs 157 atf_check -e ignore -o match:"found: ${c}\$" \ 158 openssl storeutl -noout -subject "${subject}" \ 159 ${DESTDIR}/${DISTBASE}/etc/ssl/untrusted 160} 161 162check_in_bundle() { 163 local b=${DISTBASE}${DISTBASE+/} 164 local crtfile=$1 165 local line 166 167 line=$(tail +5 "${crtfile}" | head -1) 168 atf_check grep -q "${line}" ${DESTDIR}${DISTBASE}/etc/ssl/cert.pem 169} 170 171check_not_in_bundle() { 172 local b=${DISTBASE}${DISTBASE+/} 173 local crtfile=$1 174 local line 175 176 line=$(tail +5 "${crtfile}" | head -1) 177 atf_check -s exit:1 grep -q "${line}" etc/ssl/cert.pem 178} 179 180atf_test_case rehash 181rehash_head() 182{ 183 atf_set "descr" "Test the rehash command" 184} 185rehash_body() 186{ 187 certctl_setup 188 atf_check certctl rehash 189 190 # Verify non-colliding trusted certificates 191 (set1; set2) >trusted 192 while read crtname hash ; do 193 check_trusted "${crtname}" 194 done <trusted 195 196 # Verify colliding trusted certificates 197 coll >coll 198 while read crtname hash ; do 199 check_trusted "${crtname}" $(wc -l <coll) 200 done <coll 201 202 # Verify untrusted certificates 203 set3 >untrusted 204 while read crtname hash ; do 205 check_untrusted "${crtname}" 206 done <untrusted 207 208 # Verify bundle 209 for f in etc/ssl/certs/*.? ; do 210 check_in_bundle "${f}" 211 done 212 for f in etc/ssl/untrusted/*.? ; do 213 check_not_in_bundle "${f}" 214 done 215} 216 217atf_test_case list 218list_head() 219{ 220 atf_set "descr" "Test the list and untrusted commands" 221} 222list_body() 223{ 224 certctl_setup 225 atf_check certctl rehash 226 227 atf_check -o save:trusted.out certctl list 228 sortfile trusted.out 229 # the ordering of the colliding certificates is partly 230 # determined by fields that change every time we regenerate 231 # them, so ignore them in the diff 232 atf_check diff -u \ 233 --ignore-matching-lines $collhash \ 234 trusted.expect trusted.out 235 236 atf_check -o save:untrusted.out certctl untrusted 237 sortfile untrusted.out 238 atf_check diff -u \ 239 untrusted.expect untrusted.out 240} 241 242atf_test_case trust 243trust_head() 244{ 245 atf_set "descr" "Test the trust command" 246} 247trust_body() 248{ 249 certctl_setup 250 atf_check certctl rehash 251 crtname=$(set3 | (read crtname hash ; echo ${crtname})) 252 crtfile=usr/share/certs/untrusted/${crtname}.crt 253 check_untrusted ${crtname} 254 check_not_in_bundle ${crtfile} 255 atf_check -e match:"was previously untrusted" \ 256 certctl trust ${crtfile} 257 check_trusted ${crtname} 258 check_in_bundle ${crtfile} 259} 260 261atf_test_case untrust 262untrust_head() 263{ 264 atf_set "descr" "Test the untrust command" 265} 266untrust_body() 267{ 268 certctl_setup 269 atf_check certctl rehash 270 crtname=$(set1 | (read crtname hash ; echo ${crtname})) 271 crtfile=usr/share/certs/trusted/${crtname}.crt 272 check_trusted "${crtname}" 273 check_in_bundle ${crtfile} 274 atf_check certctl untrust "${crtfile}" 275 check_untrusted "${crtname}" 276 check_not_in_bundle ${crtfile} 277} 278 279atf_test_case metalog 280metalog_head() 281{ 282 atf_set "descr" "Verify the metalog" 283} 284metalog_body() 285{ 286 export DISTBASE=/base 287 certctl_setup 288 289 # certctl gets DESTDIR and DISTBASE from environment 290 rm -f metalog.orig 291 atf_check certctl -U -M metalog.orig rehash 292 sed -E 's/(type=file) .*/\1/' metalog.orig | sort >metalog.short 293 atf_check diff -u metalog.expect metalog.short 294 295 # certctl gets DESTDIR and DISTBASE from command line 296 rm -f metalog.orig 297 atf_check env -uDESTDIR -uDISTBASE \ 298 certctl -D ${DESTDIR} -d ${DISTBASE} -U -M metalog.orig rehash 299 sed -E 's/(type=file) .*/\1/' metalog.orig | sort >metalog.short 300 atf_check diff -u metalog.expect metalog.short 301 302 # as above, but intentionally add trailing slashes 303 rm -f metalog.orig 304 atf_check env -uDESTDIR -uDISTBASE \ 305 certctl -D ${DESTDIR}// -d ${DISTBASE}/ -U -M metalog.orig rehash 306 sed -E 's/(type=file) .*/\1/' metalog.orig | sort >metalog.short 307 atf_check diff -u metalog.expect metalog.short 308} 309 310atf_test_case misc 311misc_head() 312{ 313 atf_set "descr" "Test miscellaneous edge cases" 314} 315misc_body() 316{ 317 # certctl rejects DISTBASE that does not begin with a slash 318 atf_check -s exit:1 -e match:"begin with a slash" \ 319 certctl -d base -n rehash 320 atf_check -s exit:1 -e match:"begin with a slash" \ 321 env DISTBASE=base certctl -n rehash 322} 323 324atf_init_test_cases() 325{ 326 atf_add_test_case rehash 327 atf_add_test_case list 328 atf_add_test_case trust 329 atf_add_test_case untrust 330 atf_add_test_case metalog 331 atf_add_test_case misc 332} 333