3 ## MAca-bundle.pl -- Regenerate ca-root-nss.crt from the Mozilla certdata.txt
5 ## Rewritten in September 2011 by Matthias Andree to heed untrust
8 ## Copyright (c) 2011, 2013 Matthias Andree <mandree@FreeBSD.org>
9 ## All rights reserved.
10 ## Copyright (c) 2018, Allan Jude <allanjude@FreeBSD.org>
12 ## Redistribution and use in source and binary forms, with or without
13 ## modification, are permitted provided that the following conditions are
16 ## * Redistributions of source code must retain the above copyright
17 ## notice, this list of conditions and the following disclaimer.
19 ## * Redistributions in binary form must reproduce the above copyright
20 ## notice, this list of conditions and the following disclaimer in the
21 ## documentation and/or other materials provided with the distribution.
23 ## THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
24 ## "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
25 ## LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
26 ## FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
27 ## COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
28 ## INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
29 ## BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
30 ## LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
31 ## CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
32 ## LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
33 ## ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
34 ## POSSIBILITY OF SUCH DAMAGE.
41 my $VERSION = '$FreeBSD$';
42 my $generated = '@' . 'generated';
52 if defined $ENV{'WITH_DEBUG'}
53 and $ENV{'WITH_DEBUG'} !~ m/(?i)^(no|0|false|)$/;
57 "infile:s" => \$infile,
58 "outputdir:s" => \$outputdir)
59 or die("Error in command line arguments\n$0 [-d] [-i input-file] [-o output-dir]\n");
62 open($inputfh, "<", $infile) or die "Failed to open $infile";
71 print $dstfile <<EOFH;
75 ## This is a single X.509 certificate for a public Certificate
76 ## Authority (CA). It was automatically extracted from Mozilla's
77 ## root CA list (the file `certdata.txt' in security/nss).
79 ## It contains a certificate trusted for server authentication.
90 ## ca-root-nss.crt -- Bundle of CA Root Certificates
92 ## This is a bundle of X.509 certificates of public Certificate
93 ## Authorities (CA). These were automatically extracted from Mozilla's
94 ## root CA list (the file `certdata.txt').
96 ## It contains certificates trusted for server authentication.
107 # returns a string like YYMMDDhhmmssZ of current time in GMT zone
110 my ($sec,$min,$hour,$mday,$mon,$year,undef,undef,undef) = gmtime(time);
111 return sprintf "%02d%02d%02d%02d%02d%02dZ", $year-100, $mon+1, $mday, $hour, $min, $sec;
116 my ($fh, $label, $certdata) = @_;
117 return unless $certdata;
118 open(OUT, "|openssl x509 -text -inform DER -fingerprint")
119 or die "could not pipe to openssl x509";
121 close(OUT) or die "openssl x509 failed with exit code $?";
124 # converts a datastream that is to be \177-style octal constants
125 # from <> to a (binary) string and returns it
133 my (undef,@oct) = split /\\/;
134 my @bin = map(chr(oct), @oct);
135 $data .= join('', @bin);
153 if (/^CKA_LABEL UTF8 "([^"]+)"/) {
157 if (/^CKA_VALUE MULTILINE_OCTAL/) {
158 $certdata = graboct($ifh);
161 if (/^CKA_SERIAL_NUMBER MULTILINE_OCTAL/) {
162 $serial = graboct($ifh);
165 if (/^CKA_NSS_SERVER_DISTRUST_AFTER MULTILINE_OCTAL/)
167 my $distrust_after = graboct($ifh);
168 my $time_now = timenow();
169 if ($time_now >= $distrust_after) { $distrust = 1; }
171 printf STDERR "line $.: $cka_label ser #%d: distrust after %s, now: %s -> distrust $distrust\n", $serial, $distrust_after, timenow();
178 return ($serial, $cka_label, $certdata);
192 if (/^CKA_LABEL UTF8 "([^"]+)"/) {
196 if (/^CKA_SERIAL_NUMBER MULTILINE_OCTAL/) {
197 $serial = graboct($ifh);
200 if (/^CKA_TRUST_SERVER_AUTH CK_TRUST (\S+)$/)
202 if ($1 eq 'CKT_NSS_NOT_TRUSTED') {
204 } elsif ($1 eq 'CKT_NSS_TRUSTED_DELEGATOR') {
206 } elsif ($1 ne 'CKT_NSS_MUST_VERIFY_TRUST') {
207 confess "Unknown trust setting on line $.:\n"
209 . "Script must be updated:";
214 if (!$maytrust && !$distrust && $debug) {
215 print STDERR "line $.: no explicit trust/distrust found for $cka_label\n";
218 my $trust = ($maytrust and not $distrust);
219 return ($serial, $cka_label, $trust);
223 print_header(*STDOUT, "");
229 if (/^CKA_CLASS CK_OBJECT_CLASS CKO_CERTIFICATE/) {
230 my ($serial, $label, $certdata) = grabcert($inputfh);
231 if (defined $certs{$label."\0".$serial}) {
232 warn "Certificate $label duplicated!\n";
234 if (defined $certdata) {
235 $certs{$label."\0".$serial} = $certdata;
236 # We store the label in a separate hash because truncating the key
237 # with \0 was causing garbage data after the end of the text.
238 $labels{$label."\0".$serial} = $label;
239 } else { # $certdata undefined? distrust_after in effect
242 } elsif (/^CKA_CLASS CK_OBJECT_CLASS CKO_NSS_TRUST/) {
243 my ($serial, $label, $trust) = grabtrust($inputfh);
244 if (defined $trusts{$label."\0".$serial}) {
245 warn "Trust for $label duplicated!\n";
247 $trusts{$label."\0".$serial} = $trust;
248 $labels{$label."\0".$serial} = $label;
249 } elsif (/^CVS_ID.*Revision: ([^ ]*).*/) {
250 print "## Source: \"certdata.txt\" CVS revision $1\n##\n\n";
254 sub label_to_filename(@) {
256 map { s/\0.*//; s/[^[:alnum:]\-]/_/g; $_ = "$_.pem"; } @res;
257 return wantarray ? @res : $res[0];
260 # weed out untrusted certificates
261 foreach my $it (keys %trusts) {
263 if (!exists($certs{$it})) {
264 warn "Found trust for nonexistent certificate $labels{$it}\n" if $debug;
267 warn "Skipping untrusted $labels{$it}\n" if $debug;
274 print "## Untrusted certificates omitted from this bundle: $untrusted\n\n";
276 print STDERR "## Untrusted certificates omitted from this bundle: $untrusted\n";
279 foreach my $it (sort {uc($a) cmp uc($b)} keys %certs) {
282 if (!exists($trusts{$it})) {
283 die "Found certificate without trust block,\naborting";
286 $filename = label_to_filename($labels{$it});
287 open($fh, ">", "$outputdir/$filename") or die "Failed to open certificate $filename";
288 print_header($fh, $labels{$it});
290 printcert($fh, $labels{$it}, $certs{$it});
292 close($fh) or die "Unable to close: $filename";
297 print STDERR "Trusting $certcount: $labels{$it}\n" if $debug;
300 if ($certcount < 25) {
301 die "Certificate count of $certcount is implausibly low.\nAbort";
305 print "## Number of certificates: $certcount\n";
306 print "## End of file.\n";
308 print STDERR "## Number of certificates: $certcount\n";