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/strings/string_piece.h" 12 #include "base/strings/string_util.h" 13 #include "content/browser/frame_host/navigation_entry_impl.h" 14 #include "content/browser/renderer_host/render_process_host_impl.h" 15 #include "content/browser/renderer_host/render_view_host_impl.h" 16 #include "content/browser/site_instance_impl.h" 17 #include "content/browser/ssl/ssl_cert_error_handler.h" 18 #include "content/browser/ssl/ssl_request_info.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/resource_type.h" 22 #include "content/public/common/ssl_status.h" 23 #include "content/public/common/url_constants.h" 24 #include "net/ssl/ssl_info.h" 25 26 27 namespace content { 28 29 SSLPolicy::SSLPolicy(SSLPolicyBackend* backend) 30 : backend_(backend) { 31 DCHECK(backend_); 32 } 33 34 void SSLPolicy::OnCertError(SSLCertErrorHandler* handler) { 35 bool expired_previous_decision; 36 // First we check if we know the policy for this error. 37 DCHECK(handler->ssl_info().is_valid()); 38 SSLHostStateDelegate::CertJudgment judgment = 39 backend_->QueryPolicy(*handler->ssl_info().cert.get(), 40 handler->request_url().host(), 41 handler->cert_error(), 42 &expired_previous_decision); 43 44 if (judgment == SSLHostStateDelegate::ALLOWED) { 45 handler->ContinueRequest(); 46 return; 47 } 48 49 // For all other hosts, which must be DENIED, a blocking page is shown to the 50 // user every time they come back to the page. 51 int options_mask = 0; 52 switch (handler->cert_error()) { 53 case net::ERR_CERT_COMMON_NAME_INVALID: 54 case net::ERR_CERT_DATE_INVALID: 55 case net::ERR_CERT_AUTHORITY_INVALID: 56 case net::ERR_CERT_WEAK_SIGNATURE_ALGORITHM: 57 case net::ERR_CERT_WEAK_KEY: 58 case net::ERR_CERT_NAME_CONSTRAINT_VIOLATION: 59 if (!handler->fatal()) 60 options_mask |= OVERRIDABLE; 61 else 62 options_mask |= STRICT_ENFORCEMENT; 63 if (expired_previous_decision) 64 options_mask |= EXPIRED_PREVIOUS_DECISION; 65 OnCertErrorInternal(handler, options_mask); 66 break; 67 case net::ERR_CERT_NO_REVOCATION_MECHANISM: 68 // Ignore this error. 69 handler->ContinueRequest(); 70 break; 71 case net::ERR_CERT_UNABLE_TO_CHECK_REVOCATION: 72 // We ignore this error but will show a warning status in the location 73 // bar. 74 handler->ContinueRequest(); 75 break; 76 case net::ERR_CERT_CONTAINS_ERRORS: 77 case net::ERR_CERT_REVOKED: 78 case net::ERR_CERT_INVALID: 79 case net::ERR_SSL_WEAK_SERVER_EPHEMERAL_DH_KEY: 80 case net::ERR_SSL_PINNED_KEY_NOT_IN_CERT_CHAIN: 81 if (handler->fatal()) 82 options_mask |= STRICT_ENFORCEMENT; 83 if (expired_previous_decision) 84 options_mask |= EXPIRED_PREVIOUS_DECISION; 85 OnCertErrorInternal(handler, options_mask); 86 break; 87 default: 88 NOTREACHED(); 89 handler->CancelRequest(); 90 break; 91 } 92 } 93 94 void SSLPolicy::DidRunInsecureContent(NavigationEntryImpl* entry, 95 const std::string& security_origin) { 96 if (!entry) 97 return; 98 99 SiteInstance* site_instance = entry->site_instance(); 100 if (!site_instance) 101 return; 102 103 backend_->HostRanInsecureContent(GURL(security_origin).host(), 104 site_instance->GetProcess()->GetID()); 105 } 106 107 void SSLPolicy::OnRequestStarted(SSLRequestInfo* info) { 108 // TODO(abarth): This mechanism is wrong. What we should be doing is sending 109 // this information back through WebKit and out some FrameLoaderClient 110 // methods. 111 112 if (net::IsCertStatusError(info->ssl_cert_status())) 113 backend_->HostRanInsecureContent(info->url().host(), info->child_id()); 114 } 115 116 void SSLPolicy::UpdateEntry(NavigationEntryImpl* entry, 117 WebContentsImpl* web_contents) { 118 DCHECK(entry); 119 120 InitializeEntryIfNeeded(entry); 121 122 if (!entry->GetURL().SchemeIsSecure()) 123 return; 124 125 if (!web_contents->DisplayedInsecureContent()) 126 entry->GetSSL().content_status &= ~SSLStatus::DISPLAYED_INSECURE_CONTENT; 127 128 // An HTTPS response may not have a certificate for some reason. When that 129 // happens, use the unauthenticated (HTTP) rather than the authentication 130 // broken security style so that we can detect this error condition. 131 if (!entry->GetSSL().cert_id) { 132 entry->GetSSL().security_style = SECURITY_STYLE_UNAUTHENTICATED; 133 return; 134 } 135 136 if (web_contents->DisplayedInsecureContent()) 137 entry->GetSSL().content_status |= SSLStatus::DISPLAYED_INSECURE_CONTENT; 138 139 if (net::IsCertStatusError(entry->GetSSL().cert_status)) { 140 // Minor errors don't lower the security style to 141 // SECURITY_STYLE_AUTHENTICATION_BROKEN. 142 if (!net::IsCertStatusMinorError(entry->GetSSL().cert_status)) { 143 entry->GetSSL().security_style = 144 SECURITY_STYLE_AUTHENTICATION_BROKEN; 145 } 146 return; 147 } 148 149 SiteInstance* site_instance = entry->site_instance(); 150 // Note that |site_instance| can be NULL here because NavigationEntries don't 151 // necessarily have site instances. Without a process, the entry can't 152 // possibly have insecure content. See bug http://crbug.com/12423. 153 if (site_instance && 154 backend_->DidHostRunInsecureContent( 155 entry->GetURL().host(), site_instance->GetProcess()->GetID())) { 156 entry->GetSSL().security_style = 157 SECURITY_STYLE_AUTHENTICATION_BROKEN; 158 entry->GetSSL().content_status |= SSLStatus::RAN_INSECURE_CONTENT; 159 return; 160 } 161 } 162 163 void SSLPolicy::OnAllowCertificate(scoped_refptr<SSLCertErrorHandler> handler, 164 bool allow) { 165 DCHECK(handler->ssl_info().is_valid()); 166 if (allow) { 167 // Default behavior for accepting a certificate. 168 // Note that we should not call SetMaxSecurityStyle here, because the active 169 // NavigationEntry has just been deleted (in HideInterstitialPage) and the 170 // new NavigationEntry will not be set until DidNavigate. This is ok, 171 // because the new NavigationEntry will have its max security style set 172 // within DidNavigate. 173 // 174 // While AllowCertForHost() executes synchronously on this thread, 175 // ContinueRequest() gets posted to a different thread. Calling 176 // AllowCertForHost() first ensures deterministic ordering. 177 backend_->AllowCertForHost(*handler->ssl_info().cert.get(), 178 handler->request_url().host(), 179 handler->cert_error()); 180 handler->ContinueRequest(); 181 } else { 182 // Default behavior for rejecting a certificate. 183 handler->CancelRequest(); 184 } 185 } 186 187 //////////////////////////////////////////////////////////////////////////////// 188 // Certificate Error Routines 189 190 void SSLPolicy::OnCertErrorInternal(SSLCertErrorHandler* handler, 191 int options_mask) { 192 bool overridable = (options_mask & OVERRIDABLE) != 0; 193 bool strict_enforcement = (options_mask & STRICT_ENFORCEMENT) != 0; 194 bool expired_previous_decision = 195 (options_mask & EXPIRED_PREVIOUS_DECISION) != 0; 196 CertificateRequestResultType result = 197 CERTIFICATE_REQUEST_RESULT_TYPE_CONTINUE; 198 GetContentClient()->browser()->AllowCertificateError( 199 handler->render_process_id(), 200 handler->render_frame_id(), 201 handler->cert_error(), 202 handler->ssl_info(), 203 handler->request_url(), 204 handler->resource_type(), 205 overridable, 206 strict_enforcement, 207 expired_previous_decision, 208 base::Bind(&SSLPolicy::OnAllowCertificate, 209 base::Unretained(this), 210 make_scoped_refptr(handler)), 211 &result); 212 switch (result) { 213 case CERTIFICATE_REQUEST_RESULT_TYPE_CONTINUE: 214 break; 215 case CERTIFICATE_REQUEST_RESULT_TYPE_CANCEL: 216 handler->CancelRequest(); 217 break; 218 case CERTIFICATE_REQUEST_RESULT_TYPE_DENY: 219 handler->DenyRequest(); 220 break; 221 default: 222 NOTREACHED(); 223 } 224 } 225 226 void SSLPolicy::InitializeEntryIfNeeded(NavigationEntryImpl* entry) { 227 if (entry->GetSSL().security_style != SECURITY_STYLE_UNKNOWN) 228 return; 229 230 entry->GetSSL().security_style = entry->GetURL().SchemeIsSecure() ? 231 SECURITY_STYLE_AUTHENTICATED : SECURITY_STYLE_UNAUTHENTICATED; 232 } 233 234 void SSLPolicy::OriginRanInsecureContent(const std::string& origin, int pid) { 235 GURL parsed_origin(origin); 236 if (parsed_origin.SchemeIsSecure()) 237 backend_->HostRanInsecureContent(parsed_origin.host(), pid); 238 } 239 240 } // namespace content 241