1 // Copyright (c) 2012 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 "content/browser/ssl/ssl_policy.h"
7 #include "base/base_switches.h"
9 #include "base/command_line.h"
10 #include "base/memory/singleton.h"
11 #include "base/metrics/histogram_macros.h"
12 #include "base/strings/string_piece.h"
13 #include "base/strings/string_util.h"
14 #include "content/browser/frame_host/navigation_entry_impl.h"
15 #include "content/browser/renderer_host/render_process_host_impl.h"
16 #include "content/browser/renderer_host/render_view_host_impl.h"
17 #include "content/browser/site_instance_impl.h"
18 #include "content/browser/ssl/ssl_cert_error_handler.h"
19 #include "content/browser/ssl/ssl_request_info.h"
20 #include "content/browser/web_contents/web_contents_impl.h"
21 #include "content/public/browser/content_browser_client.h"
22 #include "content/public/browser/web_contents.h"
23 #include "content/public/common/resource_type.h"
24 #include "content/public/common/ssl_status.h"
25 #include "content/public/common/url_constants.h"
26 #include "net/ssl/ssl_info.h"
33 // Events for UMA. Do not reorder or change!
34 enum SSLGoodCertSeenEvent
{
35 NO_PREVIOUS_EXCEPTION
= 0,
36 HAD_PREVIOUS_EXCEPTION
= 1,
37 SSL_GOOD_CERT_SEEN_EVENT_MAX
= 2
41 SSLPolicy::SSLPolicy(SSLPolicyBackend
* backend
)
46 void SSLPolicy::OnCertError(SSLCertErrorHandler
* handler
) {
47 bool expired_previous_decision
;
48 // First we check if we know the policy for this error.
49 DCHECK(handler
->ssl_info().is_valid());
50 SSLHostStateDelegate::CertJudgment judgment
=
51 backend_
->QueryPolicy(*handler
->ssl_info().cert
.get(),
52 handler
->request_url().host(),
53 handler
->cert_error(),
54 &expired_previous_decision
);
56 if (judgment
== SSLHostStateDelegate::ALLOWED
) {
57 handler
->ContinueRequest();
61 // For all other hosts, which must be DENIED, a blocking page is shown to the
62 // user every time they come back to the page.
64 switch (handler
->cert_error()) {
65 case net::ERR_CERT_COMMON_NAME_INVALID
:
66 case net::ERR_CERT_DATE_INVALID
:
67 case net::ERR_CERT_AUTHORITY_INVALID
:
68 case net::ERR_CERT_WEAK_SIGNATURE_ALGORITHM
:
69 case net::ERR_CERT_WEAK_KEY
:
70 case net::ERR_CERT_NAME_CONSTRAINT_VIOLATION
:
71 case net::ERR_CERT_VALIDITY_TOO_LONG
:
72 if (!handler
->fatal())
73 options_mask
|= OVERRIDABLE
;
75 options_mask
|= STRICT_ENFORCEMENT
;
76 if (expired_previous_decision
)
77 options_mask
|= EXPIRED_PREVIOUS_DECISION
;
78 OnCertErrorInternal(handler
, options_mask
);
80 case net::ERR_CERT_NO_REVOCATION_MECHANISM
:
82 handler
->ContinueRequest();
84 case net::ERR_CERT_UNABLE_TO_CHECK_REVOCATION
:
85 // We ignore this error but will show a warning status in the location
87 handler
->ContinueRequest();
89 case net::ERR_CERT_CONTAINS_ERRORS
:
90 case net::ERR_CERT_REVOKED
:
91 case net::ERR_CERT_INVALID
:
92 case net::ERR_SSL_WEAK_SERVER_EPHEMERAL_DH_KEY
:
93 case net::ERR_SSL_PINNED_KEY_NOT_IN_CERT_CHAIN
:
95 options_mask
|= STRICT_ENFORCEMENT
;
96 if (expired_previous_decision
)
97 options_mask
|= EXPIRED_PREVIOUS_DECISION
;
98 OnCertErrorInternal(handler
, options_mask
);
102 handler
->CancelRequest();
107 void SSLPolicy::DidRunInsecureContent(NavigationEntryImpl
* entry
,
108 const std::string
& security_origin
) {
112 SiteInstance
* site_instance
= entry
->site_instance();
116 backend_
->HostRanInsecureContent(GURL(security_origin
).host(),
117 site_instance
->GetProcess()->GetID());
120 void SSLPolicy::OnRequestStarted(SSLRequestInfo
* info
) {
121 // TODO(abarth): This mechanism is wrong. What we should be doing is sending
122 // this information back through WebKit and out some FrameLoaderClient
125 if (net::IsCertStatusError(info
->ssl_cert_status())) {
126 backend_
->HostRanInsecureContent(info
->url().host(), info
->child_id());
127 } else if (info
->ssl_cert_id() && info
->url().SchemeIsCryptographic()) {
128 // If the scheme is https: or wss: *and* the security info for the cert has
129 // been set (i.e. the cert id is not 0), revoke any previous decisions that
130 // have occurred. If the cert info has not been set, do nothing since it
131 // isn't known if the connection was actually a valid connection or if it
133 SSLGoodCertSeenEvent event
= NO_PREVIOUS_EXCEPTION
;
134 if (backend_
->HasAllowException(info
->url().host())) {
135 // If there's no certificate error, a good certificate has been seen, so
136 // clear out any exceptions that were made by the user for bad
138 backend_
->RevokeUserAllowExceptions(info
->url().host());
139 event
= HAD_PREVIOUS_EXCEPTION
;
141 UMA_HISTOGRAM_ENUMERATION("interstitial.ssl.good_cert_seen", event
,
142 SSL_GOOD_CERT_SEEN_EVENT_MAX
);
146 void SSLPolicy::UpdateEntry(NavigationEntryImpl
* entry
,
147 WebContents
* web_contents
) {
150 InitializeEntryIfNeeded(entry
);
152 if (entry
->GetSSL().security_style
== SECURITY_STYLE_UNAUTHENTICATED
)
155 if (!web_contents
->DisplayedInsecureContent())
156 entry
->GetSSL().content_status
&= ~SSLStatus::DISPLAYED_INSECURE_CONTENT
;
158 if (web_contents
->DisplayedInsecureContent())
159 entry
->GetSSL().content_status
|= SSLStatus::DISPLAYED_INSECURE_CONTENT
;
161 if (entry
->GetSSL().security_style
== SECURITY_STYLE_AUTHENTICATION_BROKEN
)
164 SiteInstance
* site_instance
= entry
->site_instance();
165 // Note that |site_instance| can be NULL here because NavigationEntries don't
166 // necessarily have site instances. Without a process, the entry can't
167 // possibly have insecure content. See bug http://crbug.com/12423.
169 backend_
->DidHostRunInsecureContent(
170 entry
->GetURL().host(), site_instance
->GetProcess()->GetID())) {
171 entry
->GetSSL().security_style
=
172 SECURITY_STYLE_AUTHENTICATION_BROKEN
;
173 entry
->GetSSL().content_status
|= SSLStatus::RAN_INSECURE_CONTENT
;
179 SecurityStyle
SSLPolicy::GetSecurityStyleForResource(
182 net::CertStatus cert_status
) {
183 // An HTTPS response may not have a certificate for some reason. When that
184 // happens, use the unauthenticated (HTTP) rather than the authentication
185 // broken security style so that we can detect this error condition.
186 if (!url
.SchemeIsCryptographic() || !cert_id
)
187 return SECURITY_STYLE_UNAUTHENTICATED
;
189 // Minor errors don't lower the security style to
190 // SECURITY_STYLE_AUTHENTICATION_BROKEN.
191 if (net::IsCertStatusError(cert_status
) &&
192 !net::IsCertStatusMinorError(cert_status
)) {
193 return SECURITY_STYLE_AUTHENTICATION_BROKEN
;
196 return SECURITY_STYLE_AUTHENTICATED
;
199 void SSLPolicy::OnAllowCertificate(scoped_refptr
<SSLCertErrorHandler
> handler
,
201 DCHECK(handler
->ssl_info().is_valid());
203 // Default behavior for accepting a certificate.
204 // Note that we should not call SetMaxSecurityStyle here, because the active
205 // NavigationEntry has just been deleted (in HideInterstitialPage) and the
206 // new NavigationEntry will not be set until DidNavigate. This is ok,
207 // because the new NavigationEntry will have its max security style set
208 // within DidNavigate.
210 // While AllowCertForHost() executes synchronously on this thread,
211 // ContinueRequest() gets posted to a different thread. Calling
212 // AllowCertForHost() first ensures deterministic ordering.
213 backend_
->AllowCertForHost(*handler
->ssl_info().cert
.get(),
214 handler
->request_url().host(),
215 handler
->cert_error());
216 handler
->ContinueRequest();
218 // Default behavior for rejecting a certificate.
219 handler
->CancelRequest();
223 ////////////////////////////////////////////////////////////////////////////////
224 // Certificate Error Routines
226 void SSLPolicy::OnCertErrorInternal(SSLCertErrorHandler
* handler
,
228 bool overridable
= (options_mask
& OVERRIDABLE
) != 0;
229 bool strict_enforcement
= (options_mask
& STRICT_ENFORCEMENT
) != 0;
230 bool expired_previous_decision
=
231 (options_mask
& EXPIRED_PREVIOUS_DECISION
) != 0;
232 CertificateRequestResultType result
=
233 CERTIFICATE_REQUEST_RESULT_TYPE_CONTINUE
;
234 GetContentClient()->browser()->AllowCertificateError(
235 handler
->render_process_id(),
236 handler
->render_frame_id(),
237 handler
->cert_error(),
239 handler
->request_url(),
240 handler
->resource_type(),
243 expired_previous_decision
,
244 base::Bind(&SSLPolicy::OnAllowCertificate
,
245 base::Unretained(this),
246 make_scoped_refptr(handler
)),
249 case CERTIFICATE_REQUEST_RESULT_TYPE_CONTINUE
:
251 case CERTIFICATE_REQUEST_RESULT_TYPE_CANCEL
:
252 handler
->CancelRequest();
254 case CERTIFICATE_REQUEST_RESULT_TYPE_DENY
:
255 handler
->DenyRequest();
262 void SSLPolicy::InitializeEntryIfNeeded(NavigationEntryImpl
* entry
) {
263 if (entry
->GetSSL().security_style
!= SECURITY_STYLE_UNKNOWN
)
266 entry
->GetSSL().security_style
= GetSecurityStyleForResource(
267 entry
->GetURL(), entry
->GetSSL().cert_id
, entry
->GetSSL().cert_status
);
270 void SSLPolicy::OriginRanInsecureContent(const std::string
& origin
, int pid
) {
271 GURL
parsed_origin(origin
);
272 if (parsed_origin
.SchemeIsCryptographic())
273 backend_
->HostRanInsecureContent(parsed_origin
.host(), pid
);
276 } // namespace content