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:     [{_U, Props}] = escalus_users:get_users([hacker]),
  194:     Port = proplists:get_value(port, Props),
  195:     case mongoose_helper:get_listeners(mim(), #{port => Port}) of
  196:         [_Listener] ->
  197:             escalus:create_users(Config, escalus:get_users([hacker])),
  198:             escalus:init_per_testcase(message_zlib_limit, Config);
  199:         [] ->
  200:             {skip, port_not_configured_on_server}
  201:     end;
  202: init_per_testcase(CaseName, Config) ->
  203:     escalus:init_per_testcase(CaseName, Config).
  204: 
  205: end_per_testcase(message_zlib_limit, Config) ->
  206:     escalus:delete_users(Config, escalus:get_users([hacker]));
  207: end_per_testcase(CaseName, Config) ->
  208:     escalus:end_per_testcase(CaseName, Config).
  209: 
  210: %%--------------------------------------------------------------------
  211: %% Message tests
  212: %%--------------------------------------------------------------------
  213: 
  214: log_one(Config) ->
  215:     escalus:fresh_story(Config, [{alice, 1}], fun(Alice) ->
  216: 
  217:         escalus_client:send(Alice, escalus_stanza:chat_to(Alice, <<"Hi!">>)),
  218:         escalus:assert(is_chat_message, [<<"Hi!">>], escalus_client:wait_for_stanza(Alice))
  219: 
  220:         end).
  221: 
  222: log_one_scram_plus(Config) ->
  223:     escalus:fresh_story(Config, [{neustradamus, 1}], fun(Neustradamus) ->
  224: 
  225:         escalus_client:send(Neustradamus, escalus_stanza:chat_to(Neustradamus, <<"Hi!">>)),
  226:         escalus:assert(is_chat_message, [<<"Hi!">>], escalus_client:wait_for_stanza(Neustradamus))
  227: 
  228:         end).
  229: 
  230: log_one_digest(Config) ->
  231:     log_one([{escalus_auth_method, <<"DIGEST-MD5">>} | Config]).
  232: 
  233: log_one_scram_sha1(Config) ->
  234:     log_one([{escalus_auth_method, <<"SCRAM-SHA-1">>} | Config]).
  235: 
  236: log_one_scram_sha224(Config) ->
  237:     log_one([{escalus_auth_method, <<"SCRAM-SHA-224">>} | Config]).
  238: 
  239: log_one_scram_sha256(Config) ->
  240:     log_one([{escalus_auth_method, <<"SCRAM-SHA-256">>} | Config]).
  241: 
  242:  log_one_scram_sha384(Config) ->
  243:     log_one([{escalus_auth_method, <<"SCRAM-SHA-384">>} | Config]).
  244: 
  245: log_one_scram_sha512(Config) ->
  246:     log_one([{escalus_auth_method, <<"SCRAM-SHA-512">>} | Config]).
  247: 
  248: log_one_scram_sha1_plus(Config) ->
  249:     log_one_scram_plus([{escalus_auth_method, <<"SCRAM-SHA-1-PLUS">>} | Config]).
  250: 
  251: log_one_scram_sha224_plus(Config) ->
  252:     log_one_scram_plus([{escalus_auth_method, <<"SCRAM-SHA-224-PLUS">>} | Config]).
  253: 
  254: log_one_scram_sha256_plus(Config) ->
  255:     log_one_scram_plus([{escalus_auth_method, <<"SCRAM-SHA-256-PLUS">>} | Config]).
  256: 
  257: log_one_scram_sha384_plus(Config) ->
  258:     log_one_scram_plus([{escalus_auth_method, <<"SCRAM-SHA-384-PLUS">>} | Config]).
  259: 
  260: log_one_scram_sha512_plus(Config) ->
  261:     log_one_scram_plus([{escalus_auth_method, <<"SCRAM-SHA-512-PLUS">>} | Config]).
  262: 
  263: configure_sha1_log_with_sha1(Config) ->
  264:     configure_and_log_scram(Config, sha, <<"SCRAM-SHA-1">>).
  265: 
  266: configure_sha224_log_with_sha224(Config) ->
  267:     configure_and_log_scram(Config, sha224, <<"SCRAM-SHA-224">>).
  268: 
  269: configure_sha256_log_with_sha256(Config) ->
  270:     configure_and_log_scram(Config, sha256, <<"SCRAM-SHA-256">>).
  271: 
  272: configure_sha384_log_with_sha384(Config) ->
  273:     configure_and_log_scram(Config, sha384, <<"SCRAM-SHA-384">>).
  274: 
  275: configure_sha512_log_with_sha512(Config) ->
  276:     configure_and_log_scram(Config, sha512, <<"SCRAM-SHA-512">>).
  277: 
  278: configure_sha1_log_with_sha1_plus(Config) ->
  279:     configure_and_log_scram_plus(Config, sha, <<"SCRAM-SHA-1-PLUS">>).
  280: 
  281: configure_sha224_log_with_sha224_plus(Config) ->
  282:     configure_and_log_scram_plus(Config, sha224, <<"SCRAM-SHA-224-PLUS">>).
  283: 
  284: configure_sha256_log_with_sha256_plus(Config) ->
  285:     configure_and_log_scram_plus(Config, sha256, <<"SCRAM-SHA-256-PLUS">>).
  286: 
  287: configure_sha384_log_with_sha384_plus(Config) ->
  288:     configure_and_log_scram_plus(Config, sha384, <<"SCRAM-SHA-384-PLUS">>).
  289: 
  290: configure_sha512_log_with_sha512_plus(Config) ->
  291:     configure_and_log_scram_plus(Config, sha512, <<"SCRAM-SHA-512-PLUS">>).
  292: 
  293: configure_sha1_fail_log_with_sha224(Config) ->
  294:     configure_and_fail_log_scram(Config, sha, <<"SCRAM-SHA-224">>).
  295: 
  296: configure_sha224_fail_log_with_sha256(Config) ->
  297:     configure_and_fail_log_scram(Config, sha224, <<"SCRAM-SHA-256">>).
  298: 
  299: configure_sha256_fail_log_with_sha384(Config) ->
  300:     configure_and_fail_log_scram(Config, sha256, <<"SCRAM-SHA-384">>).
  301: 
  302: configure_sha384_fail_log_with_sha512(Config) ->
  303:     configure_and_fail_log_scram(Config, sha384, <<"SCRAM-SHA-512">>).
  304: 
  305: configure_sha512_fail_log_with_sha1(Config) ->
  306:     configure_and_fail_log_scram(Config, sha512, <<"SCRAM-SHA-1">>).
  307: 
  308: %%
  309: %% configure_sha*_plus_fail_log_with_sha* tests are succeeding due to the fact that
  310: %% escalus, when configured with fast_tls and login with scram, sets channel binding
  311: %% flag to 'y'. This indicates that escalus supports channel binding but the server
  312: %% does not. The server did advertise the SCRAM PLUS mechanism, so this flag is
  313: %% incorrect and could be the result of the man-in-the-middle attack attempting to
  314: %% downgrade the authentication mechanism. Because of that, the authentication should fail.
  315: %%
  316: configure_sha1_plus_fail_log_with_sha1(Config) ->
  317:     configure_scram_plus_and_fail_log_scram(Config, sha, <<"SCRAM-SHA-1">>).
  318: 
  319: configure_sha224_plus_fail_log_with_sha224(Config) ->
  320:     configure_scram_plus_and_fail_log_scram(Config, sha224, <<"SCRAM-SHA-224">>).
  321: 
  322: configure_sha256_plus_fail_log_with_sha256(Config) ->
  323:     configure_scram_plus_and_fail_log_scram(Config, sha256, <<"SCRAM-SHA-256">>).
  324: 
  325: configure_sha384_plus_fail_log_with_sha384(Config) ->
  326:     configure_scram_plus_and_fail_log_scram(Config, sha384, <<"SCRAM-SHA-384">>).
  327: 
  328: configure_sha512_plus_fail_log_with_sha512(Config) ->
  329:     configure_scram_plus_and_fail_log_scram(Config, sha512, <<"SCRAM-SHA-512">>).
  330: 
  331: log_non_existent_plain(Config) ->
  332:     {auth_failed, _, Xmlel} = log_non_existent(Config),
  333:     #xmlel{name = <<"failure">>} = Xmlel,
  334:     #xmlel{} = exml_query:subelement(Xmlel, <<"not-authorized">>).
  335: 
  336: log_non_existent_digest(Config) ->
  337:     R = log_non_existent([{escalus_auth_method, <<"DIGEST-MD5">>} | Config]),
  338:     {expected_challenge, _, _} = R.
  339: 
  340: log_non_existent_scram(Config) ->
  341:     R = log_non_existent([{escalus_auth_method, <<"SCRAM-SHA-1">>} | Config]),
  342:     {expected_challenge, _, _} = R.
  343: 
  344: log_non_existent(Config) ->
  345:     [{kate, UserSpec}] = escalus_users:get_users([kate]),
  346:     {error, {connection_step_failed, _, R}} = escalus_client:start(Config, UserSpec, <<"res">>),
  347:     R.
  348: 
  349: blocked_user(Config) ->
  350:     [{_, Spec}] = escalus_users:get_users([alice]),
  351:     Config1 = set_acl_for_blocking(Config, Spec),
  352:     try
  353:         {ok, _Alice, _Spec2, _Features} = escalus_connection:start(Spec),
  354:         ct:fail("Alice authenticated but shouldn't")
  355:     catch
  356:         error:{assertion_failed, assert, is_iq_result, Stanza, _Bin} ->
  357:             <<"cancel">> = exml_query:path(Stanza, [{element, <<"error">>}, {attr, <<"type">>}])
  358:     after
  359:         unset_acl_for_blocking(Config1)
  360:     end,
  361:     ok.
  362: 
  363: messages_story(Config) ->
  364:     escalus:story(Config, [{alice, 1}, {bob, 1}], fun(Alice, Bob) ->
  365: 
  366:         % Alice sends a message to Bob
  367:         escalus_client:send(Alice, escalus_stanza:chat_to(Bob, <<"Hi!">>)),
  368: 
  369:         % Bob gets the message
  370:         escalus_assert:is_chat_message(<<"Hi!">>, escalus_client:wait_for_stanza(Bob))
  371: 
  372:     end).
  373: 
  374: message_zlib_limit(Config) ->
  375:     escalus:story(Config, [{alice, 1}], fun(Alice) ->
  376:         [{_, Spec}] = escalus_users:get_users([hacker]),
  377:         {ok, Hacker, _Features} = escalus_connection:start(Spec),
  378: 
  379:         ManySpaces = [ 32 || _N <- lists:seq(1, 10*1024) ],
  380: 
  381:         escalus:send(Hacker, escalus_stanza:chat_to(Alice, ManySpaces)),
  382: 
  383:         escalus:assert(is_stream_error, [<<"policy-violation">>, <<"child element too big">>],
  384:                        escalus:wait_for_stanza(Hacker)),
  385:         escalus:assert(is_stream_end, escalus:wait_for_stanza(Hacker))
  386: 
  387:     end).
  388: 
  389: %%--------------------------------------------------------------------
  390: %% Helpers
  391: %%--------------------------------------------------------------------
  392: 
  393: configure_c2s_listener(Config) ->
  394:     C2SPort = ct:get_config({hosts, mim, c2s_port}),
  395:     [C2SListener = #{tls := TLSOpts}] =
  396:         mongoose_helper:get_listeners(mim(), #{port => C2SPort, module => ejabberd_c2s}),
  397:     %% replace starttls with tls
  398:     NewTLSOpts = TLSOpts#{mode := tls},
  399:     mongoose_helper:restart_listener(mim(), C2SListener#{tls := NewTLSOpts}),
  400:     [{c2s_listener, C2SListener} | Config].
  401: 
  402: create_tls_users(Config) ->
  403:    Config1 = escalus:create_users(Config, escalus:get_users([alice, neustradamus])),
  404:    Users = proplists:get_value(escalus_users, Config1, []),
  405:    NSpec = lists:keydelete(starttls, 1, proplists:get_value(neustradamus, Users)),
  406:    NSpec2 = {neustradamus, lists:keystore(ssl, 1, NSpec, {ssl, true})},
  407:    NewUsers = lists:keystore(neustradamus, 1, Users, NSpec2),
  408:    AliceSpec = proplists:get_value(alice, Users),
  409:    AliceSpec2 = {alice, lists:keystore(ssl, 1, AliceSpec, {ssl, true})},
  410:    NewUsers2 = lists:keystore(alice, 1, NewUsers, AliceSpec2),
  411:    lists:keystore(escalus_users, 1, Config1, {escalus_users, NewUsers2}).
  412: 
  413: delete_tls_users(Config) ->
  414:     escalus:delete_users(Config, escalus:get_users([alice, neustradamus])).
  415: 
  416: assert_password_format(GroupName, Config) ->
  417:     Users = proplists:get_value(escalus_users, Config),
  418:     [verify_format(GroupName, User) || User <- Users],
  419:     Config.
  420: 
  421: verify_format(GroupName, {_User, Props}) ->
  422:     Username = escalus_utils:jid_to_lower(proplists:get_value(username, Props)),
  423:     Server = proplists:get_value(server, Props),
  424:     Password = proplists:get_value(password, Props),
  425:     JID = mongoose_helper:make_jid(Username, Server),
  426:     {SPassword, _} = rpc(mim(), ejabberd_auth, get_passterm_with_authmodule, [host_type(), JID]),
  427:     do_verify_format(GroupName, Password, SPassword).
  428: 
  429: 
  430: do_verify_format(GroupName, _P, #{iteration_count := _IC,
  431:                                   sha    := #{salt := _, stored_key := _, server_key := _},
  432:                                   sha224 := #{salt := _, stored_key := _, server_key := _},
  433:                                   sha256 := #{salt := _, stored_key := _, server_key := _},
  434:                                   sha384 := #{salt := _, stored_key := _, server_key := _},
  435:                                   sha512 := #{salt := _, stored_key := _, server_key := _}}) when
  436:                  GroupName == login_scram orelse GroupName == scram ->
  437:     true;
  438: do_verify_format({scram, Sha}, _Password, ScramMap = #{iteration_count := _IC}) ->
  439:    maps:is_key(Sha, ScramMap);
  440: do_verify_format(login_scram, _Password, SPassword) ->
  441:     %% returned password is a tuple containing scram data
  442:     {_, _, _, _} = SPassword;
  443: do_verify_format(_, Password, SPassword) ->
  444:     Password = SPassword.
  445: 
  446: set_acl_for_blocking(Config, Spec) ->
  447:     User = proplists:get_value(username, Spec),
  448:     LUser = jid:nodeprep(User),
  449:     mongoose_helper:backup_and_set_config_option(Config, [{acl, host_type()}, blocked],
  450:                                                  [#{user => LUser, match => current_domain}]).
  451: 
  452: unset_acl_for_blocking(Config) ->
  453:     mongoose_helper:restore_config_option(Config, [{acl, host_type()}, blocked]).
  454: 
  455: configure_and_log_scram(Config, Sha, Mech) ->
  456:     set_scram_sha(Config, Sha),
  457:     log_one([{escalus_auth_method, Mech} | Config]).
  458: 
  459: configure_and_log_scram_plus(Config, Sha, Mech) ->
  460:     set_scram_sha(Config, Sha),
  461:     log_one_scram_plus([{escalus_auth_method, Mech} | Config]).
  462: 
  463: configure_and_fail_log_scram(Config, Sha, Mech) ->
  464:     set_scram_sha(Config, Sha),
  465:     {expected_challenge, _, _} = fail_log_one([{escalus_auth_method, Mech} | Config]).
  466: 
  467: configure_scram_plus_and_fail_log_scram(Config, Sha, Mech) ->
  468:     set_scram_sha(Config, Sha),
  469:     {expected_challenge, _, _} = fail_log_one_scram_plus([{escalus_auth_method, Mech} | Config]).
  470: 
  471: set_scram_sha(Config, Sha) ->
  472:     NewAuthOpts = mongoose_helper:auth_opts_with_password_format({scram, [Sha]}),
  473:     mongoose_helper:change_config_option(Config, {auth, host_type()}, NewAuthOpts),
  474:     assert_password_format({scram, Sha}, Config).
  475: 
  476: fail_log_one(Config) ->
  477:     [{alice, UserSpec}] = escalus_users:get_users([alice]),
  478:     {error, {connection_step_failed, _, R}} = escalus_client:start(Config, UserSpec, <<"res">>),
  479:     R.
  480: 
  481: fail_log_one_scram_plus(Config) ->
  482:     [{neustradamus, UserSpec}] = escalus_users:get_users([neustradamus]),
  483:     {error, {connection_step_failed, _, R}} = escalus_client:start(Config, UserSpec, <<"res">>),
  484:     R.
  485: 
  486: are_sasl_scram_modules_supported() ->
  487:     ScramModules = [cyrsasl_scram_sha1, cyrsasl_scram_sha224, cyrsasl_scram_sha256,
  488:                     cyrsasl_scram_sha384, cyrsasl_scram_sha512],
  489:     IsSupported = [mongoose_helper:supports_sasl_module(Module) || Module <- ScramModules],
  490:     [true, true, true, true, true] == IsSupported.
  491: 
  492: restore_c2s(Config) ->
  493:    C2SListener = proplists:get_value(c2s_listener, Config),
  494:    mongoose_helper:restart_listener(mim(), C2SListener).