[tbb-commits] [Git][tpo/applications/tor-browser][tor-browser-102.12.0esr-13.0-1] 12 commits: fixup! Bug 40933: Add tor-launcher functionality

richard (@richard) git at gitlab.torproject.org
Thu Jun 8 20:12:38 UTC 2023



richard pushed to branch tor-browser-102.12.0esr-13.0-1 at The Tor Project / Applications / Tor Browser


Commits:
2bf330d2 by Pier Angelo Vendrame at 2023-06-08T14:56:19+02:00
fixup! Bug 40933: Add tor-launcher functionality

Added a newnym function

- - - - -
ed24f026 by Pier Angelo Vendrame at 2023-06-08T14:56:20+02:00
fixup! Bug 10760: Integrate TorButton to TorBrowser core

Bug 40938: Moving the domain isolator out of torbutton

- - - - -
4e063afe by Arthur Edelstein at 2023-06-08T14:56:20+02:00
Bug 3455: Add DomainIsolator, for isolating circuit by domain.

Add an XPCOM component that registers a ProtocolProxyChannelFilter
which sets the username/password for each web request according to
url bar domain.

Bug 9442: Add New Circuit button

Bug 13766: Set a 10 minute circuit dirty timeout for the catch-all circ.

Bug 19206: Include a 128 bit random tag as part of the domain isolator nonce.

Bug 19206: Clear out the domain isolator state on `New Identity`.

Bug 21201.2: Isolate by firstPartyDomain from OriginAttributes

Bug 21745: Fix handling of catch-all circuit

Bug 41741: Refactor the domain isolator and new circuit

- - - - -
9018b2cd by Pier Angelo Vendrame at 2023-06-08T14:56:20+02:00
fixup! Bug 3455: Add DomainIsolator, for isolating circuit by domain.

Refactors to the old JS code.

- - - - -
47c03bfc by Pier Angelo Vendrame at 2023-06-08T14:56:21+02:00
fixup! Bug 3455: Add DomainIsolator, for isolating circuit by domain.

Manage NEWNYM here.

- - - - -
d33564dd by Pier Angelo Vendrame at 2023-06-08T14:56:21+02:00
fixup! Bug 3455: Add DomainIsolator, for isolating circuit by domain.

Removed the XPCOM definition of the domain isolator.

- - - - -
ae68e4b5 by Pier Angelo Vendrame at 2023-06-08T14:56:22+02:00
fixup! Bug 10760: Integrate TorButton to TorBrowser core

Extract the new identity button from torbutton

- - - - -
322c6af1 by Pier Angelo Vendrame at 2023-06-08T14:56:22+02:00
fixup! Bug 3455: Add DomainIsolator, for isolating circuit by domain.

Actually added the new circuit button.

- - - - -
1ba65aa4 by Pier Angelo Vendrame at 2023-06-08T14:56:22+02:00
fixup! Bug 41600: Add a tor circuit display panel.

Use the new domain isolator interface.

- - - - -
beec825e by Pier Angelo Vendrame at 2023-06-08T14:56:23+02:00
fixup! Bug 40209: Implement Basic Crypto Safety

Use the new domain isolator interface

- - - - -
e3478fc9 by Pier Angelo Vendrame at 2023-06-08T14:56:23+02:00
fixup! Bug 10760: Integrate TorButton to TorBrowser core

Remove string changes from Torbutton.
We will add them back in the TorStrings commit.

- - - - -
cf8bb1df by Pier Angelo Vendrame at 2023-06-08T14:56:24+02:00
fixup! Add TorStrings module for localization

Add our DTDs where needed.

These changes were originally in the torbutton commit, but I think they
are better fit here, with all the strings files.

- - - - -


15 changed files:

- browser/actors/CryptoSafetyParent.jsm
- browser/base/content/appmenu-viewcache.inc.xhtml
- browser/base/content/browser-menubar.inc
- browser/base/content/browser-sets.inc
- browser/base/content/browser.js
- browser/base/content/navigator-toolbox.inc.xhtml
- browser/components/torcircuit/content/torCircuitPanel.js
- + toolkit/components/tor-launcher/TorDomainIsolator.jsm
- toolkit/components/tor-launcher/TorProtocolService.jsm
- toolkit/components/tor-launcher/TorStartupService.jsm
- toolkit/components/tor-launcher/moz.build
- toolkit/torbutton/chrome/content/torbutton.js
- − toolkit/torbutton/components/domain-isolator.js
- toolkit/torbutton/jar.mn
- toolkit/torbutton/modules/utils.js


Changes:

=====================================
browser/actors/CryptoSafetyParent.jsm
=====================================
@@ -12,6 +12,12 @@ const { XPCOMUtils } = ChromeUtils.import(
   "resource://gre/modules/XPCOMUtils.jsm"
 );
 
+ChromeUtils.defineModuleGetter(
+  this,
+  "TorDomainIsolator",
+  "resource://gre/modules/TorDomainIsolator.jsm"
+);
+
 XPCOMUtils.defineLazyGetter(this, "cryptoSafetyBundle", () => {
   return Services.strings.createBundle(
     "chrome://browser/locale/cryptoSafetyPrompt.properties"
@@ -75,7 +81,11 @@ class CryptoSafetyParent extends JSWindowActorParent {
     );
 
     if (buttonPressed === 0) {
-      this.browsingContext.topChromeWindow.torbutton_new_circuit();
+      const { browsingContext } = this.manager;
+      const browser = browsingContext.embedderElement;
+      if (browser) {
+        TorDomainIsolator.newCircuitForBrowser(browser.ownerGlobal.gBrowser);
+      }
     }
   }
 }


=====================================
browser/base/content/appmenu-viewcache.inc.xhtml
=====================================
@@ -63,9 +63,9 @@
                      key="new-identity-key"/>
       <toolbarbutton id="appMenuNewCircuit"
                      class="subviewbutton"
-                     key="torbutton-new-circuit-key"
+                     key="new-circuit-key"
                      label="&torbutton.context_menu.new_circuit_sentence_case;"
-                     oncommand="torbutton_new_circuit();"/>
+                     oncommand="TorDomainIsolator.newCircuitForBrowser(gBrowser);"/>
       <toolbarseparator/>
       <toolbarbutton id="appMenu-bookmarks-button"
                      class="subviewbutton subviewbutton-nav"


=====================================
browser/base/content/browser-menubar.inc
=====================================
@@ -33,9 +33,9 @@
                           key="new-identity-key"/>
                 <menuitem id="menu_newCircuit"
                           accesskey="&torbutton.context_menu.new_circuit_key;"
-                          key="torbutton-new-circuit-key"
+                          key="new-circuit-key"
                           label="&torbutton.context_menu.new_circuit;"
-                          oncommand="torbutton_new_circuit();"/>
+                          oncommand="TorDomainIsolator.newCircuitForBrowser(gBrowser);"/>
                 <menuseparator/>
                 <menuitem id="menu_openLocation"
                           hidden="true"


=====================================
browser/base/content/browser-sets.inc
=====================================
@@ -389,5 +389,5 @@
          internal="true"/>
 #endif
     <key id="new-identity-key" modifiers="accel shift" key="U" oncommand="NewIdentityButton.onCommand(event)"/>
-    <key id="torbutton-new-circuit-key" modifiers="accel shift" key="L" oncommand="torbutton_new_circuit()"/>
+    <key id="new-circuit-key" modifiers="accel shift" key="L" oncommand="TorDomainIsolator.newCircuitForBrowser(gBrowser)"/>
   </keyset>


=====================================
browser/base/content/browser.js
=====================================
@@ -82,6 +82,7 @@ XPCOMUtils.defineLazyModuleGetters(this, {
   TorConnect: "resource:///modules/TorConnect.jsm",
   TorConnectState: "resource:///modules/TorConnect.jsm",
   TorConnectTopics: "resource:///modules/TorConnect.jsm",
+  TorDomainIsolator: "resource://gre/modules/TorDomainIsolator.jsm",
   Translation: "resource:///modules/translation/TranslationParent.jsm",
   UITour: "resource:///modules/UITour.jsm",
   UpdateUtils: "resource://gre/modules/UpdateUtils.jsm",


=====================================
browser/base/content/navigator-toolbox.inc.xhtml
=====================================
@@ -569,7 +569,7 @@
 
     <toolbarbutton id="new-circuit-button" class="toolbarbutton-1 chromeclass-toolbar-additional"
                    label="&torbutton.context_menu.new_circuit;"
-                   oncommand="torbutton_new_circuit();"
+                   oncommand="TorDomainIsolator.newCircuitForBrowser(gBrowser);"
                    tooltiptext="&torbutton.context_menu.new_circuit;"/>
 
     <toolbarbutton id="fullscreen-button" class="toolbarbutton-1 chromeclass-toolbar-additional"


=====================================
browser/components/torcircuit/content/torCircuitPanel.js
=====================================
@@ -193,7 +193,7 @@ var gTorCircuitPanel = {
     document
       .getElementById("tor-circuit-new-circuit")
       .addEventListener("command", () => {
-        torbutton_new_circuit();
+        TorDomainIsolator.newCircuitForBrowser(gBrowser);
         // And hide.
         // NOTE: focus should return to the toolbar button, which we expect to
         // remain visible during reload.
@@ -415,20 +415,14 @@ var gTorCircuitPanel = {
    */
   _updateCurrentBrowser(matchingCredentials = null) {
     const browser = gBrowser.selectedBrowser;
-    const { getDomainForBrowser } = ChromeUtils.import(
-      "resource://torbutton/modules/utils.js"
-    );
-    const domain = getDomainForBrowser(browser);
+    const domain = TorDomainIsolator.getDomainForBrowser(browser);
     // We choose the currentURI, which matches what is shown in the URL bar and
     // will match up with the domain.
     // In contrast, documentURI corresponds to the shown page. E.g. it could
     // point to "about:certerror".
     const scheme = browser.currentURI?.scheme;
 
-    const domainIsolator = Cc["@torproject.org/domain-isolator;1"].getService(
-      Ci.nsISupports
-    ).wrappedJSObject;
-    let credentials = domainIsolator.getSocksProxyCredentials(
+    let credentials = TorDomainIsolator.getSocksProxyCredentials(
       domain,
       browser.contentPrincipal.originAttributes.userContextId
     );


=====================================
toolkit/components/tor-launcher/TorDomainIsolator.jsm
=====================================
@@ -0,0 +1,362 @@
+// A component for Tor Browser that puts requests from different
+// first party domains on separate Tor circuits.
+
+var EXPORTED_SYMBOLS = ["TorDomainIsolator"];
+
+const { Services } = ChromeUtils.import("resource://gre/modules/Services.jsm");
+const { XPCOMUtils } = ChromeUtils.import(
+  "resource://gre/modules/XPCOMUtils.jsm"
+);
+const { ConsoleAPI } = ChromeUtils.import("resource://gre/modules/Console.jsm");
+
+Cu.importGlobalProperties(["crypto"]);
+
+XPCOMUtils.defineLazyServiceGetters(this, {
+  ProtocolProxyService: [
+    "@mozilla.org/network/protocol-proxy-service;1",
+    "nsIProtocolProxyService",
+  ],
+});
+
+ChromeUtils.defineModuleGetter(
+  this,
+  "TorProtocolService",
+  "resource://gre/modules/TorProtocolService.jsm"
+);
+
+const logger = new ConsoleAPI({
+  prefix: "TorDomainIsolator",
+  maxLogLevel: "warn",
+  maxLogLevelPref: "browser.tordomainisolator.loglevel",
+});
+
+// The string to use instead of the domain when it is not known.
+const CATCHALL_DOMAIN = "--unknown--";
+
+// The preference to observe, to know whether isolation should be enabled or
+// disabled.
+const NON_TOR_PROXY_PREF = "extensions.torbutton.use_nontor_proxy";
+
+// The topic of new identity, to observe to cleanup all the nonces.
+const NEW_IDENTITY_TOPIC = "new-identity-requested";
+
+class TorDomainIsolatorImpl {
+  // A mutable map that records what nonce we are using for each domain.
+  #noncesForDomains = new Map();
+
+  // A mutable map that records what nonce we are using for each tab container.
+  #noncesForUserContextId = new Map();
+
+  // A bool that controls if we use SOCKS auth for isolation or not.
+  #isolationEnabled = true;
+
+  // Specifies when the current catch-all circuit was first used
+  #catchallDirtySince = Date.now();
+
+  /**
+   * Initialize the domain isolator.
+   * This function will setup the proxy filter that injects the credentials and
+   * register some observers.
+   */
+  init() {
+    logger.info("Setup circuit isolation by domain and user context");
+
+    if (Services.prefs.getBoolPref(NON_TOR_PROXY_PREF)) {
+      this.#isolationEnabled = false;
+    }
+    this.#setupProxyFilter();
+
+    Services.prefs.addObserver(NON_TOR_PROXY_PREF, this);
+    Services.obs.addObserver(this, NEW_IDENTITY_TOPIC);
+  }
+
+  /**
+   * Removes the observers added in the initialization.
+   */
+  uninit() {
+    Services.prefs.removeObserver(NON_TOR_PROXY_PREF, this);
+    Services.obs.removeObserver(this, NEW_IDENTITY_TOPIC);
+  }
+
+  enable() {
+    logger.trace("Domain isolation enabled");
+    this.#isolationEnabled = true;
+  }
+
+  disable() {
+    logger.trace("Domain isolation disabled");
+    this.#isolationEnabled = false;
+  }
+
+  /**
+   * Return the credentials to use as username and password for the SOCKS proxy,
+   * given a certain domain and userContextId. Optionally, create them.
+   *
+   * @param firstPartyDomain The first party domain associated to the requests
+   * @param userContextId The context ID associated to the request
+   * @param create Whether to create the nonce, if it is not available
+   * @return Either the credential, or null if we do not have them and create is
+   * false.
+   */
+  getSocksProxyCredentials(firstPartyDomain, userContextId, create = false) {
+    if (!this.#noncesForDomains.has(firstPartyDomain)) {
+      if (!create) {
+        return null;
+      }
+      const nonce = this.#nonce();
+      logger.info(`New nonce for first party ${firstPartyDomain}: ${nonce}`);
+      this.#noncesForDomains.set(firstPartyDomain, nonce);
+    }
+    if (!this.#noncesForUserContextId.has(userContextId)) {
+      if (!create) {
+        return null;
+      }
+      const nonce = this.#nonce();
+      logger.info(`New nonce for userContextId ${userContextId}: ${nonce}`);
+      this.#noncesForUserContextId.set(userContextId, nonce);
+    }
+    return {
+      username: this.#makeUsername(firstPartyDomain, userContextId),
+      password:
+        this.#noncesForDomains.get(firstPartyDomain) +
+        this.#noncesForUserContextId.get(userContextId),
+    };
+  }
+
+  /**
+   * Create a new nonce for the FP domain of the selected browser and reload the
+   * tab with a new circuit.
+   *
+   * @param browser Should be the gBrowser from the context of the caller
+   */
+  newCircuitForBrowser(browser) {
+    const firstPartyDomain = getDomainForBrowser(browser.selectedBrowser);
+    this.#newCircuitForDomain(firstPartyDomain);
+    // TODO: How to properly handle the user context? Should we use
+    // (domain, userContextId) pairs, instead of concatenating nonces?
+    browser.reloadWithFlags(Ci.nsIWebNavigation.LOAD_FLAGS_BYPASS_CACHE);
+  }
+
+  /**
+   * Clear the isolation state cache, forcing new circuits to be used for all
+   * subsequent requests.
+   */
+  clearIsolation() {
+    logger.trace("Clearing isolation nonces.");
+
+    // Per-domain and per contextId nonces are stored in maps, so simply clear
+    // them.
+    this.#noncesForDomains.clear();
+    this.#noncesForUserContextId.clear();
+
+    // Force a rotation on the next catch-all circuit use by setting the
+    // creation time to the epoch.
+    this.#catchallDirtySince = 0;
+  }
+
+  async observe(subject, topic, data) {
+    if (topic === "nsPref:changed" && data === NON_TOR_PROXY_PREF) {
+      if (Services.prefs.getBoolPref(NON_TOR_PROXY_PREF)) {
+        this.disable();
+      } else {
+        this.enable();
+      }
+    } else if (topic === NEW_IDENTITY_TOPIC) {
+      logger.info(
+        "New identity has been requested, clearing isolation tokens."
+      );
+      this.clearIsolation();
+      try {
+        await TorProtocolService.newnym();
+      } catch (e) {
+        logger.error("Could not send the newnym command", e);
+        // TODO: What UX to use here? See tor-browser#41708
+      }
+    }
+  }
+
+  /**
+   * Setup a filter that for every HTTPChannel, replaces the default SOCKS proxy
+   * with one that authenticates to the SOCKS server (the tor client process)
+   * with a username (the first party domain and userContextId) and a nonce
+   * password.
+   * Tor provides a separate circuit for each username+password combination.
+   */
+  #setupProxyFilter() {
+    const filterFunction = (aChannel, aProxy) => {
+      if (!this.#isolationEnabled) {
+        return aProxy;
+      }
+      try {
+        const channel = aChannel.QueryInterface(Ci.nsIChannel);
+        let firstPartyDomain =
+          channel.loadInfo.originAttributes.firstPartyDomain;
+        const userContextId = channel.loadInfo.originAttributes.userContextId;
+        if (firstPartyDomain === "") {
+          firstPartyDomain = CATCHALL_DOMAIN;
+          if (Date.now() - this.#catchallDirtySince > 1000 * 10 * 60) {
+            logger.info(
+              "tor catchall circuit has been dirty for over 10 minutes. Rotating."
+            );
+            this.#newCircuitForDomain(CATCHALL_DOMAIN);
+            this.#catchallDirtySince = Date.now();
+          }
+        }
+        const replacementProxy = this.#applySocksProxyCredentials(
+          aProxy,
+          firstPartyDomain,
+          userContextId
+        );
+        logger.debug(
+          `Requested ${channel.URI.spec} via ${replacementProxy.username}:${replacementProxy.password}`
+        );
+        return replacementProxy;
+      } catch (e) {
+        logger.error("Error while setting a new proxy", e);
+        return null;
+      }
+    };
+
+    ProtocolProxyService.registerChannelFilter(
+      {
+        applyFilter(aChannel, aProxy, aCallback) {
+          aCallback.onProxyFilterResult(filterFunction(aChannel, aProxy));
+        },
+      },
+      0
+    );
+  }
+
+  /**
+   * Takes a proxyInfo object (originalProxy) and returns a new proxyInfo
+   * object with the same properties, except the username is set to the
+   * the domain and userContextId, and the password is a nonce.
+   */
+  #applySocksProxyCredentials(originalProxy, domain, userContextId) {
+    const proxy = originalProxy.QueryInterface(Ci.nsIProxyInfo);
+    const { username, password } = this.getSocksProxyCredentials(
+      domain,
+      userContextId,
+      true
+    );
+    return ProtocolProxyService.newProxyInfoWithAuth(
+      "socks",
+      proxy.host,
+      proxy.port,
+      username,
+      password,
+      "", // aProxyAuthorizationHeader
+      "", // aConnectionIsolationKey
+      proxy.flags,
+      proxy.failoverTimeout,
+      proxy.failoverProxy
+    );
+  }
+
+  /**
+   * Combine the needed data into a username for the proxy.
+   */
+  #makeUsername(domain, userContextId) {
+    if (!domain) {
+      domain = CATCHALL_DOMAIN;
+    }
+    return `${domain}:${userContextId}`;
+  }
+
+  /**
+   * Generate a new 128 bit random tag.
+   *
+   * Strictly speaking both using a cryptographic entropy source and using 128
+   * bits of entropy for the tag are likely overkill, as correct behavior only
+   * depends on how unlikely it is for there to be a collision.
+   */
+  #nonce() {
+    return Array.from(crypto.getRandomValues(new Uint8Array(16)), byte =>
+      byte.toString(16).padStart(2, "0")
+    ).join("");
+  }
+
+  /**
+   * Re-generate the nonce for a certain domain.
+   */
+  #newCircuitForDomain(domain) {
+    if (!domain) {
+      domain = CATCHALL_DOMAIN;
+    }
+    this.#noncesForDomains.set(domain, this.#nonce());
+    logger.info(
+      `New domain isolation for ${domain}: ${this.#noncesForDomains.get(
+        domain
+      )}`
+    );
+  }
+
+  /**
+   * Re-generate the nonce for a userContextId.
+   *
+   * Currently, this function is not hooked to anything.
+   */
+  #newCircuitForUserContextId(userContextId) {
+    this.#noncesForUserContextId.set(userContextId, this.#nonce());
+    logger.info(
+      `New container isolation for ${userContextId}: ${this.#noncesForUserContextId.get(
+        userContextId
+      )}`
+    );
+  }
+}
+
+/**
+ * Get the first party domain for a certain browser.
+ *
+ * @param browser The browser to get the FP-domain for.
+ *
+ * Please notice that it should be gBrowser.selectedBrowser, because
+ * browser.documentURI is the actual shown page, and might be an error page.
+ * In this case, we rely on currentURI, which for gBrowser is an alias of
+ * gBrowser.selectedBrowser.currentURI.
+ * See browser/base/content/tabbrowser.js and tor-browser#31562.
+ */
+function getDomainForBrowser(browser) {
+  let fpd = browser.contentPrincipal.originAttributes.firstPartyDomain;
+
+  // Bug 31562: For neterror or certerror, get the original URL from
+  // browser.currentURI and use it to calculate the firstPartyDomain.
+  const knownErrors = [
+    "about:neterror",
+    "about:certerror",
+    "about:httpsonlyerror",
+  ];
+  const { documentURI } = browser;
+  if (
+    documentURI &&
+    documentURI.schemeIs("about") &&
+    knownErrors.some(x => documentURI.spec.startsWith(x))
+  ) {
+    const knownSchemes = ["http", "https"];
+    const currentURI = browser.currentURI;
+    if (currentURI && knownSchemes.some(x => currentURI.schemeIs(x))) {
+      try {
+        fpd = Services.eTLD.getBaseDomainFromHost(currentURI.host);
+      } catch (e) {
+        if (
+          e.result === Cr.NS_ERROR_HOST_IS_IP_ADDRESS ||
+          e.result === Cr.NS_ERROR_INSUFFICIENT_DOMAIN_LEVELS
+        ) {
+          fpd = currentURI.host;
+        } else {
+          logger.error(
+            `Failed to get first party domain for host ${currentURI.host}`,
+            e
+          );
+        }
+      }
+    }
+  }
+
+  return fpd;
+}
+
+const TorDomainIsolator = new TorDomainIsolatorImpl();
+// Reduce global vars pollution
+TorDomainIsolator.getDomainForBrowser = getDomainForBrowser;


=====================================
toolkit/components/tor-launcher/TorProtocolService.jsm
=====================================
@@ -4,6 +4,7 @@
 
 var EXPORTED_SYMBOLS = ["TorProtocolService"];
 
+const { ConsoleAPI } = ChromeUtils.import("resource://gre/modules/Console.jsm");
 const { Services } = ChromeUtils.import("resource://gre/modules/Services.jsm");
 const { setTimeout } = ChromeUtils.import("resource://gre/modules/Timer.jsm");
 ChromeUtils.defineModuleGetter(
@@ -11,9 +12,6 @@ ChromeUtils.defineModuleGetter(
   "FileUtils",
   "resource://gre/modules/FileUtils.jsm"
 );
-const { XPCOMUtils } = ChromeUtils.import(
-  "resource://gre/modules/XPCOMUtils.jsm"
-);
 
 Cu.importGlobalProperties(["crypto"]);
 
@@ -45,18 +43,9 @@ const TorTopics = Object.freeze({
   ProcessRestarted: "TorProcessRestarted",
 });
 
-// Logger adapted from CustomizableUI.jsm
-XPCOMUtils.defineLazyGetter(this, "logger", () => {
-  const { ConsoleAPI } = ChromeUtils.import(
-    "resource://gre/modules/Console.jsm"
-  );
-  // TODO: Use a preference to set the log level.
-  const consoleOptions = {
-    // maxLogLevel: "warn",
-    maxLogLevel: "all",
-    prefix: "TorProtocolService",
-  };
-  return new ConsoleAPI(consoleOptions);
+const logger = new ConsoleAPI({
+  maxLogLevel: "warn",
+  prefix: "TorProtocolService",
 });
 
 // Manage the connection to tor's control port, to update its settings and query
@@ -194,6 +183,10 @@ const TorProtocolService = {
     TorMonitorService.retrieveBootstrapStatus();
   },
 
+  async newnym() {
+    return this.sendCommand("SIGNAL NEWNYM");
+  },
+
   // TODO: transform the following 4 functions in getters. At the moment they
   // are also used in torbutton.
 


=====================================
toolkit/components/tor-launcher/TorStartupService.jsm
=====================================
@@ -33,6 +33,12 @@ ChromeUtils.defineModuleGetter(
   "resource:///modules/TorSettings.jsm"
 );
 
+ChromeUtils.defineModuleGetter(
+  this,
+  "TorDomainIsolator",
+  "resource://gre/modules/TorDomainIsolator.jsm"
+);
+
 /* Browser observer topis */
 const BrowserTopics = Object.freeze({
   ProfileAfterChange: "profile-after-change",
@@ -67,12 +73,16 @@ class TorStartupService {
     TorSettings.init();
     TorConnect.init();
 
+    TorDomainIsolator.init();
+
     gInited = true;
   }
 
   _uninit() {
     Services.obs.removeObserver(this, BrowserTopics.QuitApplicationGranted);
 
+    TorDomainIsolator.uninit();
+
     // Close any helper connection first...
     TorProtocolService.uninit();
     // ... and only then closes the event monitor connection, which will cause


=====================================
toolkit/components/tor-launcher/moz.build
=====================================
@@ -1,5 +1,6 @@
 EXTRA_JS_MODULES += [
     "TorBootstrapRequest.jsm",
+    "TorDomainIsolator.jsm",
     "TorLauncherUtil.jsm",
     "TorMonitorService.jsm",
     "TorParsers.jsm",


=====================================
toolkit/torbutton/chrome/content/torbutton.js
=====================================
@@ -1,6 +1,5 @@
 // window globals
 var torbutton_init;
-var torbutton_new_circuit;
 
 (() => {
   // Bug 1506 P1-P5: This is the main Torbutton overlay file. Much needs to be
@@ -16,9 +15,7 @@ var torbutton_new_circuit;
 
   let {
     unescapeTorString,
-    getDomainForBrowser,
     torbutton_log,
-    torbutton_get_property_string,
   } = ChromeUtils.import("resource://torbutton/modules/utils.js");
   let { configureControlPortModule, wait_for_controller } = ChromeUtils.import(
     "resource://torbutton/modules/tor-control-port.js"
@@ -46,32 +43,22 @@ var torbutton_new_circuit;
   // in a component, not the XUL overlay.
   var torbutton_unique_pref_observer = {
     register() {
-      this.forced_ua = false;
-      m_tb_prefs.addObserver("extensions.torbutton", this);
-      m_tb_prefs.addObserver("browser.privatebrowsing.autostart", this);
-      m_tb_prefs.addObserver("javascript", this);
+      Services.prefs.addObserver("browser.privatebrowsing.autostart", this);
     },
 
     unregister() {
-      m_tb_prefs.removeObserver("extensions.torbutton", this);
-      m_tb_prefs.removeObserver("browser.privatebrowsing.autostart", this);
-      m_tb_prefs.removeObserver("javascript", this);
+      Services.prefs.removeObserver("browser.privatebrowsing.autostart", this);
     },
 
     // topic:   what event occurred
     // subject: what nsIPrefBranch we're observing
     // data:    which pref has been changed (relative to subject)
     observe(subject, topic, data) {
-      if (topic !== "nsPref:changed") {
-        return;
-      }
-      switch (data) {
-        case "browser.privatebrowsing.autostart":
-          torbutton_update_disk_prefs();
-          break;
-        case "extensions.torbutton.use_nontor_proxy":
-          torbutton_use_nontor_proxy();
-          break;
+      if (
+        topic === "nsPref:changed" &&
+        data === "browser.privatebrowsing.autostart"
+      ) {
+        torbutton_update_disk_prefs();
       }
     },
   };
@@ -113,62 +100,6 @@ var torbutton_new_circuit;
     },
   };
 
-  var torbutton_new_identity_observers = {
-    register() {
-      Services.obs.addObserver(this, "new-identity-requested");
-    },
-
-    observe(aSubject, aTopic, aData) {
-      if (aTopic !== "new-identity-requested") {
-        return;
-      }
-
-      // Clear the domain isolation state.
-      torbutton_log(3, "Clearing domain isolator");
-      const domainIsolator = Cc["@torproject.org/domain-isolator;1"].getService(
-        Ci.nsISupports
-      ).wrappedJSObject;
-      domainIsolator.clearIsolation();
-
-      torbutton_log(3, "New Identity: Sending NEWNYM");
-      // We only support TBB for newnym.
-      if (
-        !m_tb_control_pass ||
-        (!m_tb_control_ipc_file && !m_tb_control_port)
-      ) {
-        const warning = torbutton_get_property_string(
-          "torbutton.popup.no_newnym"
-        );
-        torbutton_log(
-          5,
-          "Torbutton cannot safely newnym. It does not have access to the Tor Control Port."
-        );
-        window.alert(warning);
-      } else {
-        const warning = torbutton_get_property_string(
-          "torbutton.popup.no_newnym"
-        );
-        torbutton_send_ctrl_cmd("SIGNAL NEWNYM")
-          .then(res => {
-            if (!res) {
-              torbutton_log(
-                5,
-                "Torbutton was unable to request a new circuit from Tor"
-              );
-              window.alert(warning);
-            }
-          })
-          .catch(e => {
-            torbutton_log(
-              5,
-              "Torbutton was unable to request a new circuit from Tor " + e
-            );
-            window.alert(warning);
-          });
-      }
-    },
-  };
-
   // Bug 1506 P2-P4: This code sets some version variables that are irrelevant.
   // It does read out some important environment variables, though. It is
   // called once per browser window.. This might belong in a component.
@@ -258,8 +189,6 @@ var torbutton_new_circuit;
       true
     );
 
-    torbutton_new_identity_observers.register();
-
     torbutton_log(3, "init completed");
   };
 
@@ -374,36 +303,6 @@ var torbutton_new_circuit;
     return response;
   }
 
-  // Bug 1506 P4: Needed for New IP Address
-  torbutton_new_circuit = function() {
-    let firstPartyDomain = getDomainForBrowser(gBrowser.selectedBrowser);
-
-    let domainIsolator = Cc["@torproject.org/domain-isolator;1"].getService(
-      Ci.nsISupports
-    ).wrappedJSObject;
-
-    domainIsolator.newCircuitForDomain(firstPartyDomain);
-
-    gBrowser.reloadWithFlags(Ci.nsIWebNavigation.LOAD_FLAGS_BYPASS_CACHE);
-  };
-
-  /* Called when we switch the use_nontor_proxy pref in either direction.
-   *
-   * Enables/disables domain isolation and then does new identity
-   */
-  function torbutton_use_nontor_proxy() {
-    let domainIsolator = Cc["@torproject.org/domain-isolator;1"].getService(
-      Ci.nsISupports
-    ).wrappedJSObject;
-
-    if (m_tb_prefs.getBoolPref("extensions.torbutton.use_nontor_proxy")) {
-      // Disable domain isolation
-      domainIsolator.disableIsolation();
-    } else {
-      domainIsolator.enableIsolation();
-    }
-  }
-
   async function torbutton_do_tor_check() {
     let checkSvc = Cc["@torproject.org/torbutton-torCheckService;1"].getService(
       Ci.nsISupports


=====================================
toolkit/torbutton/components/domain-isolator.js deleted
=====================================
@@ -1,312 +0,0 @@
-// # domain-isolator.js
-// A component for TorBrowser that puts requests from different
-// first party domains on separate tor circuits.
-
-// This file is written in call stack order (later functions
-// call earlier functions). The code file can be processed
-// with docco.js to provide clear documentation.
-
-// ### Abbreviations
-
-const { Services } = ChromeUtils.import("resource://gre/modules/Services.jsm");
-const { XPCOMUtils } = ChromeUtils.import(
-  "resource://gre/modules/XPCOMUtils.jsm"
-);
-
-XPCOMUtils.defineLazyModuleGetters(this, {
-  ComponentUtils: "resource://gre/modules/ComponentUtils.jsm",
-});
-
-// Make the logger available.
-let logger = Cc["@torproject.org/torbutton-logger;1"].getService(Ci.nsISupports)
-  .wrappedJSObject;
-
-// Import crypto object (FF 37+).
-Cu.importGlobalProperties(["crypto"]);
-
-// ## mozilla namespace.
-// Useful functionality for interacting with Mozilla services.
-let mozilla = {};
-
-// __mozilla.protocolProxyService__.
-// Mozilla's protocol proxy service, useful for managing proxy connections made
-// by the browser.
-mozilla.protocolProxyService = Cc[
-  "@mozilla.org/network/protocol-proxy-service;1"
-].getService(Ci.nsIProtocolProxyService);
-
-// __mozilla.registerProxyChannelFilter(filterFunction, positionIndex)__.
-// Registers a proxy channel filter with the Mozilla Protocol Proxy Service,
-// which will help to decide the proxy to be used for a given channel.
-// The filterFunction should expect two arguments, (aChannel, aProxy),
-// where aProxy is the proxy or list of proxies that would be used by default
-// for the given channel, and should return a new Proxy or list of Proxies.
-mozilla.registerProxyChannelFilter = function(filterFunction, positionIndex) {
-  let proxyFilter = {
-    applyFilter(aChannel, aProxy, aCallback) {
-      aCallback.onProxyFilterResult(filterFunction(aChannel, aProxy));
-    },
-  };
-  mozilla.protocolProxyService.registerChannelFilter(
-    proxyFilter,
-    positionIndex
-  );
-};
-
-// ## tor functionality.
-let tor = {};
-
-// __tor.noncesForDomains__.
-// A mutable map that records what nonce we are using for each domain.
-tor.noncesForDomains = new Map();
-
-// __tor.noncesForUserContextId__.
-// A mutable map that records what nonce we are using for each tab container.
-tor.noncesForUserContextId = new Map();
-
-// __tor.isolationEabled__.
-// A bool that controls if we use SOCKS auth for isolation or not.
-tor.isolationEnabled = true;
-
-// __tor.unknownDirtySince__.
-// Specifies when the current catch-all circuit was first used
-tor.unknownDirtySince = Date.now();
-
-tor.passwordForDomainAndUserContextId = function(
-  domain,
-  userContextId,
-  create
-) {
-  // Check if we already have a nonce. If not, possibly create one for this
-  // domain and userContextId.
-  if (!tor.noncesForDomains.has(domain)) {
-    if (!create) {
-      return null;
-    }
-    tor.noncesForDomains.set(domain, tor.nonce());
-  }
-  if (!tor.noncesForUserContextId.has(userContextId)) {
-    if (!create) {
-      return null;
-    }
-    tor.noncesForUserContextId.set(userContextId, tor.nonce());
-  }
-  return (
-    tor.noncesForDomains.get(domain) +
-    tor.noncesForUserContextId.get(userContextId)
-  );
-};
-
-tor.usernameForDomainAndUserContextId = function(domain, userContextId) {
-  return `${domain}:${userContextId}`;
-};
-
-// __tor.socksProxyCredentials(originalProxy, domain, userContextId)__.
-// Takes a proxyInfo object (originalProxy) and returns a new proxyInfo
-// object with the same properties, except the username is set to the
-// the domain and userContextId, and the password is a nonce.
-tor.socksProxyCredentials = function(originalProxy, domain, userContextId) {
-  let proxy = originalProxy.QueryInterface(Ci.nsIProxyInfo);
-  let proxyUsername = tor.usernameForDomainAndUserContextId(
-    domain,
-    userContextId
-  );
-  let proxyPassword = tor.passwordForDomainAndUserContextId(
-    domain,
-    userContextId,
-    true
-  );
-  return mozilla.protocolProxyService.newProxyInfoWithAuth(
-    "socks",
-    proxy.host,
-    proxy.port,
-    proxyUsername,
-    proxyPassword,
-    "", // aProxyAuthorizationHeader
-    "", // aConnectionIsolationKey
-    proxy.flags,
-    proxy.failoverTimeout,
-    proxy.failoverProxy
-  );
-};
-
-tor.nonce = function() {
-  // Generate a new 128 bit random tag.  Strictly speaking both using a
-  // cryptographic entropy source and using 128 bits of entropy for the
-  // tag are likely overkill, as correct behavior only depends on how
-  // unlikely it is for there to be a collision.
-  let tag = new Uint8Array(16);
-  crypto.getRandomValues(tag);
-
-  // Convert the tag to a hex string.
-  let tagStr = "";
-  for (let i = 0; i < tag.length; i++) {
-    tagStr += (tag[i] >>> 4).toString(16);
-    tagStr += (tag[i] & 0x0f).toString(16);
-  }
-
-  return tagStr;
-};
-
-tor.newCircuitForDomain = function(domain) {
-  // Re-generate the nonce for the domain.
-  if (domain === "") {
-    domain = "--unknown--";
-  }
-  tor.noncesForDomains.set(domain, tor.nonce());
-  logger.eclog(
-    3,
-    `New domain isolation for ${domain}: ${tor.noncesForDomains.get(domain)}`
-  );
-};
-
-tor.newCircuitForUserContextId = function(userContextId) {
-  // Re-generate the nonce for the context.
-  tor.noncesForUserContextId.set(userContextId, tor.nonce());
-  logger.eclog(
-    3,
-    `New container isolation for ${userContextId}: ${tor.noncesForUserContextId.get(
-      userContextId
-    )}`
-  );
-};
-
-// __tor.clearIsolation()_.
-// Clear the isolation state cache, forcing new circuits to be used for all
-// subsequent requests.
-tor.clearIsolation = function() {
-  // Per-domain and per contextId nonces are stored in maps, so simply clear them.
-  tor.noncesForDomains.clear();
-  tor.noncesForUserContextId.clear();
-
-  // Force a rotation on the next catch-all circuit use by setting the creation
-  // time to the epoch.
-  tor.unknownDirtySince = 0;
-};
-
-// __tor.isolateCircuitsByDomain()__.
-// For every HTTPChannel, replaces the default SOCKS proxy with one that authenticates
-// to the SOCKS server (the tor client process) with a username (the first party domain
-// and userContextId) and a nonce password. Tor provides a separate circuit for each
-// username+password combination.
-tor.isolateCircuitsByDomain = function() {
-  mozilla.registerProxyChannelFilter(function(aChannel, aProxy) {
-    if (!tor.isolationEnabled) {
-      return aProxy;
-    }
-    try {
-      let channel = aChannel.QueryInterface(Ci.nsIChannel),
-        firstPartyDomain = channel.loadInfo.originAttributes.firstPartyDomain,
-        userContextId = channel.loadInfo.originAttributes.userContextId;
-      if (firstPartyDomain === "") {
-        firstPartyDomain = "--unknown--";
-        if (Date.now() - tor.unknownDirtySince > 1000 * 10 * 60) {
-          logger.eclog(
-            3,
-            "tor catchall circuit has been dirty for over 10 minutes. Rotating."
-          );
-          tor.newCircuitForDomain("--unknown--");
-          tor.unknownDirtySince = Date.now();
-        }
-      }
-      let replacementProxy = tor.socksProxyCredentials(
-        aProxy,
-        firstPartyDomain,
-        userContextId
-      );
-      logger.eclog(
-        3,
-        `tor SOCKS: ${channel.URI.spec} via
-                       ${replacementProxy.username}:${replacementProxy.password}`
-      );
-      return replacementProxy;
-    } catch (e) {
-      logger.eclog(4, `tor domain isolator error: ${e.message}`);
-      return null;
-    }
-  }, 0);
-};
-
-// ## XPCOM component construction.
-// Module specific constants
-const kMODULE_NAME = "TorBrowser Domain Isolator";
-const kMODULE_CONTRACTID = "@torproject.org/domain-isolator;1";
-const kMODULE_CID = Components.ID("e33fd6d4-270f-475f-a96f-ff3140279f68");
-
-// DomainIsolator object.
-function DomainIsolator() {
-  this.wrappedJSObject = this;
-}
-
-// Firefox component requirements
-DomainIsolator.prototype = {
-  QueryInterface: ChromeUtils.generateQI([Ci.nsIObserver]),
-  classDescription: kMODULE_NAME,
-  classID: kMODULE_CID,
-  contractID: kMODULE_CONTRACTID,
-  observe(subject, topic, data) {
-    if (topic === "profile-after-change") {
-      logger.eclog(3, "domain isolator: set up isolating circuits by domain");
-
-      if (Services.prefs.getBoolPref("extensions.torbutton.use_nontor_proxy")) {
-        tor.isolationEnabled = false;
-      }
-      tor.isolateCircuitsByDomain();
-    }
-  },
-
-  newCircuitForDomain(domain) {
-    tor.newCircuitForDomain(domain);
-  },
-
-  /**
-   * Return the stored SOCKS proxy username and password for the given domain
-   * and user context ID.
-   *
-   * @param {string} firstPartyDomain - The domain to lookup credentials for.
-   * @param {integer} userContextId - The ID for the user context.
-   *
-   * @return {{ username: string, password: string }?} - The SOCKS credentials,
-   *   or null if none are found.
-   */
-  getSocksProxyCredentials(firstPartyDomain, userContextId) {
-    if (firstPartyDomain == "") {
-      firstPartyDomain = "--unknown--";
-    }
-    let proxyPassword = tor.passwordForDomainAndUserContextId(
-      firstPartyDomain,
-      userContextId,
-      // Do not create a new entry if it does not exist.
-      false
-    );
-    if (!proxyPassword) {
-      return null;
-    }
-    return {
-      username: tor.usernameForDomainAndUserContextId(
-        firstPartyDomain,
-        userContextId
-      ),
-      password: proxyPassword,
-    };
-  },
-
-  enableIsolation() {
-    tor.isolationEnabled = true;
-  },
-
-  disableIsolation() {
-    tor.isolationEnabled = false;
-  },
-
-  clearIsolation() {
-    tor.clearIsolation();
-  },
-
-  wrappedJSObject: null,
-};
-
-// Assign factory to global object.
-const NSGetFactory = XPCOMUtils.generateNSGetFactory
-  ? XPCOMUtils.generateNSGetFactory([DomainIsolator])
-  : ComponentUtils.generateNSGetFactory([DomainIsolator]);


=====================================
toolkit/torbutton/jar.mn
=====================================
@@ -43,9 +43,5 @@ torbutton.jar:
 % component {f36d72c9-9718-4134-b550-e109638331d7} %components/torbutton-logger.js
 % contract @torproject.org/torbutton-logger;1 {f36d72c9-9718-4134-b550-e109638331d7}
 
-% component {e33fd6d4-270f-475f-a96f-ff3140279f68} %components/domain-isolator.js
-% contract @torproject.org/domain-isolator;1 {e33fd6d4-270f-475f-a96f-ff3140279f68}
-
 % category profile-after-change StartupObserver @torproject.org/startup-observer;1
-% category profile-after-change DomainIsolator @torproject.org/domain-isolator;1
 % category profile-after-change DragDropFilter @torproject.org/torbutton-dragDropFilter;1


=====================================
toolkit/torbutton/modules/utils.js
=====================================
@@ -213,45 +213,6 @@ var unescapeTorString = function(str) {
   return _torControl._strUnescape(str);
 };
 
-var getFPDFromHost = hostname => {
-  try {
-    return Services.eTLD.getBaseDomainFromHost(hostname);
-  } catch (e) {
-    if (
-      e.result == Cr.NS_ERROR_HOST_IS_IP_ADDRESS ||
-      e.result == Cr.NS_ERROR_INSUFFICIENT_DOMAIN_LEVELS
-    ) {
-      return hostname;
-    }
-  }
-  return null;
-};
-
-// Assuming this is called with gBrowser.selectedBrowser
-var getDomainForBrowser = browser => {
-  let fpd = browser.contentPrincipal.originAttributes.firstPartyDomain;
-  // Bug 31562: For neterror or certerror, get the original URL from
-  // browser.currentURI and use it to calculate the firstPartyDomain.
-  let knownErrors = [
-    "about:neterror",
-    "about:certerror",
-    "about:httpsonlyerror",
-  ];
-  let documentURI = browser.documentURI;
-  if (
-    documentURI &&
-    documentURI.schemeIs("about") &&
-    knownErrors.some(x => documentURI.spec.startsWith(x))
-  ) {
-    let knownSchemes = ["http", "https", "ftp"];
-    let currentURI = browser.currentURI;
-    if (currentURI && knownSchemes.some(x => currentURI.schemeIs(x))) {
-      fpd = getFPDFromHost(currentURI.host) || fpd;
-    }
-  }
-  return fpd;
-};
-
 var m_tb_torlog = Cc["@torproject.org/torbutton-logger;1"].getService(
   Ci.nsISupports
 ).wrappedJSObject;
@@ -310,7 +271,6 @@ let EXPORTED_SYMBOLS = [
   "bindPrefAndInit",
   "getEnv",
   "getLocale",
-  "getDomainForBrowser",
   "getPrefValue",
   "observe",
   "showDialog",



View it on GitLab: https://gitlab.torproject.org/tpo/applications/tor-browser/-/compare/2165eb00af36b03a457bfd87d701f575b3bdb458...cf8bb1dfe4f24b42ad13650e354b1a2753eb7849

-- 
View it on GitLab: https://gitlab.torproject.org/tpo/applications/tor-browser/-/compare/2165eb00af36b03a457bfd87d701f575b3bdb458...cf8bb1dfe4f24b42ad13650e354b1a2753eb7849
You're receiving this email because of your account on gitlab.torproject.org.


-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.torproject.org/pipermail/tbb-commits/attachments/20230608/1c9b3bcd/attachment-0001.htm>


More information about the tbb-commits mailing list