1 // Copyright 2012 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 // Brought to you by number 42.
6
7 #ifndef NET_COOKIES_COOKIE_OPTIONS_H_
8 #define NET_COOKIES_COOKIE_OPTIONS_H_
9
10 #include <ostream>
11 #include <string>
12
13 #include "net/base/net_export.h"
14 #include "net/cookies/cookie_constants.h"
15 #include "net/cookies/cookie_inclusion_status.h"
16 #include "net/first_party_sets/same_party_context.h"
17 #include "url/gurl.h"
18
19 namespace net {
20
21 class NET_EXPORT CookieOptions {
22 public:
23
24 // Relation between the cookie and the navigational environment.
25 class NET_EXPORT SameSiteCookieContext {
26 public:
27 // CROSS_SITE to SAME_SITE_STRICT are ordered from least to most trusted
28 // environment. Don't renumber, used in histograms.
29 enum class ContextType {
30 CROSS_SITE = 0,
31 // Same rules as lax but the http method is unsafe.
32 SAME_SITE_LAX_METHOD_UNSAFE = 1,
33 SAME_SITE_LAX = 2,
34 SAME_SITE_STRICT = 3,
35
36 // Keep last, used for histograms.
37 COUNT
38 };
39
40 // Holds metadata about the factors that went into deciding the ContextType.
41 //
42 // These values may be used for recording histograms or
43 // CookieInclusionStatus warnings, but SHOULD NOT be relied
44 // upon for cookie inclusion decisions. Use only the ContextTypes for that.
45 //
46 // When adding a field, also update CompleteEquivalenceForTesting.
47 struct NET_EXPORT ContextMetadata {
48 // Possible "downgrades" for the SameSite context type, e.g. from a more
49 // trusted context to a less trusted context, as a result of some behavior
50 // change affecting the same-site calculation.
51 enum class ContextDowngradeType {
52 // Context not downgraded.
53 kNoDowngrade,
54 // Context was originally strictly same-site, was downgraded to laxly
55 // same-site.
56 kStrictToLax,
57 // Context was originally strictly same-site, was downgraded to
58 // cross-site.
59 kStrictToCross,
60 // Context was originally laxly same-site, was downgraded to cross-site.
61 kLaxToCross,
62 };
63
64 // These values are persisted to logs. Entries should not be renumbered
65 // and numeric values should never be reused.
66 // This enum is to help collect metrics for https://crbug.com/1221316.
67 // Specifically it's to help indicate how many cookies are accessed with a
68 // given redirect type in order to provide a denominator for the
69 // Cookie.CrossSiteRedirectDowngradeChangesInclusion2.* metrics.
70 // Note: for this enum the notation A->B->C means that a navigation from
71 // A to B was redirected to C. I.e.: A is the initator of the navigation
72 // and C is the final url.
73 enum class ContextRedirectTypeBug1221316 {
74 kUnset =
75 0, // Indicates this value was unused and shouldn't be read. E.x.:
76 // A javascript access means this value is meaningless.
77 kNoRedirect = 1, // There weren't any redirects. E.x.: A->B, A->A
78 kCrossSiteRedirect =
79 2, // There was a redirect but it didn't start and
80 // end at the same site or the redirect was to a different site
81 // than the site-for-cookies. E.x.: A->B->C or B->B->B when the
82 // site-for-cookies is A.
83 kPartialSameSiteRedirect =
84 3, // There was a redirect and the start and
85 // end are the same site. E.x.: A->B->A. Only this one could
86 // potentially set cross_site_redirect_downgrade.
87 kAllSameSiteRedirect = 4, // There was a redirect and all urls are the
88 // same site. E.x.:, A->A->A
89 kMaxValue = kAllSameSiteRedirect
90 };
91
92 // These values are persisted to logs. Entries should not be renumbered
93 // and numeric values should never be reused.
94 enum class HttpMethod {
95 // kUnset indicates this enum wasn't applicable in the context.
96 kUnset = -1,
97 // kUnknown indicates we were unable to convert the method string to
98 // this enum.
99 kUnknown = 0,
100 kGet = 1,
101 kHead = 2,
102 kPost = 3,
103 KPut = 4,
104 kDelete = 5,
105 kConnect = 6,
106 kOptions = 7,
107 kTrace = 8,
108 kPatch = 9,
109 kMaxValue = kPatch
110 };
111
112 // Records the type of any context downgrade due to a cross-site redirect,
113 // i.e. whether the spec change in
114 // https://github.com/httpwg/http-extensions/pull/1348 changed the result
115 // of the context calculation. Note that a lax-to-cross downgrade can only
116 // happen for response cookies, because a laxly same-site context only
117 // happens for a top-level cross-site request, which cannot be downgraded
118 // due to a cross-site redirect to a non-top-level cross-site request.
119 // This only records whether the context was downgraded, not whether the
120 // cookie's inclusion result was changed.
121 ContextDowngradeType cross_site_redirect_downgrade =
122 ContextDowngradeType::kNoDowngrade;
123
124 ContextRedirectTypeBug1221316 redirect_type_bug_1221316 =
125 ContextRedirectTypeBug1221316::kUnset;
126
127 // Records the HTTP method of requests that result in a cross-site
128 // redirect downgrade. May be kUnset if there wasn't a downgrade or if the
129 // cookie access wasn't due to a request.
130 //
131 // Note that this field is always set when there was a context
132 // downgrade but the associated histrogram is only recorded when that
133 // context downgrade results in a change in inclusion status.
134 HttpMethod http_method_bug_1221316 = HttpMethod::kUnset;
135 };
136
137 // The following three constructors apply default values for the metadata
138 // members.
SameSiteCookieContext()139 SameSiteCookieContext()
140 : SameSiteCookieContext(ContextType::CROSS_SITE,
141 ContextType::CROSS_SITE) {}
142
SameSiteCookieContext(ContextType same_site_context)143 explicit SameSiteCookieContext(ContextType same_site_context)
144 : SameSiteCookieContext(same_site_context, same_site_context) {}
145
SameSiteCookieContext(ContextType same_site_context,ContextType schemeful_same_site_context)146 SameSiteCookieContext(ContextType same_site_context,
147 ContextType schemeful_same_site_context)
148 : SameSiteCookieContext(same_site_context,
149 schemeful_same_site_context,
150 ContextMetadata(),
151 ContextMetadata()) {}
152
153 // Schemeful and schemeless context types are consistency-checked against
154 // each other, but the metadata is stored as-is (i.e. the values in
155 // `metadata` and `schemeful_metadata` may be logically inconsistent), as
156 // the metadata is not relied upon for correctness.
SameSiteCookieContext(ContextType same_site_context,ContextType schemeful_same_site_context,ContextMetadata metadata,ContextMetadata schemeful_metadata)157 SameSiteCookieContext(ContextType same_site_context,
158 ContextType schemeful_same_site_context,
159 ContextMetadata metadata,
160 ContextMetadata schemeful_metadata)
161 : context_(same_site_context),
162 schemeful_context_(schemeful_same_site_context),
163 metadata_(metadata),
164 schemeful_metadata_(schemeful_metadata) {
165 DCHECK_LE(schemeful_context_, context_);
166 }
167
168 // Convenience method which returns a SameSiteCookieContext with the most
169 // inclusive contexts. This allows access to all SameSite cookies.
170 static SameSiteCookieContext MakeInclusive();
171
172 // Convenience method which returns a SameSiteCookieContext with the most
173 // inclusive contexts for set. This allows setting all SameSite cookies.
174 static SameSiteCookieContext MakeInclusiveForSet();
175
176 // Returns the context for determining SameSite cookie inclusion.
177 ContextType GetContextForCookieInclusion() const;
178
179 // Returns the metadata describing how this context was calculated, under
180 // the currently applicable schemeful/schemeless mode.
181 // TODO(chlily): Should take the CookieAccessSemantics as well, to
182 // accurately account for the context actually used for a given cookie.
183 const ContextMetadata& GetMetadataForCurrentSchemefulMode() const;
184
185 // If you're just trying to determine if a cookie is accessible you likely
186 // want to use GetContextForCookieInclusion() which will return the correct
187 // context regardless the status of same-site features.
context()188 ContextType context() const { return context_; }
schemeful_context()189 ContextType schemeful_context() const { return schemeful_context_; }
190
191 // You probably want to use GetMetadataForCurrentSchemefulMode() instead of
192 // these getters, since that takes into account the applicable schemeful
193 // mode.
metadata()194 const ContextMetadata& metadata() const { return metadata_; }
metadata()195 ContextMetadata& metadata() { return metadata_; }
schemeful_metadata()196 const ContextMetadata& schemeful_metadata() const {
197 return schemeful_metadata_;
198 }
schemeful_metadata()199 ContextMetadata& schemeful_metadata() { return schemeful_metadata_; }
200
201 // Sets context types. Does not check for consistency between context and
202 // schemeful context. Does not touch the metadata.
203 void SetContextTypesForTesting(ContextType context_type,
204 ContextType schemeful_context_type);
205
206 // Returns whether the context types and all fields of the metadata structs
207 // are the same.
208 bool CompleteEquivalenceForTesting(
209 const SameSiteCookieContext& other) const;
210
211 // Equality operators disregard any metadata! (Only the context types are
212 // compared, not how they were computed.)
213 NET_EXPORT friend bool operator==(
214 const CookieOptions::SameSiteCookieContext& lhs,
215 const CookieOptions::SameSiteCookieContext& rhs);
216 NET_EXPORT friend bool operator!=(
217 const CookieOptions::SameSiteCookieContext& lhs,
218 const CookieOptions::SameSiteCookieContext& rhs);
219
220 private:
221 ContextType context_;
222 ContextType schemeful_context_;
223
224 ContextMetadata metadata_;
225 ContextMetadata schemeful_metadata_;
226 };
227
228 // Creates a CookieOptions object which:
229 //
230 // * Excludes HttpOnly cookies
231 // * Excludes SameSite cookies
232 // * Updates last-accessed time.
233 // * Does not report excluded cookies in APIs that can do so.
234 // * Excludes SameParty cookies.
235 //
236 // These settings can be altered by calling:
237 //
238 // * |set_{include,exclude}_httponly()|
239 // * |set_same_site_cookie_context()|
240 // * |set_do_not_update_access_time()|
241 // * |set_same_party_cookie_context_type()|
242 CookieOptions();
243 CookieOptions(const CookieOptions& other);
244 CookieOptions(CookieOptions&& other);
245 ~CookieOptions();
246
247 CookieOptions& operator=(const CookieOptions&);
248 CookieOptions& operator=(CookieOptions&&);
249
set_exclude_httponly()250 void set_exclude_httponly() { exclude_httponly_ = true; }
set_include_httponly()251 void set_include_httponly() { exclude_httponly_ = false; }
exclude_httponly()252 bool exclude_httponly() const { return exclude_httponly_; }
253
254 // How trusted is the current browser environment when it comes to accessing
255 // SameSite cookies. Default is not trusted, e.g. CROSS_SITE.
set_same_site_cookie_context(const SameSiteCookieContext & context)256 void set_same_site_cookie_context(const SameSiteCookieContext& context) {
257 same_site_cookie_context_ = context;
258 }
259
same_site_cookie_context()260 const SameSiteCookieContext& same_site_cookie_context() const {
261 return same_site_cookie_context_;
262 }
263
set_update_access_time()264 void set_update_access_time() { update_access_time_ = true; }
set_do_not_update_access_time()265 void set_do_not_update_access_time() { update_access_time_ = false; }
update_access_time()266 bool update_access_time() const { return update_access_time_; }
267
set_return_excluded_cookies()268 void set_return_excluded_cookies() { return_excluded_cookies_ = true; }
unset_return_excluded_cookies()269 void unset_return_excluded_cookies() { return_excluded_cookies_ = false; }
return_excluded_cookies()270 bool return_excluded_cookies() const { return return_excluded_cookies_; }
271
set_same_party_context(const SamePartyContext & context)272 void set_same_party_context(const SamePartyContext& context) {
273 same_party_context_ = context;
274 }
same_party_context()275 const SamePartyContext& same_party_context() const {
276 return same_party_context_;
277 }
278
279 // Getter/setter of |full_party_context_size_| for logging purposes.
set_full_party_context_size(uint32_t len)280 void set_full_party_context_size(uint32_t len) {
281 full_party_context_size_ = len;
282 }
full_party_context_size()283 uint32_t full_party_context_size() const { return full_party_context_size_; }
284
set_is_in_nontrivial_first_party_set(bool is_member)285 void set_is_in_nontrivial_first_party_set(bool is_member) {
286 is_in_nontrivial_first_party_set_ = is_member;
287 }
is_in_nontrivial_first_party_set()288 bool is_in_nontrivial_first_party_set() const {
289 return is_in_nontrivial_first_party_set_;
290 }
291
292 // Convenience method for where you need a CookieOptions that will
293 // work for getting/setting all types of cookies, including HttpOnly and
294 // SameSite cookies. Also specifies not to update the access time, because
295 // usually this is done to get all the cookies to check that they are correct,
296 // including the creation time. This basically makes a CookieOptions that is
297 // the opposite of the default CookieOptions.
298 static CookieOptions MakeAllInclusive();
299
300 private:
301 // Keep default values in sync with
302 // content/public/common/cookie_manager.mojom.
303 bool exclude_httponly_ = true;
304 SameSiteCookieContext same_site_cookie_context_;
305 bool update_access_time_ = true;
306 bool return_excluded_cookies_ = false;
307
308 SamePartyContext same_party_context_;
309
310 // The size of the isolation_info.party_context plus the top-frame site.
311 // Stored for logging purposes.
312 uint32_t full_party_context_size_ = 0;
313 // Whether the site requesting cookie access (as opposed to e.g. the
314 // `site_for_cookies`) is a member (or owner) of a nontrivial First-Party
315 // Set.
316 // This is included here temporarily, for the purpose of ignoring SameParty
317 // for sites that are not participating in the Origin Trial.
318 // TODO(https://crbug.com/1163990): remove this field.
319 bool is_in_nontrivial_first_party_set_ = false;
320 };
321
322 NET_EXPORT bool operator==(
323 const CookieOptions::SameSiteCookieContext::ContextMetadata& lhs,
324 const CookieOptions::SameSiteCookieContext::ContextMetadata& rhs);
325 NET_EXPORT bool operator!=(
326 const CookieOptions::SameSiteCookieContext::ContextMetadata& lhs,
327 const CookieOptions::SameSiteCookieContext::ContextMetadata& rhs);
328
329 // Allows gtest to print more helpful error messages instead of printing hex.
330 // (No need to null-check `os` because we can assume gtest will properly pass a
331 // non-null pointer, and it is dereferenced immediately anyway.)
PrintTo(CookieOptions::SameSiteCookieContext::ContextType ct,std::ostream * os)332 inline void PrintTo(CookieOptions::SameSiteCookieContext::ContextType ct,
333 std::ostream* os) {
334 *os << static_cast<int>(ct);
335 }
336
PrintTo(const CookieOptions::SameSiteCookieContext::ContextMetadata & m,std::ostream * os)337 inline void PrintTo(
338 const CookieOptions::SameSiteCookieContext::ContextMetadata& m,
339 std::ostream* os) {
340 *os << "{";
341 *os << " cross_site_redirect_downgrade: "
342 << static_cast<int>(m.cross_site_redirect_downgrade);
343 *os << ", redirect_type_bug_1221316: "
344 << static_cast<int>(m.redirect_type_bug_1221316);
345 *os << ", http_method_bug_1221316: "
346 << static_cast<int>(m.http_method_bug_1221316);
347 *os << " }";
348 }
349
PrintTo(const CookieOptions::SameSiteCookieContext & sscc,std::ostream * os)350 inline void PrintTo(const CookieOptions::SameSiteCookieContext& sscc,
351 std::ostream* os) {
352 *os << "{ context: ";
353 PrintTo(sscc.context(), os);
354 *os << ", schemeful_context: ";
355 PrintTo(sscc.schemeful_context(), os);
356 *os << ", metadata: ";
357 PrintTo(sscc.metadata(), os);
358 *os << ", schemeful_metadata: ";
359 PrintTo(sscc.schemeful_metadata(), os);
360 *os << " }";
361 }
362
363 } // namespace net
364
365 #endif // NET_COOKIES_COOKIE_OPTIONS_H_
366