summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/leap/bitmask/gui/mainwindow.py31
-rw-r--r--src/leap/bitmask/gui/statemachines.py16
-rw-r--r--src/leap/bitmask/gui/wizard.py7
-rw-r--r--src/leap/bitmask/platform_init/initializers.py10
-rw-r--r--src/leap/bitmask/provider/providerbootstrapper.py (renamed from src/leap/bitmask/services/eip/providerbootstrapper.py)68
-rw-r--r--src/leap/bitmask/provider/tests/__init__.py0
-rw-r--r--src/leap/bitmask/provider/tests/test_providerbootstrapper.py (renamed from src/leap/bitmask/services/eip/tests/test_providerbootstrapper.py)12
-rw-r--r--src/leap/bitmask/services/connections.py1
-rw-r--r--src/leap/bitmask/services/eip/__init__.py27
-rw-r--r--src/leap/bitmask/services/eip/connection.py1
-rw-r--r--src/leap/bitmask/services/eip/darwinvpnlauncher.py190
-rw-r--r--src/leap/bitmask/services/eip/linuxvpnlauncher.py232
-rw-r--r--src/leap/bitmask/services/eip/vpnlauncher.py290
-rw-r--r--src/leap/bitmask/services/eip/vpnlaunchers.py963
-rw-r--r--src/leap/bitmask/services/eip/vpnprocess.py4
-rw-r--r--src/leap/bitmask/services/eip/windowsvpnlauncher.py69
-rw-r--r--src/leap/bitmask/util/constants.py2
17 files changed, 902 insertions, 1021 deletions
diff --git a/src/leap/bitmask/gui/mainwindow.py b/src/leap/bitmask/gui/mainwindow.py
index 947ce58c..92d6906e 100644
--- a/src/leap/bitmask/gui/mainwindow.py
+++ b/src/leap/bitmask/gui/mainwindow.py
@@ -36,9 +36,9 @@ from leap.bitmask.gui import statemachines
from leap.bitmask.gui.statuspanel import StatusPanelWidget
from leap.bitmask.gui.wizard import Wizard
+from leap.bitmask.provider.providerbootstrapper import ProviderBootstrapper
from leap.bitmask.services.eip.eipbootstrapper import EIPBootstrapper
from leap.bitmask.services.eip.eipconfig import EIPConfig
-from leap.bitmask.services.eip.providerbootstrapper import ProviderBootstrapper
# XXX: Soledad might not work out of the box in Windows, issue #2932
from leap.bitmask.services.soledad.soledadbootstrapper import \
SoledadBootstrapper
@@ -53,12 +53,12 @@ from leap.bitmask.services.eip.vpnprocess import VPN
from leap.bitmask.services.eip.vpnprocess import OpenVPNAlreadyRunning
from leap.bitmask.services.eip.vpnprocess import AlienOpenVPNAlreadyRunning
-from leap.bitmask.services.eip.vpnlaunchers import VPNLauncherException
-from leap.bitmask.services.eip.vpnlaunchers import OpenVPNNotFoundException
-from leap.bitmask.services.eip.vpnlaunchers import EIPNoPkexecAvailable
-from leap.bitmask.services.eip.vpnlaunchers import \
+from leap.bitmask.services.eip.vpnlauncher import VPNLauncherException
+from leap.bitmask.services.eip.vpnlauncher import OpenVPNNotFoundException
+from leap.bitmask.services.eip.linuxvpnlauncher import EIPNoPkexecAvailable
+from leap.bitmask.services.eip.linuxvpnlauncher import \
EIPNoPolkitAuthAgentAvailable
-from leap.bitmask.services.eip.vpnlaunchers import EIPNoTunKextLoaded
+from leap.bitmask.services.eip.darwinvpnlauncher import EIPNoTunKextLoaded
from leap.bitmask.util.keyring_helpers import has_keyring
from leap.bitmask.util.leap_log_handler import LeapLogHandler
@@ -1535,7 +1535,9 @@ class MainWindow(QtGui.QMainWindow):
# TODO we should have a way of parsing the latest lines in the vpn
# log buffer so we can have a more precise idea of which type
# of error did we have (server side, local problem, etc)
- abnormal = True
+
+ qtsigs = self._eip_connection.qtsigs
+ signal = qtsigs.disconnected_signal
# XXX check if these exitCodes are pkexec/cocoasudo specific
if exitCode in (126, 127):
@@ -1544,28 +1546,25 @@ class MainWindow(QtGui.QMainWindow):
"because you did not authenticate properly."),
error=True)
self._vpn.killit()
+ signal = qtsigs.connection_aborted_signal
+
elif exitCode != 0 or not self.user_stopped_eip:
self._status_panel.set_global_status(
self.tr("Encrypted Internet finished in an "
"unexpected manner!"), error=True)
- else:
- abnormal = False
+ signal = qtsigs.connection_died_signal
+
if exitCode == 0 and IS_MAC:
# XXX remove this warning after I fix cocoasudo.
logger.warning("The above exit code MIGHT BE WRONG.")
- # We emit signals to trigger transitions in the state machine:
- qtsigs = self._eip_connection.qtsigs
- if abnormal:
- signal = qtsigs.connection_died_signal
- else:
- signal = qtsigs.disconnected_signal
-
# XXX verify that the logic kees the same w/o the abnormal flag
# after the refactor to EIPConnection has been completed
# (eipconductor taking the most of the logic under transitions
# that right now are handled under status_panel)
#self._stop_eip(abnormal)
+
+ # We emit signals to trigger transitions in the state machine:
signal.emit()
def _on_raise_window_event(self, req):
diff --git a/src/leap/bitmask/gui/statemachines.py b/src/leap/bitmask/gui/statemachines.py
index c3dd5ed3..94726720 100644
--- a/src/leap/bitmask/gui/statemachines.py
+++ b/src/leap/bitmask/gui/statemachines.py
@@ -128,11 +128,25 @@ class ConnectionMachineBuilder(object):
states[_OFF])
# * If we receive the connection_died, we transition
- # to the off state
+ # from on directly to the off state
states[_ON].addTransition(
conn.qtsigs.connection_died_signal,
states[_OFF])
+ # * If we receive the connection_aborted, we transition
+ # from connecting to the off state
+ states[_CON].addTransition(
+ conn.qtsigs.connection_aborted_signal,
+ states[_OFF])
+ # * Connection died can in some cases also be
+ # triggered while we are in CONNECTING
+ # state. I should be avoided, since connection_aborted
+ # is clearer (and reserve connection_died
+ # for transitions from on->off
+ states[_CON].addTransition(
+ conn.qtsigs.connection_died_signal,
+ states[_OFF])
+
# adding states to the machine
for state in states.itervalues():
machine.addState(state)
diff --git a/src/leap/bitmask/gui/wizard.py b/src/leap/bitmask/gui/wizard.py
index 45734b81..bb38b136 100644
--- a/src/leap/bitmask/gui/wizard.py
+++ b/src/leap/bitmask/gui/wizard.py
@@ -14,7 +14,6 @@
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
-
"""
First run wizard
"""
@@ -27,15 +26,13 @@ from functools import partial
from PySide import QtCore, QtGui
from twisted.internet import threads
-from leap.bitmask.config import flags
from leap.bitmask.config.providerconfig import ProviderConfig
from leap.bitmask.crypto.srpregister import SRPRegister
-from leap.bitmask.util.privilege_policies import is_missing_policy_permissions
+from leap.bitmask.provider.providerbootstrapper import ProviderBootstrapper
+from leap.bitmask.services import get_service_display_name, get_supported
from leap.bitmask.util.request_helpers import get_content
from leap.bitmask.util.keyring_helpers import has_keyring
from leap.bitmask.util.password import basic_password_checks
-from leap.bitmask.services.eip.providerbootstrapper import ProviderBootstrapper
-from leap.bitmask.services import get_service_display_name, get_supported
from ui_wizard import Ui_Wizard
diff --git a/src/leap/bitmask/platform_init/initializers.py b/src/leap/bitmask/platform_init/initializers.py
index 831c6a1c..d93efbc6 100644
--- a/src/leap/bitmask/platform_init/initializers.py
+++ b/src/leap/bitmask/platform_init/initializers.py
@@ -29,7 +29,9 @@ import tempfile
from PySide import QtGui
from leap.bitmask.config.leapsettings import LeapSettings
-from leap.bitmask.services.eip import vpnlaunchers
+from leap.bitmask.services.eip import get_vpn_launcher
+from leap.bitmask.services.eip.linuxvpnlauncher import LinuxVPNLauncher
+from leap.bitmask.services.eip.darwinvpnlauncher import DarwinVPNLauncher
from leap.bitmask.util import first
from leap.bitmask.util import privilege_policies
@@ -106,7 +108,7 @@ def check_missing():
config = LeapSettings()
alert_missing = config.get_alert_missing_scripts()
- launcher = vpnlaunchers.get_platform_launcher()
+ launcher = get_vpn_launcher()
missing_scripts = launcher.missing_updown_scripts
missing_other = launcher.missing_other_files
@@ -251,7 +253,7 @@ def _darwin_install_missing_scripts(badexec, notfound):
"..",
"Resources",
"openvpn")
- launcher = vpnlaunchers.DarwinVPNLauncher
+ launcher = DarwinVPNLauncher
if os.path.isdir(installer_path):
fd, tempscript = tempfile.mkstemp(prefix="leap_installer-")
@@ -356,7 +358,7 @@ def _linux_install_missing_scripts(badexec, notfound):
"""
success = False
installer_path = os.path.join(os.getcwd(), "apps", "eip", "files")
- launcher = vpnlaunchers.LinuxVPNLauncher
+ launcher = LinuxVPNLauncher
# XXX refactor with darwin, same block.
diff --git a/src/leap/bitmask/services/eip/providerbootstrapper.py b/src/leap/bitmask/provider/providerbootstrapper.py
index 3b7c9899..751da828 100644
--- a/src/leap/bitmask/services/eip/providerbootstrapper.py
+++ b/src/leap/bitmask/provider/providerbootstrapper.py
@@ -14,7 +14,6 @@
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
-
"""
Provider bootstrapping
"""
@@ -32,11 +31,11 @@ from leap.bitmask.util import get_path_prefix
from leap.bitmask.util.constants import REQUEST_TIMEOUT
from leap.bitmask.services.abstractbootstrapper import AbstractBootstrapper
from leap.bitmask.provider.supportedapis import SupportedAPIs
+from leap.common import ca_bundle
from leap.common.certs import get_digest
from leap.common.files import check_and_fix_urw_only, get_mtime, mkdir_p
from leap.common.check import leap_assert, leap_assert_type, leap_check
-
logger = logging.getLogger(__name__)
@@ -85,16 +84,32 @@ class ProviderBootstrapper(AbstractBootstrapper):
self._provider_config = None
self._download_if_needed = False
+ @property
+ def verify(self):
+ """
+ Verify parameter for requests.
+
+ :returns: either False, if checks are skipped, or the
+ path to the ca bundle.
+ :rtype: bool or str
+ """
+ if self._bypass_checks:
+ verify = False
+ else:
+ verify = ca_bundle.where()
+ return verify
+
def _check_name_resolution(self):
"""
Checks that the name resolution for the provider name works
"""
leap_assert(self._domain, "Cannot check DNS without a domain")
-
logger.debug("Checking name resolution for %s" % (self._domain))
# We don't skip this check, since it's basic for the whole
# system to work
+ # err --- but we can do it after a failure, to diagnose what went
+ # wrong. Right now we're just adding connection overhead. -- kali
socket.gethostbyname(self._domain)
def _check_https(self, *args):
@@ -102,24 +117,29 @@ class ProviderBootstrapper(AbstractBootstrapper):
Checks that https is working and that the provided certificate
checks out
"""
-
leap_assert(self._domain, "Cannot check HTTPS without a domain")
-
logger.debug("Checking https for %s" % (self._domain))
# We don't skip this check, since it's basic for the whole
- # system to work
+ # system to work.
+ # err --- but we can do it after a failure, to diagnose what went
+ # wrong. Right now we're just adding connection overhead. -- kali
try:
res = self._session.get("https://%s" % (self._domain,),
- verify=not self._bypass_checks,
+ verify=self.verify,
timeout=REQUEST_TIMEOUT)
res.raise_for_status()
- except requests.exceptions.SSLError:
+ except requests.exceptions.SSLError as exc:
+ logger.exception(exc)
self._err_msg = self.tr("Provider certificate could "
"not be verified")
raise
- except Exception:
+ except Exception as exc:
+ # XXX careful!. The error might be also a SSL handshake
+ # timeout error, in which case we should retry a couple of times
+ # more, for cases where the ssl server gives high latencies.
+ logger.exception(exc)
self._err_msg = self.tr("Provider does not support HTTPS")
raise
@@ -129,11 +149,13 @@ class ProviderBootstrapper(AbstractBootstrapper):
"""
leap_assert(self._domain,
"Cannot download provider info without a domain")
-
logger.debug("Downloading provider info for %s" % (self._domain))
- headers = {}
+ # --------------------------------------------------------------
+ # TODO factor out with the download routines in services.
+ # Watch out! We're handling the verify paramenter differently here.
+ headers = {}
provider_json = os.path.join(get_path_prefix(), "leap", "providers",
self._domain, "provider.json")
mtime = get_mtime(provider_json)
@@ -142,16 +164,18 @@ class ProviderBootstrapper(AbstractBootstrapper):
headers['if-modified-since'] = mtime
uri = "https://%s/%s" % (self._domain, "provider.json")
- verify = not self._bypass_checks
+ verify = self.verify
if mtime: # the provider.json exists
- provider_config = ProviderConfig()
- provider_config.load(provider_json)
+ # So, we're getting it from the api.* and checking against
+ # the provider ca.
try:
- verify = provider_config.get_ca_cert_path()
+ provider_config = ProviderConfig()
+ provider_config.load(provider_json)
uri = provider_config.get_api_uri() + '/provider.json'
+ verify = provider_config.get_ca_cert_path()
except MissingCACert:
- # get_ca_cert_path fails if the certificate does not exists.
+ # no ca? then download from main domain again.
pass
logger.debug("Requesting for provider.json... "
@@ -165,6 +189,9 @@ class ProviderBootstrapper(AbstractBootstrapper):
# Not modified
if res.status_code == 304:
logger.debug("Provider definition has not been modified")
+ # --------------------------------------------------------------
+ # end refactor, more or less...
+ # XXX Watch out, have to check the supported api yet.
else:
provider_definition, mtime = get_content(res)
@@ -181,8 +208,8 @@ class ProviderBootstrapper(AbstractBootstrapper):
else:
api_supported = ', '.join(SupportedAPIs.SUPPORTED_APIS)
error = ('Unsupported provider API version. '
- 'Supported versions are: {}. '
- 'Found: {}.').format(api_supported, api_version)
+ 'Supported versions are: {0}. '
+ 'Found: {1}.').format(api_supported, api_version)
logger.error(error)
raise UnsupportedProviderAPI(error)
@@ -230,7 +257,8 @@ class ProviderBootstrapper(AbstractBootstrapper):
"""
Downloads the CA cert that is going to be used for the api URL
"""
-
+ # XXX maybe we can skip this step if
+ # we have a fresh one.
leap_assert(self._provider_config, "Cannot download the ca cert "
"without a provider config!")
@@ -244,7 +272,7 @@ class ProviderBootstrapper(AbstractBootstrapper):
return
res = self._session.get(self._provider_config.get_ca_cert_uri(),
- verify=not self._bypass_checks,
+ verify=self.verify,
timeout=REQUEST_TIMEOUT)
res.raise_for_status()
diff --git a/src/leap/bitmask/provider/tests/__init__.py b/src/leap/bitmask/provider/tests/__init__.py
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/src/leap/bitmask/provider/tests/__init__.py
diff --git a/src/leap/bitmask/services/eip/tests/test_providerbootstrapper.py b/src/leap/bitmask/provider/tests/test_providerbootstrapper.py
index b0685676..9b47d60e 100644
--- a/src/leap/bitmask/services/eip/tests/test_providerbootstrapper.py
+++ b/src/leap/bitmask/provider/tests/test_providerbootstrapper.py
@@ -14,15 +14,12 @@
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
-
-
"""
Tests for the Provider Boostrapper checks
These will be whitebox tests since we want to make sure the private
implementation is checking what we expect.
"""
-
import os
import mock
import socket
@@ -39,13 +36,12 @@ from nose.twistedtools import deferred, reactor
from twisted.internet import threads
from requests.models import Response
-from leap.bitmask.services.eip.providerbootstrapper import ProviderBootstrapper
-from leap.bitmask.services.eip.providerbootstrapper import \
- UnsupportedProviderAPI
-from leap.bitmask.services.eip.providerbootstrapper import WrongFingerprint
-from leap.bitmask.provider.supportedapis import SupportedAPIs
from leap.bitmask.config.providerconfig import ProviderConfig
from leap.bitmask.crypto.tests import fake_provider
+from leap.bitmask.provider.providerbootstrapper import ProviderBootstrapper
+from leap.bitmask.provider.providerbootstrapper import UnsupportedProviderAPI
+from leap.bitmask.provider.providerbootstrapper import WrongFingerprint
+from leap.bitmask.provider.supportedapis import SupportedAPIs
from leap.common.files import mkdir_p
from leap.common.testing.https_server import where
from leap.common.testing.basetest import BaseLeapTest
diff --git a/src/leap/bitmask/services/connections.py b/src/leap/bitmask/services/connections.py
index f3ab9e8e..8aeb4e0c 100644
--- a/src/leap/bitmask/services/connections.py
+++ b/src/leap/bitmask/services/connections.py
@@ -103,6 +103,7 @@ class AbstractLEAPConnection(object):
# Bypass stages
connection_died_signal = None
+ connection_aborted_signal = None
class Disconnected(State):
"""Disconnected state"""
diff --git a/src/leap/bitmask/services/eip/__init__.py b/src/leap/bitmask/services/eip/__init__.py
index dd010027..6030cac3 100644
--- a/src/leap/bitmask/services/eip/__init__.py
+++ b/src/leap/bitmask/services/eip/__init__.py
@@ -20,7 +20,11 @@ leap.bitmask.services.eip module initialization
import os
import tempfile
-from leap.bitmask.platform_init import IS_WIN
+from leap.bitmask.services.eip.darwinvpnlauncher import DarwinVPNLauncher
+from leap.bitmask.services.eip.linuxvpnlauncher import LinuxVPNLauncher
+from leap.bitmask.services.eip.windowsvpnlauncher import WindowsVPNLauncher
+from leap.bitmask.platform_init import IS_LINUX, IS_MAC, IS_WIN
+from leap.common.check import leap_assert
def get_openvpn_management():
@@ -40,3 +44,24 @@ def get_openvpn_management():
port = "unix"
return host, port
+
+
+def get_vpn_launcher():
+ """
+ Return the VPN launcher for the current platform.
+ """
+ if not (IS_LINUX or IS_MAC or IS_WIN):
+ error_msg = "VPN Launcher not implemented for this platform."
+ raise NotImplementedError(error_msg)
+
+ launcher = None
+ if IS_LINUX:
+ launcher = LinuxVPNLauncher
+ elif IS_MAC:
+ launcher = DarwinVPNLauncher
+ elif IS_WIN:
+ launcher = WindowsVPNLauncher
+
+ leap_assert(launcher is not None)
+
+ return launcher()
diff --git a/src/leap/bitmask/services/eip/connection.py b/src/leap/bitmask/services/eip/connection.py
index 5f05ba07..08b29070 100644
--- a/src/leap/bitmask/services/eip/connection.py
+++ b/src/leap/bitmask/services/eip/connection.py
@@ -40,6 +40,7 @@ class EIPConnectionSignals(QtCore.QObject):
disconnected_signal = QtCore.Signal()
connection_died_signal = QtCore.Signal()
+ connection_aborted_signal = QtCore.Signal()
class EIPConnection(AbstractLEAPConnection):
diff --git a/src/leap/bitmask/services/eip/darwinvpnlauncher.py b/src/leap/bitmask/services/eip/darwinvpnlauncher.py
new file mode 100644
index 00000000..f3b6bfc8
--- /dev/null
+++ b/src/leap/bitmask/services/eip/darwinvpnlauncher.py
@@ -0,0 +1,190 @@
+# -*- coding: utf-8 -*-
+# darwinvpnlauncher.py
+# Copyright (C) 2013 LEAP
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program 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 General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+"""
+Darwin VPN launcher implementation.
+"""
+import commands
+import getpass
+import logging
+import os
+
+from leap.bitmask.services.eip.vpnlauncher import VPNLauncher
+from leap.bitmask.services.eip.vpnlauncher import VPNLauncherException
+from leap.bitmask.util import get_path_prefix
+
+logger = logging.getLogger(__name__)
+
+
+class EIPNoTunKextLoaded(VPNLauncherException):
+ pass
+
+
+class DarwinVPNLauncher(VPNLauncher):
+ """
+ VPN launcher for the Darwin Platform
+ """
+ COCOASUDO = "cocoasudo"
+ # XXX need the good old magic translate for these strings
+ # (look for magic in 0.2.0 release)
+ SUDO_MSG = ("Bitmask needs administrative privileges to run "
+ "Encrypted Internet.")
+ INSTALL_MSG = ("\"Bitmask needs administrative privileges to install "
+ "missing scripts and fix permissions.\"")
+
+ INSTALL_PATH = os.path.realpath(os.getcwd() + "/../../")
+ INSTALL_PATH_ESCAPED = os.path.realpath(os.getcwd() + "/../../")
+ OPENVPN_BIN = 'openvpn.leap'
+ OPENVPN_PATH = "%s/Contents/Resources/openvpn" % (INSTALL_PATH,)
+ OPENVPN_PATH_ESCAPED = "%s/Contents/Resources/openvpn" % (
+ INSTALL_PATH_ESCAPED,)
+
+ UP_SCRIPT = "%s/client.up.sh" % (OPENVPN_PATH,)
+ DOWN_SCRIPT = "%s/client.down.sh" % (OPENVPN_PATH,)
+ OPENVPN_DOWN_PLUGIN = '%s/openvpn-down-root.so' % (OPENVPN_PATH,)
+
+ UPDOWN_FILES = (UP_SCRIPT, DOWN_SCRIPT, OPENVPN_DOWN_PLUGIN)
+ OTHER_FILES = []
+
+ @classmethod
+ def cmd_for_missing_scripts(kls, frompath):
+ """
+ Returns a command that can copy the missing scripts.
+ :rtype: str
+ """
+ to = kls.OPENVPN_PATH_ESCAPED
+
+ cmd = "#!/bin/sh\n"
+ cmd += "mkdir -p {0}\n".format(to)
+ cmd += "cp '{0}'/* {1}\n".format(frompath, to)
+ cmd += "chmod 744 {0}/*".format(to)
+
+ return cmd
+
+ @classmethod
+ def is_kext_loaded(kls):
+ """
+ Checks if the needed kext is loaded before launching openvpn.
+
+ :returns: True if kext is loaded, False otherwise.
+ :rtype: bool
+ """
+ return bool(commands.getoutput('kextstat | grep "leap.tun"'))
+
+ @classmethod
+ def _get_icon_path(kls):
+ """
+ Returns the absolute path to the app icon.
+
+ :rtype: str
+ """
+ resources_path = os.path.abspath(
+ os.path.join(os.getcwd(), "../../Contents/Resources"))
+
+ return os.path.join(resources_path, "leap-client.tiff")
+
+ @classmethod
+ def get_cocoasudo_ovpn_cmd(kls):
+ """
+ Returns a string with the cocoasudo command needed to run openvpn
+ as admin with a nice password prompt. The actual command needs to be
+ appended.
+
+ :rtype: (str, list)
+ """
+ # TODO add translation support for this
+ sudo_msg = ("Bitmask needs administrative privileges to run "
+ "Encrypted Internet.")
+ iconpath = kls._get_icon_path()
+ has_icon = os.path.isfile(iconpath)
+ args = ["--icon=%s" % iconpath] if has_icon else []
+ args.append("--prompt=%s" % (sudo_msg,))
+
+ return kls.COCOASUDO, args
+
+ @classmethod
+ def get_cocoasudo_installmissing_cmd(kls):
+ """
+ Returns a string with the cocoasudo command needed to install missing
+ files as admin with a nice password prompt. The actual command needs to
+ be appended.
+
+ :rtype: (str, list)
+ """
+ # TODO add translation support for this
+ install_msg = ('"Bitmask needs administrative privileges to install '
+ 'missing scripts and fix permissions."')
+ iconpath = kls._get_icon_path()
+ has_icon = os.path.isfile(iconpath)
+ args = ["--icon=%s" % iconpath] if has_icon else []
+ args.append("--prompt=%s" % (install_msg,))
+
+ return kls.COCOASUDO, args
+
+ @classmethod
+ def get_vpn_command(kls, eipconfig, providerconfig, socket_host,
+ socket_port="unix", openvpn_verb=1):
+ """
+ Returns the OSX implementation for the vpn launching command.
+
+ Might raise:
+ EIPNoTunKextLoaded,
+ OpenVPNNotFoundException,
+ VPNLauncherException.
+
+ :param eipconfig: eip configuration object
+ :type eipconfig: EIPConfig
+ :param providerconfig: provider specific configuration
+ :type providerconfig: ProviderConfig
+ :param socket_host: either socket path (unix) or socket IP
+ :type socket_host: str
+ :param socket_port: either string "unix" if it's a unix socket,
+ or port otherwise
+ :type socket_port: str
+ :param openvpn_verb: the openvpn verbosity wanted
+ :type openvpn_verb: int
+
+ :return: A VPN command ready to be launched.
+ :rtype: list
+ """
+ if not kls.is_kext_loaded():
+ raise EIPNoTunKextLoaded
+
+ # we use `super` in order to send the class to use
+ command = super(DarwinVPNLauncher, kls).get_vpn_command(
+ eipconfig, providerconfig, socket_host, socket_port, openvpn_verb)
+
+ cocoa, cargs = kls.get_cocoasudo_ovpn_cmd()
+ cargs.extend(command)
+ command = cargs
+ command.insert(0, cocoa)
+
+ command.extend(['--setenv', "LEAPUSER", getpass.getuser()])
+
+ return command
+
+ @classmethod
+ def get_vpn_env(kls):
+ """
+ Returns a dictionary with the custom env for the platform.
+ This is mainly used for setting LD_LIBRARY_PATH to the correct
+ path when distributing a standalone client
+
+ :rtype: dict
+ """
+ return {
+ "DYLD_LIBRARY_PATH": os.path.join(get_path_prefix(), "..", "lib")
+ }
diff --git a/src/leap/bitmask/services/eip/linuxvpnlauncher.py b/src/leap/bitmask/services/eip/linuxvpnlauncher.py
new file mode 100644
index 00000000..c2c28627
--- /dev/null
+++ b/src/leap/bitmask/services/eip/linuxvpnlauncher.py
@@ -0,0 +1,232 @@
+# -*- coding: utf-8 -*-
+# linuxvpnlauncher.py
+# Copyright (C) 2013 LEAP
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program 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 General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+"""
+Linux VPN launcher implementation.
+"""
+import commands
+import logging
+import os
+import subprocess
+import time
+
+from leap.bitmask.config import flags
+from leap.bitmask.util import privilege_policies
+from leap.bitmask.util.privilege_policies import LinuxPolicyChecker
+from leap.common.files import which
+from leap.bitmask.services.eip.vpnlauncher import VPNLauncher
+from leap.bitmask.services.eip.vpnlauncher import VPNLauncherException
+from leap.bitmask.util import get_path_prefix
+from leap.common.check import leap_assert
+from leap.bitmask.util import first
+
+logger = logging.getLogger(__name__)
+
+
+class EIPNoPolkitAuthAgentAvailable(VPNLauncherException):
+ pass
+
+
+class EIPNoPkexecAvailable(VPNLauncherException):
+ pass
+
+
+def _is_pkexec_in_system():
+ """
+ Checks the existence of the pkexec binary in system.
+ """
+ pkexec_path = which('pkexec')
+ if len(pkexec_path) == 0:
+ return False
+ return True
+
+
+def _is_auth_agent_running():
+ """
+ Checks if a polkit daemon is running.
+
+ :return: True if it's running, False if it's not.
+ :rtype: boolean
+ """
+ ps = 'ps aux | grep polkit-%s-authentication-agent-1'
+ opts = (ps % case for case in ['[g]nome', '[k]de'])
+ is_running = map(lambda l: commands.getoutput(l), opts)
+ return any(is_running)
+
+
+def _try_to_launch_agent():
+ """
+ Tries to launch a polkit daemon.
+ """
+ env = None
+ if flags.STANDALONE is True:
+ env = {"PYTHONPATH": os.path.abspath('../../../../lib/')}
+ try:
+ # We need to quote the command because subprocess call
+ # will do "sh -c 'foo'", so if we do not quoute it we'll end
+ # up with a invocation to the python interpreter. And that
+ # is bad.
+ subprocess.call(["python -m leap.bitmask.util.polkit_agent"],
+ shell=True, env=env)
+ except Exception as exc:
+ logger.exception(exc)
+
+
+class LinuxVPNLauncher(VPNLauncher):
+ PKEXEC_BIN = 'pkexec'
+ OPENVPN_BIN = 'openvpn'
+ OPENVPN_BIN_PATH = os.path.join(
+ get_path_prefix(), "..", "apps", "eip", OPENVPN_BIN)
+
+ SYSTEM_CONFIG = "/etc/leap"
+ UP_DOWN_FILE = "resolv-update"
+ UP_DOWN_PATH = "%s/%s" % (SYSTEM_CONFIG, UP_DOWN_FILE)
+
+ # We assume this is there by our openvpn dependency, and
+ # we will put it there on the bundle too.
+ # TODO adapt to the bundle path.
+ OPENVPN_DOWN_ROOT_BASE = "/usr/lib/openvpn/"
+ OPENVPN_DOWN_ROOT_FILE = "openvpn-plugin-down-root.so"
+ OPENVPN_DOWN_ROOT_PATH = "%s/%s" % (
+ OPENVPN_DOWN_ROOT_BASE,
+ OPENVPN_DOWN_ROOT_FILE)
+
+ UP_SCRIPT = DOWN_SCRIPT = UP_DOWN_PATH
+ UPDOWN_FILES = (UP_DOWN_PATH,)
+ POLKIT_PATH = LinuxPolicyChecker.get_polkit_path()
+ OTHER_FILES = (POLKIT_PATH, )
+
+ @classmethod
+ def maybe_pkexec(kls):
+ """
+ Checks whether pkexec is available in the system, and
+ returns the path if found.
+
+ Might raise:
+ EIPNoPkexecAvailable,
+ EIPNoPolkitAuthAgentAvailable.
+
+ :returns: a list of the paths where pkexec is to be found
+ :rtype: list
+ """
+ if _is_pkexec_in_system():
+ if not _is_auth_agent_running():
+ _try_to_launch_agent()
+ time.sleep(0.5)
+ if _is_auth_agent_running():
+ pkexec_possibilities = which(kls.PKEXEC_BIN)
+ leap_assert(len(pkexec_possibilities) > 0,
+ "We couldn't find pkexec")
+ return pkexec_possibilities
+ else:
+ logger.warning("No polkit auth agent found. pkexec " +
+ "will use its own auth agent.")
+ raise EIPNoPolkitAuthAgentAvailable()
+ else:
+ logger.warning("System has no pkexec")
+ raise EIPNoPkexecAvailable()
+
+ @classmethod
+ def missing_other_files(kls):
+ """
+ 'Extend' the VPNLauncher's missing_other_files to check if the polkit
+ files is outdated. If the polkit file that is in OTHER_FILES exists but
+ is not up to date, it is added to the missing list.
+
+ :returns: a list of missing files
+ :rtype: list of str
+ """
+ # we use `super` in order to send the class to use
+ missing = super(LinuxVPNLauncher, kls).missing_other_files()
+ polkit_file = LinuxPolicyChecker.get_polkit_path()
+ if polkit_file not in missing:
+ if privilege_policies.is_policy_outdated(kls.OPENVPN_BIN_PATH):
+ missing.append(polkit_file)
+
+ return missing
+
+ @classmethod
+ def get_vpn_command(kls, eipconfig, providerconfig, socket_host,
+ socket_port="unix", openvpn_verb=1):
+ """
+ Returns the Linux implementation for the vpn launching command.
+
+ Might raise:
+ EIPNoPkexecAvailable,
+ EIPNoPolkitAuthAgentAvailable,
+ OpenVPNNotFoundException,
+ VPNLauncherException.
+
+ :param eipconfig: eip configuration object
+ :type eipconfig: EIPConfig
+ :param providerconfig: provider specific configuration
+ :type providerconfig: ProviderConfig
+ :param socket_host: either socket path (unix) or socket IP
+ :type socket_host: str
+ :param socket_port: either string "unix" if it's a unix socket,
+ or port otherwise
+ :type socket_port: str
+ :param openvpn_verb: the openvpn verbosity wanted
+ :type openvpn_verb: int
+
+ :return: A VPN command ready to be launched.
+ :rtype: list
+ """
+ # we use `super` in order to send the class to use
+ command = super(LinuxVPNLauncher, kls).get_vpn_command(
+ eipconfig, providerconfig, socket_host, socket_port, openvpn_verb)
+
+ pkexec = kls.maybe_pkexec()
+ if pkexec:
+ command.insert(0, first(pkexec))
+
+ return command
+
+ @classmethod
+ def cmd_for_missing_scripts(kls, frompath, pol_file):
+ """
+ Returns a sh script that can copy the missing files.
+
+ :param frompath: The path where the up/down scripts live
+ :type frompath: str
+ :param pol_file: The path where the dynamically generated
+ policy file lives
+ :type pol_file: str
+
+ :rtype: str
+ """
+ to = kls.SYSTEM_CONFIG
+
+ cmd = '#!/bin/sh\n'
+ cmd += 'mkdir -p "%s"\n' % (to, )
+ cmd += 'cp "%s/%s" "%s"\n' % (frompath, kls.UP_DOWN_FILE, to)
+ cmd += 'cp "%s" "%s"\n' % (pol_file, kls.POLKIT_PATH)
+ cmd += 'chmod 644 "%s"\n' % (kls.POLKIT_PATH, )
+
+ return cmd
+
+ @classmethod
+ def get_vpn_env(kls):
+ """
+ Returns a dictionary with the custom env for the platform.
+ This is mainly used for setting LD_LIBRARY_PATH to the correct
+ path when distributing a standalone client
+
+ :rtype: dict
+ """
+ return {
+ "LD_LIBRARY_PATH": os.path.join(get_path_prefix(), "..", "lib")
+ }
diff --git a/src/leap/bitmask/services/eip/vpnlauncher.py b/src/leap/bitmask/services/eip/vpnlauncher.py
new file mode 100644
index 00000000..935d75f1
--- /dev/null
+++ b/src/leap/bitmask/services/eip/vpnlauncher.py
@@ -0,0 +1,290 @@
+# -*- coding: utf-8 -*-
+# vpnlauncher.py
+# Copyright (C) 2013 LEAP
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program 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 General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+"""
+Platform independant VPN launcher interface.
+"""
+import getpass
+import logging
+import os
+import stat
+
+from abc import ABCMeta, abstractmethod
+from functools import partial
+
+from leap.bitmask.config import flags
+from leap.bitmask.config.leapsettings import LeapSettings
+from leap.bitmask.config.providerconfig import ProviderConfig
+from leap.bitmask.services.eip.eipconfig import EIPConfig, VPNGatewaySelector
+from leap.bitmask.util import first
+from leap.bitmask.util import get_path_prefix
+from leap.common.check import leap_assert, leap_assert_type
+from leap.common.files import which
+
+logger = logging.getLogger(__name__)
+
+
+class VPNLauncherException(Exception):
+ pass
+
+
+class OpenVPNNotFoundException(VPNLauncherException):
+ pass
+
+
+def _has_updown_scripts(path, warn=True):
+ """
+ Checks the existence of the up/down scripts and its
+ exec bit if applicable.
+
+ :param path: the path to be checked
+ :type path: str
+
+ :param warn: whether we should log the absence
+ :type warn: bool
+
+ :rtype: bool
+ """
+ is_file = os.path.isfile(path)
+ if warn and not is_file:
+ logger.error("Could not find up/down script %s. "
+ "Might produce DNS leaks." % (path,))
+
+ # XXX check if applies in win
+ is_exe = False
+ try:
+ is_exe = (stat.S_IXUSR & os.stat(path)[stat.ST_MODE] != 0)
+ except OSError as e:
+ logger.warn("%s" % (e,))
+ if warn and not is_exe:
+ logger.error("Up/down script %s is not executable. "
+ "Might produce DNS leaks." % (path,))
+ return is_file and is_exe
+
+
+def _has_other_files(path, warn=True):
+ """
+ Checks the existence of other important files.
+
+ :param path: the path to be checked
+ :type path: str
+
+ :param warn: whether we should log the absence
+ :type warn: bool
+
+ :rtype: bool
+ """
+ is_file = os.path.isfile(path)
+ if warn and not is_file:
+ logger.warning("Could not find file during checks: %s. " % (
+ path,))
+ return is_file
+
+
+class VPNLauncher(object):
+ """
+ Abstract launcher class
+ """
+ __metaclass__ = ABCMeta
+
+ UPDOWN_FILES = None
+ OTHER_FILES = None
+
+ @classmethod
+ @abstractmethod
+ def get_vpn_command(kls, eipconfig, providerconfig,
+ socket_host, socket_port, openvpn_verb=1):
+ """
+ Returns the platform dependant vpn launching command
+
+ Might raise:
+ OpenVPNNotFoundException,
+ VPNLauncherException.
+
+ :param eipconfig: eip configuration object
+ :type eipconfig: EIPConfig
+ :param providerconfig: provider specific configuration
+ :type providerconfig: ProviderConfig
+ :param socket_host: either socket path (unix) or socket IP
+ :type socket_host: str
+ :param socket_port: either string "unix" if it's a unix socket,
+ or port otherwise
+ :type socket_port: str
+ :param openvpn_verb: the openvpn verbosity wanted
+ :type openvpn_verb: int
+
+ :return: A VPN command ready to be launched.
+ :rtype: list
+ """
+ leap_assert_type(eipconfig, EIPConfig)
+ leap_assert_type(providerconfig, ProviderConfig)
+
+ kwargs = {}
+ if flags.STANDALONE:
+ kwargs['path_extension'] = os.path.join(
+ get_path_prefix(), "..", "apps", "eip")
+
+ openvpn_possibilities = which(kls.OPENVPN_BIN, **kwargs)
+ if len(openvpn_possibilities) == 0:
+ raise OpenVPNNotFoundException()
+
+ openvpn = first(openvpn_possibilities)
+ args = []
+
+ args += [
+ '--setenv', "LEAPOPENVPN", "1"
+ ]
+
+ if openvpn_verb is not None:
+ args += ['--verb', '%d' % (openvpn_verb,)]
+
+ gateways = []
+ leap_settings = LeapSettings()
+ domain = providerconfig.get_domain()
+ gateway_conf = leap_settings.get_selected_gateway(domain)
+
+ if gateway_conf == leap_settings.GATEWAY_AUTOMATIC:
+ gateway_selector = VPNGatewaySelector(eipconfig)
+ gateways = gateway_selector.get_gateways()
+ else:
+ gateways = [gateway_conf]
+
+ if not gateways:
+ logger.error('No gateway was found!')
+ raise VPNLauncherException(kls.tr('No gateway was found!'))
+
+ logger.debug("Using gateways ips: {0}".format(', '.join(gateways)))
+
+ for gw in gateways:
+ args += ['--remote', gw, '1194', 'udp']
+
+ args += [
+ '--client',
+ '--dev', 'tun',
+ ##############################################################
+ # persist-tun makes ping-restart fail because it leaves a
+ # broken routing table
+ ##############################################################
+ # '--persist-tun',
+ '--persist-key',
+ '--tls-client',
+ '--remote-cert-tls',
+ 'server'
+ ]
+
+ openvpn_configuration = eipconfig.get_openvpn_configuration()
+ for key, value in openvpn_configuration.items():
+ args += ['--%s' % (key,), value]
+
+ user = getpass.getuser()
+
+ ##############################################################
+ # The down-root plugin fails in some situations, so we don't
+ # drop privs for the time being
+ ##############################################################
+ # args += [
+ # '--user', user,
+ # '--group', grp.getgrgid(os.getgroups()[-1]).gr_name
+ # ]
+
+ if socket_port == "unix": # that's always the case for linux
+ args += [
+ '--management-client-user', user
+ ]
+
+ args += [
+ '--management-signal',
+ '--management', socket_host, socket_port,
+ '--script-security', '2'
+ ]
+
+ if _has_updown_scripts(kls.UP_SCRIPT):
+ args += [
+ '--up', '\"%s\"' % (kls.UP_SCRIPT,),
+ ]
+
+ if _has_updown_scripts(kls.DOWN_SCRIPT):
+ args += [
+ '--down', '\"%s\"' % (kls.DOWN_SCRIPT,)
+ ]
+
+ ###########################################################
+ # For the time being we are disabling the usage of the
+ # down-root plugin, because it doesn't quite work as
+ # expected (i.e. it doesn't run route -del as root
+ # when finishing, so it fails to properly
+ # restart/quit)
+ ###########################################################
+ # if _has_updown_scripts(kls.OPENVPN_DOWN_PLUGIN):
+ # args += [
+ # '--plugin', kls.OPENVPN_DOWN_ROOT,
+ # '\'%s\'' % kls.DOWN_SCRIPT # for OSX
+ # '\'script_type=down %s\'' % kls.DOWN_SCRIPT # for Linux
+ # ]
+
+ args += [
+ '--cert', eipconfig.get_client_cert_path(providerconfig),
+ '--key', eipconfig.get_client_cert_path(providerconfig),
+ '--ca', providerconfig.get_ca_cert_path()
+ ]
+
+ command_and_args = [openvpn] + args
+ logger.debug("Running VPN with command:")
+ logger.debug(" ".join(command_and_args))
+
+ return command_and_args
+
+ @classmethod
+ def get_vpn_env(kls):
+ """
+ Returns a dictionary with the custom env for the platform.
+ This is mainly used for setting LD_LIBRARY_PATH to the correct
+ path when distributing a standalone client
+
+ :rtype: dict
+ """
+ return {}
+
+ @classmethod
+ def missing_updown_scripts(kls):
+ """
+ Returns what updown scripts are missing.
+
+ :rtype: list
+ """
+ leap_assert(kls.UPDOWN_FILES is not None,
+ "Need to define UPDOWN_FILES for this particular "
+ "launcher before calling this method")
+ file_exist = partial(_has_updown_scripts, warn=False)
+ zipped = zip(kls.UPDOWN_FILES, map(file_exist, kls.UPDOWN_FILES))
+ missing = filter(lambda (path, exists): exists is False, zipped)
+ return [path for path, exists in missing]
+
+ @classmethod
+ def missing_other_files(kls):
+ """
+ Returns what other important files are missing during startup.
+ Same as missing_updown_scripts but does not check for exec bit.
+
+ :rtype: list
+ """
+ leap_assert(kls.OTHER_FILES is not None,
+ "Need to define OTHER_FILES for this particular "
+ "auncher before calling this method")
+ file_exist = partial(_has_other_files, warn=False)
+ zipped = zip(kls.OTHER_FILES, map(file_exist, kls.OTHER_FILES))
+ missing = filter(lambda (path, exists): exists is False, zipped)
+ return [path for path, exists in missing]
diff --git a/src/leap/bitmask/services/eip/vpnlaunchers.py b/src/leap/bitmask/services/eip/vpnlaunchers.py
deleted file mode 100644
index daa0d81f..00000000
--- a/src/leap/bitmask/services/eip/vpnlaunchers.py
+++ /dev/null
@@ -1,963 +0,0 @@
-# -*- coding: utf-8 -*-
-# vpnlaunchers.py
-# Copyright (C) 2013 LEAP
-#
-# This program is free software: you can redistribute it and/or modify
-# it under the terms of the GNU General Public License as published by
-# the Free Software Foundation, either version 3 of the License, or
-# (at your option) any later version.
-#
-# This program 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 General Public License for more details.
-#
-# You should have received a copy of the GNU General Public License
-# along with this program. If not, see <http://www.gnu.org/licenses/>.
-
-"""
-Platform dependant VPN launchers
-"""
-import commands
-import logging
-import getpass
-import os
-import platform
-import stat
-import subprocess
-try:
- import grp
-except ImportError:
- pass # ignore, probably windows
-
-from abc import ABCMeta, abstractmethod
-from functools import partial
-from time import sleep
-
-from leap.bitmask.config import flags
-from leap.bitmask.config.leapsettings import LeapSettings
-
-from leap.bitmask.config.providerconfig import ProviderConfig
-from leap.bitmask.services.eip.eipconfig import EIPConfig, VPNGatewaySelector
-from leap.bitmask.util import first
-from leap.bitmask.util import get_path_prefix
-from leap.bitmask.util.privilege_policies import LinuxPolicyChecker
-from leap.bitmask.util import privilege_policies
-from leap.common.check import leap_assert, leap_assert_type
-from leap.common.files import which
-
-
-logger = logging.getLogger(__name__)
-
-
-class VPNLauncherException(Exception):
- pass
-
-
-class OpenVPNNotFoundException(VPNLauncherException):
- pass
-
-
-class EIPNoPolkitAuthAgentAvailable(VPNLauncherException):
- pass
-
-
-class EIPNoPkexecAvailable(VPNLauncherException):
- pass
-
-
-class EIPNoTunKextLoaded(VPNLauncherException):
- pass
-
-
-class VPNLauncher(object):
- """
- Abstract launcher class
- """
- __metaclass__ = ABCMeta
-
- UPDOWN_FILES = None
- OTHER_FILES = None
-
- @abstractmethod
- def get_vpn_command(self, eipconfig=None, providerconfig=None,
- socket_host=None, socket_port=None):
- """
- Returns the platform dependant vpn launching command
-
- :param eipconfig: eip configuration object
- :type eipconfig: EIPConfig
- :param providerconfig: provider specific configuration
- :type providerconfig: ProviderConfig
- :param socket_host: either socket path (unix) or socket IP
- :type socket_host: str
- :param socket_port: either string "unix" if it's a unix
- socket, or port otherwise
- :type socket_port: str
-
- :return: A VPN command ready to be launched
- :rtype: list
- """
- return []
-
- @abstractmethod
- def get_vpn_env(self):
- """
- Returns a dictionary with the custom env for the platform.
- This is mainly used for setting LD_LIBRARY_PATH to the correct
- path when distributing a standalone client
-
- :rtype: dict
- """
- return {}
-
- @classmethod
- def missing_updown_scripts(kls):
- """
- Returns what updown scripts are missing.
- :rtype: list
- """
- leap_assert(kls.UPDOWN_FILES is not None,
- "Need to define UPDOWN_FILES for this particular "
- "auncher before calling this method")
- file_exist = partial(_has_updown_scripts, warn=False)
- zipped = zip(kls.UPDOWN_FILES, map(file_exist, kls.UPDOWN_FILES))
- missing = filter(lambda (path, exists): exists is False, zipped)
- return [path for path, exists in missing]
-
- @classmethod
- def missing_other_files(kls):
- """
- Returns what other important files are missing during startup.
- Same as missing_updown_scripts but does not check for exec bit.
- :rtype: list
- """
- leap_assert(kls.UPDOWN_FILES is not None,
- "Need to define OTHER_FILES for this particular "
- "auncher before calling this method")
- file_exist = partial(_has_other_files, warn=False)
- zipped = zip(kls.OTHER_FILES, map(file_exist, kls.OTHER_FILES))
- missing = filter(lambda (path, exists): exists is False, zipped)
- return [path for path, exists in missing]
-
-
-def get_platform_launcher():
- launcher = globals()[platform.system() + "VPNLauncher"]
- leap_assert(launcher, "Unimplemented platform launcher: %s" %
- (platform.system(),))
- return launcher()
-
-
-def _is_pkexec_in_system():
- """
- Checks the existence of the pkexec binary in system.
- """
- pkexec_path = which('pkexec')
- if len(pkexec_path) == 0:
- return False
- return True
-
-
-def _has_updown_scripts(path, warn=True):
- """
- Checks the existence of the up/down scripts and its
- exec bit if applicable.
-
- :param path: the path to be checked
- :type path: str
-
- :param warn: whether we should log the absence
- :type warn: bool
-
- :rtype: bool
- """
- is_file = os.path.isfile(path)
- if warn and not is_file:
- logger.error("Could not find up/down script %s. "
- "Might produce DNS leaks." % (path,))
-
- # XXX check if applies in win
- is_exe = False
- try:
- is_exe = (stat.S_IXUSR & os.stat(path)[stat.ST_MODE] != 0)
- except OSError as e:
- logger.warn("%s" % (e,))
- if warn and not is_exe:
- logger.error("Up/down script %s is not executable. "
- "Might produce DNS leaks." % (path,))
- return is_file and is_exe
-
-
-def _has_other_files(path, warn=True):
- """
- Checks the existence of other important files.
-
- :param path: the path to be checked
- :type path: str
-
- :param warn: whether we should log the absence
- :type warn: bool
-
- :rtype: bool
- """
- is_file = os.path.isfile(path)
- if warn and not is_file:
- logger.warning("Could not find file during checks: %s. " % (
- path,))
- return is_file
-
-
-def _is_auth_agent_running():
- """
- Checks if a polkit daemon is running.
-
- :return: True if it's running, False if it's not.
- :rtype: boolean
- """
- ps = 'ps aux | grep polkit-%s-authentication-agent-1'
- opts = (ps % case for case in ['[g]nome', '[k]de'])
- is_running = map(lambda l: commands.getoutput(l), opts)
- return any(is_running)
-
-
-def _try_to_launch_agent():
- """
- Tries to launch a polkit daemon.
- """
- env = None
- if flags.STANDALONE is True:
- env = {"PYTHONPATH": os.path.abspath('../../../../lib/')}
- try:
- # We need to quote the command because subprocess call
- # will do "sh -c 'foo'", so if we do not quoute it we'll end
- # up with a invocation to the python interpreter. And that
- # is bad.
- subprocess.call(["python -m leap.bitmask.util.polkit_agent"],
- shell=True, env=env)
- except Exception as exc:
- logger.exception(exc)
-
-
-class LinuxVPNLauncher(VPNLauncher):
- """
- VPN launcher for the Linux platform
- """
-
- PKEXEC_BIN = 'pkexec'
- OPENVPN_BIN = 'openvpn'
- OPENVPN_BIN_PATH = os.path.join(
- get_path_prefix(), "..", "apps", "eip", OPENVPN_BIN)
-
- SYSTEM_CONFIG = "/etc/leap"
- UP_DOWN_FILE = "resolv-update"
- UP_DOWN_PATH = "%s/%s" % (SYSTEM_CONFIG, UP_DOWN_FILE)
-
- # We assume this is there by our openvpn dependency, and
- # we will put it there on the bundle too.
- # TODO adapt to the bundle path.
- OPENVPN_DOWN_ROOT_BASE = "/usr/lib/openvpn/"
- OPENVPN_DOWN_ROOT_FILE = "openvpn-plugin-down-root.so"
- OPENVPN_DOWN_ROOT_PATH = "%s/%s" % (
- OPENVPN_DOWN_ROOT_BASE,
- OPENVPN_DOWN_ROOT_FILE)
-
- UPDOWN_FILES = (UP_DOWN_PATH,)
- POLKIT_PATH = LinuxPolicyChecker.get_polkit_path()
- OTHER_FILES = (POLKIT_PATH, )
-
- def missing_other_files(self):
- """
- 'Extend' the VPNLauncher's missing_other_files to check if the polkit
- files is outdated. If the polkit file that is in OTHER_FILES exists but
- is not up to date, it is added to the missing list.
-
- :returns: a list of missing files
- :rtype: list of str
- """
- missing = VPNLauncher.missing_other_files.im_func(self)
- polkit_file = LinuxPolicyChecker.get_polkit_path()
- if polkit_file not in missing:
- if privilege_policies.is_policy_outdated(self.OPENVPN_BIN_PATH):
- missing.append(polkit_file)
-
- return missing
-
- @classmethod
- def cmd_for_missing_scripts(kls, frompath, pol_file):
- """
- Returns a sh script that can copy the missing files.
-
- :param frompath: The path where the up/down scripts live
- :type frompath: str
- :param pol_file: The path where the dynamically generated
- policy file lives
- :type pol_file: str
-
- :rtype: str
- """
- to = kls.SYSTEM_CONFIG
-
- cmd = '#!/bin/sh\n'
- cmd += 'mkdir -p "%s"\n' % (to, )
- cmd += 'cp "%s/%s" "%s"\n' % (frompath, kls.UP_DOWN_FILE, to)
- cmd += 'cp "%s" "%s"\n' % (pol_file, kls.POLKIT_PATH)
- cmd += 'chmod 644 "%s"\n' % (kls.POLKIT_PATH, )
-
- return cmd
-
- @classmethod
- def maybe_pkexec(kls):
- """
- Checks whether pkexec is available in the system, and
- returns the path if found.
-
- Might raise EIPNoPkexecAvailable or EIPNoPolkitAuthAgentAvailable
-
- :returns: a list of the paths where pkexec is to be found
- :rtype: list
- """
- if _is_pkexec_in_system():
- if not _is_auth_agent_running():
- _try_to_launch_agent()
- sleep(0.5)
- if _is_auth_agent_running():
- pkexec_possibilities = which(kls.PKEXEC_BIN)
- leap_assert(len(pkexec_possibilities) > 0,
- "We couldn't find pkexec")
- return pkexec_possibilities
- else:
- logger.warning("No polkit auth agent found. pkexec " +
- "will use its own auth agent.")
- raise EIPNoPolkitAuthAgentAvailable()
- else:
- logger.warning("System has no pkexec")
- raise EIPNoPkexecAvailable()
-
- @classmethod
- def maybe_down_plugin(kls):
- """
- Returns the path of the openvpn down-root-plugin, searching first
- in the relative path for the standalone bundle, and then in the system
- path where the debian package puts it.
-
- :returns: the path where the plugin was found, or None
- :rtype: str or None
- """
- cwd = os.getcwd()
- rel_path_in_bundle = os.path.join(
- 'apps', 'eip', 'files', kls.OPENVPN_DOWN_ROOT_FILE)
- abs_path_in_bundle = os.path.join(cwd, rel_path_in_bundle)
- if os.path.isfile(abs_path_in_bundle):
- return abs_path_in_bundle
- abs_path_in_system = kls.OPENVPN_DOWN_ROOT_PATH
- if os.path.isfile(abs_path_in_system):
- return abs_path_in_system
-
- logger.warning("We could not find the down-root-plugin, so no updown "
- "scripts will be run. DNS leaks are likely!")
- return None
-
- def get_vpn_command(self, eipconfig=None, providerconfig=None,
- socket_host=None, socket_port="unix", openvpn_verb=1):
- """
- Returns the platform dependant vpn launching command. It will
- look for openvpn in the regular paths and algo in
- path_prefix/apps/eip/ (in case standalone is set)
-
- Might raise:
- VPNLauncherException,
- OpenVPNNotFoundException.
-
- :param eipconfig: eip configuration object
- :type eipconfig: EIPConfig
-
- :param providerconfig: provider specific configuration
- :type providerconfig: ProviderConfig
-
- :param socket_host: either socket path (unix) or socket IP
- :type socket_host: str
-
- :param socket_port: either string "unix" if it's a unix
- socket, or port otherwise
- :type socket_port: str
-
- :param openvpn_verb: openvpn verbosity wanted
- :type openvpn_verb: int
-
- :return: A VPN command ready to be launched
- :rtype: list
- """
- leap_assert(eipconfig, "We need an eip config")
- leap_assert_type(eipconfig, EIPConfig)
- leap_assert(providerconfig, "We need a provider config")
- leap_assert_type(providerconfig, ProviderConfig)
- leap_assert(socket_host, "We need a socket host!")
- leap_assert(socket_port, "We need a socket port!")
-
- kwargs = {}
- if flags.STANDALONE:
- kwargs['path_extension'] = os.path.join(
- get_path_prefix(), "..", "apps", "eip")
-
- openvpn_possibilities = which(self.OPENVPN_BIN, **kwargs)
-
- if len(openvpn_possibilities) == 0:
- raise OpenVPNNotFoundException()
-
- openvpn = first(openvpn_possibilities)
- args = []
-
- pkexec = self.maybe_pkexec()
- if pkexec:
- args.append(openvpn)
- openvpn = first(pkexec)
-
- args += [
- '--setenv', "LEAPOPENVPN", "1"
- ]
-
- if openvpn_verb is not None:
- args += ['--verb', '%d' % (openvpn_verb,)]
-
- gateways = []
- leap_settings = LeapSettings()
- domain = providerconfig.get_domain()
- gateway_conf = leap_settings.get_selected_gateway(domain)
-
- if gateway_conf == leap_settings.GATEWAY_AUTOMATIC:
- gateway_selector = VPNGatewaySelector(eipconfig)
- gateways = gateway_selector.get_gateways()
- else:
- gateways = [gateway_conf]
-
- if not gateways:
- logger.error('No gateway was found!')
- raise VPNLauncherException(self.tr('No gateway was found!'))
-
- logger.debug("Using gateways ips: {0}".format(', '.join(gateways)))
-
- for gw in gateways:
- args += ['--remote', gw, '1194', 'udp']
-
- args += [
- '--client',
- '--dev', 'tun',
- ##############################################################
- # persist-tun makes ping-restart fail because it leaves a
- # broken routing table
- ##############################################################
- # '--persist-tun',
- '--persist-key',
- '--tls-client',
- '--remote-cert-tls',
- 'server'
- ]
-
- openvpn_configuration = eipconfig.get_openvpn_configuration()
-
- for key, value in openvpn_configuration.items():
- args += ['--%s' % (key,), value]
-
- ##############################################################
- # The down-root plugin fails in some situations, so we don't
- # drop privs for the time being
- ##############################################################
- # args += [
- # '--user', getpass.getuser(),
- # '--group', grp.getgrgid(os.getgroups()[-1]).gr_name
- # ]
-
- if socket_port == "unix": # that's always the case for linux
- args += [
- '--management-client-user', getpass.getuser()
- ]
-
- args += [
- '--management-signal',
- '--management', socket_host, socket_port,
- '--script-security', '2'
- ]
-
- plugin_path = self.maybe_down_plugin()
- # If we do not have the down plugin neither in the bundle
- # nor in the system, we do not do updown scripts. The alternative
- # is leaving the user without the ability to restore dns and routes
- # to its original state.
-
- if plugin_path and _has_updown_scripts(self.UP_DOWN_PATH):
- args += [
- '--up', self.UP_DOWN_PATH,
- '--down', self.UP_DOWN_PATH,
- ##############################################################
- # For the time being we are disabling the usage of the
- # down-root plugin, because it doesn't quite work as
- # expected (i.e. it doesn't run route -del as root
- # when finishing, so it fails to properly
- # restart/quit)
- ##############################################################
- # '--plugin', plugin_path,
- # '\'script_type=down %s\'' % self.UP_DOWN_PATH
- ]
-
- args += [
- '--cert', eipconfig.get_client_cert_path(providerconfig),
- '--key', eipconfig.get_client_cert_path(providerconfig),
- '--ca', providerconfig.get_ca_cert_path()
- ]
-
- logger.debug("Running VPN with command:")
- logger.debug("%s %s" % (openvpn, " ".join(args)))
-
- return [openvpn] + args
-
- def get_vpn_env(self):
- """
- Returns a dictionary with the custom env for the platform.
- This is mainly used for setting LD_LIBRARY_PATH to the correct
- path when distributing a standalone client
-
- :rtype: dict
- """
- return {
- "LD_LIBRARY_PATH": os.path.join(get_path_prefix(), "..", "lib")
- }
-
-
-class DarwinVPNLauncher(VPNLauncher):
- """
- VPN launcher for the Darwin Platform
- """
-
- COCOASUDO = "cocoasudo"
- # XXX need the good old magic translate for these strings
- # (look for magic in 0.2.0 release)
- SUDO_MSG = ("Bitmask needs administrative privileges to run "
- "Encrypted Internet.")
- INSTALL_MSG = ("\"Bitmask needs administrative privileges to install "
- "missing scripts and fix permissions.\"")
-
- INSTALL_PATH = os.path.realpath(os.getcwd() + "/../../")
- INSTALL_PATH_ESCAPED = os.path.realpath(os.getcwd() + "/../../")
- OPENVPN_BIN = 'openvpn.leap'
- OPENVPN_PATH = "%s/Contents/Resources/openvpn" % (INSTALL_PATH,)
- OPENVPN_PATH_ESCAPED = "%s/Contents/Resources/openvpn" % (
- INSTALL_PATH_ESCAPED,)
-
- UP_SCRIPT = "%s/client.up.sh" % (OPENVPN_PATH,)
- DOWN_SCRIPT = "%s/client.down.sh" % (OPENVPN_PATH,)
- OPENVPN_DOWN_PLUGIN = '%s/openvpn-down-root.so' % (OPENVPN_PATH,)
-
- UPDOWN_FILES = (UP_SCRIPT, DOWN_SCRIPT, OPENVPN_DOWN_PLUGIN)
- OTHER_FILES = []
-
- @classmethod
- def cmd_for_missing_scripts(kls, frompath):
- """
- Returns a command that can copy the missing scripts.
- :rtype: str
- """
- to = kls.OPENVPN_PATH_ESCAPED
- cmd = "#!/bin/sh\nmkdir -p %s\ncp \"%s/\"* %s\nchmod 744 %s/*" % (
- to, frompath, to, to)
- return cmd
-
- @classmethod
- def maybe_kextloaded(kls):
- """
- Checks if the needed kext is loaded before launching openvpn.
- """
- return bool(commands.getoutput('kextstat | grep "leap.tun"'))
-
- def _get_resource_path(self):
- """
- Returns the absolute path to the app resources directory
-
- :rtype: str
- """
- return os.path.abspath(
- os.path.join(
- os.getcwd(),
- "../../Contents/Resources"))
-
- def _get_icon_path(self):
- """
- Returns the absolute path to the app icon
-
- :rtype: str
- """
- return os.path.join(self._get_resource_path(),
- "leap-client.tiff")
-
- def get_cocoasudo_ovpn_cmd(self):
- """
- Returns a string with the cocoasudo command needed to run openvpn
- as admin with a nice password prompt. The actual command needs to be
- appended.
-
- :rtype: (str, list)
- """
- iconpath = self._get_icon_path()
- has_icon = os.path.isfile(iconpath)
- args = ["--icon=%s" % iconpath] if has_icon else []
- args.append("--prompt=%s" % (self.SUDO_MSG,))
-
- return self.COCOASUDO, args
-
- def get_cocoasudo_installmissing_cmd(self):
- """
- Returns a string with the cocoasudo command needed to install missing
- files as admin with a nice password prompt. The actual command needs to
- be appended.
-
- :rtype: (str, list)
- """
- iconpath = self._get_icon_path()
- has_icon = os.path.isfile(iconpath)
- args = ["--icon=%s" % iconpath] if has_icon else []
- args.append("--prompt=%s" % (self.INSTALL_MSG,))
-
- return self.COCOASUDO, args
-
- def get_vpn_command(self, eipconfig=None, providerconfig=None,
- socket_host=None, socket_port="unix", openvpn_verb=1):
- """
- Returns the platform dependant vpn launching command
-
- Might raise VPNException.
-
- :param eipconfig: eip configuration object
- :type eipconfig: EIPConfig
-
- :param providerconfig: provider specific configuration
- :type providerconfig: ProviderConfig
-
- :param socket_host: either socket path (unix) or socket IP
- :type socket_host: str
-
- :param socket_port: either string "unix" if it's a unix
- socket, or port otherwise
- :type socket_port: str
-
- :param openvpn_verb: openvpn verbosity wanted
- :type openvpn_verb: int
-
- :return: A VPN command ready to be launched
- :rtype: list
- """
- leap_assert(eipconfig, "We need an eip config")
- leap_assert_type(eipconfig, EIPConfig)
- leap_assert(providerconfig, "We need a provider config")
- leap_assert_type(providerconfig, ProviderConfig)
- leap_assert(socket_host, "We need a socket host!")
- leap_assert(socket_port, "We need a socket port!")
-
- if not self.maybe_kextloaded():
- raise EIPNoTunKextLoaded
-
- kwargs = {}
- if flags.STANDALONE:
- kwargs['path_extension'] = os.path.join(
- get_path_prefix(), "..", "apps", "eip")
-
- openvpn_possibilities = which(
- self.OPENVPN_BIN,
- **kwargs)
- if len(openvpn_possibilities) == 0:
- raise OpenVPNNotFoundException()
-
- openvpn = first(openvpn_possibilities)
- args = [openvpn]
-
- args += [
- '--setenv', "LEAPOPENVPN", "1"
- ]
-
- if openvpn_verb is not None:
- args += ['--verb', '%d' % (openvpn_verb,)]
-
- gateways = []
- leap_settings = LeapSettings()
- domain = providerconfig.get_domain()
- gateway_conf = leap_settings.get_selected_gateway(domain)
-
- if gateway_conf == leap_settings.GATEWAY_AUTOMATIC:
- gateway_selector = VPNGatewaySelector(eipconfig)
- gateways = gateway_selector.get_gateways()
- else:
- gateways = [gateway_conf]
-
- if not gateways:
- logger.error('No gateway was found!')
- raise VPNLauncherException(self.tr('No gateway was found!'))
-
- logger.debug("Using gateways ips: {0}".format(', '.join(gateways)))
-
- for gw in gateways:
- args += ['--remote', gw, '1194', 'udp']
-
- args += [
- '--client',
- '--dev', 'tun',
- ##############################################################
- # persist-tun makes ping-restart fail because it leaves a
- # broken routing table
- ##############################################################
- # '--persist-tun',
- '--persist-key',
- '--tls-client',
- '--remote-cert-tls',
- 'server'
- ]
-
- openvpn_configuration = eipconfig.get_openvpn_configuration()
- for key, value in openvpn_configuration.items():
- args += ['--%s' % (key,), value]
-
- user = getpass.getuser()
-
- ##############################################################
- # The down-root plugin fails in some situations, so we don't
- # drop privs for the time being
- ##############################################################
- # args += [
- # '--user', user,
- # '--group', grp.getgrgid(os.getgroups()[-1]).gr_name
- # ]
-
- if socket_port == "unix":
- args += [
- '--management-client-user', user
- ]
-
- args += [
- '--management-signal',
- '--management', socket_host, socket_port,
- '--script-security', '2'
- ]
-
- if _has_updown_scripts(self.UP_SCRIPT):
- args += [
- '--up', '\"%s\"' % (self.UP_SCRIPT,),
- ]
-
- if _has_updown_scripts(self.DOWN_SCRIPT):
- args += [
- '--down', '\"%s\"' % (self.DOWN_SCRIPT,)
- ]
-
- # should have the down script too
- if _has_updown_scripts(self.OPENVPN_DOWN_PLUGIN):
- args += [
- ###########################################################
- # For the time being we are disabling the usage of the
- # down-root plugin, because it doesn't quite work as
- # expected (i.e. it doesn't run route -del as root
- # when finishing, so it fails to properly
- # restart/quit)
- ###########################################################
- # '--plugin', self.OPENVPN_DOWN_PLUGIN,
- # '\'%s\'' % self.DOWN_SCRIPT
- ]
-
- # we set user to be passed to the up/down scripts
- args += [
- '--setenv', "LEAPUSER", "%s" % (user,)]
-
- args += [
- '--cert', eipconfig.get_client_cert_path(providerconfig),
- '--key', eipconfig.get_client_cert_path(providerconfig),
- '--ca', providerconfig.get_ca_cert_path()
- ]
-
- command, cargs = self.get_cocoasudo_ovpn_cmd()
- cmd_args = cargs + args
-
- logger.debug("Running VPN with command:")
- logger.debug("%s %s" % (command, " ".join(cmd_args)))
-
- return [command] + cmd_args
-
- def get_vpn_env(self):
- """
- Returns a dictionary with the custom env for the platform.
- This is mainly used for setting LD_LIBRARY_PATH to the correct
- path when distributing a standalone client
-
- :rtype: dict
- """
- return {
- "DYLD_LIBRARY_PATH": os.path.join(get_path_prefix(), "..", "lib")
- }
-
-
-class WindowsVPNLauncher(VPNLauncher):
- """
- VPN launcher for the Windows platform
- """
-
- OPENVPN_BIN = 'openvpn_leap.exe'
-
- # XXX UPDOWN_FILES ... we do not have updown files defined yet!
- # (and maybe we won't)
-
- def get_vpn_command(self, eipconfig=None, providerconfig=None,
- socket_host=None, socket_port="9876", openvpn_verb=1):
- """
- Returns the platform dependant vpn launching command. It will
- look for openvpn in the regular paths and algo in
- path_prefix/apps/eip/ (in case standalone is set)
-
- Might raise VPNException.
-
- :param eipconfig: eip configuration object
- :type eipconfig: EIPConfig
-
- :param providerconfig: provider specific configuration
- :type providerconfig: ProviderConfig
-
- :param socket_host: either socket path (unix) or socket IP
- :type socket_host: str
-
- :param socket_port: either string "unix" if it's a unix
- socket, or port otherwise
- :type socket_port: str
-
- :param openvpn_verb: the openvpn verbosity wanted
- :type openvpn_verb: int
-
- :return: A VPN command ready to be launched
- :rtype: list
- """
- leap_assert(eipconfig, "We need an eip config")
- leap_assert_type(eipconfig, EIPConfig)
- leap_assert(providerconfig, "We need a provider config")
- leap_assert_type(providerconfig, ProviderConfig)
- leap_assert(socket_host, "We need a socket host!")
- leap_assert(socket_port, "We need a socket port!")
- leap_assert(socket_port != "unix",
- "We cannot use unix sockets in windows!")
-
- openvpn_possibilities = which(
- self.OPENVPN_BIN,
- path_extension=os.path.join(get_path_prefix(),
- "..", "apps", "eip"))
-
- if len(openvpn_possibilities) == 0:
- raise OpenVPNNotFoundException()
-
- openvpn = first(openvpn_possibilities)
- args = []
-
- args += [
- '--setenv', "LEAPOPENVPN", "1"
- ]
-
- if openvpn_verb is not None:
- args += ['--verb', '%d' % (openvpn_verb,)]
-
- gateways = []
- leap_settings = LeapSettings()
- domain = providerconfig.get_domain()
- gateway_conf = leap_settings.get_selected_gateway(domain)
-
- if gateway_conf == leap_settings.GATEWAY_AUTOMATIC:
- gateway_selector = VPNGatewaySelector(eipconfig)
- gateways = gateway_selector.get_gateways()
- else:
- gateways = [gateway_conf]
-
- if not gateways:
- logger.error('No gateway was found!')
- raise VPNLauncherException(self.tr('No gateway was found!'))
-
- logger.debug("Using gateways ips: {0}".format(', '.join(gateways)))
-
- for gw in gateways:
- args += ['--remote', gw, '1194', 'udp']
-
- args += [
- '--client',
- '--dev', 'tun',
- ##############################################################
- # persist-tun makes ping-restart fail because it leaves a
- # broken routing table
- ##############################################################
- # '--persist-tun',
- '--persist-key',
- '--tls-client',
- # We make it log to a file because we cannot attach to the
- # openvpn process' stdout since it's a process with more
- # privileges than we are
- '--log-append', 'eip.log',
- '--remote-cert-tls',
- 'server'
- ]
-
- openvpn_configuration = eipconfig.get_openvpn_configuration()
- for key, value in openvpn_configuration.items():
- args += ['--%s' % (key,), value]
-
- ##############################################################
- # The down-root plugin fails in some situations, so we don't
- # drop privs for the time being
- ##############################################################
- # args += [
- # '--user', getpass.getuser(),
- # #'--group', grp.getgrgid(os.getgroups()[-1]).gr_name
- # ]
-
- args += [
- '--management-signal',
- '--management', socket_host, socket_port,
- '--script-security', '2'
- ]
-
- args += [
- '--cert', eipconfig.get_client_cert_path(providerconfig),
- '--key', eipconfig.get_client_cert_path(providerconfig),
- '--ca', providerconfig.get_ca_cert_path()
- ]
-
- logger.debug("Running VPN with command:")
- logger.debug("%s %s" % (openvpn, " ".join(args)))
-
- return [openvpn] + args
-
- def get_vpn_env(self):
- """
- Returns a dictionary with the custom env for the platform.
- This is mainly used for setting LD_LIBRARY_PATH to the correct
- path when distributing a standalone client
-
- :rtype: dict
- """
- return {}
-
-
-if __name__ == "__main__":
- logger = logging.getLogger(name='leap')
- logger.setLevel(logging.DEBUG)
- console = logging.StreamHandler()
- console.setLevel(logging.DEBUG)
- formatter = logging.Formatter(
- '%(asctime)s '
- '- %(name)s - %(levelname)s - %(message)s')
- console.setFormatter(formatter)
- logger.addHandler(console)
-
- try:
- abs_launcher = VPNLauncher()
- except Exception as e:
- assert isinstance(e, TypeError), "Something went wrong"
- print "Abstract Prefixer class is working as expected"
-
- vpnlauncher = get_platform_launcher()
-
- eipconfig = EIPConfig()
- eipconfig.set_api_version('1')
- if eipconfig.load("leap/providers/bitmask.net/eip-service.json"):
- provider = ProviderConfig()
- if provider.load("leap/providers/bitmask.net/provider.json"):
- vpnlauncher.get_vpn_command(eipconfig=eipconfig,
- providerconfig=provider,
- socket_host="/blah")
diff --git a/src/leap/bitmask/services/eip/vpnprocess.py b/src/leap/bitmask/services/eip/vpnprocess.py
index 15ac812b..707967e0 100644
--- a/src/leap/bitmask/services/eip/vpnprocess.py
+++ b/src/leap/bitmask/services/eip/vpnprocess.py
@@ -27,7 +27,7 @@ import socket
from PySide import QtCore
from leap.bitmask.config.providerconfig import ProviderConfig
-from leap.bitmask.services.eip.vpnlaunchers import get_platform_launcher
+from leap.bitmask.services.eip import get_vpn_launcher
from leap.bitmask.services.eip.eipconfig import EIPConfig
from leap.bitmask.services.eip.udstelnet import UDSTelnet
from leap.bitmask.util import first
@@ -697,7 +697,7 @@ class VPNProcess(protocol.ProcessProtocol, VPNManager):
self._socket_host = socket_host
self._socket_port = socket_port
- self._launcher = get_platform_launcher()
+ self._launcher = get_vpn_launcher()
self._last_state = None
self._last_status = None
diff --git a/src/leap/bitmask/services/eip/windowsvpnlauncher.py b/src/leap/bitmask/services/eip/windowsvpnlauncher.py
new file mode 100644
index 00000000..3f1ed43b
--- /dev/null
+++ b/src/leap/bitmask/services/eip/windowsvpnlauncher.py
@@ -0,0 +1,69 @@
+# -*- coding: utf-8 -*-
+# windowsvpnlauncher.py
+# Copyright (C) 2013 LEAP
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program 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 General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+"""
+Windows VPN launcher implementation.
+"""
+import logging
+
+from leap.bitmask.services.eip.vpnlauncher import VPNLauncher
+from leap.common.check import leap_assert
+
+logger = logging.getLogger(__name__)
+
+
+class WindowsVPNLauncher(VPNLauncher):
+ """
+ VPN launcher for the Windows platform
+ """
+
+ OPENVPN_BIN = 'openvpn_leap.exe'
+
+ # XXX UPDOWN_FILES ... we do not have updown files defined yet!
+ # (and maybe we won't)
+ @classmethod
+ def get_vpn_command(kls, eipconfig, providerconfig, socket_host,
+ socket_port="9876", openvpn_verb=1):
+ """
+ Returns the Windows implementation for the vpn launching command.
+
+ Might raise:
+ OpenVPNNotFoundException,
+ VPNLauncherException.
+
+ :param eipconfig: eip configuration object
+ :type eipconfig: EIPConfig
+ :param providerconfig: provider specific configuration
+ :type providerconfig: ProviderConfig
+ :param socket_host: either socket path (unix) or socket IP
+ :type socket_host: str
+ :param socket_port: either string "unix" if it's a unix socket,
+ or port otherwise
+ :type socket_port: str
+ :param openvpn_verb: the openvpn verbosity wanted
+ :type openvpn_verb: int
+
+ :return: A VPN command ready to be launched.
+ :rtype: list
+ """
+ leap_assert(socket_port != "unix",
+ "We cannot use unix sockets in windows!")
+
+ # we use `super` in order to send the class to use
+ command = super(WindowsVPNLauncher, kls).get_vpn_command(
+ eipconfig, providerconfig, socket_host, socket_port, openvpn_verb)
+
+ return command
diff --git a/src/leap/bitmask/util/constants.py b/src/leap/bitmask/util/constants.py
index 63f6b1f7..e6a6bdce 100644
--- a/src/leap/bitmask/util/constants.py
+++ b/src/leap/bitmask/util/constants.py
@@ -16,4 +16,4 @@
# along with this program. If not, see <http://www.gnu.org/licenses/>.
SIGNUP_TIMEOUT = 5
-REQUEST_TIMEOUT = 10
+REQUEST_TIMEOUT = 15