commit c916965ec2fe0b29c30414bb3c0ce3bf6ca207c5
Author: Kathy Brade <brade(a)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 166a8ae..777a253 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();
@@ -399,15 +403,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 a18f779..313a51b 100644
--- a/src/modules/tl-util.jsm
+++ b/src/modules/tl-util.jsm
@@ -247,6 +247,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.
@@ -377,22 +385,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.
@@ -417,10 +500,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.
{
@@ -432,10 +513,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)
@@ -461,67 +540,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)
{
@@ -531,6 +633,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) {}
+ },
};
@@ -541,6 +659,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,
@@ -550,6 +669,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()
{
@@ -662,6 +782,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;
+ }
+ },
};