aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--rebar.config2
-rw-r--r--src/ejabberd_auth.erl29
-rw-r--r--src/ejabberd_auth_mnesia.erl2
-rw-r--r--src/ejabberd_auth_sql.erl59
-rw-r--r--src/ejabberd_c2s.erl17
-rw-r--r--src/ejabberd_option.erl34
-rw-r--r--src/ejabberd_options.erl3
-rw-r--r--src/ejabberd_piefxis.erl17
8 files changed, 106 insertions, 57 deletions
diff --git a/rebar.config b/rebar.config
index 61748af56..c464de277 100644
--- a/rebar.config
+++ b/rebar.config
@@ -57,7 +57,7 @@
{stringprep, ".*", {git, "https://github.com/processone/stringprep", {tag, "1.0.23"}}},
{if_var_true, stun,
{stun, ".*", {git, "https://github.com/processone/stun", {tag, "1.0.39"}}}},
- {xmpp, ".*", {git, "https://github.com/processone/xmpp", "e21de94967c9d6b632058b1f5d34614e0dc9bfe8"}},
+ {xmpp, ".*", {git, "https://github.com/processone/xmpp", "651050f9619fd768491e8abb6e8db6c778eec2b3"}},
{yconf, ".*", {git, "https://github.com/processone/yconf", {tag, "1.0.8"}}}
]}.
diff --git a/src/ejabberd_auth.erl b/src/ejabberd_auth.erl
index aa3284f3d..f5530b9ea 100644
--- a/src/ejabberd_auth.erl
+++ b/src/ejabberd_auth.erl
@@ -33,7 +33,7 @@
set_password/3, check_password/4,
check_password/6, check_password_with_authmodule/4,
check_password_with_authmodule/6, try_register/3,
- get_users/0, get_users/1, password_to_scram/1,
+ get_users/0, get_users/1, password_to_scram/2,
get_users/2, import_info/0,
count_users/1, import/5, import_start/2,
count_users/2, get_password/2,
@@ -554,7 +554,7 @@ db_try_register(User, Server, Password, Mod) ->
case erlang:function_exported(Mod, try_register, 3) of
true ->
Password1 = case Mod:store_type(Server) of
- scram -> password_to_scram(Password);
+ scram -> password_to_scram(Server, Password);
_ -> Password
end,
Ret = case use_cache(Mod, Server) of
@@ -579,7 +579,7 @@ db_set_password(User, Server, Password, Mod) ->
case erlang:function_exported(Mod, set_password, 3) of
true ->
Password1 = case Mod:store_type(Server) of
- scram -> password_to_scram(Password);
+ scram -> password_to_scram(Server, Password);
_ -> Password
end,
Ret = case use_cache(Mod, Server) of
@@ -753,25 +753,28 @@ is_password_scram_valid(Password, Scram) ->
false;
_ ->
IterationCount = Scram#scram.iterationcount,
+ Hash = Scram#scram.hash,
Salt = base64:decode(Scram#scram.salt),
- SaltedPassword = scram:salted_password(sha, Password, Salt, IterationCount),
- StoredKey = scram:stored_key(sha, scram:client_key(sha, SaltedPassword)),
+ SaltedPassword = scram:salted_password(Hash, Password, Salt, IterationCount),
+ StoredKey = scram:stored_key(Hash, scram:client_key(Hash, SaltedPassword)),
base64:decode(Scram#scram.storedkey) == StoredKey
end.
-password_to_scram(Password) ->
- password_to_scram(Password, ?SCRAM_DEFAULT_ITERATION_COUNT).
+password_to_scram(Host, Password) ->
+ password_to_scram(Host, Password, ?SCRAM_DEFAULT_ITERATION_COUNT).
-password_to_scram(#scram{} = Password, _IterationCount) ->
+password_to_scram(_Host, #scram{} = Password, _IterationCount) ->
Password;
-password_to_scram(Password, IterationCount) ->
+password_to_scram(Host, Password, IterationCount) ->
+ Hash = ejabberd_option:auth_scram_hash(Host),
Salt = p1_rand:bytes(?SALT_LENGTH),
- SaltedPassword = scram:salted_password(sha, Password, Salt, IterationCount),
- StoredKey = scram:stored_key(sha, scram:client_key(sha, SaltedPassword)),
- ServerKey = scram:server_key(sha, SaltedPassword),
+ SaltedPassword = scram:salted_password(Hash, Password, Salt, IterationCount),
+ StoredKey = scram:stored_key(Hash, scram:client_key(Hash, SaltedPassword)),
+ ServerKey = scram:server_key(Hash, SaltedPassword),
#scram{storedkey = base64:encode(StoredKey),
serverkey = base64:encode(ServerKey),
salt = base64:encode(Salt),
+ hash = Hash,
iterationcount = IterationCount}.
%%%----------------------------------------------------------------------
@@ -938,7 +941,7 @@ convert_to_scram(Server) ->
fun({U, S}) ->
case get_password(U, S) of
Pass when is_binary(Pass) ->
- SPass = password_to_scram(Pass),
+ SPass = password_to_scram(Server, Pass),
set_password(U, S, SPass);
_ ->
ok
diff --git a/src/ejabberd_auth_mnesia.erl b/src/ejabberd_auth_mnesia.erl
index fb728313e..76bd35340 100644
--- a/src/ejabberd_auth_mnesia.erl
+++ b/src/ejabberd_auth_mnesia.erl
@@ -255,7 +255,7 @@ transform(#passwd{us = {U, S}, password = Password} = P)
[U, S]),
P;
_ ->
- Scram = ejabberd_auth:password_to_scram(Password),
+ Scram = ejabberd_auth:password_to_scram(global, Password),
P#passwd{password = Scram}
end;
plain ->
diff --git a/src/ejabberd_auth_sql.erl b/src/ejabberd_auth_sql.erl
index e42b42dbf..da51594f0 100644
--- a/src/ejabberd_auth_sql.erl
+++ b/src/ejabberd_auth_sql.erl
@@ -56,16 +56,19 @@ store_type(Server) ->
ejabberd_auth:password_format(Server).
set_password(User, Server, Password) ->
- F = fun() ->
- if is_record(Password, scram) ->
- set_password_scram_t(
- User, Server,
- Password#scram.storedkey, Password#scram.serverkey,
- Password#scram.salt, Password#scram.iterationcount);
- true ->
- set_password_t(User, Server, Password)
- end
- end,
+ F =
+ fun() ->
+ case Password of
+ #scram{hash = Hash, storedkey = SK, serverkey = SEK,
+ salt = Salt, iterationcount = IC} ->
+ SK2 = scram_hash_encode(Hash, SK),
+ set_password_scram_t(
+ User, Server,
+ SK2, SEK, Salt, IC);
+ _ ->
+ set_password_t(User, Server, Password)
+ end
+ end,
case ejabberd_sql:sql_transaction(Server, F) of
{atomic, _} ->
{cache, {ok, Password}};
@@ -74,14 +77,17 @@ set_password(User, Server, Password) ->
end.
try_register(User, Server, Password) ->
- Res = if is_record(Password, scram) ->
- add_user_scram(
- Server, User,
- Password#scram.storedkey, Password#scram.serverkey,
- Password#scram.salt, Password#scram.iterationcount);
- true ->
- add_user(Server, User, Password)
- end,
+ Res =
+ case Password of
+ #scram{hash = Hash, storedkey = SK, serverkey = SEK,
+ salt = Salt, iterationcount = IC} ->
+ SK2 = scram_hash_encode(Hash, SK),
+ add_user_scram(
+ Server, User,
+ SK2, SEK, Salt, IC);
+ _ ->
+ add_user(Server, User, Password)
+ end,
case Res of
{updated, 1} -> {cache, {ok, Password}};
_ -> {nocache, {error, exists}}
@@ -106,9 +112,15 @@ get_password(User, Server) ->
{selected, [{Password, <<>>, <<>>, 0}]} ->
{cache, {ok, Password}};
{selected, [{StoredKey, ServerKey, Salt, IterationCount}]} ->
- {cache, {ok, #scram{storedkey = StoredKey,
+ {Hash, SK} = case StoredKey of
+ <<"sha256:", Rest/binary>> -> {sha256, Rest};
+ <<"sha512:", Rest/binary>> -> {sha512, Rest};
+ Other -> {sha, Other}
+ end,
+ {cache, {ok, #scram{storedkey = SK,
serverkey = ServerKey,
salt = Salt,
+ hash = Hash,
iterationcount = IterationCount}}};
{selected, []} ->
{cache, error};
@@ -126,6 +138,13 @@ remove_user(User, Server) ->
-define(BATCH_SIZE, 1000).
+scram_hash_encode(Hash, StoreKey) ->
+ case Hash of
+ sha -> StoreKey;
+ sha256 -> <<"sha256:", StoreKey/binary>>;
+ sha512 -> <<"sha512:", StoreKey/binary>>
+ end.
+
set_password_scram_t(LUser, LServer,
StoredKey, ServerKey, Salt, IterationCount) ->
?SQL_UPSERT_T(
@@ -282,7 +301,7 @@ export(_Server) ->
"password=%(Password)s"])];
(Host, #passwd{us = {LUser, LServer}, password = #scram{} = Scram})
when LServer == Host ->
- StoredKey = Scram#scram.storedkey,
+ StoredKey = scram_hash_encode(Scram#scram.hash, Scram#scram.storedkey),
ServerKey = Scram#scram.serverkey,
Salt = Scram#scram.salt,
IterationCount = Scram#scram.iterationcount,
diff --git a/src/ejabberd_c2s.erl b/src/ejabberd_c2s.erl
index 56410ed82..3f42e9dd6 100644
--- a/src/ejabberd_c2s.erl
+++ b/src/ejabberd_c2s.erl
@@ -376,18 +376,23 @@ authenticated_stream_features(#{lserver := LServer}) ->
sasl_mechanisms(Mechs, #{lserver := LServer, stream_encrypted := Encrypted} = State) ->
Type = ejabberd_auth:store_type(LServer),
Mechs1 = ejabberd_option:disable_sasl_mechanisms(LServer),
+
+ ScramHash = ejabberd_option:auth_scram_hash(LServer),
+ ShaAv = Type == plain orelse (Type == scram andalso ScramHash == sha),
+ Sha256Av = Type == plain orelse (Type == scram andalso ScramHash == sha256),
+ Sha512Av = Type == plain orelse (Type == scram andalso ScramHash == sha512),
%% I re-created it from cyrsasl ets magic, but I think it's wrong
%% TODO: need to check before 18.09 release
lists:filter(
fun(<<"ANONYMOUS">>) ->
ejabberd_auth_anonymous:is_sasl_anonymous_enabled(LServer);
(<<"DIGEST-MD5">>) -> Type == plain;
- (<<"SCRAM-SHA-1">>) -> Type /= external;
- (<<"SCRAM-SHA-1-PLUS">>) -> Type /= external andalso Encrypted;
- (<<"SCRAM-SHA-256">>) -> Type == plain;
- (<<"SCRAM-SHA-256-PLUS">>) -> Type == plain andalso Encrypted;
- (<<"SCRAM-SHA-512">>) -> Type == plain;
- (<<"SCRAM-SHA-512-PLUS">>) -> Type == plain andalso Encrypted;
+ (<<"SCRAM-SHA-1">>) -> ShaAv;
+ (<<"SCRAM-SHA-1-PLUS">>) -> ShaAv andalso Encrypted;
+ (<<"SCRAM-SHA-256">>) -> Sha256Av;
+ (<<"SCRAM-SHA-256-PLUS">>) -> Sha256Av andalso Encrypted;
+ (<<"SCRAM-SHA-512">>) -> Sha512Av;
+ (<<"SCRAM-SHA-512-PLUS">>) -> Sha512Av andalso Encrypted;
(<<"PLAIN">>) -> true;
(<<"X-OAUTH2">>) -> [ejabberd_auth_anonymous] /= ejabberd_auth:auth_modules(LServer);
(<<"EXTERNAL">>) -> maps:get(tls_verify, State, false);
diff --git a/src/ejabberd_option.erl b/src/ejabberd_option.erl
index e5b47f01f..873f76ed1 100644
--- a/src/ejabberd_option.erl
+++ b/src/ejabberd_option.erl
@@ -17,6 +17,7 @@
-export([auth_method/0, auth_method/1]).
-export([auth_opts/0, auth_opts/1]).
-export([auth_password_format/0, auth_password_format/1]).
+-export([auth_scram_hash/0, auth_scram_hash/1]).
-export([auth_use_cache/0, auth_use_cache/1]).
-export([c2s_cafile/0, c2s_cafile/1]).
-export([c2s_ciphers/0, c2s_ciphers/1]).
@@ -90,9 +91,9 @@
-export([oom_killer/0]).
-export([oom_queue/0]).
-export([oom_watermark/0]).
+-export([outgoing_s2s_families/0, outgoing_s2s_families/1]).
-export([outgoing_s2s_ipv4_address/0, outgoing_s2s_ipv4_address/1]).
-export([outgoing_s2s_ipv6_address/0, outgoing_s2s_ipv6_address/1]).
--export([outgoing_s2s_families/0, outgoing_s2s_families/1]).
-export([outgoing_s2s_port/0, outgoing_s2s_port/1]).
-export([outgoing_s2s_timeout/0, outgoing_s2s_timeout/1]).
-export([pam_service/0, pam_service/1]).
@@ -138,8 +139,8 @@
-export([sql_connect_timeout/0, sql_connect_timeout/1]).
-export([sql_database/0, sql_database/1]).
-export([sql_keepalive_interval/0, sql_keepalive_interval/1]).
--export([sql_password/0, sql_password/1]).
-export([sql_odbc_driver/0, sql_odbc_driver/1]).
+-export([sql_password/0, sql_password/1]).
-export([sql_pool_size/0, sql_pool_size/1]).
-export([sql_port/0, sql_port/1]).
-export([sql_prepared_statements/0, sql_prepared_statements/1]).
@@ -238,6 +239,13 @@ auth_password_format() ->
auth_password_format(Host) ->
ejabberd_config:get_option({auth_password_format, Host}).
+-spec auth_scram_hash() -> 'sha' | 'sha256' | 'sha512'.
+auth_scram_hash() ->
+ auth_scram_hash(global).
+-spec auth_scram_hash(global | binary()) -> 'sha' | 'sha256' | 'sha512'.
+auth_scram_hash(Host) ->
+ ejabberd_config:get_option({auth_scram_hash, Host}).
+
-spec auth_use_cache() -> boolean().
auth_use_cache() ->
auth_use_cache(global).
@@ -669,17 +677,17 @@ outgoing_s2s_families() ->
outgoing_s2s_families(Host) ->
ejabberd_config:get_option({outgoing_s2s_families, Host}).
--spec outgoing_s2s_ipv4_address() -> inet:ip4_address().
+-spec outgoing_s2s_ipv4_address() -> 'undefined' | inet:ip4_address().
outgoing_s2s_ipv4_address() ->
outgoing_s2s_ipv4_address(global).
--spec outgoing_s2s_ipv4_address(global | binary()) -> inet:ip4_address().
+-spec outgoing_s2s_ipv4_address(global | binary()) -> 'undefined' | inet:ip4_address().
outgoing_s2s_ipv4_address(Host) ->
ejabberd_config:get_option({outgoing_s2s_ipv4_address, Host}).
--spec outgoing_s2s_ipv6_address() -> inet:ip6_address().
+-spec outgoing_s2s_ipv6_address() -> 'undefined' | inet:ip6_address().
outgoing_s2s_ipv6_address() ->
outgoing_s2s_ipv6_address(global).
--spec outgoing_s2s_ipv6_address(global | binary()) -> inet:ip6_address().
+-spec outgoing_s2s_ipv6_address(global | binary()) -> 'undefined' | inet:ip6_address().
outgoing_s2s_ipv6_address(Host) ->
ejabberd_config:get_option({outgoing_s2s_ipv6_address, Host}).
@@ -938,13 +946,6 @@ sql_keepalive_interval() ->
sql_keepalive_interval(Host) ->
ejabberd_config:get_option({sql_keepalive_interval, Host}).
--spec sql_password() -> binary().
-sql_password() ->
- sql_password(global).
--spec sql_password(global | binary()) -> binary().
-sql_password(Host) ->
- ejabberd_config:get_option({sql_password, Host}).
-
-spec sql_odbc_driver() -> binary().
sql_odbc_driver() ->
sql_odbc_driver(global).
@@ -952,6 +953,13 @@ sql_odbc_driver() ->
sql_odbc_driver(Host) ->
ejabberd_config:get_option({sql_odbc_driver, Host}).
+-spec sql_password() -> binary().
+sql_password() ->
+ sql_password(global).
+-spec sql_password(global | binary()) -> binary().
+sql_password(Host) ->
+ ejabberd_config:get_option({sql_password, Host}).
+
-spec sql_pool_size() -> pos_integer().
sql_pool_size() ->
sql_pool_size(global).
diff --git a/src/ejabberd_options.erl b/src/ejabberd_options.erl
index a03071dc1..445a3515b 100644
--- a/src/ejabberd_options.erl
+++ b/src/ejabberd_options.erl
@@ -79,6 +79,8 @@ opt_type(auth_opts) ->
end;
opt_type(auth_password_format) ->
econf:enum([plain, scram]);
+opt_type(auth_scram_hash) ->
+ econf:enum([sha, sha256, sha512]);
opt_type(auth_use_cache) ->
econf:bool();
opt_type(c2s_cafile) ->
@@ -517,6 +519,7 @@ options() ->
fun(Host) -> [ejabberd_config:default_db(Host, ejabberd_auth)] end},
{auth_opts, []},
{auth_password_format, plain},
+ {auth_scram_hash, sha},
{auth_use_cache,
fun(Host) -> ejabberd_config:get_option({use_cache, Host}) end},
{c2s_cafile, undefined},
diff --git a/src/ejabberd_piefxis.erl b/src/ejabberd_piefxis.erl
index 2712d599a..fb1525f71 100644
--- a/src/ejabberd_piefxis.erl
+++ b/src/ejabberd_piefxis.erl
@@ -181,21 +181,32 @@ export_user(User, Server, Fd) ->
{<<"password">>, Pass}],
children = Els})).
-format_scram_password(#scram{storedkey = StoredKey, serverkey = ServerKey,
+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)),
- <<"scram:", StoredKeyB64/binary, ",", ServerKeyB64/binary, ",", SaltB64/binary, ",", IterationCountBin/binary>>.
+ Hash2 = case Hash of
+ sha -> <<>>;
+ sha256 -> <<"sha256,">>;
+ sha512 -> <<"sha512,">>
+ end,
+ <<"scram:", Hash2/binary, StoredKeyB64/binary, ",", ServerKeyB64/binary, ",", SaltB64/binary, ",", IterationCountBin/binary>>.
parse_scram_password(PassData) ->
Split = binary:split(PassData, <<",">>, [global]),
- [StoredKeyB64, ServerKeyB64, SaltB64, IterationCountBin] = Split,
+ [Hash, StoredKeyB64, ServerKeyB64, SaltB64, IterationCountBin] =
+ case Split of
+ [K1, K2, K3, K4] -> [sha, K1, K2, K3, K4];
+ [<<"sha256">>, K1, K2, K3, K4] -> [sha256, K1, K2, K3, K4];
+ [<<"sha512">>, K1, K2, K3, K4] -> [sha512, K1, K2, K3, K4]
+ end,
#scram{
storedkey = base64:decode(StoredKeyB64),
serverkey = base64:decode(ServerKeyB64),
salt = base64:decode(SaltB64),
+ hash = Hash,
iterationcount = (binary_to_integer(IterationCountBin))
}.