aboutsummaryrefslogtreecommitdiffstats
path: root/lib/ssl
diff options
context:
space:
mode:
Diffstat (limited to 'lib/ssl')
-rw-r--r--lib/ssl/doc/src/ssl.xml26
-rw-r--r--lib/ssl/src/ssl.erl9
-rw-r--r--lib/ssl/src/ssl_certificate.erl21
-rw-r--r--lib/ssl/src/ssl_cipher.erl8
-rw-r--r--lib/ssl/src/ssl_connection.erl17
-rw-r--r--lib/ssl/src/ssl_handshake.erl21
-rw-r--r--lib/ssl/src/ssl_record.erl12
-rw-r--r--lib/ssl/test/ssl_basic_SUITE.erl63
-rw-r--r--lib/ssl/test/ssl_npn_handshake_SUITE.erl54
-rw-r--r--lib/ssl/test/ssl_to_openssl_SUITE.erl4
10 files changed, 160 insertions, 75 deletions
diff --git a/lib/ssl/doc/src/ssl.xml b/lib/ssl/doc/src/ssl.xml
index 6979fb5b5e..e9858a3220 100644
--- a/lib/ssl/doc/src/ssl.xml
+++ b/lib/ssl/doc/src/ssl.xml
@@ -80,8 +80,8 @@
{cacerts, [der_encoded()]} | {cacertfile, path()} |
|{dh, der_encoded()} | {dhfile, path()} | {ciphers, ciphers()} |
{ssl_imp, ssl_imp()} | {reuse_sessions, boolean()} | {reuse_session, fun()}
- {next_protocols_advertised, list(binary()} |
- {client_preferred_next_protocols, binary(), client | server, list(binary())}
+ {next_protocols_advertised, [binary()]} |
+ {client_preferred_next_protocols, client | server, [binary()]}
</c></p>
<p><c>transportoption() = {cb_info, {CallbackModule::atom(), DataTag::atom(), ClosedTag::atom(), ErrTag:atom()}}
@@ -313,25 +313,25 @@ fun(OtpCert :: #'OTPCertificate'{}, Event :: {bad_cert, Reason :: atom()} |
when possible.
</item>
- <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
+ <tag>{client_preferred_next_protocols, Precedence :: server | client, ClientPrefs :: [binary()]}</tag>
+ <tag>{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
+ <p>If precedence is server the negotiated protocol will be the
first protocol that appears on the server advertised list that is
- also on the clients preference list.</p>
+ also on the client 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
+ <p>If precedence is client the negotiated protocol will be the
+ first protocol that appears on the client preference list that is
also on the server advertised list.</p>
- <p> If the client does not support any of the servers advertised
+ <p>If the client does not support any of the server 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
+ server does not support Next Protocol Negotiation the
connection will be aborted if no default protocol is supplied.</p>
</item>
</taglist>
@@ -388,7 +388,7 @@ fun(OtpCert :: #'OTPCertificate'{}, Event :: {bad_cert, Reason :: atom()} |
and CipherSuite is of type ciphersuite().
</item>
- <tag>{next_protocols_advertised, Protocols :: list(binary())}</tag>
+ <tag>{next_protocols_advertised, Protocols :: [binary()]}</tag>
<item>The list of protocols to send to the client if the client indicates
it supports the Next Protocol extension. The client may select a protocol
that is not on this list. The list of protocols must not contain an empty
diff --git a/lib/ssl/src/ssl.erl b/lib/ssl/src/ssl.erl
index 0ba59cede2..fc06b5f1b0 100644
--- a/lib/ssl/src/ssl.erl
+++ b/lib/ssl/src/ssl.erl
@@ -612,8 +612,15 @@ handle_options(Opts0, _Role) ->
CertFile = handle_option(certfile, Opts, <<>>),
+ Versions = case handle_option(versions, Opts, []) of
+ [] ->
+ ssl_record:supported_protocol_versions();
+ Vsns ->
+ [ssl_record:protocol_version(Vsn) || Vsn <- Vsns]
+ end,
+
SSLOptions = #ssl_options{
- versions = handle_option(versions, Opts, []),
+ versions = Versions,
verify = validate_option(verify, Verify),
verify_fun = VerifyFun,
fail_if_no_peer_cert = FailIfNoPeerCert,
diff --git a/lib/ssl/src/ssl_certificate.erl b/lib/ssl/src/ssl_certificate.erl
index 86f5617b54..01a7cd93b5 100644
--- a/lib/ssl/src/ssl_certificate.erl
+++ b/lib/ssl/src/ssl_certificate.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2007-2012. All Rights Reserved.
+%% Copyright Ericsson AB 2007-2013. 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
@@ -37,8 +37,7 @@
is_valid_extkey_usage/2,
is_valid_key_usage/2,
select_extension/2,
- extensions_list/1,
- signature_type/1
+ extensions_list/1
]).
%%====================================================================
@@ -167,22 +166,6 @@ extensions_list(Extensions) ->
Extensions.
%%--------------------------------------------------------------------
--spec signature_type(term()) -> rsa | dsa .
-%%
-%% Description:
-%%--------------------------------------------------------------------
-signature_type(RSA) when RSA == ?sha1WithRSAEncryption;
- RSA == ?md5WithRSAEncryption;
- RSA == ?sha224WithRSAEncryption;
- RSA == ?sha256WithRSAEncryption;
- RSA == ?sha384WithRSAEncryption;
- RSA == ?sha512WithRSAEncryption
- ->
- rsa;
-signature_type(?'id-dsa-with-sha1') ->
- dsa.
-
-%%--------------------------------------------------------------------
%%% Internal functions
%%--------------------------------------------------------------------
certificate_chain(OtpCert, _Cert, CertDbHandle, CertsDbRef, Chain) ->
diff --git a/lib/ssl/src/ssl_cipher.erl b/lib/ssl/src/ssl_cipher.erl
index 567690a413..d91e2a89a0 100644
--- a/lib/ssl/src/ssl_cipher.erl
+++ b/lib/ssl/src/ssl_cipher.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2007-2012. All Rights Reserved.
+%% Copyright Ericsson AB 2007-2013. 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
@@ -483,10 +483,10 @@ filter(undefined, Ciphers) ->
filter(DerCert, Ciphers) ->
OtpCert = public_key:pkix_decode_cert(DerCert, otp),
SigAlg = OtpCert#'OTPCertificate'.signatureAlgorithm,
- case ssl_certificate:signature_type(SigAlg#'SignatureAlgorithm'.algorithm) of
- rsa ->
+ case public_key:pkix_sign_types(SigAlg#'SignatureAlgorithm'.algorithm) of
+ {_, rsa} ->
filter_rsa(OtpCert, Ciphers -- dsa_signed_suites());
- dsa ->
+ {_, dsa} ->
Ciphers -- rsa_signed_suites()
end.
diff --git a/lib/ssl/src/ssl_connection.erl b/lib/ssl/src/ssl_connection.erl
index 8f4fd88d42..4d29ecce7a 100644
--- a/lib/ssl/src/ssl_connection.erl
+++ b/lib/ssl/src/ssl_connection.erl
@@ -73,7 +73,6 @@
session_cache, %
session_cache_cb, %
negotiated_version, % tls_version()
- supported_protocol_versions, % [atom()]
client_certificate_requested = false,
key_algorithm, % atom as defined by cipher_suite
hashsign_algorithm, % atom as defined by cipher_suite
@@ -472,6 +471,13 @@ abbreviated(#finished{verify_data = Data} = Finished,
handle_own_alert(Alert, Version, abbreviated, State)
end;
+%% only allowed to send next_protocol message after change cipher spec
+%% & before finished message and it is not allowed during renegotiation
+abbreviated(#next_protocol{selected_protocol = SelectedProtocol},
+ #state{role = server, expecting_next_protocol_negotiation = true} = State0) ->
+ {Record, State} = next_record(State0#state{next_protocol = SelectedProtocol}),
+ next_state(abbreviated, abbreviated, Record, State);
+
abbreviated(timeout, State) ->
{ next_state, abbreviated, State, hibernate };
@@ -656,11 +662,10 @@ cipher(#certificate_verify{signature = Signature, hashsign_algorithm = CertHashS
handle_own_alert(Alert, Version, cipher, State0)
end;
-% client must send a next protocol message if we are expecting it
+%% client must send a next protocol message if we are expecting it
cipher(#finished{}, #state{role = server, expecting_next_protocol_negotiation = true,
next_protocol = undefined, negotiated_version = Version} = State0) ->
- handle_own_alert(?ALERT_REC(?FATAL,?UNEXPECTED_MESSAGE), Version, cipher, State0),
- {stop, normal, State0};
+ handle_own_alert(?ALERT_REC(?FATAL,?UNEXPECTED_MESSAGE), Version, cipher, State0);
cipher(#finished{verify_data = Data} = Finished,
#state{negotiated_version = Version,
@@ -682,8 +687,8 @@ cipher(#finished{verify_data = Data} = Finished,
handle_own_alert(Alert, Version, cipher, State)
end;
-% only allowed to send next_protocol message after change cipher spec
-% & before finished message and it is not allowed during renegotiation
+%% only allowed to send next_protocol message after change cipher spec
+%% & before finished message and it is not allowed during renegotiation
cipher(#next_protocol{selected_protocol = SelectedProtocol},
#state{role = server, expecting_next_protocol_negotiation = true} = State0) ->
{Record, State} = next_record(State0#state{next_protocol = SelectedProtocol}),
diff --git a/lib/ssl/src/ssl_handshake.erl b/lib/ssl/src/ssl_handshake.erl
index 1929370991..889d310ca8 100644
--- a/lib/ssl/src/ssl_handshake.erl
+++ b/lib/ssl/src/ssl_handshake.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2007-2012. All Rights Reserved.
+%% Copyright Ericsson AB 2007-2013. 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
@@ -61,11 +61,7 @@ client_hello(Host, Port, ConnectionStates,
ciphers = UserSuites
} = SslOpts,
Cache, CacheCb, Renegotiation, OwnCert) ->
-
- Fun = fun(Version) ->
- ssl_record:protocol_version(Version)
- end,
- Version = ssl_record:highest_protocol_version(lists:map(Fun, Versions)),
+ Version = ssl_record:highest_protocol_version(Versions),
Pending = ssl_record:pending_connection_state(ConnectionStates, read),
SecParams = Pending#connection_state.security_parameters,
Ciphers = available_suites(UserSuites, Version),
@@ -139,10 +135,11 @@ hello(#server_hello{cipher_suite = CipherSuite, server_version = Version,
compression_method = Compression, random = Random,
session_id = SessionId, renegotiation_info = Info,
hash_signs = _HashSigns} = Hello,
- #ssl_options{secure_renegotiate = SecureRenegotation, next_protocol_selector = NextProtocolSelector},
+ #ssl_options{secure_renegotiate = SecureRenegotation, next_protocol_selector = NextProtocolSelector,
+ versions = SupportedVersions},
ConnectionStates0, Renegotiation) ->
%%TODO: select hash and signature algorigthm
- case ssl_record:is_acceptable_version(Version) of
+ case ssl_record:is_acceptable_version(Version, SupportedVersions) of
true ->
case handle_renegotiation_info(client, Info, ConnectionStates0,
Renegotiation, SecureRenegotation, []) of
@@ -171,7 +168,7 @@ hello(#client_hello{client_version = ClientVersion, random = Random,
{Port, Session0, Cache, CacheCb, ConnectionStates0, Cert}, Renegotiation) ->
%% TODO: select hash and signature algorithm
Version = select_version(ClientVersion, Versions),
- case ssl_record:is_acceptable_version(Version) of
+ case ssl_record:is_acceptable_version(Version, Versions) of
true ->
{Type, #session{cipher_suite = CipherSuite,
compression_method = Compression} = Session}
@@ -869,11 +866,7 @@ hello_security_parameters(server, Version, ConnectionState, CipherSuite, Random,
}.
select_version(ClientVersion, Versions) ->
- Fun = fun(Version) ->
- ssl_record:protocol_version(Version)
- end,
- ServerVersion = ssl_record:highest_protocol_version(lists:map(Fun,
- Versions)),
+ ServerVersion = ssl_record:highest_protocol_version(Versions),
ssl_record:lowest_protocol_version(ClientVersion, ServerVersion).
select_cipher_suite([], _) ->
diff --git a/lib/ssl/src/ssl_record.erl b/lib/ssl/src/ssl_record.erl
index 173b9611c6..26aca56739 100644
--- a/lib/ssl/src/ssl_record.erl
+++ b/lib/ssl/src/ssl_record.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2007-2012. All Rights Reserved.
+%% Copyright Ericsson AB 2007-2013. 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
@@ -56,7 +56,7 @@
%% Misc.
-export([protocol_version/1, lowest_protocol_version/2,
highest_protocol_version/1, supported_protocol_versions/0,
- is_acceptable_version/1]).
+ is_acceptable_version/1, is_acceptable_version/2]).
-export([compressions/0]).
@@ -475,8 +475,10 @@ supported_protocol_versions([_|_] = Vsns) ->
%%--------------------------------------------------------------------
-spec is_acceptable_version(tls_version()) -> boolean().
+-spec is_acceptable_version(tls_version(), Supported :: [tls_version()]) -> boolean().
%%
%% Description: ssl version 2 is not acceptable security risks are too big.
+%%
%%--------------------------------------------------------------------
is_acceptable_version({N,_})
when N >= ?LOWEST_MAJOR_SUPPORTED_VERSION ->
@@ -484,6 +486,12 @@ is_acceptable_version({N,_})
is_acceptable_version(_) ->
false.
+is_acceptable_version({N,_} = Version, Versions)
+ when N >= ?LOWEST_MAJOR_SUPPORTED_VERSION ->
+ lists:member(Version, Versions);
+is_acceptable_version(_,_) ->
+ false.
+
%%--------------------------------------------------------------------
-spec compressions() -> [binary()].
%%
diff --git a/lib/ssl/test/ssl_basic_SUITE.erl b/lib/ssl/test/ssl_basic_SUITE.erl
index b5c6a1da49..6b8f226a77 100644
--- a/lib/ssl/test/ssl_basic_SUITE.erl
+++ b/lib/ssl/test/ssl_basic_SUITE.erl
@@ -126,7 +126,8 @@ api_tests() ->
hibernate,
listen_socket,
ssl_accept_timeout,
- ssl_recv_timeout
+ ssl_recv_timeout,
+ versions_option
].
session_tests() ->
@@ -1194,12 +1195,12 @@ tcp_connect(Config) when is_list(Config) ->
{_, ServerNode, Hostname} = ssl_test_lib:run_where(Config),
TcpOpts = [binary, {reuseaddr, true}],
- Server = ssl_test_lib:start_upgrade_server([{node, ServerNode}, {port, 0},
- {from, self()},
- {timeout, 5000},
- {mfa, {?MODULE, dummy, []}},
- {tcp_options, TcpOpts},
- {ssl_options, ServerOpts}]),
+ Server = ssl_test_lib:start_upgrade_server_error([{node, ServerNode}, {port, 0},
+ {from, self()},
+ {timeout, 5000},
+ {mfa, {?MODULE, dummy, []}},
+ {tcp_options, TcpOpts},
+ {ssl_options, ServerOpts}]),
Port = ssl_test_lib:inet_port(Server),
{ok, Socket} = gen_tcp:connect(Hostname, Port, [binary, {packet, 0}]),
@@ -1222,12 +1223,12 @@ tcp_connect_big(Config) when is_list(Config) ->
{_, ServerNode, Hostname} = ssl_test_lib:run_where(Config),
TcpOpts = [binary, {reuseaddr, true}],
- Server = ssl_test_lib:start_upgrade_server([{node, ServerNode}, {port, 0},
- {from, self()},
- {timeout, 5000},
- {mfa, {?MODULE, dummy, []}},
- {tcp_options, TcpOpts},
- {ssl_options, ServerOpts}]),
+ Server = ssl_test_lib:start_upgrade_server_error([{node, ServerNode}, {port, 0},
+ {from, self()},
+ {timeout, 5000},
+ {mfa, {?MODULE, dummy, []}},
+ {tcp_options, TcpOpts},
+ {ssl_options, ServerOpts}]),
Port = ssl_test_lib:inet_port(Server),
{ok, Socket} = gen_tcp:connect(Hostname, Port, [binary, {packet, 0}]),
@@ -2659,6 +2660,42 @@ session_cache_process_mnesia(Config) when is_list(Config) ->
session_cache_process(mnesia,Config).
%%--------------------------------------------------------------------
+
+versions_option() ->
+ [{doc,"Test API versions option to connect/listen."}].
+versions_option(Config) when is_list(Config) ->
+ ClientOpts = ?config(client_opts, Config),
+ ServerOpts = ?config(server_opts, Config),
+
+ Supported = proplists:get_value(supported, ssl:versions()),
+ Available = proplists:get_value(available, ssl:versions()),
+ {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config),
+ Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0},
+ {from, self()},
+ {mfa, {ssl_test_lib, send_recv_result_active, []}},
+ {options, [{versions, Supported} | ServerOpts]}]),
+ Port = ssl_test_lib:inet_port(Server),
+
+ Client = ssl_test_lib:start_client([{node, ClientNode}, {port, Port},
+ {host, Hostname},
+ {from, self()},
+ {mfa, {ssl_test_lib, send_recv_result_active, []}},
+ {options, ClientOpts}]),
+
+ ssl_test_lib:check_result(Server, ok, Client, ok),
+ Server ! listen,
+
+ ErrClient = ssl_test_lib:start_client_error([{node, ClientNode}, {port, Port},
+ {host, Hostname},
+ {from, self()},
+ {options, [{versions , Available -- Supported} | ClientOpts]}]),
+ receive
+ {Server, _} ->
+ ok
+ end,
+
+ ssl_test_lib:check_result(ErrClient, {error, {tls_alert, "protocol version"}}).
+%%--------------------------------------------------------------------
%% Internal functions ------------------------------------------------
%%--------------------------------------------------------------------
send_recv_result(Socket) ->
diff --git a/lib/ssl/test/ssl_npn_handshake_SUITE.erl b/lib/ssl/test/ssl_npn_handshake_SUITE.erl
index 862690cd7b..8c1b22cf5e 100644
--- a/lib/ssl/test/ssl_npn_handshake_SUITE.erl
+++ b/lib/ssl/test/ssl_npn_handshake_SUITE.erl
@@ -24,6 +24,7 @@
-compile(export_all).
-include_lib("common_test/include/ct.hrl").
+-define(SLEEP, 500).
%%--------------------------------------------------------------------
%% Common Test interface functions -----------------------------------
%%--------------------------------------------------------------------
@@ -55,7 +56,8 @@ next_protocol_tests() ->
fallback_npn_handshake_server_preference,
client_negotiate_server_does_not_support,
no_client_negotiate_but_server_supports_npn,
- renegotiate_from_client_after_npn_handshake
+ renegotiate_from_client_after_npn_handshake,
+ npn_handshake_session_reused
].
next_protocol_not_supported() ->
@@ -231,6 +233,56 @@ npn_not_supported_server(Config) when is_list(Config)->
{error, {options, {not_supported_in_sslv3, AdvProtocols}}} = ssl:listen(0, ServerOpts).
+%--------------------------------------------------------------------------------
+npn_handshake_session_reused(Config) when is_list(Config)->
+ ClientOpts0 = ?config(client_opts, Config),
+ 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,
+
+ {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config),
+ Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0},
+ {from, self()},
+ {mfa, {ssl_test_lib, session_info_result, []}},
+ {options, ServerOpts}]),
+
+ Port = ssl_test_lib:inet_port(Server),
+ Client = ssl_test_lib:start_client([{node, ClientNode}, {port, Port},
+ {host, Hostname},
+ {from, self()},
+ {mfa, {ssl_test_lib, no_result_msg, []}},
+ {options, ClientOpts}]),
+
+ SessionInfo =
+ receive
+ {Server, Info} ->
+ Info
+ end,
+
+ Server ! {listen, {mfa, {ssl_test_lib, no_result, []}}},
+
+ %% Make sure session is registered
+ ct:sleep(?SLEEP),
+
+ Client1 =
+ ssl_test_lib:start_client([{node, ClientNode},
+ {port, Port}, {host, Hostname},
+ {mfa, {ssl_test_lib, session_info_result, []}},
+ {from, self()}, {options, ClientOpts}]),
+
+ receive
+ {Client1, SessionInfo} ->
+ ok;
+ {Client1, Other} ->
+ ct:fail(Other)
+ end,
+
+ ssl_test_lib:close(Server),
+ ssl_test_lib:close(Client),
+ ssl_test_lib:close(Client1).
+
%%--------------------------------------------------------------------
%% Internal functions ------------------------------------------------
%%--------------------------------------------------------------------
diff --git a/lib/ssl/test/ssl_to_openssl_SUITE.erl b/lib/ssl/test/ssl_to_openssl_SUITE.erl
index 4f53132d5d..a3d382f837 100644
--- a/lib/ssl/test/ssl_to_openssl_SUITE.erl
+++ b/lib/ssl/test/ssl_to_openssl_SUITE.erl
@@ -1202,8 +1202,8 @@ start_erlang_server_and_openssl_client_with_opts(Config, ErlangServerOpts, OpenS
{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) ++
+ Version = ssl_record:protocol_version(ssl_record:highest_protocol_version([])),
+ Cmd = "openssl s_client " ++ OpenSSLClientOpts ++ " -msg -port " ++ integer_to_list(Port) ++ version_flag(Version) ++
" -host localhost",
ct:print("openssl cmd: ~p~n", [Cmd]),