[Date Prev][Date Next] [Thread Prev][Thread Next] [Date Index] [Thread Index]

Bug#1111966: marked as done (bookworm-pu: package botan3/2.19.3+dfsg-1+deb12u1)



Your message dated Sat, 06 Sep 2025 12:14:50 +0100
with message-id <ee4c0876608d99eb3f8b333b556fbd92e7a652eb.camel@adam-barratt.org.uk>
and subject line Closing p-u requests for fixes included in 12.12
has caused the Debian Bug report #1111966,
regarding bookworm-pu: package botan3/2.19.3+dfsg-1+deb12u1
to be marked as done.

This means that you claim that the problem has been dealt with.
If this is not the case it is now your responsibility to reopen the
Bug report if necessary, and/or fix the problem forthwith.

(NB: If you are a system administrator and have no idea what this
message is talking about, this may indicate a serious mail system
misconfiguration somewhere. Please contact owner@bugs.debian.org
immediately.)


-- 
1111966: https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=1111966
Debian Bug Tracking System
Contact owner@bugs.debian.org with problems
--- Begin Message ---
Package: release.debian.org
Severity: normal
Tags: bookworm
X-Debbugs-Cc: botan3@packages.debian.org, gcs@debian.org
Control: affects -1 + src:botan3
User: release.debian.org@packages.debian.org
Usertags: pu

This update fixes various low severity security issues, debdiff
below.

Cheers,
        Moritz

diff -Nru botan-2.19.3+dfsg/debian/changelog botan-2.19.3+dfsg/debian/changelog
--- botan-2.19.3+dfsg/debian/changelog	2022-11-17 21:59:51.000000000 +0100
+++ botan-2.19.3+dfsg/debian/changelog	2025-08-24 14:57:06.000000000 +0200
@@ -1,3 +1,12 @@
+botan (2.19.3+dfsg-1+deb12u1) bookworm; urgency=medium
+
+  * CVE-2024-34702
+  * CVE-2024-34703
+  * CVE-2024-39312
+  * CVE-2024-50382/CVE-2024-50383 (Closes: #1086039)
+
+ -- Moritz Mühlenhoff <jmm@debian.org>  Sun, 24 Aug 2025 14:57:06 +0200
+
 botan (2.19.3+dfsg-1) unstable; urgency=high
 
   * New upstream release:
diff -Nru botan-2.19.3+dfsg/debian/patches/CVE-2024-34702.patch botan-2.19.3+dfsg/debian/patches/CVE-2024-34702.patch
--- botan-2.19.3+dfsg/debian/patches/CVE-2024-34702.patch	1970-01-01 01:00:00.000000000 +0100
+++ botan-2.19.3+dfsg/debian/patches/CVE-2024-34702.patch	2025-08-22 15:01:52.000000000 +0200
@@ -0,0 +1,719 @@
+From 68338f5912534c74469f7f4e6e22b37aa5159952 Mon Sep 17 00:00:00 2001
+From: Jack Lloyd <jack@randombit.net>
+Date: Sun, 7 Jul 2024 05:02:48 -0400
+Subject: [PATCH] Address various name constraint bugs
+
+--- botan-2.19.3+dfsg.orig/src/lib/x509/asn1_alt_name.cpp
++++ botan-2.19.3+dfsg/src/lib/x509/asn1_alt_name.cpp
+@@ -257,6 +257,10 @@ void AlternativeName::decode_from(BER_De
+             const uint32_t ip = load_be<uint32_t>(obj.bits(), 0);
+             add_attribute("IP", ipv4_to_string(ip));
+             }
++         else if(obj.length() != 16)
++            {
++            throw Decoding_Error("Invalid length for IP address SAN");
++            }
+          }
+ 
+       }
+--- botan-2.19.3+dfsg.orig/src/lib/x509/name_constraint.cpp
++++ botan-2.19.3+dfsg/src/lib/x509/name_constraint.cpp
+@@ -163,31 +163,48 @@ GeneralName::MatchResult GeneralName::ma
+ 
+ bool GeneralName::matches_dns(const std::string& nam) const
+    {
+-   if(nam.size() == name().size())
++   const std::string constraint = tolower_string(name());
++   const std::string issued = tolower_string(nam);
++
++   if(nam.size() == constraint.size())
+       {
+-      return tolower_string(nam) == tolower_string(name());
++      return issued == constraint;
+       }
+-   else if(name().size() > nam.size())
++   else if(constraint.size() > nam.size())
+       {
+       // The constraint is longer than the issued name: not possibly a match
+       return false;
+       }
+-   else // name.size() < nam.size()
++   else
+       {
+-      // constr is suffix of nam
+-      const std::string constr = name().front() == '.' ? name() : "." + name();
+-      const std::string substr = nam.substr(nam.size() - constr.size(), constr.size());
+-      return tolower_string(constr) == tolower_string(substr);
++      if(constraint.empty()) {
++         return true;
++      }
++
++      std::string substr = issued.substr(nam.size() - constraint.size(), constraint.size());
++
++      if(constraint.front() == '.') {
++         return substr == constraint;
++      } else if(substr[0] == '.') {
++         return substr.substr(1) == constraint;
++      } else {
++         return substr == constraint && issued[issued.size() - constraint.size() - 1] == '.';
+       }
+    }
++}
+ 
+ bool GeneralName::matches_dn(const std::string& nam) const
+    {
+    std::stringstream ss(nam);
+-   std::stringstream tt(name());
+-   X509_DN nam_dn, my_dn;
+-
++   X509_DN nam_dn;
+    ss >> nam_dn;
++   return matches_dn_obj(nam_dn);
++   }
++
++bool GeneralName::matches_dn_obj(const X509_DN& nam_dn) const
++   {
++   std::stringstream tt(name());
++   X509_DN my_dn;
+    tt >> my_dn;
+ 
+    auto attr = nam_dn.get_attributes();
+@@ -270,4 +287,278 @@ std::ostream& operator<<(std::ostream& o
+    os << gs.minimum() << "," << gs.maximum() << "," << gs.base();
+    return os;
+    }
++
++NameConstraints::NameConstraints(std::vector<GeneralSubtree>&& permitted_subtrees,
++                                 std::vector<GeneralSubtree>&& excluded_subtrees) :
++   m_permitted_subtrees(permitted_subtrees), m_excluded_subtrees(excluded_subtrees)
++   {
++   for(const auto& c : m_permitted_subtrees)
++      {
++      m_permitted_name_types.insert(c.base().type());
++      }
++   for(const auto& c : m_excluded_subtrees)
++      {
++      m_excluded_name_types.insert(c.base().type());
++      }
++   }
++
++namespace {
++
++bool looks_like_ipv4(const std::string& s)
++   {
++   try
++     {
++     // ignores return value
++     string_to_ipv4(s);
++     return true;
++     }
++   catch(...)
++      {
++      return false;
++      }
++   }
++
++}
++
++bool NameConstraints::is_permitted(const X509_Certificate& cert, bool reject_unknown) const {
++   if(permitted().empty()) {
++      return true;
++   }
++
++   const auto& alt_name = cert.subject_alt_name();
++
++   if(reject_unknown) {
++      if(m_permitted_name_types.find("URI") != m_permitted_name_types.end() && !alt_name.get_attribute("URI").empty()) {
++         return false;
++      }
++      if(m_permitted_name_types.find("RFC822") != m_permitted_name_types.end() && !alt_name.get_attribute("RFC822").empty()) {
++         return false;
++      }
++   }
++
++   auto is_permitted_dn = [&](const X509_DN& dn) {
++      // If no restrictions, then immediate accept
++      if(m_permitted_name_types.find("DN") == m_permitted_name_types.end()) {
++         return true;
++      }
++
++      if(dn.empty()) {
++         return true;
++      }
++
++      for(const auto& c : m_permitted_subtrees) {
++         if(c.base().type() == "DN" && c.base().matches_dn_obj(dn)) {
++            return true;
++         }
++      }
++
++      // There is at least one permitted name and we didn't match
++      return false;
++   };
++
++   auto is_permitted_dns_name = [&](const std::string& name) {
++      if(name.empty() || name[0] == '.') {
++         return false;
++      }
++
++      // If no restrictions, then immediate accept
++      if(m_permitted_name_types.find("DNS") == m_permitted_name_types.end()) {
++         return true;
++      }
++
++      for(const auto& c : m_permitted_subtrees) {
++         if(c.base().type() == "DNS" && c.base().matches_dns(name)) {
++            return true;
++         }
++      }
++
++      // There is at least one permitted name and we didn't match
++      return false;
++   };
++
++   auto is_permitted_ipv4 = [&](const std::string& ipv4) {
++      // If no restrictions, then immediate accept
++      if(m_permitted_name_types.find("IP") == m_permitted_name_types.end()) {
++         return true;
++      }
++
++      for(const auto& c : m_permitted_subtrees) {
++         if(c.base().type() == "IP" && c.base().matches_ip(ipv4)) {
++            return true;
++         }
++      }
++
++      // There is at least one permitted name and we didn't match
++      return false;
++   };
++
++   if(!is_permitted_dn(cert.subject_dn())) {
++      return false;
++   }
++
++   if(!is_permitted_dn(alt_name.dn()))
++      {
++      return false;
++      }
++
++   for(const auto& alt_dns : alt_name.get_attribute("DNS")) {
++      if(!is_permitted_dns_name(alt_dns)) {
++         return false;
++      }
++   }
++
++   for(const auto& alt_ipv4 : alt_name.get_attribute("IP")) {
++      if(!is_permitted_ipv4(alt_ipv4)) {
++         return false;
++      }
++   }
++
++   if(!alt_name.has_items())
++      {
++      for(const auto& cn : cert.subject_info("Name"))
++         {
++         if(cn.find(".") != std::string::npos)
++            {
++            if(looks_like_ipv4(cn))
++               {
++               if(!is_permitted_ipv4(cn))
++                  {
++                  return false;
++                  }
++               }
++            else
++               {
++               if(!is_permitted_dns_name(cn))
++                  {
++                  return false;
++                  }
++               }
++            }
++         }
++      }
++
++   // We didn't encounter a name that doesn't have a matching constraint
++   return true;
+ }
++
++bool NameConstraints::is_excluded(const X509_Certificate& cert, bool reject_unknown) const {
++   if(excluded().empty()) {
++      return false;
++   }
++
++   const auto& alt_name = cert.subject_alt_name();
++
++   if(reject_unknown) {
++      if(m_excluded_name_types.find("URI") != m_excluded_name_types.end() && !alt_name.get_attribute("URI").empty()) {
++         return false;
++      }
++      if(m_excluded_name_types.find("RFC822") != m_excluded_name_types.end() && !alt_name.get_attribute("RFC822").empty()) {
++         return false;
++      }
++   }
++
++   auto is_excluded_dn = [&](const X509_DN& dn) {
++      // If no restrictions, then immediate accept
++      if(m_excluded_name_types.find("DN") == m_excluded_name_types.end()) {
++         return false;
++      }
++
++      if(dn.empty()) {
++         return false;
++      }
++
++      for(const auto& c : m_excluded_subtrees) {
++         if(c.base().type() == "DN" && c.base().matches_dn_obj(dn)) {
++            return true;
++         }
++      }
++
++      // There is at least one excluded name and we didn't match
++      return false;
++   };
++
++   auto is_excluded_dns_name = [&](const std::string& name) {
++      if(name.empty() || name[0] == '.') {
++         return true;
++      }
++
++      // If no restrictions, then immediate accept
++      if(m_excluded_name_types.find("DNS") == m_excluded_name_types.end()) {
++         return false;
++      }
++
++      for(const auto& c : m_excluded_subtrees) {
++         if(c.base().type() == "DNS" && c.base().matches_dns(name)) {
++            return true;
++         }
++      }
++
++      // There is at least one excluded name and we didn't match
++      return false;
++   };
++
++   auto is_excluded_ipv4 = [&](const std::string& ipv4) {
++      // If no restrictions, then immediate accept
++      if(m_excluded_name_types.find("IP") == m_excluded_name_types.end()) {
++         return false;
++      }
++
++      for(const auto& c : m_excluded_subtrees) {
++         if(c.base().type() == "IP" && c.base().matches_ip(ipv4)) {
++            return true;
++         }
++      }
++
++      // There is at least one excluded name and we didn't match
++      return false;
++   };
++
++   if(is_excluded_dn(cert.subject_dn())) {
++      return true;
++   }
++
++   if(is_excluded_dn(alt_name.dn())) {
++      return true;
++   }
++
++   for(const auto& alt_dns : alt_name.get_attribute("DNS")) {
++      if(is_excluded_dns_name(alt_dns)) {
++         return true;
++      }
++   }
++
++   for(const auto& alt_ipv4 : alt_name.get_attribute("IP")) {
++      if(is_excluded_ipv4(alt_ipv4)) {
++         return true;
++      }
++   }
++
++   if(!alt_name.has_items())
++      {
++      for(const auto& cn : cert.subject_info("Name"))
++         {
++         if(cn.find(".") != std::string::npos)
++            {
++            if(looks_like_ipv4(cn))
++               {
++               if(is_excluded_ipv4(cn))
++                  {
++                  return true;
++                  }
++               }
++            else
++               {
++               if(is_excluded_dns_name(cn))
++                  {
++                  return true;
++                  }
++               }
++            }
++         }
++      }
++
++   // We didn't encounter a name that matched any prohibited name
++   return false;
++}
++
++}  // namespace Botan
+--- botan-2.19.3+dfsg.orig/src/lib/x509/pkix_types.h
++++ botan-2.19.3+dfsg/src/lib/x509/pkix_types.h
+@@ -191,6 +191,9 @@ class BOTAN_PUBLIC_API(2,0) Attribute fi
+ * Handles parsing GeneralName types in their BER and canonical string
+ * encoding. Allows matching GeneralNames against each other using
+ * the rules laid out in the RFC 5280, sec. 4.2.1.10 (Name Contraints).
++*
++* This entire class is deprecated and will be removed in a future
++* major release
+ */
+ class BOTAN_PUBLIC_API(2,0) GeneralName final : public ASN1_Object
+    {
+@@ -213,6 +216,7 @@ class BOTAN_PUBLIC_API(2,0) GeneralName
+       * Creates a new GeneralName for its string format.
+       * @param str type and name, colon-separated, e.g., "DNS:google.com"
+       */
++      BOTAN_DEPRECATED("Deprecated no replacement")
+       GeneralName(const std::string& str);
+ 
+       void encode_into(DER_Encoder&) const override;
+@@ -234,15 +238,17 @@ class BOTAN_PUBLIC_API(2,0) GeneralName
+       * @param cert certificate to be matched
+       * @return the match result
+       */
++      BOTAN_DEPRECATED("Deprecated no replacement")
+       MatchResult matches(const X509_Certificate& cert) const;
+ 
+-   private:
+-      std::string m_type;
+-      std::string m_name;
+-
+       bool matches_dns(const std::string&) const;
+       bool matches_dn(const std::string&) const;
++      bool matches_dn_obj(const X509_DN& dn) const;
+       bool matches_ip(const std::string&) const;
++
++   private:
++      std::string m_type;
++      std::string m_name;
+    };
+ 
+ std::ostream& operator<<(std::ostream& os, const GeneralName& gn);
+@@ -253,6 +259,9 @@ std::ostream& operator<<(std::ostream& o
+ * The Name Constraint extension adds a minimum and maximum path
+ * length to a GeneralName to form a constraint. The length limits
+ * are currently unused.
++*
++* This entire class is deprecated and will be removed in a future
++* major release
+ */
+ class BOTAN_PUBLIC_API(2,0) GeneralSubtree final : public ASN1_Object
+    {
+@@ -260,6 +269,7 @@ class BOTAN_PUBLIC_API(2,0) GeneralSubtr
+       /**
+       * Creates an empty name constraint.
+       */
++      BOTAN_DEPRECATED("Deprecated no replacement")
+       GeneralSubtree() : m_base(), m_minimum(0), m_maximum(std::numeric_limits<std::size_t>::max())
+       {}
+ 
+@@ -269,6 +279,7 @@ class BOTAN_PUBLIC_API(2,0) GeneralSubtr
+       * @param min minimum path length
+       * @param max maximum path length
+       */
++      BOTAN_DEPRECATED("Deprecated no replacement")
+       GeneralSubtree(const GeneralName& base, size_t min, size_t max)
+       : m_base(base), m_minimum(min), m_maximum(max)
+       {}
+@@ -277,6 +288,7 @@ class BOTAN_PUBLIC_API(2,0) GeneralSubtr
+       * Creates a new name constraint for its string format.
+       * @param str name constraint
+       */
++      BOTAN_DEPRECATED("Deprecated no replacement")
+       GeneralSubtree(const std::string& str);
+ 
+       void encode_into(DER_Encoder&) const override;
+@@ -325,9 +337,7 @@ class BOTAN_PUBLIC_API(2,0) NameConstrai
+       * @param excluded_subtrees names for which the certificate is not permitted
+       */
+       NameConstraints(std::vector<GeneralSubtree>&& permitted_subtrees,
+-                    std::vector<GeneralSubtree>&& excluded_subtrees)
+-      : m_permitted_subtrees(permitted_subtrees), m_excluded_subtrees(excluded_subtrees)
+-      {}
++                      std::vector<GeneralSubtree>&& excluded_subtrees);
+ 
+       /**
+       * @return permitted names
+@@ -339,9 +349,22 @@ class BOTAN_PUBLIC_API(2,0) NameConstrai
+       */
+       const std::vector<GeneralSubtree>& excluded() const { return m_excluded_subtrees; }
+ 
++      /**
++      * Return true if all of the names in the certificate are permitted
++      */
++      bool is_permitted(const X509_Certificate& cert, bool reject_unknown) const;
++
++      /**
++      * Return true if any of the names in the certificate are excluded
++      */
++      bool is_excluded(const X509_Certificate& cert, bool reject_unknown) const;
++
+    private:
+       std::vector<GeneralSubtree> m_permitted_subtrees;
+       std::vector<GeneralSubtree> m_excluded_subtrees;
++
++      std::set<std::string> m_permitted_name_types;
++      std::set<std::string> m_excluded_name_types;
+    };
+ 
+ /**
+--- botan-2.19.3+dfsg.orig/src/lib/x509/x509_ext.cpp
++++ botan-2.19.3+dfsg/src/lib/x509/x509_ext.cpp
+@@ -601,27 +601,27 @@ void Name_Constraints::decode_inner(cons
+    {
+    std::vector<GeneralSubtree> permit, exclude;
+    BER_Decoder ber(in);
+-   BER_Decoder ext = ber.start_cons(SEQUENCE);
+-   BER_Object per = ext.get_next_object();
++   BER_Decoder inner = ber.start_cons(SEQUENCE);
++   BER_Object per = inner.get_next_object();
+ 
+-   ext.push_back(per);
++   inner.push_back(per);
+    if(per.is_a(0, ASN1_Tag(CONSTRUCTED | CONTEXT_SPECIFIC)))
+       {
+-      ext.decode_list(permit,ASN1_Tag(0),ASN1_Tag(CONSTRUCTED | CONTEXT_SPECIFIC));
++      inner.decode_list(permit,ASN1_Tag(0),ASN1_Tag(CONSTRUCTED | CONTEXT_SPECIFIC));
+       if(permit.empty())
+          throw Encoding_Error("Empty Name Contraint list");
+       }
+ 
+-   BER_Object exc = ext.get_next_object();
+-   ext.push_back(exc);
+-   if(per.is_a(1, ASN1_Tag(CONSTRUCTED | CONTEXT_SPECIFIC)))
++   BER_Object exc = inner.get_next_object();
++   inner.push_back(exc);
++   if(exc.is_a(1, ASN1_Tag(CONSTRUCTED | CONTEXT_SPECIFIC)))
+       {
+-      ext.decode_list(exclude,ASN1_Tag(1),ASN1_Tag(CONSTRUCTED | CONTEXT_SPECIFIC));
++      inner.decode_list(exclude,ASN1_Tag(1),ASN1_Tag(CONSTRUCTED | CONTEXT_SPECIFIC));
+       if(exclude.empty())
+          throw Encoding_Error("Empty Name Contraint list");
+       }
+ 
+-   ext.end_cons();
++   inner.end_cons();
+ 
+    if(permit.empty() && exclude.empty())
+       throw Encoding_Error("Empty Name Contraint extension");
+@@ -650,11 +650,17 @@ void Name_Constraints::contents_to(Data_
+       }
+    }
+ 
+-void Name_Constraints::validate(const X509_Certificate& subject, const X509_Certificate& issuer,
++void Name_Constraints::validate(const X509_Certificate& subject, const X509_Certificate& /*issuer*/,
+       const std::vector<std::shared_ptr<const X509_Certificate>>& cert_path,
+       std::vector<std::set<Certificate_Status_Code>>& cert_status,
+       size_t pos)
+    {
++   // This is much smaller limit than in Botan3 because here name constraint checks
++   // are much more expensive due to optimizations which would be difficult to
++   // backport here.
++   const size_t MAX_NC_COMPARES = (1 << 12);
++   const size_t total_constraints = m_name_constraints.permitted().size() + m_name_constraints.excluded().size();
++
+    if(!m_name_constraints.permitted().empty() || !m_name_constraints.excluded().empty())
+       {
+       if(!subject.is_CA_cert())
+@@ -663,54 +669,34 @@ void Name_Constraints::validate(const X5
+          }
+ 
+       const bool issuer_name_constraint_critical =
+-         issuer.is_critical("X509v3.NameConstraints");
++         subject.is_critical("X509v3.NameConstraints");
+ 
+       // Check that all subordinate certs pass the name constraint
+       for(size_t j = 0; j < pos; ++j)
+          {
+-         bool permitted = m_name_constraints.permitted().empty();
+-         bool failed = false;
++         const auto& cert = cert_path.at(j);
++
++         const size_t total_names =
++            cert->subject_dn().dn_info().size() +
++            cert->subject_alt_name().get_attributes().size();
+ 
+-         for(auto c: m_name_constraints.permitted())
+-            {
+-            switch(c.base().matches(*cert_path.at(j)))
+-               {
+-               case GeneralName::MatchResult::NotFound:
+-               case GeneralName::MatchResult::All:
+-                  permitted = true;
+-                  break;
+-               case GeneralName::MatchResult::UnknownType:
+-                  failed = issuer_name_constraint_critical;
+-                  permitted = true;
+-                  break;
+-               default:
+-                  break;
+-               }
+-            }
+-
+-         for(auto c: m_name_constraints.excluded())
+-            {
+-            switch(c.base().matches(*cert_path.at(j)))
+-               {
+-               case GeneralName::MatchResult::All:
+-               case GeneralName::MatchResult::Some:
+-                  failed = true;
+-                  break;
+-               case GeneralName::MatchResult::UnknownType:
+-                  failed = issuer_name_constraint_critical;
+-                  break;
+-               default:
+-                  break;
+-               }
+-            }
++         if(total_names * total_constraints >= MAX_NC_COMPARES) {
++            cert_status.at(j).insert(Certificate_Status_Code::NAME_CONSTRAINT_ERROR);
++            continue;
++         }
++
++         if(!m_name_constraints.is_permitted(*cert, issuer_name_constraint_critical)) {
++            cert_status.at(j).insert(Certificate_Status_Code::NAME_CONSTRAINT_ERROR);
++            continue;
++         }
+ 
+-         if(failed || !permitted)
+-            {
++         if(m_name_constraints.is_excluded(*cert, issuer_name_constraint_critical)) {
+             cert_status.at(j).insert(Certificate_Status_Code::NAME_CONSTRAINT_ERROR);
+-            }
++            continue;
+          }
+       }
+    }
++}
+ 
+ namespace {
+ 
+--- botan-2.19.3+dfsg.orig/src/lib/x509/x509cert.cpp
++++ botan-2.19.3+dfsg/src/lib/x509/x509cert.cpp
+@@ -17,6 +17,7 @@
+ #include <botan/oids.h>
+ #include <botan/hash.h>
+ #include <botan/hex.h>
++#include <botan/internal/stl_util.h>
+ #include <algorithm>
+ #include <sstream>
+ 
+@@ -788,16 +789,35 @@ bool X509_Certificate::matches_dns_name(
+    if(name.empty())
+       return false;
+ 
+-   std::vector<std::string> issued_names = subject_info("DNS");
++   bool is_ipv4 = false;
+ 
+-   // Fall back to CN only if no DNS names are set (RFC 6125 sec 6.4.4)
+-   if(issued_names.empty())
++   try {
++      string_to_ipv4(name);
++      is_ipv4 = true;
++      }
++   catch(...) {}
++
++   std::vector<std::string> issued_names;
++
++   if(subject_alt_name().has_items()) {
++      issued_names = subject_alt_name().get_attribute(is_ipv4 ? "IP" : "DNS");
++   } else if(is_ipv4 == false) {
++      // Use CN only if no SAN is included
+       issued_names = subject_info("Name");
++   }
+ 
+    for(size_t i = 0; i != issued_names.size(); ++i)
+       {
+-      if(host_wildcard_match(issued_names[i], name))
+-         return true;
++      if(is_ipv4)
++         {
++         if(issued_names[i] == name)
++            return true;
++         }
++      else
++         {
++         if(host_wildcard_match(issued_names[i], name))
++            return true;
++         }
+       }
+ 
+    return false;
+--- botan-2.19.3+dfsg.orig/src/python/botan2.py
++++ botan-2.19.3+dfsg/src/python/botan2.py
+@@ -1285,6 +1285,7 @@ def _load_buf_or_file(filename, buf, fil
+ #
+ class X509Cert(object): # pylint: disable=invalid-name
+     def __init__(self, filename=None, buf=None):
++        self.__obj = c_void_p(0)
+         self.__obj = _load_buf_or_file(filename, buf, _DLL.botan_x509_cert_load_file, _DLL.botan_x509_cert_load)
+ 
+     def __del__(self):
+@@ -1464,6 +1465,7 @@ class X509Cert(object): # pylint: disabl
+ #
+ class X509CRL(object):
+     def __init__(self, filename=None, buf=None):
++        self.__obj = c_void_p(0)
+         self.__obj = _load_buf_or_file(filename, buf, _DLL.botan_x509_crl_load_file, _DLL.botan_x509_crl_load)
+ 
+     def __del__(self):
+--- botan-2.19.3+dfsg.orig/src/scripts/test_python.py
++++ botan-2.19.3+dfsg/src/scripts/test_python.py
+@@ -474,9 +474,6 @@ ofvkP1EDmpx50fHLawIDAQAB
+         self.assertEqual(cert.issuer_dn('Organizational Unit', 0), 'bsi')
+         self.assertEqual(cert.issuer_dn('Country', 0), 'DE')
+ 
+-        self.assertTrue(cert.hostname_match('csca-germany'))
+-        self.assertFalse(cert.hostname_match('csca-slovakia'))
+-
+         self.assertEqual(cert.not_before(), 1184858838)
+         self.assertEqual(cert.not_after(), 1831907880)
+ 
+--- botan-2.19.3+dfsg.orig/src/tests/test_name_constraint.cpp
++++ botan-2.19.3+dfsg/src/tests/test_name_constraint.cpp
+@@ -29,17 +29,17 @@ class Name_Constraint_Tests final : publ
+             std::make_tuple(
+                "Root_Email_Name_Constraint.crt",
+                "Invalid_Email_Name_Constraint.crt",
+-               "Invalid Email Name Constraint",
++               "",
+                "Certificate does not pass name constraint"),
+             std::make_tuple(
+                "Root_DN_Name_Constraint.crt",
+                "Invalid_DN_Name_Constraint.crt",
+-               "Invalid DN Name Constraint",
++               "",
+                "Certificate does not pass name constraint"),
+             std::make_tuple(
+                "Root_DN_Name_Constraint.crt",
+                "Valid_DN_Name_Constraint.crt",
+-               "Valid DN Name Constraint",
++               "",
+                "Verified"),
+             std::make_tuple(
+                "Root_DNS_Name_Constraint.crt",
+@@ -49,12 +49,12 @@ class Name_Constraint_Tests final : publ
+             std::make_tuple(
+                "Root_IP_Name_Constraint.crt",
+                "Valid_IP_Name_Constraint.crt",
+-               "Valid IP Name Constraint",
++               "",
+                "Verified"),
+             std::make_tuple(
+                "Root_IP_Name_Constraint.crt",
+                "Invalid_IP_Name_Constraint.crt",
+-               "Invalid IP Name Constraint",
++               "",
+                "Certificate does not pass name constraint"),
+             };
+          std::vector<Test::Result> results;
diff -Nru botan-2.19.3+dfsg/debian/patches/CVE-2024-34703.patch botan-2.19.3+dfsg/debian/patches/CVE-2024-34703.patch
--- botan-2.19.3+dfsg/debian/patches/CVE-2024-34703.patch	1970-01-01 01:00:00.000000000 +0100
+++ botan-2.19.3+dfsg/debian/patches/CVE-2024-34703.patch	2025-08-22 15:02:33.000000000 +0200
@@ -0,0 +1,22 @@
+From 94e9154c143aa5264da6254a6a1be5bc66ee2b5a Mon Sep 17 00:00:00 2001
+From: Jack Lloyd <jack@randombit.net>
+Date: Tue, 20 Feb 2024 06:32:57 -0500
+Subject: [PATCH] When decoding an arbitrary elliptic curve, set an upper bound
+ on length
+
+--- botan-2.19.3+dfsg.orig/src/lib/pubkey/ec_group/ec_group.cpp
++++ botan-2.19.3+dfsg/src/lib/pubkey/ec_group/ec_group.cpp
+@@ -334,8 +334,11 @@ std::shared_ptr<EC_Group_Data> EC_Group:
+          .end_cons()
+          .verify_end();
+ 
+-      if(p.bits() < 64 || p.is_negative() || !is_bailie_psw_probable_prime(p))
+-         throw Decoding_Error("Invalid ECC p parameter");
++      if(p.bits() < 112 || p.bits() > 1024)
++         throw Decoding_Error("ECC p parameter is invalid size");
++
++      if(p.is_negative() || !is_bailie_psw_probable_prime(p))
++         throw Decoding_Error("ECC p parameter is not a prime");
+ 
+       if(a.is_negative() || a >= p)
+          throw Decoding_Error("Invalid ECC a parameter");
diff -Nru botan-2.19.3+dfsg/debian/patches/CVE-2024-39312.patch botan-2.19.3+dfsg/debian/patches/CVE-2024-39312.patch
--- botan-2.19.3+dfsg/debian/patches/CVE-2024-39312.patch	1970-01-01 01:00:00.000000000 +0100
+++ botan-2.19.3+dfsg/debian/patches/CVE-2024-39312.patch	2025-08-22 15:03:22.000000000 +0200
@@ -0,0 +1,118 @@
+From cbbcc934043a0b1fa573a0022d002fd93f40214d Mon Sep 17 00:00:00 2001
+From: Jack Lloyd <jack@randombit.net>
+Date: Sat, 11 May 2024 14:32:36 -0400
+Subject: [PATCH] During X.509 verification, first check the signatures
+
+--- botan-2.19.3+dfsg.orig/src/lib/x509/x509path.cpp
++++ botan-2.19.3+dfsg/src/lib/x509/x509path.cpp
+@@ -51,6 +51,70 @@ PKIX::check_chain(const std::vector<std:
+    if(!cert_path[0]->allowed_usage(usage))
+       cert_status[0].insert(Certificate_Status_Code::INVALID_USAGE);
+ 
++   for(size_t i = 0; i != cert_path.size(); ++i)
++      {
++      std::set<Certificate_Status_Code>& status = cert_status.at(i);
++
++      const bool at_self_signed_root = (i == cert_path.size() - 1);
++
++      const std::shared_ptr<const X509_Certificate>& subject = cert_path[i];
++
++      const std::shared_ptr<const X509_Certificate>& issuer = cert_path[at_self_signed_root ? (i) : (i + 1)];
++
++      std::unique_ptr<Public_Key> issuer_key(issuer->subject_public_key());
++
++      // Check the signature algorithm is known
++      if(OIDS::oid2str_or_empty(subject->signature_algorithm().get_oid()).empty())
++         {
++         status.insert(Certificate_Status_Code::SIGNATURE_ALGO_UNKNOWN);
++         }
++      else
++         {
++         // only perform the following checks if the signature algorithm is known
++         if(!issuer_key)
++            {
++            status.insert(Certificate_Status_Code::CERT_PUBKEY_INVALID);
++            }
++         else
++            {
++            const Certificate_Status_Code sig_status = subject->verify_signature(*issuer_key);
++
++            if(sig_status != Certificate_Status_Code::VERIFIED)
++               status.insert(sig_status);
++
++            if(issuer_key->estimated_strength() < min_signature_algo_strength)
++               status.insert(Certificate_Status_Code::SIGNATURE_METHOD_TOO_WEAK);
++            }
++
++         // Ignore untrusted hashes on self-signed roots
++         if(trusted_hashes.size() > 0 && !at_self_signed_root)
++            {
++            if(trusted_hashes.count(subject->hash_used_for_signature()) == 0)
++               status.insert(Certificate_Status_Code::UNTRUSTED_HASH);
++            }
++         }
++      }
++
++
++   // If any of the signatures were invalid, return immediately; we know the
++   // chain is invalid and signature failure is always considered the most
++   // critical result. This does mean other problems in the certificate (eg
++   // expired) will not be reported, but we'd have to assume any such data is
++   // anyway arbitrary considering we couldn't verify the signature chain
++
++   for(size_t i = 0; i != cert_path.size(); ++i)
++      {
++      for(auto status : cert_status.at(i))
++         {
++         // This ignores errors relating to the key or hash being weak since
++         // these are somewhat advisory
++         if(static_cast<uint32_t>(status) >= 5000)
++            {
++            return cert_status;
++            }
++         }
++      }
++
+    if(cert_path[0]->is_CA_cert() == false &&
+       cert_path[0]->has_constraints(KEY_CERT_SIGN))
+       {
+@@ -114,39 +178,6 @@ PKIX::check_chain(const std::vector<std:
+       if(!issuer->is_CA_cert() && !self_signed_ee_cert)
+          status.insert(Certificate_Status_Code::CA_CERT_NOT_FOR_CERT_ISSUER);
+ 
+-      std::unique_ptr<Public_Key> issuer_key(issuer->subject_public_key());
+-
+-      // Check the signature algorithm is known
+-      if(OIDS::oid2str_or_empty(subject->signature_algorithm().get_oid()).empty())
+-         {
+-         status.insert(Certificate_Status_Code::SIGNATURE_ALGO_UNKNOWN);
+-         }
+-      else
+-         {
+-         // only perform the following checks if the signature algorithm is known
+-         if(!issuer_key)
+-            {
+-            status.insert(Certificate_Status_Code::CERT_PUBKEY_INVALID);
+-            }
+-         else
+-            {
+-            const Certificate_Status_Code sig_status = subject->verify_signature(*issuer_key);
+-
+-            if(sig_status != Certificate_Status_Code::VERIFIED)
+-               status.insert(sig_status);
+-
+-            if(issuer_key->estimated_strength() < min_signature_algo_strength)
+-               status.insert(Certificate_Status_Code::SIGNATURE_METHOD_TOO_WEAK);
+-            }
+-
+-         // Ignore untrusted hashes on self-signed roots
+-         if(trusted_hashes.size() > 0 && !at_self_signed_root)
+-            {
+-            if(trusted_hashes.count(subject->hash_used_for_signature()) == 0)
+-               status.insert(Certificate_Status_Code::UNTRUSTED_HASH);
+-            }
+-         }
+-
+       // Check cert extensions
+ 
+       if(subject->x509_version() == 1)
diff -Nru botan-2.19.3+dfsg/debian/patches/CVE-2024-50382-CVE-2024-50383.patch botan-2.19.3+dfsg/debian/patches/CVE-2024-50382-CVE-2024-50383.patch
--- botan-2.19.3+dfsg/debian/patches/CVE-2024-50382-CVE-2024-50383.patch	1970-01-01 01:00:00.000000000 +0100
+++ botan-2.19.3+dfsg/debian/patches/CVE-2024-50382-CVE-2024-50383.patch	2025-08-22 15:04:03.000000000 +0200
@@ -0,0 +1,45 @@
+From 53b0cfde580e86b03d0d27a488b6c134f662e957 Mon Sep 17 00:00:00 2001
+From: Jack Lloyd <jack@randombit.net>
+Date: Sat, 19 Oct 2024 07:43:18 -0400
+Subject: [PATCH] Add more value barriers to avoid compiler induced side
+ channels
+
+--- botan-2.19.3+dfsg.orig/src/lib/utils/donna128.h
++++ botan-2.19.3+dfsg/src/lib/utils/donna128.h
+@@ -8,6 +8,7 @@
+ #ifndef BOTAN_CURVE25519_DONNA128_H_
+ #define BOTAN_CURVE25519_DONNA128_H_
+ 
++#include <botan/internal/ct_utils.h>
+ #include <botan/mul128.h>
+ 
+ namespace Botan {
+@@ -61,7 +62,7 @@ class donna128 final
+          l += x.l;
+          h += x.h;
+ 
+-         const uint64_t carry = (l < x.l);
++         const uint64_t carry = CT::Mask<uint64_t>::is_lt(l, x.l).if_set_return(1);
+          h += carry;
+          return *this;
+          }
+@@ -69,7 +70,7 @@ class donna128 final
+       donna128& operator+=(uint64_t x)
+          {
+          l += x;
+-         const uint64_t carry = (l < x);
++         const uint64_t carry = CT::Mask<uint64_t>::is_lt(l, x).if_set_return(1);
+          h += carry;
+          return *this;
+          }
+--- botan-2.19.3+dfsg.orig/src/lib/utils/ghash/ghash.cpp
++++ botan-2.19.3+dfsg/src/lib/utils/ghash/ghash.cpp
+@@ -139,7 +139,7 @@ void GHASH::key_schedule(const uint8_t k
+          m_HM[4*j+2*i+1] = H1;
+ 
+          // GCM's bit ops are reversed so we carry out of the bottom
+-         const uint64_t carry = R * (H1 & 1);
++         const uint64_t carry = CT::Mask<uint64_t>::expand(H1 & 1).if_set_return(R);
+          H1 = (H1 >> 1) | (H0 << 63);
+          H0 = (H0 >> 1) ^ carry;
+          }
diff -Nru botan-2.19.3+dfsg/debian/patches/series botan-2.19.3+dfsg/debian/patches/series
--- botan-2.19.3+dfsg/debian/patches/series	2022-06-18 11:56:15.000000000 +0200
+++ botan-2.19.3+dfsg/debian/patches/series	2025-08-22 15:03:52.000000000 +0200
@@ -1,2 +1,6 @@
 readdir_hurd.patch
 use_python3.patch
+CVE-2024-34702.patch
+CVE-2024-34703.patch
+CVE-2024-39312.patch
+CVE-2024-50382-CVE-2024-50383.patch

--- End Message ---
--- Begin Message ---
Package: release.debian.org
Version: 12.12

Hi,

Each of the updates referenced by these requests was included in
today's 12.12 point release for bookworm.

Regards,

Adam

--- End Message ---

Reply to: