File 3861-ssl-ssl_trace-updates.patch of Package erlang
From 8f205c2b160c6f4f1833464cfc09630d6fa80e19 Mon Sep 17 00:00:00 2001
From: Jakub Witczak <kuba@erlang.org>
Date: Sat, 7 Jan 2023 15:39:45 +0100
Subject: [PATCH] ssl: ssl_trace updates
- add tc_check_profiles test to ssl_trace_SUITE
- return ok tuple from ssl_trace:start
- OCSP, cert trace profiles
- key update trace profile
- ssl_trace:write/2
- no more budget message
- add tls_sender:init to rle trace profile
- hibernate trace profile
- ssn trace profile
---
lib/ssl/src/dtls_connection.erl | 12 ++
lib/ssl/src/ssl.erl | 5 +
lib/ssl/src/ssl_certificate.erl | 30 ++++
lib/ssl/src/ssl_gen_statem.erl | 17 ++-
lib/ssl/src/ssl_handshake.erl | 14 ++
lib/ssl/src/ssl_trace.erl | 163 +++++++++++++++++-----
lib/ssl/src/tls_connection.erl | 13 +-
lib/ssl/src/tls_connection_1_3.erl | 17 +++
lib/ssl/src/tls_dtls_connection.erl | 11 ++
lib/ssl/src/tls_sender.erl | 50 +++++++
lib/ssl/src/tls_server_session_ticket.erl | 98 ++++++++++++-
lib/ssl/src/tls_v1.erl | 23 +++
lib/ssl/test/ssl_test_lib.erl | 9 ++
lib/ssl/test/ssl_trace_SUITE.erl | 96 ++++++++++---
14 files changed, 494 insertions(+), 64 deletions(-)
diff --git a/lib/ssl/src/dtls_connection.erl b/lib/ssl/src/dtls_connection.erl
index 94576a577f..eab76d2ef2 100644
--- a/lib/ssl/src/dtls_connection.erl
+++ b/lib/ssl/src/dtls_connection.erl
@@ -155,6 +155,9 @@
code_change/4,
format_status/2]).
+%% Tracing
+-export([handle_trace/3]).
+
%%====================================================================
%% Internal application API
%%====================================================================
@@ -862,3 +865,12 @@ is_time_to_renegotiate(N, M) when N < M->
is_time_to_renegotiate(_,_) ->
true.
+%%%################################################################
+%%%#
+%%%# Tracing
+%%%#
+handle_trace(hbn,
+ {call, {?MODULE, connection,
+ [_Type = info, Event, _State]}},
+ Stack) ->
+ {io_lib:format("Type = info Event = ~W ", [Event, 10]), Stack}.
diff --git a/lib/ssl/src/ssl.erl b/lib/ssl/src/ssl.erl
index 37bcc36c12..e77c14bdaa 100644
--- a/lib/ssl/src/ssl.erl
+++ b/lib/ssl/src/ssl.erl
@@ -2764,6 +2764,11 @@ unambiguous_path(Value) ->
%%%#
%%%# Tracing
%%%#
+handle_trace(csp, {return_from, {?MODULE, opt_ocsp, 3}, Return}, Stack) ->
+ #{ocsp_stapling := Stapling, ocsp_nonce := Nonce,
+ ocsp_responder_certs := Certs} = Return,
+ {io_lib:format("Stapling = ~w Nonce = ~W Certs = ~W",
+ [Stapling, Nonce, 5, Certs, 5]), Stack};
handle_trace(rle, {call, {?MODULE, listen, Args}}, Stack0) ->
Role = server,
{io_lib:format("(*~w) Args = ~W", [Role, Args, 10]), [{role, Role} | Stack0]};
diff --git a/lib/ssl/src/ssl_certificate.erl b/lib/ssl/src/ssl_certificate.erl
index 2e2b43f564..d0a9ca875e 100644
--- a/lib/ssl/src/ssl_certificate.erl
+++ b/lib/ssl/src/ssl_certificate.erl
@@ -85,6 +85,9 @@
available_cert_key_pairs/2
]).
+%% Tracing
+-export([handle_trace/3]).
+
%%====================================================================
%% Internal application API
%%====================================================================
@@ -825,3 +828,30 @@ cert_issuers(OTPCerts) ->
cert_auth_member(ChainSubjects, CertAuths) ->
CommonAuthorities = sets:intersection(sets:from_list(ChainSubjects), sets:from_list(CertAuths)),
not sets:is_empty(CommonAuthorities).
+
+%%%################################################################
+%%%#
+%%%# Tracing
+%%%#
+handle_trace(crt,
+ {call, {?MODULE, validate, [Cert, StatusOrExt| _]}}, Stack) ->
+ {io_lib:format("[~W] StatusOrExt = ~W", [Cert, 3, StatusOrExt, 10]), Stack};
+handle_trace(crt, {call, {?MODULE, verify_cert_extensions,
+ [Cert,
+ _UserState,
+ [], _Context]}}, Stack) ->
+ {io_lib:format(" no more extensions [~W]", [Cert, 3]), Stack};
+handle_trace(crt, {call, {?MODULE, verify_cert_extensions,
+ [Cert,
+ #{ocsp_responder_certs := _ResponderCerts,
+ ocsp_state := OcspState,
+ issuer := Issuer} = _UserState,
+ [#certificate_status{response = OcspResponsDer} |
+ _Exts], _Context]}}, Stack) ->
+ {io_lib:format("#2 OcspState = ~W Issuer = [~W] OcspResponsDer = ~W [~W]",
+ [OcspState, 10, Issuer, 3, OcspResponsDer, 2, Cert, 3]),
+ Stack};
+handle_trace(crt, {return_from,
+ {ssl_certificate, verify_cert_extensions, 4},
+ {valid, #{issuer := Issuer}}}, Stack) ->
+ {io_lib:format(" extensions valid Issuer = ~W", [Issuer, 3]), Stack}.
diff --git a/lib/ssl/src/ssl_gen_statem.erl b/lib/ssl/src/ssl_gen_statem.erl
index a9e16a309d..9d21ac154c 100644
--- a/lib/ssl/src/ssl_gen_statem.erl
+++ b/lib/ssl/src/ssl_gen_statem.erl
@@ -2225,4 +2225,19 @@ maybe_generate_client_shares(_) ->
%%%#
handle_trace(rle,
{call, {?MODULE, init, Args = [[Role | _]]}}, Stack0) ->
- {io_lib:format("(*~w) Args = ~W", [Role, Args, 3]), [{role, Role} | Stack0]}.
+ {io_lib:format("(*~w) Args = ~W", [Role, Args, 3]), [{role, Role} | Stack0]};
+handle_trace(hbn,
+ {call, {?MODULE, hibernate_after,
+ [_StateName = connection, State, Actions]}},
+ Stack) ->
+ #state{ssl_options= #{hibernate_after := HibernateAfter}} = State,
+ {io_lib:format("* * * maybe hibernating in ~w ms * * * Actions = ~W ",
+ [HibernateAfter, Actions, 10]), Stack};
+handle_trace(hbn,
+ {return_from, {?MODULE, hibernate_after, 3},
+ {Cmd, Arg,_State, Actions}},
+ Stack) ->
+ {io_lib:format("Cmd = ~w Arg = ~w Actions = ~W", [Cmd, Arg, Actions, 10]), Stack};
+handle_trace(hbn,
+ {call, {?MODULE, handle_common_event, [timeout, hibernate, connection | _]}}, Stack) ->
+ {io_lib:format("* * * hibernating * * *", []), Stack}.
diff --git a/lib/ssl/src/ssl_handshake.erl b/lib/ssl/src/ssl_handshake.erl
index 56a1ca81b9..9293a9f742 100644
--- a/lib/ssl/src/ssl_handshake.erl
+++ b/lib/ssl/src/ssl_handshake.erl
@@ -89,6 +89,8 @@
validation_fun_and_state/4,
path_validation_alert/1]).
+%% Tracing
+-export([handle_trace/3]).
%%====================================================================
%% Create handshake messages
%%====================================================================
@@ -3894,3 +3896,15 @@ extkey_oids_to_names([?'anyExtendedKeyUsage'| Rest], Acc) ->
extkey_oids_to_names(Rest, ["anyExtendedKeyUsage"| Acc]);
extkey_oids_to_names([Other| Rest], Acc) ->
extkey_oids_to_names(Rest, [Other | Acc]).
+
+%%%################################################################
+%%%#
+%%%# Tracing
+%%%#
+handle_trace(csp,
+ {call, {?MODULE, maybe_add_certificate_status_request,
+ [_Version, #{ocsp_stapling := true},
+ _OcspNonce, _HelloExtensions]}},
+ Stack) ->
+ {io_lib:format("#1 ADDING certificate status request",
+ []), Stack}.
diff --git a/lib/ssl/src/ssl_trace.erl b/lib/ssl/src/ssl_trace.erl
index c40381b78d..325e31d1fd 100644
--- a/lib/ssl/src/ssl_trace.erl
+++ b/lib/ssl/src/ssl_trace.erl
@@ -21,15 +21,16 @@
%%
%%% Purpose:
-%%% This module implements support for using the Erlang trace in a simple way for ssl
-%%% tracing.
+%%% This module implements support for using the Erlang trace in a simple way
+%%% for ssl tracing.
%%%
-%%% Begin the session with ssl_trace:start(). This will do a dbg:start() if needed and
-%%% then dbg:p/2 to set some flags.
+%%% Begin the session with ssl_trace:start(). This will do a dbg:start()
+%%% if needed and then dbg:p/2 to set some flags.
%%%
-%%% Next select trace profiles to activate: for example plain text printouts of messages
-%%% sent or received. This is switched on and off with ssl_trace:on(TraceProfile(s)) and
-%%% ssl_trace:off(TraceProfile(s)). For example:
+%%% Next select trace profiles to activate: for example plain text
+%%% printouts of messages sent or received. This is switched on and off with
+%%% ssl_trace:on(TraceProfile(s)) and ssl_trace:off(TraceProfile(s)).
+%%% For example:
%%%
%%% ssl_trace:on(rle) -- switch on printing role traces
%%% ssl_trace:on([api, rle]) -- switch on printing role and api traces
@@ -37,23 +38,26 @@
%%%
%%% To switch, use the off/0 or off/1 function in the same way, for example:
%%%
-%%% ssl_trace:off(api) -- switch off api tracing, but keep all other
+%%% ssl_trace:off(api) -- switch off api tracing, keep all other
%%% ssl_trace:off() -- switch off all ssl tracing
%%%
-%%% Present the trace result with some other method than the default io:format/2:
+%%% Present the trace result with some other method than the default
+%%% io:format/2:
%%% ssl_trace:start(fun(Format,Args) ->
%%% my_special( io_lib:format(Format,Args) )
%%% end)
-%%% Write to ssl_trace.txt file with budget of 1000 trace entries:
+%%% Write traces to text file with budget of 1000 trace entries:
%%% ssl_trace:start(IoFmt, [file, {budget, 1000}])
%%%
-module(ssl_trace).
-export([start/0, start/1, start/2, stop/0, on/0, on/1, off/0, off/1, is_on/0,
- is_off/0]).
+ is_off/0, write/2]).
-export([init/1, handle_call/3, handle_cast/2, handle_info/2]).
%% Internal apply_after:
-export([ets_delete/2]).
+%% Test purpose
+-export([trace_profiles/0]).
-behaviour(gen_server).
-define(SERVER, ?MODULE).
@@ -64,7 +68,8 @@
-record(state, {
file = undefined,
types_on = [],
- io_device = undefined
+ io_device = undefined,
+ write_fun
}).
%%%----------------------------------------------------------------
@@ -78,12 +83,14 @@ start(IoFmtFun) when is_function(IoFmtFun,2) ; is_function(IoFmtFun,3) ->
start(IoFmtFun, TraceOpts) when is_function(IoFmtFun,2);
is_function(IoFmtFun,3);
is_list(TraceOpts) ->
- {ok, Pid} = gen_server:start({local,?SERVER}, ?MODULE, TraceOpts, []),
+ WriteFun = fun(F,A,S) -> IoFmtFun(F,A), S end,
+ {ok, Pid} = gen_server:start({local,?SERVER}, ?MODULE,
+ [{write_fun, WriteFun}, TraceOpts], []),
true = is_process_alive(Pid),
catch dbg:start(),
start_tracer(IoFmtFun, TraceOpts),
dbg:p(all, [timestamp, c]),
- get_all_trace_profiles().
+ {ok, get_all_trace_profiles()}.
stop() ->
try
@@ -112,15 +119,18 @@ is_on() ->
is_off() ->
get_all_trace_profiles() -- is_on().
+write(Fmt, Args) ->
+ gen_server:call(?SERVER, {write, Fmt, Args}, ?CALL_TIMEOUT).
+
%%%----------------------------------------------------------------
-init(_) ->
+init(Args) ->
try
ets:new(?MODULE, [public, named_table])
catch
exit:badarg ->
ok
end,
- {ok, #state{}}.
+ {ok, #state{write_fun = proplists:get_value(write_fun, Args)}}.
handle_call({switch,on,Profiles}, _From, State) ->
[enable_profile(P) || P <- Profiles],
@@ -143,6 +153,11 @@ handle_call(file_close, _From, #state{io_device = IODevice} = State) ->
ok
end,
{reply, ok, State#state{io_device = undefined}};
+handle_call({write, Fmt, Args}, _From, State) ->
+ #state{io_device = IODevice, write_fun = WriteFun0} = State,
+ WriteFun = get_write_fun(IODevice, WriteFun0),
+ WriteFun(Fmt, Args, processed),
+ {reply, ok, State};
handle_call(C, _From, State) ->
io:format('*** Unknown call: ~p~n',[C]),
{reply, {error,{unknown_call,C}}, State}.
@@ -189,7 +204,12 @@ start_tracer(WriteFun, TraceOpts) when is_function(WriteFun,3) ->
Acc0 = [{budget, proplists:get_value(budget, TraceOpts, ?TRACE_BUDGET)}],
Acc1 = case lists:member(file, TraceOpts) of
true ->
- [{file, ?TRACE_FILE} | Acc0];
+ TraceFile =
+ case init:get_argument(ssl_trace_file) of
+ {ok, [[Path]]} -> Path;
+ _ -> ?TRACE_FILE
+ end,
+ [{file, TraceFile} | Acc0];
_ ->
Acc0
end,
@@ -214,16 +234,7 @@ start_dbg_tracer(WriteFun, InitHandlerAcc0) when is_function(WriteFun, 3) ->
try_handle_trace(ProfilesOn, Arg, WriteFun0, HandlerAcc) ->
IODevice = proplists:get_value(io_device, HandlerAcc),
- WriteFun =
- case is_pid(IODevice) of
- true ->
- fun(Format, Args, Return) ->
- ok = io:format(IODevice, Format, Args),
- Return
- end;
- false ->
- WriteFun0
- end,
+ WriteFun = get_write_fun(IODevice, WriteFun0),
Budget0 = proplists:get_value(budget, HandlerAcc, 0),
Timestamp = trace_ts(Arg),
Pid = trace_pid(Arg),
@@ -242,13 +253,13 @@ try_handle_trace(ProfilesOn, Arg, WriteFun0, HandlerAcc) ->
{skip, NewProcessStack} ->
%% Don't try to process this later
put_proc_stack(Pid, NewProcessStack),
- reduce_budget(BAcc);
+ reduce_budget(BAcc, WriteFun);
{Txt, NewProcessStack} when is_list(Txt) ->
put_proc_stack(Pid, NewProcessStack),
write_txt(WriteFun, Timestamp, Pid,
common_prefix(TraceInfo, Role,
Profile) ++ Txt),
- reduce_budget(BAcc)
+ reduce_budget(BAcc, WriteFun)
catch
_:_ ->
%% not processed by custom handler
@@ -268,15 +279,33 @@ try_handle_trace(ProfilesOn, Arg, WriteFun0, HandlerAcc) ->
common_prefix(TraceInfo, Role,
" ")]),
TraceInfo, 7], processed),
- reduce_budget(Budget0);
+ reduce_budget(Budget0, WriteFun);
_ ->
Budget1
end,
[{budget, Budget2} | proplists:delete(budget, HandlerAcc)].
-reduce_budget(B) when B>1 ->
+get_write_fun(IODevice, WriteFun0) ->
+ case is_pid(IODevice) of
+ true ->
+ fun(Format, Args, Return) ->
+ ok = io:format(IODevice, Format, Args),
+ Return
+ end;
+ false ->
+ WriteFun0
+ end.
+
+reduce_budget(B, _) when B > 1 ->
B - 1;
-reduce_budget(_B) ->
+reduce_budget(_, WriteFun) ->
+ case get(no_budget_msg_written) of
+ undefined ->
+ WriteFun("No more trace budget!~n", [], processed),
+ put(no_budget_msg_written, true);
+ _ ->
+ ok
+ end,
0.
write_txt(WriteFun, Timestamp, Pid, Txt) when is_list(Txt) ->
@@ -394,10 +423,74 @@ trace_profiles() ->
{ssl_gen_statem,
[{initial_hello,3}, {connect, 8}, {close, 2}, {terminate_alert, 1}]}
]},
- {rle, %%role
+ {csp, %% OCSP
+ fun(M, F, A) -> dbg:tpl(M, F, A, x) end,
+ fun(M, F, A) -> dbg:ctpl(M, F, A) end,
+ [{ssl_handshake, [{maybe_add_certificate_status_request, 4},
+ {client_hello_extensions, 10}, {cert_status_check, 5},
+ {get_ocsp_responder_list, 1}, {handle_ocsp_extension, 2},
+ {handle_server_hello_extensions, 10},
+ {handle_client_hello_extensions, 10},
+ {cert_status_check, 5}]},
+ {public_key, [{ocsp_extensions, 1}, %%{pkix_decode_cert, 2},
+ {pkix_ocsp_validate, 5}]},
+ {ssl, [{opt_ocsp, 3}]},
+ {ssl_certificate, [{verify_cert_extensions, 4}]},
+ {ssl_test_lib, [{init_openssl_server, 3}, {openssl_server_loop, 3}]},
+ {tls_connection, [{wait_ocsp_stapling, 3}]},
+ {dtls_connection, [{initial_hello, 3}, {hello, 3}, {connection, 3}]},
+ {tls_dtls_connection, [{wait_ocsp_stapling, 3}, {certify, 3}]},
+ {tls_handshake, [{ocsp_expect, 1}, {client_hello, 11}]},
+ {dtls_handshake, [{client_hello, 8}]}]},
+ {crt, %% certificates
+ fun(M, F, A) -> dbg:tpl(M, F, A, x) end,
+ fun(M, F, A) -> dbg:ctpl(M, F, A) end,
+ [{public_key, [{pkix_path_validation, 3}]},
+ {ssl_certificate, [{validate, 3}]},
+ {ssl_gen_statem, [{initial_hello, 3}]},
+ {ssl_handshake, [{path_validate, 11}, {path_validation, 10},
+ {maybe_check_hostname, 3}, {maybe_check_hostname, 3}]},
+ {tls_handshake_1_3, [{path_validation, 10}]},
+ {tls_connection_1_3, [{init,1}]},
+ {tls_connection, [{init,1}]},
+ {dtls_connection, [{init,1}]}]},
+ {kdt, %% key update
+ fun(M, F, A) -> dbg:tpl(M, F, A, x) end,
+ fun(M, F, A) -> dbg:ctpl(M, F, A) end,
+ [{tls_connection_1_3, [{handle_key_update, 2}]},
+ {tls_sender, [{init, 3}, {time_to_rekey, 6},
+ {send_post_handshake_data, 4}]},
+ {tls_v1, [{update_traffic_secret, 2}]}]},
+ {rle, %% role
fun(M, F, A) -> dbg:tpl(M, F, A, x) end,
fun(M, F, A) -> dbg:ctpl(M, F, A) end,
[{ssl, [{listen,2}, {connect,3}]},
{ssl_gen_statem, [{init, 1}]},
- {tls_server_session_ticket, [{init,1}]}]}
- ].
+ {tls_server_session_ticket, [{init,1}]},
+ {tls_sender, [{init, 3}]}]},
+ {ssn, %% session
+ fun(M, F, A) -> dbg:tpl(M, F, A, x) end,
+ fun(M, F, A) -> dbg:ctpl(M, F, A) end,
+ [{tls_server_session_ticket,
+ [{handle_call,3}, {handle_cast,2}, {handle_info,2},
+ {terminate,2}, {start_link,7},
+ {init,1}, {initial_state,1}, {validate_binder,5}, {stateful_store,0},
+ {stateful_ticket_store,6}, {stateful_use,4}, {stateful_use,6},
+ {stateful_usable_ticket,5}, {stateful_living_ticket,2},
+ {stateful_psk_ticket_id,1}, {generate_stateless_ticket,5}, {stateless_use,6},
+ {stateless_usable_ticket,5}, {stateless_living_ticket,5}, {in_window,2},
+ {stateless_anti_replay,5}]},
+ {tls_handshake_1_3,
+ [{get_ticket_data,3}]}]},
+ {hbn, %% hibernate
+ fun(M, F, A) -> dbg:tpl(M, F, A, x) end,
+ fun(M, F, A) -> dbg:ctpl(M, F, A) end,
+ [{tls_sender,
+ [{connection, 3}, {hibernate_after, 3}]},
+ {dtls_connection,
+ [{connection,3},
+ {gen_info, 3}]},
+ {dtls_gen_connection,
+ [{handle_info,3}]},
+ {ssl_gen_statem,
+ [{hibernate_after, 3}, {handle_common_event, 4}]}]}].
diff --git a/lib/ssl/src/tls_connection.erl b/lib/ssl/src/tls_connection.erl
index 1687721e54..07c922ce76 100644
--- a/lib/ssl/src/tls_connection.erl
+++ b/lib/ssl/src/tls_connection.erl
@@ -135,7 +135,9 @@
terminate/3,
code_change/4,
format_status/2]).
-
+
+%% Tracing
+-export([handle_trace/3]).
%%====================================================================
%% Internal application API
%%====================================================================
@@ -596,3 +598,12 @@ choose_tls_fsm(#{versions := Versions},
end;
choose_tls_fsm(_, _) ->
tls_1_0_to_1_2_fsm.
+
+%%%################################################################
+%%%#
+%%%# Tracing
+%%%#
+handle_trace(csp,
+ {call, {?MODULE, wait_ocsp_stapling,
+ [Type, Event|_]}}, Stack) ->
+ {io_lib:format("Type = ~w Event = ~W", [Type, Event, 10]), Stack}.
diff --git a/lib/ssl/src/tls_connection_1_3.erl b/lib/ssl/src/tls_connection_1_3.erl
index c99a64c42e..6824430a5d 100644
--- a/lib/ssl/src/tls_connection_1_3.erl
+++ b/lib/ssl/src/tls_connection_1_3.erl
@@ -145,6 +145,8 @@
send_key_update/2,
update_cipher_key/2]).
+%% Tracing
+-export([handle_trace/3]).
%%====================================================================
%% Internal API
%%====================================================================
@@ -745,3 +747,18 @@ handle_change_cipher_spec(Type, Msg, StateName, #state{protocol_specific = PS0}
fail ->
ssl_gen_statem:handle_common_event(Type, Msg, StateName, State)
end.
+
+%%%################################################################
+%%%#
+%%%# Tracing
+%%%#
+handle_trace(kdt,
+ {call, {?MODULE, handle_key_update,
+ [#key_update{request_update = update_requested}, _State]}},
+ Stack) ->
+ {io_lib:format("KeyUpdate procedure 2/4 - ~w received", [update_requested]), Stack};
+handle_trace(kdt,
+ {call, {?MODULE, handle_key_update,
+ [#key_update{request_update = update_not_requested}, _State]}},
+ Stack) ->
+ {io_lib:format("KeyUpdate procedure 4/4 - ~w received", [update_not_requested]), Stack}.
diff --git a/lib/ssl/src/tls_dtls_connection.erl b/lib/ssl/src/tls_dtls_connection.erl
index 14d812252b..ebf6f1b085 100644
--- a/lib/ssl/src/tls_dtls_connection.erl
+++ b/lib/ssl/src/tls_dtls_connection.erl
@@ -63,6 +63,9 @@
downgrade/3,
gen_handshake/4]).
+%% Tracing
+-export([handle_trace/3]).
+
%%--------------------------------------------------------------------
-spec internal_renegotiation(pid(), ssl_record:connection_states()) ->
ok.
@@ -1724,3 +1727,11 @@ assert_curve(ECCCurve) ->
_ ->
ok
end.
+
+%%%################################################################
+%%%#
+%%%# Tracing
+%%%#
+handle_trace(csp,
+ {call, {?MODULE, wait_ocsp_stapling, [Type, Msg | _]}}, Stack) ->
+ {io_lib:format("Type = ~w Msg = ~W", [Type, Msg, 10]), Stack}.
diff --git a/lib/ssl/src/tls_sender.erl b/lib/ssl/src/tls_sender.erl
index b53a801b90..4f7920ed35 100644
--- a/lib/ssl/src/tls_sender.erl
+++ b/lib/ssl/src/tls_sender.erl
@@ -54,6 +54,8 @@
connection/3,
handshake/3,
death_row/3]).
+%% Tracing
+-export([handle_trace/3]).
-record(static,
{connection_pid,
@@ -712,3 +714,51 @@ hibernate_after(connection = StateName,
{next_state, StateName, State, [{timeout, HibernateAfter, hibernate} | Actions]};
hibernate_after(StateName, State, Actions) ->
{next_state, StateName, State, Actions}.
+
+%%%################################################################
+%%%#
+%%%# Tracing
+%%%#
+handle_trace(kdt,
+ {call, {?MODULE, time_to_rekey,
+ [_Version, Data, Map, _RenegotiateAt,
+ KeyUpdateAt, BytesSent]}}, Stack) ->
+ #{current_write := #{sequence_number := Sn}} = Map,
+ DataSize = iolist_size(Data),
+ {io_lib:format("~w) (BytesSent:~w + DataSize:~w) > KeyUpdateAt:~w",
+ [Sn, BytesSent, DataSize, KeyUpdateAt]), Stack};
+handle_trace(kdt,
+ {call, {?MODULE, send_post_handshake_data,
+ [{key_update, update_requested}|_]}}, Stack) ->
+ {io_lib:format("KeyUpdate procedure 1/4 - update_requested sent", []), Stack};
+handle_trace(kdt,
+ {call, {?MODULE, send_post_handshake_data,
+ [{key_update, update_not_requested}|_]}}, Stack) ->
+ {io_lib:format("KeyUpdate procedure 3/4 - update_not_requested sent", []), Stack};
+handle_trace(hbn,
+ {call, {?MODULE, connection,
+ [timeout, hibernate | _]}}, Stack) ->
+ {io_lib:format("* * * hibernating * * *", []), Stack};
+handle_trace(hbn,
+ {call, {?MODULE, hibernate_after,
+ [_StateName = connection, State, Actions]}},
+ Stack) ->
+ #data{static=#static{hibernate_after = HibernateAfter}} = State,
+ {io_lib:format("* * * maybe hibernating in ~w ms * * * Actions = ~W ",
+ [HibernateAfter, Actions, 10]), Stack};
+handle_trace(hbn,
+ {return_from, {?MODULE, hibernate_after, 3},
+ {Cmd, Arg,_State, Actions}},
+ Stack) ->
+ {io_lib:format("Cmd = ~w Arg = ~w Actions = ~W", [Cmd, Arg, Actions, 10]), Stack};
+handle_trace(rle,
+ {call, {?MODULE, init, [Type, Opts, _StateData]}}, Stack0) ->
+ {Pid, #{role := Role,
+ socket := _Socket,
+ key_update_at := KeyUpdateAt,
+ erl_dist := IsErlDist,
+ trackers := Trackers,
+ negotiated_version := _Version}} = Opts,
+ {io_lib:format("(*~w) Type = ~w Pid = ~w Trackers = ~w Dist = ~w KeyUpdateAt = ~w",
+ [Role, Type, Pid, Trackers, IsErlDist, KeyUpdateAt]),
+ [{role, Role} | Stack0]}.
diff --git a/lib/ssl/src/tls_server_session_ticket.erl b/lib/ssl/src/tls_server_session_ticket.erl
index 2e2d708c6f..e0fea089bc 100644
--- a/lib/ssl/src/tls_server_session_ticket.erl
+++ b/lib/ssl/src/tls_server_session_ticket.erl
@@ -40,6 +40,9 @@
-export([init/1, handle_call/3, handle_cast/2, handle_info/2,
terminate/2, code_change/3, format_status/2]).
+%% Tracing
+-export([handle_trace/3]).
+
-define(SERVER, ?MODULE).
-record(state, {
@@ -85,7 +88,7 @@ use(Pid, Identifiers, Prf, HandshakeHist) ->
init([Listener | Args]) ->
process_flag(trap_exit, true),
Monitor = inet:monitor(Listener),
- State = inital_state(Args),
+ State = initial_state(Args),
{ok, State#state{listen_monitor = Monitor}}.
-spec handle_call(Request :: term(), From :: {pid(), term()}, State :: term()) ->
@@ -156,18 +159,18 @@ code_change(_OldVsn, State, _Extra) ->
Status :: list()) -> Status :: term().
format_status(_Opt, Status) ->
Status.
+
%%%===================================================================
%%% Internal functions
%%%===================================================================
-
-inital_state([stateless, Lifetime, _, MaxEarlyDataSize, undefined, Seed]) ->
+initial_state([stateless, Lifetime, _, MaxEarlyDataSize, undefined, Seed]) ->
#state{nonce = 0,
stateless = #{seed => stateless_seed(Seed),
window => undefined},
lifetime = Lifetime,
max_early_data_size = MaxEarlyDataSize
};
-inital_state([stateless, Lifetime, _, MaxEarlyDataSize, {Window, K, M}, Seed]) ->
+initial_state([stateless, Lifetime, _, MaxEarlyDataSize, {Window, K, M}, Seed]) ->
erlang:send_after(Window * 1000, self(), rotate_bloom_filters),
#state{nonce = 0,
stateless = #{bloom_filter => tls_bloom_filter:new(K, M),
@@ -177,7 +180,7 @@ inital_state([stateless, Lifetime, _, MaxEarlyDataSize, {Window, K, M}, Seed]) -
lifetime = Lifetime,
max_early_data_size = MaxEarlyDataSize
};
-inital_state([stateful, Lifetime, TicketStoreSize, MaxEarlyDataSize|_]) ->
+initial_state([stateful, Lifetime, TicketStoreSize, MaxEarlyDataSize|_]) ->
%% statfeful servers replay
%% protection is that it saves
%% all valid tickets
@@ -481,3 +484,88 @@ warm_up_windows(_) ->
%% 2*WindowSize to ensure tickets from a previous instance of the server
%% (before a restart) cannot be reused, if the ticket encryption seed is reused.
2.
+
+%%%################################################################
+%%%#
+%%%# Tracing
+%%%#
+handle_trace(rle, {call, {?MODULE, init, [[ListenSocket, Mode, Lifetime,
+ StoreSize | _T]]}}, Stack) ->
+ {io_lib:format("(*server) ([ListenSocket = ~w Mode = ~w Lifetime = ~w "
+ "StoreSize = ~w, ...])",
+ [ListenSocket, Mode, Lifetime, StoreSize]),
+ [{role, server} | Stack]};
+handle_trace(ssn,
+ {call, {?MODULE, terminate, [Reason, _State]}}, Stack) ->
+ {io_lib:format("(Reason ~w)", [Reason]), Stack};
+handle_trace(ssn,
+ {call, {?MODULE, handle_call,
+ [CallTuple, _From, _State]}}, Stack) ->
+ {io_lib:format("(Call = ~w)", [element(1, CallTuple)]), Stack};
+handle_trace(ssn,
+ {call, {?MODULE,handle_call,
+ [{Call = use_ticket,
+ {offered_psks,
+ [{psk_identity, PskIdentity, _ObfAge}],
+ [Binder]},
+ _Prf,
+ _HandshakeHist}, _From, _State]}}, Stack) ->
+ {io_lib:format("(Call = ~w PskIdentity = ~W Binder = ~W)",
+ [Call, PskIdentity, 5, Binder, 5]), Stack};
+handle_trace(ssn,
+ {call, {?MODULE, validate_binder,
+ [Binder, _HandshakeHist, PSK, _Prf, _AlertDetail]}}, Stack) ->
+ {io_lib:format("(Binder = ~W PSK = ~W)", [Binder, 5, PSK, 5]), Stack};
+handle_trace(ssn,
+ {call, {?MODULE, initial_state,
+ [[Mode, _Lifetime, _StoreSize,_MaxEarlyDataSize,
+ Window, Seed]]}}, Stack) ->
+ {io_lib:format("(Mode = ~w Window = ~w Seed = ~W)", [Mode, Window, Seed, 5]), Stack};
+handle_trace(ssn,
+ {call, {?MODULE, generate_stateless_ticket,
+ [_BaseTicket, _Prf, MasterSecret, _State]}}, Stack) ->
+ {io_lib:format("(MasterSecret = ~W)", [MasterSecret, 5]), Stack};
+handle_trace(ssn,
+ {call, {?MODULE, stateless_use,
+ [[{psk_identity, Encrypted, _ObfAge} | _],
+ [Binder | _],
+ _Prf, _HandshakeHist, _Index, _State]}}, Stack) ->
+ {io_lib:format("(Encrypted = ~W Binder = ~W)", [Encrypted, 5, Binder, 5]), Stack};
+handle_trace(ssn,
+ {call, {?MODULE, in_window, [RealAge, Window]}}, Stack) ->
+ {io_lib:format("(RealAge = ~w Window = ~w)",
+ [RealAge, Window]), Stack};
+handle_trace(ssn,
+ {call, {?MODULE, stateless_usable_ticket,
+ [{stateless_ticket, _Prf, _PreSharedKey, _TicketAgeAdd,
+ _Lifetime, _Timestamp}, _ObfAge, Binder,
+ _HandshakeHist, Window]}}, Stack) ->
+ {io_lib:format("(Binder = ~W Window = ~w)", [Binder, 5, Window]), Stack};
+handle_trace(ssn,
+ {call, {?MODULE, stateless_anti_replay,
+ [_Index, PSK, _Binder, _State]}}, Stack) ->
+ {io_lib:format("(PSK = ~W)", [PSK, 5]), Stack};
+handle_trace(ssn,
+ {return_from, {?MODULE, stateless_use, 6},
+ {{ok, {_Index, PSK}}, _State}}, Stack) ->
+ {io_lib:format("PSK = ~W", [PSK, 5]), Stack};
+handle_trace(ssn,
+ {return_from, {?MODULE, generate_stateless_ticket, 4},
+ {new_session_ticket, _LifeTime, _AgeAdd, _Nonce, Ticket,
+ _Extensions}}, Stack) ->
+ {io_lib:format("Ticket = ~W", [Ticket, 5]), Stack};
+handle_trace(ssn,
+ {return_from, {?MODULE, initial_state, 1},
+ {state, _Stateless = #{seed := {IV, Shard}, window := Window},
+ _Stateful = undefined, _Nonce, _Lifetime ,
+ _MaxEarlyDataSize, ListenMonitor}}, Stack) ->
+ {io_lib:format("IV = ~W Shard = ~W Window = ~w ListenMonitor = ~w",
+ [IV, 5, Shard, 5, Window, ListenMonitor]), Stack};
+handle_trace(ssn,
+ {return_from, {?MODULE, stateless_anti_replay, 4},
+ {{ok, {_Index, PSK}}, _State}}, Stack) ->
+ {io_lib:format("ticket OK ~W", [PSK, 5]), Stack};
+handle_trace(ssn,
+ {return_from, {?MODULE, stateless_anti_replay, 4},
+ Return}, Stack) ->
+ {io_lib:format("ticket REJECTED ~W", [Return, 5]), Stack}.
diff --git a/lib/ssl/src/tls_v1.erl b/lib/ssl/src/tls_v1.erl
index dd891967bc..fdbeac298f 100644
--- a/lib/ssl/src/tls_v1.erl
+++ b/lib/ssl/src/tls_v1.erl
@@ -90,6 +90,9 @@
finished_verify_data/3,
pre_shared_key/3]).
+%% Tracing
+-export([handle_trace/3]).
+
-type named_curve() :: sect571r1 | sect571k1 | secp521r1 | brainpoolP512r1 |
sect409k1 | sect409r1 | brainpoolP384r1 | secp384r1 |
sect283k1 | sect283r1 | brainpoolP256r1 | secp256k1 | secp256r1 |
@@ -1273,3 +1276,23 @@ enum_to_oid(29) -> ?'id-X25519';
enum_to_oid(30) -> ?'id-X448';
enum_to_oid(_) ->
undefined.
+
+%%%################################################################
+%%%#
+%%%# Tracing
+%%%#
+handle_trace(kdt,
+ {call, {?MODULE, update_traffic_secret,
+ [_HKDF, ApplicationTrafficSecret0]}},
+ Stack) ->
+ ATS0 = string:sub_string(
+ binary:bin_to_list(
+ binary:encode_hex(ApplicationTrafficSecret0)), 1, 5) ++ "...",
+ {io_lib:format("ApplicationTrafficSecret0 = \"~s\"", [ATS0]), Stack};
+handle_trace(kdt,
+ {return_from, {?MODULE, update_traffic_secret, 2},
+ Return}, Stack) ->
+ ATS = string:sub_string(
+ binary:bin_to_list(
+ binary:encode_hex(Return)), 1, 5) ++ "...",
+ {io_lib:format("ApplicationTrafficSecret = \"~s\"", [ATS]), Stack}.
diff --git a/lib/ssl/test/ssl_test_lib.erl b/lib/ssl/test/ssl_test_lib.erl
index cea2897b4c..cf47f6ac07 100644
--- a/lib/ssl/test/ssl_test_lib.erl
+++ b/lib/ssl/test/ssl_test_lib.erl
@@ -217,6 +217,8 @@
verify_early_data/1,
trace/0
]).
+%% Tracing
+-export([handle_trace/3]).
-record(sslsocket, { fd = nil, pid = nil}).
-define(SLEEP, 1000).
@@ -4135,3 +4137,10 @@ curve_default(_) ->
trace() ->
ssl_trace:start(fun ct:pal/2, []),
ssl_trace:on().
+
+handle_trace(rle,
+ {call, {?MODULE, init_openssl_server, [Mode, ResponderPort | _]}}, Stack0) ->
+ Role = server,
+ {io_lib:format("(*~w) Mode = ~w ResponderPort = ~w",
+ [Role, Mode, ResponderPort]),
+ [{role, Role} | Stack0]}.
diff --git a/lib/ssl/test/ssl_trace_SUITE.erl b/lib/ssl/test/ssl_trace_SUITE.erl
index 7e64c9cb2e..376b9a01ab 100644
--- a/lib/ssl/test/ssl_trace_SUITE.erl
+++ b/lib/ssl/test/ssl_trace_SUITE.erl
@@ -44,7 +44,12 @@
tc_budget_option/0,
tc_budget_option/1,
tc_file_option/0,
- tc_file_option/1]).
+ tc_file_option/1,
+ tc_write/0,
+ tc_write/1,
+ tc_check_profiles/0,
+ tc_check_profiles/1]).
+-define(TRACE_FILE, "ssl_trace.txt").
%%--------------------------------------------------------------------
%% Common Test interface functions -----------------------------------
@@ -53,7 +58,7 @@ suite() -> [{ct_hooks,[ts_install_cth]},
{timetrap,{seconds,60}}].
all() -> [tc_basic, tc_no_trace, tc_api_profile, tc_rle_profile,
- tc_budget_option, tc_file_option].
+ tc_budget_option, tc_write, tc_file_option, tc_check_profiles].
init_per_suite(Config) ->
catch crypto:stop(),
@@ -82,7 +87,7 @@ end_per_testcase(_TC, Config) ->
tc_basic() ->
[{doc, "Basic test of ssl_trace API"}].
tc_basic(_Config) ->
- L0 = ssl_trace:start(),
+ {ok, L0} = ssl_trace:start(),
true = is_pid(whereis(ssl_trace)),
true = is_list(L0),
{ok,L0} = ssl_trace:on(),
@@ -105,7 +110,7 @@ tc_basic(_Config) ->
ok = ssl_trace:stop(),
undefined = whereis(ssl_trace),
- [api, rle] = ssl_trace:start(),
+ {ok, [api, crt, csp, hbn, kdt, rle, ssn]} = ssl_trace:start(),
{ok, [api]} = ssl_trace:on(api),
{ok, []} = ssl_trace:off(api),
ok = ssl_trace:stop(),
@@ -113,7 +118,6 @@ tc_basic(_Config) ->
tc_no_trace() ->
[{doc, "Verify there are no traces if not enabled"}].
-
tc_no_trace(Config) ->
Ref = ssl_trace_start(),
[Server, Client] = ssl_connect(Config),
@@ -127,7 +131,6 @@ tc_no_trace(Config) ->
tc_api_profile() ->
[{doc, "Verify traces for 'api' trace profile"}].
-
tc_api_profile(Config) ->
On = [api, rle],
Off = [],
@@ -149,13 +152,16 @@ tc_api_profile(Config) ->
{" (client) <- ssl_gen_statem:connect/8 returned",
ssl_gen_statem, connect},
{" (client) <- ssl:connect/3 returned", ssl, connect},
- {" (server) <- ssl:handshake/2 returned", ssl, handshake}
- ],
+ {" (server) <- ssl:handshake/2 returned", ssl, handshake},
+ {" (client) <- tls_sender:init/3 returned", tls_sender, init},
+ {" (server) <- tls_sender:init/3 returned", tls_sender, init}],
processed =>
["rle ('?') -> ssl_gen_statem:init/1 (*client)",
"rle ('?') -> ssl_gen_statem:init/1 (*server)",
"rle ('?') -> ssl:listen/2 (*server) Args",
- "rle ('?') -> ssl:connect/3 (*client) Args"]},
+ "rle ('?') -> ssl:connect/3 (*client) Args",
+ "rle ('?') -> tls_sender:init/3 (*server)",
+ "rle ('?') -> tls_sender:init/3 (*client)"]},
TracesAfterDisconnect =
#{
call =>
@@ -205,7 +211,6 @@ tc_api_profile(Config) ->
tc_rle_profile() ->
[{doc, "Verify traces for 'rle' trace profile"}].
-
tc_rle_profile(Config) ->
On = [rle],
ExpectedTraces =
@@ -214,12 +219,16 @@ tc_rle_profile(Config) ->
[],
return_from =>
[{" (client) <- ssl:connect/3 returned", ssl, connect},
- {" (server) <- ssl:listen/2 returned", ssl, listen}],
+ {" (server) <- ssl:listen/2 returned", ssl, listen},
+ {" (client) <- tls_sender:init/3 returned", tls_sender, init},
+ {" (server) <- tls_sender:init/3 returned", tls_sender, init}],
processed =>
["rle ('?') -> ssl:listen/2 (*server) Args =",
"rle ('?') -> ssl:connect/3 (*client) Args",
"rle ('?') -> ssl_gen_statem:init/1 (*server) Args = [[server",
- "rle ('?') -> ssl_gen_statem:init/1 (*client) Args = [[client"]},
+ "rle ('?') -> ssl_gen_statem:init/1 (*client) Args = [[client",
+ "rle ('?') -> tls_sender:init/3 (*server)",
+ "rle ('?') -> tls_sender:init/3 (*client)"]},
Ref = ssl_trace_start(),
{ok, On} = ssl_trace:on(On),
[Server, Client] = ssl_connect(Config),
@@ -234,10 +243,10 @@ tc_rle_profile(Config) ->
tc_budget_option() ->
[{doc, "Verify that budget option limits amount of traces"}].
-
tc_budget_option(Config) ->
Ref = ssl_trace_start(make_ref(), [{budget, 10}]),
{ok, [api,rle]} = ssl_trace:on([api,rle]),
+ ssl_trace:write("Not a trace from dbg - not included in budget", []),
[Server, Client] = ssl_connect(Config),
ssl_test_lib:close(Server),
ssl_test_lib:close(Client),
@@ -261,15 +270,31 @@ tc_budget_option(Config) ->
tc_file_option() ->
[{doc, "Verify that file option redirects traces to file"}].
-
tc_file_option(Config) ->
_Ref = ssl_trace_start(make_ref(), [{budget, 10}, file]),
{ok, [api,rle]} = ssl_trace:on([api,rle]),
[Server, Client] = ssl_connect(Config),
ssl_test_lib:close(Server),
ssl_test_lib:close(Client),
- ActualTraceCnt = count_line("ssl_trace.txt"),
- ExpectedTraceCnt = 10,
+ ActualTraceCnt = count_line(?TRACE_FILE),
+ ExpectedTraceCnt = 11, %% budget + 1 message about end of budget
+ ssl_trace:stop(),
+ case ExpectedTraceCnt == ActualTraceCnt of
+ true ->
+ ok;
+ _ ->
+ ?FAIL("Expected ~w traces, but found ~w",
+ [ExpectedTraceCnt, ActualTraceCnt])
+ end.
+
+tc_write() ->
+ [{doc, "Verify that custom messages can be written"}].
+tc_write(_Config) ->
+ _Ref = ssl_trace_start(make_ref(), [{budget, 10}, file]),
+ {ok, [api,rle]} = ssl_trace:on([api,rle]),
+ ssl_trace:write("Custom trace message ~w", [msg]),
+ ActualTraceCnt = count_line(?TRACE_FILE),
+ ExpectedTraceCnt = 1,
ssl_trace:stop(),
case ExpectedTraceCnt == ActualTraceCnt of
true ->
@@ -279,17 +304,44 @@ tc_file_option(Config) ->
[ExpectedTraceCnt, ActualTraceCnt])
end.
+tc_check_profiles() ->
+ [{doc, "Verify that trace profile contain valid functions"}].
+tc_check_profiles(_Config) ->
+ CheckFun =
+ fun(Profile, Module, Fun, DefinedFunctions) ->
+ case lists:member(Fun, DefinedFunctions) of
+ true -> ok;
+ _ ->
+ {F, A} = Fun,
+ ct:fail("~w:~w/~w from '~w' trace profile not found",
+ [Module, F, A, Profile])
+ end
+ end,
+ CheckModule =
+ fun(Profile, {Module, Funs}) ->
+ DefinedFunctions = Module:module_info(functions),
+ [CheckFun(Profile, Module, F, DefinedFunctions) ||
+ F <- Funs]
+ end,
+ CheckTProfile =
+ fun({Profile, _, _, ModFunsTuples}) ->
+ [CheckModule(Profile, MFTuple) ||
+ MFTuple <- ModFunsTuples]
+ end,
+ [CheckTProfile(P) || P <- ssl_trace:trace_profiles()],
+ ok.
+
%%%----------------------------------------------------------------
ssl_trace_start() ->
ssl_trace_start(make_ref(), []).
ssl_trace_start(Ref, TraceOpts) ->
TestProcess = self(),
- [_|_] = ssl_trace:start(fun(Format,Args) ->
- ct:log(Format, Args),
- TestProcess ! {Ref, Args}
- end,
- TraceOpts),
+ {ok, [_|_]} = ssl_trace:start(fun(Format,Args) ->
+ ct:log(Format, Args),
+ TestProcess ! {Ref, Args}
+ end,
+ TraceOpts),
Ref.
receive_map(Ref) ->
@@ -409,7 +461,7 @@ count_line(Filename) ->
Count;
{error, Reason} ->
?PAL("~s open error reason:~s~n", [Filename, Reason]),
- ng
+ ct:fail(Reason)
end.
count_line(IoDevice, Count) ->
--
2.35.3