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/strings/string_piece.h"
12 #include "base/strings/string_util.h"
13 #include "content/browser/renderer_host/render_process_host_impl.h"
14 #include "content/browser/renderer_host/render_view_host_impl.h"
15 #include "content/browser/site_instance_impl.h"
16 #include "content/browser/ssl/ssl_cert_error_handler.h"
17 #include "content/browser/ssl/ssl_request_info.h"
18 #include "content/browser/web_contents/navigation_entry_impl.h"
19 #include "content/browser/web_contents/web_contents_impl.h"
20 #include "content/public/browser/content_browser_client.h"
21 #include "content/public/common/ssl_status.h"
22 #include "content/public/common/url_constants.h"
23 #include "net/ssl/ssl_info.h"
24 #include "webkit/glue/resource_type.h"
29 const char kDot
= '.';
31 bool IsIntranetHost(const std::string
& host
) {
32 const size_t dot
= host
.find(kDot
);
33 return dot
== std::string::npos
|| dot
== host
.length() - 1;
40 SSLPolicy::SSLPolicy(SSLPolicyBackend
* backend
)
45 void SSLPolicy::OnCertError(SSLCertErrorHandler
* handler
) {
46 // First we check if we know the policy for this error.
47 net::CertPolicy::Judgment judgment
= backend_
->QueryPolicy(
48 handler
->ssl_info().cert
.get(), handler
->request_url().host());
50 if (judgment
== net::CertPolicy::ALLOWED
) {
51 handler
->ContinueRequest();
55 // The judgment is either DENIED or UNKNOWN.
56 // For now we handle the DENIED as the UNKNOWN, which means a blocking
57 // page is shown to the user every time he comes back to the page.
59 switch (handler
->cert_error()) {
60 case net::ERR_CERT_COMMON_NAME_INVALID
:
61 case net::ERR_CERT_DATE_INVALID
:
62 case net::ERR_CERT_AUTHORITY_INVALID
:
63 case net::ERR_CERT_WEAK_SIGNATURE_ALGORITHM
:
64 case net::ERR_CERT_WEAK_KEY
:
65 OnCertErrorInternal(handler
, !handler
->fatal(), handler
->fatal());
67 case net::ERR_CERT_NO_REVOCATION_MECHANISM
:
69 handler
->ContinueRequest();
71 case net::ERR_CERT_UNABLE_TO_CHECK_REVOCATION
:
72 // We ignore this error but will show a warning status in the location
74 handler
->ContinueRequest();
76 case net::ERR_CERT_CONTAINS_ERRORS
:
77 case net::ERR_CERT_REVOKED
:
78 case net::ERR_CERT_INVALID
:
79 OnCertErrorInternal(handler
, false, handler
->fatal());
83 handler
->CancelRequest();
88 void SSLPolicy::DidRunInsecureContent(NavigationEntryImpl
* entry
,
89 const std::string
& security_origin
) {
93 SiteInstance
* site_instance
= entry
->site_instance();
97 backend_
->HostRanInsecureContent(GURL(security_origin
).host(),
98 site_instance
->GetProcess()->GetID());
101 void SSLPolicy::OnRequestStarted(SSLRequestInfo
* info
) {
102 // TODO(abarth): This mechanism is wrong. What we should be doing is sending
103 // this information back through WebKit and out some FrameLoaderClient
106 if (net::IsCertStatusError(info
->ssl_cert_status()))
107 backend_
->HostRanInsecureContent(info
->url().host(), info
->child_id());
110 void SSLPolicy::UpdateEntry(NavigationEntryImpl
* entry
,
111 WebContentsImpl
* web_contents
) {
114 InitializeEntryIfNeeded(entry
);
116 if (!entry
->GetURL().SchemeIsSecure())
119 // An HTTPS response may not have a certificate for some reason. When that
120 // happens, use the unauthenticated (HTTP) rather than the authentication
121 // broken security style so that we can detect this error condition.
122 if (!entry
->GetSSL().cert_id
) {
123 entry
->GetSSL().security_style
= SECURITY_STYLE_UNAUTHENTICATED
;
127 if (!(entry
->GetSSL().cert_status
& net::CERT_STATUS_COMMON_NAME_INVALID
)) {
128 // CAs issue certificates for intranet hosts to everyone. Therefore, we
129 // mark intranet hosts as being non-unique.
130 if (IsIntranetHost(entry
->GetURL().host())) {
131 entry
->GetSSL().cert_status
|= net::CERT_STATUS_NON_UNIQUE_NAME
;
135 if (net::IsCertStatusError(entry
->GetSSL().cert_status
)) {
136 // Minor errors don't lower the security style to
137 // SECURITY_STYLE_AUTHENTICATION_BROKEN.
138 if (!net::IsCertStatusMinorError(entry
->GetSSL().cert_status
)) {
139 entry
->GetSSL().security_style
=
140 SECURITY_STYLE_AUTHENTICATION_BROKEN
;
145 SiteInstance
* site_instance
= entry
->site_instance();
146 // Note that |site_instance| can be NULL here because NavigationEntries don't
147 // necessarily have site instances. Without a process, the entry can't
148 // possibly have insecure content. See bug http://crbug.com/12423.
150 backend_
->DidHostRunInsecureContent(
151 entry
->GetURL().host(), site_instance
->GetProcess()->GetID())) {
152 entry
->GetSSL().security_style
=
153 SECURITY_STYLE_AUTHENTICATION_BROKEN
;
154 entry
->GetSSL().content_status
|= SSLStatus::RAN_INSECURE_CONTENT
;
158 if (web_contents
->DisplayedInsecureContent())
159 entry
->GetSSL().content_status
|= SSLStatus::DISPLAYED_INSECURE_CONTENT
;
162 void SSLPolicy::OnAllowCertificate(scoped_refptr
<SSLCertErrorHandler
> handler
,
165 // Default behavior for accepting a certificate.
166 // Note that we should not call SetMaxSecurityStyle here, because the active
167 // NavigationEntry has just been deleted (in HideInterstitialPage) and the
168 // new NavigationEntry will not be set until DidNavigate. This is ok,
169 // because the new NavigationEntry will have its max security style set
170 // within DidNavigate.
172 // While AllowCertForHost() executes synchronously on this thread,
173 // ContinueRequest() gets posted to a different thread. Calling
174 // AllowCertForHost() first ensures deterministic ordering.
175 backend_
->AllowCertForHost(handler
->ssl_info().cert
.get(),
176 handler
->request_url().host());
177 handler
->ContinueRequest();
179 // Default behavior for rejecting a certificate.
181 // While DenyCertForHost() executes synchronously on this thread,
182 // CancelRequest() gets posted to a different thread. Calling
183 // DenyCertForHost() first ensures deterministic ordering.
184 backend_
->DenyCertForHost(handler
->ssl_info().cert
.get(),
185 handler
->request_url().host());
186 handler
->CancelRequest();
190 ////////////////////////////////////////////////////////////////////////////////
191 // Certificate Error Routines
193 void SSLPolicy::OnCertErrorInternal(SSLCertErrorHandler
* handler
,
195 bool strict_enforcement
) {
196 CertificateRequestResultType result
=
197 CERTIFICATE_REQUEST_RESULT_TYPE_CONTINUE
;
198 GetContentClient()->browser()->AllowCertificateError(
199 handler
->render_process_id(),
200 handler
->render_view_id(),
201 handler
->cert_error(),
203 handler
->request_url(),
204 handler
->resource_type(),
207 base::Bind(&SSLPolicy::OnAllowCertificate
, base::Unretained(this),
208 make_scoped_refptr(handler
)),
211 case CERTIFICATE_REQUEST_RESULT_TYPE_CONTINUE
:
213 case CERTIFICATE_REQUEST_RESULT_TYPE_CANCEL
:
214 handler
->CancelRequest();
216 case CERTIFICATE_REQUEST_RESULT_TYPE_DENY
:
217 handler
->DenyRequest();
224 void SSLPolicy::InitializeEntryIfNeeded(NavigationEntryImpl
* entry
) {
225 if (entry
->GetSSL().security_style
!= SECURITY_STYLE_UNKNOWN
)
228 entry
->GetSSL().security_style
= entry
->GetURL().SchemeIsSecure() ?
229 SECURITY_STYLE_AUTHENTICATED
: SECURITY_STYLE_UNAUTHENTICATED
;
232 void SSLPolicy::OriginRanInsecureContent(const std::string
& origin
, int pid
) {
233 GURL
parsed_origin(origin
);
234 if (parsed_origin
.SchemeIsSecure())
235 backend_
->HostRanInsecureContent(parsed_origin
.host(), pid
);
238 } // namespace content