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   // An HTTPS response may not have a certificate for some reason.  When that
    114   // happens, use the unauthenticated (HTTP) rather than the authentication
    115   // broken security style so that we can detect this error condition.
    116   if (!entry->GetSSL().cert_id) {
    117     entry->GetSSL().security_style = SECURITY_STYLE_UNAUTHENTICATED;
    118     return;
    119   }
    120 
    121   if (net::IsCertStatusError(entry->GetSSL().cert_status)) {
    122     // Minor errors don't lower the security style to
    123     // SECURITY_STYLE_AUTHENTICATION_BROKEN.
    124     if (!net::IsCertStatusMinorError(entry->GetSSL().cert_status)) {
    125       entry->GetSSL().security_style =
    126           SECURITY_STYLE_AUTHENTICATION_BROKEN;
    127     }
    128     return;
    129   }
    130 
    131   SiteInstance* site_instance = entry->site_instance();
    132   // Note that |site_instance| can be NULL here because NavigationEntries don't
    133   // necessarily have site instances.  Without a process, the entry can't
    134   // possibly have insecure content.  See bug http://crbug.com/12423.
    135   if (site_instance &&
    136       backend_->DidHostRunInsecureContent(
    137           entry->GetURL().host(), site_instance->GetProcess()->GetID())) {
    138     entry->GetSSL().security_style =
    139         SECURITY_STYLE_AUTHENTICATION_BROKEN;
    140     entry->GetSSL().content_status |= SSLStatus::RAN_INSECURE_CONTENT;
    141     return;
    142   }
    143 
    144   if (web_contents->DisplayedInsecureContent())
    145     entry->GetSSL().content_status |= SSLStatus::DISPLAYED_INSECURE_CONTENT;
    146   else
    147     entry->GetSSL().content_status &= ~SSLStatus::DISPLAYED_INSECURE_CONTENT;
    148 }
    149 
    150 void SSLPolicy::OnAllowCertificate(scoped_refptr<SSLCertErrorHandler> handler,
    151                                    bool allow) {
    152   if (allow) {
    153     // Default behavior for accepting a certificate.
    154     // Note that we should not call SetMaxSecurityStyle here, because the active
    155     // NavigationEntry has just been deleted (in HideInterstitialPage) and the
    156     // new NavigationEntry will not be set until DidNavigate.  This is ok,
    157     // because the new NavigationEntry will have its max security style set
    158     // within DidNavigate.
    159     //
    160     // While AllowCertForHost() executes synchronously on this thread,
    161     // ContinueRequest() gets posted to a different thread. Calling
    162     // AllowCertForHost() first ensures deterministic ordering.
    163     backend_->AllowCertForHost(handler->ssl_info().cert.get(),
    164                                handler->request_url().host(),
    165                                handler->cert_error());
    166     handler->ContinueRequest();
    167   } else {
    168     // Default behavior for rejecting a certificate.
    169     //
    170     // While DenyCertForHost() executes synchronously on this thread,
    171     // CancelRequest() gets posted to a different thread. Calling
    172     // DenyCertForHost() first ensures deterministic ordering.
    173     backend_->DenyCertForHost(handler->ssl_info().cert.get(),
    174                               handler->request_url().host(),
    175                               handler->cert_error());
    176     handler->CancelRequest();
    177   }
    178 }
    179 
    180 ////////////////////////////////////////////////////////////////////////////////
    181 // Certificate Error Routines
    182 
    183 void SSLPolicy::OnCertErrorInternal(SSLCertErrorHandler* handler,
    184                                     bool overridable,
    185                                     bool strict_enforcement) {
    186   CertificateRequestResultType result =
    187       CERTIFICATE_REQUEST_RESULT_TYPE_CONTINUE;
    188   GetContentClient()->browser()->AllowCertificateError(
    189       handler->render_process_id(),
    190       handler->render_view_id(),
    191       handler->cert_error(),
    192       handler->ssl_info(),
    193       handler->request_url(),
    194       handler->resource_type(),
    195       overridable,
    196       strict_enforcement,
    197       base::Bind(&SSLPolicy::OnAllowCertificate, base::Unretained(this),
    198                  make_scoped_refptr(handler)),
    199       &result);
    200   switch (result) {
    201     case CERTIFICATE_REQUEST_RESULT_TYPE_CONTINUE:
    202       break;
    203     case CERTIFICATE_REQUEST_RESULT_TYPE_CANCEL:
    204       handler->CancelRequest();
    205       break;
    206     case CERTIFICATE_REQUEST_RESULT_TYPE_DENY:
    207       handler->DenyRequest();
    208       break;
    209     default:
    210       NOTREACHED();
    211   }
    212 }
    213 
    214 void SSLPolicy::InitializeEntryIfNeeded(NavigationEntryImpl* entry) {
    215   if (entry->GetSSL().security_style != SECURITY_STYLE_UNKNOWN)
    216     return;
    217 
    218   entry->GetSSL().security_style = entry->GetURL().SchemeIsSecure() ?
    219       SECURITY_STYLE_AUTHENTICATED : SECURITY_STYLE_UNAUTHENTICATED;
    220 }
    221 
    222 void SSLPolicy::OriginRanInsecureContent(const std::string& origin, int pid) {
    223   GURL parsed_origin(origin);
    224   if (parsed_origin.SchemeIsSecure())
    225     backend_->HostRanInsecureContent(parsed_origin.host(), pid);
    226 }
    227 
    228 }  // namespace content
    229