mirror of
https://github.com/processone/ejabberd
synced 2025-10-03 17:59:31 +02:00
Support older Matrix rooms versions starting from version 4
This commit is contained in:
parent
9d1d57cd82
commit
038491d2ec
4 changed files with 210 additions and 33 deletions
|
@ -21,6 +21,13 @@
|
|||
-record(room_version,
|
||||
{id :: binary(),
|
||||
%% use the same field names as in Synapse
|
||||
enforce_key_validity :: boolean(),
|
||||
special_case_aliases_auth :: boolean(),
|
||||
strict_canonicaljson :: boolean(),
|
||||
limit_notifications_power_levels :: boolean(),
|
||||
knock_join_rule :: boolean(),
|
||||
restricted_join_rule :: boolean(),
|
||||
restricted_join_rule_fix :: boolean(),
|
||||
knock_restricted_join_rule :: boolean(),
|
||||
enforce_int_power_levels :: boolean(),
|
||||
implicit_room_creator :: boolean(),
|
||||
|
|
|
@ -41,6 +41,7 @@
|
|||
handle_info/2, terminate/2, code_change/3,
|
||||
depends/2, mod_opt_type/1, mod_options/1, mod_doc/0]).
|
||||
-export([parse_auth/1, encode_canonical_json/1,
|
||||
is_canonical_json/1,
|
||||
get_id_domain_exn/1,
|
||||
base64_decode/1, base64_encode/1,
|
||||
prune_event/2, get_event_id/2, content_hash/1,
|
||||
|
@ -155,8 +156,8 @@ process([<<"federation">>, <<"v2">>, <<"invite">>, RoomID, EventID],
|
|||
%% TODO: check type and userid
|
||||
Host = ejabberd_config:get_myname(),
|
||||
PrunedEvent = prune_event(Event, RoomVersion),
|
||||
?DEBUG("invite ~p~n", [{RoomID, EventID, Event, RoomVer, catch mod_matrix_gw_s2s:check_signature(Host, PrunedEvent), get_pruned_event_id(PrunedEvent)}]),
|
||||
case mod_matrix_gw_s2s:check_signature(Host, PrunedEvent) of
|
||||
%?DEBUG("invite ~p~n", [{RoomID, EventID, Event, RoomVer, catch mod_matrix_gw_s2s:check_signature(Host, PrunedEvent, RoomVersion), get_pruned_event_id(PrunedEvent)}]),
|
||||
case mod_matrix_gw_s2s:check_signature(Host, PrunedEvent, RoomVersion) of
|
||||
true ->
|
||||
case get_pruned_event_id(PrunedEvent) of
|
||||
EventID ->
|
||||
|
@ -629,9 +630,15 @@ prune_event(#{<<"type">> := Type, <<"content">> := Content} = Event,
|
|||
Content2 =
|
||||
case Type of
|
||||
<<"m.room.member">> ->
|
||||
C3 = maps:with([<<"membership">>,
|
||||
C3 =
|
||||
case RoomVersion#room_version.restricted_join_rule_fix of
|
||||
true ->
|
||||
maps:with([<<"membership">>,
|
||||
<<"join_authorised_via_users_server">>],
|
||||
Content),
|
||||
Content);
|
||||
false ->
|
||||
maps:with([<<"membership">>], Content)
|
||||
end,
|
||||
case RoomVersion#room_version.updated_redaction_rules of
|
||||
false ->
|
||||
C3;
|
||||
|
@ -653,7 +660,12 @@ prune_event(#{<<"type">> := Type, <<"content">> := Content} = Event,
|
|||
Content
|
||||
end;
|
||||
<<"m.room.join_rules">> ->
|
||||
maps:with([<<"join_rule">>, <<"allow">>], Content);
|
||||
case RoomVersion#room_version.restricted_join_rule of
|
||||
false ->
|
||||
maps:with([<<"join_rule">>], Content);
|
||||
true ->
|
||||
maps:with([<<"join_rule">>, <<"allow">>], Content)
|
||||
end;
|
||||
<<"m.room.power_levels">> ->
|
||||
case RoomVersion#room_version.updated_redaction_rules of
|
||||
false ->
|
||||
|
@ -677,6 +689,8 @@ prune_event(#{<<"type">> := Type, <<"content">> := Content} = Event,
|
|||
true ->
|
||||
maps:with([<<"redacts">>], Content)
|
||||
end;
|
||||
<<"m.room.aliases">> when RoomVersion#room_version.special_case_aliases_auth ->
|
||||
maps:with([<<"aliases">>], Content);
|
||||
_ -> #{}
|
||||
end,
|
||||
Event2#{<<"content">> := Content2}.
|
||||
|
@ -716,6 +730,27 @@ sort_json(List) when is_list(List) ->
|
|||
sort_json(JSON) ->
|
||||
JSON.
|
||||
|
||||
is_canonical_json(N) when is_integer(N),
|
||||
-16#1FFFFFFFFFFFFF =< N,
|
||||
N =< 16#1FFFFFFFFFFFFF ->
|
||||
true;
|
||||
is_canonical_json(B) when is_binary(B) ->
|
||||
true;
|
||||
is_canonical_json(B) when is_boolean(B) ->
|
||||
true;
|
||||
is_canonical_json(Map) when is_map(Map) ->
|
||||
maps:fold(
|
||||
fun(_K, V, true) ->
|
||||
is_canonical_json(V);
|
||||
(_K, _V, false) ->
|
||||
false
|
||||
end, true, Map);
|
||||
is_canonical_json(List) when is_list(List) ->
|
||||
lists:all(fun is_canonical_json/1, List);
|
||||
is_canonical_json(_) ->
|
||||
false.
|
||||
|
||||
|
||||
base64_decode(B) ->
|
||||
Fixed =
|
||||
case size(B) rem 4 of
|
||||
|
|
|
@ -103,6 +103,7 @@
|
|||
-define(ROOM_MESSAGE, <<"m.room.message">>).
|
||||
-define(ROOM_HISTORY_VISIBILITY, <<"m.room.history_visibility">>).
|
||||
-define(ROOM_TOPIC, <<"m.room.topic">>).
|
||||
-define(ROOM_ALIASES, <<"m.room.aliases">>).
|
||||
|
||||
-define(MAX_DEPTH, 16#7FFFFFFFFFFFFFFF).
|
||||
-define(MAX_TXN_RETRIES, 5).
|
||||
|
@ -650,9 +651,7 @@ handle_event(cast, {join_direct, MatrixServer, RoomID, Sender, UserID}, State, D
|
|||
Host, get, MatrixServer,
|
||||
[<<"_matrix">>, <<"federation">>, <<"v1">>, <<"make_join">>,
|
||||
RoomID, UserID],
|
||||
[{<<"ver">>, <<"9">>},
|
||||
{<<"ver">>, <<"10">>},
|
||||
{<<"ver">>, <<"11">>}],
|
||||
[{<<"ver">>, V} || V <- supported_versions()],
|
||||
none,
|
||||
[{timeout, 5000}],
|
||||
[{sync, true},
|
||||
|
@ -770,9 +769,7 @@ handle_event(cast, {join, UserJID, Packet}, _State, Data) ->
|
|||
Host, get, MatrixServer,
|
||||
[<<"_matrix">>, <<"federation">>, <<"v1">>, <<"make_join">>,
|
||||
RoomID, UserID],
|
||||
[{<<"ver">>, <<"9">>},
|
||||
{<<"ver">>, <<"10">>},
|
||||
{<<"ver">>, <<"11">>}],
|
||||
[{<<"ver">>, V} || V <- supported_versions()],
|
||||
none,
|
||||
[{timeout, 5000}],
|
||||
[{sync, true},
|
||||
|
@ -1280,7 +1277,7 @@ check_event_auth(Event, StateMap, Data) ->
|
|||
<<"ban">> ->
|
||||
check_event_auth_ban(
|
||||
Event, StateMap, Data);
|
||||
<<"knock">> ->
|
||||
<<"knock">> when (Data#data.room_version)#room_version.knock_join_rule ->
|
||||
check_event_auth_knock(
|
||||
Event, StateMap, Data);
|
||||
_ ->
|
||||
|
@ -1289,6 +1286,18 @@ check_event_auth(Event, StateMap, Data) ->
|
|||
_ ->
|
||||
false
|
||||
end;
|
||||
?ROOM_ALIASES when (Data#data.room_version)#room_version.special_case_aliases_auth ->
|
||||
case Event#event.state_key of
|
||||
undefined ->
|
||||
false;
|
||||
StateKey ->
|
||||
case mod_matrix_gw:get_id_domain_exn(Event#event.sender) of
|
||||
StateKey ->
|
||||
true;
|
||||
_ ->
|
||||
false
|
||||
end
|
||||
end;
|
||||
_ ->
|
||||
Sender = Event#event.sender,
|
||||
case maps:find({?ROOM_MEMBER, Sender}, StateMap) of
|
||||
|
@ -1372,8 +1381,11 @@ check_event_auth_join(Event, StateMap, Data) ->
|
|||
case {JoinRule, SenderMembership} of
|
||||
{<<"public">>, _} -> true;
|
||||
{<<"invite">>, <<"invite">>} -> true;
|
||||
{<<"knock">>, <<"invite">>} -> true;
|
||||
{<<"restricted">>, <<"invite">>} ->
|
||||
{<<"knock">>, <<"invite">>}
|
||||
when (Data#data.room_version)#room_version.knock_join_rule ->
|
||||
true;
|
||||
{<<"restricted">>, <<"invite">>}
|
||||
when (Data#data.room_version)#room_version.restricted_join_rule ->
|
||||
%% TODO
|
||||
true;
|
||||
{<<"knock_restricted">>, <<"invite">>}
|
||||
|
@ -1442,7 +1454,7 @@ check_event_auth_leave(Event, StateMap, Data) ->
|
|||
case SenderMembership of
|
||||
<<"invite">> -> true;
|
||||
<<"join">> -> true;
|
||||
<<"knock">> -> true;
|
||||
<<"knock">> when (Data#data.room_version)#room_version.knock_join_rule -> true;
|
||||
_ -> false
|
||||
end;
|
||||
_ ->
|
||||
|
@ -1609,6 +1621,13 @@ check_event_auth_power_levels(Event, StateMap, Data) ->
|
|||
try
|
||||
case Event#event.json of
|
||||
#{<<"content">> := NewPL = #{<<"users">> := Users}} when is_map(Users) ->
|
||||
CheckKeys =
|
||||
case (Data#data.room_version)#room_version.limit_notifications_power_levels of
|
||||
false ->
|
||||
[<<"events">>, <<"users">>];
|
||||
true ->
|
||||
[<<"events">>, <<"users">>, <<"notifications">>]
|
||||
end,
|
||||
case (Data#data.room_version)#room_version.enforce_int_power_levels of
|
||||
true ->
|
||||
lists:foreach(
|
||||
|
@ -1632,7 +1651,7 @@ check_event_auth_power_levels(Event, StateMap, Data) ->
|
|||
end
|
||||
end, [], NewMap)
|
||||
end,
|
||||
[<<"events">>, <<"users">>, <<"notifications">>]);
|
||||
CheckKeys);
|
||||
false ->
|
||||
ok
|
||||
end,
|
||||
|
@ -1677,7 +1696,7 @@ check_event_auth_power_levels(Event, StateMap, Data) ->
|
|||
end
|
||||
end, [], maps:merge(OldMap, NewMap))
|
||||
end,
|
||||
[<<"events">>, <<"users">>, <<"notifications">>]),
|
||||
CheckKeys),
|
||||
true;
|
||||
_ ->
|
||||
true
|
||||
|
@ -1772,7 +1791,7 @@ fill_event(JSON, Data) ->
|
|||
_ -> []
|
||||
end
|
||||
end,
|
||||
compute_event_auth_keys(JSON))),
|
||||
compute_event_auth_keys(JSON, Data#data.room_version))),
|
||||
{JSON#{<<"auth_events">> => AuthEvents,
|
||||
<<"depth">> => Depth2,
|
||||
<<"origin">> => MatrixServer,
|
||||
|
@ -1923,7 +1942,8 @@ get_latest_events(Pid) ->
|
|||
check_event_signature(Host, Event) ->
|
||||
PrunedEvent = mod_matrix_gw:prune_event(Event#event.json,
|
||||
Event#event.room_version),
|
||||
mod_matrix_gw_s2s:check_signature(Host, PrunedEvent).
|
||||
mod_matrix_gw_s2s:check_signature(Host, PrunedEvent,
|
||||
Event#event.room_version).
|
||||
|
||||
find_event(Pid, EventID) ->
|
||||
gen_statem:call(Pid, {find_event, EventID}).
|
||||
|
@ -2526,8 +2546,85 @@ find_power_level_event(EventID, Data) ->
|
|||
end, undefined, Event#event.auth_events).
|
||||
|
||||
|
||||
binary_to_room_version(<<"4">>) ->
|
||||
#room_version{id = <<"4">>,
|
||||
enforce_key_validity = false,
|
||||
special_case_aliases_auth = true,
|
||||
strict_canonicaljson = false,
|
||||
limit_notifications_power_levels = false,
|
||||
knock_join_rule = false,
|
||||
restricted_join_rule = false,
|
||||
restricted_join_rule_fix = false,
|
||||
knock_restricted_join_rule = false,
|
||||
enforce_int_power_levels = false,
|
||||
implicit_room_creator = false,
|
||||
updated_redaction_rules = false
|
||||
};
|
||||
binary_to_room_version(<<"5">>) ->
|
||||
#room_version{id = <<"5">>,
|
||||
enforce_key_validity = true,
|
||||
special_case_aliases_auth = true,
|
||||
strict_canonicaljson = false,
|
||||
limit_notifications_power_levels = false,
|
||||
knock_join_rule = false,
|
||||
restricted_join_rule = false,
|
||||
restricted_join_rule_fix = false,
|
||||
knock_restricted_join_rule = false,
|
||||
enforce_int_power_levels = false,
|
||||
implicit_room_creator = false,
|
||||
updated_redaction_rules = false
|
||||
};
|
||||
binary_to_room_version(<<"6">>) ->
|
||||
#room_version{id = <<"6">>,
|
||||
enforce_key_validity = true,
|
||||
special_case_aliases_auth = false,
|
||||
strict_canonicaljson = true,
|
||||
limit_notifications_power_levels = true,
|
||||
knock_join_rule = false,
|
||||
restricted_join_rule = false,
|
||||
restricted_join_rule_fix = false,
|
||||
knock_restricted_join_rule = false,
|
||||
enforce_int_power_levels = false,
|
||||
implicit_room_creator = false,
|
||||
updated_redaction_rules = false
|
||||
};
|
||||
binary_to_room_version(<<"7">>) ->
|
||||
#room_version{id = <<"7">>,
|
||||
enforce_key_validity = true,
|
||||
special_case_aliases_auth = false,
|
||||
strict_canonicaljson = true,
|
||||
limit_notifications_power_levels = true,
|
||||
knock_join_rule = true,
|
||||
restricted_join_rule = false,
|
||||
restricted_join_rule_fix = false,
|
||||
knock_restricted_join_rule = false,
|
||||
enforce_int_power_levels = false,
|
||||
implicit_room_creator = false,
|
||||
updated_redaction_rules = false
|
||||
};
|
||||
binary_to_room_version(<<"8">>) ->
|
||||
#room_version{id = <<"8">>,
|
||||
enforce_key_validity = true,
|
||||
special_case_aliases_auth = false,
|
||||
strict_canonicaljson = true,
|
||||
limit_notifications_power_levels = true,
|
||||
knock_join_rule = true,
|
||||
restricted_join_rule = true,
|
||||
restricted_join_rule_fix = false,
|
||||
knock_restricted_join_rule = false,
|
||||
enforce_int_power_levels = false,
|
||||
implicit_room_creator = false,
|
||||
updated_redaction_rules = false
|
||||
};
|
||||
binary_to_room_version(<<"9">>) ->
|
||||
#room_version{id = <<"9">>,
|
||||
enforce_key_validity = true,
|
||||
special_case_aliases_auth = false,
|
||||
strict_canonicaljson = true,
|
||||
limit_notifications_power_levels = true,
|
||||
knock_join_rule = true,
|
||||
restricted_join_rule = true,
|
||||
restricted_join_rule_fix = true,
|
||||
knock_restricted_join_rule = false,
|
||||
enforce_int_power_levels = false,
|
||||
implicit_room_creator = false,
|
||||
|
@ -2535,6 +2632,13 @@ binary_to_room_version(<<"9">>) ->
|
|||
};
|
||||
binary_to_room_version(<<"10">>) ->
|
||||
#room_version{id = <<"10">>,
|
||||
enforce_key_validity = true,
|
||||
special_case_aliases_auth = false,
|
||||
strict_canonicaljson = true,
|
||||
limit_notifications_power_levels = true,
|
||||
knock_join_rule = true,
|
||||
restricted_join_rule = true,
|
||||
restricted_join_rule_fix = true,
|
||||
knock_restricted_join_rule = true,
|
||||
enforce_int_power_levels = true,
|
||||
implicit_room_creator = false,
|
||||
|
@ -2542,6 +2646,13 @@ binary_to_room_version(<<"10">>) ->
|
|||
};
|
||||
binary_to_room_version(<<"11">>) ->
|
||||
#room_version{id = <<"11">>,
|
||||
enforce_key_validity = true,
|
||||
special_case_aliases_auth = false,
|
||||
strict_canonicaljson = true,
|
||||
limit_notifications_power_levels = true,
|
||||
knock_join_rule = true,
|
||||
restricted_join_rule = true,
|
||||
restricted_join_rule_fix = true,
|
||||
knock_restricted_join_rule = true,
|
||||
enforce_int_power_levels = true,
|
||||
implicit_room_creator = true,
|
||||
|
@ -2550,6 +2661,10 @@ binary_to_room_version(<<"11">>) ->
|
|||
binary_to_room_version(_) ->
|
||||
false.
|
||||
|
||||
supported_versions() ->
|
||||
[<<"4">>, <<"5">>, <<"6">>, <<"7">>, <<"8">>, <<"9">>,
|
||||
<<"10">>, <<"11">>].
|
||||
|
||||
json_to_event(#{<<"type">> := Type,
|
||||
<<"room_id">> := RoomID,
|
||||
<<"depth">> := Depth,
|
||||
|
@ -2562,6 +2677,17 @@ json_to_event(#{<<"type">> := Type,
|
|||
is_list(AuthEvents) ->
|
||||
StateKey = maps:get(<<"state_key">>, JSON, undefined),
|
||||
EventID = mod_matrix_gw:get_event_id(JSON, RoomVersion),
|
||||
case RoomVersion#room_version.strict_canonicaljson of
|
||||
true ->
|
||||
case mod_matrix_gw:is_canonical_json(JSON) of
|
||||
true ->
|
||||
ok;
|
||||
false ->
|
||||
throw(non_canonical_json)
|
||||
end;
|
||||
false ->
|
||||
ok
|
||||
end,
|
||||
#event{id = EventID,
|
||||
room_version = RoomVersion,
|
||||
room_id = RoomID,
|
||||
|
@ -3162,12 +3288,13 @@ new_room_id() ->
|
|||
MatrixServer = mod_matrix_gw_opt:matrix_domain(Host),
|
||||
<<$!, S/binary, $:, MatrixServer/binary>>.
|
||||
|
||||
compute_event_auth_keys(#{<<"type">> := ?ROOM_CREATE}) ->
|
||||
compute_event_auth_keys(#{<<"type">> := ?ROOM_CREATE}, _RoomVersion) ->
|
||||
[];
|
||||
compute_event_auth_keys(#{<<"type">> := ?ROOM_MEMBER,
|
||||
<<"sender">> := Sender,
|
||||
<<"content">> := #{<<"membership">> := Membership} = Content,
|
||||
<<"state_key">> := StateKey}) ->
|
||||
<<"state_key">> := StateKey},
|
||||
RoomVersion) ->
|
||||
Common = [{?ROOM_CREATE, <<"">>},
|
||||
{?ROOM_POWER_LEVELS, <<"">>},
|
||||
{?ROOM_MEMBER, Sender},
|
||||
|
@ -3175,7 +3302,8 @@ compute_event_auth_keys(#{<<"type">> := ?ROOM_MEMBER,
|
|||
case Membership of
|
||||
<<"join">> ->
|
||||
case Content of
|
||||
#{<<"join_authorised_via_users_server">> := AuthUser} ->
|
||||
#{<<"join_authorised_via_users_server">> := AuthUser}
|
||||
when RoomVersion#room_version.restricted_join_rule ->
|
||||
[{?ROOM_MEMBER, AuthUser}, {?ROOM_JOIN_RULES, <<"">>} | Common];
|
||||
_ ->
|
||||
[{?ROOM_JOIN_RULES, <<"">>} | Common]
|
||||
|
@ -3192,7 +3320,7 @@ compute_event_auth_keys(#{<<"type">> := ?ROOM_MEMBER,
|
|||
_ ->
|
||||
Common
|
||||
end;
|
||||
compute_event_auth_keys(#{<<"type">> := _, <<"sender">> := Sender}) ->
|
||||
compute_event_auth_keys(#{<<"type">> := _, <<"sender">> := Sender}, _RoomVersion) ->
|
||||
[{?ROOM_CREATE, <<"">>},
|
||||
{?ROOM_POWER_LEVELS, <<"">>},
|
||||
{?ROOM_MEMBER, Sender}].
|
||||
|
|
|
@ -28,7 +28,7 @@
|
|||
|
||||
%% API
|
||||
-export([start_link/2, supervisor/1, create_db/0,
|
||||
get_connection/2, check_auth/5, check_signature/2,
|
||||
get_connection/2, check_auth/5, check_signature/3,
|
||||
get_matrix_host_port/2]).
|
||||
|
||||
%% gen_statem callbacks
|
||||
|
@ -38,6 +38,7 @@
|
|||
-include("logger.hrl").
|
||||
-include("ejabberd_http.hrl").
|
||||
-include_lib("kernel/include/inet.hrl").
|
||||
-include("mod_matrix_gw.hrl").
|
||||
|
||||
-record(matrix_s2s,
|
||||
{to :: binary(),
|
||||
|
@ -169,18 +170,21 @@ check_auth(Host, MatrixServer, AuthParams, Content, Request) ->
|
|||
false
|
||||
end.
|
||||
|
||||
check_signature(Host, JSON) ->
|
||||
check_signature(Host, JSON, RoomVersion) ->
|
||||
case JSON of
|
||||
#{<<"sender">> := Sender,
|
||||
<<"signatures">> := Sigs} ->
|
||||
<<"signatures">> := Sigs,
|
||||
<<"origin_server_ts">> := OriginServerTS} ->
|
||||
MatrixServer = mod_matrix_gw:get_id_domain_exn(Sender),
|
||||
case Sigs of
|
||||
#{MatrixServer := #{} = KeySig} ->
|
||||
case maps:next(maps:iterator(KeySig)) of
|
||||
{KeyID, _Sig, _} ->
|
||||
case catch get_key(Host, MatrixServer, KeyID) of
|
||||
{ok, VerifyKey, _ValidUntil} ->
|
||||
%% TODO: check ValidUntil
|
||||
{ok, VerifyKey, ValidUntil} ->
|
||||
if
|
||||
not RoomVersion#room_version.enforce_key_validity or
|
||||
OriginServerTS =< ValidUntil ->
|
||||
case check_signature(JSON, MatrixServer, KeyID, VerifyKey) of
|
||||
true ->
|
||||
true;
|
||||
|
@ -188,6 +192,9 @@ check_signature(Host, JSON) ->
|
|||
?WARNING_MSG("Failed authentication: ~p", [JSON]),
|
||||
false
|
||||
end;
|
||||
true ->
|
||||
false
|
||||
end;
|
||||
_ ->
|
||||
false
|
||||
end;
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue