git: a1e075d14e - main - Add SA-26:73.caroot.

From: Gordon Tetlow <gordon_at_FreeBSD.org>
Date: Wed, 21 Oct 2026 06:54:30 UTC
The branch main has been updated by gordon:

URL: https://cgit.FreeBSD.org/doc/commit/?id=a1e075d14edbad8678663823095270a5cc86a432

commit a1e075d14edbad8678663823095270a5cc86a432
Author:     Gordon Tetlow <gordon@FreeBSD.org>
AuthorDate: 2026-10-21 06:33:51 +0000
Commit:     Gordon Tetlow <gordon@FreeBSD.org>
CommitDate: 2026-10-21 06:33:51 +0000

    Add SA-26:73.caroot.
    
    Approved by:    so
---
 website/data/security/advisories.toml              |     4 +
 .../advisories/FreeBSD-SA-26:73.caroot.asc         |   180 +
 .../security/patches/SA-26:73/caroot-14.4.patch    | 18618 +++++++++++++++++++
 .../patches/SA-26:73/caroot-14.4.patch.asc         |    17 +
 .../security/patches/SA-26:73/caroot-14.5.patch    |  1743 ++
 .../patches/SA-26:73/caroot-14.5.patch.asc         |    17 +
 .../security/patches/SA-26:73/caroot-15.patch      |  1743 ++
 .../security/patches/SA-26:73/caroot-15.patch.asc  |    17 +
 8 files changed, 22339 insertions(+)

diff --git a/website/data/security/advisories.toml b/website/data/security/advisories.toml
index 31ba77c237..03e4c4ec92 100644
--- a/website/data/security/advisories.toml
+++ b/website/data/security/advisories.toml
@@ -1,6 +1,10 @@
 # Sort advisories by year, month and day
 # $FreeBSD$
 
+[[advisories]]
+name = "FreeBSD-SA-26:73.caroot"
+date = "2026-10-21"
+
 [[advisories]]
 name = "FreeBSD-SA-26:72.bhyve"
 date = "2026-10-14"
diff --git a/website/static/security/advisories/FreeBSD-SA-26:73.caroot.asc b/website/static/security/advisories/FreeBSD-SA-26:73.caroot.asc
new file mode 100644
index 0000000000..a83ad7aee7
--- /dev/null
+++ b/website/static/security/advisories/FreeBSD-SA-26:73.caroot.asc
@@ -0,0 +1,180 @@
+-----BEGIN PGP SIGNED MESSAGE-----
+Hash: SHA512
+
+=============================================================================
+FreeBSD-SA-26:73.caroot                                     Security Advisory
+                                                          The FreeBSD Project
+
+Topic:          TLS certificate store update due to eMudhra compromise
+
+Category:       core
+Module:         caroot
+Announced:      2026-10-21
+Affects:        All supported versions of FreeBSD.
+Corrected:      2026-10-21 04:42:19 UTC (stable/15, 15.1-STABLE)
+                2026-10-21 04:44:35 UTC (releng/15.1, 15.1-RELEASE-p6)
+                2026-10-21 04:42:07 UTC (stable/14, 14.5-STABLE)
+                2026-10-21 04:44:07 UTC (releng/14.5, 14.5-RELEASE-p3)
+                2026-10-21 04:43:22 UTC (releng/14.4, 14.4-RELEASE-p12)
+
+For general information regarding FreeBSD Security Advisories,
+including descriptions of the fields above, security branches, and the
+following sections, please visit <URL:https://security.FreeBSD.org/>.
+
+I.   Background
+
+The root certificate bundle is the trust store that is used by OpenSSL
+programs and libraries to aid in determining whether it should trust a given
+TLS certificate.  It is generated from the root certificate list maintained
+by Mozilla's Network Security Services (NSS) project.
+
+II.  Problem Description
+
+eMudhra generated several of its TLS intermediate CA certificates using a
+flawed procedure that used trivially small private keys.  The keys have been
+recovered by several parties and publicly disclosed.  The intermediates are
+signed by the "emSign ECC Root CA - G3" root certificate, which was included
+in the trusted certificate bundle.
+
+The intermediates have been revoked, but OpenSSL does not check certificate
+revocation by default.
+
+The corrected bundle marks "emSign ECC Root CA - G3" as untrusted.  It also
+incorporates the other root certificate additions and removals in NSS
+3.131.1.
+
+III. Impact
+
+An attacker able to intercept network traffic can use the compromised
+keys to issue a certificate that impersonates any TLS server, allowing
+the traffic to be read or modified without detection.
+
+IV.  Workaround
+
+The certctl(8) utility manages the trust store.  The affected root
+certificate can be marked untrusted manually:
+
+# cp /etc/ssl/certs/14bc7599.0 /usr/share/certs/untrusted/14bc7599.pem
+# certctl rehash
+
+The file 14bc7599.0 corresponds to "emSign ECC Root CA - G3" as listed by
+`certctl list`.
+
+Software that uses its own trust store is not affected.
+
+V.   Solution
+
+Upgrade your vulnerable system to a supported FreeBSD stable or
+release / security branch (releng) dated after the correction date,
+and restart all daemons that use OpenSSL, or reboot the system.
+
+Perform one of the following:
+
+1) To update your vulnerable system installed from base system packages:
+
+Systems running a 15.0-RELEASE or later version of FreeBSD on the amd64 or
+arm64 platforms, which were installed using base system packages, can be
+updated via the pkg(8) utility:
+
+# pkg upgrade -r FreeBSD-base
+
+2) To update your vulnerable system installed from binary distribution sets:
+
+Systems running a RELEASE version of FreeBSD on the amd64 or arm64 platforms
+which were not installed using base system packages can be updated via the
+freebsd-update(8) utility:
+
+# freebsd-update fetch
+# freebsd-update install
+
+Users of FreeBSD Update should ensure that freebsd-update(8) is allowed to
+create and delete files.  This is allowed by default.
+
+3) To update your vulnerable system via a source code patch:
+
+The following patches have been verified to apply to the applicable
+FreeBSD release branches.
+
+a) Download the relevant patch from the location below, and verify the
+detached PGP signature using your PGP utility.
+
+[FreeBSD 15.1]
+# fetch https://security.FreeBSD.org/patches/SA-26:73/caroot-15.patch
+# fetch https://security.FreeBSD.org/patches/SA-26:73/caroot-15.patch.asc
+# gpg --verify caroot-15.patch.asc
+
+[FreeBSD 14.5]
+# fetch https://security.FreeBSD.org/patches/SA-26:73/caroot-14.5.patch
+# fetch https://security.FreeBSD.org/patches/SA-26:73/caroot-14.5.patch.asc
+# gpg --verify caroot-14.5.patch.asc
+
+[FreeBSD 14.4]
+# fetch https://security.FreeBSD.org/patches/SA-26:73/caroot-14.4.patch
+# fetch https://security.FreeBSD.org/patches/SA-26:73/caroot-14.4.patch.asc
+# gpg --verify caroot-14.4.patch.asc
+
+b) Apply the patch.  Execute the following commands as root:
+
+# cd /usr/src
+# patch -E -p0 < /path/to/patch
+
+c) Recompile the operating system using buildworld and installworld as
+described in <URL:https://www.FreeBSD.org/handbook/makeworld.html>.
+
+Restart all daemons that use OpenSSL, or reboot the system.
+
+VI.  Correction details
+
+This issue is corrected as of the corresponding Git commit hash in the
+following stable and release branches:
+
+Branch/path                             Hash                     Revision
+- -------------------------------------------------------------------------
+stable/15/                              adab1d807360    stable/15-n286061
+releng/15.1/                            10201c02ac78  releng/15.1-n283642
+stable/14/                              6853bad2e25a    stable/14-n275372
+releng/14.5/                            923a96f4d870  releng/14.5-n274897
+releng/14.4/                            091c8ad19f88  releng/14.4-n273796
+- -------------------------------------------------------------------------
+
+Run the following command to see which files were modified by a
+particular commit:
+
+# git show --stat <commit hash>
+
+Or visit the following URL, replacing NNNNNN with the hash:
+
+<URL:https://cgit.freebsd.org/src/commit/?id=NNNNNN>
+
+To determine the commit count in a working tree (for comparison against
+nNNNNNN in the table above), run:
+
+# git rev-list --count --first-parent HEAD
+
+VII. References
+
+<URL:https://emudhra.we-have-a-problem.com>
+
+<URL:https://bugzilla.mozilla.org/show_bug.cgi?id=2083449>
+
+<URL:https://bugzilla.mozilla.org/show_bug.cgi?id=2084101>
+
+The latest revision of this advisory is available at
+<URL:https://security.FreeBSD.org/advisories/FreeBSD-SA-26:73.caroot.asc>
+-----BEGIN PGP SIGNATURE-----
+
+iQJPBAEBCgA5FiEEthUnfoEIffdcgYM7bljekB8AGu8FAmrYXJUbFIAAAAAABAAO
+bWFudTIsMi41KzEuMTIsMCwzAAoJEG5Y3pAfABrvhbgQAJIRSTS1G6neqLDAN4jK
+lJuAYCueN6zk1+K1Ve4pkhHRrwBDKybO7UnFywT+tYlGqB4NhtHj+/0ACwPgKOz3
+vPCj9qdR0IbgwORKfwVtDMhCrcp4PFc7lPk4p8rUx28mO0B6KoQuS2DWuTWKLoH0
+pSiOZJy6rWlXoWsRfSzyHc/YaABzvcPUiu+P5wdXobutNDH+z6eB1Ix5cSL+Ran7
+xeDVxMNMr6UiaqrVBhn9U6mtgWSWtrnVsGIz9RoHkNAPQ90UU6lY70foCsLbt6up
+Uv+oMncTiXCGIJx54eN8BmuSewgoopfIMgUk7k4PHVZqTJZymSEKlbv5yyKGIGl4
+sAe/FIknJlm86K53bPaqLpi0EhxRpkPwSiOGhrU2tfoFNK9bi+XgmSjVKVLmxo+b
+dv3i2pqJBjgcivVsf9ByMjTPyKM8GAmaDc5WN12OAhufqld641aAEbNCQcwKWk9R
+ZQVhDyffGKvCvTQQVWS7sb8poBkCptvcK2JLig0D2adKfNYfhJ8gwWqjH1gJJ3zr
+jfaOZ0NcLVYalnYgUqvU4Z15AwKpZPYwFUSXPdI9CGK1SLP/yirmG+IyBvqV6tKa
+cbKFlHkLaMDsZDOgzJaIyU/1+hwHn9NiqUmtg81MKiNkJ6V3af8iBitTai54nKTl
+PUDc2S0W6JALSLf8nORNNHbs
+=jwIl
+-----END PGP SIGNATURE-----
diff --git a/website/static/security/patches/SA-26:73/caroot-14.4.patch b/website/static/security/patches/SA-26:73/caroot-14.4.patch
new file mode 100644
index 0000000000..2f4e217069
--- /dev/null
+++ b/website/static/security/patches/SA-26:73/caroot-14.4.patch
@@ -0,0 +1,18618 @@
+--- ObsoleteFiles.inc.orig
++++ ObsoleteFiles.inc
+@@ -51,6 +51,90 @@
+ #   xargs -n1 | sort | uniq -d;
+ # done
+ 
++# 20261020: Regenerate trust store
++OLD_FILES+=usr/share/certs/trusted/Atos_TrustedRoot_2011.pem
++OLD_FILES+=usr/share/certs/trusted/SecureSign_Root_CA12.pem
++OLD_FILES+=usr/share/certs/trusted/ePKI_Root_Certification_Authority.pem
++OLD_FILES+=usr/share/certs/trusted/emSign_ECC_Root_CA_-_C3.pem
++OLD_FILES+=usr/share/certs/trusted/emSign_ECC_Root_CA_-_G3.pem
++OLD_FILES+=usr/share/certs/trusted/emSign_Root_CA_-_C1.pem
++OLD_FILES+=usr/share/certs/untrusted/Entrust_Root_Certification_Authority.pem
++
++# 20260430: Regenerate trust store
++OLD_FILES+=usr/share/certs/trusted/Entrust_net_Premium_2048_Secure_Server_CA.pem
++OLD_FILES+=usr/share/certs/trusted/AffirmTrust_Premium.pem
++OLD_FILES+=usr/share/certs/trusted/Starfield_Class_2_CA.pem
++OLD_FILES+=usr/share/certs/trusted/GTS_Root_R2.pem
++OLD_FILES+=usr/share/certs/trusted/GLOBALTRUST_2020.pem
++OLD_FILES+=usr/share/certs/trusted/Secure_Global_CA.pem
++OLD_FILES+=usr/share/certs/trusted/GlobalSign_Root_CA.pem
++OLD_FILES+=usr/share/certs/trusted/CommScope_Public_Trust_RSA_Root-01.pem
++OLD_FILES+=usr/share/certs/trusted/Trustwave_Global_ECC_P384_Certification_Authority.pem
++OLD_FILES+=usr/share/certs/trusted/SwissSign_Gold_CA_-_G2.pem
++OLD_FILES+=usr/share/certs/trusted/Go_Daddy_Class_2_CA.pem
++OLD_FILES+=usr/share/certs/trusted/FIRMAPROFESIONAL_CA_ROOT-A_WEB.pem
++OLD_FILES+=usr/share/certs/trusted/Trustwave_Global_Certification_Authority.pem
++OLD_FILES+=usr/share/certs/trusted/Trustwave_Global_ECC_P256_Certification_Authority.pem
++OLD_FILES+=usr/share/certs/trusted/CommScope_Public_Trust_ECC_Root-01.pem
++OLD_FILES+=usr/share/certs/trusted/Entrust_Root_Certification_Authority_-_EC1.pem
++OLD_FILES+=usr/share/certs/trusted/Certigna.pem
++OLD_FILES+=usr/share/certs/trusted/AffirmTrust_Commercial.pem
++OLD_FILES+=usr/share/certs/trusted/DigiCert_Assured_ID_Root_CA.pem
++OLD_FILES+=usr/share/certs/trusted/Baltimore_CyberTrust_Root.pem
++OLD_FILES+=usr/share/certs/trusted/CommScope_Public_Trust_ECC_Root-02.pem
++OLD_FILES+=usr/share/certs/trusted/Entrust_Root_Certification_Authority.pem
++OLD_FILES+=usr/share/certs/trusted/AffirmTrust_Premium_ECC.pem
++OLD_FILES+=usr/share/certs/trusted/CommScope_Public_Trust_RSA_Root-02.pem
++OLD_FILES+=usr/share/certs/trusted/TeliaSonera_Root_CA_v1.pem
++OLD_FILES+=usr/share/certs/trusted/QuoVadis_Root_CA_2.pem
++OLD_FILES+=usr/share/certs/trusted/COMODO_Certification_Authority.pem
++OLD_FILES+=usr/share/certs/trusted/SecureTrust_CA.pem
++OLD_FILES+=usr/share/certs/trusted/Comodo_AAA_Services_root.pem
++OLD_FILES+=usr/share/certs/trusted/Entrust_Root_Certification_Authority_-_G2.pem
++OLD_FILES+=usr/share/certs/trusted/QuoVadis_Root_CA_3.pem
++OLD_FILES+=usr/share/certs/trusted/DigiCert_High_Assurance_EV_Root_CA.pem
++OLD_FILES+=usr/share/certs/trusted/certSIGN_ROOT_CA.pem
++OLD_FILES+=usr/share/certs/trusted/DigiCert_Global_Root_CA.pem
++OLD_FILES+=usr/share/certs/trusted/AffirmTrust_Networking.pem
++OLD_FILES+=usr/share/certs/trusted/XRamp_Global_CA_Root.pem
++OLD_FILES+=usr/share/certs/untrusted/Security_Communication_RootCA3.pem
++OLD_FILES+=usr/share/certs/untrusted/GeoTrust_Primary_Certification_Authority_-_G3.pem
++OLD_FILES+=usr/share/certs/untrusted/E-Tugra_Global_Root_CA_ECC_v3.pem
++OLD_FILES+=usr/share/certs/untrusted/Symantec_Class_2_Public_Primary_Certification_Authority_-_G4.pem
++OLD_FILES+=usr/share/certs/untrusted/EE_Certification_Centre_Root_CA.pem
++OLD_FILES+=usr/share/certs/untrusted/EC-ACC.pem
++OLD_FILES+=usr/share/certs/untrusted/Hellenic_Academic_and_Research_Institutions_RootCA_2011.pem
++OLD_FILES+=usr/share/certs/untrusted/GeoTrust_Universal_CA.pem
++OLD_FILES+=usr/share/certs/untrusted/Global_Chambersign_Root_-_2008.pem
++OLD_FILES+=usr/share/certs/untrusted/Camerfirma_Global_Chambersign_Root.pem
++OLD_FILES+=usr/share/certs/untrusted/Verisign_Class_1_Public_Primary_Certification_Authority_-_G3.pem
++OLD_FILES+=usr/share/certs/untrusted/Symantec_Class_1_Public_Primary_Certification_Authority_-_G6.pem
++OLD_FILES+=usr/share/certs/untrusted/GeoTrust_Primary_Certification_Authority_-_G2.pem
++OLD_FILES+=usr/share/certs/untrusted/TrustCor_RootCert_CA-2.pem
++OLD_FILES+=usr/share/certs/untrusted/GeoTrust_Universal_CA_2.pem
++OLD_FILES+=usr/share/certs/untrusted/Network_Solutions_Certificate_Authority.pem
++OLD_FILES+=usr/share/certs/untrusted/thawte_Primary_Root_CA.pem
++OLD_FILES+=usr/share/certs/untrusted/E-Tugra_Global_Root_CA_RSA_v3.pem
++OLD_FILES+=usr/share/certs/untrusted/GeoTrust_Primary_Certification_Authority.pem
++OLD_FILES+=usr/share/certs/untrusted/Taiwan_GRCA.pem
++OLD_FILES+=usr/share/certs/untrusted/Verisign_Class_2_Public_Primary_Certification_Authority_-_G3.pem
++OLD_FILES+=usr/share/certs/untrusted/Symantec_Class_2_Public_Primary_Certification_Authority_-_G6.pem
++OLD_FILES+=usr/share/certs/untrusted/VeriSign_Class_3_Public_Primary_Certification_Authority_-_G5.pem
++OLD_FILES+=usr/share/certs/untrusted/SwissSign_Platinum_CA_-_G2.pem
++OLD_FILES+=usr/share/certs/untrusted/TrustCor_RootCert_CA-1.pem
++OLD_FILES+=usr/share/certs/untrusted/thawte_Primary_Root_CA_-_G3.pem
++OLD_FILES+=usr/share/certs/untrusted/Camerfirma_Chambers_of_Commerce_Root.pem
++OLD_FILES+=usr/share/certs/untrusted/SwissSign_Silver_CA_-_G2.pem
++OLD_FILES+=usr/share/certs/untrusted/TrustCor_ECA-1.pem
++OLD_FILES+=usr/share/certs/untrusted/VeriSign_Class_3_Public_Primary_Certification_Authority_-_G4.pem
++OLD_FILES+=usr/share/certs/untrusted/SecureSign_RootCA11.pem
++OLD_FILES+=usr/share/certs/untrusted/Chambers_of_Commerce_Root_-_2008.pem
++OLD_FILES+=usr/share/certs/untrusted/Verisign_Class_3_Public_Primary_Certification_Authority_-_G3.pem
++OLD_FILES+=usr/share/certs/untrusted/VeriSign_Universal_Root_Certification_Authority.pem
++OLD_FILES+=usr/share/certs/untrusted/LuxTrust_Global_Root_2.pem
++OLD_FILES+=usr/share/certs/untrusted/thawte_Primary_Root_CA_-_G2.pem
++OLD_FILES+=usr/share/certs/untrusted/Symantec_Class_1_Public_Primary_Certification_Authority_-_G4.pem
++
+ # 20251218: Remove old MLINK to apmconf(8)
+ OLD_FILES+=usr/share/man/man8/apmconf.8.gz
+ 
+--- secure/caroot/MAca-bundle.pl.orig
++++ secure/caroot/MAca-bundle.pl
+@@ -1,305 +0,0 @@
+-#!/usr/bin/env perl
+-##
+-##  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 <mandree@FreeBSD.org>
+-##  All rights reserved.
+-##  Copyright (c) 2018, Allan Jude <allanjude@FreeBSD.org>
+-##
+-##  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;
+-use Getopt::Long;
+-use Time::Local qw( timegm_posix );
+-use POSIX qw( strftime );
+-
+-my $generated = '@' . 'generated';
+-my $inputfh = *STDIN;
+-my $debug = 0;
+-my $infile;
+-my $outputdir;
+-my %labels;
+-my %certs;
+-my %trusts;
+-
+-$debug++
+-    if defined $ENV{'WITH_DEBUG'}
+-	and $ENV{'WITH_DEBUG'} !~ m/(?i)^(no|0|false|)$/;
+-
+-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");
+-
+-if ($infile) {
+-    open($inputfh, "<", $infile) or die "Failed to open $infile";
+-}
+-
+-sub print_header($$)
+-{
+-    my $dstfile = shift;
+-    my $label = shift;
+-
+-    if ($outputdir) {
+-	print $dstfile <<EOFH;
+-##
+-##  $label
+-##
+-##  This is a single X.509 certificate for a public Certificate
+-##  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";
+-    print OUT $certdata;
+-    close(OUT) or die "openssl x509 failed with exit code $?";
+-}
+-
+-# converts a datastream that is to be \177-style octal constants
+-# from <> to a (binary) string and returns it
+-sub graboct($)
+-{
+-    my $ifh = shift;
+-    my $data;
+-
+-    while (<$ifh>) {
+-	last if /^END/;
+-	my (undef,@oct) = split /\\/;
+-	my @bin = map(chr(oct), @oct);
+-	$data .= join('', @bin);
+-    }
+-
+-    return $data;
+-}
+-
+-sub grabcert($)
+-{
+-    my $ifh = shift;
+-    my $certdata;
+-    my $cka_label = '';
+-    my $serial = 0;
+-    my $distrust = 0;
+-
+-    while (<$ifh>) {
+-	chomp;
+-	last if ($_ eq '');
+-
+-	if (/^CKA_LABEL UTF8 "([^"]+)"/) {
+-	    $cka_label = $1;
+-	}
+-
+-	if (/^CKA_VALUE MULTILINE_OCTAL/) {
+-	    $certdata = graboct($ifh);
+-	}
+-
+-	if (/^CKA_SERIAL_NUMBER MULTILINE_OCTAL/) {
+-	    $serial = graboct($ifh);
+-	}
+-
+-	if (/^CKA_NSS_SERVER_DISTRUST_AFTER MULTILINE_OCTAL/)
+-	{
+-	    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;
+-	    }
+-	}
+-    }
+-    return ($serial, $cka_label, $certdata);
+-}
+-
+-sub grabtrust($) {
+-    my $ifh = shift;
+-    my $cka_label;
+-    my $serial;
+-    my $maytrust = 0;
+-    my $distrust = 0;
+-
+-    while (<$ifh>) {
+-	chomp;
+-	last if ($_ eq '');
+-
+-	if (/^CKA_LABEL UTF8 "([^"]+)"/) {
+-	    $cka_label = $1;
+-	}
+-
+-	if (/^CKA_SERIAL_NUMBER MULTILINE_OCTAL/) {
+-	    $serial = graboct($ifh);
+-	}
+-
+-	if (/^CKA_TRUST_SERVER_AUTH CK_TRUST (\S+)$/)
+-	{
+-	    if ($1 eq      'CKT_NSS_NOT_TRUSTED') {
+-		$distrust = 1;
+-	    } elsif ($1 eq 'CKT_NSS_TRUSTED_DELEGATOR') {
+-		$maytrust = 1;
+-	    } elsif ($1 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);
+-}
+-
+-if (!$outputdir) {
+-	print_header(*STDOUT, "");
+-}
+-
+-my $untrusted = 0;
+-
+-while (<$inputfh>) {
+-    if (/^CKA_CLASS CK_OBJECT_CLASS CKO_CERTIFICATE/) {
+-	my ($serial, $label, $certdata) = grabcert($inputfh);
+-	if (defined $certs{$label."\0".$serial}) {
+-	    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 ++;
+-	}
+-    } elsif (/^CKA_CLASS CK_OBJECT_CLASS CKO_NSS_TRUST/) {
+-	my ($serial, $label, $trust) = grabtrust($inputfh);
+-	if (defined $trusts{$label."\0".$serial}) {
+-	    warn "Trust for $label duplicated!\n";
+-	}
+-	$trusts{$label."\0".$serial} = $trust;
+-	$labels{$label."\0".$serial} = $label;
+-    } elsif (/^CVS_ID.*Revision: ([^ ]*).*/) {
+-        print "##  Source: \"certdata.txt\" CVS revision $1\n##\n\n";
+-    }
+-}
+-
+-sub label_to_filename(@) {
+-    my @res = @_;
+-    map { s/\0.*//; s/[^[:alnum:]\-]/_/g; $_ = "$_.pem"; } @res;
+-    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 $certcount = 0;
+-foreach my $it (sort {uc($a) cmp uc($b)} keys %certs) {
+-    my $fh = *STDOUT;
+-    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});
+-    }
+-    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";
+--- secure/caroot/Makefile.orig
++++ secure/caroot/Makefile
+@@ -6,12 +6,18 @@
+ 
+ .include <bsd.obj.mk>
+ 
++# Set this to an upstream hash or tag
++# https://hg-edge.mozilla.org/projects/nss/tags
++HGVER = NSS_3_131_1_RTM
++
+ # To be used by secteam@ to update the trusted certificates
+ fetchcerts: .PHONY
+-	fetch --no-sslv3 --no-tlsv1 -o certdata.txt 'https://raw.githubusercontent.com/mozilla-firefox/firefox/refs/heads/release/security/nss/lib/ckfw/builtins/certdata.txt'
++	fetch --mirror -o certdata.txt 'https://hg-edge.mozilla.org/projects/nss/raw-file/${HGVER}/lib/ckfw/builtins/certdata.txt'
+ 
+ cleancerts: .PHONY
+ 	@${MAKE} -C ${.CURDIR}/trusted ${.TARGET}
++	@${MAKE} -C ${.CURDIR}/untrusted ${.TARGET}
+ 
+ updatecerts: .PHONY cleancerts fetchcerts
+-	perl ${.CURDIR}/MAca-bundle.pl -i certdata.txt -o ${.CURDIR}/trusted
++	perl ${.CURDIR}/ca-extract.pl -i certdata.txt \
++	    -t ${.CURDIR}/trusted -u ${.CURDIR}/untrusted
+--- /dev/null
++++ secure/caroot/ca-extract.pl
+@@ -0,0 +1,253 @@
++#!/usr/bin/env perl
++#-
++# SPDX-License-Identifier: BSD-2-Clause
++#
++#  Copyright (c) 2011, 2013 Matthias Andree <mandree@FreeBSD.org>
++#  Copyright (c) 2018 Allan Jude <allanjude@FreeBSD.org>
++#  Copyright (c) 2025 Dag-Erling Smørgrav <des@FreeBSD.org>
++#
++# Redistribution and use in source and binary forms, with or without
++# modification, are permitted provided that the following conditions
++# are met:
++# 1. Redistributions of source code must retain the above copyright
++#    notice, this list of conditions and the following disclaimer.
++# 2. 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 AUTHOR 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 AUTHOR 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.
++#
++#
++# ca-extract.pl -- Extract trusted and untrusted certificates from
++# Mozilla's certdata.txt.
++#
++# Rewritten in September 2011 by Matthias Andree to heed untrust
++#
++
++use strict;
++use warnings;
++use Carp;
++use MIME::Base64;
++use Getopt::Long;
++use Time::Local qw( timegm_posix );
++
++my $generated = '@' . 'generated';
++my $inputfh = *STDIN;
++my $debug = 0;
++my $infile;
++my $trustdir = "trusted";
++my $untrustdir = "untrusted";
++my %labels;
++my %certs;
++my %trusts;
++my %expires;
++
++$debug++
++    if defined $ENV{'WITH_DEBUG'}
++	and $ENV{'WITH_DEBUG'} !~ m/(?i)^(no|0|false|)$/;
++
++GetOptions (
++	"debug+" => \$debug,
++	"infile:s" => \$infile,
++	"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";
++}
++
++sub print_header($$)
++{
++    my $dstfile = shift;
++    my $label = shift;
++
++    print $dstfile <<EOFH;
++##
++##  $label
++##
++##  This is a single X.509 certificate for a public Certificate
++##  Authority (CA). It was automatically extracted from Mozilla's
++##  root CA list (the file `certdata.txt' in security/nss)
++##  licensed under the MPL 2.0, http://mozilla.org/MPL/2.0/.
++##
++##  $generated
++##
++EOFH
++}
++
++sub printcert($$$)
++{
++    my ($fh, $label, $certdata) = @_;
++    return unless $certdata;
++    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 $?";
++}
++
++# converts a datastream that is to be \177-style octal constants
++# from <> to a (binary) string and returns it
++sub graboct($)
++{
++    my $ifh = shift;
++    my $data = "";
++
++    while (<$ifh>) {
++	last if /^END/;
++	$data .= join('', map { chr(oct($_)) } m/\\([0-7]{3})/g);
++    }
++
++    return $data;
++}
++
++sub grabcert($)
++{
++    my $ifh = shift;
++    my $certdata;
++    my $cka_label = '';
++    my $serial = 0;
++    my $distrust = 0;
++
++    while (<$ifh>) {
++	chomp;
++	last if ($_ eq '');
++
++	if (/^CKA_LABEL UTF8 "([^"]+)"/) {
++	    $cka_label = $1;
++	}
++
++	if (/^CKA_VALUE MULTILINE_OCTAL/) {
++	    $certdata = graboct($ifh);
++	}
++
++	if (/^CKA_SERIAL_NUMBER MULTILINE_OCTAL/) {
++	    $serial = graboct($ifh);
++	}
++
++	if (/^CKA_NSS_SERVER_DISTRUST_AFTER MULTILINE_OCTAL/)
++	{
++	    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);
++	    $expires{$cka_label."\0".$serial} = $distrust_after;
++	}
++    }
++    return ($serial, $cka_label, $certdata);
++}
++
++sub grabtrust($) {
++    my $ifh = shift;
++    my $cka_label;
++    my $serial;
++    my $maytrust = 0;
++    my $distrust = 0;
++
++    while (<$ifh>) {
++	chomp;
++	last if ($_ eq '');
++
++	if (/^CKA_LABEL UTF8 "([^"]+)"/) {
++	    $cka_label = $1;
++	}
++
++	if (/^CKA_SERIAL_NUMBER MULTILINE_OCTAL/) {
++	    $serial = graboct($ifh);
++	}
++
++	if (/^CKA_TRUST_SERVER_AUTH CK_TRUST (\S+)$/) {
++	    if ($1 eq      'CKT_NSS_NOT_TRUSTED') {
++		$distrust = 1;
++	    } elsif ($1 eq 'CKT_NSS_TRUSTED_DELEGATOR') {
++		$maytrust = 1;
++	    } elsif ($1 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 (<$inputfh>) {
++    if (/^CKA_CLASS CK_OBJECT_CLASS CKO_CERTIFICATE/) {
++	my ($serial, $label, $certdata) = grabcert($inputfh);
++	if (defined $certs{$label."\0".$serial}) {
++	    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;
++	}
++    } elsif (/^CKA_CLASS CK_OBJECT_CLASS CKO_NSS_TRUST/) {
++	my ($serial, $label, $trust) = grabtrust($inputfh);
++	if (defined $trusts{$label."\0".$serial}) {
++	    warn "Trust for $label duplicated!\n";
++	}
++	$trusts{$label."\0".$serial} = $trust;
++	$labels{$label."\0".$serial} = $label;
++    } elsif (/^CVS_ID.*Revision: ([^ ]*).*/) {
++        print "##  Source: \"certdata.txt\" CVS revision $1\n##\n\n";
++    }
++}
++
++sub label_to_filename(@) {
++    my @res = @_;
++    map { s/\0.*//; s/[^[:alnum:]\-]/_/g; $_ = "$_.pem"; } @res;
++    return wantarray ? @res : $res[0];
++}
++
++my $untrusted = 0;
++my $trusted = 0;
++my $now = time;
++
++foreach my $it (sort {uc($a) cmp uc($b)} keys %certs) {
++    my $fh = *STDOUT;
++    my $outputdir;
++    my $filename;
++    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";
++    }
++}
++
++printf STDERR "##  Trusted certificates:   %4d\n", $trusted;
++printf STDERR "##  Untrusted certificates: %4d\n", $untrusted;
+--- secure/caroot/trusted/ACCVRAIZ1.pem.orig
++++ secure/caroot/trusted/ACCVRAIZ1.pem
+@@ -3,11 +3,8 @@
+ ##
+ ##  This is a single X.509 certificate for a public Certificate
+ ##  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
++##  root CA list (the file `certdata.txt' in security/nss)
++##  licensed under the MPL 2.0, http://mozilla.org/MPL/2.0/.
+ ##
+ ##  @generated
+ ##
+@@ -16,11 +13,11 @@
+         Version: 3 (0x2)
+         Serial Number: 6828503384748696800 (0x5ec3b7a6437fa4e0)
+         Signature Algorithm: sha1WithRSAEncryption
+-        Issuer: CN = ACCVRAIZ1, OU = PKIACCV, O = ACCV, C = ES
++        Issuer: CN=ACCVRAIZ1, OU=PKIACCV, O=ACCV, C=ES
+         Validity
+             Not Before: May  5 09:37:37 2011 GMT
+             Not After : Dec 31 09:37:37 2030 GMT
+-        Subject: CN = ACCVRAIZ1, OU = PKIACCV, O = ACCV, C = ES
++        Subject: CN=ACCVRAIZ1, OU=PKIACCV, O=ACCV, C=ES
+         Subject Public Key Info:
+             Public Key Algorithm: rsaEncryption
+                 Public-Key: (4096 bit)
+@@ -79,6 +76,7 @@
+             X509v3 CRL Distribution Points: 
+                 Full Name:
+                   URI:http://www.accv.es/fileadmin/Archivos/certificados/raizaccv1_der.crl
++
+             X509v3 Key Usage: critical
+                 Certificate Sign, CRL Sign
+             X509v3 Subject Alternative Name: 
+--- secure/caroot/trusted/AC_RAIZ_FNMT-RCM.pem.orig
++++ secure/caroot/trusted/AC_RAIZ_FNMT-RCM.pem
+@@ -3,11 +3,8 @@
+ ##
+ ##  This is a single X.509 certificate for a public Certificate
+ ##  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
++##  root CA list (the file `certdata.txt' in security/nss)
++##  licensed under the MPL 2.0, http://mozilla.org/MPL/2.0/.
+ ##
+ ##  @generated
+ ##
+@@ -17,11 +14,11 @@
+         Serial Number:
+             5d:93:8d:30:67:36:c8:06:1d:1a:c7:54:84:69:07
+         Signature Algorithm: sha256WithRSAEncryption
+-        Issuer: C = ES, O = FNMT-RCM, OU = AC RAIZ FNMT-RCM
++        Issuer: C=ES, O=FNMT-RCM, OU=AC RAIZ FNMT-RCM
+         Validity
+             Not Before: Oct 29 15:59:56 2008 GMT
+             Not After : Jan  1 00:00:00 2030 GMT
+-        Subject: C = ES, O = FNMT-RCM, OU = AC RAIZ FNMT-RCM
++        Subject: C=ES, O=FNMT-RCM, OU=AC RAIZ FNMT-RCM
+         Subject Public Key Info:
+             Public Key Algorithm: rsaEncryption
+                 Public-Key: (4096 bit)
+--- secure/caroot/trusted/AC_RAIZ_FNMT-RCM_SERVIDORES_SEGUROS.pem.orig
++++ secure/caroot/trusted/AC_RAIZ_FNMT-RCM_SERVIDORES_SEGUROS.pem
+@@ -3,11 +3,8 @@
+ ##
+ ##  This is a single X.509 certificate for a public Certificate
+ ##  Authority (CA). It was automatically extracted from Mozilla's
+-##  root CA list (the file `certdata.txt' in security/nss).
+-##
*** 21429 LINES SKIPPED ***