24
1
mirror of https://github.com/processone/ejabberd.git synced 2024-06-04 21:27:16 +02:00

Respond with 'Bad Request' to unexpected 'Host' header

Where "unexpected" means the host in 'Host' header is not
a registered route. The rationale is to avoid propagation
of uknown "Host" further in the code, which may lead to
nasty errors related to reading configuration values, calling
functions from ejabberd_router.erl, etc.
This commit is contained in:
Evgeny Khramtsov 2019-06-23 13:12:00 +03:00
parent e477a8c220
commit 83c291c064

View File

@ -127,14 +127,12 @@ init(SockMod, Socket, Opts) ->
RequestHandlers = proplists:get_value(request_handlers, Opts, []), RequestHandlers = proplists:get_value(request_handlers, Opts, []),
?DEBUG("S: ~p~n", [RequestHandlers]), ?DEBUG("S: ~p~n", [RequestHandlers]),
DefaultHost = proplists:get_value(default_host, Opts),
{ok, RE} = re:compile(<<"^(?:\\[(.*?)\\]|(.*?))(?::(\\d+))?$">>), {ok, RE} = re:compile(<<"^(?:\\[(.*?)\\]|(.*?))(?::(\\d+))?$">>),
CustomHeaders = proplists:get_value(custom_headers, Opts, []), CustomHeaders = proplists:get_value(custom_headers, Opts, []),
State = #state{sockmod = SockMod1, State = #state{sockmod = SockMod1,
socket = Socket1, socket = Socket1,
default_host = DefaultHost,
custom_headers = CustomHeaders, custom_headers = CustomHeaders,
options = Opts, options = Opts,
request_handlers = RequestHandlers, request_handlers = RequestHandlers,
@ -264,19 +262,20 @@ process_header(State, Data) ->
{http_header, _, 'Accept-Language' = Name, _, Langs}} -> {http_header, _, 'Accept-Language' = Name, _, Langs}} ->
State#state{request_lang = parse_lang(Langs), State#state{request_lang = parse_lang(Langs),
request_headers = add_header(Name, Langs, State)}; request_headers = add_header(Name, Langs, State)};
{ok, {http_header, _, 'Host' = Name, _, Host}} -> {ok, {http_header, _, 'Host' = Name, _, Value}} ->
{Host, Port, TP} = get_transfer_protocol(State#state.addr_re, SockMod, Value),
State#state{request_host = Host, State#state{request_host = Host,
request_headers = add_header(Name, Host, State)}; request_port = Port,
request_tp = TP,
request_headers = add_header(Name, Value, State)};
{ok, {http_header, _, Name, _, Value}} when is_binary(Name) -> {ok, {http_header, _, Name, _, Value}} when is_binary(Name) ->
State#state{request_headers = State#state{request_headers =
add_header(normalize_header_name(Name), Value, State)}; add_header(normalize_header_name(Name), Value, State)};
{ok, {http_header, _, Name, _, Value}} -> {ok, {http_header, _, Name, _, Value}} ->
State#state{request_headers = State#state{request_headers =
add_header(Name, Value, State)}; add_header(Name, Value, State)};
{ok, http_eoh} {ok, http_eoh} when State#state.request_host == undefined;
when State#state.request_host == undefined -> State#state.request_host == error ->
?DEBUG("An HTTP request without 'Host' HTTP "
"header was received.", []),
{State1, Out} = process_request(State), {State1, Out} = process_request(State),
send_text(State1, Out), send_text(State1, Out),
process_header(State, {ok, {http_error, <<>>}}); process_header(State, {ok, {http_error, <<>>}});
@ -284,32 +283,33 @@ process_header(State, Data) ->
?DEBUG("(~w) http query: ~w ~p~n", ?DEBUG("(~w) http query: ~w ~p~n",
[State#state.socket, State#state.request_method, [State#state.socket, State#state.request_method,
element(2, State#state.request_path)]), element(2, State#state.request_path)]),
{HostProvided, Port, TP} = case ejabberd_router:is_my_route(State#state.request_host) of
get_transfer_protocol(State#state.addr_re, SockMod, true ->
State#state.request_host), {State3, Out} = process_request(State),
Host = get_host_really_served(State#state.default_host, send_text(State3, Out),
HostProvided), case State3#state.request_keepalive of
State2 = State#state{request_host = Host, true ->
request_port = Port, request_tp = TP}, #state{sockmod = SockMod, socket = Socket,
{State3, Out} = process_request(State2), trail = State3#state.trail,
send_text(State3, Out), options = State#state.options,
case State3#state.request_keepalive of default_host = State#state.default_host,
true -> custom_headers = State#state.custom_headers,
#state{sockmod = SockMod, socket = Socket, request_handlers = State#state.request_handlers,
trail = State3#state.trail, addr_re = State#state.addr_re};
options = State#state.options, _ ->
default_host = State#state.default_host, #state{end_of_request = true,
custom_headers = State#state.custom_headers, trail = State3#state.trail,
request_handlers = State#state.request_handlers, options = State#state.options,
addr_re = State#state.addr_re}; default_host = State#state.default_host,
_ -> custom_headers = State#state.custom_headers,
#state{end_of_request = true, request_handlers = State#state.request_handlers,
trail = State3#state.trail, addr_re = State#state.addr_re}
options = State#state.options, end;
default_host = State#state.default_host, false ->
custom_headers = State#state.custom_headers, Out = make_text_output(State, 400, State#state.custom_headers,
request_handlers = State#state.request_handlers, <<"Host not served">>),
addr_re = State#state.addr_re} send_text(State, Out),
process_header(State, {ok, {http_error, <<>>}})
end; end;
_ -> _ ->
#state{end_of_request = true, #state{end_of_request = true,
@ -323,14 +323,6 @@ process_header(State, Data) ->
add_header(Name, Value, State)-> add_header(Name, Value, State)->
[{Name, Value} | State#state.request_headers]. [{Name, Value} | State#state.request_headers].
get_host_really_served(undefined, Provided) ->
Provided;
get_host_really_served(Default, Provided) ->
case ejabberd_router:is_my_host(Provided) of
true -> Provided;
false -> Default
end.
get_transfer_protocol(RE, SockMod, HostPort) -> get_transfer_protocol(RE, SockMod, HostPort) ->
{Proto, DefPort} = case SockMod of {Proto, DefPort} = case SockMod of
gen_tcp -> {http, 80}; gen_tcp -> {http, 80};
@ -338,15 +330,15 @@ get_transfer_protocol(RE, SockMod, HostPort) ->
end, end,
{Host, Port} = case re:run(HostPort, RE, [{capture,[1,2,3],binary}]) of {Host, Port} = case re:run(HostPort, RE, [{capture,[1,2,3],binary}]) of
nomatch -> nomatch ->
{<<"0.0.0.0">>, DefPort}; {error, DefPort};
{match, [<<>>, H, <<>>]} -> {match, [<<>>, H, <<>>]} ->
{H, DefPort}; {jid:nameprep(H), DefPort};
{match, [H, <<>>, <<>>]} -> {match, [H, <<>>, <<>>]} ->
{H, DefPort}; {jid:nameprep(H), DefPort};
{match, [<<>>, H, PortStr]} -> {match, [<<>>, H, PortStr]} ->
{H, binary_to_integer(PortStr)}; {jid:nameprep(H), binary_to_integer(PortStr)};
{match, [H, <<>>, PortStr]} -> {match, [H, <<>>, PortStr]} ->
{H, binary_to_integer(PortStr)} {jid:nameprep(H), binary_to_integer(PortStr)}
end, end,
{Host, Port, Proto}. {Host, Port, Proto}.
@ -438,6 +430,10 @@ process_request(#state{request_host = undefined,
custom_headers = CustomHeaders} = State) -> custom_headers = CustomHeaders} = State) ->
{State, make_text_output(State, 400, CustomHeaders, {State, make_text_output(State, 400, CustomHeaders,
<<"Missing Host header">>)}; <<"Missing Host header">>)};
process_request(#state{request_host = error,
custom_headers = CustomHeaders} = State) ->
{State, make_text_output(State, 400, CustomHeaders,
<<"Malformed Host header">>)};
process_request(#state{request_method = Method, process_request(#state{request_method = Method,
request_auth = Auth, request_auth = Auth,
request_lang = Lang, request_lang = Lang,