1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ 2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */ 3 /* This Source Code Form is subject to the terms of the Mozilla Public 4 * License, v. 2.0. If a copy of the MPL was not distributed with this 5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 6 7 #ifndef mozilla_dom_ReferrerInfo_h 8 #define mozilla_dom_ReferrerInfo_h 9 10 #include "nsCOMPtr.h" 11 #include "nsIReferrerInfo.h" 12 #include "nsReadableUtils.h" 13 #include "mozilla/Maybe.h" 14 #include "mozilla/HashFunctions.h" 15 #include "mozilla/dom/ReferrerPolicyBinding.h" 16 17 #define REFERRERINFOF_CONTRACTID "@mozilla.org/referrer-info;1" 18 // 041a129f-10ce-4bda-a60d-e027a26d5ed0 19 #define REFERRERINFO_CID \ 20 { \ 21 0x041a129f, 0x10ce, 0x4bda, { \ 22 0xa6, 0x0d, 0xe0, 0x27, 0xa2, 0x6d, 0x5e, 0xd0 \ 23 } \ 24 } 25 26 class nsIHttpChannel; 27 class nsIURI; 28 class nsIChannel; 29 class nsILoadInfo; 30 class nsINode; 31 class nsIPrincipal; 32 33 namespace mozilla { 34 class StyleSheet; 35 class URLAndReferrerInfo; 36 37 namespace net { 38 class HttpBaseChannel; 39 class nsHttpChannel; 40 } // namespace net 41 } // namespace mozilla 42 43 using mozilla::Maybe; 44 45 namespace mozilla { 46 namespace dom { 47 48 /** 49 * The ReferrerInfo class holds the raw referrer and potentially a referrer 50 * policy which allows to query the computed referrer which should be applied to 51 * a channel as the actual referrer value. 52 * 53 * The ReferrerInfo class solely contains readonly fields and represents a 1:1 54 * sync to the referrer header of the corresponding channel. In turn that means 55 * the class is immutable - so any modifications require to clone the current 56 * ReferrerInfo. 57 * 58 * For example if a request undergoes a redirect, the new channel 59 * will need a new ReferrerInfo clone with members being updated accordingly. 60 */ 61 62 class ReferrerInfo : public nsIReferrerInfo { 63 public: 64 typedef enum ReferrerPolicy ReferrerPolicyEnum; 65 ReferrerInfo(); 66 67 explicit ReferrerInfo( 68 nsIURI* aOriginalReferrer, 69 ReferrerPolicyEnum aPolicy = ReferrerPolicy::_empty, 70 bool aSendReferrer = true, 71 const Maybe<nsCString>& aComputedReferrer = Maybe<nsCString>()); 72 73 // Creates already initialized ReferrerInfo from an element or a document. 74 explicit ReferrerInfo(const Element&); 75 explicit ReferrerInfo(const Document&); 76 77 // create an exact copy of the ReferrerInfo 78 already_AddRefed<ReferrerInfo> Clone() const; 79 80 // create an copy of the ReferrerInfo with new referrer policy 81 already_AddRefed<ReferrerInfo> CloneWithNewPolicy( 82 ReferrerPolicyEnum aPolicy) const; 83 84 // create an copy of the ReferrerInfo with new send referrer 85 already_AddRefed<ReferrerInfo> CloneWithNewSendReferrer( 86 bool aSendReferrer) const; 87 88 // create an copy of the ReferrerInfo with new original referrer 89 already_AddRefed<ReferrerInfo> CloneWithNewOriginalReferrer( 90 nsIURI* aOriginalReferrer) const; 91 92 // Record the telemetry for the referrer policy. 93 void RecordTelemetry(nsIHttpChannel* aChannel); 94 95 /* 96 * Helper function to create a new ReferrerInfo object from other. We will not 97 * pass in any computed values and override referrer policy if needed 98 * 99 * @param aOther the other referrerInfo object to init from. 100 * @param aPolicyOverride referrer policy to override if necessary. 101 */ 102 static already_AddRefed<nsIReferrerInfo> CreateFromOtherAndPolicyOverride( 103 nsIReferrerInfo* aOther, ReferrerPolicyEnum aPolicyOverride); 104 105 /* 106 * Helper function to create a new ReferrerInfo object from a given document 107 * and override referrer policy if needed (for example, when parsing link 108 * header or speculative loading). 109 * 110 * @param aDocument the document to init referrerInfo object. 111 * @param aPolicyOverride referrer policy to override if necessary. 112 */ 113 static already_AddRefed<nsIReferrerInfo> CreateFromDocumentAndPolicyOverride( 114 Document* aDoc, ReferrerPolicyEnum aPolicyOverride); 115 116 /* 117 * Implements step 3.1 and 3.3 of the Determine request's Referrer algorithm 118 * from the Referrer Policy specification. 119 * 120 * https://w3c.github.io/webappsec/specs/referrer-policy/#determine-requests-referrer 121 */ 122 static already_AddRefed<nsIReferrerInfo> CreateForFetch( 123 nsIPrincipal* aPrincipal, Document* aDoc); 124 125 /** 126 * Helper function to create new ReferrerInfo object from a given external 127 * stylesheet. The returned nsIReferrerInfo object will be used for any 128 * requests or resources referenced by the sheet. 129 * 130 * @param aSheet the stylesheet to init referrerInfo. 131 * @param aPolicy referrer policy from header if there's any. 132 */ 133 static already_AddRefed<nsIReferrerInfo> CreateForExternalCSSResources( 134 StyleSheet* aExternalSheet, 135 ReferrerPolicyEnum aPolicy = ReferrerPolicy::_empty); 136 137 /** 138 * Helper function to create new ReferrerInfo object from a given document. 139 * The returned nsIReferrerInfo object will be used for any requests or 140 * resources referenced by internal stylesheet (for example style="" or 141 * wrapped by <style> tag). 142 * 143 * @param aDocument the document to init referrerInfo object. 144 */ 145 static already_AddRefed<nsIReferrerInfo> CreateForInternalCSSResources( 146 Document* aDocument); 147 148 /** 149 * Helper function to create new ReferrerInfo object from a given document. 150 * The returned nsIReferrerInfo object will be used for any requests or 151 * resources referenced by SVG. 152 * 153 * @param aDocument the document to init referrerInfo object. 154 */ 155 static already_AddRefed<nsIReferrerInfo> CreateForSVGResources( 156 Document* aDocument); 157 158 /** 159 * Check whether the given referrer's scheme is allowed to be computed and 160 * sent. The allowlist schemes are: http, https. 161 */ 162 static bool IsReferrerSchemeAllowed(nsIURI* aReferrer); 163 164 /* 165 * The Referrer Policy should be inherited for nested browsing contexts that 166 * are not created from responses. Such as: srcdoc, data, blob. 167 */ 168 static bool ShouldResponseInheritReferrerInfo(nsIChannel* aChannel); 169 170 /* 171 * Check whether referrer is allowed to send in secure to insecure scenario. 172 */ 173 static nsresult HandleSecureToInsecureReferral(nsIURI* aOriginalURI, 174 nsIURI* aURI, 175 ReferrerPolicyEnum aPolicy, 176 bool& aAllowed); 177 178 /** 179 * Returns true if the given channel is cross-origin request 180 * 181 * Computing whether the request is cross-origin may be expensive, so please 182 * do that in cases where we're going to use this information later on. 183 */ 184 static bool IsCrossOriginRequest(nsIHttpChannel* aChannel); 185 186 /** 187 * Returns true if the given channel is cross-site request. 188 */ 189 static bool IsCrossSiteRequest(nsIHttpChannel* aChannel); 190 191 /** 192 * Returns true if the given channel is suppressed by Referrer-Policy header 193 * and should set "null" to Origin header. 194 */ 195 static bool ShouldSetNullOriginHeader(net::HttpBaseChannel* aChannel, 196 nsIURI* aOriginURI); 197 198 /** 199 * Getter for network.http.sendRefererHeader. 200 */ 201 static uint32_t GetUserReferrerSendingPolicy(); 202 203 /** 204 * Getter for network.http.referer.XOriginPolicy. 205 */ 206 static uint32_t GetUserXOriginSendingPolicy(); 207 208 /** 209 * Getter for network.http.referer.trimmingPolicy. 210 */ 211 static uint32_t GetUserTrimmingPolicy(); 212 213 /** 214 * Getter for network.http.referer.XOriginTrimmingPolicy. 215 */ 216 static uint32_t GetUserXOriginTrimmingPolicy(); 217 218 /** 219 * Return default referrer policy which is controlled by user 220 * prefs: 221 * network.http.referer.defaultPolicy for regular mode 222 * network.http.referer.defaultPolicy.trackers for third-party trackers 223 * in regular mode 224 * network.http.referer.defaultPolicy.pbmode for private mode 225 * network.http.referer.defaultPolicy.trackers.pbmode for third-party trackers 226 * in private mode 227 */ 228 static ReferrerPolicyEnum GetDefaultReferrerPolicy( 229 nsIHttpChannel* aChannel = nullptr, nsIURI* aURI = nullptr, 230 bool privateBrowsing = false); 231 232 /* 233 * Helper function to parse ReferrerPolicy from meta tag referrer content. 234 * For example: <meta name="referrer" content="origin"> 235 * 236 * @param aContent content string to be transformed into ReferrerPolicyEnum, 237 * e.g. "origin". 238 */ 239 static ReferrerPolicyEnum ReferrerPolicyFromMetaString( 240 const nsAString& aContent); 241 242 /* 243 * Helper function to parse ReferrerPolicy from string content of 244 * referrerpolicy attribute. 245 * For example: <a href="http://example.com" referrerpolicy="no-referrer"> 246 * 247 * @param aContent content string to be transformed into ReferrerPolicyEnum, 248 * e.g. "no-referrer". 249 */ 250 static ReferrerPolicyEnum ReferrerPolicyAttributeFromString( 251 const nsAString& aContent); 252 253 /* 254 * Helper function to parse ReferrerPolicy from string content of 255 * Referrer-Policy header. 256 * For example: Referrer-Policy: origin no-referrer 257 * https://www.w3.org/tr/referrer-policy/#parse-referrer-policy-from-header 258 * 259 * @param aContent content string to be transformed into ReferrerPolicyEnum. 260 * e.g. "origin no-referrer" 261 */ 262 static ReferrerPolicyEnum ReferrerPolicyFromHeaderString( 263 const nsAString& aContent); 264 265 /* 266 * Helper function to convert ReferrerPolicy enum to string 267 * 268 * @param aPolicy referrer policy to convert. 269 */ 270 static const char* ReferrerPolicyToString(ReferrerPolicyEnum aPolicy); 271 272 /** 273 * Hash function for this object 274 */ 275 HashNumber Hash() const; 276 277 NS_DECL_THREADSAFE_ISUPPORTS 278 NS_DECL_NSIREFERRERINFO 279 NS_DECL_NSISERIALIZABLE 280 281 private: 282 virtual ~ReferrerInfo() = default; 283 284 ReferrerInfo(const ReferrerInfo& rhs); 285 286 /* 287 * Default referrer policy to use 288 */ 289 enum DefaultReferrerPolicy : uint32_t { 290 eDefaultPolicyNoReferrer = 0, 291 eDefaultPolicySameOrgin = 1, 292 eDefaultPolicyStrictWhenXorigin = 2, 293 eDefaultPolicyNoReferrerWhenDownGrade = 3, 294 }; 295 296 /* 297 * Trimming policy when compute referrer, indicate how much information in the 298 * referrer will be sent. Order matters here. 299 */ 300 enum TrimmingPolicy : uint32_t { 301 ePolicyFullURI = 0, 302 ePolicySchemeHostPortPath = 1, 303 ePolicySchemeHostPort = 2, 304 }; 305 306 /* 307 * Referrer sending policy, indicates type of action could trigger to send 308 * referrer header, not send at all, send only with user's action (click on a 309 * link) or send even with inline content request (image request). 310 * Order matters here. 311 */ 312 enum ReferrerSendingPolicy : uint32_t { 313 ePolicyNotSend = 0, 314 ePolicySendWhenUserTrigger = 1, 315 ePolicySendInlineContent = 2, 316 }; 317 318 /* 319 * Sending referrer when cross origin policy, indicates when referrer should 320 * be send when compare 2 origins. Order matters here. 321 */ 322 enum XOriginSendingPolicy : uint32_t { 323 ePolicyAlwaysSend = 0, 324 ePolicySendWhenSameDomain = 1, 325 ePolicySendWhenSameHost = 2, 326 }; 327 328 /* 329 * Handle user controlled pref network.http.referer.XOriginPolicy 330 */ 331 nsresult HandleUserXOriginSendingPolicy(nsIURI* aURI, nsIURI* aReferrer, 332 bool& aAllowed) const; 333 334 /* 335 * Handle user controlled pref network.http.sendRefererHeader 336 */ 337 nsresult HandleUserReferrerSendingPolicy(nsIHttpChannel* aChannel, 338 bool& aAllowed) const; 339 340 /* 341 * Compute trimming policy from user controlled prefs. 342 * This function is called when we already made sure a nonempty referrer is 343 * allowed to send. 344 */ 345 TrimmingPolicy ComputeTrimmingPolicy(nsIHttpChannel* aChannel) const; 346 347 // HttpBaseChannel could access IsInitialized() and ComputeReferrer(); 348 friend class mozilla::net::HttpBaseChannel; 349 350 /* 351 * Compute referrer for a given channel. The computation result then will be 352 * stored in this class and then used to set the actual referrer header of 353 * the channel. The computation could be controlled by several user prefs 354 * which are defined in StaticPrefList.yaml (see StaticPrefList.yaml for more 355 * details): 356 * network.http.sendRefererHeader 357 * network.http.referer.spoofSource 358 * network.http.referer.hideOnionSource 359 * network.http.referer.XOriginPolicy 360 * network.http.referer.trimmingPolicy 361 * network.http.referer.XOriginTrimmingPolicy 362 */ 363 nsresult ComputeReferrer(nsIHttpChannel* aChannel); 364 365 /* 366 * Check whether the ReferrerInfo has been initialized or not. 367 */ IsInitialized()368 bool IsInitialized() { return mInitialized; } 369 370 // nsHttpChannel, Document could access IsPolicyOverrided(); 371 friend class mozilla::net::nsHttpChannel; 372 friend class mozilla::dom::Document; 373 /* 374 * Check whether if unset referrer policy is overrided by default or not 375 */ IsPolicyOverrided()376 bool IsPolicyOverrided() { return mOverridePolicyByDefault; } 377 378 /* 379 * Get origin string from a given valid referrer URI (http, https) 380 * 381 * @aReferrer - the full referrer URI 382 * @aResult - the resulting aReferrer in string format. 383 */ 384 nsresult GetOriginFromReferrerURI(nsIURI* aReferrer, 385 nsACString& aResult) const; 386 387 /* 388 * Trim a given referrer with a given a trimming policy, 389 */ 390 nsresult TrimReferrerWithPolicy(nsIURI* aReferrer, 391 TrimmingPolicy aTrimmingPolicy, 392 nsACString& aResult) const; 393 394 /** 395 * Returns true if we should ignore less restricted referrer policies, 396 * including 'unsafe_url', 'no_referrer_when_downgrade' and 397 * 'origin_when_cross_origin', for the given channel. We only apply this 398 * restriction for cross-site requests. For the same-site request, we will 399 * still allow overriding the default referrer policy with less restricted 400 * one. 401 * 402 * Note that the channel triggered by the system and the extension will be 403 * exempt from this restriction. 404 */ 405 bool ShouldIgnoreLessRestrictedPolicies( 406 nsIHttpChannel* aChannel, const ReferrerPolicyEnum aPolicy) const; 407 408 /* 409 * Limit referrer length using the following ruleset: 410 * - If the length of referrer URL is over max length, strip down to origin. 411 * - If the origin is still over max length, remove the referrer entirely. 412 * 413 * This function comlements TrimReferrerPolicy and needs to be called right 414 * after TrimReferrerPolicy. 415 * 416 * @aChannel - used to query information needed for logging to the console. 417 * @aReferrer - the full referrer URI; needs to be identical to aReferrer 418 * passed to TrimReferrerPolicy. 419 * @aTrimmingPolicy - represents the trimming policy which was applied to the 420 * referrer; needs to be identical to aTrimmingPolicy 421 * passed to TrimReferrerPolicy. 422 * @aInAndOutTrimmedReferrer - an in and outgoing argument representing the 423 * referrer value. Please pass the result of 424 * TrimReferrerWithPolicy as 425 * aInAndOutTrimmedReferrer which will then be 426 * reduced to the origin or completely truncated 427 * in case the referrer value exceeds the length 428 * limitation. 429 */ 430 nsresult LimitReferrerLength(nsIHttpChannel* aChannel, nsIURI* aReferrer, 431 TrimmingPolicy aTrimmingPolicy, 432 nsACString& aInAndOutTrimmedReferrer) const; 433 434 /* 435 * Write message to the error console 436 */ 437 void LogMessageToConsole(nsIHttpChannel* aChannel, const char* aMsg, 438 const nsTArray<nsString>& aParams) const; 439 440 friend class mozilla::URLAndReferrerInfo; 441 442 nsCOMPtr<nsIURI> mOriginalReferrer; 443 444 ReferrerPolicyEnum mPolicy; 445 446 // Indicates if the referrer should be sent or not even when it's available 447 // (default is true). 448 bool mSendReferrer; 449 450 // Since the ReferrerInfo is immutable, we use this member as a helper to 451 // ensure no one can call e.g. init() twice to modify state of the 452 // ReferrerInfo. 453 bool mInitialized; 454 455 // Indicates if unset referrer policy is overrided by default 456 bool mOverridePolicyByDefault; 457 458 // Store a computed referrer for a given channel 459 Maybe<nsCString> mComputedReferrer; 460 461 #ifdef DEBUG 462 // Indicates if the telemetry has been recorded. This is used to make sure the 463 // telemetry will be only recored once. 464 bool mTelemetryRecorded = false; 465 #endif // DEBUG 466 }; 467 468 } // namespace dom 469 } // namespace mozilla 470 471 #endif // mozilla_dom_ReferrerInfo_h 472