blob: 0fda14583785ce85664ffebc250aaa2393c471ce [file] [log] [blame]
// Copyright 2020 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 "net/base/isolation_info.h"
#include "base/check_op.h"
namespace net {
namespace {
// Checks that |origin| is consistent with |site_for_cookies|.
bool ValidateSameSite(const url::Origin& origin,
const SiteForCookies& site_for_cookies) {
// If not sending SameSite cookies, or sending them for a non-scheme, consider
// all origins consistent. Note that SiteForCookies should never be created
// for websocket schemes for valid navigations, since frames can't be
// navigated to those schemes.
if (site_for_cookies.IsNull() ||
(site_for_cookies.scheme() != url::kHttpScheme &&
site_for_cookies.scheme() != url::kHttpsScheme)) {
return true;
}
// Shouldn't send cookies for opaque origins.
if (origin.opaque())
return false;
// TODO(https://crbug.com/1060631): GetURL() is expensive. Maybe make a
// version of IsFirstParty that works on origins?
return site_for_cookies.IsFirstParty(origin.GetURL());
}
// Checks if these values are consistent. See IsolationInfo::Create() for
// descriptions of consistent sets of values. Also allows values used by the
// 0-argument constructor. Additionally, |opaque_and_non_transient| can only be
// true if both origins are opaque and |site_for_cookies| is null.
bool IsConsistent(IsolationInfo::RedirectMode redirect_mode,
const base::Optional<url::Origin>& top_frame_origin,
const base::Optional<url::Origin>& frame_origin,
const SiteForCookies& site_for_cookies,
bool opaque_and_non_transient) {
// Check for the default-constructed case.
if (!top_frame_origin) {
return redirect_mode == IsolationInfo::RedirectMode::kUpdateNothing &&
!frame_origin && site_for_cookies.IsNull() &&
!opaque_and_non_transient;
}
// |frame_origin| may only be nullopt is |top_frame_origin| is as well.
if (!frame_origin)
return false;
// As long as there is a |top_frame_origin|, |site_for_cookies| must be
// consistent with the |top_frame_origin|.
if (!ValidateSameSite(*top_frame_origin, site_for_cookies))
return false;
if (opaque_and_non_transient) {
return (redirect_mode == IsolationInfo::RedirectMode::kUpdateNothing &&
top_frame_origin->opaque() && top_frame_origin == frame_origin &&
site_for_cookies.IsNull());
}
switch (redirect_mode) {
case IsolationInfo::RedirectMode::kUpdateTopFrame:
// TODO(https://crbug.com/1056706): Check that |top_frame_origin| and
// |frame_origin| are the same, once the ViewSource code creates a
// consistent IsolationInfo object.
//
// TODO(https://crbug.com/1060631): Once CreatePartial() is removed, check
// if SiteForCookies is non-null if the scheme is HTTP or HTTPS.
return true;
case IsolationInfo::RedirectMode::kUpdateFrameOnly:
// For subframe navigations, the subframe's origin may not be consistent
// with the SiteForCookies, so SameSite cookies may be sent if there's a
// redirect to main frames site.
return true;
case IsolationInfo::RedirectMode::kUpdateNothing:
// SiteForCookies must consistent with the frame origin as well for
// subresources.
return ValidateSameSite(*frame_origin, site_for_cookies);
}
}
} // namespace
IsolationInfo::IsolationInfo()
: IsolationInfo(RedirectMode::kUpdateNothing,
base::nullopt,
base::nullopt,
SiteForCookies(),
false /* opaque_and_non_transient */) {}
IsolationInfo::IsolationInfo(const IsolationInfo&) = default;
IsolationInfo::IsolationInfo(IsolationInfo&&) = default;
IsolationInfo::~IsolationInfo() = default;
IsolationInfo& IsolationInfo::operator=(const IsolationInfo&) = default;
IsolationInfo& IsolationInfo::operator=(IsolationInfo&&) = default;
IsolationInfo IsolationInfo::CreateForInternalRequest(
const url::Origin& top_frame_origin) {
return IsolationInfo(RedirectMode::kUpdateNothing, top_frame_origin,
top_frame_origin,
SiteForCookies::FromOrigin(top_frame_origin),
false /* opaque_and_non_transient */);
}
IsolationInfo IsolationInfo::CreateTransient() {
return CreateForInternalRequest(url::Origin());
}
IsolationInfo IsolationInfo::CreateOpaqueAndNonTransient() {
url::Origin opaque_origin;
return IsolationInfo(RedirectMode::kUpdateNothing, opaque_origin,
opaque_origin, SiteForCookies(),
true /* opaque_and_non_transient */);
}
IsolationInfo IsolationInfo::Create(RedirectMode redirect_mode,
const url::Origin& top_frame_origin,
const url::Origin& frame_origin,
const SiteForCookies& site_for_cookies) {
return IsolationInfo(redirect_mode, top_frame_origin, frame_origin,
site_for_cookies, false /* opaque_and_non_transient */);
}
IsolationInfo IsolationInfo::CreatePartial(
RedirectMode redirect_mode,
const net::NetworkIsolationKey& network_isolation_key) {
if (!network_isolation_key.IsFullyPopulated())
return IsolationInfo();
url::Origin top_frame_origin = *network_isolation_key.GetTopFrameOrigin();
url::Origin frame_origin;
if (network_isolation_key.GetFrameOrigin().has_value()) {
frame_origin = *network_isolation_key.GetFrameOrigin();
} else if (redirect_mode == RedirectMode::kUpdateTopFrame) {
frame_origin = top_frame_origin;
} else {
frame_origin = url::Origin();
}
bool opaque_and_non_transient = top_frame_origin.opaque() &&
frame_origin.opaque() &&
!network_isolation_key.IsTransient();
return IsolationInfo(redirect_mode, top_frame_origin, frame_origin,
SiteForCookies(), opaque_and_non_transient);
}
base::Optional<IsolationInfo> IsolationInfo::CreateIfConsistent(
RedirectMode redirect_mode,
const base::Optional<url::Origin>& top_frame_origin,
const base::Optional<url::Origin>& frame_origin,
const SiteForCookies& site_for_cookies,
bool opaque_and_non_transient) {
if (!IsConsistent(redirect_mode, top_frame_origin, frame_origin,
site_for_cookies, opaque_and_non_transient)) {
return base::nullopt;
}
return IsolationInfo(redirect_mode, top_frame_origin, frame_origin,
site_for_cookies, opaque_and_non_transient);
}
IsolationInfo IsolationInfo::CreateForRedirect(
const url::Origin& new_origin) const {
if (redirect_mode_ == RedirectMode::kUpdateNothing)
return *this;
if (redirect_mode_ == RedirectMode::kUpdateFrameOnly) {
return IsolationInfo(redirect_mode_, top_frame_origin_, new_origin,
site_for_cookies_, opaque_and_non_transient_);
}
DCHECK_EQ(RedirectMode::kUpdateTopFrame, redirect_mode_);
return IsolationInfo(redirect_mode_, new_origin, new_origin,
SiteForCookies::FromOrigin(new_origin),
opaque_and_non_transient_);
}
bool IsolationInfo::IsEqualForTesting(const IsolationInfo& other) const {
return (redirect_mode_ == other.redirect_mode_ &&
top_frame_origin_ == other.top_frame_origin_ &&
frame_origin_ == other.frame_origin_ &&
network_isolation_key_ == other.network_isolation_key_ &&
opaque_and_non_transient_ == other.opaque_and_non_transient_ &&
site_for_cookies_.IsEquivalent(other.site_for_cookies_));
}
IsolationInfo::IsolationInfo(
RedirectMode redirect_mode,
const base::Optional<url::Origin>& top_frame_origin,
const base::Optional<url::Origin>& frame_origin,
const SiteForCookies& site_for_cookies,
bool opaque_and_non_transient)
: redirect_mode_(redirect_mode),
top_frame_origin_(top_frame_origin),
frame_origin_(frame_origin),
network_isolation_key_(
!top_frame_origin ? NetworkIsolationKey()
: NetworkIsolationKey(*top_frame_origin,
*frame_origin,
opaque_and_non_transient)),
site_for_cookies_(site_for_cookies),
opaque_and_non_transient_(opaque_and_non_transient) {
DCHECK(IsConsistent(redirect_mode_, top_frame_origin_, frame_origin_,
site_for_cookies_, opaque_and_non_transient_));
}
} // namespace net