[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:
Robey Pointer 2003-11-10 08:49:50 +00:00
parent 2ff9f46751
commit 11815d4d83
14 changed files with 80 additions and 84 deletions

View File

@ -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

View File

@ -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
View File

@ -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
View File

@ -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

View File

@ -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)

View File

@ -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
View File

@ -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:

View File

@ -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):

View File

@ -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)

View File

@ -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)

View File

@ -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

View File

@ -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

View File

@ -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',

View File

@ -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]