blob: bd4a401fdca89c26bbe2f083fe327182f1a359a4 [file] [log] [blame]
// Copyright 2015 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "content/browser/devtools/protocol/security_handler.h"
#include <memory>
#include <string>
#include <utility>
#include <vector>
#include "base/base64.h"
#include "content/browser/devtools/devtools_session.h"
#include "content/browser/frame_host/render_frame_host_impl.h"
#include "content/public/browser/navigation_controller.h"
#include "content/public/browser/navigation_entry.h"
#include "content/public/browser/security_style_explanations.h"
#include "content/public/browser/ssl_status.h"
#include "content/public/browser/web_contents.h"
#include "content/public/browser/web_contents_delegate.h"
#include "net/cert/x509_certificate.h"
#include "net/cert/x509_util.h"
#include "third_party/WebKit/public/platform/WebMixedContentContextType.h"
namespace content {
namespace protocol {
using Explanations = protocol::Array<Security::SecurityStateExplanation>;
namespace {
std::string SecurityStyleToProtocolSecurityState(
blink::WebSecurityStyle security_style) {
switch (security_style) {
case blink::kWebSecurityStyleUnknown:
return Security::SecurityStateEnum::Unknown;
case blink::kWebSecurityStyleNeutral:
return Security::SecurityStateEnum::Neutral;
case blink::kWebSecurityStyleInsecure:
return Security::SecurityStateEnum::Insecure;
case blink::kWebSecurityStyleSecure:
return Security::SecurityStateEnum::Secure;
default:
NOTREACHED();
return Security::SecurityStateEnum::Unknown;
}
}
std::string MixedContentTypeToProtocolMixedContentType(
blink::WebMixedContentContextType mixed_content_type) {
switch (mixed_content_type) {
case blink::WebMixedContentContextType::kNotMixedContent:
return Security::MixedContentTypeEnum::None;
case blink::WebMixedContentContextType::kBlockable:
return Security::MixedContentTypeEnum::Blockable;
case blink::WebMixedContentContextType::kOptionallyBlockable:
return Security::MixedContentTypeEnum::OptionallyBlockable;
case blink::WebMixedContentContextType::kShouldBeBlockable:
// kShouldBeBlockable is not used for explanations.
NOTREACHED();
return Security::MixedContentTypeEnum::OptionallyBlockable;
default:
NOTREACHED();
return Security::MixedContentTypeEnum::None;
}
}
void AddExplanations(
const std::string& security_style,
const std::vector<SecurityStyleExplanation>& explanations_to_add,
Explanations* explanations) {
for (const auto& it : explanations_to_add) {
std::unique_ptr<protocol::Array<String>> certificate =
protocol::Array<String>::create();
if (it.certificate) {
std::string encoded;
base::Base64Encode(net::x509_util::CryptoBufferAsStringPiece(
it.certificate->cert_buffer()),
&encoded);
certificate->addItem(encoded);
for (const auto& cert : it.certificate->intermediate_buffers()) {
base::Base64Encode(
net::x509_util::CryptoBufferAsStringPiece(cert.get()), &encoded);
certificate->addItem(encoded);
}
}
explanations->addItem(
Security::SecurityStateExplanation::Create()
.SetSecurityState(security_style)
.SetTitle(it.title)
.SetSummary(it.summary)
.SetDescription(it.description)
.SetCertificate(std::move(certificate))
.SetMixedContentType(MixedContentTypeToProtocolMixedContentType(
it.mixed_content_type))
.Build());
}
}
} // namespace
// static
std::vector<SecurityHandler*> SecurityHandler::ForAgentHost(
DevToolsAgentHostImpl* host) {
return DevToolsSession::HandlersForAgentHost<SecurityHandler>(
host, Security::Metainfo::domainName);
}
SecurityHandler::SecurityHandler()
: DevToolsDomainHandler(Security::Metainfo::domainName),
enabled_(false),
host_(nullptr) {
}
SecurityHandler::~SecurityHandler() {
}
void SecurityHandler::Wire(UberDispatcher* dispatcher) {
frontend_.reset(new Security::Frontend(dispatcher->channel()));
Security::Dispatcher::wire(dispatcher, this);
}
void SecurityHandler::AttachToRenderFrameHost() {
DCHECK(host_);
WebContents* web_contents = WebContents::FromRenderFrameHost(host_);
WebContentsObserver::Observe(web_contents);
// Send an initial DidChangeVisibleSecurityState event.
DCHECK(enabled_);
DidChangeVisibleSecurityState();
}
void SecurityHandler::SetRenderer(int process_host_id,
RenderFrameHostImpl* frame_host) {
host_ = frame_host;
if (enabled_ && host_)
AttachToRenderFrameHost();
}
void SecurityHandler::DidChangeVisibleSecurityState() {
DCHECK(enabled_);
SecurityStyleExplanations security_style_explanations;
blink::WebSecurityStyle security_style =
web_contents()->GetDelegate()->GetSecurityStyle(
web_contents(), &security_style_explanations);
const std::string security_state =
SecurityStyleToProtocolSecurityState(security_style);
std::unique_ptr<Explanations> explanations = Explanations::create();
AddExplanations(Security::SecurityStateEnum::Insecure,
security_style_explanations.insecure_explanations,
explanations.get());
AddExplanations(Security::SecurityStateEnum::Neutral,
security_style_explanations.neutral_explanations,
explanations.get());
AddExplanations(Security::SecurityStateEnum::Secure,
security_style_explanations.secure_explanations,
explanations.get());
AddExplanations(Security::SecurityStateEnum::Info,
security_style_explanations.info_explanations,
explanations.get());
std::unique_ptr<Security::InsecureContentStatus> insecure_status =
Security::InsecureContentStatus::Create()
.SetRanMixedContent(security_style_explanations.ran_mixed_content)
.SetDisplayedMixedContent(
security_style_explanations.displayed_mixed_content)
.SetContainedMixedForm(
security_style_explanations.contained_mixed_form)
.SetRanContentWithCertErrors(
security_style_explanations.ran_content_with_cert_errors)
.SetDisplayedContentWithCertErrors(
security_style_explanations.displayed_content_with_cert_errors)
.SetRanInsecureContentStyle(SecurityStyleToProtocolSecurityState(
security_style_explanations.ran_insecure_content_style))
.SetDisplayedInsecureContentStyle(
SecurityStyleToProtocolSecurityState(
security_style_explanations.displayed_insecure_content_style))
.Build();
frontend_->SecurityStateChanged(
security_state,
security_style_explanations.scheme_is_cryptographic,
std::move(explanations),
std::move(insecure_status),
Maybe<std::string>(security_style_explanations.summary));
}
void SecurityHandler::DidFinishNavigation(NavigationHandle* navigation_handle) {
if (cert_error_override_mode_ == CertErrorOverrideMode::kHandleEvents)
FlushPendingCertificateErrorNotifications();
}
void SecurityHandler::FlushPendingCertificateErrorNotifications() {
for (auto callback : cert_error_callbacks_)
callback.second.Run(content::CERTIFICATE_REQUEST_RESULT_TYPE_CANCEL);
cert_error_callbacks_.clear();
}
bool SecurityHandler::NotifyCertificateError(int cert_error,
const GURL& request_url,
CertErrorCallback handler) {
if (cert_error_override_mode_ == CertErrorOverrideMode::kIgnoreAll) {
if (handler)
handler.Run(content::CERTIFICATE_REQUEST_RESULT_TYPE_CONTINUE);
return true;
}
if (!enabled_)
return false;
frontend_->CertificateError(++last_cert_error_id_,
net::ErrorToShortString(cert_error),
request_url.spec());
if (!handler ||
cert_error_override_mode_ != CertErrorOverrideMode::kHandleEvents) {
return false;
}
cert_error_callbacks_[last_cert_error_id_] = handler;
return true;
}
Response SecurityHandler::Enable() {
enabled_ = true;
if (host_)
AttachToRenderFrameHost();
return Response::OK();
}
Response SecurityHandler::Disable() {
enabled_ = false;
cert_error_override_mode_ = CertErrorOverrideMode::kDisabled;
WebContentsObserver::Observe(nullptr);
FlushPendingCertificateErrorNotifications();
return Response::OK();
}
Response SecurityHandler::HandleCertificateError(int event_id,
const String& action) {
if (cert_error_callbacks_.find(event_id) == cert_error_callbacks_.end()) {
return Response::Error(
String("Unknown event id: " + std::to_string(event_id)));
}
content::CertificateRequestResultType type =
content::CERTIFICATE_REQUEST_RESULT_TYPE_CANCEL;
Response response = Response::OK();
if (action == Security::CertificateErrorActionEnum::Continue) {
type = content::CERTIFICATE_REQUEST_RESULT_TYPE_CONTINUE;
} else if (action == Security::CertificateErrorActionEnum::Cancel) {
type = content::CERTIFICATE_REQUEST_RESULT_TYPE_CANCEL;
} else {
response =
Response::Error(String("Unknown Certificate Error Action: " + action));
}
cert_error_callbacks_[event_id].Run(type);
cert_error_callbacks_.erase(event_id);
return response;
}
Response SecurityHandler::SetOverrideCertificateErrors(bool override) {
if (override) {
if (!enabled_)
return Response::Error("Security domain not enabled");
if (cert_error_override_mode_ == CertErrorOverrideMode::kIgnoreAll)
return Response::Error("Certificate errors are already being ignored.");
cert_error_override_mode_ = CertErrorOverrideMode::kHandleEvents;
} else {
cert_error_override_mode_ = CertErrorOverrideMode::kDisabled;
FlushPendingCertificateErrorNotifications();
}
return Response::OK();
}
Response SecurityHandler::SetIgnoreCertificateErrors(bool ignore) {
if (ignore) {
if (cert_error_override_mode_ == CertErrorOverrideMode::kHandleEvents)
return Response::Error("Certificate errors are already overridden.");
cert_error_override_mode_ = CertErrorOverrideMode::kIgnoreAll;
} else {
cert_error_override_mode_ = CertErrorOverrideMode::kDisabled;
}
return Response::OK();
}
} // namespace protocol
} // namespace content