diff --git a/src/mod_privilege.erl b/src/mod_privilege.erl index 2597ac8fe66..8b7fe061200 100644 --- a/src/mod_privilege.erl +++ b/src/mod_privilege.erl @@ -214,7 +214,8 @@ component_connected(Host) -> fun(ServerHost) -> Proc = gen_mod:get_module_proc_check(ServerHost, ?MODULE), gen_server:cast(Proc, {component_connected, Host}) - end, ejabberd_option:hosts()). + end, [H || H <- ejabberd_option:hosts(), + gen_mod:is_loaded(H, ?MODULE)]). -spec component_disconnected(binary(), binary()) -> ok. component_disconnected(Host, _Reason) -> @@ -222,7 +223,8 @@ component_disconnected(Host, _Reason) -> fun(ServerHost) -> Proc = gen_mod:get_module_proc_check(ServerHost, ?MODULE), gen_server:cast(Proc, {component_disconnected, Host}) - end, ejabberd_option:hosts()). + end, [H || H <- ejabberd_option:hosts(), + gen_mod:is_loaded(H, ?MODULE)]). %% %% Message processing @@ -266,12 +268,16 @@ component_send_packet({#iq{from = From, type = Type} = IQ, State}) - when Type /= error -> + when Type == get; Type == set -> Host = From#jid.lserver, ServerHost = To#jid.lserver, Permissions = get_permissions(ServerHost), Result = case {maps:find(Host, Permissions), get_iq_encapsulated_details(IQ)} of + {_, {unprivileged_iq}} -> + ?DEBUG("Component ~ts sent a not-wrapped IQ stanza, routing it as-is.", + [From#jid.lserver]), + IQ; {{ok, Access}, {privileged_iq, EncapType, EncapNs, EncapFrom, EncIq}} when (EncapType == Type) and ((EncapFrom == undefined) or (EncapFrom == To)) -> NsPermissions = proplists:get_value(iq, Access, []), @@ -307,10 +313,6 @@ component_send_packet({#iq{from = From, "IQ stanza and the TO in top-level IQ stanza do not match", []), drop; - {_, {unprivileged_iq}} -> - ?DEBUG("Component ~ts sent a not-wrapped IQ stanza, routing it as-is.", - [From#jid.lserver]), - IQ; {_, {error, ErrType, _Err}} -> ?INFO_MSG("IQ not forwarded: Component tried to send not valid IQ stanza: ~p.", [ErrType]), diff --git a/test/ejabberd_SUITE.erl b/test/ejabberd_SUITE.erl index 50699812df4..e58e4e8a799 100644 --- a/test/ejabberd_SUITE.erl +++ b/test/ejabberd_SUITE.erl @@ -556,6 +556,9 @@ component_tests() -> test_missing_to, test_invalid_from, test_component_send, + test_component_privilege, + test_component_privilege_vhosts, + test_component_privilege_result, bad_nonza, codec_failure]}]. @@ -831,6 +834,82 @@ test_component_send(Config) -> sub_els = [#ping{}]}), disconnect(Config). +test_component_privilege(Config) -> + Server = ?COMMON_VHOST, + Host = jid:make(Server), + Upload = jid:make(?UPLOAD_VHOST), + User = jid:make(<<"test_privilege">>, Server), + From = server_jid(Config), + %% Without privileges, plain IQs go to the host and to a service + %% that is not a host, as if mod_privilege were not loaded (#4472) + #iq{type = result, from = Upload} = + send_recv(Config, #iq{type = get, to = Upload, from = From, + sub_els = [#disco_info{}]}), + gen_mod:update_module(Server, mod_privilege, + #{iq => [{?NS_PING, [{both, all}]}]}), + ejabberd_hooks:run(component_connected, [From#jid.lserver]), + ?recv1(#message{from = Host, sub_els = [#privilege{}]}), + %% With privileges, plain IQs still go anywhere + #iq{type = result, from = Host} = + send_recv(Config, #iq{type = get, to = Host, from = From, + sub_els = [#ping{}]}), + #iq{type = result, from = Upload} = + send_recv(Config, #iq{type = get, to = Upload, from = From, + sub_els = [#disco_info{}]}), + %% A privileged IQ in a granted namespace is forwarded + #iq{type = result, from = User, + sub_els = [#privilege{forwarded = #forwarded{sub_els = [_]}}]} = + send_recv(Config, #iq{type = get, to = User, from = From, + sub_els = [#privileged_iq{ + iq = #iq{type = get, to = User, from = User, + sub_els = [#ping{}]}}]}), + %% A privileged IQ in a namespace not granted is dropped + send(Config, #iq{type = get, to = User, from = From, + sub_els = [#privileged_iq{ + iq = #iq{type = get, to = User, from = User, + sub_els = [#disco_info{}]}}]}), + ID = send(Config, #iq{type = get, to = Host, from = From, + sub_els = [#ping{}]}), + ?recv1(#iq{type = result, id = ID, from = Host}), + gen_mod:update_module(Server, mod_privilege, #{iq => []}), + disconnect(Config). + +test_component_privilege_vhosts(Config) -> + %% mod_privilege is loaded on a vhost that comes after vhosts + %% that do not load it: the component still gets its privileges there + Server = ?MNESIA_VHOST, + From = server_jid(Config), + Modules = ejabberd_option:modules(Server), + Opts = #{roster => [{both, none}, {get, none}, {set, none}], + iq => [{?NS_PING, [{both, all}]}], + presence => [{managed_entity, none}, {roster, none}], + message => [{outgoing, none}]}, + %% The suite's configuration may load mod_privilege everywhere: + %% start it here with this vhost's own options + Loaded = gen_mod:is_loaded(Server, mod_privilege), + Loaded andalso gen_mod:stop_module(Server, mod_privilege), + ejabberd_config:set_option({modules, Server}, + [{mod_privilege, Opts, 0} | Modules]), + {ok, _} = gen_mod:start_module(Server, mod_privilege), + ejabberd_hooks:run(component_connected, [From#jid.lserver]), + ?recv1(#message{from = #jid{lserver = Server}, sub_els = [#privilege{}]}), + gen_mod:stop_module(Server, mod_privilege), + ejabberd_config:set_option({modules, Server}, Modules), + Loaded andalso gen_mod:start_module(Server, mod_privilege), + disconnect(Config). + +test_component_privilege_result(Config) -> + %% A component's answer to an IQ from the server, a result without a + %% payload (a push service's to mod_push), is routed as it is: it is + %% not a privileged IQ, which only a get or a set can be + From = server_jid(Config), + To = jid:make(?COMMON_VHOST), + IQ = #iq{type = result, id = p1_rand:get_string(), from = From, to = To}, + {IQ, state} = mod_privilege:component_send_packet({IQ, state}), + Err = IQ#iq{type = error, sub_els = [xmpp:err_service_unavailable()]}, + {Err, state} = mod_privilege:component_send_packet({Err, state}), + disconnect(Config). + s2s_dialback(Config) -> Server = ?config(server, Config), ejabberd_s2s:stop_s2s_connections(), diff --git a/test/ejabberd_SUITE_data/ejabberd.yml b/test/ejabberd_SUITE_data/ejabberd.yml index e136fd84748..d28dfc802f1 100644 --- a/test/ejabberd_SUITE_data/ejabberd.yml +++ b/test/ejabberd_SUITE_data/ejabberd.yml @@ -124,6 +124,7 @@ modules: mod_configure: [] mod_disco: [] mod_ping: [] + mod_privilege: [] mod_proxy65: port: PROXY_PORT vcard: VCARD