• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2021 The Chromium Authors
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 #include "net/cookies/cookie_partition_key.h"
6 
7 #include <ostream>
8 #include <tuple>
9 
10 #include "base/feature_list.h"
11 #include "base/logging.h"
12 #include "base/types/optional_util.h"
13 #include "net/base/cronet_buildflags.h"
14 #include "net/base/features.h"
15 #include "net/cookies/cookie_constants.h"
16 #include "net/cookies/cookie_util.h"
17 #include "net/cookies/site_for_cookies.h"
18 
19 #if !BUILDFLAG(CRONET_BUILD)
20 #include "mojo/public/cpp/bindings/default_construct_tag.h"
21 #endif
22 
23 namespace net {
24 
25 namespace {
26 
WarnAndCreateUnexpected(const std::string & message)27 base::unexpected<std::string> WarnAndCreateUnexpected(
28     const std::string& message) {
29   DLOG(WARNING) << message;
30   return base::unexpected(message);
31 }
32 
SerializeSchemefulSite(const SchemefulSite & site)33 std::string SerializeSchemefulSite(const SchemefulSite& site) {
34   return site.GetURL().SchemeIsFile() ? site.SerializeFileSiteWithHost()
35                                       : site.Serialize();
36 }
37 
38 }  // namespace
39 
SerializedCookiePartitionKey(base::PassKey<CookiePartitionKey> key,const std::string & site,bool has_cross_site_ancestor)40 CookiePartitionKey::SerializedCookiePartitionKey::SerializedCookiePartitionKey(
41     base::PassKey<CookiePartitionKey> key,
42     const std::string& site,
43     bool has_cross_site_ancestor)
44     : top_level_site_(site),
45       has_cross_site_ancestor_(has_cross_site_ancestor) {}
46 
47 const std::string&
TopLevelSite() const48 CookiePartitionKey::SerializedCookiePartitionKey::TopLevelSite() const {
49   return top_level_site_;
50 }
51 
GetDebugString() const52 std::string CookiePartitionKey::SerializedCookiePartitionKey::GetDebugString()
53     const {
54   std::string out = TopLevelSite();
55   if (base::FeatureList::IsEnabled(
56           features::kAncestorChainBitEnabledInPartitionedCookies)) {
57     base::StrAppend(
58         &out, {", ", has_cross_site_ancestor() ? "cross-site" : "same-site"});
59   }
60   return out;
61 }
62 
63 #if !BUILDFLAG(CRONET_BUILD)
CookiePartitionKey(mojo::DefaultConstruct::Tag)64 CookiePartitionKey::CookiePartitionKey(mojo::DefaultConstruct::Tag) {}
65 #endif
has_cross_site_ancestor() const66 bool CookiePartitionKey::SerializedCookiePartitionKey::has_cross_site_ancestor()
67     const {
68   return has_cross_site_ancestor_;
69 }
70 
71 // static
BoolToAncestorChainBit(bool cross_site)72 CookiePartitionKey::AncestorChainBit CookiePartitionKey::BoolToAncestorChainBit(
73     bool cross_site) {
74   return cross_site ? AncestorChainBit::kCrossSite
75                     : AncestorChainBit::kSameSite;
76 }
77 
CookiePartitionKey(const SchemefulSite & site,std::optional<base::UnguessableToken> nonce,AncestorChainBit ancestor_chain_bit)78 CookiePartitionKey::CookiePartitionKey(
79     const SchemefulSite& site,
80     std::optional<base::UnguessableToken> nonce,
81     AncestorChainBit ancestor_chain_bit)
82     : site_(site), nonce_(nonce), ancestor_chain_bit_(ancestor_chain_bit) {
83 }
84 
CookiePartitionKey(bool from_script)85 CookiePartitionKey::CookiePartitionKey(bool from_script)
86     : from_script_(from_script) {}
87 
88 CookiePartitionKey::CookiePartitionKey(const CookiePartitionKey& other) =
89     default;
90 
91 CookiePartitionKey::CookiePartitionKey(CookiePartitionKey&& other) = default;
92 
93 CookiePartitionKey& CookiePartitionKey::operator=(
94     const CookiePartitionKey& other) = default;
95 
96 CookiePartitionKey& CookiePartitionKey::operator=(CookiePartitionKey&& other) =
97     default;
98 
99 CookiePartitionKey::~CookiePartitionKey() = default;
100 
operator ==(const CookiePartitionKey & other) const101 bool CookiePartitionKey::operator==(const CookiePartitionKey& other) const {
102   AncestorChainBit this_bit = MaybeAncestorChainBit();
103   AncestorChainBit other_bit = other.MaybeAncestorChainBit();
104 
105   return std::tie(site_, nonce_, this_bit) ==
106          std::tie(other.site_, other.nonce_, other_bit);
107 }
108 
operator !=(const CookiePartitionKey & other) const109 bool CookiePartitionKey::operator!=(const CookiePartitionKey& other) const {
110   return !(*this == other);
111 }
112 
operator <(const CookiePartitionKey & other) const113 bool CookiePartitionKey::operator<(const CookiePartitionKey& other) const {
114   AncestorChainBit this_bit = MaybeAncestorChainBit();
115   AncestorChainBit other_bit = other.MaybeAncestorChainBit();
116   return std::tie(site_, nonce_, this_bit) <
117          std::tie(other.site_, other.nonce_, other_bit);
118 }
119 
120 // static
121 base::expected<CookiePartitionKey::SerializedCookiePartitionKey, std::string>
Serialize(const std::optional<CookiePartitionKey> & in)122 CookiePartitionKey::Serialize(const std::optional<CookiePartitionKey>& in) {
123   if (!in) {
124     return base::ok(SerializedCookiePartitionKey(
125         base::PassKey<CookiePartitionKey>(), kEmptyCookiePartitionKey, true));
126   }
127 
128   if (!in->IsSerializeable()) {
129     return WarnAndCreateUnexpected("CookiePartitionKey is not serializeable");
130   }
131 
132   return base::ok(SerializedCookiePartitionKey(
133       base::PassKey<CookiePartitionKey>(), SerializeSchemefulSite(in->site_),
134       in->IsThirdParty()));
135 }
136 
FromNetworkIsolationKey(const NetworkIsolationKey & network_isolation_key,const SiteForCookies & site_for_cookies,const SchemefulSite & request_site,bool main_frame_navigation)137 std::optional<CookiePartitionKey> CookiePartitionKey::FromNetworkIsolationKey(
138     const NetworkIsolationKey& network_isolation_key,
139     const SiteForCookies& site_for_cookies,
140     const SchemefulSite& request_site,
141     bool main_frame_navigation) {
142   if (cookie_util::PartitionedCookiesDisabledByCommandLine()) {
143     return std::nullopt;
144   }
145 
146   const std::optional<base::UnguessableToken>& nonce =
147       network_isolation_key.GetNonce();
148 
149   // Use frame site for nonced partitions. Since the nonce is unique, this
150   // still creates a unique partition key. The reason we use the frame site is
151   // to align CookiePartitionKey's implementation of nonced partitions with
152   // StorageKey's. See https://crbug.com/1440765.
153   const std::optional<SchemefulSite>& partition_key_site =
154       nonce ? network_isolation_key.GetFrameSiteForCookiePartitionKey(
155                   NetworkIsolationKey::CookiePartitionKeyPassKey())
156             : network_isolation_key.GetTopFrameSite();
157   if (!partition_key_site) {
158     return std::nullopt;
159   }
160 
161   // When a main_frame_navigation occurs, the ancestor chain bit value should
162   // always be kSameSite, unless there is a nonce, since a main frame has no
163   // ancestor, context: crbug.com/(337206302).
164   AncestorChainBit ancestor_chain_bit;
165   if (nonce) {
166     ancestor_chain_bit = AncestorChainBit::kCrossSite;
167   } else if (main_frame_navigation) {
168     ancestor_chain_bit = AncestorChainBit::kSameSite;
169   } else if (site_for_cookies.IsNull()) {
170     ancestor_chain_bit = AncestorChainBit::kCrossSite;
171   } else {
172     ancestor_chain_bit = BoolToAncestorChainBit(
173         !site_for_cookies.IsFirstParty(request_site.GetURL()));
174   }
175 
176   return CookiePartitionKey(*partition_key_site, nonce, ancestor_chain_bit);
177 }
178 
179 // static
FromStorageKeyComponents(const SchemefulSite & site,AncestorChainBit ancestor_chain_bit,const std::optional<base::UnguessableToken> & nonce)180 std::optional<CookiePartitionKey> CookiePartitionKey::FromStorageKeyComponents(
181     const SchemefulSite& site,
182     AncestorChainBit ancestor_chain_bit,
183     const std::optional<base::UnguessableToken>& nonce) {
184   if (cookie_util::PartitionedCookiesDisabledByCommandLine()) {
185     return std::nullopt;
186   }
187   return CookiePartitionKey::FromWire(site, ancestor_chain_bit, nonce);
188 }
189 
190 // static
191 base::expected<std::optional<CookiePartitionKey>, std::string>
FromStorage(const std::string & top_level_site,bool has_cross_site_ancestor)192 CookiePartitionKey::FromStorage(const std::string& top_level_site,
193                                 bool has_cross_site_ancestor) {
194   if (top_level_site == kEmptyCookiePartitionKey) {
195     return base::ok(std::nullopt);
196   }
197 
198   base::expected<CookiePartitionKey, std::string> key = DeserializeInternal(
199       top_level_site, BoolToAncestorChainBit(has_cross_site_ancestor),
200       ParsingMode::kStrict);
201   if (!key.has_value()) {
202     DLOG(WARNING) << key.error();
203   }
204 
205   return key;
206 }
207 
208 // static
209 base::expected<CookiePartitionKey, std::string>
FromUntrustedInput(const std::string & top_level_site,bool has_cross_site_ancestor)210 CookiePartitionKey::FromUntrustedInput(const std::string& top_level_site,
211                                        bool has_cross_site_ancestor) {
212   if (top_level_site.empty()) {
213     return WarnAndCreateUnexpected("top_level_site is unexpectedly empty");
214   }
215 
216   base::expected<CookiePartitionKey, std::string> key = DeserializeInternal(
217       top_level_site, BoolToAncestorChainBit(has_cross_site_ancestor),
218       ParsingMode::kLoose);
219   if (!key.has_value()) {
220     return WarnAndCreateUnexpected(key.error());
221   }
222   return key;
223 }
224 
225 base::expected<CookiePartitionKey, std::string>
DeserializeInternal(const std::string & top_level_site,CookiePartitionKey::AncestorChainBit has_cross_site_ancestor,CookiePartitionKey::ParsingMode parsing_mode)226 CookiePartitionKey::DeserializeInternal(
227     const std::string& top_level_site,
228     CookiePartitionKey::AncestorChainBit has_cross_site_ancestor,
229     CookiePartitionKey::ParsingMode parsing_mode) {
230   if (cookie_util::PartitionedCookiesDisabledByCommandLine()) {
231     return WarnAndCreateUnexpected("Partitioned cookies are disabled");
232   }
233 
234   auto schemeful_site = SchemefulSite::Deserialize(top_level_site);
235   if (schemeful_site.opaque()) {
236     return WarnAndCreateUnexpected(
237         "Cannot deserialize opaque origin to CookiePartitionKey");
238   } else if (parsing_mode == ParsingMode::kStrict &&
239              SerializeSchemefulSite(schemeful_site) != top_level_site) {
240     return WarnAndCreateUnexpected(
241         "Cannot deserialize malformed top_level_site to CookiePartitionKey");
242   }
243   return base::ok(CookiePartitionKey(schemeful_site, std::nullopt,
244                                      has_cross_site_ancestor));
245 }
246 
IsSerializeable() const247 bool CookiePartitionKey::IsSerializeable() const {
248   // We should not try to serialize a partition key created by a renderer.
249   DCHECK(!from_script_);
250   return !site_.opaque() && !nonce_.has_value();
251 }
252 
MaybeAncestorChainBit() const253 CookiePartitionKey::AncestorChainBit CookiePartitionKey::MaybeAncestorChainBit()
254     const {
255   return ancestor_chain_enabled_ ? ancestor_chain_bit_
256                                  : AncestorChainBit::kCrossSite;
257 }
258 
operator <<(std::ostream & os,const CookiePartitionKey & cpk)259 std::ostream& operator<<(std::ostream& os, const CookiePartitionKey& cpk) {
260   os << cpk.site();
261   if (cpk.nonce().has_value()) {
262     os << ",nonced";
263   }
264   os << (cpk.IsThirdParty() ? ",cross_site" : ",same_site");
265   return os;
266 }
267 
268 }  // namespace net
269