From 457409e8a460fa115565a5450e7f4eb4a558f32c Mon Sep 17 00:00:00 2001
From: Raimo Niskanen <raimo@erlang.org>
Date: Wed, 29 Nov 2017 09:58:22 +0100
Subject: Read in -ssl_dist_optfile to ETS

---
 lib/ssl/doc/src/ssl_distribution.xml | 128 +++++++++++++++++++++++++++++------
 lib/ssl/src/ssl_dist_sup.erl         |  67 +++++++++++++++++-
 2 files changed, 171 insertions(+), 24 deletions(-)

(limited to 'lib/ssl')

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/ssl_test@myhost.conf"</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/ssl_test@myhost.conf"
+  -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/ssl_test@myhost.conf"
+  -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/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.
-- 
cgit v1.2.3