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#ifndef CHROME_BROWSER_SSL_SSL_ERROR_CLASSIFICATION_H_
6#define CHROME_BROWSER_SSL_SSL_ERROR_CLASSIFICATION_H_
7
8#include <string>
9#include <vector>
10
11#include "base/time/time.h"
12#include "content/public/browser/notification_observer.h"
13#include "content/public/browser/notification_registrar.h"
14#include "net/cert/x509_certificate.h"
15#include "url/gurl.h"
16
17namespace content {
18class WebContents;
19}
20
21// This class classifies characteristics of SSL errors, including information
22// about captive portal detection.
23//
24// This class should only be used on the UI thread because its
25// implementation uses captive_portal::CaptivePortalService which can only be
26// accessed on the UI thread.
27class SSLErrorClassification : public content::NotificationObserver {
28 public:
29  SSLErrorClassification(content::WebContents* web_contents,
30                         const base::Time& current_time,
31                         const GURL& url,
32                         int cert_error,
33                         const net::X509Certificate& cert);
34  virtual ~SSLErrorClassification();
35
36  // Returns true if the system time is in the past.
37  static bool IsUserClockInThePast(const base::Time& time_now);
38
39  // Returns true if the system time is too far in the future or the user is
40  // using a version of Chrome which is more than 1 year old.
41  static bool IsUserClockInTheFuture(const base::Time& time_now);
42
43  // Returns true if the Windows platform is likely to not have SHA-256 support.
44  // On other platforms, returns false always.
45  static bool MaybeWindowsLacksSHA256Support();
46
47  // A function which calculates the severity score when the ssl error is
48  // |CERT_DATE_INVALID|. The calculated score is between 0.0 and 1.0, higher
49  // being more severe, indicating how severe the certificate's
50  // date invalid error is.
51  void InvalidDateSeverityScore();
52
53  // A function which calculates the severity score when the ssl error is
54  // |CERT_COMMON_NAME_INVALID|. The calculated score is between 0.0 and 1.0,
55  // higher being more severe, indicating how severe the certificate's common
56  // name invalid error is.
57  void InvalidCommonNameSeverityScore();
58
59  void RecordUMAStatistics(bool overridable) const;
60  void RecordCaptivePortalUMAStatistics(bool overridable) const;
61  base::TimeDelta TimePassedSinceExpiry() const;
62
63 private:
64  FRIEND_TEST_ALL_PREFIXES(SSLErrorClassificationTest, TestDateInvalidScore);
65  FRIEND_TEST_ALL_PREFIXES(SSLErrorClassificationTest, TestNameMismatch);
66  FRIEND_TEST_ALL_PREFIXES(SSLErrorClassificationTest,
67                           TestHostNameHasKnownTLD);
68
69  typedef std::vector<std::string> Tokens;
70
71  // Returns true if the hostname has a known Top Level Domain.
72  static bool IsHostNameKnownTLD(const std::string& host_name);
73
74  // Returns true if the site's hostname differs from one of the DNS
75  // names in the certificate (CN or SANs) only by the presence or
76  // absence of the single-label prefix "www". E.g.:
77  //
78  //     www.example.com ~ example.com -> true
79  //     example.com ~ www.example.com -> true
80  //     www.food.example.com ~ example.com -> false
81  //     mail.example.com ~ example.com -> false
82  bool IsWWWSubDomainMatch() const;
83
84  // Returns true if |child| is a subdomain of any of the |potential_parents|.
85  bool NameUnderAnyNames(const Tokens& child,
86                        const std::vector<Tokens>& potential_parents) const;
87
88  // Returns true if any of the |potential_children| is a subdomain of the
89  // |parent|. The inverse case should be treated carefully as this is most
90  // likely a MITM attack. We don't want foo.appspot.com to be able to MITM for
91  // appspot.com.
92  bool AnyNamesUnderName(const std::vector<Tokens>& potential_children,
93                        const Tokens& parent) const;
94
95  // Returns true if |hostname| is too broad for the scope of a wildcard
96  // certificate. E.g.:
97  //
98  //     a.b.example.com ~ *.example.com --> true
99  //     b.example.com ~ *.example.com --> false
100  bool IsSubDomainOutsideWildcard(const Tokens& hostname) const;
101
102  // Returns true if the certificate is a shared certificate. Note - This
103  // function should be used with caution (only for UMA histogram) as an
104  // attacker could easily get a certificate with more than 5 names in the SAN
105  // fields.
106  bool IsCertLikelyFromMultiTenantHosting() const;
107
108  static std::vector<Tokens> GetTokenizedDNSNames(
109      const std::vector<std::string>& dns_names);
110
111  // If |potential_subdomain| is a subdomain of |parent|, returns the
112  // number of DNS labels by which |potential_subdomain| is under
113  // |parent|. Otherwise, returns 0.
114  //
115  // For example,
116  //
117  //   FindSubDomainDifference(Tokenize("a.b.example.com"),
118  //                           Tokenize("example.com"))
119  // --> 2.
120  size_t FindSubDomainDifference(const Tokens& potential_subdomain,
121                                 const Tokens& parent) const;
122
123  static Tokens Tokenize(const std::string& name);
124
125  float CalculateScoreTimePassedSinceExpiry() const;
126  float CalculateScoreEnvironments() const;
127
128  // content::NotificationObserver:
129  virtual void Observe(
130      int type,
131      const content::NotificationSource& source,
132      const content::NotificationDetails& details) OVERRIDE;
133
134  content::WebContents* web_contents_;
135  // This stores the current time.
136  base::Time current_time_;
137  const GURL& request_url_;
138  int cert_error_;
139  // This stores the certificate.
140  const net::X509Certificate& cert_;
141  // Is captive portal detection enabled?
142  bool captive_portal_detection_enabled_;
143  // Did the probe complete before the interstitial was closed?
144  bool captive_portal_probe_completed_;
145  // Did the captive portal probe receive an error or get a non-HTTP response?
146  bool captive_portal_no_response_;
147  // Was a captive portal detected?
148  bool captive_portal_detected_;
149
150  content::NotificationRegistrar registrar_;
151};
152
153#endif  // CHROME_BROWSER_SSL_SSL_ERROR_CLASSIFICATION_H_
154