[tor-commits] [tor-launcher/master] Bug 20185: Avoid using Unix domain socket paths that are too long

gk at torproject.org gk at torproject.org
Thu Oct 27 18:31:58 UTC 2016


commit 4dd8f6130f931616cf014e0ded444c30e04c8bad
Author: Kathy Brade <brade at pearlcrescent.com>
Date:   Thu Oct 27 12:02:57 2016 -0400

    Bug 20185: Avoid using Unix domain socket paths that are too long
    
    Enforce a maximum length of 100 for Unix domain socket paths.
    
    If $XDG_RUNTIME_DIR is set, create a unique subdirectory within that
      directory and place the control and SOCKS sockets there if the
      resulting paths will not be too long
    else if the length of <tor-data-dir>/control.socket is less than 100
      characters, place both sockets under <tor-data-dir> (this is compatible
      with the Tor Browser 6.5a3 behavior)
    else create a unique subdirectory under /tmp and place the sockets there.
    
    The unique subdirectory that is created under $XDG_RUNTIME_DIR or /tmp
    will be named Tor if no such directory exists; otherwise, an integer
    suffix will be appended until a new, uniquely named directory is found
    such as /tmp/Tor-1.
    
    Also, when starting tor, only include a SocksPort argument if a Unix
    domain socket path or a host/port is available.
---
 src/components/tl-process.js |  30 +++--
 src/modules/tl-util.jsm      | 254 ++++++++++++++++++++++++++++++++++---------
 2 files changed, 223 insertions(+), 61 deletions(-)

diff --git a/src/components/tl-process.js b/src/components/tl-process.js
index 844aba1..de71d45 100644
--- a/src/components/tl-process.js
+++ b/src/components/tl-process.js
@@ -142,7 +142,11 @@ TorProcessService.prototype =
 
       this.mObsSvc.notifyObservers(null, "TorProcessExited", null);
 
-      if (!this.mIsQuitting)
+      if (this.mIsQuitting)
+      {
+        TorLauncherUtil.cleanupTempDirectories();
+      }
+      else
       {
         this.mProtocolSvc.TorCleanupConnection();
 
@@ -395,15 +399,21 @@ TorProcessService.prototype =
       // a TCP port and an IPC port (e.g., a Unix domain socket).
       if (socksPortInfo)
       {
-        let socksPortArg = (socksPortInfo.ipcFile)
-                          ? this._ipcPortArg(socksPortInfo.ipcFile)
-                          : socksPortInfo.host + ':' + socksPortInfo.port;
-        let socksPortFlags = TorLauncherUtil.getCharPref(
-                                "extensions.torlauncher.socks_port_flags");
-        if (socksPortFlags)
-          socksPortArg += ' ' + socksPortFlags;
-        args.push("SocksPort");
-        args.push(socksPortArg);
+        let socksPortArg;
+        if (socksPortInfo.ipcFile)
+          socksPortArg = this._ipcPortArg(socksPortInfo.ipcFile)
+        else if (socksPortInfo.host && (socksPortInfo.port != 0))
+          socksPortArg = socksPortInfo.host + ':' + socksPortInfo.port;
+
+        if (socksPortArg)
+        {
+          let socksPortFlags = TorLauncherUtil.getCharPref(
+                                  "extensions.torlauncher.socks_port_flags");
+          if (socksPortFlags)
+            socksPortArg += ' ' + socksPortFlags;
+          args.push("SocksPort");
+          args.push(socksPortArg);
+        }
       }
 
       var pid = this._getpid();
diff --git a/src/modules/tl-util.jsm b/src/modules/tl-util.jsm
index a1256fd..2f8d14e 100644
--- a/src/modules/tl-util.jsm
+++ b/src/modules/tl-util.jsm
@@ -251,6 +251,14 @@ let TorLauncherUtil =  // Public
     } catch (e) {}
   },
 
+  clearUserPref: function(aPrefName)
+  {
+    try
+    {
+      TLUtilInternal.mPrefsSvc.clearUserPref(aPrefName);
+    } catch (e) {}
+  },
+
   // Currently, this returns a random permutation of an array, bridgeArray.
   // Later, we might want to change this function to weight based on the
   // bridges' bandwidths.
@@ -397,22 +405,97 @@ let TorLauncherUtil =  // Public
     if (!aTorFileType)
       return null;
 
-    let isRelativePath = true;
+    let torFile;  // an nsIFile to be returned
+    let path;     // a relative or absolute path that will determine torFile
+
+    let isRelativePath = false;
     let isUserData = (aTorFileType != "tor") &&
                      (aTorFileType != "torrc-defaults");
     let isControlIPC = ("control_ipc" == aTorFileType);
     let isSOCKSIPC = ("socks_ipc" == aTorFileType);
     let isIPC = isControlIPC || isSOCKSIPC;
+    let checkIPCPathLen = true;
+
+    const kControlIPCFileName = "control.socket";
+    const kSOCKSIPCFileName = "socks.socket";
+    let extraIPCPathLen = (isSOCKSIPC) ? 2 : 0;
+    let ipcFileName;
+    if (isControlIPC)
+      ipcFileName = kControlIPCFileName;
+    else if (isSOCKSIPC)
+      ipcFileName = kSOCKSIPCFileName;
+
+    // If this is the first request for an IPC path during this browser
+    // session, remove the old temporary directory. This helps to keep /tmp
+    // clean if the browser crashes or is killed.
+    let ipcDirPath;
+    if (isIPC && TLUtilInternal.mIsFirstIPCPathRequest)
+    {
+      this.cleanupTempDirectories();
+      TLUtilInternal.mIsFirstIPCPathRequest = false;
+    }
+    else
+    {
+      // Retrieve path for IPC objects (it may have already been determined).
+      ipcDirPath = this.getCharPref(TLUtilInternal.kIPCDirPrefName);
+    }
+
+    // First, check the _path preference for this file type.
     let prefName = "extensions.torlauncher." + aTorFileType + "_path";
-    let path = this.getCharPref(prefName);
+    path = this.getCharPref(prefName);
     if (path)
     {
       let re = (this.isWindows) ?  /^[A-Za-z]:\\/ : /^\//;
       isRelativePath = !re.test(path);
+      checkIPCPathLen = false; // always try to use path if provided in pref
     }
-    else
+    else if (isIPC)
+    {
+      if (ipcDirPath)
+      {
+        // We have already determined where IPC objects will be placed.
+        torFile = Cc['@mozilla.org/file/local;1'].createInstance(Ci.nsIFile);
+        torFile.initWithPath(ipcDirPath);
+        torFile.append(ipcFileName);
+        checkIPCPathLen = false; // already checked.
+      }
+      else
+      {
+        // If XDG_RUNTIME_DIR is set, use it as the base directory for IPC
+        // objects (e.g., Unix domain sockets) -- assuming it is not too long.
+        let env = Cc["@mozilla.org/process/environment;1"]
+                    .getService(Ci.nsIEnvironment);
+        if (env.exists("XDG_RUNTIME_DIR"))
+        {
+          let ipcDir = TLUtilInternal._createUniqueIPCDir(
+                                               env.get("XDG_RUNTIME_DIR"));
+          if (ipcDir)
+          {
+            let f = ipcDir.clone();
+            f.append(ipcFileName);
+            if (TLUtilInternal._isIPCPathLengthOK(f.path, extraIPCPathLen))
+            {
+              torFile = f;
+              checkIPCPathLen = false; // no need to check again.
+
+              // Store directory path so it can be reused for other IPC objects
+              // and so it can be removed during exit.
+              this.setCharPref(TLUtilInternal.kIPCDirPrefName, ipcDir.path);
+            }
+            else
+            {
+              // too long; remove the directory that we just created.
+              ipcDir.remove(false);
+            }
+          }
+        }
+      }
+    }
+
+    if (!path && !torFile)
     {
-      // Get default path.
+      // No preference and no pre-determined IPC path: use a default path.
+      isRelativePath = true;
       if (TLUtilInternal._isUserDataOutsideOfAppDir)
       {
         // This block is used for the TorBrowser-Data/ case.
@@ -437,10 +520,8 @@ let TorLauncherUtil =  // Public
             path = "Tor/torrc";
           else if ("tordatadir" == aTorFileType)
             path = "Tor";
-          else if (isControlIPC)
-            path = "Tor/control.socket";
-          else if (isSOCKSIPC)
-            path = "Tor/socks.socket";
+          else if (isIPC)
+            path = "Tor/" + ipcFileName;
         }
         else // Linux and others.
         {
@@ -452,10 +533,8 @@ let TorLauncherUtil =  // Public
             path = "Tor/torrc";
           else if ("tordatadir" == aTorFileType)
             path = "Tor";
-          else if (isControlIPC)
-            path = "Tor/control.socket";
-          else if (isSOCKSIPC)
-            path = "Tor/socks.socket";
+          else if (isIPC)
+            path = "Tor/" + ipcFileName;
         }
       }
       else if (this.isWindows)
@@ -481,67 +560,90 @@ let TorLauncherUtil =  // Public
           path = "Data/Tor/torrc";
         else if ("tordatadir" == aTorFileType)
           path = "Data/Tor";
-        else if (isControlIPC)
-          path = "Data/Tor/control.socket";
-        else if (isSOCKSIPC)
-          path = "Data/Tor/socks.socket";
+        else if (isIPC)
+          path = "Data/Tor/" + ipcFileName;
       }
-    }
 
-    if (!path)
-      return null;
+      if (!path)
+        return null;
+    }
 
     try
     {
-      let f;
-      if (isRelativePath)
+      if (path)
       {
-        // Turn 'path' into an absolute path.
-        if (TLUtilInternal._isUserDataOutsideOfAppDir)
+        if (isRelativePath)
         {
-          let baseDir = isUserData ? TLUtilInternal._dataDir
-                                   : TLUtilInternal._appDir;
-          f = baseDir.clone();
+          // Turn 'path' into an absolute path.
+          if (TLUtilInternal._isUserDataOutsideOfAppDir)
+          {
+            let baseDir = isUserData ? TLUtilInternal._dataDir
+                                     : TLUtilInternal._appDir;
+            torFile = baseDir.clone();
+          }
+          else
+          {
+            torFile = TLUtilInternal._appDir.clone();
+            torFile.append("TorBrowser");
+          }
+          torFile.appendRelativePath(path);
         }
         else
         {
-          f = TLUtilInternal._appDir.clone();
-          f.append("TorBrowser");
+          torFile = Cc['@mozilla.org/file/local;1'].createInstance(Ci.nsIFile);
+          torFile.initWithPath(path);
         }
-        f.appendRelativePath(path);
-      }
-      else
-      {
-        f = Cc['@mozilla.org/file/local;1'].createInstance(Ci.nsIFile);
-        f.initWithPath(path);
-      }
 
-      if (!f.exists() && !isIPC && aCreate)
-      {
-        try
-        {
-          if ("tordatadir" == aTorFileType)
-            f.create(f.DIRECTORY_TYPE, 0700);
-          else
-            f.create(f.NORMAL_FILE_TYPE, 0600);
-        }
-        catch (e)
+        if (!torFile.exists() && !isIPC && aCreate)
         {
-          TorLauncherLogger.safelog(4, "unable to create " + f.path + ": ", e);
-          return null;
+          try
+          {
+            if ("tordatadir" == aTorFileType)
+              torFile.create(torFile.DIRECTORY_TYPE, 0700);
+            else
+              torFile.create(torFile.NORMAL_FILE_TYPE, 0600);
+          }
+          catch (e)
+          {
+            TorLauncherLogger.safelog(4,
+                                "unable to create " + torFile.path + ": ", e);
+            return null;
+          }
         }
       }
 
       // If the file exists or an IPC object was requested, normalize the path
       // and return a file object. The control and SOCKS IPC objects will be
       // created by tor.
-      if (f.exists() || isIPC)
+      if (torFile.exists() || isIPC)
       {
-        try { f.normalize(); } catch(e) {}
-        return f;
+        try { torFile.normalize(); } catch(e) {}
+
+        // Ensure that the IPC path length is short enough for use by the
+        // operating system. If not, create and use a unique directory under
+        // /tmp for all IPC objects. The created directory path is stored in
+        // a preference so it can be reused for other IPC objects and so it
+        // can be removed during exit.
+        if (isIPC && checkIPCPathLen &&
+            !TLUtilInternal._isIPCPathLengthOK(torFile.path, extraIPCPathLen))
+        {
+          torFile = TLUtilInternal._createUniqueIPCDir("/tmp");
+          if (!torFile)
+          {
+            TorLauncherLogger.log(4,
+                              "failed to create unique directory under /tmp");
+            return null;
+          }
+
+          this.setCharPref(TLUtilInternal.kIPCDirPrefName, torFile.path);
+          torFile.append(ipcFileName);
+        }
+
+        return torFile;
       }
 
-      TorLauncherLogger.log(4, aTorFileType + " file not found: " + f.path);
+      TorLauncherLogger.log(4, aTorFileType + " file not found: "
+                               + torFile.path);
     }
     catch(e)
     {
@@ -551,6 +653,22 @@ let TorLauncherUtil =  // Public
 
     return null;  // File not found or error (logged above).
   }, // getTorFile()
+
+  cleanupTempDirectories: function()
+  {
+    try
+    {
+      let dirPath = this.getCharPref(TLUtilInternal.kIPCDirPrefName);
+      this.clearUserPref(TLUtilInternal.kIPCDirPrefName);
+      if (dirPath)
+      {
+        let f = Cc['@mozilla.org/file/local;1'].createInstance(Ci.nsIFile);
+        f.initWithPath(dirPath);
+        if (f.exists())
+          f.remove(false); // Remove directory if it is empty
+      }
+    } catch(e) {}
+  },
 };
 
 
@@ -561,6 +679,7 @@ let TLUtilInternal =  // Private
 {
   kThunderbirdID: "{3550f703-e582-4d05-9a08-453d09bdfdc6}",
   kInstantbirdID: "{33cb9019-c295-46dd-be21-8c4936574bee}",
+  kIPCDirPrefName: "extensions.torlauncher.tmp_ipc_dir",
 
   mPrefsSvc : null,
   mStringBundle : null,
@@ -570,6 +689,7 @@ let TLUtilInternal =  // Private
                                          //   this._isUserDataOutsideOfAppDir)
   mAppDir: null,        // nsIFile (cached; access via this._appDir)
   mDataDir: null,       // nsIFile (cached; access via this._dataDir)
+  mIsFirstIPCPathRequest : true,
 
   _init: function()
   {
@@ -682,6 +802,38 @@ let TLUtilInternal =  // Private
     return this.mDataDir;
   }, // get _dataDir
 
+  // Return true if aPath is short enough to be used as an IPC object path,
+  // e.g., for a Unix domain socket path. aExtraLen is the "delta" necessary
+  // to accommodate other IPC objects that have longer names; it is used to
+  // account for "control.socket" vs. "socks.socket" (we want to ensure that
+  // all IPC objects are placed in the same parent directory unless the user
+  // has set prefs or env vars to explicitly specify the path for an object).
+  // We enforce a maximum length of 100 because all operating systems allow
+  // at least 100 characters for Unix domain socket paths.
+  _isIPCPathLengthOK: function(aPath, aExtraLen)
+  {
+    const kMaxIPCPathLen = 100;
+    return aPath && ((aPath.length + aExtraLen) <= kMaxIPCPathLen);
+  },
+
+  // Returns an nsIFile or null if a unique directory could not be created.
+  _createUniqueIPCDir: function(aBasePath)
+  {
+    try
+    {
+      let d = Cc['@mozilla.org/file/local;1'].createInstance(Ci.nsIFile);
+      d.initWithPath(aBasePath);
+      d.append("Tor");
+      d.createUnique(Ci.nsIFile.DIRECTORY_TYPE, 0700);
+      return d;
+    }
+    catch (e)
+    {
+      TorLauncherLogger.safelog(4, "_createUniqueIPCDir failed for "
+                                   + aBasePath + ": ", e);
+      return null;
+    }
+  },
 };
 
 



More information about the tor-commits mailing list