#!/usr/bin/perl # Copyright (C) Internet Systems Consortium, Inc. ("ISC") # # SPDX-License-Identifier: MPL-2.0 # # This Source Code Form is subject to the terms of the Mozilla Public # License, v. 2.0. If a copy of the MPL was not distributed with this # file, you can obtain one at https://mozilla.org/MPL/2.0/. # # See the COPYRIGHT file distributed with this work for additional # information regarding copyright ownership. use warnings; use strict; use POSIX qw(strftime); my $now = strftime "%Y%m%d%H%M%S", gmtime; sub ext8601 ($) { my $d = shift; $d =~ s{(....)(..)(..)(..)(..)(..)} {$1-$2-$3.$4:$5:$6+0000}; return $d; } sub getkey ($$) { my $h = shift; my $k = shift; m{\s+(\d+)\s+(\d+)\s+(\d+)\s+[(]\s*$}; $k->{flags} = $1; $k->{protocol} = $2; $k->{algorithm} = $3; my $data = "("; while (<$h>) { s{^\s+}{}; s{\s+$}{}; last if m{^[)]}; $data .= $_; } m{ alg = (\S+)\s*; key id = (\d+)}; $k->{alg} = $1; $k->{id} = $2; $k->{data} = $data; return $k; } sub fmtkey ($) { my $k = shift; return sprintf "%16s tag %s", $k->{name}, $k->{id}; } sub printstatus ($) { my $a = shift; if ($a->{removehd} ne "19700101000000") { printf " untrusted and to be removed at %s\n", ext8601 $a->{removehd}; } elsif ($a->{addhd} le $now) { printf " trusted\n"; } else { printf " waiting for %s\n", ext8601 $a->{addhd}; } } sub digkeys ($) { my $name = shift; my $keys; open my $d, "-|", qw{dig +multiline DNSKEY}, $name; while (<$d>) { next unless m{^([a-z0-9.-]*)\s+\d+\s+IN\s+DNSKEY\s+}; next unless $name eq $1; push @$keys, getkey $d, { name => $name }; } return $keys; } my $anchor; my $owner = "."; while (<>) { next unless m{^([a-z0-9.-]*)\s+KEYDATA\s+(\d+)\s+(\d+)\s+(\d+)\s+}; my $k = getkey *ARGV, { name => $1, refresh => $2, addhd => $3, removehd => $4, }; if ($k->{name} eq "") { $k->{name} = $owner; } else { $owner = $k->{name}; } $k->{name} =~ s{[.]*$}{.}; push @{$anchor->{$k->{name}}}, $k; } for my $name (keys %$anchor) { my $keys = digkeys $name; my $anchors = $anchor->{$name}; for my $k (@$keys) { if ($k->{flags} & 1) { printf "%s %s", fmtkey $k, $k->{alg}; } else { # ZSK - skipping next; } if ($k->{flags} & 512) { print " revoked;"; } my $a; for my $t (@$anchors) { if ($t->{data} eq $k->{data} and $t->{protocol} eq $k->{protocol} and $t->{algorithm} eq $k->{algorithm}) { $t->{matched} = 1; $a = $t; last; } } if (not defined $a) { print " no trust anchor\n"; next; } printstatus $a; } for my $a (@$anchors) { next if $a->{matched}; printf "%s %s missing;", fmtkey $a, $a->{alg}; printstatus $a; } } exit; __END__ =head1 NAME check5011 - summarize DNSSEC trust anchor status =head1 SYNOPSIS check5011 > =head1 DESCRIPTION The BIND managed-keys file contains DNSSEC trust anchors that can be automatically updated according to RFC 5011. The B program reads this file and prints a summary of the status of the trust anchors. It fetches the corresponding DNSKEY records using B and compares them to the trust anchors. Each key is printed on a line with its name, its tag, and its algorithm, followed by a summary of its status. =over =item C The key is currently trusted. =item C The key is new, and B is waiting for the "add hold-down" period to pass before the key will be trusted. =item C The key was revoked and will be removed at the stated time. =item C The key is present in the DNS but not in the managed-keys file. =item C The key has its revoked flag set. This is printed before the key's trust anchor status which should normally be C if B has observed the revocation. =item C There is no DNSKEY record for this trust anchor. This is printed before the key's trust anchor status. =back By default the managed keys are stored in a file called F in B's working directory. This location can be changed with B's B option. If you are using views the file may be named with the SHA256 hash of a view name with a F<.mkeys> extension added. =head1 AUTHOR =over =item Written by Tony Finch =item at the University of Cambridge Computing Service. =item You may do anything with this. It has no warranty. =item L =back =head1 SEE ALSO dig(1), named(8) =cut