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