def test_valid_string_unescaping(self): unescapeable = { '\\\\': '\\', # \\ -> \ r'\"': r'"', # \" -> " r'\\\"': r'\"', # \\\" -> \" r'\\\\\"': r'\\"', # \\\\\" -> \\" '\\"\\\\': '"\\', # \"\\ -> "\ "\\'": "'", # \' -> ' "\\\\\\'": "\\'", # \\\' -> \ r'some\"text': 'some"text', 'some\\word': 'someword', '\\delete\\ al\\l un\\used \\backslashes': 'delete all unused backslashes', '\\n\\r\\t': '\n\r\t', '\\x00 \\x0123': 'x00 x0123', '\\\\x00 \\\\x00': '\\x00 \\x00', '\\\\\\x00 \\\\\\x00': '\\x00 \\x00' } for escaped, correct_unescaped in unescapeable.items(): escaped = '"{}"'.format(escaped) unescaped = unescape_quoted_string(escaped) msg = "Wrong unescape: {escaped} -> {unescaped} instead of {correct}" msg = msg.format(unescaped=unescaped, escaped=escaped, correct=correct_unescaped) self.assertEqual(unescaped, correct_unescaped, msg=msg)
def test_string_unescape_octals(self): ''' Octal numbers can be escaped by a backslash: \0 is interpreted as a byte with the value 0 ''' for number in range(0x7f): escaped = '\\%o' % number result = unescape_quoted_string('"{}"'.format(escaped)) expected = chr(number) msg = "Number not decoded correctly: {escaped} -> {result} instead of {expected}" msg = msg.format(escaped=escaped, result=repr(result), expected=repr(expected)) self.assertEqual(result, expected, msg=msg)
def test_string_unescape_octals(self): ''' Octal numbers can be escaped by a backslash: \0 is interpreted as a byte with the value 0 ''' for number in range(1000): escaped = '\\{}'.format(number) result = unescape_quoted_string('"{}"'.format(escaped)) expected = escaped.decode('string-escape') if expected[0] == '\\' and len(expected) > 1: expected = expected[1:] msg = "Number not decoded correctly: {escaped} -> {result} instead of {expected}" msg = msg.format(escaped=escaped, result=repr(result), expected=repr(expected)) self.assertEquals(result, expected, msg=msg)
def _do_authenticate(self, protoinfo): """ Callback on PROTOCOLINFO to actually authenticate once we know what's supported. """ methods = None cookie_auth = False for line in protoinfo.split('\n'): if line[:5] == 'AUTH ': kw = parse_keywords(line[5:].replace(' ', '\n')) methods = kw['METHODS'].split(',') if not methods: raise RuntimeError( "Didn't find AUTH line in PROTOCOLINFO response." ) if 'SAFECOOKIE' in methods or 'COOKIE' in methods: cookiefile_match = re.search(r'COOKIEFILE=("(?:[^"\\]|\\.)*")', protoinfo) if cookiefile_match: cookiefile = cookiefile_match.group(1) cookiefile = unescape_quoted_string(cookiefile) try: self._read_cookie(cookiefile) except IOError as why: txtorlog.msg("Reading COOKIEFILE failed: " + str(why)) cookie_auth = False else: cookie_auth = True else: txtorlog.msg("Didn't get COOKIEFILE") if cookie_auth: if 'SAFECOOKIE' in methods: txtorlog.msg("Using SAFECOOKIE authentication", cookiefile, len(self._cookie_data), "bytes") self.client_nonce = os.urandom(32) cmd = 'AUTHCHALLENGE SAFECOOKIE ' + \ base64.b16encode(self.client_nonce) d = self.queue_command(cmd) d.addCallback(self._safecookie_authchallenge) d.addCallback(self._bootstrap) d.addErrback(self._auth_failed) return elif 'COOKIE' in methods: txtorlog.msg("Using COOKIE authentication", cookiefile, len(self._cookie_data), "bytes") d = self.authenticate(self._cookie_data) d.addCallback(self._bootstrap) d.addErrback(self._auth_failed) return if self.password_function and 'HASHEDPASSWORD' in methods: d = defer.maybeDeferred(self.password_function) d.addCallback(self._do_password_authentication) d.addErrback(self._auth_failed) return if 'NULL' in methods: d = self.queue_command('AUTHENTICATE') d.addCallback(self._bootstrap) d.addErrback(self._auth_failed) return raise RuntimeError( "The Tor I connected to doesn't support SAFECOOKIE nor COOKIE" " authentication and I have no password_function specified." )
def _do_authenticate(self, protoinfo): """ Callback on PROTOCOLINFO to actually authenticate once we know what's supported. """ methods = None cookie_auth = False for line in protoinfo.split('\n'): if line[:5] == 'AUTH ': kw = parse_keywords(line[5:].replace(' ', '\n')) methods = kw['METHODS'].split(',') if not methods: raise RuntimeError( "Didn't find AUTH line in PROTOCOLINFO response.") if 'SAFECOOKIE' in methods or 'COOKIE' in methods: cookiefile_match = re.search(r'COOKIEFILE=("(?:[^"\\]|\\.)*")', protoinfo) if cookiefile_match: cookiefile = cookiefile_match.group(1) cookiefile = unescape_quoted_string(cookiefile) try: self._read_cookie(cookiefile) except IOError as why: txtorlog.msg("Reading COOKIEFILE failed: " + str(why)) cookie_auth = False else: cookie_auth = True else: txtorlog.msg("Didn't get COOKIEFILE") if cookie_auth: if 'SAFECOOKIE' in methods: txtorlog.msg("Using SAFECOOKIE authentication", cookiefile, len(self._cookie_data), "bytes") self.client_nonce = os.urandom(32) cmd = 'AUTHCHALLENGE SAFECOOKIE ' + \ base64.b16encode(self.client_nonce) d = self.queue_command(cmd) d.addCallback(self._safecookie_authchallenge) d.addCallback(self._bootstrap) d.addErrback(self._auth_failed) return elif 'COOKIE' in methods: txtorlog.msg("Using COOKIE authentication", cookiefile, len(self._cookie_data), "bytes") d = self.authenticate(self._cookie_data) d.addCallback(self._bootstrap) d.addErrback(self._auth_failed) return if self.password_function and 'HASHEDPASSWORD' in methods: d = defer.maybeDeferred(self.password_function) d.addCallback(self._do_password_authentication) d.addErrback(self._auth_failed) return raise RuntimeError( "The Tor I connected to doesn't support SAFECOOKIE nor COOKIE" " authentication and I have no password_function specified.")
def _do_authenticate(self, protoinfo): """ Callback on PROTOCOLINFO to actually authenticate once we know what's supported. """ methods = None cookie_auth = False for line in protoinfo.split('\n'): if line[:5] == 'AUTH ': kw = parse_keywords(line[5:].replace(' ', '\n')) methods = kw['METHODS'].split(',') if not methods: raise RuntimeError( "Didn't find AUTH line in PROTOCOLINFO response.") if 'SAFECOOKIE' in methods or 'COOKIE' in methods: cookiefile_match = re.search(r'COOKIEFILE=("(?:[^"\\]|\\.)*")', protoinfo) if cookiefile_match: cookiefile = cookiefile_match.group(1) cookiefile = unescape_quoted_string(cookiefile) try: self._read_cookie(cookiefile) cookie_auth = True except IOError as why: txtorlog.msg("Reading COOKIEFILE failed: " + str(why)) if self.password_function and 'HASHEDPASSWORD' in methods: txtorlog.msg("Falling back to password") else: raise RuntimeError( "Failed to read COOKIEFILE '{fname}': {msg}\n". format( fname=cookiefile, msg=str(why), ) # "On Debian, join the debian-tor group" ) else: txtorlog.msg("Didn't get COOKIEFILE") raise RuntimeError( "Got 'COOKIE' or 'SAFECOOKIE' method, but no 'COOKIEFILE'") if cookie_auth: if 'SAFECOOKIE' in methods: txtorlog.msg("Using SAFECOOKIE authentication", cookiefile, len(self._cookie_data), "bytes") self.client_nonce = os.urandom(32) cmd = b'AUTHCHALLENGE SAFECOOKIE ' + \ hexlify(self.client_nonce) d = self.queue_command(cmd) d.addCallback(self._safecookie_authchallenge) d.addCallback(self._bootstrap) return d elif 'COOKIE' in methods: txtorlog.msg("Using COOKIE authentication", cookiefile, len(self._cookie_data), "bytes") d = self.authenticate(self._cookie_data) d.addCallback(self._bootstrap) return d if self.password_function and 'HASHEDPASSWORD' in methods: d = defer.maybeDeferred(self.password_function) d.addCallback(maybe_coroutine) d.addCallback(self._do_password_authentication) return d if 'NULL' in methods: d = self.queue_command('AUTHENTICATE') d.addCallback(self._bootstrap) return d return defer.fail( RuntimeError( "The Tor I connected to doesn't support SAFECOOKIE nor COOKIE" " authentication (or we can't read the cookie files) and I have" " no password_function specified."))