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:
Changes:
browser/actors/CryptoSafetyParent.jsm
... |
... |
@@ -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
|
} |
browser/base/content/appmenu-viewcache.inc.xhtml
... |
... |
@@ -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"
|
browser/base/content/browser-menubar.inc
... |
... |
@@ -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"
|
browser/base/content/browser-sets.inc
... |
... |
@@ -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> |
browser/base/content/browser.js
... |
... |
@@ -82,6 +82,7 @@ XPCOMUtils.defineLazyModuleGetters(this, { |
82
|
82
|
TorConnect: "resource:///modules/TorConnect.jsm",
|
83
|
83
|
TorConnectState: "resource:///modules/TorConnect.jsm",
|
84
|
84
|
TorConnectTopics: "resource:///modules/TorConnect.jsm",
|
|
85
|
+ TorDomainIsolator: "resource://gre/modules/TorDomainIsolator.jsm",
|
85
|
86
|
Translation: "resource:///modules/translation/TranslationParent.jsm",
|
86
|
87
|
UITour: "resource:///modules/UITour.jsm",
|
87
|
88
|
UpdateUtils: "resource://gre/modules/UpdateUtils.jsm",
|
browser/base/content/navigator-toolbox.inc.xhtml
... |
... |
@@ -569,7 +569,7 @@ |
569
|
569
|
|
570
|
570
|
<toolbarbutton id="new-circuit-button" class="toolbarbutton-1 chromeclass-toolbar-additional"
|
571
|
571
|
label="&torbutton.context_menu.new_circuit;"
|
572
|
|
- _oncommand_="torbutton_new_circuit();"
|
|
572
|
+ _oncommand_="TorDomainIsolator.newCircuitForBrowser(gBrowser);"
|
573
|
573
|
tooltiptext="&torbutton.context_menu.new_circuit;"/>
|
574
|
574
|
|
575
|
575
|
<toolbarbutton id="fullscreen-button" class="toolbarbutton-1 chromeclass-toolbar-additional"
|
browser/components/torcircuit/content/torCircuitPanel.js
... |
... |
@@ -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
|
);
|
toolkit/components/tor-launcher/TorDomainIsolator.jsm
|
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; |
toolkit/components/tor-launcher/TorProtocolService.jsm
... |
... |
@@ -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
|
|
toolkit/components/tor-launcher/TorStartupService.jsm
... |
... |
@@ -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
|
toolkit/components/tor-launcher/moz.build
1
|
1
|
EXTRA_JS_MODULES += [
|
2
|
2
|
"TorBootstrapRequest.jsm",
|
|
3
|
+ "TorDomainIsolator.jsm",
|
3
|
4
|
"TorLauncherUtil.jsm",
|
4
|
5
|
"TorMonitorService.jsm",
|
5
|
6
|
"TorParsers.jsm",
|
toolkit/torbutton/chrome/content/torbutton.js
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
|
toolkit/torbutton/components/domain-isolator.js
deleted
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]); |
toolkit/torbutton/jar.mn
... |
... |
@@ -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 |
toolkit/torbutton/modules/utils.js
... |
... |
@@ -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",
|
|