Bug 1330890 - Part 1: Spoofing the time zone as UTC when fingerprinting resistance is enabled (adopt from Tor #16622). r=arthuredelstein,Ehsan
authorTim Huang <tihuang@mozilla.com>
Tue, 02 May 2017 15:21:04 +0800
changeset 356970 0fedf8c86ceb3d306ab6f655012d22303e10993f
parent 356969 6b8416bae412867ce8694485059d87837f252fee
child 356971 80144f502fb63d35755c1601f647637c31368765
push id31777
push usercbook@mozilla.com
push dateMon, 08 May 2017 08:04:08 +0000
treeherdermozilla-central@81977c96c6ff [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersarthuredelstein, Ehsan
bugs1330890, 16622
milestone55.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1330890 - Part 1: Spoofing the time zone as UTC when fingerprinting resistance is enabled (adopt from Tor #16622). r=arthuredelstein,Ehsan This patch adds a new service for fingerprinting resistance, which is called nsRFPService. This service will be put in /toolkit/components/resistfingerprinting. This service is responsible for observing the change of pref 'privacy.resistfingerprinting' and doing underlying jobs. And it also in charge of caching pref setting of 'privacy.resistfingerprinting' and changing environment value 'TZ'. This service will be initialized within nsContentUtils::Init(). During initialization, it will store the original TZ value and set the value according to 'privacy.resistfingerprinting'. It also changes environment value 'TZ' and calls nsJSUtils::ResetTimeZone() in response to the change of the pref. This service is only a nsIObserver for now. In the future, however, it will be responsible for more fingerprinting resistance jobs, like changing prefs after 'privacy.resistfingerprinting' is changed. The environment variable 'TZ' will be set to 'UTC' when 'privacy.resistFingerprinting' is true. By doing so, Firefox will use UTC as its local time zone instead of the default local time zone. This prevents a browser be fingerprinted through the local time zone. After the 'privacy.resistFingerprinting' is turned off, the service will restore 'TZ' back to the original TZ setting, the user's setting or the default system timezone. MozReview-Commit-ID: 8V47ZATgrKE
dom/base/nsContentUtils.cpp
dom/base/nsContentUtils.h
toolkit/components/moz.build
toolkit/components/resistfingerprinting/moz.build
toolkit/components/resistfingerprinting/nsRFPService.cpp
toolkit/components/resistfingerprinting/nsRFPService.h
--- a/dom/base/nsContentUtils.cpp
+++ b/dom/base/nsContentUtils.cpp
@@ -286,17 +286,16 @@ bool nsContentUtils::sTrustedFullScreenO
 bool nsContentUtils::sIsCutCopyAllowed = true;
 bool nsContentUtils::sIsFrameTimingPrefEnabled = false;
 bool nsContentUtils::sIsPerformanceTimingEnabled = false;
 bool nsContentUtils::sIsResourceTimingEnabled = false;
 bool nsContentUtils::sIsUserTimingLoggingEnabled = false;
 bool nsContentUtils::sIsExperimentalAutocompleteEnabled = false;
 bool nsContentUtils::sIsWebComponentsEnabled = false;
 bool nsContentUtils::sIsCustomElementsEnabled = false;
-bool nsContentUtils::sPrivacyResistFingerprinting = false;
 bool nsContentUtils::sSendPerformanceTimingNotifications = false;
 bool nsContentUtils::sUseActivityCursor = false;
 bool nsContentUtils::sAnimationsAPICoreEnabled = false;
 bool nsContentUtils::sAnimationsAPIElementAnimateEnabled = false;
 bool nsContentUtils::sGetBoxQuadsEnabled = false;
 bool nsContentUtils::sSkipCursorMoveForSameValueSet = false;
 bool nsContentUtils::sRequestIdleCallbackEnabled = false;
 
@@ -596,19 +595,16 @@ nsContentUtils::Init()
                                "dom.forms.autocomplete.experimental", false);
 
   Preferences::AddBoolVarCache(&sIsWebComponentsEnabled,
                                "dom.webcomponents.enabled", false);
 
   Preferences::AddBoolVarCache(&sIsCustomElementsEnabled,
                                "dom.webcomponents.customelements.enabled", false);
 
-  Preferences::AddBoolVarCache(&sPrivacyResistFingerprinting,
-                               "privacy.resistFingerprinting", false);
-
   Preferences::AddIntVarCache(&sPrivacyMaxInnerWidth,
                               "privacy.window.maxInnerWidth",
                               1000);
 
   Preferences::AddIntVarCache(&sPrivacyMaxInnerHeight,
                               "privacy.window.maxInnerHeight",
                               1000);
 
@@ -651,16 +647,18 @@ nsContentUtils::Init()
                                "dom.input.skip_cursor_move_for_same_value_set",
                                true);
 
   Preferences::AddBoolVarCache(&sRequestIdleCallbackEnabled,
                                "dom.requestIdleCallback.enabled", false);
 
   Element::InitCCCallbacks();
 
+  Unused << nsRFPService::GetOrCreate();
+
   nsCOMPtr<nsIUUIDGenerator> uuidGenerator =
     do_GetService("@mozilla.org/uuid-generator;1", &rv);
   if (NS_WARN_IF(NS_FAILED(rv))) {
     return rv;
   }
   uuidGenerator.forget(&sUUIDGenerator);
 
   sInitialized = true;
@@ -2188,27 +2186,27 @@ nsContentUtils::IsCallerChrome()
   // If the check failed, look for UniversalXPConnect on the cx compartment.
   return xpc::IsUniversalXPConnectEnabled(GetCurrentJSContext());
 }
 
 /* static */
 bool
 nsContentUtils::ShouldResistFingerprinting()
 {
-  return sPrivacyResistFingerprinting;
+  return nsRFPService::IsResistFingerprintingEnabled();
 }
 
 bool
 nsContentUtils::ShouldResistFingerprinting(nsIDocShell* aDocShell)
 {
   if (!aDocShell) {
     return false;
   }
   bool isChrome = nsContentUtils::IsChromeDoc(aDocShell->GetDocument());
-  return !isChrome && sPrivacyResistFingerprinting;
+  return !isChrome && nsRFPService::IsResistFingerprintingEnabled();
 }
 
 /* static */
 void
 nsContentUtils::CalcRoundedWindowSizeForResistingFingerprinting(int32_t  aChromeWidth,
                                                                 int32_t  aChromeHeight,
                                                                 int32_t  aScreenWidth,
                                                                 int32_t  aScreenHeight,
--- a/dom/base/nsContentUtils.h
+++ b/dom/base/nsContentUtils.h
@@ -33,16 +33,17 @@
 #include "mozilla/dom/ScriptSettings.h"
 #include "mozilla/FloatingPoint.h"
 #include "mozilla/net/ReferrerPolicy.h"
 #include "mozilla/Logging.h"
 #include "mozilla/NotNull.h"
 #include "nsIContentPolicy.h"
 #include "nsIDocument.h"
 #include "nsPIDOMWindow.h"
+#include "nsRFPService.h"
 
 #if defined(XP_WIN)
 // Undefine LoadImage to prevent naming conflict with Windows.
 #undef LoadImage
 #endif
 
 class imgICache;
 class imgIContainer;
@@ -2173,17 +2174,17 @@ public:
   /*
    * Returns true if the browser should attempt to prevent the given caller type
    * from collecting distinctive information about the browser that could
    * be used to "fingerprint" and track the user across websites.
    */
   static bool ResistFingerprinting(mozilla::dom::CallerType aCallerType)
   {
     return aCallerType != mozilla::dom::CallerType::System &&
-           sPrivacyResistFingerprinting;
+           mozilla::nsRFPService::IsResistFingerprintingEnabled();
   }
 
   /**
    * Returns true if the browser should show busy cursor when loading page.
    */
   static bool UseActivityCursor()
   {
     return sUseActivityCursor;
@@ -3047,17 +3048,16 @@ private:
   static uint32_t sHandlingInputTimeout;
   static bool sIsPerformanceTimingEnabled;
   static bool sIsResourceTimingEnabled;
   static bool sIsUserTimingLoggingEnabled;
   static bool sIsFrameTimingPrefEnabled;
   static bool sIsExperimentalAutocompleteEnabled;
   static bool sIsWebComponentsEnabled;
   static bool sIsCustomElementsEnabled;
-  static bool sPrivacyResistFingerprinting;
   static bool sSendPerformanceTimingNotifications;
   static bool sUseActivityCursor;
   static bool sAnimationsAPICoreEnabled;
   static bool sAnimationsAPIElementAnimateEnabled;
   static bool sGetBoxQuadsEnabled;
   static bool sSkipCursorMoveForSameValueSet;
   static bool sRequestIdleCallbackEnabled;
   static uint32_t sCookiesLifetimePolicy;
--- a/toolkit/components/moz.build
+++ b/toolkit/components/moz.build
@@ -48,16 +48,17 @@ DIRS += [
     'privatebrowsing',
     'processsingleton',
     'promiseworker',
     'prompts',
     'protobuf',
     'reader',
     'remotebrowserutils',
     'reflect',
+    'resistfingerprinting',
     'securityreporter',
     'startup',
     'statusfilter',
     'telemetry',
     'thumbnails',
     'timermanager',
     'tooltiptext',
     'typeaheadfind',
new file mode 100644
--- /dev/null
+++ b/toolkit/components/resistfingerprinting/moz.build
@@ -0,0 +1,15 @@
+# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
+# vim: set filetype=python:
+# 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/.
+
+UNIFIED_SOURCES += [
+    'nsRFPService.cpp',
+]
+
+FINAL_LIBRARY = 'xul'
+
+EXPORTS += [
+    'nsRFPService.h',
+]
new file mode 100644
--- /dev/null
+++ b/toolkit/components/resistfingerprinting/nsRFPService.cpp
@@ -0,0 +1,151 @@
+/* -*- 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 "nsRFPService.h"
+
+#include "mozilla/ClearOnShutdown.h"
+#include "mozilla/Preferences.h"
+#include "mozilla/Services.h"
+#include "mozilla/StaticPtr.h"
+
+#include "nsCOMPtr.h"
+#include "nsServiceManagerUtils.h"
+#include "nsString.h"
+
+#include "nsIObserverService.h"
+#include "nsIPrefBranch.h"
+#include "nsIPrefService.h"
+#include "nsJSUtils.h"
+
+#include "prenv.h"
+
+using namespace mozilla;
+
+#define RESIST_FINGERPRINTING_PREF "privacy.resistFingerprinting"
+
+NS_IMPL_ISUPPORTS(nsRFPService, nsIObserver)
+
+static StaticRefPtr<nsRFPService> sRFPService;
+static bool sInitialized = false;
+bool nsRFPService::sPrivacyResistFingerprinting = false;
+
+/* static */
+nsRFPService*
+nsRFPService::GetOrCreate()
+{
+  if (!sInitialized) {
+    sRFPService = new nsRFPService();
+    nsresult rv = sRFPService->Init();
+
+    if (NS_FAILED(rv)) {
+      sRFPService = nullptr;
+      return nullptr;
+    }
+
+    ClearOnShutdown(&sRFPService);
+    sInitialized = true;
+  }
+
+  return sRFPService;
+}
+
+nsresult
+nsRFPService::Init()
+{
+  MOZ_ASSERT(NS_IsMainThread());
+
+  nsresult rv;
+
+  nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
+  NS_ENSURE_TRUE(obs, NS_ERROR_NOT_AVAILABLE);
+
+  rv = obs->AddObserver(this, NS_XPCOM_SHUTDOWN_OBSERVER_ID, false);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  nsCOMPtr<nsIPrefBranch> prefs = do_GetService(NS_PREFSERVICE_CONTRACTID);
+  NS_ENSURE_TRUE(prefs, NS_ERROR_NOT_AVAILABLE);
+
+  rv = prefs->AddObserver(RESIST_FINGERPRINTING_PREF, this, false);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  // We backup the original TZ value here.
+  const char* tzValue = PR_GetEnv("TZ");
+  if (tzValue) {
+    mInitialTZValue = nsCString(tzValue);
+  }
+
+  // Call UpdatePref() here to cache the value of 'privacy.resistFingerprinting'
+  // and set the timezone.
+  UpdatePref();
+  return rv;
+}
+
+void
+nsRFPService::UpdatePref()
+{
+  sPrivacyResistFingerprinting = Preferences::GetBool(RESIST_FINGERPRINTING_PREF);
+
+  if (sPrivacyResistFingerprinting) {
+    PR_SetEnv("TZ=UTC");
+  } else if (sInitialized) {
+    // We will not touch the TZ value if 'privacy.resistFingerprinting' is false during
+    // the time of initialization.
+    if (!mInitialTZValue.IsEmpty()) {
+      nsAutoCString tzValue = NS_LITERAL_CSTRING("TZ=") + mInitialTZValue;
+      PR_SetEnv(tzValue.get());
+    } else {
+#if defined(XP_LINUX) || defined (XP_MACOSX)
+      // For POSIX like system, we reset the TZ to the /etc/localtime, which is the
+      // system timezone.
+      PR_SetEnv("TZ=:/etc/localtime");
+#else
+      // For Windows, we reset the TZ to an empty string. This will make Windows to use
+      // its system timezone.
+      PR_SetEnv("TZ=");
+#endif
+    }
+  }
+
+  // We don't have to call _tzset() here for Windows since the following
+  // function nsJSUtils::ResetTimeZone() will call it for us.
+  nsJSUtils::ResetTimeZone();
+}
+
+void
+nsRFPService::StartShutdown()
+{
+  MOZ_ASSERT(NS_IsMainThread());
+
+  nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
+
+  if (obs) {
+    obs->RemoveObserver(this, NS_XPCOM_SHUTDOWN_OBSERVER_ID);
+
+    nsCOMPtr<nsIPrefBranch> prefs = do_GetService(NS_PREFSERVICE_CONTRACTID);
+
+    if (prefs) {
+      prefs->RemoveObserver(RESIST_FINGERPRINTING_PREF, this);
+    }
+  }
+}
+
+NS_IMETHODIMP
+nsRFPService::Observe(nsISupports* aObject, const char* aTopic,
+                      const char16_t* aMessage)
+{
+  if (!strcmp(NS_PREFBRANCH_PREFCHANGE_TOPIC_ID, aTopic)) {
+    NS_ConvertUTF16toUTF8 pref(aMessage);
+
+    if (pref.EqualsLiteral(RESIST_FINGERPRINTING_PREF)) {
+      UpdatePref();
+    }
+  }
+
+  if (!strcmp(NS_XPCOM_SHUTDOWN_OBSERVER_ID, aTopic)) {
+    StartShutdown();
+  }
+
+  return NS_OK;
+}
new file mode 100644
--- /dev/null
+++ b/toolkit/components/resistfingerprinting/nsRFPService.h
@@ -0,0 +1,44 @@
+/* -*- 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/. */
+
+#ifndef __nsRFPService_h__
+#define __nsRFPService_h__
+
+#include "nsIObserver.h"
+
+#include "nsString.h"
+
+namespace mozilla {
+
+class nsRFPService final : public nsIObserver
+{
+public:
+  NS_DECL_ISUPPORTS
+  NS_DECL_NSIOBSERVER
+
+  static nsRFPService* GetOrCreate();
+  static bool IsResistFingerprintingEnabled()
+  {
+    return sPrivacyResistFingerprinting;
+  }
+
+private:
+  nsresult Init();
+
+  nsRFPService() {}
+
+  ~nsRFPService() {}
+
+  void UpdatePref();
+  void StartShutdown();
+
+  static bool sPrivacyResistFingerprinting;
+
+  nsCString mInitialTZValue;
+};
+
+} // mozilla namespace
+
+#endif /* __nsRFPService_h__ */