| OLD | NEW | 
|    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  Loading... | 
|   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 | 
| OLD | NEW |