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