diff options
-rw-r--r-- | Makefile | 3 | ||||
-rwxr-xr-x | demo_server.py | 25 | ||||
-rw-r--r-- | paramiko/__init__.py | 2 | ||||
-rw-r--r-- | paramiko/auth_transport.py | 189 | ||||
-rw-r--r-- | paramiko/dsskey.py | 58 | ||||
-rw-r--r-- | paramiko/kex_gex.py | 2 | ||||
-rw-r--r-- | paramiko/kex_group1.py | 2 | ||||
-rw-r--r-- | paramiko/pkey.py | 79 | ||||
-rw-r--r-- | paramiko/rsakey.py | 44 | ||||
-rw-r--r-- | paramiko/transport.py | 92 | ||||
-rw-r--r-- | user_rsa_key | 15 | ||||
-rw-r--r-- | user_rsa_key.pub | 1 |
12 files changed, 324 insertions, 188 deletions
@@ -8,8 +8,9 @@ RELEASE=charmander release: python ./setup.py sdist --formats=zip -docs: +docs: always epydoc -o docs/ paramiko +always: # places where the version number is stored: # diff --git a/demo_server.py b/demo_server.py index 7fd25ad3..65b45cf7 100755 --- a/demo_server.py +++ b/demo_server.py @@ -1,6 +1,6 @@ #!/usr/bin/python -import sys, os, socket, threading, logging, traceback +import sys, os, socket, threading, logging, traceback, base64 import paramiko # setup logging @@ -18,10 +18,14 @@ if len(l.handlers) == 0: host_key = paramiko.DSSKey() host_key.read_private_key_file('demo_dss_key') -print 'Read key: ' + paramiko.hexify(host_key.get_fingerprint()) +print 'Read key: ' + paramiko.util.hexify(host_key.get_fingerprint()) class ServerTransport(paramiko.Transport): + # 'data' is the output of base64.encodestring(str(key)) + data = 'AAAAB3NzaC1yc2EAAAABIwAAAIEAyO4it3fHlmGZWJaGrfeHOVY7RWO3P9M7hpfAu7jJ2d7eothvfeuoRFtJwhUmZDluRdFyhFY/hFAh76PJKGAusIqIQKlkJxMCKDqIexkgHAfID/6mqvmnSJf0b5W8v5h2pI/stOSwTQ+pxVhwJ9ctYDhRSlF0iTUWT10hcuO4Ks8=' + good_pub_key = paramiko.RSAKey(data=base64.decodestring(data)) + def check_channel_request(self, kind, chanid): if kind == 'session': return ServerChannel(chanid) @@ -32,6 +36,11 @@ class ServerTransport(paramiko.Transport): return self.AUTH_SUCCESSFUL return self.AUTH_FAILED + def check_auth_publickey(self, username, key): + if (username == 'robey') and (key == self.good_pub_key): + return self.AUTH_SUCCESSFUL + return self.AUTH_FAILED + class ServerChannel(paramiko.Channel): "Channel descendant that pretends to understand pty and shell requests" @@ -79,11 +88,13 @@ try: t.add_server_key(host_key) t.ultra_debug = 0 t.start_server(event) - # print repr(t) - event.wait(10) - if not t.is_active(): - print '*** SSH negotiation failed.' - sys.exit(1) + while 1: + event.wait(0.1) + if not t.is_active(): + print '*** SSH negotiation failed.' + sys.exit(1) + if event.isSet(): + break # print repr(t) # wait for auth diff --git a/paramiko/__init__.py b/paramiko/__init__.py index 81d41edb..76531611 100644 --- a/paramiko/__init__.py +++ b/paramiko/__init__.py @@ -41,5 +41,5 @@ class DSSKey (dsskey.DSSKey): __all__ = [ 'Transport', 'Channel', 'RSAKey', 'DSSKey', 'transport', - 'auth_transport', 'channel', 'rsakey', 'ddskey', 'util', + 'auth_transport', 'channel', 'rsakey', 'dsskey', 'util', 'SSHException' ] diff --git a/paramiko/auth_transport.py b/paramiko/auth_transport.py index 34f11744..5c4516f5 100644 --- a/paramiko/auth_transport.py +++ b/paramiko/auth_transport.py @@ -13,7 +13,10 @@ _DISCONNECT_SERVICE_NOT_AVAILABLE, _DISCONNECT_AUTH_CANCELLED_BY_USER, \ class Transport (BaseTransport): - "BaseTransport with the auth framework hooked up" + """ + Subclass of L{BaseTransport} that handles authentication. This separation + keeps either class file from being too unwieldy. + """ AUTH_SUCCESSFUL, AUTH_PARTIALLY_SUCCESSFUL, AUTH_FAILED = range(3) @@ -53,15 +56,23 @@ class Transport (BaseTransport): """ return self.authenticated and self.active - def _request_auth(self): - m = Message() - m.add_byte(chr(_MSG_SERVICE_REQUEST)) - m.add_string('ssh-userauth') - self._send_message(m) - def auth_key(self, username, key, event): + """ + Authenticate to the server using a private key. The key is used to + sign data from the server, so it must include the private part. The + given L{event} is triggered on success or failure. On success, + L{is_authenticated} will return C{True}. + + @param username: the username to authenticate as. + @type username: string + @param key: the private key to authenticate with. + @type key: L{PKey <pkey.PKey>} + @param event: an event to trigger when the authentication attempt is + complete (whether it was successful or not) + @type event: threading.Event + """ 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 authenticate unless we're on a secure link raise SSHException('No existing session') try: self.lock.acquire() @@ -74,7 +85,20 @@ class Transport (BaseTransport): self.lock.release() def auth_password(self, username, password, event): - 'authenticate using a password; event is triggered on success or fail' + """ + Authenticate to the server using a password. The username and password + are sent over an encrypted link, and the given L{event} is triggered on + success or failure. On success, L{is_authenticated} will return + C{True}. + + @param username: the username to authenticate as. + @type username: string + @param password: the password to authenticate with. + @type password: string + @param event: an event to trigger when the authentication attempt is + complete (whether it was successful or not) + @type event: threading.Event + """ 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 raise SSHException('No existing session') @@ -88,7 +112,58 @@ class Transport (BaseTransport): finally: self.lock.release() - def disconnect_service_not_available(self): + def get_allowed_auths(self, username): + "override me!" + return 'password' + + def check_auth_none(self, username): + "override me! return int ==> auth status" + return self.AUTH_FAILED + + def check_auth_password(self, username, password): + "override me! return int ==> auth status" + return self.AUTH_FAILED + + def check_auth_publickey(self, username, key): + """ + I{(subclass override)} + Determine if a given key supplied by the client is acceptable for use + in authentication. You should override this method in server mode to + check the username and key and decide if you would accept a signature + made using this key. + + Return C{AUTH_FAILED} if the key is not accepted, C{AUTH_SUCCESSFUL} + if the key is accepted and completes the authentication, or + C{AUTH_PARTIALLY_SUCCESSFUL} if your authentication is stateful, and + this key is accepted for authentication, but more authentication is + required. (In this latter case, L{get_allowed_auths} will be called + to report to the client what options it has for continuing the + authentication.) + + The default implementation always returns C{AUTH_FAILED}. + + @param username: the username of the authenticating client. + @type username: string + @param key: the key object provided by the client. + @type key: L{PKey <pkey.PKey>} + @return: C{AUTH_FAILED} if the client can't authenticate with this key; + C{AUTH_SUCCESSFUL} if it can; C{AUTH_PARTIALLY_SUCCESSFUL} if it can + authenticate with this key but must continue with authentication. + @rtype: int + """ + return self.AUTH_FAILED + + + ### internals... + + + def _request_auth(self): + m = Message() + m.add_byte(chr(_MSG_SERVICE_REQUEST)) + m.add_string('ssh-userauth') + self._send_message(m) + + def _disconnect_service_not_available(self): m = Message() m.add_byte(chr(_MSG_DISCONNECT)) m.add_int(_DISCONNECT_SERVICE_NOT_AVAILABLE) @@ -97,7 +172,7 @@ class Transport (BaseTransport): self._send_message(m) self.close() - def disconnect_no_more_auth(self): + def _disconnect_no_more_auth(self): m = Message() m.add_byte(chr(_MSG_DISCONNECT)) m.add_int(_DISCONNECT_NO_MORE_AUTH_METHODS_AVAILABLE) @@ -106,7 +181,19 @@ class Transport (BaseTransport): self._send_message(m) self.close() - def parse_service_request(self, m): + def _get_session_blob(self, key, service, username): + m = Message() + m.add_string(self.session_id) + m.add_byte(chr(_MSG_USERAUTH_REQUEST)) + m.add_string(username) + m.add_string(service) + m.add_string('publickey') + m.add_boolean(1) + m.add_string(key.get_name()) + m.add_string(str(key)) + return str(m) + + def _parse_service_request(self, m): service = m.get_string() if self.server_mode and (service == 'ssh-userauth'): # accepted @@ -116,9 +203,9 @@ class Transport (BaseTransport): self._send_message(m) return # dunno this one - self.disconnect_service_not_available() + self._disconnect_service_not_available() - def parse_service_accept(self, m): + def _parse_service_accept(self, m): service = m.get_string() if service == 'ssh-userauth': self._log(DEBUG, 'userauth is OK') @@ -134,30 +221,16 @@ class Transport (BaseTransport): m.add_boolean(1) m.add_string(self.private_key.get_name()) m.add_string(str(self.private_key)) - m.add_string(self.private_key.sign_ssh_session(self.randpool, self.H, self.username)) + blob = self._get_session_blob(self.private_key, 'ssh-connection', self.username) + sig = self.private_key.sign_ssh_data(self.randpool, blob) + m.add_string(str(sig)) else: raise SSHException('Unknown auth method "%s"' % self.auth_method) self._send_message(m) else: self._log(DEBUG, 'Service request "%s" accepted (?)' % service) - def get_allowed_auths(self, username): - "override me!" - return 'password' - - def check_auth_none(self, username): - "override me! return int ==> auth status" - return self.AUTH_FAILED - - def check_auth_password(self, username, password): - "override me! return int ==> auth status" - return self.AUTH_FAILED - - def check_auth_publickey(self, username, key): - "override me! return int ==> auth status" - return self.AUTH_FAILED - - def parse_userauth_request(self, m): + def _parse_userauth_request(self, m): if not self.server_mode: # er, uh... what? m = Message() @@ -174,11 +247,11 @@ class Transport (BaseTransport): method = m.get_string() self._log(DEBUG, 'Auth request (type=%s) service=%s, username=%s' % (method, service, username)) if service != 'ssh-connection': - self.disconnect_service_not_available() + self._disconnect_service_not_available() return if (self.auth_username is not None) and (self.auth_username != username): self._log(DEBUG, 'Auth rejected because the client attempted to change username in mid-flight') - self.disconnect_no_more_auth() + self._disconnect_no_more_auth() return if method == 'none': result = self.check_auth_none(username) @@ -194,8 +267,32 @@ class Transport (BaseTransport): else: result = self.check_auth_password(username, password) elif method == 'publickey': - # FIXME - result = self.check_auth_none(username) + sig_attached = m.get_boolean() + keytype = m.get_string() + keyblob = m.get_string() + key = self._key_from_blob(keytype, keyblob) + if (key is None) or (not key.valid): + self._log(DEBUG, 'Auth rejected: unsupported or mangled public key') + self._disconnect_no_more_auth() + return + # first check if this key is okay... if not, we can skip the verify + result = self.check_auth_publickey(username, key) + if result != self.AUTH_FAILED: + # key is okay, verify it + if not sig_attached: + # client wants to know if this key is acceptable, before it + # signs anything... send special "ok" message + m = Message() + m.add_byte(chr(_MSG_USERAUTH_PK_OK)) + m.add_string(keytype) + m.add_string(keyblob) + self._send_message(m) + return + sig = Message(m.get_string()) + blob = self._get_session_blob(key, service, username) + if not key.verify_ssh_sig(blob, sig): + self._log(DEBUG, 'Auth rejected: invalid signature') + result = self.AUTH_FAILED else: result = self.check_auth_none(username) # okay, send result @@ -215,15 +312,15 @@ class Transport (BaseTransport): self.auth_fail_count += 1 self._send_message(m) if self.auth_fail_count >= 10: - self.disconnect_no_more_auth() + self._disconnect_no_more_auth() - def parse_userauth_success(self, m): + def _parse_userauth_success(self, m): self._log(INFO, 'Authentication successful!') self.authenticated = True if self.auth_event != None: self.auth_event.set() - def parse_userauth_failure(self, m): + def _parse_userauth_failure(self, m): authlist = m.get_list() partial = m.get_boolean() if partial: @@ -237,7 +334,7 @@ class Transport (BaseTransport): if self.auth_event != None: self.auth_event.set() - def parse_userauth_banner(self, m): + def _parse_userauth_banner(self, m): banner = m.get_string() lang = m.get_string() self._log(INFO, 'Auth banner: ' + banner) @@ -245,11 +342,11 @@ class Transport (BaseTransport): _handler_table = BaseTransport._handler_table.copy() _handler_table.update({ - _MSG_SERVICE_REQUEST: parse_service_request, - _MSG_SERVICE_ACCEPT: parse_service_accept, - _MSG_USERAUTH_REQUEST: parse_userauth_request, - _MSG_USERAUTH_SUCCESS: parse_userauth_success, - _MSG_USERAUTH_FAILURE: parse_userauth_failure, - _MSG_USERAUTH_BANNER: parse_userauth_banner, + _MSG_SERVICE_REQUEST: _parse_service_request, + _MSG_SERVICE_ACCEPT: _parse_service_accept, + _MSG_USERAUTH_REQUEST: _parse_userauth_request, + _MSG_USERAUTH_SUCCESS: _parse_userauth_success, + _MSG_USERAUTH_FAILURE: _parse_userauth_failure, + _MSG_USERAUTH_BANNER: _parse_userauth_banner, }) diff --git a/paramiko/dsskey.py b/paramiko/dsskey.py index 3eca8589..e0006e53 100644 --- a/paramiko/dsskey.py +++ b/paramiko/dsskey.py @@ -3,7 +3,6 @@ import base64 from ssh_exception import SSHException from message import Message -from transport import _MSG_USERAUTH_REQUEST from util import inflate_long, deflate_long from Crypto.PublicKey import DSA from Crypto.Hash import SHA @@ -14,9 +13,11 @@ from util import format_binary class DSSKey (PKey): - def __init__(self, msg=None): + def __init__(self, msg=None, data=None): self.valid = 0 - if (msg == None) or (msg.get_string() != 'ssh-dss'): + if (msg is None) and (data is not None): + msg = Message(data) + if (msg is None) or (msg.get_string() != 'ssh-dss'): return self.p = msg.get_mpint() self.q = msg.get_mpint() @@ -36,9 +37,33 @@ class DSSKey (PKey): m.add_mpint(self.y) return str(m) + def __hash__(self): + h = hash(self.get_name()) + h = h * 37 + hash(self.p) + h = h * 37 + hash(self.q) + h = h * 37 + hash(self.g) + h = h * 37 + hash(self.y) + # h might be a long by now... + return hash(h) + def get_name(self): return 'ssh-dss' + def sign_ssh_data(self, randpool, data): + hash = SHA.new(data).digest() + dss = DSA.construct((long(self.y), long(self.g), long(self.p), long(self.q), long(self.x))) + # generate a suitable k + qsize = len(deflate_long(self.q, 0)) + while 1: + k = inflate_long(randpool.get_bytes(qsize), 1) + if (k > 2) and (k < self.q): + break + r, s = dss.sign(inflate_long(hash, 1), k) + m = Message() + m.add_string('ssh-dss') + m.add_string(deflate_long(r, 0) + deflate_long(s, 0)) + return m + def verify_ssh_sig(self, data, msg): if not self.valid: return 0 @@ -59,21 +84,6 @@ class DSSKey (PKey): dss = DSA.construct((long(self.y), long(self.g), long(self.p), long(self.q))) return dss.verify(sigM, (sigR, sigS)) - def sign_ssh_data(self, randpool, data): - hash = SHA.new(data).digest() - dss = DSA.construct((long(self.y), long(self.g), long(self.p), long(self.q), long(self.x))) - # generate a suitable k - qsize = len(deflate_long(self.q, 0)) - while 1: - k = inflate_long(randpool.get_bytes(qsize), 1) - if (k > 2) and (k < self.q): - break - r, s = dss.sign(inflate_long(hash, 1), k) - m = Message() - m.add_string('ssh-dss') - m.add_string(deflate_long(r, 0) + deflate_long(s, 0)) - return str(m) - def read_private_key_file(self, filename): # private key file contains: # DSAPrivateKey = { version = 0, p, q, g, y, x } @@ -94,15 +104,3 @@ class DSSKey (PKey): self.x = keylist[5] self.size = len(deflate_long(self.p, 0)) self.valid = 1 - - def sign_ssh_session(self, randpool, sid, username): - m = Message() - m.add_string(sid) - m.add_byte(chr(_MSG_USERAUTH_REQUEST)) - m.add_string(username) - m.add_string('ssh-connection') - m.add_string('publickey') - m.add_boolean(1) - m.add_string('ssh-dss') - m.add_string(str(self)) - return self.sign_ssh_data(randpool, str(m)) diff --git a/paramiko/kex_gex.py b/paramiko/kex_gex.py index f14c7864..3fcb8f26 100644 --- a/paramiko/kex_gex.py +++ b/paramiko/kex_gex.py @@ -145,7 +145,7 @@ class KexGex(object): m.add_byte(chr(_MSG_KEXDH_GEX_REPLY)) m.add_string(key) m.add_mpint(self.f) - m.add_string(sig) + m.add_string(str(sig)) self.transport._send_message(m) self.transport._activate_outbound() diff --git a/paramiko/kex_group1.py b/paramiko/kex_group1.py index 31123269..13585135 100644 --- a/paramiko/kex_group1.py +++ b/paramiko/kex_group1.py @@ -97,6 +97,6 @@ class KexGroup1(object): m.add_byte(chr(_MSG_KEXDH_REPLY)) m.add_string(key) m.add_mpint(self.f) - m.add_string(sig) + m.add_string(str(sig)) self.transport._send_message(m) self.transport._activate_outbound() diff --git a/paramiko/pkey.py b/paramiko/pkey.py index c4c7599d..6ad2845d 100644 --- a/paramiko/pkey.py +++ b/paramiko/pkey.py @@ -7,28 +7,50 @@ class PKey (object): Base class for public keys. """ - def __init__(self, msg=None): + def __init__(self, msg=None, data=None): """ - Create a new instance of this public key type. If C{msg} is not - C{None}, the key's public part(s) will be filled in from the - message. + Create a new instance of this public key type. If C{msg} is given, + the key's public part(s) will be filled in from the message. If + C{data} is given, the key's public part(s) will be filled in from + the string. @param msg: an optional SSH L{Message} containing a public key of this type. @type msg: L{Message} + @param data: an optional string containing a public key of this type + @type data: string """ pass def __str__(self): """ Return a string of an SSH L{Message} made up of the public part(s) of - this key. + this key. This string is suitable for passing to L{__init__} to + re-create the key object later. @return: string representation of an SSH key message. @rtype: string """ return '' + def __cmp__(self, other): + """ + Compare this key to another. Returns 0 if this key is equivalent to + the given key, or non-0 if they are different. Only the public parts + of the key are compared, so a public key will compare equal to its + corresponding private key. + + @param other: key to compare to. + @type other: L{PKey} + @return: 0 if the two keys are equivalent, non-0 otherwise. + @rtype: int + """ + hs = hash(self) + ho = hash(other) + if hs != ho: + return cmp(hs, ho) + return cmp(str(self), str(other)) + def get_name(self): """ Return the name of this private key implementation. @@ -50,6 +72,20 @@ class PKey (object): """ return MD5.new(str(self)).digest() + def sign_ssh_data(self, randpool, data): + """ + Sign a blob of data with this private key, and return a L{Message} + representing an SSH signature message. + + @param randpool: a secure random number generator. + @type randpool: L{Crypto.Util.randpool.RandomPool} + @param data: the data to sign. + @type data: string + @return: an SSH signature message. + @rtype: L{Message} + """ + return '' + def verify_ssh_sig(self, data, msg): """ Given a blob of data, and an SSH message representing a signature of @@ -65,23 +101,6 @@ class PKey (object): """ return False - def sign_ssh_data(self, randpool, data): - """ - Sign a blob of data with this private key, and return a string - representing an SSH signature message. - - @bug: It would be cleaner for this method to return a L{Message} - object, so it would be complementary to L{verify_ssh_sig}. FIXME. - - @param randpool: a secure random number generator. - @type randpool: L{Crypto.Util.randpool.RandomPool} - @param data: the data to sign. - @type data: string - @return: string representation of an SSH signature message. - @rtype: string - """ - return '' - def read_private_key_file(self, filename): """ Read private key contents from a file into this object. @@ -94,19 +113,3 @@ class PKey (object): @raise binascii.Error: on base64 decoding error """ pass - - def sign_ssh_session(self, randpool, sid, username): - """ - Sign an SSH authentication request. - - @bug: Same as L{sign_ssh_data} - - @param randpool: a secure random number generator. - @type randpool: L{Crypto.Util.randpool.RandomPool} - @param sid: the session ID given by the server - @type sid: string - @param username: the username to use in the authentication request - @type username: string - @return: string representation of an SSH signature message. - @rtype: string - """ diff --git a/paramiko/rsakey.py b/paramiko/rsakey.py index e0935d5d..b797c6b9 100644 --- a/paramiko/rsakey.py +++ b/paramiko/rsakey.py @@ -1,7 +1,6 @@ #!/usr/bin/python from message import Message -from transport import _MSG_USERAUTH_REQUEST from Crypto.PublicKey import RSA from Crypto.Hash import SHA from ber import BER @@ -11,9 +10,11 @@ import base64 class RSAKey (PKey): - def __init__(self, msg=None): + def __init__(self, msg=None, data=''): self.valid = 0 - if (msg == None) or (msg.get_string() != 'ssh-rsa'): + if (msg is None) and (data is not None): + msg = Message(data) + if (msg is None) or (msg.get_string() != 'ssh-rsa'): return self.e = msg.get_mpint() self.n = msg.get_mpint() @@ -29,6 +30,12 @@ class RSAKey (PKey): m.add_mpint(self.n) return str(m) + def __hash__(self): + h = hash(self.get_name()) + h = h * 37 + hash(self.e) + h = h * 37 + hash(self.n) + return hash(h) + def get_name(self): return 'ssh-rsa' @@ -41,6 +48,15 @@ class RSAKey (PKey): filler = '\xff' * (self.size - len(SHA1_DIGESTINFO) - len(data) - 3) return '\x00\x01' + filler + '\x00' + SHA1_DIGESTINFO + data + def sign_ssh_data(self, randpool, data): + hash = SHA.new(data).digest() + rsa = RSA.construct((long(self.n), long(self.e), long(self.d))) + sig = deflate_long(rsa.sign(self._pkcs1imify(hash), '')[0], 0) + m = Message() + m.add_string('ssh-rsa') + m.add_string(sig) + return m + def verify_ssh_sig(self, data, msg): if (not self.valid) or (msg.get_string() != 'ssh-rsa'): return False @@ -52,15 +68,6 @@ class RSAKey (PKey): rsa = RSA.construct((long(self.n), long(self.e))) return rsa.verify(hash, (sig,)) - def sign_ssh_data(self, randpool, data): - hash = SHA.new(data).digest() - rsa = RSA.construct((long(self.n), long(self.e), long(self.d))) - sig = deflate_long(rsa.sign(self._pkcs1imify(hash), '')[0], 0) - m = Message() - m.add_string('ssh-rsa') - m.add_string(sig) - return str(m) - def read_private_key_file(self, filename): # private key file contains: # RSAPrivateKey = { version = 0, n, e, d, p, q, d mod p-1, d mod q-1, q**-1 mod p } @@ -82,16 +89,3 @@ class RSAKey (PKey): self.q = keylist[5] self.size = len(deflate_long(self.n, 0)) self.valid = 1 - - def sign_ssh_session(self, randpool, sid, username): - m = Message() - m.add_string(sid) - m.add_byte(chr(_MSG_USERAUTH_REQUEST)) - m.add_string(username) - m.add_string('ssh-connection') - m.add_string('publickey') - m.add_boolean(1) - m.add_string('ssh-rsa') - m.add_string(str(self)) - return self.sign_ssh_data(randpool, str(m)) - diff --git a/paramiko/transport.py b/paramiko/transport.py index 1a42fb76..cf914f75 100644 --- a/paramiko/transport.py +++ b/paramiko/transport.py @@ -155,6 +155,26 @@ class BaseTransport (threading.Thread): self.server_accepts = [ ] self.server_accept_cv = threading.Condition(self.lock) + def __repr__(self): + """ + Returns a string representation of this object, for debugging. + + @rtype: string + """ + if not self.active: + 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 len(self.channels) == 1: + out += ' (active; 1 open channel)' + else: + out += ' (active; %d open channels)' % len(self.channels) + out += '>' + return out + def start_client(self, event=None): self.completion_event = event self.start() @@ -179,6 +199,19 @@ class BaseTransport (threading.Thread): self.server_key_dict[key.get_name()] = key def get_server_key(self): + """ + Return the active host key, in server mode. After negotiating with the + client, this method will return the negotiated host key. If only one + type of host key was set with L{add_server_key}, that's the only key + that will ever be returned. But in cases where you have set more than + one type of host key (for example, an RSA key and a DSS key), the key + type will be negotiated by the client, and this method will return the + key of the type agreed on. If the host key has not been negotiated + yet, C{None} is returned. In client mode, the behavior is undefined. + + @return: host key of the type negotiated by the client, or C{None}. + @rtype: L{PKey <pkey.PKey>} + """ try: return self.server_key_dict[self.host_key_type] except KeyError: @@ -228,37 +261,6 @@ class BaseTransport (threading.Thread): return False load_server_moduli = staticmethod(load_server_moduli) - def _get_modulus_pack(self): - "used by KexGex to find primes for group exchange" - return self._modulus_pack - - def __repr__(self): - """ - Returns a string representation of this object, for debugging. - - @rtype: string - """ - if not self.active: - 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 len(self.channels) == 1: - out += ' (active; 1 open channel)' - else: - out += ' (active; %d open channels)' % len(self.channels) - out += '>' - return out - - def _log(self, level, msg): - if type(msg) == type([]): - for m in msg: - self.logger.log(level, m) - else: - self.logger.log(level, msg) - def close(self): """ Close this session, and any open channels that are tied to it. @@ -468,6 +470,17 @@ class BaseTransport (threading.Thread): ### internals... + def _log(self, level, msg): + if type(msg) == type([]): + for m in msg: + self.logger.log(level, m) + else: + self.logger.log(level, msg) + + def _get_modulus_pack(self): + "used by KexGex to find primes for group exchange" + return self._modulus_pack + def _unlink_channel(self, chanid): "used by a Channel to remove itself from the active channel list" try: @@ -597,13 +610,16 @@ class BaseTransport (threading.Thread): "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': - key = DSSKey(Message(host_key)) + def _key_from_blob(self, keytype, keyblob): + if keytype == 'ssh-rsa': + return RSAKey(Message(keyblob)) + elif keytype == 'ssh-dss': + return DSSKey(Message(keyblob)) else: - key = None + return None + + def _verify_key(self, host_key, sig): + key = self._key_from_blob(self.host_key_type, host_key) if (key == None) or not key.valid: raise SSHException('Unknown host key type') if not key.verify_ssh_sig(self.H, Message(sig)): @@ -745,7 +761,7 @@ class BaseTransport (threading.Thread): kind of key negotiation we support. """ if self.server_mode: - if (self.modulus_pack is None) and ('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__, diff --git a/user_rsa_key b/user_rsa_key new file mode 100644 index 00000000..ee64f23c --- /dev/null +++ b/user_rsa_key @@ -0,0 +1,15 @@ +-----BEGIN RSA PRIVATE KEY----- +MIICXQIBAAKBgQDI7iK3d8eWYZlYloat94c5VjtFY7c/0zuGl8C7uMnZ3t6i2G99 +66hEW0nCFSZkOW5F0XKEVj+EUCHvo8koYC6wiohAqWQnEwIoOoh7GSAcB8gP/qaq ++adIl/Rvlby/mHakj+y05LBND6nFWHAn1y1gOFFKUXSJNRZPXSFy47gqzwIBIwKB +gQCbANjz7q/pCXZLp1Hz6tYHqOvlEmjK1iabB1oqafrMpJ0eibUX/u+FMHq6StR5 +M5413BaDWHokPdEJUnabfWXXR3SMlBUKrck0eAer1O8m78yxu3OEdpRk+znVo4DL +guMeCdJB/qcF0kEsx+Q8HP42MZU1oCmk3PbfXNFwaHbWuwJBAOQ/ry/hLD7AqB8x +DmCM82A9E59ICNNlHOhxpJoh6nrNTPCsBAEu/SmqrL8mS6gmbRKUaya5Lx1pkxj2 +s/kWOokCQQDhXCcYXjjWiIfxhl6Rlgkk1vmI0l6785XSJNv4P7pXjGmShXfIzroh +S8uWK3tL0GELY7+UAKDTUEVjjQdGxYSXAkEA3bo1JzKCwJ3lJZ1ebGuqmADRO6UP +40xH977aadfN1mEI6cusHmgpISl0nG5YH7BMsvaT+bs1FUH8m+hXDzoqOwJBAK3Z +X/za+KV/REya2z0b+GzgWhkXUGUa/owrEBdHGriQ47osclkUgPUdNqcLmaDilAF4 +1Z4PHPrI5RJIONAx+JECQQC/fChqjBgFpk6iJ+BOdSexQpgfxH/u/457W10Y43HR +soS+8btbHqjQkowQ/2NTlUfWvqIlfxs6ZbFsIp/HrhZL +-----END RSA PRIVATE KEY----- diff --git a/user_rsa_key.pub b/user_rsa_key.pub new file mode 100644 index 00000000..ac722f17 --- /dev/null +++ b/user_rsa_key.pub @@ -0,0 +1 @@ +ssh-rsa AAAAB3NzaC1yc2EAAAABIwAAAIEAyO4it3fHlmGZWJaGrfeHOVY7RWO3P9M7hpfAu7jJ2d7eothvfeuoRFtJwhUmZDluRdFyhFY/hFAh76PJKGAusIqIQKlkJxMCKDqIexkgHAfID/6mqvmnSJf0b5W8v5h2pI/stOSwTQ+pxVhwJ9ctYDhRSlF0iTUWT10hcuO4Ks8= robey@ralph.lag.net |