./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/5,
33 unregister_connection/5,
34 session_cleanup/5
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([register_connection/5, session_cleanup/5, unregister_connection/5,
55 login/3]).
56
57 -include("mongoose.hrl").
58 -include("jlib.hrl").
59 -include("session.hrl").
60 -include("mongoose_config_spec.hrl").
61
62 -record(anonymous, {us :: jid:simple_bare_jid(),
63 sid :: ejabberd_sm:sid()
64 }).
65
66 %% @doc Create the anonymous table if at least one host type has anonymous
67 %% features enabled. Register to login / logout events
68 -spec start(mongooseim:host_type()) -> ok.
69 start(HostType) ->
70 %% TODO: Check cluster mode
71 63 mnesia:create_table(anonymous, [{ram_copies, [node()]},
72 {type, bag},
73 {attributes, record_info(fields, anonymous)}]),
74 63 mnesia:add_table_copy(anonymous, node(), ram_copies),
75 %% The hooks are needed to add / remove users from the anonymous tables
76 63 ejabberd_hooks:add(sm_register_connection_hook, HostType, ?MODULE, register_connection, 100),
77 63 ejabberd_hooks:add(sm_remove_connection_hook, HostType, ?MODULE, unregister_connection, 100),
78 63 ejabberd_hooks:add(session_cleanup, HostType, ?MODULE, session_cleanup, 50),
79 63 ok.
80
81 -spec stop(mongooseim:host_type()) -> ok.
82 stop(HostType) ->
83
:-(
ejabberd_hooks:delete(sm_register_connection_hook, HostType, ?MODULE, register_connection, 100),
84
:-(
ejabberd_hooks:delete(sm_remove_connection_hook, HostType, ?MODULE, unregister_connection, 100),
85
:-(
ejabberd_hooks:delete(session_cleanup, HostType, ?MODULE, session_cleanup, 50),
86
:-(
ok.
87
88 -spec config_spec() -> mongoose_config_spec:config_section().
89 config_spec() ->
90 166 #section{
91 items = #{<<"allow_multiple_connections">> => #option{type = boolean},
92 <<"protocol">> => #option{type = atom,
93 validate = {enum, [sasl_anon, login_anon, both]}}
94 },
95 defaults = #{<<"allow_multiple_connections">> => false,
96 <<"protocol">> => sasl_anon}
97 }.
98
99 %% @doc Return true if multiple connections have been allowed in the config file
100 %% defaults to false
101 -spec allow_multiple_connections(mongooseim:host_type()) -> boolean().
102 allow_multiple_connections(HostType) ->
103
:-(
mongoose_config:get_opt([{auth, HostType}, anonymous, allow_multiple_connections]).
104
105 does_user_exist(_, LUser, LServer) ->
106 5 does_anonymous_user_exist(LUser, LServer).
107
108 %% @doc Check if user exist in the anonymous database
109 -spec does_anonymous_user_exist(LUser :: jid:luser(),
110 LServer :: jid:lserver()) -> boolean().
111 does_anonymous_user_exist(LUser, LServer) ->
112 7 US = {LUser, LServer},
113 7 case catch mnesia:dirty_read({anonymous, US}) of
114 [] ->
115 3 false;
116 [_H|_T] ->
117 4 true
118 end.
119
120
121 %% @doc Remove connection from Mnesia tables
122 -spec remove_connection(SID :: ejabberd_sm:sid(),
123 LUser :: jid:luser(),
124 LServer :: jid:lserver()
125 ) -> {atomic|aborted|error, _}.
126 remove_connection(SID, LUser, LServer) ->
127 2 US = {LUser, LServer},
128 2 F = fun() ->
129 2 mnesia:delete_object({anonymous, US, SID})
130 end,
131 2 mnesia:transaction(F).
132
133
134 %% @doc Register connection
135 -spec register_connection(Acc,
136 HostType :: mongooseim:host_type(),
137 SID :: ejabberd_sm:sid(),
138 JID :: jid:jid(),
139 Info :: ejabberd_sm:info()) -> Acc when Acc :: any().
140 register_connection(Acc, HostType, SID, #jid{luser = LUser, lserver = LServer},
141 #{auth_module := cyrsasl_anonymous}) ->
142 2 mongoose_hooks:register_user(HostType, LServer, LUser),
143 2 US = {LUser, LServer},
144 2 mnesia:sync_dirty(fun() -> mnesia:write(#anonymous{us = US, sid = SID}) end),
145 2 Acc;
146 register_connection(Acc, _HostType, _SID, _JID, _Info) ->
147
:-(
Acc.
148
149 %% @doc Remove an anonymous user from the anonymous users table
150 -spec unregister_connection(Acc, SID :: ejabberd_sm:sid(), JID :: jid:jid(),
151 any(), ejabberd_sm:close_reason()) -> Acc
152 when Acc :: mongoose_acc:t().
153 unregister_connection(Acc, SID, #jid{luser = LUser, lserver = LServer}, _, _) ->
154 2 purge_hook(does_anonymous_user_exist(LUser, LServer),
155 mongoose_acc:host_type(Acc),
156 LUser, LServer),
157 2 remove_connection(SID, LUser, LServer),
158 2 Acc.
159
160
161 %% @doc Launch the hook to purge user data only for anonymous users.
162 -spec purge_hook(boolean(), mongooseim:host_type(), jid:luser(), jid:lserver()) -> 'ok'.
163 purge_hook(false, _HostType, _LUser, _LServer) ->
164
:-(
ok;
165 purge_hook(true, HostType, LUser, LServer) ->
166 2 Acc = mongoose_acc:new(#{ location => ?LOCATION,
167 host_type => HostType,
168 lserver => LServer,
169 element => undefined }),
170 2 mongoose_hooks:anonymous_purge_hook(LServer, Acc, LUser).
171
172 -spec session_cleanup(Acc :: map(), LUser :: jid:luser(),
173 LServer :: jid:lserver(),
174 LResource :: jid:lresource(),
175 SID :: ejabberd_sm:sid()) -> any().
176 session_cleanup(Acc, LUser, LServer, _LResource, SID) ->
177
:-(
remove_connection(SID, LUser, LServer),
178
:-(
Acc.
179
180 %% ---------------------------------
181 %% Specific anonymous auth functions
182 %% ---------------------------------
183
184 -spec authorize(mongoose_credentials:t()) -> {ok, mongoose_credentials:t()}
185 | {error, any()}.
186 authorize(Creds) ->
187
:-(
ejabberd_auth:authorize_with_check_password(?MODULE, Creds).
188
189 %% @doc When anonymous login is enabled, check the password for permanent users
190 %% before allowing access
191 -spec check_password(HostType :: mongooseim:host_type(),
192 LUser :: jid:luser(),
193 LServer :: jid:lserver(),
194 Password :: binary()) -> boolean().
195 check_password(HostType, LUser, LServer, Password) ->
196
:-(
check_password(HostType, LUser, LServer, Password, undefined, undefined).
197
198 check_password(HostType, LUser, LServer, _Password, _Digest, _DigestGen) ->
199 %% We refuse login for registered accounts (They cannot logged but
200 %% they however are "reserved")
201
:-(
case ejabberd_auth:does_stored_user_exist(
202 HostType, jid:make_noprep(LUser, LServer, <<>>)) of
203 %% If user exists in other module, reject anonymous authentication
204
:-(
true -> false;
205 %% If we are not sure whether the user exists in other module, reject anon auth
206
:-(
{error, _Error} -> false;
207
:-(
false -> login(HostType, LUser, LServer)
208 end.
209
210
211 -spec login(HostType :: mongoooseim:host_type(), LUser :: jid:luser(),
212 LServer :: jid:lserver()) -> boolean().
213 login(HostType, LUser, LServer) ->
214
:-(
case is_protocol_enabled(HostType, login_anon) of
215
:-(
false -> false;
216 true ->
217
:-(
case does_anonymous_user_exist(LUser, LServer) of
218 %% Reject the login if an anonymous user with the same login
219 %% is already logged and if multiple login has not been enable
220 %% in the config file.
221
:-(
true -> allow_multiple_connections(HostType);
222 %% Accept login and add user to the anonymous table
223
:-(
false -> true
224 end
225 end.
226
227
228 %% @doc When anonymous login is enabled, check that the user is permanent before
229 %% changing its password
230 -spec set_password(HostType :: mongooseim:host_type(),
231 LUser :: jid:luser(),
232 LServer :: jid:lserver(),
233 Password :: binary()) -> ok | {error, not_allowed}.
234 set_password(_HostType, LUser, LServer, _Password) ->
235
:-(
case does_anonymous_user_exist(LUser, LServer) of
236 true ->
237
:-(
ok;
238 false ->
239
:-(
{error, not_allowed}
240 end.
241
242 -spec get_registered_users(mongooseim:host_type(), jid:lserver(), list()) ->
243 [jid:simple_bare_jid()].
244 get_registered_users(_HostType, LServer, _) ->
245 5 [{U, S} || #session{us = {U, S}} <- ejabberd_sm:get_vh_session_list(LServer)].
246
247 %% @doc Return password of permanent user or false for anonymous users
248 -spec get_password(HostType :: mongooseim:host_type(),
249 LUser :: jid:luser(),
250 LServer :: jid:lserver()) -> binary() | false.
251 get_password(HostType, LUser, LServer) ->
252
:-(
case does_anonymous_user_exist(LUser, LServer) orelse login(HostType, LUser, LServer) of
253 %% We return the default value if the user is anonymous
254 true ->
255
:-(
<<>>;
256 %% We return the permanent user password otherwise
257 false ->
258
:-(
false
259 end.
260
261 %% @doc Returns true if the SASL mechanism is supportedon the server
262 %% Anonymous login can be used with a standard authentication method
263 %% (i.e. with clients that do not support SASL ANONYMOUS)
264 -spec supports_sasl_module(mongooseim:host_type(), cyrsasl:sasl_module()) -> boolean().
265 supports_sasl_module(HostType, cyrsasl_anonymous) ->
266 6 is_protocol_enabled(HostType, sasl_anon);
267 supports_sasl_module(HostType, cyrsasl_plain) ->
268 4 is_protocol_enabled(HostType, login_anon);
269 supports_sasl_module(HostType, cyrsasl_digest) ->
270
:-(
is_protocol_enabled(HostType, login_anon);
271 supports_sasl_module(HostType, Mechanism) ->
272 40 case mongoose_scram:enabled(HostType, Mechanism) of
273 true ->
274 40 is_protocol_enabled(HostType, login_anon);
275 _ ->
276
:-(
false
277 end.
278
279 %% @doc Returns true if the requested anonymous protocol is enabled
280 -spec is_protocol_enabled(mongooseim:host_type(), sasl_anon | login_anon) -> boolean().
281 is_protocol_enabled(HostType, Protocol) ->
282 50 case anonymous_protocol(HostType) of
283 50 both -> true;
284
:-(
Protocol -> true;
285
:-(
_ -> false
286 end.
287
288 %% @doc Returns the anonymous protocol to use, defaults to sasl_anon
289 -spec anonymous_protocol(mongooseim:host_type()) -> sasl_anon | login_anon | both.
290 anonymous_protocol(HostType) ->
291 50 mongoose_config:get_opt([{auth, HostType}, anonymous, protocol]).
292
293 -spec supported_features() -> [atom()].
294 32 supported_features() -> [dynamic_domains].
Line Hits Source