Verified Commit ce5cd2d4 by meskio

[feat] add provider pinning

Pin the provider.json and the ca cert for the public providers.

- Resolves: #9074
parent 34fa8f02
......@@ -8,6 +8,7 @@ Changelog
Features
~~~~~~~~
- `#8217 <https://0xacab.org/leap/bitmask-dev/issues/8217>`_: renew OpenPGP keys before they expire.
- `#9074 <https://0xacab.org/leap/bitmask-dev/issues/9074>`_: pin provider ca certs.
- Set a windows title, so that Bitmask windows can be programmatically manipulated.
Misc
......
......@@ -17,7 +17,7 @@
"""
Bonafide protocol.
"""
import os
import os.path
from collections import defaultdict
from leap.bitmask.bonafide import config
......@@ -31,7 +31,7 @@ from twisted.logger import Logger
COMMANDS = 'signup', 'authenticate', 'logout', 'stats'
_preffix = get_path_prefix()
_preffix = os.path.join(get_path_prefix(), 'leap')
class BonafideProtocol(object):
......@@ -60,7 +60,7 @@ class BonafideProtocol(object):
username, provider_id = config.get_username_and_provider(full_id)
credentials = UsernamePassword(username, password)
api = self._get_api(provider)
provider_pem = _get_provider_ca_path(provider_id)
provider_pem = config.get_ca_cert_path(_preffix, provider_id)
session = Session(credentials, api, provider_pem)
self._sessions[full_id] = session
return session
......@@ -192,8 +192,3 @@ class BonafideProtocol(object):
def do_update_user(self):
# FIXME to be implemented
pass
def _get_provider_ca_path(provider_id):
return os.path.join(
_preffix, 'leap', 'providers', provider_id, 'keys', 'ca', 'cacert.pem')
......@@ -20,6 +20,7 @@ Configuration for a LEAP provider.
import binascii
import json
import os
import pkg_resources
import platform
import shutil
import sys
......@@ -37,6 +38,7 @@ 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.util import here, STANDALONE
from leap.common.check import leap_assert
from leap.common.config import get_path_prefix as common_get_path_prefix
......@@ -73,10 +75,17 @@ def get_provider_path(domain, config='provider.json'):
return os.path.join('providers', domain, config)
def get_ca_cert_path(domain):
# TODO sanitize domain
def get_ca_cert_path(basedir, domain):
leap_assert(domain is not None, 'get_provider_path: We need a domain')
return os.path.join('providers', domain, 'keys', 'ca', 'cacert.pem')
enc_domain = domain.encode(sys.getfilesystemencoding())
cert_path = os.path.join(basedir, 'providers', enc_domain, 'keys', 'ca',
'cacert.pem')
if not is_file(cert_path):
pinned_cert = get_pinned_path(domain, '.pem')
if is_file(pinned_cert):
return pinned_cert
return cert_path
def update_modification_ts(path):
......@@ -128,7 +137,12 @@ def list_providers():
path = os.path.expanduser(path)
if not os.path.isdir(path):
os.makedirs(path)
return os.listdir(path)
configured = os.listdir(path)
pinned = os.listdir(get_pinned_path())
pinned = [provider[:-5] for provider in pinned if provider[-5:] == ".json"]
return set(configured + pinned)
def delete_provider(domain):
......@@ -141,6 +155,20 @@ def delete_provider(domain):
Provider.providers[domain] = None
def get_pinned_path(domain=None, extension='.json'):
if domain is None:
filename = ''
else:
filename = domain.encode(sys.getfilesystemencoding()) + extension
if STANDALONE:
# TODO: do the bundling part
return os.path.join(here(), "..", "apps", "providers", filename)
return pkg_resources.resource_filename(
'leap.bitmask.bonafide.providers', filename)
class Provider(object):
SERVICES_MAP = {
......@@ -210,9 +238,13 @@ class Provider(object):
def is_configured(self):
provider_json = self._get_provider_json_path()
if not is_file(provider_json):
pinned_json = get_pinned_path(self._domain)
if not is_file(provider_json) and not is_file(pinned_json):
return False
if not is_file(self._get_ca_cert_path()):
provider_cert = self._get_ca_cert_path()
pinned_cert = get_pinned_path(self._domain, '.pem')
if not is_file(provider_cert) and not is_file(pinned_cert):
return False
return True
......@@ -246,7 +278,7 @@ class Provider(object):
return failure
d = self.maybe_download_provider_info(replace=replace_if_newer)
d.addCallback(self.maybe_download_ca_cert)
d.addCallback(self.maybe_download_ca_cert, replace_if_newer)
d.addCallback(self.validate_ca_cert)
d.addCallbacks(first_bootstrap_done, first_bootstrap_error)
d.addCallback(self.maybe_download_services_config)
......@@ -270,8 +302,6 @@ class Provider(object):
Download the provider.json info from the main domain.
This SHOULD only be used once with the DOMAIN url.
"""
# TODO handle pre-seeded providers?
# or let client handle that? We could move them to bonafide.
provider_json = self._get_provider_json_path()
if is_file(provider_json) and not replace:
......@@ -300,12 +330,15 @@ class Provider(object):
"""
pass
def maybe_download_ca_cert(self, ignored):
def maybe_download_ca_cert(self, ignored, replace=False):
"""
:rtype: deferred
"""
path = self._get_ca_cert_path()
if is_file(path):
# TODO: doesn't update the cert :((((
enc_domain = self._domain.encode(sys.getfilesystemencoding())
path = os.path.join(self._basedir, 'providers', enc_domain, 'keys',
'ca', 'cacert.pem')
if not replace and is_file(path):
return defer.succeed('ca_cert_path_already_exists')
def errback(failure):
......@@ -452,9 +485,7 @@ class Provider(object):
return configs_path
def _get_ca_cert_path(self):
domain = self._domain.encode(sys.getfilesystemencoding())
cert_path = os.path.join(self._basedir, get_ca_cert_path(domain))
return cert_path
return get_ca_cert_path(self._basedir, self._domain)
def _get_ca_cert_uri(self):
try:
......@@ -468,7 +499,11 @@ class Provider(object):
path = self._get_provider_json_path()
if not is_file(path):
self.log.debug('cannot LOAD provider config path %s' % path)
return
path = get_pinned_path(self._domain)
if not is_file(path):
return
self.log.debug('using pinned provider %s' % path)
with open(path, 'r') as config:
self._provider_config = Record(**json.load(config))
......
{
"api_uri": "https://api.calyx.net:4430",
"api_version": "1",
"ca_cert_fingerprint": "SHA256: 43683c9ba3862c5384a8c1885072fcac40b5d2d4dd67331443f13a3077fa2e69",
"ca_cert_uri": "https://calyx.net/ca.crt",
"default_language": "en",
"description": {
"en": "Calyx Institute privacy focused ISP testbed"
},
"domain": "calyx.net",
"enrollment_policy": "open",
"languages": [
"en"
],
"name": {
"en": "calyx"
},
"service": {
"allow_anonymous": false,
"allow_free": true,
"allow_limited_bandwidth": false,
"allow_paid": false,
"allow_registration": true,
"allow_unlimited_bandwidth": true,
"bandwidth_limit": 102400,
"default_service_level": 1,
"levels": {
"1": {
"description": "Please donate.",
"name": "free"
}
}
},
"services": [
"openvpn"
]
}
\ No newline at end of file
-----BEGIN CERTIFICATE-----
MIIFYzCCA0ugAwIBAgIBATANBgkqhkiG9w0BAQ0FADBEMQ4wDAYDVQQKDAVjYWx5
eDEaMBgGA1UECwwRaHR0cHM6Ly9jYWx5eC5uZXQxFjAUBgNVBAMMDWNhbHl4IFJv
b3QgQ0EwHhcNMTMwNzAyMDAwMDAwWhcNMjMwNzAyMDAwMDAwWjBEMQ4wDAYDVQQK
DAVjYWx5eDEaMBgGA1UECwwRaHR0cHM6Ly9jYWx5eC5uZXQxFjAUBgNVBAMMDWNh
bHl4IFJvb3QgQ0EwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQDupdnx
Bgat537XOqrZOulE/RvjoXB1S07sy9/MMtksXFoQuWJZRCSTp1Jaqg3H/e9o1nct
LQO91+izfJe07TUyajFl7CfllYgMeyKTYcT85dFwNX4pcIHZr8UpmO0MpGBoR4W1
8cPa3vxAG0CsyUmrASJVyhRouk4qazRosM5RwBxTdMzCK7L3SwqPQoxlY9YmRJlD
XYZlK5VMJd0dj9XxhMeFs5n43R0bsDENryrExSbuxoNfnUoQg3wffKk+Z0gW7YgW
ivPsbObqOgXUuBEU0xr9xMNBpU33ffLIsccrHq1EKp8zGfCOcww6v7+zEadUkVLo
6j/rRhYYgRw9lijZG1rMuV/mTGnUqbjHsdoz5mzkFFWeTSqo44lvhveUyCcwRNmi
2sjS77l0fCTzfreufffFoOEcRVMRfsnJdu/xPeARoXILEx8nQ421mSn6spOZlDQr
Tt0T0BAWt+VNc+m0IGSW3SwS7r5MUyQ/M5GrbQBGi5W2SzPriKZ79YTOwPVmXKLZ
vJoEuKRDkEPJLBAhcD5oSQljOm/Wp/hjmRH4HnI1y4XMshWlDsyRDB1Au5yrsfwN
noFVSskEcbXlZfNgml4lktLBqz+qwsw+voq6Ak7ROKbc0ii5s8+iNMbAtIK7GcFF
kuKKIyRmmGlDim/SDhlNdWo7Ah4Akde7zfWufwIDAQABo2AwXjAdBgNVHQ4EFgQU
AY8+K4ZupAQ+L9ttFJG3vaLBq5gwDgYDVR0PAQH/BAQDAgIEMAwGA1UdEwQFMAMB
Af8wHwYDVR0jBBgwFoAUAY8+K4ZupAQ+L9ttFJG3vaLBq5gwDQYJKoZIhvcNAQEN
BQADggIBAOpXi5o3g/2o2rPa53iG7Zgcy8RpePGgZk6xknGYWeLamEqSh+XWQZ2w
2kQP54bf8HfPj3ugJBWsVtYAs/ltJwzeBfYDrwEJd1N8tw2IRuGlQOWiTAVVLBj4
Zs+dikSuMoA399f/7BlUIEpVLUiV/emTtbkjFnDeKEV9zql6ypR0BtR8Knf8ALvL
YfMsWLvTe4rXeypzxIaE2pn8ttcXLYAX0ml2MofTi5xcDhMn1vznKIvs82xhncQx
I1MJMWqPHNHgJUJpA+y1IFh5LPbpag9PKQ0yQ9sM+/dyGumF2jElsMw71flh/Txr
2dEv8+FNV1pPK26XJZBK24rNWFs30eAFfH9EQCwVla174I4PDoWqsIR7vtQMObDt
Bq34R3TjjJJIt2sCSlYLooWwiK7Q+d/SgYqA+MSDmmwhzm86ToK6cwbCsvuw1AxR
X6VIs4U8wOotgljzX/CSpKqlxcqZjhnAuelZ1+KiN8RHKPj7AzSLYOv/YwTjLTIq
EOxquoNR58uDa5pBG22a7xWbSaKosn/mEl8SrUr6klzzc8Vh09IMoxrw74uLdAg2
1jnrhm7qg91Ttb0aXiqbV+Kg/qQzojdewnnoBFnv4jaQ3y8zDCfMhsBtWlWz4Knb
Zqga1WyRm3Gj1j6IV0oOincYMrw5YA7bgXpwop/Lo/mmliMA14ps
-----END CERTIFICATE-----
{
"api_uri": "https://api.demo.bitmask.net:4430",
"api_version": "1",
"ca_cert_fingerprint": "SHA256: 0f17c033115f6b76ff67871872303ff65034efe7dd1b910062ca323eb4da5c7e",
"ca_cert_uri": "https://demo.bitmask.net/ca.crt",
"default_language": "en",
"description": {
"el": "demo.bitmask.net allows you to test the Bitmask application. User accounts may be periodically deleted.",
"en": "demo.bitmask.net allows you to test the Bitmask application. User accounts may be periodically deleted.",
"es": "demo.bitmask.net allows you to test the Bitmask application. User accounts may be periodically deleted."
},
"domain": "demo.bitmask.net",
"enrollment_policy": "open",
"languages": [
"de",
"en",
"es",
"pt"
],
"name": {
"en": "Bitmask"
},
"service": {
"allow_anonymous": true,
"allow_free": true,
"allow_limited_bandwidth": false,
"allow_paid": false,
"allow_registration": true,
"allow_unlimited_bandwidth": true,
"bandwidth_limit": 102400,
"default_service_level": 1,
"levels": {
"1": {
"description": "Please donate.",
"name": "free"
}
}
},
"services": [
"openvpn"
]
}
\ No newline at end of file
-----BEGIN CERTIFICATE-----
MIIFbzCCA1egAwIBAgIBATANBgkqhkiG9w0BAQ0FADBKMRgwFgYDVQQDDA9CaXRt
YXNrIFJvb3QgQ0ExEDAOBgNVBAoMB0JpdG1hc2sxHDAaBgNVBAsME2h0dHBzOi8v
Yml0bWFzay5uZXQwHhcNMTIxMTA2MDAwMDAwWhcNMjIxMTA2MDAwMDAwWjBKMRgw
FgYDVQQDDA9CaXRtYXNrIFJvb3QgQ0ExEDAOBgNVBAoMB0JpdG1hc2sxHDAaBgNV
BAsME2h0dHBzOi8vYml0bWFzay5uZXQwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAw
ggIKAoICAQC1eV4YvayaU+maJbWrD4OHo3d7S1BtDlcvkIRS1Fw3iYDjsyDkZxai
dHp4EUasfNQ+EVtXUvtk6170EmLco6Elg8SJBQ27trE6nielPRPCfX3fQzETRfvB
7tNvGw4Jn2YKiYoMD79kkjgyZjkJ2r/bEHUSevmR09BRp86syHZerdNGpXYhcQ84
CA1+V+603GFIHnrP+uQDdssW93rgDNYu+exT+Wj6STfnUkugyjmPRPjL7wh0tzy+
znCeLl4xiV3g9sjPnc7r2EQKd5uaTe3j71sDPF92KRk0SSUndREz+B1+Dbe/RGk4
MEqGFuOzrtsgEhPIX0hplhb0Tgz/rtug+yTT7oJjBa3u20AAOQ38/M99EfdeJvc4
lPFF1XBBLh6X9UKF72an2NuANiX6XPySnJgZ7nZ09RiYZqVwu/qt3DfvLfhboq+0
bQvLUPXrVDr70onv5UDjpmEA/cLmaIqqrduuTkFZOym65/PfAPvpGnt7crQj/Ibl
DEDYZQmP7AS+6zBjoOzNjUGE5r40zWAR1RSi7zliXTu+yfsjXUIhUAWmYR6J3KxB
lfsiHBQ+8dn9kC3YrUexWoOqBiqJOAJzZh5Y1tqgzfh+2nmHSB2dsQRs7rDRRlyy
YMbkpzL9ZsOUO2eTP1mmar6YjCN+rggYjRrX71K2SpBG6b1zZxOG+wIDAQABo2Aw
XjAdBgNVHQ4EFgQUuYGDLL2sswnYpHHvProt1JU+D48wDgYDVR0PAQH/BAQDAgIE
MAwGA1UdEwQFMAMBAf8wHwYDVR0jBBgwFoAUuYGDLL2sswnYpHHvProt1JU+D48w
DQYJKoZIhvcNAQENBQADggIBADeG67vaFcbITGpi51264kHPYPEWaXUa5XYbtmBl
cXYyB6hY5hv/YNuVGJ1gWsDmdeXEyj0j2icGQjYdHRfwhrbEri+h1EZOm1cSBDuY
k/P5+ctHyOXx8IE79DBsZ6IL61UKIaKhqZBfLGYcWu17DVV6+LT+AKtHhOrv3TSj
RnAcKnCbKqXLhUPXpK0eTjPYS2zQGQGIhIy9sQXVXJJJsGrPgMxna1Xw2JikBOCG
htD/JKwt6xBmNwktH0GI/LVtVgSp82Clbn9C4eZN9E5YbVYjLkIEDhpByeC71QhX
EIQ0ZR56bFuJA/CwValBqV/G9gscTPQqd+iETp8yrFpAVHOW+YzSFbxjTEkBte1J
aF0vmbqdMAWLk+LEFPQRptZh0B88igtx6tV5oVd+p5IVRM49poLhuPNJGPvMj99l
mlZ4+AeRUnbOOeAEuvpLJbel4rhwFzmUiGoeTVoPZyMevWcVFq6BMkS+jRR2w0jK
G6b0v5XDHlcFYPOgUrtsOBFJVwbutLvxdk6q37kIFnWCd8L3kmES5q4wjyFK47Co
Ja8zlx64jmMZPg/t3wWqkZgXZ14qnbyG5/lGsj5CwVtfDljrhN0oCWK1FZaUmW3d
69db12/g4f6phldhxiWuGC/W6fCW5kre7nmhshcltqAJJuU47iX+DarBFiIj816e
yV8e
-----END CERTIFICATE-----
{
"api_uri": "https://api.black.riseup.net:443",
"api_version": "1",
"ca_cert_fingerprint": "SHA256: a5244308a1374709a9afce95e3ae47c1b44bc2398c0a70ccbf8b3a8a97f29494",
"ca_cert_uri": "https://black.riseup.net/ca.crt",
"default_language": "en",
"description": {
"en": "Riseup is a non-profit collective in Seattle that provides online communication tools for people and groups working toward liberatory social change."
},
"domain": "riseup.net",
"enrollment_policy": "open",
"languages": [
"en"
],
"name": {
"en": "Riseup Networks"
},
"service": {
"allow_anonymous": false,
"allow_free": true,
"allow_limited_bandwidth": false,
"allow_paid": false,
"allow_registration": true,
"allow_unlimited_bandwidth": true,
"bandwidth_limit": 102400,
"default_service_level": 1,
"levels": {
"1": {
"description": "Please donate.",
"name": "free"
}
}
},
"services": [
"openvpn"
]
}
-----BEGIN CERTIFICATE-----
MIIFjTCCA3WgAwIBAgIBATANBgkqhkiG9w0BAQ0FADBZMRgwFgYDVQQKDA9SaXNl
dXAgTmV0d29ya3MxGzAZBgNVBAsMEmh0dHBzOi8vcmlzZXVwLm5ldDEgMB4GA1UE
AwwXUmlzZXVwIE5ldHdvcmtzIFJvb3QgQ0EwHhcNMTQwNDI4MDAwMDAwWhcNMjQw
NDI4MDAwMDAwWjBZMRgwFgYDVQQKDA9SaXNldXAgTmV0d29ya3MxGzAZBgNVBAsM
Emh0dHBzOi8vcmlzZXVwLm5ldDEgMB4GA1UEAwwXUmlzZXVwIE5ldHdvcmtzIFJv
b3QgQ0EwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQC76J4ciMJ8Sg0m
TP7DF2DT9zNe0Csk4myoMFC57rfJeqsAlJCv1XMzBmXrw8wq/9z7XHv6n/0sWU7a
7cF2hLR33ktjwODlx7vorU39/lXLndo492ZBhXQtG1INMShyv+nlmzO6GT7ESfNE
LliFitEzwIegpMqxCIHXFuobGSCWF4N0qLHkq/SYUMoOJ96O3hmPSl1kFDRMtWXY
iw1SEKjUvpyDJpVs3NGxeLCaA7bAWhDY5s5Yb2fA1o8ICAqhowurowJpW7n5ZuLK
5VNTlNy6nZpkjt1QycYvNycffyPOFm/Q/RKDlvnorJIrihPkyniV3YY5cGgP+Qkx
HUOT0uLA6LHtzfiyaOqkXwc4b0ZcQD5Vbf6Prd20Ppt6ei0zazkUPwxld3hgyw58
m/4UIjG3PInWTNf293GngK2Bnz8Qx9e/6TueMSAn/3JBLem56E0WtmbLVjvko+LF
PM5xA+m0BmuSJtrD1MUCXMhqYTtiOvgLBlUm5zkNxALzG+cXB28k6XikXt6MRG7q
hzIPG38zwkooM55yy5i1YfcIi5NjMH6A+t4IJxxwb67MSb6UFOwg5kFokdONZcwj
shczHdG9gLKSBIvrKa03Nd3W2dF9hMbRu//STcQxOailDBQCnXXfAATj9pYzdY4k
ha8VCAREGAKTDAex9oXf1yRuktES4QIDAQABo2AwXjAdBgNVHQ4EFgQUC4tdmLVu
f9hwfK4AGliaet5KkcgwDgYDVR0PAQH/BAQDAgIEMAwGA1UdEwQFMAMBAf8wHwYD
VR0jBBgwFoAUC4tdmLVuf9hwfK4AGliaet5KkcgwDQYJKoZIhvcNAQENBQADggIB
AGzL+GRnYu99zFoy0bXJKOGCF5XUXP/3gIXPRDqQf5g7Cu/jYMID9dB3No4Zmf7v
qHjiSXiS8jx1j/6/Luk6PpFbT7QYm4QLs1f4BlfZOti2KE8r7KRDPIecUsUXW6P/
3GJAVYH/+7OjA39za9AieM7+H5BELGccGrM5wfl7JeEz8in+V2ZWDzHQO4hMkiTQ
4ZckuaL201F68YpiItBNnJ9N5nHr1MRiGyApHmLXY/wvlrOpclh95qn+lG6/2jk7
3AmihLOKYMlPwPakJg4PYczm3icFLgTpjV5sq2md9bRyAg3oPGfAuWHmKj2Ikqch
Td5CHKGxEEWbGUWEMP0s1A/JHWiCbDigc4Cfxhy56CWG4q0tYtnc2GMw8OAUO6Wf
Xu5pYKNkzKSEtT/MrNJt44tTZWbKV/Pi/N2Fx36my7TgTUj7g3xcE9eF4JV2H/sg
tsK3pwE0FEqGnT4qMFbixQmc8bGyuakr23wjMvfO7eZUxBuWYR2SkcP26sozF9PF
tGhbZHQVGZUTVPyvwahMUEhbPGVerOW0IYpxkm0x/eaWdTc4vPpf/rIlgbAjarnJ
UN9SaWRlWKSdP4haujnzCoJbM7dU9bjvlGZNyXEekgeT0W2qFeGGp+yyUWw8tNsp
0BuC1b7uW/bBn/xKm319wXVDvBgZgcktMolak39V7DVO
-----END CERTIFICATE-----
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment