aboutsummaryrefslogtreecommitdiffstats
path: root/lib/ssl/src
diff options
context:
space:
mode:
Diffstat (limited to 'lib/ssl/src')
-rw-r--r--lib/ssl/src/dtls_connection.erl65
-rw-r--r--lib/ssl/src/ssl_alert.erl28
-rw-r--r--lib/ssl/src/ssl_alert.hrl4
-rw-r--r--lib/ssl/src/ssl_connection.erl23
4 files changed, 98 insertions, 22 deletions
diff --git a/lib/ssl/src/dtls_connection.erl b/lib/ssl/src/dtls_connection.erl
index b6aafc3fa4..ff3e69bae5 100644
--- a/lib/ssl/src/dtls_connection.erl
+++ b/lib/ssl/src/dtls_connection.erl
@@ -276,7 +276,9 @@ init({call, _} = Type, Event, #state{role = server, transport_cb = gen_udp} = St
Result = ssl_connection:init(Type, Event,
State#state{flight_state = {retransmit, ?INITIAL_RETRANSMIT_TIMEOUT},
protocol_specific = #{current_cookie_secret => dtls_v1:cookie_secret(),
- previous_cookie_secret => <<>>}},
+ previous_cookie_secret => <<>>,
+ ignored_alerts => 0,
+ max_ignored_alerts => 10}},
?MODULE),
erlang:send_after(dtls_v1:cookie_timeout(), self(), new_cookie_secret),
Result;
@@ -374,7 +376,7 @@ hello(internal, #server_hello{} = Hello,
ssl_options = SslOptions} = State) ->
case dtls_handshake:hello(Hello, SslOptions, ConnectionStates0, Renegotiation) of
#alert{} = Alert ->
- ssl_connection:handle_own_alert(Alert, ReqVersion, hello, State);
+ handle_own_alert(Alert, ReqVersion, hello, State);
{Version, NewId, ConnectionStates, ProtoExt, Protocol} ->
ssl_connection:handle_session(Hello,
Version, NewId, ConnectionStates, ProtoExt, Protocol, State)
@@ -546,7 +548,7 @@ handle_call(Event, From, StateName, State) ->
handle_common_event(internal, #alert{} = Alert, StateName,
#state{negotiated_version = Version} = State) ->
- ssl_connection:handle_own_alert(Alert, Version, StateName, State);
+ handle_own_alert(Alert, Version, StateName, State);
%%% DTLS record protocol level handshake messages
handle_common_event(internal, #ssl_tls{type = ?HANDSHAKE,
fragment = Data},
@@ -565,7 +567,7 @@ handle_common_event(internal, #ssl_tls{type = ?HANDSHAKE,
State#state{unprocessed_handshake_events = unprocessed_events(Events)}, Events}
end
catch throw:#alert{} = Alert ->
- ssl_connection:handle_own_alert(Alert, Version, StateName, State0)
+ handle_own_alert(Alert, Version, StateName, State0)
end;
%%% DTLS record protocol level application data messages
handle_common_event(internal, #ssl_tls{type = ?APPLICATION_DATA, fragment = Data}, StateName, State) ->
@@ -580,7 +582,7 @@ handle_common_event(internal, #ssl_tls{type = ?ALERT, fragment = EncAlerts}, Sta
Alerts = [_|_] ->
handle_alerts(Alerts, {next_state, StateName, State});
#alert{} = Alert ->
- ssl_connection:handle_own_alert(Alert, Version, StateName, State)
+ handle_own_alert(Alert, Version, StateName, State)
end;
%% Ignore unknown TLS record level protocol messages
handle_common_event(internal, #ssl_tls{type = _Unknown}, StateName, State) ->
@@ -632,7 +634,7 @@ handle_client_hello(#client_hello{client_version = ClientVersion} = Hello,
case dtls_handshake:hello(Hello, SslOpts, {Port, Session0, Cache, CacheCb,
ConnectionStates0, Cert, KeyExAlg}, Renegotiation) of
#alert{} = Alert ->
- ssl_connection:handle_own_alert(Alert, ClientVersion, hello, State0);
+ handle_own_alert(Alert, ClientVersion, hello, State0);
{Version, {Type, Session},
ConnectionStates, Protocol0, ServerHelloExt, HashSign} ->
Protocol = case Protocol0 of
@@ -967,3 +969,54 @@ unprocessed_events(Events) ->
%% process more TLS-records received on the socket.
erlang:length(Events)-1.
+handle_own_alert(Alert, Version, StateName, #state{transport_cb = gen_udp,
+ role = Role,
+ ssl_options = Options} = State0) ->
+ case ignore_alert(Alert, State0) of
+ {true, State} ->
+ log_ignore_alert(Options#ssl_options.log_alert, StateName, Alert, Role),
+ {next_state, StateName, State};
+ {false, State} ->
+ ssl_connection:handle_own_alert(Alert, Version, StateName, State)
+ end;
+handle_own_alert(Alert, Version, StateName, State) ->
+ ssl_connection:handle_own_alert(Alert, Version, StateName, State).
+
+
+ignore_alert(#alert{level = ?FATAL}, #state{protocol_specific = #{ignored_alerts := N,
+ max_ignored_alerts := N}} = State) ->
+ {false, State};
+ignore_alert(#alert{level = ?FATAL} = Alert,
+ #state{protocol_specific = #{ignored_alerts := N} = PS} = State) ->
+ case is_ignore_alert(Alert) of
+ true ->
+ {true, State#state{protocol_specific = PS#{ignored_alerts => N+1}}};
+ false ->
+ {false, State}
+ end;
+ignore_alert(_, State) ->
+ {false, State}.
+
+%% RFC 6347 4.1.2.7. Handling Invalid Records
+%% recommends to silently ignore invalid DTLS records when
+%% upd is the transport. Note we do not support compression so no need
+%% include ?DECOMPRESSION_FAILURE
+is_ignore_alert(#alert{description = ?BAD_RECORD_MAC}) ->
+ true;
+is_ignore_alert(#alert{description = ?RECORD_OVERFLOW}) ->
+ true;
+is_ignore_alert(#alert{description = ?DECODE_ERROR}) ->
+ true;
+is_ignore_alert(#alert{description = ?DECRYPT_ERROR}) ->
+ true;
+is_ignore_alert(#alert{description = ?ILLEGAL_PARAMETER}) ->
+ true;
+is_ignore_alert(_) ->
+ false.
+
+log_ignore_alert(true, StateName, Alert, Role) ->
+ Txt = ssl_alert:alert_txt(Alert),
+ error_logger:format("DTLS over UDP ~p: In state ~p ignored to send ALERT ~s as DoS-attack mitigation \n",
+ [Role, StateName, Txt]);
+log_ignore_alert(false, _, _,_) ->
+ ok.
diff --git a/lib/ssl/src/ssl_alert.erl b/lib/ssl/src/ssl_alert.erl
index b923785e17..db415a3666 100644
--- a/lib/ssl/src/ssl_alert.erl
+++ b/lib/ssl/src/ssl_alert.erl
@@ -32,7 +32,7 @@
-include("ssl_record.hrl").
-include("ssl_internal.hrl").
--export([decode/1, alert_txt/1, reason_code/2]).
+-export([decode/1, own_alert_txt/1, alert_txt/1, reason_code/2]).
%%====================================================================
%% Internal application API
@@ -60,12 +60,28 @@ reason_code(#alert{description = Description}, _) ->
{tls_alert, string:to_lower(description_txt(Description))}.
%%--------------------------------------------------------------------
+-spec own_alert_txt(#alert{}) -> string().
+%%
+%% Description: Returns the error string for given alert generated
+%% by the erlang implementation.
+%%--------------------------------------------------------------------
+own_alert_txt(#alert{level = Level, description = Description, where = {Mod,Line}, reason = undefined, role = Role}) ->
+ "at " ++ Mod ++ ":" ++ integer_to_list(Line) ++ " generated " ++ string:to_upper(atom_to_list(Role)) ++ " ALERT: " ++
+ level_txt(Level) ++ description_txt(Description);
+own_alert_txt(#alert{reason = Reason} = Alert) ->
+ BaseTxt = own_alert_txt(Alert#alert{reason = undefined}),
+ FormatDepth = 9, % Some limit on printed representation of an error
+ ReasonTxt = lists:flatten(io_lib:format("~P", [Reason, FormatDepth])),
+ BaseTxt ++ " - " ++ ReasonTxt.
+
+%%--------------------------------------------------------------------
-spec alert_txt(#alert{}) -> string().
%%
-%% Description: Returns the error string for given alert.
+%% Description: Returns the error string for given alert received from
+%% the peer.
%%--------------------------------------------------------------------
-alert_txt(#alert{level = Level, description = Description, where = {Mod,Line}, reason = undefined, role = Role}) ->
- "at " ++ Mod ++ ":" ++ integer_to_list(Line) ++ " " ++ string:to_upper(atom_to_list(Role)) ++ " ALERT: " ++
+alert_txt(#alert{level = Level, description = Description, reason = undefined, role = Role}) ->
+ "received " ++ string:to_upper(atom_to_list(Role)) ++ " ALERT: " ++
level_txt(Level) ++ description_txt(Description);
alert_txt(#alert{reason = Reason} = Alert) ->
BaseTxt = alert_txt(Alert#alert{reason = undefined}),
@@ -103,8 +119,8 @@ description_txt(?UNEXPECTED_MESSAGE) ->
"Unexpected Message";
description_txt(?BAD_RECORD_MAC) ->
"Bad Record MAC";
-description_txt(?DECRYPTION_FAILED) ->
- "Decryption Failed";
+description_txt(?DECRYPTION_FAILED_RESERVED) ->
+ "Decryption Failed Reserved";
description_txt(?RECORD_OVERFLOW) ->
"Record Overflow";
description_txt(?DECOMPRESSION_FAILURE) ->
diff --git a/lib/ssl/src/ssl_alert.hrl b/lib/ssl/src/ssl_alert.hrl
index 1aabb6c55a..35670edea5 100644
--- a/lib/ssl/src/ssl_alert.hrl
+++ b/lib/ssl/src/ssl_alert.hrl
@@ -40,7 +40,7 @@
%% close_notify(0),
%% unexpected_message(10),
%% bad_record_mac(20),
-%% decryption_failed(21),
+%% decryption_failed_reserved(21),
%% record_overflow(22),
%% decompression_failure(30),
%% handshake_failure(40),
@@ -78,7 +78,7 @@
-define(CLOSE_NOTIFY, 0).
-define(UNEXPECTED_MESSAGE, 10).
-define(BAD_RECORD_MAC, 20).
--define(DECRYPTION_FAILED, 21).
+-define(DECRYPTION_FAILED_RESERVED, 21).
-define(RECORD_OVERFLOW, 22).
-define(DECOMPRESSION_FAILURE, 30).
-define(HANDSHAKE_FAILURE, 40).
diff --git a/lib/ssl/src/ssl_connection.erl b/lib/ssl/src/ssl_connection.erl
index 5cd66387ae..b031d3d47b 100644
--- a/lib/ssl/src/ssl_connection.erl
+++ b/lib/ssl/src/ssl_connection.erl
@@ -1144,7 +1144,8 @@ handle_alert(#alert{level = ?FATAL} = Alert, StateName,
port = Port, session = Session, user_application = {_Mon, Pid},
role = Role, socket_options = Opts, tracker = Tracker}) ->
invalidate_session(Role, Host, Port, Session),
- log_alert(SslOpts#ssl_options.log_alert, Connection:protocol_name(), StateName, Alert#alert{role = opposite_role(Role)}),
+ log_alert(SslOpts#ssl_options.log_alert, Role, Connection:protocol_name(),
+ StateName, Alert#alert{role = opposite_role(Role)}),
alert_user(Transport, Tracker, Socket, StateName, Opts, Pid, From, Alert, Role, Connection),
{stop, normal};
@@ -1155,7 +1156,8 @@ handle_alert(#alert{level = ?WARNING, description = ?CLOSE_NOTIFY} = Alert,
handle_alert(#alert{level = ?WARNING, description = ?NO_RENEGOTIATION} = Alert, StateName,
#state{role = Role, ssl_options = SslOpts, protocol_cb = Connection, renegotiation = {true, internal}} = State) ->
- log_alert(SslOpts#ssl_options.log_alert, Connection:protocol_name(), StateName, Alert#alert{role = opposite_role(Role)}),
+ log_alert(SslOpts#ssl_options.log_alert, Role,
+ Connection:protocol_name(), StateName, Alert#alert{role = opposite_role(Role)}),
handle_normal_shutdown(Alert, StateName, State),
{stop, {shutdown, peer_close}};
@@ -1163,7 +1165,8 @@ handle_alert(#alert{level = ?WARNING, description = ?NO_RENEGOTIATION} = Alert,
#state{role = Role,
ssl_options = SslOpts, renegotiation = {true, From},
protocol_cb = Connection} = State0) ->
- log_alert(SslOpts#ssl_options.log_alert, Connection:protocol_name(), StateName, Alert#alert{role = opposite_role(Role)}),
+ log_alert(SslOpts#ssl_options.log_alert, Role,
+ Connection:protocol_name(), StateName, Alert#alert{role = opposite_role(Role)}),
gen_statem:reply(From, {error, renegotiation_rejected}),
{Record, State} = Connection:next_record(State0),
%% Go back to connection!
@@ -1172,7 +1175,8 @@ handle_alert(#alert{level = ?WARNING, description = ?NO_RENEGOTIATION} = Alert,
%% Gracefully log and ignore all other warning alerts
handle_alert(#alert{level = ?WARNING} = Alert, StateName,
#state{ssl_options = SslOpts, protocol_cb = Connection, role = Role} = State0) ->
- log_alert(SslOpts#ssl_options.log_alert, Connection:protocol_name(), StateName, Alert#alert{role = opposite_role(Role)}),
+ log_alert(SslOpts#ssl_options.log_alert, Role,
+ Connection:protocol_name(), StateName, Alert#alert{role = opposite_role(Role)}),
{Record, State} = Connection:next_record(State0),
Connection:next_event(StateName, Record, State).
@@ -2372,10 +2376,13 @@ alert_user(Transport, Tracker, Socket, Active, Pid, From, Alert, Role, Connectio
Transport, Socket, Connection, Tracker), ReasonCode})
end.
-log_alert(true, ProtocolName, StateName, Alert) ->
+log_alert(true, Role, ProtocolName, StateName, #alert{role = Role} = Alert) ->
+ Txt = ssl_alert:own_alert_txt(Alert),
+ error_logger:info_report(io_lib:format("~s ~p: In state ~p ~s\n", [ProtocolName, Role, StateName, Txt]));
+log_alert(true, Role, ProtocolName, StateName, Alert) ->
Txt = ssl_alert:alert_txt(Alert),
- error_logger:format("~s: In state ~p ~s\n", [ProtocolName, StateName, Txt]);
-log_alert(false, _, _, _) ->
+ error_logger:info_report(io_lib:format("~s ~p: In state ~p ~s\n", [ProtocolName, Role, StateName, Txt]));
+log_alert(false, _, _, _, _) ->
ok.
handle_own_alert(Alert, Version, StateName,
@@ -2393,7 +2400,7 @@ handle_own_alert(Alert, Version, StateName,
ignore
end,
try %% Try to tell the local user
- log_alert(SslOpts#ssl_options.log_alert, Connection:protocol_name(), StateName, Alert#alert{role = Role}),
+ log_alert(SslOpts#ssl_options.log_alert, Role, Connection:protocol_name(), StateName, Alert#alert{role = Role}),
handle_normal_shutdown(Alert,StateName, State)
catch _:_ ->
ok