Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(68)

Side by Side Diff: components/ssl_errors/error_classification.cc

Issue 1355413003: Move error classification into the ssl_errors component (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: remove size_t / int mixing Created 5 years, 1 month ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
1 // Copyright 2014 The Chromium Authors. All rights reserved. 1 // Copyright 2015 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 "components/ssl_errors/error_classification.h"
6
5 #include <vector> 7 #include <vector>
6 8
7 #include "chrome/browser/ssl/ssl_error_classification.h"
8
9 #include "base/build_time.h" 9 #include "base/build_time.h"
10 #include "base/lazy_instance.h" 10 #include "base/lazy_instance.h"
11 #include "base/metrics/histogram_macros.h" 11 #include "base/metrics/histogram_macros.h"
12 #include "base/strings/string_split.h" 12 #include "base/strings/string_split.h"
13 #include "base/strings/utf_string_conversions.h" 13 #include "base/strings/utf_string_conversions.h"
14 #include "base/time/time.h" 14 #include "base/time/time.h"
15 #include "components/ssl_errors/error_info.h" 15 #include "components/ssl_errors/error_info.h"
16 #include "components/url_formatter/url_formatter.h" 16 #include "components/url_formatter/url_formatter.h"
17 #include "net/base/net_util.h" 17 #include "net/base/net_util.h"
18 #include "net/base/registry_controlled_domains/registry_controlled_domain.h" 18 #include "net/base/registry_controlled_domains/registry_controlled_domain.h"
19 #include "net/cert/x509_cert_types.h" 19 #include "net/cert/x509_cert_types.h"
20 #include "net/cert/x509_certificate.h" 20 #include "net/cert/x509_certificate.h"
21 #include "url/gurl.h" 21 #include "url/gurl.h"
22 22
23 #if defined(OS_WIN) 23 #if defined(OS_WIN)
24 #include "base/win/win_util.h" 24 #include "base/win/win_util.h"
25 #include "base/win/windows_version.h" 25 #include "base/win/windows_version.h"
26 #endif 26 #endif
27 27
28 using base::Time; 28 using base::Time;
29 using base::TimeTicks; 29 using base::TimeTicks;
30 using base::TimeDelta; 30 using base::TimeDelta;
31 31
32 namespace ssl_errors {
32 namespace { 33 namespace {
33 34
34 // Events for UMA. Do not reorder or change! 35 // Events for UMA. Do not reorder or change!
35 enum SSLInterstitialCause { 36 enum SSLInterstitialCause {
36 CLOCK_PAST, 37 CLOCK_PAST,
37 CLOCK_FUTURE, 38 CLOCK_FUTURE,
38 WWW_SUBDOMAIN_MATCH, 39 WWW_SUBDOMAIN_MATCH,
39 SUBDOMAIN_MATCH, 40 SUBDOMAIN_MATCH,
40 SUBDOMAIN_INVERSE_MATCH, 41 SUBDOMAIN_INVERSE_MATCH,
41 SUBDOMAIN_OUTSIDE_WILDCARD, 42 SUBDOMAIN_OUTSIDE_WILDCARD,
(...skipping 11 matching lines...) Expand all
53 void RecordSSLInterstitialCause(bool overridable, SSLInterstitialCause event) { 54 void RecordSSLInterstitialCause(bool overridable, SSLInterstitialCause event) {
54 if (overridable) { 55 if (overridable) {
55 UMA_HISTOGRAM_ENUMERATION("interstitial.ssl.cause.overridable", event, 56 UMA_HISTOGRAM_ENUMERATION("interstitial.ssl.cause.overridable", event,
56 UNUSED_INTERSTITIAL_CAUSE_ENTRY); 57 UNUSED_INTERSTITIAL_CAUSE_ENTRY);
57 } else { 58 } else {
58 UMA_HISTOGRAM_ENUMERATION("interstitial.ssl.cause.nonoverridable", event, 59 UMA_HISTOGRAM_ENUMERATION("interstitial.ssl.cause.nonoverridable", event,
59 UNUSED_INTERSTITIAL_CAUSE_ENTRY); 60 UNUSED_INTERSTITIAL_CAUSE_ENTRY);
60 } 61 }
61 } 62 }
62 63
63 int GetLevensteinDistance(const std::string& str1, 64 size_t GetLevensteinDistance(const std::string& str1, const std::string& str2) {
64 const std::string& str2) {
65 if (str1 == str2) 65 if (str1 == str2)
66 return 0; 66 return 0;
67 if (str1.size() == 0) 67 if (str1.size() == 0)
68 return str2.size(); 68 return str2.size();
69 if (str2.size() == 0) 69 if (str2.size() == 0)
70 return str1.size(); 70 return str1.size();
71 std::vector<int> kFirstRow(str2.size() + 1, 0); 71 std::vector<size_t> kFirstRow(str2.size() + 1, 0);
72 std::vector<int> kSecondRow(str2.size() + 1, 0); 72 std::vector<size_t> kSecondRow(str2.size() + 1, 0);
73 73
74 for (size_t i = 0; i < kFirstRow.size(); ++i) 74 for (size_t i = 0; i < kFirstRow.size(); ++i)
75 kFirstRow[i] = i; 75 kFirstRow[i] = i;
76 for (size_t i = 0; i < str1.size(); ++i) { 76 for (size_t i = 0; i < str1.size(); ++i) {
77 kSecondRow[0] = i + 1; 77 kSecondRow[0] = i + 1;
78 for (size_t j = 0; j < str2.size(); ++j) { 78 for (size_t j = 0; j < str2.size(); ++j) {
79 int cost = str1[i] == str2[j] ? 0 : 1; 79 int cost = str1[i] == str2[j] ? 0 : 1;
80 kSecondRow[j+1] = std::min(std::min( 80 kSecondRow[j + 1] =
81 kSecondRow[j] + 1, kFirstRow[j + 1] + 1), kFirstRow[j] + cost); 81 std::min(std::min(kSecondRow[j] + 1, kFirstRow[j + 1] + 1),
82 kFirstRow[j] + cost);
82 } 83 }
83 for (size_t j = 0; j < kFirstRow.size(); j++) 84 for (size_t j = 0; j < kFirstRow.size(); j++)
84 kFirstRow[j] = kSecondRow[j]; 85 kFirstRow[j] = kSecondRow[j];
85 } 86 }
86 return kSecondRow[str2.size()]; 87 return kSecondRow[str2.size()];
87 } 88 }
88 89
90 std::vector<HostnameTokens> GetTokenizedDNSNames(
91 const std::vector<std::string>& dns_names) {
92 std::vector<HostnameTokens> dns_name_tokens;
93 for (const auto& dns_name : dns_names) {
94 HostnameTokens dns_name_token_single;
95 if (dns_name.empty() || dns_name.find('\0') != std::string::npos ||
96 !(IsHostNameKnownTLD(dns_name))) {
97 dns_name_token_single.push_back(std::string());
98 } else {
99 dns_name_token_single = Tokenize(dns_name);
100 }
101 dns_name_tokens.push_back(dns_name_token_single);
102 }
103 return dns_name_tokens;
104 }
105
106 size_t FindSubDomainDifference(const HostnameTokens& potential_subdomain,
107 const HostnameTokens& parent) {
108 // A check to ensure that the number of tokens in the tokenized_parent is
109 // less than the tokenized_potential_subdomain.
110 if (parent.size() >= potential_subdomain.size())
111 return 0;
112
113 size_t tokens_match = 0;
114 size_t diff_size = potential_subdomain.size() - parent.size();
115 for (size_t i = 0; i < parent.size(); ++i) {
116 if (parent[i] == potential_subdomain[i + diff_size])
117 tokens_match++;
118 }
119 if (tokens_match == parent.size())
120 return diff_size;
121 return 0;
122 }
123
89 // The time to use when doing build time operations in browser tests. 124 // The time to use when doing build time operations in browser tests.
90 base::LazyInstance<base::Time> g_testing_build_time = LAZY_INSTANCE_INITIALIZER; 125 base::LazyInstance<base::Time> g_testing_build_time = LAZY_INSTANCE_INITIALIZER;
91 126
92 } // namespace 127 } // namespace
93 128
94 SSLErrorClassification::SSLErrorClassification(const base::Time& current_time, 129 void RecordUMAStatistics(bool overridable,
95 const GURL& url, 130 const base::Time& current_time,
96 int cert_error, 131 const GURL& request_url,
97 const net::X509Certificate& cert) 132 int cert_error,
98 : current_time_(current_time), 133 const net::X509Certificate& cert) {
99 request_url_(url),
100 cert_error_(cert_error),
101 cert_(cert) {}
102
103 SSLErrorClassification::~SSLErrorClassification() { }
104
105 void SSLErrorClassification::RecordUMAStatistics(
106 bool overridable) const {
107 ssl_errors::ErrorInfo::ErrorType type = 134 ssl_errors::ErrorInfo::ErrorType type =
108 ssl_errors::ErrorInfo::NetErrorToErrorType(cert_error_); 135 ssl_errors::ErrorInfo::NetErrorToErrorType(cert_error);
109 UMA_HISTOGRAM_ENUMERATION("interstitial.ssl_error_type", type, 136 UMA_HISTOGRAM_ENUMERATION("interstitial.ssl_error_type", type,
110 ssl_errors::ErrorInfo::END_OF_ENUM); 137 ssl_errors::ErrorInfo::END_OF_ENUM);
111 switch (type) { 138 switch (type) {
112 case ssl_errors::ErrorInfo::CERT_DATE_INVALID: { 139 case ssl_errors::ErrorInfo::CERT_DATE_INVALID: {
113 if (IsUserClockInThePast(base::Time::NowFromSystemTime())) { 140 if (IsUserClockInThePast(base::Time::NowFromSystemTime())) {
114 RecordSSLInterstitialCause(overridable, CLOCK_PAST); 141 RecordSSLInterstitialCause(overridable, CLOCK_PAST);
115 } else if (IsUserClockInTheFuture(base::Time::NowFromSystemTime())) { 142 } else if (IsUserClockInTheFuture(base::Time::NowFromSystemTime())) {
116 RecordSSLInterstitialCause(overridable, CLOCK_FUTURE); 143 RecordSSLInterstitialCause(overridable, CLOCK_FUTURE);
117 } else if (cert_.HasExpired() && 144 } else if (cert.HasExpired() &&
118 (current_time_ - cert_.valid_expiry()).InDays() < 28) { 145 (current_time - cert.valid_expiry()).InDays() < 28) {
119 RecordSSLInterstitialCause(overridable, EXPIRED_RECENTLY); 146 RecordSSLInterstitialCause(overridable, EXPIRED_RECENTLY);
120 } 147 }
121 break; 148 break;
122 } 149 }
123 case ssl_errors::ErrorInfo::CERT_COMMON_NAME_INVALID: { 150 case ssl_errors::ErrorInfo::CERT_COMMON_NAME_INVALID: {
124 std::string host_name = request_url_.host(); 151 std::string host_name = request_url.host();
125 if (IsHostNameKnownTLD(host_name)) { 152 if (IsHostNameKnownTLD(host_name)) {
126 Tokens host_name_tokens = Tokenize(host_name); 153 HostnameTokens host_name_tokens = Tokenize(host_name);
127 if (IsWWWSubDomainMatch()) 154 if (IsWWWSubDomainMatch(request_url, cert))
128 RecordSSLInterstitialCause(overridable, WWW_SUBDOMAIN_MATCH); 155 RecordSSLInterstitialCause(overridable, WWW_SUBDOMAIN_MATCH);
129 if (IsSubDomainOutsideWildcard(host_name_tokens)) 156 if (IsSubDomainOutsideWildcard(request_url, cert))
130 RecordSSLInterstitialCause(overridable, SUBDOMAIN_OUTSIDE_WILDCARD); 157 RecordSSLInterstitialCause(overridable, SUBDOMAIN_OUTSIDE_WILDCARD);
131 std::vector<std::string> dns_names; 158 std::vector<std::string> dns_names;
132 cert_.GetDNSNames(&dns_names); 159 cert.GetDNSNames(&dns_names);
133 std::vector<Tokens> dns_name_tokens = GetTokenizedDNSNames(dns_names); 160 std::vector<HostnameTokens> dns_name_tokens =
161 GetTokenizedDNSNames(dns_names);
134 if (NameUnderAnyNames(host_name_tokens, dns_name_tokens)) 162 if (NameUnderAnyNames(host_name_tokens, dns_name_tokens))
135 RecordSSLInterstitialCause(overridable, SUBDOMAIN_MATCH); 163 RecordSSLInterstitialCause(overridable, SUBDOMAIN_MATCH);
136 if (AnyNamesUnderName(dns_name_tokens, host_name_tokens)) 164 if (AnyNamesUnderName(dns_name_tokens, host_name_tokens))
137 RecordSSLInterstitialCause(overridable, SUBDOMAIN_INVERSE_MATCH); 165 RecordSSLInterstitialCause(overridable, SUBDOMAIN_INVERSE_MATCH);
138 if (IsCertLikelyFromMultiTenantHosting()) 166 if (IsCertLikelyFromMultiTenantHosting(request_url, cert))
139 RecordSSLInterstitialCause(overridable, LIKELY_MULTI_TENANT_HOSTING); 167 RecordSSLInterstitialCause(overridable, LIKELY_MULTI_TENANT_HOSTING);
140 if (IsCertLikelyFromSameDomain()) 168 if (IsCertLikelyFromSameDomain(request_url, cert))
141 RecordSSLInterstitialCause(overridable, LIKELY_SAME_DOMAIN); 169 RecordSSLInterstitialCause(overridable, LIKELY_SAME_DOMAIN);
142 } else { 170 } else {
143 RecordSSLInterstitialCause(overridable, HOST_NAME_NOT_KNOWN_TLD); 171 RecordSSLInterstitialCause(overridable, HOST_NAME_NOT_KNOWN_TLD);
144 } 172 }
145 break; 173 break;
146 } 174 }
147 case ssl_errors::ErrorInfo::CERT_AUTHORITY_INVALID: { 175 case ssl_errors::ErrorInfo::CERT_AUTHORITY_INVALID: {
148 const std::string& hostname = request_url_.HostNoBrackets(); 176 const std::string& hostname = request_url.HostNoBrackets();
149 if (net::IsLocalhost(hostname)) 177 if (net::IsLocalhost(hostname))
150 RecordSSLInterstitialCause(overridable, LOCALHOST); 178 RecordSSLInterstitialCause(overridable, LOCALHOST);
151 if (IsHostnameNonUniqueOrDotless(hostname)) 179 if (IsHostnameNonUniqueOrDotless(hostname))
152 RecordSSLInterstitialCause(overridable, PRIVATE_URL); 180 RecordSSLInterstitialCause(overridable, PRIVATE_URL);
153 if (net::X509Certificate::IsSelfSigned(cert_.os_cert_handle())) 181 if (net::X509Certificate::IsSelfSigned(cert.os_cert_handle()))
154 RecordSSLInterstitialCause(overridable, SELF_SIGNED); 182 RecordSSLInterstitialCause(overridable, SELF_SIGNED);
155 break; 183 break;
156 } 184 }
157 default: 185 default:
158 break; 186 break;
159 } 187 }
160 UMA_HISTOGRAM_ENUMERATION("interstitial.ssl.connection_type", 188 UMA_HISTOGRAM_ENUMERATION("interstitial.ssl.connection_type",
161 net::NetworkChangeNotifier::GetConnectionType(), 189 net::NetworkChangeNotifier::GetConnectionType(),
162 net::NetworkChangeNotifier::CONNECTION_LAST); 190 net::NetworkChangeNotifier::CONNECTION_LAST);
163 } 191 }
164 192
165 bool SSLErrorClassification::IsUserClockInThePast(const base::Time& time_now) { 193 bool IsUserClockInThePast(const base::Time& time_now) {
166 base::Time build_time; 194 base::Time build_time;
167 if (!g_testing_build_time.Get().is_null()) { 195 if (!g_testing_build_time.Get().is_null()) {
168 build_time = g_testing_build_time.Get(); 196 build_time = g_testing_build_time.Get();
169 } else { 197 } else {
170 #if defined(DONT_EMBED_BUILD_METADATA) && !defined(OFFICIAL_BUILD) 198 #if defined(DONT_EMBED_BUILD_METADATA) && !defined(OFFICIAL_BUILD)
171 return false; 199 return false;
172 #else 200 #else
173 build_time = base::GetBuildTime(); 201 build_time = base::GetBuildTime();
174 #endif 202 #endif
175 } 203 }
176 204
177 if (time_now < build_time - base::TimeDelta::FromDays(2)) 205 if (time_now < build_time - base::TimeDelta::FromDays(2))
178 return true; 206 return true;
179 return false; 207 return false;
180 } 208 }
181 209
182 bool SSLErrorClassification::IsUserClockInTheFuture( 210 bool IsUserClockInTheFuture(const base::Time& time_now) {
183 const base::Time& time_now) {
184 base::Time build_time; 211 base::Time build_time;
185 if (!g_testing_build_time.Get().is_null()) { 212 if (!g_testing_build_time.Get().is_null()) {
186 build_time = g_testing_build_time.Get(); 213 build_time = g_testing_build_time.Get();
187 } else { 214 } else {
188 #if defined(DONT_EMBED_BUILD_METADATA) && !defined(OFFICIAL_BUILD) 215 #if defined(DONT_EMBED_BUILD_METADATA) && !defined(OFFICIAL_BUILD)
189 return false; 216 return false;
190 #else 217 #else
191 build_time = base::GetBuildTime(); 218 build_time = base::GetBuildTime();
192 #endif 219 #endif
193 } 220 }
194 221
195 if (time_now > build_time + base::TimeDelta::FromDays(365)) 222 if (time_now > build_time + base::TimeDelta::FromDays(365))
196 return true; 223 return true;
197 return false; 224 return false;
198 } 225 }
199 226
200 // static 227 void SetBuildTimeForTesting(const base::Time& testing_time) {
201 void SSLErrorClassification::SetBuildTimeForTesting(
202 const base::Time& testing_time) {
203 g_testing_build_time.Get() = testing_time; 228 g_testing_build_time.Get() = testing_time;
204 } 229 }
205 230
206 bool SSLErrorClassification::MaybeWindowsLacksSHA256Support() { 231 bool IsHostNameKnownTLD(const std::string& host_name) {
207 #if defined(OS_WIN) 232 size_t tld_length = net::registry_controlled_domains::GetRegistryLength(
208 return !base::win::MaybeHasSHA256Support(); 233 host_name, net::registry_controlled_domains::EXCLUDE_UNKNOWN_REGISTRIES,
209 #else 234 net::registry_controlled_domains::INCLUDE_PRIVATE_REGISTRIES);
210 return false;
211 #endif
212 }
213
214 bool SSLErrorClassification::IsHostNameKnownTLD(const std::string& host_name) {
215 size_t tld_length =
216 net::registry_controlled_domains::GetRegistryLength(
217 host_name,
218 net::registry_controlled_domains::EXCLUDE_UNKNOWN_REGISTRIES,
219 net::registry_controlled_domains::INCLUDE_PRIVATE_REGISTRIES);
220 if (tld_length == 0 || tld_length == std::string::npos) 235 if (tld_length == 0 || tld_length == std::string::npos)
221 return false; 236 return false;
222 return true; 237 return true;
223 } 238 }
224 239
225 std::vector<SSLErrorClassification::Tokens> SSLErrorClassification:: 240 HostnameTokens Tokenize(const std::string& name) {
226 GetTokenizedDNSNames(const std::vector<std::string>& dns_names) { 241 return base::SplitString(name, ".", base::KEEP_WHITESPACE,
227 std::vector<std::vector<std::string>> dns_name_tokens; 242 base::SPLIT_WANT_ALL);
228 for (size_t i = 0; i < dns_names.size(); ++i) {
229 std::vector<std::string> dns_name_token_single;
230 if (dns_names[i].empty() || dns_names[i].find('\0') != std::string::npos
231 || !(IsHostNameKnownTLD(dns_names[i]))) {
232 dns_name_token_single.push_back(std::string());
233 } else {
234 dns_name_token_single = Tokenize(dns_names[i]);
235 }
236 dns_name_tokens.push_back(dns_name_token_single);
237 }
238 return dns_name_tokens;
239 }
240
241 size_t SSLErrorClassification::FindSubDomainDifference(
242 const Tokens& potential_subdomain, const Tokens& parent) const {
243 // A check to ensure that the number of tokens in the tokenized_parent is
244 // less than the tokenized_potential_subdomain.
245 if (parent.size() >= potential_subdomain.size())
246 return 0;
247
248 size_t tokens_match = 0;
249 size_t diff_size = potential_subdomain.size() - parent.size();
250 for (size_t i = 0; i < parent.size(); ++i) {
251 if (parent[i] == potential_subdomain[i + diff_size])
252 tokens_match++;
253 }
254 if (tokens_match == parent.size())
255 return diff_size;
256 return 0;
257 }
258
259 SSLErrorClassification::Tokens SSLErrorClassification::
260 Tokenize(const std::string& name) {
261 return base::SplitString(
262 name, ".", base::KEEP_WHITESPACE, base::SPLIT_WANT_ALL);
263 } 243 }
264 244
265 // We accept the inverse case for www for historical reasons. 245 // We accept the inverse case for www for historical reasons.
266 bool SSLErrorClassification::GetWWWSubDomainMatch( 246 bool IsWWWSubDomainMatch(const GURL& request_url,
267 const std::string& host_name, 247 const net::X509Certificate& cert) {
268 const std::vector<std::string>& dns_names, 248 std::string www_host;
269 std::string* www_match_host_name) { 249 std::vector<std::string> dns_names;
250 cert.GetDNSNames(&dns_names);
251 return GetWWWSubDomainMatch(request_url, dns_names, &www_host);
252 }
253
254 bool GetWWWSubDomainMatch(const GURL& request_url,
255 const std::vector<std::string>& dns_names,
256 std::string* www_match_host_name) {
257 const std::string& host_name = request_url.host();
258
270 if (IsHostNameKnownTLD(host_name)) { 259 if (IsHostNameKnownTLD(host_name)) {
271 // Need to account for all possible domains given in the SSL certificate. 260 // Need to account for all possible domains given in the SSL certificate.
272 for (size_t i = 0; i < dns_names.size(); ++i) { 261 for (size_t i = 0; i < dns_names.size(); ++i) {
273 if (dns_names[i].empty() || 262 if (dns_names[i].empty() ||
274 dns_names[i].find('\0') != std::string::npos || 263 dns_names[i].find('\0') != std::string::npos ||
275 dns_names[i].length() == host_name.length() || 264 dns_names[i].length() == host_name.length() ||
276 !IsHostNameKnownTLD(dns_names[i])) { 265 !IsHostNameKnownTLD(dns_names[i])) {
277 continue; 266 continue;
278 } else if (dns_names[i].length() > host_name.length()) { 267 } else if (dns_names[i].length() > host_name.length()) {
279 if (url_formatter::StripWWW(base::ASCIIToUTF16(dns_names[i])) == 268 if (url_formatter::StripWWW(base::ASCIIToUTF16(dns_names[i])) ==
280 base::ASCIIToUTF16(host_name)) { 269 base::ASCIIToUTF16(host_name)) {
281 *www_match_host_name = dns_names[i]; 270 *www_match_host_name = dns_names[i];
282 return true; 271 return true;
283 } 272 }
284 } else { 273 } else {
285 if (url_formatter::StripWWW(base::ASCIIToUTF16(host_name)) == 274 if (url_formatter::StripWWW(base::ASCIIToUTF16(host_name)) ==
286 base::ASCIIToUTF16(dns_names[i])) { 275 base::ASCIIToUTF16(dns_names[i])) {
287 *www_match_host_name = dns_names[i]; 276 *www_match_host_name = dns_names[i];
288 return true; 277 return true;
289 } 278 }
290 } 279 }
291 } 280 }
292 } 281 }
293 return false; 282 return false;
294 } 283 }
295 284
296 bool SSLErrorClassification::IsWWWSubDomainMatch() const { 285 bool NameUnderAnyNames(const HostnameTokens& child,
297 const std::string& host_name = request_url_.host(); 286 const std::vector<HostnameTokens>& potential_parents) {
298 std::vector<std::string> dns_names;
299 cert_.GetDNSNames(&dns_names);
300 std::string www_host;
301 return GetWWWSubDomainMatch(host_name, dns_names, &www_host);
302 }
303
304 bool SSLErrorClassification::NameUnderAnyNames(
305 const Tokens& child,
306 const std::vector<Tokens>& potential_parents) const {
307 bool result = false; 287 bool result = false;
308 // Need to account for all the possible domains given in the SSL certificate. 288 // Need to account for all the possible domains given in the SSL certificate.
309 for (size_t i = 0; i < potential_parents.size(); ++i) { 289 for (size_t i = 0; i < potential_parents.size(); ++i) {
310 if (potential_parents[i].empty() || 290 if (potential_parents[i].empty() ||
311 potential_parents[i].size() >= child.size()) { 291 potential_parents[i].size() >= child.size()) {
312 result = result || false; 292 result = result || false;
313 } else { 293 } else {
314 size_t domain_diff = FindSubDomainDifference(child, 294 size_t domain_diff = FindSubDomainDifference(child, potential_parents[i]);
315 potential_parents[i]); 295 if (domain_diff == 1 && child[0] != "www")
316 if (domain_diff == 1 && child[0] != "www")
317 result = result || true; 296 result = result || true;
318 } 297 }
319 } 298 }
320 return result; 299 return result;
321 } 300 }
322 301
323 bool SSLErrorClassification::AnyNamesUnderName( 302 bool AnyNamesUnderName(const std::vector<HostnameTokens>& potential_children,
324 const std::vector<Tokens>& potential_children, 303 const HostnameTokens& parent) {
325 const Tokens& parent) const {
326 bool result = false; 304 bool result = false;
327 // Need to account for all the possible domains given in the SSL certificate. 305 // Need to account for all the possible domains given in the SSL certificate.
328 for (size_t i = 0; i < potential_children.size(); ++i) { 306 for (size_t i = 0; i < potential_children.size(); ++i) {
329 if (potential_children[i].empty() || 307 if (potential_children[i].empty() ||
330 potential_children[i].size() <= parent.size()) { 308 potential_children[i].size() <= parent.size()) {
331 result = result || false; 309 result = result || false;
332 } else { 310 } else {
333 size_t domain_diff = FindSubDomainDifference(potential_children[i], 311 size_t domain_diff =
334 parent); 312 FindSubDomainDifference(potential_children[i], parent);
335 if (domain_diff == 1 && potential_children[i][0] != "www") 313 if (domain_diff == 1 && potential_children[i][0] != "www")
336 result = result || true; 314 result = result || true;
337 } 315 }
338 } 316 }
339 return result; 317 return result;
340 } 318 }
341 319
342 bool SSLErrorClassification::IsSubDomainOutsideWildcard( 320 bool IsSubDomainOutsideWildcard(const GURL& request_url,
343 const Tokens& host_name_tokens) const { 321 const net::X509Certificate& cert) {
344 std::string host_name = request_url_.host(); 322 std::string host_name = request_url.host();
323 HostnameTokens host_name_tokens = Tokenize(host_name);
345 std::vector<std::string> dns_names; 324 std::vector<std::string> dns_names;
346 cert_.GetDNSNames(&dns_names); 325 cert.GetDNSNames(&dns_names);
347 bool result = false; 326 bool result = false;
348 327
349 // This method requires that the host name be longer than the dns name on 328 // This method requires that the host name be longer than the dns name on
350 // the certificate. 329 // the certificate.
351 for (size_t i = 0; i < dns_names.size(); ++i) { 330 for (size_t i = 0; i < dns_names.size(); ++i) {
352 const std::string& name = dns_names[i]; 331 const std::string& name = dns_names[i];
353 if (name.length() < 2 || name.length() >= host_name.length() || 332 if (name.length() < 2 || name.length() >= host_name.length() ||
354 name.find('\0') != std::string::npos || 333 name.find('\0') != std::string::npos || !IsHostNameKnownTLD(name) ||
355 !IsHostNameKnownTLD(name) 334 name[0] != '*' || name[1] != '.') {
356 || name[0] != '*' || name[1] != '.') {
357 continue; 335 continue;
358 } 336 }
359 337
360 // Move past the "*.". 338 // Move past the "*.".
361 std::string extracted_dns_name = name.substr(2); 339 std::string extracted_dns_name = name.substr(2);
362 if (FindSubDomainDifference( 340 if (FindSubDomainDifference(host_name_tokens,
363 host_name_tokens, Tokenize(extracted_dns_name)) == 2) { 341 Tokenize(extracted_dns_name)) == 2) {
364 return true; 342 return true;
365 } 343 }
366 } 344 }
367 return result; 345 return result;
368 } 346 }
369 347
370 bool SSLErrorClassification::IsCertLikelyFromMultiTenantHosting() const { 348 bool IsCertLikelyFromMultiTenantHosting(const GURL& request_url,
371 std::string host_name = request_url_.host(); 349 const net::X509Certificate& cert) {
350 std::string host_name = request_url.host();
372 std::vector<std::string> dns_names; 351 std::vector<std::string> dns_names;
373 std::vector<std::string> dns_names_domain; 352 std::vector<std::string> dns_names_domain;
374 cert_.GetDNSNames(&dns_names); 353 cert.GetDNSNames(&dns_names);
375 size_t dns_names_size = dns_names.size(); 354 size_t dns_names_size = dns_names.size();
376 355
377 // If there is only 1 DNS name then it is definitely not a shared certificate. 356 // If there is only 1 DNS name then it is definitely not a shared certificate.
378 if (dns_names_size == 0 || dns_names_size == 1) 357 if (dns_names_size == 0 || dns_names_size == 1)
379 return false; 358 return false;
380 359
381 // Check to see if all the domains in the SAN field in the SSL certificate are 360 // Check to see if all the domains in the SAN field in the SSL certificate are
382 // the same or not. 361 // the same or not.
383 for (size_t i = 0; i < dns_names_size; ++i) { 362 for (size_t i = 0; i < dns_names_size; ++i) {
384 dns_names_domain.push_back( 363 dns_names_domain.push_back(
385 net::registry_controlled_domains:: 364 net::registry_controlled_domains::GetDomainAndRegistry(
386 GetDomainAndRegistry(
387 dns_names[i], 365 dns_names[i],
388 net::registry_controlled_domains::INCLUDE_PRIVATE_REGISTRIES)); 366 net::registry_controlled_domains::INCLUDE_PRIVATE_REGISTRIES));
389 } 367 }
390 for (size_t i = 1; i < dns_names_domain.size(); ++i) { 368 for (size_t i = 1; i < dns_names_domain.size(); ++i) {
391 if (dns_names_domain[i] != dns_names_domain[0]) 369 if (dns_names_domain[i] != dns_names_domain[0])
392 return false; 370 return false;
393 } 371 }
394 372
395 // If the number of DNS names is more than 5 then assume that it is a shared 373 // If the number of DNS names is more than 5 then assume that it is a shared
396 // certificate. 374 // certificate.
397 static const int kDistinctNameThreshold = 5; 375 static const int kDistinctNameThreshold = 5;
398 if (dns_names_size > kDistinctNameThreshold) 376 if (dns_names_size > kDistinctNameThreshold)
399 return true; 377 return true;
400 378
401 // Heuristic - The edit distance between all the strings should be at least 5 379 // Heuristic - The edit distance between all the strings should be at least 5
402 // for it to be counted as a shared SSLCertificate. If even one pair of 380 // for it to be counted as a shared SSLCertificate. If even one pair of
403 // strings edit distance is below 5 then the certificate is no longer 381 // strings edit distance is below 5 then the certificate is no longer
404 // considered as a shared certificate. Include the host name in the URL also 382 // considered as a shared certificate. Include the host name in the URL also
405 // while comparing. 383 // while comparing.
406 dns_names.push_back(host_name); 384 dns_names.push_back(host_name);
407 static const int kMinimumEditDsitance = 5; 385 static const size_t kMinimumEditDsitance = 5;
408 for (size_t i = 0; i < dns_names_size; ++i) { 386 for (size_t i = 0; i < dns_names_size; ++i) {
409 for (size_t j = i + 1; j < dns_names_size; ++j) { 387 for (size_t j = i + 1; j < dns_names_size; ++j) {
410 int edit_distance = GetLevensteinDistance(dns_names[i], dns_names[j]); 388 size_t edit_distance = GetLevensteinDistance(dns_names[i], dns_names[j]);
411 if (edit_distance < kMinimumEditDsitance) 389 if (edit_distance < kMinimumEditDsitance)
412 return false; 390 return false;
413 } 391 }
414 } 392 }
415 return true; 393 return true;
416 } 394 }
417 395
418 bool SSLErrorClassification::IsCertLikelyFromSameDomain() const { 396 bool IsCertLikelyFromSameDomain(const GURL& request_url,
419 std::string host_name = request_url_.host(); 397 const net::X509Certificate& cert) {
398 std::string host_name = request_url.host();
420 std::vector<std::string> dns_names; 399 std::vector<std::string> dns_names;
421 cert_.GetDNSNames(&dns_names); 400 cert.GetDNSNames(&dns_names);
422 401
423 dns_names.push_back(host_name); 402 dns_names.push_back(host_name);
424 std::vector<std::string> dns_names_domain; 403 std::vector<std::string> dns_names_domain;
425 404
426 for (const std::string& dns_name : dns_names) { 405 for (const std::string& dns_name : dns_names) {
427 dns_names_domain.push_back( 406 dns_names_domain.push_back(
428 net::registry_controlled_domains::GetDomainAndRegistry( 407 net::registry_controlled_domains::GetDomainAndRegistry(
429 dns_name, 408 dns_name,
430 net::registry_controlled_domains::INCLUDE_PRIVATE_REGISTRIES)); 409 net::registry_controlled_domains::INCLUDE_PRIVATE_REGISTRIES));
431 } 410 }
432 411
433 DCHECK(!dns_names_domain.empty()); 412 DCHECK(!dns_names_domain.empty());
434 const std::string& host_name_domain = dns_names_domain.back(); 413 const std::string& host_name_domain = dns_names_domain.back();
435 414
436 // Last element is the original domain. So, excluding it. 415 // Last element is the original domain. So, excluding it.
437 return std::find(dns_names_domain.begin(), dns_names_domain.end() - 1, 416 return std::find(dns_names_domain.begin(), dns_names_domain.end() - 1,
438 host_name_domain) != dns_names_domain.end() - 1; 417 host_name_domain) != dns_names_domain.end() - 1;
439 } 418 }
440 419
441 // static 420 bool IsHostnameNonUniqueOrDotless(const std::string& hostname) {
442 bool SSLErrorClassification::IsHostnameNonUniqueOrDotless(
443 const std::string& hostname) {
444 return net::IsHostnameNonUnique(hostname) || 421 return net::IsHostnameNonUnique(hostname) ||
445 hostname.find('.') == std::string::npos; 422 hostname.find('.') == std::string::npos;
446 } 423 }
424
425 } // namespace ssl_errors
OLDNEW
« no previous file with comments | « components/ssl_errors/error_classification.h ('k') | components/ssl_errors/error_classification_unittest.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698