diff options
Diffstat (limited to '')
-rw-r--r-- | dom/security/ReferrerInfo.h | 470 |
1 files changed, 470 insertions, 0 deletions
diff --git a/dom/security/ReferrerInfo.h b/dom/security/ReferrerInfo.h new file mode 100644 index 0000000000..78440a5a70 --- /dev/null +++ b/dom/security/ReferrerInfo.h @@ -0,0 +1,470 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef mozilla_dom_ReferrerInfo_h +#define mozilla_dom_ReferrerInfo_h + +#include "nsCOMPtr.h" +#include "nsIReferrerInfo.h" +#include "nsReadableUtils.h" +#include "mozilla/Maybe.h" +#include "mozilla/HashFunctions.h" +#include "mozilla/dom/ReferrerPolicyBinding.h" + +#define REFERRERINFOF_CONTRACTID "@mozilla.org/referrer-info;1" +// 041a129f-10ce-4bda-a60d-e027a26d5ed0 +#define REFERRERINFO_CID \ + { \ + 0x041a129f, 0x10ce, 0x4bda, { \ + 0xa6, 0x0d, 0xe0, 0x27, 0xa2, 0x6d, 0x5e, 0xd0 \ + } \ + } + +class nsIHttpChannel; +class nsIURI; +class nsIChannel; +class nsILoadInfo; +class nsINode; +class nsIPrincipal; + +namespace mozilla { +class StyleSheet; +class URLAndReferrerInfo; + +namespace net { +class HttpBaseChannel; +class nsHttpChannel; +} // namespace net +} // namespace mozilla + +namespace mozilla::dom { + +/** + * The ReferrerInfo class holds the raw referrer and potentially a referrer + * policy which allows to query the computed referrer which should be applied to + * a channel as the actual referrer value. + * + * The ReferrerInfo class solely contains readonly fields and represents a 1:1 + * sync to the referrer header of the corresponding channel. In turn that means + * the class is immutable - so any modifications require to clone the current + * ReferrerInfo. + * + * For example if a request undergoes a redirect, the new channel + * will need a new ReferrerInfo clone with members being updated accordingly. + */ + +class ReferrerInfo : public nsIReferrerInfo { + public: + typedef enum ReferrerPolicy ReferrerPolicyEnum; + ReferrerInfo(); + + explicit ReferrerInfo( + nsIURI* aOriginalReferrer, + ReferrerPolicyEnum aPolicy = ReferrerPolicy::_empty, + bool aSendReferrer = true, + const Maybe<nsCString>& aComputedReferrer = Maybe<nsCString>()); + + // Creates already initialized ReferrerInfo from an element or a document. + explicit ReferrerInfo(const Element&); + explicit ReferrerInfo(const Document&); + + // Creates already initialized ReferrerInfo from an element or a document with + // a specific referrer policy. + ReferrerInfo(const Element&, ReferrerPolicyEnum); + + // create an exact copy of the ReferrerInfo + already_AddRefed<ReferrerInfo> Clone() const; + + // create an copy of the ReferrerInfo with new referrer policy + already_AddRefed<ReferrerInfo> CloneWithNewPolicy( + ReferrerPolicyEnum aPolicy) const; + + // create an copy of the ReferrerInfo with new send referrer + already_AddRefed<ReferrerInfo> CloneWithNewSendReferrer( + bool aSendReferrer) const; + + // create an copy of the ReferrerInfo with new original referrer + already_AddRefed<ReferrerInfo> CloneWithNewOriginalReferrer( + nsIURI* aOriginalReferrer) const; + + // Record the telemetry for the referrer policy. + void RecordTelemetry(nsIHttpChannel* aChannel); + + /* + * Helper function to create a new ReferrerInfo object from a given document + * and override referrer policy if needed (for example, when parsing link + * header or speculative loading). + * + * @param aDocument the document to init referrerInfo object. + * @param aPolicyOverride referrer policy to override if necessary. + */ + static already_AddRefed<nsIReferrerInfo> CreateFromDocumentAndPolicyOverride( + Document* aDoc, ReferrerPolicyEnum aPolicyOverride); + + /* + * Implements step 3.1 and 3.3 of the Determine request's Referrer algorithm + * from the Referrer Policy specification. + * + * https://w3c.github.io/webappsec/specs/referrer-policy/#determine-requests-referrer + */ + static already_AddRefed<nsIReferrerInfo> CreateForFetch( + nsIPrincipal* aPrincipal, Document* aDoc); + + /** + * Helper function to create new ReferrerInfo object from a given external + * stylesheet. The returned nsIReferrerInfo object will be used for any + * requests or resources referenced by the sheet. + * + * @param aSheet the stylesheet to init referrerInfo. + * @param aPolicy referrer policy from header if there's any. + */ + static already_AddRefed<nsIReferrerInfo> CreateForExternalCSSResources( + StyleSheet* aExternalSheet, + ReferrerPolicyEnum aPolicy = ReferrerPolicy::_empty); + + /** + * Helper function to create new ReferrerInfo object from a given document. + * The returned nsIReferrerInfo object will be used for any requests or + * resources referenced by internal stylesheet (for example style="" or + * wrapped by <style> tag), as well as SVG resources. + * + * @param aDocument the document to init referrerInfo object. + */ + static already_AddRefed<nsIReferrerInfo> CreateForInternalCSSAndSVGResources( + Document* aDocument); + + /** + * Check whether the given referrer's scheme is allowed to be computed and + * sent. The allowlist schemes are: http, https. + */ + static bool IsReferrerSchemeAllowed(nsIURI* aReferrer); + + /* + * The Referrer Policy should be inherited for nested browsing contexts that + * are not created from responses. Such as: srcdoc, data, blob. + */ + static bool ShouldResponseInheritReferrerInfo(nsIChannel* aChannel); + + /* + * Check whether referrer is allowed to send in secure to insecure scenario. + */ + static nsresult HandleSecureToInsecureReferral(nsIURI* aOriginalURI, + nsIURI* aURI, + ReferrerPolicyEnum aPolicy, + bool& aAllowed); + + /** + * Returns true if the given channel is cross-origin request + * + * Computing whether the request is cross-origin may be expensive, so please + * do that in cases where we're going to use this information later on. + */ + static bool IsCrossOriginRequest(nsIHttpChannel* aChannel); + + /** + * Returns true if aReferrer's origin and aChannel's URI are cross-origin. + */ + static bool IsReferrerCrossOrigin(nsIHttpChannel* aChannel, + nsIURI* aReferrer); + + /** + * Returns true if the given channel is cross-site request. + */ + static bool IsCrossSiteRequest(nsIHttpChannel* aChannel); + + /** + * Returns true if the given channel is suppressed by Referrer-Policy header + * and should set "null" to Origin header. + */ + static bool ShouldSetNullOriginHeader(net::HttpBaseChannel* aChannel, + nsIURI* aOriginURI); + + /** + * Getter for network.http.sendRefererHeader. + */ + static uint32_t GetUserReferrerSendingPolicy(); + + /** + * Getter for network.http.referer.XOriginPolicy. + */ + static uint32_t GetUserXOriginSendingPolicy(); + + /** + * Getter for network.http.referer.trimmingPolicy. + */ + static uint32_t GetUserTrimmingPolicy(); + + /** + * Getter for network.http.referer.XOriginTrimmingPolicy. + */ + static uint32_t GetUserXOriginTrimmingPolicy(); + + /** + * Return default referrer policy which is controlled by user + * prefs: + * network.http.referer.defaultPolicy for regular mode + * network.http.referer.defaultPolicy.trackers for third-party trackers + * in regular mode + * network.http.referer.defaultPolicy.pbmode for private mode + * network.http.referer.defaultPolicy.trackers.pbmode for third-party trackers + * in private mode + */ + static ReferrerPolicyEnum GetDefaultReferrerPolicy( + nsIHttpChannel* aChannel = nullptr, nsIURI* aURI = nullptr, + bool aPrivateBrowsing = false); + + /** + * Return default referrer policy for third party which is controlled by user + * prefs: + * network.http.referer.defaultPolicy.trackers for regular mode + * network.http.referer.defaultPolicy.trackers.pbmode for private mode + */ + static ReferrerPolicyEnum GetDefaultThirdPartyReferrerPolicy( + bool aPrivateBrowsing = false); + + /* + * Helper function to parse ReferrerPolicy from meta tag referrer content. + * For example: <meta name="referrer" content="origin"> + * + * @param aContent content string to be transformed into ReferrerPolicyEnum, + * e.g. "origin". + */ + static ReferrerPolicyEnum ReferrerPolicyFromMetaString( + const nsAString& aContent); + + /* + * Helper function to parse ReferrerPolicy from string content of + * referrerpolicy attribute. + * For example: <a href="http://example.com" referrerpolicy="no-referrer"> + * + * @param aContent content string to be transformed into ReferrerPolicyEnum, + * e.g. "no-referrer". + */ + static ReferrerPolicyEnum ReferrerPolicyAttributeFromString( + const nsAString& aContent); + + /* + * Helper function to parse ReferrerPolicy from string content of + * Referrer-Policy header. + * For example: Referrer-Policy: origin no-referrer + * https://www.w3.org/tr/referrer-policy/#parse-referrer-policy-from-header + * + * @param aContent content string to be transformed into ReferrerPolicyEnum. + * e.g. "origin no-referrer" + */ + static ReferrerPolicyEnum ReferrerPolicyFromHeaderString( + const nsAString& aContent); + + /* + * Helper function to convert ReferrerPolicy enum to string + * + * @param aPolicy referrer policy to convert. + */ + static const char* ReferrerPolicyToString(ReferrerPolicyEnum aPolicy); + + /** + * Hash function for this object + */ + HashNumber Hash() const; + + NS_DECL_THREADSAFE_ISUPPORTS + NS_DECL_NSIREFERRERINFO + NS_DECL_NSISERIALIZABLE + + private: + virtual ~ReferrerInfo() = default; + + ReferrerInfo(const ReferrerInfo& rhs); + + /* + * Trimming policy when compute referrer, indicate how much information in the + * referrer will be sent. Order matters here. + */ + enum TrimmingPolicy : uint32_t { + ePolicyFullURI = 0, + ePolicySchemeHostPortPath = 1, + ePolicySchemeHostPort = 2, + }; + + /* + * Referrer sending policy, indicates type of action could trigger to send + * referrer header, not send at all, send only with user's action (click on a + * link) or send even with inline content request (image request). + * Order matters here. + */ + enum ReferrerSendingPolicy : uint32_t { + ePolicyNotSend = 0, + ePolicySendWhenUserTrigger = 1, + ePolicySendInlineContent = 2, + }; + + /* + * Sending referrer when cross origin policy, indicates when referrer should + * be send when compare 2 origins. Order matters here. + */ + enum XOriginSendingPolicy : uint32_t { + ePolicyAlwaysSend = 0, + ePolicySendWhenSameDomain = 1, + ePolicySendWhenSameHost = 2, + }; + + /* + * Handle user controlled pref network.http.referer.XOriginPolicy + */ + nsresult HandleUserXOriginSendingPolicy(nsIURI* aURI, nsIURI* aReferrer, + bool& aAllowed) const; + + /* + * Handle user controlled pref network.http.sendRefererHeader + */ + nsresult HandleUserReferrerSendingPolicy(nsIHttpChannel* aChannel, + bool& aAllowed) const; + + /* + * Compute trimming policy from user controlled prefs. + * This function is called when we already made sure a nonempty referrer is + * allowed to send. + */ + TrimmingPolicy ComputeTrimmingPolicy(nsIHttpChannel* aChannel, + nsIURI* aReferrer) const; + + // HttpBaseChannel could access IsInitialized() and ComputeReferrer(); + friend class mozilla::net::HttpBaseChannel; + + /* + * Compute referrer for a given channel. The computation result then will be + * stored in this class and then used to set the actual referrer header of + * the channel. The computation could be controlled by several user prefs + * which are defined in StaticPrefList.yaml (see StaticPrefList.yaml for more + * details): + * network.http.sendRefererHeader + * network.http.referer.spoofSource + * network.http.referer.hideOnionSource + * network.http.referer.XOriginPolicy + * network.http.referer.trimmingPolicy + * network.http.referer.XOriginTrimmingPolicy + */ + nsresult ComputeReferrer(nsIHttpChannel* aChannel); + + /* + * Check whether the ReferrerInfo has been initialized or not. + */ + bool IsInitialized() { return mInitialized; } + + // nsHttpChannel, Document could access IsPolicyOverrided(); + friend class mozilla::net::nsHttpChannel; + friend class mozilla::dom::Document; + /* + * Check whether if unset referrer policy is overrided by default or not + */ + bool IsPolicyOverrided() { return mOverridePolicyByDefault; } + + /* + * Get origin string from a given valid referrer URI (http, https) + * + * @aReferrer - the full referrer URI + * @aResult - the resulting aReferrer in string format. + */ + nsresult GetOriginFromReferrerURI(nsIURI* aReferrer, + nsACString& aResult) const; + + /* + * Trim a given referrer with a given a trimming policy, + */ + nsresult TrimReferrerWithPolicy(nsIURI* aReferrer, + TrimmingPolicy aTrimmingPolicy, + nsACString& aResult) const; + + /** + * Returns true if we should ignore less restricted referrer policies, + * including 'unsafe_url', 'no_referrer_when_downgrade' and + * 'origin_when_cross_origin', for the given channel. We only apply this + * restriction for cross-site requests. For the same-site request, we will + * still allow overriding the default referrer policy with less restricted + * one. + * + * Note that the channel triggered by the system and the extension will be + * exempt from this restriction. + */ + bool ShouldIgnoreLessRestrictedPolicies( + nsIHttpChannel* aChannel, const ReferrerPolicyEnum aPolicy) const; + + /* + * Limit referrer length using the following ruleset: + * - If the length of referrer URL is over max length, strip down to origin. + * - If the origin is still over max length, remove the referrer entirely. + * + * This function comlements TrimReferrerPolicy and needs to be called right + * after TrimReferrerPolicy. + * + * @aChannel - used to query information needed for logging to the console. + * @aReferrer - the full referrer URI; needs to be identical to aReferrer + * passed to TrimReferrerPolicy. + * @aTrimmingPolicy - represents the trimming policy which was applied to the + * referrer; needs to be identical to aTrimmingPolicy + * passed to TrimReferrerPolicy. + * @aInAndOutTrimmedReferrer - an in and outgoing argument representing the + * referrer value. Please pass the result of + * TrimReferrerWithPolicy as + * aInAndOutTrimmedReferrer which will then be + * reduced to the origin or completely truncated + * in case the referrer value exceeds the length + * limitation. + */ + nsresult LimitReferrerLength(nsIHttpChannel* aChannel, nsIURI* aReferrer, + TrimmingPolicy aTrimmingPolicy, + nsACString& aInAndOutTrimmedReferrer) const; + + /** + * The helper function to read the old data format before gecko 100 for + * deserialization. + */ + nsresult ReadTailDataBeforeGecko100(const uint32_t& aData, + nsIObjectInputStream* aInputStream); + + /* + * Write message to the error console + */ + void LogMessageToConsole(nsIHttpChannel* aChannel, const char* aMsg, + const nsTArray<nsString>& aParams) const; + + friend class mozilla::URLAndReferrerInfo; + + nsCOMPtr<nsIURI> mOriginalReferrer; + + ReferrerPolicyEnum mPolicy; + + // The referrer policy that has been set originally for the channel. Note that + // the policy may have been overridden by the default referrer policy, so we + // need to keep track of this if we need to recover the original referrer + // policy. + ReferrerPolicyEnum mOriginalPolicy; + + // Indicates if the referrer should be sent or not even when it's available + // (default is true). + bool mSendReferrer; + + // Since the ReferrerInfo is immutable, we use this member as a helper to + // ensure no one can call e.g. init() twice to modify state of the + // ReferrerInfo. + bool mInitialized; + + // Indicates if unset referrer policy is overrided by default + bool mOverridePolicyByDefault; + + // Store a computed referrer for a given channel + Maybe<nsCString> mComputedReferrer; + +#ifdef DEBUG + // Indicates if the telemetry has been recorded. This is used to make sure the + // telemetry will be only recored once. + bool mTelemetryRecorded = false; +#endif // DEBUG +}; + +} // namespace mozilla::dom + +#endif // mozilla_dom_ReferrerInfo_h |