diff options
Diffstat (limited to 'lib/ssl')
| -rw-r--r-- | lib/ssl/src/Makefile | 5 | ||||
| -rw-r--r-- | lib/ssl/src/dtls_connection.erl | 2 | ||||
| -rw-r--r-- | lib/ssl/src/dtls_udp_listener.erl | 22 | ||||
| -rw-r--r-- | lib/ssl/src/inet6_tls_dist.erl | 7 | ||||
| -rw-r--r-- | lib/ssl/src/inet_tls_dist.erl | 664 | ||||
| -rw-r--r-- | lib/ssl/src/ssl.app.src | 7 | ||||
| -rw-r--r-- | lib/ssl/src/ssl_cipher.erl | 91 | ||||
| -rw-r--r-- | lib/ssl/src/ssl_cipher.hrl | 51 | ||||
| -rw-r--r-- | lib/ssl/src/ssl_connection.erl | 321 | ||||
| -rw-r--r-- | lib/ssl/src/ssl_dist_sup.erl | 14 | ||||
| -rw-r--r-- | lib/ssl/src/ssl_handshake.erl | 85 | ||||
| -rw-r--r-- | lib/ssl/src/ssl_handshake.hrl | 11 | ||||
| -rw-r--r-- | lib/ssl/src/ssl_tls_dist_proxy.erl | 479 | ||||
| -rw-r--r-- | lib/ssl/src/tls_connection.erl | 11 | ||||
| -rw-r--r-- | lib/ssl/test/ssl_test_lib.erl | 4 | 
15 files changed, 1062 insertions, 712 deletions
| diff --git a/lib/ssl/src/Makefile b/lib/ssl/src/Makefile index 2e7df9792e..8eba5cf347 100644 --- a/lib/ssl/src/Makefile +++ b/lib/ssl/src/Makefile @@ -1,7 +1,7 @@  #  # %CopyrightBegin%  # -# Copyright Ericsson AB 1999-2016. All Rights Reserved. +# Copyright Ericsson AB 1999-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. @@ -87,8 +87,7 @@ MODULES= \  	ssl_v2 \  	ssl_v3 \  	tls_v1 \ -	dtls_v1 \ -	ssl_tls_dist_proxy +	dtls_v1  INTERNAL_HRL_FILES = \  	ssl_alert.hrl ssl_cipher.hrl \ diff --git a/lib/ssl/src/dtls_connection.erl b/lib/ssl/src/dtls_connection.erl index a352b7e025..ae04167ec0 100644 --- a/lib/ssl/src/dtls_connection.erl +++ b/lib/ssl/src/dtls_connection.erl @@ -551,7 +551,7 @@ handle_info(new_cookie_secret, StateName,                                              CookieInfo#{current_cookie_secret => dtls_v1:cookie_secret(),                                                          previous_cookie_secret => Secret}}};  handle_info(Msg, StateName, State) -> -    ssl_connection:handle_info(Msg, StateName, State). +    ssl_connection:StateName(info, Msg, State, ?MODULE).  handle_call(Event, From, StateName, State) ->      ssl_connection:handle_call(Event, From, StateName, State, ?MODULE). diff --git a/lib/ssl/src/dtls_udp_listener.erl b/lib/ssl/src/dtls_udp_listener.erl index c789a32087..c9e04767aa 100644 --- a/lib/ssl/src/dtls_udp_listener.erl +++ b/lib/ssl/src/dtls_udp_listener.erl @@ -35,7 +35,7 @@  -record(state,   	{port,  -	 listner, +	 listener,  	 dtls_options,  	 emulated_options,  	 dtls_msq_queues = kv_new(), @@ -81,7 +81,7 @@ init([Port, EmOpts, InetOptions, DTLSOptions]) ->  		    first = true,  		    dtls_options = DTLSOptions,  		    emulated_options = EmOpts, -		    listner = Socket, +		    listener = Socket,                      close = false}}      catch _:_ ->  	    {error, closed} @@ -91,7 +91,7 @@ handle_call({accept, _}, _, #state{close = true} = State) ->  handle_call({accept, Accepter}, From, #state{first = true,  					     accepters = Accepters, -					     listner = Socket} = State0) -> +					     listener = Socket} = State0) ->      next_datagram(Socket),      State = State0#state{first = false,  			 accepters = queue:in({Accepter, From}, Accepters)}, 		  @@ -100,7 +100,7 @@ handle_call({accept, Accepter}, From, #state{first = true,  handle_call({accept, Accepter}, From, #state{accepters = Accepters} = State0) ->      State = State0#state{accepters = queue:in({Accepter, From}, Accepters)}, 		       {noreply, State}; -handle_call(sockname, _, #state{listner = Socket} = State) -> +handle_call(sockname, _, #state{listener = Socket} = State) ->      Reply = inet:sockname(Socket),      {reply, Reply, State};  handle_call(close, _, #state{dtls_processes = Processes, @@ -114,7 +114,7 @@ handle_call(close, _, #state{dtls_processes = Processes,                            end, queue:to_list(Accepters)),              {reply, ok,  State#state{close = true, accepters = queue:new()}}      end; -handle_call({get_sock_opts, {SocketOptNames, EmOptNames}}, _, #state{listner = Socket, +handle_call({get_sock_opts, {SocketOptNames, EmOptNames}}, _, #state{listener = Socket,                                                                 emulated_options = EmOpts} = State) ->      case get_socket_opts(Socket, SocketOptNames) of          {ok, Opts} -> @@ -125,7 +125,7 @@ handle_call({get_sock_opts, {SocketOptNames, EmOptNames}}, _, #state{listner = S  handle_call(get_all_opts, _, #state{dtls_options = DTLSOptions,                                      emulated_options = EmOpts} = State) ->      {reply, {ok, EmOpts, DTLSOptions}, State}; -handle_call({set_sock_opts, {SocketOpts, NewEmOpts}}, _, #state{listner = Socket, emulated_options = EmOpts0} = State) -> +handle_call({set_sock_opts, {SocketOpts, NewEmOpts}}, _, #state{listener = Socket, emulated_options = EmOpts0} = State) ->      set_socket_opts(Socket, SocketOpts),      EmOpts = do_set_emulated_opts(NewEmOpts, EmOpts0),      {reply, ok, State#state{emulated_options = EmOpts}}. @@ -134,7 +134,7 @@ handle_cast({active_once, Client, Pid}, State0) ->      State = handle_active_once(Client, Pid, State0),      {noreply, State}. -handle_info({udp, Socket, IP, InPortNo, _} = Msg, #state{listner = Socket} = State0) -> +handle_info({udp, Socket, IP, InPortNo, _} = Msg, #state{listener = Socket} = State0) ->      State = handle_datagram({IP, InPortNo}, Msg, State0),      next_datagram(Socket),      {noreply, State}; @@ -142,11 +142,11 @@ handle_info({udp, Socket, IP, InPortNo, _} = Msg, #state{listner = Socket} = Sta  %% UDP socket does not have a connection and should not receive an econnreset  %% This does however happens on on some windows versions. Just ignoring it  %% appears to make things work as expected!  -handle_info({udp_error, Socket, econnreset = Error}, #state{listner = Socket} = State) -> +handle_info({udp_error, Socket, econnreset = Error}, #state{listener = Socket} = State) ->      Report = io_lib:format("Ignore SSL UDP Listener: Socket error: ~p ~n", [Error]),      error_logger:info_report(Report),      {noreply, State}; -handle_info({udp_error, Socket, Error}, #state{listner = Socket} = State) -> +handle_info({udp_error, Socket, Error}, #state{listener = Socket} = State) ->      Report = io_lib:format("SSL UDP Listener shutdown: Socket error: ~p ~n", [Error]),      error_logger:info_report(Report),      {noreply, State#state{close=true}}; @@ -225,10 +225,10 @@ setup_new_connection(User, From, Client, Msg, #state{dtls_processes = Processes,  						     dtls_msq_queues = MsgQueues,  						     dtls_options = DTLSOpts,  						     port = Port, -						     listner = Socket, +						     listener = Socket,  						     emulated_options = EmOpts} = State) ->      ConnArgs = [server, "localhost", Port, {self(), {Client, Socket}}, -		{DTLSOpts, EmOpts, udp_listner}, User, dtls_socket:default_cb_info()], +		{DTLSOpts, EmOpts, udp_listener}, User, dtls_socket:default_cb_info()],      case dtls_connection_sup:start_child(ConnArgs) of  	{ok, Pid} ->  	    erlang:monitor(process, Pid), diff --git a/lib/ssl/src/inet6_tls_dist.erl b/lib/ssl/src/inet6_tls_dist.erl index ffd7296f93..96ce4d493a 100644 --- a/lib/ssl/src/inet6_tls_dist.erl +++ b/lib/ssl/src/inet6_tls_dist.erl @@ -1,7 +1,7 @@  %%  %% %CopyrightBegin%  %% -%% Copyright Ericsson AB 2015. All Rights Reserved. +%% Copyright Ericsson AB 2015-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. @@ -21,7 +21,8 @@  %%  -module(inet6_tls_dist). --export([childspecs/0, listen/1, accept/1, accept_connection/5, +-export([childspecs/0]). +-export([listen/1, accept/1, accept_connection/5,  	 setup/5, close/1, select/1]).  childspecs() -> @@ -43,4 +44,4 @@ setup(Node, Type, MyNode, LongOrShortNames,SetupTime) ->      inet_tls_dist:gen_setup(inet6_tcp, Node, Type, MyNode, LongOrShortNames,SetupTime).  close(Socket) -> -    inet_tls_dist:close(Socket). +    inet_tls_dist:gen_close(inet6_tcp, Socket). diff --git a/lib/ssl/src/inet_tls_dist.erl b/lib/ssl/src/inet_tls_dist.erl index 0da4b3587f..d644cbe66a 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. @@ -21,17 +21,26 @@  %%  -module(inet_tls_dist). --export([childspecs/0, listen/1, accept/1, accept_connection/5, +-export([childspecs/0]). +-export([listen/1, accept/1, accept_connection/5,  	 setup/5, close/1, select/1, is_node_name/1]).  %% Generalized dist API  -export([gen_listen/2, gen_accept/2, gen_accept_connection/6, -	 gen_setup/6, gen_select/2]). +	 gen_setup/6, gen_close/2, gen_select/2]). + +-export([split_node/1, nodelay/0]). + +-export([dbg/0]). % Debug  -include_lib("kernel/include/net_address.hrl").  -include_lib("kernel/include/dist.hrl").  -include_lib("kernel/include/dist_util.hrl"). +-include("ssl_api.hrl"). + +%% ------------------------------------------------------------------------- +  childspecs() ->      {ok, [{ssl_dist_sup,{ssl_dist_sup, start_link, []},  	   permanent, infinity, supervisor, [ssl_dist_sup]}]}. @@ -40,111 +49,366 @@ select(Node) ->      gen_select(inet_tcp, Node).  gen_select(Driver, Node) -> -    case split_node(atom_to_list(Node), $@, []) of -	[_, Host] -> -	    case inet:getaddr(Host, Driver:family()) of +    case split_node(Node) of +        false -> +            false; +        Host -> +	    case Driver:getaddr(Host) of  		{ok, _} -> true;  		_ -> false -	    end; -	_ ->  -	    false +	    end      end. -is_node_name(Node) when is_atom(Node) -> -    select(Node); -is_node_name(_) -> -    false. +%% ------------------------------------------------------------------------- + +is_node_name(Node) -> +    case split_node(Node) of +        false -> +            false; +        _Host -> +            true +    end. + +%% ------------------------------------------------------------------------- + +hs_data_common(#sslsocket{pid = DistCtrl} = SslSocket) -> +    #hs_data{ +       f_send = +           fun (Ctrl, Packet) when Ctrl == DistCtrl -> +                   f_send(SslSocket, Packet) +           end, +       f_recv = +           fun (Ctrl, Length, Timeout) when Ctrl == DistCtrl -> +                   f_recv(SslSocket, Length, Timeout) +           end, +       f_setopts_pre_nodeup = +           fun (Ctrl) when Ctrl == DistCtrl -> +                   f_setopts_pre_nodeup(SslSocket) +           end, +       f_setopts_post_nodeup = +           fun (Ctrl) when Ctrl == DistCtrl -> +%%%                   sys:trace(Ctrl, true), +                   f_setopts_post_nodeup(SslSocket) +           end, +       f_getll = +           fun (Ctrl) when Ctrl == DistCtrl -> +                   f_getll(DistCtrl) +           end, +       f_address = +           fun (Ctrl, Node) when Ctrl == DistCtrl -> +                   f_address(SslSocket, Node) +           end, +       mf_tick = +           fun (Ctrl) when Ctrl == DistCtrl -> +                   mf_tick(DistCtrl) +           end, +       mf_getstat = +           fun (Ctrl) when Ctrl == DistCtrl -> +                   mf_getstat(SslSocket) +           end, +       mf_setopts = +           fun (Ctrl, Opts) when Ctrl == DistCtrl -> +                   mf_setopts(SslSocket, Opts) +           end, +       mf_getopts = +           fun (Ctrl, Opts) when Ctrl == DistCtrl -> +                   mf_getopts(SslSocket, Opts) +           end, +       f_handshake_complete = +           fun (Ctrl, Node, DHandle) when Ctrl == DistCtrl -> +                   f_handshake_complete(DistCtrl, Node, DHandle) +           end}. + +f_send(SslSocket, Packet) -> +    ssl:send(SslSocket, Packet). + +f_recv(SslSocket, Length, Timeout) -> +    case ssl:recv(SslSocket, Length, Timeout) of +        {ok, Bin} when is_binary(Bin) -> +            {ok, binary_to_list(Bin)}; +        Other -> +            Other +    end. + +f_setopts_pre_nodeup(_SslSocket) -> +    ok. + +f_setopts_post_nodeup(_SslSocket) -> +    ok. + +f_getll(DistCtrl) -> +    {ok, DistCtrl}. + +f_address(SslSocket, Node) -> +    case ssl:peername(SslSocket) of +        {ok, Address} -> +            case split_node(Node) of +                false -> +                    {error, no_node}; +                Host -> +                    #net_address{ +                       address=Address, host=Host, +                       protocol=tls, family=inet} +            end +    end. + +mf_tick(DistCtrl) -> +    DistCtrl ! tick, +    ok. + +mf_getstat(SslSocket) -> +    case ssl:getstat( +           SslSocket, [recv_cnt, send_cnt, send_pend]) of +        {ok, Stat} -> +            split_stat(Stat,0,0,0); +        Error -> +            Error +    end. + +mf_setopts(SslSocket, Opts) -> +    case setopts_filter(Opts) of +        [] -> +            ssl:setopts(SslSocket, Opts); +        Opts1 -> +            {error, {badopts,Opts1}} +    end. + +mf_getopts(SslSocket, Opts) -> +    ssl:getopts(SslSocket, Opts). + +f_handshake_complete(DistCtrl, Node, DHandle) -> +    ssl_connection:handshake_complete(DistCtrl, Node, DHandle). + + +setopts_filter(Opts) -> +    [Opt || {K,_} = Opt <- Opts, +            K =:= active orelse K =:= deliver orelse K =:= packet]. + +split_stat([{recv_cnt, R}|Stat], _, W, P) -> +    split_stat(Stat, R, W, P); +split_stat([{send_cnt, W}|Stat], R, _, P) -> +    split_stat(Stat, R, W, P); +split_stat([{send_pend, P}|Stat], R, W, _) -> +    split_stat(Stat, R, W, P); +split_stat([], R, W, P) -> +    {ok, R, W, P}. + +%% -------------------------------------------------------------------------  listen(Name) ->      gen_listen(inet_tcp, Name).  gen_listen(Driver, Name) -> -    ssl_tls_dist_proxy:listen(Driver, Name). +    case inet_tcp_dist:gen_listen(Driver, Name) of +        {ok, {Socket, Address, Creation}} -> +            inet:setopts(Socket, [{packet, 4}]), +            {ok, {Socket, Address#net_address{protocol=tls}, Creation}}; +        Other -> +            Other +    end. + +%% -------------------------------------------------------------------------  accept(Listen) ->      gen_accept(inet_tcp, Listen).  gen_accept(Driver, Listen) -> -    ssl_tls_dist_proxy:accept(Driver, Listen). +    Kernel = self(), +    monitor_pid( +      spawn_opt( +        fun () -> +                accept_loop(Driver, Listen, Kernel) +        end, +        [link, {priority, max}])). -accept_connection(AcceptPid, Socket, MyNode, Allowed, SetupTime) -> -    gen_accept_connection(inet_tcp, AcceptPid, Socket, MyNode, Allowed, SetupTime). +accept_loop(Driver, Listen, Kernel) -> +    case Driver:accept(Listen) of +        {ok, Socket} -> +            Opts = get_ssl_options(server), +            wait_for_code_server(), +            case ssl:ssl_accept( +                   Socket, [{active, false}, {packet, 4}] ++ Opts, +                   net_kernel:connecttime()) of +                {ok, #sslsocket{pid = DistCtrl} = SslSocket} -> +                    monitor_pid(DistCtrl), +                    trace( +                      Kernel ! +                          {accept, self(), DistCtrl, +                           Driver:family(), tls}), +                    receive +                        {Kernel, controller, Pid} -> +                            ok = ssl:controlling_process(SslSocket, Pid), +                            trace( +                              Pid ! {self(), controller}); +                        {Kernel, unsupported_protocol} -> +                            exit(trace(unsupported_protocol)) +                    end, +                    accept_loop(Driver, Listen, Kernel); +		{error, {options, _}} = Error -> +		    %% Bad options: that's probably our fault. +                    %% Let's log that. +		    error_logger:error_msg( +                      "Cannot accept TLS distribution connection: ~s~n", +                      [ssl:format_error(Error)]), +                    _ = trace(Error), +                    gen_tcp:close(Socket); +		Other -> +                    _ = trace(Other), +                    gen_tcp:close(Socket) +	    end; +	Error -> +	    exit(trace(Error)) +    end, +    accept_loop(Driver, Listen, Kernel). -gen_accept_connection(Driver, AcceptPid, Socket, MyNode, Allowed, SetupTime) -> +wait_for_code_server() -> +    %% This is an ugly hack.  Upgrading a socket to TLS requires the +    %% crypto module to be loaded.  Loading the crypto module triggers +    %% its on_load function, which calls code:priv_dir/1 to find the +    %% directory where its NIF library is.  However, distribution is +    %% started earlier than the code server, so the code server is not +    %% necessarily started yet, and code:priv_dir/1 might fail because +    %% of that, if we receive an incoming connection on the +    %% distribution port early enough. +    %% +    %% If the on_load function of a module fails, the module is +    %% unloaded, and the function call that triggered loading it fails +    %% with 'undef', which is rather confusing. +    %% +    %% Thus, the accept process will terminate, and be +    %% restarted by ssl_dist_sup.  However, it won't have any memory +    %% of being asked by net_kernel to listen for incoming +    %% connections.  Hence, the node will believe that it's open for +    %% distribution, but it actually isn't. +    %% +    %% So let's avoid that by waiting for the code server to start. +    case whereis(code_server) of +	undefined -> +	    timer:sleep(10), +	    wait_for_code_server(); +	Pid when is_pid(Pid) -> +	    ok +    end. + +%% ------------------------------------------------------------------------- + +accept_connection(AcceptPid, DistCtrl, MyNode, Allowed, SetupTime) -> +    gen_accept_connection( +      inet_tcp, AcceptPid, DistCtrl, MyNode, Allowed, SetupTime). + +gen_accept_connection( +  Driver, AcceptPid, DistCtrl, MyNode, Allowed, SetupTime) ->      Kernel = self(), -    spawn_link(fun() -> do_accept(Driver, Kernel, AcceptPid, Socket, -				  MyNode, Allowed, SetupTime) end). +    monitor_pid( +      spawn_opt( +        fun() -> +                do_accept( +                  Driver, Kernel, AcceptPid, DistCtrl, +                  MyNode, Allowed, SetupTime) +        end, +        [link, {priority, max}])). + +do_accept(Driver, Kernel, AcceptPid, DistCtrl, MyNode, Allowed, SetupTime) -> +    SslSocket = ssl_connection:get_sslsocket(DistCtrl), +    receive +	{AcceptPid, controller} -> +	    Timer = dist_util:start_timer(SetupTime), +	    case check_ip(Driver, SslSocket) of +		true -> +                    HSData0 = hs_data_common(SslSocket), +                    HSData = +                        HSData0#hs_data{ +                          kernel_pid = Kernel, +                          this_node = MyNode, +                          socket = DistCtrl, +                          timer = Timer, +                          this_flags = 0, +                          allowed = Allowed}, +		    dist_util:handshake_other_started(trace(HSData)); +		{false,IP} -> +		    error_logger:error_msg( +                      "** Connection attempt from " +                      "disallowed IP ~w ** ~n", [IP]), +		    ?shutdown(trace(no_node)) +	    end +    end. + + -setup(Node, Type, MyNode, LongOrShortNames,SetupTime) -> -    gen_setup(inet_tcp, Node, Type, MyNode, LongOrShortNames,SetupTime). +setup(Node, Type, MyNode, LongOrShortNames, SetupTime) -> +    gen_setup(inet_tcp, Node, Type, MyNode, LongOrShortNames, SetupTime). -gen_setup(Driver, Node, Type, MyNode, LongOrShortNames,SetupTime) -> +gen_setup(Driver, Node, Type, MyNode, LongOrShortNames, SetupTime) ->      Kernel = self(), -    spawn_opt(fun() -> do_setup(Driver, Kernel, Node, Type, MyNode, LongOrShortNames, SetupTime) end, [link, {priority, max}]). -		    +    monitor_pid( +      spawn_opt( +        fun() -> +                do_setup( +                  Driver, Kernel, Node, Type, +                  MyNode, LongOrShortNames, SetupTime) +        end, +        [link, {priority, max}])). +  do_setup(Driver, Kernel, Node, Type, MyNode, LongOrShortNames, SetupTime) ->      [Name, Address] = splitnode(Driver, Node, LongOrShortNames), -    case inet:getaddr(Address, Driver:family()) of +    case Driver:getaddr(Address) of  	{ok, Ip} -> -	    Timer = dist_util:start_timer(SetupTime), +            Timer = trace(dist_util:start_timer(SetupTime)),  	    ErlEpmd = net_kernel:epmd_module(),  	    case ErlEpmd:port_please(Name, Ip) of  		{port, TcpPort, Version} -> -		    ?trace("port_please(~p) -> version ~p~n",  -			   [Node,Version]), +                    Opts = trace(connect_options(get_ssl_options(client))),  		    dist_util:reset_timer(Timer), -		    case ssl_tls_dist_proxy:connect(Driver, Ip, TcpPort) of -			{ok, Socket} -> -			    HSData = connect_hs_data(Kernel, Node, MyNode, Socket,  -						     Timer, Version, Ip, TcpPort, Address, -						     Type), -			    dist_util:handshake_we_started(HSData); +                    case ssl:connect( +                           Ip, TcpPort, +                           [binary, {active, false}, {packet, 4}, +                            Driver:family(), nodelay()] ++ Opts, +                           net_kernel:connecttime()) of +			{ok, #sslsocket{pid = DistCtrl} = SslSocket} -> +                            monitor_pid(DistCtrl), +                            ok = ssl:controlling_process(SslSocket, self()), +                            HSData0 = hs_data_common(SslSocket), +			    HSData = +                                HSData0#hs_data{ +                                  kernel_pid = Kernel, +                                  other_node = Node, +                                  this_node = MyNode, +                                  socket = DistCtrl, +                                  timer = Timer, +                                  this_flags = 0, +                                  other_version = Version, +                                  request_type = Type}, +			    dist_util:handshake_we_started(trace(HSData));  			Other ->  			    %% Other Node may have closed since   			    %% port_please ! -			    ?trace("other node (~p) " -				   "closed since port_please.~n",  -				   [Node]), -			    ?shutdown2(Node, {shutdown, {connect_failed, Other}}) +			    ?shutdown2( +                               Node, +                               trace({shutdown, {connect_failed, Other}}))  		    end;  		Other -> -		    ?trace("port_please (~p) " -			   "failed.~n", [Node]), -		    ?shutdown2(Node, {shutdown, {port_please_failed, Other}}) +		    ?shutdown2( +                       Node, +                       trace({shutdown, {port_please_failed, Other}}))  	    end;  	Other -> -	    ?trace("inet_getaddr(~p) " -		   "failed (~p).~n", [Node,Other]), -	    ?shutdown2(Node, {shutdown, {inet_getaddr_failed, Other}}) +	    ?shutdown2(Node, trace({shutdown, {getaddr_failed, Other}}))      end.  close(Socket) -> -    gen_tcp:close(Socket), -    ok. +    gen_close(inet, Socket). + +gen_close(Driver, Socket) -> +    trace(Driver:close(Socket)). -do_accept(Driver, Kernel, AcceptPid, Socket, MyNode, Allowed, SetupTime) -> -    process_flag(priority, max), -    receive -	{AcceptPid, controller} -> -	    Timer = dist_util:start_timer(SetupTime), -	    case check_ip(Driver, Socket) of -		true -> -		    HSData = accept_hs_data(Kernel, MyNode, Socket, Timer, Allowed), -		    dist_util:handshake_other_started(HSData); -		{false,IP} -> -		    error_logger:error_msg("** Connection attempt from " -					   "disallowed IP ~w ** ~n", [IP]), -		    ?shutdown(no_node) -	    end -    end.  %% ------------------------------------------------------------  %% Do only accept new connection attempts from nodes at our  %% own LAN, if the check_ip environment parameter is true.  %% ------------------------------------------------------------ -check_ip(Driver, Socket) -> +check_ip(Driver, SslSocket) ->      case application:get_env(check_ip) of  	{ok, true} -> -	    case get_ifs(Socket) of +	    case get_ifs(SslSocket) of  		{ok, IFs, IP} ->  		    check_ip(Driver, IFs, IP);  		_ -> @@ -154,9 +418,18 @@ check_ip(Driver, Socket) ->  	    true      end. -get_ifs(Socket) -> +check_ip(Driver, [{OwnIP, _, Netmask}|IFs], PeerIP) -> +    case {Driver:mask(Netmask, PeerIP), Driver:mask(Netmask, OwnIP)} of +	{M, M} -> true; +	_      -> check_ip(IFs, PeerIP) +    end; +check_ip(_Driver, [], PeerIP) -> +    {false, PeerIP}. + +get_ifs(#sslsocket{fd = {gen_tcp, Socket, _}}) ->      case inet:peername(Socket) of  	{ok, {IP, _}} -> +            %% XXX this is seriously broken for IPv6  	    case inet:getif(Socket) of  		{ok, IFs} -> {ok, IFs, IP};  		Error     -> Error @@ -165,14 +438,6 @@ get_ifs(Socket) ->  	    Error      end. -check_ip(Driver, [{OwnIP, _, Netmask}|IFs], PeerIP) -> -    case {Driver:mask(Netmask, PeerIP), Driver:mask(Netmask, OwnIP)} of -	{M, M} -> true; -	_      -> check_ip(IFs, PeerIP) -    end; -check_ip(_Driver, [], PeerIP) -> -    {false, PeerIP}. -  %% If Node is illegal terminate the connection setup!!  splitnode(Driver, Node, LongOrShortNames) -> @@ -181,11 +446,13 @@ splitnode(Driver, Node, LongOrShortNames) ->  	    Host = lists:append(Tail),  	    check_node(Driver, Name, Node, Host, LongOrShortNames);  	[_] -> -	    error_logger:error_msg("** Nodename ~p illegal, no '@' character **~n", -		      [Node]), +	    error_logger:error_msg( +              "** Nodename ~p illegal, no '@' character **~n", +              [Node]),  	    ?shutdown(Node);  	_ -> -	    error_logger:error_msg("** Nodename ~p illegal **~n", [Node]), +	    error_logger:error_msg( +              "** Nodename ~p illegal **~n", [Node]),  	    ?shutdown(Node)      end. @@ -196,23 +463,34 @@ check_node(Driver, Name, Node, Host, LongOrShortNames) ->  		{ok, _} ->  		    [Name, Host];  		_ -> -		    error_logger:error_msg("** System running to use " -					   "fully qualified " -					   "hostnames **~n" -					   "** Hostname ~s is illegal **~n", -					   [Host]), +		    error_logger:error_msg( +                      "** System running to use " +                      "fully qualified hostnames **~n" +                      "** Hostname ~s is illegal **~n", +                      [Host]),  		    ?shutdown(Node)  	    end;  	[_, _ | _] when LongOrShortNames == shortnames -> -	    error_logger:error_msg("** System NOT running to use fully qualified " -			      "hostnames **~n" -		      "** Hostname ~s is illegal **~n", -		      [Host]), +	    error_logger:error_msg( +              "** System NOT running to use " +              "fully qualified hostnames **~n" +              "** Hostname ~s is illegal **~n", +              [Host]),  	    ?shutdown(Node);  	_ ->  	    [Name, Host]      end. +split_node(Node) when is_atom(Node) -> +    case split_node(atom_to_list(Node), $@, []) of +        [_, Host] -> +            Host; +        _ -> +            false +    end; +split_node(_) -> +    false. +%%  split_node([Chr|T], Chr, Ack) ->       [lists:reverse(Ack)|split_node(T, Chr, [])];  split_node([H|T], Chr, Ack) ->  @@ -220,70 +498,154 @@ split_node([H|T], Chr, Ack) ->  split_node([], _, Ack) ->       [lists:reverse(Ack)]. -connect_hs_data(Kernel, Node, MyNode, Socket, Timer, Version, Ip, TcpPort, Address, Type) -> -    common_hs_data(Kernel, MyNode, Socket, Timer,  -		   #hs_data{other_node = Node, -			    other_version = Version, -			    f_address =  -				fun(_,_) -> -					#net_address{address = {Ip,TcpPort}, -						     host = Address, -						     protocol = proxy, -						     family = inet} -				end, -			    request_type = Type -			   }). - -accept_hs_data(Kernel, MyNode, Socket, Timer, Allowed) -> -    common_hs_data(Kernel, MyNode, Socket, Timer, #hs_data{ -					     allowed = Allowed, -					     f_address = fun get_remote_id/2 -					    }). - -common_hs_data(Kernel, MyNode, Socket, Timer, HsData) -> -    HsData#hs_data{ -      kernel_pid = Kernel, -      this_node = MyNode, -      socket = Socket, -      timer = Timer, -      this_flags = 0, -      f_send =  -	  fun(S,D) ->  -		  gen_tcp:send(S,D)  -	  end, -      f_recv =  -	  fun(S,N,T) ->  -		  gen_tcp:recv(S,N,T)  -	  end, -      f_setopts_pre_nodeup =  -	  fun(S) -> -		  inet:setopts(S, [{active, false}, {packet, 4}]) -	  end, -		   f_setopts_post_nodeup =  -	  fun(S) ->  -		  inet:setopts(S, [{deliver, port},{active, true}]) -	  end, -      f_getll =  -	  fun(S) ->  -		  inet:getll(S)  -	  end, -      mf_tick =  -	  fun(S) ->  -		  gen_tcp:send(S, <<>>) -	  end, -      mf_getstat =  -	  fun(S) -> -		  {ok, Stats} = inet:getstat(S, [recv_cnt, send_cnt, send_pend]), -		  R = proplists:get_value(recv_cnt, Stats, 0), -		  W = proplists:get_value(send_cnt, Stats, 0), -		  P = proplists:get_value(send_pend, Stats, 0), -		  {ok, R,W,P} -	  end}. - -get_remote_id(Socket, _Node) -> -    case ssl_tls_dist_proxy:get_tcp_address(Socket) of -        {ok, Address} -> -	    Address; -	{error, _Reason} -> -	    ?shutdown(no_node) +%% ------------------------------------------------------------------------- + +connect_options(Opts) -> +    case application:get_env(kernel, inet_dist_connect_options) of +	{ok,ConnectOpts} -> +	    lists:ukeysort(1, ConnectOpts ++ Opts); +	_ -> +	    Opts      end. + +%% we may not always want the nodelay behaviour +%% for performance reasons +nodelay() -> +    case application:get_env(kernel, dist_nodelay) of +	undefined -> +	    {nodelay, true}; +	{ok, true} -> +	    {nodelay, true}; +	{ok, false} -> +	    {nodelay, false}; +	_ -> +	    {nodelay, true} +    end. + + +get_ssl_options(Type) -> +    case init:get_argument(ssl_dist_opt) of +	{ok, Args} -> +	    [{erl_dist, true} | ssl_options(Type, lists:append(Args))]; +	_ -> +	    [{erl_dist, true}] +    end. + +ssl_options(_,[]) -> +    []; +ssl_options(server, ["client_" ++ _, _Value |T]) -> +    ssl_options(server,T); +ssl_options(client, ["server_" ++ _, _Value|T]) -> +    ssl_options(client,T); +ssl_options(server, ["server_certfile", Value|T]) -> +    [{certfile, Value} | ssl_options(server,T)]; +ssl_options(client, ["client_certfile", Value | T]) -> +    [{certfile, Value} | ssl_options(client,T)]; +ssl_options(server, ["server_cacertfile", Value|T]) -> +    [{cacertfile, Value} | ssl_options(server,T)]; +ssl_options(client, ["client_cacertfile", Value|T]) -> +    [{cacertfile, Value} | ssl_options(client,T)]; +ssl_options(server, ["server_keyfile", Value|T]) -> +    [{keyfile, Value} | ssl_options(server,T)]; +ssl_options(client, ["client_keyfile", Value|T]) -> +    [{keyfile, Value} | ssl_options(client,T)]; +ssl_options(server, ["server_password", Value|T]) -> +    [{password, Value} | ssl_options(server,T)]; +ssl_options(client, ["client_password", Value|T]) -> +    [{password, Value} | ssl_options(client,T)]; +ssl_options(server, ["server_verify", Value|T]) -> +    [{verify, atomize(Value)} | ssl_options(server,T)]; +ssl_options(client, ["client_verify", Value|T]) -> +    [{verify, atomize(Value)} | ssl_options(client,T)]; +ssl_options(server, ["server_verify_fun", Value|T]) -> +    [{verify_fun, verify_fun(Value)} | ssl_options(server,T)]; +ssl_options(client, ["client_verify_fun", Value|T]) -> +    [{verify_fun, verify_fun(Value)} | ssl_options(client,T)]; +ssl_options(server, ["server_crl_check", Value|T]) -> +    [{crl_check, atomize(Value)} | ssl_options(server,T)]; +ssl_options(client, ["client_crl_check", Value|T]) -> +    [{crl_check, atomize(Value)} | ssl_options(client,T)]; +ssl_options(server, ["server_crl_cache", Value|T]) -> +    [{crl_cache, termify(Value)} | ssl_options(server,T)]; +ssl_options(client, ["client_crl_cache", Value|T]) -> +    [{crl_cache, termify(Value)} | ssl_options(client,T)]; +ssl_options(server, ["server_reuse_sessions", Value|T]) -> +    [{reuse_sessions, atomize(Value)} | ssl_options(server,T)]; +ssl_options(client, ["client_reuse_sessions", Value|T]) -> +    [{reuse_sessions, atomize(Value)} | ssl_options(client,T)]; +ssl_options(server, ["server_secure_renegotiate", Value|T]) -> +    [{secure_renegotiate, atomize(Value)} | ssl_options(server,T)]; +ssl_options(client, ["client_secure_renegotiate", Value|T]) -> +    [{secure_renegotiate, atomize(Value)} | ssl_options(client,T)]; +ssl_options(server, ["server_depth", Value|T]) -> +    [{depth, list_to_integer(Value)} | ssl_options(server,T)]; +ssl_options(client, ["client_depth", Value|T]) -> +    [{depth, list_to_integer(Value)} | ssl_options(client,T)]; +ssl_options(server, ["server_hibernate_after", Value|T]) -> +    [{hibernate_after, list_to_integer(Value)} | ssl_options(server,T)]; +ssl_options(client, ["client_hibernate_after", Value|T]) -> +    [{hibernate_after, list_to_integer(Value)} | ssl_options(client,T)]; +ssl_options(server, ["server_ciphers", Value|T]) -> +    [{ciphers, Value} | ssl_options(server,T)]; +ssl_options(client, ["client_ciphers", Value|T]) -> +    [{ciphers, Value} | ssl_options(client,T)]; +ssl_options(server, ["server_dhfile", Value|T]) -> +    [{dhfile, Value} | ssl_options(server,T)]; +ssl_options(server, ["server_fail_if_no_peer_cert", Value|T]) -> +    [{fail_if_no_peer_cert, atomize(Value)} | ssl_options(server,T)]; +ssl_options(Type, Opts) -> +    error(malformed_ssl_dist_opt, [Type, Opts]). + +atomize(List) when is_list(List) -> +    list_to_atom(List); +atomize(Atom) when is_atom(Atom) -> +    Atom. + +termify(String) when is_list(String) -> +    {ok, Tokens, _} = erl_scan:string(String ++ "."), +    {ok, Term} = erl_parse:parse_term(Tokens), +    Term. + +verify_fun(Value) -> +    case termify(Value) of +	{Mod, Func, State} when is_atom(Mod), is_atom(Func) -> +	    Fun = fun Mod:Func/3, +	    {Fun, State}; +	_ -> +	    error(malformed_ssl_dist_opt, [Value]) +    end. + +%% ------------------------------------------------------------------------- + +%% Trace point +trace(Term) -> Term. + +%% Keep an eye on distribution Pid:s we know of +monitor_pid(Pid) -> +    %%spawn( +    %%  fun () -> +    %%          MRef = erlang:monitor(process, Pid), +    %%          receive +    %%              {'DOWN', MRef, _, _, normal} -> +    %%                  error_logger:error_report( +    %%                    [dist_proc_died, +    %%                     {reason, normal}, +    %%                     {pid, Pid}]); +    %%              {'DOWN', MRef, _, _, Reason} -> +    %%                  error_logger:info_report( +    %%                    [dist_proc_died, +    %%                     {reason, Reason}, +    %%                     {pid, Pid}]) +    %%          end +    %%  end), +    Pid. + +dbg() -> +    dbg:stop(), +    dbg:tracer(), +    dbg:p(all, c), +    dbg:tpl(?MODULE, cx), +    dbg:tpl(erlang, dist_ctrl_get_data_notification, cx), +    dbg:tpl(erlang, dist_ctrl_get_data, cx), +    dbg:tpl(erlang, dist_ctrl_put_data, cx), +    ok. diff --git a/lib/ssl/src/ssl.app.src b/lib/ssl/src/ssl.app.src index 51407ef3b9..c5b55641a1 100644 --- a/lib/ssl/src/ssl.app.src +++ b/lib/ssl/src/ssl.app.src @@ -37,7 +37,6 @@  	       %% Erlang Distribution over SSL/TLS  	       inet_tls_dist,  	       inet6_tls_dist, -	       ssl_tls_dist_proxy,  	       ssl_dist_sup,                 ssl_dist_connection_sup,                 ssl_dist_admin_sup, @@ -63,7 +62,5 @@      {applications, [crypto, public_key, kernel, stdlib]},      {env, []},      {mod, {ssl_app, []}}, -    {runtime_dependencies, ["stdlib-3.2","public_key-1.5","kernel-3.0", -			    "erts-7.0","crypto-3.3", "inets-5.10.7"]}]}. - - +    {runtime_dependencies, ["stdlib-3.2","public_key-1.5","kernel-6.0", +			    "erts-10.0","crypto-3.3", "inets-5.10.7"]}]}. diff --git a/lib/ssl/src/ssl_cipher.erl b/lib/ssl/src/ssl_cipher.erl index 50c5f0d755..b6cd22dd13 100644 --- a/lib/ssl/src/ssl_cipher.erl +++ b/lib/ssl/src/ssl_cipher.erl @@ -375,30 +375,38 @@ psk_suites({3, N}) ->  psk_suites(N)    when N >= 3 ->      [ +     ?TLS_ECDHE_PSK_WITH_AES_256_GCM_SHA384,       ?TLS_DHE_PSK_WITH_AES_256_GCM_SHA384,       ?TLS_RSA_PSK_WITH_AES_256_GCM_SHA384,       ?TLS_PSK_WITH_AES_256_GCM_SHA384, +     ?TLS_ECDHE_PSK_WITH_AES_256_CBC_SHA384,       ?TLS_DHE_PSK_WITH_AES_256_CBC_SHA384,       ?TLS_RSA_PSK_WITH_AES_256_CBC_SHA384,       ?TLS_PSK_WITH_AES_256_CBC_SHA384, +     ?TLS_ECDHE_PSK_WITH_AES_128_GCM_SHA256,       ?TLS_DHE_PSK_WITH_AES_128_GCM_SHA256,       ?TLS_RSA_PSK_WITH_AES_128_GCM_SHA256,       ?TLS_PSK_WITH_AES_128_GCM_SHA256, +     ?TLS_ECDHE_PSK_WITH_AES_128_CBC_SHA256,       ?TLS_DHE_PSK_WITH_AES_128_CBC_SHA256,       ?TLS_RSA_PSK_WITH_AES_128_CBC_SHA256,       ?TLS_PSK_WITH_AES_128_CBC_SHA256      ] ++ psk_suites(0);  psk_suites(_) -> -	[?TLS_DHE_PSK_WITH_AES_256_CBC_SHA, +	[?TLS_ECDHE_PSK_WITH_AES_256_CBC_SHA, +	 ?TLS_DHE_PSK_WITH_AES_256_CBC_SHA,  	 ?TLS_RSA_PSK_WITH_AES_256_CBC_SHA,  	 ?TLS_PSK_WITH_AES_256_CBC_SHA, +	 ?TLS_ECDHE_PSK_WITH_AES_128_CBC_SHA,  	 ?TLS_DHE_PSK_WITH_AES_128_CBC_SHA,  	 ?TLS_RSA_PSK_WITH_AES_128_CBC_SHA,  	 ?TLS_PSK_WITH_AES_128_CBC_SHA, +	 ?TLS_ECDHE_PSK_WITH_3DES_EDE_CBC_SHA,  	 ?TLS_DHE_PSK_WITH_3DES_EDE_CBC_SHA,  	 ?TLS_RSA_PSK_WITH_3DES_EDE_CBC_SHA,  	 ?TLS_PSK_WITH_3DES_EDE_CBC_SHA, +	 ?TLS_ECDHE_PSK_WITH_RC4_128_SHA,  	 ?TLS_DHE_PSK_WITH_RC4_128_SHA,  	 ?TLS_RSA_PSK_WITH_RC4_128_SHA,  	 ?TLS_PSK_WITH_RC4_128_SHA]. @@ -565,6 +573,15 @@ suite_definition(?TLS_RSA_PSK_WITH_AES_128_CBC_SHA) ->  suite_definition(?TLS_RSA_PSK_WITH_AES_256_CBC_SHA) ->      {rsa_psk, aes_256_cbc, sha, default_prf}; +%%% PSK NULL Cipher Suites RFC 4785 + +suite_definition(?TLS_PSK_WITH_NULL_SHA) -> +    {psk, null, sha, default_prf}; +suite_definition(?TLS_DHE_PSK_WITH_NULL_SHA) -> +    {dhe_psk, null, sha, default_prf}; +suite_definition(?TLS_RSA_PSK_WITH_NULL_SHA) -> +    {rsa_psk, null, sha, default_prf}; +  %%% TLS 1.2 PSK Cipher Suites RFC 5487  suite_definition(?TLS_PSK_WITH_AES_128_GCM_SHA256) -> @@ -606,6 +623,36 @@ suite_definition(?TLS_RSA_PSK_WITH_NULL_SHA256) ->  suite_definition(?TLS_RSA_PSK_WITH_NULL_SHA384) ->      {rsa_psk, null, sha384, default_prf}; +%%% ECDHE PSK Cipher Suites RFC 5489 + +suite_definition(?TLS_ECDHE_PSK_WITH_RC4_128_SHA) -> +    {ecdhe_psk, rc4_128, sha, default_prf}; +suite_definition(?TLS_ECDHE_PSK_WITH_3DES_EDE_CBC_SHA) -> +    {ecdhe_psk, '3des_ede_cbc', sha, default_prf}; +suite_definition(?TLS_ECDHE_PSK_WITH_AES_128_CBC_SHA) -> +    {ecdhe_psk, aes_128_cbc, sha, default_prf}; +suite_definition(?TLS_ECDHE_PSK_WITH_AES_256_CBC_SHA) -> +    {ecdhe_psk, aes_256_cbc, sha, default_prf}; +suite_definition(?TLS_ECDHE_PSK_WITH_AES_128_CBC_SHA256) -> +    {ecdhe_psk, aes_128_cbc, sha256, default_prf}; +suite_definition(?TLS_ECDHE_PSK_WITH_AES_256_CBC_SHA384) -> +    {ecdhe_psk, aes_256_cbc, sha384, default_prf}; +suite_definition(?TLS_ECDHE_PSK_WITH_NULL_SHA256) -> +    {ecdhe_psk, null, sha256, default_prf}; +suite_definition(?TLS_ECDHE_PSK_WITH_NULL_SHA384) -> +    {ecdhe_psk, null, sha384, default_prf}; + +%%% ECDHE_PSK with AES-GCM and AES-CCM Cipher Suites, draft-ietf-tls-ecdhe-psk-aead-05 + +suite_definition(?TLS_ECDHE_PSK_WITH_AES_128_GCM_SHA256) -> +    {ecdhe_psk, aes_128_gcm, null, sha256}; +suite_definition(?TLS_ECDHE_PSK_WITH_AES_256_GCM_SHA384) -> +    {ecdhe_psk, aes_256_gcm, null, sha384}; +%% suite_definition(?TLS_ECDHE_PSK_WITH_AES_128_CCM_8_SHA256) -> +%%    {ecdhe_psk, aes_128_ccm, null, sha256}; +%% suite_definition(?TLS_ECDHE_PSK_WITH_AES_128_CCM_SHA256) -> +%%    {ecdhe_psk, aes_256_ccm, null, sha256}; +  %%% SRP Cipher Suites RFC 5054  suite_definition(?TLS_SRP_SHA_WITH_3DES_EDE_CBC_SHA) -> @@ -867,6 +914,15 @@ suite({rsa_psk, aes_128_cbc,sha}) ->  suite({rsa_psk, aes_256_cbc,sha}) ->      ?TLS_RSA_PSK_WITH_AES_256_CBC_SHA; +%%% PSK NULL Cipher Suites RFC 4785 + +suite({psk, null, sha}) -> +    ?TLS_PSK_WITH_NULL_SHA; +suite({dhe_psk, null, sha}) -> +    ?TLS_DHE_PSK_WITH_NULL_SHA; +suite({rsa_psk, null, sha}) -> +    ?TLS_RSA_PSK_WITH_NULL_SHA; +  %%% TLS 1.2 PSK Cipher Suites RFC 5487  suite({psk, aes_128_gcm, null, sha256}) -> @@ -908,6 +964,36 @@ suite({rsa_psk, null, sha256}) ->  suite({rsa_psk, null, sha384}) ->      ?TLS_RSA_PSK_WITH_NULL_SHA384; +%%% ECDHE PSK Cipher Suites RFC 5489 + +suite({ecdhe_psk, rc4_128,sha}) -> +    ?TLS_ECDHE_PSK_WITH_RC4_128_SHA; +suite({ecdhe_psk, '3des_ede_cbc',sha}) -> +    ?TLS_ECDHE_PSK_WITH_3DES_EDE_CBC_SHA; +suite({ecdhe_psk, aes_128_cbc,sha}) -> +    ?TLS_ECDHE_PSK_WITH_AES_128_CBC_SHA; +suite({ecdhe_psk, aes_256_cbc,sha}) -> +    ?TLS_ECDHE_PSK_WITH_AES_256_CBC_SHA; +suite({ecdhe_psk, aes_128_cbc, sha256}) -> +    ?TLS_ECDHE_PSK_WITH_AES_128_CBC_SHA256; +suite({ecdhe_psk, aes_256_cbc, sha384}) -> +    ?TLS_ECDHE_PSK_WITH_AES_256_CBC_SHA384; +suite({ecdhe_psk, null, sha256}) -> +    ?TLS_ECDHE_PSK_WITH_NULL_SHA256; +suite({ecdhe_psk, null, sha384}) -> +    ?TLS_ECDHE_PSK_WITH_NULL_SHA384; + +%%% ECDHE_PSK with AES-GCM and AES-CCM Cipher Suites, draft-ietf-tls-ecdhe-psk-aead-05 + +suite({ecdhe_psk, aes_128_gcm, null, sha256}) -> +    ?TLS_ECDHE_PSK_WITH_AES_128_GCM_SHA256; +suite({ecdhe_psk, aes_256_gcm, null, sha384}) -> +    ?TLS_ECDHE_PSK_WITH_AES_256_GCM_SHA384; +%% suite({ecdhe_psk, aes_128_ccm, null, sha256}) -> +%%     ?TLS_ECDHE_PSK_WITH_AES_128_CCM_8_SHA256; +%% suite({ecdhe_psk, aes_256_ccm, null, sha256}) -> +%%     ?TLS_ECDHE_PSK_WITH_AES_128_CCM_SHA256; +  %%% SRP Cipher Suites RFC 5054  suite({srp_anon, '3des_ede_cbc', sha}) -> @@ -1467,7 +1553,8 @@ is_acceptable_keyexchange(dhe_dss, Algos) ->  is_acceptable_keyexchange(dhe_rsa, Algos) ->      proplists:get_bool(dh, Algos) andalso          proplists:get_bool(rsa, Algos); -is_acceptable_keyexchange(ecdh_anon, Algos) -> +is_acceptable_keyexchange(KeyExchange, Algos) when KeyExchange == ecdh_anon; +                                                   KeyExchange == ecdhe_psk ->      proplists:get_bool(ecdh, Algos);  is_acceptable_keyexchange(KeyExchange, Algos) when KeyExchange == ecdh_ecdsa;                                                     KeyExchange == ecdhe_ecdsa -> diff --git a/lib/ssl/src/ssl_cipher.hrl b/lib/ssl/src/ssl_cipher.hrl index 8e8f3d9c67..e5462d8402 100644 --- a/lib/ssl/src/ssl_cipher.hrl +++ b/lib/ssl/src/ssl_cipher.hrl @@ -399,6 +399,17 @@  %%      TLS_RSA_PSK_WITH_AES_256_CBC_SHA      = { 0x00, 0x95 };  -define(TLS_RSA_PSK_WITH_AES_256_CBC_SHA, <<?BYTE(16#00), ?BYTE(16#95)>>). +%%% PSK NULL Cipher Suites RFC 4785 + +%%      TLS_PSK_WITH_NULL_SHA      = { 0x00, 0x2C }; +-define(TLS_PSK_WITH_NULL_SHA, <<?BYTE(16#00), ?BYTE(16#2C)>>). + +%%      TLS_DHE_PSK_WITH_NULL_SHA  = { 0x00, 0x2D }; +-define(TLS_DHE_PSK_WITH_NULL_SHA, <<?BYTE(16#00), ?BYTE(16#2D)>>). + +%%      TLS_RSA_PSK_WITH_NULL_SHA  = { 0x00, 0x2E }; +-define(TLS_RSA_PSK_WITH_NULL_SHA, <<?BYTE(16#00), ?BYTE(16#2E)>>). +  %%% TLS 1.2 PSK Cipher Suites RFC 5487  %%      TLS_PSK_WITH_AES_128_GCM_SHA256       = {0x00,0xA8}; @@ -455,6 +466,46 @@  %%      TLS_RSA_PSK_WITH_NULL_SHA384          = {0x00,0xB9};  -define(TLS_RSA_PSK_WITH_NULL_SHA384, <<?BYTE(16#00), ?BYTE(16#B9)>>). +%%% ECDHE PSK Cipher Suites RFC 5489 + +%%      TLS_ECDHE_PSK_WITH_RC4_128_SHA          = {0xC0,0x33}; +-define(TLS_ECDHE_PSK_WITH_RC4_128_SHA, <<?BYTE(16#C0), ?BYTE(16#33)>>). + +%%      TLS_ECDHE_PSK_WITH_3DES_EDE_CBC_SHA     = {0xC0,0x34}; +-define(TLS_ECDHE_PSK_WITH_3DES_EDE_CBC_SHA, <<?BYTE(16#C0), ?BYTE(16#34)>>). + +%%      TLS_ECDHE_PSK_WITH_AES_128_CBC_SHA      = {0xC0,0x35}; +-define(TLS_ECDHE_PSK_WITH_AES_128_CBC_SHA, <<?BYTE(16#C0), ?BYTE(16#35)>>). + +%%      TLS_ECDHE_PSK_WITH_AES_256_CBC_SHA      = {0xC0,0x36}; +-define(TLS_ECDHE_PSK_WITH_AES_256_CBC_SHA, <<?BYTE(16#C0), ?BYTE(16#36)>>). + +%%      TLS_ECDHE_PSK_WITH_AES_128_CBC_SHA256   = {0xC0,0x37}; +-define(TLS_ECDHE_PSK_WITH_AES_128_CBC_SHA256, <<?BYTE(16#C0), ?BYTE(16#37)>>). + +%%      TLS_ECDHE_PSK_WITH_AES_256_CBC_SHA384   = {0xC0,0x38}; +-define(TLS_ECDHE_PSK_WITH_AES_256_CBC_SHA384, <<?BYTE(16#C0), ?BYTE(16#38)>>). + +%%      TLS_ECDHE_PSK_WITH_NULL_SHA256          = {0xC0,0x3A}; +-define(TLS_ECDHE_PSK_WITH_NULL_SHA256, <<?BYTE(16#C0), ?BYTE(16#3A)>>). + +%%      TLS_ECDHE_PSK_WITH_NULL_SHA384          = {0xC0,0x3B}; +-define(TLS_ECDHE_PSK_WITH_NULL_SHA384, <<?BYTE(16#C0), ?BYTE(16#3B)>>). + +%%% ECDHE_PSK with AES-GCM and AES-CCM Cipher Suites, draft-ietf-tls-ecdhe-psk-aead-05 + +%%      TLS_ECDHE_PSK_WITH_AES_128_GCM_SHA256   = {0xTBD; 0xTBD} {0xD0,0x01}; +-define(TLS_ECDHE_PSK_WITH_AES_128_GCM_SHA256, <<?BYTE(16#D0), ?BYTE(16#01)>>). + +%%      TLS_ECDHE_PSK_WITH_AES_256_GCM_SHA384   = {0xTBD; 0xTBD} {0xD0,0x02}; +-define(TLS_ECDHE_PSK_WITH_AES_256_GCM_SHA384, <<?BYTE(16#D0), ?BYTE(16#02)>>). + +%%      TLS_ECDHE_PSK_WITH_AES_128_CCM_8_SHA256 = {0xTBD; 0xTBD} {0xD0,0x03}; +-define(TLS_ECDHE_PSK_WITH_AES_128_CCM_8_SHA256, <<?BYTE(16#D0), ?BYTE(16#03)>>). + +%%      TLS_ECDHE_PSK_WITH_AES_128_CCM_SHA256   = {0xTBD; 0xTBD} {0xD0,0x05}; +-define(TLS_ECDHE_PSK_WITH_AES_128_CCM_SHA256, <<?BYTE(16#D0), ?BYTE(16#05)>>). +  %%% SRP Cipher Suites RFC 5054  %%      TLS_SRP_SHA_WITH_3DES_EDE_CBC_SHA     = { 0xC0,0x1A }; diff --git a/lib/ssl/src/ssl_connection.erl b/lib/ssl/src/ssl_connection.erl index 2dbe08e0a7..a5c7630f81 100644 --- a/lib/ssl/src/ssl_connection.erl +++ b/lib/ssl/src/ssl_connection.erl @@ -44,12 +44,14 @@  -export([send/2, recv/3, close/2, shutdown/2,  	 new_user/2, get_opts/2, set_opts/2,   	 peer_certificate/1, renegotiation/1, negotiated_protocol/1, prf/5, +         get_sslsocket/1, handshake_complete/3,  	 connection_information/2, handle_common_event/5  	]).  %% General gen_statem state functions with extra callback argument   %% to determine if it is an SSL/TLS or DTLS gen_statem machine --export([init/4, hello/4, abbreviated/4, certify/4, cipher/4, connection/4, downgrade/4]). +-export([init/4, hello/4, abbreviated/4, certify/4, cipher/4, +         connection/4, death_row/4, downgrade/4]).  %% gen_statem callbacks  -export([terminate/3, format_status/2]). @@ -146,8 +148,8 @@ socket_control(Connection, Socket, Pid, Transport) ->  -spec socket_control(tls_connection | dtls_connection, port(), pid(), atom(), pid()| undefined) ->       {ok, #sslsocket{}} | {error, reason()}.    %%--------------------------------------------------------------------	     -socket_control(Connection, Socket, Pid, Transport, udp_listner) -> -    %% dtls listner process must have the socket control +socket_control(Connection, Socket, Pid, Transport, udp_listener) -> +    %% dtls listener process must have the socket control      {ok, Connection:socket(Pid, Transport, Socket, Connection, undefined)};  socket_control(tls_connection = Connection, Socket, Pid, Transport, ListenTracker) -> @@ -262,6 +264,13 @@ peer_certificate(ConnectionPid) ->  renegotiation(ConnectionPid) ->      call(ConnectionPid, renegotiate).  + +get_sslsocket(ConnectionPid) -> +    call(ConnectionPid, get_sslsocket). + +handshake_complete(ConnectionPid, Node, DHandle) -> +    call(ConnectionPid, {handshake_complete, Node, DHandle}). +  %%--------------------------------------------------------------------  -spec prf(pid(), binary() | 'master_secret', binary(),  	  [binary() | ssl:prf_random()], non_neg_integer()) -> @@ -359,6 +368,12 @@ init({call, From}, {start, {Opts, EmOpts}, Timeout},              socket_options = SockOpts} = State0, Connection) ->      try           SslOpts = ssl:handle_options(Opts, OrigSSLOptions), +        case SslOpts of +            #ssl_options{erl_dist = true} -> +                process_flag(priority, max); +            _ -> +                ok +        end,  	State = ssl_config(SslOpts, Role, State0),  	init({call, From}, {start, Timeout},   	     State#state{ssl_options = SslOpts, socket_options = new_emulated(EmOpts, SockOpts)}, Connection) @@ -517,7 +532,7 @@ certify(internal, #server_key_exchange{exchange_keys = Keys},    when Alg == dhe_dss; Alg == dhe_rsa;         Alg == ecdhe_rsa; Alg == ecdhe_ecdsa;         Alg == dh_anon; Alg == ecdh_anon; -       Alg == psk; Alg == dhe_psk; Alg == rsa_psk; +       Alg == psk; Alg == dhe_psk; Alg == ecdhe_psk; Alg == rsa_psk;         Alg == srp_dss; Alg == srp_rsa; Alg == srp_anon ->      Params = ssl_handshake:decode_server_key(Keys, Alg, ssl:tls_version(Version)), @@ -542,6 +557,15 @@ certify(internal, #server_key_exchange{exchange_keys = Keys},  	    end      end; +certify(internal, #certificate_request{}, +	#state{role = client, negotiated_version = Version, +               key_algorithm = Alg} = State, _) +  when Alg == dh_anon; Alg == ecdh_anon; +       Alg == psk; Alg == dhe_psk; Alg == ecdhe_psk; Alg == rsa_psk; +       Alg == srp_dss; Alg == srp_rsa; Alg == srp_anon -> +    handle_own_alert(?ALERT_REC(?FATAL, ?HANDSHAKE_FAILURE), +                     Version, certify, State); +  certify(internal, #certificate_request{} = CertRequest,  	#state{session = #session{own_certificate = Cert},  	       role = client, @@ -739,7 +763,7 @@ cipher(Type, Msg, State, Connection) ->  		 #state{}, tls_connection | dtls_connection) ->  			gen_statem:state_function_result().  %%-------------------------------------------------------------------- -connection({call, From}, {application_data, Data},   +connection({call, {FromPid, _} = From}, {application_data, Data},  	   #state{protocol_cb = Connection} = State, Connection) ->      %% We should look into having a worker process to do this to       %% parallize send and receive decoding and not block the receiver @@ -747,7 +771,13 @@ connection({call, From}, {application_data, Data},       try  	 write_application_data(Data, From, State)       catch throw:Error -> -	     hibernate_after(connection, State, [{reply, From, Error}]) +             case self() of +                 FromPid -> +                     {stop, {shutdown, Error}}; +                 _ -> +                     hibernate_after( +                       connection, State, [{reply, From, Error}]) +             end       end;  connection({call, RecvFrom}, {recv, N, Timeout},    	   #state{protocol_cb = Connection,  socket_options = @@ -775,8 +805,50 @@ connection({call, From}, negotiated_protocol,  	   #state{negotiated_protocol = SelectedProtocol} = State, _) ->      hibernate_after(connection, State,  		    [{reply, From, {ok, SelectedProtocol}}]); +connection( +  {call, From}, {handshake_complete, _Node, DHandle}, +  #state{ +     ssl_options = #ssl_options{erl_dist = true}, +     socket_options = SockOpts, +     protocol_specific = ProtocolSpecific} = State, +  Connection) -> +    %% From now on we execute on normal priority +    process_flag(priority, normal), +    try erlang:dist_ctrl_get_data_notification(DHandle) of +        _ -> +            NewState = +                State#state{ +                  socket_options = +                      SockOpts#socket_options{active = true}, +                  protocol_specific = +                      ProtocolSpecific#{d_handle => DHandle}}, +            {Record, NewerState} = Connection:next_record_if_active(NewState), +            Connection:next_event(connection, Record, NewerState, [{reply, From, ok}]) +    catch _:Reason -> +            death_row(State, Reason) +    end;  connection({call, From}, Msg, State, Connection) ->      handle_call(Msg, From, connection, State, Connection); +connection( +  info, dist_data = Msg, +  #state{ +     ssl_options = #ssl_options{erl_dist = true}, +     protocol_specific = #{d_handle := DHandle}} = State, +  _) -> +    eat_msgs(Msg), +    try send_dist_data(connection, State, DHandle, []) +    catch _:Reason -> +            death_row(State, Reason) +    end; +connection( +  info, tick = Msg, +  #state{ +     ssl_options = #ssl_options{erl_dist = true}, +     protocol_specific = #{d_handle := _}}, +  _) -> +    eat_msgs(Msg), +    {keep_state_and_data, +     [{next_event, {call, {self(), undefined}}, {application_data, <<>>}}]};  connection(info, Msg, State, _) ->      handle_info(Msg, connection, State);  connection(internal, {recv, _}, State, Connection) -> @@ -785,6 +857,30 @@ connection(Type, Msg, State, Connection) ->      handle_common_event(Type, Msg, connection, State, Connection).  %%-------------------------------------------------------------------- +-spec death_row(gen_statem:event_type(), term(), +		#state{}, tls_connection | dtls_connection) -> +		       gen_statem:state_function_result(). +%%-------------------------------------------------------------------- +%% We just wait for the owner to die which triggers the monitor, +%% or the socket may die too +death_row( +  info, {'DOWN', MonitorRef, _, _, Reason}, +  #state{user_application={MonitorRef,_Pid} = State}, +  _) -> +    {stop, {shutdown, Reason}, State}; +death_row( +  info, {'EXIT', Socket, Reason}, #state{socket = Socket} = State, _) -> +    {stop, {shutdown, Reason}, State}; +death_row(state_timeout, Reason, _State, _Connection) -> +    {stop, {shutdown,Reason}}; +death_row(_Type, _Msg, State, _Connection) -> +    {keep_state, State, [postpone]}. + +%% State entry function +death_row(State, Reason) -> +    {next_state, death_row, State, [{state_timeout, 5000, Reason}]}. + +%%--------------------------------------------------------------------  -spec downgrade(gen_statem:event_type(), term(),   		#state{}, tls_connection | dtls_connection) ->  		       gen_statem:state_function_result(). @@ -795,10 +891,10 @@ downgrade(internal, #alert{description = ?CLOSE_NOTIFY},      tls_socket:setopts(Transport, Socket, [{active, false}, {packet, 0}, {mode, binary}]),      Transport:controlling_process(Socket, Pid),      gen_statem:reply(From, {ok, Socket}), -    {stop, normal, State}; +    stop_normal(State);  downgrade(timeout, downgrade, #state{downgrade = {_, From}} = State, _) ->      gen_statem:reply(From, {error, timeout}), -    {stop, normal, State}; +    stop_normal(State);  downgrade(Type, Event, State, Connection) ->      handle_common_event(Type, Event, downgrade, State, Connection). @@ -868,7 +964,7 @@ handle_call({shutdown, How0}, From, _,  	    #state{transport_cb = Transport,  		   negotiated_version = Version,  		   connection_states = ConnectionStates, -		   socket = Socket}, Connection) -> +		   socket = Socket} = State, Connection) ->      case How0 of  	How when How == write; How == both ->	      	    Alert = ?ALERT_REC(?WARNING, ?CLOSE_NOTIFY), @@ -884,7 +980,7 @@ handle_call({shutdown, How0}, From, _,  	    {keep_state_and_data, [{reply, From, ok}]};  	Error ->  	    gen_statem:reply(From, {error, Error}), -	    {stop, normal} +            stop_normal(State)      end;  handle_call({recv, _N, _Timeout}, From, _,    		  #state{socket_options =  @@ -919,6 +1015,15 @@ handle_call({set_opts, Opts0}, From, StateName,  handle_call(renegotiate, From, StateName, _, _) when StateName =/= connection ->      {keep_state_and_data, [{reply, From, {error, already_renegotiating}}]}; + +handle_call( +  get_sslsocket, From, _StateName, +  #state{transport_cb = Transport, socket = Socket, tracker = Tracker}, +  Connection) -> +    SslSocket = +        Connection:socket(self(), Transport, Socket, Connection, Tracker), +    {keep_state_and_data, [{reply, From, SslSocket}]}; +  handle_call({prf, Secret, Label, Seed, WantedLength}, From, _,  	    #state{connection_states = ConnectionStates,  		   negotiated_version = Version}, _) -> @@ -955,18 +1060,19 @@ handle_info({ErrorTag, Socket, econnaborted}, StateName,  		   tracker = Tracker} = State)  when StateName =/= connection ->      alert_user(Transport, Tracker,Socket,   	       StartFrom, ?ALERT_REC(?FATAL, ?CLOSE_NOTIFY), Role, Connection), -    {stop, normal, State}; +    stop_normal(State);  handle_info({ErrorTag, Socket, Reason}, StateName, #state{socket = Socket,  							  error_tag = ErrorTag} = State)  ->      Report = io_lib:format("SSL: Socket error: ~p ~n", [Reason]), -    error_logger:info_report(Report), +    error_logger:error_report(Report),      handle_normal_shutdown(?ALERT_REC(?FATAL, ?CLOSE_NOTIFY), StateName, State), -    {stop, normal, State}; +    stop_normal(State); -handle_info({'DOWN', MonitorRef, _, _, _}, _,  -	    State = #state{user_application={MonitorRef,_Pid}}) -> -    {stop, normal, State};    +handle_info( +  {'DOWN', MonitorRef, _, _, _}, _, +  #state{user_application={MonitorRef,_Pid}} = State) -> +    stop_normal(State);  %%% So that terminate will be run when supervisor issues shutdown  handle_info({'EXIT', _Sup, shutdown}, _StateName, State) -> @@ -974,6 +1080,8 @@ handle_info({'EXIT', _Sup, shutdown}, _StateName, State) ->  handle_info({'EXIT', Socket, normal}, _StateName, #state{socket = Socket} = State) ->      %% Handle as transport close"      {stop, {shutdown, transport_closed}, State}; +handle_info({'EXIT', Socket, Reason}, _StateName, #state{socket = Socket} = State) -> +    {stop, {shutdown, Reason}, State};  handle_info(allow_renegotiate, StateName, State) ->      {next_state, StateName, State#state{allow_renegotiate = true}}; @@ -997,6 +1105,38 @@ handle_info(Msg, StateName, #state{socket = Socket, error_tag = Tag} = State) ->      error_logger:info_report(Report),      {next_state, StateName, State}. + + +send_dist_data(StateName, State, DHandle, Acc) -> +    case erlang:dist_ctrl_get_data(DHandle) of +        none -> +            erlang:dist_ctrl_get_data_notification(DHandle), +            hibernate_after(StateName, State, lists:reverse(Acc)); +        Data -> +            send_dist_data( +              StateName, State, DHandle, +              [{next_event, {call, {self(), undefined}}, {application_data, Data}} +               |Acc]) +    end. + +%% Overload mitigation +eat_msgs(Msg) -> +    receive Msg -> eat_msgs(Msg) +    after 0 -> ok +    end. + +%% When running with erl_dist the stop reason 'normal' +%% would be too silent and prevent cleanup +stop_normal(State) -> +    Reason = +        case State of +            #state{ssl_options = #ssl_options{erl_dist = true}} -> +                {shutdown, normal}; +            _ -> +                normal +        end, +    {stop, Reason, State}. +  %%--------------------------------------------------------------------  %% gen_statem callbacks  %%-------------------------------------------------------------------- @@ -1071,7 +1211,7 @@ format_status(terminate, [_, StateName, State]) ->  %%--------------------------------------------------------------------  %%%   %%-------------------------------------------------------------------- -write_application_data(Data0, From,  +write_application_data(Data0, {FromPid, _} = From,  		       #state{socket = Socket,  			      negotiated_version = Version,  			      protocol_cb = Connection, @@ -1086,10 +1226,19 @@ write_application_data(Data0, From,  	    Connection:renegotiate(State#state{renegotiation = {true, internal}},   			[{next_event, {call, From}, {application_data, Data0}}]);  	false -> -	    {Msgs, ConnectionStates} = Connection:encode_data(Data, Version, ConnectionStates0), -	    Result = Connection:send(Transport, Socket, Msgs), -	        ssl_connection:hibernate_after(connection, State#state{connection_states = ConnectionStates},  -					       [{reply, From, Result}]) +	    {Msgs, ConnectionStates} = +                Connection:encode_data(Data, Version, ConnectionStates0), +            NewState = State#state{connection_states = ConnectionStates}, +	    case Connection:send(Transport, Socket, Msgs) of +                ok when FromPid =:= self() -> +                    hibernate_after(connection, NewState, []); +                Error when FromPid =:= self() -> +                    {stop, {shutdown, Error}, NewState}; +                ok -> +                    hibernate_after(connection, NewState, [{reply, From, ok}]); +                Result -> +                    hibernate_after(connection, NewState, [{reply, From, Result}]) +            end      end.  read_application_data(Data, #state{user_application = {_Mon, Pid}, @@ -1109,30 +1258,57 @@ read_application_data(Data, #state{user_application = {_Mon, Pid},  	      end,      case get_data(SOpts, BytesToRead, Buffer1) of  	{ok, ClientData, Buffer} -> % Send data -	    SocketOpt = deliver_app_data(Transport, Socket, SOpts,  -					 ClientData, Pid, RecvFrom, Tracker, Connection), -	    cancel_timer(Timer), -	    State = State0#state{user_data_buffer = Buffer, -				 start_or_recv_from = undefined, -				 timer = undefined, -				 bytes_to_read = undefined, -				 socket_options = SocketOpt  -				}, -	    if -		SocketOpt#socket_options.active =:= false; Buffer =:= <<>> ->  -		    %% Passive mode, wait for active once or recv -		    %% Active and empty, get more data -		    Connection:next_record_if_active(State); -	 	true -> %% We have more data - 		    read_application_data(<<>>, State) -	    end; +            case State0 of +                #state{ +                   ssl_options = #ssl_options{erl_dist = true}, +                  protocol_specific = #{d_handle := DHandle}} -> +                    State = +                        State0#state{ +                          user_data_buffer = Buffer, +                          bytes_to_read = undefined}, +                    try erlang:dist_ctrl_put_data(DHandle, ClientData) of +                        _ +                          when SOpts#socket_options.active =:= false; +                               Buffer =:= <<>> -> +                            %% Passive mode, wait for active once or recv +                            %% Active and empty, get more data +                            Connection:next_record_if_active(State); +                        _ -> %% We have more data +                            read_application_data(<<>>, State) +                    catch _:Reason -> +                            death_row(State, Reason) +                    end; +                _ -> +                    SocketOpt = +                        deliver_app_data( +                          Transport, Socket, SOpts, +                          ClientData, Pid, RecvFrom, Tracker, Connection), +                    cancel_timer(Timer), +                    State = +                        State0#state{ +                          user_data_buffer = Buffer, +                          start_or_recv_from = undefined, +                          timer = undefined, +                          bytes_to_read = undefined, +                          socket_options = SocketOpt +                         }, +                    if +                        SocketOpt#socket_options.active =:= false; +                        Buffer =:= <<>> -> +                            %% Passive mode, wait for active once or recv +                            %% Active and empty, get more data +                            Connection:next_record_if_active(State); +                        true -> %% We have more data +                            read_application_data(<<>>, State) +                    end +            end;  	{more, Buffer} -> % no reply, we need more data  	    Connection:next_record(State0#state{user_data_buffer = Buffer});  	{passive, Buffer} ->  	    Connection:next_record_if_active(State0#state{user_data_buffer = Buffer});  	{error,_Reason} -> %% Invalid packet in packet mode  	    deliver_packet_error(Transport, Socket, SOpts, Buffer1, Pid, RecvFrom, Tracker, Connection), -	    {stop, normal, State0} +            stop_normal(State0)      end.  %%--------------------------------------------------------------------  %%%  @@ -1142,12 +1318,12 @@ handle_alert(#alert{level = ?FATAL} = Alert, StateName,  		    protocol_cb = Connection,  		    ssl_options = SslOpts, start_or_recv_from = From, host = Host,  		    port = Port, session = Session, user_application = {_Mon, Pid}, -		    role = Role, socket_options = Opts, tracker = Tracker}) -> +		    role = Role, socket_options = Opts, tracker = Tracker} = State) ->      invalidate_session(Role, Host, Port, Session),      log_alert(SslOpts#ssl_options.log_alert, Role, Connection:protocol_name(),                 StateName, Alert#alert{role = opposite_role(Role)}),      alert_user(Transport, Tracker, Socket, StateName, Opts, Pid, From, Alert, Role, Connection), -    {stop, normal}; +    stop_normal(State);  handle_alert(#alert{level = ?WARNING, description = ?CLOSE_NOTIFY} = Alert,   	     StateName, State) ->  @@ -1400,6 +1576,16 @@ certify_client_key_exchange(#client_dhe_psk_identity{} = ClientKey,      PremasterSecret =   	ssl_handshake:premaster_secret(ClientKey, ServerDhPrivateKey, Params, PSKLookup),      calculate_master_secret(PremasterSecret, State0, Connection, certify, cipher); + +certify_client_key_exchange(#client_ecdhe_psk_identity{} = ClientKey, +			    #state{diffie_hellman_keys = ServerEcDhPrivateKey, +				   ssl_options = +				       #ssl_options{user_lookup_fun = PSKLookup}} = State, +			    Connection) -> +    PremasterSecret = +	ssl_handshake:premaster_secret(ClientKey, ServerEcDhPrivateKey, PSKLookup), +    calculate_master_secret(PremasterSecret, State, Connection, certify, cipher); +  certify_client_key_exchange(#client_rsa_psk_identity{} = ClientKey,  			    #state{private_key = Key,  				   ssl_options =  @@ -1419,6 +1605,7 @@ certify_server(#state{key_algorithm = Algo} = State, _) when Algo == dh_anon;  							     Algo == ecdh_anon;   							     Algo == psk;   							     Algo == dhe_psk;  +							     Algo == ecdhe_psk;   							     Algo == srp_anon  ->      State; @@ -1525,6 +1712,28 @@ key_exchange(#state{role = server, key_algorithm = dhe_psk,      State = Connection:queue_handshake(Msg, State0),      State#state{diffie_hellman_keys = DHKeys}; +key_exchange(#state{role = server, key_algorithm = ecdhe_psk, +		    ssl_options = #ssl_options{psk_identity = PskIdentityHint}, +		    hashsign_algorithm = HashSignAlgo, +		    private_key = PrivateKey, +                    session = #session{ecc = ECCCurve}, +		    connection_states = ConnectionStates0, +		    negotiated_version = Version +		   } = State0, Connection) -> +    ECDHKeys = public_key:generate_key(ECCCurve), +    #{security_parameters := SecParams} = +	ssl_record:pending_connection_state(ConnectionStates0, read), +    #security_parameters{client_random = ClientRandom, +			 server_random = ServerRandom} = SecParams, +    Msg =  ssl_handshake:key_exchange(server, ssl:tls_version(Version), +				      {ecdhe_psk, +				       PskIdentityHint, ECDHKeys, +				       HashSignAlgo, ClientRandom, +				       ServerRandom, +				       PrivateKey}), +    State = Connection:queue_handshake(Msg, State0), +    State#state{diffie_hellman_keys = ECDHKeys}; +  key_exchange(#state{role = server, key_algorithm = rsa_psk,  		    ssl_options = #ssl_options{psk_identity = undefined}} = State, _) ->      State; @@ -1623,6 +1832,17 @@ key_exchange(#state{role = client,  				      {dhe_psk,   				       SslOpts#ssl_options.psk_identity, DhPubKey}),      Connection:queue_handshake(Msg, State0); + +key_exchange(#state{role = client, +		    ssl_options = SslOpts, +		    key_algorithm = ecdhe_psk, +		    negotiated_version = Version, +		    diffie_hellman_keys = ECDHKeys} = State0, Connection) -> +    Msg =  ssl_handshake:key_exchange(client, ssl:tls_version(Version), +				      {ecdhe_psk, +				       SslOpts#ssl_options.psk_identity, ECDHKeys}), +    Connection:queue_handshake(Msg, State0); +  key_exchange(#state{role = client,  		    ssl_options = SslOpts,  		    key_algorithm = rsa_psk, @@ -1678,6 +1898,12 @@ rsa_psk_key_exchange(Version, PskIdentity, PremasterSecret,  rsa_psk_key_exchange(_, _, _, _) ->      throw (?ALERT_REC(?FATAL,?HANDSHAKE_FAILURE, pub_key_is_not_rsa)). +request_client_cert(#state{key_algorithm = Alg} = State, _) +  when Alg == dh_anon; Alg == ecdh_anon; +       Alg == psk; Alg == dhe_psk; Alg == ecdhe_psk; Alg == rsa_psk; +       Alg == srp_dss; Alg == srp_rsa; Alg == srp_anon -> +    State; +  request_client_cert(#state{ssl_options = #ssl_options{verify = verify_peer,   						      signature_algs = SupportedHashSigns},  			   connection_states = ConnectionStates0, @@ -1799,6 +2025,18 @@ calculate_secret(#server_dhe_psk_params{      calculate_master_secret(PremasterSecret, State#state{diffie_hellman_keys = Keys},  			    Connection, certify, certify); +calculate_secret(#server_ecdhe_psk_params{ +                    dh_params = #server_ecdh_params{curve = ECCurve}} = ServerKey, +                 #state{ssl_options = #ssl_options{user_lookup_fun = PSKLookup}} =  +		     State=#state{session=Session}, Connection) -> +    ECDHKeys = public_key:generate_key(ECCurve), + +    PremasterSecret = ssl_handshake:premaster_secret(ServerKey, ECDHKeys, PSKLookup), +    calculate_master_secret(PremasterSecret, +			    State#state{diffie_hellman_keys = ECDHKeys, +					session = Session#session{ecc = ECCurve}}, +			    Connection, certify, certify); +  calculate_secret(#server_srp_params{srp_n = Prime, srp_g = Generator} = ServerKey,  		 #state{ssl_options = #ssl_options{srp_identity = SRPId}} = State,   		 Connection) -> @@ -1883,6 +2121,7 @@ is_anonymous(Algo) when Algo == dh_anon;  			Algo == ecdh_anon;  			Algo == psk;  			Algo == dhe_psk; +			Algo == ecdhe_psk;  			Algo == rsa_psk;  			Algo == srp_anon ->      true; diff --git a/lib/ssl/src/ssl_dist_sup.erl b/lib/ssl/src/ssl_dist_sup.erl index 690b896919..c241a9bced 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. @@ -46,8 +46,7 @@ start_link() ->  init([]) ->          AdminSup = ssl_admin_child_spec(),      ConnectionSup = ssl_connection_sup(), -    ProxyServer = proxy_server_child_spec(), -    {ok, {{one_for_all, 10, 3600}, [AdminSup, ProxyServer, ConnectionSup]}}. +    {ok, {{one_for_all, 10, 3600}, [AdminSup, ConnectionSup]}}.  %%--------------------------------------------------------------------  %%% Internal functions @@ -69,12 +68,3 @@ ssl_connection_sup() ->      Modules = [ssl_connection_sup],      Type = supervisor,      {Name, StartFunc, Restart, Shutdown, Type, Modules}. - -proxy_server_child_spec() -> -    Name = ssl_tls_dist_proxy,   -    StartFunc = {ssl_tls_dist_proxy, start_link, []}, -    Restart = permanent,  -    Shutdown = 4000, -    Modules = [ssl_tls_dist_proxy], -    Type = worker, -    {Name, StartFunc, Restart, Shutdown, Type, Modules}. diff --git a/lib/ssl/src/ssl_handshake.erl b/lib/ssl/src/ssl_handshake.erl index b1661624b5..fc4181a760 100644 --- a/lib/ssl/src/ssl_handshake.erl +++ b/lib/ssl/src/ssl_handshake.erl @@ -227,6 +227,7 @@ certificate_request(CipherSuite, CertDbHandle, CertDbRef, HashSigns, Version) ->  		   {ecdh, #'ECPrivateKey'{}} |  		   {psk, binary()} |  		   {dhe_psk, binary(), binary()} | +		   {ecdhe_psk, binary(), #'ECPrivateKey'{}} |  		   {srp, {binary(), binary()}, #srp_user{}, {HashAlgo::atom(), SignAlgo::atom()},  		   binary(), binary(), public_key:private_key()}) ->      #client_key_exchange{} | #server_key_exchange{}. @@ -264,6 +265,13 @@ key_exchange(client, _Version, {dhe_psk, Identity, PublicKey}) ->  		dh_public = PublicKey}  	       }; +key_exchange(client, _Version, {ecdhe_psk, Identity, #'ECPrivateKey'{publicKey =  ECPublicKey}}) -> +    #client_key_exchange{ +       exchange_keys = #client_ecdhe_psk_identity{ +			  identity = Identity, +			  dh_public = ECPublicKey} +      }; +  key_exchange(client, _Version, {psk_premaster_secret, PskIdentity, Secret, {_, PublicKey, _}}) ->      EncPremasterSecret =  	encrypted_premaster_secret(Secret, PublicKey), @@ -310,6 +318,16 @@ key_exchange(server, Version, {dhe_psk, PskIdentityHint, {PublicKey, _},      enc_server_key_exchange(Version, ServerEDHPSKParams,  			    HashSign, ClientRandom, ServerRandom, PrivateKey); +key_exchange(server, Version, {ecdhe_psk, PskIdentityHint, +			       #'ECPrivateKey'{publicKey =  ECPublicKey, +					       parameters = ECCurve}, +			       HashSign, ClientRandom, ServerRandom, PrivateKey}) -> +    ServerECDHEPSKParams = #server_ecdhe_psk_params{ +      hint = PskIdentityHint, +      dh_params = #server_ecdh_params{curve = ECCurve, public = ECPublicKey}}, +    enc_server_key_exchange(Version, ServerECDHEPSKParams, HashSign, +			    ClientRandom, ServerRandom, PrivateKey); +  key_exchange(server, Version, {srp, {PublicKey, _},  			       #srp_user{generator = Generator, prime = Prime,  					 salt = Salt}, @@ -532,14 +550,31 @@ premaster_secret(#server_dhe_psk_params{  		    LookupFun) ->      PremasterSecret = premaster_secret(PublicDhKey, PrivateDhKey, Params),      psk_secret(IdentityHint, LookupFun, PremasterSecret); + +premaster_secret(#server_ecdhe_psk_params{ +		    hint = IdentityHint, +                    dh_params = #server_ecdh_params{ +                                   public = ECServerPubKey}}, +		    PrivateEcDhKey, +		    LookupFun) -> +    PremasterSecret = premaster_secret(#'ECPoint'{point = ECServerPubKey}, PrivateEcDhKey), +    psk_secret(IdentityHint, LookupFun, PremasterSecret); +  premaster_secret({rsa_psk, PSKIdentity}, PSKLookup, RSAPremasterSecret) -> -    psk_secret(PSKIdentity, PSKLookup, RSAPremasterSecret). +    psk_secret(PSKIdentity, PSKLookup, RSAPremasterSecret); + +premaster_secret(#client_ecdhe_psk_identity{ +		    identity =  PSKIdentity, +		    dh_public = PublicEcDhPoint}, PrivateEcDhKey, PSKLookup) -> +    PremasterSecret = premaster_secret(#'ECPoint'{point = PublicEcDhPoint}, PrivateEcDhKey), +    psk_secret(PSKIdentity, PSKLookup, PremasterSecret).  premaster_secret(#client_dhe_psk_identity{  		    identity =  PSKIdentity,  		    dh_public = PublicDhKey}, PrivateKey, #'DHParameter'{} = Params, PSKLookup) ->      PremasterSecret = premaster_secret(PublicDhKey, PrivateKey, Params),      psk_secret(PSKIdentity, PSKLookup, PremasterSecret). +  premaster_secret(#client_psk_identity{identity = PSKIdentity}, PSKLookup) ->      psk_secret(PSKIdentity, PSKLookup);  premaster_secret({psk, PSKIdentity}, PSKLookup) -> @@ -887,6 +922,7 @@ enc_server_key_exchange(Version, Params, {HashAlgo, SignAlgo},  			    | #client_ec_diffie_hellman_public{}  			    | #client_psk_identity{}  			    | #client_dhe_psk_identity{} +			    | #client_ecdhe_psk_identity{}  			    | #client_rsa_psk_identity{}  			    | #client_srp_public{}.  %% @@ -1048,6 +1084,7 @@ dec_server_key(<<?UINT16(Len), PskIdentityHint:Len/binary, _/binary>> = KeyStruc  		       params_bin = BinMsg,  		       hashsign = HashSign,  		       signature = Signature}; +  dec_server_key(<<?UINT16(Len), IdentityHint:Len/binary,  		 ?UINT16(PLen), P:PLen/binary,  		 ?UINT16(GLen), G:GLen/binary, @@ -1062,6 +1099,22 @@ dec_server_key(<<?UINT16(Len), IdentityHint:Len/binary,  		       params_bin = BinMsg,  		       hashsign = HashSign,  		       signature = Signature}; +dec_server_key(<<?UINT16(Len), IdentityHint:Len/binary, +		 ?BYTE(?NAMED_CURVE), ?UINT16(CurveID), +		 ?BYTE(PointLen), ECPoint:PointLen/binary, +		 _/binary>> = KeyStruct, +	       ?KEY_EXCHANGE_EC_DIFFIE_HELLMAN_PSK, Version) -> +    DHParams = #server_ecdh_params{ +                  curve = {namedCurve, tls_v1:enum_to_oid(CurveID)}, +                  public = ECPoint}, +    Params = #server_ecdhe_psk_params{ +      hint = IdentityHint, +      dh_params = DHParams}, +    {BinMsg, HashSign, Signature} = dec_server_key_params(Len + 2 + PointLen + 4, KeyStruct, Version), +    #server_key_params{params = Params, +		       params_bin = BinMsg, +		       hashsign = HashSign, +		       signature = Signature};  dec_server_key(<<?UINT16(NLen), N:NLen/binary,  		 ?UINT16(GLen), G:GLen/binary,  		 ?BYTE(SLen), S:SLen/binary, @@ -1132,7 +1185,8 @@ filter_hashsigns([Suite | Suites], [{KeyExchange,_,_,_} | Algos], HashSigns, Acc        KeyExchange == ecdh_anon;        KeyExchange == srp_anon;        KeyExchange == psk; -      KeyExchange == dhe_psk -> +      KeyExchange == dhe_psk; +      KeyExchange == ecdhe_psk ->      %% In this case hashsigns is not used as the kexchange is anonaymous      filter_hashsigns(Suites, Algos, HashSigns, [Suite| Acc]). @@ -1496,6 +1550,8 @@ advertises_ec_ciphers([{ecdhe_rsa, _,_,_} | _]) ->      true;  advertises_ec_ciphers([{ecdh_anon, _,_,_} | _]) ->      true; +advertises_ec_ciphers([{ecdhe_psk, _,_,_} | _]) -> +    true;  advertises_ec_ciphers([_| Rest]) ->      advertises_ec_ciphers(Rest). @@ -1790,6 +1846,18 @@ encode_server_key(#server_dhe_psk_params{      YLen = byte_size(Y),      <<?UINT16(Len), PskIdentityHint/binary,        ?UINT16(PLen), P/binary, ?UINT16(GLen), G/binary, ?UINT16(YLen), Y/binary>>; +encode_server_key(Params = #server_ecdhe_psk_params{hint = undefined}) -> +    encode_server_key(Params#server_ecdhe_psk_params{hint = <<>>}); +encode_server_key(#server_ecdhe_psk_params{ +		     hint = PskIdentityHint, +		     dh_params = #server_ecdh_params{ +		       curve = {namedCurve, ECCurve}, public = ECPubKey}}) -> +    %%TODO: support arbitrary keys +    Len = byte_size(PskIdentityHint), +    KLen = size(ECPubKey), +    <<?UINT16(Len), PskIdentityHint/binary, +      ?BYTE(?NAMED_CURVE), ?UINT16((tls_v1:oid_to_enum(ECCurve))), +      ?BYTE(KLen), ECPubKey/binary>>;  encode_server_key(#server_srp_params{srp_n = N, srp_g = G,	srp_s = S, srp_b = B}) ->      NLen = byte_size(N),      GLen = byte_size(G), @@ -1822,6 +1890,12 @@ encode_client_key(#client_dhe_psk_identity{identity = Id, dh_public = DHPublic},      Len = byte_size(Id),      DHLen = byte_size(DHPublic),      <<?UINT16(Len), Id/binary, ?UINT16(DHLen), DHPublic/binary>>; +encode_client_key(Identity = #client_ecdhe_psk_identity{identity = undefined}, Version) -> +    encode_client_key(Identity#client_ecdhe_psk_identity{identity = <<"psk_identity">>}, Version); +encode_client_key(#client_ecdhe_psk_identity{identity = Id, dh_public = DHPublic}, _) -> +    Len = byte_size(Id), +    DHLen = byte_size(DHPublic), +    <<?UINT16(Len), Id/binary, ?BYTE(DHLen), DHPublic/binary>>;  encode_client_key(Identity = #client_rsa_psk_identity{identity = undefined}, Version) ->      encode_client_key(Identity#client_rsa_psk_identity{identity = <<"psk_identity">>}, Version);  encode_client_key(#client_rsa_psk_identity{identity = Id, exchange_keys = ExchangeKeys}, Version) -> @@ -1873,6 +1947,10 @@ dec_client_key(<<?UINT16(Len), Id:Len/binary,  		 ?UINT16(DH_YLen), DH_Y:DH_YLen/binary>>,  	       ?KEY_EXCHANGE_DHE_PSK, _) ->      #client_dhe_psk_identity{identity = Id, dh_public = DH_Y}; +dec_client_key(<<?UINT16(Len), Id:Len/binary, +		 ?BYTE(DH_YLen), DH_Y:DH_YLen/binary>>, +	       ?KEY_EXCHANGE_EC_DIFFIE_HELLMAN_PSK, _) -> +    #client_ecdhe_psk_identity{identity = Id, dh_public = DH_Y};  dec_client_key(<<?UINT16(Len), Id:Len/binary, PKEPMS/binary>>,  	       ?KEY_EXCHANGE_RSA_PSK, {3, 0}) ->      #client_rsa_psk_identity{identity = Id, @@ -2050,6 +2128,8 @@ key_exchange_alg(psk) ->      ?KEY_EXCHANGE_PSK;  key_exchange_alg(dhe_psk) ->      ?KEY_EXCHANGE_DHE_PSK; +key_exchange_alg(ecdhe_psk) -> +    ?KEY_EXCHANGE_EC_DIFFIE_HELLMAN_PSK;  key_exchange_alg(rsa_psk) ->      ?KEY_EXCHANGE_RSA_PSK;  key_exchange_alg(Alg) @@ -2308,6 +2388,7 @@ is_acceptable_hash_sign({_, ecdsa} = Algos, ecdsa, ecdsa, ecdhe_ecdsa, Supported  is_acceptable_hash_sign(_, _, _, KeyExAlgo, _) when         KeyExAlgo == psk;        KeyExAlgo == dhe_psk; +      KeyExAlgo == ecdhe_psk;        KeyExAlgo == srp_anon;        KeyExAlgo == dh_anon;        KeyExAlgo == ecdhe_anon      diff --git a/lib/ssl/src/ssl_handshake.hrl b/lib/ssl/src/ssl_handshake.hrl index 324b7dbde3..a191fcf766 100644 --- a/lib/ssl/src/ssl_handshake.hrl +++ b/lib/ssl/src/ssl_handshake.hrl @@ -133,6 +133,7 @@  -define(KEY_EXCHANGE_DIFFIE_HELLMAN, 1).  -define(KEY_EXCHANGE_EC_DIFFIE_HELLMAN, 6).  -define(KEY_EXCHANGE_PSK, 2). +-define(KEY_EXCHANGE_EC_DIFFIE_HELLMAN_PSK, 7).  -define(KEY_EXCHANGE_DHE_PSK, 3).  -define(KEY_EXCHANGE_RSA_PSK, 4).  -define(KEY_EXCHANGE_SRP, 5). @@ -162,6 +163,11 @@  	  dh_params  	 }). +-record(server_ecdhe_psk_params, { +	  hint, +	  dh_params +	 }). +  -record(server_srp_params, {  	  srp_n, %% opaque srp_N<1..2^16-1>  	  srp_g, %% opaque srp_g<1..2^16-1> @@ -254,6 +260,11 @@  	  dh_public  	 }). +-record(client_ecdhe_psk_identity, { +	  identity, +	  dh_public +	 }). +  -record(client_rsa_psk_identity, {  	  identity,  	  exchange_keys diff --git a/lib/ssl/src/ssl_tls_dist_proxy.erl b/lib/ssl/src/ssl_tls_dist_proxy.erl deleted file mode 100644 index 08947f24dd..0000000000 --- a/lib/ssl/src/ssl_tls_dist_proxy.erl +++ /dev/null @@ -1,479 +0,0 @@ -%% -%% %CopyrightBegin% -%% -%% Copyright Ericsson AB 2011-2016. All Rights Reserved. -%% -%% Licensed under the Apache License, Version 2.0 (the "License"); -%% you may not use this file except in compliance with the License. -%% You may obtain a copy of the License at -%% -%%     http://www.apache.org/licenses/LICENSE-2.0 -%% -%% Unless required by applicable law or agreed to in writing, software -%% distributed under the License is distributed on an "AS IS" BASIS, -%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -%% See the License for the specific language governing permissions and -%% limitations under the License. -%% -%% %CopyrightEnd% -%% --module(ssl_tls_dist_proxy). - - --export([listen/2, accept/2, connect/3, get_tcp_address/1]). --export([init/1, start_link/0, handle_call/3, handle_cast/2, handle_info/2,  -	 terminate/2, code_change/3, ssl_options/2]). - --include_lib("kernel/include/net_address.hrl"). - --record(state,  -	{listen, -	 accept_loop -	}). - --define(PPRE, 4). --define(PPOST, 4). - - -%%==================================================================== -%% Internal application API -%%==================================================================== - -listen(Driver, Name) -> -    gen_server:call(?MODULE, {listen, Driver, Name}, infinity). - -accept(Driver, Listen) -> -    gen_server:call(?MODULE, {accept, Driver, Listen}, infinity). - -connect(Driver, Ip, Port) -> -    gen_server:call(?MODULE, {connect, Driver, Ip, Port}, infinity). - - -do_listen(Options) -> -    {First,Last} = case application:get_env(kernel,inet_dist_listen_min) of -                        {ok,N} when is_integer(N) -> -                            case application:get_env(kernel, -                                                    inet_dist_listen_max) of -                               {ok,M} when is_integer(M) -> -                                   {N,M}; -                               _ -> -                                   {N,N} -                            end; -                        _ -> -                            {0,0} -                   end, -    do_listen(First, Last, listen_options([{backlog,128}|Options])). - -do_listen(First,Last,_) when First > Last -> -    {error,eaddrinuse}; -do_listen(First,Last,Options) -> -    case gen_tcp:listen(First, Options) of -        {error, eaddrinuse} -> -            do_listen(First+1,Last,Options); -        Other -> -            Other -    end. - -listen_options(Opts0) -> -    Opts1 = -        case application:get_env(kernel, inet_dist_use_interface) of -            {ok, Ip} -> -                [{ip, Ip} | Opts0]; -            _ -> -                Opts0 -        end, -    case application:get_env(kernel, inet_dist_listen_options) of -        {ok,ListenOpts} -> -            ListenOpts ++ Opts1; -        _ -> -            Opts1 -    end. - -connect_options(Opts) -> -    case application:get_env(kernel, inet_dist_connect_options) of -	{ok,ConnectOpts} -> -	    lists:ukeysort(1, ConnectOpts ++ Opts); -	_ -> -	    Opts -    end. - -%%==================================================================== -%% gen_server callbacks -%%==================================================================== - -start_link() -> -    gen_server:start_link({local, ?MODULE}, ?MODULE, [], []). - -init([]) -> -    process_flag(priority, max), -    {ok, #state{}}. - -handle_call({listen, Driver, Name}, _From, State) -> -    case gen_tcp:listen(0, [{active, false}, {packet,?PPRE}, {ip, loopback}]) of -	{ok, Socket} -> -	    {ok, World} = do_listen([{active, false}, binary, {packet,?PPRE}, {reuseaddr, true}, -                                     Driver:family()]), -	    {ok, TcpAddress} = get_tcp_address(Socket), -	    {ok, WorldTcpAddress} = get_tcp_address(World), -	    {_,Port} = WorldTcpAddress#net_address.address, -	    ErlEpmd = net_kernel:epmd_module(), -	    case ErlEpmd:register_node(Name, Port, Driver) of -		{ok, Creation} -> -		    {reply, {ok, {Socket, TcpAddress, Creation}}, -		     State#state{listen={Socket, World}}}; -		{error, _} = Error -> -		    {reply, Error, State} -	    end; -	Error -> -	    {reply, Error, State} -    end; - -handle_call({accept, _Driver, Listen}, {From, _}, State = #state{listen={_, World}}) -> -    Self = self(), -    ErtsPid = spawn_link(fun() -> accept_loop(Self, erts, Listen, From) end), -    WorldPid = spawn_link(fun() -> accept_loop(Self, world, World, Listen) end), -    {reply, ErtsPid, State#state{accept_loop={ErtsPid, WorldPid}}}; - -handle_call({connect, Driver, Ip, Port}, {From, _}, State) -> -    Me = self(), -    Pid = spawn_link(fun() -> setup_proxy(Driver, Ip, Port, Me) end), -    receive  -	{Pid, go_ahead, LPort} ->  -	    Res = {ok, Socket} = try_connect(LPort), -	    case gen_tcp:controlling_process(Socket, From) of -		{error, badarg} = Error -> {reply, Error, State};   % From is dead anyway. -		ok -> -		    flush_old_controller(From, Socket), -		    {reply, Res, State} -	    end; -	{Pid, Error} -> -	    {reply, Error, State} -    end; - -handle_call(_What, _From, State) -> -    {reply, ok, State}. - -handle_cast(_What, State) -> -    {noreply, State}. - -handle_info(_What, State) -> -    {noreply, State}. - -terminate(_Reason, _St) -> -    ok. - -code_change(_OldVsn, St, _Extra) -> -    {ok, St}. - -%%-------------------------------------------------------------------- -%%% Internal functions -%%-------------------------------------------------------------------- -get_tcp_address(Socket) -> -    case inet:sockname(Socket) of -	{ok, Address} -> -	    {ok, Host} = inet:gethostname(), -	    NetAddress = #net_address{ -			    address = Address, -			    host = Host, -			    protocol = proxy, -			    family = inet -			   }, -	    {ok, NetAddress}; -	{error, _} = Error -> Error -    end. - -accept_loop(Proxy, erts = Type, Listen, Extra) -> -    process_flag(priority, max), -    case gen_tcp:accept(Listen) of -	{ok, Socket} -> -	    Extra ! {accept,self(),Socket,inet,proxy}, -	    receive  -		{_Kernel, controller, Pid} -> -		    inet:setopts(Socket, [nodelay()]), -		    ok = gen_tcp:controlling_process(Socket, Pid), -		    flush_old_controller(Pid, Socket), -		    Pid ! {self(), controller}; -		{_Kernel, unsupported_protocol} -> -		    exit(unsupported_protocol) -	    end; -	{error, closed} -> -	    %% The listening socket is closed: the proxy process is -	    %% shutting down.  Exit normally, to avoid generating a -	    %% spurious error report. -	    exit(normal); -	Error -> -	    exit(Error) -    end, -    accept_loop(Proxy, Type, Listen, Extra); - -accept_loop(Proxy, world = Type, Listen, Extra) -> -    process_flag(priority, max), -    case gen_tcp:accept(Listen) of -	{ok, Socket} -> -	    Opts = get_ssl_options(server), -	    wait_for_code_server(), -	    case ssl:ssl_accept(Socket, Opts) of -		{ok, SslSocket} -> -		    PairHandler = -			spawn_link(fun() -> -					   setup_connection(SslSocket, Extra) -				   end), -		    ok = ssl:controlling_process(SslSocket, PairHandler), -		    flush_old_controller(PairHandler, SslSocket); -		{error, {options, _}} = Error -> -		    %% Bad options: that's probably our fault.  Let's log that. -		    error_logger:error_msg("Cannot accept TLS distribution connection: ~s~n", -					   [ssl:format_error(Error)]), -		    gen_tcp:close(Socket); -		_ -> -		    gen_tcp:close(Socket) -	    end; -	Error -> -	    exit(Error) -    end, -    accept_loop(Proxy, Type, Listen, Extra). - -wait_for_code_server() -> -    %% This is an ugly hack.  Upgrading a socket to TLS requires the -    %% crypto module to be loaded.  Loading the crypto module triggers -    %% its on_load function, which calls code:priv_dir/1 to find the -    %% directory where its NIF library is.  However, distribution is -    %% started earlier than the code server, so the code server is not -    %% necessarily started yet, and code:priv_dir/1 might fail because -    %% of that, if we receive an incoming connection on the -    %% distribution port early enough. -    %% -    %% If the on_load function of a module fails, the module is -    %% unloaded, and the function call that triggered loading it fails -    %% with 'undef', which is rather confusing. -    %% -    %% Thus, the ssl_tls_dist_proxy process will terminate, and be -    %% restarted by ssl_dist_sup.  However, it won't have any memory -    %% of being asked by net_kernel to listen for incoming -    %% connections.  Hence, the node will believe that it's open for -    %% distribution, but it actually isn't. -    %% -    %% So let's avoid that by waiting for the code server to start. -    case whereis(code_server) of -	undefined -> -	    timer:sleep(10), -	    wait_for_code_server(); -	Pid when is_pid(Pid) -> -	    ok -    end. - -try_connect(Port) -> -    case gen_tcp:connect({127,0,0,1}, Port, [{active, false}, {packet,?PPRE}, nodelay()]) of -	R = {ok, _S} -> -	    R; -	{error, _R} -> -	    try_connect(Port) -    end. - -setup_proxy(Driver, Ip, Port, Parent) -> -    process_flag(trap_exit, true), -    Opts = connect_options(get_ssl_options(client)), -    case ssl:connect(Ip, Port, [{active, true}, binary, {packet,?PPRE}, nodelay(), -                                Driver:family()] ++ Opts) of -	{ok, World} -> -	    {ok, ErtsL} = gen_tcp:listen(0, [{active, true}, {ip, loopback}, binary, {packet,?PPRE}]), -	    {ok, #net_address{address={_,LPort}}} = get_tcp_address(ErtsL), -	    Parent ! {self(), go_ahead, LPort}, -	    case gen_tcp:accept(ErtsL) of -		{ok, Erts} -> -		    %% gen_tcp:close(ErtsL), -		    loop_conn_setup(World, Erts); -		Err -> -		    Parent ! {self(), Err} -	    end; -	{error, {options, _}} = Err -> -	    %% Bad options: that's probably our fault.  Let's log that. -	    error_logger:error_msg("Cannot open TLS distribution connection: ~s~n", -				   [ssl:format_error(Err)]), -	    Parent ! {self(), Err}; -	Err -> -	    Parent ! {self(), Err} -    end. - - -%% we may not always want the nodelay behaviour -%% %% for performance reasons - -nodelay() -> -    case application:get_env(kernel, dist_nodelay) of -  undefined -> -      {nodelay, true}; -  {ok, true} -> -      {nodelay, true}; -  {ok, false} -> -      {nodelay, false}; -  _ -> -      {nodelay, true} -    end. - -setup_connection(World, ErtsListen) -> -    process_flag(trap_exit, true), -    {ok, TcpAddress} = get_tcp_address(ErtsListen), -    {_Addr,Port} = TcpAddress#net_address.address, -    {ok, Erts} = gen_tcp:connect({127,0,0,1}, Port, [{active, true}, binary, {packet,?PPRE}, nodelay()]), -    ssl:setopts(World, [{active,true}, {packet,?PPRE}, nodelay()]), -    loop_conn_setup(World, Erts). - -loop_conn_setup(World, Erts) -> -    receive  -	{ssl, World, Data = <<$a, _/binary>>} -> -	    gen_tcp:send(Erts, Data), -	    ssl:setopts(World, [{packet,?PPOST}, nodelay()]), -	    inet:setopts(Erts, [{packet,?PPOST}, nodelay()]), -	    loop_conn(World, Erts); -	{tcp, Erts, Data = <<$a, _/binary>>} -> -	    ssl:send(World, Data), -	    ssl:setopts(World, [{packet,?PPOST}, nodelay()]), -	    inet:setopts(Erts, [{packet,?PPOST}, nodelay()]), -	    loop_conn(World, Erts); -	{ssl, World, Data = <<_, _/binary>>} -> -	    gen_tcp:send(Erts, Data), -	    loop_conn_setup(World, Erts); -	{tcp, Erts, Data = <<_, _/binary>>} -> -	    ssl:send(World, Data), -	    loop_conn_setup(World, Erts); -	{ssl, World, Data} -> -	    gen_tcp:send(Erts, Data), -	    loop_conn_setup(World, Erts); -	{tcp, Erts, Data} -> -	    ssl:send(World, Data), -	    loop_conn_setup(World, Erts); -	{tcp_closed, Erts} -> -	    ssl:close(World); -	{ssl_closed,  World} -> -	    gen_tcp:close(Erts); -	{ssl_error, World, _} -> - -	    ssl:close(World) -    end. - -loop_conn(World, Erts) -> -    receive  -	{ssl, World, Data} -> -	    gen_tcp:send(Erts, Data), -	    loop_conn(World, Erts); -	{tcp, Erts, Data} -> -	    ssl:send(World, Data), -	    loop_conn(World, Erts); -	{tcp_closed, Erts} -> -	    ssl:close(World); -	{ssl_closed,  World} -> -	    gen_tcp:close(Erts); -	{ssl_error, World, _} -> -	    ssl:close(World) -    end. - -get_ssl_options(Type) -> -    case init:get_argument(ssl_dist_opt) of -	{ok, Args} -> -	    [{erl_dist, true} | ssl_options(Type, lists:append(Args))]; -	_ -> -	    [{erl_dist, true}] -    end. - -ssl_options(_,[]) -> -    []; -ssl_options(server, ["client_" ++ _, _Value |T]) -> -    ssl_options(server,T); -ssl_options(client, ["server_" ++ _, _Value|T]) -> -    ssl_options(client,T); -ssl_options(server, ["server_certfile", Value|T]) -> -    [{certfile, Value} | ssl_options(server,T)]; -ssl_options(client, ["client_certfile", Value | T]) -> -    [{certfile, Value} | ssl_options(client,T)]; -ssl_options(server, ["server_cacertfile", Value|T]) -> -    [{cacertfile, Value} | ssl_options(server,T)]; -ssl_options(client, ["client_cacertfile", Value|T]) -> -    [{cacertfile, Value} | ssl_options(client,T)]; -ssl_options(server, ["server_keyfile", Value|T]) -> -    [{keyfile, Value} | ssl_options(server,T)]; -ssl_options(client, ["client_keyfile", Value|T]) -> -    [{keyfile, Value} | ssl_options(client,T)]; -ssl_options(server, ["server_password", Value|T]) -> -    [{password, Value} | ssl_options(server,T)]; -ssl_options(client, ["client_password", Value|T]) -> -    [{password, Value} | ssl_options(client,T)]; -ssl_options(server, ["server_verify", Value|T]) -> -    [{verify, atomize(Value)} | ssl_options(server,T)]; -ssl_options(client, ["client_verify", Value|T]) -> -    [{verify, atomize(Value)} | ssl_options(client,T)]; -ssl_options(server, ["server_verify_fun", Value|T]) -> -    [{verify_fun, verify_fun(Value)} | ssl_options(server,T)]; -ssl_options(client, ["client_verify_fun", Value|T]) -> -    [{verify_fun, verify_fun(Value)} | ssl_options(client,T)]; -ssl_options(server, ["server_crl_check", Value|T]) -> -    [{crl_check, atomize(Value)} | ssl_options(server,T)]; -ssl_options(client, ["client_crl_check", Value|T]) -> -    [{crl_check, atomize(Value)} | ssl_options(client,T)]; -ssl_options(server, ["server_crl_cache", Value|T]) -> -    [{crl_cache, termify(Value)} | ssl_options(server,T)]; -ssl_options(client, ["client_crl_cache", Value|T]) -> -    [{crl_cache, termify(Value)} | ssl_options(client,T)]; -ssl_options(server, ["server_reuse_sessions", Value|T]) -> -    [{reuse_sessions, atomize(Value)} | ssl_options(server,T)]; -ssl_options(client, ["client_reuse_sessions", Value|T]) -> -    [{reuse_sessions, atomize(Value)} | ssl_options(client,T)]; -ssl_options(server, ["server_secure_renegotiate", Value|T]) -> -    [{secure_renegotiate, atomize(Value)} | ssl_options(server,T)]; -ssl_options(client, ["client_secure_renegotiate", Value|T]) -> -    [{secure_renegotiate, atomize(Value)} | ssl_options(client,T)]; -ssl_options(server, ["server_depth", Value|T]) -> -    [{depth, list_to_integer(Value)} | ssl_options(server,T)]; -ssl_options(client, ["client_depth", Value|T]) -> -    [{depth, list_to_integer(Value)} | ssl_options(client,T)]; -ssl_options(server, ["server_hibernate_after", Value|T]) -> -    [{hibernate_after, list_to_integer(Value)} | ssl_options(server,T)]; -ssl_options(client, ["client_hibernate_after", Value|T]) -> -    [{hibernate_after, list_to_integer(Value)} | ssl_options(client,T)]; -ssl_options(server, ["server_ciphers", Value|T]) -> -    [{ciphers, Value} | ssl_options(server,T)]; -ssl_options(client, ["client_ciphers", Value|T]) -> -    [{ciphers, Value} | ssl_options(client,T)]; -ssl_options(server, ["server_dhfile", Value|T]) -> -    [{dhfile, Value} | ssl_options(server,T)]; -ssl_options(server, ["server_fail_if_no_peer_cert", Value|T]) -> -    [{fail_if_no_peer_cert, atomize(Value)} | ssl_options(server,T)]; -ssl_options(Type, Opts) -> -    error(malformed_ssl_dist_opt, [Type, Opts]). - -atomize(List) when is_list(List) -> -    list_to_atom(List); -atomize(Atom) when is_atom(Atom) -> -    Atom. - -termify(String) when is_list(String) -> -    {ok, Tokens, _} = erl_scan:string(String ++ "."), -    {ok, Term} = erl_parse:parse_term(Tokens), -    Term. - -verify_fun(Value) -> -    case termify(Value) of -	{Mod, Func, State} when is_atom(Mod), is_atom(Func) -> -	    Fun = fun Mod:Func/3, -	    {Fun, State}; -	_ -> -	    error(malformed_ssl_dist_opt, [Value]) -    end. - -flush_old_controller(Pid, Socket) -> -    receive -	{tcp, Socket, Data} -> -	    Pid ! {tcp, Socket, Data}, -	    flush_old_controller(Pid, Socket); -	{tcp_closed, Socket} -> -	    Pid ! {tcp_closed, Socket}, -	    flush_old_controller(Pid, Socket); -	{ssl, Socket, Data} -> -	    Pid ! {ssl, Socket, Data}, -	    flush_old_controller(Pid, Socket); -	{ssl_closed, Socket} -> -	    Pid ! {ssl_closed, Socket}, -	    flush_old_controller(Pid, Socket) -    after 0 -> -	    ok -    end. diff --git a/lib/ssl/src/tls_connection.erl b/lib/ssl/src/tls_connection.erl index 010e904839..9272aebbf4 100644 --- a/lib/ssl/src/tls_connection.erl +++ b/lib/ssl/src/tls_connection.erl @@ -65,7 +65,7 @@  %% gen_statem state functions  -export([init/3, error/3, downgrade/3, %% Initiation and take down states  	 hello/3, certify/3, cipher/3, abbreviated/3, %% Handshake states  -	 connection/3]).  +	 connection/3, death_row/3]).  %% gen_statem callbacks  -export([callback_mode/0, terminate/3, code_change/4, format_status/2]). @@ -381,6 +381,13 @@ connection(Type, Event, State) ->      ssl_connection:connection(Type, Event, State, ?MODULE).  %%-------------------------------------------------------------------- +-spec death_row(gen_statem:event_type(), term(), #state{}) -> +		       gen_statem:state_function_result(). +%%-------------------------------------------------------------------- +death_row(Type, Event, State) -> +     ssl_connection:death_row(Type, Event, State, ?MODULE). + +%%--------------------------------------------------------------------  -spec downgrade(gen_statem:event_type(), term(), #state{}) ->  		       gen_statem:state_function_result().  %%-------------------------------------------------------------------- @@ -437,7 +444,7 @@ handle_info({CloseTag, Socket}, StateName,              next_event(StateName, no_record, State)      end;  handle_info(Msg, StateName, State) -> -    ssl_connection:handle_info(Msg, StateName, State). +    ssl_connection:StateName(info, Msg, State, ?MODULE).  handle_common_event(internal, #alert{} = Alert, StateName,   		    #state{negotiated_version = Version} = State) -> diff --git a/lib/ssl/test/ssl_test_lib.erl b/lib/ssl/test/ssl_test_lib.erl index 13265debb1..4e7252f469 100644 --- a/lib/ssl/test/ssl_test_lib.erl +++ b/lib/ssl/test/ssl_test_lib.erl @@ -1533,10 +1533,14 @@ is_psk_anon_suite({psk, _,_}) ->      true;  is_psk_anon_suite({dhe_psk,_,_}) ->      true; +is_psk_anon_suite({ecdhe_psk,_,_}) -> +    true;  is_psk_anon_suite({psk, _,_,_}) ->      true;  is_psk_anon_suite({dhe_psk, _,_,_}) ->      true; +is_psk_anon_suite({ecdhe_psk, _,_,_}) -> +    true;  is_psk_anon_suite(_) ->      false. | 
