[Author Prev][Author Next][Thread Prev][Thread Next][Author Index][Thread Index]
[tor-commits] [arm/master] Helper function for authenticating to the tor controller
commit b86a709cdba4c57f5e5fa9418f6e795dcfa8c63b
Author: Damian Johnson <atagar@xxxxxxxxxxxxxx>
Date: Sat Sep 14 13:15:57 2013 -0700
Helper function for authenticating to the tor controller
More refactoring for arm's starter module. This moves authentication into a
helper method, greatly expanding the error output for users and adds unit
tests.
---
arm/settings.cfg | 28 +++++++++++++++++++
arm/starter.py | 79 +++++++++++++++++++++++++++++++++++++-----------------
test/starter.py | 74 +++++++++++++++++++++++++++++++++++++++++++++++++-
3 files changed, 155 insertions(+), 26 deletions(-)
diff --git a/arm/settings.cfg b/arm/settings.cfg
index c73a16f..368d418 100644
--- a/arm/settings.cfg
+++ b/arm/settings.cfg
@@ -18,6 +18,34 @@ msg.help
|arm -b -i 1643 hide connection data, attaching to control port 1643
|arm -e we -c /tmp/cfg use this configuration file with 'WARN'/'ERR' events
+msg.wrong_port_type
+|Please check in your torrc that %i is the ControlPort. Maybe you configured
+|it to be the ORPort or SocksPort instead?
+
+msg.wrong_socket_type
+|Unable to connect to tor. Are you sure the interface you specified belongs to
+|tor?
+
+msg.uncrcognized_auth_type
+|Tor is using a type of authentication we do not recognize...
+|
+| %s
+|
+|Please check that arm is up to date and if there is an existing issue on
+|'http://bugs.torproject.org'. If there isn't one then let us know!
+
+msg.missing_password_bug
+|BUG: You provided a password but despite this stem reported that it was
+|missing. This shouldn't happen - please let us know about it!
+|
+| http://bugs.torproject.org
+
+msg.unreadable_cookie_file
+|We were unable to read tor's authentication cookie...
+|
+| Path: %s
+| Issue: %s
+
# Important tor configuration options (shown by default)
config.important BandwidthRate
config.important BandwidthBurst
diff --git a/arm/starter.py b/arm/starter.py
index 057fb86..fa2289d 100644
--- a/arm/starter.py
+++ b/arm/starter.py
@@ -8,13 +8,13 @@ command line parameters.
import collections
import getopt
+import getpass
import os
import sys
import stem.util.connection
import time
-import getpass
import locale
import logging
import platform
@@ -42,6 +42,11 @@ CONFIG = stem.util.conf.config_dict("arm", {
"startup.blindModeEnabled": False,
"startup.events": "N3",
"msg.help": "",
+ "msg.wrong_port_type": "",
+ "msg.wrong_socket_type": "",
+ "msg.uncrcognized_auth_type": "",
+ "msg.missing_password_bug": "",
+ "msg.unreadable_cookie_file": "",
})
# notices given if the user is running arm or tor as root
@@ -80,6 +85,16 @@ ARGS = {
OPT = "gi:s:c:dbe:vh"
OPT_EXPANDED = ["interface=", "socket=", "config=", "debug", "blind", "event=", "version", "help"]
+try:
+ pathPrefix = os.path.dirname(sys.argv[0])
+ if pathPrefix and not pathPrefix.endswith("/"):
+ pathPrefix = pathPrefix + "/"
+
+ config = stem.util.conf.get_config("arm")
+ config.load("%sarm/settings.cfg" % pathPrefix)
+except IOError, exc:
+ stem.util.log.warn(NO_INTERNAL_CFG_MSG % arm.util.sysTools.getFileErrorMsg(exc))
+
def _get_args(argv):
"""
@@ -168,6 +183,43 @@ def _get_controller(args):
raise ValueError("Unable to connect to tor. Maybe it's running without a ControlPort?")
+def _authenticate(controller, password):
+ """
+ Authenticates to the given Controller.
+
+ :param stem.control.Controller controller: controller to be authenticated to
+ :param str args: password to authenticate with, **None** if nothing was provided
+
+ :raises: **ValueError** if unable to authenticate
+ """
+
+ chroot = arm.util.torTools.get_chroot()
+
+ try:
+ controller.authenticate(password = password, chroot_path = chroot)
+ except stem.connection.IncorrectSocketType:
+ control_socket = controller.get_socket()
+
+ if isinstance(control_socket, stem.socket.ControlPort):
+ raise ValueError(CONFIG['msg.wrong_port_type'] % control_socket.get_port())
+ else:
+ raise ValueError(CONFIG['msg.wrong_socket_type'])
+ except stem.connection.UnrecognizedAuthMethods as exc:
+ raise ValueError(CONFIG['msg.uncrcognized_auth_type'] % ', '.join(exc.unknown_auth_methods))
+ except stem.connection.IncorrectPassword:
+ raise ValueError("Incorrect password")
+ except stem.connection.MissingPassword:
+ if password:
+ raise ValueError(CONFIG['msg.missing_password_bug'])
+
+ password = getpass.getpass("Tor controller password: ")
+ return _authenticate(controller, password)
+ except stem.connection.UnreadableCookieFile as exc:
+ raise ValueError(CONFIG['msg.unreadable_cookie_file'] % (exc.cookie_path, str(exc)))
+ except stem.connection.AuthenticationFailure as exc:
+ raise ValueError("Unable to authenticate: %s" % exc)
+
+
def _dumpConfig():
"""
Dumps the current arm and tor configurations at the DEBUG runlevel. This
@@ -222,11 +274,6 @@ def main():
pathPrefix = pathPrefix + "/"
try:
- config.load("%sarm/settings.cfg" % pathPrefix)
- except IOError, exc:
- stem.util.log.warn(NO_INTERNAL_CFG_MSG % arm.util.sysTools.getFileErrorMsg(exc))
-
- try:
args = _get_args(sys.argv[1:])
except getopt.GetoptError as exc:
print "%s (for usage provide --help)" % exc
@@ -289,29 +336,11 @@ def main():
try:
controller = _get_controller(args)
+ _authenticate(controller, CONFIG['tor.password'])
except ValueError as exc:
print exc
exit(1)
- chroot = arm.util.torTools.get_chroot()
-
- try:
- controller.authenticate(password = CONFIG["tor.password"], chroot_path = chroot)
- except (stem.connection.MissingPassword, stem.connection.IncorrectPassword) as exc:
- if isinstance(stem.connection.IncorrectPassword, exc):
- print "Password found in '%s' was incorrect" % args.config
-
- try:
- passphrase = getpass.getpass("Controller password: ")
- controller.authenticate(password = passphrase, chroot_path = chroot)
- del passphrase # removing reference as early as possible to free memory
- except stem.connection.IncorrectPassword:
- print "Incorrect password"
- sys.exit(1)
- except stem.connection.AuthenticationFailure as exc:
- print "Unable to authenticate: %s" % exc
- sys.exit(1)
-
# Removing references to the controller password so the memory can be
# freed. Without direct memory access this is about the best we can do.
diff --git a/test/starter.py b/test/starter.py
index f8c4f0c..d1c90cf 100644
--- a/test/starter.py
+++ b/test/starter.py
@@ -7,9 +7,16 @@ import unittest
from mock import Mock, patch
-from arm.starter import _get_args, _get_controller, ARGS
+from arm.starter import (
+ _get_args,
+ _get_controller,
+ _authenticate,
+ ARGS,
+)
import stem
+import stem.connection
+import stem.socket
class TestArgumentParsing(unittest.TestCase):
def test_that_we_get_default_values(self):
@@ -127,3 +134,68 @@ class TestGetController(unittest.TestCase):
self.fail()
except ValueError, exc:
self.assertEqual(msg, str(exc))
+
+class TestAuthenticate(unittest.TestCase):
+ @patch('arm.util.torTools.get_chroot')
+ def test_success(self, get_chroot_mock):
+ controller = Mock()
+
+ get_chroot_mock.return_value = '' # no chroot
+ _authenticate(controller, None)
+ controller.authenticate.assert_called_with(password = None, chroot_path = '')
+ controller.authenticate.reset_mock()
+
+ get_chroot_mock.return_value = '/my/chroot'
+ _authenticate(controller, 's3krit!!!')
+ controller.authenticate.assert_called_with(password = 's3krit!!!', chroot_path = '/my/chroot')
+
+ @patch('arm.util.torTools.get_chroot', Mock(return_value = ''))
+ @patch('getpass.getpass')
+ def test_success_with_password_prompt(self, getpass_mock):
+ controller = Mock()
+
+ def authenticate_mock(password, **kwargs):
+ if password is None:
+ raise stem.connection.MissingPassword('no password')
+ elif password == 'my_password':
+ return None # success
+ else:
+ raise ValueError("Unexpected authenticate_mock input: %s" % password)
+
+ controller.authenticate.side_effect = authenticate_mock
+ getpass_mock.return_value = 'my_password'
+
+ _authenticate(controller, None)
+ controller.authenticate.assert_any_call(password = None, chroot_path = '')
+ controller.authenticate.assert_any_call(password = 'my_password', chroot_path = '')
+
+ @patch('arm.util.torTools.get_chroot', Mock(return_value = ''))
+ def test_failure(self):
+ controller = Mock()
+
+ controller.authenticate.side_effect = stem.connection.IncorrectSocketType('unable to connect to socket')
+ controller.get_socket.return_value = stem.socket.ControlPort(connect = False)
+ self._assert_authenticate_fails_with(controller, 'Please check in your torrc that 9051 is the ControlPort.')
+
+ controller.get_socket.return_value = stem.socket.ControlSocketFile(connect = False)
+ self._assert_authenticate_fails_with(controller, 'Are you sure the interface you specified belongs to')
+
+ controller.authenticate.side_effect = stem.connection.UnrecognizedAuthMethods('unable to connect', ['telepathy'])
+ self._assert_authenticate_fails_with(controller, 'Tor is using a type of authentication we do not recognize...\n\n telepathy')
+
+ controller.authenticate.side_effect = stem.connection.IncorrectPassword('password rejected')
+ self._assert_authenticate_fails_with(controller, 'Incorrect password')
+
+ controller.authenticate.side_effect = stem.connection.UnreadableCookieFile('permission denied', '/tmp/my_cookie', False)
+ self._assert_authenticate_fails_with(controller, "We were unable to read tor's authentication cookie...\n\n Path: /tmp/my_cookie\n Issue: permission denied")
+
+ controller.authenticate.side_effect = stem.connection.OpenAuthRejected('crazy failure')
+ self._assert_authenticate_fails_with(controller, 'Unable to authenticate: crazy failure')
+
+ def _assert_authenticate_fails_with(self, controller, msg):
+ try:
+ _get_controller(_authenticate(controller, None))
+ self.fail()
+ except ValueError, exc:
+ if not msg in str(exc):
+ self.fail("Expected...\n\n%s\n\n... which couldn't be found in...\n\n%s" % (msg, exc))
_______________________________________________
tor-commits mailing list
tor-commits@xxxxxxxxxxxxxxxxxxxx
https://lists.torproject.org/cgi-bin/mailman/listinfo/tor-commits