1
0
Fork 0
mirror of https://gitlab.com/famedly/conduit.git synced 2025-10-15 19:42:07 +00:00

chore: bump ruma and axum

This commit is contained in:
Matthias Ahouansou 2025-06-22 00:22:55 +01:00
parent b44b5641f0
commit 5b68ce890d
No known key found for this signature in database
14 changed files with 492 additions and 369 deletions

View file

@ -408,6 +408,13 @@ impl state_res::Event for PduEvent {
fn redacts(&self) -> Option<&Self::Id> {
self.redacts.as_ref()
}
// We currently don't store rejected events (see steps 6-8 of `handle_incoming_pdu`), even
// though we should according to the spec:
// https://spec.matrix.org/v1.14/rooms/v11/#rejected-events
fn rejected(&self) -> bool {
false
}
}
// These impl's allow us to dedup state snapshots when resolving state

View file

@ -2,7 +2,7 @@ mod data;
pub use data::Data;
use ruma::{events::AnySyncTimelineEvent, push::PushConditionPowerLevelsCtx};
use crate::{services, Error, PduEvent, Result, MATRIX_VERSIONS};
use crate::{services, Error, PduEvent, Result, SUPPORTED_VERSIONS};
use bytes::BytesMut;
use ruma::{
api::{
@ -58,7 +58,7 @@ impl Service {
.try_into_http_request::<BytesMut>(
&destination,
SendAccessToken::IfRequired(""),
MATRIX_VERSIONS,
&SUPPORTED_VERSIONS,
)
.map_err(|e| {
warn!("Failed to find destination {}: {}", destination, e);

View file

@ -51,25 +51,26 @@ impl Service {
/// 0. Check the server is in the room
/// 1. Skip the PDU if we already know about it
/// 1.1. Remove unsigned field
/// 2. Check signatures, otherwise drop
/// 3. Check content hash, redact if doesn't match
/// 4. Fetch any missing auth events doing all checks listed here starting at 1. These are not
/// 2. Check event is valid, otherwise drop
/// 3. Check signatures, otherwise drop
/// 4. Check content hash, redact if doesn't match
/// 5. Fetch any missing auth events doing all checks listed here starting at 1. These are not
/// timeline events
/// 5. Reject "due to auth events" if can't get all the auth events or some of the auth events are
/// 6. Reject "due to auth events" if can't get all the auth events or some of the auth events are
/// also rejected "due to auth events"
/// 6. Reject "due to auth events" if the event doesn't pass auth based on the auth events
/// 7. Persist this event as an outlier
/// 8. If not timeline event: stop
/// 9. Fetch any missing prev events doing all checks listed here starting at 1. These are timeline
/// 7. Reject "due to auth events" if the event doesn't pass auth based on the auth events
/// 8. Persist this event as an outlier
/// 9. If not timeline event: stop
/// 10. Fetch any missing prev events doing all checks listed here starting at 1. These are timeline
/// events
/// 10. Fetch missing state and auth chain events by calling /state_ids at backwards extremities
/// 11. Fetch missing state and auth chain events by calling /state_ids at backwards extremities
/// doing all the checks in this list starting at 1. These are not timeline events
/// 11. Check the auth of the event passes based on the state of the event
/// 12. Ensure that the state is derived from the previous current state (i.e. we calculated by
/// 12. Check the auth of the event passes based on the state of the event
/// 13. Ensure that the state is derived from the previous current state (i.e. we calculated by
/// doing state res where one of the inputs was a previously trusted set of state, don't just
/// trust a set of state we got from a remote)
/// 13. Use state resolution to find new room state
/// 14. Check if the event passes auth based on the "current state" of the room, if not soft fail it
/// 14. Use state resolution to find new room state
/// 15. Check if the event passes auth based on the "current state" of the room, if not soft fail it
// We use some AsyncRecursiveType hacks here so we can call this async function recursively
#[tracing::instrument(skip(self, value, is_timeline_event, pub_key_map))]
pub(crate) async fn handle_incoming_pdu<'a>(
@ -135,7 +136,7 @@ impl Service {
.await?;
self.check_room_id(room_id, &incoming_pdu)?;
// 8. if not timeline event: stop
// 9. if not timeline event: stop
if !is_timeline_event {
return Ok(None);
}
@ -145,7 +146,7 @@ impl Service {
return Ok(None);
}
// 9. Fetch any missing prev events doing all checks listed here starting at 1. These are timeline events
// 10. Fetch any missing prev events doing all checks listed here starting at 1. These are timeline events
let (sorted_prev_events, mut eventid_info) = self
.fetch_unknown_prev_events(
origin,
@ -313,8 +314,9 @@ impl Service {
// 1.1. Remove unsigned field
value.remove("unsigned");
// 2. Check signatures, otherwise drop
// 3. check content hash, redact if doesn't match
// 2. Check event is valid, otherwise drop
// 3. Check signatures, otherwise drop
// 4. check content hash, redact if doesn't match
let create_event_content: RoomCreateEventContent =
serde_json::from_str(create_event.content.get()).map_err(|e| {
error!("Invalid create event: {}", e);
@ -326,6 +328,15 @@ impl Service {
.rules()
.expect("Supported room version has rules");
debug!("Checking format of join event PDU");
if let Err(e) = state_res::check_pdu_format(&value, &room_version_rules.event_format) {
warn!("Invalid PDU with event ID {event_id} received: {e}");
return Err(Error::BadRequest(
ErrorKind::InvalidParam,
"Received Invalid PDU",
));
}
// TODO: For RoomVersion6 we must check that Raw<..> is canonical do we anywhere?: https://matrix.org/docs/spec/rooms/v6#canonical-json
// We go through all the signatures we see on the value and fetch the corresponding signing
@ -421,8 +432,8 @@ impl Service {
self.check_room_id(room_id, &incoming_pdu)?;
if !auth_events_known {
// 4. fetch any missing auth events doing all checks listed here starting at 1. These are not timeline events
// 5. Reject "due to auth events" if can't get all the auth events or some of the auth events are also rejected "due to auth events"
// 5. fetch any missing auth events doing all checks listed here starting at 1. These are not timeline events
// 6. Reject "due to auth events" if can't get all the auth events or some of the auth events are also rejected "due to auth events"
// NOTE: Step 5 is not applied anymore because it failed too often
debug!(event_id = ?incoming_pdu.event_id, "Fetching auth events");
self.fetch_and_handle_outliers(
@ -440,7 +451,7 @@ impl Service {
.await;
}
// 6. Reject "due to auth events" if the event doesn't pass auth based on the auth events
// 7. Reject "due to auth events" if the event doesn't pass auth based on the auth events
debug!(
"Auth check for {} based on auth events",
incoming_pdu.event_id
@ -448,6 +459,7 @@ impl Service {
// Build map of auth events
let mut auth_events = HashMap::new();
let mut auth_events_by_event_id = HashMap::new();
for id in &incoming_pdu.auth_events {
let auth_event = match services().rooms.timeline.get_pdu(id)? {
Some(e) => e,
@ -457,46 +469,33 @@ impl Service {
}
};
self.check_room_id(room_id, &auth_event)?;
match auth_events.entry((
auth_event.kind.to_string().into(),
auth_event
.state_key
.clone()
.expect("all auth events have state keys"),
)) {
hash_map::Entry::Vacant(v) => {
v.insert(auth_event);
}
hash_map::Entry::Occupied(_) => {
return Err(Error::BadRequest(
ErrorKind::InvalidParam,
"Auth event's type and state_key combination exists multiple times.",
));
}
}
auth_events_by_event_id.insert(auth_event.event_id.clone(), auth_event.clone());
auth_events.insert(
(
StateEventType::from(auth_event.kind.to_string()),
auth_event
.state_key
.clone()
.expect("all auth events have state keys"),
),
auth_event,
);
}
// The original create event must be in the auth events
if !matches!(
auth_events
.get(&(StateEventType::RoomCreate, "".to_owned()))
.map(|a| a.as_ref()),
Some(_) | None
) {
return Err(Error::BadRequest(
ErrorKind::InvalidParam,
"Incoming event refers to wrong create event.",
));
}
if state_res::event_auth::auth_check(
// first time we are doing any sort of auth check, so we check state-independent
// auth rules in addition to the state-dependent ones.
if state_res::check_state_independent_auth_rules(
&room_version_rules.authorization,
&incoming_pdu,
|k, s| auth_events.get(&(k.to_string().into(), s.to_owned())),
|event_id| auth_events_by_event_id.get(event_id),
)
.is_err()
|| state_res::check_state_dependent_auth_rules(
&room_version_rules.authorization,
&incoming_pdu,
|k, s| auth_events.get(&(k.to_string().into(), s.to_owned())),
)
.is_err()
{
return Err(Error::BadRequest(
ErrorKind::InvalidParam,
@ -506,7 +505,7 @@ impl Service {
debug!("Validation successful.");
// 7. Persist the event as an outlier.
// 8. Persist the event as an outlier.
services()
.rooms
.outlier
@ -557,7 +556,7 @@ impl Service {
.rules()
.expect("Supported room version has rules");
// 10. Fetch missing state and auth chain events by calling /state_ids at backwards extremities
// 11. Fetch missing state and auth chain events by calling /state_ids at backwards extremities
// doing all the checks in this list starting at 1. These are not timeline events.
// TODO: if we know the prev_events of the incoming event we can avoid the request and build
@ -807,8 +806,8 @@ impl Service {
state_at_incoming_event.expect("we always set this to some above");
debug!("Starting auth check");
// 11. Check the auth of the event passes based on the state of the event
if state_res::event_auth::auth_check(
// 12. Check the auth of the event passes based on the state of the event
if state_res::check_state_dependent_auth_rules(
&room_version_rules.authorization,
&incoming_pdu,
|k, s| {
@ -840,7 +839,7 @@ impl Service {
&room_version_rules.authorization,
)?;
let soft_fail = state_res::event_auth::auth_check(
let soft_fail = state_res::check_state_dependent_auth_rules(
&room_version_rules.authorization,
&incoming_pdu,
|k, s| auth_events.get(&(k.clone(), s.to_owned())),
@ -891,7 +890,7 @@ impl Service {
}
};
// 13. Use state resolution to find new room state
// 14. Use state resolution to find new room state
// We start looking at current room state now, so lets lock the room
let mutex_state = Arc::clone(
@ -974,7 +973,7 @@ impl Service {
.await?;
}
// 14. Check if the event passes auth based on the "current state" of the room, if not soft fail it
// 15. Check if the event passes auth based on the "current state" of the room, if not soft fail it
debug!("Starting soft fail auth check");
if soft_fail {
@ -1399,7 +1398,7 @@ impl Service {
}
}
let sorted = state_res::lexicographical_topological_sort(&graph, |event_id| {
let sorted = state_res::reverse_topological_power_sort(&graph, |event_id| {
// This return value is the key used for sorting events,
// events are then sorted by power level, time,
// and lexically by event_id.

View file

@ -169,6 +169,18 @@ impl Service {
}
}
let rules = room_version_id
.rules()
.expect("Supported room version has rules");
debug!("Checking format of join event PDU");
if let Err(e) = state_res::check_pdu_format(&join_event, &rules.event_format) {
warn!(
"Invalid PDU with event ID {event_id} received from `/send_join` response: {e}"
);
return Err(Error::BadServerResponse("Received Invalid PDU"));
}
services().rooms.short.get_or_create_shortroomid(room_id)?;
info!("Parsing join event");
@ -240,28 +252,32 @@ impl Service {
}
info!("Running send_join auth check");
if let Err(e) = state_res::event_auth::auth_check(
&room_version_id
.rules()
.expect("Supported room version has rules")
.authorization,
if let Err(e) = state_res::check_state_independent_auth_rules(
&rules.authorization,
&parsed_join_pdu,
|k, s| {
services()
.rooms
.timeline
.get_pdu(
state.get(
&services()
.rooms
.short
.get_or_create_shortstatekey(&k.to_string().into(), s)
.ok()?,
)?,
)
.ok()?
},
) {
|event_id| services().rooms.timeline.get_pdu(event_id).ok().flatten(),
)
.and_then(|_| {
state_res::check_state_dependent_auth_rules(
&rules.authorization,
&parsed_join_pdu,
|k, s| {
services()
.rooms
.timeline
.get_pdu(
state.get(
&services()
.rooms
.short
.get_or_create_shortstatekey(&k.to_string().into(), s)
.ok()?,
)?,
)
.ok()?
},
)
}) {
warn!("Auth check failed: {e}");
return Err(Error::BadRequest(
ErrorKind::InvalidParam,
@ -674,6 +690,15 @@ async fn validate_and_add_event_id(
}
}
let rules = &room_version
.rules()
.expect("Supported room version has rules");
if let Err(e) = state_res::check_pdu_format(&value, &rules.event_format) {
warn!("Invalid PDU with event ID {event_id} received from `/send_join` response: {e}");
return Err(Error::BadServerResponse("Received Invalid PDU"));
}
let origin_server_ts = value.get("origin_server_ts").ok_or_else(|| {
error!("Invalid PDU, no origin_server_ts field");
Error::BadRequest(
@ -702,13 +727,7 @@ async fn validate_and_add_event_id(
.globals
.filter_keys_server_map(unfiltered_keys, origin_server_ts, room_version);
if let Err(e) = ruma::signatures::verify_event(
&keys,
&value,
&room_version
.rules()
.expect("Supported room version has rules"),
) {
if let Err(e) = ruma::signatures::verify_event(&keys, &value, rules) {
warn!("Event {} failed verification {:?} {}", event_id, pdu, e);
back_off(event_id).await;
return Err(Error::BadServerResponse("Event failed verification."));

View file

@ -713,6 +713,10 @@ impl Service {
&content,
&room_version_rules.authorization,
)?;
let mut auth_events_by_event_id = HashMap::new();
for event in auth_events.values() {
auth_events_by_event_id.insert(event.event_id.clone(), event.clone());
}
// Our depth is the maximum depth of prev_events + 1
let depth = prev_events
@ -769,10 +773,18 @@ impl Service {
signatures: None,
};
if state_res::auth_check(&room_version_rules.authorization, &pdu, |k, s| {
auth_events.get(&(k.clone(), s.to_owned()))
})
if state_res::check_state_independent_auth_rules(
&room_version_rules.authorization,
&pdu,
|event_id| auth_events_by_event_id.get(event_id),
)
.is_err()
|| state_res::check_state_dependent_auth_rules(
&room_version_rules.authorization,
&pdu,
|k, s| auth_events.get(&(k.clone(), s.to_owned())),
)
.is_err()
{
return Err(Error::BadRequest(
ErrorKind::forbidden(),
@ -814,6 +826,14 @@ impl Service {
}
}
if let Err(e) = state_res::check_pdu_format(&pdu_json, &room_version_rules.event_format) {
warn!("locally constructed event is not a valid PDU: {e}");
return Err(Error::BadRequest(
ErrorKind::InvalidParam,
"Event is invalid",
));
}
// Generate event id
pdu.event_id = EventId::parse_arc(format!(
"${}",