[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