Files
2018-11-13 23:20:57 +00:00

627 lines
12 KiB
Perl
Executable File

#!/usr/bin/perl
#
# Copyright 2007-2014 SPARTA, Inc. All rights reserved. See the COPYING
# file distributed with this software for details.
#
#
# keyarch
#
# This script archives old KSK and ZSK keys.
#
#
# If we're executing from a packed environment, make sure we've got the
# library path for the packed modules.
#
BEGIN
{
if($ENV{'PAR_TEMP'})
{
unshift @INC, ("$ENV{'PAR_TEMP'}/inc/lib");
}
}
use strict;
use Getopt::Long qw(:config no_ignore_case_always);
use Net::DNS::SEC::Tools::conf;
use Net::DNS::SEC::Tools::dnssectools;
use Net::DNS::SEC::Tools::keyrec;
use Net::DNS::SEC::Tools::rollrec;
#
# Version information.
#
my $NAME = "keyarch";
my $VERS = "$NAME version: 2.1.0";
my $DTVERS = "DNSSEC-Tools Version: 2.2.3";
##########################################
#
# Data required for command line options.
#
my $fnarg; # Rollrec file to be managed.
my %dtconf = (); # DNSSEC-Tools config file values.
my %opts = (); # Filled option array.
my @opts =
(
"zone=s", # Zone to archive.
"kskonly", # Only archives KSKs.
"zskonly", # Only archives ZSKs.
"dtconfig=s", # Execution-specific config file.
"help", # Give a usage message and exit.
"quiet", # Quiet output.
"verbose", # Verbose output.
"Version", # Display the version number.
);
#
# Flag values for the various options. Variable/option connection should
# be obvious.
#
my $zone; # Zone to archive.
my $kskonly = 1; # KSK-only flag.
my $zskonly = 1; # ZSK-only flag.
my $verbose = 0; # Verbose option.
my $quiet = 0; # Quiet option.
#
# Count of archived keys.
#
my $keycount = 0;
#
# Command paths.
#
my $MV = "/bin/mv";
#######################################################################
#
# Do Everything.
#
my $ret = main();
exit($ret);
#-----------------------------------------------------------------------------
# Routine: main()
#
# Purpose: Do Everything.
#
sub main
{
my $ftype; # Type of file argument.
#
# Set up how to handle module errors.
#
erraction(ERR_MSG);
#
# Use a local config file if we're running as part of a packed
# configuration.
#
if(runpacked())
{
setconffile("$ENV{'PAR_TEMP'}/inc/dnssec-tools.conf");
}
#
# Check our options and arguments.
#
optsandargs();
#
# Ensure we have a valid file argument.
#
$ftype = dt_filetype($fnarg);
if(($ftype eq "mixed") || ($ftype eq "unknown"))
{
print STDERR "file argument must be either a keyrec file OR a rollrec file\n";
exit(-2);
}
#
# If we were given a rollrec file, we'll handle a single zone (if
# -zone was given) or all the file's zones (if -zone wasn't given.)
#
# If we were given a keyrec file, we'll handle a single zone (if
# -zone was given) or all the file's zones (if -zone wasn't given.)
#
if($ftype eq "rollrec")
{
#
# Read the rollrec file.
#
rollrec_read($fnarg);
#
# Check the zone (if specified) or the whole rollrec file.
#
if(defined($zone))
{
chkzone($zone,0);
}
else
{
#
# Check all the file's zones.
#
foreach my $rrn (sort(rollrec_names()))
{
chkzone($rrn,0);
}
}
rollrec_close();
}
else
{
#
# Read the keyrec file.
#
keyrec_read($fnarg);
#
# Check the zone (if specified) or the whole keyrec file.
#
if(defined($zone))
{
chkzone($zone,1);
}
else
{
#
# Check each zone in the keyrec file.
#
foreach my $krn (sort(keyrec_names()))
{
my $kt; # Keyrec's type.
$kt = keyrec_recval($krn,'keyrec_type');
next if($kt ne 'zone');
chkzone($krn,1);
}
}
}
#
# Close up shop.
#
vprint("$keycount keys archived");
return($keycount);
}
#-----------------------------------------------------------------------------
# Routine: chkzone()
#
# Purpose: Check this zone for obsolete signing sets. If we find
# any of the requested types, its keys will be moved to the
# proper archive directory.
#
sub chkzone
{
my $zone = shift; # Zone to check.
my $krfflag = shift; # Keyrec-read flag.
my $krf; # Zone's keyrec file.
my $archdir; # Zone's archive dir.
my $saved = 0; # Saved-keys flag.
#
# Read the zone's keyrec file.
#
if(!$krfflag)
{
$krf = rollrec_recval($zone,'keyrec');
keyrec_read($krf);
}
#
# Get the zone's archive directory.
#
# This check is performed here (instead of with other options)
# so that each zone can have its own personal archive directory.
#
$archdir = keyrec_recval($zone,'archivedir') || $dtconf{'archivedir'};
return if(!checkdir($zone,$archdir));
vprint("archive directory: $archdir\t\t($zone)");
#
# Check this zone for obsolete signing sets. If we find any of
# the requested types, we'll archive its keys.
#
foreach my $krn (sort(keyrec_names()))
{
my $keytype; # Key's type.
my $keyprv; # Private key file.
my $keypub; # Public key file.
$keytype = keyrec_recval($krn,'keyrec_type');
#
# Skip non-obsolete and non-revoked keys.
#
next if($keytype !~ /obs/);
#
# Skip KSKs if we're only archiving ZSKs.
#
next if(($keytype =~ /ksk/) && !$kskonly);
#
# Skip ZSKs if we're only archiving KSKs.
#
next if(($keytype =~ /zsk/) && !$zskonly);
#
# Build the key file names.
#
$keyprv = "$krn.private";
$keypub = "$krn.key";
#
# Save the keys.
#
archit($zone,$krn,$keyprv,$archdir,0);
archit($zone,$krn,$keypub,$archdir,1);
$saved++;
}
#
# Close up the keyrec file.
#
keyrec_write() if($saved);
keyrec_close();
}
#-----------------------------------------------------------------------------
# Routine: archit()
#
# Purpose: Archive the actual key file.
#
sub archit
{
my $zone = shift; # Key's zone.
my $keyname = shift; # Key's name
my $keyfile = shift; # Key to archive.
my $archdir = shift; # Archive directory.
my $pubflag = shift; # Public-key flag.
my $kronos = time; # Timestamp.
my $newname; # New key path.
#
# Go home if the key file doesn't exist.
#
return if(!-e $keyfile);
#
# Build the new name.
#
$newname = "$archdir/$kronos.$keyfile";
#
# Move the key and maybe give a message.
#
system("$MV $keyfile $newname");
if($verbose)
{
print("archived $keyfile\t\t($zone)\n");
}
else
{
nqprint("archived $keyfile");
}
#
# If this is a public key, we'll reset the key's path in the keyrec.
#
keyrec_setval('key',$keyname,'keypath',$newname) if($pubflag);
#
# Bump our count of archived keys.
#
$keycount++;
}
#-----------------------------------------------------------------------------
# Routine: optsandargs()
#
# Purpose: Parse our options and arguments.
#
sub optsandargs
{
my $argc = @ARGV; # Number of arguments.
my $dir; # Execution directory.
#
# Check our options.
#
GetOptions(\%opts,@opts) || usage();
$verbose = $opts{'verbose'};
$quiet = $opts{'quiet'};
$zone = $opts{'zone'};
$kskonly = $opts{'kskonly'};
$zskonly = $opts{'zskonly'};
#
# Show the usage or version number if requested.
#
usage() if(defined($opts{'help'}));
version() if(defined($opts{'version'}));
#
# Check for a rollrec file name.
#
$fnarg = $ARGV[0] || rollrec_default();
if(($fnarg eq "") || !defined($fnarg))
{
print STDERR "no rollrec file specified\n";
exit(-1);
}
#
# Ensure we weren't given both -quiet and -verbose.
#
if($quiet && $verbose)
{
print STDERR "-quiet and -verbose are mutually exclusive\n";
exit(-1);
}
#
# Ensure we weren't given both -kskonly and -zskonly.
#
if($kskonly && $zskonly)
{
print STDERR "-kskonly and -zskonly are mutually exclusive\n";
exit(-1);
}
#
# Ensure we weren't given both -kskonly and -zskonly.
#
$zskonly = 0 if($kskonly);
$kskonly = 0 if($zskonly);
#
# If -kskonly and -zskonly weren't given, turn 'em both on.
#
if(!$kskonly && !$zskonly)
{
$kskonly = 1;
$zskonly = 1;
}
#
# If there's a -dtconfig command line option, we'll use that,
# if we're running packed.
#
if(exists($opts{'dtconfig'}))
{
setconffile($opts{'dtconfig'});
}
#
# Check for a rollrec file name.
#
%dtconf = parseconfig();
}
#----------------------------------------------------------------------
# Routine: checkdir()
#
# Purpose: Ensures archive directory exists and is a writable directory.
#
sub checkdir
{
my $zone = shift; # Zone name.
my $archdir = shift; # Zone's archive directory.
#
# Check for directory existence.
#
if(!-e $archdir)
{
print STDERR "$zone: archive directory \"$archdir\" does not exist\n";
return(0);
}
#
# Check that the directory is really a directory.
#
if(!-d $archdir)
{
print STDERR "$zone: archive directory \"$archdir\" is not a directory\n";
return(0);
}
#
# Check that the directory is writable.
#
if(!-w $archdir)
{
print STDERR "$zone: archive directory \"$archdir\" is not writable\n";
return(0);
}
return(1);
}
#----------------------------------------------------------------------
# Routine: vprint()
#
# Purpose: Verbose printing.
#
sub vprint
{
my $str = shift;
return if(!$verbose);
print "$str\n";
}
#----------------------------------------------------------------------
# Routine: nqprint()
#
# Purpose: Non-quiet printing.
#
sub nqprint
{
my $str = shift;
return if($quiet);
print "$str\n";
}
#----------------------------------------------------------------------
# Routine: version()
#
# Purpose: Print the version number(s) and exit.
#
sub version
{
print STDERR "$VERS\n";
print STDERR "$DTVERS\n";
exit(0);
}
#-----------------------------------------------------------------------------
# Routine: usage()
#
# Purpose: Print a usage message and exit.
#
sub usage
{
print STDERR "usage: keyarch [options] <keyrec-file | rollrec-file>\n";
print STDERR "\toptions:\n";
print STDERR "\t\t-zone <zonename>\n";
print STDERR "\t\t-kskonly\n";
print STDERR "\t\t-zskonly\n";
print STDERR "\t\t-dtconfig <config_file>\n";
print STDERR "\t\t-quiet\n";
print STDERR "\t\t-verbose\n";
print STDERR "\t\t-Version\n";
print STDERR "\t\t-help\n";
exit(0);
}
1;
##############################################################################
#
=pod
=head1 NAME
keyarch - DNSSEC-Tools daemon to archive old KSK and ZSK keys
=head1 SYNOPSIS
keyarch [options] <keyrec_file | rollrec_file>
=head1 DESCRIPTION
The B<keyarch> program archives old KSK and ZSK keys. Keys are considered old
if they are revoked or obsolete. Keys marked as either I<kskrev> or I<zskrev>
are revoked; keys marked as either I<kskobs> or I<zskobs> are obsolete.
Archived keys are prefixed with the seconds-since-epoch as a means of
distinguishing a zone's keys that have the same five digit number.
If the required file argument is a I<keyrec> file, then expired keys listed
in that file are archived. If the file argument is a I<rollrec> file, the
I<keyrec> files of the zones in that file are checked for expired keys.
If the B<-zone> option is given, then only revoked and obsolete keys belonging
to the specified zone will be archived.
The archive directory is either zone-specific (listed in the zone's I<keyrec>
record in the zone's I<keyrec> file) or the default archive directory given
in the DNSSEC-Tools configuration file.
The count of archived keys is given as the program's exit code. Error exit
codes are negative.
=head1 OPTIONS
The following options are recognized:
=over 4
=item B<-zone zone_file>
Name of the zone whose KSKs will be archived. If this is not given, then
all the zones defined in the I<rollrec> file will be checked.
=item B<-kskonly>
Only archive KSK keys.
=item B<-zskonly>
Only archive ZSK keys.
=item B<-dtconfig config_file>
Name of an alternate DNSSEC-Tools configuration file to be processed.
If specified, this configuration file is used I<in place> of the normal
DNSSEC-Tools configuration file B<not> in addition to it. Also, it will be
handled prior to I<keyrec> files, I<rollrec> files, and command-line options.
=item B<-quiet>
No output will be given.
=item B<-verbose>
Verbose output will be given.
=item B<-help>
Display a usage message.
=item B<-Version>
Displays the version information for B<keyarch> and the DNSSEC-Tools package.
=back
=head1 EXIT VALUES
On success, B<keyarch>'s exit code is the number of keys archived.
B<keyarch> has a 0 exit code if the help message is given.
B<keyarch> has a negative exit code if an error is encountered.
=head1 COPYRIGHT
Copyright 2007-2014 SPARTA, Inc. All rights reserved.
See the COPYING file included with the DNSSEC-Tools package for details.
=head1 AUTHOR
Wayne Morrison, tewok@tislabs.com
=head1 SEE ALSO
B<rollerd(8)>,
B<zonesigner(8)>
B<Net::DNS::SEC::Tools::conf.pm(3)>,
B<Net::DNS::SEC::Tools::dnssectools.pm(3)>,
B<Net::DNS::SEC::Tools::defaults.pm(3)>,
B<Net::DNS::SEC::Tools::keyrec.pm(3)>,
B<Net::DNS::SEC::Tools::rollrec.pm(3)>
B<keyrec(5)>,
B<rollrec(5)>
=cut