Commit 97d9fd6f authored by Titouan Rigoudy's avatar Titouan Rigoudy Committed by Commit Bot

[CORS-RFC1918] Add per-request ClientSecurityState.

For subresource URL requests, the renderer URLLoader uses its factory's
parameters to determine the client security state of the request
initiator. Navigations however use a central URLLoaderFactory in the
browser process.

This change introduces a new trusted parameter in ResourceRequest
specifying the client security state applying to the current request,
if not was set in the factory parameters.

Regarding //services/network and BUILD.gn changes in particular:

A lot of these changes stem from the introduction of ClientSecurityState
as a new dependency of url_loader.mojom and URLRequest's typemap,
ResourceRequest. This breaks the existing layering scheme:

  mojom:url_loader_base
   -> cpp:cpp_base

Because suddenly cpp:cpp_base must depend on mojom:url_loader_base for
client_security_state.mojom.h. We break the cycle by extracting the
dependency of mojom:url_loader_base from cpp:cpp_base into its own build
target:

  cpp:cpp_base
   -> mojom:url_loader_base
     -> cpp:cross_origin_embedder_policy

Bug: chromium:1124346
Change-Id: Ie42d604b628a147a3963df0b20cc88b8b61bf98b
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2401523
Commit-Queue: Titouan Rigoudy <titouan@chromium.org>
Auto-Submit: Titouan Rigoudy <titouan@chromium.org>
Reviewed-by: default avatarMike West <mkwst@chromium.org>
Reviewed-by: default avatarArthur Sonzogni <arthursonzogni@chromium.org>
Reviewed-by: default avatarMatt Menke <mmenke@chromium.org>
Cr-Commit-Position: refs/heads/master@{#811256}
parent 9e410191
......@@ -216,7 +216,8 @@ class NavigationURLLoaderImplTest : public testing::Test {
nullptr /* blob_url_loader_factory */,
base::UnguessableToken::Create() /* devtools_navigation_token */,
base::UnguessableToken::Create() /* devtools_frame_token */,
false /* obey_origin_policy */, {} /* cors_exempt_headers */));
false /* obey_origin_policy */, {} /* cors_exempt_headers */,
nullptr /* client_security_state */));
std::vector<std::unique_ptr<NavigationLoaderInterceptor>> interceptors;
most_recent_resource_request_ = base::nullopt;
interceptors.push_back(std::make_unique<TestNavigationLoaderInterceptor>(
......
......@@ -90,7 +90,8 @@ class NavigationURLLoaderTest : public testing::Test {
nullptr /* blob_url_loader_factory */,
base::UnguessableToken::Create() /* devtools_navigation_token */,
base::UnguessableToken::Create() /* devtools_frame_token */,
false /* obey_origin_policy */, {} /* cors_exempt_headers */));
false /* obey_origin_policy */, {} /* cors_exempt_headers */,
nullptr /* client_security_state */));
return NavigationURLLoader::Create(
browser_context_.get(),
BrowserContext::GetDefaultStoragePartition(browser_context_.get()),
......
......@@ -2806,7 +2806,7 @@ void NavigationRequest::OnStartChecksComplete(
: nullptr,
devtools_navigation_token(), frame_tree_node_->devtools_frame_token(),
OriginPolicyThrottle::ShouldRequestOriginPolicy(common_params_->url),
std::move(cors_exempt_headers)),
std::move(cors_exempt_headers), nullptr /* client_security_state */),
std::move(navigation_ui_data), service_worker_handle_.get(),
appcache_handle_.get(), std::move(prefetched_signed_exchange_cache_),
this, IsServedFromBackForwardCache(), CreateCookieAccessObserver(),
......
......@@ -23,7 +23,8 @@ NavigationRequestInfo::NavigationRequestInfo(
const base::UnguessableToken& devtools_navigation_token,
const base::UnguessableToken& devtools_frame_token,
bool obey_origin_policy,
net::HttpRequestHeaders cors_exempt_headers)
net::HttpRequestHeaders cors_exempt_headers,
network::mojom::ClientSecurityStatePtr client_security_state)
: common_params(std::move(common_params)),
begin_params(std::move(begin_params)),
isolation_info(isolation_info),
......@@ -39,7 +40,8 @@ NavigationRequestInfo::NavigationRequestInfo(
devtools_navigation_token(devtools_navigation_token),
devtools_frame_token(devtools_frame_token),
obey_origin_policy(obey_origin_policy),
cors_exempt_headers(std::move(cors_exempt_headers)) {}
cors_exempt_headers(std::move(cors_exempt_headers)),
client_security_state(std::move(client_security_state)) {}
NavigationRequestInfo::~NavigationRequestInfo() {}
......
......@@ -16,6 +16,7 @@
#include "net/base/isolation_info.h"
#include "net/http/http_request_headers.h"
#include "services/network/public/cpp/shared_url_loader_factory.h"
#include "services/network/public/mojom/client_security_state.mojom-forward.h"
#include "url/gurl.h"
#include "url/origin.h"
......@@ -25,23 +26,25 @@ namespace content {
// ResourceDispatcherHost. It is initialized on the UI thread, and then passed
// to the IO thread by a NavigationRequest object.
struct CONTENT_EXPORT NavigationRequestInfo {
NavigationRequestInfo(mojom::CommonNavigationParamsPtr common_params,
mojom::BeginNavigationParamsPtr begin_params,
const net::IsolationInfo& isolation_info,
bool is_main_frame,
bool parent_is_main_frame,
bool are_ancestors_secure,
int frame_tree_node_id,
bool is_for_guests_only,
bool report_raw_headers,
bool is_prerendering,
bool upgrade_if_insecure,
std::unique_ptr<network::PendingSharedURLLoaderFactory>
blob_url_loader_factory,
const base::UnguessableToken& devtools_navigation_token,
const base::UnguessableToken& devtools_frame_token,
bool obey_origin_policy,
net::HttpRequestHeaders cors_exempt_headers);
NavigationRequestInfo(
mojom::CommonNavigationParamsPtr common_params,
mojom::BeginNavigationParamsPtr begin_params,
const net::IsolationInfo& isolation_info,
bool is_main_frame,
bool parent_is_main_frame,
bool are_ancestors_secure,
int frame_tree_node_id,
bool is_for_guests_only,
bool report_raw_headers,
bool is_prerendering,
bool upgrade_if_insecure,
std::unique_ptr<network::PendingSharedURLLoaderFactory>
blob_url_loader_factory,
const base::UnguessableToken& devtools_navigation_token,
const base::UnguessableToken& devtools_frame_token,
bool obey_origin_policy,
net::HttpRequestHeaders cors_exempt_headers,
network::mojom::ClientSecurityStatePtr client_security_state);
NavigationRequestInfo(const NavigationRequestInfo& other) = delete;
~NavigationRequestInfo();
......@@ -89,6 +92,10 @@ struct CONTENT_EXPORT NavigationRequestInfo {
const bool obey_origin_policy;
const net::HttpRequestHeaders cors_exempt_headers;
// Specifies the security state applying to the navigation. For iframes, this
// is the security state of their parent. Nullptr otherwise.
const network::mojom::ClientSecurityStatePtr client_security_state;
};
} // namespace content
......
......@@ -180,16 +180,31 @@ component("cookies_mojom_support") {
defines = [ "IS_NETWORK_CPP_BASE_IMPL" ]
}
# This component is separate from cpp_base as it is a dependency of
# //services/network/public/mojom:url_loader_base.
component("cross_origin_embedder_policy") {
sources = [
"cross_origin_embedder_policy.cc",
"cross_origin_embedder_policy.h",
"cross_origin_embedder_policy_mojom_traits.cc",
"cross_origin_embedder_policy_mojom_traits.h",
]
deps = [
"//base",
"//mojo/public/mojom/base",
"//services/network/public/mojom:url_loader_base_shared",
]
defines = [ "IS_NETWORK_CPP_BASE_IMPL" ]
}
component("cpp_base") {
output_name = "network_cpp_base"
sources = [
"cors/cors_error_status.cc",
"cors/cors_error_status.h",
"cross_origin_embedder_policy.cc",
"cross_origin_embedder_policy.h",
"cross_origin_embedder_policy_mojom_traits.cc",
"cross_origin_embedder_policy_mojom_traits.h",
"cross_origin_opener_policy.cc",
"cross_origin_opener_policy.h",
"cross_origin_opener_policy_mojom_traits.cc",
......@@ -243,6 +258,7 @@ component("cpp_base") {
public_deps = [
":cookies_mojom_support",
":crash_keys",
":cross_origin_embedder_policy",
":ip_address_mojom_support",
"//services/network/public/mojom:url_loader_base",
"//third_party/webrtc_overrides:webrtc_component",
......
......@@ -23,6 +23,17 @@ mojo::PendingRemote<mojom::CookieAccessObserver> Clone(
return new_remote;
}
// Returns true iff either holds true:
//
// - both |lhs| and |rhs| are nullopt, or
// - neither is nullopt and they both contain equal values
//
bool OptionalTrustedParamsEqualsForTesting(
const base::Optional<ResourceRequest::TrustedParams>& lhs,
const base::Optional<ResourceRequest::TrustedParams>& rhs) {
return (!lhs && !rhs) || (lhs && rhs && lhs->EqualsForTesting(*rhs));
}
} // namespace
ResourceRequest::TrustedParams::TrustedParams() = default;
......@@ -40,6 +51,7 @@ ResourceRequest::TrustedParams& ResourceRequest::TrustedParams::operator=(
cookie_observer =
Clone(&const_cast<mojo::PendingRemote<mojom::CookieAccessObserver>&>(
other.cookie_observer));
client_security_state = other.client_security_state.Clone();
return *this;
}
......@@ -47,7 +59,8 @@ bool ResourceRequest::TrustedParams::EqualsForTesting(
const TrustedParams& trusted_params) const {
return isolation_info.IsEqualForTesting(trusted_params.isolation_info) &&
disable_secure_dns == trusted_params.disable_secure_dns &&
has_user_activation == trusted_params.has_user_activation;
has_user_activation == trusted_params.has_user_activation &&
client_security_state == trusted_params.client_security_state;
}
ResourceRequest::ResourceRequest() {}
......@@ -55,14 +68,6 @@ ResourceRequest::ResourceRequest(const ResourceRequest& request) = default;
ResourceRequest::~ResourceRequest() {}
bool ResourceRequest::EqualsForTesting(const ResourceRequest& request) const {
if ((trusted_params && !request.trusted_params) ||
(!trusted_params && request.trusted_params)) {
return false;
}
if (trusted_params && request.trusted_params) {
if (!trusted_params->EqualsForTesting(*request.trusted_params))
return false;
}
return method == request.method && url == request.url &&
site_for_cookies.IsEquivalent(request.site_for_cookies) &&
force_ignore_site_for_cookies ==
......@@ -115,6 +120,8 @@ bool ResourceRequest::EqualsForTesting(const ResourceRequest& request) const {
request.is_signed_exchange_prefetch_cache_enabled &&
obey_origin_policy == request.obey_origin_policy &&
recursive_prefetch_token == request.recursive_prefetch_token &&
OptionalTrustedParamsEqualsForTesting(trusted_params,
request.trusted_params) &&
trust_token_params == request.trust_token_params;
}
......
......@@ -20,6 +20,7 @@
#include "net/url_request/referrer_policy.h"
#include "services/network/public/cpp/optional_trust_token_params.h"
#include "services/network/public/cpp/resource_request_body.h"
#include "services/network/public/mojom/client_security_state.mojom.h"
#include "services/network/public/mojom/cookie_access_observer.mojom.h"
#include "services/network/public/mojom/cors.mojom-shared.h"
#include "services/network/public/mojom/fetch_api.mojom-shared.h"
......@@ -53,6 +54,7 @@ struct COMPONENT_EXPORT(NETWORK_CPP_BASE) ResourceRequest {
bool disable_secure_dns = false;
bool has_user_activation = false;
mojo::PendingRemote<mojom::CookieAccessObserver> cookie_observer;
mojom::ClientSecurityStatePtr client_security_state;
};
ResourceRequest();
......
......@@ -149,12 +149,16 @@ bool StructTraits<network::mojom::TrustedUrlRequestParamsDataView,
network::ResourceRequest::TrustedParams>::
Read(network::mojom::TrustedUrlRequestParamsDataView data,
network::ResourceRequest::TrustedParams* out) {
if (!data.ReadIsolationInfo(&out->isolation_info))
if (!data.ReadIsolationInfo(&out->isolation_info)) {
return false;
}
out->disable_secure_dns = data.disable_secure_dns();
out->has_user_activation = data.has_user_activation();
out->cookie_observer = data.TakeCookieObserver<
mojo::PendingRemote<network::mojom::CookieAccessObserver>>();
if (!data.ReadClientSecurityState(&out->client_security_state)) {
return false;
}
return true;
}
......
......@@ -24,6 +24,7 @@
#include "services/network/public/cpp/resource_request_body.h"
#include "services/network/public/cpp/site_for_cookies_mojom_traits.h"
#include "services/network/public/mojom/chunked_data_pipe_getter.mojom.h"
#include "services/network/public/mojom/client_security_state.mojom-forward.h"
#include "services/network/public/mojom/cookie_access_observer.mojom.h"
#include "services/network/public/mojom/data_pipe_getter.mojom.h"
#include "services/network/public/mojom/trust_tokens.mojom.h"
......@@ -74,6 +75,10 @@ struct COMPONENT_EXPORT(NETWORK_CPP_BASE)
const_cast<network::ResourceRequest::TrustedParams&>(trusted_params)
.cookie_observer);
}
static const network::mojom::ClientSecurityStatePtr& client_security_state(
const network::ResourceRequest::TrustedParams& trusted_params) {
return trusted_params.client_security_state;
}
static bool Read(network::mojom::TrustedUrlRequestParamsDataView data,
network::ResourceRequest::TrustedParams* out);
......
......@@ -153,7 +153,11 @@ mojom("url_loader_base") {
generate_java = true
sources = [
"chunked_data_pipe_getter.mojom",
"client_security_state.mojom",
"cross_origin_embedder_policy.mojom",
"data_pipe_getter.mojom",
"fetch_api.mojom",
"ip_address_space.mojom",
"mutable_network_traffic_annotation_tag.mojom",
"mutable_partial_network_traffic_annotation_tag.mojom",
"trust_tokens.mojom",
......@@ -161,6 +165,7 @@ mojom("url_loader_base") {
public_deps = [
"//mojo/public/mojom/base",
"//url/mojom:url_mojom_gurl",
"//url/mojom:url_mojom_origin",
]
......@@ -172,6 +177,17 @@ mojom("url_loader_base") {
# Shared by cpp_typemaps and blink_cpp_typemaps.
shared_typemaps = [
{
types = [
{
mojom = "network.mojom.CrossOriginEmbedderPolicy"
cpp = "::network::CrossOriginEmbedderPolicy"
},
]
traits_headers = [ "//services/network/public/cpp/cross_origin_embedder_policy_mojom_traits.h" ]
traits_deps =
[ "//services/network/public/cpp:cross_origin_embedder_policy" ]
},
{
types = [
{
......@@ -424,17 +440,14 @@ mojom("mojom") {
"content_security_policy.mojom",
"cors.mojom",
"cors_origin_pattern.mojom",
"cross_origin_embedder_policy.mojom",
"cross_origin_opener_policy.mojom",
"default_credentials.mojom",
"dhcp_wpad_url_client.mojom",
"digitally_signed.mojom",
"fetch_api.mojom",
"host_resolver.mojom",
"http_raw_headers.mojom",
"http_raw_request_response_info.mojom",
"http_request_headers.mojom",
"ip_address_space.mojom",
"isolation_info.mojom",
"load_timing_info.mojom",
"mdns_responder.mojom",
......@@ -518,15 +531,6 @@ mojom("mojom") {
# Typemaps which apply to both Blink and non-Blink bindings.
shared_cpp_typemaps = [
{
types = [
{
mojom = "network.mojom.CrossOriginEmbedderPolicy"
cpp = "::network::CrossOriginEmbedderPolicy"
},
]
traits_headers = [ "//services/network/public/cpp/cross_origin_embedder_policy_mojom_traits.h" ]
},
{
types = [
{
......
// Copyright 2020 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.
module network.mojom;
import "services/network/public/mojom/cross_origin_embedder_policy.mojom";
import "services/network/public/mojom/ip_address_space.mojom";
// How to treat private network requests.
//
// Private network requests are any requests to a resource served by a
// non-public IP address.
//
// See the CORS-RFC1918 spec for details: https://wicg.github.io/cors-rfc1918.
enum PrivateNetworkRequestPolicy {
// Allow all requests.
kAllow,
// Forbid requests to more-private address spaces than that of the initiator,
// when the initiator is not in a secure context.
kBlockFromInsecureToMorePrivate,
};
struct ClientSecurityState {
// See: https://html.spec.whatwg.org/multipage/origin.html#coep
CrossOriginEmbedderPolicy cross_origin_embedder_policy;
// Whether the initiator of the requests is in a web secure context.
// See: https://developer.mozilla.org/en-US/docs/Web/Security/Secure_Contexts
bool is_web_secure_context = false;
// The initiator's IP AddressSpace.
IPAddressSpace ip_address_space = kUnknown;
// The policy to apply to private network requests.
PrivateNetworkRequestPolicy private_network_request_policy = kAllow;
};
......@@ -12,11 +12,12 @@ import "mojo/public/mojom/base/unguessable_token.mojom";
import "mojo/public/mojom/base/values.mojom";
import "services/network/public/mojom/address_list.mojom";
import "services/network/public/mojom/cert_verifier_service.mojom";
import "services/network/public/mojom/client_security_state.mojom";
import "services/network/public/mojom/cookie_access_observer.mojom";
import "services/network/public/mojom/cookie_manager.mojom";
import "services/network/public/mojom/default_credentials.mojom";
import "services/network/public/mojom/cors_origin_pattern.mojom";
import "services/network/public/mojom/cross_origin_embedder_policy.mojom";
import "services/network/public/mojom/default_credentials.mojom";
import "services/network/public/mojom/host_resolver.mojom";
import "services/network/public/mojom/http_request_headers.mojom";
import "services/network/public/mojom/ip_address.mojom";
......@@ -572,37 +573,6 @@ struct URLLoaderFactoryOverride {
bool skip_cors_enabled_scheme_check = false;
};
// How to treat private network requests.
//
// Private network requests are any requests to a resource served by a
// non-public IP address.
//
// See the CORS-RFC1918 spec for details: https://wicg.github.io/cors-rfc1918.
enum PrivateNetworkRequestPolicy {
// Allow all requests.
kAllow,
// Forbid requests to more-private address spaces than that of the initiator,
// when the initiator is not in a secure context.
kBlockFromInsecureToMorePrivate,
};
struct ClientSecurityState {
// https://mikewest.github.io/corpp/#integration-html
// https://mikewest.github.io/corpp/#initialize-embedder-policy-for-global
CrossOriginEmbedderPolicy cross_origin_embedder_policy;
// Whether the initiator of the requests is in a web secure context.
// See: https://developer.mozilla.org/en-US/docs/Web/Security/Secure_Contexts
bool is_web_secure_context = false;
// The initiator's IP AddressSpace.
IPAddressSpace ip_address_space = kUnknown;
// The policy to apply to private network requests.
PrivateNetworkRequestPolicy private_network_request_policy = kAllow;
};
// Whether to forbid all Trust Tokens redemption and signing operations
// (https://github.com/wicg/trust-token-api).
enum TrustTokenRedemptionPolicy {
......
......@@ -9,6 +9,7 @@ import "mojo/public/mojom/base/file.mojom";
import "mojo/public/mojom/base/file_path.mojom";
import "mojo/public/mojom/base/time.mojom";
import "mojo/public/mojom/base/unguessable_token.mojom";
import "services/network/public/mojom/client_security_state.mojom";
import "services/network/public/mojom/cors.mojom";
import "services/network/public/mojom/cookie_access_observer.mojom";
import "services/network/public/mojom/chunked_data_pipe_getter.mojom";
......@@ -92,6 +93,14 @@ struct TrustedUrlRequestParams {
// a cookie. If this is set to non-null, the observer passed to
// URLLoaderFactory will be ignored.
pending_remote<CookieAccessObserver>? cookie_observer;
// Specifies the security state of the client, for cases when the
// URLLoaderFactory is shared among multiple clients.
//
// This field is only used if no ClientSecurityState was specified in the
// URLLoaderFactoryParams passed to the factory. Otherwise, the value from
// the factory params is used unconditionally.
ClientSecurityState? client_security_state;
};
// Typemapped to network::ResourceRequest.
......
......@@ -836,6 +836,7 @@ mojom("mojom_core") {
"//cc/mojom",
"//services/data_decoder/public/mojom:mojom_resource_snapshot_for_web_bundle",
"//services/network/public/mojom:cookies_mojom",
"//services/network/public/mojom:url_loader_base",
"//third_party/blink/public/mojom/frame",
"//third_party/blink/public/mojom/tokens",
"//ui/events/mojom",
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment