diff options
author | Kali Kaneko <kali@leap.se> | 2018-01-10 12:31:17 +0100 |
---|---|---|
committer | Kali Kaneko <kali@leap.se> | 2018-01-25 01:19:10 +0100 |
commit | 5ffa0c1710ce4038b94a026a58daf8f12aef2ec4 (patch) | |
tree | 61723ea8c2385b2930ed82f401f52d6239ecc221 /src/leap/bitmask/bonafide | |
parent | 5510c24e61046269e5b29df7d7ffb67a42bdc763 (diff) |
[feat] support anonymous vpn
honor the anonymous certificate for the providers that offer it.
this still needs a change in bonafide, in which if provider supports
anonymous access we still have to download eip-service.json
for testing, I assume this has been already manually downloaded.
Diffstat (limited to 'src/leap/bitmask/bonafide')
-rw-r--r-- | src/leap/bitmask/bonafide/_protocol.py | 39 | ||||
-rw-r--r-- | src/leap/bitmask/bonafide/config.py | 41 | ||||
-rw-r--r-- | src/leap/bitmask/bonafide/service.py | 4 | ||||
-rw-r--r-- | src/leap/bitmask/bonafide/session.py | 26 |
4 files changed, 68 insertions, 42 deletions
diff --git a/src/leap/bitmask/bonafide/_protocol.py b/src/leap/bitmask/bonafide/_protocol.py index e044875f..ceb29efd 100644 --- a/src/leap/bitmask/bonafide/_protocol.py +++ b/src/leap/bitmask/bonafide/_protocol.py @@ -25,7 +25,8 @@ from leap.bitmask.bonafide.provider import Api from leap.bitmask.bonafide.session import Session, OK from leap.common.config import get_path_prefix -from twisted.cred.credentials import UsernamePassword +from twisted.cred.credentials import UsernamePassword, Anonymous +from twisted.cred.checkers import ANONYMOUS from twisted.internet.defer import fail from twisted.logger import Logger @@ -52,13 +53,16 @@ class BonafideProtocol(object): self._apis[provider.domain] = api return api - def _get_session(self, provider, full_id, password=""): + def _get_or_create_session(self, provider, full_id, password=""): if full_id in self._sessions: return self._sessions[full_id] - - # TODO if password/username null, then pass AnonymousCreds - username, provider_id = config.get_username_and_provider(full_id) - credentials = UsernamePassword(username, password) + if full_id == ANONYMOUS: + credentials = Anonymous() + provider_id = provider.domain + else: + username, provider_id = config.get_username_and_provider( + full_id) + credentials = UsernamePassword(username, password) api = self._get_api(provider) provider_pem = config.get_ca_cert_path(_preffix, provider_id) session = Session(credentials, api, provider_pem) @@ -90,7 +94,7 @@ class BonafideProtocol(object): return user username, _ = config.get_username_and_provider(full_id) - session = self._get_session(provider, full_id, password) + session = self._get_or_create_session(provider, full_id, password) d = session.signup(username, password, invite) d.addCallback(return_user) d.addErrback(self._del_session_errback, full_id) @@ -102,7 +106,7 @@ class BonafideProtocol(object): provider = config.Provider.get(provider_id, autoconf=autoconf) def maybe_finish_provider_bootstrap(result): - session = self._get_session(provider, full_id, password) + session = self._get_or_create_session(provider, full_id, password) d = provider.download_services_config_with_auth(session) d.addCallback(lambda _: result) return d @@ -121,7 +125,7 @@ class BonafideProtocol(object): self.log.debug('AUTH for %s' % full_id) - session = self._get_session(provider, full_id, password) + session = self._get_or_create_session(provider, full_id, password) d = session.authenticate() d.addCallback(return_token_and_uuid, session) d.addErrback(self._del_session_errback, full_id) @@ -171,7 +175,6 @@ class BonafideProtocol(object): return config.delete_provider(provider_id) def do_provider_list(self, seeded=False): - # TODO: seeded, we don't have pinned providers yet providers = config.list_providers() return [{"domain": p} for p in providers] @@ -182,11 +185,17 @@ class BonafideProtocol(object): d = self._sessions[full_id].get_smtp_cert() return d - def do_get_vpn_cert(self, full_id): - if (full_id not in self._sessions or - not self._sessions[full_id].is_authenticated): - return fail(RuntimeError("There is no session for such user")) - d = self._sessions[full_id].get_vpn_cert() + def do_get_vpn_cert(self, full_id, anonymous=False): + if anonymous: + _, provider_id = full_id.split('@') + provider = config.Provider.get(provider_id, autoconf=True) + d = self._get_or_create_session( + provider, ANONYMOUS).get_vpn_cert() + else: + if (full_id not in self._sessions or + not self._sessions[full_id].is_authenticated): + return fail(RuntimeError("There is no session for such user")) + d = self._sessions[full_id].get_vpn_cert() return d def do_update_user(self): diff --git a/src/leap/bitmask/bonafide/config.py b/src/leap/bitmask/bonafide/config.py index 222726b7..fe40f277 100644 --- a/src/leap/bitmask/bonafide/config.py +++ b/src/leap/bitmask/bonafide/config.py @@ -31,13 +31,15 @@ from cryptography.hazmat.primitives import hashes from cryptography.x509 import load_pem_x509_certificate from urlparse import urlparse +from twisted.cred.credentials import Anonymous from twisted.internet import defer from twisted.logger import Logger 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.bitmask.bonafide.provider import Discovery +from leap.bitmask.bonafide.session import Session from leap.bitmask.util import here, STANDALONE from leap.common.check import leap_assert @@ -266,6 +268,10 @@ class Provider(object): self.log.debug('Bootstrapping provider %s' % domain) def first_bootstrap_done(ignored): + if self._allows_anonymous: + # we continue bootstrapping, we do not + # need to wait for authentication. + return try: self.first_bootstrap.callback('got config') except defer.AlreadyCalledError: @@ -282,6 +288,14 @@ class Provider(object): d.addCallback(self.maybe_download_services_config) self.ongoing_bootstrap = d + def _allows_anonymous(self): + try: + anon = self._provider_config.get( + 'service').get('allows_anonymous') + except ValueError: + anon = False + return anon + def callWhenMainConfigReady(self, cb, *args, **kw): d = self.first_bootstrap d.addCallback(lambda _: cb(*args, **kw)) @@ -388,17 +402,23 @@ class Provider(object): return os.path.isfile(self._get_configs_path()) def maybe_download_services_config(self, ignored): - # TODO --- currently, some providers (mail.bitmask.net) raise 401 # UNAUTHENTICATED if we try to get the services # See: # https://leap.se/code/issues/7906 + def first_bootstrap_done(ignored): + try: + self.first_bootstrap.callback('got config') + except defer.AlreadyCalledError: + pass + uri, met, path = self._get_configs_download_params() d = httpRequest( self._http._agent, uri, method=met, saveto=path) d.addCallback(lambda _: self._load_provider_json()) d.addCallback( lambda _: self._get_config_for_all_services(session=None)) + d.addCallback(first_bootstrap_done) d.addErrback(lambda _: 'ok for now') return d @@ -499,6 +519,10 @@ class Provider(object): self._disco.netloc = parsed.netloc def _get_config_for_all_services(self, session): + if session is None: + provider_cert = self._get_ca_cert_path() + session = Session(Anonymous(), self.api_uri, provider_cert) + services_dict = self._load_provider_configs() configs_path = self._get_configs_path() with open(configs_path) as jsonf: @@ -510,12 +534,8 @@ class Provider(object): for subservice in self.SERVICES_MAP[service]: uri = base + str(services_dict[subservice]) path = self._get_service_config_path(subservice) - if session: - d = session.fetch_provider_configs( - uri, path, method='GET') - else: - d = self._fetch_provider_configs_unauthenticated( - uri, path, method='GET') + d = session.fetch_provider_configs( + uri, path, method='GET') pending.append(d) return defer.gatherResults(pending) @@ -525,11 +545,6 @@ class Provider(object): services_dict = Record(**json.load(jsonf)).services return services_dict - def _fetch_provider_configs_unauthenticated(self, uri, path): - self.log.info('Downloading config for %s...' % uri) - return httpRequest( - self._http._agent, uri, saveto=path) - class Record(object): def __init__(self, **kw): diff --git a/src/leap/bitmask/bonafide/service.py b/src/leap/bitmask/bonafide/service.py index 43f51768..5856a263 100644 --- a/src/leap/bitmask/bonafide/service.py +++ b/src/leap/bitmask/bonafide/service.py @@ -131,12 +131,12 @@ class BonafideService(HookableService): def do_provider_list(self, seeded=False): return self._bonafide.do_provider_list(seeded) - def do_get_vpn_cert(self, username): + def do_get_vpn_cert(self, username, anonymous=False): if not username: return defer.fail( RuntimeError('No username, cannot get VPN cert.')) - d = self._bonafide.do_get_vpn_cert(username) + d = self._bonafide.do_get_vpn_cert(username, anonymous=anonymous) d.addCallback(lambda response: (username, response)) return d diff --git a/src/leap/bitmask/bonafide/session.py b/src/leap/bitmask/bonafide/session.py index 988cbb99..d6a39447 100644 --- a/src/leap/bitmask/bonafide/session.py +++ b/src/leap/bitmask/bonafide/session.py @@ -17,6 +17,7 @@ """ LEAP Session management. """ +from twisted.cred.credentials import IAnonymous, IUsernamePassword from twisted.internet import defer, reactor from twisted.logger import Logger @@ -47,7 +48,6 @@ class Session(object): log = Logger() def __init__(self, credentials, api, provider_cert): - # TODO check if an anonymous credentials is passed. # TODO move provider_cert to api object. # On creation, it should be able to retrieve all the info it needs # (calling bootstrap). @@ -56,9 +56,12 @@ class Session(object): # and a "autoconfig" attribute passed on initialization. # TODO get a file-descriptor for password if not in credentials # TODO merge self._request with config.Provider._http_request ? - - self.username = credentials.username - self.password = credentials.password + if IAnonymous.providedBy(credentials): + self.username = None + self.password = None + elif IUsernamePassword.providedBy(credentials): + self.username = credentials.username + self.password = credentials.password self._provider_cert = provider_cert self._api = api self._initialize_session() @@ -86,7 +89,10 @@ class Session(object): @property def is_authenticated(self): - return self._srp_auth.srp_user.authenticated() + if self.username is None: + return False + else: + return self._srp_auth.srp_user.authenticated() @defer.inlineCallbacks def authenticate(self): @@ -133,8 +139,8 @@ class Session(object): met = self._api.get_update_user_method() params = self._srp_password.get_password_params( self.username, password) - update = yield self._request(self._agent, uri, values=params, - method=met) + yield self._request(self._agent, uri, values=params, + method=met) self.password = password self._srp_auth = _srp.SRPAuthMechanism(self.username, password) defer.returnValue(OK) @@ -153,16 +159,12 @@ class Session(object): # User certificates def get_vpn_cert(self): - # TODO pass it to the provider object so that it can save it in the - # right path. uri = self._api.get_vpn_cert_uri() met = self._api.get_vpn_cert_method() return self._request(self._agent, uri, method=met) @_auth_required def get_smtp_cert(self): - # TODO pass it to the provider object so that it can save it in the - # right path. uri = self._api.get_smtp_cert_uri() met = self._api.get_smtp_cert_method() return self._request(self._agent, uri, method=met) @@ -198,7 +200,7 @@ class Session(object): @defer.inlineCallbacks def fetch_provider_configs(self, uri, path, method='GET'): - config = yield self._request( + yield self._request( self._agent, uri, method=method, saveto=path) defer.returnValue('ok') |