summaryrefslogtreecommitdiffstats
path: root/meta-oe
diff options
context:
space:
mode:
authorGyorgy Sarvari <skandigraun@gmail.com>2025-10-05 15:06:10 +0200
committerGyorgy Sarvari <skandigraun@gmail.com>2025-10-12 13:08:46 +0200
commita742bea9924133f7484a659afc0ba0ac391a67b8 (patch)
treedca5c2aedf173c5591bc98da0b66578f74022701 /meta-oe
parent6c5e7ee581567e6dc0abf0aab6f191a007bf10ef (diff)
downloadmeta-openembedded-a742bea9924133f7484a659afc0ba0ac391a67b8.tar.gz
botan: patch CVE-2024-39312
Details: https://nvd.nist.gov/vuln/detail/CVE-2024-39312 Signed-off-by: Gyorgy Sarvari <skandigraun@gmail.com>
Diffstat (limited to 'meta-oe')
-rw-r--r--meta-oe/recipes-crypto/botan/botan/0001-Address-various-name-constraint-bugs.patch749
-rw-r--r--meta-oe/recipes-crypto/botan/botan_2.19.1.bb1
2 files changed, 750 insertions, 0 deletions
diff --git a/meta-oe/recipes-crypto/botan/botan/0001-Address-various-name-constraint-bugs.patch b/meta-oe/recipes-crypto/botan/botan/0001-Address-various-name-constraint-bugs.patch
new file mode 100644
index 0000000000..e9abdaedb3
--- /dev/null
+++ b/meta-oe/recipes-crypto/botan/botan/0001-Address-various-name-constraint-bugs.patch
@@ -0,0 +1,749 @@
1From 6c39cdf1aa7efccb5dbeaf859fddf06500b78aa8 Mon Sep 17 00:00:00 2001
2From: Jack Lloyd <jack@randombit.net>
3Date: Sun, 7 Jul 2024 05:02:48 -0400
4Subject: [PATCH] Address various name constraint bugs
5
6CVE: CVE-2024-39312
7Upstream-Status: Backport [https://github.com/randombit/botan/commit/68338f5912534c74469f7f4e6e22b37aa5159952]
8
9Signed-off-by: Gyorgy Sarvari <skandigraun@gmail.com>
10---
11 src/lib/x509/asn1_alt_name.cpp | 4 +
12 src/lib/x509/name_constraint.cpp | 313 ++++++++++++++++++++++++++++-
13 src/lib/x509/pkix_types.h | 37 +++-
14 src/lib/x509/x509_ext.cpp | 80 +++-----
15 src/lib/x509/x509cert.cpp | 30 ++-
16 src/python/botan2.py | 2 +
17 src/scripts/test_python.py | 3 -
18 src/tests/test_name_constraint.cpp | 10 +-
19 8 files changed, 401 insertions(+), 78 deletions(-)
20
21diff --git a/src/lib/x509/asn1_alt_name.cpp b/src/lib/x509/asn1_alt_name.cpp
22index a347a31..574b9fb 100644
23--- a/src/lib/x509/asn1_alt_name.cpp
24+++ b/src/lib/x509/asn1_alt_name.cpp
25@@ -257,6 +257,10 @@ void AlternativeName::decode_from(BER_Decoder& source)
26 const uint32_t ip = load_be<uint32_t>(obj.bits(), 0);
27 add_attribute("IP", ipv4_to_string(ip));
28 }
29+ else if(obj.length() != 16)
30+ {
31+ throw Decoding_Error("Invalid length for IP address SAN");
32+ }
33 }
34
35 }
36diff --git a/src/lib/x509/name_constraint.cpp b/src/lib/x509/name_constraint.cpp
37index c904572..632bc8c 100644
38--- a/src/lib/x509/name_constraint.cpp
39+++ b/src/lib/x509/name_constraint.cpp
40@@ -163,31 +163,48 @@ GeneralName::MatchResult GeneralName::matches(const X509_Certificate& cert) cons
41
42 bool GeneralName::matches_dns(const std::string& nam) const
43 {
44- if(nam.size() == name().size())
45+ const std::string constraint = tolower_string(name());
46+ const std::string issued = tolower_string(nam);
47+
48+ if(nam.size() == constraint.size())
49 {
50- return tolower_string(nam) == tolower_string(name());
51+ return issued == constraint;
52 }
53- else if(name().size() > nam.size())
54+ else if(constraint.size() > nam.size())
55 {
56 // The constraint is longer than the issued name: not possibly a match
57 return false;
58 }
59- else // name.size() < nam.size()
60+ else
61 {
62- // constr is suffix of nam
63- const std::string constr = name().front() == '.' ? name() : "." + name();
64- const std::string substr = nam.substr(nam.size() - constr.size(), constr.size());
65- return tolower_string(constr) == tolower_string(substr);
66+ if(constraint.empty()) {
67+ return true;
68+ }
69+
70+ std::string substr = issued.substr(nam.size() - constraint.size(), constraint.size());
71+
72+ if(constraint.front() == '.') {
73+ return substr == constraint;
74+ } else if(substr[0] == '.') {
75+ return substr.substr(1) == constraint;
76+ } else {
77+ return substr == constraint && issued[issued.size() - constraint.size() - 1] == '.';
78 }
79 }
80+}
81
82 bool GeneralName::matches_dn(const std::string& nam) const
83 {
84 std::stringstream ss(nam);
85- std::stringstream tt(name());
86- X509_DN nam_dn, my_dn;
87-
88+ X509_DN nam_dn;
89 ss >> nam_dn;
90+ return matches_dn_obj(nam_dn);
91+ }
92+
93+bool GeneralName::matches_dn_obj(const X509_DN& nam_dn) const
94+ {
95+ std::stringstream tt(name());
96+ X509_DN my_dn;
97 tt >> my_dn;
98
99 auto attr = nam_dn.get_attributes();
100@@ -270,4 +287,278 @@ std::ostream& operator<<(std::ostream& os, const GeneralSubtree& gs)
101 os << gs.minimum() << "," << gs.maximum() << "," << gs.base();
102 return os;
103 }
104+
105+NameConstraints::NameConstraints(std::vector<GeneralSubtree>&& permitted_subtrees,
106+ std::vector<GeneralSubtree>&& excluded_subtrees) :
107+ m_permitted_subtrees(permitted_subtrees), m_excluded_subtrees(excluded_subtrees)
108+ {
109+ for(const auto& c : m_permitted_subtrees)
110+ {
111+ m_permitted_name_types.insert(c.base().type());
112+ }
113+ for(const auto& c : m_excluded_subtrees)
114+ {
115+ m_excluded_name_types.insert(c.base().type());
116+ }
117+ }
118+
119+namespace {
120+
121+bool looks_like_ipv4(const std::string& s)
122+ {
123+ try
124+ {
125+ // ignores return value
126+ string_to_ipv4(s);
127+ return true;
128+ }
129+ catch(...)
130+ {
131+ return false;
132+ }
133+ }
134+
135+}
136+
137+bool NameConstraints::is_permitted(const X509_Certificate& cert, bool reject_unknown) const {
138+ if(permitted().empty()) {
139+ return true;
140+ }
141+
142+ const auto& alt_name = cert.subject_alt_name();
143+
144+ if(reject_unknown) {
145+ if(m_permitted_name_types.find("URI") != m_permitted_name_types.end() && !alt_name.get_attribute("URI").empty()) {
146+ return false;
147+ }
148+ if(m_permitted_name_types.find("RFC822") != m_permitted_name_types.end() && !alt_name.get_attribute("RFC822").empty()) {
149+ return false;
150+ }
151+ }
152+
153+ auto is_permitted_dn = [&](const X509_DN& dn) {
154+ // If no restrictions, then immediate accept
155+ if(m_permitted_name_types.find("DN") == m_permitted_name_types.end()) {
156+ return true;
157+ }
158+
159+ if(dn.empty()) {
160+ return true;
161+ }
162+
163+ for(const auto& c : m_permitted_subtrees) {
164+ if(c.base().type() == "DN" && c.base().matches_dn_obj(dn)) {
165+ return true;
166+ }
167+ }
168+
169+ // There is at least one permitted name and we didn't match
170+ return false;
171+ };
172+
173+ auto is_permitted_dns_name = [&](const std::string& name) {
174+ if(name.empty() || name[0] == '.') {
175+ return false;
176+ }
177+
178+ // If no restrictions, then immediate accept
179+ if(m_permitted_name_types.find("DNS") == m_permitted_name_types.end()) {
180+ return true;
181+ }
182+
183+ for(const auto& c : m_permitted_subtrees) {
184+ if(c.base().type() == "DNS" && c.base().matches_dns(name)) {
185+ return true;
186+ }
187+ }
188+
189+ // There is at least one permitted name and we didn't match
190+ return false;
191+ };
192+
193+ auto is_permitted_ipv4 = [&](const std::string& ipv4) {
194+ // If no restrictions, then immediate accept
195+ if(m_permitted_name_types.find("IP") == m_permitted_name_types.end()) {
196+ return true;
197+ }
198+
199+ for(const auto& c : m_permitted_subtrees) {
200+ if(c.base().type() == "IP" && c.base().matches_ip(ipv4)) {
201+ return true;
202+ }
203+ }
204+
205+ // There is at least one permitted name and we didn't match
206+ return false;
207+ };
208+
209+ if(!is_permitted_dn(cert.subject_dn())) {
210+ return false;
211+ }
212+
213+ if(!is_permitted_dn(alt_name.dn()))
214+ {
215+ return false;
216+ }
217+
218+ for(const auto& alt_dns : alt_name.get_attribute("DNS")) {
219+ if(!is_permitted_dns_name(alt_dns)) {
220+ return false;
221+ }
222+ }
223+
224+ for(const auto& alt_ipv4 : alt_name.get_attribute("IP")) {
225+ if(!is_permitted_ipv4(alt_ipv4)) {
226+ return false;
227+ }
228+ }
229+
230+ if(!alt_name.has_items())
231+ {
232+ for(const auto& cn : cert.subject_info("Name"))
233+ {
234+ if(cn.find(".") != std::string::npos)
235+ {
236+ if(looks_like_ipv4(cn))
237+ {
238+ if(!is_permitted_ipv4(cn))
239+ {
240+ return false;
241+ }
242+ }
243+ else
244+ {
245+ if(!is_permitted_dns_name(cn))
246+ {
247+ return false;
248+ }
249+ }
250+ }
251+ }
252+ }
253+
254+ // We didn't encounter a name that doesn't have a matching constraint
255+ return true;
256 }
257+
258+bool NameConstraints::is_excluded(const X509_Certificate& cert, bool reject_unknown) const {
259+ if(excluded().empty()) {
260+ return false;
261+ }
262+
263+ const auto& alt_name = cert.subject_alt_name();
264+
265+ if(reject_unknown) {
266+ if(m_excluded_name_types.find("URI") != m_excluded_name_types.end() && !alt_name.get_attribute("URI").empty()) {
267+ return false;
268+ }
269+ if(m_excluded_name_types.find("RFC822") != m_excluded_name_types.end() && !alt_name.get_attribute("RFC822").empty()) {
270+ return false;
271+ }
272+ }
273+
274+ auto is_excluded_dn = [&](const X509_DN& dn) {
275+ // If no restrictions, then immediate accept
276+ if(m_excluded_name_types.find("DN") == m_excluded_name_types.end()) {
277+ return false;
278+ }
279+
280+ if(dn.empty()) {
281+ return false;
282+ }
283+
284+ for(const auto& c : m_excluded_subtrees) {
285+ if(c.base().type() == "DN" && c.base().matches_dn_obj(dn)) {
286+ return true;
287+ }
288+ }
289+
290+ // There is at least one excluded name and we didn't match
291+ return false;
292+ };
293+
294+ auto is_excluded_dns_name = [&](const std::string& name) {
295+ if(name.empty() || name[0] == '.') {
296+ return true;
297+ }
298+
299+ // If no restrictions, then immediate accept
300+ if(m_excluded_name_types.find("DNS") == m_excluded_name_types.end()) {
301+ return false;
302+ }
303+
304+ for(const auto& c : m_excluded_subtrees) {
305+ if(c.base().type() == "DNS" && c.base().matches_dns(name)) {
306+ return true;
307+ }
308+ }
309+
310+ // There is at least one excluded name and we didn't match
311+ return false;
312+ };
313+
314+ auto is_excluded_ipv4 = [&](const std::string& ipv4) {
315+ // If no restrictions, then immediate accept
316+ if(m_excluded_name_types.find("IP") == m_excluded_name_types.end()) {
317+ return false;
318+ }
319+
320+ for(const auto& c : m_excluded_subtrees) {
321+ if(c.base().type() == "IP" && c.base().matches_ip(ipv4)) {
322+ return true;
323+ }
324+ }
325+
326+ // There is at least one excluded name and we didn't match
327+ return false;
328+ };
329+
330+ if(is_excluded_dn(cert.subject_dn())) {
331+ return true;
332+ }
333+
334+ if(is_excluded_dn(alt_name.dn())) {
335+ return true;
336+ }
337+
338+ for(const auto& alt_dns : alt_name.get_attribute("DNS")) {
339+ if(is_excluded_dns_name(alt_dns)) {
340+ return true;
341+ }
342+ }
343+
344+ for(const auto& alt_ipv4 : alt_name.get_attribute("IP")) {
345+ if(is_excluded_ipv4(alt_ipv4)) {
346+ return true;
347+ }
348+ }
349+
350+ if(!alt_name.has_items())
351+ {
352+ for(const auto& cn : cert.subject_info("Name"))
353+ {
354+ if(cn.find(".") != std::string::npos)
355+ {
356+ if(looks_like_ipv4(cn))
357+ {
358+ if(is_excluded_ipv4(cn))
359+ {
360+ return true;
361+ }
362+ }
363+ else
364+ {
365+ if(is_excluded_dns_name(cn))
366+ {
367+ return true;
368+ }
369+ }
370+ }
371+ }
372+ }
373+
374+ // We didn't encounter a name that matched any prohibited name
375+ return false;
376+}
377+
378+} // namespace Botan
379diff --git a/src/lib/x509/pkix_types.h b/src/lib/x509/pkix_types.h
380index 9831219..0b85887 100644
381--- a/src/lib/x509/pkix_types.h
382+++ b/src/lib/x509/pkix_types.h
383@@ -191,6 +191,9 @@ class BOTAN_PUBLIC_API(2,0) Attribute final : public ASN1_Object
384 * Handles parsing GeneralName types in their BER and canonical string
385 * encoding. Allows matching GeneralNames against each other using
386 * the rules laid out in the RFC 5280, sec. 4.2.1.10 (Name Contraints).
387+*
388+* This entire class is deprecated and will be removed in a future
389+* major release
390 */
391 class BOTAN_PUBLIC_API(2,0) GeneralName final : public ASN1_Object
392 {
393@@ -213,6 +216,7 @@ class BOTAN_PUBLIC_API(2,0) GeneralName final : public ASN1_Object
394 * Creates a new GeneralName for its string format.
395 * @param str type and name, colon-separated, e.g., "DNS:google.com"
396 */
397+ BOTAN_DEPRECATED("Deprecated no replacement")
398 GeneralName(const std::string& str);
399
400 void encode_into(DER_Encoder&) const override;
401@@ -234,15 +238,17 @@ class BOTAN_PUBLIC_API(2,0) GeneralName final : public ASN1_Object
402 * @param cert certificate to be matched
403 * @return the match result
404 */
405+ BOTAN_DEPRECATED("Deprecated no replacement")
406 MatchResult matches(const X509_Certificate& cert) const;
407
408- private:
409- std::string m_type;
410- std::string m_name;
411-
412 bool matches_dns(const std::string&) const;
413 bool matches_dn(const std::string&) const;
414+ bool matches_dn_obj(const X509_DN& dn) const;
415 bool matches_ip(const std::string&) const;
416+
417+ private:
418+ std::string m_type;
419+ std::string m_name;
420 };
421
422 std::ostream& operator<<(std::ostream& os, const GeneralName& gn);
423@@ -253,6 +259,9 @@ std::ostream& operator<<(std::ostream& os, const GeneralName& gn);
424 * The Name Constraint extension adds a minimum and maximum path
425 * length to a GeneralName to form a constraint. The length limits
426 * are currently unused.
427+*
428+* This entire class is deprecated and will be removed in a future
429+* major release
430 */
431 class BOTAN_PUBLIC_API(2,0) GeneralSubtree final : public ASN1_Object
432 {
433@@ -260,6 +269,7 @@ class BOTAN_PUBLIC_API(2,0) GeneralSubtree final : public ASN1_Object
434 /**
435 * Creates an empty name constraint.
436 */
437+ BOTAN_DEPRECATED("Deprecated no replacement")
438 GeneralSubtree() : m_base(), m_minimum(0), m_maximum(std::numeric_limits<std::size_t>::max())
439 {}
440
441@@ -269,6 +279,7 @@ class BOTAN_PUBLIC_API(2,0) GeneralSubtree final : public ASN1_Object
442 * @param min minimum path length
443 * @param max maximum path length
444 */
445+ BOTAN_DEPRECATED("Deprecated no replacement")
446 GeneralSubtree(const GeneralName& base, size_t min, size_t max)
447 : m_base(base), m_minimum(min), m_maximum(max)
448 {}
449@@ -277,6 +288,7 @@ class BOTAN_PUBLIC_API(2,0) GeneralSubtree final : public ASN1_Object
450 * Creates a new name constraint for its string format.
451 * @param str name constraint
452 */
453+ BOTAN_DEPRECATED("Deprecated no replacement")
454 GeneralSubtree(const std::string& str);
455
456 void encode_into(DER_Encoder&) const override;
457@@ -325,9 +337,7 @@ class BOTAN_PUBLIC_API(2,0) NameConstraints final
458 * @param excluded_subtrees names for which the certificate is not permitted
459 */
460 NameConstraints(std::vector<GeneralSubtree>&& permitted_subtrees,
461- std::vector<GeneralSubtree>&& excluded_subtrees)
462- : m_permitted_subtrees(permitted_subtrees), m_excluded_subtrees(excluded_subtrees)
463- {}
464+ std::vector<GeneralSubtree>&& excluded_subtrees);
465
466 /**
467 * @return permitted names
468@@ -339,9 +349,22 @@ class BOTAN_PUBLIC_API(2,0) NameConstraints final
469 */
470 const std::vector<GeneralSubtree>& excluded() const { return m_excluded_subtrees; }
471
472+ /**
473+ * Return true if all of the names in the certificate are permitted
474+ */
475+ bool is_permitted(const X509_Certificate& cert, bool reject_unknown) const;
476+
477+ /**
478+ * Return true if any of the names in the certificate are excluded
479+ */
480+ bool is_excluded(const X509_Certificate& cert, bool reject_unknown) const;
481+
482 private:
483 std::vector<GeneralSubtree> m_permitted_subtrees;
484 std::vector<GeneralSubtree> m_excluded_subtrees;
485+
486+ std::set<std::string> m_permitted_name_types;
487+ std::set<std::string> m_excluded_name_types;
488 };
489
490 /**
491diff --git a/src/lib/x509/x509_ext.cpp b/src/lib/x509/x509_ext.cpp
492index e81e15c..51bc517 100644
493--- a/src/lib/x509/x509_ext.cpp
494+++ b/src/lib/x509/x509_ext.cpp
495@@ -601,27 +601,27 @@ void Name_Constraints::decode_inner(const std::vector<uint8_t>& in)
496 {
497 std::vector<GeneralSubtree> permit, exclude;
498 BER_Decoder ber(in);
499- BER_Decoder ext = ber.start_cons(SEQUENCE);
500- BER_Object per = ext.get_next_object();
501+ BER_Decoder inner = ber.start_cons(SEQUENCE);
502+ BER_Object per = inner.get_next_object();
503
504- ext.push_back(per);
505+ inner.push_back(per);
506 if(per.is_a(0, ASN1_Tag(CONSTRUCTED | CONTEXT_SPECIFIC)))
507 {
508- ext.decode_list(permit,ASN1_Tag(0),ASN1_Tag(CONSTRUCTED | CONTEXT_SPECIFIC));
509+ inner.decode_list(permit,ASN1_Tag(0),ASN1_Tag(CONSTRUCTED | CONTEXT_SPECIFIC));
510 if(permit.empty())
511 throw Encoding_Error("Empty Name Contraint list");
512 }
513
514- BER_Object exc = ext.get_next_object();
515- ext.push_back(exc);
516- if(per.is_a(1, ASN1_Tag(CONSTRUCTED | CONTEXT_SPECIFIC)))
517+ BER_Object exc = inner.get_next_object();
518+ inner.push_back(exc);
519+ if(exc.is_a(1, ASN1_Tag(CONSTRUCTED | CONTEXT_SPECIFIC)))
520 {
521- ext.decode_list(exclude,ASN1_Tag(1),ASN1_Tag(CONSTRUCTED | CONTEXT_SPECIFIC));
522+ inner.decode_list(exclude,ASN1_Tag(1),ASN1_Tag(CONSTRUCTED | CONTEXT_SPECIFIC));
523 if(exclude.empty())
524 throw Encoding_Error("Empty Name Contraint list");
525 }
526
527- ext.end_cons();
528+ inner.end_cons();
529
530 if(permit.empty() && exclude.empty())
531 throw Encoding_Error("Empty Name Contraint extension");
532@@ -650,11 +650,17 @@ void Name_Constraints::contents_to(Data_Store& subject, Data_Store&) const
533 }
534 }
535
536-void Name_Constraints::validate(const X509_Certificate& subject, const X509_Certificate& issuer,
537+void Name_Constraints::validate(const X509_Certificate& subject, const X509_Certificate& /*issuer*/,
538 const std::vector<std::shared_ptr<const X509_Certificate>>& cert_path,
539 std::vector<std::set<Certificate_Status_Code>>& cert_status,
540 size_t pos)
541 {
542+ // This is much smaller limit than in Botan3 because here name constraint checks
543+ // are much more expensive due to optimizations which would be difficult to
544+ // backport here.
545+ const size_t MAX_NC_COMPARES = (1 << 12);
546+ const size_t total_constraints = m_name_constraints.permitted().size() + m_name_constraints.excluded().size();
547+
548 if(!m_name_constraints.permitted().empty() || !m_name_constraints.excluded().empty())
549 {
550 if(!subject.is_CA_cert())
551@@ -663,54 +669,34 @@ void Name_Constraints::validate(const X509_Certificate& subject, const X509_Cert
552 }
553
554 const bool issuer_name_constraint_critical =
555- issuer.is_critical("X509v3.NameConstraints");
556+ subject.is_critical("X509v3.NameConstraints");
557
558 // Check that all subordinate certs pass the name constraint
559 for(size_t j = 0; j < pos; ++j)
560 {
561- bool permitted = m_name_constraints.permitted().empty();
562- bool failed = false;
563+ const auto& cert = cert_path.at(j);
564
565- for(auto c: m_name_constraints.permitted())
566- {
567- switch(c.base().matches(*cert_path.at(j)))
568- {
569- case GeneralName::MatchResult::NotFound:
570- case GeneralName::MatchResult::All:
571- permitted = true;
572- break;
573- case GeneralName::MatchResult::UnknownType:
574- failed = issuer_name_constraint_critical;
575- permitted = true;
576- break;
577- default:
578- break;
579- }
580- }
581+ const size_t total_names =
582+ cert->subject_dn().dn_info().size() +
583+ cert->subject_alt_name().get_attributes().size();
584
585- for(auto c: m_name_constraints.excluded())
586- {
587- switch(c.base().matches(*cert_path.at(j)))
588- {
589- case GeneralName::MatchResult::All:
590- case GeneralName::MatchResult::Some:
591- failed = true;
592- break;
593- case GeneralName::MatchResult::UnknownType:
594- failed = issuer_name_constraint_critical;
595- break;
596- default:
597- break;
598- }
599- }
600+ if(total_names * total_constraints >= MAX_NC_COMPARES) {
601+ cert_status.at(j).insert(Certificate_Status_Code::NAME_CONSTRAINT_ERROR);
602+ continue;
603+ }
604
605- if(failed || !permitted)
606- {
607+ if(!m_name_constraints.is_permitted(*cert, issuer_name_constraint_critical)) {
608 cert_status.at(j).insert(Certificate_Status_Code::NAME_CONSTRAINT_ERROR);
609- }
610+ continue;
611+ }
612+
613+ if(m_name_constraints.is_excluded(*cert, issuer_name_constraint_critical)) {
614+ cert_status.at(j).insert(Certificate_Status_Code::NAME_CONSTRAINT_ERROR);
615+ continue;
616 }
617 }
618 }
619+}
620
621 namespace {
622
623diff --git a/src/lib/x509/x509cert.cpp b/src/lib/x509/x509cert.cpp
624index 55f279c..fd1bb4c 100644
625--- a/src/lib/x509/x509cert.cpp
626+++ b/src/lib/x509/x509cert.cpp
627@@ -17,6 +17,7 @@
628 #include <botan/oids.h>
629 #include <botan/hash.h>
630 #include <botan/hex.h>
631+#include <botan/internal/stl_util.h>
632 #include <algorithm>
633 #include <sstream>
634
635@@ -788,16 +789,35 @@ bool X509_Certificate::matches_dns_name(const std::string& name) const
636 if(name.empty())
637 return false;
638
639- std::vector<std::string> issued_names = subject_info("DNS");
640+ bool is_ipv4 = false;
641
642- // Fall back to CN only if no DNS names are set (RFC 6125 sec 6.4.4)
643- if(issued_names.empty())
644+ try {
645+ string_to_ipv4(name);
646+ is_ipv4 = true;
647+ }
648+ catch(...) {}
649+
650+ std::vector<std::string> issued_names;
651+
652+ if(subject_alt_name().has_items()) {
653+ issued_names = subject_alt_name().get_attribute(is_ipv4 ? "IP" : "DNS");
654+ } else if(is_ipv4 == false) {
655+ // Use CN only if no SAN is included
656 issued_names = subject_info("Name");
657+ }
658
659 for(size_t i = 0; i != issued_names.size(); ++i)
660 {
661- if(host_wildcard_match(issued_names[i], name))
662- return true;
663+ if(is_ipv4)
664+ {
665+ if(issued_names[i] == name)
666+ return true;
667+ }
668+ else
669+ {
670+ if(host_wildcard_match(issued_names[i], name))
671+ return true;
672+ }
673 }
674
675 return false;
676diff --git a/src/python/botan2.py b/src/python/botan2.py
677index 6ae1bd6..f424303 100755
678--- a/src/python/botan2.py
679+++ b/src/python/botan2.py
680@@ -1285,6 +1285,7 @@ def _load_buf_or_file(filename, buf, file_fn, buf_fn):
681 #
682 class X509Cert(object): # pylint: disable=invalid-name
683 def __init__(self, filename=None, buf=None):
684+ self.__obj = c_void_p(0)
685 self.__obj = _load_buf_or_file(filename, buf, _DLL.botan_x509_cert_load_file, _DLL.botan_x509_cert_load)
686
687 def __del__(self):
688@@ -1464,6 +1465,7 @@ class X509Cert(object): # pylint: disable=invalid-name
689 #
690 class X509CRL(object):
691 def __init__(self, filename=None, buf=None):
692+ self.__obj = c_void_p(0)
693 self.__obj = _load_buf_or_file(filename, buf, _DLL.botan_x509_crl_load_file, _DLL.botan_x509_crl_load)
694
695 def __del__(self):
696diff --git a/src/scripts/test_python.py b/src/scripts/test_python.py
697index 2202c0e..c45b9f1 100644
698--- a/src/scripts/test_python.py
699+++ b/src/scripts/test_python.py
700@@ -474,9 +474,6 @@ ofvkP1EDmpx50fHLawIDAQAB
701 self.assertEqual(cert.issuer_dn('Organizational Unit', 0), 'bsi')
702 self.assertEqual(cert.issuer_dn('Country', 0), 'DE')
703
704- self.assertTrue(cert.hostname_match('csca-germany'))
705- self.assertFalse(cert.hostname_match('csca-slovakia'))
706-
707 self.assertEqual(cert.not_before(), 1184858838)
708 self.assertEqual(cert.not_after(), 1831907880)
709
710diff --git a/src/tests/test_name_constraint.cpp b/src/tests/test_name_constraint.cpp
711index d99e967..7e08641 100644
712--- a/src/tests/test_name_constraint.cpp
713+++ b/src/tests/test_name_constraint.cpp
714@@ -29,17 +29,17 @@ class Name_Constraint_Tests final : public Test
715 std::make_tuple(
716 "Root_Email_Name_Constraint.crt",
717 "Invalid_Email_Name_Constraint.crt",
718- "Invalid Email Name Constraint",
719+ "",
720 "Certificate does not pass name constraint"),
721 std::make_tuple(
722 "Root_DN_Name_Constraint.crt",
723 "Invalid_DN_Name_Constraint.crt",
724- "Invalid DN Name Constraint",
725+ "",
726 "Certificate does not pass name constraint"),
727 std::make_tuple(
728 "Root_DN_Name_Constraint.crt",
729 "Valid_DN_Name_Constraint.crt",
730- "Valid DN Name Constraint",
731+ "",
732 "Verified"),
733 std::make_tuple(
734 "Root_DNS_Name_Constraint.crt",
735@@ -49,12 +49,12 @@ class Name_Constraint_Tests final : public Test
736 std::make_tuple(
737 "Root_IP_Name_Constraint.crt",
738 "Valid_IP_Name_Constraint.crt",
739- "Valid IP Name Constraint",
740+ "",
741 "Verified"),
742 std::make_tuple(
743 "Root_IP_Name_Constraint.crt",
744 "Invalid_IP_Name_Constraint.crt",
745- "Invalid IP Name Constraint",
746+ "",
747 "Certificate does not pass name constraint"),
748 };
749 std::vector<Test::Result> results;
diff --git a/meta-oe/recipes-crypto/botan/botan_2.19.1.bb b/meta-oe/recipes-crypto/botan/botan_2.19.1.bb
index 6477da4dbf..71e805b655 100644
--- a/meta-oe/recipes-crypto/botan/botan_2.19.1.bb
+++ b/meta-oe/recipes-crypto/botan/botan_2.19.1.bb
@@ -9,6 +9,7 @@ SRC_URI = "https://botan.randombit.net/releases/Botan-${PV}.tar.xz \
9 file://0002-FIX-intermediates-can-sign-their-own-OCSP-responses.patch \ 9 file://0002-FIX-intermediates-can-sign-their-own-OCSP-responses.patch \
10 file://0003-FIX-missing-validation-of-authority-of-delegation-re.patch \ 10 file://0003-FIX-missing-validation-of-authority-of-delegation-re.patch \
11 file://0004-review-comments.patch \ 11 file://0004-review-comments.patch \
12 file://0001-Address-various-name-constraint-bugs.patch \
12 " 13 "
13SRC_URI[sha256sum] = "e26e00cfefda64082afdd540d3c537924f645d6a674afed2cd171005deff5560" 14SRC_URI[sha256sum] = "e26e00cfefda64082afdd540d3c537924f645d6a674afed2cd171005deff5560"
14 15