This is an automated email from the git hooks/post-receive script. x2go pushed a commit to annotated tag 0.6.1.0 in repository x2goclient.
commit 34715b3f19c32462696fb202513297f5ace37771 Author: Mike Gabriel <mike.gabr...@das-netzwerkteam.de> Date: Tue Sep 18 13:10:40 2018 +0200 Entirely rewrite PyHocaCLI.authenticate() function. (Fixes: #1308). --- debian/changelog | 1 + pyhoca-cli | 2 + pyhoca/cli/frontend.py | 177 +++++++++++++++++++++++++++++++++++++++++-------- 3 files changed, 151 insertions(+), 29 deletions(-) diff --git a/debian/changelog b/debian/changelog index 11ecdc90..d0b46a46 100644 --- a/debian/changelog +++ b/debian/changelog @@ -13,6 +13,7 @@ pyhoca-cli (0.5.99.1-0x2go2) UNRELEASED; urgency=medium a usable / expectable value. - Add --forward-sshagent / -A cmdline option for enabling SSH agent forwarding. + - Entirely rewrite PyHocaCLI.authenticate() function. (Fixes: #1308). * debian/rules: + Build for Python3, not Python2. + Switch to pybuild DH build system. diff --git a/pyhoca-cli b/pyhoca-cli index 1856777d..7bf6138e 100755 --- a/pyhoca-cli +++ b/pyhoca-cli @@ -372,6 +372,8 @@ Possible values for the --pack NX option are: _dummy = int(a.auth_attempts) except ValueError: runtime_error ("value for cmd line argument --auth-attempts has to be of type integer", parser=p, exitcode=1) + if int(a.auth_attempts) < 1: + a.auth_attempts = "1" if a.server: diff --git a/pyhoca/cli/frontend.py b/pyhoca/cli/frontend.py index a73dc2f0..4dbe6439 100644 --- a/pyhoca/cli/frontend.py +++ b/pyhoca/cli/frontend.py @@ -468,7 +468,22 @@ class PyHocaCLI(x2go.X2GoClient): """ connected = False - force_password_auth = False + + if self.args.password: + password = self.args.password + self.args.password = None + cmdline_password = True + force_password_auth = True + else: + password = None + cmdline_password = False + force_password_auth = False + + if self.args.force_password: + force_password_auth = True + + passphrase = None + passphrase_unlock_counter = 3 _username = self.args.username or self._X2GoClient__get_session_username(self.x2go_session_hash) @@ -477,53 +492,157 @@ class PyHocaCLI(x2go.X2GoClient): _username = self.session_profiles.get_broker_username() try: - _auth_count = self.auth_attempts +1 + _auth_count = self.auth_attempts while not connected and _auth_count: + try: - if self.args.force_password: - self.args.password = getpass.getpass() - self._X2GoClient__connect_session(self.x2go_session_hash, username=_username, password=self.args.password, force_password_auth=force_password_auth) + + # decrement authentication counter... + _auth_count -= 1 + + # show interactive password prompt + if force_password_auth and not cmdline_password: + + password = getpass.getpass() + + # workaround for Python bug: http://bugs.python.org/issue11236 + try: + if password is not None and '\x03' in password: + raise KeyboardInterrupt() + except KeyboardInterrupt: + self._runtime_error('Authentication cancelled by user by hitting Ctrl-C at password prompt', exitcode=-200) + + if not password: + self._pyhoca_logger('password is empty, please re-try... (hit Ctrl-C plus ENTER to cancel)', loglevel=x2go.loglevel_WARN, ) + _auth_count += 1 + continue + + # connection attempt with remote X2Go Server + self._X2GoClient__connect_session(self.x2go_session_hash, username=_username, password=password, passphrase=passphrase, force_password_auth=force_password_auth) + + # we succeeeded connected = True - force_password_auth = False + + # this will end the loop (as connected is set to True) + continue + except x2go.PasswordRequiredException as e: - self._pyhoca_logger('unlock SSH key file (%s)' % self.args.ssh_privkey, loglevel=x2go.loglevel_NOTICE, ) - self.args.password = getpass.getpass() + + # x2go.PasswordRequiredException: This exception gets raised if an SSH pubkey is protected by a passphrase + + if not force_password_auth and passphrase_unlock_counter >= 1: + if passphrase == '': + self._pyhoca_logger('empty SSH key passphrase (%s), try again...' % self.args.ssh_privkey, loglevel=x2go.loglevel_WARN, ) + self._pyhoca_logger('unlock SSH key file (%s)' % self.args.ssh_privkey, loglevel=x2go.loglevel_NOTICE, ) + passphrase = getpass.getpass() + passphrase_unlock_counter -= 1 + # undo auth counter decrement + _auth_count += 1 + continue + + if not force_password_auth and _auth_count >= 1: + self._pyhoca_logger('unlocking of SSH key failed, proceeding with interactive login', loglevel=x2go.loglevel_WARN, ) + force_password_auth = True + password = None + passphrase = None + except x2go.AuthenticationException as e: - force_password_auth = True - self._pyhoca_logger('passwordless login for ,,%s\'\' failed' % _username, loglevel=x2go.loglevel_WARN, ) - self._pyhoca_logger('proceeding to interactive login for user ,,%s\'\'' % _username, loglevel=x2go.loglevel_NOTICE, ) + + # x2go.AuthenticationException: This exception gets raised if the authentication failed + + if force_password_auth: + self._pyhoca_logger('password based login for ,,%s\'\' failed [AuthException]' % _username, loglevel=x2go.loglevel_WARN, ) + else: + self._pyhoca_logger('passwordless login for ,,%s\'\' failed [AuthException]' % _username, loglevel=x2go.loglevel_WARN, ) + + # if the previous login attempt was pubkey based, enforce interactive login for the next round... + if not password and _auth_count >= 1: + self._pyhoca_logger('proceeding to interactive login for user ,,%s\'\'' % _username, loglevel=x2go.loglevel_NOTICE, ) + force_password_auth = True + # undo auth counter decrement + _auth_count += 1 + + # a password was provided via the command line + elif password and cmdline_password and _auth_count >= 1: + self._pyhoca_logger('cmdline provided password failed, proceeding to interactive login for user ,,%s\'\'' % _username, loglevel=x2go.loglevel_WARN, ) + force_password_auth = True + cmdline_password = False + + # else, if the previous attempt was already interactive, offer re-trying + elif force_password_auth and _auth_count >= 1: + self._pyhoca_logger('please re-try login for user ,,%s\'\'' % _username, loglevel=x2go.loglevel_NOTICE, ) + + passphrase = None + password = None + except x2go.BadHostKeyException: + + # let's bail out here immediately... + self._runtime_error('SSH host key verification for remote host [%s]:%s failed' % (self.args.server, self.args.remote_ssh_port), exitcode=-254) + except x2go.SSHException as e: - if str(e) not in ('not a valid DSA private key file', 'Incompatible ssh peer (no acceptable kex algorithm)', 'No authentication methods available'): - self._runtime_error(str(e), exitcode=253) - force_password_auth = True - self._pyhoca_logger('passwordless login for ,,%s\'\' failed' % _username, loglevel=x2go.loglevel_WARN, ) - self._pyhoca_logger('proceeding to interactive login for user ,,%s\'\'' % _username, loglevel=x2go.loglevel_NOTICE, ) - if force_password_auth: - self.args.password = getpass.getpass() + # this bit only captures problems with the SSH key file, other + # SSHExceptions are simply ignored (and we proceed to interactive login) + + if str(e).lower().startswith('could not deserialize key data') \ + : + + # this error gets thrown when the passphrase for unlocking the SSH privkey was wrong + + if not force_password_auth and passphrase_unlock_counter >= 1: + if passphrase is not None and passphrase != '': + self._pyhoca_logger('wrong SSH key passphrase (%s), try again...' % self.args.ssh_privkey, loglevel=x2go.loglevel_WARN, ) + self._pyhoca_logger('unlock SSH key file (%s)' % self.args.ssh_privkey, loglevel=x2go.loglevel_NOTICE, ) + passphrase = getpass.getpass() + passphrase_unlock_counter -= 1 + # undo auth counter decrement + _auth_count += 1 + continue + + if not force_password_auth and _auth_count >= 1: + self._pyhoca_logger('unlocking of SSH key failed, proceeding with interactive login', loglevel=x2go.loglevel_WARN, ) + force_password_auth = True + password = None + passphrase = None + + elif not str(e).lower().startswith('not a valid dsa private key file') or \ + not str(e).lower().startswith('not a valid rsa private key file') or \ + not str(e).lower().startswith('incompatible ssh peer (no acceptable kex algorithm)') or \ + not str(e).lower().startswith('no authentication methods available') \ + : + + if force_password_auth: + self._pyhoca_logger('password based login for ,,%s\'\' failed [SSHException]' % _username, loglevel=x2go.loglevel_WARN, ) + else: + self._pyhoca_logger('passwordless login for ,,%s\'\' failed [SSHException]' % _username, loglevel=x2go.loglevel_WARN, ) + + # let's bail out here... + self._runtime_error(str(e), exitcode=253) - # workaround for Python bug: http://bugs.python.org/issue11236 - try: - if self.args.password is not None and '\x03' in self.args.password: - raise KeyboardInterrupt() - except KeyboardInterrupt: - self._runtime_error('Authentication cancelled by user by hitting Ctrl-C at password prompt', exitcode=-200) + else: - _auth_count -= 1 + self._pyhoca_logger('[SSHException] the following error will be ignored: %s' % str(e), loglevel=x2go.loglevel_WARN) + self._pyhoca_logger('proceeding to interactive login for user ,,%s\'\'' % _username, loglevel=x2go.loglevel_NOTICE, ) + force_password_auth = True + password = None + passphrase = None - if not connected and not _auth_count: + if not connected and _auth_count <= 0: if self.auth_attempts >= 2: self._runtime_error('authentication failed, too many failures during interactive login', exitcode=-201) elif self.auth_attempts == 1: - self._runtime_error('interactive authentication failed', exitcode=-202) - else: - self._runtime_error('non-interactive authentication failed', exitcode=-203) + self._runtime_error('authentication failed', exitcode=-202) + except socket.error as e: self._runtime_error('a socket error occured while establishing the connection: %s' % str(e), exitcode=-245) + # drop clear text passwords... + password = None + passphrase = None + def MainLoop(self): """\ Start the main loop of this application. -- Alioth's /home/x2go-admin/maintenancescripts/git/hooks/post-receive-email on /srv/git/code.x2go.org/x2goclient.git _______________________________________________ x2go-commits mailing list x2go-commits@lists.x2go.org https://lists.x2go.org/listinfo/x2go-commits