From e4d424bf56c265cf28a21021c92ea4ab3fb7de6d Mon Sep 17 00:00:00 2001 From: Pawel Chmielowski Date: Thu, 10 Jul 2025 11:44:13 +0200 Subject: [PATCH] Add auth_password_types_hidden_in_scram1 option This option allows disabling some auth mechanisms to be offered in SASL1 features. This makes adding new password types easier, by ensuring that new password use will be offered only to clients that have new type stored (SASL2 clients that send us user info before features need to be sent), but not to clients where we don't know if they have new passwords. --- rebar.config | 2 +- rebar.lock | 2 +- src/ejabberd_c2s.erl | 41 +++++++++++++++++++++++------------- src/ejabberd_option.erl | 8 +++++++ src/ejabberd_options.erl | 3 +++ src/ejabberd_options_doc.erl | 11 ++++++++++ 6 files changed, 50 insertions(+), 17 deletions(-) diff --git a/rebar.config b/rebar.config index 192c7b18c..16cde2cc4 100644 --- a/rebar.config +++ b/rebar.config @@ -77,7 +77,7 @@ {stringprep, "~> 1.0.31", {git, "https://github.com/processone/stringprep", {tag, "1.0.31"}}}, {if_var_true, stun, {stun, "~> 1.2.17", {git, "https://github.com/processone/stun", {tag, "1.2.17"}}}}, - {xmpp, "~> 1.10.0", {git, "https://github.com/processone/xmpp", "9b028c110083e4d979d88c286873d0abf08fa532"}}, + {xmpp, "~> 1.10.0", {git, "https://github.com/processone/xmpp", "b474d673938856a546265cd5db1139244bfb95f3"}}, {yconf, "~> 1.0.18", {git, "https://github.com/processone/yconf", {tag, "1.0.18"}}} ]}. diff --git a/rebar.lock b/rebar.lock index caac96087..c42e49c40 100644 --- a/rebar.lock +++ b/rebar.lock @@ -35,7 +35,7 @@ {<<"unicode_util_compat">>,{pkg,<<"unicode_util_compat">>,<<"0.7.1">>},1}, {<<"xmpp">>, {git,"https://github.com/processone/xmpp", - {ref,"9b028c110083e4d979d88c286873d0abf08fa532"}}, + {ref,"b474d673938856a546265cd5db1139244bfb95f3"}}, 0}, {<<"yconf">>,{pkg,<<"yconf">>,<<"1.0.18">>},0}]}. [ diff --git a/src/ejabberd_c2s.erl b/src/ejabberd_c2s.erl index 1852dde32..1a03adc9f 100644 --- a/src/ejabberd_c2s.erl +++ b/src/ejabberd_c2s.erl @@ -440,21 +440,32 @@ sasl_mechanisms(Mechs, #{lserver := LServer, stream_encrypted := Encrypted} = St end, %% 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">>) -> Digest; - (<<"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); - (_) -> false - end, Mechs -- Mechs1). + Mechs2 = lists:filter( + fun(<<"ANONYMOUS">>) -> + ejabberd_auth_anonymous:is_sasl_anonymous_enabled(LServer); + (<<"DIGEST-MD5">>) -> Digest; + (<<"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); + (_) -> false + end, Mechs -- Mechs1), + case ejabberd_option:auth_password_types_hidden_in_scram1() of + [] -> Mechs2; + List -> + Mechs3 = lists:foldl( + fun(plain, Acc) -> Acc -- [<<"PLAIN">>]; + (scram_sha1, Acc) -> Acc -- [<<"SCRAM-SHA-1">>, <<"SCRAM-SHA-1-PLUS">>]; + (scram_sha256, Acc) -> Acc -- [<<"SCRAM-SHA-256">>, <<"SCRAM-SHA-256-PLUS">>]; + (scram_sha512, Acc) -> Acc -- [<<"SCRAM-SHA-512">>, <<"SCRAM-SHA-512-PLUS">>] + end, Mechs2, List), + {Mechs3, Mechs2} + end. sasl_options(#{lserver := LServer}) -> case ejabberd_option:disable_sasl_scram_downgrade_protection(LServer) of diff --git a/src/ejabberd_option.erl b/src/ejabberd_option.erl index 3a69da867..4361571f8 100644 --- a/src/ejabberd_option.erl +++ b/src/ejabberd_option.erl @@ -18,6 +18,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_password_types_hidden_in_scram1/0, auth_password_types_hidden_in_scram1/1]). -export([auth_scram_hash/0, auth_scram_hash/1]). -export([auth_stored_password_types/0, auth_stored_password_types/1]). -export([auth_use_cache/0, auth_use_cache/1]). @@ -263,6 +264,13 @@ auth_password_format() -> auth_password_format(Host) -> ejabberd_config:get_option({auth_password_format, Host}). +-spec auth_password_types_hidden_in_scram1() -> ['plain' | 'scram_sha1' | 'scram_sha256' | 'scram_sha512']. +auth_password_types_hidden_in_scram1() -> + auth_password_types_hidden_in_scram1(global). +-spec auth_password_types_hidden_in_scram1(global | binary()) -> ['plain' | 'scram_sha1' | 'scram_sha256' | 'scram_sha512']. +auth_password_types_hidden_in_scram1(Host) -> + ejabberd_config:get_option({auth_password_types_hidden_in_scram1, Host}). + -spec auth_scram_hash() -> 'sha' | 'sha256' | 'sha512'. auth_scram_hash() -> auth_scram_hash(global). diff --git a/src/ejabberd_options.erl b/src/ejabberd_options.erl index 521687aa2..ee3acfc58 100644 --- a/src/ejabberd_options.erl +++ b/src/ejabberd_options.erl @@ -79,6 +79,8 @@ opt_type(auth_opts) -> end; opt_type(auth_stored_password_types) -> econf:list(econf:enum([plain, scram_sha1, scram_sha256, scram_sha512])); +opt_type(auth_password_types_hidden_in_scram1) -> + econf:list(econf:enum([plain, scram_sha1, scram_sha256, scram_sha512])); opt_type(auth_password_format) -> econf:enum([plain, scram]); opt_type(auth_scram_hash) -> @@ -564,6 +566,7 @@ options() -> {auth_password_format, plain}, {auth_scram_hash, sha}, {auth_stored_password_types, []}, + {auth_password_types_hidden_in_scram1, []}, {auth_external_user_exists_check, true}, {auth_use_cache, fun(Host) -> ejabberd_config:get_option({use_cache, Host}) end}, diff --git a/src/ejabberd_options_doc.erl b/src/ejabberd_options_doc.erl index dabfae06d..98f170c96 100644 --- a/src/ejabberd_options_doc.erl +++ b/src/ejabberd_options_doc.erl @@ -392,6 +392,17 @@ doc() -> "SASL PLAIN and SASL SCRAM-SHA-1/256/512(-PLUS). The SCRAM variant " "depends on the _`auth_scram_hash`_ option."), "", ?T("The default value is 'plain'."), ""]}}, + + {auth_password_types_hidden_in_scram1, + #{value => "[plain | scram_sha1 | scram_sha256 | scram_sha512]", + note => "added in 25.07", + desc => + ?T("List of password types that should not be offered in SCRAM1 authenticatication. " + "Because SCRAM1, unlike SCRAM2, can't have list of available mechanisms tailored to " + "individual user, it's possible that offered mechanisms will not be compatible " + "with stored password, especially if new password type was added recently. " + "This option allows disabling offering some mechanisms in SASL1, to a time until new " + "password type will be available for all users.")}}, {auth_scram_hash, #{value => "sha | sha256 | sha512", desc =>