1#!/usr/bin/perl
2
3use warnings;
4use strict;
5
6use POSIX qw(strftime);
7my $now = strftime "%Y%m%d%H%M%S", gmtime;
8
9sub ext8601 ($) {
10	my $d = shift;
11	$d =~ s{(....)(..)(..)(..)(..)(..)}
12	       {$1-$2-$3.$4:$5:$6+0000};
13	return $d;
14}
15
16sub getkey ($$) {
17	my $h = shift;
18	my $k = shift;
19	m{\s+(\d+)\s+(\d+)\s+(\d+)\s+[(]\s*$};
20	$k->{flags}     = $1;
21	$k->{protocol}  = $2;
22	$k->{algorithm} = $3;
23	my $data = "(";
24	while (<$h>) {
25		s{^\s+}{};
26		s{\s+$}{};
27		last if m{^[)]};
28		$data .= $_;
29	}
30	m{ alg = (\S+); key id = (\d+)};
31	$k->{alg}  = $1;
32	$k->{id}   = $2;
33	$k->{data} = $data;
34	return $k;
35}
36
37sub fmtkey ($) {
38	my $k = shift;
39	return sprintf "%16s tag %s", $k->{name}, $k->{id};
40}
41
42sub printstatus ($) {
43	my $a = shift;
44	if ($a->{removehd} ne "19700101000000") {
45		printf " untrusted and to be removed at %s\n", ext8601 $a->{removehd};
46	} elsif ($a->{addhd} lt $now) {
47		printf " trusted\n";
48	} else {
49		printf " waiting for %s\n", ext8601 $a->{addhd};
50	}
51}
52
53sub digkeys ($) {
54	my $name = shift;
55	my $keys;
56	open my $d, "-|", qw{dig +multiline DNSKEY}, $name;
57	while (<$d>) {
58		next unless m{^([a-z0-9.-]*)\s+\d+\s+IN\s+DNSKEY\s+};
59		next unless $name eq $1;
60		push @$keys, getkey $d, { name => $name };
61	}
62	return $keys;
63}
64
65my $anchor;
66my $owner = ".";
67while (<>) {
68	next unless m{^([a-z0-9.-]*)\s+KEYDATA\s+(\d+)\s+(\d+)\s+(\d+)\s+};
69	my $k = getkey *ARGV, {
70		name     => $1,
71		refresh  => $2,
72		addhd    => $3,
73		removehd => $4,
74	};
75	if ($k->{name} eq "") {
76		$k->{name} = $owner;
77	} else {
78		$owner = $k->{name};
79	}
80	$k->{name} =~ s{[.]*$}{.};
81	push @{$anchor->{$k->{name}}}, $k;
82}
83
84for my $name (keys %$anchor) {
85	my $keys = digkeys $name;
86	my $anchors = $anchor->{$name};
87	for my $k (@$keys) {
88		if ($k->{flags} & 1) {
89			printf "%s %s", fmtkey $k, $k->{alg};
90		} else {
91			# ZSK - skipping
92			next;
93		}
94		if ($k->{flags} & 512) {
95			print " revoked;";
96		}
97		my $a;
98		for my $t (@$anchors) {
99			if ($t->{data} eq $k->{data} and
100			    $t->{protocol} eq $k->{protocol} and
101			    $t->{algorithm} eq $k->{algorithm}) {
102				$t->{matched} = 1;
103				$a = $t;
104				last;
105			}
106		}
107		if (not defined $a) {
108			print " no trust anchor\n";
109			next;
110		}
111		printstatus $a;
112	}
113	for my $a (@$anchors) {
114		next if $a->{matched};
115		printf "%s %s missing;", fmtkey $a, $a->{alg};
116		printstatus $a;
117	}
118}
119
120exit;
121
122__END__
123
124=head1 NAME
125
126check5011 - summarize DNSSEC trust anchor status
127
128=head1 SYNOPSIS
129
130check5011 <I<managed-keys.bind>>
131
132=head1 DESCRIPTION
133
134The BIND managed-keys file contains DNSSEC trust anchors
135that can be automatically updated according to RFC 5011. The
136B<check5011> program reads this file and prints a summary of the
137status of the trust anchors. It fetches the corresponding
138DNSKEY records using B<dig> and compares them to the trust anchors.
139
140Each key is printed on a line with its name, its tag, and its
141algorithm, followed by a summary of its status.
142
143=over
144
145=item C<trusted>
146
147The key is currently trusted.
148
149=item C<waiting for ...>
150
151The key is new, and B<named> is waiting for the "add hold-down" period
152to pass before the key will be trusted.
153
154=item C<untrusted and to be removed at ...>
155
156The key was revoked and will be removed at the stated time.
157
158=item C<no trust anchor>
159
160The key is present in the DNS but not in the managed-keys file.
161
162=item C<revoked>
163
164The key has its revoked flag set. This is printed before the key's
165trust anchor status which should normally be C<untrusted...> if
166B<named> has observed the revocation.
167
168=item C<missing>
169
170There is no DNSKEY record for this trust anchor. This is printed
171before the key's trust anchor status.
172
173=back
174
175By default the managed keys are stored in a file called
176F<managed-keys.bind> in B<named>'s working directory. This location
177can be changed with B<named>'s B<managed-keys-directory> option. If
178you are using views the file may be named with the SHA256 hash of a
179view name with a F<.mkeys> extension added.
180
181=head1 AUTHOR
182
183=over
184
185=item Written by Tony Finch <fanf2@cam.ac.uk> <dot@dotat.at>
186
187=item at the University of Cambridge Computing Service.
188
189=item You may do anything with this. It has no warranty.
190
191=item L<http://creativecommons.org/publicdomain/zero/1.0/>
192
193=back
194
195=head1 SEE ALSO
196
197dig(1), named(8)
198
199=cut
200