bin/dnssec-keytool
branchhs12
changeset 77 35905799cfd1
child 85 c47953192c5c
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/bin/dnssec-keytool	Thu Jan 13 00:20:26 2011 +0100
@@ -0,0 +1,350 @@
+#! /usr/bin/perl
+
+use v5.10;
+use warnings;
+use strict;
+use FindBin;
+use File::Temp;
+use Getopt::Long;
+use Pod::Usage;
+use File::Basename;
+use if $ENV{DEBUG} => "Smart::Comments";
+use DNStools::Config qw(get_config);
+
+my $ME = basename $0;
+
+sub read_conf(@);
+sub read_argv($);
+sub rm_keys($@);
+sub ck_zone($@);
+sub create_ksk($@);
+sub create_zsk($@);
+sub post_create($@);
+
+MAIN: {
+    ### reading config
+    my %conf = get_config("$FindBin::Bin/dnstools.conf", "/etc/dnstools.conf");
+
+    my ($cmd, @zones) = read_argv($conf{master_dir});
+
+    given ($cmd) {
+        when ("rm") { rm_keys($conf{master_dir}, @zones); exit }
+        when ("ck") { ck_zone($conf{master_dir}, @zones) }
+        when ("ksk") { create_ksk($conf{master_dir}, @zones) }
+    };
+
+    create_zsk($conf{master_dir}, @zones);
+    post_create($conf{master_dir}, @zones);
+}
+
+sub read_argv ($) {
+    my ($master_dir) = @_;
+    my ($cmd, @zones);    # return
+
+    GetOptions(
+        "zsk"      => sub { $cmd = "zsk" },
+        "ksk"      => sub { $cmd = "ksk" },
+        "rm"       => sub { $cmd = "rm" },
+        "ck|check" => sub { $cmd = "ck" },
+        "h|help" => sub { pod2usage(-exitvalue => 0, -verbose => 1) },
+        "m|man"  => sub {
+            pod2usage(
+                -exitvalue => 0,
+                -noperldoc => system("perldoc -V &>/dev/null"),
+                -verbose   => 2
+            );
+        },
+      )
+      and @ARGV
+      or pod2usage;
+
+    # checks the zones in argv if there are managed zones
+    foreach (@ARGV) {
+        chomp(my $zone = `idn --quiet "$_"`);
+
+        die "zone $zone is not managed\n"
+          if not -f "$master_dir/$zone/$zone";
+
+        push @zones, $zone;
+    }
+    return ($cmd, @zones);
+}
+
+
+sub rm_keys ($@) {
+
+    # deletes all the keys were handed over -rm in argv
+    my ($master_dir, @zone) = @_;
+
+    for (@zone) {
+        my $zone = $_;
+
+        my $zpf = "$master_dir/$zone";
+        my $ep  = 0;
+
+        if (-e "$zpf/$zone.signed") {
+            unlink "$zpf/$zone.signed" and $ep = 1;
+        }
+        if (-e "$zpf/.keycounter") {
+            unlink "$zpf/.keycounter" and $ep = 1;
+        }
+        if (-e "$zpf/.index.ksk") {
+            unlink "$zpf/.index.ksk" and $ep = 1;
+        }
+        if (-e "$zpf/.index.zsk") {
+            unlink "$zpf/.index.zsk" and $ep = 1;
+        }
+        if (-e "$zpf/dsset-$zone.") {
+            unlink "$zpf/dsset-$zone." and $ep = 1;
+        }
+        if (-e "$zpf/keyset-$zone.") {
+            unlink "$zpf/keyset-$zone." and $ep = 1;
+        }
+
+        for (glob("$zpf/K$zone*")) {
+            chomp($_);
+            unlink("$_");
+        }
+
+        if ($ep == 1) {
+            print " * $zone: removed key-set\n";
+        }
+
+	open(my $old, "$zpf/$zone") or die "$zpf/$zone: $!\n";
+	my $fh = File::Temp->new(DIR => $zpf) or die "Can't create tmpfile: $!\n";
+	print $fh grep { not /^\s*\$INCLUDE.*"K$zone.*\.key"/i } <$old>;
+	rename($fh->filename => "$zpf/$zone")
+	    or die "Can't rename " . $fh->filename . " to $zpf/$zone: $!\n";
+    }
+}
+
+sub create_ksk ($@) {
+    my ($master_dir, @zone) = @_;
+    my @index;
+    my $keyname;
+
+    for (@zone) {
+        my $zone = $_;
+        my $zpf  = "$master_dir/$zone";
+
+        $keyname =
+          `cd $zpf && dnssec-keygen -a RSASHA1 -b 2048 -f KSK -n ZONE $zone`;
+
+        unless (-f "$zpf/.index.ksk") { @index = (); }
+        else {
+            open(INDEX, "$zpf/.index.ksk") or die "$zpf/.index.ksk: $!\n";
+            @index = <INDEX>;
+            close(INDEX);
+        }
+
+        push @index, $keyname;
+        if (@index > 2) { shift(@index); }
+
+        {
+            my $fh = File::Temp->new(DIR => "$zpf")
+              or die "Can't create tmpdir: $!\n";
+            print $fh join "" => @index, "";
+            rename($fh->filename => "$zpf/.index.ksk")
+              or die "Can't rename "
+              . $fh->filename
+              . " to $zpf/.index.ksk: $!\n";
+        }
+
+        chomp($keyname);
+        print " * $zone: new KSK $keyname\n";
+        print "!! THE KSK must be published !! \n";
+
+    }
+}
+
+sub create_zsk ($@) {
+    my ($master_dir, @zone) = @_;
+    my @index;
+    my $keyname;
+
+    for (@zone) {
+        my $zone = $_;
+        my $zpf  = "$master_dir/$zone";
+
+        $keyname = `cd $zpf && dnssec-keygen -a RSASHA1 -b 512 -n ZONE $zone`;
+
+        unless (-f "$zpf/.index.zsk") {
+            @index = ();
+        }
+        else {
+            open(INDEX, "$zpf/.index.zsk") or die "$zpf/.index.zsk: $!\n";
+            @index = <INDEX>;
+            close(INDEX);
+        }
+
+        push @index, $keyname;
+        if (@index > 2) { shift(@index); }
+
+        {
+            my $fh = File::Temp->new(DIR => "$zpf")
+              or die "Can't create tmpdir: $!\n";
+            print $fh join "" => @index, "";
+            rename($fh->filename => "$zpf/.index.zsk")
+              or die "Can't rename "
+              . $fh->filename
+              . " to $zpf/.index.zsk: $!\n";
+        }
+        chomp($keyname);
+        print " * $zone: new ZSK $keyname\n";
+
+        open(KC, ">$zpf/.keycounter") or die "$zpf/keycounter: $!\n";
+        print KC "0";
+        close(KC);
+    }
+}
+
+sub ck_zone ($@) {
+    my ($master_dir, @zone) = @_;
+
+    for (@zone) {
+        my $zone = $_;
+        my $zpf  = "$master_dir/$zone";
+        my $keyfile;
+        my @content;
+        my @keylist;
+
+        for (<$zpf/*>) {
+            if (m#(K$zone.*\.key)#) {
+                $keyfile = $1;
+                open(KEYFILE, "<", "$zpf/$keyfile")
+                  or die "$zpf/$keyfile: $!\n";
+                @content = <KEYFILE>;
+                close(KEYFILE);
+                for (@content) {
+                    if (m#DNSKEY.257#) {
+                        push @keylist, $keyfile;
+                    }
+                }
+            }
+        }
+
+        open(INDEX, ">$zpf/.index.ksk") or die "$zpf/.index.ksk: $!\n";
+        for (@keylist) {
+            s#\.key##;
+            print INDEX "$_\n";
+        }
+        close(INDEX);
+
+        print " * $zone: new .index.ksk created\n";
+        if (-f "$zpf/.index.zsk") {
+            unlink("$zpf/.index.zsk") or die "$zpf/.index.zsk: $!\n";
+        }
+    }
+}
+
+sub post_create ($@) {
+    my ($master_dir, @zone) = @_;
+    for (@zone) {
+        my $zone = $_;
+        `touch $master_dir/$zone/$zone`;
+        &kill_useless_keys($zone, $master_dir);
+        &key_to_zonefile($zone, $master_dir);
+    }
+}
+
+sub kill_useless_keys ($@) {
+
+    # the function deletes all keys that are not available in the zone
+
+    my $zone       = $_[0];
+    my $master_dir = $_[1];
+    my @keylist    = ();
+    my $zpf        = "$master_dir/$zone";
+
+    open(INDEX, "<$zpf/.index.zsk") or die "$zpf/.index.zsk: $!\n";
+    @keylist = <INDEX>;
+    close(INDEX);
+    open(INDEX, "<$zpf/.index.ksk") or die "$zpf/.index.ksk: $!\n";
+    push @keylist, <INDEX>;
+
+    # shortened the key name from the index file on the id in order to
+    # be able to compare
+    for (@keylist) {
+        chomp;
+        s#K.*\+.*\+(.*)#$1#;
+    }
+
+    # reviewed every key file (KSK, ZSK), whether they are described in
+    # the respective index file. if not they will be deleted.
+    for (glob("$master_dir/$zone/K*")) {
+        chomp;
+        my $file     = $_;
+        my $rm_count = 1;
+        my $keyname;
+        for (@keylist) {
+            if ($file =~ /$_/) { $rm_count = 0; }
+        }
+        if ($rm_count == 1) {
+            unlink "$file";
+            if ($file =~ /$zpf\/(.*\.key)/) {
+                print " * $zone: Key $1 removed \n";
+            }
+        }
+    }
+}
+
+sub key_to_zonefile ($@) {
+
+    # the function added all keys to the indexfile
+    my $zone       = $_[0];
+    my $master_dir = $_[1];
+    my $zpf        = "$master_dir/$zone";
+    my @old_content;
+    my @new_content = ();
+
+    open(ZONEFILE, "<$zpf/$zone");
+    @old_content = <ZONEFILE>;
+    close(ZONEFILE);
+
+    for (@old_content) {
+        unless (m#INCLUDE.*key#) { push @new_content, $_; }
+    }
+
+    for (<$zpf/*>) {
+        if (m#(.*\/)(K.*\.key)#) {
+            push @new_content, "\$INCLUDE \"$2\"\n";
+        }
+    }
+    open(ZONEFILE, ">$zpf/$zone") or die "$zpf/$zone: $!\n";
+    print ZONEFILE @new_content;
+    close(ZONEFILE);
+}
+
+__END__
+
+=pod
+
+=head1 NAME
+
+dnssec-keytool
+
+=head1 SYNOPSIS
+
+dnssec-keytool {-z|-k|-r|-c} zone
+
+=head1 DESCRIPTION
+
+Blabla.
+
+=head1 OPTIONS
+
+=over
+
+=item B<-z>  created a new ZSK
+
+=item B<-k>  created a new ZSK and KSK
+
+=item B<-r>  delete the key-set of a zone
+
+=item B<-c>  created configuration files for the dnstools and a new ZSK for an existing KSK
+
+=back
+
+=cut
+
+# vim:sts=4 sw=4 aw ai sm: