summaryrefslogtreecommitdiffhomepage
path: root/paramiko/transport.py
diff options
context:
space:
mode:
Diffstat (limited to 'paramiko/transport.py')
-rw-r--r--paramiko/transport.py394
1 files changed, 241 insertions, 153 deletions
diff --git a/paramiko/transport.py b/paramiko/transport.py
index 6bbfa757..f982d78f 100644
--- a/paramiko/transport.py
+++ b/paramiko/transport.py
@@ -12,6 +12,7 @@ MSG_CHANNEL_OPEN, MSG_CHANNEL_OPEN_SUCCESS, MSG_CHANNEL_OPEN_FAILURE, \
MSG_CHANNEL_SUCCESS, MSG_CHANNEL_FAILURE = range(90, 101)
import sys, os, string, threading, socket, logging, struct
+from ssh_exception import SSHException
from message import Message
from channel import Channel
from util import format_binary, safe_string, inflate_long, deflate_long, tb_strings
@@ -19,6 +20,7 @@ from rsakey import RSAKey
from dsskey import DSSKey
from kex_group1 import KexGroup1
from kex_gex import KexGex
+from primes import ModulusPack
# these come from PyCrypt
# http://www.amk.ca/python/writing/pycrypt/
@@ -81,21 +83,21 @@ class BaseTransport(threading.Thread):
preferred_keys = [ 'ssh-rsa', 'ssh-dss' ]
preferred_kex = [ 'diffie-hellman-group1-sha1', 'diffie-hellman-group-exchange-sha1' ]
- cipher_info = {
+ _cipher_info = {
'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 },
}
- mac_info = {
+ _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 },
}
- kex_info = {
+ _kex_info = {
'diffie-hellman-group1-sha1': KexGroup1,
'diffie-hellman-group-exchange-sha1': KexGex,
}
@@ -107,7 +109,7 @@ class BaseTransport(threading.Thread):
OPEN_FAILED_RESOURCE_SHORTAGE = range(1, 5)
def __init__(self, sock):
- threading.Thread.__init__(self)
+ threading.Thread.__init__(self, target=self._run)
self.randpool = randpool
self.sock = sock
self.sock.settimeout(0.1)
@@ -123,11 +125,10 @@ class BaseTransport(threading.Thread):
self.session_id = None
# /negotiated crypto parameters
self.expected_packet = 0
- self.active = 0
+ self.active = False
self.initial_kex_done = 0
self.write_lock = threading.Lock() # lock around outbound writes (packet computation)
self.lock = threading.Lock() # synchronization (always higher level than write_lock)
- self.authenticated = 0
self.channels = { } # (id -> Channel)
self.channel_events = { } # (id -> Event)
self.channel_counter = 1
@@ -135,6 +136,7 @@ class BaseTransport(threading.Thread):
self.window_size = 65536
self.max_packet_size = 2048
self.ultra_debug = 0
+ self.modulus_pack = None
# used for noticing when to re-key:
self.received_bytes = 0
self.received_packets = 0
@@ -165,27 +167,69 @@ class BaseTransport(threading.Thread):
except KeyError:
return None
+ def load_server_moduli(self, filename=None):
+ """
+ I{(optional)}
+ Load a file of prime moduli for use in doing group-exchange key
+ negotiation in server mode. It's a rather obscure option and can be
+ safely ignored.
+
+ In server mode, the remote client may request "group-exchange" key
+ negotiation, which asks the server to send a random prime number that
+ fits certain criteria. These primes are pretty difficult to compute,
+ so they can't be generated on demand. But many systems contain a file
+ of suitable primes (usually named something like C{/etc/ssh/moduli}).
+ If you call C{load_server_moduli} and it returns C{True}, then this
+ file of primes has been loaded and we will support "group-exchange" in
+ server mode. Otherwise server mode will just claim that it doesn't
+ support that method of key negotiation.
+
+ @param filename: optional path to the moduli file, if you happen to
+ know that it's not in a standard location.
+ @type filename: string
+ @return: True if a moduli file was successfully loaded; False
+ otherwise.
+ @rtype: boolean
+
+ @since: doduo
+
+ @note: This has no effect when used in client mode.
+ """
+ self.modulus_pack = ModulusPack(self.randpool)
+ # places to look for the openssh "moduli" file
+ file_list = [ '/etc/ssh/moduli', '/usr/local/etc/moduli' ]
+ if filename is not None:
+ file_list.insert(0, filename)
+ for fn in file_list:
+ try:
+ self.modulus_pack.read_file(fn)
+ return True
+ except IOError:
+ pass
+ # none succeeded
+ self.modulus_pack = None
+ return False
+
+ def _get_modulus_pack(self):
+ "used by KexGex to find primes for group exchange"
+ return self.modulus_pack
+
def __repr__(self):
if not self.active:
- return '<paramiko.Transport (unconnected)>'
- out = '<sesch.Transport'
+ return '<paramiko.BaseTransport (unconnected)>'
+ out = '<paramiko.BaseTransport'
#if self.remote_version != '':
# out += ' (server version "%s")' % self.remote_version
if self.local_cipher != '':
out += ' (cipher %s)' % self.local_cipher
- if self.authenticated:
- if len(self.channels) == 1:
- out += ' (active; 1 open channel)'
- else:
- out += ' (active; %d open channels)' % len(self.channels)
- elif self.initial_kex_done:
- out += ' (connected; awaiting auth)'
+ if len(self.channels) == 1:
+ out += ' (active; 1 open channel)'
else:
- out += ' (connecting)'
+ out += ' (active; %d open channels)' % len(self.channels)
out += '>'
return out
- def log(self, level, msg):
+ def _log(self, level, msg):
if type(msg) == type([]):
for m in msg:
self.logger.log(level, m)
@@ -193,14 +237,29 @@ class BaseTransport(threading.Thread):
self.logger.log(level, msg)
def close(self):
- self.active = 0
+ """
+ Close this session, and any open channels that are tied to it.
+ """
+ self.active = False
self.engine_in = self.engine_out = None
self.sequence_number_in = self.sequence_number_out = 0L
for chan in self.channels.values():
- chan.unlink()
+ chan._unlink()
def get_remote_server_key(self):
- 'returns (type, key) where type is like "ssh-rsa" and key is an opaque string'
+ """
+ Return the host key of the server (in client mode).
+ The type string is usually either C{"ssh-rsa"} or C{"ssh-dss"} and the
+ key is an opaque string, which may be saved or used for comparison with
+ previously-seen keys. (In other words, you don't need to worry about
+ the content of the key, only that it compares equal to the key you
+ expected to see.)
+
+ @raise SSHException: if no session is currently active.
+
+ @return: tuple of (key type, key)
+ @rtype: (string, string)
+ """
if (not self.active) or (not self.initial_kex_done):
raise SSHException('No existing session')
key_msg = Message(self.host_key)
@@ -208,10 +267,13 @@ class BaseTransport(threading.Thread):
return key_type, self.host_key
def is_active(self):
- return self.active
+ """
+ Return true if this session is active (open).
- def is_authenticated(self):
- return self.authenticated and self.active
+ @return: True if the session is still active (open); False if the session is closed.
+ @rtype: boolean
+ """
+ return self.active
def open_session(self):
return self.open_channel('session')
@@ -230,9 +292,9 @@ class BaseTransport(threading.Thread):
m.add_int(self.max_packet_size)
self.channels[chanid] = chan = Channel(chanid)
self.channel_events[chanid] = event = threading.Event()
- chan.set_transport(self)
+ chan._set_transport(self)
chan.set_window(self.window_size, self.max_packet_size)
- self.send_message(m)
+ self._send_message(m)
finally:
self.lock.release()
while 1:
@@ -249,7 +311,8 @@ class BaseTransport(threading.Thread):
self.lock.release()
return chan
- def unlink_channel(self, chanid):
+ def _unlink_channel(self, chanid):
+ "used by a Channel to remove itself from the active channel list"
try:
self.lock.acquire()
if self.channels.has_key(chanid):
@@ -257,7 +320,7 @@ class BaseTransport(threading.Thread):
finally:
self.lock.release()
- def read_all(self, n):
+ def _read_all(self, n):
out = ''
while n > 0:
try:
@@ -271,7 +334,7 @@ class BaseTransport(threading.Thread):
raise EOFError()
return out
- def write_all(self, out):
+ def _write_all(self, out):
while len(out) > 0:
n = self.sock.send(out)
if n <= 0:
@@ -281,7 +344,7 @@ class BaseTransport(threading.Thread):
out = out[n:]
return
- def build_packet(self, payload):
+ def _build_packet(self, payload):
# pad up at least 4 bytes, to nearest block-size (usually 8)
bsize = self.block_size_out
padding = 3 + bsize - ((len(payload) + 8) % bsize)
@@ -291,11 +354,12 @@ class BaseTransport(threading.Thread):
packet += randpool.get_bytes(padding)
return packet
- def send_message(self, data):
+ def _send_message(self, data):
+ # FIXME: should we check for rekeying here too?
# encrypt this sucka
- packet = self.build_packet(str(data))
+ packet = self._build_packet(str(data))
if self.ultra_debug:
- self.log(DEBUG, format_binary(packet, 'OUT: '))
+ self._log(DEBUG, format_binary(packet, 'OUT: '))
if self.engine_out != None:
out = self.engine_out.encrypt(packet)
else:
@@ -308,29 +372,29 @@ class BaseTransport(threading.Thread):
out += HMAC.HMAC(self.mac_key_out, payload, self.local_mac_engine).digest()[:self.local_mac_len]
self.sequence_number_out += 1L
self.sequence_number_out %= 0x100000000L
- self.write_all(out)
+ self._write_all(out)
finally:
self.write_lock.release()
- def read_message(self):
+ def _read_message(self):
"only one thread will ever be in this function"
- header = self.read_all(self.block_size_in)
+ header = self._read_all(self.block_size_in)
if self.engine_in != None:
header = self.engine_in.decrypt(header)
if self.ultra_debug:
- self.log(DEBUG, format_binary(header, 'IN: '));
+ self._log(DEBUG, format_binary(header, 'IN: '));
packet_size = struct.unpack('>I', header[:4])[0]
# leftover contains decrypted bytes from the first block (after the length field)
leftover = header[4:]
if (packet_size - len(leftover)) % self.block_size_in != 0:
raise SSHException('Invalid packet blocking')
- buffer = self.read_all(packet_size + self.remote_mac_len - len(leftover))
+ buffer = self._read_all(packet_size + self.remote_mac_len - len(leftover))
packet = buffer[:packet_size - len(leftover)]
post_packet = buffer[packet_size - len(leftover):]
if self.engine_in != None:
packet = self.engine_in.decrypt(packet)
if self.ultra_debug:
- self.log(DEBUG, format_binary(packet, 'IN: '));
+ self._log(DEBUG, format_binary(packet, 'IN: '));
packet = leftover + packet
if self.remote_mac_len > 0:
mac = post_packet[:self.remote_mac_len]
@@ -341,7 +405,7 @@ class BaseTransport(threading.Thread):
padding = ord(packet[0])
payload = packet[1:packet_size - padding + 1]
randpool.add_event(packet[packet_size - padding + 1])
- #self.log(DEBUG, 'Got payload (%d bytes, %d padding)' % (packet_size, padding))
+ #self._log(DEBUG, 'Got payload (%d bytes, %d padding)' % (packet_size, padding))
msg = Message(payload[1:])
msg.seqno = self.sequence_number_in
self.sequence_number_in = (self.sequence_number_in + 1) & 0xffffffffL
@@ -351,10 +415,10 @@ class BaseTransport(threading.Thread):
if (self.received_packets >= self.REKEY_PACKETS) or (self.received_bytes >= self.REKEY_BYTES):
# only ask once for rekeying
if self.local_kex_init is None:
- self.log(DEBUG, 'Rekeying (hit %d packets, %d bytes)' % (self.received_packets,
- self.received_bytes))
+ self._log(DEBUG, 'Rekeying (hit %d packets, %d bytes)' % (self.received_packets,
+ self.received_bytes))
self.received_packets_overflow = 0
- self.send_kex_init()
+ self._send_kex_init()
else:
# we've asked to rekey already -- give them 20 packets to
# comply, then just drop the connection
@@ -364,14 +428,18 @@ class BaseTransport(threading.Thread):
return ord(payload[0]), msg
- def set_K_H(self, k, h):
+ def _set_K_H(self, k, h):
"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:
self.session_id = h
- def verify_key(self, host_key, sig):
+ def _expect_packet(self, type):
+ "used by a kex object to register the next packet type it expects to see"
+ self.expected_packet = type
+
+ def _verify_key(self, host_key, sig):
if self.host_key_type == 'ssh-rsa':
key = RSAKey(Message(host_key))
elif self.host_key_type == 'ssh-dss':
@@ -384,7 +452,7 @@ class BaseTransport(threading.Thread):
raise SSHException('Signature verification (%s) failed. Boo. Robey should debug this.' % self.host_key_type)
self.host_key = host_key
- def compute_key(self, id, nbytes):
+ def _compute_key(self, id, nbytes):
"id is 'A' - 'F' for the various keys used by ssh"
m = Message()
m.add_mpint(self.K)
@@ -402,30 +470,30 @@ class BaseTransport(threading.Thread):
sofar += hash
return out[:nbytes]
- def get_cipher(self, name, key, iv):
- if not self.cipher_info.has_key(name):
+ def _get_cipher(self, name, key, iv):
+ if not self._cipher_info.has_key(name):
raise SSHException('Unknown client cipher ' + name)
- return self.cipher_info[name]['class'].new(key, self.cipher_info[name]['mode'], iv)
+ return self._cipher_info[name]['class'].new(key, self._cipher_info[name]['mode'], iv)
- def run(self):
- self.active = 1
+ def _run(self):
+ self.active = True
try:
# SSH-1.99-OpenSSH_2.9p2
- self.write_all(self.local_version + '\r\n')
- self.check_banner()
- self.send_kex_init()
+ self._write_all(self.local_version + '\r\n')
+ self._check_banner()
+ self._send_kex_init()
self.expected_packet = MSG_KEXINIT
while self.active:
- ptype, m = self.read_message()
+ ptype, m = self._read_message()
if ptype == MSG_IGNORE:
continue
elif ptype == MSG_DISCONNECT:
- self.parse_disconnect(m)
- self.active = 0
+ self._parse_disconnect(m)
+ self.active = False
break
elif ptype == MSG_DEBUG:
- self.parse_debug(m)
+ self._parse_debug(m)
continue
if self.expected_packet != 0:
if ptype != self.expected_packet:
@@ -435,28 +503,29 @@ class BaseTransport(threading.Thread):
self.kex_engine.parse_next(ptype, m)
continue
- if self.handler_table.has_key(ptype):
- self.handler_table[ptype](self, m)
- elif self.channel_handler_table.has_key(ptype):
+ if self._handler_table.has_key(ptype):
+ self._handler_table[ptype](self, m)
+ elif self._channel_handler_table.has_key(ptype):
chanid = m.get_int()
if self.channels.has_key(chanid):
- self.channel_handler_table[ptype](self.channels[chanid], m)
+ self._channel_handler_table[ptype](self.channels[chanid], m)
else:
- self.log(WARNING, 'Oops, unhandled type %d' % ptype)
+ self._log(WARNING, 'Oops, unhandled type %d' % ptype)
msg = Message()
msg.add_byte(chr(MSG_UNIMPLEMENTED))
msg.add_int(m.seqno)
- self.send_message(msg)
+ self._send_message(msg)
except SSHException, e:
- self.log(DEBUG, 'Exception: ' + str(e))
- self.log(DEBUG, tb_strings())
+ self._log(DEBUG, 'Exception: ' + str(e))
+ self._log(DEBUG, tb_strings())
except EOFError, e:
- self.log(DEBUG, 'EOF')
+ self._log(DEBUG, 'EOF')
+ self._log(DEBUG, tb_strings())
except Exception, e:
- self.log(DEBUG, 'Unknown exception: ' + str(e))
- self.log(DEBUG, tb_strings())
+ self._log(DEBUG, 'Unknown exception: ' + str(e))
+ self._log(DEBUG, tb_strings())
if self.active:
- self.active = 0
+ self.active = False
if self.completion_event != None:
self.completion_event.set()
if self.auth_event != None:
@@ -468,36 +537,49 @@ class BaseTransport(threading.Thread):
### protocol stages
def renegotiate_keys(self):
+ """
+ Force this session to switch to new keys. Normally this is done
+ automatically after the session hits a certain number of packets or
+ bytes sent or received, but this method gives you the option of forcing
+ new keys whenever you want. Negotiating new keys causes a pause in
+ traffic both ways as the two sides swap keys and do computations. This
+ method returns when the session has switched to new keys, or the
+ session has died mid-negotiation.
+
+ @return: True if the renegotiation was successful, and the link is
+ using new keys; False if the session dropped during renegotiation.
+ @rtype: boolean
+ """
self.completion_event = threading.Event()
- self.send_kex_init()
+ self._send_kex_init()
while 1:
self.completion_event.wait(0.1);
if not self.active:
- return 0
+ return False
if self.completion_event.isSet():
break
- return 1
+ return True
- def negotiate_keys(self, m):
+ def _negotiate_keys(self, m):
# throws SSHException on anything unusual
if self.local_kex_init == None:
# remote side wants to renegotiate
- self.send_kex_init()
- self.parse_kex_init(m)
+ self._send_kex_init()
+ self._parse_kex_init(m)
self.kex_engine.start_kex()
- def check_banner(self):
+ def _check_banner(self):
# this is slow, but we only have to do it once
for i in range(5):
buffer = ''
while not '\n' in buffer:
- buffer += self.read_all(1)
+ buffer += self._read_all(1)
buffer = buffer[:-1]
if (len(buffer) > 0) and (buffer[-1] == '\r'):
buffer = buffer[:-1]
if buffer[:4] == 'SSH-':
break
- self.log(DEBUG, 'Banner: ' + buffer)
+ self._log(DEBUG, 'Banner: ' + buffer)
if buffer[:4] != 'SSH-':
raise SSHException('Indecipherable protocol version "' + buffer + '"')
# save this server version string for later
@@ -516,17 +598,21 @@ class BaseTransport(threading.Thread):
client = segs[2]
if version != '1.99' and version != '2.0':
raise SSHException('Incompatible version (%s instead of 2.0)' % (version,))
- self.log(INFO, 'Connected (version %s, client %s)' % (version, client))
+ self._log(INFO, 'Connected (version %s, client %s)' % (version, client))
- def send_kex_init(self):
- # send a really wimpy kex-init packet that says we're a bare-bones ssh client
+ def _send_kex_init(self):
+ """
+ announce to the other side that we'd like to negotiate keys, and what
+ kind of key negotiation we support.
+ """
if self.server_mode:
- # FIXME: can't do group-exchange (gex) yet -- too slow
- if 'diffie-hellman-group-exchange-sha1' in self.preferred_kex:
+ if (self.modulus_pack is None) and ('diffie-hellman-group-exchange-sha1' in self.preferred_kex):
+ # can't do group-exchange if we don't have a pack of potential primes
self.preferred_kex.remove('diffie-hellman-group-exchange-sha1')
-
- available_server_keys = filter(self.server_key_dict.keys().__contains__,
- self.preferred_keys)
+ available_server_keys = filter(self.server_key_dict.keys().__contains__,
+ self.preferred_keys)
+ else:
+ available_server_keys = self.preferred_keys
m = Message()
m.add_byte(chr(MSG_KEXINIT))
@@ -545,9 +631,9 @@ class BaseTransport(threading.Thread):
m.add_int(0)
# save a copy for later (needed to compute a hash)
self.local_kex_init = str(m)
- self.send_message(m)
+ self._send_message(m)
- def parse_kex_init(self, m):
+ def _parse_kex_init(self, m):
# reset counters of when to re-key, since we are now re-keying
self.received_bytes = 0
self.received_packets = 0
@@ -580,7 +666,7 @@ class BaseTransport(threading.Thread):
agreed_kex = filter(kex_algo_list.__contains__, self.preferred_kex)
if len(agreed_kex) == 0:
raise SSHException('Incompatible ssh peer (no acceptable kex algorithm)')
- self.kex_engine = self.kex_info[agreed_kex[0]](self)
+ self.kex_engine = self._kex_info[agreed_kex[0]](self)
if self.server_mode:
available_server_keys = filter(self.server_key_dict.keys().__contains__,
@@ -608,7 +694,7 @@ class BaseTransport(threading.Thread):
raise SSHException('Incompatible ssh server (no acceptable ciphers)')
self.local_cipher = agreed_local_ciphers[0]
self.remote_cipher = agreed_remote_ciphers[0]
- self.log(DEBUG, 'Ciphers agreed: local=%s, remote=%s' % (self.local_cipher, self.remote_cipher))
+ self._log(DEBUG, 'Ciphers agreed: local=%s, remote=%s' % (self.local_cipher, self.remote_cipher))
if self.server_mode:
agreed_remote_macs = filter(self.preferred_macs.__contains__, client_mac_algo_list)
@@ -621,19 +707,19 @@ class BaseTransport(threading.Thread):
self.local_mac = agreed_local_macs[0]
self.remote_mac = agreed_remote_macs[0]
- 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))
- self.log(DEBUG, 'using kex %s; server key type %s; cipher: local %s, remote %s; mac: local %s, remote %s' %
- (agreed_kex[0], self.host_key_type, self.local_cipher, self.remote_cipher, self.local_mac,
- self.remote_mac))
+ 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))
+ self._log(DEBUG, 'using kex %s; server key type %s; cipher: local %s, remote %s; mac: local %s, remote %s' %
+ (agreed_kex[0], self.host_key_type, self.local_cipher, self.remote_cipher, self.local_mac,
+ self.remote_mac))
# save for computing hash later...
# now wait! openssh has a bug (and others might too) where there are
@@ -642,50 +728,52 @@ class BaseTransport(threading.Thread):
# away those bytes because they aren't part of the hash.
self.remote_kex_init = chr(MSG_KEXINIT) + m.get_so_far()
- def activate_inbound(self):
+ def _activate_inbound(self):
"switch on newly negotiated encryption parameters for inbound traffic"
- self.block_size_in = self.cipher_info[self.remote_cipher]['block-size']
+ self.block_size_in = self._cipher_info[self.remote_cipher]['block-size']
if self.server_mode:
- IV_in = self.compute_key('A', self.block_size_in)
- key_in = self.compute_key('C', self.cipher_info[self.remote_cipher]['key-size'])
+ IV_in = self._compute_key('A', self.block_size_in)
+ key_in = self._compute_key('C', self._cipher_info[self.remote_cipher]['key-size'])
else:
- IV_in = self.compute_key('B', self.block_size_in)
- key_in = self.compute_key('D', self.cipher_info[self.remote_cipher]['key-size'])
- self.engine_in = self.get_cipher(self.remote_cipher, key_in, IV_in)
- self.remote_mac_len = self.mac_info[self.remote_mac]['size']
- self.remote_mac_engine = self.mac_info[self.remote_mac]['class']
+ IV_in = self._compute_key('B', self.block_size_in)
+ key_in = self._compute_key('D', self._cipher_info[self.remote_cipher]['key-size'])
+ self.engine_in = self._get_cipher(self.remote_cipher, key_in, IV_in)
+ self.remote_mac_len = self._mac_info[self.remote_mac]['size']
+ self.remote_mac_engine = self._mac_info[self.remote_mac]['class']
# initial mac keys are done in the hash's natural size (not the potentially truncated
# transmission size)
if self.server_mode:
- self.mac_key_in = self.compute_key('E', self.remote_mac_engine.digest_size)
+ self.mac_key_in = self._compute_key('E', self.remote_mac_engine.digest_size)
else:
- self.mac_key_in = self.compute_key('F', self.remote_mac_engine.digest_size)
+ self.mac_key_in = self._compute_key('F', self.remote_mac_engine.digest_size)
- def activate_outbound(self):
+ def _activate_outbound(self):
"switch on newly negotiated encryption parameters for outbound traffic"
m = Message()
m.add_byte(chr(MSG_NEWKEYS))
- self.send_message(m)
- self.block_size_out = self.cipher_info[self.local_cipher]['block-size']
+ self._send_message(m)
+ self.block_size_out = self._cipher_info[self.local_cipher]['block-size']
if self.server_mode:
- IV_out = self.compute_key('B', self.block_size_out)
- key_out = self.compute_key('D', self.cipher_info[self.local_cipher]['key-size'])
+ IV_out = self._compute_key('B', self.block_size_out)
+ key_out = self._compute_key('D', self._cipher_info[self.local_cipher]['key-size'])
else:
- IV_out = self.compute_key('A', self.block_size_out)
- key_out = self.compute_key('C', self.cipher_info[self.local_cipher]['key-size'])
- self.engine_out = self.get_cipher(self.local_cipher, key_out, IV_out)
- self.local_mac_len = self.mac_info[self.local_mac]['size']
- self.local_mac_engine = self.mac_info[self.local_mac]['class']
+ IV_out = self._compute_key('A', self.block_size_out)
+ key_out = self._compute_key('C', self._cipher_info[self.local_cipher]['key-size'])
+ self.engine_out = self._get_cipher(self.local_cipher, key_out, IV_out)
+ self.local_mac_len = self._mac_info[self.local_mac]['size']
+ self.local_mac_engine = self._mac_info[self.local_mac]['class']
# initial mac keys are done in the hash's natural size (not the potentially truncated
# transmission size)
if self.server_mode:
- self.mac_key_out = self.compute_key('F', self.local_mac_engine.digest_size)
+ self.mac_key_out = self._compute_key('F', self.local_mac_engine.digest_size)
else:
- self.mac_key_out = self.compute_key('E', self.local_mac_engine.digest_size)
+ self.mac_key_out = self._compute_key('E', self.local_mac_engine.digest_size)
+ # we always expect to receive NEWKEYS now
+ self.expected_packet = MSG_NEWKEYS
- def parse_newkeys(self, m):
- self.log(DEBUG, 'Switch to new keys ...')
- self.activate_inbound()
+ def _parse_newkeys(self, m):
+ self._log(DEBUG, 'Switch to new keys ...')
+ self._activate_inbound()
# can also free a bunch of stuff here
self.local_kex_init = self.remote_kex_init = None
self.e = self.f = self.K = self.x = None
@@ -697,24 +785,24 @@ class BaseTransport(threading.Thread):
self.completion_event.set()
return
- def parse_disconnect(self, m):
+ def _parse_disconnect(self, m):
code = m.get_int()
desc = m.get_string()
- self.log(INFO, 'Disconnect (code %d): %s' % (code, desc))
+ self._log(INFO, 'Disconnect (code %d): %s' % (code, desc))
- def parse_channel_open_success(self, m):
+ def _parse_channel_open_success(self, m):
chanid = m.get_int()
server_chanid = m.get_int()
server_window_size = m.get_int()
server_max_packet_size = m.get_int()
if not self.channels.has_key(chanid):
- self.log(WARNING, 'Success for unrequested channel! [??]')
+ self._log(WARNING, 'Success for unrequested channel! [??]')
return
try:
self.lock.acquire()
chan = self.channels[chanid]
chan.set_remote_channel(server_chanid, server_window_size, server_max_packet_size)
- self.log(INFO, 'Secsh channel %d opened.' % chanid)
+ self._log(INFO, 'Secsh channel %d opened.' % chanid)
if self.channel_events.has_key(chanid):
self.channel_events[chanid].set()
del self.channel_events[chanid]
@@ -722,7 +810,7 @@ class BaseTransport(threading.Thread):
self.lock.release()
return
- def parse_channel_open_failure(self, m):
+ def _parse_channel_open_failure(self, m):
chanid = m.get_int()
reason = m.get_int()
reason_str = m.get_string()
@@ -731,7 +819,7 @@ class BaseTransport(threading.Thread):
reason_text = CONNECTION_FAILED_CODE[reason]
else:
reason_text = '(unknown code)'
- self.log(INFO, 'Secsh channel %d open FAILED: %s: %s' % (chanid, reason_str, reason_text))
+ self._log(INFO, 'Secsh channel %d open FAILED: %s: %s' % (chanid, reason_str, reason_text))
try:
self.lock.aquire()
if self.channels.has_key(chanid):
@@ -747,14 +835,14 @@ class BaseTransport(threading.Thread):
"override me! return object descended from Channel to allow, or None to reject"
return None
- def parse_channel_open(self, m):
+ def _parse_channel_open(self, m):
kind = m.get_string()
chanid = m.get_int()
initial_window_size = m.get_int()
max_packet_size = m.get_int()
reject = False
if not self.server_mode:
- self.log(DEBUG, 'Rejecting "%s" channel request from server.' % kind)
+ self._log(DEBUG, 'Rejecting "%s" channel request from server.' % kind)
reject = True
reason = self.OPEN_FAILED_ADMINISTRATIVELY_PROHIBITED
else:
@@ -766,7 +854,7 @@ class BaseTransport(threading.Thread):
self.lock.release()
chan = self.check_channel_request(kind, my_chanid)
if (chan is None) or (type(chan) is int):
- self.log(DEBUG, 'Rejecting "%s" channel request from client.' % kind)
+ self._log(DEBUG, 'Rejecting "%s" channel request from client.' % kind)
reject = True
if type(chan) is int:
reason = chan
@@ -779,12 +867,12 @@ class BaseTransport(threading.Thread):
msg.add_int(reason)
msg.add_string('')
msg.add_string('en')
- self.send_message(msg)
+ self._send_message(msg)
return
try:
self.lock.acquire()
self.channels[my_chanid] = chan
- chan.set_transport(self)
+ chan._set_transport(self)
chan.set_window(self.window_size, self.max_packet_size)
chan.set_remote_channel(chanid, initial_window_size, max_packet_size)
finally:
@@ -795,8 +883,8 @@ class BaseTransport(threading.Thread):
m.add_int(my_chanid)
m.add_int(self.window_size)
m.add_int(self.max_packet_size)
- self.send_message(m)
- self.log(INFO, 'Secsh channel %d opened.' % my_chanid)
+ self._send_message(m)
+ self._log(INFO, 'Secsh channel %d opened.' % my_chanid)
try:
self.lock.acquire()
self.server_accepts.append(chan)
@@ -820,21 +908,21 @@ class BaseTransport(threading.Thread):
self.lock.release()
return chan
- def parse_debug(self, m):
+ def _parse_debug(self, m):
always_display = m.get_boolean()
msg = m.get_string()
lang = m.get_string()
- self.log(DEBUG, 'Debug msg: ' + safe_string(msg))
-
- handler_table = {
- MSG_NEWKEYS: parse_newkeys,
- MSG_CHANNEL_OPEN_SUCCESS: parse_channel_open_success,
- MSG_CHANNEL_OPEN_FAILURE: parse_channel_open_failure,
- MSG_CHANNEL_OPEN: parse_channel_open,
- MSG_KEXINIT: negotiate_keys,
+ self._log(DEBUG, 'Debug msg: ' + safe_string(msg))
+
+ _handler_table = {
+ MSG_NEWKEYS: _parse_newkeys,
+ MSG_CHANNEL_OPEN_SUCCESS: _parse_channel_open_success,
+ MSG_CHANNEL_OPEN_FAILURE: _parse_channel_open_failure,
+ MSG_CHANNEL_OPEN: _parse_channel_open,
+ MSG_KEXINIT: _negotiate_keys,
}
- channel_handler_table = {
+ _channel_handler_table = {
MSG_CHANNEL_SUCCESS: Channel.request_success,
MSG_CHANNEL_FAILURE: Channel.request_failed,
MSG_CHANNEL_DATA: Channel.feed,