blob: 9a3e55e920b7911b1c21dc6e657d351f191573ec [file] [log] [blame]
// Copyright 2019 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 <string>
#include "base/feature_list.h"
#include "base/values.h"
#include "net/base/features.h"
#include "net/base/network_isolation_key.h"
#include "net/base/registry_controlled_domains/registry_controlled_domain.h"
#include "url/gurl.h"
#include "url/origin.h"
#include "url/url_constants.h"
namespace net {
namespace {
std::string GetOriginDebugString(const base::Optional<url::Origin>& origin) {
return origin ? origin->GetDebugString() : "null";
}
// If |origin| has a value and represents an HTTP or HTTPS scheme, replace its
// host with its registerable domain if possible, and replace its port with the
// standard port for its scheme. Otherwise, does nothing. WS and WSS origins are
// not modified, as they shouldn't be used meaningfully for NIKs, though trying
// to navigate to a WS URL may generate such a NIK.
void SwitchToRegistrableDomainAndRemovePort(
base::Optional<url::Origin>* origin) {
if (!origin->has_value())
return;
if ((*origin)->scheme() != url::kHttpsScheme &&
(*origin)->scheme() != url::kHttpScheme) {
return;
}
// scheme() returns the empty string for opaque origins.
DCHECK(!(*origin)->opaque());
std::string registrable_domain = GetDomainAndRegistry(
origin->value(),
net::registry_controlled_domains::INCLUDE_PRIVATE_REGISTRIES);
// GetDomainAndRegistry() returns an empty string for IP literals and
// effective TLDs.
if (registrable_domain.empty())
registrable_domain = (*origin)->host();
*origin = url::Origin::CreateFromNormalizedTuple(
(*origin)->scheme(), registrable_domain,
url::DefaultPortForScheme((*origin)->scheme().c_str(),
(*origin)->scheme().length()));
}
} // namespace
NetworkIsolationKey::NetworkIsolationKey(const url::Origin& top_frame_origin,
const url::Origin& frame_origin)
: NetworkIsolationKey(top_frame_origin,
frame_origin,
false /* opaque_and_non_transient */) {}
NetworkIsolationKey::NetworkIsolationKey()
: use_frame_origin_(base::FeatureList::IsEnabled(
net::features::kAppendFrameOriginToNetworkIsolationKey)) {}
NetworkIsolationKey::NetworkIsolationKey(
const NetworkIsolationKey& network_isolation_key) = default;
NetworkIsolationKey::~NetworkIsolationKey() = default;
NetworkIsolationKey& NetworkIsolationKey::operator=(
const NetworkIsolationKey& network_isolation_key) = default;
NetworkIsolationKey& NetworkIsolationKey::operator=(
NetworkIsolationKey&& network_isolation_key) = default;
NetworkIsolationKey NetworkIsolationKey::CreateTransient() {
url::Origin opaque_origin;
return NetworkIsolationKey(opaque_origin, opaque_origin);
}
NetworkIsolationKey NetworkIsolationKey::CreateOpaqueAndNonTransient() {
url::Origin opaque_origin;
return NetworkIsolationKey(opaque_origin, opaque_origin,
true /* opaque_and_non_transient */);
}
NetworkIsolationKey NetworkIsolationKey::CreateWithNewFrameOrigin(
const url::Origin& new_frame_origin) const {
if (!top_frame_origin_)
return NetworkIsolationKey();
NetworkIsolationKey key(top_frame_origin_.value(), new_frame_origin);
key.opaque_and_non_transient_ = opaque_and_non_transient_;
return key;
}
std::string NetworkIsolationKey::ToString() const {
if (IsTransient())
return "";
if (IsOpaque()) {
// This key is opaque but not transient.
DCHECK(opaque_and_non_transient_);
return "opaque non-transient " +
top_frame_origin_->nonce_->token().ToString();
}
return top_frame_origin_->Serialize() +
(use_frame_origin_ ? " " + frame_origin_->Serialize() : "");
}
std::string NetworkIsolationKey::ToDebugString() const {
// The space-separated serialization of |top_frame_origin_| and
// |frame_origin_|.
std::string return_string = GetOriginDebugString(top_frame_origin_);
if (use_frame_origin_) {
return_string += " " + GetOriginDebugString(frame_origin_);
}
if (IsFullyPopulated() && IsOpaque() && opaque_and_non_transient_) {
return_string += " non-transient";
}
return return_string;
}
bool NetworkIsolationKey::IsFullyPopulated() const {
return top_frame_origin_.has_value() &&
(!use_frame_origin_ || frame_origin_.has_value());
}
bool NetworkIsolationKey::IsTransient() const {
if (!IsFullyPopulated())
return true;
if (opaque_and_non_transient_) {
DCHECK(IsOpaque());
return false;
}
return IsOpaque();
}
bool NetworkIsolationKey::ToValue(base::Value* out_value) const {
if (IsEmpty()) {
*out_value = base::Value(base::Value::Type::LIST);
return true;
}
if (IsTransient())
return false;
base::Optional<std::string> top_frame_value =
top_frame_origin_->SerializeWithNonce();
if (!top_frame_value)
return false;
*out_value = base::Value(base::Value::Type::LIST);
out_value->Append(std::move(*top_frame_value));
if (use_frame_origin_) {
base::Optional<std::string> frame_value =
frame_origin_->SerializeWithNonce();
if (!frame_value)
return false;
out_value->Append(std::move(*frame_value));
}
return true;
}
bool NetworkIsolationKey::FromValue(
const base::Value& value,
NetworkIsolationKey* network_isolation_key) {
if (!value.is_list())
return false;
base::Value::ConstListView list = value.GetList();
if (list.empty()) {
*network_isolation_key = NetworkIsolationKey();
return true;
}
bool use_frame_origin = base::FeatureList::IsEnabled(
net::features::kAppendFrameOriginToNetworkIsolationKey);
if ((!use_frame_origin && list.size() != 1) ||
(use_frame_origin && list.size() != 2)) {
return false;
}
if (!list[0].is_string())
return false;
base::Optional<url::Origin> deserialized_top_frame =
url::Origin::Deserialize(list[0].GetString());
if (!deserialized_top_frame)
return false;
url::Origin top_frame_origin = *deserialized_top_frame;
// An opaque origin key will only be serialized into a base::Value if
// |opaque_and_non_transient_| is set. Therefore if either origin is opaque,
// |opaque_and_non_transient_| must be true.
bool opaque_and_non_transient = top_frame_origin.opaque();
if (!use_frame_origin) {
*network_isolation_key =
NetworkIsolationKey(top_frame_origin, top_frame_origin);
network_isolation_key->opaque_and_non_transient_ = opaque_and_non_transient;
return true;
}
if (!list[1].is_string())
return false;
base::Optional<url::Origin> deserialized_frame =
url::Origin::Deserialize(list[1].GetString());
if (!deserialized_frame)
return false;
url::Origin frame_origin = *deserialized_frame;
opaque_and_non_transient |= frame_origin.opaque();
*network_isolation_key = NetworkIsolationKey(top_frame_origin, frame_origin);
network_isolation_key->opaque_and_non_transient_ = opaque_and_non_transient;
return true;
}
bool NetworkIsolationKey::IsEmpty() const {
return !top_frame_origin_.has_value() && !frame_origin_.has_value();
}
NetworkIsolationKey::NetworkIsolationKey(const url::Origin& top_frame_origin,
const url::Origin& frame_origin,
bool opaque_and_non_transient)
: opaque_and_non_transient_(opaque_and_non_transient),
use_frame_origin_(base::FeatureList::IsEnabled(
net::features::kAppendFrameOriginToNetworkIsolationKey)),
top_frame_origin_(top_frame_origin),
original_top_frame_origin_(top_frame_origin) {
DCHECK(!opaque_and_non_transient || top_frame_origin.opaque());
if (use_frame_origin_) {
DCHECK(!opaque_and_non_transient || frame_origin.opaque());
frame_origin_ = frame_origin;
original_frame_origin_ = frame_origin;
}
if (base::FeatureList::IsEnabled(
net::features::kUseRegistrableDomainInNetworkIsolationKey)) {
ReplaceOriginsWithRegistrableDomains();
}
}
void NetworkIsolationKey::ReplaceOriginsWithRegistrableDomains() {
SwitchToRegistrableDomainAndRemovePort(&top_frame_origin_);
SwitchToRegistrableDomainAndRemovePort(&frame_origin_);
}
bool NetworkIsolationKey::IsOpaque() const {
return top_frame_origin_->opaque() ||
(use_frame_origin_ && frame_origin_->opaque());
}
} // namespace net