summaryrefslogtreecommitdiff
path: root/src/leap
diff options
context:
space:
mode:
authorkali <kali@leap.se>2012-09-20 03:15:13 +0900
committerkali <kali@leap.se>2012-09-20 03:15:13 +0900
commitdb704eecf513fcc48365844523557d6edab28293 (patch)
treef37516c86c5d77f23f7e57d80ee4c39b3b651c3e /src/leap
parentadd7973b3d1633b2776cb90f237415c6cac65d99 (diff)
parent50396fca082652dd1a1617e0d029c1c726e4c651 (diff)
Merge branch 'develop' into feature/systray-menu
Conflicts: src/leap/app.py
Diffstat (limited to 'src/leap')
-rw-r--r--src/leap/__init__.py5
-rw-r--r--src/leap/base/config.py202
-rw-r--r--src/leap/base/constants.py15
-rw-r--r--src/leap/base/providers.py2
-rw-r--r--src/leap/base/tests/test_config.py64
-rw-r--r--src/leap/base/tests/test_providers.py6
-rw-r--r--src/leap/baseapp/eip.py21
-rw-r--r--src/leap/certs/__init__.py7
-rw-r--r--src/leap/eip/checks.py125
-rw-r--r--src/leap/eip/config.py66
-rw-r--r--src/leap/eip/constants.py2
-rw-r--r--src/leap/eip/eipconnection.py26
-rw-r--r--src/leap/eip/exceptions.py15
-rw-r--r--src/leap/eip/openvpnconnection.py7
-rw-r--r--src/leap/eip/specs.py10
-rw-r--r--src/leap/eip/tests/data.py14
-rw-r--r--src/leap/eip/tests/test_checks.py4
-rw-r--r--src/leap/eip/tests/test_config.py26
18 files changed, 401 insertions, 216 deletions
diff --git a/src/leap/__init__.py b/src/leap/__init__.py
index 75bddd6d..5e003931 100644
--- a/src/leap/__init__.py
+++ b/src/leap/__init__.py
@@ -28,3 +28,8 @@ except ImportError:
pass
__full_version__ = __appname__ + '/' + str(__version__)
+
+try:
+ from leap._branding import BRANDING as __branding
+except ImportError:
+ __branding = {}
diff --git a/src/leap/base/config.py b/src/leap/base/config.py
index 5a52637c..a468a849 100644
--- a/src/leap/base/config.py
+++ b/src/leap/base/config.py
@@ -18,6 +18,9 @@ from leap.base import exceptions
from leap.base import constants
from leap.util.fileutil import (mkdir_p)
+# move to base!
+from leap.eip import exceptions as eipexceptions
+
class BaseLeapConfig(object):
slug = None
@@ -145,9 +148,9 @@ class JSONLeapConfig(BaseLeapConfig):
config[k] = v()
self._config.serialize(to)
- def load(self, fromfile=None, from_uri=None, fetcher=None):
+ def load(self, fromfile=None, from_uri=None, fetcher=None, verify=False):
if from_uri is not None:
- fetched = self.fetch(from_uri, fetcher=fetcher)
+ fetched = self.fetch(from_uri, fetcher=fetcher, verify=verify)
if fetched:
return
if fromfile is None:
@@ -156,12 +159,21 @@ class JSONLeapConfig(BaseLeapConfig):
# XXX check for no errors, etc
self._config.config = newconfig
- def fetch(self, uri, fetcher=None):
+ def fetch(self, uri, fetcher=None, verify=True):
if not fetcher:
fetcher = self.fetcher
- request = fetcher.get(uri)
+ logger.debug('verify: %s', verify)
+ request = fetcher.get(uri, verify=verify)
+
+ # XXX get 404, ...
+ # and raise a UnableToFetch...
request.raise_for_status()
fd, fname = tempfile.mkstemp(suffix=".json")
+ if not request.json:
+ try:
+ json.loads(request.content)
+ except ValueError:
+ raise eipexceptions.LeapBadConfigFetchedError
with open(fname, 'w') as tmp:
tmp.write(json.dumps(request.json))
self._loadtemp(fname)
@@ -239,7 +251,7 @@ def get_config_file(filename, folder=None):
def get_default_provider_path():
default_subpath = os.path.join("providers",
- constants.DEFAULT_TEST_PROVIDER)
+ constants.DEFAULT_PROVIDER)
default_provider_path = get_config_file(
'',
folder=default_subpath)
@@ -266,55 +278,55 @@ def get_groupname():
# json stuff
# XXX merge with JSONConfig / EIPChecks as appropiate.
-def get_config_json(config_file=None):
- """
- will replace get_config function be developing them
- in parralel for branch purposes.
- @param: configuration file
- @type: file
- @rparam: configuration turples
- @rtype: dictionary
- """
- if not config_file:
+#def get_config_json(config_file=None):
+ #"""
+ #will replace get_config function be developing them
+ #in parralel for branch purposes.
+ #@param: configuration file
+ #@type: file
+ #@rparam: configuration turples
+ #@rtype: dictionary
+ #"""
+ #if not config_file:
#TODO: NOT SURE WHAT this default should be, if anything
- fpath = get_config_file('eip.json')
- if not os.path.isfile(fpath):
- dpath, cfile = os.path.split(fpath)
- if not os.path.isdir(dpath):
- mkdir_p(dpath)
- with open(fpath, 'wb') as configfile:
- configfile.flush()
- try:
- return json.load(open(fpath))
- except ValueError:
- raise exceptions.MissingConfigFileError
-
- else:
+ #fpath = get_config_file('eip.json')
+ #if not os.path.isfile(fpath):
+ #dpath, cfile = os.path.split(fpath)
+ #if not os.path.isdir(dpath):
+ #mkdir_p(dpath)
+ #with open(fpath, 'wb') as configfile:
+ #configfile.flush()
+ #try:
+ #return json.load(open(fpath))
+ #except ValueError:
+ #raise exceptions.MissingConfigFileError
+#
+ #else:
#TODO: add validity checks of file
- try:
- return json.load(open(config_file))
- except IOError:
- raise exceptions.MissingConfigFileError
-
-
-def get_definition_file(url=None):
- """
- """
+ #try:
+ #return json.load(open(config_file))
+ #except IOError:
+ #raise exceptions.MissingConfigFileError
+#
+#
+#def get_definition_file(url=None):
+ #"""
+ #"""
#TODO: determine good default location of definition file.
- r = requests.get(url)
- return r.json
-
-
-def is_internet_up():
- """TODO: Build more robust network diagnosis capabilities
- """
- try:
- requests.get('http://128.30.52.45', timeout=1)
- return True
- except requests.Timeout: # as err:
- pass
- return False
-
+ #r = requests.get(url)
+ #return r.json
+#
+#
+#def is_internet_up():
+ #"""TODO: Build more robust network diagnosis capabilities
+ #"""
+ #try:
+ #requests.get('http://128.30.52.45', timeout=1)
+ #return True
+ #except requests.Timeout: # as err:
+ #pass
+ #return False
+#
# XXX DEPRECATE.
# move to eip.checks
#
@@ -323,49 +335,49 @@ def is_internet_up():
# moving it here transiently until I clean merge commit.
# -- kali 2012-08-24 00:32
#
-
-
-class Configuration(object):
- """
- All configurations (providers et al) will be managed in this class.
- """
- def __init__(self, provider_url=None):
- try:
+#
+#
+#class Configuration(object):
+ #"""
+ #All configurations (providers et al) will be managed in this class.
+ #"""
+ #def __init__(self, provider_url=None):
+ #try:
#requests.get('foo')
- self.providers = {}
- self.error = False
- provider_file = self.check_and_get_definition_file(provider_url)
- self.providers['default'] = get_config_json(provider_file)
- except (requests.HTTPError, requests.RequestException) as e:
- self.error = e.message
- except requests.ConnectionError as e:
- if e.message == "[Errno 113] No route to host":
- if not is_internet_up:
+ #self.providers = {}
+ #self.error = False
+ #provider_file = self.check_and_get_definition_file(provider_url)
+ #self.providers['default'] = get_config_json(provider_file)
+ #except (requests.HTTPError, requests.RequestException) as e:
+ #self.error = e.message
+ #except requests.ConnectionError as e:
+ #if e.message == "[Errno 113] No route to host":
+ #if not is_internet_up:
# this was meant to be a function invocation I guess...
- self.error = "No valid internet connection found"
- else:
- self.error = "Provider server appears currently down."
-
- def check_and_get_definition_file(self, provider_url):
- """
- checks if provider definition.json file is present.
- if not downloads one from the web.
- """
- default_provider_path = get_default_provider_path()
-
- if not os.path.isdir(default_provider_path):
- mkdir_p(default_provider_path)
-
- definition_file = get_config_file(
- 'definition.json',
- folder=default_provider_path)
-
- if os.path.isfile(definition_file):
- return
-
- else:
- r = requests.get(provider_url)
- r.raise_for_status()
- with open(definition_file, 'wb') as f:
- f.write(json.dumps(r.json, indent=4))
- return definition_file
+ #self.error = "No valid internet connection found"
+ #else:
+ #self.error = "Provider server appears currently down."
+#
+ #def check_and_get_definition_file(self, provider_url):
+ #"""
+ #checks if provider definition.json file is present.
+ #if not downloads one from the web.
+ #"""
+ #default_provider_path = get_default_provider_path()
+#
+ #if not os.path.isdir(default_provider_path):
+ #mkdir_p(default_provider_path)
+#
+ #definition_file = get_config_file(
+ #'definition.json',
+ #folder=default_provider_path)
+#
+ #if os.path.isfile(definition_file):
+ #return
+#
+ #else:
+ #r = requests.get(provider_url)
+ #r.raise_for_status()
+ #with open(definition_file, 'wb') as f:
+ #f.write(json.dumps(r.json, indent=4))
+ #return definition_file
diff --git a/src/leap/base/constants.py b/src/leap/base/constants.py
index 6266c693..7a1415fb 100644
--- a/src/leap/base/constants.py
+++ b/src/leap/base/constants.py
@@ -1,23 +1,26 @@
"""constants to be used in base module"""
-APP_NAME = "leap"
+from leap import __branding
+APP_NAME = __branding.get("short_name", "leap")
# default provider placeholder
# using `example.org` we make sure that this
# is not going to be resolved during the tests phases
# (we expect testers to add it to their /etc/hosts
-DEFAULT_TEST_PROVIDER = "testprovider.example.org"
+DEFAULT_PROVIDER = __branding.get(
+ "provider_domain",
+ "testprovider.example.org")
-DEFINITION_EXPECTED_PATH = "provider-definition.json"
+DEFINITION_EXPECTED_PATH = "provider.json"
DEFAULT_PROVIDER_DEFINITION = {
- u'api_uri': u'https://api.testprovider.example.org/',
+ u'api_uri': u'https://api.%s/' % DEFAULT_PROVIDER,
u'api_version': u'0.1.0',
u'ca_cert': u'8aab80ae4326fd30721689db813733783fe0bd7e',
- u'ca_cert_uri': u'https://testprovider.example.org/cacert.pem',
+ u'ca_cert_uri': u'https://%s/cacert.pem' % DEFAULT_PROVIDER,
u'description': {u'en': u'This is a test provider'},
u'display_name': {u'en': u'Test Provider'},
- u'domain': u'testprovider.example.org',
+ u'domain': u'%s' % DEFAULT_PROVIDER,
u'enrollment_policy': u'open',
u'public_key': u'cb7dbd679f911e85bc2e51bd44afd7308ee19c21',
u'serial': 1,
diff --git a/src/leap/base/providers.py b/src/leap/base/providers.py
index ce30d4a4..7b219cc7 100644
--- a/src/leap/base/providers.py
+++ b/src/leap/base/providers.py
@@ -9,7 +9,7 @@ class LeapProviderDefinition(baseconfig.JSONLeapConfig):
def _get_slug(self):
provider_path = baseconfig.get_default_provider_path()
return baseconfig.get_config_file(
- 'provider-definition.json',
+ 'provider.json',
folder=provider_path)
def _set_slug(self, *args, **kwargs):
diff --git a/src/leap/base/tests/test_config.py b/src/leap/base/tests/test_config.py
index 40461b99..bede5ea1 100644
--- a/src/leap/base/tests/test_config.py
+++ b/src/leap/base/tests/test_config.py
@@ -65,15 +65,15 @@ class ProviderTest(BaseLeapTest):
pass
-class BareHomeTestCase(ProviderTest):
+# XXX depreacated. similar test in eip.checks
- __name__ = "provider_config_tests_bare_home"
-
- # XXX review. is it still needed?
-
- def test_should_raise_if_missing_eip_json(self):
- with self.assertRaises(exceptions.MissingConfigFileError):
- config.get_config_json(os.path.join(self.home, 'eip.json'))
+#class BareHomeTestCase(ProviderTest):
+#
+ #__name__ = "provider_config_tests_bare_home"
+#
+ #def test_should_raise_if_missing_eip_json(self):
+ #with self.assertRaises(exceptions.MissingConfigFileError):
+ #config.get_config_json(os.path.join(self.home, 'eip.json'))
class ProviderDefinitionTestCase(ProviderTest):
@@ -94,8 +94,10 @@ class ProviderDefinitionTestCase(ProviderTest):
json.dump(eipconstants.EIP_SAMPLE_JSON, fp)
-# these tests below should move to wherever
-# we put the fetcher for provider files and related stuff.
+# these tests below should move to
+# eip.checks
+# config.Configuration has been deprecated
+
# TODO:
# - We're instantiating a ProviderTest because we're doing the home wipeoff
# on setUpClass instead of the setUp (for speedup of the general cases).
@@ -112,26 +114,26 @@ class ProviderDefinitionTestCase(ProviderTest):
# (so we can pass mock easily).
-class ProviderFetchConError(ProviderTest):
- def test_connection_error(self):
- with mock.patch.object(requests, "get") as mock_method:
- mock_method.side_effect = requests.ConnectionError
- cf = config.Configuration()
- self.assertIsInstance(cf.error, str)
-
-
-class ProviderFetchHttpError(ProviderTest):
- def test_file_not_found(self):
- with mock.patch.object(requests, "get") as mock_method:
- mock_method.side_effect = requests.HTTPError
- cf = config.Configuration()
- self.assertIsInstance(cf.error, str)
-
-
-class ProviderFetchInvalidUrl(ProviderTest):
- def test_invalid_url(self):
- cf = config.Configuration("ht")
- self.assertTrue(cf.error)
+#class ProviderFetchConError(ProviderTest):
+ #def test_connection_error(self):
+ #with mock.patch.object(requests, "get") as mock_method:
+ #mock_method.side_effect = requests.ConnectionError
+ #cf = config.Configuration()
+ #self.assertIsInstance(cf.error, str)
+#
+#
+#class ProviderFetchHttpError(ProviderTest):
+ #def test_file_not_found(self):
+ #with mock.patch.object(requests, "get") as mock_method:
+ #mock_method.side_effect = requests.HTTPError
+ #cf = config.Configuration()
+ #self.assertIsInstance(cf.error, str)
+#
+#
+#class ProviderFetchInvalidUrl(ProviderTest):
+ #def test_invalid_url(self):
+ #cf = config.Configuration("ht")
+ #self.assertTrue(cf.error)
# end provider fetch tests
@@ -218,7 +220,7 @@ class ConfigHelperFunctions(BaseLeapTest):
config.get_default_provider_path(),
os.path.expanduser(
'~/.config/leap/providers/%s/' %
- constants.DEFAULT_TEST_PROVIDER)
+ constants.DEFAULT_PROVIDER)
)
# validate ip
diff --git a/src/leap/base/tests/test_providers.py b/src/leap/base/tests/test_providers.py
index 23f63a95..9e0ff90c 100644
--- a/src/leap/base/tests/test_providers.py
+++ b/src/leap/base/tests/test_providers.py
@@ -6,9 +6,11 @@ except ImportError:
import os
+from leap import __branding as BRANDING
from leap.testing.basetest import BaseLeapTest
from leap.base import providers
+
EXPECTED_DEFAULT_CONFIG = {
"api_version": "0.1.0",
"description": "test provider",
@@ -45,8 +47,8 @@ class TestLeapProviderDefinition(BaseLeapTest):
os.path.join(
self.home,
'.config', 'leap', 'providers',
- 'testprovider.example.org',
- 'provider-definition.json'))
+ '%s' % BRANDING.get('provider_domain'),
+ 'provider.json'))
with self.assertRaises(AttributeError):
self.definition.slug = 23
diff --git a/src/leap/baseapp/eip.py b/src/leap/baseapp/eip.py
index 68bd2f24..6c147cb4 100644
--- a/src/leap/baseapp/eip.py
+++ b/src/leap/baseapp/eip.py
@@ -1,5 +1,7 @@
+from __future__ import print_function
import logging
import time
+import sys
from PyQt4 import QtCore
@@ -38,8 +40,9 @@ class EIPConductorAppMixin(object):
debug=self.debugmode,
ovpn_verbosity=opts.openvpn_verb)
- # XXX remove skip download when sample service is ready
- self.conductor.run_checks(skip_download=True)
+ # XXX get skip_download from cli flag
+ skip_download = False
+ self.conductor.run_checks(skip_download=skip_download)
self.error_check()
# XXX should receive "ready" signal
@@ -58,13 +61,11 @@ class EIPConductorAppMixin(object):
"""
logger.debug('error check')
- #####################################
- # XXX refactor in progress (by #504)
-
errq = self.conductor.error_queue
while errq.qsize() != 0:
logger.debug('%s errors left in conductor queue', errq.qsize())
- error = errq.get()
+ # we get exception and original traceback from queue
+ error, tb = errq.get()
# redundant log, debugging the loop.
logger.error('%s: %s', error.__class__.__name__, error.message)
@@ -73,10 +74,8 @@ class EIPConductorAppMixin(object):
self.handle_eip_error(error)
else:
- # This is not quite working. FIXME
- import traceback
- traceback.print_exc()
- raise error
+ # deprecated form of raising exception.
+ raise error, None, tb
if error.failfirst is True:
break
@@ -132,6 +131,8 @@ class EIPConductorAppMixin(object):
ErrorDialog(errtype="critical",
msg=message,
label="critical error")
+ elif error.warning:
+ logger.warning(error.message)
else:
dialog = ErrorDialog()
diff --git a/src/leap/certs/__init__.py b/src/leap/certs/__init__.py
new file mode 100644
index 00000000..c4d009b1
--- /dev/null
+++ b/src/leap/certs/__init__.py
@@ -0,0 +1,7 @@
+import os
+
+_where = os.path.split(__file__)[0]
+
+
+def where(filename):
+ return os.path.join(_where, filename)
diff --git a/src/leap/eip/checks.py b/src/leap/eip/checks.py
index f368c551..cf758314 100644
--- a/src/leap/eip/checks.py
+++ b/src/leap/eip/checks.py
@@ -9,6 +9,8 @@ import netifaces
import ping
import requests
+from leap import __branding as BRANDING
+from leap import certs
from leap.base import constants as baseconstants
from leap.base import providers
from leap.eip import config as eipconfig
@@ -20,6 +22,11 @@ from leap.util.fileutil import mkdir_p
logger = logging.getLogger(name=__name__)
"""
+ProviderCertChecker
+-------------------
+Checks on certificates. To be moved to base.
+docs TBD
+
EIPConfigChecker
----------
It is used from the eip conductor (a instance of EIPConnection that is
@@ -36,14 +43,15 @@ LeapNetworkChecker
------------------
Network checks. To be moved to base.
docs TBD
-
-ProviderCertChecker
--------------------
-Checks on certificates.
-docs TBD
"""
+def get_ca_cert():
+ ca_file = BRANDING.get('provider_ca_file')
+ if ca_file:
+ return certs.where(ca_file)
+
+
class LeapNetworkChecker(object):
"""
all network related checks
@@ -67,6 +75,7 @@ class LeapNetworkChecker(object):
# XXX we probably should raise an exception here?
# unless we use this as smoke test
try:
+ # XXX remove this hardcoded random ip
requests.get('http://216.172.161.165')
except (requests.HTTPError, requests.RequestException) as e:
self.error = e.message
@@ -124,7 +133,7 @@ class ProviderCertChecker(object):
"""
def __init__(self, fetcher=requests):
self.fetcher = fetcher
- self.cacert = None
+ self.cacert = get_ca_cert()
def run_all(self, checker=None, skip_download=False):
if not checker:
@@ -138,9 +147,10 @@ class ProviderCertChecker(object):
# For MVS
checker.is_there_provider_ca()
- checker.is_https_working()
- checker.check_new_cert_needed()
- #checker.download_new_client_cert()
+
+ # XXX FAKE IT!!!
+ checker.is_https_working(verify=False)
+ checker.check_new_cert_needed(verify=False)
def download_ca_cert(self):
# MVS+
@@ -159,34 +169,49 @@ class ProviderCertChecker(object):
raise NotImplementedError
def is_there_provider_ca(self):
- # XXX fake it till you make it! :P
+ from leap import certs
+ logger.debug('do we have provider_ca?')
+ cacert_path = BRANDING.get('provider_ca_file', None)
+ if not cacert_path:
+ logger.debug('False')
+ return False
+ self.cacert = certs.where(cacert_path)
+ logger.debug('True')
return True
- # enable this when we have
- # a custom "branded" bundle
- # certs package.
- try:
- from leap.custom import certs
- except ImportError:
- raise
- self.cacert = certs.where('cacert.pem')
-
def is_https_working(self, uri=None, verify=True):
+ if uri is None:
+ uri = self._get_root_uri()
# XXX raise InsecureURI or something better
+ logger.debug('is https working?')
+ logger.debug('uri: %s', uri)
assert uri.startswith('https')
if verify is True and self.cacert is not None:
+ logger.debug('verify cert: %s', self.cacert)
verify = self.cacert
- self.fetcher.get(uri, verify=verify)
- return True
+ try:
+ self.fetcher.get(uri, verify=verify)
+ except requests.exceptions.SSLError:
+ logger.debug('False!')
+ raise eipexceptions.EIPBadCertError
+ else:
+ logger.debug('True')
+ return True
- def check_new_cert_needed(self, skip_download=False):
+ def check_new_cert_needed(self, skip_download=False, verify=True):
+ logger.debug('is new cert needed?')
if not self.is_cert_valid(do_raise=False):
- self.download_new_client_cert(skip_download=skip_download)
+ logger.debug('True')
+ self.download_new_client_cert(
+ skip_download=skip_download,
+ verify=verify)
return True
+ logger.debug('False')
return False
def download_new_client_cert(self, uri=None, verify=True,
skip_download=False):
+ logger.debug('download new client cert')
if skip_download:
return True
if uri is None:
@@ -195,20 +220,28 @@ class ProviderCertChecker(object):
assert uri.startswith('https')
if verify is True and self.cacert is not None:
verify = self.cacert
- req = self.fetcher.get(uri, verify=verify)
- pemfile_content = req.content
- self.is_valid_pemfile(pemfile_content)
- cert_path = self._get_client_cert_path()
- self.write_cert(pemfile_content, to=cert_path)
+ try:
+ req = self.fetcher.get(uri, verify=verify)
+ req.raise_for_status()
+ except requests.exceptions.SSLError:
+ logger.warning('SSLError while fetching cert. '
+ 'Look below for stack trace.')
+ # XXX raise better exception
+ raise
+ try:
+ pemfile_content = req.content
+ self.is_valid_pemfile(pemfile_content)
+ cert_path = self._get_client_cert_path()
+ self.write_cert(pemfile_content, to=cert_path)
+ except:
+ logger.warning('Error while validating cert')
+ raise
return True
def is_cert_valid(self, cert_path=None, do_raise=True):
exists = lambda: self.is_certificate_exists()
valid_pemfile = lambda: self.is_valid_pemfile()
not_expired = lambda: self.is_cert_not_expired()
- #print 'exists?', exists
- #print 'valid', valid_pemfile
- #print 'not expired', not_expired
valid = exists() and valid_pemfile() and not_expired()
if not valid:
@@ -250,18 +283,26 @@ class ProviderCertChecker(object):
# XXX use gnutls for get proper
# validation.
# crypto.X509Certificate(cert_s)
+ sep = "-" * 5 + "BEGIN CERTIFICATE" + "-" * 5
+ # we might have private key and cert in the same file
+ certparts = cert_s.split(sep)
+ if len(certparts) > 1:
+ cert_s = sep + certparts[1]
ssl.PEM_cert_to_DER_cert(cert_s)
except:
# XXX raise proper exception
raise
return True
+ def _get_root_uri(self):
+ return u"https://%s/" % baseconstants.DEFAULT_PROVIDER
+
def _get_client_cert_uri(self):
- return "https://%s/cert/get" % (baseconstants.DEFAULT_TEST_PROVIDER)
+ # XXX get the whole thing from constants
+ return "https://%s/1/cert" % (baseconstants.DEFAULT_PROVIDER)
def _get_client_cert_path(self):
# MVS+ : get provider path
- #import ipdb;ipdb.set_trace()
return eipspecs.client_cert_path()
def write_cert(self, pemfile_content, to=None):
@@ -370,7 +411,11 @@ class EIPConfigChecker(object):
domain = config.get('provider', None)
uri = self._get_provider_definition_uri(domain=domain)
- self.defaultprovider.load(from_uri=uri, fetcher=self.fetcher)
+ # FIXME! Pass ca path verify!!!
+ self.defaultprovider.load(
+ from_uri=uri,
+ fetcher=self.fetcher,
+ verify=False)
self.defaultprovider.save()
def fetch_eip_service_config(self, skip_download=False,
@@ -414,14 +459,18 @@ class EIPConfigChecker(object):
def _get_provider_definition_uri(self, domain=None, path=None):
if domain is None:
- domain = baseconstants.DEFAULT_TEST_PROVIDER
+ domain = baseconstants.DEFAULT_PROVIDER
if path is None:
path = baseconstants.DEFINITION_EXPECTED_PATH
- return "https://%s/%s" % (domain, path)
+ uri = u"https://%s/%s" % (domain, path)
+ logger.debug('getting provider definition from %s' % uri)
+ return uri
def _get_eip_service_uri(self, domain=None, path=None):
if domain is None:
- domain = baseconstants.DEFAULT_TEST_PROVIDER
+ domain = baseconstants.DEFAULT_PROVIDER
if path is None:
path = eipconstants.EIP_SERVICE_EXPECTED_PATH
- return "https://%s/%s" % (domain, path)
+ uri = "https://%s/%s" % (domain, path)
+ logger.debug('getting eip service file from %s', uri)
+ return uri
diff --git a/src/leap/eip/config.py b/src/leap/eip/config.py
index c0e17a19..44922310 100644
--- a/src/leap/eip/config.py
+++ b/src/leap/eip/config.py
@@ -3,7 +3,9 @@ import os
import platform
import tempfile
-from leap.util.fileutil import (which, check_and_fix_urw_only)
+from leap import __branding as BRANDING
+from leap import certs
+from leap.util.fileutil import (which, mkdir_p, check_and_fix_urw_only)
from leap.base import config as baseconfig
from leap.baseapp.permcheck import (is_pkexec_in_system,
@@ -12,13 +14,18 @@ from leap.eip import exceptions as eip_exceptions
from leap.eip import specs as eipspecs
logger = logging.getLogger(name=__name__)
+provider_ca_file = BRANDING.get('provider_ca_file', None)
class EIPConfig(baseconfig.JSONLeapConfig):
spec = eipspecs.eipconfig_spec
def _get_slug(self):
- return baseconfig.get_config_file('eip.json')
+ dppath = baseconfig.get_default_provider_path()
+ eipjsonpath = baseconfig.get_config_file(
+ 'eip-service.json',
+ folder=dppath)
+ return eipjsonpath
def _set_slug(self, *args, **kwargs):
raise AttributeError("you cannot set slug")
@@ -48,6 +55,25 @@ def get_socket_path():
return socket_path
+def get_eip_gateway():
+ """
+ return the first host in the list of hosts
+ under gateways list
+ """
+ eipconfig = EIPConfig()
+ eipconfig.load()
+ conf = eipconfig.get_config()
+ gateways = conf.get('gateways', None)
+ if len(gateways) > 0:
+ # we just pick first
+ gw = gateways[0]
+ hosts = gw['hosts']
+ if len(hosts) > 0:
+ return hosts[0]
+ else:
+ return "testprovider.example.org"
+
+
def build_ovpn_options(daemon=False, socket_path=None, **kwargs):
"""
build a list of options
@@ -84,9 +110,11 @@ def build_ovpn_options(daemon=False, socket_path=None, **kwargs):
opts.append("%s" % verbosity)
# remote
- # XXX get remote from eip.json
opts.append('--remote')
- opts.append('testprovider.example.org')
+ gw = get_eip_gateway()
+ #gw = "springbokvpn.org"
+ logger.debug('setting eip gateway to %s', gw)
+ opts.append(str(gw))
opts.append('1194')
opts.append('udp')
@@ -137,6 +165,7 @@ def build_ovpn_options(daemon=False, socket_path=None, **kwargs):
#if daemon is True:
#opts.append('--daemon')
+ logger.debug('vpn options: %s', opts)
return opts
@@ -211,15 +240,30 @@ def check_vpn_keys():
logger.debug('client cert = %s', client_cert)
# if no keys, raise error.
- # should be catched by the ui and signal user.
+ # it's catched by the ui and signal user.
+
+ if not os.path.isfile(provider_ca):
+ # not there. let's try to copy.
+ folder, filename = os.path.split(provider_ca)
+ if not os.path.isdir(folder):
+ mkdir_p(folder)
+ if provider_ca_file:
+ cacert = certs.where(provider_ca_file)
+ with open(provider_ca, 'w') as pca:
+ with open(cacert, 'r') as cac:
+ pca.write(cac.read())
+
+ if not os.path.isfile(provider_ca):
+ logger.error('key file %s not found. aborting.',
+ provider_ca)
+ raise eip_exceptions.EIPInitNoKeyFileError
+
+ if not os.path.isfile(client_cert):
+ logger.error('key file %s not found. aborting.',
+ client_cert)
+ raise eip_exceptions.EIPInitNoKeyFileError
for keyfile in (provider_ca, client_cert):
- if not os.path.isfile(keyfile):
- logger.error('key file %s not found. aborting.',
- keyfile)
- raise eip_exceptions.EIPInitNoKeyFileError
-
- # check proper permission on keys
# bad perms? try to fix them
try:
check_and_fix_urw_only(keyfile)
diff --git a/src/leap/eip/constants.py b/src/leap/eip/constants.py
index ce50f5e0..9af5a947 100644
--- a/src/leap/eip/constants.py
+++ b/src/leap/eip/constants.py
@@ -1,3 +1,3 @@
# not used anymore with the new JSONConfig.slug
EIP_CONFIG = "eip.json"
-EIP_SERVICE_EXPECTED_PATH = "eip-service.json"
+EIP_SERVICE_EXPECTED_PATH = "1/config/eip-service.json"
diff --git a/src/leap/eip/eipconnection.py b/src/leap/eip/eipconnection.py
index 3a879f01..4e240f16 100644
--- a/src/leap/eip/eipconnection.py
+++ b/src/leap/eip/eipconnection.py
@@ -4,7 +4,9 @@ EIP Connection Class
from __future__ import (absolute_import,)
import logging
import Queue
+import sys
+from leap.eip.checks import ProviderCertChecker
from leap.eip.checks import EIPConfigChecker
from leap.eip import config as eipconfig
from leap.eip import exceptions as eip_exceptions
@@ -21,7 +23,10 @@ class EIPConnection(OpenVPNConnection):
Status updates (connected, bandwidth, etc) are signaled to the GUI.
"""
- def __init__(self, config_checker=EIPConfigChecker, *args, **kwargs):
+ def __init__(self,
+ provider_cert_checker=ProviderCertChecker,
+ config_checker=EIPConfigChecker,
+ *args, **kwargs):
self.settingsfile = kwargs.get('settingsfile', None)
self.logfile = kwargs.get('logfile', None)
@@ -29,6 +34,8 @@ class EIPConnection(OpenVPNConnection):
status_signals = kwargs.pop('status_signals', None)
self.status = EIPConnectionStatus(callbacks=status_signals)
+
+ self.provider_cert_checker = provider_cert_checker()
self.config_checker = config_checker()
host = eipconfig.get_socket_path()
@@ -44,11 +51,25 @@ class EIPConnection(OpenVPNConnection):
run all eip checks previous to attempting a connection
"""
logger.debug('running conductor checks')
+
+ def push_err(exc):
+ # keep the original traceback!
+ exc_traceback = sys.exc_info()[2]
+ self.error_queue.put((exc, exc_traceback))
+
+ try:
+ # network (1)
+ self.provider_cert_checker.run_all()
+ except Exception as exc:
+ push_err(exc)
try:
self.config_checker.run_all(skip_download=skip_download)
+ except Exception as exc:
+ push_err(exc)
+ try:
self.run_openvpn_checks()
except Exception as exc:
- self.error_queue.put(exc)
+ push_err(exc)
def connect(self):
"""
@@ -82,6 +103,7 @@ class EIPConnection(OpenVPNConnection):
# XXX this separation does not
# make sense anymore after having
# merged Connection and Manager classes.
+ # XXX GET RID OF THIS FUNCTION HERE!
try:
state = self.get_connection_state()
except eip_exceptions.ConnectionRefusedError:
diff --git a/src/leap/eip/exceptions.py b/src/leap/eip/exceptions.py
index 467be7fe..f048621f 100644
--- a/src/leap/eip/exceptions.py
+++ b/src/leap/eip/exceptions.py
@@ -40,6 +40,8 @@ class EIPClientError(Exception):
base EIPClient exception
"""
critical = False
+ failfirst = False
+ warning = False
class CriticalError(EIPClientError):
@@ -54,7 +56,7 @@ class Warning(EIPClientError):
"""
just that, warnings
"""
- pass
+ warning = True
class EIPNoPolkitAuthAgentAvailable(CriticalError):
@@ -81,10 +83,21 @@ class EIPNoCommandError(EIPClientError):
"<br/>(Might be a permissions problem)")
+class EIPBadCertError(Warning):
+ # XXX this should be critical and fail close
+ message = "cert verification failed"
+ usermessage = "there is a problem with provider certificate"
+
+
+class LeapBadConfigFetchedError(Warning):
+ message = "provider sent a malformed json file"
+ usermessage = "an error occurred during configuratio of leap services"
+
#
# errors still needing some love
#
+
class EIPInitNoKeyFileError(CriticalError):
message = "No vpn keys found in the expected path"
usermessage = "We could not find your eip certs in the expected path"
diff --git a/src/leap/eip/openvpnconnection.py b/src/leap/eip/openvpnconnection.py
index b679a544..92ae9de9 100644
--- a/src/leap/eip/openvpnconnection.py
+++ b/src/leap/eip/openvpnconnection.py
@@ -114,11 +114,10 @@ to be triggered for each one of them.
"""
try:
eip_config.check_vpn_keys()
- except eip_exceptions.EIPInitNoKeyFileError:
- self.missing_vpn_keyfile = True
except eip_exceptions.EIPInitBadKeyFilePermError:
- logger.error('error while checking vpn keys')
- self.bad_keyfile_perms = True
+ logger.error('Bad VPN Keys permission!')
+ # do nothing now
+ # and raise the rest ...
def _launch_openvpn(self):
"""
diff --git a/src/leap/eip/specs.py b/src/leap/eip/specs.py
index e617574c..05aef590 100644
--- a/src/leap/eip/specs.py
+++ b/src/leap/eip/specs.py
@@ -1,15 +1,21 @@
from __future__ import (unicode_literals)
import os
+from leap import __branding
from leap.base import config as baseconfig
+PROVIDER_CA_CERT = __branding.get(
+ 'provider_ca_file',
+ 'testprovider-ca-cert.pem')
provider_ca_path = lambda: unicode(os.path.join(
baseconfig.get_default_provider_path(),
'keys', 'ca',
- 'testprovider-ca-cert.pem'
+ PROVIDER_CA_CERT
))
+PROVIDER_DOMAIN = __branding.get('provider_domain', 'testprovider.example.org')
+
client_cert_path = lambda: unicode(os.path.join(
baseconfig.get_default_provider_path(),
@@ -20,7 +26,7 @@ client_cert_path = lambda: unicode(os.path.join(
eipconfig_spec = {
'provider': {
'type': unicode,
- 'default': u"testprovider.example.org",
+ 'default': u"%s" % PROVIDER_DOMAIN,
'required': True,
},
'transport': {
diff --git a/src/leap/eip/tests/data.py b/src/leap/eip/tests/data.py
index 284b398f..4da0e18f 100644
--- a/src/leap/eip/tests/data.py
+++ b/src/leap/eip/tests/data.py
@@ -1,21 +1,25 @@
from __future__ import unicode_literals
import os
+from leap import __branding
+
# sample data used in tests
+PROVIDER = __branding.get('provider_domain')
+
EIP_SAMPLE_JSON = {
- "provider": "testprovider.example.org",
+ "provider": "%s" % PROVIDER,
"transport": "openvpn",
"openvpn_protocol": "tcp",
"openvpn_port": 80,
"openvpn_ca_certificate": os.path.expanduser(
"~/.config/leap/providers/"
- "testprovider.example.org/"
- "keys/ca/testprovider-ca-cert.pem"),
+ "%s/"
+ "keys/ca/testprovider-ca-cert.pem" % PROVIDER),
"openvpn_client_certificate": os.path.expanduser(
"~/.config/leap/providers/"
- "testprovider.example.org/"
- "keys/client/openvpn.pem"),
+ "%s/"
+ "keys/client/openvpn.pem" % PROVIDER),
"connect_on_login": True,
"block_cleartext_traffic": True,
"primary_gateway": "usa_west",
diff --git a/src/leap/eip/tests/test_checks.py b/src/leap/eip/tests/test_checks.py
index 952b10d2..42aa9cce 100644
--- a/src/leap/eip/tests/test_checks.py
+++ b/src/leap/eip/tests/test_checks.py
@@ -331,10 +331,10 @@ class ProviderCertCheckerHTTPSTests(BaseHTTPSServerTestCase, BaseLeapTest):
fetcher.get(uri, verify=True)
self.assertTrue(
"SSL23_GET_SERVER_HELLO:unknown protocol" in exc.message)
- with self.assertRaises(requests.exceptions.SSLError) as exc:
+ with self.assertRaises(eipexceptions.EIPBadCertError) as exc:
checker.is_https_working(uri=uri, verify=True)
self.assertTrue(
- "SSL23_GET_SERVER_HELLO:unknown protocol" in exc.message)
+ "cert verification failed" in exc.message)
# get cacert from testing.https_server
cacert = where_cert('cacert.pem')
diff --git a/src/leap/eip/tests/test_config.py b/src/leap/eip/tests/test_config.py
index 60300770..f9f963dc 100644
--- a/src/leap/eip/tests/test_config.py
+++ b/src/leap/eip/tests/test_config.py
@@ -1,3 +1,4 @@
+import json
import os
import platform
import stat
@@ -9,11 +10,17 @@ except ImportError:
#from leap.base import constants
#from leap.eip import config as eip_config
+from leap import __branding as BRANDING
+from leap.eip import config as eipconfig
+from leap.eip.tests.data import EIP_SAMPLE_SERVICE
from leap.testing.basetest import BaseLeapTest
from leap.util.fileutil import mkdir_p
_system = platform.system()
+PROVIDER = BRANDING.get('provider_domain')
+PROVIDER_SHORTNAME = BRANDING.get('short_name')
+
class EIPConfigTest(BaseLeapTest):
@@ -39,6 +46,14 @@ class EIPConfigTest(BaseLeapTest):
open(tfile, 'wb').close()
os.chmod(tfile, stat.S_IRUSR | stat.S_IWUSR | stat.S_IXUSR)
+ def write_sample_eipservice(self):
+ conf = eipconfig.EIPConfig()
+ folder, f = os.path.split(conf.filename)
+ if not os.path.isdir(folder):
+ mkdir_p(folder)
+ with open(conf.filename, 'w') as fd:
+ fd.write(json.dumps(EIP_SAMPLE_SERVICE))
+
def get_expected_openvpn_args(self):
args = []
username = self.get_username()
@@ -51,7 +66,7 @@ class EIPConfigTest(BaseLeapTest):
args.append('--persist-tun')
args.append('--persist-key')
args.append('--remote')
- args.append('testprovider.example.org')
+ args.append('%s' % eipconfig.get_eip_gateway())
# XXX get port!?
args.append('1194')
# XXX get proto
@@ -80,23 +95,23 @@ class EIPConfigTest(BaseLeapTest):
args.append(os.path.join(
self.home,
'.config', 'leap', 'providers',
- 'testprovider.example.org',
+ '%s' % PROVIDER,
'keys', 'client',
'openvpn.pem'))
args.append('--key')
args.append(os.path.join(
self.home,
'.config', 'leap', 'providers',
- 'testprovider.example.org',
+ '%s' % PROVIDER,
'keys', 'client',
'openvpn.pem'))
args.append('--ca')
args.append(os.path.join(
self.home,
'.config', 'leap', 'providers',
- 'testprovider.example.org',
+ '%s' % PROVIDER,
'keys', 'ca',
- 'testprovider-ca-cert.pem'))
+ '%s-cacert.pem' % PROVIDER_SHORTNAME))
return args
# build command string
@@ -107,6 +122,7 @@ class EIPConfigTest(BaseLeapTest):
def test_build_ovpn_command_empty_config(self):
self.touch_exec()
+ self.write_sample_eipservice()
from leap.eip import config as eipconfig
from leap.util.fileutil import which
path = os.environ['PATH']