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/net/CookieService.h"
10 #include "mozilla/net/CookieServiceParent.h"
11 #include "mozilla/net/NeckoParent.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 "nsIEffectiveTLDService.h"
20 #include "nsMixedContentBlocker.h"
22 using namespace mozilla::ipc
;
27 CookieServiceParent::CookieServiceParent() {
28 // Instantiate the cookieservice via the service manager, so it sticks around
30 nsCOMPtr
<nsICookieService
> cs
= do_GetService(NS_COOKIESERVICE_CONTRACTID
);
32 // Get the CookieService instance directly, so we can call internal methods.
33 mCookieService
= CookieService::GetSingleton();
34 NS_ASSERTION(mCookieService
, "couldn't get nsICookieService");
36 mTLDService
= do_GetService(NS_EFFECTIVETLDSERVICE_CONTRACTID
);
37 MOZ_ALWAYS_TRUE(mTLDService
);
39 mProcessingCookie
= false;
42 void CookieServiceParent::RemoveBatchDeletedCookies(nsIArray
* aCookieList
) {
44 aCookieList
->GetLength(&len
);
45 OriginAttributes attrs
;
46 CookieStruct cookieStruct
;
47 nsTArray
<CookieStruct
> cookieStructList
;
48 nsTArray
<OriginAttributes
> attrsList
;
49 for (uint32_t i
= 0; i
< len
; i
++) {
50 nsCOMPtr
<nsICookie
> xpcCookie
= do_QueryElementAt(aCookieList
, i
);
51 const auto& cookie
= xpcCookie
->AsCookie();
52 attrs
= cookie
.OriginAttributesRef();
53 cookieStruct
= cookie
.ToIPC();
55 // Child only needs to know HttpOnly cookies exists, not its value
56 // Same for Secure cookies going to a process for an insecure site.
57 if (cookie
.IsHttpOnly() || !InsecureCookieOrSecureOrigin(cookie
)) {
58 cookieStruct
.value() = "";
60 cookieStructList
.AppendElement(cookieStruct
);
61 attrsList
.AppendElement(attrs
);
63 Unused
<< SendRemoveBatchDeletedCookies(cookieStructList
, attrsList
);
66 void CookieServiceParent::RemoveAll() { Unused
<< SendRemoveAll(); }
68 void CookieServiceParent::RemoveCookie(const Cookie
& cookie
) {
69 const OriginAttributes
& attrs
= cookie
.OriginAttributesRef();
70 CookieStruct cookieStruct
= cookie
.ToIPC();
72 // Child only needs to know HttpOnly cookies exists, not its value
73 // Same for Secure cookies going to a process for an insecure site.
74 if (cookie
.IsHttpOnly() || !InsecureCookieOrSecureOrigin(cookie
)) {
75 cookieStruct
.value() = "";
77 Unused
<< SendRemoveCookie(cookieStruct
, attrs
);
80 void CookieServiceParent::AddCookie(const Cookie
& cookie
) {
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
<< SendAddCookie(cookieStruct
, attrs
);
92 bool CookieServiceParent::ContentProcessHasCookie(const Cookie
& cookie
) {
94 if (NS_WARN_IF(NS_FAILED(CookieCommons::GetBaseDomainFromHost(
95 mTLDService
, cookie
.Host(), baseDomain
)))) {
99 CookieKey
cookieKey(baseDomain
, cookie
.OriginAttributesRef());
100 return mCookieKeysInContent
.MaybeGet(cookieKey
).isSome();
103 bool CookieServiceParent::InsecureCookieOrSecureOrigin(const Cookie
& cookie
) {
104 nsCString baseDomain
;
105 // CookieStorage notifications triggering this won't fail to get base domain
106 MOZ_ALWAYS_SUCCEEDS(CookieCommons::GetBaseDomainFromHost(
107 mTLDService
, cookie
.Host(), baseDomain
));
109 // cookie is insecure or cookie is associated with a secure-origin process
110 CookieKey
cookieKey(baseDomain
, cookie
.OriginAttributesRef());
111 if (Maybe
<bool> allowSecure
= mCookieKeysInContent
.MaybeGet(cookieKey
)) {
112 return (!cookie
.IsSecure() || *allowSecure
);
117 void CookieServiceParent::TrackCookieLoad(nsIChannel
* aChannel
) {
118 nsCOMPtr
<nsIURI
> uri
;
119 aChannel
->GetURI(getter_AddRefs(uri
));
121 nsCOMPtr
<nsILoadInfo
> loadInfo
= aChannel
->LoadInfo();
122 OriginAttributes attrs
= loadInfo
->GetOriginAttributes();
123 bool isSafeTopLevelNav
= CookieCommons::IsSafeTopLevelNav(aChannel
);
124 bool hadCrossSiteRedirects
= false;
125 bool isSameSiteForeign
=
126 CookieCommons::IsSameSiteForeign(aChannel
, uri
, &hadCrossSiteRedirects
);
128 // TODO (Bug 1874174): A channel could load both unpartitioned and partitioned
129 // cookie jars together. We will need to track both originAttributes for them.
130 StoragePrincipalHelper::PrepareEffectiveStoragePrincipalOriginAttributes(
133 nsCOMPtr
<mozIThirdPartyUtil
> thirdPartyUtil
;
134 thirdPartyUtil
= do_GetService(THIRDPARTYUTIL_CONTRACTID
);
136 uint32_t rejectedReason
= 0;
137 ThirdPartyAnalysisResult result
= thirdPartyUtil
->AnalyzeChannel(
138 aChannel
, false, nullptr, nullptr, &rejectedReason
);
140 nsTArray
<OriginAttributes
> originAttributesList
;
141 originAttributesList
.AppendElement(attrs
);
143 for (auto& originAttributes
: originAttributesList
) {
144 UpdateCookieInContentList(uri
, originAttributes
);
147 // Send matching cookies to Child.
148 nsTArray
<Cookie
*> foundCookieList
;
149 mCookieService
->GetCookiesForURI(
150 uri
, aChannel
, result
.contains(ThirdPartyAnalysis::IsForeign
),
151 result
.contains(ThirdPartyAnalysis::IsThirdPartyTrackingResource
),
152 result
.contains(ThirdPartyAnalysis::IsThirdPartySocialTrackingResource
),
153 result
.contains(ThirdPartyAnalysis::IsStorageAccessPermissionGranted
),
154 rejectedReason
, isSafeTopLevelNav
, isSameSiteForeign
,
155 hadCrossSiteRedirects
, false, true, originAttributesList
,
157 nsTArray
<CookieStructTable
> matchingCookiesListTable
;
158 SerializeCookieListTable(foundCookieList
, matchingCookiesListTable
, uri
);
159 Unused
<< SendTrackCookiesLoad(matchingCookiesListTable
);
162 // we append outgoing cookie info into a list here so the ContentParent can
163 // filter cookies passing to unnecessary ContentProcesses
164 void CookieServiceParent::UpdateCookieInContentList(
165 nsIURI
* uri
, const OriginAttributes
& originAttrs
) {
166 nsCString baseDomain
;
167 bool requireAHostMatch
= false;
169 // prevent malformed urls from being added to the cookie list
170 if (NS_WARN_IF(NS_FAILED(CookieCommons::GetBaseDomain(
171 mTLDService
, uri
, baseDomain
, requireAHostMatch
)))) {
175 CookieKey
cookieKey(baseDomain
, originAttrs
);
176 bool& allowSecure
= mCookieKeysInContent
.LookupOrInsert(cookieKey
, false);
178 allowSecure
|| nsMixedContentBlocker::IsPotentiallyTrustworthyOrigin(uri
);
182 void CookieServiceParent::SerializeCookieListTable(
183 const nsTArray
<Cookie
*>& aFoundCookieList
,
184 nsTArray
<CookieStructTable
>& aCookiesListTable
, nsIURI
* aHostURI
) {
185 nsTHashMap
<nsCStringHashKey
, CookieStructTable
*> cookieListTable
;
187 for (Cookie
* cookie
: aFoundCookieList
) {
188 nsAutoCString attrsSuffix
;
189 cookie
->OriginAttributesRef().CreateSuffix(attrsSuffix
);
190 CookieStructTable
* table
=
191 cookieListTable
.LookupOrInsertWith(attrsSuffix
, [&] {
192 CookieStructTable
* newTable
= aCookiesListTable
.AppendElement();
193 newTable
->attrs() = cookie
->OriginAttributesRef();
197 CookieStruct
* cookieStruct
= table
->cookies().AppendElement();
198 *cookieStruct
= cookie
->ToIPC();
200 // clear http-only cookie values
201 if (cookie
->IsHttpOnly()) {
202 // Value only needs to exist if an HttpOnly cookie exists.
203 cookieStruct
->value() = "";
206 // clear secure cookie values in insecure context
207 bool potentiallyTurstworthy
=
208 nsMixedContentBlocker::IsPotentiallyTrustworthyOrigin(aHostURI
);
209 if (cookie
->IsSecure() && !potentiallyTurstworthy
) {
210 cookieStruct
->value() = "";
215 IPCResult
CookieServiceParent::RecvGetCookieList(
216 nsIURI
* aHost
, const bool& aIsForeign
,
217 const bool& aIsThirdPartyTrackingResource
,
218 const bool& aIsThirdPartySocialTrackingResource
,
219 const bool& aStorageAccessPermissionGranted
,
220 const uint32_t& aRejectedReason
, const bool& aIsSafeTopLevelNav
,
221 const bool& aIsSameSiteForeign
, const bool& aHadCrossSiteRedirects
,
222 nsTArray
<OriginAttributes
>&& aAttrsList
, GetCookieListResolver
&& aResolve
) {
223 // Send matching cookies to Child.
225 return IPC_FAIL(this, "aHost must not be null");
228 // we append outgoing cookie info into a list here so the ContentParent can
229 // filter cookies that do not need to go to certain ContentProcesses
230 for (const auto& attrs
: aAttrsList
) {
231 UpdateCookieInContentList(aHost
, attrs
);
234 nsTArray
<Cookie
*> foundCookieList
;
235 // Note: passing nullptr as aChannel to GetCookiesForURI() here is fine since
236 // this argument is only used for proper reporting of cookie loads, but the
237 // child process already does the necessary reporting in this case for us.
238 mCookieService
->GetCookiesForURI(
239 aHost
, nullptr, aIsForeign
, aIsThirdPartyTrackingResource
,
240 aIsThirdPartySocialTrackingResource
, aStorageAccessPermissionGranted
,
241 aRejectedReason
, aIsSafeTopLevelNav
, aIsSameSiteForeign
,
242 aHadCrossSiteRedirects
, false, true, aAttrsList
, foundCookieList
);
244 nsTArray
<CookieStructTable
> matchingCookiesListTable
;
245 SerializeCookieListTable(foundCookieList
, matchingCookiesListTable
, aHost
);
247 aResolve(matchingCookiesListTable
);
252 void CookieServiceParent::ActorDestroy(ActorDestroyReason aWhy
) {
253 // Nothing needed here. Called right before destructor since this is a
254 // non-refcounted class.
257 IPCResult
CookieServiceParent::RecvSetCookies(
258 const nsCString
& aBaseDomain
, const OriginAttributes
& aOriginAttributes
,
259 nsIURI
* aHost
, bool aFromHttp
, const nsTArray
<CookieStruct
>& aCookies
) {
260 return SetCookies(aBaseDomain
, aOriginAttributes
, aHost
, aFromHttp
, aCookies
);
263 IPCResult
CookieServiceParent::SetCookies(
264 const nsCString
& aBaseDomain
, const OriginAttributes
& aOriginAttributes
,
265 nsIURI
* aHost
, bool aFromHttp
, const nsTArray
<CookieStruct
>& aCookies
,
266 dom::BrowsingContext
* aBrowsingContext
) {
267 if (!mCookieService
) {
271 // Deserialize URI. Having a host URI is mandatory and should always be
272 // provided by the child; thus we consider failure fatal.
274 return IPC_FAIL(this, "aHost must not be null");
277 // We set this to true while processing this cookie update, to make sure
278 // we don't send it back to the same content process.
279 mProcessingCookie
= true;
282 mCookieService
->SetCookiesFromIPC(aBaseDomain
, aOriginAttributes
, aHost
,
283 aFromHttp
, aCookies
, aBrowsingContext
);
284 mProcessingCookie
= false;
285 return ok
? IPC_OK() : IPC_FAIL(this, "Invalid cookie received.");
289 } // namespace mozilla