Botan 3.12.0
Crypto and TLS for C&
name_constraint.cpp
Go to the documentation of this file.
1/*
2* X.509 Name Constraint
3* (C) 2015 Kai Michaelis
4* 2024,2026 Jack Lloyd
5*
6* Botan is released under the Simplified BSD License (see license.txt)
7*/
8
9#include <botan/pkix_types.h>
10
11#include <botan/ber_dec.h>
12#include <botan/x509cert.h>
13#include <botan/internal/concat_util.h>
14#include <botan/internal/fmt.h>
15#include <botan/internal/int_utils.h>
16#include <botan/internal/loadstor.h>
17#include <botan/internal/parsing.h>
18#include <botan/internal/stl_util.h>
19#include <botan/internal/x509_utils.h>
20#include <span>
21
22namespace Botan {
23
24class DER_Encoder;
25
26namespace {
27
28std::string canonicalize_dns_name(std::string_view name) {
29 return tolower_string(name);
30}
31
32} // namespace
33
34std::string GeneralName::type() const {
35 switch(m_type) {
37 throw Encoding_Error("Could not convert unknown NameType to string");
39 return "RFC822";
40 case NameType::DNS:
41 return "DNS";
42 case NameType::URI:
43 return "URI";
44 case NameType::DN:
45 return "DN";
46 case NameType::IPv4:
47 return "IP";
48 case NameType::IPv6:
49 return "IPv6";
50 case NameType::Other:
51 return "Other";
52 }
53
55}
56
58 return GeneralName::make<RFC822_IDX>(email);
59}
60
61GeneralName GeneralName::dns(std::string_view dns) {
62 return GeneralName::make<DNS_IDX>(dns);
63}
64
65GeneralName GeneralName::uri(std::string_view uri) {
66 return GeneralName::make<URI_IDX>(uri);
67}
68
70 return GeneralName::make<DN_IDX>(std::move(dn));
71}
72
76
77GeneralName GeneralName::ipv4_address(uint32_t ipv4, uint32_t mask) {
78 auto subnet = IPv4Subnet::from_address_and_mask(ipv4, mask);
79 if(!subnet.has_value()) {
80 throw Invalid_Argument("IPv4 subnet mask is not a contiguous CIDR prefix");
81 }
82 return GeneralName::make<IPV4_IDX>(*subnet);
83}
84
86 return GeneralName::make<IPV4_IDX>(IPv4Subnet::host(ipv4));
87}
88
90 return GeneralName::make<IPV4_IDX>(subnet);
91}
92
94 return GeneralName::make<IPV6_IDX>(IPv6Subnet::host(ipv6));
95}
96
98 return GeneralName::make<IPV6_IDX>(subnet);
99}
100
101std::string GeneralName::name() const {
102 const size_t index = m_name.index();
103
104 if(index == RFC822_IDX) {
105 return std::get<RFC822_IDX>(m_name);
106 } else if(index == DNS_IDX) {
107 return std::get<DNS_IDX>(m_name);
108 } else if(index == URI_IDX) {
109 return std::get<URI_IDX>(m_name);
110 } else if(index == DN_IDX) {
111 return std::get<DN_IDX>(m_name).to_string();
112 } else if(index == IPV4_IDX) {
113 const auto& subnet = std::get<IPV4_IDX>(m_name);
114 return subnet.is_host() ? subnet.address().to_string() : subnet.to_string();
115 } else if(index == IPV6_IDX) {
116 const auto& subnet = std::get<IPV6_IDX>(m_name);
117 return subnet.is_host() ? subnet.address().to_string() : subnet.to_string();
118 } else {
120 }
121}
122
123std::vector<uint8_t> GeneralName::binary_name() const {
124 return std::visit(Botan::overloaded{
125 [](const Botan::X509_DN& dn) { return Botan::ASN1::put_in_sequence(dn.get_bits()); },
126 [](const IPv4Subnet& subnet) { return subnet.serialize(); },
127 [](const IPv6Subnet& subnet) { return subnet.serialize(); },
128 [](const auto&) -> std::vector<uint8_t> {
129 throw Invalid_State("Cannot convert GeneralName to binary string");
130 },
131 },
132 m_name);
133}
134
136 throw Not_Implemented("GeneralName encoding");
137}
138
140 const BER_Object obj = ber.get_next_object();
141
143 m_type = NameType::Other;
144 } else if(obj.is_a(1, ASN1_Class::ContextSpecific)) {
145 m_type = NameType::RFC822;
146 m_name.emplace<RFC822_IDX>(ASN1::to_string(obj));
147 } else if(obj.is_a(2, ASN1_Class::ContextSpecific)) {
148 // Store it in case insensitive form so we don't have to do it
149 // again while matching
150 auto dns = canonicalize_dns_name(ASN1::to_string(obj));
151 // An empty DNS subtree has no clear meaning, reject immediately
152 if(dns.empty()) {
153 throw Decoding_Error("Empty DNS name in GeneralName");
154 }
155 m_type = NameType::DNS;
156 m_name.emplace<DNS_IDX>(std::move(dns));
157 } else if(obj.is_a(6, ASN1_Class::ContextSpecific)) {
158 m_type = NameType::URI;
159 m_name.emplace<URI_IDX>(ASN1::to_string(obj));
161 X509_DN dn;
162 BER_Decoder dec(obj, ber.limits());
163 dn.decode_from(dec);
164 m_type = NameType::DN;
165 m_name.emplace<DN_IDX>(dn);
166 } else if(obj.is_a(7, ASN1_Class::ContextSpecific)) {
167 if(obj.length() == 8) {
168 const auto addr_and_mask = std::span<const uint8_t, 8>{obj.bits(), 8};
169 auto subnet = IPv4Subnet::from_address_and_mask(addr_and_mask);
170 if(!subnet.has_value()) {
171 throw Decoding_Error("IPv4 name constraint mask is not a contiguous CIDR prefix");
172 }
173
174 m_type = NameType::IPv4;
175 m_name.emplace<IPV4_IDX>(*subnet);
176 } else if(obj.length() == 32) {
177 const auto addr_and_mask = std::span<const uint8_t, 32>{obj.bits(), 32};
178 auto subnet = IPv6Subnet::from_address_and_mask(addr_and_mask);
179 if(!subnet.has_value()) {
180 throw Decoding_Error("IPv6 name constraint mask is not a contiguous CIDR prefix");
181 }
182
183 m_type = NameType::IPv6;
184 m_name.emplace<IPV6_IDX>(*subnet);
185 } else {
186 throw Decoding_Error("Invalid IP name constraint size " + std::to_string(obj.length()));
187 }
188 } else {
189 m_type = NameType::Unknown;
190 }
191}
192
193bool GeneralName::matches_dns(const std::string& dns_name) const {
194 if(m_type == NameType::DNS) {
195 const auto& constraint = std::get<DNS_IDX>(m_name);
196 return matches_dns(dns_name, constraint);
197 }
198 return false;
199}
200
201bool GeneralName::matches_ipv4(uint32_t ip) const {
202 if(m_type == NameType::IPv4) {
203 return std::get<IPV4_IDX>(m_name).contains(IPv4Address(ip));
204 }
205 return false;
206}
207
209 if(m_type == NameType::IPv6) {
210 return std::get<IPV6_IDX>(m_name).contains(ip);
211 }
212 return false;
213}
214
215bool GeneralName::matches_dn(const X509_DN& dn) const {
216 if(m_type == NameType::DN) {
217 const X509_DN& constraint = std::get<DN_IDX>(m_name);
218 return matches_dn(dn, constraint);
219 }
220 return false;
221}
222
224 class MatchScore final {
225 public:
226 MatchScore() : m_any(false), m_some(false), m_all(true) {}
227
228 void add(bool m) {
229 m_any = true;
230 m_some |= m;
231 m_all &= m;
232 }
233
234 MatchResult result() const {
235 if(!m_any) {
237 } else if(m_all) {
238 return MatchResult::All;
239 } else if(m_some) {
240 return MatchResult::Some;
241 } else {
242 return MatchResult::None;
243 }
244 }
245
246 private:
247 bool m_any;
248 bool m_some;
249 bool m_all;
250 };
251
252 const X509_DN& dn = cert.subject_dn();
253 const AlternativeName& alt_name = cert.subject_alt_name();
254
255 MatchScore score;
256
257 if(m_type == NameType::DNS) {
258 const auto& constraint = std::get<DNS_IDX>(m_name);
259
260 const auto& alt_names = alt_name.dns();
261
262 for(const std::string& dns : alt_names) {
263 score.add(matches_dns(dns, constraint));
264 }
265
266 if(alt_name.count() == 0) {
267 // Check CN instead...
268 for(const std::string& cn : dn.get_attribute("CN")) {
269 if(!string_to_ipv4(cn).has_value()) {
270 score.add(matches_dns(canonicalize_dns_name(cn), constraint));
271 }
272 }
273 }
274 } else if(m_type == NameType::DN) {
275 const X509_DN& constraint = std::get<DN_IDX>(m_name);
276 score.add(matches_dn(dn, constraint));
277
278 for(const auto& alt_dn : alt_name.directory_names()) {
279 score.add(matches_dn(alt_dn, constraint));
280 }
281 } else if(m_type == NameType::IPv4) {
282 const auto& subnet = std::get<IPV4_IDX>(m_name);
283
284 if(alt_name.count() == 0) {
285 // Check CN instead...
286 for(const std::string& cn : dn.get_attribute("CN")) {
287 if(auto ipv4 = string_to_ipv4(cn)) {
288 score.add(subnet.contains(IPv4Address(*ipv4)));
289 }
290 }
291 } else {
292 for(const uint32_t ipv4 : alt_name.ipv4_address()) {
293 score.add(subnet.contains(IPv4Address(ipv4)));
294 }
295 }
296 } else if(m_type == NameType::IPv6) {
297 for(const auto& ipv6 : alt_name.ipv6_address()) {
298 score.add(matches_ipv6(ipv6));
299 }
300 } else {
301 // URI and email name constraint matching not implemented
303 }
304
305 return score.result();
306}
307
308//static
309bool GeneralName::matches_dns(std::string_view name, std::string_view constraint) {
310 // both constraint and name are assumed already tolower
311 if(name.size() == constraint.size()) {
312 return name == constraint;
313 } else if(constraint.size() > name.size()) {
314 // The constraint is longer than the issued name: not possibly a match
315 return false;
316 } else {
317 BOTAN_ASSERT_NOMSG(name.size() > constraint.size());
318
319 if(constraint.empty()) {
320 return true;
321 }
322
323 const std::string_view substr = name.substr(name.size() - constraint.size(), constraint.size());
324
325 if(constraint.front() == '.') {
326 return substr == constraint;
327 } else if(substr[0] == '.') {
328 return substr.substr(1) == constraint;
329 } else {
330 return substr == constraint && name[name.size() - constraint.size() - 1] == '.';
331 }
332 }
333}
334
335//static
336bool GeneralName::matches_dn(const X509_DN& name, const X509_DN& constraint) {
337 // Perform DN matching by comparing RDNs in sequence, i.e.,
338 // whether the constraint is a prefix of the name.
339 const auto& name_info = name.dn_info();
340 const auto& constraint_info = constraint.dn_info();
341
342 if(constraint_info.size() > name_info.size()) {
343 return false;
344 }
345
346 for(size_t i = 0; i < constraint_info.size(); ++i) {
347 if(name_info[i].first != constraint_info[i].first ||
348 !x500_name_cmp(name_info[i].second.value(), constraint_info[i].second.value())) {
349 return false;
350 }
351 }
352
353 return !constraint_info.empty();
354}
355
356std::ostream& operator<<(std::ostream& os, const GeneralName& gn) {
357 os << gn.type() << ":" << gn.name();
358 return os;
359}
360
362
364 throw Not_Implemented("GeneralSubtree encoding");
365}
366
368 /*
369 * RFC 5280 Section 4.2.1.10:
370 * Within this profile, the minimum and maximum fields are not used with any
371 * name forms, thus, the minimum MUST be zero, and maximum MUST be absent.
372 */
373 size_t minimum = 0;
374 std::optional<size_t> maximum;
375
376 ber.start_sequence()
377 .decode(m_base)
380 .end_cons();
381
382 if(minimum != 0) {
383 throw Decoding_Error("GeneralSubtree minimum must be 0");
384 }
385 if(maximum.has_value()) {
386 throw Decoding_Error("GeneralSubtree maximum must be absent");
387 }
388}
389
390std::ostream& operator<<(std::ostream& os, const GeneralSubtree& gs) {
391 os << gs.base();
392 return os;
393}
394
395NameConstraints::NameConstraints(std::vector<GeneralSubtree>&& permitted_subtrees,
396 std::vector<GeneralSubtree>&& excluded_subtrees) :
397 m_permitted_subtrees(std::move(permitted_subtrees)), m_excluded_subtrees(std::move(excluded_subtrees)) {
398 for(const auto& c : m_permitted_subtrees) {
399 m_permitted_name_types.insert(c.base().type_code());
400 }
401 for(const auto& c : m_excluded_subtrees) {
402 m_excluded_name_types.insert(c.base().type_code());
403 }
404}
405
406namespace {
407
408bool exceeds_limit(size_t dn_count, size_t alt_count, size_t constraint_count) {
409 /**
410 * OpenSSL uses a similar limit, but applies it to the total number of
411 * constraints, while we apply it to permitted and excluded independently.
412 */
413 constexpr size_t MAX_NC_CHECKS = (1 << 16);
414
415 if(auto names = checked_add(dn_count, alt_count)) {
416 if(auto product = checked_mul(*names, constraint_count)) {
417 if(*product < MAX_NC_CHECKS) {
418 return false;
419 }
420 }
421 }
422 return true;
423}
424
425} // namespace
426
427bool NameConstraints::is_permitted(const X509_Certificate& cert, bool reject_unknown) const {
428 if(permitted().empty()) {
429 return true;
430 }
431
432 const auto& alt_name = cert.subject_alt_name();
433
434 if(exceeds_limit(cert.subject_dn().count(), alt_name.count(), permitted().size())) {
435 return false;
436 }
437
438 if(reject_unknown) {
439 if(m_permitted_name_types.contains(GeneralName::NameType::Other) && !alt_name.other_names().empty()) {
440 return false;
441 }
442 if(m_permitted_name_types.contains(GeneralName::NameType::URI) && !alt_name.uris().empty()) {
443 return false;
444 }
445 if(m_permitted_name_types.contains(GeneralName::NameType::RFC822) && !alt_name.email().empty()) {
446 return false;
447 }
448 }
449
450 auto is_permitted_dn = [&](const X509_DN& dn) {
451 // If no restrictions, then immediate accept
452 if(!m_permitted_name_types.contains(GeneralName::NameType::DN)) {
453 return true;
454 }
455
456 for(const auto& c : m_permitted_subtrees) {
457 if(c.base().matches_dn(dn)) {
458 return true;
459 }
460 }
461
462 // There is at least one permitted name and we didn't match
463 return false;
464 };
465
466 auto is_permitted_dns_name = [&](const std::string& name) {
467 if(name.empty() || name.starts_with(".")) {
468 return false;
469 }
470
471 // If no restrictions, then immediate accept
472 if(!m_permitted_name_types.contains(GeneralName::NameType::DNS)) {
473 return true;
474 }
475
476 for(const auto& c : m_permitted_subtrees) {
477 if(c.base().matches_dns(name)) {
478 return true;
479 }
480 }
481
482 // There is at least one permitted name and we didn't match
483 return false;
484 };
485
486 /*
487 RFC 5280 4.2.1.10: iPAddress is a single GeneralName element where
488 IPv4 and IPv6 are distinguished only by the length.
489
490 An iPAddress subtree of either version therefore restricts the iPAddress name
491 form for both versions.
492 */
493 const bool ip_form_restricted = m_permitted_name_types.contains(GeneralName::NameType::IPv4) ||
494 m_permitted_name_types.contains(GeneralName::NameType::IPv6);
495
496 auto is_permitted_ipv4 = [&](uint32_t ipv4) {
497 if(!ip_form_restricted) {
498 return true;
499 }
500
501 for(const auto& c : m_permitted_subtrees) {
502 if(c.base().matches_ipv4(ipv4)) {
503 return true;
504 }
505 }
506
507 // We might here check if there are any IPv6 permitted names which are
508 // mapped IPv4 addresses, and if so check if any of those apply. It's not
509 // clear if this is desirable, and RFC 5280 is completely silent on the issue.
510
511 // There is at least one permitted iPAddress name and we didn't match
512 return false;
513 };
514
515 auto is_permitted_ipv6 = [&](const IPv6Address& ipv6) {
516 if(!ip_form_restricted) {
517 return true;
518 }
519
520 for(const auto& c : m_permitted_subtrees) {
521 if(c.base().matches_ipv6(ipv6)) {
522 return true;
523 }
524 }
525
526 // There is at least one permitted iPAddress name and we didn't match
527 return false;
528 };
529
530 /*
531 RFC 5280 4.1.2.6:
532 If subject naming information is present only in the
533 subjectAltName extension (e.g., a key bound only to an email
534 address or URI), then the subject name MUST be an empty
535 sequence and the subjectAltName extension MUST be critical.
536
537 RFC 5280 4.2.1.10:
538 Restrictions of the form directoryName MUST be applied to the subject
539 field in the certificate (when the certificate includes a non-empty
540 subject field) and to any names of type directoryName in the
541 subjectAltName extension.
542 */
543 if(!cert.subject_dn().empty() && !is_permitted_dn(cert.subject_dn())) {
544 return false;
545 }
546
547 for(const auto& alt_dn : alt_name.directory_names()) {
548 if(!is_permitted_dn(alt_dn)) {
549 return false;
550 }
551 }
552
553 for(const auto& alt_dns : alt_name.dns()) {
554 if(!is_permitted_dns_name(alt_dns)) {
555 return false;
556 }
557 }
558
559 for(const auto& alt_ipv4 : alt_name.ipv4_address()) {
560 if(!is_permitted_ipv4(alt_ipv4)) {
561 return false;
562 }
563 }
564
565 for(const auto& alt_ipv6 : alt_name.ipv6_address()) {
566 if(!is_permitted_ipv6(alt_ipv6)) {
567 return false;
568 }
569 }
570
571 if(alt_name.count() == 0) {
572 for(const auto& cn : cert.subject_info("Name")) {
573 if(cn.find(".") != std::string::npos) {
574 if(auto ipv4 = string_to_ipv4(cn)) {
575 if(!is_permitted_ipv4(ipv4.value())) {
576 return false;
577 }
578 } else {
579 if(!is_permitted_dns_name(canonicalize_dns_name(cn))) {
580 return false;
581 }
582 }
583 }
584 }
585 }
586
587 // We didn't encounter a name that doesn't have a matching constraint
588 return true;
589}
590
591bool NameConstraints::is_excluded(const X509_Certificate& cert, bool reject_unknown) const {
592 if(excluded().empty()) {
593 return false;
594 }
595
596 const auto& alt_name = cert.subject_alt_name();
597
598 if(exceeds_limit(cert.subject_dn().count(), alt_name.count(), excluded().size())) {
599 return true;
600 }
601
602 if(reject_unknown) {
603 // This is one is overly broad: we should just reject if there is a name constraint
604 // with the same OID as one of the other names
605 if(m_excluded_name_types.contains(GeneralName::NameType::Other) && !alt_name.other_names().empty()) {
606 return true;
607 }
608 if(m_excluded_name_types.contains(GeneralName::NameType::URI) && !alt_name.uris().empty()) {
609 return true;
610 }
611 if(m_excluded_name_types.contains(GeneralName::NameType::RFC822) && !alt_name.email().empty()) {
612 return true;
613 }
614 }
615
616 auto is_excluded_dn = [&](const X509_DN& dn) {
617 // If no restrictions, then immediate accept
618 if(!m_excluded_name_types.contains(GeneralName::NameType::DN)) {
619 return false;
620 }
621
622 for(const auto& c : m_excluded_subtrees) {
623 if(c.base().matches_dn(dn)) {
624 return true;
625 }
626 }
627
628 // There is at least one excluded name and we didn't match
629 return false;
630 };
631
632 auto is_excluded_dns_name = [&](const std::string& name) {
633 if(name.empty() || name.starts_with(".")) {
634 return true;
635 }
636
637 // If no restrictions, then immediate accept
638 if(!m_excluded_name_types.contains(GeneralName::NameType::DNS)) {
639 return false;
640 }
641
642 const bool name_has_wildcard = (name.find('*') != std::string::npos);
643
644 for(const auto& c : m_excluded_subtrees) {
645 if(c.base().matches_dns(name)) {
646 return true;
647 }
648
649 /*
650 RFC 5280 4.2.1.10 - "any name matching a restriction in the
651 excludedSubtrees field is invalid".
652
653 If the cert has a wildcard SAN (*.example.com), and that wildcard
654 could be matched against an excluded name, it must be rejected.
655 */
656 if(name_has_wildcard && c.base().m_type == GeneralName::NameType::DNS) {
657 const auto& constraint = std::get<GeneralName::DNS_IDX>(c.base().m_name);
658 if(host_wildcard_match(name, constraint)) {
659 return true;
660 }
661 }
662 }
663
664 // There is at least one excluded name and we didn't match
665 return false;
666 };
667
668 auto is_excluded_ipv4 = [&](uint32_t ipv4) {
669 if(m_excluded_name_types.contains(GeneralName::NameType::IPv4)) {
670 for(const auto& c : m_excluded_subtrees) {
671 if(c.base().matches_ipv4(ipv4)) {
672 return true;
673 }
674 }
675 }
676
677 // This name did not match any of the excluded names
678 return false;
679 };
680
681 auto is_excluded_ipv6 = [&](const IPv6Address& ipv6) {
682 if(m_excluded_name_types.contains(GeneralName::NameType::IPv6)) {
683 for(const auto& c : m_excluded_subtrees) {
684 if(c.base().matches_ipv6(ipv6)) {
685 return true;
686 }
687 }
688 }
689
690 // An IPv4-mapped IPv6 address names an IPv4 address so verify that
691 // address is not restricted by an IPv4 excludes rule
692 if(m_excluded_name_types.contains(GeneralName::NameType::IPv4)) {
693 if(auto embedded_v4 = ipv6.as_ipv4()) {
694 for(const auto& c : m_excluded_subtrees) {
695 if(c.base().matches_ipv4(*embedded_v4)) {
696 return true;
697 }
698 }
699 }
700 }
701
702 // This name did not match any of the excluded names
703 return false;
704 };
705
706 if(is_excluded_dn(cert.subject_dn())) {
707 return true;
708 }
709
710 for(const auto& alt_dn : alt_name.directory_names()) {
711 if(is_excluded_dn(alt_dn)) {
712 return true;
713 }
714 }
715
716 for(const auto& alt_dns : alt_name.dns()) {
717 if(is_excluded_dns_name(alt_dns)) {
718 return true;
719 }
720 }
721
722 for(const auto& alt_ipv4 : alt_name.ipv4_address()) {
723 if(is_excluded_ipv4(alt_ipv4)) {
724 return true;
725 }
726 }
727
728 for(const auto& alt_ipv6 : alt_name.ipv6_address()) {
729 if(is_excluded_ipv6(alt_ipv6)) {
730 return true;
731 }
732 }
733
734 if(alt_name.count() == 0) {
735 for(const auto& cn : cert.subject_info("Name")) {
736 if(cn.find(".") != std::string::npos) {
737 if(auto ipv4 = string_to_ipv4(cn)) {
738 if(is_excluded_ipv4(ipv4.value())) {
739 return true;
740 }
741 } else {
742 if(is_excluded_dns_name(canonicalize_dns_name(cn))) {
743 return true;
744 }
745 }
746 }
747 }
748 }
749
750 // We didn't encounter a name that matched any prohibited name
751 return false;
752}
753
754} // namespace Botan
#define BOTAN_ASSERT_NOMSG(expr)
Definition assert.h:75
#define BOTAN_ASSERT_UNREACHABLE()
Definition assert.h:163
const std::set< X509_DN > & directory_names() const
Return the set of directory names included in this alternative name.
Definition pkix_types.h:198
size_t count() const
Definition alt_name.cpp:51
const std::set< uint32_t > & ipv4_address() const
Return the set of IPv4 addresses included in this alternative name.
Definition pkix_types.h:186
const std::set< std::string > & dns() const
Return the set of DNS names included in this alternative name.
Definition pkix_types.h:183
const std::set< IPv6Address > & ipv6_address() const
Return the set of IPv6 addresses included in this alternative name.
Definition pkix_types.h:189
BER_Object get_next_object()
Definition ber_dec.cpp:426
BER_Decoder & decode(bool &out)
Definition ber_dec.h:220
Limits limits() const
Definition ber_dec.h:98
BER_Decoder & end_cons()
Definition ber_dec.cpp:524
BER_Decoder start_sequence()
Definition ber_dec.h:160
BER_Decoder & decode_optional(T &out, ASN1_Type type_tag, ASN1_Class class_tag, const T &default_value=T())
Definition ber_dec.h:285
size_t length() const
Definition asn1_obj.h:152
const uint8_t * bits() const
Definition asn1_obj.h:150
bool is_a(ASN1_Type type_tag, ASN1_Class class_tag) const
Definition asn1_obj.cpp:66
X.509 GeneralName Type.
Definition pkix_types.h:286
static GeneralName email(std::string_view email)
void decode_from(BER_Decoder &from) override
bool matches_ipv6(const IPv6Address &ip) const
GeneralName()=default
static GeneralName ipv4_address(uint32_t ipv4)
void encode_into(DER_Encoder &to) const override
std::string type() const
static GeneralName uri(std::string_view uri)
MatchResult matches(const X509_Certificate &cert) const
bool matches_dn(const X509_DN &dn) const
std::vector< uint8_t > binary_name() const
std::string name() const
static GeneralName ipv6_address(const IPv6Address &ipv6)
bool matches_dns(const std::string &dns_name) const
bool matches_ipv4(uint32_t ip) const
static GeneralName dns(std::string_view dns)
static GeneralName directory_name(Botan::X509_DN dn)
A single Name Constraint.
Definition pkix_types.h:404
void encode_into(DER_Encoder &to) const override
const GeneralName & base() const
Definition pkix_types.h:418
void decode_from(BER_Decoder &from) override
static std::optional< IPv4Subnet > from_address_and_mask(std::span< const uint8_t, 8 > addr_and_mask)
static IPv4Subnet host(IPv4Address address)
static std::optional< IPv6Subnet > from_address_and_mask(std::span< const uint8_t, 32 > addr_and_mask)
static IPv6Subnet host(IPv6Address address)
bool is_permitted(const X509_Certificate &cert, bool reject_unknown) const
bool is_excluded(const X509_Certificate &cert, bool reject_unknown) const
const std::vector< GeneralSubtree > & permitted() const
Definition pkix_types.h:449
const std::vector< GeneralSubtree > & excluded() const
Definition pkix_types.h:456
const X509_DN & subject_dn() const
Definition x509cert.cpp:418
std::vector< std::string > subject_info(std::string_view name) const
Definition x509cert.cpp:645
const AlternativeName & subject_alt_name() const
Definition x509cert.cpp:602
std::vector< std::string > get_attribute(std::string_view attr) const
Definition x509_dn.cpp:181
bool empty() const
Definition pkix_types.h:88
void decode_from(BER_Decoder &from) override
Definition x509_dn.cpp:338
size_t count() const
Definition pkix_types.h:90
std::vector< uint8_t > put_in_sequence(const std::vector< uint8_t > &contents)
Definition asn1_obj.cpp:177
std::string to_string(const BER_Object &obj)
Definition asn1_obj.cpp:190
constexpr std::optional< T > checked_add(T a, T b)
Definition int_utils.h:19
std::string tolower_string(std::string_view str)
Definition parsing.cpp:377
ASN1_Type
Definition asn1_obj.h:43
bool x500_name_cmp(std::string_view name1, std::string_view name2)
Definition x509_dn.cpp:82
std::ostream & operator<<(std::ostream &out, const OID &oid)
Definition asn1_oid.cpp:300
constexpr std::optional< T > checked_mul(T a, T b)
Definition int_utils.h:46
std::optional< uint32_t > string_to_ipv4(std::string_view str)
Definition parsing.cpp:155
bool host_wildcard_match(std::string_view issued, std::string_view host)
Definition parsing.cpp:389