Skip to content

Commit 1ad7f2a

Browse files
committed
ssl: Ticket and session server recovery
As TLS-1.3 ticket usages is an optional feature the server handling it will be started on demand and is local to to the TLS server that it was configured for. Due to this restart to recover from intermediate bugs via supervisor tree are unpractical and it should instead be a temporary process under its simple_one_for_one supervisor and restart handled on demand. Note that if it would crash so restart is needed old ticket are useless as the server state is lost. It is similar for TLS-1.2 session servers
1 parent 3a8d895 commit 1ad7f2a

15 files changed

Lines changed: 333 additions & 87 deletions

lib/ssl/src/dtls_packet_demux.erl

Lines changed: 18 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -117,7 +117,7 @@ getstat(PacketSocket, Opts) ->
117117
init([Owner, Port0, TransportInfo, EmOpts, DTLSOptions, Socket]) ->
118118
InternalActiveN = get_internal_active_n(),
119119
erlang:monitor(process, Owner),
120-
{ok, SessionIdHandle} = session_id_tracker(Socket, DTLSOptions),
120+
{ok, SessionIdHandle} = session_id_tracker(Socket),
121121
proc_lib:set_label({dtls_server_packet_demultiplexer, Port0}),
122122
{ok, #state{active_n = InternalActiveN,
123123
port = Port0,
@@ -139,11 +139,11 @@ handle_call({accept, Accepter}, From, #state{active_n = N,
139139
listener = Socket} = State0) ->
140140
next_datagram(Socket, N),
141141
State = State0#state{first = false,
142-
accepters = queue:in({Accepter, From}, Accepters)},
142+
accepters = queue:in({Accepter, From}, Accepters)},
143143
{noreply, State};
144144

145145
handle_call({accept, Accepter}, From, #state{accepters = Accepters} = State0) ->
146-
State = State0#state{accepters = queue:in({Accepter, From}, Accepters)},
146+
State = State0#state{accepters = queue:in({Accepter, From}, Accepters)},
147147
{noreply, State};
148148
handle_call(sockname, _, #state{listener = Socket} = State) ->
149149
Reply = inet:sockname(Socket),
@@ -348,8 +348,16 @@ setup_new_connection(User, From, Client, Msg, #state{dtls_processes = Processes,
348348
dtls_options = DTLSOpts,
349349
port = Port,
350350
listener = Socket,
351-
session_id_tracker = Tracker,
351+
session_id_tracker = Tracker0,
352352
emulated_options = EmOpts} = State) ->
353+
Tracker = case erlang:is_process_alive(Tracker0) of
354+
true ->
355+
Tracker0;
356+
false ->
357+
{ok, Tracker1} = session_id_tracker(Socket),
358+
Tracker1
359+
end,
360+
353361
ConnArgs = [server, "localhost", Port, {self(), {Client, Socket}},
354362
{DTLSOpts,
355363
emulated_opts_list(EmOpts, [mode, active], []),
@@ -360,11 +368,12 @@ setup_new_connection(User, From, Client, Msg, #state{dtls_processes = Processes,
360368
erlang:monitor(process, Pid),
361369
gen_server:reply(From, {ok, Pid}),
362370
Pid ! Msg,
363-
State#state{dtls_msq_queues = kv_insert(Client, {Pid, queue:new()}, MsgQueues),
371+
State#state{session_id_tracker = Tracker,
372+
dtls_msq_queues = kv_insert(Client, {Pid, queue:new()}, MsgQueues),
364373
dtls_processes = kv_insert(Pid, Client, Processes)};
365374
{error, Reason} ->
366375
gen_server:reply(From, {error, Reason}),
367-
State
376+
State#state{session_id_tracker = Tracker}
368377
end.
369378

370379
kv_update(Key, Value, Store) ->
@@ -411,7 +420,7 @@ get_socket_opts(Socket, SocketOpts, Cb) ->
411420
do_set_emulated_opts([], Opts) ->
412421
Opts;
413422
do_set_emulated_opts([{mode, Value} | Rest], Opts) ->
414-
do_set_emulated_opts(Rest, Opts#socket_options{mode = Value});
423+
do_set_emulated_opts(Rest, Opts#socket_options{mode = Value});
415424
do_set_emulated_opts([{active, N0} | Rest], Opts=#socket_options{active = Active}) when is_integer(N0) ->
416425
N = tls_socket:update_active_n(N0, Active),
417426
do_set_emulated_opts(Rest, Opts#socket_options{active = N});
@@ -421,14 +430,14 @@ do_set_emulated_opts([{active, Value} | Rest], Opts) ->
421430
emulated_opts_list(_,[], Acc) ->
422431
Acc;
423432
emulated_opts_list( Opts, [mode | Rest], Acc) ->
424-
emulated_opts_list(Opts, Rest, [{mode, Opts#socket_options.mode} | Acc]);
433+
emulated_opts_list(Opts, Rest, [{mode, Opts#socket_options.mode} | Acc]);
425434
emulated_opts_list(Opts, [active | Rest], Acc) ->
426435
emulated_opts_list(Opts, Rest, [{active, Opts#socket_options.active} | Acc]).
427436

428437
%% Regardless of the option reuse_sessions we need the session_id_tracker
429438
%% to generate session ids, but no sessions will be stored unless
430439
%% reuse_sessions = true.
431-
session_id_tracker(Listener,_) ->
440+
session_id_tracker(Listener) ->
432441
dtls_server_session_cache_sup:start_child(Listener).
433442

434443
get_internal_active_n() ->

lib/ssl/src/ssl.erl

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2504,9 +2504,11 @@ handshake(#sslsocket{connection_cb = tls_gen_connection,
25042504
when is_list(SslOpts), is_list(Trackers), ?IS_TIMEOUT(Timeout) ->
25052505
try
25062506
Tracker = proplists:get_value(option_tracker, Trackers),
2507-
{ok, EmOpts, _} = tls_socket:get_all_opts(Tracker),
2508-
ssl_gen_statem:handshake(Socket, {SslOpts,
2509-
tls_socket:emulated_socket_options(EmOpts, #socket_options{})}, Timeout)
2507+
{ok, EmOpts} = tls_socket:get_emulated_opts(Tracker),
2508+
ssl_gen_statem:handshake(Socket,
2509+
{SslOpts,
2510+
tls_socket:emulated_socket_options(EmOpts,
2511+
#socket_options{})}, Timeout)
25102512
catch
25112513
Error = {error, _Reason} -> Error
25122514
end;

lib/ssl/src/ssl_config.erl

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -126,7 +126,8 @@ get_ticket_store_size() ->
126126
application_int(server_session_ticket_store_size, 1000).
127127

128128
get_max_early_data_size() ->
129-
application_int(server_session_ticket_max_early_data, ?DEFAULT_MAX_EARLY_DATA_SIZE).
129+
application_int(server_session_ticket_max_early_data,
130+
?DEFAULT_MAX_EARLY_DATA_SIZE).
130131

131132
get_internal_active_n() ->
132133
get_internal_active_n(false).

lib/ssl/src/ssl_record.erl

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -412,7 +412,7 @@ nonce_seed(_,_, CipherState) ->
412412
%%--------------------------------------------------------------------
413413

414414
empty_connection_state(ConnectionEnd) ->
415-
MaxEarlyDataSize = ssl_config:get_max_early_data_size(),
415+
MaxEarlyDataSize = 0, %% TLS-1.3 only, this is not called by TLS-1.3
416416
empty_connection_state(ConnectionEnd, _Version = undefined, MaxEarlyDataSize).
417417
%%
418418
empty_connection_state(ConnectionEnd, Version, MaxEarlyDataSize) ->

lib/ssl/src/ssl_server_session_cache.erl

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -104,9 +104,7 @@ reuse_session(Pid, SessionId) ->
104104
%% Description: Makes a session available for reuse
105105
%%--------------------------------------------------------------------
106106
register_session(Pid, Session) ->
107-
gen_server:cast(Pid, {register_session, Session}).
108-
109-
107+
gen_server:cast(Pid, {register_session, Session}).
110108
%%%===================================================================
111109
%%% gen_server callbacks
112110
%%%===================================================================

lib/ssl/src/ssl_server_session_cache_sup.erl

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -51,13 +51,11 @@ start_child(Listener) ->
5151
%%% Supervisor callback
5252
%%%=========================================================================
5353
init(_) ->
54-
SupFlags = #{strategy => simple_one_for_one,
55-
intensity => 3,
56-
period => 3600
54+
SupFlags = #{strategy => simple_one_for_one
5755
},
5856
ChildSpecs = [#{id => undefined,
5957
start => {ssl_server_session_cache, start_link, []},
60-
restart => transient,
58+
restart => temporary,
6159
shutdown => 4000,
6260
modules => [ssl_server_session_cache],
6361
type => worker

lib/ssl/src/tls_dtls_gen_connection.erl

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -145,7 +145,10 @@ initial_state(Role, Sender, Tab, Host, Port, Socket, {SSLOptions, SocketOptions,
145145
%% Use highest supported version for client/server random nonce generation
146146
#{versions := [Version|_]} = SSLOptions,
147147
BeastMitigation = maps:get(beast_mitigation, SSLOptions, disabled),
148-
ConnectionStates = tls_record:init_connection_states(Role, Version, BeastMitigation),
148+
MaxEarlyData =
149+
tls_gen_connection_1_3:init_max_early_data_size(Role),
150+
ConnectionStates = tls_record:init_connection_states(Role, Version,
151+
BeastMitigation, MaxEarlyData),
149152
#{session_cb := SessionCacheCb} = ssl_config:pre_1_3_session_opts(Role),
150153
UserMonitor = erlang:monitor(process, User),
151154

lib/ssl/src/tls_gen_connection_1_3.erl

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,7 @@
5151
update_cipher_key/2,
5252
maybe_traffic_keylog_1_3/4,
5353
maybe_forget_hs_secrets/2,
54+
init_max_early_data_size/1,
5455
do_maybe/0]).
5556

5657
%%--------------------------------------------------------------------

lib/ssl/src/tls_record.erl

Lines changed: 0 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,6 @@
3737

3838
%% Handling of incoming data
3939
-export([get_tls_records/5,
40-
init_connection_states/3,
4140
init_connection_states/4]).
4241

4342
%% Encoding TLS records
@@ -69,20 +68,10 @@
6968
%% Handling of incoming data
7069
%%====================================================================
7170
%%--------------------------------------------------------------------
72-
-spec init_connection_states(Role, Version, BeastMitigation) ->
73-
ssl_record:connection_states() when
74-
Role :: client | server,
75-
Version :: tls_version(),
76-
BeastMitigation :: one_n_minus_one | zero_n | disabled.
77-
7871
%%
7972
%% Description: Creates a connection_states record with appropriate
8073
%% values for the initial SSL connection setup.
8174
%%--------------------------------------------------------------------
82-
init_connection_states(Role, Version, BeastMitigation) ->
83-
MaxEarlyDataSize = ssl_config:get_max_early_data_size(),
84-
init_connection_states(Role, Version, BeastMitigation, MaxEarlyDataSize).
85-
%%
8675
-spec init_connection_states(Role, Version, BeastMitigation,
8776
MaxEarlyDataSize) ->
8877
ssl_record:connection_states() when

lib/ssl/src/tls_server_connection_1_3.erl

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -665,16 +665,20 @@ maybe_send_session_ticket(State, 0) ->
665665
State;
666666
maybe_send_session_ticket(#state{connection_states = ConnectionStates,
667667
static_env = #static_env{trackers = Trackers,
668-
protocol_cb = Connection}
669-
} = State0, N) ->
668+
protocol_cb = Connection}}
669+
= State0, N) ->
670670
Tracker = proplists:get_value(session_tickets_tracker, Trackers),
671671
#{security_parameters := SecParamsR} =
672672
ssl_record:current_connection_state(ConnectionStates, read),
673673
#security_parameters{prf_algorithm = HKDF,
674674
resumption_master_secret = RMS} = SecParamsR,
675-
Ticket = new_session_ticket(Tracker, HKDF, RMS, State0),
676-
{State, _} = Connection:send_handshake(Ticket, State0),
677-
maybe_send_session_ticket(State, N - 1).
675+
case new_session_ticket(Tracker, HKDF, RMS, State0) of
676+
no_ticket -> %% Continuous restarts of ticket handler (unlikely scenario)
677+
State0;
678+
Ticket ->
679+
{State, _} = Connection:send_handshake(Ticket, State0),
680+
maybe_send_session_ticket(State, N - 1)
681+
end.
678682

679683
new_session_ticket(Tracker, HKDF, RMS,
680684
#state{ssl_options = #{session_tickets := stateful_with_cert},

0 commit comments

Comments
 (0)