aboutsummaryrefslogtreecommitdiffstats
path: root/lib/ssl/src/tls_connection.erl
diff options
context:
space:
mode:
Diffstat (limited to 'lib/ssl/src/tls_connection.erl')
-rw-r--r--lib/ssl/src/tls_connection.erl105
1 files changed, 63 insertions, 42 deletions
diff --git a/lib/ssl/src/tls_connection.erl b/lib/ssl/src/tls_connection.erl
index 80a8c6b72c..cee69a05a5 100644
--- a/lib/ssl/src/tls_connection.erl
+++ b/lib/ssl/src/tls_connection.erl
@@ -108,7 +108,7 @@ start_fsm(Role, Host, Port, Socket, {#ssl_options{erl_dist = true},_, Tracker} =
end.
%%--------------------------------------------------------------------
--spec start_link(atom(), pid(), host(), inet:port_number(), port(), list(), pid(), tuple()) ->
+-spec start_link(atom(), pid(), ssl:host(), inet:port_number(), port(), list(), pid(), tuple()) ->
{ok, pid()} | ignore | {error, reason()}.
%%
%% Description: Creates a gen_statem process which calls Module:init/1 to
@@ -143,22 +143,24 @@ pids(#state{protocol_specific = #{sender := Sender}}) ->
%%====================================================================
%% State transition handling
%%====================================================================
-next_record(#state{unprocessed_handshake_events = N} = State) when N > 0 ->
- {no_record, State#state{unprocessed_handshake_events = N-1}};
-
+next_record(#state{handshake_env =
+ #handshake_env{unprocessed_handshake_events = N} = HsEnv}
+ = State) when N > 0 ->
+ {no_record, State#state{handshake_env =
+ HsEnv#handshake_env{unprocessed_handshake_events = N-1}}};
next_record(#state{protocol_buffers =
- #protocol_buffers{tls_packets = [], tls_cipher_texts = [CT | Rest]}
- = Buffers,
- connection_states = ConnStates0,
- ssl_options = #ssl_options{padding_check = Check}} = State) ->
- case tls_record:decode_cipher_text(CT, ConnStates0, Check) of
- {Plain, ConnStates} ->
- {Plain, State#state{protocol_buffers =
- Buffers#protocol_buffers{tls_cipher_texts = Rest},
- connection_states = ConnStates}};
- #alert{} = Alert ->
- {Alert, State}
- end;
+ #protocol_buffers{tls_packets = [], tls_cipher_texts = [#ssl_tls{type = Type}| _] = CipherTexts0}
+ = Buffers,
+ connection_states = ConnectionStates0,
+ ssl_options = #ssl_options{padding_check = Check}} = State) ->
+ case decode_cipher_texts(Type, CipherTexts0, ConnectionStates0, Check, <<>>) of
+ {#ssl_tls{} = Record, ConnectionStates, CipherTexts} ->
+ {Record, State#state{protocol_buffers = Buffers#protocol_buffers{tls_cipher_texts = CipherTexts},
+ connection_states = ConnectionStates}};
+ {#alert{} = Alert, ConnectionStates, CipherTexts} ->
+ {Alert, State#state{protocol_buffers = Buffers#protocol_buffers{tls_cipher_texts = CipherTexts},
+ connection_states = ConnectionStates}}
+ end;
next_record(#state{protocol_buffers = #protocol_buffers{tls_packets = [], tls_cipher_texts = []},
protocol_specific = #{active_n_toggle := true, active_n := N} = ProtocolSpec,
static_env = #static_env{socket = Socket,
@@ -196,6 +198,22 @@ next_event(StateName, Record, State, Actions) ->
{next_state, StateName, State, [{next_event, internal, Alert} | Actions]}
end.
+decode_cipher_texts(Type, [] = CipherTexts, ConnectionStates, _, Acc) ->
+ {#ssl_tls{type = Type, fragment = Acc}, ConnectionStates, CipherTexts};
+decode_cipher_texts(Type,
+ [#ssl_tls{type = Type} = CT | CipherTexts], ConnectionStates0, Check, Acc) ->
+ case tls_record:decode_cipher_text(CT, ConnectionStates0, Check) of
+ {#ssl_tls{type = ?APPLICATION_DATA, fragment = Plain}, ConnectionStates} ->
+ decode_cipher_texts(Type, CipherTexts,
+ ConnectionStates, Check, <<Acc/binary, Plain/binary>>);
+ {#ssl_tls{type = Type, fragment = Plain}, ConnectionStates} ->
+ {#ssl_tls{type = Type, fragment = Plain}, ConnectionStates, CipherTexts};
+ #alert{} = Alert ->
+ {Alert, ConnectionStates0, CipherTexts}
+ end;
+decode_cipher_texts(Type, CipherTexts, ConnectionStates, _, Acc) ->
+ {#ssl_tls{type = Type, fragment = Acc}, ConnectionStates, CipherTexts}.
+
%%% TLS record protocol level application data messages
handle_protocol_record(#ssl_tls{type = ?APPLICATION_DATA, fragment = Data}, StateName, State0) ->
@@ -227,8 +245,12 @@ handle_protocol_record(#ssl_tls{type = ?HANDSHAKE, fragment = Data},
connection ->
ssl_connection:hibernate_after(StateName, State, Events);
_ ->
+ HsEnv = State#state.handshake_env,
{next_state, StateName,
- State#state{unprocessed_handshake_events = unprocessed_events(Events)}, Events}
+ State#state{protocol_buffers = Buffers,
+ handshake_env =
+ HsEnv#handshake_env{unprocessed_handshake_events
+ = unprocessed_events(Events)}}, Events}
end
end
catch throw:#alert{} = Alert ->
@@ -263,15 +285,17 @@ handle_protocol_record(#ssl_tls{type = _Unknown}, StateName, State) ->
renegotiation(Pid, WriteState) ->
gen_statem:call(Pid, {user_renegotiate, WriteState}).
-renegotiate(#state{static_env = #static_env{role = client}} = State, Actions) ->
+renegotiate(#state{static_env = #static_env{role = client},
+ handshake_env = HsEnv} = State, Actions) ->
%% Handle same way as if server requested
%% the renegotiation
Hs0 = ssl_handshake:init_handshake_history(),
- {next_state, connection, State#state{tls_handshake_history = Hs0},
+ {next_state, connection, State#state{handshake_env = HsEnv#handshake_env{tls_handshake_history = Hs0}},
[{next_event, internal, #hello_request{}} | Actions]};
renegotiate(#state{static_env = #static_env{role = server,
socket = Socket,
transport_cb = Transport},
+ handshake_env = HsEnv,
negotiated_version = Version,
connection_states = ConnectionStates0} = State0, Actions) ->
HelloRequest = ssl_handshake:hello_request(),
@@ -282,20 +306,20 @@ renegotiate(#state{static_env = #static_env{role = server,
send(Transport, Socket, BinMsg),
State = State0#state{connection_states =
ConnectionStates,
- tls_handshake_history = Hs0},
+ handshake_env = HsEnv#handshake_env{tls_handshake_history = Hs0}},
next_event(hello, no_record, State, Actions).
send_handshake(Handshake, State) ->
send_handshake_flight(queue_handshake(Handshake, State)).
queue_handshake(Handshake, #state{negotiated_version = Version,
- tls_handshake_history = Hist0,
+ handshake_env = #handshake_env{tls_handshake_history = Hist0} = HsEnv,
flight_buffer = Flight0,
connection_states = ConnectionStates0} = State0) ->
{BinHandshake, ConnectionStates, Hist} =
encode_handshake(Handshake, Version, ConnectionStates0, Hist0),
State0#state{connection_states = ConnectionStates,
- tls_handshake_history = Hist,
+ handshake_env = HsEnv#handshake_env{tls_handshake_history = Hist},
flight_buffer = Flight0 ++ [BinHandshake]}.
send_handshake_flight(#state{static_env = #static_env{socket = Socket,
@@ -318,14 +342,14 @@ reinit(#state{protocol_specific = #{sender := Sender},
tls_sender:update_connection_state(Sender, Write, Version),
reinit_handshake_data(State).
-reinit_handshake_data(State) ->
+reinit_handshake_data(#state{handshake_env = HsEnv} =State) ->
%% premaster_secret, public_key_info and tls_handshake_info
%% are only needed during the handshake phase.
%% To reduce memory foot print of a connection reinitialize them.
State#state{
premaster_secret = undefined,
public_key_info = undefined,
- tls_handshake_history = ssl_handshake:init_handshake_history()
+ handshake_env = HsEnv#handshake_env{tls_handshake_history = ssl_handshake:init_handshake_history()}
}.
select_sni_extension(#client_hello{extensions = HelloExtensions}) ->
@@ -440,10 +464,10 @@ init({call, From}, {start, Timeout},
socket = Socket,
session_cache = Cache,
session_cache_cb = CacheCb},
+ handshake_env = #handshake_env{renegotiation = {Renegotiation, _}} = HsEnv,
ssl_options = SslOpts,
session = #session{own_certificate = Cert} = Session0,
- connection_states = ConnectionStates0,
- renegotiation = {Renegotiation, _}
+ connection_states = ConnectionStates0
} = State0) ->
Timer = ssl_connection:start_or_recv_cancel_timer(Timeout, From),
Hello = tls_handshake:client_hello(Host, Port, ConnectionStates0, SslOpts,
@@ -459,7 +483,7 @@ init({call, From}, {start, Timeout},
negotiated_version = Version, %% Requested version
session =
Session0#session{session_id = Hello#client_hello.session_id},
- tls_handshake_history = Handshake,
+ handshake_env = HsEnv#handshake_env{tls_handshake_history = Handshake},
start_or_recv_from = From,
timer = Timer},
next_event(hello, no_record, State);
@@ -505,8 +529,8 @@ hello(internal, #client_hello{client_version = ClientVersion} = Hello,
port = Port,
session_cache = Cache,
session_cache_cb = CacheCb},
+ handshake_env = #handshake_env{renegotiation = {Renegotiation, _}} = HsEnv,
session = #session{own_certificate = Cert} = Session0,
- renegotiation = {Renegotiation, _},
negotiated_protocol = CurrentProtocol,
key_algorithm = KeyExAlg,
ssl_options = SslOpts} = State) ->
@@ -526,7 +550,7 @@ hello(internal, #client_hello{client_version = ClientVersion} = Hello,
State#state{connection_states = ConnectionStates,
negotiated_version = Version,
hashsign_algorithm = HashSign,
- client_hello_version = ClientVersion,
+ handshake_env = HsEnv#handshake_env{client_hello_version = ClientVersion},
session = Session,
negotiated_protocol = Protocol})
end;
@@ -534,7 +558,7 @@ hello(internal, #server_hello{} = Hello,
#state{connection_states = ConnectionStates0,
negotiated_version = ReqVersion,
static_env = #static_env{role = client},
- renegotiation = {Renegotiation, _},
+ handshake_env = #handshake_env{renegotiation = {Renegotiation, _}},
ssl_options = SslOptions} = State) ->
case tls_handshake:hello(Hello, SslOptions, ConnectionStates0, Renegotiation) of
#alert{} = Alert ->
@@ -620,7 +644,7 @@ connection(internal, #hello_request{},
port = Port,
session_cache = Cache,
session_cache_cb = CacheCb},
- renegotiation = {Renegotiation, peer},
+ handshake_env = #handshake_env{renegotiation = {Renegotiation, peer}},
session = #session{own_certificate = Cert} = Session0,
ssl_options = SslOpts,
protocol_specific = #{sender := Pid},
@@ -642,7 +666,7 @@ connection(internal, #hello_request{},
port = Port,
session_cache = Cache,
session_cache_cb = CacheCb},
- renegotiation = {Renegotiation, _},
+ handshake_env = #handshake_env{renegotiation = {Renegotiation, _}},
session = #session{own_certificate = Cert} = Session0,
ssl_options = SslOpts,
connection_states = ConnectionStates} = State0) ->
@@ -653,6 +677,7 @@ connection(internal, #hello_request{},
= Hello#client_hello.session_id}}, Actions);
connection(internal, #client_hello{} = Hello,
#state{static_env = #static_env{role = server},
+ handshake_env = HsEnv,
allow_renegotiate = true,
connection_states = CS,
protocol_specific = #{sender := Sender}
@@ -666,7 +691,7 @@ connection(internal, #client_hello{} = Hello,
{ok, Write} = tls_sender:renegotiate(Sender),
next_event(hello, no_record, State#state{connection_states = CS#{current_write => Write},
allow_renegotiate = false,
- renegotiation = {true, peer}
+ handshake_env = HsEnv#handshake_env{renegotiation = {true, peer}}
},
[{next_event, internal, Hello}]);
connection(internal, #client_hello{},
@@ -734,7 +759,6 @@ initial_state(Role, Sender, Host, Port, Socket, {SSLOptions, SocketOptions, Trac
erl_dist = IsErlDist} = SSLOptions,
ConnectionStates = tls_record:init_connection_states(Role, BeastMitigation),
- ErlDistData = erl_dist_data(IsErlDist),
SessionCacheCb = case application:get_env(ssl, session_cb) of
{ok, Cb} when is_atom(Cb) ->
Cb;
@@ -763,15 +787,17 @@ initial_state(Role, Sender, Host, Port, Socket, {SSLOptions, SocketOptions, Trac
},
#state{
static_env = InitStatEnv,
+ handshake_env = #handshake_env{
+ tls_handshake_history = ssl_handshake:init_handshake_history(),
+ renegotiation = {false, first}
+ },
socket_options = SocketOptions,
ssl_options = SSLOptions,
session = #session{is_resumable = new},
- erl_dist_data = ErlDistData,
connection_states = ConnectionStates,
protocol_buffers = #protocol_buffers{},
user_application = {UserMonitor, User},
user_data_buffer = <<>>,
- renegotiation = {false, first},
allow_renegotiate = SSLOptions#ssl_options.client_renegotiation,
start_or_recv_from = undefined,
flight_buffer = [],
@@ -781,12 +807,6 @@ initial_state(Role, Sender, Host, Port, Socket, {SSLOptions, SocketOptions, Trac
}
}.
-erl_dist_data(true) ->
- #{dist_handle => undefined,
- dist_buffer => <<>>};
-erl_dist_data(false) ->
- #{}.
-
initialize_tls_sender(#state{static_env = #static_env{
role = Role,
transport_cb = Transport,
@@ -903,6 +923,7 @@ handle_alerts(_, {stop, _, _} = Stop) ->
Stop;
handle_alerts([#alert{level = ?WARNING, description = ?CLOSE_NOTIFY} | _Alerts],
{next_state, connection = StateName, #state{user_data_buffer = Buffer,
+ socket_options = #socket_options{active = false},
protocol_buffers = #protocol_buffers{tls_cipher_texts = CTs}} =
State}) when (Buffer =/= <<>>) orelse
(CTs =/= []) ->