Botan 3.13.0
Crypto and TLS for C&
Botan::GeneralName Class Referencefinal

X.509 GeneralName Type. More...

#include <pkix_types.h>

Inheritance diagram for Botan::GeneralName:
Botan::ASN1_Object

Public Types

enum  MatchResult : uint8_t {
  All , Some , None , NotFound ,
  UnknownType
}
enum class  NameType : uint8_t {
  Unknown = 0 , RFC822 = 1 , DNS = 2 , URI = 3 ,
  DN = 4 , IPv4 = 5 , IPv6 = 6 , Other = 7
}

Public Member Functions

std::vector< uint8_t > BER_encode () const
std::vector< uint8_t > binary_name () const
void decode_from (BER_Decoder &from) override
void encode_into (DER_Encoder &to) const override
 GeneralName ()=default
MatchResult matches (const X509_Certificate &cert) const
bool matches_dn (const X509_DN &dn) const
bool matches_dns (const DNSName &dns_name) const
bool matches_dns (const std::string &dns_name) const
bool matches_email (const EmailAddress &addr) const
bool matches_email (const SmtpUtf8Mailbox &mailbox) const
bool matches_ipv4 (const IPv4Address &ip) const
bool matches_ipv4 (uint32_t ip) const
bool matches_ipv6 (const IPv6Address &ip) const
bool matches_uri (const URI &uri) const
std::string name () const
std::string type () const
NameType type_code () const

Static Public Member Functions

static GeneralName _dns_san_value (std::string_view dns)
static GeneralName _uri_san_value (std::string_view full_uri)
static GeneralName directory_name (Botan::X509_DN dn)
static GeneralName dns (std::string_view dns)
static GeneralName email (std::string_view email)
static GeneralName ipv4_address (const IPv4Subnet &subnet)
static GeneralName ipv4_address (IPv4Address ipv4)
static GeneralName ipv4_address (uint32_t ipv4)
static GeneralName ipv4_address (uint32_t ipv4, uint32_t mask)
static GeneralName ipv6_address (const IPv6Address &ipv6)
static GeneralName ipv6_address (const IPv6Subnet &subnet)
static GeneralName uri (std::string_view uri)

Friends

class NameConstraints

Detailed Description

X.509 GeneralName Type.

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 Constraints).

This entire class is deprecated and will be removed in a future major release

Definition at line 543 of file pkix_types.h.

Member Enumeration Documentation

◆ MatchResult

Enumerator
All 
Some 
None 
NotFound 
UnknownType 

Definition at line 545 of file pkix_types.h.

545 : uint8_t /* NOLINT(*-use-enum-class) */ {
546 All,
547 Some,
548 None,
549 NotFound,
551 };

◆ NameType

enum class Botan::GeneralName::NameType : uint8_t
strong
Enumerator
Unknown 
RFC822 
DNS 
URI 
DN 
IPv4 
IPv6 
Other 

Definition at line 553 of file pkix_types.h.

553 : uint8_t {
554 Unknown = 0,
555 RFC822 = 1,
556 DNS = 2,
557 URI = 3,
558 DN = 4,
559 IPv4 = 5,
560 IPv6 = 6,
561 Other = 7,
562 };

Constructor & Destructor Documentation

◆ GeneralName()

Member Function Documentation

◆ _dns_san_value()

GeneralName Botan::GeneralName::_dns_san_value ( std::string_view dns)
static

Wrap a DNS SAN in a GeneralName, this is used for ffi

Warning
internal function that may be removed at any time

Definition at line 345 of file name_constraint.cpp.

345 {
346 if(auto dns = DNSConstraint::from_san_value(dns_name)) {
347 return {NameType::DNS, std::move(*dns)};
348 } else {
349 throw Invalid_Argument(fmt("Invalid DNS SAN value '{}'", dns_name));
350 }
351}
static GeneralName dns(std::string_view dns)
std::string fmt(std::string_view format, const T &... args)
Definition fmt.h:53

References DNS, dns(), Botan::fmt(), and GeneralName().

Referenced by GeneralName().

◆ _uri_san_value()

GeneralName Botan::GeneralName::_uri_san_value ( std::string_view full_uri)
static

Wrap a URI SAN in a GeneralName, this is used for ffi

Warning
internal function that may be removed at any time

Definition at line 337 of file name_constraint.cpp.

337 {
338 if(auto uri = URIConstraint::from_san_value(full_uri)) {
339 return {NameType::URI, std::move(*uri)};
340 } else {
341 throw Invalid_Argument(fmt("Invalid URI SAN value '{}'", full_uri));
342 }
343}
static GeneralName uri(std::string_view uri)

References Botan::fmt(), GeneralName(), URI, and uri().

Referenced by GeneralName().

◆ BER_encode()

std::vector< uint8_t > Botan::ASN1_Object::BER_encode ( ) const
inherited

Return the encoding of this object. This is a convenience method when just one object needs to be serialized. Use DER_Encoder for complicated encodings.

Definition at line 21 of file asn1_obj.cpp.

21 {
22 std::vector<uint8_t> output;
23 DER_Encoder der(output);
24 this->encode_into(der);
25 return output;
26}
virtual void encode_into(DER_Encoder &to) const =0

References encode_into().

Referenced by decode_from(), Botan::PKCS12::export_to(), Botan::Certificate_Store_In_SQL::find_all_certs(), Botan::Certificate_Store_In_SQL::find_cert(), Botan::X509_Certificate::fingerprint(), Botan::Certificate_Store_In_SQL::insert_cert(), Botan::X509_Object::PEM_encode(), and Botan::PSS_Params::PSS_Params().

◆ binary_name()

std::vector< uint8_t > Botan::GeneralName::binary_name ( ) const
Returns
The name as binary string. Format depends on type.

Definition at line 398 of file name_constraint.cpp.

398 {
399 return std::visit(Botan::overloaded{
400 [](const Botan::X509_DN& dn) { return Botan::ASN1::put_in_sequence(dn.get_bits()); },
401 [](const IPv4Subnet& subnet) { return subnet.serialize(); },
402 [](const IPv6Subnet& subnet) { return subnet.serialize(); },
403 [](const auto&) -> std::vector<uint8_t> {
404 throw Invalid_State("Cannot convert GeneralName to binary string");
405 },
406 },
407 m_name);
408}
std::vector< uint8_t > put_in_sequence(const std::vector< uint8_t > &contents)
Definition asn1_obj.cpp:208
overloaded(Ts...) -> overloaded< Ts... >

References Botan::ASN1::put_in_sequence().

Referenced by botan_x509_general_name_view_binary_value().

◆ decode_from()

void Botan::GeneralName::decode_from ( BER_Decoder & from)
overridevirtual

Decode whatever this object is from from

Parameters
fromthe BER_Decoder that will be read from

Implements Botan::ASN1_Object.

Definition at line 468 of file name_constraint.cpp.

468 {
469 const BER_Object obj = ber.get_next_object();
470
471 if(obj.is_a(0, ASN1_Class::ExplicitContextSpecific)) {
472 m_type = NameType::Other;
473 } else if(obj.is_a(1, ASN1_Class::ContextSpecific)) {
474 /*
475 RFC 5280 4.2.1.10:
476 A name constraint for Internet mail addresses MAY specify a
477 particular mailbox, all addresses at a particular host, or all
478 mailboxes in a domain.
479 EmailConstraint::from_string validates and canonicalizes per the
480 Section 7.5 matching rules.
481 */
482 auto constraint = EmailConstraint::from_string(ASN1::to_string(obj));
483 if(!constraint.has_value()) {
484 throw Decoding_Error("Malformed RFC822 name in GeneralName");
485 }
486 m_type = NameType::RFC822;
487 m_name = std::move(*constraint);
488 } else if(obj.is_a(2, ASN1_Class::ContextSpecific)) {
489 auto constraint = DNSConstraint::from_string(ASN1::to_string(obj));
490 if(!constraint.has_value()) {
491 throw Decoding_Error("Malformed DNS name in GeneralName");
492 }
493 m_type = NameType::DNS;
494 m_name = std::move(*constraint);
495 } else if(obj.is_a(6, ASN1_Class::ContextSpecific)) {
496 /*
497 RFC 5280 4.2.1.10:
498 For URIs, the constraint applies to the host part of the name.
499 The constraint MUST be specified as a fully qualified domain
500 name and MAY specify a host or a domain. Examples would be
501 "host.example.com" and ".example.com".
502 */
503 auto constraint = URIConstraint::from_string(ASN1::to_string(obj));
504 if(!constraint.has_value()) {
505 throw Decoding_Error("Malformed URI name in GeneralName");
506 }
507 m_type = NameType::URI;
508 m_name = std::move(*constraint);
509 } else if(obj.is_a(4, ASN1_Class::ContextSpecific | ASN1_Class::Constructed)) {
510 X509_DN dn;
511 BER_Decoder dec(obj, ber.limits());
512 dn.decode_from(dec);
513 dec.verify_end();
514 m_type = NameType::DN;
515 m_name.emplace<X509_DN>(dn);
516 } else if(obj.is_a(7, ASN1_Class::ContextSpecific)) {
517 if(obj.length() == 8) {
518 const auto addr_and_mask = std::span<const uint8_t, 8>{obj.bits(), 8};
519 auto subnet = IPv4Subnet::from_address_and_mask(addr_and_mask);
520 if(!subnet.has_value()) {
521 throw Decoding_Error("IPv4 name constraint mask is not a contiguous CIDR prefix");
522 }
523
524 m_type = NameType::IPv4;
525 m_name.emplace<IPv4Subnet>(*subnet);
526 } else if(obj.length() == 32) {
527 const auto addr_and_mask = std::span<const uint8_t, 32>{obj.bits(), 32};
528 auto subnet = IPv6Subnet::from_address_and_mask(addr_and_mask);
529 if(!subnet.has_value()) {
530 throw Decoding_Error("IPv6 name constraint mask is not a contiguous CIDR prefix");
531 }
532
533 m_type = NameType::IPv6;
534 m_name.emplace<IPv6Subnet>(*subnet);
535 } else {
536 throw Decoding_Error("Invalid IP name constraint size " + std::to_string(obj.length()));
537 }
538 } else {
539 m_type = NameType::Unknown;
540 }
541}
static std::optional< IPv4Subnet > from_address_and_mask(std::span< const uint8_t, 8 > addr_and_mask)
static std::optional< IPv6Subnet > from_address_and_mask(std::span< const uint8_t, 32 > addr_and_mask)
std::string to_string(const BER_Object &obj)
Definition asn1_obj.cpp:224

References Botan::BER_Object::bits(), Botan::Constructed, Botan::ContextSpecific, Botan::X509_DN::decode_from(), DN, DNS, Botan::ExplicitContextSpecific, Botan::IPv4Subnet::from_address_and_mask(), Botan::IPv6Subnet::from_address_and_mask(), Botan::BER_Decoder::get_next_object(), IPv4, IPv6, Botan::BER_Object::is_a(), Botan::BER_Object::length(), Botan::BER_Decoder::limits(), Other, RFC822, Botan::ASN1::to_string(), Unknown, URI, and Botan::BER_Decoder::verify_end().

Referenced by GeneralName().

◆ directory_name()

GeneralName Botan::GeneralName::directory_name ( Botan::X509_DN dn)
static

Definition at line 353 of file name_constraint.cpp.

353 {
354 return {NameType::DN, std::move(dn)};
355}

References DN, and GeneralName().

Referenced by GeneralName().

◆ dns()

GeneralName Botan::GeneralName::dns ( std::string_view dns)
static

Definition at line 321 of file name_constraint.cpp.

321 {
322 if(auto constraint = DNSConstraint::from_string(dns)) {
323 return {NameType::DNS, std::move(*constraint)};
324 } else {
325 throw Invalid_Argument(fmt("Invalid DNS name constraint '{}'", dns));
326 }
327}

References DNS, dns(), Botan::fmt(), and GeneralName().

Referenced by _dns_san_value(), dns(), GeneralName(), and matches().

◆ email()

GeneralName Botan::GeneralName::email ( std::string_view email)
static

Definition at line 313 of file name_constraint.cpp.

313 {
314 if(auto constraint = EmailConstraint::from_string(email)) {
315 return {NameType::RFC822, std::move(*constraint)};
316 } else {
317 throw Invalid_Argument(fmt("Invalid RFC822 name constraint '{}'", email));
318 }
319}
static GeneralName email(std::string_view email)

References email(), Botan::fmt(), GeneralName(), and RFC822.

Referenced by email(), and GeneralName().

◆ encode_into()

void Botan::GeneralName::encode_into ( DER_Encoder & to) const
overridevirtual

Encode whatever this object is into to

Parameters
tothe DER_Encoder that will be written to

Implements Botan::ASN1_Object.

Definition at line 410 of file name_constraint.cpp.

410 {
411 /*
412 GeneralName ::= CHOICE {
413 otherName [0] OtherName,
414 rfc822Name [1] IA5String,
415 dNSName [2] IA5String,
416 x400Address [3] ORAddress,
417 directoryName [4] Name,
418 ediPartyName [5] EDIPartyName,
419 uniformResourceIdentifier [6] IA5String,
420 iPAddress [7] OCTET STRING,
421 registeredID [8] OBJECT IDENTIFIER }
422 */
423 auto emit_ia5_implicit = [&](uint32_t tag, std::string_view value) {
424 const ASN1_String str(value, ASN1_Type::Ia5String);
425 to.add_object(ASN1_Type(tag), ASN1_Class::ContextSpecific, str.value());
426 };
427
428 switch(m_type) {
429 case NameType::RFC822:
430 emit_ia5_implicit(1, std::get<EmailConstraint>(m_name).value());
431 return;
432 case NameType::DNS:
433 emit_ia5_implicit(2, std::get<DNSConstraint>(m_name).value());
434 return;
435 case NameType::URI:
436 emit_ia5_implicit(6, std::get<URIConstraint>(m_name).value());
437 return;
438 case NameType::DN:
439 to.add_object(ASN1_Type(4), ASN1_Class::ExplicitContextSpecific, std::get<X509_DN>(m_name).DER_encode());
440 return;
441 case NameType::IPv4: {
442 // In a name constraint the iPAddress is always address followed by mask,
443 // even for a single host (unlike the SAN form)
444 const auto& subnet = std::get<IPv4Subnet>(m_name);
445 const auto addr_and_mask =
446 concat(subnet.address().to_bytes(), IPv4Address::netmask(subnet.prefix_length()).to_bytes());
447 // NOLINTNEXTLINE(clang-analyzer-optin.core.EnumCastOutOfRange)
448 to.add_object(ASN1_Type(7), ASN1_Class::ContextSpecific, addr_and_mask);
449 return;
450 }
451 case NameType::IPv6: {
452 const auto& subnet = std::get<IPv6Subnet>(m_name);
453 const auto addr_and_mask =
454 concat(subnet.address().address(), IPv6Address::netmask(subnet.prefix_length()).address());
455 // NOLINTNEXTLINE(clang-analyzer-optin.core.EnumCastOutOfRange)
456 to.add_object(ASN1_Type(7), ASN1_Class::ContextSpecific, addr_and_mask);
457 return;
458 }
459 case NameType::Other:
461 // Decoding retains only the type tag for these forms, not the value
462 break;
463 }
464
465 throw Encoding_Error("Cannot encode GeneralName of Other or Unknown type");
466}
std::array< uint8_t, 4 > to_bytes() const
The address as four bytes, network-byte-order.
static IPv4Address netmask(size_t bits)
std::array< uint8_t, 16 > address() const
static IPv6Address netmask(size_t bits)
ASN1_Type
Definition asn1_obj.h:47
constexpr auto concat(Rs &&... ranges)
Definition concat_util.h:90

References Botan::DER_Encoder::add_object(), Botan::concat(), Botan::ContextSpecific, DN, DNS, Botan::ExplicitContextSpecific, Botan::Ia5String, IPv4, IPv6, Botan::IPv4Address::netmask(), Botan::IPv6Address::netmask(), Other, RFC822, Unknown, URI, and Botan::ASN1_String::value().

Referenced by GeneralName().

◆ ipv4_address() [1/4]

GeneralName Botan::GeneralName::ipv4_address ( const IPv4Subnet & subnet)
static

Definition at line 373 of file name_constraint.cpp.

373 {
374 return {NameType::IPv4, subnet};
375}

References GeneralName(), and IPv4.

◆ ipv4_address() [2/4]

GeneralName Botan::GeneralName::ipv4_address ( IPv4Address ipv4)
static

Definition at line 369 of file name_constraint.cpp.

369 {
370 return {NameType::IPv4, IPv4Subnet::host(ipv4)};
371}
static IPv4Subnet host(IPv4Address address)

References GeneralName(), Botan::IPv4Subnet::host(), and IPv4.

◆ ipv4_address() [3/4]

GeneralName Botan::GeneralName::ipv4_address ( uint32_t ipv4)
static

Definition at line 357 of file name_constraint.cpp.

357 {
358 return GeneralName::ipv4_address(IPv4Address(ipv4));
359}
static GeneralName ipv4_address(uint32_t ipv4)

References GeneralName(), and ipv4_address().

Referenced by GeneralName(), and ipv4_address().

◆ ipv4_address() [4/4]

GeneralName Botan::GeneralName::ipv4_address ( uint32_t ipv4,
uint32_t mask )
static

Definition at line 361 of file name_constraint.cpp.

361 {
362 if(auto subnet = IPv4Subnet::from_address_and_mask(ipv4, mask)) {
363 return {NameType::IPv4, *subnet};
364 } else {
365 throw Invalid_Argument("IPv4 subnet mask is not a contiguous CIDR prefix");
366 }
367}

References Botan::IPv4Subnet::from_address_and_mask(), GeneralName(), and IPv4.

◆ ipv6_address() [1/2]

GeneralName Botan::GeneralName::ipv6_address ( const IPv6Address & ipv6)
static

Definition at line 377 of file name_constraint.cpp.

377 {
378 return {NameType::IPv6, IPv6Subnet::host(ipv6)};
379}
static IPv6Subnet host(IPv6Address address)

References GeneralName(), Botan::IPv6Subnet::host(), and IPv6.

Referenced by GeneralName().

◆ ipv6_address() [2/2]

GeneralName Botan::GeneralName::ipv6_address ( const IPv6Subnet & subnet)
static

Definition at line 381 of file name_constraint.cpp.

381 {
382 return {NameType::IPv6, subnet};
383}

References GeneralName(), and IPv6.

◆ matches()

GeneralName::MatchResult Botan::GeneralName::matches ( const X509_Certificate & cert) const

Checks whether a given certificate (partially) matches this name.

Parameters
certcertificate to be matched
Returns
the match result

Definition at line 672 of file name_constraint.cpp.

672 {
673 class MatchScore final {
674 public:
675 MatchScore() : m_any(false), m_some(false), m_all(true) {}
676
677 void add(bool m) {
678 m_any = true;
679 m_some |= m;
680 m_all &= m;
681 }
682
683 MatchResult result() const {
684 if(!m_any) {
685 return MatchResult::NotFound;
686 } else if(m_all) {
687 return MatchResult::All;
688 } else if(m_some) {
689 return MatchResult::Some;
690 } else {
691 return MatchResult::None;
692 }
693 }
694
695 private:
696 bool m_any;
697 bool m_some;
698 bool m_all;
699 };
700
701 const X509_DN& dn = cert.subject_dn();
702 const AlternativeName& alt_name = cert.subject_alt_name();
703
704 MatchScore score;
705
706 if(m_type == NameType::DNS) {
707 const auto& constraint = std::get<DNSConstraint>(m_name).value();
708
709 for(const auto& dns : alt_name.dns_names()) {
710 score.add(dns_subtree_match(dns.to_string(), constraint));
711 }
712
713 if(alt_name.is_empty()) {
714 // TODO(Botan4): CN fallback is deprecated for removal in Botan4.
715 // Check CN instead...
716 for(const std::string& cn : dn.get_attribute("CN")) {
717 if(cn.find('.') == std::string::npos) {
718 continue;
719 }
720 if(IPv4Address::from_string(cn).has_value()) {
721 continue;
722 }
723 if(auto dns_form = DNSName::from_san_string(cn)) {
724 score.add(dns_subtree_match(dns_form->to_string(), constraint));
725 }
726 }
727 }
728 } else if(m_type == NameType::DN) {
729 const X509_DN& constraint = std::get<X509_DN>(m_name);
730 score.add(matches_dn(dn, constraint));
731
732 for(const auto& alt_dn : alt_name.directory_names()) {
733 score.add(matches_dn(alt_dn, constraint));
734 }
735 } else if(m_type == NameType::IPv4) {
736 const auto& subnet = std::get<IPv4Subnet>(m_name);
737
738 if(alt_name.is_empty()) {
739 // TODO(Botan4): CN fallback is deprecated for removal in Botan4.
740 // Check CN instead...
741 for(const std::string& cn : dn.get_attribute("CN")) {
742 if(auto ipv4 = IPv4Address::from_string(cn)) {
743 score.add(subnet.contains(*ipv4));
744 }
745 }
746 } else {
747 for(const auto& ipv4 : alt_name.ipv4_addresses()) {
748 score.add(subnet.contains(ipv4));
749 }
750 }
751 } else if(m_type == NameType::IPv6) {
752 for(const auto& ipv6 : alt_name.ipv6_addresses()) {
753 score.add(matches_ipv6(ipv6));
754 }
755 } else if(m_type == NameType::URI) {
756 for(const auto& uri : alt_name.uri_names()) {
757 score.add(matches_uri(uri));
758 }
759 } else if(m_type == NameType::RFC822) {
760 for(const auto& addr : alt_name.email_addresses()) {
761 score.add(matches_email(addr));
762 }
763 } else {
764 // Only NameType::Other (and the sentinel Unknown) remain; those
765 // cannot be matched without per-OID semantics.
767 }
768
769 return score.result();
770}
static std::optional< DNSName > from_san_string(std::string_view name)
Definition dns_name.cpp:149
bool matches_ipv6(const IPv6Address &ip) const
bool matches_uri(const URI &uri) const
bool matches_dn(const X509_DN &dn) const
bool matches_email(const EmailAddress &addr) const
static std::optional< IPv4Address > from_string(std::string_view str)

References All, Botan::AlternativeName::directory_names(), DN, DNS, dns(), Botan::AlternativeName::dns_names(), Botan::AlternativeName::email_addresses(), Botan::DNSName::from_san_string(), Botan::IPv4Address::from_string(), Botan::X509_DN::get_attribute(), IPv4, Botan::AlternativeName::ipv4_addresses(), IPv6, Botan::AlternativeName::ipv6_addresses(), Botan::AlternativeName::is_empty(), matches_dn(), matches_email(), matches_ipv6(), matches_uri(), None, NotFound, RFC822, Some, Botan::X509_Certificate::subject_alt_name(), Botan::X509_Certificate::subject_dn(), UnknownType, URI, uri(), and Botan::AlternativeName::uri_names().

◆ matches_dn()

bool Botan::GeneralName::matches_dn ( const X509_DN & dn) const

Definition at line 571 of file name_constraint.cpp.

571 {
572 if(m_type == NameType::DN) {
573 return matches_dn(dn, std::get<X509_DN>(m_name));
574 }
575 return false;
576}

References DN, and matches_dn().

Referenced by matches(), and matches_dn().

◆ matches_dns() [1/2]

bool Botan::GeneralName::matches_dns ( const DNSName & dns_name) const

Definition at line 550 of file name_constraint.cpp.

550 {
551 if(m_type == NameType::DNS) {
552 return dns_subtree_match(dns_name.to_string(), std::get<DNSConstraint>(m_name).value());
553 }
554 return false;
555}

References DNS, and Botan::DNSName::to_string().

◆ matches_dns() [2/2]

bool Botan::GeneralName::matches_dns ( const std::string & dns_name) const

Definition at line 543 of file name_constraint.cpp.

543 {
544 if(m_type == NameType::DNS) {
545 return dns_subtree_match(dns_name, std::get<DNSConstraint>(m_name).value());
546 }
547 return false;
548}

References DNS.

◆ matches_email() [1/2]

bool Botan::GeneralName::matches_email ( const EmailAddress & addr) const

Definition at line 608 of file name_constraint.cpp.

608 {
609 if(m_type != NameType::RFC822) {
610 return false;
611 }
612 return email_subtree_match(addr, std::get<EmailConstraint>(m_name).value());
613}

References RFC822.

Referenced by matches().

◆ matches_email() [2/2]

bool Botan::GeneralName::matches_email ( const SmtpUtf8Mailbox & mailbox) const

Definition at line 615 of file name_constraint.cpp.

615 {
616 if(m_type != NameType::RFC822) {
617 return false;
618 }
619 /*
620 RFC 9598 Section 6:
621 Setup converts the inputs of the comparison ... to constraint
622 comparison form. For both the name constraint and the subject,
623 this will convert all A-labels and NR-LDH labels to lowercase.
624 Strip the Local-part and "@" separator from each rfc822Name and
625 SmtpUTF8Mailbox, which leaves just the domain part. After setup,
626 follow the comparison steps defined in Section 4.2.1.10 of
627 [RFC5280] as follows. If the resulting name constraint domain
628 starts with a "." character, then for the name constraint to
629 match, a suffix of the resulting subject alternative name domain
630 MUST match the name constraint (including the leading ".") octet
631 for octet. If the resulting name constraint domain does not
632 start with a "." character, then for the name constraint to
633 match, the entire resulting subject alternative name domain MUST
634 match the name constraint octet for octet.
635
636 Per RFC 9598 Section 3 the SmtpUTF8Mailbox domain is already A-label /
637 NR-LDH and lowercase by construction (DNSName::from_string enforces
638 LDH + lowercase). The rfc822Name constraint flows through the same
639 DNSName validation. So octet-for-octet comparison is the correct
640 algorithm with no IDNA conversion required.
641 */
642 const std::string& candidate_domain = mailbox.domain().to_string();
643 const std::string& constraint = std::get<EmailConstraint>(m_name).value();
644 if(constraint.find('@') != std::string::npos) {
645 /*
646 * The situation with SmtpUTF8Mailbox mailbox constraints (with '@') is a bit confused.
647 *
648 * RFC 9549 updates RFC 5280 to completely drop support for mailbox constraints.
649 * Then RFC 9598 Section 6 (relevant section quoted above) defines a mechanism to
650 * apply rfc822 mailbox name constraints to SmtpUTF8Mailbox, but it does so in a
651 * completely insecure way, namely by stripping off the local-part and comparing just
652 * the domains. Under these rules, if an intermediate certificate had a permittedSubtrees
653 * containing alice@example.com then a leaf certificate could have a SmtpUTF8Mailbox
654 * containing bob@example.com, and per RFC 9598 that's fine because we are supposed
655 * to just check the domains.
656 *
657 * This is obviously nonsense. Here we return false, which ensures that
658 * is_permitted_smtp_utf8 never accepts on a mailbox constraint. In is_excluded_smtp_utf8
659 * we first call matches_email then additionally (for mailbox constraints) reject any
660 * matching domain using the additional check in mailbox_form_constraint_covers_domain.
661 */
662 return false;
663 }
664 if(!constraint.empty() && constraint.front() == '.') {
665 // Leading-dot subtree form: suffix match including the dot.
666 return candidate_domain.ends_with(constraint);
667 }
668 // Host form: exact match on the domain.
669 return candidate_domain == constraint;
670}

References Botan::SmtpUtf8Mailbox::domain(), RFC822, and Botan::DNSName::to_string().

◆ matches_ipv4() [1/2]

bool Botan::GeneralName::matches_ipv4 ( const IPv4Address & ip) const
inline

Definition at line 625 of file pkix_types.h.

625{ return matches_ipv4(ip.address()); }
bool matches_ipv4(uint32_t ip) const

References matches_ipv4(), and matches_ipv4().

Referenced by matches_ipv4().

◆ matches_ipv4() [2/2]

bool Botan::GeneralName::matches_ipv4 ( uint32_t ip) const

Definition at line 557 of file name_constraint.cpp.

557 {
558 if(m_type == NameType::IPv4) {
559 return std::get<IPv4Subnet>(m_name).contains(IPv4Address(ip));
560 }
561 return false;
562}

References IPv4.

Referenced by matches_ipv4().

◆ matches_ipv6()

bool Botan::GeneralName::matches_ipv6 ( const IPv6Address & ip) const

Definition at line 564 of file name_constraint.cpp.

564 {
565 if(m_type == NameType::IPv6) {
566 return std::get<IPv6Subnet>(m_name).contains(ip);
567 }
568 return false;
569}

References IPv6.

Referenced by matches().

◆ matches_uri()

bool Botan::GeneralName::matches_uri ( const URI & uri) const

Definition at line 578 of file name_constraint.cpp.

578 {
579 if(m_type != NameType::URI) {
580 return false;
581 }
582 // RFC 5280 4.2.1.10 does not provide for applying a DNS-form URI
583 // constraint to an IP-literal host.
584 const auto host = uri.host();
585 if(!host.has_value() || !std::holds_alternative<DNSName>(host->get())) {
586 return false;
587 }
588 const std::string& dns_host = std::get<DNSName>(host->get()).to_string();
589 const std::string& constraint = std::get<URIConstraint>(m_name).value();
590 /*
591 RFC 5280 4.2.1.10:
592 When the constraint begins with a period, it MAY be expanded with
593 one or more labels. That is, the constraint ".example.com" is
594 satisfied by both host.example.com and my.host.example.com.
595 However, the constraint ".example.com" is not satisfied by
596 "example.com". When the constraint does not begin with a period,
597 it specifies a host.
598
599 So a bare-host URI constraint is exact-match only; subdomains don't
600 satisfy it. dns_subtree_match handles the leading-dot form correctly.
601 */
602 if(!constraint.empty() && constraint.front() == '.') {
603 return dns_subtree_match(dns_host, constraint);
604 }
605 return dns_host == constraint;
606}

References URI, and uri().

Referenced by matches().

◆ name()

std::string Botan::GeneralName::name ( ) const
Returns
The name as string. Format depends on type.

Definition at line 385 of file name_constraint.cpp.

385 {
386 return std::visit(
388 [](const EmailConstraint& c) -> std::string { return c.value(); },
389 [](const DNSConstraint& c) -> std::string { return c.value(); },
390 [](const URIConstraint& c) -> std::string { return c.value(); },
391 [](const X509_DN& dn) -> std::string { return dn.to_string(); },
392 [](const IPv4Subnet& s) -> std::string { return s.is_host() ? s.address().to_string() : s.to_string(); },
393 [](const IPv6Subnet& s) -> std::string { return s.is_host() ? s.address().to_string() : s.to_string(); },
394 },
395 m_name);
396}

Referenced by botan_x509_general_name_view_string_value(), and Botan::operator<<().

◆ type()

std::string Botan::GeneralName::type ( ) const
Returns
Type of the name. Can be DN, DNS, IP, RFC822 or URI.

Definition at line 290 of file name_constraint.cpp.

290 {
291 switch(m_type) {
293 throw Encoding_Error("Could not convert unknown NameType to string");
294 case NameType::RFC822:
295 return "RFC822";
296 case NameType::DNS:
297 return "DNS";
298 case NameType::URI:
299 return "URI";
300 case NameType::DN:
301 return "DN";
302 case NameType::IPv4:
303 return "IP";
304 case NameType::IPv6:
305 return "IPv6";
306 case NameType::Other:
307 return "Other";
308 }
309
311}
#define BOTAN_ASSERT_UNREACHABLE()
Definition assert.h:166

References BOTAN_ASSERT_UNREACHABLE, DN, DNS, IPv4, IPv6, Other, RFC822, Unknown, and URI.

Referenced by Botan::operator<<().

◆ type_code()

NameType Botan::GeneralName::type_code ( ) const
inline
Returns
Type of the name expressed in this restriction

Definition at line 596 of file pkix_types.h.

596{ return m_type; }

References type_code().

Referenced by botan_x509_general_name_get_type(), botan_x509_general_name_view_binary_value(), botan_x509_general_name_view_string_value(), Botan::NameConstraints::is_excluded(), and type_code().

◆ uri()

GeneralName Botan::GeneralName::uri ( std::string_view uri)
static

Definition at line 329 of file name_constraint.cpp.

329 {
330 if(auto constraint = URIConstraint::from_string(uri)) {
331 return {NameType::URI, std::move(*constraint)};
332 } else {
333 throw Invalid_Argument(fmt("Invalid URI name constraint '{}'", uri));
334 }
335}

References Botan::fmt(), GeneralName(), URI, and uri().

Referenced by _uri_san_value(), GeneralName(), matches(), matches_uri(), and uri().

◆ NameConstraints

friend class NameConstraints
friend

Definition at line 634 of file pkix_types.h.

References NameConstraints.

Referenced by NameConstraints.


The documentation for this class was generated from the following files: