Update To 11.40.268.0
[platform/framework/web/crosswalk.git] / src / chrome / browser / ssl / ssl_error_classification.cc
1 // Copyright 2014 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include <vector>
6
7 #include "chrome/browser/ssl/ssl_error_classification.h"
8
9 #include "base/build_time.h"
10 #include "base/metrics/field_trial.h"
11 #include "base/metrics/histogram.h"
12 #include "base/strings/string_split.h"
13 #include "base/strings/utf_string_conversions.h"
14 #include "base/time/time.h"
15 #include "chrome/browser/browser_process.h"
16 #include "chrome/browser/chrome_notification_types.h"
17 #include "chrome/browser/profiles/profile.h"
18 #include "chrome/browser/ssl/ssl_error_info.h"
19 #include "content/public/browser/notification_service.h"
20 #include "content/public/browser/web_contents.h"
21 #include "net/base/net_util.h"
22 #include "net/base/registry_controlled_domains/registry_controlled_domain.h"
23 #include "net/cert/x509_cert_types.h"
24 #include "net/cert/x509_certificate.h"
25 #include "url/gurl.h"
26
27 #if defined(ENABLE_CAPTIVE_PORTAL_DETECTION)
28 #include "chrome/browser/captive_portal/captive_portal_service.h"
29 #include "chrome/browser/captive_portal/captive_portal_service_factory.h"
30 #endif
31
32 #if defined(OS_WIN)
33 #include "base/win/win_util.h"
34 #include "base/win/windows_version.h"
35 #endif
36
37 using base::Time;
38 using base::TimeTicks;
39 using base::TimeDelta;
40
41 namespace {
42
43 // Events for UMA. Do not reorder or change!
44 enum SSLInterstitialCause {
45   CLOCK_PAST,
46   CLOCK_FUTURE,
47   WWW_SUBDOMAIN_MATCH,
48   SUBDOMAIN_MATCH,
49   SUBDOMAIN_INVERSE_MATCH,
50   SUBDOMAIN_OUTSIDE_WILDCARD,
51   HOST_NAME_NOT_KNOWN_TLD,
52   LIKELY_MULTI_TENANT_HOSTING,
53   LOCALHOST,
54   PRIVATE_URL,
55   AUTHORITY_ERROR_CAPTIVE_PORTAL,
56   UNUSED_INTERSTITIAL_CAUSE_ENTRY,
57 };
58
59 // Events for UMA. Do not reorder or change!
60 enum SSLInterstitialCauseCaptivePortal {
61   CAPTIVE_PORTAL_DETECTION_ENABLED,
62   CAPTIVE_PORTAL_DETECTION_ENABLED_OVERRIDABLE,
63   CAPTIVE_PORTAL_PROBE_COMPLETED,
64   CAPTIVE_PORTAL_PROBE_COMPLETED_OVERRIDABLE,
65   CAPTIVE_PORTAL_NO_RESPONSE,
66   CAPTIVE_PORTAL_NO_RESPONSE_OVERRIDABLE,
67   CAPTIVE_PORTAL_DETECTED,
68   CAPTIVE_PORTAL_DETECTED_OVERRIDABLE,
69   UNUSED_CAPTIVE_PORTAL_EVENT,
70 };
71
72 void RecordSSLInterstitialSeverityScore(float ssl_severity_score,
73                                         int cert_error) {
74   if (SSLErrorInfo::NetErrorToErrorType(cert_error) ==
75       SSLErrorInfo::CERT_DATE_INVALID) {
76     UMA_HISTOGRAM_COUNTS_100("interstitial.ssl.severity_score.date_invalid",
77                              static_cast<int>(ssl_severity_score * 100));
78   } else if (SSLErrorInfo::NetErrorToErrorType(cert_error) ==
79       SSLErrorInfo::CERT_COMMON_NAME_INVALID) {
80     UMA_HISTOGRAM_COUNTS_100(
81         "interstitial.ssl.severity_score.common_name_invalid",
82         static_cast<int>(ssl_severity_score * 100));
83   } else if (SSLErrorInfo::NetErrorToErrorType(cert_error) ==
84              SSLErrorInfo::CERT_AUTHORITY_INVALID) {
85     UMA_HISTOGRAM_COUNTS_100(
86         "interstitial.ssl.severity_score.authority_invalid",
87         static_cast<int>(ssl_severity_score * 100));
88   }
89 }
90
91 // Scores/weights which will be constant through all the SSL error types.
92 static const float kServerWeight = 0.5f;
93 static const float kClientWeight = 0.5f;
94
95 void RecordSSLInterstitialCause(bool overridable, SSLInterstitialCause event) {
96   if (overridable) {
97     UMA_HISTOGRAM_ENUMERATION("interstitial.ssl.cause.overridable", event,
98                               UNUSED_INTERSTITIAL_CAUSE_ENTRY);
99   } else {
100     UMA_HISTOGRAM_ENUMERATION("interstitial.ssl.cause.nonoverridable", event,
101                               UNUSED_INTERSTITIAL_CAUSE_ENTRY);
102   }
103 }
104
105 #if defined(ENABLE_CAPTIVE_PORTAL_DETECTION)
106 void RecordCaptivePortalEventStats(SSLInterstitialCauseCaptivePortal event) {
107   UMA_HISTOGRAM_ENUMERATION("interstitial.ssl.captive_portal",
108                             event,
109                             UNUSED_CAPTIVE_PORTAL_EVENT);
110 }
111 #endif
112
113 int GetLevensteinDistance(const std::string& str1,
114                           const std::string& str2) {
115   if (str1 == str2)
116     return 0;
117   if (str1.size() == 0)
118     return str2.size();
119   if (str2.size() == 0)
120     return str1.size();
121   std::vector<int> kFirstRow(str2.size() + 1, 0);
122   std::vector<int> kSecondRow(str2.size() + 1, 0);
123
124   for (size_t i = 0; i < kFirstRow.size(); ++i)
125     kFirstRow[i] = i;
126   for (size_t i = 0; i < str1.size(); ++i) {
127     kSecondRow[0] = i + 1;
128     for (size_t j = 0; j < str2.size(); ++j) {
129       int cost = str1[i] == str2[j] ? 0 : 1;
130       kSecondRow[j+1] = std::min(std::min(
131           kSecondRow[j] + 1, kFirstRow[j + 1] + 1), kFirstRow[j] + cost);
132     }
133     for (size_t j = 0; j < kFirstRow.size(); j++)
134       kFirstRow[j] = kSecondRow[j];
135   }
136   return kSecondRow[str2.size()];
137 }
138
139 } // namespace
140
141 SSLErrorClassification::SSLErrorClassification(
142     content::WebContents* web_contents,
143     const base::Time& current_time,
144     const GURL& url,
145     int cert_error,
146     const net::X509Certificate& cert)
147   : web_contents_(web_contents),
148     current_time_(current_time),
149     request_url_(url),
150     cert_error_(cert_error),
151     cert_(cert),
152     captive_portal_detection_enabled_(false),
153     captive_portal_probe_completed_(false),
154     captive_portal_no_response_(false),
155     captive_portal_detected_(false) {
156 #if defined(ENABLE_CAPTIVE_PORTAL_DETECTION)
157   Profile* profile = Profile::FromBrowserContext(
158       web_contents_->GetBrowserContext());
159   CaptivePortalService* captive_portal_service =
160       CaptivePortalServiceFactory::GetForProfile(profile);
161   captive_portal_detection_enabled_ = captive_portal_service->enabled();
162   captive_portal_service->DetectCaptivePortal();
163   registrar_.Add(this,
164                  chrome::NOTIFICATION_CAPTIVE_PORTAL_CHECK_RESULT,
165                  content::Source<Profile>(profile));
166 #endif
167 }
168
169 SSLErrorClassification::~SSLErrorClassification() { }
170
171 void SSLErrorClassification::RecordCaptivePortalUMAStatistics(
172     bool overridable) const {
173 #if defined(ENABLE_CAPTIVE_PORTAL_DETECTION)
174   if (captive_portal_detection_enabled_)
175     RecordCaptivePortalEventStats(
176         overridable ?
177         CAPTIVE_PORTAL_DETECTION_ENABLED_OVERRIDABLE :
178         CAPTIVE_PORTAL_DETECTION_ENABLED);
179   if (captive_portal_probe_completed_)
180     RecordCaptivePortalEventStats(
181         overridable ?
182         CAPTIVE_PORTAL_PROBE_COMPLETED_OVERRIDABLE :
183         CAPTIVE_PORTAL_PROBE_COMPLETED);
184   // Log only one of portal detected and no response results.
185   if (captive_portal_detected_)
186     RecordCaptivePortalEventStats(
187         overridable ?
188         CAPTIVE_PORTAL_DETECTED_OVERRIDABLE :
189         CAPTIVE_PORTAL_DETECTED);
190   else if (captive_portal_no_response_)
191     RecordCaptivePortalEventStats(
192         overridable ?
193         CAPTIVE_PORTAL_NO_RESPONSE_OVERRIDABLE :
194         CAPTIVE_PORTAL_NO_RESPONSE);
195 #endif
196 }
197
198 void SSLErrorClassification::InvalidDateSeverityScore() {
199   SSLErrorInfo::ErrorType type =
200       SSLErrorInfo::NetErrorToErrorType(cert_error_);
201   DCHECK_EQ(type, SSLErrorInfo::CERT_DATE_INVALID);
202
203   // Client-side characteristics. Check whether or not the system's clock is
204   // wrong and whether or not the user has encountered this error before.
205   float severity_date_score = 0.0f;
206
207   static const float kCertificateExpiredWeight = 0.3f;
208   static const float kNotYetValidWeight = 0.2f;
209
210   static const float kSystemClockWeight = 0.75f;
211   static const float kSystemClockWrongWeight = 0.1f;
212   static const float kSystemClockRightWeight = 1.0f;
213
214   if (IsUserClockInThePast(current_time_)  ||
215       IsUserClockInTheFuture(current_time_)) {
216     severity_date_score += kClientWeight * kSystemClockWeight *
217         kSystemClockWrongWeight;
218   } else {
219     severity_date_score += kClientWeight * kSystemClockWeight *
220         kSystemClockRightWeight;
221   }
222   // TODO(felt): (crbug.com/393262) Check website settings.
223
224   // Server-side characteristics. Check whether the certificate has expired or
225   // is not yet valid. If the certificate has expired then factor the time which
226   // has passed since expiry.
227   if (cert_.HasExpired()) {
228     severity_date_score += kServerWeight * kCertificateExpiredWeight *
229         CalculateScoreTimePassedSinceExpiry();
230   }
231   if (current_time_ < cert_.valid_start())
232     severity_date_score += kServerWeight * kNotYetValidWeight;
233
234   RecordSSLInterstitialSeverityScore(severity_date_score, cert_error_);
235 }
236
237 void SSLErrorClassification::InvalidCommonNameSeverityScore() {
238   SSLErrorInfo::ErrorType type =
239       SSLErrorInfo::NetErrorToErrorType(cert_error_);
240   DCHECK_EQ(type, SSLErrorInfo::CERT_COMMON_NAME_INVALID);
241   float severity_name_score = 0.0f;
242
243   static const float kWWWDifferenceWeight = 0.3f;
244   static const float kNameUnderAnyNamesWeight = 0.2f;
245   static const float kAnyNamesUnderNameWeight = 1.0f;
246   static const float kLikelyMultiTenantHostingWeight = 0.1f;
247
248   std::string host_name = request_url_.host();
249   if (IsHostNameKnownTLD(host_name)) {
250     Tokens host_name_tokens = Tokenize(host_name);
251     if (IsWWWSubDomainMatch())
252       severity_name_score += kServerWeight * kWWWDifferenceWeight;
253     if (IsSubDomainOutsideWildcard(host_name_tokens))
254       severity_name_score += kServerWeight * kWWWDifferenceWeight;
255
256     std::vector<std::string> dns_names;
257     cert_.GetDNSNames(&dns_names);
258     std::vector<Tokens> dns_name_tokens = GetTokenizedDNSNames(dns_names);
259     if (NameUnderAnyNames(host_name_tokens, dns_name_tokens))
260       severity_name_score += kServerWeight * kNameUnderAnyNamesWeight;
261     // Inverse case is more likely to be a MITM attack.
262     if (AnyNamesUnderName(dns_name_tokens, host_name_tokens))
263       severity_name_score += kServerWeight * kAnyNamesUnderNameWeight;
264     if (IsCertLikelyFromMultiTenantHosting())
265       severity_name_score += kServerWeight * kLikelyMultiTenantHostingWeight;
266   }
267
268   static const float kEnvironmentWeight = 0.25f;
269
270   severity_name_score += kClientWeight * kEnvironmentWeight *
271       CalculateScoreEnvironments();
272
273   RecordSSLInterstitialSeverityScore(severity_name_score, cert_error_);
274 }
275
276 void SSLErrorClassification::InvalidAuthoritySeverityScore() {
277   SSLErrorInfo::ErrorType type = SSLErrorInfo::NetErrorToErrorType(cert_error_);
278   DCHECK_EQ(type, SSLErrorInfo::CERT_AUTHORITY_INVALID);
279
280   // For |CERT_AUTHORITY_INVALID| errors if captive portals have been detected
281   // then don't calculate the score, just return.
282   if (captive_portal_probe_completed_ && captive_portal_detected_)
283     return;
284
285   float severity_authority_score = 0.0f;
286
287   static const float kLocalhostWeight = 0.7f;
288   static const float kPrivateURLWeight = 0.3f;
289   if (net::IsLocalhost(request_url_.HostNoBrackets()))
290     severity_authority_score += kClientWeight * kLocalhostWeight;
291   if (IsHostnameNonUniqueOrDotless(request_url_.HostNoBrackets()))
292     severity_authority_score += kClientWeight * kPrivateURLWeight;
293
294   RecordSSLInterstitialSeverityScore(severity_authority_score, cert_error_);
295 }
296
297 void SSLErrorClassification::RecordUMAStatistics(
298     bool overridable) const {
299   SSLErrorInfo::ErrorType type =
300       SSLErrorInfo::NetErrorToErrorType(cert_error_);
301   switch (type) {
302     case SSLErrorInfo::CERT_DATE_INVALID: {
303       if (IsUserClockInThePast(base::Time::NowFromSystemTime()))
304         RecordSSLInterstitialCause(overridable, CLOCK_PAST);
305       if (IsUserClockInTheFuture(base::Time::NowFromSystemTime()))
306         RecordSSLInterstitialCause(overridable, CLOCK_FUTURE);
307       break;
308     }
309     case SSLErrorInfo::CERT_COMMON_NAME_INVALID: {
310       std::string host_name = request_url_.host();
311       if (IsHostNameKnownTLD(host_name)) {
312         Tokens host_name_tokens = Tokenize(host_name);
313         if (IsWWWSubDomainMatch())
314           RecordSSLInterstitialCause(overridable, WWW_SUBDOMAIN_MATCH);
315         if (IsSubDomainOutsideWildcard(host_name_tokens))
316           RecordSSLInterstitialCause(overridable, SUBDOMAIN_OUTSIDE_WILDCARD);
317         std::vector<std::string> dns_names;
318         cert_.GetDNSNames(&dns_names);
319         std::vector<Tokens> dns_name_tokens = GetTokenizedDNSNames(dns_names);
320         if (NameUnderAnyNames(host_name_tokens, dns_name_tokens))
321           RecordSSLInterstitialCause(overridable, SUBDOMAIN_MATCH);
322         if (AnyNamesUnderName(dns_name_tokens, host_name_tokens))
323           RecordSSLInterstitialCause(overridable, SUBDOMAIN_INVERSE_MATCH);
324         if (IsCertLikelyFromMultiTenantHosting())
325           RecordSSLInterstitialCause(overridable, LIKELY_MULTI_TENANT_HOSTING);
326       } else {
327          RecordSSLInterstitialCause(overridable, HOST_NAME_NOT_KNOWN_TLD);
328       }
329       break;
330     }
331     case SSLErrorInfo::CERT_AUTHORITY_INVALID: {
332       const std::string& hostname = request_url_.HostNoBrackets();
333       if (net::IsLocalhost(hostname))
334         RecordSSLInterstitialCause(overridable, LOCALHOST);
335       if (IsHostnameNonUniqueOrDotless(hostname))
336         RecordSSLInterstitialCause(overridable, PRIVATE_URL);
337       if (captive_portal_probe_completed_ && captive_portal_detected_)
338         RecordSSLInterstitialCause(overridable, AUTHORITY_ERROR_CAPTIVE_PORTAL);
339       break;
340     }
341     default:
342       break;
343   }
344 }
345
346 base::TimeDelta SSLErrorClassification::TimePassedSinceExpiry() const {
347   base::TimeDelta delta = current_time_ - cert_.valid_expiry();
348   return delta;
349 }
350
351 float SSLErrorClassification::CalculateScoreTimePassedSinceExpiry() const {
352   base::TimeDelta delta = TimePassedSinceExpiry();
353   int64 time_passed = delta.InDays();
354   const int64 kHighThreshold = 7;
355   const int64 kLowThreshold = 4;
356   static const float kHighThresholdWeight = 0.4f;
357   static const float kMediumThresholdWeight = 0.3f;
358   static const float kLowThresholdWeight = 0.2f;
359   if (time_passed >= kHighThreshold)
360     return kHighThresholdWeight;
361   else if (time_passed >= kLowThreshold)
362     return kMediumThresholdWeight;
363   else
364     return kLowThresholdWeight;
365 }
366
367 float SSLErrorClassification::CalculateScoreEnvironments() const {
368   static const float kWifiWeight = 0.7f;
369   static const float kCellularWeight = 0.7f;
370   static const float kEthernetWeight = 0.7f;
371   static const float kOtherWeight = 0.7f;
372   net::NetworkChangeNotifier::ConnectionType type =
373       net::NetworkChangeNotifier::GetConnectionType();
374   if (type == net::NetworkChangeNotifier::CONNECTION_WIFI)
375     return kWifiWeight;
376   if (type == net::NetworkChangeNotifier::CONNECTION_2G ||
377       type == net::NetworkChangeNotifier::CONNECTION_3G ||
378       type == net::NetworkChangeNotifier::CONNECTION_4G ) {
379     return kCellularWeight;
380   }
381   if (type == net::NetworkChangeNotifier::CONNECTION_ETHERNET)
382     return kEthernetWeight;
383 #if defined(ENABLE_CAPTIVE_PORTAL_DETECTION)
384   // Assume if captive portals are detected then the user is connected using a
385   // hot spot.
386   static const float kHotspotWeight = 0.2f;
387   if (captive_portal_probe_completed_ && captive_portal_detected_)
388       return kHotspotWeight;
389 #endif
390   return kOtherWeight;
391 }
392
393 bool SSLErrorClassification::IsUserClockInThePast(const base::Time& time_now) {
394 #if defined(DONT_EMBED_BUILD_METADATA) && !defined(OFFICIAL_BUILD)
395   return false;
396 #else
397   base::Time build_time = base::GetBuildTime();
398   if (time_now < build_time - base::TimeDelta::FromDays(2))
399     return true;
400   return false;
401 #endif
402 }
403
404 bool SSLErrorClassification::IsUserClockInTheFuture(
405     const base::Time& time_now) {
406 #if defined(DONT_EMBED_BUILD_METADATA) && !defined(OFFICIAL_BUILD)
407   return false;
408 #else
409   base::Time build_time = base::GetBuildTime();
410   if (time_now > build_time + base::TimeDelta::FromDays(365))
411     return true;
412   return false;
413 #endif
414 }
415
416 bool SSLErrorClassification::MaybeWindowsLacksSHA256Support() {
417 #if defined(OS_WIN)
418   return !base::win::MaybeHasSHA256Support();
419 #else
420   return false;
421 #endif
422 }
423
424 bool SSLErrorClassification::IsHostNameKnownTLD(const std::string& host_name) {
425   size_t tld_length =
426       net::registry_controlled_domains::GetRegistryLength(
427           host_name,
428           net::registry_controlled_domains::EXCLUDE_UNKNOWN_REGISTRIES,
429           net::registry_controlled_domains::INCLUDE_PRIVATE_REGISTRIES);
430   if (tld_length == 0 || tld_length == std::string::npos)
431     return false;
432   return true;
433 }
434
435 std::vector<SSLErrorClassification::Tokens> SSLErrorClassification::
436 GetTokenizedDNSNames(const std::vector<std::string>& dns_names) {
437   std::vector<std::vector<std::string>> dns_name_tokens;
438   for (size_t i = 0; i < dns_names.size(); ++i) {
439     std::vector<std::string> dns_name_token_single;
440     if (dns_names[i].empty() || dns_names[i].find('\0') != std::string::npos
441         || !(IsHostNameKnownTLD(dns_names[i]))) {
442       dns_name_token_single.push_back(std::string());
443     } else {
444       dns_name_token_single = Tokenize(dns_names[i]);
445     }
446     dns_name_tokens.push_back(dns_name_token_single);
447   }
448   return dns_name_tokens;
449 }
450
451 size_t SSLErrorClassification::FindSubDomainDifference(
452     const Tokens& potential_subdomain, const Tokens& parent) const {
453   // A check to ensure that the number of tokens in the tokenized_parent is
454   // less than the tokenized_potential_subdomain.
455   if (parent.size() >= potential_subdomain.size())
456     return 0;
457
458   size_t tokens_match = 0;
459   size_t diff_size = potential_subdomain.size() - parent.size();
460   for (size_t i = 0; i < parent.size(); ++i) {
461     if (parent[i] == potential_subdomain[i + diff_size])
462       tokens_match++;
463   }
464   if (tokens_match == parent.size())
465     return diff_size;
466   return 0;
467 }
468
469 SSLErrorClassification::Tokens SSLErrorClassification::
470 Tokenize(const std::string& name) {
471   Tokens name_tokens;
472   base::SplitStringDontTrim(name, '.', &name_tokens);
473   return name_tokens;
474 }
475
476 // We accept the inverse case for www for historical reasons.
477 bool SSLErrorClassification::IsWWWSubDomainMatch() const {
478   std::string host_name = request_url_.host();
479   if (IsHostNameKnownTLD(host_name)) {
480     std::vector<std::string> dns_names;
481     cert_.GetDNSNames(&dns_names);
482     bool result = false;
483     // Need to account for all possible domains given in the SSL certificate.
484     for (size_t i = 0; i < dns_names.size(); ++i) {
485       if (dns_names[i].empty() || dns_names[i].find('\0') != std::string::npos
486           || dns_names[i].length() == host_name.length()
487           || !(IsHostNameKnownTLD(dns_names[i]))) {
488         result = result || false;
489       } else if (dns_names[i].length() > host_name.length()) {
490         result = result ||
491             net::StripWWW(base::ASCIIToUTF16(dns_names[i])) ==
492             base::ASCIIToUTF16(host_name);
493       } else {
494         result = result ||
495             net::StripWWW(base::ASCIIToUTF16(host_name)) ==
496             base::ASCIIToUTF16(dns_names[i]);
497       }
498     }
499     return result;
500   }
501   return false;
502 }
503
504 bool SSLErrorClassification::NameUnderAnyNames(
505     const Tokens& child,
506     const std::vector<Tokens>& potential_parents) const {
507   bool result = false;
508   // Need to account for all the possible domains given in the SSL certificate.
509   for (size_t i = 0; i < potential_parents.size(); ++i) {
510     if (potential_parents[i].empty() ||
511         potential_parents[i].size() >= child.size()) {
512       result = result || false;
513     } else {
514       size_t domain_diff = FindSubDomainDifference(child,
515                                                    potential_parents[i]);
516       if (domain_diff == 1 &&  child[0] != "www")
517         result = result || true;
518     }
519   }
520   return result;
521 }
522
523 bool SSLErrorClassification::AnyNamesUnderName(
524     const std::vector<Tokens>& potential_children,
525     const Tokens& parent) const {
526   bool result = false;
527   // Need to account for all the possible domains given in the SSL certificate.
528   for (size_t i = 0; i < potential_children.size(); ++i) {
529     if (potential_children[i].empty() ||
530         potential_children[i].size() <= parent.size()) {
531       result = result || false;
532     } else {
533       size_t domain_diff = FindSubDomainDifference(potential_children[i],
534                                                    parent);
535       if (domain_diff == 1 &&  potential_children[i][0] != "www")
536         result = result || true;
537     }
538   }
539   return result;
540 }
541
542 bool SSLErrorClassification::IsSubDomainOutsideWildcard(
543     const Tokens& host_name_tokens) const {
544   std::string host_name = request_url_.host();
545   std::vector<std::string> dns_names;
546   cert_.GetDNSNames(&dns_names);
547   bool result = false;
548
549   // This method requires that the host name be longer than the dns name on
550   // the certificate.
551   for (size_t i = 0; i < dns_names.size(); ++i) {
552     const std::string& name = dns_names[i];
553     if (name.length() < 2 || name.length() >= host_name.length() ||
554         name.find('\0') != std::string::npos ||
555         !IsHostNameKnownTLD(name)
556         || name[0] != '*' || name[1] != '.') {
557       continue;
558     }
559
560     // Move past the "*.".
561     std::string extracted_dns_name = name.substr(2);
562     if (FindSubDomainDifference(
563         host_name_tokens, Tokenize(extracted_dns_name)) == 2) {
564       return true;
565     }
566   }
567   return result;
568 }
569
570 bool SSLErrorClassification::IsCertLikelyFromMultiTenantHosting() const {
571   std::string host_name = request_url_.host();
572   std::vector<std::string> dns_names;
573   std::vector<std::string> dns_names_domain;
574   cert_.GetDNSNames(&dns_names);
575   size_t dns_names_size = dns_names.size();
576
577   // If there is only 1 DNS name then it is definitely not a shared certificate.
578   if (dns_names_size == 0 || dns_names_size == 1)
579     return false;
580
581   // Check to see if all the domains in the SAN field in the SSL certificate are
582   // the same or not.
583   for (size_t i = 0; i < dns_names_size; ++i) {
584     dns_names_domain.push_back(
585         net::registry_controlled_domains::
586         GetDomainAndRegistry(
587             dns_names[i],
588             net::registry_controlled_domains::INCLUDE_PRIVATE_REGISTRIES));
589   }
590   for (size_t i = 1; i < dns_names_domain.size(); ++i) {
591     if (dns_names_domain[i] != dns_names_domain[0])
592       return false;
593   }
594
595   // If the number of DNS names is more than 5 then assume that it is a shared
596   // certificate.
597   static const int kDistinctNameThreshold = 5;
598   if (dns_names_size > kDistinctNameThreshold)
599     return true;
600
601   // Heuristic - The edit distance between all the strings should be at least 5
602   // for it to be counted as a shared SSLCertificate. If even one pair of
603   // strings edit distance is below 5 then the certificate is no longer
604   // considered as a shared certificate. Include the host name in the URL also
605   // while comparing.
606   dns_names.push_back(host_name);
607   static const int kMinimumEditDsitance = 5;
608   for (size_t i = 0; i < dns_names_size; ++i) {
609     for (size_t j = i + 1; j < dns_names_size; ++j) {
610       int edit_distance = GetLevensteinDistance(dns_names[i], dns_names[j]);
611       if (edit_distance < kMinimumEditDsitance)
612         return false;
613     }
614   }
615   return true;
616 }
617
618 // static
619 bool SSLErrorClassification::IsHostnameNonUniqueOrDotless(
620     const std::string& hostname) {
621   return net::IsHostnameNonUnique(hostname) ||
622          hostname.find('.') == std::string::npos;
623 }
624
625 void SSLErrorClassification::Observe(
626     int type,
627     const content::NotificationSource& source,
628     const content::NotificationDetails& details) {
629 #if defined(ENABLE_CAPTIVE_PORTAL_DETECTION)
630   // When detection is disabled, captive portal service always sends
631   // RESULT_INTERNET_CONNECTED. Ignore any probe results in that case.
632   if (!captive_portal_detection_enabled_)
633     return;
634   if (type == chrome::NOTIFICATION_CAPTIVE_PORTAL_CHECK_RESULT) {
635     captive_portal_probe_completed_ = true;
636     CaptivePortalService::Results* results =
637         content::Details<CaptivePortalService::Results>(
638             details).ptr();
639     // If a captive portal was detected at any point when the interstitial was
640     // displayed, assume that the interstitial was caused by a captive portal.
641     // Example scenario:
642     // 1- Interstitial displayed and captive portal detected, setting the flag.
643     // 2- Captive portal detection automatically opens portal login page.
644     // 3- User logs in on the portal login page.
645     // A notification will be received here for RESULT_INTERNET_CONNECTED. Make
646     // sure we don't clear the captive protal flag, since the interstitial was
647     // potentially caused by the captive portal.
648     captive_portal_detected_ = captive_portal_detected_ ||
649         (results->result == captive_portal::RESULT_BEHIND_CAPTIVE_PORTAL);
650     // Also keep track of non-HTTP portals and error cases.
651     captive_portal_no_response_ = captive_portal_no_response_ ||
652         (results->result == captive_portal::RESULT_NO_RESPONSE);
653   }
654 #endif
655 }