aboutsummaryrefslogtreecommitdiffstats
path: root/lib/ssl
diff options
context:
space:
mode:
Diffstat (limited to 'lib/ssl')
-rw-r--r--lib/ssl/doc/src/notes.xml229
-rw-r--r--lib/ssl/doc/src/ssl.xml130
-rw-r--r--lib/ssl/doc/src/ssl_app.xml48
-rw-r--r--lib/ssl/doc/src/ssl_crl_cache_api.xml6
-rw-r--r--lib/ssl/doc/src/ssl_distribution.xml23
-rw-r--r--lib/ssl/doc/src/ssl_session_cache_api.xml10
-rw-r--r--lib/ssl/examples/src/client_server.erl7
-rw-r--r--lib/ssl/src/Makefile1
-rw-r--r--lib/ssl/src/dtls_connection.erl2
-rw-r--r--lib/ssl/src/inet6_tls_dist.erl46
-rw-r--r--lib/ssl/src/inet_tls_dist.erl109
-rw-r--r--lib/ssl/src/ssl.app.src3
-rw-r--r--lib/ssl/src/ssl.appup.src20
-rw-r--r--lib/ssl/src/ssl.erl135
-rw-r--r--lib/ssl/src/ssl_certificate.erl22
-rw-r--r--lib/ssl/src/ssl_cipher.erl54
-rw-r--r--lib/ssl/src/ssl_connection.erl42
-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_handshake.erl9
-rw-r--r--lib/ssl/src/ssl_internal.hrl17
-rw-r--r--lib/ssl/src/ssl_manager.erl206
-rw-r--r--lib/ssl/src/ssl_record.erl16
-rw-r--r--lib/ssl/src/ssl_session.erl11
-rw-r--r--lib/ssl/src/ssl_session_cache.erl8
-rw-r--r--lib/ssl/src/ssl_session_cache_api.erl1
-rw-r--r--lib/ssl/src/ssl_tls_dist_proxy.erl166
-rw-r--r--lib/ssl/src/ssl_v3.erl3
-rw-r--r--lib/ssl/src/tls_connection.erl7
-rw-r--r--lib/ssl/src/tls_record.erl62
-rw-r--r--lib/ssl/src/tls_v1.erl4
-rw-r--r--lib/ssl/test/Makefile5
-rw-r--r--lib/ssl/test/erl_make_certs.erl4
-rw-r--r--lib/ssl/test/make_certs.erl22
-rw-r--r--lib/ssl/test/ssl_ECC_SUITE.erl20
-rw-r--r--lib/ssl/test/ssl_basic_SUITE.erl166
-rw-r--r--lib/ssl/test/ssl_certificate_verify_SUITE.erl184
-rw-r--r--lib/ssl/test/ssl_crl_SUITE.erl82
-rw-r--r--lib/ssl/test/ssl_dist_SUITE.erl206
-rw-r--r--lib/ssl/test/ssl_session_cache_SUITE.erl152
-rw-r--r--lib/ssl/test/ssl_sni_SUITE.erl8
-rw-r--r--lib/ssl/test/ssl_test_lib.erl92
-rw-r--r--lib/ssl/test/ssl_to_openssl_SUITE.erl375
-rw-r--r--lib/ssl/test/ssl_upgrade_SUITE.erl25
-rw-r--r--lib/ssl/vsn.mk2
45 files changed, 2035 insertions, 730 deletions
diff --git a/lib/ssl/doc/src/notes.xml b/lib/ssl/doc/src/notes.xml
index 6faa3d5f9a..e5070bc247 100644
--- a/lib/ssl/doc/src/notes.xml
+++ b/lib/ssl/doc/src/notes.xml
@@ -26,12 +26,219 @@
<file>notes.xml</file>
</header>
<p>This document describes the changes made to the SSL application.</p>
- <section><title>SSL 7.1</title>
+
+
+<section><title>SSL 7.3</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p>
+ Make sure there is only one poller validator at a time
+ for validating the session cache.</p>
+ <p>
+ Own Id: OTP-13185</p>
+ </item>
+ <item>
+ <p>
+ A timing related issue could cause ssl to hang,
+ especially happened with newer versions of OpenSSL in
+ combination with ECC ciphers.</p>
+ <p>
+ Own Id: OTP-13253</p>
+ </item>
+ <item>
+ <p>
+ Work around a race condition in the TLS distribution
+ start.</p>
+ <p>
+ Own Id: OTP-13268</p>
+ </item>
+ <item>
+ <p>
+ Big handshake messages are now correctly fragmented in
+ the TLS record layer.</p>
+ <p>
+ Own Id: OTP-13306</p>
+ </item>
+ <item>
+ <p>
+ Improve portability of ECC tests in Crypto and SSL for
+ "exotic" OpenSSL versions.</p>
+ <p>
+ Own Id: OTP-13311</p>
+ </item>
+ <item>
+ <p>
+ Certificate extensions marked as critical are ignored
+ when using verify_none</p>
+ <p>
+ Own Id: OTP-13377</p>
+ </item>
+ <item>
+ <p>
+ If a certificate doesn't contain a CRL Distribution
+ Points extension, and the relevant CRL is not in the
+ cache, and the <c>crl_check</c> option is not set to
+ <c>best_effort</c> , the revocation check should fail.</p>
+ <p>
+ Own Id: OTP-13378</p>
+ </item>
+ <item>
+ <p>
+ Enable TLS distribution over IPv6</p>
+ <p>
+ Own Id: OTP-13391</p>
+ </item>
+ </list>
+ </section>
+
+
+ <section><title>Improvements and New Features</title>
+ <list>
+ <item>
+ <p>
+ Improve error reporting for TLS distribution</p>
+ <p>
+ Own Id: OTP-13219</p>
+ </item>
+ <item>
+ <p>
+ Include options from connect, listen and accept in
+ <c>connection_information/1,2</c></p>
+ <p>
+ Own Id: OTP-13232</p>
+ </item>
+ <item>
+ <p>
+ Allow adding extra options for outgoing TLS distribution
+ connections, as supported for plain TCP connections.</p>
+ <p>
+ Own Id: OTP-13285</p>
+ </item>
+ <item>
+ <p>
+ Use loopback as server option in TLS-distribution module</p>
+ <p>
+ Own Id: OTP-13300</p>
+ </item>
+ <item>
+ <p>
+ Verify certificate signature against original certificate
+ binary.</p>
+ <p>
+ This avoids bugs due to encoding errors when re-encoding
+ a decode certificate. As there exists several decode step
+ and using of different ASN.1 specification this is a risk
+ worth avoiding.</p>
+ <p>
+ Own Id: OTP-13334</p>
+ </item>
+ <item>
+ <p>
+ Use <c>application:ensure_all_started/2</c> instead of
+ hard-coding dependencies</p>
+ <p>
+ Own Id: OTP-13363</p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
+<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>
@@ -107,12 +314,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>
@@ -163,6 +364,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 22ac98c24e..a76d46ee9b 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,7 +60,7 @@
<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>
@@ -160,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>
@@ -168,7 +144,9 @@
<p>According to old API.</p></item>
<tag><c>ciphersuite() =</c></tag>
- <item><p><c>{key_exchange(), cipher(), hash()}</c></p></item>
+
+ <item><p><c>{key_exchange(), cipher(), MAC::hash()} |
+ {key_exchange(), cipher(), MAC::hash(), PRF::hash()}</c></p></item>
<tag><c>key_exchange()=</c></tag>
<item><p><c>rsa | dhe_dss | dhe_rsa | dh_anon | psk | dhe_psk
@@ -180,7 +158,7 @@
| aes_128_cbc | aes_256_cbc | aes_128_gcm | aes_256_gcm</c></p></item>
<tag><c>hash() =</c></tag>
- <item><p><c>md5 | sha</c></p></item>
+ <item><p><c>md5 | sha | sha224 | sha256 | sha348 | sha512</c></p></item>
<tag><c>prf_random() =</c></tag>
<item><p><c>client_random | server_random</c></p></item>
@@ -245,7 +223,7 @@
<url href="http://www.ietf.org/rfc/rfc5746.txt">RFC 5746</url>.
By default <c>secure_renegotiate</c> is set to <c>false</c>,
that is, secure renegotiation is used if possible,
- but it fallback to unsecure renegotiation if the peer
+ but it falls back to insecure renegotiation if the peer
does not support
<url href="http://www.ietf.org/rfc/rfc5746.txt">RFC 5746</url>.</p>
</item>
@@ -293,7 +271,11 @@ atom()}} |
terminate regarding verification failures and the connection is
established.</p></item>
<item><p>If called with an extension unknown to the user application,
- return value <c>{unknown, UserState}</c> is to be used.</p></item>
+ return value <c>{unknown, UserState}</c> is to be used.</p>
+
+ <p>Note that if the fun returns <c>unknown</c> for an extension marked
+ as critical, validation will fail.</p>
+ </item>
</list>
<p>Default option <c>verify_fun</c> in <c>verify_peer mode</c>:</p>
@@ -315,6 +297,8 @@ atom()}} |
<code>
{fun(_,{bad_cert, _}, UserState) ->
{valid, UserState};
+ (_,{extension, #'Extension'{critical = true}}, UserState) ->
+ {valid, UserState};
(_,{extension, _}, UserState) ->
{unknown, UserState};
(_, valid, UserState) ->
@@ -331,7 +315,7 @@ atom()}} |
<tag><c>unknown_ca</c></tag>
<item><p>No trusted CA was found in the trusted store. The trusted CA is
normally a so called ROOT CA, which is a self-signed certificate. Trust can
- be claimed for an intermediat CA (trusted anchor does not have to be
+ be claimed for an intermediate CA (trusted anchor does not have to be
self-signed according to X-509) by using option <c>partial_chain</c>.</p>
</item>
@@ -376,7 +360,7 @@ marker="public_key:public_key#pkix_path_validation-3">public_key:pkix_path_valid
<tag><c>{http, timeout()}</c></tag>
<item><p>
Enables fetching of CRLs specified as http URIs in<seealso
- marker="public_key:public_key_records"> X509 cerificate extensions.</seealso>
+ marker="public_key:public_key_records"> X509 certificate extensions.</seealso>
Requires the OTP inets application.</p>
</item>
</taglist>
@@ -480,8 +464,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>
@@ -538,7 +522,6 @@ fun(srp, Username :: string(), UserState :: term()) ->
be supported by the server for the prevention to work.
</p></warning>
</item>
-
</taglist>
</section>
@@ -636,14 +619,14 @@ fun(srp, Username :: string(), UserState :: term()) ->
<tag><c>{sni_hosts, [{hostname(), ssloptions()}]}</c></tag>
<item><p>If the server receives a SNI (Server Name Indication) from the client
- matching a host listed in the <c>sni_hosts</c> option, the speicific options for
+ matching a host listed in the <c>sni_hosts</c> option, the specific options for
that host will override previously specified options.
The option <c>sni_fun</c>, and <c>sni_hosts</c> are mutually exclusive.</p></item>
<tag><c>{sni_fun, SNIfun::fun()}</c></tag>
<item><p>If the server receives a SNI (Server Name Indication) from the client,
- the given function will be called to retrive <c>ssloptions()</c> for indicated server.
+ the given function will be called to retrieve <c>ssloptions()</c> for the indicated server.
These options will be merged into predefined <c>ssloptions()</c>.
The function should be defined as:
@@ -657,7 +640,7 @@ fun(srp, Username :: string(), UserState :: term()) ->
of resources of such an operation is higher for the server than the
client. This can act as a vector for denial of service attacks. The SSL
application already takes measures to counter-act such attempts,
- but client-initiated renegotiation can be stricly disabled by setting
+ but client-initiated renegotiation can be strictly disabled by setting
this option to <c>false</c>. The default value is <c>true</c>.
Note that disabling renegotiation can result in long-lived connections
becoming unusable due to limits on the number of messages the underlying
@@ -773,26 +756,13 @@ fun(srp, Username :: string(), UserState :: term()) ->
<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.
- </fsummary>
- <type>
- <v>CipherSuite = ciphersuite()</v>
- <v>ProtocolVersion = protocol()</v>
- </type>
- <desc><p>Returns the Negotiated Protocol version and cipher suite.</p>
+ <desc><p>Closes or downgrades an SSL connection. In the latter case the transport
+ connection will be handed over to the <c>NewController</c> process after receiving
+ the TLS close alert from the peer. The returned transport socket will have
+ the following options set: <c>[{active, false}, {packet, 0}, {mode, binary}]</c></p>
</desc>
</func>
-
+
<func>
<name>controlling_process(SslSocket, NewOwner) ->
ok | {error, Reason}</name>
@@ -811,40 +781,36 @@ fun(srp, Username :: string(), UserState :: term()) ->
<func>
<name>connection_information(SslSocket) ->
- {ok, Info} | {error, Reason} </name>
+ {ok, Result} | {error, Reason} </name>
<fsummary>Returns all the connection information.
</fsummary>
<type>
- <v>Info = [InfoTuple]</v>
- <v>InfoTuple = {protocol, Protocol} | {cipher_suite, CipherSuite} | {sni_hostname, SNIHostname}</v>
- <v>CipherSuite = ciphersuite()</v>
- <v>ProtocolVersion = protocol()</v>
- <v>SNIHostname = string()</v>
+ <v>Item = protocol | cipher_suite | sni_hostname | atom()</v>
+ <d>Meaningful atoms, not specified above, are the ssl option names.</d>
+ <v>Result = [{Item::atom(), Value::term()}]</v>
<v>Reason = term()</v>
</type>
- <desc><p>Return all the connection information containing negotiated protocol version, cipher suite, and the hostname of SNI extension.
- Info will be a proplists containing all the connection information on success, otherwise <c>{error, Reason}</c> will be returned.</p>
+ <desc><p>Returns all relevant information about the connection, ssl options that
+ are undefined will be filtered out.</p>
</desc>
</func>
<func>
<name>connection_information(SslSocket, Items) ->
- {ok, Info} | {error, Reason} </name>
+ {ok, Result} | {error, Reason} </name>
<fsummary>Returns the requested connection information.
</fsummary>
<type>
- <v>Items = [Item]</v>
- <v>Item = protocol | cipher_suite | sni_hostname</v>
- <v>Info = [InfoTuple]</v>
- <v>InfoTuple = {protocol, Protocol} | {cipher_suite, CipherSuite} | {sni_hostname, SNIHostname}</v>
- <v>CipherSuite = ciphersuite()</v>
- <v>ProtocolVersion = protocol()</v>
- <v>SNIHostname = string()</v>
+ <v>Items = [Item]</v>
+ <v>Item = protocol | cipher_suite | sni_hostname | atom()</v>
+ <d>Meaningful atoms, not specified above, are the ssl option names.</d>
+ <v>Result = [{Item::atom(), Value::term()}]</v>
<v>Reason = term()</v>
</type>
- <desc><p>Returns the connection information you requested. The connection information you can request contains protocol, cipher_suite, and sni_hostname.
- <c>{ok, Info}</c> will be returned if it executes sucessfully. The Info is a proplists containing the information you requested.
- Otherwise, <c>{error, Reason}</c> will be returned.</p>
+ <desc><p>Returns the requested information items about the connection,
+ if they are defined.</p>
+ <note><p>If only undefined options are requested the
+ resulting list can be empty.</p></note>
</desc>
</func>
@@ -1171,7 +1137,7 @@ fun(srp, Username :: string(), UserState :: term()) ->
<seealso marker="#listen-2"> listen/2</seealso>, and <seealso
marker="#ssl_accept-2">ssl_accept/[1,2,3]</seealso>.
For the negotiated TLS/SSL version, see <seealso
- marker="#connection_info-1">ssl:connection_info/1
+ marker="#connection_information-1">ssl:connection_information/1
</seealso>.</item>
<tag><c>available</c></tag>
diff --git a/lib/ssl/doc/src/ssl_app.xml b/lib/ssl/doc/src/ssl_app.xml
index 51ce0cedf1..d05ece3971 100644
--- a/lib/ssl/doc/src/ssl_app.xml
+++ b/lib/ssl/doc/src/ssl_app.xml
@@ -33,7 +33,35 @@
<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>For security reasons DES cipher suites are 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
+ (<url href="http://www.ietf.org/rfc/rfc6066.txt">RFC 6066</url>) is supported.</item>
+ </list>
+ </description>
+
<section>
<title>DEPENDENCIES</title>
<p>The SSL application uses the <c>public_key</c> and
@@ -58,7 +86,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 +94,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>
@@ -103,7 +138,10 @@
<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_distribution.xml b/lib/ssl/doc/src/ssl_distribution.xml
index a347ce5ae6..dc04d446b0 100644
--- a/lib/ssl/doc/src/ssl_distribution.xml
+++ b/lib/ssl/doc/src/ssl_distribution.xml
@@ -271,4 +271,27 @@ Eshell V5.0 (abort with ^G)
<p>The <c>init:get_arguments()</c> call verifies that the correct
arguments are supplied to the emulator.</p>
</section>
+
+ <section>
+ <title>Using SSL distribution over IPv6</title>
+ <p>It is possible to use SSL distribution over IPv6 instead of
+ IPv4. To do this, pass the option <c>-proto_dist inet6_tls</c>
+ instead of <c>-proto_dist inet_tls</c> when starting Erlang,
+ either on the command line or in the <c>ERL_FLAGS</c> environment
+ variable.</p>
+
+ <p>An example command line with this option would look like this:</p>
+ <code type="none">
+$ erl -boot /home/me/ssl/start_ssl -proto_dist inet6_tls
+ -ssl_dist_opt server_certfile "/home/me/ssl/erlserver.pem"
+ -ssl_dist_opt server_secure_renegotiate true client_secure_renegotiate true
+ -sname ssl_test
+Erlang (BEAM) emulator version 5.0 [source]
+
+Eshell V5.0 (abort with ^G)
+(ssl_test@myhost)1> </code>
+
+ <p>A node started in this way will only be able to communicate with
+ other nodes using SSL distribution over IPv6.</p>
+ </section>
</chapter>
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/examples/src/client_server.erl b/lib/ssl/examples/src/client_server.erl
index 799027123f..019b5130d2 100644
--- a/lib/ssl/examples/src/client_server.erl
+++ b/lib/ssl/examples/src/client_server.erl
@@ -26,9 +26,7 @@
start() ->
%% Start ssl application
- application:start(crypto),
- application:start(public_key),
- application:start(ssl),
+ {ok, StartedApps} = application:ensure_all_started(ssl),
%% Let the current process be the server that listens and accepts
%% Listen
@@ -52,7 +50,8 @@ start() ->
ssl:close(ASock),
io:fwrite("Listen: closing and terminating.~n"),
ssl:close(LSock),
- application:stop(ssl).
+
+ lists:foreach(fun application:stop/1, lists:reverse(StartedApps)).
%% Client connect
diff --git a/lib/ssl/src/Makefile b/lib/ssl/src/Makefile
index 790328dc45..7a7a373487 100644
--- a/lib/ssl/src/Makefile
+++ b/lib/ssl/src/Makefile
@@ -51,6 +51,7 @@ MODULES= \
ssl_dist_sup\
ssl_sup \
inet_tls_dist \
+ inet6_tls_dist \
ssl_certificate\
ssl_pkix_db\
ssl_cipher \
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/inet6_tls_dist.erl b/lib/ssl/src/inet6_tls_dist.erl
new file mode 100644
index 0000000000..ffd7296f93
--- /dev/null
+++ b/lib/ssl/src/inet6_tls_dist.erl
@@ -0,0 +1,46 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2015. All Rights Reserved.
+%%
+%% Licensed under the Apache License, Version 2.0 (the "License");
+%% you may not use this file except in compliance with the License.
+%% You may obtain a copy of the License at
+%%
+%% http://www.apache.org/licenses/LICENSE-2.0
+%%
+%% Unless required by applicable law or agreed to in writing, software
+%% distributed under the License is distributed on an "AS IS" BASIS,
+%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+%% See the License for the specific language governing permissions and
+%% limitations under the License.
+%%
+%% %CopyrightEnd%
+%%
+
+%%
+-module(inet6_tls_dist).
+
+-export([childspecs/0, listen/1, accept/1, accept_connection/5,
+ setup/5, close/1, select/1]).
+
+childspecs() ->
+ inet_tls_dist:childspecs().
+
+select(Node) ->
+ inet_tls_dist:gen_select(inet6_tcp, Node).
+
+listen(Name) ->
+ inet_tls_dist:gen_listen(inet6_tcp, Name).
+
+accept(Listen) ->
+ inet_tls_dist:gen_accept(inet6_tcp, Listen).
+
+accept_connection(AcceptPid, Socket, MyNode, Allowed, SetupTime) ->
+ inet_tls_dist:gen_accept_connection(inet6_tcp, AcceptPid, Socket, MyNode, Allowed, SetupTime).
+
+setup(Node, Type, MyNode, LongOrShortNames,SetupTime) ->
+ inet_tls_dist:gen_setup(inet6_tcp, Node, Type, MyNode, LongOrShortNames,SetupTime).
+
+close(Socket) ->
+ inet_tls_dist:close(Socket).
diff --git a/lib/ssl/src/inet_tls_dist.erl b/lib/ssl/src/inet_tls_dist.erl
index b6e62a18c9..ec26142a75 100644
--- a/lib/ssl/src/inet_tls_dist.erl
+++ b/lib/ssl/src/inet_tls_dist.erl
@@ -24,18 +24,28 @@
-export([childspecs/0, listen/1, accept/1, accept_connection/5,
setup/5, close/1, select/1, is_node_name/1]).
+%% Generalized dist API
+-export([gen_listen/2, gen_accept/2, gen_accept_connection/6,
+ gen_setup/6, gen_select/2]).
+
-include_lib("kernel/include/net_address.hrl").
-include_lib("kernel/include/dist.hrl").
-include_lib("kernel/include/dist_util.hrl").
childspecs() ->
{ok, [{ssl_dist_sup,{ssl_dist_sup, start_link, []},
- permanent, 2000, worker, [ssl_dist_sup]}]}.
+ permanent, infinity, supervisor, [ssl_dist_sup]}]}.
select(Node) ->
+ gen_select(inet_tcp, Node).
+
+gen_select(Driver, Node) ->
case split_node(atom_to_list(Node), $@, []) of
- [_,_Host] ->
- true;
+ [_, Host] ->
+ case inet:getaddr(Host, Driver:family()) of
+ {ok, _} -> true;
+ _ -> false
+ end;
_ ->
false
end.
@@ -46,23 +56,35 @@ is_node_name(_) ->
false.
listen(Name) ->
- ssl_tls_dist_proxy:listen(Name).
+ gen_listen(inet_tcp, Name).
+
+gen_listen(Driver, Name) ->
+ ssl_tls_dist_proxy:listen(Driver, Name).
accept(Listen) ->
- ssl_tls_dist_proxy:accept(Listen).
+ gen_accept(inet_tcp, Listen).
+
+gen_accept(Driver, Listen) ->
+ ssl_tls_dist_proxy:accept(Driver, Listen).
accept_connection(AcceptPid, Socket, MyNode, Allowed, SetupTime) ->
+ gen_accept_connection(inet_tcp, AcceptPid, Socket, MyNode, Allowed, SetupTime).
+
+gen_accept_connection(Driver, AcceptPid, Socket, MyNode, Allowed, SetupTime) ->
Kernel = self(),
- spawn_link(fun() -> do_accept(Kernel, AcceptPid, Socket,
+ spawn_link(fun() -> do_accept(Driver, Kernel, AcceptPid, Socket,
MyNode, Allowed, SetupTime) end).
setup(Node, Type, MyNode, LongOrShortNames,SetupTime) ->
+ gen_setup(inet_tcp, Node, Type, MyNode, LongOrShortNames,SetupTime).
+
+gen_setup(Driver, Node, Type, MyNode, LongOrShortNames,SetupTime) ->
Kernel = self(),
- spawn_opt(fun() -> do_setup(Kernel, Node, Type, MyNode, LongOrShortNames, SetupTime) end, [link, {priority, max}]).
+ spawn_opt(fun() -> do_setup(Driver, Kernel, Node, Type, MyNode, LongOrShortNames, SetupTime) end, [link, {priority, max}]).
-do_setup(Kernel, Node, Type, MyNode, LongOrShortNames, SetupTime) ->
- [Name, Address] = splitnode(Node, LongOrShortNames),
- case inet:getaddr(Address, inet) of
+do_setup(Driver, Kernel, Node, Type, MyNode, LongOrShortNames, SetupTime) ->
+ [Name, Address] = splitnode(Driver, Node, LongOrShortNames),
+ case inet:getaddr(Address, Driver:family()) of
{ok, Ip} ->
Timer = dist_util:start_timer(SetupTime),
case erl_epmd:port_please(Name, Ip) of
@@ -70,41 +92,41 @@ do_setup(Kernel, Node, Type, MyNode, LongOrShortNames, SetupTime) ->
?trace("port_please(~p) -> version ~p~n",
[Node,Version]),
dist_util:reset_timer(Timer),
- case ssl_tls_dist_proxy:connect(Ip, TcpPort) of
+ case ssl_tls_dist_proxy:connect(Driver, Ip, TcpPort) of
{ok, Socket} ->
HSData = connect_hs_data(Kernel, Node, MyNode, Socket,
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) ->
gen_tcp:close(Socket),
ok.
-do_accept(Kernel, AcceptPid, Socket, MyNode, Allowed, SetupTime) ->
+do_accept(Driver, Kernel, AcceptPid, Socket, MyNode, Allowed, SetupTime) ->
process_flag(priority, max),
receive
{AcceptPid, controller} ->
Timer = dist_util:start_timer(SetupTime),
- case check_ip(Socket) of
+ case check_ip(Driver, Socket) of
true ->
HSData = accept_hs_data(Kernel, MyNode, Socket, Timer, Allowed),
dist_util:handshake_other_started(HSData);
@@ -118,12 +140,12 @@ do_accept(Kernel, AcceptPid, Socket, MyNode, Allowed, SetupTime) ->
%% Do only accept new connection attempts from nodes at our
%% own LAN, if the check_ip environment parameter is true.
%% ------------------------------------------------------------
-check_ip(Socket) ->
+check_ip(Driver, Socket) ->
case application:get_env(check_ip) of
{ok, true} ->
case get_ifs(Socket) of
{ok, IFs, IP} ->
- check_ip(IFs, IP);
+ check_ip(Driver, IFs, IP);
_ ->
?shutdown(no_node)
end;
@@ -142,37 +164,21 @@ get_ifs(Socket) ->
Error
end.
-check_ip([{OwnIP, _, Netmask}|IFs], PeerIP) ->
- case {mask(Netmask, PeerIP), mask(Netmask, OwnIP)} of
+check_ip(Driver, [{OwnIP, _, Netmask}|IFs], PeerIP) ->
+ case {Driver:mask(Netmask, PeerIP), Driver:mask(Netmask, OwnIP)} of
{M, M} -> true;
_ -> check_ip(IFs, PeerIP)
end;
-check_ip([], PeerIP) ->
+check_ip(_Driver, [], PeerIP) ->
{false, PeerIP}.
-mask({M1,M2,M3,M4}, {IP1,IP2,IP3,IP4}) ->
- {M1 band IP1,
- M2 band IP2,
- M3 band IP3,
- M4 band IP4};
-
-mask({M1,M2,M3,M4, M5, M6, M7, M8}, {IP1,IP2,IP3,IP4, IP5, IP6, IP7, IP8}) ->
- {M1 band IP1,
- M2 band IP2,
- M3 band IP3,
- M4 band IP4,
- M5 band IP5,
- M6 band IP6,
- M7 band IP7,
- M8 band IP8}.
-
%% If Node is illegal terminate the connection setup!!
-splitnode(Node, LongOrShortNames) ->
+splitnode(Driver, Node, LongOrShortNames) ->
case split_node(atom_to_list(Node), $@, []) of
[Name|Tail] when Tail =/= [] ->
Host = lists:append(Tail),
- check_node(Name, Node, Host, LongOrShortNames);
+ check_node(Driver, Name, Node, Host, LongOrShortNames);
[_] ->
error_logger:error_msg("** Nodename ~p illegal, no '@' character **~n",
[Node]),
@@ -182,15 +188,20 @@ splitnode(Node, LongOrShortNames) ->
?shutdown(Node)
end.
-check_node(Name, Node, Host, LongOrShortNames) ->
+check_node(Driver, Name, Node, Host, LongOrShortNames) ->
case split_node(Host, $., []) of
[_] when LongOrShortNames == longnames ->
- error_logger:error_msg("** System running to use "
- "fully qualified "
- "hostnames **~n"
- "** Hostname ~s is illegal **~n",
- [Host]),
- ?shutdown(Node);
+ case Driver:parse_address(Host) of
+ {ok, _} ->
+ [Name, Host];
+ _ ->
+ error_logger:error_msg("** System running to use "
+ "fully qualified "
+ "hostnames **~n"
+ "** Hostname ~s is illegal **~n",
+ [Host]),
+ ?shutdown(Node)
+ end;
[_, _ | _] when LongOrShortNames == shortnames ->
error_logger:error_msg("** System NOT running to use fully qualified "
"hostnames **~n"
diff --git a/lib/ssl/src/ssl.app.src b/lib/ssl/src/ssl.app.src
index be8ef6f85f..1a2bf90ccf 100644
--- a/lib/ssl/src/ssl.app.src
+++ b/lib/ssl/src/ssl.app.src
@@ -31,6 +31,7 @@
ssl_listen_tracker_sup,
%% Erlang Distribution over SSL/TLS
inet_tls_dist,
+ inet6_tls_dist,
ssl_tls_dist_proxy,
ssl_dist_sup,
%% SSL/TLS session handling
@@ -54,6 +55,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 8d5bd6f8d8..057906bcb3 100644
--- a/lib/ssl/src/ssl.appup.src
+++ b/lib/ssl/src/ssl.appup.src
@@ -1,24 +1,20 @@
%% -*- erlang -*-
{"%VSN%",
[
- {<<"7\\.0">>, [{load_module, ssl, soft_purge, soft_purge, []},
- {load_module, ssl_connection, soft_purge, soft_purge, []},
- {load_module, tls_connection, soft_purge, soft_purge, []},
- {load_module, ssl_session, soft_purge, soft_purge, []},
- {load_module, ssl_session_cache, soft_purge, soft_purge, []}
- ]},
+ {<<"7\\.2">>, [{load_module, tls_connection, soft_purge, soft_purge, []},
+ {load_module, ssl_tls_dist_proxy, soft_purge, soft_purge, []}
+ ]},
+ {<<"7\\..*">>, [{restart_application, ssl}]},
{<<"6\\..*">>, [{restart_application, ssl}]},
{<<"5\\..*">>, [{restart_application, ssl}]},
{<<"4\\..*">>, [{restart_application, ssl}]},
{<<"3\\..*">>, [{restart_application, ssl}]}
],
[
- {<<"7\\.0">>, [{load_module, ssl, soft_purge, soft_purge, []},
- {load_module, ssl_connection, soft_purge, soft_purge, []},
- {load_module, tls_connection, soft_purge, soft_purge, []},
- {load_module, ssl_session, soft_purge, soft_purge, []},
- {load_module, ssl_session_cache, soft_purge, soft_purge, []}
- ]},
+ {<<"7\\.2">>, [{load_module, tls_connection, soft_purge, soft_purge, []},
+ {load_module, ssl_tls_dist_proxy, soft_purge, soft_purge, []}
+ ]},
+ {<<"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 03495cfd90..780bef5877 100644
--- a/lib/ssl/src/ssl.erl
+++ b/lib/ssl/src/ssl.erl
@@ -37,7 +37,7 @@
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,
+-export([cipher_suites/0, cipher_suites/1,
connection_info/1, versions/0, session_info/1, format_error/1,
renegotiate/1, prf/5, negotiated_protocol/1, negotiated_next_protocol/1,
connection_information/1, connection_information/2]).
@@ -60,22 +60,19 @@
-spec start() -> ok | {error, reason()}.
-spec start(permanent | transient | temporary) -> ok | {error, reason()}.
%%
-%% Description: Utility function that starts the ssl,
-%% crypto and public_key applications. Default type
-%% is temporary. see application(3)
+%% Description: Utility function that starts the ssl and applications
+%% that it depends on.
+%% see application(3)
%%--------------------------------------------------------------------
start() ->
- application:start(crypto),
- application:start(asn1),
- application:start(public_key),
- application:start(ssl).
-
+ start(temporary).
start(Type) ->
- application:start(crypto, Type),
- application:start(asn1),
- application:start(public_key, Type),
- application:start(ssl, Type).
-
+ case application:ensure_all_started(ssl, Type) of
+ {ok, _} ->
+ ok;
+ Other ->
+ Other
+ end.
%%--------------------------------------------------------------------
-spec stop() -> ok.
%%
@@ -105,7 +102,7 @@ connect(Socket, SslOptions0, Timeout) when is_port(Socket),
{gen_tcp, tcp, tcp_closed, tcp_error}),
EmulatedOptions = ssl_socket:emulated_options(),
{ok, SocketValues} = ssl_socket:getopts(Transport, Socket, EmulatedOptions),
- try handle_options(SslOptions0 ++ SocketValues) of
+ try handle_options(SslOptions0 ++ SocketValues, client) of
{ok, #config{transport_info = CbInfo, ssl = SslOptions, emulated = EmOpts,
connection_cb = ConnectionCb}} ->
@@ -127,7 +124,7 @@ connect(Host, Port, Options) ->
connect(Host, Port, Options, infinity).
connect(Host, Port, Options, Timeout) when (is_integer(Timeout) andalso Timeout > 0) or (Timeout == infinity) ->
- try handle_options(Options) of
+ try handle_options(Options, client) of
{ok, Config} ->
do_connect(Host,Port,Config,Timeout)
catch
@@ -145,7 +142,7 @@ listen(_Port, []) ->
{error, nooptions};
listen(Port, Options0) ->
try
- {ok, Config} = handle_options(Options0),
+ {ok, Config} = handle_options(Options0, server),
ConnectionCb = connection_cb(Options0),
#config{transport_info = {Transport, _, _, _}, inet_user = Options, connection_cb = ConnectionCb,
ssl = SslOpts, emulated = EmOpts} = Config,
@@ -233,7 +230,7 @@ ssl_accept(Socket, SslOptions, Timeout) when is_port(Socket),
EmulatedOptions = ssl_socket:emulated_options(),
{ok, SocketValues} = ssl_socket:getopts(Transport, Socket, EmulatedOptions),
ConnetionCb = connection_cb(SslOptions),
- try handle_options(SslOptions ++ SocketValues) of
+ try handle_options(SslOptions ++ SocketValues, server) of
{ok, #config{transport_info = CbInfo, ssl = SslOpts, emulated = EmOpts}} ->
ok = ssl_socket:setopts(Transport, Socket, ssl_socket:internal_inet_values()),
{ok, Port} = ssl_socket:port(Transport, Socket),
@@ -315,24 +312,32 @@ controlling_process(#sslsocket{pid = {Listen,
%%
%% Description: Return SSL information for the connection
%%--------------------------------------------------------------------
-connection_information(#sslsocket{pid = Pid}) when is_pid(Pid) -> ssl_connection:connection_information(Pid);
-connection_information(#sslsocket{pid = {Listen, _}}) when is_port(Listen) -> {error, enotconn}.
-
+connection_information(#sslsocket{pid = Pid}) when is_pid(Pid) ->
+ case ssl_connection:connection_information(Pid) of
+ {ok, Info} ->
+ {ok, [Item || Item = {_Key, Value} <- Info, Value =/= undefined]};
+ Error ->
+ Error
+ end;
+connection_information(#sslsocket{pid = {Listen, _}}) when is_port(Listen) ->
+ {error, enotconn}.
%%--------------------------------------------------------------------
--spec connection_information(#sslsocket{}, [atom]) -> {ok, list()} | {error, reason()}.
+-spec connection_information(#sslsocket{}, [atom()]) -> {ok, list()} | {error, reason()}.
%%
%% Description: Return SSL information for the connection
%%--------------------------------------------------------------------
connection_information(#sslsocket{} = SSLSocket, Items) ->
case connection_information(SSLSocket) of
- {ok, I} ->
- {ok, lists:filter(fun({K, _}) -> lists:foldl(fun(K1, Acc) when K1 =:= K -> Acc + 1; (_, Acc) -> Acc end, 0, Items) > 0 end, I)};
- E ->
- E
+ {ok, Info} ->
+ {ok, [Item || Item = {Key, Value} <- Info, lists:member(Key, Items),
+ Value =/= undefined]};
+ Error ->
+ Error
end.
%%--------------------------------------------------------------------
+%% Deprecated
-spec connection_info(#sslsocket{}) -> {ok, {tls_record:tls_atom_version(), ssl_cipher:erl_cipher_suite()}} |
{error, reason()}.
%%
@@ -372,15 +377,6 @@ peercert(#sslsocket{pid = {Listen, _}}) when is_port(Listen) ->
{error, enotconn}.
%%--------------------------------------------------------------------
--spec suite_definition(ssl_cipher:cipher_suite()) -> ssl_cipher:erl_cipher_suite().
-%%
-%% Description: Return erlang cipher suite definition.
-%%--------------------------------------------------------------------
-suite_definition(S) ->
- {KeyExchange, Cipher, Hash, _} = ssl_cipher:suite_definition(S),
- {KeyExchange, Cipher, Hash}.
-
-%%--------------------------------------------------------------------
-spec negotiated_protocol(#sslsocket{}) -> {ok, binary()} | {error, reason()}.
%%
%% Description: Returns the protocol that has been negotiated. If no
@@ -410,7 +406,7 @@ negotiated_next_protocol(Socket) ->
%%--------------------------------------------------------------------
cipher_suites(erlang) ->
Version = tls_record:highest_protocol_version([]),
- ssl_cipher:filter_suites([suite_definition(S)
+ ssl_cipher:filter_suites([ssl_cipher:erl_suite_definition(S)
|| S <- ssl_cipher:suites(Version)]);
cipher_suites(openssl) ->
Version = tls_record:highest_protocol_version([]),
@@ -418,7 +414,7 @@ cipher_suites(openssl) ->
|| S <- ssl_cipher:filter_suites(ssl_cipher:suites(Version))];
cipher_suites(all) ->
Version = tls_record:highest_protocol_version([]),
- ssl_cipher:filter_suites([suite_definition(S)
+ ssl_cipher:filter_suites([ssl_cipher:erl_suite_definition(S)
|| S <-ssl_cipher:all_suites(Version)]).
cipher_suites() ->
cipher_suites(erlang).
@@ -630,7 +626,8 @@ handle_options(Opts0, #ssl_options{protocol = Protocol, cacerts = CaCerts0,
cacertfile = CaCertFile0} = InheritedSslOpts) ->
RecordCB = record_cb(Protocol),
CaCerts = handle_option(cacerts, Opts0, CaCerts0),
- {Verify, FailIfNoPeerCert, CaCertDefault, VerifyFun, PartialChainHanlder} = handle_verify_options(Opts0, CaCerts),
+ {Verify, FailIfNoPeerCert, CaCertDefault, VerifyFun, PartialChainHanlder,
+ VerifyClientOnce} = handle_verify_options(Opts0, CaCerts),
CaCertFile = case proplists:get_value(cacertfile, Opts0, CaCertFile0) of
undefined ->
CaCertDefault;
@@ -643,11 +640,12 @@ handle_options(Opts0, #ssl_options{protocol = Protocol, cacerts = CaCerts0,
verify = Verify,
verify_fun = VerifyFun,
partial_chain = PartialChainHanlder,
- fail_if_no_peer_cert = FailIfNoPeerCert},
+ fail_if_no_peer_cert = FailIfNoPeerCert,
+ verify_client_once = VerifyClientOnce},
SslOpts1 = lists:foldl(fun(Key, PropList) ->
proplists:delete(Key, PropList)
end, Opts0, [cacerts, cacertfile, verify, verify_fun, partial_chain,
- fail_if_no_peer_cert]),
+ fail_if_no_peer_cert, verify_client_once]),
case handle_option(versions, SslOpts1, []) of
[] ->
new_ssl_options(SslOpts1, NewVerifyOpts, RecordCB);
@@ -655,10 +653,10 @@ handle_options(Opts0, #ssl_options{protocol = Protocol, cacerts = CaCerts0,
Versions = [RecordCB:protocol_version(Vsn) || Vsn <- Value],
new_ssl_options(proplists:delete(versions, SslOpts1),
NewVerifyOpts#ssl_options{versions = Versions}, record_cb(Protocol))
- end.
+ end;
%% Handle all options in listen and connect
-handle_options(Opts0) ->
+handle_options(Opts0, Role) ->
Opts = proplists:expand([{binary, [{mode, binary}]},
{list, [{mode, list}]}], Opts0),
assert_proplist(Opts),
@@ -667,7 +665,7 @@ handle_options(Opts0) ->
ReuseSessionFun = fun(_, _, _, _) -> true end,
CaCerts = handle_option(cacerts, Opts, undefined),
- {Verify, FailIfNoPeerCert, CaCertDefault, VerifyFun, PartialChainHanlder} =
+ {Verify, FailIfNoPeerCert, CaCertDefault, VerifyFun, PartialChainHanlder, VerifyClientOnce} =
handle_verify_options(Opts, CaCerts),
CertFile = handle_option(certfile, Opts, <<>>),
@@ -686,7 +684,7 @@ handle_options(Opts0) ->
verify_fun = VerifyFun,
partial_chain = PartialChainHanlder,
fail_if_no_peer_cert = FailIfNoPeerCert,
- verify_client_once = handle_option(verify_client_once, Opts, false),
+ verify_client_once = VerifyClientOnce,
depth = handle_option(depth, Opts, 1),
cert = handle_option(cert, Opts, undefined),
certfile = CertFile,
@@ -706,7 +704,9 @@ handle_options(Opts0) ->
reuse_session = handle_option(reuse_session, Opts, ReuseSessionFun),
reuse_sessions = handle_option(reuse_sessions, Opts, true),
secure_renegotiate = handle_option(secure_renegotiate, Opts, false),
- client_renegotiation = handle_option(client_renegotiation, Opts, true),
+ client_renegotiation = handle_option(client_renegotiation, Opts,
+ default_option_role(server, true, Role),
+ server, Role),
renegotiate_at = handle_option(renegotiate_at, Opts, ?DEFAULT_RENEGOTIATE_AT),
hibernate_after = handle_option(hibernate_after, Opts, undefined),
erl_dist = handle_option(erl_dist, Opts, false),
@@ -723,10 +723,16 @@ handle_options(Opts0) ->
server_name_indication = handle_option(server_name_indication, Opts, undefined),
sni_hosts = handle_option(sni_hosts, Opts, []),
sni_fun = handle_option(sni_fun, Opts, undefined),
- honor_cipher_order = handle_option(honor_cipher_order, Opts, false),
+ honor_cipher_order = handle_option(honor_cipher_order, Opts,
+ default_option_role(server, false, Role),
+ server, Role),
protocol = proplists:get_value(protocol, Opts, tls),
padding_check = proplists:get_value(padding_check, Opts, true),
- fallback = proplists:get_value(fallback, Opts, false),
+ fallback = handle_option(fallback, Opts,
+ proplists:get_value(fallback, Opts,
+ default_option_role(client,
+ false, Role)),
+ client, Role),
crl_check = handle_option(crl_check, Opts, false),
crl_cache = handle_option(crl_cache, Opts, {ssl_crl_cache, {internal, []}})
},
@@ -756,6 +762,13 @@ handle_options(Opts0) ->
inet_user = SockOpts, transport_info = CbInfo, connection_cb = ConnetionCb
}}.
+
+
+handle_option(OptionName, Opts, Default, Role, Role) ->
+ handle_option(OptionName, Opts, Default);
+handle_option(_, _, undefined = Value, _, _) ->
+ Value.
+
handle_option(sni_fun, Opts, Default) ->
OptFun = validate_option(sni_fun,
proplists:get_value(sni_fun, Opts, Default)),
@@ -772,7 +785,6 @@ handle_option(OptionName, Opts, Default) ->
validate_option(OptionName,
proplists:get_value(OptionName, Opts, Default)).
-
validate_option(versions, Versions) ->
validate_versions(Versions, Versions);
validate_option(verify, Value)
@@ -1190,6 +1202,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]) ->
@@ -1214,7 +1228,8 @@ emulated_socket_options(InetValues, #socket_options{
new_ssl_options([], #ssl_options{} = Opts, _) ->
Opts;
new_ssl_options([{verify_client_once, Value} | Rest], #ssl_options{} = Opts, RecordCB) ->
- new_ssl_options(Rest, Opts#ssl_options{verify_client_once = validate_option(verify_client_once, Value)}, RecordCB);
+ new_ssl_options(Rest, Opts#ssl_options{verify_client_once =
+ validate_option(verify_client_once, Value)}, RecordCB);
new_ssl_options([{depth, Value} | Rest], #ssl_options{} = Opts, RecordCB) ->
new_ssl_options(Rest, Opts#ssl_options{depth = validate_option(depth, Value)}, RecordCB);
new_ssl_options([{cert, Value} | Rest], #ssl_options{} = Opts, RecordCB) ->
@@ -1278,6 +1293,12 @@ handle_verify_options(Opts, CaCerts) ->
DefaultVerifyNoneFun =
{fun(_,{bad_cert, _}, UserState) ->
{valid, UserState};
+ (_,{extension, #'Extension'{critical = true}}, UserState) ->
+ %% This extension is marked as critical, so
+ %% certificate verification should fail if we don't
+ %% understand the extension. However, this is
+ %% `verify_none', so let's accept it anyway.
+ {valid, UserState};
(_,{extension, _}, UserState) ->
{unknown, UserState};
(_, valid, UserState) ->
@@ -1293,29 +1314,35 @@ handle_verify_options(Opts, CaCerts) ->
PartialChainHanlder = handle_option(partial_chain, Opts,
fun(_) -> unknown_ca end),
+ VerifyClientOnce = handle_option(verify_client_once, Opts, false),
+
%% Handle 0, 1, 2 for backwards compatibility
case proplists:get_value(verify, Opts, verify_none) of
0 ->
{verify_none, false,
ca_cert_default(verify_none, VerifyNoneFun, CaCerts),
- VerifyNoneFun, PartialChainHanlder};
+ VerifyNoneFun, PartialChainHanlder, VerifyClientOnce};
1 ->
{verify_peer, false,
ca_cert_default(verify_peer, UserVerifyFun, CaCerts),
- UserVerifyFun, PartialChainHanlder};
+ UserVerifyFun, PartialChainHanlder, VerifyClientOnce};
2 ->
{verify_peer, true,
ca_cert_default(verify_peer, UserVerifyFun, CaCerts),
- UserVerifyFun, PartialChainHanlder};
+ UserVerifyFun, PartialChainHanlder, VerifyClientOnce};
verify_none ->
{verify_none, false,
ca_cert_default(verify_none, VerifyNoneFun, CaCerts),
- VerifyNoneFun, PartialChainHanlder};
+ VerifyNoneFun, PartialChainHanlder, VerifyClientOnce};
verify_peer ->
{verify_peer, UserFailIfNoPeerCert,
ca_cert_default(verify_peer, UserVerifyFun, CaCerts),
- UserVerifyFun, PartialChainHanlder};
+ UserVerifyFun, PartialChainHanlder, VerifyClientOnce};
Value ->
throw({error, {options, {verify, Value}}})
end.
+default_option_role(Role, Value, Role) ->
+ Value;
+default_option_role(_,_,_) ->
+ undefined.
diff --git a/lib/ssl/src/ssl_certificate.erl b/lib/ssl/src/ssl_certificate.erl
index 4658e76ab1..e9dc5764a3 100644
--- a/lib/ssl/src/ssl_certificate.erl
+++ b/lib/ssl/src/ssl_certificate.erl
@@ -56,15 +56,15 @@
%% errors. Returns {RootCert, Path, VerifyErrors}
%%--------------------------------------------------------------------
trusted_cert_and_path(CertChain, CertDbHandle, CertDbRef, PartialChainHandler) ->
- Path = [Cert | _] = lists:reverse(CertChain),
- OtpCert = public_key:pkix_decode_cert(Cert, otp),
+ Path = [BinCert | _] = lists:reverse(CertChain),
+ OtpCert = public_key:pkix_decode_cert(BinCert, otp),
SignedAndIssuerID =
case public_key:pkix_is_self_signed(OtpCert) of
true ->
{ok, IssuerId} = public_key:pkix_issuer_id(OtpCert, self),
{self, IssuerId};
false ->
- other_issuer(OtpCert, CertDbHandle)
+ other_issuer(OtpCert, BinCert, CertDbHandle)
end,
case SignedAndIssuerID of
@@ -187,7 +187,7 @@ public_key_type(?'id-ecPublicKey') ->
%%--------------------------------------------------------------------
%%% Internal functions
%%--------------------------------------------------------------------
-certificate_chain(OtpCert, _Cert, CertDbHandle, CertsDbRef, Chain) ->
+certificate_chain(OtpCert, BinCert, CertDbHandle, CertsDbRef, Chain) ->
IssuerAndSelfSigned =
case public_key:pkix_is_self_signed(OtpCert) of
true ->
@@ -200,7 +200,7 @@ certificate_chain(OtpCert, _Cert, CertDbHandle, CertsDbRef, Chain) ->
{_, true = SelfSigned} ->
certificate_chain(CertDbHandle, CertsDbRef, Chain, ignore, ignore, SelfSigned);
{{error, issuer_not_found}, SelfSigned} ->
- case find_issuer(OtpCert, CertDbHandle) of
+ case find_issuer(OtpCert, BinCert, CertDbHandle) of
{ok, {SerialNr, Issuer}} ->
certificate_chain(CertDbHandle, CertsDbRef, Chain,
SerialNr, Issuer, SelfSigned);
@@ -232,12 +232,12 @@ certificate_chain(CertDbHandle, CertsDbRef, Chain, SerialNr, Issuer, _SelfSigned
{ok, undefined, lists:reverse(Chain)}
end.
-find_issuer(OtpCert, CertDbHandle) ->
+find_issuer(OtpCert, BinCert, CertDbHandle) ->
IsIssuerFun =
fun({_Key, {_Der, #'OTPCertificate'{} = ErlCertCandidate}}, Acc) ->
case public_key:pkix_is_issuer(OtpCert, ErlCertCandidate) of
true ->
- case verify_cert_signer(OtpCert, ErlCertCandidate#'OTPCertificate'.tbsCertificate) of
+ case verify_cert_signer(BinCert, ErlCertCandidate#'OTPCertificate'.tbsCertificate) of
true ->
throw(public_key:pkix_issuer_id(ErlCertCandidate, self));
false ->
@@ -265,9 +265,9 @@ is_valid_extkey_usage(KeyUse, server) ->
%% Server wants to verify client
is_valid_key_usage(KeyUse, ?'id-kp-clientAuth').
-verify_cert_signer(OtpCert, SignerTBSCert) ->
+verify_cert_signer(BinCert, SignerTBSCert) ->
PublicKey = public_key(SignerTBSCert#'OTPTBSCertificate'.subjectPublicKeyInfo),
- public_key:pkix_verify(public_key:pkix_encode('OTPCertificate', OtpCert, otp), PublicKey).
+ public_key:pkix_verify(BinCert, PublicKey).
public_key(#'OTPSubjectPublicKeyInfo'{algorithm = #'PublicKeyAlgorithm'{algorithm = ?'id-ecPublicKey',
parameters = Params},
@@ -281,12 +281,12 @@ public_key(#'OTPSubjectPublicKeyInfo'{algorithm = #'PublicKeyAlgorithm'{algorith
subjectPublicKey = Key}) ->
{Key, Params}.
-other_issuer(OtpCert, CertDbHandle) ->
+other_issuer(OtpCert, BinCert, CertDbHandle) ->
case public_key:pkix_issuer_id(OtpCert, other) of
{ok, IssuerId} ->
{other, IssuerId};
{error, issuer_not_found} ->
- case find_issuer(OtpCert, CertDbHandle) of
+ case find_issuer(OtpCert, BinCert, CertDbHandle) of
{ok, IssuerId} ->
{other, IssuerId};
Other ->
diff --git a/lib/ssl/src/ssl_cipher.erl b/lib/ssl/src/ssl_cipher.erl
index 8c2a16ba96..d737f74aa3 100644
--- a/lib/ssl/src/ssl_cipher.erl
+++ b/lib/ssl/src/ssl_cipher.erl
@@ -34,10 +34,11 @@
-include_lib("public_key/include/public_key.hrl").
-export([security_parameters/2, security_parameters/3, suite_definition/1,
+ erl_suite_definition/1,
cipher_init/3, decipher/6, cipher/5, decipher_aead/6, cipher_aead/6,
suite/1, suites/1, all_suites/1,
ec_keyed_suites/0, anonymous_suites/1, psk_suites/1, srp_suites/0,
- rc4_suites/1, openssl_suite/1, openssl_suite_name/1, filter/2, filter_suites/1,
+ rc4_suites/1, des_suites/1, openssl_suite/1, openssl_suite_name/1, filter/2, filter_suites/1,
hash_algorithm/1, sign_algorithm/1, is_acceptable_hash/2, is_fallback/1]).
-export_type([cipher_suite/0,
@@ -48,8 +49,11 @@
| aes_128_cbc | aes_256_cbc | aes_128_gcm | aes_256_gcm | chacha20_poly1305.
-type hash() :: null | sha | md5 | sha224 | sha256 | sha384 | sha512.
-type key_algo() :: null | rsa | dhe_rsa | dhe_dss | ecdhe_ecdsa| ecdh_ecdsa | ecdh_rsa| srp_rsa| srp_dss | psk | dhe_psk | rsa_psk | dh_anon | ecdh_anon | srp_anon.
--type erl_cipher_suite() :: {key_algo(), cipher(), hash()}.
--type int_cipher_suite() :: {key_algo(), cipher(), hash(), hash() | default_prf}.
+-type erl_cipher_suite() :: {key_algo(), cipher(), hash()} % Pre TLS 1.2
+ %% TLS 1.2, internally PRE TLS 1.2 will use default_prf
+ | {key_algo(), cipher(), hash(), hash() | default_prf}.
+
+
-type cipher_suite() :: binary().
-type cipher_enum() :: integer().
-type openssl_cipher_suite() :: string().
@@ -311,7 +315,8 @@ all_suites(Version) ->
++ anonymous_suites(Version)
++ psk_suites(Version)
++ srp_suites()
- ++ rc4_suites(Version).
+ ++ rc4_suites(Version)
+ ++ des_suites(Version).
%%--------------------------------------------------------------------
-spec anonymous_suites(ssl_record:ssl_version() | integer()) -> [cipher_suite()].
%%
@@ -415,9 +420,19 @@ rc4_suites({3, N}) when N =< 3 ->
?TLS_RSA_WITH_RC4_128_MD5,
?TLS_ECDH_ECDSA_WITH_RC4_128_SHA,
?TLS_ECDH_RSA_WITH_RC4_128_SHA].
+%%--------------------------------------------------------------------
+-spec des_suites(Version::ssl_record:ssl_version()) -> [cipher_suite()].
+%%
+%% Description: Returns a list of the cipher suites
+%% with DES cipher, only supported if explicitly set by user.
+%% Are not considered secure any more.
+%%--------------------------------------------------------------------
+des_suites(_)->
+ [?TLS_DHE_RSA_WITH_DES_CBC_SHA,
+ ?TLS_RSA_WITH_DES_CBC_SHA].
%%--------------------------------------------------------------------
--spec suite_definition(cipher_suite()) -> int_cipher_suite().
+-spec suite_definition(cipher_suite()) -> erl_cipher_suite().
%%
%% Description: Return erlang cipher suite definition.
%% Note: Currently not supported suites are commented away.
@@ -722,6 +737,20 @@ suite_definition(?TLS_DHE_RSA_WITH_CHACHA20_POLY1305_SHA256) ->
{dhe_rsa, chacha20_poly1305, null, sha256}.
%%--------------------------------------------------------------------
+-spec erl_suite_definition(cipher_suite()) -> erl_cipher_suite().
+%%
+%% Description: Return erlang cipher suite definition. Filters last value
+%% for now (compatibility reasons).
+%%--------------------------------------------------------------------
+erl_suite_definition(S) ->
+ case suite_definition(S) of
+ {KeyExchange, Cipher, Hash, default_prf} ->
+ {KeyExchange, Cipher, Hash};
+ Suite ->
+ Suite
+ end.
+
+%%--------------------------------------------------------------------
-spec suite(erl_cipher_suite()) -> cipher_suite().
%%
%% Description: Return TLS cipher suite definition.
@@ -1384,18 +1413,14 @@ filter(DerCert, Ciphers) ->
%%
%% Description: Filter suites for algorithms supported by crypto.
%%-------------------------------------------------------------------
-filter_suites(Suites = [{_,_,_}|_]) ->
+filter_suites(Suites = [Value|_]) when is_tuple(Value) ->
Algos = crypto:supports(),
+ Hashs = proplists:get_value(hashs, Algos),
lists:filter(fun({KeyExchange, Cipher, Hash}) ->
is_acceptable_keyexchange(KeyExchange, proplists:get_value(public_keys, Algos)) andalso
is_acceptable_cipher(Cipher, proplists:get_value(ciphers, Algos)) andalso
- is_acceptable_hash(Hash, proplists:get_value(hashs, Algos))
- end, Suites);
-
-filter_suites(Suites = [{_,_,_,_}|_]) ->
- Algos = crypto:supports(),
- Hashs = proplists:get_value(hashs, Algos),
- lists:filter(fun({KeyExchange, Cipher, Hash, Prf}) ->
+ is_acceptable_hash(Hash, proplists:get_value(hashs, Algos));
+ ({KeyExchange, Cipher, Hash, Prf}) ->
is_acceptable_keyexchange(KeyExchange, proplists:get_value(public_keys, Algos)) andalso
is_acceptable_cipher(Cipher, proplists:get_value(ciphers, Algos)) andalso
is_acceptable_hash(Hash, Hashs) andalso
@@ -1714,7 +1739,8 @@ dhe_rsa_suites() ->
?TLS_DHE_RSA_WITH_DES_CBC_SHA,
?TLS_DHE_RSA_WITH_AES_128_GCM_SHA256,
?TLS_DHE_RSA_WITH_AES_256_GCM_SHA384,
- ?TLS_DHE_RSA_WITH_CHACHA20_POLY1305_SHA256].
+ ?TLS_DHE_RSA_WITH_CHACHA20_POLY1305_SHA256
+ ].
psk_rsa_suites() ->
[?TLS_RSA_PSK_WITH_AES_256_GCM_SHA384,
diff --git a/lib/ssl/src/ssl_connection.erl b/lib/ssl/src/ssl_connection.erl
index f8afbdb41d..ec7d086934 100644
--- a/lib/ssl/src/ssl_connection.erl
+++ b/lib/ssl/src/ssl_connection.erl
@@ -836,15 +836,22 @@ handle_sync_event(session_info, _, StateName,
#state{session = #session{session_id = Id,
cipher_suite = Suite}} = State) ->
{reply, [{session_id, Id},
- {cipher_suite, ssl:suite_definition(Suite)}],
+ {cipher_suite, ssl_cipher:erl_suite_definition(Suite)}],
StateName, State, get_timeout(State)};
handle_sync_event(peer_certificate, _, StateName,
#state{session = #session{peer_certificate = Cert}}
= State) ->
{reply, {ok, Cert}, StateName, State, get_timeout(State)};
-handle_sync_event(connection_information, _, StateName, #state{sni_hostname = SNIHostname, session = #session{cipher_suite = CipherSuite}, negotiated_version = Version} = State) ->
- {reply, {ok, [{protocol, tls_record:protocol_version(Version)}, {cipher_suite, ssl:suite_definition(CipherSuite)}, {sni_hostname, SNIHostname}]}, StateName, State, get_timeout(State)}.
+handle_sync_event(connection_information, _, StateName, State) ->
+ Info = connection_info(State),
+ {reply, {ok, Info}, StateName, State, get_timeout(State)}.
+connection_info(#state{sni_hostname = SNIHostname,
+ session = #session{cipher_suite = CipherSuite},
+ negotiated_version = Version, ssl_options = Opts}) ->
+ [{protocol, tls_record:protocol_version(Version)},
+ {cipher_suite, ssl_cipher:erl_suite_definition(CipherSuite)},
+ {sni_hostname, SNIHostname}] ++ ssl_options_list(Opts).
handle_info({ErrorTag, Socket, econnaborted}, StateName,
#state{socket = Socket, transport_cb = Transport,
@@ -974,7 +981,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,
@@ -1781,7 +1788,7 @@ handle_trusted_certs_db(#state{ssl_options = #ssl_options{cacertfile = <<>>, cac
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);
@@ -1885,3 +1892,28 @@ negotiated_hashsign(undefined, Alg, Version) ->
negotiated_hashsign(HashSign = {_, _}, _, _) ->
HashSign.
+ssl_options_list(SslOptions) ->
+ Fileds = record_info(fields, ssl_options),
+ Values = tl(tuple_to_list(SslOptions)),
+ ssl_options_list(Fileds, Values, []).
+
+ssl_options_list([],[], Acc) ->
+ lists:reverse(Acc);
+%% Skip internal options, only return user options
+ssl_options_list([protocol | Keys], [_ | Values], Acc) ->
+ ssl_options_list(Keys, Values, Acc);
+ssl_options_list([erl_dist | Keys], [_ | Values], Acc) ->
+ ssl_options_list(Keys, Values, Acc);
+ssl_options_list([renegotiate_at | Keys], [_ | Values], Acc) ->
+ ssl_options_list(Keys, Values, Acc);
+ssl_options_list([ciphers = Key | Keys], [Value | Values], Acc) ->
+ ssl_options_list(Keys, Values,
+ [{Key, lists:map(
+ fun(Suite) ->
+ ssl_cipher:erl_suite_definition(Suite)
+ end, Value)}
+ | Acc]);
+ssl_options_list([Key | Keys], [Value | Values], Acc) ->
+ ssl_options_list(Keys, Values, [{Key, Value} | Acc]).
+
+
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_handshake.erl b/lib/ssl/src/ssl_handshake.erl
index e9e140836b..e98073080a 100644
--- a/lib/ssl/src/ssl_handshake.erl
+++ b/lib/ssl/src/ssl_handshake.erl
@@ -2072,12 +2072,9 @@ crl_check(OtpCert, Check, CertDbHandle, CertDbRef, {Callback, CRLDbHandle}, _) -
],
case dps_and_crls(OtpCert, Callback, CRLDbHandle, ext) of
no_dps ->
- case dps_and_crls(OtpCert, Callback, CRLDbHandle, same_issuer) of
- [] ->
- valid; %% No relevant CRL existed
- DpsAndCRls ->
- crl_check_same_issuer(OtpCert, Check, DpsAndCRls, Options)
- end;
+ crl_check_same_issuer(OtpCert, Check,
+ dps_and_crls(OtpCert, Callback, CRLDbHandle, same_issuer),
+ Options);
DpsAndCRLs -> %% This DP list may be empty if relevant CRLs existed
%% but could not be retrived, will result in {bad_cert, revocation_status_undetermined}
case public_key:pkix_crls_validate(OtpCert, DpsAndCRLs, Options) of
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..311dac4619 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,13 @@ 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),
+ session_client_invalidator = undefined,
+ session_server_invalidator = undefined
}}.
%%--------------------------------------------------------------------
@@ -269,7 +282,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 +321,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 +334,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,
@@ -380,13 +380,17 @@ handle_cast({invalidate_pem, File},
handle_info(validate_sessions, #state{session_cache_cb = CacheCb,
session_cache_client = ClientCache,
session_cache_server = ServerCache,
- session_lifetime = LifeTime
+ session_lifetime = LifeTime,
+ session_client_invalidator = Client,
+ session_server_invalidator = Server
} = State) ->
Timer = erlang:send_after(?SESSION_VALIDATION_INTERVAL,
self(), validate_sessions),
- start_session_validator(ClientCache, CacheCb, LifeTime),
- start_session_validator(ServerCache, CacheCb, LifeTime),
- {noreply, State#state{session_validation_timer = Timer}};
+ CPid = start_session_validator(ClientCache, CacheCb, LifeTime, Client),
+ SPid = start_session_validator(ServerCache, CacheCb, LifeTime, Server),
+ {noreply, State#state{session_validation_timer = Timer,
+ session_client_invalidator = CPid,
+ session_server_invalidator = SPid}};
handle_info({delayed_clean_session, Key, Cache}, #state{session_cache_cb = CacheCb
@@ -413,10 +417,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}.
@@ -473,9 +477,11 @@ validate_session(Port, Session, LifeTime) ->
invalidate_session(Port, Session)
end.
-start_session_validator(Cache, CacheCb, LifeTime) ->
+start_session_validator(Cache, CacheCb, LifeTime, undefined) ->
spawn_link(?MODULE, init_session_validator,
- [[get(ssl_manager), Cache, CacheCb, LifeTime]]).
+ [[get(ssl_manager), Cache, CacheCb, LifeTime]]);
+start_session_validator(_,_,_, Pid) ->
+ Pid.
init_session_validator([SslManagerName, Cache, CacheCb, LifeTime]) ->
put(ssl_manager, SslManagerName),
@@ -497,7 +503,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 +519,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 +554,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 +581,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 +691,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 +713,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()}, undefined);
+invalidate_session_cache(Pid, _CacheCb, _Cache) ->
+ Pid.
diff --git a/lib/ssl/src/ssl_record.erl b/lib/ssl/src/ssl_record.erl
index 75cfecdf5e..ce6b8fb84f 100644
--- a/lib/ssl/src/ssl_record.erl
+++ b/lib/ssl/src/ssl_record.erl
@@ -311,9 +311,19 @@ set_pending_cipher_state(#connection_states{pending_read = Read,
%%
%% Description: Encodes a handshake message to send on the ssl-socket.
%%--------------------------------------------------------------------
-encode_handshake(Frag, Version, ConnectionStates) ->
- encode_plain_text(?HANDSHAKE, Version, Frag, ConnectionStates).
-
+encode_handshake(Frag, Version,
+ #connection_states{current_write =
+ #connection_state{
+ security_parameters =
+ #security_parameters{bulk_cipher_algorithm = BCA}}} =
+ ConnectionStates) ->
+ case iolist_size(Frag) of
+ N when N > ?MAX_PLAIN_TEXT_LENGTH ->
+ Data = split_bin(iolist_to_binary(Frag), ?MAX_PLAIN_TEXT_LENGTH, Version, BCA),
+ encode_iolist(?HANDSHAKE, Data, Version, ConnectionStates);
+ _ ->
+ encode_plain_text(?HANDSHAKE, Version, Frag, ConnectionStates)
+ end.
%%--------------------------------------------------------------------
-spec encode_alert_record(#alert{}, ssl_version(), #connection_states{}) ->
{iolist(), #connection_states{}}.
diff --git a/lib/ssl/src/ssl_session.erl b/lib/ssl/src/ssl_session.erl
index 0d6cc93a20..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};
diff --git a/lib/ssl/src/ssl_session_cache.erl b/lib/ssl/src/ssl_session_cache.erl
index cfc48cd935..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.
@@ -86,6 +86,12 @@ select_session(Cache, PartialKey) ->
[{{{PartialKey,'_'}, '$1'},[],['$1']}]).
%%--------------------------------------------------------------------
+%% Description: Returns the cache size
+%%--------------------------------------------------------------------
+size(Cache) ->
+ ets:info(Cache, size).
+
+%%--------------------------------------------------------------------
%%% Internal functions
%%--------------------------------------------------------------------
cache_name(Name) ->
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..4c789793ec 100644
--- a/lib/ssl/src/ssl_tls_dist_proxy.erl
+++ b/lib/ssl/src/ssl_tls_dist_proxy.erl
@@ -20,7 +20,7 @@
-module(ssl_tls_dist_proxy).
--export([listen/1, accept/1, connect/2, get_tcp_address/1]).
+-export([listen/2, accept/2, connect/3, get_tcp_address/1]).
-export([init/1, start_link/0, handle_call/3, handle_cast/2, handle_info/2,
terminate/2, code_change/3, ssl_options/2]).
@@ -39,14 +39,63 @@
%% Internal application API
%%====================================================================
-listen(Name) ->
- gen_server:call(?MODULE, {listen, Name}, infinity).
+listen(Driver, Name) ->
+ gen_server:call(?MODULE, {listen, Driver, Name}, infinity).
+
+accept(Driver, Listen) ->
+ gen_server:call(?MODULE, {accept, Driver, Listen}, infinity).
+
+connect(Driver, Ip, Port) ->
+ gen_server:call(?MODULE, {connect, Driver, 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.
-accept(Listen) ->
- gen_server:call(?MODULE, {accept, Listen}, infinity).
+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.
-connect(Ip, Port) ->
- gen_server:call(?MODULE, {connect, Ip, Port}, infinity).
+connect_options(Opts) ->
+ case application:get_env(kernel, inet_dist_connect_options) of
+ {ok,ConnectOpts} ->
+ lists:ukeysort(1, ConnectOpts ++ Opts);
+ _ ->
+ Opts
+ end.
%%====================================================================
%% gen_server callbacks
@@ -59,29 +108,34 @@ init([]) ->
process_flag(priority, max),
{ok, #state{}}.
-handle_call({listen, Name}, _From, State) ->
- case gen_tcp:listen(0, [{active, false}, {packet,?PPRE}]) of
+handle_call({listen, Driver, Name}, _From, State) ->
+ case gen_tcp:listen(0, [{active, false}, {packet,?PPRE}, {ip, loopback}]) 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},
+ Driver:family()]),
{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;
-handle_call({accept, Listen}, {From, _}, State = #state{listen={_, World}}) ->
+handle_call({accept, _Driver, Listen}, {From, _}, State = #state{listen={_, World}}) ->
Self = self(),
ErtsPid = spawn_link(fun() -> accept_loop(Self, erts, Listen, From) end),
WorldPid = spawn_link(fun() -> accept_loop(Self, world, World, Listen) end),
{reply, ErtsPid, State#state{accept_loop={ErtsPid, WorldPid}}};
-handle_call({connect, Ip, Port}, {From, _}, State) ->
+handle_call({connect, Driver, Ip, Port}, {From, _}, State) ->
Me = self(),
- Pid = spawn_link(fun() -> setup_proxy(Ip, Port, Me) end),
+ Pid = spawn_link(fun() -> setup_proxy(Driver, Ip, Port, Me) end),
receive
{Pid, go_ahead, LPort} ->
Res = {ok, Socket} = try_connect(LPort),
@@ -134,6 +188,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};
@@ -150,6 +205,7 @@ accept_loop(Proxy, world = Type, Listen, Extra) ->
case gen_tcp:accept(Listen) of
{ok, Socket} ->
Opts = get_ssl_options(server),
+ wait_for_code_server(),
case ssl:ssl_accept(Socket, Opts) of
{ok, SslSocket} ->
PairHandler =
@@ -158,6 +214,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;
@@ -166,20 +227,50 @@ accept_loop(Proxy, world = Type, Listen, Extra) ->
end,
accept_loop(Proxy, Type, Listen, Extra).
+wait_for_code_server() ->
+ %% This is an ugly hack. Upgrading a socket to TLS requires the
+ %% crypto module to be loaded. Loading the crypto module triggers
+ %% its on_load function, which calls code:priv_dir/1 to find the
+ %% directory where its NIF library is. However, distribution is
+ %% started earlier than the code server, so the code server is not
+ %% necessarily started yet, and code:priv_dir/1 might fail because
+ %% of that, if we receive an incoming connection on the
+ %% distribution port early enough.
+ %%
+ %% If the on_load function of a module fails, the module is
+ %% unloaded, and the function call that triggered loading it fails
+ %% with 'undef', which is rather confusing.
+ %%
+ %% Thus, the ssl_tls_dist_proxy process will terminate, and be
+ %% restarted by ssl_dist_sup. However, it won't have any memory
+ %% of being asked by net_kernel to listen for incoming
+ %% connections. Hence, the node will believe that it's open for
+ %% distribution, but it actually isn't.
+ %%
+ %% So let's avoid that by waiting for the code server to start.
+ case whereis(code_server) of
+ undefined ->
+ timer:sleep(10),
+ wait_for_code_server();
+ Pid when is_pid(Pid) ->
+ ok
+ end.
+
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} ->
try_connect(Port)
end.
-setup_proxy(Ip, Port, Parent) ->
+setup_proxy(Driver, 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
+ Opts = connect_options(get_ssl_options(client)),
+ case ssl:connect(Ip, Port, [{active, true}, binary, {packet,?PPRE}, nodelay(),
+ Driver:family()] ++ Opts) of
{ok, World} ->
- {ok, ErtsL} = gen_tcp:listen(0, [{active, true}, {ip, {127,0,0,1}}, binary, {packet,?PPRE}]),
+ {ok, ErtsL} = gen_tcp:listen(0, [{active, true}, {ip, loopback}, binary, {packet,?PPRE}]),
{ok, #net_address{address={_,LPort}}} = get_tcp_address(ErtsL),
Parent ! {self(), go_ahead, LPort},
case gen_tcp:accept(ErtsL) of
@@ -189,29 +280,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..f98ea83771 100644
--- a/lib/ssl/src/ssl_v3.erl
+++ b/lib/ssl/src/ssl_v3.erl
@@ -143,8 +143,7 @@ suites() ->
?TLS_RSA_WITH_3DES_EDE_CBC_SHA,
?TLS_DHE_RSA_WITH_AES_128_CBC_SHA,
?TLS_DHE_DSS_WITH_AES_128_CBC_SHA,
- ?TLS_RSA_WITH_AES_128_CBC_SHA,
- ?TLS_RSA_WITH_DES_CBC_SHA
+ ?TLS_RSA_WITH_AES_128_CBC_SHA
].
%%--------------------------------------------------------------------
diff --git a/lib/ssl/src/tls_connection.erl b/lib/ssl/src/tls_connection.erl
index 3093508f61..c3f0206d25 100644
--- a/lib/ssl/src/tls_connection.erl
+++ b/lib/ssl/src/tls_connection.erl
@@ -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,
@@ -763,6 +764,8 @@ handle_tls_handshake(Handle, StateName,
case Handle(Packet, FsmReturn) of
{next_state, NextStateName, State, _Timeout} ->
handle_tls_handshake(Handle, NextStateName, State);
+ {next_state, NextStateName, State} ->
+ handle_tls_handshake(Handle, NextStateName, State);
{stop, _,_} = Stop ->
Stop
end;
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/src/tls_v1.erl b/lib/ssl/src/tls_v1.erl
index 71e5f349dd..09e378cfeb 100644
--- a/lib/ssl/src/tls_v1.erl
+++ b/lib/ssl/src/tls_v1.erl
@@ -208,9 +208,7 @@ suites(Minor) when Minor == 1; Minor == 2 ->
?TLS_DHE_DSS_WITH_AES_128_CBC_SHA,
?TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA,
?TLS_ECDH_RSA_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
+ ?TLS_RSA_WITH_AES_128_CBC_SHA
];
suites(3) ->
[
diff --git a/lib/ssl/test/Makefile b/lib/ssl/test/Makefile
index 999df320a3..89a62c3a4b 100644
--- a/lib/ssl/test/Makefile
+++ b/lib/ssl/test/Makefile
@@ -81,7 +81,7 @@ HRL_FILES_NEEDED_IN_TEST = \
TARGET_FILES = $(MODULES:%=$(EBIN)/%.$(EMULATOR))
-INCLUDES = -I. -I$(ERL_TOP)/lib/test_server/include/
+INCLUDES = -I.
DATADIRS = ssl_basic_SUITE_data
@@ -100,8 +100,7 @@ RELSYSDIR = $(RELEASE_PATH)/ssl_test
# The path to the test_server ebin dir is needed when
# running the target "targets".
# ----------------------------------------------------
-ERL_COMPILE_FLAGS += -pa ../../../internal_tools/test_server/ebin \
- $(INCLUDES)
+ERL_COMPILE_FLAGS += $(INCLUDES)
# ----------------------------------------------------
# Targets
diff --git a/lib/ssl/test/erl_make_certs.erl b/lib/ssl/test/erl_make_certs.erl
index 8e909a5b74..f5cada9021 100644
--- a/lib/ssl/test/erl_make_certs.erl
+++ b/lib/ssl/test/erl_make_certs.erl
@@ -334,7 +334,9 @@ make_key(dsa, _Opts) ->
gen_dsa2(128, 20); %% Bytes i.e. {1024, 160}
make_key(ec, _Opts) ->
%% (OBS: for testing only)
- gen_ec2(secp256k1).
+ CurveOid = hd(tls_v1:ecc_curves(0)),
+ NamedCurve = pubkey_cert_records:namedCurves(CurveOid),
+ gen_ec2(NamedCurve).
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% RSA key generation (OBS: for testing only)
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 3a9f21ea99..75b639b23b 100644
--- a/lib/ssl/test/ssl_ECC_SUITE.erl
+++ b/lib/ssl/test/ssl_ECC_SUITE.erl
@@ -248,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) ->
@@ -270,10 +273,11 @@ 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) ->
diff --git a/lib/ssl/test/ssl_basic_SUITE.erl b/lib/ssl/test/ssl_basic_SUITE.erl
index 6f6107de2c..847a9f19de 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,
@@ -121,6 +121,7 @@ options_tests() ->
api_tests() ->
[connection_info,
+ connection_information,
peername,
peercert,
peercert_with_client_cert,
@@ -136,6 +137,7 @@ api_tests() ->
shutdown_both,
shutdown_error,
hibernate,
+ hibernate_right_away,
listen_socket,
ssl_accept_timeout,
ssl_recv_timeout,
@@ -181,6 +183,8 @@ cipher_tests() ->
rc4_rsa_cipher_suites,
rc4_ecdh_rsa_cipher_suites,
rc4_ecdsa_cipher_suites,
+ des_rsa_cipher_suites,
+ des_ecdh_rsa_cipher_suites,
default_reject_anonymous].
cipher_tests_ec() ->
@@ -331,6 +335,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()]),
@@ -435,7 +447,7 @@ connection_info(Config) when is_list(Config) ->
{from, self()},
{mfa, {?MODULE, connection_info_result, []}},
{options,
- [{ciphers,[{rsa,des_cbc,sha,no_export}]} |
+ [{ciphers,[{rsa, aes_128_cbc, sha}]} |
ClientOpts]}]),
ct:log("Testcase ~p, Client ~p Server ~p ~n",
@@ -444,13 +456,44 @@ connection_info(Config) when is_list(Config) ->
Version =
tls_record:protocol_version(tls_record:highest_protocol_version([])),
- ServerMsg = ClientMsg = {ok, {Version, {rsa, des_cbc, sha}}},
+ ServerMsg = ClientMsg = {ok, {Version, {rsa, aes_128_cbc, sha}}},
+
+ ssl_test_lib:check_result(Server, ServerMsg, Client, ClientMsg),
+
+ ssl_test_lib:close(Server),
+ ssl_test_lib:close(Client).
+
+%%--------------------------------------------------------------------
+
+connection_information() ->
+ [{doc,"Test the API function ssl:connection_information/1"}].
+connection_information(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, connection_information_result, []}},
+ {options, ServerOpts}]),
+
+ Port = ssl_test_lib:inet_port(Server),
+ Client = ssl_test_lib:start_client([{node, ClientNode}, {port, Port},
+ {host, Hostname},
+ {from, self()},
+ {mfa, {?MODULE, connection_information_result, []}},
+ {options, ClientOpts}]),
+
+ ct:log("Testcase ~p, Client ~p Server ~p ~n",
+ [self(), Client, Server]),
+
+ ServerMsg = ClientMsg = ok,
ssl_test_lib:check_result(Server, ServerMsg, Client, ClientMsg),
ssl_test_lib:close(Server),
ssl_test_lib:close(Client).
+
%%--------------------------------------------------------------------
protocol_versions() ->
[{doc,"Test to set a list of protocol versions in app environment."}].
@@ -1156,6 +1199,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"}].
@@ -1924,6 +1984,23 @@ rc4_ecdsa_cipher_suites(Config) when is_list(Config) ->
Ciphers = ssl_test_lib:rc4_suites(NVersion),
run_suites(Ciphers, Version, Config, rc4_ecdsa).
+%%-------------------------------------------------------------------
+des_rsa_cipher_suites()->
+ [{doc, "Test the RC4 ciphersuites"}].
+des_rsa_cipher_suites(Config) when is_list(Config) ->
+ NVersion = tls_record:highest_protocol_version([]),
+ Version = tls_record:protocol_version(NVersion),
+ Ciphers = ssl_test_lib:des_suites(NVersion),
+ run_suites(Ciphers, Version, Config, des_rsa).
+%-------------------------------------------------------------------
+des_ecdh_rsa_cipher_suites()->
+ [{doc, "Test the RC4 ciphersuites"}].
+des_ecdh_rsa_cipher_suites(Config) when is_list(Config) ->
+ NVersion = tls_record:highest_protocol_version([]),
+ Version = tls_record:protocol_version(NVersion),
+ Ciphers = ssl_test_lib:des_suites(NVersion),
+ run_suites(Ciphers, Version, Config, des_dhe_rsa).
+
%%--------------------------------------------------------------------
default_reject_anonymous()->
[{doc,"Test that by default anonymous cipher suites are rejected "}].
@@ -2660,7 +2737,12 @@ defaults(Config) when is_list(Config)->
true = lists:member(sslv3, Available),
false = lists:member(sslv3, Supported),
false = lists:member({rsa,rc4_128,sha}, ssl:cipher_suites()),
- true = lists:member({rsa,rc4_128,sha}, ssl:cipher_suites(all)).
+ true = lists:member({rsa,rc4_128,sha}, ssl:cipher_suites(all)),
+ false = lists:member({rsa,des_cbc,sha}, ssl:cipher_suites()),
+ true = lists:member({rsa,des_cbc,sha}, ssl:cipher_suites(all)),
+ false = lists:member({dhe_rsa,des_cbc,sha}, ssl:cipher_suites()),
+ true = lists:member({dhe_rsa,des_cbc,sha}, ssl:cipher_suites(all)).
+
%%--------------------------------------------------------------------
reuseaddr() ->
[{doc,"Test reuseaddr option"}].
@@ -2898,6 +2980,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."}].
@@ -3911,7 +4030,15 @@ run_suites(Ciphers, Version, Config, Type) ->
rc4_ecdsa ->
{?config(client_opts, Config),
[{ciphers, Ciphers} |
- ?config(server_ecdsa_opts, Config)]}
+ ?config(server_ecdsa_opts, Config)]};
+ des_dhe_rsa ->
+ {?config(client_opts, Config),
+ [{ciphers, Ciphers} |
+ ?config(server_rsa_opts, Config)]};
+ des_rsa ->
+ {?config(client_opts, Config),
+ [{ciphers, Ciphers} |
+ ?config(server_opts, Config)]}
end,
Result = lists:map(fun(Cipher) ->
@@ -3926,7 +4053,7 @@ run_suites(Ciphers, Version, Config, Type) ->
end.
erlang_cipher_suite(Suite) when is_list(Suite)->
- ssl:suite_definition(ssl_cipher:openssl_suite(Suite));
+ ssl_cipher:erl_suite_definition(ssl_cipher:openssl_suite(Suite));
erlang_cipher_suite(Suite) ->
Suite.
@@ -3947,11 +4074,11 @@ cipher(CipherSuite, Version, Config, ClientOpts, ServerOpts) ->
Port = ssl_test_lib:inet_port(Server),
Client = ssl_test_lib:start_client([{node, ClientNode}, {port, Port},
{host, Hostname},
- {from, self()},
- {mfa, {ssl_test_lib, cipher_result, [ConnectionInfo]}},
- {options,
- [{ciphers,[CipherSuite]} |
- ClientOpts]}]),
+ {from, self()},
+ {mfa, {ssl_test_lib, cipher_result, [ConnectionInfo]}},
+ {options,
+ [{ciphers,[CipherSuite]} |
+ ClientOpts]}]),
Result = ssl_test_lib:wait_for_result(Server, ok, Client, ok),
@@ -3965,6 +4092,17 @@ cipher(CipherSuite, Version, Config, ClientOpts, ServerOpts) ->
[{ErlangCipherSuite, Error}]
end.
+connection_information_result(Socket) ->
+ {ok, Info = [_ | _]} = ssl:connection_information(Socket),
+ case length(Info) > 3 of
+ true ->
+ %% Atleast one ssloption() is set
+ ct:log("Info ~p", [Info]),
+ ok;
+ false ->
+ ct:fail(no_ssl_options_returned)
+ end.
+
connection_info_result(Socket) ->
{ok, Info} = ssl:connection_information(Socket, [protocol, cipher_suite]),
{ok, {proplists:get_value(protocol, Info), proplists:get_value(cipher_suite, Info)}}.
@@ -4091,6 +4229,12 @@ first_rsa_suite([{dhe_rsa, _, _} = Suite| _]) ->
Suite;
first_rsa_suite([{rsa, _, _} = Suite| _]) ->
Suite;
+first_rsa_suite([{ecdhe_rsa, _, _, _} = Suite | _]) ->
+ Suite;
+first_rsa_suite([{dhe_rsa, _, _, _} = Suite| _]) ->
+ Suite;
+first_rsa_suite([{rsa, _, _, _} = Suite| _]) ->
+ Suite;
first_rsa_suite([_ | Rest]) ->
first_rsa_suite(Rest).
diff --git a/lib/ssl/test/ssl_certificate_verify_SUITE.erl b/lib/ssl/test/ssl_certificate_verify_SUITE.erl
index 5940a86a7f..d10506cb69 100644
--- a/lib/ssl/test/ssl_certificate_verify_SUITE.erl
+++ b/lib/ssl/test/ssl_certificate_verify_SUITE.erl
@@ -66,7 +66,9 @@ tests() ->
invalid_signature_client,
invalid_signature_server,
extended_key_usage_verify_peer,
- extended_key_usage_verify_none].
+ extended_key_usage_verify_none,
+ critical_extension_verify_peer,
+ critical_extension_verify_none].
error_handling_tests()->
[client_with_cert_cipher_suites_handshake,
@@ -75,7 +77,8 @@ error_handling_tests()->
unknown_server_ca_accept_verify_none,
unknown_server_ca_accept_verify_peer,
unknown_server_ca_accept_backwardscompatibility,
- no_authority_key_identifier].
+ no_authority_key_identifier,
+ no_authority_key_identifier_and_nonstandard_encoding].
init_per_suite(Config0) ->
catch crypto:stop(),
@@ -794,6 +797,121 @@ extended_key_usage_verify_none(Config) when is_list(Config) ->
ssl_test_lib:close(Client).
%%--------------------------------------------------------------------
+critical_extension_verify_peer() ->
+ [{doc,"Test cert that has a critical unknown extension in verify_peer mode"}].
+
+critical_extension_verify_peer(Config) when is_list(Config) ->
+ ClientOpts = ?config(client_verification_opts, Config),
+ ServerOpts = ?config(server_verification_opts, Config),
+ PrivDir = ?config(priv_dir, Config),
+ Active = ?config(active, Config),
+ ReceiveFunction = ?config(receive_function, Config),
+
+ KeyFile = filename:join(PrivDir, "otpCA/private/key.pem"),
+ NewCertName = integer_to_list(erlang:unique_integer()) ++ ".pem",
+
+ ServerCertFile = proplists:get_value(certfile, ServerOpts),
+ NewServerCertFile = filename:join([PrivDir, "server", NewCertName]),
+ add_critical_netscape_cert_type(ServerCertFile, NewServerCertFile, KeyFile),
+ NewServerOpts = [{certfile, NewServerCertFile} | proplists:delete(certfile, ServerOpts)],
+
+ ClientCertFile = proplists:get_value(certfile, ClientOpts),
+ NewClientCertFile = filename:join([PrivDir, "client", NewCertName]),
+ add_critical_netscape_cert_type(ClientCertFile, NewClientCertFile, KeyFile),
+ NewClientOpts = [{certfile, NewClientCertFile} | proplists:delete(certfile, ClientOpts)],
+
+ {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config),
+
+ Server = ssl_test_lib:start_server_error(
+ [{node, ServerNode}, {port, 0},
+ {from, self()},
+ {mfa, {ssl_test_lib, ReceiveFunction, []}},
+ {options, [{verify, verify_peer}, {active, Active} | NewServerOpts]}]),
+ Port = ssl_test_lib:inet_port(Server),
+ Client = ssl_test_lib:start_client_error(
+ [{node, ClientNode}, {port, Port},
+ {host, Hostname},
+ {from, self()},
+ {mfa, {ssl_test_lib, ReceiveFunction, []}},
+ {options, [{verify, verify_peer}, {active, Active} | NewClientOpts]}]),
+
+ %% This certificate has a critical extension that we don't
+ %% understand. Therefore, verification should fail.
+ tcp_delivery_workaround(Server, {error, {tls_alert, "unsupported certificate"}},
+ Client, {error, {tls_alert, "unsupported certificate"}}),
+
+ ssl_test_lib:close(Server),
+ ok.
+
+%%--------------------------------------------------------------------
+critical_extension_verify_none() ->
+ [{doc,"Test cert that has a critical unknown extension in verify_none mode"}].
+
+critical_extension_verify_none(Config) when is_list(Config) ->
+ ClientOpts = ?config(client_verification_opts, Config),
+ ServerOpts = ?config(server_verification_opts, Config),
+ PrivDir = ?config(priv_dir, Config),
+ Active = ?config(active, Config),
+ ReceiveFunction = ?config(receive_function, Config),
+
+ KeyFile = filename:join(PrivDir, "otpCA/private/key.pem"),
+ NewCertName = integer_to_list(erlang:unique_integer()) ++ ".pem",
+
+ ServerCertFile = proplists:get_value(certfile, ServerOpts),
+ NewServerCertFile = filename:join([PrivDir, "server", NewCertName]),
+ add_critical_netscape_cert_type(ServerCertFile, NewServerCertFile, KeyFile),
+ NewServerOpts = [{certfile, NewServerCertFile} | proplists:delete(certfile, ServerOpts)],
+
+ ClientCertFile = proplists:get_value(certfile, ClientOpts),
+ NewClientCertFile = filename:join([PrivDir, "client", NewCertName]),
+ add_critical_netscape_cert_type(ClientCertFile, NewClientCertFile, KeyFile),
+ NewClientOpts = [{certfile, NewClientCertFile} | proplists:delete(certfile, ClientOpts)],
+
+ {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, ReceiveFunction, []}},
+ {options, [{verify, verify_none}, {active, Active} | NewServerOpts]}]),
+ Port = ssl_test_lib:inet_port(Server),
+ Client = ssl_test_lib:start_client(
+ [{node, ClientNode}, {port, Port},
+ {host, Hostname},
+ {from, self()},
+ {mfa, {ssl_test_lib, ReceiveFunction, []}},
+ {options, [{verify, verify_none}, {active, Active} | NewClientOpts]}]),
+
+ %% This certificate has a critical extension that we don't
+ %% understand. But we're using `verify_none', so verification
+ %% shouldn't fail.
+ ssl_test_lib:check_result(Server, ok, Client, ok),
+
+ ssl_test_lib:close(Server),
+ ssl_test_lib:close(Client),
+ ok.
+
+add_critical_netscape_cert_type(CertFile, NewCertFile, KeyFile) ->
+ [KeyEntry] = ssl_test_lib:pem_to_der(KeyFile),
+ Key = ssl_test_lib:public_key(public_key:pem_entry_decode(KeyEntry)),
+
+ [{'Certificate', DerCert, _}] = ssl_test_lib:pem_to_der(CertFile),
+ OTPCert = public_key:pkix_decode_cert(DerCert, otp),
+ %% This is the "Netscape Cert Type" extension, telling us that the
+ %% certificate can be used for SSL clients and SSL servers.
+ NetscapeCertTypeExt = #'Extension'{
+ extnID = {2,16,840,1,113730,1,1},
+ critical = true,
+ extnValue = <<3,2,6,192>>},
+ OTPTbsCert = OTPCert#'OTPCertificate'.tbsCertificate,
+ Extensions = OTPTbsCert#'OTPTBSCertificate'.extensions,
+ NewOTPTbsCert = OTPTbsCert#'OTPTBSCertificate'{
+ extensions = [NetscapeCertTypeExt] ++ Extensions},
+ NewDerCert = public_key:pkix_sign(NewOTPTbsCert, Key),
+ ssl_test_lib:der_to_pem(NewCertFile, [{'Certificate', NewDerCert, not_encrypted}]),
+ ok.
+
+%%--------------------------------------------------------------------
no_authority_key_identifier() ->
[{doc, "Test cert that does not have authorityKeyIdentifier extension"
" but are present in trusted certs db."}].
@@ -850,6 +968,68 @@ delete_authority_key_extension([Head | Rest], Acc) ->
%%--------------------------------------------------------------------
+no_authority_key_identifier_and_nonstandard_encoding() ->
+ [{doc, "Test cert with nonstandard encoding that does not have"
+ " authorityKeyIdentifier extension but are present in trusted certs db."}].
+
+no_authority_key_identifier_and_nonstandard_encoding(Config) when is_list(Config) ->
+ ClientOpts = ?config(client_verification_opts, Config),
+ ServerOpts = ?config(server_verification_opts, Config),
+ PrivDir = ?config(priv_dir, Config),
+
+ KeyFile = filename:join(PrivDir, "otpCA/private/key.pem"),
+ [KeyEntry] = ssl_test_lib:pem_to_der(KeyFile),
+ Key = ssl_test_lib:public_key(public_key:pem_entry_decode(KeyEntry)),
+
+ CertFile = proplists:get_value(certfile, ServerOpts),
+ NewCertFile = filename:join(PrivDir, "server/new_cert.pem"),
+ [{'Certificate', DerCert, _}] = ssl_test_lib:pem_to_der(CertFile),
+ ServerCert = public_key:pkix_decode_cert(DerCert, plain),
+ ServerTbsCert = ServerCert#'Certificate'.tbsCertificate,
+ Extensions0 = ServerTbsCert#'TBSCertificate'.extensions,
+ %% need to remove authorityKeyIdentifier extension to cause DB lookup by signature
+ Extensions = delete_authority_key_extension(Extensions0, []),
+ NewExtensions = replace_key_usage_extension(Extensions, []),
+ NewServerTbsCert = ServerTbsCert#'TBSCertificate'{extensions = NewExtensions},
+
+ ct:log("Extensions ~p~n, NewExtensions: ~p~n", [Extensions, NewExtensions]),
+
+ TbsDer = public_key:pkix_encode('TBSCertificate', NewServerTbsCert, plain),
+ Sig = public_key:sign(TbsDer, md5, Key),
+ NewServerCert = ServerCert#'Certificate'{tbsCertificate = NewServerTbsCert, signature = Sig},
+ NewDerCert = public_key:pkix_encode('Certificate', NewServerCert, plain),
+ ssl_test_lib:der_to_pem(NewCertFile, [{'Certificate', NewDerCert, not_encrypted}]),
+ NewServerOpts = [{certfile, NewCertFile} | proplists:delete(certfile, ServerOpts)],
+
+ {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config),
+
+ Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0},
+ {from, self()},
+ {mfa, {ssl_test_lib,
+ send_recv_result_active, []}},
+ {options, [{active, true} | NewServerOpts]}]),
+ Port = ssl_test_lib:inet_port(Server),
+ Client = ssl_test_lib:start_client([{node, ClientNode}, {port, Port},
+ {host, Hostname},
+ {from, self()},
+ {mfa, {ssl_test_lib,
+ send_recv_result_active, []}},
+ {options, [{verify, verify_peer} | ClientOpts]}]),
+ ssl_test_lib:check_result(Server, ok, Client, ok),
+ ssl_test_lib:close(Server),
+ ssl_test_lib:close(Client).
+
+replace_key_usage_extension([], Acc) ->
+ lists:reverse(Acc);
+replace_key_usage_extension([#'Extension'{extnID = ?'id-ce-keyUsage'} = E | Rest], Acc) ->
+ %% A nonstandard DER encoding of [digitalSignature, keyEncipherment]
+ Val = <<3, 2, 0, 16#A0>>,
+ replace_key_usage_extension(Rest, [E#'Extension'{extnValue = Val} | Acc]);
+replace_key_usage_extension([Head | Rest], Acc) ->
+ replace_key_usage_extension(Rest, [Head | Acc]).
+
+%%--------------------------------------------------------------------
+
invalid_signature_server() ->
[{doc,"Test client with invalid signature"}].
diff --git a/lib/ssl/test/ssl_crl_SUITE.erl b/lib/ssl/test/ssl_crl_SUITE.erl
index 44580be1ff..5b86027210 100644
--- a/lib/ssl/test/ssl_crl_SUITE.erl
+++ b/lib/ssl/test/ssl_crl_SUITE.erl
@@ -53,7 +53,7 @@ groups() ->
{idp_crl, [], basic_tests()}].
basic_tests() ->
- [crl_verify_valid, crl_verify_revoked].
+ [crl_verify_valid, crl_verify_revoked, crl_verify_no_crl].
init_per_suite(Config) ->
@@ -186,11 +186,6 @@ crl_verify_revoked(Config) when is_list(Config) ->
{ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config),
- Server = ssl_test_lib:start_server_error([{node, ServerNode}, {port, 0},
- {from, self()},
- {options, ServerOpts}]),
- Port = ssl_test_lib:inet_port(Server),
-
ssl_crl_cache:insert({file, filename:join([PrivDir, "erlangCA", "crl.pem"])}),
ssl_crl_cache:insert({file, filename:join([PrivDir, "otpCA", "crl.pem"])}),
@@ -206,16 +201,55 @@ crl_verify_revoked(Config) when is_list(Config) ->
{verify, verify_peer}]
end,
- Client = ssl_test_lib:start_client_error([{node, ClientNode}, {port, Port},
- {host, Hostname},
- {from, self()},
- {options, ClientOpts}]),
- receive
- {Server, AlertOrColse} ->
- ct:pal("Server Alert or Close ~p", [AlertOrColse])
- end,
- ssl_test_lib:check_result(Client, {error, {tls_alert, "certificate revoked"}}).
+ crl_verify_error(Hostname, ServerNode, ServerOpts, ClientNode, ClientOpts,
+ "certificate revoked").
+crl_verify_no_crl() ->
+ [{doc,"Verify a simple CRL chain when the CRL is missing"}].
+crl_verify_no_crl(Config) when is_list(Config) ->
+ PrivDir = ?config(cert_dir, Config),
+ Check = ?config(crl_check, Config),
+ ServerOpts = [{keyfile, filename:join([PrivDir, "server", "key.pem"])},
+ {certfile, filename:join([PrivDir, "server", "cert.pem"])},
+ {cacertfile, filename:join([PrivDir, "server", "cacerts.pem"])}],
+ ClientOpts = case ?config(idp_crl, Config) of
+ true ->
+ [{cacertfile, filename:join([PrivDir, "server", "cacerts.pem"])},
+ {crl_check, Check},
+ {crl_cache, {ssl_crl_cache, {internal, [{http, 5000}]}}},
+ {verify, verify_peer}];
+ false ->
+ [{cacertfile, filename:join([PrivDir, "server", "cacerts.pem"])},
+ {crl_check, Check},
+ {verify, verify_peer}]
+ end,
+ {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config),
+
+ %% In case we're running an HTTP server that serves CRLs, let's
+ %% rename those files, so the CRL is absent when we try to verify
+ %% it.
+ %%
+ %% If we're not using an HTTP server, we just need to refrain from
+ %% adding the CRLs to the cache manually.
+ rename_crl(filename:join([PrivDir, "erlangCA", "crl.pem"])),
+ rename_crl(filename:join([PrivDir, "otpCA", "crl.pem"])),
+
+ %% The expected outcome when the CRL is missing depends on the
+ %% crl_check setting.
+ case Check of
+ true ->
+ %% The error "revocation status undetermined" gets turned
+ %% into "bad certificate".
+ crl_verify_error(Hostname, ServerNode, ServerOpts, ClientNode, ClientOpts,
+ "bad certificate");
+ peer ->
+ crl_verify_error(Hostname, ServerNode, ServerOpts, ClientNode, ClientOpts,
+ "bad certificate");
+ best_effort ->
+ %% In "best effort" mode, we consider the certificate not
+ %% to be revoked if we can't find the appropriate CRL.
+ crl_verify_valid(Hostname, ServerNode, ServerOpts, ClientNode, ClientOpts)
+ end.
crl_verify_valid(Hostname, ServerNode, ServerOpts, ClientNode, ClientOpts) ->
Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0},
@@ -236,6 +270,22 @@ crl_verify_valid(Hostname, ServerNode, ServerOpts, ClientNode, ClientOpts) ->
ssl_test_lib:close(Server),
ssl_test_lib:close(Client).
+crl_verify_error(Hostname, ServerNode, ServerOpts, ClientNode, ClientOpts, ExpectedAlert) ->
+ Server = ssl_test_lib:start_server_error([{node, ServerNode}, {port, 0},
+ {from, self()},
+ {options, ServerOpts}]),
+ Port = ssl_test_lib:inet_port(Server),
+
+ Client = ssl_test_lib:start_client_error([{node, ClientNode}, {port, Port},
+ {host, Hostname},
+ {from, self()},
+ {options, ClientOpts}]),
+ receive
+ {Server, AlertOrClose} ->
+ ct:pal("Server Alert or Close ~p", [AlertOrClose])
+ end,
+ ssl_test_lib:check_result(Client, {error, {tls_alert, ExpectedAlert}}).
+
%%--------------------------------------------------------------------
%% Internal functions ------------------------------------------------
%%--------------------------------------------------------------------
@@ -259,3 +309,5 @@ make_dir_path(PathComponents) ->
"",
PathComponents).
+rename_crl(Filename) ->
+ file:rename(Filename, Filename ++ ".notfound").
diff --git a/lib/ssl/test/ssl_dist_SUITE.erl b/lib/ssl/test/ssl_dist_SUITE.erl
index 72d62b29a7..00f9ee8e3c 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, connect_options, use_interface].
groups() ->
[].
@@ -250,6 +251,173 @@ 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) ->
+ try_setting_priority(fun do_listen_options/2, Config).
+
+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).
+%%--------------------------------------------------------------------
+connect_options() ->
+ [{doc, "Test inet_dist_connect_options"}].
+connect_options(Config) when is_list(Config) ->
+ try_setting_priority(fun do_connect_options/2, Config).
+
+do_connect_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_connect_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]),
+ %% Node 1 will have a socket with elevated priority.
+ [_|_] = Elevated1,
+ %% Node 2 will not, since it only applies to outbound connections.
+ [] = 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 +432,30 @@ tstsrvr_format(Fmt, ArgList) ->
send_to_tstcntrl(Message) ->
send_to_tstsrvr({message, Message}).
+try_setting_priority(TestFun, 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),
+ TestFun(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.
+
+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
@@ -346,17 +538,13 @@ host_name() ->
Host.
mk_node_name(Config) ->
- {A, B, C} = erlang:now(),
+ N = erlang:unique_integer([positive]),
Case = ?config(testcase, Config),
atom_to_list(?MODULE)
++ "_"
++ atom_to_list(Case)
++ "_"
- ++ integer_to_list(A)
- ++ "-"
- ++ integer_to_list(B)
- ++ "-"
- ++ integer_to_list(C).
+ ++ integer_to_list(N).
mk_node_cmdline(ListenPort, Name, Args) ->
Static = "-detached -noinput",
@@ -585,12 +773,10 @@ rand_bin(N) ->
rand_bin(0, Acc) ->
Acc;
rand_bin(N, Acc) ->
- rand_bin(N-1, [random:uniform(256)-1|Acc]).
+ rand_bin(N-1, [rand:uniform(256)-1|Acc]).
make_randfile(Dir) ->
{ok, IoDev} = file:open(filename:join([Dir, "RAND"]), [write]),
- {A, B, C} = erlang:now(),
- random:seed(A, B, C),
ok = file:write(IoDev, rand_bin(1024)),
file:close(IoDev).
diff --git a/lib/ssl/test/ssl_session_cache_SUITE.erl b/lib/ssl/test/ssl_session_cache_SUITE.erl
index 924898f6fa..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).
@@ -46,7 +47,9 @@ all() ->
[session_cleanup,
session_cache_process_list,
session_cache_process_mnesia,
- client_unique_session].
+ client_unique_session,
+ max_table_size
+ ].
groups() ->
[].
@@ -92,7 +95,17 @@ init_per_testcase(session_cleanup, Config) ->
Config;
init_per_testcase(client_unique_session, Config) ->
- ct:timetrap({seconds, 20}),
+ 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) ->
@@ -122,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),
@@ -148,7 +165,7 @@ client_unique_session(Config) when is_list(Config) ->
{options, ServerOpts}]),
Port = ssl_test_lib:inet_port(Server),
LastClient = clients_start(Server,
- ClientNode, Hostname, Port, ClientOpts, 20),
+ ClientNode, Hostname, Port, ClientOpts, client_unique_session, 20),
receive
{LastClient, {ok, _}} ->
ok
@@ -157,7 +174,8 @@ client_unique_session(Config) when is_list(Config) ->
[_, _,_, _, Prop] = StatusInfo,
State = ssl_test_lib:state(Prop),
ClientCache = element(2, State),
- 1 = ets:info(ClientCache, size),
+
+ 1 = ssl_session_cache:size(ClientCache),
ssl_test_lib:close(Server, 500),
ssl_test_lib:close(LastClient).
@@ -223,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)"}].
@@ -264,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
%%--------------------------------------------------------------------
@@ -403,21 +429,73 @@ session_cache_process(_Type,Config) when is_list(Config) ->
ssl_basic_SUITE:reuse_session(Config).
-clients_start(_Server, ClientNode, Hostname, Port, ClientOpts, 0) ->
+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, ClientOpts}]);
-clients_start(Server, ClientNode, Hostname, Port, ClientOpts, N) ->
+ {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, ClientOpts}]]),
+ {from, self()}, {options, test_copts(Test, N, ClientOpts)}]]),
Server ! listen,
- clients_start(Server, ClientNode, Hostname, Port, ClientOpts, N-1).
+ 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_sni_SUITE.erl b/lib/ssl/test/ssl_sni_SUITE.erl
index f6ffe91027..90c2a49e61 100644
--- a/lib/ssl/test/ssl_sni_SUITE.erl
+++ b/lib/ssl/test/ssl_sni_SUITE.erl
@@ -108,8 +108,12 @@ ssl_recv(SSLSocket, CurrentData, ExpectedData) ->
send_and_hostname(SSLSocket) ->
ssl:send(SSLSocket, "OK"),
- {ok, [{sni_hostname, Hostname}]} = ssl:connection_information(SSLSocket, [sni_hostname]),
- Hostname.
+ case ssl:connection_information(SSLSocket, [sni_hostname]) of
+ {ok, [{sni_hostname, Hostname}]} ->
+ Hostname;
+ {ok, []} ->
+ undefined
+ end.
rdnPart([[#'AttributeTypeAndValue'{type=Type, value=Value} | _] | _], Type) ->
Value;
diff --git a/lib/ssl/test/ssl_test_lib.erl b/lib/ssl/test/ssl_test_lib.erl
index f25f6f9425..b0495cb5ac 100644
--- a/lib/ssl/test/ssl_test_lib.erl
+++ b/lib/ssl/test/ssl_test_lib.erl
@@ -818,7 +818,17 @@ rsa_suites(CounterPart) ->
(_) ->
false
end,
- ssl:cipher_suites()).
+ common_ciphers(CounterPart)).
+
+common_ciphers(crypto) ->
+ ssl:cipher_suites();
+common_ciphers(openssl) ->
+ OpenSslSuites =
+ string:tokens(string:strip(os:cmd("openssl ciphers"), right, $\n), ":"),
+ [ssl_cipher:erl_suite_definition(S)
+ || S <- ssl_cipher:suites(tls_record:highest_protocol_version([])),
+ lists:member(ssl_cipher:openssl_suite_name(S), OpenSslSuites)
+ ].
rsa_non_signed_suites() ->
lists:filter(fun({rsa, _, _}) ->
@@ -971,6 +981,10 @@ rc4_suites(Version) ->
Suites = ssl_cipher:rc4_suites(Version),
ssl_cipher:filter_suites(Suites).
+des_suites(Version) ->
+ Suites = ssl_cipher:des_suites(Version),
+ ssl_cipher:filter_suites(Suites).
+
pem_to_der(File) ->
{ok, PemBin} = file:read_file(File),
public_key:pem_decode(PemBin).
@@ -1158,23 +1172,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.
@@ -1192,13 +1210,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([]),
@@ -1208,7 +1228,7 @@ filter_suites(Ciphers0) ->
++ ssl_cipher:srp_suites()
++ ssl_cipher:rc4_suites(Version),
Supported1 = ssl_cipher:filter_suites(Supported0),
- Supported2 = [ssl:suite_definition(S) || S <- Supported1],
+ Supported2 = [ssl_cipher:erl_suite_definition(S) || S <- Supported1],
[Cipher || Cipher <- Ciphers0, lists:member(Cipher, Supported2)].
-define(OPENSSL_QUIT, "Q\n").
@@ -1243,3 +1263,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..6934d7f851 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) ->
@@ -1269,8 +1268,12 @@ client_check_result(Port, DataExpected) ->
send_and_hostname(SSLSocket) ->
ssl:send(SSLSocket, "OK"),
- {ok, [{sni_hostname, Hostname}]} = ssl:connection_information(SSLSocket, [sni_hostname]),
- Hostname.
+ case ssl:connection_information(SSLSocket, [sni_hostname]) of
+ {ok, []} ->
+ undefined;
+ {ok, [{sni_hostname, Hostname}]} ->
+ Hostname
+ end.
erlang_server_openssl_client_sni_test(Config, SNIHostname, ExpectedSNIHostname, ExpectedCN) ->
ct:log("Start running handshake, Config: ~p, SNIHostname: ~p, ExpectedSNIHostname: ~p, ExpectedCN: ~p", [Config, SNIHostname, ExpectedSNIHostname, ExpectedCN]),
@@ -1280,14 +1283,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 +1312,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 +1339,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 ++ "",
+ Exe = "openssl",
+ Args = ["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]),
+ OpenSslPort = ssl_test_lib:portable_open_port(Exe, Args),
ssl_test_lib:wait_for_openssl_server(Port),
@@ -1399,13 +1401,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 +1447,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 +1482,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 +1513,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,
+ 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],
- 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),
@@ -1546,17 +1552,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 +1578,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 +1616,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 +1645,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 +1682,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 +1754,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 +1765,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 +1772,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 d65bdf6983..f5f4b25b23 100644
--- a/lib/ssl/test/ssl_upgrade_SUITE.erl
+++ b/lib/ssl/test/ssl_upgrade_SUITE.erl
@@ -40,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) ->
diff --git a/lib/ssl/vsn.mk b/lib/ssl/vsn.mk
index 4587c448f6..47cb2909fb 100644
--- a/lib/ssl/vsn.mk
+++ b/lib/ssl/vsn.mk
@@ -1 +1 @@
-SSL_VSN = 7.1
+SSL_VSN = 7.3