Pier Angelo Vendrame pushed to branch tor-browser-115.1.0esr-13.0-1 at The Tor Project / Applications / Tor Browser
Commits:
-
c3d2496b
by Pier Angelo Vendrame at 2023-07-27T21:07:52+02:00
-
642df684
by Pier Angelo Vendrame at 2023-07-27T21:07:53+02:00
-
9e825b61
by Pier Angelo Vendrame at 2023-07-27T21:07:53+02:00
-
8061f810
by Pier Angelo Vendrame at 2023-07-27T21:07:53+02:00
-
f5a3f4af
by Pier Angelo Vendrame at 2023-07-27T21:07:54+02:00
-
71b08c4e
by Pier Angelo Vendrame at 2023-07-27T21:07:54+02:00
-
e1a69b4e
by Pier Angelo Vendrame at 2023-07-27T21:07:55+02:00
-
d457b6f8
by Pier Angelo Vendrame at 2023-07-31T20:42:54+02:00
-
c4e8cd0f
by Pier Angelo Vendrame at 2023-07-31T20:42:57+02:00
13 changed files:
- browser/components/torpreferences/content/connectionPane.js
- browser/installer/package-manifest.in
- browser/modules/TorStrings.jsm
- toolkit/components/tor-launcher/TorLauncherUtil.sys.mjs
- toolkit/components/tor-launcher/TorMonitorService.sys.mjs
- toolkit/components/tor-launcher/TorParsers.sys.mjs
- toolkit/components/tor-launcher/TorProcess.sys.mjs
- toolkit/components/tor-launcher/TorProtocolService.sys.mjs
- toolkit/torbutton/chrome/content/torbutton.js
- toolkit/torbutton/components.conf
- − toolkit/torbutton/modules/TorbuttonStartupObserver.jsm
- toolkit/torbutton/moz.build
- − toolkit/torbutton/torbutton.manifest
Changes:
... | ... | @@ -14,8 +14,11 @@ const { setTimeout, clearTimeout } = ChromeUtils.import( |
14 | 14 | const { TorSettings, TorSettingsTopics, TorSettingsData, TorBridgeSource } =
|
15 | 15 | ChromeUtils.import("resource:///modules/TorSettings.jsm");
|
16 | 16 | |
17 | -const { TorProtocolService } = ChromeUtils.import(
|
|
18 | - "resource://gre/modules/TorProtocolService.jsm"
|
|
17 | +const { TorParsers } = ChromeUtils.importESModule(
|
|
18 | + "resource://gre/modules/TorParsers.sys.mjs"
|
|
19 | +);
|
|
20 | +const { TorProtocolService } = ChromeUtils.importESModule(
|
|
21 | + "resource://gre/modules/TorProtocolService.sys.mjs"
|
|
19 | 22 | );
|
20 | 23 | const { TorMonitorService, TorMonitorTopics } = ChromeUtils.import(
|
21 | 24 | "resource://gre/modules/TorMonitorService.jsm"
|
... | ... | @@ -495,7 +498,7 @@ const gConnectionPane = (function () { |
495 | 498 | });
|
496 | 499 | const idString = TorStrings.settings.bridgeId;
|
497 | 500 | const id = card.querySelector(selectors.bridges.cardId);
|
498 | - const details = parseBridgeLine(bridgeString);
|
|
501 | + const details = TorParsers.parseBridgeLine(bridgeString);
|
|
499 | 502 | if (details && details.id !== undefined) {
|
500 | 503 | card.setAttribute("data-bridge-id", details.id);
|
501 | 504 | }
|
... | ... | @@ -1111,23 +1114,3 @@ function makeBridgeId(bridgeString) { |
1111 | 1114 | hash & 0x000000ff,
|
1112 | 1115 | ];
|
1113 | 1116 | } |
1114 | - |
|
1115 | -function parseBridgeLine(line) {
|
|
1116 | - const re =
|
|
1117 | - /^\s*(\S+\s+)?([0-9a-fA-F\.\[\]\:]+:\d{1,5})(\s+[0-9a-fA-F]{40})?(\s+.+)?/;
|
|
1118 | - const matches = line.match(re);
|
|
1119 | - if (!matches) {
|
|
1120 | - return null;
|
|
1121 | - }
|
|
1122 | - let bridge = { addr: matches[2] };
|
|
1123 | - if (matches[1] !== undefined) {
|
|
1124 | - bridge.transport = matches[1].trim();
|
|
1125 | - }
|
|
1126 | - if (matches[3] !== undefined) {
|
|
1127 | - bridge.id = matches[3].trim().toUpperCase();
|
|
1128 | - }
|
|
1129 | - if (matches[4] !== undefined) {
|
|
1130 | - bridge.args = matches[4].trim();
|
|
1131 | - }
|
|
1132 | - return bridge;
|
|
1133 | -} |
... | ... | @@ -228,7 +228,6 @@ |
228 | 228 | @RESPATH@/components/tor-launcher.manifest
|
229 | 229 | @RESPATH@/chrome/torbutton.manifest
|
230 | 230 | @RESPATH@/chrome/torbutton/*
|
231 | -@RESPATH@/components/torbutton.manifest
|
|
232 | 231 | @RESPATH@/chrome/toolkit@JAREXT@
|
233 | 232 | @RESPATH@/chrome/toolkit.manifest
|
234 | 233 | #ifdef MOZ_GTK
|
... | ... | @@ -11,9 +11,11 @@ const { Services } = ChromeUtils.import("resource://gre/modules/Services.jsm"); |
11 | 11 | const { AppConstants } = ChromeUtils.import(
|
12 | 12 | "resource://gre/modules/AppConstants.jsm"
|
13 | 13 | );
|
14 | -const { getLocale } = ChromeUtils.import(
|
|
15 | - "resource://torbutton/modules/utils.js"
|
|
16 | -);
|
|
14 | + |
|
15 | +function getLocale() {
|
|
16 | + const locale = Services.locale.appLocaleAsBCP47;
|
|
17 | + return locale === "ja-JP-macos" ? "ja" : locale;
|
|
18 | +}
|
|
17 | 19 | |
18 | 20 | /*
|
19 | 21 | Tor Property String Bundle
|
... | ... | @@ -5,6 +5,12 @@ |
5 | 5 | * Tor Launcher Util JS Module
|
6 | 6 | *************************************************************************/
|
7 | 7 | |
8 | +const lazy = {};
|
|
9 | + |
|
10 | +ChromeUtils.defineESModuleGetters(lazy, {
|
|
11 | + FileUtils: "resource://gre/modules/FileUtils.sys.jsm",
|
|
12 | +});
|
|
13 | + |
|
8 | 14 | const kPropBundleURI = "chrome://torbutton/locale/torlauncher.properties";
|
9 | 15 | const kPropNamePrefix = "torlauncher.";
|
10 | 16 | const kIPCDirPrefName = "extensions.torlauncher.tmp_ipc_dir";
|
... | ... | @@ -209,14 +215,15 @@ class TorFile { |
209 | 215 | // and return a file object. The control and SOCKS IPC objects will be
|
210 | 216 | // created by tor.
|
211 | 217 | normalize() {
|
212 | - if (!this.file.exists() && !this.isIPC) {
|
|
218 | + if (this.file.exists()) {
|
|
219 | + try {
|
|
220 | + this.file.normalize();
|
|
221 | + } catch (e) {
|
|
222 | + console.warn("Normalization of the path failed", e);
|
|
223 | + }
|
|
224 | + } else if (!this.isIPC) {
|
|
213 | 225 | throw new Error(`${this.fileType} file not found: ${this.file.path}`);
|
214 | 226 | }
|
215 | - try {
|
|
216 | - this.file.normalize();
|
|
217 | - } catch (e) {
|
|
218 | - console.warn("Normalization of the path failed", e);
|
|
219 | - }
|
|
220 | 227 | |
221 | 228 | // Ensure that the IPC path length is short enough for use by the
|
222 | 229 | // operating system. If not, create and use a unique directory under
|
... | ... | @@ -452,6 +459,154 @@ export const TorLauncherUtil = Object.freeze({ |
452 | 459 | return result ? result : "";
|
453 | 460 | },
|
454 | 461 | |
462 | + /**
|
|
463 | + * Determine what kind of SOCKS port has been requested for this session or
|
|
464 | + * the browser has been configured for.
|
|
465 | + * On Windows (where Unix domain sockets are not supported), TCP is always
|
|
466 | + * used.
|
|
467 | + *
|
|
468 | + * The following environment variables are supported and take precedence over
|
|
469 | + * preferences:
|
|
470 | + * TOR_TRANSPROXY (do not use a proxy)
|
|
471 | + * TOR_SOCKS_IPC_PATH (file system path; ignored on Windows)
|
|
472 | + * TOR_SOCKS_HOST
|
|
473 | + * TOR_SOCKS_PORT
|
|
474 | + *
|
|
475 | + * The following preferences are consulted:
|
|
476 | + * network.proxy.socks
|
|
477 | + * network.proxy.socks_port
|
|
478 | + * extensions.torlauncher.socks_port_use_ipc (Boolean)
|
|
479 | + * extensions.torlauncher.socks_ipc_path (file system path)
|
|
480 | + * If extensions.torlauncher.socks_ipc_path is empty, a default path is used.
|
|
481 | + *
|
|
482 | + * When using TCP, if a value is not defined via an env variable it is
|
|
483 | + * taken from the corresponding browser preference if possible. The
|
|
484 | + * exceptions are:
|
|
485 | + * If network.proxy.socks contains a file: URL, a default value of
|
|
486 | + * "127.0.0.1" is used instead.
|
|
487 | + * If the network.proxy.socks_port value is not valid (outside the
|
|
488 | + * (0; 65535] range), a default value of 9150 is used instead.
|
|
489 | + *
|
|
490 | + * The SOCKS configuration will not influence the launch of a tor daemon and
|
|
491 | + * the configuration of the control port in any way.
|
|
492 | + * When a SOCKS configuration is required without TOR_SKIP_LAUNCH, the browser
|
|
493 | + * will try to configure the tor instance to use the required configuration.
|
|
494 | + * This also applies to TOR_TRANSPROXY (at least for now): tor will be
|
|
495 | + * launched with its defaults.
|
|
496 | + *
|
|
497 | + * TODO: add a preference to ignore the current configuration, and let tor
|
|
498 | + * listen on any free port. Then, the browser will prompt the daemon the port
|
|
499 | + * to use through the control port (even though this is quite dangerous at the
|
|
500 | + * moment, because with network disabled tor will disable also the SOCKS
|
|
501 | + * listeners, so it means that we will have to check it every time we change
|
|
502 | + * the network status).
|
|
503 | + */
|
|
504 | + getPreferredSocksConfiguration() {
|
|
505 | + if (Services.env.exists("TOR_TRANSPROXY")) {
|
|
506 | + Services.prefs.setBoolPref("network.proxy.socks_remote_dns", false);
|
|
507 | + Services.prefs.setIntPref("network.proxy.type", 0);
|
|
508 | + Services.prefs.setIntPref("network.proxy.socks_port", 0);
|
|
509 | + Services.prefs.setCharPref("network.proxy.socks", "");
|
|
510 | + return { transproxy: true };
|
|
511 | + }
|
|
512 | + |
|
513 | + let useIPC;
|
|
514 | + const socksPortInfo = {
|
|
515 | + transproxy: false,
|
|
516 | + };
|
|
517 | + |
|
518 | + if (!this.isWindows && Services.env.exists("TOR_SOCKS_IPC_PATH")) {
|
|
519 | + useIPC = true;
|
|
520 | + const ipcPath = Services.env.get("TOR_SOCKS_IPC_PATH");
|
|
521 | + if (ipcPath) {
|
|
522 | + socksPortInfo.ipcFile = new lazy.FileUtils.File(ipcPath);
|
|
523 | + }
|
|
524 | + } else {
|
|
525 | + // Check for TCP host and port environment variables.
|
|
526 | + if (Services.env.exists("TOR_SOCKS_HOST")) {
|
|
527 | + socksPortInfo.host = Services.env.get("TOR_SOCKS_HOST");
|
|
528 | + useIPC = false;
|
|
529 | + }
|
|
530 | + if (Services.env.exists("TOR_SOCKS_PORT")) {
|
|
531 | + const port = parseInt(Services.env.get("TOR_SOCKS_PORT"), 10);
|
|
532 | + if (Number.isInteger(port) && port > 0 && port <= 65535) {
|
|
533 | + socksPortInfo.port = port;
|
|
534 | + useIPC = false;
|
|
535 | + }
|
|
536 | + }
|
|
537 | + }
|
|
538 | + |
|
539 | + if (useIPC === undefined) {
|
|
540 | + socksPortInfo.useIPC =
|
|
541 | + !this.isWindows &&
|
|
542 | + Services.prefs.getBoolPref(
|
|
543 | + "extensions.torlauncher.socks_port_use_ipc",
|
|
544 | + false
|
|
545 | + );
|
|
546 | + }
|
|
547 | + |
|
548 | + // Fill in missing SOCKS info from prefs.
|
|
549 | + if (socksPortInfo.useIPC) {
|
|
550 | + if (!socksPortInfo.ipcFile) {
|
|
551 | + socksPortInfo.ipcFile = TorLauncherUtil.getTorFile("socks_ipc", false);
|
|
552 | + }
|
|
553 | + } else {
|
|
554 | + if (!socksPortInfo.host) {
|
|
555 | + let socksAddr = Services.prefs.getCharPref(
|
|
556 | + "network.proxy.socks",
|
|
557 | + "127.0.0.1"
|
|
558 | + );
|
|
559 | + let socksAddrHasHost = socksAddr && !socksAddr.startsWith("file:");
|
|
560 | + socksPortInfo.host = socksAddrHasHost ? socksAddr : "127.0.0.1";
|
|
561 | + }
|
|
562 | + |
|
563 | + if (!socksPortInfo.port) {
|
|
564 | + let socksPort = Services.prefs.getIntPref(
|
|
565 | + "network.proxy.socks_port",
|
|
566 | + 0
|
|
567 | + );
|
|
568 | + // This pref is set as 0 by default in Firefox, use 9150 if we get 0.
|
|
569 | + socksPortInfo.port =
|
|
570 | + socksPort > 0 && socksPort <= 65535 ? socksPort : 9150;
|
|
571 | + }
|
|
572 | + }
|
|
573 | + |
|
574 | + return socksPortInfo;
|
|
575 | + },
|
|
576 | + |
|
577 | + setProxyConfiguration(socksPortInfo) {
|
|
578 | + if (socksPortInfo.transproxy) {
|
|
579 | + return;
|
|
580 | + }
|
|
581 | + |
|
582 | + if (socksPortInfo.useIPC) {
|
|
583 | + const fph = Services.io
|
|
584 | + .getProtocolHandler("file")
|
|
585 | + .QueryInterface(Ci.nsIFileProtocolHandler);
|
|
586 | + const fileURI = fph.newFileURI(socksPortInfo.ipcFile);
|
|
587 | + Services.prefs.setCharPref("network.proxy.socks", fileURI.spec);
|
|
588 | + Services.prefs.setIntPref("network.proxy.socks_port", 0);
|
|
589 | + } else {
|
|
590 | + if (socksPortInfo.host) {
|
|
591 | + Services.prefs.setCharPref("network.proxy.socks", socksPortInfo.host);
|
|
592 | + }
|
|
593 | + if (socksPortInfo.port) {
|
|
594 | + Services.prefs.setIntPref(
|
|
595 | + "network.proxy.socks_port",
|
|
596 | + socksPortInfo.port
|
|
597 | + );
|
|
598 | + }
|
|
599 | + }
|
|
600 | + |
|
601 | + if (socksPortInfo.ipcFile || socksPortInfo.host || socksPortInfo.port) {
|
|
602 | + Services.prefs.setBoolPref("network.proxy.socks_remote_dns", true);
|
|
603 | + Services.prefs.setIntPref("network.proxy.type", 1);
|
|
604 | + }
|
|
605 | + |
|
606 | + // Force prefs to be synced to disk
|
|
607 | + Services.prefs.savePrefFile(null);
|
|
608 | + },
|
|
609 | + |
|
455 | 610 | get shouldStartAndOwnTor() {
|
456 | 611 | const kPrefStartTor = "extensions.torlauncher.start_tor";
|
457 | 612 | try {
|
... | ... | @@ -13,6 +13,10 @@ import { TorLauncherUtil } from "resource://gre/modules/TorLauncherUtil.sys.mjs" |
13 | 13 | |
14 | 14 | const lazy = {};
|
15 | 15 | |
16 | +ChromeUtils.defineESModuleGetters(lazy, {
|
|
17 | + TorProtocolService: "resource://gre/modules/TorProtocolService.sys.mjs",
|
|
18 | +});
|
|
19 | + |
|
16 | 20 | ChromeUtils.defineModuleGetter(
|
17 | 21 | lazy,
|
18 | 22 | "controller",
|
... | ... | @@ -233,7 +237,10 @@ export const TorMonitorService = { |
233 | 237 | // TorProcess should be instanced once, then always reused and restarted
|
234 | 238 | // only through the prompt it exposes when the controlled process dies.
|
235 | 239 | if (!this._torProcess) {
|
236 | - this._torProcess = new TorProcess();
|
|
240 | + this._torProcess = new TorProcess(
|
|
241 | + lazy.TorProtocolService.torControlPortInfo,
|
|
242 | + lazy.TorProtocolService.torSOCKSPortInfo
|
|
243 | + );
|
|
237 | 244 | this._torProcess.onExit = () => {
|
238 | 245 | this._shutDownEventMonitor();
|
239 | 246 | Services.obs.notifyObservers(null, TorTopics.ProcessExited);
|
... | ... | @@ -254,6 +261,7 @@ export const TorMonitorService = { |
254 | 261 | await this._torProcess.start();
|
255 | 262 | if (this._torProcess.isRunning) {
|
256 | 263 | logger.info("tor started");
|
264 | + this._torProcessStartTime = Date.now();
|
|
257 | 265 | }
|
258 | 266 | } catch (e) {
|
259 | 267 | // TorProcess already logs the error.
|
... | ... | @@ -267,4 +267,18 @@ export const TorParsers = Object.freeze({ |
267 | 267 | rv += aStr.substring(lastAdded, aStr.length - 1);
|
268 | 268 | return rv;
|
269 | 269 | },
|
270 | + |
|
271 | + parseBridgeLine(line) {
|
|
272 | + const re =
|
|
273 | + /\s*(?:(?<transport>\S+)\s+)?(?<addr>[0-9a-fA-F\.\[\]\:]+:\d{1,5})(?:\s+(?<id>[0-9a-fA-F]{40}))?(?:\s+(?<args>.+))?/;
|
|
274 | + const match = re.exec(line);
|
|
275 | + if (!match) {
|
|
276 | + throw new Error("Invalid bridge line.");
|
|
277 | + }
|
|
278 | + const bridge = match.groups;
|
|
279 | + if (!bridge.transport) {
|
|
280 | + bridge.transport = "vanilla";
|
|
281 | + }
|
|
282 | + return bridge;
|
|
283 | + },
|
|
270 | 284 | }); |
1 | +/* This Source Code Form is subject to the terms of the Mozilla Public
|
|
2 | + * License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
3 | + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
|
4 | + |
|
1 | 5 | import { setTimeout } from "resource://gre/modules/Timer.sys.mjs";
|
2 | 6 | import { ConsoleAPI } from "resource://gre/modules/Console.sys.mjs";
|
3 | 7 | import { Subprocess } from "resource://gre/modules/Subprocess.sys.mjs";
|
4 | 8 | |
5 | 9 | const lazy = {};
|
6 | 10 | |
7 | -ChromeUtils.defineModuleGetter(
|
|
8 | - lazy,
|
|
9 | - "TorProtocolService",
|
|
10 | - "resource://gre/modules/TorProtocolService.jsm"
|
|
11 | -);
|
|
12 | -const { TorLauncherUtil } = ChromeUtils.import(
|
|
13 | - "resource://gre/modules/TorLauncherUtil.jsm"
|
|
14 | -);
|
|
15 | - |
|
16 | -const { TorParsers } = ChromeUtils.import(
|
|
17 | - "resource://gre/modules/TorParsers.jsm"
|
|
18 | -);
|
|
11 | +ChromeUtils.defineESModuleGetters(lazy, {
|
|
12 | + TorLauncherUtil: "resource://gre/modules/TorLauncherUtil.sys.mjs",
|
|
13 | + TorParsers: "resource://gre/modules/TorParsers.sys.mjs",
|
|
14 | +});
|
|
19 | 15 | |
20 | 16 | const TorProcessStatus = Object.freeze({
|
21 | 17 | Unknown: 0,
|
... | ... | @@ -30,53 +26,86 @@ const logger = new ConsoleAPI({ |
30 | 26 | });
|
31 | 27 | |
32 | 28 | export class TorProcess {
|
33 | - _exeFile = null;
|
|
34 | - _dataDir = null;
|
|
35 | - _args = [];
|
|
36 | - _subprocess = null;
|
|
37 | - _status = TorProcessStatus.Unknown;
|
|
38 | - _torProcessStartTime = null; // JS Date.now()
|
|
39 | - _didConnectToTorControlPort = false; // Have we ever made a connection?
|
|
29 | + #controlSettings;
|
|
30 | + #socksSettings;
|
|
31 | + #exeFile = null;
|
|
32 | + #dataDir = null;
|
|
33 | + #args = [];
|
|
34 | + #subprocess = null;
|
|
35 | + #status = TorProcessStatus.Unknown;
|
|
36 | + // Have we ever made a connection on the control port?
|
|
37 | + #didConnectToTorControlPort = false;
|
|
38 | + |
|
39 | + onExit = exitCode => {};
|
|
40 | + onRestart = () => {};
|
|
41 | + |
|
42 | + constructor(controlSettings, socksSettings) {
|
|
43 | + if (
|
|
44 | + controlSettings &&
|
|
45 | + !controlSettings.password &&
|
|
46 | + !controlSettings.cookieFilePath
|
|
47 | + ) {
|
|
48 | + throw new Error("Unauthenticated control port is not supported");
|
|
49 | + }
|
|
40 | 50 | |
41 | - onExit = null;
|
|
42 | - onRestart = null;
|
|
51 | + const checkPort = port =>
|
|
52 | + port === undefined ||
|
|
53 | + (Number.isInteger(controlSettings.port) &&
|
|
54 | + controlSettings.port > 0 &&
|
|
55 | + controlSettings.port < 65535);
|
|
56 | + if (!checkPort(controlSettings?.port)) {
|
|
57 | + throw new Error("Invalid control port");
|
|
58 | + }
|
|
59 | + if (!checkPort(socksSettings.port)) {
|
|
60 | + throw new Error("Invalid port specified for the SOCKS port");
|
|
61 | + }
|
|
62 | + |
|
63 | + this.#controlSettings = { ...controlSettings };
|
|
64 | + const ipcFileToString = file =>
|
|
65 | + "unix:" + lazy.TorParsers.escapeString(file.path);
|
|
66 | + if (controlSettings.ipcFile) {
|
|
67 | + this.#controlSettings.ipcFile = ipcFileToString(controlSettings.ipcFile);
|
|
68 | + }
|
|
69 | + this.#socksSettings = { ...socksSettings };
|
|
70 | + if (socksSettings.ipcFile) {
|
|
71 | + this.#socksSettings.ipcFile = ipcFileToString(socksSettings.ipcFile);
|
|
72 | + }
|
|
73 | + }
|
|
43 | 74 | |
44 | 75 | get status() {
|
45 | - return this._status;
|
|
76 | + return this.#status;
|
|
46 | 77 | }
|
47 | 78 | |
48 | 79 | get isRunning() {
|
49 | 80 | return (
|
50 | - this._status === TorProcessStatus.Starting ||
|
|
51 | - this._status === TorProcessStatus.Running
|
|
81 | + this.#status === TorProcessStatus.Starting ||
|
|
82 | + this.#status === TorProcessStatus.Running
|
|
52 | 83 | );
|
53 | 84 | }
|
54 | 85 | |
55 | 86 | async start() {
|
56 | - if (this._subprocess) {
|
|
87 | + if (this.#subprocess) {
|
|
57 | 88 | return;
|
58 | 89 | }
|
59 | 90 | |
60 | - this._status = TorProcessStatus.Unknown;
|
|
91 | + this.#status = TorProcessStatus.Unknown;
|
|
61 | 92 | |
62 | 93 | try {
|
63 | - this._makeArgs();
|
|
64 | - this._addControlPortArg();
|
|
65 | - this._addSocksPortArg();
|
|
94 | + this.#makeArgs();
|
|
95 | + this.#addControlPortArgs();
|
|
96 | + this.#addSocksPortArg();
|
|
66 | 97 | |
67 | 98 | const pid = Services.appinfo.processID;
|
68 | 99 | if (pid !== 0) {
|
69 | - this._args.push("__OwningControllerProcess");
|
|
70 | - this._args.push("" + pid);
|
|
100 | + this.#args.push("__OwningControllerProcess", pid.toString());
|
|
71 | 101 | }
|
72 | 102 | |
73 | - if (TorLauncherUtil.shouldShowNetworkSettings) {
|
|
74 | - this._args.push("DisableNetwork");
|
|
75 | - this._args.push("1");
|
|
103 | + if (lazy.TorLauncherUtil.shouldShowNetworkSettings) {
|
|
104 | + this.#args.push("DisableNetwork", "1");
|
|
76 | 105 | }
|
77 | 106 | |
78 | - this._status = TorProcessStatus.Starting;
|
|
79 | - this._didConnectToTorControlPort = false;
|
|
107 | + this.#status = TorProcessStatus.Starting;
|
|
108 | + this.#didConnectToTorControlPort = false;
|
|
80 | 109 | |
81 | 110 | // useful for simulating slow tor daemon launch
|
82 | 111 | const kPrefTorDaemonLaunchDelay = "extensions.torlauncher.launch_delay";
|
... | ... | @@ -88,29 +117,31 @@ export class TorProcess { |
88 | 117 | await new Promise(resolve => setTimeout(() => resolve(), launchDelay));
|
89 | 118 | }
|
90 | 119 | |
91 | - logger.debug(`Starting ${this._exeFile.path}`, this._args);
|
|
120 | + logger.debug(`Starting ${this.#exeFile.path}`, this.#args);
|
|
92 | 121 | const options = {
|
93 | - command: this._exeFile.path,
|
|
94 | - arguments: this._args,
|
|
122 | + command: this.#exeFile.path,
|
|
123 | + arguments: this.#args,
|
|
95 | 124 | stderr: "stdout",
|
96 | - workdir: TorLauncherUtil.getTorFile("pt-startup-dir", false).path,
|
|
125 | + workdir: lazy.TorLauncherUtil.getTorFile("pt-startup-dir", false).path,
|
|
97 | 126 | };
|
98 | - this._subprocess = await Subprocess.call(options);
|
|
99 | - this._dumpStdout();
|
|
100 | - this._watchProcess();
|
|
101 | - this._status = TorProcessStatus.Running;
|
|
102 | - this._torProcessStartTime = Date.now();
|
|
127 | + this.#subprocess = await Subprocess.call(options);
|
|
128 | + this.#status = TorProcessStatus.Running;
|
|
103 | 129 | } catch (e) {
|
104 | - this._status = TorProcessStatus.Exited;
|
|
105 | - this._subprocess = null;
|
|
130 | + this.#status = TorProcessStatus.Exited;
|
|
131 | + this.#subprocess = null;
|
|
106 | 132 | logger.error("startTor error:", e);
|
107 | 133 | throw e;
|
108 | 134 | }
|
135 | + |
|
136 | + // Do not await the following functions, as they will return only when the
|
|
137 | + // process exits.
|
|
138 | + this.#dumpStdout();
|
|
139 | + this.#watchProcess();
|
|
109 | 140 | }
|
110 | 141 | |
111 | 142 | // Forget about a process.
|
112 | 143 | //
|
113 | - // Instead of killing the tor process, we rely on the TAKEOWNERSHIP feature
|
|
144 | + // Instead of killing the tor process, we rely on the TAKEOWNERSHIP feature
|
|
114 | 145 | // to shut down tor when we close the control port connection.
|
115 | 146 | //
|
116 | 147 | // Previously, we sent a SIGNAL HALT command to the tor control port,
|
... | ... | @@ -123,36 +154,38 @@ export class TorProcess { |
123 | 154 | // Still, before closing the owning connection, this class should forget about
|
124 | 155 | // the process, so that future notifications will be ignored.
|
125 | 156 | forget() {
|
126 | - this._subprocess = null;
|
|
127 | - this._status = TorProcessStatus.Exited;
|
|
157 | + this.#subprocess = null;
|
|
158 | + this.#status = TorProcessStatus.Exited;
|
|
128 | 159 | }
|
129 | 160 | |
130 | 161 | // The owner of the process can use this function to tell us that they
|
131 | 162 | // successfully connected to the control port. This information will be used
|
132 | 163 | // only to decide which text to show in the confirmation dialog if tor exits.
|
133 | 164 | connectionWorked() {
|
134 | - this._didConnectToTorControlPort = true;
|
|
165 | + this.#didConnectToTorControlPort = true;
|
|
135 | 166 | }
|
136 | 167 | |
137 | - async _dumpStdout() {
|
|
168 | + async #dumpStdout() {
|
|
138 | 169 | let string;
|
139 | 170 | while (
|
140 | - this._subprocess &&
|
|
141 | - (string = await this._subprocess.stdout.readString())
|
|
171 | + this.#subprocess &&
|
|
172 | + (string = await this.#subprocess.stdout.readString())
|
|
142 | 173 | ) {
|
143 | 174 | dump(string);
|
144 | 175 | }
|
145 | 176 | }
|
146 | 177 | |
147 | - async _watchProcess() {
|
|
148 | - const watched = this._subprocess;
|
|
178 | + async #watchProcess() {
|
|
179 | + const watched = this.#subprocess;
|
|
149 | 180 | if (!watched) {
|
150 | 181 | return;
|
151 | 182 | }
|
183 | + let processExitCode;
|
|
152 | 184 | try {
|
153 | 185 | const { exitCode } = await watched.wait();
|
186 | + processExitCode = exitCode;
|
|
154 | 187 | |
155 | - if (watched !== this._subprocess) {
|
|
188 | + if (watched !== this.#subprocess) {
|
|
156 | 189 | logger.debug(`A Tor process exited with code ${exitCode}.`);
|
157 | 190 | } else if (exitCode) {
|
158 | 191 | logger.warn(`The watched Tor process exited with code ${exitCode}.`);
|
... | ... | @@ -163,30 +196,31 @@ export class TorProcess { |
163 | 196 | logger.error("Failed to watch the tor process", e);
|
164 | 197 | }
|
165 | 198 | |
166 | - if (watched === this._subprocess) {
|
|
167 | - this._processExitedUnexpectedly();
|
|
199 | + if (watched === this.#subprocess) {
|
|
200 | + this.#processExitedUnexpectedly(processExitCode);
|
|
168 | 201 | }
|
169 | 202 | }
|
170 | 203 | |
171 | - _processExitedUnexpectedly() {
|
|
172 | - this._subprocess = null;
|
|
173 | - this._status = TorProcessStatus.Exited;
|
|
204 | + #processExitedUnexpectedly(exitCode) {
|
|
205 | + this.#subprocess = null;
|
|
206 | + this.#status = TorProcessStatus.Exited;
|
|
174 | 207 | |
175 | 208 | // TODO: Move this logic somewhere else?
|
176 | 209 | let s;
|
177 | - if (!this._didConnectToTorControlPort) {
|
|
210 | + if (!this.#didConnectToTorControlPort) {
|
|
178 | 211 | // tor might be misconfigured, becauser we could never connect to it
|
179 | 212 | const key = "tor_exited_during_startup";
|
180 | - s = TorLauncherUtil.getLocalizedString(key);
|
|
213 | + s = lazy.TorLauncherUtil.getLocalizedString(key);
|
|
181 | 214 | } else {
|
182 | 215 | // tor exited suddenly, so configuration should be okay
|
183 | 216 | s =
|
184 | - TorLauncherUtil.getLocalizedString("tor_exited") +
|
|
217 | + lazy.TorLauncherUtil.getLocalizedString("tor_exited") +
|
|
185 | 218 | "\n\n" +
|
186 | - TorLauncherUtil.getLocalizedString("tor_exited2");
|
|
219 | + lazy.TorLauncherUtil.getLocalizedString("tor_exited2");
|
|
187 | 220 | }
|
188 | 221 | logger.info(s);
|
189 | - const defaultBtnLabel = TorLauncherUtil.getLocalizedString("restart_tor");
|
|
222 | + const defaultBtnLabel =
|
|
223 | + lazy.TorLauncherUtil.getLocalizedString("restart_tor");
|
|
190 | 224 | let cancelBtnLabel = "OK";
|
191 | 225 | try {
|
192 | 226 | const kSysBundleURI = "chrome://global/locale/commonDialogs.properties";
|
... | ... | @@ -196,51 +230,43 @@ export class TorProcess { |
196 | 230 | logger.warn("Could not localize the cancel button", e);
|
197 | 231 | }
|
198 | 232 | |
199 | - const restart = TorLauncherUtil.showConfirm(
|
|
233 | + const restart = lazy.TorLauncherUtil.showConfirm(
|
|
200 | 234 | null,
|
201 | 235 | s,
|
202 | 236 | defaultBtnLabel,
|
203 | 237 | cancelBtnLabel
|
204 | 238 | );
|
205 | 239 | if (restart) {
|
206 | - this.start().then(() => {
|
|
207 | - if (this.onRestart) {
|
|
208 | - this.onRestart();
|
|
209 | - }
|
|
210 | - });
|
|
211 | - } else if (this.onExit) {
|
|
212 | - this.onExit();
|
|
240 | + this.start().then(this.onRestart);
|
|
241 | + } else {
|
|
242 | + this.onExit(exitCode);
|
|
213 | 243 | }
|
214 | 244 | }
|
215 | 245 | |
216 | - _makeArgs() {
|
|
217 | - // Ideally, we would cd to the Firefox application directory before
|
|
218 | - // starting tor (but we don't know how to do that). Instead, we
|
|
219 | - // rely on the TBB launcher to start Firefox from the right place.
|
|
220 | - |
|
246 | + #makeArgs() {
|
|
247 | + this.#exeFile = lazy.TorLauncherUtil.getTorFile("tor", false);
|
|
248 | + const torrcFile = lazy.TorLauncherUtil.getTorFile("torrc", true);
|
|
221 | 249 | // Get the Tor data directory first so it is created before we try to
|
222 | 250 | // construct paths to files that will be inside it.
|
223 | - this._exeFile = TorLauncherUtil.getTorFile("tor", false);
|
|
224 | - const torrcFile = TorLauncherUtil.getTorFile("torrc", true);
|
|
225 | - this._dataDir = TorLauncherUtil.getTorFile("tordatadir", true);
|
|
226 | - const onionAuthDir = TorLauncherUtil.getTorFile("toronionauthdir", true);
|
|
227 | - const hashedPassword = lazy.TorProtocolService.torGetPassword(true);
|
|
251 | + this.#dataDir = lazy.TorLauncherUtil.getTorFile("tordatadir", true);
|
|
252 | + const onionAuthDir = lazy.TorLauncherUtil.getTorFile(
|
|
253 | + "toronionauthdir",
|
|
254 | + true
|
|
255 | + );
|
|
228 | 256 | let detailsKey;
|
229 | - if (!this._exeFile) {
|
|
257 | + if (!this.#exeFile) {
|
|
230 | 258 | detailsKey = "tor_missing";
|
231 | 259 | } else if (!torrcFile) {
|
232 | 260 | detailsKey = "torrc_missing";
|
233 | - } else if (!this._dataDir) {
|
|
261 | + } else if (!this.#dataDir) {
|
|
234 | 262 | detailsKey = "datadir_missing";
|
235 | 263 | } else if (!onionAuthDir) {
|
236 | 264 | detailsKey = "onionauthdir_missing";
|
237 | - } else if (!hashedPassword) {
|
|
238 | - detailsKey = "password_hash_missing";
|
|
239 | 265 | }
|
240 | 266 | if (detailsKey) {
|
241 | - const details = TorLauncherUtil.getLocalizedString(detailsKey);
|
|
267 | + const details = lazy.TorLauncherUtil.getLocalizedString(detailsKey);
|
|
242 | 268 | const key = "unable_to_start_tor";
|
243 | - const err = TorLauncherUtil.getFormattedLocalizedString(
|
|
269 | + const err = lazy.TorLauncherUtil.getFormattedLocalizedString(
|
|
244 | 270 | key,
|
245 | 271 | [details],
|
246 | 272 | 1
|
... | ... | @@ -248,7 +274,7 @@ export class TorProcess { |
248 | 274 | throw new Error(err);
|
249 | 275 | }
|
250 | 276 | |
251 | - const torrcDefaultsFile = TorLauncherUtil.getTorFile(
|
|
277 | + const torrcDefaultsFile = lazy.TorLauncherUtil.getTorFile(
|
|
252 | 278 | "torrc-defaults",
|
253 | 279 | false
|
254 | 280 | );
|
... | ... | @@ -258,77 +284,131 @@ export class TorProcess { |
258 | 284 | const geoip6File = torrcDefaultsFile.clone();
|
259 | 285 | geoip6File.leafName = "geoip6";
|
260 | 286 | |
261 | - this._args = [];
|
|
287 | + this.#args = [];
|
|
262 | 288 | if (torrcDefaultsFile) {
|
263 | - this._args.push("--defaults-torrc");
|
|
264 | - this._args.push(torrcDefaultsFile.path);
|
|
289 | + this.#args.push("--defaults-torrc", torrcDefaultsFile.path);
|
|
265 | 290 | }
|
266 | - this._args.push("-f");
|
|
267 | - this._args.push(torrcFile.path);
|
|
268 | - this._args.push("DataDirectory");
|
|
269 | - this._args.push(this._dataDir.path);
|
|
270 | - this._args.push("ClientOnionAuthDir");
|
|
271 | - this._args.push(onionAuthDir.path);
|
|
272 | - this._args.push("GeoIPFile");
|
|
273 | - this._args.push(geoipFile.path);
|
|
274 | - this._args.push("GeoIPv6File");
|
|
275 | - this._args.push(geoip6File.path);
|
|
276 | - this._args.push("HashedControlPassword");
|
|
277 | - this._args.push(hashedPassword);
|
|
291 | + this.#args.push("-f", torrcFile.path);
|
|
292 | + this.#args.push("DataDirectory", this.#dataDir.path);
|
|
293 | + this.#args.push("ClientOnionAuthDir", onionAuthDir.path);
|
|
294 | + this.#args.push("GeoIPFile", geoipFile.path);
|
|
295 | + this.#args.push("GeoIPv6File", geoip6File.path);
|
|
278 | 296 | }
|
279 | 297 | |
280 | - _addControlPortArg() {
|
|
281 | - // Include a ControlPort argument to support switching between
|
|
282 | - // a TCP port and an IPC port (e.g., a Unix domain socket). We
|
|
283 | - // include a "+__" prefix so that (1) this control port is added
|
|
284 | - // to any control ports that the user has defined in their torrc
|
|
285 | - // file and (2) it is never written to torrc.
|
|
298 | + /**
|
|
299 | + * Add all the arguments related to the control port.
|
|
300 | + * We use the + prefix so that the the port is added to any other port already
|
|
301 | + * defined in the torrc, and the __ prefix so that it is never written to
|
|
302 | + * torrc.
|
|
303 | + */
|
|
304 | + #addControlPortArgs() {
|
|
305 | + if (!this.#controlSettings) {
|
|
306 | + return;
|
|
307 | + }
|
|
308 | + |
|
286 | 309 | let controlPortArg;
|
287 | - const controlIPCFile = lazy.TorProtocolService.torGetControlIPCFile();
|
|
288 | - const controlPort = lazy.TorProtocolService.torGetControlPort();
|
|
289 | - if (controlIPCFile) {
|
|
290 | - controlPortArg = this._ipcPortArg(controlIPCFile);
|
|
291 | - } else if (controlPort) {
|
|
292 | - controlPortArg = "" + controlPort;
|
|
310 | + if (this.#controlSettings.ipcFile) {
|
|
311 | + controlPortArg = this.#controlSettings.ipcFile;
|
|
312 | + } else if (this.#controlSettings.port) {
|
|
313 | + controlPortArg = this.#controlSettings.host
|
|
314 | + ? `${this.#controlSettings.host}:${this.#controlSettings.port}`
|
|
315 | + : this.#controlSettings.port.toString();
|
|
293 | 316 | }
|
294 | 317 | if (controlPortArg) {
|
295 | - this._args.push("+__ControlPort");
|
|
296 | - this._args.push(controlPortArg);
|
|
318 | + this.#args.push("+__ControlPort", controlPortArg);
|
|
319 | + }
|
|
320 | + |
|
321 | + if (this.#controlSettings.password) {
|
|
322 | + this.#args.push(
|
|
323 | + "HashedControlPassword",
|
|
324 | + this.#hashPassword(this.#controlSettings.password)
|
|
325 | + );
|
|
326 | + }
|
|
327 | + if (this.#controlSettings.cookieFilePath) {
|
|
328 | + this.#args.push("CookieAuthentication", "1");
|
|
329 | + this.#args.push("CookieAuthFile", this.#controlSettings.cookieFilePath);
|
|
297 | 330 | }
|
298 | 331 | }
|
299 | 332 | |
300 | - _addSocksPortArg() {
|
|
301 | - // Include a SocksPort argument to support switching between
|
|
302 | - // a TCP port and an IPC port (e.g., a Unix domain socket). We
|
|
303 | - // include a "+__" prefix so that (1) this SOCKS port is added
|
|
304 | - // to any SOCKS ports that the user has defined in their torrc
|
|
305 | - // file and (2) it is never written to torrc.
|
|
306 | - const socksPortInfo = lazy.TorProtocolService.torGetSOCKSPortInfo();
|
|
307 | - if (socksPortInfo) {
|
|
308 | - let socksPortArg;
|
|
309 | - if (socksPortInfo.ipcFile) {
|
|
310 | - socksPortArg = this._ipcPortArg(socksPortInfo.ipcFile);
|
|
311 | - } else if (socksPortInfo.host && socksPortInfo.port != 0) {
|
|
312 | - socksPortArg = socksPortInfo.host + ":" + socksPortInfo.port;
|
|
313 | - }
|
|
314 | - if (socksPortArg) {
|
|
315 | - let socksPortFlags = Services.prefs.getCharPref(
|
|
316 | - "extensions.torlauncher.socks_port_flags",
|
|
317 | - "IPv6Traffic PreferIPv6 KeepAliveIsolateSOCKSAuth"
|
|
318 | - );
|
|
319 | - if (socksPortFlags) {
|
|
320 | - socksPortArg += " " + socksPortFlags;
|
|
321 | - }
|
|
322 | - this._args.push("+__SocksPort");
|
|
323 | - this._args.push(socksPortArg);
|
|
333 | + /**
|
|
334 | + * Add the argument related to the control port.
|
|
335 | + * We use the + prefix so that the the port is added to any other port already
|
|
336 | + * defined in the torrc, and the __ prefix so that it is never written to
|
|
337 | + * torrc.
|
|
338 | + */
|
|
339 | + #addSocksPortArg() {
|
|
340 | + let socksPortArg;
|
|
341 | + if (this.#socksSettings.ipcFile) {
|
|
342 | + socksPortArg = this.#socksSettings.ipcFile;
|
|
343 | + } else if (this.#socksSettings.port != 0) {
|
|
344 | + socksPortArg = this.#socksSettings.host
|
|
345 | + ? `${this.#socksSettings.host}:${this.#socksSettings.port}`
|
|
346 | + : this.#socksSettings.port.toString();
|
|
347 | + }
|
|
348 | + if (socksPortArg) {
|
|
349 | + const socksPortFlags = Services.prefs.getCharPref(
|
|
350 | + "extensions.torlauncher.socks_port_flags",
|
|
351 | + "IPv6Traffic PreferIPv6 KeepAliveIsolateSOCKSAuth"
|
|
352 | + );
|
|
353 | + if (socksPortFlags) {
|
|
354 | + socksPortArg += " " + socksPortFlags;
|
|
324 | 355 | }
|
356 | + this.#args.push("+__SocksPort", socksPortArg);
|
|
357 | + }
|
|
358 | + }
|
|
359 | + |
|
360 | + // Based on Vidalia's TorSettings::hashPassword().
|
|
361 | + #hashPassword(aHexPassword) {
|
|
362 | + if (!aHexPassword) {
|
|
363 | + return null;
|
|
325 | 364 | }
|
365 | + |
|
366 | + // Generate a random, 8 byte salt value.
|
|
367 | + const salt = Array.from(crypto.getRandomValues(new Uint8Array(8)));
|
|
368 | + |
|
369 | + // Convert hex-encoded password to an array of bytes.
|
|
370 | + const password = [];
|
|
371 | + for (let i = 0; i < aHexPassword.length; i += 2) {
|
|
372 | + password.push(parseInt(aHexPassword.substring(i, i + 2), 16));
|
|
373 | + }
|
|
374 | + |
|
375 | + // Run through the S2K algorithm and convert to a string.
|
|
376 | + const toHex = v => v.toString(16).padStart(2, "0");
|
|
377 | + const arrayToHex = aArray => aArray.map(toHex).join("");
|
|
378 | + const kCodedCount = 96;
|
|
379 | + const hashVal = this.#cryptoSecretToKey(password, salt, kCodedCount);
|
|
380 | + return "16:" + arrayToHex(salt) + toHex(kCodedCount) + arrayToHex(hashVal);
|
|
326 | 381 | }
|
327 | 382 | |
328 | - // Return a ControlPort or SocksPort argument for aIPCFile (an nsIFile).
|
|
329 | - // The result is unix:/path or unix:"/path with spaces" with appropriate
|
|
330 | - // C-style escaping within the path portion.
|
|
331 | - _ipcPortArg(aIPCFile) {
|
|
332 | - return "unix:" + TorParsers.escapeString(aIPCFile.path);
|
|
383 | + // #cryptoSecretToKey() is similar to Vidalia's crypto_secret_to_key().
|
|
384 | + // It generates and returns a hash of aPassword by following the iterated
|
|
385 | + // and salted S2K algorithm (see RFC 2440 section 3.6.1.3).
|
|
386 | + // See also https://gitlab.torproject.org/tpo/core/torspec/-/blob/main/control-spec.txt#L3824.
|
|
387 | + // Returns an array of bytes.
|
|
388 | + #cryptoSecretToKey(aPassword, aSalt, aCodedCount) {
|
|
389 | + const inputArray = aSalt.concat(aPassword);
|
|
390 | + |
|
391 | + // Subtle crypto only has the final digest, and does not allow incremental
|
|
392 | + // updates.
|
|
393 | + const hasher = Cc["@mozilla.org/security/hash;1"].createInstance(
|
|
394 | + Ci.nsICryptoHash
|
|
395 | + );
|
|
396 | + hasher.init(hasher.SHA1);
|
|
397 | + const kEXPBIAS = 6;
|
|
398 | + let count = (16 + (aCodedCount & 15)) << ((aCodedCount >> 4) + kEXPBIAS);
|
|
399 | + while (count > 0) {
|
|
400 | + if (count > inputArray.length) {
|
|
401 | + hasher.update(inputArray, inputArray.length);
|
|
402 | + count -= inputArray.length;
|
|
403 | + } else {
|
|
404 | + const finalArray = inputArray.slice(0, count);
|
|
405 | + hasher.update(finalArray, finalArray.length);
|
|
406 | + count = 0;
|
|
407 | + }
|
|
408 | + }
|
|
409 | + return hasher
|
|
410 | + .finish(false)
|
|
411 | + .split("")
|
|
412 | + .map(b => b.charCodeAt(0));
|
|
333 | 413 | }
|
334 | 414 | } |
... | ... | @@ -289,9 +289,8 @@ export const TorProtocolService = { |
289 | 289 | // are also used in torbutton.
|
290 | 290 | |
291 | 291 | // Returns Tor password string or null if an error occurs.
|
292 | - torGetPassword(aPleaseHash) {
|
|
293 | - const pw = this._controlPassword;
|
|
294 | - return aPleaseHash ? this._hashPassword(pw) : pw;
|
|
292 | + torGetPassword() {
|
|
293 | + return this._controlPassword;
|
|
295 | 294 | },
|
296 | 295 | |
297 | 296 | torGetControlIPCFile() {
|
... | ... | @@ -306,6 +305,24 @@ export const TorProtocolService = { |
306 | 305 | return this._SOCKSPortInfo;
|
307 | 306 | },
|
308 | 307 | |
308 | + get torControlPortInfo() {
|
|
309 | + const info = {
|
|
310 | + password: this._controlPassword,
|
|
311 | + };
|
|
312 | + if (this._controlIPCFile) {
|
|
313 | + info.ipcFile = this._controlIPCFile?.clone();
|
|
314 | + }
|
|
315 | + if (this._controlPort) {
|
|
316 | + info.host = this._controlHost;
|
|
317 | + info.port = this._controlPort;
|
|
318 | + }
|
|
319 | + return info;
|
|
320 | + },
|
|
321 | + |
|
322 | + get torSOCKSPortInfo() {
|
|
323 | + return this._SOCKSPortInfo;
|
|
324 | + },
|
|
325 | + |
|
309 | 326 | // Public, but called only internally
|
310 | 327 | |
311 | 328 | // Executes a command on the control port.
|
... | ... | @@ -469,115 +486,8 @@ export const TorProtocolService = { |
469 | 486 | this._controlPassword = this._generateRandomPassword();
|
470 | 487 | }
|
471 | 488 | |
472 | - // Determine what kind of SOCKS port Tor and the browser will use.
|
|
473 | - // On Windows (where Unix domain sockets are not supported), TCP is
|
|
474 | - // always used.
|
|
475 | - //
|
|
476 | - // The following environment variables are supported and take
|
|
477 | - // precedence over preferences:
|
|
478 | - // TOR_SOCKS_IPC_PATH (file system path; ignored on Windows)
|
|
479 | - // TOR_SOCKS_HOST
|
|
480 | - // TOR_SOCKS_PORT
|
|
481 | - //
|
|
482 | - // The following preferences are consulted:
|
|
483 | - // network.proxy.socks
|
|
484 | - // network.proxy.socks_port
|
|
485 | - // extensions.torlauncher.socks_port_use_ipc (Boolean)
|
|
486 | - // extensions.torlauncher.socks_ipc_path (file system path)
|
|
487 | - // If extensions.torlauncher.socks_ipc_path is empty, a default
|
|
488 | - // path is used (<tor-data-directory>/socks.socket).
|
|
489 | - //
|
|
490 | - // When using TCP, if a value is not defined via an env variable it is
|
|
491 | - // taken from the corresponding browser preference if possible. The
|
|
492 | - // exceptions are:
|
|
493 | - // If network.proxy.socks contains a file: URL, a default value of
|
|
494 | - // "127.0.0.1" is used instead.
|
|
495 | - // If the network.proxy.socks_port value is 0, a default value of
|
|
496 | - // 9150 is used instead.
|
|
497 | - //
|
|
498 | - // Supported scenarios:
|
|
499 | - // 1. By default, an IPC object at a default path is used.
|
|
500 | - // 2. If extensions.torlauncher.socks_port_use_ipc is set to false,
|
|
501 | - // a TCP socket at 127.0.0.1:9150 is used, unless different values
|
|
502 | - // are set in network.proxy.socks and network.proxy.socks_port.
|
|
503 | - // 3. If the TOR_SOCKS_IPC_PATH env var is set, an IPC object at that
|
|
504 | - // path is used (e.g., a Unix domain socket).
|
|
505 | - // 4. If the TOR_SOCKS_HOST and/or TOR_SOCKS_PORT env vars are set, TCP
|
|
506 | - // is used. Values not set via env vars will be taken from the
|
|
507 | - // network.proxy.socks and network.proxy.socks_port prefs as described
|
|
508 | - // above.
|
|
509 | - // 5. If extensions.torlauncher.socks_port_use_ipc is true and
|
|
510 | - // extensions.torlauncher.socks_ipc_path is set, an IPC object at
|
|
511 | - // the specified path is used.
|
|
512 | - // 6. Tor Launcher is disabled. Torbutton will respect the env vars if
|
|
513 | - // present; if not, the values in network.proxy.socks and
|
|
514 | - // network.proxy.socks_port are used without modification.
|
|
515 | - |
|
516 | - let useIPC;
|
|
517 | - this._SOCKSPortInfo = { ipcFile: undefined, host: undefined, port: 0 };
|
|
518 | - if (!isWindows && Services.env.exists("TOR_SOCKS_IPC_PATH")) {
|
|
519 | - let ipcPath = Services.env.get("TOR_SOCKS_IPC_PATH");
|
|
520 | - this._SOCKSPortInfo.ipcFile = new lazy.FileUtils.File(ipcPath);
|
|
521 | - useIPC = true;
|
|
522 | - } else {
|
|
523 | - // Check for TCP host and port environment variables.
|
|
524 | - if (Services.env.exists("TOR_SOCKS_HOST")) {
|
|
525 | - this._SOCKSPortInfo.host = Services.env.get("TOR_SOCKS_HOST");
|
|
526 | - useIPC = false;
|
|
527 | - }
|
|
528 | - if (Services.env.exists("TOR_SOCKS_PORT")) {
|
|
529 | - this._SOCKSPortInfo.port = parseInt(
|
|
530 | - Services.env.get("TOR_SOCKS_PORT"),
|
|
531 | - 10
|
|
532 | - );
|
|
533 | - useIPC = false;
|
|
534 | - }
|
|
535 | - }
|
|
536 | - |
|
537 | - if (useIPC === undefined) {
|
|
538 | - useIPC =
|
|
539 | - !isWindows &&
|
|
540 | - Services.prefs.getBoolPref(
|
|
541 | - "extensions.torlauncher.socks_port_use_ipc",
|
|
542 | - false
|
|
543 | - );
|
|
544 | - }
|
|
545 | - |
|
546 | - // Fill in missing SOCKS info from prefs.
|
|
547 | - if (useIPC) {
|
|
548 | - if (!this._SOCKSPortInfo.ipcFile) {
|
|
549 | - this._SOCKSPortInfo.ipcFile = TorLauncherUtil.getTorFile(
|
|
550 | - "socks_ipc",
|
|
551 | - false
|
|
552 | - );
|
|
553 | - }
|
|
554 | - } else {
|
|
555 | - if (!this._SOCKSPortInfo.host) {
|
|
556 | - let socksAddr = Services.prefs.getCharPref(
|
|
557 | - "network.proxy.socks",
|
|
558 | - "127.0.0.1"
|
|
559 | - );
|
|
560 | - let socksAddrHasHost = socksAddr && !socksAddr.startsWith("file:");
|
|
561 | - this._SOCKSPortInfo.host = socksAddrHasHost ? socksAddr : "127.0.0.1";
|
|
562 | - }
|
|
563 | - |
|
564 | - if (!this._SOCKSPortInfo.port) {
|
|
565 | - let socksPort = Services.prefs.getIntPref(
|
|
566 | - "network.proxy.socks_port",
|
|
567 | - 0
|
|
568 | - );
|
|
569 | - // This pref is set as 0 by default in Firefox, use 9150 if we get 0.
|
|
570 | - this._SOCKSPortInfo.port = socksPort != 0 ? socksPort : 9150;
|
|
571 | - }
|
|
572 | - }
|
|
573 | - |
|
574 | - logger.info("SOCKS port type: " + (useIPC ? "IPC" : "TCP"));
|
|
575 | - if (useIPC) {
|
|
576 | - logger.info(`ipcFile: ${this._SOCKSPortInfo.ipcFile.path}`);
|
|
577 | - } else {
|
|
578 | - logger.info(`SOCKS host: ${this._SOCKSPortInfo.host}`);
|
|
579 | - logger.info(`SOCKS port: ${this._SOCKSPortInfo.port}`);
|
|
580 | - }
|
|
489 | + this._SOCKSPortInfo = TorLauncherUtil.getPreferredSocksConfiguration();
|
|
490 | + TorLauncherUtil.setProxyConfiguration(this._SOCKSPortInfo);
|
|
581 | 491 | |
582 | 492 | // Set the global control port info parameters.
|
583 | 493 | // These values may be overwritten by torbutton when it initializes, but
|
... | ... | @@ -781,38 +691,6 @@ export const TorProtocolService = { |
781 | 691 | return pwd;
|
782 | 692 | },
|
783 | 693 | |
784 | - // Based on Vidalia's TorSettings::hashPassword().
|
|
785 | - _hashPassword(aHexPassword) {
|
|
786 | - if (!aHexPassword) {
|
|
787 | - return null;
|
|
788 | - }
|
|
789 | - |
|
790 | - // Generate a random, 8 byte salt value.
|
|
791 | - const salt = Array.from(crypto.getRandomValues(new Uint8Array(8)));
|
|
792 | - |
|
793 | - // Convert hex-encoded password to an array of bytes.
|
|
794 | - const password = [];
|
|
795 | - for (let i = 0; i < aHexPassword.length; i += 2) {
|
|
796 | - password.push(parseInt(aHexPassword.substring(i, i + 2), 16));
|
|
797 | - }
|
|
798 | - |
|
799 | - // Run through the S2K algorithm and convert to a string.
|
|
800 | - const kCodedCount = 96;
|
|
801 | - const hashVal = this._cryptoSecretToKey(password, salt, kCodedCount);
|
|
802 | - if (!hashVal) {
|
|
803 | - logger.error("_cryptoSecretToKey() failed");
|
|
804 | - return null;
|
|
805 | - }
|
|
806 | - |
|
807 | - const arrayToHex = aArray =>
|
|
808 | - aArray.map(item => this._toHex(item, 2)).join("");
|
|
809 | - let rv = "16:";
|
|
810 | - rv += arrayToHex(salt);
|
|
811 | - rv += this._toHex(kCodedCount, 2);
|
|
812 | - rv += arrayToHex(hashVal);
|
|
813 | - return rv;
|
|
814 | - },
|
|
815 | - |
|
816 | 694 | // Returns -1 upon failure.
|
817 | 695 | _cryptoRandInt(aMax) {
|
818 | 696 | // Based on tor's crypto_rand_int().
|
... | ... | @@ -831,43 +709,6 @@ export const TorProtocolService = { |
831 | 709 | return val % aMax;
|
832 | 710 | },
|
833 | 711 | |
834 | - // _cryptoSecretToKey() is similar to Vidalia's crypto_secret_to_key().
|
|
835 | - // It generates and returns a hash of aPassword by following the iterated
|
|
836 | - // and salted S2K algorithm (see RFC 2440 section 3.6.1.3).
|
|
837 | - // Returns an array of bytes.
|
|
838 | - _cryptoSecretToKey(aPassword, aSalt, aCodedCount) {
|
|
839 | - if (!aPassword || !aSalt) {
|
|
840 | - return null;
|
|
841 | - }
|
|
842 | - |
|
843 | - const inputArray = aSalt.concat(aPassword);
|
|
844 | - |
|
845 | - // Subtle crypto only has the final digest, and does not allow incremental
|
|
846 | - // updates. Also, it is async, so we should hash and keep the hash in a
|
|
847 | - // variable if we wanted to switch to getters.
|
|
848 | - // So, keeping this implementation should be okay for now.
|
|
849 | - const hasher = Cc["@mozilla.org/security/hash;1"].createInstance(
|
|
850 | - Ci.nsICryptoHash
|
|
851 | - );
|
|
852 | - hasher.init(hasher.SHA1);
|
|
853 | - const kEXPBIAS = 6;
|
|
854 | - let count = (16 + (aCodedCount & 15)) << ((aCodedCount >> 4) + kEXPBIAS);
|
|
855 | - while (count > 0) {
|
|
856 | - if (count > inputArray.length) {
|
|
857 | - hasher.update(inputArray, inputArray.length);
|
|
858 | - count -= inputArray.length;
|
|
859 | - } else {
|
|
860 | - const finalArray = inputArray.slice(0, count);
|
|
861 | - hasher.update(finalArray, finalArray.length);
|
|
862 | - count = 0;
|
|
863 | - }
|
|
864 | - }
|
|
865 | - return hasher
|
|
866 | - .finish(false)
|
|
867 | - .split("")
|
|
868 | - .map(b => b.charCodeAt(0));
|
|
869 | - },
|
|
870 | - |
|
871 | 712 | _toHex(aValue, aMinLen) {
|
872 | 713 | return aValue.toString(16).padStart(aMinLen, "0");
|
873 | 714 | },
|
... | ... | @@ -60,7 +60,7 @@ var torbutton_init; |
60 | 60 | } else {
|
61 | 61 | try {
|
62 | 62 | // Try to get password from Tor Launcher.
|
63 | - m_tb_control_pass = TorProtocolService.torGetPassword(false);
|
|
63 | + m_tb_control_pass = TorProtocolService.torGetPassword();
|
|
64 | 64 | } catch (e) {}
|
65 | 65 | }
|
66 | 66 |
1 | 1 | Classes = [
|
2 | - {
|
|
3 | - "cid": "{06322def-6fde-4c06-aef6-47ae8e799629}",
|
|
4 | - "contract_ids": [
|
|
5 | - "@torproject.org/startup-observer;1"
|
|
6 | - ],
|
|
7 | - "jsm": "resource://torbutton/modules/TorbuttonStartupObserver.jsm",
|
|
8 | - "constructor": "StartupObserver",
|
|
9 | - },
|
|
10 | 2 | {
|
11 | 3 | "cid": "{f36d72c9-9718-4134-b550-e109638331d7}",
|
12 | 4 | "contract_ids": [
|
1 | -// Bug 1506 P1-3: This code is mostly hackish remnants of session store
|
|
2 | -// support. There are a couple of observer events that *might* be worth
|
|
3 | -// listening to. Search for 1506 in the code.
|
|
4 | - |
|
5 | -/*************************************************************************
|
|
6 | - * Startup observer (_javascript_ XPCOM component)
|
|
7 | - *
|
|
8 | - * Cases tested (each during Tor and Non-Tor, FF4 and FF3.6)
|
|
9 | - * 1. Crash
|
|
10 | - * 2. Upgrade
|
|
11 | - * 3. Fresh install
|
|
12 | - *
|
|
13 | - *************************************************************************/
|
|
14 | - |
|
15 | -var EXPORTED_SYMBOLS = ["StartupObserver"];
|
|
16 | - |
|
17 | -const { Services } = ChromeUtils.import("resource://gre/modules/Services.jsm");
|
|
18 | -const { XPCOMUtils } = ChromeUtils.import(
|
|
19 | - "resource://gre/modules/XPCOMUtils.jsm"
|
|
20 | -);
|
|
21 | - |
|
22 | -const { TorProtocolService } = ChromeUtils.import(
|
|
23 | - "resource://gre/modules/TorProtocolService.jsm"
|
|
24 | -);
|
|
25 | - |
|
26 | -const lazy = {};
|
|
27 | - |
|
28 | -XPCOMUtils.defineLazyModuleGetters(lazy, {
|
|
29 | - FileUtils: "resource://gre/modules/FileUtils.jsm",
|
|
30 | -});
|
|
31 | - |
|
32 | -function StartupObserver() {
|
|
33 | - this.logger = Cc["@torproject.org/torbutton-logger;1"].getService(
|
|
34 | - Ci.nsISupports
|
|
35 | - ).wrappedJSObject;
|
|
36 | - this._prefs = Services.prefs;
|
|
37 | - this.logger.log(3, "Startup Observer created");
|
|
38 | - |
|
39 | - try {
|
|
40 | - // XXX: We're in a race with HTTPS-Everywhere to update our proxy settings
|
|
41 | - // before the initial SSL-Observatory test... If we lose the race, Firefox
|
|
42 | - // caches the old proxy settings for check.tp.o somehwere, and it never loads :(
|
|
43 | - this.setProxySettings();
|
|
44 | - } catch (e) {
|
|
45 | - this.logger.log(
|
|
46 | - 4,
|
|
47 | - "Early proxy change failed. Will try again at profile load. Error: " + e
|
|
48 | - );
|
|
49 | - }
|
|
50 | -}
|
|
51 | - |
|
52 | -StartupObserver.prototype = {
|
|
53 | - // Bug 6803: We need to get the env vars early due to
|
|
54 | - // some weird proxy caching code that showed up in FF15.
|
|
55 | - // Otherwise, homepage domain loads fail forever.
|
|
56 | - setProxySettings() {
|
|
57 | - // Bug 1506: Still want to get these env vars
|
|
58 | - if (Services.env.exists("TOR_TRANSPROXY")) {
|
|
59 | - this.logger.log(3, "Resetting Tor settings to transproxy");
|
|
60 | - this._prefs.setBoolPref("network.proxy.socks_remote_dns", false);
|
|
61 | - this._prefs.setIntPref("network.proxy.type", 0);
|
|
62 | - this._prefs.setIntPref("network.proxy.socks_port", 0);
|
|
63 | - this._prefs.setCharPref("network.proxy.socks", "");
|
|
64 | - } else {
|
|
65 | - // Try to retrieve SOCKS proxy settings from Tor Launcher.
|
|
66 | - let socksPortInfo;
|
|
67 | - try {
|
|
68 | - socksPortInfo = TorProtocolService.torGetSOCKSPortInfo();
|
|
69 | - } catch (e) {
|
|
70 | - this.logger.log(3, "tor launcher failed " + e);
|
|
71 | - }
|
|
72 | - |
|
73 | - // If Tor Launcher is not available, check environment variables.
|
|
74 | - if (!socksPortInfo) {
|
|
75 | - socksPortInfo = { ipcFile: undefined, host: undefined, port: 0 };
|
|
76 | - |
|
77 | - let isWindows = Services.appinfo.OS === "WINNT";
|
|
78 | - if (!isWindows && Services.env.exists("TOR_SOCKS_IPC_PATH")) {
|
|
79 | - socksPortInfo.ipcFile = new lazy.FileUtils.File(
|
|
80 | - Services.env.get("TOR_SOCKS_IPC_PATH")
|
|
81 | - );
|
|
82 | - } else {
|
|
83 | - if (Services.env.exists("TOR_SOCKS_HOST")) {
|
|
84 | - socksPortInfo.host = Services.env.get("TOR_SOCKS_HOST");
|
|
85 | - }
|
|
86 | - if (Services.env.exists("TOR_SOCKS_PORT")) {
|
|
87 | - socksPortInfo.port = parseInt(Services.env.get("TOR_SOCKS_PORT"));
|
|
88 | - }
|
|
89 | - }
|
|
90 | - }
|
|
91 | - |
|
92 | - // Adjust network.proxy prefs.
|
|
93 | - if (socksPortInfo.ipcFile) {
|
|
94 | - let fph = Services.io
|
|
95 | - .getProtocolHandler("file")
|
|
96 | - .QueryInterface(Ci.nsIFileProtocolHandler);
|
|
97 | - let fileURI = fph.newFileURI(socksPortInfo.ipcFile);
|
|
98 | - this.logger.log(3, "Reset socks to " + fileURI.spec);
|
|
99 | - this._prefs.setCharPref("network.proxy.socks", fileURI.spec);
|
|
100 | - this._prefs.setIntPref("network.proxy.socks_port", 0);
|
|
101 | - } else {
|
|
102 | - if (socksPortInfo.host) {
|
|
103 | - this._prefs.setCharPref("network.proxy.socks", socksPortInfo.host);
|
|
104 | - this.logger.log(3, "Reset socks host to " + socksPortInfo.host);
|
|
105 | - }
|
|
106 | - if (socksPortInfo.port) {
|
|
107 | - this._prefs.setIntPref(
|
|
108 | - "network.proxy.socks_port",
|
|
109 | - socksPortInfo.port
|
|
110 | - );
|
|
111 | - this.logger.log(3, "Reset socks port to " + socksPortInfo.port);
|
|
112 | - }
|
|
113 | - }
|
|
114 | - |
|
115 | - if (socksPortInfo.ipcFile || socksPortInfo.host || socksPortInfo.port) {
|
|
116 | - this._prefs.setBoolPref("network.proxy.socks_remote_dns", true);
|
|
117 | - this._prefs.setIntPref("network.proxy.type", 1);
|
|
118 | - }
|
|
119 | - }
|
|
120 | - |
|
121 | - // Force prefs to be synced to disk
|
|
122 | - Services.prefs.savePrefFile(null);
|
|
123 | - |
|
124 | - this.logger.log(3, "Synced network settings to environment.");
|
|
125 | - },
|
|
126 | - |
|
127 | - observe(subject, topic, data) {
|
|
128 | - if (topic == "profile-after-change") {
|
|
129 | - this.setProxySettings();
|
|
130 | - }
|
|
131 | - |
|
132 | - // In all cases, force prefs to be synced to disk
|
|
133 | - Services.prefs.savePrefFile(null);
|
|
134 | - },
|
|
135 | - |
|
136 | - // Hack to get us registered early to observe recovery
|
|
137 | - _xpcom_categories: [{ category: "profile-after-change" }],
|
|
138 | -}; |
... | ... | @@ -8,7 +8,3 @@ JAR_MANIFESTS += ['jar.mn'] |
8 | 8 | XPCOM_MANIFESTS += [
|
9 | 9 | "components.conf",
|
10 | 10 | ] |
11 | - |
|
12 | -EXTRA_COMPONENTS += [
|
|
13 | - "torbutton.manifest",
|
|
14 | -] |
1 | -category profile-after-change StartupObserver @torproject.org/startup-observer;1 |