// 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 #include #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 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 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::Serialize(const std::optional& 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::FromNetworkIsolationKey( const NetworkIsolationKey& network_isolation_key, SiteForCookies site_for_cookies, SchemefulSite request_site) { const std::optional& 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& 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::FromStorageKeyComponents( const SchemefulSite& site, AncestorChainBit ancestor_chain_bit, const std::optional& nonce) { return CookiePartitionKey::FromWire(site, ancestor_chain_bit, nonce); } // static base::expected, 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 key = DeserializeInternal( top_level_site, BoolToAncestorChainBit(has_cross_site_ancestor)); if (!key.has_value()) { DLOG(WARNING) << key.error(); } return key; } // static base::expected 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 key = DeserializeInternal( top_level_site, BoolToAncestorChainBit(has_cross_site_ancestor)); if (!key.has_value()) { return WarnAndCreateUnexpected(key.error()); } return key; } base::expected 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