diff options
-rw-r--r-- | doc/guide.tex | 24 | ||||
-rw-r--r-- | src/ejabberd_c2s.erl | 34 | ||||
-rw-r--r-- | src/mod_roster.erl | 12 |
3 files changed, 57 insertions, 13 deletions
diff --git a/doc/guide.tex b/doc/guide.tex index e0ff370f9..2f573aa4f 100644 --- a/doc/guide.tex +++ b/doc/guide.tex @@ -3869,6 +3869,13 @@ Options: \begin{description} \backend{\term{rosteritem} and \term{rostergroup}} \iqdiscitem{Roster Management (\ns{jabber:iq:roster})} + \titem{access} \ind{options!access} + This option can be configured to specify rules to restrict roster management. + If a rule returns `deny' on the requested user name, + that user cannot modify his personal roster: + not add/remove/modify contacts, + or subscribe/unsubscribe presence. + By default there aren't restrictions. \titem{\{versioning, false|true\}} \ind{options!versioning}Enables Roster Versioning. This option is disabled by default. @@ -3892,6 +3899,23 @@ This example configuration enables Roster Versioning with storage of current id: ]}. \end{verbatim} +With this example configuration only admins can manage their rosters; +everybody else cannot modify the roster: +\begin{verbatim} +{acl, admin, {user, "bob", "example.org"}}. +{acl, admin, {user, "sarah", "example.org"}}. + +{access, roster, [{allow, admin}, + {deny, all}]}. + +{modules, + [ + ... + {mod_roster, [{access, roster}]}, + ... + ]}. +\end{verbatim} + \makesubsection{modservicelog}{\modservicelog{}} \ind{modules!\modservicelog{}}\ind{message auditing}\ind{Bandersnatch} diff --git a/src/ejabberd_c2s.erl b/src/ejabberd_c2s.erl index 2ee4e0a22..309287721 100644 --- a/src/ejabberd_c2s.erl +++ b/src/ejabberd_c2s.erl @@ -1764,11 +1764,12 @@ presence_track(From, To, Packet, StateData) -> StateData#state{pres_i = I, pres_a = A}; 'subscribe' -> - ejabberd_hooks:run(roster_out_subscription, - StateData#state.server, - [StateData#state.user, StateData#state.server, To, subscribe]), - check_privacy_route(From, StateData, exmpp_jid:bare(From), - To, Packet), + try_check_privacy_route(subscribe, StateData#state.user, StateData#state.server, + From, StateData, exmpp_jid:bare(From), To, Packet), + StateData; + 'unsubscribe' -> + try_check_privacy_route(subscribe, StateData#state.user, StateData#state.server, + From, StateData, exmpp_jid:bare(From), To, Packet), StateData; 'subscribed' -> ejabberd_hooks:run(roster_out_subscription, @@ -1777,13 +1778,6 @@ presence_track(From, To, Packet, StateData) -> check_privacy_route(From, StateData, exmpp_jid:bare(From), To, Packet), StateData; - 'unsubscribe' -> - ejabberd_hooks:run(roster_out_subscription, - StateData#state.server, - [StateData#state.user, StateData#state.server, To, unsubscribe]), - check_privacy_route(From, StateData, exmpp_jid:bare(From), - To, Packet), - StateData; 'unsubscribed' -> ejabberd_hooks:run(roster_out_subscription, StateData#state.server, @@ -1805,6 +1799,22 @@ presence_track(From, To, Packet, StateData) -> pres_a = A} end. +%%% Check ACL before allowing to send a subscription stanza +try_check_privacy_route(Type, User, Server, From, StateData, FromRoute, To, Packet) -> + JID1 = exmpp_jid:make(User, Server, undefined), + Access = gen_mod:get_module_opt(Server, mod_roster, access, all), + case acl:match_rule(Server, Access, JID1) of + deny -> + %% Silently drop this (un)subscription request + ok; + allow -> + ejabberd_hooks:run(roster_out_subscription, + Server, + [User, Server, To, Type]), + check_privacy_route(From, StateData, FromRoute, + To, Packet) + end. + check_privacy_route(From, StateData, FromRoute, To, Packet) -> case ejabberd_hooks:run_fold( privacy_check_packet, StateData#state.server, diff --git a/src/mod_roster.erl b/src/mod_roster.erl index 2ee73e1f6..39c8c50b9 100644 --- a/src/mod_roster.erl +++ b/src/mod_roster.erl @@ -238,7 +238,7 @@ process_local_iq(From, To, #iq{type = get} = IQ_Rec) process_iq_get(From, To, IQ_Rec); process_local_iq(From, To, #iq{type = set} = IQ_Rec) when ?IS_JID(From), ?IS_JID(To), ?IS_IQ_RECORD(IQ_Rec) -> - process_iq_set(From, To, IQ_Rec). + try_process_iq_set(From, To, IQ_Rec). roster_hash(Items) -> sha:sha(term_to_binary( @@ -449,6 +449,16 @@ process_iq_set(From, To, #iq{payload = Request} = IQ_Rec) -> end, exmpp_iq:result(IQ_Rec). +try_process_iq_set(From, To, IQ) -> + LServer = exmpp_jid:prep_domain_as_list(From), + Access = gen_mod:get_module_opt(LServer, ?MODULE, access, all), + case acl:match_rule(LServer, Access, From) of + deny -> + exmpp_iq:error(IQ, 'not-allowed'); + allow -> + process_iq_set(From, To, IQ) + end. + %% @spec (From, To, El) -> ok %% From = exmpp_jid:jid() %% To = exmpp_jid:jid() |