summaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
-rw-r--r--paramiko/auth_transport.py44
-rw-r--r--paramiko/ssh_exception.py10
2 files changed, 42 insertions, 12 deletions
diff --git a/paramiko/auth_transport.py b/paramiko/auth_transport.py
index 45ef8ce9..d4053e4b 100644
--- a/paramiko/auth_transport.py
+++ b/paramiko/auth_transport.py
@@ -32,7 +32,7 @@ from common import *
import util
from transport import BaseTransport
from message import Message
-from ssh_exception import SSHException, BadAuthenticationType
+from ssh_exception import SSHException, BadAuthenticationType, PartialAuthentication
class Transport (BaseTransport):
@@ -118,6 +118,10 @@ class Transport (BaseTransport):
authentication succeeds or fails. On failure, an exception is raised.
Otherwise, the method simply returns.
+ If the server requires multi-step authentication (which is very rare),
+ this method will return a list of auth types permissible for the next
+ step. Otherwise, in the normal case, an empty list is returned.
+
@param username: the username to authenticate as.
@type username: string
@param key: the private key to authenticate with.
@@ -125,6 +129,9 @@ class Transport (BaseTransport):
@param event: an event to trigger when the authentication attempt is
complete (whether it was successful or not)
@type event: threading.Event
+ @return: list of auth types permissible for the next stage of
+ authentication (normally empty).
+ @rtype: list
@raise BadAuthenticationType: if public-key authentication isn't
allowed by the server for this user (and no event was passed in).
@@ -149,8 +156,8 @@ class Transport (BaseTransport):
self.lock.release()
if event is not None:
# caller wants to wait for event themselves
- return
- self._wait_for_response(my_event)
+ return []
+ return self._wait_for_response(my_event)
def auth_password(self, username, password, event=None):
"""
@@ -165,7 +172,11 @@ class Transport (BaseTransport):
Since 1.1, if no event is passed, this method will block until the
authentication succeeds or fails. On failure, an exception is raised.
Otherwise, the method simply returns.
-
+
+ If the server requires multi-step authentication (which is very rare),
+ this method will return a list of auth types permissible for the next
+ step. Otherwise, in the normal case, an empty list is returned.
+
@param username: the username to authenticate as.
@type username: string
@param password: the password to authenticate with.
@@ -173,6 +184,9 @@ class Transport (BaseTransport):
@param event: an event to trigger when the authentication attempt is
complete (whether it was successful or not)
@type event: threading.Event
+ @return: list of auth types permissible for the next stage of
+ authentication (normally empty).
+ @rtype: list
@raise BadAuthenticationType: if password authentication isn't
allowed by the server for this user (and no event was passed in).
@@ -197,8 +211,8 @@ class Transport (BaseTransport):
self.lock.release()
if event is not None:
# caller wants to wait for event themselves
- return
- self._wait_for_response(my_event)
+ return []
+ return self._wait_for_response(my_event)
### internals...
@@ -254,8 +268,13 @@ class Transport (BaseTransport):
e = self.get_exception()
if e is None:
e = SSHException('Authentication failed.')
+ # this is horrible. python Exception isn't yet descended from
+ # object, so type(e) won't work. :(
+ if issubclass(e.__class__, PartialAuthentication):
+ return e.allowed_types
raise e
-
+ return []
+
def _parse_service_request(self, m):
service = m.get_string()
if self.server_mode and (service == 'ssh-userauth'):
@@ -396,12 +415,13 @@ class Transport (BaseTransport):
partial = m.get_boolean()
if partial:
self._log(INFO, 'Authentication continues...')
- self._log(DEBUG, 'Methods: ' + str(partial))
- # FIXME: multi-part auth not supported
- pass
- if self.auth_method not in authlist:
+ self._log(DEBUG, 'Methods: ' + str(authlist))
+ self.saved_exception = PartialAuthentication(authlist)
+ elif self.auth_method not in authlist:
+ self._log(INFO, 'Authentication type not permitted.')
self.saved_exception = BadAuthenticationType('Bad authentication type', authlist)
- self._log(INFO, 'Authentication failed.')
+ else:
+ self._log(INFO, 'Authentication failed.')
self.authenticated = False
self.username = None
if self.auth_event != None:
diff --git a/paramiko/ssh_exception.py b/paramiko/ssh_exception.py
index 1f9173e1..bd6a3caa 100644
--- a/paramiko/ssh_exception.py
+++ b/paramiko/ssh_exception.py
@@ -53,3 +53,13 @@ class BadAuthenticationType (SSHException):
def __init__(self, explanation, types):
SSHException.__init__(self, explanation)
self.allowed_types = types
+
+class PartialAuthentication (SSHException):
+ """
+ An internal exception thrown in the case of partial authentication.
+ """
+ allowed_types = []
+
+ def __init__(self, types):
+ SSHException.__init__(self, 'partial authentication')
+ self.allowed_types = types