mirror of
https://forgejo.ellis.link/continuwuation/continuwuity.git
synced 2026-05-26 20:49:55 +00:00
fix: Verify policy server signatures on all events, not just timeline ones
style: Clarifications style: Clippy
This commit is contained in:
@@ -6,18 +6,18 @@
|
|||||||
use std::{collections::BTreeMap, time::Duration};
|
use std::{collections::BTreeMap, time::Duration};
|
||||||
|
|
||||||
use conduwuit::{
|
use conduwuit::{
|
||||||
Err, Error, Event, PduEvent, Result, debug, debug_error, debug_info, debug_warn, error,
|
debug, debug_error, debug_info, debug_warn, error, implement, info, state_res::EventTypeExt, trace, utils::to_canonical_object,
|
||||||
implement, info, state_res::EventTypeExt, trace, utils::to_canonical_object, warn,
|
warn, Err, Error, Event, PduEvent, Result,
|
||||||
};
|
};
|
||||||
use http::StatusCode;
|
use http::StatusCode;
|
||||||
use ruma::{
|
use ruma::{
|
||||||
CanonicalJsonObject, CanonicalJsonValue, KeyId, RoomId, ServerName, SigningKeyAlgorithm,
|
api::error::ErrorKind, canonical_json::redact, events::{room::policy::RoomPolicyEventContent, StateEventType}, room_version_rules::{RedactionRules, RoomVersionRules}, serde::{base64::Standard, Base64}, signatures::{to_canonical_json_string_for_signing, verify_canonical_json_bytes},
|
||||||
api::error::ErrorKind,
|
CanonicalJsonObject,
|
||||||
canonical_json::redact,
|
CanonicalJsonValue,
|
||||||
events::{StateEventType, room::policy::RoomPolicyEventContent},
|
KeyId,
|
||||||
room_version_rules::{RedactionRules, RoomVersionRules},
|
RoomId,
|
||||||
serde::{Base64, base64::Standard},
|
ServerName,
|
||||||
signatures::{to_canonical_json_string_for_signing, verify_canonical_json_bytes},
|
SigningKeyAlgorithm,
|
||||||
};
|
};
|
||||||
use ruminuwuity::policy::policy_sign::unstable::Request as PolicySignRequest;
|
use ruminuwuity::policy::policy_sign::unstable::Request as PolicySignRequest;
|
||||||
use serde_json::value::RawValue;
|
use serde_json::value::RawValue;
|
||||||
@@ -72,15 +72,13 @@ pub(super) fn verify_policy_signature(
|
|||||||
|
|
||||||
/// Asks a remote policy server if the event is allowed.
|
/// Asks a remote policy server if the event is allowed.
|
||||||
///
|
///
|
||||||
/// If the event is the `org.matrix.msc4284.policy` configuration state event,
|
/// If the event is the `m.room.policy` configuration state event,
|
||||||
/// this check is skipped. Similarly, if there is no policy server configured in
|
/// this check is skipped. Similarly, if there is no policy server configured in
|
||||||
/// the PDU's room, or the configured server is not present in the room, the
|
/// the PDU's room, or the configured server is not present in the room, the
|
||||||
/// check is also skipped.
|
/// check is also skipped.
|
||||||
///
|
///
|
||||||
/// If the policy server marks the event as spam, Ok(false) is returned,
|
/// If the policy server marks the event as spam, the relevant error is returned. Otherwise,
|
||||||
/// otherwise Ok(true) allows the event. If the policy server cannot be
|
/// the incoming PDU JSON is mutated to include the new policy server signature.
|
||||||
/// contacted for whatever reason, Err(e) is returned, which generally is a
|
|
||||||
/// fail-open operation.
|
|
||||||
#[implement(super::Service)]
|
#[implement(super::Service)]
|
||||||
#[tracing::instrument(skip(self, pdu, pdu_json, room_version_rules), level = "info")]
|
#[tracing::instrument(skip(self, pdu, pdu_json, room_version_rules), level = "info")]
|
||||||
pub async fn policy_server_allows_event(
|
pub async fn policy_server_allows_event(
|
||||||
@@ -152,6 +150,7 @@ pub async fn policy_server_allows_event(
|
|||||||
);
|
);
|
||||||
return Ok(());
|
return Ok(());
|
||||||
}
|
}
|
||||||
|
// N.B. In a future room version, this will be a soft failure specifically.
|
||||||
debug_info!(
|
debug_info!(
|
||||||
via = %ps.via,
|
via = %ps.via,
|
||||||
"Event is incoming but does not have a valid policy server signature; asking policy \
|
"Event is incoming but does not have a valid policy server signature; asking policy \
|
||||||
@@ -183,6 +182,10 @@ pub async fn policy_server_allows_event(
|
|||||||
self.fetch_policy_server_signature(pdu, pdu_json, &ps.via, outgoing, room_id, ps_key, 0)
|
self.fetch_policy_server_signature(pdu, pdu_json, &ps.via, outgoing, room_id, ps_key, 0)
|
||||||
.await
|
.await
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Handles an error returned by the policy server. If the error is one that should be returned to
|
||||||
|
/// the user, it is propagated, otherwise the request may be retried (for example, when
|
||||||
|
/// rate-limited).
|
||||||
#[allow(clippy::too_many_arguments)]
|
#[allow(clippy::too_many_arguments)]
|
||||||
#[implement(super::Service)]
|
#[implement(super::Service)]
|
||||||
async fn handle_policy_server_error(
|
async fn handle_policy_server_error(
|
||||||
@@ -290,7 +293,8 @@ async fn handle_policy_server_error(
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Asks a remote policy server for a signature on this event.
|
/// Asks a remote policy server for a signature on this event.
|
||||||
/// If the policy server signs this event, the original data is mutated.
|
/// If the policy server signs this event, the original data is mutated. Otherwise, the error is
|
||||||
|
/// handled and potentially returned.
|
||||||
#[allow(clippy::too_many_arguments)]
|
#[allow(clippy::too_many_arguments)]
|
||||||
#[implement(super::Service)]
|
#[implement(super::Service)]
|
||||||
#[tracing::instrument(skip_all, fields(event_id=%pdu.event_id(), via=%via), level = "info")]
|
#[tracing::instrument(skip_all, fields(event_id=%pdu.event_id(), via=%via), level = "info")]
|
||||||
@@ -358,7 +362,7 @@ pub async fn fetch_policy_server_signature(
|
|||||||
response.signatures
|
response.signatures
|
||||||
);
|
);
|
||||||
return Err!(BadServerResponse(
|
return Err!(BadServerResponse(
|
||||||
"Policy server did not include expected server name in signatures"
|
"Policy server did not sign the event"
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
// Unwraps are safe here because we checked both in the above if statement
|
// Unwraps are safe here because we checked both in the above if statement
|
||||||
|
|||||||
@@ -1,18 +1,17 @@
|
|||||||
use std::{borrow::Borrow, collections::BTreeMap, sync::Arc, time::Instant};
|
use std::{borrow::Borrow, collections::BTreeMap, sync::Arc, time::Instant};
|
||||||
|
|
||||||
use conduwuit::{
|
use conduwuit::{
|
||||||
Err, Result, debug, debug_info, debug_warn, err, implement, is_equal_to,
|
debug, debug_info, debug_warn, err, implement, is_equal_to, matrix::{state_res, Event, EventTypeExt, PduEvent, StateKey}, trace,
|
||||||
matrix::{Event, EventTypeExt, PduEvent, StateKey, state_res},
|
|
||||||
trace,
|
|
||||||
utils::{
|
utils::{
|
||||||
IterStream,
|
IterStream,
|
||||||
stream::{BroadbandExt, ReadyExt},
|
stream::{BroadbandExt, ReadyExt},
|
||||||
},
|
},
|
||||||
warn,
|
Err,
|
||||||
|
Result,
|
||||||
};
|
};
|
||||||
use futures::{FutureExt, StreamExt, future::ready};
|
use futures::{future::ready, FutureExt, StreamExt};
|
||||||
use ruma::{
|
use ruma::{
|
||||||
CanonicalJsonValue, RoomId, ServerName, api::error::ErrorKind, events::StateEventType,
|
api::error::ErrorKind, events::StateEventType, CanonicalJsonValue, RoomId, ServerName,
|
||||||
};
|
};
|
||||||
use tokio::join;
|
use tokio::join;
|
||||||
|
|
||||||
@@ -249,36 +248,34 @@ where
|
|||||||
if !soft_fail {
|
if !soft_fail {
|
||||||
// Don't call the below checks on events that have already soft-failed, there's
|
// Don't call the below checks on events that have already soft-failed, there's
|
||||||
// no reason to re-calculate that.
|
// no reason to re-calculate that.
|
||||||
// 14-pre. If the event is not a state event, ask the policy server about it
|
// 14-pre. ask the policy server to sign the event, if possible
|
||||||
if incoming_pdu.state_key.is_none() {
|
debug!(event_id = %incoming_pdu.event_id, "Checking policy server for event");
|
||||||
debug!(event_id = %incoming_pdu.event_id, "Checking policy server for event");
|
if let Err(e) = self
|
||||||
if let Err(e) = self
|
.policy_server_allows_event(
|
||||||
.policy_server_allows_event(
|
&incoming_pdu,
|
||||||
&incoming_pdu,
|
&mut incoming_pdu.to_canonical_object(),
|
||||||
&mut incoming_pdu.to_canonical_object(),
|
room_id,
|
||||||
room_id,
|
&room_version_rules,
|
||||||
&room_version_rules,
|
true,
|
||||||
true,
|
)
|
||||||
)
|
.await
|
||||||
.await
|
{
|
||||||
{
|
if matches!(e.kind(), ErrorKind::Forbidden) {
|
||||||
if matches!(e.kind(), ErrorKind::Forbidden) {
|
info!(
|
||||||
warn!(
|
|
||||||
event_id = %incoming_pdu.event_id,
|
|
||||||
error = %e,
|
|
||||||
"Event has been marked as spam by policy server: {}",
|
|
||||||
e.message(),
|
|
||||||
);
|
|
||||||
soft_fail = true;
|
|
||||||
} else {
|
|
||||||
return Err(e);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
debug!(
|
|
||||||
event_id = %incoming_pdu.event_id,
|
event_id = %incoming_pdu.event_id,
|
||||||
"Event has passed policy server check."
|
error = %e,
|
||||||
|
"Event has been marked as spam by policy server: {}",
|
||||||
|
e.message(),
|
||||||
);
|
);
|
||||||
|
soft_fail = true;
|
||||||
|
} else {
|
||||||
|
return Err(e);
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
debug!(
|
||||||
|
event_id = %incoming_pdu.event_id,
|
||||||
|
"Event has passed policy server check."
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Additionally, if this is a redaction for a soft-failed event, we soft-fail it
|
// Additionally, if this is a redaction for a soft-failed event, we soft-fail it
|
||||||
@@ -298,9 +295,7 @@ where
|
|||||||
.is_event_soft_failed(&redact_id)
|
.is_event_soft_failed(&redact_id)
|
||||||
.await
|
.await
|
||||||
{
|
{
|
||||||
// TODO: This should avoid pushing the event to the timeline instead of using
|
info!(
|
||||||
// soft-fails as a hack
|
|
||||||
warn!(
|
|
||||||
redact_id = %redact_id,
|
redact_id = %redact_id,
|
||||||
"Redaction is for a soft-failed event"
|
"Redaction is for a soft-failed event"
|
||||||
);
|
);
|
||||||
@@ -367,9 +362,11 @@ where
|
|||||||
self.services
|
self.services
|
||||||
.pdu_metadata
|
.pdu_metadata
|
||||||
.mark_event_soft_failed(incoming_pdu.event_id());
|
.mark_event_soft_failed(incoming_pdu.event_id());
|
||||||
debug_warn!(
|
|
||||||
|
info!(
|
||||||
elapsed = ?timer.elapsed(),
|
elapsed = ?timer.elapsed(),
|
||||||
"Event has been soft-failed",
|
event_id = %incoming_pdu.event_id,
|
||||||
|
"Event was soft failed"
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
debug_info!(
|
debug_info!(
|
||||||
|
|||||||
Reference in New Issue
Block a user