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

renamed auth_key -> auth_publickey; more docs.
renamed Transport.auth_key to auth_publickey for consistency.  and lots more
documentation.
This commit is contained in:
Robey Pointer 2003-12-31 06:31:43 +00:00
parent daa8a2ec0d
commit 3a8887a420
6 changed files with 176 additions and 13 deletions

2
README
View File

@ -135,7 +135,5 @@ are still running (and you'll have to kill -9 from another shell window).
* ctr forms of ciphers are missing (blowfish-ctr, aes128-ctr, aes256-ctr) * ctr forms of ciphers are missing (blowfish-ctr, aes128-ctr, aes256-ctr)
* can't handle password-protected private key files * can't handle password-protected private key files
* multi-part auth not supported (ie, need username AND pk) * multi-part auth not supported (ie, need username AND pk)
* should have a simple synchronous method that handles all auth & events,
by pre-seeding the password or key info, and the expected key
* server mode needs better doc * server mode needs better doc

View File

@ -108,7 +108,7 @@ try:
if len(path) == 0: if len(path) == 0:
path = default_path path = default_path
key.read_private_key_file(path) key.read_private_key_file(path)
t.auth_key(username, key, event) t.auth_publickey(username, key, event)
elif auth == 'd': elif auth == 'd':
key = paramiko.DSSKey() key = paramiko.DSSKey()
default_path = os.environ['HOME'] + '/.ssh/id_dsa' default_path = os.environ['HOME'] + '/.ssh/id_dsa'

View File

@ -41,6 +41,10 @@ class ServerTransport(paramiko.Transport):
return self.AUTH_SUCCESSFUL return self.AUTH_SUCCESSFUL
return self.AUTH_FAILED return self.AUTH_FAILED
def get_allowed_auths(self, username):
return 'password,publickey'
class ServerChannel(paramiko.Channel): class ServerChannel(paramiko.Channel):
"Channel descendant that pretends to understand pty and shell requests" "Channel descendant that pretends to understand pty and shell requests"
@ -86,7 +90,6 @@ 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.ultra_debug = 0
t.start_server(event) t.start_server(event)
while 1: while 1:
event.wait(0.1) event.wait(0.1)

View File

@ -9,11 +9,15 @@ __author__ = "Robey Pointer <robey@lag.net>"
__date__ = "10 Nov 2003" __date__ = "10 Nov 2003"
__version__ = "0.1-charmander" __version__ = "0.1-charmander"
__credits__ = "Huzzah!" __credits__ = "Huzzah!"
__license__ = "Lesser GNU Public License (LGPL)"
import ssh_exception, transport, auth_transport, channel, rsakey, dsskey, util import ssh_exception, transport, auth_transport, channel, rsakey, dsskey
class SSHException (ssh_exception.SSHException): class SSHException (ssh_exception.SSHException):
"""
Exception thrown by failures in SSH2 protocol negotiation or logic errors.
"""
pass pass
class Transport (auth_transport.Transport): class Transport (auth_transport.Transport):
@ -34,9 +38,17 @@ class Channel (channel.Channel):
pass pass
class RSAKey (rsakey.RSAKey): class RSAKey (rsakey.RSAKey):
"""
Representation of an RSA key which can be used to sign and verify SSH2
data.
"""
pass pass
class DSSKey (dsskey.DSSKey): class DSSKey (dsskey.DSSKey):
"""
Representation of a DSS key which can be used to sign an verify SSH2
data.
"""
pass pass

View File

@ -56,7 +56,7 @@ class Transport (BaseTransport):
""" """
return self.authenticated and self.active return self.authenticated and self.active
def auth_key(self, username, key, event): def auth_publickey(self, username, key, event):
""" """
Authenticate to the server using a private key. The key is used to 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 sign data from the server, so it must include the private part. The
@ -113,15 +113,70 @@ class Transport (BaseTransport):
self.lock.release() self.lock.release()
def get_allowed_auths(self, username): def get_allowed_auths(self, username):
"override me!" """
I{(subclass override)}
Return a list of authentication methods supported by the server.
This list is sent to clients attempting to authenticate, to inform them
of authentication methods that might be successful.
The "list" is actually a string of comma-separated names of types of
authentication. Possible values are C{"password"}, C{"publickey"},
and C{"none"}.
The default implementation always returns C{"password"}.
@param username: the username requesting authentication.
@type username: string
@return: a comma-separated list of authentication types
@rtype: string
"""
return 'password' return 'password'
def check_auth_none(self, username): def check_auth_none(self, username):
"override me! return int ==> auth status" """
I{(subclass override)}
Determine if a client may open channels with no (further)
authentication. You should override this method in server mode.
Return C{AUTH_FAILED} if the client must authenticate, or
C{AUTH_SUCCESSFUL} if it's okay for the client to not authenticate.
The default implementation always returns C{AUTH_FAILED}.
@param username: the username of the client.
@type username: string
@return: C{AUTH_FAILED} if the authentication fails; C{AUTH_SUCCESSFUL}
if it succeeds.
@rtype: int
"""
return self.AUTH_FAILED return self.AUTH_FAILED
def check_auth_password(self, username, password): def check_auth_password(self, username, password):
"override me! return int ==> auth status" """
I{(subclass override)}
Determine if a given username and password supplied by the client is
acceptable for use in authentication. You should override this method
in server mode.
Return C{AUTH_FAILED} if the password is not accepted,
C{AUTH_SUCCESSFUL} if the password 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 password: the password given by the client.
@type password: string
@return: C{AUTH_FAILED} if the authentication fails; C{AUTH_SUCCESSFUL}
if it succeeds; C{AUTH_PARTIALLY_SUCCESSFUL} if the password auth is
successful, but authentication must continue.
@rtype: int
"""
return self.AUTH_FAILED return self.AUTH_FAILED
def check_auth_publickey(self, username, key): def check_auth_publickey(self, username, key):

View File

@ -176,10 +176,59 @@ class BaseTransport (threading.Thread):
return out return out
def start_client(self, event=None): def start_client(self, event=None):
"""
Negotiate a new SSH2 session as a client. This is the first step after
creating a new L{Transport}. A separate thread is created for protocol
negotiation, so this method returns immediately.
When negotiation is done (successful or not), the given C{Event} will
be triggered. On failure, L{is_active} will return C{False}.
After a successful negotiation, you will usually want to authenticate,
calling L{auth_password <Transport.auth_password>} or
L{auth_publickey <Transport.auth_publickey>}.
@note: L{connect} is a simpler method for connecting as a client.
@note: After calling this method (or L{start_server} or L{connect}),
you should no longer directly read from or write to the original socket
object.
@param event: an event to trigger when negotiation is complete.
@type event: threading.Event
"""
self.completion_event = event self.completion_event = event
self.start() self.start()
def start_server(self, event=None): def start_server(self, event=None):
"""
Negotiate a new SSH2 session as a server. This is the first step after
creating a new L{Transport} and setting up your server host key(s). A
separate thread is created for protocol negotiation, so this method
returns immediately.
When negotiation is done (successful or not), the given C{Event} will
be triggered. On failure, L{is_active} will return C{False}.
After a successful negotiation, the client will need to authenticate.
Override the methods
L{get_allowed_auths <Transport.get_allowed_auths>},
L{check_auth_none <Transport.check_auth_none>},
L{check_auth_password <Transport.check_auth_password>}, and
L{check_auth_publickey <Transport.check_auth_publickey>} to control the
authentication process.
After a successful authentication, the client should request to open
a channel. Override L{check_channel_request} to allow channels to
be opened.
@note: After calling this method (or L{start_client} or L{connect}),
you should no longer directly read from or write to the original socket
object.
@param event: an event to trigger when negotiation is complete.
@type event: threading.Event
"""
self.server_mode = 1 self.server_mode = 1
self.completion_event = event self.completion_event = event
self.start() self.start()
@ -301,9 +350,29 @@ class BaseTransport (threading.Thread):
return self.active return self.active
def open_session(self): def open_session(self):
"""
Request a new channel to the server, of type C{"session"}. This
is just an alias for C{open_channel('session')}.
@return: a new L{Channel} on success, or C{None} if the request is
rejected or the session ends prematurely.
@rtype: L{Channel}
"""
return self.open_channel('session') return self.open_channel('session')
def open_channel(self, kind): def open_channel(self, kind):
"""
Request a new channel to the server. L{Channel}s are socket-like
objects used for the actual transfer of data across the session.
You may only request a channel after negotiating encryption (using
L{connect} or L{start_client} and authenticating.
@param kind: the kind of channel requested (usually C{"session"}).
@type kind: string
@return: a new L{Channel} on success, or C{None} if the request is
rejected or the session ends prematurely.
@rtype: L{Channel}
"""
chan = None chan = None
try: try:
self.lock.acquire() self.lock.acquire()
@ -361,7 +430,33 @@ class BaseTransport (threading.Thread):
return True return True
def check_channel_request(self, kind, chanid): def check_channel_request(self, kind, chanid):
"override me! return object descended from Channel to allow, or None to reject" """
I{(subclass override)}
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.
In server mode, you will generally want to subclass L{Channel} to
override some of the methods for handling client requests (such as
connecting to a subsystem or 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.
The default implementation always returns C{None}, rejecting any
channel requests. A useful server must override this method.
@param kind: the kind of channel the client would like to open
(usually C{"session"}).
@type kind: string
@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 None return None
def accept(self, timeout=None): def accept(self, timeout=None):
@ -385,8 +480,8 @@ class BaseTransport (threading.Thread):
Negotiate an SSH2 session, and optionally verify the server's host key Negotiate an SSH2 session, and optionally verify the server's host key
and authenticate using a password or private key. This is a shortcut and authenticate using a password or private key. This is a shortcut
for L{start_client}, L{get_remote_server_key}, and for L{start_client}, L{get_remote_server_key}, and
L{Transport.auth_password} or L{Transport.auth_key}. Use those methods L{Transport.auth_password} or L{Transport.auth_publickey}. Use those
if you want more control. methods if you want more control.
You can use this method immediately after creating a Transport to You can use this method immediately after creating a Transport to
negotiate encryption with a server. If it fails, an exception will be negotiate encryption with a server. If it fails, an exception will be
@ -450,7 +545,7 @@ class BaseTransport (threading.Thread):
self.auth_password(username, password, event) self.auth_password(username, password, event)
else: else:
self._log(DEBUG, 'Attempting password auth...') self._log(DEBUG, 'Attempting password auth...')
self.auth_key(username, pkey, event) self.auth_publickey(username, pkey, event)
while 1: while 1:
event.wait(0.1) event.wait(0.1)
if not self.active: if not self.active: