25
1
mirror of https://github.com/processone/ejabberd.git synced 2024-11-20 16:15:59 +01:00

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.
This commit is contained in:
Evgeniy Khramtsov 2018-02-09 18:12:50 +03:00
parent 5c85106a41
commit 672c2f75d3
13 changed files with 49 additions and 24 deletions

View 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{}} ->

View 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) ->

View 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.

View 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) ->

View 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,

View 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)]),

View 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} ->

View 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)

View 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(

View 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),

View 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} ->

View 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

View 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