[project @ Arch-1:robey@lag.net--2003-public%secsh--dev--1.0--patch-9]
rename secsh -> paramiko also, rename SecshException back to SSHException. sigh. :)
This commit is contained in:
parent
2ff9f46751
commit
11815d4d83
2
MANIFEST
2
MANIFEST
|
@ -6,8 +6,8 @@ dsskey.py
|
||||||
kex_gex.py
|
kex_gex.py
|
||||||
kex_group1.py
|
kex_group1.py
|
||||||
message.py
|
message.py
|
||||||
|
paramiko.py
|
||||||
rsakey.py
|
rsakey.py
|
||||||
secsh.py
|
|
||||||
setup.py
|
setup.py
|
||||||
transport.py
|
transport.py
|
||||||
util.py
|
util.py
|
||||||
|
|
11
Makefile
11
Makefile
|
@ -1,18 +1,11 @@
|
||||||
# releases:
|
# releases:
|
||||||
# aerodactyl (13sep03)
|
# aerodactyl (13sep03)
|
||||||
# bulbasaur
|
# bulbasaur
|
||||||
# charmander
|
# charmander (10nov03)
|
||||||
|
|
||||||
RELEASE=bulbasaur
|
RELEASE=charmander
|
||||||
|
|
||||||
release:
|
release:
|
||||||
mkdir ../secsh-$(RELEASE)
|
|
||||||
cp README ../secsh-$(RELEASE)
|
|
||||||
cp *.py ../secsh-$(RELEASE)
|
|
||||||
cd .. && zip -r secsh-$(RELEASE).zip secsh-$(RELEASE)
|
|
||||||
echo rm -rf ../secsh-$(RELEASE)
|
|
||||||
|
|
||||||
py:
|
|
||||||
python ./setup.py sdist --formats=zip
|
python ./setup.py sdist --formats=zip
|
||||||
|
|
||||||
# places where the version number is stored:
|
# places where the version number is stored:
|
||||||
|
|
6
NOTES
6
NOTES
|
@ -1,6 +1,6 @@
|
||||||
|
|
||||||
+-------------------+ +-----------------+
|
+-------------------+ +-----------------+
|
||||||
(Socket)InputStream ---> | secsh transport | <===> | secsh channel |
|
(Socket)InputStream ---> | ssh2 transport | <===> | ssh2 channel |
|
||||||
(Socket)OutputStream --> | (auth, pipe) | N | (buffer) |
|
(Socket)OutputStream --> | (auth, pipe) | N | (buffer) |
|
||||||
+-------------------+ +-----------------+
|
+-------------------+ +-----------------+
|
||||||
@ feeder thread | |
|
@ feeder thread | |
|
||||||
|
@ -8,8 +8,8 @@
|
||||||
- feed into channel +---> OutputStream
|
- feed into channel +---> OutputStream
|
||||||
buffers
|
buffers
|
||||||
|
|
||||||
SIS <-- @ --> (parse, find chan) --> secsh chan: buffer <-- SSHInputStream
|
SIS <-- @ --> (parse, find chan) --> ssh2 chan: buffer <-- SSHInputStream
|
||||||
SSHOutputStream --> secsh chan --> secsh transport --> SOS [no thread]
|
SSHOutputStream --> ssh2 chan --> ssh2 transport --> SOS [no thread]
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
15
README
15
README
|
@ -1,14 +1,15 @@
|
||||||
secsh 0.1
|
paramiko 0.1
|
||||||
"charmander" release, 10 nov 2003
|
"charmander" release, 10 nov 2003
|
||||||
|
|
||||||
(c) 2003 Robey Pointer <robey@lag.net>
|
(c) 2003 Robey Pointer <robey@lag.net>
|
||||||
|
|
||||||
http://www.lag.net/~robey/secsh/
|
http://www.lag.net/~robey/paramiko/
|
||||||
|
|
||||||
|
|
||||||
*** WHAT
|
*** WHAT
|
||||||
|
|
||||||
secsh is a module for python 2.3 that implements the SSH2 protocol for secure
|
"paramiko" is a combination of the esperanto words for "paranoid" and "friend".
|
||||||
|
it's a module for python 2.3 that implements the SSH2 protocol for secure
|
||||||
(encrypted and authenticated) connections to remote machines. unlike SSL (aka
|
(encrypted and authenticated) connections to remote machines. unlike SSL (aka
|
||||||
TLS), SSH2 protocol does not require heirarchical certificates signed by a
|
TLS), SSH2 protocol does not require heirarchical certificates signed by a
|
||||||
powerful central authority. you may know SSH2 as the protocol that replaced
|
powerful central authority. you may know SSH2 as the protocol that replaced
|
||||||
|
@ -22,7 +23,7 @@ key), and opening flow-controled "channels" to the server, which are returned
|
||||||
as socket-like objects. you are responsible for verifying that the server's
|
as socket-like objects. you are responsible for verifying that the server's
|
||||||
host key is the one you expected to see, and you have control over which kinds
|
host key is the one you expected to see, and you have control over which kinds
|
||||||
of encryption or hashing you prefer (if you care), but all of the heavy lifting
|
of encryption or hashing you prefer (if you care), but all of the heavy lifting
|
||||||
is done by the secsh module.
|
is done by the paramiko module.
|
||||||
|
|
||||||
it is written entirely in python (no C or platform-dependent code) and is
|
it is written entirely in python (no C or platform-dependent code) and is
|
||||||
released under the GNU LGPL (lesser GPL).
|
released under the GNU LGPL (lesser GPL).
|
||||||
|
@ -55,7 +56,7 @@ know these screwy steps. i just don't understand windows enough.])
|
||||||
*** DEMO
|
*** DEMO
|
||||||
|
|
||||||
the demo client (demo.py) is a raw implementation of the normal 'ssh' CLI tool.
|
the demo client (demo.py) is a raw implementation of the normal 'ssh' CLI tool.
|
||||||
while the secsh library should work on all platforms, the demo app will only
|
while the paramiko library should work on all platforms, the demo app will only
|
||||||
run on posix, because it uses select.
|
run on posix, because it uses select.
|
||||||
|
|
||||||
you can run demo.py with no arguments, or you can give a hostname (or
|
you can run demo.py with no arguments, or you can give a hostname (or
|
||||||
|
@ -65,9 +66,9 @@ the host keys from there, though it's easily confused. you can choose to
|
||||||
authenticate with a password, or with an RSA or DSS key, but it can only
|
authenticate with a password, or with an RSA or DSS key, but it can only
|
||||||
read your private key file(s) if they're not password-protected.
|
read your private key file(s) if they're not password-protected.
|
||||||
|
|
||||||
the demo app leaves a logfile called "demo.log" so you can see what secsh
|
the demo app leaves a logfile called "demo.log" so you can see what paramiko
|
||||||
logs as it works. but the most interesting part is probably the code itself,
|
logs as it works. but the most interesting part is probably the code itself,
|
||||||
which hopefully demonstrates how you can use the secsh library.
|
which hopefully demonstrates how you can use the paramiko library.
|
||||||
|
|
||||||
there's also now a demo server (demo_server.py) which listens on port 2200
|
there's also now a demo server (demo_server.py) which listens on port 2200
|
||||||
and accepts a login (robey/foo) and pretends to be a BBS, just to demonstrate
|
and accepts a login (robey/foo) and pretends to be a BBS, just to demonstrate
|
||||||
|
|
|
@ -4,7 +4,7 @@ from transport import BaseTransport
|
||||||
from transport import MSG_SERVICE_REQUEST, MSG_SERVICE_ACCEPT, MSG_USERAUTH_REQUEST, MSG_USERAUTH_FAILURE, \
|
from transport import MSG_SERVICE_REQUEST, MSG_SERVICE_ACCEPT, MSG_USERAUTH_REQUEST, MSG_USERAUTH_FAILURE, \
|
||||||
MSG_USERAUTH_SUCCESS, MSG_USERAUTH_BANNER
|
MSG_USERAUTH_SUCCESS, MSG_USERAUTH_BANNER
|
||||||
from message import Message
|
from message import Message
|
||||||
from secsh import SecshException
|
from paramiko import SSHException
|
||||||
from logging import DEBUG, INFO, WARNING, ERROR, CRITICAL
|
from logging import DEBUG, INFO, WARNING, ERROR, CRITICAL
|
||||||
|
|
||||||
DISCONNECT_SERVICE_NOT_AVAILABLE, DISCONNECT_AUTH_CANCELLED_BY_USER, \
|
DISCONNECT_SERVICE_NOT_AVAILABLE, DISCONNECT_AUTH_CANCELLED_BY_USER, \
|
||||||
|
@ -34,7 +34,7 @@ class Transport(BaseTransport):
|
||||||
def auth_key(self, username, key, event):
|
def auth_key(self, username, key, event):
|
||||||
if (not self.active) or (not self.initial_kex_done):
|
if (not self.active) or (not self.initial_kex_done):
|
||||||
# we should never try to send the password unless we're on a secure link
|
# we should never try to send the password unless we're on a secure link
|
||||||
raise SecshException('No existing session')
|
raise SSHException('No existing session')
|
||||||
try:
|
try:
|
||||||
self.lock.acquire()
|
self.lock.acquire()
|
||||||
self.auth_event = event
|
self.auth_event = event
|
||||||
|
@ -49,7 +49,7 @@ class Transport(BaseTransport):
|
||||||
'authenticate using a password; event is triggered on success or fail'
|
'authenticate using a password; event is triggered on success or fail'
|
||||||
if (not self.active) or (not self.initial_kex_done):
|
if (not self.active) or (not self.initial_kex_done):
|
||||||
# we should never try to send the password unless we're on a secure link
|
# we should never try to send the password unless we're on a secure link
|
||||||
raise SecshException('No existing session')
|
raise SSHException('No existing session')
|
||||||
try:
|
try:
|
||||||
self.lock.acquire()
|
self.lock.acquire()
|
||||||
self.auth_event = event
|
self.auth_event = event
|
||||||
|
@ -108,7 +108,7 @@ class Transport(BaseTransport):
|
||||||
m.add_string(str(self.private_key))
|
m.add_string(str(self.private_key))
|
||||||
m.add_string(self.private_key.sign_ssh_session(self.randpool, self.H, self.username))
|
m.add_string(self.private_key.sign_ssh_session(self.randpool, self.H, self.username))
|
||||||
else:
|
else:
|
||||||
raise SecshException('Unknown auth method "%s"' % self.auth_method)
|
raise SSHException('Unknown auth method "%s"' % self.auth_method)
|
||||||
self.send_message(m)
|
self.send_message(m)
|
||||||
else:
|
else:
|
||||||
self.log(DEBUG, 'Service request "%s" accepted (?)' % service)
|
self.log(DEBUG, 'Service request "%s" accepted (?)' % service)
|
||||||
|
|
22
channel.py
22
channel.py
|
@ -1,5 +1,5 @@
|
||||||
from message import Message
|
from message import Message
|
||||||
from secsh import SecshException
|
from paramiko import SSHException
|
||||||
from transport import MSG_CHANNEL_REQUEST, MSG_CHANNEL_CLOSE, MSG_CHANNEL_WINDOW_ADJUST, MSG_CHANNEL_DATA, \
|
from transport import MSG_CHANNEL_REQUEST, MSG_CHANNEL_CLOSE, MSG_CHANNEL_WINDOW_ADJUST, MSG_CHANNEL_DATA, \
|
||||||
MSG_CHANNEL_EOF, MSG_CHANNEL_SUCCESS, MSG_CHANNEL_FAILURE
|
MSG_CHANNEL_EOF, MSG_CHANNEL_SUCCESS, MSG_CHANNEL_FAILURE
|
||||||
|
|
||||||
|
@ -15,7 +15,7 @@ def set_nonblocking(fd):
|
||||||
|
|
||||||
class Channel(object):
|
class Channel(object):
|
||||||
"""
|
"""
|
||||||
Abstraction for a secsh channel.
|
Abstraction for an SSH2 channel.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(self, chanid):
|
def __init__(self, chanid):
|
||||||
|
@ -31,11 +31,11 @@ class Channel(object):
|
||||||
self.in_buffer_cv = threading.Condition(self.lock)
|
self.in_buffer_cv = threading.Condition(self.lock)
|
||||||
self.out_buffer_cv = threading.Condition(self.lock)
|
self.out_buffer_cv = threading.Condition(self.lock)
|
||||||
self.name = str(chanid)
|
self.name = str(chanid)
|
||||||
self.logger = logging.getLogger('secsh.chan.' + str(chanid))
|
self.logger = logging.getLogger('paramiko.chan.' + str(chanid))
|
||||||
self.pipe_rfd = self.pipe_wfd = None
|
self.pipe_rfd = self.pipe_wfd = None
|
||||||
|
|
||||||
def __repr__(self):
|
def __repr__(self):
|
||||||
out = '<secsh.Channel %d' % self.chanid
|
out = '<paramiko.Channel %d' % self.chanid
|
||||||
if self.closed:
|
if self.closed:
|
||||||
out += ' (closed)'
|
out += ' (closed)'
|
||||||
elif self.active:
|
elif self.active:
|
||||||
|
@ -186,7 +186,7 @@ class Channel(object):
|
||||||
|
|
||||||
def get_pty(self, term='vt100', width=80, height=24):
|
def get_pty(self, term='vt100', width=80, height=24):
|
||||||
if self.closed or self.eof_received or self.eof_sent or not self.active:
|
if self.closed or self.eof_received or self.eof_sent or not self.active:
|
||||||
raise SecshException('Channel is not open')
|
raise SSHException('Channel is not open')
|
||||||
m = Message()
|
m = Message()
|
||||||
m.add_byte(chr(MSG_CHANNEL_REQUEST))
|
m.add_byte(chr(MSG_CHANNEL_REQUEST))
|
||||||
m.add_int(self.remote_chanid)
|
m.add_int(self.remote_chanid)
|
||||||
|
@ -202,7 +202,7 @@ class Channel(object):
|
||||||
|
|
||||||
def invoke_shell(self):
|
def invoke_shell(self):
|
||||||
if self.closed or self.eof_received or self.eof_sent or not self.active:
|
if self.closed or self.eof_received or self.eof_sent or not self.active:
|
||||||
raise SecshException('Channel is not open')
|
raise SSHException('Channel is not open')
|
||||||
m = Message()
|
m = Message()
|
||||||
m.add_byte(chr(MSG_CHANNEL_REQUEST))
|
m.add_byte(chr(MSG_CHANNEL_REQUEST))
|
||||||
m.add_int(self.remote_chanid)
|
m.add_int(self.remote_chanid)
|
||||||
|
@ -212,7 +212,7 @@ class Channel(object):
|
||||||
|
|
||||||
def exec_command(self, command):
|
def exec_command(self, command):
|
||||||
if self.closed or self.eof_received or self.eof_sent or not self.active:
|
if self.closed or self.eof_received or self.eof_sent or not self.active:
|
||||||
raise SecshException('Channel is not open')
|
raise SSHException('Channel is not open')
|
||||||
m = Message()
|
m = Message()
|
||||||
m.add_byte(chr(MSG_CHANNEL_REQUEST))
|
m.add_byte(chr(MSG_CHANNEL_REQUEST))
|
||||||
m.add_int(self.remote_chanid)
|
m.add_int(self.remote_chanid)
|
||||||
|
@ -223,7 +223,7 @@ class Channel(object):
|
||||||
|
|
||||||
def invoke_subsystem(self, subsystem):
|
def invoke_subsystem(self, subsystem):
|
||||||
if self.closed or self.eof_received or self.eof_sent or not self.active:
|
if self.closed or self.eof_received or self.eof_sent or not self.active:
|
||||||
raise SecshException('Channel is not open')
|
raise SSHException('Channel is not open')
|
||||||
m = Message()
|
m = Message()
|
||||||
m.add_byte(chr(MSG_CHANNEL_REQUEST))
|
m.add_byte(chr(MSG_CHANNEL_REQUEST))
|
||||||
m.add_int(self.remote_chanid)
|
m.add_int(self.remote_chanid)
|
||||||
|
@ -234,7 +234,7 @@ class Channel(object):
|
||||||
|
|
||||||
def resize_pty(self, width=80, height=24):
|
def resize_pty(self, width=80, height=24):
|
||||||
if self.closed or self.eof_received or self.eof_sent or not self.active:
|
if self.closed or self.eof_received or self.eof_sent or not self.active:
|
||||||
raise SecshException('Channel is not open')
|
raise SSHException('Channel is not open')
|
||||||
m = Message()
|
m = Message()
|
||||||
m.add_byte(chr(MSG_CHANNEL_REQUEST))
|
m.add_byte(chr(MSG_CHANNEL_REQUEST))
|
||||||
m.add_int(self.remote_chanid)
|
m.add_int(self.remote_chanid)
|
||||||
|
@ -250,7 +250,7 @@ class Channel(object):
|
||||||
|
|
||||||
def set_name(self, name):
|
def set_name(self, name):
|
||||||
self.name = name
|
self.name = name
|
||||||
self.logger = logging.getLogger('secsh.chan.' + name)
|
self.logger = logging.getLogger('paramiko.chan.' + name)
|
||||||
|
|
||||||
def get_name(self):
|
def get_name(self):
|
||||||
return self.name
|
return self.name
|
||||||
|
@ -557,7 +557,7 @@ class ChannelFile(object):
|
||||||
self.softspace = False
|
self.softspace = False
|
||||||
|
|
||||||
def __repr__(self):
|
def __repr__(self):
|
||||||
return '<secsh.ChannelFile from ' + repr(self.channel) + '>'
|
return '<paramiko.ChannelFile from ' + repr(self.channel) + '>'
|
||||||
|
|
||||||
def __iter__(self):
|
def __iter__(self):
|
||||||
return self
|
return self
|
||||||
|
|
10
demo.py
10
demo.py
|
@ -1,7 +1,7 @@
|
||||||
#!/usr/bin/python
|
#!/usr/bin/python
|
||||||
|
|
||||||
import sys, os, socket, threading, getpass, logging, time, base64, select, termios, tty, traceback
|
import sys, os, socket, threading, getpass, logging, time, base64, select, termios, tty, traceback
|
||||||
import secsh
|
import paramiko
|
||||||
|
|
||||||
|
|
||||||
##### utility functions
|
##### utility functions
|
||||||
|
@ -31,7 +31,7 @@ def load_host_keys():
|
||||||
##### main demo
|
##### main demo
|
||||||
|
|
||||||
# setup logging
|
# setup logging
|
||||||
l = logging.getLogger("secsh")
|
l = logging.getLogger("paramiko")
|
||||||
l.setLevel(logging.DEBUG)
|
l.setLevel(logging.DEBUG)
|
||||||
if len(l.handlers) == 0:
|
if len(l.handlers) == 0:
|
||||||
f = open('demo.log', 'w')
|
f = open('demo.log', 'w')
|
||||||
|
@ -65,7 +65,7 @@ except Exception, e:
|
||||||
|
|
||||||
try:
|
try:
|
||||||
event = threading.Event()
|
event = threading.Event()
|
||||||
t = secsh.Transport(sock)
|
t = paramiko.Transport(sock)
|
||||||
t.ultra_debug = 0
|
t.ultra_debug = 0
|
||||||
t.start_client(event)
|
t.start_client(event)
|
||||||
# print repr(t)
|
# print repr(t)
|
||||||
|
@ -103,7 +103,7 @@ try:
|
||||||
auth = default_auth
|
auth = default_auth
|
||||||
|
|
||||||
if auth == 'r':
|
if auth == 'r':
|
||||||
key = secsh.RSAKey()
|
key = paramiko.RSAKey()
|
||||||
default_path = os.environ['HOME'] + '/.ssh/id_rsa'
|
default_path = os.environ['HOME'] + '/.ssh/id_rsa'
|
||||||
path = raw_input('RSA key [%s]: ' % default_path)
|
path = raw_input('RSA key [%s]: ' % default_path)
|
||||||
if len(path) == 0:
|
if len(path) == 0:
|
||||||
|
@ -111,7 +111,7 @@ try:
|
||||||
key.read_private_key_file(path)
|
key.read_private_key_file(path)
|
||||||
t.auth_key(username, key, event)
|
t.auth_key(username, key, event)
|
||||||
elif auth == 'd':
|
elif auth == 'd':
|
||||||
key = secsh.DSSKey()
|
key = paramiko.DSSKey()
|
||||||
default_path = os.environ['HOME'] + '/.ssh/id_dsa'
|
default_path = os.environ['HOME'] + '/.ssh/id_dsa'
|
||||||
path = raw_input('DSS key [%s]: ' % default_path)
|
path = raw_input('DSS key [%s]: ' % default_path)
|
||||||
if len(path) == 0:
|
if len(path) == 0:
|
||||||
|
|
|
@ -1,10 +1,10 @@
|
||||||
#!/usr/bin/python
|
#!/usr/bin/python
|
||||||
|
|
||||||
import sys, os, socket, threading, logging, traceback
|
import sys, os, socket, threading, logging, traceback
|
||||||
import secsh
|
import paramiko
|
||||||
|
|
||||||
# setup logging
|
# setup logging
|
||||||
l = logging.getLogger("secsh")
|
l = logging.getLogger("paramiko")
|
||||||
l.setLevel(logging.DEBUG)
|
l.setLevel(logging.DEBUG)
|
||||||
if len(l.handlers) == 0:
|
if len(l.handlers) == 0:
|
||||||
f = open('demo_server.log', 'w')
|
f = open('demo_server.log', 'w')
|
||||||
|
@ -12,11 +12,11 @@ if len(l.handlers) == 0:
|
||||||
lh.setFormatter(logging.Formatter('%(levelname)-.3s [%(asctime)s] %(name)s: %(message)s', '%Y%m%d:%H%M%S'))
|
lh.setFormatter(logging.Formatter('%(levelname)-.3s [%(asctime)s] %(name)s: %(message)s', '%Y%m%d:%H%M%S'))
|
||||||
l.addHandler(lh)
|
l.addHandler(lh)
|
||||||
|
|
||||||
host_key = secsh.RSAKey()
|
host_key = paramiko.RSAKey()
|
||||||
host_key.read_private_key_file('demo_host_key')
|
host_key.read_private_key_file('demo_host_key')
|
||||||
|
|
||||||
|
|
||||||
class ServerTransport(secsh.Transport):
|
class ServerTransport(paramiko.Transport):
|
||||||
def check_channel_request(self, kind, chanid):
|
def check_channel_request(self, kind, chanid):
|
||||||
if kind == 'session':
|
if kind == 'session':
|
||||||
return ServerChannel(chanid)
|
return ServerChannel(chanid)
|
||||||
|
@ -27,11 +27,11 @@ class ServerTransport(secsh.Transport):
|
||||||
return self.AUTH_SUCCESSFUL
|
return self.AUTH_SUCCESSFUL
|
||||||
return self.AUTH_FAILED
|
return self.AUTH_FAILED
|
||||||
|
|
||||||
class ServerChannel(secsh.Channel):
|
class ServerChannel(paramiko.Channel):
|
||||||
"Channel descendant that pretends to understand pty and shell requests"
|
"Channel descendant that pretends to understand pty and shell requests"
|
||||||
|
|
||||||
def __init__(self, chanid):
|
def __init__(self, chanid):
|
||||||
secsh.Channel.__init__(self, chanid)
|
paramiko.Channel.__init__(self, chanid)
|
||||||
self.event = threading.Event()
|
self.event = threading.Event()
|
||||||
|
|
||||||
def check_pty_request(self, term, width, height, pixelwidth, pixelheight, modes):
|
def check_pty_request(self, term, width, height, pixelwidth, pixelheight, modes):
|
||||||
|
|
10
kex_gex.py
10
kex_gex.py
|
@ -6,7 +6,7 @@
|
||||||
|
|
||||||
from message import Message
|
from message import Message
|
||||||
from util import inflate_long, deflate_long, generate_prime, bit_length
|
from util import inflate_long, deflate_long, generate_prime, bit_length
|
||||||
from secsh import SecshException
|
from paramiko import SSHException
|
||||||
from transport import MSG_NEWKEYS
|
from transport import MSG_NEWKEYS
|
||||||
from Crypto.Hash import SHA
|
from Crypto.Hash import SHA
|
||||||
from Crypto.Util import number
|
from Crypto.Util import number
|
||||||
|
@ -49,7 +49,7 @@ class KexGex(object):
|
||||||
return self.parse_kexdh_gex_init(m)
|
return self.parse_kexdh_gex_init(m)
|
||||||
elif ptype == MSG_KEXDH_GEX_REPLY:
|
elif ptype == MSG_KEXDH_GEX_REPLY:
|
||||||
return self.parse_kexdh_gex_reply(m)
|
return self.parse_kexdh_gex_reply(m)
|
||||||
raise SecshException('KexGex asked to handle packet type %d' % ptype)
|
raise SSHException('KexGex asked to handle packet type %d' % ptype)
|
||||||
|
|
||||||
def generate_x(self):
|
def generate_x(self):
|
||||||
# generate an "x" (1 < x < (p-1)/2).
|
# generate an "x" (1 < x < (p-1)/2).
|
||||||
|
@ -108,7 +108,7 @@ class KexGex(object):
|
||||||
# reject if p's bit length < 1024 or > 8192
|
# reject if p's bit length < 1024 or > 8192
|
||||||
bitlen = bit_length(self.p)
|
bitlen = bit_length(self.p)
|
||||||
if (bitlen < 1024) or (bitlen > 8192):
|
if (bitlen < 1024) or (bitlen > 8192):
|
||||||
raise SecshException('Server-generated gex p (don\'t ask) is out of range (%d bits)' % bitlen)
|
raise SSHException('Server-generated gex p (don\'t ask) is out of range (%d bits)' % bitlen)
|
||||||
self.transport.log(DEBUG, 'Got server p (%d bits)' % bitlen)
|
self.transport.log(DEBUG, 'Got server p (%d bits)' % bitlen)
|
||||||
self.generate_x()
|
self.generate_x()
|
||||||
# now compute e = g^x mod p
|
# now compute e = g^x mod p
|
||||||
|
@ -122,7 +122,7 @@ class KexGex(object):
|
||||||
def parse_kexdh_gex_init(self, m):
|
def parse_kexdh_gex_init(self, m):
|
||||||
self.e = m.get_mpint()
|
self.e = m.get_mpint()
|
||||||
if (self.e < 1) or (self.e > self.p - 1):
|
if (self.e < 1) or (self.e > self.p - 1):
|
||||||
raise SecshException('Client kex "e" is out of range')
|
raise SSHException('Client kex "e" is out of range')
|
||||||
self.generate_x()
|
self.generate_x()
|
||||||
K = pow(self.e, self.x, P)
|
K = pow(self.e, self.x, P)
|
||||||
key = str(self.transport.get_server_key())
|
key = str(self.transport.get_server_key())
|
||||||
|
@ -154,7 +154,7 @@ class KexGex(object):
|
||||||
self.f = m.get_mpint()
|
self.f = m.get_mpint()
|
||||||
sig = m.get_string()
|
sig = m.get_string()
|
||||||
if (self.f < 1) or (self.f > self.p - 1):
|
if (self.f < 1) or (self.f > self.p - 1):
|
||||||
raise SecshException('Server kex "f" is out of range')
|
raise SSHException('Server kex "f" is out of range')
|
||||||
K = pow(self.f, self.x, self.p)
|
K = pow(self.f, self.x, self.p)
|
||||||
# okay, build up the hash H of (V_C || V_S || I_C || I_S || K_S || min || n || max || p || g || e || f || K)
|
# okay, build up the hash H of (V_C || V_S || I_C || I_S || K_S || min || n || max || p || g || e || f || K)
|
||||||
hm = Message().add(self.transport.local_version).add(self.transport.remote_version)
|
hm = Message().add(self.transport.local_version).add(self.transport.remote_version)
|
||||||
|
|
|
@ -5,7 +5,7 @@
|
||||||
# "g" generator.
|
# "g" generator.
|
||||||
|
|
||||||
from message import Message, inflate_long
|
from message import Message, inflate_long
|
||||||
from secsh import SecshException
|
from paramiko import SSHException
|
||||||
from transport import MSG_NEWKEYS
|
from transport import MSG_NEWKEYS
|
||||||
from Crypto.Hash import SHA
|
from Crypto.Hash import SHA
|
||||||
from logging import DEBUG, INFO, WARNING, ERROR, CRITICAL
|
from logging import DEBUG, INFO, WARNING, ERROR, CRITICAL
|
||||||
|
@ -59,14 +59,14 @@ class KexGroup1(object):
|
||||||
return self.parse_kexdh_init(m)
|
return self.parse_kexdh_init(m)
|
||||||
elif not self.transport.server_mode and (ptype == MSG_KEXDH_REPLY):
|
elif not self.transport.server_mode and (ptype == MSG_KEXDH_REPLY):
|
||||||
return self.parse_kexdh_reply(m)
|
return self.parse_kexdh_reply(m)
|
||||||
raise SecshException('KexGroup1 asked to handle packet type %d' % ptype)
|
raise SSHException('KexGroup1 asked to handle packet type %d' % ptype)
|
||||||
|
|
||||||
def parse_kexdh_reply(self, m):
|
def parse_kexdh_reply(self, m):
|
||||||
# client mode
|
# client mode
|
||||||
host_key = m.get_string()
|
host_key = m.get_string()
|
||||||
self.f = m.get_mpint()
|
self.f = m.get_mpint()
|
||||||
if (self.f < 1) or (self.f > P - 1):
|
if (self.f < 1) or (self.f > P - 1):
|
||||||
raise SecshException('Server kex "f" is out of range')
|
raise SSHException('Server kex "f" is out of range')
|
||||||
sig = m.get_string()
|
sig = m.get_string()
|
||||||
K = pow(self.f, self.x, P)
|
K = pow(self.f, self.x, P)
|
||||||
# okay, build up the hash H of (V_C || V_S || I_C || I_S || K_S || e || f || K)
|
# okay, build up the hash H of (V_C || V_S || I_C || I_S || K_S || e || f || K)
|
||||||
|
@ -82,7 +82,7 @@ class KexGroup1(object):
|
||||||
# server mode
|
# server mode
|
||||||
self.e = m.get_mpint()
|
self.e = m.get_mpint()
|
||||||
if (self.e < 1) or (self.e > P - 1):
|
if (self.e < 1) or (self.e > P - 1):
|
||||||
raise SecshException('Client kex "e" is out of range')
|
raise SSHException('Client kex "e" is out of range')
|
||||||
K = pow(self.e, self.x, P)
|
K = pow(self.e, self.x, P)
|
||||||
key = str(self.transport.get_server_key())
|
key = str(self.transport.get_server_key())
|
||||||
# okay, build up the hash H of (V_C || V_S || I_C || I_S || K_S || e || f || K)
|
# okay, build up the hash H of (V_C || V_S || I_C || I_S || K_S || e || f || K)
|
||||||
|
|
|
@ -1,11 +1,11 @@
|
||||||
# implementation of a secsh "message"
|
# implementation of an SSH2 "message"
|
||||||
|
|
||||||
import string, types, struct
|
import string, types, struct
|
||||||
from util import inflate_long, deflate_long
|
from util import inflate_long, deflate_long
|
||||||
|
|
||||||
|
|
||||||
class Message(object):
|
class Message(object):
|
||||||
"represents the encoding of a secsh message"
|
"represents the encoding of an SSH2 message"
|
||||||
|
|
||||||
def __init__(self, content=''):
|
def __init__(self, content=''):
|
||||||
self.packet = content
|
self.packet = content
|
||||||
|
|
|
@ -5,7 +5,7 @@ import sys
|
||||||
if (sys.version_info[0] < 2) or ((sys.version_info[0] == 2) and (sys.version_info[1] < 3)):
|
if (sys.version_info[0] < 2) or ((sys.version_info[0] == 2) and (sys.version_info[1] < 3)):
|
||||||
raise RuntimeError('You need python 2.3 for this module.')
|
raise RuntimeError('You need python 2.3 for this module.')
|
||||||
|
|
||||||
class SecshException(Exception):
|
class SSHException(Exception):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
10
setup.py
10
setup.py
|
@ -6,20 +6,22 @@ Emphasis is on using SSH2 as an alternative to SSL for making secure
|
||||||
connections between pyton scripts. All major ciphers and hash methods
|
connections between pyton scripts. All major ciphers and hash methods
|
||||||
are supported.
|
are supported.
|
||||||
|
|
||||||
|
(Previous name: secsh)
|
||||||
|
|
||||||
Required packages:
|
Required packages:
|
||||||
pyCrypto
|
pyCrypto
|
||||||
'''
|
'''
|
||||||
|
|
||||||
setup(name = "secsh",
|
setup(name = "paramiko",
|
||||||
version = "0.1-charmander",
|
version = "0.1-charmander",
|
||||||
description = "SSH2 protocol library",
|
description = "SSH2 protocol library",
|
||||||
author = "Robey Pointer",
|
author = "Robey Pointer",
|
||||||
author_email = "robey@lag.net",
|
author_email = "robey@lag.net",
|
||||||
url = "http://www.lag.net/~robey/secsh/",
|
url = "http://www.lag.net/~robey/paramiko/",
|
||||||
py_modules = [ 'secsh', 'transport', 'auth_transport', 'channel',
|
py_modules = [ 'paramiko', 'transport', 'auth_transport', 'channel',
|
||||||
'message', 'util', 'ber', 'kex_group1', 'kex_gex',
|
'message', 'util', 'ber', 'kex_group1', 'kex_gex',
|
||||||
'rsakey', 'dsskey' ],
|
'rsakey', 'dsskey' ],
|
||||||
download_url = 'http://www.lag.net/~robey/secsh/secsh-0.1-charmander.zip',
|
download_url = 'http://www.lag.net/~robey/paramiko/paramiko-0.1-charmander.zip',
|
||||||
license = 'LGPL',
|
license = 'LGPL',
|
||||||
platforms = 'Posix; MacOS X; Windows',
|
platforms = 'Posix; MacOS X; Windows',
|
||||||
classifiers = [ 'Development Status :: 3 - Alpha',
|
classifiers = [ 'Development Status :: 3 - Alpha',
|
||||||
|
|
44
transport.py
44
transport.py
|
@ -14,7 +14,7 @@ MSG_CHANNEL_OPEN, MSG_CHANNEL_OPEN_SUCCESS, MSG_CHANNEL_OPEN_FAILURE, \
|
||||||
import sys, os, string, threading, socket, logging, struct
|
import sys, os, string, threading, socket, logging, struct
|
||||||
from message import Message
|
from message import Message
|
||||||
from channel import Channel
|
from channel import Channel
|
||||||
from secsh import SecshException
|
from paramiko import SSHException
|
||||||
from util import format_binary, safe_string, inflate_long, deflate_long, tb_strings
|
from util import format_binary, safe_string, inflate_long, deflate_long, tb_strings
|
||||||
from rsakey import RSAKey
|
from rsakey import RSAKey
|
||||||
from dsskey import DSSKey
|
from dsskey import DSSKey
|
||||||
|
@ -131,7 +131,7 @@ class BaseTransport(threading.Thread):
|
||||||
self.channels = { } # (id -> Channel)
|
self.channels = { } # (id -> Channel)
|
||||||
self.channel_events = { } # (id -> Event)
|
self.channel_events = { } # (id -> Event)
|
||||||
self.channel_counter = 1
|
self.channel_counter = 1
|
||||||
self.logger = logging.getLogger('secsh.transport')
|
self.logger = logging.getLogger('paramiko.transport')
|
||||||
self.window_size = 65536
|
self.window_size = 65536
|
||||||
self.max_packet_size = 2048
|
self.max_packet_size = 2048
|
||||||
self.ultra_debug = 0
|
self.ultra_debug = 0
|
||||||
|
@ -167,7 +167,7 @@ class BaseTransport(threading.Thread):
|
||||||
|
|
||||||
def __repr__(self):
|
def __repr__(self):
|
||||||
if not self.active:
|
if not self.active:
|
||||||
return '<secsh.Transport (unconnected)>'
|
return '<paramiko.Transport (unconnected)>'
|
||||||
out = '<sesch.Transport'
|
out = '<sesch.Transport'
|
||||||
#if self.remote_version != '':
|
#if self.remote_version != '':
|
||||||
# out += ' (server version "%s")' % self.remote_version
|
# out += ' (server version "%s")' % self.remote_version
|
||||||
|
@ -202,7 +202,7 @@ class BaseTransport(threading.Thread):
|
||||||
def get_remote_server_key(self):
|
def get_remote_server_key(self):
|
||||||
'returns (type, key) where type is like "ssh-rsa" and key is an opaque string'
|
'returns (type, key) where type is like "ssh-rsa" and key is an opaque string'
|
||||||
if (not self.active) or (not self.initial_kex_done):
|
if (not self.active) or (not self.initial_kex_done):
|
||||||
raise SecshException('No existing session')
|
raise SSHException('No existing session')
|
||||||
key_msg = Message(self.host_key)
|
key_msg = Message(self.host_key)
|
||||||
key_type = key_msg.get_string()
|
key_type = key_msg.get_string()
|
||||||
return key_type, self.host_key
|
return key_type, self.host_key
|
||||||
|
@ -323,7 +323,7 @@ class BaseTransport(threading.Thread):
|
||||||
# leftover contains decrypted bytes from the first block (after the length field)
|
# leftover contains decrypted bytes from the first block (after the length field)
|
||||||
leftover = header[4:]
|
leftover = header[4:]
|
||||||
if (packet_size - len(leftover)) % self.block_size_in != 0:
|
if (packet_size - len(leftover)) % self.block_size_in != 0:
|
||||||
raise SecshException('Invalid packet blocking')
|
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)]
|
packet = buffer[:packet_size - len(leftover)]
|
||||||
post_packet = buffer[packet_size - len(leftover):]
|
post_packet = buffer[packet_size - len(leftover):]
|
||||||
|
@ -337,7 +337,7 @@ class BaseTransport(threading.Thread):
|
||||||
mac_payload = struct.pack('>II', self.sequence_number_in, packet_size) + packet
|
mac_payload = struct.pack('>II', self.sequence_number_in, packet_size) + packet
|
||||||
my_mac = HMAC.HMAC(self.mac_key_in, mac_payload, self.remote_mac_engine).digest()[:self.remote_mac_len]
|
my_mac = HMAC.HMAC(self.mac_key_in, mac_payload, self.remote_mac_engine).digest()[:self.remote_mac_len]
|
||||||
if my_mac != mac:
|
if my_mac != mac:
|
||||||
raise SecshException('Mismatched MAC')
|
raise SSHException('Mismatched MAC')
|
||||||
padding = ord(packet[0])
|
padding = ord(packet[0])
|
||||||
payload = packet[1:packet_size - padding + 1]
|
payload = packet[1:packet_size - padding + 1]
|
||||||
randpool.add_event(packet[packet_size - padding + 1])
|
randpool.add_event(packet[packet_size - padding + 1])
|
||||||
|
@ -360,7 +360,7 @@ class BaseTransport(threading.Thread):
|
||||||
# comply, then just drop the connection
|
# comply, then just drop the connection
|
||||||
self.received_packets_overflow += 1
|
self.received_packets_overflow += 1
|
||||||
if self.received_packets_overflow >= 20:
|
if self.received_packets_overflow >= 20:
|
||||||
raise SecshException('Remote transport is ignoring rekey requests')
|
raise SSHException('Remote transport is ignoring rekey requests')
|
||||||
|
|
||||||
return ord(payload[0]), msg
|
return ord(payload[0]), msg
|
||||||
|
|
||||||
|
@ -379,9 +379,9 @@ class BaseTransport(threading.Thread):
|
||||||
else:
|
else:
|
||||||
key = None
|
key = None
|
||||||
if (key == None) or not key.valid:
|
if (key == None) or not key.valid:
|
||||||
raise SecshException('Unknown host key type')
|
raise SSHException('Unknown host key type')
|
||||||
if not key.verify_ssh_sig(self.H, Message(sig)):
|
if not key.verify_ssh_sig(self.H, Message(sig)):
|
||||||
raise SecshException('Signature verification (%s) failed. Boo. Robey should debug this.' % self.host_key_type)
|
raise SSHException('Signature verification (%s) failed. Boo. Robey should debug this.' % self.host_key_type)
|
||||||
self.host_key = host_key
|
self.host_key = host_key
|
||||||
|
|
||||||
def compute_key(self, id, nbytes):
|
def compute_key(self, id, nbytes):
|
||||||
|
@ -404,7 +404,7 @@ class BaseTransport(threading.Thread):
|
||||||
|
|
||||||
def get_cipher(self, name, key, iv):
|
def get_cipher(self, name, key, iv):
|
||||||
if not self.cipher_info.has_key(name):
|
if not self.cipher_info.has_key(name):
|
||||||
raise SecshException('Unknown client cipher ' + 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):
|
def run(self):
|
||||||
|
@ -429,7 +429,7 @@ class BaseTransport(threading.Thread):
|
||||||
continue
|
continue
|
||||||
if self.expected_packet != 0:
|
if self.expected_packet != 0:
|
||||||
if ptype != self.expected_packet:
|
if ptype != self.expected_packet:
|
||||||
raise SecshException('Expecting packet %d, got %d' % (self.expected_packet, ptype))
|
raise SSHException('Expecting packet %d, got %d' % (self.expected_packet, ptype))
|
||||||
self.expected_packet = 0
|
self.expected_packet = 0
|
||||||
if (ptype >= 30) and (ptype <= 39):
|
if (ptype >= 30) and (ptype <= 39):
|
||||||
self.kex_engine.parse_next(ptype, m)
|
self.kex_engine.parse_next(ptype, m)
|
||||||
|
@ -447,7 +447,7 @@ class BaseTransport(threading.Thread):
|
||||||
msg.add_byte(chr(MSG_UNIMPLEMENTED))
|
msg.add_byte(chr(MSG_UNIMPLEMENTED))
|
||||||
msg.add_int(m.seqno)
|
msg.add_int(m.seqno)
|
||||||
self.send_message(msg)
|
self.send_message(msg)
|
||||||
except SecshException, e:
|
except SSHException, e:
|
||||||
self.log(DEBUG, 'Exception: ' + str(e))
|
self.log(DEBUG, 'Exception: ' + str(e))
|
||||||
self.log(DEBUG, tb_strings())
|
self.log(DEBUG, tb_strings())
|
||||||
except EOFError, e:
|
except EOFError, e:
|
||||||
|
@ -479,7 +479,7 @@ class BaseTransport(threading.Thread):
|
||||||
return 1
|
return 1
|
||||||
|
|
||||||
def negotiate_keys(self, m):
|
def negotiate_keys(self, m):
|
||||||
# throws SecshException on anything unusual
|
# throws SSHException on anything unusual
|
||||||
if self.local_kex_init == None:
|
if self.local_kex_init == None:
|
||||||
# remote side wants to renegotiate
|
# remote side wants to renegotiate
|
||||||
self.send_kex_init()
|
self.send_kex_init()
|
||||||
|
@ -499,7 +499,7 @@ class BaseTransport(threading.Thread):
|
||||||
break
|
break
|
||||||
self.log(DEBUG, 'Banner: ' + buffer)
|
self.log(DEBUG, 'Banner: ' + buffer)
|
||||||
if buffer[:4] != 'SSH-':
|
if buffer[:4] != 'SSH-':
|
||||||
raise SecshException('Indecipherable protocol version "' + buffer + '"')
|
raise SSHException('Indecipherable protocol version "' + buffer + '"')
|
||||||
# save this server version string for later
|
# save this server version string for later
|
||||||
self.remote_version = buffer
|
self.remote_version = buffer
|
||||||
# pull off any attached comment
|
# pull off any attached comment
|
||||||
|
@ -511,11 +511,11 @@ class BaseTransport(threading.Thread):
|
||||||
# parse out version string and make sure it matches
|
# parse out version string and make sure it matches
|
||||||
segs = buffer.split('-', 2)
|
segs = buffer.split('-', 2)
|
||||||
if len(segs) < 3:
|
if len(segs) < 3:
|
||||||
raise SecshException('Invalid SSH banner')
|
raise SSHException('Invalid SSH banner')
|
||||||
version = segs[1]
|
version = segs[1]
|
||||||
client = segs[2]
|
client = segs[2]
|
||||||
if version != '1.99' and version != '2.0':
|
if version != '1.99' and version != '2.0':
|
||||||
raise SecshException('Incompatible version (%s instead of 2.0)' % (version,))
|
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):
|
def send_kex_init(self):
|
||||||
|
@ -566,7 +566,7 @@ class BaseTransport(threading.Thread):
|
||||||
# no compression support (yet?)
|
# no compression support (yet?)
|
||||||
if (not('none' in client_compress_algo_list) or
|
if (not('none' in client_compress_algo_list) or
|
||||||
not('none' in server_compress_algo_list)):
|
not('none' in server_compress_algo_list)):
|
||||||
raise SecshException('Incompatible ssh peer.')
|
raise SSHException('Incompatible ssh peer.')
|
||||||
|
|
||||||
# as a server, we pick the first item in the client's list that we support.
|
# as a server, we pick the first item in the client's list that we support.
|
||||||
# as a client, we pick the first item in our list that the server supports.
|
# as a client, we pick the first item in our list that the server supports.
|
||||||
|
@ -575,7 +575,7 @@ class BaseTransport(threading.Thread):
|
||||||
else:
|
else:
|
||||||
agreed_kex = filter(kex_algo_list.__contains__, self.preferred_kex)
|
agreed_kex = filter(kex_algo_list.__contains__, self.preferred_kex)
|
||||||
if len(agreed_kex) == 0:
|
if len(agreed_kex) == 0:
|
||||||
raise SecshException('Incompatible ssh peer (no acceptable kex algorithm)')
|
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:
|
if self.server_mode:
|
||||||
|
@ -583,10 +583,10 @@ class BaseTransport(threading.Thread):
|
||||||
else:
|
else:
|
||||||
agreed_keys = filter(server_key_algo_list.__contains__, self.preferred_keys)
|
agreed_keys = filter(server_key_algo_list.__contains__, self.preferred_keys)
|
||||||
if len(agreed_keys) == 0:
|
if len(agreed_keys) == 0:
|
||||||
raise SecshException('Incompatible ssh peer (no acceptable host key)')
|
raise SSHException('Incompatible ssh peer (no acceptable host key)')
|
||||||
self.host_key_type = agreed_keys[0]
|
self.host_key_type = agreed_keys[0]
|
||||||
if self.server_mode and (self.get_server_key() is None):
|
if self.server_mode and (self.get_server_key() is None):
|
||||||
raise SecshException('Incompatible ssh peer (can\'t match requested host key type)')
|
raise SSHException('Incompatible ssh peer (can\'t match requested host key type)')
|
||||||
|
|
||||||
if self.server_mode:
|
if self.server_mode:
|
||||||
agreed_local_ciphers = filter(self.preferred_ciphers.__contains__,
|
agreed_local_ciphers = filter(self.preferred_ciphers.__contains__,
|
||||||
|
@ -599,7 +599,7 @@ class BaseTransport(threading.Thread):
|
||||||
agreed_remote_ciphers = filter(server_encrypt_algo_list.__contains__,
|
agreed_remote_ciphers = filter(server_encrypt_algo_list.__contains__,
|
||||||
self.preferred_ciphers)
|
self.preferred_ciphers)
|
||||||
if (len(agreed_local_ciphers) == 0) or (len(agreed_remote_ciphers) == 0):
|
if (len(agreed_local_ciphers) == 0) or (len(agreed_remote_ciphers) == 0):
|
||||||
raise SecshException('Incompatible ssh server (no acceptable ciphers)')
|
raise SSHException('Incompatible ssh server (no acceptable ciphers)')
|
||||||
self.local_cipher = agreed_local_ciphers[0]
|
self.local_cipher = agreed_local_ciphers[0]
|
||||||
self.remote_cipher = agreed_remote_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))
|
||||||
|
@ -611,7 +611,7 @@ class BaseTransport(threading.Thread):
|
||||||
agreed_local_macs = filter(client_mac_algo_list.__contains__, self.preferred_macs)
|
agreed_local_macs = filter(client_mac_algo_list.__contains__, self.preferred_macs)
|
||||||
agreed_remote_macs = filter(server_mac_algo_list.__contains__, self.preferred_macs)
|
agreed_remote_macs = filter(server_mac_algo_list.__contains__, self.preferred_macs)
|
||||||
if (len(agreed_local_macs) == 0) or (len(agreed_remote_macs) == 0):
|
if (len(agreed_local_macs) == 0) or (len(agreed_remote_macs) == 0):
|
||||||
raise SecshException('Incompatible ssh server (no acceptable macs)')
|
raise SSHException('Incompatible ssh server (no acceptable macs)')
|
||||||
self.local_mac = agreed_local_macs[0]
|
self.local_mac = agreed_local_macs[0]
|
||||||
self.remote_mac = agreed_remote_macs[0]
|
self.remote_mac = agreed_remote_macs[0]
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue