summaryrefslogtreecommitdiff
path: root/src/leap/eip
diff options
context:
space:
mode:
Diffstat (limited to 'src/leap/eip')
-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
10 files changed, 224 insertions, 71 deletions
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 c280f70d..65683485 100644
--- a/src/leap/eip/openvpnconnection.py
+++ b/src/leap/eip/openvpnconnection.py
@@ -117,11 +117,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']