git: a1e075d14e - main - Add SA-26:73.caroot.
- Go to: [ bottom of page ] [ top of archives ] [ this month ]
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 ***