1: -module(sasl2_SUITE).
    2: 
    3: -compile([export_all, nowarn_export_all]).
    4: 
    5: -include_lib("stdlib/include/assert.hrl").
    6: -include_lib("exml/include/exml.hrl").
    7: -include_lib("escalus/include/escalus_xmlns.hrl").
    8: 
    9: -define(NS_SASL_2, <<"urn:xmpp:sasl:2">>).
   10: 
   11: %%--------------------------------------------------------------------
   12: %% Suite configuration
   13: %%--------------------------------------------------------------------
   14: 
   15: all() ->
   16:     [
   17:      {group, all_tests}
   18:     ].
   19: 
   20: groups() ->
   21:     [
   22:      {all_tests, [parallel],
   23:       [
   24:        {group, basic},
   25:        {group, scram},
   26:        {group, stream_management}
   27:       ]},
   28:      {basic, [parallel],
   29:       [
   30:        server_does_not_announce_if_not_tls,
   31:        server_announces_sasl2_with_some_mechanism_and_inline_sm,
   32:        authenticate_stanza_has_invalid_mechanism,
   33:        user_agent_is_invalid,
   34:        user_agent_is_invalid_uuid_but_not_v4,
   35:        authenticate_with_plain,
   36:        authenticate_with_plain_and_user_agent_without_id,
   37:        authenticate_again_results_in_stream_error
   38:       ]},
   39:      {scram, [parallel],
   40:       [
   41:        authenticate_with_scram_abort,
   42:        authenticate_with_scram_bad_abort,
   43:        authenticate_with_scram_bad_response,
   44:        authenticate_with_scram
   45:       ]},
   46:      {stream_management, [parallel],
   47:       [
   48:        sm_failure_missing_previd_does_not_stop_sasl2,
   49:        sm_failure_invalid_h_does_not_stop_sasl2,
   50:        sm_failure_exceeding_h_does_not_stop_sasl2,
   51:        sm_failure_unknown_smid_does_not_stop_sasl2,
   52:        sm_is_bound_at_sasl2_success
   53:       ]}
   54:     ].
   55: 
   56: %%--------------------------------------------------------------------
   57: %% Init & teardown
   58: %%--------------------------------------------------------------------
   59: 
   60: init_per_suite(Config) ->
   61:     Config1 = load_sasl_extensible(Config),
   62:     escalus:init_per_suite(Config1).
   63: 
   64: end_per_suite(Config) ->
   65:     escalus_fresh:clean(),
   66:     dynamic_modules:restore_modules(Config),
   67:     escalus:end_per_suite(Config).
   68: 
   69: init_per_group(scram, Config) ->
   70:     case mongoose_helper:supports_sasl_module(cyrsasl_scram_sha256) of
   71:         false ->
   72:             {skip, "scram password type not supported"};
   73:         true ->
   74:             Config
   75:     end;
   76: init_per_group(_GroupName, Config) ->
   77:     Config.
   78: 
   79: end_per_group(_GroupName, Config) ->
   80:     Config.
   81: 
   82: init_per_testcase(Name, Config) ->
   83:     escalus:init_per_testcase(Name, Config).
   84: 
   85: end_per_testcase(Name, Config) ->
   86:     escalus:end_per_testcase(Name, Config).
   87: 
   88: load_sasl_extensible(Config) ->
   89:     HostType = domain_helper:host_type(),
   90:     Config1 = dynamic_modules:save_modules(HostType, Config),
   91:     sasl2_helper:load_all_sasl2_modules(HostType),
   92:     Config1.
   93: 
   94: %%--------------------------------------------------------------------
   95: %% tests
   96: %%--------------------------------------------------------------------
   97: 
   98: server_does_not_announce_if_not_tls(Config) ->
   99:     Steps = [connect_non_tls_user, start_stream_get_features],
  100:     #{features := Features} = sasl2_helper:apply_steps(Steps, Config),
  101:     Sasl2 = exml_query:path(Features, [{element_with_ns, <<"authentication">>, ?NS_SASL_2}]),
  102:     ?assertEqual(undefined, Sasl2).
  103: 
  104: server_announces_sasl2_with_some_mechanism_and_inline_sm(Config) ->
  105:     Steps = [create_connect_tls, start_stream_get_features],
  106:     #{features := Features} = sasl2_helper:apply_steps(Steps, Config),
  107:     Sasl2 = exml_query:path(Features, [{element_with_ns, <<"authentication">>, ?NS_SASL_2}]),
  108:     ?assertNotEqual(undefined, Sasl2),
  109:     Mechs = exml_query:paths(Sasl2, [{element, <<"mechanism">>}]),
  110:     ?assertNotEqual([], Mechs),
  111:     Sm = exml_query:path(Sasl2, [{element, <<"inline">>},
  112:                                  {element_with_ns, <<"sm">>, ?NS_STREAM_MGNT_3}]),
  113:     ?assertNotEqual(undefined, Sm).
  114: 
  115: authenticate_stanza_has_invalid_mechanism(Config) ->
  116:     Steps = [start_new_user, send_invalid_mech_auth_stanza],
  117:     #{answer := Response} = sasl2_helper:apply_steps(Steps, Config),
  118:     ?assertMatch(#xmlel{name = <<"failure">>, attrs = [{<<"xmlns">>, ?NS_SASL_2}]}, Response).
  119: 
  120: user_agent_is_invalid(Config) ->
  121:     Steps = [start_new_user, send_bad_user_agent],
  122:     #{answer := Response} = sasl2_helper:apply_steps(Steps, Config),
  123:     escalus:assert(is_stream_error, [<<"policy-violation">>, <<>>], Response).
  124: 
  125: user_agent_is_invalid_uuid_but_not_v4(Config) ->
  126:     Steps = [start_new_user, send_bad_user_agent_uuid],
  127:     #{answer := Response} = sasl2_helper:apply_steps(Steps, Config),
  128:     escalus:assert(is_stream_error, [<<"policy-violation">>, <<>>], Response).
  129: 
  130: authenticate_with_plain(Config) ->
  131:     Steps = [start_new_user, plain_authentication, receive_features],
  132:     auth_with_plain(Steps, Config).
  133: 
  134: authenticate_with_plain_and_user_agent_without_id(Config) ->
  135:     Steps = [start_new_user, plain_auth_user_agent_without_id, receive_features],
  136:     auth_with_plain(Steps, Config).
  137: 
  138: auth_with_plain(Steps, Config) ->
  139:     #{answer := Success, features := Features} = sasl2_helper:apply_steps(Steps, Config),
  140:     ?assertMatch(#xmlel{name = <<"success">>, attrs = [{<<"xmlns">>, ?NS_SASL_2}]}, Success),
  141:     CData = exml_query:path(Success, [{element, <<"additional-data">>}, cdata], <<>>),
  142:     ?assert(is_binary(CData) andalso 0 =< byte_size(CData)),
  143:     Identifier = exml_query:path(Success, [{element, <<"authorization-identifier">>}, cdata], <<>>),
  144:     ?assertNotEqual(error, jid:from_binary(Identifier)),
  145:     ?assertMatch(#xmlel{name = <<"stream:features">>}, Features).
  146: 
  147: authenticate_with_scram_abort(Config) ->
  148:     Steps = [start_new_user, scram_step_1, scram_abort],
  149:     #{answer := Response} = sasl2_helper:apply_steps(Steps, Config),
  150:     ?assertMatch(#xmlel{name = <<"failure">>, attrs = [{<<"xmlns">>, ?NS_SASL_2}]}, Response),
  151:     Aborted = exml_query:path(Response, [{element_with_ns, <<"aborted">>, ?NS_SASL}]),
  152:     ?assertNotEqual(undefined, Aborted).
  153: 
  154: authenticate_with_scram_bad_abort(Config) ->
  155:     Steps = [start_new_user, scram_step_1, scram_bad_abort],
  156:     #{answer := Response} = sasl2_helper:apply_steps(Steps, Config),
  157:     escalus:assert(is_stream_error, [<<"invalid-namespace">>, <<>>], Response).
  158: 
  159: authenticate_with_scram_bad_response(Config) ->
  160:     Steps = [start_new_user, scram_step_1, scram_bad_ns_response],
  161:     #{answer := Response} = sasl2_helper:apply_steps(Steps, Config),
  162:     escalus:assert(is_stream_error, [<<"invalid-namespace">>, <<>>], Response).
  163: 
  164: authenticate_with_scram(Config) ->
  165:     Steps = [start_new_user, scram_step_1, scram_step_2, receive_features],
  166:     #{answer := Success, features := Features} = sasl2_helper:apply_steps(Steps, Config),
  167:     ?assertMatch(#xmlel{name = <<"success">>, attrs = [{<<"xmlns">>, ?NS_SASL_2}]}, Success),
  168:     CData = exml_query:path(Success, [{element, <<"additional-data">>}, cdata], <<>>),
  169:     ?assert(is_binary(CData) andalso 0 =< byte_size(CData)),
  170:     Identifier = exml_query:path(Success, [{element, <<"authorization-identifier">>}, cdata], <<>>),
  171:     ?assertNotEqual(error, jid:from_binary(Identifier)),
  172:     ?assertMatch(#xmlel{name = <<"stream:features">>}, Features).
  173: 
  174: authenticate_again_results_in_stream_error(Config) ->
  175:     Steps = [start_new_user, plain_authentication, receive_features, plain_authentication],
  176:     #{answer := Response} = sasl2_helper:apply_steps(Steps, Config),
  177:     escalus:assert(is_stream_error, [<<"policy-violation">>, <<>>], Response).
  178: 
  179: sm_failure_missing_previd_does_not_stop_sasl2(Config) ->
  180:     Steps = [create_user, buffer_messages_and_die, connect_tls, start_stream_get_features,
  181:              auth_with_resumption_missing_previd, receive_features],
  182:     #{answer := Success} = sasl2_helper:apply_steps(Steps, Config),
  183:     ?assertMatch(#xmlel{name = <<"success">>, attrs = [{<<"xmlns">>, ?NS_SASL_2}]}, Success),
  184:     Resumed = exml_query:path(Success, [{element_with_ns, <<"failed">>, ?NS_STREAM_MGNT_3}]),
  185:     escalus:assert(is_sm_failed, [<<"bad-request">>], Resumed).
  186: 
  187: sm_failure_invalid_h_does_not_stop_sasl2(Config) ->
  188:     Steps = [create_user, buffer_messages_and_die, connect_tls, start_stream_get_features,
  189:              auth_with_resumption_invalid_h, receive_features],
  190:     #{answer := Success} = sasl2_helper:apply_steps(Steps, Config),
  191:     ?assertMatch(#xmlel{name = <<"success">>, attrs = [{<<"xmlns">>, ?NS_SASL_2}]}, Success),
  192:     Resumed = exml_query:path(Success, [{element_with_ns, <<"failed">>, ?NS_STREAM_MGNT_3}]),
  193:     escalus:assert(is_sm_failed, [<<"bad-request">>], Resumed).
  194: 
  195: sm_failure_exceeding_h_does_not_stop_sasl2(Config) ->
  196:     Steps = [create_user, buffer_messages_and_die, connect_tls, start_stream_get_features,
  197:              auth_with_resumption_exceeding_h, receive_features],
  198:     #{answer := Success} = sasl2_helper:apply_steps(Steps, Config),
  199:     ?assertMatch(#xmlel{name = <<"success">>, attrs = [{<<"xmlns">>, ?NS_SASL_2}]}, Success),
  200:     Resumed = exml_query:path(Success, [{element_with_ns, <<"failed">>, ?NS_STREAM_MGNT_3}]),
  201:     escalus:assert(is_sm_failed, [<<"bad-request">>], Resumed).
  202: 
  203: sm_failure_unknown_smid_does_not_stop_sasl2(Config) ->
  204:     Steps = [create_user, buffer_messages_and_die, connect_tls, start_stream_get_features,
  205:              auth_with_resumption_unknown_smid, receive_features],
  206:     #{answer := Success} = sasl2_helper:apply_steps(Steps, Config),
  207:     ?assertMatch(#xmlel{name = <<"success">>, attrs = [{<<"xmlns">>, ?NS_SASL_2}]}, Success),
  208:     Resumed = exml_query:path(Success, [{element_with_ns, <<"failed">>, ?NS_STREAM_MGNT_3}]),
  209:     escalus:assert(is_sm_failed, [<<"item-not-found">>], Resumed).
  210: 
  211: sm_is_bound_at_sasl2_success(Config) ->
  212:     Steps = [create_user, buffer_messages_and_die, connect_tls, start_stream_get_features,
  213:              auth_with_resumption, has_no_more_stanzas, can_send_messages],
  214:     #{answer := Success, smid := SMID} = sasl2_helper:apply_steps(Steps, Config),
  215:     ?assertMatch(#xmlel{name = <<"success">>, attrs = [{<<"xmlns">>, ?NS_SASL_2}]}, Success),
  216:     Resumed = exml_query:path(Success, [{element_with_ns, <<"resumed">>, ?NS_STREAM_MGNT_3}]),
  217:     ?assert(escalus_pred:is_sm_resumed(SMID, Resumed)).