new parent exception for all auth failures, and new specific exception for bad host key
This commit is contained in:
Robey Pointer 2006-05-09 09:45:49 -07:00
parent 02e8178510
commit 8843feb633
6 changed files with 70 additions and 29 deletions

View File

@ -60,8 +60,8 @@ if sys.version_info < (2, 2):
__author__ = "Robey Pointer <robey@lag.net>" __author__ = "Robey Pointer <robey@lag.net>"
__date__ = "11 Mar 2005" __date__ = "11 Mar 2005"
__version__ = "1.5.4 (tentacool)" __version__ = "1.6 (u?)"
__version_info__ = (1, 5, 4) __version_info__ = (1, 6, 0)
__license__ = "GNU Lesser General Public License (LGPL)" __license__ = "GNU Lesser General Public License (LGPL)"
@ -69,7 +69,9 @@ from transport import randpool, SecurityOptions, Transport
from client import SSHClient, MissingHostKeyPolicy, AutoAddPolicy, RejectPolicy from client import SSHClient, MissingHostKeyPolicy, AutoAddPolicy, RejectPolicy
from auth_handler import AuthHandler from auth_handler import AuthHandler
from channel import Channel, ChannelFile from channel import Channel, ChannelFile
from ssh_exception import SSHException, PasswordRequiredException, BadAuthenticationType, ChannelException from ssh_exception import SSHException, PasswordRequiredException, \
BadAuthenticationType, ChannelException, BadHostKeyException, \
AuthenticationException
from server import ServerInterface, SubsystemHandler, InteractiveQuery from server import ServerInterface, SubsystemHandler, InteractiveQuery
from rsakey import RSAKey from rsakey import RSAKey
from dsskey import DSSKey from dsskey import DSSKey
@ -96,7 +98,7 @@ for x in (Transport, SecurityOptions, Channel, SFTPServer, SSHException,
SFTPHandle, SFTPServerInterface, BufferedFile, Agent, AgentKey, SFTPHandle, SFTPServerInterface, BufferedFile, Agent, AgentKey,
PKey, BaseSFTP, SFTPFile, ServerInterface, HostKeys, SSHClient, PKey, BaseSFTP, SFTPFile, ServerInterface, HostKeys, SSHClient,
MissingHostKeyPolicy, AutoAddPolicy, RejectPolicy, ChannelException, MissingHostKeyPolicy, AutoAddPolicy, RejectPolicy, ChannelException,
SSHConfig): SSHConfig, BadHostKeyException, AuthenticationException):
x.__module__ = 'paramiko' x.__module__ = 'paramiko'
from common import AUTH_SUCCESSFUL, AUTH_PARTIALLY_SUCCESSFUL, AUTH_FAILED, \ from common import AUTH_SUCCESSFUL, AUTH_PARTIALLY_SUCCESSFUL, AUTH_FAILED, \
@ -119,9 +121,11 @@ __all__ = [ 'Transport',
'Agent', 'Agent',
'Message', 'Message',
'SSHException', 'SSHException',
'AuthenticationException',
'PasswordRequiredException', 'PasswordRequiredException',
'BadAuthenticationType', 'BadAuthenticationType',
'ChannelException', 'ChannelException',
'BadHostKeyException',
'SFTP', 'SFTP',
'SFTPFile', 'SFTPFile',
'SFTPHandle', 'SFTPHandle',

View File

@ -29,7 +29,8 @@ import encodings.utf_8
from paramiko.common import * from paramiko.common import *
from paramiko import util from paramiko import util
from paramiko.message import Message from paramiko.message import Message
from paramiko.ssh_exception import SSHException, BadAuthenticationType, PartialAuthentication from paramiko.ssh_exception import SSHException, AuthenticationException, \
BadAuthenticationType, PartialAuthentication
from paramiko.server import InteractiveQuery from paramiko.server import InteractiveQuery
@ -158,14 +159,14 @@ class AuthHandler (object):
if not self.transport.is_active(): if not self.transport.is_active():
e = self.transport.get_exception() e = self.transport.get_exception()
if e is None: if e is None:
e = SSHException('Authentication failed.') e = AuthenticationException('Authentication failed.')
raise e raise e
if event.isSet(): if event.isSet():
break break
if not self.is_authenticated(): if not self.is_authenticated():
e = self.transport.get_exception() e = self.transport.get_exception()
if e is None: if e is None:
e = SSHException('Authentication failed.') e = AuthenticationException('Authentication failed.')
# this is horrible. python Exception isn't yet descended from # this is horrible. python Exception isn't yet descended from
# object, so type(e) won't work. :( # object, so type(e) won't work. :(
if issubclass(e.__class__, PartialAuthentication): if issubclass(e.__class__, PartialAuthentication):
@ -410,4 +411,3 @@ class AuthHandler (object):
MSG_USERAUTH_INFO_RESPONSE: _parse_userauth_info_response, MSG_USERAUTH_INFO_RESPONSE: _parse_userauth_info_response,
} }

View File

@ -186,7 +186,7 @@ class SSHClient (object):
""" """
return self._host_keys return self._host_keys
def set_log_channel(self, channel): def set_log_channel(self, name):
""" """
Set the channel for logging. The default is C{"paramiko.transport"} Set the channel for logging. The default is C{"paramiko.transport"}
but it can be set to anything you want. but it can be set to anything you want.
@ -194,7 +194,7 @@ class SSHClient (object):
@param name: new channel name for logging @param name: new channel name for logging
@type name: str @type name: str
""" """
self._log_channel = channel self._log_channel = name
def set_missing_host_key_policy(self, policy): def set_missing_host_key_policy(self, policy):
""" """
@ -245,8 +245,11 @@ class SSHClient (object):
for authentication for authentication
@type key_filename: str @type key_filename: str
@raise SSHException: if there was an error authenticating or verifying @raise BadHostKeyException: if the server's host key could not be
the server's host key verified
@raise AuthenticationException: if authentication failed
@raise SSHException: if there was any other error connecting or
establishing an SSH session
""" """
t = self._transport = Transport((hostname, port)) t = self._transport = Transport((hostname, port))
if self._log_channel is not None: if self._log_channel is not None:
@ -254,7 +257,6 @@ class SSHClient (object):
t.start_client() t.start_client()
server_key = t.get_remote_server_key() server_key = t.get_remote_server_key()
server_key_hex = hexify(server_key.get_fingerprint())
keytype = server_key.get_name() keytype = server_key.get_name()
our_server_key = self._system_host_keys.get(hostname, {}).get(keytype, None) our_server_key = self._system_host_keys.get(hostname, {}).get(keytype, None)
@ -263,14 +265,11 @@ class SSHClient (object):
if our_server_key is None: if our_server_key is None:
# will raise exception if the key is rejected; let that fall out # will raise exception if the key is rejected; let that fall out
self._policy.missing_host_key(self, hostname, server_key) self._policy.missing_host_key(self, hostname, server_key)
# if this continues, assume the key is ok # if the callback returns, assume the key is ok
our_server_key = server_key our_server_key = server_key
our_server_key_hex = hexify(our_server_key.get_fingerprint())
if server_key != our_server_key: if server_key != our_server_key:
raise SSHException('Host key for server %s does not match! (%s != %s)' % raise BadHostKeyException(hostname, server_key, our_server_key)
(hostname, our_server_key_kex, server_key_hex))
if username is None: if username is None:
username = getpass.getuser() username = getpass.getuser()

View File

@ -28,14 +28,25 @@ class SSHException (Exception):
pass pass
class PasswordRequiredException (SSHException): class AuthenticationException (SSHException):
"""
Exception raised when authentication failed for some reason. It may be
possible to retry with different credentials. (Other classes specify more
specific reasons.)
@since: 1.6
"""
pass
class PasswordRequiredException (AuthenticationException):
""" """
Exception raised when a password is needed to unlock a private key file. Exception raised when a password is needed to unlock a private key file.
""" """
pass pass
class BadAuthenticationType (SSHException): class BadAuthenticationType (AuthenticationException):
""" """
Exception raised when an authentication type (like password) is used, but Exception raised when an authentication type (like password) is used, but
the server isn't allowing that type. (It may only allow public-key, for the server isn't allowing that type. (It may only allow public-key, for
@ -58,7 +69,7 @@ class BadAuthenticationType (SSHException):
return SSHException.__str__(self) + ' (allowed_types=%r)' % self.allowed_types return SSHException.__str__(self) + ' (allowed_types=%r)' % self.allowed_types
class PartialAuthentication (SSHException): class PartialAuthentication (AuthenticationException):
""" """
An internal exception thrown in the case of partial authentication. An internal exception thrown in the case of partial authentication.
""" """
@ -73,8 +84,32 @@ class ChannelException (SSHException):
""" """
Exception raised when an attempt to open a new L{Channel} fails. Exception raised when an attempt to open a new L{Channel} fails.
@ivar code: the error code returned by the server
@type code: int
@since: 1.6 @since: 1.6
""" """
def __init__(self, code, text): def __init__(self, code, text):
SSHException.__init__(self, text) SSHException.__init__(self, text)
self.code = code self.code = code
class BadHostKeyException (SSHException):
"""
The host key given by the SSH server did not match what we were expecting.
@ivar hostname: the hostname of the SSH server
@type hostname: str
@ivar key: the host key presented by the server
@type key: L{PKey}
@ivar expected_key: the host key expected
@type expected_key: L{PKey}
@since: 1.6
"""
def __init__(self, hostname, got_key, expected_key):
SSHException.__init__(self, 'Host key for server %s does not match!' % hostname)
self.hostname = hostname
self.key = got_key
self.expected_key = expected_key

View File

@ -992,8 +992,9 @@ class Transport (threading.Thread):
@raise BadAuthenticationType: if password authentication isn't @raise BadAuthenticationType: if password authentication isn't
allowed by the server for this user (and no event was passed in) allowed by the server for this user (and no event was passed in)
@raise SSHException: if the authentication failed (and no event was @raise AuthenticationException: if the authentication failed (and no
passed in) event was passed in)
@raise SSHException: if there was a network error
""" """
if (not self.active) or (not self.initial_kex_done): if (not self.active) or (not self.initial_kex_done):
# we should never try to send the password unless we're on a secure link # we should never try to send the password unless we're on a secure link
@ -1056,13 +1057,14 @@ class Transport (threading.Thread):
complete (whether it was successful or not) complete (whether it was successful or not)
@type event: threading.Event @type event: threading.Event
@return: list of auth types permissible for the next stage of @return: list of auth types permissible for the next stage of
authentication (normally empty). authentication (normally empty)
@rtype: list @rtype: list
@raise BadAuthenticationType: if public-key authentication isn't @raise BadAuthenticationType: if public-key authentication isn't
allowed by the server for this user (and no event was passed in). allowed by the server for this user (and no event was passed in)
@raise SSHException: if the authentication failed (and no event was @raise AuthenticationException: if the authentication failed (and no
passed in). event was passed in)
@raise SSHException: if there was a network error
""" """
if (not self.active) or (not self.initial_kex_done): if (not self.active) or (not self.initial_kex_done):
# we should never try to authenticate unless we're on a secure link # we should never try to authenticate unless we're on a secure link
@ -1119,7 +1121,8 @@ class Transport (threading.Thread):
@raise BadAuthenticationType: if public-key authentication isn't @raise BadAuthenticationType: if public-key authentication isn't
allowed by the server for this user allowed by the server for this user
@raise SSHException: if the authentication failed @raise AuthenticationException: if the authentication failed
@raise SSHException: if there was a network error
@since: 1.5 @since: 1.5
""" """

View File

@ -258,7 +258,7 @@ class TransportTest (unittest.TestCase):
self.assert_(False) self.assert_(False)
except: except:
etype, evalue, etb = sys.exc_info() etype, evalue, etb = sys.exc_info()
self.assertEquals(SSHException, etype) self.assert_(issubclass(etype, SSHException))
self.tc.auth_password(username='slowdive', password='pygmalion') self.tc.auth_password(username='slowdive', password='pygmalion')
event.wait(1.0) event.wait(1.0)
self.assert_(event.isSet()) self.assert_(event.isSet())