OLD | NEW |
| (Empty) |
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. | |
4 | |
5 #include "content/browser/ssl/ssl_policy.h" | |
6 | |
7 #include "base/base_switches.h" | |
8 #include "base/bind.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_error_handler.h" | |
19 #include "content/browser/web_contents/web_contents_impl.h" | |
20 #include "content/common/security_style_util.h" | |
21 #include "content/public/browser/content_browser_client.h" | |
22 #include "content/public/browser/ssl_status.h" | |
23 #include "content/public/browser/web_contents.h" | |
24 #include "content/public/common/resource_type.h" | |
25 #include "content/public/common/url_constants.h" | |
26 #include "net/ssl/ssl_info.h" | |
27 #include "url/gurl.h" | |
28 | |
29 namespace content { | |
30 | |
31 namespace { | |
32 | |
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 | |
38 }; | |
39 | |
40 void OnAllowCertificate(SSLErrorHandler* handler, | |
41 const SSLPolicy* const policy, | |
42 CertificateRequestResultType decision) { | |
43 DCHECK(handler->ssl_info().is_valid()); | |
44 switch (decision) { | |
45 case CERTIFICATE_REQUEST_RESULT_TYPE_CONTINUE: | |
46 // Note that we should not call SetMaxSecurityStyle here, because | |
47 // the active NavigationEntry has just been deleted (in | |
48 // HideInterstitialPage) and the new NavigationEntry will not be | |
49 // set until DidNavigate. This is ok, because the new | |
50 // NavigationEntry will have its max security style set within | |
51 // DidNavigate. | |
52 // | |
53 // While AllowCertForHost() executes synchronously on this thread, | |
54 // ContinueRequest() gets posted to a different thread. Calling | |
55 // AllowCertForHost() first ensures deterministic ordering. | |
56 policy->backend()->AllowCertForHost(*handler->ssl_info().cert.get(), | |
57 handler->request_url().host(), | |
58 handler->cert_error()); | |
59 handler->ContinueRequest(); | |
60 return; | |
61 case CERTIFICATE_REQUEST_RESULT_TYPE_DENY: | |
62 handler->DenyRequest(); | |
63 return; | |
64 case CERTIFICATE_REQUEST_RESULT_TYPE_CANCEL: | |
65 handler->CancelRequest(); | |
66 return; | |
67 } | |
68 } | |
69 | |
70 } // namespace | |
71 | |
72 SSLPolicy::SSLPolicy(SSLPolicyBackend* backend) | |
73 : backend_(backend) { | |
74 DCHECK(backend_); | |
75 } | |
76 | |
77 void SSLPolicy::OnCertError(std::unique_ptr<SSLErrorHandler> handler) { | |
78 bool expired_previous_decision = false; | |
79 // First we check if we know the policy for this error. | |
80 DCHECK(handler->ssl_info().is_valid()); | |
81 SSLHostStateDelegate::CertJudgment judgment = | |
82 backend_->QueryPolicy(*handler->ssl_info().cert.get(), | |
83 handler->request_url().host(), | |
84 handler->cert_error(), | |
85 &expired_previous_decision); | |
86 | |
87 if (judgment == SSLHostStateDelegate::ALLOWED) { | |
88 handler->ContinueRequest(); | |
89 return; | |
90 } | |
91 | |
92 // For all other hosts, which must be DENIED, a blocking page is shown to the | |
93 // user every time they come back to the page. | |
94 int options_mask = 0; | |
95 switch (handler->cert_error()) { | |
96 case net::ERR_CERT_COMMON_NAME_INVALID: | |
97 case net::ERR_CERT_DATE_INVALID: | |
98 case net::ERR_CERT_AUTHORITY_INVALID: | |
99 case net::ERR_CERT_WEAK_SIGNATURE_ALGORITHM: | |
100 case net::ERR_CERT_WEAK_KEY: | |
101 case net::ERR_CERT_NAME_CONSTRAINT_VIOLATION: | |
102 case net::ERR_CERT_VALIDITY_TOO_LONG: | |
103 case net::ERR_CERTIFICATE_TRANSPARENCY_REQUIRED: | |
104 if (!handler->fatal()) | |
105 options_mask |= OVERRIDABLE; | |
106 else | |
107 options_mask |= STRICT_ENFORCEMENT; | |
108 if (expired_previous_decision) | |
109 options_mask |= EXPIRED_PREVIOUS_DECISION; | |
110 OnCertErrorInternal(std::move(handler), options_mask); | |
111 break; | |
112 case net::ERR_CERT_NO_REVOCATION_MECHANISM: | |
113 // Ignore this error. | |
114 handler->ContinueRequest(); | |
115 break; | |
116 case net::ERR_CERT_UNABLE_TO_CHECK_REVOCATION: | |
117 // We ignore this error but will show a warning status in the location | |
118 // bar. | |
119 handler->ContinueRequest(); | |
120 break; | |
121 case net::ERR_CERT_CONTAINS_ERRORS: | |
122 case net::ERR_CERT_REVOKED: | |
123 case net::ERR_CERT_INVALID: | |
124 case net::ERR_SSL_WEAK_SERVER_EPHEMERAL_DH_KEY: | |
125 case net::ERR_SSL_PINNED_KEY_NOT_IN_CERT_CHAIN: | |
126 if (handler->fatal()) | |
127 options_mask |= STRICT_ENFORCEMENT; | |
128 if (expired_previous_decision) | |
129 options_mask |= EXPIRED_PREVIOUS_DECISION; | |
130 OnCertErrorInternal(std::move(handler), options_mask); | |
131 break; | |
132 default: | |
133 NOTREACHED(); | |
134 handler->CancelRequest(); | |
135 break; | |
136 } | |
137 } | |
138 | |
139 void SSLPolicy::DidRunInsecureContent(NavigationEntryImpl* entry, | |
140 const GURL& security_origin) { | |
141 if (!entry) | |
142 return; | |
143 | |
144 SiteInstance* site_instance = entry->site_instance(); | |
145 if (!site_instance) | |
146 return; | |
147 | |
148 backend_->HostRanInsecureContent(security_origin.host(), | |
149 site_instance->GetProcess()->GetID()); | |
150 } | |
151 | |
152 void SSLPolicy::DidRunContentWithCertErrors(NavigationEntryImpl* entry, | |
153 const GURL& security_origin) { | |
154 if (!entry) | |
155 return; | |
156 | |
157 SiteInstance* site_instance = entry->site_instance(); | |
158 if (!site_instance) | |
159 return; | |
160 | |
161 backend_->HostRanContentWithCertErrors(security_origin.host(), | |
162 site_instance->GetProcess()->GetID()); | |
163 } | |
164 | |
165 void SSLPolicy::OnRequestStarted(const GURL& url, | |
166 bool has_certificate, | |
167 net::CertStatus cert_status) { | |
168 if (has_certificate && url.SchemeIsCryptographic() && | |
169 !net::IsCertStatusError(cert_status)) { | |
170 // If the scheme is https: or wss: *and* the security info for the | |
171 // cert has been set (i.e. the cert id is not 0) and the cert did | |
172 // not have any errors, revoke any previous decisions that | |
173 // have occurred. If the cert info has not been set, do nothing since it | |
174 // isn't known if the connection was actually a valid connection or if it | |
175 // had a cert error. | |
176 SSLGoodCertSeenEvent event = NO_PREVIOUS_EXCEPTION; | |
177 if (backend_->HasAllowException(url.host())) { | |
178 // If there's no certificate error, a good certificate has been seen, so | |
179 // clear out any exceptions that were made by the user for bad | |
180 // certificates. This intentionally does not apply to cached resources | |
181 // (see https://crbug.com/634553 for an explanation). | |
182 backend_->RevokeUserAllowExceptions(url.host()); | |
183 event = HAD_PREVIOUS_EXCEPTION; | |
184 } | |
185 UMA_HISTOGRAM_ENUMERATION("interstitial.ssl.good_cert_seen", event, | |
186 SSL_GOOD_CERT_SEEN_EVENT_MAX); | |
187 } | |
188 } | |
189 | |
190 void SSLPolicy::UpdateEntry(NavigationEntryImpl* entry, | |
191 WebContents* web_contents) { | |
192 DCHECK(entry); | |
193 | |
194 WebContentsImpl* web_contents_impl = | |
195 static_cast<WebContentsImpl*>(web_contents); | |
196 | |
197 InitializeEntryIfNeeded(entry); | |
198 | |
199 if (entry->GetSSL().security_style == SECURITY_STYLE_UNAUTHENTICATED) | |
200 return; | |
201 | |
202 if (!web_contents_impl->DisplayedInsecureContent()) | |
203 entry->GetSSL().content_status &= ~SSLStatus::DISPLAYED_INSECURE_CONTENT; | |
204 | |
205 if (web_contents_impl->DisplayedInsecureContent()) | |
206 entry->GetSSL().content_status |= SSLStatus::DISPLAYED_INSECURE_CONTENT; | |
207 | |
208 if (!web_contents_impl->DisplayedContentWithCertErrors()) | |
209 entry->GetSSL().content_status &= | |
210 ~SSLStatus::DISPLAYED_CONTENT_WITH_CERT_ERRORS; | |
211 | |
212 if (web_contents_impl->DisplayedContentWithCertErrors()) | |
213 entry->GetSSL().content_status |= | |
214 SSLStatus::DISPLAYED_CONTENT_WITH_CERT_ERRORS; | |
215 | |
216 SiteInstance* site_instance = entry->site_instance(); | |
217 // Note that |site_instance| can be NULL here because NavigationEntries don't | |
218 // necessarily have site instances. Without a process, the entry can't | |
219 // possibly have insecure content. See bug http://crbug.com/12423. | |
220 if (site_instance && | |
221 backend_->DidHostRunInsecureContent( | |
222 entry->GetURL().host(), site_instance->GetProcess()->GetID())) { | |
223 entry->GetSSL().security_style = | |
224 SECURITY_STYLE_AUTHENTICATION_BROKEN; | |
225 entry->GetSSL().content_status |= SSLStatus::RAN_INSECURE_CONTENT; | |
226 } | |
227 | |
228 if (site_instance && | |
229 backend_->DidHostRunContentWithCertErrors( | |
230 entry->GetURL().host(), site_instance->GetProcess()->GetID())) { | |
231 entry->GetSSL().security_style = SECURITY_STYLE_AUTHENTICATION_BROKEN; | |
232 entry->GetSSL().content_status |= SSLStatus::RAN_CONTENT_WITH_CERT_ERRORS; | |
233 } | |
234 } | |
235 | |
236 //////////////////////////////////////////////////////////////////////////////// | |
237 // Certificate Error Routines | |
238 | |
239 void SSLPolicy::OnCertErrorInternal(std::unique_ptr<SSLErrorHandler> handler, | |
240 int options_mask) { | |
241 bool overridable = (options_mask & OVERRIDABLE) != 0; | |
242 bool strict_enforcement = (options_mask & STRICT_ENFORCEMENT) != 0; | |
243 bool expired_previous_decision = | |
244 (options_mask & EXPIRED_PREVIOUS_DECISION) != 0; | |
245 | |
246 WebContents* web_contents = handler->web_contents(); | |
247 int cert_error = handler->cert_error(); | |
248 const net::SSLInfo& ssl_info = handler->ssl_info(); | |
249 const GURL& request_url = handler->request_url(); | |
250 ResourceType resource_type = handler->resource_type(); | |
251 GetContentClient()->browser()->AllowCertificateError( | |
252 web_contents, cert_error, ssl_info, request_url, resource_type, | |
253 overridable, strict_enforcement, expired_previous_decision, | |
254 base::Bind(&OnAllowCertificate, base::Owned(handler.release()), this)); | |
255 } | |
256 | |
257 void SSLPolicy::InitializeEntryIfNeeded(NavigationEntryImpl* entry) { | |
258 if (entry->GetSSL().security_style != SECURITY_STYLE_UNKNOWN) | |
259 return; | |
260 | |
261 entry->GetSSL().security_style = GetSecurityStyleForResource( | |
262 entry->GetURL(), !!entry->GetSSL().certificate, | |
263 entry->GetSSL().cert_status); | |
264 } | |
265 | |
266 } // namespace content | |
OLD | NEW |