Roll src/third_party/WebKit eac3800:0237a66 (svn 202606:202607)
[chromium-blink-merge.git] / chrome / browser / ssl / security_state_model.cc
blob808c7d4b08aa81a8353511dd229b0b0d920ecfc0
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
3 // found in the LICENSE file.
5 #include "chrome/browser/ssl/security_state_model.h"
7 #include "base/command_line.h"
8 #include "base/metrics/field_trial.h"
9 #include "base/metrics/histogram_macros.h"
10 #include "base/prefs/pref_service.h"
11 #include "chrome/browser/profiles/profile.h"
12 #include "chrome/common/chrome_constants.h"
13 #include "chrome/common/chrome_switches.h"
14 #include "chrome/common/pref_names.h"
15 #include "chrome/common/pref_names.h"
16 #include "content/public/browser/cert_store.h"
17 #include "content/public/browser/navigation_entry.h"
18 #include "content/public/browser/navigation_handle.h"
19 #include "content/public/browser/web_contents.h"
20 #include "content/public/common/origin_util.h"
21 #include "net/ssl/ssl_connection_status_flags.h"
23 #if defined(OS_CHROMEOS)
24 #include "chrome/browser/chromeos/policy/policy_cert_service.h"
25 #include "chrome/browser/chromeos/policy/policy_cert_service_factory.h"
26 #endif
28 DEFINE_WEB_CONTENTS_USER_DATA_KEY(SecurityStateModel);
30 namespace {
32 SecurityStateModel::SecurityLevel GetSecurityLevelForNonSecureFieldTrial() {
33 std::string choice =
34 base::CommandLine::ForCurrentProcess()->GetSwitchValueASCII(
35 switches::kMarkNonSecureAs);
36 std::string group = base::FieldTrialList::FindFullName("MarkNonSecureAs");
38 // Do not change this enum. It is used in the histogram.
39 enum MarkNonSecureStatus { NEUTRAL, DUBIOUS, NON_SECURE, LAST_STATUS };
40 const char kEnumeration[] = "MarkNonSecureAs";
42 SecurityStateModel::SecurityLevel level = SecurityStateModel::NONE;
43 MarkNonSecureStatus status;
45 if (choice == switches::kMarkNonSecureAsNeutral) {
46 status = NEUTRAL;
47 level = SecurityStateModel::NONE;
48 } else if (choice == switches::kMarkNonSecureAsNonSecure) {
49 status = NON_SECURE;
50 level = SecurityStateModel::SECURITY_ERROR;
51 } else if (group == switches::kMarkNonSecureAsNeutral) {
52 status = NEUTRAL;
53 level = SecurityStateModel::NONE;
54 } else if (group == switches::kMarkNonSecureAsNonSecure) {
55 status = NON_SECURE;
56 level = SecurityStateModel::SECURITY_ERROR;
57 } else {
58 status = NEUTRAL;
59 level = SecurityStateModel::NONE;
62 UMA_HISTOGRAM_ENUMERATION(kEnumeration, status, LAST_STATUS);
63 return level;
66 scoped_refptr<net::X509Certificate> GetCertForSSLStatus(
67 const content::SSLStatus& ssl) {
68 scoped_refptr<net::X509Certificate> cert;
69 return content::CertStore::GetInstance()->RetrieveCert(ssl.cert_id, &cert)
70 ? cert
71 : nullptr;
74 SecurityStateModel::SHA1DeprecationStatus GetSHA1DeprecationStatus(
75 scoped_refptr<net::X509Certificate> cert,
76 const content::SSLStatus& ssl) {
77 if (!cert || !(ssl.cert_status & net::CERT_STATUS_SHA1_SIGNATURE_PRESENT))
78 return SecurityStateModel::NO_DEPRECATED_SHA1;
80 // The internal representation of the dates for UI treatment of SHA-1.
81 // See http://crbug.com/401365 for details.
82 static const int64_t kJanuary2017 = INT64_C(13127702400000000);
83 if (cert->valid_expiry() >= base::Time::FromInternalValue(kJanuary2017))
84 return SecurityStateModel::DEPRECATED_SHA1_BROKEN;
85 static const int64_t kJanuary2016 = INT64_C(13096080000000000);
86 if (cert->valid_expiry() >= base::Time::FromInternalValue(kJanuary2016))
87 return SecurityStateModel::DEPRECATED_SHA1_WARNING;
89 return SecurityStateModel::NO_DEPRECATED_SHA1;
92 SecurityStateModel::MixedContentStatus GetMixedContentStatus(
93 const content::SSLStatus& ssl) {
94 bool ran_insecure_content = false;
95 bool displayed_insecure_content = false;
96 if (ssl.content_status & content::SSLStatus::RAN_INSECURE_CONTENT)
97 ran_insecure_content = true;
98 if (ssl.content_status & content::SSLStatus::DISPLAYED_INSECURE_CONTENT)
99 displayed_insecure_content = true;
101 if (ran_insecure_content && displayed_insecure_content)
102 return SecurityStateModel::RAN_AND_DISPLAYED_MIXED_CONTENT;
103 if (ran_insecure_content)
104 return SecurityStateModel::RAN_MIXED_CONTENT;
105 if (displayed_insecure_content)
106 return SecurityStateModel::DISPLAYED_MIXED_CONTENT;
108 return SecurityStateModel::NO_MIXED_CONTENT;
111 SecurityStateModel::SecurityLevel GetSecurityLevelForRequest(
112 const GURL& url,
113 const content::SSLStatus& ssl,
114 Profile* profile,
115 scoped_refptr<net::X509Certificate> cert,
116 SecurityStateModel::SHA1DeprecationStatus sha1_status,
117 SecurityStateModel::MixedContentStatus mixed_content_status) {
118 switch (ssl.security_style) {
119 case content::SECURITY_STYLE_UNKNOWN:
120 return SecurityStateModel::NONE;
122 case content::SECURITY_STYLE_UNAUTHENTICATED: {
123 if (!content::IsOriginSecure(url) && url.IsStandard())
124 return GetSecurityLevelForNonSecureFieldTrial();
125 return SecurityStateModel::NONE;
128 case content::SECURITY_STYLE_AUTHENTICATION_BROKEN:
129 return SecurityStateModel::SECURITY_ERROR;
131 case content::SECURITY_STYLE_WARNING:
132 NOTREACHED();
133 return SecurityStateModel::SECURITY_WARNING;
135 case content::SECURITY_STYLE_AUTHENTICATED: {
136 #if defined(OS_CHROMEOS)
137 // Report if there is a policy cert first, before reporting any other
138 // authenticated-but-with-errors cases. A policy cert is a strong
139 // indicator of a MITM being present (the enterprise), while the
140 // other authenticated-but-with-errors indicate something may
141 // be wrong, or may be wrong in the future, but is unclear now.
142 policy::PolicyCertService* service =
143 policy::PolicyCertServiceFactory::GetForProfile(profile);
144 if (service && service->UsedPolicyCertificates())
145 return SecurityStateModel::SECURITY_POLICY_WARNING;
146 #endif
148 if (sha1_status == SecurityStateModel::DEPRECATED_SHA1_BROKEN)
149 return SecurityStateModel::SECURITY_ERROR;
150 if (sha1_status == SecurityStateModel::DEPRECATED_SHA1_WARNING)
151 return SecurityStateModel::NONE;
153 // Active mixed content is downgraded to the BROKEN style and
154 // handled above.
155 DCHECK_NE(SecurityStateModel::RAN_MIXED_CONTENT, mixed_content_status);
156 DCHECK_NE(SecurityStateModel::RAN_AND_DISPLAYED_MIXED_CONTENT,
157 mixed_content_status);
158 // This should be kept in sync with
159 // |kDisplayedInsecureContentStyle|. That is: the treatment
160 // given to passive mixed content here should be expressed by
161 // |kDisplayedInsecureContentStyle|, which is used to coordinate
162 // the treatment of passive mixed content with other security UI
163 // elements outside of //chrome.
164 if (mixed_content_status == SecurityStateModel::DISPLAYED_MIXED_CONTENT)
165 return SecurityStateModel::NONE;
167 if (net::IsCertStatusError(ssl.cert_status)) {
168 DCHECK(net::IsCertStatusMinorError(ssl.cert_status));
169 return SecurityStateModel::NONE;
171 if (net::SSLConnectionStatusToVersion(ssl.connection_status) ==
172 net::SSL_CONNECTION_VERSION_SSL3) {
173 // SSLv3 will be removed in the future.
174 return SecurityStateModel::SECURITY_WARNING;
176 if ((ssl.cert_status & net::CERT_STATUS_IS_EV) && cert)
177 return SecurityStateModel::EV_SECURE;
178 return SecurityStateModel::SECURE;
182 return SecurityStateModel::NONE;
185 } // namespace
187 const content::SecurityStyle
188 SecurityStateModel::kDisplayedInsecureContentStyle =
189 content::SECURITY_STYLE_UNAUTHENTICATED;
190 const content::SecurityStyle SecurityStateModel::kRanInsecureContentStyle =
191 content::SECURITY_STYLE_AUTHENTICATION_BROKEN;
193 SecurityStateModel::SecurityInfo::SecurityInfo()
194 : security_level(SecurityStateModel::NONE),
195 sha1_deprecation_status(SecurityStateModel::NO_DEPRECATED_SHA1),
196 mixed_content_status(SecurityStateModel::NO_MIXED_CONTENT),
197 scheme_is_cryptographic(false),
198 cert_status(0),
199 cert_id(0),
200 security_bits(-1),
201 connection_status(0) {}
203 SecurityStateModel::SecurityInfo::~SecurityInfo() {}
205 SecurityStateModel::~SecurityStateModel() {}
207 const SecurityStateModel::SecurityInfo& SecurityStateModel::GetSecurityInfo()
208 const {
209 content::NavigationEntry* entry =
210 web_contents_->GetController().GetVisibleEntry();
211 if (!entry) {
212 security_info_ = SecurityInfo();
213 visible_url_ = GURL();
214 visible_ssl_status_ = content::SSLStatus();
215 return security_info_;
218 if (entry->GetURL() == visible_url_ &&
219 entry->GetSSL().Equals(visible_ssl_status_)) {
220 return security_info_;
223 SecurityInfoForRequest(
224 entry->GetURL(), entry->GetSSL(),
225 Profile::FromBrowserContext(web_contents_->GetBrowserContext()),
226 &security_info_);
227 visible_url_ = entry->GetURL();
228 visible_ssl_status_ = entry->GetSSL();
229 return security_info_;
232 // static
233 void SecurityStateModel::SecurityInfoForRequest(const GURL& url,
234 const content::SSLStatus& ssl,
235 Profile* profile,
236 SecurityInfo* security_info) {
237 scoped_refptr<net::X509Certificate> cert = GetCertForSSLStatus(ssl);
238 security_info->cert_id = ssl.cert_id;
239 security_info->sha1_deprecation_status = GetSHA1DeprecationStatus(cert, ssl);
240 security_info->mixed_content_status = GetMixedContentStatus(ssl);
241 security_info->security_bits = ssl.security_bits;
242 security_info->connection_status = ssl.connection_status;
243 security_info->cert_status = ssl.cert_status;
244 security_info->scheme_is_cryptographic = url.SchemeIsCryptographic();
246 security_info->sct_verify_statuses.clear();
247 for (const auto& sct : ssl.signed_certificate_timestamp_ids) {
248 security_info->sct_verify_statuses.push_back(sct.status);
251 security_info->security_level = GetSecurityLevelForRequest(
252 url, ssl, profile, cert, security_info->sha1_deprecation_status,
253 security_info->mixed_content_status);
256 SecurityStateModel::SecurityStateModel(content::WebContents* web_contents)
257 : web_contents_(web_contents) {}