1%%
2%% %CopyrightBegin%
3%%
4%% Copyright Ericsson AB 2019-2020. All Rights Reserved.
5%%
6%% Licensed under the Apache License, Version 2.0 (the "License");
7%% you may not use this file except in compliance with the License.
8%% You may obtain a copy of the License at
9%%
10%%     http://www.apache.org/licenses/LICENSE-2.0
11%%
12%% Unless required by applicable law or agreed to in writing, software
13%% distributed under the License is distributed on an "AS IS" BASIS,
14%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15%% See the License for the specific language governing permissions and
16%% limitations under the License.
17%%
18%% %CopyrightEnd%
19%%
20%%
21
22-module(openssl_sni_SUITE).
23
24-include_lib("common_test/include/ct.hrl").
25%% Callback functions
26-export([all/0,
27         groups/0,
28         init_per_suite/1,
29         end_per_suite/1,
30         init_per_group/2,
31         end_per_group/2,
32         init_per_testcase/2,
33         end_per_testcase/2]).
34
35%% Testcases
36-export([erlang_server_openssl_client_sni_match/1,
37         erlang_server_openssl_client_sni_match_fun/1,
38         erlang_server_openssl_client_sni_no_match/1,
39         erlang_server_openssl_client_sni_no_match_fun/1,
40         erlang_server_openssl_client_sni_no_header/1,
41         erlang_server_openssl_client_sni_no_header_fun/1
42        ]).
43
44%% Apply exports
45-export([send_and_hostname/1
46        ]).
47
48-define(OPENSSL_QUIT, "Q\n").
49-define(OPENSSL_RENEGOTIATE, "R\n").
50-define(SLEEP, 1000).
51
52%%--------------------------------------------------------------------
53%% Common Test interface functions -----------------------------------
54%%--------------------------------------------------------------------
55
56all() ->
57    %% Note: SNI not supported in sslv3
58    case ssl_test_lib:openssl_sane_dtls() of
59        true ->
60            [{group, 'tlsv1.3'},
61             {group, 'tlsv1.2'},
62             {group, 'tlsv1.1'},
63             {group, 'tlsv1'}
64             %% Seems broken in openssl
65             %%{group, 'dtlsv1.2'},
66             %%{group, 'dtlsv1'}
67            ];
68        false ->
69            [{group, 'tlsv1.3'},
70             {group, 'tlsv1.2'},
71             {group, 'tlsv1.1'},
72             {group, 'tlsv1'}]
73    end.
74
75groups() ->
76     case ssl_test_lib:openssl_sane_dtls() of
77         true ->
78             [{'tlsv1.3', [], sni_tests()},
79              {'tlsv1.2', [], sni_tests()},
80              {'tlsv1.1', [], sni_tests()},
81              {'tlsv1', [], sni_tests()}
82              %% Seems broken in openssl
83              %%{'dtlsv1.2', [], sni_tests()},
84              %%{'dtlsv1', [], sni_tests()}
85             ];
86        false ->
87             [{'tlsv1.3', [], sni_tests()},
88              {'tlsv1.2', [], sni_tests()},
89              {'tlsv1.1', [], sni_tests()},
90              {'tlsv1', [], sni_tests()}
91             ]
92     end.
93
94sni_tests() ->
95    [erlang_server_openssl_client_sni_match,
96     erlang_server_openssl_client_sni_match_fun,
97     erlang_server_openssl_client_sni_no_match,
98     erlang_server_openssl_client_sni_no_match_fun,
99     erlang_server_openssl_client_sni_no_header,
100     erlang_server_openssl_client_sni_no_header_fun].
101
102init_per_suite(Config0) ->
103    case os:find_executable("openssl") of
104        false ->
105            {skip, "Openssl not found"};
106        _ ->
107            case check_openssl_sni_support(Config0) of
108                {skip, _} = Skip ->
109                    Skip;
110                _ ->
111                    ct:pal("Version: ~p", [os:cmd("openssl version")]),
112                    catch crypto:stop(),
113                    try crypto:start() of
114                        ok ->
115                            ssl_test_lib:clean_start(),
116                            Config = ssl_test_lib:make_rsa_cert(Config0),
117                            RsaOpts = ssl_test_lib:ssl_options(server_rsa_opts, Config),
118                            [{sni_server_opts, [{sni_hosts,
119                                  [{"a.server", [
120                                                 {certfile, proplists:get_value(certfile, RsaOpts)},
121                                                 {keyfile,  proplists:get_value(keyfile, RsaOpts)}
122                                                ]},
123                                   {"b.server", [
124                                                 {certfile, proplists:get_value(certfile, RsaOpts)},
125                                                 {keyfile, proplists:get_value(keyfile, RsaOpts)}
126                                                ]}
127                                  ]}]} | Config]
128                    catch _:_  ->
129                            {skip, "Crypto did not start"}
130                    end
131            end
132    end.
133
134end_per_suite(_Config) ->
135    ssl:stop(),
136    application:stop(crypto),
137    ssl_test_lib:kill_openssl().
138
139init_per_group(GroupName, Config) ->
140    ssl_test_lib:init_per_group_openssl(GroupName, Config).
141
142end_per_group(GroupName, Config) ->
143    ssl_test_lib:end_per_group(GroupName, Config).
144
145init_per_testcase(_TestCase, Config) ->
146    ct:timetrap({seconds, 10}),
147    Config.
148
149
150end_per_testcase(_, Config) ->
151    Config.
152
153%%--------------------------------------------------------------------
154%% Test Cases --------------------------------------------------------
155%%--------------------------------------------------------------------
156erlang_server_openssl_client_sni_no_header(Config) when is_list(Config) ->
157    erlang_server_openssl_client_sni_test(Config, undefined, undefined, "server Peer cert").
158
159erlang_server_openssl_client_sni_no_header_fun(Config) when is_list(Config) ->
160    erlang_server_openssl_client_sni_test_sni_fun(Config, undefined, undefined, "server Peer cert").
161
162erlang_server_openssl_client_sni_match(Config) when is_list(Config) ->
163    erlang_server_openssl_client_sni_test(Config, "a.server", "a.server", "server Peer cert").
164
165erlang_server_openssl_client_sni_match_fun(Config) when is_list(Config) ->
166    erlang_server_openssl_client_sni_test_sni_fun(Config, "a.server", "a.server", "server Peer cert").
167
168erlang_server_openssl_client_sni_no_match(Config) when is_list(Config) ->
169    erlang_server_openssl_client_sni_test(Config, "c.server", undefined, "server Peer cert").
170
171erlang_server_openssl_client_sni_no_match_fun(Config) when is_list(Config) ->
172    erlang_server_openssl_client_sni_test_sni_fun(Config, "c.server", undefined, "server Peer cert").
173
174
175erlang_server_openssl_client_sni_test(Config, SNIHostname, ExpectedSNIHostname, ExpectedCN) ->
176    Version = ssl_test_lib:protocol_version(Config),
177    ct:log("Start running handshake, Config: ~p, SNIHostname: ~p, ExpectedSNIHostname: ~p, ExpectedCN: ~p",
178           [Config, SNIHostname, ExpectedSNIHostname, ExpectedCN]),
179    ServerOptions = proplists:get_value(sni_server_opts, Config) ++ proplists:get_value(server_rsa_opts, Config),
180    {_, ServerNode, Hostname} = ssl_test_lib:run_where(Config),
181    Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0},
182                                        {from, self()}, {mfa, {?MODULE, send_and_hostname, []}},
183                                        {options, ServerOptions}]),
184    Port = ssl_test_lib:inet_port(Server),
185    Exe = "openssl",
186    ClientArgs = case SNIHostname of
187		     undefined ->
188			 openssl_client_args(Version, Hostname,Port);
189		     _ ->
190			 openssl_client_args(Version, Hostname, Port, SNIHostname)
191		 end,
192    ClientPort = ssl_test_lib:portable_open_port(Exe, ClientArgs),
193
194    ssl_test_lib:check_result(Server, ExpectedSNIHostname),
195    ssl_test_lib:close_port(ClientPort),
196    ssl_test_lib:close(Server),
197    ok.
198
199
200erlang_server_openssl_client_sni_test_sni_fun(Config, SNIHostname, ExpectedSNIHostname, ExpectedCN) ->
201    Version = ssl_test_lib:protocol_version(Config),
202    ct:log("Start running handshake for sni_fun, Config: ~p, SNIHostname: ~p, ExpectedSNIHostname: ~p, ExpectedCN: ~p",
203           [Config, SNIHostname, ExpectedSNIHostname, ExpectedCN]),
204    [{sni_hosts, ServerSNIConf}] = proplists:get_value(sni_server_opts, Config),
205    SNIFun = fun(Domain) -> proplists:get_value(Domain, ServerSNIConf, undefined) end,
206    ServerOptions = proplists:get_value(server_rsa_opts, Config) ++ [{sni_fun, SNIFun}],
207    {_, ServerNode, Hostname} = ssl_test_lib:run_where(Config),
208    Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0},
209                                        {from, self()}, {mfa, {?MODULE, send_and_hostname, []}},
210                                        {options, ServerOptions}]),
211    Port = ssl_test_lib:inet_port(Server),
212    Exe = "openssl",
213    ClientArgs = case SNIHostname of
214		     undefined ->
215			 openssl_client_args(Version, Hostname,Port);
216		     _ ->
217			 openssl_client_args(Version, Hostname, Port, SNIHostname)
218		 end,
219
220    ClientPort = ssl_test_lib:portable_open_port(Exe, ClientArgs),
221
222    ssl_test_lib:check_result(Server, ExpectedSNIHostname),
223    ssl_test_lib:close_port(ClientPort),
224    ssl_test_lib:close(Server).
225%%--------------------------------------------------------------------
226%% Callback functions  ------------------------------------------------
227%%--------------------------------------------------------------------
228send_and_hostname(SSLSocket) ->
229    ssl:send(SSLSocket, "OK"),
230    case ssl:connection_information(SSLSocket, [sni_hostname]) of
231	{ok, []} ->
232	    undefined;
233	{ok, [{sni_hostname, Hostname}]} ->
234	    Hostname
235    end.
236%%--------------------------------------------------------------------
237%% Internal functions  -----------------------------------------------
238%%--------------------------------------------------------------------
239openssl_client_args(Version, Hostname, Port) ->
240    ssl_test_lib:maybe_force_ipv4(["s_client", "-connect", Hostname ++ ":" ++ integer_to_list(Port),
241                                   ssl_test_lib:version_flag(Version)]).
242
243openssl_client_args(Version, Hostname, Port, ServerName) ->
244    ssl_test_lib:maybe_force_ipv4(["s_client",  "-connect", Hostname ++ ":" ++
245                                       integer_to_list(Port),
246                                   ssl_test_lib:version_flag(Version), "-servername", ServerName]).
247
248check_openssl_sni_support(Config) ->
249    HelpText = os:cmd("openssl s_client --help"),
250    case ssl_test_lib:is_sane_oppenssl_client() of
251        true ->
252            case string:str(HelpText, "-servername") of
253                0 ->
254                    {skip, "Current openssl doesn't support SNI"};
255                _ ->
256                    Config
257            end;
258        false ->
259            {skip, "Current openssl doesn't support SNI or extension handling is flawed"}
260    end.
261