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