From bb69ee68d0674cb87049489dae8c22665916534f Mon Sep 17 00:00:00 2001 From: nexy7574 Date: Fri, 13 Jun 2025 17:55:56 +0100 Subject: [PATCH 1/3] Add room version H11 --- Cargo.lock | 11 ------ Cargo.toml | 6 ++-- src/core/matrix/state_res/room_version.rs | 43 ++++++++++++++++++----- 3 files changed, 37 insertions(+), 23 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 160be0c7..e7a61534 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3695,7 +3695,6 @@ dependencies = [ [[package]] name = "ruma" version = "0.10.1" -source = "git+https://forgejo.ellis.link/continuwuation/ruwuma?rev=d6870a7fb7f6cccff63f7fd0ff6c581bad80e983#d6870a7fb7f6cccff63f7fd0ff6c581bad80e983" dependencies = [ "assign", "js_int", @@ -3715,7 +3714,6 @@ dependencies = [ [[package]] name = "ruma-appservice-api" version = "0.10.0" -source = "git+https://forgejo.ellis.link/continuwuation/ruwuma?rev=d6870a7fb7f6cccff63f7fd0ff6c581bad80e983#d6870a7fb7f6cccff63f7fd0ff6c581bad80e983" dependencies = [ "js_int", "ruma-common", @@ -3727,7 +3725,6 @@ dependencies = [ [[package]] name = "ruma-client-api" version = "0.18.0" -source = "git+https://forgejo.ellis.link/continuwuation/ruwuma?rev=d6870a7fb7f6cccff63f7fd0ff6c581bad80e983#d6870a7fb7f6cccff63f7fd0ff6c581bad80e983" dependencies = [ "as_variant", "assign", @@ -3750,7 +3747,6 @@ dependencies = [ [[package]] name = "ruma-common" version = "0.13.0" -source = "git+https://forgejo.ellis.link/continuwuation/ruwuma?rev=d6870a7fb7f6cccff63f7fd0ff6c581bad80e983#d6870a7fb7f6cccff63f7fd0ff6c581bad80e983" dependencies = [ "as_variant", "base64 0.22.1", @@ -3782,7 +3778,6 @@ dependencies = [ [[package]] name = "ruma-events" version = "0.28.1" -source = "git+https://forgejo.ellis.link/continuwuation/ruwuma?rev=d6870a7fb7f6cccff63f7fd0ff6c581bad80e983#d6870a7fb7f6cccff63f7fd0ff6c581bad80e983" dependencies = [ "as_variant", "indexmap 2.9.0", @@ -3807,7 +3802,6 @@ dependencies = [ [[package]] name = "ruma-federation-api" version = "0.9.0" -source = "git+https://forgejo.ellis.link/continuwuation/ruwuma?rev=d6870a7fb7f6cccff63f7fd0ff6c581bad80e983#d6870a7fb7f6cccff63f7fd0ff6c581bad80e983" dependencies = [ "bytes", "headers", @@ -3829,7 +3823,6 @@ dependencies = [ [[package]] name = "ruma-identifiers-validation" version = "0.9.5" -source = "git+https://forgejo.ellis.link/continuwuation/ruwuma?rev=d6870a7fb7f6cccff63f7fd0ff6c581bad80e983#d6870a7fb7f6cccff63f7fd0ff6c581bad80e983" dependencies = [ "js_int", "thiserror 2.0.12", @@ -3838,7 +3831,6 @@ dependencies = [ [[package]] name = "ruma-identity-service-api" version = "0.9.0" -source = "git+https://forgejo.ellis.link/continuwuation/ruwuma?rev=d6870a7fb7f6cccff63f7fd0ff6c581bad80e983#d6870a7fb7f6cccff63f7fd0ff6c581bad80e983" dependencies = [ "js_int", "ruma-common", @@ -3848,7 +3840,6 @@ dependencies = [ [[package]] name = "ruma-macros" version = "0.13.0" -source = "git+https://forgejo.ellis.link/continuwuation/ruwuma?rev=d6870a7fb7f6cccff63f7fd0ff6c581bad80e983#d6870a7fb7f6cccff63f7fd0ff6c581bad80e983" dependencies = [ "cfg-if", "proc-macro-crate", @@ -3863,7 +3854,6 @@ dependencies = [ [[package]] name = "ruma-push-gateway-api" version = "0.9.0" -source = "git+https://forgejo.ellis.link/continuwuation/ruwuma?rev=d6870a7fb7f6cccff63f7fd0ff6c581bad80e983#d6870a7fb7f6cccff63f7fd0ff6c581bad80e983" dependencies = [ "js_int", "ruma-common", @@ -3875,7 +3865,6 @@ dependencies = [ [[package]] name = "ruma-signatures" version = "0.15.0" -source = "git+https://forgejo.ellis.link/continuwuation/ruwuma?rev=d6870a7fb7f6cccff63f7fd0ff6c581bad80e983#d6870a7fb7f6cccff63f7fd0ff6c581bad80e983" dependencies = [ "base64 0.22.1", "ed25519-dalek", diff --git a/Cargo.toml b/Cargo.toml index 1abff107..e4c2f665 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -348,9 +348,9 @@ version = "0.1.2" # Used for matrix spec type definitions and helpers [workspace.dependencies.ruma] -git = "https://forgejo.ellis.link/continuwuation/ruwuma" -#branch = "conduwuit-changes" -rev = "d6870a7fb7f6cccff63f7fd0ff6c581bad80e983" +#git = "https://forgejo.ellis.link/continuwuation/ruwuma" +#rev = "b1a55ab8fa3d2e3db3240d04339835f71cfc84d4" +path = "../ruwuma/crates/ruma" # nex: temp features = [ "compat", "rand", diff --git a/src/core/matrix/state_res/room_version.rs b/src/core/matrix/state_res/room_version.rs index 8dfd6cde..fead8576 100644 --- a/src/core/matrix/state_res/room_version.rs +++ b/src/core/matrix/state_res/room_version.rs @@ -29,6 +29,8 @@ pub enum StateResolutionVersion { V1, /// State resolution for room at version 2 or later. V2, + /// State resolution for hydra rooms + V2_1, } #[cfg_attr(not(feature = "unstable-exhaustive-types"), non_exhaustive)] @@ -80,6 +82,19 @@ pub struct RoomVersion { /// /// See: [MSC2175](https://github.com/matrix-org/matrix-spec-proposals/pull/2175) for more information. pub use_room_create_sender: bool, + /// Whether the room creator is a superuser. + /// A superuser will always have infinite power level and gains special privileges. + /// + /// See: [MSC4289](https://github.com/matrix-org/matrix-spec-proposals/pull/4289) for more information. + pub room_creator_is_superuser: bool, + /// Whether the room version supports estoppel events. + /// + /// See: [MSC4290](https://github.com/matrix-org/matrix-spec-proposals/pull/4290) + pub estoppel_events: bool, + /// Whether the room's m.room.create event ID is itself the room ID. + /// + /// See: [MSC4291](https://github.com/matrix-org/matrix-spec-proposals/pull/4291) + pub create_id_as_room_id: bool, } impl RoomVersion { @@ -97,15 +112,9 @@ impl RoomVersion { knock_restricted_join_rule: false, integer_power_levels: false, use_room_create_sender: false, - }; - pub const V10: Self = Self { - knock_restricted_join_rule: true, - integer_power_levels: true, - ..Self::V9 - }; - pub const V11: Self = Self { - use_room_create_sender: true, - ..Self::V10 + room_creator_is_superuser: false, + estoppel_events: false, + create_id_as_room_id: false, }; pub const V2: Self = Self { state_res: StateResolutionVersion::V2, @@ -130,6 +139,21 @@ impl RoomVersion { pub const V7: Self = Self { allow_knocking: true, ..Self::V6 }; pub const V8: Self = Self { restricted_join_rules: true, ..Self::V7 }; pub const V9: Self = Self::V8; + pub const V10: Self = Self { + knock_restricted_join_rule: true, + integer_power_levels: true, + ..Self::V9 + }; + pub const V11: Self = Self { + use_room_create_sender: true, + ..Self::V10 + }; + pub const HYDRA_V11: Self = Self{ + room_creator_is_superuser: true, + estoppel_events: true, + create_id_as_room_id: true, + ..Self::V11 + }; pub fn new(version: &RoomVersionId) -> Result { Ok(match version { @@ -144,6 +168,7 @@ impl RoomVersion { | RoomVersionId::V9 => Self::V9, | RoomVersionId::V10 => Self::V10, | RoomVersionId::V11 => Self::V11, + | RoomVersionId::HydraV11 => Self::HYDRA_V11, | ver => return Err(Error::Unsupported(format!("found version `{ver}`"))), }) } From 78f0031c34254f692efd60c8b134aebd9ce20dfd Mon Sep 17 00:00:00 2001 From: nexy7574 Date: Fri, 13 Jun 2025 18:06:36 +0100 Subject: [PATCH 2/3] Add new room ID check to m.room.create auth --- src/core/matrix/state_res/event_auth.rs | 28 ++++++++++++++++--------- 1 file changed, 18 insertions(+), 10 deletions(-) diff --git a/src/core/matrix/state_res/event_auth.rs b/src/core/matrix/state_res/event_auth.rs index 759ab5cb..6c16182f 100644 --- a/src/core/matrix/state_res/event_auth.rs +++ b/src/core/matrix/state_res/event_auth.rs @@ -56,6 +56,7 @@ pub fn auth_types_for_event( sender: &UserId, state_key: Option<&str>, content: &RawJsonValue, + room_version: &RoomVersion, ) -> serde_json::Result> { if kind == &TimelineEventType::RoomCreate { return Ok(vec![]); @@ -64,8 +65,11 @@ pub fn auth_types_for_event( let mut auth_types = vec![ (StateEventType::RoomPowerLevels, StateKey::new()), (StateEventType::RoomMember, sender.as_str().into()), - (StateEventType::RoomCreate, StateKey::new()), ]; + if !room_version.create_id_as_room_id { + auth_types.push((StateEventType::RoomCreate, StateKey::new())) + // m.room.create is only referenced if it isn't the room ID + } if kind == &TimelineEventType::RoomMember { #[derive(Deserialize)] @@ -183,16 +187,20 @@ where return Ok(false); } - // If the domain of the room_id does not match the domain of the sender, reject - let Some(room_id_server_name) = incoming_event.room_id().server_name() else { - warn!("room ID has no servername"); - return Ok(false); - }; - - if room_id_server_name != sender.server_name() { - warn!("servername of room ID does not match servername of sender"); - return Ok(false); + if room_version.create_id_as_room_id { + let expected = format!("!{}:{}", incoming_event.event_id().localpart(), sender.server_name()); + if incoming_event.room_id().as_str() != expected { + warn!("room create included a room ID that does not match the event ID"); + return Ok(false); + } + } else { + // If the domain of the room_id does not match the domain of the sender, reject + let Some(_room_id_server_name) = incoming_event.room_id().server_name() else { + warn!("room ID has no servername"); + return Ok(false); + }; } + // If content.room_version is present and is not a recognized version, reject let content: RoomCreateContentFields = from_json_str(incoming_event.content().get())?; From f183f99b07aad40c238d56f1f7bd8d5c5d0f5d41 Mon Sep 17 00:00:00 2001 From: nexy7574 Date: Fri, 13 Jun 2025 21:05:10 +0100 Subject: [PATCH 3/3] Utilise the room ID to fetch the create event For #3 Also ran a cargo fmt --- src/core/matrix/state_res/event_auth.rs | 56 ++++++++++++++----- src/core/matrix/state_res/room_version.rs | 33 +++++------ .../rooms/event_handler/handle_outlier_pdu.rs | 5 ++ 3 files changed, 63 insertions(+), 31 deletions(-) diff --git a/src/core/matrix/state_res/event_auth.rs b/src/core/matrix/state_res/event_auth.rs index 6c16182f..414c1b87 100644 --- a/src/core/matrix/state_res/event_auth.rs +++ b/src/core/matrix/state_res/event_auth.rs @@ -5,7 +5,7 @@ use futures::{ future::{OptionFuture, join3}, }; use ruma::{ - Int, OwnedUserId, RoomVersionId, UserId, + EventId, Int, OwnedUserId, RoomVersionId, UserId, events::room::{ create::RoomCreateEventContent, join_rules::{JoinRule, RoomJoinRulesEventContent}, @@ -140,14 +140,16 @@ pub fn auth_types_for_event( event_id = incoming_event.event_id().as_str(), ) )] -pub async fn auth_check( +pub async fn auth_check( room_version: &RoomVersion, incoming_event: &Incoming, current_third_party_invite: Option<&Incoming>, - fetch_state: F, + fetch_state: FS, + fetch_event: FE, ) -> Result where - F: Fn(&StateEventType, &str) -> Fut + Send, + FS: Fn(&StateEventType, &str) -> Fut + Send, + FE: Fn(&EventId) -> Fut + Send, Fut: Future> + Send, Fetched: Event + Send, Incoming: Event + Send + Sync, @@ -188,7 +190,8 @@ where } if room_version.create_id_as_room_id { - let expected = format!("!{}:{}", incoming_event.event_id().localpart(), sender.server_name()); + let expected = + format!("!{}:{}", incoming_event.event_id().localpart(), sender.server_name()); if incoming_event.room_id().as_str() != expected { warn!("room create included a room ID that does not match the event ID"); return Ok(false); @@ -200,7 +203,6 @@ where return Ok(false); }; } - // If content.room_version is present and is not a recognized version, reject let content: RoomCreateContentFields = from_json_str(incoming_event.content().get())?; @@ -249,16 +251,39 @@ where } */ - let (room_create_event, power_levels_event, sender_member_event) = join3( + let (mut room_create_event, power_levels_event, sender_member_event) = join3( fetch_state(&StateEventType::RoomCreate, ""), fetch_state(&StateEventType::RoomPowerLevels, ""), fetch_state(&StateEventType::RoomMember, sender.as_str()), ) .await; - let room_create_event = match room_create_event { + if room_version.create_id_as_room_id { + // TODO: fetch the create event from the room ID + let create_event_id = &EventId::parse(incoming_event.room_id().localpart()); + // if let Err(e) = create_event_id { + // error!(?e, "invalid room ID for create event"); + // return Ok(false); + // } + // room_create_event = fetch_event(create_event_id).await; + match create_event_id { + | Ok(id) => { + room_create_event = fetch_event(id).await; + if room_create_event.is_none() { + warn!("could not find m.room.create event for PDU"); + return Ok(false); + } + room_create_event = room_create_event; + }, + | Err(e) => { + error!(?e, "invalid room ID for create event"); + return Ok(false); + }, + } + } + let real_room_create_event = match room_create_event { | None => { - warn!("no m.room.create event in auth chain"); + warn!("could not find an applicable m.room.create event for PDU"); return Ok(false); }, | Some(e) => e, @@ -267,7 +292,8 @@ where // 3. If event does not have m.room.create in auth_events reject if !incoming_event .auth_events() - .any(|id| id == room_create_event.event_id()) + .any(|id| id == real_room_create_event.event_id()) + && !room_version.create_id_as_room_id { warn!("no m.room.create event in auth events"); return Ok(false); @@ -283,9 +309,9 @@ where federate: bool, } let room_create_content: RoomCreateContentFederate = - from_json_str(room_create_event.content().get())?; + from_json_str(real_room_create_event.content().get())?; if !room_create_content.federate - && room_create_event.sender().server_name() != incoming_event.sender().server_name() + && real_room_create_event.sender().server_name() != incoming_event.sender().server_name() { warn!( "room is not federated and event's sender domain does not match create event's \ @@ -370,7 +396,7 @@ where join_rules_event.as_ref(), user_for_join_auth.as_deref(), &user_for_join_auth_membership, - &room_create_event, + &real_room_create_event, )? { return Ok(false); } @@ -419,10 +445,10 @@ where | _ => { // If no power level event found the creator gets 100 everyone else gets 0 let is_creator = if room_version.use_room_create_sender { - room_create_event.sender() == sender + real_room_create_event.sender() == sender } else { #[allow(deprecated)] - from_json_str::(room_create_event.content().get()) + from_json_str::(real_room_create_event.content().get()) .is_ok_and(|create| create.creator.unwrap() == *sender) }; diff --git a/src/core/matrix/state_res/room_version.rs b/src/core/matrix/state_res/room_version.rs index fead8576..33da9368 100644 --- a/src/core/matrix/state_res/room_version.rs +++ b/src/core/matrix/state_res/room_version.rs @@ -83,7 +83,8 @@ pub struct RoomVersion { /// See: [MSC2175](https://github.com/matrix-org/matrix-spec-proposals/pull/2175) for more information. pub use_room_create_sender: bool, /// Whether the room creator is a superuser. - /// A superuser will always have infinite power level and gains special privileges. + /// A superuser will always have infinite power level and gains special + /// privileges. /// /// See: [MSC4289](https://github.com/matrix-org/matrix-spec-proposals/pull/4289) for more information. pub room_creator_is_superuser: bool, @@ -98,6 +99,12 @@ pub struct RoomVersion { } impl RoomVersion { + pub const HYDRA_V11: Self = Self { + room_creator_is_superuser: true, + estoppel_events: true, + create_id_as_room_id: true, + ..Self::V11 + }; pub const V1: Self = Self { disposition: RoomDisposition::Stable, event_format: EventFormatVersion::V1, @@ -116,6 +123,15 @@ impl RoomVersion { estoppel_events: false, create_id_as_room_id: false, }; + pub const V10: Self = Self { + knock_restricted_join_rule: true, + integer_power_levels: true, + ..Self::V9 + }; + pub const V11: Self = Self { + use_room_create_sender: true, + ..Self::V10 + }; pub const V2: Self = Self { state_res: StateResolutionVersion::V2, ..Self::V1 @@ -139,21 +155,6 @@ impl RoomVersion { pub const V7: Self = Self { allow_knocking: true, ..Self::V6 }; pub const V8: Self = Self { restricted_join_rules: true, ..Self::V7 }; pub const V9: Self = Self::V8; - pub const V10: Self = Self { - knock_restricted_join_rule: true, - integer_power_levels: true, - ..Self::V9 - }; - pub const V11: Self = Self { - use_room_create_sender: true, - ..Self::V10 - }; - pub const HYDRA_V11: Self = Self{ - room_creator_is_superuser: true, - estoppel_events: true, - create_id_as_room_id: true, - ..Self::V11 - }; pub fn new(version: &RoomVersionId) -> Result { Ok(match version { diff --git a/src/service/rooms/event_handler/handle_outlier_pdu.rs b/src/service/rooms/event_handler/handle_outlier_pdu.rs index 5339249d..76aa9054 100644 --- a/src/service/rooms/event_handler/handle_outlier_pdu.rs +++ b/src/service/rooms/event_handler/handle_outlier_pdu.rs @@ -130,12 +130,17 @@ pub(super) async fn handle_outlier_pdu<'a>( let key = (ty.to_owned(), sk.into()); ready(auth_events.get(&key)) }; + let event_fetch = |id: &EventId| { + let id = id.to_owned(); + Box::pin(self.services.timeline.get_pdu(&id)) + }; let auth_check = state_res::event_auth::auth_check( &to_room_version(&room_version_id), &incoming_pdu, None, // TODO: third party invite state_fetch, + event_fetch, ) .await .map_err(|e| err!(Request(Forbidden("Auth check failed: {e:?}"))))?;