[project @ Arch-1:robey@lag.net--2003-public%secsh--dev--1.0--patch-70]

clean up server interface; no longer need to subclass Channel
- export AUTH_*, OPEN_FAILED_*, and the new OPEN_SUCCEEDED into the paramiko
  namespace instead of making people dig into paramiko.Transport.AUTH_* etc.
- move all of the check_* methods from Channel to ServerInterface so apps
  don't need to subclass Channel anymore just to run an ssh server
- ServerInterface.check_channel_request() returns an error code now, not a
  new Channel object
- fix demo_server.py to follow all these changes
- fix a bunch of places where i used "string" in docstrings but meant "str"
- added Channel.get_id()
This commit is contained in:
Robey Pointer 2004-09-03 22:39:20 +00:00
parent 440b3de06a
commit aba7e37a38
9 changed files with 293 additions and 208 deletions

2
README
View File

@ -155,3 +155,5 @@ v0.9 FEAROW
* multi-part auth not supported (ie, need username AND pk)
* server mode needs better documentation
* sftp server mode
ivysaur?

View File

@ -20,38 +20,34 @@ class Server (paramiko.ServerInterface):
data = 'AAAAB3NzaC1yc2EAAAABIwAAAIEAyO4it3fHlmGZWJaGrfeHOVY7RWO3P9M7hpfAu7jJ2d7eothvfeuoRFtJwhUmZDluRdFyhFY/hFAh76PJKGAusIqIQKlkJxMCKDqIexkgHAfID/6mqvmnSJf0b5W8v5h2pI/stOSwTQ+pxVhwJ9ctYDhRSlF0iTUWT10hcuO4Ks8='
good_pub_key = paramiko.RSAKey(data=base64.decodestring(data))
def __init__(self):
self.event = threading.Event()
def check_channel_request(self, kind, chanid):
if kind == 'session':
return ServerChannel(chanid)
return paramiko.Transport.OPEN_FAILED_ADMINISTRATIVELY_PROHIBITED
return paramiko.OPEN_SUCCEEDED
return paramiko.OPEN_FAILED_ADMINISTRATIVELY_PROHIBITED
def check_auth_password(self, username, password):
if (username == 'robey') and (password == 'foo'):
return paramiko.Transport.AUTH_SUCCESSFUL
return paramiko.Transport.AUTH_FAILED
return paramiko.AUTH_SUCCESSFUL
return paramiko.AUTH_FAILED
def check_auth_publickey(self, username, key):
print 'Auth attempt with key: ' + paramiko.util.hexify(key.get_fingerprint())
if (username == 'robey') and (key == self.good_pub_key):
return paramiko.Transport.AUTH_SUCCESSFUL
return paramiko.Transport.AUTH_FAILED
return paramiko.AUTH_SUCCESSFUL
return paramiko.AUTH_FAILED
def get_allowed_auths(self, username):
return 'password,publickey'
class ServerChannel (paramiko.Channel):
"Channel descendant that pretends to understand pty and shell requests"
def __init__(self, chanid):
paramiko.Channel.__init__(self, chanid)
self.event = threading.Event()
def check_pty_request(self, term, width, height, pixelwidth, pixelheight, modes):
def check_channel_shell_request(self, channel):
self.event.set()
return True
def check_shell_request(self):
self.event.set()
def check_channel_pty_request(self, channel, term, width, height, pixelwidth,
pixelheight, modes):
return True
@ -85,7 +81,8 @@ try:
print '(Failed to load moduli -- gex will be unsupported.)'
raise
t.add_server_key(host_key)
t.start_server(event, Server())
server = Server()
t.start_server(event, server)
while 1:
event.wait(0.1)
if not t.is_active():
@ -101,8 +98,8 @@ try:
print '*** No channel.'
sys.exit(1)
print 'Authenticated!'
chan.event.wait(10)
if not chan.event.isSet():
server.event.wait(10)
if not server.event.isSet():
print '*** Client never asked for a shell.'
sys.exit(1)

View File

@ -79,6 +79,9 @@ SFTP = sftp.SFTP
ServerInterface = server.ServerInterface
SecurityOptions = transport.SecurityOptions
from common import AUTH_SUCCESSFUL, AUTH_PARTIALLY_SUCCESSFUL, AUTH_FAILED, \
OPEN_SUCCEEDED, OPEN_FAILED_ADMINISTRATIVELY_PROHIBITED, OPEN_FAILED_CONNECT_FAILED, \
OPEN_FAILED_UNKNOWN_CHANNEL_TYPE, OPEN_FAILED_RESOURCE_SHORTAGE
__all__ = [ 'Transport',
'SecurityOptions',

View File

@ -47,8 +47,6 @@ class Transport (BaseTransport):
another shell window).
"""
AUTH_SUCCESSFUL, AUTH_PARTIALLY_SUCCESSFUL, AUTH_FAILED = range(3)
def __init__(self, sock):
BaseTransport.__init__(self, sock)
self.username = None
@ -60,11 +58,13 @@ class Transport (BaseTransport):
self.auth_complete = 0
def __repr__(self):
out = '<paramiko.Transport at %s' % hex(id(self))
if not self.active:
return '<paramiko.Transport (unconnected)>'
out = '<paramiko.Transport'
out += ' (unconnected)'
else:
if self.local_cipher != '':
out += ' (cipher %s, %d bits)' % (self.local_cipher, self._cipher_info[self.local_cipher]['key-size'] * 8)
out += ' (cipher %s, %d bits)' % (self.local_cipher,
self._cipher_info[self.local_cipher]['key-size'] * 8)
if self.authenticated:
if len(self.channels) == 1:
out += ' (active; 1 open channel)'
@ -268,21 +268,24 @@ class Transport (BaseTransport):
# the list of valid auth types from the callback anyway
self._log(DEBUG, 'Auth request to change passwords (rejected)')
newpassword = m.get_string().decode('UTF-8')
result = self.AUTH_FAILED
result = AUTH_FAILED
else:
result = self.server_object.check_auth_password(username, password)
elif method == 'publickey':
sig_attached = m.get_boolean()
keytype = m.get_string()
keyblob = m.get_string()
key = self._key_from_blob(keytype, keyblob)
try:
key = self._key_info[keytype](Message(keyblob))
except:
key = None
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.server_object.check_auth_publickey(username, key)
if result != self.AUTH_FAILED:
if result != AUTH_FAILED:
# key is okay, verify it
if not sig_attached:
# client wants to know if this key is acceptable, before it
@ -297,12 +300,12 @@ class Transport (BaseTransport):
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
result = AUTH_FAILED
else:
result = self.server_object.check_auth_none(username)
# okay, send result
m = Message()
if result == self.AUTH_SUCCESSFUL:
if result == AUTH_SUCCESSFUL:
self._log(DEBUG, 'Auth granted.')
m.add_byte(chr(MSG_USERAUTH_SUCCESS))
self.auth_complete = 1
@ -310,7 +313,7 @@ class Transport (BaseTransport):
self._log(DEBUG, 'Auth rejected.')
m.add_byte(chr(MSG_USERAUTH_FAILURE))
m.add_string(self.server_object.get_allowed_auths(username))
if result == self.AUTH_PARTIALLY_SUCCESSFUL:
if result == AUTH_PARTIALLY_SUCCESSFUL:
m.add_boolean(1)
else:
m.add_boolean(0)

View File

@ -87,7 +87,7 @@ class Channel (object):
"""
Returns a string representation of this object, for debugging.
@rtype: string
@rtype: str
"""
out = '<paramiko.Channel %d' % self.chanid
if self.closed:
@ -111,7 +111,7 @@ class Channel (object):
basic terminal semantics for the next command you execute.
@param term: the terminal type to emulate (for example, C{'vt100'}).
@type term: string
@type term: str
@param width: width (in characters) of the terminal screen
@type width: int
@param height: height (in characters) of the terminal screen
@ -173,7 +173,7 @@ class Channel (object):
being executed.
@param command: a shell command to execute.
@type command: string
@type command: str
@return: C{True} if the operation succeeded; C{False} if not.
@rtype: bool
"""
@ -201,7 +201,7 @@ class Channel (object):
requested subsystem.
@param subsystem: name of the subsystem being requested.
@type subsystem: string
@type subsystem: str
@return: C{True} if the operation succeeded; C{False} if not.
@rtype: bool
"""
@ -268,8 +268,8 @@ class Channel (object):
of the log level used for debugging. The name can be fetched with the
L{get_name} method.
@param name: new channel name
@type name: string
@param name: new channel name.
@type name: str
"""
self.name = name
self.logger = logging.getLogger('paramiko.chan.' + name)
@ -278,11 +278,25 @@ class Channel (object):
"""
Get the name of this channel that was previously set by L{set_name}.
@return: the name of this channel
@rtype: string
@return: the name of this channel.
@rtype: str
"""
return self.name
def get_id(self):
"""
Return the ID # for this channel. The channel ID is unique across
a L{Transport} and usually a small number. It's also the number
passed to L{ServerInterface.check_channel_request} when determining
whether to accept a channel request in server mode.
@return: the ID of this channel.
@rtype: int
@since: ivysaur
"""
return self.chanid
### socket API
@ -389,7 +403,7 @@ class Channel (object):
@param nbytes: maximum number of bytes to read.
@type nbytes: int
@return: data.
@rtype: string
@rtype: str
@raise socket.timeout: if no data is ready before the timeout set by
L{settimeout}.
@ -436,7 +450,7 @@ class Channel (object):
data.
@param s: data to send.
@type s: string
@type s: str
@return: number of bytes actually sent.
@rtype: int
@ -490,7 +504,7 @@ class Channel (object):
either all data has been sent or an error occurs. Nothing is returned.
@param s: data to send.
@type s: string
@type s: str
@raise socket.timeout: if sending stalled for longer than the timeout
set by L{settimeout}.
@ -579,83 +593,6 @@ class Channel (object):
### overrides
def check_pty_request(self, term, width, height, pixelwidth, pixelheight, modes):
"""
I{(subclass override)}
Determine if a pseudo-terminal of the given dimensions (usually
requested for shell access) can be provided.
The default implementation always returns C{False}.
@param term: type of terminal requested (for example, C{"vt100"}).
@type term: string
@param width: width of screen in characters.
@type width: int
@param height: height of screen in characters.
@type height: int
@param pixelwidth: width of screen in pixels, if known (may be C{0} if
unknown).
@type pixelwidth: int
@param pixelheight: height of screen in pixels, if known (may be C{0}
if unknown).
@type pixelheight: int
@return: C{True} if the psuedo-terminal has been allocated; C{False}
otherwise.
@rtype: boolean
"""
return False
def check_shell_request(self):
"""
I{(subclass override)}
Determine if a shell will be provided to the client. If this method
returns C{True}, this channel should be connected to the stdin/stdout
of a shell.
The default implementation always returns C{False}.
@return: C{True} if this channel is now hooked up to a shell; C{False}
if a shell can't or won't be provided.
@rtype: boolean
"""
return False
def check_subsystem_request(self, name):
"""
I{(subclass override)}
Determine if a requested subsystem will be provided to the client. If
this method returns C{True}, all future I/O through this channel will
be assumed to be connected to the requested subsystem. An example of
a subsystem is C{sftp}.
The default implementation always returns C{False}.
@return: C{True} if this channel is now hooked up to the requested
subsystem; C{False} if that subsystem can't or won't be provided.
@rtype: boolean
"""
return False
def check_window_change_request(self, width, height, pixelwidth, pixelheight):
"""
I{(subclass override)}
Determine if the pseudo-terminal can be resized.
The default implementation always returns C{False}.
@param width: width of screen in characters.
@type width: int
@param height: height of screen in characters.
@type height: int
@param pixelwidth: width of screen in pixels, if known (may be C{0} if
unknown).
@type pixelwidth: int
@param pixelheight: height of screen in pixels, if known (may be C{0}
if unknown).
@type pixelheight: int
@return: C{True} if the terminal was resized; C{False} if not.
"""
return False
### calls from Transport
@ -717,6 +654,7 @@ class Channel (object):
def _handle_request(self, m):
key = m.get_string()
want_reply = m.get_boolean()
server = self.transport.server_object
ok = False
if key == 'exit-status':
self.exit_status = m.get_int()
@ -731,18 +669,32 @@ class Channel (object):
pixelwidth = m.get_int()
pixelheight = m.get_int()
modes = m.get_string()
ok = self.check_pty_request(term, width, height, pixelwidth, pixelheight, modes)
if server is None:
ok = False
else:
ok = server.check_channel_pty_request(self, term, width, height, pixelwidth,
pixelheight, modes)
elif key == 'shell':
ok = self.check_shell_request()
if server is None:
ok = False
else:
ok = server.check_channel_shell_request(self)
elif key == 'subsystem':
name = m.get_string()
ok = self.check_subsystem_request(name)
if server is None:
ok = False
else:
ok = server.check_channel_subsystem_request(self, name)
elif key == 'window-change':
width = m.get_int()
height = m.get_int()
pixelwidth = m.get_int()
pixelheight = m.get_int()
ok = self.check_window_change_request(width, height, pixelwidth, pixelheight)
if server is None:
ok = False
else:
ok = server.check_channel_window_change_request(self, width, height, pixelwidth,
pixelheight)
else:
self._log(DEBUG, 'Unhandled channel request "%s"' % key)
ok = False
@ -931,7 +883,7 @@ class ChannelFile (BufferedFile):
"""
Returns a string representation of this object, for debugging.
@rtype: string
@rtype: str
"""
return '<paramiko.ChannelFile from ' + repr(self.channel) + '>'

View File

@ -35,7 +35,19 @@ MSG_CHANNEL_OPEN, MSG_CHANNEL_OPEN_SUCCESS, MSG_CHANNEL_OPEN_FAILURE, \
MSG_CHANNEL_SUCCESS, MSG_CHANNEL_FAILURE = range(90, 101)
# authentication request return codes:
AUTH_SUCCESSFUL, AUTH_PARTIALLY_SUCCESSFUL, AUTH_FAILED = range(3)
# channel request failed reasons:
(OPEN_SUCCEEDED,
OPEN_FAILED_ADMINISTRATIVELY_PROHIBITED,
OPEN_FAILED_CONNECT_FAILED,
OPEN_FAILED_UNKNOWN_CHANNEL_TYPE,
OPEN_FAILED_RESOURCE_SHORTAGE) = range(0, 5)
CONNECTION_FAILED_CODE = {
1: 'Administratively prohibited',
2: 'Connect failed',
@ -43,6 +55,7 @@ CONNECTION_FAILED_CODE = {
4: 'Resource shortage'
}
DISCONNECT_SERVICE_NOT_AVAILABLE, DISCONNECT_AUTH_CANCELLED_BY_USER, \
DISCONNECT_NO_MORE_AUTH_METHODS_AVAILABLE = 7, 13, 14

View File

@ -22,6 +22,7 @@
L{ServerInterface} is an interface to override for server support.
"""
from common import *
from auth_transport import Transport
class ServerInterface (object):
@ -37,31 +38,46 @@ class ServerInterface (object):
def check_channel_request(self, kind, chanid):
"""
Determine if a channel request of a given type will be granted, and
return a suitable L{Channel} object. This method is called in server
mode when the client requests a channel, after authentication is
complete.
return C{OPEN_SUCCEEDED} or an error code. This method is
called in server mode when the client requests a channel, after
authentication is complete.
You will generally want to subclass L{Channel} to override some of the
methods for handling client requests (such as connecting to a subsystem
opening a shell) to determine what you want to allow or disallow. For
this reason, L{check_channel_request} must return a new object of that
type. The C{chanid} parameter is passed so that you can use it in
L{Channel}'s constructor.
If you allow channel requests (and an ssh server that didn't would be
useless), you should also override some of the channel request methods
below, which are used to determine which services will be allowed on
a given channel:
- L{check_channel_pty_request}
- L{check_channel_shell_request}
- L{check_channel_subsystem_request}
- L{check_channel_window_change_request}
The default implementation always returns C{None}, rejecting any
channel requests. A useful server must override this method.
The C{chanid} parameter is a small number that uniquely identifies the
channel within a L{Transport}. A L{Channel} object is not created
unless this method returns C{OPEN_SUCCEEDED} -- once a
L{Channel} object is created, you can call L{Channel.get_id} to
retrieve the channel ID.
The return value should either be C{OPEN_SUCCEEDED} (or
C{0}) to allow the channel request, or one of the following error
codes to reject it:
- C{OPEN_FAILED_ADMINISTRATIVELY_PROHIBITED}
- C{OPEN_FAILED_CONNECT_FAILED}
- C{OPEN_FAILED_UNKNOWN_CHANNEL_TYPE}
- C{OPEN_FAILED_RESOURCE_SHORTAGE}
The default implementation always returns
C{OPEN_FAILED_ADMINISTRATIVELY_PROHIBITED}.
@param kind: the kind of channel the client would like to open
(usually C{"session"}).
@type kind: string
@type kind: str
@param chanid: ID of the channel, required to create a new L{Channel}
object.
@type chanid: int
@return: a new L{Channel} object (or subclass thereof), or C{None} to
refuse the request.
@rtype: L{Channel}
@return: a success or failure code (listed above).
@rtype: int
"""
return None
return OPEN_FAILED_ADMINISTRATIVELY_PROHIBITED
def get_allowed_auths(self, username):
"""
@ -76,9 +92,9 @@ class ServerInterface (object):
The default implementation always returns C{"password"}.
@param username: the username requesting authentication.
@type username: string
@type username: str
@return: a comma-separated list of authentication types
@rtype: string
@rtype: str
"""
return 'password'
@ -87,46 +103,46 @@ class ServerInterface (object):
Determine if a client may open channels with no (further)
authentication.
Return L{Transport.AUTH_FAILED} if the client must authenticate, or
L{Transport.AUTH_SUCCESSFUL} if it's okay for the client to not
Return L{AUTH_FAILED} if the client must authenticate, or
L{AUTH_SUCCESSFUL} if it's okay for the client to not
authenticate.
The default implementation always returns L{Transport.AUTH_FAILED}.
The default implementation always returns L{AUTH_FAILED}.
@param username: the username of the client.
@type username: string
@return: L{Transport.AUTH_FAILED} if the authentication fails;
L{Transport.AUTH_SUCCESSFUL} if it succeeds.
@type username: str
@return: L{AUTH_FAILED} if the authentication fails;
L{AUTH_SUCCESSFUL} if it succeeds.
@rtype: int
"""
return Transport.AUTH_FAILED
return AUTH_FAILED
def check_auth_password(self, username, password):
"""
Determine if a given username and password supplied by the client is
acceptable for use in authentication.
Return L{Transport.AUTH_FAILED} if the password is not accepted,
L{Transport.AUTH_SUCCESSFUL} if the password is accepted and completes
the authentication, or L{Transport.AUTH_PARTIALLY_SUCCESSFUL} if your
Return L{AUTH_FAILED} if the password is not accepted,
L{AUTH_SUCCESSFUL} if the password is accepted and completes
the authentication, or L{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 L{Transport.AUTH_FAILED}.
The default implementation always returns L{AUTH_FAILED}.
@param username: the username of the authenticating client.
@type username: string
@type username: str
@param password: the password given by the client.
@type password: string
@return: L{Transport.AUTH_FAILED} if the authentication fails;
L{Transport.AUTH_SUCCESSFUL} if it succeeds;
L{Transport.AUTH_PARTIALLY_SUCCESSFUL} if the password auth is
@type password: str
@return: L{AUTH_FAILED} if the authentication fails;
L{AUTH_SUCCESSFUL} if it succeeds;
L{AUTH_PARTIALLY_SUCCESSFUL} if the password auth is
successful, but authentication must continue.
@rtype: int
"""
return Transport.AUTH_FAILED
return AUTH_FAILED
def check_auth_publickey(self, username, key):
"""
@ -135,24 +151,115 @@ class ServerInterface (object):
check the username and key and decide if you would accept a signature
made using this key.
Return L{Transport.AUTH_FAILED} if the key is not accepted,
L{Transport.AUTH_SUCCESSFUL} if the key is accepted and completes the
authentication, or L{Transport.AUTH_PARTIALLY_SUCCESSFUL} if your
Return L{AUTH_FAILED} if the key is not accepted,
L{AUTH_SUCCESSFUL} if the key is accepted and completes the
authentication, or L{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 L{Transport.AUTH_FAILED}.
The default implementation always returns L{AUTH_FAILED}.
@param username: the username of the authenticating client.
@type username: string
@type username: str
@param key: the key object provided by the client.
@type key: L{PKey <pkey.PKey>}
@return: L{Transport.AUTH_FAILED} if the client can't authenticate
with this key; L{Transport.AUTH_SUCCESSFUL} if it can;
L{Transport.AUTH_PARTIALLY_SUCCESSFUL} if it can authenticate with
@return: L{AUTH_FAILED} if the client can't authenticate
with this key; L{AUTH_SUCCESSFUL} if it can;
L{AUTH_PARTIALLY_SUCCESSFUL} if it can authenticate with
this key but must continue with authentication.
@rtype: int
"""
return Transport.AUTH_FAILED
return AUTH_FAILED
### Channel requests
def check_channel_pty_request(self, channel, term, width, height, pixelwidth, pixelheight,
modes):
"""
Determine if a pseudo-terminal of the given dimensions (usually
requested for shell access) can be provided on the given channel.
The default implementation always returns C{False}.
@param channel: the L{Channel} the pty request arrived on.
@type channel: L{Channel}
@param term: type of terminal requested (for example, C{"vt100"}).
@type term: str
@param width: width of screen in characters.
@type width: int
@param height: height of screen in characters.
@type height: int
@param pixelwidth: width of screen in pixels, if known (may be C{0} if
unknown).
@type pixelwidth: int
@param pixelheight: height of screen in pixels, if known (may be C{0}
if unknown).
@type pixelheight: int
@return: C{True} if the psuedo-terminal has been allocated; C{False}
otherwise.
@rtype: boolean
"""
return False
def check_channel_shell_request(self, channel):
"""
Determine if a shell will be provided to the client on the given
channel. If this method returns C{True}, the channel should be
connected to the stdin/stdout of a shell (or something that acts like
a shell).
The default implementation always returns C{False}.
@param channel: the L{Channel} the pty request arrived on.
@type channel: L{Channel}
@return: C{True} if this channel is now hooked up to a shell; C{False}
if a shell can't or won't be provided.
@rtype: boolean
"""
return False
def check_channel_subsystem_request(self, channel, name):
"""
Determine if a requested subsystem will be provided to the client on
the given channel. If this method returns C{True}, all future I/O
through this channel will be assumed to be connected to the requested
subsystem. An example of a subsystem is C{sftp}.
The default implementation always returns C{False}.
@param channel: the L{Channel} the pty request arrived on.
@type channel: L{Channel}
@param name: name of the requested subsystem.
@type name: str
@return: C{True} if this channel is now hooked up to the requested
subsystem; C{False} if that subsystem can't or won't be provided.
@rtype: boolean
"""
return False
def check_channel_window_change_request(self, channel, width, height, pixelwidth, pixelheight):
"""
Determine if the pseudo-terminal on the given channel can be resized.
This only makes sense if a pty was previously allocated on it.
The default implementation always returns C{False}.
@param channel: the L{Channel} the pty request arrived on.
@type channel: L{Channel}
@param width: width of screen in characters.
@type width: int
@param height: height of screen in characters.
@type height: int
@param pixelwidth: width of screen in pixels, if known (may be C{0} if
unknown).
@type pixelwidth: int
@param pixelheight: height of screen in pixels, if known (may be C{0}
if unknown).
@type pixelheight: int
@return: C{True} if the terminal was resized; C{False} if not.
"""
return False

View File

@ -36,7 +36,7 @@ _FX_OK = 0
_FX_EOF, _FX_NO_SUCH_FILE, _FX_PERMISSION_DENIED, _FX_FAILURE, _FX_BAD_MESSAGE, \
_FX_NO_CONNECTION, _FX_CONNECTION_LOST, _FX_OP_UNSUPPORTED = range(1, 9)
VERSION = 3
_VERSION = 3
class SFTPAttributes (object):
@ -238,12 +238,12 @@ class SFTP (object):
else:
self.logger = logging.getLogger('paramiko.sftp')
# protocol: (maybe should move to a different method)
self._send_packet(_CMD_INIT, struct.pack('>I', VERSION))
self._send_packet(_CMD_INIT, struct.pack('>I', _VERSION))
t, data = self._read_packet()
if t != _CMD_VERSION:
raise SFTPError('Incompatible sftp protocol')
version = struct.unpack('>I', data[:4])[0]
# if version != VERSION:
# if version != _VERSION:
# raise SFTPError('Incompatible sftp protocol')
def from_transport(selfclass, t):

View File

@ -64,6 +64,8 @@ class SecurityOptions (object):
If you try to add an algorithm that paramiko doesn't recognize,
C{ValueError} will be raised. If you try to assign something besides a
tuple to one of the fields, L{TypeError} will be raised.
@since: ivysaur
"""
__slots__ = [ 'ciphers', 'digests', 'key_types', 'kex', '_transport' ]
@ -74,7 +76,7 @@ class SecurityOptions (object):
"""
Returns a string representation of this object, for debugging.
@rtype: string
@rtype: str
"""
return '<paramiko.SecurityOptions for %s>' % repr(self._transport)
@ -162,9 +164,6 @@ class BaseTransport (threading.Thread):
REKEY_PACKETS = pow(2, 30)
REKEY_BYTES = pow(2, 30)
OPEN_FAILED_ADMINISTRATIVELY_PROHIBITED, OPEN_FAILED_CONNECT_FAILED, OPEN_FAILED_UNKNOWN_CHANNEL_TYPE, \
OPEN_FAILED_RESOURCE_SHORTAGE = range(1, 5)
_modulus_pack = None
def __init__(self, sock):
@ -176,7 +175,7 @@ class BaseTransport (threading.Thread):
If the object is not actually a socket, it must have the following
methods:
- C{send(string)}: Writes from 1 to C{len(string)} bytes, and
- C{send(str)}: Writes from 1 to C{len(str)} bytes, and
returns an int representing the number of bytes written. Returns
0 or raises C{EOFError} if the stream has been closed.
- C{recv(int)}: Reads from 1 to C{int} bytes and returns them as a
@ -264,13 +263,15 @@ class BaseTransport (threading.Thread):
"""
Returns a string representation of this object, for debugging.
@rtype: string
@rtype: str
"""
out = '<paramiko.BaseTransport at %s' % hex(id(self))
if not self.active:
return '<paramiko.BaseTransport (unconnected)>'
out = '<paramiko.BaseTransport'
out += ' (unconnected)'
else:
if self.local_cipher != '':
out += ' (cipher %s, %d bits)' % (self.local_cipher, self._cipher_info[self.local_cipher]['key-size'] * 8)
out += ' (cipher %s, %d bits)' % (self.local_cipher,
self._cipher_info[self.local_cipher]['key-size'] * 8)
if len(self.channels) == 1:
out += ' (active; 1 open channel)'
else:
@ -287,6 +288,8 @@ class BaseTransport (threading.Thread):
@return: an object that can be used to change the preferred algorithms
for encryption, digest (hash), public key, and key exchange.
@rtype: L{SecurityOptions}
@since: ivysaur
"""
return SecurityOptions(self)
@ -407,7 +410,7 @@ class BaseTransport (threading.Thread):
@param filename: optional path to the moduli file, if you happen to
know that it's not in a standard location.
@type filename: string
@type filename: str
@return: True if a moduli file was successfully loaded; False
otherwise.
@rtype: bool
@ -502,6 +505,9 @@ class BaseTransport (threading.Thread):
@rtype: L{Channel}
"""
chan = None
if not self.active:
# don't bother trying to allocate a channel
return None
try:
self.lock.acquire()
chanid = self.channel_counter
@ -603,7 +609,7 @@ class BaseTransport (threading.Thread):
extensions to the SSH2 protocol.
@param kind: name of the request.
@type kind: string
@type kind: str
@param data: an optional tuple containing additional data to attach
to the request.
@type data: tuple
@ -659,7 +665,7 @@ class BaseTransport (threading.Thread):
does not support any global requests.
@param kind: the kind of global request being made.
@type kind: string
@type kind: str
@param msg: any extra arguments to the request.
@type msg: L{Message}
@return: C{True} or a tuple of data if the request was granted;
@ -706,15 +712,15 @@ class BaseTransport (threading.Thread):
@param hostkeytype: the type of host key expected from the server
(usually C{"ssh-rsa"} or C{"ssh-dss"}), or C{None} if you don't want
to do host key verification.
@type hostkeytype: string
@type hostkeytype: str
@param hostkey: the host key expected from the server, or C{None} if
you don't want to do host key verification.
@type hostkey: string
@type hostkey: str
@param username: the username to authenticate as.
@type username: string
@type username: str
@param password: a password to use for authentication, if you want to
use password authentication; otherwise C{None}.
@type password: string
@type password: str
@param pkey: a private key to use for authentication, if you want to
use private key authentication; otherwise C{None}.
@type pkey: L{PKey<pkey.PKey>}
@ -1042,6 +1048,9 @@ class BaseTransport (threading.Thread):
chanid = m.get_int()
if self.channels.has_key(chanid):
self._channel_handler_table[ptype](self.channels[chanid], m)
else:
self._log(ERROR, 'Channel request for unknown channel %d' % chanid)
self.active = False
else:
self._log(WARNING, 'Oops, unhandled type %d' % ptype)
msg = Message()
@ -1127,7 +1136,9 @@ class BaseTransport (threading.Thread):
if self.server_mode:
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')
pkex = list(self.get_security_options().kex)
pkex.remove('diffie-hellman-group-exchange-sha1')
self.get_security_options().kex = pkex
available_server_keys = filter(self.server_key_dict.keys().__contains__,
self._preferred_keys)
else:
@ -1394,7 +1405,7 @@ class BaseTransport (threading.Thread):
if not self.server_mode:
self._log(DEBUG, 'Rejecting "%s" channel request from server.' % kind)
reject = True
reason = self.OPEN_FAILED_ADMINISTRATIVELY_PROHIBITED
reason = OPEN_FAILED_ADMINISTRATIVELY_PROHIBITED
else:
try:
self.lock.acquire()
@ -1402,14 +1413,10 @@ class BaseTransport (threading.Thread):
self.channel_counter += 1
finally:
self.lock.release()
chan = self.server_object.check_channel_request(kind, my_chanid)
if (chan is None) or (type(chan) is int):
reason = self.server_object.check_channel_request(kind, my_chanid)
if reason != OPEN_SUCCEEDED:
self._log(DEBUG, 'Rejecting "%s" channel request from client.' % kind)
reject = True
if type(chan) is int:
reason = chan
else:
reason = self.OPEN_FAILED_ADMINISTRATIVELY_PROHIBITED
if reject:
msg = Message()
msg.add_byte(chr(MSG_CHANNEL_OPEN_FAILURE))
@ -1419,6 +1426,7 @@ class BaseTransport (threading.Thread):
msg.add_string('en')
self._send_message(msg)
return
chan = Channel(my_chanid)
try:
self.lock.acquire()
self.channels[my_chanid] = chan