This file is indexed.

/usr/share/pyshared/x2go/sshproxy.py is in python-x2go 0.4.0.8-1.

This file is owned by root:root, with mode 0o644.

The actual contents of the file can be viewed below.

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
# -*- coding: utf-8 -*-

# Copyright (C) 2010-2013 by Mike Gabriel <mike.gabriel@das-netzwerkteam.de>
#
# Python X2Go is free software; you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as published by
# the Free Software Foundation; either version 3 of the License, or
# (at your option) any later version.
#
# Python X2Go is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program; if not, write to the
# Free Software Foundation, Inc.,
# 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA.

"""\
L{X2GoSSHProxy} class - providing a forwarding tunnel for connecting to servers behind firewalls.

"""
__NAME__ = 'x2gosshproxy-pylib'

# modules
import gevent
import os
import copy
import paramiko
import threading

import string
import random

# Python X2Go modules
import forward
import checkhosts
import log
import utils
import x2go_exceptions

from x2go.defaults import CURRENT_LOCAL_USER as _CURRENT_LOCAL_USER
from x2go.defaults import LOCAL_HOME as _LOCAL_HOME
from x2go.defaults import X2GO_SSH_ROOTDIR as _X2GO_SSH_ROOTDIR

import x2go._paramiko
x2go._paramiko.monkey_patch_paramiko()

class X2GoSSHProxy(paramiko.SSHClient, threading.Thread):
    """\
    X2GoSSHProxy can be used to proxy X2Go connections through a firewall via SSH.

    """
    fw_tunnel = None

    def __init__(self, hostname=None, port=22, username=None, password=None, force_password_auth=False, key_filename=None,
                 local_host='localhost', local_port=22022, remote_host='localhost', remote_port=22,
                 known_hosts=None, add_to_known_hosts=False, pkey=None, look_for_keys=False, allow_agent=False,
                 sshproxy_host=None, sshproxy_port=22, sshproxy_user=None,
                 sshproxy_password=None, sshproxy_force_password_auth=False, sshproxy_key_filename=None, sshproxy_pkey=None,
                 sshproxy_look_for_keys=False, sshproxy_allow_agent=False,
                 sshproxy_tunnel=None,
                 ssh_rootdir=os.path.join(_LOCAL_HOME, _X2GO_SSH_ROOTDIR), 
                 session_instance=None,
                 logger=None, loglevel=log.loglevel_DEFAULT, ):
        """\
        Initialize an X2GoSSHProxy instance. Use an instance of this class to tunnel X2Go requests through
        a proxying SSH server (i.e. to subLANs that are separated by firewalls or to private IP subLANs that
        are NATted behind routers).

        @param username: login user name to be used on the SSH proxy host
        @type username: C{str}
        @param password: user's password on the SSH proxy host, with private key authentication it will be
            used to unlock the key (if needed)
        @type password: C{str}
        @param key_filename: name of a SSH private key file
        @type key_filename: C{str}
        @param pkey: a private DSA/RSA key object (as provided by Paramiko/SSH)
        @type pkey: C{RSA/DSA key instance}
        @param force_password_auth: enforce password authentication even if a key(file) is present
        @type force_password_auth: C{bool}
        @param look_for_keys: look for key files with standard names and try those if any can be found
        @type look_for_keys: C{bool}
        @param allow_agent: try authentication via a locally available SSH agent
        @type allow_agent: C{bool}
        @param local_host: bind SSH tunnel to the C{local_host} IP socket address (default: localhost)
        @type local_host: C{str}
        @param local_port: IP socket port to bind the SSH tunnel to (default; 22022)
        @type local_port: C{int}
        @param remote_host: remote endpoint of the SSH proxying/forwarding tunnel (default: localhost)
        @type remote_host: C{str}
        @param remote_port: remote endpoint's IP socket port for listening SSH daemon (default: 22)
        @type remote_port: C{int}
        @param known_hosts: full path to a custom C{known_hosts} file
        @type known_hosts: C{str}
        @param add_to_known_hosts: automatically add host keys of unknown SSH hosts to the C{known_hosts} file
        @type add_to_known_hosts: C{bool}
        @param hostname: alias for C{local_host}
        @type hostname: C{str}
        @param port: alias for C{local_port}
        @type port: C{int}
        @param sshproxy_host: alias for C{hostname}
        @type sshproxy_host: C{str}
        @param sshproxy_port: alias for C{post}
        @type sshproxy_port: C{int}
        @param sshproxy_user: alias for C{username}
        @type sshproxy_user: C{str}
        @param sshproxy_password: alias for C{password}
        @type sshproxy_password: C{str}
        @param sshproxy_key_filename: alias for C{key_filename}
        @type sshproxy_key_filename: C{str}
        @param sshproxy_pkey: alias for C{pkey}
        @type sshproxy_pkey: C{RSA/DSA key instance} (Paramiko)
        @param sshproxy_force_password_auth: alias for C{force_password_auth}
        @type sshproxy_force_password_auth: C{bool}
        @param sshproxy_look_for_keys: alias for C{look_for_keys}
        @type sshproxy_look_for_keys: C{bool}
        @param sshproxy_allow_agent: alias for C{allow_agent}
        @type sshproxy_allow_agent: C{bool}

        @param sshproxy_tunnel: a string of the format <local_host>:<local_port>:<remote_host>:<remote_port> 
            which will override---if used---the options: C{local_host}, C{local_port}, C{remote_host} and C{remote_port}
        @type sshproxy_tunnel: C{str}

        @param ssh_rootdir: local user's SSH base directory (default: ~/.ssh)
        @type ssh_rootdir: C{str}
        @param session_instance: the L{X2GoSession} instance that builds up this SSH proxying tunnel
        @type session_instance: L{X2GoSession} instance
        @param logger: you can pass an L{X2GoLogger} object to the
            L{X2GoSSHProxy} constructor
        @type logger: L{X2GoLogger} instance
        @param loglevel: if no L{X2GoLogger} object has been supplied a new one will be
            constructed with the given loglevel
        @type loglevel: int

        @raise X2GoSSHProxyAuthenticationException: if the SSH proxy caused a C{paramiko.AuthenticationException}
        @raise X2GoSSHProxyException: if the SSH proxy caused a C{paramiko.SSHException}
        """
        if logger is None:
            self.logger = log.X2GoLogger(loglevel=loglevel)
        else:
            self.logger = copy.deepcopy(logger)
        self.logger.tag = __NAME__

        self.hostname, self.port, self.username = hostname, port, username

        if sshproxy_port: self.port = sshproxy_port

        # translate between X2GoSession options and paramiko.SSHCLient.connect() options
        # if <hostname>:<port> is used for sshproxy_host, then this <port> is used
        if sshproxy_host:
            if sshproxy_host.find(':'):
                self.hostname = sshproxy_host.split(':')[0]
                try: self.port = int(sshproxy_host.split(':')[1])
                except IndexError: pass
            else:
                self.hostname = sshproxy_host

        if sshproxy_user: self.username = sshproxy_user
        if sshproxy_password: password = sshproxy_password
        if sshproxy_force_password_auth: force_password_auth = sshproxy_force_password_auth
        if sshproxy_key_filename: key_filename = sshproxy_key_filename
        if sshproxy_pkey: pkey = sshproxy_pkey
        if sshproxy_look_for_keys: look_for_keys = sshproxy_look_for_keys
        if sshproxy_allow_agent: allow_agent = sshproxy_allow_agent
        if sshproxy_tunnel:
            self.local_host, self.local_port, self.remote_host, self.remote_port = sshproxy_tunnel.split(':')
            self.local_port = int(self.local_port)
            self.remote_port = int(self.remote_port)
        else:
            self.local_host = local_host
            self.local_port = int(local_port)
            self.remote_host = remote_host
            self.remote_port = int(remote_port)

        # allow more trailing whitespace tolerance in hostnames
        self.hostname = self.hostname.strip()
        self.local_host = self.local_host.strip()
        self.remote_host = self.remote_host.strip()

        # enforce IPv4 for localhost addresses!!!
        _hostname = self.hostname
        if _hostname in ('localhost', 'localhost.localdomain'):
            _hostname = '127.0.0.1'
        if self.local_host in ('localhost', 'localhost.localdomain'):
            self.local_host = '127.0.0.1'
        if self.remote_host in ('localhost', 'localhost.localdomain'):
            self.remote_host = '127.0.0.1'

        if username is None:
            username = _CURRENT_LOCAL_USER

        self._keepalive = True
        self.session_instance = session_instance

        self.client_instance = None
        if self.session_instance is not None:
            self.client_instance = self.session_instance.get_client_instance()

        self.ssh_rootdir = ssh_rootdir
        paramiko.SSHClient.__init__(self)

        self.known_hosts = known_hosts
        if self.known_hosts:
            utils.touch_file(self.known_hosts)
            self.load_host_keys(self.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())

        try:
            if key_filename or pkey or look_for_keys or allow_agent or (password and force_password_auth):
                try:
                    if password and force_password_auth:
                        self.connect(_hostname, port=self.port,
                                     username=self.username,
                                     password=password,
                                     key_filename=None,
                                     pkey=None,
                                     look_for_keys=False,
                                     allow_agent=False,
                                    )
                    elif (key_filename and os.path.exists(os.path.normpath(key_filename))) or pkey:
                        self.connect(_hostname, port=self.port,
                                     username=self.username, 
                                     key_filename=key_filename,
                                     pkey=pkey,
                                     look_for_keys=look_for_keys,
                                     allow_agent=allow_agent,
                                    )
                    else:
                        self.connect(_hostname, port=self.port,
                                     username=self.username, 
                                     look_for_keys=look_for_keys,
                                     allow_agent=allow_agent,
                                    )

                except x2go_exceptions.AuthenticationException, e:
                    self.close()
                    raise x2go_exceptions.X2GoSSHProxyAuthenticationException('pubkey auth mechanisms both failed')
                except x2go_exceptions.SSHException, e:
                    self.close()
                    raise x2go_exceptions.X2GoSSHProxyAuthenticationException('interactive authentication required')
                except:
                    raise

                # since Paramiko 1.7.7.1 there is compression available, let's use it if present...
                t = self.get_transport()
                if x2go._paramiko.PARAMIKO_FEATURE['use-compression']:
                    t.use_compression(compress=True)

            # if there is no 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(_hostname, port=self.port,
                                 username=self.username,
                                 password=password,
                                 look_for_keys=False,
                                 allow_agent=False,
                                )
                except x2go_exceptions.AuthenticationException:
                    self.close()
                    raise x2go_exceptions.X2GoSSHProxyAuthenticationException('interactive auth mechanisms failed')
                except:
                    self.close()
                    raise

        except (x2go_exceptions.SSHException, IOError), e:
            self.close()
            raise x2go_exceptions.X2GoSSHProxyException(str(e))
        except:
            self.close()
            raise

        self.set_missing_host_key_policy(paramiko.RejectPolicy())
        threading.Thread.__init__(self)
        self.daemon = True

    def check_host(self):
        """\
        Wraps around a Paramiko/SSH host key check.

        """
        _hostname = self.hostname

        # force into IPv4 for localhost connections
        if _hostname in ('localhost', 'localhost.localdomain'):
            _hostname = '127.0.0.1'

        _valid = False
        (_valid, _hostname, _port, _fingerprint, _fingerprint_type) = checkhosts.check_ssh_host_key(self, _hostname, port=self.port)
        if not _valid and self.session_instance:
            _valid = self.session_instance.HOOK_check_host_dialog(self.remote_host, self.remote_port, fingerprint=_fingerprint, fingerprint_type=_fingerprint_type)
        return _valid

    def run(self):
        """\
        Start the SSH proxying tunnel...

        @raise X2GoSSHProxyException: if the SSH proxy could not retrieve an SSH transport for proxying a X2Go server-client connection

        """
        if self.get_transport() is not None and self.get_transport().is_authenticated():
            self.local_port = utils.detect_unused_port(bind_address=self.local_host, preferred_port=self.local_port)
            if self.client_instance is not None:
                _profile_id = self.session_instance.get_profile_id()
                if self.client_instance.session_profiles.has_profile(_profile_id):
                    self.client_instance.session_profiles.update_value(_profile_id,
                                                                       'sshproxytunnel',
                                                                       '%s:%s:%s:%s' % (self.local_host, self.local_port, self.remote_host, self.remote_port)
                                                                      )
                self.client_instance.session_profiles.write_user_config = True
                self.client_instance.session_profiles.write()
            self.fw_tunnel = forward.start_forward_tunnel(local_host=self.local_host,
                                                          local_port=self.local_port,
                                                          remote_host=self.remote_host,
                                                          remote_port=self.remote_port,
                                                          ssh_transport=self.get_transport(),
                                                          logger=self.logger, )
            self.logger('SSH proxy tunnel via [%s]:%s has been set up' % (self.hostname, self.port), loglevel=log.loglevel_NOTICE)
            self.logger('SSH proxy tunnel startpoint is [%s]:%s, endpoint is [%s]:%s' % (self.local_host, self.local_port, self.remote_host, self.remote_port), loglevel=log.loglevel_NOTICE)

            while self._keepalive:
                gevent.sleep(.1)

        else:
            raise x2go_exceptions.X2GoSSHProxyException('SSH proxy connection could not retrieve an SSH transport')

    def get_local_proxy_host(self):
        """\
        Retrieve the local IP socket address this SSH proxying tunnel is (about to) bind/bound to.

        @return: local IP socket address
        @rtype: C{str}

        """
        return self.local_host

    def get_local_proxy_port(self):
        """\
        Retrieve the local IP socket port this SSH proxying tunnel is (about to) bind/bound to.

        @return: local IP socket port
        @rtype: C{int}

        """
        return self.local_port

    def get_remote_host(self):
        """\
        Retrieve the remote IP socket address at the remote end of the SSH proxying tunnel.

        @return: remote IP socket address
        @rtype: C{str}

        """
        return self.remote_host

    def get_remote_port(self):
        """\
        Retrieve the remote IP socket port of the target system's SSH daemon.

        @return: remote SSH port
        @rtype: C{int}

        """
        return self.remote_port

    def stop_thread(self):
        """\
        Tear down the SSH proxying tunnel.

        """
        if self.fw_tunnel is not None and self.fw_tunnel.is_active:
            self.logger('taking down SSH proxy tunnel via [%s]:%s' % (self.hostname, self.port), loglevel=log.loglevel_NOTICE)
        try: forward.stop_forward_tunnel(self.fw_tunnel)
        except: pass
        self.fw_tunnel = None
        self._keepalive = False
        if self.get_transport() is not None:
            self.logger('closing SSH proxy connection to [%s]:%s' % (self.hostname, self.port), loglevel=log.loglevel_NOTICE)
        self.close()
        self.password = self.sshproxy_password = None

    def __del__(self):
        """\
        Class desctructor.

        """
        self.stop_thread()