9#include <botan/pkix_types.h>
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>
28std::string canonicalize_dns_name(std::string_view name) {
37 throw Encoding_Error(
"Could not convert unknown NameType to string");
58 return GeneralName::make<RFC822_IDX>(
email);
62 return GeneralName::make<DNS_IDX>(
dns);
66 return GeneralName::make<URI_IDX>(
uri);
70 return GeneralName::make<DN_IDX>(std::move(dn));
79 if(!subnet.has_value()) {
82 return GeneralName::make<IPV4_IDX>(*subnet);
90 return GeneralName::make<IPV4_IDX>(subnet);
98 return GeneralName::make<IPV6_IDX>(subnet);
102 const size_t index = m_name.index();
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();
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");
156 m_name.emplace<DNS_IDX>(std::move(
dns));
165 m_name.emplace<DN_IDX>(dn);
168 const auto addr_and_mask = std::span<const uint8_t, 8>{obj.
bits(), 8};
170 if(!subnet.has_value()) {
171 throw Decoding_Error(
"IPv4 name constraint mask is not a contiguous CIDR prefix");
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};
179 if(!subnet.has_value()) {
180 throw Decoding_Error(
"IPv6 name constraint mask is not a contiguous CIDR prefix");
184 m_name.emplace<IPV6_IDX>(*subnet);
195 const auto& constraint = std::get<DNS_IDX>(m_name);
203 return std::get<IPV4_IDX>(m_name).contains(
IPv4Address(ip));
210 return std::get<IPV6_IDX>(m_name).contains(ip);
217 const X509_DN& constraint = std::get<DN_IDX>(m_name);
224 class MatchScore final {
226 MatchScore() : m_any(
false), m_some(
false), m_all(
true) {}
258 const auto& constraint = std::get<DNS_IDX>(m_name);
260 const auto& alt_names = alt_name.
dns();
262 for(
const std::string&
dns : alt_names) {
266 if(alt_name.
count() == 0) {
270 score.add(
matches_dns(canonicalize_dns_name(cn), constraint));
275 const X509_DN& constraint = std::get<DN_IDX>(m_name);
282 const auto& subnet = std::get<IPV4_IDX>(m_name);
284 if(alt_name.
count() == 0) {
305 return score.result();
311 if(
name.size() == constraint.size()) {
312 return name == constraint;
313 }
else if(constraint.size() >
name.size()) {
319 if(constraint.empty()) {
323 const std::string_view substr =
name.substr(
name.size() - constraint.size(), constraint.size());
325 if(constraint.front() ==
'.') {
326 return substr == constraint;
327 }
else if(substr[0] ==
'.') {
328 return substr.substr(1) == constraint;
330 return substr == constraint &&
name[
name.size() - constraint.size() - 1] ==
'.';
339 const auto& name_info =
name.dn_info();
340 const auto& constraint_info = constraint.dn_info();
342 if(constraint_info.size() > name_info.size()) {
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())) {
353 return !constraint_info.empty();
357 os << gn.
type() <<
":" << gn.
name();
374 std::optional<size_t> maximum;
385 if(maximum.has_value()) {
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());
401 for(
const auto& c : m_excluded_subtrees) {
402 m_excluded_name_types.insert(c.base().type_code());
408bool exceeds_limit(
size_t dn_count,
size_t alt_count,
size_t constraint_count) {
413 constexpr size_t MAX_NC_CHECKS = (1 << 16);
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) {
450 auto is_permitted_dn = [&](
const X509_DN& dn) {
456 for(
const auto& c : m_permitted_subtrees) {
457 if(c.base().matches_dn(dn)) {
466 auto is_permitted_dns_name = [&](
const std::string& name) {
467 if(name.empty() || name.starts_with(
".")) {
476 for(
const auto& c : m_permitted_subtrees) {
477 if(c.base().matches_dns(name)) {
496 auto is_permitted_ipv4 = [&](uint32_t ipv4) {
497 if(!ip_form_restricted) {
501 for(
const auto& c : m_permitted_subtrees) {
502 if(c.base().matches_ipv4(ipv4)) {
515 auto is_permitted_ipv6 = [&](
const IPv6Address& ipv6) {
516 if(!ip_form_restricted) {
520 for(
const auto& c : m_permitted_subtrees) {
521 if(c.base().matches_ipv6(ipv6)) {
547 for(
const auto& alt_dn : alt_name.directory_names()) {
548 if(!is_permitted_dn(alt_dn)) {
553 for(
const auto& alt_dns : alt_name.dns()) {
554 if(!is_permitted_dns_name(alt_dns)) {
559 for(
const auto& alt_ipv4 : alt_name.ipv4_address()) {
560 if(!is_permitted_ipv4(alt_ipv4)) {
565 for(
const auto& alt_ipv6 : alt_name.ipv6_address()) {
566 if(!is_permitted_ipv6(alt_ipv6)) {
571 if(alt_name.count() == 0) {
573 if(cn.find(
".") != std::string::npos) {
575 if(!is_permitted_ipv4(ipv4.value())) {
579 if(!is_permitted_dns_name(canonicalize_dns_name(cn))) {
616 auto is_excluded_dn = [&](
const X509_DN& dn) {
622 for(
const auto& c : m_excluded_subtrees) {
623 if(c.base().matches_dn(dn)) {
632 auto is_excluded_dns_name = [&](
const std::string& name) {
633 if(name.empty() || name.starts_with(
".")) {
642 const bool name_has_wildcard = (name.find(
'*') != std::string::npos);
644 for(
const auto& c : m_excluded_subtrees) {
645 if(c.base().matches_dns(name)) {
657 const auto& constraint = std::get<GeneralName::DNS_IDX>(c.base().m_name);
668 auto is_excluded_ipv4 = [&](uint32_t ipv4) {
670 for(
const auto& c : m_excluded_subtrees) {
671 if(c.base().matches_ipv4(ipv4)) {
681 auto is_excluded_ipv6 = [&](
const IPv6Address& ipv6) {
683 for(
const auto& c : m_excluded_subtrees) {
684 if(c.base().matches_ipv6(ipv6)) {
693 if(
auto embedded_v4 = ipv6.as_ipv4()) {
694 for(
const auto& c : m_excluded_subtrees) {
695 if(c.base().matches_ipv4(*embedded_v4)) {
710 for(
const auto& alt_dn : alt_name.directory_names()) {
711 if(is_excluded_dn(alt_dn)) {
716 for(
const auto& alt_dns : alt_name.dns()) {
717 if(is_excluded_dns_name(alt_dns)) {
722 for(
const auto& alt_ipv4 : alt_name.ipv4_address()) {
723 if(is_excluded_ipv4(alt_ipv4)) {
728 for(
const auto& alt_ipv6 : alt_name.ipv6_address()) {
729 if(is_excluded_ipv6(alt_ipv6)) {
734 if(alt_name.count() == 0) {
736 if(cn.find(
".") != std::string::npos) {
738 if(is_excluded_ipv4(ipv4.value())) {
742 if(is_excluded_dns_name(canonicalize_dns_name(cn))) {
#define BOTAN_ASSERT_NOMSG(expr)
#define BOTAN_ASSERT_UNREACHABLE()
const std::set< X509_DN > & directory_names() const
Return the set of directory names included in this alternative name.
const std::set< uint32_t > & ipv4_address() const
Return the set of IPv4 addresses included in this alternative name.
const std::set< std::string > & dns() const
Return the set of DNS names included in this alternative name.
const std::set< IPv6Address > & ipv6_address() const
Return the set of IPv6 addresses included in this alternative name.
BER_Object get_next_object()
BER_Decoder & decode(bool &out)
BER_Decoder start_sequence()
BER_Decoder & decode_optional(T &out, ASN1_Type type_tag, ASN1_Class class_tag, const T &default_value=T())
const uint8_t * bits() const
bool is_a(ASN1_Type type_tag, ASN1_Class class_tag) const
static GeneralName email(std::string_view email)
void decode_from(BER_Decoder &from) override
bool matches_ipv6(const IPv6Address &ip) const
static GeneralName ipv4_address(uint32_t ipv4)
void encode_into(DER_Encoder &to) const override
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
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.
void encode_into(DER_Encoder &to) const override
const GeneralName & base() const
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)
NameConstraints()=default
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
const std::vector< GeneralSubtree > & excluded() const
const X509_DN & subject_dn() const
std::vector< std::string > subject_info(std::string_view name) const
const AlternativeName & subject_alt_name() const
std::vector< std::string > get_attribute(std::string_view attr) const
void decode_from(BER_Decoder &from) override
std::vector< uint8_t > put_in_sequence(const std::vector< uint8_t > &contents)
std::string to_string(const BER_Object &obj)
constexpr std::optional< T > checked_add(T a, T b)
@ ExplicitContextSpecific
std::string tolower_string(std::string_view str)
bool x500_name_cmp(std::string_view name1, std::string_view name2)
std::ostream & operator<<(std::ostream &out, const OID &oid)
constexpr std::optional< T > checked_mul(T a, T b)
std::optional< uint32_t > string_to_ipv4(std::string_view str)
bool host_wildcard_match(std::string_view issued, std::string_view host)