Skip to content

Commit 13b092a

Browse files
author
Erlang/OTP
committed
Merge branch 'whaileee/inets/httpd/httpoxy/OTP-19729' into maint-28
* whaileee/inets/httpd/httpoxy/OTP-19729: Add tests for CVE-2016-1000107 fix [inets/3392] via code-review; canonicalize the HTTP variable name as uppercase and skip any occurrence of PROXY. [inets/3392] Fix for CVE-2016-1000107.
2 parents 756621c + 1bfd566 commit 13b092a

File tree

4 files changed

+121
-8
lines changed

4 files changed

+121
-8
lines changed

lib/inets/examples/server_root/cgi-bin/printenv.bat

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,33 @@
1+
::
2+
:: %CopyrightBegin%
3+
::
4+
:: SPDX-License-Identifier: Apache-2.0
5+
::
6+
:: Copyright Ericsson AB 1997-2025. All Rights Reserved.
7+
::
8+
:: Licensed under the Apache License, Version 2.0 (the "License");
9+
:: you may not use this file except in compliance with the License.
10+
:: You may obtain a copy of the License at
11+
::
12+
:: http://www.apache.org/licenses/LICENSE-2.0
13+
::
14+
:: Unless required by applicable law or agreed to in writing, software
15+
:: distributed under the License is distributed on an "AS IS" BASIS,
16+
:: WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
17+
:: See the License for the specific language governing permissions and
18+
:: limitations under the License.
19+
::
20+
:: %CopyrightEnd%
21+
::
22+
::
23+
24+
125
@echo off
226
echo tomrad > c:\cygwin\tmp\hej
327
echo Content-type: text/html
428
echo.
529
echo ^<HTML^> ^<HEAD^> ^<TITLE^>OS Environment^</TITLE^> ^</HEAD^> ^<BODY^>^<PRE^>
30+
set http_proxy=%HTTP_PROXY%
631
set
732
echo ^</PRE^>^</BODY^>^</HTML^>
833

Lines changed: 26 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,31 @@
11
#!/bin/sh
2+
#
3+
# %CopyrightBegin%
4+
#
5+
# SPDX-License-Identifier: Apache-2.0
6+
#
7+
# Copyright Ericsson AB 1997-2025. All Rights Reserved.
8+
#
9+
# Licensed under the Apache License, Version 2.0 (the "License");
10+
# you may not use this file except in compliance with the License.
11+
# You may obtain a copy of the License at
12+
#
13+
# http://www.apache.org/licenses/LICENSE-2.0
14+
#
15+
# Unless required by applicable law or agreed to in writing, software
16+
# distributed under the License is distributed on an "AS IS" BASIS,
17+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
18+
# See the License for the specific language governing permissions and
19+
# limitations under the License.
20+
#
21+
# %CopyrightEnd%
22+
#
23+
#
24+
25+
226
echo "Content-type: text/html"
327
echo ""
428
echo "<HTML> <HEAD> <TITLE>OS Environment</TITLE> </HEAD> <BODY><PRE>"
29+
export http_proxy=$HTTP_PROXY
530
env
6-
echo "</PRE></BODY></HTML>"
31+
echo "</PRE></BODY></HTML>"

lib/inets/src/http_server/httpd_script_env.erl

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,8 @@
4444
%%
4545
%% Description: Creates a list of cgi/esi environment variables and
4646
%% there values.
47+
%%
48+
%% Note: "PROXY" header/variable is skipped because of CVE-2016-1000107
4749
%%--------------------------------------------------------------------------
4850
create_env(ScriptType, ModData, ScriptElements) ->
4951
create_basic_elements(ScriptType, ModData)
@@ -151,6 +153,8 @@ create_http_header_elements(ScriptType, [{Name, [Value | _] = Values } |
151153
create_http_header_elements(ScriptType, [{Name, Value} | Headers], Acc, OtherAcc)
152154
when is_list(Value) ->
153155
try http_env_element(ScriptType, Name, Value) of
156+
skipped ->
157+
create_http_header_elements(ScriptType, Headers, Acc, OtherAcc);
154158
Element ->
155159
create_http_header_elements(ScriptType, Headers, [Element | Acc],
156160
OtherAcc)
@@ -160,9 +164,16 @@ create_http_header_elements(ScriptType, [{Name, Value} | Headers], Acc, OtherAcc
160164
[{Name, Value} | OtherAcc])
161165
end.
162166

163-
http_env_element(cgi, VarName0, Value) ->
164-
VarName = re:replace(VarName0,"-","_", [{return,list}, global]),
165-
{"HTTP_"++ http_util:to_upper(VarName), Value};
167+
http_env_element(cgi, VarName0, Value) ->
168+
case http_util:to_upper(VarName0) of
169+
"PROXY" ->
170+
%% CVE-2016-1000107 – https://github.com/erlang/otp/issues/3392
171+
skipped;
172+
VarName1 ->
173+
VarNameUpper = re:replace(VarName1, "-", "_", [{return, list}, global]),
174+
{"HTTP_" ++ VarNameUpper, Value}
175+
end;
176+
166177
http_env_element(esi, VarName0, Value) ->
167178
list_to_existing_atom(VarName0),
168179
VarName = re:replace(VarName0,"-","_", [{return,list}, global]),

lib/inets/test/httpd_SUITE.erl

Lines changed: 56 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,8 @@
4444
%% Seconds before successful auths timeout.
4545
-define(AUTH_TIMEOUT,5).
4646
-define(URL_START, "http://").
47-
47+
-define(URL_START_HTTPS, "https://").
48+
-define(SSL_NO_VERIFY, {ssl, [{verify, verify_none}]}).
4849
%%--------------------------------------------------------------------
4950
%% Common Test interface functions -----------------------------------
5051
%%--------------------------------------------------------------------
@@ -135,13 +136,14 @@ groups() ->
135136
{security, [], [security_1_1, security_1_0]},
136137
{logging, [], [disk_log_internal, disk_log_exists,
137138
disk_log_bad_size, disk_log_bad_file]},
138-
{http_1_1, [], [esi_propagate, esi_atom_leak, {group, http_1_1_parallel}] ++ load()},
139+
{http_1_1, [], [esi_propagate, esi_atom_leak, {group, http_1_1_parallel},
140+
cgi_bin_env] ++ load()},
139141
{http_1_1_parallel, [parallel],
140142
[host, chunked, expect, cgi, cgi_chunked_encoding_test,
141143
trace, range, if_modified_since, mod_esi_chunk_timeout,
142144
esi_put, esi_patch, esi_post, esi_headers]
143145
++ http_head() ++ http_get()},
144-
{http_1_0, [], [{group, http_1_0_parallel} | load()]},
146+
{http_1_0, [], [cgi_bin_env, {group, http_1_0_parallel} | load()]},
145147
{http_1_0_parallel, [parallel], [host, cgi, trace] ++ http_head() ++ http_get()},
146148
{http_rel_path_script_alias, [], [cgi]},
147149
{esi, [], [erl_script_timeout_default,
@@ -1293,6 +1295,51 @@ alias(Config) when is_list(Config) ->
12931295
[Test301(T) || T <- TestURIs301],
12941296
ok.
12951297

1298+
cgi_bin_env() ->
1299+
[{doc, "Test whether HTTP_PROXY header is not applied to an environment
1300+
that runs the cgi script"}].
1301+
cgi_bin_env(Config) ->
1302+
Proto = case proplists:get_value(type, Config, undefined) =:= ssl of
1303+
true -> https;
1304+
_ -> http
1305+
end,
1306+
Cgi = case os:type() of
1307+
{win32, _} ->
1308+
"printenv.bat";
1309+
_ ->
1310+
"printenv.sh"
1311+
end,
1312+
HttpOpts = case Proto of
1313+
https -> [?SSL_NO_VERIFY];
1314+
_ -> []
1315+
end,
1316+
RandomString = base64:encode(crypto:strong_rand_bytes(9)),
1317+
Endpoint = "/cgi-bin/" ++ Cgi,
1318+
Env = os:env(),
1319+
%% Grab the value of HTTP_PROXY from the environment before the request
1320+
HttpProxyEnv = proplists:get_value("HTTP_PROXY", Env, undefined),
1321+
Url = url(Proto, Endpoint, Config),
1322+
{ok, {_Status, _Headers, Body}} = httpc:request(get, {Url, [{"PROXY", RandomString},
1323+
{"proxy", RandomString}]},
1324+
HttpOpts, []),
1325+
%% The script prints the system's environment to the body so we need to
1326+
%% grab the value of interest
1327+
HttpEnv = re:split(Body, "\n"),
1328+
BinSize = size(<<"HTTP_PROXY">>) * 8,
1329+
%% Filter keys of interest, while converting to proplist
1330+
EnvProp = [{binary_to_list(Key), binary_to_list(Val)} ||
1331+
<<Key:BinSize/bitstring, "=", Val/bitstring>> <- HttpEnv,
1332+
Key =:= <<"HTTP_PROXY">>],
1333+
%% EnvProp should only have HTTP_PROXY or be an empty list
1334+
RespHttpProxyEnv = proplists:get_value("HTTP_PROXY", EnvProp, undefined),
1335+
case HttpProxyEnv of
1336+
undefined ->
1337+
%% HTTP_PROXY was not set before the request
1338+
?assertEqual([], EnvProp);
1339+
_ ->
1340+
%% HTTP_PROXY was set, so ensure it's the same in the body
1341+
?assertEqual(HttpProxyEnv, RespHttpProxyEnv)
1342+
end.
12961343
%%-------------------------------------------------------------------------
12971344
actions() ->
12981345
[{doc, "Test mod_actions"}].
@@ -1985,10 +2032,15 @@ tls_alert(Config) when is_list(Config) ->
19852032
%%--------------------------------------------------------------------
19862033
%% Internal functions -----------------------------------
19872034
%%--------------------------------------------------------------------
2035+
url(https, End, Config) ->
2036+
?URL_START_HTTPS ++ url(End, Config);
19882037
url(http, End, Config) ->
2038+
?URL_START ++ url(End, Config).
2039+
2040+
url(End, Config) ->
19892041
Port = proplists:get_value(port, Config),
19902042
{ok,Host} = inet:gethostname(),
1991-
?URL_START ++ Host ++ ":" ++ integer_to_list(Port) ++ End.
2043+
Host ++ ":" ++ integer_to_list(Port) ++ End.
19922044

19932045
http_get_url(Port0, HeaderDelay, ChunkDelay, BadChunkDelay) ->
19942046
{ok, Host} = inet:gethostname(),

0 commit comments

Comments
 (0)