diff options
Diffstat (limited to 'src/ejabberd_auth.erl')
-rw-r--r-- | src/ejabberd_auth.erl | 579 |
1 files changed, 291 insertions, 288 deletions
diff --git a/src/ejabberd_auth.erl b/src/ejabberd_auth.erl index c8beba9db..e2bc89807 100644 --- a/src/ejabberd_auth.erl +++ b/src/ejabberd_auth.erl @@ -27,357 +27,360 @@ %% TODO: Use the functions in ejabberd auth to add and remove users. -module(ejabberd_auth). + -author('alexey@process-one.net'). %% External exports --export([start/0, - set_password/3, - check_password/3, - check_password/5, - check_password_with_authmodule/3, - check_password_with_authmodule/5, - try_register/3, - dirty_get_registered_users/0, - get_vh_registered_users/1, - get_vh_registered_users/2, +-export([start/0, set_password/3, check_password/3, + check_password/5, check_password_with_authmodule/3, + check_password_with_authmodule/5, try_register/3, + dirty_get_registered_users/0, get_vh_registered_users/1, + get_vh_registered_users/2, export/1, get_vh_registered_users_number/1, - get_vh_registered_users_number/2, - get_password/2, - get_password_s/2, - get_password_with_authmodule/2, - is_user_exists/2, - is_user_exists_in_other_modules/3, - remove_user/2, - remove_user/3, - plain_password_required/1, - store_type/1, - entropy/1 - ]). + get_vh_registered_users_number/2, get_password/2, + get_password_s/2, get_password_with_authmodule/2, + is_user_exists/2, is_user_exists_in_other_modules/3, + remove_user/2, remove_user/3, plain_password_required/1, + store_type/1, entropy/1]). -export([auth_modules/1]). -include("ejabberd.hrl"). -%%%---------------------------------------------------------------------- -%%% API -%%%---------------------------------------------------------------------- +-type opts() :: [{prefix, binary()} | {from, integer()} | + {to, integer()} | {limit, integer()} | + {offset, integer()}]. + +-callback start(binary()) -> any(). +-callback plain_password_required() -> boolean(). +-callback store_type() -> plain | external | scram. +-callback set_password(binary(), binary(), binary()) -> ok | {error, atom()}. +-callback remove_user(binary(), binary()) -> any(). +-callback remove_user(binary(), binary(), binary()) -> any(). +-callback is_user_exists(binary(), binary()) -> boolean() | {error, atom()}. +-callback check_password(binary(), binary(), binary()) -> boolean(). +-callback check_password(binary(), binary(), binary(), binary(), + fun((binary()) -> binary())) -> boolean(). +-callback try_register(binary(), binary(), binary()) -> {atomic, atom()} | + {error, atom()}. +-callback dirty_get_registered_users() -> [{binary(), binary()}]. +-callback get_vh_registered_users(binary()) -> [{binary(), binary()}]. +-callback get_vh_registered_users(binary(), opts()) -> [{binary(), binary()}]. +-callback get_vh_registered_users_number(binary()) -> number(). +-callback get_vh_registered_users_number(binary(), opts()) -> number(). +-callback get_password(binary(), binary()) -> false | binary(). +-callback get_password_s(binary(), binary()) -> binary(). + start() -> - lists:foreach( - fun(Host) -> - lists:foreach( - fun(M) -> - M:start(Host) - end, auth_modules(Host)) - end, ?MYHOSTS). - -%% This is only executed by ejabberd_c2s for non-SASL auth client + lists:foreach(fun (Host) -> + lists:foreach(fun (M) -> M:start(Host) end, + auth_modules(Host)) + end, + ?MYHOSTS). + plain_password_required(Server) -> - lists:any( - fun(M) -> - M:plain_password_required() - end, auth_modules(Server)). + lists:any(fun (M) -> M:plain_password_required() end, + auth_modules(Server)). store_type(Server) -> - lists:foldl( - fun(_, external) -> - external; - (M, scram) -> - case M:store_type() of - external -> - external; - _Else -> - scram - end; - (M, plain) -> - M:store_type() - end, plain, auth_modules(Server)). - -%% @doc Check if the user and password can login in server. -%% @spec (User::string(), Server::string(), Password::string()) -> -%% true | false + lists:foldl(fun (_, external) -> external; + (M, scram) -> + case M:store_type() of + external -> external; + _Else -> scram + end; + (M, plain) -> M:store_type() + end, + plain, auth_modules(Server)). + +-spec check_password(binary(), binary(), binary()) -> boolean(). + check_password(User, Server, Password) -> - case check_password_with_authmodule(User, Server, Password) of - {true, _AuthModule} -> true; - false -> false + case check_password_with_authmodule(User, Server, + Password) + of + {true, _AuthModule} -> true; + false -> false end. -%% @doc Check if the user and password can login in server. -%% @spec (User::string(), Server::string(), Password::string(), -%% Digest::string(), DigestGen::function()) -> -%% true | false -check_password(User, Server, Password, Digest, DigestGen) -> - case check_password_with_authmodule(User, Server, Password, - Digest, DigestGen) of - {true, _AuthModule} -> true; - false -> false +-spec check_password(binary(), binary(), binary(), binary(), + fun((binary()) -> binary())) -> boolean(). + +check_password(User, Server, Password, Digest, + DigestGen) -> + case check_password_with_authmodule(User, Server, + Password, Digest, DigestGen) + of + {true, _AuthModule} -> true; + false -> false end. -%% @doc Check if the user and password can login in server. -%% The user can login if at least an authentication method accepts the user -%% and the password. -%% The first authentication method that accepts the credentials is returned. -%% @spec (User::string(), Server::string(), Password::string()) -> -%% {true, AuthModule} | false -%% where -%% AuthModule = ejabberd_auth_anonymous | ejabberd_auth_external -%% | ejabberd_auth_internal | ejabberd_auth_ldap -%% | ejabberd_auth_odbc | ejabberd_auth_pam -check_password_with_authmodule(User, Server, Password) -> - check_password_loop(auth_modules(Server), [User, Server, Password]). - -check_password_with_authmodule(User, Server, Password, Digest, DigestGen) -> - check_password_loop(auth_modules(Server), [User, Server, Password, - Digest, DigestGen]). - -check_password_loop([], _Args) -> - false; +-spec check_password_with_authmodule(binary(), binary(), binary()) -> false | + {true, atom()}. + +check_password_with_authmodule(User, Server, + Password) -> + check_password_loop(auth_modules(Server), + [User, Server, Password]). + +-spec check_password_with_authmodule(binary(), binary(), binary(), binary(), + fun((binary()) -> binary())) -> false | + {true, atom()}. + +check_password_with_authmodule(User, Server, Password, + Digest, DigestGen) -> + check_password_loop(auth_modules(Server), + [User, Server, Password, Digest, DigestGen]). + +check_password_loop([], _Args) -> false; check_password_loop([AuthModule | AuthModules], Args) -> case apply(AuthModule, check_password, Args) of - true -> - {true, AuthModule}; - false -> - check_password_loop(AuthModules, Args) + true -> {true, AuthModule}; + false -> check_password_loop(AuthModules, Args) end. +-spec set_password(binary(), binary(), binary()) -> ok | + {error, atom()}. -%% @spec (User::string(), Server::string(), Password::string()) -> -%% ok | {error, ErrorType} -%% where ErrorType = empty_password | not_allowed | invalid_jid -set_password(_User, _Server, "") -> - %% We do not allow empty password +set_password(_User, _Server, <<"">>) -> {error, empty_password}; set_password(User, Server, Password) -> - lists:foldl( - fun(M, {error, _}) -> - M:set_password(User, Server, Password); - (_M, Res) -> - Res - end, {error, not_allowed}, auth_modules(Server)). - -%% @spec (User, Server, Password) -> {atomic, ok} | {atomic, exists} | {error, not_allowed} -try_register(_User, _Server, "") -> - %% We do not allow empty password - {error, not_allowed}; + lists:foldl(fun (M, {error, _}) -> + M:set_password(User, Server, Password); + (_M, Res) -> Res + end, + {error, not_allowed}, auth_modules(Server)). + +-spec try_register(binary(), binary(), binary()) -> {atomic, atom()} | + {error, atom()}. + +try_register(_User, _Server, <<"">>) -> + {error, not_allowed}; try_register(User, Server, Password) -> - case is_user_exists(User,Server) of - true -> - {atomic, exists}; - false -> - case lists:member(jlib:nameprep(Server), ?MYHOSTS) of - true -> - Res = lists:foldl( - fun(_M, {atomic, ok} = Res) -> - Res; - (M, _) -> - M:try_register(User, Server, Password) - end, {error, not_allowed}, auth_modules(Server)), - case Res of - {atomic, ok} -> - ejabberd_hooks:run(register_user, Server, - [User, Server]), - {atomic, ok}; - _ -> Res - end; - false -> - {error, not_allowed} - end + case is_user_exists(User, Server) of + true -> {atomic, exists}; + false -> + case lists:member(jlib:nameprep(Server), ?MYHOSTS) of + true -> + Res = lists:foldl(fun (_M, {atomic, ok} = Res) -> Res; + (M, _) -> + M:try_register(User, Server, Password) + end, + {error, not_allowed}, auth_modules(Server)), + case Res of + {atomic, ok} -> + ejabberd_hooks:run(register_user, Server, + [User, Server]), + {atomic, ok}; + _ -> Res + end; + false -> {error, not_allowed} + end end. -%% Registered users list do not include anonymous users logged +-spec dirty_get_registered_users() -> [{binary(), binary()}]. + dirty_get_registered_users() -> - lists:flatmap( - fun(M) -> - M:dirty_get_registered_users() - end, auth_modules()). + lists:flatmap(fun (M) -> M:dirty_get_registered_users() + end, + auth_modules()). + +-spec get_vh_registered_users(binary()) -> [{binary(), binary()}]. -%% Registered users list do not include anonymous users logged get_vh_registered_users(Server) -> - lists:flatmap( - fun(M) -> - M:get_vh_registered_users(Server) - end, auth_modules(Server)). + lists:flatmap(fun (M) -> + M:get_vh_registered_users(Server) + end, + auth_modules(Server)). + +-spec get_vh_registered_users(binary(), opts()) -> [{binary(), binary()}]. get_vh_registered_users(Server, Opts) -> - lists:flatmap( - fun(M) -> - case erlang:function_exported( - M, get_vh_registered_users, 2) of - true -> - M:get_vh_registered_users(Server, Opts); - false -> - M:get_vh_registered_users(Server) - end - end, auth_modules(Server)). + lists:flatmap(fun (M) -> + case erlang:function_exported(M, + get_vh_registered_users, + 2) + of + true -> M:get_vh_registered_users(Server, Opts); + false -> M:get_vh_registered_users(Server) + end + end, + auth_modules(Server)). get_vh_registered_users_number(Server) -> - lists:sum( - lists:map( - fun(M) -> - case erlang:function_exported( - M, get_vh_registered_users_number, 1) of - true -> - M:get_vh_registered_users_number(Server); - false -> - length(M:get_vh_registered_users(Server)) - end - end, auth_modules(Server))). + lists:sum(lists:map(fun (M) -> + case erlang:function_exported(M, + get_vh_registered_users_number, + 1) + of + true -> + M:get_vh_registered_users_number(Server); + false -> + length(M:get_vh_registered_users(Server)) + end + end, + auth_modules(Server))). + +-spec get_vh_registered_users_number(binary(), opts()) -> number(). get_vh_registered_users_number(Server, Opts) -> - lists:sum( - lists:map( - fun(M) -> - case erlang:function_exported( - M, get_vh_registered_users_number, 2) of - true -> - M:get_vh_registered_users_number(Server, Opts); - false -> - length(M:get_vh_registered_users(Server)) - end - end, auth_modules(Server))). - -%% @doc Get the password of the user. -%% @spec (User::string(), Server::string()) -> Password::string() + lists:sum(lists:map(fun (M) -> + case erlang:function_exported(M, + get_vh_registered_users_number, + 2) + of + true -> + M:get_vh_registered_users_number(Server, + Opts); + false -> + length(M:get_vh_registered_users(Server)) + end + end, + auth_modules(Server))). + +-spec get_password(binary(), binary()) -> false | binary(). + get_password(User, Server) -> - lists:foldl( - fun(M, false) -> - M:get_password(User, Server); - (_M, Password) -> - Password - end, false, auth_modules(Server)). + lists:foldl(fun (M, false) -> + M:get_password(User, Server); + (_M, Password) -> Password + end, + false, auth_modules(Server)). + +-spec get_password_s(binary(), binary()) -> binary(). get_password_s(User, Server) -> case get_password(User, Server) of - false -> - ""; - Password when is_list(Password) -> - Password; - _ -> - "" + false -> <<"">>; + Password -> Password end. -%% @doc Get the password of the user and the auth module. -%% @spec (User::string(), Server::string()) -> -%% {Password::string(), AuthModule::atom()} | {false, none} +-spec get_password_with_authmodule(binary(), binary()) -> {false | binary(), atom()}. + get_password_with_authmodule(User, Server) -> - lists:foldl( - fun(M, {false, _}) -> - {M:get_password(User, Server), M}; - (_M, {Password, AuthModule}) -> - {Password, AuthModule} - end, {false, none}, auth_modules(Server)). - -%% Returns true if the user exists in the DB or if an anonymous user is logged -%% under the given name + lists:foldl(fun (M, {false, _}) -> + {M:get_password(User, Server), M}; + (_M, {Password, AuthModule}) -> {Password, AuthModule} + end, + {false, none}, auth_modules(Server)). + +-spec is_user_exists(binary(), binary()) -> boolean(). + is_user_exists(User, Server) -> - lists:any( - fun(M) -> - case M:is_user_exists(User, Server) of - {error, Error} -> - ?ERROR_MSG("The authentication module ~p returned an " - "error~nwhen checking user ~p in server ~p~n" - "Error message: ~p", - [M, User, Server, Error]), - false; - Else -> - Else - end - end, auth_modules(Server)). - -%% Check if the user exists in all authentications module except the module -%% passed as parameter -%% @spec (Module::atom(), User, Server) -> true | false | maybe + lists:any(fun (M) -> + case M:is_user_exists(User, Server) of + {error, Error} -> + ?ERROR_MSG("The authentication module ~p returned " + "an error~nwhen checking user ~p in server " + "~p~nError message: ~p", + [M, User, Server, Error]), + false; + Else -> Else + end + end, + auth_modules(Server)). + +-spec is_user_exists_in_other_modules(atom(), binary(), binary()) -> boolean() | maybe. + is_user_exists_in_other_modules(Module, User, Server) -> - is_user_exists_in_other_modules_loop( - auth_modules(Server)--[Module], - User, Server). -is_user_exists_in_other_modules_loop([], _User, _Server) -> + is_user_exists_in_other_modules_loop(auth_modules(Server) + -- [Module], + User, Server). + +is_user_exists_in_other_modules_loop([], _User, + _Server) -> false; -is_user_exists_in_other_modules_loop([AuthModule|AuthModules], User, Server) -> +is_user_exists_in_other_modules_loop([AuthModule + | AuthModules], + User, Server) -> case AuthModule:is_user_exists(User, Server) of - true -> - true; - false -> - is_user_exists_in_other_modules_loop(AuthModules, User, Server); - {error, Error} -> - ?DEBUG("The authentication module ~p returned an error~nwhen " - "checking user ~p in server ~p~nError message: ~p", - [AuthModule, User, Server, Error]), - maybe + true -> true; + false -> + is_user_exists_in_other_modules_loop(AuthModules, User, + Server); + {error, Error} -> + ?DEBUG("The authentication module ~p returned " + "an error~nwhen checking user ~p in server " + "~p~nError message: ~p", + [AuthModule, User, Server, Error]), + maybe end. +-spec remove_user(binary(), binary()) -> ok. -%% @spec (User, Server) -> ok -%% @doc Remove user. -%% Note: it may return ok even if there was some problem removing the user. remove_user(User, Server) -> - lists:foreach( - fun(M) -> - M:remove_user(User, Server) - end, auth_modules(Server)), - ejabberd_hooks:run(remove_user, jlib:nameprep(Server), [User, Server]), + lists:foreach(fun (M) -> M:remove_user(User, Server) + end, + auth_modules(Server)), + ejabberd_hooks:run(remove_user, jlib:nameprep(Server), + [User, Server]), ok. -%% @spec (User, Server, Password) -> ok | not_exists | not_allowed | bad_request | error -%% @doc Try to remove user if the provided password is correct. -%% The removal is attempted in each auth method provided: -%% when one returns 'ok' the loop stops; -%% if no method returns 'ok' then it returns the error message indicated by the last method attempted. +-spec remove_user(binary(), binary(), binary()) -> any(). + remove_user(User, Server, Password) -> - R = lists:foldl( - fun(_M, ok = Res) -> - Res; - (M, _) -> - M:remove_user(User, Server, Password) - end, error, auth_modules(Server)), + R = lists:foldl(fun (_M, ok = Res) -> Res; + (M, _) -> M:remove_user(User, Server, Password) + end, + error, auth_modules(Server)), case R of - ok -> ejabberd_hooks:run(remove_user, jlib:nameprep(Server), [User, Server]); - _ -> none + ok -> + ejabberd_hooks:run(remove_user, jlib:nameprep(Server), + [User, Server]); + _ -> none end, R. -%% @spec (IOList) -> non_negative_float() %% @doc Calculate informational entropy. -entropy(IOList) -> - case binary_to_list(iolist_to_binary(IOList)) of - "" -> - 0.0; - S -> - Set = lists:foldl( - fun(C, [Digit, Printable, LowLetter, HiLetter, Other]) -> - if C >= $a, C =< $z -> - [Digit, Printable, 26, HiLetter, Other]; - C >= $0, C =< $9 -> - [9, Printable, LowLetter, HiLetter, Other]; - C >= $A, C =< $Z -> - [Digit, Printable, LowLetter, 26, Other]; - C >= 16#21, C =< 16#7e -> - [Digit, 33, LowLetter, HiLetter, Other]; - true -> - [Digit, Printable, LowLetter, HiLetter, 128] - end - end, [0, 0, 0, 0, 0], S), - length(S) * math:log(lists:sum(Set))/math:log(2) +entropy(B) -> + case binary_to_list(B) of + "" -> 0.0; + S -> + Set = lists:foldl(fun (C, + [Digit, Printable, LowLetter, HiLetter, + Other]) -> + if C >= $a, C =< $z -> + [Digit, Printable, 26, HiLetter, + Other]; + C >= $0, C =< $9 -> + [9, Printable, LowLetter, HiLetter, + Other]; + C >= $A, C =< $Z -> + [Digit, Printable, LowLetter, 26, + Other]; + C >= 33, C =< 126 -> + [Digit, 33, LowLetter, HiLetter, + Other]; + true -> + [Digit, Printable, LowLetter, + HiLetter, 128] + end + end, + [0, 0, 0, 0, 0], S), + length(S) * math:log(lists:sum(Set)) / math:log(2) end. -%%%---------------------------------------------------------------------- -%%% Internal functions -%%%---------------------------------------------------------------------- -%% Return the lists of all the auth modules actually used in the -%% configuration auth_modules() -> - lists:usort( - lists:flatmap( - fun(Server) -> - auth_modules(Server) - end, ?MYHOSTS)). + lists:usort(lists:flatmap(fun (Server) -> + auth_modules(Server) + end, + ?MYHOSTS)). + +-spec auth_modules(binary()) -> [atom()]. -%% Return the list of authenticated modules for a given host auth_modules(Server) -> LServer = jlib:nameprep(Server), - Method = ejabberd_config:get_local_option({auth_method, LServer}), - Methods = if - Method == undefined -> []; - is_list(Method) -> Method; - is_atom(Method) -> [Method] - end, - [list_to_atom("ejabberd_auth_" ++ atom_to_list(M)) || M <- Methods]. + Methods = ejabberd_config:get_local_option( + {auth_method, LServer}, + fun(V) when is_list(V) -> + true = lists:all(fun is_atom/1, V), + V; + (V) when is_atom(V) -> + [V] + end, []), + [jlib:binary_to_atom(<<"ejabberd_auth_", + (jlib:atom_to_binary(M))/binary>>) + || M <- Methods]. + +export(Server) -> + ejabberd_auth_internal:export(Server). |