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

Reply via email to