From 31d6c20ce5d6d104e1bf85a8695af07145fc7292 Mon Sep 17 00:00:00 2001 From: Ingela Anderton Andin Date: Mon, 24 Jun 2024 11:08:51 +0200 Subject: [PATCH] ssl: Correct Option handling in TLS-1.3 Options supplied based on SNI did not properly override listen options for TLS-1.3 connections. --- lib/ssl/src/tls_handshake_1_3.erl | 25 ++--- lib/ssl/test/ssl_api_SUITE.erl | 146 +++++++++++++++++++++++++++++- lib/ssl/test/ssl_test_lib.erl | 85 +++++++++++++++++ 3 files changed, 241 insertions(+), 15 deletions(-) diff --git a/lib/ssl/src/tls_handshake_1_3.erl b/lib/ssl/src/tls_handshake_1_3.erl index 34690ef9dd9d..5c22ded50166 100644 --- a/lib/ssl/src/tls_handshake_1_3.erl +++ b/lib/ssl/src/tls_handshake_1_3.erl @@ -623,17 +623,23 @@ build_content(Context, THash) -> do_start(#client_hello{cipher_suites = ClientCiphers, session_id = SessionId, extensions = Extensions} = Hello, - #state{ssl_options = #{ciphers := ServerCiphers, - signature_algs := ServerSignAlgs, - supported_groups := ServerGroups0, - alpn_preferred_protocols := ALPNPreferredProtocols, - keep_secrets := KeepSecrets, - honor_cipher_order := HonorCipherOrder, - early_data := EarlyDataEnabled}} = State0) -> + State0) -> SNI = maps:get(sni, Extensions, undefined), EarlyDataIndication = maps:get(early_data, Extensions, undefined), {Ref,Maybe} = maybe(), try + #state{connection_states = ConnectionStates0, + session = Session0, + ssl_options = #{ciphers := ServerCiphers, + signature_algs := ServerSignAlgs, + supported_groups := ServerGroups0, + alpn_preferred_protocols := ALPNPreferredProtocols, + keep_secrets := KeepSecrets, + honor_cipher_order := HonorCipherOrder, + early_data := EarlyDataEnabled}, + connection_env = #connection_env{cert_key_alts = CertKeyAlts}} = State1 = + Maybe(ssl_gen_statem:handle_sni_extension(SNI, State0)), + ClientGroups0 = Maybe(supported_groups_from_extensions(Extensions)), ClientGroups = Maybe(get_supported_groups(ClientGroups0)), ServerGroups = Maybe(get_supported_groups(ServerGroups0)), @@ -654,11 +660,6 @@ do_start(#client_hello{cipher_suites = ClientCiphers, CookieExt = maps:get(cookie, Extensions, undefined), Cookie = get_cookie(CookieExt), - #state{connection_states = ConnectionStates0, - session = Session0, - connection_env = #connection_env{cert_key_alts = CertKeyAlts}} = State1 = - Maybe(ssl_gen_statem:handle_sni_extension(SNI, State0)), - Maybe(validate_cookie(Cookie, State1)), %% Handle ALPN extension if ALPN is configured diff --git a/lib/ssl/test/ssl_api_SUITE.erl b/lib/ssl/test/ssl_api_SUITE.erl index 5d1d346d55ed..03d09f9b0eec 100644 --- a/lib/ssl/test/ssl_api_SUITE.erl +++ b/lib/ssl/test/ssl_api_SUITE.erl @@ -65,6 +65,8 @@ versions/1, versions_option_based_on_sni/0, versions_option_based_on_sni/1, + ciphers_option_based_on_sni/0, + ciphers_option_based_on_sni/1, active_n/0, active_n/1, dh_params/0, @@ -210,6 +212,7 @@ log/2, get_connection_information/3, protocol_version_check/2, + suite_check/2, check_peercert/2, %%TODO Keep? run_error_server/1, @@ -263,7 +266,8 @@ since_1_2() -> [ conf_signature_algs, no_common_signature_algs, - versions_option_based_on_sni + versions_option_based_on_sni, + ciphers_option_based_on_sni ]. pre_1_3() -> @@ -1105,6 +1109,42 @@ versions_option_based_on_sni(Config) when is_list(Config) -> ssl_test_lib:check_result(Server, ok), ssl_test_lib:close(Server), ssl_test_lib:close(Client). +%%-------------------------------------------------------------------- + +ciphers_option_based_on_sni() -> + [{doc,"Test that SNI versions option is selected over default ciphers option"}]. + +ciphers_option_based_on_sni(Config) when is_list(Config) -> + ClientOpts = ssl_test_lib:ssl_options(client_rsa_verify_opts, Config), + ServerOpts = ssl_test_lib:ssl_options(server_rsa_opts, Config), + TestVersion = ssl_test_lib:protocol_version(Config), + Suites = rsa_cipher_suites_not_default(TestVersion), + {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config), + + SNI = net_adm:localhost(), + Fun = fun(ServerName) -> + case ServerName of + SNI -> + [{ciphers, Suites} | ServerOpts]; + _ -> + ServerOpts + end + end, + + Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0}, + {from, self()}, + {mfa, {?MODULE, suite_check, [TestVersion]}}, + {options, [{sni_fun, Fun} | ServerOpts]}]), + Port = ssl_test_lib:inet_port(Server), + Client = ssl_test_lib:start_client([{node, ClientNode}, {port, Port}, + {host, Hostname}, + {from, self()}, + {mfa, {ssl_test_lib, no_result, []}}, + {options, [{server_name_indication, SNI} | ClientOpts]}]), + + ssl_test_lib:check_result(Server, ok), + ssl_test_lib:close(Server), + ssl_test_lib:close(Client). %%-------------------------------------------------------------------- %% Test case adapted from gen_tcp_misc_SUITE. @@ -3507,5 +3547,105 @@ run_sha1_cert_conf('tlsv1.2', #{client_config := ClientOpts, server_config := Se IncludeLegacyAlg = SigAlgs ++ [LegacyAlg], ssl_test_lib:basic_test( [{verify, verify_peer}, {signature_algs, IncludeLegacyAlg} | ClientOpts], [{signature_algs, IncludeLegacyAlg} | ServerOpts], Config); -run_sha1_cert_conf(_, #{client_config := ClientOpts, server_config := ServerOpts}, Config, _) -> - ssl_test_lib:basic_test([{verify, verify_peer} | ClientOpts], ServerOpts, Config). + +run_sha1_cert_conf(_, #{client_config := ClientOpts, server_config := ServerOpts}, Config, LegacyAlg) -> + NVersion = ssl_test_lib:n_version(proplists:get_value(version, Config)), + SigOpts = ssl_test_lib:sig_algs(LegacyAlg, NVersion), + ssl_test_lib:basic_test([{verify, verify_peer} | ClientOpts] ++ SigOpts, ServerOpts, Config). + + +rsa_cipher_suites_not_default('tlsv1.3'= Version) -> + [_ | Suites] = ssl:cipher_suites(default, Version), + Suites; +rsa_cipher_suites_not_default(Version) -> + ssl_test_lib:test_ciphers(ecdhe_rsa, aes_128_cbc, Version). + +suite_check(Socket, 'tlsv1.3'= Version) -> + [_, Suite| _] = ssl:cipher_suites(default, Version), + case ssl:connection_information(Socket, [selected_cipher_suite]) of + {ok, [{selected_cipher_suite, Suite}]} -> + ok; + Other -> + ct:fail({expected, Suite, got, Other}) + end; +suite_check(Socket, Version) -> + [Suite |_] = rsa_cipher_suites_not_default(Version), + case ssl:connection_information(Socket, [selected_cipher_suite]) of + {ok, [{selected_cipher_suite, Suite}]} -> + ok; + Other -> + ct:fail({expected, Suite, got, Other}) + end. + +sig_algs(rsa_pss_pss, _) -> + [{signature_algs, [rsa_pss_pss_sha512, + rsa_pss_pss_sha384, + rsa_pss_pss_sha256]}]; +sig_algs(rsa_pss_rsae, _) -> + [{signature_algs, [rsa_pss_rsae_sha512, + rsa_pss_rsae_sha384, + rsa_pss_rsae_sha256]}]; +sig_algs(rsa, {3,N}) when N >=3 -> + [{signature_algs, [rsa_pss_rsae_sha512, + rsa_pss_rsae_sha384, + rsa_pss_rsae_sha256, + {sha512, rsa}, + {sha384, rsa}, + {sha256, rsa}, + {sha, rsa} + ]}]; +sig_algs(ecdsa, {3,N}) when N >=3 -> + [{signature_algs, [ + {sha512, ecdsa}, + {sha384, ecdsa}, + {sha256, ecdsa}, + {sha, ecdsa}]}]; +sig_algs(dsa, {3, N}) when N >=3 -> + [{signature_algs, [{sha,dsa}]}]; +sig_algs(_,_) -> + []. + +all_sig_algs() -> + {signature_algs, list_1_3_sig_algs() ++ list_common_sig_algs() ++ list_1_2_sig_algs()}. + +all_1_3_sig_algs() -> + {signature_algs, list_1_3_sig_algs() ++ list_common_sig_algs()}. + +all_1_2_sig_algs() -> + {signature_algs, list_common_sig_algs() ++ list_1_2_sig_algs()}. +list_1_3_sig_algs() -> + [ + eddsa_ed25519, + eddsa_ed448, + ecdsa_secp521r1_sha512, + ecdsa_secp384r1_sha384, + ecdsa_secp256r1_sha256, + ecdsa_brainpoolP512r1tls13_sha512, + ecdsa_brainpoolP384r1tls13_sha384, + ecdsa_brainpoolP256r1tls13_sha256 + ]. + +list_common_sig_algs() -> + [ + rsa_pss_pss_sha512, + rsa_pss_pss_sha384, + rsa_pss_pss_sha256, + rsa_pss_rsae_sha512, + rsa_pss_rsae_sha384, + rsa_pss_rsae_sha256 + ]. + +list_1_2_sig_algs() -> + [ + {sha512, ecdsa}, + {sha512, rsa}, + {sha384, ecdsa}, + {sha384, rsa}, + {sha256, ecdsa}, + {sha256, rsa}, + {sha224, ecdsa}, + {sha224, rsa}, + {sha, ecdsa}, + {sha, rsa}, + {sha, dsa} + ]. diff --git a/lib/ssl/test/ssl_test_lib.erl b/lib/ssl/test/ssl_test_lib.erl index 56febfd8b8f4..574c63c9abad 100644 --- a/lib/ssl/test/ssl_test_lib.erl +++ b/lib/ssl/test/ssl_test_lib.erl @@ -149,6 +149,8 @@ ]). -export([tls_version/1, + n_version/1, + sig_algs/2, is_protocol_version/1, is_tls_version/1, is_dtls_version/1, @@ -3661,6 +3663,17 @@ tls_version('dtlsv1.2' = Atom) -> tls_version(Atom) -> tls_record:protocol_version(Atom). +n_version(Version) when + Version == 'tlsv1.3'; + Version == 'tlsv1.2'; + Version == 'tlsv1.1'; + Version == 'tlsv1'; + Version == 'sslv3' -> + tls_record:protocol_version(Version); +n_version(Version) when Version == 'dtlsv1.2'; + Version == 'dtlsv1' -> + dtls_record:protocol_version(Version). + consume_port_exit(OpenSSLPort) -> receive {'EXIT', OpenSSLPort, _} -> @@ -4148,3 +4161,75 @@ curve_default(eddsa) -> ed25519; curve_default(_) -> ?DEFAULT_CURVE. +sig_algs(rsa_pss_pss, _) -> + [{signature_algs, [rsa_pss_pss_sha512, + rsa_pss_pss_sha384, + rsa_pss_pss_sha256]}]; +sig_algs(rsa_pss_rsae, _) -> + [{signature_algs, [rsa_pss_rsae_sha512, + rsa_pss_rsae_sha384, + rsa_pss_rsae_sha256]}]; +sig_algs(rsa, {3,N}) when N >=3 -> + [{signature_algs, [rsa_pss_rsae_sha512, + rsa_pss_rsae_sha384, + rsa_pss_rsae_sha256, + {sha512, rsa}, + {sha384, rsa}, + {sha256, rsa}, + {sha, rsa} + ]}]; +sig_algs(ecdsa, {3,N}) when N >=3 -> + [{signature_algs, [ + {sha512, ecdsa}, + {sha384, ecdsa}, + {sha256, ecdsa}, + {sha, ecdsa}]}]; +sig_algs(dsa, {3, N}) when N >=3 -> + [{signature_algs, [{sha,dsa}]}]; +sig_algs(_,_) -> + []. + +all_sig_algs() -> + {signature_algs, list_1_3_sig_algs() ++ list_common_sig_algs() ++ list_1_2_sig_algs()}. + +all_1_3_sig_algs() -> + {signature_algs, list_1_3_sig_algs() ++ list_common_sig_algs()}. + +all_1_2_sig_algs() -> + {signature_algs, list_common_sig_algs() ++ list_1_2_sig_algs()}. +list_1_3_sig_algs() -> + [ + eddsa_ed25519, + eddsa_ed448, + ecdsa_secp521r1_sha512, + ecdsa_secp384r1_sha384, + ecdsa_secp256r1_sha256, + ecdsa_brainpoolP512r1tls13_sha512, + ecdsa_brainpoolP384r1tls13_sha384, + ecdsa_brainpoolP256r1tls13_sha256 + ]. + +list_common_sig_algs() -> + [ + rsa_pss_pss_sha512, + rsa_pss_pss_sha384, + rsa_pss_pss_sha256, + rsa_pss_rsae_sha512, + rsa_pss_rsae_sha384, + rsa_pss_rsae_sha256 + ]. + +list_1_2_sig_algs() -> + [ + {sha512, ecdsa}, + {sha512, rsa}, + {sha384, ecdsa}, + {sha384, rsa}, + {sha256, ecdsa}, + {sha256, rsa}, + {sha224, ecdsa}, + {sha224, rsa}, + {sha, ecdsa}, + {sha, rsa}, + {sha, dsa} + ].