Chris@16: // Chris@16: // ssl/impl/rfc2818_verification.ipp Chris@16: // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Chris@16: // Chris@101: // Copyright (c) 2003-2015 Christopher M. Kohlhoff (chris at kohlhoff dot com) Chris@16: // Chris@16: // Distributed under the Boost Software License, Version 1.0. (See accompanying Chris@16: // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) Chris@16: // Chris@16: Chris@16: #ifndef BOOST_ASIO_SSL_IMPL_RFC2818_VERIFICATION_IPP Chris@16: #define BOOST_ASIO_SSL_IMPL_RFC2818_VERIFICATION_IPP Chris@16: Chris@16: #if defined(_MSC_VER) && (_MSC_VER >= 1200) Chris@16: # pragma once Chris@16: #endif // defined(_MSC_VER) && (_MSC_VER >= 1200) Chris@16: Chris@16: #include Chris@16: Chris@16: #if !defined(BOOST_ASIO_ENABLE_OLD_SSL) Chris@16: # include Chris@16: # include Chris@16: # include Chris@16: # include Chris@16: # include Chris@16: #endif // !defined(BOOST_ASIO_ENABLE_OLD_SSL) Chris@16: Chris@16: #include Chris@16: Chris@16: namespace boost { Chris@16: namespace asio { Chris@16: namespace ssl { Chris@16: Chris@16: #if !defined(BOOST_ASIO_ENABLE_OLD_SSL) Chris@16: Chris@16: bool rfc2818_verification::operator()( Chris@16: bool preverified, verify_context& ctx) const Chris@16: { Chris@16: using namespace std; // For memcmp. Chris@16: Chris@16: // Don't bother looking at certificates that have failed pre-verification. Chris@16: if (!preverified) Chris@16: return false; Chris@16: Chris@16: // We're only interested in checking the certificate at the end of the chain. Chris@16: int depth = X509_STORE_CTX_get_error_depth(ctx.native_handle()); Chris@16: if (depth > 0) Chris@16: return true; Chris@16: Chris@16: // Try converting the host name to an address. If it is an address then we Chris@16: // need to look for an IP address in the certificate rather than a host name. Chris@16: boost::system::error_code ec; Chris@16: ip::address address = ip::address::from_string(host_, ec); Chris@16: bool is_address = !ec; Chris@16: Chris@16: X509* cert = X509_STORE_CTX_get_current_cert(ctx.native_handle()); Chris@16: Chris@16: // Go through the alternate names in the certificate looking for matching DNS Chris@16: // or IP address entries. Chris@16: GENERAL_NAMES* gens = static_cast( Chris@16: X509_get_ext_d2i(cert, NID_subject_alt_name, 0, 0)); Chris@16: for (int i = 0; i < sk_GENERAL_NAME_num(gens); ++i) Chris@16: { Chris@16: GENERAL_NAME* gen = sk_GENERAL_NAME_value(gens, i); Chris@16: if (gen->type == GEN_DNS && !is_address) Chris@16: { Chris@16: ASN1_IA5STRING* domain = gen->d.dNSName; Chris@16: if (domain->type == V_ASN1_IA5STRING && domain->data && domain->length) Chris@16: { Chris@16: const char* pattern = reinterpret_cast(domain->data); Chris@16: std::size_t pattern_length = domain->length; Chris@16: if (match_pattern(pattern, pattern_length, host_.c_str())) Chris@16: { Chris@16: GENERAL_NAMES_free(gens); Chris@16: return true; Chris@16: } Chris@16: } Chris@16: } Chris@16: else if (gen->type == GEN_IPADD && is_address) Chris@16: { Chris@16: ASN1_OCTET_STRING* ip_address = gen->d.iPAddress; Chris@16: if (ip_address->type == V_ASN1_OCTET_STRING && ip_address->data) Chris@16: { Chris@16: if (address.is_v4() && ip_address->length == 4) Chris@16: { Chris@16: ip::address_v4::bytes_type bytes = address.to_v4().to_bytes(); Chris@16: if (memcmp(bytes.data(), ip_address->data, 4) == 0) Chris@16: { Chris@16: GENERAL_NAMES_free(gens); Chris@16: return true; Chris@16: } Chris@16: } Chris@16: else if (address.is_v6() && ip_address->length == 16) Chris@16: { Chris@16: ip::address_v6::bytes_type bytes = address.to_v6().to_bytes(); Chris@16: if (memcmp(bytes.data(), ip_address->data, 16) == 0) Chris@16: { Chris@16: GENERAL_NAMES_free(gens); Chris@16: return true; Chris@16: } Chris@16: } Chris@16: } Chris@16: } Chris@16: } Chris@16: GENERAL_NAMES_free(gens); Chris@16: Chris@16: // No match in the alternate names, so try the common names. We should only Chris@16: // use the "most specific" common name, which is the last one in the list. Chris@16: X509_NAME* name = X509_get_subject_name(cert); Chris@16: int i = -1; Chris@16: ASN1_STRING* common_name = 0; Chris@16: while ((i = X509_NAME_get_index_by_NID(name, NID_commonName, i)) >= 0) Chris@16: { Chris@16: X509_NAME_ENTRY* name_entry = X509_NAME_get_entry(name, i); Chris@16: common_name = X509_NAME_ENTRY_get_data(name_entry); Chris@16: } Chris@16: if (common_name && common_name->data && common_name->length) Chris@16: { Chris@16: const char* pattern = reinterpret_cast(common_name->data); Chris@16: std::size_t pattern_length = common_name->length; Chris@16: if (match_pattern(pattern, pattern_length, host_.c_str())) Chris@16: return true; Chris@16: } Chris@16: Chris@16: return false; Chris@16: } Chris@16: Chris@16: bool rfc2818_verification::match_pattern(const char* pattern, Chris@16: std::size_t pattern_length, const char* host) Chris@16: { Chris@16: using namespace std; // For tolower. Chris@16: Chris@16: const char* p = pattern; Chris@16: const char* p_end = p + pattern_length; Chris@16: const char* h = host; Chris@16: Chris@16: while (p != p_end && *h) Chris@16: { Chris@16: if (*p == '*') Chris@16: { Chris@16: ++p; Chris@16: while (*h && *h != '.') Chris@16: if (match_pattern(p, p_end - p, h++)) Chris@16: return true; Chris@16: } Chris@16: else if (tolower(*p) == tolower(*h)) Chris@16: { Chris@16: ++p; Chris@16: ++h; Chris@16: } Chris@16: else Chris@16: { Chris@16: return false; Chris@16: } Chris@16: } Chris@16: Chris@16: return p == p_end && !*h; Chris@16: } Chris@16: Chris@16: #endif // !defined(BOOST_ASIO_ENABLE_OLD_SSL) Chris@16: Chris@16: } // namespace ssl Chris@16: } // namespace asio Chris@16: } // namespace boost Chris@16: Chris@16: #include Chris@16: Chris@16: #endif // BOOST_ASIO_SSL_IMPL_RFC2818_VERIFICATION_IPP