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 "nsIHttpChannel.h"
13 #include "nsReadableUtils.h"
14 #include "mozilla/Maybe.h"
15 #include "mozilla/HashFunctions.h"
16 #include "mozilla/dom/ReferrerPolicyBinding.h"
17 
18 #define REFERRERINFOF_CONTRACTID "@mozilla.org/referrer-info;1"
19 // 041a129f-10ce-4bda-a60d-e027a26d5ed0
20 #define REFERRERINFO_CID                             \
21   {                                                  \
22     0x041a129f, 0x10ce, 0x4bda, {                    \
23       0xa6, 0x0d, 0xe0, 0x27, 0xa2, 0x6d, 0x5e, 0xd0 \
24     }                                                \
25   }
26 
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   /*
93    * Helper function to create a new ReferrerInfo object from other. We will not
94    * pass in any computed values and override referrer policy if needed
95    *
96    * @param aOther the other referrerInfo object to init from.
97    * @param aPolicyOverride referrer policy to override if necessary.
98    */
99   static already_AddRefed<nsIReferrerInfo> CreateFromOtherAndPolicyOverride(
100       nsIReferrerInfo* aOther, ReferrerPolicyEnum aPolicyOverride);
101 
102   /*
103    * Helper function to create a new ReferrerInfo object from a given document
104    * and override referrer policy if needed (for example, when parsing link
105    * header or speculative loading).
106    *
107    * @param aDocument the document to init referrerInfo object.
108    * @param aPolicyOverride referrer policy to override if necessary.
109    */
110   static already_AddRefed<nsIReferrerInfo> CreateFromDocumentAndPolicyOverride(
111       Document* aDoc, ReferrerPolicyEnum aPolicyOverride);
112 
113   /*
114    * Implements step 3.1 and 3.3 of the Determine request's Referrer algorithm
115    * from the Referrer Policy specification.
116    *
117    * https://w3c.github.io/webappsec/specs/referrer-policy/#determine-requests-referrer
118    */
119   static already_AddRefed<nsIReferrerInfo> CreateForFetch(
120       nsIPrincipal* aPrincipal, Document* aDoc);
121 
122   /**
123    * Helper function to create new ReferrerInfo object from a given external
124    * stylesheet. The returned nsIReferrerInfo object will be used for any
125    * requests or resources referenced by the sheet.
126    *
127    * @param aSheet the stylesheet to init referrerInfo.
128    * @param aPolicy referrer policy from header if there's any.
129    */
130   static already_AddRefed<nsIReferrerInfo> CreateForExternalCSSResources(
131       StyleSheet* aExternalSheet,
132       ReferrerPolicyEnum aPolicy = ReferrerPolicy::_empty);
133 
134   /**
135    * Helper function to create new ReferrerInfo object from a given document.
136    * The returned nsIReferrerInfo object will be used for any requests or
137    * resources referenced by internal stylesheet (for example style="" or
138    * wrapped by <style> tag).
139    *
140    * @param aDocument the document to init referrerInfo object.
141    */
142   static already_AddRefed<nsIReferrerInfo> CreateForInternalCSSResources(
143       Document* aDocument);
144 
145   /**
146    * Helper function to create new ReferrerInfo object from a given document.
147    * The returned nsIReferrerInfo object will be used for any requests or
148    * resources referenced by SVG.
149    *
150    * @param aDocument the document to init referrerInfo object.
151    */
152   static already_AddRefed<nsIReferrerInfo> CreateForSVGResources(
153       Document* aDocument);
154 
155   /**
156    * Check whether the given referrer's scheme is allowed to be computed and
157    * sent. The whitelist schemes are: http, https, ftp.
158    */
159   static bool IsReferrerSchemeAllowed(nsIURI* aReferrer);
160 
161   /*
162    * The Referrer Policy should be inherited for nested browsing contexts that
163    * are not created from responses. Such as: srcdoc, data, blob.
164    */
165   static bool ShouldResponseInheritReferrerInfo(nsIChannel* aChannel);
166 
167   /*
168    * Check whether referrer is allowed to send in secure to insecure scenario.
169    */
170   static nsresult HandleSecureToInsecureReferral(nsIURI* aOriginalURI,
171                                                  nsIURI* aURI,
172                                                  ReferrerPolicyEnum aPolicy,
173                                                  bool& aAllowed);
174 
175   /**
176    * Returns true if the given channel is cross-origin request
177    *
178    * Computing whether the request is cross-origin may be expensive, so please
179    * do that in cases where we're going to use this information later on.
180    */
181   static bool IsCrossOriginRequest(nsIHttpChannel* aChannel);
182 
183   /**
184    * Returns true if the given channel is suppressed by Referrer-Policy header
185    * and should set "null" to Origin header.
186    */
187   static bool ShouldSetNullOriginHeader(net::HttpBaseChannel* aChannel,
188                                         nsIURI* aOriginURI);
189 
190   /**
191    * Getter for network.http.sendRefererHeader.
192    */
193   static uint32_t GetUserReferrerSendingPolicy();
194 
195   /**
196    * Getter for network.http.referer.XOriginPolicy.
197    */
198   static uint32_t GetUserXOriginSendingPolicy();
199 
200   /**
201    * Getter for network.http.referer.trimmingPolicy.
202    */
203   static uint32_t GetUserTrimmingPolicy();
204 
205   /**
206    * Getter for network.http.referer.XOriginTrimmingPolicy.
207    */
208   static uint32_t GetUserXOriginTrimmingPolicy();
209 
210   /**
211    * Return default referrer policy which is controlled by user
212    * prefs:
213    * network.http.referer.defaultPolicy for regular mode
214    * network.http.referer.defaultPolicy.trackers for third-party trackers
215    * in regular mode
216    * network.http.referer.defaultPolicy.pbmode for private mode
217    * network.http.referer.defaultPolicy.trackers.pbmode for third-party trackers
218    * in private mode
219    */
220   static ReferrerPolicyEnum GetDefaultReferrerPolicy(
221       nsIHttpChannel* aChannel = nullptr, nsIURI* aURI = nullptr,
222       bool privateBrowsing = false);
223 
224   /*
225    * Helper function to parse ReferrerPolicy from meta tag referrer content.
226    * For example: <meta name="referrer" content="origin">
227    *
228    * @param aContent content string to be transformed into ReferrerPolicyEnum,
229    *                 e.g. "origin".
230    */
231   static ReferrerPolicyEnum ReferrerPolicyFromMetaString(
232       const nsAString& aContent);
233 
234   /*
235    * Helper function to parse ReferrerPolicy from string content of
236    * referrerpolicy attribute.
237    * For example: <a href="http://example.com" referrerpolicy="no-referrer">
238    *
239    * @param aContent content string to be transformed into ReferrerPolicyEnum,
240    *                 e.g. "no-referrer".
241    */
242   static ReferrerPolicyEnum ReferrerPolicyAttributeFromString(
243       const nsAString& aContent);
244 
245   /*
246    * Helper function to parse ReferrerPolicy from string content of
247    * Referrer-Policy header.
248    * For example: Referrer-Policy: origin no-referrer
249    * https://www.w3.org/tr/referrer-policy/#parse-referrer-policy-from-header
250    *
251    * @param aContent content string to be transformed into ReferrerPolicyEnum.
252    *                e.g. "origin no-referrer"
253    */
254   static ReferrerPolicyEnum ReferrerPolicyFromHeaderString(
255       const nsAString& aContent);
256 
257   /*
258    * Helper function to convert ReferrerPolicy enum to string
259    *
260    * @param aPolicy referrer policy to convert.
261    */
262   static const char* ReferrerPolicyToString(ReferrerPolicyEnum aPolicy);
263 
264   /**
265    * Hash function for this object
266    */
267   HashNumber Hash() const;
268 
269   NS_DECL_THREADSAFE_ISUPPORTS
270   NS_DECL_NSIREFERRERINFO
271   NS_DECL_NSISERIALIZABLE
272 
273  private:
274   virtual ~ReferrerInfo() = default;
275 
276   ReferrerInfo(const ReferrerInfo& rhs);
277 
278   /*
279    * Default referrer policy to use
280    */
281   enum DefaultReferrerPolicy : uint32_t {
282     eDefaultPolicyNoReferrer = 0,
283     eDefaultPolicySameOrgin = 1,
284     eDefaultPolicyStrictWhenXorigin = 2,
285     eDefaultPolicyNoReferrerWhenDownGrade = 3,
286   };
287 
288   /*
289    * Trimming policy when compute referrer, indicate how much information in the
290    * referrer will be sent. Order matters here.
291    */
292   enum TrimmingPolicy : uint32_t {
293     ePolicyFullURI = 0,
294     ePolicySchemeHostPortPath = 1,
295     ePolicySchemeHostPort = 2,
296   };
297 
298   /*
299    * Referrer sending policy, indicates type of action could trigger to send
300    * referrer header, not send at all, send only with user's action (click on a
301    * link) or send even with inline content request (image request).
302    * Order matters here.
303    */
304   enum ReferrerSendingPolicy : uint32_t {
305     ePolicyNotSend = 0,
306     ePolicySendWhenUserTrigger = 1,
307     ePolicySendInlineContent = 2,
308   };
309 
310   /*
311    * Sending referrer when cross origin policy, indicates when referrer should
312    * be send when compare 2 origins. Order matters here.
313    */
314   enum XOriginSendingPolicy : uint32_t {
315     ePolicyAlwaysSend = 0,
316     ePolicySendWhenSameDomain = 1,
317     ePolicySendWhenSameHost = 2,
318   };
319 
320   /*
321    * Handle user controlled pref network.http.referer.XOriginPolicy
322    */
323   nsresult HandleUserXOriginSendingPolicy(nsIURI* aURI, nsIURI* aReferrer,
324                                           bool& aAllowed) const;
325 
326   /*
327    * Handle user controlled pref network.http.sendRefererHeader
328    */
329   nsresult HandleUserReferrerSendingPolicy(nsIHttpChannel* aChannel,
330                                            bool& aAllowed) const;
331 
332   /*
333    * Compute trimming policy from user controlled prefs.
334    * This function is called when we already made sure a nonempty referrer is
335    * allowed to send.
336    */
337   TrimmingPolicy ComputeTrimmingPolicy(nsIHttpChannel* aChannel) const;
338 
339   // HttpBaseChannel could access IsInitialized() and ComputeReferrer();
340   friend class mozilla::net::HttpBaseChannel;
341 
342   /*
343    * Compute referrer for a given channel. The computation result then will be
344    * stored in this class and then used to set the actual referrer header of
345    * the channel. The computation could be controlled by several user prefs
346    * which are defined in StaticPrefList.yaml (see StaticPrefList.yaml for more
347    * details):
348    *  network.http.sendRefererHeader
349    *  network.http.referer.spoofSource
350    *  network.http.referer.hideOnionSource
351    *  network.http.referer.XOriginPolicy
352    *  network.http.referer.trimmingPolicy
353    *  network.http.referer.XOriginTrimmingPolicy
354    */
355   nsresult ComputeReferrer(nsIHttpChannel* aChannel);
356 
357   /*
358    * Check whether the ReferrerInfo has been initialized or not.
359    */
IsInitialized()360   bool IsInitialized() { return mInitialized; }
361 
362   // nsHttpChannel, Document could access IsPolicyOverrided();
363   friend class mozilla::net::nsHttpChannel;
364   friend class mozilla::dom::Document;
365   /*
366    * Check whether if unset referrer policy is overrided by default or not
367    */
IsPolicyOverrided()368   bool IsPolicyOverrided() { return mOverridePolicyByDefault; }
369 
370   /*
371    *  Get origin string from a given valid referrer URI (http, https, ftp)
372    *
373    *  @aReferrer - the full referrer URI
374    *  @aResult - the resulting aReferrer in string format.
375    */
376   nsresult GetOriginFromReferrerURI(nsIURI* aReferrer,
377                                     nsACString& aResult) const;
378 
379   /*
380    * Trim a given referrer with a given a trimming policy,
381    */
382   nsresult TrimReferrerWithPolicy(nsIURI* aReferrer,
383                                   TrimmingPolicy aTrimmingPolicy,
384                                   nsACString& aResult) const;
385 
386   /*
387    *  Limit referrer length using the following ruleset:
388    *   - If the length of referrer URL is over max length, strip down to origin.
389    *   - If the origin is still over max length, remove the referrer entirely.
390    *
391    *  This function comlements TrimReferrerPolicy and needs to be called right
392    *  after TrimReferrerPolicy.
393    *
394    *  @aChannel - used to query information needed for logging to the console.
395    *  @aReferrer - the full referrer URI; needs to be identical to aReferrer
396    *               passed to TrimReferrerPolicy.
397    *  @aTrimmingPolicy - represents the trimming policy which was applied to the
398    *                     referrer; needs to be identical to aTrimmingPolicy
399    *                     passed to TrimReferrerPolicy.
400    *  @aInAndOutTrimmedReferrer -  an in and outgoing argument representing the
401    *                               referrer value. Please pass the result of
402    *                               TrimReferrerWithPolicy as
403    *                               aInAndOutTrimmedReferrer which will then be
404    *                               reduced to the origin or completely truncated
405    *                               in case the referrer value exceeds the length
406    *                               limitation.
407    */
408   nsresult LimitReferrerLength(nsIHttpChannel* aChannel, nsIURI* aReferrer,
409                                TrimmingPolicy aTrimmingPolicy,
410                                nsACString& aInAndOutTrimmedReferrer) const;
411 
412   /*
413    * Write message to the error console
414    */
415   void LogMessageToConsole(nsIHttpChannel* aChannel, const char* aMsg,
416                            const nsTArray<nsString>& aParams) const;
417 
418   friend class mozilla::URLAndReferrerInfo;
419 
420   nsCOMPtr<nsIURI> mOriginalReferrer;
421 
422   ReferrerPolicyEnum mPolicy;
423 
424   // Indicates if the referrer should be sent or not even when it's available
425   // (default is true).
426   bool mSendReferrer;
427 
428   // Since the ReferrerInfo is immutable, we use this member as a helper to
429   // ensure no one can call e.g. init() twice to modify state of the
430   // ReferrerInfo.
431   bool mInitialized;
432 
433   // Indicates if unset referrer policy is overrided by default
434   bool mOverridePolicyByDefault;
435 
436   // Store a computed referrer for a given channel
437   Maybe<nsCString> mComputedReferrer;
438 };
439 
440 }  // namespace dom
441 }  // namespace mozilla
442 
443 #endif  // mozilla_dom_ReferrerInfo_h
444