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
14SYSTEMTESTTOP=..
15. $SYSTEMTESTTOP/conf.sh
16
17status=0
18
19pzone=parent.nil
20pfile=parent.db
21
22czone=child.parent.nil
23cfile=child.db
24
25echo_i "generating child's keys"
26# active zsk
27czsk1=`$KEYGEN -q -a rsasha1 -L 30 $czone`
28
29# not yet published or active
30czsk2=`$KEYGEN -q -a rsasha1 -P none -A none $czone`
31
32# published but not active
33czsk3=`$KEYGEN -q -a rsasha1 -A none $czone`
34
35# inactive
36czsk4=`$KEYGEN -q -a rsasha1 -P now-24h -A now-24h -I now $czone`
37
38# active in 12 hours, inactive 12 hours after that...
39czsk5=`$KEYGEN -q -a rsasha1 -P now+12h -A now+12h -I now+24h $czone`
40
41# explicit successor to czk5
42# (suppressing warning about lack of removal date)
43czsk6=`$KEYGEN -q -S $czsk5 -i 6h 2>/dev/null`
44
45# active ksk
46cksk1=`$KEYGEN -q -a rsasha1 -fk -L 30 $czone`
47
48# published but not YET active; will be active in 20 seconds
49cksk2=`$KEYGEN -q -a rsasha1 -fk $czone`
50# $SETTIME moved after other $KEYGENs
51
52echo_i "revoking key"
53# revoking key changes its ID
54cksk3=`$KEYGEN -q -a rsasha1 -fk $czone`
55cksk4=`$REVOKE $cksk3`
56
57echo_i "setting up sync key"
58cksk5=`$KEYGEN -q -a rsasha1 -fk -P now+1mo -A now+1mo -Psync now $czone`
59
60echo_i "and future sync key"
61cksk6=`$KEYGEN -q -a rsasha1 -fk -P now+1mo -A now+1mo -Psync now+1mo $czone`
62
63echo_i "generating parent keys"
64pzsk=`$KEYGEN -q -a rsasha1 $pzone`
65pksk=`$KEYGEN -q -a rsasha1 -fk $pzone`
66
67echo_i "setting child's activation time"
68# using now+30s to fix RT 24561
69$SETTIME -A now+30s $cksk2 > /dev/null
70
71echo_i "signing child zone"
72czoneout=`$SIGNER -Sg -e now+1d -X now+2d -o $czone $cfile`
73
74echo_i "signing parent zone"
75pzoneout=`$SIGNER -Sg -o $pzone $pfile`
76
77czactive=$(keyfile_to_key_id $czsk1)
78czgenerated=$(keyfile_to_key_id $czsk2)
79czpublished=$(keyfile_to_key_id $czsk3)
80czinactive=$(keyfile_to_key_id $czsk4)
81czpredecessor=$(keyfile_to_key_id $czsk5)
82czsuccessor=$(keyfile_to_key_id $czsk6)
83ckactive=$(keyfile_to_key_id $cksk1)
84ckpublished=$(keyfile_to_key_id $cksk2)
85ckprerevoke=$(keyfile_to_key_id $cksk3)
86ckrevoked=$(keyfile_to_key_id $cksk4)
87
88pzid=$(keyfile_to_key_id $pzsk)
89pkid=$(keyfile_to_key_id $pksk)
90
91echo_i "checking dnssec-signzone output matches expectations"
92ret=0
93echo "$pzoneout" | grep 'KSKs: 1 active, 0 stand-by, 0 revoked' > /dev/null || ret=1
94echo "$pzoneout" | grep 'ZSKs: 1 active, 0 stand-by, 0 revoked' > /dev/null || ret=1
95echo "$czoneout" | grep 'KSKs: 1 active, 1 stand-by, 1 revoked' > /dev/null || ret=1
96echo "$czoneout" | grep 'ZSKs: 1 active, 2 stand-by, 0 revoked' > /dev/null || ret=1
97if [ $ret != 0 ]; then
98	echo_i "parent $pzoneout"
99	echo_i "child $czoneout"
100	echo_i "failed";
101fi
102status=`expr $status + $ret`
103
104echo_i "rechecking dnssec-signzone output with -x"
105ret=0
106# use an alternate output file so -x doesn't interfere with later checks
107pzoneout=`$SIGNER -Sxg -o $pzone -f ${pfile}2.signed $pfile`
108czoneout=`$SIGNER -Sxg -e now+1d -X now+2d -o $czone -f ${cfile}2.signed $cfile`
109echo "$pzoneout" | grep 'KSKs: 1 active, 0 stand-by, 0 revoked' > /dev/null || ret=1
110echo "$pzoneout" | grep 'ZSKs: 1 active, 0 present, 0 revoked' > /dev/null || ret=1
111echo "$czoneout" | grep 'KSKs: 1 active, 1 stand-by, 1 revoked' > /dev/null || ret=1
112echo "$czoneout" | grep 'ZSKs: 1 active, 2 present, 0 revoked' > /dev/null || ret=1
113if [ $ret != 0 ]; then
114	echo_i "parent $pzoneout"
115	echo_i "child $czoneout"
116	echo_i "failed";
117fi
118status=`expr $status + $ret`
119
120echo_i "checking parent zone DNSKEY set"
121ret=0
122grep "key id = $pzid" $pfile.signed > /dev/null || {
123	ret=1
124	echo_i "missing expected parent ZSK id = $pzid"
125}
126grep "key id = $pkid" $pfile.signed > /dev/null || {
127	ret=1
128	echo_i "missing expected parent KSK id = $pkid"
129}
130if [ $ret != 0 ]; then echo_i "failed"; fi
131status=`expr $status + $ret`
132
133echo_i "checking parent zone DS records"
134ret=0
135awk '$2 == "DS" {print $3}' $pfile.signed > dsset.out
136grep -w "$ckactive" dsset.out > /dev/null || ret=1
137grep -w "$ckpublished" dsset.out > /dev/null || ret=1
138# revoked key should not be there, hence the &&
139grep -w "$ckprerevoke" dsset.out > /dev/null && ret=1
140grep -w "$ckrevoked" dsset.out > /dev/null && ret=1
141if [ $ret != 0 ]; then echo_i "failed"; fi
142status=`expr $status + $ret`
143
144echo_i "checking child zone DNSKEY set"
145ret=0
146grep "key id = $ckactive\$" $cfile.signed > /dev/null || {
147	ret=1
148	echo_i "missing expected child KSK id = $ckactive"
149}
150grep "key id = $ckpublished\$" $cfile.signed > /dev/null || {
151	ret=1
152	echo_i "missing expected child prepublished KSK id = $ckpublished"
153}
154grep "key id = $ckrevoked\$" $cfile.signed > /dev/null || {
155	ret=1
156	echo_i "missing expected child revoked KSK id = $ckrevoked"
157}
158grep "key id = $czactive\$" $cfile.signed > /dev/null || {
159	ret=1
160	echo_i "missing expected child ZSK id = $czactive"
161}
162grep "key id = $czpublished\$" $cfile.signed > /dev/null || {
163	ret=1
164	echo_i "missing expected child prepublished ZSK id = $czpublished"
165}
166grep "key id = $czinactive\$" $cfile.signed > /dev/null || {
167	ret=1
168	echo_i "missing expected child inactive ZSK id = $czinactive"
169}
170# should not be there, hence the &&
171grep "key id = $ckprerevoke\$" $cfile.signed > /dev/null && {
172	ret=1
173	echo_i "found unexpected child pre-revoke ZSK id = $ckprerevoke"
174}
175grep "key id = $czgenerated\$" $cfile.signed > /dev/null && {
176	ret=1
177	echo_i "found unexpected child generated ZSK id = $czgenerated"
178}
179grep "key id = $czpredecessor\$" $cfile.signed > /dev/null && {
180	echo_i "found unexpected ZSK predecessor id = $czpredecessor (ignored)"
181}
182grep "key id = $czsuccessor\$" $cfile.signed > /dev/null && {
183	echo_i "found unexpected ZSK successor id = $czsuccessor (ignored)"
184}
185#grep "key id = $czpredecessor\$" $cfile.signed > /dev/null && ret=1
186#grep "key id = $czsuccessor\$" $cfile.signed > /dev/null && ret=1
187if [ $ret != 0 ]; then echo_i "failed"; fi
188status=`expr $status + $ret`
189
190echo_i "checking key TTLs are correct"
191ret=0
192grep "${czone}. 30 IN" ${czsk1}.key > /dev/null 2>&1 || ret=1
193grep "${czone}. 30 IN" ${cksk1}.key > /dev/null 2>&1 || ret=1
194grep "${czone}. IN" ${czsk2}.key > /dev/null 2>&1 || ret=1
195$SETTIME -L 45 ${czsk2} > /dev/null
196grep "${czone}. 45 IN" ${czsk2}.key > /dev/null 2>&1 || ret=1
197$SETTIME -L 0 ${czsk2} > /dev/null
198grep "${czone}. IN" ${czsk2}.key > /dev/null 2>&1 || ret=1
199if [ $ret != 0 ]; then echo_i "failed"; fi
200status=`expr $status + $ret`
201
202echo_i "checking key TTLs were imported correctly"
203ret=0
204awk 'BEGIN {r = 0} $2 == "DNSKEY" && $1 != 30 {r = 1} END {exit r}' \
205        ${cfile}.signed || ret=1
206if [ $ret != 0 ]; then echo_i "failed"; fi
207status=`expr $status + $ret`
208
209echo_i "re-signing and checking imported TTLs again"
210ret=0
211$SETTIME -L 15 ${czsk2} > /dev/null
212czoneout=`$SIGNER -Sg -e now+1d -X now+2d -o $czone $cfile`
213awk 'BEGIN {r = 0} $2 == "DNSKEY" && $1 != 15 {r = 1} END {exit r}' \
214        ${cfile}.signed || ret=1
215if [ $ret != 0 ]; then echo_i "failed"; fi
216status=`expr $status + $ret`
217
218# There is some weirdness in Solaris 10 (Generic_120011-14), which
219# is why the next section has all those echo $ret > /dev/null;sync
220# commands
221echo_i "checking child zone signatures"
222ret=0
223# check DNSKEY signatures first
224awk '$2 == "RRSIG" && $3 == "DNSKEY" { getline; print $3 }' $cfile.signed > dnskey.sigs
225sub=0
226grep -w "$ckactive" dnskey.sigs > /dev/null || sub=1
227if [ $sub != 0 ]; then echo_i "missing ckactive $ckactive (dnskey)"; ret=1; fi
228echo $ret > /dev/null
229sync
230sub=0
231grep -w "$ckrevoked" dnskey.sigs > /dev/null || sub=1
232if [ $sub != 0 ]; then echo_i "missing ckrevoke $ckrevoke (dnskey)"; ret=1; fi
233echo $ret > /dev/null
234sync
235sub=0
236grep -w "$czactive" dnskey.sigs > /dev/null || sub=1
237if [ $sub != 0 ]; then echo_i "missing czactive $czactive (dnskey)"; ret=1; fi
238# should not be there:
239echo $ret > /dev/null
240sync
241sub=0
242grep -w "$ckprerevoke" dnskey.sigs > /dev/null && sub=1
243if [ $sub != 0 ]; then echo_i "found ckprerevoke $ckprerevoke (dnskey)"; ret=1; fi
244echo $ret > /dev/null
245sync
246sub=0
247grep -w "$ckpublished" dnskey.sigs > /dev/null && sub=1
248if [ $sub != 0 ]; then echo_i "found ckpublished $ckpublished (dnskey)"; ret=1; fi
249echo $ret > /dev/null
250sync
251sub=0
252grep -w "$czpublished" dnskey.sigs > /dev/null && sub=1
253if [ $sub != 0 ]; then echo_i "found czpublished $czpublished (dnskey)"; ret=1; fi
254echo $ret > /dev/null
255sync
256sub=0
257grep -w "$czinactive" dnskey.sigs > /dev/null && sub=1
258if [ $sub != 0 ]; then echo_i "found czinactive $czinactive (dnskey)"; ret=1; fi
259echo $ret > /dev/null
260sync
261sub=0
262grep -w "$czgenerated" dnskey.sigs > /dev/null && sub=1
263if [ $sub != 0 ]; then echo_i "found czgenerated $czgenerated (dnskey)"; ret=1; fi
264# now check other signatures first
265awk '$2 == "RRSIG" && $3 != "DNSKEY" && $3 != "CDNSKEY" && $3 != "CDS" { getline; print $3 }' $cfile.signed | sort -un > other.sigs
266# should not be there:
267echo $ret > /dev/null
268sync
269sub=0
270grep -w "$ckactive" other.sigs > /dev/null && sub=1
271if [ $sub != 0 ]; then echo_i "found ckactive $ckactive (other)"; ret=1; fi
272echo $ret > /dev/null
273sync
274sub=0
275grep -w "$ckpublished" other.sigs > /dev/null && sub=1
276if [ $sub != 0 ]; then echo_i "found ckpublished $ckpublished (other)"; ret=1; fi
277echo $ret > /dev/null
278sync
279sub=0
280grep -w "$ckprerevoke" other.sigs > /dev/null && sub=1
281if [ $sub != 0 ]; then echo_i "found ckprerevoke $ckprerevoke (other)"; ret=1; fi
282echo $ret > /dev/null
283sync
284sub=0
285grep -w "$ckrevoked" other.sigs > /dev/null && sub=1
286if [ $sub != 0 ]; then echo_i "found ckrevoked $ckrevoked (other)"; ret=1; fi
287echo $ret > /dev/null
288sync
289sub=0
290grep -w "$czpublished" other.sigs > /dev/null && sub=1
291if [ $sub != 0 ]; then echo_i "found czpublished $czpublished (other)"; ret=1; fi
292echo $ret > /dev/null
293sync
294sub=0
295grep -w "$czinactive" other.sigs > /dev/null && sub=1
296if [ $sub != 0 ]; then echo_i "found czinactive $czinactive (other)"; ret=1; fi
297echo $ret > /dev/null
298sync
299sub=0
300grep -w "$czgenerated" other.sigs > /dev/null && sub=1
301if [ $sub != 0 ]; then echo_i "found czgenerated $czgenerated (other)"; ret=1; fi
302echo $ret > /dev/null
303sync
304sub=0
305grep -w "$czpredecessor" other.sigs > /dev/null && sub=1
306if [ $sub != 0 ]; then echo_i "found czpredecessor $czpredecessor (other)"; ret=1; fi
307echo $ret > /dev/null
308sync
309sub=0
310grep -w "$czsuccessor" other.sigs > /dev/null && sub=1
311if [ $sub != 0 ]; then echo_i "found czsuccessor $czsuccessor (other)"; ret=1; fi
312if [ $ret != 0 ]; then
313    sed 's/^/I:dnskey sigs: /' < dnskey.sigs
314    sed 's/^/I:other sigs: /' < other.sigs
315    echo_i "failed";
316fi
317status=`expr $status + $ret`
318
319echo_i "checking RRSIG expiry date correctness"
320dnskey_expiry=`$CHECKZONE -o - $czone $cfile.signed 2> /dev/null |
321              awk '$4 == "RRSIG" && $5 == "DNSKEY" {print $9; exit}' |
322              cut -c1-10`
323soa_expiry=`$CHECKZONE -o - $czone $cfile.signed 2> /dev/null |
324           awk '$4 == "RRSIG" && $5 == "SOA" {print $9; exit}' |
325           cut -c1-10`
326[ $dnskey_expiry -gt $soa_expiry ] || ret=1
327if [ $ret != 0 ]; then echo_i "failed"; fi
328status=`expr $status + $ret`
329
330echo_i "waiting 30 seconds for key activation"
331sleep 30
332echo_i "re-signing child zone"
333czoneout2=`$SIGNER -Sg -o $czone -f $cfile.new $cfile.signed`
334mv $cfile.new $cfile.signed
335
336echo_i "checking dnssec-signzone output matches expectations"
337ret=0
338echo "$czoneout2" | grep 'KSKs: 2 active, 0 stand-by, 1 revoked' > /dev/null || ret=1
339if [ $ret != 0 ]; then echo_i "failed"; fi
340status=`expr $status + $ret`
341
342echo_i "checking child zone signatures again"
343ret=0
344awk '$2 == "RRSIG" && $3 == "DNSKEY" { getline; print $3 }' $cfile.signed > dnskey.sigs
345grep -w "$ckpublished" dnskey.sigs > /dev/null || ret=1
346if [ $ret != 0 ]; then echo_i "failed"; fi
347status=`expr $status + $ret`
348
349echo_i "checking sync record publication"
350ret=0
351awk 'BEGIN { r=1 } $2 == "CDNSKEY" { r=0 } END { exit r }' $cfile.signed || ret=1
352awk 'BEGIN { r=1 } $2 == "CDS" { r=0 } END { exit r }' $cfile.signed || ret=1
353if [ $ret != 0 ]; then echo_i "failed"; fi
354status=`expr $status + $ret`
355
356# this also checks that the future sync record is not yet published
357echo_i "checking sync record deletion"
358ret=0
359$SETTIME -P now -A now -Dsync now ${cksk5} > /dev/null
360$SIGNER -Sg -o $czone -f $cfile.new $cfile.signed > /dev/null
361mv $cfile.new $cfile.signed
362awk 'BEGIN { r=1 } $2 == "CDNSKEY" { r=0 } END { exit r }' $cfile.signed && ret=1
363awk 'BEGIN { r=1 } $2 == "CDS" { r=0 } END { exit r }' $cfile.signed && ret=1
364if [ $ret != 0 ]; then echo_i "failed"; fi
365status=`expr $status + $ret`
366
367echo_i "exit status: $status"
368[ $status -eq 0 ] || exit 1
369