summaryrefslogtreecommitdiffstats
path: root/toolkit/components/antitracking/StoragePrincipalHelper.h
blob: f813417eb63243fae899f3a5183cd998007ed8a7 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
/* -*- 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_StoragePrincipalHelper_h
#define mozilla_StoragePrincipalHelper_h

#include <cstdint>
#include "ErrorList.h"
#include "nsStringFwd.h"

/**
 * StoragePrincipal
 * ~~~~~~~~~~~~~~~~

 * StoragePrincipal is the nsIPrincipal to be used to open the cookie jar of a
 * resource's origin. Normally, the StoragePrincipal corresponds to the
 * resource's origin, but, in some scenarios, it can be different: it has the
 * `partitionKey` attribute set to the top-level “site” (i.e., scheme plus
 * eTLD+1 of the origin of the top-level document).
 *
 * Each storage component should always use the StoragePrincipal instead of the
 * 'real' one in order to implement the partitioning correctly. See the list of
 * the components here: https://privacycg.github.io/storage-partitioning/
 *
 * On the web, each resource has its own origin (see
 * https://html.spec.whatwg.org/multipage/origin.html#concept-origin) and each
 * origin has its own cookie jar, containing cookies, storage data, cache and so
 * on.
 *
 * In gecko-world, the origin and its attributes are stored and managed by the
 * nsIPrincipal interface. Both a resource's Principal and a resource's
 * StoragePrincipal are nsIPrincipal interfaces and, normally, they are the same
 * object.
 *
 * Naming and usage
 * ~~~~~~~~~~~~~~~~
 *
 * StoragePrincipal exposes four types of principals for a resource:
 * - Regular Principal:
 *     A “first-party” principal derived from the origin of the resource. This
 *     does not have the `partitionKey` origin attribute set.
 * - Partitioned Principal:
 *     The regular principal plus the partitionKey origin attribute set to
 *     the site of the top-level document (i.e., scheme plus eTLD+1).
 * - Storage Access Principal:
 *     A dynamic principal that changes when a resource receives storage access.
 *     By default, when storage access is denied, this is equal to the
 *     Partitioned Principal. When storage access is granted, this is equal to
 *     the Regular Principal.
 * - Foreign Partitioned Principal
 *     A principal that would be decided according to the fact that if the
 *     resource is a third party or not. If the resource is in a third-party
 *     context, this will be the partitioned principal. Otherwise, a regular
 *     principal will be used. Also, this doesn't like Storage Access Principal
 *     which changes according to storage access of a resource. Note that this
 *     is dFPI only; this prinipcal will always return regular principal when
 *     dFPI is disabled.
 *
 * Consumers of StoragePrincipal can request the principal type that meets their
 * needs. For example, storage that should always be partitioned should choose
 * the Partitioned Principal, while storage that should change with storage
 * access grants should choose the Storage Access Principal. And the storage
 * should be always partiitoned in the third-party context should use the
 * Foreign Partitioned Principal.
 *
 * You can obtain these nsIPrincipal objects:
 *
 * From a Document:
 * - Regular Principal: nsINode::NodePrincipal
 * - Storage Access Principal: Document::EffectiveStoragePrincipal
 * - Partitioned Principal: Document::PartitionedPrincipal
 *
 * From a Global object:
 * - Regular Principal: nsIScriptObjectPrincipal::GetPrincipal
 * - Storage Access Principal:
 *     nsIScriptObjectPrincipal::GetEffectiveStoragePrincipal
 * - Partitioned Principal: nsIScriptObjectPrincipal::PartitionedPrincipal
 *
 * From a Worker:
 * - Regular Principal: WorkerPrivate::GetPrincipal (main-thread)
 * - Regular Principal: WorkerPrivate::GetPrincipalInfo (worker thread)
 * - Storage Access Principal: WorkerPrivate::GetEffectiveStoragePrincipalInfo
 *                  (worker-thread)
 *
 * For a nsIChannel, the final principals must be calculated and they can be
 * obtained by calling:
 * - Regular Principal: nsIScriptSecurityManager::getChannelResultPrincipal
 * - Storage Access Principal:
 *     nsIScriptSecurityManager::getChannelResultStoragePrincipal
 * - Partitioned and regular Principal:
 *     nsIScriptSecurityManager::getChannelResultPrincipals
 *
 * Each use of nsIPrincipal is unique and it should be reviewed by anti-tracking
 * peers. But we can group the use of nsIPrincipal in these categories:
 *
 * - Network loading: use the Regular Principal
 * - Cache, not directly visible by content (network cache, HSTS, image cache,
 *   etc): Use the Storage Access Principal (in the future we will use the
 *   Partitioned Principal, but this part is not done yet)
 * - Storage APIs or anything that is written on disk (or kept in memory in
 *   private-browsing): use the Storage Access Principal
 * - PostMessage: if in the agent-cluster, use the Regular Principal. Otherwise,
 *   use the Storage Access Principal
 *
 * Storage access permission
 * ~~~~~~~~~~~~~~~~~~~~~~~~~
 *
 * When the storage access permission is granted, any of the Storage Access
 * Principal getter methods will return the Regular Principal instead of the
 * Partitioned Principal, and each storage component should consider the new
 * principal only.
 *
 * The trackers and the 3rd parties (in dFPI) will have access to its
 first-party
 * cookie jar, escaping from its partitioning.
 *
 * Storage access permissions can be granted in several ways:
 * - The Storage Access API
 *     (https://developer.mozilla.org/en-US/docs/Web/API/Storage_Access_API)
 * - ETP’s heuristics
 *
 (https://developer.mozilla.org/en-US/docs/Mozilla/Firefox/Privacy/Storage_access_policy#Storage_access_grants)
 * - A dFPI-specific login heuristic
 *     (https://bugzilla.mozilla.org/show_bug.cgi?id=1616585#c12)
 *
 * There are several ways to receive storage-permission notifications. You can
 * use these notifications to re-initialize components, to nullify or enable
 them
 * to use the “new” effective StoragePrincipal. The list of the notifications
 is:
 *
 * - Add some code in nsGlobalWindowInner::StorageAccessPermissionGranted().
 * - WorkerScope::StorageAccessPermissionGranted for Workers.
 * - observe the permission changes (not recommended)
 *
 * Scope of Storage Access
 * ~~~~~~~~~~~~~~~~~~~~~~~
 *
 * Immediately after access is granted, the permission is propagated and
 notified
 * to any contexts (windows and workers) in the same agent-cluster
 * (BrowserContextGroup).
 *
 * This means that if A.com has 2 iframes with B.com, and one of the 2 Bs
 obtains
 * the storage access, the other B will be notified too. Other B.com, 3rd
 parties
 * in other agent clusters will not obtain the storage permission.
 *
 * When the page is reloaded or is loaded for the first time, if it contains
 * B.com, and B.com has received the storage permission for the same first-party
 * in a previous loading, B.com will have the storage access permission granted
 * immediately.
 *
 * Cookies, LocalStorage, indexedDB
 * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 *
 * When granting storage permission, several storage and channel API getters and
 * constructors will start exposing first-party cookie jar objects
 (localStorage,
 * BroadcastChannel, etc).
 *
 * There is a side effect of this change: If a tracker has a reference to these
 * objects pre-storage permission granting, it will be able to interact with the
 * partitioned and the non-partitioned cookie jar at the same time. Note that
 * similar synchronization can be done server-side too. Because of this, we
 don’t
 * think that privacy-wise, this is an issue.
 *
 * localStorage supports StoragePrincipal, and will be switched after storage
 * access is granted. Trackers listed in the pref
 * privacy.restrict3rdpartystorage.partitionedHosts will use another special
 * partitioned session-only storage called PartitionedLocalStorage.
 *
 * sessionStorage is not covered by StoragePrincipal, but is double-keyed using
 * the top-level site when dFPI is active
 * (https://bugzilla.mozilla.org/show_bug.cgi?id=1629707).
 *
 * SharedWorkers and BroadcastChannels
 * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 *
 * SharedWorker and BroadcastChannel instances latch the effective storage
 * principal at the moment of their creation. Existing bindings to the
 * partitioned storage principal will continue to exist and operate even as it
 * becomes possible to create bindings associated with the Regular Principal.
 * This makes it possible for such globals to bi-directionally bridge
 information
 * between partitioned and non-partitioned principals.
 *
 * This is true until the page is reloaded. After the reload, the partitioned
 * cookie jar will no longer be accessible.
 *
 * We are planning to clear the partitioned site-data as soon as the page is
 * reloaded or dismissed (not done yet - bug 1628313).
 *
 * {Dedicated,Shared,Service}Workers
 * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 *
 * The storage access permission propagation happens with a ControlRunnable.
 This
 * could impact the use of sync event-loops. Take a reference of the principal
 * you want to use because it can change!
 *
 * ServiceWorkers are currently disabled for partitioned contexts.
 *
 * Client API uses the regular nsIPrincipal always because there is not a direct
 * connection between this API and the cookie jar. If we want to support
 * ServiceWorkers in partitioned context, this part must be revisited.
 */

class nsIChannel;
class nsICookieJarSettings;
class nsIDocShell;
class nsILoadGroup;
class nsIPrincipal;
class nsIURI;
class nsPIDOMWindowInner;

namespace mozilla {

namespace dom {
class Document;
class WorkerPrivate;
}  // namespace dom

namespace ipc {
class PrincipalInfo;
}

class OriginAttributes;

class StoragePrincipalHelper final {
 public:
  static nsresult Create(nsIChannel* aChannel, nsIPrincipal* aPrincipal,
                         bool aForceIsolation,
                         nsIPrincipal** aStoragePrincipal);

  static nsresult CreatePartitionedPrincipalForServiceWorker(
      nsIPrincipal* aPrincipal, nsICookieJarSettings* aCookieJarSettings,
      nsIPrincipal** aPartitionedPrincipal);

  static nsresult PrepareEffectiveStoragePrincipalOriginAttributes(
      nsIChannel* aChannel, OriginAttributes& aOriginAttributes);

  // A helper function to verify storage principal info with the principal info.
  static bool VerifyValidStoragePrincipalInfoForPrincipalInfo(
      const mozilla::ipc::PrincipalInfo& aStoragePrincipalInfo,
      const mozilla::ipc::PrincipalInfo& aPrincipalInfo);

  // A helper function to verify client principal info with the principal info.
  //
  // Note that the client principal refers the principal of the client, which is
  // supposed to be the foreign partitioned principal.
  static bool VerifyValidClientPrincipalInfoForPrincipalInfo(
      const mozilla::ipc::PrincipalInfo& aClientPrincipalInfo,
      const mozilla::ipc::PrincipalInfo& aPrincipalInfo);

  enum PrincipalType {
    // This is the first-party principal.
    eRegularPrincipal,

    // This is a dynamic principal based on the current state of the origin. If
    // the origin has the storage permission granted, effective storagePrincipal
    // will be the regular principal, otherwise, the partitioned Principal
    // will be used.
    eStorageAccessPrincipal,

    // This is the first-party principal, plus, First-party isolation attribute
    // set.
    ePartitionedPrincipal,

    // This principal returns different results based on whether its associated
    // channel/window is in a third-party context. While in a third-party
    // context, it returns the partitioned principal; otherwise, it returns the
    // regular principal.
    //
    // Note that this principal is not a dynamic principal like
    // `eStorageAccessPrincipal`, which changes depending on whether the storage
    // access permission is granted. This principal doesn't take the storage
    // access permission into consideration. Also, this principle is used in
    // dFPI only, meaning that it always returns the regular principal when dFP
    // Is disabled.
    eForeignPartitionedPrincipal,
  };

  /**
   * Extract the principal from the channel/document according to the given
   * principal type.
   */
  static nsresult GetPrincipal(nsIChannel* aChannel,
                               PrincipalType aPrincipalType,
                               nsIPrincipal** aPrincipal);
  static nsresult GetPrincipal(nsPIDOMWindowInner* aWindow,
                               PrincipalType aPrincipalType,
                               nsIPrincipal** aPrincipal);

  // Check if we need to use the partitioned principal for the service worker of
  // the given docShell. Please do not use this API unless you cannot get the
  // foreign partitioned principal, e.g. creating the inital about:blank page.
  static bool ShouldUsePartitionPrincipalForServiceWorker(
      nsIDocShell* aDocShell);

  static bool ShouldUsePartitionPrincipalForServiceWorker(
      dom::WorkerPrivate* aWorkerPrivate);

  /**
   * Extract the right OriginAttributes from the channel's triggering
   * principal.
   */
  static bool GetOriginAttributes(nsIChannel* aChannel,
                                  OriginAttributes& aAttributes,
                                  PrincipalType aPrincipalType);

  static bool GetRegularPrincipalOriginAttributes(
      dom::Document* aDocument, OriginAttributes& aAttributes);

  static bool GetRegularPrincipalOriginAttributes(
      nsILoadGroup* aLoadGroup, OriginAttributes& aAttributes);

  // These methods return the correct originAttributes to be used for network
  // state components (HSTS, network cache, image-cache, and so on).
  static bool GetOriginAttributesForNetworkState(nsIChannel* aChannel,
                                                 OriginAttributes& aAttributes);
  static void GetOriginAttributesForNetworkState(dom::Document* aDocument,
                                                 OriginAttributes& aAttributes);
  static void UpdateOriginAttributesForNetworkState(
      nsIURI* aFirstPartyURI, OriginAttributes& aAttributes);

  // For HSTS we want to force 'HTTP' in the partition key.
  static bool GetOriginAttributesForHSTS(nsIChannel* aChannel,
                                         OriginAttributes& aAttributes);

  // Like the function above, this function forces `HTTPS` in the partition key.
  // The OA created by this function is mainly used in DNS cache. The spec
  // specifies that the presence of HTTPS RR for an origin also indicates that
  // all HTTP resources are available over HTTPS, so we use this function to
  // ensure that all HTTPS RRs in DNS cache are accessed by HTTPS requests only.
  static bool GetOriginAttributesForHTTPSRR(nsIChannel* aChannel,
                                            OriginAttributes& aAttributes);

  // Get the origin attributes from a PrincipalInfo
  static bool GetOriginAttributes(
      const mozilla::ipc::PrincipalInfo& aPrincipalInfo,
      OriginAttributes& aAttributes);

  static bool PartitionKeyHasBaseDomain(const nsAString& aPartitionKey,
                                        const nsACString& aBaseDomain);

  static bool PartitionKeyHasBaseDomain(const nsAString& aPartitionKey,
                                        const nsAString& aBaseDomain);
};

}  // namespace mozilla

#endif  // mozilla_StoragePrincipalHelper_h