Backed out changeset b71c8c052463 (bug 1943846) for causing mass failures. CLOSED...
[gecko.git] / netwerk / cookie / CookieServiceParent.cpp
blobd679d5a933cad06a143455bab7ac7e849e0affb4
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/. */
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"
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"
23 using namespace mozilla::ipc;
25 namespace mozilla {
26 namespace net {
28 CookieServiceParent::CookieServiceParent(dom::ContentParent* aContentParent) {
29 MOZ_ASSERT(aContentParent);
31 // Instantiate the cookieservice via the service manager, so it sticks around
32 // until shutdown.
33 nsCOMPtr<nsICookieService> cs = do_GetService(NS_COOKIESERVICE_CONTRACTID);
35 // Get the CookieService instance directly, so we can call internal methods.
36 mCookieService = CookieService::GetSingleton();
37 NS_ASSERTION(mCookieService, "couldn't get nsICookieService");
39 mTLDService = do_GetService(NS_EFFECTIVETLDSERVICE_CONTRACTID);
40 MOZ_ALWAYS_TRUE(mTLDService);
42 mProcessingCookie = false;
44 nsTArray<nsCOMPtr<nsIPrincipal>> list;
45 aContentParent->TakeCookieInProcessCache(list);
47 for (nsIPrincipal* principal : list) {
48 nsCOMPtr<nsIURI> uri = principal->GetURI();
49 UpdateCookieInContentList(uri, principal->OriginAttributesRef());
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();
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() = "";
71 cookieStructList.AppendElement(cookieStruct);
72 attrsList.AppendElement(attrs);
74 Unused << SendRemoveBatchDeletedCookies(cookieStructList, attrsList);
77 void CookieServiceParent::RemoveAll() { Unused << SendRemoveAll(); }
79 void CookieServiceParent::RemoveCookie(const Cookie& cookie,
80 const nsID* aOperationID) {
81 const OriginAttributes& attrs = cookie.OriginAttributesRef();
82 CookieStruct cookieStruct = cookie.ToIPC();
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() = "";
89 Unused << SendRemoveCookie(cookieStruct, attrs,
90 aOperationID ? Some(*aOperationID) : Nothing());
93 void CookieServiceParent::AddCookie(const Cookie& cookie,
94 const nsID* aOperationID) {
95 const OriginAttributes& attrs = cookie.OriginAttributesRef();
96 CookieStruct cookieStruct = cookie.ToIPC();
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() = "";
103 Unused << SendAddCookie(cookieStruct, attrs,
104 aOperationID ? Some(*aOperationID) : Nothing());
107 bool CookieServiceParent::ContentProcessHasCookie(const Cookie& cookie) {
108 return ContentProcessHasCookie(cookie.Host(), cookie.OriginAttributesRef());
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;
119 CookieKey cookieKey(baseDomain, aOriginAttributes);
120 return mCookieKeysInContent.MaybeGet(cookieKey).isSome();
123 bool CookieServiceParent::InsecureCookieOrSecureOrigin(const Cookie& cookie) {
124 nsCString baseDomain;
125 // CookieStorage notifications triggering this won't fail to get base domain
126 MOZ_ALWAYS_SUCCEEDS(CookieCommons::GetBaseDomainFromHost(
127 mTLDService, cookie.Host(), baseDomain));
129 // cookie is insecure or cookie is associated with a secure-origin process
130 CookieKey cookieKey(baseDomain, cookie.OriginAttributesRef());
131 if (Maybe<bool> allowSecure = mCookieKeysInContent.MaybeGet(cookieKey)) {
132 return (!cookie.IsSecure() || *allowSecure);
134 return false;
137 void CookieServiceParent::TrackCookieLoad(nsIChannel* aChannel) {
138 nsCOMPtr<nsIURI> uri;
139 aChannel->GetURI(getter_AddRefs(uri));
141 nsCOMPtr<nsILoadInfo> loadInfo = aChannel->LoadInfo();
142 bool isSafeTopLevelNav = CookieCommons::IsSafeTopLevelNav(aChannel);
143 bool hadCrossSiteRedirects = false;
144 bool isSameSiteForeign =
145 CookieCommons::IsSameSiteForeign(aChannel, uri, &hadCrossSiteRedirects);
147 nsCOMPtr<mozIThirdPartyUtil> thirdPartyUtil;
148 thirdPartyUtil = do_GetService(THIRDPARTYUTIL_CONTRACTID);
150 uint32_t rejectedReason = 0;
151 ThirdPartyAnalysisResult result = thirdPartyUtil->AnalyzeChannel(
152 aChannel, false, nullptr, nullptr, &rejectedReason);
154 OriginAttributes storageOriginAttributes = loadInfo->GetOriginAttributes();
155 StoragePrincipalHelper::PrepareEffectiveStoragePrincipalOriginAttributes(
156 aChannel, storageOriginAttributes);
158 nsTArray<OriginAttributes> originAttributesList;
159 originAttributesList.AppendElement(storageOriginAttributes);
161 // CHIPS - If CHIPS is enabled the partitioned cookie jar is always available
162 // (and therefore the partitioned OriginAttributes), the unpartitioned cookie
163 // jar is only available in first-party or third-party with storageAccess
164 // contexts.
165 nsCOMPtr<nsICookieJarSettings> cookieJarSettings =
166 CookieCommons::GetCookieJarSettings(aChannel);
167 bool isCHIPS = StaticPrefs::network_cookie_CHIPS_enabled() &&
168 !cookieJarSettings->GetBlockingAllContexts();
169 bool isUnpartitioned =
170 !result.contains(ThirdPartyAnalysis::IsForeign) ||
171 result.contains(ThirdPartyAnalysis::IsStorageAccessPermissionGranted);
172 if (isCHIPS && isUnpartitioned) {
173 // Assert that the storage originAttributes is empty. In other words,
174 // it's unpartitioned.
175 MOZ_ASSERT(storageOriginAttributes.mPartitionKey.IsEmpty());
176 // Add the partitioned principal to principals
177 OriginAttributes partitionedOriginAttributes;
178 StoragePrincipalHelper::GetOriginAttributes(
179 aChannel, partitionedOriginAttributes,
180 StoragePrincipalHelper::ePartitionedPrincipal);
181 // Only append the partitioned originAttributes if the partitionKey is set.
182 // The partitionKey could be empty for partitionKey in partitioned
183 // originAttributes if the channel is for privilege request, such as
184 // extension's requests.
185 if (!partitionedOriginAttributes.mPartitionKey.IsEmpty()) {
186 originAttributesList.AppendElement(partitionedOriginAttributes);
190 for (auto& originAttributes : originAttributesList) {
191 UpdateCookieInContentList(uri, originAttributes);
194 // Send matching cookies to Child.
195 nsTArray<RefPtr<Cookie>> foundCookieList;
196 mCookieService->GetCookiesForURI(
197 uri, aChannel, result.contains(ThirdPartyAnalysis::IsForeign),
198 result.contains(ThirdPartyAnalysis::IsThirdPartyTrackingResource),
199 result.contains(ThirdPartyAnalysis::IsThirdPartySocialTrackingResource),
200 result.contains(ThirdPartyAnalysis::IsStorageAccessPermissionGranted),
201 rejectedReason, isSafeTopLevelNav, isSameSiteForeign,
202 hadCrossSiteRedirects, false, true, originAttributesList,
203 foundCookieList);
204 nsTArray<CookieStructTable> matchingCookiesListTable;
205 SerializeCookieListTable(foundCookieList, matchingCookiesListTable, uri);
206 Unused << SendTrackCookiesLoad(matchingCookiesListTable);
209 // we append outgoing cookie info into a list here so the ContentParent can
210 // filter cookies passing to unnecessary ContentProcesses
211 void CookieServiceParent::UpdateCookieInContentList(
212 nsIURI* uri, const OriginAttributes& originAttrs) {
213 nsCString baseDomain;
214 bool requireAHostMatch = false;
216 // prevent malformed urls from being added to the cookie list
217 if (NS_WARN_IF(NS_FAILED(CookieCommons::GetBaseDomain(
218 mTLDService, uri, baseDomain, requireAHostMatch)))) {
219 return;
222 CookieKey cookieKey(baseDomain, originAttrs);
223 bool& allowSecure = mCookieKeysInContent.LookupOrInsert(cookieKey, false);
224 allowSecure =
225 allowSecure || nsMixedContentBlocker::IsPotentiallyTrustworthyOrigin(uri);
228 // static
229 void CookieServiceParent::SerializeCookieListTable(
230 const nsTArray<RefPtr<Cookie>>& aFoundCookieList,
231 nsTArray<CookieStructTable>& aCookiesListTable, nsIURI* aHostURI) {
232 // Stores the index in aCookiesListTable by origin attributes suffix.
233 nsTHashMap<nsCStringHashKey, size_t> cookieListTable;
235 for (Cookie* cookie : aFoundCookieList) {
236 nsAutoCString attrsSuffix;
237 cookie->OriginAttributesRef().CreateSuffix(attrsSuffix);
238 size_t tableIndex = cookieListTable.LookupOrInsertWith(attrsSuffix, [&] {
239 size_t index = aCookiesListTable.Length();
240 CookieStructTable* newTable = aCookiesListTable.AppendElement();
241 newTable->attrs() = cookie->OriginAttributesRef();
242 return index;
245 CookieStruct* cookieStruct =
246 aCookiesListTable[tableIndex].cookies().AppendElement();
247 *cookieStruct = cookie->ToIPC();
249 // clear http-only cookie values
250 if (cookie->IsHttpOnly()) {
251 // Value only needs to exist if an HttpOnly cookie exists.
252 cookieStruct->value() = "";
255 // clear secure cookie values in insecure context
256 bool potentiallyTurstworthy =
257 nsMixedContentBlocker::IsPotentiallyTrustworthyOrigin(aHostURI);
258 if (cookie->IsSecure() && !potentiallyTurstworthy) {
259 cookieStruct->value() = "";
264 IPCResult CookieServiceParent::RecvGetCookieList(
265 nsIURI* aHost, const bool& aIsForeign,
266 const bool& aIsThirdPartyTrackingResource,
267 const bool& aIsThirdPartySocialTrackingResource,
268 const bool& aStorageAccessPermissionGranted,
269 const uint32_t& aRejectedReason, const bool& aIsSafeTopLevelNav,
270 const bool& aIsSameSiteForeign, const bool& aHadCrossSiteRedirects,
271 nsTArray<OriginAttributes>&& aAttrsList, GetCookieListResolver&& aResolve) {
272 // Send matching cookies to Child.
273 if (!aHost) {
274 return IPC_FAIL(this, "aHost must not be null");
277 // we append outgoing cookie info into a list here so the ContentParent can
278 // filter cookies that do not need to go to certain ContentProcesses
279 for (const auto& attrs : aAttrsList) {
280 UpdateCookieInContentList(aHost, attrs);
283 nsTArray<RefPtr<Cookie>> foundCookieList;
284 // Note: passing nullptr as aChannel to GetCookiesForURI() here is fine since
285 // this argument is only used for proper reporting of cookie loads, but the
286 // child process already does the necessary reporting in this case for us.
287 mCookieService->GetCookiesForURI(
288 aHost, nullptr, aIsForeign, aIsThirdPartyTrackingResource,
289 aIsThirdPartySocialTrackingResource, aStorageAccessPermissionGranted,
290 aRejectedReason, aIsSafeTopLevelNav, aIsSameSiteForeign,
291 aHadCrossSiteRedirects, false, true, aAttrsList, foundCookieList);
293 nsTArray<CookieStructTable> matchingCookiesListTable;
294 SerializeCookieListTable(foundCookieList, matchingCookiesListTable, aHost);
296 aResolve(matchingCookiesListTable);
298 return IPC_OK();
301 void CookieServiceParent::ActorDestroy(ActorDestroyReason aWhy) {
302 // Nothing needed here. Called right before destructor since this is a
303 // non-refcounted class.
306 IPCResult CookieServiceParent::RecvSetCookies(
307 const nsCString& aBaseDomain, const OriginAttributes& aOriginAttributes,
308 nsIURI* aHost, bool aFromHttp, bool aIsThirdParty,
309 const nsTArray<CookieStruct>& aCookies) {
310 if (!ContentProcessHasCookie(aBaseDomain, aOriginAttributes)) {
311 return IPC_FAIL(this, "Invalid set-cookie request from content process");
314 return SetCookies(aBaseDomain, aOriginAttributes, aHost, aFromHttp,
315 aIsThirdParty, aCookies);
318 IPCResult CookieServiceParent::SetCookies(
319 const nsCString& aBaseDomain, const OriginAttributes& aOriginAttributes,
320 nsIURI* aHost, bool aFromHttp, bool aIsThirdParty,
321 const nsTArray<CookieStruct>& aCookies,
322 dom::BrowsingContext* aBrowsingContext) {
323 if (!mCookieService) {
324 return IPC_OK();
327 // Deserialize URI. Having a host URI is mandatory and should always be
328 // provided by the child; thus we consider failure fatal.
329 if (!aHost) {
330 return IPC_FAIL(this, "aHost must not be null");
333 // We set this to true while processing this cookie update, to make sure
334 // we don't send it back to the same content process.
335 mProcessingCookie = true;
337 bool ok = mCookieService->SetCookiesFromIPC(aBaseDomain, aOriginAttributes,
338 aHost, aFromHttp, aIsThirdParty,
339 aCookies, aBrowsingContext);
340 mProcessingCookie = false;
341 return ok ? IPC_OK() : IPC_FAIL(this, "Invalid cookie received.");
344 } // namespace net
345 } // namespace mozilla