[tor-commits] [torbutton/maint-9.0] Bug 30237: Control port module improvements for v3 client authentication

sysrqb at torproject.org sysrqb at torproject.org
Fri Jan 3 20:38:36 UTC 2020


commit 46efc92348dbed06fc31ddfb0a5ac2e4e8554de2
Author: Kathy Brade <brade at pearlcrescent.com>
Date:   Tue Nov 12 15:38:10 2019 -0500

    Bug 30237: Control port module improvements for v3 client authentication
    
    Split the configuration portion of the controller() function into
    a new configureControlPortModule() function. This allows the
    Torbutton code to continue to own the configuration (with help from
    Tor Launcher) while making it possible for the v3 client authentication
    prompt to use Torbutton's control port module.
    
    Also, include error information returned by Tor within error objects
    generated by the control port module and add support for Tor's new
    ONION_CLIENT_AUTH_ADD control port command.
---
 chrome/content/tor-circuit-display.js | 21 ++++----
 chrome/content/torbutton.js           |  8 +--
 modules/tor-control-port.js           | 95 ++++++++++++++++++++++++++++-------
 3 files changed, 92 insertions(+), 32 deletions(-)

diff --git a/chrome/content/tor-circuit-display.js b/chrome/content/tor-circuit-display.js
index 21bcc57b..1e58b2b4 100644
--- a/chrome/content/tor-circuit-display.js
+++ b/chrome/content/tor-circuit-display.js
@@ -6,9 +6,8 @@
 // with docco.js to produce pretty documentation.
 //
 // This script is to be embedded in torbutton.xul. It defines a single global
-// function, createTorCircuitDisplay(ipcFile, host, port, password), which
-// activates the automatic Tor circuit display for the current tab and any
-// future tabs.
+// function, createTorCircuitDisplay(), which activates the automatic Tor
+// circuit display for the current tab and any future tabs.
 //
 // See https://trac.torproject.org/8641
 
@@ -16,11 +15,12 @@
 /* global document, gBrowser, Components */
 
 // ### Main function
-// __createTorCircuitDisplay(ipcFile, host, port, password, enablePrefName)__.
+// __createTorCircuitDisplay(enablePrefName)__.
 // The single function that prepares tor circuit display. Connects to a tor
-// control port with the given ipcFile or host plus port, and password, and
-// binds to a named bool pref whose value determines whether the circuit display
-// is enabled or disabled.
+// control port using information provided to the control port module via
+// a previous call to configureControlPortModule(), and binds to a named
+// bool pref whose value determines whether the circuit display is enabled
+// or disabled.
 let createTorCircuitDisplay = (function () {
 
 "use strict";
@@ -415,11 +415,11 @@ let ensureCorrectPopupDimensions = function () {
 
 // ## Main function
 
-// __setupDisplay(ipcFile, host, port, password, enablePrefName)__.
+// __setupDisplay(enablePrefName)__.
 // Once called, the Tor circuit display will be started whenever
 // the "enablePref" is set to true, and stopped when it is set to false.
 // A reference to this function (called createTorCircuitDisplay) is exported as a global.
-let setupDisplay = function (ipcFile, host, port, password, enablePrefName) {
+let setupDisplay = function (enablePrefName) {
   setupGuardNote();
   let myController = null,
       stopCollectingIsolationData = null,
@@ -442,8 +442,7 @@ let setupDisplay = function (ipcFile, host, port, password, enablePrefName) {
       },
       start = function () {
         if (!myController) {
-          myController = controller(ipcFile, host, port || 9151, password,
-                function (err) {
+          myController = controller(function (err) {
             // An error has occurred.
             logger.eclog(5, err);
             logger.eclog(5, "Disabling tor display circuit because of an error.");
diff --git a/chrome/content/torbutton.js b/chrome/content/torbutton.js
index aacae042..b9642a89 100644
--- a/chrome/content/torbutton.js
+++ b/chrome/content/torbutton.js
@@ -21,6 +21,7 @@ let {
   getDomainForBrowser,
 } = ChromeUtils.import("resource://torbutton/modules/utils.js", {});
 let SecurityPrefs = ChromeUtils.import("resource://torbutton/modules/security-prefs.js", {});
+let { configureControlPortModule } = Cu.import("resource://torbutton/modules/tor-control-port.js", {});
 
 const k_tb_last_browser_version_pref = "extensions.torbutton.lastBrowserVersion";
 const k_tb_browser_update_needed_pref = "extensions.torbutton.updateNeeded";
@@ -334,6 +335,9 @@ function torbutton_init() {
         }
     }
 
+    configureControlPortModule(m_tb_control_ipc_file, m_tb_control_host,
+                               m_tb_control_port, m_tb_control_pass);
+
     // Add about:tor IPC message listener.
     window.messageManager.addMessageListener("AboutTor:Loaded",
                                    torbutton_abouttor_message_handler);
@@ -353,9 +357,7 @@ function torbutton_init() {
     torbutton_notify_if_update_needed();
 
     try {
-        createTorCircuitDisplay(m_tb_control_ipc_file, m_tb_control_host,
-                                m_tb_control_port, m_tb_control_pass,
-                               "extensions.torbutton.display_circuit");
+        createTorCircuitDisplay("extensions.torbutton.display_circuit");
     } catch(e) {
         torbutton_log(4, "Error creating the tor circuit display " + e);
     }
diff --git a/modules/tor-control-port.js b/modules/tor-control-port.js
index 9f6dbeb6..1b0a79ad 100644
--- a/modules/tor-control-port.js
+++ b/modules/tor-control-port.js
@@ -7,10 +7,15 @@
 //
 // To import the module, use
 //
-//  let { controller } = Components.utils.import("path/to/tor-control-port.js", {});
+//  let { configureControlPortModule, controller } =
+//                Components.utils.import("path/to/tor-control-port.js", {});
+//
+// See the second-to-last function defined in this file:
+//   configureControlPortModule(ipcFile, host, port, password)
+// for usage of the configureControlPortModule function.
 //
 // See the last function defined in this file:
-//   controller(ipcFile, host, port, password, onError)
+//   controller(onError)
 // for usage of the controller function.
 
 /* jshint esnext: true */
@@ -229,7 +234,16 @@ io.matchRepliesToCommands = function (asyncSend, dispatcher) {
     let [command, replyCallback, errorCallback] = commandQueue.shift();
     if (message.match(/^2/) && replyCallback) replyCallback(message);
     if (message.match(/^[45]/) && errorCallback) {
-      errorCallback(new Error(command + " -> " + message));
+      let myErr = new Error(command + " -> " + message);
+      // Add Tor-specific information to the Error object.
+      let idx = message.indexOf(' ');
+      if (idx > 0) {
+        myErr.torStatusCode = message.substring(0, idx);
+        myErr.torMessage = message.substring(idx);
+      } else {
+        myErr.torStatusCode = message;
+      }
+      errorCallback(myErr);
     }
   });
   // Create and return a version of sendCommand that returns a Promise.
@@ -562,6 +576,24 @@ info.getConf = function (aControlSocket, key) {
                        .then(info.getMultipleResponseValues);
 };
 
+// ## onionAuth
+// A namespace for functions related to tor's ONION_CLIENT_AUTH_* commands.
+let onionAuth = {};
+
+// __onionAuth.add(controlSocket, hsAddress, b64PrivateKey, nickname, isPermanent)__.
+// Sends a ONION_CLIENT_AUTH_ADD command to add a private key to the
+// Tor configuration.
+onionAuth.add = function (aControlSocket, hsAddress, b64PrivateKey,
+                          nickname, isPermanent) {
+  const keyType = "x25519";
+  let cmd = `onion_client_auth_add ${hsAddress} ${keyType}:${b64PrivateKey}`;
+  if (nickname)
+    cmd += ` ClientName=${nickname}`;
+  if (isPermanent)
+    cmd += " Flags=Permanent";
+  return aControlSocket.sendCommand(cmd);
+};
+
 // ## event
 // Handlers for events
 
@@ -617,6 +649,9 @@ tor.controller = function (ipcFile, host, port, password, onError) {
       isOpen = true;
   return { getInfo : key => info.getInfo(socket, key),
            getConf : key => info.getConf(socket, key),
+           onionAuthAdd : (hsAddress, b64PrivateKey, nickname, isPermanent) =>
+                            onionAuth.add(socket, hsAddress, b64PrivateKey,
+                                          nickname, isPermanent),
            watchEvent : (type, filter, onData) =>
                           event.watchEvent(socket, type, filter, onData),
            isOpen : () => isOpen,
@@ -626,29 +661,53 @@ tor.controller = function (ipcFile, host, port, password, onError) {
 
 // ## Export
 
-// __controller(ipcFile, host, port, password, onError)__.
-// Instantiates and returns a controller object connected to a tor ControlPort
-// on ipcFile or at host:port, authenticating with the given password, if
-// the controller doesn't yet exist. Otherwise returns the existing controller
-// to the given ipcFile or host:port.
+let controlPortInfo = {};
+
+// __configureControlPortModule(ipcFile, host, port, password)__.
+// Sets Tor control port connection parameters to be used in future calls to
+// the controller() function. Example:
+//     configureControlPortModule(undefined, "127.0.0.1", 9151, "MyPassw0rd");
+var configureControlPortModule = function (ipcFile, host, port, password) {
+  controlPortInfo.ipcFile = ipcFile;
+  controlPortInfo.host = host;
+  controlPortInfo.port = port || 9151;
+  controlPortInfo.password = password;
+};
+
+// __controller(onError)__.
+// Instantiates and returns a controller object that is connected and
+// authenticated to a Tor ControlPort using the connection parameters
+// provided in the most recent call to configureControlPortModule(), if
+// the controller doesn't yet exist. Otherwise returns the existing
+// controller to the given ipcFile or host:port.
 // onError is called with an error object as its single argument whenever
 // an error occurs. Example:
 //
 //     // Get the controller
-//     let c = controller(undefined, "127.0.0.1", 9151, "MyPassw0rd",
+//     let c = controller(
 //                    function (error) { console.log(error.message || error); });
 //     // Send command and receive `250` reply or error message in a promise:
 //     let replyPromise = c.getInfo("ip-to-country/16.16.16.16");
 //     // Close the controller permanently
 //     c.close();
-var controller = function (ipcFile, host, port, password, onError) {
-  let dest = (ipcFile) ? "unix:" + ipcFile.path : host + ":" + port,
-      maybeController = tor.controllerCache[dest];
-  return (tor.controllerCache[dest] =
-           (maybeController && maybeController.isOpen()) ?
-             maybeController :
-             tor.controller(ipcFile, host, port, password, onError));
+var controller = function (onError) {
+  if (!controlPortInfo.ipcFile && !controlPortInfo.host)
+    throw new Error("Please call configureControlPortModule first");
+
+  const dest = (controlPortInfo.ipcFile)
+               ? `unix:${controlPortInfo.ipcFile.path}`
+               : `${controlPortInfo.host}:${controlPortInfo.port}`;
+  const maybeController = tor.controllerCache[dest];
+  if (maybeController && maybeController.isOpen())
+    return maybeController;
+
+  tor.controllerCache[dest] = tor.controller(controlPortInfo.ipcFile,
+                                             controlPortInfo.host,
+                                             controlPortInfo.port,
+                                             controlPortInfo.password,
+                                             onError);
+  return tor.controllerCache[dest];
 };
 
-// Export the controller function for external use.
-var EXPORTED_SYMBOLS = ["controller"];
+// Export functions for external use.
+var EXPORTED_SYMBOLS = ["configureControlPortModule", "controller"];





More information about the tor-commits mailing list