[tor-commits] [arm/release] fix: using all controller authentication methods
atagar at torproject.org
atagar at torproject.org
Sun Sep 25 21:38:31 UTC 2011
commit 8070fd004acde588f4d56d75bf6aa2c3f4d61b77
Author: Damian Johnson <atagar at torproject.org>
Date: Wed Sep 21 09:33:28 2011 -0700
fix: using all controller authentication methods
This is a hacked up version of the fix for
https://trac.torproject.org/projects/tor/ticket/3958
It's a bit of a Frankenstein and will be removed when the TorCtl fix is merged.
---
src/starter.py | 9 ++-
src/util/torTools.py | 181 ++++++++++++++++++++++++++++++++++++++++++++++++++
2 files changed, 186 insertions(+), 4 deletions(-)
diff --git a/src/starter.py b/src/starter.py
index dd5f5c9..4315f06 100644
--- a/src/starter.py
+++ b/src/starter.py
@@ -203,15 +203,16 @@ def _torCtlConnect(controlAddr="127.0.0.1", controlPort=9051, passphrase=None, i
conn = None
try:
- conn, authType, authValue = TorCtl.TorCtl.preauth_connect(controlAddr, controlPort)
+ #conn, authType, authValue = TorCtl.TorCtl.preauth_connect(controlAddr, controlPort)
+ conn, authTypes, authValue = util.torTools.preauth_connect_alt(controlAddr, controlPort)
- if authType == TorCtl.TorCtl.AUTH_TYPE.PASSWORD:
+ if TorCtl.TorCtl.AUTH_TYPE.PASSWORD in authTypes:
# password authentication, promting for the password if it wasn't provided
if passphrase: authValue = passphrase
else:
try: authValue = getpass.getpass("Controller password: ")
except KeyboardInterrupt: return None
- elif authType == TorCtl.TorCtl.AUTH_TYPE.COOKIE and authValue[0] != "/":
+ elif TorCtl.TorCtl.AUTH_TYPE.COOKIE in authTypes and authValue[0] != "/":
# Connecting to the control port will probably fail if it's using cookie
# authentication and the cookie path is relative (unfortunately this is
# the case for TBB). This is discussed in:
@@ -226,7 +227,7 @@ def _torCtlConnect(controlAddr="127.0.0.1", controlPort=9051, passphrase=None, i
except IOError: pass
# appends the path prefix if it's set
- if authType == TorCtl.TorCtl.AUTH_TYPE.COOKIE:
+ if TorCtl.TorCtl.AUTH_TYPE.COOKIE in authTypes:
pathPrefix = util.torTools.getConn().getPathPrefix()
# The os.path.join function is kinda stupid. If given an absolute path
diff --git a/src/util/torTools.py b/src/util/torTools.py
index 8c7a1ed..e59b35e 100644
--- a/src/util/torTools.py
+++ b/src/util/torTools.py
@@ -359,6 +359,187 @@ def isTorRunning():
return False
+# ============================================================
+# TODO: Remove when TorCtl can handle multiple auth methods
+# https://trac.torproject.org/projects/tor/ticket/3958
+#
+# The following is a hacked up version of the fix in that ticket.
+# ============================================================
+
+class FixedConnection(TorCtl.Connection):
+ def __init__(self, sock):
+ TorCtl.Connection.__init__(self, sock)
+ self._authTypes = []
+
+ def get_auth_types(self):
+ """
+ Provides the list of authentication types used for the control port. Each
+ are members of the AUTH_TYPE enumeration and return results will always
+ have at least one result. This raises an IOError if the query to
+ PROTOCOLINFO fails.
+ """
+
+ if not self._authTypes:
+ # check PROTOCOLINFO for authentication type
+ try:
+ authInfo = self.sendAndRecv("PROTOCOLINFO\r\n")[1][1]
+ except Exception, exc:
+ if exc.message: excMsg = ": %s" % exc
+ else: excMsg = ""
+ raise IOError("Unable to query PROTOCOLINFO for the authentication type%s" % excMsg)
+
+ # parses the METHODS and COOKIEFILE entries for details we need to
+ # authenticate
+
+ authTypes, cookiePath = [], None
+
+ for entry in authInfo.split():
+ if entry.startswith("METHODS="):
+ # Comma separated list of our authentication types. If we have
+ # multiple then any of them will work.
+
+ methodsEntry = entry[8:]
+
+ for authEntry in methodsEntry.split(","):
+ if authEntry == "NULL":
+ authTypes.append(TorCtl.AUTH_TYPE.NONE)
+ elif authEntry == "HASHEDPASSWORD":
+ authTypes.append(TorCtl.AUTH_TYPE.PASSWORD)
+ elif authEntry == "COOKIE":
+ authTypes.append(TorCtl.AUTH_TYPE.COOKIE)
+ else:
+ # not of a recognized authentication type (new addition to the
+ # control-spec?)
+
+ raise IOError("Unrecognized authentication type: %s" % authEntry)
+ elif entry.startswith("COOKIEFILE=\"") and entry.endswith("\""):
+ # Quoted path of the authentication cookie. This only exists if we're
+ # using cookie auth and, of course, doesn't account for chroot.
+
+ cookiePath = entry[12:-1]
+
+ # There should always be a AUTH METHODS entry. If we didn't then throw a
+ # wobbly.
+
+ if not authTypes:
+ raise IOError("PROTOCOLINFO response didn't include any authentication methods")
+
+ self._authType = authTypes[0]
+ self._authTypes = authTypes
+ self._cookiePath = cookiePath
+
+ return list(self._authTypes)
+
+ def authenticate(self, secret=""):
+ """
+ Authenticates to the control port. If an issue arises this raises either of
+ the following:
+ - IOError for failures in reading an authentication cookie or querying
+ PROTOCOLINFO.
+ - TorCtl.ErrorReply for authentication failures or if the secret is
+ undefined when using password authentication
+ """
+
+ # fetches authentication type and cookie path if still unloaded
+ if not self._authTypes: self.get_auth_types()
+
+ # validates input
+ if TorCtl.AUTH_TYPE.PASSWORD in self._authTypes and secret == "":
+ raise TorCtl.ErrorReply("Unable to authenticate: no passphrase provided")
+
+ # tries each of our authentication methods, throwing the last exception if
+ # they all fail
+
+ raisedExc = None
+ for authMethod in self._authTypes:
+ authCookie = None
+ try:
+ if authMethod == TorCtl.AUTH_TYPE.NONE:
+ self.authenticate_password("")
+ elif authMethod == TorCtl.AUTH_TYPE.PASSWORD:
+ self.authenticate_password(secret)
+ else:
+ authCookie = open(self._cookiePath, "r")
+ self.authenticate_cookie(authCookie)
+ authCookie.close()
+
+ # Did the above raise an exception? No? Cool, we're done.
+ return
+ except TorCtl.ErrorReply, exc:
+ if authCookie: authCookie.close()
+ issue = str(exc)
+
+ # simplifies message if the wrong credentials were provided (common
+ # mistake)
+ if issue.startswith("515 Authentication failed: "):
+ if issue[27:].startswith("Password did not match"):
+ issue = "password incorrect"
+ elif issue[27:] == "Wrong length on authentication cookie.":
+ issue = "cookie value incorrect"
+
+ raisedExc = TorCtl.ErrorReply("Unable to authenticate: %s" % issue)
+ except IOError, exc:
+ if authCookie: authCookie.close()
+ issue = None
+
+ # cleaner message for common errors
+ if str(exc).startswith("[Errno 13] Permission denied"):
+ issue = "permission denied"
+ elif str(exc).startswith("[Errno 2] No such file or directory"):
+ issue = "file doesn't exist"
+
+ # if problem's recognized give concise message, otherwise print exception
+ # string
+ if issue: raisedExc = IOError("Failed to read authentication cookie (%s): %s" % (issue, self._cookiePath))
+ else: raisedExc = IOError("Failed to read authentication cookie: %s" % exc)
+
+ # if we got to this point then we failed to authenticate and should have a
+ # raisedExc
+
+ if raisedExc: raise raisedExc
+
+def preauth_connect_alt(controlAddr="127.0.0.1", controlPort=9051,
+ ConnClass=FixedConnection):
+ """
+ Provides an uninitiated torctl connection components for the control port,
+ returning a tuple of the form...
+ (torctl connection, authTypes, authValue)
+
+ The authValue corresponds to the cookie path if using an authentication
+ cookie, otherwise this is the empty string. This raises an IOError in case
+ of failure.
+
+ Arguments:
+ controlAddr - ip address belonging to the controller
+ controlPort - port belonging to the controller
+ ConnClass - connection type to instantiate
+ """
+
+ conn = None
+ try:
+ s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
+ s.connect((controlAddr, controlPort))
+ conn = ConnClass(s)
+ authTypes, authValue = conn.get_auth_types(), ""
+
+ if TorCtl.AUTH_TYPE.COOKIE in authTypes:
+ authValue = conn.get_auth_cookie_path()
+
+ return (conn, authTypes, authValue)
+ except socket.error, exc:
+ if conn: conn.close()
+
+ if "Connection refused" in exc.args:
+ # most common case - tor control port isn't available
+ raise IOError("Connection refused. Is the ControlPort enabled?")
+
+ raise IOError("Failed to establish socket: %s" % exc)
+ except Exception, exc:
+ if conn: conn.close()
+ raise IOError(exc)
+
+# ============================================================
+
def getConn():
"""
Singleton constructor for a Controller. Be aware that this starts as being
More information about the tor-commits
mailing list