summaryrefslogtreecommitdiffhomepage
path: root/paramiko/transport.py
diff options
context:
space:
mode:
Diffstat (limited to 'paramiko/transport.py')
-rw-r--r--paramiko/transport.py70
1 files changed, 58 insertions, 12 deletions
diff --git a/paramiko/transport.py b/paramiko/transport.py
index 603570d9..0a3a6859 100644
--- a/paramiko/transport.py
+++ b/paramiko/transport.py
@@ -209,11 +209,17 @@ class Transport(threading.Thread, ClosingContextManager):
_key_info = {
'ssh-rsa': RSAKey,
+ 'ssh-rsa-cert-v01@openssh.com': RSAKey,
'ssh-dss': DSSKey,
+ 'ssh-dss-cert-v01@openssh.com': DSSKey,
'ecdsa-sha2-nistp256': ECDSAKey,
+ 'ecdsa-sha2-nistp256-cert-v01@openssh.com': ECDSAKey,
'ecdsa-sha2-nistp384': ECDSAKey,
+ 'ecdsa-sha2-nistp384-cert-v01@openssh.com': ECDSAKey,
'ecdsa-sha2-nistp521': ECDSAKey,
+ 'ecdsa-sha2-nistp521-cert-v01@openssh.com': ECDSAKey,
'ssh-ed25519': Ed25519Key,
+ 'ssh-ed25519-cert-v01@openssh.com': Ed25519Key,
}
_kex_info = {
@@ -292,10 +298,12 @@ class Transport(threading.Thread, ClosingContextManager):
arguments.
"""
self.active = False
+ self.hostname = None
if isinstance(sock, string_types):
# convert "host:port" into (host, port)
hl = sock.split(':', 1)
+ self.hostname = hl[0]
if len(hl) == 1:
sock = (hl[0], 22)
else:
@@ -303,6 +311,7 @@ class Transport(threading.Thread, ClosingContextManager):
if type(sock) is tuple:
# connect to the given (host, port)
hostname, port = sock
+ self.hostname = hostname
reason = 'No suitable address family'
addrinfos = socket.getaddrinfo(
hostname, port, socket.AF_UNSPEC, socket.SOCK_STREAM
@@ -446,15 +455,39 @@ class Transport(threading.Thread, ClosingContextManager):
"""
return SecurityOptions(self)
- def set_gss_host(self, gss_host):
+ def set_gss_host(self, gss_host, trust_dns=True, gssapi_requested=True):
"""
- Setter for C{gss_host} if GSS-API Key Exchange is performed.
+ Normalize/canonicalize ``self.gss_host`` depending on various factors.
- :param str gss_host: The targets name in the kerberos database
- Default: The name of the host to connect to
- """
- # We need the FQDN to get this working with SSPI
- self.gss_host = socket.getfqdn(gss_host)
+ :param str gss_host:
+ The explicitly requested GSS-oriented hostname to connect to (i.e.
+ what the host's name is in the Kerberos database.) Defaults to
+ ``self.hostname`` (which will be the 'real' target hostname and/or
+ host portion of given socket object.)
+ :param bool trust_dns:
+ Indicates whether or not DNS is trusted; if true, DNS will be used
+ to canonicalize the GSS hostname (which again will either be
+ ``gss_host`` or the transport's default hostname.)
+ (Defaults to True due to backwards compatibility.)
+ :param bool gssapi_requested:
+ Whether GSSAPI key exchange or authentication was even requested.
+ If not, this is a no-op and nothing happens
+ (and ``self.gss_host`` is not set.)
+ (Defaults to True due to backwards compatibility.)
+ :returns: ``None``.
+ """
+ # No GSSAPI in play == nothing to do
+ if not gssapi_requested:
+ return
+ # Obtain the correct host first - did user request a GSS-specific name
+ # to use that is distinct from the actual SSH target hostname?
+ if gss_host is None:
+ gss_host = self.hostname
+ # Finally, canonicalize via DNS if DNS is trusted.
+ if trust_dns and gss_host is not None:
+ gss_host = socket.getfqdn(gss_host)
+ # And set attribute for reference later.
+ self.gss_host = gss_host
def start_client(self, event=None, timeout=None):
"""
@@ -1075,6 +1108,7 @@ class Transport(threading.Thread, ClosingContextManager):
gss_auth=False,
gss_kex=False,
gss_deleg_creds=True,
+ gss_trust_dns=True,
):
"""
Negotiate an SSH2 session, and optionally verify the server's host key
@@ -1113,13 +1147,26 @@ class Transport(threading.Thread, ClosingContextManager):
Perform GSS-API Key Exchange and user authentication.
:param bool gss_deleg_creds:
Whether to delegate GSS-API client credentials.
+ :param gss_trust_dns:
+ Indicates whether or not the DNS is trusted to securely
+ canonicalize the name of the host being connected to (default
+ ``True``).
:raises: `.SSHException` -- if the SSH2 negotiation fails, the host key
supplied by the server is incorrect, or authentication fails.
+
+ .. versionchanged:: 2.3
+ Added the ``gss_trust_dns`` argument.
"""
if hostkey is not None:
self._preferred_keys = [hostkey.get_name()]
+ self.set_gss_host(
+ gss_host=gss_host,
+ trust_dns=gss_trust_dns,
+ gssapi_requested=gss_kex or gss_auth,
+ )
+
self.start_client()
# check host key if we were given one
@@ -1144,7 +1191,9 @@ class Transport(threading.Thread, ClosingContextManager):
if (pkey is not None) or (password is not None) or gss_auth or gss_kex:
if gss_auth:
self._log(DEBUG, 'Attempting GSS-API auth... (gssapi-with-mic)') # noqa
- self.auth_gssapi_with_mic(username, gss_host, gss_deleg_creds)
+ self.auth_gssapi_with_mic(
+ username, self.gss_host, gss_deleg_creds,
+ )
elif gss_kex:
self._log(DEBUG, 'Attempting GSS-API auth... (gssapi-keyex)')
self.auth_gssapi_keyex(username)
@@ -1862,8 +1911,6 @@ class Transport(threading.Thread, ClosingContextManager):
continue
elif ptype == MSG_DISCONNECT:
self._parse_disconnect(m)
- self.active = False
- self.packetizer.close()
break
elif ptype == MSG_DEBUG:
self._parse_debug(m)
@@ -1891,8 +1938,7 @@ class Transport(threading.Thread, ClosingContextManager):
self._log(DEBUG, 'Ignoring message for dead channel %d' % chanid) # noqa
else:
self._log(ERROR, 'Channel request for unknown channel %d' % chanid) # noqa
- self.active = False
- self.packetizer.close()
+ break
elif (
self.auth_handler is not None and
ptype in self.auth_handler._handler_table