diff options
Diffstat (limited to 'src/lib/cert/x509/name_constraint.cpp')
-rw-r--r-- | src/lib/cert/x509/name_constraint.cpp | 269 |
1 files changed, 269 insertions, 0 deletions
diff --git a/src/lib/cert/x509/name_constraint.cpp b/src/lib/cert/x509/name_constraint.cpp new file mode 100644 index 000000000..d763076cb --- /dev/null +++ b/src/lib/cert/x509/name_constraint.cpp @@ -0,0 +1,269 @@ +/* +* X.509 Name Constraint +* (C) 2015 Kai Michaelis +* +* Botan is released under the Simplified BSD License (see license.txt) +*/ + +#include <botan/name_constraint.h> +#include <botan/ber_dec.h> +#include <botan/der_enc.h> +#include <botan/charset.h> +#include <botan/loadstor.h> +#include <botan/x509_dn.h> +#include <botan/x509cert.h> +#include <sstream> + +namespace Botan { + +GeneralName::GeneralName(const std::string& v) : GeneralName() + { + size_t p = v.find(':'); + + if(p != std::string::npos) + { + m_type = v.substr(0,p); + m_name = v.substr(p + 1,std::string::npos); + } + else + { + throw Invalid_Argument("Failed to decode Name Constraint"); + } + } + +void GeneralName::encode_into(class DER_Encoder&) const + { + throw std::runtime_error("General Name encoding not implemented"); + } + +void GeneralName::decode_from(class BER_Decoder& ber) + { + BER_Object obj = ber.get_next_object(); + if((obj.class_tag != CONTEXT_SPECIFIC) && + (obj.class_tag != (CONTEXT_SPECIFIC | CONSTRUCTED))) + throw Decoding_Error("Invalid class tag while decoding GeneralName"); + + const ASN1_Tag tag = obj.type_tag; + + if(tag == 1 || tag == 2 || tag == 6) + { + m_name = Charset::transcode(ASN1::to_string(obj),LATIN1_CHARSET,LOCAL_CHARSET); + + if(tag == 1) + { + m_type = "RFC822"; + } + else if(tag == 2) + { + m_type = "DNS"; + } + else if(tag == 6) + { + m_type = "URI"; + } + } + else if(tag == 4) + { + X509_DN dn; + std::multimap<std::string, std::string> nam; + BER_Decoder dec(obj.value); + std::stringstream ss; + + dn.decode_from(dec); + ss << dn; + + m_name = ss.str(); + m_type = "DN"; + } + else if(tag == 7) + { + if(obj.value.size() == 8) + { + const std::vector<byte> ip(obj.value.begin(),obj.value.begin() + 4); + const std::vector<byte> net(obj.value.begin() + 4,obj.value.end()); + m_type = "IP"; + m_name = ipv4_to_string(load_be<u32bit>(ip.data(),0)) + "/" + ipv4_to_string(load_be<u32bit>(net.data(),0)); + } + } + else + { + throw Decoding_Error("Found unknown GeneralName type"); + } + } + +GeneralName::MatchResult GeneralName::matches(const X509_Certificate& cert) const + { + if(type() == "DNS" || type() == "DN" || type() == "IP") + { + std::vector<std::string> nam; + std::function<bool(const GeneralName*,const std::string&)> match_fn; + + if(type() == "DNS") + { + match_fn = std::mem_fn(&GeneralName::matches_dns); + nam = cert.subject_info("DNS"); + + if(nam.empty()) + { + nam = cert.subject_info("CN"); + } + } + else if(type() == "DN") + { + std::stringstream ss; + + match_fn = std::mem_fn(&GeneralName::matches_dn); + ss << cert.subject_dn(); + + nam.push_back(ss.str()); + } + else if(type() == "IP") + { + match_fn = std::mem_fn(&GeneralName::matches_ip); + nam = cert.subject_info("IP"); + } + + else + { + throw Unsupported_Argument("Unsupported Name Constraint"); + } + + if(nam.empty()) + { + return MatchResult::NotFound; + } + + bool some = false; + bool all = true; + + for(const std::string& n: nam) + { + bool m = match_fn(this,n); + + some |= m; + all &= m; + } + + if(all) + { + return MatchResult::All; + } + else if(some) + { + return MatchResult::Some; + } + else + { + return MatchResult::None; + } + } + else + { + return MatchResult::UnknownType; + } + } + +bool GeneralName::matches_dns(const std::string& nam) const + { + if(nam.size() == name().size()) + return nam == name(); + else if(name().size() > nam.size()) + return false; + else // nam.size() > name().size() + { + std::string constr = name().front() == '.' ? name() : "." + name(); + // constr is suffix of nam + return constr == nam.substr(nam.size() - constr.size(),constr.size()); + } + } + +bool GeneralName::matches_dn(const std::string& nam) const + { + std::stringstream ss(nam); + std::stringstream tt(name()); + X509_DN nam_dn, my_dn; + + ss >> nam_dn; + tt >> my_dn; + + auto attr = nam_dn.get_attributes(); + bool ret = true; + int trys = 0; + + for(const std::pair<OID,std::string>& c: my_dn.get_attributes()) + { + auto i = attr.equal_range(c.first); + + if(i.first != i.second) + { + trys += 1; + ret &= i.first->second == c.second; + } + } + + return trys > 0 && ret; + } + +bool GeneralName::matches_ip(const std::string& nam) const + { + u32bit ip = string_to_ipv4(nam); + std::vector<std::string> p = split_on(name(),'/'); + + if(p.size() != 2) + throw Decoding_Error("failed to parse IPv4 address"); + + u32bit net = string_to_ipv4(p.at(0)); + u32bit mask = string_to_ipv4(p.at(1)); + + return (ip & mask) == net; + } + +std::ostream& operator<<(std::ostream& os, const GeneralName& gn) + { + os << gn.type() << ":" << gn.name(); + return os; + } + +GeneralSubtree::GeneralSubtree(const std::string& v) : GeneralSubtree() + { + size_t p0, p1; + size_t min = stoull(v,&p0,10); + size_t max = stoull(v.substr(p0 + 1),&p1,10); + GeneralName gn(v.substr(p0 + p1 + 2)); + + if(p0 > 0 && p1 > 0) + { + m_minimum = min; + m_maximum = max; + m_base = gn; + } + else + { + throw Invalid_Argument("Failed to decode Name Constraint"); + } + } + +void GeneralSubtree::encode_into(class DER_Encoder&) const + { + throw std::runtime_error("General Subtree encoding not implemented"); + } + +void GeneralSubtree::decode_from(class BER_Decoder& ber) + { + ber.start_cons(SEQUENCE) + .decode(m_base) + .decode_optional(m_minimum,ASN1_Tag(0),CONTEXT_SPECIFIC,size_t(0)) + .end_cons(); + + if(m_minimum != 0) + throw Decoding_Error("GeneralSubtree minimum must be 0"); + + m_maximum = std::numeric_limits<std::size_t>::max(); + } + +std::ostream& operator<<(std::ostream& os, const GeneralSubtree& gs) + { + os << gs.minimum() << "," << gs.maximum() << "," << gs.base(); + return os; + } +} |