aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorQijiang Fan <[email protected]>2015-03-14 16:58:30 +0800
committerIngela Anderton Andin <[email protected]>2015-05-12 14:02:41 +0200
commit181ceb12675b59de9bd7a881fe9b58995d03bac0 (patch)
tree1a011bb037b41df46c3d92297796beee5486fd85
parent53ae813221c2dee502e2c114e68c2674c96b89cd (diff)
downloadotp-181ceb12675b59de9bd7a881fe9b58995d03bac0.tar.gz
otp-181ceb12675b59de9bd7a881fe9b58995d03bac0.tar.bz2
otp-181ceb12675b59de9bd7a881fe9b58995d03bac0.zip
ssl: add tests for SNI server support
This commit adds tests for SNI server support in: * ssl_sni_SUITE.erl * ssl_to_openssl_SUITE.erl And some more modifications: * make_certs also makes two certs for SNI, and adds extra options for SNI.
-rw-r--r--lib/ssl/test/Makefile1
-rw-r--r--lib/ssl/test/make_certs.erl2
-rw-r--r--lib/ssl/test/ssl_sni_SUITE.erl136
-rw-r--r--lib/ssl/test/ssl_test_lib.erl17
-rw-r--r--lib/ssl/test/ssl_to_openssl_SUITE.erl93
5 files changed, 244 insertions, 5 deletions
diff --git a/lib/ssl/test/Makefile b/lib/ssl/test/Makefile
index 8c45a788a4..886cc7726b 100644
--- a/lib/ssl/test/Makefile
+++ b/lib/ssl/test/Makefile
@@ -53,6 +53,7 @@ MODULES = \
ssl_to_openssl_SUITE \
ssl_ECC_SUITE \
ssl_upgrade_SUITE\
+ ssl_sni_SUITE \
make_certs\
erl_make_certs
diff --git a/lib/ssl/test/make_certs.erl b/lib/ssl/test/make_certs.erl
index 77631f62d3..4a193d48fe 100644
--- a/lib/ssl/test/make_certs.erl
+++ b/lib/ssl/test/make_certs.erl
@@ -81,7 +81,7 @@ all(DataDir, PrivDir, C = #config{}) ->
create_rnd(DataDir, PrivDir), % For all requests
rootCA(PrivDir, "erlangCA", C),
intermediateCA(PrivDir, "otpCA", "erlangCA", C),
- endusers(PrivDir, "otpCA", ["client", "server", "revoked"], C),
+ endusers(PrivDir, "otpCA", ["client", "server", "revoked", "a.server", "b.server"], C),
endusers(PrivDir, "erlangCA", ["localhost"], C),
%% Create keycert files
SDir = filename:join([PrivDir, "server"]),
diff --git a/lib/ssl/test/ssl_sni_SUITE.erl b/lib/ssl/test/ssl_sni_SUITE.erl
new file mode 100644
index 0000000000..134e508b10
--- /dev/null
+++ b/lib/ssl/test/ssl_sni_SUITE.erl
@@ -0,0 +1,136 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2008-2015. All Rights Reserved.
+%%
+%% The contents of this file are subject to the Erlang Public License,
+%% Version 1.1, (the "License"); you may not use this file except in
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved online at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%% the License for the specific language governing rights and limitations
+%% under the License.
+%%
+%% %CopyrightEnd%
+%%
+
+%%
+
+-module(ssl_sni_SUITE).
+
+-compile(export_all).
+
+-include_lib("common_test/include/ct.hrl").
+-include_lib("public_key/include/public_key.hrl").
+
+%%--------------------------------------------------------------------
+%% Common Test interface functions -----------------------------------
+%%--------------------------------------------------------------------
+suite() -> [{ct_hooks,[ts_install_cth]}].
+
+all() -> [no_sni_header, sni_match, sni_no_match].
+
+init_per_suite(Config0) ->
+ catch crypto:stop(),
+ try crypto:start() of
+ ok ->
+ ssl:start(),
+ Result =
+ (catch make_certs:all(?config(data_dir, Config0),
+ ?config(priv_dir, Config0))),
+ ct:log("Make certs ~p~n", [Result]),
+ ssl_test_lib:cert_options(Config0)
+ catch _:_ ->
+ {skip, "Crypto did not start"}
+ end.
+
+end_per_suite(_) ->
+ ssl:stop(),
+ application:stop(crypto).
+
+%%--------------------------------------------------------------------
+%% Test Cases --------------------------------------------------------
+%%--------------------------------------------------------------------
+no_sni_header(Config) ->
+ run_handshake(Config, undefined, undefined, "server").
+
+sni_match(Config) ->
+ run_handshake(Config, "a.server", "a.server", "a.server").
+
+sni_no_match(Config) ->
+ run_handshake(Config, "c.server", undefined, "server").
+
+
+
+%%--------------------------------------------------------------------
+%% Internal Functions ------------------------------------------------
+%%--------------------------------------------------------------------
+
+
+ssl_recv(SSLSocket, Expect) ->
+ ssl_recv(SSLSocket, "", Expect).
+
+ssl_recv(SSLSocket, CurrentData, ExpectedData) ->
+ receive
+ {ssl, SSLSocket, Data} ->
+ NeweData = CurrentData ++ Data,
+ case NeweData of
+ ExpectedData ->
+ ok;
+ _ ->
+ ssl_recv(SSLSocket, NeweData, ExpectedData)
+ end;
+ Other ->
+ ct:fail({unexpected_message, Other})
+ after 4000 ->
+ ct:fail({timeout, CurrentData, ExpectedData})
+ end.
+
+
+
+send_and_hostname(SSLSocket) ->
+ ssl:send(SSLSocket, "OK"),
+ {ok, [{sni_hostname, Hostname}]} = ssl:connection_information(SSLSocket, [sni_hostname]),
+ Hostname.
+
+rdnPart([[#'AttributeTypeAndValue'{type=Type, value=Value} | _] | _], Type) -> Value;
+rdnPart([_ | Tail], Type) -> rdnPart(Tail, Type);
+rdnPart([], _) -> unknown.
+
+rdn_to_string({utf8String, Binary}) ->
+ erlang:binary_to_list(Binary);
+rdn_to_string({printableString, String}) ->
+ String.
+
+recv_and_certificate(SSLSocket) ->
+ ssl_recv(SSLSocket, "OK"),
+ {ok, PeerCert} = ssl:peercert(SSLSocket),
+ #'OTPCertificate'{tbsCertificate = #'OTPTBSCertificate'{subject = {rdnSequence, Subject}}} = public_key:pkix_decode_cert(PeerCert, otp),
+ ct:log("Subject of certificate received from server: ~p", [Subject]),
+ rdn_to_string(rdnPart(Subject, ?'id-at-commonName')).
+
+
+run_handshake(Config, SNIHostname, ExpectedSNIHostname, ExpectedCN) ->
+ ct:log("Start running handshake, Config: ~p, SNIHostname: ~p, ExpectedSNIHostname: ~p, ExpectedCN: ~p", [Config, SNIHostname, ExpectedSNIHostname, ExpectedCN]),
+ ServerOptions = ?config(sni_server_opts, Config) ++ ?config(server_opts, Config),
+ ClientOptions =
+ case SNIHostname of
+ undefined ->
+ ?config(client_opts, Config);
+ _ ->
+ [{server_name_indication, SNIHostname}] ++ ?config(client_opts, Config)
+ end,
+ ct:log("Options: ~p", [[ServerOptions, ClientOptions]]),
+ {ClientNode, 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),
+ 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).
diff --git a/lib/ssl/test/ssl_test_lib.erl b/lib/ssl/test/ssl_test_lib.erl
index d19e3b7fdb..8b98e6f16b 100644
--- a/lib/ssl/test/ssl_test_lib.erl
+++ b/lib/ssl/test/ssl_test_lib.erl
@@ -354,6 +354,11 @@ cert_options(Config) ->
BadKeyFile = filename:join([?config(priv_dir, Config),
"badkey.pem"]),
PskSharedSecret = <<1,2,3,4,5,6,7,8,9,10,11,12,13,14,15>>,
+
+ SNIServerACertFile = filename:join([?config(priv_dir, Config), "a.server", "cert.pem"]),
+ SNIServerAKeyFile = filename:join([?config(priv_dir, Config), "a.server", "key.pem"]),
+ SNIServerBCertFile = filename:join([?config(priv_dir, Config), "b.server", "cert.pem"]),
+ SNIServerBKeyFile = filename:join([?config(priv_dir, Config), "b.server", "key.pem"]),
[{client_opts, [{ssl_imp, new},{reuseaddr, true}]},
{client_verification_opts, [{cacertfile, ClientCaCertFile},
{certfile, ClientCertFile},
@@ -414,7 +419,17 @@ cert_options(Config) ->
{server_bad_cert, [{ssl_imp, new},{cacertfile, ServerCaCertFile},
{certfile, BadCertFile}, {keyfile, ServerKeyFile}]},
{server_bad_key, [{ssl_imp, new},{cacertfile, ServerCaCertFile},
- {certfile, ServerCertFile}, {keyfile, BadKeyFile}]}
+ {certfile, ServerCertFile}, {keyfile, BadKeyFile}]},
+ {sni_server_opts, [{sni_hosts, [
+ {"a.server", [
+ {certfile, SNIServerACertFile},
+ {keyfile, SNIServerAKeyFile}
+ ]},
+ {"b.server", [
+ {certfile, SNIServerBCertFile},
+ {keyfile, SNIServerBKeyFile}
+ ]}
+ ]}]}
| Config].
diff --git a/lib/ssl/test/ssl_to_openssl_SUITE.erl b/lib/ssl/test/ssl_to_openssl_SUITE.erl
index 94426a3061..3807a9983c 100644
--- a/lib/ssl/test/ssl_to_openssl_SUITE.erl
+++ b/lib/ssl/test/ssl_to_openssl_SUITE.erl
@@ -50,9 +50,9 @@ all() ->
groups() ->
[{basic, [], basic_tests()},
- {'tlsv1.2', [], all_versions_tests() ++ alpn_tests() ++ npn_tests()},
- {'tlsv1.1', [], all_versions_tests() ++ alpn_tests() ++ npn_tests()},
- {'tlsv1', [], all_versions_tests()++ alpn_tests() ++ npn_tests()},
+ {'tlsv1.2', [], all_versions_tests() ++ alpn_tests() ++ npn_tests() ++ sni_server_tests()},
+ {'tlsv1.1', [], all_versions_tests() ++ alpn_tests() ++ npn_tests() ++ sni_server_tests()},
+ {'tlsv1', [], all_versions_tests()++ alpn_tests() ++ npn_tests() ++ sni_server_tests()},
{'sslv3', [], all_versions_tests()}].
basic_tests() ->
@@ -101,6 +101,11 @@ npn_tests() ->
erlang_client_openssl_server_npn_only_client,
erlang_client_openssl_server_npn_only_server].
+sni_server_tests() ->
+ [erlang_server_oepnssl_client_sni_match,
+ erlang_server_openssl_client_sni_no_match,
+ erlang_server_openssl_client_sni_no_header].
+
init_per_suite(Config0) ->
Dog = ct:timetrap(?LONG_TIMEOUT *2),
@@ -222,6 +227,12 @@ special_init(TestCase, Config)
check_openssl_npn_support(Config)
end;
+special_init(TestCase, Config)
+ when TestCase == erlang_server_openssl_client_sni_match;
+ TestCase == erlang_server_openssl_client_sni_no_match;
+ TestCase == erlang_server_openssl_client_sni_no_header ->
+ check_openssl_sni_support(Config);
+
special_init(_, Config) ->
Config.
@@ -1181,6 +1192,16 @@ erlang_server_openssl_client_npn_only_client(Config) when is_list(Config) ->
ssl_test_lib:check_result(Server, ok)
end),
ok.
+%--------------------------------------------------------------------------
+erlang_server_openssl_client_sni_no_header(Config) when is_list(Config) ->
+ erlang_server_openssl_client_sni_test(Config, undefined, undefined, "server").
+
+erlang_server_openssl_client_sni_match(Config) when is_list(Config) ->
+ erlang_server_openssl_client_sni_test(Config, "a.server", "a.server", "a.server").
+
+erlang_server_openssl_client_sni_no_match(Config) when is_list(Config) ->
+ erlang_server_openssl_client_sni_test(Config, "c.server", undefined, "server").
+
%%--------------------------------------------------------------------
%% Internal functions ------------------------------------------------
@@ -1207,6 +1228,64 @@ run_suites(Ciphers, Version, Config, Type) ->
ct:fail(cipher_suite_failed_see_test_case_log)
end.
+client_read_check([], _NewData) -> ok;
+client_read_check([Hd | T], NewData) ->
+ case binary:match(NewData, list_to_binary(Hd)) of
+ nomatch ->
+ nomatch;
+ _ ->
+ client_read_check(T, NewData)
+ end.
+client_read_bulk(Port, DataExpected, DataReceived) ->
+ receive
+ {Port, {data, TheData}} ->
+ Data = list_to_binary(TheData),
+ NewData = <<DataReceived/binary, Data/binary>>,
+ ct:log("New Data: ~p", [NewData]),
+ case client_read_check(DataExpected, NewData) of
+ ok ->
+ ok;
+ _ ->
+ client_read_bulk(Port, DataExpected, NewData)
+ end;
+ _ ->
+ ct:fail("unexpected_message")
+ after 4000 ->
+ ct:fail("timeout")
+ end.
+client_read_bulk(Port, DataExpected) ->
+ client_read_bulk(Port, DataExpected, <<"">>).
+
+send_and_hostname(SSLSocket) ->
+ ssl:send(SSLSocket, "OK"),
+ {ok, [{sni_hostname, Hostname}]} = ssl:connection_information(SSLSocket, [sni_hostname]),
+ Hostname.
+
+erlang_server_openssl_client_sni_test(Config, SNIHostname, ExpectedSNIHostname, ExpectedCN) ->
+ ct:log("Start running handshake, Config: ~p, SNIHostname: ~p, ExpectedSNIHostname: ~p, ExpectedCN: ~p", [Config, SNIHostname, ExpectedSNIHostname, ExpectedCN]),
+ ServerOptions = ?config(sni_server_opts, Config) ++ ?config(server_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),
+ ClientCommand = case SNIHostname of
+ undefined ->
+ "openssl s_client -connect " ++ Hostname ++ ":" ++ integer_to_list(Port);
+ _ ->
+ "openssl s_client -connect " ++ Hostname ++ ":" ++ integer_to_list(Port) ++ " -servername " ++ SNIHostname
+ end,
+ ct:log("Options: ~p", [[ServerOptions, ClientCommand]]),
+ ClientPort = open_port({spawn, ClientCommand}, [stderr_to_stdout]),
+ ssl_test_lib:check_result(Server, ExpectedSNIHostname),
+ ExpectedClientOutput = ["OK", "/CN=" ++ ExpectedCN ++ "/"],
+ ok = client_read_bulk(ClientPort, ExpectedClientOutput),
+ ssl_test_lib:close_port(ClientPort),
+ ssl_test_lib:close(Server),
+ ok.
+
+
+
cipher(CipherSuite, Version, Config, ClientOpts, ServerOpts) ->
process_flag(trap_exit, true),
ct:log("Testing CipherSuite ~p~n", [CipherSuite]),
@@ -1588,6 +1667,14 @@ server_sent_garbage(Socket) ->
end.
+check_openssl_sni_support(Config) ->
+ HelpText = os:cmd("openssl s_client --help"),
+ case string:str(HelpText, "-servername") of
+ 0 ->
+ {skip, "Current openssl doesn't support SNI"};
+ _ ->
+ Config
+ end.
check_openssl_npn_support(Config) ->
HelpText = os:cmd("openssl s_client --help"),