1: %%==============================================================================
    2: %% Copyright 2010 Erlang Solutions Ltd.
    3: %%
    4: %% Licensed under the Apache License, Version 2.0 (the "License");
    5: %% you may not use this file except in compliance with the License.
    6: %% You may obtain a copy of the License at
    7: %%
    8: %% http://www.apache.org/licenses/LICENSE-2.0
    9: %%
   10: %% Unless required by applicable law or agreed to in writing, software
   11: %% distributed under the License is distributed on an "AS IS" BASIS,
   12: %% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   13: %% See the License for the specific language governing permissions and
   14: %% limitations under the License.
   15: %%==============================================================================
   16: 
   17: -module(login_SUITE).
   18: -compile([export_all, nowarn_export_all]).
   19: 
   20: -include_lib("exml/include/exml.hrl").
   21: 
   22: -import(distributed_helper, [mim/0,
   23:                              require_rpc_nodes/1,
   24:                              rpc/4]).
   25: 
   26: -import(domain_helper, [host_type/0, domain/0]).
   27: 
   28: %%--------------------------------------------------------------------
   29: %% Suite configuration
   30: %%--------------------------------------------------------------------
   31: 
   32: all() ->
   33:     [
   34:      {group, login},
   35:      {group, login_digest},
   36:      {group, login_scram},
   37:      {group, login_scram_store_plain},
   38:      {group, login_specific_scram},
   39:      {group, login_scram_tls},
   40:      {group, messages}
   41:     ].
   42: 
   43: groups() ->
   44:     [{login, [parallel], all_tests()},
   45:      {login_digest, [sequence], digest_tests()},
   46:      {login_scram, [parallel], scram_tests()},
   47:      {login_scram_store_plain, [parallel], scram_tests()},
   48:      {login_scram_tls, [parallel], scram_tests()},
   49:      {login_specific_scram, [sequence], configure_specific_scram_test()},
   50:      {messages, [sequence], [messages_story, message_zlib_limit]}].
   51: 
   52: scram_tests() ->
   53:     [log_one,
   54:      log_one_scram_sha1,
   55:      log_one_scram_sha224,
   56:      log_one_scram_sha256,
   57:      log_one_scram_sha384,
   58:      log_one_scram_sha512,
   59:      log_one_scram_sha1_plus,
   60:      log_one_scram_sha224_plus,
   61:      log_one_scram_sha256_plus,
   62:      log_one_scram_sha384_plus,
   63:      log_one_scram_sha512_plus].
   64: 
   65: configure_specific_scram_test() ->
   66:     [configure_sha1_log_with_sha1,
   67:      configure_sha224_log_with_sha224,
   68:      configure_sha256_log_with_sha256,
   69:      configure_sha384_log_with_sha384,
   70:      configure_sha512_log_with_sha512,
   71:      configure_sha1_log_with_sha1_plus,
   72:      configure_sha224_log_with_sha224_plus,
   73:      configure_sha256_log_with_sha256_plus,
   74:      configure_sha384_log_with_sha384_plus,
   75:      configure_sha512_log_with_sha512_plus,
   76:      configure_sha1_fail_log_with_sha224,
   77:      configure_sha224_fail_log_with_sha256,
   78:      configure_sha256_fail_log_with_sha384,
   79:      configure_sha384_fail_log_with_sha512,
   80:      configure_sha512_fail_log_with_sha1,
   81:      configure_sha1_plus_fail_log_with_sha1,
   82:      configure_sha224_plus_fail_log_with_sha224,
   83:      configure_sha256_plus_fail_log_with_sha256,
   84:      configure_sha384_plus_fail_log_with_sha384,
   85:      configure_sha512_plus_fail_log_with_sha512].
   86: 
   87: all_tests() ->
   88:     [log_one,
   89:      log_non_existent_plain,
   90:      log_one_scram_sha1,
   91:      log_non_existent_scram,
   92:      blocked_user
   93:     ].
   94: 
   95: digest_tests() ->
   96:     [log_one_digest,
   97:      log_non_existent_digest].
   98: 
   99: suite() ->
  100:     require_rpc_nodes([mim]) ++ escalus:suite().
  101: 
  102: %%--------------------------------------------------------------------
  103: %% Init & teardown
  104: %%--------------------------------------------------------------------
  105: 
  106: init_per_suite(Config) ->
  107:     escalus:init_per_suite(Config).
  108: 
  109: end_per_suite(Config) ->
  110:     escalus_fresh:clean(),
  111:     escalus:end_per_suite(Config).
  112: 
  113: init_per_group(login_digest = GroupName, ConfigIn) ->
  114:     Config = backup_and_set_options(GroupName, ConfigIn),
  115:     case mongoose_helper:supports_sasl_module(cyrsasl_digest) of
  116:         false ->
  117:             mongoose_helper:restore_config(Config),
  118:             {skip, "digest password type not supported"};
  119:         true ->
  120:             escalus:create_users(Config, escalus:get_users([alice, bob]))
  121:     end;
  122: init_per_group(GroupName, ConfigIn)
  123:   when GroupName == login_scram;
  124:        GroupName == login_scram_store_plain ->
  125:     Config = backup_and_set_options(GroupName, ConfigIn),
  126:     case are_sasl_scram_modules_supported() of
  127:         false ->
  128:             mongoose_helper:restore_config(Config),
  129:             {skip, "scram password type not supported"};
  130:         true ->
  131:             Config2 = escalus:create_users(Config, escalus:get_users([alice, bob, neustradamus])),
  132:             assert_password_format(GroupName, Config2)
  133:     end;
  134: init_per_group(login_scram_tls = GroupName, ConfigIn) ->
  135:     Config = backup_and_set_options(GroupName, ConfigIn),
  136:     case are_sasl_scram_modules_supported() of
  137:         false ->
  138:             mongoose_helper:restore_config(Config),
  139:             {skip, "scram password type not supported"};
  140:         true ->
  141:             Config1 = configure_c2s_listener(Config),
  142:             Config2 = create_tls_users(Config1),
  143:             assert_password_format(scram, Config2)
  144:     end;
  145: init_per_group(login_specific_scram = GroupName, ConfigIn) ->
  146:     Config = backup_and_set_options(GroupName, ConfigIn),
  147:     case are_sasl_scram_modules_supported() of
  148:         false ->
  149:             mongoose_helper:restore_config(Config),
  150:             {skip, "scram password type not supported"};
  151:         true ->
  152:             escalus:create_users(Config, escalus:get_users([alice, bob, neustradamus]))
  153:     end;
  154: init_per_group(GroupName, ConfigIn) ->
  155:     Config = backup_and_set_options(GroupName, ConfigIn),
  156:     escalus:create_users(Config, escalus:get_users([alice, bob])).
  157: 
  158: backup_and_set_options(GroupName, Config) ->
  159:     mongoose_helper:backup_and_set_config_option(Config, {auth, host_type()}, auth_opts(GroupName)).
  160: 
  161: auth_opts(login_digest) ->
  162:     AuthOpts = mongoose_helper:auth_opts_with_password_format(plain),
  163:     AuthOpts#{sasl_mechanisms => [cyrsasl_digest]};
  164: auth_opts(login_scram_store_plain) ->
  165:     mongoose_helper:auth_opts_with_password_format(plain);
  166: auth_opts(_GroupName) ->
  167:     mongoose_helper:auth_opts_with_password_format(scram).
  168: 
  169: end_per_group(login_digest, Config) ->
  170:     mongoose_helper:restore_config(Config),
  171:     escalus:delete_users(Config, escalus:get_users([alice, bob]));
  172: end_per_group(GroupName, Config) when
  173:     GroupName == login_scram; GroupName == login_specific_scram ->
  174:     mongoose_helper:restore_config(Config),
  175:     escalus:delete_users(Config, escalus:get_users([alice, bob, neustradamus]));
  176: end_per_group(login_scram_tls, Config) ->
  177:     mongoose_helper:restore_config(Config),
  178:     restore_c2s(Config),
  179:     delete_tls_users(Config);
  180: end_per_group(_GroupName, Config) ->
  181:     mongoose_helper:restore_config(Config),
  182:     escalus:delete_users(Config, escalus:get_users([alice, bob])).
  183: 
  184: init_per_testcase(CaseName, Config) when
  185:       CaseName =:= log_one_scram_sha1; CaseName =:= log_non_existent_scram ->
  186:     case mongoose_helper:supports_sasl_module(cyrsasl_scram_sha1) of
  187:         false ->
  188:             {skip, "scram password type not supported"};
  189:         true ->
  190:             escalus:init_per_testcase(CaseName, Config)
  191:     end;
  192: init_per_testcase(message_zlib_limit, Config) ->
  193:     Listeners = [Listener
  194:                  || {Listener, _, _} <- rpc(mim(), mongoose_config, get_opt, [listen])],
  195:     [{_U, Props}] = escalus_users:get_users([hacker]),
  196:     Port = proplists:get_value(port, Props),
  197:     case lists:keymember(Port, 1, Listeners) of
  198:         true ->
  199:             escalus:create_users(Config, escalus:get_users([hacker])),
  200:             escalus:init_per_testcase(message_zlib_limit, Config);
  201:         false ->
  202:             {skip, port_not_configured_on_server}
  203:     end;
  204: init_per_testcase(CaseName, Config) ->
  205:     escalus:init_per_testcase(CaseName, Config).
  206: 
  207: end_per_testcase(message_zlib_limit, Config) ->
  208:     escalus:delete_users(Config, escalus:get_users([hacker]));
  209: end_per_testcase(CaseName, Config) ->
  210:     escalus:end_per_testcase(CaseName, Config).
  211: 
  212: %%--------------------------------------------------------------------
  213: %% Message tests
  214: %%--------------------------------------------------------------------
  215: 
  216: log_one(Config) ->
  217:     escalus:fresh_story(Config, [{alice, 1}], fun(Alice) ->
  218: 
  219:         escalus_client:send(Alice, escalus_stanza:chat_to(Alice, <<"Hi!">>)),
  220:         escalus:assert(is_chat_message, [<<"Hi!">>], escalus_client:wait_for_stanza(Alice))
  221: 
  222:         end).
  223: 
  224: log_one_scram_plus(Config) ->
  225:     escalus:fresh_story(Config, [{neustradamus, 1}], fun(Neustradamus) ->
  226: 
  227:         escalus_client:send(Neustradamus, escalus_stanza:chat_to(Neustradamus, <<"Hi!">>)),
  228:         escalus:assert(is_chat_message, [<<"Hi!">>], escalus_client:wait_for_stanza(Neustradamus))
  229: 
  230:         end).
  231: 
  232: log_one_digest(Config) ->
  233:     log_one([{escalus_auth_method, <<"DIGEST-MD5">>} | Config]).
  234: 
  235: log_one_scram_sha1(Config) ->
  236:     log_one([{escalus_auth_method, <<"SCRAM-SHA-1">>} | Config]).
  237: 
  238: log_one_scram_sha224(Config) ->
  239:     log_one([{escalus_auth_method, <<"SCRAM-SHA-224">>} | Config]).
  240: 
  241: log_one_scram_sha256(Config) ->
  242:     log_one([{escalus_auth_method, <<"SCRAM-SHA-256">>} | Config]).
  243: 
  244:  log_one_scram_sha384(Config) ->
  245:     log_one([{escalus_auth_method, <<"SCRAM-SHA-384">>} | Config]).
  246: 
  247: log_one_scram_sha512(Config) ->
  248:     log_one([{escalus_auth_method, <<"SCRAM-SHA-512">>} | Config]).
  249: 
  250: log_one_scram_sha1_plus(Config) ->
  251:     log_one_scram_plus([{escalus_auth_method, <<"SCRAM-SHA-1-PLUS">>} | Config]).
  252: 
  253: log_one_scram_sha224_plus(Config) ->
  254:     log_one_scram_plus([{escalus_auth_method, <<"SCRAM-SHA-224-PLUS">>} | Config]).
  255: 
  256: log_one_scram_sha256_plus(Config) ->
  257:     log_one_scram_plus([{escalus_auth_method, <<"SCRAM-SHA-256-PLUS">>} | Config]).
  258: 
  259: log_one_scram_sha384_plus(Config) ->
  260:     log_one_scram_plus([{escalus_auth_method, <<"SCRAM-SHA-384-PLUS">>} | Config]).
  261: 
  262: log_one_scram_sha512_plus(Config) ->
  263:     log_one_scram_plus([{escalus_auth_method, <<"SCRAM-SHA-512-PLUS">>} | Config]).
  264: 
  265: configure_sha1_log_with_sha1(Config) ->
  266:     configure_and_log_scram(Config, sha, <<"SCRAM-SHA-1">>).
  267: 
  268: configure_sha224_log_with_sha224(Config) ->
  269:     configure_and_log_scram(Config, sha224, <<"SCRAM-SHA-224">>).
  270: 
  271: configure_sha256_log_with_sha256(Config) ->
  272:     configure_and_log_scram(Config, sha256, <<"SCRAM-SHA-256">>).
  273: 
  274: configure_sha384_log_with_sha384(Config) ->
  275:     configure_and_log_scram(Config, sha384, <<"SCRAM-SHA-384">>).
  276: 
  277: configure_sha512_log_with_sha512(Config) ->
  278:     configure_and_log_scram(Config, sha512, <<"SCRAM-SHA-512">>).
  279: 
  280: configure_sha1_log_with_sha1_plus(Config) ->
  281:     configure_and_log_scram_plus(Config, sha, <<"SCRAM-SHA-1-PLUS">>).
  282: 
  283: configure_sha224_log_with_sha224_plus(Config) ->
  284:     configure_and_log_scram_plus(Config, sha224, <<"SCRAM-SHA-224-PLUS">>).
  285: 
  286: configure_sha256_log_with_sha256_plus(Config) ->
  287:     configure_and_log_scram_plus(Config, sha256, <<"SCRAM-SHA-256-PLUS">>).
  288: 
  289: configure_sha384_log_with_sha384_plus(Config) ->
  290:     configure_and_log_scram_plus(Config, sha384, <<"SCRAM-SHA-384-PLUS">>).
  291: 
  292: configure_sha512_log_with_sha512_plus(Config) ->
  293:     configure_and_log_scram_plus(Config, sha512, <<"SCRAM-SHA-512-PLUS">>).
  294: 
  295: configure_sha1_fail_log_with_sha224(Config) ->
  296:     configure_and_fail_log_scram(Config, sha, <<"SCRAM-SHA-224">>).
  297: 
  298: configure_sha224_fail_log_with_sha256(Config) ->
  299:     configure_and_fail_log_scram(Config, sha224, <<"SCRAM-SHA-256">>).
  300: 
  301: configure_sha256_fail_log_with_sha384(Config) ->
  302:     configure_and_fail_log_scram(Config, sha256, <<"SCRAM-SHA-384">>).
  303: 
  304: configure_sha384_fail_log_with_sha512(Config) ->
  305:     configure_and_fail_log_scram(Config, sha384, <<"SCRAM-SHA-512">>).
  306: 
  307: configure_sha512_fail_log_with_sha1(Config) ->
  308:     configure_and_fail_log_scram(Config, sha512, <<"SCRAM-SHA-1">>).
  309: 
  310: %%
  311: %% configure_sha*_plus_fail_log_with_sha* tests are succeeding due to the fact that
  312: %% escalus, when configured with fast_tls and login with scram, sets channel binding
  313: %% flag to 'y'. This indicates that escalus supports channel binding but the server
  314: %% does not. The server did advertise the SCRAM PLUS mechanism, so this flag is
  315: %% incorrect and could be the result of the man-in-the-middle attack attempting to
  316: %% downgrade the authentication mechanism. Because of that, the authentication should fail.
  317: %%
  318: configure_sha1_plus_fail_log_with_sha1(Config) ->
  319:     configure_scram_plus_and_fail_log_scram(Config, sha, <<"SCRAM-SHA-1">>).
  320: 
  321: configure_sha224_plus_fail_log_with_sha224(Config) ->
  322:     configure_scram_plus_and_fail_log_scram(Config, sha224, <<"SCRAM-SHA-224">>).
  323: 
  324: configure_sha256_plus_fail_log_with_sha256(Config) ->
  325:     configure_scram_plus_and_fail_log_scram(Config, sha256, <<"SCRAM-SHA-256">>).
  326: 
  327: configure_sha384_plus_fail_log_with_sha384(Config) ->
  328:     configure_scram_plus_and_fail_log_scram(Config, sha384, <<"SCRAM-SHA-384">>).
  329: 
  330: configure_sha512_plus_fail_log_with_sha512(Config) ->
  331:     configure_scram_plus_and_fail_log_scram(Config, sha512, <<"SCRAM-SHA-512">>).
  332: 
  333: log_non_existent_plain(Config) ->
  334:     {auth_failed, _, Xmlel} = log_non_existent(Config),
  335:     #xmlel{name = <<"failure">>} = Xmlel,
  336:     #xmlel{} = exml_query:subelement(Xmlel, <<"not-authorized">>).
  337: 
  338: log_non_existent_digest(Config) ->
  339:     R = log_non_existent([{escalus_auth_method, <<"DIGEST-MD5">>} | Config]),
  340:     {expected_challenge, _, _} = R.
  341: 
  342: log_non_existent_scram(Config) ->
  343:     R = log_non_existent([{escalus_auth_method, <<"SCRAM-SHA-1">>} | Config]),
  344:     {expected_challenge, _, _} = R.
  345: 
  346: log_non_existent(Config) ->
  347:     [{kate, UserSpec}] = escalus_users:get_users([kate]),
  348:     {error, {connection_step_failed, _, R}} = escalus_client:start(Config, UserSpec, <<"res">>),
  349:     R.
  350: 
  351: blocked_user(Config) ->
  352:     [{_, Spec}] = escalus_users:get_users([alice]),
  353:     Config1 = set_acl_for_blocking(Config, Spec),
  354:     try
  355:         {ok, _Alice, _Spec2, _Features} = escalus_connection:start(Spec),
  356:         ct:fail("Alice authenticated but shouldn't")
  357:     catch
  358:         error:{assertion_failed, assert, is_iq_result, Stanza, _Bin} ->
  359:             <<"cancel">> = exml_query:path(Stanza, [{element, <<"error">>}, {attr, <<"type">>}])
  360:     after
  361:         unset_acl_for_blocking(Config1)
  362:     end,
  363:     ok.
  364: 
  365: messages_story(Config) ->
  366:     escalus:story(Config, [{alice, 1}, {bob, 1}], fun(Alice, Bob) ->
  367: 
  368:         % Alice sends a message to Bob
  369:         escalus_client:send(Alice, escalus_stanza:chat_to(Bob, <<"Hi!">>)),
  370: 
  371:         % Bob gets the message
  372:         escalus_assert:is_chat_message(<<"Hi!">>, escalus_client:wait_for_stanza(Bob))
  373: 
  374:     end).
  375: 
  376: message_zlib_limit(Config) ->
  377:     escalus:story(Config, [{alice, 1}], fun(Alice) ->
  378:         [{_, Spec}] = escalus_users:get_users([hacker]),
  379:         {ok, Hacker, _Features} = escalus_connection:start(Spec),
  380: 
  381:         ManySpaces = [ 32 || _N <- lists:seq(1, 10*1024) ],
  382: 
  383:         escalus:send(Hacker, escalus_stanza:chat_to(Alice, ManySpaces)),
  384: 
  385:         escalus:assert(is_stream_error, [<<"policy-violation">>, <<"child element too big">>],
  386:                        escalus:wait_for_stanza(Hacker)),
  387:         escalus:assert(is_stream_end, escalus:wait_for_stanza(Hacker))
  388: 
  389:     end).
  390: 
  391: %%--------------------------------------------------------------------
  392: %% Helpers
  393: %%--------------------------------------------------------------------
  394: 
  395: configure_c2s_listener(Config) ->
  396:     C2SPort = ct:get_config({hosts, mim, c2s_port}),
  397:     [C2SListener = #{tls := TLSOpts}] =
  398:         mongoose_helper:get_listeners(mim(), #{port => C2SPort, module => ejabberd_c2s}),
  399:     %% replace starttls with tls
  400:     NewTLSOpts = [tls | TLSOpts -- [starttls]],
  401:     mongoose_helper:restart_listener(mim(), C2SListener#{tls := NewTLSOpts}),
  402:     [{c2s_listener, C2SListener} | Config].
  403: 
  404: create_tls_users(Config) ->
  405:    Config1 = escalus:create_users(Config, escalus:get_users([alice, neustradamus])),
  406:    Users = proplists:get_value(escalus_users, Config1, []),
  407:    NSpec = lists:keydelete(starttls, 1, proplists:get_value(neustradamus, Users)),
  408:    NSpec2 = {neustradamus, lists:keystore(ssl, 1, NSpec, {ssl, true})},
  409:    NewUsers = lists:keystore(neustradamus, 1, Users, NSpec2),
  410:    AliceSpec = proplists:get_value(alice, Users),
  411:    AliceSpec2 = {alice, lists:keystore(ssl, 1, AliceSpec, {ssl, true})},
  412:    NewUsers2 = lists:keystore(alice, 1, NewUsers, AliceSpec2),
  413:    lists:keystore(escalus_users, 1, Config1, {escalus_users, NewUsers2}).
  414: 
  415: delete_tls_users(Config) ->
  416:     escalus:delete_users(Config, escalus:get_users([alice, neustradamus])).
  417: 
  418: assert_password_format(GroupName, Config) ->
  419:     Users = proplists:get_value(escalus_users, Config),
  420:     [verify_format(GroupName, User) || User <- Users],
  421:     Config.
  422: 
  423: verify_format(GroupName, {_User, Props}) ->
  424:     Username = escalus_utils:jid_to_lower(proplists:get_value(username, Props)),
  425:     Server = proplists:get_value(server, Props),
  426:     Password = proplists:get_value(password, Props),
  427:     JID = mongoose_helper:make_jid(Username, Server),
  428:     {SPassword, _} = rpc(mim(), ejabberd_auth, get_passterm_with_authmodule, [host_type(), JID]),
  429:     do_verify_format(GroupName, Password, SPassword).
  430: 
  431: 
  432: do_verify_format(GroupName, _P, #{iteration_count := _IC,
  433:                                   sha    := #{salt := _, stored_key := _, server_key := _},
  434:                                   sha224 := #{salt := _, stored_key := _, server_key := _},
  435:                                   sha256 := #{salt := _, stored_key := _, server_key := _},
  436:                                   sha384 := #{salt := _, stored_key := _, server_key := _},
  437:                                   sha512 := #{salt := _, stored_key := _, server_key := _}}) when
  438:                  GroupName == login_scram orelse GroupName == scram ->
  439:     true;
  440: do_verify_format({scram, Sha}, _Password, ScramMap = #{iteration_count := _IC}) ->
  441:    maps:is_key(Sha, ScramMap);
  442: do_verify_format(login_scram, _Password, SPassword) ->
  443:     %% returned password is a tuple containing scram data
  444:     {_, _, _, _} = SPassword;
  445: do_verify_format(_, Password, SPassword) ->
  446:     Password = SPassword.
  447: 
  448: set_acl_for_blocking(Config, Spec) ->
  449:     User = proplists:get_value(username, Spec),
  450:     LUser = jid:nodeprep(User),
  451:     mongoose_helper:backup_and_set_config_option(Config, [{acl, host_type()}, blocked],
  452:                                                  [#{user => LUser, match => current_domain}]).
  453: 
  454: unset_acl_for_blocking(Config) ->
  455:     mongoose_helper:restore_config_option(Config, [{acl, host_type()}, blocked]).
  456: 
  457: configure_and_log_scram(Config, Sha, Mech) ->
  458:     set_scram_sha(Config, Sha),
  459:     log_one([{escalus_auth_method, Mech} | Config]).
  460: 
  461: configure_and_log_scram_plus(Config, Sha, Mech) ->
  462:     set_scram_sha(Config, Sha),
  463:     log_one_scram_plus([{escalus_auth_method, Mech} | Config]).
  464: 
  465: configure_and_fail_log_scram(Config, Sha, Mech) ->
  466:     set_scram_sha(Config, Sha),
  467:     {expected_challenge, _, _} = fail_log_one([{escalus_auth_method, Mech} | Config]).
  468: 
  469: configure_scram_plus_and_fail_log_scram(Config, Sha, Mech) ->
  470:     set_scram_sha(Config, Sha),
  471:     {expected_challenge, _, _} = fail_log_one_scram_plus([{escalus_auth_method, Mech} | Config]).
  472: 
  473: set_scram_sha(Config, Sha) ->
  474:     NewAuthOpts = mongoose_helper:auth_opts_with_password_format({scram, [Sha]}),
  475:     mongoose_helper:change_config_option(Config, {auth, host_type()}, NewAuthOpts),
  476:     assert_password_format({scram, Sha}, Config).
  477: 
  478: fail_log_one(Config) ->
  479:     [{alice, UserSpec}] = escalus_users:get_users([alice]),
  480:     {error, {connection_step_failed, _, R}} = escalus_client:start(Config, UserSpec, <<"res">>),
  481:     R.
  482: 
  483: fail_log_one_scram_plus(Config) ->
  484:     [{neustradamus, UserSpec}] = escalus_users:get_users([neustradamus]),
  485:     {error, {connection_step_failed, _, R}} = escalus_client:start(Config, UserSpec, <<"res">>),
  486:     R.
  487: 
  488: are_sasl_scram_modules_supported() ->
  489:     ScramModules = [cyrsasl_scram_sha1, cyrsasl_scram_sha224, cyrsasl_scram_sha256,
  490:                     cyrsasl_scram_sha384, cyrsasl_scram_sha512],
  491:     IsSupported = [mongoose_helper:supports_sasl_module(Module) || Module <- ScramModules],
  492:     [true, true, true, true, true] == IsSupported.
  493: 
  494: restore_c2s(Config) ->
  495:    C2SListener = proplists:get_value(c2s_listener, Config),
  496:    mongoose_helper:restart_listener(mim(), C2SListener).