diff options
author | Evgeniy Khramtsov <ekhramtsov@process-one.net> | 2018-02-09 18:12:50 +0300 |
---|---|---|
committer | Evgeniy Khramtsov <ekhramtsov@process-one.net> | 2018-02-09 18:12:50 +0300 |
commit | 672c2f75d3ebd659c514b0ce1ecc70ecaa99f31f (patch) | |
tree | 92111588ed1c5d5877dac12d348652cb87b44f6c | |
parent | 5c85106a41cffd52f68471597a1eb597e38bfdcd (diff) |
Introduce option 'validate_stream'
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.
-rw-r--r-- | src/ejabberd_bosh.erl | 3 | ||||
-rw-r--r-- | src/ejabberd_c2s.erl | 4 | ||||
-rw-r--r-- | src/ejabberd_config.erl | 14 | ||||
-rw-r--r-- | src/ejabberd_s2s_in.erl | 3 | ||||
-rw-r--r-- | src/ejabberd_service.erl | 15 | ||||
-rw-r--r-- | src/mod_admin_extra.erl | 3 | ||||
-rw-r--r-- | src/mod_announce.erl | 6 | ||||
-rw-r--r-- | src/mod_delegation.erl | 3 | ||||
-rw-r--r-- | src/mod_mam.erl | 3 | ||||
-rw-r--r-- | src/mod_offline.erl | 6 | ||||
-rw-r--r-- | src/mod_privilege.erl | 3 | ||||
-rw-r--r-- | src/xmpp_stream_in.erl | 5 | ||||
-rw-r--r-- | src/xmpp_stream_out.erl | 5 |
13 files changed, 49 insertions, 24 deletions
diff --git a/src/ejabberd_bosh.erl b/src/ejabberd_bosh.erl index 4a552f43c..1ec45a3e2 100644 --- a/src/ejabberd_bosh.erl +++ b/src/ejabberd_bosh.erl @@ -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{}} -> diff --git a/src/ejabberd_c2s.erl b/src/ejabberd_c2s.erl index e7ce7b910..1e81f4d1a 100644 --- a/src/ejabberd_c2s.erl +++ b/src/ejabberd_c2s.erl @@ -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) -> diff --git a/src/ejabberd_config.erl b/src/ejabberd_config.erl index f898936f5..5ec2556f6 100644 --- a/src/ejabberd_config.erl +++ b/src/ejabberd_config.erl @@ -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. diff --git a/src/ejabberd_s2s_in.erl b/src/ejabberd_s2s_in.erl index 025dc2962..5345727a2 100644 --- a/src/ejabberd_s2s_in.erl +++ b/src/ejabberd_s2s_in.erl @@ -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) -> diff --git a/src/ejabberd_service.erl b/src/ejabberd_service.erl index 7b3543ae2..7016cd77d 100644 --- a/src/ejabberd_service.erl +++ b/src/ejabberd_service.erl @@ -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, diff --git a/src/mod_admin_extra.erl b/src/mod_admin_extra.erl index ec7376e1d..8d530e5c8 100644 --- a/src/mod_admin_extra.erl +++ b/src/mod_admin_extra.erl @@ -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)]), diff --git a/src/mod_announce.erl b/src/mod_announce.erl index 6db1e4529..e9da1d9c7 100644 --- a/src/mod_announce.erl +++ b/src/mod_announce.erl @@ -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} -> diff --git a/src/mod_delegation.erl b/src/mod_delegation.erl index 350a2db37..9822e81fd 100644 --- a/src/mod_delegation.erl +++ b/src/mod_delegation.erl @@ -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) diff --git a/src/mod_mam.erl b/src/mod_mam.erl index 8e8f57171..97033bafb 100644 --- a/src/mod_mam.erl +++ b/src/mod_mam.erl @@ -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( diff --git a/src/mod_offline.erl b/src/mod_offline.erl index d9f66843e..6b32fc98c 100644 --- a/src/mod_offline.erl +++ b/src/mod_offline.erl @@ -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), diff --git a/src/mod_privilege.erl b/src/mod_privilege.erl index 8f2f446ee..ceed74d31 100644 --- a/src/mod_privilege.erl +++ b/src/mod_privilege.erl @@ -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} -> diff --git a/src/xmpp_stream_in.erl b/src/xmpp_stream_in.erl index 6e07e9006..4f8be911e 100644 --- a/src/xmpp_stream_in.erl +++ b/src/xmpp_stream_in.erl @@ -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 diff --git a/src/xmpp_stream_out.erl b/src/xmpp_stream_out.erl index ce67d4231..b2367a09b 100644 --- a/src/xmpp_stream_out.erl +++ b/src/xmpp_stream_out.erl @@ -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 |