diff options
author | Ruben Pollan <meskio@sindominio.net> | 2017-05-16 18:57:16 +0200 |
---|---|---|
committer | Ruben Pollan <meskio@sindominio.net> | 2017-06-05 19:42:13 +0200 |
commit | 97774379834f0a06731093de54c986f8f7fd51a3 (patch) | |
tree | 1c5c11be27a289d2f0ea02fdac4dfd96954b9c68 /src/leap/bitmask/bonafide/config.py | |
parent | 0edc6cf6a4270e94443fc6c8bfa73c5ca1ad5d92 (diff) |
[bug] fail bonafide bootstrap for self sign certs
- Resolves: #8881
Diffstat (limited to 'src/leap/bitmask/bonafide/config.py')
-rw-r--r-- | src/leap/bitmask/bonafide/config.py | 76 |
1 files changed, 43 insertions, 33 deletions
diff --git a/src/leap/bitmask/bonafide/config.py b/src/leap/bitmask/bonafide/config.py index 5f3bff9e..1e6e550f 100644 --- a/src/leap/bitmask/bonafide/config.py +++ b/src/leap/bitmask/bonafide/config.py @@ -32,17 +32,16 @@ from cryptography.x509 import load_pem_x509_certificate from urlparse import urlparse from twisted.internet import defer, reactor -from twisted.internet.ssl import ClientContextFactory from twisted.logger import Logger -from twisted.web.client import Agent, downloadPage +from twisted.web.client import downloadPage -from leap.bitmask.bonafide._http import httpRequest from leap.bitmask.bonafide.provider import Discovery from leap.bitmask.bonafide.errors import NotConfiguredError, NetworkError from leap.common.check import leap_assert from leap.common.config import get_path_prefix as common_get_path_prefix from leap.common.files import mkdir_p +from leap.common.http import HTTPClient APPNAME = "bonafide" @@ -161,8 +160,6 @@ def delete_provider(domain): class Provider(object): - # TODO add validation - SERVICES_MAP = { 'openvpn': ['eip'], 'mx': ['soledad', 'smtp']} @@ -174,8 +171,7 @@ class Provider(object): stuck_bootstrap = defaultdict(None) def __init__(self, domain, autoconf=False, basedir=None, - check_certificate=True): - # TODO: I need a way to know if it was already configured + cert_path=None): if not basedir: basedir = os.path.join(_preffix, 'leap') self._basedir = os.path.expanduser(basedir) @@ -184,18 +180,13 @@ class Provider(object): self._provider_config = None is_configured = self.is_configured() - if not is_configured: - check_certificate = False - - if check_certificate: - self.contextFactory = None + if is_configured: + self._http = HTTPClient(self._get_ca_cert_path()) else: - # XXX we should do this only for the FIRST provider download. - # For the rest, we should pass the ca cert to the agent. - # That means that RIGHT AFTER DOWNLOADING provider_info, - # we should instantiate a new Agent... - self.contextFactory = WebClientContextFactory() - self._agent = Agent(reactor, self.contextFactory) + # TODO: we distribute our own cert bundle but it's too outdated, + # let's use for now the one from the system + # see: leap.common.ca_bundle.where() + self._http = HTTPClient(cert_path) self._load_provider_json() @@ -260,6 +251,8 @@ class Provider(object): ongoing = self.ongoing_bootstrap.get(domain) if ongoing: self.log.debug('Already bootstrapping this provider...') + self.ongoing_bootstrap[domain].addCallback( + self._reload_http_client) return self.first_bootstrap[self._domain] = defer.Deferred() @@ -270,10 +263,14 @@ class Provider(object): except defer.AlreadyCalledError: pass + def first_bootstrap_error(failure): + self.first_bootstrap[domain].errback(failure) + return failure + d = self.maybe_download_provider_info() d.addCallback(self.maybe_download_ca_cert) d.addCallback(self.validate_ca_cert) - d.addCallback(first_bootstrap_done) + d.addCallbacks(first_bootstrap_done, first_bootstrap_error) d.addCallback(self.maybe_download_services_config) self.ongoing_bootstrap[domain] = d @@ -311,7 +308,8 @@ class Provider(object): shutil.rmtree(folders) raise NetworkError(failure.getErrorMessage()) - d = downloadPage(uri, provider_json, method=met) + d = self._http.request(uri, method=met) + d.addCallback(_write_to_file, provider_json) d.addCallback(lambda _: self._load_provider_json()) d.addErrback(errback) return d @@ -326,20 +324,27 @@ class Provider(object): """ :rtype: deferred """ - - def errback(self, failure): - raise NetworkError(failure.getErrorMessage()) - path = self._get_ca_cert_path() if is_file(path): return defer.succeed('ca_cert_path_already_exists') + def errback(failure): + raise NetworkError(failure.getErrorMessage()) + uri = self._get_ca_cert_uri() mkdir_p(os.path.split(path)[0]) + + # We don't validate the TLS cert for this connection, + # just check the fingerprint of the ca.cert d = downloadPage(uri, path) + d.addCallback(self._reload_http_client) d.addErrback(errback) return d + def _reload_http_client(self, ret): + self._http = HTTPClient(self._get_ca_cert_path()) + return ret + def validate_ca_cert(self, ignored): expected = self._get_expected_ca_cert_fingerprint() algo, expectedfp = expected.split(':') @@ -377,6 +382,12 @@ class Provider(object): # UNAUTHENTICATED if we try to get the services # See: # https://leap.se/code/issues/7906 + def check_error(content): + c = json.loads(content) + if 'error' in c: + raise Exception(c['error']) + return content + def further_bootstrap_needs_auth(ignored): self.log.warn('Cannot download services config yet, need auth') pending_deferred = defer.Deferred() @@ -385,7 +396,9 @@ class Provider(object): uri, met, path = self._get_configs_download_params() - d = downloadPage(uri, path, method=met) + d = self._http.request(uri, method=met) + d.addCallback(check_error) + d.addCallback(_write_to_file, path) d.addCallback(lambda _: self._load_provider_json()) d.addCallback( lambda _: self._get_config_for_all_services(session=None)) @@ -553,13 +566,10 @@ class Provider(object): def _fetch_provider_configs_unauthenticated(self, uri, path): self.log.info('Downloading config for %s...' % uri) - d = downloadPage(uri, path, method='GET') + d = self._http.request(uri) + d.addCallback(_write_to_file, path) return d - def _http_request(self, *args, **kw): - # XXX pass if-modified-since header - return httpRequest(self._agent, *args, **kw) - class Record(object): def __init__(self, **kw): @@ -569,9 +579,9 @@ class Record(object): return self.__dict__ -class WebClientContextFactory(ClientContextFactory): - def getContext(self, hostname, port): - return ClientContextFactory.getContext(self) +def _write_to_file(content, path): + with open(path, 'w') as f: + f.write(content) if __name__ == '__main__': |