]> granicus.if.org Git - ejabberd/commitdiff
Introduce option 'validate_stream'
authorEvgeniy Khramtsov <ekhramtsov@process-one.net>
Fri, 9 Feb 2018 15:12:50 +0000 (18:12 +0300)
committerEvgeniy Khramtsov <ekhramtsov@process-one.net>
Fri, 9 Feb 2018 15:12:50 +0000 (18:12 +0300)
If set to `true`, all incoming XML packets are fully validated
against known schemas. If an error occurs, the packet will be bounced
with the corresponding error reason. The default value is `false`.
The option might be useful to protect client software from sofisticated
bugs related to XML validation as well as for client developers
who want to catch validation errors at early stage of development.

Note that the option might have slight performance impact, so use it
with care on loaded machines.

13 files changed:
src/ejabberd_bosh.erl
src/ejabberd_c2s.erl
src/ejabberd_config.erl
src/ejabberd_s2s_in.erl
src/ejabberd_service.erl
src/mod_admin_extra.erl
src/mod_announce.erl
src/mod_delegation.erl
src/mod_mam.erl
src/mod_offline.erl
src/mod_privilege.erl
src/xmpp_stream_in.erl
src/xmpp_stream_out.erl

index 4a552f43c5f1474471543fb781a12be5dee94550..1ec45a3e2fb2925da454332df9abdad98dd0ef78 100644 (file)
@@ -739,9 +739,10 @@ bounce_receivers(State, Reason) ->
                State, Receivers ++ ShapedReceivers).
 
 bounce_els_from_obuf(State) ->
+    Opts = ejabberd_config:codec_options(State#state.host),
     p1_queue:foreach(
       fun({xmlstreamelement, El}) ->
-             try xmpp:decode(El, ?NS_CLIENT, [ignore_els]) of
+             try xmpp:decode(El, ?NS_CLIENT, Opts) of
                  Pkt when ?is_stanza(Pkt) ->
                      case {xmpp:get_from(Pkt), xmpp:get_to(Pkt)} of
                          {#jid{}, #jid{}} ->
index e7ce7b910d38a88eb85ae08bbeab3d8691dd47da..1e81f4d1aa9a163a3244f64cb6eed1b18f9582df 100644 (file)
@@ -418,8 +418,10 @@ handle_stream_start(StreamStart, #{lserver := LServer} = State) ->
            send(State#{lserver => ?MYNAME}, xmpp:serr_host_unknown());
        true ->
            State1 = change_shaper(State),
+           Opts = ejabberd_config:codec_options(LServer),
+           State2 = State1#{codec_options => Opts},
            ejabberd_hooks:run_fold(
-             c2s_stream_started, LServer, State1, [StreamStart])
+             c2s_stream_started, LServer, State2, [StreamStart])
     end.
 
 handle_stream_end(Reason, #{lserver := LServer} = State) ->
index f898936f56430bfdec2b3d8ddbe3d41f53364a39..5ec2556f65af2499ad4fe5451afba0fdb602d057 100644 (file)
@@ -36,7 +36,8 @@
         is_elixir_enabled/0, v_dbs/1, v_dbs_mods/1,
         default_db/1, default_db/2, default_ram_db/1, default_ram_db/2,
         default_queue_type/1, queue_dir/0, fsm_limit_opts/1,
-        use_cache/1, cache_size/1, cache_missed/1, cache_life_time/1]).
+        use_cache/1, cache_size/1, cache_missed/1, cache_life_time/1,
+        codec_options/1]).
 
 -export([start/2]).
 
@@ -1418,11 +1419,13 @@ opt_type(shared_key) ->
     fun iolist_to_binary/1;
 opt_type(node_start) ->
     fun(I) when is_integer(I), I>=0 -> I end;
+opt_type(validate_stream) ->
+    fun(B) when is_boolean(B) -> B end;
 opt_type(_) ->
     [hide_sensitive_log_data, hosts, language, max_fsm_queue,
      default_db, default_ram_db, queue_type, queue_dir, loglevel,
      use_cache, cache_size, cache_missed, cache_life_time,
-     shared_key, node_start].
+     shared_key, node_start, validate_stream].
 
 -spec may_hide_data(any()) -> any().
 may_hide_data(Data) ->
@@ -1469,3 +1472,10 @@ cache_missed(Host) ->
 %% NOTE: the integer value returned is in *seconds*
 cache_life_time(Host) ->
     get_option({cache_life_time, Host}, 3600).
+
+-spec codec_options(binary() | global) -> [xmpp:decode_option()].
+codec_options(Host) ->
+    case get_option({validate_stream, Host}, false) of
+       true -> [];
+       false -> [ignore_els]
+    end.
index 025dc296298366f11b52345498b4797d00f5219d..5345727a298cd90bf12d3d2223003d073c2a94cf 100644 (file)
@@ -169,7 +169,8 @@ handle_stream_start(_StreamStart, #{lserver := LServer} = State) ->
            send(State, xmpp:serr_host_unknown());
        true ->
            ServerHost = ejabberd_router:host_of_route(LServer),
-           State#{server_host => ServerHost}
+           Opts = ejabberd_config:codec_options(LServer),
+           State#{server_host => ServerHost, codec_options => Opts}
     end.
 
 handle_stream_end(Reason, #{server_host := LServer} = State) ->
index 7b3543ae2fddab896fe467412036cace1d0222c1..7016cd77d5ea8b9e69ec06822c789ab0fcbb40d0 100644 (file)
@@ -116,22 +116,23 @@ handle_stream_start(_StreamStart,
                      lang := Lang,
                      host_opts := HostOpts} = State) ->
     case ejabberd_router:is_my_host(RemoteServer) of
-                          true ->
+       true ->
            Txt = <<"Unable to register route on existing local domain">>,
            xmpp_stream_in:send(State, xmpp:serr_conflict(Txt, Lang));
-                          false ->
+       false ->
            NewHostOpts = case dict:is_key(RemoteServer, HostOpts) of
                              true ->
                                  HostOpts;
                              false ->
                                  case dict:find(global, HostOpts) of
-                                  {ok, GlobalPass} ->
+                                     {ok, GlobalPass} ->
                                          dict:from_list([{RemoteServer, GlobalPass}]);
-                                  error ->
+                                     error ->
                                          HostOpts
-                              end
-                      end,
-           State#{host_opts => NewHostOpts}
+                                 end
+                         end,
+           CodecOpts = ejabberd_config:codec_options(global),
+           State#{host_opts => NewHostOpts, codec_options => CodecOpts}
     end.
 
 get_password_fun(#{remote_server := RemoteServer,
index ec7376e1dacc3a80fc822281f47a91f1a6b52838..8d530e5c8608b9fad8c6ed1543163a53b9fd5171 100644 (file)
@@ -1549,7 +1549,8 @@ send_stanza(FromString, ToString, Stanza) ->
        #xmlel{} = El = fxml_stream:parse_element(Stanza),
        From = jid:decode(FromString),
        To = jid:decode(ToString),
-       Pkt = xmpp:decode(El, ?NS_CLIENT, [ignore_els]),
+       CodecOpts = ejabberd_config:codec_options(From#jid.lserver),
+       Pkt = xmpp:decode(El, ?NS_CLIENT, CodecOpts),
        ejabberd_router:route(xmpp:set_from_to(Pkt, From, To))
     catch _:{xmpp_codec, Why} ->
            io:format("incorrect stanza: ~s~n", [xmpp:format_error(Why)]),
index 6db1e4529f800658ee5703182e8fb7c97cc5b554..e9da1d9c7c0e9be78ef236bed294aacd7680a5fe 100644 (file)
@@ -715,7 +715,8 @@ send_motd({#presence{type = available},
     Mod = gen_mod:db_mod(LServer, ?MODULE),
     case get_motd(Mod, LServer) of
        {ok, Packet} ->
-           try xmpp:decode(Packet, ?NS_CLIENT, [ignore_els]) of
+           CodecOpts = ejabberd_config:codec_options(LServer),
+           try xmpp:decode(Packet, ?NS_CLIENT, CodecOpts) of
                Msg ->
                    case is_motd_user(Mod, LUser, LServer) of
                        false ->
@@ -806,7 +807,8 @@ get_stored_motd(LServer) ->
     Mod = gen_mod:db_mod(LServer, ?MODULE),
     case get_motd(Mod, LServer) of
         {ok, Packet} ->
-           try xmpp:decode(Packet, ?NS_CLIENT, [ignore_els]) of
+           CodecOpts = ejabberd_config:codec_options(LServer),
+           try xmpp:decode(Packet, ?NS_CLIENT, CodecOpts) of
                #message{body = Body, subject = Subject} ->
                    {xmpp:get_text(Subject), xmpp:get_text(Body)}
            catch _:{xmpp_codec, Why} ->
index 350a2db376247f5eb70206fc5485d81d52587d39..9822e81fd5302e240dd0337719e53c745c3139b5 100644 (file)
@@ -261,9 +261,10 @@ process_iq(#iq{to = To, lang = Lang, sub_els = [SubEl]} = IQ, Type) ->
 process_iq_result(#iq{from = From, to = To, id = ID, lang = Lang} = IQ,
                  #iq{type = result} = ResIQ) ->
     try
+       CodecOpts = ejabberd_config:codec_options(To#jid.lserver),
        #delegation{forwarded = #forwarded{sub_els = [SubEl]}} =
            xmpp:get_subtag(ResIQ, #delegation{}),
-       case xmpp:decode(SubEl, ?NS_CLIENT, [ignore_els]) of
+       case xmpp:decode(SubEl, ?NS_CLIENT, CodecOpts) of
            #iq{from = To, to = From, type = Type, id = ID} = Reply
              when Type == error; Type == result ->
                ejabberd_router:route(Reply)
index 8e8f57171adf2b6c6f8693e787b7b349d327bb3f..97033bafb3fdbb5d6f30e5e521da47bc12587345 100644 (file)
@@ -918,7 +918,8 @@ select(LServer, JidRequestor, JidArchive, Query, RSM, MsgType) ->
 msg_to_el(#archive_msg{timestamp = TS, packet = El, nick = Nick,
                       peer = Peer, id = ID},
          MsgType, JidRequestor, #jid{lserver = LServer} = JidArchive) ->
-    try xmpp:decode(El, ?NS_CLIENT, [ignore_els]) of
+    CodecOpts = ejabberd_config:codec_options(LServer),
+    try xmpp:decode(El, ?NS_CLIENT, CodecOpts) of
        Pkt1 ->
            Pkt2 = set_stanza_id(Pkt1, JidArchive, ID),
            Pkt3 = maybe_update_from_to(
index d9f66843e5f3b6ae013aa4f0885a312431105d4b..6b32fc98c07d39c836ef231a0e87ecfffeacb660 100644 (file)
@@ -596,7 +596,8 @@ get_offline_els(LUser, LServer) ->
 -spec offline_msg_to_route(binary(), #offline_msg{}) ->
                                  {route, message()} | error.
 offline_msg_to_route(LServer, #offline_msg{from = From, to = To} = R) ->
-    try xmpp:decode(R#offline_msg.packet, ?NS_CLIENT, [ignore_els]) of
+    CodecOpts = ejabberd_config:codec_options(LServer),
+    try xmpp:decode(R#offline_msg.packet, ?NS_CLIENT, CodecOpts) of
        Pkt ->
            Pkt1 = xmpp:set_from_to(Pkt, From, To),
            Pkt2 = add_delay_info(Pkt1, LServer, R#offline_msg.timestamp),
@@ -611,10 +612,11 @@ offline_msg_to_route(LServer, #offline_msg{from = From, to = To} = R) ->
 -spec read_messages(binary(), binary()) -> [{binary(), message()}].
 read_messages(LUser, LServer) ->
     Mod = gen_mod:db_mod(LServer, ?MODULE),
+    CodecOpts = ejabberd_config:codec_options(LServer),
     lists:flatmap(
       fun({Seq, From, To, TS, El}) ->
              Node = integer_to_binary(Seq),
-             try xmpp:decode(El, ?NS_CLIENT, [ignore_els]) of
+             try xmpp:decode(El, ?NS_CLIENT, CodecOpts) of
                  Pkt ->
                      Node = integer_to_binary(Seq),
                      Pkt1 = add_delay_info(Pkt, LServer, TS),
index 8f2f446ee6f2520f7406d3699866867211186fd3..ceed74d31f433f9aae7826ba1a3045bd4a2b0aed 100644 (file)
@@ -276,9 +276,10 @@ get_permissions(ServerHost) ->
 forward_message(#message{to = To} = Msg) ->
     ServerHost = To#jid.lserver,
     Lang = xmpp:get_lang(Msg),
+    CodecOpts = ejabberd_config:codec_options(ServerHost),
     try xmpp:try_subtag(Msg, #privilege{}) of
        #privilege{forwarded = #forwarded{sub_els = [SubEl]}} ->
-           try xmpp:decode(SubEl, ?NS_CLIENT, [ignore_els]) of
+           try xmpp:decode(SubEl, ?NS_CLIENT, CodecOpts) of
                #message{} = NewMsg ->
                    case NewMsg#message.from of
                        #jid{lresource = <<"">>, lserver = ServerHost} ->
index 6e07e9006cb189b890dac0fd68826ca72a942be3..4f8be911eac57c8dcf199f6d384673b7ea7283fc 100644 (file)
@@ -230,6 +230,7 @@ init([Module, {_SockMod, Socket}, Opts]) ->
                      stream_encrypted => Encrypted,
                      stream_version => {1,0},
                      stream_authenticated => false,
+                     codec_options => [ignore_els],
                      xmlns => ?NS_CLIENT,
                      lang => <<"">>,
                      user => <<"">>,
@@ -342,9 +343,9 @@ handle_info({'$gen_event', El}, #{stream_state := wait_for_stream} = State) ->
          false -> send_pkt(State1, xmpp:serr_invalid_xml())
       end);
 handle_info({'$gen_event', {xmlstreamelement, El}},
-           #{xmlns := NS, mod := Mod} = State) ->
+           #{xmlns := NS, mod := Mod, codec_options := Opts} = State) ->
     noreply(
-      try xmpp:decode(El, NS, [ignore_els]) of
+      try xmpp:decode(El, NS, Opts) of
          Pkt ->
              State1 = try Mod:handle_recv(El, Pkt, State)
                       catch _:undef -> State
index ce67d4231c7fd31b84be1779474eeb049d73066d..b2367a09bdce5a56f3dd661bd97610e2bcb67d73 100644 (file)
@@ -244,6 +244,7 @@ init([Mod, _SockMod, From, To, Opts]) ->
              lang => <<"">>,
              remote_server => To,
              xmlns => ?NS_SERVER,
+             codec_options => [ignore_els],
              stream_direction => out,
              stream_timeout => {timer:seconds(30), Time},
              stream_id => new_id(),
@@ -347,9 +348,9 @@ handle_info({'$gen_event', {xmlstreamerror, Reason}}, #{lang := Lang}= State) ->
              send_pkt(State1, Err)
       end);
 handle_info({'$gen_event', {xmlstreamelement, El}},
-           #{xmlns := NS, mod := Mod} = State) ->
+           #{xmlns := NS, mod := Mod, codec_options := Opts} = State) ->
     noreply(
-      try xmpp:decode(El, NS, [ignore_els]) of
+      try xmpp:decode(El, NS, Opts) of
          Pkt ->
              State1 = try Mod:handle_recv(El, Pkt, State)
                       catch _:undef -> State