[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:
parent
440b3de06a
commit
aba7e37a38
2
README
2
README
|
@ -155,3 +155,5 @@ v0.9 FEAROW
|
||||||
* multi-part auth not supported (ie, need username AND pk)
|
* multi-part auth not supported (ie, need username AND pk)
|
||||||
* server mode needs better documentation
|
* server mode needs better documentation
|
||||||
* sftp server mode
|
* sftp server mode
|
||||||
|
|
||||||
|
ivysaur?
|
||||||
|
|
|
@ -20,38 +20,34 @@ class Server (paramiko.ServerInterface):
|
||||||
data = 'AAAAB3NzaC1yc2EAAAABIwAAAIEAyO4it3fHlmGZWJaGrfeHOVY7RWO3P9M7hpfAu7jJ2d7eothvfeuoRFtJwhUmZDluRdFyhFY/hFAh76PJKGAusIqIQKlkJxMCKDqIexkgHAfID/6mqvmnSJf0b5W8v5h2pI/stOSwTQ+pxVhwJ9ctYDhRSlF0iTUWT10hcuO4Ks8='
|
data = 'AAAAB3NzaC1yc2EAAAABIwAAAIEAyO4it3fHlmGZWJaGrfeHOVY7RWO3P9M7hpfAu7jJ2d7eothvfeuoRFtJwhUmZDluRdFyhFY/hFAh76PJKGAusIqIQKlkJxMCKDqIexkgHAfID/6mqvmnSJf0b5W8v5h2pI/stOSwTQ+pxVhwJ9ctYDhRSlF0iTUWT10hcuO4Ks8='
|
||||||
good_pub_key = paramiko.RSAKey(data=base64.decodestring(data))
|
good_pub_key = paramiko.RSAKey(data=base64.decodestring(data))
|
||||||
|
|
||||||
|
def __init__(self):
|
||||||
|
self.event = threading.Event()
|
||||||
|
|
||||||
def check_channel_request(self, kind, chanid):
|
def check_channel_request(self, kind, chanid):
|
||||||
if kind == 'session':
|
if kind == 'session':
|
||||||
return ServerChannel(chanid)
|
return paramiko.OPEN_SUCCEEDED
|
||||||
return paramiko.Transport.OPEN_FAILED_ADMINISTRATIVELY_PROHIBITED
|
return paramiko.OPEN_FAILED_ADMINISTRATIVELY_PROHIBITED
|
||||||
|
|
||||||
def check_auth_password(self, username, password):
|
def check_auth_password(self, username, password):
|
||||||
if (username == 'robey') and (password == 'foo'):
|
if (username == 'robey') and (password == 'foo'):
|
||||||
return paramiko.Transport.AUTH_SUCCESSFUL
|
return paramiko.AUTH_SUCCESSFUL
|
||||||
return paramiko.Transport.AUTH_FAILED
|
return paramiko.AUTH_FAILED
|
||||||
|
|
||||||
def check_auth_publickey(self, username, key):
|
def check_auth_publickey(self, username, key):
|
||||||
print 'Auth attempt with key: ' + paramiko.util.hexify(key.get_fingerprint())
|
print 'Auth attempt with key: ' + paramiko.util.hexify(key.get_fingerprint())
|
||||||
if (username == 'robey') and (key == self.good_pub_key):
|
if (username == 'robey') and (key == self.good_pub_key):
|
||||||
return paramiko.Transport.AUTH_SUCCESSFUL
|
return paramiko.AUTH_SUCCESSFUL
|
||||||
return paramiko.Transport.AUTH_FAILED
|
return paramiko.AUTH_FAILED
|
||||||
|
|
||||||
def get_allowed_auths(self, username):
|
def get_allowed_auths(self, username):
|
||||||
return 'password,publickey'
|
return 'password,publickey'
|
||||||
|
|
||||||
|
def check_channel_shell_request(self, channel):
|
||||||
class ServerChannel (paramiko.Channel):
|
self.event.set()
|
||||||
"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):
|
|
||||||
return True
|
return True
|
||||||
|
|
||||||
def check_shell_request(self):
|
def check_channel_pty_request(self, channel, term, width, height, pixelwidth,
|
||||||
self.event.set()
|
pixelheight, modes):
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
|
||||||
|
@ -85,7 +81,8 @@ try:
|
||||||
print '(Failed to load moduli -- gex will be unsupported.)'
|
print '(Failed to load moduli -- gex will be unsupported.)'
|
||||||
raise
|
raise
|
||||||
t.add_server_key(host_key)
|
t.add_server_key(host_key)
|
||||||
t.start_server(event, Server())
|
server = Server()
|
||||||
|
t.start_server(event, server)
|
||||||
while 1:
|
while 1:
|
||||||
event.wait(0.1)
|
event.wait(0.1)
|
||||||
if not t.is_active():
|
if not t.is_active():
|
||||||
|
@ -101,8 +98,8 @@ try:
|
||||||
print '*** No channel.'
|
print '*** No channel.'
|
||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
print 'Authenticated!'
|
print 'Authenticated!'
|
||||||
chan.event.wait(10)
|
server.event.wait(10)
|
||||||
if not chan.event.isSet():
|
if not server.event.isSet():
|
||||||
print '*** Client never asked for a shell.'
|
print '*** Client never asked for a shell.'
|
||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
|
|
||||||
|
|
|
@ -79,6 +79,9 @@ SFTP = sftp.SFTP
|
||||||
ServerInterface = server.ServerInterface
|
ServerInterface = server.ServerInterface
|
||||||
SecurityOptions = transport.SecurityOptions
|
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',
|
__all__ = [ 'Transport',
|
||||||
'SecurityOptions',
|
'SecurityOptions',
|
||||||
|
|
|
@ -47,8 +47,6 @@ class Transport (BaseTransport):
|
||||||
another shell window).
|
another shell window).
|
||||||
"""
|
"""
|
||||||
|
|
||||||
AUTH_SUCCESSFUL, AUTH_PARTIALLY_SUCCESSFUL, AUTH_FAILED = range(3)
|
|
||||||
|
|
||||||
def __init__(self, sock):
|
def __init__(self, sock):
|
||||||
BaseTransport.__init__(self, sock)
|
BaseTransport.__init__(self, sock)
|
||||||
self.username = None
|
self.username = None
|
||||||
|
@ -60,20 +58,22 @@ class Transport (BaseTransport):
|
||||||
self.auth_complete = 0
|
self.auth_complete = 0
|
||||||
|
|
||||||
def __repr__(self):
|
def __repr__(self):
|
||||||
|
out = '<paramiko.Transport at %s' % hex(id(self))
|
||||||
if not self.active:
|
if not self.active:
|
||||||
return '<paramiko.Transport (unconnected)>'
|
out += ' (unconnected)'
|
||||||
out = '<paramiko.Transport'
|
|
||||||
if self.local_cipher != '':
|
|
||||||
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)'
|
|
||||||
else:
|
|
||||||
out += ' (active; %d open channels)' % len(self.channels)
|
|
||||||
elif self.initial_kex_done:
|
|
||||||
out += ' (connected; awaiting auth)'
|
|
||||||
else:
|
else:
|
||||||
out += ' (connecting)'
|
if self.local_cipher != '':
|
||||||
|
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)'
|
||||||
|
else:
|
||||||
|
out += ' (active; %d open channels)' % len(self.channels)
|
||||||
|
elif self.initial_kex_done:
|
||||||
|
out += ' (connected; awaiting auth)'
|
||||||
|
else:
|
||||||
|
out += ' (connecting)'
|
||||||
out += '>'
|
out += '>'
|
||||||
return out
|
return out
|
||||||
|
|
||||||
|
@ -268,21 +268,24 @@ class Transport (BaseTransport):
|
||||||
# the list of valid auth types from the callback anyway
|
# the list of valid auth types from the callback anyway
|
||||||
self._log(DEBUG, 'Auth request to change passwords (rejected)')
|
self._log(DEBUG, 'Auth request to change passwords (rejected)')
|
||||||
newpassword = m.get_string().decode('UTF-8')
|
newpassword = m.get_string().decode('UTF-8')
|
||||||
result = self.AUTH_FAILED
|
result = AUTH_FAILED
|
||||||
else:
|
else:
|
||||||
result = self.server_object.check_auth_password(username, password)
|
result = self.server_object.check_auth_password(username, password)
|
||||||
elif method == 'publickey':
|
elif method == 'publickey':
|
||||||
sig_attached = m.get_boolean()
|
sig_attached = m.get_boolean()
|
||||||
keytype = m.get_string()
|
keytype = m.get_string()
|
||||||
keyblob = 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):
|
if (key is None) or (not key.valid):
|
||||||
self._log(DEBUG, 'Auth rejected: unsupported or mangled public key')
|
self._log(DEBUG, 'Auth rejected: unsupported or mangled public key')
|
||||||
self._disconnect_no_more_auth()
|
self._disconnect_no_more_auth()
|
||||||
return
|
return
|
||||||
# first check if this key is okay... if not, we can skip the verify
|
# first check if this key is okay... if not, we can skip the verify
|
||||||
result = self.server_object.check_auth_publickey(username, key)
|
result = self.server_object.check_auth_publickey(username, key)
|
||||||
if result != self.AUTH_FAILED:
|
if result != AUTH_FAILED:
|
||||||
# key is okay, verify it
|
# key is okay, verify it
|
||||||
if not sig_attached:
|
if not sig_attached:
|
||||||
# client wants to know if this key is acceptable, before it
|
# 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)
|
blob = self._get_session_blob(key, service, username)
|
||||||
if not key.verify_ssh_sig(blob, sig):
|
if not key.verify_ssh_sig(blob, sig):
|
||||||
self._log(DEBUG, 'Auth rejected: invalid signature')
|
self._log(DEBUG, 'Auth rejected: invalid signature')
|
||||||
result = self.AUTH_FAILED
|
result = AUTH_FAILED
|
||||||
else:
|
else:
|
||||||
result = self.server_object.check_auth_none(username)
|
result = self.server_object.check_auth_none(username)
|
||||||
# okay, send result
|
# okay, send result
|
||||||
m = Message()
|
m = Message()
|
||||||
if result == self.AUTH_SUCCESSFUL:
|
if result == AUTH_SUCCESSFUL:
|
||||||
self._log(DEBUG, 'Auth granted.')
|
self._log(DEBUG, 'Auth granted.')
|
||||||
m.add_byte(chr(MSG_USERAUTH_SUCCESS))
|
m.add_byte(chr(MSG_USERAUTH_SUCCESS))
|
||||||
self.auth_complete = 1
|
self.auth_complete = 1
|
||||||
|
@ -310,7 +313,7 @@ class Transport (BaseTransport):
|
||||||
self._log(DEBUG, 'Auth rejected.')
|
self._log(DEBUG, 'Auth rejected.')
|
||||||
m.add_byte(chr(MSG_USERAUTH_FAILURE))
|
m.add_byte(chr(MSG_USERAUTH_FAILURE))
|
||||||
m.add_string(self.server_object.get_allowed_auths(username))
|
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)
|
m.add_boolean(1)
|
||||||
else:
|
else:
|
||||||
m.add_boolean(0)
|
m.add_boolean(0)
|
||||||
|
|
|
@ -87,7 +87,7 @@ class Channel (object):
|
||||||
"""
|
"""
|
||||||
Returns a string representation of this object, for debugging.
|
Returns a string representation of this object, for debugging.
|
||||||
|
|
||||||
@rtype: string
|
@rtype: str
|
||||||
"""
|
"""
|
||||||
out = '<paramiko.Channel %d' % self.chanid
|
out = '<paramiko.Channel %d' % self.chanid
|
||||||
if self.closed:
|
if self.closed:
|
||||||
|
@ -111,7 +111,7 @@ class Channel (object):
|
||||||
basic terminal semantics for the next command you execute.
|
basic terminal semantics for the next command you execute.
|
||||||
|
|
||||||
@param term: the terminal type to emulate (for example, C{'vt100'}).
|
@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
|
@param width: width (in characters) of the terminal screen
|
||||||
@type width: int
|
@type width: int
|
||||||
@param height: height (in characters) of the terminal screen
|
@param height: height (in characters) of the terminal screen
|
||||||
|
@ -173,7 +173,7 @@ class Channel (object):
|
||||||
being executed.
|
being executed.
|
||||||
|
|
||||||
@param command: a shell command to execute.
|
@param command: a shell command to execute.
|
||||||
@type command: string
|
@type command: str
|
||||||
@return: C{True} if the operation succeeded; C{False} if not.
|
@return: C{True} if the operation succeeded; C{False} if not.
|
||||||
@rtype: bool
|
@rtype: bool
|
||||||
"""
|
"""
|
||||||
|
@ -201,7 +201,7 @@ class Channel (object):
|
||||||
requested subsystem.
|
requested subsystem.
|
||||||
|
|
||||||
@param subsystem: name of the subsystem being requested.
|
@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.
|
@return: C{True} if the operation succeeded; C{False} if not.
|
||||||
@rtype: bool
|
@rtype: bool
|
||||||
"""
|
"""
|
||||||
|
@ -268,8 +268,8 @@ class Channel (object):
|
||||||
of the log level used for debugging. The name can be fetched with the
|
of the log level used for debugging. The name can be fetched with the
|
||||||
L{get_name} method.
|
L{get_name} method.
|
||||||
|
|
||||||
@param name: new channel name
|
@param name: new channel name.
|
||||||
@type name: string
|
@type name: str
|
||||||
"""
|
"""
|
||||||
self.name = name
|
self.name = name
|
||||||
self.logger = logging.getLogger('paramiko.chan.' + 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}.
|
Get the name of this channel that was previously set by L{set_name}.
|
||||||
|
|
||||||
@return: the name of this channel
|
@return: the name of this channel.
|
||||||
@rtype: string
|
@rtype: str
|
||||||
"""
|
"""
|
||||||
return self.name
|
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
|
### socket API
|
||||||
|
|
||||||
|
@ -389,7 +403,7 @@ class Channel (object):
|
||||||
@param nbytes: maximum number of bytes to read.
|
@param nbytes: maximum number of bytes to read.
|
||||||
@type nbytes: int
|
@type nbytes: int
|
||||||
@return: data.
|
@return: data.
|
||||||
@rtype: string
|
@rtype: str
|
||||||
|
|
||||||
@raise socket.timeout: if no data is ready before the timeout set by
|
@raise socket.timeout: if no data is ready before the timeout set by
|
||||||
L{settimeout}.
|
L{settimeout}.
|
||||||
|
@ -436,7 +450,7 @@ class Channel (object):
|
||||||
data.
|
data.
|
||||||
|
|
||||||
@param s: data to send.
|
@param s: data to send.
|
||||||
@type s: string
|
@type s: str
|
||||||
@return: number of bytes actually sent.
|
@return: number of bytes actually sent.
|
||||||
@rtype: int
|
@rtype: int
|
||||||
|
|
||||||
|
@ -490,7 +504,7 @@ class Channel (object):
|
||||||
either all data has been sent or an error occurs. Nothing is returned.
|
either all data has been sent or an error occurs. Nothing is returned.
|
||||||
|
|
||||||
@param s: data to send.
|
@param s: data to send.
|
||||||
@type s: string
|
@type s: str
|
||||||
|
|
||||||
@raise socket.timeout: if sending stalled for longer than the timeout
|
@raise socket.timeout: if sending stalled for longer than the timeout
|
||||||
set by L{settimeout}.
|
set by L{settimeout}.
|
||||||
|
@ -579,83 +593,6 @@ class Channel (object):
|
||||||
### overrides
|
### 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
|
### calls from Transport
|
||||||
|
@ -717,6 +654,7 @@ class Channel (object):
|
||||||
def _handle_request(self, m):
|
def _handle_request(self, m):
|
||||||
key = m.get_string()
|
key = m.get_string()
|
||||||
want_reply = m.get_boolean()
|
want_reply = m.get_boolean()
|
||||||
|
server = self.transport.server_object
|
||||||
ok = False
|
ok = False
|
||||||
if key == 'exit-status':
|
if key == 'exit-status':
|
||||||
self.exit_status = m.get_int()
|
self.exit_status = m.get_int()
|
||||||
|
@ -731,18 +669,32 @@ class Channel (object):
|
||||||
pixelwidth = m.get_int()
|
pixelwidth = m.get_int()
|
||||||
pixelheight = m.get_int()
|
pixelheight = m.get_int()
|
||||||
modes = m.get_string()
|
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':
|
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':
|
elif key == 'subsystem':
|
||||||
name = m.get_string()
|
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':
|
elif key == 'window-change':
|
||||||
width = m.get_int()
|
width = m.get_int()
|
||||||
height = m.get_int()
|
height = m.get_int()
|
||||||
pixelwidth = m.get_int()
|
pixelwidth = m.get_int()
|
||||||
pixelheight = 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:
|
else:
|
||||||
self._log(DEBUG, 'Unhandled channel request "%s"' % key)
|
self._log(DEBUG, 'Unhandled channel request "%s"' % key)
|
||||||
ok = False
|
ok = False
|
||||||
|
@ -931,7 +883,7 @@ class ChannelFile (BufferedFile):
|
||||||
"""
|
"""
|
||||||
Returns a string representation of this object, for debugging.
|
Returns a string representation of this object, for debugging.
|
||||||
|
|
||||||
@rtype: string
|
@rtype: str
|
||||||
"""
|
"""
|
||||||
return '<paramiko.ChannelFile from ' + repr(self.channel) + '>'
|
return '<paramiko.ChannelFile from ' + repr(self.channel) + '>'
|
||||||
|
|
||||||
|
|
|
@ -35,7 +35,19 @@ MSG_CHANNEL_OPEN, MSG_CHANNEL_OPEN_SUCCESS, MSG_CHANNEL_OPEN_FAILURE, \
|
||||||
MSG_CHANNEL_SUCCESS, MSG_CHANNEL_FAILURE = range(90, 101)
|
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:
|
# 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 = {
|
CONNECTION_FAILED_CODE = {
|
||||||
1: 'Administratively prohibited',
|
1: 'Administratively prohibited',
|
||||||
2: 'Connect failed',
|
2: 'Connect failed',
|
||||||
|
@ -43,6 +55,7 @@ CONNECTION_FAILED_CODE = {
|
||||||
4: 'Resource shortage'
|
4: 'Resource shortage'
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
DISCONNECT_SERVICE_NOT_AVAILABLE, DISCONNECT_AUTH_CANCELLED_BY_USER, \
|
DISCONNECT_SERVICE_NOT_AVAILABLE, DISCONNECT_AUTH_CANCELLED_BY_USER, \
|
||||||
DISCONNECT_NO_MORE_AUTH_METHODS_AVAILABLE = 7, 13, 14
|
DISCONNECT_NO_MORE_AUTH_METHODS_AVAILABLE = 7, 13, 14
|
||||||
|
|
||||||
|
|
|
@ -22,6 +22,7 @@
|
||||||
L{ServerInterface} is an interface to override for server support.
|
L{ServerInterface} is an interface to override for server support.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
from common import *
|
||||||
from auth_transport import Transport
|
from auth_transport import Transport
|
||||||
|
|
||||||
class ServerInterface (object):
|
class ServerInterface (object):
|
||||||
|
@ -37,31 +38,46 @@ class ServerInterface (object):
|
||||||
def check_channel_request(self, kind, chanid):
|
def check_channel_request(self, kind, chanid):
|
||||||
"""
|
"""
|
||||||
Determine if a channel request of a given type will be granted, and
|
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
|
return C{OPEN_SUCCEEDED} or an error code. This method is
|
||||||
mode when the client requests a channel, after authentication is
|
called in server mode when the client requests a channel, after
|
||||||
complete.
|
authentication is complete.
|
||||||
|
|
||||||
You will generally want to subclass L{Channel} to override some of the
|
If you allow channel requests (and an ssh server that didn't would be
|
||||||
methods for handling client requests (such as connecting to a subsystem
|
useless), you should also override some of the channel request methods
|
||||||
opening a shell) to determine what you want to allow or disallow. For
|
below, which are used to determine which services will be allowed on
|
||||||
this reason, L{check_channel_request} must return a new object of that
|
a given channel:
|
||||||
type. The C{chanid} parameter is passed so that you can use it in
|
- L{check_channel_pty_request}
|
||||||
L{Channel}'s constructor.
|
- 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
|
The C{chanid} parameter is a small number that uniquely identifies the
|
||||||
channel requests. A useful server must override this method.
|
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
|
@param kind: the kind of channel the client would like to open
|
||||||
(usually C{"session"}).
|
(usually C{"session"}).
|
||||||
@type kind: string
|
@type kind: str
|
||||||
@param chanid: ID of the channel, required to create a new L{Channel}
|
@param chanid: ID of the channel, required to create a new L{Channel}
|
||||||
object.
|
object.
|
||||||
@type chanid: int
|
@type chanid: int
|
||||||
@return: a new L{Channel} object (or subclass thereof), or C{None} to
|
@return: a success or failure code (listed above).
|
||||||
refuse the request.
|
@rtype: int
|
||||||
@rtype: L{Channel}
|
|
||||||
"""
|
"""
|
||||||
return None
|
return OPEN_FAILED_ADMINISTRATIVELY_PROHIBITED
|
||||||
|
|
||||||
def get_allowed_auths(self, username):
|
def get_allowed_auths(self, username):
|
||||||
"""
|
"""
|
||||||
|
@ -76,9 +92,9 @@ class ServerInterface (object):
|
||||||
The default implementation always returns C{"password"}.
|
The default implementation always returns C{"password"}.
|
||||||
|
|
||||||
@param username: the username requesting authentication.
|
@param username: the username requesting authentication.
|
||||||
@type username: string
|
@type username: str
|
||||||
@return: a comma-separated list of authentication types
|
@return: a comma-separated list of authentication types
|
||||||
@rtype: string
|
@rtype: str
|
||||||
"""
|
"""
|
||||||
return 'password'
|
return 'password'
|
||||||
|
|
||||||
|
@ -87,46 +103,46 @@ class ServerInterface (object):
|
||||||
Determine if a client may open channels with no (further)
|
Determine if a client may open channels with no (further)
|
||||||
authentication.
|
authentication.
|
||||||
|
|
||||||
Return L{Transport.AUTH_FAILED} if the client must authenticate, or
|
Return L{AUTH_FAILED} if the client must authenticate, or
|
||||||
L{Transport.AUTH_SUCCESSFUL} if it's okay for the client to not
|
L{AUTH_SUCCESSFUL} if it's okay for the client to not
|
||||||
authenticate.
|
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.
|
@param username: the username of the client.
|
||||||
@type username: string
|
@type username: str
|
||||||
@return: L{Transport.AUTH_FAILED} if the authentication fails;
|
@return: L{AUTH_FAILED} if the authentication fails;
|
||||||
L{Transport.AUTH_SUCCESSFUL} if it succeeds.
|
L{AUTH_SUCCESSFUL} if it succeeds.
|
||||||
@rtype: int
|
@rtype: int
|
||||||
"""
|
"""
|
||||||
return Transport.AUTH_FAILED
|
return AUTH_FAILED
|
||||||
|
|
||||||
def check_auth_password(self, username, password):
|
def check_auth_password(self, username, password):
|
||||||
"""
|
"""
|
||||||
Determine if a given username and password supplied by the client is
|
Determine if a given username and password supplied by the client is
|
||||||
acceptable for use in authentication.
|
acceptable for use in authentication.
|
||||||
|
|
||||||
Return L{Transport.AUTH_FAILED} if the password is not accepted,
|
Return L{AUTH_FAILED} if the password is not accepted,
|
||||||
L{Transport.AUTH_SUCCESSFUL} if the password is accepted and completes
|
L{AUTH_SUCCESSFUL} if the password is accepted and completes
|
||||||
the authentication, or L{Transport.AUTH_PARTIALLY_SUCCESSFUL} if your
|
the authentication, or L{AUTH_PARTIALLY_SUCCESSFUL} if your
|
||||||
authentication is stateful, and this key is accepted for
|
authentication is stateful, and this key is accepted for
|
||||||
authentication, but more authentication is required. (In this latter
|
authentication, but more authentication is required. (In this latter
|
||||||
case, L{get_allowed_auths} will be called to report to the client what
|
case, L{get_allowed_auths} will be called to report to the client what
|
||||||
options it has for continuing the authentication.)
|
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.
|
@param username: the username of the authenticating client.
|
||||||
@type username: string
|
@type username: str
|
||||||
@param password: the password given by the client.
|
@param password: the password given by the client.
|
||||||
@type password: string
|
@type password: str
|
||||||
@return: L{Transport.AUTH_FAILED} if the authentication fails;
|
@return: L{AUTH_FAILED} if the authentication fails;
|
||||||
L{Transport.AUTH_SUCCESSFUL} if it succeeds;
|
L{AUTH_SUCCESSFUL} if it succeeds;
|
||||||
L{Transport.AUTH_PARTIALLY_SUCCESSFUL} if the password auth is
|
L{AUTH_PARTIALLY_SUCCESSFUL} if the password auth is
|
||||||
successful, but authentication must continue.
|
successful, but authentication must continue.
|
||||||
@rtype: int
|
@rtype: int
|
||||||
"""
|
"""
|
||||||
return Transport.AUTH_FAILED
|
return AUTH_FAILED
|
||||||
|
|
||||||
def check_auth_publickey(self, username, key):
|
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
|
check the username and key and decide if you would accept a signature
|
||||||
made using this key.
|
made using this key.
|
||||||
|
|
||||||
Return L{Transport.AUTH_FAILED} if the key is not accepted,
|
Return L{AUTH_FAILED} if the key is not accepted,
|
||||||
L{Transport.AUTH_SUCCESSFUL} if the key is accepted and completes the
|
L{AUTH_SUCCESSFUL} if the key is accepted and completes the
|
||||||
authentication, or L{Transport.AUTH_PARTIALLY_SUCCESSFUL} if your
|
authentication, or L{AUTH_PARTIALLY_SUCCESSFUL} if your
|
||||||
authentication is stateful, and this key is accepted for
|
authentication is stateful, and this key is accepted for
|
||||||
authentication, but more authentication is required. (In this latter
|
authentication, but more authentication is required. (In this latter
|
||||||
case, L{get_allowed_auths} will be called to report to the client what
|
case, L{get_allowed_auths} will be called to report to the client what
|
||||||
options it has for continuing the authentication.)
|
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.
|
@param username: the username of the authenticating client.
|
||||||
@type username: string
|
@type username: str
|
||||||
@param key: the key object provided by the client.
|
@param key: the key object provided by the client.
|
||||||
@type key: L{PKey <pkey.PKey>}
|
@type key: L{PKey <pkey.PKey>}
|
||||||
@return: L{Transport.AUTH_FAILED} if the client can't authenticate
|
@return: L{AUTH_FAILED} if the client can't authenticate
|
||||||
with this key; L{Transport.AUTH_SUCCESSFUL} if it can;
|
with this key; L{AUTH_SUCCESSFUL} if it can;
|
||||||
L{Transport.AUTH_PARTIALLY_SUCCESSFUL} if it can authenticate with
|
L{AUTH_PARTIALLY_SUCCESSFUL} if it can authenticate with
|
||||||
this key but must continue with authentication.
|
this key but must continue with authentication.
|
||||||
@rtype: int
|
@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
|
||||||
|
|
|
@ -36,7 +36,7 @@ _FX_OK = 0
|
||||||
_FX_EOF, _FX_NO_SUCH_FILE, _FX_PERMISSION_DENIED, _FX_FAILURE, _FX_BAD_MESSAGE, \
|
_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)
|
_FX_NO_CONNECTION, _FX_CONNECTION_LOST, _FX_OP_UNSUPPORTED = range(1, 9)
|
||||||
|
|
||||||
VERSION = 3
|
_VERSION = 3
|
||||||
|
|
||||||
|
|
||||||
class SFTPAttributes (object):
|
class SFTPAttributes (object):
|
||||||
|
@ -238,12 +238,12 @@ class SFTP (object):
|
||||||
else:
|
else:
|
||||||
self.logger = logging.getLogger('paramiko.sftp')
|
self.logger = logging.getLogger('paramiko.sftp')
|
||||||
# protocol: (maybe should move to a different method)
|
# 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()
|
t, data = self._read_packet()
|
||||||
if t != _CMD_VERSION:
|
if t != _CMD_VERSION:
|
||||||
raise SFTPError('Incompatible sftp protocol')
|
raise SFTPError('Incompatible sftp protocol')
|
||||||
version = struct.unpack('>I', data[:4])[0]
|
version = struct.unpack('>I', data[:4])[0]
|
||||||
# if version != VERSION:
|
# if version != _VERSION:
|
||||||
# raise SFTPError('Incompatible sftp protocol')
|
# raise SFTPError('Incompatible sftp protocol')
|
||||||
|
|
||||||
def from_transport(selfclass, t):
|
def from_transport(selfclass, t):
|
||||||
|
|
|
@ -64,6 +64,8 @@ class SecurityOptions (object):
|
||||||
If you try to add an algorithm that paramiko doesn't recognize,
|
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
|
C{ValueError} will be raised. If you try to assign something besides a
|
||||||
tuple to one of the fields, L{TypeError} will be raised.
|
tuple to one of the fields, L{TypeError} will be raised.
|
||||||
|
|
||||||
|
@since: ivysaur
|
||||||
"""
|
"""
|
||||||
__slots__ = [ 'ciphers', 'digests', 'key_types', 'kex', '_transport' ]
|
__slots__ = [ 'ciphers', 'digests', 'key_types', 'kex', '_transport' ]
|
||||||
|
|
||||||
|
@ -74,7 +76,7 @@ class SecurityOptions (object):
|
||||||
"""
|
"""
|
||||||
Returns a string representation of this object, for debugging.
|
Returns a string representation of this object, for debugging.
|
||||||
|
|
||||||
@rtype: string
|
@rtype: str
|
||||||
"""
|
"""
|
||||||
return '<paramiko.SecurityOptions for %s>' % repr(self._transport)
|
return '<paramiko.SecurityOptions for %s>' % repr(self._transport)
|
||||||
|
|
||||||
|
@ -162,9 +164,6 @@ class BaseTransport (threading.Thread):
|
||||||
REKEY_PACKETS = pow(2, 30)
|
REKEY_PACKETS = pow(2, 30)
|
||||||
REKEY_BYTES = 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
|
_modulus_pack = None
|
||||||
|
|
||||||
def __init__(self, sock):
|
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
|
If the object is not actually a socket, it must have the following
|
||||||
methods:
|
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
|
returns an int representing the number of bytes written. Returns
|
||||||
0 or raises C{EOFError} if the stream has been closed.
|
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
|
- C{recv(int)}: Reads from 1 to C{int} bytes and returns them as a
|
||||||
|
@ -264,17 +263,19 @@ class BaseTransport (threading.Thread):
|
||||||
"""
|
"""
|
||||||
Returns a string representation of this object, for debugging.
|
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:
|
if not self.active:
|
||||||
return '<paramiko.BaseTransport (unconnected)>'
|
out += ' (unconnected)'
|
||||||
out = '<paramiko.BaseTransport'
|
|
||||||
if self.local_cipher != '':
|
|
||||||
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:
|
else:
|
||||||
out += ' (active; %d open channels)' % len(self.channels)
|
if self.local_cipher != '':
|
||||||
|
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:
|
||||||
|
out += ' (active; %d open channels)' % len(self.channels)
|
||||||
out += '>'
|
out += '>'
|
||||||
return out
|
return out
|
||||||
|
|
||||||
|
@ -287,6 +288,8 @@ class BaseTransport (threading.Thread):
|
||||||
@return: an object that can be used to change the preferred algorithms
|
@return: an object that can be used to change the preferred algorithms
|
||||||
for encryption, digest (hash), public key, and key exchange.
|
for encryption, digest (hash), public key, and key exchange.
|
||||||
@rtype: L{SecurityOptions}
|
@rtype: L{SecurityOptions}
|
||||||
|
|
||||||
|
@since: ivysaur
|
||||||
"""
|
"""
|
||||||
return SecurityOptions(self)
|
return SecurityOptions(self)
|
||||||
|
|
||||||
|
@ -407,7 +410,7 @@ class BaseTransport (threading.Thread):
|
||||||
|
|
||||||
@param filename: optional path to the moduli file, if you happen to
|
@param filename: optional path to the moduli file, if you happen to
|
||||||
know that it's not in a standard location.
|
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
|
@return: True if a moduli file was successfully loaded; False
|
||||||
otherwise.
|
otherwise.
|
||||||
@rtype: bool
|
@rtype: bool
|
||||||
|
@ -502,6 +505,9 @@ class BaseTransport (threading.Thread):
|
||||||
@rtype: L{Channel}
|
@rtype: L{Channel}
|
||||||
"""
|
"""
|
||||||
chan = None
|
chan = None
|
||||||
|
if not self.active:
|
||||||
|
# don't bother trying to allocate a channel
|
||||||
|
return None
|
||||||
try:
|
try:
|
||||||
self.lock.acquire()
|
self.lock.acquire()
|
||||||
chanid = self.channel_counter
|
chanid = self.channel_counter
|
||||||
|
@ -603,7 +609,7 @@ class BaseTransport (threading.Thread):
|
||||||
extensions to the SSH2 protocol.
|
extensions to the SSH2 protocol.
|
||||||
|
|
||||||
@param kind: name of the request.
|
@param kind: name of the request.
|
||||||
@type kind: string
|
@type kind: str
|
||||||
@param data: an optional tuple containing additional data to attach
|
@param data: an optional tuple containing additional data to attach
|
||||||
to the request.
|
to the request.
|
||||||
@type data: tuple
|
@type data: tuple
|
||||||
|
@ -659,7 +665,7 @@ class BaseTransport (threading.Thread):
|
||||||
does not support any global requests.
|
does not support any global requests.
|
||||||
|
|
||||||
@param kind: the kind of global request being made.
|
@param kind: the kind of global request being made.
|
||||||
@type kind: string
|
@type kind: str
|
||||||
@param msg: any extra arguments to the request.
|
@param msg: any extra arguments to the request.
|
||||||
@type msg: L{Message}
|
@type msg: L{Message}
|
||||||
@return: C{True} or a tuple of data if the request was granted;
|
@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
|
@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
|
(usually C{"ssh-rsa"} or C{"ssh-dss"}), or C{None} if you don't want
|
||||||
to do host key verification.
|
to do host key verification.
|
||||||
@type hostkeytype: string
|
@type hostkeytype: str
|
||||||
@param hostkey: the host key expected from the server, or C{None} if
|
@param hostkey: the host key expected from the server, or C{None} if
|
||||||
you don't want to do host key verification.
|
you don't want to do host key verification.
|
||||||
@type hostkey: string
|
@type hostkey: str
|
||||||
@param username: the username to authenticate as.
|
@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
|
@param password: a password to use for authentication, if you want to
|
||||||
use password authentication; otherwise C{None}.
|
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
|
@param pkey: a private key to use for authentication, if you want to
|
||||||
use private key authentication; otherwise C{None}.
|
use private key authentication; otherwise C{None}.
|
||||||
@type pkey: L{PKey<pkey.PKey>}
|
@type pkey: L{PKey<pkey.PKey>}
|
||||||
|
@ -1042,6 +1048,9 @@ class BaseTransport (threading.Thread):
|
||||||
chanid = m.get_int()
|
chanid = m.get_int()
|
||||||
if self.channels.has_key(chanid):
|
if self.channels.has_key(chanid):
|
||||||
self._channel_handler_table[ptype](self.channels[chanid], m)
|
self._channel_handler_table[ptype](self.channels[chanid], m)
|
||||||
|
else:
|
||||||
|
self._log(ERROR, 'Channel request for unknown channel %d' % chanid)
|
||||||
|
self.active = False
|
||||||
else:
|
else:
|
||||||
self._log(WARNING, 'Oops, unhandled type %d' % ptype)
|
self._log(WARNING, 'Oops, unhandled type %d' % ptype)
|
||||||
msg = Message()
|
msg = Message()
|
||||||
|
@ -1127,7 +1136,9 @@ class BaseTransport (threading.Thread):
|
||||||
if self.server_mode:
|
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
|
# 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__,
|
available_server_keys = filter(self.server_key_dict.keys().__contains__,
|
||||||
self._preferred_keys)
|
self._preferred_keys)
|
||||||
else:
|
else:
|
||||||
|
@ -1394,7 +1405,7 @@ class BaseTransport (threading.Thread):
|
||||||
if not self.server_mode:
|
if not self.server_mode:
|
||||||
self._log(DEBUG, 'Rejecting "%s" channel request from server.' % kind)
|
self._log(DEBUG, 'Rejecting "%s" channel request from server.' % kind)
|
||||||
reject = True
|
reject = True
|
||||||
reason = self.OPEN_FAILED_ADMINISTRATIVELY_PROHIBITED
|
reason = OPEN_FAILED_ADMINISTRATIVELY_PROHIBITED
|
||||||
else:
|
else:
|
||||||
try:
|
try:
|
||||||
self.lock.acquire()
|
self.lock.acquire()
|
||||||
|
@ -1402,14 +1413,10 @@ class BaseTransport (threading.Thread):
|
||||||
self.channel_counter += 1
|
self.channel_counter += 1
|
||||||
finally:
|
finally:
|
||||||
self.lock.release()
|
self.lock.release()
|
||||||
chan = self.server_object.check_channel_request(kind, my_chanid)
|
reason = self.server_object.check_channel_request(kind, my_chanid)
|
||||||
if (chan is None) or (type(chan) is int):
|
if reason != OPEN_SUCCEEDED:
|
||||||
self._log(DEBUG, 'Rejecting "%s" channel request from client.' % kind)
|
self._log(DEBUG, 'Rejecting "%s" channel request from client.' % kind)
|
||||||
reject = True
|
reject = True
|
||||||
if type(chan) is int:
|
|
||||||
reason = chan
|
|
||||||
else:
|
|
||||||
reason = self.OPEN_FAILED_ADMINISTRATIVELY_PROHIBITED
|
|
||||||
if reject:
|
if reject:
|
||||||
msg = Message()
|
msg = Message()
|
||||||
msg.add_byte(chr(MSG_CHANNEL_OPEN_FAILURE))
|
msg.add_byte(chr(MSG_CHANNEL_OPEN_FAILURE))
|
||||||
|
@ -1419,6 +1426,7 @@ class BaseTransport (threading.Thread):
|
||||||
msg.add_string('en')
|
msg.add_string('en')
|
||||||
self._send_message(msg)
|
self._send_message(msg)
|
||||||
return
|
return
|
||||||
|
chan = Channel(my_chanid)
|
||||||
try:
|
try:
|
||||||
self.lock.acquire()
|
self.lock.acquire()
|
||||||
self.channels[my_chanid] = chan
|
self.channels[my_chanid] = chan
|
||||||
|
|
Loading…
Reference in New Issue