mirror of
https://github.com/processone/ejabberd
synced 2025-10-03 09:49:18 +02: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:
parent
5c85106a41
commit
672c2f75d3
13 changed files with 49 additions and 24 deletions
|
@ -739,9 +739,10 @@ bounce_receivers(State, Reason) ->
|
||||||
State, Receivers ++ ShapedReceivers).
|
State, Receivers ++ ShapedReceivers).
|
||||||
|
|
||||||
bounce_els_from_obuf(State) ->
|
bounce_els_from_obuf(State) ->
|
||||||
|
Opts = ejabberd_config:codec_options(State#state.host),
|
||||||
p1_queue:foreach(
|
p1_queue:foreach(
|
||||||
fun({xmlstreamelement, El}) ->
|
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) ->
|
Pkt when ?is_stanza(Pkt) ->
|
||||||
case {xmpp:get_from(Pkt), xmpp:get_to(Pkt)} of
|
case {xmpp:get_from(Pkt), xmpp:get_to(Pkt)} of
|
||||||
{#jid{}, #jid{}} ->
|
{#jid{}, #jid{}} ->
|
||||||
|
|
|
@ -418,8 +418,10 @@ handle_stream_start(StreamStart, #{lserver := LServer} = State) ->
|
||||||
send(State#{lserver => ?MYNAME}, xmpp:serr_host_unknown());
|
send(State#{lserver => ?MYNAME}, xmpp:serr_host_unknown());
|
||||||
true ->
|
true ->
|
||||||
State1 = change_shaper(State),
|
State1 = change_shaper(State),
|
||||||
|
Opts = ejabberd_config:codec_options(LServer),
|
||||||
|
State2 = State1#{codec_options => Opts},
|
||||||
ejabberd_hooks:run_fold(
|
ejabberd_hooks:run_fold(
|
||||||
c2s_stream_started, LServer, State1, [StreamStart])
|
c2s_stream_started, LServer, State2, [StreamStart])
|
||||||
end.
|
end.
|
||||||
|
|
||||||
handle_stream_end(Reason, #{lserver := LServer} = State) ->
|
handle_stream_end(Reason, #{lserver := LServer} = State) ->
|
||||||
|
|
|
@ -36,7 +36,8 @@
|
||||||
is_elixir_enabled/0, v_dbs/1, v_dbs_mods/1,
|
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_db/1, default_db/2, default_ram_db/1, default_ram_db/2,
|
||||||
default_queue_type/1, queue_dir/0, fsm_limit_opts/1,
|
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]).
|
-export([start/2]).
|
||||||
|
|
||||||
|
@ -1418,11 +1419,13 @@ opt_type(shared_key) ->
|
||||||
fun iolist_to_binary/1;
|
fun iolist_to_binary/1;
|
||||||
opt_type(node_start) ->
|
opt_type(node_start) ->
|
||||||
fun(I) when is_integer(I), I>=0 -> I end;
|
fun(I) when is_integer(I), I>=0 -> I end;
|
||||||
|
opt_type(validate_stream) ->
|
||||||
|
fun(B) when is_boolean(B) -> B end;
|
||||||
opt_type(_) ->
|
opt_type(_) ->
|
||||||
[hide_sensitive_log_data, hosts, language, max_fsm_queue,
|
[hide_sensitive_log_data, hosts, language, max_fsm_queue,
|
||||||
default_db, default_ram_db, queue_type, queue_dir, loglevel,
|
default_db, default_ram_db, queue_type, queue_dir, loglevel,
|
||||||
use_cache, cache_size, cache_missed, cache_life_time,
|
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().
|
-spec may_hide_data(any()) -> any().
|
||||||
may_hide_data(Data) ->
|
may_hide_data(Data) ->
|
||||||
|
@ -1469,3 +1472,10 @@ cache_missed(Host) ->
|
||||||
%% NOTE: the integer value returned is in *seconds*
|
%% NOTE: the integer value returned is in *seconds*
|
||||||
cache_life_time(Host) ->
|
cache_life_time(Host) ->
|
||||||
get_option({cache_life_time, Host}, 3600).
|
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.
|
||||||
|
|
|
@ -169,7 +169,8 @@ handle_stream_start(_StreamStart, #{lserver := LServer} = State) ->
|
||||||
send(State, xmpp:serr_host_unknown());
|
send(State, xmpp:serr_host_unknown());
|
||||||
true ->
|
true ->
|
||||||
ServerHost = ejabberd_router:host_of_route(LServer),
|
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.
|
end.
|
||||||
|
|
||||||
handle_stream_end(Reason, #{server_host := LServer} = State) ->
|
handle_stream_end(Reason, #{server_host := LServer} = State) ->
|
||||||
|
|
|
@ -131,7 +131,8 @@ handle_stream_start(_StreamStart,
|
||||||
HostOpts
|
HostOpts
|
||||||
end
|
end
|
||||||
end,
|
end,
|
||||||
State#{host_opts => NewHostOpts}
|
CodecOpts = ejabberd_config:codec_options(global),
|
||||||
|
State#{host_opts => NewHostOpts, codec_options => CodecOpts}
|
||||||
end.
|
end.
|
||||||
|
|
||||||
get_password_fun(#{remote_server := RemoteServer,
|
get_password_fun(#{remote_server := RemoteServer,
|
||||||
|
|
|
@ -1549,7 +1549,8 @@ send_stanza(FromString, ToString, Stanza) ->
|
||||||
#xmlel{} = El = fxml_stream:parse_element(Stanza),
|
#xmlel{} = El = fxml_stream:parse_element(Stanza),
|
||||||
From = jid:decode(FromString),
|
From = jid:decode(FromString),
|
||||||
To = jid:decode(ToString),
|
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))
|
ejabberd_router:route(xmpp:set_from_to(Pkt, From, To))
|
||||||
catch _:{xmpp_codec, Why} ->
|
catch _:{xmpp_codec, Why} ->
|
||||||
io:format("incorrect stanza: ~s~n", [xmpp:format_error(Why)]),
|
io:format("incorrect stanza: ~s~n", [xmpp:format_error(Why)]),
|
||||||
|
|
|
@ -715,7 +715,8 @@ send_motd({#presence{type = available},
|
||||||
Mod = gen_mod:db_mod(LServer, ?MODULE),
|
Mod = gen_mod:db_mod(LServer, ?MODULE),
|
||||||
case get_motd(Mod, LServer) of
|
case get_motd(Mod, LServer) of
|
||||||
{ok, Packet} ->
|
{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 ->
|
Msg ->
|
||||||
case is_motd_user(Mod, LUser, LServer) of
|
case is_motd_user(Mod, LUser, LServer) of
|
||||||
false ->
|
false ->
|
||||||
|
@ -806,7 +807,8 @@ get_stored_motd(LServer) ->
|
||||||
Mod = gen_mod:db_mod(LServer, ?MODULE),
|
Mod = gen_mod:db_mod(LServer, ?MODULE),
|
||||||
case get_motd(Mod, LServer) of
|
case get_motd(Mod, LServer) of
|
||||||
{ok, Packet} ->
|
{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} ->
|
#message{body = Body, subject = Subject} ->
|
||||||
{xmpp:get_text(Subject), xmpp:get_text(Body)}
|
{xmpp:get_text(Subject), xmpp:get_text(Body)}
|
||||||
catch _:{xmpp_codec, Why} ->
|
catch _:{xmpp_codec, Why} ->
|
||||||
|
|
|
@ -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,
|
process_iq_result(#iq{from = From, to = To, id = ID, lang = Lang} = IQ,
|
||||||
#iq{type = result} = ResIQ) ->
|
#iq{type = result} = ResIQ) ->
|
||||||
try
|
try
|
||||||
|
CodecOpts = ejabberd_config:codec_options(To#jid.lserver),
|
||||||
#delegation{forwarded = #forwarded{sub_els = [SubEl]}} =
|
#delegation{forwarded = #forwarded{sub_els = [SubEl]}} =
|
||||||
xmpp:get_subtag(ResIQ, #delegation{}),
|
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
|
#iq{from = To, to = From, type = Type, id = ID} = Reply
|
||||||
when Type == error; Type == result ->
|
when Type == error; Type == result ->
|
||||||
ejabberd_router:route(Reply)
|
ejabberd_router:route(Reply)
|
||||||
|
|
|
@ -918,7 +918,8 @@ select(LServer, JidRequestor, JidArchive, Query, RSM, MsgType) ->
|
||||||
msg_to_el(#archive_msg{timestamp = TS, packet = El, nick = Nick,
|
msg_to_el(#archive_msg{timestamp = TS, packet = El, nick = Nick,
|
||||||
peer = Peer, id = ID},
|
peer = Peer, id = ID},
|
||||||
MsgType, JidRequestor, #jid{lserver = LServer} = JidArchive) ->
|
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 ->
|
Pkt1 ->
|
||||||
Pkt2 = set_stanza_id(Pkt1, JidArchive, ID),
|
Pkt2 = set_stanza_id(Pkt1, JidArchive, ID),
|
||||||
Pkt3 = maybe_update_from_to(
|
Pkt3 = maybe_update_from_to(
|
||||||
|
|
|
@ -596,7 +596,8 @@ get_offline_els(LUser, LServer) ->
|
||||||
-spec offline_msg_to_route(binary(), #offline_msg{}) ->
|
-spec offline_msg_to_route(binary(), #offline_msg{}) ->
|
||||||
{route, message()} | error.
|
{route, message()} | error.
|
||||||
offline_msg_to_route(LServer, #offline_msg{from = From, to = To} = R) ->
|
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 ->
|
Pkt ->
|
||||||
Pkt1 = xmpp:set_from_to(Pkt, From, To),
|
Pkt1 = xmpp:set_from_to(Pkt, From, To),
|
||||||
Pkt2 = add_delay_info(Pkt1, LServer, R#offline_msg.timestamp),
|
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()}].
|
-spec read_messages(binary(), binary()) -> [{binary(), message()}].
|
||||||
read_messages(LUser, LServer) ->
|
read_messages(LUser, LServer) ->
|
||||||
Mod = gen_mod:db_mod(LServer, ?MODULE),
|
Mod = gen_mod:db_mod(LServer, ?MODULE),
|
||||||
|
CodecOpts = ejabberd_config:codec_options(LServer),
|
||||||
lists:flatmap(
|
lists:flatmap(
|
||||||
fun({Seq, From, To, TS, El}) ->
|
fun({Seq, From, To, TS, El}) ->
|
||||||
Node = integer_to_binary(Seq),
|
Node = integer_to_binary(Seq),
|
||||||
try xmpp:decode(El, ?NS_CLIENT, [ignore_els]) of
|
try xmpp:decode(El, ?NS_CLIENT, CodecOpts) of
|
||||||
Pkt ->
|
Pkt ->
|
||||||
Node = integer_to_binary(Seq),
|
Node = integer_to_binary(Seq),
|
||||||
Pkt1 = add_delay_info(Pkt, LServer, TS),
|
Pkt1 = add_delay_info(Pkt, LServer, TS),
|
||||||
|
|
|
@ -276,9 +276,10 @@ get_permissions(ServerHost) ->
|
||||||
forward_message(#message{to = To} = Msg) ->
|
forward_message(#message{to = To} = Msg) ->
|
||||||
ServerHost = To#jid.lserver,
|
ServerHost = To#jid.lserver,
|
||||||
Lang = xmpp:get_lang(Msg),
|
Lang = xmpp:get_lang(Msg),
|
||||||
|
CodecOpts = ejabberd_config:codec_options(ServerHost),
|
||||||
try xmpp:try_subtag(Msg, #privilege{}) of
|
try xmpp:try_subtag(Msg, #privilege{}) of
|
||||||
#privilege{forwarded = #forwarded{sub_els = [SubEl]}} ->
|
#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 ->
|
#message{} = NewMsg ->
|
||||||
case NewMsg#message.from of
|
case NewMsg#message.from of
|
||||||
#jid{lresource = <<"">>, lserver = ServerHost} ->
|
#jid{lresource = <<"">>, lserver = ServerHost} ->
|
||||||
|
|
|
@ -230,6 +230,7 @@ init([Module, {_SockMod, Socket}, Opts]) ->
|
||||||
stream_encrypted => Encrypted,
|
stream_encrypted => Encrypted,
|
||||||
stream_version => {1,0},
|
stream_version => {1,0},
|
||||||
stream_authenticated => false,
|
stream_authenticated => false,
|
||||||
|
codec_options => [ignore_els],
|
||||||
xmlns => ?NS_CLIENT,
|
xmlns => ?NS_CLIENT,
|
||||||
lang => <<"">>,
|
lang => <<"">>,
|
||||||
user => <<"">>,
|
user => <<"">>,
|
||||||
|
@ -342,9 +343,9 @@ handle_info({'$gen_event', El}, #{stream_state := wait_for_stream} = State) ->
|
||||||
false -> send_pkt(State1, xmpp:serr_invalid_xml())
|
false -> send_pkt(State1, xmpp:serr_invalid_xml())
|
||||||
end);
|
end);
|
||||||
handle_info({'$gen_event', {xmlstreamelement, El}},
|
handle_info({'$gen_event', {xmlstreamelement, El}},
|
||||||
#{xmlns := NS, mod := Mod} = State) ->
|
#{xmlns := NS, mod := Mod, codec_options := Opts} = State) ->
|
||||||
noreply(
|
noreply(
|
||||||
try xmpp:decode(El, NS, [ignore_els]) of
|
try xmpp:decode(El, NS, Opts) of
|
||||||
Pkt ->
|
Pkt ->
|
||||||
State1 = try Mod:handle_recv(El, Pkt, State)
|
State1 = try Mod:handle_recv(El, Pkt, State)
|
||||||
catch _:undef -> State
|
catch _:undef -> State
|
||||||
|
|
|
@ -244,6 +244,7 @@ init([Mod, _SockMod, From, To, Opts]) ->
|
||||||
lang => <<"">>,
|
lang => <<"">>,
|
||||||
remote_server => To,
|
remote_server => To,
|
||||||
xmlns => ?NS_SERVER,
|
xmlns => ?NS_SERVER,
|
||||||
|
codec_options => [ignore_els],
|
||||||
stream_direction => out,
|
stream_direction => out,
|
||||||
stream_timeout => {timer:seconds(30), Time},
|
stream_timeout => {timer:seconds(30), Time},
|
||||||
stream_id => new_id(),
|
stream_id => new_id(),
|
||||||
|
@ -347,9 +348,9 @@ handle_info({'$gen_event', {xmlstreamerror, Reason}}, #{lang := Lang}= State) ->
|
||||||
send_pkt(State1, Err)
|
send_pkt(State1, Err)
|
||||||
end);
|
end);
|
||||||
handle_info({'$gen_event', {xmlstreamelement, El}},
|
handle_info({'$gen_event', {xmlstreamelement, El}},
|
||||||
#{xmlns := NS, mod := Mod} = State) ->
|
#{xmlns := NS, mod := Mod, codec_options := Opts} = State) ->
|
||||||
noreply(
|
noreply(
|
||||||
try xmpp:decode(El, NS, [ignore_els]) of
|
try xmpp:decode(El, NS, Opts) of
|
||||||
Pkt ->
|
Pkt ->
|
||||||
State1 = try Mod:handle_recv(El, Pkt, State)
|
State1 = try Mod:handle_recv(El, Pkt, State)
|
||||||
catch _:undef -> State
|
catch _:undef -> State
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue