File 4621-ssl-Add-missing-state-update.patch of Package erlang

From 530c88c233d5e59438b174fcf3d578d2eae1185f Mon Sep 17 00:00:00 2001
From: Ingela Anderton Andin <ingela@erlang.org>
Date: Wed, 8 Dec 2021 11:58:05 +0100
Subject: [PATCH 1/2] ssl: Add missing state update

Update server handshake environment to reflect the SNI
value also when it did not result in any new options being used.

Closes #4450

Test cases also needed to be updated to perform tests properly.
The test cases seemed to be modeled after implementation
behavior rather then the expected behavior.
---
 lib/ssl/src/ssl_gen_statem.erl     |  14 +-
 lib/ssl/test/openssl_sni_SUITE.erl | 261 +++++++++++++++--------------
 lib/ssl/test/ssl_sni_SUITE.erl     | 113 +++++++------
 lib/ssl/test/ssl_test_lib.erl      |  74 ++++++--
 4 files changed, 268 insertions(+), 194 deletions(-)

diff --git a/lib/ssl/src/ssl_gen_statem.erl b/lib/ssl/src/ssl_gen_statem.erl
index 3bbb820ef7..ddd529d032 100644
--- a/lib/ssl/src/ssl_gen_statem.erl
+++ b/lib/ssl/src/ssl_gen_statem.erl
@@ -1233,12 +1233,18 @@ is_hostname_recognized(_, _) ->
 handle_sni_hostname(Hostname,
                     #state{static_env = #static_env{role = Role} = InitStatEnv0,
                            handshake_env = HsEnv,
-                           connection_env = CEnv} = State0) ->
-    NewOptions = update_ssl_options_from_sni(State0#state.ssl_options, Hostname),
+                           connection_env = CEnv,
+                           ssl_options = Opts} = State0) ->
+    NewOptions = update_ssl_options_from_sni(Opts, Hostname),
     case NewOptions of
 	undefined ->
-	    State0;
-	_ ->
+            case maps:get(server_name_indication, Opts) of
+                disable when Role == client->
+                    State0;
+                _ ->
+                    State0#state{handshake_env = HsEnv#handshake_env{sni_hostname = Hostname}}
+            end;
+        _ ->
 	    {ok, #{cert_db_ref := Ref,
                    cert_db_handle := CertDbHandle,
                    fileref_db_handle := FileRefHandle,
diff --git a/lib/ssl/test/openssl_sni_SUITE.erl b/lib/ssl/test/openssl_sni_SUITE.erl
index 2ab553d335..4e25ddb467 100644
--- a/lib/ssl/test/openssl_sni_SUITE.erl
+++ b/lib/ssl/test/openssl_sni_SUITE.erl
@@ -33,12 +33,12 @@
          end_per_testcase/2]).
 
 %% Testcases
--export([erlang_server_openssl_client_sni_match/1,
-         erlang_server_openssl_client_sni_match_fun/1,
-         erlang_server_openssl_client_sni_no_match/1,
-         erlang_server_openssl_client_sni_no_match_fun/1,
-         erlang_server_openssl_client_sni_no_header/1,
-         erlang_server_openssl_client_sni_no_header_fun/1
+-export([sni_match/1,
+         sni_match_fun/1,
+         sni_no_match/1,
+         sni_no_match_fun/1,
+         sni_no_header/1,
+         sni_no_header_fun/1
         ]).
 
 %% Apply exports
@@ -60,10 +60,9 @@ all() ->
             [{group, 'tlsv1.3'},
              {group, 'tlsv1.2'},
              {group, 'tlsv1.1'},
-             {group, 'tlsv1'}
-             %% Seems broken in openssl 
-             %%{group, 'dtlsv1.2'},
-             %%{group, 'dtlsv1'}
+             {group, 'tlsv1'},
+             {group, 'dtlsv1.2'},
+             {group, 'dtlsv1'}
             ];
         false ->
             [{group, 'tlsv1.3'},
@@ -78,10 +77,9 @@ groups() ->
              [{'tlsv1.3', [], sni_tests()},
               {'tlsv1.2', [], sni_tests()},
               {'tlsv1.1', [], sni_tests()},
-              {'tlsv1', [], sni_tests()}
-              %% Seems broken in openssl 
-              %%{'dtlsv1.2', [], sni_tests()},
-              %%{'dtlsv1', [], sni_tests()}
+              {'tlsv1', [], sni_tests()},
+              {'dtlsv1.2', [], sni_tests()},
+              {'dtlsv1', [], sni_tests()}
              ];
         false ->
              [{'tlsv1.3', [], sni_tests()},
@@ -92,45 +90,37 @@ groups() ->
      end.
  
 sni_tests() ->
-    [erlang_server_openssl_client_sni_match,
-     erlang_server_openssl_client_sni_match_fun,
-     erlang_server_openssl_client_sni_no_match,
-     erlang_server_openssl_client_sni_no_match_fun,
-     erlang_server_openssl_client_sni_no_header,
-     erlang_server_openssl_client_sni_no_header_fun].
+    [sni_match,
+     sni_match_fun,
+     sni_no_match,
+     sni_no_match_fun,
+     sni_no_header,
+     sni_no_header_fun].
 
 init_per_suite(Config0) ->
-    case os:find_executable("openssl") of
-        false ->
-            {skip, "Openssl not found"};
-        _ ->
+    catch crypto:stop(),
+    try crypto:start() of
+        ok ->
             case check_openssl_sni_support(Config0) of
                 {skip, _} = Skip ->
                     Skip;
-                _ ->
-                    ct:pal("Version: ~p", [os:cmd("openssl version")]),
-                    catch crypto:stop(),
-                    try crypto:start() of
-                        ok ->
-                            ssl_test_lib:clean_start(),
-                            Config = ssl_test_lib:make_rsa_cert(Config0),
-                            RsaOpts = ssl_test_lib:ssl_options(server_rsa_opts, Config),
-                            [{sni_server_opts, [{sni_hosts,
-                                  [{"a.server", [
-                                                 {certfile, proplists:get_value(certfile, RsaOpts)},
-                                                 {keyfile,  proplists:get_value(keyfile, RsaOpts)}
-                                                ]},
-                                   {"b.server", [
-                                                 {certfile, proplists:get_value(certfile, RsaOpts)},
-                                                 {keyfile, proplists:get_value(keyfile, RsaOpts)}
-                                                ]}
-                                  ]}]} | Config]
-                    catch _:_  ->
-                            {skip, "Crypto did not start"}
-                    end
+                Config1 ->
+                    %% Needed by version interop test in ssl_test_lib
+                    Config = ssl_test_lib:make_rsa_cert(Config1),
+                    ssl_test_lib:clean_start(),
+                    Hostname = net_adm:localhost(),
+                    {#{server_config := ServerConf,
+                       client_config := ClientConf},
+                     #{server_config := LServerConf,
+                       client_config := LClientConf}} = ssl_test_lib:make_rsa_sni_configs(),
+                    [{client_opts, ClientConf}, {client_local_opts, LClientConf},
+                     {sni_server_opts, [{sni_hosts, [{Hostname, ServerConf}]} | LServerConf]} | Config]
             end
+    catch _:_  ->
+            {skip, "Crypto did not start"}
     end.
 
+
 end_per_suite(_Config) ->
     ssl:stop(),
     application:stop(crypto),
@@ -153,75 +143,65 @@ end_per_testcase(_, Config) ->
 %%--------------------------------------------------------------------
 %% Test Cases --------------------------------------------------------
 %%--------------------------------------------------------------------
-erlang_server_openssl_client_sni_no_header(Config) when is_list(Config) ->
-    erlang_server_openssl_client_sni_test(Config, undefined, undefined, "server Peer cert").
-
-erlang_server_openssl_client_sni_no_header_fun(Config) when is_list(Config) ->
-    erlang_server_openssl_client_sni_test_sni_fun(Config, undefined, undefined, "server Peer cert").
-
-erlang_server_openssl_client_sni_match(Config) when is_list(Config) ->  
-    erlang_server_openssl_client_sni_test(Config, "a.server", "a.server", "server Peer cert").
+sni_no_header(Config) when is_list(Config) ->
+    {_, ServerNode, _} = ssl_test_lib:run_where(Config),
+    ServerOptions = ssl_test_lib:ssl_options(proplists:get_value(sni_server_opts, Config), Config),
+    ClientOptions = ssl_test_lib:ssl_options([{server_name_indication, disable} |
+                                              proplists:get_value(client_local_opts, Config)], Config),
+    case proplists:get_value(openssl_disable, Config) of
+        true ->
+            sni_test(ServerNode, ServerOptions, ClientOptions, Config);
+        false ->
+            {skip, no_openssl_sni_disable_flag}
+    end.
 
-erlang_server_openssl_client_sni_match_fun(Config) when is_list(Config) ->
-    erlang_server_openssl_client_sni_test_sni_fun(Config, "a.server", "a.server", "server Peer cert").
+sni_no_header_fun(Config) when is_list(Config) ->
+    {_, ServerNode, _} = ssl_test_lib:run_where(Config),
+    [{sni_hosts, ServerSNIConf}| DefaultConf] = proplists:get_value(sni_server_opts, Config),
+    SNIFun = fun(Domain) -> proplists:get_value(Domain, ServerSNIConf, []) end,
+    ServerOptions =  ssl_test_lib:ssl_options(DefaultConf, Config) ++ [{sni_fun, SNIFun}],
+    ClientOptions =  ssl_test_lib:ssl_options([{server_name_indication, disable} |
+                                               proplists:get_value(client_local_opts, Config)], Config),
+    case proplists:get_value(openssl_disable, Config) of
+        true ->
+            sni_test(ServerNode, ServerOptions, ClientOptions, Config);
+        false ->
+            {skip, no_openssl_sni_disable_flag}
+    end.
 
-erlang_server_openssl_client_sni_no_match(Config) when is_list(Config) ->
-    erlang_server_openssl_client_sni_test(Config, "c.server", undefined, "server Peer cert").
+sni_match(Config) when is_list(Config) ->
+    {_, ServerNode, HostName} = ssl_test_lib:run_where(Config),
+    ServerOptions = ssl_test_lib:ssl_options(proplists:get_value(sni_server_opts, Config), Config),
+    ClientOptions =  ssl_test_lib:ssl_options([{server_name_indication, HostName} |
+                                               proplists:get_value(client_opts, Config)], Config),
+    sni_test(ServerNode, ServerOptions, ClientOptions, Config).
 
-erlang_server_openssl_client_sni_no_match_fun(Config) when is_list(Config) ->
-    erlang_server_openssl_client_sni_test_sni_fun(Config, "c.server", undefined, "server Peer cert").
+sni_match_fun(Config) when is_list(Config) ->
+    {_, ServerNode, HostName} = ssl_test_lib:run_where(Config),
+    [{sni_hosts, ServerSNIConf}| DefaultConf] = proplists:get_value(sni_server_opts, Config),
+    SNIFun = fun(Domain) -> proplists:get_value(Domain, ServerSNIConf, undefined) end,
+    ServerOptions =  ssl_test_lib:ssl_options(DefaultConf, Config) ++ [{sni_fun, SNIFun}],
+    ClientOptions =  ssl_test_lib:ssl_options([{server_name_indication, HostName} |
+                                               proplists:get_value(client_opts, Config)], Config),
+    sni_test(ServerNode, ServerOptions, ClientOptions, Config).
 
+sni_no_match(Config) when is_list(Config) ->
+    {_, ServerNode, HostName} = ssl_test_lib:run_where(Config),
+    [_ | DefaultConf] = proplists:get_value(sni_server_opts, Config),
+    ClientOptions =  ssl_test_lib:ssl_options([{server_name_indication, HostName} |
+                                               proplists:get_value(client_opts, Config)], Config),
+    ServerOptions =  ssl_test_lib:ssl_options(DefaultConf, Config),
+    sni_test(ServerNode, ServerOptions, ClientOptions, Config).
 
-erlang_server_openssl_client_sni_test(Config, SNIHostname, ExpectedSNIHostname, ExpectedCN) ->
-    Version = ssl_test_lib:protocol_version(Config),
-    ct:log("Start running handshake, Config: ~p, SNIHostname: ~p, ExpectedSNIHostname: ~p, ExpectedCN: ~p", 
-           [Config, SNIHostname, ExpectedSNIHostname, ExpectedCN]),
-    ServerOptions = proplists:get_value(sni_server_opts, Config) ++ proplists:get_value(server_rsa_opts, Config),
-    {_, ServerNode, Hostname} = ssl_test_lib:run_where(Config),
-    Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0},
-                                        {from, self()}, {mfa, {?MODULE, send_and_hostname, []}},
-                                        {options, ServerOptions}]),
-    Port = ssl_test_lib:inet_port(Server),
-    Exe = "openssl",
-    ClientArgs = case SNIHostname of
-		     undefined ->
-			 openssl_client_args(Version, Hostname,Port);
-		     _ ->
-			 openssl_client_args(Version, Hostname, Port, SNIHostname)
-		 end,       
-    ClientPort = ssl_test_lib:portable_open_port(Exe, ClientArgs),  
-  
-    ssl_test_lib:check_result(Server, ExpectedSNIHostname),
-    ssl_test_lib:close_port(ClientPort),
-    ssl_test_lib:close(Server),
-    ok.
+sni_no_match_fun(Config) when is_list(Config) ->
+    {_, ServerNode, HostName} = ssl_test_lib:run_where(Config),
+    [{sni_hosts, _}| DefaultConf] = proplists:get_value(sni_server_opts, Config),
+    ServerOptions =  ssl_test_lib:ssl_options(DefaultConf, Config),
+    ClientOptions =  ssl_test_lib:ssl_options([{server_name_indication, HostName} |
+                                               proplists:get_value(client_local_opts, Config)], Config),
+    sni_test(ServerNode, ServerOptions, ClientOptions, Config).
 
 
-erlang_server_openssl_client_sni_test_sni_fun(Config, SNIHostname, ExpectedSNIHostname, ExpectedCN) ->
-    Version = ssl_test_lib:protocol_version(Config),
-    ct:log("Start running handshake for sni_fun, Config: ~p, SNIHostname: ~p, ExpectedSNIHostname: ~p, ExpectedCN: ~p",
-           [Config, SNIHostname, ExpectedSNIHostname, ExpectedCN]),
-    [{sni_hosts, ServerSNIConf}] = proplists:get_value(sni_server_opts, Config),
-    SNIFun = fun(Domain) -> proplists:get_value(Domain, ServerSNIConf, undefined) end,
-    ServerOptions = proplists:get_value(server_rsa_opts, Config) ++ [{sni_fun, SNIFun}],
-    {_, ServerNode, Hostname} = ssl_test_lib:run_where(Config),
-    Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0},
-                                        {from, self()}, {mfa, {?MODULE, send_and_hostname, []}},
-                                        {options, ServerOptions}]),
-    Port = ssl_test_lib:inet_port(Server),
-    Exe = "openssl",
-    ClientArgs = case SNIHostname of
-		     undefined ->
-			 openssl_client_args(Version, Hostname,Port);
-		     _ ->
-			 openssl_client_args(Version, Hostname, Port, SNIHostname)
-		 end,       
-
-    ClientPort = ssl_test_lib:portable_open_port(Exe, ClientArgs), 
-    
-    ssl_test_lib:check_result(Server, ExpectedSNIHostname),
-    ssl_test_lib:close_port(ClientPort),
-    ssl_test_lib:close(Server).
 %%--------------------------------------------------------------------
 %% Callback functions  ------------------------------------------------
 %%--------------------------------------------------------------------
@@ -236,25 +216,60 @@ send_and_hostname(SSLSocket) ->
 %%--------------------------------------------------------------------
 %% Internal functions  -----------------------------------------------
 %%--------------------------------------------------------------------
-openssl_client_args(Version, Hostname, Port) ->
-    ssl_test_lib:maybe_force_ipv4(["s_client", "-connect", Hostname ++ ":" ++ integer_to_list(Port), 
-                                   ssl_test_lib:version_flag(Version)]).
+sni_test(ServerNode, ServerOptions0, ClientOptions, Config) ->
+    %% Test Erlang server SNI handling with OpenSSL client
+    Version = ssl_test_lib:protocol_version(Config),
+    ServerOptions = maybe_add_sigalgs(Version, ServerOptions0),
+
+
+    Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0},
+                                        {from, self()}, {mfa, {?MODULE, send_and_hostname, []}},
+                                        {options, ServerOptions}]),
+    Port = ssl_test_lib:inet_port(Server),
+
+    SNIHostname = case proplists:get_value(server_name_indication, ClientOptions) of
+                      disable ->
+                          undefined;
+                      Name ->
+                          Name
+                  end,
+
+    Sigalgs = maybe_add_openssl_sigalgs(Version),
+    ClientPort = ssl_test_lib:start_client(openssl, 
+                                           Sigalgs ++ [{port, Port}, 
+                                                       {server_name, SNIHostname} | 
+                                                                ClientOptions], 
+                                           Config),
+  
+    ssl_test_lib:check_result(Server, SNIHostname),
+    ssl_test_lib:close_port(ClientPort),
+    ssl_test_lib:close(Server),
+    ok.
+
+maybe_add_sigalgs(Version, ServerOptions) when Version == 'tlsv1.3';
+                                               Version == 'tlsv1.2' ->
+    [{signature_algs, [rsa_pss_rsae_sha512,
+                       rsa_pss_rsae_sha384,
+                       rsa_pss_rsae_sha256]} | ServerOptions];
+maybe_add_sigalgs(_, ServerOptions) ->
+    ServerOptions.
 
-openssl_client_args(Version, Hostname, Port, ServerName) ->
-    ssl_test_lib:maybe_force_ipv4(["s_client",  "-connect", Hostname ++ ":" ++ 
-                                       integer_to_list(Port), 
-                                   ssl_test_lib:version_flag(Version), "-servername", ServerName]).
+maybe_add_openssl_sigalgs(Version) when Version == 'tlsv1.3';
+                                        Version == 'tlsv1.2' ->
+    [{sigalgs, "rsa_pss_rsae_sha512:rsa_pss_rsae_sha384:rsa_pss_rsae_sha256"}];
+maybe_add_openssl_sigalgs(_) ->
+    [].
 
 check_openssl_sni_support(Config) ->
-    HelpText = os:cmd("openssl s_client --help"),
-    case ssl_test_lib:is_sane_oppenssl_client() of
-        true ->
-            case string:str(HelpText, "-servername") of
+    HelpText = ssl_test_lib:portable_cmd("openssl", ["s_client", "--help"]),
+    case string:str(HelpText, "-servername") of
+        0 ->
+            {skip, "Current openssl doesn't support SNI"};
+        _ ->
+            case string:str(HelpText, "-noservername") of
                 0 ->
-                    {skip, "Current openssl doesn't support SNI"};
+                    [{openssl_disable, false} | Config];
                 _ ->
-                    Config
-            end;
-        false ->
-            {skip, "Current openssl doesn't support SNI or extension handling is flawed"}
+                    [{openssl_disable, true} | Config]
+            end
     end.
diff --git a/lib/ssl/test/ssl_sni_SUITE.erl b/lib/ssl/test/ssl_sni_SUITE.erl
index c855f154f5..247ab7e4cc 100644
--- a/lib/ssl/test/ssl_sni_SUITE.erl
+++ b/lib/ssl/test/ssl_sni_SUITE.erl
@@ -106,18 +106,16 @@ init_per_suite(Config0) ->
     try crypto:start() of
         ok ->
             ssl_test_lib:clean_start(),
-            Config = ssl_test_lib:make_rsa_cert(Config0),
-            RsaOpts = ssl_test_lib:ssl_options(server_rsa_opts, Config),
-            [{sni_server_opts, [{sni_hosts, [
-                                              {"a.server", [
-                                                            {certfile, proplists:get_value(certfile, RsaOpts)},
-                                                            {keyfile,  proplists:get_value(keyfile, RsaOpts)}
-                                                           ]},
-                                              {"b.server", [
-                                                            {certfile, proplists:get_value(certfile, RsaOpts)},
-                                                            {keyfile, proplists:get_value(keyfile, RsaOpts)}
-                                                           ]}
-                                             ]}]} | Config] 
+            Hostname = net_adm:localhost(),
+            {#{server_config := ServerConf,
+               client_config := ClientConf},
+             #{server_config := LServerConf,
+               client_config := LClientConf}} = ssl_test_lib:make_rsa_sni_configs(),
+            %% RSA certs files needed by *dot cases
+            ssl_test_lib:make_rsa_cert([{client_opts, ClientConf},
+                                        {client_local_opts, LClientConf},
+                                        {sni_server_opts, [{sni_hosts, [{Hostname, ServerConf}]} | LServerConf]}
+                                       | Config0])
     catch _:_  ->
             {skip, "Crypto did not start"}
     end.
@@ -150,22 +148,52 @@ end_per_testcase(_TestCase, Config) ->
 %% Test Cases --------------------------------------------------------
 %%--------------------------------------------------------------------
 no_sni_header(Config) ->
-    run_handshake(Config, undefined, undefined, "server Peer cert").
+    {ClientNode, ServerNode, HostName} = ssl_test_lib:run_where(Config),
+    ServerOptions = ssl_test_lib:ssl_options(proplists:get_value(sni_server_opts, Config), Config),
+    ClientOptions = ssl_test_lib:ssl_options([{server_name_indication, disable} |
+                                              proplists:get_value(client_local_opts, Config)], Config),
+    basic_sni_test(ServerNode, ServerOptions, ClientNode, ClientOptions, HostName, undefined).
 
 no_sni_header_fun(Config) ->
-    run_sni_fun_handshake(Config, undefined, undefined, "server Peer cert").
+    {ClientNode, ServerNode, HostName} = ssl_test_lib:run_where(Config),
+    [{sni_hosts, ServerSNIConf}| DefaultConf] = proplists:get_value(sni_server_opts, Config),
+    SNIFun = fun(Domain) -> proplists:get_value(Domain, ServerSNIConf, []) end,
+    ServerOptions =  ssl_test_lib:ssl_options(DefaultConf, Config) ++ [{sni_fun, SNIFun}],
+    ClientOptions =  ssl_test_lib:ssl_options([{server_name_indication, disable} |
+                                               proplists:get_value(client_local_opts, Config)], Config),
+    basic_sni_test(ServerNode, ServerOptions, ClientNode, ClientOptions, HostName, undefined).
 
 sni_match(Config) ->
-    run_handshake(Config, "a.server", "a.server",  "server Peer cert").
+    {ClientNode, ServerNode, HostName} = ssl_test_lib:run_where(Config),
+    ServerOptions = ssl_test_lib:ssl_options(proplists:get_value(sni_server_opts, Config), Config),
+    ClientOptions =  ssl_test_lib:ssl_options([{server_name_indication, HostName} |
+                                               proplists:get_value(client_opts, Config)], Config),
+    basic_sni_test(ServerNode, ServerOptions, ClientNode, ClientOptions, HostName, HostName).
 
 sni_match_fun(Config) ->
-    run_sni_fun_handshake(Config, "a.server", "a.server", "server Peer cert").
+    {ClientNode, ServerNode, HostName} = ssl_test_lib:run_where(Config),
+    [{sni_hosts, ServerSNIConf}| DefaultConf] = proplists:get_value(sni_server_opts, Config),
+    SNIFun = fun(Domain) -> proplists:get_value(Domain, ServerSNIConf, undefined) end,
+    ServerOptions =  ssl_test_lib:ssl_options(DefaultConf, Config) ++ [{sni_fun, SNIFun}],
+    ClientOptions =  ssl_test_lib:ssl_options([{server_name_indication, HostName} |
+                                               proplists:get_value(client_opts, Config)], Config),
+    basic_sni_test(ServerNode, ServerOptions, ClientNode, ClientOptions, HostName, HostName).
 
 sni_no_match(Config) ->
-    run_handshake(Config, "c.server", undefined, "server Peer cert").
+   {ClientNode, ServerNode, HostName} = ssl_test_lib:run_where(Config),
+    [_ | DefaultConf] = proplists:get_value(sni_server_opts, Config),
+    ClientOptions =  ssl_test_lib:ssl_options([{server_name_indication, HostName} |
+                                               proplists:get_value(client_opts, Config)], Config),
+    ServerOptions =  ssl_test_lib:ssl_options(DefaultConf, Config),
+    basic_sni_alert_test(ServerNode, ServerOptions, ClientNode, ClientOptions, HostName).
 
 sni_no_match_fun(Config) ->
-    run_sni_fun_handshake(Config, "c.server", undefined, "server Peer cert").
+    {ClientNode, ServerNode, HostName} = ssl_test_lib:run_where(Config),
+    [{sni_hosts, _}| DefaultConf] = proplists:get_value(sni_server_opts, Config),
+    ServerOptions =  ssl_test_lib:ssl_options(DefaultConf, Config),
+    ClientOptions =  ssl_test_lib:ssl_options([{server_name_indication, HostName} |
+                                               proplists:get_value(client_local_opts, Config)], Config),
+    basic_sni_alert_test(ServerNode, ServerOptions, ClientNode, ClientOptions, HostName).
 
 dns_name(Config) ->
     Hostname = "OTP.test.server",
@@ -415,57 +443,28 @@ recv_and_certificate(SSLSocket) ->
 %%--------------------------------------------------------------------
 %% Internal Functions ------------------------------------------------
 %%--------------------------------------------------------------------
-run_sni_fun_handshake(Config, SNIHostname, ExpectedSNIHostname, ExpectedCN) ->
-    ct:log("Start running handshake for sni_fun, Config: ~p, SNIHostname: ~p, "
-	   "ExpectedSNIHostname: ~p, ExpectedCN: ~p", 
-	   [Config, SNIHostname, ExpectedSNIHostname, ExpectedCN]),
-    [{sni_hosts, ServerSNIConf}] = proplists:get_value(sni_server_opts, Config),
-    SNIFun = fun(Domain) -> proplists:get_value(Domain, ServerSNIConf, undefined) end,
-    ServerOptions =  ssl_test_lib:ssl_options(server_rsa_opts, Config) ++ [{sni_fun, SNIFun}],
-    ClientOptions = 
-    case SNIHostname of
-        undefined ->
-            ssl_test_lib:ssl_options(client_rsa_opts, Config);
-        _ ->
-            [{server_name_indication, SNIHostname}] ++ ssl_test_lib:ssl_options(client_rsa_opts, Config)
-    end,
-    ct:log("Options: ~p", [[ServerOptions, ClientOptions]]),
-    {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config),
+basic_sni_test(ServerNode, ServerOptions, ClientNode, ClientOptions, HostName, Expected) ->
     Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0},
                                         {from, self()}, {mfa, {?MODULE, send_and_hostname, []}},
                                         {options, ServerOptions}]),
     Port = ssl_test_lib:inet_port(Server),
     Client = ssl_test_lib:start_client([{node, ClientNode}, {port, Port},
-                                        {host, Hostname}, {from, self()},
+                                        {host, HostName}, {from, self()},
                                         {mfa, {?MODULE, recv_and_certificate, []}},
-                                        {options, ClientOptions}]),
-    ssl_test_lib:check_result(Server, ExpectedSNIHostname, Client, ExpectedCN),
+                                        {options, [{verify, verify_peer} | ClientOptions]}]),
+    ssl_test_lib:check_result(Server, Expected, Client,  "server Peer cert"),
     ssl_test_lib:close(Server),
     ssl_test_lib:close(Client).
 
-run_handshake(Config, SNIHostname, ExpectedSNIHostname, ExpectedCN) ->
-    ct:log("Start running handshake, Config: ~p, SNIHostname: ~p, "
-	   "ExpectedSNIHostname: ~p, ExpectedCN: ~p", 
-	   [Config, SNIHostname, ExpectedSNIHostname, ExpectedCN]),
-    ServerOptions = proplists:get_value(sni_server_opts, Config) ++ ssl_test_lib:ssl_options(server_rsa_opts, Config),
-    ClientOptions = 
-        case SNIHostname of
-            undefined ->
-                ssl_test_lib:ssl_options(client_rsa_opts, Config);
-            _ ->
-                [{server_name_indication, SNIHostname}] ++ ssl_test_lib:ssl_options(client_rsa_opts, Config)
-        end,
-    ct:log("Options: ~p", [[ServerOptions, ClientOptions]]),
-    {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config),
+basic_sni_alert_test(ServerNode, ServerOptions, ClientNode, ClientOptions, HostName) ->
     Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0},
-                                        {from, self()}, {mfa, {?MODULE, send_and_hostname, []}},
+                                        {from, self()}, {mfa, {ssl_test_lib, no_result, []}},
                                         {options, ServerOptions}]),
     Port = ssl_test_lib:inet_port(Server),
-    Client = ssl_test_lib:start_client([{node, ClientNode}, {port, Port},
-                                        {host, Hostname}, {from, self()},
-                                        {mfa, {?MODULE, recv_and_certificate, []}},
-                                        {options, ClientOptions}]),
-    ssl_test_lib:check_result(Server, ExpectedSNIHostname, Client, ExpectedCN),
+    Client = ssl_test_lib:start_client_error([{node, ClientNode}, {port, Port},
+                                              {host, HostName}, {from, self()},
+                                              {options, [{verify, verify_peer} | ClientOptions]}]),
+    ssl_test_lib:check_client_alert(Client, handshake_failure),
     ssl_test_lib:close(Server),
     ssl_test_lib:close(Client).
 
diff --git a/lib/ssl/test/ssl_test_lib.erl b/lib/ssl/test/ssl_test_lib.erl
index 778913c4b9..fe52b60f98 100644
--- a/lib/ssl/test/ssl_test_lib.erl
+++ b/lib/ssl/test/ssl_test_lib.erl
@@ -172,6 +172,7 @@
          make_ec_cert_chains/5,
          make_rsa_1024_cert/1,
          make_rsa_pss_pem/4,
+         make_rsa_sni_configs/0,
          gen_conf/4,
          make_mix_cert/1,
          default_cert_chain_conf/0,
@@ -1519,6 +1520,34 @@ make_rsa_pss_pem(Alg, _UserConf, Config, Suffix) ->
     #{server_config => SConf,
       client_config => CConf}.
 
+make_rsa_sni_configs() ->
+    Sha = appropriate_sha(crypto:supports()),
+    HostName = net_adm:localhost(),
+    HostConf = make_sni_conf(HostName, Sha),
+    LocalHostConf = make_sni_conf("localhost", Sha),
+    {HostConf, LocalHostConf}.
+
+make_sni_conf(HostName, Sha) ->
+    public_key:pkix_test_data(#{server_chain =>
+                                    #{root => [{digest, Sha},
+                                               {key, hardcode_rsa_key(1)}],
+                                      intermediates => [[{digest, Sha},
+                                                         {key, hardcode_rsa_key(2)}]],
+                                      peer => [{digest, Sha},
+                                               {extensions, [#'Extension'{extnID =
+                                                                              ?'id-ce-subjectAltName',
+                                                                          extnValue = [{dNSName, HostName}],
+                                                                          critical = false}]},
+                                               {key, hardcode_rsa_key(3)}
+                                              ]},
+                                client_chain =>
+                                    #{root => [{digest, Sha},
+                                               {key, hardcode_rsa_key(4)}],
+                                      intermediates => [[{digest, Sha},
+                                                         {key, hardcode_rsa_key(5)}]],
+                                      peer => [{digest, Sha},
+                                               {key, hardcode_rsa_key(6)}]}}).
+
 gen_conf(ClientChainType, ServerChainType, UserClient, UserServer) ->
     gen_conf(ClientChainType, ServerChainType, UserClient, UserServer, ?DEFAULT_CURVE).
 %%
@@ -2113,6 +2142,7 @@ start_client(openssl, Port, ClientOpts, Config) ->
     MaxFragLen = openssl_maxfag_option(proplists:get_value(maxfrag, ClientOpts, false)),
     SessionArgs =  proplists:get_value(session_args, ClientOpts, []),
     HostName = proplists:get_value(hostname, ClientOpts, net_adm:localhost()),
+    SNI = openssl_sni(proplists:get_value(server_name_indication, ClientOpts, undefined)),
     Debug = openssl_debug_options(),
 
     Exe = "openssl",
@@ -2120,20 +2150,37 @@ start_client(openssl, Port, ClientOpts, Config) ->
                 undefined ->
                      ["s_client",
                       "-verify", "2",
-                      "-connect", hostname_format(HostName) ++ ":" ++ integer_to_list(Port), cipher_flag(Version),
+                      "-connect", hostname_format(HostName) ++ ":" ++ integer_to_list(Port),
+                      cipher_flag(Version),
                       ciphers(Ciphers, Version),
-                      version_flag(Version)]
-                         ++ CertArgs ++ SigAlgs ++ AlpnArgs ++ NpnArgs ++ Reconnect ++ MaxFragLen ++ SessionArgs
-                         ++ Debug;
+                      version_flag(Version)] ++
+                         CertArgs ++
+                         SigAlgs ++
+                         SNI ++
+                         AlpnArgs ++
+                         NpnArgs ++
+                         Reconnect ++
+                         MaxFragLen ++
+                         SessionArgs ++
+                         Debug;
                  Group ->
                      ["s_client",
                       "-verify", "2",
-                      "-connect", hostname_format(HostName) ++ ":" ++ integer_to_list(Port), cipher_flag(Version),
-                      ciphers(Ciphers, Version), "-groups", Group,
-                      version_flag(Version)]
-                         ++ CertArgs ++ SigAlgs ++ AlpnArgs ++ NpnArgs ++ Reconnect ++ MaxFragLen ++ SessionArgs
-                         ++ Debug
-                 end,
+                      "-connect", hostname_format(HostName) ++ ":" ++ integer_to_list(Port),
+                      cipher_flag(Version),
+                      ciphers(Ciphers, Version),
+                      "-groups", Group,
+                      version_flag(Version)] ++
+                         CertArgs ++
+                         SigAlgs ++
+                         SNI ++
+                         AlpnArgs ++
+                         NpnArgs ++
+                         Reconnect ++
+                         MaxFragLen ++
+                         SessionArgs ++
+                         Debug
+             end,
     Args = maybe_force_ipv4(Args0),
     OpenSslPort = portable_open_port(Exe, Args),
     true = port_command(OpenSslPort, "Hello world"),
@@ -2263,6 +2310,13 @@ openssl_maxfag_option(false) ->
 openssl_maxfag_option(Int) ->
     ["-maxfraglen", integer_to_list(Int)].
 
+openssl_sni(undefined) ->
+    [];
+openssl_sni(disable) ->
+    ["-noservername"];
+openssl_sni(ServerName) ->
+    ["-servername", ServerName].
+
 openssl_debug_options() ->
     ["-msg", "-debug"].
 %%
-- 
2.31.1

openSUSE Build Service is sponsored by