summaryrefslogtreecommitdiffhomepage
path: root/paramiko/transport.py
diff options
context:
space:
mode:
Diffstat (limited to 'paramiko/transport.py')
-rw-r--r--paramiko/transport.py165
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):