| OLD | NEW |
| 1 // Copyright (c) 2010 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2010 The Chromium Authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 #include "net/base/x509_openssl_util.h" | 5 #include "net/base/x509_openssl_util.h" |
| 6 | 6 |
| 7 #include <algorithm> | 7 #include <algorithm> |
| 8 | 8 |
| 9 #include "base/logging.h" | 9 #include "base/logging.h" |
| 10 #include "base/string_piece.h" | 10 #include "base/string_piece.h" |
| 11 #include "base/string_util.h" | |
| 12 #include "net/base/x509_cert_types.h" | 11 #include "net/base/x509_cert_types.h" |
| 13 | 12 |
| 14 namespace net { | 13 namespace net { |
| 15 | 14 |
| 16 namespace x509_openssl_util { | 15 namespace x509_openssl_util { |
| 17 | 16 |
| 18 bool ParsePrincipalKeyAndValueByIndex(X509_NAME* name, | 17 bool ParsePrincipalKeyAndValueByIndex(X509_NAME* name, |
| 19 int index, | 18 int index, |
| 20 std::string* key, | 19 std::string* key, |
| 21 std::string* value) { | 20 std::string* value) { |
| (...skipping 41 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 63 return false; | 62 return false; |
| 64 | 63 |
| 65 base::StringPiece str_date(reinterpret_cast<const char*>(x509_time->data), | 64 base::StringPiece str_date(reinterpret_cast<const char*>(x509_time->data), |
| 66 x509_time->length); | 65 x509_time->length); |
| 67 | 66 |
| 68 CertDateFormat format = x509_time->type == V_ASN1_UTCTIME ? | 67 CertDateFormat format = x509_time->type == V_ASN1_UTCTIME ? |
| 69 CERT_DATE_FORMAT_UTC_TIME : CERT_DATE_FORMAT_GENERALIZED_TIME; | 68 CERT_DATE_FORMAT_UTC_TIME : CERT_DATE_FORMAT_GENERALIZED_TIME; |
| 70 return ParseCertificateDate(str_date, format, time); | 69 return ParseCertificateDate(str_date, format, time); |
| 71 } | 70 } |
| 72 | 71 |
| 73 // TODO(joth): Investigate if we can upstream this into the OpenSSL library, | |
| 74 // to avoid duplicating this logic across projects. | |
| 75 bool VerifyHostname(const std::string& hostname, | |
| 76 const std::vector<std::string>& cert_names) { | |
| 77 DCHECK(!hostname.empty()); | |
| 78 | |
| 79 // Simple host name validation. A valid domain name must only contain | |
| 80 // alpha, digits, hyphens, and dots. An IP address may have digits and dots, | |
| 81 // and also square braces and colons for IPv6 addresses. | |
| 82 std::string reference_name; | |
| 83 reference_name.reserve(hostname.length()); | |
| 84 | |
| 85 bool found_alpha = false; | |
| 86 bool found_ip6_chars = false; | |
| 87 bool found_hyphen = false; | |
| 88 int dot_count = 0; | |
| 89 | |
| 90 size_t first_dot_index = std::string::npos; | |
| 91 for (std::string::const_iterator it = hostname.begin(); | |
| 92 it != hostname.end(); ++it) { | |
| 93 char c = *it; | |
| 94 if (IsAsciiAlpha(c)) { | |
| 95 found_alpha = true; | |
| 96 c = base::ToLowerASCII(c); | |
| 97 } else if (c == '.') { | |
| 98 ++dot_count; | |
| 99 if (first_dot_index == std::string::npos) | |
| 100 first_dot_index = reference_name.length(); | |
| 101 } else if (c == ':') { | |
| 102 found_ip6_chars = true; | |
| 103 } else if (c == '-') { | |
| 104 found_hyphen = true; | |
| 105 } else if (!IsAsciiDigit(c)) { | |
| 106 LOG(WARNING) << "Invalid char " << c << " in hostname " << hostname; | |
| 107 return false; | |
| 108 } | |
| 109 reference_name.push_back(c); | |
| 110 } | |
| 111 DCHECK(!reference_name.empty()); | |
| 112 | |
| 113 if (found_ip6_chars || !found_alpha) { | |
| 114 // For now we just do simple localhost IP address support, primarily as | |
| 115 // it's needed by the test server. TODO(joth): Replace this with full IP | |
| 116 // address support. See http://crbug.com/62973 | |
| 117 if (hostname == "127.0.0.1" && | |
| 118 std::find(cert_names.begin(), cert_names.end(), hostname) != | |
| 119 cert_names.end()) { | |
| 120 DVLOG(1) << "Allowing localhost IP certificate: " << hostname; | |
| 121 return true; | |
| 122 } | |
| 123 NOTIMPLEMENTED() << hostname; // See comment above. | |
| 124 return false; | |
| 125 } | |
| 126 | |
| 127 // |wildcard_domain| is the remainder of |host| after the leading host | |
| 128 // component is stripped off, but includes the leading dot e.g. | |
| 129 // "www.f.com" -> ".f.com". | |
| 130 // If there is no meaningful domain part to |host| (e.g. it is an IP address | |
| 131 // or contains no dots) then |wildcard_domain| will be empty. | |
| 132 // We required at least 3 components (i.e. 2 dots) as a basic protection | |
| 133 // against too-broad wild-carding. | |
| 134 base::StringPiece wildcard_domain; | |
| 135 if (found_alpha && !found_ip6_chars && dot_count >= 2) { | |
| 136 DCHECK(first_dot_index != std::string::npos); | |
| 137 wildcard_domain = reference_name; | |
| 138 wildcard_domain.remove_prefix(first_dot_index); | |
| 139 DCHECK(wildcard_domain.starts_with(".")); | |
| 140 } | |
| 141 | |
| 142 for (std::vector<std::string>::const_iterator it = cert_names.begin(); | |
| 143 it != cert_names.end(); ++it) { | |
| 144 // Catch badly corrupt cert names up front. | |
| 145 if (it->empty() || it->find('\0') != std::string::npos) { | |
| 146 LOG(WARNING) << "Bad name in cert: " << *it; | |
| 147 continue; | |
| 148 } | |
| 149 const std::string cert_name_string(StringToLowerASCII(*it)); | |
| 150 base::StringPiece cert_match(cert_name_string); | |
| 151 | |
| 152 // Remove trailing dot, if any. | |
| 153 if (cert_match.ends_with(".")) | |
| 154 cert_match.remove_suffix(1); | |
| 155 | |
| 156 // The hostname must be at least as long as the cert name it is matching, | |
| 157 // as we require the wildcard (if present) to match at least one character. | |
| 158 if (cert_match.length() > reference_name.length()) | |
| 159 continue; | |
| 160 | |
| 161 if (cert_match == reference_name) | |
| 162 return true; | |
| 163 | |
| 164 // Next see if this cert name starts with a wildcard, so long as the | |
| 165 // hostname we're matching against has a valid 'domain' part to match. | |
| 166 // Note the "-10" version of draft-saintandre-tls-server-id-check allows | |
| 167 // the wildcard to appear anywhere in the leftmost label, rather than | |
| 168 // requiring it to be the only character. See also http://crbug.com/60719 | |
| 169 if (wildcard_domain.empty() || !cert_match.starts_with("*")) | |
| 170 continue; | |
| 171 | |
| 172 // Erase the * but not the . from the domain, as we need to include the dot | |
| 173 // in the comparison. | |
| 174 cert_match.remove_prefix(1); | |
| 175 | |
| 176 // Do character by character comparison on the remainder to see | |
| 177 // if we have a wildcard match. This intentionally does no special handling | |
| 178 // for any other wildcard characters in |domain|; alternatively it could | |
| 179 // detect these and skip those candidate cert names. | |
| 180 if (cert_match == wildcard_domain) | |
| 181 return true; | |
| 182 } | |
| 183 DVLOG(1) << "Could not find any match for " << hostname | |
| 184 << " (canonicalized as " << reference_name | |
| 185 << ") in cert names " << JoinString(cert_names, '|'); | |
| 186 return false; | |
| 187 } | |
| 188 | |
| 189 } // namespace x509_openssl_util | 72 } // namespace x509_openssl_util |
| 190 | 73 |
| 191 } // namespace net | 74 } // namespace net |
| OLD | NEW |