aboutsummaryrefslogtreecommitdiffstats
path: root/lib/ssl/test
diff options
context:
space:
mode:
authorBen Murphy <[email protected]>2011-12-11 18:10:55 +0000
committerIngela Anderton Andin <[email protected]>2012-09-20 14:37:03 +0200
commita2ee149615fc551e82d7d7df5126962e96dcab74 (patch)
tree51c7042f9fb3afeedc905aa017bff988af71fd11 /lib/ssl/test
parentb58ce62a0cf296ca8cd8cd77b053bc70ecf28758 (diff)
downloadotp-a2ee149615fc551e82d7d7df5126962e96dcab74.tar.gz
otp-a2ee149615fc551e82d7d7df5126962e96dcab74.tar.bz2
otp-a2ee149615fc551e82d7d7df5126962e96dcab74.zip
ssl: Support for SSL Next Protocol Negotiation
* http://technotes.googlecode.com/git/nextprotoneg.html
Diffstat (limited to 'lib/ssl/test')
-rw-r--r--lib/ssl/test/Makefile2
-rw-r--r--lib/ssl/test/make_certs.erl19
-rw-r--r--lib/ssl/test/ssl_npn_handshake_SUITE.erl218
-rw-r--r--lib/ssl/test/ssl_npn_hello_SUITE.erl115
-rw-r--r--lib/ssl/test/ssl_to_openssl_SUITE.erl271
5 files changed, 620 insertions, 5 deletions
diff --git a/lib/ssl/test/Makefile b/lib/ssl/test/Makefile
index 343157b22e..d36dcb588b 100644
--- a/lib/ssl/test/Makefile
+++ b/lib/ssl/test/Makefile
@@ -44,6 +44,8 @@ MODULES = \
ssl_to_openssl_SUITE \
ssl_session_cache_SUITE \
ssl_dist_SUITE \
+ ssl_npn_hello_SUITE \
+ ssl_npn_handshake_SUITE \
make_certs\
erl_make_certs
diff --git a/lib/ssl/test/make_certs.erl b/lib/ssl/test/make_certs.erl
index 693289990c..4603a9f846 100644
--- a/lib/ssl/test/make_certs.erl
+++ b/lib/ssl/test/make_certs.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2007-2010. All Rights Reserved.
+%% Copyright Ericsson AB 2007-2012. 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
@@ -121,7 +121,19 @@ create_self_signed_cert(Root, OpenSSLCmd, CAName, Cnf) ->
" -keyout ", KeyFile,
" -out ", CertFile],
Env = [{"ROOTDIR", Root}],
- cmd(Cmd, Env).
+ cmd(Cmd, Env),
+ fix_key_file(OpenSSLCmd, KeyFile).
+
+% openssl 1.0 generates key files in pkcs8 format by default and we don't handle this format
+fix_key_file(OpenSSLCmd, KeyFile) ->
+ KeyFileTmp = KeyFile ++ ".tmp",
+ Cmd = [OpenSSLCmd, " rsa",
+ " -in ",
+ KeyFile,
+ " -out ",
+ KeyFileTmp],
+ cmd(Cmd, []),
+ ok = file:rename(KeyFileTmp, KeyFile).
create_ca_dir(Root, CAName, Cnf) ->
CARoot = filename:join([Root, CAName]),
@@ -139,7 +151,8 @@ create_req(Root, OpenSSLCmd, CnfFile, KeyFile, ReqFile) ->
" -keyout ", KeyFile,
" -out ", ReqFile],
Env = [{"ROOTDIR", Root}],
- cmd(Cmd, Env).
+ cmd(Cmd, Env),
+ fix_key_file(OpenSSLCmd, KeyFile).
sign_req(Root, OpenSSLCmd, CA, CertType, ReqFile, CertFile) ->
CACnfFile = filename:join([Root, CA, "ca.cnf"]),
diff --git a/lib/ssl/test/ssl_npn_handshake_SUITE.erl b/lib/ssl/test/ssl_npn_handshake_SUITE.erl
new file mode 100644
index 0000000000..f2327756c3
--- /dev/null
+++ b/lib/ssl/test/ssl_npn_handshake_SUITE.erl
@@ -0,0 +1,218 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2008-2012. 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_npn_handshake_SUITE).
+
+%% Note: This directive should only be used in test suites.
+-compile(export_all).
+-include_lib("common_test/include/ct.hrl").
+
+suite() -> [{ct_hooks,[ts_install_cth]}].
+
+init_per_suite(Config) ->
+ try crypto:start() of
+ ok ->
+ application:start(public_key),
+ ssl:start(),
+ Result =
+ (catch make_certs:all(?config(data_dir, Config),
+ ?config(priv_dir, Config))),
+ test_server:format("Make certs ~p~n", [Result]),
+ ssl_test_lib:cert_options(Config)
+ catch _:_ ->
+ {skip, "Crypto did not start"}
+ end.
+
+end_per_suite(_Config) ->
+ ssl:stop(),
+ application:stop(crypto).
+
+
+all() ->
+ [validate_empty_protocols_are_not_allowed_test,
+ validate_empty_advertisement_list_is_allowed_test,
+ validate_advertisement_must_be_a_binary_list_test,
+ validate_client_protocols_must_be_a_tuple_test,
+ perform_normal_npn_handshake_server_preference_test,
+ perform_normal_npn_handshake_client_preference_test,
+ perform_fallback_npn_handshake_test,
+ perform_fallback_npn_handshake_server_preference_test,
+ perform_client_tries_to_negotiate_but_server_does_not_support_test,
+ perform_client_does_not_try_to_negotiate_but_server_supports_npn_test,
+ perform_renegotiate_from_client_after_npn_handshake].
+
+connection_info_result(Socket) ->
+ ssl:connection_info(Socket).
+
+validate_empty_protocols_are_not_allowed_test(_Config) ->
+ {error, {eoptions, {next_protocols_advertised, <<>>}}} = (catch ssl:listen(9443, [{next_protocols_advertised, [<<"foo/1">>, <<"">>]}])),
+ {error, {eoptions, {client_preferred_next_protocols, <<>>}}} = (catch ssl:connect({127,0,0,1}, 9443, [{client_preferred_next_protocols, {<<"foox/1">>, client, [<<"foo/1">>, <<"">>]}}], infinity)),
+ Option = {client_preferred_next_protocols, {<<"">>, client, [<<"foo/1">>, <<"blah/1">>]}},
+ {error, {eoptions, Option}} = (catch ssl:connect({127,0,0,1}, 9443, [Option], infinity)).
+
+validate_empty_advertisement_list_is_allowed_test(_Config) ->
+ Option = {next_protocols_advertised, []},
+ {ok, Socket} = ssl:listen(0, [Option]),
+ ssl:close(Socket).
+
+validate_advertisement_must_be_a_binary_list_test(_Config) ->
+ Option = {next_protocols_advertised, blah},
+ {error, {eoptions, Option}} = (catch ssl:listen(9443, [Option])).
+
+validate_client_protocols_must_be_a_tuple_test(_Config) ->
+ Option = {client_preferred_next_protocols, [<<"foo/1">>]},
+ {error, {eoptions, Option}} = (catch ssl:connect({127,0,0,1}, 9443, [Option])).
+
+
+
+perform_client_does_not_try_to_negotiate_but_server_supports_npn_test(Config) ->
+ run_npn_handshake_test(Config,
+ [],
+ [{next_protocols_advertised, [<<"spdy/1">>, <<"http/1.1">>, <<"http/1.0">>]}],
+ {error, next_protocol_not_negotiated}).
+
+perform_client_tries_to_negotiate_but_server_does_not_support_test(Config) ->
+ run_npn_handshake_test(Config,
+ [{client_preferred_next_protocols, {<<"http/1.1">>, client, [<<"spdy/2">>]}}],
+ [],
+ {error, next_protocol_not_negotiated}).
+
+perform_fallback_npn_handshake_test(Config) ->
+ run_npn_handshake_test(Config,
+ [{client_preferred_next_protocols, {<<"http/1.1">>, client, [<<"spdy/2">>]}}],
+ [{next_protocols_advertised, [<<"spdy/1">>, <<"http/1.1">>, <<"http/1.0">>]}],
+ {ok, <<"http/1.1">>}).
+
+perform_fallback_npn_handshake_server_preference_test(Config) ->
+ run_npn_handshake_test(Config,
+ [{client_preferred_next_protocols, {<<"http/1.1">>, server, [<<"spdy/2">>]}}],
+ [{next_protocols_advertised, [<<"spdy/1">>, <<"http/1.1">>, <<"http/1.0">>]}],
+ {ok, <<"http/1.1">>}).
+
+
+perform_normal_npn_handshake_client_preference_test(Config) ->
+ run_npn_handshake_test(Config,
+ [{client_preferred_next_protocols, {<<"http/1.1">>, client, [<<"http/1.0">>, <<"http/1.1">>]}}],
+ [{next_protocols_advertised, [<<"spdy/2">>, <<"http/1.1">>, <<"http/1.0">>]}],
+ {ok, <<"http/1.0">>}).
+
+perform_normal_npn_handshake_server_preference_test(Config) ->
+ run_npn_handshake_test(Config,
+ [{client_preferred_next_protocols, {<<"http/1.1">>, server, [<<"http/1.0">>, <<"http/1.1">>]}}],
+ [{next_protocols_advertised, [<<"spdy/2">>, <<"http/1.1">>, <<"http/1.0">>]}],
+ {ok, <<"http/1.1">>}).
+
+
+perform_renegotiate_from_client_after_npn_handshake(Config) ->
+ Data = "hello world",
+
+ ClientOpts0 = ?config(client_opts, Config),
+ ClientOpts = [{client_preferred_next_protocols, {<<"http/1.1">>, client, [<<"http/1.0">>]}}] ++ ClientOpts0,
+ ServerOpts0 = ?config(server_opts, Config),
+ ServerOpts = [{next_protocols_advertised, [<<"spdy/2">>, <<"http/1.1">>, <<"http/1.0">>]}] ++ ServerOpts0,
+ ExpectedProtocol = {ok, <<"http/1.0">>},
+
+ {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config),
+ Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0},
+ {from, self()},
+ {mfa, {?MODULE, ssl_receive_and_assert_npn, [ExpectedProtocol, Data]}},
+ {options, ServerOpts}]),
+
+ Port = ssl_test_lib:inet_port(Server),
+ Client = ssl_test_lib:start_client([{node, ClientNode}, {port, Port},
+ {host, Hostname},
+ {from, self()},
+ {mfa, {?MODULE, assert_npn_and_renegotiate_and_send_data, [ExpectedProtocol, Data]}},
+ {options, ClientOpts}]),
+
+ ssl_test_lib:check_result(Server, ok, Client, ok).
+
+%--------------------------------------------------------------------------------
+
+run_npn_handshake_test(Config, ClientExtraOpts, ServerExtraOpts, ExpectedProtocol) ->
+ Data = "hello world",
+
+ ClientOpts0 = ?config(client_opts, Config),
+ ClientOpts = ClientExtraOpts ++ ClientOpts0,
+ ServerOpts0 = ?config(server_opts, Config),
+ ServerOpts = ServerExtraOpts ++ ServerOpts0,
+
+ {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config),
+ Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0},
+ {from, self()},
+ {mfa, {?MODULE, ssl_receive_and_assert_npn, [ExpectedProtocol, Data]}},
+ {options, ServerOpts}]),
+
+ Port = ssl_test_lib:inet_port(Server),
+ Client = ssl_test_lib:start_client([{node, ClientNode}, {port, Port},
+ {host, Hostname},
+ {from, self()},
+ {mfa, {?MODULE, ssl_send_and_assert_npn, [ExpectedProtocol, Data]}},
+ {options, ClientOpts}]),
+
+ ssl_test_lib:check_result(Server, ok, Client, ok).
+
+assert_npn(Socket, Protocol) ->
+ test_server:format("Negotiated Protocol ~p, Expecting: ~p ~n", [ssl:negotiated_next_protocol(Socket), Protocol]),
+ Protocol = ssl:negotiated_next_protocol(Socket).
+
+assert_npn_and_renegotiate_and_send_data(Socket, Protocol, Data) ->
+ assert_npn(Socket, Protocol),
+ test_server:format("Renegotiating ~n", []),
+ ok = ssl:renegotiate(Socket),
+ ssl:send(Socket, Data),
+ assert_npn(Socket, Protocol),
+ ok.
+
+ssl_send_and_assert_npn(Socket, Protocol, Data) ->
+ assert_npn(Socket, Protocol),
+ ssl_send(Socket, Data).
+
+ssl_receive_and_assert_npn(Socket, Protocol, Data) ->
+ assert_npn(Socket, Protocol),
+ ssl_receive(Socket, Data).
+
+ssl_send(Socket, Data) ->
+ test_server:format("Connection info: ~p~n",
+ [ssl:connection_info(Socket)]),
+ ssl:send(Socket, Data).
+
+ssl_receive(Socket, Data) ->
+ ssl_receive(Socket, Data, []).
+
+ssl_receive(Socket, Data, Buffer) ->
+ test_server:format("Connection info: ~p~n",
+ [ssl:connection_info(Socket)]),
+ receive
+ {ssl, Socket, MoreData} ->
+ test_server:format("Received ~p~n",[MoreData]),
+ NewBuffer = Buffer ++ MoreData,
+ case NewBuffer of
+ Data ->
+ ssl:send(Socket, "Got it"),
+ ok;
+ _ ->
+ ssl_receive(Socket, Data, NewBuffer)
+ end;
+ Other ->
+ test_server:fail({unexpected_message, Other})
+ after 4000 ->
+ test_server:fail({did_not_get, Data})
+ end.
diff --git a/lib/ssl/test/ssl_npn_hello_SUITE.erl b/lib/ssl/test/ssl_npn_hello_SUITE.erl
new file mode 100644
index 0000000000..f177778178
--- /dev/null
+++ b/lib/ssl/test/ssl_npn_hello_SUITE.erl
@@ -0,0 +1,115 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2008-2012. 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_npn_hello_SUITE).
+
+%% Note: This directive should only be used in test suites.
+-compile(export_all).
+-include("ssl_handshake.hrl").
+-include("ssl_record.hrl").
+-include_lib("common_test/include/ct.hrl").
+
+suite() -> [{ct_hooks,[ts_install_cth]}].
+
+all() ->
+ [encode_and_decode_npn_client_hello_test,
+ encode_and_decode_npn_server_hello_test,
+ encode_and_decode_client_hello_test,
+ encode_and_decode_server_hello_test,
+ create_server_hello_with_advertised_protocols_test,
+ create_server_hello_with_no_advertised_protocols_test].
+
+
+create_client_handshake(Npn) ->
+ ssl_handshake:encode_handshake(#client_hello{
+ client_version = {1, 2},
+ random = <<1:256>>,
+ session_id = <<>>,
+ cipher_suites = "",
+ compression_methods = "",
+ next_protocol_negotiation = Npn,
+ renegotiation_info = #renegotiation_info{}
+ }, vsn).
+
+
+encode_and_decode_client_hello_test(_Config) ->
+ HandShakeData = create_client_handshake(undefined),
+ Version = ssl_record:protocol_version(ssl_record:highest_protocol_version([])),
+ {[{DecodedHandshakeMessage, _Raw}], _} = ssl_handshake:get_tls_handshake(Version, list_to_binary(HandShakeData), <<>>),
+ NextProtocolNegotiation = DecodedHandshakeMessage#client_hello.next_protocol_negotiation,
+ NextProtocolNegotiation = undefined.
+
+encode_and_decode_npn_client_hello_test(_Config) ->
+ HandShakeData = create_client_handshake(#next_protocol_negotiation{extension_data = <<>>}),
+ Version = ssl_record:protocol_version(ssl_record:highest_protocol_version([])),
+ {[{DecodedHandshakeMessage, _Raw}], _} = ssl_handshake:get_tls_handshake(Version� list_to_binary(HandShakeData), <<>>),
+ NextProtocolNegotiation = DecodedHandshakeMessage#client_hello.next_protocol_negotiation,
+ NextProtocolNegotiation = #next_protocol_negotiation{extension_data = <<>>}.
+
+create_server_handshake(Npn) ->
+ ssl_handshake:encode_handshake(#server_hello{
+ server_version = {1, 2},
+ random = <<1:256>>,
+ session_id = <<>>,
+ cipher_suite = <<1,2>>,
+ compression_method = 1,
+ next_protocol_negotiation = Npn,
+ renegotiation_info = #renegotiation_info{}
+ }, vsn).
+
+encode_and_decode_server_hello_test(_Config) ->
+ HandShakeData = create_server_handshake(undefined),
+ Version = ssl_record:protocol_version(ssl_record:highest_protocol_version([])),
+ {[{DecodedHandshakeMessage, _Raw}], _} =
+ ssl_handshake:get_tls_handshake(Version, list_to_binary(HandShakeData), <<>>),
+ NextProtocolNegotiation = DecodedHandshakeMessage#server_hello.next_protocol_negotiation,
+ NextProtocolNegotiation = undefined.
+
+encode_and_decode_npn_server_hello_test(_Config) ->
+ HandShakeData = create_server_handshake(#next_protocol_negotiation{extension_data = <<6, "spdy/2">>}),
+ Version = ssl_record:protocol_version(ssl_record:highest_protocol_version([])),
+ {[{DecodedHandshakeMessage, _Raw}], _} = ssl_handshake:get_tls_handshake(Version, list_to_binary(HandShakeData), <<>>),
+ NextProtocolNegotiation = DecodedHandshakeMessage#server_hello.next_protocol_negotiation,
+ ct:print("~p ~n", [NextProtocolNegotiation]),
+ NextProtocolNegotiation = #next_protocol_negotiation{extension_data = <<6, "spdy/2">>}.
+
+create_connection_states() ->
+ #connection_states{
+ pending_read = #connection_state{
+ security_parameters = #security_parameters{
+ server_random = <<1:256>>,
+ compression_algorithm = 1,
+ cipher_suite = <<1, 2>>
+ }
+ },
+
+ current_read = #connection_state {
+ secure_renegotiation = false
+ }
+ }.
+
+create_server_hello_with_no_advertised_protocols_test(_Config) ->
+ Hello = ssl_handshake:server_hello(<<>>, {3, 0}, create_connection_states(), false, undefined),
+ undefined = Hello#server_hello.next_protocol_negotiation.
+
+create_server_hello_with_advertised_protocols_test(_Config) ->
+ Hello = ssl_handshake:server_hello(<<>>, {3, 0}, create_connection_states(), false, [<<"spdy/1">>, <<"http/1.0">>, <<"http/1.1">>]),
+ #next_protocol_negotiation{extension_data = <<6, "spdy/1", 8, "http/1.0", 8, "http/1.1">>} = Hello#server_hello.next_protocol_negotiation.
diff --git a/lib/ssl/test/ssl_to_openssl_SUITE.erl b/lib/ssl/test/ssl_to_openssl_SUITE.erl
index d446014f7b..cc3c6439ac 100644
--- a/lib/ssl/test/ssl_to_openssl_SUITE.erl
+++ b/lib/ssl/test/ssl_to_openssl_SUITE.erl
@@ -114,6 +114,17 @@ special_init(TestCase, Config)
special_init(ssl2_erlang_server_openssl_client, Config) ->
check_sane_openssl_sslv2(Config);
+special_init(TestCase, Config)
+ when TestCase == erlang_client_openssl_server_npn_negotiation;
+ TestCase == erlang_server_openssl_client_npn_negotiation;
+ TestCase == erlang_server_openssl_client_npn_negotiation_and_renegotiate;
+ TestCase == erlang_client_openssl_server_npn_negotiation_and_renegotiate;
+ TestCase == erlang_server_openssl_client_npn_negotiate_only_on_server;
+ TestCase == erlang_server_openssl_client_npn_negotiate_only_on_client;
+ TestCase == erlang_client_openssl_server_npn_negotiate_only_on_client;
+ TestCase == erlang_client_openssl_server_npn_negotiate_only_on_server ->
+ check_openssl_npn_support(Config);
+
special_init(_, Config) ->
Config.
@@ -187,8 +198,16 @@ all_versions_tests() ->
ciphers_rsa_signed_certs,
ciphers_dsa_signed_certs,
erlang_client_bad_openssl_server,
- ssl2_erlang_server_openssl_client
- ].
+ expired_session,
+ ssl2_erlang_server_openssl_client,
+ erlang_client_openssl_server_npn_negotiation,
+ erlang_server_openssl_client_npn_negotiation,
+ erlang_server_openssl_client_npn_negotiation_and_renegotiate,
+ erlang_client_openssl_server_npn_negotiation_and_renegotiate,
+ erlang_server_openssl_client_npn_negotiate_only_on_client,
+ erlang_server_openssl_client_npn_negotiate_only_on_server,
+ erlang_client_openssl_server_npn_negotiate_only_on_client,
+ erlang_client_openssl_server_npn_negotiate_only_on_server].
init_per_group(GroupName, Config) ->
case ssl_test_lib:is_tls_version(GroupName) of
@@ -1069,6 +1088,245 @@ ssl2_erlang_server_openssl_client(Config) when is_list(Config) ->
ok.
%%--------------------------------------------------------------------
+erlang_client_openssl_server_npn_negotiation(doc) ->
+ ["Test erlang client with openssl server doing npn negotiation"];
+erlang_client_openssl_server_npn_negotiation(suite) ->
+ [];
+erlang_client_openssl_server_npn_negotiation(Config) when is_list(Config) ->
+ Data = "From openssl to erlang",
+ start_erlang_client_and_openssl_server_for_npn_negotiation(Config, Data, fun(Client, OpensslPort) ->
+ port_command(OpensslPort, Data),
+
+ ssl_test_lib:check_result(Client, ok)
+ end),
+
+ ok.
+
+
+%%--------------------------------------------------------------------
+erlang_client_openssl_server_npn_negotiation_and_renegotiate(doc) ->
+ ["Test erlang client with openssl server doing npn negotiation and renegotiate"];
+erlang_client_openssl_server_npn_negotiation_and_renegotiate(suite) ->
+ [];
+erlang_client_openssl_server_npn_negotiation_and_renegotiate(Config) when is_list(Config) ->
+ Data = "From openssl to erlang",
+ start_erlang_client_and_openssl_server_for_npn_negotiation(Config, Data, fun(Client, OpensslPort) ->
+ port_command(OpensslPort, ?OPENSSL_RENEGOTIATE),
+ test_server:sleep(?SLEEP),
+ port_command(OpensslPort, Data),
+ ssl_test_lib:check_result(Client, ok)
+ end),
+ ok.
+
+
+%%--------------------------------------------------------------------------
+
+
+erlang_server_openssl_client_npn_negotiation(doc) ->
+ ["Test erlang server with openssl client and npn negotiation"];
+erlang_server_openssl_client_npn_negotiation(suite) ->
+ [];
+erlang_server_openssl_client_npn_negotiation(Config) when is_list(Config) ->
+
+ Data = "From openssl to erlang",
+ start_erlang_server_and_openssl_client_for_npn_negotiation(Config, Data, fun(Server, OpensslPort) ->
+ port_command(OpensslPort, Data),
+ ssl_test_lib:check_result(Server, ok)
+ end),
+ ok.
+
+%%--------------------------------------------------------------------------
+
+erlang_server_openssl_client_npn_negotiation_and_renegotiate(doc) ->
+ ["Test erlang server with openssl client and npn negotiation with renegotiation"];
+erlang_server_openssl_client_npn_negotiation_and_renegotiate(suite) ->
+ [];
+erlang_server_openssl_client_npn_negotiation_and_renegotiate(Config) when is_list(Config) ->
+ Data = "From openssl to erlang",
+ start_erlang_server_and_openssl_client_for_npn_negotiation(Config, Data, fun(Server, OpensslPort) ->
+ port_command(OpensslPort, ?OPENSSL_RENEGOTIATE),
+ test_server:sleep(?SLEEP),
+ port_command(OpensslPort, Data),
+ ssl_test_lib:check_result(Server, ok)
+ end),
+ ok.
+%%--------------------------------------------------------------------------
+
+erlang_client_openssl_server_npn_negotiate_only_on_server(Config) when is_list(Config) ->
+ Data = "From openssl to erlang",
+ start_erlang_client_and_openssl_server_with_opts(Config, [], "-nextprotoneg spdy/2", Data, fun(Server, OpensslPort) ->
+ port_command(OpensslPort, Data),
+ ssl_test_lib:check_result(Server, ok)
+ end),
+ ok.
+
+%%--------------------------------------------------------------------------
+
+erlang_client_openssl_server_npn_negotiate_only_on_client(Config) when is_list(Config) ->
+ Data = "From openssl to erlang",
+ start_erlang_client_and_openssl_server_with_opts(Config, [{client_preferred_next_protocols, {<<"http/1.1">>, client, [<<"spdy/2">>]}}], "", Data, fun(Server, OpensslPort) ->
+ port_command(OpensslPort, Data),
+ ssl_test_lib:check_result(Server, ok)
+ end),
+ ok.
+
+%%--------------------------------------------------------------------------
+erlang_server_openssl_client_npn_negotiate_only_on_server(Config) when is_list(Config) ->
+ Data = "From openssl to erlang",
+ start_erlang_server_and_openssl_client_with_opts(Config, [{next_protocols_advertised, [<<"spdy/2">>]}], "", Data, fun(Server, OpensslPort) ->
+ port_command(OpensslPort, Data),
+ ssl_test_lib:check_result(Server, ok)
+ end),
+ ok.
+
+erlang_server_openssl_client_npn_negotiate_only_on_client(Config) when is_list(Config) ->
+ Data = "From openssl to erlang",
+ start_erlang_server_and_openssl_client_with_opts(Config, [], "-nextprotoneg spdy/2", Data, fun(Server, OpensslPort) ->
+ port_command(OpensslPort, Data),
+ ssl_test_lib:check_result(Server, ok)
+ end),
+ ok.
+
+%%--------------------------------------------------------------------------
+
+start_erlang_client_and_openssl_server_with_opts(Config, ErlangClientOpts, OpensslServerOpts, Data, Callback) ->
+ process_flag(trap_exit, true),
+ ServerOpts = ?config(server_opts, Config),
+ ClientOpts0 = ?config(client_opts, Config),
+ ClientOpts = ErlangClientOpts ++ ClientOpts0,
+
+ {ClientNode, _, Hostname} = ssl_test_lib:run_where(Config),
+
+ Data = "From openssl to erlang",
+
+ Port = ssl_test_lib:inet_port(node()),
+ CertFile = proplists:get_value(certfile, ServerOpts),
+ KeyFile = proplists:get_value(keyfile, ServerOpts),
+
+ Cmd = "openssl s_server " ++ OpensslServerOpts ++ " -accept " ++ integer_to_list(Port) ++
+ " -cert " ++ CertFile ++ " -key " ++ KeyFile,
+
+ test_server:format("openssl cmd: ~p~n", [Cmd]),
+
+ OpensslPort = open_port({spawn, Cmd}, [stderr_to_stdout]),
+
+ wait_for_openssl_server(),
+
+ Client = ssl_test_lib:start_client([{node, ClientNode}, {port, Port},
+ {host, Hostname},
+ {from, self()},
+ {mfa, {?MODULE,
+ erlang_ssl_receive, [Data]}},
+ {options, ClientOpts}]),
+
+ Callback(Client, OpensslPort),
+
+ %% Clean close down! Server needs to be closed first !!
+ close_port(OpensslPort),
+
+ ssl_test_lib:close(Client),
+ process_flag(trap_exit, false).
+
+start_erlang_client_and_openssl_server_for_npn_negotiation(Config, Data, Callback) ->
+ process_flag(trap_exit, true),
+ ServerOpts = ?config(server_opts, Config),
+ ClientOpts0 = ?config(client_opts, Config),
+ ClientOpts = [{client_preferred_next_protocols, {<<"http/1.1">>, client, [<<"spdy/2">>]}} | ClientOpts0],
+
+ {ClientNode, _, Hostname} = ssl_test_lib:run_where(Config),
+
+ Data = "From openssl to erlang",
+
+ Port = ssl_test_lib:inet_port(node()),
+ CertFile = proplists:get_value(certfile, ServerOpts),
+ KeyFile = proplists:get_value(keyfile, ServerOpts),
+
+ Cmd = "openssl s_server -msg -nextprotoneg http/1.1,spdy/2 -accept " ++ integer_to_list(Port) ++
+ " -cert " ++ CertFile ++ " -key " ++ KeyFile,
+
+ test_server:format("openssl cmd: ~p~n", [Cmd]),
+
+ OpensslPort = open_port({spawn, Cmd}, [stderr_to_stdout]),
+
+ wait_for_openssl_server(),
+
+ Client = ssl_test_lib:start_client([{node, ClientNode}, {port, Port},
+ {host, Hostname},
+ {from, self()},
+ {mfa, {?MODULE,
+ erlang_ssl_receive_and_assert_npn, [<<"spdy/2">>, Data]}},
+ {options, ClientOpts}]),
+
+ Callback(Client, OpensslPort),
+
+ %% Clean close down! Server needs to be closed first !!
+ close_port(OpensslPort),
+
+ ssl_test_lib:close(Client),
+ process_flag(trap_exit, false).
+
+start_erlang_server_and_openssl_client_for_npn_negotiation(Config, Data, Callback) ->
+ process_flag(trap_exit, true),
+ ServerOpts0 = ?config(server_opts, Config),
+ ServerOpts = [{next_protocols_advertised, [<<"spdy/2">>]}, ServerOpts0],
+
+ {_, ServerNode, _} = ssl_test_lib:run_where(Config),
+
+
+ Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0},
+ {from, self()},
+ {mfa, {?MODULE, erlang_ssl_receive_and_assert_npn, [<<"spdy/2">>, Data]}},
+ {options, ServerOpts}]),
+ Port = ssl_test_lib:inet_port(Server),
+
+ Cmd = "openssl s_client -nextprotoneg http/1.0,spdy/2 -msg -port " ++ integer_to_list(Port) ++
+ " -host localhost",
+
+ test_server:format("openssl cmd: ~p~n", [Cmd]),
+
+ OpenSslPort = open_port({spawn, Cmd}, [stderr_to_stdout]),
+
+ Callback(Server, OpenSslPort),
+
+ ssl_test_lib:close(Server),
+
+ close_port(OpenSslPort),
+ process_flag(trap_exit, false).
+
+start_erlang_server_and_openssl_client_with_opts(Config, ErlangServerOpts, OpenSSLClientOpts, Data, Callback) ->
+ process_flag(trap_exit, true),
+ ServerOpts0 = ?config(server_opts, Config),
+ ServerOpts = ErlangServerOpts ++ ServerOpts0,
+
+ {_, ServerNode, _} = ssl_test_lib:run_where(Config),
+
+
+ Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0},
+ {from, self()},
+ {mfa, {?MODULE, erlang_ssl_receive, [Data]}},
+ {options, ServerOpts}]),
+ Port = ssl_test_lib:inet_port(Server),
+
+ Cmd = "openssl s_client " ++ OpenSSLClientOpts ++ " -msg -port " ++ integer_to_list(Port) ++
+ " -host localhost",
+
+ test_server:format("openssl cmd: ~p~n", [Cmd]),
+
+ OpenSslPort = open_port({spawn, Cmd}, [stderr_to_stdout]),
+
+ Callback(Server, OpenSslPort),
+
+ ssl_test_lib:close(Server),
+
+ close_port(OpenSslPort),
+ process_flag(trap_exit, false).
+
+
+erlang_ssl_receive_and_assert_npn(Socket, Protocol, Data) ->
+ {ok, Protocol} = ssl:negotiated_next_protocol(Socket),
+ erlang_ssl_receive(Socket, Data),
+ {ok, Protocol} = ssl:negotiated_next_protocol(Socket),
+ ok.
erlang_ssl_receive(Socket, Data) ->
test_server:format("Connection info: ~p~n",
@@ -1168,6 +1426,15 @@ version_flag('tlsv1.2') ->
version_flag(sslv3) ->
" -ssl3 ".
+check_openssl_npn_support(Config) ->
+ HelpText = os:cmd("openssl s_client --help"),
+ case string:str(HelpText, "nextprotoneg") of
+ 0 ->
+ {skip, "Openssl not compiled with nextprotoneg support"};
+ _ ->
+ Config
+ end.
+
check_sane_openssl_renegotaite(Config) ->
case os:cmd("openssl version") of
"OpenSSL 0.9.8" ++ _ ->