aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--lib/public_key/doc/src/public_key.xml112
-rw-r--r--lib/public_key/src/pubkey_cert.erl152
-rw-r--r--lib/public_key/src/public_key.erl27
-rw-r--r--lib/ssl/doc/src/ssl_distribution.xml128
-rw-r--r--lib/ssl/src/inet_tls_dist.erl8
-rw-r--r--lib/ssl/src/ssl.erl20
-rw-r--r--lib/ssl/src/ssl_dist_sup.erl67
-rw-r--r--lib/ssl/src/ssl_tls_dist_proxy.erl30
8 files changed, 434 insertions, 110 deletions
diff --git a/lib/public_key/doc/src/public_key.xml b/lib/public_key/doc/src/public_key.xml
index 5230cef496..dea35bc390 100644
--- a/lib/public_key/doc/src/public_key.xml
+++ b/lib/public_key/doc/src/public_key.xml
@@ -774,6 +774,7 @@ fun(#'DistributionPoint'{}, #'CertificateList'{},
<func>
<name>pkix_test_data(Options) -> Config </name>
+ <name>pkix_test_data([chain_opts()]) -> [conf_opt()]</name>
<fsummary>Creates certificate test data.</fsummary>
<type>
<v>Options = #{chain_type() := chain_opts()} </v>
@@ -781,30 +782,83 @@ fun(#'DistributionPoint'{}, #'CertificateList'{},
<v>chain_type() = server_chain | client_chain </v>
- <v>chain_opts() = #{chain_end() := [cert_opt()],
- intermediates => [[cert_opt()]]}</v>
- <d>A valid chain must have at least a ROOT and a peer cert</d>
-
- <v>chain_end() = root | peer </v>
-
+ <v>chain_opts() = #{root := [cert_opt()] | root_cert(),
+ peer := [cert_opt()],
+ intermediates => [[cert_opt()]]}</v>
+ <d>
+ A valid chain must have at least a ROOT and a peer cert.
+ The root cert can be given either as a cert pre-generated by
+ <seealso marker="#pkix_test_root_cert-2">
+ pkix_test_root_cert/2
+ </seealso>, or as root cert generation options.
+ </d>
+ <v>root_cert() = #{cert := der_encoded(), key := Key}</v>
+ <d>
+ A root certificate generated by
+ <seealso marker="#pkix_test_root_cert-2">
+ pkix_test_root_cert/2
+ </seealso>.
+ </d>
<v>cert_opt() = {Key, Value}</v>
<d>For available options see <seealso marker="#cert_opt"> cert_opt()</seealso> below.</d>
<v>Config = #{server_config := [conf_opt()],
client_config := [conf_opt()]}</v>
- <v>conf_opt() = {cert, der_encoded()} | {key, der_encoded()} |{cacerts, [der_encoded()]}</v>
- <d>This is a subset of the type <seealso marker="ssl:ssl#type-ssloption"> ssl:ssl_option()</seealso> </d>
+ <v>conf_opt() = {cert, der_encoded()} | {key, PrivateKey} |{cacerts, [der_encoded()]}</v>
+ <d>
+ This is a subset of the type
+ <seealso marker="ssl:ssl#type-ssloption"> ssl:ssl_option()</seealso>.
+ <c>PrivateKey</c> is what
+ <seealso marker="#generate_key-1">generate_key/1</seealso>
+ returns.
+ </d>
</type>
<desc>
- <p>Creates certificate test data to facilitate automated testing
- of applications using X509-certificates often through
- SSL/TLS. The test data can be used when you have control
- over both the client and the server in a test scenario.
+ <p>
+ Creates certificate configuration(s) consisting of certificate
+ and its private key plus CA certificate bundle, for a client
+ and a server, intended to facilitate automated testing
+ of applications using X509-certificates,
+ often through SSL/TLS. The test data can be used
+ when you have control over both the client and the server
+ in a test scenario.
+ </p>
+ <p>
+ When this function is called with a map containing
+ client and server chain specifications;
+ it generates both a client and a server certificate chain
+ where the <c>cacerts</c>
+ returned for the server contains the root cert the server
+ should trust and the intermediate certificates the server
+ should present to connecting clients.
+ The root cert the server should trust is the one used
+ as root of the client certificate chain.
+ Vice versa applies to the <c>cacerts</c> returned for the client.
+ The root cert(s) can either be pre-generated with
+ <seealso marker="#pkix_test_root_cert-2">
+ pkix_test_root_cert/2
+ </seealso>, or if options are specified; it is (they are)
+ generated.
+ </p>
+ <p>
+ When this function is called with a list of certificate options;
+ it generates a configuration with just one node certificate
+ where <c>cacerts</c> contains the root cert
+ and the intermediate certs that should be presented to a peer.
+ In this case the same root cert must be used for all peers.
+ This is useful in for example an Erlang distributed cluster
+ where any node, towards another node, acts either
+ as a server or as a client depending on who connects to whom.
+ The generated certificate contains a subject altname,
+ which is not needed in a client certificate,
+ but makes the certificate useful for both roles.
+ </p>
+ <p>
+ The <marker id="cert_opt"/><c>cert_opt()</c>
+ type consists of the following options:
</p>
-
- <p> The <marker id="cert_opt"/> cert_opt() type consists of the following options: </p>
<taglist>
<tag> {digest, digest_type()}</tag>
<item><p>Hash algorithm to be used for
@@ -851,6 +905,36 @@ fun(#'DistributionPoint'{}, #'CertificateList'{},
</desc>
</func>
+ <func>
+ <name>pkix_test_root_cert(Name, Options) -> RootCert</name>
+ <fsummary>Generates a test data root cert.</fsummary>
+ <type>
+ <v>Name = string()</v>
+ <d>The root certificate name.</d>
+ <v>Options = [cert_opt()]</v>
+ <d>
+ For available options see
+ <seealso marker="#cert_opt">cert_opt()</seealso>
+ under
+ <seealso marker="#pkix_test_data-1">pkix_test_data/1</seealso>.
+ </d>
+ <v>RootCert = #{cert := der_encoded(), key := Key}</v>
+ <d>
+ A root certificate and key. The <c>Key</c> is generated by
+ <seealso marker="#generate_key-1">generate_key/1</seealso>.
+ </d>
+ </type>
+ <desc>
+ <p>
+ Generates a root certificate that can be used
+ in multiple calls to
+ <seealso marker="#pkix_test_data-1">pkix_test_data/1</seealso>
+ when you want the same root certificate for
+ several generated certificates.
+ </p>
+ </desc>
+ </func>
+
<func>
<name>pkix_verify(Cert, Key) -> boolean()</name>
<fsummary>Verifies PKIX x.509 certificate signature.</fsummary>
diff --git a/lib/public_key/src/pubkey_cert.erl b/lib/public_key/src/pubkey_cert.erl
index 76fd0f8133..c433a96585 100644
--- a/lib/public_key/src/pubkey_cert.erl
+++ b/lib/public_key/src/pubkey_cert.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2008-2016. All Rights Reserved.
+%% Copyright Ericsson AB 2008-2017. All Rights Reserved.
%%
%% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License.
@@ -33,11 +33,12 @@
is_fixed_dh_cert/1, verify_data/1, verify_fun/4,
select_extension/2, match_name/3,
extensions_list/1, cert_auth_key_id/1, time_str_2_gregorian_sec/1,
- gen_test_certs/1]).
+ gen_test_certs/1, root_cert/2]).
-define(NULL, 0).
--export_type([chain_opts/0, test_config/0]).
+-export_type([cert_opt/0, chain_opts/0, conf_opt/0,
+ test_config/0, test_root_cert/0]).
-type cert_opt() :: {digest, public_key:digest_type()} |
{key, public_key:key_params() | public_key:private_key()} |
@@ -46,9 +47,12 @@
-type chain_end() :: root | peer.
-type chain_opts() :: #{chain_end() := [cert_opt()], intermediates => [[cert_opt()]]}.
-type conf_opt() :: {cert, public_key:der_encoded()} |
- {key, public_key:der_encoded()} |
+ {key, public_key:private_key()} |
{cacerts, [public_key:der_encoded()]}.
--type test_config() :: #{server_config := [conf_opt()], client_config := [conf_opt()]}.
+-type test_config() ::
+ #{server_config := [conf_opt()], client_config := [conf_opt()]}.
+-type test_root_cert() ::
+ #{cert := binary(), key := public_key:private_key()}.
%%====================================================================
%% Internal application APIu
%%====================================================================
@@ -430,31 +434,94 @@ match_name(Fun, Name, PermittedName, [Head | Tail]) ->
false ->
match_name(Fun, Name, Head, Tail)
end.
+
%%%
--spec gen_test_certs(#{server_chain:= chain_opts(), client_chain:= chain_opts()}) -> test_config().
-
-%% Generates server and and client configuration for testing
+-spec gen_test_certs(#{server_chain:= chain_opts(),
+ client_chain:= chain_opts()} |
+ chain_opts()) ->
+ test_config() |
+ [conf_opt()].
+%%
+%% Generates server and and client configuration for testing
%% purposes. All certificate options have default values
-gen_test_certs(#{client_chain := #{root := ClientRootConf,
- intermediates := ClientCAs,
- peer := ClientPeer},
- server_chain :=
- #{root := ServerRootConf,
- intermediates := ServerCAs,
- peer := ServerPeer}}) ->
- SRootKey = gen_key(proplists:get_value(key, ServerRootConf, default_key_gen())),
- CRootKey = gen_key(proplists:get_value(key, ClientRootConf, default_key_gen())),
- ServerRoot = root_cert("server", SRootKey, ClientRootConf),
- ClientRoot = root_cert("client", CRootKey, ServerRootConf),
-
- [{ServerDERCert, ServerDERKey} | ServerCAsKeys] = config(server, ServerRoot,
- SRootKey, lists:reverse([ServerPeer | lists:reverse(ServerCAs)])),
- [{ClientDERCert, ClientDERKey} | ClientCAsKeys] = config(client, ClientRoot,
- CRootKey, lists:reverse([ClientPeer | lists:reverse(ClientCAs)])),
- ServerDERCA = ca_config(ClientRoot, ServerCAsKeys),
- ClientDERCA = ca_config(ServerRoot, ClientCAsKeys),
- #{server_config => [{cert, ServerDERCert}, {key, ServerDERKey}, {cacerts, ServerDERCA}],
- client_config => [{cert, ClientDERCert}, {key, ClientDERKey}, {cacerts, ClientDERCA}]}.
+gen_test_certs(
+ #{client_chain :=
+ #{root := ClientRoot,
+ intermediates := ClientCAs,
+ peer := ClientPeer},
+ server_chain :=
+ #{root := ServerRoot,
+ intermediates := ServerCAs,
+ peer := ServerPeer}}) ->
+ #{cert := ServerRootCert, key := ServerRootKey} =
+ case ServerRoot of
+ #{} ->
+ ServerRoot;
+ ServerRootConf when is_list(ServerRootConf) ->
+ root_cert("SERVER ROOT CA", ServerRootConf)
+ end,
+ #{cert := ClientRootCert, key := ClientRootKey} =
+ case ClientRoot of
+ #{} ->
+ ClientRoot;
+ ClientRootConf when is_list(ClientRootConf) ->
+ root_cert("CLIENT ROOT CA", ClientRootConf)
+ end,
+ [{ServerDERCert, ServerDERKey} | ServerCAsKeys] =
+ config(
+ server, ServerRootCert, ServerRootKey,
+ lists:reverse([ServerPeer | lists:reverse(ServerCAs)])),
+ [{ClientDERCert, ClientDERKey} | ClientCAsKeys] =
+ config(
+ client, ClientRootCert, ClientRootKey,
+ lists:reverse([ClientPeer | lists:reverse(ClientCAs)])),
+ ServerDERCA = ca_config(ClientRootCert, ServerCAsKeys),
+ ClientDERCA = ca_config(ServerRootCert, ClientCAsKeys),
+ #{server_config =>
+ [{cert, ServerDERCert}, {key, ServerDERKey},
+ {cacerts, ServerDERCA}],
+ client_config =>
+ [{cert, ClientDERCert}, {key, ClientDERKey},
+ {cacerts, ClientDERCA}]};
+%%
+%% Generates a node configuration for testing purposes,
+%% when using the node server cert also for the client.
+%% All certificate options have default values
+gen_test_certs(
+ #{root := Root, intermediates := CAs, peer := Peer}) ->
+ #{cert := RootCert, key := RootKey} =
+ case Root of
+ #{} ->
+ Root;
+ RootConf when is_list(RootConf) ->
+ root_cert("SERVER ROOT CA", RootConf)
+ end,
+ [{DERCert, DERKey} | CAsKeys] =
+ config(
+ server, RootCert, RootKey,
+ lists:reverse([Peer | lists:reverse(CAs)])),
+ DERCAs = ca_config(RootCert, CAsKeys),
+ [{cert, DERCert}, {key, DERKey}, {cacerts, DERCAs}].
+
+%%%
+-spec root_cert(string(), [cert_opt()]) -> test_root_cert().
+%%
+%% Generate a self-signed root cert
+root_cert(Name, Opts) ->
+ PrivKey = gen_key(proplists:get_value(key, Opts, default_key_gen())),
+ TBS = cert_template(),
+ Issuer = subject("root", Name),
+ OTPTBS =
+ TBS#'OTPTBSCertificate'{
+ signature = sign_algorithm(PrivKey, Opts),
+ issuer = Issuer,
+ validity = validity(Opts),
+ subject = Issuer,
+ subjectPublicKeyInfo = public_key(PrivKey),
+ extensions = extensions(undefined, ca, Opts)
+ },
+ #{cert => public_key:pkix_sign(OTPTBS, PrivKey),
+ key => PrivKey}.
%%--------------------------------------------------------------------
%%% Internal functions
@@ -1103,7 +1170,7 @@ missing_basic_constraints(OtpCert, SelfSigned, ValidationState, VerifyFun, UserS
UserState}
end.
- gen_key(KeyGen) ->
+gen_key(KeyGen) ->
case is_key(KeyGen) of
true ->
KeyGen;
@@ -1120,28 +1187,14 @@ is_key(#'ECPrivateKey'{}) ->
is_key(_) ->
false.
-root_cert(Role, PrivKey, Opts) ->
- TBS = cert_template(),
- Issuer = issuer("root", Role, " ROOT CA"),
- OTPTBS = TBS#'OTPTBSCertificate'{
- signature = sign_algorithm(PrivKey, Opts),
- issuer = Issuer,
- validity = validity(Opts),
- subject = Issuer,
- subjectPublicKeyInfo = public_key(PrivKey),
- extensions = extensions(Role, ca, Opts)
- },
- public_key:pkix_sign(OTPTBS, PrivKey).
cert_template() ->
#'OTPTBSCertificate'{
version = v3,
- serialNumber = trunc(rand:uniform()*100000000)*10000 + 1,
+ serialNumber = erlang:unique_integer([positive, monotonic]),
issuerUniqueID = asn1_NOVALUE,
subjectUniqueID = asn1_NOVALUE
}.
-issuer(Contact, Role, Name) ->
- subject(Contact, Role ++ Name).
subject(Contact, Name) ->
Opts = [{email, Contact ++ "@example.org"},
@@ -1176,9 +1229,11 @@ validity(Opts) ->
DefFrom0 = calendar:gregorian_days_to_date(calendar:date_to_gregorian_days(date())-1),
DefTo0 = calendar:gregorian_days_to_date(calendar:date_to_gregorian_days(date())+7),
{DefFrom, DefTo} = proplists:get_value(validity, Opts, {DefFrom0, DefTo0}),
- Format = fun({Y,M,D}) ->
- lists:flatten(io_lib:format("~w~2..0w~2..0w000000Z",[Y,M,D]))
- end,
+ Format =
+ fun({Y,M,D}) ->
+ lists:flatten(
+ io_lib:format("~4..0w~2..0w~2..0w000000Z",[Y,M,D]))
+ end,
#'Validity'{notBefore={generalTime, Format(DefFrom)},
notAfter ={generalTime, Format(DefTo)}}.
@@ -1240,7 +1295,6 @@ cert(Role, #'OTPCertificate'{tbsCertificate = #'OTPTBSCertificate'{subject = Iss
subject = subject(Contact, atom_to_list(Role) ++ Name),
subjectPublicKeyInfo = public_key(Key),
extensions = extensions(Role, Type, Opts)
-
},
public_key:pkix_sign(OTPTBS, PrivKey).
@@ -1297,7 +1351,7 @@ add_default_extensions(server, peer, Exts) ->
],
add_default_extensions(Default, Exts);
-add_default_extensions(_, peer, Exts) ->
+add_default_extensions(client, peer, Exts) ->
Exts.
add_default_extensions(Defaults0, Exts) ->
diff --git a/lib/public_key/src/public_key.erl b/lib/public_key/src/public_key.erl
index 6788c1ee92..034126655c 100644
--- a/lib/public_key/src/public_key.erl
+++ b/lib/public_key/src/public_key.erl
@@ -59,7 +59,8 @@
pkix_crl_verify/2,
pkix_crl_issuer/1,
short_name_hash/1,
- pkix_test_data/1
+ pkix_test_data/1,
+ pkix_test_root_cert/2
]).
-export_type([public_key/0, private_key/0, pem_entry/0,
@@ -1033,10 +1034,12 @@ short_name_hash({rdnSequence, _Attributes} = Name) ->
%%--------------------------------------------------------------------
--spec pkix_test_data(#{chain_type() := pubkey_cert:chain_opts()}) ->
- pubkey_cert:test_config().
+-spec pkix_test_data(#{chain_type() := pubkey_cert:chain_opts()} |
+ pubkey_cert:chain_opts()) ->
+ pubkey_cert:test_config() |
+ [pubkey_cert:conf_opt()].
-%% Description: Generates OpenSSL-style hash of a name.
+%% Description: Generates cert(s) and ssl configuration
%%--------------------------------------------------------------------
pkix_test_data(#{client_chain := ClientChain0,
@@ -1045,7 +1048,21 @@ pkix_test_data(#{client_chain := ClientChain0,
ClientChain = maps:merge(Default, ClientChain0),
ServerChain = maps:merge(Default, ServerChain0),
pubkey_cert:gen_test_certs(#{client_chain => ClientChain,
- server_chain => ServerChain}).
+ server_chain => ServerChain});
+pkix_test_data(#{} = Chain) ->
+ Default = #{intermediates => []},
+ pubkey_cert:gen_test_certs(maps:merge(Default, Chain)).
+
+%%--------------------------------------------------------------------
+-spec pkix_test_root_cert(
+ Name :: string(), Opts :: [pubkey_cert:cert_opt()]) ->
+ pubkey_cert:test_root_cert().
+
+%% Description: Generates a root cert suitable for pkix_test_data/1
+%%--------------------------------------------------------------------
+
+pkix_test_root_cert(Name, Opts) ->
+ pubkey_cert:root_cert(Name, Opts).
%%--------------------------------------------------------------------
%%% Internal functions
diff --git a/lib/ssl/doc/src/ssl_distribution.xml b/lib/ssl/doc/src/ssl_distribution.xml
index 61f88e3860..7f8a08f704 100644
--- a/lib/ssl/doc/src/ssl_distribution.xml
+++ b/lib/ssl/doc/src/ssl_distribution.xml
@@ -4,7 +4,7 @@
<chapter>
<header>
<copyright>
- <year>2000</year><year>2016</year>
+ <year>2000</year><year>2017</year>
<holder>Ericsson AB. All Rights Reserved.</holder>
</copyright>
<legalnotice>
@@ -180,10 +180,96 @@ Eshell V5.0 (abort with ^G)
<section>
<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>
+ The SSL distribution options can be written into a file
+ that is consulted when the node is started. This file name
+ is then specified with the command line argument
+ <c>-ssl_dist_optfile</c>.
+ </p>
+ <p>
+ Any available SSL option can be specified in an options file,
+ but note that options that take a <c>fun()</c> has to use
+ the syntax <c>fun Mod:Func/Arity</c> since a function
+ body can not be compiled when consulting a file.
+ </p>
+ <p>
+ Do not tamper with the socket options
+ <c>list</c>, <c>binary</c>, <c>active</c>, <c>packet</c>,
+ <c>nodelay</c> and <c>deliver</c> since they are used
+ by the distribution protocol handler itself.
+ Other raw socket options such as <c>packet_size</c> may
+ interfere severely, so beware!
+ </p>
+ <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 file
+ <c>"/home/me/ssl/erlserver.pem"</c> contains both
+ the server certificate and its private key.
+ </p>
+ <p>
+ Create a file named for example
+ <c>"/home/me/ssl/[email protected]"</c>:
+ </p>
+ <code type="none"><![CDATA[
+[{server,
+ [{certfile, "/home/me/ssl/erlserver.pem"},
+ {secure_renegotiate, true}]},
+ {client,
+ [{secure_renegotiate, true}]}].]]>
+ </code>
+ <p>
+ And then start the node like this
+ (line breaks in the command are for readability,
+ and shall not be there when typed):
+ </p>
+ <code type="none"><![CDATA[
+$ erl -boot /home/me/ssl/start_ssl -proto_dist inet_tls
+ -ssl_dist_optfile "/home/me/ssl/[email protected]"
+ -sname ssl_test]]>
+ </code>
+ <p>
+ The options in the <c>{server, Opts}</c> tuple are used
+ when calling <c>ssl:ssl_accept/3</c>, and the options in the
+ <c>{client, Opts}</c> tuple are used when calling
+ <c>ssl:connect/4</c>.
+ </p>
+ <p>
+ For the client, the option
+ <c>{server_name_indication, atom_to_list(TargetNode)}</c>
+ is added when connecting.
+ This makes it possible to use the client option
+ <c>{verify, verify_peer}</c>,
+ and the client will verify that the certificate matches
+ the node name you are connecting to.
+ This only works if the the server certificate is issued
+ to the name <c>atom_to_list(TargetNode)</c>.
+ </p>
+ <p>
+ For the server it is also possible to use the option
+ <c>{verify, verify_peer}</c> and the server will only accept
+ client connections with certificates that are trusted by
+ a root certificate that the server knows.
+ A client that presents an untrusted certificate will be rejected.
+ This option is preferably combined with
+ <c>{fail_if_no_peer_cert, true}</c> or a client will
+ still be accepted if it does not present any certificate.
+ </p>
+ <p>
+ A node started in this way is fully functional, using SSL
+ as the distribution protocol.
+ </p>
+ </section>
+
+ <section>
+ <title>Specifying SSL Options (Legacy)</title>
+
+ <p>
+ As in the previous section the PEM file
+ <c>"/home/me/ssl/erlserver.pem"</c> contains both
+ 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>
@@ -226,24 +312,26 @@ Eshell V5.0 (abort with ^G)
SSL options and their values. Argument <c>-ssl_dist_opt</c> can
be repeated any number of times.</p>
- <p>An example command line can now look as follows
+ <p>
+ An example command line doing the same as the example
+ in the previous section can now look as follows
(line breaks in the command are for readability,
- and are not be there when typed):</p>
- <code type="none">
+ and shall not be there when typed):
+ </p>
+ <code type="none"><![CDATA[
$ erl -boot /home/me/ssl/start_ssl -proto_dist inet_tls
- -ssl_dist_opt server_certfile "/home/me/ssl/erlserver.pem"
+ -ssl_dist_opt server_certfile "/home/me/ssl/erlserver.pem"
-ssl_dist_opt server_secure_renegotiate true client_secure_renegotiate true
-sname ssl_test
Erlang (BEAM) emulator version 5.0 [source]
-
+
Eshell V5.0 (abort with ^G)
-(ssl_test@myhost)1> </code>
- <p>A node started in this way is fully functional, using SSL
- as the distribution protocol.</p>
+(ssl_test@myhost)1>]]>
+ </code>
</section>
<section>
- <title>Setting up Environment to Always Use SSL</title>
+ <title>Setting up Environment to Always Use SSL (Legacy)</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
@@ -285,15 +373,11 @@ Eshell V5.0 (abort with ^G)
variable.</p>
<p>An example command line with this option would look like this:</p>
- <code type="none">
+ <code type="none"><![CDATA[
$ erl -boot /home/me/ssl/start_ssl -proto_dist inet6_tls
- -ssl_dist_opt server_certfile "/home/me/ssl/erlserver.pem"
- -ssl_dist_opt server_secure_renegotiate true client_secure_renegotiate true
- -sname ssl_test
-Erlang (BEAM) emulator version 5.0 [source]
-
-Eshell V5.0 (abort with ^G)
-(ssl_test@myhost)1> </code>
+ -ssl_dist_optfile "/home/me/ssl/[email protected]"
+ -sname ssl_test]]>
+ </code>
<p>A node started in this way will only be able to communicate with
other nodes using SSL distribution over IPv6.</p>
diff --git a/lib/ssl/src/inet_tls_dist.erl b/lib/ssl/src/inet_tls_dist.erl
index 78094c474b..4c677b9c33 100644
--- a/lib/ssl/src/inet_tls_dist.erl
+++ b/lib/ssl/src/inet_tls_dist.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2011-2016. All Rights Reserved.
+%% Copyright Ericsson AB 2011-2017. All Rights Reserved.
%%
%% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License.
@@ -93,7 +93,11 @@ do_setup(Driver, Kernel, Node, Type, MyNode, LongOrShortNames, SetupTime) ->
?trace("port_please(~p) -> version ~p~n",
[Node,Version]),
dist_util:reset_timer(Timer),
- case ssl_tls_dist_proxy:connect(Driver, Address, TcpPort) of
+ case
+ ssl_tls_dist_proxy:connect(
+ Driver, Address, TcpPort,
+ [{server_name_indication, atom_to_list(Node)}])
+ of
{ok, Socket} ->
HSData = connect_hs_data(Kernel, Node, MyNode, Socket,
Timer, Version, Ip, TcpPort, Address,
diff --git a/lib/ssl/src/ssl.erl b/lib/ssl/src/ssl.erl
index 4007e44a83..4bff9fdf39 100644
--- a/lib/ssl/src/ssl.erl
+++ b/lib/ssl/src/ssl.erl
@@ -992,17 +992,21 @@ validate_option(next_protocols_advertised, Value) when is_list(Value) ->
Value;
validate_option(next_protocols_advertised, undefined) ->
undefined;
-validate_option(server_name_indication = Opt, Value) when is_list(Value) ->
+validate_option(server_name_indication, Value) when is_list(Value) ->
%% RFC 6066, Section 3: Currently, the only server names supported are
%% DNS hostnames
- case inet_parse:domain(Value) of
- false ->
- throw({error, {options, {{Opt, Value}}}});
- true ->
- Value
- end;
-validate_option(server_name_indication, undefined = Value) ->
+ %% case inet_parse:domain(Value) of
+ %% false ->
+ %% throw({error, {options, {{Opt, Value}}}});
+ %% true ->
+ %% Value
+ %% end;
+ %%
+ %% But the definition seems very diffuse, so let all strings through
+ %% and leave it up to public_key to decide...
Value;
+validate_option(server_name_indication, undefined) ->
+ undefined;
validate_option(server_name_indication, disable) ->
disable;
diff --git a/lib/ssl/src/ssl_dist_sup.erl b/lib/ssl/src/ssl_dist_sup.erl
index 690b896919..e92f3d3979 100644
--- a/lib/ssl/src/ssl_dist_sup.erl
+++ b/lib/ssl/src/ssl_dist_sup.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2011-2016. All Rights Reserved.
+%% Copyright Ericsson AB 2011-2017. All Rights Reserved.
%%
%% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License.
@@ -30,6 +30,9 @@
%% Supervisor callback
-export([init/1]).
+%% Debug
+-export([consult/1]).
+
%%%=========================================================================
%%% API
%%%=========================================================================
@@ -37,7 +40,18 @@
-spec start_link() -> {ok, pid()} | ignore | {error, term()}.
start_link() ->
- supervisor:start_link({local, ?MODULE}, ?MODULE, []).
+ case init:get_argument(ssl_dist_optfile) of
+ {ok, [File]} ->
+ DistOpts = consult(File),
+ TabOpts = [set, protected, named_table],
+ Tab = ets:new(ssl_dist_opts, TabOpts),
+ true = ets:insert(Tab, DistOpts),
+ supervisor:start_link({local, ?MODULE}, ?MODULE, []);
+ {ok, BadArg} ->
+ error({bad_ssl_dist_optfile, BadArg});
+ error ->
+ supervisor:start_link({local, ?MODULE}, ?MODULE, [])
+ end.
%%%=========================================================================
%%% Supervisor callback
@@ -78,3 +92,52 @@ proxy_server_child_spec() ->
Modules = [ssl_tls_dist_proxy],
Type = worker,
{Name, StartFunc, Restart, Shutdown, Type, Modules}.
+
+consult(File) ->
+ case erl_prim_loader:get_file(File) of
+ {ok, Binary, _FullName} ->
+ Encoding =
+ case epp:read_encoding_from_binary(Binary) of
+ none -> latin1;
+ Enc -> Enc
+ end,
+ case unicode:characters_to_list(Binary, Encoding) of
+ {error, _String, Rest} ->
+ error(
+ {bad_ssl_dist_optfile, {encoding_error, Rest}});
+ {incomplete, _String, Rest} ->
+ error(
+ {bad_ssl_dist_optfile, {encoding_incomplete, Rest}});
+ String when is_list(String) ->
+ consult_string(String)
+ end;
+ error ->
+ error({bad_ssl_dist_optfile, File})
+ end.
+
+consult_string(String) ->
+ case erl_scan:string(String) of
+ {error, Info, Location} ->
+ error({bad_ssl_dist_optfile, {scan_error, Info, Location}});
+ {ok, Tokens, _EndLocation} ->
+ consult_tokens(Tokens)
+ end.
+
+consult_tokens(Tokens) ->
+ case erl_parse:parse_exprs(Tokens) of
+ {error, Info} ->
+ error({bad_ssl_dist_optfile, {parse_error, Info}});
+ {ok, [Expr]} ->
+ consult_expr(Expr);
+ {ok, Other} ->
+ error({bad_ssl_dist_optfile, {parse_error, Other}})
+ end.
+
+consult_expr(Expr) ->
+ {value, Value, Bs} = erl_eval:expr(Expr, erl_eval:new_bindings()),
+ case erl_eval:bindings(Bs) of
+ [] ->
+ Value;
+ Other ->
+ error({bad_ssl_dist_optfile, {bindings, Other}})
+ end.
diff --git a/lib/ssl/src/ssl_tls_dist_proxy.erl b/lib/ssl/src/ssl_tls_dist_proxy.erl
index 08947f24dd..12a057fd22 100644
--- a/lib/ssl/src/ssl_tls_dist_proxy.erl
+++ b/lib/ssl/src/ssl_tls_dist_proxy.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2011-2016. All Rights Reserved.
+%% Copyright Ericsson AB 2011-2017. All Rights Reserved.
%%
%% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License.
@@ -20,7 +20,7 @@
-module(ssl_tls_dist_proxy).
--export([listen/2, accept/2, connect/3, get_tcp_address/1]).
+-export([listen/2, accept/2, connect/4, get_tcp_address/1]).
-export([init/1, start_link/0, handle_call/3, handle_cast/2, handle_info/2,
terminate/2, code_change/3, ssl_options/2]).
@@ -45,8 +45,9 @@ listen(Driver, Name) ->
accept(Driver, Listen) ->
gen_server:call(?MODULE, {accept, Driver, Listen}, infinity).
-connect(Driver, Ip, Port) ->
- gen_server:call(?MODULE, {connect, Driver, Ip, Port}, infinity).
+connect(Driver, Ip, Port, ExtraOpts) ->
+ gen_server:call(
+ ?MODULE, {connect, Driver, Ip, Port, ExtraOpts}, infinity).
do_listen(Options) ->
@@ -134,9 +135,11 @@ handle_call({accept, _Driver, Listen}, {From, _}, State = #state{listen={_, Worl
WorldPid = spawn_link(fun() -> accept_loop(Self, world, World, Listen) end),
{reply, ErtsPid, State#state{accept_loop={ErtsPid, WorldPid}}};
-handle_call({connect, Driver, Ip, Port}, {From, _}, State) ->
+handle_call({connect, Driver, Ip, Port, ExtraOpts}, {From, _}, State) ->
Me = self(),
- Pid = spawn_link(fun() -> setup_proxy(Driver, Ip, Port, Me) end),
+ Pid =
+ spawn_link(
+ fun() -> setup_proxy(Driver, Ip, Port, ExtraOpts, Me) end),
receive
{Pid, go_ahead, LPort} ->
Res = {ok, Socket} = try_connect(LPort),
@@ -270,9 +273,9 @@ try_connect(Port) ->
try_connect(Port)
end.
-setup_proxy(Driver, Ip, Port, Parent) ->
+setup_proxy(Driver, Ip, Port, ExtraOpts, Parent) ->
process_flag(trap_exit, true),
- Opts = connect_options(get_ssl_options(client)),
+ Opts = connect_options(ExtraOpts ++ get_ssl_options(client)),
case ssl:connect(Ip, Port, [{active, true}, binary, {packet,?PPRE}, nodelay(),
Driver:family()] ++ Opts) of
{ok, World} ->
@@ -369,6 +372,17 @@ loop_conn(World, Erts) ->
end.
get_ssl_options(Type) ->
+ try ets:lookup(ssl_dist_opts, Type) of
+ [{Type, Opts}] ->
+ [{erl_dist, true} | Opts];
+ _ ->
+ get_ssl_dist_arguments(Type)
+ catch
+ error:badarg ->
+ get_ssl_dist_arguments(Type)
+ end.
+
+get_ssl_dist_arguments(Type) ->
case init:get_argument(ssl_dist_opt) of
{ok, Args} ->
[{erl_dist, true} | ssl_options(Type, lists:append(Args))];