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"
28 DEFINE_WEB_CONTENTS_USER_DATA_KEY(SecurityStateModel
);
32 SecurityStateModel::SecurityLevel
GetSecurityLevelForNonSecureFieldTrial() {
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
) {
47 level
= SecurityStateModel::NONE
;
48 } else if (choice
== switches::kMarkNonSecureAsNonSecure
) {
50 level
= SecurityStateModel::SECURITY_ERROR
;
51 } else if (group
== switches::kMarkNonSecureAsNeutral
) {
53 level
= SecurityStateModel::NONE
;
54 } else if (group
== switches::kMarkNonSecureAsNonSecure
) {
56 level
= SecurityStateModel::SECURITY_ERROR
;
59 level
= SecurityStateModel::NONE
;
62 UMA_HISTOGRAM_ENUMERATION(kEnumeration
, status
, LAST_STATUS
);
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
)
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(
113 const content::SSLStatus
& ssl
,
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
:
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
;
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
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
;
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),
201 connection_status(0) {}
203 SecurityStateModel::SecurityInfo::~SecurityInfo() {}
205 SecurityStateModel::~SecurityStateModel() {}
207 const SecurityStateModel::SecurityInfo
& SecurityStateModel::GetSecurityInfo()
209 content::NavigationEntry
* entry
=
210 web_contents_
->GetController().GetVisibleEntry();
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()),
227 visible_url_
= entry
->GetURL();
228 visible_ssl_status_
= entry
->GetSSL();
229 return security_info_
;
233 void SecurityStateModel::SecurityInfoForRequest(const GURL
& url
,
234 const content::SSLStatus
& ssl
,
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
) {}