Stop hardcoding trust of old matrix.org key (#5374)

There are a few changes going on here:

* We make checking the signature on a key server response optional: if no
  verify_keys are specified, we trust to TLS to validate the connection.

* We change the default config so that it does not require responses to be
  signed by the old key.

* We replace the old 'perspectives' config with 'trusted_key_servers', which
  is also formatted slightly differently.

* We emit a warning to the logs every time we trust a key server response
  signed by the old key.
This commit is contained in:
Richard van der Hoff 2019-06-06 17:33:11 +01:00 committed by GitHub
parent 833c406b9b
commit 9fbb20a531
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
6 changed files with 294 additions and 96 deletions

View file

@ -585,25 +585,27 @@ class PerspectivesKeyFetcher(BaseV2KeyFetcher):
super(PerspectivesKeyFetcher, self).__init__(hs)
self.clock = hs.get_clock()
self.client = hs.get_http_client()
self.perspective_servers = self.config.perspectives
self.key_servers = self.config.key_servers
@defer.inlineCallbacks
def get_keys(self, keys_to_fetch):
"""see KeyFetcher.get_keys"""
@defer.inlineCallbacks
def get_key(perspective_name, perspective_keys):
def get_key(key_server):
try:
result = yield self.get_server_verify_key_v2_indirect(
keys_to_fetch, perspective_name, perspective_keys
keys_to_fetch, key_server
)
defer.returnValue(result)
except KeyLookupError as e:
logger.warning("Key lookup failed from %r: %s", perspective_name, e)
logger.warning(
"Key lookup failed from %r: %s", key_server.server_name, e
)
except Exception as e:
logger.exception(
"Unable to get key from %r: %s %s",
perspective_name,
key_server.server_name,
type(e).__name__,
str(e),
)
@ -613,8 +615,8 @@ class PerspectivesKeyFetcher(BaseV2KeyFetcher):
results = yield logcontext.make_deferred_yieldable(
defer.gatherResults(
[
run_in_background(get_key, p_name, p_keys)
for p_name, p_keys in self.perspective_servers.items()
run_in_background(get_key, server)
for server in self.key_servers
],
consumeErrors=True,
).addErrback(unwrapFirstError)
@ -629,17 +631,15 @@ class PerspectivesKeyFetcher(BaseV2KeyFetcher):
@defer.inlineCallbacks
def get_server_verify_key_v2_indirect(
self, keys_to_fetch, perspective_name, perspective_keys
self, keys_to_fetch, key_server
):
"""
Args:
keys_to_fetch (dict[str, dict[str, int]]):
the keys to be fetched. server_name -> key_id -> min_valid_ts
perspective_name (str): name of the notary server to query for the keys
perspective_keys (dict[str, VerifyKey]): map of key_id->key for the
notary server
key_server (synapse.config.key.TrustedKeyServer): notary server to query for
the keys
Returns:
Deferred[dict[str, dict[str, synapse.storage.keys.FetchKeyResult]]]: map
@ -649,6 +649,7 @@ class PerspectivesKeyFetcher(BaseV2KeyFetcher):
KeyLookupError if there was an error processing the entire response from
the server
"""
perspective_name = key_server.server_name
logger.info(
"Requesting keys %s from notary server %s",
keys_to_fetch.items(),
@ -689,11 +690,13 @@ class PerspectivesKeyFetcher(BaseV2KeyFetcher):
)
try:
processed_response = yield self._process_perspectives_response(
perspective_name,
perspective_keys,
self._validate_perspectives_response(
key_server,
response,
time_added_ms=time_now_ms,
)
processed_response = yield self.process_v2_response(
perspective_name, response, time_added_ms=time_now_ms
)
except KeyLookupError as e:
logger.warning(
@ -717,28 +720,24 @@ class PerspectivesKeyFetcher(BaseV2KeyFetcher):
defer.returnValue(keys)
def _process_perspectives_response(
self, perspective_name, perspective_keys, response, time_added_ms
def _validate_perspectives_response(
self, key_server, response,
):
"""Parse a 'Server Keys' structure from the result of a /key/query request
Checks that the entry is correctly signed by the perspectives server, and then
passes over to process_v2_response
"""Optionally check the signature on the result of a /key/query request
Args:
perspective_name (str): the name of the notary server that produced this
result
perspective_keys (dict[str, VerifyKey]): map of key_id->key for the
notary server
key_server (synapse.config.key.TrustedKeyServer): the notary server that
produced this result
response (dict): the json-decoded Server Keys response object
time_added_ms (int): the timestamp to record in server_keys_json
Returns:
Deferred[dict[str, FetchKeyResult]]: map from key_id to result object
"""
perspective_name = key_server.server_name
perspective_keys = key_server.verify_keys
if perspective_keys is None:
# signature checking is disabled on this server
return
if (
u"signatures" not in response
or perspective_name not in response[u"signatures"]
@ -751,6 +750,13 @@ class PerspectivesKeyFetcher(BaseV2KeyFetcher):
verify_signed_json(response, perspective_name, perspective_keys[key_id])
verified = True
if perspective_name == "matrix.org" and key_id == "ed25519:auto":
logger.warning(
"Trusting trusted_key_server responses signed by the "
"compromised matrix.org signing key 'ed25519:auto'. "
"This is a placebo."
)
if not verified:
raise KeyLookupError(
"Response not signed with a known key: signed with: %r, known keys: %r"
@ -760,10 +766,6 @@ class PerspectivesKeyFetcher(BaseV2KeyFetcher):
)
)
return self.process_v2_response(
perspective_name, response, time_added_ms=time_added_ms
)
class ServerKeyFetcher(BaseV2KeyFetcher):
"""KeyFetcher impl which fetches keys from the origin servers"""