restricted_cookie_manager.cc 37 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927
  1. // Copyright 2017 The Chromium Authors. All rights reserved.
  2. // Use of this source code is governed by a BSD-style license that can be
  3. // found in the LICENSE file.
  4. #include "services/network/restricted_cookie_manager.h"
  5. #include <memory>
  6. #include <utility>
  7. #include <vector>
  8. #include "base/bind.h"
  9. #include "base/callback_helpers.h"
  10. #include "base/compiler_specific.h" // for [[fallthrough]];
  11. #include "base/memory/raw_ptr.h"
  12. #include "base/memory/weak_ptr.h"
  13. #include "base/metrics/histogram_macros.h"
  14. #include "base/run_loop.h"
  15. #include "base/stl_util.h"
  16. #include "base/strings/string_util.h"
  17. #include "base/task/sequenced_task_runner.h"
  18. #include "base/threading/sequenced_task_runner_handle.h"
  19. #include "mojo/public/cpp/bindings/message.h"
  20. #include "mojo/public/cpp/bindings/remote.h"
  21. #include "net/base/features.h"
  22. #include "net/base/isolation_info.h"
  23. #include "net/base/registry_controlled_domains/registry_controlled_domain.h"
  24. #include "net/cookies/canonical_cookie.h"
  25. #include "net/cookies/cookie_access_result.h"
  26. #include "net/cookies/cookie_constants.h"
  27. #include "net/cookies/cookie_inclusion_status.h"
  28. #include "net/cookies/cookie_options.h"
  29. #include "net/cookies/cookie_partition_key.h"
  30. #include "net/cookies/cookie_store.h"
  31. #include "net/cookies/cookie_util.h"
  32. #include "net/cookies/first_party_set_metadata.h"
  33. #include "net/cookies/site_for_cookies.h"
  34. #include "services/network/cookie_settings.h"
  35. #include "services/network/public/cpp/features.h"
  36. #include "services/network/public/mojom/cookie_access_observer.mojom.h"
  37. #include "services/network/public/mojom/cookie_manager.mojom.h"
  38. #include "services/network/public/mojom/network_context.mojom.h"
  39. #include "services/network/public/mojom/network_service.mojom.h"
  40. #include "url/gurl.h"
  41. namespace network {
  42. namespace {
  43. // TODO(cfredric): the `force_ignore_top_frame_party` param being false prevents
  44. // `document.cookie` access for same-party scripts embedded in an extension
  45. // frame. It would be better if we allowed that similarly to how we allow
  46. // SameParty cookies for requests in same-party contexts embedded in top-level
  47. // extension frames.
  48. const bool kForceIgnoreTopFrameParty = false;
  49. net::CookieOptions MakeOptionsForSet(
  50. mojom::RestrictedCookieManagerRole role,
  51. const GURL& url,
  52. const net::SiteForCookies& site_for_cookies,
  53. const net::IsolationInfo& isolation_info,
  54. const CookieSettings& cookie_settings,
  55. const net::FirstPartySetMetadata& first_party_set_metadata) {
  56. net::CookieOptions options;
  57. bool force_ignore_site_for_cookies =
  58. cookie_settings.ShouldIgnoreSameSiteRestrictions(url, site_for_cookies);
  59. if (role == mojom::RestrictedCookieManagerRole::SCRIPT) {
  60. options.set_exclude_httponly(); // Default, but make it explicit here.
  61. options.set_same_site_cookie_context(
  62. net::cookie_util::ComputeSameSiteContextForScriptSet(
  63. url, site_for_cookies, force_ignore_site_for_cookies));
  64. } else {
  65. // mojom::RestrictedCookieManagerRole::NETWORK
  66. options.set_include_httponly();
  67. options.set_same_site_cookie_context(
  68. net::cookie_util::ComputeSameSiteContextForSubresource(
  69. url, site_for_cookies, force_ignore_site_for_cookies));
  70. }
  71. options.set_same_party_context(first_party_set_metadata.context());
  72. if (isolation_info.party_context().has_value()) {
  73. // Count the top-frame site since it's not in the party_context.
  74. options.set_full_party_context_size(isolation_info.party_context()->size() +
  75. 1);
  76. }
  77. options.set_is_in_nontrivial_first_party_set(
  78. first_party_set_metadata.frame_entry().has_value());
  79. return options;
  80. }
  81. net::CookieOptions MakeOptionsForGet(
  82. mojom::RestrictedCookieManagerRole role,
  83. const GURL& url,
  84. const net::SiteForCookies& site_for_cookies,
  85. const net::IsolationInfo& isolation_info,
  86. const CookieSettings& cookie_settings,
  87. const net::FirstPartySetMetadata& first_party_set_metadata) {
  88. // TODO(https://crbug.com/925311): Wire initiator here.
  89. net::CookieOptions options;
  90. bool force_ignore_site_for_cookies =
  91. cookie_settings.ShouldIgnoreSameSiteRestrictions(url, site_for_cookies);
  92. if (role == mojom::RestrictedCookieManagerRole::SCRIPT) {
  93. options.set_exclude_httponly(); // Default, but make it explicit here.
  94. options.set_same_site_cookie_context(
  95. net::cookie_util::ComputeSameSiteContextForScriptGet(
  96. url, site_for_cookies, absl::nullopt /*initiator*/,
  97. force_ignore_site_for_cookies));
  98. } else {
  99. // mojom::RestrictedCookieManagerRole::NETWORK
  100. options.set_include_httponly();
  101. options.set_same_site_cookie_context(
  102. net::cookie_util::ComputeSameSiteContextForSubresource(
  103. url, site_for_cookies, force_ignore_site_for_cookies));
  104. }
  105. options.set_same_party_context(first_party_set_metadata.context());
  106. if (isolation_info.party_context().has_value()) {
  107. // Count the top-frame site since it's not in the party_context.
  108. options.set_full_party_context_size(isolation_info.party_context()->size() +
  109. 1);
  110. }
  111. options.set_is_in_nontrivial_first_party_set(
  112. first_party_set_metadata.frame_entry().has_value());
  113. return options;
  114. }
  115. } // namespace
  116. // static
  117. void RestrictedCookieManager::ComputeFirstPartySetMetadata(
  118. const url::Origin& origin,
  119. const net::CookieStore* cookie_store,
  120. const net::IsolationInfo& isolation_info,
  121. base::OnceCallback<void(net::FirstPartySetMetadata)> callback) {
  122. std::pair<base::OnceCallback<void(net::FirstPartySetMetadata)>,
  123. base::OnceCallback<void(net::FirstPartySetMetadata)>>
  124. callbacks = base::SplitOnceCallback(std::move(callback));
  125. absl::optional<net::FirstPartySetMetadata> metadata =
  126. net::cookie_util::ComputeFirstPartySetMetadataMaybeAsync(
  127. /*request_site=*/net::SchemefulSite(origin), isolation_info,
  128. cookie_store->cookie_access_delegate(), kForceIgnoreTopFrameParty,
  129. std::move(callbacks.first));
  130. if (metadata.has_value())
  131. std::move(callbacks.second).Run(std::move(metadata.value()));
  132. }
  133. bool CookieWithAccessResultComparer::operator()(
  134. const net::CookieWithAccessResult& cookie_with_access_result1,
  135. const net::CookieWithAccessResult& cookie_with_access_result2) const {
  136. // Compare just the cookie portion of the CookieWithAccessResults so a cookie
  137. // only ever has one entry in the map. For a given cookie we want to send a
  138. // new access notification whenever its access results change. If we keyed off
  139. // of both the cookie and its current access result, if a cookie shifted from
  140. // "allowed" to "blocked" the cookie would wind up with two entries in the
  141. // map. If the cookie then shifted back to "allowed" we wouldn't send a new
  142. // notification because cookie/allowed already existed in the map. In the case
  143. // of a cookie shifting from "allowed" to "blocked,"
  144. // SkipAccessNotificationForCookieItem() checks the access result. If the
  145. // cookie exists in the map but its status is "allowed" we evict the old
  146. // entry.
  147. return cookie_with_access_result1.cookie < cookie_with_access_result2.cookie;
  148. }
  149. CookieAccesses* RestrictedCookieManager::GetCookieAccessesForURLAndSite(
  150. const GURL& url,
  151. const net::SiteForCookies& site_for_cookies) {
  152. std::unique_ptr<CookieAccesses>& entry =
  153. recent_cookie_accesses_[std::make_pair(url, site_for_cookies)];
  154. if (!entry) {
  155. entry = std::make_unique<CookieAccesses>();
  156. }
  157. return entry.get();
  158. }
  159. bool RestrictedCookieManager::SkipAccessNotificationForCookieItem(
  160. CookieAccesses* cookie_accesses,
  161. const net::CookieWithAccessResult& cookie_item) {
  162. DCHECK(cookie_accesses);
  163. // Have we sent information about this cookie to the |cookie_observer_|
  164. // before?
  165. std::set<net::CookieWithAccessResult>::iterator existing_slot =
  166. cookie_accesses->find(cookie_item);
  167. // If this is the first time seeing this cookie make a note and don't skip
  168. // the notification.
  169. if (existing_slot == cookie_accesses->end()) {
  170. // Don't store more than a max number of cookies, in the interest of
  171. // limiting memory consumption.
  172. const int kMaxCookieCount = 32;
  173. if (cookie_accesses->size() == kMaxCookieCount) {
  174. cookie_accesses->clear();
  175. }
  176. cookie_accesses->insert(cookie_item);
  177. return false;
  178. }
  179. // If the cookie and its access result are unchanged since we last updated
  180. // the |cookie_observer_|, skip notifying the |cookie_observer_| again.
  181. if (existing_slot->cookie.HasEquivalentDataMembers(cookie_item.cookie) &&
  182. existing_slot->access_result == cookie_item.access_result) {
  183. return true;
  184. }
  185. // The cookie's access result has changed - update it in our record of what
  186. // we've sent to the |cookie_observer_|. It's safe to update the existing
  187. // entry in the set because the access_result field does not determine the
  188. // CookieWithAccessResult's location in the set.
  189. const_cast<net::CookieWithAccessResult&>(*existing_slot).access_result =
  190. cookie_item.access_result;
  191. // Don't skip notifying the |cookie_observer_| of the change.
  192. return false;
  193. }
  194. class RestrictedCookieManager::Listener : public base::LinkNode<Listener> {
  195. public:
  196. Listener(net::CookieStore* cookie_store,
  197. const RestrictedCookieManager* restricted_cookie_manager,
  198. const GURL& url,
  199. const net::SiteForCookies& site_for_cookies,
  200. const url::Origin& top_frame_origin,
  201. const absl::optional<net::CookiePartitionKey>& cookie_partition_key,
  202. net::CookieOptions options,
  203. mojo::PendingRemote<mojom::CookieChangeListener> mojo_listener,
  204. const bool first_party_sets_enabled)
  205. : cookie_store_(cookie_store),
  206. restricted_cookie_manager_(restricted_cookie_manager),
  207. url_(url),
  208. site_for_cookies_(site_for_cookies),
  209. top_frame_origin_(top_frame_origin),
  210. options_(options),
  211. mojo_listener_(std::move(mojo_listener)),
  212. first_party_sets_enabled_(first_party_sets_enabled) {
  213. // TODO(pwnall): add a constructor w/options to net::CookieChangeDispatcher.
  214. cookie_store_subscription_ =
  215. cookie_store->GetChangeDispatcher().AddCallbackForUrl(
  216. url, cookie_partition_key,
  217. base::BindRepeating(
  218. &Listener::OnCookieChange,
  219. // Safe because net::CookieChangeDispatcher guarantees that
  220. // the callback will stop being called immediately after we
  221. // remove the subscription, and the cookie store lives on
  222. // the same thread as we do.
  223. base::Unretained(this)));
  224. }
  225. Listener(const Listener&) = delete;
  226. Listener& operator=(const Listener&) = delete;
  227. ~Listener() { DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); }
  228. mojo::Remote<mojom::CookieChangeListener>& mojo_listener() {
  229. DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
  230. return mojo_listener_;
  231. }
  232. private:
  233. // net::CookieChangeDispatcher callback.
  234. void OnCookieChange(const net::CookieChangeInfo& change) {
  235. DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
  236. bool delegate_treats_url_as_trustworthy =
  237. cookie_store_->cookie_access_delegate() &&
  238. cookie_store_->cookie_access_delegate()->ShouldTreatUrlAsTrustworthy(
  239. url_);
  240. // CookieChangeDispatcher doesn't check for inclusion against `options_`, so
  241. // we need to double-check that.
  242. net::CookieSamePartyStatus same_party_status =
  243. net::cookie_util::GetSamePartyStatus(change.cookie, options_,
  244. first_party_sets_enabled_);
  245. if (!change.cookie
  246. .IncludeForRequestURL(
  247. url_, options_,
  248. net::CookieAccessParams{change.access_result.access_semantics,
  249. delegate_treats_url_as_trustworthy,
  250. same_party_status})
  251. .status.IsInclude()) {
  252. return;
  253. }
  254. // When a user blocks a site's access to cookies, the existing cookies are
  255. // not deleted. This check prevents the site from observing their cookies
  256. // being deleted at a later time, which can happen due to eviction or due to
  257. // the user explicitly deleting all cookies.
  258. if (!restricted_cookie_manager_->cookie_settings().IsCookieAccessible(
  259. change.cookie, url_, site_for_cookies_, top_frame_origin_)) {
  260. return;
  261. }
  262. mojo_listener_->OnCookieChange(change);
  263. }
  264. // Expected to outlive |restricted_cookie_manager_| which outlives this.
  265. raw_ptr<const net::CookieStore> cookie_store_;
  266. // The CookieChangeDispatcher subscription used by this listener.
  267. std::unique_ptr<net::CookieChangeSubscription> cookie_store_subscription_;
  268. // Raw pointer usage is safe because RestrictedCookieManager owns this
  269. // instance and is guaranteed to outlive it.
  270. const raw_ptr<const RestrictedCookieManager> restricted_cookie_manager_;
  271. // The URL whose cookies this listener is interested in.
  272. const GURL url_;
  273. // Site context in which we're used; used to determine if a cookie is accessed
  274. // in a third-party context.
  275. const net::SiteForCookies site_for_cookies_;
  276. // Site context in which we're used; used to check content settings.
  277. const url::Origin top_frame_origin_;
  278. // CanonicalCookie::IncludeForRequestURL options for this listener's interest.
  279. const net::CookieOptions options_;
  280. mojo::Remote<mojom::CookieChangeListener> mojo_listener_;
  281. const bool first_party_sets_enabled_;
  282. SEQUENCE_CHECKER(sequence_checker_);
  283. };
  284. RestrictedCookieManager::RestrictedCookieManager(
  285. const mojom::RestrictedCookieManagerRole role,
  286. net::CookieStore* cookie_store,
  287. const CookieSettings& cookie_settings,
  288. const url::Origin& origin,
  289. const net::IsolationInfo& isolation_info,
  290. mojo::PendingRemote<mojom::CookieAccessObserver> cookie_observer,
  291. const bool first_party_sets_enabled,
  292. net::FirstPartySetMetadata first_party_set_metadata)
  293. : role_(role),
  294. cookie_store_(cookie_store),
  295. cookie_settings_(cookie_settings),
  296. origin_(origin),
  297. isolation_info_(isolation_info),
  298. cookie_observer_(std::move(cookie_observer)),
  299. first_party_set_metadata_(std::move(first_party_set_metadata)),
  300. cookie_partition_key_(net::CookiePartitionKey::FromNetworkIsolationKey(
  301. isolation_info.network_isolation_key(),
  302. base::OptionalOrNullptr(
  303. first_party_set_metadata_.top_frame_entry().has_value()
  304. ? absl::make_optional(
  305. first_party_set_metadata_.top_frame_entry()->primary())
  306. : absl::nullopt))),
  307. cookie_partition_key_collection_(
  308. net::CookiePartitionKeyCollection::FromOptional(
  309. cookie_partition_key_)),
  310. first_party_sets_enabled_(first_party_sets_enabled) {
  311. DCHECK(cookie_store);
  312. }
  313. RestrictedCookieManager::~RestrictedCookieManager() {
  314. DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
  315. base::LinkNode<Listener>* node = listeners_.head();
  316. while (node != listeners_.end()) {
  317. Listener* listener_reference = node->value();
  318. node = node->next();
  319. // The entire list is going away, no need to remove nodes from it.
  320. delete listener_reference;
  321. }
  322. }
  323. void RestrictedCookieManager::OverrideIsolationInfoForTesting(
  324. const net::IsolationInfo& new_isolation_info) {
  325. base::RunLoop run_loop;
  326. isolation_info_ = new_isolation_info;
  327. ComputeFirstPartySetMetadata(
  328. origin_, cookie_store_, isolation_info_,
  329. base::BindOnce(
  330. &RestrictedCookieManager::OnGotFirstPartySetMetadataForTesting,
  331. weak_ptr_factory_.GetWeakPtr(), run_loop.QuitClosure()));
  332. run_loop.Run();
  333. }
  334. void RestrictedCookieManager::OnGotFirstPartySetMetadataForTesting(
  335. base::OnceClosure done_closure,
  336. net::FirstPartySetMetadata first_party_set_metadata) {
  337. first_party_set_metadata_ = std::move(first_party_set_metadata);
  338. cookie_partition_key_ = net::CookiePartitionKey::FromNetworkIsolationKey(
  339. isolation_info_.network_isolation_key(),
  340. base::OptionalOrNullptr(
  341. first_party_set_metadata_.top_frame_entry().has_value()
  342. ? absl::make_optional(
  343. first_party_set_metadata_.top_frame_entry()->primary())
  344. : absl::nullopt));
  345. cookie_partition_key_collection_ =
  346. net::CookiePartitionKeyCollection::FromOptional(cookie_partition_key_);
  347. std::move(done_closure).Run();
  348. }
  349. bool RestrictedCookieManager::IsPartitionedCookiesEnabled() const {
  350. return cookie_partition_key_.has_value();
  351. }
  352. namespace {
  353. bool PartitionedCookiesAllowed(
  354. bool partitioned_cookies_runtime_feature_enabled,
  355. const absl::optional<net::CookiePartitionKey>& cookie_partition_key) {
  356. if (base::FeatureList::IsEnabled(
  357. net::features::kPartitionedCookiesBypassOriginTrial) ||
  358. partitioned_cookies_runtime_feature_enabled)
  359. return true;
  360. // We allow partition keys which have a nonce since the Origin Trial is
  361. // only meant to test cookies set with the Partitioned attribute.
  362. return cookie_partition_key && cookie_partition_key->nonce();
  363. }
  364. } // namespace
  365. void RestrictedCookieManager::GetAllForUrl(
  366. const GURL& url,
  367. const net::SiteForCookies& site_for_cookies,
  368. const url::Origin& top_frame_origin,
  369. mojom::CookieManagerGetOptionsPtr options,
  370. bool partitioned_cookies_runtime_feature_enabled,
  371. GetAllForUrlCallback callback) {
  372. DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
  373. if (!ValidateAccessToCookiesAt(url, site_for_cookies, top_frame_origin)) {
  374. std::move(callback).Run({});
  375. return;
  376. }
  377. // TODO(morlovich): Try to validate site_for_cookies as well.
  378. net::CookieOptions net_options =
  379. MakeOptionsForGet(role_, url, site_for_cookies, isolation_info_,
  380. cookie_settings(), first_party_set_metadata_);
  381. // TODO(https://crbug.com/977040): remove set_return_excluded_cookies() once
  382. // removing deprecation warnings.
  383. net_options.set_return_excluded_cookies();
  384. cookie_store_->GetCookieListWithOptionsAsync(
  385. url, net_options,
  386. PartitionedCookiesAllowed(partitioned_cookies_runtime_feature_enabled,
  387. cookie_partition_key_)
  388. ? cookie_partition_key_collection_
  389. : net::CookiePartitionKeyCollection(),
  390. base::BindOnce(&RestrictedCookieManager::CookieListToGetAllForUrlCallback,
  391. weak_ptr_factory_.GetWeakPtr(), url, site_for_cookies,
  392. top_frame_origin, net_options, std::move(options),
  393. std::move(callback)));
  394. }
  395. void RestrictedCookieManager::CookieListToGetAllForUrlCallback(
  396. const GURL& url,
  397. const net::SiteForCookies& site_for_cookies,
  398. const url::Origin& top_frame_origin,
  399. const net::CookieOptions& net_options,
  400. mojom::CookieManagerGetOptionsPtr options,
  401. GetAllForUrlCallback callback,
  402. const net::CookieAccessResultList& cookie_list,
  403. const net::CookieAccessResultList& excluded_list) {
  404. DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
  405. net::CookieAccessResultList maybe_included_cookies = cookie_list;
  406. net::CookieAccessResultList excluded_cookies = excluded_list;
  407. cookie_settings().AnnotateAndMoveUserBlockedCookies(
  408. url, site_for_cookies, &top_frame_origin, maybe_included_cookies,
  409. excluded_cookies);
  410. std::vector<net::CookieWithAccessResult> result;
  411. std::vector<mojom::CookieOrLineWithAccessResultPtr>
  412. on_cookies_accessed_result;
  413. CookieAccesses* cookie_accesses =
  414. GetCookieAccessesForURLAndSite(url, site_for_cookies);
  415. auto add_excluded = [&]() {
  416. // TODO(https://crbug.com/977040): Stop reporting accesses of cookies with
  417. // warning reasons once samesite tightening up is rolled out.
  418. for (const auto& cookie_and_access_result : excluded_cookies) {
  419. if (!cookie_and_access_result.access_result.status.ShouldWarn() &&
  420. !cookie_and_access_result.access_result.status.HasOnlyExclusionReason(
  421. net::CookieInclusionStatus::EXCLUDE_USER_PREFERENCES)) {
  422. continue;
  423. }
  424. // Skip sending a notification about this cookie access?
  425. if (SkipAccessNotificationForCookieItem(cookie_accesses,
  426. cookie_and_access_result)) {
  427. continue;
  428. }
  429. on_cookies_accessed_result.push_back(
  430. mojom::CookieOrLineWithAccessResult::New(
  431. mojom::CookieOrLine::NewCookie(cookie_and_access_result.cookie),
  432. cookie_and_access_result.access_result));
  433. }
  434. };
  435. if (!base::FeatureList::IsEnabled(features::kFasterSetCookie))
  436. add_excluded();
  437. if (!maybe_included_cookies.empty())
  438. result.reserve(maybe_included_cookies.size());
  439. mojom::CookieMatchType match_type = options->match_type;
  440. const std::string& match_name = options->name;
  441. for (const net::CookieWithAccessResult& cookie_item :
  442. maybe_included_cookies) {
  443. const net::CanonicalCookie& cookie = cookie_item.cookie;
  444. net::CookieAccessResult access_result = cookie_item.access_result;
  445. const std::string& cookie_name = cookie.Name();
  446. if (match_type == mojom::CookieMatchType::EQUALS) {
  447. if (cookie_name != match_name)
  448. continue;
  449. } else if (match_type == mojom::CookieMatchType::STARTS_WITH) {
  450. if (!base::StartsWith(cookie_name, match_name,
  451. base::CompareCase::SENSITIVE)) {
  452. continue;
  453. }
  454. } else {
  455. NOTREACHED();
  456. }
  457. if (access_result.status.IsInclude()) {
  458. result.push_back(cookie_item);
  459. }
  460. if (!base::FeatureList::IsEnabled(features::kFasterSetCookie)) {
  461. // Skip sending a notification about this cookie access?
  462. if (SkipAccessNotificationForCookieItem(cookie_accesses, cookie_item)) {
  463. continue;
  464. }
  465. on_cookies_accessed_result.push_back(
  466. mojom::CookieOrLineWithAccessResult::New(
  467. mojom::CookieOrLine::NewCookie(cookie), access_result));
  468. }
  469. }
  470. auto notify_observer = [&]() {
  471. if (cookie_observer_ && !on_cookies_accessed_result.empty()) {
  472. cookie_observer_->OnCookiesAccessed(mojom::CookieAccessDetails::New(
  473. mojom::CookieAccessDetails::Type::kRead, url, site_for_cookies,
  474. std::move(on_cookies_accessed_result), absl::nullopt));
  475. }
  476. };
  477. if (!base::FeatureList::IsEnabled(features::kFasterSetCookie))
  478. notify_observer();
  479. if (!maybe_included_cookies.empty() && IsPartitionedCookiesEnabled()) {
  480. UMA_HISTOGRAM_COUNTS_100(
  481. "Net.RestrictedCookieManager.PartitionedCookiesInScript",
  482. base::ranges::count_if(result,
  483. [](const net::CookieWithAccessResult& c) {
  484. return c.cookie.IsPartitioned();
  485. }));
  486. }
  487. std::move(callback).Run(
  488. base::FeatureList::IsEnabled(features::kFasterSetCookie)
  489. ? result
  490. : std::move(result));
  491. if (base::FeatureList::IsEnabled(features::kFasterSetCookie)) {
  492. add_excluded();
  493. for (auto& cookie : result) {
  494. // Skip sending a notification about this cookie access?
  495. if (SkipAccessNotificationForCookieItem(cookie_accesses, cookie)) {
  496. continue;
  497. }
  498. on_cookies_accessed_result.push_back(
  499. mojom::CookieOrLineWithAccessResult::New(
  500. mojom::CookieOrLine::NewCookie(cookie.cookie),
  501. cookie.access_result));
  502. }
  503. notify_observer();
  504. }
  505. }
  506. void RestrictedCookieManager::SetCanonicalCookie(
  507. const net::CanonicalCookie& cookie,
  508. const GURL& url,
  509. const net::SiteForCookies& site_for_cookies,
  510. const url::Origin& top_frame_origin,
  511. net::CookieInclusionStatus status,
  512. SetCanonicalCookieCallback callback) {
  513. DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
  514. // Don't allow a status that has an exclusion reason as they should have
  515. // already been taken care of on the renderer side.
  516. if (!status.IsInclude()) {
  517. mojo::ReportBadMessage(
  518. "RestrictedCookieManager: unexpected cookie inclusion status");
  519. std::move(callback).Run(false);
  520. return;
  521. }
  522. if (!ValidateAccessToCookiesAt(url, site_for_cookies, top_frame_origin,
  523. &cookie)) {
  524. std::move(callback).Run(false);
  525. return;
  526. }
  527. // TODO(morlovich): Try to validate site_for_cookies as well.
  528. bool blocked = !cookie_settings_.IsCookieAccessible(
  529. cookie, url, site_for_cookies, top_frame_origin);
  530. if (blocked)
  531. status.AddExclusionReason(
  532. net::CookieInclusionStatus::EXCLUDE_USER_PREFERENCES);
  533. // Don't allow URLs with leading dots like https://.some-weird-domain.com
  534. // This probably never happens.
  535. if (!net::cookie_util::DomainIsHostOnly(url.host()))
  536. status.AddExclusionReason(
  537. net::CookieInclusionStatus::EXCLUDE_INVALID_DOMAIN);
  538. if (!status.IsInclude()) {
  539. if (cookie_observer_) {
  540. std::vector<network::mojom::CookieOrLineWithAccessResultPtr>
  541. result_with_access_result;
  542. result_with_access_result.push_back(
  543. mojom::CookieOrLineWithAccessResult::New(
  544. mojom::CookieOrLine::NewCookie(cookie),
  545. net::CookieAccessResult(status)));
  546. cookie_observer_->OnCookiesAccessed(mojom::CookieAccessDetails::New(
  547. mojom::CookieAccessDetails::Type::kChange, url, site_for_cookies,
  548. std::move(result_with_access_result), absl::nullopt));
  549. }
  550. std::move(callback).Run(false);
  551. return;
  552. }
  553. // TODO(pwnall): Validate the CanonicalCookie fields.
  554. // Update the creation and last access times.
  555. // Note: This used to be a call to NowFromSystemTime, but this caused
  556. // inconsistency with the expiration date, which was capped checking
  557. // against Now. If any issues crop up related to this change please
  558. // contact the owners of http://crbug.com/1335859.
  559. base::Time now = base::Time::Now();
  560. // TODO(http://crbug.com/1024053): Log metrics
  561. const GURL& origin_url = origin_.GetURL();
  562. net::CookieSourceScheme source_scheme =
  563. GURL::SchemeIsCryptographic(origin_.scheme())
  564. ? net::CookieSourceScheme::kSecure
  565. : net::CookieSourceScheme::kNonSecure;
  566. // If the renderer's cookie has a partition key that was not created using
  567. // CookiePartitionKey::FromScript, then the cookie's partition key should be
  568. // equal to RestrictedCookieManager's partition key.
  569. absl::optional<net::CookiePartitionKey> cookie_partition_key =
  570. cookie.PartitionKey();
  571. // If the `cookie_partition_key_` has a nonce then force all cookie writes to
  572. // be in the nonce based partition even if the cookie was not set with the
  573. // Partitioned attribute.
  574. if (net::CookiePartitionKey::HasNonce(cookie_partition_key_)) {
  575. cookie_partition_key = cookie_partition_key_;
  576. }
  577. if (cookie_partition_key) {
  578. // RestrictedCookieManager having a null partition key strictly implies the
  579. // feature is disabled. If that is the case, we treat the cookie as
  580. // unpartitioned.
  581. if (!cookie_partition_key_) {
  582. cookie_partition_key = absl::nullopt;
  583. } else {
  584. bool cookie_partition_key_ok =
  585. cookie_partition_key->from_script() ||
  586. cookie_partition_key.value() == cookie_partition_key_.value();
  587. UMA_HISTOGRAM_BOOLEAN("Net.RestrictedCookieManager.CookiePartitionKeyOK",
  588. cookie_partition_key_ok);
  589. if (!cookie_partition_key_ok) {
  590. mojo::ReportBadMessage(
  591. "RestrictedCookieManager: unexpected cookie partition key");
  592. std::move(callback).Run(false);
  593. return;
  594. }
  595. if (cookie_partition_key->from_script()) {
  596. cookie_partition_key = cookie_partition_key_;
  597. }
  598. }
  599. }
  600. if (IsPartitionedCookiesEnabled()) {
  601. UMA_HISTOGRAM_BOOLEAN("Net.RestrictedCookieManager.SetPartitionedCookie",
  602. cookie_partition_key.has_value());
  603. }
  604. std::unique_ptr<net::CanonicalCookie> sanitized_cookie =
  605. net::CanonicalCookie::FromStorage(
  606. cookie.Name(), cookie.Value(), cookie.Domain(), cookie.Path(), now,
  607. cookie.ExpiryDate(), now, now, cookie.IsSecure(), cookie.IsHttpOnly(),
  608. cookie.SameSite(), cookie.Priority(), cookie.IsSameParty(),
  609. cookie_partition_key, source_scheme, origin_.port());
  610. DCHECK(sanitized_cookie);
  611. // FromStorage() uses a less strict version of IsCanonical(), we need to check
  612. // the stricter version as well here.
  613. if (!sanitized_cookie->IsCanonical()) {
  614. std::move(callback).Run(false);
  615. return;
  616. }
  617. net::CanonicalCookie cookie_copy = *sanitized_cookie;
  618. net::CookieOptions options =
  619. MakeOptionsForSet(role_, url, site_for_cookies, isolation_info_,
  620. cookie_settings(), first_party_set_metadata_);
  621. net::CookieAccessResult cookie_access_result(status);
  622. cookie_store_->SetCanonicalCookieAsync(
  623. std::move(sanitized_cookie), origin_url, options,
  624. base::BindOnce(&RestrictedCookieManager::SetCanonicalCookieResult,
  625. weak_ptr_factory_.GetWeakPtr(), url, site_for_cookies,
  626. cookie_copy, options, std::move(callback)),
  627. cookie_access_result);
  628. }
  629. void RestrictedCookieManager::SetCanonicalCookieResult(
  630. const GURL& url,
  631. const net::SiteForCookies& site_for_cookies,
  632. const net::CanonicalCookie& cookie,
  633. const net::CookieOptions& net_options,
  634. SetCanonicalCookieCallback user_callback,
  635. net::CookieAccessResult access_result) {
  636. // TODO(https://crbug.com/977040): Only report pure INCLUDE once samesite
  637. // tightening up is rolled out.
  638. DCHECK(!access_result.status.HasExclusionReason(
  639. net::CookieInclusionStatus::EXCLUDE_USER_PREFERENCES));
  640. if (access_result.status.IsInclude() || access_result.status.ShouldWarn()) {
  641. if (cookie_observer_) {
  642. std::vector<mojom::CookieOrLineWithAccessResultPtr> notify;
  643. notify.push_back(mojom::CookieOrLineWithAccessResult::New(
  644. mojom::CookieOrLine::NewCookie(cookie), access_result));
  645. cookie_observer_->OnCookiesAccessed(mojom::CookieAccessDetails::New(
  646. mojom::CookieAccessDetails::Type::kChange, url, site_for_cookies,
  647. std::move(notify), absl::nullopt));
  648. }
  649. }
  650. std::move(user_callback).Run(access_result.status.IsInclude());
  651. }
  652. void RestrictedCookieManager::AddChangeListener(
  653. const GURL& url,
  654. const net::SiteForCookies& site_for_cookies,
  655. const url::Origin& top_frame_origin,
  656. mojo::PendingRemote<mojom::CookieChangeListener> mojo_listener,
  657. AddChangeListenerCallback callback) {
  658. DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
  659. if (!ValidateAccessToCookiesAt(url, site_for_cookies, top_frame_origin)) {
  660. std::move(callback).Run();
  661. return;
  662. }
  663. net::CookieOptions net_options =
  664. MakeOptionsForGet(role_, url, site_for_cookies, isolation_info_,
  665. cookie_settings(), first_party_set_metadata_);
  666. auto listener = std::make_unique<Listener>(
  667. cookie_store_, this, url, site_for_cookies, top_frame_origin,
  668. cookie_partition_key_, net_options, std::move(mojo_listener),
  669. first_party_sets_enabled_);
  670. listener->mojo_listener().set_disconnect_handler(
  671. base::BindOnce(&RestrictedCookieManager::RemoveChangeListener,
  672. weak_ptr_factory_.GetWeakPtr(),
  673. // Safe because this owns the listener, so the listener is
  674. // guaranteed to be alive for as long as the weak pointer
  675. // above resolves.
  676. base::Unretained(listener.get())));
  677. // The linked list takes over the Listener ownership.
  678. listeners_.Append(listener.release());
  679. std::move(callback).Run();
  680. }
  681. void RestrictedCookieManager::SetCookieFromString(
  682. const GURL& url,
  683. const net::SiteForCookies& site_for_cookies,
  684. const url::Origin& top_frame_origin,
  685. const std::string& cookie,
  686. bool partitioned_cookies_runtime_feature_enabled,
  687. SetCookieFromStringCallback callback) {
  688. DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
  689. bool site_for_cookies_ok =
  690. BoundSiteForCookies().IsEquivalent(site_for_cookies);
  691. bool top_frame_origin_ok = top_frame_origin == BoundTopFrameOrigin();
  692. if (base::FeatureList::IsEnabled(features::kFasterSetCookie)) {
  693. std::move(callback).Run(site_for_cookies_ok, top_frame_origin_ok);
  694. callback = base::DoNothing();
  695. }
  696. net::CookieInclusionStatus status;
  697. std::unique_ptr<net::CanonicalCookie> parsed_cookie =
  698. net::CanonicalCookie::Create(
  699. url, cookie, base::Time::Now(), absl::nullopt /* server_time */,
  700. PartitionedCookiesAllowed(partitioned_cookies_runtime_feature_enabled,
  701. cookie_partition_key_)
  702. ? cookie_partition_key_
  703. : absl::nullopt,
  704. &status);
  705. if (!parsed_cookie) {
  706. if (cookie_observer_) {
  707. std::vector<network::mojom::CookieOrLineWithAccessResultPtr>
  708. result_with_access_result;
  709. result_with_access_result.push_back(
  710. mojom::CookieOrLineWithAccessResult::New(
  711. mojom::CookieOrLine::NewCookieString(cookie),
  712. net::CookieAccessResult(status)));
  713. cookie_observer_->OnCookiesAccessed(mojom::CookieAccessDetails::New(
  714. mojom::CookieAccessDetails::Type::kChange, url, site_for_cookies,
  715. std::move(result_with_access_result), absl::nullopt));
  716. }
  717. std::move(callback).Run(site_for_cookies_ok, top_frame_origin_ok);
  718. return;
  719. }
  720. // Further checks (origin_, settings), as well as logging done by
  721. // SetCanonicalCookie()
  722. SetCanonicalCookie(
  723. *parsed_cookie, url, site_for_cookies, top_frame_origin, status,
  724. base::BindOnce([](base::OnceClosure closure,
  725. bool success) { std::move(closure).Run(); },
  726. // Although these values are being called outside
  727. // ValidateAccessToCookiesAt, the checks done in that
  728. // method are called shortly after synchronously.
  729. base::BindOnce(std::move(callback), site_for_cookies_ok,
  730. top_frame_origin_ok)));
  731. }
  732. void RestrictedCookieManager::GetCookiesString(
  733. const GURL& url,
  734. const net::SiteForCookies& site_for_cookies,
  735. const url::Origin& top_frame_origin,
  736. bool partitioned_cookies_runtime_feature_enabled,
  737. GetCookiesStringCallback callback) {
  738. DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
  739. // Checks done by GetAllForUrl.
  740. // Match everything.
  741. auto match_options = mojom::CookieManagerGetOptions::New();
  742. match_options->name = "";
  743. match_options->match_type = mojom::CookieMatchType::STARTS_WITH;
  744. GetAllForUrl(url, site_for_cookies, top_frame_origin,
  745. std::move(match_options),
  746. partitioned_cookies_runtime_feature_enabled,
  747. base::BindOnce([](const std::vector<net::CookieWithAccessResult>&
  748. cookies) {
  749. return net::CanonicalCookie::BuildCookieLine(cookies);
  750. }).Then(std::move(callback)));
  751. }
  752. void RestrictedCookieManager::CookiesEnabledFor(
  753. const GURL& url,
  754. const net::SiteForCookies& site_for_cookies,
  755. const url::Origin& top_frame_origin,
  756. CookiesEnabledForCallback callback) {
  757. if (!ValidateAccessToCookiesAt(url, site_for_cookies, top_frame_origin)) {
  758. std::move(callback).Run(false);
  759. return;
  760. }
  761. std::move(callback).Run(cookie_settings_.IsFullCookieAccessAllowed(
  762. url, site_for_cookies, top_frame_origin,
  763. CookieSettings::QueryReason::kCookies));
  764. }
  765. void RestrictedCookieManager::RemoveChangeListener(Listener* listener) {
  766. DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
  767. listener->RemoveFromList();
  768. delete listener;
  769. }
  770. bool RestrictedCookieManager::ValidateAccessToCookiesAt(
  771. const GURL& url,
  772. const net::SiteForCookies& site_for_cookies,
  773. const url::Origin& top_frame_origin,
  774. const net::CanonicalCookie* cookie_being_set) {
  775. if (origin_.opaque()) {
  776. mojo::ReportBadMessage("Access is denied in this context");
  777. return false;
  778. }
  779. bool site_for_cookies_ok =
  780. BoundSiteForCookies().IsEquivalent(site_for_cookies);
  781. DCHECK(site_for_cookies_ok)
  782. << "site_for_cookies from renderer='" << site_for_cookies.ToDebugString()
  783. << "' from browser='" << BoundSiteForCookies().ToDebugString() << "';";
  784. bool top_frame_origin_ok = (top_frame_origin == BoundTopFrameOrigin());
  785. DCHECK(top_frame_origin_ok)
  786. << "top_frame_origin from renderer='" << top_frame_origin
  787. << "' from browser='" << BoundTopFrameOrigin() << "';";
  788. UMA_HISTOGRAM_BOOLEAN("Net.RestrictedCookieManager.SiteForCookiesOK",
  789. site_for_cookies_ok);
  790. UMA_HISTOGRAM_BOOLEAN("Net.RestrictedCookieManager.TopFrameOriginOK",
  791. top_frame_origin_ok);
  792. // Don't allow setting cookies on other domains. See crbug.com/996786.
  793. if (cookie_being_set && !cookie_being_set->IsDomainMatch(url.host())) {
  794. mojo::ReportBadMessage("Setting cookies on other domains is disallowed.");
  795. return false;
  796. }
  797. if (origin_.IsSameOriginWith(url))
  798. return true;
  799. mojo::ReportBadMessage("Incorrect url origin");
  800. return false;
  801. }
  802. void RestrictedCookieManager::ConvertPartitionedCookiesToUnpartitioned(
  803. const GURL& url) {
  804. DCHECK(base::FeatureList::IsEnabled(net::features::kPartitionedCookies));
  805. if (base::FeatureList::IsEnabled(
  806. net::features::kPartitionedCookiesBypassOriginTrial)) {
  807. return;
  808. }
  809. cookie_store_->ConvertPartitionedCookiesToUnpartitioned(url);
  810. }
  811. } // namespace network