new file mode 100644
@@ -0,0 +1,171 @@
+From cbc8b341ea4b38c388e89682b86da107a9fdc38c Mon Sep 17 00:00:00 2001
+From: Jack Lloyd <jack@randombit.net>
+Date: Sun, 15 Mar 2026 11:17:22 -0400
+Subject: [PATCH] Fix name constraint DNS check when falling back to CN
+
+When verifying name constraints which restrict the set of allowed DNS names, the
+logic would first check the Subject Alternative Name if available, but if the
+SAN is absent then it would check the CN field of the issued name since that is
+a common fallback for DNS names when the SAN is missing.
+
+However this check failed to properly canonicalize the potential DNS name in the
+CN, allowing a mis-issued cert with a mixed-case CN field and absent SAN to
+bypass any imposed excludedSubtrees rules.
+
+CVE: CVE-2026-32884
+Upstream-Status: Backport [https://github.com/randombit/botan/commit/db8baebd92bded036da6faf8bf3c324b67de9cf4]
+Signed-off-by: Gyorgy Sarvari <skandigraun@gmail.com>
+---
+ src/lib/x509/name_constraint.cpp | 16 +++++++---
+ .../Invalid_DNS_Excluded_Mixed_Case_CN.crt | 19 ++++++++++++
+ .../Root_DNS_Excluded_Mixed_Case_CN.crt | 19 ++++++++++++
+ src/tests/test_name_constraint.cpp | 29 +++++++++++++++++++
+ 4 files changed, 79 insertions(+), 4 deletions(-)
+ create mode 100644 src/tests/data/x509/name_constraint/Invalid_DNS_Excluded_Mixed_Case_CN.crt
+ create mode 100644 src/tests/data/x509/name_constraint/Root_DNS_Excluded_Mixed_Case_CN.crt
+
+diff --git a/src/lib/x509/name_constraint.cpp b/src/lib/x509/name_constraint.cpp
+index e767624..8708b95 100644
+--- a/src/lib/x509/name_constraint.cpp
++++ b/src/lib/x509/name_constraint.cpp
+@@ -19,6 +19,14 @@ namespace Botan {
+
+ class DER_Encoder;
+
++namespace {
++
++std::string canonicalize_dns_name(std::string_view name) {
++ return tolower_string(name);
++}
++
++} // namespace
++
+ std::string GeneralName::type() const {
+ switch(m_type) {
+ case NameType::Unknown:
+@@ -75,7 +83,7 @@ void GeneralName::decode_from(BER_Decoder& ber) {
+ m_type = NameType::DNS;
+ // Store it in case insensitive form so we don't have to do it
+ // again while matching
+- m_name.emplace<DNS_IDX>(tolower_string(ASN1::to_string(obj)));
++ m_name.emplace<DNS_IDX>(canonicalize_dns_name(ASN1::to_string(obj)));
+ } else if(obj.is_a(6, ASN1_Class::ContextSpecific)) {
+ m_type = NameType::URI;
+ m_name.emplace<URI_IDX>(ASN1::to_string(obj));
+@@ -174,7 +182,7 @@ GeneralName::MatchResult GeneralName::matches(const X509_Certificate& cert) cons
+ // Check CN instead...
+ for(const std::string& cn : dn.get_attribute("CN")) {
+ if(!string_to_ipv4(cn).has_value()) {
+- score.add(matches_dns(cn, constraint));
++ score.add(matches_dns(canonicalize_dns_name(cn), constraint));
+ }
+ }
+ }
+@@ -421,7 +429,7 @@ bool NameConstraints::is_permitted(const X509_Certificate& cert, bool reject_unk
+ return false;
+ }
+ } else {
+- if(!is_permitted_dns_name(cn)) {
++ if(!is_permitted_dns_name(canonicalize_dns_name(cn))) {
+ return false;
+ }
+ }
+@@ -540,7 +548,7 @@ bool NameConstraints::is_excluded(const X509_Certificate& cert, bool reject_unkn
+ return true;
+ }
+ } else {
+- if(is_excluded_dns_name(cn)) {
++ if(is_excluded_dns_name(canonicalize_dns_name(cn))) {
+ return true;
+ }
+ }
+diff --git a/src/tests/data/x509/name_constraint/Invalid_DNS_Excluded_Mixed_Case_CN.crt b/src/tests/data/x509/name_constraint/Invalid_DNS_Excluded_Mixed_Case_CN.crt
+new file mode 100644
+index 0000000..fe2c773
+--- /dev/null
++++ b/src/tests/data/x509/name_constraint/Invalid_DNS_Excluded_Mixed_Case_CN.crt
+@@ -0,0 +1,19 @@
++-----BEGIN CERTIFICATE-----
++MIIDCTCCAfGgAwIBAgIUAbjRT6B+WHVf1Wo9JdFHlHegF1cwDQYJKoZIhvcNAQEL
++BQAwFzEVMBMGA1UEAwwMVGVzdCBSb290IENBMB4XDTI2MDMxNTE0NTM1MloXDTI3
++MDMxNTE0NTM1MlowFzEVMBMGA1UEAwwMU3ViLkVWSUwuQ09NMIIBIjANBgkqhkiG
++9w0BAQEFAAOCAQ8AMIIBCgKCAQEAsn1rve+kuZJd2udT4QEnjdws+YRT+lJI+rG/
++4OpljAcxQywCc6oHDY4ouaqOKdhT9+luvqRVSEnH3JfczT/D+wZD+gBOlgUqsmXp
++QasD8HSyHWdjyaLrkZuBGICoUZTh9a7ZOPuIQ2sblxOvBm3klndpSpXJC6sJsWAk
++tyU+5WdNz+ncTC82sFSY0YQLC2QvxckEP6Jxi4I+h9vANtWXCk6CRz/kT4dY3cD4
++VBFtAy+vUVYUZnLi48fEYlMt4rP64OerY7hjw8gE+66rmUQPml4+DAek+xJtL4Sl
++Q0SGytWE9tJb1zzICv4TNvj/+IKmbK4BCu7fVjOd2C64zNFTywIDAQABo00wSzAJ
++BgNVHRMEAjAAMB0GA1UdDgQWBBS6hC1DrVlXAq/GWDYl/UQbP4TmLTAfBgNVHSME
++GDAWgBQPZ2Gp3izLTRAUzQKCKRLbM5OA2DANBgkqhkiG9w0BAQsFAAOCAQEAcWBy
++f9+tJDDj7T7z4QziZnxIfKP7x7uP9wAlYGRpKcbbW6SHAg6DA1V5GBWHendkg1hb
++Nywy7pUFgJ0xlJ7KYr76Ylfa1BTUPT+gExJWvSmsg8WSQ4q3bOMVB1qRxWDv/8KY
++ZOT03KEzLyGL6ia9r/UFw1jibxS26Ff6qCE9EhDLA/4z0D/+9QzdLATuzSn/SLSR
++wtarLaWhCfOSpfRrekYhaSndG45BCKpUd99iWt1HA4LyjZe8/8cxsRkD8klaCalG
++8pRp+PolijzmYsrEXuG22Bq2idgxHTRvw8l4rBQrSdMWuWqqTdIpFIUrXNft0a5f
++d8RmhI6PZlPL43OCxw==
++-----END CERTIFICATE-----
+diff --git a/src/tests/data/x509/name_constraint/Root_DNS_Excluded_Mixed_Case_CN.crt b/src/tests/data/x509/name_constraint/Root_DNS_Excluded_Mixed_Case_CN.crt
+new file mode 100644
+index 0000000..503206f
+--- /dev/null
++++ b/src/tests/data/x509/name_constraint/Root_DNS_Excluded_Mixed_Case_CN.crt
+@@ -0,0 +1,19 @@
++-----BEGIN CERTIFICATE-----
++MIIDGjCCAgKgAwIBAgIUJj5ZhECZYOzt5qWnoN8DMcZzlzgwDQYJKoZIhvcNAQEL
++BQAwFzEVMBMGA1UEAwwMVGVzdCBSb290IENBMB4XDTI2MDMxNTE0NTM1MloXDTM2
++MDMxMjE0NTM1MlowFzEVMBMGA1UEAwwMVGVzdCBSb290IENBMIIBIjANBgkqhkiG
++9w0BAQEFAAOCAQ8AMIIBCgKCAQEAtEqBgRHixpRodBHeqXrcttdKD3tpGG+S67tM
++WT1h+572k6EFIZ9RVfYokH32sNFNz8LNI5FwKKFTf/WaVhMdsqxaMrf/+06UzKdw
++7dKz+Q5uLGrygv6opSVqhojkjLZH78zmEER0Gba4Ac4FGq5pjafA2jtoIMDyQ2SV
++IpOy932WCajQL6IM20yHPQAsr0c0hoFP4RdbJPuiEVPfZv95VjEwwV0zMBFmiICk
++gItEDfiexBHEIvXAGNBModMHaBhUDzHZp0X7gNW/MD2ieiQGoLTyG/t7sKv0J3zV
++LnUpXVIrmHSIkGVfuc5EFOlq6OXZ/J29VjPEnVVeZARDhgyGRQIDAQABo14wXDAP
++BgNVHRMBAf8EBTADAQH/MB0GA1UdDgQWBBQPZ2Gp3izLTRAUzQKCKRLbM5OA2DAO
++BgNVHQ8BAf8EBAMCAQYwGgYDVR0eAQH/BBAwDqEMMAqCCGV2aWwuY29tMA0GCSqG
++SIb3DQEBCwUAA4IBAQA5RLYaZWVxFIKpZRM5ZBk4fDDlAvRl7NVhMeQO3DMyyIzw
++Bp7IIEgH0I6PlL4/02SzSaqUuVVXYyO1LhbgBlFnE1h818zz+jN06i0lUemuXGAo
++wnHBwLW+V/r0JBm4BbnbneCYHUFS07sfR9IjQqV9Futp2WILwieZ7Ib96xGuX96L
++f6pxYEd82rYSXa/K14HvMKXkzC3qe72V+/E1GOPZqzlbZFriYfRUHUOY6P7LjcWl
++3Z+aUQcG8vEHbJjTd+ZZzP2PICjLKgaX1acpBOUR6pelHgcUmuR3z86V/DD8TWsH
++BHDLLBxTje/8fL0PR4qo4r4F+2Cj+hXnY/TFVQ+W
++-----END CERTIFICATE-----
+diff --git a/src/tests/test_name_constraint.cpp b/src/tests/test_name_constraint.cpp
+index 61aff53..5daa343 100644
+--- a/src/tests/test_name_constraint.cpp
++++ b/src/tests/test_name_constraint.cpp
+@@ -69,6 +69,35 @@ class Name_Constraint_Tests final : public Test {
+
+ BOTAN_REGISTER_TEST("x509", "x509_path_name_constraint", Name_Constraint_Tests);
+
++// Verify that DNS constraints are case-insensitive also when falling back to the CN
++class Name_Constraint_Excluded_CN_Case_Test final : public Test {
++ public:
++ std::vector<Test::Result> run() override {
++ Test::Result result("X509v3 Name Constraints: excluded DNS with mixed-case CN and no SAN");
++
++ const Botan::X509_Certificate root(
++ Test::data_file("x509/name_constraint/Root_DNS_Excluded_Mixed_Case_CN.crt"));
++ const Botan::X509_Certificate leaf(
++ Test::data_file("x509/name_constraint/Invalid_DNS_Excluded_Mixed_Case_CN.crt"));
++
++ Botan::Certificate_Store_In_Memory trusted;
++ trusted.add_certificate(root);
++
++ const Botan::Path_Validation_Restrictions restrictions(false, 80);
++ const auto validation_time = Botan::calendar_point(2026, 6, 1, 0, 0, 0).to_std_timepoint();
++
++ const auto path_result = Botan::x509_path_validate(
++ leaf, restrictions, trusted, "" /* hostname */, Botan::Usage_Type::UNSPECIFIED, validation_time);
++
++ result.test_eq(
++ "validation result", path_result.result_string(), "Certificate does not pass name constraint");
++
++ return {result};
++ }
++};
++
++BOTAN_REGISTER_TEST("x509", "x509_name_constraint_excluded_cn_case", Name_Constraint_Excluded_CN_Case_Test);
++
+ #endif
+
+ } // namespace
@@ -7,6 +7,7 @@ SECTION = "libs"
SRC_URI = "https://botan.randombit.net/releases/Botan-${PV}.tar.xz \
file://CVE-2026-32877.patch \
file://CVE-2026-32883.patch \
+ file://CVE-2026-32884.patch \
"
SRC_URI[sha256sum] = "fde194236f6d5434f136ea0a0627f6cc9d26af8b96e9f1e1c7d8c82cd90f4f24"
Details: https://nvd.nist.gov/vuln/detail/CVE-2026-32884 The backported patch was selected based on the security.rst[1] file of the project, that mentions the date of the fix. When looked through the commits from that date, picked the one that's description matches the CVE description. The included test passed successfully (along with the other tests). [1]: https://github.com/randombit/botan/blob/master/doc/security.rst Signed-off-by: Gyorgy Sarvari <skandigraun@gmail.com> --- .../botan/botan/CVE-2026-32884.patch | 171 ++++++++++++++++++ meta-oe/recipes-crypto/botan/botan_3.10.0.bb | 1 + 2 files changed, 172 insertions(+) create mode 100644 meta-oe/recipes-crypto/botan/botan/CVE-2026-32884.patch