[X2Go-Commits] python-x2go.git - twofactorauth (branch) updated: 0.0.37.0-48-g2be37b5

X2Go dev team git-admin at x2go.org
Sat Sep 14 15:56:14 CEST 2013


The branch, twofactorauth has been updated
       via  2be37b5636f7867e4e692d9337bd986ba8a1393e (commit)
      from  88a3386e5db21c608da8dd382c268912ecc57b37 (commit)

Those revisions listed above that are new to this repository have
not appeared on any other notification email; so we list those
revisions in full, below.

- Log -----------------------------------------------------------------
-----------------------------------------------------------------------

Summary of changes:
 x2go/backends/control/_stdout.py |   68 ++++++++++++++++----------------------
 x2go/checkhosts.py               |   68 ++++++++++++++++++++++++++++++++++++--
 x2go/registry.py                 |   15 +++++----
 x2go/session.py                  |    5 ++-
 x2go/sshproxy.py                 |   30 ++++++++++++++---
 x2go/x2go_exceptions.py          |    2 ++
 6 files changed, 134 insertions(+), 54 deletions(-)

The diff of changes is:
diff --git a/x2go/backends/control/_stdout.py b/x2go/backends/control/_stdout.py
index a07c3f5..7490809 100644
--- a/x2go/backends/control/_stdout.py
+++ b/x2go/backends/control/_stdout.py
@@ -34,6 +34,9 @@ import gevent
 import copy
 import binascii
 
+import string
+import random
+
 # Python X2go modules
 import x2go.sshproxy as sshproxy
 import x2go.log as log
@@ -238,41 +241,16 @@ class X2goControlSessionSTDOUT(paramiko.SSHClient):
 
     def check_host(self, hostname, port=22):
         """\
-        Perform a Paramiko/SSH host key check.
+        Wraps around a Paramiko/SSH host key check.
 
         """
-        _hostname = hostname
-        _port = port
-        _fingerprint = 'NO-FINGERPRINT'
-        _fingerprint_type = 'SOME-KEY-TYPE'
-
-        _check_policy = checkhosts.X2goCheckHostKeyPolicy()
-        self.set_missing_host_key_policy(_check_policy)
-
-        try:
-            paramiko.SSHClient.connect(self, hostname=hostname, port=port, username='foo', password='bar')
-        except x2go_exceptions.AuthenticationException:
-            host_ok = True
-            self.logger('SSH host key verification for host [%s]:%s succeeded. Host is already known to the client\'s Paramiko/SSH sub-system.' % (_hostname, _port), loglevel=log.loglevel_NOTICE)
-        except x2go_exceptions.SSHException, e:
-            msg = str(e)
-            if msg.startswith('Checked host key for X2go server '):
-                host_ok = False
-                _hostname = _check_policy.get_hostname().split(':')[0].lstrip('[').rstrip(']')
-                _port = _check_policy.get_hostname().split(':')[1]
-                _fingerprint = _check_policy.get_key_fingerprint_with_colons()
-                _fingerprint_type = _check_policy.get_key_name()
-                self.logger('SSH host key verification for host [%s]:%s with %s fingerprint ,,%s\'\' failed. Seeing this X2go server for the first time.' % (_hostname, _port, _fingerprint_type, _fingerprint), loglevel=log.loglevel_NOTICE)
-            else:
-                raise(e)
-
-        self.set_missing_host_key_policy(paramiko.RejectPolicy())
-        return (host_ok, _hostname, _port, _fingerprint, _fingerprint_type)
+        return checkhosts.check_ssh_host_key(self, hostname, port=port)
 
     def connect(self, hostname, port=22, username='', password='', pkey=None,
                 use_sshproxy=False, sshproxy_host='', sshproxy_user='', sshproxy_password='',
                 sshproxy_key_filename='', sshproxy_tunnel='',
                 key_filename=None, timeout=None, allow_agent=False, look_for_keys=False,
+                session_instance=None,
                 add_to_known_hosts=False, force_password_auth=False):
         """\
         Connect to an X2go server and authenticate to it. This method is directly
@@ -326,6 +304,9 @@ class X2goControlSessionSTDOUT(paramiko.SSHClient):
         @param force_password_auth: non-paramiko option, disable pub/priv key authentication 
             completely, even if the C{pkey} or the C{key_filename} parameter is given
         @type force_password_auth: C{bool}
+        @param session_instance: an instance L{X2goSession} using this L{X2goControlSessionSTDOUT}
+            instance.
+        @type session_instance: C{instance}
 
         @raise BadHostKeyException: if the server's host key could not be
             verified
@@ -337,14 +318,13 @@ class X2goControlSessionSTDOUT(paramiko.SSHClient):
         """
         if use_sshproxy and sshproxy_host and sshproxy_user:
             try:
-                add_to_known_hosts = add_to_known_hosts | self.add_to_known_hosts
-                self.sshproxy_session = sshproxy.X2goSSHProxy(add_to_known_hosts=add_to_known_hosts,
-                                                              known_hosts=self.known_hosts,
+                self.sshproxy_session = sshproxy.X2goSSHProxy(known_hosts=self.known_hosts,
                                                               sshproxy_host=sshproxy_host,
                                                               sshproxy_user=sshproxy_user,
                                                               sshproxy_password=sshproxy_password,
                                                               sshproxy_key_filename=sshproxy_key_filename,
                                                               sshproxy_tunnel=sshproxy_tunnel,
+                                                              session_instance=session_instance,
                                                               logger=self.logger,
                                                              )
 
@@ -364,6 +344,10 @@ class X2goControlSessionSTDOUT(paramiko.SSHClient):
                 gevent.sleep(1)
                 port = self.sshproxy_session.get_local_proxy_port()
 
+
+        if not add_to_known_hosts and session_instance:
+            self.set_missing_host_key_policy(checkhosts.X2goInteractiveAddPolicy(caller=self, session_instance=session_instance))
+
         if add_to_known_hosts:
             self.set_missing_host_key_policy(paramiko.AutoAddPolicy())
 
@@ -374,6 +358,9 @@ class X2goControlSessionSTDOUT(paramiko.SSHClient):
 
         self.logger('connecting to %s' % hostname, loglevel=log.loglevel_NOTICE)
 
+        if self.known_hosts is not None:
+            utils.touch_file(self.known_hosts)
+            self.load_host_keys(self.known_hosts)
         if (key_filename and os.path.exists(os.path.normpath(key_filename))) or pkey:
             try:
                 self.logger('trying SSH pub/priv key authentication with server', loglevel=log.loglevel_DEBUG)
@@ -399,8 +386,11 @@ class X2goControlSessionSTDOUT(paramiko.SSHClient):
                     self.close()
                     raise(e)
 
-        # if there is not private key, we will use the given password
-        elif password:
+        # if there is not private key, we will use the given password, if any
+        else:
+            # create a random password if password is empty to trigger host key validity check
+            if not password:
+                password = "".join([random.choice(string.letters+string.digits) for x in range(1, 20)])
             self.logger('performing SSH keyboard-interactive authentication with server', loglevel=log.loglevel_DEBUG)
             try:
                 paramiko.SSHClient.connect(self, hostname, port=port, username=username, password=password,
@@ -410,13 +400,11 @@ class X2goControlSessionSTDOUT(paramiko.SSHClient):
                     self.sshproxy_session.stop_thread()
                 self.close()
                 raise(e)
-
-        # authentication failed
-        else:
-            if self.sshproxy_session:
-                self.sshproxy_session.stop_thread()
-            self.close()
-            raise paramiko.AuthenticationException()
+            except x2go_exceptions.X2goHostKeyException, e:
+                if self.sshproxy_session:
+                    self.sshproxy_session.stop_thread()
+                self.close()
+                raise(e)
 
         self.set_missing_host_key_policy(paramiko.RejectPolicy())
 
diff --git a/x2go/checkhosts.py b/x2go/checkhosts.py
index d4eda9a..2bdcc4b 100644
--- a/x2go/checkhosts.py
+++ b/x2go/checkhosts.py
@@ -28,9 +28,11 @@ import paramiko
 import binascii
 
 # Python X2go modules
+import sshproxy
+import log
 import x2go_exceptions
 
-class X2goCheckHostKeyPolicy(paramiko.MissingHostKeyPolicy):
+class X2goInteractiveAddPolicy(paramiko.MissingHostKeyPolicy):
     """\
     Policy for making host key information available to Python X2go after a 
     Paramiko/SSH connect has been attempted. A connect that uses this 
@@ -38,13 +40,32 @@ class X2goCheckHostKeyPolicy(paramiko.MissingHostKeyPolicy):
 
     This is used by L{X2goControlSessionSTDOUT}.
     """
+    def __init__(self, caller=None, session_instance=None):
+        self.caller = caller
+        self.session_instance = session_instance
+
     def missing_host_key(self, client, hostname, key):
         self.client = client
         self.hostname = hostname
         self.key = key
-        client._log(paramiko.common.DEBUG, 'Checking %s host key for %s: %s' %
+        client._log(paramiko.common.DEBUG, 'Interactively Checking %s host key for %s: %s' %
                    (key.get_name(), hostname, binascii.hexlify(key.get_fingerprint())))
-        raise x2go_exceptions.SSHException('Checked host key for X2go server %s' % hostname)
+        if self.session_instance:
+            self.session_instance.logger('SSH host key verification for host %s with %s fingerprint ,,%s\'\' initiated. We are seeing this X2go server for the first time.' % (hostname, self.get_key_name(), self.get_key_fingerprint_with_colons()), loglevel=log.loglevel_NOTICE)
+            _valid = self.session_instance.HOOK_check_host_dialog(self.get_hostname_name(),
+                                                                  port=self.get_hostname_port(),
+                                                                  fingerprint=self.get_key_fingerprint_with_colons(),
+                                                                  fingerprint_type=self.get_key_name(),
+                                                                 )
+            if _valid:
+                paramiko.AutoAddPolicy().missing_host_key(client, hostname, key)
+            else:
+                if type(self.caller) in (sshproxy.X2goSSHProxy, ):
+                    raise x2go_exceptions.X2goSSHProxyHostKeyException('Invalid host %s is not authorized for access. Add the host to Paramiko/SSH\'s known_hosts file.' % hostname)
+                else:
+                    raise x2go_exceptions.X2goHostKeyException('Invalid host %s is not authorized for access. Add the host to Paramiko/SSH\'s known_hosts file.' % hostname)
+        else:
+            raise x2go_exceptions.SSHException('Policy has collected host key information on %s for further introspection' % hostname)
 
     def get_client(self):
         return self.client
@@ -52,6 +73,12 @@ class X2goCheckHostKeyPolicy(paramiko.MissingHostKeyPolicy):
     def get_hostname(self):
         return self.hostname
 
+    def get_hostname_name(self):
+        return self.get_hostname().split(':')[0].lstrip('[').rstrip(']')
+
+    def get_hostname_port(self):
+        return self.get_hostname().split(':')[1]
+
     def get_key(self):
         return self.key
 
@@ -72,3 +99,38 @@ class X2goCheckHostKeyPolicy(paramiko.MissingHostKeyPolicy):
                 _colon_fingerprint += ':'
         return _colon_fingerprint.rstrip(':')
 
+
+def check_ssh_host_key(x2go_sshclient_instance, hostname, port=22):
+    """\
+    Perform a Paramiko/SSH host key check.
+
+    """
+    _hostname = hostname
+    _port = port
+    _fingerprint = 'NO-FINGERPRINT'
+    _fingerprint_type = 'SOME-KEY-TYPE'
+
+    _check_policy = X2goInteractiveAddPolicy()
+    x2go_sshclient_instance.set_missing_host_key_policy(_check_policy)
+
+    host_ok = False
+    try:
+        paramiko.SSHClient.connect(x2go_sshclient_instance, hostname=hostname, port=port, username='foo', password='bar')
+    except x2go_exceptions.AuthenticationException:
+        host_ok = True
+        x2go_sshclient_instance.logger('SSH host key verification for host [%s]:%s succeeded. Host is already known to the client\'s Paramiko/SSH sub-system.' % (_hostname, _port), loglevel=log.loglevel_NOTICE)
+    except x2go_exceptions.SSHException, e:
+        msg = str(e)
+        if msg.startswith('Policy has collected host key information on '):
+            _hostname = _check_policy.get_hostname().split(':')[0].lstrip('[').rstrip(']')
+            _port = _check_policy.get_hostname().split(':')[1]
+            _fingerprint = _check_policy.get_key_fingerprint_with_colons()
+            _fingerprint_type = _check_policy.get_key_name()
+        else:
+            raise(e)
+        x2go_sshclient_instance.set_missing_host_key_policy(paramiko.RejectPolicy())
+    except:
+        # let any other error be handled by subsequent algorithms
+        pass
+
+    return (host_ok, _hostname, _port, _fingerprint, _fingerprint_type)
diff --git a/x2go/registry.py b/x2go/registry.py
index 7a17017..8d134e7 100644
--- a/x2go/registry.py
+++ b/x2go/registry.py
@@ -71,6 +71,9 @@ class X2goSessionRegistry(object):
         self.registry = {}
         self.control_sessions = {}
 
+    def keys(self):
+        return self.registry.keys()
+
     def __repr__(self):
         result = 'X2goSessionRegistry('
         for p in dir(self):
@@ -118,13 +121,13 @@ class X2goSessionRegistry(object):
             _session_summary['control_params'] = _r and self(session_uuid).control_params or {}
             _session_summary['terminal_session'] = _r and self(session_uuid).get_terminal_session() or None
             _session_summary['terminal_params'] = _r and self(session_uuid).terminal_params or {}
-            _session_summary['active_threads'] = _r and self(session_uuid).get_terminal_session().active_threads or []
+            _session_summary['active_threads'] = _r and bool(self(session_uuid).get_terminal_session()) and self(session_uuid).get_terminal_session().active_threads or []
             _session_summary['backends'] = {
-                'control': _r and self(session_uuid)._control_backend or None,
-                'terminal': _r and self(session_uuid)._terminal_backend or None,
-                'info': _r and self(session_uuid)._info_backend or None,
-                'list': _r and self(session_uuid)._list_backend or None,
-                'proxy': _r and self(session_uuid)._proxy_backend or None,
+                'control': _r and self(session_uuid).control_backend or None,
+                'terminal': _r and self(session_uuid).terminal_backend or None,
+                'info': _r and self(session_uuid).info_backend or None,
+                'list': _r and self(session_uuid).list_backend or None,
+                'proxy': _r and self(session_uuid).proxy_backend or None,
             }
 
         if _r:
diff --git a/x2go/session.py b/x2go/session.py
index d65226f..c225c60 100644
--- a/x2go/session.py
+++ b/x2go/session.py
@@ -525,7 +525,10 @@ class X2goSession(object):
             _params.update(self.control_params)
             _params.update(self.sshproxy_params)
 
-            self.connected = self.control_session.connect(self.server, use_sshproxy=self.use_sshproxy, **_params)
+            self.connected = self.control_session.connect(self.server,
+                                                          use_sshproxy=self.use_sshproxy, 
+                                                          session_instance=self, 
+                                                          **_params)
             # remove credentials immediately
             self.control_params['password'] = ''
             try: del self.control_params['sshproxy_user']
diff --git a/x2go/sshproxy.py b/x2go/sshproxy.py
index 69cdce7..7ef8e63 100644
--- a/x2go/sshproxy.py
+++ b/x2go/sshproxy.py
@@ -31,8 +31,12 @@ import paramiko
 import threading
 import socket
 
+import string
+import random
+
 # Python X2go modules
 import x2go.forward as forward
+import x2go.checkhosts as checkhosts
 import x2go.log as log
 import x2go.utils as utils
 from x2go.x2go_exceptions import *
@@ -56,6 +60,7 @@ class X2goSSHProxy(paramiko.SSHClient, threading.Thread):
                  sshproxy_password=None, sshproxy_key_filename=None, 
                  sshproxy_tunnel=None,
                  ssh_rootdir=os.path.join(_LOCAL_HOME, _X2GO_SSH_ROOTDIR), 
+                 session_instance=None,
                  logger=None, loglevel=log.loglevel_DEFAULT, ):
         """\
         STILL UNDOCUMENTED
@@ -77,7 +82,7 @@ class X2goSSHProxy(paramiko.SSHClient, threading.Thread):
         self.hostname, self.port, self.username = hostname, port, username
 
         # translate between X2goSession options and paramiko.SSHCLient.connect() options
-        if sshproxy_host: 
+        if sshproxy_host:
             if sshproxy_host.find(':'):
                 self.hostname = sshproxy_host.split(':')[0]
                 try: self.port = int(sshproxy_host.split(':')[1])
@@ -109,6 +114,9 @@ class X2goSSHProxy(paramiko.SSHClient, threading.Thread):
             utils.touch_file(known_hosts)
             self.load_host_keys(known_hosts)
 
+        if not add_to_known_hosts and session_instance:
+            self.set_missing_host_key_policy(checkhosts.X2goInteractiveAddPolicy(caller=self, session_instance=session_instance))
+
         if add_to_known_hosts:
             self.set_missing_host_key_policy(paramiko.AutoAddPolicy())
 
@@ -124,7 +132,11 @@ class X2goSSHProxy(paramiko.SSHClient, threading.Thread):
                 except AuthenticationException, e:
                     raise X2goSSHProxyAuthenticationException('pubkey auth mechanisms both failed')
 
-            elif password:
+            # if there is not private key, we will use the given password, if any
+            else:
+                # create a random password if password is empty to trigger host key validity check
+                if not password:
+                    password = "".join([random.choice(string.letters+string.digits) for x in range(1, 20)])
                 try:
                     self.connect(self.hostname, port=self.port,
                                  username=self.username,
@@ -134,8 +146,7 @@ class X2goSSHProxy(paramiko.SSHClient, threading.Thread):
                                 )
                 except AuthenticationException:
                     raise X2goSSHProxyAuthenticationException('interactive auth mechanisms failed')
-            else:
-                raise X2goSSHProxyAuthenticationException('no auth mechanism available')
+
         except paramiko.SSHException, e:
             raise X2goSSHProxyException(str(e))
 
@@ -143,6 +154,17 @@ class X2goSSHProxy(paramiko.SSHClient, threading.Thread):
         threading.Thread.__init__(self)
         self.daemon = True
 
+    def check_host(self):
+        """\
+        Wraps around a Paramiko/SSH host key check.
+
+        """
+        _valid = False
+        (_valid, _hostname, _port, _fingerprint, _fingerprint_type) = checkhosts.check_ssh_host_key(self, self.hostname, port=self.port)
+        if not _valid and self.session_instance:
+            _valid = self.session_instance.HOOK_check_host_dialog(_hostname, _port, fingerprint=_fingerprint, fingerprint_type=_fingerprint_type)
+        return _valid
+
     def run(self):
 
         if self.get_transport() is not None and self.get_transport().is_authenticated():
diff --git a/x2go/x2go_exceptions.py b/x2go/x2go_exceptions.py
index 73faf76..22a98fb 100644
--- a/x2go/x2go_exceptions.py
+++ b/x2go/x2go_exceptions.py
@@ -43,6 +43,8 @@ class _X2goException(exceptions.BaseException): pass
 class X2goClientException(_X2goException): pass
 class X2goSessionException(_X2goException): pass
 class X2goControlSessionException(_X2goException): pass
+class X2goHostKeyException(_X2goException): pass
+class X2goSSHProxyHostKeyException(_X2goException): pass
 class X2goTerminalSessionException(_X2goException): pass
 class X2goSessionCacheException(_X2goException): pass
 class X2goUserException(_X2goException): pass


hooks/post-receive
-- 
python-x2go.git (Python X2Go Client API)

This is an automated email from the git hooks/post-receive script. It was
generated because a ref change was pushed to the repository containing
the project "python-x2go.git" (Python X2Go Client API).




More information about the x2go-commits mailing list