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/web_contents.h"
19 #include "content/public/common/origin_util.h"
20 #include "net/ssl/ssl_connection_status_flags.h"
22 #if defined(OS_CHROMEOS)
23 #include "chrome/browser/chromeos/policy/policy_cert_service.h"
24 #include "chrome/browser/chromeos/policy/policy_cert_service_factory.h"
27 DEFINE_WEB_CONTENTS_USER_DATA_KEY(SecurityStateModel
);
31 SecurityStateModel::SecurityLevel
GetSecurityLevelForNonSecureFieldTrial() {
33 base::CommandLine::ForCurrentProcess()->GetSwitchValueASCII(
34 switches::kMarkNonSecureAs
);
35 std::string group
= base::FieldTrialList::FindFullName("MarkNonSecureAs");
37 // Do not change this enum. It is used in the histogram.
38 enum MarkNonSecureStatus
{ NEUTRAL
, DUBIOUS
, NON_SECURE
, LAST_STATUS
};
39 const char kEnumeration
[] = "MarkNonSecureAs";
41 SecurityStateModel::SecurityLevel level
= SecurityStateModel::NONE
;
42 MarkNonSecureStatus status
;
44 if (choice
== switches::kMarkNonSecureAsNeutral
) {
46 level
= SecurityStateModel::NONE
;
47 } else if (choice
== switches::kMarkNonSecureAsNonSecure
) {
49 level
= SecurityStateModel::SECURITY_ERROR
;
50 } else if (group
== switches::kMarkNonSecureAsNeutral
) {
52 level
= SecurityStateModel::NONE
;
53 } else if (group
== switches::kMarkNonSecureAsNonSecure
) {
55 level
= SecurityStateModel::SECURITY_ERROR
;
58 level
= SecurityStateModel::NONE
;
61 UMA_HISTOGRAM_ENUMERATION(kEnumeration
, status
, LAST_STATUS
);
65 scoped_refptr
<net::X509Certificate
> GetCertForSSLStatus(
66 const content::SSLStatus
& ssl
) {
67 scoped_refptr
<net::X509Certificate
> cert
;
68 return content::CertStore::GetInstance()->RetrieveCert(ssl
.cert_id
, &cert
)
73 SecurityStateModel::SHA1DeprecationStatus
GetSHA1DeprecationStatus(
74 scoped_refptr
<net::X509Certificate
> cert
,
75 const content::SSLStatus
& ssl
) {
76 if (!cert
|| !(ssl
.cert_status
& net::CERT_STATUS_SHA1_SIGNATURE_PRESENT
))
77 return SecurityStateModel::NO_DEPRECATED_SHA1
;
79 // The internal representation of the dates for UI treatment of SHA-1.
80 // See http://crbug.com/401365 for details.
81 static const int64_t kJanuary2017
= INT64_C(13127702400000000);
82 if (cert
->valid_expiry() >= base::Time::FromInternalValue(kJanuary2017
))
83 return SecurityStateModel::DEPRECATED_SHA1_BROKEN
;
84 static const int64_t kJanuary2016
= INT64_C(13096080000000000);
85 if (cert
->valid_expiry() >= base::Time::FromInternalValue(kJanuary2016
))
86 return SecurityStateModel::DEPRECATED_SHA1_WARNING
;
88 return SecurityStateModel::NO_DEPRECATED_SHA1
;
91 SecurityStateModel::MixedContentStatus
GetMixedContentStatus(
92 const content::SSLStatus
& ssl
) {
93 bool ran_insecure_content
= false;
94 bool displayed_insecure_content
= false;
95 if (ssl
.content_status
& content::SSLStatus::RAN_INSECURE_CONTENT
)
96 ran_insecure_content
= true;
97 if (ssl
.content_status
& content::SSLStatus::DISPLAYED_INSECURE_CONTENT
)
98 displayed_insecure_content
= true;
100 if (ran_insecure_content
&& displayed_insecure_content
)
101 return SecurityStateModel::RAN_AND_DISPLAYED_MIXED_CONTENT
;
102 if (ran_insecure_content
)
103 return SecurityStateModel::RAN_MIXED_CONTENT
;
104 if (displayed_insecure_content
)
105 return SecurityStateModel::DISPLAYED_MIXED_CONTENT
;
107 return SecurityStateModel::NO_MIXED_CONTENT
;
110 SecurityStateModel::SecurityLevel
GetSecurityLevelForRequest(
112 const content::SSLStatus
& ssl
,
114 scoped_refptr
<net::X509Certificate
> cert
,
115 SecurityStateModel::SHA1DeprecationStatus sha1_status
,
116 SecurityStateModel::MixedContentStatus mixed_content_status
) {
117 switch (ssl
.security_style
) {
118 case content::SECURITY_STYLE_UNKNOWN
:
119 return SecurityStateModel::NONE
;
121 case content::SECURITY_STYLE_UNAUTHENTICATED
: {
122 if (!content::IsOriginSecure(url
) && url
.IsStandard())
123 return GetSecurityLevelForNonSecureFieldTrial();
124 return SecurityStateModel::NONE
;
127 case content::SECURITY_STYLE_AUTHENTICATION_BROKEN
:
128 return SecurityStateModel::SECURITY_ERROR
;
130 case content::SECURITY_STYLE_WARNING
:
132 return SecurityStateModel::SECURITY_WARNING
;
134 case content::SECURITY_STYLE_AUTHENTICATED
: {
135 #if defined(OS_CHROMEOS)
136 // Report if there is a policy cert first, before reporting any other
137 // authenticated-but-with-errors cases. A policy cert is a strong
138 // indicator of a MITM being present (the enterprise), while the
139 // other authenticated-but-with-errors indicate something may
140 // be wrong, or may be wrong in the future, but is unclear now.
141 policy::PolicyCertService
* service
=
142 policy::PolicyCertServiceFactory::GetForProfile(profile
);
143 if (service
&& service
->UsedPolicyCertificates())
144 return SecurityStateModel::SECURITY_POLICY_WARNING
;
147 if (sha1_status
== SecurityStateModel::DEPRECATED_SHA1_BROKEN
)
148 return SecurityStateModel::SECURITY_ERROR
;
149 if (sha1_status
== SecurityStateModel::DEPRECATED_SHA1_WARNING
)
150 return SecurityStateModel::NONE
;
152 // Active mixed content is downgraded to the BROKEN style and
154 DCHECK_NE(SecurityStateModel::RAN_MIXED_CONTENT
, mixed_content_status
);
155 DCHECK_NE(SecurityStateModel::RAN_AND_DISPLAYED_MIXED_CONTENT
,
156 mixed_content_status
);
157 // This should be kept in sync with
158 // |kDisplayedInsecureContentStyle|. That is: the treatment
159 // given to passive mixed content here should be expressed by
160 // |kDisplayedInsecureContentStyle|, which is used to coordinate
161 // the treatment of passive mixed content with other security UI
162 // elements outside of //chrome.
163 if (mixed_content_status
== SecurityStateModel::DISPLAYED_MIXED_CONTENT
)
164 return SecurityStateModel::NONE
;
166 if (net::IsCertStatusError(ssl
.cert_status
)) {
167 DCHECK(net::IsCertStatusMinorError(ssl
.cert_status
));
168 return SecurityStateModel::NONE
;
170 if (net::SSLConnectionStatusToVersion(ssl
.connection_status
) ==
171 net::SSL_CONNECTION_VERSION_SSL3
) {
172 // SSLv3 will be removed in the future.
173 return SecurityStateModel::SECURITY_WARNING
;
175 if ((ssl
.cert_status
& net::CERT_STATUS_IS_EV
) && cert
)
176 return SecurityStateModel::EV_SECURE
;
177 return SecurityStateModel::SECURE
;
181 return SecurityStateModel::NONE
;
186 const content::SecurityStyle
187 SecurityStateModel::kDisplayedInsecureContentStyle
=
188 content::SECURITY_STYLE_UNAUTHENTICATED
;
189 const content::SecurityStyle
SecurityStateModel::kRanInsecureContentStyle
=
190 content::SECURITY_STYLE_AUTHENTICATION_BROKEN
;
192 SecurityStateModel::SecurityInfo::SecurityInfo()
193 : security_level(SecurityStateModel::NONE
),
194 sha1_deprecation_status(SecurityStateModel::NO_DEPRECATED_SHA1
),
195 mixed_content_status(SecurityStateModel::NO_MIXED_CONTENT
),
196 scheme_is_cryptographic(false),
200 connection_status(0) {}
202 SecurityStateModel::SecurityInfo::~SecurityInfo() {}
204 SecurityStateModel::~SecurityStateModel() {}
206 void SecurityStateModel::SecurityStateChanged() {
207 DCHECK(web_contents_
);
208 content::NavigationEntry
* entry
=
209 web_contents_
->GetController().GetVisibleEntry();
213 SecurityInfoForRequest(
214 entry
->GetURL(), entry
->GetSSL(),
215 Profile::FromBrowserContext(web_contents_
->GetBrowserContext()),
219 const SecurityStateModel::SecurityInfo
& SecurityStateModel::security_info()
221 return security_info_
;
225 void SecurityStateModel::SecurityInfoForRequest(const GURL
& url
,
226 const content::SSLStatus
& ssl
,
228 SecurityInfo
* security_info
) {
229 scoped_refptr
<net::X509Certificate
> cert
= GetCertForSSLStatus(ssl
);
230 security_info
->cert_id
= ssl
.cert_id
;
231 security_info
->sha1_deprecation_status
= GetSHA1DeprecationStatus(cert
, ssl
);
232 security_info
->mixed_content_status
= GetMixedContentStatus(ssl
);
233 security_info
->security_bits
= ssl
.security_bits
;
234 security_info
->connection_status
= ssl
.connection_status
;
235 security_info
->cert_status
= ssl
.cert_status
;
236 security_info
->scheme_is_cryptographic
= url
.SchemeIsCryptographic();
238 security_info
->sct_verify_statuses
.clear();
239 for (const auto& sct
: ssl
.signed_certificate_timestamp_ids
) {
240 security_info
->sct_verify_statuses
.push_back(sct
.status
);
243 security_info
->security_level
= GetSecurityLevelForRequest(
244 url
, ssl
, profile
, cert
, security_info
->sha1_deprecation_status
,
245 security_info
->mixed_content_status
);
248 SecurityStateModel::SecurityStateModel(content::WebContents
* web_contents
)
249 : web_contents_(web_contents
) {}