Home | History | Annotate | Line # | Download | only in scripts
      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