From c7a7cfb9b93f0db9c99ed21c34f67e0b6adfcb62 Mon Sep 17 00:00:00 2001 From: Maria Scott Date: Wed, 1 Sep 2021 10:31:32 +0200 Subject: 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 --- src/ranch_ssl.erl | 43 +++++++++++++++++++++++++++++++- test/acceptor_SUITE.erl | 66 +++++++++++++++++++++++++++++++++++++++++++------ 2 files changed, 100 insertions(+), 9 deletions(-) diff --git a/src/ranch_ssl.erl b/src/ranch_ssl.erl index bdfd2e4..dfaa75c 100644 --- a/src/ranch_ssl.erl +++ b/src/ranch_ssl.erl @@ -130,10 +130,12 @@ do_listen(SocketOpts0, Logger) -> SocketOpts2 = ranch:set_option_default(SocketOpts1, nodelay, true), SocketOpts3 = ranch:set_option_default(SocketOpts2, send_timeout, 30000), 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. %% The port in the options takes precedence over the one in the %% 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' and 'list' are disallowed but they are handled @@ -144,6 +146,22 @@ disallowed_listen_options() -> fallback, server_name_indication, srp_identity |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()) -> {ok, ssl:sslsocket()} | {error, closed | timeout | atom()}. accept(LSocket, Timeout) -> @@ -296,3 +314,26 @@ cleanup(#{socket_opts:=SocketOpts}) -> end; cleanup(_) -> 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. diff --git a/test/acceptor_SUITE.erl b/test/acceptor_SUITE.erl index 1028614..335a155 100644 --- a/test/acceptor_SUITE.erl +++ b/test/acceptor_SUITE.erl @@ -68,7 +68,8 @@ groups() -> ssl_many_listen_sockets_no_reuseport, ssl_error_eaddrinuse, ssl_error_no_cert, - ssl_error_eacces + ssl_error_eacces, + ssl_unsupported_tlsv13_options ]}, {misc, [ misc_bad_transport, misc_bad_transport_options, @@ -599,13 +600,11 @@ ssl_active_echo(_) -> ok. 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. do_ssl_active_n_echo() -> @@ -875,6 +874,46 @@ ssl_error_eacces(_) -> ok 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_10_acceptors_10_listen_sockets(_) -> @@ -1674,3 +1713,14 @@ do_os_supports_local_sockets() -> do_tempname() -> 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. -- cgit v1.2.3