aboutsummaryrefslogtreecommitdiffstats
path: root/lib/ssl
diff options
context:
space:
mode:
Diffstat (limited to 'lib/ssl')
-rw-r--r--lib/ssl/doc/src/Makefile2
-rw-r--r--lib/ssl/doc/src/notes.xml75
-rw-r--r--lib/ssl/doc/src/ssl.xml1135
-rw-r--r--lib/ssl/doc/src/ssl_app.xml87
-rw-r--r--lib/ssl/doc/src/ssl_crl_cache.xml53
-rw-r--r--lib/ssl/doc/src/ssl_crl_cache_api.xml52
-rw-r--r--lib/ssl/doc/src/ssl_distribution.xml241
-rw-r--r--lib/ssl/doc/src/ssl_introduction.xml53
-rw-r--r--lib/ssl/doc/src/ssl_protocol.xml130
-rw-r--r--lib/ssl/doc/src/ssl_session_cache_api.xml121
-rw-r--r--lib/ssl/doc/src/usersguide.xml5
-rw-r--r--lib/ssl/doc/src/using_ssl.xml103
-rw-r--r--lib/ssl/src/dtls_connection.erl4
-rw-r--r--lib/ssl/src/dtls_handshake.erl4
-rw-r--r--lib/ssl/src/ssl.app.src4
-rw-r--r--lib/ssl/src/ssl.erl119
-rw-r--r--lib/ssl/src/ssl_alert.erl2
-rw-r--r--lib/ssl/src/ssl_alert.hrl3
-rw-r--r--lib/ssl/src/ssl_api.hrl2
-rw-r--r--lib/ssl/src/ssl_connection.erl82
-rw-r--r--lib/ssl/src/ssl_connection.hrl5
-rw-r--r--lib/ssl/src/ssl_crl_cache_api.erl6
-rw-r--r--lib/ssl/src/ssl_handshake.erl130
-rw-r--r--lib/ssl/src/ssl_handshake.hrl9
-rw-r--r--lib/ssl/src/ssl_internal.hrl14
-rw-r--r--lib/ssl/src/tls_connection.erl78
-rw-r--r--lib/ssl/src/tls_handshake.erl21
-rw-r--r--lib/ssl/test/Makefile2
-rw-r--r--lib/ssl/test/erl_make_certs.erl8
-rw-r--r--lib/ssl/test/make_certs.erl2
-rw-r--r--lib/ssl/test/ssl_alpn_handshake_SUITE.erl414
-rw-r--r--lib/ssl/test/ssl_handshake_SUITE.erl59
-rw-r--r--lib/ssl/test/ssl_npn_handshake_SUITE.erl8
-rw-r--r--lib/ssl/test/ssl_sni_SUITE.erl168
-rw-r--r--lib/ssl/test/ssl_test_lib.erl19
-rw-r--r--lib/ssl/test/ssl_to_openssl_SUITE.erl453
36 files changed, 2663 insertions, 1010 deletions
diff --git a/lib/ssl/doc/src/Makefile b/lib/ssl/doc/src/Makefile
index cfbf98f6e3..143756bd39 100644
--- a/lib/ssl/doc/src/Makefile
+++ b/lib/ssl/doc/src/Makefile
@@ -37,7 +37,7 @@ RELSYSDIR = $(RELEASE_PATH)/lib/$(APPLICATION)-$(VSN)
# Target Specs
# ----------------------------------------------------
XML_APPLICATION_FILES = refman.xml
-XML_REF3_FILES = ssl.xml ssl_crl_cache.xml ssl_crl_cache.xml ssl_session_cache_api.xml
+XML_REF3_FILES = ssl.xml ssl_crl_cache.xml ssl_crl_cache_api.xml ssl_session_cache_api.xml
XML_REF6_FILES = ssl_app.xml
XML_PART_FILES = release_notes.xml usersguide.xml
diff --git a/lib/ssl/doc/src/notes.xml b/lib/ssl/doc/src/notes.xml
index 4349e5a456..352563700b 100644
--- a/lib/ssl/doc/src/notes.xml
+++ b/lib/ssl/doc/src/notes.xml
@@ -25,7 +25,80 @@
<file>notes.xml</file>
</header>
<p>This document describes the changes made to the SSL application.</p>
- <section><title>SSL 5.3.8</title>
+ <section><title>SSL 6.0</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p>
+ Exclude self-signed trusted anchor certificates from
+ certificate prospective certification path according to
+ RFC 3280.</p>
+ <p>
+ This will avoid some unnecessary certificate processing.</p>
+ <p>
+ Own Id: OTP-12449</p>
+ </item>
+ </list>
+ </section>
+
+
+ <section><title>Improvements and New Features</title>
+ <list>
+ <item>
+ <p>
+ Separate client and server session cache internally.</p>
+ <p>
+ Avoid session table growth when client starts many
+ connections in such a manner that many connections are
+ started before session reuse is possible. Only save a new
+ session in client if there is no equivalent session
+ already stored.</p>
+ <p>
+ Own Id: OTP-11365</p>
+ </item>
+ <item>
+ <p>
+ The PEM cache is now validated by a background process,
+ instead of always keeping it if it is small enough and
+ clearing it otherwise. That strategy required that small
+ caches where cleared by API function if a file changes on
+ disk.</p>
+ <p>
+ However export the API function to clear the cache as it
+ may still be useful.</p>
+ <p>
+ Own Id: OTP-12391</p>
+ </item>
+ <item>
+ <p>
+ Add padding check for TLS-1.0 to remove Poodle
+ vulnerability from TLS 1.0, also add the option
+ padding_check. This option only affects TLS-1.0
+ connections and if set to false it disables the block
+ cipher padding check to be able to interoperate with
+ legacy software.</p>
+ <p>
+ *** POTENTIAL INCOMPATIBILITY ***</p>
+ <p>
+ Own Id: OTP-12420</p>
+ </item>
+ <item>
+ <p>
+ Add support for TLS_FALLBACK_SCSV used to prevent
+ undesired TLS version downgrades. If used by a client
+ that is vulnerable to the POODLE attack, and the server
+ also supports TLS_FALLBACK_SCSV, the attack can be
+ prevented.</p>
+ <p>
+ Own Id: OTP-12458</p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
+<section><title>SSL 5.3.8</title>
<section><title>Fixed Bugs and Malfunctions</title>
<list>
diff --git a/lib/ssl/doc/src/ssl.xml b/lib/ssl/doc/src/ssl.xml
index 47100c0d81..18d98e5efb 100644
--- a/lib/ssl/doc/src/ssl.xml
+++ b/lib/ssl/doc/src/ssl.xml
@@ -21,243 +21,280 @@
</legalnotice>
<title>ssl</title>
+ <prepared></prepared>
+ <docno></docno>
+ <date></date>
+ <rev></rev>
<file>ssl.xml</file>
</header>
<module>ssl</module>
<modulesummary>Interface Functions for Secure Socket Layer</modulesummary>
<description>
- <p>This module contains interface functions to the Secure Socket
- Layer.
- </p>
+ <p>This module contains interface functions for the SSL.</p>
</description>
<section>
<title>SSL</title>
<list type="bulleted">
- <item>ssl requires the crypto and public_key applications.</item>
+ <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>
+ 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 may be configured.</item>
- <item>Ephemeral Diffie-Hellman cipher suites are supported
+ 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 crypto
- supports it and named curves are used.
+ <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 spec so there is not any
- real motivation to implement them.</item>
+ 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
- yet. </item>
- <item>Support for 'Server Name Indication' extension client side
- (RFC 6066 section 3).</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>COMMON DATA TYPES</title>
- <p>The following data types are used in the functions below:
- </p>
+ <title>DATA TYPES</title>
+ <p>The following data types are used in the functions for SSL:</p>
- <p><c>boolean() = true | false</c></p>
+ <taglist>
- <p><c>option() = socketoption() | ssloption() | transportoption()</c></p>
+ <tag><c>boolean() =</c></tag>
+ <item><p><c>true | false</c></p></item>
- <p><c>socketoption() = proplists:property() - The default socket options are
- [{mode,list},{packet, 0},{header, 0},{active, true}].
- </c></p>
+ <tag><c>option() =</c></tag>
+ <item><p><c>socketoption() | ssloption() | transportoption()</c></p>
+ </item>
- <p>For valid options
- see <seealso marker="kernel:inet">inet(3)</seealso> and
- <seealso marker="kernel:gen_tcp">gen_tcp(3)</seealso>.
- </p>
-
- <p><marker id="type-ssloption"></marker><c>ssloption() = {verify, verify_type()} |
- {verify_fun, {fun(), term()}} |
- {fail_if_no_peer_cert, boolean()}
- {depth, integer()} |
- {cert, der_encoded()}| {certfile, path()} |
- {key, {'RSAPrivateKey'| 'DSAPrivateKey' | 'ECPrivateKey' |'PrivateKeyInfo', der_encoded()}} |
- {keyfile, path()} | {password, string()} |
- {cacerts, [der_encoded()]} | {cacertfile, path()} |
- |{dh, der_encoded()} | {dhfile, path()} | {ciphers, ciphers()} |
- {user_lookup_fun, {fun(), term()}}, {psk_identity, string()}, {srp_identity, {string(), string()}} |
- {ssl_imp, ssl_imp()} | {reuse_sessions, boolean()} | {reuse_session, fun()}
- {next_protocols_advertised, [binary()]} |
- {client_preferred_next_protocols, {client | server, [binary()]} | {client | server, [binary()], binary()}} |
- {log_alert, boolean()} | {server_name_indication, hostname() | disable}
- </c></p>
-
- <p><c>transportoption() = {cb_info, {CallbackModule :: atom(), DataTag :: atom(), ClosedTag :: atom(), ErrTag:atom()}}
- - defaults to {gen_tcp, tcp, tcp_closed, tcp_error}. Can be used to customize
- the transport layer. The callback module must implement a reliable transport
- protocol and behave as gen_tcp and in addition have functions corresponding to
- inet:setopts/2, inet:getopts/2, inet:peername/1, inet:sockname/1 and inet:port/1.
- The callback gen_tcp is treated specially and will call inet directly.
- </c></p>
-
- <p><c>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; CallbackModule =
- atom()</c>
- </p> <p><c>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; DataTag =
- atom() - tag used in socket data message.</c></p>
- <p><c>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ClosedTag = atom() - tag used in
- socket close message.</c></p>
-
- <p><c>verify_type() = verify_none | verify_peer</c></p>
-
- <p><c>path() = string() - representing a file path.</c></p>
+ <tag><c>socketoption() =</c></tag>
+ <item><p><c>proplists:property()</c></p>
+ <p>The default socket options are
+ <c>[{mode,list},{packet, 0},{header, 0},{active, true}]</c>.</p>
+ <p>For valid options, see the
+ <seealso marker="kernel:inet">inet(3)</seealso> and
+ <seealso marker="kernel:gen_tcp">gen_tcp(3)</seealso> manual pages
+ in Kernel.</p></item>
- <p><c>der_encoded() = binary() -Asn1 DER encoded entity as an erlang binary.</c></p>
-
- <p><c>host() = hostname() | ipaddress()</c></p>
-
- <p><c>hostname() = string()</c></p>
-
- <p><c>
- ip_address() = {N1,N2,N3,N4} % IPv4
- | {K1,K2,K3,K4,K5,K6,K7,K8} % IPv6 </c></p>
+ <tag><marker id="type-ssloption"></marker><c>ssloption() =</c></tag>
+ <item>
+ <p><c>{verify, verify_type()}</c></p>
+ <p><c>| {verify_fun, {fun(), term()}}</c></p>
+ <p><c>| {fail_if_no_peer_cert, boolean()} {depth, integer()}</c></p>
+ <p><c>| {cert, public_key:der_encoded()}</c></p>
+ <p><c>| {certfile, path()}</c></p>
+ <p><c>| {key, {'RSAPrivateKey'| 'DSAPrivateKey' | 'ECPrivateKey'
+ | 'PrivateKeyInfo', public_key:der_encoded()}}</c></p>
+ <p><c>| {keyfile, path()}</c></p>
+ <p><c>| {password, string()}</c></p>
+ <p><c>| {cacerts, [public_key:der_encoded()]}</c></p>
+ <p><c>| {cacertfile, path()}</c></p>
+ <p><c>| {dh, public_key:der_encoded()}</c></p>
+ <p><c>| {dhfile, path()}</c></p>
+ <p><c>| {ciphers, ciphers()}</c></p>
+ <p><c>| {user_lookup_fun, {fun(), term()}}, {psk_identity, string()},
+ {srp_identity, {string(), string()}}</c></p>
+ <p><c>| {reuse_sessions, boolean()}</c></p>
+ <p><c>| {reuse_session, fun()} {next_protocols_advertised, [binary()]}</c></p>
+ <p><c>| {client_preferred_next_protocols, {client | server,
+ [binary()]} | {client | server, [binary()], binary()}}</c></p>
+ <p><c>| {log_alert, boolean()}</c></p>
+ <p><c>| {server_name_indication, hostname() | disable}</c></p>
+ <p><c>| {sni_hosts, [{hostname(), ssloptions()}]}</c></p>
+ <p><c>| {sni_fun, SNIfun::fun()}</c></p>
+ </item>
+
+ <tag><c>transportoption() =</c></tag>
+ <item><p><c>{cb_info, {CallbackModule::atom(), DataTag::atom(),
+
+ ClosedTag::atom(), ErrTag:atom()}}</c></p>
+ <p>Defaults to <c>{gen_tcp, tcp, tcp_closed, tcp_error}</c>. Can be used
+ to customize the transport layer. The callback module must implement a
+ reliable transport protocol, behave as <c>gen_tcp</c>, and have functions
+ corresponding to <c>inet:setopts/2</c>, <c>inet:getopts/2</c>,
+ <c>inet:peername/1</c>, <c>inet:sockname/1</c>, and <c>inet:port/1</c>.
+ The callback <c>gen_tcp</c> is treated specially and calls <c>inet</c>
+ directly.</p>
+ <taglist>
+ <tag><c>CallbackModule =</c></tag>
+ <item><p><c>atom()</c></p></item>
+ <tag><c>DataTag =</c></tag>
+ <item><p><c>atom()</c></p>
+ <p>Used in socket data message.</p></item>
+ <tag><c>ClosedTag =</c></tag>
+ <item><p><c>atom()</c></p>
+ <p>Used in socket close message.</p></item>
+ </taglist>
+ </item>
- <p><c>sslsocket() - opaque to the user. </c></p>
-
- <p><c>protocol() = sslv3 | tlsv1 | 'tlsv1.1' | 'tlsv1.2' </c></p>
-
- <p><c>ciphers() = [ciphersuite()] | string() (according to old API)</c></p>
-
- <p><c>ciphersuite() =
- {key_exchange(), cipher(), hash()}</c></p>
-
- <p><c>key_exchange() = rsa | dhe_dss | dhe_rsa | dh_anon
- | psk | dhe_psk | rsa_psk | srp_anon | srp_dss | srp_rsa
- | ecdh_anon | ecdh_ecdsa | ecdhe_ecdsa | ecdh_rsa | ecdhe_rsa
- </c></p>
+ <tag><c>verify_type() =</c></tag>
+ <item><p><c>verify_none | verify_peer</c></p></item>
+
+ <tag><c>path() =</c></tag>
+ <item><p><c>string()</c></p>
+ <p>Represents a file path.</p></item>
+
+ <tag><c>public_key:der_encoded() =</c></tag>
+ <item><p><c>binary()</c></p>
+ <p>ASN.1 DER-encoded entity as an Erlang binary.</p></item>
+
+ <tag><c>host() =</c></tag>
+ <item><p><c>hostname() | ipaddress()</c></p></item>
+
+ <tag><c>hostname() =</c></tag>
+ <item><p><c>string()</c></p></item>
+
+ <tag><c>ip_address() =</c></tag>
+ <item><p><c>{N1,N2,N3,N4} % IPv4 | {K1,K2,K3,K4,K5,K6,K7,K8} % IPv6
+ </c></p></item>
- <p><c>cipher() = rc4_128 | des_cbc | '3des_ede_cbc'
- | aes_128_cbc | aes_256_cbc | aes_128_gcm | aes_256_gcm </c></p>
+ <tag><c>sslsocket() =</c></tag>
+ <item><p>opaque()</p></item>
- <p> <c>hash() = md5 | sha
- </c></p>
+ <tag><c>protocol() =</c></tag>
+ <item><p><c>sslv3 | tlsv1 | 'tlsv1.1' | 'tlsv1.2'</c></p></item>
- <p><c>prf_random() = client_random | server_random
- </c></p>
+ <tag><c>ciphers() =</c></tag>
+ <item><p><c>= [ciphersuite()] | string()</c></p>
+ <p>According to old API.</p></item>
- <p><c>srp_param_type() = srp_1024 | srp_1536 | srp_2048 | srp_3072
- | srp_4096 | srp_6144 | srp_8192</c></p>
+ <tag><c>ciphersuite() =</c></tag>
+ <item><p><c>{key_exchange(), cipher(), hash()}</c></p></item>
+ <tag><c>key_exchange()=</c></tag>
+ <item><p><c>rsa | dhe_dss | dhe_rsa | dh_anon | psk | dhe_psk
+ | rsa_psk | srp_anon | srp_dss | srp_rsa | ecdh_anon | ecdh_ecdsa
+ | ecdhe_ecdsa | ecdh_rsa | ecdhe_rsa</c></p></item>
+
+ <tag><c>cipher() =</c></tag>
+ <item><p><c>rc4_128 | des_cbc | '3des_ede_cbc'
+ | 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>
+
+ <tag><c>prf_random() =</c></tag>
+ <item><p><c>client_random | server_random</c></p></item>
+
+ <tag><c>srp_param_type() =</c></tag>
+ <item><p><c>srp_1024 | srp_1536 | srp_2048 | srp_3072
+ | srp_4096 | srp_6144 | srp_8192</c></p></item>
+
+ <tag><c>SNIfun::fun()</c></tag>
+ <item><p><c>= fun(ServerName :: string()) -> ssloptions()</c></p></item>
+
+ </taglist>
</section>
<section>
<title>SSL OPTION DESCRIPTIONS - COMMON for SERVER and CLIENT</title>
- <p>Options described here are options that are have the same
- meaning in the client and the server.
- </p>
+ <p>The following options have the same meaning in the client and
+ the server:</p>
<taglist>
- <tag>{cert, der_encoded()}</tag>
- <item> The DER encoded users certificate. If this option
- is supplied it will override the certfile option.</item>
+ <tag><c>{cert, public_key:der_encoded()}</c></tag>
+ <item><p>The DER-encoded users certificate. If this option
+ is supplied, it overrides option <c>certfile</c>.</p></item>
- <tag>{certfile, path()}</tag>
- <item>Path to a file containing the user's PEM encoded certificate.</item>
+ <tag><c>{certfile, path()}</c></tag>
+ <item><p>Path to a file containing the user certificate.</p></item>
- <tag>{key, {'RSAPrivateKey'| 'DSAPrivateKey' | 'ECPrivateKey' |'PrivateKeyInfo', der_encoded()}}</tag>
- <item> The DER encoded users private key. If this option
- is supplied it will override the keyfile option.</item>
+ <tag><c>{key, {'RSAPrivateKey'| 'DSAPrivateKey' | 'ECPrivateKey'
+ |'PrivateKeyInfo', public_key:der_encoded()}}</c></tag>
+ <item><p>The DER-encoded user's private key. If this option
+ is supplied, it overrides option <c>keyfile</c>.</p></item>
- <tag>{keyfile, path()}</tag>
- <item>Path to file containing user's
- private PEM encoded key. As PEM-files may contain several
- entries this option defaults to the same file as given by
- certfile option.</item>
-
- <tag>{password, string()}</tag>
- <item>String containing the user's password.
- Only used if the private keyfile is password protected.
- </item>
-
- <tag>{cacerts, [der_encoded()]}</tag>
- <item> The DER encoded trusted certificates. If this option
- is supplied it will override the cacertfile option.</item>
-
- <tag>{ciphers, ciphers()}</tag>
- <item>The cipher suites that should be supported. The function
+ <tag><c>{keyfile, path()}</c></tag>
+ <item><p>Path to the file containing the user's
+ private PEM-encoded key. As PEM-files can contain several
+ entries, this option defaults to the same file as given by
+ option <c>certfile</c>.</p></item>
+
+ <tag><c>{password, string()}</c></tag>
+ <item><p>String containing the user's password. Only used if the
+ private keyfile is password-protected.</p></item>
+
+ <tag><c>{ciphers, ciphers()}</c></tag>
+ <item><p>Supported cipher suites. The function
<c>cipher_suites/0</c> can be used to find all ciphers that are
- supported by default. <c>cipher_suites(all)</c> may be called
- to find all available cipher suites.
- Pre-Shared Key (<url href="http://www.ietf.org/rfc/rfc4279.txt">RFC 4279</url> and
+ supported by default. <c>cipher_suites(all)</c> can be called
+ to find all available cipher suites. Pre-Shared Key
+ (<url href="http://www.ietf.org/rfc/rfc4279.txt">RFC 4279</url> and
<url href="http://www.ietf.org/rfc/rfc5487.txt">RFC 5487</url>),
- Secure Remote Password (<url href="http://www.ietf.org/rfc/rfc5054.txt">RFC 5054</url>)
+ Secure Remote Password
+ (<url href="http://www.ietf.org/rfc/rfc5054.txt">RFC 5054</url>), RC4 cipher suites,
and anonymous cipher suites only work if explicitly enabled by
- this option and they are supported/enabled by the peer also.
- Note that anonymous cipher suites are supported for testing purposes
- only and should not be used when security matters.
- </item>
-
- <tag>{ssl_imp, new | old}</tag>
- <item>No longer has any meaning as the old implementation has
- been removed, it will be ignored.
- </item>
-
- <tag>{secure_renegotiate, boolean()}</tag>
- <item>Specifies if to reject renegotiation attempt that does
- not live up to <url href="http://www.ietf.org/rfc/rfc5746.txt">RFC 5746</url>. By default secure_renegotiate is
- set to false i.e. secure renegotiation will be used if possible
- but it will fallback to unsecure renegotiation if the peer
- does not support <url href="http://www.ietf.org/rfc/rfc5746.txt">RFC 5746</url>.
+ this option; they are supported/enabled by the peer also.
+ Anonymous cipher suites are supported for testing purposes
+ only and are not be used when security matters.</p></item>
+
+ <tag><c>{secure_renegotiate, boolean()}</c></tag>
+ <item><p>Specifies if to reject renegotiation attempt that does
+ not live up to
+ <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
+ does not support
+ <url href="http://www.ietf.org/rfc/rfc5746.txt">RFC 5746</url>.</p>
</item>
- <tag>{depth, integer()}</tag>
- <item>
- The depth is the maximum number of non-self-issued
- intermediate certificates that may follow the peer certificate
- in a valid certification path. So if depth is 0 the PEER must
- be signed by the trusted ROOT-CA directly, if 1 the path can
- be PEER, CA, ROOT-CA, if it is 2 PEER, CA, CA, ROOT-CA and so
- on. The default value is 1.
- </item>
+ <tag><c>{depth, integer()}</c></tag>
+ <item><p>Maximum number of non-self-issued
+ intermediate certificates that can follow the peer certificate
+ in a valid certification path. So, if depth is 0 the PEER must
+ be signed by the trusted ROOT-CA directly; if 1 the path can
+ be PEER, CA, ROOT-CA; if 2 the path can be PEER, CA, CA,
+ ROOT-CA, and so on. The default value is 1.</p></item>
- <tag>{verify_fun, {Verifyfun :: fun(), InitialUserState :: term()}}</tag>
- <item>
- <p>The verification fun should be defined as:</p>
+ <tag><c>{verify_fun, {Verifyfun :: fun(), InitialUserState ::
+ term()}}</c></tag>
+ <item><p>The verification fun is to be defined as follows:</p>
<code>
-fun(OtpCert :: #'OTPCertificate'{}, Event :: {bad_cert, Reason :: atom() | {revoked, atom()}} |
+fun(OtpCert :: #'OTPCertificate'{}, Event :: {bad_cert, Reason :: atom() | {revoked,
+atom()}} |
{extension, #'Extension'{}}, InitialUserState :: term()) ->
{valid, UserState :: term()} | {valid_peer, UserState :: term()} |
{fail, Reason :: term()} | {unknown, UserState :: term()}.
</code>
- <p>The verify fun will be called during the X509-path
- validation when an error or an extension unknown to the ssl
- application is encountered. Additionally it will be called
+ <p>The verification fun is called during the X509-path
+ validation when an error or an extension unknown to the SSL
+ application is encountered. It is also called
when a certificate is considered valid by the path validation
to allow access to each certificate in the path to the user
- application. Note that it will differentiate between the
- peer certificate and CA certificates by using valid_peer or
- valid as the second argument to the verify fun. See <seealso
- marker="public_key:cert_records">the public_key User's
- Guide</seealso> for definition of #'OTPCertificate'{} and
- #'Extension'{}.</p>
-
- <p>If the verify callback fun returns {fail, Reason}, the
- verification process is immediately stopped and an alert is
- sent to the peer and the TLS/SSL handshake is terminated. If
- the verify callback fun returns {valid, UserState}, the
- verification process is continued. If the verify callback fun
- always returns {valid, UserState}, the TLS/SSL handshake will
- not be terminated with respect to verification failures and
- the connection will be established. If called with an
- extension unknown to the user application, the return value
- {unknown, UserState} should be used.</p>
-
- <p>The default verify_fun option in verify_peer mode:</p>
+ application. It differentiates between the peer
+ certificate and the CA certificates by using <c>valid_peer</c> or
+ <c>valid</c> as second argument to the verification fun. See the
+ <seealso marker="public_key:public_key_records">public_key User's
+ Guide</seealso> for definition of <c>#'OTPCertificate'{}</c> and
+ <c>#'Extension'{}</c>.</p>
+
+ <list type="bulleted">
+ <item><p>If the verify callback fun returns <c>{fail, Reason}</c>,
+ the verification process is immediately stopped, an alert is
+ sent to the peer, and the TLS/SSL handshake terminates.</p></item>
+ <item><p>If the verify callback fun returns <c>{valid, UserState}</c>,
+ the verification process continues.</p></item>
+ <item><p>If the verify callback fun always returns
+ <c>{valid, UserState}</c>, the TLS/SSL handshake does not
+ 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>
+ </list>
+
+ <p>Default option <c>verify_fun</c> in <c>verify_peer mode</c>:</p>
<code>
{fun(_,{bad_cert, _} = Reason, _) ->
@@ -271,7 +308,7 @@ fun(OtpCert :: #'OTPCertificate'{}, Event :: {bad_cert, Reason :: atom() | {revo
end, []}
</code>
- <p>The default verify_fun option in verify_none mode:</p>
+ <p>Default option <c>verify_fun</c> in mode <c>verify_none</c>:</p>
<code>
{fun(_,{bad_cert, _}, UserState) ->
@@ -285,28 +322,31 @@ fun(OtpCert :: #'OTPCertificate'{}, Event :: {bad_cert, Reason :: atom() | {revo
end, []}
</code>
- <p>Possible path validation errors are given on the form {bad_cert, Reason} where Reason is:</p>
+ <p>The possible path validation errors are given on form
+ <c>{bad_cert, Reason}</c> where <c>Reason</c> is:</p>
<taglist>
- <tag>unknown_ca</tag>
- <item>No trusted CA was found in the trusted store. The trusted CA is
- normally a so called ROOT CA that is a self-signed cert. Trust may
- be claimed for an intermediat CA (trusted anchor does not have to be self signed
- according to X-509) by using the option <c>partial_chain</c></item>
-
- <tag>selfsigned_peer</tag>
- <item>The chain consisted only of one self-signed certificate.</item>
-
- <tag>PKIX X-509-path validation error</tag>
- <item> Possible such reasons see <seealso
- marker="public_key:public_key#pkix_path_validation-3"> public_key:pkix_path_validation/3 </seealso></item>
+ <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
+ self-signed according to X-509) by using option <c>partial_chain</c>.</p>
+ </item>
+
+ <tag><c>selfsigned_peer</c></tag>
+ <item><p>The chain consisted only of one self-signed certificate.</p></item>
+
+ <tag><c>PKIX X-509-path validation error</c></tag>
+ <item><p>For possible reasons, see <seealso
+marker="public_key:public_key#pkix_path_validation-3">public_key:pkix_path_validation/3</seealso>
+ </p></item>
</taglist>
</item>
- <tag>{crl_check, boolean() | peer | best_effort }</tag>
+ <tag><c>{crl_check, boolean() | peer | best_effort }</c></tag>
<item>
Perform CRL (Certificate Revocation List) verification
- <seealso marker="public_key:public_key#pkix_crl_validate-3">
+ <seealso marker="public_key:public_key#pkix_crls_validate-3">
(public_key:pkix_crls_validate/3)</seealso> on all the certificates during the path validation
<seealso
marker="public_key:public_key#pkix_path_validation-3">(public_key:pkix_path_validation/3)
@@ -322,49 +362,48 @@ fun(OtpCert :: #'OTPCertificate'{}, Event :: {bad_cert, Reason :: atom() | {revo
<p>The CA certificates specified for the connection will be used to
construct the certificate chain validating the CRLs.</p>
- <p>The CRLs will be fetched from a local or external cache
+ <p>The CRLs will be fetched from a local or external cache see
<seealso marker="ssl:ssl_crl_cache_api">ssl_crl_cache_api(3)</seealso>.</p>
</item>
- <tag>{crl_cache, {Module :: atom(), {DbHandle :: internal | term(), Args :: list()}}}</tag>
+ <tag><c>{crl_cache, {Module :: atom(), {DbHandle :: internal | term(), Args :: list()}}}</c></tag>
<item>
<p>Module defaults to ssl_crl_cache with <c> DbHandle </c> internal and an
empty argument list. The following arguments may be specified for the internal cache.</p>
<taglist>
- <tag>{http, timeout()}</tag>
- <item>
+ <tag><c>{http, timeout()}</c></tag>
+ <item><p>
Enables fetching of CRLs specified as http URIs in<seealso
- marker="public_key:cert_records"> X509 cerificate extensions.</seealso>
- Requires the OTP inets application.
+ marker="public_key:public_key_records"> X509 cerificate extensions.</seealso>
+ Requires the OTP inets application.</p>
</item>
</taglist>
</item>
-
- <tag>{partial_chain, fun(Chain::[DerCert]) -> {trusted_ca, DerCert} | unknown_ca </tag>
-
- <item>
- Claim an intermediat CA in the chain as trusted. TLS will then perform the public_key:pkix_path_validation/3
- with the selected CA as trusted anchor and the rest of the chain.
- </item>
-
- <tag>{versions, [protocol()]}</tag>
- <item>TLS protocol versions that will be supported by started clients and servers.
- This option overrides the application environment option <c>protocol_version</c>. If the
- environment option is not set it defaults to all versions, except SSL-3.0, supported by the SSL application. See also
- <seealso marker="ssl:ssl_app">ssl(6)</seealso>
- </item>
- <tag>{hibernate_after, integer()|undefined}</tag>
- <item>When an integer-value is specified, the <c>ssl_connection</c>
- will go into hibernation after the specified number of milliseconds
- of inactivity, thus reducing its memory footprint. When
- <c>undefined</c> is specified (this is the default), the process
- will never go into hibernation.
- </item>
+ <tag><c>{partial_chain, fun(Chain::[DerCert]) -> {trusted_ca, DerCert} |
+ unknown_ca }</c></tag>
+ <item><p>Claim an intermediate CA in the chain as trusted. TLS then
+ performs <seealso
+ marker="public_key:public_key#pkix_path_validation-3">public_key:pkix_path_validation/3</seealso>
+ with the selected CA as trusted anchor and the rest of the chain.</p></item>
+
+ <tag><c>{versions, [protocol()]}</c></tag>
+ <item><p>TLS protocol versions supported by started clients and servers.
+ This option overrides the application environment option
+ <c>protocol_version</c>. If the environment option is not set, it defaults
+ to all versions, except SSL-3.0, supported by the SSL application.
+ See also <seealso marker="ssl:ssl_app">ssl(6).</seealso></p></item>
+
+ <tag><c>{hibernate_after, integer()|undefined}</c></tag>
+ <item><p>When an integer-value is specified, <c>ssl_connection</c>
+ goes into hibernation after the specified number of milliseconds
+ of inactivity, thus reducing its memory footprint. When
+ <c>undefined</c> is specified (this is the default), the process
+ never goes into hibernation.</p></item>
+
+ <tag><c>{user_lookup_fun, {Lookupfun :: fun(), UserState :: term()}}</c></tag>
+ <item><p>The lookup fun is to defined as follows:</p>
- <tag>{user_lookup_fun, {Lookupfun :: fun(), UserState :: term()}}</tag>
- <item>
- <p>The lookup fun should be defined as:</p>
<code>
fun(psk, PSKIdentity ::string(), UserState :: term()) ->
{ok, SharedSecret :: binary()} | error;
@@ -372,104 +411,121 @@ fun(srp, Username :: string(), UserState :: term()) ->
{ok, {SRPParams :: srp_param_type(), Salt :: binary(), DerivedKey :: binary()}} | error.
</code>
- <p>For Pre-Shared Key (PSK) cipher suites, the lookup fun will
- be called by the client and server to determine the shared
- secret. When called by the client, PSKIdentity will be set to the
- hint presented by the server or undefined. When called by the
- server, PSKIdentity is the identity presented by the client.
- </p>
-
- <p>For Secure Remote Password (SRP), the fun will only be used by the server to obtain
- parameters that it will use to generate its session keys. <c>DerivedKey</c> should be
- derived according to <url href="http://tools.ietf.org/html/rfc2945#section-3"> RFC 2945</url> and
- <url href="http://tools.ietf.org/html/rfc5054#section-2.4"> RFC 5054</url>:
- <c>crypto:sha([Salt, crypto:sha([Username, &lt;&lt;$:&gt;&gt;, Password])]) </c>
+ <p>For Pre-Shared Key (PSK) cipher suites, the lookup fun is
+ called by the client and server to determine the shared
+ secret. When called by the client, <c>PSKIdentity</c> is set to the
+ hint presented by the server or to undefined. When called by the
+ server, <c>PSKIdentity</c> is the identity presented by the client.</p>
+
+ <p>For Secure Remote Password (SRP), the fun is only used by the server to
+ obtain parameters that it uses to generate its session keys.
+ <c>DerivedKey</c> is to be derived according to
+ <url href="http://tools.ietf.org/html/rfc2945#section-3"> RFC 2945</url> and
+ <url href="http://tools.ietf.org/html/rfc5054#section-2.4"> RFC 5054</url>:
+ <c>crypto:sha([Salt, crypto:sha([Username, &lt;&lt;$:&gt;&gt;, Password])])</c>
</p>
</item>
- <tag>{padding_check, boolean()}</tag>
- <item>
- <p> This option only affects TLS-1.0 connections.
- If set to false it disables the block cipher padding check
- to be able to interoperate with legacy software.
- </p>
-
- <warning><p> Using this option makes TLS vulnerable to
- the Poodle attack</p></warning>
-
- </item>
-
+ <tag><c>{padding_check, boolean()}</c></tag>
+ <item><p>Affects TLS-1.0 connections only.
+ If set to <c>false</c>, it disables the block cipher padding check
+ to be able to interoperate with legacy software.</p></item>
+
</taglist>
-
+
+ <warning><p>Using <c>{padding_check, boolean()}</c> makes TLS
+ vulnerable to the Poodle attack.</p></warning>
+
</section>
<section>
<title>SSL OPTION DESCRIPTIONS - CLIENT SIDE</title>
- <p>Options described here are client specific or has a slightly different
- meaning in the client than in the server.</p>
+ <p>The following options are client-specific or have a slightly different
+ meaning in the client than in the server:</p>
<taglist>
- <tag>{verify, verify_type()}</tag>
- <item> In verify_none mode the default behavior will be to
- allow all x509-path validation errors. See also the verify_fun
- option.
- </item>
- <tag>{reuse_sessions, boolean()}</tag>
- <item>Specifies if client should try to reuse sessions
- when possible.
+
+ <tag><c>{verify, verify_type()}</c></tag>
+ <item><p>In mode <c>verify_none</c> the default behavior is to allow
+ all x509-path validation errors. See also option <c>verify_fun</c>.</p>
</item>
+
+ <tag><c>{reuse_sessions, boolean()}</c></tag>
+ <item><p>Specifies if the client is to try to reuse sessions
+ when possible.</p></item>
+
+ <tag><c>{cacerts, [public_key:der_encoded()]}</c></tag>
+ <item><p>The DER-encoded trusted certificates. If this option
+ is supplied it overrides option <c>cacertfile</c>.</p></item>
- <tag>{cacertfile, path()}</tag>
- <item>The path to a file containing PEM encoded CA certificates. The CA
+ <tag><c>{cacertfile, path()}</c></tag>
+ <item><p>Path to a file containing PEM-encoded CA certificates. The CA
certificates are used during server authentication and when building the
- client certificate chain.
- </item>
-
- <tag>{client_preferred_next_protocols, {Precedence :: server | client, ClientPrefs :: [binary()]}}</tag>
- <tag>{client_preferred_next_protocols, {Precedence :: server | client, ClientPrefs :: [binary()], Default :: binary()}}</tag>
+ client certificate chain.</p>
+ </item>
+
+ <tag><c>{alpn_advertised_protocols, [binary()]}</c></tag>
+ <item>
+ <p>The list of protocols supported by the client to be sent to the
+ server to be used for an Application-Layer Protocol Negotiation (ALPN).
+ If the server supports ALPN then it will choose a protocol from this
+ list; otherwise it will fail the connection with a "no_application_protocol"
+ alert. A server that does not support ALPN will ignore this value.</p>
+
+ <p>The list of protocols must not contain an empty binary.</p>
+
+ <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>
<item>
- <p>Indicates the client will try to perform Next Protocol
+ <p>Indicates that the client is to try to perform Next Protocol
Negotiation.</p>
- <p>If precedence is server the negotiated protocol will be the
- first protocol that appears on the server advertised list that is
+ <p>If precedence is server, the negotiated protocol is the
+ first protocol to be shown on the server advertised list, which is
also on the client preference list.</p>
- <p>If precedence is client the negotiated protocol will be the
- first protocol that appears on the client preference list that is
+ <p>If precedence is client, the negotiated protocol is the
+ first protocol to be shown on the client preference list, which is
also on the server advertised list.</p>
<p>If the client does not support any of the server advertised
- protocols or the server does not advertise any protocols the
- client will fallback to the first protocol in its list or if a
- default is supplied it will fallback to that instead. If the
- server does not support Next Protocol Negotiation the
- connection will be aborted if no default protocol is supplied.</p>
+ protocols or the server does not advertise any protocols, the
+ client falls back to the first protocol in its list or to the
+ default protocol (if a default is supplied). If the
+ server does not support Next Protocol Negotiation, the
+ connection terminates if no default protocol is supplied.</p>
</item>
- <tag>{psk_identity, string()}</tag>
- <item>Specifies the identity the client presents to the server. The matching secret is
- found by calling the user_look_fun.
- </item>
- <tag>{srp_identity, {Username :: string(), Password :: string()}</tag>
- <item>Specifies the Username and Password to use to authenticate to the server.
+ <tag><c>{psk_identity, string()}</c></tag>
+ <item><p>Specifies the identity the client presents to the server.
+ The matching secret is found by calling <c>user_lookup_fun</c>.</p>
</item>
- <tag>{server_name_indication, hostname()}</tag>
- <tag>{server_name_indication, disable}</tag>
+
+ <tag><c>{srp_identity, {Username :: string(), Password :: string()}
+ </c></tag>
+ <item><p>Specifies the username and password to use to authenticate
+ to the server.</p></item>
+
+ <tag><c>{server_name_indication, hostname()}</c></tag>
+ <item><p>Can be specified when upgrading a TCP socket to a TLS
+ socket to use the TLS Server Name Indication extension.</p></item>
+
+ <tag><c>{server_name_indication, disable}</c></tag>
<item>
- <p>This option can be specified when upgrading a TCP socket to a TLS
- socket to use the TLS Server Name Indication extension.</p>
- <p>When starting a TLS connection without upgrade the Server Name
- Indication extension will be sent if possible, this option may also be
+ <p>When starting a TLS connection without upgrade, the Server Name
+ Indication extension is sent if possible. This option can be
used to disable that behavior.</p>
</item>
- <tag>{fallback, boolean()}</tag>
+ <tag><c>{fallback, boolean()}</c></tag>
<item>
<p> Send special cipher suite TLS_FALLBACK_SCSV to avoid undesired TLS version downgrade.
Defaults to false</p>
<warning><p>Note this option is not needed in normal TLS usage and should not be used
- to implement new clients. But legacy clients that that retries connections in the following manner</p>
+ to implement new clients. But legacy clients that retries connections in the following manner</p>
<p><c> ssl:connect(Host, Port, [...{versions, ['tlsv2', 'tlsv1.1', 'tlsv1', 'sslv3']}])</c></p>
<p><c> ssl:connect(Host, Port, [...{versions, [tlsv1.1', 'tlsv1', 'sslv3']}, {fallback, true}])</c></p>
@@ -487,123 +543,149 @@ fun(srp, Username :: string(), UserState :: term()) ->
<section>
<title>SSL OPTION DESCRIPTIONS - SERVER SIDE</title>
- <p>Options described here are server specific or has a slightly different
- meaning in the server than in the client.</p>
+ <p>The following options are server-specific or have a slightly different
+ meaning in the server than in the client:</p>
<taglist>
+
+ <tag><c>{cacerts, [public_key:der_encoded()]}</c></tag>
+ <item><p>The DER-encoded trusted certificates. If this option
+ is supplied it overrides option <c>cacertfile</c>.</p></item>
- <tag>{cacertfile, path()}</tag>
- <item>The path to a file containing PEM encoded CA
+ <tag><c>{cacertfile, path()}</c></tag>
+ <item><p>Path to a file containing PEM-encoded CA
certificates. The CA certificates are used to build the server
- certificate chain, and for client authentication. Also the CAs
- are used in the list of acceptable client CAs passed to the
- client when a certificate is requested. May be omitted if there
- is no need to verify the client and if there are not any
- intermediate CAs for the server certificate.
- </item>
+ certificate chain and for client authentication. The CAs are
+ also used in the list of acceptable client CAs passed to the
+ client when a certificate is requested. Can be omitted if there
+ is no need to verify the client and if there are no
+ intermediate CAs for the server certificate.</p></item>
- <tag>{dh, der_encoded()}</tag>
- <item>The DER encoded Diffie Hellman parameters. If this option
- is supplied it will override the dhfile option.
+ <tag><c>{dh, public_key:der_encoded()}</c></tag>
+ <item><p>The DER-encoded Diffie-Hellman parameters. If specified,
+ it overrides option <c>dhfile</c>.</p></item>
+
+ <tag><c>{dhfile, path()}</c></tag>
+ <item><p>Path to a file containing PEM-encoded Diffie Hellman parameters
+ to be used by the server if a cipher suite using Diffie Hellman key
+ exchange is negotiated. If not specified, default parameters are used.
+ </p></item>
+
+ <tag><c>{verify, verify_type()}</c></tag>
+ <item><p>A server only does x509-path validation in mode <c>verify_peer</c>,
+ as it then sends a certificate request to the client
+ (this message is not sent if the verify option is <c>verify_none</c>).
+ You can then also want to specify option <c>fail_if_no_peer_cert</c>.
+ </p></item>
+
+ <tag><c>{fail_if_no_peer_cert, boolean()}</c></tag>
+ <item><p>Used together with <c>{verify, verify_peer}</c> by an SSL server.
+ If set to <c>true</c>, the server fails if the client does not have
+ a certificate to send, that is, sends an empty certificate. If set to
+ <c>false</c>, it fails only if the client sends an invalid
+ certificate (an empty certificate is considered valid). Defaults to false.</p>
</item>
- <tag>{dhfile, path()}</tag>
- <item>Path to file containing PEM encoded Diffie Hellman parameters,
- for the server to use if a cipher suite using Diffie Hellman key exchange
- is negotiated. If not specified default parameters will be used.
- </item>
+ <tag><c>{reuse_sessions, boolean()}</c></tag>
+ <item><p>Specifies if the server is to agree to reuse sessions
+ when requested by the clients. See also option <c>reuse_session</c>.
+ </p></item>
+
+ <tag><c>{reuse_session, fun(SuggestedSessionId,
+ PeerCert, Compression, CipherSuite) -> boolean()}</c></tag>
+ <item><p>Enables the SSL server to have a local policy
+ for deciding if a session is to be reused or not.
+ Meaningful only if <c>reuse_sessions</c> is set to <c>true</c>.
+ <c>SuggestedSessionId</c> is a <c>binary()</c>, <c>PeerCert</c> is
+ a DER-encoded certificate, <c>Compression</c> is an enumeration integer,
+ and <c>CipherSuite</c> is of type <c>ciphersuite()</c>.</p></item>
+
+ <tag><c>{alpn_preferred_protocols, [binary()]}</c></tag>
+ <item>
+ <p>Indicates the server will try to perform Application-Layer
+ Protocol Negotiation (ALPN).</p>
- <tag>{verify, verify_type()}</tag>
- <item>Servers only do the x509-path validation in verify_peer
- mode, as it then will send a certificate request to the client
- (this message is not sent if the verify option is verify_none)
- and you may then also want to specify the option
- fail_if_no_peer_cert.
- </item>
+ <p>The list of protocols is in order of preference. The protocol
+ negotiated will be the first in the list that matches one of the
+ protocols advertised by the client. If no protocol matches, the
+ server will fail the connection with a "no_application_protocol" alert.</p>
- <tag>{fail_if_no_peer_cert, boolean()}</tag>
- <item>Used together with {verify, verify_peer} by an ssl server.
- If set to true, the server will fail if the client does not have
- a certificate to send, i.e. sends a empty certificate, if set to
- false it will only fail if the client sends an invalid
- certificate (an empty certificate is considered valid).
+ <p>The negotiated protocol can be retrieved using the <c>negotiated_protocol/1</c> function.</p>
</item>
- <tag>{reuse_sessions, boolean()}</tag>
- <item>Specifies if the server should agree to reuse sessions
- when the clients request to do so. See also the reuse_session
- option.
- </item>
+ <tag><c>{next_protocols_advertised, Protocols :: [binary()]}</c></tag>
+ <item><p>List of protocols to send to the client if the client indicates that
+ it supports the Next Protocol extension. The client can select a protocol
+ that is not on this list. The list of protocols must not contain an empty
+ binary. If the server negotiates a Next Protocol, it can be accessed
+ using the <c>negotiated_next_protocol/1</c> method.</p></item>
- <tag>{reuse_session, fun(SuggestedSessionId,
- PeerCert, Compression, CipherSuite) -> boolean()}</tag>
- <item>Enables the ssl server to have a local policy
- for deciding if a session should be reused or not,
- only meaningful if <c>reuse_sessions</c> is set to true.
- SuggestedSessionId is a binary(), PeerCert is a DER encoded
- certificate, Compression is an enumeration integer
- and CipherSuite is of type ciphersuite().
- </item>
+ <tag><c>{psk_identity, string()}</c></tag>
+ <item><p>Specifies the server identity hint, which the server presents to
+ the client.</p></item>
- <tag>{next_protocols_advertised, Protocols :: [binary()]}</tag>
- <item>The list of protocols to send to the client if the client indicates
- it supports the Next Protocol extension. The client may select a protocol
- that is not on this list. The list of protocols must not contain an empty
- binary. If the server negotiates a Next Protocol it can be accessed
- using <c>negotiated_next_protocol/1</c> method.
- </item>
+ <tag><c>{log_alert, boolean()}</c></tag>
+ <item><p>If set to <c>false</c>, error reports are not displayed.</p></item>
+
+ <tag><c>{honor_cipher_order, boolean()}</c></tag>
+ <item><p>If set to <c>true</c>, use the server preference for cipher
+ selection. If set to <c>false</c> (the default), use the client
+ preference.</p></item>
+
+ <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
+ 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.
+ These options will be merged into predefined <c>ssloptions()</c>.
+
+ The function should be defined as:
+ <c>fun(ServerName :: string()) -> ssloptions()</c>
+ and can be specified as a fun or as named <c>fun module:function/1</c>
+
+ The option <c>sni_fun</c>, and <c>sni_hosts</c> are mutually exclusive.</p></item>
- <tag>{psk_identity, string()}</tag>
- <item>Specifies the server identity hint the server presents to the client.
- </item>
- <tag>{log_alert, boolean()}</tag>
- <item>If false, error reports will not be displayed.</item>
- <tag>{honor_cipher_order, boolean()}</tag>
- <item>If true, use the server's preference for cipher selection. If false
- (the default), use the client's preference.
- </item>
</taglist>
</section>
<section>
<title>General</title>
- <p>When an ssl socket is in active mode (the default), data from the
+ <p>When an SSL socket is in active mode (the default), data from the
socket is delivered to the owner of the socket in the form of
- messages:
- </p>
+ messages:</p>
+
<list type="bulleted">
- <item>{ssl, Socket, Data}
- </item>
- <item>{ssl_closed, Socket}
- </item>
- <item>
- {ssl_error, Socket, Reason}
- </item>
+ <item><p><c>{ssl, Socket, Data}</c></p></item>
+ <item><p><c>{ssl_closed, Socket}</c></p></item>
+ <item><p><c>{ssl_error, Socket, Reason}</c></p></item>
</list>
-
- <p>A <c>Timeout</c> argument specifies a timeout in milliseconds. The
- default value for a <c>Timeout</c> argument is <c>infinity</c>.
- </p>
+
+ <p>A <c>Timeout</c> argument specifies a time-out in milliseconds. The
+ default value for argument <c>Timeout</c> is <c>infinity</c>.</p>
</section>
<funcs>
<func>
<name>cipher_suites() -></name>
<name>cipher_suites(Type) -> ciphers()</name>
- <fsummary> Returns a list of supported cipher suites</fsummary>
+ <fsummary>Returns a list of supported cipher suites.</fsummary>
<type>
<v>Type = erlang | openssl | all</v>
-
</type>
<desc><p>Returns a list of supported cipher suites.
- cipher_suites() is equivalent to cipher_suites(erlang).
- Type openssl is provided for backwards compatibility with
- old ssl that used openssl. cipher_suites(all) returns
+ <c>cipher_suites()</c> is equivalent to <c>cipher_suites(erlang).</c>
+ Type <c>openssl</c> is provided for backwards compatibility with the
+ old SSL, which used OpenSSL. <c>cipher_suites(all)</c> returns
all available cipher suites. The cipher suites not present
- in cipher_suites(erlang) but in included in cipher_suites(all)
- will not be used unless explicitly configured by the user.
- </p>
+ in <c>cipher_suites(erlang)</c> but included in
+ <c>cipher_suites(all)</c> are not used unless explicitly configured
+ by the user.</p>
</desc>
</func>
@@ -623,17 +705,17 @@ fun(srp, Username :: string(), UserState :: term()) ->
<name>connect(Socket, SslOptions) -> </name>
<name>connect(Socket, SslOptions, Timeout) -> {ok, SslSocket}
| {error, Reason}</name>
- <fsummary> Upgrades a gen_tcp, or
- equivalent, connected socket to an ssl socket. </fsummary>
+ <fsummary>Upgrades a <c>gen_tcp</c>, or
+ equivalent, connected socket to an SSL socket.</fsummary>
<type>
- <v>Socket = socket()</v>
- <v>SslOptions = [ssloption()]</v>
+ <v>Socket = socket()</v>
+ <v>SslOptions = [ssloption()]</v>
<v>Timeout = integer() | infinity</v>
<v>SslSocket = sslsocket()</v>
<v>Reason = term()</v>
</type>
- <desc> <p>Upgrades a gen_tcp, or equivalent,
- connected socket to an ssl socket i.e. performs the
+ <desc><p>Upgrades a <c>gen_tcp</c>, or equivalent,
+ connected socket to an SSL socket, that is, performs the
client-side ssl handshake.</p>
</desc>
</func>
@@ -642,7 +724,7 @@ fun(srp, Username :: string(), UserState :: term()) ->
<name>connect(Host, Port, Options) -></name>
<name>connect(Host, Port, Options, Timeout) ->
{ok, SslSocket} | {error, Reason}</name>
- <fsummary>Opens an ssl connection to Host, Port. </fsummary>
+ <fsummary>Opens an SSL connection to <c>Host</c>, <c>Port</c>.</fsummary>
<type>
<v>Host = host()</v>
<v>Port = integer()</v>
@@ -651,72 +733,109 @@ fun(srp, Username :: string(), UserState :: term()) ->
<v>SslSocket = sslsocket()</v>
<v>Reason = term()</v>
</type>
- <desc> <p>Opens an ssl connection to Host, Port.</p> </desc>
+ <desc><p>Opens an SSL connection to <c>Host</c>, <c>Port</c>.</p></desc>
</func>
<func>
<name>close(SslSocket) -> ok | {error, Reason}</name>
- <fsummary>Close an ssl connection</fsummary>
+ <fsummary>Closes an SSL connection.</fsummary>
<type>
<v>SslSocket = sslsocket()</v>
<v>Reason = term()</v>
</type>
- <desc><p>Close an ssl connection.</p>
+ <desc><p>Closes an SSL connection.</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>
</func>
<func>
<name>controlling_process(SslSocket, NewOwner) ->
ok | {error, Reason}</name>
-
<fsummary>Assigns a new controlling process to the
- ssl-socket.</fsummary>
-
+ SSL socket.</fsummary>
<type>
<v>SslSocket = sslsocket()</v>
<v>NewOwner = pid()</v>
<v>Reason = term()</v>
</type>
- <desc><p>Assigns a new controlling process to the ssl-socket. A
- controlling process is the owner of an ssl-socket, and receives
- all messages from the socket.</p>
+ <desc><p>Assigns a new controlling process to the SSL socket. A
+ controlling process is the owner of an SSL socket, and receives
+ all messages from the socket.</p>
</desc>
</func>
<func>
- <name>connection_info(SslSocket) ->
- {ok, {ProtocolVersion, CipherSuite}} | {error, Reason} </name>
- <fsummary>Returns the negotiated protocol version and cipher suite.
+ <name>connection_information(SslSocket) ->
+ {ok, Info} | {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>Reason = term()</v>
</type>
- <desc><p>Returns the negotiated protocol version and cipher suite.</p>
+ <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>
</func>
- <func>
+ <func>
+ <name>connection_information(SslSocket, Items) ->
+ {ok, Info} | {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>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>
+ </func>
+
+ <func>
<name>format_error(Reason) -> string()</name>
- <fsummary>Return an error string.</fsummary>
+ <fsummary>Returns an error string.</fsummary>
<type>
<v>Reason = term()</v>
</type>
<desc>
- <p>Presents the error returned by an ssl function as a printable string.</p>
+ <p>Presents the error returned by an SSL function as a printable string.</p>
</desc>
</func>
<func>
<name>getopts(Socket, OptionNames) ->
{ok, [socketoption()]} | {error, Reason}</name>
- <fsummary>Get the value of the specified options.</fsummary>
+ <fsummary>Gets the values of the specified options.</fsummary>
<type>
<v>Socket = sslsocket()</v>
<v>OptionNames = [atom()]</v>
</type>
<desc>
- <p>Get the value of the specified socket options.
+ <p>Gets the values of the specified socket options.
</p>
</desc>
</func>
@@ -724,34 +843,49 @@ fun(srp, Username :: string(), UserState :: term()) ->
<func>
<name>listen(Port, Options) ->
{ok, ListenSocket} | {error, Reason}</name>
- <fsummary>Creates an ssl listen socket.</fsummary>
+ <fsummary>Creates an SSL listen socket.</fsummary>
<type>
<v>Port = integer()</v>
<v>Options = options()</v>
<v>ListenSocket = sslsocket()</v>
</type>
<desc>
- <p>Creates an ssl listen socket.</p>
+ <p>Creates an SSL listen socket.</p>
</desc>
</func>
<func>
+ <name>negotiated_protocol(Socket) -> {ok, Protocol} | {error, protocol_not_negotiated}</name>
+ <fsummary>Returns the protocol negotiated through ALPN or NPN extensions.</fsummary>
+ <type>
+ <v>Socket = sslsocket()</v>
+ <v>Protocol = binary()</v>
+ </type>
+ <desc>
+ <p>
+ Returns the protocol negotiated through ALPN or NPN extensions.
+ </p>
+ </desc>
+ </func>
+
+ <func>
<name>peercert(Socket) -> {ok, Cert} | {error, Reason}</name>
- <fsummary>Return the peer certificate.</fsummary>
+ <fsummary>Returns the peer certificate.</fsummary>
<type>
<v>Socket = sslsocket()</v>
<v>Cert = binary()</v>
</type>
<desc>
- <p>The peer certificate is returned as a DER encoded binary.
- The certificate can be decoded with <c>public_key:pkix_decode_cert/2</c>.
- </p>
+ <p>The peer certificate is returned as a DER-encoded binary.
+ The certificate can be decoded with
+ <c>public_key:pkix_decode_cert/2</c>.</p>
</desc>
</func>
+
<func>
<name>peername(Socket) -> {ok, {Address, Port}} |
{error, Reason}</name>
- <fsummary>Return peer address and port.</fsummary>
+ <fsummary>Returns the peer address and port.</fsummary>
<type>
<v>Socket = sslsocket()</v>
<v>Address = ipaddress()</v>
@@ -761,12 +895,32 @@ fun(srp, Username :: string(), UserState :: term()) ->
<p>Returns the address and port number of the peer.</p>
</desc>
</func>
+
+ <func>
+ <name>prf(Socket, Secret, Label, Seed, WantedLength) -> {ok, binary()} | {error, reason()}</name>
+ <fsummary>Uses a session Pseudo-Random Function to generate key material.</fsummary>
+ <type>
+ <v>Socket = sslsocket()</v>
+ <v>Secret = binary() | master_secret</v>
+ <v>Label = binary()</v>
+ <v>Seed = [binary() | prf_random()]</v>
+ <v>WantedLength = non_neg_integer()</v>
+ </type>
+ <desc>
+ <p>Uses the Pseudo-Random Function (PRF) of a TLS session to generate
+ extra key material. It either takes user-generated values for
+ <c>Secret</c> and <c>Seed</c> or atoms directing it to use a specific
+ value from the session security parameters.</p>
+ <p>Can only be used with TLS connections; <c>{error, undefined}</c>
+ is returned for SSLv3 connections.</p>
+ </desc>
+ </func>
<func>
<name>recv(Socket, Length) -> </name>
<name>recv(Socket, Length, Timeout) -> {ok, Data} | {error,
Reason}</name>
- <fsummary>Receive data on a socket.</fsummary>
+ <fsummary>Receives data on a socket.</fsummary>
<type>
<v>Socket = sslsocket()</v>
<v>Length = integer()</v>
@@ -774,63 +928,43 @@ fun(srp, Username :: string(), UserState :: term()) ->
<v>Data = [char()] | binary()</v>
</type>
<desc>
- <p>This function receives a packet from a socket in passive
- mode. A closed socket is indicated by a return value
+ <p>Receives a packet from a socket in passive
+ mode. A closed socket is indicated by return value
<c>{error, closed}</c>.</p>
- <p>The <c>Length</c> argument is only meaningful when
- the socket is in <c>raw</c> mode and denotes the number of
+ <p>Argument <c>Length</c> is meaningful only when
+ the socket is in mode <c>raw</c> and denotes the number of
bytes to read. If <c>Length</c> = 0, all available bytes are
returned. If <c>Length</c> &gt; 0, exactly <c>Length</c>
bytes are returned, or an error; possibly discarding less
than <c>Length</c> bytes of data when the socket gets closed
from the other side.</p>
- <p>The optional <c>Timeout</c> parameter specifies a timeout in
+ <p>Optional argument <c>Timeout</c> specifies a time-out in
milliseconds. The default value is <c>infinity</c>.</p>
</desc>
</func>
<func>
- <name>prf(Socket, Secret, Label, Seed, WantedLength) -> {ok, binary()} | {error, reason()}</name>
- <fsummary>Use a sessions pseudo random function to generate key material.</fsummary>
- <type>
- <v>Socket = sslsocket()</v>
- <v>Secret = binary() | master_secret</v>
- <v>Label = binary()</v>
- <v>Seed = [binary() | prf_random()]</v>
- <v>WantedLength = non_neg_integer()</v>
- </type>
- <desc>
- <p>Use the pseudo random function (PRF) of a TLS session to generate
- additional key material. It either takes user generated values for
- <c>Secret</c> and <c>Seed</c> or atoms directing it use a specific
- value from the session security parameters.</p>
- <p>This function can only be used with TLS connections, <c>{error, undefined}</c>
- is returned for SSLv3 connections.</p>
- </desc>
- </func>
-
- <func>
<name>renegotiate(Socket) -> ok | {error, Reason}</name>
- <fsummary> Initiates a new handshake.</fsummary>
+ <fsummary>Initiates a new handshake.</fsummary>
<type>
<v>Socket = sslsocket()</v>
</type>
<desc><p>Initiates a new handshake. A notable return value is
<c>{error, renegotiation_rejected}</c> indicating that the peer
- refused to go through with the renegotiation but the connection
+ refused to go through with the renegotiation, but the connection
is still active using the previously negotiated session.</p>
</desc>
</func>
<func>
<name>send(Socket, Data) -> ok | {error, Reason}</name>
- <fsummary>Write data to a socket.</fsummary>
+ <fsummary>Writes data to a socket.</fsummary>
<type>
<v>Socket = sslsocket()</v>
<v>Data = iodata()</v>
</type>
<desc>
- <p>Writes <c>Data</c> to <c>Socket</c>. </p>
+ <p>Writes <c>Data</c> to <c>Socket</c>.</p>
<p>A notable return value is <c>{error, closed}</c> indicating that
the socket is closed.</p>
</desc>
@@ -838,31 +972,31 @@ fun(srp, Username :: string(), UserState :: term()) ->
<func>
<name>setopts(Socket, Options) -> ok | {error, Reason}</name>
- <fsummary>Set socket options.</fsummary>
+ <fsummary>Sets socket options.</fsummary>
<type>
<v>Socket = sslsocket()</v>
<v>Options = [socketoption]()</v>
</type>
<desc>
- <p>Sets options according to <c>Options</c> for the socket
- <c>Socket</c>. </p>
+ <p>Sets options according to <c>Options</c> for socket
+ <c>Socket</c>.</p>
</desc>
</func>
<func>
<name>shutdown(Socket, How) -> ok | {error, Reason}</name>
- <fsummary>Immediately close a socket</fsummary>
+ <fsummary>Immediately closes a socket.</fsummary>
<type>
<v>Socket = sslsocket()</v>
<v>How = read | write | read_write</v>
<v>Reason = reason()</v>
</type>
<desc>
- <p>Immediately close a socket in one or two directions.</p>
+ <p>Immediately closes a socket in one or two directions.</p>
<p><c>How == write</c> means closing the socket for writing,
reading from it is still possible.</p>
<p>To be able to handle that the peer has done a shutdown on
- the write side, the <c>{exit_on_close, false}</c> option
+ the write side, option <c>{exit_on_close, false}</c>
is useful.</p>
</desc>
</func>
@@ -870,16 +1004,16 @@ fun(srp, Username :: string(), UserState :: term()) ->
<func>
<name>ssl_accept(Socket) -> </name>
<name>ssl_accept(Socket, Timeout) -> ok | {error, Reason}</name>
- <fsummary>Perform server-side SSL/TLS handshake</fsummary>
+ <fsummary>Performs server-side SSL/TLS handshake.</fsummary>
<type>
<v>Socket = sslsocket()</v>
<v>Timeout = integer()</v>
<v>Reason = term()</v>
</type>
<desc>
- <p> Performs the SSL/TLS server-side handshake <c>Socket</c> is a socket as returned
- by <seealso
- marker="#transport_accept-2">ssl:transport_accept/[1,2]</seealso>
+ <p>Performs the SSL/TLS server-side handshake.</p>
+ <p><c>Socket</c> is a socket as returned by
+ <seealso marker="#transport_accept-2">ssl:transport_accept/[1,2]</seealso>
</p>
</desc>
</func>
@@ -887,7 +1021,7 @@ fun(srp, Username :: string(), UserState :: term()) ->
<func>
<name>ssl_accept(Socket, SslOptions) -> </name>
<name>ssl_accept(Socket, SslOptions, Timeout) -> {ok, Socket} | ok | {error, Reason}</name>
- <fsummary>Perform server-side SSL/TLS handshake</fsummary>
+ <fsummary>Performs server-side SSL/TLS handshake.</fsummary>
<type>
<v>Socket = socket() | sslsocket() </v>
<v>SslOptions = ssloptions()</v>
@@ -895,17 +1029,19 @@ fun(srp, Username :: string(), UserState :: term()) ->
<v>Reason = term()</v>
</type>
<desc>
- <p> If <c>Socket</c> is a socket() - upgrades a gen_tcp, or equivalent, socket to an ssl socket
- i.e. performs the SSL/TLS server-side handshake and returns the ssl socket.
- </p>
+ <p>If <c>Socket</c> is a <c>socket()</c>: upgrades a <c>gen_tcp</c>,
+ or equivalent, socket to an SSL socket, that is, performs
+ the SSL/TLS server-side handshake and returns the SSL socket.</p>
- <warning><p>Note that the listen socket should be in {active, false} mode
+ <warning><p>The listen socket is to be in mode <c>{active, false}</c>
before telling the client that the server is ready to upgrade
- by calling this function, otherwise the upgrade may
- or may not succeed depending on timing.</p></warning>
+ by calling this function, else the upgrade succeeds or does not
+ succeed depending on timing.</p></warning>
- <p> If <c>Socket</c> is an sslsocket() - provides additional SSL/TLS options to those specified in <seealso
- marker="#listen-2">ssl:listen/2 </seealso> and then performs the SSL/TLS handshake.
+ <p>If <c>Socket</c> is an <c>sslsocket()</c>: provides extra SSL/TLS
+ options to those specified in
+ <seealso marker="#listen-2">ssl:listen/2 </seealso> and then performs
+ the SSL/TLS handshake.
</p>
</desc>
</func>
@@ -913,14 +1049,14 @@ fun(srp, Username :: string(), UserState :: term()) ->
<func>
<name>sockname(Socket) -> {ok, {Address, Port}} |
{error, Reason}</name>
- <fsummary>Return the local address and port.</fsummary>
+ <fsummary>Returns the local address and port.</fsummary>
<type>
<v>Socket = sslsocket()</v>
<v>Address = ipaddress()</v>
<v>Port = integer()</v>
</type>
<desc>
- <p>Returns the local address and port number of the socket
+ <p>Returns the local address and port number of socket
<c>Socket</c>.</p>
</desc>
</func>
@@ -928,22 +1064,21 @@ fun(srp, Username :: string(), UserState :: term()) ->
<func>
<name>start() -> </name>
<name>start(Type) -> ok | {error, Reason}</name>
- <fsummary>Starts the Ssl application. </fsummary>
+ <fsummary>Starts the SSL application.</fsummary>
<type>
- <v>Type = permanent | transient | temporary</v>
+ <v>Type = permanent | transient | temporary</v>
</type>
<desc>
- <p>Starts the Ssl application. Default type
- is temporary.
- <seealso marker="kernel:application">application(3)</seealso></p>
+ <p>Starts the SSL application. Default type
+ is <c>temporary</c>.</p>
</desc>
</func>
+
<func>
<name>stop() -> ok </name>
- <fsummary>Stops the Ssl application.</fsummary>
+ <fsummary>Stops the SSL application.</fsummary>
<desc>
- <p>Stops the Ssl application.
- <seealso marker="kernel:application">application(3)</seealso></p>
+ <p>Stops the SSL application.</p>
</desc>
</func>
@@ -951,8 +1086,8 @@ fun(srp, Username :: string(), UserState :: term()) ->
<name>transport_accept(ListenSocket) -></name>
<name>transport_accept(ListenSocket, Timeout) ->
{ok, NewSocket} | {error, Reason}</name>
- <fsummary>Accept an incoming connection and
- prepare for <c>ssl_accept</c></fsummary>
+ <fsummary>Accepts an incoming connection and
+ prepares for <c>ssl_accept</c>.</fsummary>
<type>
<v>ListenSocket = NewSocket = sslsocket()</v>
<v>Timeout = integer()</v>
@@ -961,23 +1096,22 @@ fun(srp, Username :: string(), UserState :: term()) ->
<desc>
<p>Accepts an incoming connection request on a listen socket.
<c>ListenSocket</c> must be a socket returned from
- <seealso
- marker="#listen-2"> ssl:listen/2</seealso>.
- The socket returned should be passed to
+ <seealso marker="#listen-2"> ssl:listen/2</seealso>.
+ The socket returned is to be passed to
<seealso marker="#ssl_accept-2"> ssl:ssl_accept[2,3]</seealso>
- to complete handshaking i.e
+ to complete handshaking, that is,
establishing the SSL/TLS connection.</p>
<warning>
<p>The socket returned can only be used with
- <seealso marker="#ssl_accept-2"> ssl:ssl_accept[2,3]</seealso>
- no traffic can be sent or received before that call.</p>
+ <seealso marker="#ssl_accept-2"> ssl:ssl_accept[2,3]</seealso>.
+ No traffic can be sent or received before that call.</p>
</warning>
<p>The accepted socket inherits the options set for
- <c>ListenSocket</c> in <seealso
- marker="#listen-2"> ssl:listen/2</seealso>.</p>
+ <c>ListenSocket</c> in
+ <seealso marker="#listen-2"> ssl:listen/2</seealso>.</p>
<p>The default
value for <c>Timeout</c> is <c>infinity</c>. If
- <c>Timeout</c> is specified, and no connection is accepted
+ <c>Timeout</c> is specified and no connection is accepted
within the given time, <c>{error, timeout}</c> is
returned.</p>
</desc>
@@ -986,59 +1120,42 @@ fun(srp, Username :: string(), UserState :: term()) ->
<func>
<name>versions() -> [versions_info()]</name>
<fsummary>Returns version information relevant for the
- ssl application.</fsummary>
+ SSL application.</fsummary>
<type>
<v>versions_info() = {app_vsn, string()} | {supported | available, [protocol()] </v>
</type>
<desc>
- <p>
- Returns version information relevant for the
- ssl application.
- </p>
+ <p>Returns version information relevant for the SSL
+ application.</p>
<taglist>
- <tag>app_vsn</tag>
- <item> The application version of the OTP ssl application.</item>
-
- <tag>supported</tag>
+ <tag><c>app_vsn</c></tag>
+ <item>The application version of the SSL application.</item>
+ <tag><c>supported</c></tag>
<item>TLS/SSL versions supported by default.
- Overridden by a versions option on
- <seealso marker="#connect-2"> connect/[2,3,4]</seealso>, <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
+ Overridden by a version option on
+ <seealso marker="#connect-2"> connect/[2,3,4]</seealso>,
+ <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
- </seealso></item>
-
- <tag>available</tag>
- <item>All TLS/SSL versions that the Erlang ssl application
- can support. Note that TLS 1.2 requires sufficient support
- from the crypto application. </item>
+ </seealso>.</item>
+
+ <tag><c>available</c></tag>
+ <item>All TLS/SSL versions supported by the SSL application.
+ TLS 1.2 requires sufficient support from the Crypto
+ application.</item>
</taglist>
</desc>
</func>
- <func>
- <name>negotiated_next_protocol(Socket) -> {ok, Protocol} | {error, next_protocol_not_negotiated}</name>
- <fsummary>Returns the Next Protocol negotiated.</fsummary>
- <type>
- <v>Socket = sslsocket()</v>
- <v>Protocol = binary()</v>
- </type>
- <desc>
- <p>
- Returns the Next Protocol negotiated.
- </p>
- </desc>
- </func>
-
+
</funcs>
<section>
<title>SEE ALSO</title>
- <p><seealso marker="kernel:inet">inet(3) </seealso> and
- <seealso marker="kernel:gen_tcp">gen_tcp(3) </seealso>
+ <p><seealso marker="kernel:inet">inet(3)</seealso> and
+ <seealso marker="kernel:gen_tcp">gen_tcp(3)</seealso>
</p>
</section>
</erlref>
-
diff --git a/lib/ssl/doc/src/ssl_app.xml b/lib/ssl/doc/src/ssl_app.xml
index e3a3fc27f2..f17f5cb9fe 100644
--- a/lib/ssl/doc/src/ssl_app.xml
+++ b/lib/ssl/doc/src/ssl_app.xml
@@ -22,66 +22,60 @@
</legalnotice>
<title>ssl</title>
+ <prepared></prepared>
+ <docno></docno>
+ <date></date>
+ <rev></rev>
<file>ssl_app.sgml</file>
</header>
<app>ssl</app>
- <appsummary>The SSL application provides secure communication over
+ <appsummary>The ssl application provides secure communication over
sockets.</appsummary>
+ <description></description>
<section>
<title>DEPENDENCIES</title>
- <p>The ssl application uses the Erlang applications public_key and
- crypto to handle public keys and encryption, hence these
- applications needs to be loaded for the ssl application to work. In
- an embedded environment that means they need to be started with
- application:start/[1,2] before the ssl application is started.
- </p>
+ <p>The SSL application uses the <c>public_key</c> and
+ Crypto application to handle public keys and encryption, hence
+ these applications must be loaded for the SSL application to work.
+ In an embedded environment this means they must be started with
+ <c>application:start/[1,2]</c> before the SSL application is
+ started.</p>
</section>
<section>
- <title>ENVIRONMENT</title>
- <p>The following application environment configuration parameters
- are defined for the SSL application. See <seealso
- marker="kernel:application">application(3)</seealso>for more
- information about configuration parameters.
- </p>
- <p>Note that the environment parameters can be set on the command line,
- for instance,</p>
- <p><c>erl ... -ssl protocol_version '[sslv3, tlsv1]' ...</c>.
- </p>
+ <title>CONFIGURATION</title>
+ <p>The application environment configuration parameters in this section
+ are defined for the SSL application. For more information
+ about configuration parameters, see the
+ <seealso marker="kernel:application">application(3)</seealso>
+ manual page in Kernel.</p>
+
+ <p>The environment parameters can be set on the command line,
+ for example:</p>
+
+ <p><c>erl -ssl protocol_version "['tlsv1.2', 'tlsv1.1']"</c></p>
+
<taglist>
- <tag><c><![CDATA[protocol_version = [sslv3|tlsv1] <optional>]]></c>.</tag>
- <item>
- <p>Protocol that will be supported by started clients and
- servers. If this option is not set it will default to all
- protocols currently supported by the erlang ssl application.
- Note that this option may be overridden by the version option
- to ssl:connect/[2,3] and ssl:listen/2.
- </p>
- </item>
+ <tag><c><![CDATA[protocol_version = <seealso marker="kernel:error_logger">ssl:protocol()</seealso> <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.
+ This option can be overridden by the version option
+ 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>The lifetime of session data in seconds.
- </p>
- </item>
+ <item><p>Lifetime of the session data in seconds.</p></item>
- <tag><c><![CDATA[session_cb = atom() <optional>]]></c></tag>
- <item>
- <p>
- Name of session cache callback module that implements
- the ssl_session_cache_api behavior, defaults to
- ssl_session_cache.erl.
- </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>
<tag><c><![CDATA[session_cb_init_args = proplist:proplist() <optional>]]></c></tag>
- <item>
- <p>
- List of additional user defined arguments to the init function in session cache
- callback module, defaults to [].
- </p>
- </item>
+
+ <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[ssl_pem_cache_clean = integer() <optional>]]></c></tag>
<item>
@@ -96,6 +90,11 @@
</section>
<section>
+ <title>ERROR LOGGER AND EVENT HANDLERS</title>
+ <p>The SSL application uses the default <seealso marker="kernel:error_logger">OTP error logger</seealso> to log unexpected errors and TLS alerts. The logging of TLS alerts may be turned off with the <c>log_alert</c> option. </p>
+ </section>
+
+ <section>
<title>SEE ALSO</title>
<p><seealso marker="kernel:application">application(3)</seealso></p>
</section>
diff --git a/lib/ssl/doc/src/ssl_crl_cache.xml b/lib/ssl/doc/src/ssl_crl_cache.xml
index b291c7b633..83b03375b1 100644
--- a/lib/ssl/doc/src/ssl_crl_cache.xml
+++ b/lib/ssl/doc/src/ssl_crl_cache.xml
@@ -29,38 +29,37 @@
<p>
Implements an internal CRL (Certificate Revocation List) cache.
In addition to implementing the <seealso
- marker="ssl_cache_crl_api"> ssl_cache_crl_api</seealso> behaviour
+ marker="ssl_crl_cache_api"> ssl_crl_cache_api</seealso> behaviour
the following functions are available.
</p>
</description>
<funcs>
<func>
- <name>insert(CRLSrc) -> ok | {error, Reason}</name>
- <name>insert(URI, CRLSrc) -> ok | {error, Reason}</name>
- <fsummary> </fsummary>
- <type>
- <v> CRLSrc = {file, string()} | {der, [ <seealso
- marker="public_key:public_key"> der_encoded() </seealso> ]}</v>
- <v> URI = http_uri:uri()</v>
- <v> Reason = term()</v>
- </type>
- <desc>
- Insert CRLs into the ssl applications local cache.
- </desc>
- </func>
-
- <func>
- <name>delete(Entries) -> ok | {error, Reason} </name>
- <fsummary> </fsummary>
- <type>
- <v> Entries = http_uri:uri() | {file, string()} | {der, [<seealso
- marker="public_key:public_key"> der_encoded() </seealso>]}</v>
- <v> Reason = term()</v>
- </type>
- <desc>
- Delete CRLs from the ssl applications local cache.
- </desc>
- </func>
+ <name>delete(Entries) -> ok | {error, Reason} </name>
+ <fsummary> </fsummary>
+ <type>
+ <v> Entries = <seealso marker="inets:http_uri">http_uri:uri() </seealso> | {file, string()} | {der, [<seealso
+ marker="public_key:public_key"> public_key:der_encoded() </seealso>]}</v>
+ <v> Reason = term()</v>
+ </type>
+ <desc>
+ <p>Delete CRLs from the ssl applications local cache. </p>
+ </desc>
+ </func>
+ <func>
+ <name>insert(CRLSrc) -> ok | {error, Reason}</name>
+ <name>insert(URI, CRLSrc) -> ok | {error, Reason}</name>
+ <fsummary> </fsummary>
+ <type>
+ <v> CRLSrc = {file, string()} | {der, [ <seealso
+ marker="public_key:public_key"> public_key:der_encoded() </seealso> ]}</v>
+ <v> URI = <seealso marker="inets:http_uri">http_uri:uri() </seealso> </v>
+ <v> Reason = term()</v>
+ </type>
+ <desc>
+ <p>Insert CRLs into the ssl applications local cache. </p>
+ </desc>
+ </func>
</funcs>
</erlref> \ No newline at end of file
diff --git a/lib/ssl/doc/src/ssl_crl_cache_api.xml b/lib/ssl/doc/src/ssl_crl_cache_api.xml
index 3f518496be..9230442ae0 100644
--- a/lib/ssl/doc/src/ssl_crl_cache_api.xml
+++ b/lib/ssl/doc/src/ssl_crl_cache_api.xml
@@ -40,17 +40,40 @@
</description>
<section>
- <title>Common Data Types</title>
+ <title>DATA TYPES</title>
<p>The following data types are used in the functions below:
</p>
- <p><c>cache_ref() = opaque()</c></p>
- <p> dist_point() = #'DistributionPoint'{} see <seealso
- marker="public_key:cert_records"> X509 certificates records</seealso></p>
+ <taglist>
+
+ <tag><c>cache_ref() =</c></tag>
+ <item>opaque()</item>
+ <tag><c>dist_point() =</c></tag>
+ <item><p>#'DistributionPoint'{} see <seealso
+ marker="public_key:public_key_records"> X509 certificates records</seealso></p></item>
+
+ </taglist>
+
</section>
-
<funcs>
+ <func>
+ <name>fresh_crl(DistributionPoint, CRL) -> FreshCRL</name>
+ <fsummary> <c>fun fresh_crl/2 </c> will be used as input option <c>update_crl</c> to
+ public_key:pkix_crls_validate/3 </fsummary>
+ <type>
+ <v> DistributionPoint = dist_point() </v>
+ <v> CRL = [<seealso
+ marker="public_key:public_key">public_key:der_encoded()</seealso>] </v>
+ <v> FreshCRL = [<seealso
+ marker="public_key:public_key">public_key:der_encoded()</seealso>] </v>
+ </type>
+ <desc>
+ <p> <c>fun fresh_crl/2 </c> will be used as input option <c>update_crl</c> to
+ <seealso marker="public_key:public_key#pkix_crls_validate-3">public_key:pkix_crls_validate/3 </seealso> </p>
+ </desc>
+ </func>
+
<func>
<name>lookup(DistributionPoint, DbHandle) -> not_available | CRLs </name>
<fsummary> </fsummary>
@@ -60,7 +83,7 @@
<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>. </p>
This function may choose to only look in the cache or to follow distribution point
links depending on how the cache is administrated.
</desc>
@@ -78,22 +101,5 @@
<p>Select the CRLs in the cache that are issued by <c>Issuer</c> </p>
</desc>
</func>
-
- <func>
- <name>fresh_crl(DistributionPoint, CRL) -> FreshCRL</name>
- <fsummary> <c>fun fresh_crl/2 </c> will be used as input option <c>update_crl</c> to
- public_key:pkix_crls_validate/3 </fsummary>
- <type>
- <v> DistributionPoint = dist_point() </v>
- <v> CRL = [<seealso
- marker="public_key:public_key">public_key:der_encoded()</seealso>] </v>
- <v> FreshCRL = [<seealso
- marker="public_key:public_key">public_key:der_encoded()</seealso>] </v>
- </type>
- <desc>
- <p> <c>fun fresh_crl/2 </c> will be used as input option <c>update_crl</c> to
- <seealso marker="public_key#pkix_path_validation-3">public_key:pkix_crls_validate/3 </seealso> </p>
- </desc>
- </func>
</funcs>
</erlref> \ No newline at end of file
diff --git a/lib/ssl/doc/src/ssl_distribution.xml b/lib/ssl/doc/src/ssl_distribution.xml
index 4b4d042f70..effb304938 100644
--- a/lib/ssl/doc/src/ssl_distribution.xml
+++ b/lib/ssl/doc/src/ssl_distribution.xml
@@ -31,23 +31,20 @@
<rev>B</rev>
<file>ssl_distribution.xml</file>
</header>
- <p>This chapter describes how the Erlang distribution can use
- SSL to get additional verification and security.
- </p>
+ <p>This section describes how the Erlang distribution can use
+ SSL to get extra verification and security.</p>
- <section>
- <title>Introduction</title>
- <p>The Erlang distribution can in theory use almost any connection
- based protocol as bearer. A module that implements the protocol
- specific parts of the connection setup is however needed. The
- default distribution module is <c>inet_tcp_dist</c> which is
- included in the Kernel application. When starting an
+ <p>The Erlang distribution can in theory use almost any
+ connection-based protocol as bearer. However, a module that
+ implements the protocol-specific parts of the connection setup is
+ needed. The default distribution module is <c>inet_tcp_dist</c>
+ in the Kernel application. When starting an
Erlang node distributed, <c>net_kernel</c> uses this module to
- setup listen ports and connections. </p>
+ set up listen ports and connections.</p>
- <p>In the SSL application there is an additional distribution
- module, <c>inet_tls_dist</c> which can be used as an
- alternative. All distribution connections will be using SSL and
+ <p>In the SSL application, an exra distribution
+ module, <c>inet_tls_dist</c>, can be used as an
+ alternative. All distribution connections will use SSL and
all participating Erlang nodes in a distributed system must use
this distribution module.</p>
@@ -55,35 +52,45 @@
SSL connection setup. Erlang node cookies are however always
used, as they can be used to differentiate between two different
Erlang networks.</p>
- <p>Setting up Erlang distribution over SSL involves some simple but
- necessary steps:</p>
+
+ <p>To set up Erlang distribution over SSL:</p>
<list type="bulleted">
- <item>Building boot scripts including the SSL application</item>
- <item>Specifying the distribution module for net_kernel</item>
- <item>Specifying security options and other SSL options</item>
+ <item><em>Step 1:</em> Build boot scripts including the
+ SSL application.</item>
+ <item><em>Step 2:</em> Specify the distribution module for
+ <c>net_kernel</c>.</item>
+ <item><em>Step 3:</em> Specify the security options and other
+ SSL options.</item>
+ <item><em>Step 4:</em> Set up the environment to always use SSL.</item>
</list>
- <p>The rest of this chapter describes the above mentioned steps in
- more detail.</p>
- </section>
+
+ <p>The following sections describe these steps.</p>
<section>
- <title>Building boot scripts including the SSL application</title>
+ <title>Building Boot Scripts Including the ssl Application</title>
<p>Boot scripts are built using the <c>systools</c> utility in the
- SASL application. Refer to the SASL documentations
- for more information on systools. This is only an example of
+ <c>sasl</c> application. For more information on <c>systools</c>,
+ see the <c>sasl</c> documentation. This is only an example of
what can be done.</p>
- <p>The simplest boot script possible includes only the Kernel
- and STDLIB applications. Such a script is located in the
- Erlang distributions bin directory. The source for the script
- can be found under the Erlang installation top directory under
- <c><![CDATA[releases/<OTP version>/start_clean.rel]]></c>. Copy that
- script to another location (and preferably another name)
- and add the applications crypto, public_key and SSL with their current version numbers
- after the STDLIB application.</p>
- <p>An example .rel file with SSL added may look like this:</p>
+ <p>The simplest boot script possible includes only the Kernel
+ and STDLIB applications. Such a script is located in the
+ <c>bin</c> directory of the Erlang distribution. The source for the
+ script is found under the Erlang installation top directory under
+ <c><![CDATA[releases/<OTP version>/start_clean.rel]]></c>.</p>
+
+ <p>Do the following:</p>
+ <list type="bulleted">
+ <item><p>Copy that script to another location (and preferably another
+ name).</p></item>
+ <item><p>Add the applications Crypto, Public Key, and
+ SSL with their current version numbers after the
+ STDLIB application.</p></item>
+ </list>
+ <p>The following shows an example <c>.rel</c> file with SSL
+ added:</p>
<code type="none">
{release, {"OTP APN 181 01","R15A"}, {erts, "5.9"},
[{kernel,"2.15"},
@@ -94,23 +101,29 @@
]}.
</code>
- <p>Note that the version numbers surely will differ in your system.
- Whenever one of the applications included in the script is
- upgraded, the script has to be changed.</p>
- <p>Assuming the above .rel file is stored in a file
- <c>start_ssl.rel</c> in the current directory, a boot script
- can be built like this:</p>
+ <p>The version numbers differ in your system. Whenever one of the
+ applications included in the script is upgraded, change the script.</p>
+ <p>Do the following:</p>
+ <list type="bulleted">
+ <item><p>Build the boot script.</p>
+ <p>Assuming the <c>.rel file</c> is stored in a file
+ <c>start_ssl.rel</c> in the current directory, a boot script
+ can be built as follows:</p></item>
+ </list>
<code type="none">
1> systools:make_script("start_ssl",[]). </code>
- <p>There will now be a file <c>start_ssl.boot</c> in the current
- directory. To test the boot script, start Erlang with the
- <c>-boot</c> command line parameter specifying this boot script
- (with its full path but without the <c>.boot</c> suffix), in
- Unix it could look like this:</p>
- <p></p>
+ <p>There is now a <c>start_ssl.boot</c> file in the current
+ directory.</p>
+ <p>Do the following:</p>
+ <list type="bulleted">
+ <item><p>Test the boot script. To do this, start Erlang with the
+ <c>-boot</c> command-line parameter specifying this boot script
+ (with its full path, but without the <c>.boot</c> suffix). In
+ UNIX it can look as follows:</p></item>
+ </list>
<code type="none"><![CDATA[
$ erl -boot /home/me/ssl/start_ssl
Erlang (BEAM) emulator version 5.0
@@ -118,86 +131,99 @@ Erlang (BEAM) emulator version 5.0
Eshell V5.0 (abort with ^G)
1> whereis(ssl_manager).
<0.41.0> ]]></code>
- <p>The <c>whereis</c> function call verifies that the SSL
- application is really started.</p>
- <p>As an alternative to building a bootscript, one can explicitly
+ <p>The <c>whereis</c> function-call verifies that the SSL
+ application is started.</p>
+
+ <p>As an alternative to building a bootscript, you can explicitly
add the path to the SSL <c>ebin</c> directory on the command
- line. This is done with the command line option <c>-pa</c>. This
+ line. This is done with command-line option <c>-pa</c>. This
works as the SSL application does not need to be started for the
distribution to come up, as a clone of the SSL application is
- hooked into the kernel application, so as long as the
- SSL applications code can be reached, the distribution will
- start. The <c>-pa</c> method is only recommended for testing
- purposes.</p>
+ hooked into the Kernel application. So, as long as the
+ SSL application code can be reached, the distribution starts.
+ The <c>-pa</c> method is only recommended for testing purposes.</p>
- <note><p>Note that the clone of the SSL application is necessary to
+ <note><p>The clone of the SSL application must
enable the use of the SSL code in such an early bootstage as
- needed to setup the distribution, however this will make it
+ needed to set up the distribution. However, this makes it
impossible to soft upgrade the SSL application.</p></note>
</section>
<section>
- <title>Specifying distribution module for net_kernel</title>
+ <title>Specifying Distribution Module for net_kernel</title>
<p>The distribution module for SSL is named <c>inet_tls_dist</c>
- and is specified on the command line with the <c>-proto_dist</c>
- option. The argument to <c>-proto_dist</c> should be the module
- name without the <c>_dist</c> suffix, so this distribution
+ and is specified on the command line with option <c>-proto_dist</c>.
+ The argument to <c>-proto_dist</c> is to be the module
+ name without suffix <c>_dist</c>. So, this distribution
module is specified with <c>-proto_dist inet_tls</c> on the
command line.</p>
- <p></p>
- <p>Extending the command line from above gives us the following:</p>
+ <p>Extending the command line gives the following:</p>
<code type="none">
$ erl -boot /home/me/ssl/start_ssl -proto_dist inet_tls </code>
-<p>For the distribution to actually be started, we need to give
-the emulator a name as well:</p>
+<p>For the distribution to be started, give the emulator a name as well:</p>
<code type="none">
$ erl -boot /home/me/ssl/start_ssl -proto_dist inet_tls -sname ssl_test
Erlang (BEAM) emulator version 5.0 [source]
Eshell V5.0 (abort with ^G)
(ssl_test@myhost)1> </code>
- <p>Note however that a node started in this way will refuse to talk
- to other nodes, as no ssl parameters are supplied
- (see below).</p>
+
+ <p>However, a node started in this way refuses to talk
+ to other nodes, as no SSL parameters are supplied
+ (see the next section).</p>
</section>
<section>
- <title>Specifying SSL options</title> <p>For SSL to work, at least
- a public key and certificate needs to be specified for the server
- side. In the following example the PEM-files consists of two
- entries the servers certificate and its private key.</p>
-
- <p>On the <c>erl</c> command line one can specify options that the
- SSL distribution will add when creating a socket.</p>
-
- <p>One can specify the simpler SSL options certfile, keyfile,
- password, cacertfile, verify, reuse_sessions,
- secure_renegotiate, depth, hibernate_after and ciphers (use old
- string format) by adding the prefix server_ or client_ to the
- option name. The server can also take the options dhfile and
- fail_if_no_peer_cert (also prefixed).
- <c>client_</c>-prfixed options are used when the distribution initiates a
- connection to another node and the <c>server_</c>-prefixed options are used
- when accepting a connection from a remote node. </p>
-
- <p> More complex options such as verify_fun are not available at
- the moment but a mechanism to handle such options may be added in
- a future release. </p>
-
- <p> Raw socket options such as packet and size must not be specified on
- the command line</p>.
-
- <p>The command line argument for specifying the SSL options is named
- <c>-ssl_dist_opt</c> and should be followed by pairs of
- SSL options and their values. The <c>-ssl_dist_opt</c> argument can
+ <title>Specifying SSL Options</title>
+ <p>For SSL to work, at least
+ a public key and a certificate must be specified for the server
+ side. In the following example, the PEM-files consist of two
+ entries, the server certificate and its private key.</p>
+
+ <p>On the <c>erl</c> command line you can specify options that the
+ SSL distribution adds when creating a socket.</p>
+
+ <p>The simplest SSL options in the following list can be specified
+ by adding the
+ prefix <c>server_</c> or <c>client_</c> to the option name:</p>
+ <list type="bulleted">
+ <item><c>certfile</c></item>
+ <item><c>keyfile</c></item>
+ <item><c>password</c></item>
+ <item><c>cacertfile</c></item>
+ <item><c>verify</c></item>
+ <item><c>reuse_sessions</c></item>
+ <item><c>secure_renegotiate</c></item>
+ <item><c>depth</c></item>
+ <item><c>hibernate_after</c></item>
+ <item><c>ciphers</c> (use old string format)</item>
+ </list>
+
+ <p>The server can also take the options <c>dhfile</c> and
+ <c>fail_if_no_peer_cert</c> (also prefixed).</p>
+
+ <p><c>client_</c>-prefixed options are used when the distribution
+ initiates a connection to another node. <c>server_</c>-prefixed
+ options are used when accepting a connection from a remote node.</p>
+
+ <p>More complex options, such as <c>verify_fun</c>, are currently not
+ available, but a mechanism to handle such options may be added in
+ a future release.</p>
+
+ <p>Raw socket options, such as <c>packet</c> and <c>size</c> must not
+ be specified on the command line.</p>
+
+ <p>The command-line argument for specifying the SSL options is named
+ <c>-ssl_dist_opt</c> and is to be followed by pairs of
+ SSL options and their values. Argument <c>-ssl_dist_opt</c> can
be repeated any number of times.</p>
- <p>An example command line would now look something like this
+ <p>An example command line can now look as follows
(line breaks in the command are for readability,
- they should not be there when typed):</p>
+ and are not be there when typed):</p>
<code type="none">
$ erl -boot /home/me/ssl/start_ssl -proto_dist inet_tls
-ssl_dist_opt server_certfile "/home/me/ssl/erlserver.pem"
@@ -207,20 +233,20 @@ 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 be fully functional, using SSL
+ <p>A node started in this way is fully functional, using SSL
as the distribution protocol.</p>
</section>
<section>
- <title>Setting up environment to always use SSL</title>
- <p>A convenient way to specify arguments to Erlang is to use the
- <c>ERL_FLAGS</c> environment variable. All the flags needed to
- use SSL distribution can be specified in that variable and will
- then be interpreted as command line arguments for all
+ <title>Setting up Environment to Always Use SSL</title>
+ <p>A convenient way to specify arguments to Erlang is to use environment
+ variable <c>ERL_FLAGS</c>. All the flags needed to
+ use the SSL distribution can be specified in that variable and are
+ then interpreted as command-line arguments for all
subsequent invocations of Erlang.</p>
- <p></p>
- <p>In a Unix (Bourne) shell it could look like this (line breaks for
- readability, they should not be there when typed):</p>
+
+ <p>In a Unix (Bourne) shell, it can look as follows (line breaks are for
+ readability, they are not to be there when typed):</p>
<code type="none">
$ ERL_FLAGS="-boot /home/me/ssl/start_ssl -proto_dist inet_tls
-ssl_dist_opt server_certfile /home/me/ssl/erlserver.pem
@@ -240,7 +266,8 @@ Eshell V5.0 (abort with ^G)
{ssl_dist_opt,["server_secure_renegotiate","true",
"client_secure_renegotiate","true"]
{home,["/home/me"]}] </code>
+
<p>The <c>init:get_arguments()</c> call verifies that the correct
- arguments are supplied to the emulator. </p>
+ arguments are supplied to the emulator.</p>
</section>
</chapter>
diff --git a/lib/ssl/doc/src/ssl_introduction.xml b/lib/ssl/doc/src/ssl_introduction.xml
new file mode 100644
index 0000000000..64607a393a
--- /dev/null
+++ b/lib/ssl/doc/src/ssl_introduction.xml
@@ -0,0 +1,53 @@
+<?xml version="1.0" encoding="utf-8" ?>
+<!DOCTYPE chapter SYSTEM "chapter.dtd">
+
+<chapter>
+ <header>
+ <copyright>
+ <year>2015</year>
+ <year>2015</year>
+ <holder>Ericsson AB, All Rights Reserved</holder>
+ </copyright>
+ <legalnotice>
+ The contents of this file are subject to the Erlang Public License,
+ Version 1.1, (the "License"); you may not use this file except in
+ compliance with the License. You should have received a copy of the
+ Erlang Public License along with this software. If not, it can be
+ retrieved online at http://www.erlang.org/.
+
+ Software distributed under the License is distributed on an "AS IS"
+ basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+ the License for the specific language governing rights and limitations
+ under the License.
+
+ The Initial Developer of the Original Code is Ericsson AB.
+ </legalnotice>
+
+ <title>Introduction</title>
+ <prepared>OTP team</prepared>
+ <docno></docno>
+ <date>2015-03-05</date>
+ <rev>A</rev>
+ <file>ssl_introduction.xml</file>
+ </header>
+
+ <section>
+ <title>Purpose</title>
+ <p>Transport Layer Security (TLS) and its predecessor, the Secure
+ Sockets Layer (SSL), are cryptographic protocols designed to
+ provide communications security over a computer network. The protocols use
+ use X.509 certificates and hence public key (asymmetric) cryptography to
+ authenticate the counterpart with whom they communicate,
+ and to exchange a symmetric key for payload encryption. The protocol provides
+ data/message confidentiality (encryption), integrity (through message authentication code checks)
+ and host verification (through certificate path validation).</p>
+ </section>
+
+ <section>
+ <title>Prerequisites</title>
+ <p>It is assumed that the reader is familiar with the Erlang
+ programming language, the concepts of OTP, and has a basic
+ understanding of SSL/TLS.</p>
+ </section>
+
+</chapter>
diff --git a/lib/ssl/doc/src/ssl_protocol.xml b/lib/ssl/doc/src/ssl_protocol.xml
index 80d9cc4ee8..cc49515066 100644
--- a/lib/ssl/doc/src/ssl_protocol.xml
+++ b/lib/ssl/doc/src/ssl_protocol.xml
@@ -4,7 +4,7 @@
<chapter>
<header>
<copyright>
- <year>2003</year><year>2013</year>
+ <year>2003</year><year>2015</year>
<holder>Ericsson AB. All Rights Reserved.</holder>
</copyright>
<legalnotice>
@@ -21,33 +21,42 @@
</legalnotice>
- <title>Transport Layer Security (TLS) and its predecessor, Secure Socket Layer (SSL)</title>
+ <title>TLS and its Predecessor, SSL</title>
+ <prepared></prepared>
+ <responsible></responsible>
+ <docno></docno>
+ <approved></approved>
+ <checked></checked>
+ <date></date>
+ <rev></rev>
<file>ssl_protocol.xml</file>
</header>
- <p>The erlang SSL application currently implements the protocol SSL/TLS
- for currently supported versions see <seealso marker="ssl">ssl(3)</seealso>
+ <p>The Erlang SSL application implements the SSL/TLS protocol
+ for the currently supported versions, see the
+ <seealso marker="ssl">ssl(3)</seealso> manual page.
</p>
- <p>By default erlang SSL is run over the TCP/IP protocol even
- though you could plug in any other reliable transport protocol
- with the same API as gen_tcp.</p>
+ <p>By default SSL/TLS is run over the TCP/IP protocol even
+ though you can plug in any other reliable transport protocol
+ with the same Application Programming Interface (API) as the
+ <c>gen_tcp</c> module in Kernel.</p>
- <p>If a client and server wants to use an upgrade mechanism, such as
- defined by RFC2817, to upgrade a regular TCP/IP connection to an SSL
- connection the erlang SSL API supports this. This can be useful for
- things such as supporting HTTP and HTTPS on the same port and
+ <p>If a client and a server wants to use an upgrade mechanism, such as
+ defined by RFC 2817, to upgrade a regular TCP/IP connection to an SSL
+ connection, this is supported by the Erlang SSL application API. This can be
+ useful for, for example, supporting HTTP and HTTPS on the same port and
implementing virtual hosting.
</p>
<section>
- <title>Security overview</title>
+ <title>Security Overview</title>
- <p>To achieve authentication and privacy the client and server will
- perform a TLS Handshake procedure before transmitting or receiving
- any data. During the handshake they agree on a protocol version and
- cryptographic algorithms, they generate shared secrets using public
- key cryptographics and optionally authenticate each other with
+ <p>To achieve authentication and privacy, the client and server
+ perform a TLS handshake procedure before transmitting or receiving
+ any data. During the handshake, they agree on a protocol version and
+ cryptographic algorithms, generate shared secrets using public
+ key cryptographies, and optionally authenticate each other with
digital certificates.</p>
</section>
@@ -55,20 +64,21 @@
<title>Data Privacy and Integrity</title>
<p>A <em>symmetric key</em> algorithm has one key only. The key is
- used for both encryption and decryption. These algorithms are fast
- compared to public key algorithms (using two keys, a public and a
- private one) and are therefore typically used for encrypting bulk
+ used for both encryption and decryption. These algorithms are fast,
+ compared to public key algorithms (using two keys, one public and one
+ private) and are therefore typically used for encrypting bulk
data.
</p>
<p>The keys for the symmetric encryption are generated uniquely
for each connection and are based on a secret negotiated
- in the TLS handshake. </p>
+ in the TLS handshake.</p>
- <p>The TLS handshake protocol and data transfer is run on top of
- the TLS Record Protocol that uses a keyed-hash MAC (Message
- Authenticity Code), or HMAC, to protect the message's data
- integrity. From the TLS RFC "A Message Authentication Code is a
+ <p>The TLS handshake protocol and data transfer is run on top of
+ the TLS Record Protocol, which uses a keyed-hash Message
+ Authenticity Code (MAC), or a Hash-based MAC (HMAC),
+ to protect the message data
+ integrity. From the TLS RFC: "A Message Authentication Code is a
one-way hash computed from a message and some secret data. It is
difficult to forge without knowing the secret data. Its purpose is
to detect if the message has been altered."
@@ -82,40 +92,43 @@
passport. The holder of the certificate is called the
<em>subject</em>. The certificate is signed
with the private key of the issuer of the certificate. A chain
- of trust is build by having the issuer in its turn being
- certified by another certificate and so on until you reach the
- so called root certificate that is self signed i.e. issued
+ of trust is built by having the issuer in its turn being
+ certified by another certificate, and so on, until you reach the
+ so called root certificate, which is self-signed, that is, issued
by itself.</p>
- <p>Certificates are issued by <em>certification
- authorities</em> (<em>CA</em>s) only. There are a handful of
- top CAs in the world that issue root certificates. You can
- examine the certificates of several of them by clicking
+ <p>Certificates are issued by Certification Authorities (CAs) only.
+ A handful of top CAs in the world issue root certificates. You can
+ examine several of these certificates by clicking
through the menus of your web browser.
</p>
</section>
<section>
- <title>Authentication of Sender</title>
+ <title>Peer Authentication</title>
- <p>Authentication of the sender is done by public key path
- validation as defined in RFC 3280. Simplified that means that
- each certificate in the certificate chain is issued by the one
- before, the certificates attributes are valid ones, and the
- root cert is a trusted cert that is present in the trusted
- certs database kept by the peer.</p>
+ <p>Authentication of the peer is done by public key path
+ validation as defined in RFC 3280. This means basically
+ the following:</p>
+ <list type="bulleted">
+ <item>Each certificate in the certificate chain is issued by the
+ previous one.</item>
+ <item>The certificates attributes are valid.</item>
+ <item>The root certificate is a trusted certificate that is present
+ in the trusted certificate database kept by the peer.</item>
+ </list>
- <p>The server will always send a certificate chain as part of
- the TLS handshake, but the client will only send one if
- the server requests it. If the client does not have
- an appropriate certificate it may send an "empty" certificate
+ <p>The server always sends a certificate chain as part of
+ the TLS handshake, but the client only sends one if requested
+ by the server. If the client does not have
+ an appropriate certificate, it can send an "empty" certificate
to the server.</p>
- <p>The client may choose to accept some path evaluation errors
- for instance a web browser may ask the user if they want to
- accept an unknown CA root certificate. The server, if it request
- a certificate, will on the other hand not accept any path validation
- errors. It is configurable if the server should accept
+ <p>The client can choose to accept some path evaluation errors,
+ for example, a web browser can ask the user whether to
+ accept an unknown CA root certificate. The server, if it requests
+ a certificate, does however not accept any path validation
+ errors. It is configurable if the server is to accept
or reject an "empty" certificate as response to
a certificate request.</p>
</section>
@@ -123,25 +136,24 @@
<section>
<title>TLS Sessions</title>
- <p>From the TLS RFC "A TLS session is an association between a
- client and a server. Sessions are created by the handshake
+ <p>From the TLS RFC: "A TLS session is an association between a
+ client and a server. Sessions are created by the handshake
protocol. Sessions define a set of cryptographic security
parameters, which can be shared among multiple
connections. Sessions are used to avoid the expensive negotiation
of new security parameters for each connection."</p>
<p>Session data is by default kept by the SSL application in a
- memory storage hence session data will be lost at application
- restart or takeover. Users may define their own callback module
+ memory storage, hence session data is lost at application
+ restart or takeover. Users can define their own callback module
to handle session data storage if persistent data storage is
- required. Session data will also be invalidated after 24 hours
- from it was saved, for security reasons. It is of course
- possible to configure the amount of time the session data should be
- saved.</p>
+ required. Session data is also invalidated after 24 hours
+ from it was saved, for security reasons. The amount of time the
+ session data is to be saved can be configured.</p>
- <p>SSL clients will by default try to reuse an available session,
- SSL servers will by default agree to reuse sessions when clients
- ask to do so.</p>
+ <p>By default the SSL clients try to reuse an available session and
+ by default the SSL servers agree to reuse sessions when clients
+ ask for it.</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 9f87d31e90..28b5f4ce23 100644
--- a/lib/ssl/doc/src/ssl_session_cache_api.xml
+++ b/lib/ssl/doc/src/ssl_session_cache_api.xml
@@ -21,42 +21,54 @@
</legalnotice>
<title>ssl</title>
+ <prepared></prepared>
+ <docno></docno>
+ <date></date>
+ <rev></rev>
<file>ssl_session_cache_api.xml</file>
</header>
<module>ssl_session_cache_api</module>
- <modulesummary>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.</modulesummary>
+ <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>
<section>
- <title>Common Data Types</title>
+ <title>DATA TYPES</title>
- <p>The following data types are used in the functions below:
- </p>
+ <p>The following data types are used in the functions for
+ <c>ssl_session_cache_api</c>:</p>
- <p><c>cache_ref() = opaque()</c></p>
-
- <p><c>key() = {partialkey(), session_id()}</c></p>
-
- <p><c>partialkey() = opaque()</c></p>
-
- <p><c>session_id() = binary()</c></p>
+ <taglist>
+ <tag><c>cache_ref() =</c></tag>
+ <item><p><c>opaque()</c></p></item>
+
+ <tag><c>key() =</c></tag>
+ <item><p><c>{partialkey(), session_id()}</c></p></item>
+
+ <tag><c>partialkey() =</c></tag>
+ <item><p><c>opaque()</c></p></item>
+
+ <tag><c>session_id() =</c></tag>
+ <item><p><c>binary()</c></p></item>
+
+ <tag><c>session()</c> =</tag>
+ <item><p><c>opaque()</c></p></item>
+ </taglist>
- <p><c>session() = opaque()</c></p>
-
</section>
<funcs>
<func>
<name>delete(Cache, Key) -> _</name>
- <fsummary></fsummary>
+ <fsummary>Deletes a cache entry.</fsummary>
<type>
- <v> Cache = cache_ref()</v>
- <v> Key = key()</v>
+ <v>Cache = cache_ref()</v>
+ <v>Key = key()</v>
</type>
<desc>
- <p> Deletes a cache entry. Will only be called from the cache
+ <p>Deletes a cache entry. Is only called from the cache
handling process.
</p>
</desc>
@@ -69,49 +81,50 @@
<v></v>
</type>
<desc>
- <p>Calls Fun(Elem, AccIn) on successive elements of the
- cache, starting with AccIn == Acc0. Fun/2 must return a new
- accumulator which is passed to the next call. The function returns
- the final value of the accumulator. Acc0 is returned if the cache is
- empty.
+ <p>Calls <c>Fun(Elem, AccIn)</c> on successive elements of the
+ cache, starting with <c>AccIn == Acc0</c>. <c>Fun/2</c> must
+ return a new accumulator, which is passed to the next call.
+ The function returns the final value of the accumulator.
+ <c>Acc0</c> is returned if the cache is empty.
</p>
</desc>
</func>
<func>
<name>init(Args) -> opaque() </name>
- <fsummary>Return cache reference</fsummary>
+ <fsummary>Returns cache reference.</fsummary>
<type>
<v>Args = proplists:proplist()</v>
- <d>Will always include the property {role, client | server}. Currently this
- is the only predefined property, there may also be user defined properties.
- <seealso marker="ssl_app"> See also application environment variable
- session_cb_init_args</seealso>
- </d>
</type>
<desc>
+ <p>Includes property <c>{role, client | server}</c>.
+ Currently this is the only predefined property,
+ there can also be user-defined properties. See also
+ application environment variable
+ <seealso marker="ssl_app">session_cb_init_args</seealso>.
+ </p>
<p>Performs possible initializations of the cache and returns
- a reference to it that will be used as parameter to the other
- API functions. Will be called by the cache handling processes
- init function, hence putting the same requirements on it as a
- normal process init function. Note that this function will be
- called twice when starting the ssl application, once with the
- role client and once with the role server, as the ssl application
- must be prepared to take on both roles.
+ a reference to it that is used as parameter to the other
+ API functions. Is called by the cache handling processes
+ <c>init</c> function, hence putting the same requirements on it
+ as a normal process <c>init</c> function. This function is
+ called twice when starting the SSL application, once with
+ the role client and once with the role server, as the SSL
+ application must be prepared to take on both roles.
</p>
</desc>
</func>
<func>
<name>lookup(Cache, Key) -> Entry</name>
- <fsummary> Looks up a cache entry.</fsummary>
+ <fsummary>Looks up a cache entry.</fsummary>
<type>
- <v> Cache = cache_ref()</v>
- <v> Key = key()</v>
- <v> Entry = session() | undefined </v>
+ <v>Cache = cache_ref()</v>
+ <v>Key = key()</v>
+ <v>Entry = session() | undefined</v>
</type>
<desc>
- <p>Looks up a cache entry. Should be callable from any
+ <p>Looks up a cache entry. Is to be callable from any
process.
</p>
</desc>
@@ -119,14 +132,14 @@
<func>
<name>select_session(Cache, PartialKey) -> [session()]</name>
- <fsummary>Selects a sessions that could be reused.</fsummary>
+ <fsummary>Selects sessions that can be reused.</fsummary>
<type>
- <v> Cache = cache_ref()</v>
- <v> PartialKey = partialkey()</v>
- <v> Session = session()</v>
+ <v>Cache = cache_ref()</v>
+ <v>PartialKey = partialkey()</v>
+ <v>Session = session()</v>
</type>
<desc>
- <p>Selects a sessions that could be reused. Should be callable
+ <p>Selects sessions that can be reused. Is to be callable
from any process.
</p>
</desc>
@@ -137,7 +150,7 @@
<fsummary>Called by the process that handles the cache when it
is about to terminate.</fsummary>
<type>
- <v>Cache = term() - as returned by init/0</v>
+ <v>Cache = term() - as returned by init/0</v>
</type>
<desc>
<p>Takes care of possible cleanup that is needed when the
@@ -148,15 +161,15 @@
<func>
<name>update(Cache, Key, Session) -> _</name>
- <fsummary> Caches a new session or updates a already cached one.</fsummary>
+ <fsummary>Caches a new session or updates an already cached one.</fsummary>
<type>
- <v> Cache = cache_ref()</v>
- <v> Key = key()</v>
- <v> Session = session()</v>
+ <v>Cache = cache_ref()</v>
+ <v>Key = key()</v>
+ <v>Session = session()</v>
</type>
<desc>
- <p> Caches a new session or updates a already cached one. Will
- only be called from the cache handling process.
+ <p>Caches a new session or updates an already cached one. Is
+ only called from the cache handling process.
</p>
</desc>
</func>
diff --git a/lib/ssl/doc/src/usersguide.xml b/lib/ssl/doc/src/usersguide.xml
index b1c7190085..6fce022507 100644
--- a/lib/ssl/doc/src/usersguide.xml
+++ b/lib/ssl/doc/src/usersguide.xml
@@ -23,14 +23,17 @@
<title>SSL User's Guide</title>
<prepared>OTP Team</prepared>
+ <docno></docno>
<date>2003-05-26</date>
+ <rev></rev>
<file>usersguide.sgml</file>
</header>
<description>
- <p>The <em>SSL</em> application provides secure communication over
+ <p>The Secure Socket Layer (SSL) application provides secure communication over
sockets.
</p>
</description>
+ <xi:include href="ssl_introduction.xml"/>
<xi:include href="ssl_protocol.xml"/>
<xi:include href="using_ssl.xml"/>
<xi:include href="ssl_distribution.xml"/>
diff --git a/lib/ssl/doc/src/using_ssl.xml b/lib/ssl/doc/src/using_ssl.xml
index cce388d02a..dbbc1aa9d3 100644
--- a/lib/ssl/doc/src/using_ssl.xml
+++ b/lib/ssl/doc/src/using_ssl.xml
@@ -21,126 +21,131 @@
</legalnotice>
- <title>Using the SSL API</title>
+ <title>Using SSL API</title>
+ <prepared></prepared>
+ <responsible></responsible>
+ <docno></docno>
+ <approved></approved>
+ <checked></checked>
+ <date></date>
+ <rev></rev>
<file>using_ssl.xml</file>
</header>
-
- <section>
- <title>General information</title>
- <p>To see relevant version information for ssl you can
- call ssl:versions/0</p>
+ <p>To see relevant version information for ssl, call
+ <seealso marker="ssl:ssl#versions-0"><c>ssl:versions/0</c></seealso>
+ .</p>
- <p>To see all supported cipher suites
- call ssl:cipher_suites/0. Note that available cipher suites
- for a connection will depend on your certificate. It is also
- possible to specify a specific cipher suite(s) that you
- want your connection to use. Default is to use the strongest
- available.</p>
-
- </section>
+ <p>To see all supported cipher suites, call <seealso marker="ssl:ssl#cipher_suites-1"><c>ssl:cipher_suites(all)</c> </seealso>.
+ The available cipher suites for a connection depend on your certificate.
+ Specific cipher suites that you want your connection to use can also be
+ specified. Default is to use the strongest available.</p>
<section>
- <title>Setting up connections</title>
+ <title>Setting up Connections</title>
- <p>Here follows some small example of how to set up client/server connections
- using the erlang shell. The returned value of the sslsocket has been abbreviated with
- <c>[...]</c> as it can be fairly large and is opaque.</p>
+ <p>This section shows a small example of how to set up client/server connections
+ using the Erlang shell. The returned value of the <c>sslsocket</c> is abbreviated
+ with <c>[...]</c> as it can be fairly large and is opaque.</p>
<section>
- <title>Minmal example</title>
+ <title>Minimal Example</title>
- <note><p> The minimal setup is not the most secure setup of ssl.</p>
+ <note><p> The minimal setup is not the most secure setup of SSL.</p>
</note>
-
- <p> Start server side</p>
+
+ <p>To set up client/server connections:</p>
+
+ <p><em>Step 1:</em> Start the server side:</p>
<code type="erl">1 server> ssl:start().
ok</code>
- <p>Create an ssl listen socket</p>
+ <p><em>Step 2:</em> Create an SSL listen socket:</p>
<code type="erl">2 server> {ok, ListenSocket} =
ssl:listen(9999, [{certfile, "cert.pem"}, {keyfile, "key.pem"},{reuseaddr, true}]).
{ok,{sslsocket, [...]}}</code>
- <p>Do a transport accept on the ssl listen socket</p>
+ <p><em>Step 3:</em> Do a transport accept on the SSL listen socket:</p>
<code type="erl">3 server> {ok, Socket} = ssl:transport_accept(ListenSocket).
{ok,{sslsocket, [...]}}</code>
- <p>Start client side</p>
+ <p><em>Step 4:</em> Start the client side:</p>
<code type="erl">1 client> ssl:start().
ok</code>
<code type="erl">2 client> {ok, Socket} = ssl:connect("localhost", 9999, [], infinity).
{ok,{sslsocket, [...]}}</code>
- <p>Do the ssl handshake</p>
+ <p><em>Step 5:</em> Do the SSL handshake:</p>
<code type="erl">4 server> ok = ssl:ssl_accept(Socket).
ok</code>
- <p>Send a messag over ssl</p>
+ <p><em>Step 6:</em> Send a message over SSL:</p>
<code type="erl">5 server> ssl:send(Socket, "foo").
ok</code>
- <p>Flush the shell message queue to see that we got the message
- sent on the server side</p>
+ <p><em>Step 7:</em> Flush the shell message queue to see that the message
+ was sent on the server side:</p>
<code type="erl">3 client> flush().
Shell got {ssl,{sslsocket,[...]},"foo"}
ok</code>
</section>
<section>
- <title>Upgrade example</title>
+ <title>Upgrade Example</title>
- <note><p> To upgrade a TCP/IP connection to an ssl connection the
- client and server have to aggre to do so. Agreement
- may be accompliced by using a protocol such the one used by HTTP
- specified in RFC 2817.</p> </note>
+ <note><p>To upgrade a TCP/IP connection to an SSL connection, the
+ client and server must agree to do so. The agreement
+ can be accomplished by using a protocol, for example, the one used by HTTP
+ specified in RFC 2817.</p></note>
+
+ <p>To upgrade to an SSL connection:</p>
- <p>Start server side</p>
+ <p><em>Step 1:</em> Start the server side:</p>
<code type="erl">1 server> ssl:start().
ok</code>
- <p>Create a normal tcp listen socket</p>
+ <p><em>Step 2:</em> Create a normal TCP listen socket:</p>
<code type="erl">2 server> {ok, ListenSocket} = gen_tcp:listen(9999, [{reuseaddr, true}]).
{ok, #Port&lt;0.475&gt;}</code>
- <p>Accept client connection</p>
+ <p><em>Step 3:</em> Accept client connection:</p>
<code type="erl">3 server> {ok, Socket} = gen_tcp:accept(ListenSocket).
{ok, #Port&lt;0.476&gt;}</code>
- <p>Start client side</p>
+ <p><em>Step 4:</em> Start the client side:</p>
<code type="erl">1 client> ssl:start().
ok</code>
<code type="erl">2 client> {ok, Socket} = gen_tcp:connect("localhost", 9999, [], infinity).</code>
- <p>Make sure active is set to false before trying
- to upgrade a connection to an ssl connection, otherwhise
- ssl handshake messages may be deliverd to the wrong process.</p>
+ <p><em>Step 5:</em> Ensure <c>active</c> is set to <c>false</c> before trying
+ to upgrade a connection to an SSL connection, otherwise
+ SSL handshake messages can be delivered to the wrong process:</p>
<code type="erl">4 server> inet:setopts(Socket, [{active, false}]).
ok</code>
- <p>Do the ssl handshake.</p>
+ <p><em>Step 6:</em> Do the SSL handshake:</p>
<code type="erl">5 server> {ok, SSLSocket} = ssl:ssl_accept(Socket, [{cacertfile, "cacerts.pem"},
{certfile, "cert.pem"}, {keyfile, "key.pem"}]).
{ok,{sslsocket,[...]}}</code>
- <p> Upgrade to an ssl connection. Note that the client and server
- must agree upon the upgrade and the server must call
- ssl:accept/2 before the client calls ssl:connect/3.</p>
+ <p><em>Step 7:</em> Upgrade to an SSL connection. The client and server
+ must agree upon the upgrade. The server must call
+ <c>ssl:accept/2</c> before the client calls <c>ssl:connect/3.</c></p>
<code type="erl">3 client>{ok, SSLSocket} = ssl:connect(Socket, [{cacertfile, "cacerts.pem"},
{certfile, "cert.pem"}, {keyfile, "key.pem"}], infinity).
{ok,{sslsocket,[...]}}</code>
- <p>Send a messag over ssl</p>
+ <p><em>Step 8:</em> Send a message over SSL:</p>
<code type="erl">4 client> ssl:send(SSLSocket, "foo").
ok</code>
- <p>Set active true on the ssl socket</p>
+ <p><em>Step 9:</em> Set <c>active true</c> on the SSL socket:</p>
<code type="erl">4 server> ssl:setopts(SSLSocket, [{active, true}]).
ok</code>
- <p>Flush the shell message queue to see that we got the message
- sent on the client side</p>
+ <p><em>Step 10:</em> Flush the shell message queue to see that the message
+ was sent on the client side:</p>
<code type="erl">5 server> flush().
Shell got {ssl,{sslsocket,[...]},"foo"}
ok</code>
diff --git a/lib/ssl/src/dtls_connection.erl b/lib/ssl/src/dtls_connection.erl
index f177a8610d..610e2c4e41 100644
--- a/lib/ssl/src/dtls_connection.erl
+++ b/lib/ssl/src/dtls_connection.erl
@@ -228,9 +228,9 @@ hello(Hello,
case dtls_handshake:hello(Hello, SslOptions, ConnectionStates0, Renegotiation) of
#alert{} = Alert ->
handle_own_alert(Alert, ReqVersion, hello, State);
- {Version, NewId, ConnectionStates, NextProtocol} ->
+ {Version, NewId, ConnectionStates, ProtoExt, Protocol} ->
ssl_connection:handle_session(Hello,
- Version, NewId, ConnectionStates, NextProtocol, State)
+ Version, NewId, ConnectionStates, ProtoExt, Protocol, State)
end;
hello(Msg, State) ->
diff --git a/lib/ssl/src/dtls_handshake.erl b/lib/ssl/src/dtls_handshake.erl
index 31d525b295..30381df050 100644
--- a/lib/ssl/src/dtls_handshake.erl
+++ b/lib/ssl/src/dtls_handshake.erl
@@ -181,8 +181,8 @@ handle_server_hello_extensions(Version, SessionId, Random, CipherSuite,
SslOpt, ConnectionStates0, Renegotiation) of
#alert{} = Alert ->
Alert;
- {ConnectionStates, Protocol} ->
- {Version, SessionId, ConnectionStates, Protocol}
+ {ConnectionStates, ProtoExt, Protocol} ->
+ {Version, SessionId, ConnectionStates, ProtoExt, Protocol}
end.
dtls_fragment(Mss, MsgType, Len, MsgSeq, Bin, Offset, Acc)
diff --git a/lib/ssl/src/ssl.app.src b/lib/ssl/src/ssl.app.src
index 955875fa95..be8ef6f85f 100644
--- a/lib/ssl/src/ssl.app.src
+++ b/lib/ssl/src/ssl.app.src
@@ -53,7 +53,7 @@
{applications, [crypto, public_key, kernel, stdlib]},
{env, []},
{mod, {ssl_app, []}},
- {runtime_dependencies, ["stdlib-2.0","public_key-0.22","kernel-3.0",
- "erts-6.0","crypto-3.3"]}]}.
+ {runtime_dependencies, ["stdlib-2.0","public_key-1.0","kernel-3.0",
+ "erts-6.0","crypto-3.3", "inets-5.10.7"]}]}.
diff --git a/lib/ssl/src/ssl.erl b/lib/ssl/src/ssl.erl
index 623fa92121..225a9be66f 100644
--- a/lib/ssl/src/ssl.erl
+++ b/lib/ssl/src/ssl.erl
@@ -38,9 +38,13 @@
%% SSL/TLS protocol handling
-export([cipher_suites/0, cipher_suites/1, suite_definition/1,
connection_info/1, versions/0, session_info/1, format_error/1,
- renegotiate/1, prf/5, negotiated_next_protocol/1]).
+ renegotiate/1, prf/5, negotiated_protocol/1, negotiated_next_protocol/1,
+ connection_information/1, connection_information/2]).
%% Misc
--export([random_bytes/1]).
+-export([random_bytes/1, handle_options/2]).
+
+-deprecated({negotiated_next_protocol, 1, next_major_release}).
+-deprecated({connection_info, 1, next_major_release}).
-include("ssl_api.hrl").
-include("ssl_internal.hrl").
@@ -284,16 +288,42 @@ controlling_process(#sslsocket{pid = {Listen,
is_pid(NewOwner) ->
Transport:controlling_process(Listen, NewOwner).
+
+%%--------------------------------------------------------------------
+-spec connection_information(#sslsocket{}) -> {ok, list()} | {error, reason()}.
+%%
+%% 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}.
+
+
+%%--------------------------------------------------------------------
+-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
+ end.
+
%%--------------------------------------------------------------------
-spec connection_info(#sslsocket{}) -> {ok, {tls_record:tls_atom_version(), ssl_cipher:erl_cipher_suite()}} |
{error, reason()}.
%%
%% Description: Returns ssl protocol and cipher used for the connection
%%--------------------------------------------------------------------
-connection_info(#sslsocket{pid = Pid}) when is_pid(Pid) ->
- ssl_connection:info(Pid);
-connection_info(#sslsocket{pid = {Listen, _}}) when is_port(Listen) ->
- {error, enotconn}.
+connection_info(#sslsocket{} = SSLSocket) ->
+ case connection_information(SSLSocket) of
+ {ok, Result} ->
+ {ok, {proplists:get_value(protocol, Result), proplists:get_value(cipher_suite, Result)}};
+ Error ->
+ Error
+ end.
%%--------------------------------------------------------------------
-spec peername(#sslsocket{}) -> {ok, {inet:ip_address(), inet:port_number()}} | {error, reason()}.
@@ -330,13 +360,27 @@ suite_definition(S) ->
{KeyExchange, Cipher, Hash}.
%%--------------------------------------------------------------------
+-spec negotiated_protocol(#sslsocket{}) -> {ok, binary()} | {error, reason()}.
+%%
+%% Description: Returns the protocol that has been negotiated. If no
+%% protocol has been negotiated will return {error, protocol_not_negotiated}
+%%--------------------------------------------------------------------
+negotiated_protocol(#sslsocket{pid = Pid}) ->
+ ssl_connection:negotiated_protocol(Pid).
+
+%%--------------------------------------------------------------------
-spec negotiated_next_protocol(#sslsocket{}) -> {ok, binary()} | {error, reason()}.
%%
%% Description: Returns the next protocol that has been negotiated. If no
%% protocol has been negotiated will return {error, next_protocol_not_negotiated}
%%--------------------------------------------------------------------
-negotiated_next_protocol(#sslsocket{pid = Pid}) ->
- ssl_connection:negotiated_next_protocol(Pid).
+negotiated_next_protocol(Socket) ->
+ case negotiated_protocol(Socket) of
+ {error, protocol_not_negotiated} ->
+ {error, next_protocol_not_negotiated};
+ Res ->
+ Res
+ end.
%%--------------------------------------------------------------------
-spec cipher_suites(erlang | openssl | all) -> [ssl_cipher:erl_cipher_suite()] |
@@ -644,6 +688,10 @@ handle_options(Opts0) ->
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),
+ alpn_advertised_protocols =
+ handle_option(alpn_advertised_protocols, Opts, undefined),
+ alpn_preferred_protocols =
+ handle_option(alpn_preferred_protocols, Opts, undefined),
next_protocols_advertised =
handle_option(next_protocols_advertised, Opts, undefined),
next_protocol_selector =
@@ -651,6 +699,8 @@ handle_options(Opts0) ->
handle_option(client_preferred_next_protocols, Opts, undefined)),
log_alert = handle_option(log_alert, Opts, true),
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),
protocol = proplists:get_value(protocol, Opts, tls),
padding_check = proplists:get_value(padding_check, Opts, true),
@@ -667,7 +717,8 @@ handle_options(Opts0) ->
user_lookup_fun, psk_identity, srp_identity, ciphers,
reuse_session, reuse_sessions, ssl_imp,
cb_info, renegotiate_at, secure_renegotiate, hibernate_after,
- erl_dist, next_protocols_advertised,
+ erl_dist, alpn_advertised_protocols, sni_hosts, sni_fun,
+ alpn_preferred_protocols, next_protocols_advertised,
client_preferred_next_protocols, log_alert,
server_name_indication, honor_cipher_order, padding_check, crl_check, crl_cache,
fallback],
@@ -683,6 +734,18 @@ handle_options(Opts0) ->
inet_user = SockOpts, transport_info = CbInfo, connection_cb = ConnetionCb
}}.
+handle_option(sni_fun, Opts, Default) ->
+ OptFun = validate_option(sni_fun,
+ proplists:get_value(sni_fun, Opts, Default)),
+ OptHosts = proplists:get_value(sni_hosts, Opts, undefined),
+ case {OptFun, OptHosts} of
+ {Default, _} ->
+ Default;
+ {_, undefined} ->
+ OptFun;
+ _ ->
+ throw({error, {conflict_options, [sni_fun, sni_hosts]}})
+ end;
handle_option(OptionName, Opts, Default) ->
validate_option(OptionName,
proplists:get_value(OptionName, Opts, Default)).
@@ -803,6 +866,20 @@ validate_option(hibernate_after, Value) when is_integer(Value), Value >= 0 ->
Value;
validate_option(erl_dist,Value) when is_boolean(Value) ->
Value;
+validate_option(Opt, Value)
+ when Opt =:= alpn_advertised_protocols orelse Opt =:= alpn_preferred_protocols,
+ is_list(Value) ->
+ case tls_record:highest_protocol_version([]) of
+ {3,0} ->
+ throw({error, {options, {not_supported_in_sslv3, {Opt, Value}}}});
+ _ ->
+ validate_binary_list(Opt, Value),
+ Value
+ end;
+validate_option(Opt, Value)
+ when Opt =:= alpn_advertised_protocols orelse Opt =:= alpn_preferred_protocols,
+ Value =:= undefined ->
+ undefined;
validate_option(client_preferred_next_protocols = Opt, {Precedence, PreferredProtocols} = Value)
when is_list(PreferredProtocols) ->
case tls_record:highest_protocol_version([]) of
@@ -846,6 +923,20 @@ validate_option(server_name_indication, disable) ->
disable;
validate_option(server_name_indication, undefined) ->
undefined;
+validate_option(sni_hosts, []) ->
+ [];
+validate_option(sni_hosts, [{Hostname, SSLOptions} | Tail]) when is_list(Hostname) ->
+ RecursiveSNIOptions = proplists:get_value(sni_hosts, SSLOptions, undefined),
+ case RecursiveSNIOptions of
+ undefined ->
+ [{Hostname, validate_options(SSLOptions)} | validate_option(sni_hosts, Tail)];
+ _ ->
+ throw({error, {options, {sni_hosts, RecursiveSNIOptions}}})
+ end;
+validate_option(sni_fun, undefined) ->
+ undefined;
+validate_option(sni_fun, Fun) when is_function(Fun) ->
+ Fun;
validate_option(honor_cipher_order, Value) when is_boolean(Value) ->
Value;
validate_option(padding_check, Value) when is_boolean(Value) ->
@@ -861,6 +952,12 @@ validate_option(crl_cache, {Cb, {_Handle, Options}} = Value) when is_atom(Cb) an
validate_option(Opt, Value) ->
throw({error, {options, {Opt, Value}}}).
+
+validate_options([]) ->
+ [];
+validate_options([{Opt, Value} | Tail]) ->
+ [{Opt, validate_option(Opt, Value)} | validate_options(Tail)].
+
validate_npn_ordering(client) ->
ok;
validate_npn_ordering(server) ->
@@ -1131,6 +1228,10 @@ new_ssl_options([{secure_renegotiate, Value} | Rest], #ssl_options{} = Opts, Rec
new_ssl_options(Rest, Opts#ssl_options{secure_renegotiate = validate_option(secure_renegotiate, Value)}, RecordCB);
new_ssl_options([{hibernate_after, Value} | Rest], #ssl_options{} = Opts, RecordCB) ->
new_ssl_options(Rest, Opts#ssl_options{hibernate_after = validate_option(hibernate_after, Value)}, RecordCB);
+new_ssl_options([{alpn_advertised_protocols, Value} | Rest], #ssl_options{} = Opts, RecordCB) ->
+ new_ssl_options(Rest, Opts#ssl_options{alpn_advertised_protocols = validate_option(alpn_advertised_protocols, Value)}, RecordCB);
+new_ssl_options([{alpn_preferred_protocols, Value} | Rest], #ssl_options{} = Opts, RecordCB) ->
+ new_ssl_options(Rest, Opts#ssl_options{alpn_preferred_protocols = validate_option(alpn_preferred_protocols, Value)}, RecordCB);
new_ssl_options([{next_protocols_advertised, Value} | Rest], #ssl_options{} = Opts, RecordCB) ->
new_ssl_options(Rest, Opts#ssl_options{next_protocols_advertised = validate_option(next_protocols_advertised, Value)}, RecordCB);
new_ssl_options([{client_preferred_next_protocols, Value} | Rest], #ssl_options{} = Opts, RecordCB) ->
diff --git a/lib/ssl/src/ssl_alert.erl b/lib/ssl/src/ssl_alert.erl
index 9e372f739a..c46facb75d 100644
--- a/lib/ssl/src/ssl_alert.erl
+++ b/lib/ssl/src/ssl_alert.erl
@@ -163,5 +163,7 @@ description_txt(?UNKNOWN_PSK_IDENTITY) ->
"unknown psk identity";
description_txt(?INAPPROPRIATE_FALLBACK) ->
"inappropriate fallback";
+description_txt(?NO_APPLICATION_PROTOCOL) ->
+ "no application protocol";
description_txt(Enum) ->
lists:flatten(io_lib:format("unsupported/unknown alert: ~p", [Enum])).
diff --git a/lib/ssl/src/ssl_alert.hrl b/lib/ssl/src/ssl_alert.hrl
index a3619e4a35..70b7523975 100644
--- a/lib/ssl/src/ssl_alert.hrl
+++ b/lib/ssl/src/ssl_alert.hrl
@@ -69,6 +69,8 @@
%% bad_certificate_hash_value(114),
%% RFC 4366
%% unknown_psk_identity(115),
+%% RFC 7301
+%% no_application_protocol(120),
%% (255)
%% } AlertDescription;
@@ -103,6 +105,7 @@
-define(BAD_CERTIFICATE_STATUS_RESPONSE, 113).
-define(BAD_CERTIFICATE_HASH_VALUE, 114).
-define(UNKNOWN_PSK_IDENTITY, 115).
+-define(NO_APPLICATION_PROTOCOL, 120).
-define(ALERT_REC(Level,Desc), #alert{level=Level,description=Desc,where={?FILE, ?LINE}}).
diff --git a/lib/ssl/src/ssl_api.hrl b/lib/ssl/src/ssl_api.hrl
index 22185ff60a..78127eeafa 100644
--- a/lib/ssl/src/ssl_api.hrl
+++ b/lib/ssl/src/ssl_api.hrl
@@ -49,6 +49,8 @@
{srp_identity, {string(), string()}} |
{ciphers, ciphers()} | {ssl_imp, ssl_imp()} | {reuse_sessions, boolean()} |
{reuse_session, fun()} | {hibernate_after, integer()|undefined} |
+ {alpn_advertised_protocols, [binary()]} |
+ {alpn_preferred_protocols, [binary()]} |
{next_protocols_advertised, list(binary())} |
{client_preferred_next_protocols, binary(), client | server, list(binary())}.
diff --git a/lib/ssl/src/ssl_connection.erl b/lib/ssl/src/ssl_connection.erl
index 08d0145aa7..64fa7bab0d 100644
--- a/lib/ssl/src/ssl_connection.erl
+++ b/lib/ssl/src/ssl_connection.erl
@@ -41,11 +41,12 @@
%% User Events
-export([send/2, recv/3, close/1, shutdown/2,
- new_user/2, get_opts/2, set_opts/2, info/1, session_info/1,
- peer_certificate/1, renegotiation/1, negotiated_next_protocol/1, prf/5
+ new_user/2, get_opts/2, set_opts/2, session_info/1,
+ peer_certificate/1, renegotiation/1, negotiated_protocol/1, prf/5,
+ connection_information/1
]).
--export([handle_session/6]).
+-export([handle_session/7]).
%% SSL FSM state functions
-export([hello/3, abbreviated/3, certify/3, cipher/3, connection/3]).
@@ -161,6 +162,14 @@ recv(Pid, Length, Timeout) ->
sync_send_all_state_event(Pid, {recv, Length, Timeout}).
%%--------------------------------------------------------------------
+-spec connection_information(pid()) -> {ok, list()} | {error, reason()}.
+%%
+%% Description: Get the SNI hostname
+%%--------------------------------------------------------------------
+connection_information(Pid) when is_pid(Pid) ->
+ sync_send_all_state_event(Pid, connection_information).
+
+%%--------------------------------------------------------------------
-spec close(pid()) -> ok | {error, reason()}.
%%
%% Description: Close an ssl connection
@@ -191,12 +200,12 @@ new_user(ConnectionPid, User) ->
sync_send_all_state_event(ConnectionPid, {new_user, User}).
%%--------------------------------------------------------------------
--spec negotiated_next_protocol(pid()) -> {ok, binary()} | {error, reason()}.
+-spec negotiated_protocol(pid()) -> {ok, binary()} | {error, reason()}.
%%
%% Description: Returns the negotiated protocol
%%--------------------------------------------------------------------
-negotiated_next_protocol(ConnectionPid) ->
- sync_send_all_state_event(ConnectionPid, negotiated_next_protocol).
+negotiated_protocol(ConnectionPid) ->
+ sync_send_all_state_event(ConnectionPid, negotiated_protocol).
%%--------------------------------------------------------------------
-spec get_opts(pid(), list()) -> {ok, list()} | {error, reason()}.
@@ -214,14 +223,6 @@ set_opts(ConnectionPid, Options) ->
sync_send_all_state_event(ConnectionPid, {set_opts, Options}).
%%--------------------------------------------------------------------
--spec info(pid()) -> {ok, {atom(), tuple()}} | {error, reason()}.
-%%
-%% Description: Returns ssl protocol and cipher used for the connection
-%%--------------------------------------------------------------------
-info(ConnectionPid) ->
- sync_send_all_state_event(ConnectionPid, info).
-
-%%--------------------------------------------------------------------
-spec session_info(pid()) -> {ok, list()} | {error, reason()}.
%%
%% Description: Returns info about the ssl session
@@ -258,27 +259,26 @@ prf(ConnectionPid, Secret, Label, Seed, WantedLength) ->
handle_session(#server_hello{cipher_suite = CipherSuite,
compression_method = Compression},
- Version, NewId, ConnectionStates, NextProtocol,
+ Version, NewId, ConnectionStates, ProtoExt, Protocol0,
#state{session = #session{session_id = OldId},
- negotiated_version = ReqVersion} = State0) ->
+ negotiated_version = ReqVersion,
+ negotiated_protocol = CurrentProtocol} = State0) ->
{KeyAlgorithm, _, _, _} =
ssl_cipher:suite_definition(CipherSuite),
PremasterSecret = make_premaster_secret(ReqVersion, KeyAlgorithm),
-
- NewNextProtocol = case NextProtocol of
- undefined ->
- State0#state.next_protocol;
- _ ->
- NextProtocol
- end,
-
+
+ {ExpectNPN, Protocol} = case Protocol0 of
+ undefined -> {false, CurrentProtocol};
+ _ -> {ProtoExt =:= npn, Protocol0}
+ end,
+
State = State0#state{key_algorithm = KeyAlgorithm,
negotiated_version = Version,
connection_states = ConnectionStates,
premaster_secret = PremasterSecret,
- expecting_next_protocol_negotiation = NextProtocol =/= undefined,
- next_protocol = NewNextProtocol},
+ expecting_next_protocol_negotiation = ExpectNPN,
+ negotiated_protocol = Protocol},
case ssl_session:is_new(OldId, NewId) of
true ->
@@ -371,7 +371,7 @@ abbreviated(#finished{verify_data = Data} = Finished,
abbreviated(#next_protocol{selected_protocol = SelectedProtocol},
#state{role = server, expecting_next_protocol_negotiation = true} = State0,
Connection) ->
- {Record, State} = Connection:next_record(State0#state{next_protocol = SelectedProtocol}),
+ {Record, State} = Connection:next_record(State0#state{negotiated_protocol = SelectedProtocol}),
Connection:next_state(abbreviated, abbreviated, Record, State#state{expecting_next_protocol_negotiation = false});
abbreviated(timeout, State, _) ->
@@ -593,7 +593,7 @@ cipher(#certificate_verify{signature = Signature, hashsign_algorithm = CertHashS
%% client must send a next protocol message if we are expecting it
cipher(#finished{}, #state{role = server, expecting_next_protocol_negotiation = true,
- next_protocol = undefined, negotiated_version = Version} = State0,
+ negotiated_protocol = undefined, negotiated_version = Version} = State0,
Connection) ->
Connection:handle_own_alert(?ALERT_REC(?FATAL,?UNEXPECTED_MESSAGE), Version, cipher, State0);
@@ -623,7 +623,7 @@ cipher(#finished{verify_data = Data} = Finished,
cipher(#next_protocol{selected_protocol = SelectedProtocol},
#state{role = server, expecting_next_protocol_negotiation = true,
expecting_finished = true} = State0, Connection) ->
- {Record, State} = Connection:next_record(State0#state{next_protocol = SelectedProtocol}),
+ {Record, State} = Connection:next_record(State0#state{negotiated_protocol = SelectedProtocol}),
Connection:next_state(cipher, cipher, Record, State#state{expecting_next_protocol_negotiation = false});
cipher(timeout, State, _) ->
@@ -759,10 +759,10 @@ handle_sync_event({get_opts, OptTags}, _From, StateName,
socket_options = SockOpts} = State) ->
OptsReply = get_socket_opts(Transport, Socket, OptTags, SockOpts, []),
{reply, OptsReply, StateName, State, get_timeout(State)};
-handle_sync_event(negotiated_next_protocol, _From, StateName, #state{next_protocol = undefined} = State) ->
- {reply, {error, next_protocol_not_negotiated}, StateName, State, get_timeout(State)};
-handle_sync_event(negotiated_next_protocol, _From, StateName, #state{next_protocol = NextProtocol} = State) ->
- {reply, {ok, NextProtocol}, StateName, State, get_timeout(State)};
+handle_sync_event(negotiated_protocol, _From, StateName, #state{negotiated_protocol = undefined} = State) ->
+ {reply, {error, protocol_not_negotiated}, StateName, State, get_timeout(State)};
+handle_sync_event(negotiated_protocol, _From, StateName, #state{negotiated_protocol = SelectedProtocol} = State) ->
+ {reply, {ok, SelectedProtocol}, StateName, State, get_timeout(State)};
handle_sync_event({set_opts, Opts0}, _From, StateName0,
#state{socket_options = Opts1,
protocol_cb = Connection,
@@ -830,13 +830,6 @@ handle_sync_event({prf, Secret, Label, Seed, WantedLength}, _, StateName,
error:Reason -> {error, Reason}
end,
{reply, Reply, StateName, State, get_timeout(State)};
-handle_sync_event(info, _, StateName,
- #state{negotiated_version = Version,
- session = #session{cipher_suite = Suite}} = State) ->
-
- AtomVersion = tls_record:protocol_version(Version),
- {reply, {ok, {AtomVersion, ssl:suite_definition(Suite)}},
- StateName, State, get_timeout(State)};
handle_sync_event(session_info, _, StateName,
#state{session = #session{session_id = Id,
cipher_suite = Suite}} = State) ->
@@ -846,7 +839,10 @@ handle_sync_event(session_info, _, StateName,
handle_sync_event(peer_certificate, _, StateName,
#state{session = #session{peer_certificate = Cert}}
= State) ->
- {reply, {ok, Cert}, StateName, State, get_timeout(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_info({ErrorTag, Socket, econnaborted}, StateName,
#state{socket = Socket, transport_cb = Transport,
@@ -1484,11 +1480,11 @@ finalize_handshake(State0, StateName, Connection) ->
next_protocol(#state{role = server} = State, _) ->
State;
-next_protocol(#state{next_protocol = undefined} = State, _) ->
+next_protocol(#state{negotiated_protocol = undefined} = State, _) ->
State;
next_protocol(#state{expecting_next_protocol_negotiation = false} = State, _) ->
State;
-next_protocol(#state{next_protocol = NextProtocol} = State0, Connection) ->
+next_protocol(#state{negotiated_protocol = NextProtocol} = State0, Connection) ->
NextProtocolMessage = ssl_handshake:next_protocol(NextProtocol),
Connection:send_handshake(NextProtocolMessage, State0).
diff --git a/lib/ssl/src/ssl_connection.hrl b/lib/ssl/src/ssl_connection.hrl
index ac3b26e4bf..d95b51132a 100644
--- a/lib/ssl/src/ssl_connection.hrl
+++ b/lib/ssl/src/ssl_connection.hrl
@@ -78,9 +78,10 @@
allow_renegotiate = true ::boolean(),
expecting_next_protocol_negotiation = false ::boolean(),
expecting_finished = false ::boolean(),
- next_protocol = undefined :: undefined | binary(),
+ negotiated_protocol = undefined :: undefined | binary(),
client_ecc, % {Curves, PointFmt}
- tracker :: pid() %% Tracker process for listen socket
+ tracker :: pid(), %% Tracker process for listen socket
+ sni_hostname = undefined
}).
-define(DEFAULT_DIFFIE_HELLMAN_PARAMS,
diff --git a/lib/ssl/src/ssl_crl_cache_api.erl b/lib/ssl/src/ssl_crl_cache_api.erl
index 0915ba12e5..79db65104b 100644
--- a/lib/ssl/src/ssl_crl_cache_api.erl
+++ b/lib/ssl/src/ssl_crl_cache_api.erl
@@ -25,6 +25,6 @@
-type db_handle() :: term().
--callback lookup(#'DistributionPoint'{}, db_handle()) -> not_available | [public_key:der_encode()].
--callback select(term(), db_handle()) -> [public_key:der_encode()].
--callback fresh_crl(#'DistributionPoint'{}, public_key:der_encode()) -> public_key:der_encode().
+-callback lookup(#'DistributionPoint'{}, db_handle()) -> not_available | [public_key:der_encoded()].
+-callback select(term(), db_handle()) -> [public_key:der_encoded()].
+-callback fresh_crl(#'DistributionPoint'{}, public_key:der_encoded()) -> public_key:der_encoded().
diff --git a/lib/ssl/src/ssl_handshake.erl b/lib/ssl/src/ssl_handshake.erl
index 5c5f386c6f..b538fefe53 100644
--- a/lib/ssl/src/ssl_handshake.erl
+++ b/lib/ssl/src/ssl_handshake.erl
@@ -136,6 +136,7 @@ client_hello_extensions(Host, Version, CipherSuites, SslOpts, ConnectionStates,
hash_signs = advertised_hash_signs(Version),
ec_point_formats = EcPointFormats,
elliptic_curves = EllipticCurves,
+ alpn = encode_alpn(SslOpts#ssl_options.alpn_advertised_protocols, Renegotiation),
next_protocol_negotiation =
encode_client_protocol_negotiation(SslOpts#ssl_options.next_protocol_selector,
Renegotiation),
@@ -242,7 +243,7 @@ key_exchange(client, _Version, {dh, PublicKey}) ->
dh_public = PublicKey}
};
-key_exchange(client, _Version, {ecdh, #'ECPrivateKey'{publicKey = {0, ECPublicKey}}}) ->
+key_exchange(client, _Version, {ecdh, #'ECPrivateKey'{publicKey = ECPublicKey}}) ->
#client_key_exchange{
exchange_keys = #client_ec_diffie_hellman_public{
dh_public = ECPublicKey}
@@ -283,7 +284,7 @@ key_exchange(server, Version, {dh, {PublicKey, _},
enc_server_key_exchange(Version, ServerDHParams, HashSign,
ClientRandom, ServerRandom, PrivateKey);
-key_exchange(server, Version, {ecdh, #'ECPrivateKey'{publicKey = {0, ECPublicKey},
+key_exchange(server, Version, {ecdh, #'ECPrivateKey'{publicKey = ECPublicKey,
parameters = ECCurve}, HashSign,
ClientRandom, ServerRandom, PrivateKey}) ->
ServerECParams = #server_ecdh_params{curve = ECCurve, public = ECPublicKey},
@@ -577,11 +578,10 @@ prf({3,_N}, Secret, Label, Seed, WantedLength) ->
%%--------------------------------------------------------------------
select_hashsign(_, undefined, _Version) ->
{null, anon};
-select_hashsign(undefined, Cert, Version) ->
- #'OTPCertificate'{tbsCertificate = TBSCert} = public_key:pkix_decode_cert(Cert, otp),
- #'OTPSubjectPublicKeyInfo'{algorithm = {_,Algo, _}} = TBSCert#'OTPTBSCertificate'.subjectPublicKeyInfo,
- select_hashsign_algs(undefined, Algo, Version);
-select_hashsign(#hash_sign_algos{hash_sign_algos = HashSigns}, Cert, Version) ->
+%% The signature_algorithms extension was introduced with TLS 1.2. Ignore it if we have
+%% negotiated a lower version.
+select_hashsign(#hash_sign_algos{hash_sign_algos = HashSigns}, Cert, {Major, Minor} = Version)
+ when Major >= 3 andalso Minor >= 3 ->
#'OTPCertificate'{tbsCertificate = TBSCert} =public_key:pkix_decode_cert(Cert, otp),
#'OTPSubjectPublicKeyInfo'{algorithm = {_,Algo, _}} = TBSCert#'OTPTBSCertificate'.subjectPublicKeyInfo,
DefaultHashSign = {_, Sign} = select_hashsign_algs(undefined, Algo, Version),
@@ -599,7 +599,11 @@ select_hashsign(#hash_sign_algos{hash_sign_algos = HashSigns}, Cert, Version) ->
DefaultHashSign;
[HashSign| _] ->
HashSign
- end.
+ end;
+select_hashsign(_, Cert, Version) ->
+ #'OTPCertificate'{tbsCertificate = TBSCert} = public_key:pkix_decode_cert(Cert, otp),
+ #'OTPSubjectPublicKeyInfo'{algorithm = {_,Algo, _}} = TBSCert#'OTPTBSCertificate'.subjectPublicKeyInfo,
+ select_hashsign_algs(undefined, Algo, Version).
%%--------------------------------------------------------------------
-spec select_hashsign_algs(#hash_sign_algos{}| undefined, oid(), ssl_record:ssl_version()) ->
@@ -764,6 +768,11 @@ encode_hello_extensions([], Acc) ->
Size = byte_size(Acc),
<<?UINT16(Size), Acc/binary>>;
+encode_hello_extensions([#alpn{extension_data = ExtensionData} | Rest], Acc) ->
+ Len = byte_size(ExtensionData),
+ ExtLen = Len + 2,
+ encode_hello_extensions(Rest, <<?UINT16(?ALPN_EXT), ?UINT16(ExtLen), ?UINT16(Len),
+ ExtensionData/binary, Acc/binary>>);
encode_hello_extensions([#next_protocol_negotiation{extension_data = ExtensionData} | Rest], Acc) ->
Len = byte_size(ExtensionData),
encode_hello_extensions(Rest, <<?UINT16(?NEXTPROTONEG_EXT), ?UINT16(Len),
@@ -862,6 +871,25 @@ decode_client_key(ClientKey, Type, Version) ->
decode_server_key(ServerKey, Type, Version) ->
dec_server_key(ServerKey, key_exchange_alg(Type), Version).
+%%
+%% Description: Encode and decode functions for ALPN extension data.
+%%--------------------------------------------------------------------
+
+%% While the RFC opens the door to allow ALPN during renegotiation, in practice
+%% this does not work and it is recommended to ignore any ALPN extension during
+%% renegotiation, as done here.
+encode_alpn(_, true) ->
+ undefined;
+encode_alpn(undefined, _) ->
+ undefined;
+encode_alpn(Protocols, _) ->
+ #alpn{extension_data = lists:foldl(fun encode_protocol/2, <<>>, Protocols)}.
+
+decode_alpn(undefined) ->
+ undefined;
+decode_alpn(#alpn{extension_data=Data}) ->
+ decode_protocols(Data, []).
+
encode_client_protocol_negotiation(undefined, _) ->
undefined;
encode_client_protocol_negotiation(_, false) ->
@@ -1124,8 +1152,10 @@ handle_client_hello_extensions(RecordCB, Random, ClientCipherSuites,
#hello_extensions{renegotiation_info = Info,
srp = SRP,
ec_point_formats = ECCFormat,
+ alpn = ALPN,
next_protocol_negotiation = NextProtocolNegotiation}, Version,
- #ssl_options{secure_renegotiate = SecureRenegotation} = Opts,
+ #ssl_options{secure_renegotiate = SecureRenegotation,
+ alpn_preferred_protocols = ALPNPreferredProtocols} = Opts,
#session{cipher_suite = NegotiatedCipherSuite,
compression_method = Compression} = Session0,
ConnectionStates0, Renegotiation) ->
@@ -1134,19 +1164,34 @@ handle_client_hello_extensions(RecordCB, Random, ClientCipherSuites,
Random, NegotiatedCipherSuite,
ClientCipherSuites, Compression,
ConnectionStates0, Renegotiation, SecureRenegotation),
- ProtocolsToAdvertise = handle_next_protocol_extension(NextProtocolNegotiation, Renegotiation, Opts),
-
+
ServerHelloExtensions = #hello_extensions{
renegotiation_info = renegotiation_info(RecordCB, server,
ConnectionStates, Renegotiation),
- ec_point_formats = server_ecc_extension(Version, ECCFormat),
- next_protocol_negotiation =
- encode_protocols_advertised_on_server(ProtocolsToAdvertise)
+ ec_point_formats = server_ecc_extension(Version, ECCFormat)
},
- {Session, ConnectionStates, ServerHelloExtensions}.
+
+ %% If we receive an ALPN extension and have ALPN configured for this connection,
+ %% we handle it. Otherwise we check for the NPN extension.
+ if
+ ALPN =/= undefined, ALPNPreferredProtocols =/= undefined ->
+ case handle_alpn_extension(ALPNPreferredProtocols, decode_alpn(ALPN)) of
+ #alert{} = Alert ->
+ Alert;
+ Protocol ->
+ {Session, ConnectionStates, Protocol,
+ ServerHelloExtensions#hello_extensions{alpn=encode_alpn([Protocol], Renegotiation)}}
+ end;
+ true ->
+ ProtocolsToAdvertise = handle_next_protocol_extension(NextProtocolNegotiation, Renegotiation, Opts),
+ {Session, ConnectionStates, undefined,
+ ServerHelloExtensions#hello_extensions{next_protocol_negotiation=
+ encode_protocols_advertised_on_server(ProtocolsToAdvertise)}}
+ end.
handle_server_hello_extensions(RecordCB, Random, CipherSuite, Compression,
#hello_extensions{renegotiation_info = Info,
+ alpn = ALPN,
next_protocol_negotiation = NextProtocolNegotiation}, Version,
#ssl_options{secure_renegotiate = SecureRenegotation,
next_protocol_selector = NextProtoSelector},
@@ -1155,11 +1200,23 @@ handle_server_hello_extensions(RecordCB, Random, CipherSuite, Compression,
CipherSuite, undefined,
Compression, ConnectionStates0,
Renegotiation, SecureRenegotation),
- case handle_next_protocol(NextProtocolNegotiation, NextProtoSelector, Renegotiation) of
- #alert{} = Alert ->
- Alert;
- Protocol ->
- {ConnectionStates, Protocol}
+
+ %% If we receive an ALPN extension then this is the protocol selected,
+ %% otherwise handle the NPN extension.
+ case decode_alpn(ALPN) of
+ %% ServerHello contains exactly one protocol: the one selected.
+ %% We also ignore the ALPN extension during renegotiation (see encode_alpn/2).
+ [Protocol] when not Renegotiation ->
+ {ConnectionStates, alpn, Protocol};
+ undefined ->
+ case handle_next_protocol(NextProtocolNegotiation, NextProtoSelector, Renegotiation) of
+ #alert{} = Alert ->
+ Alert;
+ Protocol ->
+ {ConnectionStates, npn, Protocol}
+ end;
+ _ -> %% {error, _Reason} or a list of 0/2+ protocols.
+ ?ALERT_REC(?FATAL, ?HANDSHAKE_FAILURE)
end.
select_version(RecordCB, ClientVersion, Versions) ->
@@ -1267,10 +1324,11 @@ hello_extensions_list(#hello_extensions{renegotiation_info = RenegotiationInfo,
hash_signs = HashSigns,
ec_point_formats = EcPointFormats,
elliptic_curves = EllipticCurves,
+ alpn = ALPN,
next_protocol_negotiation = NextProtocolNegotiation,
sni = Sni}) ->
[Ext || Ext <- [RenegotiationInfo, SRP, HashSigns,
- EcPointFormats, EllipticCurves, NextProtocolNegotiation, Sni], Ext =/= undefined].
+ EcPointFormats, EllipticCurves, ALPN, NextProtocolNegotiation, Sni], Ext =/= undefined].
srp_user(#ssl_options{srp_identity = {UserName, _}}) ->
#srp{username = UserName};
@@ -1708,6 +1766,10 @@ dec_server_key_signature(_, _, _) ->
dec_hello_extensions(<<>>, Acc) ->
Acc;
+dec_hello_extensions(<<?UINT16(?ALPN_EXT), ?UINT16(ExtLen), ?UINT16(Len), ExtensionData:Len/binary, Rest/binary>>, Acc)
+ when Len + 2 =:= ExtLen ->
+ ALPN = #alpn{extension_data = ExtensionData},
+ dec_hello_extensions(Rest, Acc#hello_extensions{alpn = ALPN});
dec_hello_extensions(<<?UINT16(?NEXTPROTONEG_EXT), ?UINT16(Len), ExtensionData:Len/binary, Rest/binary>>, Acc) ->
NextP = #next_protocol_negotiation{extension_data = ExtensionData},
dec_hello_extensions(Rest, Acc#hello_extensions{next_protocol_negotiation = NextP});
@@ -1788,18 +1850,19 @@ dec_sni(<<?BYTE(_), ?UINT16(Len), _:Len, Rest/binary>>) -> dec_sni(Rest);
dec_sni(_) -> undefined.
decode_next_protocols({next_protocol_negotiation, Protocols}) ->
- decode_next_protocols(Protocols, []).
-decode_next_protocols(<<>>, Acc) ->
+ decode_protocols(Protocols, []).
+
+decode_protocols(<<>>, Acc) ->
lists:reverse(Acc);
-decode_next_protocols(<<?BYTE(Len), Protocol:Len/binary, Rest/binary>>, Acc) ->
+decode_protocols(<<?BYTE(Len), Protocol:Len/binary, Rest/binary>>, Acc) ->
case Len of
0 ->
- {error, invalid_next_protocols};
+ {error, invalid_protocols};
_ ->
- decode_next_protocols(Rest, [Protocol|Acc])
+ decode_protocols(Rest, [Protocol|Acc])
end;
-decode_next_protocols(_Bytes, _Acc) ->
- {error, invalid_next_protocols}.
+decode_protocols(_Bytes, _Acc) ->
+ {error, invalid_protocols}.
%% encode/decode stream of certificate data to/from list of certificate data
certs_to_list(ASN1Certs) ->
@@ -1853,6 +1916,17 @@ key_exchange_alg(_) ->
%%-------------Extension handling --------------------------------
+%% Receive protocols, choose one from the list, return it.
+handle_alpn_extension(_, {error, _Reason}) ->
+ ?ALERT_REC(?FATAL, ?HANDSHAKE_FAILURE);
+handle_alpn_extension([], _) ->
+ ?ALERT_REC(?FATAL, ?NO_APPLICATION_PROTOCOL);
+handle_alpn_extension([ServerProtocol|Tail], ClientProtocols) ->
+ case lists:member(ServerProtocol, ClientProtocols) of
+ true -> ServerProtocol;
+ false -> handle_alpn_extension(Tail, ClientProtocols)
+ end.
+
handle_next_protocol(undefined,
_NextProtocolSelector, _Renegotiating) ->
undefined;
diff --git a/lib/ssl/src/ssl_handshake.hrl b/lib/ssl/src/ssl_handshake.hrl
index 80284faef0..91f674a6fc 100644
--- a/lib/ssl/src/ssl_handshake.hrl
+++ b/lib/ssl/src/ssl_handshake.hrl
@@ -95,6 +95,7 @@
-record(hello_extensions, {
renegotiation_info,
hash_signs, % supported combinations of hashes/signature algos
+ alpn,
next_protocol_negotiation = undefined, % [binary()]
srp,
ec_point_formats,
@@ -301,6 +302,14 @@
}).
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% Application-Layer Protocol Negotiation RFC 7301
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+-define(ALPN_EXT, 16).
+
+-record(alpn, {extension_data}).
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% Next Protocol Negotiation
%% (http://tools.ietf.org/html/draft-agl-tls-nextprotoneg-02)
%% (http://technotes.googlecode.com/git/nextprotoneg.html)
diff --git a/lib/ssl/src/ssl_internal.hrl b/lib/ssl/src/ssl_internal.hrl
index 8df79f9e8c..baeae68bc4 100644
--- a/lib/ssl/src/ssl_internal.hrl
+++ b/lib/ssl/src/ssl_internal.hrl
@@ -116,16 +116,20 @@
hibernate_after :: boolean(),
%% This option should only be set to true by inet_tls_dist
erl_dist = false :: boolean(),
- next_protocols_advertised = undefined, %% [binary()],
+ alpn_advertised_protocols = undefined :: [binary()] | undefined ,
+ alpn_preferred_protocols = undefined :: [binary()] | undefined,
+ next_protocols_advertised = undefined :: [binary()] | undefined,
next_protocol_selector = undefined, %% fun([binary()]) -> binary())
log_alert :: boolean(),
server_name_indication = undefined,
+ sni_hosts :: [{inet:hostname(), [tuple()]}],
+ sni_fun :: function() | undefined,
%% Should the server prefer its own cipher order over the one provided by
%% the client?
- honor_cipher_order = false,
- padding_check = true,
- fallback = false,
- crl_check,
+ honor_cipher_order = false :: boolean(),
+ padding_check = true :: boolean(),
+ fallback = false :: boolean(),
+ crl_check :: boolean() | peer | best_effort,
crl_cache
}).
diff --git a/lib/ssl/src/tls_connection.erl b/lib/ssl/src/tls_connection.erl
index 77d3aa7889..3304ffcddb 100644
--- a/lib/ssl/src/tls_connection.erl
+++ b/lib/ssl/src/tls_connection.erl
@@ -188,19 +188,27 @@ hello(Hello = #client_hello{client_version = ClientVersion,
renegotiation = {Renegotiation, _},
session_cache = Cache,
session_cache_cb = CacheCb,
+ negotiated_protocol = CurrentProtocol,
ssl_options = SslOpts}) ->
case tls_handshake:hello(Hello, SslOpts, {Port, Session0, Cache, CacheCb,
ConnectionStates0, Cert}, Renegotiation) of
+ #alert{} = Alert ->
+ handle_own_alert(Alert, ClientVersion, hello, State);
{Version, {Type, Session},
- ConnectionStates, ServerHelloExt} ->
+ ConnectionStates, Protocol0, ServerHelloExt} ->
+
+ Protocol = case Protocol0 of
+ undefined -> CurrentProtocol;
+ _ -> Protocol0
+ end,
+
HashSign = ssl_handshake:select_hashsign(HashSigns, Cert, Version),
ssl_connection:hello({common_client_hello, Type, ServerHelloExt, HashSign},
State#state{connection_states = ConnectionStates,
negotiated_version = Version,
session = Session,
- client_ecc = {EllipticCurves, EcPointFormats}}, ?MODULE);
- #alert{} = Alert ->
- handle_own_alert(Alert, ClientVersion, hello, State)
+ client_ecc = {EllipticCurves, EcPointFormats},
+ negotiated_protocol = Protocol}, ?MODULE)
end;
hello(Hello,
#state{connection_states = ConnectionStates0,
@@ -211,9 +219,9 @@ hello(Hello,
case tls_handshake:hello(Hello, SslOptions, ConnectionStates0, Renegotiation) of
#alert{} = Alert ->
handle_own_alert(Alert, ReqVersion, hello, State);
- {Version, NewId, ConnectionStates, NextProtocol} ->
+ {Version, NewId, ConnectionStates, ProtoExt, Protocol} ->
ssl_connection:handle_session(Hello,
- Version, NewId, ConnectionStates, NextProtocol, State)
+ Version, NewId, ConnectionStates, ProtoExt, Protocol, State)
end;
hello(Msg, State) ->
@@ -390,6 +398,23 @@ initial_state(Role, Host, Port, Socket, {SSLOptions, SocketOptions, Tracker}, Us
tracker = Tracker
}.
+
+update_ssl_options_from_sni(OrigSSLOptions, SNIHostname) ->
+ SSLOption =
+ case OrigSSLOptions#ssl_options.sni_fun of
+ undefined ->
+ proplists:get_value(SNIHostname,
+ OrigSSLOptions#ssl_options.sni_hosts);
+ SNIFun ->
+ SNIFun(SNIHostname)
+ end,
+ case SSLOption of
+ undefined ->
+ undefined;
+ _ ->
+ ssl:handle_options(SSLOption, OrigSSLOptions)
+ end.
+
next_state(Current,_, #alert{} = Alert, #state{negotiated_version = Version} = State) ->
handle_own_alert(Alert, Version, Current, State);
@@ -418,15 +443,17 @@ next_state(Current, Next, #ssl_tls{type = ?HANDSHAKE, fragment = Data},
%% This message should not be included in handshake
%% message hashes. Already in negotiation so it will be ignored!
?MODULE:SName(Packet, State);
- ({#client_hello{} = Packet, Raw}, {next_state, connection = SName, State}) ->
+ ({#client_hello{} = Packet, Raw}, {next_state, connection = SName, HState0}) ->
+ HState = handle_sni_extension(Packet, HState0),
Version = Packet#client_hello.client_version,
Hs0 = ssl_handshake:init_handshake_history(),
Hs1 = ssl_handshake:update_handshake_history(Hs0, Raw),
- ?MODULE:SName(Packet, State#state{tls_handshake_history=Hs1,
- renegotiation = {true, peer}});
- ({Packet, Raw}, {next_state, SName, State = #state{tls_handshake_history=Hs0}}) ->
+ ?MODULE:SName(Packet, HState#state{tls_handshake_history=Hs1,
+ renegotiation = {true, peer}});
+ ({Packet, Raw}, {next_state, SName, HState0 = #state{tls_handshake_history=Hs0}}) ->
+ HState = handle_sni_extension(Packet, HState0),
Hs1 = ssl_handshake:update_handshake_history(Hs0, Raw),
- ?MODULE:SName(Packet, State#state{tls_handshake_history=Hs1});
+ ?MODULE:SName(Packet, HState#state{tls_handshake_history=Hs1});
(_, StopState) -> StopState
end,
try
@@ -973,3 +1000,32 @@ convert_options_partial_chain(Options, up) ->
list_to_tuple(Head ++ [{partial_chain, fun(_) -> unknown_ca end}] ++ Tail);
convert_options_partial_chain(Options, down) ->
list_to_tuple(proplists:delete(partial_chain, tuple_to_list(Options))).
+
+handle_sni_extension(#client_hello{extensions = HelloExtensions}, State0) ->
+ case HelloExtensions#hello_extensions.sni of
+ undefined ->
+ State0;
+ #sni{hostname = Hostname} ->
+ NewOptions = update_ssl_options_from_sni(State0#state.ssl_options, Hostname),
+ case NewOptions of
+ undefined ->
+ State0;
+ _ ->
+ {ok, Ref, CertDbHandle, FileRefHandle, CacheHandle, CRLDbHandle, OwnCert, Key, DHParams} =
+ ssl_config:init(NewOptions, State0#state.role),
+ State0#state{
+ session = State0#state.session#session{own_certificate = OwnCert},
+ file_ref_db = FileRefHandle,
+ cert_db_ref = Ref,
+ cert_db = CertDbHandle,
+ crl_db = CRLDbHandle,
+ session_cache = CacheHandle,
+ private_key = Key,
+ diffie_hellman_params = DHParams,
+ ssl_options = NewOptions,
+ sni_hostname = Hostname
+ }
+ end
+ end;
+handle_sni_extension(_, State0) ->
+ State0.
diff --git a/lib/ssl/src/tls_handshake.erl b/lib/ssl/src/tls_handshake.erl
index 548ec4aebe..d936310991 100644
--- a/lib/ssl/src/tls_handshake.erl
+++ b/lib/ssl/src/tls_handshake.erl
@@ -78,11 +78,14 @@ client_hello(Host, Port, ConnectionStates,
%%--------------------------------------------------------------------
-spec hello(#server_hello{} | #client_hello{}, #ssl_options{},
#connection_states{} | {inet:port_number(), #session{}, db_handle(),
- atom(), #connection_states{}, binary() | undefined},
+ atom(), #connection_states{},
+ binary() | undefined},
boolean()) ->
- {tls_record:tls_version(), session_id(), #connection_states{}, binary() | undefined}|
- {tls_record:tls_version(), {resumed | new, #session{}}, #connection_states{},
- #hello_extensions{}} |
+ {tls_record:tls_version(), session_id(),
+ #connection_states{}, alpn | npn, binary() | undefined}|
+ {tls_record:tls_version(), {resumed | new, #session{}},
+ #connection_states{}, binary() | undefined,
+ #hello_extensions{}} |
#alert{}.
%%
%% Description: Handles a recieved hello message
@@ -245,8 +248,10 @@ handle_client_hello_extensions(Version, Type, Random, CipherSuites,
try ssl_handshake:handle_client_hello_extensions(tls_record, Random, CipherSuites,
HelloExt, Version, SslOpts,
Session0, ConnectionStates0, Renegotiation) of
- {Session, ConnectionStates, ServerHelloExt} ->
- {Version, {Type, Session}, ConnectionStates, ServerHelloExt}
+ #alert{} = Alert ->
+ Alert;
+ {Session, ConnectionStates, Protocol, ServerHelloExt} ->
+ {Version, {Type, Session}, ConnectionStates, Protocol, ServerHelloExt}
catch throw:Alert ->
Alert
end.
@@ -259,7 +264,7 @@ handle_server_hello_extensions(Version, SessionId, Random, CipherSuite,
SslOpt, ConnectionStates0, Renegotiation) of
#alert{} = Alert ->
Alert;
- {ConnectionStates, Protocol} ->
- {Version, SessionId, ConnectionStates, Protocol}
+ {ConnectionStates, ProtoExt, Protocol} ->
+ {Version, SessionId, ConnectionStates, ProtoExt, Protocol}
end.
diff --git a/lib/ssl/test/Makefile b/lib/ssl/test/Makefile
index 09cc5981e7..886cc7726b 100644
--- a/lib/ssl/test/Makefile
+++ b/lib/ssl/test/Makefile
@@ -36,6 +36,7 @@ VSN=$(GS_VSN)
MODULES = \
ssl_test_lib \
+ ssl_alpn_handshake_SUITE \
ssl_basic_SUITE \
ssl_bench_SUITE \
ssl_cipher_SUITE \
@@ -52,6 +53,7 @@ MODULES = \
ssl_to_openssl_SUITE \
ssl_ECC_SUITE \
ssl_upgrade_SUITE\
+ ssl_sni_SUITE \
make_certs\
erl_make_certs
diff --git a/lib/ssl/test/erl_make_certs.erl b/lib/ssl/test/erl_make_certs.erl
index b534c0130e..12ad1e5402 100644
--- a/lib/ssl/test/erl_make_certs.erl
+++ b/lib/ssl/test/erl_make_certs.erl
@@ -114,7 +114,7 @@ verify_signature(DerEncodedCert, DerKey, _KeyParams) ->
#'DSAPrivateKey'{p=P, q=Q, g=G, y=Y} ->
public_key:pkix_verify(DerEncodedCert, {Y, #'Dss-Parms'{p=P, q=Q, g=G}});
#'ECPrivateKey'{version = _Version, privateKey = _PrivKey,
- parameters = Params, publicKey = {0, PubKey}} ->
+ parameters = Params, publicKey = PubKey} ->
public_key:pkix_verify(DerEncodedCert, {#'ECPoint'{point = PubKey}, Params})
end.
@@ -292,7 +292,7 @@ publickey(#'DSAPrivateKey'{p=P, q=Q, g=G, y=Y}) ->
publickey(#'ECPrivateKey'{version = _Version,
privateKey = _PrivKey,
parameters = Params,
- publicKey = {0, PubKey}}) ->
+ publicKey = PubKey}) ->
Algo = #'PublicKeyAlgorithm'{algorithm= ?'id-ecPublicKey', parameters=Params},
#'OTPSubjectPublicKeyInfo'{algorithm = Algo,
subjectPublicKey = #'ECPoint'{point = PubKey}}.
@@ -401,9 +401,9 @@ gen_ec2(CurveId) ->
{PubKey, PrivKey} = crypto:generate_key(ecdh, CurveId),
#'ECPrivateKey'{version = 1,
- privateKey = binary_to_list(PrivKey),
+ privateKey = PrivKey,
parameters = {namedCurve, pubkey_cert_records:namedCurves(CurveId)},
- publicKey = {0, PubKey}}.
+ publicKey = PubKey}.
%% See fips_186-3.pdf
dsa_search(T, P0, Q, Iter) when Iter > 0 ->
diff --git a/lib/ssl/test/make_certs.erl b/lib/ssl/test/make_certs.erl
index 77631f62d3..4a193d48fe 100644
--- a/lib/ssl/test/make_certs.erl
+++ b/lib/ssl/test/make_certs.erl
@@ -81,7 +81,7 @@ all(DataDir, PrivDir, C = #config{}) ->
create_rnd(DataDir, PrivDir), % For all requests
rootCA(PrivDir, "erlangCA", C),
intermediateCA(PrivDir, "otpCA", "erlangCA", C),
- endusers(PrivDir, "otpCA", ["client", "server", "revoked"], C),
+ endusers(PrivDir, "otpCA", ["client", "server", "revoked", "a.server", "b.server"], C),
endusers(PrivDir, "erlangCA", ["localhost"], C),
%% Create keycert files
SDir = filename:join([PrivDir, "server"]),
diff --git a/lib/ssl/test/ssl_alpn_handshake_SUITE.erl b/lib/ssl/test/ssl_alpn_handshake_SUITE.erl
new file mode 100644
index 0000000000..ccd70fa605
--- /dev/null
+++ b/lib/ssl/test/ssl_alpn_handshake_SUITE.erl
@@ -0,0 +1,414 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2008-2015. All Rights Reserved.
+%%
+%% The contents of this file are subject to the Erlang Public License,
+%% Version 1.1, (the "License"); you may not use this file except in
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved online at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%% the License for the specific language governing rights and limitations
+%% under the License.
+%%
+%% %CopyrightEnd%
+%%
+
+%%
+-module(ssl_alpn_handshake_SUITE).
+
+%% Note: This directive should only be used in test suites.
+-compile(export_all).
+-include_lib("common_test/include/ct.hrl").
+
+-define(SLEEP, 500).
+
+%%--------------------------------------------------------------------
+%% Common Test interface functions -----------------------------------
+%%--------------------------------------------------------------------
+
+suite() -> [{ct_hooks,[ts_install_cth]}].
+
+all() ->
+ [{group, 'tlsv1.2'},
+ {group, 'tlsv1.1'},
+ {group, 'tlsv1'},
+ {group, 'sslv3'}].
+
+groups() ->
+ [
+ {'tlsv1.2', [], alpn_tests()},
+ {'tlsv1.1', [], alpn_tests()},
+ {'tlsv1', [], alpn_tests()},
+ {'sslv3', [], alpn_not_supported()}
+ ].
+
+alpn_tests() ->
+ [empty_protocols_are_not_allowed,
+ protocols_must_be_a_binary_list,
+ empty_client,
+ empty_server,
+ empty_client_empty_server,
+ no_matching_protocol,
+ client_alpn_and_server_alpn,
+ client_alpn_and_server_no_support,
+ client_no_support_and_server_alpn,
+ client_alpn_npn_and_server_alpn,
+ client_alpn_npn_and_server_alpn_npn,
+ client_alpn_and_server_alpn_npn,
+ client_renegotiate,
+ session_reused
+ ].
+
+alpn_not_supported() ->
+ [alpn_not_supported_client,
+ alpn_not_supported_server
+ ].
+
+init_per_suite(Config) ->
+ catch crypto:stop(),
+ try crypto:start() of
+ ok ->
+ ssl:start(),
+ Result =
+ (catch make_certs:all(?config(data_dir, Config),
+ ?config(priv_dir, Config))),
+ ct:log("Make certs ~p~n", [Result]),
+ ssl_test_lib:cert_options(Config)
+ catch _:_ ->
+ {skip, "Crypto did not start"}
+ end.
+
+end_per_suite(_Config) ->
+ ssl:stop(),
+ application:unload(ssl),
+ application:stop(crypto).
+
+
+init_per_group(GroupName, Config) ->
+ case ssl_test_lib:is_tls_version(GroupName) of
+ true ->
+ case ssl_test_lib:sufficient_crypto_support(GroupName) of
+ true ->
+ ssl_test_lib:init_tls_version(GroupName),
+ Config;
+ false ->
+ {skip, "Missing crypto support"}
+ end;
+ _ ->
+ ssl:start(),
+ Config
+ end.
+
+end_per_group(_GroupName, Config) ->
+ Config.
+
+%%--------------------------------------------------------------------
+%% Test Cases --------------------------------------------------------
+%%--------------------------------------------------------------------
+
+empty_protocols_are_not_allowed(Config) when is_list(Config) ->
+ {error, {options, {alpn_preferred_protocols, {invalid_protocol, <<>>}}}}
+ = (catch ssl:listen(9443,
+ [{alpn_preferred_protocols, [<<"foo/1">>, <<"">>]}])),
+ {error, {options, {alpn_advertised_protocols, {invalid_protocol, <<>>}}}}
+ = (catch ssl:connect({127,0,0,1}, 9443,
+ [{alpn_advertised_protocols, [<<"foo/1">>, <<"">>]}])).
+
+%--------------------------------------------------------------------------------
+
+protocols_must_be_a_binary_list(Config) when is_list(Config) ->
+ Option1 = {alpn_preferred_protocols, hello},
+ {error, {options, Option1}} = (catch ssl:listen(9443, [Option1])),
+ Option2 = {alpn_preferred_protocols, [<<"foo/1">>, hello]},
+ {error, {options, {alpn_preferred_protocols, {invalid_protocol, hello}}}}
+ = (catch ssl:listen(9443, [Option2])),
+ Option3 = {alpn_advertised_protocols, hello},
+ {error, {options, Option3}} = (catch ssl:connect({127,0,0,1}, 9443, [Option3])),
+ Option4 = {alpn_advertised_protocols, [<<"foo/1">>, hello]},
+ {error, {options, {alpn_advertised_protocols, {invalid_protocol, hello}}}}
+ = (catch ssl:connect({127,0,0,1}, 9443, [Option4])).
+
+%--------------------------------------------------------------------------------
+
+empty_client(Config) when is_list(Config) ->
+ run_failing_handshake(Config,
+ [{alpn_advertised_protocols, []}],
+ [{alpn_preferred_protocols, [<<"spdy/2">>, <<"spdy/3">>, <<"http/2">>]}],
+ {connect_failed,{tls_alert,"no application protocol"}}).
+
+%--------------------------------------------------------------------------------
+
+empty_server(Config) when is_list(Config) ->
+ run_failing_handshake(Config,
+ [{alpn_advertised_protocols, [<<"http/1.0">>, <<"http/1.1">>]}],
+ [{alpn_preferred_protocols, []}],
+ {connect_failed,{tls_alert,"no application protocol"}}).
+
+%--------------------------------------------------------------------------------
+
+empty_client_empty_server(Config) when is_list(Config) ->
+ run_failing_handshake(Config,
+ [{alpn_advertised_protocols, []}],
+ [{alpn_preferred_protocols, []}],
+ {connect_failed,{tls_alert,"no application protocol"}}).
+
+%--------------------------------------------------------------------------------
+
+no_matching_protocol(Config) when is_list(Config) ->
+ run_failing_handshake(Config,
+ [{alpn_advertised_protocols, [<<"http/1.0">>, <<"http/1.1">>]}],
+ [{alpn_preferred_protocols, [<<"spdy/2">>, <<"spdy/3">>, <<"http/2">>]}],
+ {connect_failed,{tls_alert,"no application protocol"}}).
+
+%--------------------------------------------------------------------------------
+
+client_alpn_and_server_alpn(Config) when is_list(Config) ->
+ run_handshake(Config,
+ [{alpn_advertised_protocols, [<<"http/1.0">>, <<"http/1.1">>]}],
+ [{alpn_preferred_protocols, [<<"spdy/2">>, <<"http/1.1">>, <<"http/1.0">>]}],
+ {ok, <<"http/1.1">>}).
+
+%--------------------------------------------------------------------------------
+
+client_alpn_and_server_no_support(Config) when is_list(Config) ->
+ run_handshake(Config,
+ [{alpn_advertised_protocols, [<<"http/1.0">>, <<"http/1.1">>]}],
+ [],
+ {error, protocol_not_negotiated}).
+
+%--------------------------------------------------------------------------------
+
+client_no_support_and_server_alpn(Config) when is_list(Config) ->
+ run_handshake(Config,
+ [],
+ [{alpn_preferred_protocols, [<<"spdy/2">>, <<"http/1.1">>, <<"http/1.0">>]}],
+ {error, protocol_not_negotiated}).
+
+%--------------------------------------------------------------------------------
+
+client_alpn_npn_and_server_alpn(Config) when is_list(Config) ->
+ run_handshake(Config,
+ [{alpn_advertised_protocols, [<<"http/1.0">>, <<"http/1.1">>]},
+ {client_preferred_next_protocols, {client, [<<"spdy/2">>], <<"spdy/3">>}}],
+ [{alpn_preferred_protocols, [<<"spdy/2">>, <<"http/1.1">>, <<"http/1.0">>]}],
+ {ok, <<"http/1.1">>}).
+
+%--------------------------------------------------------------------------------
+
+client_alpn_npn_and_server_alpn_npn(Config) when is_list(Config) ->
+ run_handshake(Config,
+ [{alpn_advertised_protocols, [<<"http/1.0">>, <<"http/1.1">>]},
+ {client_preferred_next_protocols, {client, [<<"spdy/2">>], <<"spdy/3">>}}],
+ [{alpn_preferred_protocols, [<<"spdy/2">>, <<"http/1.1">>, <<"http/1.0">>]},
+ {next_protocols_advertised, [<<"spdy/2">>, <<"http/1.0">>]}],
+ {ok, <<"http/1.1">>}).
+
+%--------------------------------------------------------------------------------
+
+client_alpn_and_server_alpn_npn(Config) when is_list(Config) ->
+ run_handshake(Config,
+ [{alpn_advertised_protocols, [<<"http/1.0">>, <<"http/1.1">>]}],
+ [{alpn_preferred_protocols, [<<"spdy/2">>, <<"http/1.1">>, <<"http/1.0">>]},
+ {next_protocols_advertised, [<<"spdy/2">>, <<"http/1.0">>]}],
+ {ok, <<"http/1.1">>}).
+
+%--------------------------------------------------------------------------------
+
+client_renegotiate(Config) when is_list(Config) ->
+ Data = "hello world",
+
+ ClientOpts0 = ?config(client_opts, Config),
+ ClientOpts = [{alpn_advertised_protocols, [<<"http/1.0">>]}] ++ ClientOpts0,
+ ServerOpts0 = ?config(server_opts, Config),
+ ServerOpts = [{alpn_preferred_protocols, [<<"spdy/2">>, <<"http/1.1">>, <<"http/1.0">>]}] ++ ServerOpts0,
+ ExpectedProtocol = {ok, <<"http/1.0">>},
+
+ {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config),
+ Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0},
+ {from, self()},
+ {mfa, {?MODULE, ssl_receive_and_assert_alpn, [ExpectedProtocol, Data]}},
+ {options, ServerOpts}]),
+
+ Port = ssl_test_lib:inet_port(Server),
+ Client = ssl_test_lib:start_client([{node, ClientNode}, {port, Port},
+ {host, Hostname},
+ {from, self()},
+ {mfa, {?MODULE, assert_alpn_and_renegotiate_and_send_data, [ExpectedProtocol, Data]}},
+ {options, ClientOpts}]),
+
+ ssl_test_lib:check_result(Server, ok, Client, ok).
+
+%--------------------------------------------------------------------------------
+
+session_reused(Config) when is_list(Config)->
+ ClientOpts0 = ?config(client_opts, Config),
+ ClientOpts = [{alpn_advertised_protocols, [<<"http/1.0">>]}] ++ ClientOpts0,
+ ServerOpts0 = ?config(server_opts, Config),
+ ServerOpts = [{alpn_preferred_protocols, [<<"spdy/2">>, <<"http/1.1">>, <<"http/1.0">>]}] ++ ServerOpts0,
+
+ {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config),
+ Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0},
+ {from, self()},
+ {mfa, {ssl_test_lib, session_info_result, []}},
+ {options, ServerOpts}]),
+
+ Port = ssl_test_lib:inet_port(Server),
+ Client = ssl_test_lib:start_client([{node, ClientNode}, {port, Port},
+ {host, Hostname},
+ {from, self()},
+ {mfa, {ssl_test_lib, no_result_msg, []}},
+ {options, ClientOpts}]),
+
+ SessionInfo =
+ receive
+ {Server, Info} ->
+ Info
+ end,
+
+ Server ! {listen, {mfa, {ssl_test_lib, no_result, []}}},
+
+ %% Make sure session is registered
+ ct:sleep(?SLEEP),
+
+ Client1 =
+ ssl_test_lib:start_client([{node, ClientNode},
+ {port, Port}, {host, Hostname},
+ {mfa, {ssl_test_lib, session_info_result, []}},
+ {from, self()}, {options, ClientOpts}]),
+
+ receive
+ {Client1, SessionInfo} ->
+ ok;
+ {Client1, Other} ->
+ ct:fail(Other)
+ end,
+
+ ssl_test_lib:close(Server),
+ ssl_test_lib:close(Client),
+ ssl_test_lib:close(Client1).
+
+%--------------------------------------------------------------------------------
+
+alpn_not_supported_client(Config) when is_list(Config) ->
+ ClientOpts0 = ?config(client_opts, Config),
+ PrefProtocols = {client_preferred_next_protocols,
+ {client, [<<"http/1.0">>], <<"http/1.1">>}},
+ ClientOpts = [PrefProtocols] ++ ClientOpts0,
+ {ClientNode, _ServerNode, Hostname} = ssl_test_lib:run_where(Config),
+ Client = ssl_test_lib:start_client_error([{node, ClientNode},
+ {port, 8888}, {host, Hostname},
+ {from, self()}, {options, ClientOpts}]),
+
+ ssl_test_lib:check_result(Client, {error,
+ {options,
+ {not_supported_in_sslv3, PrefProtocols}}}).
+
+%--------------------------------------------------------------------------------
+
+alpn_not_supported_server(Config) when is_list(Config)->
+ ServerOpts0 = ?config(server_opts, Config),
+ AdvProtocols = {next_protocols_advertised, [<<"spdy/2">>, <<"http/1.1">>, <<"http/1.0">>]},
+ ServerOpts = [AdvProtocols] ++ ServerOpts0,
+
+ {error, {options, {not_supported_in_sslv3, AdvProtocols}}} = ssl:listen(0, ServerOpts).
+
+%%--------------------------------------------------------------------
+%% Internal functions ------------------------------------------------
+%%--------------------------------------------------------------------
+
+run_failing_handshake(Config, ClientExtraOpts, ServerExtraOpts, ExpectedResult) ->
+ ClientOpts = ClientExtraOpts ++ ?config(client_opts, Config),
+ ServerOpts = ServerExtraOpts ++ ?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, placeholder, []}},
+ {options, ServerOpts}]),
+
+ Port = ssl_test_lib:inet_port(Server),
+ ExpectedResult
+ = ssl_test_lib:start_client([{node, ClientNode}, {port, Port},
+ {host, Hostname},
+ {from, self()},
+ {mfa, {?MODULE, placeholder, []}},
+ {options, ClientOpts}]).
+
+run_handshake(Config, ClientExtraOpts, ServerExtraOpts, ExpectedProtocol) ->
+ Data = "hello world",
+
+ ClientOpts0 = ?config(client_opts, Config),
+ ClientOpts = ClientExtraOpts ++ ClientOpts0,
+ ServerOpts0 = ?config(server_opts, Config),
+ ServerOpts = ServerExtraOpts ++ ServerOpts0,
+
+ {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config),
+ Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0},
+ {from, self()},
+ {mfa, {?MODULE, ssl_receive_and_assert_alpn, [ExpectedProtocol, Data]}},
+ {options, ServerOpts}]),
+
+ Port = ssl_test_lib:inet_port(Server),
+ Client = ssl_test_lib:start_client([{node, ClientNode}, {port, Port},
+ {host, Hostname},
+ {from, self()},
+ {mfa, {?MODULE, ssl_send_and_assert_alpn, [ExpectedProtocol, Data]}},
+ {options, ClientOpts}]),
+
+ ssl_test_lib:check_result(Server, ok, Client, ok).
+
+assert_alpn(Socket, Protocol) ->
+ ct:log("Negotiated Protocol ~p, Expecting: ~p ~n",
+ [ssl:negotiated_protocol(Socket), Protocol]),
+ Protocol = ssl:negotiated_protocol(Socket).
+
+assert_alpn_and_renegotiate_and_send_data(Socket, Protocol, Data) ->
+ assert_alpn(Socket, Protocol),
+ ct:log("Renegotiating ~n", []),
+ ok = ssl:renegotiate(Socket),
+ ssl:send(Socket, Data),
+ assert_alpn(Socket, Protocol),
+ ok.
+
+ssl_send_and_assert_alpn(Socket, Protocol, Data) ->
+ assert_alpn(Socket, Protocol),
+ ssl_send(Socket, Data).
+
+ssl_receive_and_assert_alpn(Socket, Protocol, Data) ->
+ assert_alpn(Socket, Protocol),
+ ssl_receive(Socket, Data).
+
+ssl_send(Socket, Data) ->
+ ct:log("Connection info: ~p~n",
+ [ssl:connection_info(Socket)]),
+ ssl:send(Socket, Data).
+
+ssl_receive(Socket, Data) ->
+ ssl_receive(Socket, Data, []).
+
+ssl_receive(Socket, Data, Buffer) ->
+ ct:log("Connection info: ~p~n",
+ [ssl:connection_info(Socket)]),
+ receive
+ {ssl, Socket, MoreData} ->
+ ct:log("Received ~p~n",[MoreData]),
+ NewBuffer = Buffer ++ MoreData,
+ case NewBuffer of
+ Data ->
+ ssl:send(Socket, "Got it"),
+ ok;
+ _ ->
+ ssl_receive(Socket, Data, NewBuffer)
+ end;
+ Other ->
+ ct:fail({unexpected_message, Other})
+ after 4000 ->
+ ct:fail({did_not_get, Data})
+ end.
+
+connection_info_result(Socket) ->
+ ssl:connection_info(Socket).
diff --git a/lib/ssl/test/ssl_handshake_SUITE.erl b/lib/ssl/test/ssl_handshake_SUITE.erl
index 8dca733526..d4433393a1 100644
--- a/lib/ssl/test/ssl_handshake_SUITE.erl
+++ b/lib/ssl/test/ssl_handshake_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2008-2014. All Rights Reserved.
+%% Copyright Ericsson AB 2008-2015. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -40,7 +40,47 @@ all() -> [decode_hello_handshake,
encode_single_hello_sni_extension_correctly,
decode_single_hello_sni_extension_correctly,
decode_empty_server_sni_correctly,
- select_proper_tls_1_2_rsa_default_hashsign].
+ select_proper_tls_1_2_rsa_default_hashsign,
+ ignore_hassign_extension_pre_tls_1_2].
+
+%%--------------------------------------------------------------------
+init_per_suite(Config) ->
+ Config.
+end_per_suite(Config) ->
+ Config.
+
+init_per_group(_GroupName, Config) ->
+ Config.
+
+end_per_group(_,Config) ->
+ Config.
+
+init_per_testcase(ignore_hassign_extension_pre_tls_1_2, Config0) ->
+ catch crypto:stop(),
+ try crypto:start() of
+ ok ->
+ case is_supported(sha512) of
+ true ->
+ ssl:start(),
+ %% make rsa certs using oppenssl
+ Result =
+ (catch make_certs:all(?config(data_dir, Config0),
+ ?config(priv_dir, Config0))),
+ ct:log("Make certs ~p~n", [Result]),
+ ssl_test_lib:cert_options(Config0);
+ false ->
+ {skip, "Crypto did not support sha512"}
+ end
+ catch _:_ ->
+ {skip, "Crypto did not start"}
+ end;
+init_per_testcase(_, Config0) ->
+ Config0.
+
+end_per_testcase(ignore_hassign_extension_pre_tls_1_2, _) ->
+ crypto:stop();
+end_per_testcase(_TestCase, Config) ->
+ Config.
%%--------------------------------------------------------------------
%% Test Cases --------------------------------------------------------
@@ -121,3 +161,18 @@ select_proper_tls_1_2_rsa_default_hashsign(_Config) ->
{md5sha, rsa} = ssl_handshake:select_hashsign_algs(undefined, ?rsaEncryption, {3,2}),
{md5sha, rsa} = ssl_handshake:select_hashsign_algs(undefined, ?rsaEncryption, {3,0}).
+
+ignore_hassign_extension_pre_tls_1_2(Config) ->
+ Opts = ?config(server_opts, Config),
+ CertFile = proplists:get_value(certfile, Opts),
+ [{_, Cert, _}] = ssl_test_lib:pem_to_der(CertFile),
+ HashSigns = #hash_sign_algos{hash_sign_algos = [{sha512, rsa}, {sha, dsa}]},
+ {sha512, rsa} = ssl_handshake:select_hashsign(HashSigns, Cert, {3,3}),
+ %%% Ignore
+ {md5sha, rsa} = ssl_handshake:select_hashsign(HashSigns, Cert, {3,2}),
+ {md5sha, rsa} = ssl_handshake:select_hashsign(HashSigns, Cert, {3,0}).
+
+is_supported(Hash) ->
+ Algos = crypto:supports(),
+ Hashs = proplists:get_value(hashs, Algos),
+ lists:member(Hash, Hashs).
diff --git a/lib/ssl/test/ssl_npn_handshake_SUITE.erl b/lib/ssl/test/ssl_npn_handshake_SUITE.erl
index 30c0a67a36..326f907e66 100644
--- a/lib/ssl/test/ssl_npn_handshake_SUITE.erl
+++ b/lib/ssl/test/ssl_npn_handshake_SUITE.erl
@@ -172,7 +172,7 @@ no_client_negotiate_but_server_supports_npn(Config) when is_list(Config) ->
run_npn_handshake(Config,
[],
[{next_protocols_advertised, [<<"spdy/1">>, <<"http/1.1">>, <<"http/1.0">>]}],
- {error, next_protocol_not_negotiated}).
+ {error, protocol_not_negotiated}).
%--------------------------------------------------------------------------------
@@ -180,7 +180,7 @@ client_negotiate_server_does_not_support(Config) when is_list(Config) ->
run_npn_handshake(Config,
[{client_preferred_next_protocols, {client, [<<"spdy/2">>], <<"http/1.1">>}}],
[],
- {error, next_protocol_not_negotiated}).
+ {error, protocol_not_negotiated}).
%--------------------------------------------------------------------------------
renegotiate_from_client_after_npn_handshake(Config) when is_list(Config) ->
@@ -311,8 +311,8 @@ run_npn_handshake(Config, ClientExtraOpts, ServerExtraOpts, ExpectedProtocol) ->
assert_npn(Socket, Protocol) ->
ct:log("Negotiated Protocol ~p, Expecting: ~p ~n",
- [ssl:negotiated_next_protocol(Socket), Protocol]),
- Protocol = ssl:negotiated_next_protocol(Socket).
+ [ssl:negotiated_protocol(Socket), Protocol]),
+ Protocol = ssl:negotiated_protocol(Socket).
assert_npn_and_renegotiate_and_send_data(Socket, Protocol, Data) ->
assert_npn(Socket, Protocol),
diff --git a/lib/ssl/test/ssl_sni_SUITE.erl b/lib/ssl/test/ssl_sni_SUITE.erl
new file mode 100644
index 0000000000..46cd644e4d
--- /dev/null
+++ b/lib/ssl/test/ssl_sni_SUITE.erl
@@ -0,0 +1,168 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2008-2015. All Rights Reserved.
+%%
+%% The contents of this file are subject to the Erlang Public License,
+%% Version 1.1, (the "License"); you may not use this file except in
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved online at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%% the License for the specific language governing rights and limitations
+%% under the License.
+%%
+%% %CopyrightEnd%
+%%
+
+%%
+
+-module(ssl_sni_SUITE).
+
+-compile(export_all).
+
+-include_lib("common_test/include/ct.hrl").
+-include_lib("public_key/include/public_key.hrl").
+
+%%--------------------------------------------------------------------
+%% Common Test interface functions -----------------------------------
+%%--------------------------------------------------------------------
+suite() -> [{ct_hooks,[ts_install_cth]}].
+
+all() -> [no_sni_header, sni_match, sni_no_match] ++ [no_sni_header_fun, sni_match_fun, sni_no_match_fun].
+
+init_per_suite(Config0) ->
+ catch crypto:stop(),
+ try crypto:start() of
+ ok ->
+ ssl:start(),
+ Result =
+ (catch make_certs:all(?config(data_dir, Config0),
+ ?config(priv_dir, Config0))),
+ ct:log("Make certs ~p~n", [Result]),
+ ssl_test_lib:cert_options(Config0)
+ catch _:_ ->
+ {skip, "Crypto did not start"}
+ end.
+
+end_per_suite(_) ->
+ ssl:stop(),
+ application:stop(crypto).
+
+%%--------------------------------------------------------------------
+%% Test Cases --------------------------------------------------------
+%%--------------------------------------------------------------------
+no_sni_header(Config) ->
+ run_handshake(Config, undefined, undefined, "server").
+
+no_sni_header_fun(Config) ->
+ run_sni_fun_handshake(Config, undefined, undefined, "server").
+
+sni_match(Config) ->
+ run_handshake(Config, "a.server", "a.server", "a.server").
+
+sni_match_fun(Config) ->
+ run_sni_fun_handshake(Config, "a.server", "a.server", "a.server").
+
+sni_no_match(Config) ->
+ run_handshake(Config, "c.server", undefined, "server").
+
+sni_no_match_fun(Config) ->
+ run_sni_fun_handshake(Config, "c.server", undefined, "server").
+
+
+%%--------------------------------------------------------------------
+%% Internal Functions ------------------------------------------------
+%%--------------------------------------------------------------------
+
+
+ssl_recv(SSLSocket, Expect) ->
+ ssl_recv(SSLSocket, "", Expect).
+
+ssl_recv(SSLSocket, CurrentData, ExpectedData) ->
+ receive
+ {ssl, SSLSocket, Data} ->
+ NeweData = CurrentData ++ Data,
+ case NeweData of
+ ExpectedData ->
+ ok;
+ _ ->
+ ssl_recv(SSLSocket, NeweData, ExpectedData)
+ end;
+ Other ->
+ ct:fail({unexpected_message, Other})
+ after 4000 ->
+ ct:fail({timeout, CurrentData, ExpectedData})
+ end.
+
+
+
+send_and_hostname(SSLSocket) ->
+ ssl:send(SSLSocket, "OK"),
+ {ok, [{sni_hostname, Hostname}]} = ssl:connection_information(SSLSocket, [sni_hostname]),
+ Hostname.
+
+rdnPart([[#'AttributeTypeAndValue'{type=Type, value=Value} | _] | _], Type) -> Value;
+rdnPart([_ | Tail], Type) -> rdnPart(Tail, Type);
+rdnPart([], _) -> unknown.
+
+rdn_to_string({utf8String, Binary}) ->
+ erlang:binary_to_list(Binary);
+rdn_to_string({printableString, String}) ->
+ String.
+
+recv_and_certificate(SSLSocket) ->
+ ssl_recv(SSLSocket, "OK"),
+ {ok, PeerCert} = ssl:peercert(SSLSocket),
+ #'OTPCertificate'{tbsCertificate = #'OTPTBSCertificate'{subject = {rdnSequence, Subject}}} = public_key:pkix_decode_cert(PeerCert, otp),
+ ct:log("Subject of certificate received from server: ~p", [Subject]),
+ rdn_to_string(rdnPart(Subject, ?'id-at-commonName')).
+
+run_sni_fun_handshake(Config, SNIHostname, ExpectedSNIHostname, ExpectedCN) ->
+ ct:log("Start running handshake for sni_fun, Config: ~p, SNIHostname: ~p, ExpectedSNIHostname: ~p, ExpectedCN: ~p", [Config, SNIHostname, ExpectedSNIHostname, ExpectedCN]),
+ [{sni_hosts, ServerSNIConf}] = ?config(sni_server_opts, Config),
+ SNIFun = fun(Domain) -> proplists:get_value(Domain, ServerSNIConf, undefined) end,
+ ServerOptions = ?config(server_opts, Config) ++ [{sni_fun, SNIFun}],
+ ClientOptions =
+ case SNIHostname of
+ undefined ->
+ ?config(client_opts, Config);
+ _ ->
+ [{server_name_indication, SNIHostname}] ++ ?config(client_opts, Config)
+ end,
+ ct:log("Options: ~p", [[ServerOptions, ClientOptions]]),
+ {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config),
+ Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0},
+ {from, self()}, {mfa, {?MODULE, send_and_hostname, []}},
+ {options, ServerOptions}]),
+ Port = ssl_test_lib:inet_port(Server),
+ Client = ssl_test_lib:start_client([{node, ClientNode}, {port, Port},
+ {host, Hostname}, {from, self()},
+ {mfa, {?MODULE, recv_and_certificate, []}},
+ {options, ClientOptions}]),
+ ssl_test_lib:check_result(Server, ExpectedSNIHostname, Client, ExpectedCN).
+
+
+run_handshake(Config, SNIHostname, ExpectedSNIHostname, ExpectedCN) ->
+ ct:log("Start running handshake, Config: ~p, SNIHostname: ~p, ExpectedSNIHostname: ~p, ExpectedCN: ~p", [Config, SNIHostname, ExpectedSNIHostname, ExpectedCN]),
+ ServerOptions = ?config(sni_server_opts, Config) ++ ?config(server_opts, Config),
+ ClientOptions =
+ case SNIHostname of
+ undefined ->
+ ?config(client_opts, Config);
+ _ ->
+ [{server_name_indication, SNIHostname}] ++ ?config(client_opts, Config)
+ end,
+ ct:log("Options: ~p", [[ServerOptions, ClientOptions]]),
+ {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config),
+ Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0},
+ {from, self()}, {mfa, {?MODULE, send_and_hostname, []}},
+ {options, ServerOptions}]),
+ Port = ssl_test_lib:inet_port(Server),
+ Client = ssl_test_lib:start_client([{node, ClientNode}, {port, Port},
+ {host, Hostname}, {from, self()},
+ {mfa, {?MODULE, recv_and_certificate, []}},
+ {options, ClientOptions}]),
+ ssl_test_lib:check_result(Server, ExpectedSNIHostname, Client, ExpectedCN).
diff --git a/lib/ssl/test/ssl_test_lib.erl b/lib/ssl/test/ssl_test_lib.erl
index 7d0546210c..8b98e6f16b 100644
--- a/lib/ssl/test/ssl_test_lib.erl
+++ b/lib/ssl/test/ssl_test_lib.erl
@@ -354,6 +354,11 @@ cert_options(Config) ->
BadKeyFile = filename:join([?config(priv_dir, Config),
"badkey.pem"]),
PskSharedSecret = <<1,2,3,4,5,6,7,8,9,10,11,12,13,14,15>>,
+
+ SNIServerACertFile = filename:join([?config(priv_dir, Config), "a.server", "cert.pem"]),
+ SNIServerAKeyFile = filename:join([?config(priv_dir, Config), "a.server", "key.pem"]),
+ SNIServerBCertFile = filename:join([?config(priv_dir, Config), "b.server", "cert.pem"]),
+ SNIServerBKeyFile = filename:join([?config(priv_dir, Config), "b.server", "key.pem"]),
[{client_opts, [{ssl_imp, new},{reuseaddr, true}]},
{client_verification_opts, [{cacertfile, ClientCaCertFile},
{certfile, ClientCertFile},
@@ -414,7 +419,17 @@ cert_options(Config) ->
{server_bad_cert, [{ssl_imp, new},{cacertfile, ServerCaCertFile},
{certfile, BadCertFile}, {keyfile, ServerKeyFile}]},
{server_bad_key, [{ssl_imp, new},{cacertfile, ServerCaCertFile},
- {certfile, ServerCertFile}, {keyfile, BadKeyFile}]}
+ {certfile, ServerCertFile}, {keyfile, BadKeyFile}]},
+ {sni_server_opts, [{sni_hosts, [
+ {"a.server", [
+ {certfile, SNIServerACertFile},
+ {keyfile, SNIServerAKeyFile}
+ ]},
+ {"b.server", [
+ {certfile, SNIServerBCertFile},
+ {keyfile, SNIServerBKeyFile}
+ ]}
+ ]}]}
| Config].
@@ -1090,6 +1105,8 @@ cipher_restriction(Config0) ->
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" ++ _} ->
diff --git a/lib/ssl/test/ssl_to_openssl_SUITE.erl b/lib/ssl/test/ssl_to_openssl_SUITE.erl
index 942c446ec4..0413415e49 100644
--- a/lib/ssl/test/ssl_to_openssl_SUITE.erl
+++ b/lib/ssl/test/ssl_to_openssl_SUITE.erl
@@ -50,9 +50,9 @@ all() ->
groups() ->
[{basic, [], basic_tests()},
- {'tlsv1.2', [], all_versions_tests() ++ npn_tests()},
- {'tlsv1.1', [], all_versions_tests() ++ npn_tests()},
- {'tlsv1', [], all_versions_tests()++ npn_tests()},
+ {'tlsv1.2', [], all_versions_tests() ++ alpn_tests() ++ npn_tests() ++ sni_server_tests()},
+ {'tlsv1.1', [], all_versions_tests() ++ alpn_tests() ++ npn_tests() ++ sni_server_tests()},
+ {'tlsv1', [], all_versions_tests()++ alpn_tests() ++ npn_tests() ++ sni_server_tests()},
{'sslv3', [], all_versions_tests()}].
basic_tests() ->
@@ -79,6 +79,18 @@ all_versions_tests() ->
expired_session,
ssl2_erlang_server_openssl_client].
+alpn_tests() ->
+ [erlang_client_alpn_openssl_server_alpn,
+ erlang_server_alpn_openssl_client_alpn,
+ erlang_client_alpn_openssl_server,
+ erlang_client_openssl_server_alpn,
+ erlang_server_alpn_openssl_client,
+ erlang_server_openssl_client_alpn,
+ erlang_client_alpn_openssl_server_alpn_renegotiate,
+ erlang_server_alpn_openssl_client_alpn_renegotiate,
+ erlang_client_alpn_npn_openssl_server_alpn_npn,
+ erlang_server_alpn_npn_openssl_client_alpn_npn].
+
npn_tests() ->
[erlang_client_openssl_server_npn,
erlang_server_openssl_client_npn,
@@ -89,6 +101,14 @@ npn_tests() ->
erlang_client_openssl_server_npn_only_client,
erlang_client_openssl_server_npn_only_server].
+sni_server_tests() ->
+ [erlang_server_openssl_client_sni_match,
+ erlang_server_openssl_client_sni_match_fun,
+ erlang_server_openssl_client_sni_no_match,
+ erlang_server_openssl_client_sni_no_match_fun,
+ erlang_server_openssl_client_sni_no_header,
+ erlang_server_openssl_client_sni_no_header_fun].
+
init_per_suite(Config0) ->
Dog = ct:timetrap(?LONG_TIMEOUT *2),
@@ -161,6 +181,36 @@ special_init(ssl2_erlang_server_openssl_client, Config) ->
check_sane_openssl_sslv2(Config);
special_init(TestCase, Config)
+ when TestCase == erlang_client_alpn_openssl_server_alpn;
+ TestCase == erlang_server_alpn_openssl_client_alpn;
+ TestCase == erlang_client_alpn_openssl_server;
+ TestCase == erlang_client_openssl_server_alpn;
+ TestCase == erlang_server_alpn_openssl_client;
+ TestCase == erlang_server_openssl_client_alpn ->
+ check_openssl_alpn_support(Config);
+
+special_init(TestCase, Config)
+ when TestCase == erlang_client_alpn_openssl_server_alpn_renegotiate;
+ TestCase == erlang_server_alpn_openssl_client_alpn_renegotiate ->
+ {ok, Version} = application:get_env(ssl, protocol_version),
+ case check_sane_openssl_renegotaite(Config, Version) of
+ {skip, _} = Skip ->
+ Skip;
+ _ ->
+ check_openssl_alpn_support(Config)
+ end;
+
+special_init(TestCase, Config)
+ when TestCase == erlang_client_alpn_npn_openssl_server_alpn_npn;
+ TestCase == erlang_server_alpn_npn_openssl_client_alpn_npn ->
+ case check_openssl_alpn_support(Config) of
+ {skip, _} = Skip ->
+ Skip;
+ _ ->
+ check_openssl_npn_support(Config)
+ end;
+
+special_init(TestCase, Config)
when TestCase == erlang_client_openssl_server_npn;
TestCase == erlang_server_openssl_client_npn;
TestCase == erlang_server_openssl_client_npn_only_server;
@@ -179,6 +229,16 @@ special_init(TestCase, Config)
_ ->
check_openssl_npn_support(Config)
end;
+
+special_init(TestCase, Config)
+ when TestCase == erlang_server_openssl_client_sni_match;
+ TestCase == erlang_server_openssl_client_sni_no_match;
+ TestCase == erlang_server_openssl_client_sni_no_header;
+ TestCase == erlang_server_openssl_client_sni_match_fun;
+ TestCase == erlang_server_openssl_client_sni_no_match_fun;
+ TestCase == erlang_server_openssl_client_sni_no_header_fun ->
+ check_openssl_sni_support(Config);
+
special_init(_, Config) ->
Config.
@@ -924,6 +984,128 @@ ssl2_erlang_server_openssl_client(Config) when is_list(Config) ->
process_flag(trap_exit, false).
%%--------------------------------------------------------------------
+
+erlang_client_alpn_openssl_server_alpn(Config) when is_list(Config) ->
+ Data = "From openssl to erlang",
+ start_erlang_client_and_openssl_server_for_alpn_negotiation(Config, Data, fun(Client, OpensslPort) ->
+ true = port_command(OpensslPort, Data),
+
+ ssl_test_lib:check_result(Client, ok)
+ end),
+ ok.
+
+%%--------------------------------------------------------------------
+
+erlang_server_alpn_openssl_client_alpn(Config) when is_list(Config) ->
+ Data = "From openssl to erlang",
+ start_erlang_server_and_openssl_client_for_alpn_negotiation(Config, Data, fun(Client, OpensslPort) ->
+ true = port_command(OpensslPort, Data),
+
+ ssl_test_lib:check_result(Client, ok)
+ end),
+ ok.
+
+%%--------------------------------------------------------------------------
+
+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)
+ end),
+ ok.
+
+%%--------------------------------------------------------------------------
+
+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",
+ Data, fun(Server, OpensslPort) ->
+ true = port_command(OpensslPort, Data),
+ ssl_test_lib:check_result(Server, ok)
+ end),
+ ok.
+
+%%--------------------------------------------------------------------------
+
+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_advertised_protocols, [<<"spdy/2">>]}],
+ "",
+ Data, fun(Server, OpensslPort) ->
+ true = port_command(OpensslPort, Data),
+ ssl_test_lib:check_result(Server, ok)
+ end),
+ ok.
+
+%%--------------------------------------------------------------------------
+
+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",
+ Data, fun(Server, OpensslPort) ->
+ true = port_command(OpensslPort, Data),
+ ssl_test_lib:check_result(Server, ok)
+ end),
+ ok.
+
+%%--------------------------------------------------------------------
+
+erlang_client_alpn_openssl_server_alpn_renegotiate(Config) when is_list(Config) ->
+ Data = "From openssl to erlang",
+ start_erlang_client_and_openssl_server_for_alpn_negotiation(Config, Data, fun(Client, OpensslPort) ->
+ true = port_command(OpensslPort, ?OPENSSL_RENEGOTIATE),
+ ct:sleep(?SLEEP),
+ true = port_command(OpensslPort, Data),
+
+ ssl_test_lib:check_result(Client, ok)
+ end),
+ ok.
+
+%%--------------------------------------------------------------------
+
+erlang_server_alpn_openssl_client_alpn_renegotiate(Config) when is_list(Config) ->
+ Data = "From openssl to erlang",
+ start_erlang_server_and_openssl_client_for_alpn_negotiation(Config, Data, fun(Client, OpensslPort) ->
+ true = port_command(OpensslPort, ?OPENSSL_RENEGOTIATE),
+ ct:sleep(?SLEEP),
+ true = port_command(OpensslPort, Data),
+
+ ssl_test_lib:check_result(Client, ok)
+ end),
+ ok.
+
+%%--------------------------------------------------------------------
+
+erlang_client_alpn_npn_openssl_server_alpn_npn(Config) when is_list(Config) ->
+ Data = "From openssl to erlang",
+ start_erlang_client_and_openssl_server_for_alpn_npn_negotiation(Config, Data, fun(Client, OpensslPort) ->
+ true = port_command(OpensslPort, Data),
+
+ ssl_test_lib:check_result(Client, ok)
+ end),
+ ok.
+
+%%--------------------------------------------------------------------
+
+erlang_server_alpn_npn_openssl_client_alpn_npn(Config) when is_list(Config) ->
+ Data = "From openssl to erlang",
+ start_erlang_server_and_openssl_client_for_alpn_npn_negotiation(Config, Data, fun(Client, OpensslPort) ->
+ true = port_command(OpensslPort, Data),
+
+ ssl_test_lib:check_result(Client, ok)
+ end),
+ ok.
+
+%%--------------------------------------------------------------------
erlang_client_openssl_server_npn() ->
[{doc,"Test erlang client with openssl server doing npn negotiation"}].
@@ -1016,6 +1198,25 @@ erlang_server_openssl_client_npn_only_client(Config) when is_list(Config) ->
ssl_test_lib:check_result(Server, ok)
end),
ok.
+%--------------------------------------------------------------------------
+erlang_server_openssl_client_sni_no_header(Config) when is_list(Config) ->
+ erlang_server_openssl_client_sni_test(Config, undefined, undefined, "server").
+
+erlang_server_openssl_client_sni_no_header_fun(Config) when is_list(Config) ->
+ erlang_server_openssl_client_sni_test_sni_fun(Config, undefined, undefined, "server").
+
+erlang_server_openssl_client_sni_match(Config) when is_list(Config) ->
+ erlang_server_openssl_client_sni_test(Config, "a.server", "a.server", "a.server").
+
+erlang_server_openssl_client_sni_match_fun(Config) when is_list(Config) ->
+ erlang_server_openssl_client_sni_test_sni_fun(Config, "a.server", "a.server", "a.server").
+
+erlang_server_openssl_client_sni_no_match(Config) when is_list(Config) ->
+ erlang_server_openssl_client_sni_test(Config, "c.server", undefined, "server").
+
+erlang_server_openssl_client_sni_no_match_fun(Config) when is_list(Config) ->
+ erlang_server_openssl_client_sni_test_sni_fun(Config, "c.server", undefined, "server").
+
%%--------------------------------------------------------------------
%% Internal functions ------------------------------------------------
@@ -1042,6 +1243,89 @@ run_suites(Ciphers, Version, Config, Type) ->
ct:fail(cipher_suite_failed_see_test_case_log)
end.
+client_read_check([], _NewData) -> ok;
+client_read_check([Hd | T], NewData) ->
+ case binary:match(NewData, list_to_binary(Hd)) of
+ nomatch ->
+ nomatch;
+ _ ->
+ client_read_check(T, NewData)
+ end.
+client_read_bulk(Port, DataExpected, DataReceived) ->
+ receive
+ {Port, {data, TheData}} ->
+ Data = list_to_binary(TheData),
+ NewData = <<DataReceived/binary, Data/binary>>,
+ ct:log("New Data: ~p", [NewData]),
+ case client_read_check(DataExpected, NewData) of
+ ok ->
+ ok;
+ _ ->
+ client_read_bulk(Port, DataExpected, NewData)
+ end;
+ _ ->
+ ct:fail("unexpected_message")
+ after 4000 ->
+ ct:fail("timeout")
+ end.
+client_read_bulk(Port, DataExpected) ->
+ client_read_bulk(Port, DataExpected, <<"">>).
+
+send_and_hostname(SSLSocket) ->
+ ssl:send(SSLSocket, "OK"),
+ {ok, [{sni_hostname, Hostname}]} = ssl:connection_information(SSLSocket, [sni_hostname]),
+ Hostname.
+
+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]),
+ ServerOptions = ?config(sni_server_opts, Config) ++ ?config(server_opts, Config),
+ {_, ServerNode, Hostname} = ssl_test_lib:run_where(Config),
+ Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0},
+ {from, self()}, {mfa, {?MODULE, send_and_hostname, []}},
+ {options, ServerOptions}]),
+ Port = ssl_test_lib:inet_port(Server),
+ ClientCommand = case SNIHostname of
+ undefined ->
+ "openssl 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]),
+ ssl_test_lib:check_result(Server, ExpectedSNIHostname),
+ ExpectedClientOutput = ["OK", "/CN=" ++ ExpectedCN ++ "/"],
+ ok = client_read_bulk(ClientPort, ExpectedClientOutput),
+ ssl_test_lib:close_port(ClientPort),
+ ssl_test_lib:close(Server),
+ ok.
+
+
+erlang_server_openssl_client_sni_test_sni_fun(Config, SNIHostname, ExpectedSNIHostname, ExpectedCN) ->
+ ct:log("Start running handshake for sni_fun, Config: ~p, SNIHostname: ~p, ExpectedSNIHostname: ~p, ExpectedCN: ~p", [Config, SNIHostname, ExpectedSNIHostname, ExpectedCN]),
+ [{sni_hosts, ServerSNIConf}] = ?config(sni_server_opts, Config),
+ SNIFun = fun(Domain) -> proplists:get_value(Domain, ServerSNIConf, undefined) end,
+ ServerOptions = ?config(server_opts, Config) ++ [{sni_fun, SNIFun}],
+ {_, ServerNode, Hostname} = ssl_test_lib:run_where(Config),
+ Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0},
+ {from, self()}, {mfa, {?MODULE, send_and_hostname, []}},
+ {options, ServerOptions}]),
+ Port = ssl_test_lib:inet_port(Server),
+ ClientCommand = case SNIHostname of
+ undefined ->
+ "openssl 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]),
+ ssl_test_lib:check_result(Server, ExpectedSNIHostname),
+ ExpectedClientOutput = ["OK", "/CN=" ++ ExpectedCN ++ "/"],
+ ok = client_read_bulk(ClientPort, ExpectedClientOutput),
+ ssl_test_lib:close_port(ClientPort),
+ ssl_test_lib:close(Server),
+ ok.
+
+
cipher(CipherSuite, Version, Config, ClientOpts, ServerOpts) ->
process_flag(trap_exit, true),
ct:log("Testing CipherSuite ~p~n", [CipherSuite]),
@@ -1139,6 +1423,142 @@ start_erlang_client_and_openssl_server_with_opts(Config, ErlangClientOpts, Opens
ssl_test_lib:close(Client),
process_flag(trap_exit, false).
+start_erlang_client_and_openssl_server_for_alpn_negotiation(Config, Data, Callback) ->
+ process_flag(trap_exit, true),
+ ServerOpts = ?config(server_opts, Config),
+ ClientOpts0 = ?config(client_opts, Config),
+ ClientOpts = [{alpn_advertised_protocols, [<<"spdy/2">>]} | ClientOpts0],
+
+ {ClientNode, _, Hostname} = ssl_test_lib:run_where(Config),
+
+ Data = "From openssl to erlang",
+
+ Port = ssl_test_lib:inet_port(node()),
+ CertFile = proplists:get_value(certfile, ServerOpts),
+ KeyFile = proplists:get_value(keyfile, ServerOpts),
+ 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]),
+
+ ssl_test_lib:wait_for_openssl_server(),
+
+ Client = ssl_test_lib:start_client([{node, ClientNode}, {port, Port},
+ {host, Hostname},
+ {from, self()},
+ {mfa, {?MODULE,
+ erlang_ssl_receive_and_assert_negotiated_protocol, [<<"spdy/2">>, Data]}},
+ {options, ClientOpts}]),
+
+ Callback(Client, OpensslPort),
+
+ %% Clean close down! Server needs to be closed first !!
+ ssl_test_lib:close_port(OpensslPort),
+
+ ssl_test_lib:close(Client),
+ process_flag(trap_exit, false).
+
+start_erlang_server_and_openssl_client_for_alpn_negotiation(Config, Data, Callback) ->
+ process_flag(trap_exit, true),
+ ServerOpts0 = ?config(server_opts, Config),
+ ServerOpts = [{alpn_preferred_protocols, [<<"spdy/2">>]} | ServerOpts0],
+
+ {_, ServerNode, _} = ssl_test_lib:run_where(Config),
+
+
+ Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0},
+ {from, self()},
+ {mfa, {?MODULE, erlang_ssl_receive_and_assert_negotiated_protocol, [<<"spdy/2">>, Data]}},
+ {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]),
+
+ OpenSslPort = open_port({spawn, Cmd}, [stderr_to_stdout]),
+
+ Callback(Server, OpenSslPort),
+
+ ssl_test_lib:close(Server),
+
+ ssl_test_lib:close_port(OpenSslPort),
+ process_flag(trap_exit, false).
+
+start_erlang_client_and_openssl_server_for_alpn_npn_negotiation(Config, Data, Callback) ->
+ process_flag(trap_exit, true),
+ ServerOpts = ?config(server_opts, Config),
+ ClientOpts0 = ?config(client_opts, Config),
+ ClientOpts = [{alpn_advertised_protocols, [<<"spdy/2">>]},
+ {client_preferred_next_protocols, {client, [<<"spdy/3">>, <<"http/1.1">>]}} | ClientOpts0],
+
+ {ClientNode, _, Hostname} = ssl_test_lib:run_where(Config),
+
+ Data = "From openssl to erlang",
+
+ Port = ssl_test_lib:inet_port(node()),
+ CertFile = proplists:get_value(certfile, ServerOpts),
+ KeyFile = proplists:get_value(keyfile, ServerOpts),
+ Version = tls_record:protocol_version(tls_record:highest_protocol_version([])),
+
+ Cmd = "openssl s_server -msg -alpn http/1.1,spdy/2 -nextprotoneg spdy/3 -accept " ++ integer_to_list(Port) ++ ssl_test_lib:version_flag(Version) ++
+ " -cert " ++ CertFile ++ " -key " ++ KeyFile,
+
+ ct:log("openssl cmd: ~p~n", [Cmd]),
+
+ OpensslPort = open_port({spawn, Cmd}, [stderr_to_stdout]),
+
+ ssl_test_lib:wait_for_openssl_server(),
+
+ Client = ssl_test_lib:start_client([{node, ClientNode}, {port, Port},
+ {host, Hostname},
+ {from, self()},
+ {mfa, {?MODULE,
+ erlang_ssl_receive_and_assert_negotiated_protocol, [<<"spdy/2">>, Data]}},
+ {options, ClientOpts}]),
+
+ Callback(Client, OpensslPort),
+
+ %% Clean close down! Server needs to be closed first !!
+ ssl_test_lib:close_port(OpensslPort),
+
+ ssl_test_lib:close(Client),
+ process_flag(trap_exit, false).
+
+start_erlang_server_and_openssl_client_for_alpn_npn_negotiation(Config, Data, Callback) ->
+ process_flag(trap_exit, true),
+ ServerOpts0 = ?config(server_opts, Config),
+ ServerOpts = [{alpn_preferred_protocols, [<<"spdy/2">>]},
+ {next_protocols_advertised, [<<"spdy/3">>, <<"http/1.1">>]} | ServerOpts0],
+
+ {_, ServerNode, _} = ssl_test_lib:run_where(Config),
+
+
+ Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0},
+ {from, self()},
+ {mfa, {?MODULE, erlang_ssl_receive_and_assert_negotiated_protocol, [<<"spdy/2">>, Data]}},
+ {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]),
+
+ Callback(Server, OpenSslPort),
+
+ ssl_test_lib:close(Server),
+
+ ssl_test_lib:close_port(OpenSslPort),
+ process_flag(trap_exit, false).
+
start_erlang_client_and_openssl_server_for_npn_negotiation(Config, Data, Callback) ->
process_flag(trap_exit, true),
ServerOpts = ?config(server_opts, Config),
@@ -1167,7 +1587,7 @@ start_erlang_client_and_openssl_server_for_npn_negotiation(Config, Data, Callbac
{host, Hostname},
{from, self()},
{mfa, {?MODULE,
- erlang_ssl_receive_and_assert_npn, [<<"spdy/2">>, Data]}},
+ erlang_ssl_receive_and_assert_negotiated_protocol, [<<"spdy/2">>, Data]}},
{options, ClientOpts}]),
Callback(Client, OpensslPort),
@@ -1188,7 +1608,7 @@ start_erlang_server_and_openssl_client_for_npn_negotiation(Config, Data, Callbac
Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0},
{from, self()},
- {mfa, {?MODULE, erlang_ssl_receive_and_assert_npn, [<<"spdy/2">>, Data]}},
+ {mfa, {?MODULE, erlang_ssl_receive_and_assert_negotiated_protocol, [<<"spdy/2">>, Data]}},
{options, ServerOpts}]),
Port = ssl_test_lib:inet_port(Server),
Version = tls_record:protocol_version(tls_record:highest_protocol_version([])),
@@ -1236,10 +1656,10 @@ start_erlang_server_and_openssl_client_with_opts(Config, ErlangServerOpts, OpenS
process_flag(trap_exit, false).
-erlang_ssl_receive_and_assert_npn(Socket, Protocol, Data) ->
- {ok, Protocol} = ssl:negotiated_next_protocol(Socket),
+erlang_ssl_receive_and_assert_negotiated_protocol(Socket, Protocol, Data) ->
+ {ok, Protocol} = ssl:negotiated_protocol(Socket),
erlang_ssl_receive(Socket, Data),
- {ok, Protocol} = ssl:negotiated_next_protocol(Socket),
+ {ok, Protocol} = ssl:negotiated_protocol(Socket),
ok.
erlang_ssl_receive(Socket, Data) ->
@@ -1287,6 +1707,14 @@ server_sent_garbage(Socket) ->
end.
+check_openssl_sni_support(Config) ->
+ HelpText = os:cmd("openssl s_client --help"),
+ case string:str(HelpText, "-servername") of
+ 0 ->
+ {skip, "Current openssl doesn't support SNI"};
+ _ ->
+ Config
+ end.
check_openssl_npn_support(Config) ->
HelpText = os:cmd("openssl s_client --help"),
@@ -1297,6 +1725,15 @@ check_openssl_npn_support(Config) ->
Config
end.
+check_openssl_alpn_support(Config) ->
+ HelpText = os:cmd("openssl s_client --help"),
+ case string:str(HelpText, "alpn") of
+ 0 ->
+ {skip, "Openssl not compiled with alpn support"};
+ _ ->
+ Config
+ end.
+
check_sane_openssl_renegotaite(Config, Version) when Version == 'tlsv1.1';
Version == 'tlsv1.2' ->
case os:cmd("openssl version") of