sbin/dnssec-keytool
changeset 131 d8fa60488868
parent 113 30bd047cd057
child 137 69856eb1e826
equal deleted inserted replaced
130:5578cb7933c1 131:d8fa60488868
       
     1 #! /usr/bin/perl
       
     2 
       
     3 use v5.10;
       
     4 use warnings;
       
     5 use strict;
       
     6 use File::Temp;
       
     7 use Getopt::Long;
       
     8 use Pod::Usage;
       
     9 use File::Basename;
       
    10 use Net::LibIDN qw(:all);
       
    11 use if $ENV{DEBUG} => "Smart::Comments";
       
    12 use DNStools::Config qw(get_config);
       
    13 
       
    14 my $ME = basename $0;
       
    15 
       
    16 sub rm_keys($@);
       
    17 sub check_zone($@);
       
    18 sub create_ksk($@);
       
    19 sub create_zsk(@);
       
    20 sub post_create($@);
       
    21 
       
    22 my $CHARSET = "UTF-8";
       
    23 my %cf;
       
    24 
       
    25 MAIN: {
       
    26 
       
    27     %cf = get_config();
       
    28     my $cmd;
       
    29 
       
    30     system("command -v dnssec-keygen &>/dev/null");
       
    31     die "$ME: command 'dnssec-keygen' not found in $ENV{PATH}\n" if $?;
       
    32 
       
    33     GetOptions(
       
    34         "zsk" => sub { push @$cmd => "zsk" },
       
    35         "ksk" => sub { push @$cmd => "ksk" },
       
    36         "rm"  => sub { push @$cmd => "rm" },
       
    37         "check" => sub { $cmd = "check" },
       
    38         "h|help" => sub { pod2usage(-exit => 0, -verbose => 1) },
       
    39         "m|man"  => sub {
       
    40             pod2usage(
       
    41                 -exit      => 0,
       
    42                 # "system('perldoc -V &>/dev/null')" appears shorter, but may not
       
    43                 # do what you expect ( it still returns 0 on debian squeeze with
       
    44                 # dash as system shell even if cannot find the command in $PATH)
       
    45                 -noperldoc => system('perldoc -V >/dev/null 2>&1'),
       
    46                 -verbose   => 2
       
    47             );
       
    48         },
       
    49       )
       
    50       and @ARGV
       
    51       and @$cmd == 1
       
    52       and $cmd = $cmd->[0]
       
    53       or pod2usage;
       
    54 
       
    55     # checks the zones in argv if they're managed ones
       
    56     my @zones;
       
    57     foreach my $utf8zone (@ARGV) {
       
    58         my $zone = idn_to_ascii($utf8zone, $CHARSET);
       
    59 
       
    60         die "zone $zone is not managed\n"
       
    61           if not -f "$cf{master_dir}/$zone/$zone";
       
    62 
       
    63         push @zones, $zone;
       
    64     }
       
    65 
       
    66     given ($cmd) {
       
    67         when ("zsk")   { exit create_zsk(@zones) };
       
    68         #when ("ksk")   { return create_ksk(@zones) };
       
    69         #when ("check") { return check_zone(@zones) };
       
    70         #when ("rm")    { return rm_keys(@zones) };
       
    71 	default		{ die "not implemented\n" };
       
    72     };
       
    73 }
       
    74 
       
    75 sub rm_keys ($@) {
       
    76 
       
    77     # deletes all the keys were handed over -rm in argv
       
    78     my ($master_dir, @zone) = @_;
       
    79 
       
    80     for (@zone) {
       
    81         my $zone = $_;
       
    82 
       
    83         my $zpf = "$master_dir/$zone";
       
    84         my $ep  = 0;
       
    85 
       
    86         if (-e "$zpf/$zone.signed") {
       
    87             unlink "$zpf/$zone.signed" and $ep = 1;
       
    88         }
       
    89         if (-e "$zpf/.keycounter") {
       
    90             unlink "$zpf/.keycounter" and $ep = 1;
       
    91         }
       
    92         if (-e "$zpf/.index.ksk") {
       
    93             unlink "$zpf/.index.ksk" and $ep = 1;
       
    94         }
       
    95         if (-e "$zpf/.index.zsk") {
       
    96             unlink "$zpf/.index.zsk" and $ep = 1;
       
    97         }
       
    98         if (-e "$zpf/dsset-$zone.") {
       
    99             unlink "$zpf/dsset-$zone." and $ep = 1;
       
   100         }
       
   101         if (-e "$zpf/keyset-$zone.") {
       
   102             unlink "$zpf/keyset-$zone." and $ep = 1;
       
   103         }
       
   104 
       
   105         for (glob("$zpf/K$zone*")) {
       
   106             chomp($_);
       
   107             unlink("$_");
       
   108         }
       
   109 
       
   110         if ($ep == 1) {
       
   111             print " * $zone: removed key-set\n";
       
   112         }
       
   113 
       
   114         open(my $old, "$zpf/$zone") or die "$zpf/$zone: $!\n";
       
   115         my $fh = File::Temp->new(DIR => $zpf)
       
   116           or die "Can't create tmpfile: $!\n";
       
   117         print $fh grep { not /^\s*\$INCLUDE.*"K$zone.*\.key"/i } <$old>;
       
   118         rename($fh->filename => "$zpf/$zone")
       
   119           or die "Can't rename " . $fh->filename . " to $zpf/$zone: $!\n";
       
   120     }
       
   121 }
       
   122 
       
   123 sub create_ksk ($@) {
       
   124     my ($master_dir, @zone) = @_;
       
   125     my @index;
       
   126     my $keyname;
       
   127 
       
   128     for (@zone) {
       
   129         my $zone = $_;
       
   130         my $zpf  = "$master_dir/$zone";
       
   131 
       
   132         $keyname =
       
   133           `cd $zpf && dnssec-keygen -a RSASHA1 -b 2048 -f KSK -n ZONE $zone`;
       
   134 
       
   135         unless (-f "$zpf/.index.ksk") { @index = (); }
       
   136         else {
       
   137             open(INDEX, "$zpf/.index.ksk") or die "$zpf/.index.ksk: $!\n";
       
   138             @index = <INDEX>;
       
   139             close(INDEX);
       
   140         }
       
   141 
       
   142         push @index, $keyname;
       
   143         if (@index > 2) { shift(@index); }
       
   144 
       
   145         {
       
   146             my $fh = File::Temp->new(DIR => "$zpf")
       
   147               or die "Can't create tmpdir: $!\n";
       
   148             print $fh join "" => @index, "";
       
   149             rename($fh->filename => "$zpf/.index.ksk")
       
   150               or die "Can't rename "
       
   151               . $fh->filename
       
   152               . " to $zpf/.index.ksk: $!\n";
       
   153         }
       
   154 
       
   155         chomp($keyname);
       
   156         print " * $zone: new KSK $keyname\n";
       
   157         print "!! THE KSK must be published !! \n";
       
   158 
       
   159     }
       
   160 }
       
   161 
       
   162 sub create_zsk (@) {
       
   163     my @zones = @_;
       
   164 
       
   165     my $keyname;
       
   166 
       
   167     foreach my $zone (@zones) {
       
   168         my $dir  = "$cf{master_dir}/$zone";
       
   169 
       
   170         chomp($keyname = `cd $dir && dnssec-keygen -a RSASHA1 -b 512 -n ZONE $zone`);
       
   171 
       
   172 	my @index;
       
   173 	open(my $idx, "+>>", "$dir/.index.zsk") or die "Can't open $dir/.index.zsk: $!\n";
       
   174 	seek($idx, 0, 0);
       
   175 	chomp(@index = <$idx>);
       
   176 
       
   177         push @index, $keyname;
       
   178 	shift @index if @index > 2;
       
   179 
       
   180 	truncate($idx, 0);
       
   181 	print $idx join "\n" => @index, "";
       
   182 	close($idx);
       
   183 
       
   184         say "$zone: new ZSK $keyname";
       
   185 
       
   186         open(my $kc, ">", "$dir/.keycounter") or die "$dir/.keycounter: $!\n";
       
   187         print $kc "0\n";
       
   188         close($kc);
       
   189     }
       
   190 }
       
   191 
       
   192 sub check_zone ($@) {
       
   193     my ($master_dir, @zone) = @_;
       
   194 
       
   195     for (@zone) {
       
   196         my $zone = $_;
       
   197         my $zpf  = "$master_dir/$zone";
       
   198         my $keyfile;
       
   199         my @content;
       
   200         my @keylist;
       
   201 
       
   202         for (<$zpf/*>) {
       
   203             if (m#(K$zone.*\.key)#) {
       
   204                 $keyfile = $1;
       
   205                 open(KEYFILE, "<", "$zpf/$keyfile")
       
   206                   or die "$zpf/$keyfile: $!\n";
       
   207                 @content = <KEYFILE>;
       
   208                 close(KEYFILE);
       
   209                 for (@content) {
       
   210                     if (m#DNSKEY.257#) {
       
   211                         push @keylist, $keyfile;
       
   212                     }
       
   213                 }
       
   214             }
       
   215         }
       
   216 
       
   217         open(INDEX, ">$zpf/.index.ksk") or die "$zpf/.index.ksk: $!\n";
       
   218         for (@keylist) {
       
   219             s#\.key##;
       
   220             print INDEX "$_\n";
       
   221         }
       
   222         close(INDEX);
       
   223 
       
   224         print " * $zone: new .index.ksk created\n";
       
   225         if (-f "$zpf/.index.zsk") {
       
   226             unlink("$zpf/.index.zsk") or die "$zpf/.index.zsk: $!\n";
       
   227         }
       
   228     }
       
   229 }
       
   230 
       
   231 sub post_create ($@) {
       
   232     my ($master_dir, @zone) = @_;
       
   233     for (@zone) {
       
   234         my $zone = $_;
       
   235         `touch $master_dir/$zone/$zone`;
       
   236         &kill_useless_keys($zone, $master_dir);
       
   237         &key_to_zonefile($zone, $master_dir);
       
   238     }
       
   239 }
       
   240 
       
   241 sub kill_useless_keys ($@) {
       
   242 
       
   243     # the function deletes all keys that are not available in the zone
       
   244 
       
   245     my $zone       = $_[0];
       
   246     my $master_dir = $_[1];
       
   247     my @keylist    = ();
       
   248     my $zpf        = "$master_dir/$zone";
       
   249 
       
   250     open(INDEX, "<$zpf/.index.zsk") or die "$zpf/.index.zsk: $!\n";
       
   251     @keylist = <INDEX>;
       
   252     close(INDEX);
       
   253     open(INDEX, "<$zpf/.index.ksk") or die "$zpf/.index.ksk: $!\n";
       
   254     push @keylist, <INDEX>;
       
   255 
       
   256     # shortened the key name from the index file on the id in order to
       
   257     # be able to compare
       
   258     for (@keylist) {
       
   259         chomp;
       
   260         s#K.*\+.*\+(.*)#$1#;
       
   261     }
       
   262 
       
   263     # reviewed every key file (KSK, ZSK), whether they are described in
       
   264     # the respective index file. if not they will be deleted.
       
   265     for (glob("$master_dir/$zone/K*")) {
       
   266         chomp;
       
   267         my $file     = $_;
       
   268         my $rm_count = 1;
       
   269         my $keyname;
       
   270         for (@keylist) {
       
   271             if ($file =~ /$_/) { $rm_count = 0; }
       
   272         }
       
   273         if ($rm_count == 1) {
       
   274             unlink "$file";
       
   275             if ($file =~ /$zpf\/(.*\.key)/) {
       
   276                 print " * $zone: Key $1 removed \n";
       
   277             }
       
   278         }
       
   279     }
       
   280 }
       
   281 
       
   282 sub key_to_zonefile ($@) {
       
   283 
       
   284     # the function added all keys to the indexfile
       
   285     my $zone       = $_[0];
       
   286     my $master_dir = $_[1];
       
   287     my $zpf        = "$master_dir/$zone";
       
   288     my @old_content;
       
   289     my @new_content = ();
       
   290 
       
   291     open(ZONEFILE, "<$zpf/$zone");
       
   292     @old_content = <ZONEFILE>;
       
   293     close(ZONEFILE);
       
   294 
       
   295     for (@old_content) {
       
   296         unless (m#INCLUDE.*key#) { push @new_content, $_; }
       
   297     }
       
   298 
       
   299     for (<$zpf/*>) {
       
   300         if (m#(.*\/)(K.*\.key)#) {
       
   301             push @new_content, "\$INCLUDE \"$2\"\n";
       
   302         }
       
   303     }
       
   304     open(ZONEFILE, ">$zpf/$zone") or die "$zpf/$zone: $!\n";
       
   305     print ZONEFILE @new_content;
       
   306     close(ZONEFILE);
       
   307 }
       
   308 
       
   309 __END__
       
   310 
       
   311 =pod
       
   312 
       
   313 =head1 NAME
       
   314 
       
   315     dnssec-keytool - key management
       
   316 
       
   317 =head1 SYNOPSIS
       
   318 
       
   319     dnssec-keytool {--zsk|--ksk|--rm|--check} zone...
       
   320 
       
   321 =head1 DESCRIPTION
       
   322 
       
   323 Blabla.
       
   324 
       
   325 =head1 OPTIONS
       
   326 
       
   327 =over
       
   328 
       
   329 =item B<--zsk> 
       
   330 
       
   331 Create a new ZSK for the zones.
       
   332 
       
   333 =item B<--ksk>
       
   334 
       
   335 Create a new KSK for the zones.
       
   336 
       
   337 =item B<--rm>
       
   338 
       
   339 Remote all key material from the zones.
       
   340 
       
   341 =item B<--check>
       
   342 
       
   343 ???
       
   344 
       
   345 =back
       
   346 
       
   347 =cut
       
   348 
       
   349 # vim:sts=4 sw=4 aw ai sm: