aboutsummaryrefslogtreecommitdiffstats
path: root/lib/ssl
diff options
context:
space:
mode:
Diffstat (limited to 'lib/ssl')
-rw-r--r--lib/ssl/doc/src/notes.xml167
-rw-r--r--lib/ssl/doc/src/ssl.xml68
-rw-r--r--lib/ssl/doc/src/ssl_app.xml57
-rw-r--r--lib/ssl/doc/src/ssl_crl_cache_api.xml6
-rw-r--r--lib/ssl/doc/src/ssl_session_cache_api.xml10
-rw-r--r--lib/ssl/src/dtls_connection.erl2
-rw-r--r--lib/ssl/src/inet_tls_dist.erl14
-rw-r--r--lib/ssl/src/ssl.app.src2
-rw-r--r--lib/ssl/src/ssl.appup.src2
-rw-r--r--lib/ssl/src/ssl.erl43
-rw-r--r--lib/ssl/src/ssl_connection.erl99
-rw-r--r--lib/ssl/src/ssl_connection.hrl23
-rw-r--r--lib/ssl/src/ssl_dist_sup.erl2
-rw-r--r--lib/ssl/src/ssl_internal.hrl17
-rw-r--r--lib/ssl/src/ssl_manager.erl186
-rw-r--r--lib/ssl/src/ssl_session.erl15
-rw-r--r--lib/ssl/src/ssl_session_cache.erl10
-rw-r--r--lib/ssl/src/ssl_session_cache_api.erl1
-rw-r--r--lib/ssl/src/ssl_tls_dist_proxy.erl96
-rw-r--r--lib/ssl/src/ssl_v3.erl1
-rw-r--r--lib/ssl/src/tls_connection.erl60
-rw-r--r--lib/ssl/src/tls_record.erl62
-rw-r--r--lib/ssl/test/make_certs.erl22
-rw-r--r--lib/ssl/test/ssl_ECC_SUITE.erl66
-rw-r--r--lib/ssl/test/ssl_basic_SUITE.erl165
-rw-r--r--lib/ssl/test/ssl_dist_SUITE.erl149
-rw-r--r--lib/ssl/test/ssl_payload_SUITE.erl11
-rw-r--r--lib/ssl/test/ssl_session_cache_SUITE.erl205
-rw-r--r--lib/ssl/test/ssl_test_lib.erl106
-rw-r--r--lib/ssl/test/ssl_to_openssl_SUITE.erl367
-rw-r--r--lib/ssl/test/ssl_upgrade_SUITE.erl204
-rw-r--r--lib/ssl/vsn.mk2
32 files changed, 1654 insertions, 586 deletions
diff --git a/lib/ssl/doc/src/notes.xml b/lib/ssl/doc/src/notes.xml
index b87b1b4fa7..61d1c8355a 100644
--- a/lib/ssl/doc/src/notes.xml
+++ b/lib/ssl/doc/src/notes.xml
@@ -26,7 +26,152 @@
<file>notes.xml</file>
</header>
<p>This document describes the changes made to the SSL application.</p>
- <section><title>SSL 7.0</title>
+
+
+<section><title>SSL 7.2</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p>
+ Honor distribution port range options</p>
+ <p>
+ Own Id: OTP-12838</p>
+ </item>
+ <item>
+ <p>
+ Correct supervisor specification in TLS distribution.</p>
+ <p>
+ Own Id: OTP-13134</p>
+ </item>
+ <item>
+ <p>
+ Correct cache timeout</p>
+ <p>
+ Own Id: OTP-13141</p>
+ </item>
+ <item>
+ <p>
+ Avoid crash and restart of ssl process when key file does
+ not exist.</p>
+ <p>
+ Own Id: OTP-13144</p>
+ </item>
+ <item>
+ <p>
+ Enable passing of raw socket options on the format
+ {raw,_,_,_} to the underlying socket.</p>
+ <p>
+ Own Id: OTP-13166</p>
+ </item>
+ <item>
+ <p>
+ Hibernation with small or a zero timeout will now work as
+ expected</p>
+ <p>
+ Own Id: OTP-13189</p>
+ </item>
+ </list>
+ </section>
+
+
+ <section><title>Improvements and New Features</title>
+ <list>
+ <item>
+ <p>
+ Add upper limit for session cache, configurable on ssl
+ application level.</p>
+ <p>
+ If upper limit is reached, invalidate the current cache
+ entries, e.i the session lifetime is the max time a
+ session will be keept, but it may be invalidated earlier
+ if the max limit for the table is reached. This will keep
+ the ssl manager process well behaved, not exhusting
+ memeory. Invalidating the entries will incrementally
+ empty the cache to make room for fresh sessions entries.</p>
+ <p>
+ Own Id: OTP-12392</p>
+ </item>
+ <item>
+ <p>
+ Use new time functions to measure passed time.</p>
+ <p>
+ Own Id: OTP-12457</p>
+ </item>
+ <item>
+ <p>
+ Improved error handling in TLS distribution</p>
+ <p>
+ Own Id: OTP-13142</p>
+ </item>
+ <item>
+ <p>
+ Distribution over TLS now honors the nodelay distribution
+ flag</p>
+ <p>
+ Own Id: OTP-13143</p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
+<section><title>SSL 7.1</title>
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p>
+ Add DER encoded ECPrivateKey as valid input format for
+ key option.</p>
+ <p>
+ Own Id: OTP-12974</p>
+ </item>
+ <item>
+ <p>
+ Correct return value of default session callback module</p>
+ <p>
+ This error had the symptom that the client check for
+ unique session would always fail, potentially making the
+ client session table grow a lot and causing long setup
+ times.</p>
+ <p>
+ Own Id: OTP-12980</p>
+ </item>
+ </list>
+ </section>
+
+
+ <section><title>Improvements and New Features</title>
+ <list>
+ <item>
+ <p>
+ Add possibility to downgrade an SSL/TLS connection to a
+ tcp connection, and give back the socket control to a
+ user process.</p>
+ <p>
+ This also adds the possibility to specify a timeout to
+ the ssl:close function.</p>
+ <p>
+ Own Id: OTP-11397</p>
+ </item>
+ <item>
+ <p>
+ Add application setting to be able to change fatal alert
+ shutdown timeout, also shorten the default timeout. The
+ fatal alert timeout is the number of milliseconds between
+ sending of a fatal alert and closing the connection.
+ Waiting a little while improves the peers chances to
+ properly receiving the alert so it may shutdown
+ gracefully.</p>
+ <p>
+ Own Id: OTP-12832</p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
+<section><title>SSL 7.0</title>
<section><title>Fixed Bugs and Malfunctions</title>
<list>
@@ -51,12 +196,6 @@
<p>
Own Id: OTP-12815</p>
</item>
- <item>
- <p>
- Gracefully ignore proprietary hash_sign algorithms</p>
- <p>
- Own Id: OTP-12829</p>
- </item>
</list>
</section>
@@ -107,6 +246,20 @@
</section>
+<section><title>SSL 6.0.1.1</title>
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p>
+ Gracefully ignore proprietary hash_sign algorithms</p>
+ <p>
+ Own Id: OTP-12829</p>
+ </item>
+ </list>
+ </section>
+</section>
+
+
<section><title>SSL 6.0.1</title>
<section><title>Fixed Bugs and Malfunctions</title>
diff --git a/lib/ssl/doc/src/ssl.xml b/lib/ssl/doc/src/ssl.xml
index f23b71e28b..bf87644116 100644
--- a/lib/ssl/doc/src/ssl.xml
+++ b/lib/ssl/doc/src/ssl.xml
@@ -31,37 +31,13 @@
<module>ssl</module>
<modulesummary>Interface Functions for Secure Socket Layer</modulesummary>
<description>
- <p>This module contains interface functions for the SSL.</p>
+ <p>
+ This module contains interface functions for the SSL/TLS protocol.
+ For detailed information about the supported standards see
+ <seealso marker="ssl_app">ssl(6)</seealso>.
+ </p>
</description>
-
- <section>
- <title>SSL</title>
-
- <list type="bulleted">
- <item>For application dependencies see <seealso marker="ssl_app"> ssl(6)</seealso> </item>
- <item>Supported SSL/TLS-versions are SSL-3.0, TLS-1.0,
- TLS-1.1, and TLS-1.2.</item>
- <item>For security reasons SSL-2.0 is not supported.</item>
- <item>For security reasons SSL-3.0 is no longer supported by default,
- but can be configured.</item>
- <item>Ephemeral Diffie-Hellman cipher suites are supported,
- but not Diffie Hellman Certificates cipher suites.</item>
- <item>Elliptic Curve cipher suites are supported if the Crypto
- application supports it and named curves are used.
- </item>
- <item>Export cipher suites are not supported as the
- U.S. lifted its export restrictions in early 2000.</item>
- <item>IDEA cipher suites are not supported as they have
- become deprecated by the latest TLS specification so it is not
- motivated to implement them.</item>
- <item>CRL validation is supported.</item>
- <item>Policy certificate extensions are not supported.</item>
- <item>'Server Name Indication' extension client side
- (RFC 6066, Section 3) is supported.</item>
- </list>
-
- </section>
-
+
<section>
<title>DATA TYPES</title>
<p>The following data types are used in the functions for SSL:</p>
@@ -84,11 +60,12 @@
<seealso marker="kernel:gen_tcp">gen_tcp(3)</seealso> manual pages
in Kernel.</p></item>
- <tag><marker id="type-ssloption"></marker><c>ssloption() =</c></tag>
+ <tag><marker id="type-ssloption"/><c>ssloption() =</c></tag>
<item>
<p><c>{verify, verify_type()}</c></p>
<p><c>| {verify_fun, {fun(), term()}}</c></p>
- <p><c>| {fail_if_no_peer_cert, boolean()} {depth, integer()}</c></p>
+ <p><c>| {fail_if_no_peer_cert, boolean()}</c></p>
+ <p><c>| {depth, integer()}</c></p>
<p><c>| {cert, public_key:der_encoded()}</c></p>
<p><c>| {certfile, path()}</c></p>
<p><c>| {key, {'RSAPrivateKey'| 'DSAPrivateKey' | 'ECPrivateKey'
@@ -159,7 +136,7 @@
<tag><c>sslsocket() =</c></tag>
<item><p>opaque()</p></item>
- <tag><c>protocol() =</c></tag>
+ <tag><marker id="type-protocol"/><c>protocol() =</c></tag>
<item><p><c>sslv3 | tlsv1 | 'tlsv1.1' | 'tlsv1.2'</c></p></item>
<tag><c>ciphers() =</c></tag>
@@ -479,8 +456,8 @@ fun(srp, Username :: string(), UserState :: term()) ->
<p>The negotiated protocol can be retrieved using the <c>negotiated_protocol/1</c> function.</p>
</item>
- <tag><c>{client_preferred_next_protocols, {Precedence :: server | client, ClientPrefs :: [binary()]}}</c></tag>
- <tag><c>{client_preferred_next_protocols, {Precedence :: server | client, ClientPrefs :: [binary()], Default :: binary()}}</c></tag>
+ <tag><c>{client_preferred_next_protocols, {Precedence :: server | client, ClientPrefs :: [binary()]}}</c><br/>
+ <c>{client_preferred_next_protocols, {Precedence :: server | client, ClientPrefs :: [binary()], Default :: binary()}}</c></tag>
<item>
<p>Indicates that the client is to try to perform Next Protocol
Negotiation.</p>
@@ -537,7 +514,6 @@ fun(srp, Username :: string(), UserState :: term()) ->
be supported by the server for the prevention to work.
</p></warning>
</item>
-
</taglist>
</section>
@@ -663,11 +639,6 @@ fun(srp, Username :: string(), UserState :: term()) ->
cipher suite can encipher.
</item>
- <tag><c>{psk_identity, string()}</c></tag>
- <item>Specifies the server identity hint the server presents to the client.
- </item>
- <tag><c>{log_alert, boolean()}</c></tag>
- <item>If false, error reports will not be displayed.</item>
<tag><c>{honor_cipher_order, boolean()}</c></tag>
<item>If true, use the server's preference for cipher selection. If false
(the default), use the client's preference.
@@ -770,6 +741,21 @@ fun(srp, Username :: string(), UserState :: term()) ->
</func>
<func>
+ <name>close(SslSocket, How) -> ok | {ok, port()} | {error, Reason}</name>
+ <fsummary>Closes an SSL connection.</fsummary>
+ <type>
+ <v>SslSocket = sslsocket()</v>
+ <v>How = timeout() | {NewController::pid(), timeout()} </v>
+ <v>Reason = term()</v>
+ </type>
+ <desc><p>Closes or downgrades an SSL connection, in the later case the transport
+ connection will be handed over to the <c>NewController</c> process after reciving
+ the TLS close alert from the peer. The retuned transport socket will have
+ the following options set [{active, false}, {packet, 0}, {mode, binary}].</p>
+ </desc>
+ </func>
+
+ <func>
<name>connection_info(SslSocket) ->
{ok, {ProtocolVersion, CipherSuite}} | {error, Reason}</name>
<fsummary>Returns the Negotiated Protocol version and cipher suite.
diff --git a/lib/ssl/doc/src/ssl_app.xml b/lib/ssl/doc/src/ssl_app.xml
index 2b6dc7e8be..6c82e32a74 100644
--- a/lib/ssl/doc/src/ssl_app.xml
+++ b/lib/ssl/doc/src/ssl_app.xml
@@ -33,7 +33,33 @@
<appsummary>The ssl application provides secure communication over
sockets.</appsummary>
- <description></description>
+ <description>
+ <p>
+ The ssl application is an implementation of the SSL/TLS protocol in Erlang.
+ </p>
+ <list type="bulleted">
+ <item>Supported SSL/TLS-versions are SSL-3.0, TLS-1.0,
+ TLS-1.1, and TLS-1.2.</item>
+ <item>For security reasons SSL-2.0 is not supported.</item>
+ <item>For security reasons SSL-3.0 is no longer supported by default,
+ but can be configured.</item>
+ <item>Ephemeral Diffie-Hellman cipher suites are supported,
+ but not Diffie Hellman Certificates cipher suites.</item>
+ <item>Elliptic Curve cipher suites are supported if the Crypto
+ application supports it and named curves are used.
+ </item>
+ <item>Export cipher suites are not supported as the
+ U.S. lifted its export restrictions in early 2000.</item>
+ <item>IDEA cipher suites are not supported as they have
+ become deprecated by the latest TLS specification so it is not
+ motivated to implement them.</item>
+ <item>CRL validation is supported.</item>
+ <item>Policy certificate extensions are not supported.</item>
+ <item>'Server Name Indication' extension client side
+ (RFC 6066, Section 3) is supported.</item>
+ </list>
+ </description>
+
<section>
<title>DEPENDENCIES</title>
<p>The SSL application uses the <c>public_key</c> and
@@ -58,7 +84,7 @@
<p><c>erl -ssl protocol_version "['tlsv1.2', 'tlsv1.1']"</c></p>
<taglist>
- <tag><c><![CDATA[protocol_version = <seealso marker="kernel:error_logger">ssl:protocol()</seealso> <optional>]]></c>.</tag>
+ <tag><c>protocol_version = </c><seealso marker="ssl#type-protocol">ssl:protocol()</seealso><c><![CDATA[<optional>]]></c></tag>
<item><p>Protocol supported by started clients and
servers. If this option is not set, it defaults to all
protocols currently supported by the SSL application.
@@ -66,17 +92,24 @@
to <c>ssl:connect/[2,3]</c> and <c>ssl:listen/2</c>.</p></item>
<tag><c><![CDATA[session_lifetime = integer() <optional>]]></c></tag>
- <item><p>Lifetime of the session data in seconds.</p></item>
+ <item><p>Maximum lifetime of the session data in seconds.</p></item>
<tag><c><![CDATA[session_cb = atom() <optional>]]></c></tag>
<item><p>Name of the session cache callback module that implements
the <c>ssl_session_cache_api</c> behavior. Defaults to
- <c>ssl_session_cache.erl</c>.</p></item>
+ <c>ssl_session_cache</c>.</p></item>
<tag><c><![CDATA[session_cb_init_args = proplist:proplist() <optional>]]></c></tag>
<item><p>List of extra user-defined arguments to the <c>init</c> function
in the session cache callback module. Defaults to <c>[]</c>.</p></item>
+
+ <tag><c><![CDATA[session_cache_client_max = integer() <optional>]]></c><br/>
+ <c><![CDATA[session_cache_server_max = integer() <optional>]]></c></tag>
+ <item><p>Limits the growth of the clients/servers session cache,
+ if the maximum number of sessions is reached, the current cache entries will
+ be invalidated regardless of their remaining lifetime. Defaults to 1000.
+ </p></item>
<tag><c><![CDATA[ssl_pem_cache_clean = integer() <optional>]]></c></tag>
<item>
@@ -87,12 +120,26 @@
marker="ssl#clear_pem_cache-0">ssl:clear_pem_cache/0</seealso>
</item>
+ <tag><c><![CDATA[alert_timeout = integer() <optional>]]></c></tag>
+ <item>
+ <p>
+ Number of milliseconds between sending of a fatal alert and
+ closing the connection. Waiting a little while improves the
+ peers chances to properly receiving the alert so it may
+ shutdown gracefully. Defaults to 5000 milliseconds.
+ </p>
+ </item>
+
+
</taglist>
</section>
<section>
<title>ERROR LOGGER AND EVENT HANDLERS</title>
- <p>The SSL application uses the default <seealso marker="kernel:error_logger">OTP error logger</seealso> to log unexpected errors and TLS alerts. The logging of TLS alerts may be turned off with the <c>log_alert</c> option. </p>
+ <p>The SSL application uses the default <seealso
+ marker="kernel:error_logger">OTP error logger</seealso> to log
+ unexpected errors and TLS alerts. The logging of TLS alerts may be
+ turned off with the <c>log_alert</c> option. </p>
</section>
<section>
diff --git a/lib/ssl/doc/src/ssl_crl_cache_api.xml b/lib/ssl/doc/src/ssl_crl_cache_api.xml
index 71c1c61fe8..03ac010bfe 100644
--- a/lib/ssl/doc/src/ssl_crl_cache_api.xml
+++ b/lib/ssl/doc/src/ssl_crl_cache_api.xml
@@ -84,9 +84,9 @@
<v> CRLs = [<seealso
marker="public_key:public_key">public_key:der_encoded()</seealso>] </v>
</type>
- <desc> <p>Lookup the CRLs belonging to the distribution point <c> Distributionpoint</c>. </p>
+ <desc> <p>Lookup the CRLs belonging to the distribution point <c> Distributionpoint</c>.
This function may choose to only look in the cache or to follow distribution point
- links depending on how the cache is administrated.
+ links depending on how the cache is administrated. </p>
</desc>
</func>
@@ -103,4 +103,4 @@
</desc>
</func>
</funcs>
-</erlref> \ No newline at end of file
+</erlref>
diff --git a/lib/ssl/doc/src/ssl_session_cache_api.xml b/lib/ssl/doc/src/ssl_session_cache_api.xml
index bd9330056d..b85d8fb284 100644
--- a/lib/ssl/doc/src/ssl_session_cache_api.xml
+++ b/lib/ssl/doc/src/ssl_session_cache_api.xml
@@ -31,9 +31,13 @@
<module>ssl_session_cache_api</module>
<modulesummary>TLS session cache API</modulesummary>
- <description>Defines the API for the TLS session cache so
- that the data storage scheme can be replaced by
- defining a new callback module implementing this API.</description>
+ <description>
+ <p>
+ Defines the API for the TLS session cache so
+ that the data storage scheme can be replaced by
+ defining a new callback module implementing this API.
+ </p>
+ </description>
<section>
<title>DATA TYPES</title>
diff --git a/lib/ssl/src/dtls_connection.erl b/lib/ssl/src/dtls_connection.erl
index 78662e0ea2..153d3fef48 100644
--- a/lib/ssl/src/dtls_connection.erl
+++ b/lib/ssl/src/dtls_connection.erl
@@ -145,7 +145,7 @@ init([Role, Host, Port, Socket, {SSLOpts0, _} = Options, User, CbInfo]) ->
process_flag(trap_exit, true),
State0 = initial_state(Role, Host, Port, Socket, Options, User, CbInfo),
Handshake = ssl_handshake:init_handshake_history(),
- TimeStamp = calendar:datetime_to_gregorian_seconds({date(), time()}),
+ TimeStamp = erlang:monotonic_time(),
try ssl_config:init(SSLOpts0, Role) of
{ok, Ref, CertDbHandle, FileRefHandle, CacheHandle, CRLDbInfo, OwnCert, Key, DHParams} ->
Session = State0#state.session,
diff --git a/lib/ssl/src/inet_tls_dist.erl b/lib/ssl/src/inet_tls_dist.erl
index b6e62a18c9..6fe99a81c5 100644
--- a/lib/ssl/src/inet_tls_dist.erl
+++ b/lib/ssl/src/inet_tls_dist.erl
@@ -30,7 +30,7 @@
childspecs() ->
{ok, [{ssl_dist_sup,{ssl_dist_sup, start_link, []},
- permanent, 2000, worker, [ssl_dist_sup]}]}.
+ permanent, infinity, supervisor, [ssl_dist_sup]}]}.
select(Node) ->
case split_node(atom_to_list(Node), $@, []) of
@@ -76,23 +76,23 @@ do_setup(Kernel, Node, Type, MyNode, LongOrShortNames, SetupTime) ->
Timer, Version, Ip, TcpPort, Address,
Type),
dist_util:handshake_we_started(HSData);
- _ ->
+ Other ->
%% Other Node may have closed since
%% port_please !
?trace("other node (~p) "
"closed since port_please.~n",
[Node]),
- ?shutdown(Node)
+ ?shutdown2(Node, {shutdown, {connect_failed, Other}})
end;
- _ ->
+ Other ->
?trace("port_please (~p) "
"failed.~n", [Node]),
- ?shutdown(Node)
+ ?shutdown2(Node, {shutdown, {port_please_failed, Other}})
end;
- _Other ->
+ Other ->
?trace("inet_getaddr(~p) "
"failed (~p).~n", [Node,Other]),
- ?shutdown(Node)
+ ?shutdown2(Node, {shutdown, {inet_getaddr_failed, Other}})
end.
close(Socket) ->
diff --git a/lib/ssl/src/ssl.app.src b/lib/ssl/src/ssl.app.src
index be8ef6f85f..619ab7b610 100644
--- a/lib/ssl/src/ssl.app.src
+++ b/lib/ssl/src/ssl.app.src
@@ -54,6 +54,6 @@
{env, []},
{mod, {ssl_app, []}},
{runtime_dependencies, ["stdlib-2.0","public_key-1.0","kernel-3.0",
- "erts-6.0","crypto-3.3", "inets-5.10.7"]}]}.
+ "erts-7.0","crypto-3.3", "inets-5.10.7"]}]}.
diff --git a/lib/ssl/src/ssl.appup.src b/lib/ssl/src/ssl.appup.src
index 1476336039..11728128c4 100644
--- a/lib/ssl/src/ssl.appup.src
+++ b/lib/ssl/src/ssl.appup.src
@@ -1,12 +1,14 @@
%% -*- erlang -*-
{"%VSN%",
[
+ {<<"7\\..*">>, [{restart_application, ssl}]},
{<<"6\\..*">>, [{restart_application, ssl}]},
{<<"5\\..*">>, [{restart_application, ssl}]},
{<<"4\\..*">>, [{restart_application, ssl}]},
{<<"3\\..*">>, [{restart_application, ssl}]}
],
[
+ {<<"7\\..*">>, [{restart_application, ssl}]},
{<<"6\\..*">>, [{restart_application, ssl}]},
{<<"5\\..*">>, [{restart_application, ssl}]},
{<<"4\\..*">>, [{restart_application, ssl}]},
diff --git a/lib/ssl/src/ssl.erl b/lib/ssl/src/ssl.erl
index 9f2af73204..6551308935 100644
--- a/lib/ssl/src/ssl.erl
+++ b/lib/ssl/src/ssl.erl
@@ -34,7 +34,7 @@
listen/2, transport_accept/1, transport_accept/2,
ssl_accept/1, ssl_accept/2, ssl_accept/3,
controlling_process/2, peername/1, peercert/1, sockname/1,
- close/1, shutdown/2, recv/2, recv/3, send/2, getopts/2, setopts/2
+ close/1, close/2, shutdown/2, recv/2, recv/3, send/2, getopts/2, setopts/2
]).
%% SSL/TLS protocol handling
-export([cipher_suites/0, cipher_suites/1, suite_definition/1,
@@ -99,7 +99,8 @@ stop() ->
connect(Socket, SslOptions) when is_port(Socket) ->
connect(Socket, SslOptions, infinity).
-connect(Socket, SslOptions0, Timeout) when is_port(Socket) ->
+connect(Socket, SslOptions0, Timeout) when is_port(Socket),
+ (is_integer(Timeout) andalso Timeout > 0) or (Timeout == infinity) ->
{Transport,_,_,_} = proplists:get_value(cb_info, SslOptions0,
{gen_tcp, tcp, tcp_closed, tcp_error}),
EmulatedOptions = ssl_socket:emulated_options(),
@@ -125,7 +126,7 @@ connect(Socket, SslOptions0, Timeout) when is_port(Socket) ->
connect(Host, Port, Options) ->
connect(Host, Port, Options, infinity).
-connect(Host, Port, Options, Timeout) ->
+connect(Host, Port, Options, Timeout) when (is_integer(Timeout) andalso Timeout > 0) or (Timeout == infinity) ->
try handle_options(Options) of
{ok, Config} ->
do_connect(Host,Port,Config,Timeout)
@@ -175,7 +176,7 @@ transport_accept(#sslsocket{pid = {ListenSocket,
#config{transport_info = {Transport,_,_, _} =CbInfo,
connection_cb = ConnectionCb,
ssl = SslOpts,
- emulated = Tracker}}}, Timeout) ->
+ emulated = Tracker}}}, Timeout) when (is_integer(Timeout) andalso Timeout > 0) or (Timeout == infinity) ->
case Transport:accept(ListenSocket, Timeout) of
{ok, Socket} ->
{ok, EmOpts} = ssl_socket:get_emulated_opts(Tracker),
@@ -208,15 +209,16 @@ transport_accept(#sslsocket{pid = {ListenSocket,
ssl_accept(ListenSocket) ->
ssl_accept(ListenSocket, infinity).
-ssl_accept(#sslsocket{} = Socket, Timeout) ->
+ssl_accept(#sslsocket{} = Socket, Timeout) when (is_integer(Timeout) andalso Timeout > 0) or (Timeout == infinity) ->
ssl_connection:handshake(Socket, Timeout);
ssl_accept(ListenSocket, SslOptions) when is_port(ListenSocket) ->
ssl_accept(ListenSocket, SslOptions, infinity).
-ssl_accept(#sslsocket{} = Socket, [], Timeout) ->
+ssl_accept(#sslsocket{} = Socket, [], Timeout) when (is_integer(Timeout) andalso Timeout > 0) or (Timeout == infinity)->
ssl_accept(#sslsocket{} = Socket, Timeout);
-ssl_accept(#sslsocket{fd = {_, _, _, Tracker}} = Socket, SslOpts0, Timeout) ->
+ssl_accept(#sslsocket{fd = {_, _, _, Tracker}} = Socket, SslOpts0, Timeout) when
+ (is_integer(Timeout) andalso Timeout > 0) or (Timeout == infinity)->
try
{ok, EmOpts, InheritedSslOpts} = ssl_socket:get_all_opts(Tracker),
SslOpts = handle_options(SslOpts0, InheritedSslOpts),
@@ -224,7 +226,8 @@ ssl_accept(#sslsocket{fd = {_, _, _, Tracker}} = Socket, SslOpts0, Timeout) ->
catch
Error = {error, _Reason} -> Error
end;
-ssl_accept(Socket, SslOptions, Timeout) when is_port(Socket) ->
+ssl_accept(Socket, SslOptions, Timeout) when is_port(Socket),
+ (is_integer(Timeout) andalso Timeout > 0) or (Timeout == infinity) ->
{Transport,_,_,_} =
proplists:get_value(cb_info, SslOptions, {gen_tcp, tcp, tcp_closed, tcp_error}),
EmulatedOptions = ssl_socket:emulated_options(),
@@ -247,11 +250,27 @@ ssl_accept(Socket, SslOptions, Timeout) when is_port(Socket) ->
%% Description: Close an ssl connection
%%--------------------------------------------------------------------
close(#sslsocket{pid = Pid}) when is_pid(Pid) ->
- ssl_connection:close(Pid);
+ ssl_connection:close(Pid, {close, ?DEFAULT_TIMEOUT});
close(#sslsocket{pid = {ListenSocket, #config{transport_info={Transport,_, _, _}}}}) ->
Transport:close(ListenSocket).
%%--------------------------------------------------------------------
+-spec close(#sslsocket{}, integer() | {pid(), integer()}) -> term().
+%%
+%% Description: Close an ssl connection
+%%--------------------------------------------------------------------
+close(#sslsocket{pid = TLSPid},
+ {Pid, Timeout} = DownGrade) when is_pid(TLSPid),
+ is_pid(Pid),
+ (is_integer(Timeout) andalso Timeout > 0) or (Timeout == infinity) ->
+ ssl_connection:close(TLSPid, {close, DownGrade});
+close(#sslsocket{pid = TLSPid}, Timeout) when is_pid(TLSPid),
+ (is_integer(Timeout) andalso Timeout > 0) or (Timeout == infinity) ->
+ ssl_connection:close(TLSPid, {close, Timeout});
+close(#sslsocket{pid = {ListenSocket, #config{transport_info={Transport,_, _, _}}}}, _) ->
+ Transport:close(ListenSocket).
+
+%%--------------------------------------------------------------------
-spec send(#sslsocket{}, iodata()) -> ok | {error, reason()}.
%%
%% Description: Sends data over the ssl connection
@@ -269,7 +288,8 @@ send(#sslsocket{pid = {ListenSocket, #config{transport_info={Transport, _, _, _}
%%--------------------------------------------------------------------
recv(Socket, Length) ->
recv(Socket, Length, infinity).
-recv(#sslsocket{pid = Pid}, Length, Timeout) when is_pid(Pid) ->
+recv(#sslsocket{pid = Pid}, Length, Timeout) when is_pid(Pid),
+ (is_integer(Timeout) andalso Timeout > 0) or (Timeout == infinity)->
ssl_connection:recv(Pid, Length, Timeout);
recv(#sslsocket{pid = {Listen,
#config{transport_info = {Transport, _, _, _}}}}, _,_) when is_port(Listen)->
@@ -804,6 +824,7 @@ validate_option(key, {KeyType, Value}) when is_binary(Value),
KeyType == dsa; %% Backwards compatibility
KeyType == 'RSAPrivateKey';
KeyType == 'DSAPrivateKey';
+ KeyType == 'ECPrivateKey';
KeyType == 'PrivateKeyInfo' ->
{KeyType, Value};
@@ -1169,6 +1190,8 @@ assert_proplist([]) ->
assert_proplist([{Key,_} | Rest]) when is_atom(Key) ->
assert_proplist(Rest);
%% Handle exceptions
+assert_proplist([{raw,_,_,_} | Rest]) ->
+ assert_proplist(Rest);
assert_proplist([inet | Rest]) ->
assert_proplist(Rest);
assert_proplist([inet6 | Rest]) ->
diff --git a/lib/ssl/src/ssl_connection.erl b/lib/ssl/src/ssl_connection.erl
index 5b754c16bc..241871dc38 100644
--- a/lib/ssl/src/ssl_connection.erl
+++ b/lib/ssl/src/ssl_connection.erl
@@ -41,7 +41,7 @@
socket_control/4, socket_control/5]).
%% User Events
--export([send/2, recv/3, close/1, shutdown/2,
+-export([send/2, recv/3, close/2, shutdown/2,
new_user/2, get_opts/2, set_opts/2, session_info/1,
peer_certificate/1, renegotiation/1, negotiated_protocol/1, prf/5,
connection_information/1
@@ -171,18 +171,19 @@ connection_information(Pid) when is_pid(Pid) ->
sync_send_all_state_event(Pid, connection_information).
%%--------------------------------------------------------------------
--spec close(pid()) -> ok | {error, reason()}.
+-spec close(pid(), {close, Timeout::integer() |
+ {NewController::pid(), Timeout::integer()}}) ->
+ ok | {ok, port()} | {error, reason()}.
%%
%% Description: Close an ssl connection
%%--------------------------------------------------------------------
-close(ConnectionPid) ->
- case sync_send_all_state_event(ConnectionPid, close) of
+close(ConnectionPid, How) ->
+ case sync_send_all_state_event(ConnectionPid, How) of
{error, closed} ->
ok;
Other ->
Other
end.
-
%%--------------------------------------------------------------------
-spec shutdown(pid(), atom()) -> ok | {error, reason()}.
%%
@@ -706,12 +707,12 @@ handle_sync_event({start, Timeout}, StartFrom, StateName, #state{role = Role, s
{stop, normal, {error, Error}, State0}
end;
-handle_sync_event(close, _, StateName, #state{protocol_cb = Connection} = State) ->
- %% Run terminate before returning
- %% so that the reuseaddr inet-option will work
- %% as intended.
- (catch Connection:terminate(user_close, StateName, State)),
- {stop, normal, ok, State#state{terminated = true}};
+handle_sync_event({close, _} = Close, _, StateName, #state{protocol_cb = Connection} = State) ->
+ %% Run terminate before returning so that the reuseaddr
+ %% inet-option and possible downgrade will work as intended.
+ Result = Connection:terminate(Close, StateName, State),
+ {stop, normal, Result, State#state{terminated = true}};
+
handle_sync_event({shutdown, How0}, _, StateName,
#state{transport_cb = Transport,
negotiated_version = Version,
@@ -901,41 +902,46 @@ terminate(_, _, #state{terminated = true}) ->
%% we want to guarantee that Transport:close has been called
%% when ssl:close/1 returns.
ok;
-terminate({shutdown, transport_closed}, StateName, #state{send_queue = SendQueue,
- renegotiation = Renegotiate} = State) ->
- handle_unrecv_data(StateName, State),
+terminate({shutdown, transport_closed} = Reason,
+ _StateName, #state{send_queue = SendQueue, protocol_cb = Connection,
+ socket = Socket, transport_cb = Transport,
+ renegotiation = Renegotiate} = State) ->
handle_trusted_certs_db(State),
notify_senders(SendQueue),
- notify_renegotiater(Renegotiate);
-
-terminate({shutdown, own_alert}, _StateName, #state{send_queue = SendQueue,
- renegotiation = Renegotiate} = State) ->
+ notify_renegotiater(Renegotiate),
+ Connection:close(Reason, Socket, Transport, undefined, undefined);
+terminate({shutdown, own_alert}, _StateName, #state{send_queue = SendQueue, protocol_cb = Connection,
+ socket = Socket, transport_cb = Transport,
+ renegotiation = Renegotiate} = State) ->
handle_trusted_certs_db(State),
notify_senders(SendQueue),
- notify_renegotiater(Renegotiate);
+ notify_renegotiater(Renegotiate),
+ case application:get_env(ssl, alert_timeout) of
+ {ok, Timeout} when is_integer(Timeout) ->
+ Connection:close({timeout, Timeout}, Socket, Transport, undefined, undefined);
+ _ ->
+ Connection:close({timeout, ?DEFAULT_TIMEOUT}, Socket, Transport, undefined, undefined)
+ end;
terminate(Reason, connection, #state{negotiated_version = Version,
protocol_cb = Connection,
- connection_states = ConnectionStates,
+ connection_states = ConnectionStates0,
+ ssl_options = #ssl_options{padding_check = Check},
transport_cb = Transport, socket = Socket,
send_queue = SendQueue, renegotiation = Renegotiate} = State) ->
handle_trusted_certs_db(State),
notify_senders(SendQueue),
notify_renegotiater(Renegotiate),
- BinAlert = terminate_alert(Reason, Version, ConnectionStates),
+ {BinAlert, ConnectionStates} = terminate_alert(Reason, Version, ConnectionStates0),
Transport:send(Socket, BinAlert),
- case Connection of
- tls_connection ->
- tls_connection:workaround_transport_delivery_problems(Socket, Transport);
- _ ->
- ok
- end;
-terminate(_Reason, _StateName, #state{transport_cb = Transport,
+ Connection:close(Reason, Socket, Transport, ConnectionStates, Check);
+
+terminate(Reason, _StateName, #state{transport_cb = Transport, protocol_cb = Connection,
socket = Socket, send_queue = SendQueue,
renegotiation = Renegotiate} = State) ->
handle_trusted_certs_db(State),
notify_senders(SendQueue),
notify_renegotiater(Renegotiate),
- Transport:close(Socket).
+ Connection:close(Reason, Socket, Transport, undefined, undefined).
format_status(normal, [_, State]) ->
[{data, [{"StateData", State}]}];
@@ -968,7 +974,7 @@ ssl_config(Opts, Role, State) ->
{ok, Ref, CertDbHandle, FileRefHandle, CacheHandle, CRLDbInfo, OwnCert, Key, DHParams} =
ssl_config:init(Opts, Role),
Handshake = ssl_handshake:init_handshake_history(),
- TimeStamp = calendar:datetime_to_gregorian_seconds({date(), time()}),
+ TimeStamp = erlang:monotonic_time(),
Session = State#state.session,
State#state{tls_handshake_history = Handshake,
session = Session#session{own_certificate = OwnCert,
@@ -1758,37 +1764,24 @@ get_timeout(#state{ssl_options=#ssl_options{hibernate_after = undefined}}) ->
get_timeout(#state{ssl_options=#ssl_options{hibernate_after = HibernateAfter}}) ->
HibernateAfter.
-terminate_alert(Reason, Version, ConnectionStates) when Reason == normal;
- Reason == user_close ->
- {BinAlert, _} = ssl_alert:encode(?ALERT_REC(?WARNING, ?CLOSE_NOTIFY),
- Version, ConnectionStates),
- BinAlert;
-terminate_alert({shutdown, _}, Version, ConnectionStates) ->
- {BinAlert, _} = ssl_alert:encode(?ALERT_REC(?WARNING, ?CLOSE_NOTIFY),
- Version, ConnectionStates),
- BinAlert;
+terminate_alert(normal, Version, ConnectionStates) ->
+ ssl_alert:encode(?ALERT_REC(?WARNING, ?CLOSE_NOTIFY),
+ Version, ConnectionStates);
+terminate_alert({Reason, _}, Version, ConnectionStates) when Reason == close;
+ Reason == shutdown ->
+ ssl_alert:encode(?ALERT_REC(?WARNING, ?CLOSE_NOTIFY),
+ Version, ConnectionStates);
terminate_alert(_, Version, ConnectionStates) ->
- {BinAlert, _} = ssl_alert:encode(?ALERT_REC(?FATAL, ?INTERNAL_ERROR),
- Version, ConnectionStates),
- BinAlert.
-
-handle_unrecv_data(StateName, #state{socket = Socket, transport_cb = Transport,
- protocol_cb = Connection} = State) ->
- ssl_socket:setopts(Transport, Socket, [{active, false}]),
- case Transport:recv(Socket, 0, 0) of
- {error, closed} ->
- ok;
- {ok, Data} ->
- Connection:handle_close_alert(Data, StateName, State)
- end.
+ ssl_alert:encode(?ALERT_REC(?FATAL, ?INTERNAL_ERROR),
+ Version, ConnectionStates).
handle_trusted_certs_db(#state{ssl_options = #ssl_options{cacertfile = <<>>, cacerts = []}}) ->
%% No trusted certs specified
ok;
handle_trusted_certs_db(#state{cert_db_ref = Ref,
cert_db = CertDb,
- ssl_options = #ssl_options{cacertfile = <<>>}}) ->
+ ssl_options = #ssl_options{cacertfile = <<>>}}) when CertDb =/= undefined ->
%% Certs provided as DER directly can not be shared
%% with other connections and it is safe to delete them when the connection ends.
ssl_pkix_db:remove_trusted_certs(Ref, CertDb);
diff --git a/lib/ssl/src/ssl_connection.hrl b/lib/ssl/src/ssl_connection.hrl
index 9a58f2b8f7..bb41ef2b62 100644
--- a/lib/ssl/src/ssl_connection.hrl
+++ b/lib/ssl/src/ssl_connection.hrl
@@ -48,27 +48,28 @@
socket_options :: #socket_options{},
connection_states :: #connection_states{} | secret_printout(),
protocol_buffers :: term() | secret_printout() , %% #protocol_buffers{} from tls_record.hrl or dtls_recor.hrl
- tls_handshake_history :: ssl_handshake:ssl_handshake_history() | secret_printout(),
- cert_db :: reference(),
+ tls_handshake_history :: ssl_handshake:ssl_handshake_history() | secret_printout()
+ | 'undefined',
+ cert_db :: reference() | 'undefined',
session :: #session{} | secret_printout(),
session_cache :: db_handle(),
session_cache_cb :: atom(),
crl_db :: term(),
- negotiated_version :: ssl_record:ssl_version(),
+ negotiated_version :: ssl_record:ssl_version() | 'undefined',
client_certificate_requested = false :: boolean(),
key_algorithm :: ssl_cipher:key_algo(),
hashsign_algorithm = {undefined, undefined},
cert_hashsign_algorithm,
- public_key_info :: ssl_handshake:public_key_info(),
- private_key :: public_key:private_key() | secret_printout(),
+ public_key_info :: ssl_handshake:public_key_info() | 'undefined',
+ private_key :: public_key:private_key() | secret_printout() | 'undefined',
diffie_hellman_params:: #'DHParameter'{} | undefined | secret_printout(),
diffie_hellman_keys :: {PublicKey :: binary(), PrivateKey :: binary()} | #'ECPrivateKey'{} | undefined | secret_printout(),
- psk_identity :: binary(), % server psk identity hint
- srp_params :: #srp_user{} | secret_printout(),
- srp_keys ::{PublicKey :: binary(), PrivateKey :: binary()} | secret_printout(),
- premaster_secret :: binary() | secret_printout() ,
+ psk_identity :: binary() | 'undefined', % server psk identity hint
+ srp_params :: #srp_user{} | secret_printout() | 'undefined',
+ srp_keys ::{PublicKey :: binary(), PrivateKey :: binary()} | secret_printout() | 'undefined',
+ premaster_secret :: binary() | secret_printout() | 'undefined',
file_ref_db :: db_handle(),
- cert_db_ref :: certdb_ref(),
+ cert_db_ref :: certdb_ref() | 'undefined',
bytes_to_read :: undefined | integer(), %% bytes to read in passive mode
user_data_buffer :: undefined | binary() | secret_printout(),
renegotiation :: undefined | {boolean(), From::term() | internal | peer},
@@ -81,7 +82,7 @@
expecting_finished = false ::boolean(),
negotiated_protocol = undefined :: undefined | binary(),
client_ecc, % {Curves, PointFmt}
- tracker :: pid(), %% Tracker process for listen socket
+ tracker :: pid() | 'undefined', %% Tracker process for listen socket
sni_hostname = undefined
}).
diff --git a/lib/ssl/src/ssl_dist_sup.erl b/lib/ssl/src/ssl_dist_sup.erl
index aa1fa57db8..435ad27a44 100644
--- a/lib/ssl/src/ssl_dist_sup.erl
+++ b/lib/ssl/src/ssl_dist_sup.erl
@@ -70,7 +70,7 @@ connection_manager_child_spec() ->
Name = ssl_connection_dist,
StartFunc = {tls_connection_sup, start_link_dist, []},
Restart = permanent,
- Shutdown = 4000,
+ Shutdown = infinity,
Modules = [tls_connection_sup],
Type = supervisor,
{Name, StartFunc, Restart, Shutdown, Type, Modules}.
diff --git a/lib/ssl/src/ssl_internal.hrl b/lib/ssl/src/ssl_internal.hrl
index 3851b2bc6e..913746598f 100644
--- a/lib/ssl/src/ssl_internal.hrl
+++ b/lib/ssl/src/ssl_internal.hrl
@@ -78,6 +78,9 @@
-define(ALL_DATAGRAM_SUPPORTED_VERSIONS, ['dtlsv1.2', dtlsv1]).
-define(MIN_DATAGRAM_SUPPORTED_VERSIONS, ['dtlsv1.2', dtlsv1]).
+-define('24H_in_msec', 86400000).
+-define('24H_in_sec', 86400).
+
-record(ssl_options, {
protocol :: tls | dtls,
versions :: [ssl_record:ssl_version()], %% ssl_record:atom_version() in API
@@ -90,16 +93,16 @@
validate_extensions_fun,
depth :: integer(),
certfile :: binary(),
- cert :: public_key:der_encoded() | secret_printout(),
+ cert :: public_key:der_encoded() | secret_printout() | 'undefined',
keyfile :: binary(),
- key :: {'RSAPrivateKey' | 'DSAPrivateKey' | 'ECPrivateKey' | 'PrivateKeyInfo', public_key:der_encoded()} | secret_printout(),
- password :: string() | secret_printout(),
- cacerts :: [public_key:der_encoded()] | secret_printout(),
+ key :: {'RSAPrivateKey' | 'DSAPrivateKey' | 'ECPrivateKey' | 'PrivateKeyInfo', public_key:der_encoded()} | secret_printout() | 'undefined',
+ password :: string() | secret_printout() | 'undefined',
+ cacerts :: [public_key:der_encoded()] | secret_printout() | 'undefined',
cacertfile :: binary(),
dh :: public_key:der_encoded() | secret_printout(),
- dhfile :: binary() | secret_printout(),
+ dhfile :: binary() | secret_printout() | 'undefined',
user_lookup_fun, % server option, fun to lookup the user
- psk_identity :: binary() | secret_printout() ,
+ psk_identity :: binary() | secret_printout() | 'undefined',
srp_identity, % client option {User, Password}
ciphers, %
%% Local policy for the server if it want's to reuse the session
@@ -115,7 +118,7 @@
%% undefined if not hibernating, or number of ms of
%% inactivity after which ssl_connection will go into
%% hibernation
- hibernate_after :: boolean(),
+ hibernate_after :: boolean() | 'undefined',
%% This option should only be set to true by inet_tls_dist
erl_dist = false :: boolean(),
alpn_advertised_protocols = undefined :: [binary()] | undefined ,
diff --git a/lib/ssl/src/ssl_manager.erl b/lib/ssl/src/ssl_manager.erl
index 2e05ba5aa5..00e95f5c5b 100644
--- a/lib/ssl/src/ssl_manager.erl
+++ b/lib/ssl/src/ssl_manager.erl
@@ -46,25 +46,27 @@
-include_lib("kernel/include/file.hrl").
-record(state, {
- session_cache_client,
- session_cache_server,
- session_cache_cb,
- session_lifetime,
- certificate_db,
- session_validation_timer,
+ session_cache_client :: db_handle(),
+ session_cache_server :: db_handle(),
+ session_cache_cb :: atom(),
+ session_lifetime :: integer(),
+ certificate_db :: db_handle(),
+ session_validation_timer :: reference(),
last_delay_timer = {undefined, undefined},%% Keep for testing purposes
- last_pem_check,
- clear_pem_cache
+ last_pem_check :: erlang:timestamp(),
+ clear_pem_cache :: integer(),
+ session_cache_client_max :: integer(),
+ session_cache_server_max :: integer(),
+ session_server_invalidator :: undefined | pid(),
+ session_client_invalidator :: undefined | pid()
}).
--define('24H_in_msec', 86400000).
--define('24H_in_sec', 86400).
-define(GEN_UNIQUE_ID_MAX_TRIES, 10).
-define(SESSION_VALIDATION_INTERVAL, 60000).
-define(CLEAR_PEM_CACHE, 120000).
-define(CLEAN_SESSION_DB, 60000).
-define(CLEAN_CERT_DB, 500).
--define(NOT_TO_BIG, 10).
+-define(DEFAULT_MAX_SESSION_CACHE, 1000).
%%====================================================================
%% API
@@ -89,7 +91,8 @@ manager_name(dist) ->
%%--------------------------------------------------------------------
start_link(Opts) ->
DistMangerName = manager_name(normal),
- gen_server:start_link({local, DistMangerName}, ?MODULE, [DistMangerName, Opts], []).
+ gen_server:start_link({local, DistMangerName},
+ ?MODULE, [DistMangerName, Opts], []).
%%--------------------------------------------------------------------
-spec start_link_dist(list()) -> {ok, pid()} | ignore | {error, term()}.
@@ -99,7 +102,8 @@ start_link(Opts) ->
%%--------------------------------------------------------------------
start_link_dist(Opts) ->
DistMangerName = manager_name(dist),
- gen_server:start_link({local, DistMangerName}, ?MODULE, [DistMangerName, Opts], []).
+ gen_server:start_link({local, DistMangerName},
+ ?MODULE, [DistMangerName, Opts], []).
%%--------------------------------------------------------------------
-spec connection_init(binary()| {der, list()}, client | server,
@@ -169,7 +173,8 @@ new_session_id(Port) ->
%% be called by ssl-connection processes.
%%--------------------------------------------------------------------
clean_cert_db(Ref, File) ->
- erlang:send_after(?CLEAN_CERT_DB, get(ssl_manager), {clean_cert_db, Ref, File}),
+ erlang:send_after(?CLEAN_CERT_DB, get(ssl_manager),
+ {clean_cert_db, Ref, File}),
ok.
%%--------------------------------------------------------------------
@@ -237,10 +242,12 @@ init([Name, Opts]) ->
SessionLifeTime =
proplists:get_value(session_lifetime, Opts, ?'24H_in_sec'),
CertDb = ssl_pkix_db:create(),
- ClientSessionCache = CacheCb:init([{role, client} |
- proplists:get_value(session_cb_init_args, Opts, [])]),
- ServerSessionCache = CacheCb:init([{role, server} |
- proplists:get_value(session_cb_init_args, Opts, [])]),
+ ClientSessionCache =
+ CacheCb:init([{role, client} |
+ proplists:get_value(session_cb_init_args, Opts, [])]),
+ ServerSessionCache =
+ CacheCb:init([{role, server} |
+ proplists:get_value(session_cb_init_args, Opts, [])]),
Timer = erlang:send_after(SessionLifeTime * 1000 + 5000,
self(), validate_sessions),
Interval = pem_check_interval(),
@@ -252,7 +259,11 @@ init([Name, Opts]) ->
session_lifetime = SessionLifeTime,
session_validation_timer = Timer,
last_pem_check = os:timestamp(),
- clear_pem_cache = Interval
+ clear_pem_cache = Interval,
+ session_cache_client_max =
+ max_session_cache_size(session_cache_client_max),
+ session_cache_server_max =
+ max_session_cache_size(session_cache_server_max)
}}.
%%--------------------------------------------------------------------
@@ -269,7 +280,8 @@ init([Name, Opts]) ->
handle_call({{connection_init, <<>>, Role, {CRLCb, UserCRLDb}}, _Pid}, _From,
#state{certificate_db = [CertDb, FileRefDb, PemChace | _] = Db} = State) ->
Ref = make_ref(),
- Result = {ok, Ref, CertDb, FileRefDb, PemChace, session_cache(Role, State), {CRLCb, crl_db_info(Db, UserCRLDb)}},
+ Result = {ok, Ref, CertDb, FileRefDb, PemChace,
+ session_cache(Role, State), {CRLCb, crl_db_info(Db, UserCRLDb)}},
{reply, Result, State#state{certificate_db = Db}};
handle_call({{connection_init, Trustedcerts, Role, {CRLCb, UserCRLDb}}, Pid}, _From,
@@ -307,7 +319,8 @@ handle_call({{cache_pem,File}, _Pid}, _,
_:Reason ->
{reply, {error, Reason}, State}
end;
-handle_call({unconditionally_clear_pem_cache, _},_, #state{certificate_db = [_,_,PemChace | _]} = State) ->
+handle_call({unconditionally_clear_pem_cache, _},_,
+ #state{certificate_db = [_,_,PemChace | _]} = State) ->
ssl_pkix_db:clear(PemChace),
{reply, ok, State}.
@@ -319,27 +332,12 @@ handle_call({unconditionally_clear_pem_cache, _},_, #state{certificate_db = [_,_
%%
%% Description: Handling cast messages
%%--------------------------------------------------------------------
-handle_cast({register_session, Host, Port, Session},
- #state{session_cache_client = Cache,
- session_cache_cb = CacheCb} = State) ->
- TimeStamp = calendar:datetime_to_gregorian_seconds({date(), time()}),
- NewSession = Session#session{time_stamp = TimeStamp},
-
- case CacheCb:select_session(Cache, {Host, Port}) of
- no_session ->
- CacheCb:update(Cache, {{Host, Port},
- NewSession#session.session_id}, NewSession);
- Sessions ->
- register_unique_session(Sessions, NewSession, CacheCb, Cache, {Host, Port})
- end,
+handle_cast({register_session, Host, Port, Session}, State0) ->
+ State = ssl_client_register_session(Host, Port, Session, State0),
{noreply, State};
-handle_cast({register_session, Port, Session},
- #state{session_cache_server = Cache,
- session_cache_cb = CacheCb} = State) ->
- TimeStamp = calendar:datetime_to_gregorian_seconds({date(), time()}),
- NewSession = Session#session{time_stamp = TimeStamp},
- CacheCb:update(Cache, {Port, NewSession#session.session_id}, NewSession),
+handle_cast({register_session, Port, Session}, State0) ->
+ State = server_register_session(Port, Session, State0),
{noreply, State};
handle_cast({invalidate_session, Host, Port,
@@ -413,10 +411,10 @@ handle_info({clean_cert_db, Ref, File},
end,
{noreply, State};
-handle_info({'EXIT', _, _}, State) ->
- %% Session validator died!! Do we need to take any action?
- %% maybe error log
- {noreply, State};
+handle_info({'EXIT', Pid, _}, #state{session_client_invalidator = Pid} = State) ->
+ {noreply, State#state{session_client_invalidator = undefined}};
+handle_info({'EXIT', Pid, _}, #state{session_server_invalidator = Pid} = State) ->
+ {noreply, State#state{session_server_invalidator = undefined}};
handle_info(_Info, State) ->
{noreply, State}.
@@ -497,7 +495,15 @@ delay_time() ->
?CLEAN_SESSION_DB
end.
-invalidate_session(Cache, CacheCb, Key, Session, #state{last_delay_timer = LastTimer} = State) ->
+max_session_cache_size(CacheType) ->
+ case application:get_env(ssl, CacheType) of
+ {ok, Size} when is_integer(Size) ->
+ Size;
+ _ ->
+ ?DEFAULT_MAX_SESSION_CACHE
+ end.
+
+invalidate_session(Cache, CacheCb, Key, Session, State) ->
case CacheCb:lookup(Cache, Key) of
undefined -> %% Session is already invalidated
{noreply, State};
@@ -505,15 +511,23 @@ invalidate_session(Cache, CacheCb, Key, Session, #state{last_delay_timer = LastT
CacheCb:delete(Cache, Key),
{noreply, State};
_ ->
- %% When a registered session is invalidated we need to wait a while before deleting
- %% it as there might be pending connections that rightfully needs to look
- %% up the session data but new connections should not get to use this session.
- CacheCb:update(Cache, Key, Session#session{is_resumable = false}),
- TRef =
- erlang:send_after(delay_time(), self(), {delayed_clean_session, Key, Cache}),
- {noreply, State#state{last_delay_timer = last_delay_timer(Key, TRef, LastTimer)}}
+ delayed_invalidate_session(CacheCb, Cache, Key, Session, State)
end.
+delayed_invalidate_session(CacheCb, Cache, Key, Session,
+ #state{last_delay_timer = LastTimer} = State) ->
+ %% When a registered session is invalidated we need to
+ %% wait a while before deleting it as there might be
+ %% pending connections that rightfully needs to look up
+ %% the session data but new connections should not get to
+ %% use this session.
+ CacheCb:update(Cache, Key, Session#session{is_resumable = false}),
+ TRef =
+ erlang:send_after(delay_time(), self(),
+ {delayed_clean_session, Key, Cache}),
+ {noreply, State#state{last_delay_timer =
+ last_delay_timer(Key, TRef, LastTimer)}}.
+
last_delay_timer({{_,_},_}, TRef, {LastServer, _}) ->
{LastServer, TRef};
last_delay_timer({_,_}, TRef, {_, LastClient}) ->
@@ -532,12 +546,12 @@ new_id(Port, Tries, Cache, CacheCb) ->
Id = crypto:rand_bytes(?NUM_OF_SESSION_ID_BYTES),
case CacheCb:lookup(Cache, {Port, Id}) of
undefined ->
- Now = calendar:datetime_to_gregorian_seconds({date(), time()}),
+ Now = erlang:monotonic_time(),
%% New sessions can not be set to resumable
%% until handshake is compleate and the
%% other session values are set.
CacheCb:update(Cache, {Port, Id}, #session{session_id = Id,
- is_resumable = false,
+ is_resumable = new,
time_stamp = Now}),
Id;
_ ->
@@ -559,15 +573,62 @@ clean_cert_db(Ref, CertDb, RefDb, PemCache, File) ->
ok
end.
+ssl_client_register_session(Host, Port, Session, #state{session_cache_client = Cache,
+ session_cache_cb = CacheCb,
+ session_cache_client_max = Max,
+ session_client_invalidator = Pid0} = State) ->
+ TimeStamp = erlang:monotonic_time(),
+ NewSession = Session#session{time_stamp = TimeStamp},
+
+ case CacheCb:select_session(Cache, {Host, Port}) of
+ no_session ->
+ Pid = do_register_session({{Host, Port},
+ NewSession#session.session_id},
+ NewSession, Max, Pid0, Cache, CacheCb),
+ State#state{session_client_invalidator = Pid};
+ Sessions ->
+ register_unique_session(Sessions, NewSession, {Host, Port}, State)
+ end.
+
+server_register_session(Port, Session, #state{session_cache_server_max = Max,
+ session_cache_server = Cache,
+ session_cache_cb = CacheCb,
+ session_server_invalidator = Pid0} = State) ->
+ TimeStamp = erlang:monotonic_time(),
+ NewSession = Session#session{time_stamp = TimeStamp},
+ Pid = do_register_session({Port, NewSession#session.session_id},
+ NewSession, Max, Pid0, Cache, CacheCb),
+ State#state{session_server_invalidator = Pid}.
+
+do_register_session(Key, Session, Max, Pid, Cache, CacheCb) ->
+ try CacheCb:size(Cache) of
+ N when N > Max ->
+ invalidate_session_cache(Pid, CacheCb, Cache);
+ _ ->
+ CacheCb:update(Cache, Key, Session),
+ Pid
+ catch
+ error:undef ->
+ CacheCb:update(Cache, Key, Session),
+ Pid
+ end.
+
+
%% Do not let dumb clients create a gigantic session table
%% for itself creating big delays at connection time.
-register_unique_session(Sessions, Session, CacheCb, Cache, PartialKey) ->
+register_unique_session(Sessions, Session, PartialKey,
+ #state{session_cache_client_max = Max,
+ session_cache_client = Cache,
+ session_cache_cb = CacheCb,
+ session_client_invalidator = Pid0} = State) ->
case exists_equivalent(Session , Sessions) of
true ->
- ok;
+ State;
false ->
- CacheCb:update(Cache, {PartialKey,
- Session#session.session_id}, Session)
+ Pid = do_register_session({PartialKey,
+ Session#session.session_id},
+ Session, Max, Pid0, Cache, CacheCb),
+ State#state{session_client_invalidator = Pid}
end.
exists_equivalent(_, []) ->
@@ -622,7 +683,8 @@ pem_check_interval() ->
end.
is_before_checkpoint(Time, CheckPoint) ->
- calendar:datetime_to_gregorian_seconds(calendar:now_to_datetime(CheckPoint)) -
+ calendar:datetime_to_gregorian_seconds(
+ calendar:now_to_datetime(CheckPoint)) -
calendar:datetime_to_gregorian_seconds(Time) > 0.
add_trusted_certs(Pid, Trustedcerts, Db) ->
@@ -643,3 +705,9 @@ crl_db_info([_,_,_,Local], {internal, Info}) ->
crl_db_info(_, UserCRLDb) ->
UserCRLDb.
+%% Only start a session invalidator if there is not
+%% one already active
+invalidate_session_cache(undefined, CacheCb, Cache) ->
+ start_session_validator(Cache, CacheCb, {invalidate_before, erlang:monotonic_time()});
+invalidate_session_cache(Pid, _CacheCb, _Cache) ->
+ Pid.
diff --git a/lib/ssl/src/ssl_session.erl b/lib/ssl/src/ssl_session.erl
index 1770faf1ff..2b24bff5ff 100644
--- a/lib/ssl/src/ssl_session.erl
+++ b/lib/ssl/src/ssl_session.erl
@@ -31,8 +31,6 @@
%% Internal application API
-export([is_new/2, client_id/4, server_id/6, valid_session/2]).
--define('24H_in_sec', 8640).
-
-type seconds() :: integer().
%%--------------------------------------------------------------------
@@ -63,13 +61,16 @@ client_id(ClientInfo, Cache, CacheCb, OwnCert) ->
SessionId
end.
--spec valid_session(#session{}, seconds()) -> boolean().
+-spec valid_session(#session{}, seconds() | {invalidate_before, integer()}) -> boolean().
%%
%% Description: Check that the session has not expired
%%--------------------------------------------------------------------
+valid_session(#session{time_stamp = TimeStamp}, {invalidate_before, Before}) ->
+ TimeStamp > Before;
valid_session(#session{time_stamp = TimeStamp}, LifeTime) ->
- Now = calendar:datetime_to_gregorian_seconds({date(), time()}),
- Now - TimeStamp < LifeTime.
+ Now = erlang:monotonic_time(),
+ Lived = erlang:convert_time_unit(Now-TimeStamp, native, seconds),
+ Lived < LifeTime.
server_id(Port, <<>>, _SslOpts, _Cert, _, _) ->
{ssl_manager:new_session_id(Port), undefined};
@@ -100,14 +101,14 @@ select_session([], _, _) ->
no_session;
select_session(Sessions, #ssl_options{ciphers = Ciphers}, OwnCert) ->
IsNotResumable =
- fun([_Id, Session]) ->
+ fun(Session) ->
not (resumable(Session#session.is_resumable) andalso
lists:member(Session#session.cipher_suite, Ciphers)
andalso (OwnCert == Session#session.own_certificate))
end,
case lists:dropwhile(IsNotResumable, Sessions) of
[] -> no_session;
- [[Id, _]|_] -> Id
+ [Session | _] -> Session#session.session_id
end.
is_resumable(_, _, #ssl_options{reuse_sessions = false}, _, _, _, _) ->
diff --git a/lib/ssl/src/ssl_session_cache.erl b/lib/ssl/src/ssl_session_cache.erl
index 11ed310477..9585e613e6 100644
--- a/lib/ssl/src/ssl_session_cache.erl
+++ b/lib/ssl/src/ssl_session_cache.erl
@@ -27,7 +27,7 @@
-include("ssl_internal.hrl").
-export([init/1, terminate/1, lookup/2, update/3, delete/2, foldl/3,
- select_session/2]).
+ select_session/2, size/1]).
%%--------------------------------------------------------------------
%% Description: Return table reference. Called by ssl_manager process.
@@ -83,7 +83,13 @@ foldl(Fun, Acc0, Cache) ->
%%--------------------------------------------------------------------
select_session(Cache, PartialKey) ->
ets:select(Cache,
- [{{{PartialKey,'$1'}, '$2'},[],['$$']}]).
+ [{{{PartialKey,'_'}, '$1'},[],['$1']}]).
+
+%%--------------------------------------------------------------------
+%% Description: Returns the cache size
+%%--------------------------------------------------------------------
+size(Cache) ->
+ ets:info(Cache, size).
%%--------------------------------------------------------------------
%%% Internal functions
diff --git a/lib/ssl/src/ssl_session_cache_api.erl b/lib/ssl/src/ssl_session_cache_api.erl
index 536b52c44b..8f62c25be5 100644
--- a/lib/ssl/src/ssl_session_cache_api.erl
+++ b/lib/ssl/src/ssl_session_cache_api.erl
@@ -33,3 +33,4 @@
-callback delete(db_handle(), key()) -> any().
-callback foldl(fun(), term(), db_handle()) -> term().
-callback select_session(db_handle(), {host(), inet:port_number()} | inet:port_number()) -> [#session{}].
+-callback size(db_handle()) -> integer().
diff --git a/lib/ssl/src/ssl_tls_dist_proxy.erl b/lib/ssl/src/ssl_tls_dist_proxy.erl
index 273d3b5521..3edd352891 100644
--- a/lib/ssl/src/ssl_tls_dist_proxy.erl
+++ b/lib/ssl/src/ssl_tls_dist_proxy.erl
@@ -48,6 +48,47 @@ accept(Listen) ->
connect(Ip, Port) ->
gen_server:call(?MODULE, {connect, Ip, Port}, infinity).
+
+do_listen(Options) ->
+ {First,Last} = case application:get_env(kernel,inet_dist_listen_min) of
+ {ok,N} when is_integer(N) ->
+ case application:get_env(kernel,
+ inet_dist_listen_max) of
+ {ok,M} when is_integer(M) ->
+ {N,M};
+ _ ->
+ {N,N}
+ end;
+ _ ->
+ {0,0}
+ end,
+ do_listen(First, Last, listen_options([{backlog,128}|Options])).
+
+do_listen(First,Last,_) when First > Last ->
+ {error,eaddrinuse};
+do_listen(First,Last,Options) ->
+ case gen_tcp:listen(First, Options) of
+ {error, eaddrinuse} ->
+ do_listen(First+1,Last,Options);
+ Other ->
+ Other
+ end.
+
+listen_options(Opts0) ->
+ Opts1 =
+ case application:get_env(kernel, inet_dist_use_interface) of
+ {ok, Ip} ->
+ [{ip, Ip} | Opts0];
+ _ ->
+ Opts0
+ end,
+ case application:get_env(kernel, inet_dist_listen_options) of
+ {ok,ListenOpts} ->
+ ListenOpts ++ Opts1;
+ _ ->
+ Opts1
+ end.
+
%%====================================================================
%% gen_server callbacks
%%====================================================================
@@ -62,13 +103,17 @@ init([]) ->
handle_call({listen, Name}, _From, State) ->
case gen_tcp:listen(0, [{active, false}, {packet,?PPRE}]) of
{ok, Socket} ->
- {ok, World} = gen_tcp:listen(0, [{active, false}, binary, {packet,?PPRE}]),
+ {ok, World} = do_listen([{active, false}, binary, {packet,?PPRE}, {reuseaddr, true}]),
{ok, TcpAddress} = get_tcp_address(Socket),
{ok, WorldTcpAddress} = get_tcp_address(World),
{_,Port} = WorldTcpAddress#net_address.address,
- {ok, Creation} = erl_epmd:register_node(Name, Port),
- {reply, {ok, {Socket, TcpAddress, Creation}},
- State#state{listen={Socket, World}}};
+ case erl_epmd:register_node(Name, Port) of
+ {ok, Creation} ->
+ {reply, {ok, {Socket, TcpAddress, Creation}},
+ State#state{listen={Socket, World}}};
+ {error, _} = Error ->
+ {reply, Error, State}
+ end;
Error ->
{reply, Error, State}
end;
@@ -134,6 +179,7 @@ accept_loop(Proxy, erts = Type, Listen, Extra) ->
Extra ! {accept,self(),Socket,inet,proxy},
receive
{_Kernel, controller, Pid} ->
+ inet:setopts(Socket, [nodelay()]),
ok = gen_tcp:controlling_process(Socket, Pid),
flush_old_controller(Pid, Socket),
Pid ! {self(), controller};
@@ -158,6 +204,11 @@ accept_loop(Proxy, world = Type, Listen, Extra) ->
end),
ok = ssl:controlling_process(SslSocket, PairHandler),
flush_old_controller(PairHandler, SslSocket);
+ {error, {options, _}} = Error ->
+ %% Bad options: that's probably our fault. Let's log that.
+ error_logger:error_msg("Cannot accept TLS distribution connection: ~s~n",
+ [ssl:format_error(Error)]),
+ gen_tcp:close(Socket);
_ ->
gen_tcp:close(Socket)
end;
@@ -167,7 +218,7 @@ accept_loop(Proxy, world = Type, Listen, Extra) ->
accept_loop(Proxy, Type, Listen, Extra).
try_connect(Port) ->
- case gen_tcp:connect({127,0,0,1}, Port, [{active, false}, {packet,?PPRE}]) of
+ case gen_tcp:connect({127,0,0,1}, Port, [{active, false}, {packet,?PPRE}, nodelay()]) of
R = {ok, _S} ->
R;
{error, _R} ->
@@ -177,7 +228,7 @@ try_connect(Port) ->
setup_proxy(Ip, Port, Parent) ->
process_flag(trap_exit, true),
Opts = get_ssl_options(client),
- case ssl:connect(Ip, Port, [{active, true}, binary, {packet,?PPRE}] ++ Opts) of
+ case ssl:connect(Ip, Port, [{active, true}, binary, {packet,?PPRE}, nodelay()] ++ Opts) of
{ok, World} ->
{ok, ErtsL} = gen_tcp:listen(0, [{active, true}, {ip, {127,0,0,1}}, binary, {packet,?PPRE}]),
{ok, #net_address{address={_,LPort}}} = get_tcp_address(ErtsL),
@@ -189,29 +240,50 @@ setup_proxy(Ip, Port, Parent) ->
Err ->
Parent ! {self(), Err}
end;
+ {error, {options, _}} = Err ->
+ %% Bad options: that's probably our fault. Let's log that.
+ error_logger:error_msg("Cannot open TLS distribution connection: ~s~n",
+ [ssl:format_error(Err)]),
+ Parent ! {self(), Err};
Err ->
Parent ! {self(), Err}
end.
+
+%% we may not always want the nodelay behaviour
+%% %% for performance reasons
+
+nodelay() ->
+ case application:get_env(kernel, dist_nodelay) of
+ undefined ->
+ {nodelay, true};
+ {ok, true} ->
+ {nodelay, true};
+ {ok, false} ->
+ {nodelay, false};
+ _ ->
+ {nodelay, true}
+ end.
+
setup_connection(World, ErtsListen) ->
process_flag(trap_exit, true),
{ok, TcpAddress} = get_tcp_address(ErtsListen),
{_Addr,Port} = TcpAddress#net_address.address,
- {ok, Erts} = gen_tcp:connect({127,0,0,1}, Port, [{active, true}, binary, {packet,?PPRE}]),
- ssl:setopts(World, [{active,true}, {packet,?PPRE}]),
+ {ok, Erts} = gen_tcp:connect({127,0,0,1}, Port, [{active, true}, binary, {packet,?PPRE}, nodelay()]),
+ ssl:setopts(World, [{active,true}, {packet,?PPRE}, nodelay()]),
loop_conn_setup(World, Erts).
loop_conn_setup(World, Erts) ->
receive
{ssl, World, Data = <<$a, _/binary>>} ->
gen_tcp:send(Erts, Data),
- ssl:setopts(World, [{packet,?PPOST}]),
- inet:setopts(Erts, [{packet,?PPOST}]),
+ ssl:setopts(World, [{packet,?PPOST}, nodelay()]),
+ inet:setopts(Erts, [{packet,?PPOST}, nodelay()]),
loop_conn(World, Erts);
{tcp, Erts, Data = <<$a, _/binary>>} ->
ssl:send(World, Data),
- ssl:setopts(World, [{packet,?PPOST}]),
- inet:setopts(Erts, [{packet,?PPOST}]),
+ ssl:setopts(World, [{packet,?PPOST}, nodelay()]),
+ inet:setopts(Erts, [{packet,?PPOST}, nodelay()]),
loop_conn(World, Erts);
{ssl, World, Data = <<_, _/binary>>} ->
gen_tcp:send(Erts, Data),
diff --git a/lib/ssl/src/ssl_v3.erl b/lib/ssl/src/ssl_v3.erl
index 5e043624a7..f169059a75 100644
--- a/lib/ssl/src/ssl_v3.erl
+++ b/lib/ssl/src/ssl_v3.erl
@@ -144,6 +144,7 @@ suites() ->
?TLS_DHE_RSA_WITH_AES_128_CBC_SHA,
?TLS_DHE_DSS_WITH_AES_128_CBC_SHA,
?TLS_RSA_WITH_AES_128_CBC_SHA,
+ ?TLS_DHE_RSA_WITH_DES_CBC_SHA,
?TLS_RSA_WITH_DES_CBC_SHA
].
diff --git a/lib/ssl/src/tls_connection.erl b/lib/ssl/src/tls_connection.erl
index 7fda2377ee..b2b85eaf8d 100644
--- a/lib/ssl/src/tls_connection.erl
+++ b/lib/ssl/src/tls_connection.erl
@@ -54,7 +54,7 @@
%% Alert and close handling
-export([send_alert/2, handle_own_alert/4, handle_close_alert/3,
handle_normal_shutdown/3, handle_unexpected_message/3,
- workaround_transport_delivery_problems/2, alert_user/6, alert_user/9
+ close/5, alert_user/6, alert_user/9
]).
%% Data handling
@@ -168,9 +168,10 @@ hello(start, #state{host = Host, port = Port, role = client,
Cache, CacheCb, Renegotiation, Cert),
Version = Hello#client_hello.client_version,
+ HelloVersion = tls_record:lowest_protocol_version(SslOpts#ssl_options.versions),
Handshake0 = ssl_handshake:init_handshake_history(),
{BinMsg, ConnectionStates, Handshake} =
- encode_handshake(Hello, Version, ConnectionStates0, Handshake0),
+ encode_handshake(Hello, HelloVersion, ConnectionStates0, Handshake0),
Transport:send(Socket, BinMsg),
State1 = State0#state{connection_states = ConnectionStates,
negotiated_version = Version, %% Requested version
@@ -211,7 +212,7 @@ hello(Hello = #client_hello{client_version = ClientVersion,
client_ecc = {EllipticCurves, EcPointFormats},
negotiated_protocol = Protocol}, ?MODULE)
end;
-hello(Hello,
+hello(Hello = #server_hello{},
#state{connection_states = ConnectionStates0,
negotiated_version = ReqVersion,
role = client,
@@ -924,8 +925,7 @@ handle_own_alert(Alert, Version, StateName,
try %% Try to tell the other side
{BinMsg, _} =
ssl_alert:encode(Alert, Version, ConnectionStates),
- Transport:send(Socket, BinMsg),
- workaround_transport_delivery_problems(Socket, Transport)
+ Transport:send(Socket, BinMsg)
catch _:_ -> %% Can crash if we are in a uninitialized state
ignore
end,
@@ -977,21 +977,57 @@ invalidate_session(client, Host, Port, Session) ->
invalidate_session(server, _, Port, Session) ->
ssl_manager:invalidate_session(Port, Session).
-workaround_transport_delivery_problems(Socket, gen_tcp = Transport) ->
+%% User downgrades connection
+%% When downgrading an TLS connection to a transport connection
+%% we must recive the close message before releasing the
+%% transport socket.
+close({close, {Pid, Timeout}}, Socket, Transport, ConnectionStates, Check) when is_pid(Pid) ->
+ ssl_socket:setopts(Transport, Socket, [{active, false}, {packet, ssl_tls}]),
+ case Transport:recv(Socket, 0, Timeout) of
+ {ok, {ssl_tls, Socket, ?ALERT, Version, Fragment}} ->
+ case tls_record:decode_cipher_text(#ssl_tls{type = ?ALERT,
+ version = Version,
+ fragment = Fragment
+ }, ConnectionStates, Check) of
+ {#ssl_tls{fragment = Plain}, _} ->
+ [Alert| _] = decode_alerts(Plain),
+ downgrade(Alert, Transport, Socket, Pid)
+ end;
+ {error, timeout} ->
+ {error, timeout};
+ _ ->
+ {error, no_tls_close}
+ end;
+%% User closes or recursive call!
+close({close, Timeout}, Socket, Transport = gen_tcp, _,_) ->
+ ssl_socket:setopts(Transport, Socket, [{active, false}]),
+ Transport:shutdown(Socket, write),
+ _ = Transport:recv(Socket, 0, Timeout),
+ ok;
+%% Peer closed socket
+close({shutdown, transport_closed}, Socket, Transport = gen_tcp, ConnectionStates, Check) ->
+ close({close, 0}, Socket, Transport, ConnectionStates, Check);
+%% We generate fatal alert
+close({shutdown, own_alert}, Socket, Transport = gen_tcp, ConnectionStates, Check) ->
%% Standard trick to try to make sure all
%% data sent to the tcp port is really delivered to the
%% peer application before tcp port is closed so that the peer will
%% get the correct TLS alert message and not only a transport close.
- ssl_socket:setopts(Transport, Socket, [{active, false}]),
- Transport:shutdown(Socket, write),
- %% Will return when other side has closed or after 30 s
+ %% Will return when other side has closed or after timout millisec
%% e.g. we do not want to hang if something goes wrong
%% with the network but we want to maximise the odds that
%% peer application gets all data sent on the tcp connection.
- Transport:recv(Socket, 0, 30000);
-workaround_transport_delivery_problems(Socket, Transport) ->
+ close({close, ?DEFAULT_TIMEOUT}, Socket, Transport, ConnectionStates, Check);
+%% Other
+close(_, Socket, Transport, _,_) ->
Transport:close(Socket).
-
+downgrade(#alert{description = ?CLOSE_NOTIFY}, Transport, Socket, Pid) ->
+ ssl_socket:setopts(Transport, Socket, [{active, false}, {packet, 0}, {mode, binary}]),
+ Transport:controlling_process(Socket, Pid),
+ {ok, Socket};
+downgrade(_, _,_,_) ->
+ {error, no_tls_close}.
+
convert_state(#state{ssl_options = Options} = State, up, "5.3.5", "5.3.6") ->
State#state{ssl_options = convert_options_partial_chain(Options, up)};
convert_state(#state{ssl_options = Options} = State, down, "5.3.6", "5.3.5") ->
diff --git a/lib/ssl/src/tls_record.erl b/lib/ssl/src/tls_record.erl
index aa524f0225..9348c8bbdd 100644
--- a/lib/ssl/src/tls_record.erl
+++ b/lib/ssl/src/tls_record.erl
@@ -41,8 +41,9 @@
-export([encode_plain_text/4]).
%% Protocol version handling
--export([protocol_version/1, lowest_protocol_version/2,
- highest_protocol_version/1, is_higher/2, supported_protocol_versions/0,
+-export([protocol_version/1, lowest_protocol_version/1, lowest_protocol_version/2,
+ highest_protocol_version/1, highest_protocol_version/2,
+ is_higher/2, supported_protocol_versions/0,
is_acceptable_version/1, is_acceptable_version/2]).
-export_type([tls_version/0, tls_atom_version/0]).
@@ -257,6 +258,18 @@ lowest_protocol_version(Version = {M,_},
Version;
lowest_protocol_version(_,Version) ->
Version.
+
+%%--------------------------------------------------------------------
+-spec lowest_protocol_version([tls_version()]) -> tls_version().
+%%
+%% Description: Lowest protocol version present in a list
+%%--------------------------------------------------------------------
+lowest_protocol_version([]) ->
+ lowest_protocol_version();
+lowest_protocol_version(Versions) ->
+ [Ver | Vers] = Versions,
+ lowest_list_protocol_version(Ver, Vers).
+
%%--------------------------------------------------------------------
-spec highest_protocol_version([tls_version()]) -> tls_version().
%%
@@ -266,19 +279,29 @@ highest_protocol_version([]) ->
highest_protocol_version();
highest_protocol_version(Versions) ->
[Ver | Vers] = Versions,
- highest_protocol_version(Ver, Vers).
+ highest_list_protocol_version(Ver, Vers).
-highest_protocol_version(Version, []) ->
+%%--------------------------------------------------------------------
+-spec highest_protocol_version(tls_version(), tls_version()) -> tls_version().
+%%
+%% Description: Highest protocol version of two given versions
+%%--------------------------------------------------------------------
+highest_protocol_version(Version = {M, N}, {M, O}) when N > O ->
+ Version;
+highest_protocol_version({M, _},
+ Version = {M, _}) ->
Version;
-highest_protocol_version(Version = {N, M}, [{N, O} | Rest]) when M > O ->
- highest_protocol_version(Version, Rest);
-highest_protocol_version({M, _}, [Version = {M, _} | Rest]) ->
- highest_protocol_version(Version, Rest);
-highest_protocol_version(Version = {M,_}, [{N,_} | Rest]) when M > N ->
- highest_protocol_version(Version, Rest);
-highest_protocol_version(_, [Version | Rest]) ->
- highest_protocol_version(Version, Rest).
+highest_protocol_version(Version = {M,_},
+ {N, _}) when M > N ->
+ Version;
+highest_protocol_version(_,Version) ->
+ Version.
+%%--------------------------------------------------------------------
+-spec is_higher(V1 :: tls_version(), V2::tls_version()) -> boolean().
+%%
+%% Description: Is V1 > V2
+%%--------------------------------------------------------------------
is_higher({M, N}, {M, O}) when N > O ->
true;
is_higher({M, _}, {N, _}) when M > N ->
@@ -352,6 +375,17 @@ is_acceptable_version(_,_) ->
%%--------------------------------------------------------------------
%%% Internal functions
%%--------------------------------------------------------------------
+
+lowest_list_protocol_version(Ver, []) ->
+ Ver;
+lowest_list_protocol_version(Ver1, [Ver2 | Rest]) ->
+ lowest_list_protocol_version(lowest_protocol_version(Ver1, Ver2), Rest).
+
+highest_list_protocol_version(Ver, []) ->
+ Ver;
+highest_list_protocol_version(Ver1, [Ver2 | Rest]) ->
+ highest_list_protocol_version(highest_protocol_version(Ver1, Ver2), Rest).
+
encode_tls_cipher_text(Type, {MajVer, MinVer}, Fragment) ->
Length = erlang:iolist_size(Fragment),
[<<?BYTE(Type), ?BYTE(MajVer), ?BYTE(MinVer), ?UINT16(Length)>>, Fragment].
@@ -370,6 +404,10 @@ mac_hash({3, N} = Version, MacAlg, MacSecret, SeqNo, Type, Length, Fragment)
highest_protocol_version() ->
highest_protocol_version(supported_protocol_versions()).
+lowest_protocol_version() ->
+ lowest_protocol_version(supported_protocol_versions()).
+
+
sufficient_tlsv1_2_crypto_support() ->
CryptoSupport = crypto:supports(),
proplists:get_bool(sha256, proplists:get_value(hashs, CryptoSupport)).
diff --git a/lib/ssl/test/make_certs.erl b/lib/ssl/test/make_certs.erl
index 7215a59823..5eebf773a7 100644
--- a/lib/ssl/test/make_certs.erl
+++ b/lib/ssl/test/make_certs.erl
@@ -116,16 +116,16 @@ do_append_files([F|Fs], RF) ->
do_append_files(Fs, RF).
rootCA(Root, Name, C) ->
- create_ca_dir(Root, Name, ca_cnf(C#config{commonName = Name})),
- create_self_signed_cert(Root, Name, req_cnf(C#config{commonName = Name}), C),
+ create_ca_dir(Root, Name, ca_cnf(Root, C#config{commonName = Name})),
+ create_self_signed_cert(Root, Name, req_cnf(Root, C#config{commonName = Name}), C),
file:copy(filename:join([Root, Name, "cert.pem"]), filename:join([Root, Name, "cacerts.pem"])),
gencrl(Root, Name, C).
intermediateCA(Root, CA, ParentCA, C) ->
- create_ca_dir(Root, CA, ca_cnf(C#config{commonName = CA})),
+ create_ca_dir(Root, CA, ca_cnf(Root, C#config{commonName = CA})),
CARoot = filename:join([Root, CA]),
CnfFile = filename:join([CARoot, "req.cnf"]),
- file:write_file(CnfFile, req_cnf(C#config{commonName = CA})),
+ file:write_file(CnfFile, req_cnf(Root, C#config{commonName = CA})),
KeyFile = filename:join([CARoot, "private", "key.pem"]),
ReqFile = filename:join([CARoot, "req.pem"]),
create_req(Root, CnfFile, KeyFile, ReqFile, C),
@@ -147,7 +147,7 @@ enduser(Root, CA, User, C) ->
UsrRoot = filename:join([Root, User]),
file:make_dir(UsrRoot),
CnfFile = filename:join([UsrRoot, "req.cnf"]),
- file:write_file(CnfFile, req_cnf(C#config{commonName = User})),
+ file:write_file(CnfFile, req_cnf(Root, C#config{commonName = User})),
KeyFile = filename:join([UsrRoot, "key.pem"]),
ReqFile = filename:join([UsrRoot, "req.pem"]),
create_req(Root, CnfFile, KeyFile, ReqFile, C),
@@ -337,10 +337,10 @@ eval_cmd(Port, Cmd) ->
%% Contents of configuration files
%%
-req_cnf(C) ->
+req_cnf(Root, C) ->
["# Purpose: Configuration for requests (end users and CAs)."
"\n"
- "ROOTDIR = $ENV::ROOTDIR\n"
+ "ROOTDIR = " ++ Root ++ "\n"
"\n"
"[req]\n"
@@ -371,10 +371,10 @@ req_cnf(C) ->
"subjectKeyIdentifier = hash\n"
"subjectAltName = email:copy\n"].
-ca_cnf(C = #config{issuing_distribution_point = true}) ->
+ca_cnf(Root, C = #config{issuing_distribution_point = true}) ->
["# Purpose: Configuration for CAs.\n"
"\n"
- "ROOTDIR = $ENV::ROOTDIR\n"
+ "ROOTDIR = " ++ Root ++ "\n"
"default_ca = ca\n"
"\n"
@@ -450,10 +450,10 @@ ca_cnf(C = #config{issuing_distribution_point = true}) ->
"crlDistributionPoints=@crl_section\n"
];
-ca_cnf(C = #config{issuing_distribution_point = false}) ->
+ca_cnf(Root, C = #config{issuing_distribution_point = false}) ->
["# Purpose: Configuration for CAs.\n"
"\n"
- "ROOTDIR = $ENV::ROOTDIR\n"
+ "ROOTDIR = " ++ Root ++ "\n"
"default_ca = ca\n"
"\n"
diff --git a/lib/ssl/test/ssl_ECC_SUITE.erl b/lib/ssl/test/ssl_ECC_SUITE.erl
index 6ea0466dde..75b639b23b 100644
--- a/lib/ssl/test/ssl_ECC_SUITE.erl
+++ b/lib/ssl/test/ssl_ECC_SUITE.erl
@@ -46,7 +46,7 @@ groups() ->
{'tlsv1', [], all_versions_groups()},
{'erlang_server', [], key_cert_combinations()},
{'erlang_client', [], key_cert_combinations()},
- {'erlang', [], key_cert_combinations()}
+ {'erlang', [], key_cert_combinations() ++ misc()}
].
all_versions_groups ()->
@@ -65,6 +65,9 @@ key_cert_combinations() ->
client_rsa_server_ecdsa
].
+misc()->
+ [client_ecdsa_server_ecdsa_with_raw_key].
+
%%--------------------------------------------------------------------
init_per_suite(Config0) ->
end_per_suite(Config0),
@@ -143,7 +146,7 @@ init_per_testcase(TestCase, Config) ->
ct:log("Ciphers: ~p~n ", [ ssl:cipher_suites()]),
end_per_testcase(TestCase, Config),
ssl:start(),
- ct:timetrap({seconds, 5}),
+ ct:timetrap({seconds, 15}),
Config.
end_per_testcase(_TestCase, Config) ->
@@ -189,6 +192,32 @@ client_rsa_server_ecdsa(Config) when is_list(Config) ->
SOpts = ?config(server_ecdsa_verify_opts, Config),
basic_test(COpts, SOpts, Config).
+client_ecdsa_server_ecdsa_with_raw_key(Config) when is_list(Config) ->
+ COpts = ?config(client_ecdsa_opts, Config),
+ SOpts = ?config(server_ecdsa_verify_opts, Config),
+ ServerCert = proplists:get_value(certfile, SOpts),
+ ServerKeyFile = proplists:get_value(keyfile, SOpts),
+ {ok, PemBin} = file:read_file(ServerKeyFile),
+ PemEntries = public_key:pem_decode(PemBin),
+ {'ECPrivateKey', Key, not_encrypted} = proplists:lookup('ECPrivateKey', PemEntries),
+ ServerKey = {'ECPrivateKey', Key},
+ ServerCA = proplists:get_value(cacertfile, SOpts),
+ ClientCert = proplists:get_value(certfile, COpts),
+ ClientKey = proplists:get_value(keyfile, COpts),
+ ClientCA = proplists:get_value(cacertfile, COpts),
+ SType = ?config(server_type, Config),
+ CType = ?config(client_type, Config),
+ {Server, Port} = start_server_with_raw_key(SType,
+ ClientCA, ServerCA,
+ ServerCert,
+ ServerKey,
+ Config),
+ Client = start_client(CType, Port, ServerCA, ClientCA,
+ ClientCert,
+ ClientKey, Config),
+ check_result(Server, SType, Client, CType),
+ close(Server, Client).
+
%%--------------------------------------------------------------------
%% Internal functions ------------------------------------------------
%%--------------------------------------------------------------------
@@ -219,10 +248,13 @@ start_client(openssl, Port, CA, OwnCa, Cert, Key, Config) ->
PrivDir = ?config(priv_dir, Config),
NewCA = new_ca(filename:join(PrivDir, "new_ca.pem"), CA, OwnCa),
Version = tls_record:protocol_version(tls_record:highest_protocol_version([])),
- Cmd = "openssl s_client -verify 2 -port " ++ integer_to_list(Port) ++ ssl_test_lib:version_flag(Version) ++
- " -cert " ++ Cert ++ " -CAfile " ++ NewCA
- ++ " -key " ++ Key ++ " -host localhost -msg -debug",
- OpenSslPort = open_port({spawn, Cmd}, [stderr_to_stdout]),
+ Exe = "openssl",
+ Args = ["s_client", "-verify", "2", "-port", integer_to_list(Port),
+ ssl_test_lib:version_flag(Version),
+ "-cert", Cert, "-CAfile", NewCA,
+ "-key", Key, "-host","localhost", "-msg", "-debug"],
+
+ OpenSslPort = ssl_test_lib:portable_open_port(Exe, Args),
true = port_command(OpenSslPort, "Hello world"),
OpenSslPort;
start_client(erlang, Port, CA, _, Cert, Key, Config) ->
@@ -241,15 +273,14 @@ start_server(openssl, CA, OwnCa, Cert, Key, Config) ->
Port = ssl_test_lib:inet_port(node()),
Version = tls_record:protocol_version(tls_record:highest_protocol_version([])),
- Cmd = "openssl s_server -accept " ++ integer_to_list(Port) ++ ssl_test_lib:version_flag(Version) ++
- " -verify 2 -cert " ++ Cert ++ " -CAfile " ++ NewCA
- ++ " -key " ++ Key ++ " -msg -debug",
- OpenSslPort = open_port({spawn, Cmd}, [stderr_to_stdout]),
+ Exe = "openssl",
+ Args = ["s_server", "-accept", integer_to_list(Port), ssl_test_lib:version_flag(Version),
+ "-verify", "2", "-cert", Cert, "-CAfile", NewCA,
+ "-key", Key, "-msg", "-debug"],
+ OpenSslPort = ssl_test_lib:portable_open_port(Exe, Args),
true = port_command(OpenSslPort, "Hello world"),
{OpenSslPort, Port};
-
start_server(erlang, CA, _, Cert, Key, Config) ->
-
{_, ServerNode, _} = ssl_test_lib:run_where(Config),
Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0},
{from, self()},
@@ -260,6 +291,17 @@ start_server(erlang, CA, _, Cert, Key, Config) ->
[{verify, verify_peer}, {cacertfile, CA},
{certfile, Cert}, {keyfile, Key}]}]),
{Server, ssl_test_lib:inet_port(Server)}.
+start_server_with_raw_key(erlang, CA, _, Cert, Key, Config) ->
+ {_, ServerNode, _} = 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,
+ [{verify, verify_peer}, {cacertfile, CA},
+ {certfile, Cert}, {key, Key}]}]),
+ {Server, ssl_test_lib:inet_port(Server)}.
check_result(Server, erlang, Client, erlang) ->
ssl_test_lib:check_result(Server, ok, Client, ok);
diff --git a/lib/ssl/test/ssl_basic_SUITE.erl b/lib/ssl/test/ssl_basic_SUITE.erl
index ecedb89c23..05b040a2ab 100644
--- a/lib/ssl/test/ssl_basic_SUITE.erl
+++ b/lib/ssl/test/ssl_basic_SUITE.erl
@@ -35,7 +35,6 @@
-include("tls_record.hrl").
-include("tls_handshake.hrl").
--define('24H_in_sec', 86400).
-define(TIMEOUT, 20000).
-define(EXPIRE, 10).
-define(SLEEP, 500).
@@ -96,6 +95,7 @@ options_tests() ->
[der_input,
misc_ssl_options,
ssl_options_not_proplist,
+ raw_ssl_option,
socket_options,
invalid_inet_get_option,
invalid_inet_get_option_not_list,
@@ -129,11 +129,14 @@ api_tests() ->
controlling_process,
upgrade,
upgrade_with_timeout,
+ downgrade,
+ close_with_timeout,
shutdown,
shutdown_write,
shutdown_both,
shutdown_error,
hibernate,
+ hibernate_right_away,
listen_socket,
ssl_accept_timeout,
ssl_recv_timeout,
@@ -265,12 +268,12 @@ init_per_testcase(protocol_versions, Config) ->
Config;
init_per_testcase(reuse_session_expired, Config) ->
- ct:timetrap({seconds, 30}),
ssl:stop(),
application:load(ssl),
application:set_env(ssl, session_lifetime, ?EXPIRE),
application:set_env(ssl, session_delay_cleanup_time, 500),
ssl:start(),
+ ct:timetrap({seconds, 30}),
Config;
init_per_testcase(empty_protocol_versions, Config) ->
@@ -303,7 +306,25 @@ init_per_testcase(TestCase, Config) when TestCase == client_renegotiate;
ct:log("TLS/SSL version ~p~n ", [tls_record:supported_protocol_versions()]),
ct:timetrap({seconds, 30}),
Config;
-init_per_testcase(ssl_accept_timeout, Config) ->
+
+init_per_testcase(TestCase, Config) when TestCase == psk_cipher_suites;
+ TestCase == psk_with_hint_cipher_suites;
+ TestCase == ciphers_rsa_signed_certs;
+ TestCase == ciphers_rsa_signed_certs_openssl_names;
+ TestCase == versions_option,
+ TestCase == tcp_connect_big ->
+ ct:log("TLS/SSL version ~p~n ", [tls_record:supported_protocol_versions()]),
+
+ ct:timetrap({seconds, 30}),
+ Config;
+init_per_testcase(rizzo, Config) ->
+ ct:log("TLS/SSL version ~p~n ", [tls_record:supported_protocol_versions()]),
+ ct:timetrap({seconds, 40}),
+ Config;
+
+init_per_testcase(TestCase, Config) when TestCase == ssl_accept_timeout;
+ TestCase == client_closes_socket;
+ TestCase == downgrade ->
ct:log("TLS/SSL version ~p~n ", [tls_record:supported_protocol_versions()]),
ct:timetrap({seconds, 15}),
Config;
@@ -311,6 +332,14 @@ init_per_testcase(clear_pem_cache, Config) ->
ct:log("TLS/SSL version ~p~n ", [tls_record:supported_protocol_versions()]),
ct:timetrap({seconds, 20}),
Config;
+init_per_testcase(raw_ssl_option, Config) ->
+ ct:timetrap({seconds, 5}),
+ case os:type() of
+ {unix,linux} ->
+ Config;
+ _ ->
+ {skip, "Raw options are platform-specific"}
+ end;
init_per_testcase(_TestCase, Config) ->
ct:log("TLS/SSL version ~p~n ", [tls_record:supported_protocol_versions()]),
@@ -1136,6 +1165,23 @@ ssl_options_not_proplist(Config) when is_list(Config) ->
BadOption]).
%%--------------------------------------------------------------------
+raw_ssl_option() ->
+ [{doc,"Ensure that a single 'raw' option is passed to ssl:listen correctly."}].
+
+raw_ssl_option(Config) when is_list(Config) ->
+ % 'raw' option values are platform-specific; these are the Linux values:
+ IpProtoTcp = 6,
+ % Use TCP_KEEPIDLE, because (e.g.) TCP_MAXSEG can't be read back reliably.
+ TcpKeepIdle = 4,
+ KeepAliveTimeSecs = 55,
+ LOptions = [{raw, IpProtoTcp, TcpKeepIdle, <<KeepAliveTimeSecs:32/native>>}],
+ {ok, LSocket} = ssl:listen(0, LOptions),
+ % Per http://www.erlang.org/doc/man/inet.html#getopts-2, we have to specify
+ % exactly which raw option we want, and the size of the buffer.
+ {ok, [{raw, IpProtoTcp, TcpKeepIdle, <<KeepAliveTimeSecs:32/native>>}]} = ssl:getopts(LSocket, [{raw, IpProtoTcp, TcpKeepIdle, 4}]).
+
+
+%%--------------------------------------------------------------------
versions() ->
[{doc,"Test API function versions/0"}].
@@ -1391,6 +1437,53 @@ upgrade_with_timeout(Config) when is_list(Config) ->
ssl_test_lib:close(Client).
%%--------------------------------------------------------------------
+downgrade() ->
+ [{doc,"Test that you can downgarde an ssl connection to an tcp connection"}].
+downgrade(Config) when is_list(Config) ->
+ ClientOpts = ?config(client_opts, Config),
+ ServerOpts = ?config(server_opts, Config),
+
+ {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config),
+
+ Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0},
+ {from, self()},
+ {mfa, {?MODULE, tls_downgrade, []}},
+ {options, [{active, false} | ServerOpts]}]),
+ Port = ssl_test_lib:inet_port(Server),
+ Client = ssl_test_lib:start_client([{node, ClientNode}, {port, Port},
+ {host, Hostname},
+ {from, self()},
+ {mfa, {?MODULE, tls_downgrade, []}},
+ {options, [{active, false} |ClientOpts]}]),
+
+ ssl_test_lib:check_result(Server, ok, Client, ok),
+ ssl_test_lib:close(Server),
+ ssl_test_lib:close(Client).
+
+%%--------------------------------------------------------------------
+close_with_timeout() ->
+ [{doc,"Test normal (not downgrade) ssl:close/2"}].
+close_with_timeout(Config) when is_list(Config) ->
+ ClientOpts = ?config(client_opts, Config),
+ ServerOpts = ?config(server_opts, Config),
+
+ {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config),
+
+ Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0},
+ {from, self()},
+ {mfa, {?MODULE, tls_close, []}},
+ {options,[{active, false} | ServerOpts]}]),
+ Port = ssl_test_lib:inet_port(Server),
+ Client = ssl_test_lib:start_client([{node, ClientNode}, {port, Port},
+ {host, Hostname},
+ {from, self()},
+ {mfa, {?MODULE, tls_close, []}},
+ {options, [{active, false} |ClientOpts]}]),
+
+ ssl_test_lib:check_result(Server, ok, Client, ok).
+
+
+%%--------------------------------------------------------------------
tcp_connect() ->
[{doc,"Test what happens when a tcp tries to connect, i,e. a bad (ssl) packet is sent first"}].
@@ -1428,6 +1521,7 @@ tcp_connect_big(Config) when is_list(Config) ->
{_, ServerNode, Hostname} = ssl_test_lib:run_where(Config),
TcpOpts = [binary, {reuseaddr, true}],
+ Rand = crypto:rand_bytes(?MAX_CIPHER_TEXT_LENGTH+1),
Server = ssl_test_lib:start_upgrade_server_error([{node, ServerNode}, {port, 0},
{from, self()},
{timeout, 5000},
@@ -1439,7 +1533,6 @@ tcp_connect_big(Config) when is_list(Config) ->
{ok, Socket} = gen_tcp:connect(Hostname, Port, [binary, {packet, 0}]),
ct:log("Testcase ~p connected to Server ~p ~n", [self(), Server]),
- Rand = crypto:rand_bytes(?MAX_CIPHER_TEXT_LENGTH+1),
gen_tcp:send(Socket, <<?BYTE(0),
?BYTE(3), ?BYTE(1), ?UINT16(?MAX_CIPHER_TEXT_LENGTH), Rand/binary>>),
@@ -2831,6 +2924,43 @@ hibernate(Config) ->
ssl_test_lib:close(Client).
%%--------------------------------------------------------------------
+
+hibernate_right_away() ->
+ [{doc,"Check that an SSL connection that is configured to hibernate "
+ "after 0 or 1 milliseconds hibernates as soon as possible and not "
+ "crashes"}].
+
+hibernate_right_away(Config) ->
+ ClientOpts = ?config(client_opts, Config),
+ ServerOpts = ?config(server_opts, Config),
+
+ {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config),
+
+ StartServerOpts = [{node, ServerNode}, {port, 0},
+ {from, self()},
+ {mfa, {ssl_test_lib, send_recv_result_active, []}},
+ {options, ServerOpts}],
+ StartClientOpts = [return_socket,
+ {node, ClientNode},
+ {host, Hostname},
+ {from, self()},
+ {mfa, {ssl_test_lib, send_recv_result_active, []}}],
+
+ Server1 = ssl_test_lib:start_server(StartServerOpts),
+ Port1 = ssl_test_lib:inet_port(Server1),
+ {Client1, #sslsocket{}} = ssl_test_lib:start_client(StartClientOpts ++
+ [{port, Port1}, {options, [{hibernate_after, 0}|ClientOpts]}]),
+ ssl_test_lib:close(Server1),
+ ssl_test_lib:close(Client1),
+
+ Server2 = ssl_test_lib:start_server(StartServerOpts),
+ Port2 = ssl_test_lib:inet_port(Server2),
+ {Client2, #sslsocket{}} = ssl_test_lib:start_client(StartClientOpts ++
+ [{port, Port2}, {options, [{hibernate_after, 1}|ClientOpts]}]),
+ ssl_test_lib:close(Server2),
+ ssl_test_lib:close(Client2).
+
+%%--------------------------------------------------------------------
listen_socket() ->
[{doc,"Check error handling and inet compliance when calling API functions with listen sockets."}].
@@ -3914,6 +4044,33 @@ connect_dist_c(S) ->
{ok, Test} = ssl:recv(S, 0, 10000),
ok.
+tls_downgrade(Socket) ->
+ ok = ssl_test_lib:send_recv_result(Socket),
+ case ssl:close(Socket, {self(), 10000}) of
+ {ok, TCPSocket} ->
+ inet:setopts(TCPSocket, [{active, true}]),
+ gen_tcp:send(TCPSocket, "Downgraded"),
+ receive
+ {tcp, TCPSocket, <<"Downgraded">>} ->
+ ok;
+ {tcp_closed, TCPSocket} ->
+ ct:pal("Peer timed out, downgrade aborted"),
+ ok;
+ Other ->
+ {error, Other}
+ end;
+ {error, timeout} ->
+ ct:pal("Timed out, downgrade aborted"),
+ ok;
+ Fail ->
+ {error, Fail}
+ end.
+
+tls_close(Socket) ->
+ ok = ssl_test_lib:send_recv_result(Socket),
+ ok = ssl:close(Socket, 5000).
+
+
%% First two clauses handles 1/n-1 splitting countermeasure Rizzo/Duong-Beast
treashold(N, {3,0}) ->
(N div 2) + 1;
diff --git a/lib/ssl/test/ssl_dist_SUITE.erl b/lib/ssl/test/ssl_dist_SUITE.erl
index 72d62b29a7..092015d3d8 100644
--- a/lib/ssl/test/ssl_dist_SUITE.erl
+++ b/lib/ssl/test/ssl_dist_SUITE.erl
@@ -40,7 +40,8 @@
%% Common Test interface functions -----------------------------------
%%--------------------------------------------------------------------
all() ->
- [basic, payload, plain_options, plain_verify_options].
+ [basic, payload, plain_options, plain_verify_options, nodelay_option,
+ listen_port_options, listen_options, use_interface].
groups() ->
[].
@@ -250,6 +251,146 @@ plain_verify_options(Config) when is_list(Config) ->
stop_ssl_node(NH1),
stop_ssl_node(NH2),
success(Config).
+%%--------------------------------------------------------------------
+nodelay_option() ->
+ [{doc,"Test specifying dist_nodelay option"}].
+nodelay_option(Config) ->
+ try
+ %% The default is 'true', so try setting it to 'false'.
+ application:set_env(kernel, dist_nodelay, false),
+ basic(Config)
+ after
+ application:unset_env(kernel, dist_nodelay)
+ end.
+
+listen_port_options() ->
+ [{doc, "Test specifying listening ports"}].
+listen_port_options(Config) when is_list(Config) ->
+ %% Start a node, and get the port number it's listening on.
+ NH1 = start_ssl_node(Config),
+ Node1 = NH1#node_handle.nodename,
+ Name1 = lists:takewhile(fun(C) -> C =/= $@ end, atom_to_list(Node1)),
+ {ok, NodesPorts} = apply_on_ssl_node(NH1, fun net_adm:names/0),
+ {Name1, Port1} = lists:keyfind(Name1, 1, NodesPorts),
+
+ %% Now start a second node, configuring it to use the same port
+ %% number.
+ PortOpt1 = "-kernel inet_dist_listen_min " ++ integer_to_list(Port1) ++
+ " inet_dist_listen_max " ++ integer_to_list(Port1),
+
+ try start_ssl_node([{additional_dist_opts, PortOpt1} | Config]) of
+ #node_handle{} ->
+ %% If the node was able to start, it didn't take the port
+ %% option into account.
+ exit(unexpected_success)
+ catch
+ exit:{accept_failed, timeout} ->
+ %% The node failed to start, as expected.
+ ok
+ end,
+
+ %% Try again, now specifying a high max port.
+ PortOpt2 = "-kernel inet_dist_listen_min " ++ integer_to_list(Port1) ++
+ " inet_dist_listen_max 65535",
+ NH2 = start_ssl_node([{additional_dist_opts, PortOpt2} | Config]),
+ Node2 = NH2#node_handle.nodename,
+ Name2 = lists:takewhile(fun(C) -> C =/= $@ end, atom_to_list(Node2)),
+ {ok, NodesPorts2} = apply_on_ssl_node(NH2, fun net_adm:names/0),
+ {Name2, Port2} = lists:keyfind(Name2, 1, NodesPorts2),
+
+ %% The new port should be higher:
+ if Port2 > Port1 ->
+ ok;
+ true ->
+ error({port, Port2, not_higher_than, Port1})
+ end,
+
+ stop_ssl_node(NH1),
+ stop_ssl_node(NH2),
+ success(Config).
+%%--------------------------------------------------------------------
+listen_options() ->
+ [{doc, "Test inet_dist_listen_options"}].
+listen_options(Config) when is_list(Config) ->
+ Prio = 1,
+ case gen_udp:open(0, [{priority,Prio}]) of
+ {ok,Socket} ->
+ case inet:getopts(Socket, [priority]) of
+ {ok,[{priority,Prio}]} ->
+ ok = gen_udp:close(Socket),
+ do_listen_options(Prio, Config);
+ _ ->
+ ok = gen_udp:close(Socket),
+ {skip,
+ "Can not set priority "++integer_to_list(Prio)++
+ " on socket"}
+ end;
+ {error,_} ->
+ {skip, "Can not set priority on socket"}
+ end.
+
+do_listen_options(Prio, Config) ->
+ PriorityString0 = "[{priority,"++integer_to_list(Prio)++"}]",
+ PriorityString =
+ case os:cmd("echo [{a,1}]") of
+ "[{a,1}]"++_ ->
+ PriorityString0;
+ _ ->
+ %% Some shells need quoting of [{}]
+ "'"++PriorityString0++"'"
+ end,
+
+ Options = "-kernel inet_dist_listen_options " ++ PriorityString,
+
+ NH1 = start_ssl_node([{additional_dist_opts, Options} | Config]),
+ NH2 = start_ssl_node([{additional_dist_opts, Options} | Config]),
+ Node2 = NH2#node_handle.nodename,
+
+ pong = apply_on_ssl_node(NH1, fun () -> net_adm:ping(Node2) end),
+
+ PrioritiesNode1 =
+ apply_on_ssl_node(NH1, fun get_socket_priorities/0),
+ PrioritiesNode2 =
+ apply_on_ssl_node(NH2, fun get_socket_priorities/0),
+
+ Elevated1 = [P || P <- PrioritiesNode1, P =:= Prio],
+ ?t:format("Elevated1: ~p~n", [Elevated1]),
+ Elevated2 = [P || P <- PrioritiesNode2, P =:= Prio],
+ ?t:format("Elevated2: ~p~n", [Elevated2]),
+ [_|_] = Elevated1,
+ [_|_] = Elevated2,
+
+ stop_ssl_node(NH1),
+ stop_ssl_node(NH2),
+ success(Config).
+%%--------------------------------------------------------------------
+use_interface() ->
+ [{doc, "Test inet_dist_use_interface"}].
+use_interface(Config) when is_list(Config) ->
+ %% Force the node to listen only on the loopback interface.
+ IpString = "'{127,0,0,1}'",
+ Options = "-kernel inet_dist_use_interface " ++ IpString,
+
+ %% Start a node, and get the port number it's listening on.
+ NH1 = start_ssl_node([{additional_dist_opts, Options} | Config]),
+ Node1 = NH1#node_handle.nodename,
+ Name = lists:takewhile(fun(C) -> C =/= $@ end, atom_to_list(Node1)),
+ {ok, NodesPorts} = apply_on_ssl_node(NH1, fun net_adm:names/0),
+ {Name, Port} = lists:keyfind(Name, 1, NodesPorts),
+
+ %% Now find the socket listening on that port, and check its sockname.
+ Sockets = apply_on_ssl_node(
+ NH1,
+ fun() ->
+ [inet:sockname(P) ||
+ P <- erlang:ports(),
+ {ok, Port} =:= (catch inet:port(P))]
+ end),
+ %% And check that it's actually listening on localhost.
+ [{ok,{{127,0,0,1},Port}}] = Sockets,
+
+ stop_ssl_node(NH1),
+ success(Config).
%%--------------------------------------------------------------------
%%% Internal functions -----------------------------------------------
@@ -264,6 +405,12 @@ tstsrvr_format(Fmt, ArgList) ->
send_to_tstcntrl(Message) ->
send_to_tstsrvr({message, Message}).
+get_socket_priorities() ->
+ [Priority ||
+ {ok,[{priority,Priority}]} <-
+ [inet:getopts(Port, [priority]) ||
+ Port <- erlang:ports(),
+ element(2, erlang:port_info(Port, name)) =:= "tcp_inet"]].
%%
%% test_server side api
diff --git a/lib/ssl/test/ssl_payload_SUITE.erl b/lib/ssl/test/ssl_payload_SUITE.erl
index b05f19d756..fb3890a811 100644
--- a/lib/ssl/test/ssl_payload_SUITE.erl
+++ b/lib/ssl/test/ssl_payload_SUITE.erl
@@ -105,7 +105,16 @@ init_per_testcase(TestCase, Config) when TestCase == server_echos_passive_huge;
TestCase == client_echos_passive_huge;
TestCase == client_echos_active_once_huge;
TestCase == client_echos_active_huge ->
- ct:timetrap({seconds, 30}),
+ ct:timetrap({seconds, 90}),
+ Config;
+
+init_per_testcase(TestCase, Config) when TestCase == server_echos_passive_big;
+ TestCase == server_echos_active_once_big;
+ TestCase == server_echos_active_big;
+ TestCase == client_echos_passive_big;
+ TestCase == client_echos_active_once_big;
+ TestCase == client_echos_active_big ->
+ ct:timetrap({seconds, 60}),
Config;
init_per_testcase(_TestCase, Config) ->
diff --git a/lib/ssl/test/ssl_session_cache_SUITE.erl b/lib/ssl/test/ssl_session_cache_SUITE.erl
index 8ddc5db4b2..85345c814f 100644
--- a/lib/ssl/test/ssl_session_cache_SUITE.erl
+++ b/lib/ssl/test/ssl_session_cache_SUITE.erl
@@ -31,6 +31,7 @@
-define(SLEEP, 500).
-define(TIMEOUT, 60000).
-define(LONG_TIMEOUT, 600000).
+-define(MAX_TABLE_SIZE, 5).
-behaviour(ssl_session_cache_api).
@@ -45,7 +46,10 @@
all() ->
[session_cleanup,
session_cache_process_list,
- session_cache_process_mnesia].
+ session_cache_process_mnesia,
+ client_unique_session,
+ max_table_size
+ ].
groups() ->
[].
@@ -90,8 +94,18 @@ init_per_testcase(session_cleanup, Config) ->
ct:timetrap({seconds, 20}),
Config;
-init_per_testcase(_TestCase, Config) ->
- ct:timetrap({seconds, 5}),
+init_per_testcase(client_unique_session, Config) ->
+ ct:timetrap({seconds, 40}),
+ Config;
+
+init_per_testcase(max_table_size, Config) ->
+ ssl:stop(),
+ application:load(ssl),
+ application:set_env(ssl, session_cache_server_max, ?MAX_TABLE_SIZE),
+ application:set_env(ssl, session_cache_client_max, ?MAX_TABLE_SIZE),
+ application:set_env(ssl, session_delay_cleanup_time, ?DELAY),
+ ssl:start(),
+ ct:timetrap({seconds, 40}),
Config.
init_customized_session_cache(Type, Config) ->
@@ -121,6 +135,10 @@ end_per_testcase(session_cleanup, Config) ->
application:unset_env(ssl, session_delay_cleanup_time),
application:unset_env(ssl, session_lifetime),
end_per_testcase(default_action, Config);
+end_per_testcase(max_table_size, Config) ->
+ application:unset_env(ssl, session_cach_server_max),
+ application:unset_env(ssl, session_cach_client_max),
+ end_per_testcase(default_action, Config);
end_per_testcase(Case, Config) when Case == session_cache_process_list;
Case == session_cache_process_mnesia ->
ets:delete(ssl_test),
@@ -131,10 +149,41 @@ end_per_testcase(_, Config) ->
%%--------------------------------------------------------------------
%% Test Cases --------------------------------------------------------
%%--------------------------------------------------------------------
+client_unique_session() ->
+ [{doc, "Test session table does not grow when client "
+ "sets up many connections"}].
+client_unique_session(Config) when is_list(Config) ->
+ process_flag(trap_exit, true),
+ ClientOpts = ?config(client_opts, Config),
+ ServerOpts = ?config(server_opts, Config),
+ {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, no_result, []}},
+ {tcp_options, [{active, false}]},
+ {options, ServerOpts}]),
+ Port = ssl_test_lib:inet_port(Server),
+ LastClient = clients_start(Server,
+ ClientNode, Hostname, Port, ClientOpts, client_unique_session, 20),
+ receive
+ {LastClient, {ok, _}} ->
+ ok
+ end,
+ {status, _, _, StatusInfo} = sys:get_status(whereis(ssl_manager)),
+ [_, _,_, _, Prop] = StatusInfo,
+ State = ssl_test_lib:state(Prop),
+ ClientCache = element(2, State),
+
+ 1 = ssl_session_cache:size(ClientCache),
+
+ ssl_test_lib:close(Server, 500),
+ ssl_test_lib:close(LastClient).
+
session_cleanup() ->
[{doc, "Test that sessions are cleand up eventually, so that the session table "
"does not grow and grow ..."}].
-session_cleanup(Config)when is_list(Config) ->
+session_cleanup(Config) when is_list(Config) ->
process_flag(trap_exit, true),
ClientOpts = ?config(client_opts, Config),
ServerOpts = ?config(server_opts, Config),
@@ -148,9 +197,9 @@ session_cleanup(Config)when is_list(Config) ->
Port = ssl_test_lib:inet_port(Server),
Client =
ssl_test_lib:start_client([{node, ClientNode},
- {port, Port}, {host, Hostname},
+ {port, Port}, {host, Hostname},
{mfa, {ssl_test_lib, no_result, []}},
- {from, self()}, {options, ClientOpts}]),
+ {from, self()}, {options, ClientOpts}]),
SessionInfo =
receive
{Server, Info} ->
@@ -192,35 +241,7 @@ session_cleanup(Config)when is_list(Config) ->
ssl_test_lib:close(Server),
ssl_test_lib:close(Client).
-check_timer(Timer) ->
- case erlang:read_timer(Timer) of
- false ->
- {status, _, _, _} = sys:get_status(whereis(ssl_manager)),
- timer:sleep(?SLEEP),
- {status, _, _, _} = sys:get_status(whereis(ssl_manager)),
- ok;
- Int ->
- ct:sleep(Int),
- check_timer(Timer)
- end.
-get_delay_timers() ->
- {status, _, _, StatusInfo} = sys:get_status(whereis(ssl_manager)),
- [_, _,_, _, Prop] = StatusInfo,
- State = ssl_test_lib:state(Prop),
- case element(8, State) of
- {undefined, undefined} ->
- ct:sleep(?SLEEP),
- get_delay_timers();
- {undefined, _} ->
- ct:sleep(?SLEEP),
- get_delay_timers();
- {_, undefined} ->
- ct:sleep(?SLEEP),
- get_delay_timers();
- DelayTimers ->
- DelayTimers
- end.
%%--------------------------------------------------------------------
session_cache_process_list() ->
[{doc,"Test reuse of sessions (short handshake)"}].
@@ -233,6 +254,42 @@ session_cache_process_mnesia(Config) when is_list(Config) ->
session_cache_process(mnesia,Config).
%%--------------------------------------------------------------------
+
+max_table_size() ->
+ [{doc,"Test max limit on session table"}].
+max_table_size(Config) when is_list(Config) ->
+ process_flag(trap_exit, true),
+ ClientOpts = ?config(client_verification_opts, Config),
+ ServerOpts = ?config(server_verification_opts, Config),
+ {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, no_result, []}},
+ {tcp_options, [{active, false}]},
+ {options, ServerOpts}]),
+ Port = ssl_test_lib:inet_port(Server),
+ LastClient = clients_start(Server,
+ ClientNode, Hostname, Port, ClientOpts, max_table_size, 20),
+ receive
+ {LastClient, {ok, _}} ->
+ ok
+ end,
+ ct:sleep(1000),
+ {status, _, _, StatusInfo} = sys:get_status(whereis(ssl_manager)),
+ [_, _,_, _, Prop] = StatusInfo,
+ State = ssl_test_lib:state(Prop),
+ ClientCache = element(2, State),
+ ServerCache = element(3, State),
+ N = ssl_session_cache:size(ServerCache),
+ M = ssl_session_cache:size(ClientCache),
+ ct:pal("~p",[{N, M}]),
+ ssl_test_lib:close(Server, 500),
+ ssl_test_lib:close(LastClient),
+ true = N =< ?MAX_TABLE_SIZE,
+ true = M =< ?MAX_TABLE_SIZE.
+
+%%--------------------------------------------------------------------
%%% Session cache API callbacks
%%--------------------------------------------------------------------
@@ -325,8 +382,8 @@ select_session(Cache, PartialKey) ->
mnesia ->
Sel = fun() ->
mnesia:select(Cache,
- [{{Cache,{PartialKey,'$1'}, '$2'},
- [],['$$']}])
+ [{{Cache,{PartialKey,'_'}, '$1'},
+ [],['$1']}])
end,
{atomic, Res} = mnesia:transaction(Sel),
Res
@@ -354,8 +411,8 @@ session_loop(Sess) ->
Pid ! {self(), Res},
session_loop(Sess);
{Pid,select_session,PKey} ->
- Sel = fun({{PKey0, Id},Session}, Acc) when PKey == PKey0 ->
- [[Id, Session]|Acc];
+ Sel = fun({{PKey0, _Id},Session}, Acc) when PKey == PKey0 ->
+ [Session | Acc];
(_,Acc) ->
Acc
end,
@@ -370,3 +427,75 @@ session_loop(Sess) ->
session_cache_process(_Type,Config) when is_list(Config) ->
ssl_basic_SUITE:reuse_session(Config).
+
+
+clients_start(_Server, ClientNode, Hostname, Port, ClientOpts, Test, 0) ->
+ %% Make sure session is registered
+ ct:sleep(?SLEEP * 2),
+ ssl_test_lib:start_client([{node, ClientNode},
+ {port, Port}, {host, Hostname},
+ {mfa, {?MODULE, connection_info_result, []}},
+ {from, self()}, {options, test_copts(Test, 0, ClientOpts)}]);
+clients_start(Server, ClientNode, Hostname, Port, ClientOpts, Test, N) ->
+ spawn_link(ssl_test_lib, start_client,
+ [[{node, ClientNode},
+ {port, Port}, {host, Hostname},
+ {mfa, {ssl_test_lib, no_result, []}},
+ {from, self()}, {options, test_copts(Test, N, ClientOpts)}]]),
+ Server ! listen,
+ wait_for_server(),
+ clients_start(Server, ClientNode, Hostname, Port, ClientOpts, Test, N-1).
+
+connection_info_result(Socket) ->
+ ssl:connection_information(Socket, [protocol, cipher_suite]).
+
+check_timer(Timer) ->
+ case erlang:read_timer(Timer) of
+ false ->
+ {status, _, _, _} = sys:get_status(whereis(ssl_manager)),
+ timer:sleep(?SLEEP),
+ {status, _, _, _} = sys:get_status(whereis(ssl_manager)),
+ ok;
+ Int ->
+ ct:sleep(Int),
+ check_timer(Timer)
+ end.
+
+get_delay_timers() ->
+ {status, _, _, StatusInfo} = sys:get_status(whereis(ssl_manager)),
+ [_, _,_, _, Prop] = StatusInfo,
+ State = ssl_test_lib:state(Prop),
+ case element(8, State) of
+ {undefined, undefined} ->
+ ct:sleep(?SLEEP),
+ get_delay_timers();
+ {undefined, _} ->
+ ct:sleep(?SLEEP),
+ get_delay_timers();
+ {_, undefined} ->
+ ct:sleep(?SLEEP),
+ get_delay_timers();
+ DelayTimers ->
+ DelayTimers
+ end.
+
+wait_for_server() ->
+ ct:sleep(100).
+
+
+test_copts(_, 0, ClientOpts) ->
+ ClientOpts;
+test_copts(max_table_size, N, ClientOpts) ->
+ Version = tls_record:highest_protocol_version([]),
+ CipherSuites = %%lists:map(fun(X) -> ssl_cipher:suite_definition(X) end, ssl_cipher:filter_suites(ssl_cipher:suites(Version))),
+[ Y|| Y = {Alg,_, _, _} <- lists:map(fun(X) -> ssl_cipher:suite_definition(X) end, ssl_cipher:filter_suites(ssl_cipher:suites(Version))), Alg =/= ecdhe_ecdsa, Alg =/= ecdh_ecdsa, Alg =/= ecdh_rsa, Alg =/= ecdhe_rsa, Alg =/= dhe_dss, Alg =/= dss],
+ case length(CipherSuites) of
+ M when M >= N ->
+ Cipher = lists:nth(N, CipherSuites),
+ ct:pal("~p",[Cipher]),
+ [{ciphers, [Cipher]} | ClientOpts];
+ _ ->
+ ClientOpts
+ end;
+test_copts(_, _, ClientOpts) ->
+ ClientOpts.
diff --git a/lib/ssl/test/ssl_test_lib.erl b/lib/ssl/test/ssl_test_lib.erl
index da744f7368..77c29668b5 100644
--- a/lib/ssl/test/ssl_test_lib.erl
+++ b/lib/ssl/test/ssl_test_lib.erl
@@ -226,6 +226,17 @@ run_client(Opts) ->
ct:log("~p:~p~nClient faild several times: connection failed: ~p ~n", [?MODULE,?LINE, Reason]),
Pid ! {self(), {error, Reason}}
end;
+ {error, econnreset = Reason} ->
+ case get(retries) of
+ N when N < 5 ->
+ ct:log("~p:~p~neconnreset retries=~p sleep ~p",[?MODULE,?LINE, N,?SLEEP]),
+ put(retries, N+1),
+ ct:sleep(?SLEEP),
+ run_client(Opts);
+ _ ->
+ ct:log("~p:~p~nClient faild several times: connection failed: ~p ~n", [?MODULE,?LINE, Reason]),
+ Pid ! {self(), {error, Reason}}
+ end;
{error, Reason} ->
ct:log("~p:~p~nClient: connection failed: ~p ~n", [?MODULE,?LINE, Reason]),
Pid ! {connect_failed, Reason};
@@ -241,7 +252,21 @@ close(Pid) ->
receive
{'DOWN', Monitor, process, Pid, Reason} ->
erlang:demonitor(Monitor),
- ct:log("~p:~p~nPid: ~p down due to:~p ~n", [?MODULE,?LINE, Pid, Reason])
+ ct:log("~p:~p~nPid: ~p down due to:~p ~n", [?MODULE,?LINE, Pid, Reason])
+
+ end.
+
+close(Pid, Timeout) ->
+ ct:log("~p:~p~n Close ~p ~n", [?MODULE,?LINE, Pid]),
+ Monitor = erlang:monitor(process, Pid),
+ Pid ! close,
+ receive
+ {'DOWN', Monitor, process, Pid, Reason} ->
+ erlang:demonitor(Monitor),
+ ct:log("~p:~p~nPid: ~p down due to:~p ~n", [?MODULE,?LINE, Pid, Reason])
+ after
+ Timeout ->
+ exit(Pid, kill)
end.
check_result(Server, ServerMsg, Client, ClientMsg) ->
@@ -360,7 +385,7 @@ cert_options(Config) ->
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_opts, []},
{client_verification_opts, [{cacertfile, ClientCaCertFile},
{certfile, ClientCertFile},
{keyfile, ClientKeyFile},
@@ -1076,6 +1101,9 @@ is_sane_ecc(openssl) ->
"OpenSSL 1.0.0" ++ _ -> % Known bug in openssl
%% manifests as SSL_CHECK_SERVERHELLO_TLSEXT:tls invalid ecpointformat list
false;
+ "OpenSSL 1.0.1l" ++ _ ->
+ %% Breaks signature verification
+ false;
"OpenSSL 0.9.8" ++ _ -> % Does not support ECC
false;
"OpenSSL 0.9.7" ++ _ -> % Does not support ECC
@@ -1130,23 +1158,27 @@ cipher_restriction(Config0) ->
end.
check_sane_openssl_version(Version) ->
- case {Version, os:cmd("openssl version")} of
- {_, "OpenSSL 1.0.2" ++ _} ->
- true;
- {_, "OpenSSL 1.0.1" ++ _} ->
- true;
- {'tlsv1.2', "OpenSSL 1.0" ++ _} ->
- false;
- {'tlsv1.1', "OpenSSL 1.0" ++ _} ->
- false;
- {'tlsv1.2', "OpenSSL 0" ++ _} ->
- false;
- {'tlsv1.1', "OpenSSL 0" ++ _} ->
- false;
- {_, _} ->
- true
+ case supports_ssl_tls_version(Version) of
+ true ->
+ case {Version, os:cmd("openssl version")} of
+ {_, "OpenSSL 1.0.2" ++ _} ->
+ true;
+ {_, "OpenSSL 1.0.1" ++ _} ->
+ true;
+ {'tlsv1.2', "OpenSSL 1.0" ++ _} ->
+ false;
+ {'tlsv1.1', "OpenSSL 1.0" ++ _} ->
+ false;
+ {'tlsv1.2', "OpenSSL 0" ++ _} ->
+ false;
+ {'tlsv1.1', "OpenSSL 0" ++ _} ->
+ false;
+ {_, _} ->
+ true
+ end;
+ false ->
+ false
end.
-
enough_openssl_crl_support("OpenSSL 0." ++ _) -> false;
enough_openssl_crl_support(_) -> true.
@@ -1164,13 +1196,15 @@ wait_for_openssl_server(Port, N) ->
end.
version_flag(tlsv1) ->
- " -tls1 ";
+ "-tls1";
version_flag('tlsv1.1') ->
- " -tls1_1 ";
+ "-tls1_1";
version_flag('tlsv1.2') ->
- " -tls1_2 ";
+ "-tls1_2";
version_flag(sslv3) ->
- " -ssl3 ".
+ "-ssl3";
+version_flag(sslv2) ->
+ "-ssl2".
filter_suites(Ciphers0) ->
Version = tls_record:highest_protocol_version([]),
@@ -1215,3 +1249,31 @@ close_loop(Port, Time, SentClose) ->
ct:log("Timeout~n",[])
end
end.
+
+portable_open_port(Exe, Args) ->
+ AbsPath = os:find_executable(Exe),
+ ct:pal("open_port({spawn_executable, ~p}, [{args, ~p}, stderr_to_stdout]).", [AbsPath, Args]),
+ open_port({spawn_executable, AbsPath},
+ [{args, Args}, stderr_to_stdout]).
+
+supports_ssl_tls_version(Version) ->
+ VersionFlag = version_flag(Version),
+ Exe = "openssl",
+ Args = ["s_client", VersionFlag],
+ Port = ssl_test_lib:portable_open_port(Exe, Args),
+ do_supports_ssl_tls_version(Port).
+
+do_supports_ssl_tls_version(Port) ->
+ receive
+ {Port, {data, "unknown option" ++ _}} ->
+ false;
+ {Port, {data, Data}} ->
+ case lists:member("error", string:tokens(Data, ":")) of
+ true ->
+ false;
+ false ->
+ do_supports_ssl_tls_version(Port)
+ end
+ after 500 ->
+ true
+ end.
diff --git a/lib/ssl/test/ssl_to_openssl_SUITE.erl b/lib/ssl/test/ssl_to_openssl_SUITE.erl
index 16b6cb10b9..bcdefb5fca 100644
--- a/lib/ssl/test/ssl_to_openssl_SUITE.erl
+++ b/lib/ssl/test/ssl_to_openssl_SUITE.erl
@@ -112,6 +112,7 @@ init_per_suite(Config0) ->
false ->
{skip, "Openssl not found"};
_ ->
+ ct:pal("Version: ~p", [os:cmd("openssl version")]),
catch crypto:stop(),
try crypto:start() of
ok ->
@@ -174,7 +175,12 @@ special_init(TestCase, Config)
check_sane_openssl_renegotaite(Config, Version);
special_init(ssl2_erlang_server_openssl_client, Config) ->
- check_sane_openssl_sslv2(Config);
+ case ssl_test_lib:supports_ssl_tls_version(sslv2) of
+ true ->
+ Config;
+ false ->
+ {skip, "sslv2 not supported by openssl"}
+ end;
special_init(TestCase, Config)
when TestCase == erlang_client_alpn_openssl_server_alpn;
@@ -262,12 +268,11 @@ basic_erlang_client_openssl_server(Config) when is_list(Config) ->
CertFile = proplists:get_value(certfile, ServerOpts),
KeyFile = proplists:get_value(keyfile, ServerOpts),
- Cmd = "openssl s_server -accept " ++ integer_to_list(Port) ++
- " -cert " ++ CertFile ++ " -key " ++ KeyFile,
-
- ct:log("openssl cmd: ~p~n", [Cmd]),
+ Exe = "openssl",
+ Args = ["s_server", "-accept", integer_to_list(Port),
+ "-cert", CertFile, "-key", KeyFile],
- OpensslPort = open_port({spawn, Cmd}, [stderr_to_stdout]),
+ OpensslPort = ssl_test_lib:portable_open_port(Exe, Args),
ssl_test_lib:wait_for_openssl_server(Port),
@@ -302,13 +307,11 @@ basic_erlang_server_openssl_client(Config) when is_list(Config) ->
{mfa, {?MODULE, erlang_ssl_receive, [Data]}},
{options, ServerOpts}]),
Port = ssl_test_lib:inet_port(Server),
-
- Cmd = "openssl s_client -port " ++ integer_to_list(Port) ++
- " -host localhost" ++ workaround_openssl_s_clinent(),
-
- ct:log("openssl cmd: ~p~n", [Cmd]),
- OpenSslPort = open_port({spawn, Cmd}, [stderr_to_stdout]),
+ Exe = "openssl",
+ Args = ["s_client", "-connect", "localhost:" ++ integer_to_list(Port) | workaround_openssl_s_clinent()],
+
+ OpenSslPort = ssl_test_lib:portable_open_port(Exe, Args),
true = port_command(OpenSslPort, Data),
ssl_test_lib:check_result(Server, ok),
@@ -334,12 +337,12 @@ erlang_client_openssl_server(Config) when is_list(Config) ->
CertFile = proplists:get_value(certfile, ServerOpts),
KeyFile = proplists:get_value(keyfile, ServerOpts),
Version = tls_record:protocol_version(tls_record:highest_protocol_version([])),
- Cmd = "openssl s_server -accept " ++ integer_to_list(Port) ++ ssl_test_lib:version_flag(Version) ++
- " -cert " ++ CertFile ++ " -key " ++ KeyFile,
-
- ct:log("openssl cmd: ~p~n", [Cmd]),
-
- OpensslPort = open_port({spawn, Cmd}, [stderr_to_stdout]),
+ Exe = "openssl",
+ Args = ["s_server", "-accept", integer_to_list(Port),
+ ssl_test_lib:version_flag(Version),
+ "-cert", CertFile, "-key", KeyFile],
+
+ OpensslPort = ssl_test_lib:portable_open_port(Exe, Args),
ssl_test_lib:wait_for_openssl_server(Port),
@@ -376,12 +379,12 @@ erlang_server_openssl_client(Config) when is_list(Config) ->
Port = ssl_test_lib:inet_port(Server),
Version = tls_record:protocol_version(tls_record:highest_protocol_version([])),
- Cmd = "openssl s_client -port " ++ integer_to_list(Port) ++ ssl_test_lib:version_flag(Version) ++
- " -host localhost",
+ Exe = "openssl",
+ Args = ["s_client", "-connect", "localhost: " ++ integer_to_list(Port),
+ ssl_test_lib:version_flag(Version)],
- ct:log("openssl cmd: ~p~n", [Cmd]),
+ OpenSslPort = ssl_test_lib:portable_open_port(Exe, Args),
- OpenSslPort = open_port({spawn, Cmd}, [stderr_to_stdout]),
true = port_command(OpenSslPort, Data),
ssl_test_lib:check_result(Server, ok),
@@ -407,14 +410,13 @@ erlang_client_openssl_server_dsa_cert(Config) when is_list(Config) ->
CertFile = proplists:get_value(certfile, ServerOpts),
KeyFile = proplists:get_value(keyfile, ServerOpts),
Version = tls_record:protocol_version(tls_record:highest_protocol_version([])),
+ Exe = "openssl",
+ Args = ["s_server", "-accept", integer_to_list(Port),
+ ssl_test_lib:version_flag(Version),
+ "-cert", CertFile, "-CAfile", CaCertFile,
+ "-key", KeyFile, "-Verify", "2", "-msg"],
- Cmd = "openssl s_server -accept " ++ integer_to_list(Port) ++ ssl_test_lib:version_flag(Version) ++
- " -cert " ++ CertFile ++ " -CAfile " ++ CaCertFile
- ++ " -key " ++ KeyFile ++ " -Verify 2 -msg",
-
- ct:log("openssl cmd: ~p~n", [Cmd]),
-
- OpensslPort = open_port({spawn, Cmd}, [stderr_to_stdout]),
+ OpensslPort = ssl_test_lib:portable_open_port(Exe, Args),
ssl_test_lib:wait_for_openssl_server(Port),
@@ -455,13 +457,14 @@ erlang_server_openssl_client_dsa_cert(Config) when is_list(Config) ->
{options, ServerOpts}]),
Port = ssl_test_lib:inet_port(Server),
Version = tls_record:protocol_version(tls_record:highest_protocol_version([])),
- Cmd = "openssl s_client -port " ++ integer_to_list(Port) ++ ssl_test_lib:version_flag(Version) ++
- " -host localhost " ++ " -cert " ++ CertFile ++ " -CAfile " ++ CaCertFile
- ++ " -key " ++ KeyFile ++ " -msg",
-
- ct:log("openssl cmd: ~p~n", [Cmd]),
-
- OpenSslPort = open_port({spawn, Cmd}, [stderr_to_stdout]),
+ Exe = "openssl",
+ Args = ["s_client", "-connect", "localhost: " ++ integer_to_list(Port),
+ ssl_test_lib:version_flag(Version),
+ "-cert", CertFile,
+ "-CAfile", CaCertFile,
+ "-key", KeyFile, "-msg"],
+
+ OpenSslPort = ssl_test_lib:portable_open_port(Exe, Args),
true = port_command(OpenSslPort, Data),
ssl_test_lib:check_result(Server, ok),
@@ -491,12 +494,13 @@ erlang_server_openssl_client_reuse_session(Config) when is_list(Config) ->
{options, ServerOpts}]),
Port = ssl_test_lib:inet_port(Server),
Version = tls_record:protocol_version(tls_record:highest_protocol_version([])),
- Cmd = "openssl s_client -port " ++ integer_to_list(Port) ++ ssl_test_lib:version_flag(Version) ++
- " -host localhost -reconnect",
-
- ct:log("openssl cmd: ~p~n", [Cmd]),
- OpenSslPort = open_port({spawn, Cmd}, [stderr_to_stdout]),
+ Exe = "openssl",
+ Args = ["s_client", "-connect", "localhost:" ++ integer_to_list(Port),
+ ssl_test_lib:version_flag(Version),
+ "-reconnect"],
+
+ OpenSslPort = ssl_test_lib:portable_open_port(Exe, Args),
true = port_command(OpenSslPort, Data),
@@ -527,12 +531,12 @@ erlang_client_openssl_server_renegotiate(Config) when is_list(Config) ->
KeyFile = proplists:get_value(keyfile, ServerOpts),
Version = tls_record:protocol_version(tls_record:highest_protocol_version([])),
- Cmd = "openssl s_server -accept " ++ integer_to_list(Port) ++ ssl_test_lib:version_flag(Version) ++
- " -cert " ++ CertFile ++ " -key " ++ KeyFile ++ " -msg",
+ Exe = "openssl",
+ Args = ["s_server", "-accept", integer_to_list(Port),
+ ssl_test_lib:version_flag(Version),
+ "-cert", CertFile, "-key", KeyFile, "-msg"],
- ct:log("openssl cmd: ~p~n", [Cmd]),
-
- OpensslPort = open_port({spawn, Cmd}, [stderr_to_stdout]),
+ OpensslPort = ssl_test_lib:portable_open_port(Exe, Args),
ssl_test_lib:wait_for_openssl_server(Port),
@@ -576,12 +580,12 @@ erlang_client_openssl_server_nowrap_seqnum(Config) when is_list(Config) ->
CertFile = proplists:get_value(certfile, ServerOpts),
KeyFile = proplists:get_value(keyfile, ServerOpts),
Version = tls_record:protocol_version(tls_record:highest_protocol_version([])),
- Cmd = "openssl s_server -accept " ++ integer_to_list(Port) ++ ssl_test_lib:version_flag(Version) ++
- " -cert " ++ CertFile ++ " -key " ++ KeyFile ++ " -msg",
+ Exe = "openssl",
+ Args = ["s_server", "-accept", integer_to_list(Port),
+ ssl_test_lib:version_flag(Version),
+ "-cert", CertFile, "-key", KeyFile, "-msg"],
- ct:log("openssl cmd: ~p~n", [Cmd]),
-
- OpensslPort = open_port({spawn, Cmd}, [stderr_to_stdout]),
+ OpensslPort = ssl_test_lib:portable_open_port(Exe, Args),
ssl_test_lib:wait_for_openssl_server(Port),
@@ -622,12 +626,12 @@ erlang_server_openssl_client_nowrap_seqnum(Config) when is_list(Config) ->
{options, [{renegotiate_at, N}, {reuse_sessions, false} | ServerOpts]}]),
Port = ssl_test_lib:inet_port(Server),
Version = tls_record:protocol_version(tls_record:highest_protocol_version([])),
- Cmd = "openssl s_client -port " ++ integer_to_list(Port) ++ ssl_test_lib:version_flag(Version) ++
- " -host localhost -msg",
-
- ct:log("openssl cmd: ~p~n", [Cmd]),
+ Exe = "openssl",
+ Args = ["s_client","-connect", "localhost: " ++ integer_to_list(Port),
+ ssl_test_lib:version_flag(Version),
+ "-msg"],
- OpenSslPort = open_port({spawn, Cmd}, [stderr_to_stdout]),
+ OpenSslPort = ssl_test_lib:portable_open_port(Exe, Args),
true = port_command(OpenSslPort, Data),
@@ -657,13 +661,13 @@ erlang_client_openssl_server_no_server_ca_cert(Config) when is_list(Config) ->
CertFile = proplists:get_value(certfile, ServerOpts),
KeyFile = proplists:get_value(keyfile, ServerOpts),
Version = tls_record:protocol_version(tls_record:highest_protocol_version([])),
- Cmd = "openssl s_server -accept " ++ integer_to_list(Port) ++ ssl_test_lib:version_flag(Version) ++
- " -cert " ++ CertFile ++ " -key " ++ KeyFile ++ " -msg",
+ Exe = "openssl",
+ Args = ["s_server", "-accept", integer_to_list(Port),
+ ssl_test_lib:version_flag(Version),
+ "-cert", CertFile, "-key", KeyFile, "-msg"],
- ct:log("openssl cmd: ~p~n", [Cmd]),
-
- OpensslPort = open_port({spawn, Cmd}, [stderr_to_stdout]),
-
+ OpensslPort = ssl_test_lib:portable_open_port(Exe, Args),
+
ssl_test_lib:wait_for_openssl_server(Port),
Client = ssl_test_lib:start_client([{node, ClientNode}, {port, Port},
@@ -699,13 +703,13 @@ erlang_client_openssl_server_client_cert(Config) when is_list(Config) ->
CaCertFile = proplists:get_value(cacertfile, ServerOpts),
KeyFile = proplists:get_value(keyfile, ServerOpts),
Version = tls_record:protocol_version(tls_record:highest_protocol_version([])),
- Cmd = "openssl s_server -accept " ++ integer_to_list(Port) ++ ssl_test_lib:version_flag(Version) ++
- " -cert " ++ CertFile ++ " -CAfile " ++ CaCertFile
- ++ " -key " ++ KeyFile ++ " -Verify 2",
+ Exe = "openssl",
+ Args = ["s_server", "-accept", integer_to_list(Port),
+ ssl_test_lib:version_flag(Version),
+ "-cert", CertFile, "-CAfile", CaCertFile,
+ "-key", KeyFile, "-Verify", "2"],
- ct:log("openssl cmd: ~p~n", [Cmd]),
-
- OpensslPort = open_port({spawn, Cmd}, [stderr_to_stdout]),
+ OpensslPort = ssl_test_lib:portable_open_port(Exe, Args),
ssl_test_lib:wait_for_openssl_server(Port),
@@ -750,15 +754,14 @@ erlang_server_openssl_client_client_cert(Config) when is_list(Config) ->
CertFile = proplists:get_value(certfile, ClientOpts),
KeyFile = proplists:get_value(keyfile, ClientOpts),
Version = tls_record:protocol_version(tls_record:highest_protocol_version([])),
- Cmd = "openssl s_client -cert " ++ CertFile ++ " -CAfile " ++ CaCertFile
- ++ " -key " ++ KeyFile ++ " -port " ++ integer_to_list(Port) ++ ssl_test_lib:version_flag(Version) ++
- " -host localhost",
-
- ct:log("openssl cmd: ~p~n", [Cmd]),
-
- OpenSslPort = open_port({spawn, Cmd}, [stderr_to_stdout]),
- true = port_command(OpenSslPort, Data),
-
+ Exe = "openssl",
+ Args = ["s_client", "-cert", CertFile,
+ "-CAfile", CaCertFile,
+ "-key", KeyFile,"-connect", "localhost:" ++ integer_to_list(Port),
+ ssl_test_lib:version_flag(Version)],
+ OpenSslPort = ssl_test_lib:portable_open_port(Exe, Args),
+
+ true = port_command(OpenSslPort, Data),
ssl_test_lib:check_result(Server, ok),
%% Clean close down! Server needs to be closed first !!
@@ -839,12 +842,10 @@ erlang_client_bad_openssl_server(Config) when is_list(Config) ->
CertFile = proplists:get_value(certfile, ServerOpts),
KeyFile = proplists:get_value(keyfile, ServerOpts),
Version = tls_record:protocol_version(tls_record:highest_protocol_version([])),
- Cmd = "openssl s_server -accept " ++ integer_to_list(Port) ++ ssl_test_lib:version_flag(Version) ++
- " -cert " ++ CertFile ++ " -key " ++ KeyFile ++ "",
-
- ct:log("openssl cmd: ~p~n", [Cmd]),
-
- OpensslPort = open_port({spawn, Cmd}, [stderr_to_stdout]),
+ Exe = "openssl",
+ Args = ["s_server", "-accept", integer_to_list(Port), ssl_test_lib:version_flag(Version),
+ "-cert", CertFile, "-key", KeyFile],
+ OpensslPort = ssl_test_lib:portable_open_port(Exe, Args),
ssl_test_lib:wait_for_openssl_server(Port),
@@ -895,12 +896,11 @@ expired_session(Config) when is_list(Config) ->
CertFile = proplists:get_value(certfile, ServerOpts),
KeyFile = proplists:get_value(keyfile, ServerOpts),
- Cmd = "openssl s_server -accept " ++ integer_to_list(Port) ++
- " -cert " ++ CertFile ++ " -key " ++ KeyFile ++ "",
+ Exe = "openssl",
+ Args = ["s_server", "-accept", integer_to_list(Port),
+ "-cert", CertFile,"-key", KeyFile],
- ct:log("openssl cmd: ~p~n", [Cmd]),
-
- OpensslPort = open_port({spawn, Cmd}, [stderr_to_stdout]),
+ OpensslPort = ssl_test_lib:portable_open_port(Exe, Args),
ssl_test_lib:wait_for_openssl_server(Port),
@@ -953,12 +953,11 @@ ssl2_erlang_server_openssl_client(Config) when is_list(Config) ->
{options, ServerOpts}]),
Port = ssl_test_lib:inet_port(Server),
- Cmd = "openssl s_client -port " ++ integer_to_list(Port) ++
- " -host localhost -ssl2 -msg",
-
- ct:log("openssl cmd: ~p~n", [Cmd]),
+ Exe = "openssl",
+ Args = ["s_client", "-connect", "localhost:" ++ integer_to_list(Port),
+ "-ssl2", "-msg"],
- OpenSslPort = open_port({spawn, Cmd}, [stderr_to_stdout]),
+ OpenSslPort = ssl_test_lib:portable_open_port(Exe, Args),
true = port_command(OpenSslPort, Data),
ct:log("Ports ~p~n", [[erlang:port_info(P) || P <- erlang:ports()]]),
@@ -1007,7 +1006,7 @@ erlang_client_alpn_openssl_server(Config) when is_list(Config) ->
Data = "From openssl to erlang",
start_erlang_client_and_openssl_server_with_opts(Config,
[{alpn_advertised_protocols, [<<"spdy/2">>]}],
- "",
+ [],
Data, fun(Server, OpensslPort) ->
true = port_command(OpensslPort, Data),
ssl_test_lib:check_result(Server, ok)
@@ -1020,7 +1019,7 @@ erlang_client_openssl_server_alpn(Config) when is_list(Config) ->
Data = "From openssl to erlang",
start_erlang_client_and_openssl_server_with_opts(Config,
[],
- "-alpn spdy/2",
+ ["-alpn", "spdy/2"],
Data, fun(Server, OpensslPort) ->
true = port_command(OpensslPort, Data),
ssl_test_lib:check_result(Server, ok)
@@ -1033,7 +1032,7 @@ erlang_server_alpn_openssl_client(Config) when is_list(Config) ->
Data = "From openssl to erlang",
start_erlang_server_and_openssl_client_with_opts(Config,
[{alpn_preferred_protocols, [<<"spdy/2">>]}],
- "",
+ [],
Data, fun(Server, OpensslPort) ->
true = port_command(OpensslPort, Data),
ssl_test_lib:check_result(Server, ok)
@@ -1046,7 +1045,7 @@ erlang_server_openssl_client_alpn(Config) when is_list(Config) ->
Data = "From openssl to erlang",
start_erlang_server_and_openssl_client_with_opts(Config,
[],
- "-alpn spdy/2",
+ ["-alpn", "spdy/2"],
Data, fun(Server, OpensslPort) ->
true = port_command(OpensslPort, Data),
ssl_test_lib:check_result(Server, ok)
@@ -1157,7 +1156,7 @@ erlang_server_openssl_client_npn_renegotiate(Config) when is_list(Config) ->
erlang_client_openssl_server_npn_only_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) ->
+ ["-nextprotoneg", "spdy/2"], Data, fun(Server, OpensslPort) ->
true = port_command(OpensslPort, Data),
ssl_test_lib:check_result(Server, ok)
end),
@@ -1169,7 +1168,7 @@ erlang_client_openssl_server_npn_only_client(Config) when is_list(Config) ->
Data = "From openssl to erlang",
start_erlang_client_and_openssl_server_with_opts(Config,
[{client_preferred_next_protocols,
- {client, [<<"spdy/2">>], <<"http/1.1">>}}], "",
+ {client, [<<"spdy/2">>], <<"http/1.1">>}}], [],
Data, fun(Server, OpensslPort) ->
true = port_command(OpensslPort, Data),
ssl_test_lib:check_result(Server, ok)
@@ -1179,7 +1178,7 @@ erlang_client_openssl_server_npn_only_client(Config) when is_list(Config) ->
%%--------------------------------------------------------------------------
erlang_server_openssl_client_npn_only_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">>]}], "",
+ start_erlang_server_and_openssl_client_with_opts(Config, [{next_protocols_advertised, [<<"spdy/2">>]}], [],
Data, fun(Server, OpensslPort) ->
true = port_command(OpensslPort, Data),
ssl_test_lib:check_result(Server, ok)
@@ -1188,7 +1187,7 @@ erlang_server_openssl_client_npn_only_server(Config) when is_list(Config) ->
erlang_server_openssl_client_npn_only_client(Config) when is_list(Config) ->
Data = "From openssl to erlang",
- start_erlang_server_and_openssl_client_with_opts(Config, [], "-nextprotoneg spdy/2",
+ start_erlang_server_and_openssl_client_with_opts(Config, [], ["-nextprotoneg", "spdy/2"],
Data, fun(Server, OpensslPort) ->
true = port_command(OpensslPort, Data),
ssl_test_lib:check_result(Server, ok)
@@ -1261,7 +1260,7 @@ client_check_result(Port, DataExpected, DataReceived) ->
client_check_result(Port, DataExpected, NewData)
end
after 3000 ->
- ct:fail({"Time out on opensssl Client", {expected, DataExpected},
+ ct:fail({"Time out on openSSL Client", {expected, DataExpected},
{got, DataReceived}})
end.
client_check_result(Port, DataExpected) ->
@@ -1280,14 +1279,14 @@ erlang_server_openssl_client_sni_test(Config, SNIHostname, ExpectedSNIHostname,
{from, self()}, {mfa, {?MODULE, send_and_hostname, []}},
{options, ServerOptions}]),
Port = ssl_test_lib:inet_port(Server),
- ClientCommand = case SNIHostname of
+ Exe = "openssl",
+ ClientArgs = case SNIHostname of
undefined ->
- "openssl s_client -connect " ++ Hostname ++ ":" ++ integer_to_list(Port);
+ ["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]),
+ ["s_client", "-connect", Hostname ++ ":" ++ integer_to_list(Port), "-servername", SNIHostname]
+ end,
+ ClientPort = ssl_test_lib:portable_open_port(Exe, ClientArgs),
%% Client check needs to be done befor server check,
%% or server check might consume client messages
@@ -1309,14 +1308,14 @@ erlang_server_openssl_client_sni_test_sni_fun(Config, SNIHostname, ExpectedSNIHo
{from, self()}, {mfa, {?MODULE, send_and_hostname, []}},
{options, ServerOptions}]),
Port = ssl_test_lib:inet_port(Server),
- ClientCommand = case SNIHostname of
+ Exe = "openssl",
+ ClientArgs = case SNIHostname of
undefined ->
- "openssl s_client -connect " ++ Hostname ++ ":" ++ integer_to_list(Port);
+ ["s_client", "-connect", Hostname ++ ":" ++ integer_to_list(Port)];
_ ->
- "openssl s_client -connect " ++ Hostname ++ ":" ++ integer_to_list(Port) ++ " -servername " ++ SNIHostname
+ ["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]),
+ ClientPort = ssl_test_lib:portable_open_port(Exe, ClientArgs),
%% Client check needs to be done befor server check,
%% or server check might consume client messages
@@ -1336,12 +1335,11 @@ cipher(CipherSuite, Version, Config, ClientOpts, ServerOpts) ->
CertFile = proplists:get_value(certfile, ServerOpts),
KeyFile = proplists:get_value(keyfile, ServerOpts),
- Cmd = "openssl s_server -accept " ++ integer_to_list(Port) ++ ssl_test_lib:version_flag(Version) ++
- " -cert " ++ CertFile ++ " -key " ++ KeyFile ++ "",
-
- ct:log("openssl cmd: ~p~n", [Cmd]),
+ Exe = "openssl",
+ Args = ["s_server", "-accept", integer_to_list(Port), ssl_test_lib:version_flag(Version),
+ "-cert", CertFile, "-key", KeyFile],
- OpenSslPort = open_port({spawn, Cmd}, [stderr_to_stdout]),
+ OpenSslPort = ssl_test_lib:portable_open_port(Exe, Args),
ssl_test_lib:wait_for_openssl_server(Port),
@@ -1399,13 +1397,19 @@ start_erlang_client_and_openssl_server_with_opts(Config, ErlangClientOpts, Opens
KeyFile = proplists:get_value(keyfile, ServerOpts),
Version = tls_record:protocol_version(tls_record:highest_protocol_version([])),
- Cmd = "openssl s_server " ++ OpensslServerOpts ++ " -accept " ++
- integer_to_list(Port) ++ ssl_test_lib:version_flag(Version) ++
- " -cert " ++ CertFile ++ " -key " ++ KeyFile,
-
- ct:log("openssl cmd: ~p~n", [Cmd]),
-
- OpensslPort = open_port({spawn, Cmd}, [stderr_to_stdout]),
+ Exe = "openssl",
+ Args = case OpensslServerOpts of
+ [] ->
+ ["s_server", "-accept",
+ integer_to_list(Port), ssl_test_lib:version_flag(Version),
+ "-cert", CertFile,"-key", KeyFile];
+ [Opt, Value] ->
+ ["s_server", Opt, Value, "-accept",
+ integer_to_list(Port), ssl_test_lib:version_flag(Version),
+ "-cert", CertFile,"-key", KeyFile]
+ end,
+
+ OpensslPort = ssl_test_lib:portable_open_port(Exe, Args),
ssl_test_lib:wait_for_openssl_server(Port),
@@ -1439,13 +1443,10 @@ start_erlang_client_and_openssl_server_for_alpn_negotiation(Config, Data, Callba
KeyFile = proplists:get_value(keyfile, ServerOpts),
Version = tls_record:protocol_version(tls_record:highest_protocol_version([])),
- Cmd = "openssl s_server -msg -alpn http/1.1,spdy/2 -accept " ++ integer_to_list(Port) ++ ssl_test_lib:version_flag(Version) ++
- " -cert " ++ CertFile ++ " -key " ++ KeyFile,
-
- ct:log("openssl cmd: ~p~n", [Cmd]),
-
- OpensslPort = open_port({spawn, Cmd}, [stderr_to_stdout]),
-
+ Exe = "openssl",
+ Args = ["s_server", "-msg", "-alpn", "http/1.1,spdy/2", "-accept", integer_to_list(Port), ssl_test_lib:version_flag(Version),
+ "-cert", CertFile, "-key" ++ KeyFile],
+ OpensslPort = ssl_test_lib:portable_open_port(Exe, Args),
ssl_test_lib:wait_for_openssl_server(Port),
Client = ssl_test_lib:start_client([{node, ClientNode}, {port, Port},
@@ -1477,12 +1478,13 @@ start_erlang_server_and_openssl_client_for_alpn_negotiation(Config, Data, Callba
{options, ServerOpts}]),
Port = ssl_test_lib:inet_port(Server),
Version = tls_record:protocol_version(tls_record:highest_protocol_version([])),
- Cmd = "openssl s_client -alpn http/1.0,spdy/2 -msg -port " ++ integer_to_list(Port) ++ ssl_test_lib:version_flag(Version) ++
- " -host localhost",
- ct:log("openssl cmd: ~p~n", [Cmd]),
+ Exe = "openssl",
+ Args = ["s_client", "-alpn", "http/1.0,spdy/2" "-msg" "-port",
+ integer_to_list(Port), ssl_test_lib:version_flag(Version),
+ "-host", "localhost"],
- OpenSslPort = open_port({spawn, Cmd}, [stderr_to_stdout]),
+ OpenSslPort = ssl_test_lib:portable_open_port(Exe, Args),
Callback(Server, OpenSslPort),
@@ -1507,12 +1509,12 @@ start_erlang_client_and_openssl_server_for_alpn_npn_negotiation(Config, Data, Ca
KeyFile = proplists:get_value(keyfile, ServerOpts),
Version = tls_record:protocol_version(tls_record:highest_protocol_version([])),
- Cmd = "openssl s_server -msg -alpn http/1.1,spdy/2 -nextprotoneg spdy/3 -accept " ++ integer_to_list(Port) ++ ssl_test_lib:version_flag(Version) ++
- " -cert " ++ CertFile ++ " -key " ++ KeyFile,
-
- ct:log("openssl cmd: ~p~n", [Cmd]),
+ Exe = "openssl",
+ Args = ["s_server", "-msg", "-alpn", "http/1.1,spdy/2", "-nextprotoneg",
+ "spdy/3", "-accept", integer_to_list(Port), ssl_test_lib:version_flag(Version),
+ "-cert" ++ CertFile ++ "-key" ++ KeyFile],
- OpensslPort = open_port({spawn, Cmd}, [stderr_to_stdout]),
+ OpensslPort = ssl_test_lib:portable_open_port(Exe, Args),
ssl_test_lib:wait_for_openssl_server(Port),
@@ -1546,17 +1548,15 @@ start_erlang_server_and_openssl_client_for_alpn_npn_negotiation(Config, Data, Ca
{options, ServerOpts}]),
Port = ssl_test_lib:inet_port(Server),
Version = tls_record:protocol_version(tls_record:highest_protocol_version([])),
- Cmd = "openssl s_client -alpn http/1.1,spdy/2 -nextprotoneg spdy/3 -msg -port " ++ integer_to_list(Port) ++ ssl_test_lib:version_flag(Version) ++
- " -host localhost",
-
- ct:log("openssl cmd: ~p~n", [Cmd]),
-
- OpenSslPort = open_port({spawn, Cmd}, [stderr_to_stdout]),
+ Exe = "openssl",
+ Args = ["s_client", "-alpn", "http/1.1,spdy/2", "-nextprotoneg", "spdy/3",
+ "-msg", "-port", integer_to_list(Port), ssl_test_lib:version_flag(Version),
+ "-host", "localhost"],
+ OpenSslPort = ssl_test_lib:portable_open_port(Exe, Args),
Callback(Server, OpenSslPort),
ssl_test_lib:close(Server),
-
ssl_test_lib:close_port(OpenSslPort),
process_flag(trap_exit, false).
@@ -1574,13 +1574,12 @@ start_erlang_client_and_openssl_server_for_npn_negotiation(Config, Data, Callbac
CertFile = proplists:get_value(certfile, ServerOpts),
KeyFile = proplists:get_value(keyfile, ServerOpts),
Version = tls_record:protocol_version(tls_record:highest_protocol_version([])),
-
- Cmd = "openssl s_server -msg -nextprotoneg http/1.1,spdy/2 -accept " ++ integer_to_list(Port) ++ ssl_test_lib:version_flag(Version) ++
- " -cert " ++ CertFile ++ " -key " ++ KeyFile,
-
- ct:log("openssl cmd: ~p~n", [Cmd]),
-
- OpensslPort = open_port({spawn, Cmd}, [stderr_to_stdout]),
+
+ Exe = "openssl",
+ Args = ["s_server", "-msg", "-nextprotoneg", "http/1.1,spdy/2", "-accept", integer_to_list(Port),
+ ssl_test_lib:version_flag(Version),
+ "-cert", CertFile, "-key", KeyFile],
+ OpensslPort = ssl_test_lib:portable_open_port(Exe, Args),
ssl_test_lib:wait_for_openssl_server(Port),
@@ -1613,12 +1612,12 @@ start_erlang_server_and_openssl_client_for_npn_negotiation(Config, Data, Callbac
{options, ServerOpts}]),
Port = ssl_test_lib:inet_port(Server),
Version = tls_record:protocol_version(tls_record:highest_protocol_version([])),
- Cmd = "openssl s_client -nextprotoneg http/1.0,spdy/2 -msg -port " ++ integer_to_list(Port) ++ ssl_test_lib:version_flag(Version) ++
- " -host localhost",
- ct:log("openssl cmd: ~p~n", [Cmd]),
+ Exe = "openssl",
+ Args = ["s_client", "-nextprotoneg", "http/1.0,spdy/2", "-msg", "-connect", "localhost:"
+ ++ integer_to_list(Port), ssl_test_lib:version_flag(Version)],
- OpenSslPort = open_port({spawn, Cmd}, [stderr_to_stdout]),
+ OpenSslPort = ssl_test_lib:portable_open_port(Exe, Args),
Callback(Server, OpenSslPort),
@@ -1642,12 +1641,12 @@ start_erlang_server_and_openssl_client_with_opts(Config, ErlangServerOpts, OpenS
{options, ServerOpts}]),
Port = ssl_test_lib:inet_port(Server),
Version = tls_record:protocol_version(tls_record:highest_protocol_version([])),
- Cmd = "openssl s_client " ++ OpenSSLClientOpts ++ " -msg -port " ++ integer_to_list(Port) ++ ssl_test_lib:version_flag(Version) ++
- " -host localhost",
-
- ct:log("openssl cmd: ~p~n", [Cmd]),
+
+ Exe = "openssl",
+ Args = ["s_client"] ++ OpenSSLClientOpts ++ ["-msg", "-connect", "localhost:" ++ integer_to_list(Port),
+ ssl_test_lib:version_flag(Version)],
- OpenSslPort = open_port({spawn, Cmd}, [stderr_to_stdout]),
+ OpenSslPort = ssl_test_lib:portable_open_port(Exe, Args),
Callback(Server, OpenSslPort),
@@ -1679,8 +1678,6 @@ erlang_ssl_receive(Socket, Data) ->
erlang_ssl_receive(Socket,Data);
Other ->
ct:fail({unexpected_message, Other})
- after 4000 ->
- ct:fail({did_not_get, Data})
end.
connection_info(Socket, Version) ->
@@ -1753,7 +1750,9 @@ check_sane_openssl_renegotaite(Config, _) ->
check_sane_openssl_renegotaite(Config).
check_sane_openssl_renegotaite(Config) ->
- case os:cmd("openssl version") of
+ case os:cmd("openssl version") of
+ "OpenSSL 1.0.0" ++ _ ->
+ {skip, "Known renegotiation bug in OpenSSL"};
"OpenSSL 0.9.8" ++ _ ->
{skip, "Known renegotiation bug in OpenSSL"};
"OpenSSL 0.9.7" ++ _ ->
@@ -1762,30 +1761,6 @@ check_sane_openssl_renegotaite(Config) ->
Config
end.
-check_sane_openssl_sslv2(Config) ->
- Port = open_port({spawn, "openssl s_client -ssl2 "}, [stderr_to_stdout]),
- case supports_sslv2(Port) of
- true ->
- Config;
- false ->
- {skip, "sslv2 not supported by openssl"}
- end.
-
-supports_sslv2(Port) ->
- receive
- {Port, {data, "unknown option -ssl2" ++ _}} ->
- false;
- {Port, {data, Data}} ->
- case lists:member("error", string:tokens(Data, ":")) of
- true ->
- false;
- false ->
- supports_sslv2(Port)
- end
- after 500 ->
- true
- end.
-
workaround_openssl_s_clinent() ->
%% http://bugs.debian.org/cgi-bin/bugreport.cgi?bug=683159
%% https://bugs.archlinux.org/task/33919
@@ -1793,13 +1768,13 @@ workaround_openssl_s_clinent() ->
%% explicitly specified
case os:cmd("openssl version") of
"OpenSSL 1.0.1c" ++ _ ->
- " -no_tls1_2 ";
+ ["-no_tls1_2"];
"OpenSSL 1.0.1d" ++ _ ->
- " -no_tls1_2 ";
+ ["-no_tls1_2"];
"OpenSSL 1.0.1e" ++ _ ->
- " -no_tls1_2 ";
+ ["-no_tls1_2"];
"OpenSSL 1.0.1f" ++ _ ->
- " -no_tls1_2 ";
+ ["-no_tls1_2"];
_ ->
- ""
+ []
end.
diff --git a/lib/ssl/test/ssl_upgrade_SUITE.erl b/lib/ssl/test/ssl_upgrade_SUITE.erl
index 17b0240fe8..f5f4b25b23 100644
--- a/lib/ssl/test/ssl_upgrade_SUITE.erl
+++ b/lib/ssl/test/ssl_upgrade_SUITE.erl
@@ -28,7 +28,8 @@
config,
server,
client,
- soft
+ soft,
+ result_proxy
}).
all() ->
@@ -39,20 +40,19 @@ all() ->
init_per_suite(Config0) ->
catch crypto:stop(),
- try {crypto:start(), erlang:system_info({wordsize, internal}) == erlang:system_info({wordsize, external})} of
- {ok, true} ->
- case ct_release_test:init(Config0) of
- {skip, Reason} ->
- {skip, Reason};
- Config ->
- {ok, _} = make_certs:all(?config(data_dir, Config),
- ?config(priv_dir, Config)),
- ssl_test_lib:cert_options(Config)
- end;
- {ok, false} ->
- {skip, "Test server will not handle halfwordemulator correctly. Skip as halfwordemulator is deprecated"}
+ try crypto:start() of
+ ok ->
+ case ct_release_test:init(Config0) of
+ {skip, Reason} ->
+ {skip, Reason};
+ Config ->
+ Result =
+ {ok, _} = make_certs:all(?config(data_dir, Config),
+ ?config(priv_dir, Config)),
+ ssl_test_lib:cert_options(Config)
+ end
catch _:_ ->
- {skip, "Crypto did not start"}
+ {skip, "Crypto did not start"}
end.
end_per_suite(Config) ->
@@ -77,45 +77,58 @@ upgrade_init(CTData, #state{config = Config} = State) ->
{ok, {_, _, Up, _Down}} = ct_release_test:get_appup(CTData, ssl),
ct:pal("Up: ~p", [Up]),
Soft = is_soft(Up), %% It is symmetrical, if upgrade is soft so is downgrade
+ Pid = spawn(?MODULE, result_proxy_init, [[]]),
case Soft of
true ->
- {Server, Client} = soft_start_connection(Config),
+ {Server, Client} = soft_start_connection(Config, Pid),
State#state{server = Server, client = Client,
- soft = Soft};
+ soft = Soft,
+ result_proxy = Pid};
false ->
- State#state{soft = Soft}
+ State#state{soft = Soft, result_proxy = Pid}
end.
-upgrade_upgraded(_, #state{soft = false, config = Config} = State) ->
- {Server, Client} = restart_start_connection(Config),
- ssl_test_lib:check_result(Server, ok, Client, ok),
+upgrade_upgraded(_, #state{soft = false, config = Config, result_proxy = Pid} = State) ->
+ ct:pal("Restart upgrade ~n", []),
+ {Server, Client} = restart_start_connection(Config, Pid),
+ Result = check_result(Pid, Server, Client),
ssl_test_lib:close(Server),
ssl_test_lib:close(Client),
+ ok = Result,
State;
upgrade_upgraded(_, #state{server = Server0, client = Client0,
- config = Config, soft = true} = State) ->
+ config = Config, soft = true,
+ result_proxy = Pid} = State) ->
+ ct:pal("Soft upgrade: ~n", []),
Server0 ! changed_version,
Client0 ! changed_version,
- ssl_test_lib:check_result(Server0, ok, Client0, ok),
+ Result = check_result(Pid, Server0, Client0),
ssl_test_lib:close(Server0),
ssl_test_lib:close(Client0),
- {Server, Client} = soft_start_connection(Config),
+ ok = Result,
+ {Server, Client} = soft_start_connection(Config, Pid),
State#state{server = Server, client = Client}.
-upgrade_downgraded(_, #state{soft = false, config = Config} = State) ->
- {Server, Client} = restart_start_connection(Config),
- ssl_test_lib:check_result(Server, ok, Client, ok),
+upgrade_downgraded(_, #state{soft = false, config = Config, result_proxy = Pid} = State) ->
+ ct:pal("Restart downgrade: ~n", []),
+ {Server, Client} = restart_start_connection(Config, Pid),
+ Result = check_result(Pid, Server, Client),
ssl_test_lib:close(Server),
ssl_test_lib:close(Client),
+ Pid ! stop,
+ ok = Result,
State;
-upgrade_downgraded(_, #state{server = Server, client = Client, soft = true} = State) ->
+upgrade_downgraded(_, #state{server = Server, client = Client, soft = true, result_proxy = Pid} = State) ->
+ ct:pal("Soft downgrade: ~n", []),
Server ! changed_version,
Client ! changed_version,
- ssl_test_lib:check_result(Server, ok, Client, ok),
+ Result = check_result(Pid, Server, Client),
+ Pid ! stop,
ssl_test_lib:close(Server),
ssl_test_lib:close(Client),
+ ok = Result,
State.
use_connection(Socket) ->
@@ -125,36 +138,35 @@ use_connection(Socket) ->
ssl_test_lib:send_recv_result_active(Socket)
end.
-soft_start_connection(Config) ->
+soft_start_connection(Config, ResulProxy) ->
ClientOpts = ?config(client_verification_opts, Config),
ServerOpts = ?config(server_verification_opts, Config),
{ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config),
- Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0},
- {from, self()},
- {mfa, {?MODULE, use_connection, []}},
- {options, ServerOpts}]),
+ Server = start_server([{node, ServerNode}, {port, 0},
+ {from, ResulProxy},
+ {mfa, {?MODULE, use_connection, []}},
+ {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, use_connection, []}},
- {options, ClientOpts}]),
+ Port = inet_port(ResulProxy, Server),
+ Client = start_client([{node, ClientNode}, {port, Port},
+ {host, Hostname},
+ {from, ResulProxy},
+ {mfa, {?MODULE, use_connection, []}},
+ {options, ClientOpts}]),
{Server, Client}.
-restart_start_connection(Config) ->
+restart_start_connection(Config, ResulProxy) ->
ClientOpts = ?config(client_verification_opts, Config),
ServerOpts = ?config(server_verification_opts, Config),
{ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config),
- Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0},
- {from, self()},
+ Server = start_server([{node, ServerNode}, {port, 0},
+ {from, ResulProxy},
{mfa, {ssl_test_lib, send_recv_result_active, []}},
{options, ServerOpts}]),
-
- Port = ssl_test_lib:inet_port(Server),
- Client = ssl_test_lib:start_client([{node, ClientNode}, {port, Port},
+ Port = inet_port(ResulProxy, Server),
+ Client = start_client([{node, ClientNode}, {port, Port},
{host, Hostname},
- {from, self()},
+ {from, ResulProxy},
{mfa, {ssl_test_lib, send_recv_result_active, []}},
{options, ClientOpts}]),
{Server, Client}.
@@ -163,3 +175,103 @@ is_soft([{restart_application, ssl}]) ->
false;
is_soft(_) ->
true.
+
+result_proxy_init(Args) ->
+ result_proxy_loop(Args).
+
+result_proxy_loop(Args) ->
+ receive
+ {Pid, {check_result, Server, Client}} ->
+ Result = do_check_result(Server, ok, Client, ok),
+ Pid ! {self(), Result},
+ result_proxy_loop(Args);
+ {Pid, port, Server} ->
+ Port = recv_port(Server),
+ Pid ! Port,
+ result_proxy_loop(Args);
+ {Pid, listen} ->
+ recv_listen(),
+ Pid ! ok,
+ result_proxy_loop(Args);
+ {Pid, connected} ->
+ Connected = recv_connected(),
+ Pid ! Connected,
+ result_proxy_loop(Args)
+ end.
+
+check_result(Pid, Server, Client) ->
+ Pid ! {self(), {check_result, Server, Client}},
+ receive
+ {Pid, Result} ->
+ Result
+ end.
+
+do_check_result(Server, ServerMsg, Client, ClientMsg) ->
+ receive
+ {Server, ServerMsg} ->
+ do_check_result(Client, ClientMsg);
+
+ {Client, ClientMsg} ->
+ do_check_result(Server, ServerMsg);
+ Unexpected ->
+ {{expected, {Client, ClientMsg}},
+ {expected, {Server, ServerMsg}}, {got, Unexpected}}
+ end.
+
+do_check_result(Pid, Msg) ->
+ receive
+ {Pid, Msg} ->
+ ok;
+ Unexpected ->
+ {{expected, {Pid, Msg}},
+ {got, Unexpected}}
+ end.
+
+inet_port(Pid, Server) ->
+ Pid ! {self(), port, Server},
+ receive
+ {port, Port} ->
+ Port
+ end.
+
+recv_port(Server) ->
+ receive
+ {Server, {port, Port}} ->
+ {port, Port}
+ end.
+
+recv_connected() ->
+ receive
+ {connected, _Socket} ->
+ ok;
+ {connect_failed, Reason} ->
+ {connect_failed, Reason}
+ end.
+
+
+start_server(Args) ->
+ Pid = proplists:get_value(from, Args),
+ Result = spawn_link(ssl_test_lib, run_server, [Args]),
+ Pid ! {self(), listen},
+ receive
+ ok ->
+ ok
+ end,
+ Result.
+
+start_client(Args) ->
+ Pid = proplists:get_value(from, Args),
+ Result = spawn_link(ssl_test_lib, run_client_init, [lists:delete(return_socket, Args)]),
+ Pid ! {self(), connected},
+ receive
+ ok ->
+ Result;
+ Reason ->
+ exit(Reason)
+ end.
+
+recv_listen()->
+ receive
+ {listen, up} ->
+ ok
+ end.
diff --git a/lib/ssl/vsn.mk b/lib/ssl/vsn.mk
index 171147adf2..aa1af21990 100644
--- a/lib/ssl/vsn.mk
+++ b/lib/ssl/vsn.mk
@@ -1 +1 @@
-SSL_VSN = 7.0
+SSL_VSN = 7.2