OLD | NEW |
---|---|
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 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 | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 #include "content/browser/ssl/ssl_manager.h" | 5 #include "content/browser/ssl/ssl_manager.h" |
6 | 6 |
7 #include <set> | 7 #include <set> |
8 | 8 |
9 #include "base/bind.h" | 9 #include "base/bind.h" |
10 #include "base/macros.h" | 10 #include "base/macros.h" |
11 #include "base/metrics/histogram_macros.h" | |
11 #include "base/strings/utf_string_conversions.h" | 12 #include "base/strings/utf_string_conversions.h" |
12 #include "base/supports_user_data.h" | 13 #include "base/supports_user_data.h" |
13 #include "content/browser/frame_host/navigation_entry_impl.h" | 14 #include "content/browser/frame_host/navigation_entry_impl.h" |
14 #include "content/browser/loader/resource_dispatcher_host_impl.h" | 15 #include "content/browser/loader/resource_dispatcher_host_impl.h" |
15 #include "content/browser/loader/resource_request_info_impl.h" | 16 #include "content/browser/loader/resource_request_info_impl.h" |
16 #include "content/browser/ssl/ssl_error_handler.h" | 17 #include "content/browser/ssl/ssl_error_handler.h" |
17 #include "content/browser/ssl/ssl_policy.h" | |
18 #include "content/browser/web_contents/web_contents_impl.h" | 18 #include "content/browser/web_contents/web_contents_impl.h" |
19 #include "content/common/security_style_util.h" | |
19 #include "content/public/browser/browser_context.h" | 20 #include "content/public/browser/browser_context.h" |
20 #include "content/public/browser/browser_thread.h" | 21 #include "content/public/browser/browser_thread.h" |
22 #include "content/public/browser/certificate_request_result_type.h" | |
23 #include "content/public/browser/content_browser_client.h" | |
21 #include "content/public/browser/navigation_details.h" | 24 #include "content/public/browser/navigation_details.h" |
22 #include "content/public/browser/resource_request_details.h" | 25 #include "content/public/browser/ssl_host_state_delegate.h" |
23 #include "content/public/browser/ssl_status.h" | 26 #include "content/public/browser/ssl_status.h" |
24 #include "net/url_request/url_request.h" | 27 #include "net/url_request/url_request.h" |
25 | 28 |
26 namespace content { | 29 namespace content { |
27 | 30 |
28 namespace { | 31 namespace { |
29 | 32 |
30 const char kSSLManagerKeyName[] = "content_ssl_manager"; | 33 const char kSSLManagerKeyName[] = "content_ssl_manager"; |
31 | 34 |
35 // Events for UMA. Do not reorder or change! | |
36 enum SSLGoodCertSeenEvent { | |
37 NO_PREVIOUS_EXCEPTION = 0, | |
38 HAD_PREVIOUS_EXCEPTION = 1, | |
39 SSL_GOOD_CERT_SEEN_EVENT_MAX = 2 | |
40 }; | |
41 | |
42 void OnAllowCertificate(SSLErrorHandler* handler, | |
43 SSLHostStateDelegate* state_delegate, | |
44 CertificateRequestResultType decision) { | |
45 DCHECK(handler->ssl_info().is_valid()); | |
46 switch (decision) { | |
47 case CERTIFICATE_REQUEST_RESULT_TYPE_CONTINUE: | |
48 // Note that we should not call SetMaxSecurityStyle here, because | |
49 // the active NavigationEntry has just been deleted (in | |
50 // HideInterstitialPage) and the new NavigationEntry will not be | |
51 // set until DidNavigate. This is ok, because the new | |
52 // NavigationEntry will have its max security style set within | |
53 // DidNavigate. | |
54 // | |
55 // While AllowCert() executes synchronously on this thread, | |
56 // ContinueRequest() gets posted to a different thread. Calling | |
57 // AllowCert() first ensures deterministic ordering. | |
58 if (state_delegate) { | |
59 state_delegate->AllowCert(handler->request_url().host(), | |
60 *handler->ssl_info().cert.get(), | |
61 handler->cert_error()); | |
62 } | |
63 handler->ContinueRequest(); | |
64 return; | |
65 case CERTIFICATE_REQUEST_RESULT_TYPE_DENY: | |
66 handler->DenyRequest(); | |
67 return; | |
68 case CERTIFICATE_REQUEST_RESULT_TYPE_CANCEL: | |
69 handler->CancelRequest(); | |
70 return; | |
71 } | |
72 } | |
73 | |
32 class SSLManagerSet : public base::SupportsUserData::Data { | 74 class SSLManagerSet : public base::SupportsUserData::Data { |
33 public: | 75 public: |
34 SSLManagerSet() { | 76 SSLManagerSet() { |
35 } | 77 } |
36 | 78 |
37 std::set<SSLManager*>& get() { return set_; } | 79 std::set<SSLManager*>& get() { return set_; } |
38 | 80 |
39 private: | 81 private: |
40 std::set<SSLManager*> set_; | 82 std::set<SSLManager*> set_; |
41 | 83 |
(...skipping 19 matching lines...) Expand all Loading... | |
61 handler->ContinueRequest(); | 103 handler->ContinueRequest(); |
62 } else { | 104 } else { |
63 handler->CancelRequest(); | 105 handler->CancelRequest(); |
64 } | 106 } |
65 return; | 107 return; |
66 } | 108 } |
67 | 109 |
68 SSLManager* manager = | 110 SSLManager* manager = |
69 static_cast<NavigationControllerImpl*>(&web_contents->GetController()) | 111 static_cast<NavigationControllerImpl*>(&web_contents->GetController()) |
70 ->ssl_manager(); | 112 ->ssl_manager(); |
71 manager->policy()->OnCertError(std::move(handler)); | 113 manager->OnCertError(std::move(handler)); |
72 } | 114 } |
73 | 115 |
74 } // namespace | 116 } // namespace |
75 | 117 |
76 // static | 118 // static |
77 void SSLManager::OnSSLCertificateError( | 119 void SSLManager::OnSSLCertificateError( |
78 const base::WeakPtr<SSLErrorHandler::Delegate>& delegate, | 120 const base::WeakPtr<SSLErrorHandler::Delegate>& delegate, |
79 const ResourceType resource_type, | 121 const ResourceType resource_type, |
80 const GURL& url, | 122 const GURL& url, |
81 const base::Callback<WebContents*(void)>& web_contents_getter, | 123 const base::Callback<WebContents*(void)>& web_contents_getter, |
(...skipping 33 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
115 SSLManagerSet* managers = static_cast<SSLManagerSet*>( | 157 SSLManagerSet* managers = static_cast<SSLManagerSet*>( |
116 context->GetUserData(kSSLManagerKeyName)); | 158 context->GetUserData(kSSLManagerKeyName)); |
117 | 159 |
118 for (std::set<SSLManager*>::iterator i = managers->get().begin(); | 160 for (std::set<SSLManager*>::iterator i = managers->get().begin(); |
119 i != managers->get().end(); ++i) { | 161 i != managers->get().end(); ++i) { |
120 (*i)->UpdateEntry((*i)->controller()->GetLastCommittedEntry()); | 162 (*i)->UpdateEntry((*i)->controller()->GetLastCommittedEntry()); |
121 } | 163 } |
122 } | 164 } |
123 | 165 |
124 SSLManager::SSLManager(NavigationControllerImpl* controller) | 166 SSLManager::SSLManager(NavigationControllerImpl* controller) |
125 : backend_(controller), | 167 : controller_(controller), |
126 policy_(new SSLPolicy(&backend_)), | 168 ssl_host_state_delegate_( |
127 controller_(controller) { | 169 controller->GetBrowserContext()->GetSSLHostStateDelegate()) { |
128 DCHECK(controller_); | 170 DCHECK(controller_); |
129 | 171 |
130 SSLManagerSet* managers = static_cast<SSLManagerSet*>( | 172 SSLManagerSet* managers = static_cast<SSLManagerSet*>( |
131 controller_->GetBrowserContext()->GetUserData(kSSLManagerKeyName)); | 173 controller_->GetBrowserContext()->GetUserData(kSSLManagerKeyName)); |
132 if (!managers) { | 174 if (!managers) { |
133 managers = new SSLManagerSet; | 175 managers = new SSLManagerSet; |
134 controller_->GetBrowserContext()->SetUserData(kSSLManagerKeyName, managers); | 176 controller_->GetBrowserContext()->SetUserData(kSSLManagerKeyName, managers); |
135 } | 177 } |
136 managers->get().insert(this); | 178 managers->get().insert(this); |
137 } | 179 } |
138 | 180 |
139 SSLManager::~SSLManager() { | 181 SSLManager::~SSLManager() { |
140 SSLManagerSet* managers = static_cast<SSLManagerSet*>( | 182 SSLManagerSet* managers = static_cast<SSLManagerSet*>( |
141 controller_->GetBrowserContext()->GetUserData(kSSLManagerKeyName)); | 183 controller_->GetBrowserContext()->GetUserData(kSSLManagerKeyName)); |
142 managers->get().erase(this); | 184 managers->get().erase(this); |
143 } | 185 } |
144 | 186 |
145 void SSLManager::DidCommitProvisionalLoad(const LoadCommittedDetails& details) { | 187 void SSLManager::DidCommitProvisionalLoad(const LoadCommittedDetails& details) { |
146 NavigationEntryImpl* entry = controller_->GetLastCommittedEntry(); | 188 NavigationEntryImpl* entry = controller_->GetLastCommittedEntry(); |
147 policy()->UpdateEntry(entry, controller_->delegate()->GetWebContents()); | 189 UpdateEntry(entry); |
148 // Always notify the WebContents that the SSL state changed when a | 190 // Always notify the WebContents that the SSL state changed when a |
149 // load is committed, in case the active navigation entry has changed. | 191 // load is committed, in case the active navigation entry has changed. |
150 NotifyDidChangeVisibleSSLState(); | 192 NotifyDidChangeVisibleSSLState(); |
151 } | 193 } |
152 | 194 |
153 void SSLManager::DidRunInsecureContent(const GURL& security_origin) { | 195 void SSLManager::DidRunInsecureContent(const GURL& security_origin) { |
154 NavigationEntryImpl* navigation_entry = controller_->GetLastCommittedEntry(); | 196 NavigationEntryImpl* entry = controller_->GetLastCommittedEntry(); |
155 policy()->DidRunInsecureContent(navigation_entry, security_origin); | 197 if (!entry) |
156 UpdateEntry(navigation_entry); | 198 return; |
199 | |
200 SiteInstance* site_instance = entry->site_instance(); | |
201 if (!site_instance) | |
202 return; | |
203 | |
204 if (ssl_host_state_delegate_) { | |
205 ssl_host_state_delegate_->HostRanInsecureContent( | |
206 security_origin.host(), site_instance->GetProcess()->GetID(), | |
207 SSLHostStateDelegate::MIXED_CONTENT); | |
208 } | |
209 UpdateEntry(entry); | |
210 NotifySSLInternalStateChanged(controller_->GetBrowserContext()); | |
157 } | 211 } |
158 | 212 |
159 void SSLManager::DidRunContentWithCertErrors(const GURL& security_origin) { | 213 void SSLManager::DidRunContentWithCertErrors(const GURL& security_origin) { |
160 NavigationEntryImpl* navigation_entry = controller_->GetLastCommittedEntry(); | 214 NavigationEntryImpl* entry = controller_->GetLastCommittedEntry(); |
161 policy()->DidRunContentWithCertErrors(navigation_entry, security_origin); | 215 if (!entry) |
162 UpdateEntry(navigation_entry); | 216 return; |
217 | |
218 SiteInstance* site_instance = entry->site_instance(); | |
219 if (!site_instance) | |
220 return; | |
221 | |
222 if (ssl_host_state_delegate_) { | |
223 ssl_host_state_delegate_->HostRanInsecureContent( | |
224 security_origin.host(), site_instance->GetProcess()->GetID(), | |
225 SSLHostStateDelegate::CERT_ERRORS_CONTENT); | |
226 } | |
227 UpdateEntry(entry); | |
228 NotifySSLInternalStateChanged(controller_->GetBrowserContext()); | |
163 } | 229 } |
164 | 230 |
165 void SSLManager::DidStartResourceResponse( | 231 void SSLManager::OnCertError(std::unique_ptr<SSLErrorHandler> handler) { |
166 const ResourceRequestDetails& details) { | 232 bool expired_previous_decision = false; |
167 // Notify our policy that we started a resource request. Ideally, the | 233 // First we check if we know the policy for this error. |
168 // policy should have the ability to cancel the request, but we can't do | 234 DCHECK(handler->ssl_info().is_valid()); |
169 // that yet. | 235 SSLHostStateDelegate::CertJudgment judgment = |
170 policy()->OnRequestStarted(details.url, details.has_certificate, | 236 ssl_host_state_delegate_ |
171 details.ssl_cert_status); | 237 ? ssl_host_state_delegate_->QueryPolicy( |
238 handler->request_url().host(), *handler->ssl_info().cert.get(), | |
239 handler->cert_error(), &expired_previous_decision) | |
240 : SSLHostStateDelegate::DENIED; | |
241 | |
242 if (judgment == SSLHostStateDelegate::ALLOWED) { | |
243 handler->ContinueRequest(); | |
244 return; | |
245 } | |
246 | |
247 // For all other hosts, which must be DENIED, a blocking page is shown to the | |
248 // user every time they come back to the page. | |
249 int options_mask = 0; | |
250 switch (handler->cert_error()) { | |
251 case net::ERR_CERT_COMMON_NAME_INVALID: | |
252 case net::ERR_CERT_DATE_INVALID: | |
253 case net::ERR_CERT_AUTHORITY_INVALID: | |
254 case net::ERR_CERT_WEAK_SIGNATURE_ALGORITHM: | |
255 case net::ERR_CERT_WEAK_KEY: | |
256 case net::ERR_CERT_NAME_CONSTRAINT_VIOLATION: | |
257 case net::ERR_CERT_VALIDITY_TOO_LONG: | |
258 case net::ERR_CERTIFICATE_TRANSPARENCY_REQUIRED: | |
259 if (!handler->fatal()) | |
260 options_mask |= OVERRIDABLE; | |
261 else | |
262 options_mask |= STRICT_ENFORCEMENT; | |
263 if (expired_previous_decision) | |
264 options_mask |= EXPIRED_PREVIOUS_DECISION; | |
265 OnCertErrorInternal(std::move(handler), options_mask); | |
266 break; | |
267 case net::ERR_CERT_NO_REVOCATION_MECHANISM: | |
268 // Ignore this error. | |
269 handler->ContinueRequest(); | |
270 break; | |
271 case net::ERR_CERT_UNABLE_TO_CHECK_REVOCATION: | |
272 // We ignore this error but will show a warning status in the location | |
273 // bar. | |
274 handler->ContinueRequest(); | |
275 break; | |
276 case net::ERR_CERT_CONTAINS_ERRORS: | |
277 case net::ERR_CERT_REVOKED: | |
278 case net::ERR_CERT_INVALID: | |
279 case net::ERR_SSL_WEAK_SERVER_EPHEMERAL_DH_KEY: | |
280 case net::ERR_SSL_PINNED_KEY_NOT_IN_CERT_CHAIN: | |
281 if (handler->fatal()) | |
282 options_mask |= STRICT_ENFORCEMENT; | |
283 if (expired_previous_decision) | |
284 options_mask |= EXPIRED_PREVIOUS_DECISION; | |
285 OnCertErrorInternal(std::move(handler), options_mask); | |
286 break; | |
287 default: | |
288 NOTREACHED(); | |
289 handler->CancelRequest(); | |
290 break; | |
291 } | |
172 } | 292 } |
173 | 293 |
174 void SSLManager::DidReceiveResourceRedirect( | 294 void SSLManager::DidStartResourceResponse(const GURL& url, |
175 const ResourceRedirectDetails& details) { | 295 bool has_certificate, |
176 // TODO(abarth): Make sure our redirect behavior is correct. If we ever see a | 296 net::CertStatus ssl_cert_status) { |
estark
2016/10/05 00:27:57
I added a browser test in ssl_browser_tests.cc to
| |
177 // non-HTTPS resource in the redirect chain, we want to trigger | 297 if (has_certificate && url.SchemeIsCryptographic() && |
178 // insecure content, even if the redirect chain goes back to | 298 !net::IsCertStatusError(ssl_cert_status)) { |
179 // HTTPS. This is because the network attacker can redirect the | 299 // If the scheme is https: or wss: *and* the security info for the |
180 // HTTP request to https://attacker.com/payload.js. | 300 // cert has been set (i.e. the cert id is not 0) and the cert did |
301 // not have any errors, revoke any previous decisions that | |
302 // have occurred. If the cert info has not been set, do nothing since it | |
303 // isn't known if the connection was actually a valid connection or if it | |
304 // had a cert error. | |
305 SSLGoodCertSeenEvent event = NO_PREVIOUS_EXCEPTION; | |
306 if (ssl_host_state_delegate_ && | |
307 ssl_host_state_delegate_->HasAllowException(url.host())) { | |
308 // If there's no certificate error, a good certificate has been seen, so | |
309 // clear out any exceptions that were made by the user for bad | |
310 // certificates. This intentionally does not apply to cached resources | |
311 // (see https://crbug.com/634553 for an explanation). | |
312 ssl_host_state_delegate_->RevokeUserAllowExceptions(url.host()); | |
313 event = HAD_PREVIOUS_EXCEPTION; | |
314 } | |
315 UMA_HISTOGRAM_ENUMERATION("interstitial.ssl.good_cert_seen", event, | |
316 SSL_GOOD_CERT_SEEN_EVENT_MAX); | |
317 } | |
318 } | |
319 | |
320 void SSLManager::OnCertErrorInternal(std::unique_ptr<SSLErrorHandler> handler, | |
321 int options_mask) { | |
322 bool overridable = (options_mask & OVERRIDABLE) != 0; | |
323 bool strict_enforcement = (options_mask & STRICT_ENFORCEMENT) != 0; | |
324 bool expired_previous_decision = | |
325 (options_mask & EXPIRED_PREVIOUS_DECISION) != 0; | |
326 | |
327 WebContents* web_contents = handler->web_contents(); | |
328 int cert_error = handler->cert_error(); | |
329 const net::SSLInfo& ssl_info = handler->ssl_info(); | |
330 const GURL& request_url = handler->request_url(); | |
331 ResourceType resource_type = handler->resource_type(); | |
332 GetContentClient()->browser()->AllowCertificateError( | |
333 web_contents, cert_error, ssl_info, request_url, resource_type, | |
334 overridable, strict_enforcement, expired_previous_decision, | |
335 base::Bind(&OnAllowCertificate, base::Owned(handler.release()), | |
336 ssl_host_state_delegate_)); | |
181 } | 337 } |
182 | 338 |
183 void SSLManager::UpdateEntry(NavigationEntryImpl* entry) { | 339 void SSLManager::UpdateEntry(NavigationEntryImpl* entry) { |
184 // We don't always have a navigation entry to update, for example in the | 340 // We don't always have a navigation entry to update, for example in the |
185 // case of the Web Inspector. | 341 // case of the Web Inspector. |
186 if (!entry) | 342 if (!entry) |
187 return; | 343 return; |
188 | 344 |
189 SSLStatus original_ssl_status = entry->GetSSL(); // Copy! | 345 SSLStatus original_ssl_status = entry->GetSSL(); // Copy! |
190 | 346 |
191 policy()->UpdateEntry(entry, controller_->delegate()->GetWebContents()); | 347 // Initialize the entry with an initial SecurityStyle if needed. |
348 if (entry->GetSSL().security_style == SECURITY_STYLE_UNKNOWN) { | |
349 entry->GetSSL().security_style = GetSecurityStyleForResource( | |
350 entry->GetURL(), !!entry->GetSSL().certificate, | |
351 entry->GetSSL().cert_status); | |
352 } | |
353 | |
354 WebContentsImpl* web_contents_impl = | |
355 static_cast<WebContentsImpl*>(controller_->delegate()->GetWebContents()); | |
356 if (entry->GetSSL().security_style == SECURITY_STYLE_UNAUTHENTICATED) | |
357 return; | |
358 | |
359 // Update the entry's flags for insecure content. | |
360 if (!web_contents_impl->DisplayedInsecureContent()) | |
361 entry->GetSSL().content_status &= ~SSLStatus::DISPLAYED_INSECURE_CONTENT; | |
362 if (web_contents_impl->DisplayedInsecureContent()) | |
363 entry->GetSSL().content_status |= SSLStatus::DISPLAYED_INSECURE_CONTENT; | |
364 if (!web_contents_impl->DisplayedContentWithCertErrors()) | |
365 entry->GetSSL().content_status &= | |
366 ~SSLStatus::DISPLAYED_CONTENT_WITH_CERT_ERRORS; | |
367 if (web_contents_impl->DisplayedContentWithCertErrors()) | |
368 entry->GetSSL().content_status |= | |
369 SSLStatus::DISPLAYED_CONTENT_WITH_CERT_ERRORS; | |
370 | |
371 SiteInstance* site_instance = entry->site_instance(); | |
372 // Note that |site_instance| can be NULL here because NavigationEntries don't | |
373 // necessarily have site instances. Without a process, the entry can't | |
374 // possibly have insecure content. See bug http://crbug.com/12423. | |
375 if (site_instance && ssl_host_state_delegate_ && | |
376 ssl_host_state_delegate_->DidHostRunInsecureContent( | |
377 entry->GetURL().host(), site_instance->GetProcess()->GetID(), | |
378 SSLHostStateDelegate::MIXED_CONTENT)) { | |
379 entry->GetSSL().security_style = SECURITY_STYLE_AUTHENTICATION_BROKEN; | |
380 entry->GetSSL().content_status |= SSLStatus::RAN_INSECURE_CONTENT; | |
381 } | |
382 | |
383 if (site_instance && ssl_host_state_delegate_ && | |
384 ssl_host_state_delegate_->DidHostRunInsecureContent( | |
385 entry->GetURL().host(), site_instance->GetProcess()->GetID(), | |
386 SSLHostStateDelegate::CERT_ERRORS_CONTENT)) { | |
387 entry->GetSSL().security_style = SECURITY_STYLE_AUTHENTICATION_BROKEN; | |
388 entry->GetSSL().content_status |= SSLStatus::RAN_CONTENT_WITH_CERT_ERRORS; | |
389 } | |
192 | 390 |
193 if (!entry->GetSSL().Equals(original_ssl_status)) | 391 if (!entry->GetSSL().Equals(original_ssl_status)) |
194 NotifyDidChangeVisibleSSLState(); | 392 NotifyDidChangeVisibleSSLState(); |
195 } | 393 } |
196 | 394 |
197 void SSLManager::NotifyDidChangeVisibleSSLState() { | 395 void SSLManager::NotifyDidChangeVisibleSSLState() { |
198 WebContentsImpl* contents = | 396 WebContentsImpl* contents = |
199 static_cast<WebContentsImpl*>(controller_->delegate()->GetWebContents()); | 397 static_cast<WebContentsImpl*>(controller_->delegate()->GetWebContents()); |
200 contents->DidChangeVisibleSSLState(); | 398 contents->DidChangeVisibleSSLState(); |
201 } | 399 } |
202 | 400 |
203 } // namespace content | 401 } // namespace content |
OLD | NEW |