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# shellcheck source=conf.sh
15. ../conf.sh
16# shellcheck source=kasp.sh
17. ../kasp.sh
18
19# Log errors and increment $ret.
20log_error() {
21	echo_i "error: $1"
22	ret=$((ret+1))
23}
24
25# Call dig with default options.
26dig_with_opts() {
27	$DIG +tcp +noadd +nosea +nostat +nocmd +dnssec -p "$PORT" "$@"
28}
29
30# Call rndc.
31rndccmd() {
32    "$RNDC" -c ../common/rndc.conf -p "$CONTROLPORT" -s "$@"
33}
34
35# Set zone name ($1) and policy ($2) for testing nsec3.
36set_zone_policy() {
37	ZONE=$1
38	POLICY=$2
39}
40# Set expected NSEC3 parameters: flags ($1), iterations ($2), and
41# salt length ($3).
42set_nsec3param() {
43	FLAGS=$1
44	ITERATIONS=$2
45	SALTLEN=$3
46	# Reset salt.
47	SALT=""
48}
49
50# The apex NSEC3PARAM record indicates that it is signed.
51_wait_for_nsec3param() {
52	dig_with_opts +noquestion "@${SERVER}" "$ZONE" NSEC3PARAM > "dig.out.test$n.wait" || return 1
53	grep "${ZONE}\..*IN.*NSEC3PARAM.*1.*0.*${ITERATIONS}.*${SALT}" "dig.out.test$n.wait" > /dev/null || return 1
54	grep "${ZONE}\..*IN.*RRSIG" "dig.out.test$n.wait" > /dev/null || return 1
55        return 0
56}
57# The apex NSEC record indicates that it is signed.
58_wait_for_nsec() {
59	dig_with_opts +noquestion "@${SERVER}" "$ZONE" NSEC > "dig.out.test$n.wait" || return 1
60	grep "NS SOA" "dig.out.test$n.wait" > /dev/null || return 1
61	grep "${ZONE}\..*IN.*RRSIG" "dig.out.test$n.wait" > /dev/null || return 1
62	grep "${ZONE}\..*IN.*NSEC3PARAM" "dig.out.test$n.wait" > /dev/null && return 1
63        return 0
64}
65
66# Wait for the zone to be signed.
67wait_for_zone_is_signed() {
68	n=$((n+1))
69	ret=0
70	echo_i "wait for ${ZONE} to be signed ($n)"
71
72	if [ "$1" = "nsec3" ]; then
73		retry_quiet 10 _wait_for_nsec3param || log_error "wait for ${ZONE} to be signed failed"
74	else
75		retry_quiet 10 _wait_for_nsec || log_error "wait for ${ZONE} to be signed failed"
76	fi
77
78	test "$ret" -eq 0 || echo_i "failed"
79	status=$((status+ret))
80}
81
82# Test: check NSEC in answers
83_check_nsec_nsec3param()
84{
85	dig_with_opts +noquestion @$SERVER "${ZONE}" NSEC3PARAM > "dig.out.test$n.nsec3param.$ZONE" || return 1
86	grep "NSEC3PARAM" "dig.out.test$n.nsec3param.$ZONE" > /dev/null && return 1
87	return 0
88}
89
90_check_nsec_nxdomain()
91{
92	dig_with_opts @$SERVER "nosuchname.${ZONE}" > "dig.out.test$n.nxdomain.$ZONE" || return 1
93	grep "${ZONE}.*IN.*NSEC.*NS.*SOA.*RRSIG.*NSEC.*DNSKEY" "dig.out.test$n.nxdomain.$ZONE" > /dev/null || return 1
94	grep "NSEC3" "dig.out.test$n.nxdomain.$ZONE" > /dev/null && return 1
95	return 0
96}
97
98check_nsec()
99{
100	n=$((n+1))
101	echo_i "check NSEC3PARAM response for zone ${ZONE} ($n)"
102	ret=0
103	retry_quiet 10 _check_nsec_nsec3param || log_error "unexpected NSEC3PARAM in response for zone ${ZONE}"
104	test "$ret" -eq 0 || echo_i "failed"
105	status=$((status+ret))
106
107	n=$((n+1))
108	echo_i "check NXDOMAIN response for zone ${ZONE} ($n)"
109	ret=0
110	retry_quiet 10 _check_nsec_nxdomain || log_error "bad NXDOMAIN response for zone ${ZONE}"
111	test "$ret" -eq 0 || echo_i "failed"
112	status=$((status+ret))
113}
114
115# Test: check NSEC3 parameters in answers
116_check_nsec3_nsec3param()
117{
118	dig_with_opts +noquestion @$SERVER "${ZONE}" NSEC3PARAM > "dig.out.test$n.nsec3param.$ZONE" || return 1
119	grep "${ZONE}.*0.*IN.*NSEC3PARAM.*1.*0.*${ITERATIONS}.*${SALT}" "dig.out.test$n.nsec3param.$ZONE" > /dev/null || return 1
120
121	if [ -z "$SALT" ]; then
122		SALT=`awk '$4 == "NSEC3PARAM" { print $8 }' dig.out.test$n.nsec3param.$ZONE`
123	fi
124	return 0
125}
126
127_check_nsec3_nxdomain()
128{
129	dig_with_opts @$SERVER "nosuchname.${ZONE}" > "dig.out.test$n.nxdomain.$ZONE" || return 1
130	grep ".*\.${ZONE}.*IN.*NSEC3.*1.${FLAGS}.*${ITERATIONS}.*${SALT}" "dig.out.test$n.nxdomain.$ZONE" > /dev/null || return 1
131	return 0
132}
133
134check_nsec3()
135{
136	n=$((n+1))
137	echo_i "check that NSEC3PARAM 1 0 ${ITERATIONS} is published zone ${ZONE} ($n)"
138	ret=0
139	retry_quiet 10 _check_nsec3_nsec3param || log_error "bad NSEC3PARAM response for ${ZONE}"
140	test "$ret" -eq 0 || echo_i "failed"
141	status=$((status+ret))
142
143	n=$((n+1))
144	echo_i "check NXDOMAIN response has correct NSEC3 1 ${FLAGS} ${ITERATIONS} ${SALT} for zone ${ZONE} ($n)"
145	ret=0
146	retry_quiet 10 _check_nsec3_nxdomain || log_error "bad NXDOMAIN response for zone ${ZONE}"
147	test "$ret" -eq 0 || echo_i "failed"
148	status=$((status+ret))
149}
150
151start_time="$(TZ=UTC date +%s)"
152status=0
153n=0
154
155# Zone: nsec-to-nsec3.kasp.
156set_zone_policy "nsec-to-nsec3.kasp" "nsec"
157set_server "ns3" "10.53.0.3"
158echo_i "initial check zone ${ZONE}"
159check_nsec
160dnssec_verify
161
162# Zone: nsec3.kasp.
163set_zone_policy "nsec3.kasp" "nsec3"
164set_nsec3param "0" "5" "8"
165echo_i "initial check zone ${ZONE}"
166check_nsec3
167dnssec_verify
168
169# Zone: nsec3-dynamic.kasp.
170set_zone_policy "nsec3-dynamic.kasp" "nsec3"
171set_nsec3param "0" "5" "8"
172echo_i "initial check zone ${ZONE}"
173check_nsec3
174dnssec_verify
175
176# Zone: nsec3-change.kasp.
177set_zone_policy "nsec3-change.kasp" "nsec3"
178set_nsec3param "0" "5" "8"
179echo_i "initial check zone ${ZONE}"
180check_nsec3
181dnssec_verify
182
183# Zone: nsec3-dynamic-change.kasp.
184set_zone_policy "nsec3-dynamic-change.kasp" "nsec3"
185set_nsec3param "0" "5" "8"
186echo_i "initial check zone ${ZONE}"
187check_nsec3
188dnssec_verify
189
190# Zone: nsec3-to-nsec.kasp.
191set_zone_policy "nsec3-to-nsec.kasp" "nsec3"
192set_nsec3param "0" "5" "8"
193echo_i "initial check zone ${ZONE}"
194check_nsec3
195dnssec_verify
196
197# Zone: nsec3-to-optout.kasp.
198set_zone_policy "nsec3-to-optout.kasp" "nsec3"
199set_nsec3param "0" "5" "8"
200echo_i "initial check zone ${ZONE}"
201check_nsec3
202dnssec_verify
203
204# Zone: nsec3-from-optout.kasp.
205set_zone_policy "nsec3-from-optout.kasp" "optout"
206set_nsec3param "1" "5" "8"
207echo_i "initial check zone ${ZONE}"
208check_nsec3
209dnssec_verify
210
211# Zone: nsec3-other.kasp.
212set_zone_policy "nsec3-other.kasp" "nsec3-other"
213set_nsec3param "1" "11" "0"
214echo_i "initial check zone ${ZONE}"
215check_nsec3
216dnssec_verify
217
218# Reconfig named.
219echo_i "reconfig dnssec-policy to trigger nsec3 rollovers"
220copy_setports ns3/named2.conf.in ns3/named.conf
221rndc_reconfig ns3 10.53.0.3
222
223# Zone: nsec-to-nsec3.kasp. (reconfigured)
224set_zone_policy "nsec-to-nsec3.kasp" "nsec3"
225set_nsec3param "0" "5" "8"
226echo_i "check zone ${ZONE} after reconfig"
227check_nsec3
228dnssec_verify
229
230# Zone: nsec3.kasp. (same)
231set_zone_policy "nsec3.kasp" "nsec3"
232set_nsec3param "0" "5" "8"
233echo_i "check zone ${ZONE} after reconfig"
234check_nsec3
235dnssec_verify
236
237# Zone: nsec3-dyamic.kasp. (same)
238set_zone_policy "nsec3-dynamic.kasp" "nsec3"
239set_nsec3param "0" "5" "8"
240echo_i "check zone ${ZONE} after reconfig"
241check_nsec3
242dnssec_verify
243
244# Zone: nsec3-change.kasp. (reconfigured)
245set_zone_policy "nsec3-change.kasp" "nsec3-other"
246set_nsec3param "1" "11" "0"
247echo_i "check zone ${ZONE} after reconfig"
248check_nsec3
249dnssec_verify
250
251# Zone: nsec3-dynamic-change.kasp. (reconfigured)
252set_zone_policy "nsec3-dynamic-change.kasp" "nsec3-other"
253set_nsec3param "1" "11" "0"
254echo_i "check zone ${ZONE} after reconfig"
255check_nsec3
256dnssec_verify
257
258# Zone: nsec3-to-nsec.kasp. (reconfigured)
259set_zone_policy "nsec3-to-nsec.kasp" "nsec"
260set_nsec3param "1" "11" "0"
261echo_i "check zone ${ZONE} after reconfig"
262check_nsec
263dnssec_verify
264
265# Zone: nsec3-to-optout.kasp. (reconfigured)
266# DISABLED:
267# There is a bug in the nsec3param building code that thinks when the
268# optout bit is changed, the chain already exists. [GL #2216]
269#set_zone_policy "nsec3-to-optout.kasp" "optout"
270#set_nsec3param "1" "5" "8"
271#echo_i "check zone ${ZONE} after reconfig"
272#check_nsec3
273#dnssec_verify
274
275# Zone: nsec3-from-optout.kasp. (reconfigured)
276# DISABLED:
277# There is a bug in the nsec3param building code that thinks when the
278# optout bit is changed, the chain already exists. [GL #2216]
279#set_zone_policy "nsec3-from-optout.kasp" "nsec3"
280#set_nsec3param "0" "5" "8"
281#echo_i "check zone ${ZONE} after reconfig"
282#check_nsec3
283#dnssec_verify
284
285# Zone: nsec3-other.kasp. (same)
286set_zone_policy "nsec3-other.kasp" "nsec3-other"
287set_nsec3param "1" "11" "0"
288echo_i "check zone ${ZONE} after reconfig"
289check_nsec3
290dnssec_verify
291
292# Using rndc signing -nsec3param (should fail)
293set_zone_policy "nsec3-change.kasp" "nsec3-other"
294echo_i "use rndc signing -nsec3param ${ZONE} to change NSEC3 settings"
295rndccmd $SERVER signing -nsec3param 1 1 12 ffff $ZONE > rndc.signing.test$n.$ZONE || log_error "failed to call rndc signing -nsec3param $ZONE"
296grep "zone uses dnssec-policy, use rndc dnssec command instead" rndc.signing.test$n.$ZONE > /dev/null || log_error "rndc signing -nsec3param should fail"
297check_nsec3
298dnssec_verify
299
300# Test NSEC3 and NSEC3PARAM is the same after restart
301set_zone_policy "nsec3.kasp" "nsec3"
302set_nsec3param "0" "5" "8"
303echo_i "check zone ${ZONE} before restart"
304check_nsec3
305dnssec_verify
306
307# Restart named, NSEC3 should stay the same.
308ret=0
309echo "stop ns3"
310$PERL ../stop.pl --use-rndc --port ${CONTROLPORT} nsec3 ${DIR} || ret=1
311test "$ret" -eq 0 || echo_i "failed"
312status=$((status+ret))
313
314ret=0
315echo "start ns3"
316start_server --noclean --restart --port ${PORT} nsec3 ${DIR}
317test "$ret" -eq 0 || echo_i "failed"
318status=$((status+ret))
319
320prevsalt="${SALT}"
321set_zone_policy "nsec3.kasp" "nsec3"
322set_nsec3param "0" "5" "8"
323SALT="${prevsalt}"
324echo_i "check zone ${ZONE} after restart has salt ${SALT}"
325check_nsec3
326dnssec_verify
327
328# Zone: nsec3-fails-to-load.kasp. (should be fixed after reload)
329cp ns3/template.db.in ns3/nsec3-fails-to-load.kasp.db
330rndc_reload ns3 10.53.0.3
331
332set_zone_policy "nsec3-fails-to-load.kasp" "nsec3"
333set_nsec3param "0" "5" "8"
334echo_i "check zone ${ZONE} after reload"
335check_nsec3
336dnssec_verify
337
338echo_i "exit status: $status"
339[ $status -eq 0 ] || exit 1
340