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