From 53209b9ab1c154334eafacd3ca9aebe965380d50 Mon Sep 17 00:00:00 2001 From: Evgeniy Khramtsov Date: Fri, 23 Sep 2016 12:30:33 +0300 Subject: [PATCH] Add tests for s2s code --- include/xmpp_codec.hrl | 2 +- src/ejabberd_c2s.erl | 65 ++-- src/ejabberd_s2s.erl | 18 +- src/ejabberd_s2s_in.erl | 101 ++++-- src/ejabberd_s2s_out.erl | 48 ++- src/ejabberd_service.erl | 17 +- src/xmpp_codec.erl | 23 +- test/ejabberd_SUITE.erl | 183 ++++++++-- test/ejabberd_SUITE_data/ca.key | 27 ++ test/ejabberd_SUITE_data/ca.pem | 22 ++ test/ejabberd_SUITE_data/cert.pem | 94 ++--- test/ejabberd_SUITE_data/ejabberd.yml | 5 + test/ejabberd_SUITE_data/extauth.py | 5 +- test/ejabberd_SUITE_data/gencerts.sh | 18 + test/ejabberd_SUITE_data/openssl.cnf | 323 ++++++++++++++++++ test/ejabberd_SUITE_data/self-signed-cert.pem | 46 +++ test/suite.erl | 143 +++++--- test/suite.hrl | 1 + tools/xmpp_codec.spec | 12 +- 19 files changed, 917 insertions(+), 236 deletions(-) create mode 100644 test/ejabberd_SUITE_data/ca.key create mode 100644 test/ejabberd_SUITE_data/ca.pem create mode 100755 test/ejabberd_SUITE_data/gencerts.sh create mode 100644 test/ejabberd_SUITE_data/openssl.cnf create mode 100644 test/ejabberd_SUITE_data/self-signed-cert.pem diff --git a/include/xmpp_codec.hrl b/include/xmpp_codec.hrl index 845861948..5428aad11 100644 --- a/include/xmpp_codec.hrl +++ b/include/xmpp_codec.hrl @@ -158,7 +158,7 @@ -record(stream_start, {from :: jid:jid(), to :: jid:jid(), id = <<>> :: binary(), - version = <<>> :: binary(), + version :: {non_neg_integer(),non_neg_integer()}, xmlns = <<>> :: binary(), stream_xmlns = <<>> :: binary(), db_xmlns = <<>> :: binary(), diff --git a/src/ejabberd_c2s.erl b/src/ejabberd_c2s.erl index 7dc9960e6..858e285a6 100644 --- a/src/ejabberd_c2s.erl +++ b/src/ejabberd_c2s.erl @@ -352,16 +352,16 @@ wait_for_stream({xmlstreamstart, Name, Attrs}, StateData) -> S -> S end, StreamVersion = case Version of - <<"1.0">> -> <<"1.0">>; - _ -> <<"">> + {1,0} -> {1,0}; + _ -> undefined end, IsBlacklistedIP = is_ip_blacklisted(StateData#state.ip, Lang), case lists:member(Server, ?MYHOSTS) of true when IsBlacklistedIP == false -> change_shaper(StateData, jid:make(<<"">>, Server, <<"">>)), case StreamVersion of - <<"1.0">> -> - send_header(StateData, Server, <<"1.0">>, ?MYLANG), + {1,0} -> + send_header(StateData, Server, {1,0}, ?MYLANG), case StateData#state.authenticated of false -> TLS = StateData#state.tls, @@ -490,10 +490,14 @@ wait_for_stream({xmlstreamstart, Name, Attrs}, StateData) -> send_header(StateData, ?MYNAME, StreamVersion, ?MYLANG), send_element(StateData, xmpp:serr_host_unknown()), {stop, normal, StateData} - end + end; + _ -> + send_header(StateData, ?MYNAME, {1,0}, ?MYLANG), + send_element(StateData, xmpp:serr_invalid_xml()), + {stop, normal, StateData} catch _:{xmpp_codec, Why} -> Txt = xmpp:format_error(Why), - send_header(StateData, ?MYNAME, <<"1.0">>, ?MYLANG), + send_header(StateData, ?MYNAME, {1,0}, ?MYLANG), send_element(StateData, xmpp:serr_invalid_xml(Txt, ?MYLANG)), {stop, normal, StateData} end; @@ -506,7 +510,7 @@ wait_for_stream({xmlstreamend, _}, StateData) -> send_element(StateData, xmpp:serr_not_well_formed()), {stop, normal, StateData}; wait_for_stream({xmlstreamerror, _}, StateData) -> - send_header(StateData, ?MYNAME, <<"1.0">>, <<"">>), + send_header(StateData, ?MYNAME, {1,0}, <<"">>), send_element(StateData, xmpp:serr_not_well_formed()), {stop, normal, StateData}; wait_for_stream(closed, StateData) -> @@ -1374,7 +1378,7 @@ handle_info({'DOWN', Monitor, _Type, _Object, _Info}, handle_info(system_shutdown, StateName, StateData) -> case StateName of wait_for_stream -> - send_header(StateData, ?MYNAME, <<"1.0">>, <<"en">>), + send_header(StateData, ?MYNAME, {1,0}, <<"en">>), send_element(StateData, xmpp:serr_system_shutdown()), ok; _ -> @@ -1597,39 +1601,20 @@ send_packet(StateData, Packet) -> end. -spec send_header(state(), binary(), binary(), binary()) -> ok | {error, any()}. -send_header(StateData, Server, Version, Lang) - when StateData#state.xml_socket -> - VersionAttr = case Version of - <<"">> -> []; - _ -> [{<<"version">>, Version}] - end, - LangAttr = case Lang of - <<"">> -> []; - _ -> [{<<"xml:lang">>, Lang}] - end, - Header = {xmlstreamstart, <<"stream:stream">>, - VersionAttr ++ - LangAttr ++ - [{<<"xmlns">>, <<"jabber:client">>}, - {<<"xmlns:stream">>, - <<"http://etherx.jabber.org/streams">>}, - {<<"id">>, StateData#state.streamid}, - {<<"from">>, Server}]}, - (StateData#state.sockmod):send_xml(StateData#state.socket, - Header); send_header(StateData, Server, Version, Lang) -> - VersionStr = case Version of - <<"">> -> <<"">>; - _ -> [<<" version='">>, Version, <<"'">>] - end, - LangStr = case Lang of - <<"">> -> <<"">>; - _ -> [<<" xml:lang='">>, Lang, <<"'">>] - end, - Header = io_lib:format(?STREAM_HEADER, - [StateData#state.streamid, Server, VersionStr, - LangStr]), - send_text(StateData, iolist_to_binary(Header)). + Header = #xmlel{name = Name, attrs = Attrs} = + xmpp:encode(#stream_start{version = Version, + lang = Lang, + xmlns = ?NS_CLIENT, + stream_xmlns = ?NS_STREAM, + id = StateData#state.streamid, + from = jid:make(Server)}), + if StateData#state.xml_socket -> + (StateData#state.sockmod):send_xml(StateData#state.socket, + {xmlstreamstart, Name, Attrs}); + true -> + send_text(StateData, fxml:element_to_header(Header)) + end. -spec send_trailer(state()) -> ok | {error, any()}. send_trailer(StateData) diff --git a/src/ejabberd_s2s.erl b/src/ejabberd_s2s.erl index e585257e8..3c3e698ad 100644 --- a/src/ejabberd_s2s.erl +++ b/src/ejabberd_s2s.erl @@ -39,6 +39,7 @@ remove_connection/2, find_connection/2, dirty_get_connections/0, allow_host/2, incoming_s2s_number/0, outgoing_s2s_number/0, + stop_all_connections/0, clean_temporarily_blocked_table/0, list_temporarily_blocked_hosts/0, external_host_overloaded/1, is_temporarly_blocked/1, @@ -480,7 +481,13 @@ get_commands_spec() -> "the node", policy = admin, module = ?MODULE, function = outgoing_s2s_number, - args = [], result = {s2s_outgoing, integer}}]. + args = [], result = {s2s_outgoing, integer}}, + #ejabberd_commands{name = stop_all_connections, + tags = [s2s], + desc = "Stop all outgoing and incoming connections", + policy = admin, + module = ?MODULE, function = stop_all_connections, + args = [], result = {res, rescode}}]. incoming_s2s_number() -> length(supervisor:which_children(ejabberd_s2s_in_sup)). @@ -488,6 +495,15 @@ incoming_s2s_number() -> outgoing_s2s_number() -> length(supervisor:which_children(ejabberd_s2s_out_sup)). +stop_all_connections() -> + lists:foreach( + fun({_Id, Pid, _Type, _Module}) -> + exit(Pid, kill) + end, + supervisor:which_children(ejabberd_s2s_in_sup) ++ + supervisor:which_children(ejabberd_s2s_out_sup)), + mnesia:clear_table(s2s). + %%%---------------------------------------------------------------------- %%% Update Mnesia tables diff --git a/src/ejabberd_s2s_in.erl b/src/ejabberd_s2s_in.erl index fd560a451..6d1791d0b 100644 --- a/src/ejabberd_s2s_in.erl +++ b/src/ejabberd_s2s_in.erl @@ -168,21 +168,26 @@ wait_for_stream({xmlstreamstart, Name, Attrs}, StateData) -> try xmpp:decode(#xmlel{name = Name, attrs = Attrs}) of #stream_start{xmlns = NS_SERVER, stream_xmlns = NS_STREAM} when NS_SERVER /= ?NS_SERVER; NS_STREAM /= ?NS_STREAM -> - send_header(StateData, <<" version='1.0'">>), + send_header(StateData, {1,0}), send_element(StateData, xmpp:serr_invalid_namespace()), {stop, normal, StateData}; #stream_start{to = #jid{lserver = Server}, - from = #jid{lserver = From}, - version = <<"1.0">>} + from = From, version = {1,0}} when StateData#state.tls and not StateData#state.authenticated -> - send_header(StateData, <<" version='1.0'">>), + send_header(StateData, {1,0}), Auth = if StateData#state.tls_enabled -> - {Result, Message} = - ejabberd_s2s:check_peer_certificate( - StateData#state.sockmod, - StateData#state.socket, - From), - {Result, From, Message}; + case From of + #jid{} -> + {Result, Message} = + ejabberd_s2s:check_peer_certificate( + StateData#state.sockmod, + StateData#state.socket, + From#jid.lserver), + {Result, From#jid.lserver, Message}; + undefined -> + {error, <<"(unknown)">>, + <<"Got no valid 'from' attribute">>} + end; true -> {no_verify, <<"(unknown)">>, <<"TLS not (yet) enabled">>} end, @@ -225,8 +230,8 @@ wait_for_stream({xmlstreamstart, Name, Attrs}, StateData) -> NewStateData#state{server = Server}} end; #stream_start{to = #jid{lserver = Server}, - version = <<"1.0">>} when StateData#state.authenticated -> - send_header(StateData, <<" version='1.0'">>), + version = {1,0}} when StateData#state.authenticated -> + send_header(StateData, {1,0}), send_element(StateData, #stream_features{ sub_els = ejabberd_hooks:run_fold( @@ -236,24 +241,28 @@ wait_for_stream({xmlstreamstart, Name, Attrs}, StateData) -> #stream_start{db_xmlns = ?NS_SERVER_DIALBACK} when (StateData#state.tls_required and StateData#state.tls_enabled) or (not StateData#state.tls_required) -> - send_header(StateData, <<"">>), + send_header(StateData, undefined), {next_state, stream_established, StateData}; #stream_start{} -> - send_header(StateData, <<" version='1.0'">>), + send_header(StateData, {1,0}), send_element(StateData, xmpp:serr_undefined_condition()), - {stop, normal, StateData} + {stop, normal, StateData}; + _ -> + send_header(StateData, {1,0}), + send_element(StateData, xmpp:serr_invalid_xml()), + {stop, normal, StateData} catch _:{xmpp_codec, Why} -> Txt = xmpp:format_error(Why), - send_header(StateData, <<" version='1.0'">>), - send_element(StateData, xmpp:serr_not_well_formed(Txt, ?MYLANG)), + send_header(StateData, {1,0}), + send_element(StateData, xmpp:serr_invalid_xml(Txt, ?MYLANG)), {stop, normal, StateData} end; wait_for_stream({xmlstreamerror, _}, StateData) -> - send_header(StateData, <<"">>), + send_header(StateData, {1,0}), send_element(StateData, xmpp:serr_not_well_formed()), {stop, normal, StateData}; wait_for_stream(timeout, StateData) -> - send_header(StateData, <<"">>), + send_header(StateData, {1,0}), send_element(StateData, xmpp:serr_connection_timeout()), {stop, normal, StateData}; wait_for_stream(closed, StateData) -> @@ -277,13 +286,21 @@ wait_for_feature_request(#starttls{}, StateData#state.tls_options, {certfile, CertFile}) end, + TLSOpts2 = case ejabberd_config:get_option( + {s2s_cafile, StateData#state.server}, + fun iolist_to_binary/1) of + undefined -> TLSOpts1; + CAFile -> + lists:keystore(cafile, 1, TLSOpts1, + {cafile, CAFile}) + end, TLSOpts = case ejabberd_config:get_option( {s2s_tls_compression, StateData#state.server}, fun(true) -> true; (false) -> false end, false) of - true -> lists:delete(compression_none, TLSOpts1); - false -> [compression_none | TLSOpts1] + true -> lists:delete(compression_none, TLSOpts2); + false -> [compression_none | TLSOpts2] end, TLSSocket = (StateData#state.sockmod):starttls( Socket, TLSOpts, @@ -293,8 +310,7 @@ wait_for_feature_request(#starttls{}, StateData#state{socket = TLSSocket, streamid = new_id(), tls_enabled = true, tls_options = TLSOpts}}; _ -> - Txt = <<"Unsupported TLS transport">>, - send_element(StateData, xmpp:serr_policy_violation(Txt, ?MYLANG)), + send_element(StateData, #starttls_failure{}), {stop, normal, StateData} end; wait_for_feature_request(#sasl_auth{mechanism = Mech}, @@ -313,7 +329,10 @@ wait_for_feature_request(#sasl_auth{mechanism = Mech}, StateData#state{streamid = new_id(), authenticated = true}}; true -> - send_element(StateData, #sasl_failure{}), + Txt = xmpp:mk_text(<<"Denied by ACL">>, ?MYLANG), + send_element(StateData, + #sasl_failure{reason = 'not-authorized', + text = Txt}), {stop, normal, StateData} end; _ -> @@ -495,7 +514,7 @@ handle_info({send_text, Text}, StateName, StateData) -> handle_info({timeout, Timer, _}, StateName, #state{timer = Timer} = StateData) -> if StateName == wait_for_stream -> - send_header(StateData, <<"">>); + send_header(StateData, undefined); true -> ok end, @@ -555,15 +574,15 @@ send_error(StateData, Stanza, Error) -> send_trailer(StateData) -> send_text(StateData, <<"">>). --spec send_header(state(), binary()) -> ok. +-spec send_header(state(), undefined | {integer(), integer()}) -> ok. send_header(StateData, Version) -> - send_text(StateData, - <<"">>). + Header = xmpp:encode( + #stream_start{xmlns = ?NS_SERVER, + stream_xmlns = ?NS_STREAM, + db_xmlns = ?NS_SERVER_DIALBACK, + id = StateData#state.streamid, + version = Version}), + send_text(StateData, fxml:element_to_header(Header)). -spec change_shaper(state(), binary(), jid()) -> ok. change_shaper(StateData, Host, JID) -> @@ -606,9 +625,14 @@ fsm_limit_opts(Opts) -> end end. --spec decode_element(xmlel(), state_name(), state()) -> fsm_transition(). +-spec decode_element(xmlel() | xmpp_element(), state_name(), state()) -> fsm_transition(). decode_element(#xmlel{} = El, StateName, StateData) -> - try xmpp:decode(El) of + Opts = if StateName == stream_established -> + [ignore_els]; + true -> + [] + end, + try xmpp:decode(El, Opts) of Pkt -> ?MODULE:StateName(Pkt, StateData) catch error:{xmpp_codec, Why} -> case xmpp:is_stanza(El) of @@ -620,12 +644,15 @@ decode_element(#xmlel{} = El, StateName, StateData) -> ok end, {next_state, StateName, StateData} - end. + end; +decode_element(Pkt, StateName, StateData) -> + ?MODULE:StateName(Pkt, StateData). opt_type(domain_certfile) -> fun iolist_to_binary/1; opt_type(max_fsm_queue) -> fun (I) when is_integer(I), I > 0 -> I end; opt_type(s2s_certfile) -> fun iolist_to_binary/1; +opt_type(s2s_cafile) -> fun iolist_to_binary/1; opt_type(s2s_ciphers) -> fun iolist_to_binary/1; opt_type(s2s_dhfile) -> fun iolist_to_binary/1; opt_type(s2s_protocol_options) -> @@ -647,6 +674,6 @@ opt_type(s2s_use_starttls) -> (required_trusted) -> required_trusted end; opt_type(_) -> - [domain_certfile, max_fsm_queue, s2s_certfile, + [domain_certfile, max_fsm_queue, s2s_certfile, s2s_cafile, s2s_ciphers, s2s_dhfile, s2s_protocol_options, s2s_tls_compression, s2s_use_starttls]. diff --git a/src/ejabberd_s2s_out.erl b/src/ejabberd_s2s_out.erl index dd37445d7..06ba16863 100644 --- a/src/ejabberd_s2s_out.erl +++ b/src/ejabberd_s2s_out.erl @@ -106,12 +106,6 @@ %% Specified in miliseconds. Default value is 5 minutes. -define(MAX_RETRY_DELAY, 300000). --define(STREAM_HEADER, - <<"">>). - -define(SOCKET_DEFAULT_RESULT, {error, badarg}). %%%---------------------------------------------------------------------- @@ -228,9 +222,8 @@ open_socket(init, StateData) -> ?SOCKET_DEFAULT_RESULT, AddrList) of {ok, Socket} -> - Version = if StateData#state.use_v10 -> - <<" version='1.0'">>; - true -> <<"">> + Version = if StateData#state.use_v10 -> {1,0}; + true -> undefined end, NewStateData = StateData#state{socket = Socket, tls_enabled = false, @@ -318,11 +311,10 @@ wait_for_stream({xmlstreamstart, Name, Attrs}, StateData0) -> {stop, normal, StateData}; #stream_start{xmlns = NS_SERVER, stream_xmlns = NS_STREAM} when NS_SERVER /= ?NS_SERVER; NS_STREAM /= ?NS_STREAM -> - send_header(StateData, <<" version='1.0'">>), send_element(StateData, xmpp:serr_invalid_namespace()), {stop, normal, StateData}; #stream_start{db_xmlns = ?NS_SERVER_DIALBACK, id = ID, - version = V} when V /= <<"1.0">> -> + version = V} when V /= {1,0} -> send_db_request(StateData#state{remote_streamid = ID}); #stream_start{db_xmlns = ?NS_SERVER_DIALBACK, id = ID} when StateData#state.use_v10 -> @@ -337,13 +329,14 @@ wait_for_stream({xmlstreamstart, Name, Attrs}, StateData0) -> StateData#state{db_enabled = false, remote_streamid = ID}, ?FSMTIMEOUT}; #stream_start{} -> - send_header(StateData, <<"">>), send_element(StateData, xmpp:serr_invalid_namespace()), - {stop, normal, StateData} + {stop, normal, StateData}; + _ -> + send_element(StateData, xmpp:serr_invalid_xml()), + {stop, normal, StateData} catch _:{xmpp_codec, Why} -> Txt = xmpp:format_error(Why), - send_header(StateData, <<" version='1.0'">>), - send_element(StateData, xmpp:serr_not_well_formed(Txt, ?MYLANG)), + send_element(StateData, xmpp:serr_invalid_xml(Txt, ?MYLANG)), {stop, normal, StateData} end; wait_for_stream(Event, StateData) -> @@ -469,7 +462,7 @@ wait_for_auth_result({xmlstreamelement, El}, StateData) -> wait_for_auth_result(#sasl_success{}, StateData) -> ?DEBUG("auth: ~p", [{StateData#state.myname, StateData#state.server}]), ejabberd_socket:reset_stream(StateData#state.socket), - send_header(StateData, <<" version='1.0'">>), + send_header(StateData, {1,0}), {next_state, wait_for_stream, StateData#state{streamid = new_id(), authenticated = true}, ?FSMTIMEOUT}; @@ -500,7 +493,7 @@ wait_for_starttls_proceed(#starttls_proceed{}, StateData) -> streamid = new_id(), tls_enabled = true, tls_options = TLSOpts}, - send_header(NewStateData, <<" version='1.0'">>), + send_header(NewStateData, {1,0}), {next_state, wait_for_stream, NewStateData, ?FSMTIMEOUT}; wait_for_starttls_proceed(Event, StateData) -> handle_unexpected_event(Event, wait_for_starttls_proceed, StateData). @@ -567,7 +560,8 @@ handle_unexpected_event(Event, StateName, StateData) -> {xmlstreamend, _} -> ?INFO_MSG("Closing s2s connection ~s -> ~s in state ~s: " "XML stream closed by peer", - [StateData#state.myname, StateData#state.server]), + [StateData#state.myname, StateData#state.server, + StateName]), {stop, normal, StateData}; timeout -> send_element(StateData, xmpp:serr_connection_timeout()), @@ -741,6 +735,7 @@ print_state(State) -> State. -spec send_text(state(), iodata()) -> ok. send_text(StateData, Text) -> + ?DEBUG("Send Text on stream = ~s", [Text]), ejabberd_socket:send(StateData#state.socket, Text). -spec send_element(state(), xmpp_element()) -> ok. @@ -748,15 +743,16 @@ send_element(StateData, El) -> El1 = fix_ns(xmpp:encode(El)), send_text(StateData, fxml:element_to_binary(El1)). --spec send_header(state(), binary()) -> ok. +-spec send_header(state(), undefined | {integer(), integer()}) -> ok. send_header(StateData, Version) -> - Txt = io_lib:format( - "", - [StateData#state.myname, StateData#state.server, Version]), - send_text(StateData, Txt). + Header = xmpp:encode( + #stream_start{xmlns = ?NS_SERVER, + stream_xmlns = ?NS_STREAM, + db_xmlns = ?NS_SERVER_DIALBACK, + from = jid:make(StateData#state.myname), + to = jid:make(StateData#state.server), + version = Version}), + send_text(StateData, fxml:element_to_header(Header)). -spec send_trailer(state()) -> ok. send_trailer(StateData) -> diff --git a/src/ejabberd_service.erl b/src/ejabberd_service.erl index f4338593d..46d32e4fd 100644 --- a/src/ejabberd_service.erl +++ b/src/ejabberd_service.erl @@ -149,6 +149,10 @@ wait_for_stream({xmlstreamstart, Name, Attrs}, StateData) -> #stream_start{} -> send_header(StateData, ?MYNAME), send_element(StateData, xmpp:serr_improper_addressing()), + {stop, normal, StateData}; + _ -> + send_header(StateData, ?MYNAME), + send_element(StateData, xmpp:serr_invalid_xml()), {stop, normal, StateData} catch _:{xmpp_codec, Why} -> Txt = xmpp:format_error(Why), @@ -319,13 +323,12 @@ send_error(StateData, Stanza, Error) -> -spec send_header(state(), binary()) -> ok. send_header(StateData, Host) -> - send_text(StateData, - io_lib:format( - <<"">>, - [StateData#state.streamid, fxml:crypt(Host)])). + Header = xmpp:encode( + #stream_start{xmlns = ?NS_COMPONENT, + stream_xmlns = ?NS_STREAM, + from = jid:make(Host), + id = StateData#state.streamid}), + send_text(StateData, fxml:element_to_header(Header)). -spec send_trailer(state()) -> ok. send_trailer(StateData) -> diff --git a/src/xmpp_codec.erl b/src/xmpp_codec.erl index 00ee53aaf..0a9258195 100644 --- a/src/xmpp_codec.erl +++ b/src/xmpp_codec.erl @@ -3955,6 +3955,14 @@ pp(upload_slot, 3) -> [get, put, xmlns]; pp(thumbnail, 4) -> [uri, 'media-type', width, height]; pp(_, _) -> no. +enc_version({Maj, Min}) -> + <<(integer_to_binary(Maj))/binary, $., + (integer_to_binary(Min))/binary>>. + +dec_version(S) -> + [Major, Minor] = binary:split(S, <<$.>>), + {binary_to_integer(Major), binary_to_integer(Minor)}. + enc_host_port(Host) when is_binary(Host) -> Host; enc_host_port({{_, _, _, _, _, _, _, _} = IPv6, Port}) -> @@ -5284,13 +5292,20 @@ encode_stream_start_attr_xmlns(_val, _acc) -> decode_stream_start_attr_version(__TopXMLNS, undefined) -> - <<>>; + undefined; decode_stream_start_attr_version(__TopXMLNS, _val) -> - _val. + case catch dec_version(_val) of + {'EXIT', _} -> + erlang:error({xmpp_codec, + {bad_attr_value, <<"version">>, <<"stream:stream">>, + __TopXMLNS}}); + _res -> _res + end. -encode_stream_start_attr_version(<<>>, _acc) -> _acc; +encode_stream_start_attr_version(undefined, _acc) -> + _acc; encode_stream_start_attr_version(_val, _acc) -> - [{<<"version">>, _val} | _acc]. + [{<<"version">>, enc_version(_val)} | _acc]. decode_stream_start_attr_id(__TopXMLNS, undefined) -> <<>>; diff --git a/test/ejabberd_SUITE.erl b/test/ejabberd_SUITE.erl index dbcd9a905..063f51bd1 100644 --- a/test/ejabberd_SUITE.erl +++ b/test/ejabberd_SUITE.erl @@ -20,13 +20,13 @@ make_iq_result/1, start_event_relay/0, stop_event_relay/1, put_event/2, get_event/1, bind/1, auth/1, auth/2, open_session/1, open_session/2, - zlib/1, starttls/1, close_socket/1, init_stream/1, - auth_legacy/2, auth_legacy/3]). + zlib/1, starttls/1, starttls/2, close_socket/1, init_stream/1, + auth_legacy/2, auth_legacy/3, tcp_connect/1, send_text/2]). -include("suite.hrl"). suite() -> - [{timetrap, {seconds,10}}]. + [{timetrap, {seconds,30}}]. init_per_suite(Config) -> NewConfig = init_config(Config), @@ -36,6 +36,10 @@ init_per_suite(Config) -> LDIFFile = filename:join([DataDir, "ejabberd.ldif"]), {ok, _} = file:copy(ExtAuthScript, filename:join([CWD, "extauth.py"])), {ok, _} = ldap_srv:start(LDIFFile), + inet_db:add_host({127,0,0,1}, [binary_to_list(?S2S_VHOST), + binary_to_list(?MNESIA_VHOST)]), + inet_db:set_domain(binary_to_list(randoms:get_string())), + inet_db:set_lookup([file, native]), start_ejabberd(NewConfig), NewConfig. @@ -125,6 +129,16 @@ do_init_per_group(riak, Config) -> Err -> {skip, {riak_not_available, Err}} end; +do_init_per_group(s2s, Config) -> + ejabberd_config:add_option(s2s_use_starttls, required_trusted), + ejabberd_config:add_option(domain_certfile, "cert.pem"), + Port = ?config(s2s_port, Config), + set_opt(server, ?COMMON_VHOST, + set_opt(xmlns, ?NS_SERVER, + set_opt(type, server, + set_opt(server_port, Port, + set_opt(stream_from, ?S2S_VHOST, + set_opt(lang, <<"">>, Config)))))); do_init_per_group(component, Config) -> Server = ?config(server, Config), Port = ?config(component_port, Config), @@ -132,7 +146,7 @@ do_init_per_group(component, Config) -> set_opt(server, <<"component.", Server/binary>>, set_opt(type, component, set_opt(server_port, Port, - set_opt(stream_version, <<"">>, + set_opt(stream_version, undefined, set_opt(lang, <<"">>, Config)))))); do_init_per_group(_GroupName, Config) -> Pid = start_event_relay(), @@ -158,6 +172,8 @@ end_per_group(riak, _Config) -> ok; end_per_group(component, _Config) -> ok; +end_per_group(s2s, _Config) -> + ejabberd_config:add_option(s2s_use_starttls, false); end_per_group(_GroupName, Config) -> stop_event_relay(Config), ok. @@ -215,7 +231,7 @@ init_per_testcase(TestCase, OrigConfig) -> "test_connect" ++ _ -> Config; "test_legacy_auth" ++ _ -> - init_stream(set_opt(stream_version, <<"">>, Config)); + init_stream(set_opt(stream_version, undefined, Config)); "test_auth" ++ _ -> connect(Config); "test_starttls" ++ _ -> @@ -244,6 +260,8 @@ init_per_testcase(TestCase, OrigConfig) -> Password = ?config(password, Config), ejabberd_auth:try_register(User, Server, Password), open_session(bind(auth(connect(Config)))); + _ when TestGroup == s2s_tests -> + auth(connect(starttls(connect(Config)))); _ -> open_session(bind(auth(connect(Config)))) end. @@ -262,6 +280,7 @@ legacy_auth_tests() -> no_db_tests() -> [{generic, [parallel], [test_connect_bad_xml, + test_connect_unexpected_xml, test_connect_unknown_ns, test_connect_bad_xmlns, test_connect_bad_ns_stream, @@ -286,7 +305,12 @@ no_db_tests() -> time, stats, disco]}, - {presence, [sequence], [presence]}, + {presence_and_s2s, [sequence], + [presence, + s2s_dialback, + s2s_optional, + s2s_required, + s2s_required_trusted]}, {sm, [sequence], [sm, sm_resume, @@ -433,18 +457,39 @@ extauth_tests() -> test_unregister]}]. component_tests() -> - [{component_tests, [sequence], + [{component_connect, [parallel], [test_connect_bad_xml, + test_connect_unexpected_xml, test_connect_unknown_ns, test_connect_bad_xmlns, test_connect_bad_ns_stream, test_connect_missing_to, test_connect, test_auth, - test_auth_fail, - component_missing_address, - component_invalid_from, - component_send, + test_auth_fail]}, + {component_tests, [sequence], + [test_missing_address, + test_invalid_from, + test_component_send, + bad_nonza, + codec_failure]}]. + +s2s_tests() -> + [{s2s_connect, [parallel], + [test_connect_bad_xml, + test_connect_unexpected_xml, + test_connect_unknown_ns, + test_connect_bad_xmlns, + test_connect_bad_ns_stream, + test_connect, + test_connect_s2s_starttls_required, + test_starttls, + test_connect_missing_from, + test_connect_s2s_unauthenticated_iq, + test_auth_starttls]}, + {s2s_tests, [sequence], + [test_missing_address, + test_invalid_from, bad_nonza, codec_failure]}]. @@ -453,6 +498,7 @@ groups() -> {extauth, [sequence], extauth_tests()}, {no_db, [sequence], no_db_tests()}, {component, [sequence], component_tests()}, + {s2s, [sequence], s2s_tests()}, {mnesia, [sequence], db_tests(mnesia)}, {redis, [sequence], db_tests(redis)}, {mysql, [sequence], db_tests(mysql)}, @@ -461,16 +507,17 @@ groups() -> {riak, [sequence], db_tests(riak)}]. all() -> - [{group, component}, - %%{group, ldap}, + [{group, ldap}, {group, no_db}, {group, mnesia}, - %%{group, redis}, - %%{group, mysql}, - %%{group, pgsql}, - %%{group, sqlite}, - %%{group, extauth}, - %%{group, riak}, + {group, redis}, + {group, mysql}, + {group, pgsql}, + {group, sqlite}, + {group, extauth}, + {group, riak}, + {group, component}, + {group, s2s}, stop_ejabberd]. stop_ejabberd(Config) -> @@ -480,11 +527,23 @@ stop_ejabberd(Config) -> Config. test_connect_bad_xml(Config) -> - Config0 = init_stream(set_opt(xmlns, <<"'">>, Config)), + Config0 = tcp_connect(Config), + send_text(Config0, <<"<'/>">>), + Version = ?config(stream_version, Config0), + ?recv1(#stream_start{version = Version}), ?recv1(#stream_error{reason = 'not-well-formed'}), ?recv1({xmlstreamend, <<"stream:stream">>}), close_socket(Config0). +test_connect_unexpected_xml(Config) -> + Config0 = tcp_connect(Config), + send(Config0, #caps{}), + Version = ?config(stream_version, Config0), + ?recv1(#stream_start{version = Version}), + ?recv1(#stream_error{reason = 'invalid-xml'}), + ?recv1({xmlstreamend, <<"stream:stream">>}), + close_socket(Config0). + test_connect_unknown_ns(Config) -> Config0 = init_stream(set_opt(xmlns, <<"wrong">>, Config)), ?recv1(#stream_error{reason = 'invalid-xml'}), @@ -492,7 +551,11 @@ test_connect_unknown_ns(Config) -> close_socket(Config0). test_connect_bad_xmlns(Config) -> - Config0 = init_stream(set_opt(xmlns, ?NS_SERVER, Config)), + NS = case ?config(type, Config) of + client -> ?NS_SERVER; + _ -> ?NS_CLIENT + end, + Config0 = init_stream(set_opt(xmlns, NS, Config)), ?recv1(#stream_error{reason = 'invalid-namespace'}), ?recv1({xmlstreamend, <<"stream:stream">>}), close_socket(Config0). @@ -521,19 +584,32 @@ test_connect_missing_to(Config) -> ?recv1({xmlstreamend, <<"stream:stream">>}), close_socket(Config0). +test_connect_missing_from(Config) -> + Config1 = starttls(connect(Config)), + Config2 = set_opt(stream_from, <<"">>, Config1), + Config3 = init_stream(Config2), + ?recv1(#stream_error{reason = 'policy-violation'}), + ?recv1({xmlstreamend, <<"stream:stream">>}), + close_socket(Config3). + test_connect(Config) -> disconnect(connect(Config)). -test_component_connect(Config) -> - disconnect(component_connect(Config)). +test_connect_s2s_starttls_required(Config) -> + Config1 = connect(Config), + send(Config1, #caps{}), + ?recv1(#stream_error{reason = 'policy-violation'}), + ?recv1({xmlstreamend, <<"stream:stream">>}), + close_socket(Config1). -component_connect(Config) -> - init_stream(Config). +test_connect_s2s_unauthenticated_iq(Config) -> + Config1 = connect(starttls(connect(Config))), + unauthenticated_iq(Config1). test_starttls(Config) -> case ?config(starttls, Config) of true -> - disconnect(starttls(Config)); + disconnect(connect(starttls(Config))); _ -> {skipped, 'starttls_not_available'} end. @@ -597,8 +673,11 @@ unauthenticated_stanza(Config) -> disconnect(Config). unauthenticated_iq(Config) -> + From = my_jid(Config), + To = server_jid(Config), #iq{type = error} = - send_recv(Config, #iq{type = get, sub_els = [#disco_info{}]}), + send_recv(Config, #iq{type = get, from = From, to = To, + sub_els = [#disco_info{}]}), disconnect(Config). bad_nonza(Config) -> @@ -613,23 +692,57 @@ invalid_from(Config) -> ?recv1({xmlstreamend, <<"stream:stream">>}), close_socket(Config). -component_missing_address(Config) -> +test_missing_address(Config) -> Server = server_jid(Config), #iq{type = error} = send_recv(Config, #iq{type = get, from = Server}), #iq{type = error} = send_recv(Config, #iq{type = get, to = Server}), disconnect(Config). -component_invalid_from(Config) -> +test_invalid_from(Config) -> From = jid:make(randoms:get_string()), To = jid:make(randoms:get_string()), #iq{type = error} = send_recv(Config, #iq{type = get, from = From, to = To}), disconnect(Config). -component_send(Config) -> - JID = my_jid(Config), - send(Config, #message{from = JID, to = JID}), - #message{from = JID, to = JID} = recv(), +test_component_send(Config) -> + To = jid:make(?COMMON_VHOST), + From = server_jid(Config), + #iq{type = result, from = To, to = From} = + send_recv(Config, #iq{type = get, to = To, from = From, + sub_els = [#ping{}]}), + disconnect(Config). + +s2s_dialback(Config) -> + ejabberd_s2s:stop_all_connections(), + ejabberd_config:add_option(s2s_use_starttls, false), + ejabberd_config:add_option(domain_certfile, "self-signed-cert.pem"), + s2s_ping(Config). + +s2s_optional(Config) -> + ejabberd_s2s:stop_all_connections(), + ejabberd_config:add_option(s2s_use_starttls, optional), + ejabberd_config:add_option(domain_certfile, "self-signed-cert.pem"), + s2s_ping(Config). + +s2s_required(Config) -> + ejabberd_s2s:stop_all_connections(), + ejabberd_config:add_option(s2s_use_starttls, required), + ejabberd_config:add_option(domain_certfile, "self-signed-cert.pem"), + s2s_ping(Config). + +s2s_required_trusted(Config) -> + ejabberd_s2s:stop_all_connections(), + ejabberd_config:add_option(s2s_use_starttls, required), + ejabberd_config:add_option(domain_certfile, "cert.pem"), + s2s_ping(Config). + +s2s_ping(Config) -> + From = my_jid(Config), + To = jid:make(?MNESIA_VHOST), + ID = randoms:get_string(), + ejabberd_s2s:route(From, To, #iq{id = ID, type = get, sub_els = [#ping{}]}), + ?recv1(#iq{type = result, id = ID, sub_els = []}), disconnect(Config). auth_md5(Config) -> @@ -674,6 +787,9 @@ test_legacy_auth_fail(Config0) -> test_auth(Config) -> disconnect(auth(Config)). +test_auth_starttls(Config) -> + disconnect(auth(connect(starttls(Config)))). + test_auth_fail(Config0) -> Config = set_opt(user, <<"wrong">>, set_opt(password, <<"wrong">>, Config0)), @@ -1895,7 +2011,6 @@ announce_slave(Config) -> flex_offline_master(Config) -> Peer = ?config(slave, Config), - ct:log("hooks = ~p", [ets:tab2list(hooks)]), LPeer = jid:remove_resource(Peer), lists:foreach( fun(I) -> diff --git a/test/ejabberd_SUITE_data/ca.key b/test/ejabberd_SUITE_data/ca.key new file mode 100644 index 000000000..858100686 --- /dev/null +++ b/test/ejabberd_SUITE_data/ca.key @@ -0,0 +1,27 @@ +-----BEGIN RSA PRIVATE KEY----- +MIIEpAIBAAKCAQEAxGSSFSDTbBTk2GwkORLCXoBdYq5YxwPfen8bK+8WjxRb9Thp +FsHYfImtDQV0qvcZyWnjUFxRh7Dyw7A2X690nplCdzZ9Gl+5yzzlRefHborMSnNY +rnTqx3vs9qiac0A5bzdjMY7XN3VuVwz0XWY6rAiL/7OxunCNUnQz+oswDx7cj1W4 +bb9pFzBvW5TjaAiziyzS3IxvTc7kYQYJEa99vIlDZ+Ov9rHtiF/5CZ8kHc457B3s +uc9hHxO2t0EzmBiqg7wpksJjoJeXaJvT9sKSgW6LXkjBCm/7jm1ElPq+7FCph0qp +uIsxMtu15exLKQaSRLcc+tyNkWIZGQ371D2+7wIDAQABAoIBACzcNCozV1fm5ecx +vIx05oUjmTFDVfAPyGp4wkIk2OhR5Dd9bTPPj53S7P5+coni67cAQvZGQDFYj/t3 +MtRkhaT8qRwGDEmL+CqefFidewabGdMfye//sOlkO1qUZMNStkvbQQM+95Ypcszb +nq3+/gPx59i+uSg3MXDWLlFand217d8oU4JxmCxHc9ezhkpWsdReiAukWTud+q/5 +DzyPetaP09z8Ua/YNXuI6IdsvObYxOSCI1hPPuMSQGM4hQiqkHPqPNBIJDwfM9wk +WzGom5M7nGitrKynJHdS2VRzsZwFL3Hg0yBXnSY1o8er5A6i5//dS2ISSEN9xHjz +9PRRCbECgYEA+yVmv8i5uBLuz/Oeu/iOcX9ZNHfNowuIpInWVyfVhURIc1OwP1Sy +uj5Qst2IY+Hm4IVq0sNg3cZdEk+K6RMyc/Qgd7GoYeJNKH1v0RbA6E1zEzqm8Xv+ +jA3dd7RLb5NTwFv11Qh0BDZfw2e8pCmN4oDp+n8fo7RE3NQGaLb77QsCgYEAyDBE +FoYVwXhGaKnhDT1AqM3hkOGBqheJJIxkNUnyMhlU/AxmWtfvTtw7MCP+311bz4Ma +h6yUfaEiHQJs2wkPyIaZ8CbbVyP7bXWMZzA/Rnk4dQWZ/VjRYvEzIvmz9di3w5j6 +P1fWX0QODqcY2CvHyMmPLIysbC0cjVDA4ZpDvC0CgYEAlqvrpuevtCV3rL7F3pPS +MXlrdTTi5AyJX91qAEPfr+I1bSsqM/SGfYHhPE34A6SFtPGWEvgwZx0YvWGHPynL +PRGbYPPuxzrTe5U1vkVeWoAMp96qRXpUToYK9kPudfP3bRI+vB4kLFrKvRrBa+Oa +QeeBeE1IGBiQr8NsTOpq3d0CgYB9R+d0iRlYaKL3oUjcdjbe7Wl6uAXjorMLEmks +CEjwHXZX/pKXy4dSPPU1nXFF7DEm3o9d1R1gudSVfw0MztD313TDHC4sjLIuwF/L +vB/9RKOWaJkEOe9gEj7EZqy+8I+gcz45IglguUBq3xvnPQ7ck3dsk+TcFidGMQFk +rpwxSQKBgQDbdzOJagPep0HVJPkOmF1X4idb1rnQUuMi59I3k6lFTXAaypy6nU69 +aAUgv7UY4i3XglEhbztk/o51W4/fJ1N8UzbXlBur/pJD8GN2h52ea77CbpOAmDSm +Bjjoj92wmYGfBRf7DwJQDgqxvpa0s1cwtYjNf0RmbDPzBsfzrKLKbQ== +-----END RSA PRIVATE KEY----- diff --git a/test/ejabberd_SUITE_data/ca.pem b/test/ejabberd_SUITE_data/ca.pem new file mode 100644 index 000000000..3daa7f5d6 --- /dev/null +++ b/test/ejabberd_SUITE_data/ca.pem @@ -0,0 +1,22 @@ +-----BEGIN CERTIFICATE----- +MIIDtTCCAp2gAwIBAgIJAKI8WTrCnPXzMA0GCSqGSIb3DQEBCwUAMEUxCzAJBgNV +BAYTAkFVMRMwEQYDVQQIEwpTb21lLVN0YXRlMSEwHwYDVQQKExhJbnRlcm5ldCBX +aWRnaXRzIFB0eSBMdGQwHhcNMTUwNDE1MTQxNTI0WhcNNDIwODMxMTQxNTI0WjBF +MQswCQYDVQQGEwJBVTETMBEGA1UECBMKU29tZS1TdGF0ZTEhMB8GA1UEChMYSW50 +ZXJuZXQgV2lkZ2l0cyBQdHkgTHRkMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIB +CgKCAQEAxGSSFSDTbBTk2GwkORLCXoBdYq5YxwPfen8bK+8WjxRb9ThpFsHYfImt +DQV0qvcZyWnjUFxRh7Dyw7A2X690nplCdzZ9Gl+5yzzlRefHborMSnNYrnTqx3vs +9qiac0A5bzdjMY7XN3VuVwz0XWY6rAiL/7OxunCNUnQz+oswDx7cj1W4bb9pFzBv +W5TjaAiziyzS3IxvTc7kYQYJEa99vIlDZ+Ov9rHtiF/5CZ8kHc457B3suc9hHxO2 +t0EzmBiqg7wpksJjoJeXaJvT9sKSgW6LXkjBCm/7jm1ElPq+7FCph0qpuIsxMtu1 +5exLKQaSRLcc+tyNkWIZGQ371D2+7wIDAQABo4GnMIGkMB0GA1UdDgQWBBTQ9mbL +xyIyE3pDyrNMsC36DRHp+TB1BgNVHSMEbjBsgBTQ9mbLxyIyE3pDyrNMsC36DRHp ++aFJpEcwRTELMAkGA1UEBhMCQVUxEzARBgNVBAgTClNvbWUtU3RhdGUxITAfBgNV +BAoTGEludGVybmV0IFdpZGdpdHMgUHR5IEx0ZIIJAKI8WTrCnPXzMAwGA1UdEwQF +MAMBAf8wDQYJKoZIhvcNAQELBQADggEBAGyAi//UQaUhy8RLGc33T36Ni6TnRgpz +1xu2aahMe0YfPUZsZwwCP6dK+6fSw7OsRqyXZNZJntlur30yMMDlvjXmV6UDzeS4 +/HGd/hr0LqruYpmvOKmvT/y8VkmBqsGlcaRNhSJGDzMHAVEQ0hzAJe3Emw5R753p +iVRbxPqiOVt4U/gjwtrVumSt1v9O4buWo1lTp0jxK1L6K8YWmETLuxyS3IG+i9Ij +DDNyU/UxyocP/mcscUAoV9MJX56exwPC93rPxOlwJT5e5ZMRGnwwUt017dPUrKbA +u+24S8uJCKN2w0OzsrqzC6lvxOf0JRfNxxxGr1KZYyEGT7ps1jhTebA= +-----END CERTIFICATE----- diff --git a/test/ejabberd_SUITE_data/cert.pem b/test/ejabberd_SUITE_data/cert.pem index 11e18491f..ee9cf1641 100644 --- a/test/ejabberd_SUITE_data/cert.pem +++ b/test/ejabberd_SUITE_data/cert.pem @@ -1,52 +1,54 @@ -----BEGIN CERTIFICATE----- -MIIGbDCCBVSgAwIBAgIBATANBgkqhkiG9w0BAQUFADBFMQswCQYDVQQGEwJBVTET +MIIEmTCCA4GgAwIBAgIBATANBgkqhkiG9w0BAQUFADBFMQswCQYDVQQGEwJBVTET MBEGA1UECBMKU29tZS1TdGF0ZTEhMB8GA1UEChMYSW50ZXJuZXQgV2lkZ2l0cyBQ -dHkgTHRkMB4XDTE2MDUyNDE3NDIyNVoXDTQzMTAxMDE3NDIyNVowVjELMAkGA1UE +dHkgTHRkMB4XDTE2MDkyMzA3MDMyNFoXDTQ0MDIwOTA3MDMyNFowVjELMAkGA1UE BhMCQVUxEzARBgNVBAgTClNvbWUtU3RhdGUxITAfBgNVBAoTGEludGVybmV0IFdp -ZGdpdHMgUHR5IEx0ZDEPMA0GA1UEAxMGYWN0aXZlMIGfMA0GCSqGSIb3DQEBAQUA -A4GNADCBiQKBgQC+GTA1D1+yiXgLqUhJXkSj3hj5FiqlBAfJT/8OSXYifY4M4HYv -VQrqER2Fs7jdCaeoGWDvwfK/UOV0b1ROnf+T/2bXFs8EOeqjOz4xG2oexNKVrYj9 -ICYAgmSh6Hf2cZJM/YCAISje93Xl2J2w/N7oFC1ZXasPoBIZv3Fgg7hTtQIDAQAB -o4ID2DCCA9QwCQYDVR0TBAIwADAsBglghkgBhvhCAQ0EHxYdT3BlblNTTCBHZW5l -cmF0ZWQgQ2VydGlmaWNhdGUwHQYDVR0OBBYEFEynWiCoZK4tLDk3KM1wMsbrz9Ug -MB8GA1UdIwQYMBaAFND2ZsvHIjITekPKs0ywLfoNEen5MDMGA1UdHwQsMCowKKAm -oCSGImh0dHA6Ly9sb2NhbGhvc3Q6NTI4MC9kYXRhL2NybC5kZXIwNgYIKwYBBQUH -AQEEKjAoMCYGCCsGAQUFBzABhhpodHRwOi8vbG9jYWxob3N0OjUyODAvb2NzcDAL -BgNVHQ8EBAMCBeAwEwYDVR0lBAwwCgYIKwYBBQUHAwkwggLIBgNVHREEggK/MIIC -u6A4BggrBgEFBQcIBaAsDCp0ZXN0X3NpbmdsZSEjJCVeKigpYH4rLTtfPVtde318 -XEBsb2NhbGhvc3SgPwYIKwYBBQUHCAWgMwwxdGVzdF9zaW5nbGUhIyQlXiooKWB+ -Ky07Xz1bXXt9fFxAbW5lc2lhLmxvY2FsaG9zdKA+BggrBgEFBQcIBaAyDDB0ZXN0 -X3NpbmdsZSEjJCVeKigpYH4rLTtfPVtde318XEBteXNxbC5sb2NhbGhvc3SgPgYI -KwYBBQUHCAWgMgwwdGVzdF9zaW5nbGUhIyQlXiooKWB+Ky07Xz1bXXt9fFxAcGdz -cWwubG9jYWxob3N0oD8GCCsGAQUFBwgFoDMMMXRlc3Rfc2luZ2xlISMkJV4qKClg -fistO189W117fXxcQHNxbGl0ZS5sb2NhbGhvc3SgQAYIKwYBBQUHCAWgNAwydGVz -dF9zaW5nbGUhIyQlXiooKWB+Ky07Xz1bXXt9fFxAZXh0YXV0aC5sb2NhbGhvc3Sg -PQYIKwYBBQUHCAWgMQwvdGVzdF9zaW5nbGUhIyQlXiooKWB+Ky07Xz1bXXt9fFxA -bGRhcC5sb2NhbGhvc3SgPQYIKwYBBQUHCAWgMQwvdGVzdF9zaW5nbGUhIyQlXioo -KWB+Ky07Xz1bXXt9fFxAcDFkYi5sb2NhbGhvc3SgPQYIKwYBBQUHCAWgMQwvdGVz -dF9zaW5nbGUhIyQlXiooKWB+Ky07Xz1bXXt9fFxAcmlhay5sb2NhbGhvc3SgPgYI -KwYBBQUHCAWgMgwwdGVzdF9zaW5nbGUhIyQlXiooKWB+Ky07Xz1bXXt9fFxAcmVk -aXMubG9jYWxob3N0oD4GCCsGAQUFBwgFoDIMMHRlc3Rfc2luZ2xlISMkJV4qKClg -fistO189W117fXxcQG1zc3FsLmxvY2FsaG9zdDANBgkqhkiG9w0BAQUFAAOCAQEA -et4jpmpwlE+2bw+/iqCt7sfU/5nPmQ8YtgMB+32wf7DINNJgkwOdkYJpzhlMXKrh -/bn8+Ybmq6MbK0r2R91Uu858xQf8VKExQm44qaGSyL5Ug3jsAWb3GLZSaWQo37e9 -QdDeP8XijCEyr3rum19tRIdiImsRAxJqwfaE4pUSgfCEQMkvb+6//8HSf9RRPToD -o6eAg8QerEtTfxerEdW/0K1ozOrzSrQembWOu+JjvANRl+p59j+1YOWHzS/yQeZl -K3sjFoCvXPvocRnUznvT+TSdy3ORJSjwfEcP5Crim70amZZ6NeMAxfby9wwmmX0x -zkwPCSUXliXke6T88Olj7Q== +ZGdpdHMgUHR5IEx0ZDEPMA0GA1UEAxMGYWN0aXZlMIIBIjANBgkqhkiG9w0BAQEF +AAOCAQ8AMIIBCgKCAQEAselBnOh089g/VN7gH1m43Vo67kSqh8QRnXZxfjpzt3oP +Dl5nd04eNey4ezoSBo7o1hKhj/m5KLxmy1kN+xssyutgzto1FZu8GC2jDyLvByNL +h0Z3XLmzdzBzBjosCtllJtzHlVL08SPuuOId5hToiiT8h3ElgNI4L6w+eLzhZIk5 +Rj1WojGa+pnaTEgoOaZPcNrkOj81o1tgnbLXN7HY3hJKnRp78DmPySq82cRhvfNr +ePCs6BJr3y7yYJk0nG+EOaj5BK95YSJondZ8fOZuCigJPMogEaSw0SGsSUiQrPsd ++3vZQ+3ctOimnhW7cF3fAM79g+zDdv9N9E3D+inhyQIDAQABo4IBgTCCAX0wCQYD +VR0TBAIwADAsBglghkgBhvhCAQ0EHxYdT3BlblNTTCBHZW5lcmF0ZWQgQ2VydGlm +aWNhdGUwHQYDVR0OBBYEFJgip1fThIyZu9J+YNz3XKDkOcMKMB8GA1UdIwQYMBaA +FND2ZsvHIjITekPKs0ywLfoNEen5MDMGA1UdHwQsMCowKKAmoCSGImh0dHA6Ly9s +b2NhbGhvc3Q6NTI4MC9kYXRhL2NybC5kZXIwNgYIKwYBBQUHAQEEKjAoMCYGCCsG +AQUFBzABhhpodHRwOi8vbG9jYWxob3N0OjUyODAvb2NzcDALBgNVHQ8EBAMCBeAw +JwYDVR0lBCAwHgYIKwYBBQUHAwkGCCsGAQUFBwMBBggrBgEFBQcDAjBfBgNVHREE +WDBWoBcGCCsGAQUFBwgFoAsMCWxvY2FsaG9zdKAbBggrBgEFBQcIBaAPDA1zMnMu +bG9jYWxob3N0oB4GCCsGAQUFBwgFoBIMEG1uZXNpYS5sb2NhbGhvc3QwDQYJKoZI +hvcNAQEFBQADggEBAEwHeECqeEJIz0VFA0OZ0w9+3rfZPX9K59rbJNNnKVATPhk5 +g5NFpXy1mFTV/3MWjDS1QRbgoXzOYR64S87oez4l3jyDz3YxklyjbbiN3QKaUq5h +284Ze6CiRqxIi6V2bhjjp3voMSP8BQ72bX9uAWjqQl7Z16wYuCzV4QzVZRD5p0c1 +y45WZ6J+sU1GTwEGh0vXZBlDMeTb+53smjEoCxET1ecJmStAvJi+UHiLn63Z3Yzz +CTfdAZ/mj+ytaNLVsgrULXrmZAeo064HVqeyLWL8ZBoM0zLs6u14OQOeDCCB62cj +UXb9npKmIdfsWvdii6emCVQqKBQmHnlUMCh56tE= -----END CERTIFICATE----- -----BEGIN RSA PRIVATE KEY----- -MIICXAIBAAKBgQC+GTA1D1+yiXgLqUhJXkSj3hj5FiqlBAfJT/8OSXYifY4M4HYv -VQrqER2Fs7jdCaeoGWDvwfK/UOV0b1ROnf+T/2bXFs8EOeqjOz4xG2oexNKVrYj9 -ICYAgmSh6Hf2cZJM/YCAISje93Xl2J2w/N7oFC1ZXasPoBIZv3Fgg7hTtQIDAQAB -AoGALddtJJ58eVVlOYqs/+RXsRyR8R9DUV/TcNx1qUBV2KNmafyHA4sCgsd10xQv -9D2rzIGyOp8OpswfSSC/t+WqB9+ezSruzMuX6IURdHZbX6aWWX6maICtPKEEkCmI -gaLxE/ojuOXnTEBTkVuVWtuFL9PsK/WGi/FIDzJbwqTWJ4ECQQDy9DrBAQM96B6u -G4XpFzBsfgJZoS+NaMdCwK+/jgcEpI6oxobK8tuGB6drp5jNSuQ905W9n8XjA6Xq -x8/GH9I5AkEAyE5g05HhMlxBWCq+P70pBDIamdHJcPQVL8+6NXkT+mTqqZxxkUy4 -nMfTh5zE6WfmqYNtrmNBDxXUyaoRSBydXQJACnFnCR7DBekxUGiMc/10LmWoMjQU -eC6Vyg/APiqbsJ5mJ2kJKDYSK4uurZjxn3lloCa1HAZ/GgfxHMtj6e86OQJAetq3 -wIwE12KGIZF1xpo6gfxJHHbzWngaVozN5OYyPq2O0CDH9xpbUK2vK8oXbCDx9J5L -s13lFV+Kd3X7y4LhcQJBAKSFg7ht33l8Sa0TdUkY6Tl1NBMCCLf+np+HYrAbQZux -2NtR6nj2YqeOpEe1ibWZm8tj3dzlTm1FCOIpa+pm114= +MIIEpAIBAAKCAQEAselBnOh089g/VN7gH1m43Vo67kSqh8QRnXZxfjpzt3oPDl5n +d04eNey4ezoSBo7o1hKhj/m5KLxmy1kN+xssyutgzto1FZu8GC2jDyLvByNLh0Z3 +XLmzdzBzBjosCtllJtzHlVL08SPuuOId5hToiiT8h3ElgNI4L6w+eLzhZIk5Rj1W +ojGa+pnaTEgoOaZPcNrkOj81o1tgnbLXN7HY3hJKnRp78DmPySq82cRhvfNrePCs +6BJr3y7yYJk0nG+EOaj5BK95YSJondZ8fOZuCigJPMogEaSw0SGsSUiQrPsd+3vZ +Q+3ctOimnhW7cF3fAM79g+zDdv9N9E3D+inhyQIDAQABAoIBAQCWIyxVx+36YgGA +E927VzIkyqJ0tMncbOAYq/228oj4yy6th4l1Kx1fkHdWtnjDxBJFpc9l+u4ArI1r +Cao8wIAadmxp48dshtJC7TBv86EXuvdgH11XiPcknGRVWv4T4cX099gN8cX3QcWR +jHCC3B4phnD9s8RcZAs6X/cQWQU0mxiHodYJefSXDyRIx9wimXmmW83ZqcsFftXS +MI0+jflmRTf07M4gALVL0LlaBkg2FMoNiaKYPTbubcrEMUgTDsoDsjX3Fi43qLdF +QTq+lF7HrHQ1EQlngCJupka9JxwZc3Fae6XYlDQvSDPcRxzWJoOuVBPtheGeoU3c +PAry9KihAoGBAN8HCb0k4bMN06WZjSzClKhb6eFw4GMbVpDAOwPDl2N+9+pwrGxE +ztekrM+VdXVScIj23g6wKd6fPqK6EYuEEu3Hre82e9ApqjJ34p1UcOs9Vs4N3VDy +HJnWhEytsc9c03O5nhsK1YAXoGHEPmCYGsg2UA171LDcarnO1WDmpKkNAoGBAMw2 +sTCC/LBwgsuPZL5fR10wQ1sr1fIheSL+VK10jSRDwNXT2Y4wdCpQXQ6XNi+n98n5 +VvKaE6PxFqjnKCrUUty8X5+fzVcTKpBYVICceEzpVY9FrKbeY1shMnOBRTCkaQwz +8CoEbbQz6SH5s4qW7M8iJdUJ0RulaFDfpmangTStAoGBALMkMxVjZ4rsI0GT2grG +7KNi2LTFducEUX8JeR2n4JUBql78S/LXPhGGa2x9z5ACPPQ23tyLccYowSXyMR+Q +YafuyO4pJEBrBxNsqnDXH7BEX9I43rkjEAgdf70bk4RNOmdtA+sSw7UUxTVibPwn +kPOadKiv+4JoOa2vzkL8X+yNAoGAbU85OUZkC+2tlViEDILjqDYVV8/3DUxtkxWg +LdidVDQQHGTxpvK4u42Ywh6empPGRw54RBPFP5PlFTPmhEZytEUAymi3eUyBFBKz +6MPYgRLFAZPB/vA7LqRuZPVlG8xljmqeu17zeenveIg4Wo6+44Dbz1UZ4TqAxAlz +AK/YsWECgYAPuZnIo9fWJtUAIe5IA2LIqcN0rj3PsZ/tL6eaMXqKZgCYwTvVUGbT +XD4O352t+yLM8v2hJGHrIPuHooN2dCadYuzoBvVFsRTZjGpBlAZ+EJ5WfDYFL0qf +68O2KZNXaSS8ZARlp9g3C8AFiakm/uWhtSfwx09uSBHJgld1V3GAoA== -----END RSA PRIVATE KEY----- diff --git a/test/ejabberd_SUITE_data/ejabberd.yml b/test/ejabberd_SUITE_data/ejabberd.yml index 99af18bbf..128be2aed 100644 --- a/test/ejabberd_SUITE_data/ejabberd.yml +++ b/test/ejabberd_SUITE_data/ejabberd.yml @@ -409,6 +409,7 @@ acl: user_regexp: "" define_macro: CERTFILE: "cert.pem" + CAFILE: "ca.pem" language: "en" listen: - @@ -450,6 +451,10 @@ Welcome to this XMPP server." mod_time: [] mod_version: [] registration_timeout: infinity +route_subdomains: s2s +domain_certfile: CERTFILE +s2s_use_starttls: false +s2s_cafile: CAFILE shaper: fast: 50000 normal: 1000 diff --git a/test/ejabberd_SUITE_data/extauth.py b/test/ejabberd_SUITE_data/extauth.py index 84c000144..fa2c9efd0 100755 --- a/test/ejabberd_SUITE_data/extauth.py +++ b/test/ejabberd_SUITE_data/extauth.py @@ -7,7 +7,10 @@ def read(): cmd = pkt[0] args_num = len(pkt) - 1 if cmd == 'auth' and args_num >= 3: - write(True) + if pkt[1] == "wrong": + write(False) + else: + write(True) elif cmd == 'isuser' and args_num == 2: write(True) elif cmd == 'setpass' and args_num >= 3: diff --git a/test/ejabberd_SUITE_data/gencerts.sh b/test/ejabberd_SUITE_data/gencerts.sh new file mode 100755 index 000000000..d0acd4b0c --- /dev/null +++ b/test/ejabberd_SUITE_data/gencerts.sh @@ -0,0 +1,18 @@ +#!/bin/sh +# Update openssl.cnf if needed (in particular section [alt_names]) + +rm -rf ssl +mkdir -p ssl/newcerts +touch ssl/index.txt +echo 01 > ssl/serial +echo 1000 > ssl/crlnumber +openssl genrsa -out ssl/client.key +openssl req -new -key ssl/client.key -out ssl/client.csr -config openssl.cnf -batch -subj /C=AU/ST=Some-State/O=Internet\ Widgits\ Pty\ Ltd/CN=active +openssl ca -keyfile ca.key -cert ca.pem -in ssl/client.csr -out ssl/client.crt -config openssl.cnf -days 10000 -batch -notext +openssl req -new -key ssl/client.key -out ssl/self-signed-client.csr -batch -subj /C=AU/ST=Some-State/O=Internet\ Widgits\ Pty\ Ltd/CN=active +openssl x509 -req -in ssl/self-signed-client.csr -signkey ssl/client.key -out ssl/self-signed-client.crt -days 10000 +cat ssl/client.crt > cert.pem +cat ssl/self-signed-client.crt > self-signed-cert.pem +cat ssl/client.key >> cert.pem +cat ssl/client.key >> self-signed-cert.pem +rm -rf ssl diff --git a/test/ejabberd_SUITE_data/openssl.cnf b/test/ejabberd_SUITE_data/openssl.cnf new file mode 100644 index 000000000..ff11d1460 --- /dev/null +++ b/test/ejabberd_SUITE_data/openssl.cnf @@ -0,0 +1,323 @@ +# +# OpenSSL example configuration file. +# This is mostly being used for generation of certificate requests. +# + +# This definition stops the following lines choking if HOME isn't +# defined. +HOME = . +RANDFILE = $ENV::HOME/.rnd + +# Extra OBJECT IDENTIFIER info: +#oid_file = $ENV::HOME/.oid +oid_section = new_oids + +# To use this configuration file with the "-extfile" option of the +# "openssl x509" utility, name here the section containing the +# X.509v3 extensions to use: +extensions = v3_req +# (Alternatively, use a configuration file that has only +# X.509v3 extensions in its main [= default] section.) + +[ new_oids ] +# We can add new OIDs in here for use by 'ca' and 'req'. +# Add a simple OID like this: +# testoid1=1.2.3.4 +# Or use config file substitution like this: +# testoid2=${testoid1}.5.6 + +#################################################################### +[ ca ] +default_ca = CA_default # The default ca section + +#################################################################### +[ CA_default ] + +#dir = ./demoCA # Where everything is kept +dir = ssl +certs = $dir/certs # Where the issued certs are kept +crl_dir = $dir/crl # Where the issued crl are kept +database = $dir/index.txt # database index file. +#unique_subject = no # Set to 'no' to allow creation of + # several ctificates with same subject. +new_certs_dir = $dir/newcerts # default place for new certs. + +certificate = $dir/cacert.pem # The CA certificate +serial = $dir/serial # The current serial number +crlnumber = $dir/crlnumber # the current crl number + # must be commented out to leave a V1 CRL +crl = $dir/crl.pem # The current CRL +private_key = $dir/private/cakey.pem# The private key +RANDFILE = $dir/private/.rand # private random number file + +x509_extensions = usr_cert # The extentions to add to the cert + +# Comment out the following two lines for the "traditional" +# (and highly broken) format. +name_opt = ca_default # Subject Name options +cert_opt = ca_default # Certificate field options + +# Extension copying option: use with caution. +copy_extensions = copy + +# Extensions to add to a CRL. Note: Netscape communicator chokes on V2 CRLs +# so this is commented out by default to leave a V1 CRL. +# crlnumber must also be commented out to leave a V1 CRL. +# crl_extensions = crl_ext + +default_days = 365 # how long to certify for +default_crl_days= 30 # how long before next CRL +default_md = sha1 # which md to use. +preserve = no # keep passed DN ordering + +# A few difference way of specifying how similar the request should look +# For type CA, the listed attributes must be the same, and the optional +# and supplied fields are just that :-) +policy = policy_match + +# For the CA policy +[ policy_match ] +countryName = match +stateOrProvinceName = match +organizationName = match +organizationalUnitName = optional +commonName = optional +emailAddress = optional + +# For the 'anything' policy +# At this point in time, you must list all acceptable 'object' +# types. +[ policy_anything ] +countryName = optional +stateOrProvinceName = optional +localityName = optional +organizationName = optional +organizationalUnitName = optional +commonName = optional +emailAddress = optional + +#################################################################### +[ req ] +default_bits = 1024 +default_keyfile = privkey.pem +distinguished_name = req_distinguished_name +attributes = req_attributes +x509_extensions = v3_ca # The extentions to add to the self signed cert + +# Passwords for private keys if not present they will be prompted for +# input_password = secret +# output_password = secret + +# This sets a mask for permitted string types. There are several options. +# default: PrintableString, T61String, BMPString. +# pkix : PrintableString, BMPString. +# utf8only: only UTF8Strings. +# nombstr : PrintableString, T61String (no BMPStrings or UTF8Strings). +# MASK:XXXX a literal mask value. +# WARNING: current versions of Netscape crash on BMPStrings or UTF8Strings +# so use this option with caution! +string_mask = nombstr + +req_extensions = v3_req # The extensions to add to a certificate request + +[ req_distinguished_name ] +countryName = Country Name (2 letter code) +countryName_default = AU +countryName_min = 2 +countryName_max = 2 + +stateOrProvinceName = State or Province Name (full name) +stateOrProvinceName_default = Some-State + +localityName = Locality Name (eg, city) + +0.organizationName = Organization Name (eg, company) +0.organizationName_default = Internet Widgits Pty Ltd + +# we can do this but it is not needed normally :-) +#1.organizationName = Second Organization Name (eg, company) +#1.organizationName_default = World Wide Web Pty Ltd + +organizationalUnitName = Organizational Unit Name (eg, section) +#organizationalUnitName_default = + +commonName = Common Name (eg, YOUR name) +commonName_max = 64 + +emailAddress = Email Address +emailAddress_max = 64 + +# SET-ex3 = SET extension number 3 + +[ req_attributes ] +challengePassword = A challenge password +challengePassword_min = 4 +challengePassword_max = 20 + +unstructuredName = An optional company name + +[ usr_cert ] + +# These extensions are added when 'ca' signs a request. + +# This goes against PKIX guidelines but some CAs do it and some software +# requires this to avoid interpreting an end user certificate as a CA. + +basicConstraints=CA:FALSE + +# Here are some examples of the usage of nsCertType. If it is omitted +# the certificate can be used for anything *except* object signing. + +# This is OK for an SSL server. +# nsCertType = server + +# For an object signing certificate this would be used. +# nsCertType = objsign + +# For normal client use this is typical +# nsCertType = client, email + +# and for everything including object signing: +# nsCertType = client, email, objsign + +# This is typical in keyUsage for a client certificate. +# keyUsage = nonRepudiation, digitalSignature, keyEncipherment + +# This will be displayed in Netscape's comment listbox. +nsComment = "OpenSSL Generated Certificate" + +# PKIX recommendations harmless if included in all certificates. +subjectKeyIdentifier=hash +authorityKeyIdentifier=keyid,issuer + +# This stuff is for subjectAltName and issuerAltname. +# Import the email address. +# subjectAltName=email:copy +# An alternative to produce certificates that aren't +# deprecated according to PKIX. +# subjectAltName=email:move + +# Copy subject details +# issuerAltName=issuer:copy + +#nsCaRevocationUrl = http://www.domain.dom/ca-crl.pem +#nsBaseUrl +#nsRevocationUrl +#nsRenewalUrl +#nsCaPolicyUrl +#nsSslServerName + +crlDistributionPoints = URI:http://localhost:5280/data/crl.der +authorityInfoAccess = OCSP;URI:http://localhost:5280/ocsp + +[ v3_req ] + +# Extensions to add to a certificate request + +basicConstraints = CA:FALSE +keyUsage = nonRepudiation, digitalSignature, keyEncipherment +extendedKeyUsage = OCSPSigning,serverAuth,clientAuth +subjectAltName = @alt_names + +[alt_names] +otherName.1 = 1.3.6.1.5.5.7.8.5;UTF8:"localhost" +otherName.2 = 1.3.6.1.5.5.7.8.5;UTF8:"s2s.localhost" +otherName.3 = 1.3.6.1.5.5.7.8.5;UTF8:"mnesia.localhost" + +[ v3_ca ] +crlDistributionPoints = URI:http://localhost:5280/data/crl.der + +# Extensions for a typical CA + + +# PKIX recommendation. + +subjectKeyIdentifier=hash + +authorityKeyIdentifier=keyid:always,issuer:always + +# This is what PKIX recommends but some broken software chokes on critical +# extensions. +#basicConstraints = critical,CA:true +# So we do this instead. +basicConstraints = CA:true + +# Key usage: this is typical for a CA certificate. However since it will +# prevent it being used as an test self-signed certificate it is best +# left out by default. +# keyUsage = cRLSign, keyCertSign + +# Some might want this also +# nsCertType = sslCA, emailCA + +# Include email address in subject alt name: another PKIX recommendation +# subjectAltName=email:copy +# Copy issuer details +# issuerAltName=issuer:copy + +# DER hex encoding of an extension: beware experts only! +# obj=DER:02:03 +# Where 'obj' is a standard or added object +# You can even override a supported extension: +# basicConstraints= critical, DER:30:03:01:01:FF + +[ crl_ext ] + +# CRL extensions. +# Only issuerAltName and authorityKeyIdentifier make any sense in a CRL. + +# issuerAltName=issuer:copy +authorityKeyIdentifier=keyid:always,issuer:always + +[ proxy_cert_ext ] +# These extensions should be added when creating a proxy certificate + +# This goes against PKIX guidelines but some CAs do it and some software +# requires this to avoid interpreting an end user certificate as a CA. + +basicConstraints=CA:FALSE + +# Here are some examples of the usage of nsCertType. If it is omitted +# the certificate can be used for anything *except* object signing. + +# This is OK for an SSL server. +# nsCertType = server + +# For an object signing certificate this would be used. +# nsCertType = objsign + +# For normal client use this is typical +# nsCertType = client, email + +# and for everything including object signing: +# nsCertType = client, email, objsign + +# This is typical in keyUsage for a client certificate. +# keyUsage = nonRepudiation, digitalSignature, keyEncipherment + +# This will be displayed in Netscape's comment listbox. +nsComment = "OpenSSL Generated Certificate" + +# PKIX recommendations harmless if included in all certificates. +subjectKeyIdentifier=hash +authorityKeyIdentifier=keyid,issuer:always + +# This stuff is for subjectAltName and issuerAltname. +# Import the email address. +# subjectAltName=email:copy +# An alternative to produce certificates that aren't +# deprecated according to PKIX. +# subjectAltName=email:move + +# Copy subject details +# issuerAltName=issuer:copy + +#nsCaRevocationUrl = http://www.domain.dom/ca-crl.pem +#nsBaseUrl +#nsRevocationUrl +#nsRenewalUrl +#nsCaPolicyUrl +#nsSslServerName + +# This really needs to be in place for it to be a proxy certificate. +proxyCertInfo=critical,language:id-ppl-anyLanguage,pathlen:3,policy:foo diff --git a/test/ejabberd_SUITE_data/self-signed-cert.pem b/test/ejabberd_SUITE_data/self-signed-cert.pem new file mode 100644 index 000000000..d6b34f50e --- /dev/null +++ b/test/ejabberd_SUITE_data/self-signed-cert.pem @@ -0,0 +1,46 @@ +-----BEGIN CERTIFICATE----- +MIIDKDCCAhACCQCsLYnJDV1wHDANBgkqhkiG9w0BAQsFADBWMQswCQYDVQQGEwJB +VTETMBEGA1UECBMKU29tZS1TdGF0ZTEhMB8GA1UEChMYSW50ZXJuZXQgV2lkZ2l0 +cyBQdHkgTHRkMQ8wDQYDVQQDEwZhY3RpdmUwHhcNMTYwOTIzMDcwMzI0WhcNNDQw +MjA5MDcwMzI0WjBWMQswCQYDVQQGEwJBVTETMBEGA1UECBMKU29tZS1TdGF0ZTEh +MB8GA1UEChMYSW50ZXJuZXQgV2lkZ2l0cyBQdHkgTHRkMQ8wDQYDVQQDEwZhY3Rp +dmUwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCx6UGc6HTz2D9U3uAf +WbjdWjruRKqHxBGddnF+OnO3eg8OXmd3Th417Lh7OhIGjujWEqGP+bkovGbLWQ37 +GyzK62DO2jUVm7wYLaMPIu8HI0uHRndcubN3MHMGOiwK2WUm3MeVUvTxI+644h3m +FOiKJPyHcSWA0jgvrD54vOFkiTlGPVaiMZr6mdpMSCg5pk9w2uQ6PzWjW2Cdstc3 +sdjeEkqdGnvwOY/JKrzZxGG982t48KzoEmvfLvJgmTScb4Q5qPkEr3lhImid1nx8 +5m4KKAk8yiARpLDRIaxJSJCs+x37e9lD7dy06KaeFbtwXd8Azv2D7MN2/030TcP6 +KeHJAgMBAAEwDQYJKoZIhvcNAQELBQADggEBAEAIFwHpNCVUiivAcfkxcUPKp0nn +mhGqkMDRrPA7fOCm0ir1Puz4GQ/G4i+tWejzzFoS6kKQl+sUZAUYJdziftJFFoZ7 +br3q3Xafc2dWa8SHNcHH6lA1OEk8tXlhkNl+EgSLnRGMhIf0iZL2wGjE8Hlig6cu +3h+OpbUijXUmq0XdH+ui3wNgXb7+Tosg/Od+lr0fNjkopsk3t1oiVXD4OQBZdUyq +V5XValiZjMFDUUBdxBA+l6/Qj3bFmluz+FXI8UwfbinukqADTJzkMeUjEkvmKZWO +tb+EU77NIuvg/k7b1yp4lEmATpdUfcGEuhWNtgeh5AqgMxOhAsJ7zUTA80I= +-----END CERTIFICATE----- +-----BEGIN RSA PRIVATE KEY----- +MIIEpAIBAAKCAQEAselBnOh089g/VN7gH1m43Vo67kSqh8QRnXZxfjpzt3oPDl5n +d04eNey4ezoSBo7o1hKhj/m5KLxmy1kN+xssyutgzto1FZu8GC2jDyLvByNLh0Z3 +XLmzdzBzBjosCtllJtzHlVL08SPuuOId5hToiiT8h3ElgNI4L6w+eLzhZIk5Rj1W +ojGa+pnaTEgoOaZPcNrkOj81o1tgnbLXN7HY3hJKnRp78DmPySq82cRhvfNrePCs +6BJr3y7yYJk0nG+EOaj5BK95YSJondZ8fOZuCigJPMogEaSw0SGsSUiQrPsd+3vZ +Q+3ctOimnhW7cF3fAM79g+zDdv9N9E3D+inhyQIDAQABAoIBAQCWIyxVx+36YgGA +E927VzIkyqJ0tMncbOAYq/228oj4yy6th4l1Kx1fkHdWtnjDxBJFpc9l+u4ArI1r +Cao8wIAadmxp48dshtJC7TBv86EXuvdgH11XiPcknGRVWv4T4cX099gN8cX3QcWR +jHCC3B4phnD9s8RcZAs6X/cQWQU0mxiHodYJefSXDyRIx9wimXmmW83ZqcsFftXS +MI0+jflmRTf07M4gALVL0LlaBkg2FMoNiaKYPTbubcrEMUgTDsoDsjX3Fi43qLdF +QTq+lF7HrHQ1EQlngCJupka9JxwZc3Fae6XYlDQvSDPcRxzWJoOuVBPtheGeoU3c +PAry9KihAoGBAN8HCb0k4bMN06WZjSzClKhb6eFw4GMbVpDAOwPDl2N+9+pwrGxE +ztekrM+VdXVScIj23g6wKd6fPqK6EYuEEu3Hre82e9ApqjJ34p1UcOs9Vs4N3VDy +HJnWhEytsc9c03O5nhsK1YAXoGHEPmCYGsg2UA171LDcarnO1WDmpKkNAoGBAMw2 +sTCC/LBwgsuPZL5fR10wQ1sr1fIheSL+VK10jSRDwNXT2Y4wdCpQXQ6XNi+n98n5 +VvKaE6PxFqjnKCrUUty8X5+fzVcTKpBYVICceEzpVY9FrKbeY1shMnOBRTCkaQwz +8CoEbbQz6SH5s4qW7M8iJdUJ0RulaFDfpmangTStAoGBALMkMxVjZ4rsI0GT2grG +7KNi2LTFducEUX8JeR2n4JUBql78S/LXPhGGa2x9z5ACPPQ23tyLccYowSXyMR+Q +YafuyO4pJEBrBxNsqnDXH7BEX9I43rkjEAgdf70bk4RNOmdtA+sSw7UUxTVibPwn +kPOadKiv+4JoOa2vzkL8X+yNAoGAbU85OUZkC+2tlViEDILjqDYVV8/3DUxtkxWg +LdidVDQQHGTxpvK4u42Ywh6empPGRw54RBPFP5PlFTPmhEZytEUAymi3eUyBFBKz +6MPYgRLFAZPB/vA7LqRuZPVlG8xljmqeu17zeenveIg4Wo6+44Dbz1UZ4TqAxAlz +AK/YsWECgYAPuZnIo9fWJtUAIe5IA2LIqcN0rj3PsZ/tL6eaMXqKZgCYwTvVUGbT +XD4O352t+yLM8v2hJGHrIPuHooN2dCadYuzoBvVFsRTZjGpBlAZ+EJ5WfDYFL0qf +68O2KZNXaSS8ZARlp9g3C8AFiakm/uWhtSfwx09uSBHJgld1V3GAoA== +-----END RSA PRIVATE KEY----- diff --git a/test/suite.erl b/test/suite.erl index f78b7bd1a..e4be0054f 100644 --- a/test/suite.erl +++ b/test/suite.erl @@ -27,13 +27,18 @@ init_config(Config) -> SASLPath = filename:join([PrivDir, "sasl.log"]), MnesiaDir = filename:join([PrivDir, "mnesia"]), CertFile = filename:join([DataDir, "cert.pem"]), + SelfSignedCertFile = filename:join([DataDir, "self-signed-cert.pem"]), + CAFile = filename:join([DataDir, "ca.pem"]), {ok, CWD} = file:get_cwd(), {ok, _} = file:copy(CertFile, filename:join([CWD, "cert.pem"])), + {ok, _} = file:copy(SelfSignedCertFile, + filename:join([CWD, "self-signed-cert.pem"])), + {ok, _} = file:copy(CAFile, filename:join([CWD, "ca.pem"])), {ok, CfgContentTpl} = file:read_file(ConfigPathTpl), Password = <<"password!@#$%^&*()'\"`~<>+-/;:_=[]{}|\\">>, CfgContent = process_config_tpl(CfgContentTpl, [ {c2s_port, 5222}, - {loglevel, 5}, + {loglevel, 4}, {s2s_port, 5269}, {component_port, 5270}, {web_port, 5280}, @@ -62,6 +67,7 @@ init_config(Config) -> [{server_port, ct:get_config(c2s_port, 5222)}, {server_host, "localhost"}, {component_port, ct:get_config(component_port, 5270)}, + {s2s_port, ct:get_config(s2s_port, 5269)}, {server, ?COMMON_VHOST}, {user, <<"test_single!#$%^*()`~+-;_=[]{}|\\">>}, {master_nick, <<"master_nick!@#$%^&*()'\"`~<>+-/;:_=[]{}|\\">>}, @@ -71,11 +77,14 @@ init_config(Config) -> {type, client}, {xmlns, ?NS_CLIENT}, {ns_stream, ?NS_STREAM}, - {stream_version, <<"1.0">>}, + {stream_version, {1, 0}}, {stream_id, <<"">>}, + {stream_from, <<"">>}, + {db_xmlns, <<"">>}, {mechs, []}, {lang, <<"en">>}, {base_dir, BaseDir}, + {socket, undefined}, {resource, <<"resource!@#$%^&*()'\"`~<>+-/;:_=[]{}|\\">>}, {master_resource, <<"master_resource!@#$%^&*()'\"`~<>+-/;:_=[]{}|\\">>}, {slave_resource, <<"slave_resource!@#$%^&*()'\"`~<>+-/;:_=[]{}|\\">>}, @@ -130,42 +139,50 @@ process_config_tpl(Content, [{Name, DefaultValue} | Rest]) -> process_config_tpl(NewContent, Rest). stream_header(Config) -> - NSStream = ?config(ns_stream, Config), - XMLNS = ?config(xmlns, Config), - Lang = case ?config(lang, Config) of - <<"">> -> <<"">>; - L -> iolist_to_binary(["xml:lang='", L, "'"]) - end, To = case ?config(server, Config) of - <<"">> -> <<"">>; - Server -> <<"to='", Server/binary, "'">> + <<"">> -> undefined; + Server -> jid:make(Server) end, - Version = case ?config(stream_version, Config) of - <<"">> -> <<"">>; - V -> <<"version='", V/binary, "'">> - end, - io_lib:format("", - [NSStream, XMLNS, To, Version, Lang]). + From = case ?config(stream_from, Config) of + <<"">> -> undefined; + Frm -> jid:make(Frm) + end, + #stream_start{to = To, + from = From, + lang = ?config(lang, Config), + version = ?config(stream_version, Config), + xmlns = ?config(xmlns, Config), + db_xmlns = ?config(db_xmlns, Config), + stream_xmlns = ?config(ns_stream, Config)}. connect(Config) -> NewConfig = init_stream(Config), case ?config(type, NewConfig) of client -> process_stream_features(NewConfig); + server -> process_stream_features(NewConfig); component -> NewConfig end. +tcp_connect(Config) -> + case ?config(socket, Config) of + undefined -> + {ok, Sock} = ejabberd_socket:connect( + ?config(server_host, Config), + ?config(server_port, Config), + [binary, {packet, 0}, {active, false}]), + set_opt(socket, Sock, Config); + _ -> + Config + end. + init_stream(Config) -> Version = ?config(stream_version, Config), - {ok, Sock} = ejabberd_socket:connect( - ?config(server_host, Config), - ?config(server_port, Config), - [binary, {packet, 0}, {active, false}]), - NewConfig = set_opt(socket, Sock, Config), - ok = send_text(NewConfig, stream_header(NewConfig)), + NewConfig = tcp_connect(Config), + send(NewConfig, stream_header(NewConfig)), XMLNS = case ?config(type, Config) of client -> ?NS_CLIENT; - component -> ?NS_COMPONENT + component -> ?NS_COMPONENT; + server -> ?NS_SERVER end, #stream_start{id = ID, xmlns = XMLNS, version = Version} = recv(), set_opt(stream_id, ID, NewConfig). @@ -206,13 +223,24 @@ close_socket(Config) -> Config. starttls(Config) -> + starttls(Config, false). + +starttls(Config, ShouldFail) -> send(Config, #starttls{}), - #starttls_proceed{} = recv(), - TLSSocket = ejabberd_socket:starttls( - ?config(socket, Config), - [{certfile, ?config(certfile, Config)}, - connect]), - process_stream_features(init_stream(set_opt(socket, TLSSocket, Config))). + case recv() of + #starttls_proceed{} when ShouldFail -> + ct:fail(starttls_should_have_failed); + #starttls_failure{} when ShouldFail -> + Config; + #starttls_failure{} -> + ct:fail(starttls_failed); + #starttls_proceed{} -> + TLSSocket = ejabberd_socket:starttls( + ?config(socket, Config), + [{certfile, ?config(certfile, Config)}, + connect]), + set_opt(socket, TLSSocket, Config) + end. zlib(Config) -> send(Config, #compress{methods = [<<"zlib">>]}), @@ -228,14 +256,19 @@ auth(Config, ShouldFail) -> Mechs = ?config(mechs, Config), HaveMD5 = lists:member(<<"DIGEST-MD5">>, Mechs), HavePLAIN = lists:member(<<"PLAIN">>, Mechs), + HaveExternal = lists:member(<<"EXTERNAL">>, Mechs), if HavePLAIN -> auth_SASL(<<"PLAIN">>, Config, ShouldFail); HaveMD5 -> auth_SASL(<<"DIGEST-MD5">>, Config, ShouldFail); + HaveExternal andalso Type == server -> + auth_SASL(<<"EXTERNAL">>, Config, ShouldFail); Type == client -> auth_legacy(Config, false, ShouldFail); Type == component -> - auth_component(Config, ShouldFail) + auth_component(Config, ShouldFail); + true -> + ct:fail(no_known_sasl_mechanism_available) end. bind(Config) -> @@ -341,10 +374,19 @@ wait_auth_SASL_result(Config, ShouldFail) -> ct:fail(sasl_auth_should_have_failed); #sasl_success{} -> ejabberd_socket:reset_stream(?config(socket, Config)), - send_text(Config, stream_header(Config)), - #stream_start{xmlns = ?NS_CLIENT, version = <<"1.0">>} = recv(), + send(Config, stream_header(Config)), + Type = ?config(type, Config), + NS = if Type == client -> ?NS_CLIENT; + Type == server -> ?NS_SERVER + end, + #stream_start{xmlns = NS, version = {1,0}} = recv(), #stream_features{sub_els = Fs} = recv(), - #xmpp_session{optional = true} = lists:keyfind(xmpp_session, 1, Fs), + if Type == client -> + #xmpp_session{optional = true} = + lists:keyfind(xmpp_session, 1, Fs); + true -> + ok + end, lists:foldl( fun(#feature_sm{}, ConfigAcc) -> set_opt(sm, true, ConfigAcc); @@ -427,7 +469,11 @@ send(State, Pkt) -> end, El = xmpp_codec:encode(NewPkt), ct:pal("sent: ~p <-~n~s", [El, xmpp_codec:pp(NewPkt)]), - ok = send_text(State, fxml:element_to_binary(El)), + Data = case NewPkt of + #stream_start{} -> fxml:element_to_header(El); + _ -> fxml:element_to_binary(El) + end, + ok = send_text(State, Data), NewID. send_recv(State, IQ) -> @@ -437,6 +483,9 @@ send_recv(State, IQ) -> sasl_new(<<"PLAIN">>, User, Server, Password) -> {<>, fun (_) -> {error, <<"Invalid SASL challenge">>} end}; +sasl_new(<<"EXTERNAL">>, _User, _Server, _Password) -> + {<<"">>, + fun(_) -> ct:fail(sasl_challenge_is_not_expected) end}; sasl_new(<<"DIGEST-MD5">>, User, Server, Password) -> {<<"">>, fun (ServerIn) -> @@ -567,11 +616,21 @@ set_opt(Opt, Val, Config) -> wait_for_master(Config) -> put_event(Config, slave_ready), - master_ready = get_event(Config). + case get_event(Config) of + master_ready -> + ok; + Other -> + suite:match_failure([Other], [master_ready]) + end. wait_for_slave(Config) -> put_event(Config, master_ready), - slave_ready = get_event(Config). + case get_event(Config) of + slave_ready -> + ok; + Other -> + suite:match_failure([Other], [slave_ready]) + end. make_iq_result(#iq{from = From} = IQ) -> IQ#iq{type = result, to = From, from = undefined, sub_els = []}. @@ -592,6 +651,7 @@ event_relay() -> event_relay(Events, Subscribers) -> receive {subscribe, From} -> + erlang:monitor(process, From), From ! {ok, self()}, lists:foreach( fun(Event) -> From ! {event, Event, self()} @@ -605,7 +665,14 @@ event_relay(Events, Subscribers) -> (_) -> ok end, Subscribers), - event_relay([Event|Events], Subscribers) + event_relay([Event|Events], Subscribers); + {'DOWN', _MRef, process, Pid, _Info} -> + NewSubscribers = lists:delete(Pid, Subscribers), + lists:foreach( + fun(Subscriber) -> + Subscriber ! {event, peer_down, self()} + end, NewSubscribers), + event_relay(Events, NewSubscribers) end. subscribe_to_events(Config) -> diff --git a/test/suite.hrl b/test/suite.hrl index 3095e1bb5..cbeedff53 100644 --- a/test/suite.hrl +++ b/test/suite.hrl @@ -75,6 +75,7 @@ -define(LDAP_VHOST, <<"ldap.localhost">>). -define(EXTAUTH_VHOST, <<"extauth.localhost">>). -define(RIAK_VHOST, <<"riak.localhost">>). +-define(S2S_VHOST, <<"s2s.localhost">>). insert(Val, N, Tuple) -> L = tuple_to_list(Tuple), diff --git a/tools/xmpp_codec.spec b/tools/xmpp_codec.spec index aa18899c9..7b5ca5e66 100644 --- a/tools/xmpp_codec.spec +++ b/tools/xmpp_codec.spec @@ -3215,7 +3215,9 @@ default = <<"">>}, #attr{name = <<"xml:lang">>, label = '$lang', default = <<"">>}, - #attr{name = <<"version">>, default = <<"">>}, + #attr{name = <<"version">>, + dec = {dec_version, []}, + enc = {enc_version, []}}, #attr{name = <<"id">>, default = <<"">>}]}). -xml(bob_data, @@ -3479,6 +3481,14 @@ enc_host_port({Host, Port}) -> enc_host_port(Addr) -> enc_ip(Addr). +-spec dec_version(_) -> {non_neg_integer(), non_neg_integer()}. +dec_version(S) -> + [Major, Minor] = binary:split(S, <<$.>>), + {binary_to_integer(Major), binary_to_integer(Minor)}. + +enc_version({Maj, Min}) -> + <<(integer_to_binary(Maj))/binary, $., (integer_to_binary(Min))/binary>>. + %% Local Variables: %% mode: erlang %% End: