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