blob: 40007c4deabc4287e7191aad3c00b10655fd7b94 [file] [log] [blame]
// Copyright 2021 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "net/cookies/cookie_partition_key.h"
#include <ostream>
#include <tuple>
#include "base/feature_list.h"
#include "base/logging.h"
#include "base/types/optional_util.h"
#include "net/base/cronet_buildflags.h"
#include "net/cookies/cookie_constants.h"
#include "net/cookies/site_for_cookies.h"
#if !BUILDFLAG(CRONET_BUILD)
#include "mojo/public/cpp/bindings/default_construct_tag.h"
#endif
namespace net {
namespace {
base::unexpected<std::string> WarnAndCreateUnexpected(
const std::string& message) {
DLOG(WARNING) << message;
return base::unexpected(message);
}
} // namespace
CookiePartitionKey::SerializedCookiePartitionKey::SerializedCookiePartitionKey(
const std::string& site,
bool has_cross_site_ancestor)
: top_level_site_(site),
has_cross_site_ancestor_(has_cross_site_ancestor) {}
const std::string&
CookiePartitionKey::SerializedCookiePartitionKey::TopLevelSite() const {
return top_level_site_;
}
#if !BUILDFLAG(CRONET_BUILD)
CookiePartitionKey::CookiePartitionKey(mojo::DefaultConstruct::Tag) {}
#endif
bool CookiePartitionKey::SerializedCookiePartitionKey::has_cross_site_ancestor()
const {
return has_cross_site_ancestor_;
}
// static
CookiePartitionKey::AncestorChainBit CookiePartitionKey::BoolToAncestorChainBit(
bool cross_site) {
return cross_site ? AncestorChainBit::kCrossSite
: AncestorChainBit::kSameSite;
}
CookiePartitionKey::CookiePartitionKey(
const SchemefulSite& site,
std::optional<base::UnguessableToken> nonce,
AncestorChainBit ancestor_chain_bit)
: site_(site), nonce_(nonce), ancestor_chain_bit_(ancestor_chain_bit) {
if (nonce.has_value()) {
CHECK_EQ(ancestor_chain_bit, AncestorChainBit::kCrossSite);
}
}
CookiePartitionKey::CookiePartitionKey(bool from_script)
: from_script_(from_script) {}
CookiePartitionKey::CookiePartitionKey(const CookiePartitionKey& other) =
default;
CookiePartitionKey::CookiePartitionKey(CookiePartitionKey&& other) = default;
CookiePartitionKey& CookiePartitionKey::operator=(
const CookiePartitionKey& other) = default;
CookiePartitionKey& CookiePartitionKey::operator=(CookiePartitionKey&& other) =
default;
CookiePartitionKey::~CookiePartitionKey() = default;
bool CookiePartitionKey::operator==(const CookiePartitionKey& other) const {
AncestorChainBit this_bit = MaybeAncestorChainBit();
AncestorChainBit other_bit = other.MaybeAncestorChainBit();
return std::tie(site_, nonce_, this_bit) ==
std::tie(other.site_, other.nonce_, other_bit);
}
bool CookiePartitionKey::operator!=(const CookiePartitionKey& other) const {
return !(*this == other);
}
bool CookiePartitionKey::operator<(const CookiePartitionKey& other) const {
AncestorChainBit this_bit = MaybeAncestorChainBit();
AncestorChainBit other_bit = other.MaybeAncestorChainBit();
return std::tie(site_, nonce_, this_bit) <
std::tie(other.site_, other.nonce_, other_bit);
}
// static
base::expected<CookiePartitionKey::SerializedCookiePartitionKey, std::string>
CookiePartitionKey::Serialize(const std::optional<CookiePartitionKey>& in) {
if (!in) {
return base::ok(
SerializedCookiePartitionKey(kEmptyCookiePartitionKey, true));
}
if (!in->IsSerializeable()) {
return WarnAndCreateUnexpected("CookiePartitionKey is not serializeable");
}
return base::ok(SerializedCookiePartitionKey(
in->site_.GetURL().SchemeIsFile() ? in->site_.SerializeFileSiteWithHost()
: in->site_.Serialize(),
in->IsThirdParty()));
}
std::optional<CookiePartitionKey> CookiePartitionKey::FromNetworkIsolationKey(
const NetworkIsolationKey& network_isolation_key,
SiteForCookies site_for_cookies,
SchemefulSite request_site) {
const std::optional<base::UnguessableToken>& nonce =
network_isolation_key.GetNonce();
// Use frame site for nonced partitions. Since the nonce is unique, this
// still creates a unique partition key. The reason we use the frame site is
// to align CookiePartitionKey's implementation of nonced partitions with
// StorageKey's. See https://crbug.com/1440765.
const std::optional<SchemefulSite>& partition_key_site =
nonce ? network_isolation_key.GetFrameSiteForCookiePartitionKey(
NetworkIsolationKey::CookiePartitionKeyPassKey())
: network_isolation_key.GetTopFrameSite();
if (!partition_key_site) {
return std::nullopt;
}
const auto ancestor_chain_bit =
(nonce || site_for_cookies.IsNull())
? AncestorChainBit::kCrossSite
: BoolToAncestorChainBit(
!site_for_cookies.IsFirstParty(request_site.GetURL()));
return CookiePartitionKey(*partition_key_site, nonce, ancestor_chain_bit);
}
// static
std::optional<CookiePartitionKey> CookiePartitionKey::FromStorageKeyComponents(
const SchemefulSite& site,
AncestorChainBit ancestor_chain_bit,
const std::optional<base::UnguessableToken>& nonce) {
return CookiePartitionKey::FromWire(site, ancestor_chain_bit, nonce);
}
// static
base::expected<std::optional<CookiePartitionKey>, std::string>
CookiePartitionKey::FromStorage(const std::string& top_level_site,
bool has_cross_site_ancestor) {
if (top_level_site == kEmptyCookiePartitionKey) {
return base::ok(std::nullopt);
}
base::expected<CookiePartitionKey, std::string> key = DeserializeInternal(
top_level_site, BoolToAncestorChainBit(has_cross_site_ancestor));
if (!key.has_value()) {
DLOG(WARNING) << key.error();
}
return key;
}
// static
base::expected<CookiePartitionKey, std::string>
CookiePartitionKey::FromUntrustedInput(const std::string& top_level_site,
bool has_cross_site_ancestor) {
if (top_level_site.empty()) {
return WarnAndCreateUnexpected("top_level_site is unexpectedly empty");
}
base::expected<CookiePartitionKey, std::string> key = DeserializeInternal(
top_level_site, BoolToAncestorChainBit(has_cross_site_ancestor));
if (!key.has_value()) {
return WarnAndCreateUnexpected(key.error());
}
return key;
}
base::expected<CookiePartitionKey, std::string>
CookiePartitionKey::DeserializeInternal(
const std::string& top_level_site,
CookiePartitionKey::AncestorChainBit has_cross_site_ancestor) {
auto schemeful_site = SchemefulSite::Deserialize(top_level_site);
if (schemeful_site.opaque()) {
return WarnAndCreateUnexpected(
"Cannot deserialize opaque origin to CookiePartitionKey");
}
return base::ok(CookiePartitionKey(schemeful_site, std::nullopt,
has_cross_site_ancestor));
}
bool CookiePartitionKey::IsSerializeable() const {
// We should not try to serialize a partition key created by a renderer.
DCHECK(!from_script_);
return !site_.opaque() && !nonce_.has_value();
}
CookiePartitionKey::AncestorChainBit CookiePartitionKey::MaybeAncestorChainBit()
const {
return ancestor_chain_enabled_ ? ancestor_chain_bit_
: AncestorChainBit::kCrossSite;
}
std::ostream& operator<<(std::ostream& os, const CookiePartitionKey& cpk) {
os << cpk.site();
if (cpk.nonce().has_value()) {
os << ",nonced";
}
os << (cpk.IsThirdParty() ? ",cross_site" : ",same_site");
return os;
}
} // namespace net