summaryrefslogtreecommitdiff
path: root/src/leap/eip/openvpnconnection.py
diff options
context:
space:
mode:
Diffstat (limited to 'src/leap/eip/openvpnconnection.py')
-rw-r--r--src/leap/eip/openvpnconnection.py410
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 bee8c010..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