diff options
| -rw-r--r-- | lib/ssl/doc/src/ssl.xml | 21 | ||||
| -rw-r--r-- | lib/ssl/src/dtls_connection.erl | 1 | ||||
| -rw-r--r-- | lib/ssl/src/ssl.erl | 7 | ||||
| -rw-r--r-- | lib/ssl/src/ssl_internal.hrl | 1 | ||||
| -rw-r--r-- | lib/ssl/src/tls_connection.erl | 1 | ||||
| -rw-r--r-- | lib/ssl/test/ssl_basic_SUITE.erl | 57 | 
6 files changed, 83 insertions, 5 deletions
| diff --git a/lib/ssl/doc/src/ssl.xml b/lib/ssl/doc/src/ssl.xml index 18d98e5efb..9122066787 100644 --- a/lib/ssl/doc/src/ssl.xml +++ b/lib/ssl/doc/src/ssl.xml @@ -650,6 +650,27 @@ fun(srp, Username :: string(), UserState :: term()) ->        The option <c>sni_fun</c>, and <c>sni_hosts</c> are mutually exclusive.</p></item> +      <tag><c>{client_renegotiation, boolean()}</c></tag> +      <item>In protocols that support client-initiated renegotiation, the cost +      of resources of such an operation is higher for the server than the +      client. This can act as a vector for denial of service attacks. The SSL +      application already takes measures to counter-act such attempts, +      but client-initiated renegotiation can be stricly disabled by setting +      this option to <c>false</c>. The default value is <c>true</c>. +      Note that disabling renegotiation can result in long-lived connections +      becoming unusable due to limits on the number of messages the underlying +      cipher suite can encipher. +      </item> + +      <tag><c>{psk_identity, string()}</c></tag> +      <item>Specifies the server identity hint the server presents to the client. +      </item> +      <tag><c>{log_alert, boolean()}</c></tag> +      <item>If false, error reports will not be displayed.</item> +      <tag><c>{honor_cipher_order, boolean()}</c></tag> +      <item>If true, use the server's preference for cipher selection. If false +      (the default), use the client's preference. +      </item>      </taglist>    </section> diff --git a/lib/ssl/src/dtls_connection.erl b/lib/ssl/src/dtls_connection.erl index 610e2c4e41..0c73a49a04 100644 --- a/lib/ssl/src/dtls_connection.erl +++ b/lib/ssl/src/dtls_connection.erl @@ -514,6 +514,7 @@ initial_state(Role, Host, Port, Socket, {SSLOptions, SocketOptions}, User,  	   user_data_buffer = <<>>,  	   session_cache_cb = SessionCacheCb,  	   renegotiation = {false, first}, +	   allow_renegotiate = SSLOptions#ssl_options.client_renegotiation,  	   start_or_recv_from = undefined,  	   send_queue = queue:new(),  	   protocol_cb = ?MODULE diff --git a/lib/ssl/src/ssl.erl b/lib/ssl/src/ssl.erl index 225a9be66f..f8ddfba7e3 100644 --- a/lib/ssl/src/ssl.erl +++ b/lib/ssl/src/ssl.erl @@ -685,6 +685,7 @@ handle_options(Opts0) ->  		    reuse_session = handle_option(reuse_session, Opts, ReuseSessionFun),  		    reuse_sessions = handle_option(reuse_sessions, Opts, true),  		    secure_renegotiate = handle_option(secure_renegotiate, Opts, false), +		    client_renegotiation = handle_option(client_renegotiation, Opts, true),  		    renegotiate_at = handle_option(renegotiate_at, Opts, ?DEFAULT_RENEGOTIATE_AT),  		    hibernate_after = handle_option(hibernate_after, Opts, undefined),  		    erl_dist = handle_option(erl_dist, Opts, false), @@ -715,7 +716,7 @@ handle_options(Opts0) ->  		  depth, cert, certfile, key, keyfile,  		  password, cacerts, cacertfile, dh, dhfile,  		  user_lookup_fun, psk_identity, srp_identity, ciphers, -		  reuse_session, reuse_sessions, ssl_imp, +		  reuse_session, reuse_sessions, ssl_imp, client_renegotiation,  		  cb_info, renegotiate_at, secure_renegotiate, hibernate_after,  		  erl_dist, alpn_advertised_protocols, sni_hosts, sni_fun,  		  alpn_preferred_protocols, next_protocols_advertised, @@ -857,6 +858,8 @@ validate_option(reuse_sessions, Value) when is_boolean(Value) ->  validate_option(secure_renegotiate, Value) when is_boolean(Value) ->      Value; +validate_option(client_renegotiation, Value) when is_boolean(Value) -> +    Value;  validate_option(renegotiate_at, Value) when is_integer(Value) ->      erlang:min(Value, ?DEFAULT_RENEGOTIATE_AT); @@ -1226,6 +1229,8 @@ new_ssl_options([{renegotiate_at, Value} | Rest], #ssl_options{} = Opts, RecordC      new_ssl_options(Rest, Opts#ssl_options{ renegotiate_at = validate_option(renegotiate_at, Value)}, RecordCB);  new_ssl_options([{secure_renegotiate, Value} | Rest], #ssl_options{} = Opts, RecordCB) ->       new_ssl_options(Rest, Opts#ssl_options{secure_renegotiate = validate_option(secure_renegotiate, Value)}, RecordCB);  +new_ssl_options([{client_renegotiation, Value} | Rest], #ssl_options{} = Opts, RecordCB) ->  +    new_ssl_options(Rest, Opts#ssl_options{client_renegotiation = validate_option(client_renegotiation, Value)}, RecordCB);   new_ssl_options([{hibernate_after, Value} | Rest], #ssl_options{} = Opts, RecordCB) ->       new_ssl_options(Rest, Opts#ssl_options{hibernate_after = validate_option(hibernate_after, Value)}, RecordCB);  new_ssl_options([{alpn_advertised_protocols, Value} | Rest], #ssl_options{} = Opts, RecordCB) -> diff --git a/lib/ssl/src/ssl_internal.hrl b/lib/ssl/src/ssl_internal.hrl index baeae68bc4..40eb3d0284 100644 --- a/lib/ssl/src/ssl_internal.hrl +++ b/lib/ssl/src/ssl_internal.hrl @@ -110,6 +110,7 @@  	  reuse_sessions       :: boolean(),  	  renegotiate_at,  	  secure_renegotiate, +	  client_renegotiation,  	  %% undefined if not hibernating, or number of ms of  	  %% inactivity after which ssl_connection will go into  	  %% hibernation diff --git a/lib/ssl/src/tls_connection.erl b/lib/ssl/src/tls_connection.erl index 3304ffcddb..ed7ccb3d70 100644 --- a/lib/ssl/src/tls_connection.erl +++ b/lib/ssl/src/tls_connection.erl @@ -392,6 +392,7 @@ initial_state(Role, Host, Port, Socket, {SSLOptions, SocketOptions, Tracker}, Us  	   user_data_buffer = <<>>,  	   session_cache_cb = SessionCacheCb,  	   renegotiation = {false, first}, +	   allow_renegotiate = SSLOptions#ssl_options.client_renegotiation,  	   start_or_recv_from = undefined,  	   send_queue = queue:new(),  	   protocol_cb = ?MODULE, diff --git a/lib/ssl/test/ssl_basic_SUITE.erl b/lib/ssl/test/ssl_basic_SUITE.erl index e1a36dbbd4..e131c363d1 100644 --- a/lib/ssl/test/ssl_basic_SUITE.erl +++ b/lib/ssl/test/ssl_basic_SUITE.erl @@ -162,7 +162,8 @@ renegotiate_tests() ->       client_no_wrap_sequence_number,       server_no_wrap_sequence_number,       renegotiate_dos_mitigate_active, -     renegotiate_dos_mitigate_passive]. +     renegotiate_dos_mitigate_passive, +     renegotiate_dos_mitigate_absolute].  cipher_tests() ->      [cipher_suites, @@ -2998,8 +2999,36 @@ renegotiate_dos_mitigate_passive(Config) when is_list(Config) ->      ssl_test_lib:close(Client).  %%-------------------------------------------------------------------- +renegotiate_dos_mitigate_absolute() -> +    [{doc, "Mitigate DOS computational attack by not allowing client to initiate renegotiation"}]. +renegotiate_dos_mitigate_absolute(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, {ssl_test_lib, send_recv_result_active, []}}, +				   {options, [{client_renegotiation, false} | 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_rejected, +					       []}}, +					{options, ClientOpts}]), + +    ssl_test_lib:check_result(Client, ok, Server, ok), +    ssl_test_lib:close(Server), +    ssl_test_lib:close(Client). + +%%--------------------------------------------------------------------  tcp_error_propagation_in_active_mode() -> -    [{doc,"Test that process recives {ssl_error, Socket, closed} when tcp error ocurres"}]. +    [{doc,"Test that process recives {ssl_error, Socket, closed} when tcp error occurs"}].  tcp_error_propagation_in_active_mode(Config) when is_list(Config) ->      ClientOpts = ?config(client_opts, Config),      ServerOpts = ?config(server_opts, Config), @@ -3433,12 +3462,12 @@ renegotiate_reuse_session(Socket, Data) ->      renegotiate(Socket, Data).  renegotiate_immediately(Socket) -> -    receive  +    receive  	{ssl, Socket, "Hello world"} ->  	    ok;  	%% Handle 1/n-1 splitting countermeasure Rizzo/Duong-Beast  	{ssl, Socket, "H"} -> -	    receive  +	    receive  		{ssl, Socket, "ello world"} ->  		    ok  	    end @@ -3450,6 +3479,26 @@ renegotiate_immediately(Socket) ->      ct:log("Renegotiated again"),      ssl:send(Socket, "Hello world"),      ok. + +renegotiate_rejected(Socket) -> +    receive +	{ssl, Socket, "Hello world"} -> +	    ok; +	%% Handle 1/n-1 splitting countermeasure Rizzo/Duong-Beast +	{ssl, Socket, "H"} -> +	    receive +		{ssl, Socket, "ello world"} -> +		    ok +	    end +    end, +    {error, renegotiation_rejected} = ssl:renegotiate(Socket), +    {error, renegotiation_rejected} = ssl:renegotiate(Socket), +    ct:sleep(?RENEGOTIATION_DISABLE_TIME +1), +    {error, renegotiation_rejected} = ssl:renegotiate(Socket), +    ct:log("Failed to renegotiate again"), +    ssl:send(Socket, "Hello world"), +    ok. +  new_config(PrivDir, ServerOpts0) ->      CaCertFile = proplists:get_value(cacertfile, ServerOpts0), | 
