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