blob: edcf3f84683aa8c9e74ed2208de12beb343a62f2 [file] [log] [blame]
// Copyright 2020 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "content/browser/renderer_host/policy_container_host.h"
#include "base/lazy_instance.h"
#include "content/browser/renderer_host/frame_navigation_entry.h"
#include "content/browser/renderer_host/frame_tree_node.h"
#include "content/browser/renderer_host/private_network_access_util.h"
#include "content/browser/renderer_host/render_frame_host_impl.h"
#include "content/public/browser/browser_thread.h"
#include "services/network/public/cpp/is_potentially_trustworthy.h"
#include "services/network/public/mojom/content_security_policy.mojom.h"
namespace content {
namespace {
// KeepAliveHandle is simply a class referencing a PolicyContainerHost through a
// scoped_refptr, hence maintaining it alive.
class KeepAliveHandle
: public scoped_refptr<PolicyContainerHost>,
public blink::mojom::PolicyContainerHostKeepAliveHandle {
public:
explicit KeepAliveHandle(PolicyContainerHost* policy_container_host) {
wrapped_pointer = policy_container_host;
}
private:
scoped_refptr<PolicyContainerHost> wrapped_pointer;
};
using TokenPolicyContainerMap =
std::unordered_map<blink::LocalFrameToken,
PolicyContainerHost*,
blink::LocalFrameToken::Hasher>;
base::LazyInstance<TokenPolicyContainerMap>::Leaky
g_token_policy_container_map = LAZY_INSTANCE_INITIALIZER;
} // namespace
bool operator==(const PolicyContainerPolicies& lhs,
const PolicyContainerPolicies& rhs) {
return lhs.referrer_policy == rhs.referrer_policy &&
lhs.ip_address_space == rhs.ip_address_space &&
lhs.is_web_secure_context == rhs.is_web_secure_context &&
std::equal(lhs.content_security_policies.begin(),
lhs.content_security_policies.end(),
rhs.content_security_policies.begin(),
rhs.content_security_policies.end()) &&
lhs.cross_origin_opener_policy == rhs.cross_origin_opener_policy &&
lhs.cross_origin_embedder_policy == rhs.cross_origin_embedder_policy &&
lhs.sandbox_flags == rhs.sandbox_flags &&
lhs.is_anonymous == rhs.is_anonymous;
}
bool operator!=(const PolicyContainerPolicies& lhs,
const PolicyContainerPolicies& rhs) {
return !(lhs == rhs);
}
std::ostream& operator<<(std::ostream& out,
const PolicyContainerPolicies& policies) {
out << "{ referrer_policy: " << policies.referrer_policy
<< ", ip_address_space: " << policies.ip_address_space
<< ", is_web_secure_context: " << policies.is_web_secure_context
<< ", content_security_policies: ";
if (policies.content_security_policies.empty()) {
out << "[]";
} else {
out << "[ ";
auto it = policies.content_security_policies.begin();
for (; it + 1 != policies.content_security_policies.end(); ++it) {
out << (*it)->header->header_value << ", ";
}
out << (*it)->header->header_value << " ]";
}
out << ", cross_origin_opener_policy: "
<< "{ value: " << policies.cross_origin_opener_policy.value
<< ", reporting_endpoint: "
<< policies.cross_origin_opener_policy.reporting_endpoint.value_or(
"<null>")
<< ", report_only_value: "
<< policies.cross_origin_opener_policy.report_only_value
<< ", report_only_reporting_endpoint: "
<< policies.cross_origin_opener_policy.report_only_reporting_endpoint
.value_or("<null>")
<< ", soap_by_default_value: "
<< policies.cross_origin_opener_policy.soap_by_default_value << " }";
out << ", cross_origin_embedder_policy: "
<< "{ value: " << policies.cross_origin_embedder_policy.value
<< ", reporting_endpoint: "
<< policies.cross_origin_embedder_policy.reporting_endpoint.value_or(
"<null>")
<< ", report_only_value: "
<< policies.cross_origin_embedder_policy.report_only_value
<< ", report_only_reporting_endpoint: "
<< policies.cross_origin_embedder_policy.report_only_reporting_endpoint
.value_or("<null>")
<< " }";
out << ", sandbox_flags: " << policies.sandbox_flags;
out << ", is_anonymous: " << policies.is_anonymous;
return out << " }";
}
PolicyContainerPolicies::PolicyContainerPolicies() = default;
PolicyContainerPolicies::PolicyContainerPolicies(
network::mojom::ReferrerPolicy referrer_policy,
network::mojom::IPAddressSpace ip_address_space,
bool is_web_secure_context,
std::vector<network::mojom::ContentSecurityPolicyPtr>
content_security_policies,
const network::CrossOriginOpenerPolicy& cross_origin_opener_policy,
const network::CrossOriginEmbedderPolicy& cross_origin_embedder_policy,
network::mojom::WebSandboxFlags sandbox_flags,
bool is_anonymous)
: referrer_policy(referrer_policy),
ip_address_space(ip_address_space),
is_web_secure_context(is_web_secure_context),
content_security_policies(std::move(content_security_policies)),
cross_origin_opener_policy(cross_origin_opener_policy),
cross_origin_embedder_policy(cross_origin_embedder_policy),
sandbox_flags(sandbox_flags),
is_anonymous(is_anonymous) {}
PolicyContainerPolicies::PolicyContainerPolicies(
const blink::mojom::PolicyContainerPolicies& policies) {
cross_origin_embedder_policy.value = policies.cross_origin_embedder_policy;
referrer_policy = policies.referrer_policy;
content_security_policies = mojo::Clone(policies.content_security_policies);
is_anonymous = policies.is_anonymous;
sandbox_flags = policies.sandbox_flags;
}
PolicyContainerPolicies::PolicyContainerPolicies(
const GURL& url,
network::mojom::URLResponseHead* response_head,
ContentBrowserClient* client)
: PolicyContainerPolicies(
network::mojom::ReferrerPolicy::kDefault,
CalculateIPAddressSpace(url, response_head, client),
network::IsUrlPotentiallyTrustworthy(url),
mojo::Clone(response_head->parsed_headers->content_security_policy),
response_head->parsed_headers->cross_origin_opener_policy,
response_head->parsed_headers->cross_origin_embedder_policy,
network::mojom::WebSandboxFlags::kNone,
/*is_anonymous=*/false) {
for (auto& content_security_policy :
response_head->parsed_headers->content_security_policy) {
sandbox_flags |= content_security_policy->sandbox;
}
}
PolicyContainerPolicies::PolicyContainerPolicies(PolicyContainerPolicies&&) =
default;
PolicyContainerPolicies& PolicyContainerPolicies::operator=(
PolicyContainerPolicies&&) = default;
PolicyContainerPolicies::~PolicyContainerPolicies() = default;
PolicyContainerPolicies PolicyContainerPolicies::Clone() const {
return PolicyContainerPolicies(
referrer_policy, ip_address_space, is_web_secure_context,
mojo::Clone(content_security_policies), cross_origin_opener_policy,
cross_origin_embedder_policy, sandbox_flags, is_anonymous);
}
std::unique_ptr<PolicyContainerPolicies> PolicyContainerPolicies::ClonePtr()
const {
return std::make_unique<PolicyContainerPolicies>(Clone());
}
void PolicyContainerPolicies::AddContentSecurityPolicies(
std::vector<network::mojom::ContentSecurityPolicyPtr> policies) {
content_security_policies.insert(content_security_policies.end(),
std::make_move_iterator(policies.begin()),
std::make_move_iterator(policies.end()));
}
blink::mojom::PolicyContainerPoliciesPtr
PolicyContainerPolicies::ToMojoPolicyContainerPolicies() const {
return blink::mojom::PolicyContainerPolicies::New(
cross_origin_embedder_policy.value, referrer_policy,
mojo::Clone(content_security_policies), is_anonymous, sandbox_flags);
}
PolicyContainerHost::PolicyContainerHost() = default;
PolicyContainerHost::PolicyContainerHost(PolicyContainerPolicies policies)
: policies_(std::move(policies)) {}
PolicyContainerHost::~PolicyContainerHost() {
// The PolicyContainerHost associated with |frame_token_| might have
// changed. In that case, we must not remove the map entry.
if (frame_token_ && FromFrameToken(frame_token_.value()) == this)
g_token_policy_container_map.Get().erase(frame_token_.value());
}
void PolicyContainerHost::AssociateWithFrameToken(
const blink::LocalFrameToken& frame_token,
int process_id) {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
DCHECK(!frame_token_);
frame_token_ = frame_token;
process_id_ = process_id;
g_token_policy_container_map.Get().erase(frame_token);
g_token_policy_container_map.Get().emplace(frame_token, this);
}
PolicyContainerHost* PolicyContainerHost::FromFrameToken(
const blink::LocalFrameToken& frame_token) {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
auto it = g_token_policy_container_map.Get().find(frame_token);
if (it == g_token_policy_container_map.Get().end())
return nullptr;
return it->second;
}
void PolicyContainerHost::SetReferrerPolicy(
network::mojom::ReferrerPolicy referrer_policy) {
policies_.referrer_policy = referrer_policy;
if (frame_token_) {
if (RenderFrameHostImpl* rfh = RenderFrameHostImpl::FromFrameToken(
process_id_, frame_token_.value())) {
rfh->DidChangeReferrerPolicy(referrer_policy);
}
}
}
void PolicyContainerHost::AddContentSecurityPolicies(
std::vector<network::mojom::ContentSecurityPolicyPtr>
content_security_policies) {
policies_.AddContentSecurityPolicies(std::move(content_security_policies));
}
blink::mojom::PolicyContainerPtr
PolicyContainerHost::CreatePolicyContainerForBlink() {
// This function might be called several times, for example if we need to
// recreate the RenderFrame after the renderer process died. We gracefully
// handle this by resetting the receiver and creating a new one. It would be
// good to find a way to check that the previous remote has been deleted or is
// not needed anymore. Unfortunately, this cannot be done with a disconnect
// handler, since the mojo disconnect notification is not guaranteed to be
// received before we try to create a new remote.
policy_container_host_receiver_.reset();
mojo::PendingAssociatedRemote<blink::mojom::PolicyContainerHost> remote;
Bind(blink::mojom::PolicyContainerBindParams::New(
remote.InitWithNewEndpointAndPassReceiver()));
return blink::mojom::PolicyContainer::New(
policies_.ToMojoPolicyContainerPolicies(), std::move(remote));
}
scoped_refptr<PolicyContainerHost> PolicyContainerHost::Clone() const {
return base::MakeRefCounted<PolicyContainerHost>(policies_.Clone());
}
void PolicyContainerHost::Bind(
blink::mojom::PolicyContainerBindParamsPtr bind_params) {
policy_container_host_receiver_.Bind(std::move(bind_params->receiver));
// Keep the PolicyContainerHost alive, as long as its PolicyContainer (owning
// the mojo remote) in the renderer process alive.
scoped_refptr<PolicyContainerHost> copy = this;
policy_container_host_receiver_.set_disconnect_handler(base::BindOnce(
[](scoped_refptr<PolicyContainerHost>) {}, std::move(copy)));
}
void PolicyContainerHost::IssueKeepAliveHandle(
mojo::PendingReceiver<blink::mojom::PolicyContainerHostKeepAliveHandle>
receiver) {
keep_alive_handles_receiver_set_.Add(std::make_unique<KeepAliveHandle>(this),
std::move(receiver));
}
} // namespace content