Pier Angelo Vendrame pushed to branch tor-browser-102.11.0esr-13.0-1 at The Tor Project / Applications / Tor Browser
Commits:
-
98e75e48
by Pier Angelo Vendrame at 2023-05-17T10:25:44+02:00
-
74e39196
by Pier Angelo Vendrame at 2023-05-17T10:25:52+02:00
-
0f9ea290
by Arthur Edelstein at 2023-05-17T10:25:53+02:00
-
14058280
by Pier Angelo Vendrame at 2023-05-17T10:25:53+02:00
-
c94c9662
by Pier Angelo Vendrame at 2023-05-17T10:25:53+02:00
-
994e4ce2
by Pier Angelo Vendrame at 2023-05-17T10:25:54+02:00
-
bcabee6b
by Pier Angelo Vendrame at 2023-05-17T10:25:54+02:00
-
16cf26ff
by Pier Angelo Vendrame at 2023-05-17T10:36:36+02:00
-
39b5273c
by Pier Angelo Vendrame at 2023-05-17T10:37:31+02:00
-
9cd44b23
by Pier Angelo Vendrame at 2023-05-17T10:38:45+02:00
-
b27d2320
by Pier Angelo Vendrame at 2023-05-17T10:38:48+02:00
-
5b547f81
by Pier Angelo Vendrame at 2023-05-17T10:38:49+02:00
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:
... | ... | @@ -12,6 +12,12 @@ const { XPCOMUtils } = ChromeUtils.import( |
12 | 12 | "resource://gre/modules/XPCOMUtils.jsm"
|
13 | 13 | );
|
14 | 14 | |
15 | +ChromeUtils.defineModuleGetter(
|
|
16 | + this,
|
|
17 | + "TorDomainIsolator",
|
|
18 | + "resource://gre/modules/TorDomainIsolator.jsm"
|
|
19 | +);
|
|
20 | + |
|
15 | 21 | XPCOMUtils.defineLazyGetter(this, "cryptoSafetyBundle", () => {
|
16 | 22 | return Services.strings.createBundle(
|
17 | 23 | "chrome://browser/locale/cryptoSafetyPrompt.properties"
|
... | ... | @@ -75,7 +81,11 @@ class CryptoSafetyParent extends JSWindowActorParent { |
75 | 81 | );
|
76 | 82 | |
77 | 83 | if (buttonPressed === 0) {
|
78 | - this.browsingContext.topChromeWindow.torbutton_new_circuit();
|
|
84 | + const { browsingContext } = this.manager;
|
|
85 | + const browser = browsingContext.embedderElement;
|
|
86 | + if (browser) {
|
|
87 | + TorDomainIsolator.newCircuitForBrowser(browser.ownerGlobal.gBrowser);
|
|
88 | + }
|
|
79 | 89 | }
|
80 | 90 | }
|
81 | 91 | } |
... | ... | @@ -63,9 +63,9 @@ |
63 | 63 | key="new-identity-key"/>
|
64 | 64 | <toolbarbutton id="appMenuNewCircuit"
|
65 | 65 | class="subviewbutton"
|
66 | - key="torbutton-new-circuit-key"
|
|
66 | + key="new-circuit-key"
|
|
67 | 67 | label="&torbutton.context_menu.new_circuit_sentence_case;"
|
68 | - oncommand="torbutton_new_circuit();"/>
|
|
68 | + oncommand="TorDomainIsolator.newCircuitForBrowser(gBrowser);"/>
|
|
69 | 69 | <toolbarseparator/>
|
70 | 70 | <toolbarbutton id="appMenu-bookmarks-button"
|
71 | 71 | class="subviewbutton subviewbutton-nav"
|
... | ... | @@ -33,9 +33,9 @@ |
33 | 33 | key="new-identity-key"/>
|
34 | 34 | <menuitem id="menu_newCircuit"
|
35 | 35 | accesskey="&torbutton.context_menu.new_circuit_key;"
|
36 | - key="torbutton-new-circuit-key"
|
|
36 | + key="new-circuit-key"
|
|
37 | 37 | label="&torbutton.context_menu.new_circuit;"
|
38 | - oncommand="torbutton_new_circuit();"/>
|
|
38 | + oncommand="TorDomainIsolator.newCircuitForBrowser(gBrowser);"/>
|
|
39 | 39 | <menuseparator/>
|
40 | 40 | <menuitem id="menu_openLocation"
|
41 | 41 | hidden="true"
|
... | ... | @@ -389,5 +389,5 @@ |
389 | 389 | internal="true"/>
|
390 | 390 | #endif
|
391 | 391 | <key id="new-identity-key" modifiers="accel shift" key="U" oncommand="NewIdentityButton.onCommand(event)"/>
|
392 | - <key id="torbutton-new-circuit-key" modifiers="accel shift" key="L" oncommand="torbutton_new_circuit()"/>
|
|
392 | + <key id="new-circuit-key" modifiers="accel shift" key="L" oncommand="TorDomainIsolator.newCircuitForBrowser(gBrowser)"/>
|
|
393 | 393 | </keyset> |
... | ... | @@ -80,6 +80,7 @@ XPCOMUtils.defineLazyModuleGetters(this, { |
80 | 80 | TabCrashHandler: "resource:///modules/ContentCrashHandlers.jsm",
|
81 | 81 | TelemetryEnvironment: "resource://gre/modules/TelemetryEnvironment.jsm",
|
82 | 82 | TorConnect: "resource:///modules/TorConnect.jsm",
|
83 | + TorDomainIsolator: "resource://gre/modules/TorDomainIsolator.jsm",
|
|
83 | 84 | Translation: "resource:///modules/translation/TranslationParent.jsm",
|
84 | 85 | UITour: "resource:///modules/UITour.jsm",
|
85 | 86 | UpdateUtils: "resource://gre/modules/UpdateUtils.jsm",
|
... | ... | @@ -557,7 +557,7 @@ |
557 | 557 | |
558 | 558 | <toolbarbutton id="new-circuit-button" class="toolbarbutton-1 chromeclass-toolbar-additional"
|
559 | 559 | label="&torbutton.context_menu.new_circuit;"
|
560 | - oncommand="torbutton_new_circuit();"
|
|
560 | + oncommand="TorDomainIsolator.newCircuitForBrowser(gBrowser);"
|
|
561 | 561 | tooltiptext="&torbutton.context_menu.new_circuit;"/>
|
562 | 562 | |
563 | 563 | <toolbarbutton id="fullscreen-button" class="toolbarbutton-1 chromeclass-toolbar-additional"
|
... | ... | @@ -193,7 +193,7 @@ var gTorCircuitPanel = { |
193 | 193 | document
|
194 | 194 | .getElementById("tor-circuit-new-circuit")
|
195 | 195 | .addEventListener("command", () => {
|
196 | - torbutton_new_circuit();
|
|
196 | + TorDomainIsolator.newCircuitForBrowser(gBrowser);
|
|
197 | 197 | // And hide.
|
198 | 198 | // NOTE: focus should return to the toolbar button, which we expect to
|
199 | 199 | // remain visible during reload.
|
... | ... | @@ -415,20 +415,14 @@ var gTorCircuitPanel = { |
415 | 415 | */
|
416 | 416 | _updateCurrentBrowser(matchingCredentials = null) {
|
417 | 417 | const browser = gBrowser.selectedBrowser;
|
418 | - const { getDomainForBrowser } = ChromeUtils.import(
|
|
419 | - "resource://torbutton/modules/utils.js"
|
|
420 | - );
|
|
421 | - const domain = getDomainForBrowser(browser);
|
|
418 | + const domain = TorDomainIsolator.getDomainForBrowser(browser);
|
|
422 | 419 | // We choose the currentURI, which matches what is shown in the URL bar and
|
423 | 420 | // will match up with the domain.
|
424 | 421 | // In contrast, documentURI corresponds to the shown page. E.g. it could
|
425 | 422 | // point to "about:certerror".
|
426 | 423 | const scheme = browser.currentURI?.scheme;
|
427 | 424 | |
428 | - const domainIsolator = Cc["@torproject.org/domain-isolator;1"].getService(
|
|
429 | - Ci.nsISupports
|
|
430 | - ).wrappedJSObject;
|
|
431 | - let credentials = domainIsolator.getSocksProxyCredentials(
|
|
425 | + let credentials = TorDomainIsolator.getSocksProxyCredentials(
|
|
432 | 426 | domain,
|
433 | 427 | browser.contentPrincipal.originAttributes.userContextId
|
434 | 428 | );
|
1 | +// A component for Tor Browser that puts requests from different
|
|
2 | +// first party domains on separate Tor circuits.
|
|
3 | + |
|
4 | +var EXPORTED_SYMBOLS = ["TorDomainIsolator"];
|
|
5 | + |
|
6 | +const { Services } = ChromeUtils.import("resource://gre/modules/Services.jsm");
|
|
7 | +const { XPCOMUtils } = ChromeUtils.import(
|
|
8 | + "resource://gre/modules/XPCOMUtils.jsm"
|
|
9 | +);
|
|
10 | +const { ConsoleAPI } = ChromeUtils.import("resource://gre/modules/Console.jsm");
|
|
11 | + |
|
12 | +Cu.importGlobalProperties(["crypto"]);
|
|
13 | + |
|
14 | +XPCOMUtils.defineLazyServiceGetters(this, {
|
|
15 | + ProtocolProxyService: [
|
|
16 | + "@mozilla.org/network/protocol-proxy-service;1",
|
|
17 | + "nsIProtocolProxyService",
|
|
18 | + ],
|
|
19 | +});
|
|
20 | + |
|
21 | +ChromeUtils.defineModuleGetter(
|
|
22 | + this,
|
|
23 | + "TorProtocolService",
|
|
24 | + "resource://gre/modules/TorProtocolService.jsm"
|
|
25 | +);
|
|
26 | + |
|
27 | +const logger = new ConsoleAPI({
|
|
28 | + prefix: "TorDomainIsolator",
|
|
29 | + maxLogLevel: "warn",
|
|
30 | + maxLogLevelPref: "browser.tordomainisolator.loglevel",
|
|
31 | +});
|
|
32 | + |
|
33 | +// The string to use instead of the domain when it is not known.
|
|
34 | +const CATCHALL_DOMAIN = "--unknown--";
|
|
35 | + |
|
36 | +// The preference to observe, to know whether isolation should be enabled or
|
|
37 | +// disabled.
|
|
38 | +const NON_TOR_PROXY_PREF = "extensions.torbutton.use_nontor_proxy";
|
|
39 | + |
|
40 | +// The topic of new identity, to observe to cleanup all the nonces.
|
|
41 | +const NEW_IDENTITY_TOPIC = "new-identity-requested";
|
|
42 | + |
|
43 | +class TorDomainIsolatorImpl {
|
|
44 | + // A mutable map that records what nonce we are using for each domain.
|
|
45 | + #noncesForDomains = new Map();
|
|
46 | + |
|
47 | + // A mutable map that records what nonce we are using for each tab container.
|
|
48 | + #noncesForUserContextId = new Map();
|
|
49 | + |
|
50 | + // A bool that controls if we use SOCKS auth for isolation or not.
|
|
51 | + #isolationEnabled = true;
|
|
52 | + |
|
53 | + // Specifies when the current catch-all circuit was first used
|
|
54 | + #catchallDirtySince = Date.now();
|
|
55 | + |
|
56 | + /**
|
|
57 | + * Initialize the domain isolator.
|
|
58 | + * This function will setup the proxy filter that injects the credentials and
|
|
59 | + * register some observers.
|
|
60 | + */
|
|
61 | + init() {
|
|
62 | + logger.info("Setup circuit isolation by domain and user context");
|
|
63 | + |
|
64 | + if (Services.prefs.getBoolPref(NON_TOR_PROXY_PREF)) {
|
|
65 | + this.#isolationEnabled = false;
|
|
66 | + }
|
|
67 | + this.#setupProxyFilter();
|
|
68 | + |
|
69 | + Services.prefs.addObserver(NON_TOR_PROXY_PREF, this);
|
|
70 | + Services.obs.addObserver(this, NEW_IDENTITY_TOPIC);
|
|
71 | + }
|
|
72 | + |
|
73 | + /**
|
|
74 | + * Removes the observers added in the initialization.
|
|
75 | + */
|
|
76 | + uninit() {
|
|
77 | + Services.prefs.removeObserver(NON_TOR_PROXY_PREF, this);
|
|
78 | + Services.obs.removeObserver(this, NEW_IDENTITY_TOPIC);
|
|
79 | + }
|
|
80 | + |
|
81 | + enable() {
|
|
82 | + logger.trace("Domain isolation enabled");
|
|
83 | + this.#isolationEnabled = true;
|
|
84 | + }
|
|
85 | + |
|
86 | + disable() {
|
|
87 | + logger.trace("Domain isolation disabled");
|
|
88 | + this.#isolationEnabled = false;
|
|
89 | + }
|
|
90 | + |
|
91 | + /**
|
|
92 | + * Return the credentials to use as username and password for the SOCKS proxy,
|
|
93 | + * given a certain domain and userContextId. Optionally, create them.
|
|
94 | + *
|
|
95 | + * @param firstPartyDomain The first party domain associated to the requests
|
|
96 | + * @param userContextId The context ID associated to the request
|
|
97 | + * @param create Whether to create the nonce, if it is not available
|
|
98 | + * @return Either the credential, or null if we do not have them and create is
|
|
99 | + * false.
|
|
100 | + */
|
|
101 | + getSocksProxyCredentials(firstPartyDomain, userContextId, create = false) {
|
|
102 | + if (!this.#noncesForDomains.has(firstPartyDomain)) {
|
|
103 | + if (!create) {
|
|
104 | + return null;
|
|
105 | + }
|
|
106 | + const nonce = this.#nonce();
|
|
107 | + logger.info(`New nonce for first party ${firstPartyDomain}: ${nonce}`);
|
|
108 | + this.#noncesForDomains.set(firstPartyDomain, nonce);
|
|
109 | + }
|
|
110 | + if (!this.#noncesForUserContextId.has(userContextId)) {
|
|
111 | + if (!create) {
|
|
112 | + return null;
|
|
113 | + }
|
|
114 | + const nonce = this.#nonce();
|
|
115 | + logger.info(`New nonce for userContextId ${userContextId}: ${nonce}`);
|
|
116 | + this.#noncesForUserContextId.set(userContextId, nonce);
|
|
117 | + }
|
|
118 | + return {
|
|
119 | + username: this.#makeUsername(firstPartyDomain, userContextId),
|
|
120 | + password:
|
|
121 | + this.#noncesForDomains.get(firstPartyDomain) +
|
|
122 | + this.#noncesForUserContextId.get(userContextId),
|
|
123 | + };
|
|
124 | + }
|
|
125 | + |
|
126 | + /**
|
|
127 | + * Create a new nonce for the FP domain of the selected browser and reload the
|
|
128 | + * tab with a new circuit.
|
|
129 | + *
|
|
130 | + * @param browser Should be the gBrowser from the context of the caller
|
|
131 | + */
|
|
132 | + newCircuitForBrowser(browser) {
|
|
133 | + const firstPartyDomain = getDomainForBrowser(browser.selectedBrowser);
|
|
134 | + this.#newCircuitForDomain(firstPartyDomain);
|
|
135 | + // TODO: How to properly handle the user context? Should we use
|
|
136 | + // (domain, userContextId) pairs, instead of concatenating nonces?
|
|
137 | + browser.reloadWithFlags(Ci.nsIWebNavigation.LOAD_FLAGS_BYPASS_CACHE);
|
|
138 | + }
|
|
139 | + |
|
140 | + /**
|
|
141 | + * Clear the isolation state cache, forcing new circuits to be used for all
|
|
142 | + * subsequent requests.
|
|
143 | + */
|
|
144 | + clearIsolation() {
|
|
145 | + logger.trace("Clearing isolation nonces.");
|
|
146 | + |
|
147 | + // Per-domain and per contextId nonces are stored in maps, so simply clear
|
|
148 | + // them.
|
|
149 | + this.#noncesForDomains.clear();
|
|
150 | + this.#noncesForUserContextId.clear();
|
|
151 | + |
|
152 | + // Force a rotation on the next catch-all circuit use by setting the
|
|
153 | + // creation time to the epoch.
|
|
154 | + this.#catchallDirtySince = 0;
|
|
155 | + }
|
|
156 | + |
|
157 | + async observe(subject, topic, data) {
|
|
158 | + if (topic === "nsPref:changed" && data === NON_TOR_PROXY_PREF) {
|
|
159 | + if (Services.prefs.getBoolPref(NON_TOR_PROXY_PREF)) {
|
|
160 | + this.disable();
|
|
161 | + } else {
|
|
162 | + this.enable();
|
|
163 | + }
|
|
164 | + } else if (topic === NEW_IDENTITY_TOPIC) {
|
|
165 | + logger.info(
|
|
166 | + "New identity has been requested, clearing isolation tokens."
|
|
167 | + );
|
|
168 | + this.clearIsolation();
|
|
169 | + try {
|
|
170 | + await TorProtocolService.newnym();
|
|
171 | + } catch (e) {
|
|
172 | + logger.error("Could not send the newnym command", e);
|
|
173 | + // TODO: What UX to use here? See tor-browser#41708
|
|
174 | + }
|
|
175 | + }
|
|
176 | + }
|
|
177 | + |
|
178 | + /**
|
|
179 | + * Setup a filter that for every HTTPChannel, replaces the default SOCKS proxy
|
|
180 | + * with one that authenticates to the SOCKS server (the tor client process)
|
|
181 | + * with a username (the first party domain and userContextId) and a nonce
|
|
182 | + * password.
|
|
183 | + * Tor provides a separate circuit for each username+password combination.
|
|
184 | + */
|
|
185 | + #setupProxyFilter() {
|
|
186 | + const filterFunction = (aChannel, aProxy) => {
|
|
187 | + if (!this.#isolationEnabled) {
|
|
188 | + return aProxy;
|
|
189 | + }
|
|
190 | + try {
|
|
191 | + const channel = aChannel.QueryInterface(Ci.nsIChannel);
|
|
192 | + let firstPartyDomain =
|
|
193 | + channel.loadInfo.originAttributes.firstPartyDomain;
|
|
194 | + const userContextId = channel.loadInfo.originAttributes.userContextId;
|
|
195 | + if (firstPartyDomain === "") {
|
|
196 | + firstPartyDomain = CATCHALL_DOMAIN;
|
|
197 | + if (Date.now() - this.#catchallDirtySince > 1000 * 10 * 60) {
|
|
198 | + logger.info(
|
|
199 | + "tor catchall circuit has been dirty for over 10 minutes. Rotating."
|
|
200 | + );
|
|
201 | + this.#newCircuitForDomain(CATCHALL_DOMAIN);
|
|
202 | + this.#catchallDirtySince = Date.now();
|
|
203 | + }
|
|
204 | + }
|
|
205 | + const replacementProxy = this.#applySocksProxyCredentials(
|
|
206 | + aProxy,
|
|
207 | + firstPartyDomain,
|
|
208 | + userContextId
|
|
209 | + );
|
|
210 | + logger.debug(
|
|
211 | + `Requested ${channel.URI.spec} via ${replacementProxy.username}:${replacementProxy.password}`
|
|
212 | + );
|
|
213 | + return replacementProxy;
|
|
214 | + } catch (e) {
|
|
215 | + logger.error("Error while setting a new proxy", e);
|
|
216 | + return null;
|
|
217 | + }
|
|
218 | + };
|
|
219 | + |
|
220 | + ProtocolProxyService.registerChannelFilter(
|
|
221 | + {
|
|
222 | + applyFilter(aChannel, aProxy, aCallback) {
|
|
223 | + aCallback.onProxyFilterResult(filterFunction(aChannel, aProxy));
|
|
224 | + },
|
|
225 | + },
|
|
226 | + 0
|
|
227 | + );
|
|
228 | + }
|
|
229 | + |
|
230 | + /**
|
|
231 | + * Takes a proxyInfo object (originalProxy) and returns a new proxyInfo
|
|
232 | + * object with the same properties, except the username is set to the
|
|
233 | + * the domain and userContextId, and the password is a nonce.
|
|
234 | + */
|
|
235 | + #applySocksProxyCredentials(originalProxy, domain, userContextId) {
|
|
236 | + const proxy = originalProxy.QueryInterface(Ci.nsIProxyInfo);
|
|
237 | + const { username, password } = this.getSocksProxyCredentials(
|
|
238 | + domain,
|
|
239 | + userContextId,
|
|
240 | + true
|
|
241 | + );
|
|
242 | + return ProtocolProxyService.newProxyInfoWithAuth(
|
|
243 | + "socks",
|
|
244 | + proxy.host,
|
|
245 | + proxy.port,
|
|
246 | + username,
|
|
247 | + password,
|
|
248 | + "", // aProxyAuthorizationHeader
|
|
249 | + "", // aConnectionIsolationKey
|
|
250 | + proxy.flags,
|
|
251 | + proxy.failoverTimeout,
|
|
252 | + proxy.failoverProxy
|
|
253 | + );
|
|
254 | + }
|
|
255 | + |
|
256 | + /**
|
|
257 | + * Combine the needed data into a username for the proxy.
|
|
258 | + */
|
|
259 | + #makeUsername(domain, userContextId) {
|
|
260 | + if (!domain) {
|
|
261 | + domain = CATCHALL_DOMAIN;
|
|
262 | + }
|
|
263 | + return `${domain}:${userContextId}`;
|
|
264 | + }
|
|
265 | + |
|
266 | + /**
|
|
267 | + * Generate a new 128 bit random tag.
|
|
268 | + *
|
|
269 | + * Strictly speaking both using a cryptographic entropy source and using 128
|
|
270 | + * bits of entropy for the tag are likely overkill, as correct behavior only
|
|
271 | + * depends on how unlikely it is for there to be a collision.
|
|
272 | + */
|
|
273 | + #nonce() {
|
|
274 | + return Array.from(crypto.getRandomValues(new Uint8Array(16)), byte =>
|
|
275 | + byte.toString(16).padStart(2, "0")
|
|
276 | + ).join("");
|
|
277 | + }
|
|
278 | + |
|
279 | + /**
|
|
280 | + * Re-generate the nonce for a certain domain.
|
|
281 | + */
|
|
282 | + #newCircuitForDomain(domain) {
|
|
283 | + if (!domain) {
|
|
284 | + domain = CATCHALL_DOMAIN;
|
|
285 | + }
|
|
286 | + this.#noncesForDomains.set(domain, this.#nonce());
|
|
287 | + logger.info(
|
|
288 | + `New domain isolation for ${domain}: ${this.#noncesForDomains.get(
|
|
289 | + domain
|
|
290 | + )}`
|
|
291 | + );
|
|
292 | + }
|
|
293 | + |
|
294 | + /**
|
|
295 | + * Re-generate the nonce for a userContextId.
|
|
296 | + *
|
|
297 | + * Currently, this function is not hooked to anything.
|
|
298 | + */
|
|
299 | + #newCircuitForUserContextId(userContextId) {
|
|
300 | + this.#noncesForUserContextId.set(userContextId, this.#nonce());
|
|
301 | + logger.info(
|
|
302 | + `New container isolation for ${userContextId}: ${this.#noncesForUserContextId.get(
|
|
303 | + userContextId
|
|
304 | + )}`
|
|
305 | + );
|
|
306 | + }
|
|
307 | +}
|
|
308 | + |
|
309 | +/**
|
|
310 | + * Get the first party domain for a certain browser.
|
|
311 | + *
|
|
312 | + * @param browser The browser to get the FP-domain for.
|
|
313 | + *
|
|
314 | + * Please notice that it should be gBrowser.selectedBrowser, because
|
|
315 | + * browser.documentURI is the actual shown page, and might be an error page.
|
|
316 | + * In this case, we rely on currentURI, which for gBrowser is an alias of
|
|
317 | + * gBrowser.selectedBrowser.currentURI.
|
|
318 | + * See browser/base/content/tabbrowser.js and tor-browser#31562.
|
|
319 | + */
|
|
320 | +function getDomainForBrowser(browser) {
|
|
321 | + let fpd = browser.contentPrincipal.originAttributes.firstPartyDomain;
|
|
322 | + |
|
323 | + // Bug 31562: For neterror or certerror, get the original URL from
|
|
324 | + // browser.currentURI and use it to calculate the firstPartyDomain.
|
|
325 | + const knownErrors = [
|
|
326 | + "about:neterror",
|
|
327 | + "about:certerror",
|
|
328 | + "about:httpsonlyerror",
|
|
329 | + ];
|
|
330 | + const { documentURI } = browser;
|
|
331 | + if (
|
|
332 | + documentURI &&
|
|
333 | + documentURI.schemeIs("about") &&
|
|
334 | + knownErrors.some(x => documentURI.spec.startsWith(x))
|
|
335 | + ) {
|
|
336 | + const knownSchemes = ["http", "https"];
|
|
337 | + const currentURI = browser.currentURI;
|
|
338 | + if (currentURI && knownSchemes.some(x => currentURI.schemeIs(x))) {
|
|
339 | + try {
|
|
340 | + fpd = Services.eTLD.getBaseDomainFromHost(currentURI.host);
|
|
341 | + } catch (e) {
|
|
342 | + if (
|
|
343 | + e.result === Cr.NS_ERROR_HOST_IS_IP_ADDRESS ||
|
|
344 | + e.result === Cr.NS_ERROR_INSUFFICIENT_DOMAIN_LEVELS
|
|
345 | + ) {
|
|
346 | + fpd = currentURI.host;
|
|
347 | + } else {
|
|
348 | + logger.error(
|
|
349 | + `Failed to get first party domain for host ${currentURI.host}`,
|
|
350 | + e
|
|
351 | + );
|
|
352 | + }
|
|
353 | + }
|
|
354 | + }
|
|
355 | + }
|
|
356 | + |
|
357 | + return fpd;
|
|
358 | +}
|
|
359 | + |
|
360 | +const TorDomainIsolator = new TorDomainIsolatorImpl();
|
|
361 | +// Reduce global vars pollution
|
|
362 | +TorDomainIsolator.getDomainForBrowser = getDomainForBrowser; |
... | ... | @@ -4,6 +4,7 @@ |
4 | 4 | |
5 | 5 | var EXPORTED_SYMBOLS = ["TorProtocolService"];
|
6 | 6 | |
7 | +const { ConsoleAPI } = ChromeUtils.import("resource://gre/modules/Console.jsm");
|
|
7 | 8 | const { Services } = ChromeUtils.import("resource://gre/modules/Services.jsm");
|
8 | 9 | const { setTimeout } = ChromeUtils.import("resource://gre/modules/Timer.jsm");
|
9 | 10 | ChromeUtils.defineModuleGetter(
|
... | ... | @@ -11,9 +12,6 @@ ChromeUtils.defineModuleGetter( |
11 | 12 | "FileUtils",
|
12 | 13 | "resource://gre/modules/FileUtils.jsm"
|
13 | 14 | );
|
14 | -const { XPCOMUtils } = ChromeUtils.import(
|
|
15 | - "resource://gre/modules/XPCOMUtils.jsm"
|
|
16 | -);
|
|
17 | 15 | |
18 | 16 | Cu.importGlobalProperties(["crypto"]);
|
19 | 17 | |
... | ... | @@ -45,18 +43,9 @@ const TorTopics = Object.freeze({ |
45 | 43 | ProcessRestarted: "TorProcessRestarted",
|
46 | 44 | });
|
47 | 45 | |
48 | -// Logger adapted from CustomizableUI.jsm
|
|
49 | -XPCOMUtils.defineLazyGetter(this, "logger", () => {
|
|
50 | - const { ConsoleAPI } = ChromeUtils.import(
|
|
51 | - "resource://gre/modules/Console.jsm"
|
|
52 | - );
|
|
53 | - // TODO: Use a preference to set the log level.
|
|
54 | - const consoleOptions = {
|
|
55 | - // maxLogLevel: "warn",
|
|
56 | - maxLogLevel: "all",
|
|
57 | - prefix: "TorProtocolService",
|
|
58 | - };
|
|
59 | - return new ConsoleAPI(consoleOptions);
|
|
46 | +const logger = new ConsoleAPI({
|
|
47 | + maxLogLevel: "warn",
|
|
48 | + prefix: "TorProtocolService",
|
|
60 | 49 | });
|
61 | 50 | |
62 | 51 | // Manage the connection to tor's control port, to update its settings and query
|
... | ... | @@ -194,6 +183,10 @@ const TorProtocolService = { |
194 | 183 | TorMonitorService.retrieveBootstrapStatus();
|
195 | 184 | },
|
196 | 185 | |
186 | + async newnym() {
|
|
187 | + return this.sendCommand("SIGNAL NEWNYM");
|
|
188 | + },
|
|
189 | + |
|
197 | 190 | // TODO: transform the following 4 functions in getters. At the moment they
|
198 | 191 | // are also used in torbutton.
|
199 | 192 |
... | ... | @@ -33,6 +33,12 @@ ChromeUtils.defineModuleGetter( |
33 | 33 | "resource:///modules/TorSettings.jsm"
|
34 | 34 | );
|
35 | 35 | |
36 | +ChromeUtils.defineModuleGetter(
|
|
37 | + this,
|
|
38 | + "TorDomainIsolator",
|
|
39 | + "resource://gre/modules/TorDomainIsolator.jsm"
|
|
40 | +);
|
|
41 | + |
|
36 | 42 | /* Browser observer topis */
|
37 | 43 | const BrowserTopics = Object.freeze({
|
38 | 44 | ProfileAfterChange: "profile-after-change",
|
... | ... | @@ -67,12 +73,16 @@ class TorStartupService { |
67 | 73 | TorSettings.init();
|
68 | 74 | TorConnect.init();
|
69 | 75 | |
76 | + TorDomainIsolator.init();
|
|
77 | + |
|
70 | 78 | gInited = true;
|
71 | 79 | }
|
72 | 80 | |
73 | 81 | _uninit() {
|
74 | 82 | Services.obs.removeObserver(this, BrowserTopics.QuitApplicationGranted);
|
75 | 83 | |
84 | + TorDomainIsolator.uninit();
|
|
85 | + |
|
76 | 86 | // Close any helper connection first...
|
77 | 87 | TorProtocolService.uninit();
|
78 | 88 | // ... and only then closes the event monitor connection, which will cause
|
1 | 1 | EXTRA_JS_MODULES += [
|
2 | 2 | "TorBootstrapRequest.jsm",
|
3 | + "TorDomainIsolator.jsm",
|
|
3 | 4 | "TorLauncherUtil.jsm",
|
4 | 5 | "TorMonitorService.jsm",
|
5 | 6 | "TorParsers.jsm",
|
1 | 1 | // window globals
|
2 | 2 | var torbutton_init;
|
3 | -var torbutton_new_circuit;
|
|
4 | 3 | |
5 | 4 | (() => {
|
6 | 5 | // Bug 1506 P1-P5: This is the main Torbutton overlay file. Much needs to be
|
... | ... | @@ -16,9 +15,7 @@ var torbutton_new_circuit; |
16 | 15 | |
17 | 16 | let {
|
18 | 17 | unescapeTorString,
|
19 | - getDomainForBrowser,
|
|
20 | 18 | torbutton_log,
|
21 | - torbutton_get_property_string,
|
|
22 | 19 | } = ChromeUtils.import("resource://torbutton/modules/utils.js");
|
23 | 20 | let { configureControlPortModule, wait_for_controller } = ChromeUtils.import(
|
24 | 21 | "resource://torbutton/modules/tor-control-port.js"
|
... | ... | @@ -46,32 +43,22 @@ var torbutton_new_circuit; |
46 | 43 | // in a component, not the XUL overlay.
|
47 | 44 | var torbutton_unique_pref_observer = {
|
48 | 45 | register() {
|
49 | - this.forced_ua = false;
|
|
50 | - m_tb_prefs.addObserver("extensions.torbutton", this);
|
|
51 | - m_tb_prefs.addObserver("browser.privatebrowsing.autostart", this);
|
|
52 | - m_tb_prefs.addObserver("javascript", this);
|
|
46 | + Services.prefs.addObserver("browser.privatebrowsing.autostart", this);
|
|
53 | 47 | },
|
54 | 48 | |
55 | 49 | unregister() {
|
56 | - m_tb_prefs.removeObserver("extensions.torbutton", this);
|
|
57 | - m_tb_prefs.removeObserver("browser.privatebrowsing.autostart", this);
|
|
58 | - m_tb_prefs.removeObserver("javascript", this);
|
|
50 | + Services.prefs.removeObserver("browser.privatebrowsing.autostart", this);
|
|
59 | 51 | },
|
60 | 52 | |
61 | 53 | // topic: what event occurred
|
62 | 54 | // subject: what nsIPrefBranch we're observing
|
63 | 55 | // data: which pref has been changed (relative to subject)
|
64 | 56 | observe(subject, topic, data) {
|
65 | - if (topic !== "nsPref:changed") {
|
|
66 | - return;
|
|
67 | - }
|
|
68 | - switch (data) {
|
|
69 | - case "browser.privatebrowsing.autostart":
|
|
70 | - torbutton_update_disk_prefs();
|
|
71 | - break;
|
|
72 | - case "extensions.torbutton.use_nontor_proxy":
|
|
73 | - torbutton_use_nontor_proxy();
|
|
74 | - break;
|
|
57 | + if (
|
|
58 | + topic === "nsPref:changed" &&
|
|
59 | + data === "browser.privatebrowsing.autostart"
|
|
60 | + ) {
|
|
61 | + torbutton_update_disk_prefs();
|
|
75 | 62 | }
|
76 | 63 | },
|
77 | 64 | };
|
... | ... | @@ -113,62 +100,6 @@ var torbutton_new_circuit; |
113 | 100 | },
|
114 | 101 | };
|
115 | 102 | |
116 | - var torbutton_new_identity_observers = {
|
|
117 | - register() {
|
|
118 | - Services.obs.addObserver(this, "new-identity-requested");
|
|
119 | - },
|
|
120 | - |
|
121 | - observe(aSubject, aTopic, aData) {
|
|
122 | - if (aTopic !== "new-identity-requested") {
|
|
123 | - return;
|
|
124 | - }
|
|
125 | - |
|
126 | - // Clear the domain isolation state.
|
|
127 | - torbutton_log(3, "Clearing domain isolator");
|
|
128 | - const domainIsolator = Cc["@torproject.org/domain-isolator;1"].getService(
|
|
129 | - Ci.nsISupports
|
|
130 | - ).wrappedJSObject;
|
|
131 | - domainIsolator.clearIsolation();
|
|
132 | - |
|
133 | - torbutton_log(3, "New Identity: Sending NEWNYM");
|
|
134 | - // We only support TBB for newnym.
|
|
135 | - if (
|
|
136 | - !m_tb_control_pass ||
|
|
137 | - (!m_tb_control_ipc_file && !m_tb_control_port)
|
|
138 | - ) {
|
|
139 | - const warning = torbutton_get_property_string(
|
|
140 | - "torbutton.popup.no_newnym"
|
|
141 | - );
|
|
142 | - torbutton_log(
|
|
143 | - 5,
|
|
144 | - "Torbutton cannot safely newnym. It does not have access to the Tor Control Port."
|
|
145 | - );
|
|
146 | - window.alert(warning);
|
|
147 | - } else {
|
|
148 | - const warning = torbutton_get_property_string(
|
|
149 | - "torbutton.popup.no_newnym"
|
|
150 | - );
|
|
151 | - torbutton_send_ctrl_cmd("SIGNAL NEWNYM")
|
|
152 | - .then(res => {
|
|
153 | - if (!res) {
|
|
154 | - torbutton_log(
|
|
155 | - 5,
|
|
156 | - "Torbutton was unable to request a new circuit from Tor"
|
|
157 | - );
|
|
158 | - window.alert(warning);
|
|
159 | - }
|
|
160 | - })
|
|
161 | - .catch(e => {
|
|
162 | - torbutton_log(
|
|
163 | - 5,
|
|
164 | - "Torbutton was unable to request a new circuit from Tor " + e
|
|
165 | - );
|
|
166 | - window.alert(warning);
|
|
167 | - });
|
|
168 | - }
|
|
169 | - },
|
|
170 | - };
|
|
171 | - |
|
172 | 103 | // Bug 1506 P2-P4: This code sets some version variables that are irrelevant.
|
173 | 104 | // It does read out some important environment variables, though. It is
|
174 | 105 | // called once per browser window.. This might belong in a component.
|
... | ... | @@ -258,8 +189,6 @@ var torbutton_new_circuit; |
258 | 189 | true
|
259 | 190 | );
|
260 | 191 | |
261 | - torbutton_new_identity_observers.register();
|
|
262 | - |
|
263 | 192 | torbutton_log(3, "init completed");
|
264 | 193 | };
|
265 | 194 | |
... | ... | @@ -374,36 +303,6 @@ var torbutton_new_circuit; |
374 | 303 | return response;
|
375 | 304 | }
|
376 | 305 | |
377 | - // Bug 1506 P4: Needed for New IP Address
|
|
378 | - torbutton_new_circuit = function() {
|
|
379 | - let firstPartyDomain = getDomainForBrowser(gBrowser.selectedBrowser);
|
|
380 | - |
|
381 | - let domainIsolator = Cc["@torproject.org/domain-isolator;1"].getService(
|
|
382 | - Ci.nsISupports
|
|
383 | - ).wrappedJSObject;
|
|
384 | - |
|
385 | - domainIsolator.newCircuitForDomain(firstPartyDomain);
|
|
386 | - |
|
387 | - gBrowser.reloadWithFlags(Ci.nsIWebNavigation.LOAD_FLAGS_BYPASS_CACHE);
|
|
388 | - };
|
|
389 | - |
|
390 | - /* Called when we switch the use_nontor_proxy pref in either direction.
|
|
391 | - *
|
|
392 | - * Enables/disables domain isolation and then does new identity
|
|
393 | - */
|
|
394 | - function torbutton_use_nontor_proxy() {
|
|
395 | - let domainIsolator = Cc["@torproject.org/domain-isolator;1"].getService(
|
|
396 | - Ci.nsISupports
|
|
397 | - ).wrappedJSObject;
|
|
398 | - |
|
399 | - if (m_tb_prefs.getBoolPref("extensions.torbutton.use_nontor_proxy")) {
|
|
400 | - // Disable domain isolation
|
|
401 | - domainIsolator.disableIsolation();
|
|
402 | - } else {
|
|
403 | - domainIsolator.enableIsolation();
|
|
404 | - }
|
|
405 | - }
|
|
406 | - |
|
407 | 306 | async function torbutton_do_tor_check() {
|
408 | 307 | let checkSvc = Cc["@torproject.org/torbutton-torCheckService;1"].getService(
|
409 | 308 | Ci.nsISupports
|
1 | -// # domain-isolator.js
|
|
2 | -// A component for TorBrowser that puts requests from different
|
|
3 | -// first party domains on separate tor circuits.
|
|
4 | - |
|
5 | -// This file is written in call stack order (later functions
|
|
6 | -// call earlier functions). The code file can be processed
|
|
7 | -// with docco.js to provide clear documentation.
|
|
8 | - |
|
9 | -// ### Abbreviations
|
|
10 | - |
|
11 | -const { Services } = ChromeUtils.import("resource://gre/modules/Services.jsm");
|
|
12 | -const { XPCOMUtils } = ChromeUtils.import(
|
|
13 | - "resource://gre/modules/XPCOMUtils.jsm"
|
|
14 | -);
|
|
15 | - |
|
16 | -XPCOMUtils.defineLazyModuleGetters(this, {
|
|
17 | - ComponentUtils: "resource://gre/modules/ComponentUtils.jsm",
|
|
18 | -});
|
|
19 | - |
|
20 | -// Make the logger available.
|
|
21 | -let logger = Cc["@torproject.org/torbutton-logger;1"].getService(Ci.nsISupports)
|
|
22 | - .wrappedJSObject;
|
|
23 | - |
|
24 | -// Import crypto object (FF 37+).
|
|
25 | -Cu.importGlobalProperties(["crypto"]);
|
|
26 | - |
|
27 | -// ## mozilla namespace.
|
|
28 | -// Useful functionality for interacting with Mozilla services.
|
|
29 | -let mozilla = {};
|
|
30 | - |
|
31 | -// __mozilla.protocolProxyService__.
|
|
32 | -// Mozilla's protocol proxy service, useful for managing proxy connections made
|
|
33 | -// by the browser.
|
|
34 | -mozilla.protocolProxyService = Cc[
|
|
35 | - "@mozilla.org/network/protocol-proxy-service;1"
|
|
36 | -].getService(Ci.nsIProtocolProxyService);
|
|
37 | - |
|
38 | -// __mozilla.registerProxyChannelFilter(filterFunction, positionIndex)__.
|
|
39 | -// Registers a proxy channel filter with the Mozilla Protocol Proxy Service,
|
|
40 | -// which will help to decide the proxy to be used for a given channel.
|
|
41 | -// The filterFunction should expect two arguments, (aChannel, aProxy),
|
|
42 | -// where aProxy is the proxy or list of proxies that would be used by default
|
|
43 | -// for the given channel, and should return a new Proxy or list of Proxies.
|
|
44 | -mozilla.registerProxyChannelFilter = function(filterFunction, positionIndex) {
|
|
45 | - let proxyFilter = {
|
|
46 | - applyFilter(aChannel, aProxy, aCallback) {
|
|
47 | - aCallback.onProxyFilterResult(filterFunction(aChannel, aProxy));
|
|
48 | - },
|
|
49 | - };
|
|
50 | - mozilla.protocolProxyService.registerChannelFilter(
|
|
51 | - proxyFilter,
|
|
52 | - positionIndex
|
|
53 | - );
|
|
54 | -};
|
|
55 | - |
|
56 | -// ## tor functionality.
|
|
57 | -let tor = {};
|
|
58 | - |
|
59 | -// __tor.noncesForDomains__.
|
|
60 | -// A mutable map that records what nonce we are using for each domain.
|
|
61 | -tor.noncesForDomains = new Map();
|
|
62 | - |
|
63 | -// __tor.noncesForUserContextId__.
|
|
64 | -// A mutable map that records what nonce we are using for each tab container.
|
|
65 | -tor.noncesForUserContextId = new Map();
|
|
66 | - |
|
67 | -// __tor.isolationEabled__.
|
|
68 | -// A bool that controls if we use SOCKS auth for isolation or not.
|
|
69 | -tor.isolationEnabled = true;
|
|
70 | - |
|
71 | -// __tor.unknownDirtySince__.
|
|
72 | -// Specifies when the current catch-all circuit was first used
|
|
73 | -tor.unknownDirtySince = Date.now();
|
|
74 | - |
|
75 | -tor.passwordForDomainAndUserContextId = function(
|
|
76 | - domain,
|
|
77 | - userContextId,
|
|
78 | - create
|
|
79 | -) {
|
|
80 | - // Check if we already have a nonce. If not, possibly create one for this
|
|
81 | - // domain and userContextId.
|
|
82 | - if (!tor.noncesForDomains.has(domain)) {
|
|
83 | - if (!create) {
|
|
84 | - return null;
|
|
85 | - }
|
|
86 | - tor.noncesForDomains.set(domain, tor.nonce());
|
|
87 | - }
|
|
88 | - if (!tor.noncesForUserContextId.has(userContextId)) {
|
|
89 | - if (!create) {
|
|
90 | - return null;
|
|
91 | - }
|
|
92 | - tor.noncesForUserContextId.set(userContextId, tor.nonce());
|
|
93 | - }
|
|
94 | - return (
|
|
95 | - tor.noncesForDomains.get(domain) +
|
|
96 | - tor.noncesForUserContextId.get(userContextId)
|
|
97 | - );
|
|
98 | -};
|
|
99 | - |
|
100 | -tor.usernameForDomainAndUserContextId = function(domain, userContextId) {
|
|
101 | - return `${domain}:${userContextId}`;
|
|
102 | -};
|
|
103 | - |
|
104 | -// __tor.socksProxyCredentials(originalProxy, domain, userContextId)__.
|
|
105 | -// Takes a proxyInfo object (originalProxy) and returns a new proxyInfo
|
|
106 | -// object with the same properties, except the username is set to the
|
|
107 | -// the domain and userContextId, and the password is a nonce.
|
|
108 | -tor.socksProxyCredentials = function(originalProxy, domain, userContextId) {
|
|
109 | - let proxy = originalProxy.QueryInterface(Ci.nsIProxyInfo);
|
|
110 | - let proxyUsername = tor.usernameForDomainAndUserContextId(
|
|
111 | - domain,
|
|
112 | - userContextId
|
|
113 | - );
|
|
114 | - let proxyPassword = tor.passwordForDomainAndUserContextId(
|
|
115 | - domain,
|
|
116 | - userContextId,
|
|
117 | - true
|
|
118 | - );
|
|
119 | - return mozilla.protocolProxyService.newProxyInfoWithAuth(
|
|
120 | - "socks",
|
|
121 | - proxy.host,
|
|
122 | - proxy.port,
|
|
123 | - proxyUsername,
|
|
124 | - proxyPassword,
|
|
125 | - "", // aProxyAuthorizationHeader
|
|
126 | - "", // aConnectionIsolationKey
|
|
127 | - proxy.flags,
|
|
128 | - proxy.failoverTimeout,
|
|
129 | - proxy.failoverProxy
|
|
130 | - );
|
|
131 | -};
|
|
132 | - |
|
133 | -tor.nonce = function() {
|
|
134 | - // Generate a new 128 bit random tag. Strictly speaking both using a
|
|
135 | - // cryptographic entropy source and using 128 bits of entropy for the
|
|
136 | - // tag are likely overkill, as correct behavior only depends on how
|
|
137 | - // unlikely it is for there to be a collision.
|
|
138 | - let tag = new Uint8Array(16);
|
|
139 | - crypto.getRandomValues(tag);
|
|
140 | - |
|
141 | - // Convert the tag to a hex string.
|
|
142 | - let tagStr = "";
|
|
143 | - for (let i = 0; i < tag.length; i++) {
|
|
144 | - tagStr += (tag[i] >>> 4).toString(16);
|
|
145 | - tagStr += (tag[i] & 0x0f).toString(16);
|
|
146 | - }
|
|
147 | - |
|
148 | - return tagStr;
|
|
149 | -};
|
|
150 | - |
|
151 | -tor.newCircuitForDomain = function(domain) {
|
|
152 | - // Re-generate the nonce for the domain.
|
|
153 | - if (domain === "") {
|
|
154 | - domain = "--unknown--";
|
|
155 | - }
|
|
156 | - tor.noncesForDomains.set(domain, tor.nonce());
|
|
157 | - logger.eclog(
|
|
158 | - 3,
|
|
159 | - `New domain isolation for ${domain}: ${tor.noncesForDomains.get(domain)}`
|
|
160 | - );
|
|
161 | -};
|
|
162 | - |
|
163 | -tor.newCircuitForUserContextId = function(userContextId) {
|
|
164 | - // Re-generate the nonce for the context.
|
|
165 | - tor.noncesForUserContextId.set(userContextId, tor.nonce());
|
|
166 | - logger.eclog(
|
|
167 | - 3,
|
|
168 | - `New container isolation for ${userContextId}: ${tor.noncesForUserContextId.get(
|
|
169 | - userContextId
|
|
170 | - )}`
|
|
171 | - );
|
|
172 | -};
|
|
173 | - |
|
174 | -// __tor.clearIsolation()_.
|
|
175 | -// Clear the isolation state cache, forcing new circuits to be used for all
|
|
176 | -// subsequent requests.
|
|
177 | -tor.clearIsolation = function() {
|
|
178 | - // Per-domain and per contextId nonces are stored in maps, so simply clear them.
|
|
179 | - tor.noncesForDomains.clear();
|
|
180 | - tor.noncesForUserContextId.clear();
|
|
181 | - |
|
182 | - // Force a rotation on the next catch-all circuit use by setting the creation
|
|
183 | - // time to the epoch.
|
|
184 | - tor.unknownDirtySince = 0;
|
|
185 | -};
|
|
186 | - |
|
187 | -// __tor.isolateCircuitsByDomain()__.
|
|
188 | -// For every HTTPChannel, replaces the default SOCKS proxy with one that authenticates
|
|
189 | -// to the SOCKS server (the tor client process) with a username (the first party domain
|
|
190 | -// and userContextId) and a nonce password. Tor provides a separate circuit for each
|
|
191 | -// username+password combination.
|
|
192 | -tor.isolateCircuitsByDomain = function() {
|
|
193 | - mozilla.registerProxyChannelFilter(function(aChannel, aProxy) {
|
|
194 | - if (!tor.isolationEnabled) {
|
|
195 | - return aProxy;
|
|
196 | - }
|
|
197 | - try {
|
|
198 | - let channel = aChannel.QueryInterface(Ci.nsIChannel),
|
|
199 | - firstPartyDomain = channel.loadInfo.originAttributes.firstPartyDomain,
|
|
200 | - userContextId = channel.loadInfo.originAttributes.userContextId;
|
|
201 | - if (firstPartyDomain === "") {
|
|
202 | - firstPartyDomain = "--unknown--";
|
|
203 | - if (Date.now() - tor.unknownDirtySince > 1000 * 10 * 60) {
|
|
204 | - logger.eclog(
|
|
205 | - 3,
|
|
206 | - "tor catchall circuit has been dirty for over 10 minutes. Rotating."
|
|
207 | - );
|
|
208 | - tor.newCircuitForDomain("--unknown--");
|
|
209 | - tor.unknownDirtySince = Date.now();
|
|
210 | - }
|
|
211 | - }
|
|
212 | - let replacementProxy = tor.socksProxyCredentials(
|
|
213 | - aProxy,
|
|
214 | - firstPartyDomain,
|
|
215 | - userContextId
|
|
216 | - );
|
|
217 | - logger.eclog(
|
|
218 | - 3,
|
|
219 | - `tor SOCKS: ${channel.URI.spec} via
|
|
220 | - ${replacementProxy.username}:${replacementProxy.password}`
|
|
221 | - );
|
|
222 | - return replacementProxy;
|
|
223 | - } catch (e) {
|
|
224 | - logger.eclog(4, `tor domain isolator error: ${e.message}`);
|
|
225 | - return null;
|
|
226 | - }
|
|
227 | - }, 0);
|
|
228 | -};
|
|
229 | - |
|
230 | -// ## XPCOM component construction.
|
|
231 | -// Module specific constants
|
|
232 | -const kMODULE_NAME = "TorBrowser Domain Isolator";
|
|
233 | -const kMODULE_CONTRACTID = "@torproject.org/domain-isolator;1";
|
|
234 | -const kMODULE_CID = Components.ID("e33fd6d4-270f-475f-a96f-ff3140279f68");
|
|
235 | - |
|
236 | -// DomainIsolator object.
|
|
237 | -function DomainIsolator() {
|
|
238 | - this.wrappedJSObject = this;
|
|
239 | -}
|
|
240 | - |
|
241 | -// Firefox component requirements
|
|
242 | -DomainIsolator.prototype = {
|
|
243 | - QueryInterface: ChromeUtils.generateQI([Ci.nsIObserver]),
|
|
244 | - classDescription: kMODULE_NAME,
|
|
245 | - classID: kMODULE_CID,
|
|
246 | - contractID: kMODULE_CONTRACTID,
|
|
247 | - observe(subject, topic, data) {
|
|
248 | - if (topic === "profile-after-change") {
|
|
249 | - logger.eclog(3, "domain isolator: set up isolating circuits by domain");
|
|
250 | - |
|
251 | - if (Services.prefs.getBoolPref("extensions.torbutton.use_nontor_proxy")) {
|
|
252 | - tor.isolationEnabled = false;
|
|
253 | - }
|
|
254 | - tor.isolateCircuitsByDomain();
|
|
255 | - }
|
|
256 | - },
|
|
257 | - |
|
258 | - newCircuitForDomain(domain) {
|
|
259 | - tor.newCircuitForDomain(domain);
|
|
260 | - },
|
|
261 | - |
|
262 | - /**
|
|
263 | - * Return the stored SOCKS proxy username and password for the given domain
|
|
264 | - * and user context ID.
|
|
265 | - *
|
|
266 | - * @param {string} firstPartyDomain - The domain to lookup credentials for.
|
|
267 | - * @param {integer} userContextId - The ID for the user context.
|
|
268 | - *
|
|
269 | - * @return {{ username: string, password: string }?} - The SOCKS credentials,
|
|
270 | - * or null if none are found.
|
|
271 | - */
|
|
272 | - getSocksProxyCredentials(firstPartyDomain, userContextId) {
|
|
273 | - if (firstPartyDomain == "") {
|
|
274 | - firstPartyDomain = "--unknown--";
|
|
275 | - }
|
|
276 | - let proxyPassword = tor.passwordForDomainAndUserContextId(
|
|
277 | - firstPartyDomain,
|
|
278 | - userContextId,
|
|
279 | - // Do not create a new entry if it does not exist.
|
|
280 | - false
|
|
281 | - );
|
|
282 | - if (!proxyPassword) {
|
|
283 | - return null;
|
|
284 | - }
|
|
285 | - return {
|
|
286 | - username: tor.usernameForDomainAndUserContextId(
|
|
287 | - firstPartyDomain,
|
|
288 | - userContextId
|
|
289 | - ),
|
|
290 | - password: proxyPassword,
|
|
291 | - };
|
|
292 | - },
|
|
293 | - |
|
294 | - enableIsolation() {
|
|
295 | - tor.isolationEnabled = true;
|
|
296 | - },
|
|
297 | - |
|
298 | - disableIsolation() {
|
|
299 | - tor.isolationEnabled = false;
|
|
300 | - },
|
|
301 | - |
|
302 | - clearIsolation() {
|
|
303 | - tor.clearIsolation();
|
|
304 | - },
|
|
305 | - |
|
306 | - wrappedJSObject: null,
|
|
307 | -};
|
|
308 | - |
|
309 | -// Assign factory to global object.
|
|
310 | -const NSGetFactory = XPCOMUtils.generateNSGetFactory
|
|
311 | - ? XPCOMUtils.generateNSGetFactory([DomainIsolator])
|
|
312 | - : ComponentUtils.generateNSGetFactory([DomainIsolator]); |
... | ... | @@ -43,9 +43,5 @@ torbutton.jar: |
43 | 43 | % component {f36d72c9-9718-4134-b550-e109638331d7} %components/torbutton-logger.js
|
44 | 44 | % contract @torproject.org/torbutton-logger;1 {f36d72c9-9718-4134-b550-e109638331d7}
|
45 | 45 | |
46 | -% component {e33fd6d4-270f-475f-a96f-ff3140279f68} %components/domain-isolator.js
|
|
47 | -% contract @torproject.org/domain-isolator;1 {e33fd6d4-270f-475f-a96f-ff3140279f68}
|
|
48 | - |
|
49 | 46 | % category profile-after-change StartupObserver @torproject.org/startup-observer;1
|
50 | -% category profile-after-change DomainIsolator @torproject.org/domain-isolator;1
|
|
51 | 47 | % category profile-after-change DragDropFilter @torproject.org/torbutton-dragDropFilter;1 |
... | ... | @@ -213,45 +213,6 @@ var unescapeTorString = function(str) { |
213 | 213 | return _torControl._strUnescape(str);
|
214 | 214 | };
|
215 | 215 | |
216 | -var getFPDFromHost = hostname => {
|
|
217 | - try {
|
|
218 | - return Services.eTLD.getBaseDomainFromHost(hostname);
|
|
219 | - } catch (e) {
|
|
220 | - if (
|
|
221 | - e.result == Cr.NS_ERROR_HOST_IS_IP_ADDRESS ||
|
|
222 | - e.result == Cr.NS_ERROR_INSUFFICIENT_DOMAIN_LEVELS
|
|
223 | - ) {
|
|
224 | - return hostname;
|
|
225 | - }
|
|
226 | - }
|
|
227 | - return null;
|
|
228 | -};
|
|
229 | - |
|
230 | -// Assuming this is called with gBrowser.selectedBrowser
|
|
231 | -var getDomainForBrowser = browser => {
|
|
232 | - let fpd = browser.contentPrincipal.originAttributes.firstPartyDomain;
|
|
233 | - // Bug 31562: For neterror or certerror, get the original URL from
|
|
234 | - // browser.currentURI and use it to calculate the firstPartyDomain.
|
|
235 | - let knownErrors = [
|
|
236 | - "about:neterror",
|
|
237 | - "about:certerror",
|
|
238 | - "about:httpsonlyerror",
|
|
239 | - ];
|
|
240 | - let documentURI = browser.documentURI;
|
|
241 | - if (
|
|
242 | - documentURI &&
|
|
243 | - documentURI.schemeIs("about") &&
|
|
244 | - knownErrors.some(x => documentURI.spec.startsWith(x))
|
|
245 | - ) {
|
|
246 | - let knownSchemes = ["http", "https", "ftp"];
|
|
247 | - let currentURI = browser.currentURI;
|
|
248 | - if (currentURI && knownSchemes.some(x => currentURI.schemeIs(x))) {
|
|
249 | - fpd = getFPDFromHost(currentURI.host) || fpd;
|
|
250 | - }
|
|
251 | - }
|
|
252 | - return fpd;
|
|
253 | -};
|
|
254 | - |
|
255 | 216 | var m_tb_torlog = Cc["@torproject.org/torbutton-logger;1"].getService(
|
256 | 217 | Ci.nsISupports
|
257 | 218 | ).wrappedJSObject;
|
... | ... | @@ -310,7 +271,6 @@ let EXPORTED_SYMBOLS = [ |
310 | 271 | "bindPrefAndInit",
|
311 | 272 | "getEnv",
|
312 | 273 | "getLocale",
|
313 | - "getDomainForBrowser",
|
|
314 | 274 | "getPrefValue",
|
315 | 275 | "observe",
|
316 | 276 | "showDialog",
|