diff options
Diffstat (limited to 'paramiko/transport.py')
-rw-r--r-- | paramiko/transport.py | 70 |
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 |