diff options
author | Jeff Forcier <jeff@bitprophet.org> | 2018-09-17 17:58:44 -0700 |
---|---|---|
committer | Jeff Forcier <jeff@bitprophet.org> | 2018-09-17 17:58:44 -0700 |
commit | 4cc767f5cd109a459571abbf21c68b783ce237c9 (patch) | |
tree | 68338fb6f9dc72cfaf8ee0ef5ef6cc572f613403 /paramiko/auth_handler.py | |
parent | ab823cc9836b8f3ec7cdd916f0dc53f0096e1c59 (diff) | |
parent | 068b0914e396e8a47e28245a0a69ea7bf6bea6ff (diff) | |
download | paramiko-4cc767f5cd109a459571abbf21c68b783ce237c9.tar.gz |
Merge branch '2.1' into 2.2
Foregoes a handful of unblackened bits to make the merge easier; will
blacken next anyways
Diffstat (limited to 'paramiko/auth_handler.py')
-rw-r--r-- | paramiko/auth_handler.py | 299 |
1 files changed, 174 insertions, 125 deletions
diff --git a/paramiko/auth_handler.py b/paramiko/auth_handler.py index ac6e23da..04ee2d75 100644 --- a/paramiko/auth_handler.py +++ b/paramiko/auth_handler.py @@ -24,30 +24,54 @@ import weakref import time from paramiko.common import ( - cMSG_SERVICE_REQUEST, cMSG_DISCONNECT, DISCONNECT_SERVICE_NOT_AVAILABLE, - DISCONNECT_NO_MORE_AUTH_METHODS_AVAILABLE, cMSG_USERAUTH_REQUEST, - cMSG_SERVICE_ACCEPT, DEBUG, AUTH_SUCCESSFUL, INFO, cMSG_USERAUTH_SUCCESS, - cMSG_USERAUTH_FAILURE, AUTH_PARTIALLY_SUCCESSFUL, - cMSG_USERAUTH_INFO_REQUEST, WARNING, AUTH_FAILED, cMSG_USERAUTH_PK_OK, - cMSG_USERAUTH_INFO_RESPONSE, MSG_SERVICE_REQUEST, MSG_SERVICE_ACCEPT, - MSG_USERAUTH_REQUEST, MSG_USERAUTH_SUCCESS, MSG_USERAUTH_FAILURE, - MSG_USERAUTH_BANNER, MSG_USERAUTH_INFO_REQUEST, MSG_USERAUTH_INFO_RESPONSE, - cMSG_USERAUTH_GSSAPI_RESPONSE, cMSG_USERAUTH_GSSAPI_TOKEN, - cMSG_USERAUTH_GSSAPI_MIC, MSG_USERAUTH_GSSAPI_RESPONSE, - MSG_USERAUTH_GSSAPI_TOKEN, MSG_USERAUTH_GSSAPI_ERROR, - MSG_USERAUTH_GSSAPI_ERRTOK, MSG_USERAUTH_GSSAPI_MIC, MSG_NAMES, + cMSG_SERVICE_REQUEST, + cMSG_DISCONNECT, + DISCONNECT_SERVICE_NOT_AVAILABLE, + DISCONNECT_NO_MORE_AUTH_METHODS_AVAILABLE, + cMSG_USERAUTH_REQUEST, + cMSG_SERVICE_ACCEPT, + DEBUG, + AUTH_SUCCESSFUL, + INFO, + cMSG_USERAUTH_SUCCESS, + cMSG_USERAUTH_FAILURE, + AUTH_PARTIALLY_SUCCESSFUL, + cMSG_USERAUTH_INFO_REQUEST, + WARNING, + AUTH_FAILED, + cMSG_USERAUTH_PK_OK, + cMSG_USERAUTH_INFO_RESPONSE, + MSG_SERVICE_REQUEST, + MSG_SERVICE_ACCEPT, + MSG_USERAUTH_REQUEST, + MSG_USERAUTH_SUCCESS, + MSG_USERAUTH_FAILURE, + MSG_USERAUTH_BANNER, + MSG_USERAUTH_INFO_REQUEST, + MSG_USERAUTH_INFO_RESPONSE, + cMSG_USERAUTH_GSSAPI_RESPONSE, + cMSG_USERAUTH_GSSAPI_TOKEN, + cMSG_USERAUTH_GSSAPI_MIC, + MSG_USERAUTH_GSSAPI_RESPONSE, + MSG_USERAUTH_GSSAPI_TOKEN, + MSG_USERAUTH_GSSAPI_ERROR, + MSG_USERAUTH_GSSAPI_ERRTOK, + MSG_USERAUTH_GSSAPI_MIC, + MSG_NAMES, ) from paramiko.message import Message from paramiko.py3compat import bytestring from paramiko.ssh_exception import ( - SSHException, AuthenticationException, BadAuthenticationType, + SSHException, + AuthenticationException, + BadAuthenticationType, PartialAuthentication, ) from paramiko.server import InteractiveQuery from paramiko.ssh_gss import GSSAuth, GSS_EXCEPTIONS -class AuthHandler (object): +class AuthHandler(object): """ Internal class to handle the mechanics of authentication. """ @@ -57,7 +81,7 @@ class AuthHandler (object): self.username = None self.authenticated = False self.auth_event = None - self.auth_method = '' + self.auth_method = "" self.banner = None self.password = None self.private_key = None @@ -83,7 +107,7 @@ class AuthHandler (object): self.transport.lock.acquire() try: self.auth_event = event - self.auth_method = 'none' + self.auth_method = "none" self.username = username self._request_auth() finally: @@ -93,7 +117,7 @@ class AuthHandler (object): self.transport.lock.acquire() try: self.auth_event = event - self.auth_method = 'publickey' + self.auth_method = "publickey" self.username = username self.private_key = key self._request_auth() @@ -104,21 +128,21 @@ class AuthHandler (object): self.transport.lock.acquire() try: self.auth_event = event - self.auth_method = 'password' + self.auth_method = "password" self.username = username self.password = password self._request_auth() finally: self.transport.lock.release() - def auth_interactive(self, username, handler, event, submethods=''): + def auth_interactive(self, username, handler, event, submethods=""): """ response_list = handler(title, instructions, prompt_list) """ self.transport.lock.acquire() try: self.auth_event = event - self.auth_method = 'keyboard-interactive' + self.auth_method = "keyboard-interactive" self.username = username self.interactive_handler = handler self.submethods = submethods @@ -130,7 +154,7 @@ class AuthHandler (object): self.transport.lock.acquire() try: self.auth_event = event - self.auth_method = 'gssapi-with-mic' + self.auth_method = "gssapi-with-mic" self.username = username self.gss_host = gss_host self.gss_deleg_creds = gss_deleg_creds @@ -142,7 +166,7 @@ class AuthHandler (object): self.transport.lock.acquire() try: self.auth_event = event - self.auth_method = 'gssapi-keyex' + self.auth_method = "gssapi-keyex" self.username = username self._request_auth() finally: @@ -157,15 +181,15 @@ class AuthHandler (object): def _request_auth(self): m = Message() m.add_byte(cMSG_SERVICE_REQUEST) - m.add_string('ssh-userauth') + m.add_string("ssh-userauth") self.transport._send_message(m) def _disconnect_service_not_available(self): m = Message() m.add_byte(cMSG_DISCONNECT) m.add_int(DISCONNECT_SERVICE_NOT_AVAILABLE) - m.add_string('Service not available') - m.add_string('en') + m.add_string("Service not available") + m.add_string("en") self.transport._send_message(m) self.transport.close() @@ -173,8 +197,8 @@ class AuthHandler (object): m = Message() m.add_byte(cMSG_DISCONNECT) m.add_int(DISCONNECT_NO_MORE_AUTH_METHODS_AVAILABLE) - m.add_string('No more auth methods available') - m.add_string('en') + m.add_string("No more auth methods available") + m.add_string("en") self.transport._send_message(m) self.transport.close() @@ -184,7 +208,7 @@ class AuthHandler (object): m.add_byte(cMSG_USERAUTH_REQUEST) m.add_string(username) m.add_string(service) - m.add_string('publickey') + m.add_string("publickey") m.add_boolean(True) m.add_string(key.get_name()) m.add_string(key) @@ -199,7 +223,7 @@ class AuthHandler (object): if not self.transport.is_active(): e = self.transport.get_exception() if (e is None) or issubclass(e.__class__, EOFError): - e = AuthenticationException('Authentication failed.') + e = AuthenticationException("Authentication failed.") raise e if event.is_set(): break @@ -209,7 +233,7 @@ class AuthHandler (object): if not self.is_authenticated(): e = self.transport.get_exception() if e is None: - e = AuthenticationException('Authentication failed.') + e = AuthenticationException("Authentication failed.") # this is horrible. Python Exception isn't yet descended from # object, so type(e) won't work. :( if issubclass(e.__class__, PartialAuthentication): @@ -219,7 +243,7 @@ class AuthHandler (object): def _parse_service_request(self, m): service = m.get_text() - if self.transport.server_mode and (service == 'ssh-userauth'): + if self.transport.server_mode and (service == "ssh-userauth"): # accepted m = Message() m.add_byte(cMSG_SERVICE_ACCEPT) @@ -231,27 +255,28 @@ class AuthHandler (object): def _parse_service_accept(self, m): service = m.get_text() - if service == 'ssh-userauth': - self.transport._log(DEBUG, 'userauth is OK') + if service == "ssh-userauth": + self.transport._log(DEBUG, "userauth is OK") m = Message() m.add_byte(cMSG_USERAUTH_REQUEST) m.add_string(self.username) - m.add_string('ssh-connection') + m.add_string("ssh-connection") m.add_string(self.auth_method) - if self.auth_method == 'password': + if self.auth_method == "password": m.add_boolean(False) password = bytestring(self.password) m.add_string(password) - elif self.auth_method == 'publickey': + elif self.auth_method == "publickey": m.add_boolean(True) m.add_string(self.private_key.get_name()) m.add_string(self.private_key) blob = self._get_session_blob( - self.private_key, 'ssh-connection', self.username) + self.private_key, "ssh-connection", self.username + ) sig = self.private_key.sign_ssh_data(blob) m.add_string(sig) - elif self.auth_method == 'keyboard-interactive': - m.add_string('') + elif self.auth_method == "keyboard-interactive": + m.add_string("") m.add_string(self.submethods) elif self.auth_method == "gssapi-with-mic": sshgss = GSSAuth(self.auth_method, self.gss_deleg_creds) @@ -270,10 +295,11 @@ class AuthHandler (object): m = Message() m.add_byte(cMSG_USERAUTH_GSSAPI_TOKEN) try: - m.add_string(sshgss.ssh_init_sec_context( - self.gss_host, - mech, - self.username,)) + m.add_string( + sshgss.ssh_init_sec_context( + self.gss_host, mech, self.username + ) + ) except GSS_EXCEPTIONS as e: return self._handle_local_gss_failure(e) self.transport._send_message(m) @@ -286,7 +312,8 @@ class AuthHandler (object): self.gss_host, mech, self.username, - srv_token) + srv_token, + ) except GSS_EXCEPTIONS as e: return self._handle_local_gss_failure(e) # After this step the GSSAPI should not return any @@ -301,7 +328,8 @@ class AuthHandler (object): self.transport.send_message(m) else: raise SSHException( - "Received Package: %s" % MSG_NAMES[ptype]) + "Received Package: %s" % MSG_NAMES[ptype] + ) m = Message() m.add_byte(cMSG_USERAUTH_GSSAPI_MIC) # send the MIC to the server @@ -317,48 +345,51 @@ class AuthHandler (object): min_status = m.get_int() err_msg = m.get_string() m.get_string() # Lang tag - discarded - raise SSHException("GSS-API Error:\nMajor Status: %s\n\ + raise SSHException( + "GSS-API Error:\nMajor Status: %s\n\ Minor Status: %s\ \nError Message:\ - %s\n") % (str(maj_status), - str(min_status), - err_msg) + %s\n" + ) % (str(maj_status), str(min_status), err_msg) elif ptype == MSG_USERAUTH_FAILURE: self._parse_userauth_failure(m) return else: raise SSHException( - "Received Package: %s" % MSG_NAMES[ptype]) + "Received Package: %s" % MSG_NAMES[ptype] + ) elif ( - self.auth_method == 'gssapi-keyex' and - self.transport.gss_kex_used + self.auth_method == "gssapi-keyex" + and self.transport.gss_kex_used ): kexgss = self.transport.kexgss_ctxt kexgss.set_username(self.username) mic_token = kexgss.ssh_get_mic(self.transport.session_id) m.add_string(mic_token) - elif self.auth_method == 'none': + elif self.auth_method == "none": pass else: raise SSHException( - 'Unknown auth method "%s"' % self.auth_method) + 'Unknown auth method "%s"' % self.auth_method + ) self.transport._send_message(m) else: self.transport._log( - DEBUG, - 'Service request "%s" accepted (?)' % service) + DEBUG, 'Service request "%s" accepted (?)' % service + ) def _send_auth_result(self, username, method, result): # okay, send result m = Message() if result == AUTH_SUCCESSFUL: - self.transport._log(INFO, 'Auth granted (%s).' % method) + self.transport._log(INFO, "Auth granted (%s)." % method) m.add_byte(cMSG_USERAUTH_SUCCESS) self.authenticated = True else: - self.transport._log(INFO, 'Auth rejected (%s).' % method) + self.transport._log(INFO, "Auth rejected (%s)." % method) m.add_byte(cMSG_USERAUTH_FAILURE) m.add_string( - self.transport.server_object.get_allowed_auths(username)) + self.transport.server_object.get_allowed_auths(username) + ) if result == AUTH_PARTIALLY_SUCCESSFUL: m.add_boolean(True) else: @@ -388,7 +419,7 @@ class AuthHandler (object): # er, uh... what? m = Message() m.add_byte(cMSG_USERAUTH_FAILURE) - m.add_string('none') + m.add_string("none") m.add_boolean(False) self.transport._send_message(m) return @@ -400,16 +431,18 @@ class AuthHandler (object): method = m.get_text() self.transport._log( DEBUG, - 'Auth request (type=%s) service=%s, username=%s' % ( - method, service, username)) - if service != 'ssh-connection': + "Auth request (type=%s) service=%s, username=%s" + % (method, service, username), + ) + if service != "ssh-connection": self._disconnect_service_not_available() return - if ((self.auth_username is not None) and - (self.auth_username != username)): + if (self.auth_username is not None) and ( + self.auth_username != username + ): self.transport._log( WARNING, - 'Auth rejected because the client attempted to change username in mid-flight' # noqa + "Auth rejected because the client attempted to change username in mid-flight", # noqa ) self._disconnect_no_more_auth() return @@ -417,13 +450,13 @@ class AuthHandler (object): # check if GSS-API authentication is enabled gss_auth = self.transport.server_object.enable_auth_gssapi() - if method == 'none': + if method == "none": result = self.transport.server_object.check_auth_none(username) - elif method == 'password': + elif method == "password": changereq = m.get_boolean() password = m.get_binary() try: - password = password.decode('UTF-8') + password = password.decode("UTF-8") except UnicodeError: # some clients/servers expect non-utf-8 passwords! # in this case, just return the raw byte string. @@ -433,18 +466,19 @@ class AuthHandler (object): # passwords, but collect the list of valid auth types from # the callback anyway self.transport._log( - DEBUG, - 'Auth request to change passwords (rejected)') + DEBUG, "Auth request to change passwords (rejected)" + ) newpassword = m.get_binary() try: - newpassword = newpassword.decode('UTF-8', 'replace') + newpassword = newpassword.decode("UTF-8", "replace") except UnicodeError: pass result = AUTH_FAILED else: result = self.transport.server_object.check_auth_password( - username, password) - elif method == 'publickey': + username, password + ) + elif method == "publickey": sig_attached = m.get_boolean() keytype = m.get_text() keyblob = m.get_binary() @@ -452,20 +486,21 @@ class AuthHandler (object): key = self.transport._key_info[keytype](Message(keyblob)) except SSHException as e: self.transport._log( - INFO, - 'Auth rejected: public key: %s' % str(e)) + INFO, "Auth rejected: public key: %s" % str(e) + ) key = None except: self.transport._log( - INFO, - 'Auth rejected: unsupported or mangled public key') + INFO, "Auth rejected: unsupported or mangled public key" + ) key = None if key is None: self._disconnect_no_more_auth() return # first check if this key is okay... if not, we can skip the verify result = self.transport.server_object.check_auth_publickey( - username, key) + username, key + ) if result != AUTH_FAILED: # key is okay, verify it if not sig_attached: @@ -481,13 +516,14 @@ class AuthHandler (object): blob = self._get_session_blob(key, service, username) if not key.verify_ssh_sig(blob, sig): self.transport._log( - INFO, - 'Auth rejected: invalid signature') + INFO, "Auth rejected: invalid signature" + ) result = AUTH_FAILED - elif method == 'keyboard-interactive': + elif method == "keyboard-interactive": submethods = m.get_string() result = self.transport.server_object.check_auth_interactive( - username, submethods) + username, submethods + ) if isinstance(result, InteractiveQuery): # make interactive query instead of response self._interactive_query(result) @@ -503,7 +539,8 @@ class AuthHandler (object): if mechs > 1: self.transport._log( INFO, - 'Disconnect: Received more than one GSS-API OID mechanism') + "Disconnect: Received more than one GSS-API OID mechanism", + ) self._disconnect_no_more_auth() desired_mech = m.get_string() mech_ok = sshgss.ssh_check_mech(desired_mech) @@ -511,7 +548,8 @@ class AuthHandler (object): if not mech_ok: self.transport._log( INFO, - 'Disconnect: Received an invalid GSS-API OID mechanism') + "Disconnect: Received an invalid GSS-API OID mechanism", + ) self._disconnect_no_more_auth() # send the Kerberos V5 GSSAPI OID to the client supported_mech = sshgss.ssh_gss_oids("server") @@ -520,11 +558,14 @@ class AuthHandler (object): m = Message() m.add_byte(cMSG_USERAUTH_GSSAPI_RESPONSE) m.add_bytes(supported_mech) - self.transport.auth_handler = GssapiWithMicAuthHandler(self, - sshgss) - self.transport._expected_packet = (MSG_USERAUTH_GSSAPI_TOKEN, - MSG_USERAUTH_REQUEST, - MSG_SERVICE_REQUEST) + self.transport.auth_handler = GssapiWithMicAuthHandler( + self, sshgss + ) + self.transport._expected_packet = ( + MSG_USERAUTH_GSSAPI_TOKEN, + MSG_USERAUTH_REQUEST, + MSG_SERVICE_REQUEST, + ) self.transport._send_message(m) return elif method == "gssapi-keyex" and gss_auth: @@ -535,16 +576,17 @@ class AuthHandler (object): result = AUTH_FAILED self._send_auth_result(username, method, result) try: - sshgss.ssh_check_mic(mic_token, - self.transport.session_id, - self.auth_username) + sshgss.ssh_check_mic( + mic_token, self.transport.session_id, self.auth_username + ) except Exception: result = AUTH_FAILED self._send_auth_result(username, method, result) raise result = AUTH_SUCCESSFUL self.transport.server_object.check_auth_gssapi_keyex( - username, result) + username, result + ) else: result = self.transport.server_object.check_auth_none(username) # okay, send result @@ -552,8 +594,8 @@ class AuthHandler (object): def _parse_userauth_success(self, m): self.transport._log( - INFO, - 'Authentication (%s) successful!' % self.auth_method) + INFO, "Authentication (%s) successful!" % self.auth_method + ) self.authenticated = True self.transport._auth_trigger() if self.auth_event is not None: @@ -563,22 +605,22 @@ class AuthHandler (object): authlist = m.get_list() partial = m.get_boolean() if partial: - self.transport._log(INFO, 'Authentication continues...') - self.transport._log(DEBUG, 'Methods: ' + str(authlist)) + self.transport._log(INFO, "Authentication continues...") + self.transport._log(DEBUG, "Methods: " + str(authlist)) self.transport.saved_exception = PartialAuthentication(authlist) elif self.auth_method not in authlist: self.transport._log( DEBUG, - 'Authentication type (%s) not permitted.' % self.auth_method) - self.transport._log( - DEBUG, - 'Allowed methods: ' + str(authlist)) + "Authentication type (%s) not permitted." % self.auth_method, + ) + self.transport._log(DEBUG, "Allowed methods: " + str(authlist)) self.transport.saved_exception = BadAuthenticationType( - 'Bad authentication type', authlist) + "Bad authentication type", authlist + ) else: self.transport._log( - INFO, - 'Authentication (%s) failed.' % self.auth_method) + INFO, "Authentication (%s) failed." % self.auth_method + ) self.authenticated = False self.username = None if self.auth_event is not None: @@ -587,12 +629,12 @@ class AuthHandler (object): def _parse_userauth_banner(self, m): banner = m.get_string() self.banner = banner - self.transport._log(INFO, 'Auth banner: %s' % banner) + self.transport._log(INFO, "Auth banner: %s" % banner) # who cares. def _parse_userauth_info_request(self, m): - if self.auth_method != 'keyboard-interactive': - raise SSHException('Illegal info request from server') + if self.auth_method != "keyboard-interactive": + raise SSHException("Illegal info request from server") title = m.get_text() instructions = m.get_text() m.get_binary() # lang @@ -601,7 +643,8 @@ class AuthHandler (object): for i in range(prompts): prompt_list.append((m.get_text(), m.get_boolean())) response_list = self.interactive_handler( - title, instructions, prompt_list) + title, instructions, prompt_list + ) m = Message() m.add_byte(cMSG_USERAUTH_INFO_RESPONSE) @@ -612,25 +655,28 @@ class AuthHandler (object): def _parse_userauth_info_response(self, m): if not self.transport.server_mode: - raise SSHException('Illegal info response from server') + raise SSHException("Illegal info response from server") n = m.get_int() responses = [] for i in range(n): responses.append(m.get_text()) result = self.transport.server_object.check_auth_interactive_response( - responses) + responses + ) if isinstance(result, InteractiveQuery): # make interactive query instead of response self._interactive_query(result) return self._send_auth_result( - self.auth_username, 'keyboard-interactive', result) + self.auth_username, "keyboard-interactive", result + ) def _handle_local_gss_failure(self, e): self.transport.saved_exception = e self.transport._log(DEBUG, "GSSAPI failure: %s" % str(e)) - self.transport._log(INFO, 'Authentication (%s) failed.' % - self.auth_method) + self.transport._log( + INFO, "Authentication (%s) failed." % self.auth_method + ) self.authenticated = False self.username = None if self.auth_event is not None: @@ -691,9 +737,9 @@ class GssapiWithMicAuthHandler(object): # context. sshgss = self.sshgss try: - token = sshgss.ssh_accept_sec_context(self.gss_host, - client_token, - self.auth_username) + token = sshgss.ssh_accept_sec_context( + self.gss_host, client_token, self.auth_username + ) except Exception as e: self.transport.saved_exception = e result = AUTH_FAILED @@ -704,9 +750,11 @@ class GssapiWithMicAuthHandler(object): m = Message() m.add_byte(cMSG_USERAUTH_GSSAPI_TOKEN) m.add_string(token) - self.transport._expected_packet = (MSG_USERAUTH_GSSAPI_TOKEN, - MSG_USERAUTH_GSSAPI_MIC, - MSG_USERAUTH_REQUEST) + self.transport._expected_packet = ( + MSG_USERAUTH_GSSAPI_TOKEN, + MSG_USERAUTH_GSSAPI_MIC, + MSG_USERAUTH_REQUEST, + ) self.transport._send_message(m) def _parse_userauth_gssapi_mic(self, m): @@ -715,9 +763,9 @@ class GssapiWithMicAuthHandler(object): username = self.auth_username self._restore_delegate_auth_handler() try: - sshgss.ssh_check_mic(mic_token, - self.transport.session_id, - username) + sshgss.ssh_check_mic( + mic_token, self.transport.session_id, username + ) except Exception as e: self.transport.saved_exception = e result = AUTH_FAILED @@ -727,8 +775,9 @@ class GssapiWithMicAuthHandler(object): # The OpenSSH server is able to create a TGT with the delegated # client credentials, but this is not supported by GSS-API. result = AUTH_SUCCESSFUL - self.transport.server_object.check_auth_gssapi_with_mic(username, - result) + self.transport.server_object.check_auth_gssapi_with_mic( + username, result + ) # okay, send result self._send_auth_result(username, self.method, result) |