1 // Copyright 2019 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 <string>
6
7 #include "base/feature_list.h"
8 #include "base/values.h"
9 #include "net/base/features.h"
10 #include "net/base/network_isolation_key.h"
11 #include "net/base/registry_controlled_domains/registry_controlled_domain.h"
12 #include "url/gurl.h"
13 #include "url/origin.h"
14 #include "url/url_constants.h"
15
16 namespace net {
17
18 namespace {
19
GetOriginDebugString(const base::Optional<url::Origin> & origin)20 std::string GetOriginDebugString(const base::Optional<url::Origin>& origin) {
21 return origin ? origin->GetDebugString() : "null";
22 }
23
24 // If |origin| has a value and represents an HTTP or HTTPS scheme, return a new
25 // origin with its registerable domain if possible, using the standard port for
26 // its scheme. Otherwise, return the passed in origin. WS and WSS origins are
27 // not modified, as they shouldn't be used meaningfully for NIKs, though trying
28 // to navigate to a WS URL may generate such a NIK.
SwitchToRegistrableDomainAndRemovePort(const base::Optional<url::Origin> & origin)29 base::Optional<url::Origin> SwitchToRegistrableDomainAndRemovePort(
30 const base::Optional<url::Origin>& origin) {
31 if (!origin.has_value())
32 return origin;
33
34 if (origin->scheme() != url::kHttpsScheme &&
35 origin->scheme() != url::kHttpScheme) {
36 return origin;
37 }
38
39 // scheme() returns the empty string for opaque origins.
40 DCHECK(!origin->opaque());
41
42 std::string registrable_domain = GetDomainAndRegistry(
43 origin.value(),
44 net::registry_controlled_domains::INCLUDE_PRIVATE_REGISTRIES);
45 // GetDomainAndRegistry() returns an empty string for IP literals and
46 // effective TLDs.
47 if (registrable_domain.empty())
48 registrable_domain = origin->host();
49 return url::Origin::CreateFromNormalizedTuple(
50 origin->scheme(), registrable_domain,
51 url::DefaultPortForScheme(origin->scheme().c_str(),
52 origin->scheme().length()));
53 }
54
55 } // namespace
56
NetworkIsolationKey(const url::Origin & top_frame_origin,const url::Origin & frame_origin)57 NetworkIsolationKey::NetworkIsolationKey(const url::Origin& top_frame_origin,
58 const url::Origin& frame_origin)
59 : NetworkIsolationKey(top_frame_origin,
60 frame_origin,
61 false /* opaque_and_non_transient */) {}
62
NetworkIsolationKey()63 NetworkIsolationKey::NetworkIsolationKey()
64 : use_frame_site_(base::FeatureList::IsEnabled(
65 net::features::kAppendFrameOriginToNetworkIsolationKey)) {}
66
67 NetworkIsolationKey::NetworkIsolationKey(
68 const NetworkIsolationKey& network_isolation_key) = default;
69
70 NetworkIsolationKey::NetworkIsolationKey(
71 NetworkIsolationKey&& network_isolation_key) = default;
72
73 NetworkIsolationKey::~NetworkIsolationKey() = default;
74
75 NetworkIsolationKey& NetworkIsolationKey::operator=(
76 const NetworkIsolationKey& network_isolation_key) = default;
77
78 NetworkIsolationKey& NetworkIsolationKey::operator=(
79 NetworkIsolationKey&& network_isolation_key) = default;
80
CreateTransient()81 NetworkIsolationKey NetworkIsolationKey::CreateTransient() {
82 url::Origin opaque_origin;
83 return NetworkIsolationKey(opaque_origin, opaque_origin);
84 }
85
CreateOpaqueAndNonTransient()86 NetworkIsolationKey NetworkIsolationKey::CreateOpaqueAndNonTransient() {
87 url::Origin opaque_origin;
88 return NetworkIsolationKey(opaque_origin, opaque_origin,
89 true /* opaque_and_non_transient */);
90 }
91
CreateWithNewFrameOrigin(const url::Origin & new_frame_origin) const92 NetworkIsolationKey NetworkIsolationKey::CreateWithNewFrameOrigin(
93 const url::Origin& new_frame_origin) const {
94 if (!top_frame_site_)
95 return NetworkIsolationKey();
96 NetworkIsolationKey key(top_frame_site_.value(), new_frame_origin);
97 key.opaque_and_non_transient_ = opaque_and_non_transient_;
98 return key;
99 }
100
ToString() const101 std::string NetworkIsolationKey::ToString() const {
102 if (IsTransient())
103 return "";
104
105 if (IsOpaque()) {
106 // This key is opaque but not transient.
107 DCHECK(opaque_and_non_transient_);
108 return "opaque non-transient " +
109 top_frame_site_->nonce_->token().ToString();
110 }
111
112 return top_frame_site_->Serialize() +
113 (use_frame_site_ ? " " + frame_site_->Serialize() : "");
114 }
115
ToDebugString() const116 std::string NetworkIsolationKey::ToDebugString() const {
117 // The space-separated serialization of |top_frame_site_| and
118 // |frame_site_|.
119 std::string return_string = GetOriginDebugString(top_frame_site_);
120 if (use_frame_site_) {
121 return_string += " " + GetOriginDebugString(frame_site_);
122 }
123 if (IsFullyPopulated() && IsOpaque() && opaque_and_non_transient_) {
124 return_string += " non-transient";
125 }
126 return return_string;
127 }
128
IsFullyPopulated() const129 bool NetworkIsolationKey::IsFullyPopulated() const {
130 return top_frame_site_.has_value() &&
131 (!use_frame_site_ || frame_site_.has_value());
132 }
133
IsTransient() const134 bool NetworkIsolationKey::IsTransient() const {
135 if (!IsFullyPopulated())
136 return true;
137 if (opaque_and_non_transient_) {
138 DCHECK(IsOpaque());
139 return false;
140 }
141 return IsOpaque();
142 }
143
ToValue(base::Value * out_value) const144 bool NetworkIsolationKey::ToValue(base::Value* out_value) const {
145 if (IsEmpty()) {
146 *out_value = base::Value(base::Value::Type::LIST);
147 return true;
148 }
149
150 if (IsTransient())
151 return false;
152
153 base::Optional<std::string> top_frame_value =
154 top_frame_site_->SerializeWithNonce();
155 if (!top_frame_value)
156 return false;
157 *out_value = base::Value(base::Value::Type::LIST);
158 out_value->Append(std::move(*top_frame_value));
159
160 if (use_frame_site_) {
161 base::Optional<std::string> frame_value = frame_site_->SerializeWithNonce();
162 if (!frame_value)
163 return false;
164 out_value->Append(std::move(*frame_value));
165 }
166
167 return true;
168 }
169
FromValue(const base::Value & value,NetworkIsolationKey * network_isolation_key)170 bool NetworkIsolationKey::FromValue(
171 const base::Value& value,
172 NetworkIsolationKey* network_isolation_key) {
173 if (!value.is_list())
174 return false;
175
176 base::Value::ConstListView list = value.GetList();
177 if (list.empty()) {
178 *network_isolation_key = NetworkIsolationKey();
179 return true;
180 }
181
182 bool use_frame_origin = base::FeatureList::IsEnabled(
183 net::features::kAppendFrameOriginToNetworkIsolationKey);
184 if ((!use_frame_origin && list.size() != 1) ||
185 (use_frame_origin && list.size() != 2)) {
186 return false;
187 }
188
189 if (!list[0].is_string())
190 return false;
191 base::Optional<url::Origin> deserialized_top_frame =
192 url::Origin::Deserialize(list[0].GetString());
193 if (!deserialized_top_frame)
194 return false;
195 url::Origin top_frame_origin = *deserialized_top_frame;
196
197 // An opaque origin key will only be serialized into a base::Value if
198 // |opaque_and_non_transient_| is set. Therefore if either origin is opaque,
199 // |opaque_and_non_transient_| must be true.
200 bool opaque_and_non_transient = top_frame_origin.opaque();
201
202 if (!use_frame_origin) {
203 *network_isolation_key =
204 NetworkIsolationKey(top_frame_origin, top_frame_origin);
205 network_isolation_key->opaque_and_non_transient_ = opaque_and_non_transient;
206 return true;
207 }
208
209 if (!list[1].is_string())
210 return false;
211 base::Optional<url::Origin> deserialized_frame =
212 url::Origin::Deserialize(list[1].GetString());
213 if (!deserialized_frame)
214 return false;
215 url::Origin frame_origin = *deserialized_frame;
216
217 opaque_and_non_transient |= frame_origin.opaque();
218
219 *network_isolation_key = NetworkIsolationKey(top_frame_origin, frame_origin);
220 network_isolation_key->opaque_and_non_transient_ = opaque_and_non_transient;
221 return true;
222 }
223
IsEmpty() const224 bool NetworkIsolationKey::IsEmpty() const {
225 return !top_frame_site_.has_value() && !frame_site_.has_value();
226 }
227
NetworkIsolationKey(const url::Origin & top_frame_origin,const url::Origin & frame_origin,bool opaque_and_non_transient)228 NetworkIsolationKey::NetworkIsolationKey(const url::Origin& top_frame_origin,
229 const url::Origin& frame_origin,
230 bool opaque_and_non_transient)
231 : opaque_and_non_transient_(opaque_and_non_transient),
232 use_frame_site_(base::FeatureList::IsEnabled(
233 net::features::kAppendFrameOriginToNetworkIsolationKey)),
234 top_frame_site_(
235 SwitchToRegistrableDomainAndRemovePort(top_frame_origin)) {
236 DCHECK(!opaque_and_non_transient || top_frame_origin.opaque());
237 if (use_frame_site_) {
238 DCHECK(!opaque_and_non_transient || frame_origin.opaque());
239 frame_site_ = SwitchToRegistrableDomainAndRemovePort(frame_origin);
240 }
241 }
242
IsOpaque() const243 bool NetworkIsolationKey::IsOpaque() const {
244 return top_frame_site_->opaque() ||
245 (use_frame_site_ && frame_site_->opaque());
246 }
247
248 } // namespace net
249