tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

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