diff options
Diffstat (limited to 'paramiko/transport.py')
-rw-r--r-- | paramiko/transport.py | 165 |
1 files changed, 84 insertions, 81 deletions
diff --git a/paramiko/transport.py b/paramiko/transport.py index 092ab1c6..1471b543 100644 --- a/paramiko/transport.py +++ b/paramiko/transport.py @@ -20,10 +20,7 @@ Core protocol implementation """ -import os import socket -import string -import struct import sys import threading import time @@ -33,7 +30,17 @@ import paramiko from paramiko import util from paramiko.auth_handler import AuthHandler from paramiko.channel import Channel -from paramiko.common import * # Legit, uses dozens of constants & funcs +from paramiko.common import rng, xffffffff, cMSG_CHANNEL_OPEN, cMSG_IGNORE, \ + cMSG_GLOBAL_REQUEST, DEBUG, MSG_KEXINIT, MSG_IGNORE, MSG_DISCONNECT, \ + MSG_DEBUG, ERROR, WARNING, cMSG_UNIMPLEMENTED, INFO, cMSG_KEXINIT, \ + cMSG_NEWKEYS, MSG_NEWKEYS, cMSG_REQUEST_SUCCESS, cMSG_REQUEST_FAILURE, \ + CONNECTION_FAILED_CODE, OPEN_FAILED_ADMINISTRATIVELY_PROHIBITED, \ + OPEN_SUCCEEDED, cMSG_CHANNEL_OPEN_FAILURE, cMSG_CHANNEL_OPEN_SUCCESS, \ + MSG_GLOBAL_REQUEST, MSG_REQUEST_SUCCESS, MSG_REQUEST_FAILURE, \ + MSG_CHANNEL_OPEN_SUCCESS, MSG_CHANNEL_OPEN_FAILURE, MSG_CHANNEL_OPEN, \ + MSG_CHANNEL_SUCCESS, MSG_CHANNEL_FAILURE, MSG_CHANNEL_DATA, \ + MSG_CHANNEL_EXTENDED_DATA, MSG_CHANNEL_WINDOW_ADJUST, MSG_CHANNEL_REQUEST, \ + MSG_CHANNEL_EOF, MSG_CHANNEL_CLOSE from paramiko.compress import ZlibCompressor, ZlibDecompressor from paramiko.dsskey import DSSKey from paramiko.kex_gex import KexGex @@ -41,12 +48,13 @@ from paramiko.kex_group1 import KexGroup1 from paramiko.message import Message from paramiko.packet import Packetizer, NeedRekeyException from paramiko.primes import ModulusPack +from paramiko.py3compat import string_types, long, byte_ord, b from paramiko.rsakey import RSAKey from paramiko.ecdsakey import ECDSAKey from paramiko.server import ServerInterface from paramiko.sftp_client import SFTPClient from paramiko.ssh_exception import (SSHException, BadAuthenticationType, - ChannelException, ProxyCommandFailure) + ChannelException, ProxyCommandFailure) from paramiko.util import retry_on_signal from Crypto import Random @@ -60,9 +68,11 @@ except ImportError: # for thread cleanup _active_threads = [] + def _join_lingering_threads(): for thr in _active_threads: thr.stop_thread() + import atexit atexit.register(_join_lingering_threads) @@ -76,54 +86,53 @@ class Transport (threading.Thread): forwardings). """ _PROTO_ID = '2.0' - _CLIENT_ID = 'paramiko_%s' % (paramiko.__version__) + _CLIENT_ID = 'paramiko_%s' % paramiko.__version__ - _preferred_ciphers = ( 'aes128-ctr', 'aes256-ctr', 'aes128-cbc', 'blowfish-cbc', 'aes256-cbc', '3des-cbc', - 'arcfour128', 'arcfour256' ) - _preferred_macs = ( 'hmac-sha1', 'hmac-md5', 'hmac-sha1-96', 'hmac-md5-96' ) - _preferred_keys = ( 'ssh-rsa', 'ssh-dss', 'ecdsa-sha2-nistp256' ) - _preferred_kex = ( 'diffie-hellman-group1-sha1', 'diffie-hellman-group-exchange-sha1' ) - _preferred_compression = ( 'none', ) + _preferred_ciphers = ('aes128-ctr', 'aes256-ctr', 'aes128-cbc', 'blowfish-cbc', + 'aes256-cbc', '3des-cbc', 'arcfour128', 'arcfour256') + _preferred_macs = ('hmac-sha1', 'hmac-md5', 'hmac-sha1-96', 'hmac-md5-96') + _preferred_keys = ('ssh-rsa', 'ssh-dss', 'ecdsa-sha2-nistp256') + _preferred_kex = ('diffie-hellman-group1-sha1', 'diffie-hellman-group-exchange-sha1') + _preferred_compression = ('none',) _cipher_info = { - 'aes128-ctr': { 'class': AES, 'mode': AES.MODE_CTR, 'block-size': 16, 'key-size': 16 }, - 'aes256-ctr': { 'class': AES, 'mode': AES.MODE_CTR, 'block-size': 16, 'key-size': 32 }, - 'blowfish-cbc': { 'class': Blowfish, 'mode': Blowfish.MODE_CBC, 'block-size': 8, 'key-size': 16 }, - 'aes128-cbc': { 'class': AES, 'mode': AES.MODE_CBC, 'block-size': 16, 'key-size': 16 }, - 'aes256-cbc': { 'class': AES, 'mode': AES.MODE_CBC, 'block-size': 16, 'key-size': 32 }, - '3des-cbc': { 'class': DES3, 'mode': DES3.MODE_CBC, 'block-size': 8, 'key-size': 24 }, - 'arcfour128': { 'class': ARC4, 'mode': None, 'block-size': 8, 'key-size': 16 }, - 'arcfour256': { 'class': ARC4, 'mode': None, 'block-size': 8, 'key-size': 32 }, - } + 'aes128-ctr': {'class': AES, 'mode': AES.MODE_CTR, 'block-size': 16, 'key-size': 16}, + 'aes256-ctr': {'class': AES, 'mode': AES.MODE_CTR, 'block-size': 16, 'key-size': 32}, + 'blowfish-cbc': {'class': Blowfish, 'mode': Blowfish.MODE_CBC, 'block-size': 8, 'key-size': 16}, + 'aes128-cbc': {'class': AES, 'mode': AES.MODE_CBC, 'block-size': 16, 'key-size': 16}, + 'aes256-cbc': {'class': AES, 'mode': AES.MODE_CBC, 'block-size': 16, 'key-size': 32}, + '3des-cbc': {'class': DES3, 'mode': DES3.MODE_CBC, 'block-size': 8, 'key-size': 24}, + 'arcfour128': {'class': ARC4, 'mode': None, 'block-size': 8, 'key-size': 16}, + 'arcfour256': {'class': ARC4, 'mode': None, 'block-size': 8, 'key-size': 32}, + } _mac_info = { - 'hmac-sha1': { 'class': SHA, 'size': 20 }, - 'hmac-sha1-96': { 'class': SHA, 'size': 12 }, - 'hmac-md5': { 'class': MD5, 'size': 16 }, - 'hmac-md5-96': { 'class': MD5, 'size': 12 }, - } + 'hmac-sha1': {'class': SHA, 'size': 20}, + 'hmac-sha1-96': {'class': SHA, 'size': 12}, + 'hmac-md5': {'class': MD5, 'size': 16}, + 'hmac-md5-96': {'class': MD5, 'size': 12}, + } _key_info = { 'ssh-rsa': RSAKey, 'ssh-dss': DSSKey, 'ecdsa-sha2-nistp256': ECDSAKey, - } + } _kex_info = { 'diffie-hellman-group1-sha1': KexGroup1, 'diffie-hellman-group-exchange-sha1': KexGex, - } + } _compression_info = { # zlib@openssh.com is just zlib, but only turned on after a successful # authentication. openssh servers may only offer this type because # they've had troubles with security holes in zlib in the past. - 'zlib@openssh.com': ( ZlibCompressor, ZlibDecompressor ), - 'zlib': ( ZlibCompressor, ZlibDecompressor ), - 'none': ( None, None ), + 'zlib@openssh.com': (ZlibCompressor, ZlibDecompressor), + 'zlib': (ZlibCompressor, ZlibDecompressor), + 'none': (None, None), } - _modulus_pack = None def __init__(self, sock): @@ -220,8 +229,8 @@ class Transport (threading.Thread): # tracking open channels self._channels = ChannelMap() - self.channel_events = { } # (id -> Event) - self.channels_seen = { } # (id -> True) + self.channel_events = {} # (id -> Event) + self.channels_seen = {} # (id -> True) self._channel_counter = 1 self.window_size = 65536 self.max_packet_size = 34816 @@ -244,10 +253,10 @@ class Transport (threading.Thread): # server mode: self.server_mode = False self.server_object = None - self.server_key_dict = { } - self.server_accepts = [ ] + self.server_key_dict = {} + self.server_accepts = [] self.server_accept_cv = threading.Condition(self.lock) - self.subsystem_table = { } + self.subsystem_table = {} def __repr__(self): """ @@ -468,7 +477,7 @@ class Transport (threading.Thread): """ Transport._modulus_pack = ModulusPack(rng) # places to look for the openssh "moduli" file - file_list = [ '/etc/ssh/moduli', '/usr/local/etc/moduli' ] + file_list = ['/etc/ssh/moduli', '/usr/local/etc/moduli'] if filename is not None: file_list.insert(0, filename) for fn in file_list: @@ -623,7 +632,7 @@ class Transport (threading.Thread): self.lock.release() self._send_user_message(m) while True: - event.wait(0.1); + event.wait(0.1) if not self.active: e = self.get_exception() if e is None: @@ -764,7 +773,7 @@ class Transport (threading.Thread): 0 to disable keepalives). """ self.packetizer.set_keepalive(interval, - lambda x=weakref.proxy(self): x.global_request('keepalive@lag.net', wait=False)) + lambda x=weakref.proxy(self): x.global_request('keepalive@lag.net', wait=False)) def global_request(self, kind, data=None, wait=True): """ @@ -863,12 +872,12 @@ class Transport (threading.Thread): supplied by the server is incorrect, or authentication fails. """ if hostkey is not None: - self._preferred_keys = [ hostkey.get_name() ] + self._preferred_keys = [hostkey.get_name()] self.start_client() # check host key if we were given one - if (hostkey is not None): + if hostkey is not None: key = self.get_remote_server_key() if (key.get_name() != hostkey.get_name()) or (key.asbytes() != hostkey.asbytes()): self._log(DEBUG, 'Bad host key from server') @@ -1061,12 +1070,11 @@ class Transport (threading.Thread): # to try to fake out automated scripting of the exact # type we're doing here. *shrug* :) return [] - return [ password ] + return [password] return self.auth_interactive(username, handler) except SSHException: # attempt failed; just raise the original exception raise e - return None def auth_publickey(self, username, key, event=None): """ @@ -1227,9 +1235,9 @@ class Transport (threading.Thread): .. versionadded:: 1.5.2 """ if compress: - self._preferred_compression = ( 'zlib@openssh.com', 'zlib', 'none' ) + self._preferred_compression = ('zlib@openssh.com', 'zlib', 'none') else: - self._preferred_compression = ( 'none', ) + self._preferred_compression = ('none',) def getpeername(self): """ @@ -1244,7 +1252,7 @@ class Transport (threading.Thread): """ gp = getattr(self.sock, 'getpeername', None) if gp is None: - return ('unknown', 0) + return 'unknown', 0 return gp() def stop_thread(self): @@ -1253,10 +1261,8 @@ class Transport (threading.Thread): while self.isAlive(): self.join(10) - ### internals... - def _log(self, level, msg, *args): if issubclass(type(msg), list): for m in msg: @@ -1265,11 +1271,11 @@ class Transport (threading.Thread): self.logger.log(level, msg, *args) def _get_modulus_pack(self): - "used by KexGex to find primes for group exchange" + """used by KexGex to find primes for group exchange""" return self._modulus_pack def _next_channel(self): - "you are holding the lock" + """you are holding the lock""" chanid = self._channel_counter while self._channels.get(chanid) is not None: self._channel_counter = (self._channel_counter + 1) & 0xffffff @@ -1278,7 +1284,7 @@ class Transport (threading.Thread): return chanid def _unlink_channel(self, chanid): - "used by a Channel to remove itself from the active channel list" + """used by a Channel to remove itself from the active channel list""" self._channels.delete(chanid) def _send_message(self, data): @@ -1307,14 +1313,14 @@ class Transport (threading.Thread): self.clear_to_send_lock.release() def _set_K_H(self, k, h): - "used by a kex object to set the K (root key) and H (exchange hash)" + """used by a kex object to set the K (root key) and H (exchange hash)""" self.K = k self.H = h - if self.session_id == None: + if self.session_id is None: self.session_id = h def _expect_packet(self, *ptypes): - "used by a kex object to register the next packet type it expects to see" + """used by a kex object to register the next packet type it expects to see""" self._expected_packet = tuple(ptypes) def _verify_key(self, host_key, sig): @@ -1326,7 +1332,7 @@ class Transport (threading.Thread): self.host_key = key def _compute_key(self, id, nbytes): - "id is 'A' - 'F' for the various keys used by ssh" + """id is 'A' - 'F' for the various keys used by ssh""" m = Message() m.add_mpint(self.K) m.add_bytes(self.H) @@ -1471,7 +1477,7 @@ class Transport (threading.Thread): if type(e.args) is tuple: if e.args: emsg = '%s (%d)' % (e.args[1], e.args[0]) - else: # empty tuple, e.g. socket.timeout + else: # empty tuple, e.g. socket.timeout emsg = str(e) or repr(e) else: emsg = e.args @@ -1487,7 +1493,7 @@ class Transport (threading.Thread): if self.active: self.active = False self.packetizer.close() - if self.completion_event != None: + if self.completion_event is not None: self.completion_event.set() if self.auth_handler is not None: self.auth_handler.abort() @@ -1507,10 +1513,8 @@ class Transport (threading.Thread): if self.sys.modules is not None: raise - ### protocol stages - def _negotiate_keys(self, m): # throws SSHException on anything unusual self.clear_to_send_lock.acquire() @@ -1518,7 +1522,7 @@ class Transport (threading.Thread): self.clear_to_send.clear() finally: self.clear_to_send_lock.release() - if self.local_kex_init == None: + if self.local_kex_init is None: # remote side wants to renegotiate self._send_kex_init() self._parse_kex_init(m) @@ -1580,7 +1584,7 @@ class Transport (threading.Thread): pkex.remove('diffie-hellman-group-exchange-sha1') self.get_security_options().kex = pkex available_server_keys = list(filter(list(self.server_key_dict.keys()).__contains__, - self._preferred_keys)) + self._preferred_keys)) else: available_server_keys = self._preferred_keys @@ -1618,15 +1622,15 @@ class Transport (threading.Thread): kex_follows = m.get_boolean() unused = m.get_int() - self._log(DEBUG, 'kex algos:' + str(kex_algo_list) + ' server key:' + str(server_key_algo_list) + \ - ' client encrypt:' + str(client_encrypt_algo_list) + \ - ' server encrypt:' + str(server_encrypt_algo_list) + \ - ' client mac:' + str(client_mac_algo_list) + \ - ' server mac:' + str(server_mac_algo_list) + \ - ' client compress:' + str(client_compress_algo_list) + \ - ' server compress:' + str(server_compress_algo_list) + \ - ' client lang:' + str(client_lang_list) + \ - ' server lang:' + str(server_lang_list) + \ + self._log(DEBUG, 'kex algos:' + str(kex_algo_list) + ' server key:' + str(server_key_algo_list) + + ' client encrypt:' + str(client_encrypt_algo_list) + + ' server encrypt:' + str(server_encrypt_algo_list) + + ' client mac:' + str(client_mac_algo_list) + + ' server mac:' + str(server_mac_algo_list) + + ' client compress:' + str(client_compress_algo_list) + + ' server compress:' + str(server_compress_algo_list) + + ' client lang:' + str(client_lang_list) + + ' server lang:' + str(server_lang_list) + ' kex follows?' + str(kex_follows)) # as a server, we pick the first item in the client's list that we support. @@ -1641,7 +1645,7 @@ class Transport (threading.Thread): if self.server_mode: available_server_keys = list(filter(list(self.server_key_dict.keys()).__contains__, - self._preferred_keys)) + self._preferred_keys)) agreed_keys = list(filter(available_server_keys.__contains__, server_key_algo_list)) else: agreed_keys = list(filter(server_key_algo_list.__contains__, self._preferred_keys)) @@ -1701,7 +1705,7 @@ class Transport (threading.Thread): self.remote_kex_init = cMSG_KEXINIT + m.get_so_far() def _activate_inbound(self): - "switch on newly negotiated encryption parameters for inbound traffic" + """switch on newly negotiated encryption parameters for inbound traffic""" block_size = self._cipher_info[self.remote_cipher]['block-size'] if self.server_mode: IV_in = self._compute_key('A', block_size) @@ -1725,7 +1729,7 @@ class Transport (threading.Thread): self.packetizer.set_inbound_compressor(compress_in()) def _activate_outbound(self): - "switch on newly negotiated encryption parameters for outbound traffic" + """switch on newly negotiated encryption parameters for outbound traffic""" m = Message() m.add_byte(cMSG_NEWKEYS) self._send_message(m) @@ -1782,7 +1786,7 @@ class Transport (threading.Thread): # this was the first key exchange self.initial_kex_done = True # send an event? - if self.completion_event != None: + if self.completion_event is not None: self.completion_event.set() # it's now okay to send data again (if this was a re-key) if not self.packetizer.need_rekey(): @@ -1810,7 +1814,7 @@ class Transport (threading.Thread): address = m.get_text() port = m.get_int() ok = self.server_object.check_port_forward_request(address, port) - if ok != False: + if ok: ok = (ok,) elif kind == 'cancel-tcpip-forward': address = m.get_text() @@ -1933,8 +1937,7 @@ class Transport (threading.Thread): origin_addr = m.get_text() origin_port = m.get_int() reason = self.server_object.check_channel_direct_tcpip_request( - my_chanid, (origin_addr, origin_port), - (dest_addr, dest_port)) + my_chanid, (origin_addr, origin_port), (dest_addr, dest_port)) else: reason = self.server_object.check_channel_request(kind, my_chanid) if reason != OPEN_SUCCEEDED: @@ -1988,7 +1991,7 @@ class Transport (threading.Thread): try: self.lock.acquire() if name not in self.subsystem_table: - return (None, [], {}) + return None, [], {} return self.subsystem_table[name] finally: self.lock.release() @@ -2002,7 +2005,7 @@ class Transport (threading.Thread): MSG_CHANNEL_OPEN_FAILURE: _parse_channel_open_failure, MSG_CHANNEL_OPEN: _parse_channel_open, MSG_KEXINIT: _negotiate_keys, - } + } _channel_handler_table = { MSG_CHANNEL_SUCCESS: Channel._request_success, @@ -2013,7 +2016,7 @@ class Transport (threading.Thread): MSG_CHANNEL_REQUEST: Channel._handle_request, MSG_CHANNEL_EOF: Channel._handle_eof, MSG_CHANNEL_CLOSE: Channel._handle_close, - } + } class SecurityOptions (object): |