/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* 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/. */ #include "mozilla/net/CookieServiceParent.h" #include "mozilla/dom/PContentParent.h" #include "mozilla/net/NeckoParent.h" #include "mozilla/BasePrincipal.h" #include "mozilla/ipc/URIUtils.h" #include "nsArrayUtils.h" #include "nsCookieService.h" #include "nsIChannel.h" #include "nsIEffectiveTLDService.h" #include "nsIScriptSecurityManager.h" #include "nsIPrivateBrowsingChannel.h" #include "nsNetCID.h" #include "nsPrintfCString.h" using namespace mozilla::ipc; using mozilla::BasePrincipal; using mozilla::OriginAttributes; using mozilla::dom::PContentParent; using mozilla::net::NeckoParent; namespace { // Ignore failures from this function, as they only affect whether we do or // don't show a dialog box in private browsing mode if the user sets a pref. void CreateDummyChannel(nsIURI* aHostURI, OriginAttributes& aAttrs, nsIChannel** aChannel) { nsCOMPtr principal = BasePrincipal::CreateCodebasePrincipal(aHostURI, aAttrs); if (!principal) { return; } nsCOMPtr dummyURI; nsresult rv = NS_NewURI(getter_AddRefs(dummyURI), "about:blank"); if (NS_FAILED(rv)) { return; } // The following channel is never openend, so it does not matter what // securityFlags we pass; let's follow the principle of least privilege. nsCOMPtr dummyChannel; NS_NewChannel(getter_AddRefs(dummyChannel), dummyURI, principal, nsILoadInfo::SEC_REQUIRE_SAME_ORIGIN_DATA_IS_BLOCKED, nsIContentPolicy::TYPE_INVALID); nsCOMPtr pbChannel = do_QueryInterface(dummyChannel); if (!pbChannel) { return; } pbChannel->SetPrivate(aAttrs.mPrivateBrowsingId > 0); dummyChannel.forget(aChannel); } } namespace mozilla { namespace net { CookieServiceParent::CookieServiceParent() { // Instantiate the cookieservice via the service manager, so it sticks around // until shutdown. nsCOMPtr cs = do_GetService(NS_COOKIESERVICE_CONTRACTID); // Get the nsCookieService instance directly, so we can call internal methods. mCookieService = nsCookieService::GetSingleton(); NS_ASSERTION(mCookieService, "couldn't get nsICookieService"); } CookieServiceParent::~CookieServiceParent() { } void GetInfoFromCookie(nsCookie *aCookie, CookieStruct &aCookieStruct) { aCookieStruct.name() = aCookie->Name(); aCookieStruct.value() = aCookie->Value(); aCookieStruct.host() = aCookie->Host(); aCookieStruct.path() = aCookie->Path(); aCookieStruct.expiry() = aCookie->Expiry(); aCookieStruct.lastAccessed() = aCookie->LastAccessed(); aCookieStruct.creationTime() = aCookie->CreationTime(); aCookieStruct.isSession() = aCookie->IsSession(); aCookieStruct.isSecure() = aCookie->IsSecure(); aCookieStruct.isHttpOnly() = aCookie->IsHttpOnly(); aCookieStruct.sameSite() = aCookie->SameSite(); } void CookieServiceParent::RemoveBatchDeletedCookies(nsIArray *aCookieList) { uint32_t len = 0; aCookieList->GetLength(&len); OriginAttributes attrs; CookieStruct cookieStruct; nsTArray cookieStructList; nsTArray attrsList; for (uint32_t i = 0; i < len; i++) { nsCOMPtr xpcCookie = do_QueryElementAt(aCookieList, i); auto cookie = static_cast(xpcCookie.get()); attrs = cookie->OriginAttributesRef(); GetInfoFromCookie(cookie, cookieStruct); if (!cookie->IsHttpOnly()) { cookieStructList.AppendElement(cookieStruct); attrsList.AppendElement(attrs); } } Unused << SendRemoveBatchDeletedCookies(cookieStructList, attrsList); } void CookieServiceParent::RemoveAll() { Unused << SendRemoveAll(); } void CookieServiceParent::RemoveCookie(nsICookie *aCookie) { auto cookie = static_cast(aCookie); OriginAttributes attrs = cookie->OriginAttributesRef(); CookieStruct cookieStruct; GetInfoFromCookie(cookie, cookieStruct); if (!cookie->IsHttpOnly()) { Unused << SendRemoveCookie(cookieStruct, attrs); } } void CookieServiceParent::AddCookie(nsICookie *aCookie) { auto cookie = static_cast(aCookie); OriginAttributes attrs = cookie->OriginAttributesRef(); CookieStruct cookieStruct; GetInfoFromCookie(cookie, cookieStruct); Unused << SendAddCookie(cookieStruct, attrs); } void CookieServiceParent::TrackCookieLoad(nsIChannel *aChannel) { nsCOMPtr uri; aChannel->GetURI(getter_AddRefs(uri)); nsCOMPtr loadInfo = aChannel->GetLoadInfo(); mozilla::OriginAttributes attrs; if (loadInfo) { attrs = loadInfo->GetOriginAttributes(); } // Send matching cookies to Child. nsCOMPtr thirdPartyUtil; thirdPartyUtil = do_GetService(THIRDPARTYUTIL_CONTRACTID); bool isForeign = true; thirdPartyUtil->IsThirdPartyChannel(aChannel, uri, &isForeign); nsTArray foundCookieList; mCookieService->GetCookiesForURI(uri, isForeign, false, attrs, foundCookieList); nsTArray matchingCookiesList; SerialializeCookieList(foundCookieList, matchingCookiesList, uri); Unused << SendTrackCookiesLoad(matchingCookiesList, attrs); } void CookieServiceParent::SerialializeCookieList(const nsTArray &aFoundCookieList, nsTArray &aCookiesList, nsIURI *aHostURI) { for (uint32_t i = 0; i < aFoundCookieList.Length(); i++) { nsCookie *cookie = aFoundCookieList.ElementAt(i); CookieStruct* cookieStruct = aCookiesList.AppendElement(); cookieStruct->name() = cookie->Name(); cookieStruct->value() = cookie->Value(); cookieStruct->host() = cookie->Host(); cookieStruct->path() = cookie->Path(); cookieStruct->expiry() = cookie->Expiry(); cookieStruct->lastAccessed() = cookie->LastAccessed(); cookieStruct->creationTime() = cookie->CreationTime(); cookieStruct->isSession() = cookie->IsSession(); cookieStruct->isSecure() = cookie->IsSecure(); cookieStruct->sameSite() = cookie->SameSite(); } } mozilla::ipc::IPCResult CookieServiceParent::RecvPrepareCookieList(const URIParams &aHost, const bool &aIsForeign, const OriginAttributes &aAttrs) { nsCOMPtr hostURI = DeserializeURI(aHost); // Send matching cookies to Child. nsTArray foundCookieList; mCookieService->GetCookiesForURI(hostURI, aIsForeign, false, aAttrs, foundCookieList); nsTArray matchingCookiesList; SerialializeCookieList(foundCookieList, matchingCookiesList, hostURI); Unused << SendTrackCookiesLoad(matchingCookiesList, aAttrs); return IPC_OK(); } void CookieServiceParent::ActorDestroy(ActorDestroyReason aWhy) { // Nothing needed here. Called right before destructor since this is a // non-refcounted class. } mozilla::ipc::IPCResult CookieServiceParent::RecvGetCookieString(const URIParams& aHost, const bool& aIsForeign, const OriginAttributes& aAttrs, nsCString* aResult) { if (!mCookieService) return IPC_OK(); // Deserialize URI. Having a host URI is mandatory and should always be // provided by the child; thus we consider failure fatal. nsCOMPtr hostURI = DeserializeURI(aHost); if (!hostURI) return IPC_FAIL_NO_REASON(this); mCookieService->GetCookieStringInternal(hostURI, aIsForeign, false, aAttrs, *aResult); return IPC_OK(); } mozilla::ipc::IPCResult CookieServiceParent::RecvSetCookieString(const URIParams& aHost, const bool& aIsForeign, const nsCString& aCookieString, const nsCString& aServerTime, const OriginAttributes& aAttrs, const bool& aFromHttp) { if (!mCookieService) return IPC_OK(); // Deserialize URI. Having a host URI is mandatory and should always be // provided by the child; thus we consider failure fatal. nsCOMPtr hostURI = DeserializeURI(aHost); if (!hostURI) return IPC_FAIL_NO_REASON(this); // This is a gross hack. We've already computed everything we need to know // for whether to set this cookie or not, but we need to communicate all of // this information through to nsICookiePermission, which indirectly // computes the information from the channel. We only care about the // aIsPrivate argument as nsCookieService::SetCookieStringInternal deals // with aIsForeign before we have to worry about nsCookiePermission trying // to use the channel to inspect it. nsCOMPtr dummyChannel; CreateDummyChannel(hostURI, const_cast(aAttrs), getter_AddRefs(dummyChannel)); // NB: dummyChannel could be null if something failed in CreateDummyChannel. nsDependentCString cookieString(aCookieString, 0); mCookieService->SetCookieStringInternal(hostURI, aIsForeign, cookieString, aServerTime, aFromHttp, true, aAttrs, dummyChannel); return IPC_OK(); } } // namespace net } // namespace mozilla