From 8945cc9e106465f4f4531598d49173fd7438361a Mon Sep 17 00:00:00 2001 From: Erwan Leboucher Date: Wed, 15 Jul 2026 21:53:47 +0200 Subject: [PATCH] fix(federation): Sign restricted join events before verification --- changelog.d/fix-federation-signature.bugfix | 1 + src/api/server/utils.rs | 33 ++++++++++++++++++++- 2 files changed, 33 insertions(+), 1 deletion(-) create mode 100644 changelog.d/fix-federation-signature.bugfix diff --git a/changelog.d/fix-federation-signature.bugfix b/changelog.d/fix-federation-signature.bugfix new file mode 100644 index 000000000..eb2311a81 --- /dev/null +++ b/changelog.d/fix-federation-signature.bugfix @@ -0,0 +1 @@ +Fix joining restricted rooms over federation failing with signature verification error. diff --git a/src/api/server/utils.rs b/src/api/server/utils.rs index 774675de7..d9a67c2d3 100644 --- a/src/api/server/utils.rs +++ b/src/api/server/utils.rs @@ -91,7 +91,7 @@ pub(crate) async fn validate_any_membership_event( expected_room_id: OwnedRoomId, expected_event_id: OwnedEventId, ) -> Result<(CanonicalJsonObject, MembershipState, OwnedUserId, OwnedUserId)> { - let (template_room_id, template_event_id, pdu) = services + let (template_room_id, template_event_id, mut pdu) = services .rooms .event_handler .parse_incoming_pdu(body, Some(room_version_rules)) @@ -109,6 +109,37 @@ pub(crate) async fn validate_any_membership_event( )))); } + // Only `join` events carry `join_authorised_via_users_server`; co-sign + // restricted joins so verification passes. Authorisation is enforced in + // create_join_event. + let membership_is_join = pdu + .get("content") + .and_then(|v| v.as_object()) + .and_then(|c| c.get("membership")) + .and_then(|v| v.as_str()) + .is_some_and(|m| m == "join"); + + let authorising_user = pdu + .get("content") + .and_then(|v| v.as_object()) + .and_then(|c| c.get("join_authorised_via_users_server")) + .and_then(|v| v.as_str()) + .map(UserId::parse) + .and_then(Result::ok); + + if room_version_rules.authorization.restricted_join_rule + && membership_is_join + && let Some(authorising_user) = authorising_user + && services.globals.user_is_local(&authorising_user) + { + services + .server_keys + .hash_and_sign_event(&mut pdu, room_version_rules) + .map_err(|e| { + err!(Request(InvalidParam("Failed to sign restricted join event: {e}"))) + })?; + } + services .server_keys .verify_event(&pdu, room_version_rules)