diff options
Diffstat (limited to 'src/leap/eip/openvpnconnection.py')
| -rw-r--r-- | src/leap/eip/openvpnconnection.py | 410 | 
1 files changed, 0 insertions, 410 deletions
| diff --git a/src/leap/eip/openvpnconnection.py b/src/leap/eip/openvpnconnection.py deleted file mode 100644 index 455735c8..00000000 --- a/src/leap/eip/openvpnconnection.py +++ /dev/null @@ -1,410 +0,0 @@ -""" -OpenVPN Connection -""" -from __future__ import (print_function) -from functools import partial -import logging -import os -import psutil -import shutil -import select -import socket -from time import sleep - -logger = logging.getLogger(name=__name__) - -from leap.base.connection import Connection -from leap.base.constants import OPENVPN_BIN -from leap.util.coroutines import spawn_and_watch_process -from leap.util.misc import get_openvpn_pids - -from leap.eip.udstelnet import UDSTelnet -from leap.eip import config as eip_config -from leap.eip import exceptions as eip_exceptions - - -class OpenVPNManagement(object): - -    # TODO explain a little bit how management interface works -    # and our telnet interface with support for unix sockets. - -    """ -    for more information, read openvpn management notes. -    zcat `dpkg -L openvpn | grep management` -    """ - -    def _connect_to_management(self): -        """ -        Connect to openvpn management interface -        """ -        if hasattr(self, 'tn'): -            self._close_management_socket() -        self.tn = UDSTelnet(self.host, self.port) - -        # XXX make password optional -        # specially for win. we should generate -        # the pass on the fly when invoking manager -        # from conductor - -        #self.tn.read_until('ENTER PASSWORD:', 2) -        #self.tn.write(self.password + '\n') -        #self.tn.read_until('SUCCESS:', 2) -        if self.tn: -            self._seek_to_eof() -        return True - -    def _close_management_socket(self, announce=True): -        """ -        Close connection to openvpn management interface -        """ -        logger.debug('closing socket') -        if announce: -            self.tn.write("quit\n") -            self.tn.read_all() -        self.tn.get_socket().close() -        del self.tn - -    def _seek_to_eof(self): -        """ -        Read as much as available. Position seek pointer to end of stream -        """ -        try: -            b = self.tn.read_eager() -        except EOFError: -            logger.debug("Could not read from socket. Assuming it died.") -            return -        while b: -            try: -                b = self.tn.read_eager() -            except EOFError: -                logger.debug("Could not read from socket. Assuming it died.") - -    def _send_command(self, cmd): -        """ -        Send a command to openvpn and return response as list -        """ -        if not self.connected(): -            try: -                self._connect_to_management() -            except eip_exceptions.MissingSocketError: -                #logger.warning('missing management socket') -                return [] -        try: -            if hasattr(self, 'tn'): -                self.tn.write(cmd + "\n") -        except socket.error: -            logger.error('socket error') -            self._close_management_socket(announce=False) -            return [] -        try: -            buf = self.tn.read_until(b"END", 2) -            self._seek_to_eof() -            blist = buf.split('\r\n') -            if blist[-1].startswith('END'): -                del blist[-1] -                return blist -            else: -                return [] -        except socket.error as exc: -            logger.debug('socket error: %s' % exc.message) -        except select.error as exc: -            logger.debug('select error: %s' % exc.message) - -    def _send_short_command(self, cmd): -        """ -        parse output from commands that are -        delimited by "success" instead -        """ -        if not self.connected(): -            self.connect() -        self.tn.write(cmd + "\n") -        # XXX not working? -        buf = self.tn.read_until(b"SUCCESS", 2) -        self._seek_to_eof() -        blist = buf.split('\r\n') -        return blist - -    # -    # random maybe useful vpn commands -    # - -    def pid(self): -        #XXX broken -        return self._send_short_command("pid") - - -class OpenVPNConnection(Connection, OpenVPNManagement): -    """ -    All related to invocation -    of the openvpn binary. -    It's extended by EIPConnection. -    """ - -    # XXX Inheriting from Connection was an early design idea -    # but currently that's an empty class. -    # We can get rid of that if we don't use it for sharing -    # state with other leap modules. - -    def __init__(self, -                 watcher_cb=None, -                 debug=False, -                 host=None, -                 port="unix", -                 password=None, -                 *args, **kwargs): -        """ -        :param watcher_cb: callback to be \ -called for each line in watched stdout -        :param signal_map: dictionary of signal names and callables \ -to be triggered for each one of them. -        :type watcher_cb: function -        :type signal_map: dict -        """ -        #XXX FIXME -        #change watcher_cb to line_observer -        # XXX if not host: raise ImproperlyConfigured - -        logger.debug('init openvpn connection') -        self.debug = debug -        self.ovpn_verbosity = kwargs.get('ovpn_verbosity', None) - -        self.watcher_cb = watcher_cb -        #self.signal_maps = signal_maps - -        self.subp = None -        self.watcher = None - -        self.server = None -        self.port = None -        self.proto = None - -        self.command = None -        self.args = None - -        # XXX get autostart from config -        self.autostart = True - -        # management interface init -        self.host = host -        if isinstance(port, str) and port.isdigit(): -            port = int(port) -        elif port == "unix": -            port = "unix" -        else: -            port = None -        self.port = port -        self.password = password - -    def run_openvpn_checks(self): -        """ -        runs check needed before launching -        openvpn subprocess. will raise if errors found. -        """ -        logger.debug('running openvpn checks') -        # XXX I think that "check_if_running" should be called -        # from try openvpn connection instead. -- kali. -        # let's prepare tests for that before changing it... -        self._check_if_running_instance() -        self._set_ovpn_command() -        self._check_vpn_keys() - -    def try_openvpn_connection(self): -        """ -        attempts to connect -        """ -        # XXX should make public method -        if self.command is None: -            raise eip_exceptions.EIPNoCommandError -        if self.subp is not None: -            logger.debug('cowardly refusing to launch subprocess again') -            # XXX this is not returning ???!! -            # FIXME -- so it's calling it all the same!! - -        self._launch_openvpn() - -    def connected(self): -        """ -        Returns True if connected -        rtype: bool -        """ -        # XXX make a property -        return hasattr(self, 'tn') - -    def terminate_openvpn_connection(self, shutdown=False): -        """ -        terminates openvpn child subprocess -        """ -        if self.subp: -            try: -                self._stop_openvpn() -            except eip_exceptions.ConnectionRefusedError: -                logger.warning( -                    'unable to send sigterm signal to openvpn: ' -                    'connection refused.') - -            # XXX kali -- -            # XXX review-me -            # I think this will block if child process -            # does not return. -            # Maybe we can .poll() for a given -            # interval and exit in any case. - -            RETCODE = self.subp.wait() -            if RETCODE: -                logger.error( -                    'cannot terminate subprocess! Retcode %s' -                    '(We might have left openvpn running)' % RETCODE) - -        if shutdown: -            self._cleanup_tempfiles() - -    def _cleanup_tempfiles(self): -        """ -        remove all temporal files -        we might have left behind -        """ -        # if self.port is 'unix', we have -        # created a temporal socket path that, under -        # normal circumstances, we should be able to -        # delete - -        if self.port == "unix": -            logger.debug('cleaning socket file temp folder') - -            tempfolder = os.path.split(self.host)[0] -            if os.path.isdir(tempfolder): -                try: -                    shutil.rmtree(tempfolder) -                except OSError: -                    logger.error('could not delete tmpfolder %s' % tempfolder) - -    # checks - -    def _check_if_running_instance(self): -        """ -        check if openvpn is already running -        """ -        openvpn_pids = get_openvpn_pids() -        if openvpn_pids: -            logger.debug('an openvpn instance is already running.') -            logger.debug('attempting to stop openvpn instance.') -            if not self._stop_openvpn(): -                raise eip_exceptions.OpenVPNAlreadyRunning -            return -        else: -            logger.debug('no openvpn instance found.') - -    def _set_ovpn_command(self): -        try: -            command, args = eip_config.build_ovpn_command( -                provider=self.provider, -                debug=self.debug, -                socket_path=self.host, -                ovpn_verbosity=self.ovpn_verbosity) -        except eip_exceptions.EIPNoPolkitAuthAgentAvailable: -            command = args = None -            raise -        except eip_exceptions.EIPNoPkexecAvailable: -            command = args = None -            raise - -        # XXX if not command, signal error. -        self.command = command -        self.args = args - -    def _check_vpn_keys(self): -        """ -        checks for correct permissions on vpn keys -        """ -        try: -            eip_config.check_vpn_keys(provider=self.provider) -        except eip_exceptions.EIPInitBadKeyFilePermError: -            logger.error('Bad VPN Keys permission!') -            # do nothing now -        # and raise the rest ... - -    # starting and stopping openvpn subprocess - -    def _launch_openvpn(self): -        """ -        invocation of openvpn binaries in a subprocess. -        """ -        #XXX TODO: -        #deprecate watcher_cb, -        #use _only_ signal_maps instead - -        #logger.debug('_launch_openvpn called') -        if self.watcher_cb is not None: -            linewrite_callback = self.watcher_cb -        else: -            #XXX get logger instead -            linewrite_callback = lambda line: logger.debug( -                    'watcher: %s' % line) - -        # the partial is not -        # being applied now because we're not observing the process -        # stdout like we did in the early stages. but I leave it -        # here since it will be handy for observing patterns in the -        # thru-the-manager updates (with regex) -        observers = (linewrite_callback, -                     partial(lambda con_status, -                             line: linewrite_callback, self.status)) -        subp, watcher = spawn_and_watch_process( -            self.command, -            self.args, -            observers=observers) -        self.subp = subp -        self.watcher = watcher - -    def _stop_openvpn(self): -        """ -        stop openvpn process -        by sending SIGTERM to the management -        interface -        """ -        # XXX method a bit too long, split -        logger.debug("atempting to terminate openvpn process...") -        if self.connected(): -            try: -                self._send_command("signal SIGTERM\n") -                sleep(1) -                if not self.subp:  # XXX ??? -                    return True -            except socket.error: -                logger.warning('management socket died') -                return - -        #shutting openvpn failured -        #try patching in old openvpn host and trying again -        # XXX could be more than one! -        process = self._get_openvpn_process() -        if process: -            logger.debug('process: %s' % process.name) -            cmdline = process.cmdline - -            manag_flag = "--management" -            if isinstance(cmdline, list) and manag_flag in cmdline: -                _index = cmdline.index(manag_flag) -                self.host = cmdline[_index + 1] -                self._send_command("signal SIGTERM\n") - -            #make sure the process was terminated -            process = self._get_openvpn_process() -            if not process: -                logger.debug("Existing OpenVPN Process Terminated") -                return True -            else: -                logger.error("Unable to terminate existing OpenVPN Process.") -                return False - -        return True - -    def _get_openvpn_process(self): -        for process in psutil.process_iter(): -            if OPENVPN_BIN in process.name: -                return process -        return None - -    def get_log(self, lines=1): -        log = self._send_command("log %s" % lines) -        return log | 
