new parent exception for all auth failures, and new specific exception for bad host key
This commit is contained in:
parent
02e8178510
commit
8843feb633
|
@ -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',
|
||||||
|
|
|
@ -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,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -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()
|
||||||
|
|
|
@ -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
|
||||||
|
|
||||||
|
|
|
@ -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
|
||||||
"""
|
"""
|
||||||
|
|
|
@ -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())
|
||||||
|
|
Loading…
Reference in New Issue