Browse Source

Disallow unsupported options for TLSv1.3

* beast_mitigation (also disallowed for 1.1 and 1.2)
* client_renegotiation
* next_protocols_advertised
* padding_check (also disallowed for 1.1 and 1.2)
* psk_identity
* reuse_session
* reuse_sessions
* secure_renegotiate
* user_lookup_fun
Maria Scott 3 years ago
parent
commit
c7a7cfb9b9
2 changed files with 100 additions and 9 deletions
  1. 42 1
      src/ranch_ssl.erl
  2. 58 8
      test/acceptor_SUITE.erl

+ 42 - 1
src/ranch_ssl.erl

@@ -130,10 +130,12 @@ do_listen(SocketOpts0, Logger) ->
 	SocketOpts2 = ranch:set_option_default(SocketOpts1, nodelay, true),
 	SocketOpts2 = ranch:set_option_default(SocketOpts1, nodelay, true),
 	SocketOpts3 = ranch:set_option_default(SocketOpts2, send_timeout, 30000),
 	SocketOpts3 = ranch:set_option_default(SocketOpts2, send_timeout, 30000),
 	SocketOpts = ranch:set_option_default(SocketOpts3, send_timeout_close, true),
 	SocketOpts = ranch:set_option_default(SocketOpts3, send_timeout_close, true),
+	DisallowedOpts0 = disallowed_listen_options(),
+	DisallowedOpts = unsupported_tls_options(SocketOpts) ++ DisallowedOpts0,
 	%% We set the port to 0 because it is given in the Opts directly.
 	%% We set the port to 0 because it is given in the Opts directly.
 	%% The port in the options takes precedence over the one in the
 	%% The port in the options takes precedence over the one in the
 	%% first argument.
 	%% first argument.
-	ssl:listen(0, ranch:filter_options(SocketOpts, disallowed_listen_options(),
+	ssl:listen(0, ranch:filter_options(SocketOpts, DisallowedOpts,
 		[binary, {active, false}, {packet, raw}, {reuseaddr, true}], Logger)).
 		[binary, {active, false}, {packet, raw}, {reuseaddr, true}], Logger)).
 
 
 %% 'binary' and 'list' are disallowed but they are handled
 %% 'binary' and 'list' are disallowed but they are handled
@@ -144,6 +146,22 @@ disallowed_listen_options() ->
 		fallback, server_name_indication, srp_identity
 		fallback, server_name_indication, srp_identity
 		|ranch_tcp:disallowed_listen_options()].
 		|ranch_tcp:disallowed_listen_options()].
 
 
+unsupported_tls_options(SocketOpts) ->
+	unsupported_tls_version_options(lists:usort(get_tls_versions(SocketOpts))).
+
+unsupported_tls_version_options([tlsv1|_]) ->
+	[];
+unsupported_tls_version_options(['tlsv1.1'|_]) ->
+	[beast_mitigation, padding_check];
+unsupported_tls_version_options(['tlsv1.2'|_]) ->
+	[beast_mitigation, padding_check];
+unsupported_tls_version_options(['tlsv1.3'|_]) ->
+	[beast_mitigation, client_renegotiation, next_protocols_advertised,
+		padding_check, psk_identity, reuse_session, reuse_sessions,
+		secure_renegotiate, user_lookup_fun];
+unsupported_tls_version_options(_) ->
+	[].
+
 -spec accept(ssl:sslsocket(), timeout())
 -spec accept(ssl:sslsocket(), timeout())
 	-> {ok, ssl:sslsocket()} | {error, closed | timeout | atom()}.
 	-> {ok, ssl:sslsocket()} | {error, closed | timeout | atom()}.
 accept(LSocket, Timeout) ->
 accept(LSocket, Timeout) ->
@@ -296,3 +314,26 @@ cleanup(#{socket_opts:=SocketOpts}) ->
 	end;
 	end;
 cleanup(_) ->
 cleanup(_) ->
 	ok.
 	ok.
+
+get_tls_versions(SocketOpts) ->
+	%% Socket options need to be reversed for keyfind because later options
+	%% take precedence when contained multiple times, but keyfind will return
+	%% the earliest occurence.
+	case lists:keyfind(versions, 1, lists:reverse(SocketOpts)) of
+		{versions, Versions} ->
+			Versions;
+		false ->
+			get_tls_versions_env()
+	end.
+
+get_tls_versions_env() ->
+	case application:get_env(ssl, protocol_version) of
+		{ok, Versions} ->
+			Versions;
+		undefined ->
+			get_tls_versions_app()
+	end.
+
+get_tls_versions_app() ->
+	{supported, Versions} = lists:keyfind(supported, 1, ssl:versions()),
+	Versions.

+ 58 - 8
test/acceptor_SUITE.erl

@@ -68,7 +68,8 @@ groups() ->
 		ssl_many_listen_sockets_no_reuseport,
 		ssl_many_listen_sockets_no_reuseport,
 		ssl_error_eaddrinuse,
 		ssl_error_eaddrinuse,
 		ssl_error_no_cert,
 		ssl_error_no_cert,
-		ssl_error_eacces
+		ssl_error_eacces,
+		ssl_unsupported_tlsv13_options
 	]}, {misc, [
 	]}, {misc, [
 		misc_bad_transport,
 		misc_bad_transport,
 		misc_bad_transport_options,
 		misc_bad_transport_options,
@@ -599,13 +600,11 @@ ssl_active_echo(_) ->
 	ok.
 	ok.
 
 
 ssl_active_n_echo(_) ->
 ssl_active_n_echo(_) ->
-	case application:get_key(ssl, vsn) of
-		{ok, "9.0"++_} ->
-			{skip, "No Active N support."};
-		{ok, "9.1"++_} ->
-			{skip, "No Active N support."};
-		{ok, _} ->
-			do_ssl_active_n_echo()
+	case do_get_ssl_version() >= {9, 2, 0} of
+		true ->
+			do_ssl_active_n_echo();
+		false ->
+			{skip, "No Active N support."}
 	end.
 	end.
 
 
 do_ssl_active_n_echo() ->
 do_ssl_active_n_echo() ->
@@ -875,6 +874,46 @@ ssl_error_eacces(_) ->
 			ok
 			ok
 	end.
 	end.
 
 
+ssl_unsupported_tlsv13_options(_) ->
+	{available, Versions} = lists:keyfind(available, 1, ssl:versions()),
+	case {lists:member('tlsv1.3', Versions), do_get_ssl_version() >= {10, 0, 0}} of
+		{true, true} ->
+			do_ssl_unsupported_tlsv13_options();
+		{false, _} ->
+			{skip, "No TLSv1.3 support."};
+		{_, false} ->
+			{skip, "No TLSv1.3 option dependency checking."}
+	end.
+
+do_ssl_unsupported_tlsv13_options() ->
+	doc("Ensure that a listener can be started when TLSv1.3 is "
+	    "the only protocol and unsupported options are present."),
+	CheckOpts = [
+		{beast_mitigation, one_n_minus_one},
+		{client_renegotiation, true},
+		{next_protocols_advertised, [<<"dummy">>]},
+		{padding_check, true},
+		{psk_identity, "dummy"},
+		{secure_renegotiate, true},
+		{reuse_session, fun (_, _, _, _) -> true end},
+		{reuse_sessions, true},
+		{user_lookup_fun, {fun (_, _, _) -> error end, <<"dummy">>}}
+	],
+	Name = name(),
+	Opts = ct_helper:get_certs_from_ets() ++ [{versions, ['tlsv1.3']}],
+	ok = lists:foreach(
+		fun (CheckOpt) ->
+			Opts1 = Opts ++ [CheckOpt],
+			{error, {options, dependency, _}} = ssl:listen(0, Opts1),
+			{ok, _} = ranch:start_listener(Name,
+				ranch_ssl, #{socket_opts => Opts1},
+				echo_protocol, []),
+			ok = ranch:stop_listener(Name)
+		end,
+		CheckOpts
+	),
+	ok.
+
 %% tcp.
 %% tcp.
 
 
 tcp_10_acceptors_10_listen_sockets(_) ->
 tcp_10_acceptors_10_listen_sockets(_) ->
@@ -1674,3 +1713,14 @@ do_os_supports_local_sockets() ->
 
 
 do_tempname() ->
 do_tempname() ->
 	list_to_binary(lists:droplast(os:cmd("mktemp -u"))).
 	list_to_binary(lists:droplast(os:cmd("mktemp -u"))).
+
+do_get_ssl_version() ->
+	{ok, Vsn} = application:get_key(ssl, vsn),
+	Vsns0 = re:split(Vsn, "\\D+", [{return, list}]),
+	Vsns1 = lists:map(fun list_to_integer/1, Vsns0),
+	case Vsns1 of
+		[] -> {0, 0, 0};
+		[Major] -> {Major, 0, 0};
+		[Major, Minor] -> {Major, Minor, 0};
+		[Major, Minor, Patch|_] -> {Major, Minor, Patch}
+	end.