diff options
-rw-r--r-- | demos/demo_simple.py | 2 | ||||
-rw-r--r-- | paramiko/client.py | 20 | ||||
-rw-r--r-- | paramiko/transport.py | 25 |
3 files changed, 32 insertions, 15 deletions
diff --git a/demos/demo_simple.py b/demos/demo_simple.py index 7ae3d8c8..9def57f8 100644 --- a/demos/demo_simple.py +++ b/demos/demo_simple.py @@ -79,8 +79,6 @@ try: if not UseGSSAPI and not DoGSSAPIKeyExchange: client.connect(hostname, port, username, password) else: - # SSPI works only with the FQDN of the target host - hostname = socket.getfqdn(hostname) try: client.connect(hostname, port, username, gss_auth=UseGSSAPI, gss_kex=DoGSSAPIKeyExchange) diff --git a/paramiko/client.py b/paramiko/client.py index 34491230..a9677b05 100644 --- a/paramiko/client.py +++ b/paramiko/client.py @@ -228,6 +228,7 @@ class SSHClient (ClosingContextManager): gss_host=None, banner_timeout=None, auth_timeout=None, + gss_trust_dns=True ): """ Connect to an SSH server and authenticate to it. The server's host key @@ -277,6 +278,9 @@ class SSHClient (ClosingContextManager): :param bool gss_deleg_creds: Delegate GSS-API client credentials or not :param str gss_host: The targets name in the kerberos database. default: hostname + :param bool gss_trust_dns: Indicates whether or not the DNS is trusted to + securely canonicalize the name of the host being + connected to (default ``True``). :param float banner_timeout: an optional timeout (in seconds) to wait for the SSH banner to be presented. :param float auth_timeout: an optional timeout (in seconds) to wait for @@ -331,12 +335,11 @@ class SSHClient (ClosingContextManager): t = self._transport = Transport( sock, gss_kex=gss_kex, gss_deleg_creds=gss_deleg_creds) t.use_compression(compress=compress) - if gss_kex and gss_host is None: - t.set_gss_host(hostname) - elif gss_kex and gss_host is not None: - t.set_gss_host(gss_host) - else: - pass + if gss_host is None: + t.set_gss_host(hostname, gss_trust_dns) + elif gss_host is not None: + # Don't canonicalize gss_host + t.set_gss_host(gss_host, False) if self._log_channel is not None: t.set_log_channel(self._log_channel) if banner_timeout is not None: @@ -387,10 +390,9 @@ class SSHClient (ClosingContextManager): key_filenames = [key_filename] else: key_filenames = key_filename - if gss_host is None: - gss_host = hostname + self._auth(username, password, pkey, key_filenames, allow_agent, - look_for_keys, gss_auth, gss_kex, gss_deleg_creds, gss_host) + look_for_keys, gss_auth, gss_kex, gss_deleg_creds, t.gss_host) def close(self): """ diff --git a/paramiko/transport.py b/paramiko/transport.py index 55c0fa62..8131daa1 100644 --- a/paramiko/transport.py +++ b/paramiko/transport.py @@ -292,10 +292,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 +305,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 +449,20 @@ class Transport(threading.Thread, ClosingContextManager): """ return SecurityOptions(self) - def set_gss_host(self, gss_host): + def set_gss_host(self, gss_host, dns_lookup=True): """ Setter for C{gss_host} if GSS-API Key Exchange is performed. :param str gss_host: The targets name in the kerberos database Default: The name of the host to connect to + :param bool dns_lookup: Indicates whether or not ``gss_host`` should be + canonicalized (default ``True``). + :rtype: Void """ - # We need the FQDN to get this working with SSPI - self.gss_host = socket.getfqdn(gss_host) + if dns_lookup: + self.gss_host = socket.getfqdn(gss_host) + else: + self.gss_host = gss_host def start_client(self, event=None, timeout=None): """ @@ -1075,6 +1083,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,6 +1122,9 @@ 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. @@ -1120,6 +1132,11 @@ class Transport(threading.Thread, ClosingContextManager): if hostkey is not None: self._preferred_keys = [hostkey.get_name()] + if gss_host is not None: + self.set_gss_host(gss_host, False) + elif self.hostname is not None: + self.set_gss_host(self.hostname, gss_trust_dns) + self.start_client() # check host key if we were given one @@ -1144,7 +1161,7 @@ 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) |