mirror of
https://github.com/processone/ejabberd.git
synced 2024-11-30 16:36:29 +01:00
Add password entropy check (EJAB-1326)
This commit is contained in:
parent
55bd17d6f5
commit
30366dbe98
@ -3794,6 +3794,10 @@ Options:
|
||||
from s2s leads to uncontrolled massive accounts creation by rogue users.
|
||||
\titem{\{captcha\_protected, false|true\}} \ind{options!captcha\_protected}
|
||||
Protect registrations with CAPTCHA (see section \ref{captcha}). The default is \term{false}.
|
||||
\titem{\{password\_strength, Entropy\}} \ind{options!password\_strength}
|
||||
This option sets the minimum informational entropy for passwords. The value \term{Entropy}
|
||||
is a number of bits of entropy. The recommended minimum is 32 bits.
|
||||
The default is 0, i.e. no checks are performed.
|
||||
\titem{\{welcome\_message, Message\}} \ind{options!welcomem}Set a welcome message that
|
||||
is sent to each newly registered account. The first string is the subject, and
|
||||
the second string is the message body.
|
||||
|
@ -49,7 +49,8 @@
|
||||
is_user_exists_in_other_modules/3,
|
||||
remove_user/2,
|
||||
remove_user/3,
|
||||
plain_password_required/1
|
||||
plain_password_required/1,
|
||||
entropy/1
|
||||
]).
|
||||
|
||||
-export([start/1
|
||||
@ -443,6 +444,29 @@ remove_user(User, Server, Password)
|
||||
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)
|
||||
end.
|
||||
|
||||
%%%----------------------------------------------------------------------
|
||||
%%% Internal functions
|
||||
|
@ -254,7 +254,7 @@ try_register_or_set_password(User, Server, Password, From, IQ_Rec,
|
||||
SubEl, Source, Lang, CaptchaSucceed) ->
|
||||
case {exmpp_jid:node_as_list(From), exmpp_jid:prep_domain_as_list(From)} of
|
||||
{User, Server} ->
|
||||
try_set_password(User, Server, Password, IQ_Rec, SubEl);
|
||||
try_set_password(User, Server, Password, IQ_Rec, SubEl, Lang);
|
||||
_ when CaptchaSucceed ->
|
||||
case check_from(From, Server) of
|
||||
allow ->
|
||||
@ -273,7 +273,16 @@ try_register_or_set_password(User, Server, Password, From, IQ_Rec,
|
||||
end.
|
||||
|
||||
%% @doc Try to change password and return IQ response
|
||||
try_set_password(User, Server, Password, IQ_Rec, SubEl) ->
|
||||
try_set_password(User, Server, Password, IQ_Rec, SubEl, Lang) ->
|
||||
case is_strong_password(Server, Password) of
|
||||
true ->
|
||||
try_set_password_strong(User, Server, Password, IQ_Rec, SubEl, Lang);
|
||||
false ->
|
||||
%% ErrText = "The password is too weak",
|
||||
exmpp_iq:error(IQ_Rec, 'not-acceptable')
|
||||
end.
|
||||
|
||||
try_set_password_strong(User, Server, Password, IQ_Rec, SubEl, _Lang) ->
|
||||
case ejabberd_auth:set_password(User, Server, Password) of
|
||||
ok ->
|
||||
exmpp_iq:result(IQ_Rec, SubEl);
|
||||
@ -287,20 +296,7 @@ try_set_password(User, Server, Password, IQ_Rec, SubEl) ->
|
||||
exmpp_iq:error(IQ_Rec, 'internal-server-error')
|
||||
end.
|
||||
|
||||
try_register(User, Server, Password, Source, Lang) ->
|
||||
case exmpp_stringprep:is_node(User) of
|
||||
false ->
|
||||
{error, 'bad-request'};
|
||||
_ ->
|
||||
JID = exmpp_jid:make(User,
|
||||
Server),
|
||||
Access = gen_mod:get_module_opt(Server, ?MODULE, access, all),
|
||||
case acl:match_rule(Server, Access, JID) of
|
||||
deny ->
|
||||
{error, 'forbidden'};
|
||||
allow ->
|
||||
case check_timeout(Source) of
|
||||
true ->
|
||||
try_register_strong(User, Server, Password, Source, _Lang, JID) ->
|
||||
case ejabberd_auth:try_register(User, Server, Password) of
|
||||
{atomic, ok} ->
|
||||
send_welcome_message(JID),
|
||||
@ -318,6 +314,27 @@ try_register(User, Server, Password, Source, Lang) ->
|
||||
{error, _Reason} ->
|
||||
{error, 'internal-server-error'}
|
||||
end
|
||||
end.
|
||||
|
||||
try_register(User, Server, Password, Source, Lang) ->
|
||||
case exmpp_stringprep:is_node(User) of
|
||||
false ->
|
||||
{error, 'bad-request'};
|
||||
_ ->
|
||||
JID = exmpp_jid:make(User, Server),
|
||||
Access = gen_mod:get_module_opt(Server, ?MODULE, access, all),
|
||||
case acl:match_rule(Server, Access, JID) of
|
||||
deny ->
|
||||
{error, 'forbidden'};
|
||||
allow ->
|
||||
case check_timeout(Source) of
|
||||
true ->
|
||||
case is_strong_password(Server, Password) of
|
||||
true ->
|
||||
try_register_strong(User, Server, Password, Source, Lang, JID);
|
||||
false ->
|
||||
%%ErrText = "The password is too weak",
|
||||
{error, 'not-acceptable'}
|
||||
end;
|
||||
false ->
|
||||
ErrText = "Users are not allowed to register "
|
||||
@ -492,3 +509,18 @@ process_xdata_submit(El) ->
|
||||
error
|
||||
end
|
||||
end.
|
||||
|
||||
is_strong_password(Server, Password) ->
|
||||
LServer = jlib:nameprep(Server),
|
||||
case gen_mod:get_module_opt(LServer, ?MODULE, password_strength, 0) of
|
||||
Entropy when is_number(Entropy), Entropy >= 0 ->
|
||||
if Entropy == 0 ->
|
||||
true;
|
||||
true ->
|
||||
ejabberd_auth:entropy(Password) >= Entropy
|
||||
end;
|
||||
Wrong ->
|
||||
?WARNING_MSG("Wrong value for password_strength option: ~p",
|
||||
[Wrong]),
|
||||
true
|
||||
end.
|
||||
|
Loading…
Reference in New Issue
Block a user