aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--lib/ssl/doc/src/ssl.xml39
-rw-r--r--lib/ssl/src/ssl.erl26
-rw-r--r--lib/ssl/src/ssl_handshake.erl8
-rw-r--r--lib/ssl/src/ssl_handshake.hrl2
-rw-r--r--lib/ssl/test/ssl_npn_handshake_SUITE.erl50
-rw-r--r--lib/ssl/test/ssl_npn_hello_SUITE.erl2
-rw-r--r--lib/ssl/test/ssl_to_openssl_SUITE.erl31
7 files changed, 94 insertions, 64 deletions
diff --git a/lib/ssl/doc/src/ssl.xml b/lib/ssl/doc/src/ssl.xml
index 9fc357f1fd..f0eac76264 100644
--- a/lib/ssl/doc/src/ssl.xml
+++ b/lib/ssl/doc/src/ssl.xml
@@ -303,22 +303,29 @@ fun(OtpCert :: #'OTPCertificate'{}, Event :: {bad_cert, Reason :: atom()} |
when possible.
</item>
- <tag>{client_preferred_next_protocols, Fallback :: binary(), Order :: client | server,
- PreferenceList :: list(binary())}</tag>
- <item>Indicates the client will try to perform Next Protocol Negotiation. The
- client will attempt to match a protocol in the PreferenceList with a protocol
- the server advertises. If the Order is <c>client</c> a protocol earlier in the
- PreferenceList will have precendence over a protocol later in the PreferenceList.
- Otherwise if the Order is <c>server</c> protocol precendence is determined by the
- order the server advertises its protocols. If the server does not advertise a
- protocol in the PreferenceList then the Fallback protocol
- will be chosen. Fallback must not be an empty binary and PreferenceList
- must not contain empty binaries. If the client negotiates a Next Protocol it can be accessed
- using <c>negotiated_next_protocol/1</c> method.
- </item>
-
- </taglist>
- </section>
+ <tag>{client_preferred_next_protocols, Precedence:: server | client, ClientPrefs::[binary()]}
+ {client_preferred_next_protocols, Precedence:: server | client, ClientPrefs::[binary()] , Default :: binary()}}</tag>
+
+ <item> <p>Indicates the client will try to perform Next Protocol
+ Negotiation.</p>
+
+ <p>If precedence is server the negaotiated protocol will be the
+ first protocol that appears on the server advertised list that is
+ also on the clients preference list.</p>
+
+ <p>If the precedence is client the negaotiated protocol will be the
+ first protocol that appears on the clients preference list that is
+ also on the server advertised list.</p>
+
+ <p> If the client does not support any of the servers advertised
+ protocols or the server does not advertise any protocols the
+ client will fallback to the first protocol in its list or if a
+ default is supplied it will fallback to that instead. If the
+ server does not support next protocol renegotiation the
+ connection will be aborted if no default protocol is supplied.</p>
+ </item>
+ </taglist>
+ </section>
<section>
<title>SSL OPTION DESCRIPTIONS - SERVER SIDE</title>
diff --git a/lib/ssl/src/ssl.erl b/lib/ssl/src/ssl.erl
index 9ed6acd43a..c72c4d7a93 100644
--- a/lib/ssl/src/ssl.erl
+++ b/lib/ssl/src/ssl.erl
@@ -39,6 +39,7 @@
-include("ssl_internal.hrl").
-include("ssl_record.hrl").
-include("ssl_cipher.hrl").
+-include("ssl_handshake.hrl").
-include_lib("public_key/include/public_key.hrl").
@@ -745,11 +746,16 @@ validate_option(hibernate_after, Value) when is_integer(Value), Value >= 0 ->
validate_option(erl_dist,Value) when Value == true;
Value == false ->
Value;
-validate_option(client_preferred_next_protocols, {FallbackProtocol, Order, PreferredProtocols} = Value)
- when is_list(PreferredProtocols), is_binary(FallbackProtocol),
- byte_size(FallbackProtocol) > 0, byte_size(FallbackProtocol) < 256 ->
+validate_option(client_preferred_next_protocols, {Precedence, PreferredProtocols})
+ when is_list(PreferredProtocols) ->
validate_binary_list(client_preferred_next_protocols, PreferredProtocols),
- validate_npn_ordering(Order),
+ validate_npn_ordering(Precedence),
+ {Precedence, PreferredProtocols, ?NO_PROTOCOL};
+validate_option(client_preferred_next_protocols, {Precedence, PreferredProtocols, Default} = Value)
+ when is_list(PreferredProtocols), is_binary(Default),
+ byte_size(Default) > 0, byte_size(Default) < 256 ->
+ validate_binary_list(client_preferred_next_protocols, PreferredProtocols),
+ validate_npn_ordering(Precedence),
Value;
validate_option(client_preferred_next_protocols, undefined) ->
undefined;
@@ -766,7 +772,7 @@ validate_npn_ordering(client) ->
validate_npn_ordering(server) ->
ok;
validate_npn_ordering(Value) ->
- throw({error, {eoptions, {client_preferred_next_protocols, Value}}}).
+ throw({error, {eoptions, {client_preferred_next_protocols, {invalid_precedence, Value}}}}).
validate_binary_list(Opt, List) ->
lists:foreach(
@@ -775,7 +781,7 @@ validate_binary_list(Opt, List) ->
byte_size(Bin) < 256 ->
ok;
(Bin) ->
- throw({error, {eoptions, {Opt, Bin}}})
+ throw({error, {eoptions, {Opt, {invalid_protocol, Bin}}}})
end, List).
validate_versions([], Versions) ->
@@ -896,18 +902,18 @@ detect(Pred, [H|T]) ->
make_next_protocol_selector(undefined) ->
undefined;
-make_next_protocol_selector({FallbackProtocol, client, AllProtocols}) ->
+make_next_protocol_selector({client, AllProtocols, DefaultProtocol}) ->
fun(AdvertisedProtocols) ->
case detect(fun(PreferredProtocol) -> lists:member(PreferredProtocol, AdvertisedProtocols) end, AllProtocols) of
- undefined -> FallbackProtocol;
+ undefined -> DefaultProtocol;
PreferredProtocol -> PreferredProtocol
end
end;
-make_next_protocol_selector({FallbackProtocol, server, AllProtocols}) ->
+make_next_protocol_selector({server, AllProtocols, DefaultProtocol}) ->
fun(AdvertisedProtocols) ->
case detect(fun(PreferredProtocol) -> lists:member(PreferredProtocol, AllProtocols) end, AdvertisedProtocols) of
- undefined -> FallbackProtocol;
+ undefined -> DefaultProtocol;
PreferredProtocol -> PreferredProtocol
end
end.
diff --git a/lib/ssl/src/ssl_handshake.erl b/lib/ssl/src/ssl_handshake.erl
index 3b114c1f9c..695721d51a 100644
--- a/lib/ssl/src/ssl_handshake.erl
+++ b/lib/ssl/src/ssl_handshake.erl
@@ -741,10 +741,10 @@ select_next_protocol({error, _Reason}, _NextProtocolSelector) ->
?ALERT_REC(?FATAL, ?HANDSHAKE_FAILURE);
select_next_protocol(Protocols, NextProtocolSelector) ->
case NextProtocolSelector(Protocols) of
- Protocol when is_binary(Protocol), byte_size(Protocol) > 0 ->
- Protocol;
- _ ->
- ?ALERT_REC(?FATAL, ?INTERNAL_ERROR) % we are broken internally :( api presently stops this but we might let it happen in future
+ ?NO_PROTOCOL ->
+ ?ALERT_REC(?FATAL, ?HANDSHAKE_FAILURE);
+ Protocol when is_binary(Protocol) ->
+ Protocol
end.
handle_renegotiation_info(_, #renegotiation_info{renegotiated_connection = ?byte(0)},
diff --git a/lib/ssl/src/ssl_handshake.hrl b/lib/ssl/src/ssl_handshake.hrl
index bd583e5d92..a4ce48c291 100644
--- a/lib/ssl/src/ssl_handshake.hrl
+++ b/lib/ssl/src/ssl_handshake.hrl
@@ -33,6 +33,8 @@
-type public_key_info() :: {algo_oid(), #'RSAPublicKey'{} | integer() , public_key_params()}.
-type tls_handshake_history() :: {[binary()], [binary()]}.
+-define(NO_PROTOCOL, <<>>).
+
%% Signature algorithms
-define(ANON, 0).
-define(RSA, 1).
diff --git a/lib/ssl/test/ssl_npn_handshake_SUITE.erl b/lib/ssl/test/ssl_npn_handshake_SUITE.erl
index f2327756c3..8bef2d8d22 100644
--- a/lib/ssl/test/ssl_npn_handshake_SUITE.erl
+++ b/lib/ssl/test/ssl_npn_handshake_SUITE.erl
@@ -28,16 +28,16 @@ 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)
+ 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"}
+ {skip, "Crypto did not start"}
end.
end_per_suite(_Config) ->
@@ -62,9 +62,14 @@ 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, {next_protocols_advertised, {invalid_protocol, <<>>}}}}
+ = (catch ssl:listen(9443,
+ [{next_protocols_advertised, [<<"foo/1">>, <<"">>]}])),
+ {error, {eoptions, {client_preferred_next_protocols, {invalid_protocol, <<>>}}}}
+ = (catch ssl:connect({127,0,0,1}, 9443,
+ [{client_preferred_next_protocols,
+ {client, [<<"foo/1">>, <<"">>], <<"foox/1">>}}], infinity)),
+ Option = {client_preferred_next_protocols, {invalid_protocol, <<"">>}},
{error, {eoptions, Option}} = (catch ssl:connect({127,0,0,1}, 9443, [Option], infinity)).
validate_empty_advertisement_list_is_allowed_test(_Config) ->
@@ -90,32 +95,34 @@ perform_client_does_not_try_to_negotiate_but_server_supports_npn_test(Config) ->
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">>]}}],
+ [{client_preferred_next_protocols, {client, [<<"spdy/2">>], <<"http/1.1">>}}],
[],
{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">>]}}],
+ [{client_preferred_next_protocols, {client, [<<"spdy/2">>], <<"http/1.1">>}}],
[{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">>]}}],
+ [{client_preferred_next_protocols, {server, [<<"spdy/2">>], <<"http/1.1">>}}],
[{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">>]}}],
+ [{client_preferred_next_protocols,
+ {client, [<<"http/1.0">>, <<"http/1.1">>], <<"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">>]}}],
+ [{client_preferred_next_protocols,
+ {server, [<<"http/1.0">>, <<"http/1.1">>], <<"http/1.1">>}}],
[{next_protocols_advertised, [<<"spdy/2">>, <<"http/1.1">>, <<"http/1.0">>]}],
{ok, <<"http/1.1">>}).
@@ -124,9 +131,11 @@ 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,
+ ClientOpts = [{client_preferred_next_protocols,
+ {client, [<<"http/1.0">>], <<"http/1.1">>}}] ++ ClientOpts0,
ServerOpts0 = ?config(server_opts, Config),
- ServerOpts = [{next_protocols_advertised, [<<"spdy/2">>, <<"http/1.1">>, <<"http/1.0">>]}] ++ ServerOpts0,
+ 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),
@@ -170,7 +179,8 @@ run_npn_handshake_test(Config, ClientExtraOpts, ServerExtraOpts, ExpectedProtoco
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]),
+ 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) ->
diff --git a/lib/ssl/test/ssl_npn_hello_SUITE.erl b/lib/ssl/test/ssl_npn_hello_SUITE.erl
index f177778178..0bca8bbeb4 100644
--- a/lib/ssl/test/ssl_npn_hello_SUITE.erl
+++ b/lib/ssl/test/ssl_npn_hello_SUITE.erl
@@ -60,7 +60,7 @@ encode_and_decode_client_hello_test(_Config) ->
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), <<>>),
+ {[{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 = <<>>}.
diff --git a/lib/ssl/test/ssl_to_openssl_SUITE.erl b/lib/ssl/test/ssl_to_openssl_SUITE.erl
index cc3c6439ac..30f8f60156 100644
--- a/lib/ssl/test/ssl_to_openssl_SUITE.erl
+++ b/lib/ssl/test/ssl_to_openssl_SUITE.erl
@@ -29,7 +29,7 @@
-define(TIMEOUT, 120000).
-define(LONG_TIMEOUT, 600000).
-define(SLEEP, 1000).
--define(OPENSSL_RENEGOTIATE, "r\n").
+-define(OPENSSL_RENEGOTIATE, "R\n").
-define(OPENSSL_QUIT, "Q\n").
-define(OPENSSL_GARBAGE, "P\n").
-define(EXPIRE, 10).
@@ -172,9 +172,9 @@ all() ->
groups() ->
[{basic, [], basic_tests()},
- {'tlsv1.2', [], all_versions_tests()},
- {'tlsv1.1', [], all_versions_tests()},
- {'tlsv1', [], all_versions_tests()},
+ {'tlsv1.2', [], all_versions_tests() ++ npn_tests()},
+ {'tlsv1.1', [], all_versions_tests() ++ npn_tests()},
+ {'tlsv1', [], all_versions_tests()++ npn_tests()},
{'sslv3', [], all_versions_tests()}].
basic_tests() ->
@@ -199,8 +199,10 @@ all_versions_tests() ->
ciphers_dsa_signed_certs,
erlang_client_bad_openssl_server,
expired_session,
- ssl2_erlang_server_openssl_client,
- erlang_client_openssl_server_npn_negotiation,
+ ssl2_erlang_server_openssl_client].
+
+npn_tests() ->
+ [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,
@@ -1164,7 +1166,7 @@ erlang_client_openssl_server_npn_negotiate_only_on_server(Config) when is_list(C
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) ->
+ start_erlang_client_and_openssl_server_with_opts(Config, [{client_preferred_next_protocols, {client, [<<"spdy/2">>], <<"http/1.1">>}}], "", Data, fun(Server, OpensslPort) ->
port_command(OpensslPort, Data),
ssl_test_lib:check_result(Server, ok)
end),
@@ -1202,9 +1204,11 @@ start_erlang_client_and_openssl_server_with_opts(Config, ErlangClientOpts, Opens
Port = ssl_test_lib:inet_port(node()),
CertFile = proplists:get_value(certfile, ServerOpts),
KeyFile = proplists:get_value(keyfile, ServerOpts),
+ Version = ssl_record:protocol_version(ssl_record:highest_protocol_version([])),
- Cmd = "openssl s_server " ++ OpensslServerOpts ++ " -accept " ++ integer_to_list(Port) ++
- " -cert " ++ CertFile ++ " -key " ++ KeyFile,
+ Cmd = "openssl s_server " ++ OpensslServerOpts ++ " -accept " ++
+ integer_to_list(Port) ++ version_flag(Version) ++
+ " -cert " ++ CertFile ++ " -key " ++ KeyFile,
test_server:format("openssl cmd: ~p~n", [Cmd]),
@@ -1231,7 +1235,7 @@ start_erlang_client_and_openssl_server_for_npn_negotiation(Config, Data, Callbac
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],
+ ClientOpts = [{client_preferred_next_protocols, {client, [<<"spdy/2">>], <<"http/1.1">>}} | ClientOpts0],
{ClientNode, _, Hostname} = ssl_test_lib:run_where(Config),
@@ -1240,8 +1244,9 @@ start_erlang_client_and_openssl_server_for_npn_negotiation(Config, Data, Callbac
Port = ssl_test_lib:inet_port(node()),
CertFile = proplists:get_value(certfile, ServerOpts),
KeyFile = proplists:get_value(keyfile, ServerOpts),
+ Version = ssl_record:protocol_version(ssl_record:highest_protocol_version([])),
- Cmd = "openssl s_server -msg -nextprotoneg http/1.1,spdy/2 -accept " ++ integer_to_list(Port) ++
+ Cmd = "openssl s_server -msg -nextprotoneg http/1.1,spdy/2 -accept " ++ integer_to_list(Port) ++ version_flag(Version) ++
" -cert " ++ CertFile ++ " -key " ++ KeyFile,
test_server:format("openssl cmd: ~p~n", [Cmd]),
@@ -1278,8 +1283,8 @@ start_erlang_server_and_openssl_client_for_npn_negotiation(Config, Data, Callbac
{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) ++
+ Version = ssl_record:protocol_version(ssl_record:highest_protocol_version([])),
+ Cmd = "openssl s_client -nextprotoneg http/1.0,spdy/2 -msg -port " ++ integer_to_list(Port) ++ version_flag(Version) ++
" -host localhost",
test_server:format("openssl cmd: ~p~n", [Cmd]),