summaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
-rw-r--r--.travis.yml7
-rw-r--r--paramiko/agent.py4
-rw-r--r--paramiko/buffered_pipe.py10
-rw-r--r--paramiko/channel.py43
-rw-r--r--paramiko/client.py20
-rw-r--r--paramiko/dsskey.py2
-rw-r--r--paramiko/ecdsakey.py2
-rw-r--r--paramiko/file.py20
-rw-r--r--paramiko/hostkeys.py4
-rw-r--r--paramiko/kex_gss.py4
-rw-r--r--paramiko/packet.py12
-rw-r--r--paramiko/pkey.py42
-rw-r--r--paramiko/rsakey.py2
-rw-r--r--paramiko/server.py67
-rw-r--r--paramiko/sftp_client.py19
-rw-r--r--paramiko/sftp_file.py24
-rw-r--r--paramiko/sftp_handle.py10
-rw-r--r--paramiko/sftp_server.py2
-rw-r--r--paramiko/sftp_si.py20
-rw-r--r--paramiko/ssh_exception.py18
-rw-r--r--paramiko/ssh_gss.py43
-rw-r--r--paramiko/transport.py119
-rw-r--r--sites/www/changelog.rst49
-rw-r--r--tests/test_pkey.py28
-rw-r--r--tests/test_transport.py9
25 files changed, 318 insertions, 262 deletions
diff --git a/.travis.yml b/.travis.yml
index 4cacb017..c8faf0a2 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -20,11 +20,8 @@ install:
script:
# Main tests, w/ coverage!
- inv test --coverage
- # Ensure documentation & invoke pipeline run OK.
- # Run 'docs' first since its objects.inv is referred to by 'www'.
- # Also force warnings to be errors since most of them tend to be actual
- # problems.
- - invoke docs -o -W www -o -W
+ # Ensure documentation builds, both sites, maxxed nitpicking
+ - inv sites
# flake8 is now possible!
- flake8
notifications:
diff --git a/paramiko/agent.py b/paramiko/agent.py
index a7cab4d8..bc857efa 100644
--- a/paramiko/agent.py
+++ b/paramiko/agent.py
@@ -253,7 +253,7 @@ class AgentServerProxy(AgentSSH):
"""
:param .Transport t: Transport used for SSH Agent communication forwarding
- :raises SSHException: mostly if we lost the agent
+ :raises: `.SSHException` -- mostly if we lost the agent
"""
def __init__(self, t):
AgentSSH.__init__(self)
@@ -347,7 +347,7 @@ class Agent(AgentSSH):
opened, if one is running. If no agent is running, initialization will
succeed, but `get_keys` will return an empty tuple.
- :raises SSHException:
+ :raises: `.SSHException` --
if an SSH agent is found, but speaks an incompatible protocol
"""
def __init__(self):
diff --git a/paramiko/buffered_pipe.py b/paramiko/buffered_pipe.py
index 9a65cd95..d9f5149d 100644
--- a/paramiko/buffered_pipe.py
+++ b/paramiko/buffered_pipe.py
@@ -90,7 +90,7 @@ class BufferedPipe (object):
Feed new data into this pipe. This method is assumed to be called
from a separate thread, so synchronization is done.
- :param data: the data to add, as a `str` or `bytes`
+ :param data: the data to add, as a ``str`` or ``bytes``
"""
self._lock.acquire()
try:
@@ -134,11 +134,11 @@ class BufferedPipe (object):
:param int nbytes: maximum number of bytes to read
:param float timeout:
maximum seconds to wait (or ``None``, the default, to wait forever)
- :return: the read data, as a `bytes`
+ :return: the read data, as a ``str`` or ``bytes``
- :raises PipeTimeout:
- if a timeout was specified and no data was ready before that
- timeout
+ :raises:
+ `.PipeTimeout` -- if a timeout was specified and no data was ready
+ before that timeout
"""
out = bytes()
self._lock.acquire()
diff --git a/paramiko/channel.py b/paramiko/channel.py
index f2ecc4c0..1f603cf0 100644
--- a/paramiko/channel.py
+++ b/paramiko/channel.py
@@ -46,8 +46,9 @@ def open_only(func):
"""
Decorator for `.Channel` methods which performs an openness check.
- :raises SSHException:
- If the wrapped method is called on an unopened `.Channel`.
+ :raises:
+ `.SSHException` -- If the wrapped method is called on an unopened
+ `.Channel`.
"""
@wraps(func)
def _check(self, *args, **kwds):
@@ -165,8 +166,9 @@ class Channel (ClosingContextManager):
:param int width_pixels: width (in pixels) of the terminal screen
:param int height_pixels: height (in pixels) of the terminal screen
- :raises SSHException:
- if the request was rejected or the channel was closed
+ :raises:
+ `.SSHException` -- if the request was rejected or the channel was
+ closed
"""
m = Message()
m.add_byte(cMSG_CHANNEL_REQUEST)
@@ -197,7 +199,8 @@ class Channel (ClosingContextManager):
When the shell exits, the channel will be closed and can't be reused.
You must open a new channel if you wish to open another shell.
- :raises SSHException: if the request was rejected or the channel was
+ :raises:
+ `.SSHException` -- if the request was rejected or the channel was
closed
"""
m = Message()
@@ -222,7 +225,8 @@ class Channel (ClosingContextManager):
:param str command: a shell command to execute.
- :raises SSHException: if the request was rejected or the channel was
+ :raises:
+ `.SSHException` -- if the request was rejected or the channel was
closed
"""
m = Message()
@@ -247,8 +251,9 @@ class Channel (ClosingContextManager):
:param str subsystem: name of the subsystem being requested.
- :raises SSHException:
- if the request was rejected or the channel was closed
+ :raises:
+ `.SSHException` -- if the request was rejected or the channel was
+ closed
"""
m = Message()
m.add_byte(cMSG_CHANNEL_REQUEST)
@@ -271,8 +276,9 @@ class Channel (ClosingContextManager):
:param int width_pixels: new width (in pixels) of the terminal screen
:param int height_pixels: new height (in pixels) of the terminal screen
- :raises SSHException:
- if the request was rejected or the channel was closed
+ :raises:
+ `.SSHException` -- if the request was rejected or the channel was
+ closed
"""
m = Message()
m.add_byte(cMSG_CHANNEL_REQUEST)
@@ -313,11 +319,11 @@ class Channel (ClosingContextManager):
`.Transport` or session's ``window_size`` (e.g. that set by the
``default_window_size`` kwarg for `.Transport.__init__`) will cause
`.recv_exit_status` to hang indefinitely if it is called prior to a
- sufficiently large `~Channel..read` (or if there are no threads
- calling `~Channel.read` in the background).
+ sufficiently large `.Channel.recv` (or if there are no threads
+ calling `.Channel.recv` in the background).
In these cases, ensuring that `.recv_exit_status` is called *after*
- `~Channel.read` (or, again, using threads) can avoid the hang.
+ `.Channel.recv` (or, again, using threads) can avoid the hang.
:return: the exit code (as an `int`) of the process on the server.
@@ -391,8 +397,8 @@ class Channel (ClosingContextManager):
if True, only a single x11 connection will be forwarded (by
default, any number of x11 connections can arrive over this
session)
- :param function handler:
- an optional handler to use for incoming X11 connections
+ :param handler:
+ an optional callable handler to use for incoming X11 connections
:return: the auth_cookie used
"""
if auth_protocol is None:
@@ -421,8 +427,9 @@ class Channel (ClosingContextManager):
Request for a forward SSH Agent on this channel.
This is only valid for an ssh-agent from OpenSSH !!!
- :param function handler:
- a required handler to use for incoming SSH Agent connections
+ :param handler:
+ a required callable handler to use for incoming SSH Agent
+ connections
:return: True if we are ok, else False
(at that time we always return ok)
@@ -613,7 +620,7 @@ class Channel (ClosingContextManager):
length zero is returned, the channel stream has closed.
:param int nbytes: maximum number of bytes to read.
- :return: received data, as a `bytes`
+ :return: received data, as a ``str``/``bytes``.
:raises socket.timeout:
if no data is ready before the timeout set by `settimeout`.
diff --git a/paramiko/client.py b/paramiko/client.py
index d947e1bc..224109bf 100644
--- a/paramiko/client.py
+++ b/paramiko/client.py
@@ -90,7 +90,7 @@ class SSHClient (ClosingContextManager):
:param str filename: the filename to read, or ``None``
- :raises IOError:
+ :raises: ``IOError`` --
if a filename was provided and the file could not be read
"""
if filename is None:
@@ -117,7 +117,7 @@ class SSHClient (ClosingContextManager):
:param str filename: the filename to read
- :raises IOError: if the filename could not be read
+ :raises: ``IOError`` -- if the filename could not be read
"""
self._host_keys_filename = filename
self._host_keys.load(filename)
@@ -130,7 +130,7 @@ class SSHClient (ClosingContextManager):
:param str filename: the filename to save to
- :raises IOError: if the file could not be written
+ :raises: ``IOError`` -- if the file could not be written
"""
# update local host keys from file (in case other SSH clients
@@ -281,10 +281,12 @@ class SSHClient (ClosingContextManager):
:param float banner_timeout: an optional timeout (in seconds) to wait
for the SSH banner to be presented.
- :raises BadHostKeyException: if the server's host key could not be
+ :raises:
+ `.BadHostKeyException` -- if the server's host key could not be
verified
- :raises AuthenticationException: if authentication failed
- :raises SSHException: if there was any other error connecting or
+ :raises: `.AuthenticationException` -- if authentication failed
+ :raises:
+ `.SSHException` -- if there was any other error connecting or
establishing an SSH session
:raises socket.error: if a socket error occurred while connecting
@@ -413,12 +415,12 @@ class SSHClient (ClosingContextManager):
interpreted the same way as by the built-in ``file()`` function in
Python
:param int timeout:
- set command's channel timeout. See `Channel.settimeout`.settimeout
+ set command's channel timeout. See `.Channel.settimeout`
:return:
the stdin, stdout, and stderr of the executing command, as a
3-tuple
- :raises SSHException: if the server fails to execute the command
+ :raises: `.SSHException` -- if the server fails to execute the command
"""
chan = self._transport.open_session(timeout=timeout)
if get_pty:
@@ -445,7 +447,7 @@ class SSHClient (ClosingContextManager):
:param int height_pixels: the height (in pixels) of the terminal window
:return: a new `.Channel` connected to the remote shell
- :raises SSHException: if the server fails to invoke a shell
+ :raises: `.SSHException` -- if the server fails to invoke a shell
"""
chan = self._transport.open_session()
chan.get_pty(term, width, height, width_pixels, height_pixels)
diff --git a/paramiko/dsskey.py b/paramiko/dsskey.py
index ac6875bc..55ef1e9b 100644
--- a/paramiko/dsskey.py
+++ b/paramiko/dsskey.py
@@ -208,7 +208,7 @@ class DSSKey(PKey):
generate a new host key or authentication key.
:param int bits: number of bits the generated key should be.
- :param function progress_func: Unused
+ :param progress_func: Unused
:return: new `.DSSKey` private key
"""
numbers = dsa.generate_private_key(
diff --git a/paramiko/ecdsakey.py b/paramiko/ecdsakey.py
index 51f8d8ce..f5dacac8 100644
--- a/paramiko/ecdsakey.py
+++ b/paramiko/ecdsakey.py
@@ -231,7 +231,7 @@ class ECDSAKey(PKey):
Generate a new private ECDSA key. This factory function can be used to
generate a new host key or authentication key.
- :param function progress_func: Not used for this type of key.
+ :param progress_func: Not used for this type of key.
:returns: A new private key (`.ECDSAKey`) object
"""
if bits is not None:
diff --git a/paramiko/file.py b/paramiko/file.py
index e31ad9dd..5212091a 100644
--- a/paramiko/file.py
+++ b/paramiko/file.py
@@ -67,7 +67,7 @@ class BufferedFile (ClosingContextManager):
file. This iterator happens to return the file itself, since a file is
its own iterator.
- :raises ValueError: if the file is closed.
+ :raises: ``ValueError`` -- if the file is closed.
"""
if self._closed:
raise ValueError('I/O operation on closed file')
@@ -93,10 +93,10 @@ class BufferedFile (ClosingContextManager):
def next(self):
"""
Returns the next line from the input, or raises
- `~exceptions.StopIteration` when EOF is hit. Unlike Python file
+ ``StopIteration`` when EOF is hit. Unlike Python file
objects, it's okay to mix calls to `next` and `readline`.
- :raises StopIteration: when the end of the file is reached.
+ :raises: ``StopIteration`` -- when the end of the file is reached.
:returns: a line (`str`) read from the file.
"""
@@ -107,11 +107,11 @@ class BufferedFile (ClosingContextManager):
else:
def __next__(self):
"""
- Returns the next line from the input, or raises `.StopIteration`
+ Returns the next line from the input, or raises ``StopIteration``
when EOF is hit. Unlike python file objects, it's okay to mix
calls to `.next` and `.readline`.
- :raises StopIteration: when the end of the file is reached.
+ :raises: ``StopIteration`` -- when the end of the file is reached.
:returns: a line (`str`) read from the file.
"""
@@ -152,8 +152,8 @@ class BufferedFile (ClosingContextManager):
def readinto(self, buff):
"""
- Read up to ``len(buff)`` bytes into :class:`bytearray` *buff* and
- return the number of bytes read.
+ Read up to ``len(buff)`` bytes into ``bytearray`` *buff* and return the
+ number of bytes read.
:returns:
The number of bytes read.
@@ -368,7 +368,7 @@ class BufferedFile (ClosingContextManager):
type of movement: 0 = absolute; 1 = relative to the current
position; 2 = relative to the end of the file.
- :raises IOError: if the file doesn't support random access.
+ :raises: ``IOError`` -- if the file doesn't support random access.
"""
raise IOError('File does not support seeking.')
@@ -389,7 +389,7 @@ class BufferedFile (ClosingContextManager):
written yet. (Use `flush` or `close` to force buffered data to be
written out.)
- :param str/bytes data: data to write
+ :param data: ``str``/``bytes`` data to write
"""
data = b(data)
if self._closed:
@@ -423,7 +423,7 @@ class BufferedFile (ClosingContextManager):
name is intended to match `readlines`; `writelines` does not add line
separators.)
- :param iterable sequence: an iterable sequence of strings.
+ :param sequence: an iterable sequence of strings.
"""
for line in sequence:
self.write(line)
diff --git a/paramiko/hostkeys.py b/paramiko/hostkeys.py
index 18a0d333..008ba592 100644
--- a/paramiko/hostkeys.py
+++ b/paramiko/hostkeys.py
@@ -90,7 +90,7 @@ class HostKeys (MutableMapping):
:param str filename: name of the file to read host keys from
- :raises IOError: if there was an error reading the file
+ :raises: ``IOError`` -- if there was an error reading the file
"""
with open(filename, 'r') as f:
for lineno, line in enumerate(f, 1):
@@ -118,7 +118,7 @@ class HostKeys (MutableMapping):
:param str filename: name of the file to write
- :raises IOError: if there was an error writing the file
+ :raises: ``IOError`` -- if there was an error writing the file
.. versionadded:: 1.6.1
"""
diff --git a/paramiko/kex_gss.py b/paramiko/kex_gss.py
index 40ceb5cd..ba24c0a0 100644
--- a/paramiko/kex_gss.py
+++ b/paramiko/kex_gss.py
@@ -108,7 +108,7 @@ class KexGSSGroup1(object):
"""
Parse the next packet.
- :param char ptype: The type of the incoming packet
+ :param ptype: The (string) type of the incoming packet
:param `.Message` m: The paket content
"""
if self.transport.server_mode and (ptype == MSG_KEXGSS_INIT):
@@ -345,7 +345,7 @@ class KexGSSGex(object):
"""
Parse the next packet.
- :param char ptype: The type of the incoming packet
+ :param ptype: The (string) type of the incoming packet
:param `.Message` m: The paket content
"""
if ptype == MSG_KEXGSS_GROUPREQ:
diff --git a/paramiko/packet.py b/paramiko/packet.py
index 16288a0a..95a26c6e 100644
--- a/paramiko/packet.py
+++ b/paramiko/packet.py
@@ -43,6 +43,9 @@ def compute_hmac(key, message, digest_class):
class NeedRekeyException (Exception):
+ """
+ Exception indicating a rekey is needed.
+ """
pass
@@ -253,8 +256,9 @@ class Packetizer (object):
:param int n: number of bytes to read
:return: the data read, as a `str`
- :raises EOFError:
- if the socket was closed before all the bytes could be read
+ :raises:
+ ``EOFError`` -- if the socket was closed before all the bytes could
+ be read
"""
out = bytes()
# handle over-reading from reading the banner line
@@ -413,8 +417,8 @@ class Packetizer (object):
Only one thread should ever be in this function (no other locking is
done).
- :raises SSHException: if the packet is mangled
- :raises NeedRekeyException: if the transport should rekey
+ :raises: `.SSHException` -- if the packet is mangled
+ :raises: `.NeedRekeyException` -- if the transport should rekey
"""
header = self.read_all(self.__block_size_in, check_rekey=True)
if self.__block_engine_in is not None:
diff --git a/paramiko/pkey.py b/paramiko/pkey.py
index af9370fc..35a26fc7 100644
--- a/paramiko/pkey.py
+++ b/paramiko/pkey.py
@@ -48,6 +48,12 @@ class PKey(object):
'blocksize': 16,
'mode': modes.CBC
},
+ 'AES-256-CBC': {
+ 'cipher': algorithms.AES,
+ 'keysize': 32,
+ 'blocksize': 16,
+ 'mode': modes.CBC
+ },
'DES-EDE3-CBC': {
'cipher': algorithms.TripleDES,
'keysize': 24,
@@ -68,7 +74,7 @@ class PKey(object):
:param str data: an optional string containing a public key
of this type
- :raises SSHException:
+ :raises: `.SSHException` --
if a key cannot be created from the ``data`` or ``msg`` given, or
no key was passed in.
"""
@@ -95,7 +101,7 @@ class PKey(object):
of the key are compared, so a public key will compare equal to its
corresponding private key.
- :param .Pkey other: key to compare to.
+ :param .PKey other: key to compare to.
"""
hs = hash(self)
ho = hash(other)
@@ -191,10 +197,10 @@ class PKey(object):
encrypted
:return: a new `.PKey` based on the given private key
- :raises IOError: if there was an error reading the file
- :raises PasswordRequiredException: if the private key file is
+ :raises: ``IOError`` -- if there was an error reading the file
+ :raises: `.PasswordRequiredException` -- if the private key file is
encrypted, and ``password`` is ``None``
- :raises SSHException: if the key file is invalid
+ :raises: `.SSHException` -- if the key file is invalid
"""
key = cls(filename=filename, password=password)
return key
@@ -212,10 +218,10 @@ class PKey(object):
an optional password to use to decrypt the key, if it's encrypted
:return: a new `.PKey` based on the given private key
- :raises IOError: if there was an error reading the key
- :raises PasswordRequiredException:
+ :raises: ``IOError`` -- if there was an error reading the key
+ :raises: `.PasswordRequiredException` --
if the private key file is encrypted, and ``password`` is ``None``
- :raises SSHException: if the key file is invalid
+ :raises: `.SSHException` -- if the key file is invalid
"""
key = cls(file_obj=file_obj, password=password)
return key
@@ -229,8 +235,8 @@ class PKey(object):
:param str password:
an optional password to use to encrypt the key file
- :raises IOError: if there was an error writing the file
- :raises SSHException: if the key is invalid
+ :raises: ``IOError`` -- if there was an error writing the file
+ :raises: `.SSHException` -- if the key is invalid
"""
raise Exception('Not implemented in PKey')
@@ -242,8 +248,8 @@ class PKey(object):
:param file_obj: the file-like object to write into
:param str password: an optional password to use to encrypt the key
- :raises IOError: if there was an error writing to the file
- :raises SSHException: if the key is invalid
+ :raises: ``IOError`` -- if there was an error writing to the file
+ :raises: `.SSHException` -- if the key is invalid
"""
raise Exception('Not implemented in PKey')
@@ -263,10 +269,10 @@ class PKey(object):
encrypted.
:return: data blob (`str`) that makes up the private key.
- :raises IOError: if there was an error reading the file.
- :raises PasswordRequiredException: if the private key file is
+ :raises: ``IOError`` -- if there was an error reading the file.
+ :raises: `.PasswordRequiredException` -- if the private key file is
encrypted, and ``password`` is ``None``.
- :raises SSHException: if the key file is invalid.
+ :raises: `.SSHException` -- if the key file is invalid.
"""
with open(filename, 'r') as f:
data = self._read_private_key(tag, f, password)
@@ -340,17 +346,17 @@ class PKey(object):
:param str data: data blob that makes up the private key.
:param str password: an optional password to use to encrypt the file.
- :raises IOError: if there was an error writing the file.
+ :raises: ``IOError`` -- if there was an error writing the file.
"""
with open(filename, 'w') as f:
os.chmod(filename, o600)
- self._write_private_key(f, key, format)
+ self._write_private_key(f, key, format, password=password)
def _write_private_key(self, f, key, format, password=None):
if password is None:
encryption = serialization.NoEncryption()
else:
- encryption = serialization.BestEncryption(password)
+ encryption = serialization.BestAvailableEncryption(b(password))
f.write(key.private_bytes(
serialization.Encoding.PEM,
diff --git a/paramiko/rsakey.py b/paramiko/rsakey.py
index 8ccf4c30..f6d11a09 100644
--- a/paramiko/rsakey.py
+++ b/paramiko/rsakey.py
@@ -160,7 +160,7 @@ class RSAKey(PKey):
generate a new host key or authentication key.
:param int bits: number of bits the generated key should be.
- :param function progress_func: Unused
+ :param progress_func: Unused
:return: new `.RSAKey` private key
"""
key = rsa.generate_private_key(
diff --git a/paramiko/server.py b/paramiko/server.py
index b2a07916..adc606bf 100644
--- a/paramiko/server.py
+++ b/paramiko/server.py
@@ -106,15 +106,15 @@ class ServerInterface (object):
Determine if a client may open channels with no (further)
authentication.
- Return `.AUTH_FAILED` if the client must authenticate, or
- `.AUTH_SUCCESSFUL` if it's okay for the client to not
+ Return ``AUTH_FAILED`` if the client must authenticate, or
+ ``AUTH_SUCCESSFUL`` if it's okay for the client to not
authenticate.
- The default implementation always returns `.AUTH_FAILED`.
+ The default implementation always returns ``AUTH_FAILED``.
:param str username: the username of the client.
:return:
- `.AUTH_FAILED` if the authentication fails; `.AUTH_SUCCESSFUL` if
+ ``AUTH_FAILED`` if the authentication fails; ``AUTH_SUCCESSFUL`` if
it succeeds.
:rtype: int
"""
@@ -125,21 +125,21 @@ class ServerInterface (object):
Determine if a given username and password supplied by the client is
acceptable for use in authentication.
- Return `.AUTH_FAILED` if the password is not accepted,
- `.AUTH_SUCCESSFUL` if the password is accepted and completes
- the authentication, or `.AUTH_PARTIALLY_SUCCESSFUL` if your
+ Return ``AUTH_FAILED`` if the password is not accepted,
+ ``AUTH_SUCCESSFUL`` if the password is accepted and completes
+ the authentication, or ``AUTH_PARTIALLY_SUCCESSFUL`` if your
authentication is stateful, and this key is accepted for
authentication, but more authentication is required. (In this latter
case, `get_allowed_auths` will be called to report to the client what
options it has for continuing the authentication.)
- The default implementation always returns `.AUTH_FAILED`.
+ The default implementation always returns ``AUTH_FAILED``.
:param str username: the username of the authenticating client.
:param str password: the password given by the client.
:return:
- `.AUTH_FAILED` if the authentication fails; `.AUTH_SUCCESSFUL` if
- it succeeds; `.AUTH_PARTIALLY_SUCCESSFUL` if the password auth is
+ ``AUTH_FAILED`` if the authentication fails; ``AUTH_SUCCESSFUL`` if
+ it succeeds; ``AUTH_PARTIALLY_SUCCESSFUL`` if the password auth is
successful, but authentication must continue.
:rtype: int
"""
@@ -152,9 +152,9 @@ class ServerInterface (object):
check the username and key and decide if you would accept a signature
made using this key.
- Return `.AUTH_FAILED` if the key is not accepted,
- `.AUTH_SUCCESSFUL` if the key is accepted and completes the
- authentication, or `.AUTH_PARTIALLY_SUCCESSFUL` if your
+ Return ``AUTH_FAILED`` if the key is not accepted,
+ ``AUTH_SUCCESSFUL`` if the key is accepted and completes the
+ authentication, or ``AUTH_PARTIALLY_SUCCESSFUL`` if your
authentication is stateful, and this password is accepted for
authentication, but more authentication is required. (In this latter
case, `get_allowed_auths` will be called to report to the client what
@@ -164,13 +164,13 @@ class ServerInterface (object):
If you're willing to accept the key, Paramiko will do the work of
verifying the client's signature.
- The default implementation always returns `.AUTH_FAILED`.
+ The default implementation always returns ``AUTH_FAILED``.
:param str username: the username of the authenticating client
:param .PKey key: the key object provided by the client
:return:
- `.AUTH_FAILED` if the client can't authenticate with this key;
- `.AUTH_SUCCESSFUL` if it can; `.AUTH_PARTIALLY_SUCCESSFUL` if it
+ ``AUTH_FAILED`` if the client can't authenticate with this key;
+ ``AUTH_SUCCESSFUL`` if it can; ``AUTH_PARTIALLY_SUCCESSFUL`` if it
can authenticate with this key but must continue with
authentication
:rtype: int
@@ -184,19 +184,19 @@ class ServerInterface (object):
``"keyboard-interactive"`` auth type, which requires you to send a
series of questions for the client to answer.
- Return `.AUTH_FAILED` if this auth method isn't supported. Otherwise,
+ Return ``AUTH_FAILED`` if this auth method isn't supported. Otherwise,
you should return an `.InteractiveQuery` object containing the prompts
and instructions for the user. The response will be sent via a call
to `check_auth_interactive_response`.
- The default implementation always returns `.AUTH_FAILED`.
+ The default implementation always returns ``AUTH_FAILED``.
:param str username: the username of the authenticating client
:param str submethods:
a comma-separated list of methods preferred by the client (usually
empty)
:return:
- `.AUTH_FAILED` if this auth method isn't supported; otherwise an
+ ``AUTH_FAILED`` if this auth method isn't supported; otherwise an
object containing queries for the user
:rtype: int or `.InteractiveQuery`
"""
@@ -208,9 +208,9 @@ class ServerInterface (object):
supported. You should override this method in server mode if you want
to support the ``"keyboard-interactive"`` auth type.
- Return `.AUTH_FAILED` if the responses are not accepted,
- `.AUTH_SUCCESSFUL` if the responses are accepted and complete
- the authentication, or `.AUTH_PARTIALLY_SUCCESSFUL` if your
+ Return ``AUTH_FAILED`` if the responses are not accepted,
+ ``AUTH_SUCCESSFUL`` if the responses are accepted and complete
+ the authentication, or ``AUTH_PARTIALLY_SUCCESSFUL`` if your
authentication is stateful, and this set of responses is accepted for
authentication, but more authentication is required. (In this latter
case, `get_allowed_auths` will be called to report to the client what
@@ -221,12 +221,12 @@ class ServerInterface (object):
client to respond with more answers, calling this method again. This
cycle can continue indefinitely.
- The default implementation always returns `.AUTH_FAILED`.
+ The default implementation always returns ``AUTH_FAILED``.
:param list responses: list of `str` responses from the client
:return:
- `.AUTH_FAILED` if the authentication fails; `.AUTH_SUCCESSFUL` if
- it succeeds; `.AUTH_PARTIALLY_SUCCESSFUL` if the interactive auth
+ ``AUTH_FAILED`` if the authentication fails; ``AUTH_SUCCESSFUL`` if
+ it succeeds; ``AUTH_PARTIALLY_SUCCESSFUL`` if the interactive auth
is successful, but authentication must continue; otherwise an
object containing queries for the user
:rtype: int or `.InteractiveQuery`
@@ -243,8 +243,8 @@ class ServerInterface (object):
:param str username: The username of the authenticating client
:param int gss_authenticated: The result of the krb5 authentication
:param str cc_filename: The krb5 client credentials cache filename
- :return: `.AUTH_FAILED` if the user is not authenticated otherwise
- `.AUTH_SUCCESSFUL`
+ :return: ``AUTH_FAILED`` if the user is not authenticated otherwise
+ ``AUTH_SUCCESSFUL``
:rtype: int
:note: Kerberos credential delegation is not supported.
:see: `.ssh_gss`
@@ -257,7 +257,7 @@ class ServerInterface (object):
your local kerberos library to make sure that the
krb5_principal has an account on the server and is allowed to
log in as a user.
- :see: `http://www.unix.com/man-page/all/3/krb5_kuserok/`
+ :see: http://www.unix.com/man-page/all/3/krb5_kuserok/
"""
if gss_authenticated == AUTH_SUCCESSFUL:
return AUTH_SUCCESSFUL
@@ -275,8 +275,8 @@ class ServerInterface (object):
:param str username: The username of the authenticating client
:param int gss_authenticated: The result of the krb5 authentication
:param str cc_filename: The krb5 client credentials cache filename
- :return: `.AUTH_FAILED` if the user is not authenticated otherwise
- `.AUTH_SUCCESSFUL`
+ :return: ``AUTH_FAILED`` if the user is not authenticated otherwise
+ ``AUTH_SUCCESSFUL``
:rtype: int
:note: Kerberos credential delegation is not supported.
:see: `.ssh_gss` `.kex_gss`
@@ -289,7 +289,7 @@ class ServerInterface (object):
your local kerberos library to make sure that the
krb5_principal has an account on the server and is allowed
to log in as a user.
- :see: `http://www.unix.com/man-page/all/3/krb5_kuserok/`
+ :see: http://www.unix.com/man-page/all/3/krb5_kuserok/
"""
if gss_authenticated == AUTH_SUCCESSFUL:
return AUTH_SUCCESSFUL
@@ -301,9 +301,8 @@ class ServerInterface (object):
authentication.
The default implementation always returns false.
- :return: True if GSSAPI authentication is enabled otherwise false
- :rtype: Boolean
- :see: : `.ssh_gss`
+ :returns bool: Whether GSSAPI authentication is enabled.
+ :see: `.ssh_gss`
"""
UseGSSAPI = False
return UseGSSAPI
diff --git a/paramiko/sftp_client.py b/paramiko/sftp_client.py
index ea6f88a9..12fccb2f 100644
--- a/paramiko/sftp_client.py
+++ b/paramiko/sftp_client.py
@@ -83,8 +83,8 @@ class SFTPClient(BaseSFTP, ClosingContextManager):
:param .Channel sock: an open `.Channel` using the ``"sftp"`` subsystem
- :raises SSHException: if there's an exception while negotiating
- sftp
+ :raises:
+ `.SSHException` -- if there's an exception while negotiating sftp
"""
BaseSFTP.__init__(self)
self.sock = sock
@@ -321,7 +321,7 @@ class SFTPClient(BaseSFTP, ClosingContextManager):
:param int bufsize: desired buffering (-1 = default buffer size)
:return: an `.SFTPFile` object representing the open file
- :raises IOError: if the file could not be opened.
+ :raises: ``IOError`` -- if the file could not be opened.
"""
filename = self._adjust_cwd(filename)
self._log(DEBUG, 'open(%r, %r)' % (filename, mode))
@@ -356,7 +356,7 @@ class SFTPClient(BaseSFTP, ClosingContextManager):
:param str path: path (absolute or relative) of the file to remove
- :raises IOError: if the path refers to a folder (directory)
+ :raises: ``IOError`` -- if the path refers to a folder (directory)
"""
path = self._adjust_cwd(path)
self._log(DEBUG, 'remove(%r)' % path)
@@ -371,7 +371,8 @@ class SFTPClient(BaseSFTP, ClosingContextManager):
:param str oldpath: existing name of the file or folder
:param str newpath: new name for the file or folder
- :raises IOError: if ``newpath`` is a folder, or something else goes
+ :raises:
+ ``IOError`` -- if ``newpath`` is a folder, or something else goes
wrong
"""
oldpath = self._adjust_cwd(oldpath)
@@ -522,8 +523,7 @@ class SFTPClient(BaseSFTP, ClosingContextManager):
method on Python file objects.
:param str path: path of the file to modify
- :param size: the new size of the file
- :type size: int or long
+ :param int size: the new size of the file
"""
path = self._adjust_cwd(path)
self._log(DEBUG, 'truncate(%r, %r)' % (path, size))
@@ -562,7 +562,7 @@ class SFTPClient(BaseSFTP, ClosingContextManager):
:param str path: path to be normalized
:return: normalized form of the given path (as a `str`)
- :raises IOError: if the path can't be resolved on the server
+ :raises: ``IOError`` -- if the path can't be resolved on the server
"""
path = self._adjust_cwd(path)
self._log(DEBUG, 'normalize(%r)' % path)
@@ -585,7 +585,8 @@ class SFTPClient(BaseSFTP, ClosingContextManager):
:param str path: new current working directory
- :raises IOError: if the requested path doesn't exist on the server
+ :raises:
+ ``IOError`` -- if the requested path doesn't exist on the server
.. versionadded:: 1.4
"""
diff --git a/paramiko/sftp_file.py b/paramiko/sftp_file.py
index 58653c79..337cdbeb 100644
--- a/paramiko/sftp_file.py
+++ b/paramiko/sftp_file.py
@@ -251,6 +251,11 @@ class SFTPFile (BufferedFile):
return True
def seek(self, offset, whence=0):
+ """
+ Set the file's current position.
+
+ See `file.seek` for details.
+ """
self.flush()
if whence == self.SEEK_SET:
self._realpos = self._pos = offset
@@ -267,8 +272,8 @@ class SFTPFile (BufferedFile):
exactly like `.SFTPClient.stat`, except that it operates on an
already-open file.
- :return: an `.SFTPAttributes` object containing attributes about this
- file.
+ :returns:
+ an `.SFTPAttributes` object containing attributes about this file.
"""
t, msg = self.sftp._request(CMD_FSTAT, self.handle)
if t != CMD_ATTRS:
@@ -332,7 +337,6 @@ class SFTPFile (BufferedFile):
Python file objects.
:param size: the new size of the file
- :type size: int or long
"""
self.sftp._log(
DEBUG,
@@ -370,21 +374,18 @@ class SFTPFile (BufferedFile):
:param offset:
offset into the file to begin hashing (0 means to start from the
beginning)
- :type offset: int or long
:param length:
number of bytes to hash (0 means continue to the end of the file)
- :type length: int or long
:param int block_size:
number of bytes to hash per result (must not be less than 256; 0
means to compute only one hash of the entire segment)
- :type block_size: int
:return:
`str` of bytes representing the hash of each block, concatenated
together
- :raises IOError: if the server doesn't support the "check-file"
- extension, or possibly doesn't support the hash algorithm
- requested
+ :raises:
+ ``IOError`` -- if the server doesn't support the "check-file"
+ extension, or possibly doesn't support the hash algorithm requested
.. note:: Many (most?) servers don't support this extension yet.
@@ -466,9 +467,8 @@ class SFTPFile (BufferedFile):
once.
:param chunks:
- a list of (offset, length) tuples indicating which sections of the
- file to read
- :type chunks: list(tuple(long, int))
+ a list of ``(offset, length)`` tuples indicating which sections of
+ the file to read
:return: a list of blocks read, in the same order as in ``chunks``
.. versionadded:: 1.5.4
diff --git a/paramiko/sftp_handle.py b/paramiko/sftp_handle.py
index 2d2e621c..ca473900 100644
--- a/paramiko/sftp_handle.py
+++ b/paramiko/sftp_handle.py
@@ -77,7 +77,7 @@ class SFTPHandle (ClosingContextManager):
to be 64 bits.
If the end of the file has been reached, this method may return an
- empty string to signify EOF, or it may also return `.SFTP_EOF`.
+ empty string to signify EOF, or it may also return ``SFTP_EOF``.
The default implementation checks for an attribute on ``self`` named
``readfile``, and if present, performs the read operation on the Python
@@ -85,7 +85,6 @@ class SFTPHandle (ClosingContextManager):
common case where you are wrapping a Python file object.)
:param offset: position in the file to start reading from.
- :type offset: int or long
:param int length: number of bytes to attempt to read.
:return: data read from the file, or an SFTP error code, as a `str`.
"""
@@ -120,9 +119,8 @@ class SFTPHandle (ClosingContextManager):
refer to the same file.
:param offset: position in the file to start reading from.
- :type offset: int or long
:param str data: data to write into the file.
- :return: an SFTP error code like `.SFTP_OK`.
+ :return: an SFTP error code like ``SFTP_OK``.
"""
writefile = getattr(self, 'writefile', None)
if writefile is None:
@@ -152,7 +150,7 @@ class SFTPHandle (ClosingContextManager):
:return:
an attributes object for the given file, or an SFTP error code
- (like `.SFTP_PERMISSION_DENIED`).
+ (like ``SFTP_PERMISSION_DENIED``).
:rtype: `.SFTPAttributes` or error code
"""
return SFTP_OP_UNSUPPORTED
@@ -164,7 +162,7 @@ class SFTPHandle (ClosingContextManager):
check for the presence of fields before using them.
:param .SFTPAttributes attr: the attributes to change on this file.
- :return: an `int` error code like `.SFTP_OK`.
+ :return: an `int` error code like ``SFTP_OK``.
"""
return SFTP_OP_UNSUPPORTED
diff --git a/paramiko/sftp_server.py b/paramiko/sftp_server.py
index f94c5e39..1cfe286b 100644
--- a/paramiko/sftp_server.py
+++ b/paramiko/sftp_server.py
@@ -72,7 +72,7 @@ class SFTPServer (BaseSFTP, SubsystemHandler):
:param str name: name of the requested subsystem.
:param .ServerInterface server:
the server object associated with this channel and subsystem
- :param class sftp_si:
+ :param sftp_si:
a subclass of `.SFTPServerInterface` to use for handling individual
requests.
"""
diff --git a/paramiko/sftp_si.py b/paramiko/sftp_si.py
index c335eaec..09e7025c 100644
--- a/paramiko/sftp_si.py
+++ b/paramiko/sftp_si.py
@@ -72,7 +72,7 @@ class SFTPServerInterface (object):
on that file. On success, a new object subclassed from `.SFTPHandle`
should be returned. This handle will be used for future operations
on the file (read, write, etc). On failure, an error code such as
- `.SFTP_PERMISSION_DENIED` should be returned.
+ ``SFTP_PERMISSION_DENIED`` should be returned.
``flags`` contains the requested mode for opening (read-only,
write-append, etc) as a bitset of flags from the ``os`` module:
@@ -120,7 +120,7 @@ class SFTPServerInterface (object):
`.SFTPAttributes.from_stat` will usually do what you want.
In case of an error, you should return one of the ``SFTP_*`` error
- codes, such as `.SFTP_PERMISSION_DENIED`.
+ codes, such as ``SFTP_PERMISSION_DENIED``.
:param str path: the requested path (relative or absolute) to be
listed.
@@ -150,7 +150,7 @@ class SFTPServerInterface (object):
for.
:return:
an `.SFTPAttributes` object for the given file, or an SFTP error
- code (like `.SFTP_PERMISSION_DENIED`).
+ code (like ``SFTP_PERMISSION_DENIED``).
"""
return SFTP_OP_UNSUPPORTED
@@ -168,7 +168,7 @@ class SFTPServerInterface (object):
:type path: str
:return:
an `.SFTPAttributes` object for the given file, or an SFTP error
- code (like `.SFTP_PERMISSION_DENIED`).
+ code (like ``SFTP_PERMISSION_DENIED``).
"""
return SFTP_OP_UNSUPPORTED
@@ -178,7 +178,7 @@ class SFTPServerInterface (object):
:param str path:
the requested path (relative or absolute) of the file to delete.
- :return: an SFTP error code `int` like `.SFTP_OK`.
+ :return: an SFTP error code `int` like ``SFTP_OK``.
"""
return SFTP_OP_UNSUPPORTED
@@ -197,7 +197,7 @@ class SFTPServerInterface (object):
:param str oldpath:
the requested path (relative or absolute) of the existing file.
:param str newpath: the requested new path of the file.
- :return: an SFTP error code `int` like `.SFTP_OK`.
+ :return: an SFTP error code `int` like ``SFTP_OK``.
"""
return SFTP_OP_UNSUPPORTED
@@ -214,7 +214,7 @@ class SFTPServerInterface (object):
:param str path:
requested path (relative or absolute) of the new folder.
:param .SFTPAttributes attr: requested attributes of the new folder.
- :return: an SFTP error code `int` like `.SFTP_OK`.
+ :return: an SFTP error code `int` like ``SFTP_OK``.
"""
return SFTP_OP_UNSUPPORTED
@@ -226,7 +226,7 @@ class SFTPServerInterface (object):
:param str path:
requested path (relative or absolute) of the folder to remove.
- :return: an SFTP error code `int` like `.SFTP_OK`.
+ :return: an SFTP error code `int` like ``SFTP_OK``.
"""
return SFTP_OP_UNSUPPORTED
@@ -241,7 +241,7 @@ class SFTPServerInterface (object):
:param attr:
requested attributes to change on the file (an `.SFTPAttributes`
object)
- :return: an error code `int` like `.SFTP_OK`.
+ :return: an error code `int` like ``SFTP_OK``.
"""
return SFTP_OP_UNSUPPORTED
@@ -277,7 +277,7 @@ class SFTPServerInterface (object):
:param str path: path (relative or absolute) of the symbolic link.
:return:
the target `str` path of the symbolic link, or an error code like
- `.SFTP_NO_SUCH_FILE`.
+ ``SFTP_NO_SUCH_FILE``.
"""
return SFTP_OP_UNSUPPORTED
diff --git a/paramiko/ssh_exception.py b/paramiko/ssh_exception.py
index 280a7f39..e9ab8d66 100644
--- a/paramiko/ssh_exception.py
+++ b/paramiko/ssh_exception.py
@@ -50,12 +50,10 @@ class BadAuthenticationType (AuthenticationException):
the server isn't allowing that type. (It may only allow public-key, for
example.)
- :ivar list allowed_types:
- list of allowed authentication types provided by the server (possible
- values are: ``"none"``, ``"password"``, and ``"publickey"``).
-
.. versionadded:: 1.1
"""
+ #: list of allowed authentication types provided by the server (possible
+ #: values are: ``"none"``, ``"password"``, and ``"publickey"``).
allowed_types = []
def __init__(self, explanation, types):
@@ -87,7 +85,7 @@ class ChannelException (SSHException):
"""
Exception raised when an attempt to open a new `.Channel` fails.
- :ivar int code: the error code returned by the server
+ :param int code: the error code returned by the server
.. versionadded:: 1.6
"""
@@ -102,9 +100,9 @@ class BadHostKeyException (SSHException):
"""
The host key given by the SSH server did not match what we were expecting.
- :ivar str hostname: the hostname of the SSH server
- :ivar PKey got_key: the host key presented by the server
- :ivar PKey expected_key: the host key expected
+ :param str hostname: the hostname of the SSH server
+ :param PKey got_key: the host key presented by the server
+ :param PKey expected_key: the host key expected
.. versionadded:: 1.6
"""
@@ -125,8 +123,8 @@ class ProxyCommandFailure (SSHException):
"""
The "ProxyCommand" found in the .ssh/config file returned an error.
- :ivar str command: The command line that is generating this exception.
- :ivar str error: The error captured from the proxy command output.
+ :param str command: The command line that is generating this exception.
+ :param str error: The error captured from the proxy command output.
"""
def __init__(self, command, error):
SSHException.__init__(self,
diff --git a/paramiko/ssh_gss.py b/paramiko/ssh_gss.py
index 9c88c6fc..414485f9 100644
--- a/paramiko/ssh_gss.py
+++ b/paramiko/ssh_gss.py
@@ -72,9 +72,8 @@ def GSSAuth(auth_method, gss_deleg_creds=True):
We delegate credentials by default.
:return: Either an `._SSH_GSSAPI` (Unix) object or an
`_SSH_SSPI` (Windows) object
- :rtype: Object
- :raise ImportError: If no GSS-API / SSPI module could be imported.
+ :raises: ``ImportError`` -- If no GSS-API / SSPI module could be imported.
:see: `RFC 4462 <http://www.ietf.org/rfc/rfc4462.txt>`_
:note: Check for the available API and return either an `._SSH_GSSAPI`
@@ -131,7 +130,6 @@ class _SSH_GSSAuth(object):
as the only service value.
:param str service: The desired SSH service
- :rtype: Void
"""
if service.find("ssh-"):
self._service = service
@@ -142,7 +140,6 @@ class _SSH_GSSAuth(object):
username is not set by C{ssh_init_sec_context}.
:param str username: The name of the user who attempts to login
- :rtype: Void
"""
self._username = username
@@ -155,7 +152,6 @@ class _SSH_GSSAuth(object):
:return: A byte sequence containing the number of supported
OIDs, the length of the OID and the actual OID encoded with
DER
- :rtype: Bytes
:note: In server mode we just return the OID length and the DER encoded
OID.
"""
@@ -172,7 +168,6 @@ class _SSH_GSSAuth(object):
:param str desired_mech: The desired GSS-API mechanism of the client
:return: ``True`` if the given OID is supported, otherwise C{False}
- :rtype: Boolean
"""
mech, __ = decoder.decode(desired_mech)
if mech.__str__() != self._krb5_mech:
@@ -187,7 +182,6 @@ class _SSH_GSSAuth(object):
:param int integer: The integer value to convert
:return: The byte sequence of an 32 bit integer
- :rtype: Bytes
"""
return struct.pack("!I", integer)
@@ -207,7 +201,6 @@ class _SSH_GSSAuth(object):
string service (ssh-connection),
string authentication-method
(gssapi-with-mic or gssapi-keyex)
- :rtype: Bytes
"""
mic = self._make_uint32(len(session_id))
mic += session_id
@@ -256,11 +249,11 @@ class _SSH_GSSAPI(_SSH_GSSAuth):
("pseudo negotiated" mechanism, because we
support just the krb5 mechanism :-))
:param str recv_token: The GSS-API token received from the Server
- :raise SSHException: Is raised if the desired mechanism of the client
- is not supported
+ :raises:
+ `.SSHException` -- Is raised if the desired mechanism of the client
+ is not supported
:return: A ``String`` if the GSS-API has returned a token or
``None`` if no token was returned
- :rtype: String or None
"""
self._username = username
self._gss_host = target
@@ -304,8 +297,6 @@ class _SSH_GSSAPI(_SSH_GSSAuth):
gssapi-keyex:
Returns the MIC token from GSS-API with the SSH session ID as
message.
- :rtype: String
- :see: `._ssh_build_mic`
"""
self._session_id = session_id
if not gss_kex:
@@ -329,7 +320,6 @@ class _SSH_GSSAPI(_SSH_GSSAuth):
if it's not the initial call.
:return: A ``String`` if the GSS-API has returned a token or ``None``
if no token was returned
- :rtype: String or None
"""
# hostname and username are not required for GSSAPI, but for SSPI
self._gss_host = hostname
@@ -348,7 +338,7 @@ class _SSH_GSSAPI(_SSH_GSSAuth):
:param str session_id: The SSH session ID
:param str username: The name of the user who attempts to login
:return: None if the MIC check was successful
- :raises gssapi.GSSException: if the MIC check failed
+ :raises: ``gssapi.GSSException`` -- if the MIC check failed
"""
self._session_id = session_id
self._username = username
@@ -371,7 +361,6 @@ class _SSH_GSSAPI(_SSH_GSSAuth):
Checks if credentials are delegated (server mode).
:return: ``True`` if credentials are delegated, otherwise ``False``
- :rtype: bool
"""
if self._gss_srv_ctxt.delegated_cred is not None:
return True
@@ -384,8 +373,9 @@ class _SSH_GSSAPI(_SSH_GSSAuth):
(server mode).
:param str client_token: The GSS-API token received form the client
- :raise NotImplementedError: Credential delegation is currently not
- supported in server mode
+ :raises:
+ ``NotImplementedError`` -- Credential delegation is currently not
+ supported in server mode
"""
raise NotImplementedError
@@ -427,11 +417,11 @@ class _SSH_SSPI(_SSH_GSSAuth):
("pseudo negotiated" mechanism, because we
support just the krb5 mechanism :-))
:param recv_token: The SSPI token received from the Server
- :raise SSHException: Is raised if the desired mechanism of the client
- is not supported
+ :raises:
+ `.SSHException` -- Is raised if the desired mechanism of the client
+ is not supported
:return: A ``String`` if the SSPI has returned a token or ``None`` if
no token was returned
- :rtype: String or None
"""
self._username = username
self._gss_host = target
@@ -476,8 +466,6 @@ class _SSH_SSPI(_SSH_GSSAuth):
gssapi-keyex:
Returns the MIC token from SSPI with the SSH session ID as
message.
- :rtype: String
- :see: `._ssh_build_mic`
"""
self._session_id = session_id
if not gss_kex:
@@ -501,7 +489,6 @@ class _SSH_SSPI(_SSH_GSSAuth):
if it's not the initial call.
:return: A ``String`` if the SSPI has returned a token or ``None`` if
no token was returned
- :rtype: String or None
"""
self._gss_host = hostname
self._username = username
@@ -522,7 +509,7 @@ class _SSH_SSPI(_SSH_GSSAuth):
:param str session_id: The SSH session ID
:param str username: The name of the user who attempts to login
:return: None if the MIC check was successful
- :raises sspi.error: if the MIC check failed
+ :raises: ``sspi.error`` -- if the MIC check failed
"""
self._session_id = session_id
self._username = username
@@ -548,7 +535,6 @@ class _SSH_SSPI(_SSH_GSSAuth):
Checks if credentials are delegated (server mode).
:return: ``True`` if credentials are delegated, otherwise ``False``
- :rtype: Boolean
"""
return (
self._gss_flags & sspicon.ISC_REQ_DELEGATE and
@@ -562,7 +548,8 @@ class _SSH_SSPI(_SSH_GSSAuth):
(server mode).
:param str client_token: The SSPI token received form the client
- :raise NotImplementedError: Credential delegation is currently not
- supported in server mode
+ :raises:
+ ``NotImplementedError`` -- Credential delegation is currently not
+ supported in server mode
"""
raise NotImplementedError
diff --git a/paramiko/transport.py b/paramiko/transport.py
index a5fcd047..d219550d 100644
--- a/paramiko/transport.py
+++ b/paramiko/transport.py
@@ -71,7 +71,6 @@ from paramiko.ssh_exception import (
from paramiko.util import retry_on_signal, ClosingContextManager, clamp_value
-
# for thread cleanup
_active_threads = []
@@ -79,11 +78,12 @@ def _join_lingering_threads():
for thr in _active_threads:
thr.stop_thread()
+
import atexit
atexit.register(_join_lingering_threads)
-class Transport (threading.Thread, ClosingContextManager):
+class Transport(threading.Thread, ClosingContextManager):
"""
An SSH Transport attaches to a stream (usually a socket), negotiates an
encrypted session, authenticates, and then creates stream tunnels, called
@@ -110,21 +110,22 @@ class Transport (threading.Thread, ClosingContextManager):
'aes192-cbc',
'aes256-cbc',
'3des-cbc',
- 'arcfour128',
- 'arcfour256',
)
_preferred_macs = (
'hmac-sha2-256',
'hmac-sha2-512',
+ 'hmac-sha1',
'hmac-md5',
'hmac-sha1-96',
'hmac-md5-96',
- 'hmac-sha1',
)
_preferred_keys = (
+ 'ecdsa-sha2-nistp256',
+ 'ecdsa-sha2-nistp384',
+ 'ecdsa-sha2-nistp521',
'ssh-rsa',
'ssh-dss',
- ) + tuple(ECDSAKey.supported_key_format_identifiers())
+ )
_preferred_kex = (
'diffie-hellman-group1-sha1',
'diffie-hellman-group14-sha1',
@@ -182,18 +183,6 @@ class Transport (threading.Thread, ClosingContextManager):
'block-size': 8,
'key-size': 24
},
- 'arcfour128': {
- 'class': algorithms.ARC4,
- 'mode': None,
- 'block-size': 8,
- 'key-size': 16
- },
- 'arcfour256': {
- 'class': algorithms.ARC4,
- 'mode': None,
- 'block-size': 8,
- 'key-size': 32
- },
}
@@ -210,6 +199,8 @@ class Transport (threading.Thread, ClosingContextManager):
'ssh-rsa': RSAKey,
'ssh-dss': DSSKey,
'ecdsa-sha2-nistp256': ECDSAKey,
+ 'ecdsa-sha2-nistp384': ECDSAKey,
+ 'ecdsa-sha2-nistp521': ECDSAKey,
}
_kex_info = {
@@ -453,7 +444,6 @@ class Transport (threading.Thread, ClosingContextManager):
:param str gss_host: The targets name in the kerberos database
Default: The name of the host to connect to
- :rtype: Void
"""
# We need the FQDN to get this working with SSPI
self.gss_host = socket.getfqdn(gss_host)
@@ -486,8 +476,9 @@ class Transport (threading.Thread, ClosingContextManager):
:param .threading.Event event:
an event to trigger when negotiation is complete (optional)
- :raises SSHException: if negotiation fails (and no ``event`` was passed
- in)
+ :raises:
+ `.SSHException` -- if negotiation fails (and no ``event`` was
+ passed in)
"""
self.active = True
if event is not None:
@@ -547,8 +538,9 @@ class Transport (threading.Thread, ClosingContextManager):
an object used to perform authentication and create `channels
<.Channel>`
- :raises SSHException: if negotiation fails (and no ``event`` was passed
- in)
+ :raises:
+ `.SSHException` -- if negotiation fails (and no ``event`` was
+ passed in)
"""
if server is None:
server = ServerInterface()
@@ -670,7 +662,7 @@ class Transport (threading.Thread, ClosingContextManager):
string)``. You can get the same effect by calling `.PKey.get_name`
for the key type, and ``str(key)`` for the key string.
- :raises SSHException: if no session is currently active.
+ :raises: `.SSHException` -- if no session is currently active.
:return: public key (`.PKey`) of the remote server
"""
@@ -710,7 +702,8 @@ class Transport (threading.Thread, ClosingContextManager):
:return: a new `.Channel`
- :raises SSHException: if the request is rejected or the session ends
+ :raises:
+ `.SSHException` -- if the request is rejected or the session ends
prematurely
.. versionchanged:: 1.13.4/1.14.3/1.15.3
@@ -733,7 +726,8 @@ class Transport (threading.Thread, ClosingContextManager):
x11 port, ie. 6010)
:return: a new `.Channel`
- :raises SSHException: if the request is rejected or the session ends
+ :raises:
+ `.SSHException` -- if the request is rejected or the session ends
prematurely
"""
return self.open_channel('x11', src_addr=src_addr)
@@ -747,7 +741,7 @@ class Transport (threading.Thread, ClosingContextManager):
:return: a new `.Channel`
- :raises SSHException:
+ :raises: `.SSHException` --
if the request is rejected or the session ends prematurely
"""
return self.open_channel('auth-agent@openssh.com')
@@ -799,7 +793,8 @@ class Transport (threading.Thread, ClosingContextManager):
:return: a new `.Channel` on success
- :raises SSHException: if the request is rejected, the session ends
+ :raises:
+ `.SSHException` -- if the request is rejected, the session ends
prematurely or there is a timeout openning a channel
.. versionchanged:: 1.15
@@ -886,7 +881,8 @@ class Transport (threading.Thread, ClosingContextManager):
:return: the port number (`int`) allocated by the server
- :raises SSHException: if the server refused the TCP forward request
+ :raises:
+ `.SSHException` -- if the server refused the TCP forward request
"""
if not self.active:
raise SSHException('SSH session not active')
@@ -960,8 +956,9 @@ class Transport (threading.Thread, ClosingContextManager):
traffic both ways as the two sides swap keys and do computations. This
method returns when the session has switched to new keys.
- :raises SSHException: if the key renegotiation failed (which causes the
- session to end)
+ :raises:
+ `.SSHException` -- if the key renegotiation failed (which causes
+ the session to end)
"""
self.completion_event = threading.Event()
self._send_kex_init()
@@ -1102,7 +1099,7 @@ class Transport (threading.Thread, ClosingContextManager):
:param bool gss_deleg_creds:
Whether to delegate GSS-API client credentials.
- :raises SSHException: if the SSH2 negotiation fails, the host key
+ :raises: `.SSHException` -- if the SSH2 negotiation fails, the host key
supplied by the server is incorrect, or authentication fails.
"""
if hostkey is not None:
@@ -1176,7 +1173,7 @@ class Transport (threading.Thread, ClosingContextManager):
passed to the `.SubsystemHandler` constructor later.
:param str name: name of the subsystem.
- :param class handler:
+ :param handler:
subclass of `.SubsystemHandler` that handles this subsystem.
"""
try:
@@ -1237,9 +1234,11 @@ class Transport (threading.Thread, ClosingContextManager):
`list` of auth types permissible for the next stage of
authentication (normally empty)
- :raises BadAuthenticationType: if "none" authentication isn't allowed
+ :raises:
+ `.BadAuthenticationType` -- if "none" authentication isn't allowed
by the server for this user
- :raises SSHException: if the authentication failed due to a network
+ :raises:
+ `.SSHException` -- if the authentication failed due to a network
error
.. versionadded:: 1.5
@@ -1290,11 +1289,13 @@ class Transport (threading.Thread, ClosingContextManager):
`list` of auth types permissible for the next stage of
authentication (normally empty)
- :raises BadAuthenticationType: if password authentication isn't
+ :raises:
+ `.BadAuthenticationType` -- if password authentication isn't
allowed by the server for this user (and no event was passed in)
- :raises AuthenticationException: if the authentication failed (and no
+ :raises:
+ `.AuthenticationException` -- if the authentication failed (and no
event was passed in)
- :raises SSHException: if there was a network error
+ :raises: `.SSHException` -- if there was a network error
"""
if (not self.active) or (not self.initial_kex_done):
# we should never try to send the password unless we're on a secure
@@ -1359,11 +1360,13 @@ class Transport (threading.Thread, ClosingContextManager):
`list` of auth types permissible for the next stage of
authentication (normally empty)
- :raises BadAuthenticationType: if public-key authentication isn't
+ :raises:
+ `.BadAuthenticationType` -- if public-key authentication isn't
allowed by the server for this user (and no event was passed in)
- :raises AuthenticationException: if the authentication failed (and no
+ :raises:
+ `.AuthenticationException` -- if the authentication failed (and no
event was passed in)
- :raises SSHException: if there was a network error
+ :raises: `.SSHException` -- if there was a network error
"""
if (not self.active) or (not self.initial_kex_done):
# we should never try to authenticate unless we're on a secure link
@@ -1415,10 +1418,10 @@ class Transport (threading.Thread, ClosingContextManager):
`list` of auth types permissible for the next stage of
authentication (normally empty).
- :raises BadAuthenticationType: if public-key authentication isn't
+ :raises: `.BadAuthenticationType` -- if public-key authentication isn't
allowed by the server for this user
- :raises AuthenticationException: if the authentication failed
- :raises SSHException: if there was a network error
+ :raises: `.AuthenticationException` -- if the authentication failed
+ :raises: `.SSHException` -- if there was a network error
.. versionadded:: 1.5
"""
@@ -1463,11 +1466,12 @@ class Transport (threading.Thread, ClosingContextManager):
:return: list of auth types permissible for the next stage of
authentication (normally empty)
:rtype: list
- :raise BadAuthenticationType: if gssapi-with-mic isn't
+ :raises: `.BadAuthenticationType` -- if gssapi-with-mic isn't
allowed by the server (and no event was passed in)
- :raise AuthenticationException: if the authentication failed (and no
+ :raises:
+ `.AuthenticationException` -- if the authentication failed (and no
event was passed in)
- :raise SSHException: if there was a network error
+ :raises: `.SSHException` -- if there was a network error
"""
if (not self.active) or (not self.initial_kex_done):
# we should never try to authenticate unless we're on a secure link
@@ -1487,12 +1491,12 @@ class Transport (threading.Thread, ClosingContextManager):
:returns:
a `list` of auth types permissible for the next stage of
authentication (normally empty)
- :raises BadAuthenticationType:
+ :raises: `.BadAuthenticationType` --
if GSS-API Key Exchange was not performed (and no event was passed
in)
- :raises AuthenticationException:
+ :raises: `.AuthenticationException` --
if the authentication failed (and no event was passed in)
- :raises SSHException: if there was a network error
+ :raises: `.SSHException` -- if there was a network error
"""
if (not self.active) or (not self.initial_kex_done):
# we should never try to authenticate unless we're on a secure link
@@ -1714,21 +1718,6 @@ class Transport (threading.Thread, ClosingContextManager):
def _get_cipher(self, name, key, iv, operation):
if name not in self._cipher_info:
raise SSHException('Unknown client cipher ' + name)
- if name in ('arcfour128', 'arcfour256'):
- # arcfour cipher
- cipher = Cipher(
- self._cipher_info[name]['class'](key),
- None,
- backend=default_backend()
- )
- if operation is self._ENCRYPT:
- engine = cipher.encryptor()
- else:
- engine = cipher.decryptor()
- # as per RFC 4345, the first 1536 bytes of keystream
- # generated by the cipher MUST be discarded
- engine.encrypt(" " * 1536)
- return engine
else:
cipher = Cipher(
self._cipher_info[name]['class'](key),
diff --git a/sites/www/changelog.rst b/sites/www/changelog.rst
index 553b98d1..dfa2fedc 100644
--- a/sites/www/changelog.rst
+++ b/sites/www/changelog.rst
@@ -6,11 +6,41 @@ Changelog
a connection problem or protocol error, even if ``Transport.close()``
is called. Thanks Kyle Agronick for the discovery and investigation,
and Pierce Lopez for assistance.
+* :bug:`741` (also :issue:`809`, :issue:`772`; all via :issue:`912`) Writing
+ encrypted/password-protected private key files was silently broken since 2.0
+ due to an incorrect API call; this has been fixed.
+
+ Includes a directly related fix, namely adding the ability to read
+ ``AES-256-CBC`` ciphered private keys (which is now what we tend to write out
+ as it is Cryptography's default private key cipher.)
+
+ Thanks to ``@virlos`` for the original report, Chris Harris and ``@ibuler``
+ for initial draft PRs, and ``@jhgorrell`` for the final patch.
+* :bug:`983` Move ``sha1`` above the now-arguably-broken ``md5`` in the list of
+ preferred MAC algorithms, as an incremental security improvement for users
+ whose target systems offer both. Credit: Pierce Lopez.
+* :bug:`667` The RC4/arcfour family of ciphers has been broken since version
+ 2.0; but since the algorithm is now known to be completely insecure, we are
+ opting to remove support outright instead of fixing it. Thanks to Alex Gaynor
+ for catch & patch.
+* :support:`- backported` A big formatting pass to clean up an enormous number
+ of invalid Sphinx reference links, discovered by switching to a modern,
+ rigorous nitpicking doc-building mode.
+* :bug:`900` (via :issue:`911`) Prefer newer ``ecdsa-sha2-nistp`` keys over RSA
+ and DSA keys during host key selection. This improves compatibility with
+ OpenSSH, both in terms of general behavior, and also re: ability to properly
+ leverage OpenSSH-modified ``known_hosts`` files. Credit: ``@kasdoe`` for
+ original report/PR and Pierce Lopez for the second draft.
+* :bug:`794` (via :issue:`981`) Prior support for ``ecdsa-sha2-nistp(384|521)``
+ algorithms didn't fully extend to covering host keys, preventing connection
+ to hosts which only offer these key types and no others. This is now fixed.
+ Thanks to ``@ncoult`` and ``@kasdoe`` for reports and Pierce Lopez for the
+ patch.
* :support:`974 backported` Overhaul the codebase to be PEP-8, etc, compliant
(i.e. passes the maintainer's preferred `flake8 <http://flake8.pycqa.org/>`_
configuration) and add a ``flake8`` step to the Travis config. Big thanks to
Dorian Pula!
-* :bug:`683` Make `util.log_to_file()` append instead of replace. Thanks
+* :bug:`683` Make ``util.log_to_file`` append instead of replace. Thanks
to ``@vlcinsky`` for the report.
* :release:`2.0.5 <2017-02-20>`
* :release:`1.18.2 <2017-02-20>`
@@ -86,7 +116,7 @@ Changelog
* :bug:`334 (1.17+)` Make the ``subprocess`` import in ``proxy.py`` lazy so
users on platforms without it (such as Google App Engine) can import Paramiko
successfully. (Relatedly, make it easier to tweak an active socket check
- timeout [in `Transport <paramko.transport.Transport>`] which was previously
+ timeout [in `Transport <paramiko.transport.Transport>`] which was previously
hardcoded.) Credit: Shinya Okano.
* :support:`854 backported (1.17+)` Fix incorrect docstring/param-list for
`Transport.auth_gssapi_keyex
@@ -141,10 +171,10 @@ Changelog
``proxycommand`` key in parsed config structures). Thanks to Pat Brisbin for
the catch.
* :bug:`676` (via :issue:`677`) Fix a backwards incompatibility issue that
- cropped up in `SFTPFile.prefetch <~paramiko.sftp_file.prefetch>` re: the
- erroneously non-optional ``file_size`` parameter. Should only affect users
- who manually call ``prefetch``. Thanks to ``@stevevanhooser`` for catch &
- patch.
+ cropped up in `SFTPFile.prefetch <paramiko.sftp_file.SFTPFile.prefetch>` re:
+ the erroneously non-optional ``file_size`` parameter. Should only affect
+ users who manually call ``prefetch``. Thanks to ``@stevevanhooser`` for catch
+ & patch.
* :feature:`394` Replace PyCrypto with the Python Cryptographic Authority
(PyCA) 'Cryptography' library suite. This improves security, installability,
and performance; adds PyPy support; and much more.
@@ -234,7 +264,7 @@ Changelog
* :release:`1.15.4 <2015-11-02>`
* :release:`1.14.3 <2015-11-02>`
* :release:`1.13.4 <2015-11-02>`
-* :bug:`366` Fix `~paramiko.sftp_attributes.SFTPAttributes` so its string
+* :bug:`366` Fix `~paramiko.sftp_attr.SFTPAttributes` so its string
representation doesn't raise exceptions on empty/initialized instances. Patch
by Ulrich Petri.
* :bug:`359` Use correct attribute name when trying to use Python 3's
@@ -345,8 +375,9 @@ Changelog
* :release:`1.15.1 <2014-09-22>`
* :bug:`399` SSH agent forwarding (potentially other functionality as
well) would hang due to incorrect values passed into the new window size
- arguments for `.Transport` (thanks to a botched merge). This has been
- corrected. Thanks to Dylan Thacker-Smith for the report & patch.
+ arguments for `~paramiko.transport.Transport` (thanks to a botched merge).
+ This has been corrected. Thanks to Dylan Thacker-Smith for the report &
+ patch.
* :feature:`167` Add `~paramiko.config.SSHConfig.get_hostnames` for easier
introspection of a loaded SSH config file or object. Courtesy of Søren
Løvborg.
diff --git a/tests/test_pkey.py b/tests/test_pkey.py
index 24d78c3e..394a2cf4 100644
--- a/tests/test_pkey.py
+++ b/tests/test_pkey.py
@@ -120,6 +120,18 @@ class KeyTest (unittest.TestCase):
def tearDown(self):
pass
+ def assert_keyfile_is_encrypted(self, keyfile):
+ """
+ A quick check that filename looks like an encrypted key.
+ """
+ with open(keyfile, "r") as fh:
+ self.assertEqual(
+ fh.readline()[:-1],
+ "-----BEGIN RSA PRIVATE KEY-----"
+ )
+ self.assertEqual(fh.readline()[:-1], "Proc-Type: 4,ENCRYPTED")
+ self.assertEqual(fh.readline()[0:10], "DEK-Info: ")
+
def test_1_generate_key_bytes(self):
key = util.generate_key_bytes(md5, x1234, 'happy birthday', 30)
exp = b'\x61\xE1\xF2\x72\xF4\xC1\xC4\x56\x15\x86\xBD\x32\x24\x98\xC0\xE9\x24\x67\x27\x80\xF4\x7B\xB3\x7D\xDA\x7D\x54\x01\x9E\x64'
@@ -426,6 +438,7 @@ class KeyTest (unittest.TestCase):
# When the bug under test exists, this will ValueError.
try:
key.write_private_key_file(newfile, password=newpassword)
+ self.assert_keyfile_is_encrypted(newfile)
# Verify the inner key data still matches (when no ValueError)
key2 = RSAKey(filename=newfile, password=newpassword)
self.assertEqual(key, key2)
@@ -436,3 +449,18 @@ class KeyTest (unittest.TestCase):
key = RSAKey.from_private_key_file(test_path('test_rsa.key'))
comparable = TEST_KEY_BYTESTR_2 if PY2 else TEST_KEY_BYTESTR_3
self.assertEqual(str(key), comparable)
+
+ def test_keyfile_is_actually_encrypted(self):
+ # Read an existing encrypted private key
+ file_ = test_path('test_rsa_password.key')
+ password = 'television'
+ newfile = file_ + '.new'
+ newpassword = 'radio'
+ key = RSAKey(filename=file_, password=password)
+ # Write out a newly re-encrypted copy with a new password.
+ # When the bug under test exists, this will ValueError.
+ try:
+ key.write_private_key_file(newfile, password=newpassword)
+ self.assert_keyfile_is_encrypted(newfile)
+ finally:
+ os.remove(newfile)
diff --git a/tests/test_transport.py b/tests/test_transport.py
index 2ebdf854..c426cef1 100644
--- a/tests/test_transport.py
+++ b/tests/test_transport.py
@@ -165,6 +165,15 @@ class TransportTest(unittest.TestCase):
except TypeError:
pass
+ def test_1b_security_options_reset(self):
+ o = self.tc.get_security_options()
+ # should not throw any exceptions
+ o.ciphers = o.ciphers
+ o.digests = o.digests
+ o.key_types = o.key_types
+ o.kex = o.kex
+ o.compression = o.compression
+
def test_2_compute_key(self):
self.tc.K = 123281095979686581523377256114209720774539068973101330872763622971399429481072519713536292772709507296759612401802191955568143056534122385270077606457721553469730659233569339356140085284052436697480759510519672848743794433460113118986816826624865291116513647975790797391795651716378444844877749505443714557929
self.tc.H = b'\x0C\x83\x07\xCD\xE6\x85\x6F\xF3\x0B\xA9\x36\x84\xEB\x0F\x04\xC2\x52\x0E\x9E\xD3'