--- /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: