mirror of
https://github.com/element-hq/synapse.git
synced 2026-08-29 05:38:26 +00:00
Validate policy server response and prevent overwriting signatures
This commit is contained in:
@@ -473,7 +473,7 @@ class FederationClient(FederationBase):
|
||||
@tag_args
|
||||
async def ask_policy_server_to_sign_event(
|
||||
self, destination: str, pdu: EventBase, timeout: int | None = None
|
||||
) -> JsonDict:
|
||||
) -> PolicySignResponse:
|
||||
"""Requests that the destination server (typically a policy server)
|
||||
sign the event as not spam.
|
||||
|
||||
@@ -494,9 +494,10 @@ class FederationClient(FederationBase):
|
||||
pdu.event_id,
|
||||
destination,
|
||||
)
|
||||
return await self.transport_layer.ask_policy_server_to_sign_event(
|
||||
json_response = await self.transport_layer.ask_policy_server_to_sign_event(
|
||||
destination, pdu, timeout=timeout
|
||||
)
|
||||
return validate_response(json_response, PolicySignResponse)
|
||||
|
||||
@trace
|
||||
@tag_args
|
||||
|
||||
@@ -225,7 +225,7 @@ class RoomPolicyHandler:
|
||||
|
||||
# Ask the policy server to sign this event.
|
||||
try:
|
||||
signature = await self._federation_client.ask_policy_server_to_sign_event(
|
||||
sign_response = await self._federation_client.ask_policy_server_to_sign_event(
|
||||
policy_server.server_name,
|
||||
event,
|
||||
# We set a smallish timeout here as we don't want to block event sending
|
||||
@@ -241,7 +241,7 @@ class RoomPolicyHandler:
|
||||
# also be implementation bugs. Whoever reads this when removing unstable MSC4284
|
||||
# stuff, make a decision on whether to remove this bit.
|
||||
# https://github.com/element-hq/synapse/issues/19502
|
||||
if not signature or len(signature) == 0:
|
||||
if len(sign_response.root) == 0:
|
||||
raise SynapseError(
|
||||
403,
|
||||
"This event has been rejected as probable spam by the policy server",
|
||||
@@ -261,7 +261,14 @@ class RoomPolicyHandler:
|
||||
# servers need to manually fetch signatures for. This is the code that allows
|
||||
# those events to continue working (because they're legally sent, even if missing
|
||||
# the policy server signature).
|
||||
event.signatures.update(signature)
|
||||
for key_id, signature_b64 in sign_response.root.items():
|
||||
if (
|
||||
event.signatures.get_signature(policy_server.server_name, key_id)
|
||||
is None
|
||||
):
|
||||
event.signatures.add_signature(
|
||||
policy_server.server_name, key_id, signature_b64
|
||||
)
|
||||
except HttpResponseException as ex:
|
||||
# re-wrap HTTP errors as `SynapseError` so they can be proxied to clients directly
|
||||
raise ex.to_synapse_error() from ex
|
||||
|
||||
@@ -0,0 +1,51 @@
|
||||
#
|
||||
# This file is licensed under the Affero General Public License (AGPL) version 3.
|
||||
#
|
||||
# Copyright (C) 2026 Element Creations Ltd.
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU Affero General Public License as
|
||||
# published by the Free Software Foundation, either version 3 of the
|
||||
# License, or (at your option) any later version.
|
||||
#
|
||||
# See the GNU Affero General Public License for more details:
|
||||
# <https://www.gnu.org/licenses/agpl_3.0.html>.
|
||||
#
|
||||
from pydantic import model_validator
|
||||
from typing_extensions import Self
|
||||
|
||||
from synapse.util.pydantic_models import StrictRootModel
|
||||
|
||||
|
||||
class PolicySignResponse(StrictRootModel[dict[str, str]]):
|
||||
"""
|
||||
Response to `POST /_matrix/policy/v1/sign`
|
||||
Spec: https://spec.matrix.org/v1.18/server-server-api/#post_matrixpolicyv1sign
|
||||
|
||||
Example:
|
||||
{
|
||||
"policy.example.org": {
|
||||
"ed25519:policy_server": "zLFxllD0pbBuBpfHh8NuHNaICpReF/PAOpUQTsw+bFGKiGfDNAsnhcP7pbrmhhpfbOAxIdLraQLeeiXBryLmBw"
|
||||
}
|
||||
}
|
||||
"""
|
||||
|
||||
@model_validator(mode="after")
|
||||
def check_rules(self) -> Self:
|
||||
"""
|
||||
> The Policy Server has signed the event, indicating that it recommends the event for inclusion in the room.
|
||||
> Only the Policy Server’s signature is returned.
|
||||
> This signature is to be added to the event before sending or processing the event further.
|
||||
>
|
||||
> `ed25519:policy_server` is always used for Ed25519 signatures.
|
||||
> — https://spec.matrix.org/v1.18/server-server-api/#validating-policy-server-signatures
|
||||
"""
|
||||
|
||||
for key_id in self.root.keys():
|
||||
if key_id.startswith("ed25519:") and key_id != "ed25519:policy_server":
|
||||
# > `ed25519:policy_server` is always used for Ed25519 signatures.
|
||||
raise ValueError(
|
||||
"policy servers must only use ed25519:policy_server for Ed25519 signatures"
|
||||
)
|
||||
|
||||
return self
|
||||
Reference in New Issue
Block a user