0
0
Fork 1
mirror of https://mau.dev/maunium/synapse.git synced 2024-12-13 19:23:17 +01:00

Make handling of federation Authorization header (more) compliant with RFC7230 (#12774)

The main differences are:
- values with delimiters (such as colons) should be quoted, so always
  quote the origin, since it could contain a colon followed by a port
  number
- should allow more than one space after "X-Matrix"
- quoted values with backslash-escaped characters should be unescaped
- names should be case insensitive
This commit is contained in:
Hubert Chathi 2022-05-18 06:19:30 -04:00 committed by GitHub
parent 37935b5183
commit 8afb7b55d0
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
4 changed files with 35 additions and 5 deletions

1
changelog.d/12774.misc Normal file
View file

@ -0,0 +1 @@
Make handling of federation Authorization header (more) compliant with RFC7230.

View file

@ -169,14 +169,16 @@ def _parse_auth_header(header_bytes: bytes) -> Tuple[str, str, str, Optional[str
""" """
try: try:
header_str = header_bytes.decode("utf-8") header_str = header_bytes.decode("utf-8")
params = header_str.split(" ")[1].split(",") params = re.split(" +", header_str)[1].split(",")
param_dict: Dict[str, str] = { param_dict: Dict[str, str] = {
k: v for k, v in [param.split("=", maxsplit=1) for param in params] k.lower(): v for k, v in [param.split("=", maxsplit=1) for param in params]
} }
def strip_quotes(value: str) -> str: def strip_quotes(value: str) -> str:
if value.startswith('"'): if value.startswith('"'):
return value[1:-1] return re.sub(
"\\\\(.)", lambda matchobj: matchobj.group(1), value[1:-1]
)
else: else:
return value return value

View file

@ -747,7 +747,7 @@ class MatrixFederationHttpClient:
for key, sig in request["signatures"][self.server_name].items(): for key, sig in request["signatures"][self.server_name].items():
auth_headers.append( auth_headers.append(
( (
'X-Matrix origin=%s,key="%s",sig="%s",destination="%s"' 'X-Matrix origin="%s",key="%s",sig="%s",destination="%s"'
% ( % (
self.server_name, self.server_name,
key, key,

View file

@ -17,7 +17,7 @@ from typing import Dict, List, Tuple
from synapse.api.errors import Codes from synapse.api.errors import Codes
from synapse.federation.transport.server import BaseFederationServlet from synapse.federation.transport.server import BaseFederationServlet
from synapse.federation.transport.server._base import Authenticator from synapse.federation.transport.server._base import Authenticator, _parse_auth_header
from synapse.http.server import JsonResource, cancellable from synapse.http.server import JsonResource, cancellable
from synapse.server import HomeServer from synapse.server import HomeServer
from synapse.types import JsonDict from synapse.types import JsonDict
@ -112,3 +112,30 @@ class BaseFederationServletCancellationTests(
expect_cancellation=False, expect_cancellation=False,
expected_body={"result": True}, expected_body={"result": True},
) )
class BaseFederationAuthorizationTests(unittest.TestCase):
def test_authorization_header(self) -> None:
"""Tests that the Authorization header is parsed correctly."""
# test a "normal" Authorization header
self.assertEqual(
_parse_auth_header(
b'X-Matrix origin=foo,key="ed25519:1",sig="sig",destination="bar"'
),
("foo", "ed25519:1", "sig", "bar"),
)
# test an Authorization with extra spaces, upper-case names, and escaped
# characters
self.assertEqual(
_parse_auth_header(
b'X-Matrix ORIGIN=foo,KEY="ed25\\519:1",SIG="sig",destination="bar"'
),
("foo", "ed25519:1", "sig", "bar"),
)
self.assertEqual(
_parse_auth_header(
b'X-Matrix origin=foo,key="ed25519:1",sig="sig",destination="bar",extra_field=ignored'
),
("foo", "ed25519:1", "sig", "bar"),
)