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
27namespace content {
28
29SSLPolicy::SSLPolicy(SSLPolicyBackend* backend)
30    : backend_(backend) {
31  DCHECK(backend_);
32}
33
34void 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
94void 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
107void 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
116void 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
163void 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
190void 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
226void 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
234void 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