%% %% %CopyrightBegin% %% %% Copyright Ericsson AB 2019-2019. All Rights Reserved. %% %% Licensed under the Apache License, Version 2.0 (the "License"); %% you may not use this file except in compliance with the License. %% You may obtain a copy of the License at %% %% http://www.apache.org/licenses/LICENSE-2.0 %% %% Unless required by applicable law or agreed to in writing, software %% distributed under the License is distributed on an "AS IS" BASIS, %% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. %% See the License for the specific language governing permissions and %% limitations under the License. %% %% %CopyrightEnd% %% %% -module(openssl_sni_SUITE). %% Note: This directive should only be used in test suites. -compile(export_all). -include_lib("common_test/include/ct.hrl"). -define(OPENSSL_QUIT, "Q\n"). -define(OPENSSL_RENEGOTIATE, "R\n"). -define(SLEEP, 1000). %%-------------------------------------------------------------------- %% Common Test interface functions ----------------------------------- %%-------------------------------------------------------------------- all() -> %% Note: SNI not supported in sslv3 case ssl_test_lib:openssl_sane_dtls() of true -> [{group, 'tlsv1.2'}, {group, 'tlsv1.1'}, {group, 'tlsv1'} %% Seems broken in openssl %%{group, 'dtlsv1.2'}, %%{group, 'dtlsv1'} ]; false -> [{group, 'tlsv1.2'}, {group, 'tlsv1.1'}, {group, 'tlsv1'}] end. groups() -> case ssl_test_lib:openssl_sane_dtls() of true -> [{'tlsv1.2', [], sni_tests()}, {'tlsv1.1', [], sni_tests()}, {'tlsv1', [], sni_tests()} %% Seems broken in openssl %%{'dtlsv1.2', [], sni_tests()}, %%{'dtlsv1', [], sni_tests()} ]; false -> [{'tlsv1.2', [], sni_tests()}, {'tlsv1.1', [], sni_tests()}, {'tlsv1', [], sni_tests()} ] 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]. init_per_suite(Config0) -> case os:find_executable("openssl") of false -> {skip, "Openssl not found"}; _ -> 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 end end. end_per_suite(_Config) -> ssl:stop(), application:stop(crypto), ssl_test_lib:kill_openssl(). init_per_group(GroupName, Config) -> case ssl_test_lib:is_tls_version(GroupName) of true -> case ssl_test_lib:supports_ssl_tls_version(GroupName) of true -> case ssl_test_lib:check_sane_openssl_version(GroupName) of true -> ssl_test_lib:init_tls_version(GroupName, Config); false -> {skip, openssl_does_not_support_version} end; false -> {skip, openssl_does_not_support_version} end; _ -> Config end. end_per_group(GroupName, Config) -> case ssl_test_lib:is_tls_version(GroupName) of true -> ssl_test_lib:clean_tls_version(Config); false -> Config end. init_per_testcase(_TestCase, Config) -> ct:timetrap({seconds, 10}), Config. end_per_testcase(_, Config) -> 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"). 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"). 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"). 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"). 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. 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). send_and_hostname(SSLSocket) -> ssl:send(SSLSocket, "OK"), case ssl:connection_information(SSLSocket, [sni_hostname]) of {ok, []} -> undefined; {ok, [{sni_hostname, Hostname}]} -> Hostname end. openssl_client_args(Version, Hostname, Port) -> ["s_client", "-connect", Hostname ++ ":" ++ integer_to_list(Port), ssl_test_lib:version_flag(Version)]. openssl_client_args(Version, Hostname, Port, ServerName) -> ["s_client", "-connect", Hostname ++ ":" ++ integer_to_list(Port), ssl_test_lib:version_flag(Version), "-servername", ServerName]. 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 0 -> {skip, "Current openssl doesn't support SNI"}; _ -> Config end; false -> {skip, "Current openssl doesn't support SNI or extension handling is flawed"} end.