The branch main has been updated by des:

URL: 
https://cgit.FreeBSD.org/src/commit/?id=b88b0bb784c7fdcfb8174806e822c1f8983c223f

commit b88b0bb784c7fdcfb8174806e822c1f8983c223f
Author:     Dag-Erling Smørgrav <d...@freebsd.org>
AuthorDate: 2025-08-25 21:41:36 +0000
Commit:     Dag-Erling Smørgrav <d...@freebsd.org>
CommitDate: 2025-08-25 21:41:36 +0000

    caroot: Generate both trusted and untrusted
    
    Until now, the untrusted directory has been maintained manually.  Modify
    the script used to maintain the trusted directory so it can handle both.
    While here, clean it up a bit.
    
    MFC after:      1 week
    Reviewed by:    mandree, markj
    Differential Revision:  https://reviews.freebsd.org/D51774
---
 secure/caroot/MAca-bundle.pl     | 136 ++++++++++++---------------------------
 secure/caroot/Makefile           |   3 +-
 secure/caroot/trusted/Makefile   |   6 +-
 secure/caroot/untrusted/Makefile |   5 +-
 4 files changed, 51 insertions(+), 99 deletions(-)

diff --git a/secure/caroot/MAca-bundle.pl b/secure/caroot/MAca-bundle.pl
index 58cfe1cbf6fa..411d00b9bb61 100755
--- a/secure/caroot/MAca-bundle.pl
+++ b/secure/caroot/MAca-bundle.pl
@@ -8,6 +8,7 @@
 ##  Copyright (c) 2011, 2013 Matthias Andree <mand...@freebsd.org>
 ##  All rights reserved.
 ##  Copyright (c) 2018, Allan Jude <allanj...@freebsd.org>
+##  Copyright (c) 2025 Dag-Erling Smørgrav <d...@freebsd.org>
 ##
 ##  Redistribution and use in source and binary forms, with or without
 ##  modification, are permitted provided that the following conditions are
@@ -34,6 +35,7 @@
 ##  POSSIBILITY OF SUCH DAMAGE.
 
 use strict;
+use warnings;
 use Carp;
 use MIME::Base64;
 use Getopt::Long;
@@ -44,10 +46,12 @@ my $generated = '@' . 'generated';
 my $inputfh = *STDIN;
 my $debug = 0;
 my $infile;
-my $outputdir;
+my $trustdir = "trusted";
+my $untrustdir = "untrusted";
 my %labels;
 my %certs;
 my %trusts;
+my %expires;
 
 $debug++
     if defined $ENV{'WITH_DEBUG'}
@@ -56,8 +60,9 @@ $debug++
 GetOptions (
        "debug+" => \$debug,
        "infile:s" => \$infile,
-       "outputdir:s" => \$outputdir)
-  or die("Error in command line arguments\n$0 [-d] [-i input-file] [-o 
output-dir]\n");
+       "trustdir:s" => \$trustdir,
+        "untrustdir:s" => \$untrustdir)
+  or die("Error in command line arguments\n$0 [-d] [-i input-file] [-t 
trust-dir] [-u untrust-dir]\n");
 
 if ($infile) {
     open($inputfh, "<", $infile) or die "Failed to open $infile";
@@ -68,8 +73,7 @@ sub print_header($$)
     my $dstfile = shift;
     my $label = shift;
 
-    if ($outputdir) {
-       print $dstfile <<EOFH;
+    print $dstfile <<EOFH;
 ##
 ##  $label
 ##
@@ -77,38 +81,17 @@ sub print_header($$)
 ##  Authority (CA). It was automatically extracted from Mozilla's
 ##  root CA list (the file `certdata.txt' in security/nss).
 ##
-##  It contains a certificate trusted for server authentication.
-##
-##  Extracted from nss
-##
 ##  $generated
 ##
 EOFH
-    } else {
-       print $dstfile <<EOH;
-##
-##  ca-root-nss.crt -- Bundle of CA Root Certificates
-##
-##  This is a bundle of X.509 certificates of public Certificate
-##  Authorities (CA). These were automatically extracted from Mozilla's
-##  root CA list (the file `certdata.txt').
-##
-##  It contains certificates trusted for server authentication.
-##
-##  Extracted from nss
-##
-##  $generated
-##
-EOH
-    }
 }
 
 sub printcert($$$)
 {
     my ($fh, $label, $certdata) = @_;
     return unless $certdata;
-    open(OUT, "|openssl x509 -text -inform DER -fingerprint")
-            or die "could not pipe to openssl x509";
+    open(OUT, "|-", qw(openssl x509 -text -inform DER -fingerprint))
+       or die "could not pipe to openssl x509";
     print OUT $certdata;
     close(OUT) or die "openssl x509 failed with exit code $?";
 }
@@ -118,13 +101,11 @@ sub printcert($$$)
 sub graboct($)
 {
     my $ifh = shift;
-    my $data;
+    my $data = "";
 
     while (<$ifh>) {
        last if /^END/;
-       my (undef,@oct) = split /\\/;
-       my @bin = map(chr(oct), @oct);
-       $data .= join('', @bin);
+       $data .= join('', map { chr(oct($_)) } m/\\([0-7]{3})/g);
     }
 
     return $data;
@@ -158,18 +139,8 @@ sub grabcert($)
        {
            my $distrust_after = graboct($ifh);
            my ($year, $mon, $mday, $hour, $min, $sec) = unpack "A2A2A2A2A2A2", 
$distrust_after;
-           $distrust_after = timegm_posix( $sec, $min, $hour, $mday, $mon - 1, 
$year + 100);
-           my $time_now = time;
-           # When a CA is distrusted before its NotAfter date, issued 
certificates
-           # are valid for a maximum of 398 days after that date.
-           if ($time_now >= $distrust_after + 398 * 24 * 60 * 60) { $distrust 
= 1; }
-           if ($debug) {
-               printf STDERR "line $.: $cka_label ser #%d: distrust 398 days 
after %s, now: %s -> distrust $distrust\n", $serial,
-                       strftime("%FT%TZ", gmtime($distrust_after)), 
strftime("%FT%TZ", gmtime($time_now));
-           }
-           if ($distrust) {
-               return undef;
-           }
+           $distrust_after = timegm_posix($sec, $min, $hour, $mday, $mon - 1, 
$year + 100);
+           $expires{$cka_label."\0".$serial} = $distrust_after;
        }
     }
     return ($serial, $cka_label, $certdata);
@@ -194,8 +165,7 @@ sub grabtrust($) {
            $serial = graboct($ifh);
        }
 
-       if (/^CKA_TRUST_SERVER_AUTH CK_TRUST (\S+)$/)
-       {
+       if (/^CKA_TRUST_SERVER_AUTH CK_TRUST (\S+)$/) {
            if ($1 eq      'CKT_NSS_NOT_TRUSTED') {
                $distrust = 1;
            } elsif ($1 eq 'CKT_NSS_TRUSTED_DELEGATOR') {
@@ -216,12 +186,6 @@ sub grabtrust($) {
     return ($serial, $cka_label, $trust);
 }
 
-if (!$outputdir) {
-       print_header(*STDOUT, "");
-}
-
-my $untrusted = 0;
-
 while (<$inputfh>) {
     if (/^CKA_CLASS CK_OBJECT_CLASS CKO_CERTIFICATE/) {
        my ($serial, $label, $certdata) = grabcert($inputfh);
@@ -229,12 +193,10 @@ while (<$inputfh>) {
            warn "Certificate $label duplicated!\n";
        }
        if (defined $certdata) {
-               $certs{$label."\0".$serial} = $certdata;
-               # We store the label in a separate hash because truncating the 
key
-               # with \0 was causing garbage data after the end of the text.
-               $labels{$label."\0".$serial} = $label;
-       } else { # $certdata undefined? distrust_after in effect
-               $untrusted ++;
+           $certs{$label."\0".$serial} = $certdata;
+           # We store the label in a separate hash because truncating the key
+           # with \0 was causing garbage data after the end of the text.
+           $labels{$label."\0".$serial} = $label;
        }
     } elsif (/^CKA_CLASS CK_OBJECT_CLASS CKO_NSS_TRUST/) {
        my ($serial, $label, $trust) = grabtrust($inputfh);
@@ -254,52 +216,38 @@ sub label_to_filename(@) {
     return wantarray ? @res : $res[0];
 }
 
-# weed out untrusted certificates
-foreach my $it (keys %trusts) {
-    if (!$trusts{$it}) {
-       if (!exists($certs{$it})) {
-           warn "Found trust for nonexistent certificate $labels{$it}\n" if 
$debug;
-       } else {
-           delete $certs{$it};
-           warn "Skipping untrusted $labels{$it}\n" if $debug;
-           $untrusted++;
-       }
-    }
-}
-
-if (!$outputdir) {
-    print              "##  Untrusted certificates omitted from this bundle: 
$untrusted\n\n";
-}
-print STDERR   "##  Untrusted certificates omitted from this bundle: 
$untrusted\n";
+my $untrusted = 0;
+my $trusted = 0;
+my $now = time;
 
-my $certcount = 0;
 foreach my $it (sort {uc($a) cmp uc($b)} keys %certs) {
     my $fh = *STDOUT;
+    my $outputdir;
     my $filename;
-    if (!exists($trusts{$it})) {
-       die "Found certificate without trust block,\naborting";
-    }
-    if ($outputdir) {
-       $filename = label_to_filename($labels{$it});
-       open($fh, ">", "$outputdir/$filename") or die "Failed to open 
certificate $filename";
-       print_header($fh, $labels{$it});
+    if (exists($expires{$it}) &&
+       $now >= $expires{$it} + 398 * 24 * 60 * 60) {
+       print(STDERR "## Expired: $labels{$it}\n");
+       $outputdir = $untrustdir;
+       $untrusted++;
+    } elsif (!$trusts{$it}) {
+       print(STDERR "## Untrusted: $labels{$it}\n");
+       $outputdir = $untrustdir;
+       $untrusted++;
+    } else {
+       print(STDERR "## Trusted: $labels{$it}\n");
+       $outputdir = $trustdir;
+       $trusted++;
     }
+    $filename = label_to_filename($labels{$it});
+    open($fh, ">", "$outputdir/$filename") or die "Failed to open certificate 
$outputdir/$filename";
+    print_header($fh, $labels{$it});
     printcert($fh, $labels{$it}, $certs{$it});
     if ($outputdir) {
        close($fh) or die "Unable to close: $filename";
     } else {
        print $fh "\n\n\n";
     }
-    $certcount++;
-    print STDERR "Trusting $certcount: $labels{$it}\n" if $debug;
 }
 
-if ($certcount < 25) {
-    die "Certificate count of $certcount is implausibly low.\nAbort";
-}
-
-if (!$outputdir) {
-    print "##  Number of certificates: $certcount\n";
-    print "##  End of file.\n";
-}
-print STDERR   "##  Number of certificates: $certcount\n";
+printf STDERR "##  Trusted certificates:   %4d\n", $trusted;
+printf STDERR "##  Untrusted certificates: %4d\n", $untrusted;
diff --git a/secure/caroot/Makefile b/secure/caroot/Makefile
index ace802a906a3..d48285437f10 100644
--- a/secure/caroot/Makefile
+++ b/secure/caroot/Makefile
@@ -13,4 +13,5 @@ cleancerts: .PHONY
        @${MAKE} -C ${.CURDIR}/trusted ${.TARGET}
 
 updatecerts: .PHONY cleancerts fetchcerts
-       perl ${.CURDIR}/MAca-bundle.pl -i certdata.txt -o ${.CURDIR}/trusted
+       perl ${.CURDIR}/MAca-bundle.pl -i certdata.txt \
+           -t ${.CURDIR}/trusted -u ${.CURDIR}/untrusted
diff --git a/secure/caroot/trusted/Makefile b/secure/caroot/trusted/Makefile
index b2fe43fcb802..a47e781262b8 100644
--- a/secure/caroot/trusted/Makefile
+++ b/secure/caroot/trusted/Makefile
@@ -1,10 +1,10 @@
 BINDIR=                /usr/share/certs/trusted
 
-TRUSTED_CERTS!=        echo ${.CURDIR}/*.pem 2> /dev/null || true
+TRUSTED_CERTS!=        (cd ${.CURDIR} && echo *.pem)
 
 FILES+=         ${TRUSTED_CERTS}
 
-cleancerts:
-       @[ -z "${TRUSTED_CERTS}" ] || rm ${TRUSTED_CERTS}
+cleancerts: .PHONY
+       @(cd ${.CURDIR} && rm -f ${TRUSTED_CERTS})
 
 .include <bsd.prog.mk>
diff --git a/secure/caroot/untrusted/Makefile b/secure/caroot/untrusted/Makefile
index 19d7359ddcb9..45df0a55ebd9 100644
--- a/secure/caroot/untrusted/Makefile
+++ b/secure/caroot/untrusted/Makefile
@@ -1,7 +1,10 @@
 BINDIR=                /usr/share/certs/untrusted
 
-UNTRUSTED_CERTS!=      echo ${.CURDIR}/*.pem 2> /dev/null || true
+UNTRUSTED_CERTS!=      (cd ${.CURDIR} && echo *.pem)
 
 FILES+=         ${UNTRUSTED_CERTS}
 
+cleancerts: .PHONY
+       @(cd ${.CURDIR} && rm -f ${UNTRUSTED_CERTS})
+
 .include <bsd.prog.mk>

Reply via email to