blob: 115313e45ac30a8d5f0831d8189543bc8631925a [file] [log] [blame]
Matt Menke3ab9c7b2020-04-01 22:39:571// Copyright 2020 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
5#include "net/base/isolation_info.h"
6
Hans Wennborg0924470b2020-04-27 21:08:057#include "base/check_op.h"
Matt Menke3ab9c7b2020-04-01 22:39:578
9namespace net {
10
11namespace {
12
13// Checks that |origin| is consistent with |site_for_cookies|.
14bool ValidateSameSite(const url::Origin& origin,
15 const SiteForCookies& site_for_cookies) {
16 // If not sending SameSite cookies, or sending them for a non-scheme, consider
17 // all origins consistent. Note that SiteForCookies should never be created
18 // for websocket schemes for valid navigations, since frames can't be
19 // navigated to those schemes.
20 if (site_for_cookies.IsNull() ||
21 (site_for_cookies.scheme() != url::kHttpScheme &&
22 site_for_cookies.scheme() != url::kHttpsScheme)) {
23 return true;
24 }
25
26 // Shouldn't send cookies for opaque origins.
27 if (origin.opaque())
28 return false;
29
30 // TODO(https://crbug.com/1060631): GetURL() is expensive. Maybe make a
31 // version of IsFirstParty that works on origins?
32 return site_for_cookies.IsFirstParty(origin.GetURL());
33}
34
35// Checks if these values are consistent. See IsolationInfo::Create() for
36// descriptions of consistent sets of values. Also allows values used by the
37// 0-argument constructor. Additionally, |opaque_and_non_transient| can only be
38// true if both origins are opaque and |site_for_cookies| is null.
39bool IsConsistent(IsolationInfo::RedirectMode redirect_mode,
40 const base::Optional<url::Origin>& top_frame_origin,
41 const base::Optional<url::Origin>& frame_origin,
42 const SiteForCookies& site_for_cookies,
43 bool opaque_and_non_transient) {
44 // Check for the default-constructed case.
45 if (!top_frame_origin) {
46 return redirect_mode == IsolationInfo::RedirectMode::kUpdateNothing &&
47 !frame_origin && site_for_cookies.IsNull() &&
48 !opaque_and_non_transient;
49 }
50
51 // |frame_origin| may only be nullopt is |top_frame_origin| is as well.
52 if (!frame_origin)
53 return false;
54
55 // As long as there is a |top_frame_origin|, |site_for_cookies| must be
56 // consistent with the |top_frame_origin|.
57 if (!ValidateSameSite(*top_frame_origin, site_for_cookies))
58 return false;
59
60 if (opaque_and_non_transient) {
61 return (redirect_mode == IsolationInfo::RedirectMode::kUpdateNothing &&
62 top_frame_origin->opaque() && top_frame_origin == frame_origin &&
63 site_for_cookies.IsNull());
64 }
65
66 switch (redirect_mode) {
67 case IsolationInfo::RedirectMode::kUpdateTopFrame:
68 // TODO(https://crbug.com/1056706): Check that |top_frame_origin| and
69 // |frame_origin| are the same, once the ViewSource code creates a
70 // consistent IsolationInfo object.
71 //
72 // TODO(https://crbug.com/1060631): Once CreatePartial() is removed, check
73 // if SiteForCookies is non-null if the scheme is HTTP or HTTPS.
74 return true;
75 case IsolationInfo::RedirectMode::kUpdateFrameOnly:
76 // For subframe navigations, the subframe's origin may not be consistent
77 // with the SiteForCookies, so SameSite cookies may be sent if there's a
78 // redirect to main frames site.
79 return true;
80 case IsolationInfo::RedirectMode::kUpdateNothing:
81 // SiteForCookies must consistent with the frame origin as well for
82 // subresources.
83 return ValidateSameSite(*frame_origin, site_for_cookies);
84 }
85}
86
87} // namespace
88
89IsolationInfo::IsolationInfo()
90 : IsolationInfo(RedirectMode::kUpdateNothing,
91 base::nullopt,
92 base::nullopt,
93 SiteForCookies(),
94 false /* opaque_and_non_transient */) {}
95
96IsolationInfo::IsolationInfo(const IsolationInfo&) = default;
97IsolationInfo::IsolationInfo(IsolationInfo&&) = default;
98IsolationInfo::~IsolationInfo() = default;
99IsolationInfo& IsolationInfo::operator=(const IsolationInfo&) = default;
100IsolationInfo& IsolationInfo::operator=(IsolationInfo&&) = default;
101
102IsolationInfo IsolationInfo::CreateForInternalRequest(
103 const url::Origin& top_frame_origin) {
104 return IsolationInfo(RedirectMode::kUpdateNothing, top_frame_origin,
105 top_frame_origin,
106 SiteForCookies::FromOrigin(top_frame_origin),
107 false /* opaque_and_non_transient */);
108}
109
110IsolationInfo IsolationInfo::CreateTransient() {
111 return CreateForInternalRequest(url::Origin());
112}
113
114IsolationInfo IsolationInfo::CreateOpaqueAndNonTransient() {
115 url::Origin opaque_origin;
116 return IsolationInfo(RedirectMode::kUpdateNothing, opaque_origin,
117 opaque_origin, SiteForCookies(),
118 true /* opaque_and_non_transient */);
119}
120
121IsolationInfo IsolationInfo::Create(RedirectMode redirect_mode,
122 const url::Origin& top_frame_origin,
123 const url::Origin& frame_origin,
124 const SiteForCookies& site_for_cookies) {
125 return IsolationInfo(redirect_mode, top_frame_origin, frame_origin,
126 site_for_cookies, false /* opaque_and_non_transient */);
127}
128
129IsolationInfo IsolationInfo::CreatePartial(
130 RedirectMode redirect_mode,
131 const net::NetworkIsolationKey& network_isolation_key) {
132 if (!network_isolation_key.IsFullyPopulated())
133 return IsolationInfo();
134
Matt Menkee6e4f392020-10-08 11:41:29135 url::Origin top_frame_origin = *network_isolation_key.GetTopFrameSite();
Matt Menke3ab9c7b2020-04-01 22:39:57136 url::Origin frame_origin;
Matt Menkee6e4f392020-10-08 11:41:29137 if (network_isolation_key.GetFrameSite().has_value()) {
138 frame_origin = *network_isolation_key.GetFrameSite();
Matt Menke3ab9c7b2020-04-01 22:39:57139 } else if (redirect_mode == RedirectMode::kUpdateTopFrame) {
140 frame_origin = top_frame_origin;
141 } else {
142 frame_origin = url::Origin();
143 }
144
145 bool opaque_and_non_transient = top_frame_origin.opaque() &&
146 frame_origin.opaque() &&
147 !network_isolation_key.IsTransient();
148
149 return IsolationInfo(redirect_mode, top_frame_origin, frame_origin,
150 SiteForCookies(), opaque_and_non_transient);
151}
152
153base::Optional<IsolationInfo> IsolationInfo::CreateIfConsistent(
154 RedirectMode redirect_mode,
155 const base::Optional<url::Origin>& top_frame_origin,
156 const base::Optional<url::Origin>& frame_origin,
157 const SiteForCookies& site_for_cookies,
158 bool opaque_and_non_transient) {
159 if (!IsConsistent(redirect_mode, top_frame_origin, frame_origin,
160 site_for_cookies, opaque_and_non_transient)) {
161 return base::nullopt;
162 }
163 return IsolationInfo(redirect_mode, top_frame_origin, frame_origin,
164 site_for_cookies, opaque_and_non_transient);
165}
166
167IsolationInfo IsolationInfo::CreateForRedirect(
168 const url::Origin& new_origin) const {
169 if (redirect_mode_ == RedirectMode::kUpdateNothing)
170 return *this;
171
172 if (redirect_mode_ == RedirectMode::kUpdateFrameOnly) {
173 return IsolationInfo(redirect_mode_, top_frame_origin_, new_origin,
174 site_for_cookies_, opaque_and_non_transient_);
175 }
176
177 DCHECK_EQ(RedirectMode::kUpdateTopFrame, redirect_mode_);
178 return IsolationInfo(redirect_mode_, new_origin, new_origin,
179 SiteForCookies::FromOrigin(new_origin),
180 opaque_and_non_transient_);
181}
182
183bool IsolationInfo::IsEqualForTesting(const IsolationInfo& other) const {
184 return (redirect_mode_ == other.redirect_mode_ &&
185 top_frame_origin_ == other.top_frame_origin_ &&
186 frame_origin_ == other.frame_origin_ &&
187 network_isolation_key_ == other.network_isolation_key_ &&
188 opaque_and_non_transient_ == other.opaque_and_non_transient_ &&
189 site_for_cookies_.IsEquivalent(other.site_for_cookies_));
190}
191
192IsolationInfo::IsolationInfo(
193 RedirectMode redirect_mode,
194 const base::Optional<url::Origin>& top_frame_origin,
195 const base::Optional<url::Origin>& frame_origin,
196 const SiteForCookies& site_for_cookies,
197 bool opaque_and_non_transient)
198 : redirect_mode_(redirect_mode),
199 top_frame_origin_(top_frame_origin),
200 frame_origin_(frame_origin),
201 network_isolation_key_(
202 !top_frame_origin ? NetworkIsolationKey()
203 : NetworkIsolationKey(*top_frame_origin,
204 *frame_origin,
205 opaque_and_non_transient)),
206 site_for_cookies_(site_for_cookies),
207 opaque_and_non_transient_(opaque_and_non_transient) {
208 DCHECK(IsConsistent(redirect_mode_, top_frame_origin_, frame_origin_,
209 site_for_cookies_, opaque_and_non_transient_));
210}
211
212} // namespace net