1#!@PERL@
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 http://mozilla.org/MPL/2.0/.
8#
9# See the COPYRIGHT file distributed with this work for additional
10# information regarding copyright ownership.
11
12use warnings;
13use FileHandle;
14use IPC::Open2;
15use POSIX qw/strftime/;
16
17#
18# We only compare keyid / DNSSEC algorithm pairs.  If this succeeds then
19# the crypto will likely succeed.  If it fails then the crypto will definitely
20# fail.
21#
22$prefix = "@prefix@";
23$dig = "$prefix/bin/dig +cd +dnssec +noall +answer";
24$dsfromkey = "$prefix/sbin/dnssec-dsfromkey -1 -A -f /dev/stdin";
25
26# Get "now" in a RRSIG datestamp format.
27$now = strftime "%Y%m%d%H%M%S", gmtime;
28
29foreach $zone (@ARGV) {
30	my %algorithms = ();
31	my %dnskeygood = ();
32	my %dnskeyalg = ();
33	my %dnskey = ();
34	my %dsgood = ();
35	my %ds = ();
36
37	# Read the DS records and extract the key id, algorithm pairs
38	open(DS, "$dig -t DS -q $zone|") || die("dig DS failed");
39	while(<DS>) {
40		@words = split;
41		if ($words[3] eq "RRSIG" && $words[4] eq "DS") {
42			next if ($words[8] >= $now && $words[9] <= $now);
43			print "BAD SIG DATES: $_";
44		}
45		next if ($words[3] ne "DS");
46		$ds{"$words[4] $words[5]"} = 1;
47		$algorithms{"$words[5]"} = 1;
48	}
49	close(DS);
50
51	# Read the RRSIG(DNSKEY) records and extract the key id,
52	# algorithm pairs.  Set good if we have a match against the DS
53	# records.  DNSKEY records should be before the RRSIG records.
54	open(DNSKEY, "$dig -t DNSKEY -q $zone|") || die("dig DNSKEY failed");
55	while (<DNSKEY>) {
56		@words = split;
57		if ($words[3] eq "DNSKEY") {
58			$dnskeyalg{"$words[6]"} = 1;
59			next if (! -e "/dev/stdin");
60			# get the key id ($dswords[3]).
61			$pid = open2(*Reader, *Writer, "$dsfromkey $zone");
62			die("dsfromkey failed") if ($pid == -1);
63			print Writer "$_";
64			close(Writer);
65			$line = <Reader>;
66			close(Reader);
67			@dswords = split /\s/, $line;
68			$dnskey{"$dswords[3] $dswords[4]"} = 1;
69			next;
70		}
71		next if ($words[3] ne "RRSIG" || $words[4] ne "DNSKEY");
72		if ($words[8] >= $now && $words[9] <= $now) {
73			# If we don't have /dev/stdin then just check for the
74			# RRSIG otherwise check for both the DNSKEY and
75			# RRSIG.
76			$dsgood{"$words[5]"} = 1
77				if (! -e "/dev/stdin" &&
78				    exists($ds{"$words[10] $words[5]"}));
79			$dsgood{"$words[5]"} = 1
80				if (exists($ds{"$words[10] $words[5]"}) &&
81				    exists($dnskey{"$words[10] $words[5]"}));
82			$dnskeygood{"$words[5]"} = 1
83				if (! -e "/dev/stdin");
84			$dnskeygood{"$words[5]"} = 1
85				if (exists($dnskey{"$words[10] $words[5]"}));
86		} else {
87			$dnskeygood{"$words[5]"} = 1;
88			print "BAD SIG DATES: $_";
89		}
90	}
91	close(DNSKEY);
92
93	# Do we have signatures for all DNSKEY algorithms?
94	foreach $alg ( keys %dnskeyalg ) {
95		print "Missing $zone DNSKEY RRSIG for algorithm $alg\n"
96			if (!exists($dnskeygood{$alg}));
97	}
98
99	# Do we have a matching self signed DNSKEY for all DNSSEC algorithms
100	# in the DS records.
101	$count = 0;
102	foreach $alg ( keys %algorithms ) {
103		if (exists($dsgood{$alg})) {
104			print "$zone algorithm $alg good " .
105			      "(found DS / self signed DNSKEY pair)\n";
106		} else {
107			print "$zone algorithm $alg bad " .
108			      "(no DS / self signed DNSKEY pair found)\n";
109		}
110		$count++;
111	}
112	print "$zone has no secure delegation records\n"
113		if (! $count);
114}
115