diff options
Diffstat (limited to 'service/pixelated/bitmask_libraries')
-rw-r--r-- | service/pixelated/bitmask_libraries/certs.py | 104 | ||||
-rw-r--r-- | service/pixelated/bitmask_libraries/config.py | 48 | ||||
-rw-r--r-- | service/pixelated/bitmask_libraries/nicknym.py | 10 | ||||
-rw-r--r-- | service/pixelated/bitmask_libraries/provider.py | 14 | ||||
-rw-r--r-- | service/pixelated/bitmask_libraries/session.py | 56 | ||||
-rw-r--r-- | service/pixelated/bitmask_libraries/smtp.py | 19 | ||||
-rw-r--r-- | service/pixelated/bitmask_libraries/soledad.py | 14 |
7 files changed, 72 insertions, 193 deletions
diff --git a/service/pixelated/bitmask_libraries/certs.py b/service/pixelated/bitmask_libraries/certs.py index a321e00e..9d543672 100644 --- a/service/pixelated/bitmask_libraries/certs.py +++ b/service/pixelated/bitmask_libraries/certs.py @@ -14,108 +14,42 @@ # You should have received a copy of the GNU Affero General Public License # along with Pixelated. If not, see <http://www.gnu.org/licenses/>. import os -import requests -import json -from leap.common import ca_bundle -from .config import AUTO_DETECT_CA_BUNDLE -LEAP_CERT = None -LEAP_FINGERPRINT = None -PACKAGED_CERTS_HOME = os.path.abspath(os.path.join(os.path.abspath(__file__), "..", "..", "certificates")) - - -def which_api_CA_bundle(provider): - return str(LeapCertificate(provider).api_ca_bundle()) - - -def which_bootstrap_cert_fingerprint(): - return LEAP_FINGERPRINT - - -def which_bootstrap_CA_bundle(provider): - if LEAP_CERT is not None: - return LEAP_CERT - return str(LeapCertificate(provider).auto_detect_bootstrap_ca_bundle()) - - -def refresh_ca_bundle(provider): - LeapCertificate(provider).refresh_ca_bundle() +class LeapCertificate(object): + LEAP_CERT = None + LEAP_FINGERPRINT = None -class LeapCertificate(object): def __init__(self, provider): self._config = provider.config self._server_name = provider.server_name self._provider = provider - def auto_detect_bootstrap_ca_bundle(self): - if self._config.bootstrap_ca_cert_bundle == AUTO_DETECT_CA_BUNDLE: - local_cert = self._local_bootstrap_server_cert() - if local_cert: - return local_cert - else: - return ca_bundle.where() + @staticmethod + def set_cert_and_fingerprint(cert_file=None, cert_fingerprint=None): + if cert_fingerprint is None: + LeapCertificate.LEAP_CERT = str(cert_file) if cert_file else True + LeapCertificate.LEAP_FINGERPRINT = None else: - return self._config.bootstrap_ca_cert_bundle - - def api_ca_bundle(self): - if self._provider.config.ca_cert_bundle: - return self._provider.config.ca_cert_bundle - - cert_file = self._api_cert_file() - - if not os.path.isfile(cert_file): - self._download_server_cert(cert_file) + LeapCertificate.LEAP_FINGERPRINT = cert_fingerprint + LeapCertificate.LEAP_CERT = False - return cert_file + @property + def provider_web_cert(self): + return self.LEAP_CERT - def refresh_ca_bundle(self): - cert_file = self._api_cert_file() - self._download_server_cert(cert_file) + @property + def provider_api_cert(self): + return str(os.path.join(self._provider.config.leap_home, 'providers', self._server_name, 'keys', 'client', 'api.pem')) - def _api_cert_file(self): - certs_root = self._api_certs_root_path() - return os.path.join(certs_root, 'api.pem') - - def _api_certs_root_path(self): + def setup_ca_bundle(self): path = os.path.join(self._provider.config.leap_home, 'providers', self._server_name, 'keys', 'client') if not os.path.isdir(path): os.makedirs(path, 0700) - return path - - def _local_bootstrap_server_cert(self): - cert_file = self._bootstrap_certs_cert_file() - if os.path.isfile(cert_file): - return cert_file - - cert_file = os.path.join(PACKAGED_CERTS_HOME, '%s.ca.crt' % self._server_name) - if os.path.exists(cert_file): - return cert_file - - # else download the file - cert_file = self._bootstrap_certs_cert_file() - response = requests.get('https://%s/provider.json' % self._server_name) - provider_data = json.loads(response.content) - ca_cert_uri = str(provider_data['ca_cert_uri']) - - response = requests.get(ca_cert_uri) - with open(cert_file, 'w') as file: - file.write(response.content) - - return cert_file - - def _bootstrap_certs_cert_file(self): - path = os.path.join(self._provider.config.leap_home, 'providers', self._server_name) - if not os.path.isdir(path): - os.makedirs(path, 0700) + self._download_cert(self.provider_api_cert) - file_path = os.path.join(path, '%s.ca.crt' % self._server_name) - - return file_path - - def _download_server_cert(self, cert_file_name): + def _download_cert(self, cert_file_name): cert = self._provider.fetch_valid_certificate() - with open(cert_file_name, 'w') as file: file.write(cert) diff --git a/service/pixelated/bitmask_libraries/config.py b/service/pixelated/bitmask_libraries/config.py index 8c862d0a..efb43411 100644 --- a/service/pixelated/bitmask_libraries/config.py +++ b/service/pixelated/bitmask_libraries/config.py @@ -13,10 +13,9 @@ # # You should have received a copy of the GNU Affero General Public License # along with Pixelated. If not, see <http://www.gnu.org/licenses/>. -from distutils.spawn import find_executable import os -from os.path import expanduser +from distutils.spawn import find_executable def discover_gpg_binary(): @@ -30,54 +29,19 @@ def discover_gpg_binary(): return path -DEFAULT_LEAP_HOME = os.path.join(expanduser("~"), '.leap') - SYSTEM_CA_BUNDLE = True -AUTO_DETECT_CA_BUNDLE = None class LeapConfig(object): - """ - LEAP client configuration - """ - - def __init__(self, leap_home=DEFAULT_LEAP_HOME, bootstrap_ca_cert_bundle=AUTO_DETECT_CA_BUNDLE, - ca_cert_bundle=AUTO_DETECT_CA_BUNDLE, verify_ssl=True, + def __init__(self, + leap_home=None, fetch_interval_in_s=30, - timeout_in_s=15, start_background_jobs=False, gpg_binary=discover_gpg_binary()): - """ - Constructor. - - :param server_name: The LEAP server name, e.g. demo.leap.se - :type server_name: str - - :param user_name: The LEAP account user name, normally the first part of your email, e.g. foobar for foobar@demo.leap.se - :type user_name: str - - :param user_password: The LEAP account password - :type user_password: str - - :param db_passphrase: The passphrase used to encrypt the local soledad database - :type db_passphrase: str - - :param verify_ssl: Set to false to disable strict SSL certificate validation - :type verify_ssl: bool - - :param fetch_interval_in_s: Polling interval for fetching incoming mail from LEAP server - :type fetch_interval_in_s: int - - :param timeout_in_s: Timeout for network operations, e.g. HTTP calls - :type timeout_in_s: int - - :param gpg_binary: Path to the GPG binary (must not be a symlink) - :type gpg_binary: str + timeout_in_s=15, + start_background_jobs=False, + gpg_binary=discover_gpg_binary()): - """ self.leap_home = leap_home - self.bootstrap_ca_cert_bundle = bootstrap_ca_cert_bundle - self.ca_cert_bundle = ca_cert_bundle - self.verify_ssl = verify_ssl self.timeout_in_s = timeout_in_s self.start_background_jobs = start_background_jobs self.gpg_binary = gpg_binary diff --git a/service/pixelated/bitmask_libraries/nicknym.py b/service/pixelated/bitmask_libraries/nicknym.py index bee90897..220d75e5 100644 --- a/service/pixelated/bitmask_libraries/nicknym.py +++ b/service/pixelated/bitmask_libraries/nicknym.py @@ -14,16 +14,16 @@ # You should have received a copy of the GNU Affero General Public License # along with Pixelated. If not, see <http://www.gnu.org/licenses/>. from leap.keymanager import KeyManager, openpgp, KeyNotFound -from .certs import which_api_CA_bundle +from .certs import LeapCertificate class NickNym(object): - def __init__(self, provider, config, soledad_session, username, token, uuid): + def __init__(self, provider, config, soledad_session, email_address, token, uuid): nicknym_url = _discover_nicknym_server(provider) - self._email = '%s@%s' % (username, provider.domain) - self.keymanager = KeyManager('%s@%s' % (username, provider.domain), nicknym_url, + self._email = email_address + self.keymanager = KeyManager(self._email, nicknym_url, soledad_session.soledad, - token, which_api_CA_bundle(provider), provider.api_uri, + token, LeapCertificate(provider).provider_api_cert, provider.api_uri, provider.api_version, uuid, config.gpg_binary) diff --git a/service/pixelated/bitmask_libraries/provider.py b/service/pixelated/bitmask_libraries/provider.py index 1564c974..315ea7f1 100644 --- a/service/pixelated/bitmask_libraries/provider.py +++ b/service/pixelated/bitmask_libraries/provider.py @@ -17,7 +17,7 @@ import json from leap.common.certs import get_digest import requests -from .certs import which_bootstrap_CA_bundle, which_api_CA_bundle, which_bootstrap_cert_fingerprint +from .certs import LeapCertificate from pixelated.support.tls_adapter import EnforceTLSv1Adapter @@ -26,7 +26,6 @@ class LeapProvider(object): self.server_name = server_name self.config = config self.local_ca_crt = '%s/ca.crt' % self.config.leap_home - self.provider_json = self.fetch_provider_json() @property @@ -99,8 +98,8 @@ class LeapProvider(object): def _validated_get(self, url): session = requests.session() try: - session.mount('https://', EnforceTLSv1Adapter(assert_fingerprint=which_bootstrap_cert_fingerprint())) - response = session.get(url, verify=which_bootstrap_CA_bundle(self), timeout=self.config.timeout_in_s) + session.mount('https://', EnforceTLSv1Adapter(assert_fingerprint=LeapCertificate.LEAP_FINGERPRINT)) + response = session.get(url, verify=LeapCertificate(self).provider_web_cert, timeout=self.config.timeout_in_s) response.raise_for_status() return response finally: @@ -115,16 +114,19 @@ class LeapProvider(object): def fetch_soledad_json(self): service_url = "%s/%s/config/soledad-service.json" % ( self.api_uri, self.api_version) - response = requests.get(service_url, verify=which_api_CA_bundle(self), timeout=self.config.timeout_in_s) + response = requests.get(service_url, verify=LeapCertificate(self).provider_api_cert, timeout=self.config.timeout_in_s) response.raise_for_status() return json.loads(response.content) def fetch_smtp_json(self): service_url = '%s/%s/config/smtp-service.json' % ( self.api_uri, self.api_version) - response = requests.get(service_url, verify=which_api_CA_bundle(self), timeout=self.config.timeout_in_s) + response = requests.get(service_url, verify=LeapCertificate(self).provider_api_cert, timeout=self.config.timeout_in_s) response.raise_for_status() return json.loads(response.content) def _provider_base_url(self): return 'https://%s' % self.server_name + + def address_for(self, username): + return '%s@%s' % (username, self.domain) diff --git a/service/pixelated/bitmask_libraries/session.py b/service/pixelated/bitmask_libraries/session.py index 12cbd91b..a9cb15f2 100644 --- a/service/pixelated/bitmask_libraries/session.py +++ b/service/pixelated/bitmask_libraries/session.py @@ -22,29 +22,16 @@ from leap.mail.imap.fetch import LeapIncomingMail from leap.mail.imap.account import SoledadBackedAccount from leap.mail.imap.memorystore import MemoryStore from leap.mail.imap.soledadstore import SoledadStore -from pixelated.bitmask_libraries.config import LeapConfig -from pixelated.bitmask_libraries.provider import LeapProvider -from pixelated.bitmask_libraries.certs import refresh_ca_bundle from twisted.internet import reactor from .nicknym import NickNym from leap.auth import SRPAuth from .soledad import SoledadSessionFactory from .smtp import LeapSmtp -from .config import DEFAULT_LEAP_HOME SESSIONS = {} -def open_leap_session(username, password, server_name, leap_home=DEFAULT_LEAP_HOME): - config = LeapConfig(leap_home=leap_home) - provider = LeapProvider(server_name, config) - refresh_ca_bundle(provider) - session = LeapSessionFactory(provider).create(username, password) - - return session - - class LeapSession(object): """ A LEAP session. @@ -52,9 +39,13 @@ class LeapSession(object): Properties: - - ``leap_config`` the configuration for this session (LeapClientConfig). + - ``smtp`` the smtp gateway instance (LeapSmtp). + + - ``config`` the configuration for this session (LeapClientConfig). + + - ``provider`` the responsible for interacting with provider.json (LeapProvider). - - ``srp_session`` the secure remote password session to authenticate with LEAP. See http://en.wikipedia.org/wiki/Secure_Remote_Password_protocol (LeapSecureRemotePassword) + - ``user_auth`` the secure remote password session data after authenticating with LEAP. See http://en.wikipedia.org/wiki/Secure_Remote_Password_protocol (SRPSession) - ``soledad_session`` the soledad session. See https://leap.se/soledad (LeapSecureRemotePassword) @@ -66,13 +57,6 @@ class LeapSession(object): """ def __init__(self, provider, user_auth, soledad_session, nicknym, soledad_account, incoming_mail_fetcher, smtp): - """ - Constructor. - - :param leap_config: The config for this LEAP session - :type leap_config: LeapConfig - - """ self.smtp = smtp self.config = provider.config self.provider = provider @@ -81,22 +65,25 @@ class LeapSession(object): self.nicknym = nicknym self.account = soledad_account self.incoming_mail_fetcher = incoming_mail_fetcher + self.soledad_session.soledad.sync(defer_decryption=False) + self.nicknym.generate_openpgp_key() if self.config.start_background_jobs: self.start_background_jobs() def account_email(self): - domain = self.provider.domain name = self.user_auth.username - return '%s@%s' % (name, domain) + return self.provider.address_for(name) def close(self): self.stop_background_jobs() def start_background_jobs(self): + self.smtp.ensure_running() reactor.callFromThread(self.incoming_mail_fetcher.start_loop) def stop_background_jobs(self): + self.smtp.stop() reactor.callFromThread(self.incoming_mail_fetcher.stop) def sync(self): @@ -127,16 +114,15 @@ class LeapSessionFactory(object): srp_auth = SRPAuth(self._provider.api_uri, self._provider.local_ca_crt) auth = srp_auth.authenticate(username, password) + account_email = self._provider.address_for(username) soledad = SoledadSessionFactory.create(self._provider, auth.token, auth.uuid, password) - nicknym = self._create_nicknym(auth.username, auth.token, auth.uuid, soledad) + nicknym = self._create_nicknym(account_email, auth.token, auth.uuid, soledad) account = self._create_account(auth.uuid, soledad) - incoming_mail_fetcher = self._create_incoming_mail_fetcher(nicknym, soledad, account, auth.username) + incoming_mail_fetcher = self._create_incoming_mail_fetcher(nicknym, soledad, account, account_email) - smtp = LeapSmtp(self._provider, auth.username, auth.session_id, nicknym.keymanager) - - smtp.ensure_running() + smtp = LeapSmtp(self._provider, auth, nicknym.keymanager) return LeapSession(self._provider, auth, soledad, nicknym, account, incoming_mail_fetcher, smtp) @@ -163,17 +149,13 @@ class LeapSessionFactory(object): else: raise - def _create_nicknym(self, username, token, uuid, soledad_session): - return NickNym(self._provider, self._config, soledad_session, username, token, uuid) + def _create_nicknym(self, email_address, token, uuid, soledad_session): + return NickNym(self._provider, self._config, soledad_session, email_address, token, uuid) def _create_account(self, uuid, soledad_session): memstore = MemoryStore(permanent_store=SoledadStore(soledad_session.soledad)) return SoledadBackedAccount(uuid, soledad_session.soledad, memstore) - def _create_incoming_mail_fetcher(self, nicknym, soledad_session, account, username): + def _create_incoming_mail_fetcher(self, nicknym, soledad_session, account, email_address): return LeapIncomingMail(nicknym.keymanager, soledad_session.soledad, account, - self._config.fetch_interval_in_s, self._account_email(username)) - - def _account_email(self, username): - domain = self._provider.domain - return '%s@%s' % (username, domain) + self._config.fetch_interval_in_s, email_address) diff --git a/service/pixelated/bitmask_libraries/smtp.py b/service/pixelated/bitmask_libraries/smtp.py index c22601d2..31e56995 100644 --- a/service/pixelated/bitmask_libraries/smtp.py +++ b/service/pixelated/bitmask_libraries/smtp.py @@ -17,8 +17,8 @@ import logging import os import requests import random -from .certs import which_api_CA_bundle from leap.mail.smtp import setup_smtp_gateway +from pixelated.bitmask_libraries.certs import LeapCertificate logger = logging.getLogger(__name__) @@ -26,11 +26,12 @@ logger = logging.getLogger(__name__) class LeapSmtp(object): - def __init__(self, provider, username, session_id, keymanager=None): + def __init__(self, provider, auth, keymanager=None): self.local_smtp_port_number = random.randrange(12000, 16000) self._provider = provider - self.username = username - self.session_id = session_id + self.username = auth.username + self.session_id = auth.session_id + self.user_token = auth.token self._keymanager = keymanager self._remote_hostname, self._remote_port = self._discover_remote_smtp_server() self._local_smtp_service_socket = None @@ -58,8 +59,14 @@ class LeapSmtp(object): cert_url = '%s/%s/cert' % (self._provider.api_uri, self._provider.api_version) cookies = {"_session_id": self.session_id} - - response = requests.get(cert_url, verify=which_api_CA_bundle(self._provider), cookies=cookies, timeout=self._provider.config.timeout_in_s) + headers = {} + headers["Authorization"] = 'Token token="{0}"'.format(self.user_token) + response = requests.get( + cert_url, + verify=LeapCertificate(self._provider).provider_api_cert, + cookies=cookies, + timeout=self._provider.config.timeout_in_s, + headers=headers) response.raise_for_status() client_cert = response.content diff --git a/service/pixelated/bitmask_libraries/soledad.py b/service/pixelated/bitmask_libraries/soledad.py index f3fca95a..f0cd9f2f 100644 --- a/service/pixelated/bitmask_libraries/soledad.py +++ b/service/pixelated/bitmask_libraries/soledad.py @@ -16,11 +16,9 @@ import errno import os -from leap.keymanager import KeyManager from leap.soledad.client import Soledad from leap.soledad.common.crypto import WrongMac, UnknownMacMethod -from .certs import which_api_CA_bundle - +from pixelated.bitmask_libraries.certs import LeapCertificate SOLEDAD_TIMEOUT = 120 SOLEDAD_CERT = '/tmp/ca.crt' @@ -36,14 +34,6 @@ class SoledadWrongPassphraseException(Exception): super(SoledadWrongPassphraseException, self).__init__(*args, **kwargs) -class LeapKeyManager(object): - def __init__(self, soledad, leap_session, nicknym_url): - provider = leap_session.provider - self.keymanager = KeyManager(leap_session.account_email(), nicknym_url, soledad, - leap_session.session_id, leap_session.leap_home + '/ca.crt', provider.api_uri, leap_session.api_version, - leap_session.uuid, leap_session.leap_config.gpg_binary) - - class SoledadSessionFactory(object): @classmethod def create(cls, provider, user_token, user_uuid, encryption_passphrase): @@ -68,7 +58,7 @@ class SoledadSession(object): local_db = self._local_db_path() return Soledad(self.user_uuid, unicode(encryption_passphrase), secrets, - local_db, server_url, which_api_CA_bundle(self.provider), self.user_token, defer_encryption=False) + local_db, server_url, LeapCertificate(self.provider).provider_api_cert, self.user_token, defer_encryption=False) except (WrongMac, UnknownMacMethod), e: raise SoledadWrongPassphraseException(e) |