brittspace.

This commit is contained in:
Robey Pointer 2009-11-01 21:33:13 -08:00
parent 71e872e23a
commit e0a9f91c14
3 changed files with 103 additions and 103 deletions

View File

@ -33,7 +33,7 @@ class HostKeyEntry:
"""
Representation of a line in an OpenSSH-style "known hosts" file.
"""
def __init__(self, hostnames=None, key=None):
self.valid = (hostnames is not None) and (key is not None)
self.hostnames = hostnames
@ -83,7 +83,7 @@ class HostKeyEntry:
return '%s %s %s\n' % (','.join(self.hostnames), self.key.get_name(),
self.key.get_base64())
return None
def __repr__(self):
return '<HostKeyEntry %r: %r>' % (self.hostnames, self.key)
@ -93,18 +93,18 @@ class HostKeys (UserDict.DictMixin):
Representation of an openssh-style "known hosts" file. Host keys can be
read from one or more files, and then individual hosts can be looked up to
verify server keys during SSH negotiation.
A HostKeys object can be treated like a dict; any dict lookup is equivalent
to calling L{lookup}.
@since: 1.5.3
"""
def __init__(self, filename=None):
"""
Create a new HostKeys object, optionally loading keys from an openssh
style host-key file.
@param filename: filename to load host keys from, or C{None}
@type filename: str
"""
@ -112,12 +112,12 @@ class HostKeys (UserDict.DictMixin):
self._entries = []
if filename is not None:
self.load(filename)
def add(self, hostname, keytype, key):
"""
Add a host key entry to the table. Any existing entry for a
C{(hostname, keytype)} pair will be replaced.
@param hostname: the hostname (or IP) to add
@type hostname: str
@param keytype: key type (C{"ssh-rsa"} or C{"ssh-dss"})
@ -130,21 +130,21 @@ class HostKeys (UserDict.DictMixin):
e.key = key
return
self._entries.append(HostKeyEntry([hostname], key))
def load(self, filename):
"""
Read a file of known SSH host keys, in the format used by openssh.
This type of file unfortunately doesn't exist on Windows, but on
posix, it will usually be stored in
C{os.path.expanduser("~/.ssh/known_hosts")}.
If this method is called multiple times, the host keys are merged,
not cleared. So multiple calls to C{load} will just call L{add},
replacing any existing entries and adding new ones.
@param filename: name of the file to read host keys from
@type filename: str
@raise IOError: if there was an error reading the file
"""
f = open(filename, 'r')
@ -156,19 +156,19 @@ class HostKeys (UserDict.DictMixin):
if e is not None:
self._entries.append(e)
f.close()
def save(self, filename):
"""
Save host keys into a file, in the format used by openssh. The order of
keys in the file will be preserved when possible (if these keys were
loaded from a file originally). The single exception is that combined
lines will be split into individual key lines, which is arguably a bug.
@param filename: name of the file to write
@type filename: str
@raise IOError: if there was an error writing the file
@since: 1.6.1
"""
f = open(filename, 'w')
@ -183,7 +183,7 @@ class HostKeys (UserDict.DictMixin):
Find a hostkey entry for a given hostname or IP. If no entry is found,
C{None} is returned. Otherwise a dictionary of keytype to key is
returned. The keytype will be either C{"ssh-rsa"} or C{"ssh-dss"}.
@param hostname: the hostname (or IP) to lookup
@type hostname: str
@return: keys associated with this host (or C{None})
@ -194,13 +194,13 @@ class HostKeys (UserDict.DictMixin):
self._hostname = hostname
self._entries = entries
self._hostkeys = hostkeys
def __getitem__(self, key):
for e in self._entries:
if e.key.get_name() == key:
return e.key
raise KeyError(key)
def __setitem__(self, key, val):
for e in self._entries:
if e.key is None:
@ -214,7 +214,7 @@ class HostKeys (UserDict.DictMixin):
e = HostKeyEntry([hostname], val)
self._entries.append(e)
self._hostkeys._entries.append(e)
def keys(self):
return [e.key.get_name() for e in self._entries if e.key is not None]
@ -226,12 +226,12 @@ class HostKeys (UserDict.DictMixin):
if len(entries) == 0:
return None
return SubDict(hostname, entries, self)
def check(self, hostname, key):
"""
Return True if the given key is associated with the given hostname
in this dictionary.
@param hostname: hostname (or IP) of the SSH server
@type hostname: str
@param key: the key to check
@ -253,13 +253,13 @@ class HostKeys (UserDict.DictMixin):
Remove all host keys from the dictionary.
"""
self._entries = []
def __getitem__(self, key):
ret = self.lookup(key)
if ret is None:
raise KeyError(key)
return ret
def __setitem__(self, hostname, entry):
# don't use this please.
if len(entry) == 0:
@ -274,7 +274,7 @@ class HostKeys (UserDict.DictMixin):
found = True
if not found:
self._entries.append(HostKeyEntry([hostname], entry[key_type]))
def keys(self):
# python 2.4 sets would be nice here.
ret = []
@ -294,7 +294,7 @@ class HostKeys (UserDict.DictMixin):
"""
Return a "hashed" form of the hostname, as used by openssh when storing
hashed hostnames in the known_hosts file.
@param hostname: the hostname to hash
@type hostname: str
@param salt: optional salt to use when hashing (must be 20 bytes long)

View File

@ -43,7 +43,7 @@ def open_rng_device(device_path=None):
f = None
g = None
if device_path is None:
device_path = "/dev/urandom"
@ -54,7 +54,7 @@ def open_rng_device(device_path=None):
f = open(device_path, "rb", 0)
except EnvironmentError:
raise error("Unable to open /dev/urandom")
# Open a second file descriptor for sanity checking later.
try:
g = open(device_path, "rb", 0)
@ -65,17 +65,17 @@ def open_rng_device(device_path=None):
st = os.fstat(f.fileno()) # f
if stat.S_ISREG(st.st_mode) or not stat.S_ISCHR(st.st_mode):
raise error("/dev/urandom is not a character special device")
st = os.fstat(g.fileno()) # g
if stat.S_ISREG(st.st_mode) or not stat.S_ISCHR(st.st_mode):
raise error("/dev/urandom is not a character special device")
# Check that /dev/urandom always returns the number of bytes requested
x = f.read(20)
y = g.read(20)
if len(x) != 20 or len(y) != 20:
raise error("Error reading from /dev/urandom: input truncated")
# Check that different reads return different data
if x == y:
raise error("/dev/urandom is broken; returning identical data: %r == %r" % (x, y))

View File

@ -103,7 +103,7 @@ class SecurityOptions (object):
def _get_kex(self):
return self._transport._preferred_kex
def _get_compression(self):
return self._transport._preferred_compression
@ -129,7 +129,7 @@ class SecurityOptions (object):
def _set_kex(self, x):
self._set('_preferred_kex', '_kex_info', x)
def _set_compression(self, x):
self._set('_preferred_compression', '_compression_info', x)
@ -156,14 +156,14 @@ class ChannelMap (object):
self._map[chanid] = chan
finally:
self._lock.release()
def get(self, chanid):
self._lock.acquire()
try:
return self._map.get(chanid, None)
finally:
self._lock.release()
def delete(self, chanid):
self._lock.acquire()
try:
@ -173,14 +173,14 @@ class ChannelMap (object):
pass
finally:
self._lock.release()
def values(self):
self._lock.acquire()
try:
return self._map.values()
finally:
self._lock.release()
def __len__(self):
self._lock.acquire()
try:
@ -206,7 +206,7 @@ class Transport (threading.Thread):
_preferred_keys = ( 'ssh-rsa', 'ssh-dss' )
_preferred_kex = ( 'diffie-hellman-group1-sha1', 'diffie-hellman-group-exchange-sha1' )
_preferred_compression = ( 'none', )
_cipher_info = {
'aes128-ctr': { 'class': AES, 'mode': AES.MODE_CTR, 'block-size': 16, 'key-size': 16 },
'aes256-ctr': { 'class': AES, 'mode': AES.MODE_CTR, 'block-size': 16, 'key-size': 32 },
@ -234,7 +234,7 @@ class Transport (threading.Thread):
'diffie-hellman-group1-sha1': KexGroup1,
'diffie-hellman-group-exchange-sha1': KexGex,
}
_compression_info = {
# zlib@openssh.com is just zlib, but only turned on after a successful
# authentication. openssh servers may only offer this type because
@ -319,7 +319,7 @@ class Transport (threading.Thread):
self.session_id = None
self.host_key_type = None
self.host_key = None
# state used during negotiation
self.kex_engine = None
self.H = None
@ -383,7 +383,7 @@ class Transport (threading.Thread):
out += ' (connecting)'
out += '>'
return out
def atfork(self):
"""
Terminate this Transport without closing the session. On posix
@ -391,7 +391,7 @@ class Transport (threading.Thread):
and child will share the underlying socket, but only one process can
use the connection (without corrupting the session). Use this method
to clean up a Transport object without disrupting the other process.
@since: 1.5.3
"""
self.sock.close()
@ -414,11 +414,11 @@ class Transport (threading.Thread):
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.
If an event is passed in, 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}.
(Since 1.4) If C{event} is C{None}, this method will not return until
negotation is done. On success, the method returns normally.
Otherwise an SSHException is raised.
@ -428,7 +428,7 @@ class Transport (threading.Thread):
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.
@ -465,11 +465,11 @@ class Transport (threading.Thread):
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.
If an event is passed in, 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}.
(Since 1.4) If C{event} is C{None}, this method will not return until
negotation is done. On success, the method returns normally.
Otherwise an SSHException is raised.
@ -532,7 +532,7 @@ class Transport (threading.Thread):
we are. Because this is used for signing, the key must contain private
key info, not just the public half. Only one key of each type (RSA or
DSS) is kept.
@param key: the host key to add, usually an L{RSAKey <rsakey.RSAKey>} or
L{DSSKey <dsskey.DSSKey>}.
@type key: L{PKey <pkey.PKey>}
@ -582,7 +582,7 @@ class Transport (threading.Thread):
@return: True if a moduli file was successfully loaded; False
otherwise.
@rtype: bool
@note: This has no effect when used in client mode.
"""
Transport._modulus_pack = ModulusPack(randpool)
@ -623,7 +623,7 @@ class Transport (threading.Thread):
C{str(key)} for the key string.
@raise SSHException: if no session is currently active.
@return: public key of the remote server
@rtype: L{PKey <pkey.PKey>}
"""
@ -648,7 +648,7 @@ class Transport (threading.Thread):
@return: a new L{Channel}
@rtype: L{Channel}
@raise SSHException: if the request is rejected or the session ends
prematurely
"""
@ -664,25 +664,25 @@ class Transport (threading.Thread):
@type src_addr: (str, int)
@return: a new L{Channel}
@rtype: L{Channel}
@raise SSHException: if the request is rejected or the session ends
prematurely
"""
return self.open_channel('x11', src_addr=src_addr)
def open_forwarded_tcpip_channel(self, (src_addr, src_port), (dest_addr, dest_port)):
"""
Request a new channel back to the client, of type C{"forwarded-tcpip"}.
This is used after a client has requested port forwarding, for sending
incoming connections back to the client.
@param src_addr: originator's address
@param src_port: originator's port
@param dest_addr: local (server) connected address
@param dest_port: local (server) connected port
"""
return self.open_channel('forwarded-tcpip', (dest_addr, dest_port), (src_addr, src_port))
def open_channel(self, kind, dest_addr=None, src_addr=None):
"""
Request a new channel to the server. L{Channel}s are socket-like
@ -757,19 +757,19 @@ class Transport (threading.Thread):
"""
Ask the server to forward TCP connections from a listening port on
the server, across this SSH session.
If a handler is given, that handler is called from a different thread
whenever a forwarded connection arrives. The handler parameters are::
handler(channel, (origin_addr, origin_port), (server_addr, server_port))
where C{server_addr} and C{server_port} are the address and port that
the server was listening on.
If no handler is set, the default behavior is to send new incoming
forwarded connections into the accept queue, to be picked up via
L{accept}.
@param address: the address to bind when forwarding
@type address: str
@param port: the port to forward, or 0 to ask the server to allocate
@ -779,7 +779,7 @@ class Transport (threading.Thread):
@type handler: function(Channel, (str, int), (str, int))
@return: the port # allocated by the server
@rtype: int
@raise SSHException: if the server refused the TCP forward request
"""
if not self.active:
@ -803,7 +803,7 @@ class Transport (threading.Thread):
Ask the server to cancel a previous port-forwarding request. No more
connections to the given address & port will be forwarded across this
ssh connection.
@param address: the address to stop forwarding
@type address: str
@param port: the port to stop forwarding
@ -813,7 +813,7 @@ class Transport (threading.Thread):
return
self._tcp_handler = None
self.global_request('cancel-tcpip-forward', (address, port), wait=True)
def open_sftp_client(self):
"""
Create an SFTP client channel from an open transport. On success,
@ -876,7 +876,7 @@ class Transport (threading.Thread):
C{interval} seconds without sending any data over the connection, a
"keepalive" packet will be sent (and ignored by the remote host). This
can be useful to keep connections alive over a NAT, for example.
@param interval: seconds to wait before sending a keepalive packet (or
0 to disable keepalives).
@type interval: int
@ -927,7 +927,7 @@ class Transport (threading.Thread):
Return the next channel opened by the client over this transport, in
server mode. If no channel is opened before the given timeout, C{None}
is returned.
@param timeout: seconds to wait for a channel, or C{None} to wait
forever
@type timeout: int
@ -979,7 +979,7 @@ class Transport (threading.Thread):
@param pkey: a private key to use for authentication, if you want to
use private key authentication; otherwise C{None}.
@type pkey: L{PKey<pkey.PKey>}
@raise SSHException: if the SSH2 negotiation fails, the host key
supplied by the server is incorrect, or authentication fails.
"""
@ -1007,17 +1007,17 @@ class Transport (threading.Thread):
self.auth_publickey(username, pkey)
return
def get_exception(self):
"""
Return any exception that happened during the last server request.
This can be used to fetch more specific error information after using
calls like L{start_client}. The exception (if any) is cleared after
this call.
@return: an exception, or C{None} if there is no stored exception.
@rtype: Exception
@since: 1.1
"""
self.lock.acquire()
@ -1049,7 +1049,7 @@ class Transport (threading.Thread):
self.subsystem_table[name] = (handler, larg, kwarg)
finally:
self.lock.release()
def is_authenticated(self):
"""
Return true if this session is active and authenticated.
@ -1060,7 +1060,7 @@ class Transport (threading.Thread):
@rtype: bool
"""
return self.active and (self.auth_handler is not None) and self.auth_handler.is_authenticated()
def get_username(self):
"""
Return the username this connection is authenticated for. If the
@ -1080,7 +1080,7 @@ class Transport (threading.Thread):
This will almost always fail. It may be useful for determining the
list of authentication types supported by the server, by catching the
L{BadAuthenticationType} exception raised.
@param username: the username to authenticate as
@type username: string
@return: list of auth types permissible for the next stage of
@ -1091,7 +1091,7 @@ class Transport (threading.Thread):
by the server for this user
@raise SSHException: if the authentication failed due to a network
error
@since: 1.5
"""
if (not self.active) or (not self.initial_kex_done):
@ -1105,7 +1105,7 @@ class Transport (threading.Thread):
"""
Authenticate to the server using a password. The username and password
are sent over an encrypted link.
If an C{event} is passed in, this method will return immediately, and
the event will be triggered once authentication succeeds or fails. On
success, L{is_authenticated} will return C{True}. On failure, you may
@ -1114,7 +1114,7 @@ class Transport (threading.Thread):
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.
Since 1.5, if no event is passed and C{fallback} is C{True} (the
default), if the server doesn't support plain password authentication
but does support so-called "keyboard-interactive" mode, an attempt
@ -1123,11 +1123,11 @@ class Transport (threading.Thread):
made. This is useful for some recent Gentoo and Debian distributions,
which turn off plain password authentication in a misguided belief
that interactive authentication is "more secure". (It's not.)
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: str
@param password: the password to authenticate with
@ -1142,7 +1142,7 @@ class Transport (threading.Thread):
@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)
@raise AuthenticationException: if the authentication failed (and no
@ -1188,12 +1188,12 @@ class Transport (threading.Thread):
"""
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.
If an C{event} is passed in, this method will return immediately, and
the event will be triggered once authentication succeeds or fails. On
success, L{is_authenticated} will return C{True}. On failure, you may
use L{get_exception} to get more detailed error information.
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.
@ -1212,7 +1212,7 @@ class Transport (threading.Thread):
@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)
@raise AuthenticationException: if the authentication failed (and no
@ -1232,18 +1232,18 @@ class Transport (threading.Thread):
# caller wants to wait for event themselves
return []
return self.auth_handler.wait_for_response(my_event)
def auth_interactive(self, username, handler, submethods=''):
"""
Authenticate to the server interactively. A handler is used to answer
arbitrary questions from the server. On many servers, this is just a
dumb wrapper around PAM.
This method will block until the authentication succeeds or fails,
peroidically calling the handler asynchronously to get answers to
authentication questions. The handler may be called more than once
if the server continues to ask questions.
The handler is expected to be a callable that will handle calls of the
form: C{handler(title, instructions, prompt_list)}. The C{title} is
meant to be a dialog-window title, and the C{instructions} are user
@ -1251,13 +1251,13 @@ class Transport (threading.Thread):
prompts, each prompt being a tuple of C{(str, bool)}. The string is
the prompt and the boolean indicates whether the user text should be
echoed.
A sample call would thus be:
C{handler('title', 'instructions', [('Password:', False)])}.
The handler should return a list or tuple of answers to the server's
questions.
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.
@ -1271,12 +1271,12 @@ class Transport (threading.Thread):
@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
@raise AuthenticationException: if the authentication failed
@raise SSHException: if there was a network error
@since: 1.5
"""
if (not self.active) or (not self.initial_kex_done):
@ -1325,43 +1325,43 @@ class Transport (threading.Thread):
@type hexdump: bool
"""
self.packetizer.set_hexdump(hexdump)
def get_hexdump(self):
"""
Return C{True} if the transport is currently logging hex dumps of
protocol traffic.
@return: C{True} if hex dumps are being logged
@rtype: bool
@since: 1.4
"""
return self.packetizer.get_hexdump()
def use_compression(self, compress=True):
"""
Turn on/off compression. This will only have an affect before starting
the transport (ie before calling L{connect}, etc). By default,
compression is off since it negatively affects interactive sessions.
@param compress: C{True} to ask the remote client/server to compress
traffic; C{False} to refuse compression
@type compress: bool
@since: 1.5.2
"""
if compress:
self._preferred_compression = ( 'zlib@openssh.com', 'zlib', 'none' )
else:
self._preferred_compression = ( 'none', )
def getpeername(self):
"""
Return the address of the remote side of this Transport, if possible.
This is effectively a wrapper around C{'getpeername'} on the underlying
socket. If the socket-like object has no C{'getpeername'} method,
then C{("unknown", 0)} is returned.
@return: the address if the remote host, if known
@rtype: tuple(str, int)
"""
@ -1377,7 +1377,7 @@ class Transport (threading.Thread):
### internals...
def _log(self, level, msg, *args):
if issubclass(type(msg), list):
for m in msg:
@ -1498,13 +1498,13 @@ class Transport (threading.Thread):
self.server_accept_cv.notify()
finally:
self.lock.release()
def run(self):
# (use the exposed "run" method, because if we specify a thread target
# of a private method, threading.Thread will keep a reference to it
# indefinitely, creating a GC cycle and not letting Transport ever be
# GC'd. it's a bug in Thread.)
# active=True occurs before the thread is launched, to avoid a race
_active_threads.append(self)
if self.server_mode:
@ -1929,7 +1929,7 @@ class Transport (threading.Thread):
self.global_response = m
if self.completion_event is not None:
self.completion_event.set()
def _parse_request_failure(self, m):
self._log(DEBUG, 'Global request denied.')
self.global_response = None
@ -2018,7 +2018,7 @@ class Transport (threading.Thread):
origin_addr = m.get_string()
origin_port = m.get_int()
reason = self.server_object.check_channel_direct_tcpip_request(
my_chanid, (origin_addr, origin_port),
my_chanid, (origin_addr, origin_port),
(dest_addr, dest_port))
else:
reason = self.server_object.check_channel_request(kind, my_chanid)
@ -2034,7 +2034,7 @@ class Transport (threading.Thread):
msg.add_string('en')
self._send_message(msg)
return
chan = Channel(my_chanid)
self.lock.acquire()
try: