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