CookieServiceParent.cpp (14120B)
1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ 2 /* This Source Code Form is subject to the terms of the Mozilla Public 3 * License, v. 2.0. If a copy of the MPL was not distributed with this 4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 5 6 #include "CookieCommons.h" 7 #include "CookieLogging.h" 8 #include "CookieServiceParent.h" 9 #include "mozilla/dom/ContentParent.h" 10 #include "mozilla/net/CookieService.h" 11 #include "mozilla/net/CookieServiceParent.h" 12 13 #include "mozilla/ipc/URIUtils.h" 14 #include "mozilla/StoragePrincipalHelper.h" 15 #include "mozIThirdPartyUtil.h" 16 #include "nsArrayUtils.h" 17 #include "nsIChannel.h" 18 #include "mozilla/StaticPrefs_network.h" 19 #include "nsIEffectiveTLDService.h" 20 #include "nsNetCID.h" 21 #include "nsMixedContentBlocker.h" 22 23 using namespace mozilla::ipc; 24 25 namespace mozilla { 26 namespace net { 27 28 CookieServiceParent::CookieServiceParent(dom::ContentParent* aContentParent) { 29 MOZ_ASSERT(aContentParent); 30 31 // Instantiate the cookieservice via the service manager, so it sticks around 32 // until shutdown. 33 nsCOMPtr<nsICookieService> cs = do_GetService(NS_COOKIESERVICE_CONTRACTID); 34 35 // Get the CookieService instance directly, so we can call internal methods. 36 mCookieService = CookieService::GetSingleton(); 37 NS_ASSERTION(mCookieService, "couldn't get nsICookieService"); 38 39 mTLDService = do_GetService(NS_EFFECTIVETLDSERVICE_CONTRACTID); 40 MOZ_ALWAYS_TRUE(mTLDService); 41 42 mProcessingCookie = false; 43 44 nsTArray<nsCOMPtr<nsIPrincipal>> list; 45 aContentParent->TakeCookieInProcessCache(list); 46 47 for (nsIPrincipal* principal : list) { 48 nsCOMPtr<nsIURI> uri = principal->GetURI(); 49 UpdateCookieInContentList(uri, principal->OriginAttributesRef()); 50 } 51 } 52 53 void CookieServiceParent::RemoveBatchDeletedCookies(nsIArray* aCookieList) { 54 uint32_t len = 0; 55 aCookieList->GetLength(&len); 56 OriginAttributes attrs; 57 CookieStruct cookieStruct; 58 nsTArray<CookieStruct> cookieStructList; 59 nsTArray<OriginAttributes> attrsList; 60 for (uint32_t i = 0; i < len; i++) { 61 nsCOMPtr<nsICookie> xpcCookie = do_QueryElementAt(aCookieList, i); 62 const auto& cookie = xpcCookie->AsCookie(); 63 attrs = cookie.OriginAttributesRef(); 64 cookieStruct = cookie.ToIPC(); 65 66 // Child only needs to know HttpOnly cookies exists, not its value 67 // Same for Secure cookies going to a process for an insecure site. 68 if (cookie.IsHttpOnly() || !InsecureCookieOrSecureOrigin(cookie)) { 69 cookieStruct.value() = ""; 70 } 71 cookieStructList.AppendElement(cookieStruct); 72 attrsList.AppendElement(attrs); 73 } 74 (void)SendRemoveBatchDeletedCookies(cookieStructList, attrsList); 75 } 76 77 void CookieServiceParent::RemoveAll() { (void)SendRemoveAll(); } 78 79 void CookieServiceParent::RemoveCookie(const Cookie& cookie, 80 const nsID* aOperationID) { 81 const OriginAttributes& attrs = cookie.OriginAttributesRef(); 82 CookieStruct cookieStruct = cookie.ToIPC(); 83 84 // Child only needs to know HttpOnly cookies exists, not its value 85 // Same for Secure cookies going to a process for an insecure site. 86 if (cookie.IsHttpOnly() || !InsecureCookieOrSecureOrigin(cookie)) { 87 cookieStruct.value() = ""; 88 } 89 (void)SendRemoveCookie(cookieStruct, attrs, 90 aOperationID ? Some(*aOperationID) : Nothing()); 91 } 92 93 void CookieServiceParent::AddCookie(const Cookie& cookie, 94 const nsID* aOperationID) { 95 const OriginAttributes& attrs = cookie.OriginAttributesRef(); 96 CookieStruct cookieStruct = cookie.ToIPC(); 97 98 // Child only needs to know HttpOnly cookies exists, not its value 99 // Same for Secure cookies going to a process for an insecure site. 100 if (cookie.IsHttpOnly() || !InsecureCookieOrSecureOrigin(cookie)) { 101 cookieStruct.value() = ""; 102 } 103 (void)SendAddCookie(cookieStruct, attrs, 104 aOperationID ? Some(*aOperationID) : Nothing()); 105 } 106 107 bool CookieServiceParent::ContentProcessHasCookie(const Cookie& cookie) { 108 return ContentProcessHasCookie(cookie.Host(), cookie.OriginAttributesRef()); 109 } 110 111 bool CookieServiceParent::ContentProcessHasCookie( 112 const nsACString& aHost, const OriginAttributes& aOriginAttributes) { 113 nsCString baseDomain; 114 if (NS_WARN_IF(NS_FAILED(CookieCommons::GetBaseDomainFromHost( 115 mTLDService, aHost, baseDomain)))) { 116 return false; 117 } 118 119 CookieKey cookieKey(baseDomain, aOriginAttributes); 120 return mCookieKeysInContent.MaybeGet(cookieKey).isSome(); 121 } 122 123 bool CookieServiceParent::InsecureCookieOrSecureOrigin(const Cookie& cookie) { 124 nsCString baseDomain; 125 if (NS_FAILED(CookieCommons::GetBaseDomainFromHost(mTLDService, cookie.Host(), 126 baseDomain))) { 127 MOZ_ASSERT(false, 128 "CookieServiceParent::InsecureCookieOrSecureOrigin - " 129 "GetBaseDomainFromHost shouldn't fail"); 130 return false; 131 } 132 133 // cookie is insecure or cookie is associated with a secure-origin process 134 CookieKey cookieKey(baseDomain, cookie.OriginAttributesRef()); 135 if (Maybe<bool> allowSecure = mCookieKeysInContent.MaybeGet(cookieKey)) { 136 return (!cookie.IsSecure() || *allowSecure); 137 } 138 return false; 139 } 140 141 void CookieServiceParent::TrackCookieLoad(nsIChannel* aChannel) { 142 nsCOMPtr<nsIURI> uri; 143 aChannel->GetURI(getter_AddRefs(uri)); 144 145 nsCOMPtr<nsILoadInfo> loadInfo = aChannel->LoadInfo(); 146 bool isSafeTopLevelNav = CookieCommons::IsSafeTopLevelNav(aChannel); 147 bool hadCrossSiteRedirects = false; 148 bool isSameSiteForeign = 149 CookieCommons::IsSameSiteForeign(aChannel, uri, &hadCrossSiteRedirects); 150 151 nsCOMPtr<mozIThirdPartyUtil> thirdPartyUtil; 152 thirdPartyUtil = do_GetService(THIRDPARTYUTIL_CONTRACTID); 153 154 uint32_t rejectedReason = 0; 155 ThirdPartyAnalysisResult result = thirdPartyUtil->AnalyzeChannel( 156 aChannel, false, nullptr, nullptr, &rejectedReason); 157 158 OriginAttributes storageOriginAttributes = loadInfo->GetOriginAttributes(); 159 StoragePrincipalHelper::PrepareEffectiveStoragePrincipalOriginAttributes( 160 aChannel, storageOriginAttributes); 161 162 nsTArray<OriginAttributes> originAttributesList; 163 originAttributesList.AppendElement(storageOriginAttributes); 164 165 // CHIPS - If CHIPS is enabled the partitioned cookie jar is always available 166 // (and therefore the partitioned OriginAttributes), the unpartitioned cookie 167 // jar is only available in first-party or third-party with storageAccess 168 // contexts. 169 nsCOMPtr<nsICookieJarSettings> cookieJarSettings = 170 CookieCommons::GetCookieJarSettings(aChannel); 171 bool isCHIPS = StaticPrefs::network_cookie_CHIPS_enabled() && 172 !cookieJarSettings->GetBlockingAllContexts(); 173 bool isUnpartitioned = 174 !result.contains(ThirdPartyAnalysis::IsForeign) || 175 result.contains(ThirdPartyAnalysis::IsStorageAccessPermissionGranted); 176 if (isCHIPS && isUnpartitioned) { 177 // Assert that the storage originAttributes is empty. In other words, 178 // it's unpartitioned. 179 MOZ_ASSERT(storageOriginAttributes.mPartitionKey.IsEmpty()); 180 // Add the partitioned principal to principals 181 OriginAttributes partitionedOriginAttributes; 182 StoragePrincipalHelper::GetOriginAttributes( 183 aChannel, partitionedOriginAttributes, 184 StoragePrincipalHelper::ePartitionedPrincipal); 185 // Only append the partitioned originAttributes if the partitionKey is set. 186 // The partitionKey could be empty for partitionKey in partitioned 187 // originAttributes if the channel is for privilege request, such as 188 // extension's requests. 189 if (!partitionedOriginAttributes.mPartitionKey.IsEmpty()) { 190 originAttributesList.AppendElement(partitionedOriginAttributes); 191 } 192 } 193 194 for (auto& originAttributes : originAttributesList) { 195 UpdateCookieInContentList(uri, originAttributes); 196 } 197 198 // Send matching cookies to Child. 199 nsTArray<RefPtr<Cookie>> foundCookieList; 200 mCookieService->GetCookiesForURI( 201 uri, aChannel, result.contains(ThirdPartyAnalysis::IsForeign), 202 result.contains(ThirdPartyAnalysis::IsThirdPartyTrackingResource), 203 result.contains(ThirdPartyAnalysis::IsThirdPartySocialTrackingResource), 204 result.contains(ThirdPartyAnalysis::IsStorageAccessPermissionGranted), 205 rejectedReason, isSafeTopLevelNav, isSameSiteForeign, 206 hadCrossSiteRedirects, false, true, originAttributesList, 207 foundCookieList); 208 nsTArray<CookieStructTable> matchingCookiesListTable; 209 SerializeCookieListTable(foundCookieList, matchingCookiesListTable, uri); 210 (void)SendTrackCookiesLoad(matchingCookiesListTable); 211 } 212 213 // we append outgoing cookie info into a list here so the ContentParent can 214 // filter cookies passing to unnecessary ContentProcesses 215 void CookieServiceParent::UpdateCookieInContentList( 216 nsIURI* uri, const OriginAttributes& originAttrs) { 217 nsCString baseDomain; 218 bool requireAHostMatch = false; 219 220 // prevent malformed urls from being added to the cookie list 221 if (NS_WARN_IF(NS_FAILED(CookieCommons::GetBaseDomain( 222 mTLDService, uri, baseDomain, requireAHostMatch)))) { 223 return; 224 } 225 226 CookieKey cookieKey(baseDomain, originAttrs); 227 bool& allowSecure = mCookieKeysInContent.LookupOrInsert(cookieKey, false); 228 allowSecure = 229 allowSecure || nsMixedContentBlocker::IsPotentiallyTrustworthyOrigin(uri); 230 } 231 232 // static 233 void CookieServiceParent::SerializeCookieListTable( 234 const nsTArray<RefPtr<Cookie>>& aFoundCookieList, 235 nsTArray<CookieStructTable>& aCookiesListTable, nsIURI* aHostURI) { 236 // Stores the index in aCookiesListTable by origin attributes suffix. 237 nsTHashMap<nsCStringHashKey, size_t> cookieListTable; 238 239 for (Cookie* cookie : aFoundCookieList) { 240 nsAutoCString attrsSuffix; 241 cookie->OriginAttributesRef().CreateSuffix(attrsSuffix); 242 size_t tableIndex = cookieListTable.LookupOrInsertWith(attrsSuffix, [&] { 243 size_t index = aCookiesListTable.Length(); 244 CookieStructTable* newTable = aCookiesListTable.AppendElement(); 245 newTable->attrs() = cookie->OriginAttributesRef(); 246 return index; 247 }); 248 249 CookieStruct* cookieStruct = 250 aCookiesListTable[tableIndex].cookies().AppendElement(); 251 *cookieStruct = cookie->ToIPC(); 252 253 // clear http-only cookie values 254 if (cookie->IsHttpOnly()) { 255 // Value only needs to exist if an HttpOnly cookie exists. 256 cookieStruct->value() = ""; 257 } 258 259 // clear secure cookie values in insecure context 260 bool potentiallyTurstworthy = 261 nsMixedContentBlocker::IsPotentiallyTrustworthyOrigin(aHostURI); 262 if (cookie->IsSecure() && !potentiallyTurstworthy) { 263 cookieStruct->value() = ""; 264 } 265 } 266 } 267 268 IPCResult CookieServiceParent::RecvGetCookieList( 269 nsIURI* aHost, const bool& aIsForeign, 270 const bool& aIsThirdPartyTrackingResource, 271 const bool& aIsThirdPartySocialTrackingResource, 272 const bool& aStorageAccessPermissionGranted, 273 const uint32_t& aRejectedReason, const bool& aIsSafeTopLevelNav, 274 const bool& aIsSameSiteForeign, const bool& aHadCrossSiteRedirects, 275 nsTArray<OriginAttributes>&& aAttrsList, GetCookieListResolver&& aResolve) { 276 // Send matching cookies to Child. 277 if (!aHost) { 278 return IPC_FAIL(this, "aHost must not be null"); 279 } 280 281 // we append outgoing cookie info into a list here so the ContentParent can 282 // filter cookies that do not need to go to certain ContentProcesses 283 for (const auto& attrs : aAttrsList) { 284 UpdateCookieInContentList(aHost, attrs); 285 } 286 287 nsTArray<RefPtr<Cookie>> foundCookieList; 288 // Note: passing nullptr as aChannel to GetCookiesForURI() here is fine since 289 // this argument is only used for proper reporting of cookie loads, but the 290 // child process already does the necessary reporting in this case for us. 291 mCookieService->GetCookiesForURI( 292 aHost, nullptr, aIsForeign, aIsThirdPartyTrackingResource, 293 aIsThirdPartySocialTrackingResource, aStorageAccessPermissionGranted, 294 aRejectedReason, aIsSafeTopLevelNav, aIsSameSiteForeign, 295 aHadCrossSiteRedirects, false, true, aAttrsList, foundCookieList); 296 297 nsTArray<CookieStructTable> matchingCookiesListTable; 298 SerializeCookieListTable(foundCookieList, matchingCookiesListTable, aHost); 299 300 aResolve(matchingCookiesListTable); 301 302 return IPC_OK(); 303 } 304 305 void CookieServiceParent::ActorDestroy(ActorDestroyReason aWhy) { 306 // Nothing needed here. Called right before destructor since this is a 307 // non-refcounted class. 308 } 309 310 IPCResult CookieServiceParent::RecvSetCookies( 311 const nsCString& aBaseDomain, const OriginAttributes& aOriginAttributes, 312 nsIURI* aHost, bool aFromHttp, bool aIsThirdParty, 313 const nsTArray<CookieStruct>& aCookies) { 314 if (!ContentProcessHasCookie(aBaseDomain, aOriginAttributes)) { 315 return IPC_FAIL(this, "Invalid set-cookie request from content process"); 316 } 317 318 return SetCookies(aBaseDomain, aOriginAttributes, aHost, aFromHttp, 319 aIsThirdParty, aCookies); 320 } 321 322 IPCResult CookieServiceParent::SetCookies( 323 const nsCString& aBaseDomain, const OriginAttributes& aOriginAttributes, 324 nsIURI* aHost, bool aFromHttp, bool aIsThirdParty, 325 const nsTArray<CookieStruct>& aCookies, 326 dom::BrowsingContext* aBrowsingContext) { 327 if (!mCookieService) { 328 return IPC_OK(); 329 } 330 331 // Deserialize URI. Having a host URI is mandatory and should always be 332 // provided by the child; thus we consider failure fatal. 333 if (!aHost) { 334 return IPC_FAIL(this, "aHost must not be null"); 335 } 336 337 // We set the cookie processing flag to true while processing this cookie 338 // update, to make sure we don't send it back to the same content process. 339 CookieProcessingGuard guard(this); 340 341 nsICookieValidation::ValidationError error = 342 mCookieService->SetCookiesFromIPC(aBaseDomain, aOriginAttributes, aHost, 343 aFromHttp, aIsThirdParty, aCookies, 344 aBrowsingContext); 345 MOZ_DIAGNOSTIC_ASSERT(error == nsICookieValidation::eOK); 346 347 if (error != nsICookieValidation::eOK) { 348 MOZ_LOG(gCookieLog, LogLevel::Warning, 349 ("Invalid cookie submission from the content process: %d", error)); 350 } 351 352 return IPC_OK(); 353 } 354 355 } // namespace net 356 } // namespace mozilla