• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 // Portions of this code based on Mozilla:
6 //   (netwerk/cookie/src/nsCookieService.cpp)
7 /* ***** BEGIN LICENSE BLOCK *****
8  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
9  *
10  * The contents of this file are subject to the Mozilla Public License Version
11  * 1.1 (the "License"); you may not use this file except in compliance with
12  * the License. You may obtain a copy of the License at
13  * http://www.mozilla.org/MPL/
14  *
15  * Software distributed under the License is distributed on an "AS IS" basis,
16  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
17  * for the specific language governing rights and limitations under the
18  * License.
19  *
20  * The Original Code is mozilla.org code.
21  *
22  * The Initial Developer of the Original Code is
23  * Netscape Communications Corporation.
24  * Portions created by the Initial Developer are Copyright (C) 2003
25  * the Initial Developer. All Rights Reserved.
26  *
27  * Contributor(s):
28  *   Daniel Witte (dwitte@stanford.edu)
29  *   Michiel van Leeuwen (mvl@exedo.nl)
30  *
31  * Alternatively, the contents of this file may be used under the terms of
32  * either the GNU General Public License Version 2 or later (the "GPL"), or
33  * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
34  * in which case the provisions of the GPL or the LGPL are applicable instead
35  * of those above. If you wish to allow use of your version of this file only
36  * under the terms of either the GPL or the LGPL, and not to allow others to
37  * use your version of this file under the terms of the MPL, indicate your
38  * decision by deleting the provisions above and replace them with the notice
39  * and other provisions required by the GPL or the LGPL. If you do not delete
40  * the provisions above, a recipient may use your version of this file under
41  * the terms of any one of the MPL, the GPL or the LGPL.
42  *
43  * ***** END LICENSE BLOCK ***** */
44 
45 #include "net/cookies/cookie_monster.h"
46 
47 #include <functional>
48 #include <numeric>
49 #include <set>
50 #include <utility>
51 
52 #include "base/containers/flat_map.h"
53 #include "base/feature_list.h"
54 #include "base/functional/bind.h"
55 #include "base/functional/callback.h"
56 #include "base/location.h"
57 #include "base/logging.h"
58 #include "base/metrics/field_trial.h"
59 #include "base/metrics/histogram_functions.h"
60 #include "base/metrics/histogram_macros.h"
61 #include "base/ranges/algorithm.h"
62 #include "base/strings/strcat.h"
63 #include "base/strings/string_piece.h"
64 #include "base/strings/string_util.h"
65 #include "base/strings/stringprintf.h"
66 #include "base/task/single_thread_task_runner.h"
67 #include "base/threading/thread_checker.h"
68 #include "net/base/features.h"
69 #include "net/base/isolation_info.h"
70 #include "net/base/registry_controlled_domains/registry_controlled_domain.h"
71 #include "net/base/schemeful_site.h"
72 #include "net/base/url_util.h"
73 #include "net/cookies/canonical_cookie.h"
74 #include "net/cookies/cookie_constants.h"
75 #include "net/cookies/cookie_monster_change_dispatcher.h"
76 #include "net/cookies/cookie_monster_netlog_params.h"
77 #include "net/cookies/cookie_partition_key.h"
78 #include "net/cookies/cookie_partition_key_collection.h"
79 #include "net/cookies/cookie_util.h"
80 #include "net/cookies/parsed_cookie.h"
81 #include "net/http/http_util.h"
82 #include "net/log/net_log.h"
83 #include "net/log/net_log_values.h"
84 #include "third_party/abseil-cpp/absl/types/optional.h"
85 #include "url/origin.h"
86 #include "url/third_party/mozilla/url_parse.h"
87 #include "url/url_canon.h"
88 #include "url/url_constants.h"
89 
90 using base::Time;
91 using base::TimeTicks;
92 using TimeRange = net::CookieDeletionInfo::TimeRange;
93 
94 // In steady state, most cookie requests can be satisfied by the in memory
95 // cookie monster store. If the cookie request cannot be satisfied by the in
96 // memory store, the relevant cookies must be fetched from the persistent
97 // store. The task is queued in CookieMonster::tasks_pending_ if it requires
98 // all cookies to be loaded from the backend, or tasks_pending_for_key_ if it
99 // only requires all cookies associated with an eTLD+1.
100 //
101 // On the browser critical paths (e.g. for loading initial web pages in a
102 // session restore) it may take too long to wait for the full load. If a cookie
103 // request is for a specific URL, DoCookieCallbackForURL is called, which
104 // triggers a priority load if the key is not loaded yet by calling
105 // PersistentCookieStore::LoadCookiesForKey. The request is queued in
106 // CookieMonster::tasks_pending_for_key_ and executed upon receiving
107 // notification of key load completion via CookieMonster::OnKeyLoaded(). If
108 // multiple requests for the same eTLD+1 are received before key load
109 // completion, only the first request calls
110 // PersistentCookieStore::LoadCookiesForKey, all subsequent requests are queued
111 // in CookieMonster::tasks_pending_for_key_ and executed upon receiving
112 // notification of key load completion triggered by the first request for the
113 // same eTLD+1.
114 
115 static const int kDaysInTenYears = 10 * 365;
116 static const int kMinutesInTenYears = kDaysInTenYears * 24 * 60;
117 
118 namespace {
119 
MaybeRunDeleteCallback(base::WeakPtr<net::CookieMonster> cookie_monster,base::OnceClosure callback)120 void MaybeRunDeleteCallback(base::WeakPtr<net::CookieMonster> cookie_monster,
121                             base::OnceClosure callback) {
122   if (cookie_monster && callback)
123     std::move(callback).Run();
124 }
125 
126 template <typename CB, typename... R>
MaybeRunCookieCallback(base::OnceCallback<CB> callback,R &&...result)127 void MaybeRunCookieCallback(base::OnceCallback<CB> callback, R&&... result) {
128   if (callback) {
129     std::move(callback).Run(std::forward<R>(result)...);
130   }
131 }
132 
133 // Anonymous and Fenced Frame uses a CookiePartitionKey with a nonce. In these
134 // contexts, access to unpartitioned cookie is not granted.
135 //
136 // This returns true if the |list| of key should include unpartitioned cookie in
137 // GetCookie...().
IncludeUnpartitionedCookies(const net::CookiePartitionKeyCollection & list)138 bool IncludeUnpartitionedCookies(
139     const net::CookiePartitionKeyCollection& list) {
140   if (list.IsEmpty() || list.ContainsAllKeys())
141     return true;
142 
143   for (const net::CookiePartitionKey& key : list.PartitionKeys()) {
144     if (!key.nonce())
145       return true;
146   }
147   return false;
148 }
149 
NameValueSizeBytes(const net::CanonicalCookie & cc)150 size_t NameValueSizeBytes(const net::CanonicalCookie& cc) {
151   base::CheckedNumeric<size_t> name_value_pair_size = cc.Name().size();
152   name_value_pair_size += cc.Value().size();
153   DCHECK(name_value_pair_size.IsValid());
154   return name_value_pair_size.ValueOrDie();
155 }
156 
NumBytesInCookieMapForKey(const net::CookieMonster::CookieMap & cookie_map,const std::string & key)157 size_t NumBytesInCookieMapForKey(
158     const net::CookieMonster::CookieMap& cookie_map,
159     const std::string& key) {
160   size_t result = 0;
161   auto range = cookie_map.equal_range(key);
162   for (auto it = range.first; it != range.second; ++it) {
163     result += NameValueSizeBytes(*it->second);
164   }
165   return result;
166 }
167 
NumBytesInCookieItVector(const net::CookieMonster::CookieItVector & cookie_its)168 size_t NumBytesInCookieItVector(
169     const net::CookieMonster::CookieItVector& cookie_its) {
170   size_t result = 0;
171   for (const auto& it : cookie_its) {
172     result += NameValueSizeBytes(*it->second);
173   }
174   return result;
175 }
176 
177 }  // namespace
178 
179 namespace net {
180 
181 // See comments at declaration of these variables in cookie_monster.h
182 // for details.
183 const size_t CookieMonster::kDomainMaxCookies = 180;
184 const size_t CookieMonster::kDomainPurgeCookies = 30;
185 const size_t CookieMonster::kMaxCookies = 3300;
186 const size_t CookieMonster::kPurgeCookies = 300;
187 
188 const size_t CookieMonster::kMaxDomainPurgedKeys = 100;
189 
190 const size_t CookieMonster::kPerPartitionDomainMaxCookieBytes = 10240;
191 const size_t CookieMonster::kPerPartitionDomainMaxCookies = 180;
192 
193 const size_t CookieMonster::kDomainCookiesQuotaLow = 30;
194 const size_t CookieMonster::kDomainCookiesQuotaMedium = 50;
195 const size_t CookieMonster::kDomainCookiesQuotaHigh =
196     kDomainMaxCookies - kDomainPurgeCookies - kDomainCookiesQuotaLow -
197     kDomainCookiesQuotaMedium;
198 
199 const int CookieMonster::kSafeFromGlobalPurgeDays = 30;
200 
201 namespace {
202 
ContainsControlCharacter(const std::string & s)203 bool ContainsControlCharacter(const std::string& s) {
204   return base::ranges::any_of(s, &HttpUtil::IsControlChar);
205 }
206 
207 typedef std::vector<CanonicalCookie*> CanonicalCookieVector;
208 
209 // Default minimum delay after updating a cookie's LastAccessDate before we
210 // will update it again.
211 const int kDefaultAccessUpdateThresholdSeconds = 60;
212 
213 // Comparator to sort cookies from highest creation date to lowest
214 // creation date.
215 struct OrderByCreationTimeDesc {
operator ()net::__anon841bba770211::OrderByCreationTimeDesc216   bool operator()(const CookieMonster::CookieMap::iterator& a,
217                   const CookieMonster::CookieMap::iterator& b) const {
218     return a->second->CreationDate() > b->second->CreationDate();
219   }
220 };
221 
LRACookieSorter(const CookieMonster::CookieMap::iterator & it1,const CookieMonster::CookieMap::iterator & it2)222 bool LRACookieSorter(const CookieMonster::CookieMap::iterator& it1,
223                      const CookieMonster::CookieMap::iterator& it2) {
224   if (it1->second->LastAccessDate() != it2->second->LastAccessDate())
225     return it1->second->LastAccessDate() < it2->second->LastAccessDate();
226 
227   // Ensure stability for == last access times by falling back to creation.
228   return it1->second->CreationDate() < it2->second->CreationDate();
229 }
230 
231 // For a CookieItVector iterator range [|it_begin|, |it_end|),
232 // sorts the first |num_sort| elements by LastAccessDate().
SortLeastRecentlyAccessed(CookieMonster::CookieItVector::iterator it_begin,CookieMonster::CookieItVector::iterator it_end,size_t num_sort)233 void SortLeastRecentlyAccessed(CookieMonster::CookieItVector::iterator it_begin,
234                                CookieMonster::CookieItVector::iterator it_end,
235                                size_t num_sort) {
236   DCHECK_LE(static_cast<int>(num_sort), it_end - it_begin);
237   std::partial_sort(it_begin, it_begin + num_sort, it_end, LRACookieSorter);
238 }
239 
240 // Given a single cookie vector |cookie_its|, pushs all of the secure cookies in
241 // |cookie_its| into |secure_cookie_its| and all of the non-secure cookies into
242 // |non_secure_cookie_its|. Both |secure_cookie_its| and |non_secure_cookie_its|
243 // must be non-NULL.
SplitCookieVectorIntoSecureAndNonSecure(const CookieMonster::CookieItVector & cookie_its,CookieMonster::CookieItVector * secure_cookie_its,CookieMonster::CookieItVector * non_secure_cookie_its)244 void SplitCookieVectorIntoSecureAndNonSecure(
245     const CookieMonster::CookieItVector& cookie_its,
246     CookieMonster::CookieItVector* secure_cookie_its,
247     CookieMonster::CookieItVector* non_secure_cookie_its) {
248   DCHECK(secure_cookie_its && non_secure_cookie_its);
249   for (const auto& curit : cookie_its) {
250     if (curit->second->IsSecure())
251       secure_cookie_its->push_back(curit);
252     else
253       non_secure_cookie_its->push_back(curit);
254   }
255 }
256 
LowerBoundAccessDateComparator(const CookieMonster::CookieMap::iterator it,const Time & access_date)257 bool LowerBoundAccessDateComparator(const CookieMonster::CookieMap::iterator it,
258                                     const Time& access_date) {
259   return it->second->LastAccessDate() < access_date;
260 }
261 
262 // For a CookieItVector iterator range [|it_begin|, |it_end|)
263 // from a CookieItVector sorted by LastAccessDate(), returns the
264 // first iterator with access date >= |access_date|, or cookie_its_end if this
265 // holds for all.
LowerBoundAccessDate(const CookieMonster::CookieItVector::iterator its_begin,const CookieMonster::CookieItVector::iterator its_end,const Time & access_date)266 CookieMonster::CookieItVector::iterator LowerBoundAccessDate(
267     const CookieMonster::CookieItVector::iterator its_begin,
268     const CookieMonster::CookieItVector::iterator its_end,
269     const Time& access_date) {
270   return std::lower_bound(its_begin, its_end, access_date,
271                           LowerBoundAccessDateComparator);
272 }
273 
274 // Mapping between DeletionCause and CookieChangeCause; the
275 // mapping also provides a boolean that specifies whether or not an
276 // OnCookieChange notification ought to be generated.
277 typedef struct ChangeCausePair_struct {
278   CookieChangeCause cause;
279   bool notify;
280 } ChangeCausePair;
281 const ChangeCausePair kChangeCauseMapping[] = {
282     // DELETE_COOKIE_EXPLICIT
283     {CookieChangeCause::EXPLICIT, true},
284     // DELETE_COOKIE_OVERWRITE
285     {CookieChangeCause::OVERWRITE, true},
286     // DELETE_COOKIE_EXPIRED
287     {CookieChangeCause::EXPIRED, true},
288     // DELETE_COOKIE_EVICTED
289     {CookieChangeCause::EVICTED, true},
290     // DELETE_COOKIE_DUPLICATE_IN_BACKING_STORE
291     {CookieChangeCause::EXPLICIT, false},
292     // DELETE_COOKIE_DONT_RECORD
293     {CookieChangeCause::EXPLICIT, false},
294     // DELETE_COOKIE_EVICTED_DOMAIN
295     {CookieChangeCause::EVICTED, true},
296     // DELETE_COOKIE_EVICTED_GLOBAL
297     {CookieChangeCause::EVICTED, true},
298     // DELETE_COOKIE_EVICTED_DOMAIN_PRE_SAFE
299     {CookieChangeCause::EVICTED, true},
300     // DELETE_COOKIE_EVICTED_DOMAIN_POST_SAFE
301     {CookieChangeCause::EVICTED, true},
302     // DELETE_COOKIE_EXPIRED_OVERWRITE
303     {CookieChangeCause::EXPIRED_OVERWRITE, true},
304     // DELETE_COOKIE_CONTROL_CHAR
305     {CookieChangeCause::EVICTED, true},
306     // DELETE_COOKIE_NON_SECURE
307     {CookieChangeCause::EVICTED, true},
308     // DELETE_COOKIE_EVICTED_PER_PARTITION_DOMAIN
309     {CookieChangeCause::EVICTED, true},
310     // DELETE_COOKIE_LAST_ENTRY
311     {CookieChangeCause::EXPLICIT, false}};
312 
IsCookieEligibleForEviction(CookiePriority current_priority_level,bool protect_secure_cookies,const CanonicalCookie * cookie)313 bool IsCookieEligibleForEviction(CookiePriority current_priority_level,
314                                  bool protect_secure_cookies,
315                                  const CanonicalCookie* cookie) {
316   if (cookie->Priority() == current_priority_level && protect_secure_cookies)
317     return !cookie->IsSecure();
318 
319   return cookie->Priority() == current_priority_level;
320 }
321 
CountCookiesForPossibleDeletion(CookiePriority priority,const CookieMonster::CookieItVector * cookies,bool protect_secure_cookies)322 size_t CountCookiesForPossibleDeletion(
323     CookiePriority priority,
324     const CookieMonster::CookieItVector* cookies,
325     bool protect_secure_cookies) {
326   size_t cookies_count = 0U;
327   for (const auto& cookie : *cookies) {
328     if (cookie->second->Priority() == priority) {
329       if (!protect_secure_cookies || cookie->second->IsSecure())
330         cookies_count++;
331     }
332   }
333   return cookies_count;
334 }
335 
336 // Records minutes until the expiration date of a cookie to the appropriate
337 // histogram. Only histograms cookies that have an expiration date (i.e. are
338 // persistent).
HistogramExpirationDuration(const CanonicalCookie & cookie,base::Time creation_time)339 void HistogramExpirationDuration(const CanonicalCookie& cookie,
340                                  base::Time creation_time) {
341   if (!cookie.IsPersistent())
342     return;
343 
344   int expiration_duration_minutes =
345       (cookie.ExpiryDate() - creation_time).InMinutes();
346   if (cookie.IsSecure()) {
347     UMA_HISTOGRAM_CUSTOM_COUNTS("Cookie.ExpirationDurationMinutesSecure",
348                                 expiration_duration_minutes, 1,
349                                 kMinutesInTenYears, 50);
350   } else {
351     UMA_HISTOGRAM_CUSTOM_COUNTS("Cookie.ExpirationDurationMinutesNonSecure",
352                                 expiration_duration_minutes, 1,
353                                 kMinutesInTenYears, 50);
354   }
355   // The proposed rfc6265bis sets an upper limit on Expires/Max-Age attribute
356   // values of 400 days. We need to study the impact this change would have:
357   // https://httpwg.org/http-extensions/draft-ietf-httpbis-rfc6265bis.html
358   int expiration_duration_days = (cookie.ExpiryDate() - creation_time).InDays();
359   if (expiration_duration_days > 400) {
360     UMA_HISTOGRAM_CUSTOM_COUNTS("Cookie.ExpirationDuration400DaysGT",
361                                 expiration_duration_days, 401, kDaysInTenYears,
362                                 100);
363   } else {
364     UMA_HISTOGRAM_CUSTOM_COUNTS("Cookie.ExpirationDuration400DaysLTE",
365                                 expiration_duration_days, 1, 400, 50);
366   }
367 }
368 
369 }  // namespace
370 
CookieMonster(scoped_refptr<PersistentCookieStore> store,NetLog * net_log)371 CookieMonster::CookieMonster(scoped_refptr<PersistentCookieStore> store,
372                              NetLog* net_log)
373     : CookieMonster(std::move(store),
374                     base::Seconds(kDefaultAccessUpdateThresholdSeconds),
375                     net_log) {}
376 
CookieMonster(scoped_refptr<PersistentCookieStore> store,base::TimeDelta last_access_threshold,NetLog * net_log)377 CookieMonster::CookieMonster(scoped_refptr<PersistentCookieStore> store,
378                              base::TimeDelta last_access_threshold,
379                              NetLog* net_log)
380     : same_party_attribute_enabled_(base::FeatureList::IsEnabled(
381           net::features::kSamePartyAttributeEnabled)),
382       change_dispatcher_(this, same_party_attribute_enabled_),
383       net_log_(NetLogWithSource::Make(net_log, NetLogSourceType::COOKIE_STORE)),
384       store_(std::move(store)),
385       last_access_threshold_(last_access_threshold),
386       last_statistic_record_time_(base::Time::Now()) {
387   cookieable_schemes_.insert(
388       cookieable_schemes_.begin(), kDefaultCookieableSchemes,
389       kDefaultCookieableSchemes + kDefaultCookieableSchemesCount);
390   net_log_.BeginEvent(NetLogEventType::COOKIE_STORE_ALIVE, [&] {
391     return NetLogCookieMonsterConstructorParams(store_ != nullptr);
392   });
393 }
394 
395 // Asynchronous CookieMonster API
396 
FlushStore(base::OnceClosure callback)397 void CookieMonster::FlushStore(base::OnceClosure callback) {
398   DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
399 
400   if (initialized_ && store_.get()) {
401     store_->Flush(std::move(callback));
402   } else if (callback) {
403     base::SingleThreadTaskRunner::GetCurrentDefault()->PostTask(
404         FROM_HERE, std::move(callback));
405   }
406 }
407 
SetForceKeepSessionState()408 void CookieMonster::SetForceKeepSessionState() {
409   DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
410 
411   if (store_)
412     store_->SetForceKeepSessionState();
413 }
414 
SetAllCookiesAsync(const CookieList & list,SetCookiesCallback callback)415 void CookieMonster::SetAllCookiesAsync(const CookieList& list,
416                                        SetCookiesCallback callback) {
417   DoCookieCallback(base::BindOnce(
418       // base::Unretained is safe as DoCookieCallback stores
419       // the callback on |*this|, so the callback will not outlive
420       // the object.
421       &CookieMonster::SetAllCookies, base::Unretained(this), list,
422       std::move(callback)));
423 }
424 
SetCanonicalCookieAsync(std::unique_ptr<CanonicalCookie> cookie,const GURL & source_url,const CookieOptions & options,SetCookiesCallback callback,absl::optional<CookieAccessResult> cookie_access_result)425 void CookieMonster::SetCanonicalCookieAsync(
426     std::unique_ptr<CanonicalCookie> cookie,
427     const GURL& source_url,
428     const CookieOptions& options,
429     SetCookiesCallback callback,
430     absl::optional<CookieAccessResult> cookie_access_result) {
431   DCHECK(cookie->IsCanonical());
432 
433   std::string domain = cookie->Domain();
434   DoCookieCallbackForHostOrDomain(
435       base::BindOnce(
436           // base::Unretained is safe as DoCookieCallbackForHostOrDomain stores
437           // the callback on |*this|, so the callback will not outlive
438           // the object.
439           &CookieMonster::SetCanonicalCookie, base::Unretained(this),
440           std::move(cookie), source_url, options, std::move(callback),
441           std::move(cookie_access_result)),
442       domain);
443 }
444 
GetCookieListWithOptionsAsync(const GURL & url,const CookieOptions & options,const CookiePartitionKeyCollection & cookie_partition_key_collection,GetCookieListCallback callback)445 void CookieMonster::GetCookieListWithOptionsAsync(
446     const GURL& url,
447     const CookieOptions& options,
448     const CookiePartitionKeyCollection& cookie_partition_key_collection,
449     GetCookieListCallback callback) {
450   DoCookieCallbackForURL(
451       base::BindOnce(
452           // base::Unretained is safe as DoCookieCallbackForURL stores
453           // the callback on |*this|, so the callback will not outlive
454           // the object.
455           &CookieMonster::GetCookieListWithOptions, base::Unretained(this), url,
456           options, cookie_partition_key_collection, std::move(callback)),
457       url);
458 }
459 
GetAllCookiesAsync(GetAllCookiesCallback callback)460 void CookieMonster::GetAllCookiesAsync(GetAllCookiesCallback callback) {
461   DoCookieCallback(base::BindOnce(
462       // base::Unretained is safe as DoCookieCallback stores
463       // the callback on |*this|, so the callback will not outlive
464       // the object.
465       &CookieMonster::GetAllCookies, base::Unretained(this),
466       std::move(callback)));
467 }
468 
GetAllCookiesWithAccessSemanticsAsync(GetAllCookiesWithAccessSemanticsCallback callback)469 void CookieMonster::GetAllCookiesWithAccessSemanticsAsync(
470     GetAllCookiesWithAccessSemanticsCallback callback) {
471   DoCookieCallback(base::BindOnce(
472       // base::Unretained is safe as DoCookieCallback stores
473       // the callback on |*this|, so the callback will not outlive
474       // the object.
475       &CookieMonster::GetAllCookies, base::Unretained(this),
476       base::BindOnce(&CookieMonster::AttachAccessSemanticsListForCookieList,
477                      base::Unretained(this), std::move(callback))));
478 }
479 
DeleteCanonicalCookieAsync(const CanonicalCookie & cookie,DeleteCallback callback)480 void CookieMonster::DeleteCanonicalCookieAsync(const CanonicalCookie& cookie,
481                                                DeleteCallback callback) {
482   DoCookieCallback(base::BindOnce(
483       // base::Unretained is safe as DoCookieCallback stores
484       // the callback on |*this|, so the callback will not outlive
485       // the object.
486       &CookieMonster::DeleteCanonicalCookie, base::Unretained(this), cookie,
487       std::move(callback)));
488 }
489 
DeleteAllCreatedInTimeRangeAsync(const TimeRange & creation_range,DeleteCallback callback)490 void CookieMonster::DeleteAllCreatedInTimeRangeAsync(
491     const TimeRange& creation_range,
492     DeleteCallback callback) {
493   DoCookieCallback(base::BindOnce(
494       // base::Unretained is safe as DoCookieCallback stores
495       // the callback on |*this|, so the callback will not outlive
496       // the object.
497       &CookieMonster::DeleteAllCreatedInTimeRange, base::Unretained(this),
498       creation_range, std::move(callback)));
499 }
500 
DeleteAllMatchingInfoAsync(CookieDeletionInfo delete_info,DeleteCallback callback)501 void CookieMonster::DeleteAllMatchingInfoAsync(CookieDeletionInfo delete_info,
502                                                DeleteCallback callback) {
503   auto cookie_matcher =
504       base::BindRepeating(&CookieMonster::MatchCookieDeletionInfo,
505                           base::Unretained(this), std::move(delete_info));
506 
507   DoCookieCallback(base::BindOnce(
508       // base::Unretained is safe as DoCookieCallback stores
509       // the callback on |*this|, so the callback will not outlive
510       // the object.
511       &CookieMonster::DeleteMatchingCookies, base::Unretained(this),
512       std::move(cookie_matcher), DELETE_COOKIE_EXPLICIT, std::move(callback)));
513 }
514 
DeleteSessionCookiesAsync(CookieStore::DeleteCallback callback)515 void CookieMonster::DeleteSessionCookiesAsync(
516     CookieStore::DeleteCallback callback) {
517   auto session_cookie_matcher =
518       base::BindRepeating([](const net::CanonicalCookie& cookie) {
519         return !cookie.IsPersistent();
520       });
521   DoCookieCallback(base::BindOnce(
522       // base::Unretained is safe as DoCookieCallback stores
523       // the callback on |*this|, so the callback will not outlive
524       // the object.
525       &CookieMonster::DeleteMatchingCookies, base::Unretained(this),
526       std::move(session_cookie_matcher), DELETE_COOKIE_EXPIRED,
527       std::move(callback)));
528 }
529 
DeleteMatchingCookiesAsync(CookieStore::DeletePredicate predicate,CookieStore::DeleteCallback callback)530 void CookieMonster::DeleteMatchingCookiesAsync(
531     CookieStore::DeletePredicate predicate,
532     CookieStore::DeleteCallback callback) {
533   DoCookieCallback(base::BindOnce(
534       // base::Unretained is safe as DoCookieCallback stores
535       // the callback on |*this|, so the callback will not outlive
536       // the object.
537       &CookieMonster::DeleteMatchingCookies, base::Unretained(this),
538       std::move(predicate), DELETE_COOKIE_EXPLICIT, std::move(callback)));
539 }
540 
SetCookieableSchemes(const std::vector<std::string> & schemes,SetCookieableSchemesCallback callback)541 void CookieMonster::SetCookieableSchemes(
542     const std::vector<std::string>& schemes,
543     SetCookieableSchemesCallback callback) {
544   DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
545 
546   // Calls to this method will have no effect if made after a WebView or
547   // CookieManager instance has been created.
548   if (initialized_) {
549     MaybeRunCookieCallback(std::move(callback), false);
550     return;
551   }
552 
553   cookieable_schemes_ = schemes;
554   MaybeRunCookieCallback(std::move(callback), true);
555 }
556 
557 // This function must be called before the CookieMonster is used.
SetPersistSessionCookies(bool persist_session_cookies)558 void CookieMonster::SetPersistSessionCookies(bool persist_session_cookies) {
559   DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
560   DCHECK(!initialized_);
561   net_log_.AddEntryWithBoolParams(
562       NetLogEventType::COOKIE_STORE_SESSION_PERSISTENCE, NetLogEventPhase::NONE,
563       "persistence", persist_session_cookies);
564   persist_session_cookies_ = persist_session_cookies;
565 }
566 
567 const char* const CookieMonster::kDefaultCookieableSchemes[] = {"http", "https",
568                                                                 "ws", "wss"};
569 const int CookieMonster::kDefaultCookieableSchemesCount =
570     std::size(kDefaultCookieableSchemes);
571 
GetChangeDispatcher()572 CookieChangeDispatcher& CookieMonster::GetChangeDispatcher() {
573   return change_dispatcher_;
574 }
575 
~CookieMonster()576 CookieMonster::~CookieMonster() {
577   DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
578   net_log_.EndEvent(NetLogEventType::COOKIE_STORE_ALIVE);
579 }
580 
581 // static
CookieSorter(const CanonicalCookie * cc1,const CanonicalCookie * cc2)582 bool CookieMonster::CookieSorter(const CanonicalCookie* cc1,
583                                  const CanonicalCookie* cc2) {
584   // Mozilla sorts on the path length (longest first), and then it sorts by
585   // creation time (oldest first).  The RFC says the sort order for the domain
586   // attribute is undefined.
587   if (cc1->Path().length() == cc2->Path().length())
588     return cc1->CreationDate() < cc2->CreationDate();
589   return cc1->Path().length() > cc2->Path().length();
590 }
591 
GetAllCookies(GetAllCookiesCallback callback)592 void CookieMonster::GetAllCookies(GetAllCookiesCallback callback) {
593   DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
594 
595   // This function is being called to scrape the cookie list for management UI
596   // or similar.  We shouldn't show expired cookies in this list since it will
597   // just be confusing to users, and this function is called rarely enough (and
598   // is already slow enough) that it's OK to take the time to garbage collect
599   // the expired cookies now.
600   //
601   // Note that this does not prune cookies to be below our limits (if we've
602   // exceeded them) the way that calling GarbageCollect() would.
603   GarbageCollectExpired(
604       Time::Now(), CookieMapItPair(cookies_.begin(), cookies_.end()), nullptr);
605   GarbageCollectAllExpiredPartitionedCookies(Time::Now());
606 
607   // Copy the CanonicalCookie pointers from the map so that we can use the same
608   // sorter as elsewhere, then copy the result out.
609   std::vector<CanonicalCookie*> cookie_ptrs;
610   cookie_ptrs.reserve(cookies_.size());
611   for (const auto& cookie : cookies_)
612     cookie_ptrs.push_back(cookie.second.get());
613 
614   for (const auto& cookie_partition : partitioned_cookies_) {
615     for (const auto& cookie : *cookie_partition.second.get())
616       cookie_ptrs.push_back(cookie.second.get());
617   }
618 
619   std::sort(cookie_ptrs.begin(), cookie_ptrs.end(), CookieSorter);
620 
621   CookieList cookie_list;
622   cookie_list.reserve(cookie_ptrs.size());
623   for (auto* cookie_ptr : cookie_ptrs)
624     cookie_list.push_back(*cookie_ptr);
625 
626   MaybeRunCookieCallback(std::move(callback), cookie_list);
627 }
628 
AttachAccessSemanticsListForCookieList(GetAllCookiesWithAccessSemanticsCallback callback,const CookieList & cookie_list)629 void CookieMonster::AttachAccessSemanticsListForCookieList(
630     GetAllCookiesWithAccessSemanticsCallback callback,
631     const CookieList& cookie_list) {
632   std::vector<CookieAccessSemantics> access_semantics_list;
633   for (const CanonicalCookie& cookie : cookie_list) {
634     access_semantics_list.push_back(GetAccessSemanticsForCookie(cookie));
635   }
636   MaybeRunCookieCallback(std::move(callback), cookie_list,
637                          access_semantics_list);
638 }
639 
GetCookieListWithOptions(const GURL & url,const CookieOptions & options,const CookiePartitionKeyCollection & cookie_partition_key_collection,GetCookieListCallback callback)640 void CookieMonster::GetCookieListWithOptions(
641     const GURL& url,
642     const CookieOptions& options,
643     const CookiePartitionKeyCollection& cookie_partition_key_collection,
644     GetCookieListCallback callback) {
645   DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
646 
647   CookieAccessResultList included_cookies;
648   CookieAccessResultList excluded_cookies;
649   if (HasCookieableScheme(url)) {
650     std::vector<CanonicalCookie*> cookie_ptrs;
651     if (IncludeUnpartitionedCookies(cookie_partition_key_collection)) {
652       cookie_ptrs = FindCookiesForRegistryControlledHost(url);
653     } else {
654       DCHECK(!cookie_partition_key_collection.IsEmpty());
655     }
656 
657     if (!cookie_partition_key_collection.IsEmpty()) {
658       if (cookie_partition_key_collection.ContainsAllKeys()) {
659         for (const auto& it : partitioned_cookies_) {
660           std::vector<CanonicalCookie*> partitioned_cookie_ptrs =
661               FindPartitionedCookiesForRegistryControlledHost(it.first, url);
662           cookie_ptrs.insert(cookie_ptrs.end(), partitioned_cookie_ptrs.begin(),
663                              partitioned_cookie_ptrs.end());
664         }
665       } else {
666         for (const CookiePartitionKey& key :
667              cookie_partition_key_collection.PartitionKeys()) {
668           std::vector<CanonicalCookie*> partitioned_cookie_ptrs =
669               FindPartitionedCookiesForRegistryControlledHost(key, url);
670           cookie_ptrs.insert(cookie_ptrs.end(), partitioned_cookie_ptrs.begin(),
671                              partitioned_cookie_ptrs.end());
672         }
673       }
674     }
675     std::sort(cookie_ptrs.begin(), cookie_ptrs.end(), CookieSorter);
676 
677     included_cookies.reserve(cookie_ptrs.size());
678     FilterCookiesWithOptions(url, options, &cookie_ptrs, &included_cookies,
679                              &excluded_cookies);
680   }
681 
682   MaybeRunCookieCallback(std::move(callback), included_cookies,
683                          excluded_cookies);
684 }
685 
DeleteAllCreatedInTimeRange(const TimeRange & creation_range,DeleteCallback callback)686 void CookieMonster::DeleteAllCreatedInTimeRange(const TimeRange& creation_range,
687                                                 DeleteCallback callback) {
688   DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
689 
690   uint32_t num_deleted = 0;
691   for (auto it = cookies_.begin(); it != cookies_.end();) {
692     auto curit = it;
693     CanonicalCookie* cc = curit->second.get();
694     ++it;
695 
696     if (creation_range.Contains(cc->CreationDate())) {
697       InternalDeleteCookie(curit, true, /*sync_to_store*/
698                            DELETE_COOKIE_EXPLICIT);
699       ++num_deleted;
700     }
701   }
702 
703   for (PartitionedCookieMap::iterator partition_it =
704            partitioned_cookies_.begin();
705        partition_it != partitioned_cookies_.end();) {
706     auto cur_partition_it = partition_it;
707     CookieMap::iterator cookie_it = cur_partition_it->second->begin();
708     CookieMap::iterator cookie_end = cur_partition_it->second->end();
709     // InternalDeletePartitionedCookie may delete this cookie partition if it
710     // only has one cookie, so we need to increment the iterator beforehand.
711     ++partition_it;
712 
713     while (cookie_it != cookie_end) {
714       auto cur_cookie_it = cookie_it;
715       CanonicalCookie* cc = cur_cookie_it->second.get();
716       ++cookie_it;
717 
718       if (creation_range.Contains(cc->CreationDate())) {
719         InternalDeletePartitionedCookie(cur_partition_it, cur_cookie_it,
720                                         true /*sync_to_store*/,
721                                         DELETE_COOKIE_EXPLICIT);
722         ++num_deleted;
723       }
724     }
725   }
726 
727   FlushStore(
728       base::BindOnce(&MaybeRunDeleteCallback, weak_ptr_factory_.GetWeakPtr(),
729                      callback ? base::BindOnce(std::move(callback), num_deleted)
730                               : base::OnceClosure()));
731 }
732 
MatchCookieDeletionInfo(const CookieDeletionInfo & delete_info,const net::CanonicalCookie & cookie)733 bool CookieMonster::MatchCookieDeletionInfo(
734     const CookieDeletionInfo& delete_info,
735     const net::CanonicalCookie& cookie) {
736   bool delegate_treats_url_as_trustworthy = false;  // irrelevant if no URL.
737   if (delete_info.url.has_value()) {
738     delegate_treats_url_as_trustworthy =
739         cookie_access_delegate() &&
740         cookie_access_delegate()->ShouldTreatUrlAsTrustworthy(
741             delete_info.url.value());
742   }
743 
744   // Deletion uses all inclusive options, so it's ok to get the
745   // `CookieSamePartyStatus` wrong here.
746   return delete_info.Matches(
747       cookie,
748       CookieAccessParams{GetAccessSemanticsForCookie(cookie),
749                          delegate_treats_url_as_trustworthy,
750                          CookieSamePartyStatus::kNoSamePartyEnforcement});
751 }
752 
DeleteCanonicalCookie(const CanonicalCookie & cookie,DeleteCallback callback)753 void CookieMonster::DeleteCanonicalCookie(const CanonicalCookie& cookie,
754                                           DeleteCallback callback) {
755   DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
756   uint32_t result = 0u;
757   CookieMap* cookie_map = nullptr;
758   PartitionedCookieMap::iterator cookie_partition_it;
759 
760   if (cookie.IsPartitioned()) {
761     cookie_partition_it =
762         partitioned_cookies_.find(cookie.PartitionKey().value());
763     if (cookie_partition_it != partitioned_cookies_.end())
764       cookie_map = cookie_partition_it->second.get();
765   } else {
766     cookie_map = &cookies_;
767   }
768   if (cookie_map) {
769     for (CookieMapItPair its = cookie_map->equal_range(GetKey(cookie.Domain()));
770          its.first != its.second; ++its.first) {
771       const std::unique_ptr<CanonicalCookie>& candidate = its.first->second;
772       // Historically, this has refused modification if the cookie has changed
773       // value in between the CanonicalCookie object was returned by a getter
774       // and when this ran.  The later parts of the conditional (everything but
775       // the equivalence check) attempt to preserve this behavior.
776       if (candidate->IsEquivalent(cookie) &&
777           candidate->Value() == cookie.Value()) {
778         if (cookie.IsPartitioned()) {
779           InternalDeletePartitionedCookie(cookie_partition_it, its.first, true,
780                                           DELETE_COOKIE_EXPLICIT);
781         } else {
782           InternalDeleteCookie(its.first, true, DELETE_COOKIE_EXPLICIT);
783         }
784         result = 1u;
785         break;
786       }
787     }
788   }
789   FlushStore(
790       base::BindOnce(&MaybeRunDeleteCallback, weak_ptr_factory_.GetWeakPtr(),
791                      callback ? base::BindOnce(std::move(callback), result)
792                               : base::OnceClosure()));
793 }
794 
DeleteMatchingCookies(DeletePredicate predicate,DeletionCause cause,DeleteCallback callback)795 void CookieMonster::DeleteMatchingCookies(DeletePredicate predicate,
796                                           DeletionCause cause,
797                                           DeleteCallback callback) {
798   DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
799   DCHECK(predicate);
800 
801   uint32_t num_deleted = 0;
802   for (auto it = cookies_.begin(); it != cookies_.end();) {
803     auto curit = it;
804     CanonicalCookie* cc = curit->second.get();
805     ++it;
806     if (predicate.Run(*cc)) {
807       InternalDeleteCookie(curit, true /*sync_to_store*/, cause);
808       ++num_deleted;
809     }
810   }
811   for (auto partition_it = partitioned_cookies_.begin();
812        partition_it != partitioned_cookies_.end();) {
813     // InternalDeletePartitionedCookie may invalidate |partition_it| if that
814     // cookie partition only has one cookie.
815     auto cur_partition_it = partition_it;
816     CookieMap::iterator cookie_it = cur_partition_it->second->begin();
817     CookieMap::iterator cookie_end = cur_partition_it->second->end();
818     ++partition_it;
819 
820     while (cookie_it != cookie_end) {
821       auto cur_cookie_it = cookie_it;
822       CanonicalCookie* cc = cur_cookie_it->second.get();
823       ++cookie_it;
824 
825       if (predicate.Run(*cc)) {
826         InternalDeletePartitionedCookie(cur_partition_it, cur_cookie_it, true,
827                                         cause);
828         ++num_deleted;
829       }
830     }
831   }
832 
833   FlushStore(
834       base::BindOnce(&MaybeRunDeleteCallback, weak_ptr_factory_.GetWeakPtr(),
835                      callback ? base::BindOnce(std::move(callback), num_deleted)
836                               : base::OnceClosure()));
837 }
838 
MarkCookieStoreAsInitialized()839 void CookieMonster::MarkCookieStoreAsInitialized() {
840   DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
841   initialized_ = true;
842 }
843 
FetchAllCookiesIfNecessary()844 void CookieMonster::FetchAllCookiesIfNecessary() {
845   DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
846   if (store_.get() && !started_fetching_all_cookies_) {
847     started_fetching_all_cookies_ = true;
848     FetchAllCookies();
849   }
850 }
851 
FetchAllCookies()852 void CookieMonster::FetchAllCookies() {
853   DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
854   DCHECK(store_.get()) << "Store must exist to initialize";
855   DCHECK(!finished_fetching_all_cookies_)
856       << "All cookies have already been fetched.";
857 
858   // We bind in the current time so that we can report the wall-clock time for
859   // loading cookies.
860   store_->Load(base::BindOnce(&CookieMonster::OnLoaded,
861                               weak_ptr_factory_.GetWeakPtr(), TimeTicks::Now()),
862                net_log_);
863 }
864 
OnLoaded(TimeTicks beginning_time,std::vector<std::unique_ptr<CanonicalCookie>> cookies)865 void CookieMonster::OnLoaded(
866     TimeTicks beginning_time,
867     std::vector<std::unique_ptr<CanonicalCookie>> cookies) {
868   DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
869   StoreLoadedCookies(std::move(cookies));
870   base::UmaHistogramCustomTimes("Cookie.TimeBlockedOnLoad",
871                                 base::TimeTicks::Now() - beginning_time,
872                                 base::Milliseconds(1), base::Minutes(1), 50);
873 
874   // Invoke the task queue of cookie request.
875   InvokeQueue();
876 }
877 
OnKeyLoaded(const std::string & key,std::vector<std::unique_ptr<CanonicalCookie>> cookies)878 void CookieMonster::OnKeyLoaded(
879     const std::string& key,
880     std::vector<std::unique_ptr<CanonicalCookie>> cookies) {
881   DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
882 
883   StoreLoadedCookies(std::move(cookies));
884 
885   auto tasks_pending_for_key = tasks_pending_for_key_.find(key);
886 
887   // TODO(mmenke): Can this be turned into a DCHECK?
888   if (tasks_pending_for_key == tasks_pending_for_key_.end())
889     return;
890 
891   // Run all tasks for the key. Note that running a task can result in multiple
892   // tasks being added to the back of the deque.
893   while (!tasks_pending_for_key->second.empty()) {
894     base::OnceClosure task = std::move(tasks_pending_for_key->second.front());
895     tasks_pending_for_key->second.pop_front();
896     std::move(task).Run();
897   }
898 
899   tasks_pending_for_key_.erase(tasks_pending_for_key);
900 
901   // This has to be done last, in case running a task queues a new task for the
902   // key, to ensure tasks are run in the correct order.
903   keys_loaded_.insert(key);
904 }
905 
StoreLoadedCookies(std::vector<std::unique_ptr<CanonicalCookie>> cookies)906 void CookieMonster::StoreLoadedCookies(
907     std::vector<std::unique_ptr<CanonicalCookie>> cookies) {
908   DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
909 
910   // Even if a key is expired, insert it so it can be garbage collected,
911   // removed, and sync'd.
912   CookieItVector cookies_with_control_chars;
913   std::vector<PartitionedCookieMapIterators>
914       partitioned_cookies_with_control_chars;
915 
916   for (auto& cookie : cookies) {
917     CanonicalCookie* cookie_ptr = cookie.get();
918     CookieAccessResult access_result;
919     access_result.access_semantics = CookieAccessSemantics::UNKNOWN;
920 
921     if (cookie_ptr->IsPartitioned()) {
922       auto inserted = InternalInsertPartitionedCookie(
923           GetKey(cookie_ptr->Domain()), std::move(cookie),
924           false /* sync_to_store */, access_result,
925           false /* dispatch_change */);
926       if (ContainsControlCharacter(cookie_ptr->Name()) ||
927           ContainsControlCharacter(cookie_ptr->Value())) {
928         partitioned_cookies_with_control_chars.push_back(inserted);
929       }
930     } else {
931       auto inserted =
932           InternalInsertCookie(GetKey(cookie_ptr->Domain()), std::move(cookie),
933                                false /* sync_to_store */, access_result,
934                                false /* dispatch_change */);
935 
936       if (ContainsControlCharacter(cookie_ptr->Name()) ||
937           ContainsControlCharacter(cookie_ptr->Value())) {
938         cookies_with_control_chars.push_back(inserted);
939       }
940     }
941 
942     const Time cookie_access_time(cookie_ptr->LastAccessDate());
943     if (earliest_access_time_.is_null() ||
944         cookie_access_time < earliest_access_time_) {
945       earliest_access_time_ = cookie_access_time;
946     }
947   }
948 
949   // Any cookies that contain control characters that we have loaded from the
950   // persistent store should be deleted. See http://crbug.com/238041.
951   for (auto it = cookies_with_control_chars.begin();
952        it != cookies_with_control_chars.end();) {
953     auto curit = it;
954     ++it;
955     InternalDeleteCookie(*curit, true, DELETE_COOKIE_CONTROL_CHAR);
956   }
957   for (auto it = partitioned_cookies_with_control_chars.begin();
958        it != partitioned_cookies_with_control_chars.end();) {
959     // InternalDeletePartitionedCookie may invalidate the current iterator, so
960     // we increment the iterator in the loop before calling the function.
961     auto curit = it;
962     ++it;
963     InternalDeletePartitionedCookie(curit->first, curit->second, true,
964                                     DELETE_COOKIE_CONTROL_CHAR);
965   }
966 
967   // After importing cookies from the PersistentCookieStore, verify that
968   // none of our other constraints are violated.
969   // In particular, the backing store might have given us duplicate cookies.
970 
971   // This method could be called multiple times due to priority loading, thus
972   // cookies loaded in previous runs will be validated again, but this is OK
973   // since they are expected to be much fewer than total DB.
974   EnsureCookiesMapIsValid();
975 }
976 
InvokeQueue()977 void CookieMonster::InvokeQueue() {
978   DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
979 
980   // Move all per-key tasks into the global queue, if there are any.  This is
981   // protection about a race where the store learns about all cookies loading
982   // before it learned about the cookies for a key loading.
983 
984   // Needed to prevent any recursively queued tasks from going back into the
985   // per-key queues.
986   seen_global_task_ = true;
987   for (auto& tasks_for_key : tasks_pending_for_key_) {
988     tasks_pending_.insert(tasks_pending_.begin(),
989                           std::make_move_iterator(tasks_for_key.second.begin()),
990                           std::make_move_iterator(tasks_for_key.second.end()));
991   }
992   tasks_pending_for_key_.clear();
993 
994   while (!tasks_pending_.empty()) {
995     base::OnceClosure request_task = std::move(tasks_pending_.front());
996     tasks_pending_.pop_front();
997     std::move(request_task).Run();
998   }
999 
1000   DCHECK(tasks_pending_for_key_.empty());
1001 
1002   finished_fetching_all_cookies_ = true;
1003   keys_loaded_.clear();
1004 }
1005 
EnsureCookiesMapIsValid()1006 void CookieMonster::EnsureCookiesMapIsValid() {
1007   DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
1008 
1009   // Iterate through all the of the cookies, grouped by host.
1010   for (auto next = cookies_.begin(); next != cookies_.end();) {
1011     auto cur_range_begin = next;
1012     const std::string key = cur_range_begin->first;  // Keep a copy.
1013     auto cur_range_end = cookies_.upper_bound(key);
1014     next = cur_range_end;
1015 
1016     // Ensure no equivalent cookies for this host.
1017     TrimDuplicateCookiesForKey(key, cur_range_begin, cur_range_end,
1018                                absl::nullopt);
1019   }
1020 
1021   for (auto cookie_partition_it = partitioned_cookies_.begin();
1022        cookie_partition_it != partitioned_cookies_.end();) {
1023     auto cur_cookie_partition_it = cookie_partition_it;
1024     ++cookie_partition_it;
1025 
1026     // Iterate through the cookies in this partition, grouped by host.
1027     CookieMap* cookie_partition = cur_cookie_partition_it->second.get();
1028     auto prev_range_end = cookie_partition->begin();
1029     while (prev_range_end != cookie_partition->end()) {
1030       auto cur_range_begin = prev_range_end;
1031       const std::string key = cur_range_begin->first;  // Keep a copy.
1032       auto cur_range_end = cookie_partition->upper_bound(key);
1033       prev_range_end = cur_range_end;
1034 
1035       // Ensure no equivalent cookies for this host and cookie partition key.
1036       TrimDuplicateCookiesForKey(key, cur_range_begin, cur_range_end,
1037                                  absl::make_optional(cur_cookie_partition_it));
1038     }
1039   }
1040 }
1041 
1042 // Our strategy to find duplicates is:
1043 // (1) Build a map from cookie unique key to
1044 //     {list of cookies with this signature, sorted by creation time}.
1045 // (2) For each list with more than 1 entry, keep the cookie having the
1046 //     most recent creation time, and delete the others.
1047 //
TrimDuplicateCookiesForKey(const std::string & key,CookieMap::iterator begin,CookieMap::iterator end,absl::optional<PartitionedCookieMap::iterator> cookie_partition_it)1048 void CookieMonster::TrimDuplicateCookiesForKey(
1049     const std::string& key,
1050     CookieMap::iterator begin,
1051     CookieMap::iterator end,
1052     absl::optional<PartitionedCookieMap::iterator> cookie_partition_it) {
1053   DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
1054 
1055   // Set of cookies ordered by creation time.
1056   typedef std::multiset<CookieMap::iterator, OrderByCreationTimeDesc> CookieSet;
1057 
1058   // Helper map we populate to find the duplicates.
1059   typedef std::map<CanonicalCookie::UniqueCookieKey, CookieSet> EquivalenceMap;
1060   EquivalenceMap equivalent_cookies;
1061 
1062   // The number of duplicate cookies that have been found.
1063   int num_duplicates = 0;
1064 
1065   // Iterate through all of the cookies in our range, and insert them into
1066   // the equivalence map.
1067   for (auto it = begin; it != end; ++it) {
1068     DCHECK_EQ(key, it->first);
1069     CanonicalCookie* cookie = it->second.get();
1070 
1071     CanonicalCookie::UniqueCookieKey signature(cookie->UniqueKey());
1072     CookieSet& set = equivalent_cookies[signature];
1073 
1074     // We found a duplicate!
1075     if (!set.empty())
1076       num_duplicates++;
1077 
1078     // We save the iterator into |cookies_| rather than the actual cookie
1079     // pointer, since we may need to delete it later.
1080     set.insert(it);
1081   }
1082 
1083   // If there were no duplicates, we are done!
1084   if (num_duplicates == 0)
1085     return;
1086 
1087   // Make sure we find everything below that we did above.
1088   int num_duplicates_found = 0;
1089 
1090   // Otherwise, delete all the duplicate cookies, both from our in-memory store
1091   // and from the backing store.
1092   for (std::pair<const CanonicalCookie::UniqueCookieKey, CookieSet>&
1093            equivalent_cookie : equivalent_cookies) {
1094     const CanonicalCookie::UniqueCookieKey& signature = equivalent_cookie.first;
1095     CookieSet& dupes = equivalent_cookie.second;
1096 
1097     if (dupes.size() <= 1)
1098       continue;  // This cookiename/path has no duplicates.
1099     num_duplicates_found += dupes.size() - 1;
1100 
1101     // Since |dupes| is sorted by creation time (descending), the first cookie
1102     // is the most recent one (or tied for it), so we will keep it. The rest are
1103     // duplicates.
1104     dupes.erase(dupes.begin());
1105 
1106     // TODO(crbug.com/1225444) Include cookie partition key in this log
1107     // statement as well if needed.
1108     LOG(ERROR) << base::StringPrintf(
1109         "Found %d duplicate cookies for key='%s', "
1110         "with {name='%s', domain='%s', path='%s'}",
1111         static_cast<int>(dupes.size()), key.c_str(),
1112         std::get<1>(signature).c_str(), std::get<2>(signature).c_str(),
1113         std::get<3>(signature).c_str());
1114 
1115     // Remove all the cookies identified by |dupes|. It is valid to delete our
1116     // list of iterators one at a time, since |cookies_| is a multimap (they
1117     // don't invalidate existing iterators following deletion).
1118     for (const CookieMap::iterator& dupe : dupes) {
1119       if (cookie_partition_it) {
1120         InternalDeletePartitionedCookie(
1121             cookie_partition_it.value(), dupe, true,
1122             DELETE_COOKIE_DUPLICATE_IN_BACKING_STORE);
1123       } else {
1124         InternalDeleteCookie(dupe, true,
1125                              DELETE_COOKIE_DUPLICATE_IN_BACKING_STORE);
1126       }
1127     }
1128   }
1129   DCHECK_EQ(num_duplicates, num_duplicates_found);
1130 }
1131 
1132 std::vector<CanonicalCookie*>
FindCookiesForRegistryControlledHost(const GURL & url,CookieMap * cookie_map,CookieMonster::PartitionedCookieMap::iterator * partition_it)1133 CookieMonster::FindCookiesForRegistryControlledHost(
1134     const GURL& url,
1135     CookieMap* cookie_map,
1136     CookieMonster::PartitionedCookieMap::iterator* partition_it) {
1137   DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
1138 
1139   if (!cookie_map)
1140     cookie_map = &cookies_;
1141 
1142   Time current_time = Time::Now();
1143 
1144   // Retrieve all cookies for a given key
1145   const std::string key(GetKey(url.host_piece()));
1146 
1147   std::vector<CanonicalCookie*> cookies;
1148   for (CookieMapItPair its = cookie_map->equal_range(key);
1149        its.first != its.second;) {
1150     auto curit = its.first;
1151     CanonicalCookie* cc = curit->second.get();
1152     ++its.first;
1153 
1154     // If the cookie is expired, delete it.
1155     if (cc->IsExpired(current_time)) {
1156       if (cc->IsPartitioned()) {
1157         DCHECK(partition_it);
1158         DCHECK_EQ((*partition_it)->second.get(), cookie_map);
1159         InternalDeletePartitionedCookie(*partition_it, curit, true,
1160                                         DELETE_COOKIE_EXPIRED);
1161       } else {
1162         InternalDeleteCookie(curit, true, DELETE_COOKIE_EXPIRED);
1163       }
1164       continue;
1165     }
1166     cookies.push_back(cc);
1167   }
1168   return cookies;
1169 }
1170 
1171 std::vector<CanonicalCookie*>
FindPartitionedCookiesForRegistryControlledHost(const CookiePartitionKey & cookie_partition_key,const GURL & url)1172 CookieMonster::FindPartitionedCookiesForRegistryControlledHost(
1173     const CookiePartitionKey& cookie_partition_key,
1174     const GURL& url) {
1175   DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
1176 
1177   PartitionedCookieMap::iterator it =
1178       partitioned_cookies_.find(cookie_partition_key);
1179   if (it == partitioned_cookies_.end())
1180     return std::vector<CanonicalCookie*>();
1181 
1182   return FindCookiesForRegistryControlledHost(url, it->second.get(), &it);
1183 }
1184 
FilterCookiesWithOptions(const GURL url,const CookieOptions options,std::vector<CanonicalCookie * > * cookie_ptrs,CookieAccessResultList * included_cookies,CookieAccessResultList * excluded_cookies)1185 void CookieMonster::FilterCookiesWithOptions(
1186     const GURL url,
1187     const CookieOptions options,
1188     std::vector<CanonicalCookie*>* cookie_ptrs,
1189     CookieAccessResultList* included_cookies,
1190     CookieAccessResultList* excluded_cookies) {
1191   DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
1192 
1193   // Probe to save statistics relatively frequently.  We do it here rather
1194   // than in the set path as many websites won't set cookies, and we
1195   // want to collect statistics whenever the browser's being used.
1196   Time current_time = Time::Now();
1197   RecordPeriodicStats(current_time);
1198 
1199   bool delegate_treats_url_as_trustworthy =
1200       cookie_access_delegate() &&
1201       cookie_access_delegate()->ShouldTreatUrlAsTrustworthy(url);
1202 
1203   for (CanonicalCookie* cookie_ptr : *cookie_ptrs) {
1204     // Filter out cookies that should not be included for a request to the
1205     // given |url|. HTTP only cookies are filtered depending on the passed
1206     // cookie |options|.
1207     CookieAccessResult access_result = cookie_ptr->IncludeForRequestURL(
1208         url, options,
1209         CookieAccessParams{
1210             GetAccessSemanticsForCookie(*cookie_ptr),
1211             delegate_treats_url_as_trustworthy,
1212             cookie_util::GetSamePartyStatus(*cookie_ptr, options,
1213                                             same_party_attribute_enabled_)});
1214 
1215     if (!access_result.status.IsInclude()) {
1216       if (options.return_excluded_cookies())
1217         excluded_cookies->push_back({*cookie_ptr, access_result});
1218       continue;
1219     }
1220 
1221     if (options.update_access_time())
1222       InternalUpdateCookieAccessTime(cookie_ptr, current_time);
1223 
1224     int destination_port = url.EffectiveIntPort();
1225 
1226     if (IsLocalhost(url)) {
1227       UMA_HISTOGRAM_ENUMERATION(
1228           "Cookie.Port.Read.Localhost",
1229           ReducePortRangeForCookieHistogram(destination_port));
1230       UMA_HISTOGRAM_ENUMERATION(
1231           "Cookie.Port.ReadDiffersFromSet.Localhost",
1232           IsCookieSentToSamePortThatSetIt(url, cookie_ptr->SourcePort(),
1233                                           cookie_ptr->SourceScheme()));
1234     } else {
1235       UMA_HISTOGRAM_ENUMERATION(
1236           "Cookie.Port.Read.RemoteHost",
1237           ReducePortRangeForCookieHistogram(destination_port));
1238       UMA_HISTOGRAM_ENUMERATION(
1239           "Cookie.Port.ReadDiffersFromSet.RemoteHost",
1240           IsCookieSentToSamePortThatSetIt(url, cookie_ptr->SourcePort(),
1241                                           cookie_ptr->SourceScheme()));
1242     }
1243 
1244     if (cookie_ptr->IsDomainCookie()) {
1245       UMA_HISTOGRAM_ENUMERATION(
1246           "Cookie.Port.ReadDiffersFromSet.DomainSet",
1247           IsCookieSentToSamePortThatSetIt(url, cookie_ptr->SourcePort(),
1248                                           cookie_ptr->SourceScheme()));
1249     }
1250 
1251     included_cookies->push_back({*cookie_ptr, access_result});
1252   }
1253 }
1254 
MaybeDeleteEquivalentCookieAndUpdateStatus(const std::string & key,const CanonicalCookie & cookie_being_set,bool allowed_to_set_secure_cookie,bool skip_httponly,bool already_expired,base::Time * creation_date_to_inherit,CookieInclusionStatus * status,absl::optional<PartitionedCookieMap::iterator> cookie_partition_it)1255 void CookieMonster::MaybeDeleteEquivalentCookieAndUpdateStatus(
1256     const std::string& key,
1257     const CanonicalCookie& cookie_being_set,
1258     bool allowed_to_set_secure_cookie,
1259     bool skip_httponly,
1260     bool already_expired,
1261     base::Time* creation_date_to_inherit,
1262     CookieInclusionStatus* status,
1263     absl::optional<PartitionedCookieMap::iterator> cookie_partition_it) {
1264   DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
1265   DCHECK(!status->HasExclusionReason(
1266       CookieInclusionStatus::EXCLUDE_OVERWRITE_SECURE));
1267   DCHECK(!status->HasExclusionReason(
1268       CookieInclusionStatus::EXCLUDE_OVERWRITE_HTTP_ONLY));
1269 
1270   CookieMap* cookie_map = &cookies_;
1271   if (cookie_partition_it) {
1272     cookie_map = cookie_partition_it.value()->second.get();
1273   }
1274 
1275   bool found_equivalent_cookie = false;
1276   CookieMap::iterator deletion_candidate_it = cookie_map->end();
1277   CanonicalCookie* skipped_secure_cookie = nullptr;
1278 
1279   // Check every cookie matching this domain key for equivalence.
1280   CookieMapItPair range_its = cookie_map->equal_range(key);
1281   for (auto cur_it = range_its.first; cur_it != range_its.second; ++cur_it) {
1282     CanonicalCookie* cur_existing_cookie = cur_it->second.get();
1283 
1284     // Evaluate "Leave Secure Cookies Alone":
1285     // If the cookie is being set from an insecure source, then if an
1286     // "equivalent" Secure cookie already exists, then the cookie should *not*
1287     // be updated.
1288     //
1289     // "Equivalent" means they are the same by
1290     // IsEquivalentForSecureCookieMatching(). See the comment there for
1291     // details. (Note this is not a symmetric comparison.) This notion of
1292     // equivalence is slightly more inclusive than the usual IsEquivalent() one.
1293     //
1294     // See: https://tools.ietf.org/html/draft-ietf-httpbis-cookie-alone
1295     if (cur_existing_cookie->IsSecure() && !allowed_to_set_secure_cookie &&
1296         cookie_being_set.IsEquivalentForSecureCookieMatching(
1297             *cur_existing_cookie)) {
1298       // Hold onto this for additional Netlogging later if we end up preserving
1299       // a would-have-been-deleted cookie because of this.
1300       skipped_secure_cookie = cur_existing_cookie;
1301       net_log_.AddEvent(NetLogEventType::COOKIE_STORE_COOKIE_REJECTED_SECURE,
1302                         [&](NetLogCaptureMode capture_mode) {
1303                           return NetLogCookieMonsterCookieRejectedSecure(
1304                               skipped_secure_cookie, &cookie_being_set,
1305                               capture_mode);
1306                         });
1307       status->AddExclusionReason(
1308           CookieInclusionStatus::EXCLUDE_OVERWRITE_SECURE);
1309     }
1310 
1311     if (cookie_being_set.IsEquivalent(*cur_existing_cookie)) {
1312       // We should never have more than one equivalent cookie, since they should
1313       // overwrite each other.
1314       CHECK(!found_equivalent_cookie)
1315           << "Duplicate equivalent cookies found, cookie store is corrupted.";
1316       DCHECK(deletion_candidate_it == cookie_map->end());
1317       found_equivalent_cookie = true;
1318 
1319       // The |cookie_being_set| is rejected for trying to overwrite an httponly
1320       // cookie when it should not be able to.
1321       if (skip_httponly && cur_existing_cookie->IsHttpOnly()) {
1322         net_log_.AddEvent(
1323             NetLogEventType::COOKIE_STORE_COOKIE_REJECTED_HTTPONLY,
1324             [&](NetLogCaptureMode capture_mode) {
1325               return NetLogCookieMonsterCookieRejectedHttponly(
1326                   cur_existing_cookie, &cookie_being_set, capture_mode);
1327             });
1328         status->AddExclusionReason(
1329             CookieInclusionStatus::EXCLUDE_OVERWRITE_HTTP_ONLY);
1330       } else {
1331         deletion_candidate_it = cur_it;
1332       }
1333     }
1334   }
1335 
1336   if (deletion_candidate_it != cookie_map->end()) {
1337     CanonicalCookie* deletion_candidate = deletion_candidate_it->second.get();
1338     if (deletion_candidate->Value() == cookie_being_set.Value())
1339       *creation_date_to_inherit = deletion_candidate->CreationDate();
1340     if (status->IsInclude()) {
1341       if (cookie_being_set.IsPartitioned()) {
1342         InternalDeletePartitionedCookie(
1343             cookie_partition_it.value(), deletion_candidate_it,
1344             true /* sync_to_store */,
1345             already_expired ? DELETE_COOKIE_EXPIRED_OVERWRITE
1346                             : DELETE_COOKIE_OVERWRITE);
1347       } else {
1348         InternalDeleteCookie(deletion_candidate_it, true /* sync_to_store */,
1349                              already_expired ? DELETE_COOKIE_EXPIRED_OVERWRITE
1350                                              : DELETE_COOKIE_OVERWRITE);
1351       }
1352     } else if (status->HasExclusionReason(
1353                    CookieInclusionStatus::EXCLUDE_OVERWRITE_SECURE)) {
1354       // Log that we preserved a cookie that would have been deleted due to
1355       // Leave Secure Cookies Alone. This arbitrarily only logs the last
1356       // |skipped_secure_cookie| that we were left with after the for loop, even
1357       // if there were multiple matching Secure cookies that were left alone.
1358       DCHECK(skipped_secure_cookie);
1359       net_log_.AddEvent(
1360           NetLogEventType::COOKIE_STORE_COOKIE_PRESERVED_SKIPPED_SECURE,
1361           [&](NetLogCaptureMode capture_mode) {
1362             return NetLogCookieMonsterCookiePreservedSkippedSecure(
1363                 skipped_secure_cookie, deletion_candidate, &cookie_being_set,
1364                 capture_mode);
1365           });
1366     }
1367   }
1368 }
1369 
InternalInsertCookie(const std::string & key,std::unique_ptr<CanonicalCookie> cc,bool sync_to_store,const CookieAccessResult & access_result,bool dispatch_change)1370 CookieMonster::CookieMap::iterator CookieMonster::InternalInsertCookie(
1371     const std::string& key,
1372     std::unique_ptr<CanonicalCookie> cc,
1373     bool sync_to_store,
1374     const CookieAccessResult& access_result,
1375     bool dispatch_change) {
1376   DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
1377   CanonicalCookie* cc_ptr = cc.get();
1378 
1379   net_log_.AddEvent(NetLogEventType::COOKIE_STORE_COOKIE_ADDED,
1380                     [&](NetLogCaptureMode capture_mode) {
1381                       return NetLogCookieMonsterCookieAdded(
1382                           cc.get(), sync_to_store, capture_mode);
1383                     });
1384   if (ShouldUpdatePersistentStore(cc_ptr) && sync_to_store)
1385     store_->AddCookie(*cc_ptr);
1386   auto inserted = cookies_.insert(CookieMap::value_type(key, std::move(cc)));
1387 
1388   LogCookieTypeToUMA(cc_ptr, access_result);
1389 
1390   DCHECK(access_result.status.IsInclude());
1391   if (dispatch_change) {
1392     change_dispatcher_.DispatchChange(
1393         CookieChangeInfo(*cc_ptr, access_result, CookieChangeCause::INSERTED),
1394         true);
1395   }
1396 
1397   // If this is the first cookie in |cookies_| with this key, increment the
1398   // |num_keys_| counter.
1399   bool different_prev =
1400       inserted == cookies_.begin() || std::prev(inserted)->first != key;
1401   // According to std::multiqueue documentation:
1402   // "If the container has elements with equivalent key, inserts at the upper
1403   // bound of that range. (since C++11)"
1404   // This means that "inserted" iterator either points to the last element in
1405   // the map, or the element succeeding it has to have different key.
1406   DCHECK(std::next(inserted) == cookies_.end() ||
1407          std::next(inserted)->first != key);
1408   if (different_prev)
1409     ++num_keys_;
1410 
1411   return inserted;
1412 }
1413 
ShouldUpdatePersistentStore(CanonicalCookie * cc)1414 bool CookieMonster::ShouldUpdatePersistentStore(CanonicalCookie* cc) {
1415   return (cc->IsPersistent() || persist_session_cookies_) && store_.get();
1416 }
1417 
LogCookieTypeToUMA(CanonicalCookie * cc,const CookieAccessResult & access_result)1418 void CookieMonster::LogCookieTypeToUMA(
1419     CanonicalCookie* cc,
1420     const CookieAccessResult& access_result) {
1421   int32_t type_sample =
1422       !cc->IsEffectivelySameSiteNone(access_result.access_semantics)
1423           ? 1 << COOKIE_TYPE_SAME_SITE
1424           : 0;
1425   type_sample |= cc->IsHttpOnly() ? 1 << COOKIE_TYPE_HTTPONLY : 0;
1426   type_sample |= cc->IsSecure() ? 1 << COOKIE_TYPE_SECURE : 0;
1427   UMA_HISTOGRAM_EXACT_LINEAR("Cookie.Type", type_sample,
1428                              (1 << COOKIE_TYPE_LAST_ENTRY));
1429 }
1430 
1431 CookieMonster::PartitionedCookieMapIterators
InternalInsertPartitionedCookie(std::string key,std::unique_ptr<CanonicalCookie> cc,bool sync_to_store,const CookieAccessResult & access_result,bool dispatch_change)1432 CookieMonster::InternalInsertPartitionedCookie(
1433     std::string key,
1434     std::unique_ptr<CanonicalCookie> cc,
1435     bool sync_to_store,
1436     const CookieAccessResult& access_result,
1437     bool dispatch_change) {
1438   DCHECK(cc->IsPartitioned());
1439   DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
1440   CanonicalCookie* cc_ptr = cc.get();
1441 
1442   net_log_.AddEvent(NetLogEventType::COOKIE_STORE_COOKIE_ADDED,
1443                     [&](NetLogCaptureMode capture_mode) {
1444                       return NetLogCookieMonsterCookieAdded(
1445                           cc.get(), sync_to_store, capture_mode);
1446                     });
1447   if (ShouldUpdatePersistentStore(cc_ptr) && sync_to_store)
1448     store_->AddCookie(*cc_ptr);
1449 
1450   CookiePartitionKey partition_key(cc->PartitionKey().value());
1451   PartitionedCookieMap::iterator partition_it =
1452       partitioned_cookies_.find(partition_key);
1453   if (partition_it == partitioned_cookies_.end()) {
1454     partition_it =
1455         partitioned_cookies_
1456             .insert(PartitionedCookieMap::value_type(
1457                 std::move(partition_key), std::make_unique<CookieMap>()))
1458             .first;
1459   }
1460 
1461   CookieMap::iterator cookie_it = partition_it->second->insert(
1462       CookieMap::value_type(std::move(key), std::move(cc)));
1463   ++num_partitioned_cookies_;
1464 
1465   LogCookieTypeToUMA(cc_ptr, access_result);
1466 
1467   DCHECK(access_result.status.IsInclude());
1468   if (dispatch_change) {
1469     change_dispatcher_.DispatchChange(
1470         CookieChangeInfo(*cc_ptr, access_result, CookieChangeCause::INSERTED),
1471         true);
1472   }
1473 
1474   return std::make_pair(partition_it, cookie_it);
1475 }
1476 
SetCanonicalCookie(std::unique_ptr<CanonicalCookie> cc,const GURL & source_url,const CookieOptions & options,SetCookiesCallback callback,absl::optional<CookieAccessResult> cookie_access_result)1477 void CookieMonster::SetCanonicalCookie(
1478     std::unique_ptr<CanonicalCookie> cc,
1479     const GURL& source_url,
1480     const CookieOptions& options,
1481     SetCookiesCallback callback,
1482     absl::optional<CookieAccessResult> cookie_access_result) {
1483   DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
1484 
1485   bool delegate_treats_url_as_trustworthy =
1486       cookie_access_delegate() &&
1487       cookie_access_delegate()->ShouldTreatUrlAsTrustworthy(source_url);
1488 
1489   CookieAccessResult access_result = cc->IsSetPermittedInContext(
1490       source_url, options,
1491       CookieAccessParams(GetAccessSemanticsForCookie(*cc),
1492                          delegate_treats_url_as_trustworthy,
1493                          cookie_util::GetSamePartyStatus(
1494                              *cc, options, same_party_attribute_enabled_)),
1495       cookieable_schemes_, cookie_access_result);
1496 
1497   const std::string key(GetKey(cc->Domain()));
1498 
1499   base::Time creation_date = cc->CreationDate();
1500   if (creation_date.is_null()) {
1501     creation_date = Time::Now();
1502     cc->SetCreationDate(creation_date);
1503   }
1504   bool already_expired = cc->IsExpired(creation_date);
1505 
1506   base::Time creation_date_to_inherit;
1507 
1508   absl::optional<PartitionedCookieMap::iterator> cookie_partition_it;
1509   bool should_try_to_delete_duplicates = true;
1510 
1511   if (cc->IsPartitioned()) {
1512     auto it = partitioned_cookies_.find(cc->PartitionKey().value());
1513     if (it == partitioned_cookies_.end()) {
1514       // This is the first cookie in its partition, so it won't have any
1515       // duplicates.
1516       should_try_to_delete_duplicates = false;
1517     } else {
1518       cookie_partition_it = absl::make_optional(it);
1519     }
1520   }
1521 
1522   // Iterates through existing cookies for the same eTLD+1, and potentially
1523   // deletes an existing cookie, so any ExclusionReasons in |status| that would
1524   // prevent such deletion should be finalized beforehand.
1525   if (should_try_to_delete_duplicates) {
1526     MaybeDeleteEquivalentCookieAndUpdateStatus(
1527         key, *cc, access_result.is_allowed_to_access_secure_cookies,
1528         options.exclude_httponly(), already_expired, &creation_date_to_inherit,
1529         &access_result.status, cookie_partition_it);
1530   }
1531 
1532   if (access_result.status.HasExclusionReason(
1533           CookieInclusionStatus::EXCLUDE_OVERWRITE_SECURE) ||
1534       access_result.status.HasExclusionReason(
1535           CookieInclusionStatus::EXCLUDE_OVERWRITE_HTTP_ONLY)) {
1536     DVLOG(net::cookie_util::kVlogSetCookies)
1537         << "SetCookie() not clobbering httponly cookie or secure cookie for "
1538            "insecure scheme";
1539   }
1540 
1541   if (access_result.status.IsInclude()) {
1542     DVLOG(net::cookie_util::kVlogSetCookies)
1543         << "SetCookie() key: " << key << " cc: " << cc->DebugString();
1544 
1545     if (cc->IsEffectivelySameSiteNone()) {
1546       UMA_HISTOGRAM_COUNTS_10000("Cookie.SameSiteNoneSizeBytes",
1547                                  NameValueSizeBytes(*cc));
1548     }
1549 
1550     bool is_partitioned_cookie = cc->IsPartitioned();
1551     CookiePartitionKey cookie_partition_key;
1552     if (is_partitioned_cookie)
1553       cookie_partition_key = cc->PartitionKey().value();
1554 
1555     // Realize that we might be setting an expired cookie, and the only point
1556     // was to delete the cookie which we've already done.
1557     if (!already_expired) {
1558       HistogramExpirationDuration(*cc, creation_date);
1559 
1560       // Histogram the type of scheme used on URLs that set cookies. This
1561       // intentionally includes cookies that are set or overwritten by
1562       // http:// URLs, but not cookies that are cleared by http:// URLs, to
1563       // understand if the former behavior can be deprecated for Secure
1564       // cookies.
1565       // TODO(crbug.com/993120): Consider removing this histogram. The decision
1566       // it was added to evaluate has been implemented and standardized.
1567       CookieSource cookie_source_sample =
1568           (source_url.SchemeIsCryptographic()
1569                ? (cc->IsSecure()
1570                       ? CookieSource::kSecureCookieCryptographicScheme
1571                       : CookieSource::kNonsecureCookieCryptographicScheme)
1572                : (cc->IsSecure()
1573                       ? CookieSource::kSecureCookieNoncryptographicScheme
1574                       : CookieSource::kNonsecureCookieNoncryptographicScheme));
1575       UMA_HISTOGRAM_ENUMERATION("Cookie.CookieSourceScheme",
1576                                 cookie_source_sample);
1577 
1578       UMA_HISTOGRAM_BOOLEAN("Cookie.DomainSet", cc->IsDomainCookie());
1579 
1580       if (!creation_date_to_inherit.is_null()) {
1581         cc->SetCreationDate(creation_date_to_inherit);
1582       }
1583 
1584       if (is_partitioned_cookie) {
1585         InternalInsertPartitionedCookie(key, std::move(cc), true,
1586                                         access_result);
1587       } else {
1588         InternalInsertCookie(key, std::move(cc), true, access_result);
1589       }
1590     } else {
1591       DVLOG(net::cookie_util::kVlogSetCookies)
1592           << "SetCookie() not storing already expired cookie.";
1593     }
1594 
1595     // We assume that hopefully setting a cookie will be less common than
1596     // querying a cookie.  Since setting a cookie can put us over our limits,
1597     // make sure that we garbage collect...  We can also make the assumption
1598     // that if a cookie was set, in the common case it will be used soon after,
1599     // and we will purge the expired cookies in GetCookies().
1600     if (is_partitioned_cookie) {
1601       GarbageCollectPartitionedCookies(creation_date, cookie_partition_key,
1602                                        key);
1603     } else {
1604       GarbageCollect(creation_date, key);
1605     }
1606 
1607     if (IsLocalhost(source_url)) {
1608       UMA_HISTOGRAM_ENUMERATION(
1609           "Cookie.Port.Set.Localhost",
1610           ReducePortRangeForCookieHistogram(source_url.EffectiveIntPort()));
1611     } else {
1612       UMA_HISTOGRAM_ENUMERATION(
1613           "Cookie.Port.Set.RemoteHost",
1614           ReducePortRangeForCookieHistogram(source_url.EffectiveIntPort()));
1615     }
1616 
1617     UMA_HISTOGRAM_ENUMERATION("Cookie.CookieSourceSchemeName",
1618                               GetSchemeNameEnum(source_url));
1619   }
1620 
1621   // TODO(chlily): Log metrics.
1622   MaybeRunCookieCallback(std::move(callback), access_result);
1623 }
1624 
SetAllCookies(CookieList list,SetCookiesCallback callback)1625 void CookieMonster::SetAllCookies(CookieList list,
1626                                   SetCookiesCallback callback) {
1627   DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
1628 
1629   // Nuke the existing store.
1630   while (!cookies_.empty()) {
1631     // TODO(rdsmith): The CANONICAL is a lie.
1632     InternalDeleteCookie(cookies_.begin(), true, DELETE_COOKIE_EXPLICIT);
1633   }
1634 
1635   // Set all passed in cookies.
1636   for (const auto& cookie : list) {
1637     const std::string key(GetKey(cookie.Domain()));
1638     Time creation_time = cookie.CreationDate();
1639     if (cookie.IsExpired(creation_time))
1640       continue;
1641 
1642     HistogramExpirationDuration(cookie, creation_time);
1643 
1644     CookieAccessResult access_result;
1645     access_result.access_semantics = GetAccessSemanticsForCookie(cookie);
1646 
1647     if (cookie.IsPartitioned()) {
1648       InternalInsertPartitionedCookie(
1649           key, std::make_unique<CanonicalCookie>(cookie), true, access_result);
1650       GarbageCollectPartitionedCookies(creation_time,
1651                                        cookie.PartitionKey().value(), key);
1652     } else {
1653       InternalInsertCookie(key, std::make_unique<CanonicalCookie>(cookie), true,
1654                            access_result);
1655       GarbageCollect(creation_time, key);
1656     }
1657   }
1658 
1659   // TODO(rdsmith): If this function always returns the same value, it
1660   // shouldn't have a return value.  But it should also be deleted (see
1661   // https://codereview.chromium.org/2882063002/#msg64), which would
1662   // solve the return value problem.
1663   MaybeRunCookieCallback(std::move(callback), CookieAccessResult());
1664 }
1665 
InternalUpdateCookieAccessTime(CanonicalCookie * cc,const Time & current)1666 void CookieMonster::InternalUpdateCookieAccessTime(CanonicalCookie* cc,
1667                                                    const Time& current) {
1668   DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
1669 
1670   // Based off the Mozilla code.  When a cookie has been accessed recently,
1671   // don't bother updating its access time again.  This reduces the number of
1672   // updates we do during pageload, which in turn reduces the chance our storage
1673   // backend will hit its batch thresholds and be forced to update.
1674   if ((current - cc->LastAccessDate()) < last_access_threshold_)
1675     return;
1676 
1677   cc->SetLastAccessDate(current);
1678   if (ShouldUpdatePersistentStore(cc))
1679     store_->UpdateCookieAccessTime(*cc);
1680 }
1681 
1682 // InternalDeleteCookies must not invalidate iterators other than the one being
1683 // deleted.
InternalDeleteCookie(CookieMap::iterator it,bool sync_to_store,DeletionCause deletion_cause)1684 void CookieMonster::InternalDeleteCookie(CookieMap::iterator it,
1685                                          bool sync_to_store,
1686                                          DeletionCause deletion_cause) {
1687   DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
1688 
1689   // Ideally, this would be asserted up where we define kChangeCauseMapping,
1690   // but DeletionCause's visibility (or lack thereof) forces us to make
1691   // this check here.
1692   static_assert(std::size(kChangeCauseMapping) == DELETE_COOKIE_LAST_ENTRY + 1,
1693                 "kChangeCauseMapping size should match DeletionCause size");
1694 
1695   CanonicalCookie* cc = it->second.get();
1696   DVLOG(net::cookie_util::kVlogSetCookies)
1697       << "InternalDeleteCookie()"
1698       << ", cause:" << deletion_cause << ", cc: " << cc->DebugString();
1699 
1700   ChangeCausePair mapping = kChangeCauseMapping[deletion_cause];
1701   if (deletion_cause != DELETE_COOKIE_DONT_RECORD) {
1702     net_log_.AddEvent(NetLogEventType::COOKIE_STORE_COOKIE_DELETED,
1703                       [&](NetLogCaptureMode capture_mode) {
1704                         return NetLogCookieMonsterCookieDeleted(
1705                             cc, mapping.cause, sync_to_store, capture_mode);
1706                       });
1707   }
1708 
1709   if (ShouldUpdatePersistentStore(cc) && sync_to_store)
1710     store_->DeleteCookie(*cc);
1711 
1712   change_dispatcher_.DispatchChange(
1713       CookieChangeInfo(
1714           *cc,
1715           CookieAccessResult(CookieEffectiveSameSite::UNDEFINED,
1716                              CookieInclusionStatus(),
1717                              GetAccessSemanticsForCookie(*cc),
1718                              true /* is_allowed_to_access_secure_cookies */),
1719           mapping.cause),
1720       mapping.notify);
1721 
1722   // If this is the last cookie in |cookies_| with this key, decrement the
1723   // |num_keys_| counter.
1724   bool different_prev =
1725       it == cookies_.begin() || std::prev(it)->first != it->first;
1726   bool different_next =
1727       std::next(it) == cookies_.end() || std::next(it)->first != it->first;
1728   if (different_prev && different_next)
1729     --num_keys_;
1730 
1731   DCHECK(cookies_.find(it->first) != cookies_.end())
1732       << "Called erase with an iterator not in the cookie map";
1733   cookies_.erase(it);
1734 }
1735 
InternalDeletePartitionedCookie(PartitionedCookieMap::iterator partition_it,CookieMap::iterator cookie_it,bool sync_to_store,DeletionCause deletion_cause)1736 void CookieMonster::InternalDeletePartitionedCookie(
1737     PartitionedCookieMap::iterator partition_it,
1738     CookieMap::iterator cookie_it,
1739     bool sync_to_store,
1740     DeletionCause deletion_cause) {
1741   DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
1742 
1743   // Ideally, this would be asserted up where we define kChangeCauseMapping,
1744   // but DeletionCause's visibility (or lack thereof) forces us to make
1745   // this check here.
1746   static_assert(std::size(kChangeCauseMapping) == DELETE_COOKIE_LAST_ENTRY + 1,
1747                 "kChangeCauseMapping size should match DeletionCause size");
1748 
1749   CanonicalCookie* cc = cookie_it->second.get();
1750   DCHECK(cc->IsPartitioned());
1751   DVLOG(net::cookie_util::kVlogSetCookies)
1752       << "InternalDeletePartitionedCookie()"
1753       << ", cause:" << deletion_cause << ", cc: " << cc->DebugString();
1754 
1755   ChangeCausePair mapping = kChangeCauseMapping[deletion_cause];
1756   if (deletion_cause != DELETE_COOKIE_DONT_RECORD) {
1757     net_log_.AddEvent(NetLogEventType::COOKIE_STORE_COOKIE_DELETED,
1758                       [&](NetLogCaptureMode capture_mode) {
1759                         return NetLogCookieMonsterCookieDeleted(
1760                             cc, mapping.cause, sync_to_store, capture_mode);
1761                       });
1762   }
1763 
1764   if (ShouldUpdatePersistentStore(cc) && sync_to_store)
1765     store_->DeleteCookie(*cc);
1766 
1767   change_dispatcher_.DispatchChange(
1768       CookieChangeInfo(
1769           *cc,
1770           CookieAccessResult(CookieEffectiveSameSite::UNDEFINED,
1771                              CookieInclusionStatus(),
1772                              GetAccessSemanticsForCookie(*cc),
1773                              true /* is_allowed_to_access_secure_cookies */),
1774           mapping.cause),
1775       mapping.notify);
1776 
1777   DCHECK(partition_it->second->find(cookie_it->first) !=
1778          partition_it->second->end())
1779       << "Called erase with an iterator not in this partitioned cookie map";
1780   partition_it->second->erase(cookie_it);
1781   --num_partitioned_cookies_;
1782 
1783   if (partition_it->second->empty())
1784     partitioned_cookies_.erase(partition_it);
1785 }
1786 
1787 // Domain expiry behavior is unchanged by key/expiry scheme (the
1788 // meaning of the key is different, but that's not visible to this routine).
GarbageCollect(const Time & current,const std::string & key)1789 size_t CookieMonster::GarbageCollect(const Time& current,
1790                                      const std::string& key) {
1791   DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
1792 
1793   size_t num_deleted = 0;
1794   Time safe_date(Time::Now() - base::Days(kSafeFromGlobalPurgeDays));
1795 
1796   // Collect garbage for this key, minding cookie priorities.
1797   if (cookies_.count(key) > kDomainMaxCookies) {
1798     DVLOG(net::cookie_util::kVlogGarbageCollection)
1799         << "GarbageCollect() key: " << key;
1800 
1801     CookieItVector* cookie_its;
1802 
1803     CookieItVector non_expired_cookie_its;
1804     cookie_its = &non_expired_cookie_its;
1805     num_deleted +=
1806         GarbageCollectExpired(current, cookies_.equal_range(key), cookie_its);
1807 
1808     if (cookie_its->size() > kDomainMaxCookies) {
1809       DVLOG(net::cookie_util::kVlogGarbageCollection)
1810           << "Deep Garbage Collect domain.";
1811 
1812       if (domain_purged_keys_.size() < kMaxDomainPurgedKeys)
1813         domain_purged_keys_.insert(key);
1814 
1815       size_t purge_goal =
1816           cookie_its->size() - (kDomainMaxCookies - kDomainPurgeCookies);
1817       DCHECK(purge_goal > kDomainPurgeCookies);
1818 
1819       // Sort the cookies by access date, from least-recent to most-recent.
1820       std::sort(cookie_its->begin(), cookie_its->end(), LRACookieSorter);
1821 
1822       // Remove all but the kDomainCookiesQuotaLow most-recently accessed
1823       // cookies with low-priority. Then, if cookies still need to be removed,
1824       // bump the quota and remove low- and medium-priority. Then, if cookies
1825       // _still_ need to be removed, bump the quota and remove cookies with
1826       // any priority.
1827       //
1828       // 1.  Low-priority non-secure cookies.
1829       // 2.  Low-priority secure cookies.
1830       // 3.  Medium-priority non-secure cookies.
1831       // 4.  High-priority non-secure cookies.
1832       // 5.  Medium-priority secure cookies.
1833       // 6.  High-priority secure cookies.
1834       constexpr struct {
1835         CookiePriority priority;
1836         bool protect_secure_cookies;
1837       } kPurgeRounds[] = {
1838           // 1.  Low-priority non-secure cookies.
1839           {COOKIE_PRIORITY_LOW, true},
1840           // 2.  Low-priority secure cookies.
1841           {COOKIE_PRIORITY_LOW, false},
1842           // 3.  Medium-priority non-secure cookies.
1843           {COOKIE_PRIORITY_MEDIUM, true},
1844           // 4.  High-priority non-secure cookies.
1845           {COOKIE_PRIORITY_HIGH, true},
1846           // 5.  Medium-priority secure cookies.
1847           {COOKIE_PRIORITY_MEDIUM, false},
1848           // 6.  High-priority secure cookies.
1849           {COOKIE_PRIORITY_HIGH, false},
1850       };
1851 
1852       size_t quota = 0;
1853       for (const auto& purge_round : kPurgeRounds) {
1854         // Adjust quota according to the priority of cookies. Each round should
1855         // protect certain number of cookies in order to avoid starvation.
1856         // For example, when each round starts to remove cookies, the number of
1857         // cookies of that priority are counted and a decision whether they
1858         // should be deleted or not is made. If yes, some number of cookies of
1859         // that priority are deleted considering the quota.
1860         switch (purge_round.priority) {
1861           case COOKIE_PRIORITY_LOW:
1862             quota = kDomainCookiesQuotaLow;
1863             break;
1864           case COOKIE_PRIORITY_MEDIUM:
1865             quota = kDomainCookiesQuotaMedium;
1866             break;
1867           case COOKIE_PRIORITY_HIGH:
1868             quota = kDomainCookiesQuotaHigh;
1869             break;
1870         }
1871         size_t just_deleted = 0u;
1872         // Purge up to |purge_goal| for all cookies at the given priority.  This
1873         // path will be taken only if the initial non-secure purge did not evict
1874         // enough cookies.
1875         if (purge_goal > 0) {
1876           just_deleted = PurgeLeastRecentMatches(
1877               cookie_its, purge_round.priority, quota, purge_goal,
1878               purge_round.protect_secure_cookies);
1879           DCHECK_LE(just_deleted, purge_goal);
1880           purge_goal -= just_deleted;
1881           num_deleted += just_deleted;
1882         }
1883       }
1884 
1885       DCHECK_EQ(0u, purge_goal);
1886     }
1887   }
1888 
1889   // Collect garbage for everything. With firefox style we want to preserve
1890   // cookies accessed in kSafeFromGlobalPurgeDays, otherwise evict.
1891   if (cookies_.size() > kMaxCookies && earliest_access_time_ < safe_date) {
1892     DVLOG(net::cookie_util::kVlogGarbageCollection)
1893         << "GarbageCollect() everything";
1894     CookieItVector cookie_its;
1895 
1896     num_deleted += GarbageCollectExpired(
1897         current, CookieMapItPair(cookies_.begin(), cookies_.end()),
1898         &cookie_its);
1899 
1900     if (cookie_its.size() > kMaxCookies) {
1901       DVLOG(net::cookie_util::kVlogGarbageCollection)
1902           << "Deep Garbage Collect everything.";
1903       size_t purge_goal = cookie_its.size() - (kMaxCookies - kPurgeCookies);
1904       DCHECK(purge_goal > kPurgeCookies);
1905 
1906       CookieItVector secure_cookie_its;
1907       CookieItVector non_secure_cookie_its;
1908       SplitCookieVectorIntoSecureAndNonSecure(cookie_its, &secure_cookie_its,
1909                                               &non_secure_cookie_its);
1910       size_t non_secure_purge_goal =
1911           std::min<size_t>(purge_goal, non_secure_cookie_its.size());
1912 
1913       base::Time earliest_non_secure_access_time;
1914       size_t just_deleted = GarbageCollectLeastRecentlyAccessed(
1915           current, safe_date, non_secure_purge_goal, non_secure_cookie_its,
1916           &earliest_non_secure_access_time);
1917       num_deleted += just_deleted;
1918 
1919       if (secure_cookie_its.size() == 0) {
1920         // This case is unlikely, but should still update
1921         // |earliest_access_time_| if only have non-secure cookies.
1922         earliest_access_time_ = earliest_non_secure_access_time;
1923         // Garbage collection can't delete all cookies.
1924         DCHECK(!earliest_access_time_.is_null());
1925       } else if (just_deleted < purge_goal) {
1926         size_t secure_purge_goal = std::min<size_t>(purge_goal - just_deleted,
1927                                                     secure_cookie_its.size());
1928         base::Time earliest_secure_access_time;
1929         num_deleted += GarbageCollectLeastRecentlyAccessed(
1930             current, safe_date, secure_purge_goal, secure_cookie_its,
1931             &earliest_secure_access_time);
1932 
1933         if (!earliest_non_secure_access_time.is_null() &&
1934             earliest_non_secure_access_time < earliest_secure_access_time) {
1935           earliest_access_time_ = earliest_non_secure_access_time;
1936         } else {
1937           earliest_access_time_ = earliest_secure_access_time;
1938         }
1939 
1940         // Garbage collection can't delete all cookies.
1941         DCHECK(!earliest_access_time_.is_null());
1942       }
1943 
1944       // If there are secure cookies, but deleting non-secure cookies was enough
1945       // to meet the purge goal, secure cookies are never examined, so
1946       // |earliest_access_time_| can't be determined. Leaving it alone will mean
1947       // it's no later than the real earliest last access time, so this won't
1948       // lead to any problems.
1949     }
1950   }
1951 
1952   return num_deleted;
1953 }
1954 
GarbageCollectPartitionedCookies(const base::Time & current,const CookiePartitionKey & cookie_partition_key,const std::string & key)1955 size_t CookieMonster::GarbageCollectPartitionedCookies(
1956     const base::Time& current,
1957     const CookiePartitionKey& cookie_partition_key,
1958     const std::string& key) {
1959   DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
1960 
1961   size_t num_deleted = 0;
1962   PartitionedCookieMap::iterator cookie_partition_it =
1963       partitioned_cookies_.find(cookie_partition_key);
1964 
1965   if (cookie_partition_it == partitioned_cookies_.end())
1966     return num_deleted;
1967 
1968   if (NumBytesInCookieMapForKey(*cookie_partition_it->second.get(), key) >
1969           kPerPartitionDomainMaxCookieBytes ||
1970       cookie_partition_it->second->count(key) > kPerPartitionDomainMaxCookies) {
1971     // TODO(crbug.com/1225444): Log garbage collection for partitioned cookies.
1972 
1973     CookieItVector non_expired_cookie_its;
1974     num_deleted += GarbageCollectExpiredPartitionedCookies(
1975         current, cookie_partition_it,
1976         cookie_partition_it->second->equal_range(key), &non_expired_cookie_its);
1977 
1978     size_t bytes_used = NumBytesInCookieItVector(non_expired_cookie_its);
1979 
1980     if (bytes_used > kPerPartitionDomainMaxCookieBytes ||
1981         non_expired_cookie_its.size() > kPerPartitionDomainMaxCookies) {
1982       // TODO(crbug.com/1225444): Log deep garbage collection for partitioned
1983       // cookies.
1984       std::sort(non_expired_cookie_its.begin(), non_expired_cookie_its.end(),
1985                 LRACookieSorter);
1986 
1987       for (size_t i = 0;
1988            bytes_used > kPerPartitionDomainMaxCookieBytes ||
1989            non_expired_cookie_its.size() - i > kPerPartitionDomainMaxCookies;
1990            ++i) {
1991         bytes_used -= NameValueSizeBytes(*non_expired_cookie_its[i]->second);
1992         InternalDeletePartitionedCookie(
1993             cookie_partition_it, non_expired_cookie_its[i], true,
1994             DELETE_COOKIE_EVICTED_PER_PARTITION_DOMAIN);
1995         ++num_deleted;
1996       }
1997     }
1998   }
1999 
2000   // TODO(crbug.com/1225444): Enforce global limit on partitioned cookies.
2001 
2002   return num_deleted;
2003 }
2004 
PurgeLeastRecentMatches(CookieItVector * cookies,CookiePriority priority,size_t to_protect,size_t purge_goal,bool protect_secure_cookies)2005 size_t CookieMonster::PurgeLeastRecentMatches(CookieItVector* cookies,
2006                                               CookiePriority priority,
2007                                               size_t to_protect,
2008                                               size_t purge_goal,
2009                                               bool protect_secure_cookies) {
2010   DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
2011 
2012   // 1. Count number of the cookies at |priority|
2013   size_t cookies_count_possibly_to_be_deleted = CountCookiesForPossibleDeletion(
2014       priority, cookies, false /* count all cookies */);
2015 
2016   // 2. If |cookies_count_possibly_to_be_deleted| at |priority| is less than or
2017   // equal |to_protect|, skip round in order to preserve the quota. This
2018   // involves secure and non-secure cookies at |priority|.
2019   if (cookies_count_possibly_to_be_deleted <= to_protect)
2020     return 0u;
2021 
2022   // 3. Calculate number of secure cookies at |priority|
2023   // and number of cookies at |priority| that can possibly be deleted.
2024   // It is guaranteed we do not delete more than |purge_goal| even if
2025   // |cookies_count_possibly_to_be_deleted| is higher.
2026   size_t secure_cookies = 0u;
2027   if (protect_secure_cookies) {
2028     secure_cookies = CountCookiesForPossibleDeletion(
2029         priority, cookies, protect_secure_cookies /* count secure cookies */);
2030     cookies_count_possibly_to_be_deleted -=
2031         std::max(secure_cookies, to_protect);
2032   } else {
2033     cookies_count_possibly_to_be_deleted -= to_protect;
2034   }
2035 
2036   size_t removed = 0u;
2037   size_t current = 0u;
2038   while ((removed < purge_goal && current < cookies->size()) &&
2039          cookies_count_possibly_to_be_deleted > 0) {
2040     const CanonicalCookie* current_cookie = cookies->at(current)->second.get();
2041     // Only delete the current cookie if the priority is equal to
2042     // the current level.
2043     if (IsCookieEligibleForEviction(priority, protect_secure_cookies,
2044                                     current_cookie)) {
2045       InternalDeleteCookie(cookies->at(current), true,
2046                            DELETE_COOKIE_EVICTED_DOMAIN);
2047       cookies->erase(cookies->begin() + current);
2048       removed++;
2049       cookies_count_possibly_to_be_deleted--;
2050     } else {
2051       current++;
2052     }
2053   }
2054   return removed;
2055 }
2056 
GarbageCollectExpired(const Time & current,const CookieMapItPair & itpair,CookieItVector * cookie_its)2057 size_t CookieMonster::GarbageCollectExpired(const Time& current,
2058                                             const CookieMapItPair& itpair,
2059                                             CookieItVector* cookie_its) {
2060   DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
2061 
2062   int num_deleted = 0;
2063   for (CookieMap::iterator it = itpair.first, end = itpair.second; it != end;) {
2064     auto curit = it;
2065     ++it;
2066 
2067     if (curit->second->IsExpired(current)) {
2068       InternalDeleteCookie(curit, true, DELETE_COOKIE_EXPIRED);
2069       ++num_deleted;
2070     } else if (cookie_its) {
2071       cookie_its->push_back(curit);
2072     }
2073   }
2074 
2075   return num_deleted;
2076 }
2077 
GarbageCollectExpiredPartitionedCookies(const Time & current,const PartitionedCookieMap::iterator & cookie_partition_it,const CookieMapItPair & itpair,CookieItVector * cookie_its)2078 size_t CookieMonster::GarbageCollectExpiredPartitionedCookies(
2079     const Time& current,
2080     const PartitionedCookieMap::iterator& cookie_partition_it,
2081     const CookieMapItPair& itpair,
2082     CookieItVector* cookie_its) {
2083   DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
2084 
2085   int num_deleted = 0;
2086   for (CookieMap::iterator it = itpair.first, end = itpair.second; it != end;) {
2087     auto curit = it;
2088     ++it;
2089 
2090     if (curit->second->IsExpired(current)) {
2091       InternalDeletePartitionedCookie(cookie_partition_it, curit, true,
2092                                       DELETE_COOKIE_EXPIRED);
2093       ++num_deleted;
2094     } else if (cookie_its) {
2095       cookie_its->push_back(curit);
2096     }
2097   }
2098 
2099   return num_deleted;
2100 }
2101 
GarbageCollectAllExpiredPartitionedCookies(const Time & current)2102 void CookieMonster::GarbageCollectAllExpiredPartitionedCookies(
2103     const Time& current) {
2104   DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
2105   for (auto it = partitioned_cookies_.begin();
2106        it != partitioned_cookies_.end();) {
2107     // GarbageCollectExpiredPartitionedCookies calls
2108     // InternalDeletePartitionedCookie which may invalidate
2109     // |cur_cookie_partition_it|.
2110     auto cur_cookie_partition_it = it;
2111     ++it;
2112     GarbageCollectExpiredPartitionedCookies(
2113         current, cur_cookie_partition_it,
2114         CookieMapItPair(cur_cookie_partition_it->second->begin(),
2115                         cur_cookie_partition_it->second->end()),
2116         nullptr /*cookie_its*/);
2117   }
2118 }
2119 
GarbageCollectDeleteRange(const Time & current,DeletionCause cause,CookieItVector::iterator it_begin,CookieItVector::iterator it_end)2120 size_t CookieMonster::GarbageCollectDeleteRange(
2121     const Time& current,
2122     DeletionCause cause,
2123     CookieItVector::iterator it_begin,
2124     CookieItVector::iterator it_end) {
2125   DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
2126 
2127   for (auto it = it_begin; it != it_end; it++) {
2128     InternalDeleteCookie((*it), true, cause);
2129   }
2130   return it_end - it_begin;
2131 }
2132 
GarbageCollectLeastRecentlyAccessed(const base::Time & current,const base::Time & safe_date,size_t purge_goal,CookieItVector cookie_its,base::Time * earliest_time)2133 size_t CookieMonster::GarbageCollectLeastRecentlyAccessed(
2134     const base::Time& current,
2135     const base::Time& safe_date,
2136     size_t purge_goal,
2137     CookieItVector cookie_its,
2138     base::Time* earliest_time) {
2139   DCHECK_LE(purge_goal, cookie_its.size());
2140   DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
2141 
2142   // Sorts up to *and including* |cookie_its[purge_goal]| (if it exists), so
2143   // |earliest_time| will be properly assigned even if
2144   // |global_purge_it| == |cookie_its.begin() + purge_goal|.
2145   SortLeastRecentlyAccessed(
2146       cookie_its.begin(), cookie_its.end(),
2147       cookie_its.size() < purge_goal ? purge_goal + 1 : purge_goal);
2148   // Find boundary to cookies older than safe_date.
2149   auto global_purge_it = LowerBoundAccessDate(
2150       cookie_its.begin(), cookie_its.begin() + purge_goal, safe_date);
2151   // Only delete the old cookies and delete non-secure ones first.
2152   size_t num_deleted =
2153       GarbageCollectDeleteRange(current, DELETE_COOKIE_EVICTED_GLOBAL,
2154                                 cookie_its.begin(), global_purge_it);
2155   if (global_purge_it != cookie_its.end())
2156     *earliest_time = (*global_purge_it)->second->LastAccessDate();
2157   return num_deleted;
2158 }
2159 
2160 // A wrapper around registry_controlled_domains::GetDomainAndRegistry
2161 // to make clear we're creating a key for our local map or for the persistent
2162 // store's use. Here and in FindCookiesForRegistryControlledHost() are the only
2163 // two places where we need to conditionalize based on key type.
2164 //
2165 // Note that this key algorithm explicitly ignores the scheme.  This is
2166 // because when we're entering cookies into the map from the backing store,
2167 // we in general won't have the scheme at that point.
2168 // In practical terms, this means that file cookies will be stored
2169 // in the map either by an empty string or by UNC name (and will be
2170 // limited by kMaxCookiesPerHost), and extension cookies will be stored
2171 // based on the single extension id, as the extension id won't have the
2172 // form of a DNS host and hence GetKey() will return it unchanged.
2173 //
2174 // Arguably the right thing to do here is to make the key
2175 // algorithm dependent on the scheme, and make sure that the scheme is
2176 // available everywhere the key must be obtained (specfically at backing
2177 // store load time).  This would require either changing the backing store
2178 // database schema to include the scheme (far more trouble than it's worth), or
2179 // separating out file cookies into their own CookieMonster instance and
2180 // thus restricting each scheme to a single cookie monster (which might
2181 // be worth it, but is still too much trouble to solve what is currently a
2182 // non-problem).
2183 //
2184 // static
GetKey(base::StringPiece domain)2185 std::string CookieMonster::GetKey(base::StringPiece domain) {
2186   std::string effective_domain(
2187       registry_controlled_domains::GetDomainAndRegistry(
2188           domain, registry_controlled_domains::INCLUDE_PRIVATE_REGISTRIES));
2189   if (effective_domain.empty())
2190     effective_domain = std::string(domain);
2191 
2192   return cookie_util::CookieDomainAsHost(effective_domain);
2193 }
2194 
HasCookieableScheme(const GURL & url)2195 bool CookieMonster::HasCookieableScheme(const GURL& url) {
2196   DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
2197 
2198   // Make sure the request is on a cookie-able url scheme.
2199   bool is_cookieable = base::ranges::any_of(
2200       cookieable_schemes_, [&url](const std::string& cookieable_scheme) {
2201         return url.SchemeIs(cookieable_scheme.c_str());
2202       });
2203 
2204   if (!is_cookieable) {
2205     // The scheme didn't match any in our allowed list.
2206     DVLOG(net::cookie_util::kVlogPerCookieMonster)
2207         << "WARNING: Unsupported cookie scheme: " << url.scheme();
2208   }
2209   return is_cookieable;
2210 }
2211 
GetAccessSemanticsForCookie(const CanonicalCookie & cookie) const2212 CookieAccessSemantics CookieMonster::GetAccessSemanticsForCookie(
2213     const CanonicalCookie& cookie) const {
2214   if (cookie_access_delegate())
2215     return cookie_access_delegate()->GetAccessSemantics(cookie);
2216   return CookieAccessSemantics::UNKNOWN;
2217 }
2218 
2219 // Test to see if stats should be recorded, and record them if so.
2220 // The goal here is to get sampling for the average browser-hour of
2221 // activity.  We won't take samples when the web isn't being surfed,
2222 // and when the web is being surfed, we'll take samples about every
2223 // kRecordStatisticsIntervalSeconds.
2224 // last_statistic_record_time_ is initialized to Now() rather than null
2225 // in the constructor so that we won't take statistics right after
2226 // startup, to avoid bias from browsers that are started but not used.
RecordPeriodicStats(const base::Time & current_time)2227 void CookieMonster::RecordPeriodicStats(const base::Time& current_time) {
2228   DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
2229 
2230   const base::TimeDelta kRecordStatisticsIntervalTime(
2231       base::Seconds(kRecordStatisticsIntervalSeconds));
2232 
2233   // If we've taken statistics recently, return.
2234   if (current_time - last_statistic_record_time_ <=
2235       kRecordStatisticsIntervalTime) {
2236     return;
2237   }
2238 
2239   if (DoRecordPeriodicStats())
2240     last_statistic_record_time_ = current_time;
2241 }
2242 
DoRecordPeriodicStats()2243 bool CookieMonster::DoRecordPeriodicStats() {
2244   DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
2245   // These values are all bogus if we have only partially loaded the cookies.
2246   if (started_fetching_all_cookies_ && !finished_fetching_all_cookies_)
2247     return false;
2248 
2249   base::UmaHistogramCounts100000("Cookie.Count2", cookies_.size());
2250 
2251   if (cookie_access_delegate()) {
2252     std::vector<SchemefulSite> sites;
2253     for (const auto& entry : cookies_) {
2254       sites.emplace_back(
2255           GURL(base::StrCat({url::kHttpsScheme, "://", entry.first})));
2256     }
2257     for (const auto& [partition_key, cookie_map] : partitioned_cookies_) {
2258       for (const auto& [domain, unused_cookie] : *cookie_map) {
2259         sites.emplace_back(
2260             GURL(base::StrCat({url::kHttpsScheme, "://", domain})));
2261       }
2262     }
2263     absl::optional<base::flat_map<SchemefulSite, FirstPartySetEntry>>
2264         maybe_sets = cookie_access_delegate()->FindFirstPartySetEntries(
2265             sites,
2266             base::BindOnce(&CookieMonster::RecordPeriodicFirstPartySetsStats,
2267                            weak_ptr_factory_.GetWeakPtr()));
2268     if (maybe_sets.has_value())
2269       RecordPeriodicFirstPartySetsStats(maybe_sets.value());
2270   }
2271 
2272   // Can be up to kMaxCookies.
2273   UMA_HISTOGRAM_COUNTS_10000("Cookie.NumKeys", num_keys_);
2274 
2275   std::map<std::string, size_t> n_same_site_none_cookies;
2276   for (const auto& [host_key, host_cookie] : cookies_) {
2277     if (!host_cookie || !host_cookie->IsEffectivelySameSiteNone())
2278       continue;
2279     n_same_site_none_cookies[host_key]++;
2280   }
2281   size_t max_n_cookies = 0;
2282   for (const auto& entry : n_same_site_none_cookies) {
2283     max_n_cookies = std::max(max_n_cookies, entry.second);
2284   }
2285   // Can be up to 180 cookies, the max per-domain.
2286   base::UmaHistogramCounts1000("Cookie.MaxSameSiteNoneCookiesPerKey",
2287                                max_n_cookies);
2288 
2289   // Collect stats for partitioned cookies if they are enabled.
2290   if (base::FeatureList::IsEnabled(features::kPartitionedCookies)) {
2291     base::UmaHistogramCounts1000("Cookie.PartitionCount",
2292                                  partitioned_cookies_.size());
2293     base::UmaHistogramCounts100000("Cookie.PartitionedCookieCount",
2294                                    num_partitioned_cookies_);
2295   }
2296 
2297   return true;
2298 }
2299 
RecordPeriodicFirstPartySetsStats(base::flat_map<SchemefulSite,FirstPartySetEntry> sets) const2300 void CookieMonster::RecordPeriodicFirstPartySetsStats(
2301     base::flat_map<SchemefulSite, FirstPartySetEntry> sets) const {
2302   base::flat_map<SchemefulSite, std::set<SchemefulSite>> grouped_by_owner;
2303   for (const auto& [site, entry] : sets) {
2304     grouped_by_owner[entry.primary()].insert(site);
2305   }
2306   for (const auto& set : grouped_by_owner) {
2307     int sample = std::accumulate(
2308         set.second.begin(), set.second.end(), 0,
2309         [this](int acc, const net::SchemefulSite& site) -> int {
2310           DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
2311           if (!site.has_registrable_domain_or_host())
2312             return acc;
2313           return acc + cookies_.count(site.registrable_domain_or_host());
2314         });
2315     base::UmaHistogramCustomCounts("Cookie.PerFirstPartySetCount", sample, 0,
2316                                    4000, 50);
2317   }
2318 }
2319 
DoCookieCallback(base::OnceClosure callback)2320 void CookieMonster::DoCookieCallback(base::OnceClosure callback) {
2321   DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
2322 
2323   MarkCookieStoreAsInitialized();
2324   FetchAllCookiesIfNecessary();
2325   seen_global_task_ = true;
2326 
2327   if (!finished_fetching_all_cookies_ && store_.get()) {
2328     tasks_pending_.push_back(std::move(callback));
2329     return;
2330   }
2331 
2332   std::move(callback).Run();
2333 }
2334 
DoCookieCallbackForURL(base::OnceClosure callback,const GURL & url)2335 void CookieMonster::DoCookieCallbackForURL(base::OnceClosure callback,
2336                                            const GURL& url) {
2337   DoCookieCallbackForHostOrDomain(std::move(callback), url.host_piece());
2338 }
2339 
DoCookieCallbackForHostOrDomain(base::OnceClosure callback,base::StringPiece host_or_domain)2340 void CookieMonster::DoCookieCallbackForHostOrDomain(
2341     base::OnceClosure callback,
2342     base::StringPiece host_or_domain) {
2343   DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
2344   MarkCookieStoreAsInitialized();
2345   FetchAllCookiesIfNecessary();
2346 
2347   // If cookies for the requested domain key (eTLD+1) have been loaded from DB
2348   // then run the task, otherwise load from DB.
2349   if (!finished_fetching_all_cookies_ && store_.get()) {
2350     // If a global task has been previously seen, queue the task as a global
2351     // task. Note that the CookieMonster may be in the middle of executing
2352     // the global queue, |tasks_pending_| may be empty, which is why another
2353     // bool is needed.
2354     if (seen_global_task_) {
2355       tasks_pending_.push_back(std::move(callback));
2356       return;
2357     }
2358 
2359     // Checks if the domain key has been loaded.
2360     std::string key = GetKey(host_or_domain);
2361     if (keys_loaded_.find(key) == keys_loaded_.end()) {
2362       auto it = tasks_pending_for_key_.find(key);
2363       if (it == tasks_pending_for_key_.end()) {
2364         store_->LoadCookiesForKey(
2365             key, base::BindOnce(&CookieMonster::OnKeyLoaded,
2366                                 weak_ptr_factory_.GetWeakPtr(), key));
2367         it = tasks_pending_for_key_
2368                  .insert(std::make_pair(
2369                      key, base::circular_deque<base::OnceClosure>()))
2370                  .first;
2371       }
2372       it->second.push_back(std::move(callback));
2373       return;
2374     }
2375   }
2376 
2377   std::move(callback).Run();
2378 }
2379 
2380 CookieMonster::CookieSentToSamePort
IsCookieSentToSamePortThatSetIt(const GURL & destination,int source_port,CookieSourceScheme source_scheme)2381 CookieMonster::IsCookieSentToSamePortThatSetIt(
2382     const GURL& destination,
2383     int source_port,
2384     CookieSourceScheme source_scheme) {
2385   if (source_port == url::PORT_UNSPECIFIED)
2386     return CookieSentToSamePort::kSourcePortUnspecified;
2387 
2388   if (source_port == url::PORT_INVALID)
2389     return CookieSentToSamePort::kInvalid;
2390 
2391   int destination_port = destination.EffectiveIntPort();
2392   if (source_port == destination_port)
2393     return CookieSentToSamePort::kYes;
2394 
2395   const std::string& destination_scheme = destination.scheme();
2396   bool destination_port_is_default =
2397       url::DefaultPortForScheme(destination_scheme.c_str(),
2398                                 destination_scheme.length()) ==
2399       destination_port;
2400 
2401   // Since the source port has to be specified if we got to this point, that
2402   // means this is a newer cookie that therefore has its scheme set as well.
2403   DCHECK(source_scheme != CookieSourceScheme::kUnset);
2404   std::string source_scheme_string =
2405       source_scheme == CookieSourceScheme::kSecure
2406           ? url::kHttpsScheme
2407           : url::kHttpScheme;  // wss/ws have the same default port values as
2408                                // https/http, so it's ok that we use these.
2409 
2410   bool source_port_is_default =
2411       url::DefaultPortForScheme(source_scheme_string.c_str(),
2412                                 source_scheme_string.length()) == source_port;
2413 
2414   if (destination_port_is_default && source_port_is_default)
2415     return CookieSentToSamePort::kNoButDefault;
2416 
2417   return CookieSentToSamePort::kNo;
2418 }
2419 
SiteHasCookieInOtherPartition(const net::SchemefulSite & site,const absl::optional<CookiePartitionKey> & partition_key) const2420 absl::optional<bool> CookieMonster::SiteHasCookieInOtherPartition(
2421     const net::SchemefulSite& site,
2422     const absl::optional<CookiePartitionKey>& partition_key) const {
2423   DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
2424   // If the partition key is null, it implies the partitioned cookies feature is
2425   // not enabled.
2426   if (!partition_key)
2427     return absl::nullopt;
2428 
2429   std::string domain = site.GetURL().host();
2430   if (store_ && !finished_fetching_all_cookies_ &&
2431       !keys_loaded_.count(domain)) {
2432     return absl::nullopt;
2433   }
2434 
2435   for (const auto& it : partitioned_cookies_) {
2436     if (it.first == partition_key || CookiePartitionKey::HasNonce(it.first))
2437       continue;
2438     if (it.second->find(domain) != it.second->end()) {
2439       return true;
2440     }
2441   }
2442   return false;
2443 }
2444 
2445 }  // namespace net
2446