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