summaryrefslogtreecommitdiffstats
path: root/chrome/browser/ssl/ssl_error_classification.h
blob: 89869b9ad6dc0f9804841523e57874b81aec24da (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
// Copyright 2014 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#ifndef CHROME_BROWSER_SSL_SSL_ERROR_CLASSIFICATION_H_
#define CHROME_BROWSER_SSL_SSL_ERROR_CLASSIFICATION_H_

#include <string>
#include <vector>

#include "base/time/time.h"
#include "content/public/browser/notification_observer.h"
#include "content/public/browser/notification_registrar.h"
#include "net/cert/x509_certificate.h"
#include "url/gurl.h"

namespace content {
class WebContents;
}

// This class classifies characteristics of SSL errors, including information
// about captive portal detection.
//
// This class should only be used on the UI thread because its
// implementation uses captive_portal::CaptivePortalService which can only be
// accessed on the UI thread.
class SSLErrorClassification : public content::NotificationObserver {
 public:
  SSLErrorClassification(content::WebContents* web_contents,
                         const base::Time& current_time,
                         const GURL& url,
                         int cert_error,
                         const net::X509Certificate& cert);
  ~SSLErrorClassification() override;

  // Returns true if the system time is in the past.
  static bool IsUserClockInThePast(const base::Time& time_now);

  // Returns true if the system time is too far in the future or the user is
  // using a version of Chrome which is more than 1 year old.
  static bool IsUserClockInTheFuture(const base::Time& time_now);

  // Returns true if the Windows platform is likely to not have SHA-256 support.
  // On other platforms, returns false always.
  static bool MaybeWindowsLacksSHA256Support();

  // Returns true if any one of the following conditions hold:
  // 1.|hostname| is an IP Address in an IANA-reserved range.
  // 2.|hostname| is a not-yet-assigned by ICANN gTLD.
  // 3.|hostname| is a dotless domain.
  static bool IsHostnameNonUniqueOrDotless(const std::string& hostname);

  // A function which calculates the severity score when the ssl error is
  // |CERT_DATE_INVALID|. The calculated score is between 0.0 and 1.0, higher
  // being more severe, indicating how severe the certificate's
  // date invalid error is.
  void InvalidDateSeverityScore();

  // A function which calculates the severity score when the ssl error is
  // |CERT_COMMON_NAME_INVALID|. The calculated score is between 0.0 and 1.0,
  // higher being more severe, indicating how severe the certificate's common
  // name invalid error is.
  void InvalidCommonNameSeverityScore();

  // A function which calculates the severity score when the ssl error is
  // |CERT_AUTHORITY_INVALID|, returns a score between 0.0 and 1.0, higher
  // values being more severe, indicating how severe the certificate's
  // authority invalid error is.
  void InvalidAuthoritySeverityScore();

  void RecordUMAStatistics(bool overridable) const;
  void RecordCaptivePortalUMAStatistics(bool overridable) const;
  base::TimeDelta TimePassedSinceExpiry() const;

 private:
  FRIEND_TEST_ALL_PREFIXES(SSLErrorClassificationTest, TestDateInvalidScore);
  FRIEND_TEST_ALL_PREFIXES(SSLErrorClassificationTest, TestNameMismatch);
  FRIEND_TEST_ALL_PREFIXES(SSLErrorClassificationTest,
                           TestHostNameHasKnownTLD);

  typedef std::vector<std::string> Tokens;

  // Returns true if the hostname has a known Top Level Domain.
  static bool IsHostNameKnownTLD(const std::string& host_name);

  // Returns true if the site's hostname differs from one of the DNS
  // names in the certificate (CN or SANs) only by the presence or
  // absence of the single-label prefix "www". E.g.:
  //
  //     www.example.com ~ example.com -> true
  //     example.com ~ www.example.com -> true
  //     www.food.example.com ~ example.com -> false
  //     mail.example.com ~ example.com -> false
  bool IsWWWSubDomainMatch() const;

  // Returns true if |child| is a subdomain of any of the |potential_parents|.
  bool NameUnderAnyNames(const Tokens& child,
                        const std::vector<Tokens>& potential_parents) const;

  // Returns true if any of the |potential_children| is a subdomain of the
  // |parent|. The inverse case should be treated carefully as this is most
  // likely a MITM attack. We don't want foo.appspot.com to be able to MITM for
  // appspot.com.
  bool AnyNamesUnderName(const std::vector<Tokens>& potential_children,
                        const Tokens& parent) const;

  // Returns true if |hostname| is too broad for the scope of a wildcard
  // certificate. E.g.:
  //
  //     a.b.example.com ~ *.example.com --> true
  //     b.example.com ~ *.example.com --> false
  bool IsSubDomainOutsideWildcard(const Tokens& hostname) const;

  // Returns true if the certificate is a shared certificate. Note - This
  // function should be used with caution (only for UMA histogram) as an
  // attacker could easily get a certificate with more than 5 names in the SAN
  // fields.
  bool IsCertLikelyFromMultiTenantHosting() const;

  // Returns true if the hostname in |request_url_| has the same domain
  // (effective TLD + 1 label) as at least one of the subject
  // alternative names in |cert_|.
  bool IsCertLikelyFromSameDomain() const;

  static std::vector<Tokens> GetTokenizedDNSNames(
      const std::vector<std::string>& dns_names);

  // If |potential_subdomain| is a subdomain of |parent|, returns the
  // number of DNS labels by which |potential_subdomain| is under
  // |parent|. Otherwise, returns 0.
  //
  // For example,
  //
  //   FindSubDomainDifference(Tokenize("a.b.example.com"),
  //                           Tokenize("example.com"))
  // --> 2.
  size_t FindSubDomainDifference(const Tokens& potential_subdomain,
                                 const Tokens& parent) const;

  static Tokens Tokenize(const std::string& name);

  float CalculateScoreTimePassedSinceExpiry() const;
  float CalculateScoreEnvironments() const;

  // content::NotificationObserver:
  void Observe(int type,
               const content::NotificationSource& source,
               const content::NotificationDetails& details) override;

  content::WebContents* web_contents_;
  // This stores the current time.
  base::Time current_time_;
  const GURL request_url_;
  int cert_error_;
  // This stores the certificate.
  const net::X509Certificate& cert_;
  // Is captive portal detection enabled?
  bool captive_portal_detection_enabled_;
  // Did the probe complete before the interstitial was closed?
  bool captive_portal_probe_completed_;
  // Did the captive portal probe receive an error or get a non-HTTP response?
  bool captive_portal_no_response_;
  // Was a captive portal detected?
  bool captive_portal_detected_;

  content::NotificationRegistrar registrar_;
};

#endif  // CHROME_BROWSER_SSL_SSL_ERROR_CLASSIFICATION_H_