./ct_report/coverage/ejabberd_auth_anonymous.COVER.html

1 %%%----------------------------------------------------------------------
2 %%% File : ejabberd_auth_anonymous.erl
3 %%% Author : Mickael Remond <mickael.remond@process-one.net>
4 %%% Purpose : Anonymous feature support in ejabberd
5 %%% Created : 17 Feb 2006 by Mickael Remond <mremond@process-one.net>
6 %%%
7 %%%
8 %%% ejabberd, Copyright (C) 2002-2011 ProcessOne
9 %%%
10 %%% This program is free software; you can redistribute it and/or
11 %%% modify it under the terms of the GNU General Public License as
12 %%% published by the Free Software Foundation; either version 2 of the
13 %%% License, or (at your option) any later version.
14 %%%
15 %%% This program is distributed in the hope that it will be useful,
16 %%% but WITHOUT ANY WARRANTY; without even the implied warranty of
17 %%% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
18 %%% General Public License for more details.
19 %%%
20 %%% You should have received a copy of the GNU General Public License
21 %%% along with this program; if not, write to the Free Software
22 %%% Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
23 %%%
24 %%%----------------------------------------------------------------------
25
26 -module(ejabberd_auth_anonymous).
27 -author('mickael.remond@process-one.net').
28
29 -export([start/1,
30 stop/1,
31 config_spec/0,
32 register_connection/3,
33 unregister_connection/3,
34 session_cleanup/3
35 ]).
36
37 -behaviour(mongoose_gen_auth).
38
39 %% Function used by ejabberd_auth:
40 -export([login/3,
41 set_password/4,
42 authorize/1,
43 get_password/3,
44 does_user_exist/3,
45 supports_sasl_module/2,
46 get_registered_users/3,
47 supported_features/0
48 ]).
49
50 %% Internal
51 -export([check_password/4,
52 check_password/6]).
53
54 -ignore_xref([login/3]).
55
56 -include("mongoose.hrl").
57 -include("jlib.hrl").
58 -include("session.hrl").
59 -include("mongoose_config_spec.hrl").
60
61 %% @doc Create the anonymous table if at least one host type has anonymous
62 %% features enabled. Register to login / logout events
63 -spec start(mongooseim:host_type()) -> ok.
64 start(HostType) ->
65 %% TODO: Check cluster mode
66 145 ejabberd_auth_anonymous_backend:init(HostType),
67 %% The hooks are needed to add / remove users from the anonymous tables
68 145 gen_hook:add_handlers(hooks(HostType)),
69 145 ok.
70
71 -spec stop(mongooseim:host_type()) -> ok.
72 stop(HostType) ->
73
:-(
gen_hook:delete_handlers(hooks(HostType)),
74
:-(
ejabberd_auth_anonymous_backend:stop(HostType),
75
:-(
ok.
76
77 -spec hooks(mongooseim:host_type()) -> gen_hook:hook_list().
78 hooks(HostType) ->
79 145 [
80 {sm_register_connection_hook, HostType, fun ?MODULE:register_connection/3, #{}, 100},
81 {sm_remove_connection_hook, HostType, fun ?MODULE:unregister_connection/3, #{}, 100},
82 {session_cleanup, HostType, fun ?MODULE:session_cleanup/3, #{}, 50}
83 ].
84
85 -spec config_spec() -> mongoose_config_spec:config_section().
86 config_spec() ->
87 206 #section{
88 items = #{<<"backend">> => #option{type = atom, validate = {module, ?MODULE}},
89 <<"allow_multiple_connections">> => #option{type = boolean},
90 <<"protocol">> => #option{type = atom,
91 validate = {enum, [sasl_anon, login_anon, both]}}
92 },
93 defaults = #{<<"backend">> => mnesia,
94 <<"allow_multiple_connections">> => false,
95 <<"protocol">> => sasl_anon}
96 }.
97
98 %% @doc Return true if multiple connections have been allowed in the config file
99 %% defaults to false
100 -spec allow_multiple_connections(mongooseim:host_type()) -> boolean().
101 allow_multiple_connections(HostType) ->
102
:-(
mongoose_config:get_opt([{auth, HostType}, anonymous, allow_multiple_connections]).
103
104 does_user_exist(HostType, LUser, LServer) ->
105 11 does_anonymous_user_exist(HostType, LUser, LServer).
106
107 %% @doc Check if user exist in the anonymous database
108 -spec does_anonymous_user_exist(mongooseim:host_type(), jid:luser(), jid:lserver()) -> boolean().
109 does_anonymous_user_exist(HostType, LUser, LServer) ->
110 17 US = {LUser, LServer},
111 17 ejabberd_auth_anonymous_backend:does_anonymous_user_exist(HostType, US).
112
113 %% @doc Remove connection from Mnesia tables
114 -spec remove_connection(
115 mongooseim:host_type(), ejabberd_sm:sid(), jid:luser(), jid:lserver()) -> ok.
116 remove_connection(HostType, SID, LUser, LServer) ->
117 4 US = {LUser, LServer},
118 4 ejabberd_auth_anonymous_backend:remove_connection(HostType, SID, US).
119
120 %% @doc Register connection
121 -spec register_connection(Acc, Params, Extra) -> {ok, Acc} when
122 Acc :: term(),
123 Params :: map(),
124 Extra :: map().
125 register_connection(Acc,
126 #{sid := SID,
127 jid := #jid{luser = LUser, lserver = LServer},
128 info := #{auth_module := AuthModule}},
129 #{host_type := HostType})
130 when AuthModule =:= ejabberd_auth_anonymous; % login_anon
131 AuthModule =:= cyrsasl_anonymous -> % sasl_anon
132 4 mongoose_hooks:register_user(HostType, LServer, LUser),
133 4 US = {LUser, LServer},
134 4 ejabberd_auth_anonymous_backend:add_connection(HostType, SID, US),
135 4 {ok, Acc};
136 register_connection(Acc, _Params, _Extra) ->
137
:-(
{ok, Acc}.
138
139 %% @doc Remove an anonymous user from the anonymous users table
140 -spec unregister_connection(Acc, Params, Extra) -> {ok, Acc} when
141 Acc :: mongoose_acc:t(),
142 Params :: map(),
143 Extra :: map().
144 unregister_connection(Acc, #{sid := SID, jid := #jid{luser = LUser, lserver = LServer}}, #{host_type := HostType}) ->
145 4 purge_hook(does_anonymous_user_exist(HostType, LUser, LServer), HostType, LUser, LServer),
146 4 remove_connection(HostType, SID, LUser, LServer),
147 4 {ok, Acc}.
148
149 %% @doc Launch the hook to purge user data only for anonymous users.
150 -spec purge_hook(boolean(), mongooseim:host_type(), jid:luser(), jid:lserver()) -> 'ok'.
151 purge_hook(false, _HostType, _LUser, _LServer) ->
152
:-(
ok;
153 purge_hook(true, HostType, LUser, LServer) ->
154 4 Acc = mongoose_acc:new(#{ location => ?LOCATION,
155 host_type => HostType,
156 lserver => LServer,
157 element => undefined }),
158 4 mongoose_hooks:anonymous_purge_hook(LServer, Acc, LUser).
159
160 -spec session_cleanup(Acc, Params, Extra) -> {ok, Acc} when
161 Acc :: mongoose_acc:t(),
162 Params :: map(),
163 Extra :: map().
164 session_cleanup(Acc, #{sid := SID, jid := #jid{luser = LUser, lserver = LServer}}, #{host_type := HostType}) ->
165
:-(
remove_connection(HostType, SID, LUser, LServer),
166
:-(
{ok, Acc}.
167
168 %% ---------------------------------
169 %% Specific anonymous auth functions
170 %% ---------------------------------
171
172 -spec authorize(mongoose_credentials:t()) -> {ok, mongoose_credentials:t()}
173 | {error, any()}.
174 authorize(Creds) ->
175 2 ejabberd_auth:authorize_with_check_password(?MODULE, Creds).
176
177 %% @doc When anonymous login is enabled, check the password for permanent users
178 %% before allowing access
179 -spec check_password(HostType :: mongooseim:host_type(),
180 LUser :: jid:luser(),
181 LServer :: jid:lserver(),
182 Password :: binary()) -> boolean().
183 check_password(HostType, LUser, LServer, Password) ->
184 2 check_password(HostType, LUser, LServer, Password, undefined, undefined).
185
186 check_password(HostType, LUser, LServer, _Password, _Digest, _DigestGen) ->
187 %% We refuse login for registered accounts (They cannot logged but
188 %% they however are "reserved")
189 2 case ejabberd_auth:does_stored_user_exist(
190 HostType, jid:make_noprep(LUser, LServer, <<>>)) of
191 %% If user exists in other module, reject anonymous authentication
192
:-(
true -> false;
193 %% If we are not sure whether the user exists in other module, reject anon auth
194
:-(
{error, _Error} -> false;
195 2 false -> login(HostType, LUser, LServer)
196 end.
197
198
199 -spec login(HostType :: mongooseim:host_type(), LUser :: jid:luser(),
200 LServer :: jid:lserver()) -> boolean().
201 login(HostType, LUser, LServer) ->
202 2 case is_protocol_enabled(HostType, login_anon) of
203
:-(
false -> false;
204 true ->
205 2 case does_anonymous_user_exist(HostType, LUser, LServer) of
206 %% Reject the login if an anonymous user with the same login
207 %% is already logged and if multiple login has not been enable
208 %% in the config file.
209
:-(
true -> allow_multiple_connections(HostType);
210 %% Accept login and add user to the anonymous table
211 2 false -> true
212 end
213 end.
214
215
216 %% @doc When anonymous login is enabled, check that the user is permanent before
217 %% changing its password
218 -spec set_password(HostType :: mongooseim:host_type(),
219 LUser :: jid:luser(),
220 LServer :: jid:lserver(),
221 Password :: binary()) -> ok | {error, not_allowed}.
222 set_password(HostType, LUser, LServer, _Password) ->
223
:-(
case does_anonymous_user_exist(HostType, LUser, LServer) of
224 true ->
225
:-(
ok;
226 false ->
227
:-(
{error, not_allowed}
228 end.
229
230 -spec get_registered_users(mongooseim:host_type(), jid:lserver(), list()) ->
231 [jid:simple_bare_jid()].
232 get_registered_users(_HostType, LServer, _) ->
233 12 [{U, S} || #session{us = {U, S}} <- ejabberd_sm:get_vh_session_list(LServer)].
234
235 %% @doc Return password of permanent user or false for anonymous users
236 -spec get_password(HostType :: mongooseim:host_type(),
237 LUser :: jid:luser(),
238 LServer :: jid:lserver()) -> binary() | false.
239 get_password(HostType, LUser, LServer) ->
240
:-(
case does_anonymous_user_exist(HostType, LUser, LServer) orelse login(HostType, LUser, LServer) of
241 %% We return the default value if the user is anonymous
242 true ->
243
:-(
<<>>;
244 %% We return the permanent user password otherwise
245 false ->
246
:-(
false
247 end.
248
249 %% @doc Returns true if the SASL mechanism is supportedon the server
250 %% Anonymous login can be used with a standard authentication method
251 %% (i.e. with clients that do not support SASL ANONYMOUS)
252 -spec supports_sasl_module(mongooseim:host_type(), cyrsasl:sasl_module()) -> boolean().
253 supports_sasl_module(HostType, cyrsasl_anonymous) ->
254 10 is_protocol_enabled(HostType, sasl_anon);
255 supports_sasl_module(HostType, cyrsasl_plain) ->
256 10 is_protocol_enabled(HostType, login_anon);
257 supports_sasl_module(HostType, cyrsasl_digest) ->
258
:-(
is_protocol_enabled(HostType, login_anon);
259 supports_sasl_module(HostType, Mechanism) ->
260 80 case mongoose_scram:enabled(HostType, Mechanism) of
261 true ->
262 80 is_protocol_enabled(HostType, login_anon);
263 _ ->
264
:-(
false
265 end.
266
267 %% @doc Returns true if the requested anonymous protocol is enabled
268 -spec is_protocol_enabled(mongooseim:host_type(), sasl_anon | login_anon) -> boolean().
269 is_protocol_enabled(HostType, Protocol) ->
270 102 case anonymous_protocol(HostType) of
271 102 both -> true;
272
:-(
Protocol -> true;
273
:-(
_ -> false
274 end.
275
276 %% @doc Returns the anonymous protocol to use, defaults to sasl_anon
277 -spec anonymous_protocol(mongooseim:host_type()) -> sasl_anon | login_anon | both.
278 anonymous_protocol(HostType) ->
279 102 mongoose_config:get_opt([{auth, HostType}, anonymous, protocol]).
280
281 -spec supported_features() -> [atom()].
282 45 supported_features() -> [dynamic_domains].
Line Hits Source