## ## MAca-bundle.pl -- Regenerate ca-root-nss.crt from the Mozilla certdata.txt ## ## Rewritten in September 2011 by Matthias Andree to heed untrust ## ## Copyright (c) 2011, 2013 Matthias Andree ## All rights reserved. ## ## Redistribution and use in source and binary forms, with or without ## modification, are permitted provided that the following conditions are ## met: ## ## * Redistributions of source code must retain the above copyright ## notice, this list of conditions and the following disclaimer. ## ## * Redistributions in binary form must reproduce the above copyright ## notice, this list of conditions and the following disclaimer in the ## documentation and/or other materials provided with the distribution. ## ## THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ## "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT ## LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS ## FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE ## COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, ## INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, ## BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; ## LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER ## CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT ## LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ## ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE ## POSSIBILITY OF SUCH DAMAGE. use strict; use Carp; use MIME::Base64; my $VERSION = '$FreeBSD$'; # configuration print <) { last if /^END/; my (undef,@oct) = split /\\/; my @bin = map(chr(oct), @oct); $data .= join('', @bin); } return $data; } sub grabcert() { my $certdata; my $cka_label; my $serial; while (<>) { chomp; last if ($_ eq ''); if (/^CKA_LABEL UTF8 "([^"]+)"/) { $cka_label = $1; } if (/^CKA_VALUE MULTILINE_OCTAL/) { $certdata = graboct(); } if (/^CKA_SERIAL_NUMBER MULTILINE_OCTAL/) { $serial = graboct(); } } return ($serial, $cka_label, $certdata); } sub grabtrust() { my $cka_label; my $serial; my $maytrust = 0; my $distrust = 0; while (<>) { chomp; last if ($_ eq ''); if (/^CKA_LABEL UTF8 "([^"]+)"/) { $cka_label = $1; } if (/^CKA_SERIAL_NUMBER MULTILINE_OCTAL/) { $serial = graboct(); } if (/^CKA_TRUST_(SERVER_AUTH|EMAIL_PROTECTION|CODE_SIGNING) CK_TRUST (\S+)$/) { if ($2 eq 'CKT_NSS_NOT_TRUSTED') { $distrust = 1; } elsif ($2 eq 'CKT_NSS_TRUSTED_DELEGATOR') { $maytrust = 1; } elsif ($2 ne 'CKT_NSS_MUST_VERIFY_TRUST') { confess "Unknown trust setting on line $.:\n" . "$_\n" . "Script must be updated:"; } } } if (!$maytrust && !$distrust && $debug) { print STDERR "line $.: no explicit trust/distrust found for $cka_label\n"; } my $trust = ($maytrust and not $distrust); return ($serial, $cka_label, $trust); } while (<>) { if (/^CKA_CLASS CK_OBJECT_CLASS CKO_CERTIFICATE/) { my ($serial, $label, $certdata) = grabcert(); if (defined $certs{$label."\0".$serial}) { warn "Certificate $label duplicated!\n"; } $certs{$label."\0".$serial} = $certdata; } elsif (/^CKA_CLASS CK_OBJECT_CLASS CKO_NSS_TRUST/) { my ($serial, $label, $trust) = grabtrust(); if (defined $trusts{$label."\0".$serial}) { warn "Trust for $label duplicated!\n"; } $trusts{$label."\0".$serial} = $trust; } elsif (/^CVS_ID.*Revision: ([^ ]*).*/) { print "## Source: \"certdata.txt\" CVS revision $1\n##\n\n"; } } sub printlabel(@) { my @res = @_; map { s/\0.*//; s/[^[:print:]]/_/g; $_ = "\"$_\""; } @res; return wantarray ? @res : $res[0]; } # weed out untrusted certificates my $untrusted = 0; foreach my $it (keys %trusts) { if (!$trusts{$it}) { if (!exists($certs{$it})) { warn "Found trust for nonexistent certificate ".printlabel($it)."\n" if $debug; } else { delete $certs{$it}; warn "Skipping untrusted ".printlabel($it)."\n" if $debug; $untrusted++; } } } print "## Untrusted certificates omitted from this bundle: $untrusted\n\n"; print STDERR "## Untrusted certificates omitted from this bundle: $untrusted\n"; my $certcount = 0; foreach my $it (sort {uc($a) cmp uc($b)} keys %certs) { if (!exists($trusts{$it})) { die "Found certificate without trust block,\naborting"; } printcert("", $certs{$it}); print "\n\n\n"; $certcount++; print STDERR "Trusting $certcount: ".printlabel($it)."\n" if $debug; } if ($certcount < 25) { die "Certificate count of $certcount is implausibly low.\nAbort"; } print "## Number of certificates: $certcount\n"; print STDERR "## Number of certificates: $certcount\n"; print "## End of file.\n";