Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/pipeline.yml
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,7 @@ jobs:
if [[ -z "$BRANCH_NAME" || $BRANCH_NAME =~ ^refs/pull/.* ]]; then
continue
fi
(wget -O - "https://github.com/element-hq/synapse/archive/$BRANCH_NAME.tar.gz" \
(wget -O - "https://github.com/famedly/synapse/archive/$BRANCH_NAME.tar.gz" \
| tar -xz --strip-components=1 -C /src/) \
&& echo "Successfully downloaded and extracted $BRANCH_NAME.tar.gz" \
&& break
Expand Down
42 changes: 36 additions & 6 deletions lib/Protocol/Matrix.pm
Original file line number Diff line number Diff line change
Expand Up @@ -266,18 +266,46 @@ my %ALLOWED_CONTENT_BY_TYPE = (

sub redact_event
{
my ( $event ) = @_;
my ( $event, $room_version ) = @_;
$room_version //= 1;

defined( my $type = $event->{type} ) or
croak "Event requires a 'type'";

delete $event->{redacts};
my $old_content = delete $event->{content};
my $old_unsigned = delete $event->{unsigned};

$ALLOWED_KEYS{$_} or delete $event->{$_} for keys %$event;

my $new_content = $event->{content} = {};

# Room version 11+ uses updated redaction rules:
# - m.room.create: entire content property is preserved
# - m.room.power_levels: 'invite' is also preserved
# - m.room.member: 'third_party_invite.signed' is also preserved
# - m.room.redaction: 'redacts' is also preserved
if( defined $room_version and $room_version =~ /\A[0-9]+\z/ and $room_version >= 11 ) {
if( $type eq 'm.room.create' ) {
%$new_content = %{ $old_content // {} };
$event->{unsigned}{age_ts} = $old_unsigned->{age_ts} if exists $old_unsigned->{age_ts};
return;
}
if( $type eq 'm.room.power_levels' ) {
exists $old_content->{invite} and $new_content->{invite} = $old_content->{invite};
}
if( $type eq 'm.room.member' ) {
my $tpi = $old_content->{third_party_invite};
if( ref $tpi eq 'HASH' && exists $tpi->{signed} ) {
$new_content->{third_party_invite} = { signed => $tpi->{signed} };
}
}
if( $type eq 'm.room.redaction' ) {
# In v11, 'redacts' moved into content; preserve content.redacts
exists $old_content->{redacts} and $new_content->{redacts} = $old_content->{redacts};
}
}

if( my $allowed_content_keys = $ALLOWED_CONTENT_BY_TYPE{$type} ) {
exists $old_content->{$_} and $new_content->{$_} = $old_content->{$_} for
@$allowed_content_keys;
Expand All @@ -288,8 +316,8 @@ sub redact_event

sub redacted_event
{
my ( $event ) = @_;
redact_event( $event = { %$event } );
my ( $event, $room_version ) = @_;
redact_event( $event = { %$event }, $room_version );
return $event;
}

Expand All @@ -309,6 +337,7 @@ sub sign_event_json

my $origin = $args{origin} or croak "Require an 'origin'";
my $key_id = $args{key_id} or croak "Require a 'key_id'";
my $room_version = delete $args{room_version};

# 'hashes' records the original unredacted version
{
Expand All @@ -319,7 +348,7 @@ sub sign_event_json
}

# Signature is of redacted version
sign_json( my $signed = redacted_event( $event ), %args );
sign_json( my $signed = redacted_event( $event, $room_version ), %args );

$event->{signatures} = $signed->{signatures};
}
Expand Down Expand Up @@ -349,9 +378,10 @@ sub signed_event_json

sub verify_event_json_signature
{
my ( $event, @args ) = @_;
my ( $event, %args ) = @_;

verify_json_signature( redacted_event( $event ), @args );
my $room_version = delete $args{room_version};
verify_json_signature( redacted_event( $event, $room_version ), %args );
}

=head1 AUTHOR
Expand Down
29 changes: 26 additions & 3 deletions lib/SyTest/Federation/AuthChecks.pm
Original file line number Diff line number Diff line change
Expand Up @@ -48,9 +48,12 @@ sub auth_check_event
$accepted_events, $event->{auth_events}, "m.room.create"
);

my $room_creator = _creator_for_create_event( $create_event );
return 0 unless defined $room_creator;

{
users => {
$create_event->{content}{creator} => 100,
$room_creator => 100,
},
users_default => 0,

Expand Down Expand Up @@ -86,6 +89,24 @@ sub auth_check_event
return 1;
}

sub _creator_for_create_event
{
my ( $create_event ) = @_;

$create_event or
return undef;

my $room_version = $create_event->{content}{room_version};

# For room version 11+, 'creator' is absent from content, we use sender.
if( defined $room_version and $room_version =~ /\A[0-9]+\z/ and $room_version >= 11 ) {
return $create_event->{sender};
}

# For older room versions, 'creator' must be present explicitly.
return $create_event->{content}{creator};
}

sub auth_check_event_m_room_create
{
my $self = shift;
Expand All @@ -96,7 +117,8 @@ sub auth_check_event_m_room_create
return 0;

# Any m.room.create event is acceptable, provided that the creator matches
return $event->{sender} eq $event->{content}{creator};
my $creator = _creator_for_create_event( $event );
return defined( $creator ) and $event->{sender} eq $creator;
}

sub auth_check_event_m_room_member
Expand All @@ -117,7 +139,8 @@ sub auth_check_event_m_room_member
$accepted_events, $event->{auth_events}, "m.room.create"
);

if( $create_event and $event->{state_key} eq $create_event->{content}{creator} ) {
my $creator = _creator_for_create_event( $create_event );
if( $creator and $event->{state_key} eq $creator ) {
return 1;
}

Expand Down
13 changes: 7 additions & 6 deletions lib/SyTest/Federation/Datastore.pm
Original file line number Diff line number Diff line change
Expand Up @@ -77,12 +77,13 @@ the C<signatures> key.
sub sign_event
{
my $self = shift;
my ( $event ) = @_;
my ( $event, %args ) = @_;

sign_event_json( $event,
secret_key => $self->secret_key,
origin => $self->server_name,
key_id => $self->key_id,
secret_key => $self->secret_key,
origin => $self->server_name,
key_id => $self->key_id,
room_version => $args{room_version},
);
}

Expand Down Expand Up @@ -204,12 +205,12 @@ sub create_event
$event_id = $self->next_event_id( $event_id_suffix );
$event->{event_id} = $event_id;
}
$self->sign_event( $event );
$self->sign_event( $event, room_version => $room_version );
} else {
die "event with explicit event_id in room v$room_version"
if defined $event_id;

$self->sign_event( $event );
$self->sign_event( $event, room_version => $room_version );
$event_id = id_for_event( $event, $room_version );
}

Expand Down
7 changes: 4 additions & 3 deletions lib/SyTest/Federation/Protocol.pm
Original file line number Diff line number Diff line change
Expand Up @@ -44,9 +44,10 @@ Calculates the reference hash of an event.

sub hash_event
{
my ( $event ) = @_;
my ( $event, $room_version ) = @_;
$room_version //= 1;
croak "Require an event" unless ref $event eq 'HASH';
my $redacted = redacted_event( $event );
my $redacted = redacted_event( $event, $room_version );
delete $redacted->{signatures};
delete $redacted->{age_ts};
delete $redacted->{unsigned};
Expand Down Expand Up @@ -76,7 +77,7 @@ sub id_for_event
return $event_id;
}

my $event_hash = hash_event( $event );
my $event_hash = hash_event( $event, $room_version );

# room v3 uses the unpadded-base64-encoded hash
if( $room_version eq '3' ) {
Expand Down
11 changes: 7 additions & 4 deletions lib/SyTest/Federation/Room.pm
Original file line number Diff line number Diff line change
Expand Up @@ -180,13 +180,16 @@ sub create_initial_events
$self->room_version eq "1" ? undef : $self->room_version
);

# For room version 11+, 'creator' is absent from content, we use sender.
my $create_content = {
defined( $room_version ) ? ( room_version => $room_version ) : (),
};
$create_content->{creator} = $creator if !defined( $room_version ) || $room_version < 11;

$self->create_and_insert_event(
type => "m.room.create",

content => {
creator => $creator,
defined( $room_version ) ? ( room_version => $room_version ) : (),
},
content => $create_content,
sender => $creator,
state_key => "",
);
Expand Down
9 changes: 6 additions & 3 deletions tests/30rooms/01state.pl
Original file line number Diff line number Diff line change
Expand Up @@ -32,9 +32,12 @@
$event->{sender} eq $user->user_id or
die "Expected user_id to be ${\$user->user_id}";

assert_json_keys( my $content = $event->{content}, qw( creator ));
$content->{creator} eq $user->user_id or
die "Expected creator to be ${\$user->user_id}";
my $content = $event->{content};
# For room version 11+, 'creator' is absent from content, we use sender.
if( exists $content->{creator} ) {
$content->{creator} eq $user->user_id or
die "Expected creator to be ${\$user->user_id}";
}

return 1;
});
Expand Down