Add implementation for SASL2 and Bind2

This commit is contained in:
Paweł Chmielowski 2023-11-16 16:52:18 +01:00
parent b0a9b58958
commit efffc3142a
6 changed files with 114 additions and 34 deletions

View File

@ -114,7 +114,7 @@ defmodule Ejabberd.MixProject do
{:p1_utils, "~> 1.0"},
{:pkix, "~> 1.0"},
{:stringprep, ">= 1.0.26"},
{:xmpp, ">= 1.7.0"},
{:xmpp, git: "https://github.com/processone/xmpp.git", ref: "b6c0d6310b03939aab4db7aebc0f5fe323dadc51", override: true},
{:yconf, "~> 1.0"}]
++ cond_deps()
end

View File

@ -30,6 +30,6 @@
"stringprep": {:hex, :stringprep, "1.0.29", "02f23e8c3a219a3dfe40a22e908bece3a2f68af0ff599ea8a7b714ecb21e62ee", [:rebar3], [{:p1_utils, "1.0.25", [hex: :p1_utils, repo: "hexpm", optional: false]}], "hexpm", "928eba304c3006eb1512110ebd7b87db163b00859a09375a1e4466152c6c462a"},
"stun": {:hex, :stun, "1.2.10", "53f8be69e14f9476dcaf1dfb626b9dad2380f3fba8faf2c30bdf74311cfdc008", [:rebar3], [{:fast_tls, "1.1.16", [hex: :fast_tls, repo: "hexpm", optional: false]}, {:p1_utils, "1.0.25", [hex: :p1_utils, repo: "hexpm", optional: false]}], "hexpm", "19d3eecbfcc6935f0880f8ef7e77ff373900c604092937a1acda166ae3fb40e9"},
"unicode_util_compat": {:hex, :unicode_util_compat, "0.4.1", "d869e4c68901dd9531385bb0c8c40444ebf624e60b6962d95952775cac5e90cd", [:rebar3], [], "hexpm", "1d1848c40487cdb0b30e8ed975e34e025860c02e419cb615d255849f3427439d"},
"xmpp": {:hex, :xmpp, "1.7.0", "2c3034ed501c9ccb7a5e73a462a74e082b4cf9f485bb551732e56fb15106dac9", [:rebar3], [{:ezlib, "1.0.12", [hex: :ezlib, repo: "hexpm", optional: false]}, {:fast_tls, "1.1.16", [hex: :fast_tls, repo: "hexpm", optional: false]}, {:fast_xml, "1.1.49", [hex: :fast_xml, repo: "hexpm", optional: false]}, {:idna, "6.0.0", [hex: :idna, repo: "hexpm", optional: false]}, {:p1_utils, "1.0.25", [hex: :p1_utils, repo: "hexpm", optional: false]}, {:stringprep, "1.0.29", [hex: :stringprep, repo: "hexpm", optional: false]}], "hexpm", "821bc8e2c4b288c031aa95aca17f3b215ed9bd634b7448d0f273baf8f6a46243"},
"xmpp": {:git, "https://github.com/processone/xmpp.git", "b6c0d6310b03939aab4db7aebc0f5fe323dadc51", [ref: "b6c0d6310b03939aab4db7aebc0f5fe323dadc51"]},
"yconf": {:hex, :yconf, "1.0.15", "e22998b3d7728270bdd06162a9515bd142b14fae8927cbdbd3ef639c32aa6f7a", [:rebar3], [{:fast_yaml, "1.0.36", [hex: :fast_yaml, repo: "hexpm", optional: false]}], "hexpm", "7ff2ab24d3c9833842716b9aaaa01a8f96641a7695cbb701b03445c4def01117"},
}

View File

@ -77,7 +77,7 @@
{stringprep, ".*", {git, "https://github.com/processone/stringprep", {tag, "1.0.29"}}},
{if_var_true, stun,
{stun, ".*", {git, "https://github.com/processone/stun", {tag, "1.2.10"}}}},
{xmpp, ".*", {git, "https://github.com/processone/xmpp", "307deb6065476bf102d9a8eeb594524eaed36c13"}},
{xmpp, ".*", {git, "https://github.com/processone/xmpp", "b6c0d6310b03939aab4db7aebc0f5fe323dadc51"}},
{yconf, ".*", {git, "https://github.com/processone/yconf", {tag, "1.0.15"}}}
]}.

View File

@ -40,7 +40,9 @@
handle_stream_start/2, handle_stream_end/2,
handle_unauthenticated_packet/2, handle_authenticated_packet/2,
handle_auth_success/4, handle_auth_failure/4, handle_send/3,
handle_recv/3, handle_cdata/2, handle_unbinded_packet/2]).
handle_recv/3, handle_cdata/2, handle_unbinded_packet/2,
inline_stream_features/1, handle_sasl2_inline/2,
handle_sasl2_inline_post/3, handle_bind2_inline/2]).
%% Hooks
-export([handle_unexpected_cast/2, handle_unexpected_call/3,
process_auth_result/3, reject_unauthenticated_packet/2,
@ -381,6 +383,9 @@ unauthenticated_stream_features(#{lserver := LServer}) ->
authenticated_stream_features(#{lserver := LServer}) ->
ejabberd_hooks:run_fold(c2s_post_auth_features, LServer, [], [LServer]).
inline_stream_features(#{lserver := LServer}) ->
ejabberd_hooks:run_fold(c2s_inline_features, LServer, {[], []}, [LServer]).
sasl_mechanisms(Mechs, #{lserver := LServer, stream_encrypted := Encrypted} = State) ->
Type = ejabberd_auth:store_type(LServer),
Mechs1 = ejabberd_option:disable_sasl_mechanisms(LServer),
@ -533,6 +538,18 @@ handle_cdata(Data, #{lserver := LServer} = State) ->
ejabberd_hooks:run_fold(c2s_handle_cdata, LServer,
State, [Data]).
handle_sasl2_inline(Els, #{lserver := LServer} = State) ->
ejabberd_hooks:run_fold(c2s_handle_sasl2_inline, LServer,
{State, Els, []}, []).
handle_sasl2_inline_post(Els, Results, #{lserver := LServer} = State) ->
ejabberd_hooks:run_fold(c2s_handle_sasl2_inline_post, LServer,
State, [Els, Results]).
handle_bind2_inline(Els, #{lserver := LServer} = State) ->
ejabberd_hooks:run_fold(c2s_handle_bind2_inline, LServer,
State, [Els]).
handle_recv(El, Pkt, #{lserver := LServer} = State) ->
ejabberd_hooks:run_fold(c2s_handle_recv, LServer, State, [El, Pkt]).

View File

@ -61,7 +61,8 @@
c2s_copy_session/2,
webadmin_page/3,
webadmin_user/4,
webadmin_user_parse_query/5]).
webadmin_user_parse_query/5,
c2s_handle_bind2_inline/2]).
-export([mod_opt_type/1, mod_options/1, mod_doc/0, depends/2]).
@ -130,6 +131,7 @@ start(Host, Opts) ->
{hook, disco_info, get_info, 50},
{hook, c2s_handle_info, c2s_handle_info, 50},
{hook, c2s_copy_session, c2s_copy_session, 50},
{hook, c2s_handle_bind2_inline, c2s_handle_bind2_inline, 50},
{hook, webadmin_page_host, webadmin_page, 50},
{hook, webadmin_user, webadmin_user, 50},
{hook, webadmin_user_parse_query, webadmin_user_parse_query, 50},
@ -297,6 +299,10 @@ c2s_copy_session(State, #{resend_offline := Flag}) ->
c2s_copy_session(State, _) ->
State.
c2s_handle_bind2_inline(#{jid := #jid{luser = LUser, lserver = LServer}} = State, _Els) ->
delete_all_msgs(LUser, LServer),
State.
-spec handle_offline_query(iq()) -> iq().
handle_offline_query(#iq{from = #jid{luser = U1, lserver = S1},
to = #jid{luser = U2, lserver = S2},

View File

@ -33,10 +33,15 @@
c2s_authenticated_packet/2, c2s_unauthenticated_packet/2,
c2s_unbinded_packet/2, c2s_closed/2, c2s_terminated/2,
c2s_handle_send/3, c2s_handle_info/2, c2s_handle_call/3,
c2s_handle_recv/3]).
c2s_handle_recv/3, c2s_inline_features/2,
c2s_handle_sasl2_inline/1, c2s_handle_sasl2_inline_post/3,
c2s_handle_bind2_inline/2]).
%% adjust pending session timeout / access queue
-export([get_resume_timeout/1, set_resume_timeout/2, queue_find/2]).
%% for sasl2 inline resume
-export([has_resume_data/2, post_resume_tasks/1]).
-include_lib("xmpp/include/xmpp.hrl").
-include("logger.hrl").
-include_lib("p1_utils/include/p1_queue.hrl").
@ -65,6 +70,7 @@ start(_Host, Opts) ->
init_cache(Opts),
{ok, [{hook, c2s_stream_started, c2s_stream_started, 50},
{hook, c2s_post_auth_features, c2s_stream_features, 50},
{hook, c2s_inline_features, c2s_inline_features, 50},
{hook, c2s_unauthenticated_packet, c2s_unauthenticated_packet, 50},
{hook, c2s_unbinded_packet, c2s_unbinded_packet, 50},
{hook, c2s_authenticated_packet, c2s_authenticated_packet, 50},
@ -72,6 +78,9 @@ start(_Host, Opts) ->
{hook, c2s_handle_recv, c2s_handle_recv, 50},
{hook, c2s_handle_info, c2s_handle_info, 50},
{hook, c2s_handle_call, c2s_handle_call, 50},
{hook, c2s_handle_sasl2_inline, c2s_handle_sasl2_inline, 50},
{hook, c2s_handle_sasl2_inline_post, c2s_handle_sasl2_inline_post, 50},
{hook, c2s_handle_bind2_inline, c2s_handle_bind2_inline, 50},
{hook, c2s_closed, c2s_closed, 50},
{hook, c2s_terminated, c2s_terminated, 50}]}.
@ -112,6 +121,45 @@ c2s_stream_features(Acc, Host) ->
Acc
end.
c2s_inline_features({Sasl, Bind} = Acc, Host) ->
case gen_mod:is_loaded(Host, ?MODULE) of
true ->
{[#feature_sm{xmlns = ?NS_STREAM_MGMT_3} | Sasl],
[#feature_sm{xmlns = ?NS_STREAM_MGMT_3} | Bind]};
false ->
Acc
end.
c2s_handle_sasl2_inline({State, Els, Results} = Acc) ->
case lists:keytake(sm_resume, 1, Els) of
{value, Resume, Rest} ->
case has_resume_data(State, Resume) of
{ok, NewState, Resumed} ->
Rest2 = lists:keydelete(bind2_bind, 1, Rest),
{NewState, Rest2, [Resumed | Results]};
{error, ResumeError} ->
{State, Els, [ResumeError | Results]}
end;
_ ->
Acc
end.
c2s_handle_sasl2_inline_post(State, _Els, Results) ->
case lists:keyfind(sm_resumed, 1, Results) of
false ->
State;
_ ->
post_resume_tasks(State)
end.
c2s_handle_bind2_inline(State, Els) ->
case lists:keyfind(sm_enable, 1, Els) of
#sm_enable{} = Pkt ->
negotiate_stream_mgmt(Pkt, State);
_ ->
State
end.
c2s_unauthenticated_packet(#{lang := Lang} = State, Pkt) when ?is_sm_packet(Pkt) ->
%% XEP-0198 says: "For client-to-server connections, the client MUST NOT
%% attempt to enable stream management until after it has completed Resource
@ -394,39 +442,48 @@ handle_a(State, #sm_a{h = H}) ->
resend_rack(State1).
-spec handle_resume(state(), sm_resume()) -> {ok, state()} | {error, state()}.
handle_resume(#{user := User, lserver := LServer,
lang := Lang, socket := Socket} = State,
#sm_resume{h = H, previd = PrevID, xmlns = Xmlns}) ->
R = case inherit_session_state(State, PrevID) of
{ok, InheritedState} ->
{ok, InheritedState, H};
{error, Err, InH} ->
{error, #sm_failed{reason = 'item-not-found',
text = xmpp:mk_text(format_error(Err), Lang),
h = InH, xmlns = Xmlns}, Err};
{error, Err} ->
{error, #sm_failed{reason = 'item-not-found',
text = xmpp:mk_text(format_error(Err), Lang),
xmlns = Xmlns}, Err}
end,
case R of
{ok, #{jid := JID} = ResumedState, NumHandled} ->
State1 = check_h_attribute(ResumedState, NumHandled),
#{mgmt_xmlns := AttrXmlns, mgmt_stanzas_in := AttrH} = State1,
State2 = send(State1, #sm_resumed{xmlns = AttrXmlns,
h = AttrH,
previd = PrevID}),
State3 = resend_unacked_stanzas(State2),
State4 = send(State3, #sm_r{xmlns = AttrXmlns}),
State5 = ejabberd_hooks:run_fold(c2s_session_resumed, LServer, State4, []),
?INFO_MSG("(~ts) Resumed session for ~ts",
[xmpp_socket:pp(Socket), jid:encode(JID)]),
{ok, State5};
handle_resume(#{user := User, lserver := LServer, lang := Lang} = State,
#sm_resume{} = Resume) ->
case has_resume_data(State, Resume) of
{ok, ResumedState, ResumedEl} ->
State2 = send(ResumedState, ResumedEl),
{ok, post_resume_tasks(State2)};
{error, El, Reason} ->
log_resumption_error(User, LServer, Reason),
{error, send(State, El)}
end.
-spec has_resume_data(state(), sm_resume()) ->
{ok, state(), sm_resumed()} | {error, sm_failed(), error_reason()}.
has_resume_data(#{lang := Lang} = State,
#sm_resume{h = H, previd = PrevID, xmlns = Xmlns}) ->
case inherit_session_state(State, PrevID) of
{ok, InheritedState} ->
State1 = check_h_attribute(InheritedState, H),
#{mgmt_xmlns := AttrXmlns, mgmt_stanzas_in := AttrH} = State1,
{ok, InheritedState, #sm_resumed{xmlns = AttrXmlns,
h = AttrH,
previd = PrevID}};
{error, Err, InH} ->
{error, #sm_failed{reason = 'item-not-found',
text = xmpp:mk_text(format_error(Err), Lang),
h = InH, xmlns = Xmlns}, Err};
{error, Err} ->
{error, #sm_failed{reason = 'item-not-found',
text = xmpp:mk_text(format_error(Err), Lang),
xmlns = Xmlns}, Err}
end.
-spec post_resume_tasks(state()) -> state().
post_resume_tasks(#{lserver := LServer, socket := Socket, jid := JID,
mgmt_xmlns := AttrXmlns} = State) ->
State3 = resend_unacked_stanzas(State),
State4 = send(State3, #sm_r{xmlns = AttrXmlns}),
State5 = ejabberd_hooks:run_fold(c2s_session_resumed, LServer, State4, []),
?INFO_MSG("(~ts) Resumed session for ~ts",
[xmpp_socket:pp(Socket), jid:encode(JID)]),
State5.
-spec transition_to_pending(state(), _) -> state().
transition_to_pending(#{mgmt_state := active, mod := Mod,
mgmt_timeout := 0} = State, _Reason) ->