diff --git a/rebar.lock b/rebar.lock index 7cd3f42e7..8224e06df 100644 --- a/rebar.lock +++ b/rebar.lock @@ -11,6 +11,7 @@ {<<"fast_xml">>,{pkg,<<"fast_xml">>,<<"1.1.57">>},0}, {<<"fast_yaml">>,{pkg,<<"fast_yaml">>,<<"1.0.39">>},0}, {<<"idna">>,{pkg,<<"idna">>,<<"6.1.1">>},0}, + {<<"jiffy">>,{pkg,<<"jiffy">>,<<"1.1.2">>},1}, {<<"jose">>,{pkg,<<"jose">>,<<"1.11.12">>},0}, {<<"luerl">>,{pkg,<<"luerl">>,<<"1.2.3">>},0}, {<<"mqtree">>,{pkg,<<"mqtree">>,<<"1.0.19">>},0}, @@ -40,6 +41,7 @@ {<<"fast_xml">>, <<"31EFC0F9BCEDA92069704F7A25830407DA5DC3DAD1272B810D6F2E13E73CC11A">>}, {<<"fast_yaml">>, <<"2E71168091949BAB0E5F583B340A99072B4D22D93EB86624E7850A12B1517BE4">>}, {<<"idna">>, <<"8A63070E9F7D0C62EB9D9FCB360A7DE382448200FBBD1B106CC96D3D8099DF8D">>}, + {<<"jiffy">>, <<"A9B6C9A7EC268E7CF493D028F0A4C9144F59CCB878B1AFE42841597800840A1B">>}, {<<"jose">>, <<"06E62B467B61D3726CBC19E9B5489F7549C37993DE846DFB3EE8259F9ED208B3">>}, {<<"luerl">>, <<"DF25F41944E57A7C4D9EF09D238BC3E850276C46039CFC12B8BB42ECCF36FCB1">>}, {<<"mqtree">>, <<"D769C25F898810725FC7DB0DBFFE5F72098647048B1BE2E6D772F1C2F31D8476">>}, @@ -68,6 +70,7 @@ {<<"fast_xml">>, <<"EEC34E90ADACAFE467D5DDAB635A014DED73B98B4061554B2D1972173D929C39">>}, {<<"fast_yaml">>, <<"24C7B9AB9E2B9269D64E45F4A2A1280966ADB17D31E63365CFD3EE277FB0A78D">>}, {<<"idna">>, <<"92376EB7894412ED19AC475E4A86F7B413C1B9FBB5BD16DCCD57934157944CEA">>}, + {<<"jiffy">>, <<"BB61BC42A720BBD33CB09A410E48BB79A61012C74CB8B3E75F26D988485CF381">>}, {<<"jose">>, <<"31E92B653E9210B696765CDD885437457DE1ADD2A9011D92F8CF63E4641BAB7B">>}, {<<"luerl">>, <<"1B4B9D0CA5D7D280D1D2787A6A5EE9F5A212641B62BFF91556BAA53805DF3AED">>}, {<<"mqtree">>, <<"C81065715C49A1882812F80A5AE2D842E80DD3F2D130530DF35990248BF8CE3C">>}, diff --git a/test/ejabberd_SUITE.erl b/test/ejabberd_SUITE.erl index 476c1d014..1f69715cb 100644 --- a/test/ejabberd_SUITE.erl +++ b/test/ejabberd_SUITE.erl @@ -37,7 +37,7 @@ bind/1, auth/1, auth/2, open_session/1, open_session/2, zlib/1, starttls/1, starttls/2, close_socket/1, init_stream/1, auth_legacy/2, auth_legacy/3, tcp_connect/1, send_text/2, - set_roster/3, del_roster/1]). + set_roster/3, del_roster/1, connect_sasl2/2, auth_SASL2/3, auth_fast_token/4]). -include("suite.hrl"). suite() -> @@ -331,6 +331,9 @@ init_per_testcase(TestCase, OrigConfig) -> connect(Config); "auth_plain" -> connect(Config); + "auth_sasl2" -> + Jid = jid:encode(jid:make(User, Server)), + connect_sasl2(starttls(connect_sasl2(Config, Jid)), Jid); "auth_external" ++ _ -> connect(Config); "unauthenticated_" ++ _ -> @@ -433,6 +436,7 @@ db_tests(DB) when DB == mnesia; DB == redis -> [test_register, legacy_auth_tests(), auth_plain, + auth_sasl2, auth_md5, presence_broadcast, last, @@ -851,6 +855,26 @@ auth_plain(Config) -> {skipped, 'PLAIN_not_available'} end. +auth_sasl2(Config) -> + Mechs = ?config(mechs, Config), + case lists:member(<<"DIGEST-MD5">>, Mechs) of + true -> + Config2 = disconnect(auth_SASL2(<<"DIGEST-MD5">>, Config, false)), + case ?config(fast_token, Config2) of + <<>> -> disconnect(Config); + Token -> + User = ?config(user, Config), + Jid = jid:encode(jid:make(User, ?config(server, Config))), + Hash = crypto:mac(hmac, sha256, Token, <<"Initiator">>), + CalcToken = (<>), + Config3 = connect_sasl2(starttls(connect_sasl2(Config2, Jid)), Jid), + disconnect(auth_fast_token(<<"HT-SHA-256-NONE">>, CalcToken, Config3, false)) + end; + false -> + disconnect(Config), + {skipped, 'PLAIN_not_available'} + end. + auth_external(Config0) -> Config = connect(starttls(Config0)), disconnect(auth_SASL(<<"EXTERNAL">>, Config)). diff --git a/test/ejabberd_SUITE_data/ejabberd.yml b/test/ejabberd_SUITE_data/ejabberd.yml index 4eae3baa7..f674e7944 100644 --- a/test/ejabberd_SUITE_data/ejabberd.yml +++ b/test/ejabberd_SUITE_data/ejabberd.yml @@ -156,6 +156,7 @@ Welcome to this XMPP server." get_url: GET_URL max_size: 10000 vcard: VCARD + mod_auth_fast: [] registration_timeout: infinity s2s_use_starttls: false ca_file: CAFILE diff --git a/test/suite.erl b/test/suite.erl index 30066c9c3..93ddf77de 100644 --- a/test/suite.erl +++ b/test/suite.erl @@ -273,6 +273,15 @@ connect(Config) -> component -> NewConfig end. +connect_sasl2(Config, Jid) -> + Config2 = set_opt(stream_from, Jid, Config), + NewConfig = init_stream(Config2), + case ?config(type, NewConfig) of + client -> process_stream_features(NewConfig); + server -> process_stream_features(NewConfig); + component -> NewConfig + end. + tcp_connect(Config) -> case ?config(receiver, Config) of undefined -> @@ -550,6 +559,70 @@ wait_auth_SASL_result(Config, ShouldFail) -> ct:fail(sasl_auth_failed) end. +auth_fast_token(Mech, Token, Config, ShouldFail) -> + Pkt = #sasl2_authenticate{mechanism = Mech, initial_response = Token, + sub_els = [#bind2_bind{tag = <<"TestRunner">>}], + user_agent = #sasl2_user_agent{id = <<"9A62C3A9-FE59-43B7-80F3-9E8EE25DE6C5">>, + software = <<"TestRunner">>, device = <<"tester">>}}, + send(Config, Pkt), + wait_auth_SASL2_result(Config, ShouldFail). + +auth_SASL2(Mech, Config, ShouldFail) -> + Creds = {?config(user, Config), + ?config(server, Config), + ?config(password, Config)}, + {Response, SASL} = sasl_new(Mech, Creds), + Pkt = #sasl2_authenticate{mechanism = Mech, initial_response = Response, + sub_els = [#bind2_bind{tag = <<"TestRunner">>}, #fast_request_token{mech = <<"HT-SHA-256-NONE">>}], + user_agent = #sasl2_user_agent{id = <<"9A62C3A9-FE59-43B7-80F3-9E8EE25DE6C5">>, + software = <<"TestRunner">>, device = <<"tester">>}}, + send(Config, Pkt), + wait_auth_SASL2_result(set_opt(sasl, SASL, Config), ShouldFail). + +wait_auth_SASL2_result(Config, ShouldFail) -> + receive + #sasl2_success{} when ShouldFail -> + ct:fail(sasl_auth_should_have_failed); + #sasl2_success{jid = JID} = Pkt -> + case {xmpp:get_subtag(Pkt, #bind2_bound{}), xmpp:get_subtag(Pkt, #fast_token{})} of + {false, _} -> + ct:fail(sasl2_bound_missing); + {_, Token} -> + {User, S, Resource} = jid:tolower(JID), + RawToken = case Token of + #fast_token{token = T} -> T; + _ -> <<>> + end, + Config2 = set_opt(user, User, + set_opt(resource, Resource, + set_opt(fast_token, RawToken, Config))), + receive #stream_features{sub_els = Fs} -> + lists:foldl( + fun(#feature_sm{}, ConfigAcc) -> + set_opt(sm, true, ConfigAcc); + (#feature_csi{}, ConfigAcc) -> + set_opt(csi, true, ConfigAcc); + (#rosterver_feature{}, ConfigAcc) -> + set_opt(rosterver, true, ConfigAcc); + (#feature_pre_approval{}, ConfigAcc) -> + set_opt(pre_approval, true, ConfigAcc); + (#compression{methods = Ms}, ConfigAcc) -> + set_opt(compression, Ms, ConfigAcc); + (_, ConfigAcc) -> + ConfigAcc + end, Config2, Fs) + end + end; + #sasl2_challenge{text = ClientIn} -> + {Response, SASL} = (?config(sasl, Config))(ClientIn), + send(Config, #sasl2_response{text = Response}), + wait_auth_SASL2_result(set_opt(sasl, SASL, Config), ShouldFail); + #sasl2_failure{} when ShouldFail -> + Config; + #sasl2_failure{} -> + ct:fail(sasl_auth_failed) + end. + re_register(Config) -> User = ?config(user, Config), Server = ?config(server, Config),