1 // Copyright 2020 The Chromium Authors 2 // Use of this source code is governed by a BSD-style license that can be 3 // found in the LICENSE file. 4 5 #ifndef NET_BASE_SCHEMEFUL_SITE_H_ 6 #define NET_BASE_SCHEMEFUL_SITE_H_ 7 8 #include <ostream> 9 #include <string> 10 11 #include "base/gtest_prod_util.h" 12 #include "net/base/net_export.h" 13 #include "third_party/abseil-cpp/absl/types/optional.h" 14 #include "url/origin.h" 15 16 class GURL; 17 18 namespace blink { 19 class BlinkSchemefulSite; 20 class StorageKey; 21 } // namespace blink 22 23 namespace IPC { 24 template <class P> 25 struct ParamTraits; 26 } // namespace IPC 27 28 namespace network::mojom { 29 class SchemefulSiteDataView; 30 } // namespace network::mojom 31 32 namespace mojo { 33 template <typename DataViewType, typename T> 34 struct StructTraits; 35 } // namespace mojo 36 37 namespace net { 38 39 class SiteForCookies; 40 41 // Class which represents a scheme and etld+1 for an origin, as specified by 42 // https://html.spec.whatwg.org/multipage/origin.html#obtain-a-site. 43 // 44 // A SchemefulSite is obtained from an input origin by normalizing, such that: 45 // 1. Opaque origins have distinct SchemefulSites. 46 // 2. Origins whose schemes have network hosts have the same SchemefulSite iff 47 // they share a scheme, and share a hostname or registrable domain. Origins 48 // whose schemes have network hosts include http, https, ws, wss, file, etc. 49 // 3. Origins whose schemes do not have a network host have the same 50 // SchemefulSite iff they share a scheme and host. 51 // 4. Origins which differ only by port have the same SchemefulSite. 52 // 5. Websocket origins cannot have a SchemefulSite (they trigger a DCHECK). 53 // 54 // Note that blink::BlinkSchemefulSite mirrors this class and needs to be kept 55 // in sync with any data member changes. 56 class NET_EXPORT SchemefulSite { 57 public: 58 SchemefulSite() = default; 59 60 // The passed `origin` may not match the resulting internal representation in 61 // certain circumstances. See the comment, below, on the `site_as_origin_` 62 // member. 63 explicit SchemefulSite(const url::Origin& origin); 64 65 // Using the origin constructor is preferred as this is less efficient. 66 // Should only be used if the origin for a given GURL is not readily 67 // available. 68 explicit SchemefulSite(const GURL& url); 69 70 SchemefulSite(const SchemefulSite& other); 71 SchemefulSite(SchemefulSite&& other) noexcept; 72 73 SchemefulSite& operator=(const SchemefulSite& other); 74 SchemefulSite& operator=(SchemefulSite&& other) noexcept; 75 76 // Tries to construct an instance from a (potentially untrusted) value of the 77 // internal `site_as_origin_` that got received over an RPC. 78 // 79 // Returns whether successful or not. Doesn't touch |*out| if false is 80 // returned. This returning |true| does not mean that whoever sent the values 81 // did not lie, merely that they are well-formed. 82 static bool FromWire(const url::Origin& site_as_origin, SchemefulSite* out); 83 84 // Creates a SchemefulSite iff the passed-in origin has a registerable domain. 85 static absl::optional<SchemefulSite> CreateIfHasRegisterableDomain( 86 const url::Origin&); 87 88 // If the scheme is ws or wss, it is converted to http or https, respectively. 89 // Has no effect on SchemefulSites with any other schemes. 90 // 91 // See Step 1 of algorithm "establish a WebSocket connection" in 92 // https://fetch.spec.whatwg.org/#websocket-opening-handshake. 93 void ConvertWebSocketToHttp(); 94 95 // Deserializes a string obtained from `Serialize()` to a `SchemefulSite`. 96 // Returns an opaque `SchemefulSite` if the value was invalid in any way. 97 static SchemefulSite Deserialize(const std::string& value); 98 99 // Returns a serialized version of `site_as_origin_`. If the underlying origin 100 // is invalid, returns an empty string. If serialization of opaque origins 101 // with their associated nonce is necessary, see `SerializeWithNonce()`. 102 std::string Serialize() const; 103 104 // Serializes `site_as_origin_` in cases when it has a 'file' scheme but 105 // we want to preserve the Origin's host. 106 // This was added to serialize cookie partition keys, which may contain 107 // file origins with a host. 108 std::string SerializeFileSiteWithHost() const; 109 110 std::string GetDebugString() const; 111 112 // Gets the underlying site as a GURL. If the internal Origin is opaque, 113 // returns an empty GURL. 114 GURL GetURL() const; 115 opaque()116 bool opaque() const { return site_as_origin_.opaque(); } 117 has_registrable_domain_or_host()118 bool has_registrable_domain_or_host() const { 119 return !registrable_domain_or_host().empty(); 120 } 121 122 // Testing only function which allows tests to access the underlying 123 // `site_as_origin_` in order to verify behavior. 124 const url::Origin& GetInternalOriginForTesting() const; 125 126 // Testing-only function which allows access to the private 127 // `registrable_domain_or_host` method. registrable_domain_or_host_for_testing()128 std::string registrable_domain_or_host_for_testing() const { 129 return registrable_domain_or_host(); 130 } 131 132 bool operator==(const SchemefulSite& other) const; 133 134 bool operator!=(const SchemefulSite& other) const; 135 136 bool operator<(const SchemefulSite& other) const; 137 138 private: 139 // IPC serialization code needs to access internal origin. 140 friend struct mojo::StructTraits<network::mojom::SchemefulSiteDataView, 141 SchemefulSite>; 142 friend struct IPC::ParamTraits<net::SchemefulSite>; 143 144 friend class blink::BlinkSchemefulSite; 145 146 // Create SiteForCookies from SchemefulSite needs to access internal origin, 147 // and SiteForCookies needs to access private method SchemelesslyEqual. 148 friend class SiteForCookies; 149 150 // Needed to serialize opaque and non-transient NetworkIsolationKeys, which 151 // use opaque origins. 152 friend class NetworkIsolationKey; 153 154 // Needed to serialize opaque and non-transient NetworkAnonymizationKeys, 155 // which use opaque origins. 156 friend class NetworkAnonymizationKey; 157 158 // Needed to create a bogus origin from a site. 159 // TODO(https://crbug.com/1148927): Give IsolationInfos empty origins instead, 160 // in this case, and unfriend IsolationInfo. 161 friend class IsolationInfo; 162 163 // Needed to create a bogus origin from a site. 164 friend class URLRequest; 165 166 // Needed because cookies do not account for scheme. 167 friend class CookieMonster; 168 169 // Needed for access to nonce for serialization. 170 friend class blink::StorageKey; 171 172 FRIEND_TEST_ALL_PREFIXES(SchemefulSiteTest, OpaqueSerialization); 173 FRIEND_TEST_ALL_PREFIXES(SchemefulSiteTest, InternalValue); 174 175 struct ObtainASiteResult { 176 url::Origin origin; 177 bool used_registerable_domain; 178 }; 179 180 static ObtainASiteResult ObtainASite(const url::Origin&); 181 182 explicit SchemefulSite(ObtainASiteResult); 183 184 // Deserializes a string obtained from `SerializeWithNonce()` to a 185 // `SchemefulSite`. Returns nullopt if the value was invalid in any way. 186 static absl::optional<SchemefulSite> DeserializeWithNonce( 187 const std::string& value); 188 189 // Returns a serialized version of `site_as_origin_`. For an opaque 190 // `site_as_origin_`, this serializes with the nonce. See 191 // `url::origin::SerializeWithNonce()` for usage information. 192 absl::optional<std::string> SerializeWithNonce(); 193 194 // Returns whether `this` and `other` share a host or registrable domain. 195 // Should NOT be used to check equality or equivalence. This is only used 196 // for legacy same-site cookie logic that does not check schemes. Private to 197 // restrict usage. 198 bool SchemelesslyEqual(const SchemefulSite& other) const; 199 200 // Returns the host of the underlying `origin`, which will usually be the 201 // registrable domain. This is private because if it were public, it would 202 // trivially allow circumvention of the "Schemeful"-ness of this class. 203 // However, the CookieMonster currently needs access to this, since it ignores 204 // the schemes of domains. 205 std::string registrable_domain_or_host() const { 206 return site_as_origin_.host(); 207 } 208 209 // This should not be used casually, it's an opaque Origin or an scheme+eTLD+1 210 // packed into an Origin. If you extract this value SchemefulSite is not 211 // responsible for any unexpected friction you might encounter. 212 const url::Origin& internal_value() const { return site_as_origin_; } 213 214 // Origin which stores the result of running the steps documented at 215 // https://html.spec.whatwg.org/multipage/origin.html#obtain-a-site. 216 // This is not an arbitrary origin. It must either be an opaque origin, or a 217 // scheme + eTLD+1 + default port. 218 // 219 // The `origin` passed into the SchemefulSite(const url::Origin&) constructor 220 // might not match this internal representation used by this class to track 221 // the scheme and eTLD+1 representing a schemeful site. This may be the case 222 // if, e.g., the passed `origin` has an eTLD+1 that is not equal to its 223 // hostname, or if the port number is not the default port for its scheme. 224 // 225 // In general, this `site_as_origin_` used for the internal representation 226 // should NOT be used directly by SchemefulSite consumers. 227 url::Origin site_as_origin_; 228 }; 229 230 // Provided to allow gtest to create more helpful error messages, instead of 231 // printing hex. 232 // 233 // Also used so that SchemefulSites can be the arguments of DCHECK_EQ. 234 NET_EXPORT std::ostream& operator<<(std::ostream& os, const SchemefulSite& ss); 235 236 } // namespace net 237 238 #endif // NET_BASE_SCHEMEFUL_SITE_H_ 239