aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--scripts/rcynic-prototype.pl183
1 files changed, 118 insertions, 65 deletions
diff --git a/scripts/rcynic-prototype.pl b/scripts/rcynic-prototype.pl
index c01dd1b1..09588ce9 100644
--- a/scripts/rcynic-prototype.pl
+++ b/scripts/rcynic-prototype.pl
@@ -24,19 +24,24 @@ my @anchors; # Trust anchor URIs
my @preaggregated; # Pre-aggregation source URIs
my %cache; # URIs from which we've already rsynced
-my $verbose = 1;
+my $verbose_run = 0; # Log all external programs
+my $verbose_cache = 0; # Log various cache hits
+my $verbose_walk = 0; # Log more info during certificate walk
+my $verbose_aia = 0; # Log more info for AIA errors
+my $verbose_sia_fixup = 1; # Log when fixing up SIA URIs
sub run { # Run a program
print(join(" ", "Running", @_), "\n")
- if ($verbose);
+ if ($verbose_run);
system(@_);
print("$_[0] returned $?\n")
if ($? != 0);
+ return $? == 0;
}
sub run_pipe { # Run a program and hand back its output
print(join(" ", "Running", @_), "\n")
- if ($verbose);
+ if ($verbose_run);
my $pid = open(F, "-|");
if ($pid) {
my @result = <F>;
@@ -61,7 +66,7 @@ sub mkdir_maybe { # Create missing directories
}
sub rsync { # Run rsync with our preferred options
- run("rsync", "-rtiLku", @_);
+ return run("rsync", "-rtiLku", @_);
}
sub rsync_cache { # Run rsync unless we've already done so for a URI covering this one
@@ -71,10 +76,13 @@ sub rsync_cache { # Run rsync unless we've already done so for a URI covering t
while (@path && !$cache{join("/", @path)});
if (@path) {
print("Cache hit ($path, ", join("/", @path), "), skipping rsync\n")
- if ($verbose);
- } else {
- rsync(@_);
+ if ($verbose_cache);
+ return 1;
+ } elsif (rsync(@_)) {
$cache{$path} = 1;
+ return 1;
+ } else {
+ return 0;
}
}
@@ -120,7 +128,8 @@ sub parse_cert { # Parse interesting fields from a certificate
if (/X509v3 Basic Constraints/ && $txt[$i+1] =~ /^\s*CA:TRUE\s*$/);
}
if ($res{sia} && $res{sia} !~ m=/$=) {
- print("Badly formatted AIA URI, compensating: $res{sia}\n");
+ print("Badly formatted AIA URI, compensating: $res{sia}\n")
+ if ($verbose_sia_fixup);
$res{sia} .= "/";
}
return \%res;
@@ -148,7 +157,8 @@ sub copy_cert { # Convert a certificate from DER to PEM
my $indir = shift || $unauthenticated_tree;
my $outdir = shift || $temporary_tree;
if (-f "$outdir/$name") {
- print("Already copied certificate $name, skipping\n");
+ print("Already copied certificate $name\n")
+ if ($verbose_cache);
return;
}
mkdir_maybe("$outdir/$name");
@@ -161,11 +171,13 @@ sub check_crl { # Check signature chain on a CRL, install CRL if all is well
unless ($uri);
my $file = uri_to_filename($uri);
if (-f "$authenticated_tree/$file") {
- print("Already checked CRL $file, skipping\n");
+ print("Already checked CRL $uri\n")
+ if ($verbose_cache);
return $file;
}
mkdir_maybe("$unauthenticated_tree/$file");
- rsync_cache($uri, "$unauthenticated_tree/$file");
+ rsync_cache($uri, "$unauthenticated_tree/$file")
+ or return undef;
setup_cafile(@_);
my @result = openssl_pipe("crl", "-inform", "DER", "-CAfile", $cafile, "-in", "$unauthenticated_tree/$file");
local $_;
@@ -173,8 +185,11 @@ sub check_crl { # Check signature chain on a CRL, install CRL if all is well
mkdir_maybe("$authenticated_tree/$file");
openssl("crl", "-inform", "DER", "-in", "$unauthenticated_tree/$file", "-outform", "PEM", "-out", "$authenticated_tree/$file");
return $file;
+ } elsif (grep(/certificate revoked/, @result)) {
+ print("Revoked certificate in path for CRL $uri\n");
+ return undef;
} else {
- print("Verification failure:\n");
+ print("Verification failure for CRL $uri:\n");
print(" Inputs:\n");
print(" $_\n")
foreach (($file, @_));
@@ -185,22 +200,33 @@ sub check_crl { # Check signature chain on a CRL, install CRL if all is well
}
}
+sub move {
+ my $source = shift;
+ my $destination = shift;
+ mkdir_maybe($destination);
+ rename($source, $destination)
+ or die("Couldn't rename $source to $destination");
+}
+
+
sub check_cert { # Check signature chain etc on a certificate, install certificate if everything is ok
- my $cert = shift;
+ my $uri = shift;
+ my $file = shift;
setup_cafile(@_);
my @result = openssl_pipe(qw(verify -verbose -crl_check_all -policy_check -explicit_policy -policy 1.3.6.1.5.5.7.14.2 -x509_strict -CAfile), $cafile,
- "$temporary_tree/$cert");
+ "$temporary_tree/$file");
local $_;
if (grep(/OK$/, @result)) {
- mkdir_maybe("$authenticated_tree/$cert");
- rename("$temporary_tree/$cert", "$authenticated_tree/$cert")
- or die("Couldn't rename $temporary_tree/$cert to $authenticated_tree/$cert");
+ move("$temporary_tree/$file", "$authenticated_tree/$file");
return 1;
+ } elsif (grep(/certificate revoked/, @result)) {
+ print("Revoked certificate in path for certificate $uri\n");
+ return 0;
} else {
- print("Verification failure:\n");
+ print("Verification failure for certificate $uri:\n");
print(" Inputs:\n");
print(" $_\n")
- foreach (($cert, @_));
+ foreach (($file, @_));
print(" Result:\n");
print(" $_\n")
foreach (@result);
@@ -208,88 +234,111 @@ sub check_cert { # Check signature chain etc on a certificate, install certific
}
}
-sub process_cert { # Process a certificate -- this is the core of the program
- my $c = shift; # Parsed and verified cert we're examining
- my @chain = @_; # Ancestors and CRLs
-
- die("No certificate to check!")
- unless ($c);
-
- print("Starting check of $c->{uri}\n",
- "CA: ", ($c->{ca} ? "Yes" : "No"), "\n",
- "TA: ", ($c->{ta} ? "Yes" : "No"), "\n");
- print("AIA: $c->{aia}\n") if ($c->{aia});
- print("SIA: $c->{sia}\n") if ($c->{sai});
- print("CDP: $c->{cdp}\n") if ($c->{cdp});
-
- if (!$c->{ta} && !$c->{aia}) {
- print("Non-trust-anchor certificate missing AIA extension: $c->{uri}\n");
- } elsif (!$c->{ta} && $chain[0] ne uri_to_filename($c->{aia})) {
- print("AIA does not match parent URI:\n\trsync://$chain[0]\n\t$c->{aia}\n");
+sub walk_cert { # Process a certificate -- this is the core of the program
+ my $p = shift;
+
+ die("No certificate to process!")
+ unless ($p);
+
+ print("Starting walk of $p->{uri}\n");
+ if ($verbose_walk) {
+ print("CA: ", ($p->{ca} ? "Yes" : "No"), "\n");
+ print("TA: ", ($p->{ta} ? "Yes" : "No"), "\n");
+ print("AIA: $p->{aia}\n") if ($p->{aia});
+ print("SIA: $p->{sia}\n") if ($p->{sia});
+ print("CDP: $p->{cdp}\n") if ($p->{cdp});
}
- unshift(@chain, uri_to_filename($c->{cdp}))
- if ($c->{cdp});
- unshift(@chain, $c->{file});
+ if ($p->{sia}) {
+ my @chain = (uri_to_filename($p->{cdp}), $p->{file}, @_);
+ my $sia = uri_to_filename($p->{sia});
+ mkdir_maybe("$unauthenticated_tree/$sia");
+ rsync_cache($p->{sia}, "$unauthenticated_tree/$sia");
- if ($c->{ca}) {
- print("CA certificate without SIA extension: $c->{uri}\n")
- unless ($c->{sia});
- } else {
- print("EE certificate shouldn't have SIA extension: $c->{uri}\n")
- if ($c->{sia});
- }
+ # In theory this should check all files in this directory, not
+ # just ones matching *.cer. Punt on that for now as it'd be
+ # painful in this kludgy script.
- if ($c->{sia}) {
- my $sia = uri_to_filename($c->{sia});
- mkdir_maybe("$unauthenticated_tree/$sia");
- rsync_cache($c->{sia}, "$unauthenticated_tree/$sia");
for my $file (glob("$unauthenticated_tree/${sia}*.cer")) {
$file =~ s=^$unauthenticated_tree/==;
my $uri = "rsync://" . $file;
print("Found cert $uri\n");
if (-f "$authenticated_tree/$file") {
- print("Already checked certificate $uri, skipping\n");
+ print("Already checked certificate $uri, skipping\n")
+ if ($verbose_cache);
next;
}
die("Certificate $uri is its own ancestor?!?")
if (grep({$file eq $_} @chain));
copy_cert($file);
- my $x = parse_cert($uri, $temporary_tree);
- if (!$x) {
+ my $c = parse_cert($uri, $temporary_tree);
+ if (!$c) {
print("Parse failure for $uri, skipping\n");
next;
}
- if (!$x->{cdp}) {
+ if (!$c->{aia}) {
+ print("AIA missing for $uri, skipping\n");
+ next;
+ }
+ if (!$p->{ta} && $c->{aia} ne $p->{uri}) {
+ print("AIA of $uri doesn't match parent, skipping\n");
+ print("\tSubject AIA: $c->{aia}\n",
+ "\t Issuer URI: $p->{uri}\n")
+ if ($verbose_aia);
+ if ($verbose_aia > 1) {
+ my $c_aia = "$unauthenticated_tree/" . uri_to_filename($c->{aia});
+ my $p_uri = "$unauthenticated_tree/" . uri_to_filename($p->{uri});
+ my $res = run("cmp", "-sz", $c_aia, $p_uri);
+ if ($res == 0) {
+ print("\tBoth certificates exist, content is identical\n");
+ } elsif ($res == 1) {
+ print("\tBoth certificates exist, content differs\n");
+ } elsif (! -f $c_aia) {
+ print("\tCertificate indicated by AIA not found\n");
+ }
+ }
+ next;
+ }
+ if ($c->{ca} && !$c->{sia}) {
+ print("CA certificate $uri without SIA extension, skipping\n");
+ next;
+ }
+ if (!$c->{ca} && $c->{sia}) {
+ print("EE certificate $uri with SIA extension, skipping\n");
+ next;
+ }
+ if (!$c->{cdp}) {
print("CDP missing for $uri, skipping\n");
next;
}
- my $crl = check_crl($x->{cdp}, @chain);
+ my $crl = check_crl($c->{cdp}, @chain);
if (!$crl) {
print("Problem with CRL for $uri, skipping\n");
next;
}
- if (!check_cert($file, $crl, @chain)) {
+ if (!check_cert($uri, $file, $crl, @chain)) {
print("Verification failure for $uri, skipping\n");
- unlink("$temporary_tree/$file");
next;
}
- process_cert($x, @chain);
+ walk_cert($c, @chain);
}
}
- print("Finished check of $c->{uri}\n");
+ print("Finished walk of $p->{uri}\n");
}
sub main { # Main program
my $start_time = time;
- print("Started at ", localtime($start_time), "\n");
+ print("Started at ", scalar(localtime($start_time)), "\n");
# We should read a configuration file, but for debugging it's
# easier just to wire the parameters into the script.
if (1) {
+ push(@anchors, qw(rsync://repository.apnic.net/APNIC/APNIC.cer));
+ push(@preaggregated, qw());
+ } elsif (1) {
push(@anchors, qw(rsync://ca-trial.ripe.net/ARIN/root/root.cer
rsync://ca-trial.ripe.net/RIPE/root/root.cer
rsync://ca-trial.ripe.net/arinroot/repos/root.cer
@@ -353,17 +402,21 @@ sub main { # Main program
for my $anchor (@anchors) {
my $t = parse_cert($anchor);
die("Couldn't parse trust anchor! $anchor\n")
- unless ($t);
+ unless($t);
$t->{ta} = 1;
+ if (!$t->{cdp}) {
+ print("Trust anchor $anchor has no CRL distribution point, skipping\n");
+ next;
+ }
if (!check_crl($t->{cdp}, $t->{file})) {
- print("Problem with trust anchor CRL $t->{cdp}, skipping trust anchor $anchor\n");
+ print("Problem with trust anchor $anchor CRL $t->{cdp}, skipping\n");
next;
}
- process_cert($t);
+ walk_cert($t);
}
my $stop_time = time;
- print("Finished at ", localtime($stop_time), "\n");
+ print("Finished at ", scalar(localtime($stop_time)), "\n");
my $elapsed = $stop_time - $start_time;
my $seconds = $elapsed % 60; $elapsed /= 60;