| Index: net/base/x509_certificate.cc
|
| ===================================================================
|
| --- net/base/x509_certificate.cc (revision 76583)
|
| +++ net/base/x509_certificate.cc (working copy)
|
| @@ -5,12 +5,15 @@
|
| #include "net/base/x509_certificate.h"
|
|
|
| #include <map>
|
| +#include <string>
|
| +#include <vector>
|
|
|
| #include "base/lazy_instance.h"
|
| #include "base/logging.h"
|
| #include "base/metrics/histogram.h"
|
| #include "base/singleton.h"
|
| #include "base/string_piece.h"
|
| +#include "base/string_util.h"
|
| #include "base/time.h"
|
| #include "net/base/pem_tokenizer.h"
|
|
|
| @@ -324,6 +327,132 @@
|
| return true;
|
| }
|
|
|
| +// static
|
| +bool X509Certificate::VerifyHostname(
|
| + const std::string& hostname,
|
| + const std::vector<std::string>& cert_names) {
|
| + DCHECK(!hostname.empty());
|
| +
|
| + // Simple host name validation. A valid domain name must only contain
|
| + // alpha, digits, hyphens, and dots. An IP address may have digits and dots,
|
| + // and also square braces and colons for IPv6 addresses.
|
| + std::string reference_name;
|
| + reference_name.reserve(hostname.length());
|
| +
|
| + bool found_alpha = false;
|
| + bool found_ip6_chars = false;
|
| + bool found_hyphen = false;
|
| + int dot_count = 0;
|
| +
|
| + size_t first_dot_index = std::string::npos;
|
| + for (std::string::const_iterator it = hostname.begin();
|
| + it != hostname.end(); ++it) {
|
| + char c = *it;
|
| + if (IsAsciiAlpha(c)) {
|
| + found_alpha = true;
|
| + c = base::ToLowerASCII(c);
|
| + } else if (c == '.') {
|
| + ++dot_count;
|
| + if (first_dot_index == std::string::npos)
|
| + first_dot_index = reference_name.length();
|
| + } else if (c == ':') {
|
| + found_ip6_chars = true;
|
| + } else if (c == '-') {
|
| + found_hyphen = true;
|
| + } else if (!IsAsciiDigit(c)) {
|
| + LOG(WARNING) << "Invalid char " << c << " in hostname " << hostname;
|
| + return false;
|
| + }
|
| + reference_name.push_back(c);
|
| + }
|
| + DCHECK(!reference_name.empty());
|
| +
|
| + if (found_ip6_chars || !found_alpha) {
|
| + // For now we just do simple localhost IP address support, primarily as
|
| + // it's needed by the test server. TODO(joth): Replace this with full IP
|
| + // address support. See http://crbug.com/62973
|
| + if (hostname == "127.0.0.1") {
|
| + for (size_t index = 0; index < cert_names.size(); ++index) {
|
| + if (cert_names[index] == hostname) {
|
| + DVLOG(1) << "Allowing localhost IP certificate: " << hostname;
|
| + return true;
|
| + }
|
| + }
|
| + }
|
| + NOTIMPLEMENTED() << hostname; // See comment above.
|
| + return false;
|
| + }
|
| +
|
| + // |wildcard_domain| is the remainder of |host| after the leading host
|
| + // component is stripped off, but includes the leading dot e.g.
|
| + // "www.f.com" -> ".f.com".
|
| + // If there is no meaningful domain part to |host| (e.g. it is an IP address
|
| + // or contains no dots) then |wildcard_domain| will be empty.
|
| + // We required at least 3 components (i.e. 2 dots) as a basic protection
|
| + // against too-broad wild-carding.
|
| + base::StringPiece wildcard_domain;
|
| + if (found_alpha && !found_ip6_chars && dot_count >= 2) {
|
| + DCHECK(first_dot_index != std::string::npos);
|
| + wildcard_domain = reference_name;
|
| + wildcard_domain.remove_prefix(first_dot_index);
|
| + DCHECK(wildcard_domain.starts_with("."));
|
| + }
|
| +
|
| + for (std::vector<std::string>::const_iterator it = cert_names.begin();
|
| + it != cert_names.end(); ++it) {
|
| + // Catch badly corrupt cert names up front.
|
| + if (it->empty() || it->find('\0') != std::string::npos) {
|
| + LOG(WARNING) << "Bad name in cert: " << *it;
|
| + continue;
|
| + }
|
| + const std::string cert_name_string(StringToLowerASCII(*it));
|
| + base::StringPiece cert_match(cert_name_string);
|
| +
|
| + // Remove trailing dot, if any.
|
| + if (cert_match.ends_with("."))
|
| + cert_match.remove_suffix(1);
|
| +
|
| + // The hostname must be at least as long as the cert name it is matching,
|
| + // as we require the wildcard (if present) to match at least one character.
|
| + if (cert_match.length() > reference_name.length())
|
| + continue;
|
| +
|
| + if (cert_match == reference_name)
|
| + return true;
|
| +
|
| + // Next see if this cert name starts with a wildcard, so long as the
|
| + // hostname we're matching against has a valid 'domain' part to match.
|
| + // Note the "-10" version of draft-saintandre-tls-server-id-check allows
|
| + // the wildcard to appear anywhere in the leftmost label, rather than
|
| + // requiring it to be the only character. See also http://crbug.com/60719
|
| + if (wildcard_domain.empty() || !cert_match.starts_with("*"))
|
| + continue;
|
| +
|
| + // Erase the * but not the . from the domain, as we need to include the dot
|
| + // in the comparison.
|
| + cert_match.remove_prefix(1);
|
| +
|
| + // Do character by character comparison on the remainder to see
|
| + // if we have a wildcard match. This intentionally does no special handling
|
| + // for any other wildcard characters in |domain|; alternatively it could
|
| + // detect these and skip those candidate cert names.
|
| + if (cert_match == wildcard_domain)
|
| + return true;
|
| + }
|
| + DVLOG(1) << "Could not find any match for " << hostname
|
| + << " (canonicalized as " << reference_name
|
| + << ") in cert names " << JoinString(cert_names, '|');
|
| + return false;
|
| +}
|
| +
|
| +#if !defined(USE_NSS)
|
| +bool X509Certificate::VerifyNameMatch(const std::string& hostname) const {
|
| + std::vector<std::string> dns_names;
|
| + GetDNSNames(&dns_names);
|
| + return VerifyHostname(hostname, dns_names);
|
| +}
|
| +#endif
|
| +
|
| X509Certificate::X509Certificate(OSCertHandle cert_handle,
|
| Source source,
|
| const OSCertHandles& intermediates)
|
|
|