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