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

978 lines
22 KiB
Perl
Executable File

#!/usr/bin/perl
#
# Copyright 2005-2014 SPARTA, Inc. All rights reserved. See the COPYING
# file distributed with this software for details.
#
#
# genkrf
#
# This script generates a keyrec file.
#
use strict;
use Getopt::Long qw(:config no_ignore_case_always);
use Net::DNS::SEC::Tools::conf;
use Net::DNS::SEC::Tools::defaults;
use Net::DNS::SEC::Tools::keyrec;
use Net::DNS::SEC::Tools::tooloptions;
#
# Version information.
#
my $NAME = "genkrf";
my $VERS = "$NAME version: 2.1.0";
my $DTVERS = "DNSSEC-Tools Version: 2.2.3";
#######################################################################
#
# Constants
#
our $DEF_TOOLSCONF = $Net::DNS::SEC::Tools::conf::CONFFILE;
#
# A hash mapping algorithm numbers to algorithm names.
#
our %algos = (
"001" => "rsa",
"002" => "dh",
"003" => "dsa",
"005" => "rsasha1",
"157" => "hmac_md5"
);
#
# Names of fields from bind-9.3.1/lib/dns/dst_parse.c.
# Private-key-format: v1.2
# These help in finding the size of the key from the .private key file.
#
our %algofields = (
"001" => "Modulus:",
"002" => "Prime(p):",
"003" => "Prime(p):",
"005" => "Modulus:",
"157" => "Key:"
);
#
# Data required for command line options.
#
my $zone; # Name of the zone.
my $kskcur; # Name of current KSK.
my $zskcur; # Name of current ZSK.
my $zskpub; # Name of published ZSK.
my $zonefile; # Name of the zone file.
my $signedfile; # Name of the signed zone file.
my $kskcount; # Count of KSKs to generate.
my $kskdir; # Directory where the KSK resides.
my $zskdir; # Directory where the ZSKs reside.
my $endtime; # Endtime of signature validity.
my $random; # The source of randomness.
my $krfile; # Name of the keyrec file to generate.
my $ksklife; # Lifespan of the KSK.
my $zsklife; # Lifespan of the ZSK.
my $algo; # The encryption algorithm.
my $ksksize; # The KSK size.
my $zsksize; # The ZSK size.
my $zskcount; # Count of ZSKs to generate.
my $verbose; # Verbose-output flag.
#
# Command-line options specifically for this program.
#
my @opts = (
"kskcur=s", # Specify the current KSK.
"zskcur=s", # Specify the current ZSK.
"zskpub=s", # Specify the published ZSK.
"kskdir=s", # Specify the KSK directory.
"zskdir=s", # Specify the ZSK directory.
"Version", # Display the version number.
);
#
# Data from the dnssec-tools.conf file.
#
my $keygen; # The key-generation program.
#
# Standard paths.
#
my $MKDIR = "/bin/mkdir";
my $MV = "/bin/mv";
#
# Lists of KSKs and ZSKs.
#
my @ksklist = (); # List of current KSKs.
my @curlist = (); # List of current ZSKs.
my @publist = (); # List of published ZSKs.
#######################################################################
my $ret; # Return code from main().
$ret = main();
exit($ret);
#-----------------------------------------------------------------------------
# Routine: main()
#
# Purpose: Uses global variables in this file.
#
sub main()
{
my $argc = @ARGV; # Number of command line arguments.
my $errors = 0; # Total error count.
my $retval; # Return value.
my $signset; # Signing-set name prefix.
my $curkey; # Path to current ZSK.
my $pubkey; # Path to published ZSK.
erraction(ERR_EXIT);
#
# Parse the command line options.
#
optsandargs();
#
# Build the signing-set name prefix.
#
$signset = keyrec_signset_prefix($zone);
$signset =~ s/\-$//;
#
# Generate the keyrec file.
#
$retval = keyrec_creat($krfile);
keyrec_read($krfile);
#
# Add the zone keyrec.
#
keyrec_add('zone', $zone, {
'keyrec_type' => "zone",
'zonefile' => $zonefile,
'signedzone' => $signedfile,
'endtime' => $endtime,
'kskcur' => "$signset-00001",
'kskcount' => $kskcount,
'kskdirectory' => $kskdir,
'zskcount' => $zskcount,
'zskdirectory' => $zskdir,
'zskcur' => "$signset-00002",
'zskpub' => "$signset-00003",
'lastset' => "$signset-00003",
'keyrec_signdate' => "",
'keyrec_signsecs' => "",
});
#
# Add the KSK set keyrec.
#
keyrec_add('set', "$signset-00001",
{
'zonename' => $zone,
'set_type' => 'kskcur',
'keys' => "",
'keyrec_setdate' => "",
'keyrec_setsecs' => "",
});
#
# Add the ZSKCUR set keyrec.
#
keyrec_add('set', "$signset-00002",
{
'zonename' => $zone,
'set_type' => 'zskcur',
'keys' => "",
'keyrec_setdate' => "",
'keyrec_setsecs' => "",
});
#
# Add the ZSKPUB set keyrec.
#
keyrec_add('set', "$signset-00003",
{
'zonename' => $zone,
'set_type' => 'zskpub',
'keys' => "",
'keyrec_setdate' => "",
'keyrec_setsecs' => "",
});
#
# Build all our KSKs and add them to the keyrec file.
#
if($kskcur)
{
#
# Create the key and save its name.
#
addksk($kskcur);
push @ksklist, $kskcur;
}
else
{
for(my $ind=0; $ind < $kskcount ; $ind++)
{
my $ksk; # Name of KSK.
#
# Generate our KSK.
#
$ksk = genksk($kskdir);
if(!$ksk)
{
print STDERR "unable to generate KSK.\nPlease check the $DEF_TOOLSCONF file for correctness\n";
exit(1);
}
#
# Add this KSK's keyrec to the keyrec file.
#
addksk($ksk);
#
# Save the name of this key.
#
push @ksklist, $ksk;
}
}
#
# Build all our current ZSKs and add them to the keyrec file.
#
if($zskcur)
{
#
# Create the key and save its name.
#
addzsk("zskcur",$zskcur);
push @curlist, $zskcur;
}
else
{
for(my $ind=0; $ind < $zskcount ; $ind++)
{
#
# Generate our ZSK.
#
$zskcur = genzsk($zskdir);
if(!$zskcur)
{
print STDERR "unable to generate current ZSK.\nPlease check the $DEF_TOOLSCONF file for correctness\n";
exit(1);
}
#
# Add this ZSK's keyrec to the keyrec file.
#
addzsk("zskcur",$zskcur);
#
# Save the name of this key.
#
push @curlist, $zskcur;
}
}
#
# Build all our published ZSKs and add them to the keyrec file.
#
if($zskpub)
{
#
# Create the key and save its name.
#
addzsk("zskpub",$zskpub);
push @publist, $zskpub;
}
else
{
for(my $ind=0; $ind < $zskcount ; $ind++)
{
#
# Generate our ZSK.
#
$zskpub = genzsk($zskdir);
if(!$zskpub)
{
print STDERR "unable to generate published ZSK.\nPlease check the $DEF_TOOLSCONF file for correctness\n";
exit(1);
}
#
# Add this ZSK's keyrec to the keyrec file.
#
addzsk("zskpub",$zskpub);
#
# Save the name of this key.
#
push @publist, $zskpub;
}
}
#
# Add all the keys to their respective signing sets.
#
foreach my $key (@ksklist)
{
keyrec_signset_addkey("$signset-00001",$key);
}
foreach my $key (@curlist)
{
keyrec_signset_addkey("$signset-00002",$key);
}
foreach my $key (@publist)
{
keyrec_signset_addkey("$signset-00003",$key);
}
#
# Write the keyrec file.
#
keyrec_write();
keyrec_close();
print "genkrf: keyrec file $krfile created successfully\n" if($verbose);
return(0);
}
#-----------------------------------------------------------------------------
# Routine: optsandargs()
#
# Purpose: Parse the command line for options and arguments.
#
sub optsandargs
{
my $ropts; # Reference to options hash.
my %opts = (); # Options hash.
#
# Slurp up the options.
#
opts_onerr(1);
$ropts = opts_cmdopts(@opts);
%opts = %$ropts if($ropts != undef);
#
# Get the option values. If these weren't given on the command
# line, they'll be taken from the config file or the defaults.
#
$kskcur = $opts{'kskcur'};
$zskcur = $opts{'zskcur'};
$zskpub = $opts{'zskpub'};
$kskcount = $opts{'kskcount'} || dnssec_tools_default("kskcount");
$kskdir = $opts{'kskdir'} || dnssec_tools_default("kskdir");
$ksklife = $opts{'ksklife'} || dnssec_tools_default("ksklife");
$ksksize = $opts{'ksklength'} || dnssec_tools_default("ksklength");
$zskcount = $opts{'zskcount'} || dnssec_tools_default("zskcount");
$zskdir = $opts{'zskdir'} || dnssec_tools_default("zskdir");
$zsklife = $opts{'zsklife'} || dnssec_tools_default("zsklife");
$zsksize = $opts{'zsklength'} || dnssec_tools_default("zsklength");
$algo = $opts{'algorithm'} || dnssec_tools_default("algorithm");
$endtime = $opts{'endtime'} || dnssec_tools_default("enddate");
$random = $opts{'random'} || dnssec_tools_default("random");
$keygen = $opts{'keygen'} || dnssec_tools_default("bind-keygen");
$verbose = $opts{'verbose'};
version() if(defined($opts{'Version'}));
#
# Give usage message in a few situations.
#
usage() if($#ARGV < 0);
usage() if(defined($opts{'help'}));
#
# Get the zone file and signed filename.
#
$zonefile = $ARGV[0];
if($#ARGV > 0)
{
$signedfile = $ARGV[1];
}
else
{
$signedfile = $zonefile . ".signed";
}
#
# Get the keyrec's zone and strip off a terminal ".".
#
$zone = $opts{'zone'} || $zonefile;
$zone =~ s/^(.*)\.$/$1/;
#
# Set the output filename.
#
$krfile = $opts{'krfile'} || "$zone.krf";
#
# Default to the current directory if the KSK or ZSK directories
# aren't given.
#
$kskdir = "." if(!$kskdir);
$zskdir = "." if(!$zskdir);
#
# Ensure the KSK count is valid.
#
if($kskcount < 1)
{
print STDERR "invalid KSK count: \"$kskcount\"\n";
exit(10);
}
#
# Ensure the ZSK count is valid.
#
if($zskcount < 1)
{
print STDERR "invalid ZSK count: \"$zskcount\"\n";
exit(10);
}
#
# If the verbose flag was given, show all the values.
#
verbose() if($verbose);
}
#-----------------------------------------------------------------------------
# Routine: getkeyalgo()
#
# Purpose: This routine takes the identification string of the key
# generated by dnssec-keygen as a parameter and returns the name
# of the algorithm for that key.
#
# The identification string of the key has the following format:
#
# Knnnn.+aaa+iiiii
#
# where:
#
# nnnn is the key name
# aaa is the numeric representation of the algorithm
# iiiii is the key identifier (or footprint)
#
sub getkeyalgo
{
my $key = shift;
my $algonum = $key;
$algonum =~ s/^[^\+]*\+([^+]*)\+.*$/$1/g;
return($algos{$algonum});
}
#-----------------------------------------------------------------------------
# Routine: getkeysize()
#
# Purpose: This routine takes the identification string of the key
# and an optional key directory as parameters and returns
# the size of the key. If the directory is not specified
# it defaults to the current directory.
#
sub getkeysize
{
my $keytype = shift; # Type of key to create.
my $key = shift; # Name of key.
my $keydir = shift; # Key's directory.
my $algofield;
my $algonum; # Algorithm number from key.
my $keypath;
my $keysize = 0; # Key length.
#
# If the key's size field is set, we'll use it instead of
# calculating things.
#
if($keytype eq "kskcur")
{
return($ksksize) if($ksksize > -1);
}
else
{
return($zsksize) if($zsksize > -1);
}
#
# Get the path to the key.
#
$keydir = "." if(!$keydir);
$keypath = $keydir . "/" . $key . ".private";
#
# Determine the algorithm field.
#
$algonum = $key;
$algonum =~ s/^[^\+]*\+([^+]*)\+.*$/$1/g;
$algofield = $algofields{$algonum};
#
# Open the key's file.
#
open(KF, "< $keypath") or return "";
#
# Get the algorithm line from the key file and calculate the
# key's size.
#
while(<KF>)
{
my $line = $_;
#
# Skip any lines that don't hold the specified algorithm.
#
next if($line !~ /^$algofield/);
#
# Extract the value of the field.
#
$line =~ s/^$algofield(.*)$/$1/;
$line =~ s/\s//g;
#
# The value is encoded in Base64, hence the following
# algorithm for finding the keysize.
#
$keysize = length($line);
$keysize = ($keysize / 4) * 3;
#
# Adjust the keysize a bit.
#
if($line =~ /==$/)
{
$keysize -= 2;
}
elsif($line =~ /=$/)
{
$keysize -= 1;
}
#
# And one more minor tweak...
#
$keysize *= 8;
last;
}
#
# Close the key file and return the calculated key size.
#
close(KF);
return($keysize);
}
#-----------------------------------------------------------------------------
# Routine: genksk()
#
# Purpose: Generate a new Key Signing Key (KSK.)
#
sub genksk
{
my $cmdopts = "-r $random -a $algo -b $ksksize";
my $ksk;
#
# Generate a new key.
#
# print "genksk: <$keygen $cmdopts -n zone -f KSK $zone>\n";
$ksk = `$keygen $cmdopts -n zone -f KSK $zone`;
chomp $ksk;
#
# Put the key files in their place.
#
if($kskdir ne ".")
{
`$MKDIR -p $kskdir && $MV $ksk* $kskdir`;
}
return($ksk || "");
}
#-----------------------------------------------------------------------------
# Routine: genzsk()
#
# Purpose: Generate a new Zone Signing Key (ZSK.)
#
sub genzsk
{
my $cmdopts = "-r $random -a $algo -b $zsksize";
my $zsk;
#
# Generate a new key.
#
# print "genzsk: <$keygen $cmdopts -n zone $zone>\n";
$zsk = `$keygen $cmdopts -n zone $zone`;
chomp $zsk;
#
# Put the key files in their place.
#
if($zskdir ne ".")
{
`$MKDIR -p $zskdir && $MV $zsk* $zskdir`;
}
return($zsk || "");
}
#----------------------------------------------------------------------
# Routine: addksk()
#
# Purpose: Add the KSK's keyrec to the keyrec file.
#
sub addksk
{
my $keyname = shift; # KSK's name.
my $keypath; # Path to KSK.
my $alg = getkeyalgo($keyname); # Key algorithm.
my $len = getkeysize('kskcur',$keyname,$kskdir); # Key length.
#
# Build the path to the KSK.
#
$keypath = "$kskdir/$keyname.key";
#
# Add the KSK keyrec.
#
keyrec_add('key', $keyname, {
'zonename' => $zone,
'keyrec_type' => "kskcur",
'algorithm' => $alg,
'random' => $random,
'ksklength' => $len,
'ksklife' => $ksklife,
'keypath' => "$keypath",
'keyrec_gendate' => "",
'keyrec_gensecs' => "",
});
};
#----------------------------------------------------------------------
# Routine: addzsk()
#
# Purpose: Add the ZSK's keyrec to the keyrec file.
#
sub addzsk
{
my $keytype = shift; # Type of ZSK.
my $keyname = shift; # ZSK's name.
my $keypath; # Path to ZSK.
my $alg = getkeyalgo($keyname); # Key algorithm.
my $len = getkeysize('zsk',$keyname,$zskdir); # Key length.
#
# Build the path to the ZSK.
#
$keypath = "$zskdir/$keyname.key";
#
# Add the ZSK keyrec.
#
keyrec_add('key', $keyname, {
'zonename' => $zone,
'keyrec_type' => "$keytype",
'algorithm' => $alg,
'random' => $random,
'zsklength' => $len,
'zsklife' => $zsklife,
'keypath' => "$keypath",
'keyrec_gendate' => "",
'keyrec_gensecs' => "",
});
};
#----------------------------------------------------------------------
# Routine: version()
#
# Purpose: Print the version number(s) and exit.
#
sub version
{
print STDERR "$VERS\n";
print STDERR "$DTVERS\n";
exit(0);
}
#----------------------------------------------------------------------
# Routine: verbose()
#
# Purpose: Print the argument info.
#
sub verbose
{
print "zonefile - \"$zonefile\"\n";
print "signedfile - \"$signedfile\"\n";
print "zone - \"$zone\"\n";
print "keyrec file - \"$krfile\"\n\n";
print "algorithm - \"$algo\"\n";
print "endtime - \"$endtime\"\n";
print "random - \"$random\"\n\n";
print "kskcur - \"$kskcur\"\n" if($kskcur ne "");
print "kskcount - \"$kskcount\"\n";
print "kskdir - \"$kskdir\"\n";
print "ksksize - \"$ksksize\"\n";
print "ksklife - \"$ksklife\"\n\n";
print "zskcur - \"$zskcur\"\n" if($zskcur ne "");
print "zskpub - \"$zskpub\"\n" if($zskpub ne "");
print "zskcount - \"$zskcount\"\n";
print "zskdir - \"$zskdir\"\n";
print "zsksize - \"$zsksize\"\n";
print "zsklife - \"$zsklife\"\n\n";
}
#-----------------------------------------------------------------------------
# Routine: usage()
#
# Purpose: This routine prints the usage and exits.
#
sub usage
{
print STDERR "usage: genkrf [options] <zone-file> [signed-zone-file]\n";
print STDERR "\n\t<zone-file> name of the zone file\n";
print STDERR "\t[signed-zone-file] name of the signed zone file\n";
print STDERR "\n\toptions:\n";
print STDERR "\t\t-zone ZONE name of the zone\n";
print STDERR "\t\t-krfile KEYREC zone's keyrec file\n";
print STDERR "\t\t-algorithm ALGORITHM encryption algorithm\n";
print STDERR "\t\t-endtime ENDTIME end time of signature\n";
print STDERR "\t\t-random RANDOMDEV source of randomness\n\n";
print STDERR "\t\t-kskcur KSK name of the KSK\n";
print STDERR "\t\t-kskcount COUNT count of KSKs\n";
print STDERR "\t\t-kskdir PATH KSK directory\n";
print STDERR "\t\t-ksklength KEYLEN KSK length\n";
print STDERR "\t\t-ksklife LIFESPAN KSK lifespan\n\n";
print STDERR "\t\t-zskcur ZSKCUR name of the current ZSK\n";
print STDERR "\t\t-zskpub ZSKPUB name of the published ZSK\n";
print STDERR "\t\t-zskcount COUNT count of ZSKs\n";
print STDERR "\t\t-zskdir PATH ZSK directory\n";
print STDERR "\t\t-zsklength KEYLEN ZSK length\n";
print STDERR "\t\t-zsklife LIFESPAN ZSK lifespan\n\n";
print STDERR "\t\t-verbose verbose output\n";
print STDERR "\t\t-Version display version number\n";
print STDERR "\t\t-help help message\n";
exit(0);
}
1;
##############################################################################
#
=pod
=head1 NAME
genkrf - Generate a I<keyrec> file from Key Signing Key (KSK)
and/or Zone Signing Key (ZSK) files
=head1 SYNOPSIS
genkrf [options] <zone-file> [<signed-zone-file>]
=head1 DESCRIPTION
B<genkrf> generates a I<keyrec> file from KSK and/or ZSK files. It
generates new KSK and ZSK keys if needed.
The name of the I<keyrec> file to be generated is given by the B<-krfile>
option. If this option is not specified, B<zone-name.krf> is used as the name
of the I<keyrec> file. If the I<keyrec> file already exists, it will be
overwritten with new I<keyrec> definitions.
The I<zone-file> argument is required. It specifies the name of the zone file
from which the signed zone file was created. The optional I<signed-zone-file>
argument specifies the name of the signed zone file. If it is not given, then
it defaults to B<zone-file.signed>. The signed zone file field is, in effect,
a dummy field as the zone file is not actually signed.
=head1 OPTIONS
B<genkrf> has a number of options that assist in creation of the I<keyrec>
file. These options will be set to the first value found from this search
path:
command line options
DNSSEC-Tools configuration file
DNSSEC-Tools defaults
See I<tooloptions.pm(3)> for more details.
Exceptions to this are given in the option descriptions.
The B<genkrf> options are described below.
=head2 General B<genkrf> Options
=over 4
=item B<-zone zone-name>
This option specifies the name of the zone. If it is not given then
I<zone-file> will be used as the name of the zone.
=item B<-krfile keyrec-file>
This option specifies the name of the I<keyrec> file to be generated.
If it is not given, then B<zone-name.krf> will be used.
=item B<-algorithm algorithm>
This option specifies the algorithm used to generate encryption keys.
=item B<-endtime endtime>
This option specifies the time that the signature on the zone expires,
measured in seconds.
=item B<-random random-device>
Source of randomness used to generate the zone's keys. See the man
page for B<dnssec-signzone> for the valid format of this field.
=item B<-verbose>
Display additional messages during processing. If this option is given at
least once, then a message will be displayed indicating the successful
generation of the I<keyrec> file. If it is given twice, then the values of
all options will also be displayed.
=item B<-Version>
Displays the version information for B<genkrf> and the DNSSEC-Tools package.
=item B<-help>
Display a usage message.
=back
=head2 KSK-related Options
=over 4
=item B<-kskcur KSK-name>
This option specifies the Current KSK's key file being used to sign the zone.
If this option is not given, a new KSK will be created.
=item B<-kskcount KSK-count>
This option specifies the number of KSK keys that will be generated. If this
option is not given, the default given in the DNSSEC-Tools configuration file
will be used.
=item B<-kskdir KSK-directory>
This option specifies the absolute or relative path of the directory
where the KSK resides. If this option is not given, it defaults to
the current directory ".".
=item B<-ksklength KSK-length>
This option specifies the length of the KSK encryption key.
=item B<-ksklife KSK-lifespan>
This option specifies the lifespan of the KSK encryption key. This lifespan
is B<not> inherent to the key itself. It is B<only> used to determine when
the KSK must be rolled over.
=back
=head2 ZSK-related Options
=over 4
=item B<-zskcur ZSK-name>
This option specifies the current ZSK being used to sign the zone.
If this option is not given, a new ZSK will be created.
=item B<-zskpub ZSK-name>
This option specifies the published ZSK for the zone. If this option
is not given, a new ZSK will be created.
=item B<-zskcount ZSK-count>
This option specifies the number of current and published ZSK keys that will
be generated. If this option is not given, the default given in the
DNSSEC-Tools configuration file will be used.
=item B<-zskdir ZSK-directory>
This option specifies the absolute or relative path of the directory
where the ZSKs reside. If this option is not given, it defaults to
the current directory ".".
=item B<-zsklength ZSK-length>
This option specifies the length of the ZSK encryption key.
=item B<-zsklife ZSK-lifespan>
This option specifies the lifespan of the ZSK encryption key. This lifespan
is B<not> inherent to the key itself. It is B<only> used to determine when
the ZSK must be rolled over.
=back
=head1 COPYRIGHT
Copyright 2005-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<dnssec-keygen(8)>,
B<dnssec-signzone(8)>,
B<zonesigner(8)>
B<Net::DNS::SEC::Tools::conf.pm(3)>,
B<Net::DNS::SEC::Tools::defaults.pm(3)>,
B<Net::DNS::SEC::Tools::keyrec.pm(3)>
B<conf(5)>,
B<keyrec(5)>
=cut