summaryrefslogtreecommitdiff
path: root/src/leap/bitmask/vpn/launchers/linux.py
diff options
context:
space:
mode:
Diffstat (limited to 'src/leap/bitmask/vpn/launchers/linux.py')
-rw-r--r--src/leap/bitmask/vpn/launchers/linux.py120
1 files changed, 117 insertions, 3 deletions
diff --git a/src/leap/bitmask/vpn/launchers/linux.py b/src/leap/bitmask/vpn/launchers/linux.py
index 2edfb5ea..38b9e417 100644
--- a/src/leap/bitmask/vpn/launchers/linux.py
+++ b/src/leap/bitmask/vpn/launchers/linux.py
@@ -19,18 +19,55 @@
Linux VPN launcher implementation.
"""
-import commands
import os
+import psutil
+
+from twisted.internet import defer, reactor
+from twisted.internet.endpoints import clientFromString, connectProtocol
from twisted.logger import Logger
from leap.bitmask.util import STANDALONE
from leap.bitmask.vpn.utils import first, force_eval
from leap.bitmask.vpn.privilege import LinuxPolicyChecker
+from leap.bitmask.vpn.management import ManagementProtocol
from leap.bitmask.vpn.launcher import VPNLauncher
-logger = Logger()
-COM = commands
+log = Logger()
+
+
+class OpenVPNAlreadyRunning(Exception):
+ message = ("Another openvpn instance is already running, and could "
+ "not be stopped.")
+
+
+class AlienOpenVPNAlreadyRunning(Exception):
+ message = ("Another openvpn instance is already running, and could "
+ "not be stopped because it was not launched by LEAP.")
+
+
+def _maybe_get_running_openvpn():
+ """
+ Looks for previously running openvpn instances.
+
+ :rtype: psutil Process
+ """
+ openvpn = None
+ for p in psutil.process_iter():
+ try:
+ # This needs more work, see #3268, but for the moment
+ # we need to be able to filter out arguments in the form
+ # --openvpn-foo, since otherwise we are shooting ourselves
+ # in the feet.
+
+ cmdline = p.cmdline()
+ if any(map(lambda s: s.find(
+ "LEAPOPENVPN") != -1, cmdline)):
+ openvpn = p
+ break
+ except psutil.AccessDenied:
+ pass
+ return openvpn
class LinuxVPNLauncher(VPNLauncher):
@@ -106,3 +143,80 @@ class LinuxVPNLauncher(VPNLauncher):
command.insert(0, first(pkexec))
return command
+
+ def kill_previous_openvpn(kls):
+ """
+ Checks if VPN is already running and tries to stop it.
+
+ Might raise OpenVPNAlreadyRunning.
+
+ :return: a deferred, that fires with True if stopped.
+ """
+ @defer.inlineCallbacks
+ def gotProtocol(proto):
+ return proto.signal('SIGTERM')
+
+ def connect_to_management(path):
+ # XXX this has a problem with connections to different
+ # remotes. So the reconnection will only work when we are
+ # terminating instances left running for the same provider.
+ # If we are killing an openvpn instance configured for another
+ # provider, we will get:
+ # TLS Error: local/remote TLS keys are out of sync
+ # However, that should be a rare case right now.
+ endpoint = clientFromString(reactor, path)
+ d = connectProtocol(endpoint, ManagementProtocol(verbose=False))
+ d.addCallback(gotProtocol)
+ return d
+
+ def verify_termination(ignored):
+ openvpn = _maybe_get_running_openvpn()
+ if openvpn is None:
+ log.debug('Successfully finished already running '
+ 'openvpn process.')
+ return True
+ else:
+ log.warn('Unable to terminate OpenVPN')
+ raise OpenVPNAlreadyRunning
+
+ openvpn = _maybe_get_running_openvpn()
+ if not openvpn:
+ log.debug('Could not find openvpn process while '
+ 'trying to stop it.')
+ return False
+
+ log.debug('OpenVPN is already running, trying to stop it...')
+ cmdline = openvpn.cmdline
+ management = "--management"
+
+ if isinstance(cmdline, list) and management in cmdline:
+ # we know that our invocation has this distinctive fragment, so
+ # we use this fingerprint to tell other invocations apart.
+ # this might break if we change the configuration path in the
+ # launchers
+
+ def smellslikeleap(s):
+ return "leap" in s and "providers" in s
+
+ if not any(map(smellslikeleap, cmdline)):
+ log.debug("We cannot stop this instance since we do not "
+ "recognise it as a leap invocation.")
+ raise AlienOpenVPNAlreadyRunning
+
+ try:
+ index = cmdline.index(management)
+ host = cmdline[index + 1]
+ port = cmdline[index + 2]
+ log.debug("Trying to connect to %s:%s"
+ % (host, port))
+
+ if port == 'unix':
+ path = b"unix:path=%s" % host
+ d = connect_to_management(path)
+ d.addCallback(verify_termination)
+ return d
+
+ except (Exception, AssertionError):
+ log.failure('Problem trying to terminate OpenVPN')
+ else:
+ log.debug('Could not find the expected openvpn command line.')