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) { |
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 |