123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410 |
- // Copyright 2018 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/cookie_settings.h"
- #include <functional>
- #include <iterator>
- #include "base/bind.h"
- #include "base/callback.h"
- #include "base/containers/contains.h"
- #include "base/metrics/histogram_macros.h"
- #include "base/ranges/algorithm.h"
- #include "base/stl_util.h"
- #include "components/content_settings/core/common/content_settings.h"
- #include "components/content_settings/core/common/cookie_settings_base.h"
- #include "net/base/features.h"
- #include "net/base/net_errors.h"
- #include "net/base/network_delegate.h"
- #include "net/cookies/canonical_cookie.h"
- #include "net/cookies/cookie_inclusion_status.h"
- #include "net/cookies/cookie_util.h"
- #include "net/cookies/static_cookie_policy.h"
- #include "third_party/abseil-cpp/absl/types/optional.h"
- #include "url/gurl.h"
- namespace network {
- namespace {
- using SamePartyCookieContextType = net::SamePartyContext::Type;
- bool IsExplicitSetting(const ContentSettingPatternSource& setting) {
- return !setting.primary_pattern.MatchesAllHosts() ||
- !setting.secondary_pattern.MatchesAllHosts();
- }
- const ContentSettingPatternSource* FindMatchingSetting(
- const GURL& primary_url,
- const GURL& secondary_url,
- const ContentSettingsForOneType& settings) {
- // We assume `settings` is sorted in order of precedence, so we use the first
- // matching rule we find.
- const auto& entry = base::ranges::find_if(
- settings, [&](const ContentSettingPatternSource& entry) {
- // The primary pattern is for the request URL; the secondary pattern
- // is for the first-party URL (which is the top-frame origin [if
- // available] or the site-for-cookies).
- return !entry.IsExpired() &&
- entry.primary_pattern.Matches(primary_url) &&
- entry.secondary_pattern.Matches(secondary_url);
- });
- return entry == settings.end() ? nullptr : &*entry;
- }
- } // namespace
- CookieSettings::CookieSettings() = default;
- CookieSettings::~CookieSettings() = default;
- DeleteCookiePredicate CookieSettings::CreateDeleteCookieOnExitPredicate()
- const {
- if (!HasSessionOnlyOrigins())
- return DeleteCookiePredicate();
- return base::BindRepeating(&CookieSettings::ShouldDeleteCookieOnExit,
- base::Unretained(this),
- std::cref(content_settings_));
- }
- ContentSetting CookieSettings::GetSettingForLegacyCookieAccess(
- const std::string& cookie_domain) const {
- // Default to match what was registered in the ContentSettingsRegistry.
- ContentSetting setting = CONTENT_SETTING_BLOCK;
- if (settings_for_legacy_cookie_access_.empty())
- return setting;
- // If there are no domain-specific settings, return early to avoid the cost of
- // constructing a GURL to match against.
- if (base::ranges::all_of(settings_for_legacy_cookie_access_,
- [](const ContentSettingPatternSource& entry) {
- return entry.primary_pattern.MatchesAllHosts();
- })) {
- // Take the first entry because we know all entries match any host.
- setting = settings_for_legacy_cookie_access_[0].GetContentSetting();
- DCHECK(IsValidSettingForLegacyAccess(setting));
- return setting;
- }
- // The content setting patterns are treated as domains, not URLs, so the
- // scheme is irrelevant (so we can just arbitrarily pass false).
- GURL cookie_domain_url = net::cookie_util::CookieOriginToURL(
- cookie_domain, false /* secure scheme */);
- for (const auto& entry : settings_for_legacy_cookie_access_) {
- // TODO(crbug.com/1015611): This should ignore scheme and port, but
- // currently takes them into account. It says in the policy description that
- // specifying a scheme or port in the pattern may lead to undefined
- // behavior, but this is not ideal.
- if (entry.primary_pattern.Matches(cookie_domain_url)) {
- DCHECK(IsValidSettingForLegacyAccess(entry.GetContentSetting()));
- return entry.GetContentSetting();
- }
- }
- return setting;
- }
- bool CookieSettings::ShouldIgnoreSameSiteRestrictions(
- const GURL& url,
- const net::SiteForCookies& site_for_cookies) const {
- return base::Contains(secure_origin_cookies_allowed_schemes_,
- site_for_cookies.scheme()) &&
- url.SchemeIsCryptographic();
- }
- bool CookieSettings::IsCookieAccessible(
- const net::CanonicalCookie& cookie,
- const GURL& url,
- const net::SiteForCookies& site_for_cookies,
- const absl::optional<url::Origin>& top_frame_origin) const {
- return IsHypotheticalCookieAllowed(
- GetCookieSettingWithMetadata(
- url,
- GetFirstPartyURL(site_for_cookies,
- base::OptionalOrNullptr(top_frame_origin)),
- IsThirdPartyRequest(url, site_for_cookies), QueryReason::kCookies),
- cookie.IsSameParty(), cookie.IsPartitioned(), /*record_metrics=*/true);
- }
- bool CookieSettings::ShouldAlwaysAllowCookies(
- const GURL& url,
- const GURL& first_party_url) const {
- return (base::Contains(secure_origin_cookies_allowed_schemes_,
- first_party_url.scheme()) &&
- url.SchemeIsCryptographic()) ||
- (base::Contains(matching_scheme_cookies_allowed_schemes_,
- url.scheme()) &&
- url.SchemeIs(first_party_url.scheme_piece()));
- }
- net::NetworkDelegate::PrivacySetting CookieSettings::IsPrivacyModeEnabled(
- const GURL& url,
- const net::SiteForCookies& site_for_cookies,
- const absl::optional<url::Origin>& top_frame_origin,
- SamePartyCookieContextType same_party_cookie_context_type) const {
- // PrivacySetting should be kStateDisallowed iff no cookies should ever
- // be sent on this request. E.g.:
- //
- // * if cookie settings block cookies on this site or for this URL; or
- //
- // * if cookie settings block 3P cookies, the context is cross-party, and
- // content settings blocks the 1P from using cookies; or
- //
- // * if cookie settings block 3P cookies, and the context is same-party, but
- // SameParty cookies aren't considered 1P.
- //
- // PrivacySetting should be kPartitionedStateAllowedOnly iff the request is
- // cross-party, cookie settings block 3P cookies, and content settings allows
- // the 1P to use cookies.
- //
- // Otherwise, the PrivacySetting should be kStateAllowed.
- //
- // We don't record metrics here, since this isn't actually accessing a cookie.
- CookieSettingWithMetadata metadata = GetCookieSettingWithMetadata(
- url, site_for_cookies, base::OptionalOrNullptr(top_frame_origin),
- QueryReason::kCookies);
- if (IsHypotheticalCookieAllowed(metadata,
- same_party_cookie_context_type ==
- SamePartyCookieContextType::kSameParty,
- /*is_partitioned*/ false,
- /*record_metrics=*/false)) {
- return net::NetworkDelegate::PrivacySetting::kStateAllowed;
- }
- // No unpartitioned cookie should be sent on this request. The only other
- // options are to block all cookies, or allow just partitioned cookies.
- switch (metadata.third_party_blocking_outcome) {
- case ThirdPartyBlockingOutcome::kIrrelevant:
- [[fallthrough]];
- case ThirdPartyBlockingOutcome::kAllStateDisallowed:
- return net::NetworkDelegate::PrivacySetting::kStateDisallowed;
- case ThirdPartyBlockingOutcome::kPartitionedStateAllowed:
- return net::NetworkDelegate::PrivacySetting::kPartitionedStateAllowedOnly;
- }
- }
- bool CookieSettings::BlockDueToThirdPartyCookieBlockingSetting(
- bool is_third_party_request,
- const GURL& url,
- const GURL& first_party_url,
- ContentSetting cookie_setting,
- QueryReason query_reason) const {
- if (block_third_party_cookies_ && is_third_party_request &&
- !base::Contains(third_party_cookies_allowed_schemes_,
- first_party_url.scheme())) {
- if (ShouldConsiderStorageAccessGrants(query_reason)) {
- // See if a Storage Access permission grant can unblock.
- if (const ContentSettingPatternSource* match =
- FindMatchingSetting(url, first_party_url, storage_access_grants_);
- match && match->GetContentSetting() == CONTENT_SETTING_ALLOW) {
- FireStorageAccessHistogram(net::cookie_util::StorageAccessResult::
- ACCESS_ALLOWED_STORAGE_ACCESS_GRANT);
- return false;
- }
- }
- FireStorageAccessHistogram(
- net::cookie_util::StorageAccessResult::ACCESS_BLOCKED);
- return true;
- }
- // Cookies aren't blocked solely due to the third-party-cookie blocking
- // setting, but they still may be blocked due to a global default. So we
- // have to check what the setting is here.
- FireStorageAccessHistogram(
- cookie_setting == CONTENT_SETTING_BLOCK
- ? net::cookie_util::StorageAccessResult::ACCESS_BLOCKED
- : net::cookie_util::StorageAccessResult::ACCESS_ALLOWED);
- return false;
- }
- CookieSettings::ThirdPartyBlockingOutcome
- CookieSettings::GetThirdPartyBlockingScope(const GURL& first_party_url) const {
- // If cookies are allowed for the first-party URL then we allow
- // partitioned cross-site cookies.
- if (const ContentSettingPatternSource* match = FindMatchingSetting(
- first_party_url, first_party_url, content_settings_);
- !match || match->GetContentSetting() == CONTENT_SETTING_ALLOW) {
- return ThirdPartyBlockingOutcome::kPartitionedStateAllowed;
- }
- return ThirdPartyBlockingOutcome::kAllStateDisallowed;
- }
- CookieSettings::CookieSettingWithMetadata
- CookieSettings::GetCookieSettingWithMetadata(const GURL& url,
- const GURL& first_party_url,
- bool is_third_party_request,
- QueryReason query_reason) const {
- if (ShouldAlwaysAllowCookies(url, first_party_url)) {
- return {
- /*cookie_setting=*/CONTENT_SETTING_ALLOW,
- /*third_party_blocking_outcome=*/
- ThirdPartyBlockingOutcome::kIrrelevant,
- };
- }
- // Default to allowing cookies.
- ContentSetting cookie_setting = CONTENT_SETTING_ALLOW;
- ThirdPartyBlockingOutcome third_party_blocking_outcome =
- ThirdPartyBlockingOutcome::kIrrelevant;
- bool found_explicit_setting = false;
- if (const ContentSettingPatternSource* match =
- FindMatchingSetting(url, first_party_url, content_settings_);
- match) {
- cookie_setting = match->GetContentSetting();
- found_explicit_setting = IsExplicitSetting(*match);
- }
- if (cookie_setting != CONTENT_SETTING_BLOCK && !found_explicit_setting) {
- if (BlockDueToThirdPartyCookieBlockingSetting(
- is_third_party_request, url, first_party_url, cookie_setting,
- query_reason)) {
- cookie_setting = CONTENT_SETTING_BLOCK;
- third_party_blocking_outcome =
- GetThirdPartyBlockingScope(first_party_url);
- }
- } else {
- FireStorageAccessHistogram(
- cookie_setting == CONTENT_SETTING_BLOCK
- ? net::cookie_util::StorageAccessResult::ACCESS_BLOCKED
- : net::cookie_util::StorageAccessResult::ACCESS_ALLOWED);
- }
- return {cookie_setting, third_party_blocking_outcome};
- }
- CookieSettings::CookieSettingWithMetadata
- CookieSettings::GetCookieSettingWithMetadata(
- const GURL& url,
- const net::SiteForCookies& site_for_cookies,
- const url::Origin* top_frame_origin,
- QueryReason query_reason) const {
- return GetCookieSettingWithMetadata(
- url, GetFirstPartyURL(site_for_cookies, top_frame_origin),
- IsThirdPartyRequest(url, site_for_cookies), query_reason);
- }
- ContentSetting CookieSettings::GetCookieSettingInternal(
- const GURL& url,
- const GURL& first_party_url,
- bool is_third_party_request,
- content_settings::SettingSource* source,
- QueryReason query_reason) const {
- return GetCookieSettingWithMetadata(url, first_party_url,
- is_third_party_request, query_reason)
- .cookie_setting;
- }
- bool CookieSettings::AnnotateAndMoveUserBlockedCookies(
- const GURL& url,
- const net::SiteForCookies& site_for_cookies,
- const url::Origin* top_frame_origin,
- net::CookieAccessResultList& maybe_included_cookies,
- net::CookieAccessResultList& excluded_cookies) const {
- const CookieSettingWithMetadata setting_with_metadata =
- GetCookieSettingWithMetadata(url, site_for_cookies, top_frame_origin,
- QueryReason::kCookies);
- if (IsAllowed(setting_with_metadata.cookie_setting))
- return true;
- // Add the `EXCLUDE_USER_PREFERENCES` `ExclusionReason` for cookies that ought
- // to be blocked, and find any cookies that should still be allowed.
- bool is_any_allowed = false;
- for (net::CookieWithAccessResult& cookie : maybe_included_cookies) {
- if (IsCookieAllowed(setting_with_metadata, cookie)) {
- is_any_allowed = true;
- } else {
- cookie.access_result.status.AddExclusionReason(
- net::CookieInclusionStatus::EXCLUDE_USER_PREFERENCES);
- }
- }
- for (net::CookieWithAccessResult& cookie : excluded_cookies) {
- if (!IsCookieAllowed(setting_with_metadata, cookie)) {
- cookie.access_result.status.AddExclusionReason(
- net::CookieInclusionStatus::EXCLUDE_USER_PREFERENCES);
- }
- }
- const auto to_be_moved = base::ranges::stable_partition(
- maybe_included_cookies, [](const net::CookieWithAccessResult& cookie) {
- return cookie.access_result.status.IsInclude();
- });
- excluded_cookies.insert(
- excluded_cookies.end(), std::make_move_iterator(to_be_moved),
- std::make_move_iterator(maybe_included_cookies.end()));
- maybe_included_cookies.erase(to_be_moved, maybe_included_cookies.end());
- net::cookie_util::DCheckIncludedAndExcludedCookieLists(maybe_included_cookies,
- excluded_cookies);
- return is_any_allowed;
- }
- bool CookieSettings::IsCookieAllowed(
- const CookieSettingWithMetadata& setting_with_metadata,
- const net::CookieWithAccessResult& cookie) const {
- return IsHypotheticalCookieAllowed(
- setting_with_metadata,
- cookie.cookie.IsSameParty() &&
- !cookie.access_result.status.HasExclusionReason(
- net::CookieInclusionStatus::
- EXCLUDE_SAMEPARTY_CROSS_PARTY_CONTEXT),
- cookie.cookie.IsPartitioned(),
- /*record_metrics=*/true);
- }
- bool CookieSettings::IsAllowedSamePartyCookie(
- bool is_same_party,
- ThirdPartyBlockingOutcome third_party_blocking_outcome,
- bool record_metrics) const {
- bool blocked_by_3p_but_same_party =
- is_same_party &&
- third_party_blocking_outcome != ThirdPartyBlockingOutcome::kIrrelevant;
- if (record_metrics && blocked_by_3p_but_same_party) {
- UMA_HISTOGRAM_BOOLEAN(
- "Cookie.SameParty.BlockedByThirdPartyCookieBlockingSetting",
- !sameparty_cookies_considered_first_party_);
- }
- return sameparty_cookies_considered_first_party_ &&
- blocked_by_3p_but_same_party;
- }
- // static
- bool CookieSettings::IsAllowedPartitionedCookie(
- bool is_partitioned,
- ThirdPartyBlockingOutcome third_party_blocking_outcome) {
- return is_partitioned &&
- third_party_blocking_outcome ==
- ThirdPartyBlockingOutcome::kPartitionedStateAllowed;
- }
- bool CookieSettings::IsHypotheticalCookieAllowed(
- const CookieSettingWithMetadata& setting_with_metadata,
- bool is_same_party,
- bool is_partitioned,
- bool record_metrics) const {
- DCHECK(!is_partitioned || !is_same_party);
- return IsAllowed(setting_with_metadata.cookie_setting) ||
- IsAllowedSamePartyCookie(
- is_same_party, setting_with_metadata.third_party_blocking_outcome,
- record_metrics) ||
- IsAllowedPartitionedCookie(
- is_partitioned,
- setting_with_metadata.third_party_blocking_outcome);
- }
- bool CookieSettings::HasSessionOnlyOrigins() const {
- return base::ranges::any_of(content_settings_, [](const auto& entry) {
- return entry.GetContentSetting() == CONTENT_SETTING_SESSION_ONLY;
- });
- }
- } // namespace network
|