1#!/bin/sh 2 3# Copyright (C) Internet Systems Consortium, Inc. ("ISC") 4# 5# SPDX-License-Identifier: MPL-2.0 6# 7# This Source Code Form is subject to the terms of the Mozilla Public 8# License, v. 2.0. If a copy of the MPL was not distributed with this 9# file, you can obtain one at https://mozilla.org/MPL/2.0/. 10# 11# See the COPYRIGHT file distributed with this work for additional 12# information regarding copyright ownership. 13 14# 15# Common configuration data for kasp system tests, to be sourced into 16# other shell scripts. 17# 18 19# shellcheck source=conf.sh 20. ../conf.sh 21 22############################################################################### 23# Constants # 24############################################################################### 25DEFAULT_TTL=300 26 27############################################################################### 28# Query properties # 29############################################################################### 30TSIG="" 31SHA1="FrSt77yPTFx6hTs4i2tKLB9LmE0=" 32SHA224="hXfwwwiag2QGqblopofai9NuW28q/1rH4CaTnA==" 33SHA256="R16NojROxtxH/xbDl//ehDsHm5DjWTQ2YXV+hGC2iBY=" 34VIEW1="YPfMoAk6h+3iN8MDRQC004iSNHY=" 35VIEW2="4xILSZQnuO1UKubXHkYUsvBRPu8=" 36VIEW3="C1Azf+gGPMmxrUg/WQINP6eV9Y0=" 37 38############################################################################### 39# Key properties # 40############################################################################### 41# ID 42# BASEFILE 43# EXPECT 44# ROLE 45# KSK 46# ZSK 47# FLAGS 48# LIFETIME 49# ALG_NUM 50# ALG_STR 51# ALG_LEN 52# CREATED 53# PUBLISHED 54# ACTIVE 55# RETIRED 56# REVOKED 57# REMOVED 58# GOAL 59# STATE_DNSKEY 60# STATE_ZRRSIG 61# STATE_KRRSIG 62# STATE_DS 63# EXPECT_ZRRSIG 64# EXPECT_KRRSIG 65# LEGACY 66# PRIVATE 67# PRIVKEY_STAT 68# PUBKEY_STAT 69# STATE_STAT 70 71key_key() { 72 echo "${1}__${2}" 73} 74 75key_get() { 76 eval "echo \${$(key_key "$1" "$2")}" 77} 78 79key_set() { 80 eval "$(key_key "$1" "$2")='$3'" 81} 82 83key_stat() { 84 $PERL -e 'print((stat @ARGV[0])[9] . "\n");' "$1" 85} 86 87# Save certain values in the KEY array. 88key_save() 89{ 90 # Save key id. 91 key_set "$1" ID "$KEY_ID" 92 # Save base filename. 93 key_set "$1" BASEFILE "$BASE_FILE" 94 # Save creation date. 95 key_set "$1" CREATED "${KEY_CREATED}" 96 # Save key change time. 97 key_set "$1" PRIVKEY_STAT $(key_stat "${BASE_FILE}.private") 98 key_set "$1" PUBKEY_STAT $(key_stat "${BASE_FILE}.key") 99 key_set "$1" STATE_STAT $(key_stat "${BASE_FILE}.state") 100} 101 102# Clear key state. 103# 104# This will update either the KEY1, KEY2, or KEY3 array. 105key_clear() { 106 key_set "$1" "ID" 'no' 107 key_set "$1" "IDPAD" 'no' 108 key_set "$1" "EXPECT" 'no' 109 key_set "$1" "ROLE" 'none' 110 key_set "$1" "KSK" 'no' 111 key_set "$1" "ZSK" 'no' 112 key_set "$1" "FLAGS" '0' 113 key_set "$1" "LIFETIME" 'none' 114 key_set "$1" "ALG_NUM" '0' 115 key_set "$1" "ALG_STR" 'none' 116 key_set "$1" "ALG_LEN" '0' 117 key_set "$1" "CREATED" '0' 118 key_set "$1" "PUBLISHED" 'none' 119 key_set "$1" "SYNCPUBLISH" 'none' 120 key_set "$1" "ACTIVE" 'none' 121 key_set "$1" "RETIRED" 'none' 122 key_set "$1" "REVOKED" 'none' 123 key_set "$1" "REMOVED" 'none' 124 key_set "$1" "GOAL" 'none' 125 key_set "$1" "STATE_DNSKEY" 'none' 126 key_set "$1" "STATE_KRRSIG" 'none' 127 key_set "$1" "STATE_ZRRSIG" 'none' 128 key_set "$1" "STATE_DS" 'none' 129 key_set "$1" "EXPECT_ZRRSIG" 'no' 130 key_set "$1" "EXPECT_KRRSIG" 'no' 131 key_set "$1" "LEGACY" 'no' 132 key_set "$1" "PRIVATE" 'yes' 133 key_set "$1" "PRIVKEY_STAT" '0' 134 key_set "$1" "PUBKEY_STAT" '0' 135 key_set "$1" "STATE_STAT" '0' 136} 137 138# Start clear. 139# There can be at most 4 keys at the same time during a rollover: 140# 2x KSK, 2x ZSK 141key_clear "KEY1" 142key_clear "KEY2" 143key_clear "KEY3" 144key_clear "KEY4" 145 146############################################################################### 147# Utilities # 148############################################################################### 149 150# Call dig with default options. 151_dig_with_opts() { 152 153 if [ -n "$TSIG" ]; then 154 "$DIG" +tcp +noadd +nosea +nostat +nocmd +dnssec -p "$PORT" -y "$TSIG" "$@" 155 else 156 "$DIG" +tcp +noadd +nosea +nostat +nocmd +dnssec -p "$PORT" "$@" 157 fi 158} 159 160# RNDC. 161_rndccmd() { 162 "$RNDC" -c ../common/rndc.conf -p "$CONTROLPORT" -s "$@" 163} 164 165# Print IDs of keys used for generating RRSIG records for RRsets of type $1 166# found in dig output file $2. 167get_keys_which_signed() { 168 _qtype=$1 169 _output=$2 170 # The key ID is the 11th column of the RRSIG record line. 171 awk -v qt="$_qtype" '$4 == "RRSIG" && $5 == qt {print $11}' < "$_output" 172} 173 174# Get the key ids from key files for zone $2 in directory $1. 175get_keyids() { 176 _dir=$1 177 _zone=$2 178 _regex="K${_zone}.+*+*.key" 179 180 find "${_dir}" -mindepth 1 -maxdepth 1 -name "${_regex}" | sed "s,$_dir/K${_zone}.+\([0-9]\{3\}\)+\([0-9]\{5\}\).key,\2," 181} 182 183# By default log errors and don't quit immediately. 184_log=1 185_log_error() { 186 test $_log -eq 1 && echo_i "error: $1" 187 ret=$((ret+1)) 188} 189disable_logerror() { 190 _log=0 191} 192enable_logerror() { 193 _log=1 194} 195 196# Set server key-directory ($1) and address ($2) for testing keys. 197set_server() { 198 DIR=$1 199 SERVER=$2 200} 201# Set zone name for testing keys. 202set_zone() { 203 ZONE=$1 204 DYNAMIC="no" 205} 206# By default zones are considered static. 207# When testing dynamic zones, call 'set_dynamic' after 'set_zone'. 208set_dynamic() { 209 DYNAMIC="yes" 210} 211 212# Set policy settings (name $1, number of keys $2, dnskey ttl $3) for testing keys. 213set_policy() { 214 POLICY=$1 215 NUM_KEYS=$2 216 DNSKEY_TTL=$3 217 CDS_DELETE="no" 218} 219# By default policies are considered to be secure. 220# If a zone sets its policy to "insecure", call 'set_cdsdelete' to tell the 221# system test to expect a CDS and CDNSKEY Delete record. 222set_cdsdelete() { 223 CDS_DELETE="yes" 224} 225 226# Set key properties for testing keys. 227# $1: Key to update (KEY1, KEY2, ...) 228# $2: Value 229set_keyrole() { 230 key_set "$1" "EXPECT" "yes" 231 key_set "$1" "ROLE" "$2" 232 key_set "$1" "KSK" "no" 233 key_set "$1" "ZSK" "no" 234 key_set "$1" "FLAGS" "0" 235 236 test "$2" = "ksk" && key_set "$1" "KSK" "yes" 237 test "$2" = "ksk" && key_set "$1" "FLAGS" "257" 238 239 test "$2" = "zsk" && key_set "$1" "ZSK" "yes" 240 test "$2" = "zsk" && key_set "$1" "FLAGS" "256" 241 242 test "$2" = "csk" && key_set "$1" "KSK" "yes" 243 test "$2" = "csk" && key_set "$1" "ZSK" "yes" 244 test "$2" = "csk" && key_set "$1" "FLAGS" "257" 245} 246set_keylifetime() { 247 key_set "$1" "EXPECT" "yes" 248 key_set "$1" "LIFETIME" "$2" 249} 250# The algorithm value consists of three parts: 251# $2: Algorithm (number) 252# $3: Algorithm (string-format) 253# $4: Algorithm length 254set_keyalgorithm() { 255 key_set "$1" "EXPECT" "yes" 256 key_set "$1" "ALG_NUM" "$2" 257 key_set "$1" "ALG_STR" "$3" 258 key_set "$1" "ALG_LEN" "$4" 259} 260set_keysigning() { 261 key_set "$1" "EXPECT" "yes" 262 key_set "$1" "EXPECT_KRRSIG" "$2" 263} 264set_zonesigning() { 265 key_set "$1" "EXPECT" "yes" 266 key_set "$1" "EXPECT_ZRRSIG" "$2" 267} 268 269# Set key timing metadata. Set to "none" to unset. 270# $1: Key to update (KEY1, KEY2, ...) 271# $2: Time to update (PUBLISHED, SYNCPUBLISH, ACTIVE, RETIRED, REVOKED, or REMOVED). 272# $3: Value 273set_keytime() { 274 key_set "$1" "EXPECT" "yes" 275 key_set "$1" "$2" "$3" 276} 277 278# Set key timing metadata to a value plus additional time. 279# $1: Key to update (KEY1, KEY2, ...) 280# $2: Time to update (PUBLISHED, SYNCPUBLISH, ACTIVE, RETIRED, REVOKED, or REMOVED). 281# $3: Value 282# $4: Additional time. 283set_addkeytime() { 284 if [ -x "$PYTHON" ]; then 285 # Convert "%Y%m%d%H%M%S" format to epoch seconds. 286 # Then, add the additional time (can be negative). 287 _value=$3 288 _plus=$4 289 $PYTHON > python.out.$ZONE.$1.$2 <<EOF 290from datetime import datetime 291from datetime import timedelta 292_now = datetime.strptime("$_value", "%Y%m%d%H%M%S") 293_delta = timedelta(seconds=$_plus) 294_then = _now + _delta 295print(_then.strftime("%Y%m%d%H%M%S")); 296EOF 297 # Set the expected timing metadata. 298 key_set "$1" "$2" $(cat python.out.$ZONE.$1.$2) 299 fi 300} 301 302# Set key state metadata. Set to "none" to unset. 303# $1: Key to update (KEY1, KEY2, ...) 304# $2: Key state to update (GOAL, STATE_DNSKEY, STATE_ZRRSIG, STATE_KRRSIG, or STATE_DS) 305# $3: Value 306set_keystate() { 307 key_set "$1" "EXPECT" "yes" 308 key_set "$1" "$2" "$3" 309} 310 311# Check the key $1 with id $2. 312# This requires environment variables to be set. 313# 314# This will set the following environment variables for testing: 315# BASE_FILE="${_dir}/K${_zone}.+${_alg_numpad}+${_key_idpad}" 316# KEY_FILE="${BASE_FILE}.key" 317# PRIVATE_FILE="${BASE_FILE}.private" 318# STATE_FILE="${BASE_FILE}.state" 319# KEY_ID=$(echo $1 | sed 's/^0\{0,4\}//') 320# KEY_CREATED (from the KEY_FILE) 321check_key() { 322 _dir="$DIR" 323 _zone="$ZONE" 324 _role=$(key_get "$1" ROLE) 325 _key_idpad="$2" 326 _key_id=$(echo "$_key_idpad" | sed 's/^0\{0,4\}//') 327 _alg_num=$(key_get "$1" ALG_NUM) 328 _alg_numpad=$(printf "%03d" "$_alg_num") 329 _alg_string=$(key_get "$1" ALG_STR) 330 _length=$(key_get "$1" "ALG_LEN") 331 _dnskey_ttl="$DNSKEY_TTL" 332 _lifetime=$(key_get "$1" LIFETIME) 333 _legacy=$(key_get "$1" LEGACY) 334 _private=$(key_get "$1" PRIVATE) 335 _flags=$(key_get "$1" FLAGS) 336 337 _published=$(key_get "$1" PUBLISHED) 338 _active=$(key_get "$1" ACTIVE) 339 _retired=$(key_get "$1" RETIRED) 340 _revoked=$(key_get "$1" REVOKED) 341 _removed=$(key_get "$1" REMOVED) 342 343 _goal=$(key_get "$1" GOAL) 344 _state_dnskey=$(key_get "$1" STATE_DNSKEY) 345 _state_zrrsig=$(key_get "$1" STATE_ZRRSIG) 346 _state_krrsig=$(key_get "$1" STATE_KRRSIG) 347 _state_ds=$(key_get "$1" STATE_DS) 348 349 _ksk="no" 350 _zsk="no" 351 if [ "$_role" = "ksk" ]; then 352 _ksk="yes" 353 elif [ "$_role" = "zsk" ]; then 354 _zsk="yes" 355 elif [ "$_role" = "csk" ]; then 356 _zsk="yes" 357 _ksk="yes" 358 fi 359 360 _role2="none" 361 if [ "$_flags" = "257" ]; then 362 _role2="key-signing" 363 elif [ "$_flags" = "256" ]; then 364 _role2="zone-signing" 365 fi 366 367 BASE_FILE="${_dir}/K${_zone}.+${_alg_numpad}+${_key_idpad}" 368 KEY_FILE="${BASE_FILE}.key" 369 PRIVATE_FILE="${BASE_FILE}.private" 370 STATE_FILE="${BASE_FILE}.state" 371 KEY_ID="${_key_id}" 372 373 # Check file existence. 374 [ -s "$KEY_FILE" ] || ret=1 375 if [ "$_private" = "yes" ]; then 376 [ -s "$PRIVATE_FILE" ] || ret=1 377 fi 378 if [ "$_legacy" = "no" ]; then 379 [ -s "$STATE_FILE" ] || ret=1 380 fi 381 [ "$ret" -eq 0 ] || _log_error "${BASE_FILE} files missing" 382 [ "$ret" -eq 0 ] || return 383 384 # Retrieve creation date. 385 grep "; Created:" "$KEY_FILE" > "${ZONE}.${KEY_ID}.${_alg_num}.created" || _log_error "mismatch created comment in $KEY_FILE" 386 KEY_CREATED=$(awk '{print $3}' < "${ZONE}.${KEY_ID}.${_alg_num}.created") 387 388 if [ "$_private" = "yes" ]; then 389 grep "Created: ${KEY_CREATED}" "$PRIVATE_FILE" > /dev/null || _log_error "mismatch created in $PRIVATE_FILE" 390 fi 391 if [ "$_legacy" = "no" ]; then 392 grep "Generated: ${KEY_CREATED}" "$STATE_FILE" > /dev/null || _log_error "mismatch generated in $STATE_FILE" 393 fi 394 395 test $_log -eq 1 && echo_i "check key file $BASE_FILE" 396 397 # Check the public key file. 398 grep "This is a ${_role2} key, keyid ${_key_id}, for ${_zone}." "$KEY_FILE" > /dev/null || _log_error "mismatch top comment in $KEY_FILE" 399 grep "${_zone}\. ${_dnskey_ttl} IN DNSKEY ${_flags} 3 ${_alg_num}" "$KEY_FILE" > /dev/null || _log_error "mismatch DNSKEY record in $KEY_FILE" 400 # Now check the private key file. 401 if [ "$_private" = "yes" ]; then 402 grep "Private-key-format: v1.3" "$PRIVATE_FILE" > /dev/null || _log_error "mismatch private key format in $PRIVATE_FILE" 403 grep "Algorithm: ${_alg_num} (${_alg_string})" "$PRIVATE_FILE" > /dev/null || _log_error "mismatch algorithm in $PRIVATE_FILE" 404 fi 405 # Now check the key state file. 406 if [ "$_legacy" = "no" ]; then 407 grep "This is the state of key ${_key_id}, for ${_zone}." "$STATE_FILE" > /dev/null || _log_error "mismatch top comment in $STATE_FILE" 408 if [ "$_lifetime" = "none" ]; then 409 grep "Lifetime: " "$STATE_FILE" > /dev/null && _log_error "unexpected lifetime in $STATE_FILE" 410 else 411 grep "Lifetime: ${_lifetime}" "$STATE_FILE" > /dev/null || _log_error "mismatch lifetime in $STATE_FILE" 412 fi 413 grep "Algorithm: ${_alg_num}" "$STATE_FILE" > /dev/null || _log_error "mismatch algorithm in $STATE_FILE" 414 grep "Length: ${_length}" "$STATE_FILE" > /dev/null || _log_error "mismatch length in $STATE_FILE" 415 grep "KSK: ${_ksk}" "$STATE_FILE" > /dev/null || _log_error "mismatch ksk in $STATE_FILE" 416 grep "ZSK: ${_zsk}" "$STATE_FILE" > /dev/null || _log_error "mismatch zsk in $STATE_FILE" 417 418 # Check key states. 419 if [ "$_goal" = "none" ]; then 420 grep "GoalState: " "$STATE_FILE" > /dev/null && _log_error "unexpected goal state in $STATE_FILE" 421 else 422 grep "GoalState: ${_goal}" "$STATE_FILE" > /dev/null || _log_error "mismatch goal state in $STATE_FILE" 423 fi 424 425 if [ "$_state_dnskey" = "none" ]; then 426 grep "DNSKEYState: " "$STATE_FILE" > /dev/null && _log_error "unexpected dnskey state in $STATE_FILE" 427 grep "DNSKEYChange: " "$STATE_FILE" > /dev/null && _log_error "unexpected dnskey change in $STATE_FILE" 428 else 429 grep "DNSKEYState: ${_state_dnskey}" "$STATE_FILE" > /dev/null || _log_error "mismatch dnskey state in $STATE_FILE" 430 grep "DNSKEYChange: " "$STATE_FILE" > /dev/null || _log_error "mismatch dnskey change in $STATE_FILE" 431 fi 432 433 if [ "$_state_zrrsig" = "none" ]; then 434 grep "ZRRSIGState: " "$STATE_FILE" > /dev/null && _log_error "unexpected zrrsig state in $STATE_FILE" 435 grep "ZRRSIGChange: " "$STATE_FILE" > /dev/null && _log_error "unexpected zrrsig change in $STATE_FILE" 436 else 437 grep "ZRRSIGState: ${_state_zrrsig}" "$STATE_FILE" > /dev/null || _log_error "mismatch zrrsig state in $STATE_FILE" 438 grep "ZRRSIGChange: " "$STATE_FILE" > /dev/null || _log_error "mismatch zrrsig change in $STATE_FILE" 439 fi 440 441 if [ "$_state_krrsig" = "none" ]; then 442 grep "KRRSIGState: " "$STATE_FILE" > /dev/null && _log_error "unexpected krrsig state in $STATE_FILE" 443 grep "KRRSIGChange: " "$STATE_FILE" > /dev/null && _log_error "unexpected krrsig change in $STATE_FILE" 444 else 445 grep "KRRSIGState: ${_state_krrsig}" "$STATE_FILE" > /dev/null || _log_error "mismatch krrsig state in $STATE_FILE" 446 grep "KRRSIGChange: " "$STATE_FILE" > /dev/null || _log_error "mismatch krrsig change in $STATE_FILE" 447 fi 448 449 if [ "$_state_ds" = "none" ]; then 450 grep "DSState: " "$STATE_FILE" > /dev/null && _log_error "unexpected ds state in $STATE_FILE" 451 grep "DSChange: " "$STATE_FILE" > /dev/null && _log_error "unexpected ds change in $STATE_FILE" 452 else 453 grep "DSState: ${_state_ds}" "$STATE_FILE" > /dev/null || _log_error "mismatch ds state in $STATE_FILE" 454 grep "DSChange: " "$STATE_FILE" > /dev/null || _log_error "mismatch ds change in $STATE_FILE" 455 fi 456 fi 457} 458 459# Check the key timing metadata for key $1. 460check_timingmetadata() { 461 _dir="$DIR" 462 _zone="$ZONE" 463 _key_idpad=$(key_get "$1" ID) 464 _key_id=$(echo "$_key_idpad" | sed 's/^0\{0,4\}//') 465 _alg_num=$(key_get "$1" ALG_NUM) 466 _alg_numpad=$(printf "%03d" "$_alg_num") 467 468 _published=$(key_get "$1" PUBLISHED) 469 _active=$(key_get "$1" ACTIVE) 470 _retired=$(key_get "$1" RETIRED) 471 _revoked=$(key_get "$1" REVOKED) 472 _removed=$(key_get "$1" REMOVED) 473 474 _goal=$(key_get "$1" GOAL) 475 _state_dnskey=$(key_get "$1" STATE_DNSKEY) 476 _state_zrrsig=$(key_get "$1" STATE_ZRRSIG) 477 _state_krrsig=$(key_get "$1" STATE_KRRSIG) 478 _state_ds=$(key_get "$1" STATE_DS) 479 480 _base_file=$(key_get "$1" BASEFILE) 481 _key_file="${_base_file}.key" 482 _private_file="${_base_file}.private" 483 _state_file="${_base_file}.state" 484 _legacy=$(key_get "$1" LEGACY) 485 _private=$(key_get "$1" PRIVATE) 486 487 _published=$(key_get "$1" PUBLISHED) 488 _syncpublish=$(key_get "$1" SYNCPUBLISH) 489 _active=$(key_get "$1" ACTIVE) 490 _retired=$(key_get "$1" RETIRED) 491 _revoked=$(key_get "$1" REVOKED) 492 _removed=$(key_get "$1" REMOVED) 493 494 # Check timing metadata. 495 n=$((n+1)) 496 echo_i "check key timing metadata for key $1 id ${_key_id} zone ${ZONE} ($n)" 497 ret=0 498 499 if [ "$_published" = "none" ]; then 500 grep "; Publish:" "${_key_file}" > /dev/null && _log_error "unexpected publish comment in ${_key_file}" 501 if [ "$_private" = "yes" ]; then 502 grep "Publish:" "${_private_file}" > /dev/null && _log_error "unexpected publish in ${_private_file}" 503 fi 504 if [ "$_legacy" = "no" ]; then 505 grep "Published: " "${_state_file}" > /dev/null && _log_error "unexpected publish in ${_state_file}" 506 fi 507 else 508 grep "; Publish: $_published" "${_key_file}" > /dev/null || _log_error "mismatch publish comment in ${_key_file} (expected ${_published})" 509 if [ "$_private" = "yes" ]; then 510 grep "Publish: $_published" "${_private_file}" > /dev/null || _log_error "mismatch publish in ${_private_file} (expected ${_published})" 511 fi 512 if [ "$_legacy" = "no" ]; then 513 grep "Published: $_published" "${_state_file}" > /dev/null || _log_error "mismatch publish in ${_state_file} (expected ${_published})" 514 fi 515 fi 516 517 if [ "$_syncpublish" = "none" ]; then 518 grep "; SyncPublish:" "${_key_file}" > /dev/null && _log_error "unexpected syncpublish comment in ${_key_file}" 519 if [ "$_private" = "yes" ]; then 520 grep "SyncPublish:" "${_private_file}" > /dev/null && _log_error "unexpected syncpublish in ${_private_file}" 521 fi 522 if [ "$_legacy" = "no" ]; then 523 grep "PublishCDS: " "${_state_file}" > /dev/null && _log_error "unexpected syncpublish in ${_state_file}" 524 fi 525 else 526 grep "; SyncPublish: $_syncpublish" "${_key_file}" > /dev/null || _log_error "mismatch syncpublish comment in ${_key_file} (expected ${_syncpublish})" 527 if [ "$_private" = "yes" ]; then 528 grep "SyncPublish: $_syncpublish" "${_private_file}" > /dev/null || _log_error "mismatch syncpublish in ${_private_file} (expected ${_syncpublish})" 529 fi 530 if [ "$_legacy" = "no" ]; then 531 grep "PublishCDS: $_syncpublish" "${_state_file}" > /dev/null || _log_error "mismatch syncpublish in ${_state_file} (expected ${_syncpublish})" 532 fi 533 fi 534 535 if [ "$_active" = "none" ]; then 536 grep "; Activate:" "${_key_file}" > /dev/null && _log_error "unexpected active comment in ${_key_file}" 537 if [ "$_private" = "yes" ]; then 538 grep "Activate:" "${_private_file}" > /dev/null && _log_error "unexpected active in ${_private_file}" 539 fi 540 if [ "$_legacy" = "no" ]; then 541 grep "Active: " "${_state_file}" > /dev/null && _log_error "unexpected active in ${_state_file}" 542 fi 543 else 544 grep "; Activate: $_active" "${_key_file}" > /dev/null || _log_error "mismatch active comment in ${_key_file} (expected ${_active})" 545 if [ "$_private" = "yes" ]; then 546 grep "Activate: $_active" "${_private_file}" > /dev/null || _log_error "mismatch active in ${_private_file} (expected ${_active})" 547 fi 548 if [ "$_legacy" = "no" ]; then 549 grep "Active: $_active" "${_state_file}" > /dev/null || _log_error "mismatch active in ${_state_file} (expected ${_active})" 550 fi 551 fi 552 553 if [ "$_retired" = "none" ]; then 554 grep "; Inactive:" "${_key_file}" > /dev/null && _log_error "unexpected retired comment in ${_key_file}" 555 if [ "$_private" = "yes" ]; then 556 grep "Inactive:" "${_private_file}" > /dev/null && _log_error "unexpected retired in ${_private_file}" 557 fi 558 if [ "$_legacy" = "no" ]; then 559 grep "Retired: " "${_state_file}" > /dev/null && _log_error "unexpected retired in ${_state_file}" 560 fi 561 else 562 grep "; Inactive: $_retired" "${_key_file}" > /dev/null || _log_error "mismatch retired comment in ${_key_file} (expected ${_retired})" 563 if [ "$_private" = "yes" ]; then 564 grep "Inactive: $_retired" "${_private_file}" > /dev/null || _log_error "mismatch retired in ${_private_file} (expected ${_retired})" 565 fi 566 if [ "$_legacy" = "no" ]; then 567 grep "Retired: $_retired" "${_state_file}" > /dev/null || _log_error "mismatch retired in ${_state_file} (expected ${_retired})" 568 fi 569 fi 570 571 if [ "$_revoked" = "none" ]; then 572 grep "; Revoke:" "${_key_file}" > /dev/null && _log_error "unexpected revoked comment in ${_key_file}" 573 if [ "$_private" = "yes" ]; then 574 grep "Revoke:" "${_private_file}" > /dev/null && _log_error "unexpected revoked in ${_private_file}" 575 fi 576 if [ "$_legacy" = "no" ]; then 577 grep "Revoked: " "${_state_file}" > /dev/null && _log_error "unexpected revoked in ${_state_file}" 578 fi 579 else 580 grep "; Revoke: $_revoked" "${_key_file}" > /dev/null || _log_error "mismatch revoked comment in ${_key_file} (expected ${_revoked})" 581 if [ "$_private" = "yes" ]; then 582 grep "Revoke: $_revoked" "${_private_file}" > /dev/null || _log_error "mismatch revoked in ${_private_file} (expected ${_revoked})" 583 fi 584 if [ "$_legacy" = "no" ]; then 585 grep "Revoked: $_revoked" "${_state_file}" > /dev/null || _log_error "mismatch revoked in ${_state_file} (expected ${_revoked})" 586 fi 587 fi 588 589 if [ "$_removed" = "none" ]; then 590 grep "; Delete:" "${_key_file}" > /dev/null && _log_error "unexpected removed comment in ${_key_file}" 591 if [ "$_private" = "yes" ]; then 592 grep "Delete:" "${_private_file}" > /dev/null && _log_error "unexpected removed in ${_private_file}" 593 fi 594 if [ "$_legacy" = "no" ]; then 595 grep "Removed: " "${_state_file}" > /dev/null && _log_error "unexpected removed in ${_state_file}" 596 fi 597 else 598 grep "; Delete: $_removed" "${_key_file}" > /dev/null || _log_error "mismatch removed comment in ${_key_file} (expected ${_removed})" 599 if [ "$_private" = "yes" ]; then 600 grep "Delete: $_removed" "${_private_file}" > /dev/null || _log_error "mismatch removed in ${_private_file} (expected ${_removed})" 601 fi 602 if [ "$_legacy" = "no" ]; then 603 grep "Removed: $_removed" "${_state_file}" > /dev/null || _log_error "mismatch removed in ${_state_file} (expected ${_removed})" 604 fi 605 fi 606 607 test "$ret" -eq 0 || echo_i "failed" 608 status=$((status+ret)) 609} 610 611check_keytimes() { 612 # The script relies on Python to set keytimes. 613 if [ -x "$PYTHON" ]; then 614 615 if [ "$(key_get KEY1 EXPECT)" = "yes" ]; then 616 check_timingmetadata "KEY1" 617 fi 618 if [ "$(key_get KEY2 EXPECT)" = "yes" ]; then 619 check_timingmetadata "KEY2" 620 fi 621 if [ "$(key_get KEY3 EXPECT)" = "yes" ]; then 622 check_timingmetadata "KEY3" 623 fi 624 if [ "$(key_get KEY4 EXPECT)" = "yes" ]; then 625 check_timingmetadata "KEY4" 626 fi 627 fi 628} 629 630# Check the key with key id $1 and see if it is unused. 631# This requires environment variables to be set. 632# 633# This will set the following environment variables for testing: 634# BASE_FILE="${_dir}/K${_zone}.+${_alg_numpad}+${_key_idpad}" 635# KEY_FILE="${BASE_FILE}.key" 636# PRIVATE_FILE="${BASE_FILE}.private" 637# STATE_FILE="${BASE_FILE}.state" 638# KEY_ID=$(echo $1 | sed 's/^0\{0,4\}//') 639key_unused() { 640 _dir=$DIR 641 _zone=$ZONE 642 _key_idpad=$1 643 _key_id=$(echo "$_key_idpad" | sed 's/^0\{0,4\}//') 644 _alg_num=$2 645 _alg_numpad=$(printf "%03d" "$_alg_num") 646 647 BASE_FILE="${_dir}/K${_zone}.+${_alg_numpad}+${_key_idpad}" 648 KEY_FILE="${BASE_FILE}.key" 649 PRIVATE_FILE="${BASE_FILE}.private" 650 STATE_FILE="${BASE_FILE}.state" 651 KEY_ID="${_key_id}" 652 653 test $_log -eq 1 && echo_i "key unused $KEY_ID?" 654 655 # Check file existence. 656 [ -s "$KEY_FILE" ] || ret=1 657 [ -s "$PRIVATE_FILE" ] || ret=1 658 [ -s "$STATE_FILE" ] || ret=1 659 [ "$ret" -eq 0 ] || return 660 661 # Treat keys that have been removed from the zone as unused. 662 _check_removed=1 663 grep "; Created:" "$KEY_FILE" > created.key-${KEY_ID}.test${n} || _check_removed=0 664 grep "; Delete:" "$KEY_FILE" > unused.key-${KEY_ID}.test${n} || _check_removed=0 665 if [ "$_check_removed" -eq 1 ]; then 666 _created=$(awk '{print $3}' < created.key-${KEY_ID}.test${n}) 667 _removed=$(awk '{print $3}' < unused.key-${KEY_ID}.test${n}) 668 [ "$_removed" -le "$_created" ] && return 669 fi 670 671 # If no timing metadata is set, this key is unused. 672 grep "; Publish:" "$KEY_FILE" > /dev/null && _log_error "unexpected publish comment in $KEY_FILE" 673 grep "; Activate:" "$KEY_FILE" > /dev/null && _log_error "unexpected active comment in $KEY_FILE" 674 grep "; Inactive:" "$KEY_FILE" > /dev/null && _log_error "unexpected retired comment in $KEY_FILE" 675 grep "; Revoke:" "$KEY_FILE" > /dev/null && _log_error "unexpected revoked comment in $KEY_FILE" 676 grep "; Delete:" "$KEY_FILE" > /dev/null && _log_error "unexpected removed comment in $KEY_FILE" 677 678 grep "Publish:" "$PRIVATE_FILE" > /dev/null && _log_error "unexpected publish in $PRIVATE_FILE" 679 grep "Activate:" "$PRIVATE_FILE" > /dev/null && _log_error "unexpected active in $PRIVATE_FILE" 680 grep "Inactive:" "$PRIVATE_FILE" > /dev/null && _log_error "unexpected retired in $PRIVATE_FILE" 681 grep "Revoke:" "$PRIVATE_FILE" > /dev/null && _log_error "unexpected revoked in $PRIVATE_FILE" 682 grep "Delete:" "$PRIVATE_FILE" > /dev/null && _log_error "unexpected removed in $PRIVATE_FILE" 683 684 grep "Published: " "$STATE_FILE" > /dev/null && _log_error "unexpected publish in $STATE_FILE" 685 grep "Active: " "$STATE_FILE" > /dev/null && _log_error "unexpected active in $STATE_FILE" 686 grep "Retired: " "$STATE_FILE" > /dev/null && _log_error "unexpected retired in $STATE_FILE" 687 grep "Revoked: " "$STATE_FILE" > /dev/null && _log_error "unexpected revoked in $STATE_FILE" 688 grep "Removed: " "$STATE_FILE" > /dev/null && _log_error "unexpected removed in $STATE_FILE" 689} 690 691# Test: dnssec-verify zone $1. 692dnssec_verify() 693{ 694 n=$((n+1)) 695 echo_i "dnssec-verify zone ${ZONE} ($n)" 696 ret=0 697 _dig_with_opts "$ZONE" "@${SERVER}" AXFR > dig.out.axfr.test$n || _log_error "dig ${ZONE} AXFR failed" 698 $VERIFY -z -o "$ZONE" dig.out.axfr.test$n > verify.out.$ZONE.test$n || _log_error "dnssec verify zone $ZONE failed" 699 test "$ret" -eq 0 || echo_i "failed" 700 status=$((status+ret)) 701} 702 703# Wait for the zone to be signed. 704# The apex NSEC record indicates that it is signed. 705_wait_for_nsec() { 706 _dig_with_opts "@${SERVER}" "$ZONE" NSEC > "dig.out.nsec.test$n" || return 1 707 grep "NS SOA" "dig.out.nsec.test$n" > /dev/null || return 1 708 grep "${ZONE}\..*IN.*RRSIG" "dig.out.nsec.test$n" > /dev/null || return 1 709 return 0 710} 711wait_for_nsec() { 712 n=$((n+1)) 713 ret=0 714 echo_i "wait for ${ZONE} to be signed ($n)" 715 retry_quiet 10 _wait_for_nsec || _log_error "wait for ${ZONE} to be signed failed" 716 test "$ret" -eq 0 || echo_i "failed" 717 status=$((status+ret)) 718} 719 720check_numkeys() { 721 _numkeys=$(get_keyids "$DIR" "$ZONE" | wc -l) 722 test "$_numkeys" -eq "$NUM_KEYS" || return 1 723 return 0 724} 725 726_check_keys() { 727 ret=0 728 729 # Clear key ids. 730 key_set KEY1 ID "no" 731 key_set KEY2 ID "no" 732 key_set KEY3 ID "no" 733 key_set KEY4 ID "no" 734 735 # Check key files. 736 _ids=$(get_keyids "$DIR" "$ZONE") 737 for _id in $_ids; do 738 # There are multiple key files with the same algorithm. 739 # Check them until a match is found. 740 ret=0 741 echo_i "check key id $_id" 742 743 if [ "no" = "$(key_get KEY1 ID)" ] && [ "$(key_get KEY1 EXPECT)" = "yes" ]; then 744 ret=0 745 check_key "KEY1" "$_id" 746 test "$ret" -eq 0 && key_save KEY1 && continue 747 fi 748 if [ "no" = "$(key_get KEY2 ID)" ] && [ "$(key_get KEY2 EXPECT)" = "yes" ]; then 749 ret=0 750 check_key "KEY2" "$_id" 751 test "$ret" -eq 0 && key_save KEY2 && continue 752 fi 753 if [ "no" = "$(key_get KEY3 ID)" ] && [ "$(key_get KEY3 EXPECT)" = "yes" ]; then 754 ret=0 755 check_key "KEY3" "$_id" 756 test "$ret" -eq 0 && key_save KEY3 && continue 757 fi 758 if [ "no" = "$(key_get KEY4 ID)" ] && [ "$(key_get KEY4 EXPECT)" = "yes" ]; then 759 ret=0 760 check_key "KEY4" "$_id" 761 test "$ret" -eq 0 && key_save KEY4 && continue 762 fi 763 764 # This may be an unused key. Assume algorithm of KEY1. 765 ret=0 && key_unused "$_id" "$(key_get KEY1 ALG_NUM)" 766 test "$ret" -eq 0 && continue 767 768 # If ret is still non-zero, none of the files matched. 769 echo_i "failed" 770 return 1 771 done 772 773 return 0 774} 775 776# Check keys for a configured zone. This verifies: 777# 1. The right number of keys exist in the key pool ($1). 778# 2. The right number of keys is active. Checks KEY1, KEY2, KEY3, and KEY4. 779# 780# It is expected that KEY1, KEY2, KEY3, and KEY4 arrays are set correctly. 781# Found key identifiers are stored in the right key array. 782check_keys() { 783 n=$((n+1)) 784 echo_i "check keys are created for zone ${ZONE} ($n)" 785 ret=0 786 787 echo_i "check number of keys for zone ${ZONE} in dir ${DIR} ($n)" 788 retry_quiet 10 check_numkeys || ret=1 789 if [ $ret -ne 0 ]; then 790 _numkeys=$(get_keyids "$DIR" "$ZONE" | wc -l) 791 _log_error "bad number of key files ($_numkeys) for zone $ZONE (expected $NUM_KEYS)" 792 status=$((status+ret)) 793 fi 794 795 # Temporarily don't log errors because we are searching multiple files. 796 disable_logerror 797 798 retry_quiet 3 _check_keys || ret=1 799 test "$ret" -eq 0 || echo_i "failed" 800 status=$((status+ret)) 801 802 # Turn error logs on again. 803 enable_logerror 804 805 ret=0 806 if [ "$(key_get KEY1 EXPECT)" = "yes" ]; then 807 echo_i "KEY1 ID $(key_get KEY1 ID)" 808 test "no" = "$(key_get KEY1 ID)" && _log_error "No KEY1 found for zone ${ZONE}" 809 fi 810 if [ "$(key_get KEY2 EXPECT)" = "yes" ]; then 811 echo_i "KEY2 ID $(key_get KEY2 ID)" 812 test "no" = "$(key_get KEY2 ID)" && _log_error "No KEY2 found for zone ${ZONE}" 813 fi 814 if [ "$(key_get KEY3 EXPECT)" = "yes" ]; then 815 echo_i "KEY3 ID $(key_get KEY3 ID)" 816 test "no" = "$(key_get KEY3 ID)" && _log_error "No KEY3 found for zone ${ZONE}" 817 fi 818 if [ "$(key_get KEY4 EXPECT)" = "yes" ]; then 819 echo_i "KEY4 ID $(key_get KEY4 ID)" 820 test "no" = "$(key_get KEY4 ID)" && _log_error "No KEY4 found for zone ${ZONE}" 821 fi 822 test "$ret" -eq 0 || echo_i "failed" 823 status=$((status+ret)) 824} 825 826# Call rndc dnssec -status on server $1 for zone $3 in view $4 with policy $2 827# and check output. This is a loose verification, it just tests if the right 828# policy name is returned, and if all expected keys are listed. The rndc 829# dnssec -status output also lists whether a key is published, 830# used for signing, is retired, or is removed, and if not when 831# it is scheduled to do so, and it shows the states for the various 832# DNSSEC records. 833check_dnssecstatus() { 834 _server=$1 835 _policy=$2 836 _zone=$3 837 _view=$4 838 839 n=$((n+1)) 840 echo_i "check rndc dnssec -status output for ${_zone} (policy: $_policy) ($n)" 841 ret=0 842 843 _rndccmd $_server dnssec -status $_zone in $_view > rndc.dnssec.status.out.$_zone.$n || _log_error "rndc dnssec -status zone ${_zone} failed" 844 845 if [ "$_policy" = "none" ]; then 846 grep "Zone does not have dnssec-policy" rndc.dnssec.status.out.$_zone.$n > /dev/null || log_error "bad dnssec status for unsigned zone ${_zone}" 847 else 848 grep "dnssec-policy: ${_policy}" rndc.dnssec.status.out.$_zone.$n > /dev/null || _log_error "bad dnssec status for signed zone ${_zone}" 849 if [ "$(key_get KEY1 EXPECT)" = "yes" ]; then 850 grep "key: $(key_get KEY1 ID)" rndc.dnssec.status.out.$_zone.$n > /dev/null || _log_error "missing key $(key_get KEY1 ID) from dnssec status" 851 fi 852 if [ "$(key_get KEY2 EXPECT)" = "yes" ]; then 853 grep "key: $(key_get KEY2 ID)" rndc.dnssec.status.out.$_zone.$n > /dev/null || _log_error "missing key $(key_get KEY2 ID) from dnssec status" 854 fi 855 if [ "$(key_get KEY3 EXPECT)" = "yes" ]; then 856 grep "key: $(key_get KEY3 ID)" rndc.dnssec.status.out.$_zone.$n > /dev/null || _log_error "missing key $(key_get KEY3 ID) from dnssec status" 857 fi 858 if [ "$(key_get KEY4 EXPECT)" = "yes" ]; then 859 grep "key: $(key_get KEY4 ID)" rndc.dnssec.status.out.$_zone.$n > /dev/null || _log_error "missing key $(key_get KEY4 ID) from dnssec status" 860 fi 861 fi 862 863 test "$ret" -eq 0 || echo_i "failed" 864 status=$((status+ret)) 865} 866 867# Call rndc zonestatus on server $1 for zone $2 in view $3 and check output if 868# inline-signing is enabled. 869check_inlinesigning() { 870 _server=$1 871 _zone=$2 872 _view=$3 873 874 _rndccmd $_server zonestatus $_zone in $_view > rndc.zonestatus.out.$_zone.$n || return 1 875 grep "inline signing: yes" rndc.zonestatus.out.$_zone.$n > /dev/null || return 1 876} 877 878# Call rndc zonestatus on server $1 for zone $2 in view $3 and check output if 879# the zone is dynamic. 880check_isdynamic() { 881 _server=$1 882 _zone=$2 883 _view=$3 884 885 _rndccmd $_server zonestatus $_zone in $_view > rndc.zonestatus.out.$_zone.$n || return 1 886 grep "dynamic: yes" rndc.zonestatus.out.$_zone.$n > /dev/null || return 1 887} 888 889# Check if RRset of type $1 in file $2 is signed with the right keys. 890# The right keys are the ones that expect a signature and matches the role $3. 891_check_signatures() { 892 _qtype=$1 893 _file=$2 894 _role=$3 895 896 numsigs=0 897 898 if [ "$_role" = "KSK" ]; then 899 _expect_type=EXPECT_KRRSIG 900 elif [ "$_role" = "ZSK" ]; then 901 _expect_type=EXPECT_ZRRSIG 902 fi 903 904 if [ "$(key_get KEY1 "$_expect_type")" = "yes" ] && [ "$(key_get KEY1 "$_role")" = "yes" ]; then 905 get_keys_which_signed "$_qtype" "$_file" | grep "^$(key_get KEY1 ID)$" > /dev/null || return 1 906 numsigs=$((numsigs+1)) 907 elif [ "$(key_get KEY1 EXPECT)" = "yes" ]; then 908 get_keys_which_signed "$_qtype" "$_file" | grep "^$(key_get KEY1 ID)$" > /dev/null && return 1 909 fi 910 911 if [ "$(key_get KEY2 "$_expect_type")" = "yes" ] && [ "$(key_get KEY2 "$_role")" = "yes" ]; then 912 get_keys_which_signed "$_qtype" "$_file" | grep "^$(key_get KEY2 ID)$" > /dev/null || return 1 913 numsigs=$((numsigs+1)) 914 elif [ "$(key_get KEY2 EXPECT)" = "yes" ]; then 915 get_keys_which_signed "$_qtype" "$_file" | grep "^$(key_get KEY2 ID)$" > /dev/null && return 1 916 fi 917 918 if [ "$(key_get KEY3 "$_expect_type")" = "yes" ] && [ "$(key_get KEY3 "$_role")" = "yes" ]; then 919 get_keys_which_signed "$_qtype" "$_file" | grep "^$(key_get KEY3 ID)$" > /dev/null || return 1 920 numsigs=$((numsigs+1)) 921 elif [ "$(key_get KEY3 EXPECT)" = "yes" ]; then 922 get_keys_which_signed "$_qtype" "$_file" | grep "^$(key_get KEY3 ID)$" > /dev/null && return 1 923 fi 924 925 if [ "$(key_get KEY4 "$_expect_type")" = "yes" ] && [ "$(key_get KEY4 "$_role")" = "yes" ]; then 926 get_keys_which_signed "$_qtype" "$_file" | grep "^$(key_get KEY4 ID)$" > /dev/null || return 1 927 numsigs=$((numsigs+1)) 928 elif [ "$(key_get KEY4 EXPECT)" = "yes" ]; then 929 get_keys_which_signed "$_qtype" "$_file" | grep "^$(key_get KEY4 ID)$" > /dev/null && return 1 930 fi 931 932 lines=$(get_keys_which_signed "${_qtype}" "${_file}" | wc -l) 933 test "$lines" -eq "$numsigs" || echo_i "bad number of signatures for $_qtype (got $lines, expected $numsigs)" 934 test "$lines" -eq "$numsigs" || return 1 935 936 return 0 937} 938check_signatures() { 939 retry_quiet 3 _check_signatures $1 $2 $3 || _log_error "RRset $1 in zone $ZONE incorrectly signed" 940} 941 942response_has_cds_for_key() ( 943 awk -v zone="${ZONE%%.}." \ 944 -v ttl="${DNSKEY_TTL}" \ 945 -v qtype="CDS" \ 946 -v keyid="$(key_get "${1}" ID)" \ 947 -v keyalg="$(key_get "${1}" ALG_NUM)" \ 948 -v hashalg="2" \ 949 'BEGIN { ret=1; } 950 $1 == zone && $2 == ttl && $4 == qtype && $5 == keyid && $6 == keyalg && $7 == hashalg { ret=0; exit; } 951 END { exit ret; }' \ 952 "$2" 953) 954 955response_has_cdnskey_for_key() ( 956 957 awk -v zone="${ZONE%%.}." \ 958 -v ttl="${DNSKEY_TTL}" \ 959 -v qtype="CDNSKEY" \ 960 -v flags="$(key_get "${1}" FLAGS)" \ 961 -v keyalg="$(key_get "${1}" ALG_NUM)" \ 962 'BEGIN { ret=1; } 963 $1 == zone && $2 == ttl && $4 == qtype && $5 == flags && $7 == keyalg { ret=0; exit; } 964 END { exit ret; }' \ 965 "$2" 966) 967 968# Test CDS and CDNSKEY publication. 969check_cds() { 970 971 n=$((n+1)) 972 echo_i "check CDS and CDNSKEY rrset are signed correctly for zone ${ZONE} ($n)" 973 ret=0 974 975 _checksig=0 976 977 _dig_with_opts "$ZONE" "@${SERVER}" "CDS" > "dig.out.$DIR.test$n.cds" || _log_error "dig ${ZONE} CDS failed" 978 grep "status: NOERROR" "dig.out.$DIR.test$n.cds" > /dev/null || _log_error "mismatch status in DNS response" 979 980 _dig_with_opts "$ZONE" "@${SERVER}" "CDNSKEY" > "dig.out.$DIR.test$n.cdnskey" || _log_error "dig ${ZONE} CDNSKEY failed" 981 grep "status: NOERROR" "dig.out.$DIR.test$n.cdnskey" > /dev/null || _log_error "mismatch status in DNS response" 982 983 if [ "$CDS_DELETE" = "no" ]; then 984 grep "CDS.*0 0 0 00" "dig.out.$DIR.test$n.cds" > /dev/null && _log_error "unexpected CDS DELETE record in DNS response" 985 grep "CDNSKEY.*0 3 0 AA==" "dig.out.$DIR.test$n.cdnskey" > /dev/null && _log_error "unexpected CDNSKEY DELETE record in DNS response" 986 else 987 grep "CDS.*0 0 0 00" "dig.out.$DIR.test$n.cds" > /dev/null || _log_error "missing CDS DELETE record in DNS response" 988 grep "CDNSKEY.*0 3 0 AA==" "dig.out.$DIR.test$n.cdnskey" > /dev/null || _log_error "missing CDNSKEY DELETE record in DNS response" 989 _checksig=1 990 fi 991 992 if [ "$(key_get KEY1 STATE_DS)" = "rumoured" ] || [ "$(key_get KEY1 STATE_DS)" = "omnipresent" ]; then 993 response_has_cds_for_key KEY1 "dig.out.$DIR.test$n.cds" || _log_error "missing CDS record in response for key $(key_get KEY1 ID)" 994 response_has_cdnskey_for_key KEY1 "dig.out.$DIR.test$n.cdnskey" || _log_error "missing CDNSKEY record in response for key $(key_get KEY1 ID)" 995 _checksig=1 996 elif [ "$(key_get KEY1 EXPECT)" = "yes" ]; then 997 response_has_cds_for_key KEY1 "dig.out.$DIR.test$n.cds" && _log_error "unexpected CDS record in response for key $(key_get KEY1 ID)" 998 # KEY1 should not have an associated CDNSKEY, but there may be 999 # one for another key. Since the CDNSKEY has no field for key 1000 # id, it is hard to check what key the CDNSKEY may belong to 1001 # so let's skip this check for now. 1002 fi 1003 1004 if [ "$(key_get KEY2 STATE_DS)" = "rumoured" ] || [ "$(key_get KEY2 STATE_DS)" = "omnipresent" ]; then 1005 response_has_cds_for_key KEY2 "dig.out.$DIR.test$n.cds" || _log_error "missing CDS record in response for key $(key_get KEY2 ID)" 1006 response_has_cdnskey_for_key KEY2 "dig.out.$DIR.test$n.cdnskey" || _log_error "missing CDNSKEY record in response for key $(key_get KEY2 ID)" 1007 _checksig=1 1008 elif [ "$(key_get KEY2 EXPECT)" = "yes" ]; then 1009 response_has_cds_for_key KEY2 "dig.out.$DIR.test$n.cds" && _log_error "unexpected CDS record in response for key $(key_get KEY2 ID)" 1010 # KEY2 should not have an associated CDNSKEY, but there may be 1011 # one for another key. Since the CDNSKEY has no field for key 1012 # id, it is hard to check what key the CDNSKEY may belong to 1013 # so let's skip this check for now. 1014 fi 1015 1016 if [ "$(key_get KEY3 STATE_DS)" = "rumoured" ] || [ "$(key_get KEY3 STATE_DS)" = "omnipresent" ]; then 1017 response_has_cds_for_key KEY3 "dig.out.$DIR.test$n.cds" || _log_error "missing CDS record in response for key $(key_get KEY3 ID)" 1018 response_has_cdnskey_for_key KEY3 "dig.out.$DIR.test$n.cdnskey" || _log_error "missing CDNSKEY record in response for key $(key_get KEY3 ID)" 1019 _checksig=1 1020 elif [ "$(key_get KEY3 EXPECT)" = "yes" ]; then 1021 response_has_cds_for_key KEY3 "dig.out.$DIR.test$n.cds" && _log_error "unexpected CDS record in response for key $(key_get KEY3 ID)" 1022 # KEY3 should not have an associated CDNSKEY, but there may be 1023 # one for another key. Since the CDNSKEY has no field for key 1024 # id, it is hard to check what key the CDNSKEY may belong to 1025 # so let's skip this check for now. 1026 fi 1027 1028 if [ "$(key_get KEY4 STATE_DS)" = "rumoured" ] || [ "$(key_get KEY4 STATE_DS)" = "omnipresent" ]; then 1029 response_has_cds_for_key KEY4 "dig.out.$DIR.test$n.cds" || _log_error "missing CDS record in response for key $(key_get KEY4 ID)" 1030 response_has_cdnskey_for_key KEY4 "dig.out.$DIR.test$n.cdnskey" || _log_error "missing CDNSKEY record in response for key $(key_get KEY4 ID)" 1031 _checksig=1 1032 elif [ "$(key_get KEY4 EXPECT)" = "yes" ]; then 1033 response_has_cds_for_key KEY4 "dig.out.$DIR.test$n.cds" && _log_error "unexpected CDS record in response for key $(key_get KEY4 ID)" 1034 # KEY4 should not have an associated CDNSKEY, but there may be 1035 # one for another key. Since the CDNSKEY has no field for key 1036 # id, it is hard to check what key the CDNSKEY may belong to 1037 # so let's skip this check for now. 1038 fi 1039 1040 test "$_checksig" -eq 0 || check_signatures "CDS" "dig.out.$DIR.test$n.cds" "KSK" 1041 test "$_checksig" -eq 0 || check_signatures "CDNSKEY" "dig.out.$DIR.test$n.cdnskey" "KSK" 1042 1043 test "$ret" -eq 0 || echo_i "failed" 1044 status=$((status+ret)) 1045} 1046 1047_find_dnskey() { 1048 _owner="${ZONE}." 1049 _alg="$(key_get $1 ALG_NUM)" 1050 _flags="$(key_get $1 FLAGS)" 1051 _key_file="$(key_get $1 BASEFILE).key" 1052 1053 awk '$1 == "'"$_owner"'" && $2 == "'"$DNSKEY_TTL"'" && $3 == "IN" && $4 == "DNSKEY" && $5 == "'"$_flags"'" && $6 == "3" && $7 == "'"$_alg"'" { print $8 }' < "$_key_file" 1054} 1055 1056 1057# Test DNSKEY query. 1058_check_apex_dnskey() { 1059 _dig_with_opts "$ZONE" "@${SERVER}" "DNSKEY" > "dig.out.$DIR.test$n" || return 1 1060 grep "status: NOERROR" "dig.out.$DIR.test$n" > /dev/null || return 1 1061 1062 _checksig=0 1063 1064 if [ "$(key_get KEY1 STATE_DNSKEY)" = "rumoured" ] || [ "$(key_get KEY1 STATE_DNSKEY)" = "omnipresent" ]; then 1065 _pubkey=$(_find_dnskey KEY1) 1066 test -z "$_pubkey" && return 1 1067 grep -F "$_pubkey" "dig.out.$DIR.test$n" > /dev/null || return 1 1068 _checksig=1 1069 elif [ "$(key_get KEY1 EXPECT)" = "yes" ]; then 1070 _pubkey=$(_find_dnskey KEY1) 1071 test -z "$_pubkey" && return 1 1072 grep -F "$_pubkey" "dig.out.$DIR.test$n" > /dev/null && return 1 1073 fi 1074 1075 if [ "$(key_get KEY2 STATE_DNSKEY)" = "rumoured" ] || [ "$(key_get KEY2 STATE_DNSKEY)" = "omnipresent" ]; then 1076 _pubkey=$(_find_dnskey KEY2) 1077 test -z "$_pubkey" && return 1 1078 grep -F "$_pubkey" "dig.out.$DIR.test$n" > /dev/null || return 1 1079 _checksig=1 1080 elif [ "$(key_get KEY2 EXPECT)" = "yes" ]; then 1081 _pubkey=$(_find_dnskey KEY2) 1082 test -z "$_pubkey" && return 1 1083 grep -F "$_pubkey" "dig.out.$DIR.test$n" > /dev/null && return 1 1084 fi 1085 1086 if [ "$(key_get KEY3 STATE_DNSKEY)" = "rumoured" ] || [ "$(key_get KEY3 STATE_DNSKEY)" = "omnipresent" ]; then 1087 _pubkey=$(_find_dnskey KEY3) 1088 test -z "$_pubkey" && return 1 1089 grep -F "$_pubkey" "dig.out.$DIR.test$n" > /dev/null || return 1 1090 _checksig=1 1091 elif [ "$(key_get KEY3 EXPECT)" = "yes" ]; then 1092 _pubkey=$(_find_dnskey KEY3) 1093 test -z "$_pubkey" && return 1 1094 grep -F "$_pubkey" "dig.out.$DIR.test$n" > /dev/null && return 1 1095 fi 1096 1097 if [ "$(key_get KEY4 STATE_DNSKEY)" = "rumoured" ] || [ "$(key_get KEY4 STATE_DNSKEY)" = "omnipresent" ]; then 1098 _pubkey=$(_find_dnskey KEY4) 1099 test -z "$_pubkey" && return 1 1100 grep -F "$_pubkey" "dig.out.$DIR.test$n" > /dev/null || return 1 1101 _checksig=1 1102 elif [ "$(key_get KEY4 EXPECT)" = "yes" ]; then 1103 _pubkey=$(_find_dnskey KEY4) 1104 test -z "$_pubkey" && return 1 1105 grep -F "$_pubkey" "dig.out.$DIR.test$n" > /dev/null && return 1 1106 fi 1107 1108 test "$_checksig" -eq 0 && return 0 1109 1110 _check_signatures "DNSKEY" "dig.out.$DIR.test$n" "KSK" || return 1 1111 1112 return 0 1113} 1114 1115# Test the apex of a configured zone. This checks that the SOA and DNSKEY 1116# RRsets are signed correctly and with the appropriate keys. 1117check_apex() { 1118 1119 # Test DNSKEY query. 1120 n=$((n+1)) 1121 echo_i "check DNSKEY rrset is signed correctly for zone ${ZONE} ($n)" 1122 ret=0 1123 retry_quiet 10 _check_apex_dnskey || ret=1 1124 test "$ret" -eq 0 || echo_i "failed" 1125 status=$((status+ret)) 1126 1127 # We retry the DNSKEY query for at most ten seconds to avoid test 1128 # failures due to timing issues. If the DNSKEY query check passes this 1129 # means the zone is resigned and further apex checks (SOA, CDS, CDNSKEY) 1130 # don't need to be retried quietly. 1131 1132 # Test SOA query. 1133 n=$((n+1)) 1134 echo_i "check SOA rrset is signed correctly for zone ${ZONE} ($n)" 1135 ret=0 1136 _dig_with_opts "$ZONE" "@${SERVER}" "SOA" > "dig.out.$DIR.test$n" || _log_error "dig ${ZONE} SOA failed" 1137 grep "status: NOERROR" "dig.out.$DIR.test$n" > /dev/null || _log_error "mismatch status in DNS response" 1138 grep "${ZONE}\..*${DEFAULT_TTL}.*IN.*SOA.*" "dig.out.$DIR.test$n" > /dev/null || _log_error "missing SOA record in response" 1139 check_signatures "SOA" "dig.out.$DIR.test$n" "ZSK" 1140 test "$ret" -eq 0 || echo_i "failed" 1141 status=$((status+ret)) 1142 1143 # Test CDS and CDNSKEY publication. 1144 check_cds 1145} 1146 1147# Test an RRset below the apex and verify it is signed correctly. 1148check_subdomain() { 1149 _qtype="A" 1150 n=$((n+1)) 1151 echo_i "check ${_qtype} a.${ZONE} rrset is signed correctly for zone ${ZONE} ($n)" 1152 ret=0 1153 _dig_with_opts "a.$ZONE" "@${SERVER}" $_qtype > "dig.out.$DIR.test$n" || _log_error "dig a.${ZONE} ${_qtype} failed" 1154 grep "status: NOERROR" "dig.out.$DIR.test$n" > /dev/null || _log_error "mismatch status in DNS response" 1155 grep "a.${ZONE}\..*${DEFAULT_TTL}.*IN.*${_qtype}.*10\.0\.0\.1" "dig.out.$DIR.test$n" > /dev/null || _log_error "missing a.${ZONE} ${_qtype} record in response" 1156 lines=$(get_keys_which_signed $_qtype "dig.out.$DIR.test$n" | wc -l) 1157 check_signatures $_qtype "dig.out.$DIR.test$n" "ZSK" 1158 test "$ret" -eq 0 || echo_i "failed" 1159 status=$((status+ret)) 1160} 1161 1162# Check if "CDS/CDNSKEY Published" is logged. 1163check_cdslog() { 1164 _dir=$1 1165 _zone=$2 1166 _key=$3 1167 1168 _alg=$(key_get $_key ALG_STR) 1169 _id=$(key_get $_key ID) 1170 1171 n=$((n+1)) 1172 echo_i "check CDS/CDNSKEY publication is logged in ${_dir}/named.run for key ${_zone}/${_alg}/${_id} ($n)" 1173 ret=0 1174 1175 grep "CDS for key ${_zone}/${_alg}/${_id} is now published" "${_dir}/named.run" > /dev/null || ret=1 1176 grep "CDNSKEY for key ${_zone}/${_alg}/${_id} is now published" "${_dir}/named.run" > /dev/null || ret=1 1177 1178 test "$ret" -eq 0 || echo_i "failed" 1179 status=$((status+ret)) 1180} 1181 1182# Tell named that the DS for the key in given zone has been seen in the 1183# parent (this does not actually has to be true, we just issue the command 1184# to make named believe it can continue with the rollover). 1185rndc_checkds() { 1186 _server=$1 1187 _dir=$2 1188 _key=$3 1189 _when=$4 1190 _what=$5 1191 _zone=$6 1192 _view=$7 1193 1194 _keycmd="" 1195 if [ "${_key}" != "-" ]; then 1196 _keyid=$(key_get $_key ID) 1197 _keycmd=" -key ${_keyid}" 1198 fi 1199 1200 _whencmd="" 1201 if [ "${_when}" != "now" ]; then 1202 _whencmd=" -when ${_when}" 1203 fi 1204 1205 n=$((n+1)) 1206 echo_i "calling rndc dnssec -checkds${_keycmd}${_whencmd} ${_what} zone ${_zone} in ${_view} ($n)" 1207 ret=0 1208 1209 _rndccmd $_server dnssec -checkds $_keycmd $_whencmd $_what $_zone in $_view > rndc.dnssec.checkds.out.$_zone.$n || _log_error "rndc dnssec -checkds${_keycmd}${_whencmd} ${_what} zone ${_zone} failed" 1210 1211 test "$ret" -eq 0 || echo_i "failed" 1212 status=$((status+ret)) 1213} 1214 1215# Tell named to schedule a key rollover. 1216rndc_rollover() { 1217 _server=$1 1218 _dir=$2 1219 _keyid=$3 1220 _when=$4 1221 _zone=$5 1222 _view=$6 1223 1224 _whencmd="" 1225 if [ "${_when}" != "now" ]; then 1226 _whencmd="-when ${_when}" 1227 fi 1228 1229 n=$((n+1)) 1230 echo_i "calling rndc dnssec -rollover key ${_keyid} ${_whencmd} zone ${_zone} ($n)" 1231 ret=0 1232 1233 _rndccmd $_server dnssec -rollover -key $_keyid $_whencmd $_zone in $_view > rndc.dnssec.rollover.out.$_zone.$n || _log_error "rndc dnssec -rollover (key ${_keyid} when ${_when}) zone ${_zone} failed" 1234 1235 test "$ret" -eq 0 || echo_i "failed" 1236 status=$((status+ret)) 1237} 1238