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 14 use warnings; 15 use strict; 16 17 use POSIX qw(strftime); 18 my $now = strftime "%Y%m%d%H%M%S", gmtime; 19 20 sub ext8601 ($) { 21 my $d = shift; 22 $d =~ s{(....)(..)(..)(..)(..)(..)} 23 {$1-$2-$3.$4:$5:$6+0000}; 24 return $d; 25 } 26 27 sub 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 48 sub fmtkey ($) { 49 my $k = shift; 50 return sprintf "%16s tag %s", $k->{name}, $k->{id}; 51 } 52 53 sub 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 64 sub 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 76 my $anchor; 77 my $owner = "."; 78 while (<>) { 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 95 for 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 131 exit; 132 133 __END__ 134 135 =head1 NAME 136 137 check5011 - summarize DNSSEC trust anchor status 138 139 =head1 SYNOPSIS 140 141 check5011 <I<managed-keys.bind>> 142 143 =head1 DESCRIPTION 144 145 The BIND managed-keys file contains DNSSEC trust anchors 146 that can be automatically updated according to RFC 5011. The 147 B<check5011> program reads this file and prints a summary of the 148 status of the trust anchors. It fetches the corresponding 149 DNSKEY records using B<dig> and compares them to the trust anchors. 150 151 Each key is printed on a line with its name, its tag, and its 152 algorithm, followed by a summary of its status. 153 154 =over 155 156 =item C<trusted> 157 158 The key is currently trusted. 159 160 =item C<waiting for ...> 161 162 The key is new, and B<named> is waiting for the "add hold-down" period 163 to pass before the key will be trusted. 164 165 =item C<untrusted and to be removed at ...> 166 167 The key was revoked and will be removed at the stated time. 168 169 =item C<no trust anchor> 170 171 The key is present in the DNS but not in the managed-keys file. 172 173 =item C<revoked> 174 175 The key has its revoked flag set. This is printed before the key's 176 trust anchor status which should normally be C<untrusted...> if 177 B<named> has observed the revocation. 178 179 =item C<missing> 180 181 There is no DNSKEY record for this trust anchor. This is printed 182 before the key's trust anchor status. 183 184 =back 185 186 By default the managed keys are stored in a file called 187 F<managed-keys.bind> in B<named>'s working directory. This location 188 can be changed with B<named>'s B<managed-keys-directory> option. If 189 you are using views the file may be named with the SHA256 hash of a 190 view 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 208 dig(1), named(8) 209 210 =cut 211