CookieJarSettings.h (12267B)
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_net_CookieJarSettings_h 8 #define mozilla_net_CookieJarSettings_h 9 10 #include "mozilla/Maybe.h" 11 #include "mozilla/net/NeckoChannelParams.h" 12 13 #include "nsICookieJarSettings.h" 14 #include "nsIPermission.h" 15 #include "nsTArray.h" 16 17 #define COOKIEJARSETTINGS_CONTRACTID "@mozilla.org/cookieJarSettings;1" 18 // 4ce234f1-52e8-47a9-8c8d-b02f815733c7 19 #define COOKIEJARSETTINGS_CID \ 20 {0x4ce234f1, 0x52e8, 0x47a9, {0x8c, 0x8d, 0xb0, 0x2f, 0x81, 0x57, 0x33, 0xc7}} 21 22 namespace mozilla { 23 namespace net { 24 25 class CookieJarSettingsArgs; 26 27 using CookiePermissionsArgsData = nsTArray<net::CookiePermissionData>; 28 29 /** 30 * CookieJarSettings 31 * ~~~~~~~~~~~~~~ 32 * 33 * CookieJarSettings is a snapshot of the cookie jar's configurations in a 34 * precise moment of time, such as the cookie policy and cookie permissions. 35 * This object is used by top-level documents to have a consistent cookie jar 36 * configuration also in case the user changes it. New configurations will apply 37 * only to new top-level documents. 38 * 39 * CookieJarSettings creation 40 * ~~~~~~~~~~~~~~~~~~~~~~~ 41 * 42 * CookieJarSettings is created when the top-level document's nsIChannel's 43 * nsILoadInfo is constructed. Any sub-resource and any sub-document inherits it 44 * from that nsILoadInfo. Also dedicated workers and their resources inherit it 45 * from the parent document. 46 * 47 * SharedWorkers and ServiceWorkers have their own CookieJarSettings because 48 * they don't have a single parent document (SharedWorkers could have more than 49 * one, ServiceWorkers have none). 50 * 51 * In Chrome code, we have a new CookieJarSettings when we download resources 52 * via 'Save-as...' and we also have a new CookieJarSettings for favicon 53 * downloading. 54 * 55 * Content-scripts WebExtensions also have their own CookieJarSettings because 56 * they don't have a direct access to the document they are running into. 57 * 58 * Anything else will have a special CookieJarSettings which blocks everything 59 * (CookieJarSettings::GetBlockingAll()) by forcing BEHAVIOR_REJECT as policy. 60 * When this happens, that context will not have access to the cookie jar and no 61 * cookies are sent or received. 62 * 63 * Propagation of CookieJarSettings 64 * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 65 * 66 * CookieJarSettings are shared inside the same top-level document via its 67 * nsIChannel's nsILoadInfo. This is done automatically if you pass a nsINode 68 * to NS_NewChannel(), and it must be done manually if you use a different 69 * channel constructor. For instance, this happens for any worker networking 70 * operation. 71 * 72 * We use the same CookieJarSettings for any resource belonging to the top-level 73 * document even if cross-origin. This makes the browser behave consistently a 74 * scenario where A loads B which loads A again, and cookie policy/permission 75 * changes in the meantime. 76 * 77 * Cookie Permissions propagation 78 * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 79 * 80 * CookieJarSettings populates the known cookie permissions only when required. 81 * Initially the list is empty, but when CookieJarSettings::CookiePermission() 82 * is called, the requested permission is stored in the internal list if it 83 * doesn't exist yet. 84 * 85 * This is actually nice because it relies on the permission propagation from 86 * parent to content process. No extra IPC is required. 87 * 88 * Note that we store permissions with UNKNOWN_ACTION values too because they 89 * can be set after the loading of the top-level document and we don't want to 90 * return a different value when this happens. 91 * 92 * Use of CookieJarSettings 93 * ~~~~~~~~~~~~~~~~~~~~~ 94 * 95 * In theory, there should not be direct access to cookie permissions or 96 * cookieBehavior pref. Everything should pass through CookieJarSettings. 97 * 98 * A reference to CookieJarSettings can be obtained from 99 * nsILoadInfo::GetCookieJarSettings(), from Document::CookieJarSettings() and 100 * from the WorkerPrivate::CookieJarSettings(). 101 * 102 * CookieJarSettings is thread-safe, but the permission list must be touched 103 * only on the main-thread. 104 * 105 * Testing 106 * ~~~~~~~ 107 * 108 * If you need to test the changing of cookie policy or a cookie permission, you 109 * need to workaround CookieJarSettings. This can be done opening a new window 110 * and running the test into that new global. 111 */ 112 113 /** 114 * Class that provides an nsICookieJarSettings implementation. 115 */ 116 class CookieJarSettings final : public nsICookieJarSettings { 117 public: 118 typedef nsTArray<RefPtr<nsIPermission>> CookiePermissionList; 119 120 NS_DECL_THREADSAFE_ISUPPORTS 121 NS_DECL_NSICOOKIEJARSETTINGS 122 NS_DECL_NSISERIALIZABLE 123 124 static already_AddRefed<nsICookieJarSettings> GetBlockingAll( 125 bool aShouldResistFingerprinting); 126 127 enum CreateMode { eRegular, ePrivate }; 128 129 static already_AddRefed<nsICookieJarSettings> Create( 130 CreateMode aMode, bool aShouldResistFingerprinting); 131 132 static already_AddRefed<nsICookieJarSettings> Create( 133 nsIPrincipal* aPrincipal); 134 135 // This function should be only called for XPCOM. You should never use this 136 // for other purposes. 137 static already_AddRefed<nsICookieJarSettings> CreateForXPCOM(); 138 139 static already_AddRefed<nsICookieJarSettings> Create( 140 uint32_t aCookieBehavior, const nsAString& aPartitionKey, 141 bool aIsFirstPartyIsolated, bool aIsOnContentBlockingAllowList, 142 bool aShouldResistFingerprinting); 143 144 static CookieJarSettings* Cast(nsICookieJarSettings* aCS) { 145 return static_cast<CookieJarSettings*>(aCS); 146 } 147 148 already_AddRefed<CookieJarSettings> Clone() { 149 RefPtr<CookieJarSettings> clone = new CookieJarSettings(*this); 150 return clone.forget(); 151 } 152 153 void Serialize(CookieJarSettingsArgs& aData); 154 155 static void Deserialize(const CookieJarSettingsArgs& aData, 156 nsICookieJarSettings** aCookieJarSettings); 157 158 static CookiePermissionList DeserializeCookiePermissions( 159 const CookiePermissionsArgsData& aPermissionData); 160 161 // Merge the current CookieJarSettings with the new CookieJarSettingsArgs. It 162 // returns a new merged CookieJarSettings. 163 already_AddRefed<nsICookieJarSettings> Merge( 164 const CookieJarSettingsArgs& aData); 165 166 // We don't want to send this object from parent to child process if there are 167 // no reasons. HasBeenChanged() returns true if the object has changed its 168 // internal state and it must be sent beck to the content process. 169 bool HasBeenChanged() const { return mToBeMerged; } 170 171 void UpdateIsOnContentBlockingAllowList(nsIChannel* aChannel); 172 void SetIsOnContentBlockingAllowList(bool aIsOnContentBlockingAllowList) { 173 mIsOnContentBlockingAllowList = aIsOnContentBlockingAllowList; 174 } 175 176 void SetPartitionKey(nsIURI* aURI); 177 void SetPartitionKey(const nsAString& aPartitionKey) { 178 mPartitionKey = aPartitionKey; 179 } 180 const nsAString& GetPartitionKey() { return mPartitionKey; }; 181 182 void UpdatePartitionKeyForDocumentLoadedByChannel(nsIChannel* aChannel); 183 184 void SetFingerprintingRandomizationKey(const nsTArray<uint8_t>& aKey) { 185 mFingerprintingRandomKey.reset(); 186 187 mFingerprintingRandomKey.emplace(aKey.Clone()); 188 } 189 190 // Utility function to test if the passed cookiebahvior is 191 // BEHAVIOR_REJECT_TRACKER, BEHAVIOR_REJECT_TRACKER_AND_PARTITION_FOREIGN or 192 // BEHAVIOR_REJECT_FOREIGN when 193 // network.cookie.rejectForeignWithExceptions.enabled pref is set to true. 194 static bool IsRejectThirdPartyContexts(uint32_t aCookieBehavior); 195 196 void SetTopLevelWindowContextId(uint64_t aId) { 197 mTopLevelWindowContextId = aId; 198 } 199 uint64_t GetTopLevelWindowContextId() { return mTopLevelWindowContextId; } 200 201 private: 202 enum State { 203 // No cookie permissions are allowed to be stored in this object. 204 eFixed, 205 206 // Cookie permissions can be stored in case they are unknown when they are 207 // asked or when they are sent from the parent process. 208 eProgressive, 209 }; 210 211 CookieJarSettings(uint32_t aCookieBehavior, bool aIsFirstPartyIsolated, 212 bool aShouldResistFingerprinting, State aState); 213 214 CookieJarSettings(const CookieJarSettings& aOther) { 215 mCookieBehavior = aOther.mCookieBehavior; 216 mIsFirstPartyIsolated = aOther.mIsFirstPartyIsolated; 217 mCookiePermissions = aOther.mCookiePermissions.Clone(); 218 219 mIsOnContentBlockingAllowList = aOther.mIsOnContentBlockingAllowList; 220 mIsOnContentBlockingAllowListUpdated = 221 aOther.mIsOnContentBlockingAllowListUpdated; 222 223 mPartitionKey = aOther.mPartitionKey; 224 mState = aOther.mState; 225 mToBeMerged = aOther.mToBeMerged; 226 227 mShouldResistFingerprinting = aOther.mShouldResistFingerprinting; 228 if (aOther.mFingerprintingRandomKey.isSome()) { 229 mFingerprintingRandomKey = 230 Some(aOther.mFingerprintingRandomKey.ref().Clone()); 231 } 232 233 mTopLevelWindowContextId = aOther.mTopLevelWindowContextId; 234 } 235 236 CookiePermissionList& GetCookiePermissionsListRef(); 237 238 ~CookieJarSettings(); 239 240 uint32_t mCookieBehavior; 241 bool mIsFirstPartyIsolated; 242 CookiePermissionList mCookiePermissions; 243 CookiePermissionsArgsData mIPCCookiePermissions; 244 245 bool mIsOnContentBlockingAllowList; 246 bool mIsOnContentBlockingAllowListUpdated; 247 nsString mPartitionKey; 248 State mState; 249 250 bool mToBeMerged; 251 252 // DO NOT USE THIS MEMBER TO CHECK IF YOU SHOULD RESIST FINGERPRINTING. 253 // USE THE nsContentUtils::ShouldResistFingerprinting() METHODS ONLY. 254 // 255 // As we move to fine-grained RFP control, we want to support per-domain 256 // exemptions from ResistFingerprinting. Specifically the behavior should be 257 // as such: 258 // 259 // Top-Level Document is on an Exempted Domain 260 // - RFP is disabled. 261 // 262 // Top-Level Document on an Exempted Domain embedding a non-exempted 263 // cross-origin iframe 264 // - RFP in the iframe is enabled (NOT exempted). (**) 265 // 266 // Top-Level Document on an Exempted Domain embedding an exempted cross-origin 267 // iframe 268 // - RFP in the iframe is disabled (exempted). 269 // 270 // Top-Level Document on a Non-Exempted Domain 271 // - RFP is enabled (NOT exempted). 272 // 273 // Top-Level Document on a Non-Exempted Domain embeds an exempted cross-origin 274 // iframe 275 // - RFP in the iframe is enabled (NOT exempted). (*) 276 // 277 // Exempted Document (top-level or iframe) contacts any cross-origin domain 278 // (exempted or non-exempted) 279 // - RFP is disabled (exempted) for the request 280 // 281 // Non-Exempted Document (top-level or iframe) contacts any cross-origin 282 // domain 283 // (exempted or non-exempted) 284 // - RFP is enabled (NOT exempted) for the request 285 // 286 // This boolean on CookieJarSettings will enable us to apply the most 287 // difficult rule, marked in (*). (It is difficult because the 288 // subdocument's loadinfo will look like it should be exempted.) 289 // However if we trusted this member blindly, it would not correctly apply 290 // the one marked with (**). (Because it would inherit an exemption into 291 // a subdocument that should not be exempted.) 292 // To handle this case, we only trust a CookieJar's ShouldRFP value if it 293 // says we should resist fingerprinting. If it says that we _should not_, 294 // we continue and check the channel's URI or LoadInfo and if 295 // the domain specified there is not an exempted domain, enforce RFP anyway. 296 // This all occurrs in the nscontentUtils::ShouldResistFingerprinting 297 // functions which you should be using. 298 bool mShouldResistFingerprinting; 299 300 // The key used to generate the random noise for randomizing the browser 301 // fingerprint. The key is decided by the session key and the top-level site. 302 // So, the browse fingerprint will look different to the same tracker 303 // under different top-level sites. Also, the fingerprint will change as 304 // browsing session changes. This can prevent trackers to identify individuals 305 // by using browser fingerprints. 306 Maybe<nsTArray<uint8_t>> mFingerprintingRandomKey; 307 308 // This field caches the top level window context id when loading the top 309 // level document. 310 uint64_t mTopLevelWindowContextId; 311 }; 312 313 } // namespace net 314 } // namespace mozilla 315 316 #endif // mozilla_net_CookieJarSettings_h