From dd0baa002cf09286f5d80e046e8c8611f9e20d10 Mon Sep 17 00:00:00 2001 From: Tom Briden Date: Fri, 29 May 2015 10:40:34 +0100 Subject: Honour dist_nodelay socket option in tls_dist proxy If a plaintext cluster has nodelay=1 then so should the tls cluster; significant performance issues have been seen when nodelay isn't set --- lib/ssl/src/ssl_tls_dist_proxy.erl | 33 +++++++++++++++++++++++++-------- 1 file changed, 25 insertions(+), 8 deletions(-) (limited to 'lib/ssl/src') diff --git a/lib/ssl/src/ssl_tls_dist_proxy.erl b/lib/ssl/src/ssl_tls_dist_proxy.erl index a22af6b960..c5e473f923 100644 --- a/lib/ssl/src/ssl_tls_dist_proxy.erl +++ b/lib/ssl/src/ssl_tls_dist_proxy.erl @@ -133,6 +133,7 @@ accept_loop(Proxy, erts = Type, Listen, Extra) -> Extra ! {accept,self(),Socket,inet,proxy}, receive {_Kernel, controller, Pid} -> + inet:setopts(Socket, [nodelay()]), ok = gen_tcp:controlling_process(Socket, Pid), flush_old_controller(Pid, Socket), Pid ! {self(), controller}; @@ -166,7 +167,7 @@ accept_loop(Proxy, world = Type, Listen, Extra) -> accept_loop(Proxy, Type, Listen, Extra). try_connect(Port) -> - case gen_tcp:connect({127,0,0,1}, Port, [{active, false}, {packet,?PPRE}]) of + case gen_tcp:connect({127,0,0,1}, Port, [{active, false}, {packet,?PPRE}, nodelay()]) of R = {ok, _S} -> R; {error, _R} -> @@ -176,7 +177,7 @@ try_connect(Port) -> setup_proxy(Ip, Port, Parent) -> process_flag(trap_exit, true), Opts = get_ssl_options(client), - case ssl:connect(Ip, Port, [{active, true}, binary, {packet,?PPRE}] ++ Opts) of + case ssl:connect(Ip, Port, [{active, true}, binary, {packet,?PPRE}, nodelay()] ++ Opts) of {ok, World} -> {ok, ErtsL} = gen_tcp:listen(0, [{active, true}, {ip, {127,0,0,1}}, binary, {packet,?PPRE}]), {ok, #net_address{address={_,LPort}}} = get_tcp_address(ErtsL), @@ -192,25 +193,41 @@ setup_proxy(Ip, Port, Parent) -> Parent ! {self(), Err} end. + +%% we may not always want the nodelay behaviour +%% %% for performance reasons + +nodelay() -> + case application:get_env(kernel, dist_nodelay) of + undefined -> + {nodelay, true}; + {ok, true} -> + {nodelay, true}; + {ok, false} -> + {nodelay, false}; + _ -> + {nodelay, true} + end. + setup_connection(World, ErtsListen) -> process_flag(trap_exit, true), {ok, TcpAddress} = get_tcp_address(ErtsListen), {_Addr,Port} = TcpAddress#net_address.address, - {ok, Erts} = gen_tcp:connect({127,0,0,1}, Port, [{active, true}, binary, {packet,?PPRE}]), - ssl:setopts(World, [{active,true}, {packet,?PPRE}]), + {ok, Erts} = gen_tcp:connect({127,0,0,1}, Port, [{active, true}, binary, {packet,?PPRE}, nodelay()]), + ssl:setopts(World, [{active,true}, {packet,?PPRE}, nodelay()]), loop_conn_setup(World, Erts). loop_conn_setup(World, Erts) -> receive {ssl, World, Data = <<$a, _/binary>>} -> gen_tcp:send(Erts, Data), - ssl:setopts(World, [{packet,?PPOST}]), - inet:setopts(Erts, [{packet,?PPOST}]), + ssl:setopts(World, [{packet,?PPOST}, nodelay()]), + inet:setopts(Erts, [{packet,?PPOST}, nodelay()]), loop_conn(World, Erts); {tcp, Erts, Data = <<$a, _/binary>>} -> ssl:send(World, Data), - ssl:setopts(World, [{packet,?PPOST}]), - inet:setopts(Erts, [{packet,?PPOST}]), + ssl:setopts(World, [{packet,?PPOST}, nodelay()]), + inet:setopts(Erts, [{packet,?PPOST}, nodelay()]), loop_conn(World, Erts); {ssl, World, Data = <<_, _/binary>>} -> gen_tcp:send(Erts, Data), -- cgit v1.2.3 From e68782849000e4f7b53e94d279c1f096970640d6 Mon Sep 17 00:00:00 2001 From: Yuki Ito Date: Wed, 29 Jul 2015 20:03:26 +0900 Subject: Accept 'ECPrivateKey' as a ssl key option --- lib/ssl/src/ssl.erl | 1 + 1 file changed, 1 insertion(+) (limited to 'lib/ssl/src') diff --git a/lib/ssl/src/ssl.erl b/lib/ssl/src/ssl.erl index 9f2af73204..120e8b59ed 100644 --- a/lib/ssl/src/ssl.erl +++ b/lib/ssl/src/ssl.erl @@ -804,6 +804,7 @@ validate_option(key, {KeyType, Value}) when is_binary(Value), KeyType == dsa; %% Backwards compatibility KeyType == 'RSAPrivateKey'; KeyType == 'DSAPrivateKey'; + KeyType == 'ECPrivateKey'; KeyType == 'PrivateKeyInfo' -> {KeyType, Value}; -- cgit v1.2.3 From b2c0bef3cff85784b2ccb3b91bbea997acab6187 Mon Sep 17 00:00:00 2001 From: Ingela Anderton Andin Date: Fri, 4 Sep 2015 16:13:08 +0200 Subject: ssl: Correct return value of default session callback module ssl_session_cache:select_session/2 returned [sesionid(), #session{}] instead of #session{} as the API demands. This was wrongly compensated for in the code in one place making it look like everything was good. But the client check for unique session would always fail, potentially making the client session table grow a lot and causing long setup times. --- lib/ssl/src/ssl_session.erl | 4 ++-- lib/ssl/src/ssl_session_cache.erl | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) (limited to 'lib/ssl/src') diff --git a/lib/ssl/src/ssl_session.erl b/lib/ssl/src/ssl_session.erl index 1770faf1ff..0d6cc93a20 100644 --- a/lib/ssl/src/ssl_session.erl +++ b/lib/ssl/src/ssl_session.erl @@ -100,14 +100,14 @@ select_session([], _, _) -> no_session; select_session(Sessions, #ssl_options{ciphers = Ciphers}, OwnCert) -> IsNotResumable = - fun([_Id, Session]) -> + fun(Session) -> not (resumable(Session#session.is_resumable) andalso lists:member(Session#session.cipher_suite, Ciphers) andalso (OwnCert == Session#session.own_certificate)) end, case lists:dropwhile(IsNotResumable, Sessions) of [] -> no_session; - [[Id, _]|_] -> Id + [Session | _] -> Session#session.session_id end. is_resumable(_, _, #ssl_options{reuse_sessions = false}, _, _, _, _) -> diff --git a/lib/ssl/src/ssl_session_cache.erl b/lib/ssl/src/ssl_session_cache.erl index 11ed310477..cfc48cd935 100644 --- a/lib/ssl/src/ssl_session_cache.erl +++ b/lib/ssl/src/ssl_session_cache.erl @@ -83,7 +83,7 @@ foldl(Fun, Acc0, Cache) -> %%-------------------------------------------------------------------- select_session(Cache, PartialKey) -> ets:select(Cache, - [{{{PartialKey,'$1'}, '$2'},[],['$$']}]). + [{{{PartialKey,'_'}, '$1'},[],['$1']}]). %%-------------------------------------------------------------------- %%% Internal functions -- cgit v1.2.3 From 1b06210c16465bcb995b0a54ba1b24ef1de3c5a4 Mon Sep 17 00:00:00 2001 From: Ingela Anderton Andin Date: Tue, 25 Aug 2015 18:19:38 +0200 Subject: ssl: Improve shutdown logic Add possibility to downgrade an SSL/TLS connection to a tcp connection, and give back the socket control to a user process. Add application setting to be able to change fatal alert shutdown timeout, also shorten the default timeout. The fatal alert timeout is the number of milliseconds between sending of a fatal alert and closing the connection. Waiting a little while improves the peers chances to properly receiving the alert so it may shutdown gracefully. --- lib/ssl/src/ssl.erl | 20 ++++++++- lib/ssl/src/ssl_connection.erl | 95 +++++++++++++++++++----------------------- lib/ssl/src/tls_connection.erl | 55 +++++++++++++++++++----- 3 files changed, 107 insertions(+), 63 deletions(-) (limited to 'lib/ssl/src') diff --git a/lib/ssl/src/ssl.erl b/lib/ssl/src/ssl.erl index 120e8b59ed..f611079912 100644 --- a/lib/ssl/src/ssl.erl +++ b/lib/ssl/src/ssl.erl @@ -34,7 +34,7 @@ listen/2, transport_accept/1, transport_accept/2, ssl_accept/1, ssl_accept/2, ssl_accept/3, controlling_process/2, peername/1, peercert/1, sockname/1, - close/1, shutdown/2, recv/2, recv/3, send/2, getopts/2, setopts/2 + close/1, close/2, shutdown/2, recv/2, recv/3, send/2, getopts/2, setopts/2 ]). %% SSL/TLS protocol handling -export([cipher_suites/0, cipher_suites/1, suite_definition/1, @@ -247,10 +247,26 @@ ssl_accept(Socket, SslOptions, Timeout) when is_port(Socket) -> %% Description: Close an ssl connection %%-------------------------------------------------------------------- close(#sslsocket{pid = Pid}) when is_pid(Pid) -> - ssl_connection:close(Pid); + ssl_connection:close(Pid, {close, ?DEFAULT_TIMEOUT}); close(#sslsocket{pid = {ListenSocket, #config{transport_info={Transport,_, _, _}}}}) -> Transport:close(ListenSocket). +%%-------------------------------------------------------------------- +-spec close(#sslsocket{}, integer() | {pid(), integer()}) -> term(). +%% +%% Description: Close an ssl connection +%%-------------------------------------------------------------------- +close(#sslsocket{pid = TLSPid}, + {Pid, Timeout} = DownGrade) when is_pid(TLSPid), + is_pid(Pid), + (is_integer(Timeout) andalso Timeout > 0) or (Timeout == infinity) -> + ssl_connection:close(TLSPid, {close, DownGrade}); +close(#sslsocket{pid = TLSPid}, Timeout) when is_pid(TLSPid), + (is_integer(Timeout) andalso Timeout > 0) or (Timeout == infinity) -> + ssl_connection:close(TLSPid, {close, Timeout}); +close(#sslsocket{pid = {ListenSocket, #config{transport_info={Transport,_, _, _}}}}, _) -> + Transport:close(ListenSocket). + %%-------------------------------------------------------------------- -spec send(#sslsocket{}, iodata()) -> ok | {error, reason()}. %% diff --git a/lib/ssl/src/ssl_connection.erl b/lib/ssl/src/ssl_connection.erl index 5b754c16bc..f8afbdb41d 100644 --- a/lib/ssl/src/ssl_connection.erl +++ b/lib/ssl/src/ssl_connection.erl @@ -41,7 +41,7 @@ socket_control/4, socket_control/5]). %% User Events --export([send/2, recv/3, close/1, shutdown/2, +-export([send/2, recv/3, close/2, shutdown/2, new_user/2, get_opts/2, set_opts/2, session_info/1, peer_certificate/1, renegotiation/1, negotiated_protocol/1, prf/5, connection_information/1 @@ -171,18 +171,19 @@ connection_information(Pid) when is_pid(Pid) -> sync_send_all_state_event(Pid, connection_information). %%-------------------------------------------------------------------- --spec close(pid()) -> ok | {error, reason()}. +-spec close(pid(), {close, Timeout::integer() | + {NewController::pid(), Timeout::integer()}}) -> + ok | {ok, port()} | {error, reason()}. %% %% Description: Close an ssl connection %%-------------------------------------------------------------------- -close(ConnectionPid) -> - case sync_send_all_state_event(ConnectionPid, close) of +close(ConnectionPid, How) -> + case sync_send_all_state_event(ConnectionPid, How) of {error, closed} -> ok; Other -> Other end. - %%-------------------------------------------------------------------- -spec shutdown(pid(), atom()) -> ok | {error, reason()}. %% @@ -706,12 +707,12 @@ handle_sync_event({start, Timeout}, StartFrom, StateName, #state{role = Role, s {stop, normal, {error, Error}, State0} end; -handle_sync_event(close, _, StateName, #state{protocol_cb = Connection} = State) -> - %% Run terminate before returning - %% so that the reuseaddr inet-option will work - %% as intended. - (catch Connection:terminate(user_close, StateName, State)), - {stop, normal, ok, State#state{terminated = true}}; +handle_sync_event({close, _} = Close, _, StateName, #state{protocol_cb = Connection} = State) -> + %% Run terminate before returning so that the reuseaddr + %% inet-option and possible downgrade will work as intended. + Result = Connection:terminate(Close, StateName, State), + {stop, normal, Result, State#state{terminated = true}}; + handle_sync_event({shutdown, How0}, _, StateName, #state{transport_cb = Transport, negotiated_version = Version, @@ -901,41 +902,46 @@ terminate(_, _, #state{terminated = true}) -> %% we want to guarantee that Transport:close has been called %% when ssl:close/1 returns. ok; -terminate({shutdown, transport_closed}, StateName, #state{send_queue = SendQueue, - renegotiation = Renegotiate} = State) -> - handle_unrecv_data(StateName, State), +terminate({shutdown, transport_closed} = Reason, + _StateName, #state{send_queue = SendQueue, protocol_cb = Connection, + socket = Socket, transport_cb = Transport, + renegotiation = Renegotiate} = State) -> handle_trusted_certs_db(State), notify_senders(SendQueue), - notify_renegotiater(Renegotiate); - -terminate({shutdown, own_alert}, _StateName, #state{send_queue = SendQueue, - renegotiation = Renegotiate} = State) -> + notify_renegotiater(Renegotiate), + Connection:close(Reason, Socket, Transport, undefined, undefined); +terminate({shutdown, own_alert}, _StateName, #state{send_queue = SendQueue, protocol_cb = Connection, + socket = Socket, transport_cb = Transport, + renegotiation = Renegotiate} = State) -> handle_trusted_certs_db(State), notify_senders(SendQueue), - notify_renegotiater(Renegotiate); + notify_renegotiater(Renegotiate), + case application:get_env(ssl, alert_timeout) of + {ok, Timeout} when is_integer(Timeout) -> + Connection:close({timeout, Timeout}, Socket, Transport, undefined, undefined); + _ -> + Connection:close({timeout, ?DEFAULT_TIMEOUT}, Socket, Transport, undefined, undefined) + end; terminate(Reason, connection, #state{negotiated_version = Version, protocol_cb = Connection, - connection_states = ConnectionStates, + connection_states = ConnectionStates0, + ssl_options = #ssl_options{padding_check = Check}, transport_cb = Transport, socket = Socket, send_queue = SendQueue, renegotiation = Renegotiate} = State) -> handle_trusted_certs_db(State), notify_senders(SendQueue), notify_renegotiater(Renegotiate), - BinAlert = terminate_alert(Reason, Version, ConnectionStates), + {BinAlert, ConnectionStates} = terminate_alert(Reason, Version, ConnectionStates0), Transport:send(Socket, BinAlert), - case Connection of - tls_connection -> - tls_connection:workaround_transport_delivery_problems(Socket, Transport); - _ -> - ok - end; -terminate(_Reason, _StateName, #state{transport_cb = Transport, + Connection:close(Reason, Socket, Transport, ConnectionStates, Check); + +terminate(Reason, _StateName, #state{transport_cb = Transport, protocol_cb = Connection, socket = Socket, send_queue = SendQueue, renegotiation = Renegotiate} = State) -> handle_trusted_certs_db(State), notify_senders(SendQueue), notify_renegotiater(Renegotiate), - Transport:close(Socket). + Connection:close(Reason, Socket, Transport, undefined, undefined). format_status(normal, [_, State]) -> [{data, [{"StateData", State}]}]; @@ -1758,30 +1764,17 @@ get_timeout(#state{ssl_options=#ssl_options{hibernate_after = undefined}}) -> get_timeout(#state{ssl_options=#ssl_options{hibernate_after = HibernateAfter}}) -> HibernateAfter. -terminate_alert(Reason, Version, ConnectionStates) when Reason == normal; - Reason == user_close -> - {BinAlert, _} = ssl_alert:encode(?ALERT_REC(?WARNING, ?CLOSE_NOTIFY), - Version, ConnectionStates), - BinAlert; -terminate_alert({shutdown, _}, Version, ConnectionStates) -> - {BinAlert, _} = ssl_alert:encode(?ALERT_REC(?WARNING, ?CLOSE_NOTIFY), - Version, ConnectionStates), - BinAlert; +terminate_alert(normal, Version, ConnectionStates) -> + ssl_alert:encode(?ALERT_REC(?WARNING, ?CLOSE_NOTIFY), + Version, ConnectionStates); +terminate_alert({Reason, _}, Version, ConnectionStates) when Reason == close; + Reason == shutdown -> + ssl_alert:encode(?ALERT_REC(?WARNING, ?CLOSE_NOTIFY), + Version, ConnectionStates); terminate_alert(_, Version, ConnectionStates) -> - {BinAlert, _} = ssl_alert:encode(?ALERT_REC(?FATAL, ?INTERNAL_ERROR), - Version, ConnectionStates), - BinAlert. - -handle_unrecv_data(StateName, #state{socket = Socket, transport_cb = Transport, - protocol_cb = Connection} = State) -> - ssl_socket:setopts(Transport, Socket, [{active, false}]), - case Transport:recv(Socket, 0, 0) of - {error, closed} -> - ok; - {ok, Data} -> - Connection:handle_close_alert(Data, StateName, State) - end. + ssl_alert:encode(?ALERT_REC(?FATAL, ?INTERNAL_ERROR), + Version, ConnectionStates). handle_trusted_certs_db(#state{ssl_options = #ssl_options{cacertfile = <<>>, cacerts = []}}) -> %% No trusted certs specified diff --git a/lib/ssl/src/tls_connection.erl b/lib/ssl/src/tls_connection.erl index 7fda2377ee..3093508f61 100644 --- a/lib/ssl/src/tls_connection.erl +++ b/lib/ssl/src/tls_connection.erl @@ -54,7 +54,7 @@ %% Alert and close handling -export([send_alert/2, handle_own_alert/4, handle_close_alert/3, handle_normal_shutdown/3, handle_unexpected_message/3, - workaround_transport_delivery_problems/2, alert_user/6, alert_user/9 + close/5, alert_user/6, alert_user/9 ]). %% Data handling @@ -924,8 +924,7 @@ handle_own_alert(Alert, Version, StateName, try %% Try to tell the other side {BinMsg, _} = ssl_alert:encode(Alert, Version, ConnectionStates), - Transport:send(Socket, BinMsg), - workaround_transport_delivery_problems(Socket, Transport) + Transport:send(Socket, BinMsg) catch _:_ -> %% Can crash if we are in a uninitialized state ignore end, @@ -977,21 +976,57 @@ invalidate_session(client, Host, Port, Session) -> invalidate_session(server, _, Port, Session) -> ssl_manager:invalidate_session(Port, Session). -workaround_transport_delivery_problems(Socket, gen_tcp = Transport) -> +%% User downgrades connection +%% When downgrading an TLS connection to a transport connection +%% we must recive the close message before releasing the +%% transport socket. +close({close, {Pid, Timeout}}, Socket, Transport, ConnectionStates, Check) when is_pid(Pid) -> + ssl_socket:setopts(Transport, Socket, [{active, false}, {packet, ssl_tls}]), + case Transport:recv(Socket, 0, Timeout) of + {ok, {ssl_tls, Socket, ?ALERT, Version, Fragment}} -> + case tls_record:decode_cipher_text(#ssl_tls{type = ?ALERT, + version = Version, + fragment = Fragment + }, ConnectionStates, Check) of + {#ssl_tls{fragment = Plain}, _} -> + [Alert| _] = decode_alerts(Plain), + downgrade(Alert, Transport, Socket, Pid) + end; + {error, timeout} -> + {error, timeout}; + _ -> + {error, no_tls_close} + end; +%% User closes or recursive call! +close({close, Timeout}, Socket, Transport = gen_tcp, _,_) -> + ssl_socket:setopts(Transport, Socket, [{active, false}]), + Transport:shutdown(Socket, write), + _ = Transport:recv(Socket, 0, Timeout), + ok; +%% Peer closed socket +close({shutdown, transport_closed}, Socket, Transport = gen_tcp, ConnectionStates, Check) -> + close({close, 0}, Socket, Transport, ConnectionStates, Check); +%% We generate fatal alert +close({shutdown, own_alert}, Socket, Transport = gen_tcp, ConnectionStates, Check) -> %% Standard trick to try to make sure all %% data sent to the tcp port is really delivered to the %% peer application before tcp port is closed so that the peer will %% get the correct TLS alert message and not only a transport close. - ssl_socket:setopts(Transport, Socket, [{active, false}]), - Transport:shutdown(Socket, write), - %% Will return when other side has closed or after 30 s + %% Will return when other side has closed or after timout millisec %% e.g. we do not want to hang if something goes wrong %% with the network but we want to maximise the odds that %% peer application gets all data sent on the tcp connection. - Transport:recv(Socket, 0, 30000); -workaround_transport_delivery_problems(Socket, Transport) -> + close({close, ?DEFAULT_TIMEOUT}, Socket, Transport, ConnectionStates, Check); +%% Other +close(_, Socket, Transport, _,_) -> Transport:close(Socket). - +downgrade(#alert{description = ?CLOSE_NOTIFY}, Transport, Socket, Pid) -> + ssl_socket:setopts(Transport, Socket, [{active, false}, {packet, 0}, {mode, binary}]), + Transport:controlling_process(Socket, Pid), + {ok, Socket}; +downgrade(_, _,_,_) -> + {error, no_tls_close}. + convert_state(#state{ssl_options = Options} = State, up, "5.3.5", "5.3.6") -> State#state{ssl_options = convert_options_partial_chain(Options, up)}; convert_state(#state{ssl_options = Options} = State, down, "5.3.6", "5.3.5") -> -- cgit v1.2.3 From 82aafa295a4004bcbd549c5bb888907e3ffc0c97 Mon Sep 17 00:00:00 2001 From: Ingela Anderton Andin Date: Mon, 31 Aug 2015 16:44:16 +0200 Subject: ssl: Better timeout gaurds --- lib/ssl/src/ssl.erl | 20 ++++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) (limited to 'lib/ssl/src') diff --git a/lib/ssl/src/ssl.erl b/lib/ssl/src/ssl.erl index f611079912..03495cfd90 100644 --- a/lib/ssl/src/ssl.erl +++ b/lib/ssl/src/ssl.erl @@ -99,7 +99,8 @@ stop() -> connect(Socket, SslOptions) when is_port(Socket) -> connect(Socket, SslOptions, infinity). -connect(Socket, SslOptions0, Timeout) when is_port(Socket) -> +connect(Socket, SslOptions0, Timeout) when is_port(Socket), + (is_integer(Timeout) andalso Timeout > 0) or (Timeout == infinity) -> {Transport,_,_,_} = proplists:get_value(cb_info, SslOptions0, {gen_tcp, tcp, tcp_closed, tcp_error}), EmulatedOptions = ssl_socket:emulated_options(), @@ -125,7 +126,7 @@ connect(Socket, SslOptions0, Timeout) when is_port(Socket) -> connect(Host, Port, Options) -> connect(Host, Port, Options, infinity). -connect(Host, Port, Options, Timeout) -> +connect(Host, Port, Options, Timeout) when (is_integer(Timeout) andalso Timeout > 0) or (Timeout == infinity) -> try handle_options(Options) of {ok, Config} -> do_connect(Host,Port,Config,Timeout) @@ -175,7 +176,7 @@ transport_accept(#sslsocket{pid = {ListenSocket, #config{transport_info = {Transport,_,_, _} =CbInfo, connection_cb = ConnectionCb, ssl = SslOpts, - emulated = Tracker}}}, Timeout) -> + emulated = Tracker}}}, Timeout) when (is_integer(Timeout) andalso Timeout > 0) or (Timeout == infinity) -> case Transport:accept(ListenSocket, Timeout) of {ok, Socket} -> {ok, EmOpts} = ssl_socket:get_emulated_opts(Tracker), @@ -208,15 +209,16 @@ transport_accept(#sslsocket{pid = {ListenSocket, ssl_accept(ListenSocket) -> ssl_accept(ListenSocket, infinity). -ssl_accept(#sslsocket{} = Socket, Timeout) -> +ssl_accept(#sslsocket{} = Socket, Timeout) when (is_integer(Timeout) andalso Timeout > 0) or (Timeout == infinity) -> ssl_connection:handshake(Socket, Timeout); ssl_accept(ListenSocket, SslOptions) when is_port(ListenSocket) -> ssl_accept(ListenSocket, SslOptions, infinity). -ssl_accept(#sslsocket{} = Socket, [], Timeout) -> +ssl_accept(#sslsocket{} = Socket, [], Timeout) when (is_integer(Timeout) andalso Timeout > 0) or (Timeout == infinity)-> ssl_accept(#sslsocket{} = Socket, Timeout); -ssl_accept(#sslsocket{fd = {_, _, _, Tracker}} = Socket, SslOpts0, Timeout) -> +ssl_accept(#sslsocket{fd = {_, _, _, Tracker}} = Socket, SslOpts0, Timeout) when + (is_integer(Timeout) andalso Timeout > 0) or (Timeout == infinity)-> try {ok, EmOpts, InheritedSslOpts} = ssl_socket:get_all_opts(Tracker), SslOpts = handle_options(SslOpts0, InheritedSslOpts), @@ -224,7 +226,8 @@ ssl_accept(#sslsocket{fd = {_, _, _, Tracker}} = Socket, SslOpts0, Timeout) -> catch Error = {error, _Reason} -> Error end; -ssl_accept(Socket, SslOptions, Timeout) when is_port(Socket) -> +ssl_accept(Socket, SslOptions, Timeout) when is_port(Socket), + (is_integer(Timeout) andalso Timeout > 0) or (Timeout == infinity) -> {Transport,_,_,_} = proplists:get_value(cb_info, SslOptions, {gen_tcp, tcp, tcp_closed, tcp_error}), EmulatedOptions = ssl_socket:emulated_options(), @@ -285,7 +288,8 @@ send(#sslsocket{pid = {ListenSocket, #config{transport_info={Transport, _, _, _} %%-------------------------------------------------------------------- recv(Socket, Length) -> recv(Socket, Length, infinity). -recv(#sslsocket{pid = Pid}, Length, Timeout) when is_pid(Pid) -> +recv(#sslsocket{pid = Pid}, Length, Timeout) when is_pid(Pid), + (is_integer(Timeout) andalso Timeout > 0) or (Timeout == infinity)-> ssl_connection:recv(Pid, Length, Timeout); recv(#sslsocket{pid = {Listen, #config{transport_info = {Transport, _, _, _}}}}, _,_) when is_port(Listen)-> -- cgit v1.2.3 From fcfffa3eba6adba9d1cf2c78aca5788025f0f5df Mon Sep 17 00:00:00 2001 From: Ingela Anderton Andin Date: Fri, 26 Jun 2015 13:11:57 +0200 Subject: ssl: Prepare for release We do not want ssl_soft_upgrade_SUITE to fail, but for now we do not know the details of these changes so we use a general fallback for now. --- lib/ssl/src/ssl.appup.src | 12 ++++++++++++ 1 file changed, 12 insertions(+) (limited to 'lib/ssl/src') diff --git a/lib/ssl/src/ssl.appup.src b/lib/ssl/src/ssl.appup.src index 1476336039..c95326941c 100644 --- a/lib/ssl/src/ssl.appup.src +++ b/lib/ssl/src/ssl.appup.src @@ -1,12 +1,24 @@ %% -*- erlang -*- {"%VSN%", [ + {<<"7.0">>, [{load_module, ssl, soft_purge, soft_purge, []}, + {load_module, ssl_connection, soft_purge, soft_purge, []}, + {load_module, tls_connection, soft_purge, soft_purge, []}, + {load_module, ssl_session, soft_purge, soft_purge, []}, + {load_module, ssl_session_cache, soft_purge, soft_purge, []} + ]}, {<<"6\\..*">>, [{restart_application, ssl}]}, {<<"5\\..*">>, [{restart_application, ssl}]}, {<<"4\\..*">>, [{restart_application, ssl}]}, {<<"3\\..*">>, [{restart_application, ssl}]} ], [ + {<<"7.0">>, [{load_module, ssl, soft_purge, soft_purge, []}, + {load_module, ssl_connection, soft_purge, soft_purge, []}, + {load_module, tls_connection, soft_purge, soft_purge, []}, + {load_module, ssl_session, soft_purge, soft_purge, []}, + {load_module, ssl_session_cache, soft_purge, soft_purge, []} + ]}, {<<"6\\..*">>, [{restart_application, ssl}]}, {<<"5\\..*">>, [{restart_application, ssl}]}, {<<"4\\..*">>, [{restart_application, ssl}]}, -- cgit v1.2.3 From 4efe89d8e2ef1eca4ebccf7e753839e52b08daa4 Mon Sep 17 00:00:00 2001 From: Ingela Anderton Andin Date: Thu, 17 Sep 2015 17:26:47 +0200 Subject: ssl: Correct soft upgrade test Soft upgrade test did not work as expected due to that the upgrade frame work keeps the control of the test case process to itself, so we need a proxy process to receive messages from ssl test framework. --- lib/ssl/src/ssl.appup.src | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'lib/ssl/src') diff --git a/lib/ssl/src/ssl.appup.src b/lib/ssl/src/ssl.appup.src index c95326941c..8d5bd6f8d8 100644 --- a/lib/ssl/src/ssl.appup.src +++ b/lib/ssl/src/ssl.appup.src @@ -1,7 +1,7 @@ %% -*- erlang -*- {"%VSN%", [ - {<<"7.0">>, [{load_module, ssl, soft_purge, soft_purge, []}, + {<<"7\\.0">>, [{load_module, ssl, soft_purge, soft_purge, []}, {load_module, ssl_connection, soft_purge, soft_purge, []}, {load_module, tls_connection, soft_purge, soft_purge, []}, {load_module, ssl_session, soft_purge, soft_purge, []}, @@ -13,7 +13,7 @@ {<<"3\\..*">>, [{restart_application, ssl}]} ], [ - {<<"7.0">>, [{load_module, ssl, soft_purge, soft_purge, []}, + {<<"7\\.0">>, [{load_module, ssl, soft_purge, soft_purge, []}, {load_module, ssl_connection, soft_purge, soft_purge, []}, {load_module, tls_connection, soft_purge, soft_purge, []}, {load_module, ssl_session, soft_purge, soft_purge, []}, -- cgit v1.2.3 From b02221cf90bf7021b382e4bdfb61b08cccc2c99d Mon Sep 17 00:00:00 2001 From: Roger Lipscombe Date: Mon, 7 Sep 2015 19:53:43 +0100 Subject: Pass 'raw' options through In Erlang R16B03-1, I've been passing raw options to ssl:listen as follows, and it's been working fine: % The constants are defined elsewhere. LOpts = [{raw, ?IPPROTO_TCP, ?TCP_MAXSEG, <>} | ...], {ok, LSocket} = ssl:listen(0, LOpts) In Erlang 17.3, this fails with {option_not_a_key_value_tuple,{raw,6,2,<<64,2,0,0>>}} I originally reported this in http://erlang.org/pipermail/erlang-questions/2014-October/081226.html I need to pass this particular raw option to ssl:listen, because it needs to be applied when the socket is first opened -- between inet:open and prim_inet:listen -- it cannot be applied later by setopts. This means that it needs to be done by inet_tcp:listen/2 -- well, actually by inet:open/8, but... Otherwise it's racey -- a client could connect between prim_inet:listen and the setopts call. The MSS option is advertised in the SYN,ACK packet, and can't be changed later. --- lib/ssl/src/ssl.erl | 2 ++ 1 file changed, 2 insertions(+) (limited to 'lib/ssl/src') diff --git a/lib/ssl/src/ssl.erl b/lib/ssl/src/ssl.erl index 9f2af73204..19b091df59 100644 --- a/lib/ssl/src/ssl.erl +++ b/lib/ssl/src/ssl.erl @@ -1169,6 +1169,8 @@ assert_proplist([]) -> assert_proplist([{Key,_} | Rest]) when is_atom(Key) -> assert_proplist(Rest); %% Handle exceptions +assert_proplist([{raw,_,_,_} | Rest]) -> + assert_proplist(Rest); assert_proplist([inet | Rest]) -> assert_proplist(Rest); assert_proplist([inet6 | Rest]) -> -- cgit v1.2.3 From 1c639b17643da67905717b05d1af66fe6e3b7c37 Mon Sep 17 00:00:00 2001 From: Hans Bolinder Date: Tue, 29 Sep 2015 10:12:12 +0200 Subject: Update SSL Record field types have been modified due to commit 8ce35b2: "Take out automatic insertion of 'undefined' from typed record fields". --- lib/ssl/src/ssl_connection.hrl | 23 ++++++++++++----------- lib/ssl/src/ssl_internal.hrl | 14 +++++++------- 2 files changed, 19 insertions(+), 18 deletions(-) (limited to 'lib/ssl/src') diff --git a/lib/ssl/src/ssl_connection.hrl b/lib/ssl/src/ssl_connection.hrl index 9a58f2b8f7..bb41ef2b62 100644 --- a/lib/ssl/src/ssl_connection.hrl +++ b/lib/ssl/src/ssl_connection.hrl @@ -48,27 +48,28 @@ socket_options :: #socket_options{}, connection_states :: #connection_states{} | secret_printout(), protocol_buffers :: term() | secret_printout() , %% #protocol_buffers{} from tls_record.hrl or dtls_recor.hrl - tls_handshake_history :: ssl_handshake:ssl_handshake_history() | secret_printout(), - cert_db :: reference(), + tls_handshake_history :: ssl_handshake:ssl_handshake_history() | secret_printout() + | 'undefined', + cert_db :: reference() | 'undefined', session :: #session{} | secret_printout(), session_cache :: db_handle(), session_cache_cb :: atom(), crl_db :: term(), - negotiated_version :: ssl_record:ssl_version(), + negotiated_version :: ssl_record:ssl_version() | 'undefined', client_certificate_requested = false :: boolean(), key_algorithm :: ssl_cipher:key_algo(), hashsign_algorithm = {undefined, undefined}, cert_hashsign_algorithm, - public_key_info :: ssl_handshake:public_key_info(), - private_key :: public_key:private_key() | secret_printout(), + public_key_info :: ssl_handshake:public_key_info() | 'undefined', + private_key :: public_key:private_key() | secret_printout() | 'undefined', diffie_hellman_params:: #'DHParameter'{} | undefined | secret_printout(), diffie_hellman_keys :: {PublicKey :: binary(), PrivateKey :: binary()} | #'ECPrivateKey'{} | undefined | secret_printout(), - psk_identity :: binary(), % server psk identity hint - srp_params :: #srp_user{} | secret_printout(), - srp_keys ::{PublicKey :: binary(), PrivateKey :: binary()} | secret_printout(), - premaster_secret :: binary() | secret_printout() , + psk_identity :: binary() | 'undefined', % server psk identity hint + srp_params :: #srp_user{} | secret_printout() | 'undefined', + srp_keys ::{PublicKey :: binary(), PrivateKey :: binary()} | secret_printout() | 'undefined', + premaster_secret :: binary() | secret_printout() | 'undefined', file_ref_db :: db_handle(), - cert_db_ref :: certdb_ref(), + cert_db_ref :: certdb_ref() | 'undefined', bytes_to_read :: undefined | integer(), %% bytes to read in passive mode user_data_buffer :: undefined | binary() | secret_printout(), renegotiation :: undefined | {boolean(), From::term() | internal | peer}, @@ -81,7 +82,7 @@ expecting_finished = false ::boolean(), negotiated_protocol = undefined :: undefined | binary(), client_ecc, % {Curves, PointFmt} - tracker :: pid(), %% Tracker process for listen socket + tracker :: pid() | 'undefined', %% Tracker process for listen socket sni_hostname = undefined }). diff --git a/lib/ssl/src/ssl_internal.hrl b/lib/ssl/src/ssl_internal.hrl index 3851b2bc6e..007723c982 100644 --- a/lib/ssl/src/ssl_internal.hrl +++ b/lib/ssl/src/ssl_internal.hrl @@ -90,16 +90,16 @@ validate_extensions_fun, depth :: integer(), certfile :: binary(), - cert :: public_key:der_encoded() | secret_printout(), + cert :: public_key:der_encoded() | secret_printout() | 'undefined', keyfile :: binary(), - key :: {'RSAPrivateKey' | 'DSAPrivateKey' | 'ECPrivateKey' | 'PrivateKeyInfo', public_key:der_encoded()} | secret_printout(), - password :: string() | secret_printout(), - cacerts :: [public_key:der_encoded()] | secret_printout(), + key :: {'RSAPrivateKey' | 'DSAPrivateKey' | 'ECPrivateKey' | 'PrivateKeyInfo', public_key:der_encoded()} | secret_printout() | 'undefined', + password :: string() | secret_printout() | 'undefined', + cacerts :: [public_key:der_encoded()] | secret_printout() | 'undefined', cacertfile :: binary(), dh :: public_key:der_encoded() | secret_printout(), - dhfile :: binary() | secret_printout(), + dhfile :: binary() | secret_printout() | 'undefined', user_lookup_fun, % server option, fun to lookup the user - psk_identity :: binary() | secret_printout() , + psk_identity :: binary() | secret_printout() | 'undefined', srp_identity, % client option {User, Password} ciphers, % %% Local policy for the server if it want's to reuse the session @@ -115,7 +115,7 @@ %% undefined if not hibernating, or number of ms of %% inactivity after which ssl_connection will go into %% hibernation - hibernate_after :: boolean(), + hibernate_after :: boolean() | 'undefined', %% This option should only be set to true by inet_tls_dist erl_dist = false :: boolean(), alpn_advertised_protocols = undefined :: [binary()] | undefined , -- cgit v1.2.3 From e30f65485aec78c6bd65e25e5a185100b3f8f042 Mon Sep 17 00:00:00 2001 From: Magnus Henoch Date: Tue, 20 Oct 2015 16:25:13 +0100 Subject: Adjust shutdown strategies for distribution over TLS Change ssl_dist_sup to be considered as a supervisor with infinite shutdown time. Change the ssl_connection_dist instance of tls_connection_sup to have infinite shutdown time. This avoids spurious error messages when shutting down a node that uses distribution over TLS. --- lib/ssl/src/inet_tls_dist.erl | 2 +- lib/ssl/src/ssl_dist_sup.erl | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) (limited to 'lib/ssl/src') diff --git a/lib/ssl/src/inet_tls_dist.erl b/lib/ssl/src/inet_tls_dist.erl index 7367b5c224..a633df1558 100644 --- a/lib/ssl/src/inet_tls_dist.erl +++ b/lib/ssl/src/inet_tls_dist.erl @@ -29,7 +29,7 @@ childspecs() -> {ok, [{ssl_dist_sup,{ssl_dist_sup, start_link, []}, - permanent, 2000, worker, [ssl_dist_sup]}]}. + permanent, infinity, supervisor, [ssl_dist_sup]}]}. select(Node) -> case split_node(atom_to_list(Node), $@, []) of diff --git a/lib/ssl/src/ssl_dist_sup.erl b/lib/ssl/src/ssl_dist_sup.erl index 58efeaf892..0c7fc77db7 100644 --- a/lib/ssl/src/ssl_dist_sup.erl +++ b/lib/ssl/src/ssl_dist_sup.erl @@ -69,7 +69,7 @@ connection_manager_child_spec() -> Name = ssl_connection_dist, StartFunc = {tls_connection_sup, start_link_dist, []}, Restart = permanent, - Shutdown = 4000, + Shutdown = infinity, Modules = [tls_connection_sup], Type = supervisor, {Name, StartFunc, Restart, Shutdown, Type, Modules}. -- cgit v1.2.3 From 120975c4fcb57ecd14031ac046f483e56a3daa4d Mon Sep 17 00:00:00 2001 From: Pawel Pikula Date: Thu, 22 Oct 2015 21:02:52 +0200 Subject: fix incorrect number of seconds in 24h macro The previous commit - 7b93f5d8a224a0a076a420294c95a666a763ee60 fixed the macro only in one place. --- lib/ssl/src/ssl_internal.hrl | 3 +++ lib/ssl/src/ssl_manager.erl | 2 -- lib/ssl/src/ssl_session.erl | 2 -- 3 files changed, 3 insertions(+), 4 deletions(-) (limited to 'lib/ssl/src') diff --git a/lib/ssl/src/ssl_internal.hrl b/lib/ssl/src/ssl_internal.hrl index 3851b2bc6e..8c7ed9c0d1 100644 --- a/lib/ssl/src/ssl_internal.hrl +++ b/lib/ssl/src/ssl_internal.hrl @@ -78,6 +78,9 @@ -define(ALL_DATAGRAM_SUPPORTED_VERSIONS, ['dtlsv1.2', dtlsv1]). -define(MIN_DATAGRAM_SUPPORTED_VERSIONS, ['dtlsv1.2', dtlsv1]). +-define('24H_in_msec', 86400000). +-define('24H_in_sec', 86400). + -record(ssl_options, { protocol :: tls | dtls, versions :: [ssl_record:ssl_version()], %% ssl_record:atom_version() in API diff --git a/lib/ssl/src/ssl_manager.erl b/lib/ssl/src/ssl_manager.erl index 2e05ba5aa5..cc15678f23 100644 --- a/lib/ssl/src/ssl_manager.erl +++ b/lib/ssl/src/ssl_manager.erl @@ -57,8 +57,6 @@ clear_pem_cache }). --define('24H_in_msec', 86400000). --define('24H_in_sec', 86400). -define(GEN_UNIQUE_ID_MAX_TRIES, 10). -define(SESSION_VALIDATION_INTERVAL, 60000). -define(CLEAR_PEM_CACHE, 120000). diff --git a/lib/ssl/src/ssl_session.erl b/lib/ssl/src/ssl_session.erl index 0d6cc93a20..1849a05314 100644 --- a/lib/ssl/src/ssl_session.erl +++ b/lib/ssl/src/ssl_session.erl @@ -31,8 +31,6 @@ %% Internal application API -export([is_new/2, client_id/4, server_id/6, valid_session/2]). --define('24H_in_sec', 8640). - -type seconds() :: integer(). %%-------------------------------------------------------------------- -- cgit v1.2.3 From 902ed3fe77d674fdbed3ee60ee613861ce2f1943 Mon Sep 17 00:00:00 2001 From: Magnus Henoch Date: Fri, 23 Oct 2015 17:16:38 +0100 Subject: In ssl_tls_dist_proxy, pass along EPMD registration errors The duplicate_name error returned from erl_epmd:register_node elicits a particularly precise error message from net_kernel, so let's pass it along to our caller. Not doing this for the other things that could go wrong here, since for those having the line number will likely aid debugging. --- lib/ssl/src/ssl_tls_dist_proxy.erl | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) (limited to 'lib/ssl/src') diff --git a/lib/ssl/src/ssl_tls_dist_proxy.erl b/lib/ssl/src/ssl_tls_dist_proxy.erl index 273d3b5521..ce742948b7 100644 --- a/lib/ssl/src/ssl_tls_dist_proxy.erl +++ b/lib/ssl/src/ssl_tls_dist_proxy.erl @@ -66,9 +66,13 @@ handle_call({listen, Name}, _From, State) -> {ok, TcpAddress} = get_tcp_address(Socket), {ok, WorldTcpAddress} = get_tcp_address(World), {_,Port} = WorldTcpAddress#net_address.address, - {ok, Creation} = erl_epmd:register_node(Name, Port), - {reply, {ok, {Socket, TcpAddress, Creation}}, - State#state{listen={Socket, World}}}; + case erl_epmd:register_node(Name, Port) of + {ok, Creation} -> + {reply, {ok, {Socket, TcpAddress, Creation}}, + State#state{listen={Socket, World}}}; + {error, _} = Error -> + {reply, Error, State} + end; Error -> {reply, Error, State} end; -- cgit v1.2.3 From 3dc5591d92876070e004242ff875b3f04ff92c34 Mon Sep 17 00:00:00 2001 From: Magnus Henoch Date: Wed, 28 Oct 2015 18:42:59 +0000 Subject: Avoid crash for SSL connections with nonexistent keyfile Starting an SSL connection with a nonexistent keyfile will obviously return an error: > ssl:connect("www.google.com", 443, [{keyfile, "nonexistent"}]). {error,{options,{keyfile,"nonexistent",{error,enoent}}}} But it also generates an error report with the following backtrace: ** Reason for termination = ** {badarg,[{ets,select_delete, [undefined,[{{{undefined,'_','_'},'_'},[],[true]}]], []}, {ets,match_delete,2,[{file,"ets.erl"},{line,700}]}, {ssl_pkix_db,remove_certs,2,[{file,"ssl_pkix_db.erl"},{line,243}]}, {ssl_connection,terminate,3, [{file,"ssl_connection.erl"},{line,941}]}, {tls_connection,terminate,3, [{file,"tls_connection.erl"},{line,335}]}, {gen_fsm,terminate,7,[{file,"gen_fsm.erl"},{line,610}]}, {gen_fsm,handle_msg,7,[{file,"gen_fsm.erl"},{line,532}]}, {proc_lib,init_p_do_apply,3,[{file,"proc_lib.erl"},{line,240}]}]} This happens because the ssl_connection process receives its cert_db while handling the {start, Timeout} message, but if the handshake fails, the cert_db will never be inserted into the state data, and the terminate function will use 'undefined' as an ETS table name. Avoid this by checking for 'undefined' in the handle_trusted_certs_db function. --- lib/ssl/src/ssl_connection.erl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'lib/ssl/src') diff --git a/lib/ssl/src/ssl_connection.erl b/lib/ssl/src/ssl_connection.erl index f8afbdb41d..12a56df69f 100644 --- a/lib/ssl/src/ssl_connection.erl +++ b/lib/ssl/src/ssl_connection.erl @@ -1781,7 +1781,7 @@ handle_trusted_certs_db(#state{ssl_options = #ssl_options{cacertfile = <<>>, cac ok; handle_trusted_certs_db(#state{cert_db_ref = Ref, cert_db = CertDb, - ssl_options = #ssl_options{cacertfile = <<>>}}) -> + ssl_options = #ssl_options{cacertfile = <<>>}}) when CertDb =/= undefined -> %% Certs provided as DER directly can not be shared %% with other connections and it is safe to delete them when the connection ends. ssl_pkix_db:remove_trusted_certs(Ref, CertDb); -- cgit v1.2.3 From fcf198456fcd8e80c78b58c6616f7cf1266406cc Mon Sep 17 00:00:00 2001 From: Magnus Henoch Date: Wed, 28 Oct 2015 17:06:10 +0000 Subject: Report bad options for TLS distribution connections If ssl:ssl_accept/2 returns an error related to options, it's most likely something we want to log. In particular, if the specified certificate file doesn't exist, this is where the error ends up, so we shouldn't just throw the error away. --- lib/ssl/src/ssl_tls_dist_proxy.erl | 5 +++++ 1 file changed, 5 insertions(+) (limited to 'lib/ssl/src') diff --git a/lib/ssl/src/ssl_tls_dist_proxy.erl b/lib/ssl/src/ssl_tls_dist_proxy.erl index a22af6b960..e7f7fa96a1 100644 --- a/lib/ssl/src/ssl_tls_dist_proxy.erl +++ b/lib/ssl/src/ssl_tls_dist_proxy.erl @@ -157,6 +157,11 @@ accept_loop(Proxy, world = Type, Listen, Extra) -> end), ok = ssl:controlling_process(SslSocket, PairHandler), flush_old_controller(PairHandler, SslSocket); + {error, {options, _}} = Error -> + %% Bad options: that's probably our fault. Let's log that. + error_logger:error_msg("Cannot accept TLS distribution connection: ~s~n", + [ssl:format_error(Error)]), + gen_tcp:close(Socket); _ -> gen_tcp:close(Socket) end; -- cgit v1.2.3 From 71390889787b571aa3f13e683d9e2349415f5808 Mon Sep 17 00:00:00 2001 From: Ingela Anderton Andin Date: Fri, 13 Nov 2015 15:07:26 +0100 Subject: ssl: Client should send the hello message in the lowest version it is willing to support Refactor highest_protocol_version so that code is symmetrical with lowest_protocol_version. For clarity and possible future use cases of highest_protocol_version/2 --- lib/ssl/src/tls_connection.erl | 3 +- lib/ssl/src/tls_record.erl | 62 ++++++++++++++++++++++++++++++++++-------- 2 files changed, 52 insertions(+), 13 deletions(-) (limited to 'lib/ssl/src') diff --git a/lib/ssl/src/tls_connection.erl b/lib/ssl/src/tls_connection.erl index 3093508f61..a468c131ce 100644 --- a/lib/ssl/src/tls_connection.erl +++ b/lib/ssl/src/tls_connection.erl @@ -168,9 +168,10 @@ hello(start, #state{host = Host, port = Port, role = client, Cache, CacheCb, Renegotiation, Cert), Version = Hello#client_hello.client_version, + HelloVersion = tls_record:lowest_protocol_version(SslOpts#ssl_options.versions), Handshake0 = ssl_handshake:init_handshake_history(), {BinMsg, ConnectionStates, Handshake} = - encode_handshake(Hello, Version, ConnectionStates0, Handshake0), + encode_handshake(Hello, HelloVersion, ConnectionStates0, Handshake0), Transport:send(Socket, BinMsg), State1 = State0#state{connection_states = ConnectionStates, negotiated_version = Version, %% Requested version diff --git a/lib/ssl/src/tls_record.erl b/lib/ssl/src/tls_record.erl index aa524f0225..1e266ed424 100644 --- a/lib/ssl/src/tls_record.erl +++ b/lib/ssl/src/tls_record.erl @@ -41,8 +41,9 @@ -export([encode_plain_text/4]). %% Protocol version handling --export([protocol_version/1, lowest_protocol_version/2, - highest_protocol_version/1, is_higher/2, supported_protocol_versions/0, +-export([protocol_version/1, lowest_protocol_version/1, lowest_protocol_version/2, + highest_protocol_version/1, highest_protocol_version/2, + is_higher/2, supported_protocol_versions/0, is_acceptable_version/1, is_acceptable_version/2]). -export_type([tls_version/0, tls_atom_version/0]). @@ -257,6 +258,18 @@ lowest_protocol_version(Version = {M,_}, Version; lowest_protocol_version(_,Version) -> Version. + +%%-------------------------------------------------------------------- +-spec lowest_protocol_version([tls_version()]) -> tls_version(). +%% +%% Description: Lowest protocol version present in a list +%%-------------------------------------------------------------------- +lowest_protocol_version([]) -> + lowest_protocol_version(); +lowest_protocol_version(Versions) -> + [Ver | Vers] = Versions, + lowest_list_protocol_version(Ver, Vers). + %%-------------------------------------------------------------------- -spec highest_protocol_version([tls_version()]) -> tls_version(). %% @@ -266,19 +279,29 @@ highest_protocol_version([]) -> highest_protocol_version(); highest_protocol_version(Versions) -> [Ver | Vers] = Versions, - highest_protocol_version(Ver, Vers). + highest_list_protocol_version(Ver, Vers). -highest_protocol_version(Version, []) -> +%%-------------------------------------------------------------------- +-spec highest_protocol_version(tls_version(), tls_version()) -> tls_version(). +%% +%% Description: Highest protocol version of two given versions +%%-------------------------------------------------------------------- +highest_protocol_version(Version = {M, N}, {M, O}) when N > O -> + Version; +highest_protocol_version({M, _}, + Version = {M, _}) -> Version; -highest_protocol_version(Version = {N, M}, [{N, O} | Rest]) when M > O -> - highest_protocol_version(Version, Rest); -highest_protocol_version({M, _}, [Version = {M, _} | Rest]) -> - highest_protocol_version(Version, Rest); -highest_protocol_version(Version = {M,_}, [{N,_} | Rest]) when M > N -> - highest_protocol_version(Version, Rest); -highest_protocol_version(_, [Version | Rest]) -> - highest_protocol_version(Version, Rest). +highest_protocol_version(Version = {M,_}, + {N, _}) when M > N -> + Version; +highest_protocol_version(_,Version) -> + Version. +%%-------------------------------------------------------------------- +-spec is_higher(V1 :: tls_version(), V2::tls_version()) -> tls_version(). +%% +%% Description: Is V1 > V2 +%%-------------------------------------------------------------------- is_higher({M, N}, {M, O}) when N > O -> true; is_higher({M, _}, {N, _}) when M > N -> @@ -352,6 +375,17 @@ is_acceptable_version(_,_) -> %%-------------------------------------------------------------------- %%% Internal functions %%-------------------------------------------------------------------- + +lowest_list_protocol_version(Ver, []) -> + Ver; +lowest_list_protocol_version(Ver1, [Ver2 | Rest]) -> + lowest_list_protocol_version(lowest_protocol_version(Ver1, Ver2), Rest). + +highest_list_protocol_version(Ver, []) -> + Ver; +highest_list_protocol_version(Ver1, [Ver2 | Rest]) -> + highest_list_protocol_version(highest_protocol_version(Ver1, Ver2), Rest). + encode_tls_cipher_text(Type, {MajVer, MinVer}, Fragment) -> Length = erlang:iolist_size(Fragment), [<>, Fragment]. @@ -370,6 +404,10 @@ mac_hash({3, N} = Version, MacAlg, MacSecret, SeqNo, Type, Length, Fragment) highest_protocol_version() -> highest_protocol_version(supported_protocol_versions()). +lowest_protocol_version() -> + lowest_protocol_version(supported_protocol_versions()). + + sufficient_tlsv1_2_crypto_support() -> CryptoSupport = crypto:supports(), proplists:get_bool(sha256, proplists:get_value(hashs, CryptoSupport)). -- cgit v1.2.3 From 5a2bd5648ced26ed5a8ba8009603908f5226538d Mon Sep 17 00:00:00 2001 From: Tom Briden Date: Thu, 26 Mar 2015 13:22:19 +0000 Subject: TLS Dist: Use inet_dist_ options The inet_dist_ options, such as min/max port numbers aren't used with TLS distribution. This commits uses those settings in the same way as they're used in inet_tcp_dist.erl --- lib/ssl/src/ssl_tls_dist_proxy.erl | 62 ++++++++++++++++++++++++++++++++------ 1 file changed, 52 insertions(+), 10 deletions(-) (limited to 'lib/ssl/src') diff --git a/lib/ssl/src/ssl_tls_dist_proxy.erl b/lib/ssl/src/ssl_tls_dist_proxy.erl index 273d3b5521..3a0990e8b6 100644 --- a/lib/ssl/src/ssl_tls_dist_proxy.erl +++ b/lib/ssl/src/ssl_tls_dist_proxy.erl @@ -48,6 +48,48 @@ accept(Listen) -> connect(Ip, Port) -> gen_server:call(?MODULE, {connect, 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} -> + erlang:display({inet_dist_listen_options, ListenOpts}), + ListenOpts ++ Opts1; + _ -> + Opts1 + end. + %%==================================================================== %% gen_server callbacks %%==================================================================== @@ -61,16 +103,16 @@ init([]) -> handle_call({listen, Name}, _From, State) -> case gen_tcp:listen(0, [{active, false}, {packet,?PPRE}]) of - {ok, Socket} -> - {ok, World} = gen_tcp:listen(0, [{active, false}, binary, {packet,?PPRE}]), - {ok, TcpAddress} = get_tcp_address(Socket), - {ok, WorldTcpAddress} = get_tcp_address(World), - {_,Port} = WorldTcpAddress#net_address.address, - {ok, Creation} = erl_epmd:register_node(Name, Port), - {reply, {ok, {Socket, TcpAddress, Creation}}, - State#state{listen={Socket, World}}}; - Error -> - {reply, Error, State} + {ok, Socket} -> + {ok, World} = do_listen([{active, false}, binary, {packet,?PPRE}, {reuseaddr, true}]), + {ok, TcpAddress} = get_tcp_address(Socket), + {ok, WorldTcpAddress} = get_tcp_address(World), + {_,Port} = WorldTcpAddress#net_address.address, + {ok, Creation} = erl_epmd:register_node(Name, Port), + {reply, {ok, {Socket, TcpAddress, Creation}}, + State#state{listen={Socket, World}}}; + Error -> + {reply, Error, State} end; handle_call({accept, Listen}, {From, _}, State = #state{listen={_, World}}) -> -- cgit v1.2.3 From 991d3fa399154b6166a4974406fc2bdda02a2668 Mon Sep 17 00:00:00 2001 From: Magnus Henoch Date: Tue, 17 Nov 2015 13:40:54 +0000 Subject: Test socket listen options for TLS distribution Add test that checks that the option inet_dist_listen_options is used when starting a node with TLS distribution. This test was adapted from inet_dist_options_options in erl_distribution_SUITE. --- lib/ssl/src/ssl_tls_dist_proxy.erl | 1 - 1 file changed, 1 deletion(-) (limited to 'lib/ssl/src') diff --git a/lib/ssl/src/ssl_tls_dist_proxy.erl b/lib/ssl/src/ssl_tls_dist_proxy.erl index 3a0990e8b6..c8f29cee4b 100644 --- a/lib/ssl/src/ssl_tls_dist_proxy.erl +++ b/lib/ssl/src/ssl_tls_dist_proxy.erl @@ -84,7 +84,6 @@ listen_options(Opts0) -> end, case application:get_env(kernel, inet_dist_listen_options) of {ok,ListenOpts} -> - erlang:display({inet_dist_listen_options, ListenOpts}), ListenOpts ++ Opts1; _ -> Opts1 -- cgit v1.2.3 From 5e95fdff6589432327e38415fc9c5545231e7961 Mon Sep 17 00:00:00 2001 From: Ingela Anderton Andin Date: Fri, 13 Nov 2015 21:56:30 +0100 Subject: ssl: Measure elapsed time with erlang:monotonic_time --- lib/ssl/src/dtls_connection.erl | 2 +- lib/ssl/src/ssl_connection.erl | 2 +- lib/ssl/src/ssl_manager.erl | 8 ++++---- lib/ssl/src/ssl_session.erl | 5 +++-- 4 files changed, 9 insertions(+), 8 deletions(-) (limited to 'lib/ssl/src') diff --git a/lib/ssl/src/dtls_connection.erl b/lib/ssl/src/dtls_connection.erl index 78662e0ea2..153d3fef48 100644 --- a/lib/ssl/src/dtls_connection.erl +++ b/lib/ssl/src/dtls_connection.erl @@ -145,7 +145,7 @@ init([Role, Host, Port, Socket, {SSLOpts0, _} = Options, User, CbInfo]) -> process_flag(trap_exit, true), State0 = initial_state(Role, Host, Port, Socket, Options, User, CbInfo), Handshake = ssl_handshake:init_handshake_history(), - TimeStamp = calendar:datetime_to_gregorian_seconds({date(), time()}), + TimeStamp = erlang:monotonic_time(), try ssl_config:init(SSLOpts0, Role) of {ok, Ref, CertDbHandle, FileRefHandle, CacheHandle, CRLDbInfo, OwnCert, Key, DHParams} -> Session = State0#state.session, diff --git a/lib/ssl/src/ssl_connection.erl b/lib/ssl/src/ssl_connection.erl index 12a56df69f..241871dc38 100644 --- a/lib/ssl/src/ssl_connection.erl +++ b/lib/ssl/src/ssl_connection.erl @@ -974,7 +974,7 @@ ssl_config(Opts, Role, State) -> {ok, Ref, CertDbHandle, FileRefHandle, CacheHandle, CRLDbInfo, OwnCert, Key, DHParams} = ssl_config:init(Opts, Role), Handshake = ssl_handshake:init_handshake_history(), - TimeStamp = calendar:datetime_to_gregorian_seconds({date(), time()}), + TimeStamp = erlang:monotonic_time(), Session = State#state.session, State#state{tls_handshake_history = Handshake, session = Session#session{own_certificate = OwnCert, diff --git a/lib/ssl/src/ssl_manager.erl b/lib/ssl/src/ssl_manager.erl index cc15678f23..9e535104f7 100644 --- a/lib/ssl/src/ssl_manager.erl +++ b/lib/ssl/src/ssl_manager.erl @@ -320,7 +320,7 @@ handle_call({unconditionally_clear_pem_cache, _},_, #state{certificate_db = [_,_ handle_cast({register_session, Host, Port, Session}, #state{session_cache_client = Cache, session_cache_cb = CacheCb} = State) -> - TimeStamp = calendar:datetime_to_gregorian_seconds({date(), time()}), + TimeStamp = erlang:monotonic_time(), NewSession = Session#session{time_stamp = TimeStamp}, case CacheCb:select_session(Cache, {Host, Port}) of @@ -335,7 +335,7 @@ handle_cast({register_session, Host, Port, Session}, handle_cast({register_session, Port, Session}, #state{session_cache_server = Cache, session_cache_cb = CacheCb} = State) -> - TimeStamp = calendar:datetime_to_gregorian_seconds({date(), time()}), + TimeStamp = erlang:monotonic_time(), NewSession = Session#session{time_stamp = TimeStamp}, CacheCb:update(Cache, {Port, NewSession#session.session_id}, NewSession), {noreply, State}; @@ -502,7 +502,7 @@ invalidate_session(Cache, CacheCb, Key, Session, #state{last_delay_timer = LastT #session{is_resumable = new} -> CacheCb:delete(Cache, Key), {noreply, State}; - _ -> + _ -> %% When a registered session is invalidated we need to wait a while before deleting %% it as there might be pending connections that rightfully needs to look %% up the session data but new connections should not get to use this session. @@ -530,7 +530,7 @@ new_id(Port, Tries, Cache, CacheCb) -> Id = crypto:rand_bytes(?NUM_OF_SESSION_ID_BYTES), case CacheCb:lookup(Cache, {Port, Id}) of undefined -> - Now = calendar:datetime_to_gregorian_seconds({date(), time()}), + Now = erlang:monotonic_time(), %% New sessions can not be set to resumable %% until handshake is compleate and the %% other session values are set. diff --git a/lib/ssl/src/ssl_session.erl b/lib/ssl/src/ssl_session.erl index 1849a05314..0ee8a096e9 100644 --- a/lib/ssl/src/ssl_session.erl +++ b/lib/ssl/src/ssl_session.erl @@ -66,8 +66,9 @@ client_id(ClientInfo, Cache, CacheCb, OwnCert) -> %% Description: Check that the session has not expired %%-------------------------------------------------------------------- valid_session(#session{time_stamp = TimeStamp}, LifeTime) -> - Now = calendar:datetime_to_gregorian_seconds({date(), time()}), - Now - TimeStamp < LifeTime. + Now = erlang:monotonic_time(), + Lived = erlang:convert_time_unit(Now-TimeStamp, native, seconds), + Lived < LifeTime. server_id(Port, <<>>, _SslOpts, _Cert, _, _) -> {ssl_manager:new_session_id(Port), undefined}; -- cgit v1.2.3 From 42b8a29dbae1d626f32bc16dd81a129caf741138 Mon Sep 17 00:00:00 2001 From: Ingela Anderton Andin Date: Mon, 16 Nov 2015 21:58:36 +0100 Subject: ssl: Add upper limit for session cache If upper limit is reached invalidate the current cache entries, e.i the session lifetime is the max time a session will be keept, but it may be invalidated earlier if the max limit for the table is reached. This will keep the ssl manager process well behaved, not exhusting memeory. Invalidating the entries will incrementally empty the cache to make room for fresh sessions entries. --- lib/ssl/src/ssl_manager.erl | 184 +++++++++++++++++++++++----------- lib/ssl/src/ssl_session.erl | 4 +- lib/ssl/src/ssl_session_cache.erl | 8 +- lib/ssl/src/ssl_session_cache_api.erl | 1 + 4 files changed, 138 insertions(+), 59 deletions(-) (limited to 'lib/ssl/src') diff --git a/lib/ssl/src/ssl_manager.erl b/lib/ssl/src/ssl_manager.erl index 9e535104f7..00e95f5c5b 100644 --- a/lib/ssl/src/ssl_manager.erl +++ b/lib/ssl/src/ssl_manager.erl @@ -46,15 +46,19 @@ -include_lib("kernel/include/file.hrl"). -record(state, { - session_cache_client, - session_cache_server, - session_cache_cb, - session_lifetime, - certificate_db, - session_validation_timer, + session_cache_client :: db_handle(), + session_cache_server :: db_handle(), + session_cache_cb :: atom(), + session_lifetime :: integer(), + certificate_db :: db_handle(), + session_validation_timer :: reference(), last_delay_timer = {undefined, undefined},%% Keep for testing purposes - last_pem_check, - clear_pem_cache + last_pem_check :: erlang:timestamp(), + clear_pem_cache :: integer(), + session_cache_client_max :: integer(), + session_cache_server_max :: integer(), + session_server_invalidator :: undefined | pid(), + session_client_invalidator :: undefined | pid() }). -define(GEN_UNIQUE_ID_MAX_TRIES, 10). @@ -62,7 +66,7 @@ -define(CLEAR_PEM_CACHE, 120000). -define(CLEAN_SESSION_DB, 60000). -define(CLEAN_CERT_DB, 500). --define(NOT_TO_BIG, 10). +-define(DEFAULT_MAX_SESSION_CACHE, 1000). %%==================================================================== %% API @@ -87,7 +91,8 @@ manager_name(dist) -> %%-------------------------------------------------------------------- start_link(Opts) -> DistMangerName = manager_name(normal), - gen_server:start_link({local, DistMangerName}, ?MODULE, [DistMangerName, Opts], []). + gen_server:start_link({local, DistMangerName}, + ?MODULE, [DistMangerName, Opts], []). %%-------------------------------------------------------------------- -spec start_link_dist(list()) -> {ok, pid()} | ignore | {error, term()}. @@ -97,7 +102,8 @@ start_link(Opts) -> %%-------------------------------------------------------------------- start_link_dist(Opts) -> DistMangerName = manager_name(dist), - gen_server:start_link({local, DistMangerName}, ?MODULE, [DistMangerName, Opts], []). + gen_server:start_link({local, DistMangerName}, + ?MODULE, [DistMangerName, Opts], []). %%-------------------------------------------------------------------- -spec connection_init(binary()| {der, list()}, client | server, @@ -167,7 +173,8 @@ new_session_id(Port) -> %% be called by ssl-connection processes. %%-------------------------------------------------------------------- clean_cert_db(Ref, File) -> - erlang:send_after(?CLEAN_CERT_DB, get(ssl_manager), {clean_cert_db, Ref, File}), + erlang:send_after(?CLEAN_CERT_DB, get(ssl_manager), + {clean_cert_db, Ref, File}), ok. %%-------------------------------------------------------------------- @@ -235,10 +242,12 @@ init([Name, Opts]) -> SessionLifeTime = proplists:get_value(session_lifetime, Opts, ?'24H_in_sec'), CertDb = ssl_pkix_db:create(), - ClientSessionCache = CacheCb:init([{role, client} | - proplists:get_value(session_cb_init_args, Opts, [])]), - ServerSessionCache = CacheCb:init([{role, server} | - proplists:get_value(session_cb_init_args, Opts, [])]), + ClientSessionCache = + CacheCb:init([{role, client} | + proplists:get_value(session_cb_init_args, Opts, [])]), + ServerSessionCache = + CacheCb:init([{role, server} | + proplists:get_value(session_cb_init_args, Opts, [])]), Timer = erlang:send_after(SessionLifeTime * 1000 + 5000, self(), validate_sessions), Interval = pem_check_interval(), @@ -250,7 +259,11 @@ init([Name, Opts]) -> session_lifetime = SessionLifeTime, session_validation_timer = Timer, last_pem_check = os:timestamp(), - clear_pem_cache = Interval + clear_pem_cache = Interval, + session_cache_client_max = + max_session_cache_size(session_cache_client_max), + session_cache_server_max = + max_session_cache_size(session_cache_server_max) }}. %%-------------------------------------------------------------------- @@ -267,7 +280,8 @@ init([Name, Opts]) -> handle_call({{connection_init, <<>>, Role, {CRLCb, UserCRLDb}}, _Pid}, _From, #state{certificate_db = [CertDb, FileRefDb, PemChace | _] = Db} = State) -> Ref = make_ref(), - Result = {ok, Ref, CertDb, FileRefDb, PemChace, session_cache(Role, State), {CRLCb, crl_db_info(Db, UserCRLDb)}}, + Result = {ok, Ref, CertDb, FileRefDb, PemChace, + session_cache(Role, State), {CRLCb, crl_db_info(Db, UserCRLDb)}}, {reply, Result, State#state{certificate_db = Db}}; handle_call({{connection_init, Trustedcerts, Role, {CRLCb, UserCRLDb}}, Pid}, _From, @@ -305,7 +319,8 @@ handle_call({{cache_pem,File}, _Pid}, _, _:Reason -> {reply, {error, Reason}, State} end; -handle_call({unconditionally_clear_pem_cache, _},_, #state{certificate_db = [_,_,PemChace | _]} = State) -> +handle_call({unconditionally_clear_pem_cache, _},_, + #state{certificate_db = [_,_,PemChace | _]} = State) -> ssl_pkix_db:clear(PemChace), {reply, ok, State}. @@ -317,27 +332,12 @@ handle_call({unconditionally_clear_pem_cache, _},_, #state{certificate_db = [_,_ %% %% Description: Handling cast messages %%-------------------------------------------------------------------- -handle_cast({register_session, Host, Port, Session}, - #state{session_cache_client = Cache, - session_cache_cb = CacheCb} = State) -> - TimeStamp = erlang:monotonic_time(), - NewSession = Session#session{time_stamp = TimeStamp}, - - case CacheCb:select_session(Cache, {Host, Port}) of - no_session -> - CacheCb:update(Cache, {{Host, Port}, - NewSession#session.session_id}, NewSession); - Sessions -> - register_unique_session(Sessions, NewSession, CacheCb, Cache, {Host, Port}) - end, +handle_cast({register_session, Host, Port, Session}, State0) -> + State = ssl_client_register_session(Host, Port, Session, State0), {noreply, State}; -handle_cast({register_session, Port, Session}, - #state{session_cache_server = Cache, - session_cache_cb = CacheCb} = State) -> - TimeStamp = erlang:monotonic_time(), - NewSession = Session#session{time_stamp = TimeStamp}, - CacheCb:update(Cache, {Port, NewSession#session.session_id}, NewSession), +handle_cast({register_session, Port, Session}, State0) -> + State = server_register_session(Port, Session, State0), {noreply, State}; handle_cast({invalidate_session, Host, Port, @@ -411,10 +411,10 @@ handle_info({clean_cert_db, Ref, File}, end, {noreply, State}; -handle_info({'EXIT', _, _}, State) -> - %% Session validator died!! Do we need to take any action? - %% maybe error log - {noreply, State}; +handle_info({'EXIT', Pid, _}, #state{session_client_invalidator = Pid} = State) -> + {noreply, State#state{session_client_invalidator = undefined}}; +handle_info({'EXIT', Pid, _}, #state{session_server_invalidator = Pid} = State) -> + {noreply, State#state{session_server_invalidator = undefined}}; handle_info(_Info, State) -> {noreply, State}. @@ -495,23 +495,39 @@ delay_time() -> ?CLEAN_SESSION_DB end. -invalidate_session(Cache, CacheCb, Key, Session, #state{last_delay_timer = LastTimer} = State) -> +max_session_cache_size(CacheType) -> + case application:get_env(ssl, CacheType) of + {ok, Size} when is_integer(Size) -> + Size; + _ -> + ?DEFAULT_MAX_SESSION_CACHE + end. + +invalidate_session(Cache, CacheCb, Key, Session, State) -> case CacheCb:lookup(Cache, Key) of undefined -> %% Session is already invalidated {noreply, State}; #session{is_resumable = new} -> CacheCb:delete(Cache, Key), {noreply, State}; - _ -> - %% When a registered session is invalidated we need to wait a while before deleting - %% it as there might be pending connections that rightfully needs to look - %% up the session data but new connections should not get to use this session. - CacheCb:update(Cache, Key, Session#session{is_resumable = false}), - TRef = - erlang:send_after(delay_time(), self(), {delayed_clean_session, Key, Cache}), - {noreply, State#state{last_delay_timer = last_delay_timer(Key, TRef, LastTimer)}} + _ -> + delayed_invalidate_session(CacheCb, Cache, Key, Session, State) end. +delayed_invalidate_session(CacheCb, Cache, Key, Session, + #state{last_delay_timer = LastTimer} = State) -> + %% When a registered session is invalidated we need to + %% wait a while before deleting it as there might be + %% pending connections that rightfully needs to look up + %% the session data but new connections should not get to + %% use this session. + CacheCb:update(Cache, Key, Session#session{is_resumable = false}), + TRef = + erlang:send_after(delay_time(), self(), + {delayed_clean_session, Key, Cache}), + {noreply, State#state{last_delay_timer = + last_delay_timer(Key, TRef, LastTimer)}}. + last_delay_timer({{_,_},_}, TRef, {LastServer, _}) -> {LastServer, TRef}; last_delay_timer({_,_}, TRef, {_, LastClient}) -> @@ -535,7 +551,7 @@ new_id(Port, Tries, Cache, CacheCb) -> %% until handshake is compleate and the %% other session values are set. CacheCb:update(Cache, {Port, Id}, #session{session_id = Id, - is_resumable = false, + is_resumable = new, time_stamp = Now}), Id; _ -> @@ -557,15 +573,62 @@ clean_cert_db(Ref, CertDb, RefDb, PemCache, File) -> ok end. +ssl_client_register_session(Host, Port, Session, #state{session_cache_client = Cache, + session_cache_cb = CacheCb, + session_cache_client_max = Max, + session_client_invalidator = Pid0} = State) -> + TimeStamp = erlang:monotonic_time(), + NewSession = Session#session{time_stamp = TimeStamp}, + + case CacheCb:select_session(Cache, {Host, Port}) of + no_session -> + Pid = do_register_session({{Host, Port}, + NewSession#session.session_id}, + NewSession, Max, Pid0, Cache, CacheCb), + State#state{session_client_invalidator = Pid}; + Sessions -> + register_unique_session(Sessions, NewSession, {Host, Port}, State) + end. + +server_register_session(Port, Session, #state{session_cache_server_max = Max, + session_cache_server = Cache, + session_cache_cb = CacheCb, + session_server_invalidator = Pid0} = State) -> + TimeStamp = erlang:monotonic_time(), + NewSession = Session#session{time_stamp = TimeStamp}, + Pid = do_register_session({Port, NewSession#session.session_id}, + NewSession, Max, Pid0, Cache, CacheCb), + State#state{session_server_invalidator = Pid}. + +do_register_session(Key, Session, Max, Pid, Cache, CacheCb) -> + try CacheCb:size(Cache) of + N when N > Max -> + invalidate_session_cache(Pid, CacheCb, Cache); + _ -> + CacheCb:update(Cache, Key, Session), + Pid + catch + error:undef -> + CacheCb:update(Cache, Key, Session), + Pid + end. + + %% Do not let dumb clients create a gigantic session table %% for itself creating big delays at connection time. -register_unique_session(Sessions, Session, CacheCb, Cache, PartialKey) -> +register_unique_session(Sessions, Session, PartialKey, + #state{session_cache_client_max = Max, + session_cache_client = Cache, + session_cache_cb = CacheCb, + session_client_invalidator = Pid0} = State) -> case exists_equivalent(Session , Sessions) of true -> - ok; + State; false -> - CacheCb:update(Cache, {PartialKey, - Session#session.session_id}, Session) + Pid = do_register_session({PartialKey, + Session#session.session_id}, + Session, Max, Pid0, Cache, CacheCb), + State#state{session_client_invalidator = Pid} end. exists_equivalent(_, []) -> @@ -620,7 +683,8 @@ pem_check_interval() -> end. is_before_checkpoint(Time, CheckPoint) -> - calendar:datetime_to_gregorian_seconds(calendar:now_to_datetime(CheckPoint)) - + calendar:datetime_to_gregorian_seconds( + calendar:now_to_datetime(CheckPoint)) - calendar:datetime_to_gregorian_seconds(Time) > 0. add_trusted_certs(Pid, Trustedcerts, Db) -> @@ -641,3 +705,9 @@ crl_db_info([_,_,_,Local], {internal, Info}) -> crl_db_info(_, UserCRLDb) -> UserCRLDb. +%% Only start a session invalidator if there is not +%% one already active +invalidate_session_cache(undefined, CacheCb, Cache) -> + start_session_validator(Cache, CacheCb, {invalidate_before, erlang:monotonic_time()}); +invalidate_session_cache(Pid, _CacheCb, _Cache) -> + Pid. diff --git a/lib/ssl/src/ssl_session.erl b/lib/ssl/src/ssl_session.erl index 0ee8a096e9..2b24bff5ff 100644 --- a/lib/ssl/src/ssl_session.erl +++ b/lib/ssl/src/ssl_session.erl @@ -61,10 +61,12 @@ client_id(ClientInfo, Cache, CacheCb, OwnCert) -> SessionId end. --spec valid_session(#session{}, seconds()) -> boolean(). +-spec valid_session(#session{}, seconds() | {invalidate_before, integer()}) -> boolean(). %% %% Description: Check that the session has not expired %%-------------------------------------------------------------------- +valid_session(#session{time_stamp = TimeStamp}, {invalidate_before, Before}) -> + TimeStamp > Before; valid_session(#session{time_stamp = TimeStamp}, LifeTime) -> Now = erlang:monotonic_time(), Lived = erlang:convert_time_unit(Now-TimeStamp, native, seconds), diff --git a/lib/ssl/src/ssl_session_cache.erl b/lib/ssl/src/ssl_session_cache.erl index cfc48cd935..9585e613e6 100644 --- a/lib/ssl/src/ssl_session_cache.erl +++ b/lib/ssl/src/ssl_session_cache.erl @@ -27,7 +27,7 @@ -include("ssl_internal.hrl"). -export([init/1, terminate/1, lookup/2, update/3, delete/2, foldl/3, - select_session/2]). + select_session/2, size/1]). %%-------------------------------------------------------------------- %% Description: Return table reference. Called by ssl_manager process. @@ -85,6 +85,12 @@ select_session(Cache, PartialKey) -> ets:select(Cache, [{{{PartialKey,'_'}, '$1'},[],['$1']}]). +%%-------------------------------------------------------------------- +%% Description: Returns the cache size +%%-------------------------------------------------------------------- +size(Cache) -> + ets:info(Cache, size). + %%-------------------------------------------------------------------- %%% Internal functions %%-------------------------------------------------------------------- diff --git a/lib/ssl/src/ssl_session_cache_api.erl b/lib/ssl/src/ssl_session_cache_api.erl index 536b52c44b..8f62c25be5 100644 --- a/lib/ssl/src/ssl_session_cache_api.erl +++ b/lib/ssl/src/ssl_session_cache_api.erl @@ -33,3 +33,4 @@ -callback delete(db_handle(), key()) -> any(). -callback foldl(fun(), term(), db_handle()) -> term(). -callback select_session(db_handle(), {host(), inet:port_number()} | inet:port_number()) -> [#session{}]. +-callback size(db_handle()) -> integer(). -- cgit v1.2.3 From b7d8e2d89cfcc4c4265f45adc5ffc425e43c90fe Mon Sep 17 00:00:00 2001 From: Ingela Anderton Andin Date: Wed, 18 Nov 2015 09:34:17 +0100 Subject: ssl: SSLv3 completeness We are considering removing default support for DES cipher suites. However this cipher suite is currently allowed in TLS and missing from SSL. --- lib/ssl/src/ssl_v3.erl | 1 + 1 file changed, 1 insertion(+) (limited to 'lib/ssl/src') diff --git a/lib/ssl/src/ssl_v3.erl b/lib/ssl/src/ssl_v3.erl index 5e043624a7..f169059a75 100644 --- a/lib/ssl/src/ssl_v3.erl +++ b/lib/ssl/src/ssl_v3.erl @@ -144,6 +144,7 @@ suites() -> ?TLS_DHE_RSA_WITH_AES_128_CBC_SHA, ?TLS_DHE_DSS_WITH_AES_128_CBC_SHA, ?TLS_RSA_WITH_AES_128_CBC_SHA, + ?TLS_DHE_RSA_WITH_DES_CBC_SHA, ?TLS_RSA_WITH_DES_CBC_SHA ]. -- cgit v1.2.3 From fb64cba70b4b08442e04c932f88579a35e219fdb Mon Sep 17 00:00:00 2001 From: Ingela Anderton Andin Date: Mon, 7 Dec 2015 11:10:35 +0100 Subject: ssl: Prepare for release --- lib/ssl/src/ssl.app.src | 2 +- lib/ssl/src/ssl.appup.src | 14 ++------------ 2 files changed, 3 insertions(+), 13 deletions(-) (limited to 'lib/ssl/src') diff --git a/lib/ssl/src/ssl.app.src b/lib/ssl/src/ssl.app.src index be8ef6f85f..619ab7b610 100644 --- a/lib/ssl/src/ssl.app.src +++ b/lib/ssl/src/ssl.app.src @@ -54,6 +54,6 @@ {env, []}, {mod, {ssl_app, []}}, {runtime_dependencies, ["stdlib-2.0","public_key-1.0","kernel-3.0", - "erts-6.0","crypto-3.3", "inets-5.10.7"]}]}. + "erts-7.0","crypto-3.3", "inets-5.10.7"]}]}. diff --git a/lib/ssl/src/ssl.appup.src b/lib/ssl/src/ssl.appup.src index 8d5bd6f8d8..11728128c4 100644 --- a/lib/ssl/src/ssl.appup.src +++ b/lib/ssl/src/ssl.appup.src @@ -1,24 +1,14 @@ %% -*- erlang -*- {"%VSN%", [ - {<<"7\\.0">>, [{load_module, ssl, soft_purge, soft_purge, []}, - {load_module, ssl_connection, soft_purge, soft_purge, []}, - {load_module, tls_connection, soft_purge, soft_purge, []}, - {load_module, ssl_session, soft_purge, soft_purge, []}, - {load_module, ssl_session_cache, soft_purge, soft_purge, []} - ]}, + {<<"7\\..*">>, [{restart_application, ssl}]}, {<<"6\\..*">>, [{restart_application, ssl}]}, {<<"5\\..*">>, [{restart_application, ssl}]}, {<<"4\\..*">>, [{restart_application, ssl}]}, {<<"3\\..*">>, [{restart_application, ssl}]} ], [ - {<<"7\\.0">>, [{load_module, ssl, soft_purge, soft_purge, []}, - {load_module, ssl_connection, soft_purge, soft_purge, []}, - {load_module, tls_connection, soft_purge, soft_purge, []}, - {load_module, ssl_session, soft_purge, soft_purge, []}, - {load_module, ssl_session_cache, soft_purge, soft_purge, []} - ]}, + {<<"7\\..*">>, [{restart_application, ssl}]}, {<<"6\\..*">>, [{restart_application, ssl}]}, {<<"5\\..*">>, [{restart_application, ssl}]}, {<<"4\\..*">>, [{restart_application, ssl}]}, -- cgit v1.2.3 From 80bf91029f9afcef147493a449ad0ae328efb3ae Mon Sep 17 00:00:00 2001 From: Ingela Anderton Andin Date: Tue, 8 Dec 2015 14:59:28 +0100 Subject: ssl: Correct spec --- lib/ssl/src/tls_record.erl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'lib/ssl/src') diff --git a/lib/ssl/src/tls_record.erl b/lib/ssl/src/tls_record.erl index 1e266ed424..9348c8bbdd 100644 --- a/lib/ssl/src/tls_record.erl +++ b/lib/ssl/src/tls_record.erl @@ -298,7 +298,7 @@ highest_protocol_version(_,Version) -> Version. %%-------------------------------------------------------------------- --spec is_higher(V1 :: tls_version(), V2::tls_version()) -> tls_version(). +-spec is_higher(V1 :: tls_version(), V2::tls_version()) -> boolean(). %% %% Description: Is V1 > V2 %%-------------------------------------------------------------------- -- cgit v1.2.3 From 5f49de9d6e8ae247b10e37c085bf1d1dc9945ac8 Mon Sep 17 00:00:00 2001 From: Magnus Henoch Date: Wed, 28 Oct 2015 17:15:36 +0000 Subject: Save error reasons for TLS distribution connections When establishing an outbound connection for TLS distribution, let's hold on to the failure reasons and use them as exit reasons. These exit reasons are normally invisible, but they can be seen in the logs after calling net_kernel:verbose(1). While there are trace messages in the code already, those require recompiling the module with a special flag, which is more cumbersome than changing the net_kernel verbosity level at run time. --- lib/ssl/src/inet_tls_dist.erl | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) (limited to 'lib/ssl/src') diff --git a/lib/ssl/src/inet_tls_dist.erl b/lib/ssl/src/inet_tls_dist.erl index 7367b5c224..411bb44bdc 100644 --- a/lib/ssl/src/inet_tls_dist.erl +++ b/lib/ssl/src/inet_tls_dist.erl @@ -75,23 +75,23 @@ do_setup(Kernel, Node, Type, MyNode, LongOrShortNames, SetupTime) -> Timer, Version, Ip, TcpPort, Address, Type), dist_util:handshake_we_started(HSData); - _ -> + Other -> %% Other Node may have closed since %% port_please ! ?trace("other node (~p) " "closed since port_please.~n", [Node]), - ?shutdown(Node) + ?shutdown2(Node, {shutdown, {connect_failed, Other}}) end; - _ -> + Other -> ?trace("port_please (~p) " "failed.~n", [Node]), - ?shutdown(Node) + ?shutdown2(Node, {shutdown, {port_please_failed, Other}}) end; - _Other -> + Other -> ?trace("inet_getaddr(~p) " "failed (~p).~n", [Node,Other]), - ?shutdown(Node) + ?shutdown2(Node, {shutdown, {inet_getaddr_failed, Other}}) end. close(Socket) -> -- cgit v1.2.3 From 91006821c6d65708fa05a93ec1edc2372326a3cb Mon Sep 17 00:00:00 2001 From: Magnus Henoch Date: Mon, 9 Nov 2015 18:25:56 +0000 Subject: Report bad options for outgoing TLS distribution If ssl:connect/3 returns an error related to options, let's log that so we have a chance to see it and fix it. --- lib/ssl/src/ssl_tls_dist_proxy.erl | 5 +++++ 1 file changed, 5 insertions(+) (limited to 'lib/ssl/src') diff --git a/lib/ssl/src/ssl_tls_dist_proxy.erl b/lib/ssl/src/ssl_tls_dist_proxy.erl index e7f7fa96a1..1f48ce9e8c 100644 --- a/lib/ssl/src/ssl_tls_dist_proxy.erl +++ b/lib/ssl/src/ssl_tls_dist_proxy.erl @@ -193,6 +193,11 @@ setup_proxy(Ip, Port, Parent) -> Err -> Parent ! {self(), Err} end; + {error, {options, _}} = Err -> + %% Bad options: that's probably our fault. Let's log that. + error_logger:error_msg("Cannot open TLS distribution connection: ~s~n", + [ssl:format_error(Err)]), + Parent ! {self(), Err}; Err -> Parent ! {self(), Err} end. -- cgit v1.2.3 From 4f13b9838c5f7e182661398418065b09716034f2 Mon Sep 17 00:00:00 2001 From: Andrey Mayorov Date: Fri, 11 Dec 2015 03:23:53 +0300 Subject: ssl: fix hibernate_after with instant or near instant timeouts --- lib/ssl/src/tls_connection.erl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'lib/ssl/src') diff --git a/lib/ssl/src/tls_connection.erl b/lib/ssl/src/tls_connection.erl index a468c131ce..b2b85eaf8d 100644 --- a/lib/ssl/src/tls_connection.erl +++ b/lib/ssl/src/tls_connection.erl @@ -212,7 +212,7 @@ hello(Hello = #client_hello{client_version = ClientVersion, client_ecc = {EllipticCurves, EcPointFormats}, negotiated_protocol = Protocol}, ?MODULE) end; -hello(Hello, +hello(Hello = #server_hello{}, #state{connection_states = ConnectionStates0, negotiated_version = ReqVersion, role = client, -- cgit v1.2.3