Update export/import of scram password to XEP-0227 1.1 (#3676)

This commit is contained in:
Badlop 2021-09-22 12:53:18 +02:00
parent ceeba3eea1
commit af4b49f720
1 changed files with 71 additions and 35 deletions

View File

@ -24,17 +24,15 @@
%%%----------------------------------------------------------------------
%%% Not implemented:
%%% - PEP nodes export/import
%%% - message archives export/import
%%% - write mod_piefxis with ejabberdctl commands
%%% - Export from mod_offline_sql.erl
%%% - Export from mod_private_sql.erl
%%% - XEP-227: 6. Security Considerations
%%% - Other schemas of XInclude are not tested, and may not be imported correctly.
%%% - If a host has many users, split that host in XML files with 50 users each.
%%%% Headers
-module(ejabberd_piefxis).
-protocol({xep, 227, '1.0'}).
-protocol({xep, 227, '1.1'}).
-export([import_file/1, export_server/1, export_host/2]).
@ -166,33 +164,66 @@ export_users([], _Server, _Fd) ->
export_user(User, Server, Fd) ->
Password = ejabberd_auth:get_password_s(User, Server),
LServer = jid:nameprep(Server),
Pass = case ejabberd_auth:password_format(LServer) of
scram -> format_scram_password(Password);
_ -> Password
{PassPlain, PassScram} = case ejabberd_auth:password_format(LServer) of
scram -> {[], [format_scram_password(Password)]};
_ -> {[{<<"password">>, Password}], []}
end,
Els = get_offline(User, Server) ++
Els =
PassScram ++
get_offline(User, Server) ++
get_vcard(User, Server) ++
get_privacy(User, Server) ++
get_roster(User, Server) ++
get_private(User, Server),
print(Fd, fxml:element_to_binary(
#xmlel{name = <<"user">>,
attrs = [{<<"name">>, User},
{<<"password">>, Pass}],
attrs = [{<<"name">>, User} | PassPlain],
children = Els})).
format_scram_password(#scram{hash = Hash, storedkey = StoredKey, serverkey = ServerKey,
salt = Salt, iterationcount = IterationCount}) ->
StoredKeyB64 = base64:encode(StoredKey),
ServerKeyB64 = base64:encode(ServerKey),
SaltB64 = base64:encode(Salt),
IterationCountBin = (integer_to_binary(IterationCount)),
Hash2 = case Hash of
sha -> <<>>;
sha256 -> <<"sha256,">>;
sha512 -> <<"sha512,">>
end,
<<"scram:", Hash2/binary, StoredKeyB64/binary, ",", ServerKeyB64/binary, ",", SaltB64/binary, ",", IterationCountBin/binary>>.
StoredKeyB64 = base64:encode(StoredKey),
ServerKeyB64 = base64:encode(ServerKey),
SaltB64 = base64:encode(Salt),
IterationCountBin = (integer_to_binary(IterationCount)),
MechanismB = case Hash of
sha -> <<"SCRAM-SHA-1">>;
sha256 -> <<"SCRAM-SHA-256">>;
sha512 -> <<"SCRAM-SHA-512">>
end,
Children =
[
#xmlel{name = <<"iter-count">>,
children = [{xmlcdata, IterationCountBin}]},
#xmlel{name = <<"salt">>,
children = [{xmlcdata, SaltB64}]},
#xmlel{name = <<"server-key">>,
children = [{xmlcdata, ServerKeyB64}]},
#xmlel{name = <<"stored-key">>,
children = [{xmlcdata, StoredKeyB64}]}
],
#xmlel{name = <<"scram-credentials">>,
attrs = [{<<"xmlns">>, <<?NS_PIE/binary, "#scram">>},
{<<"mechanism">>, MechanismB}],
children = Children}.
parse_scram_password(#xmlel{attrs = Attrs} = El) ->
Hash = case fxml:get_attr_s(<<"mechanism">>, Attrs) of
<<"SCRAM-SHA-1">> -> sha;
<<"SCRAM-SHA-256">> -> sha256;
<<"SCRAM-SHA-512">> -> sha512
end,
StoredKeyB64 = fxml:get_path_s(El, [{elem, <<"stored-key">>}, cdata]),
ServerKeyB64 = fxml:get_path_s(El, [{elem, <<"server-key">>}, cdata]),
IterationCountBin = fxml:get_path_s(El, [{elem, <<"iter-count">>}, cdata]),
SaltB64 = fxml:get_path_s(El, [{elem, <<"salt">>}, cdata]),
#scram{
storedkey = base64:decode(StoredKeyB64),
serverkey = base64:decode(ServerKeyB64),
salt = base64:decode(SaltB64),
hash = Hash,
iterationcount = (binary_to_integer(IterationCountBin))
};
parse_scram_password(PassData) ->
Split = binary:split(PassData, <<",">>, [global]),
@ -408,21 +439,10 @@ process_users([_|Els], State) ->
process_users([], State) ->
{ok, State}.
process_user(#xmlel{name = <<"user">>, attrs = Attrs, children = Els},
process_user(#xmlel{name = <<"user">>, attrs = Attrs, children = Els} = El,
#state{server = LServer} = State) ->
Name = fxml:get_attr_s(<<"name">>, Attrs),
Password = fxml:get_attr_s(<<"password">>, Attrs),
PasswordFormat = ejabberd_auth:password_format(LServer),
Pass = case PasswordFormat of
scram ->
case Password of
<<"scram:", PassData/binary>> ->
parse_scram_password(PassData);
P -> P
end;
_ -> Password
end,
Pass = process_password(El, LServer),
case jid:nodeprep(Name) of
error ->
stop("Invalid 'user': ~ts", [Name]);
@ -430,13 +450,29 @@ process_user(#xmlel{name = <<"user">>, attrs = Attrs, children = Els},
case ejabberd_auth:try_register(LUser, LServer, Pass) of
ok ->
process_user_els(Els, State#state{user = LUser});
{error, invalid_password} when (Password == <<>>) ->
{error, invalid_password} when (Pass == <<>>) ->
process_user_els(Els, State#state{user = LUser});
{error, Err} ->
stop("Failed to create user '~ts': ~p", [Name, Err])
end
end.
process_password(#xmlel{name = <<"user">>, attrs = Attrs} = El, LServer) ->
{PassPlain, PassOldScram} = case fxml:get_attr_s(<<"password">>, Attrs) of
<<"scram:", PassData/binary>> -> {<<"">>, PassData};
P -> {P, false}
end,
ScramCred = fxml:get_subtag(El, <<"scram-credentials">>),
PasswordFormat = ejabberd_auth:password_format(LServer),
case {PassPlain, PassOldScram, ScramCred, PasswordFormat} of
{PassPlain, false, false, plain} -> PassPlain;
{<<"">>, false, ScramCred, plain} -> parse_scram_password(ScramCred);
{<<"">>, PassOldScram, false, plain} -> parse_scram_password(PassOldScram);
{PassPlain, false, false, scram} -> PassPlain;
{<<"">>, false, ScramCred, scram} -> parse_scram_password(ScramCred);
{<<"">>, PassOldScram, false, scram} -> parse_scram_password(PassOldScram)
end.
process_user_els([#xmlel{} = El|Els], State) ->
case process_user_el(El, State) of
{ok, NewState} ->