aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorIngela Anderton Andin <ingela@erlang.org>2016-05-13 15:15:05 +0200
committerIngela Anderton Andin <ingela@erlang.org>2016-06-01 12:01:16 +0200
commite2110d0108cf17e3016abc9f0e76a40091e95f78 (patch)
tree38e1aea5f2cf5558e41187aced42dbd2391378ac
parent8e3eb916b34faf85b272031930be455163b49abf (diff)
downloadotp-e2110d0108cf17e3016abc9f0e76a40091e95f78.tar.gz
otp-e2110d0108cf17e3016abc9f0e76a40091e95f78.tar.bz2
otp-e2110d0108cf17e3016abc9f0e76a40091e95f78.zip
ssl: simplyfy code using gen_statem
-rw-r--r--lib/ssl/src/ssl_connection.erl20
-rw-r--r--lib/ssl/src/tls_connection.erl95
2 files changed, 45 insertions, 70 deletions
diff --git a/lib/ssl/src/ssl_connection.erl b/lib/ssl/src/ssl_connection.erl
index 22d107ff9c..b45c5c8fc6 100644
--- a/lib/ssl/src/ssl_connection.erl
+++ b/lib/ssl/src/ssl_connection.erl
@@ -786,12 +786,24 @@ downgrade(Type, Event, State, Connection) ->
%% Event handling functions called by state functions to handle
%% common or unexpected events for the state.
%%--------------------------------------------------------------------
+handle_common_event(internal, {handshake, {#hello_request{} = Handshake, _}}, connection = StateName,
+ #state{role = client} = State, _) ->
+ %% Should not be included in handshake history
+ {next_state, StateName, State#state{renegotiation = {true, peer}}, [{next_event, internal, Handshake}]};
+handle_common_event(internal, {handshake, {#hello_request{}, _}}, StateName, #state{role = client}, _)
+ when StateName =/= connection ->
+ {keep_state_and_data};
+handle_common_event(internal, {handshake, {Handshake, Raw}}, StateName,
+ #state{tls_handshake_history = Hs0} = State0, Connection) ->
+ %% This function handles client SNI hello extension when Handshake is
+ %% a client_hello, which needs to be determined by the connection callback.
+ %% In other cases this is a noop
+ State = Connection:handle_sni_extension(Handshake, State0),
+ HsHist = ssl_handshake:update_handshake_history(Hs0, Raw),
+ {next_state, StateName, State#state{tls_handshake_history = HsHist},
+ [{next_event, internal, Handshake}]};
handle_common_event(internal, {tls_record, TLSRecord}, StateName, State, Connection) ->
Connection:handle_common_event(internal, TLSRecord, StateName, State);
-handle_common_event(internal, #hello_request{}, StateName, #state{role = client} = State0, Connection)
- when StateName =:= connection ->
- {Record, State} = Connection:next_record(State0),
- Connection:next_event(StateName, Record, State);
handle_common_event(timeout, hibernate, _, _, _) ->
{keep_state_and_data, [hibernate]};
handle_common_event(internal, {application_data, Data}, StateName, State0, Connection) ->
diff --git a/lib/ssl/src/tls_connection.erl b/lib/ssl/src/tls_connection.erl
index 91903b4a1f..56e516bce2 100644
--- a/lib/ssl/src/tls_connection.erl
+++ b/lib/ssl/src/tls_connection.erl
@@ -50,7 +50,7 @@
%% Handshake handling
-export([renegotiate/2, send_handshake/2, send_change_cipher/2,
- reinit_handshake_data/1]).
+ reinit_handshake_data/1, handle_sni_extension/2]).
%% Alert and close handling
-export([send_alert/2, handle_own_alert/4, handle_close_alert/3,
@@ -228,16 +228,16 @@ error(_, _, _) ->
gen_statem:state_function_result().
%%--------------------------------------------------------------------
hello(internal, #client_hello{client_version = ClientVersion,
- extensions = #hello_extensions{ec_point_formats = EcPointFormats,
- elliptic_curves = EllipticCurves}} = Hello,
- State = #state{connection_states = ConnectionStates0,
- port = Port, session = #session{own_certificate = Cert} = Session0,
- renegotiation = {Renegotiation, _},
- session_cache = Cache,
- session_cache_cb = CacheCb,
- negotiated_protocol = CurrentProtocol,
- key_algorithm = KeyExAlg,
- ssl_options = SslOpts}) ->
+ extensions = #hello_extensions{ec_point_formats = EcPointFormats,
+ elliptic_curves = EllipticCurves}} = Hello,
+ #state{connection_states = ConnectionStates0,
+ port = Port, session = #session{own_certificate = Cert} = Session0,
+ renegotiation = {Renegotiation, _},
+ session_cache = Cache,
+ session_cache_cb = CacheCb,
+ negotiated_protocol = CurrentProtocol,
+ key_algorithm = KeyExAlg,
+ ssl_options = SslOpts} = State) ->
case tls_handshake:hello(Hello, SslOpts, {Port, Session0, Cache, CacheCb,
ConnectionStates0, Cert, KeyExAlg}, Renegotiation) of
@@ -311,7 +311,7 @@ cipher(Type, Event, State) ->
connection(info, Event, State) ->
handle_info(Event, connection, State);
connection(internal, #hello_request{},
- #state{host = Host, port = Port,
+ #state{role = client, host = Host, port = Port,
session = #session{own_certificate = Cert} = Session0,
session_cache = Cache, session_cache_cb = CacheCb,
ssl_options = SslOpts,
@@ -326,14 +326,16 @@ connection(internal, #hello_request{},
= Hello#client_hello.session_id}}),
next_event(hello, Record, State);
connection(internal, #client_hello{} = Hello,
- #state{role = server, allow_renegotiate = true} = State) ->
+ #state{role = server, allow_renegotiate = true} = State0) ->
%% Mitigate Computational DoS attack
%% http://www.educatedguesswork.org/2011/10/ssltls_and_computational_dos.html
%% http://www.thc.org/thc-ssl-dos/ Rather than disabling client
%% initiated renegotiation we will disallow many client initiated
%% renegotiations immediately after each other.
erlang:send_after(?WAIT_TO_ALLOW_RENEGOTIATION, self(), allow_renegotiate),
- {next_state, hello, State#state{allow_renegotiate = false}, [{next_event, internal, Hello}]};
+ {Record, State} = next_record(State0#state{allow_renegotiate = false,
+ renegotiation = {true, peer}}),
+ next_event(hello, Record, State, [{next_event, internal, Hello}]);
connection(internal, #client_hello{},
#state{role = server, allow_renegotiate = false} = State0) ->
Alert = ?ALERT_REC(?WARNING, ?NO_RENEGOTIATION),
@@ -398,39 +400,12 @@ handle_common_event(internal, #ssl_tls{type = ?HANDSHAKE, fragment = Data},
StateName, #state{protocol_buffers =
#protocol_buffers{tls_handshake_buffer = Buf0} = Buffers,
negotiated_version = Version} = State0) ->
-
- Handle =
- fun({#hello_request{} = Packet, _}, {connection, HState}) ->
- %% This message should not be included in handshake
- %% message hashes. Starts new handshake (renegotiation)
- Hs0 = ssl_handshake:init_handshake_history(),
- {HState#state{tls_handshake_history = Hs0,
- renegotiation = {true, peer}},
- {next_event, internal, Packet}};
- ({#hello_request{}, _}, {next_state, _SName, HState}) ->
- %% This message should not be included in handshake
- %% message hashes. Already in negotiation so it will be ignored!
- {HState, []};
- ({#client_hello{} = Packet, Raw}, {connection, HState0}) ->
- HState = handle_sni_extension(Packet, HState0),
- Version = Packet#client_hello.client_version,
- Hs0 = ssl_handshake:init_handshake_history(),
- Hs1 = ssl_handshake:update_handshake_history(Hs0, Raw),
- {HState#state{tls_handshake_history = Hs1,
- renegotiation = {true, peer}},
- {next_event, internal, Packet}};
-
- ({Packet, Raw}, {_SName, HState0 = #state{tls_handshake_history=Hs0}}) ->
- HState = handle_sni_extension(Packet, HState0),
- Hs1 = ssl_handshake:update_handshake_history(Hs0, Raw),
- {HState#state{tls_handshake_history=Hs1}, {next_event, internal, Packet}}
- end,
- try
+ try
{Packets, Buf} = tls_handshake:get_tls_handshake(Version,Data,Buf0),
- State1 = State0#state{protocol_buffers =
- Buffers#protocol_buffers{tls_packets = Packets,
- tls_handshake_buffer = Buf}},
- {State, Events} = tls_handshake_events(Handle, StateName, State1, []),
+ State =
+ State0#state{protocol_buffers =
+ Buffers#protocol_buffers{tls_handshake_buffer = Buf}},
+ Events = tls_handshake_events(Packets),
case StateName of
connection ->
ssl_connection:hibernate_after(StateName, State, Events);
@@ -779,24 +754,12 @@ send_or_reply(_, Pid, _From, Data) ->
send_user(Pid, Msg) ->
Pid ! Msg.
-tls_handshake_events(Handle, StateName,
- #state{protocol_buffers =
- #protocol_buffers{tls_packets = [Packet]} = Buffers} = State0, Acc) ->
- {State, Event} = Handle(Packet, {StateName,
- State0#state{protocol_buffers =
- Buffers#protocol_buffers{tls_packets = []}}}),
- {State, lists:reverse([Event |Acc])};
-tls_handshake_events(Handle, StateName,
- #state{protocol_buffers =
- #protocol_buffers{tls_packets =
- [Packet | Packets]} = Buffers} = State0, Acc) ->
- {State, Event} = Handle(Packet, {StateName, State0#state{protocol_buffers =
- Buffers#protocol_buffers{tls_packets =
- Packets}}}),
- tls_handshake_events(Handle, StateName, State, [Event | Acc]);
-
-tls_handshake_events(_Handle, _, #state{}, _) ->
- throw(?ALERT_REC(?FATAL, ?HANDSHAKE_FAILURE)).
+tls_handshake_events([]) ->
+ throw(?ALERT_REC(?FATAL, ?HANDSHAKE_FAILURE, malformed_handshake));
+tls_handshake_events(Packets) ->
+ lists:map(fun(Packet) ->
+ {next_event, internal, {handshake, Packet}}
+ end, Packets).
write_application_data(Data0, From,
#state{socket = Socket,
@@ -1065,5 +1028,5 @@ handle_sni_extension(#client_hello{extensions = HelloExtensions}, State0) ->
}
end
end;
-handle_sni_extension(_, State0) ->
- State0.
+handle_sni_extension(_, State) ->
+ State.