summaryrefslogtreecommitdiff
path: root/src/leap
diff options
context:
space:
mode:
authorKali Kaneko <kali@leap.se>2013-08-01 11:01:16 +0200
committerKali Kaneko <kali@leap.se>2013-08-01 11:01:16 +0200
commit8f3e7332e3fa0b7245eba878456f93a2e11ecbbf (patch)
treedd64e1c2d43b6c59b99a53a8938f769299adecfc /src/leap
parentb39584175fda25d812dbb90d3fa171f7a50e3914 (diff)
parent6f11db8234a7236f6dce0c4611dffa3068dc5036 (diff)
Merge branch 'reintegrate-soledad' into develop
Diffstat (limited to 'src/leap')
-rw-r--r--src/leap/gui/mainwindow.py24
-rw-r--r--src/leap/platform_init/initializers.py42
-rw-r--r--src/leap/services/eip/tests/test_providerbootstrapper.py53
-rw-r--r--src/leap/services/eip/vpnlaunchers.py44
-rw-r--r--src/leap/services/soledad/soledadbootstrapper.py48
-rw-r--r--src/leap/util/privilege_policies.py87
6 files changed, 207 insertions, 91 deletions
diff --git a/src/leap/gui/mainwindow.py b/src/leap/gui/mainwindow.py
index f333d7ec..1c871aa2 100644
--- a/src/leap/gui/mainwindow.py
+++ b/src/leap/gui/mainwindow.py
@@ -42,8 +42,8 @@ from leap.gui.statuspanel import StatusPanelWidget
from leap.services.eip.eipbootstrapper import EIPBootstrapper
from leap.services.eip.eipconfig import EIPConfig
from leap.services.eip.providerbootstrapper import ProviderBootstrapper
-# XXX: comment out soledad temporarily to avoid problem in Windows, issue #2932
-# from leap.services.soledad.soledadbootstrapper import SoledadBootstrapper
+# XXX: Soledad might not work out of the box in Windows, issue #2932
+from leap.services.soledad.soledadbootstrapper import SoledadBootstrapper
from leap.services.mail.smtpbootstrapper import SMTPBootstrapper
from leap.platform_init import IS_WIN, IS_MAC
from leap.platform_init.initializers import init_platform
@@ -199,11 +199,11 @@ class MainWindow(QtGui.QMainWindow):
self._eip_bootstrapper.download_client_certificate.connect(
self._finish_eip_bootstrap)
- #self._soledad_bootstrapper = SoledadBootstrapper()
- #self._soledad_bootstrapper.download_config.connect(
- #self._soledad_intermediate_stage)
- #self._soledad_bootstrapper.gen_key.connect(
- #self._soledad_bootstrapped_stage)
+ self._soledad_bootstrapper = SoledadBootstrapper()
+ self._soledad_bootstrapper.download_config.connect(
+ self._soledad_intermediate_stage)
+ self._soledad_bootstrapper.gen_key.connect(
+ self._soledad_bootstrapped_stage)
self._smtp_bootstrapper = SMTPBootstrapper()
self._smtp_bootstrapper.download_config.connect(
@@ -867,11 +867,11 @@ class MainWindow(QtGui.QMainWindow):
self.ui.stackedWidget.setCurrentIndex(self.EIP_STATUS_INDEX)
# XXX disabling soledad for now
- #self._soledad_bootstrapper.run_soledad_setup_checks(
- #self._provider_config,
- #self._login_widget.get_user(),
- #self._login_widget.get_password(),
- #download_if_needed=True)
+ self._soledad_bootstrapper.run_soledad_setup_checks(
+ self._provider_config,
+ self._login_widget.get_user(),
+ self._login_widget.get_password(),
+ download_if_needed=True)
self._download_eip_config()
diff --git a/src/leap/platform_init/initializers.py b/src/leap/platform_init/initializers.py
index d04daca6..3523c117 100644
--- a/src/leap/platform_init/initializers.py
+++ b/src/leap/platform_init/initializers.py
@@ -31,7 +31,7 @@ from PySide import QtGui
from leap.config.leapsettings import LeapSettings
from leap.services.eip import vpnlaunchers
from leap.util import first
-from leap.config.providerconfig import ProviderConfig
+from leap.util import privilege_policies
logger = logging.getLogger(__name__)
@@ -331,36 +331,6 @@ def DarwinInitializer():
#
# Linux initializers
#
-
-POLICY_TEMPLATE = """<?xml version="1.0" encoding="UTF-8"?>
-<!DOCTYPE policyconfig PUBLIC
- "-//freedesktop//DTD PolicyKit Policy Configuration 1.0//EN"
- "http://www.freedesktop.org/standards/PolicyKit/1/policyconfig.dtd">
-<policyconfig>
-
- <vendor>LEAP Project</vendor>
- <vendor_url>http://leap.se/</vendor_url>
-
- <action id="net.openvpn.gui.leap.run-openvpn">
- <description>Runs the openvpn binary</description>
- <description xml:lang="es">Ejecuta el binario openvpn</description>
- <message>OpenVPN needs that you authenticate to start</message>
- <message xml:lang="es">
- OpenVPN necesita autorizacion para comenzar
- </message>
- <icon_name>package-x-generic</icon_name>
- <defaults>
- <allow_any>yes</allow_any>
- <allow_inactive>yes</allow_inactive>
- <allow_active>yes</allow_active>
- </defaults>
- <annotate key="org.freedesktop.policykit.exec.path">{path}</annotate>
- <annotate key="org.freedesktop.policykit.exec.allow_gui">true</annotate>
- </action>
-</policyconfig>
-"""
-
-
def _linux_install_missing_scripts(badexec, notfound):
"""
Tries to install the missing up/down scripts.
@@ -381,14 +351,8 @@ def _linux_install_missing_scripts(badexec, notfound):
fd, tempscript = tempfile.mkstemp(prefix="leap_installer-")
polfd, pol_tempfile = tempfile.mkstemp(prefix="leap_installer-")
try:
- # We need to do the config/../apps/openvpn otherwise the
- # policy file won't work
- openvpn_path = os.path.join(
- ProviderConfig().get_path_prefix(),
- "..", "apps", "eip",
- launcher.OPENVPN_BIN)
-
- policy_contents = POLICY_TEMPLATE.format(path=openvpn_path)
+ path = launcher.OPENVPN_BIN_PATH
+ policy_contents = privilege_policies.get_policy_contents(path)
with os.fdopen(polfd, 'w') as f:
f.write(policy_contents)
diff --git a/src/leap/services/eip/tests/test_providerbootstrapper.py b/src/leap/services/eip/tests/test_providerbootstrapper.py
index cd740793..b24334a2 100644
--- a/src/leap/services/eip/tests/test_providerbootstrapper.py
+++ b/src/leap/services/eip/tests/test_providerbootstrapper.py
@@ -43,6 +43,7 @@ from leap.common.testing.https_server import where
from leap.common.testing.basetest import BaseLeapTest
from leap.services.eip.providerbootstrapper import ProviderBootstrapper
from leap.services.eip.providerbootstrapper import UnsupportedProviderAPI
+from leap.services.eip.providerbootstrapper import WrongFingerprint
from leap.provider.supportedapis import SupportedAPIs
from leap.config.providerconfig import ProviderConfig
from leap.crypto.tests import fake_provider
@@ -194,7 +195,7 @@ class ProviderBootstrapperTest(BaseLeapTest):
self.pb._should_proceed_cert = mock.MagicMock(return_value=True)
- with self.assertRaises(AssertionError):
+ with self.assertRaises(WrongFingerprint):
self.pb._check_ca_fingerprint()
# This two hashes different in the last byte, but that's good enough
@@ -282,7 +283,7 @@ yV8e
self.pb._should_proceed_cert = mock.MagicMock(return_value=True)
- with self.assertRaises(AssertionError):
+ with self.assertRaises(WrongFingerprint):
self.pb._check_ca_fingerprint()
os.unlink(cert_path)
@@ -412,6 +413,15 @@ class ProviderBootstrapperActiveTest(unittest.TestCase):
p.write("A")
return provider_path
+ def test_download_provider_info_new_provider(self):
+ self._setup_provider_config_with("1", tempfile.mkdtemp())
+ self._setup_providerbootstrapper(True)
+
+ self.pb._download_provider_info()
+ self.assertTrue(ProviderConfig.save.called)
+
+ @mock.patch('leap.config.providerconfig.ProviderConfig.get_ca_cert_path',
+ lambda x: where('cacert.pem'))
def test_download_provider_info_not_modified(self):
self._setup_provider_config_with("1", tempfile.mkdtemp())
self._setup_providerbootstrapper(True)
@@ -420,12 +430,16 @@ class ProviderBootstrapperActiveTest(unittest.TestCase):
# set mtime to something really new
os.utime(provider_path, (-1, time.time()))
- self.pb._download_provider_info()
- # we check that it doesn't do anything with the provider
+ with mock.patch.object(
+ ProviderConfig, 'get_api_uri',
+ return_value="https://localhost:%s" % (self.https_port,)):
+ self.pb._download_provider_info()
+ # we check that it doesn't save the provider
# config, because it's new enough
- self.assertFalse(ProviderConfig.load.called)
self.assertFalse(ProviderConfig.save.called)
+ @mock.patch('leap.config.providerconfig.ProviderConfig.get_ca_cert_path',
+ lambda x: where('cacert.pem'))
def test_download_provider_info_modified(self):
self._setup_provider_config_with("1", tempfile.mkdtemp())
self._setup_providerbootstrapper(True)
@@ -434,32 +448,45 @@ class ProviderBootstrapperActiveTest(unittest.TestCase):
# set mtime to something really old
os.utime(provider_path, (-1, 100))
- self.pb._download_provider_info()
+ with mock.patch.object(
+ ProviderConfig, 'get_api_uri',
+ return_value="https://localhost:%s" % (self.https_port,)):
+ self.pb._download_provider_info()
self.assertTrue(ProviderConfig.load.called)
self.assertTrue(ProviderConfig.save.called)
+ @mock.patch('leap.config.providerconfig.ProviderConfig.get_ca_cert_path',
+ lambda x: where('cacert.pem'))
def test_download_provider_info_unsupported_api_raises(self):
self._setup_provider_config_with("9999999", tempfile.mkdtemp())
self._setup_providerbootstrapper(False)
self._produce_dummy_provider_json()
- with self.assertRaises(UnsupportedProviderAPI):
- self.pb._download_provider_info()
+ with mock.patch.object(
+ ProviderConfig, 'get_api_uri',
+ return_value="https://localhost:%s" % (self.https_port,)):
+ with self.assertRaises(UnsupportedProviderAPI):
+ self.pb._download_provider_info()
+ @mock.patch('leap.config.providerconfig.ProviderConfig.get_ca_cert_path',
+ lambda x: where('cacert.pem'))
def test_download_provider_info_unsupported_api(self):
self._setup_provider_config_with(SupportedAPIs.SUPPORTED_APIS[0],
tempfile.mkdtemp())
self._setup_providerbootstrapper(False)
self._produce_dummy_provider_json()
- self.pb._download_provider_info()
+ with mock.patch.object(
+ ProviderConfig, 'get_api_uri',
+ return_value="https://localhost:%s" % (self.https_port,)):
+ self.pb._download_provider_info()
+ @mock.patch('leap.config.providerconfig.ProviderConfig.get_api_uri',
+ lambda x: 'api.uri')
+ @mock.patch('leap.config.providerconfig.ProviderConfig.get_ca_cert_path',
+ lambda x: '/cert/path')
def test_check_api_certificate_skips(self):
self.pb._provider_config = ProviderConfig()
- self.pb._provider_config.get_api_uri = mock.MagicMock(
- return_value="api.uri")
- self.pb._provider_config.get_ca_cert_path = mock.MagicMock(
- return_value="/cert/path")
self.pb._session.get = mock.MagicMock(return_value=Response())
self.pb._should_proceed_cert = mock.MagicMock(return_value=False)
diff --git a/src/leap/services/eip/vpnlaunchers.py b/src/leap/services/eip/vpnlaunchers.py
index 8522d1df..b591b3ca 100644
--- a/src/leap/services/eip/vpnlaunchers.py
+++ b/src/leap/services/eip/vpnlaunchers.py
@@ -38,6 +38,8 @@ from leap.common.files import which
from leap.config.providerconfig import ProviderConfig
from leap.services.eip.eipconfig import EIPConfig, VPNGatewaySelector
from leap.util import first
+from leap.util.privilege_policies import LinuxPolicyChecker
+from leap.util import privilege_policies
logger = logging.getLogger(__name__)
@@ -62,7 +64,7 @@ class EIPNoTunKextLoaded(VPNLauncherException):
pass
-class VPNLauncher:
+class VPNLauncher(object):
"""
Abstract launcher class
"""
@@ -237,6 +239,10 @@ class LinuxVPNLauncher(VPNLauncher):
PKEXEC_BIN = 'pkexec'
OPENVPN_BIN = 'openvpn'
+ OPENVPN_BIN_PATH = os.path.join(
+ ProviderConfig().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)
@@ -250,12 +256,26 @@ class LinuxVPNLauncher(VPNLauncher):
OPENVPN_DOWN_ROOT_BASE,
OPENVPN_DOWN_ROOT_FILE)
- POLKIT_BASE = "/usr/share/polkit-1/actions"
- POLKIT_FILE = "net.openvpn.gui.leap.policy"
- POLKIT_PATH = "%s/%s" % (POLKIT_BASE, POLKIT_FILE)
-
UPDOWN_FILES = (UP_DOWN_PATH,)
- OTHER_FILES = (POLKIT_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):
@@ -271,11 +291,13 @@ class LinuxVPNLauncher(VPNLauncher):
:rtype: str
"""
to = kls.SYSTEM_CONFIG
- cmd = "#!/bin/sh\nset -e\nmkdir -p %s\n"
- cmd = (cmd + "cp %s/%s %s\ncp \"%s\" \"%s\"") % (
- to,
- frompath, kls.UP_DOWN_FILE, to,
- pol_file, kls.POLKIT_PATH)
+
+ cmd = '#!/bin/sh\nset -e\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
diff --git a/src/leap/services/soledad/soledadbootstrapper.py b/src/leap/services/soledad/soledadbootstrapper.py
index 46e985ad..ac063152 100644
--- a/src/leap/services/soledad/soledadbootstrapper.py
+++ b/src/leap/services/soledad/soledadbootstrapper.py
@@ -86,21 +86,32 @@ class SoledadBootstrapper(AbstractBootstrapper):
secrets_path = "%s/%s.secret" % (prefix, uuid)
local_db_path = "%s/%s.db" % (prefix, uuid)
- # TODO: use the proper URL
- #server_url = 'https://mole.dev.bitmask.net:2424/user-%s' % (uuid,)
- server_url = 'https://gadwall.dev.bitmask.net:1111/user-%s' % (uuid,)
- # server_url = self._soledad_config.get_hosts(...)
-
- cert_file = self._provider_config.get_ca_cert_path()
-
- self._soledad = Soledad(uuid,
- self._password.encode("utf-8"),
- secrets_path=secrets_path,
- local_db_path=local_db_path,
- server_url=server_url,
- cert_file=cert_file,
- auth_token=srp_auth.get_token())
- self._soledad.sync()
+ # TODO: Select server based on timezone (issue #3308)
+ server_dict = self._soledad_config.get_hosts()
+
+ if len(server_dict.keys() > 0):
+ selected_server = server_dict[server_dict.keys()[0]]
+ server_url = "https://%s:%s/user-%s" % (selected_server["hostname"],
+ selected_server["port"],
+ uuid)
+
+ logger.debug("Using soledad server url: %s" % (server_url,))
+
+ cert_file = self._provider_config.get_ca_cert_path()
+
+ # TODO: If selected server fails, retry with another host
+ # (issue #3309)
+ self._soledad = Soledad(uuid,
+ self._password.encode("utf-8"),
+ secrets_path=secrets_path,
+ local_db_path=local_db_path,
+ server_url=server_url,
+ cert_file=cert_file,
+ auth_token=srp_auth.get_token())
+
+ self._soledad.sync()
+ else:
+ raise Exception("No soledad server found")
def _download_config(self):
"""
@@ -148,6 +159,10 @@ class SoledadBootstrapper(AbstractBootstrapper):
# Not modified
if res.status_code == 304:
logger.debug("Soledad definition has not been modified")
+ self._soledad_config.load(os.path.join("leap",
+ "providers",
+ self._provider_config.get_domain(),
+ "soledad-service.json"))
else:
soledad_definition, mtime = get_content(res)
@@ -159,7 +174,7 @@ class SoledadBootstrapper(AbstractBootstrapper):
self._load_and_sync_soledad(srp_auth)
- def _gen_key(self):
+ def _gen_key(self, _):
"""
Generates the key pair if needed, uploads it to the webapp and
nickserver
@@ -188,6 +203,7 @@ class SoledadBootstrapper(AbstractBootstrapper):
except KeyNotFound:
logger.debug("Key not found. Generating key for %s" % (address,))
self._keymanager.gen_key(openpgp.OpenPGPKey)
+ self._keymanager.send_key(openpgp.OpenPGPKey)
logger.debug("Key generated successfully.")
def run_soledad_setup_checks(self,
diff --git a/src/leap/util/privilege_policies.py b/src/leap/util/privilege_policies.py
index 10224bcd..72442553 100644
--- a/src/leap/util/privilege_policies.py
+++ b/src/leap/util/privilege_policies.py
@@ -27,6 +27,35 @@ from abc import ABCMeta, abstractmethod
logger = logging.getLogger(__name__)
+POLICY_TEMPLATE = """<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE policyconfig PUBLIC
+ "-//freedesktop//DTD PolicyKit Policy Configuration 1.0//EN"
+ "http://www.freedesktop.org/standards/PolicyKit/1/policyconfig.dtd">
+<policyconfig>
+
+ <vendor>LEAP Project</vendor>
+ <vendor_url>https://leap.se/</vendor_url>
+
+ <action id="net.openvpn.gui.leap.run-openvpn">
+ <description>Runs the openvpn binary</description>
+ <description xml:lang="es">Ejecuta el binario openvpn</description>
+ <message>OpenVPN needs that you authenticate to start</message>
+ <message xml:lang="es">
+ OpenVPN necesita autorizacion para comenzar
+ </message>
+ <icon_name>package-x-generic</icon_name>
+ <defaults>
+ <allow_any>yes</allow_any>
+ <allow_inactive>yes</allow_inactive>
+ <allow_active>yes</allow_active>
+ </defaults>
+ <annotate key="org.freedesktop.policykit.exec.path">{path}</annotate>
+ <annotate key="org.freedesktop.policykit.exec.allow_gui">true</annotate>
+ </action>
+</policyconfig>
+"""
+
+
def is_missing_policy_permissions():
"""
Returns True if we do not have implemented a policy checker for this
@@ -47,6 +76,36 @@ def is_missing_policy_permissions():
return policy_checker().is_missing_policy_permissions()
+def get_policy_contents(openvpn_path):
+ """
+ Returns the contents that the policy file should have.
+
+ :param openvpn_path: the openvpn path to use in the polkit file
+ :type openvpn_path: str
+ :rtype: str
+ """
+ return POLICY_TEMPLATE.format(path=openvpn_path)
+
+
+def is_policy_outdated(path):
+ """
+ Returns if the existing polkit file is outdated, comparing if the path
+ is correct.
+
+ :param path: the path that should have the polkit file.
+ :type path: str.
+ :rtype: bool
+ """
+ _system = platform.system()
+ platform_checker = _system + "PolicyChecker"
+ policy_checker = globals().get(platform_checker, None)
+ if policy_checker is None:
+ logger.debug("we could not find a policy checker implementation "
+ "for %s" % (_system,))
+ return False
+ return policy_checker().is_outdated(path)
+
+
class PolicyChecker:
"""
Abstract PolicyChecker class
@@ -72,6 +131,15 @@ class LinuxPolicyChecker(PolicyChecker):
LINUX_POLKIT_FILE = ("/usr/share/polkit-1/actions/"
"net.openvpn.gui.leap.policy")
+ @classmethod
+ def get_polkit_path(self):
+ """
+ Returns the polkit file path.
+
+ :rtype: str
+ """
+ return self.LINUX_POLKIT_FILE
+
def is_missing_policy_permissions(self):
"""
Returns True if we could not find the appropriate policykit file
@@ -80,3 +148,22 @@ class LinuxPolicyChecker(PolicyChecker):
:rtype: bool
"""
return not os.path.isfile(self.LINUX_POLKIT_FILE)
+
+ def is_outdated(self, path):
+ """
+ Returns if the existing polkit file is outdated, comparing if the path
+ is correct.
+
+ :param path: the path that should have the polkit file.
+ :type path: str.
+ :rtype: bool
+ """
+ polkit = None
+ try:
+ with open(self.LINUX_POLKIT_FILE) as f:
+ polkit = f.read()
+ except IOError, e:
+ logger.error("Error reading polkit file(%s): %r" % (
+ self.LINUX_POLKIT_FILE, e))
+
+ return get_policy_contents(path) != polkit