aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorIngela Anderton Andin <[email protected]>2011-11-17 17:45:06 +0100
committerGustav Simonsson <[email protected]>2012-03-15 12:31:08 +0100
commitc9a25aca65c1e0d7f961868c947b3252c88e6ed4 (patch)
tree0f2e6ca65b9f3ca7c454b9d0f0c7388227ba58dd
parentb6dc1a844eab061d0a7153d46e7e68296f15a504 (diff)
downloadotp-c9a25aca65c1e0d7f961868c947b3252c88e6ed4.tar.gz
otp-c9a25aca65c1e0d7f961868c947b3252c88e6ed4.tar.bz2
otp-c9a25aca65c1e0d7f961868c947b3252c88e6ed4.zip
Mitigate Computational DoS attack
-rw-r--r--lib/ssl/src/ssl_connection.erl33
-rw-r--r--lib/ssl/test/ssl_basic_SUITE.erl49
2 files changed, 75 insertions, 7 deletions
diff --git a/lib/ssl/src/ssl_connection.erl b/lib/ssl/src/ssl_connection.erl
index cec81d551b..c87ea22cf8 100644
--- a/lib/ssl/src/ssl_connection.erl
+++ b/lib/ssl/src/ssl_connection.erl
@@ -91,12 +91,14 @@
renegotiation, % {boolean(), From | internal | peer}
recv_during_renegotiation, %boolean()
send_queue, % queue()
- terminated = false %
+ terminated = false, %
+ allow_renegotiate = true
}).
-define(DEFAULT_DIFFIE_HELLMAN_PARAMS,
- #'DHParameter'{prime = ?DEFAULT_DIFFIE_HELLMAN_PRIME,
+ #'DHParameter'{prime = ?DEFAULT_DIFFIE_HELLMAN_PRIME,
base = ?DEFAULT_DIFFIE_HELLMAN_GENERATOR}).
+-define(WAIT_TO_ALLOW_RENEGOTIATION, 12000).
-type state_name() :: hello | abbreviated | certify | cipher | connection.
-type gen_fsm_state_return() :: {next_state, state_name(), #state{}} |
@@ -707,9 +709,25 @@ connection(#hello_request{}, #state{host = Host, port = Port,
ConnectionStates1,
tls_handshake_hashes = Hashes1}),
next_state(hello, Record, State);
-connection(#client_hello{} = Hello, #state{role = server} = State) ->
- hello(Hello, State);
-
+connection(#client_hello{} = Hello, #state{role = server, allow_renegotiate = true} = State) ->
+ %% Mitigate Computational DoS attack http://www.educatedguesswork.org/2011/10/ssltls_and_computational_dos.html
+ %% http://www.thc.org/thc-ssl-dos/ Rather than disabling client initiated renegotiation
+ %% we will disallow many client initiated renegotiations immediately after each other.
+ erlang:send_after(?WAIT_TO_ALLOW_RENEGOTIATION, self(), allow_renegotiate),
+ hello(Hello, State#state{allow_renegotiate = false});
+
+connection(#client_hello{}, #state{role = server, allow_renegotiate = false,
+ connection_states = ConnectionStates0,
+ socket = Socket, transport_cb = Transport,
+ negotiated_version = Version} = State0) ->
+ Alert = ?ALERT_REC(?WARNING, ?NO_RENEGOTIATION),
+ {BinMsg, ConnectionStates} =
+ encode_alert(Alert, Version, ConnectionStates0),
+ Transport:send(Socket, BinMsg),
+ {Record, State} = next_record(State0#state{connection_states =
+ ConnectionStates}),
+ next_state(connection, Record, State);
+
connection(timeout, State) ->
{next_state, connection, State, hibernate};
@@ -984,6 +1002,9 @@ handle_info({'DOWN', MonitorRef, _, _, _}, _,
State = #state{user_application={MonitorRef,_Pid}}) ->
{stop, normal, State};
+handle_info(allow_renegotiate, StateName, State) ->
+ {next_state, StateName, State#state{allow_renegotiate = true}, get_timeout(State)};
+
handle_info(Msg, StateName, State) ->
Report = io_lib:format("SSL: Got unexpected info: ~p ~n", [Msg]),
error_logger:info_report(Report),
@@ -2257,7 +2278,7 @@ renegotiate(#state{role = server,
{Record, State} = next_record(State0#state{connection_states =
ConnectionStates,
tls_handshake_hashes = Hs0}),
- next_state(hello, Record, State).
+ next_state(hello, Record, State#state{allow_renegotiate = true}).
notify_senders(SendQueue) ->
lists:foreach(fun({From, _}) ->
diff --git a/lib/ssl/test/ssl_basic_SUITE.erl b/lib/ssl/test/ssl_basic_SUITE.erl
index 8da1d947d3..f1ebac8845 100644
--- a/lib/ssl/test/ssl_basic_SUITE.erl
+++ b/lib/ssl/test/ssl_basic_SUITE.erl
@@ -37,6 +37,7 @@
-define(LONG_TIMEOUT, 600000).
-define(EXPIRE, 10).
-define(SLEEP, 500).
+-define(RENEGOTIATION_DISABLE_TIME, 12000).
%% Test server callback functions
%%--------------------------------------------------------------------
@@ -257,7 +258,7 @@ all() ->
%%different_ca_peer_sign,
no_reuses_session_server_restart_new_cert,
no_reuses_session_server_restart_new_cert_file, reuseaddr,
- hibernate, connect_twice
+ hibernate, connect_twice, renegotiate_dos_mitigate
].
groups() ->
@@ -3655,7 +3656,40 @@ connect_twice(Config) when is_list(Config) ->
ssl_test_lib:close(Client),
ssl_test_lib:close(Client1).
+%%--------------------------------------------------------------------
+renegotiate_dos_mitigate(doc) ->
+ ["Mitigate DOS computational attack by not allowing client to renegotiate many times in a row",
+ "immediately after each other"];
+
+renegotiate_dos_mitigate(suite) ->
+ [];
+
+renegotiate_dos_mitigate(Config) when is_list(Config) ->
+ ServerOpts = ?config(server_opts, Config),
+ ClientOpts = ?config(client_opts, Config),
+
+ {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config),
+
+ Server =
+ ssl_test_lib:start_server([{node, ServerNode}, {port, 0},
+ {from, self()},
+ {mfa, {?MODULE, send_recv_result_active, []}},
+ {options, [ServerOpts]}]),
+ Port = ssl_test_lib:inet_port(Server),
+
+ Client = ssl_test_lib:start_client([{node, ClientNode}, {port, Port},
+ {host, Hostname},
+ {from, self()},
+ {mfa, {?MODULE,
+ renegotiate_immediately, []}},
+ {options, ClientOpts}]),
+
+ ssl_test_lib:check_result(Client, ok, Server, ok),
+ ssl_test_lib:close(Server),
+ ssl_test_lib:close(Client).
+
+
%%--------------------------------------------------------------------
%%% Internal functions
%%--------------------------------------------------------------------
@@ -3698,6 +3732,19 @@ renegotiate_reuse_session(Socket, Data) ->
test_server:sleep(?SLEEP),
renegotiate(Socket, Data).
+renegotiate_immediately(Socket) ->
+ receive
+ {ssl, Socket, "Hello world"} ->
+ ok
+ end,
+ ok = ssl:renegotiate(Socket),
+ {error, renegotiation_rejected} = ssl:renegotiate(Socket),
+ test_server:sleep(?RENEGOTIATION_DISABLE_TIME +1),
+ ok = ssl:renegotiate(Socket),
+ test_server:format("Renegotiated again"),
+ ssl:send(Socket, "Hello world"),
+ ok.
+
new_config(PrivDir, ServerOpts0) ->
CaCertFile = proplists:get_value(cacertfile, ServerOpts0),
CertFile = proplists:get_value(certfile, ServerOpts0),