123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927 |
- // Copyright 2017 The Chromium Authors. All rights reserved.
- // Use of this source code is governed by a BSD-style license that can be
- // found in the LICENSE file.
- #include "services/network/restricted_cookie_manager.h"
- #include <memory>
- #include <utility>
- #include <vector>
- #include "base/bind.h"
- #include "base/callback_helpers.h"
- #include "base/compiler_specific.h" // for [[fallthrough]];
- #include "base/memory/raw_ptr.h"
- #include "base/memory/weak_ptr.h"
- #include "base/metrics/histogram_macros.h"
- #include "base/run_loop.h"
- #include "base/stl_util.h"
- #include "base/strings/string_util.h"
- #include "base/task/sequenced_task_runner.h"
- #include "base/threading/sequenced_task_runner_handle.h"
- #include "mojo/public/cpp/bindings/message.h"
- #include "mojo/public/cpp/bindings/remote.h"
- #include "net/base/features.h"
- #include "net/base/isolation_info.h"
- #include "net/base/registry_controlled_domains/registry_controlled_domain.h"
- #include "net/cookies/canonical_cookie.h"
- #include "net/cookies/cookie_access_result.h"
- #include "net/cookies/cookie_constants.h"
- #include "net/cookies/cookie_inclusion_status.h"
- #include "net/cookies/cookie_options.h"
- #include "net/cookies/cookie_partition_key.h"
- #include "net/cookies/cookie_store.h"
- #include "net/cookies/cookie_util.h"
- #include "net/cookies/first_party_set_metadata.h"
- #include "net/cookies/site_for_cookies.h"
- #include "services/network/cookie_settings.h"
- #include "services/network/public/cpp/features.h"
- #include "services/network/public/mojom/cookie_access_observer.mojom.h"
- #include "services/network/public/mojom/cookie_manager.mojom.h"
- #include "services/network/public/mojom/network_context.mojom.h"
- #include "services/network/public/mojom/network_service.mojom.h"
- #include "url/gurl.h"
- namespace network {
- namespace {
- // TODO(cfredric): the `force_ignore_top_frame_party` param being false prevents
- // `document.cookie` access for same-party scripts embedded in an extension
- // frame. It would be better if we allowed that similarly to how we allow
- // SameParty cookies for requests in same-party contexts embedded in top-level
- // extension frames.
- const bool kForceIgnoreTopFrameParty = false;
- net::CookieOptions MakeOptionsForSet(
- mojom::RestrictedCookieManagerRole role,
- const GURL& url,
- const net::SiteForCookies& site_for_cookies,
- const net::IsolationInfo& isolation_info,
- const CookieSettings& cookie_settings,
- const net::FirstPartySetMetadata& first_party_set_metadata) {
- net::CookieOptions options;
- bool force_ignore_site_for_cookies =
- cookie_settings.ShouldIgnoreSameSiteRestrictions(url, site_for_cookies);
- if (role == mojom::RestrictedCookieManagerRole::SCRIPT) {
- options.set_exclude_httponly(); // Default, but make it explicit here.
- options.set_same_site_cookie_context(
- net::cookie_util::ComputeSameSiteContextForScriptSet(
- url, site_for_cookies, force_ignore_site_for_cookies));
- } else {
- // mojom::RestrictedCookieManagerRole::NETWORK
- options.set_include_httponly();
- options.set_same_site_cookie_context(
- net::cookie_util::ComputeSameSiteContextForSubresource(
- url, site_for_cookies, force_ignore_site_for_cookies));
- }
- options.set_same_party_context(first_party_set_metadata.context());
- if (isolation_info.party_context().has_value()) {
- // Count the top-frame site since it's not in the party_context.
- options.set_full_party_context_size(isolation_info.party_context()->size() +
- 1);
- }
- options.set_is_in_nontrivial_first_party_set(
- first_party_set_metadata.frame_entry().has_value());
- return options;
- }
- net::CookieOptions MakeOptionsForGet(
- mojom::RestrictedCookieManagerRole role,
- const GURL& url,
- const net::SiteForCookies& site_for_cookies,
- const net::IsolationInfo& isolation_info,
- const CookieSettings& cookie_settings,
- const net::FirstPartySetMetadata& first_party_set_metadata) {
- // TODO(https://crbug.com/925311): Wire initiator here.
- net::CookieOptions options;
- bool force_ignore_site_for_cookies =
- cookie_settings.ShouldIgnoreSameSiteRestrictions(url, site_for_cookies);
- if (role == mojom::RestrictedCookieManagerRole::SCRIPT) {
- options.set_exclude_httponly(); // Default, but make it explicit here.
- options.set_same_site_cookie_context(
- net::cookie_util::ComputeSameSiteContextForScriptGet(
- url, site_for_cookies, absl::nullopt /*initiator*/,
- force_ignore_site_for_cookies));
- } else {
- // mojom::RestrictedCookieManagerRole::NETWORK
- options.set_include_httponly();
- options.set_same_site_cookie_context(
- net::cookie_util::ComputeSameSiteContextForSubresource(
- url, site_for_cookies, force_ignore_site_for_cookies));
- }
- options.set_same_party_context(first_party_set_metadata.context());
- if (isolation_info.party_context().has_value()) {
- // Count the top-frame site since it's not in the party_context.
- options.set_full_party_context_size(isolation_info.party_context()->size() +
- 1);
- }
- options.set_is_in_nontrivial_first_party_set(
- first_party_set_metadata.frame_entry().has_value());
- return options;
- }
- } // namespace
- // static
- void RestrictedCookieManager::ComputeFirstPartySetMetadata(
- const url::Origin& origin,
- const net::CookieStore* cookie_store,
- const net::IsolationInfo& isolation_info,
- base::OnceCallback<void(net::FirstPartySetMetadata)> callback) {
- std::pair<base::OnceCallback<void(net::FirstPartySetMetadata)>,
- base::OnceCallback<void(net::FirstPartySetMetadata)>>
- callbacks = base::SplitOnceCallback(std::move(callback));
- absl::optional<net::FirstPartySetMetadata> metadata =
- net::cookie_util::ComputeFirstPartySetMetadataMaybeAsync(
- /*request_site=*/net::SchemefulSite(origin), isolation_info,
- cookie_store->cookie_access_delegate(), kForceIgnoreTopFrameParty,
- std::move(callbacks.first));
- if (metadata.has_value())
- std::move(callbacks.second).Run(std::move(metadata.value()));
- }
- bool CookieWithAccessResultComparer::operator()(
- const net::CookieWithAccessResult& cookie_with_access_result1,
- const net::CookieWithAccessResult& cookie_with_access_result2) const {
- // Compare just the cookie portion of the CookieWithAccessResults so a cookie
- // only ever has one entry in the map. For a given cookie we want to send a
- // new access notification whenever its access results change. If we keyed off
- // of both the cookie and its current access result, if a cookie shifted from
- // "allowed" to "blocked" the cookie would wind up with two entries in the
- // map. If the cookie then shifted back to "allowed" we wouldn't send a new
- // notification because cookie/allowed already existed in the map. In the case
- // of a cookie shifting from "allowed" to "blocked,"
- // SkipAccessNotificationForCookieItem() checks the access result. If the
- // cookie exists in the map but its status is "allowed" we evict the old
- // entry.
- return cookie_with_access_result1.cookie < cookie_with_access_result2.cookie;
- }
- CookieAccesses* RestrictedCookieManager::GetCookieAccessesForURLAndSite(
- const GURL& url,
- const net::SiteForCookies& site_for_cookies) {
- std::unique_ptr<CookieAccesses>& entry =
- recent_cookie_accesses_[std::make_pair(url, site_for_cookies)];
- if (!entry) {
- entry = std::make_unique<CookieAccesses>();
- }
- return entry.get();
- }
- bool RestrictedCookieManager::SkipAccessNotificationForCookieItem(
- CookieAccesses* cookie_accesses,
- const net::CookieWithAccessResult& cookie_item) {
- DCHECK(cookie_accesses);
- // Have we sent information about this cookie to the |cookie_observer_|
- // before?
- std::set<net::CookieWithAccessResult>::iterator existing_slot =
- cookie_accesses->find(cookie_item);
- // If this is the first time seeing this cookie make a note and don't skip
- // the notification.
- if (existing_slot == cookie_accesses->end()) {
- // Don't store more than a max number of cookies, in the interest of
- // limiting memory consumption.
- const int kMaxCookieCount = 32;
- if (cookie_accesses->size() == kMaxCookieCount) {
- cookie_accesses->clear();
- }
- cookie_accesses->insert(cookie_item);
- return false;
- }
- // If the cookie and its access result are unchanged since we last updated
- // the |cookie_observer_|, skip notifying the |cookie_observer_| again.
- if (existing_slot->cookie.HasEquivalentDataMembers(cookie_item.cookie) &&
- existing_slot->access_result == cookie_item.access_result) {
- return true;
- }
- // The cookie's access result has changed - update it in our record of what
- // we've sent to the |cookie_observer_|. It's safe to update the existing
- // entry in the set because the access_result field does not determine the
- // CookieWithAccessResult's location in the set.
- const_cast<net::CookieWithAccessResult&>(*existing_slot).access_result =
- cookie_item.access_result;
- // Don't skip notifying the |cookie_observer_| of the change.
- return false;
- }
- class RestrictedCookieManager::Listener : public base::LinkNode<Listener> {
- public:
- Listener(net::CookieStore* cookie_store,
- const RestrictedCookieManager* restricted_cookie_manager,
- const GURL& url,
- const net::SiteForCookies& site_for_cookies,
- const url::Origin& top_frame_origin,
- const absl::optional<net::CookiePartitionKey>& cookie_partition_key,
- net::CookieOptions options,
- mojo::PendingRemote<mojom::CookieChangeListener> mojo_listener,
- const bool first_party_sets_enabled)
- : cookie_store_(cookie_store),
- restricted_cookie_manager_(restricted_cookie_manager),
- url_(url),
- site_for_cookies_(site_for_cookies),
- top_frame_origin_(top_frame_origin),
- options_(options),
- mojo_listener_(std::move(mojo_listener)),
- first_party_sets_enabled_(first_party_sets_enabled) {
- // TODO(pwnall): add a constructor w/options to net::CookieChangeDispatcher.
- cookie_store_subscription_ =
- cookie_store->GetChangeDispatcher().AddCallbackForUrl(
- url, cookie_partition_key,
- base::BindRepeating(
- &Listener::OnCookieChange,
- // Safe because net::CookieChangeDispatcher guarantees that
- // the callback will stop being called immediately after we
- // remove the subscription, and the cookie store lives on
- // the same thread as we do.
- base::Unretained(this)));
- }
- Listener(const Listener&) = delete;
- Listener& operator=(const Listener&) = delete;
- ~Listener() { DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); }
- mojo::Remote<mojom::CookieChangeListener>& mojo_listener() {
- DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
- return mojo_listener_;
- }
- private:
- // net::CookieChangeDispatcher callback.
- void OnCookieChange(const net::CookieChangeInfo& change) {
- DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
- bool delegate_treats_url_as_trustworthy =
- cookie_store_->cookie_access_delegate() &&
- cookie_store_->cookie_access_delegate()->ShouldTreatUrlAsTrustworthy(
- url_);
- // CookieChangeDispatcher doesn't check for inclusion against `options_`, so
- // we need to double-check that.
- net::CookieSamePartyStatus same_party_status =
- net::cookie_util::GetSamePartyStatus(change.cookie, options_,
- first_party_sets_enabled_);
- if (!change.cookie
- .IncludeForRequestURL(
- url_, options_,
- net::CookieAccessParams{change.access_result.access_semantics,
- delegate_treats_url_as_trustworthy,
- same_party_status})
- .status.IsInclude()) {
- return;
- }
- // When a user blocks a site's access to cookies, the existing cookies are
- // not deleted. This check prevents the site from observing their cookies
- // being deleted at a later time, which can happen due to eviction or due to
- // the user explicitly deleting all cookies.
- if (!restricted_cookie_manager_->cookie_settings().IsCookieAccessible(
- change.cookie, url_, site_for_cookies_, top_frame_origin_)) {
- return;
- }
- mojo_listener_->OnCookieChange(change);
- }
- // Expected to outlive |restricted_cookie_manager_| which outlives this.
- raw_ptr<const net::CookieStore> cookie_store_;
- // The CookieChangeDispatcher subscription used by this listener.
- std::unique_ptr<net::CookieChangeSubscription> cookie_store_subscription_;
- // Raw pointer usage is safe because RestrictedCookieManager owns this
- // instance and is guaranteed to outlive it.
- const raw_ptr<const RestrictedCookieManager> restricted_cookie_manager_;
- // The URL whose cookies this listener is interested in.
- const GURL url_;
- // Site context in which we're used; used to determine if a cookie is accessed
- // in a third-party context.
- const net::SiteForCookies site_for_cookies_;
- // Site context in which we're used; used to check content settings.
- const url::Origin top_frame_origin_;
- // CanonicalCookie::IncludeForRequestURL options for this listener's interest.
- const net::CookieOptions options_;
- mojo::Remote<mojom::CookieChangeListener> mojo_listener_;
- const bool first_party_sets_enabled_;
- SEQUENCE_CHECKER(sequence_checker_);
- };
- RestrictedCookieManager::RestrictedCookieManager(
- const mojom::RestrictedCookieManagerRole role,
- net::CookieStore* cookie_store,
- const CookieSettings& cookie_settings,
- const url::Origin& origin,
- const net::IsolationInfo& isolation_info,
- mojo::PendingRemote<mojom::CookieAccessObserver> cookie_observer,
- const bool first_party_sets_enabled,
- net::FirstPartySetMetadata first_party_set_metadata)
- : role_(role),
- cookie_store_(cookie_store),
- cookie_settings_(cookie_settings),
- origin_(origin),
- isolation_info_(isolation_info),
- cookie_observer_(std::move(cookie_observer)),
- first_party_set_metadata_(std::move(first_party_set_metadata)),
- cookie_partition_key_(net::CookiePartitionKey::FromNetworkIsolationKey(
- isolation_info.network_isolation_key(),
- base::OptionalOrNullptr(
- first_party_set_metadata_.top_frame_entry().has_value()
- ? absl::make_optional(
- first_party_set_metadata_.top_frame_entry()->primary())
- : absl::nullopt))),
- cookie_partition_key_collection_(
- net::CookiePartitionKeyCollection::FromOptional(
- cookie_partition_key_)),
- first_party_sets_enabled_(first_party_sets_enabled) {
- DCHECK(cookie_store);
- }
- RestrictedCookieManager::~RestrictedCookieManager() {
- DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
- base::LinkNode<Listener>* node = listeners_.head();
- while (node != listeners_.end()) {
- Listener* listener_reference = node->value();
- node = node->next();
- // The entire list is going away, no need to remove nodes from it.
- delete listener_reference;
- }
- }
- void RestrictedCookieManager::OverrideIsolationInfoForTesting(
- const net::IsolationInfo& new_isolation_info) {
- base::RunLoop run_loop;
- isolation_info_ = new_isolation_info;
- ComputeFirstPartySetMetadata(
- origin_, cookie_store_, isolation_info_,
- base::BindOnce(
- &RestrictedCookieManager::OnGotFirstPartySetMetadataForTesting,
- weak_ptr_factory_.GetWeakPtr(), run_loop.QuitClosure()));
- run_loop.Run();
- }
- void RestrictedCookieManager::OnGotFirstPartySetMetadataForTesting(
- base::OnceClosure done_closure,
- net::FirstPartySetMetadata first_party_set_metadata) {
- first_party_set_metadata_ = std::move(first_party_set_metadata);
- cookie_partition_key_ = net::CookiePartitionKey::FromNetworkIsolationKey(
- isolation_info_.network_isolation_key(),
- base::OptionalOrNullptr(
- first_party_set_metadata_.top_frame_entry().has_value()
- ? absl::make_optional(
- first_party_set_metadata_.top_frame_entry()->primary())
- : absl::nullopt));
- cookie_partition_key_collection_ =
- net::CookiePartitionKeyCollection::FromOptional(cookie_partition_key_);
- std::move(done_closure).Run();
- }
- bool RestrictedCookieManager::IsPartitionedCookiesEnabled() const {
- return cookie_partition_key_.has_value();
- }
- namespace {
- bool PartitionedCookiesAllowed(
- bool partitioned_cookies_runtime_feature_enabled,
- const absl::optional<net::CookiePartitionKey>& cookie_partition_key) {
- if (base::FeatureList::IsEnabled(
- net::features::kPartitionedCookiesBypassOriginTrial) ||
- partitioned_cookies_runtime_feature_enabled)
- return true;
- // We allow partition keys which have a nonce since the Origin Trial is
- // only meant to test cookies set with the Partitioned attribute.
- return cookie_partition_key && cookie_partition_key->nonce();
- }
- } // namespace
- void RestrictedCookieManager::GetAllForUrl(
- const GURL& url,
- const net::SiteForCookies& site_for_cookies,
- const url::Origin& top_frame_origin,
- mojom::CookieManagerGetOptionsPtr options,
- bool partitioned_cookies_runtime_feature_enabled,
- GetAllForUrlCallback callback) {
- DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
- if (!ValidateAccessToCookiesAt(url, site_for_cookies, top_frame_origin)) {
- std::move(callback).Run({});
- return;
- }
- // TODO(morlovich): Try to validate site_for_cookies as well.
- net::CookieOptions net_options =
- MakeOptionsForGet(role_, url, site_for_cookies, isolation_info_,
- cookie_settings(), first_party_set_metadata_);
- // TODO(https://crbug.com/977040): remove set_return_excluded_cookies() once
- // removing deprecation warnings.
- net_options.set_return_excluded_cookies();
- cookie_store_->GetCookieListWithOptionsAsync(
- url, net_options,
- PartitionedCookiesAllowed(partitioned_cookies_runtime_feature_enabled,
- cookie_partition_key_)
- ? cookie_partition_key_collection_
- : net::CookiePartitionKeyCollection(),
- base::BindOnce(&RestrictedCookieManager::CookieListToGetAllForUrlCallback,
- weak_ptr_factory_.GetWeakPtr(), url, site_for_cookies,
- top_frame_origin, net_options, std::move(options),
- std::move(callback)));
- }
- void RestrictedCookieManager::CookieListToGetAllForUrlCallback(
- const GURL& url,
- const net::SiteForCookies& site_for_cookies,
- const url::Origin& top_frame_origin,
- const net::CookieOptions& net_options,
- mojom::CookieManagerGetOptionsPtr options,
- GetAllForUrlCallback callback,
- const net::CookieAccessResultList& cookie_list,
- const net::CookieAccessResultList& excluded_list) {
- DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
- net::CookieAccessResultList maybe_included_cookies = cookie_list;
- net::CookieAccessResultList excluded_cookies = excluded_list;
- cookie_settings().AnnotateAndMoveUserBlockedCookies(
- url, site_for_cookies, &top_frame_origin, maybe_included_cookies,
- excluded_cookies);
- std::vector<net::CookieWithAccessResult> result;
- std::vector<mojom::CookieOrLineWithAccessResultPtr>
- on_cookies_accessed_result;
- CookieAccesses* cookie_accesses =
- GetCookieAccessesForURLAndSite(url, site_for_cookies);
- auto add_excluded = [&]() {
- // TODO(https://crbug.com/977040): Stop reporting accesses of cookies with
- // warning reasons once samesite tightening up is rolled out.
- for (const auto& cookie_and_access_result : excluded_cookies) {
- if (!cookie_and_access_result.access_result.status.ShouldWarn() &&
- !cookie_and_access_result.access_result.status.HasOnlyExclusionReason(
- net::CookieInclusionStatus::EXCLUDE_USER_PREFERENCES)) {
- continue;
- }
- // Skip sending a notification about this cookie access?
- if (SkipAccessNotificationForCookieItem(cookie_accesses,
- cookie_and_access_result)) {
- continue;
- }
- on_cookies_accessed_result.push_back(
- mojom::CookieOrLineWithAccessResult::New(
- mojom::CookieOrLine::NewCookie(cookie_and_access_result.cookie),
- cookie_and_access_result.access_result));
- }
- };
- if (!base::FeatureList::IsEnabled(features::kFasterSetCookie))
- add_excluded();
- if (!maybe_included_cookies.empty())
- result.reserve(maybe_included_cookies.size());
- mojom::CookieMatchType match_type = options->match_type;
- const std::string& match_name = options->name;
- for (const net::CookieWithAccessResult& cookie_item :
- maybe_included_cookies) {
- const net::CanonicalCookie& cookie = cookie_item.cookie;
- net::CookieAccessResult access_result = cookie_item.access_result;
- const std::string& cookie_name = cookie.Name();
- if (match_type == mojom::CookieMatchType::EQUALS) {
- if (cookie_name != match_name)
- continue;
- } else if (match_type == mojom::CookieMatchType::STARTS_WITH) {
- if (!base::StartsWith(cookie_name, match_name,
- base::CompareCase::SENSITIVE)) {
- continue;
- }
- } else {
- NOTREACHED();
- }
- if (access_result.status.IsInclude()) {
- result.push_back(cookie_item);
- }
- if (!base::FeatureList::IsEnabled(features::kFasterSetCookie)) {
- // Skip sending a notification about this cookie access?
- if (SkipAccessNotificationForCookieItem(cookie_accesses, cookie_item)) {
- continue;
- }
- on_cookies_accessed_result.push_back(
- mojom::CookieOrLineWithAccessResult::New(
- mojom::CookieOrLine::NewCookie(cookie), access_result));
- }
- }
- auto notify_observer = [&]() {
- if (cookie_observer_ && !on_cookies_accessed_result.empty()) {
- cookie_observer_->OnCookiesAccessed(mojom::CookieAccessDetails::New(
- mojom::CookieAccessDetails::Type::kRead, url, site_for_cookies,
- std::move(on_cookies_accessed_result), absl::nullopt));
- }
- };
- if (!base::FeatureList::IsEnabled(features::kFasterSetCookie))
- notify_observer();
- if (!maybe_included_cookies.empty() && IsPartitionedCookiesEnabled()) {
- UMA_HISTOGRAM_COUNTS_100(
- "Net.RestrictedCookieManager.PartitionedCookiesInScript",
- base::ranges::count_if(result,
- [](const net::CookieWithAccessResult& c) {
- return c.cookie.IsPartitioned();
- }));
- }
- std::move(callback).Run(
- base::FeatureList::IsEnabled(features::kFasterSetCookie)
- ? result
- : std::move(result));
- if (base::FeatureList::IsEnabled(features::kFasterSetCookie)) {
- add_excluded();
- for (auto& cookie : result) {
- // Skip sending a notification about this cookie access?
- if (SkipAccessNotificationForCookieItem(cookie_accesses, cookie)) {
- continue;
- }
- on_cookies_accessed_result.push_back(
- mojom::CookieOrLineWithAccessResult::New(
- mojom::CookieOrLine::NewCookie(cookie.cookie),
- cookie.access_result));
- }
- notify_observer();
- }
- }
- void RestrictedCookieManager::SetCanonicalCookie(
- const net::CanonicalCookie& cookie,
- const GURL& url,
- const net::SiteForCookies& site_for_cookies,
- const url::Origin& top_frame_origin,
- net::CookieInclusionStatus status,
- SetCanonicalCookieCallback callback) {
- DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
- // Don't allow a status that has an exclusion reason as they should have
- // already been taken care of on the renderer side.
- if (!status.IsInclude()) {
- mojo::ReportBadMessage(
- "RestrictedCookieManager: unexpected cookie inclusion status");
- std::move(callback).Run(false);
- return;
- }
- if (!ValidateAccessToCookiesAt(url, site_for_cookies, top_frame_origin,
- &cookie)) {
- std::move(callback).Run(false);
- return;
- }
- // TODO(morlovich): Try to validate site_for_cookies as well.
- bool blocked = !cookie_settings_.IsCookieAccessible(
- cookie, url, site_for_cookies, top_frame_origin);
- if (blocked)
- status.AddExclusionReason(
- net::CookieInclusionStatus::EXCLUDE_USER_PREFERENCES);
- // Don't allow URLs with leading dots like https://.some-weird-domain.com
- // This probably never happens.
- if (!net::cookie_util::DomainIsHostOnly(url.host()))
- status.AddExclusionReason(
- net::CookieInclusionStatus::EXCLUDE_INVALID_DOMAIN);
- if (!status.IsInclude()) {
- if (cookie_observer_) {
- std::vector<network::mojom::CookieOrLineWithAccessResultPtr>
- result_with_access_result;
- result_with_access_result.push_back(
- mojom::CookieOrLineWithAccessResult::New(
- mojom::CookieOrLine::NewCookie(cookie),
- net::CookieAccessResult(status)));
- cookie_observer_->OnCookiesAccessed(mojom::CookieAccessDetails::New(
- mojom::CookieAccessDetails::Type::kChange, url, site_for_cookies,
- std::move(result_with_access_result), absl::nullopt));
- }
- std::move(callback).Run(false);
- return;
- }
- // TODO(pwnall): Validate the CanonicalCookie fields.
- // Update the creation and last access times.
- // Note: This used to be a call to NowFromSystemTime, but this caused
- // inconsistency with the expiration date, which was capped checking
- // against Now. If any issues crop up related to this change please
- // contact the owners of http://crbug.com/1335859.
- base::Time now = base::Time::Now();
- // TODO(http://crbug.com/1024053): Log metrics
- const GURL& origin_url = origin_.GetURL();
- net::CookieSourceScheme source_scheme =
- GURL::SchemeIsCryptographic(origin_.scheme())
- ? net::CookieSourceScheme::kSecure
- : net::CookieSourceScheme::kNonSecure;
- // If the renderer's cookie has a partition key that was not created using
- // CookiePartitionKey::FromScript, then the cookie's partition key should be
- // equal to RestrictedCookieManager's partition key.
- absl::optional<net::CookiePartitionKey> cookie_partition_key =
- cookie.PartitionKey();
- // If the `cookie_partition_key_` has a nonce then force all cookie writes to
- // be in the nonce based partition even if the cookie was not set with the
- // Partitioned attribute.
- if (net::CookiePartitionKey::HasNonce(cookie_partition_key_)) {
- cookie_partition_key = cookie_partition_key_;
- }
- if (cookie_partition_key) {
- // RestrictedCookieManager having a null partition key strictly implies the
- // feature is disabled. If that is the case, we treat the cookie as
- // unpartitioned.
- if (!cookie_partition_key_) {
- cookie_partition_key = absl::nullopt;
- } else {
- bool cookie_partition_key_ok =
- cookie_partition_key->from_script() ||
- cookie_partition_key.value() == cookie_partition_key_.value();
- UMA_HISTOGRAM_BOOLEAN("Net.RestrictedCookieManager.CookiePartitionKeyOK",
- cookie_partition_key_ok);
- if (!cookie_partition_key_ok) {
- mojo::ReportBadMessage(
- "RestrictedCookieManager: unexpected cookie partition key");
- std::move(callback).Run(false);
- return;
- }
- if (cookie_partition_key->from_script()) {
- cookie_partition_key = cookie_partition_key_;
- }
- }
- }
- if (IsPartitionedCookiesEnabled()) {
- UMA_HISTOGRAM_BOOLEAN("Net.RestrictedCookieManager.SetPartitionedCookie",
- cookie_partition_key.has_value());
- }
- std::unique_ptr<net::CanonicalCookie> sanitized_cookie =
- net::CanonicalCookie::FromStorage(
- cookie.Name(), cookie.Value(), cookie.Domain(), cookie.Path(), now,
- cookie.ExpiryDate(), now, now, cookie.IsSecure(), cookie.IsHttpOnly(),
- cookie.SameSite(), cookie.Priority(), cookie.IsSameParty(),
- cookie_partition_key, source_scheme, origin_.port());
- DCHECK(sanitized_cookie);
- // FromStorage() uses a less strict version of IsCanonical(), we need to check
- // the stricter version as well here.
- if (!sanitized_cookie->IsCanonical()) {
- std::move(callback).Run(false);
- return;
- }
- net::CanonicalCookie cookie_copy = *sanitized_cookie;
- net::CookieOptions options =
- MakeOptionsForSet(role_, url, site_for_cookies, isolation_info_,
- cookie_settings(), first_party_set_metadata_);
- net::CookieAccessResult cookie_access_result(status);
- cookie_store_->SetCanonicalCookieAsync(
- std::move(sanitized_cookie), origin_url, options,
- base::BindOnce(&RestrictedCookieManager::SetCanonicalCookieResult,
- weak_ptr_factory_.GetWeakPtr(), url, site_for_cookies,
- cookie_copy, options, std::move(callback)),
- cookie_access_result);
- }
- void RestrictedCookieManager::SetCanonicalCookieResult(
- const GURL& url,
- const net::SiteForCookies& site_for_cookies,
- const net::CanonicalCookie& cookie,
- const net::CookieOptions& net_options,
- SetCanonicalCookieCallback user_callback,
- net::CookieAccessResult access_result) {
- // TODO(https://crbug.com/977040): Only report pure INCLUDE once samesite
- // tightening up is rolled out.
- DCHECK(!access_result.status.HasExclusionReason(
- net::CookieInclusionStatus::EXCLUDE_USER_PREFERENCES));
- if (access_result.status.IsInclude() || access_result.status.ShouldWarn()) {
- if (cookie_observer_) {
- std::vector<mojom::CookieOrLineWithAccessResultPtr> notify;
- notify.push_back(mojom::CookieOrLineWithAccessResult::New(
- mojom::CookieOrLine::NewCookie(cookie), access_result));
- cookie_observer_->OnCookiesAccessed(mojom::CookieAccessDetails::New(
- mojom::CookieAccessDetails::Type::kChange, url, site_for_cookies,
- std::move(notify), absl::nullopt));
- }
- }
- std::move(user_callback).Run(access_result.status.IsInclude());
- }
- void RestrictedCookieManager::AddChangeListener(
- const GURL& url,
- const net::SiteForCookies& site_for_cookies,
- const url::Origin& top_frame_origin,
- mojo::PendingRemote<mojom::CookieChangeListener> mojo_listener,
- AddChangeListenerCallback callback) {
- DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
- if (!ValidateAccessToCookiesAt(url, site_for_cookies, top_frame_origin)) {
- std::move(callback).Run();
- return;
- }
- net::CookieOptions net_options =
- MakeOptionsForGet(role_, url, site_for_cookies, isolation_info_,
- cookie_settings(), first_party_set_metadata_);
- auto listener = std::make_unique<Listener>(
- cookie_store_, this, url, site_for_cookies, top_frame_origin,
- cookie_partition_key_, net_options, std::move(mojo_listener),
- first_party_sets_enabled_);
- listener->mojo_listener().set_disconnect_handler(
- base::BindOnce(&RestrictedCookieManager::RemoveChangeListener,
- weak_ptr_factory_.GetWeakPtr(),
- // Safe because this owns the listener, so the listener is
- // guaranteed to be alive for as long as the weak pointer
- // above resolves.
- base::Unretained(listener.get())));
- // The linked list takes over the Listener ownership.
- listeners_.Append(listener.release());
- std::move(callback).Run();
- }
- void RestrictedCookieManager::SetCookieFromString(
- const GURL& url,
- const net::SiteForCookies& site_for_cookies,
- const url::Origin& top_frame_origin,
- const std::string& cookie,
- bool partitioned_cookies_runtime_feature_enabled,
- SetCookieFromStringCallback callback) {
- DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
- bool site_for_cookies_ok =
- BoundSiteForCookies().IsEquivalent(site_for_cookies);
- bool top_frame_origin_ok = top_frame_origin == BoundTopFrameOrigin();
- if (base::FeatureList::IsEnabled(features::kFasterSetCookie)) {
- std::move(callback).Run(site_for_cookies_ok, top_frame_origin_ok);
- callback = base::DoNothing();
- }
- net::CookieInclusionStatus status;
- std::unique_ptr<net::CanonicalCookie> parsed_cookie =
- net::CanonicalCookie::Create(
- url, cookie, base::Time::Now(), absl::nullopt /* server_time */,
- PartitionedCookiesAllowed(partitioned_cookies_runtime_feature_enabled,
- cookie_partition_key_)
- ? cookie_partition_key_
- : absl::nullopt,
- &status);
- if (!parsed_cookie) {
- if (cookie_observer_) {
- std::vector<network::mojom::CookieOrLineWithAccessResultPtr>
- result_with_access_result;
- result_with_access_result.push_back(
- mojom::CookieOrLineWithAccessResult::New(
- mojom::CookieOrLine::NewCookieString(cookie),
- net::CookieAccessResult(status)));
- cookie_observer_->OnCookiesAccessed(mojom::CookieAccessDetails::New(
- mojom::CookieAccessDetails::Type::kChange, url, site_for_cookies,
- std::move(result_with_access_result), absl::nullopt));
- }
- std::move(callback).Run(site_for_cookies_ok, top_frame_origin_ok);
- return;
- }
- // Further checks (origin_, settings), as well as logging done by
- // SetCanonicalCookie()
- SetCanonicalCookie(
- *parsed_cookie, url, site_for_cookies, top_frame_origin, status,
- base::BindOnce([](base::OnceClosure closure,
- bool success) { std::move(closure).Run(); },
- // Although these values are being called outside
- // ValidateAccessToCookiesAt, the checks done in that
- // method are called shortly after synchronously.
- base::BindOnce(std::move(callback), site_for_cookies_ok,
- top_frame_origin_ok)));
- }
- void RestrictedCookieManager::GetCookiesString(
- const GURL& url,
- const net::SiteForCookies& site_for_cookies,
- const url::Origin& top_frame_origin,
- bool partitioned_cookies_runtime_feature_enabled,
- GetCookiesStringCallback callback) {
- DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
- // Checks done by GetAllForUrl.
- // Match everything.
- auto match_options = mojom::CookieManagerGetOptions::New();
- match_options->name = "";
- match_options->match_type = mojom::CookieMatchType::STARTS_WITH;
- GetAllForUrl(url, site_for_cookies, top_frame_origin,
- std::move(match_options),
- partitioned_cookies_runtime_feature_enabled,
- base::BindOnce([](const std::vector<net::CookieWithAccessResult>&
- cookies) {
- return net::CanonicalCookie::BuildCookieLine(cookies);
- }).Then(std::move(callback)));
- }
- void RestrictedCookieManager::CookiesEnabledFor(
- const GURL& url,
- const net::SiteForCookies& site_for_cookies,
- const url::Origin& top_frame_origin,
- CookiesEnabledForCallback callback) {
- if (!ValidateAccessToCookiesAt(url, site_for_cookies, top_frame_origin)) {
- std::move(callback).Run(false);
- return;
- }
- std::move(callback).Run(cookie_settings_.IsFullCookieAccessAllowed(
- url, site_for_cookies, top_frame_origin,
- CookieSettings::QueryReason::kCookies));
- }
- void RestrictedCookieManager::RemoveChangeListener(Listener* listener) {
- DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
- listener->RemoveFromList();
- delete listener;
- }
- bool RestrictedCookieManager::ValidateAccessToCookiesAt(
- const GURL& url,
- const net::SiteForCookies& site_for_cookies,
- const url::Origin& top_frame_origin,
- const net::CanonicalCookie* cookie_being_set) {
- if (origin_.opaque()) {
- mojo::ReportBadMessage("Access is denied in this context");
- return false;
- }
- bool site_for_cookies_ok =
- BoundSiteForCookies().IsEquivalent(site_for_cookies);
- DCHECK(site_for_cookies_ok)
- << "site_for_cookies from renderer='" << site_for_cookies.ToDebugString()
- << "' from browser='" << BoundSiteForCookies().ToDebugString() << "';";
- bool top_frame_origin_ok = (top_frame_origin == BoundTopFrameOrigin());
- DCHECK(top_frame_origin_ok)
- << "top_frame_origin from renderer='" << top_frame_origin
- << "' from browser='" << BoundTopFrameOrigin() << "';";
- UMA_HISTOGRAM_BOOLEAN("Net.RestrictedCookieManager.SiteForCookiesOK",
- site_for_cookies_ok);
- UMA_HISTOGRAM_BOOLEAN("Net.RestrictedCookieManager.TopFrameOriginOK",
- top_frame_origin_ok);
- // Don't allow setting cookies on other domains. See crbug.com/996786.
- if (cookie_being_set && !cookie_being_set->IsDomainMatch(url.host())) {
- mojo::ReportBadMessage("Setting cookies on other domains is disallowed.");
- return false;
- }
- if (origin_.IsSameOriginWith(url))
- return true;
- mojo::ReportBadMessage("Incorrect url origin");
- return false;
- }
- void RestrictedCookieManager::ConvertPartitionedCookiesToUnpartitioned(
- const GURL& url) {
- DCHECK(base::FeatureList::IsEnabled(net::features::kPartitionedCookies));
- if (base::FeatureList::IsEnabled(
- net::features::kPartitionedCookiesBypassOriginTrial)) {
- return;
- }
- cookie_store_->ConvertPartitionedCookiesToUnpartitioned(url);
- }
- } // namespace network
|