]> granicus.if.org Git - ejabberd/commitdiff
Add tests for s2s code
authorEvgeniy Khramtsov <ekhramtsov@process-one.net>
Fri, 23 Sep 2016 09:30:33 +0000 (12:30 +0300)
committerEvgeniy Khramtsov <ekhramtsov@process-one.net>
Fri, 23 Sep 2016 09:30:33 +0000 (12:30 +0300)
19 files changed:
include/xmpp_codec.hrl
src/ejabberd_c2s.erl
src/ejabberd_s2s.erl
src/ejabberd_s2s_in.erl
src/ejabberd_s2s_out.erl
src/ejabberd_service.erl
src/xmpp_codec.erl
test/ejabberd_SUITE.erl
test/ejabberd_SUITE_data/ca.key [new file with mode: 0644]
test/ejabberd_SUITE_data/ca.pem [new file with mode: 0644]
test/ejabberd_SUITE_data/cert.pem
test/ejabberd_SUITE_data/ejabberd.yml
test/ejabberd_SUITE_data/extauth.py
test/ejabberd_SUITE_data/gencerts.sh [new file with mode: 0755]
test/ejabberd_SUITE_data/openssl.cnf [new file with mode: 0644]
test/ejabberd_SUITE_data/self-signed-cert.pem [new file with mode: 0644]
test/suite.erl
test/suite.hrl
tools/xmpp_codec.spec

index 845861948431322f067aeec544c68e712ace22a4..5428aad1176c68100c6210f130e698d2c71a08be 100644 (file)
 -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(),
index 7dc9960e643d5e61249f4447519ec6fb32228591..858e285a68a50e073b6a7b12d0c2ff2d45262908 100644 (file)
@@ -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)
index e585257e88840b990f26a70910ac4d91566210f8..3c3e698ad049d7c4d716bbc4e06ce67333bb1b4e 100644 (file)
@@ -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
 
index fd560a45136c2a34d982d78abaa143f393f16322..6d1791d0bd9e17523998f417ac8f6bda8859d954 100644 (file)
@@ -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, <<"</stream:stream>">>).
 
--spec send_header(state(), binary()) -> ok.
+-spec send_header(state(), undefined | {integer(), integer()}) -> ok.
 send_header(StateData, Version) ->
-    send_text(StateData,
-             <<"<?xml version='1.0'?><stream:stream "
-               "xmlns:stream='http://etherx.jabber.org/stream"
-               "s' xmlns='jabber:server' xmlns:db='jabber:ser"
-               "ver:dialback' id='",
-               (StateData#state.streamid)/binary, "'", Version/binary,
-               ">">>).
+    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].
index dd37445d7c60ae80a9dbfd6707ac81e76535e29a..06ba16863dad09e8d28ef7180719f64b4a1f8fc5 100644 (file)
 %% Specified in miliseconds. Default value is 5 minutes.
 -define(MAX_RETRY_DELAY, 300000).
 
--define(STREAM_HEADER,
-       <<"<?xml version='1.0'?><stream:stream "
-         "xmlns:stream='http://etherx.jabber.org/stream"
-         "s' xmlns='jabber:server' xmlns:db='jabber:ser"
-         "ver:dialback' from='~s' to='~s'~s>">>).
-
 -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(
-           "<?xml version='1.0'?><stream:stream "
-           "xmlns:stream='http://etherx.jabber.org/stream"
-           "s' xmlns='jabber:server' xmlns:db='jabber:ser"
-           "ver:dialback' from='~s' to='~s'~s>",
-           [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) ->
index f4338593d65e03af513e94d7cb40ceae7dd0b587..46d32e4fd8309673f7571d90df3595a61c24ba19 100644 (file)
@@ -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(
-               <<"<?xml version='1.0'?><stream:stream "
-                 "xmlns:stream='http://etherx.jabber.org/stream"
-                 "s' xmlns='jabber:component:accept' id='~s' "
-                 "from='~s'>">>,
-               [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) ->
index 00ee53aaf8ec179bf6d7ef06478f829ce74e749e..0a9258195b25b28ca480ecc1f631e95414416e9b 100644 (file)
@@ -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) ->
     <<>>;
index dbcd9a905fc9c688f1f263ac2c574d949869ebc8..063f51bd1cdc215aaee908d2c22e4feec804ff96 100644 (file)
                 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 (file)
index 0000000..8581006
--- /dev/null
@@ -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 (file)
index 0000000..3daa7f5
--- /dev/null
@@ -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-----
index 11e18491f4f243717d18486ba8ffe3becc9d44f4..ee9cf16417a04ce03660f0e0df651cff2769be3a 100644 (file)
@@ -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-----
index 99af18bbf79c3caa98a712efc389a22992756012..128be2aed14a89b28a2c749f7f0ee2f7049e9bfd 100644 (file)
@@ -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
index 84c000144ce1a1d70da0e8469789e9e1326af13a..fa2c9efd06bff63922aa8826bb687a4f43f04a50 100755 (executable)
@@ -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 (executable)
index 0000000..d0acd4b
--- /dev/null
@@ -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 (file)
index 0000000..ff11d14
--- /dev/null
@@ -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 (file)
index 0000000..d6b34f5
--- /dev/null
@@ -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-----
index f78b7bd1a905e5e1ddce1d055428ff3714521485..e4be0054f85c2e66ceb90a1785949ef874d0d0c1 100644 (file)
@@ -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("<?xml version='1.0'?><stream:stream "
-                 "xmlns:stream='~s' xmlns='~s' ~s ~s ~s>",
-                 [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) ->
     {<<User/binary, $@, Server/binary, 0, User/binary, 0, Password/binary>>,
      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) ->
index 3095e1bb5c50c397144dc04bc4fbbbe0c4b9c4e4..cbeedff53ebcebf6fbf464c1de86404b8f27657e 100644 (file)
@@ -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),
index aa18899c9782b74c0302155a34648876c401cf2c..7b5ca5e666589af01155ede244a7d8f50f42200a 100644 (file)
                          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: