mirror of
				https://github.com/mozilla/gecko-dev.git
				synced 2025-11-04 10:18:41 +02:00 
			
		
		
		
	This patch implements a FingerprintingProtectionState cleaner that calls the clear functions to reset fingerprinting random key. Differential Revision: https://phabricator.services.mozilla.com/D196302
		
			
				
	
	
		
			319 lines
		
	
	
	
		
			8.2 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			319 lines
		
	
	
	
		
			8.2 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 | 
						|
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
 | 
						|
/* 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 "ClearSiteData.h"
 | 
						|
 | 
						|
#include "mozilla/net/HttpBaseChannel.h"
 | 
						|
#include "mozilla/OriginAttributes.h"
 | 
						|
#include "mozilla/Preferences.h"
 | 
						|
#include "mozilla/Services.h"
 | 
						|
#include "mozilla/Unused.h"
 | 
						|
#include "nsASCIIMask.h"
 | 
						|
#include "nsCharSeparatedTokenizer.h"
 | 
						|
#include "nsContentSecurityManager.h"
 | 
						|
#include "nsContentUtils.h"
 | 
						|
#include "nsIClearDataService.h"
 | 
						|
#include "nsIHttpChannel.h"
 | 
						|
#include "nsIHttpProtocolHandler.h"
 | 
						|
#include "nsIObserverService.h"
 | 
						|
#include "nsIPrincipal.h"
 | 
						|
#include "nsIScriptError.h"
 | 
						|
#include "nsIScriptSecurityManager.h"
 | 
						|
#include "nsNetUtil.h"
 | 
						|
 | 
						|
using namespace mozilla;
 | 
						|
 | 
						|
namespace {
 | 
						|
 | 
						|
StaticRefPtr<ClearSiteData> gClearSiteData;
 | 
						|
 | 
						|
}  // namespace
 | 
						|
 | 
						|
// This object is used to suspend/resume the channel.
 | 
						|
class ClearSiteData::PendingCleanupHolder final : public nsIClearDataCallback {
 | 
						|
 public:
 | 
						|
  NS_DECL_ISUPPORTS
 | 
						|
 | 
						|
  explicit PendingCleanupHolder(nsIHttpChannel* aChannel)
 | 
						|
      : mChannel(aChannel), mPendingOp(false) {}
 | 
						|
 | 
						|
  nsresult Start() {
 | 
						|
    MOZ_ASSERT(!mPendingOp);
 | 
						|
    nsresult rv = mChannel->Suspend();
 | 
						|
    if (NS_WARN_IF(NS_FAILED(rv))) {
 | 
						|
      return rv;
 | 
						|
    }
 | 
						|
 | 
						|
    mPendingOp = true;
 | 
						|
    return NS_OK;
 | 
						|
  }
 | 
						|
 | 
						|
  // nsIClearDataCallback interface
 | 
						|
 | 
						|
  NS_IMETHOD
 | 
						|
  OnDataDeleted(uint32_t aFailedFlags) override {
 | 
						|
    MOZ_ASSERT(mPendingOp);
 | 
						|
    mPendingOp = false;
 | 
						|
 | 
						|
    mChannel->Resume();
 | 
						|
    mChannel = nullptr;
 | 
						|
 | 
						|
    return NS_OK;
 | 
						|
  }
 | 
						|
 | 
						|
 private:
 | 
						|
  ~PendingCleanupHolder() {
 | 
						|
    if (mPendingOp) {
 | 
						|
      mChannel->Resume();
 | 
						|
    }
 | 
						|
  }
 | 
						|
 | 
						|
  nsCOMPtr<nsIHttpChannel> mChannel;
 | 
						|
  bool mPendingOp;
 | 
						|
};
 | 
						|
 | 
						|
NS_INTERFACE_MAP_BEGIN(ClearSiteData::PendingCleanupHolder)
 | 
						|
  NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIClearDataCallback)
 | 
						|
  NS_INTERFACE_MAP_ENTRY(nsIClearDataCallback)
 | 
						|
NS_INTERFACE_MAP_END
 | 
						|
 | 
						|
NS_IMPL_ADDREF(ClearSiteData::PendingCleanupHolder)
 | 
						|
NS_IMPL_RELEASE(ClearSiteData::PendingCleanupHolder)
 | 
						|
 | 
						|
/* static */
 | 
						|
void ClearSiteData::Initialize() {
 | 
						|
  MOZ_ASSERT(!gClearSiteData);
 | 
						|
  MOZ_ASSERT(NS_IsMainThread());
 | 
						|
 | 
						|
  if (!XRE_IsParentProcess()) {
 | 
						|
    return;
 | 
						|
  }
 | 
						|
 | 
						|
  RefPtr<ClearSiteData> service = new ClearSiteData();
 | 
						|
 | 
						|
  nsCOMPtr<nsIObserverService> obs = services::GetObserverService();
 | 
						|
  if (NS_WARN_IF(!obs)) {
 | 
						|
    return;
 | 
						|
  }
 | 
						|
 | 
						|
  obs->AddObserver(service, NS_HTTP_ON_EXAMINE_RESPONSE_TOPIC, false);
 | 
						|
  obs->AddObserver(service, NS_XPCOM_SHUTDOWN_OBSERVER_ID, false);
 | 
						|
  gClearSiteData = service;
 | 
						|
}
 | 
						|
 | 
						|
/* static */
 | 
						|
void ClearSiteData::Shutdown() {
 | 
						|
  MOZ_ASSERT(NS_IsMainThread());
 | 
						|
 | 
						|
  if (!gClearSiteData) {
 | 
						|
    return;
 | 
						|
  }
 | 
						|
 | 
						|
  RefPtr<ClearSiteData> service = gClearSiteData;
 | 
						|
  gClearSiteData = nullptr;
 | 
						|
 | 
						|
  nsCOMPtr<nsIObserverService> obs = services::GetObserverService();
 | 
						|
  if (NS_WARN_IF(!obs)) {
 | 
						|
    return;
 | 
						|
  }
 | 
						|
 | 
						|
  obs->RemoveObserver(service, NS_HTTP_ON_EXAMINE_RESPONSE_TOPIC);
 | 
						|
  obs->RemoveObserver(service, NS_XPCOM_SHUTDOWN_OBSERVER_ID);
 | 
						|
}
 | 
						|
 | 
						|
ClearSiteData::ClearSiteData() = default;
 | 
						|
ClearSiteData::~ClearSiteData() = default;
 | 
						|
 | 
						|
NS_IMETHODIMP
 | 
						|
ClearSiteData::Observe(nsISupports* aSubject, const char* aTopic,
 | 
						|
                       const char16_t* aData) {
 | 
						|
  if (!strcmp(aTopic, NS_XPCOM_SHUTDOWN_OBSERVER_ID)) {
 | 
						|
    Shutdown();
 | 
						|
    return NS_OK;
 | 
						|
  }
 | 
						|
 | 
						|
  MOZ_ASSERT(!strcmp(aTopic, NS_HTTP_ON_EXAMINE_RESPONSE_TOPIC));
 | 
						|
 | 
						|
  nsCOMPtr<nsIHttpChannel> channel = do_QueryInterface(aSubject);
 | 
						|
  if (NS_WARN_IF(!channel)) {
 | 
						|
    return NS_OK;
 | 
						|
  }
 | 
						|
 | 
						|
  ClearDataFromChannel(channel);
 | 
						|
  return NS_OK;
 | 
						|
}
 | 
						|
 | 
						|
void ClearSiteData::ClearDataFromChannel(nsIHttpChannel* aChannel) {
 | 
						|
  MOZ_ASSERT(aChannel);
 | 
						|
 | 
						|
  nsresult rv;
 | 
						|
  nsCOMPtr<nsIURI> uri;
 | 
						|
 | 
						|
  nsIScriptSecurityManager* ssm = nsContentUtils::GetSecurityManager();
 | 
						|
  if (NS_WARN_IF(!ssm)) {
 | 
						|
    return;
 | 
						|
  }
 | 
						|
 | 
						|
  nsCOMPtr<nsIPrincipal> principal;
 | 
						|
  rv = ssm->GetChannelResultStoragePrincipal(aChannel,
 | 
						|
                                             getter_AddRefs(principal));
 | 
						|
  if (NS_WARN_IF(NS_FAILED(rv) || !principal)) {
 | 
						|
    return;
 | 
						|
  }
 | 
						|
 | 
						|
  bool secure = principal->GetIsOriginPotentiallyTrustworthy();
 | 
						|
  if (NS_WARN_IF(NS_FAILED(rv)) || !secure) {
 | 
						|
    return;
 | 
						|
  }
 | 
						|
 | 
						|
  // We want to use the final URI to check if Clear-Site-Data should be allowed
 | 
						|
  // or not.
 | 
						|
  rv = aChannel->GetURI(getter_AddRefs(uri));
 | 
						|
  if (NS_WARN_IF(NS_FAILED(rv))) {
 | 
						|
    return;
 | 
						|
  }
 | 
						|
 | 
						|
  uint32_t flags = ParseHeader(aChannel, uri);
 | 
						|
  if (flags == 0) {
 | 
						|
    // Nothing to do.
 | 
						|
    return;
 | 
						|
  }
 | 
						|
 | 
						|
  int32_t cleanFlags = 0;
 | 
						|
  RefPtr<PendingCleanupHolder> holder = new PendingCleanupHolder(aChannel);
 | 
						|
 | 
						|
  if (flags & eCookies) {
 | 
						|
    LogOpToConsole(aChannel, uri, eCookies);
 | 
						|
    cleanFlags |= nsIClearDataService::CLEAR_COOKIES |
 | 
						|
                  nsIClearDataService::CLEAR_COOKIE_BANNER_EXECUTED_RECORD |
 | 
						|
                  nsIClearDataService::CLEAR_FINGERPRINTING_PROTECTION_STATE;
 | 
						|
  }
 | 
						|
 | 
						|
  if (flags & eStorage) {
 | 
						|
    LogOpToConsole(aChannel, uri, eStorage);
 | 
						|
    cleanFlags |= nsIClearDataService::CLEAR_DOM_STORAGES |
 | 
						|
                  nsIClearDataService::CLEAR_COOKIE_BANNER_EXECUTED_RECORD |
 | 
						|
                  nsIClearDataService::CLEAR_FINGERPRINTING_PROTECTION_STATE;
 | 
						|
  }
 | 
						|
 | 
						|
  if (cleanFlags) {
 | 
						|
    nsCOMPtr<nsIClearDataService> csd =
 | 
						|
        do_GetService("@mozilla.org/clear-data-service;1");
 | 
						|
    MOZ_ASSERT(csd);
 | 
						|
 | 
						|
    rv = holder->Start();
 | 
						|
    if (NS_WARN_IF(NS_FAILED(rv))) {
 | 
						|
      return;
 | 
						|
    }
 | 
						|
 | 
						|
    rv = csd->DeleteDataFromPrincipal(principal, false /* user request */,
 | 
						|
                                      cleanFlags, holder);
 | 
						|
    if (NS_WARN_IF(NS_FAILED(rv))) {
 | 
						|
      return;
 | 
						|
    }
 | 
						|
  }
 | 
						|
}
 | 
						|
 | 
						|
uint32_t ClearSiteData::ParseHeader(nsIHttpChannel* aChannel,
 | 
						|
                                    nsIURI* aURI) const {
 | 
						|
  MOZ_ASSERT(aChannel);
 | 
						|
 | 
						|
  nsAutoCString headerValue;
 | 
						|
  nsresult rv = aChannel->GetResponseHeader("Clear-Site-Data"_ns, headerValue);
 | 
						|
  if (NS_FAILED(rv)) {
 | 
						|
    return 0;
 | 
						|
  }
 | 
						|
 | 
						|
  uint32_t flags = 0;
 | 
						|
 | 
						|
  for (auto value : nsCCharSeparatedTokenizer(headerValue, ',').ToRange()) {
 | 
						|
    // XXX This seems unnecessary, since the tokenizer already strips whitespace
 | 
						|
    // around tokens.
 | 
						|
    value.StripTaggedASCII(mozilla::ASCIIMask::MaskWhitespace());
 | 
						|
 | 
						|
    if (value.EqualsLiteral("\"cookies\"")) {
 | 
						|
      flags |= eCookies;
 | 
						|
      continue;
 | 
						|
    }
 | 
						|
 | 
						|
    if (value.EqualsLiteral("\"storage\"")) {
 | 
						|
      flags |= eStorage;
 | 
						|
      continue;
 | 
						|
    }
 | 
						|
 | 
						|
    if (value.EqualsLiteral("\"*\"")) {
 | 
						|
      flags = eCookies | eStorage;
 | 
						|
      break;
 | 
						|
    }
 | 
						|
 | 
						|
    LogErrorToConsole(aChannel, aURI, value);
 | 
						|
  }
 | 
						|
 | 
						|
  return flags;
 | 
						|
}
 | 
						|
 | 
						|
void ClearSiteData::LogOpToConsole(nsIHttpChannel* aChannel, nsIURI* aURI,
 | 
						|
                                   Type aType) const {
 | 
						|
  nsAutoString type;
 | 
						|
  TypeToString(aType, type);
 | 
						|
 | 
						|
  nsTArray<nsString> params;
 | 
						|
  params.AppendElement(type);
 | 
						|
 | 
						|
  LogToConsoleInternal(aChannel, aURI, "RunningClearSiteDataValue", params);
 | 
						|
}
 | 
						|
 | 
						|
void ClearSiteData::LogErrorToConsole(nsIHttpChannel* aChannel, nsIURI* aURI,
 | 
						|
                                      const nsACString& aUnknownType) const {
 | 
						|
  nsTArray<nsString> params;
 | 
						|
  params.AppendElement(NS_ConvertUTF8toUTF16(aUnknownType));
 | 
						|
 | 
						|
  LogToConsoleInternal(aChannel, aURI, "UnknownClearSiteDataValue", params);
 | 
						|
}
 | 
						|
 | 
						|
void ClearSiteData::LogToConsoleInternal(
 | 
						|
    nsIHttpChannel* aChannel, nsIURI* aURI, const char* aMsg,
 | 
						|
    const nsTArray<nsString>& aParams) const {
 | 
						|
  MOZ_ASSERT(aChannel);
 | 
						|
 | 
						|
  nsCOMPtr<net::HttpBaseChannel> httpChannel = do_QueryInterface(aChannel);
 | 
						|
  if (!httpChannel) {
 | 
						|
    return;
 | 
						|
  }
 | 
						|
 | 
						|
  nsAutoCString uri;
 | 
						|
  nsresult rv = aURI->GetSpec(uri);
 | 
						|
  if (NS_WARN_IF(NS_FAILED(rv))) {
 | 
						|
    return;
 | 
						|
  }
 | 
						|
 | 
						|
  httpChannel->AddConsoleReport(nsIScriptError::infoFlag, "Clear-Site-Data"_ns,
 | 
						|
                                nsContentUtils::eSECURITY_PROPERTIES, uri, 0, 0,
 | 
						|
                                nsDependentCString(aMsg), aParams);
 | 
						|
}
 | 
						|
 | 
						|
void ClearSiteData::TypeToString(Type aType, nsAString& aStr) const {
 | 
						|
  switch (aType) {
 | 
						|
    case eCookies:
 | 
						|
      aStr.AssignLiteral("cookies");
 | 
						|
      break;
 | 
						|
 | 
						|
    case eStorage:
 | 
						|
      aStr.AssignLiteral("storage");
 | 
						|
      break;
 | 
						|
 | 
						|
    default:
 | 
						|
      MOZ_CRASH("Unknown type.");
 | 
						|
  }
 | 
						|
}
 | 
						|
 | 
						|
NS_INTERFACE_MAP_BEGIN(ClearSiteData)
 | 
						|
  NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIObserver)
 | 
						|
  NS_INTERFACE_MAP_ENTRY(nsIObserver)
 | 
						|
NS_INTERFACE_MAP_END
 | 
						|
 | 
						|
NS_IMPL_ADDREF(ClearSiteData)
 | 
						|
NS_IMPL_RELEASE(ClearSiteData)
 |