mirror of
https://github.com/processone/ejabberd.git
synced 2024-11-24 16:23:40 +01:00
New ban commands use private storage to keep ban information (#4201)
This commit is contained in:
parent
ce57e02769
commit
44bafa478e
@ -49,7 +49,8 @@
|
|||||||
|
|
||||||
% Accounts
|
% Accounts
|
||||||
set_password/3, check_password_hash/4, delete_old_users/1,
|
set_password/3, check_password_hash/4, delete_old_users/1,
|
||||||
delete_old_users_vhost/2, ban_account/3, check_password/3,
|
delete_old_users_vhost/2, check_password/3,
|
||||||
|
ban_account/3, ban_account_v2/3, get_ban_details/2, unban_account/2,
|
||||||
|
|
||||||
% vCard
|
% vCard
|
||||||
set_nickname/3, get_vcard/3,
|
set_nickname/3, get_vcard/3,
|
||||||
@ -85,6 +86,7 @@
|
|||||||
-include("mod_roster.hrl").
|
-include("mod_roster.hrl").
|
||||||
-include("mod_privacy.hrl").
|
-include("mod_privacy.hrl").
|
||||||
-include("ejabberd_sm.hrl").
|
-include("ejabberd_sm.hrl").
|
||||||
|
-include_lib("xmpp/include/scram.hrl").
|
||||||
-include_lib("xmpp/include/xmpp.hrl").
|
-include_lib("xmpp/include/xmpp.hrl").
|
||||||
|
|
||||||
%%%
|
%%%
|
||||||
@ -236,8 +238,10 @@ get_commands_spec() ->
|
|||||||
"New password for user"],
|
"New password for user"],
|
||||||
result = {res, rescode},
|
result = {res, rescode},
|
||||||
result_example = ok},
|
result_example = ok},
|
||||||
|
|
||||||
#ejabberd_commands{name = ban_account, tags = [accounts],
|
#ejabberd_commands{name = ban_account, tags = [accounts],
|
||||||
desc = "Ban an account: kick sessions and set random password",
|
desc = "Ban an account: kick sessions and set random password",
|
||||||
|
longdesc = "This simply sets a random password.",
|
||||||
module = ?MODULE, function = ban_account,
|
module = ?MODULE, function = ban_account,
|
||||||
args = [{user, binary}, {host, binary}, {reason, binary}],
|
args = [{user, binary}, {host, binary}, {reason, binary}],
|
||||||
args_example = [<<"attacker">>, <<"myserver.com">>, <<"Spaming other users">>],
|
args_example = [<<"attacker">>, <<"myserver.com">>, <<"Spaming other users">>],
|
||||||
@ -245,6 +249,53 @@ get_commands_spec() ->
|
|||||||
"Reason for banning user"],
|
"Reason for banning user"],
|
||||||
result = {res, rescode},
|
result = {res, rescode},
|
||||||
result_example = ok},
|
result_example = ok},
|
||||||
|
#ejabberd_commands{name = ban_account, tags = [accounts],
|
||||||
|
desc = "Ban an account",
|
||||||
|
longdesc = "This command kicks the account sessions, "
|
||||||
|
"sets a random password, and stores ban details in the "
|
||||||
|
"account private storage. "
|
||||||
|
"This command requires mod_private to be enabled. "
|
||||||
|
"Check also _`get_ban_details`_ API "
|
||||||
|
"and `_unban_account`_ API.",
|
||||||
|
module = ?MODULE, function = ban_account_v2,
|
||||||
|
version = 2,
|
||||||
|
note = "improved in 24.xx",
|
||||||
|
args = [{user, binary}, {host, binary}, {reason, binary}],
|
||||||
|
args_example = [<<"attacker">>, <<"myserver.com">>, <<"Spaming other users">>],
|
||||||
|
args_desc = ["User name to ban", "Server name",
|
||||||
|
"Reason for banning user"],
|
||||||
|
result = {res, rescode},
|
||||||
|
result_example = ok},
|
||||||
|
#ejabberd_commands{name = get_ban_details, tags = [accounts],
|
||||||
|
desc = "Get ban details about an account",
|
||||||
|
longdesc = "Check _`ban_account`_ API.",
|
||||||
|
module = ?MODULE, function = get_ban_details,
|
||||||
|
version = 2,
|
||||||
|
note = "added in 24.xx",
|
||||||
|
args = [{user, binary}, {host, binary}],
|
||||||
|
args_example = [<<"attacker">>, <<"myserver.com">>],
|
||||||
|
args_desc = ["User name to unban", "Server name"],
|
||||||
|
result = {ban_details, {list,
|
||||||
|
{detail, {tuple, [{name, string},
|
||||||
|
{value, string}
|
||||||
|
]}}
|
||||||
|
}},
|
||||||
|
result_example = [{"reason", "Spamming other users"},
|
||||||
|
{"bandate", "2024-04-22T09:16:47.975312Z"},
|
||||||
|
{"lastdate", "2024-04-22T08:39:12Z"},
|
||||||
|
{"lastreason", "Connection reset by peer"}]},
|
||||||
|
#ejabberd_commands{name = unban_account, tags = [accounts],
|
||||||
|
desc = "Revert the ban from an account: set back the old password",
|
||||||
|
longdesc = "Check _`ban_account`_ API.",
|
||||||
|
module = ?MODULE, function = unban_account,
|
||||||
|
version = 2,
|
||||||
|
note = "added in 24.xx",
|
||||||
|
args = [{user, binary}, {host, binary}],
|
||||||
|
args_example = [<<"gooduser">>, <<"myserver.com">>],
|
||||||
|
args_desc = ["User name to unban", "Server name"],
|
||||||
|
result = {res, rescode},
|
||||||
|
result_example = ok},
|
||||||
|
|
||||||
#ejabberd_commands{name = num_resources, tags = [session],
|
#ejabberd_commands{name = num_resources, tags = [session],
|
||||||
desc = "Get the number of resources of a user",
|
desc = "Get the number of resources of a user",
|
||||||
module = ?MODULE, function = num_resources,
|
module = ?MODULE, function = num_resources,
|
||||||
@ -905,7 +956,7 @@ set_password(User, Host, Password) ->
|
|||||||
check_password(User, Host, Password) ->
|
check_password(User, Host, Password) ->
|
||||||
ejabberd_auth:check_password(User, <<>>, Host, Password).
|
ejabberd_auth:check_password(User, <<>>, Host, Password).
|
||||||
|
|
||||||
%% Copied some code from ejabberd_commands.erl
|
%% Copied some code from ejabberd_commands.erln
|
||||||
check_password_hash(User, Host, PasswordHash, HashMethod) ->
|
check_password_hash(User, Host, PasswordHash, HashMethod) ->
|
||||||
AccountPass = ejabberd_auth:get_password_s(User, Host),
|
AccountPass = ejabberd_auth:get_password_s(User, Host),
|
||||||
Methods = lists:map(fun(A) -> atom_to_binary(A, latin1) end,
|
Methods = lists:map(fun(A) -> atom_to_binary(A, latin1) end,
|
||||||
@ -978,7 +1029,7 @@ delete_or_not(LUser, LServer, TimeStamp_oldest) ->
|
|||||||
end.
|
end.
|
||||||
|
|
||||||
%%
|
%%
|
||||||
%% Ban account
|
%% Ban account v0
|
||||||
|
|
||||||
ban_account(User, Host, ReasonText) ->
|
ban_account(User, Host, ReasonText) ->
|
||||||
Reason = prepare_reason(ReasonText),
|
Reason = prepare_reason(ReasonText),
|
||||||
@ -1014,6 +1065,119 @@ prepare_reason([Reason]) ->
|
|||||||
prepare_reason(Reason) when is_binary(Reason) ->
|
prepare_reason(Reason) when is_binary(Reason) ->
|
||||||
Reason.
|
Reason.
|
||||||
|
|
||||||
|
%%
|
||||||
|
%% Ban account v2
|
||||||
|
|
||||||
|
ban_account_v2(User, Host, ReasonText) ->
|
||||||
|
case is_banned(User, Host) of
|
||||||
|
true ->
|
||||||
|
account_was_already_banned;
|
||||||
|
false ->
|
||||||
|
ban_account_v2_b(User, Host, ReasonText)
|
||||||
|
end.
|
||||||
|
|
||||||
|
ban_account_v2_b(User, Host, ReasonText) ->
|
||||||
|
Reason = prepare_reason(ReasonText),
|
||||||
|
Pass = ejabberd_auth:get_password_s(User, Host),
|
||||||
|
Last = get_last(User, Host),
|
||||||
|
BanDate = xmpp_util:encode_timestamp(erlang:timestamp()),
|
||||||
|
BanPrivateXml = build_ban_xmlel(Reason, Pass, Last, BanDate),
|
||||||
|
ok = private_set2(User, Host, BanPrivateXml),
|
||||||
|
ok = set_random_password_v2(User, Host),
|
||||||
|
kick_sessions(User, Host, Reason),
|
||||||
|
ok.
|
||||||
|
|
||||||
|
set_random_password_v2(User, Server) ->
|
||||||
|
NewPass = p1_rand:get_string(),
|
||||||
|
ok = ejabberd_auth:set_password(User, Server, NewPass).
|
||||||
|
|
||||||
|
build_ban_xmlel(Reason, Pass, {LastDate, LastReason}, BanDate) ->
|
||||||
|
PassEls = build_pass_els(Pass),
|
||||||
|
#xmlel{name = <<"banned">>,
|
||||||
|
attrs = [{<<"xmlns">>, <<"ejabberd:banned">>}],
|
||||||
|
children = [#xmlel{name = <<"reason">>, attrs = [], children = [{xmlcdata, Reason}]},
|
||||||
|
#xmlel{name = <<"password">>, attrs = [], children = PassEls},
|
||||||
|
#xmlel{name = <<"lastdate">>, attrs = [], children = [{xmlcdata, LastDate}]},
|
||||||
|
#xmlel{name = <<"lastreason">>, attrs = [], children = [{xmlcdata, LastReason}]},
|
||||||
|
#xmlel{name = <<"bandate">>, attrs = [], children = [{xmlcdata, BanDate}]}
|
||||||
|
]}.
|
||||||
|
|
||||||
|
build_pass_els(Pass) when is_binary(Pass) ->
|
||||||
|
[{xmlcdata, Pass}];
|
||||||
|
build_pass_els(#scram{storedkey = StoredKey,
|
||||||
|
serverkey = ServerKey,
|
||||||
|
salt = Salt,
|
||||||
|
hash = Hash,
|
||||||
|
iterationcount = IterationCount}) ->
|
||||||
|
[#xmlel{name = <<"storedkey">>, attrs = [], children = [{xmlcdata, StoredKey}]},
|
||||||
|
#xmlel{name = <<"serverkey">>, attrs = [], children = [{xmlcdata, ServerKey}]},
|
||||||
|
#xmlel{name = <<"salt">>, attrs = [], children = [{xmlcdata, Salt}]},
|
||||||
|
#xmlel{name = <<"hash">>, attrs = [], children = [{xmlcdata, misc:atom_to_binary(Hash)}]},
|
||||||
|
#xmlel{name = <<"iterationcount">>, attrs = [], children = [{xmlcdata, integer_to_binary(IterationCount)}]}
|
||||||
|
].
|
||||||
|
|
||||||
|
%%
|
||||||
|
%% Get ban details
|
||||||
|
|
||||||
|
get_ban_details(User, Host) ->
|
||||||
|
[El] = private_get2(User, Host, <<"banned">>, <<"ejabberd:banned">>),
|
||||||
|
Reason = fxml:get_subtag_cdata(El, <<"reason">>),
|
||||||
|
LastDate = fxml:get_subtag_cdata(El, <<"lastdate">>),
|
||||||
|
LastReason = fxml:get_subtag_cdata(El, <<"lastreason">>),
|
||||||
|
BanDate = fxml:get_subtag_cdata(El, <<"bandate">>),
|
||||||
|
[{"reason", Reason},
|
||||||
|
{"bandate", BanDate},
|
||||||
|
{"lastdate", LastDate},
|
||||||
|
{"lastreason", LastReason}
|
||||||
|
].
|
||||||
|
|
||||||
|
is_banned(User, Host) ->
|
||||||
|
case lists:keyfind("bandate", 1, get_ban_details(User, Host)) of
|
||||||
|
{_, BanDate} when BanDate /= <<>> ->
|
||||||
|
true;
|
||||||
|
_ ->
|
||||||
|
false
|
||||||
|
end.
|
||||||
|
|
||||||
|
%%
|
||||||
|
%% Unban account
|
||||||
|
|
||||||
|
unban_account(User, Host) ->
|
||||||
|
case is_banned(User, Host) of
|
||||||
|
false ->
|
||||||
|
account_was_not_banned;
|
||||||
|
true ->
|
||||||
|
unban_account2(User, Host)
|
||||||
|
end.
|
||||||
|
|
||||||
|
unban_account2(User, Host) ->
|
||||||
|
OldPass = get_oldpass(User, Host),
|
||||||
|
ok = ejabberd_auth:set_password(User, Host, OldPass),
|
||||||
|
UnBanPrivateXml = build_unban_xmlel(),
|
||||||
|
private_set2(User, Host, UnBanPrivateXml).
|
||||||
|
|
||||||
|
get_oldpass(User, Host) ->
|
||||||
|
[El] = private_get2(User, Host, <<"banned">>, <<"ejabberd:banned">>),
|
||||||
|
Pass = fxml:get_subtag(El, <<"password">>),
|
||||||
|
get_pass(Pass).
|
||||||
|
|
||||||
|
get_pass(#xmlel{children = [{xmlcdata, Pass}]}) ->
|
||||||
|
Pass;
|
||||||
|
get_pass(#xmlel{children = ScramEls} = Pass) when is_list(ScramEls) ->
|
||||||
|
StoredKey = fxml:get_subtag_cdata(Pass, <<"storedkey">>),
|
||||||
|
ServerKey = fxml:get_subtag_cdata(Pass, <<"serverkey">>),
|
||||||
|
Salt = fxml:get_subtag_cdata(Pass, <<"salt">>),
|
||||||
|
Hash = fxml:get_subtag_cdata(Pass, <<"hash">>),
|
||||||
|
IterationCount = fxml:get_subtag_cdata(Pass, <<"iterationcount">>),
|
||||||
|
#scram{storedkey = StoredKey,
|
||||||
|
serverkey = ServerKey,
|
||||||
|
salt = Salt,
|
||||||
|
hash = binary_to_existing_atom(Hash, latin1),
|
||||||
|
iterationcount = binary_to_integer(IterationCount)}.
|
||||||
|
|
||||||
|
build_unban_xmlel() ->
|
||||||
|
#xmlel{name = <<"banned">>, attrs = [{<<"xmlns">>, <<"ejabberd:banned">>}]}.
|
||||||
|
|
||||||
%%%
|
%%%
|
||||||
%%% Sessions
|
%%% Sessions
|
||||||
%%%
|
%%%
|
||||||
@ -1515,11 +1679,14 @@ set_last(User, Server, Timestamp, Status) ->
|
|||||||
%% <aa xmlns='bb'>Cluth</aa>
|
%% <aa xmlns='bb'>Cluth</aa>
|
||||||
|
|
||||||
private_get(Username, Host, Element, Ns) ->
|
private_get(Username, Host, Element, Ns) ->
|
||||||
ElementXml = #xmlel{name = Element, attrs = [{<<"xmlns">>, Ns}]},
|
Els = private_get2(Username, Host, Element, Ns),
|
||||||
Els = mod_private:get_data(jid:nodeprep(Username), jid:nameprep(Host),
|
|
||||||
[{Ns, ElementXml}]),
|
|
||||||
binary_to_list(fxml:element_to_binary(xmpp:encode(#private{sub_els = Els}))).
|
binary_to_list(fxml:element_to_binary(xmpp:encode(#private{sub_els = Els}))).
|
||||||
|
|
||||||
|
private_get2(Username, Host, Element, Ns) ->
|
||||||
|
ElementXml = #xmlel{name = Element, attrs = [{<<"xmlns">>, Ns}]},
|
||||||
|
mod_private:get_data(jid:nodeprep(Username), jid:nameprep(Host),
|
||||||
|
[{Ns, ElementXml}]).
|
||||||
|
|
||||||
private_set(Username, Host, ElementString) ->
|
private_set(Username, Host, ElementString) ->
|
||||||
case fxml_stream:parse_element(ElementString) of
|
case fxml_stream:parse_element(ElementString) of
|
||||||
{error, Error} ->
|
{error, Error} ->
|
||||||
|
Loading…
Reference in New Issue
Block a user