diff options
Diffstat (limited to 'lib/ssl')
61 files changed, 1890 insertions, 1203 deletions
diff --git a/lib/ssl/doc/src/notes.xml b/lib/ssl/doc/src/notes.xml index 7ca6cc2e5b..b8cab8cce7 100644 --- a/lib/ssl/doc/src/notes.xml +++ b/lib/ssl/doc/src/notes.xml @@ -27,6 +27,53 @@ </header> <p>This document describes the changes made to the SSL application.</p> +<section><title>SSL 10.5.3</title> + + <section><title>Fixed Bugs and Malfunctions</title> + <list> + <item> + <p> + Correct typo of ECC curve name in signature algorithm + handling. Will make the signature algorithm + ecdsa_secp521r1_sha512 succeed.</p> + <p> + Own Id: OTP-17756 Aux Id: GH-5383, PR-5397 </p> + </item> + <item> + <p> + Suppress authenticity warning when option verify_none is + explicitly supplied.</p> + <p> + Own Id: OTP-17757 Aux Id: GH-5352, PR-5395 </p> + </item> + </list> + </section> + +</section> + +<section><title>SSL 10.5.2</title> + + <section><title>Fixed Bugs and Malfunctions</title> + <list> + <item> + <p> + Fix TLS-1.2 RSA-PSS negotiation and also fix broken + certificate request message for pre-TLS-1.3 servers.</p> + <p> + Own Id: OTP-17688 Aux Id: GH-5255 </p> + </item> + <item> + <p> + Fix CRL issuer verification that under some circumstances + could fail with a function_clause error.</p> + <p> + Own Id: OTP-17723 Aux Id: GH-5300 </p> + </item> + </list> + </section> + +</section> + <section><title>SSL 10.5.1</title> <section><title>Fixed Bugs and Malfunctions</title> diff --git a/lib/ssl/doc/src/ssl.xml b/lib/ssl/doc/src/ssl.xml index 700d8be5fb..0b4bf3b3c7 100644 --- a/lib/ssl/doc/src/ssl.xml +++ b/lib/ssl/doc/src/ssl.xml @@ -485,7 +485,7 @@ version. </p> <note> <p> TLS-1.2 algorithms will not be negotiated for TLS-1.3, but TLS-1.3 RSASSA-PSS <seetype marker="#rsassa_pss_scheme">rsassa_pss_scheme()</seetype> - signature schemes may be negotiated also for TLS-1.2 from @OTP-16590@ + signature schemes may be negotiated also for TLS-1.2 from 24.1 (fully working from 24.1.3). However if TLS-1.3 is negotiated when both TLS-1.3 and TLS-1.2 is supported using defaults, the corresponding TLS-1.2 algorithms to the TLS-1.3 legacy signature schemes will be considered as the legacy schemes and applied only to certificate signatures. </p> @@ -519,7 +519,7 @@ version. <p> The client will send a <c>signature_algorithms_cert</c> extension (in the client hello message), if TLS version 1.2 - (back-ported to TLS 1.2 in @OTP-16590@) or later is used, and + (back-ported to TLS 1.2 in 24.1) or later is used, and the signature_algs_cert option is explicitly specified. By default, only the <seetype marker="#signature_algs">signature_algs</seetype> extension @@ -884,7 +884,7 @@ fun(srp, Username :: binary(), UserState :: term()) -> <datatype> <name name="ssl_imp"/> - <desc><p>Deprecated since OTP-17, has no affect.</p></desc> + <desc><p>Deprecated since OTP-17, has no effect.</p></desc> </datatype> <datatype> diff --git a/lib/ssl/src/Makefile b/lib/ssl/src/Makefile index 9cbee91946..1ba0f49f91 100644 --- a/lib/ssl/src/Makefile +++ b/lib/ssl/src/Makefile @@ -103,6 +103,7 @@ MODULES= \ tls_client_ticket_store \ tls_dist_sup \ tls_dist_server_sup \ + tls_dyn_connection_sup\ tls_sender \ tls_server_session_ticket\ tls_server_session_ticket_sup\ diff --git a/lib/ssl/src/dtls_connection.erl b/lib/ssl/src/dtls_connection.erl index 1a4f001273..47e0e9f90a 100644 --- a/lib/ssl/src/dtls_connection.erl +++ b/lib/ssl/src/dtls_connection.erl @@ -132,6 +132,8 @@ -export([renegotiate/2]). +-export([alert_or_reset_connection/3]). %% Code re-use from dtls_gen_connection. + %% gen_statem state functions -export([initial_hello/3, config_error/3, @@ -200,14 +202,19 @@ initial_hello({call, From}, {start, Timeout}, #state{static_env = #static_env{host = Host, port = Port, role = client, + socket = {_, Socket}, + transport_cb = Transport, session_cache = Cache, session_cache_cb = CacheCb}, + protocol_specific = PS, handshake_env = #handshake_env{renegotiation = {Renegotiation, _}}, connection_env = CEnv, ssl_options = #{versions := Versions} = SslOpts, session = #session{own_certificates = OwnCerts} = NewSession, connection_states = ConnectionStates0 } = State0) -> + Packages = maps:get(active_n, PS), + dtls_socket:setopts(Transport, Socket, [{active,Packages}]), Session = ssl_session:client_select_session({Host, Port, SslOpts}, Cache, CacheCb, NewSession), Hello = dtls_handshake:client_hello(Host, Port, ConnectionStates0, SslOpts, Session#session.session_id, Renegotiation, OwnCerts), @@ -223,17 +230,15 @@ initial_hello({call, From}, {start, Timeout}, State1#state{connection_env = CEnv#connection_env{negotiated_version = HelloVersion}}), State = State2#state{connection_env = CEnv#connection_env{negotiated_version = Version}, %% RequestedVersion - session = Session, - start_or_recv_from = From}, + session = Session, + start_or_recv_from = From, + protocol_specific = PS#{active_n_toggle := false} + }, dtls_gen_connection:next_event(hello, no_record, State, [{{timeout, handshake}, Timeout, close} | Actions]); initial_hello({call, _} = Type, Event, #state{static_env = #static_env{role = server}, - protocol_specific = PS} = State) -> - Result = ssl_gen_statem:?FUNCTION_NAME(Type, Event, - State#state{protocol_specific = - PS#{current_cookie_secret => dtls_v1:cookie_secret(), - previous_cookie_secret => <<>>, - ignored_alerts => 0, - max_ignored_alerts => 10}}), + protocol_specific = PS0} = State) -> + PS = PS0#{current_cookie_secret => dtls_v1:cookie_secret(), previous_cookie_secret => <<>>}, + Result = ssl_gen_statem:?FUNCTION_NAME(Type, Event, State#state{protocol_specific = PS}), erlang:send_after(dtls_v1:cookie_timeout(), self(), new_cookie_secret), Result; initial_hello(Type, Event, State) -> @@ -268,7 +273,7 @@ hello(internal, #client_hello{cookie = <<>>, handshake_env = HsEnv, connection_env = CEnv, protocol_specific = #{current_cookie_secret := Secret}} = State0) -> - case tls_dtls_connection:handle_sni_extension(State0, Hello) of + try tls_dtls_connection:handle_sni_extension(State0, Hello) of #state{} = State1 -> {ok, {IP, Port}} = dtls_socket:peername(Transport, Socket), Cookie = dtls_handshake:cookie(Secret, IP, Port, Hello), @@ -285,9 +290,9 @@ hello(internal, #client_hello{cookie = <<>>, State#state{handshake_env = HsEnv#handshake_env{ tls_handshake_history = ssl_handshake:init_handshake_history()}}, - Actions); - #alert{} = Alert -> - ssl_gen_statem:handle_own_alert(Alert, Version,?FUNCTION_NAME, State0) + Actions) + catch throw:#alert{} = Alert -> + alert_or_reset_connection(Alert, ?FUNCTION_NAME, State0) end; hello(internal, #hello_verify_request{cookie = Cookie}, #state{static_env = #static_env{role = client, @@ -317,27 +322,18 @@ hello(internal, #hello_verify_request{cookie = Cookie}, State = State2#state{connection_env = CEnv#connection_env{negotiated_version = Version} % RequestedVersion }, dtls_gen_connection:next_event(?FUNCTION_NAME, no_record, State, Actions); -hello(internal, #client_hello{extensions = Extensions, client_version = ClientVersion} = Hello, +hello(internal, #client_hello{extensions = Extensions} = Hello, #state{ssl_options = #{handshake := hello}, handshake_env = HsEnv, start_or_recv_from = From} = State0) -> - case tls_dtls_connection:handle_sni_extension(State0, Hello) of + try tls_dtls_connection:handle_sni_extension(State0, Hello) of #state{} = State -> {next_state, user_hello, State#state{start_or_recv_from = undefined, handshake_env = HsEnv#handshake_env{hello = Hello}}, - [{reply, From, {ok, Extensions}}]}; - #alert{} = Alert -> - ssl_gen_statem:handle_own_alert(Alert, ClientVersion, ?FUNCTION_NAME, State0) + [{reply, From, {ok, Extensions}}]} + catch throw:#alert{} = Alert -> + alert_or_reset_connection(Alert, ?FUNCTION_NAME, State0) end; -hello(internal, #server_hello{extensions = Extensions} = Hello, - #state{ssl_options = #{ - handshake := hello}, - handshake_env = HsEnv, - start_or_recv_from = From} = State) -> - {next_state, user_hello, State#state{start_or_recv_from = undefined, - handshake_env = HsEnv#handshake_env{ - hello = Hello}}, - [{reply, From, {ok, Extensions}}]}; hello(internal, #client_hello{cookie = Cookie} = Hello, #state{static_env = #static_env{role = server, transport_cb = Transport, socket = Socket}, @@ -350,32 +346,42 @@ hello(internal, #client_hello{cookie = Cookie} = Hello, #state{static_env = #sta handle_client_hello(Hello, State); _ -> case dtls_handshake:cookie(PSecret, IP, Port, Hello) of - Cookie -> + Cookie -> handle_client_hello(Hello, State); _ -> %% Handle bad cookie as new cookie request RFC 6347 4.1.2 hello(internal, Hello#client_hello{cookie = <<>>}, State) end end; +hello(internal, #server_hello{extensions = Extensions} = Hello, + #state{ssl_options = #{ + handshake := hello}, + handshake_env = HsEnv, + start_or_recv_from = From} = State) -> + {next_state, user_hello, State#state{start_or_recv_from = undefined, + handshake_env = HsEnv#handshake_env{ + hello = Hello}}, + [{reply, From, {ok, Extensions}}]}; + hello(internal, #server_hello{} = Hello, #state{ static_env = #static_env{role = client}, handshake_env = #handshake_env{ - renegotiation = {Renegotiation, _}, - ocsp_stapling_state = OcspState0} = HsEnv, - connection_env = #connection_env{negotiated_version = ReqVersion}, + renegotiation = {Renegotiation, _}, + ocsp_stapling_state = OcspState0} = HsEnv, connection_states = ConnectionStates0, session = #session{session_id = OldId}, ssl_options = SslOptions} = State) -> - case dtls_handshake:hello(Hello, SslOptions, ConnectionStates0, Renegotiation, OldId) of - #alert{} = Alert -> - ssl_gen_statem:handle_own_alert(Alert, ReqVersion, ?FUNCTION_NAME, State); - {Version, NewId, ConnectionStates, ProtoExt, Protocol, OcspState} -> - tls_dtls_connection:handle_session(Hello, - Version, NewId, ConnectionStates, ProtoExt, Protocol, - State#state{handshake_env = - HsEnv#handshake_env{ - ocsp_stapling_state = maps:merge(OcspState0,OcspState)}}) + try + {Version, NewId, ConnectionStates, ProtoExt, Protocol, OcspState} = + dtls_handshake:hello(Hello, SslOptions, ConnectionStates0, Renegotiation, OldId), + tls_dtls_connection:handle_session(Hello, + Version, NewId, ConnectionStates, ProtoExt, Protocol, + State#state{handshake_env = + HsEnv#handshake_env{ + ocsp_stapling_state = maps:merge(OcspState0,OcspState)}}) + catch throw:#alert{} = Alert -> + ssl_gen_statem:handle_own_alert(Alert, ?FUNCTION_NAME, State) end; hello(internal, {handshake, {#client_hello{cookie = <<>>} = Handshake, _}}, State) -> %% Initial hello should not be in handshake history @@ -487,12 +493,19 @@ cipher(Type, Event, State) -> gen_handshake(?FUNCTION_NAME, Type, Event, State). %%-------------------------------------------------------------------- --spec connection(gen_statem:event_type(), +-spec connection(gen_statem:event_type(), #hello_request{} | #client_hello{}| term(), #state{}) -> gen_statem:state_function_result(). %%-------------------------------------------------------------------- -connection(enter, _, State) -> - {keep_state, State}; +connection(enter, _, #state{connection_states = Cs0} = State0) -> + State = case maps:is_key(previous_cs, Cs0) of + false -> + State0; + true -> + Cs = maps:remove(previous_cs, Cs0), + State0#state{connection_states = Cs} + end, + {keep_state, State}; connection(info, Event, State) -> gen_info(Event, ?FUNCTION_NAME, State); connection(internal, #hello_request{}, #state{static_env = #static_env{host = Host, @@ -541,14 +554,42 @@ connection(internal, #client_hello{}, #state{static_env = #static_env{role = ser State1 = dtls_gen_connection:send_alert(Alert, State0), {Record, State} = ssl_gen_statem:prepare_connection(State1, Connection), dtls_gen_connection:next_event(?FUNCTION_NAME, Record, State); +connection(internal, new_connection, #state{ssl_options=SSLOptions, + handshake_env=HsEnv, + connection_states = OldCs} = State) -> + case maps:get(previous_cs, OldCs, undefined) of + undefined -> + #{beast_mitigation := BeastMitigation} = SSLOptions, + ConnectionStates0 = dtls_record:init_connection_states(server, BeastMitigation), + ConnectionStates = ConnectionStates0#{previous_cs => OldCs}, + {next_state, hello, State#state{handshake_env = HsEnv#handshake_env{renegotiation = {false, first}}, + connection_states = ConnectionStates}}; + _ -> + %% Someone spamming new_connection, just drop them + {keep_state, State} + end; + connection({call, From}, {application_data, Data}, State) -> try send_application_data(Data, From, ?FUNCTION_NAME, State) catch throw:Error -> ssl_gen_statem:hibernate_after(?FUNCTION_NAME, State, [{reply, From, Error}]) end; +connection({call, From}, {downgrade, Pid}, + #state{connection_env = CEnv, + static_env = #static_env{transport_cb = Transport, + socket = {_Server, Socket} = DTLSSocket}} = State) -> + %% For testing purposes, downgrades without noticing the server + dtls_socket:setopts(Transport, Socket, [{active, false}, {packet, 0}, {mode, binary}]), + Transport:controlling_process(Socket, Pid), + {stop_and_reply, {shutdown, normal}, {reply, From, {ok, DTLSSocket}}, + State#state{connection_env = CEnv#connection_env{socket_terminated = true}}}; connection(Type, Event, State) -> - tls_dtls_connection:?FUNCTION_NAME(Type, Event, State). + try + tls_dtls_connection:?FUNCTION_NAME(Type, Event, State) + catch throw:#alert{}=Alert -> + ssl_gen_statem:handle_own_alert(Alert, ?FUNCTION_NAME, State) + end. %%TODO does this make sense for DTLS ? %%-------------------------------------------------------------------- @@ -558,7 +599,12 @@ connection(Type, Event, State) -> downgrade(enter, _, State) -> {keep_state, State}; downgrade(Type, Event, State) -> - tls_dtls_connection:?FUNCTION_NAME(Type, Event, State). + try + tls_dtls_connection:?FUNCTION_NAME(Type, Event, State) + catch throw:#alert{}=Alert -> + ssl_gen_statem:handle_own_alert(Alert, ?FUNCTION_NAME, State) + end. + %%-------------------------------------------------------------------- %% gen_statem callbacks @@ -620,12 +666,15 @@ initial_state(Role, Host, Port, Socket, flight_buffer = dtls_gen_connection:new_flight(), protocol_specific = #{active_n => InternalActiveN, active_n_toggle => true, - flight_state => dtls_gen_connection:initial_flight_state(DataTag)} + flight_state => dtls_gen_connection:initial_flight_state(DataTag), + ignored_alerts => 0, + max_ignored_alerts => 10 + } }. handle_client_hello(#client_hello{client_version = ClientVersion} = Hello, State0) -> - case tls_dtls_connection:handle_sni_extension(State0, Hello) of + try #state{connection_states = ConnectionStates0, static_env = #static_env{trackers = Trackers}, handshake_env = #handshake_env{kex_algorithm = KeyExAlg, @@ -633,31 +682,28 @@ handle_client_hello(#client_hello{client_version = ClientVersion} = Hello, State negotiated_protocol = CurrentProtocol} = HsEnv, connection_env = CEnv, session = #session{own_certificates = OwnCerts} = Session0, - ssl_options = SslOpts} = State1 -> - SessionTracker = proplists:get_value(session_id_tracker, Trackers), - case dtls_handshake:hello(Hello, SslOpts, {SessionTracker, Session0, - ConnectionStates0, OwnCerts, KeyExAlg}, Renegotiation) of - #alert{} = Alert -> - ssl_gen_statem:handle_own_alert(Alert, ClientVersion, hello, State1); - {Version, {Type, Session}, - ConnectionStates, Protocol0, ServerHelloExt, HashSign} -> - Protocol = case Protocol0 of - undefined -> CurrentProtocol; - _ -> Protocol0 - end, - - State = prepare_flight(State0#state{connection_states = ConnectionStates, - connection_env = CEnv#connection_env{negotiated_version = Version}, - handshake_env = HsEnv#handshake_env{ - hashsign_algorithm = HashSign, - client_hello_version = ClientVersion, - negotiated_protocol = Protocol}, - session = Session}), - {next_state, hello, State, [{next_event, internal, {common_client_hello, Type, ServerHelloExt}}]} - end; - #alert{} = Alert -> - ssl_gen_statem:handle_own_alert(Alert, ClientVersion, hello, State0) - end. + ssl_options = SslOpts} = + tls_dtls_connection:handle_sni_extension(State0, Hello), + SessionTracker = proplists:get_value(session_id_tracker, Trackers), + {Version, {Type, Session}, ConnectionStates, Protocol0, ServerHelloExt, HashSign} = + dtls_handshake:hello(Hello, SslOpts, {SessionTracker, Session0, + ConnectionStates0, OwnCerts, KeyExAlg}, Renegotiation), + Protocol = case Protocol0 of + undefined -> CurrentProtocol; + _ -> Protocol0 + end, + + State = prepare_flight(State0#state{connection_states = ConnectionStates, + connection_env = CEnv#connection_env{negotiated_version = Version}, + handshake_env = HsEnv#handshake_env{ + hashsign_algorithm = HashSign, + client_hello_version = ClientVersion, + negotiated_protocol = Protocol}, + session = Session}), + {next_state, hello, State, [{next_event, internal, {common_client_hello, Type, ServerHelloExt}}]} + catch #alert{} = Alert -> + alert_or_reset_connection(Alert, hello, State0) + end. handle_state_timeout(flight_retransmission_timeout, StateName, @@ -670,40 +716,43 @@ handle_state_timeout(flight_retransmission_timeout, StateName, %% This will reset the retransmission timer by repeating the enter state event {repeat_state, State, Actions}. +alert_or_reset_connection(Alert, StateName, #state{connection_states = Cs} = State) -> + case maps:get(previous_cs, Cs, undefined) of + undefined -> + ssl_gen_statem:handle_own_alert(Alert, StateName, State); + PreviousConn -> + %% There exists an old connection and the new one failed, + %% reset to the old working one. + %% The next alert will be sent + HsEnv0 = State#state.handshake_env, + HsEnv = HsEnv0#handshake_env{renegotiation = undefined}, + NewState = State#state{connection_states = PreviousConn, + handshake_env = HsEnv + }, + {next_state, connection, NewState} + end. - -gen_handshake(StateName, Type, Event, - #state{connection_env = #connection_env{negotiated_version = Version}} = State) -> - try tls_dtls_connection:StateName(Type, Event, State) of - Result -> - Result - catch - _:_ -> - ssl_gen_statem:handle_own_alert(?ALERT_REC(?FATAL, ?HANDSHAKE_FAILURE, - malformed_handshake_data), - Version, StateName, State) +gen_handshake(StateName, Type, Event, State) -> + try tls_dtls_connection:StateName(Type, Event, State) + catch + throw:#alert{}=Alert -> + alert_or_reset_connection(Alert, StateName, State); + error:_ -> + Alert = ?ALERT_REC(?FATAL, ?HANDSHAKE_FAILURE, malformed_handshake_data), + alert_or_reset_connection(Alert, StateName, State) end. -gen_info(Event, connection = StateName, #state{connection_env = #connection_env{negotiated_version = Version}} = State) -> - try dtls_gen_connection:handle_info(Event, StateName, State) of - Result -> - Result - catch - _:_ -> - ssl_gen_statem:handle_own_alert(?ALERT_REC(?FATAL, ?INTERNAL_ERROR, - malformed_data), - Version, StateName, State) +gen_info(Event, connection = StateName, State) -> + try dtls_gen_connection:handle_info(Event, StateName, State) + catch error:_ -> + Alert = ?ALERT_REC(?FATAL, ?INTERNAL_ERROR, malformed_data), + alert_or_reset_connection(Alert, StateName, State) end; - -gen_info(Event, StateName, #state{connection_env = #connection_env{negotiated_version = Version}} = State) -> - try dtls_gen_connection:handle_info(Event, StateName, State) of - Result -> - Result - catch - _:_ -> - ssl_gen_statem:handle_own_alert(?ALERT_REC(?FATAL, ?HANDSHAKE_FAILURE, - malformed_handshake_data), - Version, StateName, State) +gen_info(Event, StateName, State) -> + try dtls_gen_connection:handle_info(Event, StateName, State) + catch error:_ -> + Alert = ?ALERT_REC(?FATAL, ?HANDSHAKE_FAILURE,malformed_handshake_data), + alert_or_reset_connection(Alert, StateName, State) end. prepare_flight(#state{flight_buffer = Flight, @@ -763,7 +812,7 @@ send_application_data(Data, From, _StateName, {Msgs, ConnectionStates} = dtls_record:encode_data(Data, Version, ConnectionStates0), State = State0#state{connection_states = ConnectionStates}, - case dtls_gen_connection:send(Transport, Socket, Msgs) of + case send_msgs(Transport, Socket, Msgs) of ok -> ssl_logger:debug(LogLevel, outbound, 'record', Msgs), ssl_gen_statem:hibernate_after(connection, State, [{reply, From, ok}]); @@ -772,6 +821,14 @@ send_application_data(Data, From, _StateName, end end. +send_msgs(Transport, Socket, [Msg|Msgs]) -> + case dtls_gen_connection:send(Transport, Socket, Msg) of + ok -> send_msgs(Transport, Socket, Msgs); + Error -> Error + end; +send_msgs(_, _, []) -> + ok. + time_to_renegotiate(_Data, #{current_write := #{sequence_number := Num}}, RenegotiateAt) -> diff --git a/lib/ssl/src/dtls_connection_sup.erl b/lib/ssl/src/dtls_connection_sup.erl index 4c5c0a490f..b2b9708209 100644 --- a/lib/ssl/src/dtls_connection_sup.erl +++ b/lib/ssl/src/dtls_connection_sup.erl @@ -51,17 +51,17 @@ start_child_dist(Args) -> %%%========================================================================= %%% Supervisor callback %%%========================================================================= -init(_O) -> - RestartStrategy = simple_one_for_one, - MaxR = 0, - MaxT = 3600, - - Name = undefined, % As simple_one_for_one is used. - StartFunc = {ssl_gen_statem, start_link, []}, - Restart = temporary, % E.g. should not be restarted - Shutdown = 4000, - Modules = [ssl_gen_statem, dtls_connection], - Type = worker, - - ChildSpec = {Name, StartFunc, Restart, Shutdown, Type, Modules}, - {ok, {{RestartStrategy, MaxR, MaxT}, [ChildSpec]}}. +init(_) -> + SupFlags = #{strategy => simple_one_for_one, + intensity => 0, + period => 3600 + }, + ChildSpecs = [#{id => undefined, + start => {ssl_gen_statem, start_link, []}, + restart => temporary, + shutdown => 4000, + modules => [ssl_gen_statem, dtls_connection], + type => worker + } + ], + {ok, {SupFlags, ChildSpecs}}. diff --git a/lib/ssl/src/dtls_gen_connection.erl b/lib/ssl/src/dtls_gen_connection.erl index 2032d77074..dab05335c0 100644 --- a/lib/ssl/src/dtls_gen_connection.erl +++ b/lib/ssl/src/dtls_gen_connection.erl @@ -68,6 +68,14 @@ send_alert_in_connection/2, close/5, protocol_name/0]). + + +%% See thread @ http://lists.cluenet.de/pipermail/ipv6-ops/2011-June/005755.html +%% 1280 - headers +-define(PMTUEstimate, 1200). + + + %%==================================================================== %% Internal application API %%==================================================================== @@ -105,30 +113,29 @@ next_record(#state{protocol_buffers = CurrentRead = dtls_record:get_connection_state_by_epoch(Epoch, ConnectionStates, read), case dtls_record:replay_detect(CT, CurrentRead) of false -> - decode_cipher_text(State#state{connection_states = ConnectionStates}) ; + decode_cipher_text(State) ; true -> %% Ignore replayed record - next_record(State#state{protocol_buffers = - Buffers#protocol_buffers{dtls_cipher_texts = Rest}, - connection_states = ConnectionStates}) + next_record(State#state{protocol_buffers = Buffers#protocol_buffers{dtls_cipher_texts = Rest}}) end; next_record(#state{protocol_buffers = #protocol_buffers{dtls_cipher_texts = [#ssl_tls{epoch = Epoch} | Rest]} = Buffers, - connection_states = #{current_read := #{epoch := CurrentEpoch}} = ConnectionStates} = State) + connection_states = #{current_read := #{epoch := CurrentEpoch}}} = State) when Epoch > CurrentEpoch -> %% TODO Buffer later Epoch message, drop it for now - next_record(State#state{protocol_buffers = - Buffers#protocol_buffers{dtls_cipher_texts = Rest}, - connection_states = ConnectionStates}); -next_record(#state{protocol_buffers = - #protocol_buffers{dtls_cipher_texts = [ _ | Rest]} - = Buffers, - connection_states = ConnectionStates} = State) -> - %% Drop old epoch message - next_record(State#state{protocol_buffers = - Buffers#protocol_buffers{dtls_cipher_texts = Rest}, - connection_states = ConnectionStates}); + next_record(State#state{protocol_buffers = Buffers#protocol_buffers{dtls_cipher_texts = Rest}}); +next_record(#state{protocol_buffers = #protocol_buffers{dtls_cipher_texts = + [#ssl_tls{epoch = Epoch} | Rest] + } = Buffers + } = State) -> + case Epoch of + 0 -> %% A reconnect (client might have rebooted and re-connected) + decode_cipher_text(State); + _ -> + %% Drop old epoch message + next_record(State#state{protocol_buffers = Buffers#protocol_buffers{dtls_cipher_texts = Rest}}) + end; next_record(#state{static_env = #static_env{role = server, socket = {Listener, {Client, _}}}} = State) -> dtls_packet_demux:active_once(Listener, Client, self()), @@ -182,16 +189,16 @@ next_event(StateName, no_record, %% TODO maybe buffer later epoch next_event(StateName, no_record, State, Actions); {#alert{} = Alert, State} -> - Version = State#state.connection_env#connection_env.negotiated_version, - handle_own_alert(Alert, Version, StateName, State) + handle_own_alert(Alert, StateName, State) end; next_event(connection = StateName, Record, #state{connection_states = #{current_read := #{epoch := CurrentEpoch}}} = State0, Actions) -> case Record of - #ssl_tls{epoch = CurrentEpoch, + #ssl_tls{epoch = Epoch, type = ?HANDSHAKE, - version = Version} = Record -> - State = dtls_version(StateName, Version, State0), + version = Version} = Record + when Epoch =:= CurrentEpoch; Epoch =:= 0 -> + State = dtls_version(StateName, Version, State0), {next_state, StateName, State, [{next_event, internal, {protocol_record, Record}} | Actions]}; #ssl_tls{epoch = CurrentEpoch} -> @@ -223,8 +230,7 @@ next_event(StateName, Record, %% TODO maybe buffer later epoch next_event(StateName, no_record, State0, Actions); #alert{} = Alert -> - Version = State0#state.connection_env#connection_env.negotiated_version, - handle_own_alert(Alert, Version, StateName, State0) + handle_own_alert(Alert, StateName, State0) end. initial_flight_state(udp)-> @@ -246,12 +252,11 @@ send_handshake_flight(#state{static_env = #static_env{socket = Socket, connection_states = ConnectionStates0, ssl_options = #{log_level := LogLevel}} = State0, Epoch) -> - PMTUEstimate = 1400, %% TODO make configurable #{current_write := #{max_fragment_length := MaxFragmentLength}} = ConnectionStates0, - MaxSize = min(MaxFragmentLength, PMTUEstimate), + MaxSize = min(MaxFragmentLength, ?PMTUEstimate), {Encoded, ConnectionStates} = encode_handshake_flight(lists:reverse(Flight), Version, MaxSize, Epoch, ConnectionStates0), - send(Transport, Socket, Encoded), + send_packets(Transport, Socket, Encoded), ssl_logger:debug(LogLevel, outbound, 'record', Encoded), {State0#state{connection_states = ConnectionStates}, []}; @@ -264,14 +269,12 @@ send_handshake_flight(#state{static_env = #static_env{socket = Socket, connection_states = ConnectionStates0, ssl_options = #{log_level := LogLevel}} = State0, Epoch) -> - PMTUEstimate = 1400, %% TODO make configurable #{current_write := #{max_fragment_length := MaxFragmentLength}} = ConnectionStates0, - MaxSize = min(MaxFragmentLength, PMTUEstimate), + MaxSize = min(MaxFragmentLength, ?PMTUEstimate), {HsBefore, ConnectionStates1} = encode_handshake_flight(lists:reverse(Flight0), Version, MaxSize, Epoch, ConnectionStates0), {EncChangeCipher, ConnectionStates} = encode_change_cipher(ChangeCipher, Version, Epoch, ConnectionStates1), - - send(Transport, Socket, [HsBefore, EncChangeCipher]), + send_packets(Transport, Socket, HsBefore ++ EncChangeCipher), ssl_logger:debug(LogLevel, outbound, 'record', [HsBefore]), ssl_logger:debug(LogLevel, outbound, 'record', [EncChangeCipher]), {State0#state{connection_states = ConnectionStates}, []}; @@ -285,16 +288,15 @@ send_handshake_flight(#state{static_env = #static_env{socket = Socket, connection_states = ConnectionStates0, ssl_options = #{log_level := LogLevel}} = State0, Epoch) -> - PMTUEstimate = 1400, %% TODO make configurable #{current_write := #{max_fragment_length := MaxFragmentLength}} = ConnectionStates0, - MaxSize = min(MaxFragmentLength, PMTUEstimate), + MaxSize = min(MaxFragmentLength, ?PMTUEstimate), {HsBefore, ConnectionStates1} = encode_handshake_flight(lists:reverse(Flight0), Version, MaxSize, Epoch-1, ConnectionStates0), {EncChangeCipher, ConnectionStates2} = encode_change_cipher(ChangeCipher, Version, Epoch-1, ConnectionStates1), {HsAfter, ConnectionStates} = encode_handshake_flight(lists:reverse(Flight1), Version, MaxSize, Epoch, ConnectionStates2), - send(Transport, Socket, [HsBefore, EncChangeCipher, HsAfter]), + send_packets(Transport, Socket, HsBefore ++ EncChangeCipher ++ HsAfter), ssl_logger:debug(LogLevel, outbound, 'record', [HsBefore]), ssl_logger:debug(LogLevel, outbound, 'record', [EncChangeCipher]), ssl_logger:debug(LogLevel, outbound, 'record', [HsAfter]), @@ -309,14 +311,13 @@ send_handshake_flight(#state{static_env = #static_env{socket = Socket, connection_states = ConnectionStates0, ssl_options = #{log_level := LogLevel}} = State0, Epoch) -> - PMTUEstimate = 1400, %% TODO make configurable #{current_write := #{max_fragment_length := MaxFragmentLength}} = ConnectionStates0, - MaxSize = min(MaxFragmentLength, PMTUEstimate), + MaxSize = min(MaxFragmentLength, ?PMTUEstimate), {EncChangeCipher, ConnectionStates1} = encode_change_cipher(ChangeCipher, Version, Epoch-1, ConnectionStates0), {HsAfter, ConnectionStates} = encode_handshake_flight(lists:reverse(Flight1), Version, MaxSize, Epoch, ConnectionStates1), - send(Transport, Socket, [EncChangeCipher, HsAfter]), + send_packets(Transport, Socket, EncChangeCipher ++ HsAfter), ssl_logger:debug(LogLevel, outbound, 'record', [EncChangeCipher]), ssl_logger:debug(LogLevel, outbound, 'record', [HsAfter]), {State0#state{connection_states = ConnectionStates}, []}. @@ -332,9 +333,8 @@ handle_protocol_record(#ssl_tls{type = ?APPLICATION_DATA, fragment = Data}, Stat ssl_gen_statem:hibernate_after(StateName, State, Actions) end; %%% DTLS record protocol level handshake messages -handle_protocol_record(#ssl_tls{type = ?HANDSHAKE, - fragment = Data}, - StateName, +handle_protocol_record(#ssl_tls{type = ?HANDSHAKE, epoch = Epoch, fragment = Data}, + StateName, #state{protocol_buffers = Buffers0, connection_env = #connection_env{negotiated_version = Version}, ssl_options = Options} = State) -> @@ -344,27 +344,31 @@ handle_protocol_record(#ssl_tls{type = ?HANDSHAKE, next_event(StateName, no_record, State#state{protocol_buffers = Buffers}); {Packets, Buffers} -> HsEnv = State#state.handshake_env, - Events = dtls_handshake_events(Packets), - {next_state, StateName, + HSEvents = dtls_handshake_events(Packets), + Events = case is_new_connection(Epoch, Packets, State) of + true -> [{next_event, internal, new_connection} | HSEvents]; + false -> HSEvents + end, + {next_state, StateName, State#state{protocol_buffers = Buffers, - handshake_env = - HsEnv#handshake_env{unprocessed_handshake_events - = unprocessed_events(Events)}}, Events} + handshake_env = + HsEnv#handshake_env{ + unprocessed_handshake_events = unprocessed_events(HSEvents)} + }, Events} end catch throw:#alert{} = Alert -> - handle_own_alert(Alert, Version, StateName, State) + handle_own_alert(Alert, StateName, State) end; %%% DTLS record protocol level change cipher messages handle_protocol_record(#ssl_tls{type = ?CHANGE_CIPHER_SPEC, fragment = Data}, StateName, State) -> {next_state, StateName, State, [{next_event, internal, #change_cipher_spec{type = Data}}]}; %%% DTLS record protocol level Alert messages -handle_protocol_record(#ssl_tls{type = ?ALERT, fragment = EncAlerts}, StateName, - #state{connection_env = #connection_env{negotiated_version = Version}} = State) -> +handle_protocol_record(#ssl_tls{type = ?ALERT, fragment = EncAlerts}, StateName, State) -> case decode_alerts(EncAlerts) of Alerts = [_|_] -> handle_alerts(Alerts, {next_state, StateName, State}); #alert{} = Alert -> - handle_own_alert(Alert, Version, StateName, State) + handle_own_alert(Alert, StateName, State) end; %% Ignore unknown TLS record level protocol messages handle_protocol_record(#ssl_tls{type = _Unknown}, StateName, State) -> @@ -478,6 +482,28 @@ send(Transport, {Listener, Socket}, Data) when is_pid(Listener) -> send(Transport, Socket, Data) -> % Client socket dtls_socket:send(Transport, Socket, Data). +send_packets(_Transport, _Socket, []) -> + ok; +send_packets(Transport, Socket, Packets) -> + {Packet, Rest} = pack_packets(Packets, 0, ?PMTUEstimate+80, []), + case send(Transport, Socket, Packet) of + ok -> send_packets(Transport, Socket, Rest); + Err -> Err + end. + +pack_packets([P|Rest]=Packets, SoFar, Max, Acc) -> + Size = erlang:iolist_size(P), + Next = SoFar + Size, + if Size > Max, Acc =:= [] -> + {P, Rest}; + Next < Max -> + pack_packets(Rest, Next, Max, [P|Acc]); + true -> + {lists:reverse(Acc), Packets} + end; +pack_packets([], _, _, Acc) -> + {lists:reverse(Acc), []}. + socket(Pid, Transport, Socket, _Tracker) -> dtls_socket:socket(Pid, Transport, Socket, ?MODULE). @@ -489,14 +515,12 @@ getopts(Transport, Socket, Tag) -> %% raw data from socket, unpack records handle_info({Protocol, _, _, _, Data}, StateName, - #state{static_env = #static_env{role = Role, - data_tag = Protocol}} = State0) -> + #state{static_env = #static_env{data_tag = Protocol}} = State0) -> case next_dtls_record(Data, StateName, State0) of {Record, State} -> next_event(StateName, Record, State); #alert{} = Alert -> - ssl_gen_statem:handle_normal_shutdown(Alert#alert{role = Role}, StateName, State0), - {stop, {shutdown, own_alert}, State0} + handle_own_alert(Alert, StateName, State0) end; handle_info({PassiveTag, Socket}, StateName, @@ -555,6 +579,15 @@ handle_info(Msg, StateName, State) -> %% Internal functions %%==================================================================== +is_new_connection(0, [{#client_hello{},_Raw}|_], + #state{ + connection_states = + #{current_read := #{epoch := CurrentEpoch}}}) + when CurrentEpoch > 0 -> + true; +is_new_connection(_, _, _) -> + false. + dtls_handshake_events(Packets) -> lists:map(fun(Packet) -> {next_event, internal, {handshake, Packet}} @@ -569,10 +602,15 @@ unprocessed_events(Events) -> erlang:length(Events)-1. encode_handshake_flight(Flight, Version, MaxFragmentSize, Epoch, ConnectionStates) -> - Fragments = lists:map(fun(Handshake) -> - dtls_handshake:fragment_handshake(Handshake, MaxFragmentSize) - end, Flight), - dtls_record:encode_handshake(Fragments, Version, Epoch, ConnectionStates). + Encode = fun(Fragment, {Acc, Cs0}) -> + {Enc, Cs} = dtls_record:encode_handshake(Fragment, Version, Epoch, Cs0), + {[Enc|Acc], Cs} + end, + {Rev, Cs} = lists:foldl(fun(Handshake, {Acc,Cs0}) -> + Frags = dtls_handshake:fragment_handshake(Handshake, MaxFragmentSize), + lists:foldl(Encode, {Acc,Cs0}, Frags) + end, {[], ConnectionStates}, Flight), + {lists:reverse(Rev), Cs}. encode_change_cipher(#change_cipher_spec{}, Version, Epoch, ConnectionStates) -> dtls_record:encode_change_cipher_spec(Version, Epoch, ConnectionStates). @@ -621,12 +659,28 @@ handle_alerts([], Result) -> Result; handle_alerts(_, {stop, _, _} = Stop) -> Stop; -handle_alerts([Alert | Alerts], {next_state, StateName, State}) -> - handle_alerts(Alerts, ssl_gen_statem:handle_alert(Alert, StateName, State)); -handle_alerts([Alert | Alerts], {next_state, StateName, State, _Actions}) -> - handle_alerts(Alerts, ssl_gen_statem:handle_alert(Alert, StateName, State)). +handle_alerts(Alerts, {next_state, StateName, State}) -> + handle_alerts_or_reset(Alerts, StateName, State); +handle_alerts(Alerts, {next_state, StateName, State, _Actions}) -> + handle_alerts_or_reset(Alerts, StateName, State). + +handle_alerts_or_reset([Alert|Alerts], StateName, #state{connection_states = Cs} = State) -> + case maps:get(previous_cs, Cs, undefined) of + undefined -> + handle_alerts(Alerts, ssl_gen_statem:handle_alert(Alert, StateName, State)); + PreviousConn -> + %% There exists an old connection and the new one sent alerts, + %% reset to the old working one. + HsEnv0 = State#state.handshake_env, + HsEnv = HsEnv0#handshake_env{renegotiation = undefined}, + NewState = State#state{connection_states = PreviousConn, + handshake_env = HsEnv + }, + {next_state, connection, NewState} + end. + -handle_own_alert(Alert, Version, StateName, +handle_own_alert(Alert, StateName, #state{static_env = #static_env{data_tag = udp, role = Role}, ssl_options = #{log_level := LogLevel}} = State0) -> @@ -635,10 +689,11 @@ handle_own_alert(Alert, Version, StateName, log_ignore_alert(LogLevel, StateName, Alert, Role), {next_state, StateName, State}; {false, State} -> - ssl_gen_statem:handle_own_alert(Alert, Version, StateName, State) + dtls_connection:alert_or_reset_connection(Alert, StateName, State) end; -handle_own_alert(Alert, Version, StateName, State) -> - ssl_gen_statem:handle_own_alert(Alert, Version, StateName, State). +handle_own_alert(Alert, StateName, State) -> + dtls_connection:alert_or_reset_connection(Alert, StateName, State). + ignore_alert(#alert{level = ?FATAL}, #state{protocol_specific = #{ignored_alerts := N, max_ignored_alerts := N}} = State) -> {false, State}; diff --git a/lib/ssl/src/dtls_handshake.erl b/lib/ssl/src/dtls_handshake.erl index 9999933d90..e46a52fb8c 100644 --- a/lib/ssl/src/dtls_handshake.erl +++ b/lib/ssl/src/dtls_handshake.erl @@ -108,7 +108,7 @@ hello(#server_hello{server_version = Version, random = Random, Compression, HelloExt, SslOpt, ConnectionStates0, Renegotiation, IsNew); false -> - ?ALERT_REC(?FATAL, ?PROTOCOL_VERSION) + throw(?ALERT_REC(?FATAL, ?PROTOCOL_VERSION)) end. hello(#client_hello{client_version = ClientVersion} = Hello, #{versions := Versions} = SslOpts, @@ -155,7 +155,7 @@ encode_handshake(Handshake, Version, Seq) -> %%-------------------------------------------------------------------- -spec get_dtls_handshake(ssl_record:ssl_version(), binary(), #protocol_buffers{}, ssl_options()) -> - {[dtls_handshake()], #protocol_buffers{}}. + {[{dtls_handshake(), binary()}], #protocol_buffers{}}. %% %% Description: Given buffered and new data from dtls_record, collects %% and returns it as a list of handshake messages, also returns @@ -195,13 +195,13 @@ handle_client_hello(Version, SslOpts, OwnCert), case CipherSuite of no_suite -> - ?ALERT_REC(?FATAL, ?INSUFFICIENT_SECURITY); + throw(?ALERT_REC(?FATAL, ?INSUFFICIENT_SECURITY)); _ -> #{key_exchange := KeyExAlg} = ssl_cipher_format:suite_bin_to_map(CipherSuite), case ssl_handshake:select_hashsign({ClientHashSigns, undefined}, OwnCert, KeyExAlg, SupportedHashSigns, TLSVersion) of #alert{} = Alert -> - Alert; + throw(Alert); HashSign -> handle_client_hello_extensions(Version, Type, Random, CipherSuites, HelloExt, SslOpts, Session1, ConnectionStates0, @@ -209,33 +209,27 @@ handle_client_hello(Version, end end; false -> - ?ALERT_REC(?FATAL, ?PROTOCOL_VERSION) + throw(?ALERT_REC(?FATAL, ?PROTOCOL_VERSION)) end. handle_client_hello_extensions(Version, Type, Random, CipherSuites, HelloExt, SslOpts, Session0, ConnectionStates0, Renegotiation, HashSign) -> - try ssl_handshake:handle_client_hello_extensions(dtls_record, Random, CipherSuites, - HelloExt, dtls_v1:corresponding_tls_version(Version), - SslOpts, Session0, + {Session, ConnectionStates, Protocol, ServerHelloExt} = + ssl_handshake:handle_client_hello_extensions(dtls_record, Random, CipherSuites, + HelloExt, dtls_v1:corresponding_tls_version(Version), + SslOpts, Session0, ConnectionStates0, Renegotiation, - Session0#session.is_resumable) of - {Session, ConnectionStates, Protocol, ServerHelloExt} -> - {Version, {Type, Session}, ConnectionStates, Protocol, ServerHelloExt, HashSign} - catch throw:Alert -> - Alert - end. + Session0#session.is_resumable), + {Version, {Type, Session}, ConnectionStates, Protocol, ServerHelloExt, HashSign}. handle_server_hello_extensions(Version, SessionId, Random, CipherSuite, Compression, HelloExt, SslOpt, ConnectionStates0, Renegotiation, IsNew) -> - try ssl_handshake:handle_server_hello_extensions(dtls_record, Random, CipherSuite, + {ConnectionStates, ProtoExt, Protocol, OcspState} = + ssl_handshake:handle_server_hello_extensions(dtls_record, Random, CipherSuite, Compression, HelloExt, dtls_v1:corresponding_tls_version(Version), - SslOpt, ConnectionStates0, Renegotiation, IsNew) of - {ConnectionStates, ProtoExt, Protocol, OcspState} -> - {Version, SessionId, ConnectionStates, ProtoExt, Protocol, OcspState} - catch throw:Alert -> - Alert - end. + SslOpt, ConnectionStates0, Renegotiation, IsNew), + {Version, SessionId, ConnectionStates, ProtoExt, Protocol, OcspState}. %%-------------------------------------------------------------------- diff --git a/lib/ssl/src/dtls_listener_sup.erl b/lib/ssl/src/dtls_listener_sup.erl index 4f46407290..699f27b95f 100644 --- a/lib/ssl/src/dtls_listener_sup.erl +++ b/lib/ssl/src/dtls_listener_sup.erl @@ -76,18 +76,17 @@ register_listener(OwnerAndListner, IP, Port) -> %%%========================================================================= %%% Supervisor callback %%%========================================================================= -init(_O) -> - ets:new(dtls_listener_sup, [named_table, public, set]), - RestartStrategy = simple_one_for_one, - MaxR = 0, - MaxT = 3600, - - Name = undefined, % As simple_one_for_one is used. - StartFunc = {dtls_packet_demux, start_link, []}, - Restart = temporary, % E.g. should not be restarted - Shutdown = 4000, - Modules = [dtls_packet_demux], - Type = worker, - - ChildSpec = {Name, StartFunc, Restart, Shutdown, Type, Modules}, - {ok, {{RestartStrategy, MaxR, MaxT}, [ChildSpec]}}. +init(_) -> + ets:new(dtls_listener_sup, [named_table, public, set]), + SupFlags = #{strategy => simple_one_for_one, + intensity => 0, + period => 3600 + }, + ChildSpecs = [#{id => undefined, + start => {dtls_packet_demux, start_link, []}, + restart => temporary, + shutdown => 4000, + modules => [dtls_packet_demux], + type => worker + }], + {ok, {SupFlags, ChildSpecs}}. diff --git a/lib/ssl/src/dtls_record.erl b/lib/ssl/src/dtls_record.erl index ef275fad4c..e3c9b829c4 100644 --- a/lib/ssl/src/dtls_record.erl +++ b/lib/ssl/src/dtls_record.erl @@ -75,6 +75,9 @@ init_connection_states(Role, BeastMitigation) -> ConnectionEnd = ssl_record:record_protocol_role(Role), Initial = initial_connection_state(ConnectionEnd, BeastMitigation), Current = Initial#{epoch := 0}, + %% No need to pass Version to ssl_record:empty_connection_state since + %% random nonce is generated with same algorithm for DTLS version + %% Might require a change for DTLS-1.3 InitialPending = ssl_record:empty_connection_state(ConnectionEnd, BeastMitigation), Pending = empty_connection_state(InitialPending), #{saved_read => Current, @@ -204,16 +207,17 @@ encode_alert_record(#alert{level = Level, description = Description}, %%-------------------------------------------------------------------- -spec encode_change_cipher_spec(ssl_record:ssl_version(), integer(), ssl_record:connection_states()) -> - {iolist(), ssl_record:connection_states()}. + {[iolist()], ssl_record:connection_states()}. %% %% Description: Encodes a change_cipher_spec-message to send on the ssl socket. %%-------------------------------------------------------------------- encode_change_cipher_spec(Version, Epoch, ConnectionStates) -> - encode_plain_text(?CHANGE_CIPHER_SPEC, Version, Epoch, ?byte(?CHANGE_CIPHER_SPEC_PROTO), ConnectionStates). + {Enc, Cs} = encode_plain_text(?CHANGE_CIPHER_SPEC, Version, Epoch, ?byte(?CHANGE_CIPHER_SPEC_PROTO), ConnectionStates), + {[Enc], Cs}. %%-------------------------------------------------------------------- -spec encode_data(binary(), ssl_record:ssl_version(), ssl_record:connection_states()) -> - {iolist(),ssl_record:connection_states()}. + {[iolist()],ssl_record:connection_states()}. %% %% Description: Encodes data to send on the ssl-socket. %%-------------------------------------------------------------------- @@ -236,7 +240,8 @@ encode_data(Data, Version, ConnectionStates) -> end, {[], ConnectionStates}, Frags), {lists:reverse(RevCipherText), ConnectionStates1}; _ -> - encode_plain_text(?APPLICATION_DATA, Version, Epoch, Data, ConnectionStates) + {Enc, Cs} = encode_plain_text(?APPLICATION_DATA, Version, Epoch, Data, ConnectionStates), + {[Enc], Cs} end. encode_plain_text(Type, Version, Epoch, Data, ConnectionStates) -> @@ -455,7 +460,8 @@ get_dtls_records_aux({_, _, Version, _} = Vinfo, <<?BYTE(Type),?BYTE(MajVer),?BY ?ALERT_REC(?FATAL, ?BAD_RECORD_MAC) end; get_dtls_records_aux(_, <<?BYTE(_), ?BYTE(_MajVer), ?BYTE(_MinVer), - ?UINT16(Length), _/binary>>, + ?UINT16(_Epoch), ?UINT48(_Seq), + ?UINT16(Length), _/binary>>, _Acc, _) when Length > ?MAX_CIPHER_TEXT_LENGTH -> ?ALERT_REC(?FATAL, ?RECORD_OVERFLOW); diff --git a/lib/ssl/src/dtls_server_session_cache_sup.erl b/lib/ssl/src/dtls_server_session_cache_sup.erl index 65fbb34918..c0242ab2c0 100644 --- a/lib/ssl/src/dtls_server_session_cache_sup.erl +++ b/lib/ssl/src/dtls_server_session_cache_sup.erl @@ -47,17 +47,16 @@ start_child(Listener) -> %%%========================================================================= %%% Supervisor callback %%%========================================================================= -init(_O) -> - RestartStrategy = simple_one_for_one, - MaxR = 0, - MaxT = 3600, - - Name = undefined, % As simple_one_for_one is used. - StartFunc = {ssl_server_session_cache, start_link, []}, - Restart = temporary, % E.g. should not be restarted - Shutdown = 4000, - Modules = [ssl_server_session_cache], - Type = worker, - - ChildSpec = {Name, StartFunc, Restart, Shutdown, Type, Modules}, - {ok, {{RestartStrategy, MaxR, MaxT}, [ChildSpec]}}. +init(_) -> + SupFlags = #{strategy => simple_one_for_one, + intensity => 0, + period => 3600 + }, + ChildSpecs = [#{id => undefined, + start => {ssl_server_session_cache, start_link, []}, + restart => temporary, + shutdown => 4000, + modules => [ssl_server_session_cache], + type => worker + }], + {ok, {SupFlags, ChildSpecs}}. diff --git a/lib/ssl/src/dtls_server_sup.erl b/lib/ssl/src/dtls_server_sup.erl index 7ec6db3984..1430627cf2 100644 --- a/lib/ssl/src/dtls_server_sup.erl +++ b/lib/ssl/src/dtls_server_sup.erl @@ -43,33 +43,34 @@ start_link() -> %%% Supervisor callback %%%========================================================================= -init([]) -> - DTLSListeners = dtls_listeners_spec(), - %% Add SessionTracker if we add DTLS-1.3 - Pre_1_3SessionTracker = ssl_server_session_child_spec(), - - {ok, {{one_for_all, 10, 3600}, [DTLSListeners, - Pre_1_3SessionTracker - ]}}. - +init([]) -> + SupFlags = #{strategy => one_for_all, + intensity => 10, + period => 3600 + }, + ChildSpecs = [dtls_listeners_spec(), + ssl_server_session_child_spec() + %% TODO Add DTLS-1.3 session ticket handling + ], + {ok, {SupFlags, ChildSpecs}}. %%-------------------------------------------------------------------- %%% Internal functions %%-------------------------------------------------------------------- dtls_listeners_spec() -> - Name = dtls_listener, - StartFunc = {dtls_listener_sup, start_link, []}, - Restart = permanent, - Shutdown = 4000, - Modules = [], - Type = supervisor, - {Name, StartFunc, Restart, Shutdown, Type, Modules}. + #{id => dtls_listener_sup, + start => {dtls_listener_sup, start_link, []}, + restart => permanent, + shutdown => 4000, + modules => [dtls_listener_sup], + type => supervisor + }. ssl_server_session_child_spec() -> - Name = dtls_server_session_cache_sup, - StartFunc = {dtls_server_session_cache_sup, start_link, []}, - Restart = permanent, - Shutdown = 4000, - Modules = [dtls_server_session_cache_sup], - Type = supervisor, - {Name, StartFunc, Restart, Shutdown, Type, Modules}. + #{id => dtls_server_session_cache_sup, + start => {dtls_server_session_cache_sup, start_link, []}, + restart => permanent, + shutdown => 4000, + modules => [dtls_server_session_cache_sup], + type => supervisor + }. diff --git a/lib/ssl/src/dtls_sup.erl b/lib/ssl/src/dtls_sup.erl index acc4415a9f..2b73f13e19 100644 --- a/lib/ssl/src/dtls_sup.erl +++ b/lib/ssl/src/dtls_sup.erl @@ -44,33 +44,30 @@ start_link() -> %%%========================================================================= init([]) -> - DTLSConnectionManager = dtls_connection_manager_child_spec(), - DTLSServers = dtls_server_spec(), - - {ok, {{one_for_one, 10, 3600}, [DTLSConnectionManager, - DTLSServers - ]}}. + SupFlags = #{strategy => one_for_one, + intensity => 10, + period => 3600 + }, + Children = [dtls_connection_child_spec(), server_instance_child_spec()], + {ok, {SupFlags, Children}}. - %%-------------------------------------------------------------------- %%% Internal functions %%-------------------------------------------------------------------- -dtls_server_spec() -> - Name = dtls_servers, - StartFunc = {dtls_server_sup, start_link, []}, - Restart = permanent, - Shutdown = 4000, - Modules = [dtls_server_sup], - Type = supervisor, - {Name, StartFunc, Restart, Shutdown, Type, Modules}. - -dtls_connection_manager_child_spec() -> - Name = dtls_connection, - StartFunc = {dtls_connection_sup, start_link, []}, - Restart = permanent, - - Shutdown = 4000, - Modules = [dtls_connection_sup], - Type = supervisor, - {Name, StartFunc, Restart, Shutdown, Type, Modules}. +dtls_connection_child_spec() -> + #{id => dtls_connection_sup, + start => {dtls_connection_sup, start_link, []}, + restart => permanent, + shutdown => 4000, + modules => [dtls_connection_sup], + type => supervisor + }. +server_instance_child_spec() -> + #{id => dtls_server_sup, + start => {dtls_server_sup, start_link, []}, + restart => permanent, + shutdown => 4000, + modules => [dtls_server_sup], + type => supervisor + }. diff --git a/lib/ssl/src/inet_tls_dist.erl b/lib/ssl/src/inet_tls_dist.erl index 5cfbeff387..cddc4f90bf 100644 --- a/lib/ssl/src/inet_tls_dist.erl +++ b/lib/ssl/src/inet_tls_dist.erl @@ -53,15 +53,7 @@ select(Node) -> gen_select(inet_tcp, Node). gen_select(Driver, Node) -> - case dist_util:split_node(Node) of - {node,_,Host} -> - case Driver:getaddr(Host) of - {ok, _} -> true; - _ -> false - end; - _ -> - false - end. + inet_tcp_dist:gen_select(Driver, Node). %% ------------------------------------------------------------ %% Get the address family that this distribution uses @@ -582,8 +574,8 @@ do_setup_connect(Driver, Kernel, Node, Address, Ip, TcpPort, Version, Type, MyNo Opts = trace(connect_options(get_ssl_options(client))), dist_util:reset_timer(Timer), case ssl:connect( - Address, TcpPort, - [binary, {active, false}, {packet, 4}, + Ip, TcpPort, + [binary, {active, false}, {packet, 4}, {server_name_indication, Address}, Driver:family(), {nodelay, true}] ++ Opts, net_kernel:connecttime()) of {ok, #sslsocket{pid = [_, DistCtrl| _]} = SslSocket} -> diff --git a/lib/ssl/src/ssl.app.src b/lib/ssl/src/ssl.app.src index d761520856..a43a84f26d 100644 --- a/lib/ssl/src/ssl.app.src +++ b/lib/ssl/src/ssl.app.src @@ -18,6 +18,7 @@ tls_server_session_ticket_sup, tls_server_session_ticket, tls_sup, + tls_dyn_connection_sup, ssl_dh_groups, %% DTLS dtls_connection, diff --git a/lib/ssl/src/ssl.erl b/lib/ssl/src/ssl.erl index 672cd6ef14..b1ea481fc7 100644 --- a/lib/ssl/src/ssl.erl +++ b/lib/ssl/src/ssl.erl @@ -1526,14 +1526,15 @@ handle_options(Transport, Socket, Opts0, Role, Host) -> %% Ensure all options are evaluated at startup SslOpts1 = add_missing_options(SslOpts0, ?RULES), - SslOpts = #{protocol := Protocol} + SslOpts2 = #{protocol := Protocol} = process_options(SslOpts1, #{}, #{role => Role, host => Host, rules => ?RULES}), - maybe_client_warn_no_verify(SslOpts, Role), + maybe_client_warn_no_verify(SslOpts2, Role), + SslOpts = maps:without([warn_verify_none], SslOpts2), %% Handle special options {Sock, Emulated} = emulated_options(Transport, Socket, Protocol, SockOpts0), ConnetionCb = connection_cb(Protocol), @@ -1862,7 +1863,7 @@ handle_option(user_lookup_fun = Option, Value0, Value = validate_option(Option, Value0), OptionsMap#{Option => Value}; handle_option(verify = Option, unbound, OptionsMap, #{rules := Rules}) -> - handle_verify_option(default_value(Option, Rules), OptionsMap); + handle_verify_option(default_value(Option, Rules), OptionsMap#{warn_verify_none => true}); handle_option(verify = _Option, Value, OptionsMap, _Env) -> handle_verify_option(Value, OptionsMap); handle_option(verify_fun = Option, unbound, #{verify := Verify} = OptionsMap, #{rules := Rules}) @@ -2830,9 +2831,11 @@ add_filter(undefined, Filters) -> add_filter(Filter, Filters) -> [Filter | Filters]. -maybe_client_warn_no_verify(#{verify := verify_none, log_level := LogLevel}, client) -> - ssl_logger:log(warning, LogLevel, #{description => "Authenticity is not established by certificate path validation", - reason => "Option {verify, verify_peer} and cacertfile/cacerts is missing"}, #{}); +maybe_client_warn_no_verify(#{verify := verify_none, + warn_verify_none := true, + log_level := LogLevel}, client) -> + ssl_logger:log(warning, LogLevel, #{description => "Authenticity is not established by certificate path validation", + reason => "Option {verify, verify_peer} and cacertfile/cacerts is missing"}, #{}); maybe_client_warn_no_verify(_,_) -> - %% Client certificate validation is optional in TLS + %% Warning not needed. Note client certificate validation is optional in TLS ok. diff --git a/lib/ssl/src/ssl_admin_sup.erl b/lib/ssl/src/ssl_admin_sup.erl index 0cf2ab6332..9c10bf7b41 100644 --- a/lib/ssl/src/ssl_admin_sup.erl +++ b/lib/ssl/src/ssl_admin_sup.erl @@ -43,11 +43,15 @@ start_link() -> %%% Supervisor callback %%%========================================================================= -init([]) -> - PEMCache = pem_cache_child_spec(), - SessionCertManager = session_and_cert_manager_child_spec(), - TicketStore = ticket_store_spec(), - {ok, {{rest_for_one, 10, 3600}, [PEMCache, SessionCertManager, TicketStore]}}. +init([]) -> + SupFlags = #{strategy => rest_for_one, + intensity => 10, + period => 3600 + }, + ChildSpecs = [pem_cache_child_spec(), + session_and_cert_manager_child_spec(), + ticket_store_spec()], + {ok, {SupFlags, ChildSpecs}}. manager_opts() -> CbOpts = case application:get_env(ssl, session_cb) of @@ -69,34 +73,33 @@ manager_opts() -> %%-------------------------------------------------------------------- pem_cache_child_spec() -> - Name = ssl_pem_cache, - StartFunc = {ssl_pem_cache, start_link, [[]]}, - Restart = permanent, - Shutdown = 4000, - Modules = [ssl_pem_cache], - Type = worker, - {Name, StartFunc, Restart, Shutdown, Type, Modules}. - + #{id => ssl_pem_cache, + start => {ssl_pem_cache, start_link, [[]]}, + restart => permanent, + shutdown => 4000, + modules => [ssl_pem_cache], + type => worker + }. session_and_cert_manager_child_spec() -> Opts = manager_opts(), - Name = ssl_manager, - StartFunc = {ssl_manager, start_link, [Opts]}, - Restart = permanent, - Shutdown = 4000, - Modules = [ssl_manager], - Type = worker, - {Name, StartFunc, Restart, Shutdown, Type, Modules}. + #{id => ssl_manager, + start => {ssl_manager, start_link, [Opts]}, + restart => permanent, + shutdown => 4000, + modules => [ssl_manager], + type => worker + }. ticket_store_spec() -> - Name = tls_client_ticket_store, Size = client_session_ticket_store_size(), Lifetime = client_session_ticket_lifetime(), - StartFunc = {tls_client_ticket_store, start_link, [Size,Lifetime]}, - Restart = permanent, - Shutdown = 4000, - Modules = [tls_client_ticket_store], - Type = worker, - {Name, StartFunc, Restart, Shutdown, Type, Modules}. + #{id => tls_client_ticket_store, + start => {tls_client_ticket_store, start_link, [Size, Lifetime]}, + restart => permanent, + shutdown => 4000, + modules => [tls_client_ticket_store], + type => worker + }. session_cb_init_args() -> case application:get_env(ssl, session_cb_init_args) of diff --git a/lib/ssl/src/ssl_alert.hrl b/lib/ssl/src/ssl_alert.hrl index eb3e3ec837..420b014aae 100644 --- a/lib/ssl/src/ssl_alert.hrl +++ b/lib/ssl/src/ssl_alert.hrl @@ -26,6 +26,7 @@ -ifndef(ssl_alert). -define(ssl_alert, true). +%%-define(ssl_debug, true). -include_lib("kernel/include/logger.hrl"). %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% @@ -114,8 +115,14 @@ -define(CERTIFICATE_REQUIRED, 116). -define(NO_APPLICATION_PROTOCOL, 120). --define(ALERT_REC(Level,Desc), #alert{level=Level,description=Desc,where= ?LOCATION}). --define(ALERT_REC(Level,Desc,Reason), #alert{level=Level,description=Desc,where=?LOCATION,reason=Reason}). +-ifdef(ssl_debug). +-define(ST_LOCATION, fun(Map) -> Map#{st => process_info(self(), current_stacktrace)} end (?LOCATION)). +-else. +-define(ST_LOCATION, ?LOCATION). +-endif. + +-define(ALERT_REC(Level,Desc), #alert{level=Level,description=Desc,where= ?ST_LOCATION}). +-define(ALERT_REC(Level,Desc,Reason), #alert{level=Level,description=Desc,where=?ST_LOCATION,reason=Reason}). -define(MAX_ALERTS, 10). diff --git a/lib/ssl/src/ssl_certificate.erl b/lib/ssl/src/ssl_certificate.erl index ebaa9a6bec..375a416b95 100644 --- a/lib/ssl/src/ssl_certificate.erl +++ b/lib/ssl/src/ssl_certificate.erl @@ -548,10 +548,10 @@ is_supported_signature_algorithm_1_2(#'OTPCertificate'{signatureAlgorithm = is_supported_signature_algorithm_1_2(#'OTPCertificate'{signatureAlgorithm = SignAlg}, SignAlgs) -> Scheme = ssl_cipher:signature_algorithm_to_scheme(SignAlg), {Hash, Sign, _ } = ssl_cipher:scheme_to_components(Scheme), - lists:member({pre_1_3_hash(Hash), pre_1_3_sign(Sign)}, SignAlgs). + ssl_cipher:is_supported_sign({pre_1_3_hash(Hash), pre_1_3_sign(Sign)}, ssl_cipher:signature_schemes_1_2(SignAlgs)). is_supported_signature_algorithm_1_3(#'OTPCertificate'{signatureAlgorithm = SignAlg}, SignAlgs) -> Scheme = ssl_cipher:signature_algorithm_to_scheme(SignAlg), - lists:member(Scheme, SignAlgs). + ssl_cipher:is_supported_sign(Scheme, SignAlgs). pre_1_3_sign(rsa_pkcs1) -> rsa; diff --git a/lib/ssl/src/ssl_cipher.erl b/lib/ssl/src/ssl_cipher.erl index 35b2da773b..52d37d9093 100644 --- a/lib/ssl/src/ssl_cipher.erl +++ b/lib/ssl/src/ssl_cipher.erl @@ -58,7 +58,9 @@ calc_mac_hash/4, calc_mac_hash/6, is_stream_ciphersuite/1, + is_supported_sign/2, signature_scheme/1, + signature_schemes_1_2/1, scheme_to_components/1, hash_size/1, effective_key_bits/1, @@ -564,6 +566,119 @@ hash_size(sha384) -> hash_size(sha512) -> 64. +is_supported_sign({Hash, rsa} = SignAlgo, HashSigns) -> %% PRE TLS-1.3 + lists:member(SignAlgo, HashSigns) orelse + lists:member({Hash, rsa_pss_rsae}, HashSigns); +is_supported_sign(rsa_pkcs1_sha256 = SignAlgo, HashSigns) -> %% TLS-1.3 leagcy + lists:member(SignAlgo, HashSigns) orelse + lists:member(rsa_pss_rsae_sha256, HashSigns); +is_supported_sign(rsa_pkcs1_sha384 = SignAlgo, HashSigns) -> %% TLS-1.3 leagcy + lists:member(SignAlgo, HashSigns) orelse + lists:member(rsa_pss_rsae_sha384, HashSigns); +is_supported_sign(rsa_pkcs1_sha512 = SignAlgo, HashSigns) -> %% TLS-1.3 leagcy + lists:member(SignAlgo, HashSigns) orelse + lists:member(rsa_pss_rsae_sha512, HashSigns); +is_supported_sign(SignAlgo, HashSigns) -> %% PRE TLS-1.3 SignAlgo::tuple() TLS-1.3 SignAlgo::atom() + lists:member(SignAlgo, HashSigns). + +signature_scheme(rsa_pkcs1_sha256) -> ?RSA_PKCS1_SHA256; +signature_scheme(rsa_pkcs1_sha384) -> ?RSA_PKCS1_SHA384; +signature_scheme(rsa_pkcs1_sha512) -> ?RSA_PKCS1_SHA512; +signature_scheme(ecdsa_secp256r1_sha256) -> ?ECDSA_SECP256R1_SHA256; +signature_scheme(ecdsa_secp384r1_sha384) -> ?ECDSA_SECP384R1_SHA384; +signature_scheme(ecdsa_secp521r1_sha512) -> ?ECDSA_SECP521R1_SHA512; +signature_scheme(rsa_pss_rsae_sha256) -> ?RSA_PSS_RSAE_SHA256; +signature_scheme(rsa_pss_rsae_sha384) -> ?RSA_PSS_RSAE_SHA384; +signature_scheme(rsa_pss_rsae_sha512) -> ?RSA_PSS_RSAE_SHA512; +signature_scheme(eddsa_ed25519) -> ?ED25519; +signature_scheme(eddsa_ed448) -> ?ED448; +signature_scheme(rsa_pss_pss_sha256) -> ?RSA_PSS_PSS_SHA256; +signature_scheme(rsa_pss_pss_sha384) -> ?RSA_PSS_PSS_SHA384; +signature_scheme(rsa_pss_pss_sha512) -> ?RSA_PSS_PSS_SHA512; +signature_scheme(rsa_pkcs1_sha1) -> ?RSA_PKCS1_SHA1; +signature_scheme(ecdsa_sha1) -> ?ECDSA_SHA1; +%% New algorithms on legacy format +signature_scheme({sha512, rsa_pss_pss}) -> + ?RSA_PSS_PSS_SHA512; +signature_scheme({sha384, rsa_pss_pss}) -> + ?RSA_PSS_PSS_SHA384; +signature_scheme({sha256, rsa_pss_pss}) -> + ?RSA_PSS_PSS_SHA256; +signature_scheme({sha512, rsa_pss_rsae}) -> + ?RSA_PSS_RSAE_SHA512; +signature_scheme({sha384, rsa_pss_rsae}) -> + ?RSA_PSS_RSAE_SHA384; +signature_scheme({sha256, rsa_pss_rsae}) -> + ?RSA_PSS_RSAE_SHA256; +%% Handling legacy signature algorithms +signature_scheme({Hash0, Sign0}) -> + Hash = hash_algorithm(Hash0), + Sign = sign_algorithm(Sign0), + <<?UINT16(SigAlg)>> = <<?BYTE(Hash),?BYTE(Sign)>>, + SigAlg; +signature_scheme(?RSA_PKCS1_SHA256) -> rsa_pkcs1_sha256; +signature_scheme(?RSA_PKCS1_SHA384) -> rsa_pkcs1_sha384; +signature_scheme(?RSA_PKCS1_SHA512) -> rsa_pkcs1_sha512; +signature_scheme(?ECDSA_SECP256R1_SHA256) -> ecdsa_secp256r1_sha256; +signature_scheme(?ECDSA_SECP384R1_SHA384) -> ecdsa_secp384r1_sha384; +signature_scheme(?ECDSA_SECP521R1_SHA512) -> ecdsa_secp521r1_sha512; +signature_scheme(?RSA_PSS_RSAE_SHA256) -> rsa_pss_rsae_sha256; +signature_scheme(?RSA_PSS_RSAE_SHA384) -> rsa_pss_rsae_sha384; +signature_scheme(?RSA_PSS_RSAE_SHA512) -> rsa_pss_rsae_sha512; +signature_scheme(?ED25519) -> eddsa_ed25519; +signature_scheme(?ED448) -> eddsa_ed448; +signature_scheme(?RSA_PSS_PSS_SHA256) -> rsa_pss_pss_sha256; +signature_scheme(?RSA_PSS_PSS_SHA384) -> rsa_pss_pss_sha384; +signature_scheme(?RSA_PSS_PSS_SHA512) -> rsa_pss_pss_sha512; +signature_scheme(?RSA_PKCS1_SHA1) -> rsa_pkcs1_sha1; +signature_scheme(?ECDSA_SHA1) -> ecdsa_sha1; +%% Handling legacy signature algorithms for logging purposes. These algorithms +%% cannot be used in TLS 1.3 handshakes. +signature_scheme(SignAlgo) when is_integer(SignAlgo) -> + <<?BYTE(Hash),?BYTE(Sign)>> = <<?UINT16(SignAlgo)>>, + try + {ssl_cipher:hash_algorithm(Hash), ssl_cipher:sign_algorithm(Sign)} + catch + _:_ -> + unassigned + end; +signature_scheme(_) -> unassigned. + +signature_schemes_1_2(SigAlgs) -> + lists:foldl(fun(Alg, Acc) when is_atom(Alg) -> + case scheme_to_components(Alg) of + {Hash, Sign = rsa_pss_pss,_} -> + [{Hash, Sign} | Acc]; + {Hash, Sign = rsa_pss_rsae,_} -> + [{Hash, Sign} | Acc]; + {_, _, _} -> + Acc + end; + (Alg, Acc) -> + [Alg| Acc] + end, [], SigAlgs). + +%% TODO: reserved code points? + +scheme_to_components(rsa_pkcs1_sha256) -> {sha256, rsa_pkcs1, undefined}; +scheme_to_components(rsa_pkcs1_sha384) -> {sha384, rsa_pkcs1, undefined}; +scheme_to_components(rsa_pkcs1_sha512) -> {sha512, rsa_pkcs1, undefined}; +scheme_to_components(ecdsa_secp256r1_sha256) -> {sha256, ecdsa, secp256r1}; +scheme_to_components(ecdsa_secp384r1_sha384) -> {sha384, ecdsa, secp384r1}; +scheme_to_components(ecdsa_secp521r1_sha512) -> {sha512, ecdsa, secp521r1}; +scheme_to_components(rsa_pss_rsae_sha256) -> {sha256, rsa_pss_rsae, undefined}; +scheme_to_components(rsa_pss_rsae_sha384) -> {sha384, rsa_pss_rsae, undefined}; +scheme_to_components(rsa_pss_rsae_sha512) -> {sha512, rsa_pss_rsae, undefined}; +scheme_to_components(eddsa_ed25519) -> {none, eddsa, ed25519}; +scheme_to_components(eddsa_ed448) -> {none, eddsa, ed448}; +scheme_to_components(rsa_pss_pss_sha256) -> {sha256, rsa_pss_pss, undefined}; +scheme_to_components(rsa_pss_pss_sha384) -> {sha384, rsa_pss_pss, undefined}; +scheme_to_components(rsa_pss_pss_sha512) -> {sha512, rsa_pss_pss, undefined}; +scheme_to_components(rsa_pkcs1_sha1) -> {sha1, rsa_pkcs1, undefined}; +scheme_to_components(ecdsa_sha1) -> {sha1, ecdsa, undefined}; +%% Handling legacy signature algorithms +scheme_to_components({Hash,Sign}) -> {Hash, Sign, undefined}. + %%-------------------------------------------------------------------- %%% Internal functions %%-------------------------------------------------------------------- @@ -752,71 +867,6 @@ sign_algorithm(Other) when is_integer(Other) andalso ((Other >= 4) and (Other =< sign_algorithm(Other) when is_integer(Other) andalso ((Other >= 224) and (Other =< 255)) -> Other. -signature_scheme(rsa_pkcs1_sha256) -> ?RSA_PKCS1_SHA256; -signature_scheme(rsa_pkcs1_sha384) -> ?RSA_PKCS1_SHA384; -signature_scheme(rsa_pkcs1_sha512) -> ?RSA_PKCS1_SHA512; -signature_scheme(ecdsa_secp256r1_sha256) -> ?ECDSA_SECP256R1_SHA256; -signature_scheme(ecdsa_secp384r1_sha384) -> ?ECDSA_SECP384R1_SHA384; -signature_scheme(ecdsa_secp521r1_sha512) -> ?ECDSA_SECP521R1_SHA512; -signature_scheme(rsa_pss_rsae_sha256) -> ?RSA_PSS_RSAE_SHA256; -signature_scheme(rsa_pss_rsae_sha384) -> ?RSA_PSS_RSAE_SHA384; -signature_scheme(rsa_pss_rsae_sha512) -> ?RSA_PSS_RSAE_SHA512; -signature_scheme(eddsa_ed25519) -> ?ED25519; -signature_scheme(eddsa_ed448) -> ?ED448; -signature_scheme(rsa_pss_pss_sha256) -> ?RSA_PSS_PSS_SHA256; -signature_scheme(rsa_pss_pss_sha384) -> ?RSA_PSS_PSS_SHA384; -signature_scheme(rsa_pss_pss_sha512) -> ?RSA_PSS_PSS_SHA512; -signature_scheme(rsa_pkcs1_sha1) -> ?RSA_PKCS1_SHA1; -signature_scheme(ecdsa_sha1) -> ?ECDSA_SHA1; -%% Handling legacy signature algorithms -signature_scheme({Hash0, Sign0}) -> - Hash = hash_algorithm(Hash0), - Sign = sign_algorithm(Sign0), - <<?UINT16(SigAlg)>> = <<?BYTE(Hash),?BYTE(Sign)>>, - SigAlg; -signature_scheme(?RSA_PKCS1_SHA256) -> rsa_pkcs1_sha256; -signature_scheme(?RSA_PKCS1_SHA384) -> rsa_pkcs1_sha384; -signature_scheme(?RSA_PKCS1_SHA512) -> rsa_pkcs1_sha512; -signature_scheme(?ECDSA_SECP256R1_SHA256) -> ecdsa_secp256r1_sha256; -signature_scheme(?ECDSA_SECP384R1_SHA384) -> ecdsa_secp384r1_sha384; -signature_scheme(?ECDSA_SECP521R1_SHA512) -> ecdsa_secp521r1_sha512; -signature_scheme(?RSA_PSS_RSAE_SHA256) -> rsa_pss_rsae_sha256; -signature_scheme(?RSA_PSS_RSAE_SHA384) -> rsa_pss_rsae_sha384; -signature_scheme(?RSA_PSS_RSAE_SHA512) -> rsa_pss_rsae_sha512; -signature_scheme(?ED25519) -> eddsa_ed25519; -signature_scheme(?ED448) -> eddsa_ed448; -signature_scheme(?RSA_PSS_PSS_SHA256) -> rsa_pss_pss_sha256; -signature_scheme(?RSA_PSS_PSS_SHA384) -> rsa_pss_pss_sha384; -signature_scheme(?RSA_PSS_PSS_SHA512) -> rsa_pss_pss_sha512; -signature_scheme(?RSA_PKCS1_SHA1) -> rsa_pkcs1_sha1; -signature_scheme(?ECDSA_SHA1) -> ecdsa_sha1; -%% Handling legacy signature algorithms for logging purposes. These algorithms -%% cannot be used in TLS 1.3 handshakes. -signature_scheme(SignAlgo) when is_integer(SignAlgo) -> - <<?BYTE(Hash),?BYTE(Sign)>> = <<?UINT16(SignAlgo)>>, - {ssl_cipher:hash_algorithm(Hash), ssl_cipher:sign_algorithm(Sign)}; -signature_scheme(_) -> unassigned. -%% TODO: reserved code points? - -scheme_to_components(rsa_pkcs1_sha256) -> {sha256, rsa_pkcs1, undefined}; -scheme_to_components(rsa_pkcs1_sha384) -> {sha384, rsa_pkcs1, undefined}; -scheme_to_components(rsa_pkcs1_sha512) -> {sha512, rsa_pkcs1, undefined}; -scheme_to_components(ecdsa_secp256r1_sha256) -> {sha256, ecdsa, secp256r1}; -scheme_to_components(ecdsa_secp384r1_sha384) -> {sha384, ecdsa, secp384r1}; -scheme_to_components(ecdsa_secp521r1_sha512) -> {sha512, ecdsa, secp521r1}; -scheme_to_components(rsa_pss_rsae_sha256) -> {sha256, rsa_pss_rsae, undefined}; -scheme_to_components(rsa_pss_rsae_sha384) -> {sha384, rsa_pss_rsae, undefined}; -scheme_to_components(rsa_pss_rsae_sha512) -> {sha512, rsa_pss_rsae, undefined}; -scheme_to_components(eddsa_ed25519) -> {none, eddsa, ed25519}; -scheme_to_components(eddsa_ed448) -> {none, eddsa, ed448}; -scheme_to_components(rsa_pss_pss_sha256) -> {sha256, rsa_pss_pss, undefined}; -scheme_to_components(rsa_pss_pss_sha384) -> {sha384, rsa_pss_pss, undefined}; -scheme_to_components(rsa_pss_pss_sha512) -> {sha512, rsa_pss_pss, undefined}; -scheme_to_components(rsa_pkcs1_sha1) -> {sha1, rsa_pkcs1, undefined}; -scheme_to_components(ecdsa_sha1) -> {sha1, ecdsa, undefined}; -%% Handling legacy signature algorithms -scheme_to_components({Hash,Sign}) -> {Hash, Sign, undefined}. - signature_algorithm_to_scheme(#'SignatureAlgorithm'{algorithm = ?'id-RSASSA-PSS', parameters = #'RSASSA-PSS-params'{ maskGenAlgorithm = @@ -842,7 +892,7 @@ signature_algorithm_to_scheme(#'SignatureAlgorithm'{algorithm = ?'ecdsa-with-SHA signature_algorithm_to_scheme(#'SignatureAlgorithm'{algorithm = ?'ecdsa-with-SHA384'}) -> ecdsa_secp384r1_sha384; signature_algorithm_to_scheme(#'SignatureAlgorithm'{algorithm = ?'ecdsa-with-SHA512'}) -> - ecdsa_secp512r1_sha512; + ecdsa_secp521r1_sha512; signature_algorithm_to_scheme(#'SignatureAlgorithm'{algorithm = ?'sha-1WithRSAEncryption'}) -> rsa_pkcs1_sha1; signature_algorithm_to_scheme(#'SignatureAlgorithm'{algorithm = ?sha1WithRSAEncryption}) -> diff --git a/lib/ssl/src/ssl_connection_sup.erl b/lib/ssl/src/ssl_connection_sup.erl index d930ecf2fa..f5d170934c 100644 --- a/lib/ssl/src/ssl_connection_sup.erl +++ b/lib/ssl/src/ssl_connection_sup.erl @@ -44,32 +44,33 @@ start_link() -> %%%========================================================================= init([]) -> - - TLSSup = tls_sup_child_spec(), - DTLSSup = dtls_sup_child_spec(), - - {ok, {{one_for_one, 10, 3600}, [TLSSup, DTLSSup]}}. + ChildSpecs = [tls_sup_child_spec(), dtls_sup_child_spec()], + SupFlags = #{strategy => one_for_one, + intensity => 10, + period => 3600 + }, + {ok, {SupFlags, ChildSpecs}}. + %%-------------------------------------------------------------------- %%% Internal functions %%-------------------------------------------------------------------- tls_sup_child_spec() -> - Name = tls_sup, - StartFunc = {tls_sup, start_link, []}, - Restart = permanent, - Shutdown = 4000, - Modules = [tls_sup], - Type = supervisor, - {Name, StartFunc, Restart, Shutdown, Type, Modules}. + #{id => tls_sup, + start => {tls_sup, start_link, []}, + restart => permanent, + shutdown => 4000, + modules => [tls_sup], + type => supervisor + }. dtls_sup_child_spec() -> - Name = dtls_sup, - StartFunc = {dtls_sup, start_link, []}, - Restart = permanent, - Shutdown = 4000, - Modules = [dtls_sup], - Type = supervisor, - {Name, StartFunc, Restart, Shutdown, Type, Modules}. - + #{id => dtls_sup, + start => {dtls_sup, start_link, []}, + restart => permanent, + shutdown => 4000, + modules => [dtls_sup], + type => supervisor + }. diff --git a/lib/ssl/src/ssl_crl.erl b/lib/ssl/src/ssl_crl.erl index d3b66df870..be44693b86 100644 --- a/lib/ssl/src/ssl_crl.erl +++ b/lib/ssl/src/ssl_crl.erl @@ -44,8 +44,8 @@ trusted_cert_and_path(CRL, issuer_not_found, CertPath, {Db, DbRef}) -> {error, unknown_ca} -> Issuer = public_key:pkix_normalize_name(public_key:pkix_crl_issuer(CRL)), IsIssuerFun = - fun({_Key, #cert{otp=ErlCertCandidate}}, Acc) -> - verify_crl_issuer(CRL, ErlCertCandidate, Issuer, Acc); + fun({_Key, CertCandidate}, Acc) -> + verify_crl_issuer(CRL, CertCandidate, Issuer, Acc); (_, Acc) -> Acc end, @@ -63,8 +63,8 @@ trusted_cert_and_path(CRL, issuer_not_found, CertPath, {Db, DbRef}) -> search_certpath(CRL, CertPath, Db, DbRef) -> Issuer = public_key:pkix_normalize_name(public_key:pkix_crl_issuer(CRL)), IsIssuerFun = - fun(ErlCertCandidate, Acc) -> - verify_crl_issuer(CRL, ErlCertCandidate, Issuer, Acc) + fun(CertCandidate, Acc) -> + verify_crl_issuer(CRL, CertCandidate, Issuer, Acc) end, case find_issuer(IsIssuerFun, certpath, CertPath) of {ok, OtpCert} -> @@ -105,13 +105,13 @@ find_issuer(IsIssuerFun, Db, _) -> Result end. -verify_crl_issuer(CRL, #cert{otp = ErlCertCandidate}, Issuer, NotIssuer) -> - TBSCert = ErlCertCandidate#'OTPCertificate'.tbsCertificate, +verify_crl_issuer(CRL, #cert{otp = OTPCertCandidate}, Issuer, NotIssuer) -> + TBSCert = OTPCertCandidate#'OTPCertificate'.tbsCertificate, case public_key:pkix_normalize_name(TBSCert#'OTPTBSCertificate'.subject) of Issuer -> - case public_key:pkix_crl_verify(CRL, ErlCertCandidate) of + case public_key:pkix_crl_verify(CRL, OTPCertCandidate) of true -> - throw({ok, ErlCertCandidate}); + throw({ok, OTPCertCandidate}); false -> NotIssuer end; diff --git a/lib/ssl/src/ssl_dist_admin_sup.erl b/lib/ssl/src/ssl_dist_admin_sup.erl index f60806c4cb..3e10643dcd 100644 --- a/lib/ssl/src/ssl_dist_admin_sup.erl +++ b/lib/ssl/src/ssl_dist_admin_sup.erl @@ -44,31 +44,31 @@ start_link() -> %%%========================================================================= init([]) -> - PEMCache = pem_cache_child_spec(), - SessionCertManager = session_and_cert_manager_child_spec(), - {ok, {{rest_for_one, 10, 3600}, [PEMCache, SessionCertManager]}}. - - + ChildSpecs = [pem_cache_child_spec(), + session_and_cert_manager_child_spec()], + SupFlags = #{strategy => rest_for_one, + intensity => 10, + period => 3600 + }, + {ok, {SupFlags, ChildSpecs}}. %%-------------------------------------------------------------------- %%% Internal functions %%-------------------------------------------------------------------- pem_cache_child_spec() -> - Name = ssl_pem_cache_dist, - StartFunc = {ssl_pem_cache, start_link_dist, [[]]}, - Restart = permanent, - Shutdown = 4000, - Modules = [ssl_pem_cache], - Type = worker, - {Name, StartFunc, Restart, Shutdown, Type, Modules}. - + #{id => ssl_pem_cache_dist, + start => {ssl_pem_cache, start_link_dist, [[]]}, + restart => permanent, + shutdown => 4000, + modules => [ssl_pem_cache], + type => worker + }. session_and_cert_manager_child_spec() -> Opts = ssl_admin_sup:manager_opts(), - Name = ssl_dist_manager, - StartFunc = {ssl_manager, start_link_dist, [Opts]}, - Restart = permanent, - Shutdown = 4000, - Modules = [ssl_manager], - Type = worker, - {Name, StartFunc, Restart, Shutdown, Type, Modules}. - + #{id => ssl_dist_manager, + start => {ssl_manager, start_link_dist, [Opts]}, + restart => permanent, + shutdown => 4000, + modules => [ssl_manager], + type => worker + }. diff --git a/lib/ssl/src/ssl_dist_connection_sup.erl b/lib/ssl/src/ssl_dist_connection_sup.erl index 441a7577be..47c467b358 100644 --- a/lib/ssl/src/ssl_dist_connection_sup.erl +++ b/lib/ssl/src/ssl_dist_connection_sup.erl @@ -43,19 +43,22 @@ start_link() -> %%% Supervisor callback %%%========================================================================= init([]) -> - TLSSup = tls_sup_child_spec(), - {ok, {{one_for_one, 10, 3600}, [TLSSup]}}. + SupFlags = #{strategy => one_for_one, + intensity => 10, + period => 3600 + }, + ChildSpecs = [tls_sup_child_spec()], + {ok, {SupFlags, ChildSpecs}}. - %%-------------------------------------------------------------------- %%% Internal functions %%-------------------------------------------------------------------- tls_sup_child_spec() -> - Name = dist_tls_sup, - StartFunc = {tls_dist_sup, start_link, []}, - Restart = permanent, - Shutdown = 4000, - Modules = [tls_dist_sup], - Type = supervisor, - {Name, StartFunc, Restart, Shutdown, Type, Modules}. + #{id => tls_dist_sup, + start => {tls_dist_sup, start_link, []}, + restart => permanent, + shutdown => 4000, + modules => [tls_dist_sup], + type => supervisor + }. diff --git a/lib/ssl/src/ssl_dist_sup.erl b/lib/ssl/src/ssl_dist_sup.erl index ae0887c3d9..74e4775413 100644 --- a/lib/ssl/src/ssl_dist_sup.erl +++ b/lib/ssl/src/ssl_dist_sup.erl @@ -58,30 +58,34 @@ start_link() -> %%%========================================================================= init([]) -> - AdminSup = ssl_admin_child_spec(), - ConnectionSup = ssl_connection_sup(), - {ok, {{one_for_all, 10, 3600}, [AdminSup, ConnectionSup]}}. + SupFlags = #{strategy => one_for_all, + intensity => 10, + period => 3600 + }, + ChildSpecs = [ssl_admin_child_spec(), + ssl_connection_sup()], + {ok, {SupFlags, ChildSpecs}}. %%-------------------------------------------------------------------- %%% Internal functions %%-------------------------------------------------------------------- ssl_admin_child_spec() -> - Name = ssl_dist_admin_sup, - StartFunc = {ssl_dist_admin_sup, start_link , []}, - Restart = permanent, - Shutdown = 4000, - Modules = [ssl_dist_admin_sup], - Type = supervisor, - {Name, StartFunc, Restart, Shutdown, Type, Modules}. - + #{id => ssl_dist_admin_sup, + start => {ssl_dist_admin_sup, start_link , []}, + restart => permanent, + shutdown => 4000, + modules => [ssl_dist_admin_sup], + type => supervisor + }. + ssl_connection_sup() -> - Name = tls_dist_sup, - StartFunc = {tls_dist_sup, start_link, []}, - Restart = permanent, - Shutdown = 4000, - Modules = [tls_dist_sup], - Type = supervisor, - {Name, StartFunc, Restart, Shutdown, Type, Modules}. + #{id => tls_dist_sup, + start => {tls_dist_sup, start_link, []}, + restart => permanent, + shutdown => 4000, + modules => [tls_dist_sup], + type => supervisor + }. consult(File) -> case erl_prim_loader:get_file(File) of diff --git a/lib/ssl/src/ssl_gen_statem.erl b/lib/ssl/src/ssl_gen_statem.erl index a68f9e0836..3bbb820ef7 100644 --- a/lib/ssl/src/ssl_gen_statem.erl +++ b/lib/ssl/src/ssl_gen_statem.erl @@ -87,7 +87,7 @@ %% Alert and close handling -export([send_alert/3, - handle_own_alert/4, + handle_own_alert/3, handle_alert/3, handle_normal_shutdown/3, handle_trusted_certs_db/1, @@ -126,9 +126,8 @@ start_link(Role, Host, Port, Socket, Options, User, CbInfo) -> -spec init(list()) -> no_return(). %% Description: Initialization %%-------------------------------------------------------------------- -init([_Role, Sender, _Host, _Port, _Socket, {#{erl_dist := ErlDist} = TLSOpts, _, _}, _User, _CbInfo] = InitArgs) -> +init([_Role, _Sender, _Host, _Port, _Socket, {#{erl_dist := ErlDist} = TLSOpts, _, _}, _User, _CbInfo] = InitArgs) -> process_flag(trap_exit, true), - link(Sender), case ErlDist of true -> process_flag(priority, max); @@ -515,8 +514,7 @@ initial_hello({call, From}, {start, Timeout}, [{{timeout, handshake}, Timeout, close}]) catch {Ref, #alert{} = Alert} -> - handle_own_alert(Alert, RequestedVersion, init, - State0#state{start_or_recv_from = From}) + handle_own_alert(Alert, init, State0#state{start_or_recv_from = From}) end; initial_hello({call, From}, {start, Timeout}, #state{static_env = #static_env{role = Role, protocol_cb = Connection}, @@ -687,7 +685,11 @@ downgrade(info, {CloseTag, Socket}, downgrade(info, Info, State) -> tls_gen_connection:handle_info(Info, ?FUNCTION_NAME, State); downgrade(Type, Event, State) -> - tls_dtls_connection:?FUNCTION_NAME(Type, Event, State). + try + tls_dtls_connection:?FUNCTION_NAME(Type, Event, State) + catch throw:#alert{} = Alert -> + handle_own_alert(Alert, ?FUNCTION_NAME, State) + end. %%==================================================================== %% Event/Msg handling @@ -705,9 +707,8 @@ handle_common_event(internal, {protocol_record, TLSorDTLSRecord}, StateName, Connection:handle_protocol_record(TLSorDTLSRecord, StateName, State); handle_common_event(timeout, hibernate, _, _) -> {keep_state_and_data, [hibernate]}; -handle_common_event(internal, #change_cipher_spec{type = <<1>>}, StateName, - #state{connection_env = #connection_env{negotiated_version = Version}} = State) -> - handle_own_alert(?ALERT_REC(?FATAL, ?HANDSHAKE_FAILURE), Version, StateName, State); +handle_common_event(internal, #change_cipher_spec{type = <<1>>}, StateName, State) -> + handle_own_alert(?ALERT_REC(?FATAL, ?HANDSHAKE_FAILURE), StateName, State); handle_common_event({timeout, handshake}, close, _StateName, #state{start_or_recv_from = StartFrom} = State) -> {stop_and_reply, {shutdown, user_timeout}, @@ -718,10 +719,11 @@ handle_common_event({timeout, recv}, timeout, StateName, #state{start_or_recv_fr handle_common_event(internal, {recv, RecvFrom}, StateName, #state{start_or_recv_from = RecvFrom}) when StateName =/= connection -> {keep_state_and_data, [postpone]}; -handle_common_event(Type, Msg, StateName, #state{connection_env = - #connection_env{negotiated_version = Version}} = State) -> +handle_common_event(internal, new_connection, StateName, State) -> + {next_state, StateName, State}; +handle_common_event(Type, Msg, StateName, State) -> Alert = ?ALERT_REC(?FATAL,?UNEXPECTED_MESSAGE, {unexpected_msg, {Type, Msg}}), - handle_own_alert(Alert, Version, StateName, State). + handle_own_alert(Alert, StateName, State). handle_call({application_data, _Data}, _, _, _) -> %% In renegotiation priorities handshake, send data when handshake is finished @@ -939,7 +941,7 @@ send_alert(Alert, connection, #state{static_env = #static_env{protocol_cb = Conn send_alert(Alert, _, #state{static_env = #static_env{protocol_cb = Connection}} = State) -> Connection:send_alert(Alert, State). -handle_own_alert(Alert0, _, StateName, +handle_own_alert(Alert0, StateName, #state{static_env = #static_env{role = Role, protocol_cb = Connection}, ssl_options = #{log_level := LogLevel}} = State) -> @@ -1188,11 +1190,12 @@ call(FsmPid, Event) -> {error, closed}; exit:{normal, _} -> {error, closed}; + exit:{shutdown,_} -> + {error, closed}; exit:{{shutdown, _},_} -> {error, closed} end. - check_hostname(_, "") -> ?ALERT_REC(?FATAL, ?UNRECOGNIZED_NAME, empty_sni); diff --git a/lib/ssl/src/ssl_handshake.erl b/lib/ssl/src/ssl_handshake.erl index 63fbf5ffff..2d5e29fb61 100644 --- a/lib/ssl/src/ssl_handshake.erl +++ b/lib/ssl/src/ssl_handshake.erl @@ -652,8 +652,8 @@ encode_extensions([#srp{username = UserName} | Rest], Acc) -> encode_extensions(Rest, <<?UINT16(?SRP_EXT), ?UINT16(Len), ?BYTE(SRPLen), UserName/binary, Acc/binary>>); encode_extensions([#hash_sign_algos{hash_sign_algos = HashSignAlgos} | Rest], Acc) -> - SignAlgoList = << <<(ssl_cipher:hash_algorithm(Hash)):8, (ssl_cipher:sign_algorithm(Sign)):8>> || - {Hash, Sign} <- HashSignAlgos >>, + SignAlgoList = << <<(ssl_cipher:signature_scheme(SignatureScheme)):16 >> || + SignatureScheme <- HashSignAlgos >>, ListLen = byte_size(SignAlgoList), Len = ListLen + 2, encode_extensions(Rest, <<?UINT16(?SIGNATURE_ALGORITHMS_EXT), @@ -988,17 +988,30 @@ available_signature_algs(undefined, _) -> available_signature_algs(SupportedHashSigns, Version) when Version >= {3, 3} -> case contains_scheme(SupportedHashSigns) of true -> - #signature_algorithms{signature_scheme_list = SupportedHashSigns}; + case Version of + {3,3} -> + #hash_sign_algos{hash_sign_algos = ssl_cipher:signature_schemes_1_2(SupportedHashSigns)}; + _ -> + #signature_algorithms{signature_scheme_list = SupportedHashSigns} + end; false -> #hash_sign_algos{hash_sign_algos = SupportedHashSigns} end; available_signature_algs(_, _) -> undefined. + available_signature_algs(undefined, SupportedHashSigns, _, Version) when Version >= {3,3} -> SupportedHashSigns; -available_signature_algs(#hash_sign_algos{hash_sign_algos = ClientHashSigns}, SupportedHashSigns, +available_signature_algs(#hash_sign_algos{hash_sign_algos = ClientHashSigns}, SupportedHashSigns0, _, Version) when Version >= {3,3} -> + SupportedHashSigns = + case (Version == {3,3}) andalso contains_scheme(SupportedHashSigns0) of + true -> + ssl_cipher:signature_schemes_1_2(SupportedHashSigns0); + false -> + SupportedHashSigns0 + end, sets:to_list(sets:intersection(sets:from_list(ClientHashSigns), sets:from_list(SupportedHashSigns))); available_signature_algs(_, _, _, _) -> @@ -1880,14 +1893,14 @@ supported_cert_type_or_empty(Algo, Type) -> end. certificate_authorities(CertDbHandle, CertDbRef) -> - Authorities = certificate_authorities_from_db(CertDbHandle, CertDbRef), + Authorities = [ Cert || #cert{otp = Cert} <- certificate_authorities_from_db(CertDbHandle, CertDbRef)], Enc = fun(#'OTPCertificate'{tbsCertificate=TBSCert}) -> OTPSubj = TBSCert#'OTPTBSCertificate'.subject, DNEncodedBin = public_key:pkix_encode('Name', OTPSubj, otp), DNEncodedLen = byte_size(DNEncodedBin), <<?UINT16(DNEncodedLen), DNEncodedBin/binary>> end, - list_to_binary([Enc(Cert) || {_, Cert} <- Authorities]). + list_to_binary([Enc(Cert) || Cert <- Authorities]). certificate_authorities_from_db(CertDbHandle, CertDbRef) when is_reference(CertDbRef) -> ConnectionCerts = fun({{Ref, _, _}, Cert}, Acc) when Ref == CertDbRef -> @@ -3278,6 +3291,15 @@ filter_hashsigns([Suite | Suites], [#{key_exchange := KeyExchange} | Algos], Has %% In this case hashsigns is not used as the kexchange is anonaymous filter_hashsigns(Suites, Algos, HashSigns, Version, [Suite| Acc]). +do_filter_hashsigns(rsa = SignAlgo, Suite, Suites, Algos, HashSigns, {3,3} = Version, Acc) -> + case (lists:keymember(SignAlgo, 2, HashSigns) orelse + lists:keymember(rsa_pss_rsae, 2, HashSigns) orelse + lists:keymember(rsa_pss_pss, 2, HashSigns)) of + true -> + filter_hashsigns(Suites, Algos, HashSigns, Version, [Suite| Acc]); + false -> + filter_hashsigns(Suites, Algos, HashSigns, Version, Acc) + end; do_filter_hashsigns(SignAlgo, Suite, Suites, Algos, HashSigns, Version, Acc) -> case lists:keymember(SignAlgo, 2, HashSigns) of true -> @@ -3390,8 +3412,7 @@ is_acceptable_cert_type(Sign, Types) -> %% signature_algorithms_cert = undefined is_supported_sign(SignAlgo, _, HashSigns, []) -> - lists:member(SignAlgo, HashSigns); - + ssl_cipher:is_supported_sign(SignAlgo, HashSigns); %% {'SignatureAlgorithm',{1,2,840,113549,1,1,11},'NULL'} is_supported_sign({Hash, Sign}, 'NULL', _, SignatureSchemes) -> Fun = fun (Scheme, Acc) -> @@ -3408,7 +3429,6 @@ is_supported_sign({Hash, Sign}, 'NULL', _, SignatureSchemes) -> Hash =:= H1) end, lists:foldl(Fun, false, SignatureSchemes); - %% TODO: Implement validation for the curve used in the signature %% RFC 3279 - 2.2.3 ECDSA Signature Algorithm %% When the ecdsa-with-SHA1 algorithm identifier appears as the diff --git a/lib/ssl/src/ssl_listen_tracker_sup.erl b/lib/ssl/src/ssl_listen_tracker_sup.erl index 6afd1c0009..998ec5fbc3 100644 --- a/lib/ssl/src/ssl_listen_tracker_sup.erl +++ b/lib/ssl/src/ssl_listen_tracker_sup.erl @@ -51,20 +51,19 @@ start_child_dist(Args) -> %%%========================================================================= %%% Supervisor callback %%%========================================================================= -init(_O) -> - RestartStrategy = simple_one_for_one, - MaxR = 0, - MaxT = 3600, - - Name = undefined, % As simple_one_for_one is used. - StartFunc = {tls_socket, start_link, []}, - Restart = temporary, % E.g. should not be restarted - Shutdown = 4000, - Modules = [tls_socket], - Type = worker, - - ChildSpec = {Name, StartFunc, Restart, Shutdown, Type, Modules}, - {ok, {{RestartStrategy, MaxR, MaxT}, [ChildSpec]}}. +init(_) -> + SupFlags = #{strategy => simple_one_for_one, + intensity => 0, + period => 3600 + }, + ChildSpecs = [#{id => undefined, + start => {tls_socket, start_link, []}, + restart => temporary, + shutdown => 4000, + modules => [tls_socket], + type => worker + }], + {ok, {SupFlags, ChildSpecs}}. tracker_name(normal) -> ?MODULE; diff --git a/lib/ssl/src/ssl_record.erl b/lib/ssl/src/ssl_record.erl index f1cf503097..86df169408 100644 --- a/lib/ssl/src/ssl_record.erl +++ b/lib/ssl/src/ssl_record.erl @@ -42,7 +42,7 @@ set_server_verify_data/3, set_max_fragment_length/2, empty_connection_state/2, - empty_connection_state/3, + empty_connection_state/4, record_protocol_role/1, step_encryption_state/1, step_encryption_state_read/1, @@ -55,6 +55,7 @@ -export([cipher/4, cipher/5, decipher/4, cipher_aead/4, cipher_aead/5, decipher_aead/5, is_correct_mac/2, nonce_seed/3]). +-define(TLS_1_3, {3, 4}). -export_type([ssl_version/0, ssl_atom_version/0, connection_states/0, connection_state/0]). @@ -465,10 +466,12 @@ nonce_seed(_,_, CipherState) -> empty_connection_state(ConnectionEnd, BeastMitigation) -> MaxEarlyDataSize = ssl_config:get_max_early_data_size(), - empty_connection_state(ConnectionEnd, BeastMitigation, MaxEarlyDataSize). + empty_connection_state(ConnectionEnd, _Version = undefined, + BeastMitigation, MaxEarlyDataSize). %% -empty_connection_state(ConnectionEnd, BeastMitigation, MaxEarlyDataSize) -> - SecParams = empty_security_params(ConnectionEnd), +empty_connection_state(ConnectionEnd, Version, + BeastMitigation, MaxEarlyDataSize) -> + SecParams = init_security_parameters(ConnectionEnd, Version), #{security_parameters => SecParams, beast_mitigation => BeastMitigation, compression_state => undefined, @@ -483,13 +486,16 @@ empty_connection_state(ConnectionEnd, BeastMitigation, MaxEarlyDataSize) -> early_data_limit => false }. -empty_security_params(ConnectionEnd = ?CLIENT) -> - #security_parameters{connection_end = ConnectionEnd, - client_random = random()}; -empty_security_params(ConnectionEnd = ?SERVER) -> - #security_parameters{connection_end = ConnectionEnd, - server_random = random()}. -random() -> +init_security_parameters(?CLIENT, Version) -> + #security_parameters{connection_end = ?CLIENT, + client_random = make_random(Version)}; +init_security_parameters(?SERVER, Version) -> + #security_parameters{connection_end = ?SERVER, + server_random = make_random(Version)}. + +make_random({_Major, _Minor} = Version) when Version >= ?TLS_1_3 -> + ssl_cipher:random_bytes(32); +make_random(_Version) -> Secs_since_1970 = calendar:datetime_to_gregorian_seconds( calendar:universal_time()) - 62167219200, Random_28_bytes = ssl_cipher:random_bytes(28), diff --git a/lib/ssl/src/ssl_server_session_cache_sup.erl b/lib/ssl/src/ssl_server_session_cache_sup.erl index 88f068a319..80868215d8 100644 --- a/lib/ssl/src/ssl_server_session_cache_sup.erl +++ b/lib/ssl/src/ssl_server_session_cache_sup.erl @@ -44,22 +44,19 @@ start_link() -> start_child(Listner) -> supervisor:start_child(?MODULE, [Listner | [ssl_config:pre_1_3_session_opts(server)]]). - %%%========================================================================= %%% Supervisor callback %%%========================================================================= -init(_O) -> - RestartStrategy = simple_one_for_one, - MaxR = 3, - MaxT = 3600, - - Name = undefined, % As simple_one_for_one is used. - StartFunc = {ssl_server_session_cache, start_link, []}, - Restart = transient, % Should be restarted only on abnormal termination - Shutdown = 4000, - Modules = [ssl_server_session_cache], - Type = worker, - - ChildSpec = {Name, StartFunc, Restart, Shutdown, Type, Modules}, - {ok, {{RestartStrategy, MaxR, MaxT}, [ChildSpec]}}. - +init(_) -> + SupFlags = #{strategy => simple_one_for_one, + intensity => 3, + period => 3600 + }, + ChildSpecs = [#{id => undefined, + start => {ssl_server_session_cache, start_link, []}, + restart => transient, + shutdown => 4000, + modules => [ssl_server_session_cache], + type => worker + }], + {ok, {SupFlags, ChildSpecs}}. diff --git a/lib/ssl/src/ssl_sup.erl b/lib/ssl/src/ssl_sup.erl index 05a7aaaa82..1c38eae433 100644 --- a/lib/ssl/src/ssl_sup.erl +++ b/lib/ssl/src/ssl_sup.erl @@ -43,29 +43,32 @@ start_link() -> %%% Supervisor callback %%%========================================================================= -init([]) -> - {ok, {{rest_for_one, 10, 3600}, [ssl_admin_child_spec(), - ssl_connection_sup() - ]}}. +init([]) -> + SupFlags = #{strategy => rest_for_one, + intensity => 10, + period => 3600 + }, + ChildSpecs = [ssl_admin_child_spec(), + ssl_connection_sup()], + {ok, {SupFlags, ChildSpecs}}. %%-------------------------------------------------------------------- %%% Internal functions %%-------------------------------------------------------------------- ssl_admin_child_spec() -> - Name = ssl_admin_sup, - StartFunc = {ssl_admin_sup, start_link, []}, - Restart = permanent, - Shutdown = 4000, - Modules = [ssl_admin_sup], - Type = supervisor, - {Name, StartFunc, Restart, Shutdown, Type, Modules}. - + #{id => ssl_admin_sup, + start => {ssl_admin_sup, start_link, []}, + restart => permanent, + shutdown => 4000, + modules => [ssl_admin_sup], + type => supervisor + }. + ssl_connection_sup() -> - Name = ssl_connection_sup, - StartFunc = {ssl_connection_sup, start_link, []}, - Restart = permanent, - Shutdown = 4000, - Modules = [ssl_connection_sup], - Type = supervisor, - {Name, StartFunc, Restart, Shutdown, Type, Modules}. - + #{id => ssl_connection_sup, + start => {ssl_connection_sup, start_link, []}, + restart => permanent, + shutdown => 4000, + modules => [ssl_connection_sup], + type => supervisor + }. diff --git a/lib/ssl/src/ssl_upgrade_server_session_cache_sup.erl b/lib/ssl/src/ssl_upgrade_server_session_cache_sup.erl index 69169cca0d..62f1e8e4f1 100644 --- a/lib/ssl/src/ssl_upgrade_server_session_cache_sup.erl +++ b/lib/ssl/src/ssl_upgrade_server_session_cache_sup.erl @@ -69,20 +69,19 @@ start_child(Type) -> %%%========================================================================= %%% Supervisor callback %%%========================================================================= -init(_O) -> - RestartStrategy = simple_one_for_one, - MaxR = 3, - MaxT = 3600, - - Name = undefined, % As simple_one_for_one is used. - StartFunc = {ssl_server_session_cache, start_link, []}, - Restart = transient, % Should be restarted only on abnormal termination - Shutdown = 4000, - Modules = [ssl_server_session_cache], - Type = worker, - - ChildSpec = {Name, StartFunc, Restart, Shutdown, Type, Modules}, - {ok, {{RestartStrategy, MaxR, MaxT}, [ChildSpec]}}. +init(_) -> + SupFlags = #{strategy => simple_one_for_one, + intensity => 3, + period => 3600 + }, + ChildSpecs = [#{id => undefined, + start => {ssl_server_session_cache, start_link, []}, + restart => transient, + shutdown => 4000, + modules => [ssl_server_session_cache], + type => worker + }], + {ok, {SupFlags, ChildSpecs}}. sup_name(normal) -> ?MODULE; diff --git a/lib/ssl/src/tls_connection.erl b/lib/ssl/src/tls_connection.erl index ff06b5dc71..7f0be2dbc5 100644 --- a/lib/ssl/src/tls_connection.erl +++ b/lib/ssl/src/tls_connection.erl @@ -215,7 +215,7 @@ hello(internal, #client_hello{extensions = Extensions} = Hello, [{reply, From, {ok, Extensions}}]}; hello(internal, #server_hello{extensions = Extensions} = Hello, #state{ssl_options = #{ - handshake := hello}, + handshake := hello}, handshake_env = HsEnv, start_or_recv_from = From} = State) -> {next_state, user_hello, @@ -229,52 +229,56 @@ hello(internal, #client_hello{client_version = ClientVersion} = Hello, #state{ss %% Continue in TLS 1.3 'start' state {next_state, start, State0, [{change_callback_module, tls_connection_1_3}, {next_event, internal, Hello}]}; tls_1_0_to_1_2_fsm -> - case handle_client_hello(Hello, State0) of + try handle_client_hello(Hello, State0) of {ServerHelloExt, Type, State} -> - {next_state, hello, State, [{next_event, internal, {common_client_hello, Type, ServerHelloExt}}]}; - Alert -> - ssl_gen_statem:handle_own_alert(Alert, ClientVersion, hello, - State0#state{connection_env = CEnv#connection_env{negotiated_version - = ClientVersion}}) + {next_state, hello, State, [{next_event, internal, {common_client_hello, Type, ServerHelloExt}}]} + catch throw:#alert{} = Alert -> + State = State0#state{connection_env = CEnv#connection_env{negotiated_version = ClientVersion}}, + ssl_gen_statem:handle_own_alert(Alert, hello, State) end end; hello(internal, #server_hello{} = Hello, #state{connection_states = ConnectionStates0, - connection_env = #connection_env{negotiated_version = ReqVersion} = CEnv, + connection_env = CEnv, static_env = #static_env{role = client}, handshake_env = #handshake_env{ ocsp_stapling_state = OcspState0, renegotiation = {Renegotiation, _}} = HsEnv, session = #session{session_id = OldId}, - ssl_options = SslOptions} = State) -> - case tls_handshake:hello(Hello, SslOptions, ConnectionStates0, Renegotiation, OldId) of - #alert{} = Alert -> - ssl_gen_statem:handle_own_alert(Alert, ReqVersion, hello, - State#state{connection_env = - CEnv#connection_env{negotiated_version = ReqVersion} - }); - %% Legacy TLS 1.2 and older - {Version, NewId, ConnectionStates, ProtoExt, Protocol, OcspState} -> - tls_dtls_connection:handle_session(Hello, - Version, NewId, ConnectionStates, ProtoExt, Protocol, - State#state{ - handshake_env = HsEnv#handshake_env{ - ocsp_stapling_state = maps:merge(OcspState0,OcspState)}}); - %% TLS 1.3 - {next_state, wait_sh, SelectedVersion, OcspState} -> - %% Continue in TLS 1.3 'wait_sh' state - {next_state, wait_sh, - State#state{handshake_env = HsEnv#handshake_env{ocsp_stapling_state = maps:merge(OcspState0,OcspState)}, - connection_env = CEnv#connection_env{negotiated_version = SelectedVersion}}, - [{change_callback_module, tls_connection_1_3}, {next_event, internal, Hello}]} + ssl_options = SslOptions} = State) -> + try + case tls_handshake:hello(Hello, SslOptions, ConnectionStates0, Renegotiation, OldId) of + %% Legacy TLS 1.2 and older + {Version, NewId, ConnectionStates, ProtoExt, Protocol, OcspState} -> + tls_dtls_connection:handle_session(Hello, + Version, NewId, ConnectionStates, ProtoExt, Protocol, + State#state{ + handshake_env = HsEnv#handshake_env{ + ocsp_stapling_state = maps:merge(OcspState0,OcspState)}}); + %% TLS 1.3 + {next_state, wait_sh, SelectedVersion, OcspState} -> + %% Continue in TLS 1.3 'wait_sh' state + {next_state, wait_sh, + State#state{handshake_env = HsEnv#handshake_env{ocsp_stapling_state = maps:merge(OcspState0,OcspState)}, + connection_env = CEnv#connection_env{negotiated_version = SelectedVersion}}, + [{change_callback_module, tls_connection_1_3}, {next_event, internal, Hello}]} + end + catch throw:#alert{} = Alert -> + ssl_gen_statem:handle_own_alert(Alert, hello, State) end; hello(info, Event, State) -> tls_gen_connection:handle_info(Event, ?FUNCTION_NAME, State); hello(Type, Event, State) -> - tls_dtls_connection:gen_handshake(?FUNCTION_NAME, Type, Event, State). + try tls_dtls_connection:gen_handshake(?FUNCTION_NAME, Type, Event, State) + catch throw:#alert{} = Alert -> + ssl_gen_statem:handle_own_alert(Alert, ?FUNCTION_NAME, State) + end. user_hello(Type, Event, State) -> - tls_dtls_connection:gen_handshake(?FUNCTION_NAME, Type, Event, State). + try tls_dtls_connection:gen_handshake(?FUNCTION_NAME, Type, Event, State) + catch throw:#alert{} = Alert -> + ssl_gen_statem:handle_own_alert(Alert, ?FUNCTION_NAME, State) + end. %%-------------------------------------------------------------------- -spec abbreviated(gen_statem:event_type(), term(), #state{}) -> @@ -283,7 +287,10 @@ user_hello(Type, Event, State) -> abbreviated(info, Event, State) -> gen_info(Event, ?FUNCTION_NAME, State); abbreviated(Type, Event, State) -> - tls_dtls_connection:gen_handshake(?FUNCTION_NAME, Type, Event, State). + try tls_dtls_connection:gen_handshake(?FUNCTION_NAME, Type, Event, State) + catch throw:#alert{} = Alert -> + ssl_gen_statem:handle_own_alert(Alert, ?FUNCTION_NAME, State) + end. %%-------------------------------------------------------------------- -spec wait_ocsp_stapling(gen_statem:event_type(), term(), #state{}) -> @@ -292,7 +299,10 @@ abbreviated(Type, Event, State) -> wait_ocsp_stapling(info, Event, State) -> gen_info(Event, ?FUNCTION_NAME, State); wait_ocsp_stapling(Type, Event, State) -> - tls_dtls_connection:gen_handshake(?FUNCTION_NAME, Type, Event, State). + try tls_dtls_connection:gen_handshake(?FUNCTION_NAME, Type, Event, State) + catch throw:#alert{} = Alert -> + ssl_gen_statem:handle_own_alert(Alert, ?FUNCTION_NAME, State) + end. %%-------------------------------------------------------------------- -spec certify(gen_statem:event_type(), term(), #state{}) -> @@ -301,7 +311,10 @@ wait_ocsp_stapling(Type, Event, State) -> certify(info, Event, State) -> gen_info(Event, ?FUNCTION_NAME, State); certify(Type, Event, State) -> - tls_dtls_connection:gen_handshake(?FUNCTION_NAME, Type, Event, State). + try tls_dtls_connection:gen_handshake(?FUNCTION_NAME, Type, Event, State) + catch throw:#alert{} = Alert -> + ssl_gen_statem:handle_own_alert(Alert, ?FUNCTION_NAME, State) + end. %%-------------------------------------------------------------------- -spec cipher(gen_statem:event_type(), term(), #state{}) -> @@ -310,7 +323,10 @@ certify(Type, Event, State) -> cipher(info, Event, State) -> gen_info(Event, ?FUNCTION_NAME, State); cipher(Type, Event, State) -> - tls_dtls_connection:gen_handshake(?FUNCTION_NAME, Type, Event, State). + try tls_dtls_connection:gen_handshake(?FUNCTION_NAME, Type, Event, State) + catch throw:#alert{} = Alert -> + ssl_gen_statem:handle_own_alert(Alert, ?FUNCTION_NAME, State) + end. %%-------------------------------------------------------------------- -spec connection(gen_statem:event_type(), @@ -395,7 +411,10 @@ connection(internal, #client_hello{}, State = tls_gen_connection:reinit_handshake_data(State0), tls_gen_connection:next_event(?FUNCTION_NAME, no_record, State); connection(Type, Event, State) -> - tls_dtls_connection:?FUNCTION_NAME(Type, Event, State). + try tls_dtls_connection:?FUNCTION_NAME(Type, Event, State) + catch throw:#alert{} = Alert -> + ssl_gen_statem:handle_own_alert(Alert, ?FUNCTION_NAME, State) + end. %%-------------------------------------------------------------------- -spec downgrade(gen_statem:event_type(), term(), #state{}) -> @@ -417,8 +436,7 @@ terminate({shutdown, {sender_died, Reason}}, _StateName, ssl_gen_statem:handle_trusted_certs_db(State), tls_gen_connection:close(Reason, Socket, Transport, undefined, undefined); terminate(Reason, StateName, State) -> - catch ssl_gen_statem:terminate(Reason, StateName, State), - ensure_sender_terminate(Reason, State). + ssl_gen_statem:terminate(Reason, StateName, State). format_status(Type, Data) -> ssl_gen_statem:format_status(Type, Data). @@ -433,8 +451,12 @@ initial_state(Role, Sender, Host, Port, Socket, {SSLOptions, SocketOptions, Trac {CbModule, DataTag, CloseTag, ErrorTag, PassiveTag}) -> #{beast_mitigation := BeastMitigation, erl_dist := IsErlDist, + %% Use highest supported version for client/server random nonce generation + versions := [Version|_], client_renegotiation := ClientRenegotiation} = SSLOptions, - ConnectionStates = tls_record:init_connection_states(Role, BeastMitigation), + ConnectionStates = tls_record:init_connection_states(Role, + Version, + BeastMitigation), #{session_cb := SessionCacheCb} = ssl_config:pre_1_3_session_opts(Role), UserMonitor = erlang:monitor(process, User), InitStatEnv = #static_env{ @@ -474,86 +496,65 @@ initial_state(Role, Sender, Host, Port, Socket, {SSLOptions, SocketOptions, Trac }. handle_client_hello(#client_hello{client_version = ClientVersion} = Hello, State0) -> - case tls_dtls_connection:handle_sni_extension(State0, Hello) of - #state{connection_states = ConnectionStates0, - static_env = #static_env{trackers = Trackers}, - handshake_env = #handshake_env{ - kex_algorithm = KeyExAlg, - renegotiation = {Renegotiation, _}, - negotiated_protocol = CurrentProtocol, - sni_guided_cert_selection = SNICertSelection} = HsEnv, - connection_env = CEnv, - session = #session{own_certificates = OwnCerts} = Session0, - ssl_options = SslOpts} = State -> - SessionTracker = proplists:get_value(session_id_tracker, Trackers), - case tls_handshake:hello(Hello, - SslOpts, - {SessionTracker, Session0, - ConnectionStates0, OwnCerts, KeyExAlg}, - Renegotiation) of - #alert{} = Alert -> - Alert; - {Version, {Type, Session}, - ConnectionStates, Protocol0, ServerHelloExt0, HashSign} -> - Protocol = case Protocol0 of - undefined -> CurrentProtocol; - _ -> Protocol0 - end, - ServerHelloExt = - case SNICertSelection of - true -> - ServerHelloExt0#{sni => #sni{hostname = ""}}; - false -> - ServerHelloExt0 - end, - {ServerHelloExt, Type, State#state{connection_states = ConnectionStates, - connection_env = CEnv#connection_env{negotiated_version = Version}, - handshake_env = HsEnv#handshake_env{ - hashsign_algorithm = HashSign, - client_hello_version = ClientVersion, - negotiated_protocol = Protocol}, - session = Session - }} - end; - #alert{} = Alert -> - Alert - end. + State = tls_dtls_connection:handle_sni_extension(State0, Hello), + #state{connection_states = ConnectionStates0, + static_env = #static_env{trackers = Trackers}, + handshake_env = #handshake_env{ + kex_algorithm = KeyExAlg, + renegotiation = {Renegotiation, _}, + negotiated_protocol = CurrentProtocol, + sni_guided_cert_selection = SNICertSelection} = HsEnv, + connection_env = CEnv, + session = #session{own_certificates = OwnCerts} = Session0, + ssl_options = SslOpts} = State, + SessionTracker = proplists:get_value(session_id_tracker, Trackers), + {Version, {Type, Session}, + ConnectionStates, Protocol0, ServerHelloExt0, HashSign} = + tls_handshake:hello(Hello, + SslOpts, + {SessionTracker, Session0, + ConnectionStates0, OwnCerts, KeyExAlg}, + Renegotiation), + Protocol = case Protocol0 of + undefined -> CurrentProtocol; + _ -> Protocol0 + end, + ServerHelloExt = + case SNICertSelection of + true -> + ServerHelloExt0#{sni => #sni{hostname = ""}}; + false -> + ServerHelloExt0 + end, + {ServerHelloExt, Type, State#state{connection_states = ConnectionStates, + connection_env = CEnv#connection_env{negotiated_version = Version}, + handshake_env = HsEnv#handshake_env{ + hashsign_algorithm = HashSign, + client_hello_version = ClientVersion, + negotiated_protocol = Protocol}, + session = Session + }}. -gen_info(Event, connection = StateName, #state{connection_env = #connection_env{negotiated_version = Version}} = State) -> - try tls_gen_connection:handle_info(Event, StateName, State) of - Result -> - Result - catch +gen_info(Event, connection = StateName, State) -> + try + tls_gen_connection:handle_info(Event, StateName, State) + catch _:_ -> ssl_gen_statem:handle_own_alert(?ALERT_REC(?FATAL, ?INTERNAL_ERROR, - malformed_data), - Version, StateName, State) + malformed_data), + StateName, State) end; -gen_info(Event, StateName, #state{connection_env = #connection_env{negotiated_version = Version}} = State) -> - try tls_gen_connection:handle_info(Event, StateName, State) of - Result -> - Result - catch +gen_info(Event, StateName, State) -> + try + tls_gen_connection:handle_info(Event, StateName, State) + catch _:_ -> ssl_gen_statem:handle_own_alert(?ALERT_REC(?FATAL, ?HANDSHAKE_FAILURE, - malformed_handshake_data), - Version, StateName, State) + malformed_handshake_data), + StateName, State) end. - -ensure_sender_terminate(downgrade, _) -> - ok; %% Do not terminate sender during downgrade phase -ensure_sender_terminate(_, #state{protocol_specific = #{sender := Sender}}) -> - %% Make sure TLS sender dies when connection process is terminated normally - %% This is needed if the tls_sender is blocked in prim_inet:send - Kill = fun() -> - receive - after 5000 -> - catch (exit(Sender, kill)) - end - end, - spawn(Kill). choose_tls_fsm(#{versions := Versions}, #client_hello{ diff --git a/lib/ssl/src/tls_connection_1_3.erl b/lib/ssl/src/tls_connection_1_3.erl index 2d123bab43..bacdf791ad 100644 --- a/lib/ssl/src/tls_connection_1_3.erl +++ b/lib/ssl/src/tls_connection_1_3.erl @@ -232,11 +232,10 @@ config_error(Type, Event, State) -> ssl_gen_statem:?FUNCTION_NAME(Type, Event, State). -user_hello({call, From}, cancel, #state{connection_env = #connection_env{negotiated_version = Version}} - = State) -> +user_hello({call, From}, cancel, State) -> gen_statem:reply(From, ok), ssl_gen_statem:handle_own_alert(?ALERT_REC(?FATAL, ?USER_CANCELED, user_canceled), - Version, ?FUNCTION_NAME, State); + ?FUNCTION_NAME, State); user_hello({call, From}, {handshake_continue, NewOptions, Timeout}, #state{static_env = #static_env{role = Role}, handshake_env = #handshake_env{hello = Hello}, @@ -269,7 +268,7 @@ start(internal, #client_hello{extensions = Extensions} = Hello, start(internal, #client_hello{} = Hello, State0) -> case tls_handshake_1_3:do_start(Hello, State0) of #alert{} = Alert -> - ssl_gen_statem:handle_own_alert(Alert, {3,4}, start, State0); + ssl_gen_statem:handle_own_alert(Alert, start, State0); {State, start} -> {next_state, start, State, []}; {State, negotiated} -> @@ -289,7 +288,7 @@ start(internal, #server_hello{extensions = Extensions} = ServerHello, start(internal, #server_hello{} = ServerHello, State0) -> case tls_handshake_1_3:do_start(ServerHello, State0) of #alert{} = Alert -> - ssl_gen_statem:handle_own_alert(Alert, {3,4}, start, State0); + ssl_gen_statem:handle_own_alert(Alert, start, State0); {State, NextState} -> {next_state, NextState, State, []} end; @@ -303,7 +302,7 @@ negotiated(internal, #change_cipher_spec{}, State) -> negotiated(internal, Message, State0) -> case tls_handshake_1_3:do_negotiated(Message, State0) of #alert{} = Alert -> - ssl_gen_statem:handle_own_alert(Alert, {3,4}, negotiated, State0); + ssl_gen_statem:handle_own_alert(Alert, negotiated, State0); {State, NextState} -> {next_state, NextState, State, []} end; @@ -316,7 +315,7 @@ wait_cert(internal, #certificate_1_3{} = Certificate, State0) -> case tls_handshake_1_3:do_wait_cert(Certificate, State0) of {#alert{} = Alert, State} -> - ssl_gen_statem:handle_own_alert(Alert, {3,4}, wait_cert, State); + ssl_gen_statem:handle_own_alert(Alert, wait_cert, State); {State, NextState} -> tls_gen_connection:next_event(NextState, no_record, State) end; @@ -331,7 +330,7 @@ wait_cv(internal, #certificate_verify_1_3{} = CertificateVerify, State0) -> case tls_handshake_1_3:do_wait_cv(CertificateVerify, State0) of {#alert{} = Alert, State} -> - ssl_gen_statem:handle_own_alert(Alert, {3,4}, wait_cv, State); + ssl_gen_statem:handle_own_alert(Alert, wait_cv, State); {State, NextState} -> tls_gen_connection:next_event(NextState, no_record, State) end; @@ -346,7 +345,7 @@ wait_finished(internal, #finished{} = Finished, State0) -> case tls_handshake_1_3:do_wait_finished(Finished, State0) of #alert{} = Alert -> - ssl_gen_statem:handle_own_alert(Alert, {3,4}, finished, State0); + ssl_gen_statem:handle_own_alert(Alert, finished, State0); State1 -> {Record, State} = ssl_gen_statem:prepare_connection(State1, tls_gen_connection), tls_gen_connection:next_event(connection, Record, State, @@ -370,7 +369,7 @@ wait_sh(internal, #server_hello{extensions = Extensions} = Hello, #state{ssl_op wait_sh(internal, #server_hello{} = Hello, State0) -> case tls_handshake_1_3:do_wait_sh(Hello, State0) of #alert{} = Alert -> - ssl_gen_statem:handle_own_alert(Alert, {3,4}, wait_sh, State0); + ssl_gen_statem:handle_own_alert(Alert, wait_sh, State0); {State1, start, ServerHello} -> %% hello_retry_request: go to start {next_state, start, State1, [{next_event, internal, ServerHello}]}; @@ -388,7 +387,7 @@ wait_ee(internal, #change_cipher_spec{}, State) -> wait_ee(internal, #encrypted_extensions{} = EE, State0) -> case tls_handshake_1_3:do_wait_ee(EE, State0) of #alert{} = Alert -> - ssl_gen_statem:handle_own_alert(Alert, {3,4}, wait_ee, State0); + ssl_gen_statem:handle_own_alert(Alert, wait_ee, State0); {State1, NextState} -> tls_gen_connection:next_event(NextState, no_record, State1) end; @@ -403,14 +402,14 @@ wait_cert_cr(internal, #change_cipher_spec{}, State) -> wait_cert_cr(internal, #certificate_1_3{} = Certificate, State0) -> case tls_handshake_1_3:do_wait_cert_cr(Certificate, State0) of {#alert{} = Alert, State} -> - ssl_gen_statem:handle_own_alert(Alert, {3,4}, wait_cert_cr, State); + ssl_gen_statem:handle_own_alert(Alert, wait_cert_cr, State); {State1, NextState} -> tls_gen_connection:next_event(NextState, no_record, State1) end; wait_cert_cr(internal, #certificate_request_1_3{} = CertificateRequest, State0) -> case tls_handshake_1_3:do_wait_cert_cr(CertificateRequest, State0) of #alert{} = Alert -> - ssl_gen_statem:handle_own_alert(Alert, {3,4}, wait_cert_cr, State0); + ssl_gen_statem:handle_own_alert(Alert, wait_cert_cr, State0); {State1, NextState} -> tls_gen_connection:next_event(NextState, no_record, State1) end; @@ -424,7 +423,7 @@ wait_eoed(internal, #change_cipher_spec{}, State) -> wait_eoed(internal, #end_of_early_data{} = EOED, State0) -> case tls_handshake_1_3:do_wait_eoed(EOED, State0) of {#alert{} = Alert, State} -> - ssl_gen_statem:handle_own_alert(Alert, {3,4}, wait_eoed, State); + ssl_gen_statem:handle_own_alert(Alert, wait_eoed, State); {State1, NextState} -> tls_gen_connection:next_event(NextState, no_record, State1) end; @@ -442,7 +441,7 @@ connection(internal, #key_update{} = KeyUpdate, State0) -> {ok, State} -> tls_gen_connection:next_event(?FUNCTION_NAME, no_record, State); {error, State, Alert} -> - ssl_gen_statem:handle_own_alert(Alert, {3,4}, connection, State), + ssl_gen_statem:handle_own_alert(Alert, connection, State), tls_gen_connection:next_event(?FUNCTION_NAME, no_record, State) end; connection({call, From}, negotiated_protocol, @@ -468,9 +467,14 @@ downgrade(Type, Event, State) -> initial_state(Role, Sender, Host, Port, Socket, {SSLOptions, SocketOptions, Trackers}, User, {CbModule, DataTag, CloseTag, ErrorTag, PassiveTag}) -> #{erl_dist := IsErlDist, + %% Use highest supported version for client/server random nonce generation + versions := [Version|_], client_renegotiation := ClientRenegotiation} = SSLOptions, MaxEarlyDataSize = init_max_early_data_size(Role), - ConnectionStates = tls_record:init_connection_states(Role, disabled, MaxEarlyDataSize), + ConnectionStates = tls_record:init_connection_states(Role, + Version, + disabled, + MaxEarlyDataSize), UserMonitor = erlang:monitor(process, User), InitStatEnv = #static_env{ role = Role, diff --git a/lib/ssl/src/tls_connection_sup.erl b/lib/ssl/src/tls_connection_sup.erl index b7f80ad524..435639cf68 100644 --- a/lib/ssl/src/tls_connection_sup.erl +++ b/lib/ssl/src/tls_connection_sup.erl @@ -44,24 +44,22 @@ start_link_dist() -> start_child(Args) -> supervisor:start_child(?MODULE, Args). - + start_child_dist(Args) -> supervisor:start_child(tls_dist_connection_sup, Args). %%%========================================================================= %%% Supervisor callback %%%========================================================================= -init(_O) -> - RestartStrategy = simple_one_for_one, - MaxR = 0, - MaxT = 3600, - - Name = undefined, % As simple_one_for_one is used. - StartFunc = {ssl_gen_statem, start_link, []}, - Restart = temporary, % E.g. should not be restarted - Shutdown = 4000, - Modules = [ssl_gen_statem, tls_connection, tls_connection_1_3], - Type = worker, - - ChildSpec = {Name, StartFunc, Restart, Shutdown, Type, Modules}, - {ok, {{RestartStrategy, MaxR, MaxT}, [ChildSpec]}}. +init(_) -> + SupFlags = #{strategy => simple_one_for_one, + intensity => 0, + period => 3600 + }, + ChildSpecs = [#{id => undefined, + restart => temporary, + type => supervisor, + start => {tls_dyn_connection_sup, start_link, []}}], + {ok, {SupFlags, ChildSpecs}}. + + diff --git a/lib/ssl/src/tls_dist_server_sup.erl b/lib/ssl/src/tls_dist_server_sup.erl index 96603a7495..9560d05158 100644 --- a/lib/ssl/src/tls_dist_server_sup.erl +++ b/lib/ssl/src/tls_dist_server_sup.erl @@ -43,16 +43,16 @@ start_link() -> %%% Supervisor callback %%%========================================================================= -init([]) -> - ListenTracker = listen_options_tracker_child_spec(), - SessionTracker = tls_server_session_child_spec(), - Pre_1_3SessionTracker = ssl_server_session_child_spec(), - - {ok, {{one_for_all, 10, 3600}, [ListenTracker, - SessionTracker, - Pre_1_3SessionTracker - ]}}. - +init([]) -> + SupFlags = #{strategy => one_for_all, + intensity => 10, + period => 3600 + }, + ChildSpecs = [listen_options_tracker_child_spec(), + tls_server_session_child_spec(), + ssl_server_session_child_spec()], + {ok, {SupFlags, ChildSpecs}}. + %%-------------------------------------------------------------------- %%% Internal functions @@ -61,29 +61,28 @@ init([]) -> %% Handles emulated options so that they inherited by the accept %% socket, even when setopts is performed on the listen socket listen_options_tracker_child_spec() -> - Name = dist_tls_socket, - StartFunc = {ssl_listen_tracker_sup, start_link_dist, []}, - Restart = permanent, - Shutdown = 4000, - Modules = [ssl_listen_tracker_sup], - Type = supervisor, - {Name, StartFunc, Restart, Shutdown, Type, Modules}. + #{id => dist_ssl_listen_tracker_sup, + start => {ssl_listen_tracker_sup, start_link_dist, []}, + restart => permanent, + shutdown => 4000, + modules => [ssl_listen_tracker_sup], + type => supervisor + }. tls_server_session_child_spec() -> - Name = dist_tls_server_session_ticket, - StartFunc = {tls_server_session_ticket_sup, start_link_dist, []}, - Restart = permanent, - Shutdown = 4000, - Modules = [tls_server_session_ticket_sup], - Type = supervisor, - {Name, StartFunc, Restart, Shutdown, Type, Modules}. + #{id => dist_tls_server_session_ticket, + start => {tls_server_session_ticket_sup, start_link_dist, []}, + restart => permanent, + shutdown => 4000, + modules => [tls_server_session_ticket_sup], + type => supervisor + }. ssl_server_session_child_spec() -> - Name = dist_ssl_server_session_cache_sup, - StartFunc = {ssl_upgrade_server_session_cache_sup, start_link_dist, []}, - Restart = permanent, - Shutdown = 4000, - Modules = [ssl_server_session_cache_sup], - Type = supervisor, - {Name, StartFunc, Restart, Shutdown, Type, Modules}. - + #{id => dist_ssl_upgrade_server_session_cache_sup, + start => {ssl_upgrade_server_session_cache_sup, start_link_dist, []}, + restart => permanent, + shutdown => 4000, + modules => [ssl_upgrade_server_session_cache_sup], + type => supervisor + }. diff --git a/lib/ssl/src/tls_dist_sup.erl b/lib/ssl/src/tls_dist_sup.erl index 54e0a6a514..ebff741f9b 100644 --- a/lib/ssl/src/tls_dist_sup.erl +++ b/lib/ssl/src/tls_dist_sup.erl @@ -44,32 +44,34 @@ start_link() -> %%%========================================================================= init([]) -> - - TLSConnetionSup = tls_connection_child_spec(), - ServerInstanceSup = server_instance_child_spec(), - - {ok, {{one_for_one, 10, 3600}, [TLSConnetionSup, - ServerInstanceSup - ]}}. + SupFlags = #{strategy => one_for_one, + intensity => 10, + period => 3600 + }, + ChildSpecs = [tls_connection_child_spec(), + server_instance_child_spec() + ], + {ok, {SupFlags, ChildSpecs}}. %%-------------------------------------------------------------------- %%% Internal functions %%-------------------------------------------------------------------- tls_connection_child_spec() -> - Name = dist_tls_connection, - StartFunc = {tls_connection_sup, start_link_dist, []}, - Restart = permanent, - Shutdown = 4000, - Modules = [tls_connection_sup], - Type = supervisor, - {Name, StartFunc, Restart, Shutdown, Type, Modules}. - + #{id => dist_tls_connection_sup, + start => {tls_connection_sup, start_link_dist, []}, + restart => permanent, + shutdown => 4000, + modules => [tls_connection_sup], + type => supervisor + }. + server_instance_child_spec() -> - Name = dist_tls_server_sup, - StartFunc = {tls_dist_server_sup, start_link, []}, - Restart = permanent, - Shutdown = 4000, - Modules = [tls_dist_server_sup], - Type = supervisor, - {Name, StartFunc, Restart, Shutdown, Type, Modules}. + #{id => tls_dist_server_sup, + start => {tls_dist_server_sup, start_link, []}, + restart => permanent, + shutdown => 4000, + modules => [tls_dist_server_sup], + type => supervisor + }. + diff --git a/lib/ssl/src/tls_dtls_connection.erl b/lib/ssl/src/tls_dtls_connection.erl index bb138b035b..5ee5c41b44 100644 --- a/lib/ssl/src/tls_dtls_connection.erl +++ b/lib/ssl/src/tls_dtls_connection.erl @@ -22,6 +22,8 @@ %%---------------------------------------------------------------------- %% Purpose: Common handling of a TLS/SSL/DTLS connection, see also %% tls_connection.erl and dtls_connection.erl +%% +%% NOTE: All alerts are thrown out of this module %%---------------------------------------------------------------------- -module(tls_dtls_connection). @@ -113,13 +115,11 @@ handle_session(#server_hello{cipher_suite = CipherSuite, PremasterSecret = make_premaster_secret(ReqVersion, KeyAlgorithm), - {ExpectNPN, Protocol} = case Protocol0 of - undefined -> - - {false, CurrentProtocol}; - _ -> - {ProtoExt =:= npn, Protocol0} - end, + {ExpectNPN, Protocol} = + case Protocol0 of + undefined -> {false, CurrentProtocol}; + _ -> {ProtoExt =:= npn, Protocol0} + end, State = State0#state{connection_states = ConnectionStates, handshake_env = HsEnv#handshake_env{kex_algorithm = KeyAlgorithm, @@ -163,10 +163,9 @@ hello(Type, Event, State) -> #hello_request{} | term(), #state{}) -> gen_statem:state_function_result(). %%-------------------------------------------------------------------- -user_hello({call, From}, cancel, #state{connection_env = #connection_env{negotiated_version = Version}} = State) -> +user_hello({call, From}, cancel, _State) -> gen_statem:reply(From, ok), - ssl_gen_statem:handle_own_alert(?ALERT_REC(?FATAL, ?USER_CANCELED, user_canceled), - Version, ?FUNCTION_NAME, State); + throw(?ALERT_REC(?FATAL, ?USER_CANCELED, user_canceled)); user_hello({call, From}, {handshake_continue, NewOptions, Timeout}, #state{static_env = #static_env{role = Role}, handshake_env = #handshake_env{hello = Hello}, @@ -209,7 +208,7 @@ abbreviated(internal, #finished{verify_data = Data} = Finished, Connection), Connection:next_event(connection, Record, State, [{{timeout, handshake}, infinity, close}]); #alert{} = Alert -> - ssl_gen_statem:handle_own_alert(Alert, Version, ?FUNCTION_NAME, State0) + throw(Alert) end; abbreviated(internal, #finished{verify_data = Data} = Finished, #state{static_env = #static_env{role = client, @@ -232,7 +231,7 @@ abbreviated(internal, #finished{verify_data = Data} = Finished, Connection), Connection:next_event(connection, Record, State, [{{timeout, handshake}, infinity, close} | Actions]); #alert{} = Alert -> - ssl_gen_statem:handle_own_alert(Alert, Version, ?FUNCTION_NAME, State0) + throw(Alert) end; %% only allowed to send next_protocol message after change cipher spec %% & before finished message and it is not allowed during renegotiation @@ -309,12 +308,8 @@ certify(info, Msg, State) -> handle_info(Msg, ?FUNCTION_NAME, State); certify(internal, #certificate{asn1_certificates = []}, #state{static_env = #static_env{role = server}, - connection_env = #connection_env{negotiated_version = Version}, - ssl_options = #{verify := verify_peer, - fail_if_no_peer_cert := true}} = - State) -> - Alert = ?ALERT_REC(?FATAL,?HANDSHAKE_FAILURE, no_client_certificate_provided), - ssl_gen_statem:handle_own_alert(Alert, Version, ?FUNCTION_NAME, State); + ssl_options = #{verify := verify_peer, fail_if_no_peer_cert := true}}) -> + throw(?ALERT_REC(?FATAL,?HANDSHAKE_FAILURE, no_client_certificate_provided)); certify(internal, #certificate{asn1_certificates = []}, #state{static_env = #static_env{role = server, protocol_cb = Connection}, @@ -324,11 +319,8 @@ certify(internal, #certificate{asn1_certificates = []}, Connection:next_event(?FUNCTION_NAME, no_record, State0#state{client_certificate_requested = false}); certify(internal, #certificate{}, #state{static_env = #static_env{role = server}, - connection_env = #connection_env{negotiated_version = Version}, - ssl_options = #{verify := verify_none}} = - State) -> - Alert = ?ALERT_REC(?FATAL,?UNEXPECTED_MESSAGE, unrequested_certificate), - ssl_gen_statem:handle_own_alert(Alert, Version, ?FUNCTION_NAME, State); + ssl_options = #{verify := verify_none}}) -> + throw(?ALERT_REC(?FATAL,?UNEXPECTED_MESSAGE, unrequested_certificate)); certify(internal, #certificate{}, #state{static_env = #static_env{protocol_cb = Connection}, handshake_env = #handshake_env{ @@ -355,7 +347,7 @@ certify(internal, #certificate{asn1_certificates = [Peer|_]} = Cert, handle_peer_cert(Role, PeerCert, PublicKeyInfo, State#state{client_certificate_requested = false}, Connection, []); #alert{} = Alert -> - ssl_gen_statem:handle_own_alert(Alert, Version, ?FUNCTION_NAME, State) + throw(Alert) end; certify(internal, #server_key_exchange{exchange_keys = Keys}, #state{static_env = #static_env{role = client, @@ -397,14 +389,12 @@ certify(internal, #server_key_exchange{exchange_keys = Keys}, session = session_handle_params(Params#server_key_params.params, Session)}, Connection); false -> - ssl_gen_statem:handle_own_alert(?ALERT_REC(?FATAL, ?DECRYPT_ERROR), - Version, ?FUNCTION_NAME, State) + throw(?ALERT_REC(?FATAL, ?DECRYPT_ERROR)) end end; certify(internal, #certificate_request{}, #state{static_env = #static_env{role = client}, - handshake_env = #handshake_env{kex_algorithm = KexAlg}, - connection_env = #connection_env{negotiated_version = Version}} = State) + handshake_env = #handshake_env{kex_algorithm = KexAlg}}) when KexAlg == dh_anon; KexAlg == ecdh_anon; KexAlg == psk; @@ -414,8 +404,7 @@ certify(internal, #certificate_request{}, KexAlg == srp_dss; KexAlg == srp_rsa; KexAlg == srp_anon -> - ssl_gen_statem:handle_own_alert(?ALERT_REC(?FATAL, ?HANDSHAKE_FAILURE), - Version, ?FUNCTION_NAME, State); + throw(?ALERT_REC(?FATAL, ?HANDSHAKE_FAILURE)); certify(internal, #certificate_request{}, #state{static_env = #static_env{role = client, protocol_cb = Connection}, @@ -443,7 +432,7 @@ certify(internal, #certificate_request{} = CertRequest, case ssl_handshake:select_hashsign(CertRequest, Cert, SupportedHashSigns, TLSVersion) of #alert {} = Alert -> - ssl_gen_statem:handle_own_alert(Alert, Version, ?FUNCTION_NAME, State); + throw(Alert); SelectedHashSign -> Connection:next_event(?FUNCTION_NAME, no_record, State#state{client_certificate_requested = true, @@ -455,7 +444,6 @@ certify(internal, #server_hello_done{}, #state{static_env = #static_env{role = client, protocol_cb = Connection}, session = #session{master_secret = undefined}, - connection_env = #connection_env{negotiated_version = Version}, handshake_env = #handshake_env{kex_algorithm = KexAlg, premaster_secret = undefined, server_psk_identity = PSKIdentity} = HsEnv, @@ -463,7 +451,7 @@ certify(internal, #server_hello_done{}, when KexAlg == psk -> case ssl_handshake:premaster_secret({KexAlg, PSKIdentity}, PSKLookup) of #alert{} = Alert -> - ssl_gen_statem:handle_own_alert(Alert, Version, ?FUNCTION_NAME, State0); + throw(Alert); PremasterSecret -> State = master_secret(PremasterSecret, State0#state{handshake_env = @@ -473,7 +461,7 @@ certify(internal, #server_hello_done{}, certify(internal, #server_hello_done{}, #state{static_env = #static_env{role = client, protocol_cb = Connection}, - connection_env = #connection_env{negotiated_version = {Major, Minor}} = Version, + connection_env = #connection_env{negotiated_version = {Major, Minor}}, handshake_env = #handshake_env{kex_algorithm = KexAlg, premaster_secret = undefined, server_psk_identity = PSKIdentity} = HsEnv, @@ -485,7 +473,7 @@ certify(internal, #server_hello_done{}, case ssl_handshake:premaster_secret({KexAlg, PSKIdentity}, PSKLookup, RSAPremasterSecret) of #alert{} = Alert -> - ssl_gen_statem:handle_own_alert(Alert, Version, ?FUNCTION_NAME, State0); + throw(Alert); PremasterSecret -> State = master_secret(PremasterSecret, State0#state{handshake_env = @@ -506,7 +494,7 @@ certify(internal, #server_hello_done{}, State = State0#state{connection_states = ConnectionStates}, client_certify_and_key_exchange(State, Connection); #alert{} = Alert -> - ssl_gen_statem:handle_own_alert(Alert, Version, ?FUNCTION_NAME, State0) + throw(Alert) end; %% Master secret is calculated from premaster_secret certify(internal, #server_hello_done{}, @@ -524,16 +512,14 @@ certify(internal, #server_hello_done{}, session = Session}, client_certify_and_key_exchange(State, Connection); #alert{} = Alert -> - ssl_gen_statem:handle_own_alert(Alert, Version, ?FUNCTION_NAME, State0) + throw(Alert) end; certify(internal = Type, #client_key_exchange{} = Msg, #state{static_env = #static_env{role = server}, client_certificate_requested = true, - connection_env = #connection_env{negotiated_version = Version}, - ssl_options = #{fail_if_no_peer_cert := true}} = State) -> + ssl_options = #{fail_if_no_peer_cert := true}}) -> %% We expect a certificate here - Alert = ?ALERT_REC(?FATAL,?UNEXPECTED_MESSAGE, {unexpected_msg, {Type, Msg}}), - ssl_gen_statem:handle_own_alert(Alert, Version, ?FUNCTION_NAME, State); + throw(?ALERT_REC(?FATAL,?UNEXPECTED_MESSAGE, {unexpected_msg, {Type, Msg}})); certify(internal, #client_key_exchange{exchange_keys = Keys}, State = #state{handshake_env = #handshake_env{kex_algorithm = KeyAlg}, static_env = #static_env{protocol_cb = Connection}, @@ -543,7 +529,7 @@ certify(internal, #client_key_exchange{exchange_keys = Keys}, State, Connection) catch #alert{} = Alert -> - ssl_gen_statem:handle_own_alert(Alert, Version, ?FUNCTION_NAME, State) + throw(Alert) end; certify(internal, #hello_request{}, _) -> keep_state_and_data; @@ -580,15 +566,14 @@ cipher(internal, #certificate_verify{signature = Signature, Connection:next_event(?FUNCTION_NAME, no_record, State#state{handshake_env = HsEnv#handshake_env{cert_hashsign_algorithm = HashSign}}); #alert{} = Alert -> - ssl_gen_statem:handle_own_alert(Alert, Version, ?FUNCTION_NAME, State) + throw(Alert) end; %% client must send a next protocol message if we are expecting it cipher(internal, #finished{}, #state{static_env = #static_env{role = server}, handshake_env = #handshake_env{expecting_next_protocol_negotiation = true, - negotiated_protocol = undefined}, - connection_env = #connection_env{negotiated_version = Version}} = State0) -> - ssl_gen_statem:handle_own_alert(?ALERT_REC(?FATAL,?UNEXPECTED_MESSAGE), Version, ?FUNCTION_NAME, State0); + negotiated_protocol = undefined}}) -> + throw(?ALERT_REC(?FATAL,?UNEXPECTED_MESSAGE)); cipher(internal, #finished{verify_data = Data} = Finished, #state{static_env = #static_env{role = Role, host = Host, @@ -610,7 +595,7 @@ cipher(internal, #finished{verify_data = Data} = Finished, cipher_role(Role, Data, Session, State#state{handshake_env = HsEnv#handshake_env{expecting_finished = false}}); #alert{} = Alert -> - ssl_gen_statem:handle_own_alert(Alert, Version, ?FUNCTION_NAME, State) + throw(Alert) end; %% only allowed to send next_protocol message after change cipher spec %% & before finished message and it is not allowed during renegotiation @@ -688,16 +673,10 @@ connection(Type, Event, State) -> downgrade(Type, Event, State) -> ssl_gen_statem:handle_common_event(Type, Event, ?FUNCTION_NAME, State). -gen_handshake(StateName, Type, Event, - #state{connection_env = #connection_env{negotiated_version = Version}} = State) -> - try tls_dtls_connection:StateName(Type, Event, State) of - Result -> - Result - catch - _:_ -> - ssl_gen_statem:handle_own_alert(?ALERT_REC(?FATAL, ?HANDSHAKE_FAILURE, - malformed_handshake_data), - Version, StateName, State) +gen_handshake(StateName, Type, Event, State) -> + try tls_dtls_connection:StateName(Type, Event, State) + catch error:_ -> + throw(?ALERT_REC(?FATAL, ?HANDSHAKE_FAILURE, malformed_handshake_data)) end. %%-------------------------------------------------------------------- @@ -825,20 +804,13 @@ new_server_hello(#server_hello{cipher_suite = CipherSuite, compression_method = Compression, session_id = SessionId}, #state{session = Session0, - static_env = #static_env{protocol_cb = Connection}, - connection_env = #connection_env{negotiated_version = Version}} = State0, Connection) -> - try server_certify_and_key_exchange(State0, Connection) of - #state{} = State1 -> - {State, Actions} = server_hello_done(State1, Connection), - Session = - Session0#session{session_id = SessionId, - cipher_suite = CipherSuite, - compression_method = Compression}, - Connection:next_event(certify, no_record, State#state{session = Session}, Actions) - catch - #alert{} = Alert -> - ssl_gen_statem:handle_own_alert(Alert, Version, hello, State0) - end. + static_env = #static_env{protocol_cb = Connection}} = State0, Connection) -> + #state{} = State1 = server_certify_and_key_exchange(State0, Connection), + {State, Actions} = server_hello_done(State1, Connection), + Session = Session0#session{session_id = SessionId, + cipher_suite = CipherSuite, + compression_method = Compression}, + Connection:next_event(certify, no_record, State#state{session = Session}, Actions). resumed_server_hello(#state{session = Session, connection_states = ConnectionStates0, @@ -854,7 +826,7 @@ resumed_server_hello(#state{session = Session, finalize_handshake(State1, abbreviated, Connection), Connection:next_event(abbreviated, no_record, State, Actions); #alert{} = Alert -> - ssl_gen_statem:handle_own_alert(Alert, Version, hello, State0) + throw(Alert) end. server_hello(ServerHello, State0, Connection) -> @@ -923,19 +895,11 @@ verify_client_cert(#state{static_env = #static_env{role = client}, verify_client_cert(#state{client_certificate_requested = false} = State, _) -> State. -client_certify_and_key_exchange(#state{connection_env = #connection_env{negotiated_version = Version}} = - State0, Connection) -> - try do_client_certify_and_key_exchange(State0, Connection) of - State1 = #state{} -> - {State2, Actions} = finalize_handshake(State1, certify, Connection), - State = State2#state{ - %% Reinitialize - client_certificate_requested = false}, - Connection:next_event(cipher, no_record, State, Actions) - catch - throw:#alert{} = Alert -> - ssl_gen_statem:handle_own_alert(Alert, Version, certify, State0) - end. +client_certify_and_key_exchange(State0, Connection) -> + State1 = do_client_certify_and_key_exchange(State0, Connection), + {State2, Actions} = finalize_handshake(State1, certify, Connection), + State = State2#state{client_certificate_requested = false}, %% Reinitialize + Connection:next_event(cipher, no_record, State, Actions). do_client_certify_and_key_exchange(State0, Connection) -> State1 = certify_client(State0, Connection), @@ -965,10 +929,10 @@ certify_client_key_exchange(#encrypted_premaster_secret{premaster_secret= EncPMS end; _ -> %% erlang:byte_size(Secret) =/= ?NUM_OF_PREMASTERSECRET_BYTES FakeSecret - catch + catch #alert{description = ?DECRYPT_ERROR} -> FakeSecret - end, + end, calculate_master_secret(PremasterSecret, State, Connection, certify, cipher); certify_client_key_exchange(#client_diffie_hellman_public{dh_public = ClientPublicDhKey}, #state{handshake_env = #handshake_env{diffie_hellman_params = #'DHParameter'{} = Params, @@ -1189,12 +1153,7 @@ key_exchange(#state{static_env = #static_env{role = server}, KexAlg == srp_rsa; KexAlg == srp_anon -> SrpParams = handle_srp_identity(Username, LookupFun), - Keys = case generate_srp_server_keys(SrpParams, 0) of - Alert = #alert{} -> - throw(Alert); - Keys0 = {_,_} -> - Keys0 - end, + Keys = generate_srp_server_keys(SrpParams, 0), #{security_parameters := SecParams} = ssl_record:pending_connection_state(ConnectionStates0, read), #security_parameters{client_random = ClientRandom, @@ -1301,7 +1260,7 @@ rsa_key_exchange(Version, PremasterSecret, PublicKeyInfo = {Algorithm, _, _}) {premaster_secret, PremasterSecret, PublicKeyInfo}); rsa_key_exchange(_, _, _) -> - throw (?ALERT_REC(?FATAL,?HANDSHAKE_FAILURE, pub_key_is_not_rsa)). + throw(?ALERT_REC(?FATAL,?HANDSHAKE_FAILURE, pub_key_is_not_rsa)). rsa_psk_key_exchange(Version, PskIdentity, PremasterSecret, PublicKeyInfo = {Algorithm, _, _}) @@ -1318,7 +1277,7 @@ rsa_psk_key_exchange(Version, PskIdentity, PremasterSecret, {psk_premaster_secret, PskIdentity, PremasterSecret, PublicKeyInfo}); rsa_psk_key_exchange(_, _, _, _) -> - throw (?ALERT_REC(?FATAL,?HANDSHAKE_FAILURE, pub_key_is_not_rsa)). + throw(?ALERT_REC(?FATAL,?HANDSHAKE_FAILURE, pub_key_is_not_rsa)). request_client_cert(#state{handshake_env = #handshake_env{kex_algorithm = Alg}} = State, _) when Alg == dh_anon; @@ -1366,7 +1325,7 @@ calculate_master_secret(PremasterSecret, session = Session}, Connection:next_event(Next, no_record, State); #alert{} = Alert -> - ssl_gen_statem:handle_own_alert(Alert, Version, certify, State0) + throw(Alert) end. finalize_handshake(State0, StateName, Connection) -> @@ -1480,7 +1439,7 @@ calculate_secret(#server_srp_params{srp_n = Prime, srp_g = Generator} = ServerKe certify, certify). master_secret(#alert{} = Alert, _) -> - Alert; + throw(Alert); master_secret(PremasterSecret, #state{static_env = #static_env{role = Role}, connection_env = #connection_env{negotiated_version = Version}, session = Session, @@ -1493,11 +1452,11 @@ master_secret(PremasterSecret, #state{static_env = #static_env{role = Role}, Session#session{master_secret = MasterSecret}, connection_states = ConnectionStates}; #alert{} = Alert -> - Alert + throw(Alert) end. generate_srp_server_keys(_SrpParams, 10) -> - ?ALERT_REC(?FATAL, ?ILLEGAL_PARAMETER); + throw(?ALERT_REC(?FATAL, ?ILLEGAL_PARAMETER)); generate_srp_server_keys(SrpParams = #srp_user{generator = Generator, prime = Prime, verifier = Verifier}, N) -> @@ -1510,9 +1469,8 @@ generate_srp_server_keys(SrpParams = end. generate_srp_client_keys(_Generator, _Prime, 10) -> - ?ALERT_REC(?FATAL, ?ILLEGAL_PARAMETER); + throw(?ALERT_REC(?FATAL, ?ILLEGAL_PARAMETER)); generate_srp_client_keys(Generator, Prime, N) -> - try crypto:generate_key(srp, {user, [Generator, Prime, '6a']}) of Keys -> Keys @@ -1645,7 +1603,7 @@ handle_resumed_session(SessId, #state{static_env = #static_env{host = Host, connection_states = ConnectionStates, session = Session}); #alert{} = Alert -> - ssl_gen_statem:handle_own_alert(Alert, Version, hello, State) + throw(Alert) end. make_premaster_secret({MajVer, MinVer}, rsa) -> @@ -1674,8 +1632,8 @@ handle_sni_extension(#state{static_env = case ssl_gen_statem:handle_sni_extension(PossibleSNI, State0) of {ok, State} -> State; - {error, Alert} -> - Alert + {error, #alert{}=Alert} -> + throw(Alert) end. ensure_tls({254, _} = Version) -> diff --git a/lib/ssl/src/tls_dyn_connection_sup.erl b/lib/ssl/src/tls_dyn_connection_sup.erl new file mode 100644 index 0000000000..cb0576cbd6 --- /dev/null +++ b/lib/ssl/src/tls_dyn_connection_sup.erl @@ -0,0 +1,78 @@ +%% +%% %CopyrightBegin% +%% +%% Copyright Ericsson AB 2021-2021. All Rights Reserved. +%% +%% Licensed under the Apache License, Version 2.0 (the "License"); +%% you may not use this file except in compliance with the License. +%% You may obtain a copy of the License at +%% +%% http://www.apache.org/licenses/LICENSE-2.0 +%% +%% Unless required by applicable law or agreed to in writing, software +%% distributed under the License is distributed on an "AS IS" BASIS, +%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +%% See the License for the specific language governing permissions and +%% limitations under the License. +%% +%% %CopyrightEnd% +%% + +%% +%%---------------------------------------------------------------------- +%% Purpose: Supervises the TLS generic state machine, a process that +%% owns the transport socket and hence is a significant child, and the +%% corresponding TLS sender process that sends data to avoid blocking +%% the state machine process. +%% ---------------------------------------------------------------------- + +-module(tls_dyn_connection_sup). + +-behaviour(supervisor). + +%% API +-export([start_link/0]). +-export([start_child/3]). + +%% Supervisor callback +-export([init/1]). + +%%%========================================================================= +%%% API +%%%========================================================================= +start_link() -> + supervisor:start_link(?MODULE, []). + +start_child(Sup, sender, Args) -> + supervisor:start_child(Sup, sender(Args)); +start_child(Sup, receiver, Args) -> + supervisor:start_child(Sup, receiver(Args)). + +%%%========================================================================= +%%% Supervisor callback +%%%========================================================================= +init(_) -> + SupFlags = #{strategy => one_for_all, + auto_shutdown => any_significant, + intensity => 0, + period => 3600 + }, + ChildSpecs = [], + {ok, {SupFlags, ChildSpecs}}. + +sender(Args) -> + #{id => sender, + restart => temporary, + type => worker, + start => {tls_sender, start_link, Args}, + modules => [tls_sender] + }. + +receiver(Args) -> + #{id => receiver, + restart => temporary, + type => worker, + significant => true, + start => {ssl_gen_statem, start_link, Args}, + modules => [ssl_gen_statem, tls_connection, tls_connection_1_3] + }. diff --git a/lib/ssl/src/tls_gen_connection.erl b/lib/ssl/src/tls_gen_connection.erl index 217620f62e..59d30d7cb0 100644 --- a/lib/ssl/src/tls_gen_connection.erl +++ b/lib/ssl/src/tls_gen_connection.erl @@ -82,9 +82,10 @@ start_fsm(Role, Host, Port, Socket, {#{erl_dist := false},_, Trackers} = Opts, User, {CbModule, _,_, _, _} = CbInfo, Timeout) -> try - {ok, Sender} = tls_sender:start(), - {ok, Pid} = tls_connection_sup:start_child([Role, Sender, Host, Port, Socket, - Opts, User, CbInfo]), + {ok, DynSup} = tls_connection_sup:start_child([]), + {ok, Sender} = tls_dyn_connection_sup:start_child(DynSup, sender, []), + {ok, Pid} = tls_dyn_connection_sup:start_child(DynSup, receiver, [Role, Sender, Host, Port, Socket, + Opts, User, CbInfo]), {ok, SslSocket} = ssl_gen_statem:socket_control(?MODULE, Socket, [Pid, Sender], CbModule, Trackers), ssl_gen_statem:handshake(SslSocket, Timeout) catch @@ -96,9 +97,10 @@ start_fsm(Role, Host, Port, Socket, {#{erl_dist := true},_, Trackers} = Opts, User, {CbModule, _,_, _, _} = CbInfo, Timeout) -> try - {ok, Sender} = tls_sender:start([{spawn_opt, ?DIST_CNTRL_SPAWN_OPTS}]), - {ok, Pid} = tls_connection_sup:start_child_dist([Role, Sender, Host, Port, Socket, - Opts, User, CbInfo]), + {ok, DynSup} = tls_connection_sup:start_child_dist([]), + {ok, Sender} = tls_dyn_connection_sup:start_child(DynSup, sender, [[{spawn_opt, ?DIST_CNTRL_SPAWN_OPTS}]]), + {ok, Pid} = tls_dyn_connection_sup:start_child(DynSup, receiver, [Role, Sender, Host, Port, Socket, + Opts, User, CbInfo]), {ok, SslSocket} = ssl_gen_statem:socket_control(?MODULE, Socket, [Pid, Sender], CbModule, Trackers), ssl_gen_statem:handshake(SslSocket, Timeout) catch @@ -241,13 +243,12 @@ getopts(Transport, Socket, Tag) -> %% raw data from socket, upack records handle_info({Protocol, _, Data}, StateName, - #state{static_env = #static_env{data_tag = Protocol}, - connection_env = #connection_env{negotiated_version = Version}} = State0) -> + #state{static_env = #static_env{data_tag = Protocol}} = State0) -> case next_tls_record(Data, StateName, State0) of {Record, State} -> next_event(StateName, Record, State); #alert{} = Alert -> - ssl_gen_statem:handle_own_alert(Alert, Version, StateName, State0) + ssl_gen_statem:handle_own_alert(Alert, StateName, State0) end; handle_info({PassiveTag, Socket}, StateName, #state{static_env = #static_env{socket = Socket, @@ -321,9 +322,6 @@ handle_info({CloseTag, Socket}, StateName, %% is called after all data has been deliver. {next_state, StateName, State#state{protocol_specific = PS#{active_n_toggle => true}}, []} end; -handle_info({'EXIT', Sender, Reason}, _, - #state{protocol_specific = #{sender := Sender}} = State) -> - {stop, {shutdown, {sender_died, Reason}}, State}; handle_info(Msg, StateName, State) -> ssl_gen_statem:handle_info(Msg, StateName, State). @@ -404,26 +402,25 @@ handle_protocol_record(#ssl_tls{type = ?HANDSHAKE, fragment = Data}, end end catch throw:#alert{} = Alert -> - ssl_gen_statem:handle_own_alert(Alert, Version, StateName, State0) + ssl_gen_statem:handle_own_alert(Alert, StateName, State0) end; %%% TLS record protocol level change cipher messages handle_protocol_record(#ssl_tls{type = ?CHANGE_CIPHER_SPEC, fragment = Data}, StateName, State) -> {next_state, StateName, State, [{next_event, internal, #change_cipher_spec{type = Data}}]}; %%% TLS record protocol level Alert messages -handle_protocol_record(#ssl_tls{type = ?ALERT, fragment = EncAlerts}, StateName, - #state{connection_env = #connection_env{negotiated_version = Version}} = State) -> +handle_protocol_record(#ssl_tls{type = ?ALERT, fragment = EncAlerts}, StateName,State) -> try decode_alerts(EncAlerts) of Alerts = [_|_] -> handle_alerts(Alerts, {next_state, StateName, State}); [] -> ssl_gen_statem:handle_own_alert(?ALERT_REC(?FATAL, ?HANDSHAKE_FAILURE, empty_alert), - Version, StateName, State); + StateName, State); #alert{} = Alert -> - ssl_gen_statem:handle_own_alert(Alert, Version, StateName, State) + ssl_gen_statem:handle_own_alert(Alert, StateName, State) catch _:_ -> ssl_gen_statem:handle_own_alert(?ALERT_REC(?FATAL, ?HANDSHAKE_FAILURE, alert_decode_error), - Version, StateName, State) + StateName, State) end; %% Ignore unknown TLS record level protocol messages diff --git a/lib/ssl/src/tls_handshake.erl b/lib/ssl/src/tls_handshake.erl index 05cf1a7339..cb00fa57a3 100644 --- a/lib/ssl/src/tls_handshake.erl +++ b/lib/ssl/src/tls_handshake.erl @@ -108,7 +108,8 @@ client_hello(_Host, _Port, ConnectionStates, boolean(), #session{}) -> {tls_record:tls_version(), ssl:session_id(), ssl_record:connection_states(), alpn | npn, binary() | undefined, map()}| - {atom(), atom(), tls_record:tls_version(), map()} | #alert{}. + {atom(), atom(), tls_record:tls_version(), map()}. + % Otherwise Throws #alert{} %% %% Description: Handles a received hello message %%-------------------------------------------------------------------- @@ -128,7 +129,7 @@ hello(#server_hello{server_version = {Major, Minor}, (M > 3 orelse M =:= 3 andalso N >= 4) andalso %% TLS 1.3 client (Major =:= 3 andalso Minor < 3 andalso %% Negotiating TLS 1.1 or prior Down =:= ?RANDOM_OVERRIDE_TLS11) -> - ?ALERT_REC(?FATAL, ?ILLEGAL_PARAMETER); + throw(?ALERT_REC(?FATAL, ?ILLEGAL_PARAMETER)); %% TLS 1.2 clients SHOULD also check that the last eight bytes are not %% equal to the second value if the ServerHello indicates TLS 1.1 or below. @@ -138,7 +139,7 @@ hello(#server_hello{server_version = {Major, Minor}, when (M =:= 3 andalso N =:= 3) andalso %% TLS 1.2 client (Major =:= 3 andalso Minor < 3 andalso %% Negotiating TLS 1.1 or prior Down =:= ?RANDOM_OVERRIDE_TLS11) -> - ?ALERT_REC(?FATAL, ?ILLEGAL_PARAMETER); + throw(?ALERT_REC(?FATAL, ?ILLEGAL_PARAMETER)); %% TLS 1.3 - 4.2.1. Supported Versions @@ -157,9 +158,8 @@ hello(#server_hello{server_version = LegacyVersion, compression_method = Compression, session_id = SessionId, extensions = #{server_hello_selected_version := - #server_hello_selected_version{selected_version = Version} = HelloExt} - }, - #{versions := SupportedVersions, + #server_hello_selected_version{selected_version = Version}} = HelloExt}, + #{versions := SupportedVersions, ocsp_stapling := Stapling} = SslOpt, ConnectionStates0, Renegotiation, OldId) -> %% In TLS 1.3, the TLS server indicates its version using the "supported_versions" extension @@ -169,7 +169,7 @@ hello(#server_hello{server_version = LegacyVersion, case LegacyVersion > {3,3} orelse LegacyVersion =:= {3,3} andalso Version < {3,3} of true -> - ?ALERT_REC(?FATAL, ?ILLEGAL_PARAMETER); + throw(?ALERT_REC(?FATAL, ?ILLEGAL_PARAMETER)); false -> case tls_record:is_acceptable_version(Version, SupportedVersions) of true -> @@ -186,7 +186,7 @@ hello(#server_hello{server_version = LegacyVersion, ocsp_expect => ocsp_expect(Stapling)}} end; false -> - ?ALERT_REC(?FATAL, ?ILLEGAL_PARAMETER) + throw(?ALERT_REC(?FATAL, ?ILLEGAL_PARAMETER)) end end; @@ -205,7 +205,7 @@ hello(#server_hello{server_version = Version, Compression, HelloExt, SslOpt, ConnectionStates0, Renegotiation, IsNew); false -> - ?ALERT_REC(?FATAL, ?PROTOCOL_VERSION) + throw(?ALERT_REC(?FATAL, ?PROTOCOL_VERSION)) end. @@ -219,7 +219,7 @@ hello(#server_hello{server_version = Version, {tls_record:tls_version(), {resumed | new, #session{}}, ssl_record:connection_states(), binary() | undefined, HelloExt::map(), {ssl:hash(), ssl:sign_algo()} | - undefined} | {atom(), atom()} | {atom(), atom(), tuple()} | #alert{}. + undefined} | {atom(), atom()} | {atom(), atom(), tuple()}. %% TLS 1.2 Server %% - If "supported_versions" is present (ClientHello): %% - Select version from "supported_versions" (ignore ClientHello.legacy_version) @@ -245,8 +245,8 @@ hello(#client_hello{client_version = _ClientVersion, Version = ssl_handshake:select_supported_version(ClientVersions, Versions), do_hello(Version, Versions, CipherSuites, Hello, SslOpts, Info, Renegotiation) catch - _:_ -> - ?ALERT_REC(?FATAL, ?HANDSHAKE_FAILURE, malformed_handshake_data) + error:_ -> + throw(?ALERT_REC(?FATAL, ?HANDSHAKE_FAILURE, malformed_handshake_data)) end; hello(#client_hello{client_version = ClientVersion, @@ -259,9 +259,9 @@ hello(#client_hello{client_version = ClientVersion, catch error:{case_clause,{asn1, Asn1Reason}} -> %% ASN-1 decode of certificate somehow failed - ?ALERT_REC(?FATAL, ?INTERNAL_ERROR, {failed_to_decode_own_certificate, Asn1Reason}); - _:_ -> - ?ALERT_REC(?FATAL, ?HANDSHAKE_FAILURE, malformed_handshake_data) + throw(?ALERT_REC(?FATAL, ?INTERNAL_ERROR, {failed_to_decode_own_certificate, Asn1Reason})); + error:_ -> + throw(?ALERT_REC(?FATAL, ?HANDSHAKE_FAILURE, malformed_handshake_data)) end. %%-------------------------------------------------------------------- @@ -337,13 +337,13 @@ handle_client_hello(Version, ClientHashSigns, SupportedHashSigns, OwnCert, Version), ECCCurve = ssl_handshake:select_curve(Curves, SupportedECCs, ECCOrder), {Type, #session{cipher_suite = CipherSuite} = Session1} - = ssl_handshake:select_session(SugesstedId, CipherSuites, + = ssl_handshake:select_session(SugesstedId, CipherSuites, AvailableHashSigns, Compressions, SessIdTracker, Session0#session{ecc = ECCCurve}, Version, SslOpts, OwnCert), - case CipherSuite of + case CipherSuite of no_suite -> - ?ALERT_REC(?FATAL, ?INSUFFICIENT_SECURITY, no_suitable_ciphers); + throw(?ALERT_REC(?FATAL, ?INSUFFICIENT_SECURITY, no_suitable_ciphers)); _ -> #{key_exchange := KeyExAlg} = ssl_cipher_format:suite_bin_to_map(CipherSuite), case ssl_handshake:select_hashsign({ClientHashSigns, ClientSignatureSchemes}, @@ -351,7 +351,7 @@ handle_client_hello(Version, SupportedHashSigns, Version) of #alert{} = Alert -> - Alert; + throw(Alert); HashSign -> handle_client_hello_extensions(Version, Type, Random, CipherSuites, HelloExt, @@ -361,47 +361,38 @@ handle_client_hello(Version, end end; false -> - ?ALERT_REC(?FATAL, ?PROTOCOL_VERSION) + throw(?ALERT_REC(?FATAL, ?PROTOCOL_VERSION)) end. handle_client_hello_extensions(Version, Type, Random, CipherSuites, HelloExt, SslOpts, Session0, ConnectionStates0, Renegotiation, HashSign) -> - try ssl_handshake:handle_client_hello_extensions(tls_record, Random, CipherSuites, - HelloExt, Version, SslOpts, - Session0, ConnectionStates0, + {Session, ConnectionStates, Protocol, ServerHelloExt} = + ssl_handshake:handle_client_hello_extensions(tls_record, Random, CipherSuites, + HelloExt, Version, SslOpts, + Session0, ConnectionStates0, Renegotiation, - Session0#session.is_resumable) of - {Session, ConnectionStates, Protocol, ServerHelloExt} -> - {Version, {Type, Session}, ConnectionStates, Protocol, - ServerHelloExt, HashSign} - catch throw:Alert -> - Alert - end. - + Session0#session.is_resumable), + {Version, {Type, Session}, ConnectionStates, Protocol, ServerHelloExt, HashSign}. handle_server_hello_extensions(Version, SessionId, Random, CipherSuite, - Compression, HelloExt, SslOpt, ConnectionStates0, Renegotiation, IsNew) -> - try ssl_handshake:handle_server_hello_extensions(tls_record, Random, CipherSuite, - Compression, HelloExt, Version, - SslOpt, ConnectionStates0, - Renegotiation, IsNew) of - {ConnectionStates, ProtoExt, Protocol, OcspState} -> - {Version, SessionId, ConnectionStates, ProtoExt, Protocol, OcspState} - catch throw:Alert -> - Alert - end. - + Compression, HelloExt, SslOpt, ConnectionStates0, Renegotiation, IsNew) -> + {ConnectionStates, ProtoExt, Protocol, OcspState} = + ssl_handshake:handle_server_hello_extensions(tls_record, Random, CipherSuite, + Compression, HelloExt, Version, + SslOpt, ConnectionStates0, + Renegotiation, IsNew), + {Version, SessionId, ConnectionStates, ProtoExt, Protocol, OcspState}. do_hello(undefined, _Versions, _CipherSuites, _Hello, _SslOpts, _Info, _Renegotiation) -> - ?ALERT_REC(?FATAL, ?PROTOCOL_VERSION); + throw(?ALERT_REC(?FATAL, ?PROTOCOL_VERSION)); do_hello(Version, Versions, CipherSuites, Hello, SslOpts, Info, Renegotiation) -> case ssl_cipher:is_fallback(CipherSuites) of true -> Highest = tls_record:highest_protocol_version(Versions), case tls_record:is_higher(Highest, Version) of true -> - ?ALERT_REC(?FATAL, ?INAPPROPRIATE_FALLBACK); + throw(?ALERT_REC(?FATAL, ?INAPPROPRIATE_FALLBACK)); false -> handle_client_hello(Version, Hello, SslOpts, Info, Renegotiation) end; @@ -444,9 +435,7 @@ get_tls_handshake_aux(Version, <<?BYTE(Type), ?UINT24(Length), ssl_logger:debug(LogLevel, inbound, 'handshake', Handshake), get_tls_handshake_aux(Version, Rest, Opts, [{Handshake,Raw} | Acc]) catch - throw:#alert{} = Alert -> - throw(Alert); - _:_ -> + error:_ -> throw(?ALERT_REC(?FATAL, ?HANDSHAKE_FAILURE, handshake_decode_error)) end; get_tls_handshake_aux(_Version, Data, _, Acc) -> diff --git a/lib/ssl/src/tls_record.erl b/lib/ssl/src/tls_record.erl index a551718c38..30341f1598 100644 --- a/lib/ssl/src/tls_record.erl +++ b/lib/ssl/src/tls_record.erl @@ -34,8 +34,8 @@ %% Handling of incoming data -export([get_tls_records/5, - init_connection_states/2, - init_connection_states/3]). + init_connection_states/3, + init_connection_states/4]). %% Encoding TLS records -export([encode_handshake/3, encode_alert_record/3, @@ -66,29 +66,35 @@ %% Handling of incoming data %%==================================================================== %%-------------------------------------------------------------------- --spec init_connection_states(Role, BeastMitigation) -> +-spec init_connection_states(Role, Version, BeastMitigation) -> ssl_record:connection_states() when Role :: client | server, + Version :: tls_version(), BeastMitigation :: one_n_minus_one | zero_n | disabled. -%% +%% %% Description: Creates a connection_states record with appropriate %% values for the initial SSL connection setup. %%-------------------------------------------------------------------- -init_connection_states(Role, BeastMitigation) -> +init_connection_states(Role, Version, BeastMitigation) -> MaxEarlyDataSize = ssl_config:get_max_early_data_size(), - init_connection_states(Role, BeastMitigation, MaxEarlyDataSize). + init_connection_states(Role, Version, BeastMitigation, MaxEarlyDataSize). %% --spec init_connection_states(Role, BeastMitigation, MaxEarlyDataSize) -> +-spec init_connection_states(Role, Version, BeastMitigation, + MaxEarlyDataSize) -> ssl_record:connection_states() when Role :: client | server, + Version :: tls_version(), BeastMitigation :: one_n_minus_one | zero_n | disabled, MaxEarlyDataSize :: non_neg_integer(). -init_connection_states(Role, BeastMitigation, MaxEarlyDataSize) -> +init_connection_states(Role, Version, BeastMitigation, MaxEarlyDataSize) -> ConnectionEnd = ssl_record:record_protocol_role(Role), Current = initial_connection_state(ConnectionEnd, BeastMitigation, MaxEarlyDataSize), - Pending = ssl_record:empty_connection_state(ConnectionEnd, BeastMitigation, MaxEarlyDataSize), + Pending = ssl_record:empty_connection_state(ConnectionEnd, + Version, + BeastMitigation, + MaxEarlyDataSize), #{current_read => Current, pending_read => Pending, current_write => Current, @@ -706,9 +712,7 @@ encode_fragments(Type, Version, [Text|Data], {MajVer, MinVer} = Version, CipherHeader = <<?BYTE(Type), ?BYTE(MajVer), ?BYTE(MinVer), ?UINT16(Length)>>, encode_fragments(Type, Version, Data, CS, CompS, CipherS, Seq + 1, - [[CipherHeader, CipherFragment] | CipherFragments]); -encode_fragments(_Type, _Version, _Data, CS, _CompS, _CipherS, _Seq, _CipherFragments) -> - exit({cs, CS}). + [[CipherHeader, CipherFragment] | CipherFragments]). %%-------------------------------------------------------------------- %% 1/n-1 splitting countermeasure Rizzo/Duong-Beast, RC4 ciphers are diff --git a/lib/ssl/src/tls_record_1_3.erl b/lib/ssl/src/tls_record_1_3.erl index 3e42e3bf97..92d6491e38 100644 --- a/lib/ssl/src/tls_record_1_3.erl +++ b/lib/ssl/src/tls_record_1_3.erl @@ -291,10 +291,7 @@ encode_plain_text(#inner_plaintext{ %% structures are written directly onto the wire. #tls_cipher_text{opaque_type = Type, legacy_version = {3,3}, - encoded_record = Data}; - -encode_plain_text(_, CS) -> - exit({cs, CS}). + encoded_record = Data}. additional_data(Length) -> <<?BYTE(?OPAQUE_TYPE), ?BYTE(3), ?BYTE(3),?UINT16(Length)>>. diff --git a/lib/ssl/src/tls_sender.erl b/lib/ssl/src/tls_sender.erl index 3d2cafa24c..065e5b9987 100644 --- a/lib/ssl/src/tls_sender.erl +++ b/lib/ssl/src/tls_sender.erl @@ -30,15 +30,30 @@ -include("tls_handshake_1_3.hrl"). %% API --export([start/0, start/1, initialize/2, send_data/2, - send_post_handshake/2, send_alert/2, - send_and_ack_alert/2, setopts/2, renegotiate/1, peer_renegotiate/1, downgrade/2, +-export([start_link/0, + start_link/1, + initialize/2, + send_data/2, + send_post_handshake/2, + send_alert/2, + send_and_ack_alert/2, + setopts/2, + renegotiate/1, + peer_renegotiate/1, + downgrade/2, update_connection_state/3, - dist_tls_socket/1, dist_handshake_complete/3]). + dist_tls_socket/1, + dist_handshake_complete/3]). %% gen_statem callbacks --export([callback_mode/0, init/1, terminate/3, code_change/4]). --export([init/3, connection/3, handshake/3, death_row/3]). +-export([callback_mode/0, + init/1, + terminate/3, + code_change/4]). +-export([init/3, + connection/3, + handshake/3, + death_row/3]). -record(static, {connection_pid, @@ -65,10 +80,10 @@ %%% API %%%=================================================================== %%-------------------------------------------------------------------- --spec start() -> {ok, Pid :: pid()} | +-spec start_link() -> {ok, Pid :: pid()} | ignore | {error, Error :: term()}. --spec start(list()) -> {ok, Pid :: pid()} | +-spec start_link(list()) -> {ok, Pid :: pid()} | ignore | {error, Error :: term()}. @@ -76,10 +91,10 @@ %% may happen when a socket is busy (busy port) and the %% same process is sending and receiving %%-------------------------------------------------------------------- -start() -> - gen_statem:start(?MODULE, [], []). -start(SpawnOpts) -> - gen_statem:start(?MODULE, [], SpawnOpts). +start_link() -> + gen_statem:start_link(?MODULE, [], []). +start_link(SpawnOpts) -> + gen_statem:start_link(?MODULE, [], SpawnOpts). %%-------------------------------------------------------------------- -spec initialize(pid(), map()) -> ok. @@ -613,7 +628,9 @@ call(FsmPid, Event) -> {error, closed}; exit:{normal, _} -> {error, closed}; - exit:{{shutdown, _},_} -> + exit:{shutdown,_} -> + {error, closed}; + exit:{{shutdown, _},_} -> {error, closed} end. diff --git a/lib/ssl/src/tls_server_session_ticket_sup.erl b/lib/ssl/src/tls_server_session_ticket_sup.erl index bdde94ecea..a515e8bbe0 100644 --- a/lib/ssl/src/tls_server_session_ticket_sup.erl +++ b/lib/ssl/src/tls_server_session_ticket_sup.erl @@ -59,18 +59,16 @@ sup_name(dist) -> %%%========================================================================= %%% Supervisor callback %%%========================================================================= -init(_O) -> - RestartStrategy = simple_one_for_one, - MaxR = 0, - MaxT = 3600, - - Name = undefined, % As simple_one_for_one is used. - StartFunc = {tls_server_session_ticket, start_link, []}, - Restart = temporary, % E.g. should not be restarted - Shutdown = 4000, - Modules = [tls_server_session_ticket], - Type = worker, - - ChildSpec = {Name, StartFunc, Restart, Shutdown, Type, Modules}, - {ok, {{RestartStrategy, MaxR, MaxT}, [ChildSpec]}}. - +init(_) -> + SupFlags = #{strategy => simple_one_for_one, + intensity => 0, + period => 3600 + }, + ChildSpecs = [#{id => undefined, + start => {tls_server_session_ticket, start_link, []}, + restart => transient, + shutdown => 4000, + modules => [tls_server_session_ticket], + type => worker + }], + {ok, {SupFlags, ChildSpecs}}. diff --git a/lib/ssl/src/tls_server_sup.erl b/lib/ssl/src/tls_server_sup.erl index b2f011f221..7f739ed015 100644 --- a/lib/ssl/src/tls_server_sup.erl +++ b/lib/ssl/src/tls_server_sup.erl @@ -43,18 +43,19 @@ start_link() -> %%% Supervisor callback %%%========================================================================= -init([]) -> - ListenTracker = listen_options_tracker_child_spec(), - SessionTracker = tls_server_session_child_spec(), - Pre_1_3SessionTracker = ssl_server_session_child_spec(), - Pre_1_3UpgradeSessionTracker = ssl_upgrade_server_session_child_spec(), - - {ok, {{one_for_all, 10, 3600}, [ListenTracker, - SessionTracker, - Pre_1_3SessionTracker, - Pre_1_3UpgradeSessionTracker - ]}}. - +init([]) -> + ChildSpecs = [listen_options_tracker_child_spec(), + tls_server_session_child_spec(), %% TLS-1.3 Session ticket handling + ssl_server_session_child_spec(), %% PRE TLS-1.3 session handling + ssl_upgrade_server_session_child_spec() %% PRE TLS-1.3 session handling for upgrade servers + ], + SupFlags = #{strategy => one_for_all, + intensity => 10, + period => 3600 + }, + {ok, {SupFlags, ChildSpecs}}. + + %%-------------------------------------------------------------------- %%% Internal functions @@ -63,37 +64,37 @@ init([]) -> %% Handles emulated options so that they inherited by the accept %% socket, even when setopts is performed on the listen socket listen_options_tracker_child_spec() -> - Name = tls_socket, - StartFunc = {ssl_listen_tracker_sup, start_link, []}, - Restart = permanent, - Shutdown = 4000, - Modules = [ssl_listen_tracker_sup], - Type = supervisor, - {Name, StartFunc, Restart, Shutdown, Type, Modules}. + #{id => ssl_listen_tracker_sup, + start => {ssl_listen_tracker_sup, start_link, []}, + restart => permanent, + shutdown => 4000, + modules => [ssl_listen_tracker_sup], + type => supervisor + }. tls_server_session_child_spec() -> - Name = tls_server_session_ticket, - StartFunc = {tls_server_session_ticket_sup, start_link, []}, - Restart = permanent, - Shutdown = 4000, - Modules = [tls_server_session_ticket_sup], - Type = supervisor, - {Name, StartFunc, Restart, Shutdown, Type, Modules}. + #{id => tls_server_session_ticket, + start => {tls_server_session_ticket_sup, start_link, []}, + restart => permanent, + shutdown => 4000, + modules => [tls_server_session_ticket_sup], + type => supervisor + }. ssl_server_session_child_spec() -> - Name = ssl_server_session_cache_sup, - StartFunc = {ssl_server_session_cache_sup, start_link, []}, - Restart = permanent, - Shutdown = 4000, - Modules = [ssl_server_session_cache_sup], - Type = supervisor, - {Name, StartFunc, Restart, Shutdown, Type, Modules}. + #{id => ssl_server_session_cache_sup, + start => {ssl_server_session_cache_sup, start_link, []}, + restart => permanent, + shutdown => 4000, + modules => [ssl_server_session_cache_sup], + type => supervisor + }. ssl_upgrade_server_session_child_spec() -> - Name = ssl_upgrade_server_session_cache_sup, - StartFunc = {ssl_upgrade_server_session_cache_sup, start_link, []}, - Restart = permanent, - Shutdown = 4000, - Modules = [ssl_upgrade_server_session_cache_sup], - Type = supervisor, - {Name, StartFunc, Restart, Shutdown, Type, Modules}. + #{id => ssl_upgrade_server_session_cache_sup, + start => {ssl_upgrade_server_session_cache_sup, start_link, []}, + restart => permanent, + shutdown => 4000, + modules => [ssl_upgrade_server_session_cache_sup], + type => supervisor + }. diff --git a/lib/ssl/src/tls_socket.erl b/lib/ssl/src/tls_socket.erl index 0a583dbb1d..12d59c6b4c 100644 --- a/lib/ssl/src/tls_socket.erl +++ b/lib/ssl/src/tls_socket.erl @@ -105,15 +105,7 @@ accept(ListenSocket, #config{transport_info = {Transport,_,_,_,_} = CbInfo, Tracker = proplists:get_value(option_tracker, Trackers), {ok, EmOpts} = get_emulated_opts(Tracker), {ok, Port} = tls_socket:port(Transport, Socket), - {ok, Sender} = tls_sender:start(), - ConnArgs = [server, Sender, "localhost", Port, Socket, - {SslOpts, emulated_socket_options(EmOpts, #socket_options{}), Trackers}, self(), CbInfo], - case tls_connection_sup:start_child(ConnArgs) of - {ok, Pid} -> - ssl_gen_statem:socket_control(ConnectionCb, Socket, [Pid, Sender], Transport, Trackers); - {error, Reason} -> - {error, Reason} - end; + start_tls_server_connection(SslOpts, ConnectionCb, Transport, Port, Socket, EmOpts, Trackers, CbInfo); {error, Reason} -> {error, Reason} end. @@ -399,6 +391,19 @@ code_change(_OldVsn, State, _Extra) -> call(Pid, Msg) -> gen_server:call(Pid, Msg, infinity). +start_tls_server_connection(SslOpts, ConnectionCb, Transport, Port, Socket, EmOpts, Trackers, CbInfo) -> + try + {ok, DynSup} = tls_connection_sup:start_child([]), + {ok, Sender} = tls_dyn_connection_sup:start_child(DynSup, sender, []), + ConnArgs = [server, Sender, "localhost", Port, Socket, + {SslOpts, emulated_socket_options(EmOpts, #socket_options{}), Trackers}, self(), CbInfo], + {ok, Pid} = tls_dyn_connection_sup:start_child(DynSup, receiver, ConnArgs), + ssl_gen_statem:socket_control(ConnectionCb, Socket, [Pid, Sender], Transport, Trackers) + catch + error:{badmatch, {error, _} = Error} -> + Error + end. + split_options(Opts) -> split_options(Opts, emulated_options(), [], []). split_options([], _, SocketOpts, EmuOpts) -> diff --git a/lib/ssl/src/tls_sup.erl b/lib/ssl/src/tls_sup.erl index a425ae31e2..cafc563943 100644 --- a/lib/ssl/src/tls_sup.erl +++ b/lib/ssl/src/tls_sup.erl @@ -43,14 +43,13 @@ start_link() -> %%% Supervisor callback %%%========================================================================= -init([]) -> - - TLSConnetionSup = tls_connection_child_spec(), - ServerInstanceSup = server_instance_child_spec(), - - {ok, {{one_for_one, 10, 3600}, [TLSConnetionSup, - ServerInstanceSup - ]}}. +init([]) -> + ChildSpecs = [tls_connection_child_spec(), server_instance_child_spec()], + SupFlags = #{strategy => one_for_one, + intensity => 10, + period => 3600 + }, + {ok, {SupFlags, ChildSpecs}}. %%-------------------------------------------------------------------- @@ -58,19 +57,19 @@ init([]) -> %%-------------------------------------------------------------------- tls_connection_child_spec() -> - Name = tls_connection, - StartFunc = {tls_connection_sup, start_link, []}, - Restart = permanent, - Shutdown = 4000, - Modules = [tls_connection_sup], - Type = supervisor, - {Name, StartFunc, Restart, Shutdown, Type, Modules}. + #{id => tls_connection_sup, + start => {tls_connection_sup, start_link, []}, + restart => permanent, + shutdown => 4000, + modules => [tls_connection_sup], + type => supervisor + }. server_instance_child_spec() -> - Name = tls_server_sup, - StartFunc = {tls_server_sup, start_link, []}, - Restart = permanent, - Shutdown = 4000, - Modules = [tls_server_sup], - Type = supervisor, - {Name, StartFunc, Restart, Shutdown, Type, Modules}. + #{id => tls_server_sup, + start => {tls_server_sup, start_link, []}, + restart => permanent, + shutdown => 4000, + modules => [tls_server_sup], + type => supervisor + }. diff --git a/lib/ssl/test/dtls_api_SUITE.erl b/lib/ssl/test/dtls_api_SUITE.erl index 572702af02..4117bec12e 100644 --- a/lib/ssl/test/dtls_api_SUITE.erl +++ b/lib/ssl/test/dtls_api_SUITE.erl @@ -51,9 +51,12 @@ dtls_listen_two_sockets_5/0, dtls_listen_two_sockets_5/1, dtls_listen_two_sockets_6/0, - dtls_listen_two_sockets_6/1 + dtls_listen_two_sockets_6/1, + client_restarts/0, client_restarts/1 ]). +-include_lib("ssl/src/ssl_internal.hrl"). + %%-------------------------------------------------------------------- %% Common Test interface functions ----------------------------------- %%-------------------------------------------------------------------- @@ -80,7 +83,8 @@ api_tests() -> dtls_listen_two_sockets_3, dtls_listen_two_sockets_4, dtls_listen_two_sockets_5, - dtls_listen_two_sockets_6 + dtls_listen_two_sockets_6, + client_restarts ]. init_per_suite(Config0) -> @@ -300,7 +304,6 @@ dtls_listen_two_sockets_6(_Config) when is_list(_Config) -> ssl:close(S1), ok. - replay_window() -> [{doc, "Whitebox test of replay window"}]. replay_window(_Config) -> @@ -347,6 +350,65 @@ bits_to_list(Bits, I, Acc) -> 0 -> bits_to_list(Bits bsr 1, I+1, Acc) end. +client_restarts() -> + [{doc, "Test re-connection "}]. + +client_restarts(Config) -> + ClientOpts = ssl_test_lib:ssl_options(client_rsa_opts, Config), + ServerOpts = ssl_test_lib:ssl_options(server_rsa_verify_opts, Config), + {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config), + + Server = + ssl_test_lib:start_server([{node, ServerNode}, {port, 0}, + {from, self()}, + {mfa, {ssl_test_lib, no_result, []}}, + {options, ServerOpts}]), + Port = ssl_test_lib:inet_port(Server), + Client0 = ssl_test_lib:start_client([{node, ClientNode}, + {port, Port}, {host, Hostname}, + {mfa, {ssl_test_lib, no_result, []}}, + {from, self()}, + {options, [{reuse_sessions, save} | ClientOpts]}]), + ReConnect = %% Whitebox re-connect test + fun({sslsocket, {gen_udp,_,dtls_gen_connection}, [Pid]} = Socket, ssl) -> + ct:log("~p Client Socket: ~p ~n", [self(), Socket]), + {ok, {{Adress,CPort},UDPSocket}=IntSocket} = gen_statem:call(Pid, {downgrade, self()}), + true = is_port(UDPSocket), + ct:log("Info: ~p~n", [inet:info(UDPSocket)]), + + {ok, #config{transport_info = CbInfo, connection_cb = ConnectionCb, + ssl = SslOpts0}} = ssl:handle_options(ClientOpts, client, Adress), + SslOpts = {SslOpts0, #socket_options{}, undefined}, + + ct:sleep(250), + ct:log("Client second connect: ~p ~p~n", [Socket, CbInfo]), + Res = ssl_gen_statem:connect(ConnectionCb, Adress, CPort, IntSocket, SslOpts, self(), CbInfo, infinity), + {Res, Pid} + end, + + Client0 ! {apply, self(), ReConnect}, + receive + {apply_res, {Res, _Prev}} -> + ct:log("Apply res: ~p~n", [Res]), + ok; + Msg -> + ct:log("Unhandled: ~p~n", [Msg]), + ct:fail({wrong_msg, Msg}) + end, + + receive + Msg2 -> + ct:log("Unhandled: ~p~n", [Msg2]), + ct:fail({wrong_msg, Msg2}) + after 200 -> + ct:log("Nothing received~n", []) + end, + + ssl_test_lib:close(Server), + ssl_test_lib:close(Client0), + + ok. + %%-------------------------------------------------------------------- %% Internal functions ------------------------------------------------ %%-------------------------------------------------------------------- diff --git a/lib/ssl/test/openssl_client_cert_SUITE.erl b/lib/ssl/test/openssl_client_cert_SUITE.erl index ad00f2da1f..263628c3c4 100644 --- a/lib/ssl/test/openssl_client_cert_SUITE.erl +++ b/lib/ssl/test/openssl_client_cert_SUITE.erl @@ -82,7 +82,7 @@ groups() -> [ {openssl_client, [], protocol_groups()}, {'tlsv1.3', [], tls_1_3_protocol_groups()}, - {'tlsv1.2', [], pre_tls_1_3_protocol_groups()}, + {'tlsv1.2', [], pre_tls_1_3_protocol_groups() ++ [{group, rsa_pss_rsae}, {group, rsa_pss_pss}]}, {'tlsv1.1', [], pre_tls_1_3_protocol_groups()}, {'tlsv1', [], pre_tls_1_3_protocol_groups()}, {'dtlsv1.2', [], pre_tls_1_3_protocol_groups()}, @@ -92,8 +92,10 @@ groups() -> {dsa, [], all_version_tests()}, {rsa_1_3, [], all_version_tests() ++ tls_1_3_tests() ++ [unsupported_sign_algo_client_auth, unsupported_sign_algo_cert_client_auth]}, - {rsa_pss_rsae, [], all_version_tests() ++ tls_1_3_tests()}, - {rsa_pss_pss, [], all_version_tests() ++ tls_1_3_tests()}, + {rsa_pss_rsae, [], all_version_tests()}, + {rsa_pss_pss, [], all_version_tests()}, + {rsa_pss_rsae_1_3, [], all_version_tests() ++ tls_1_3_tests()}, + {rsa_pss_pss_1_3, [], all_version_tests() ++ tls_1_3_tests()}, {ecdsa_1_3, [], all_version_tests() ++ tls_1_3_tests()}, {eddsa_1_3, [], all_version_tests() ++ tls_1_3_tests()} ]. @@ -122,8 +124,8 @@ pre_tls_1_3_protocol_groups() -> tls_1_3_protocol_groups() -> [{group, rsa_1_3}, - {group, rsa_pss_rsae}, - {group, rsa_pss_pss}, + {group, rsa_pss_rsae_1_3}, + {group, rsa_pss_pss_1_3}, {group, ecdsa_1_3}, {group, eddsa_1_3} ]. @@ -198,23 +200,26 @@ init_per_group(Group, Config0) when Group == rsa; [] -> {skip, {no_sup, Group, Version}} end; -init_per_group(Alg, Config) when Alg == rsa_pss_rsae; - Alg == rsa_pss_pss -> +init_per_group(Alg, Config) when + Alg == rsa_pss_rsae; + Alg == rsa_pss_pss; + Alg == rsa_pss_rsae_1_3; + Alg == rsa_pss_pss_1_3 -> Supports = crypto:supports(), RSAOpts = proplists:get_value(rsa_opts, Supports), case lists:member(rsa_pkcs1_pss_padding, RSAOpts) andalso lists:member(rsa_pss_saltlen, RSAOpts) andalso lists:member(rsa_mgf1_md, RSAOpts) - andalso ssl_test_lib:is_sane_oppenssl_pss(Alg) + andalso ssl_test_lib:is_sane_oppenssl_pss(rsa_alg(Alg)) of true -> #{client_config := COpts, - server_config := SOpts} = ssl_test_lib:make_rsa_pss_pem(Alg, [], Config, ""), - [{cert_key_alg, Alg} | + server_config := SOpts} = ssl_test_lib:make_rsa_pss_pem(rsa_alg(Alg), [], Config, ""), + [{cert_key_alg, rsa_alg(Alg)} | lists:delete(cert_key_alg, - [{client_cert_opts, COpts}, - {server_cert_opts, SOpts} | + [{client_cert_opts, openssl_sig_algs(Alg) ++ COpts}, + {server_cert_opts, sig_algs(rsa_alg(Alg)) ++ SOpts} | lists:delete(server_cert_opts, lists:delete(client_cert_opts, Config))])]; false -> @@ -433,3 +438,28 @@ hello_retry_client_auth_empty_cert_rejected() -> ssl_cert_tests:hello_retry_client_auth_empty_cert_rejected(). hello_retry_client_auth_empty_cert_rejected(Config) -> ssl_cert_tests:hello_retry_client_auth_empty_cert_rejected(Config). + +rsa_alg(rsa_pss_rsae_1_3) -> + rsa_pss_rsae; +rsa_alg(rsa_pss_pss_1_3) -> + rsa_pss_pss; +rsa_alg(Atom) -> + Atom. + +sig_algs(rsa_pss_pss) -> + [{signature_algs, [rsa_pss_pss_sha512, + rsa_pss_pss_sha384, + rsa_pss_pss_sha256]}]; +sig_algs(rsa_pss_rsae) -> + [{signature_algs,[rsa_pss_rsae_sha512, + rsa_pss_rsae_sha384, + rsa_pss_rsae_sha256]}]. + +openssl_sig_algs(rsa_pss_pss) -> + [{sigalgs, "rsa_pss_pss_sha256"}]; +openssl_sig_algs(rsa_pss_rsae) -> + [{sigalgs,"rsa_pss_rsae_sha256"}]; +openssl_sig_algs(rsa_pss_pss_1_3) -> + [{sigalgs, "rsa_pss_rsae_sha512:rsa_pss_rsae_sha384:rsa_pss_pss_sha256"}]; +openssl_sig_algs(rsa_pss_rsae_1_3) -> + [{sigalgs,"rsa_pss_rsae_sha512:rsa_pss_rsae_sha384:rsa_pss_rsae_sha256"}]. diff --git a/lib/ssl/test/property_test/ssl_eqc_cipher_format.erl b/lib/ssl/test/property_test/ssl_eqc_cipher_format.erl index cf6ed755f7..11330b111b 100644 --- a/lib/ssl/test/property_test/ssl_eqc_cipher_format.erl +++ b/lib/ssl/test/property_test/ssl_eqc_cipher_format.erl @@ -51,6 +51,8 @@ -endif. -endif. +-include_lib("public_key/include/public_key.hrl"). + -define('TLS_v1.3', 'tlsv1.3'). -define('TLS_v1.2', 'tlsv1.2'). -define('TLS_v1.1', 'tlsv1.1'). @@ -105,6 +107,11 @@ prop_tls_anon_cipher_suite_openssl_name() -> end ). +prop_tls_signature_algs() -> + ?FORALL(SigAlg, ?LET(SigAlg, sig_alg(), SigAlg), + true = lists:member(ssl_cipher:signature_algorithm_to_scheme(SigAlg), sig_schemes()) + ). + %%-------------------------------------------------------------------- %% Generators ----------------------------------------------- %%-------------------------------------------------------------------- @@ -272,3 +279,55 @@ openssl_legacy_names() -> "SRP-AES-128-CBC-SHA", "SRP-AES-256-CBC-SHA" ]. + + +sig_alg() -> + oneof([#'SignatureAlgorithm'{algorithm = ?'id-RSASSA-PSS', + parameters = #'RSASSA-PSS-params'{ + maskGenAlgorithm = + #'MaskGenAlgorithm'{algorithm = ?'id-mgf1', + parameters = #'HashAlgorithm'{algorithm = ?'id-sha256'}}}}, + #'SignatureAlgorithm'{algorithm = ?'id-RSASSA-PSS', + parameters = #'RSASSA-PSS-params'{ + maskGenAlgorithm = + #'MaskGenAlgorithm'{algorithm = ?'id-mgf1', + parameters = #'HashAlgorithm'{algorithm = ?'id-sha384'}}}}, + + #'SignatureAlgorithm'{algorithm = ?'id-RSASSA-PSS', + parameters = #'RSASSA-PSS-params'{ + maskGenAlgorithm = + #'MaskGenAlgorithm'{algorithm = ?'id-mgf1', + parameters = #'HashAlgorithm'{algorithm = ?'id-sha512'}}}}, + #'SignatureAlgorithm'{algorithm = ?sha256WithRSAEncryption}, + #'SignatureAlgorithm'{algorithm = ?sha384WithRSAEncryption}, + #'SignatureAlgorithm'{algorithm = ?sha512WithRSAEncryption}, + #'SignatureAlgorithm'{algorithm = ?'ecdsa-with-SHA256'}, + #'SignatureAlgorithm'{algorithm = ?'ecdsa-with-SHA384'}, + #'SignatureAlgorithm'{algorithm = ?'ecdsa-with-SHA512'}, + #'SignatureAlgorithm'{algorithm = ?'sha-1WithRSAEncryption'}, + #'SignatureAlgorithm'{algorithm = ?'ecdsa-with-SHA1'}, + #'SignatureAlgorithm'{algorithm = ?'id-Ed25519'}, + #'SignatureAlgorithm'{algorithm = ?'id-Ed448'}, + #'SignatureAlgorithm'{algorithm = ?'rsaEncryption', + parameters = 'NULL'}, + #'SignatureAlgorithm'{algorithm = ?'rsaEncryption'}, + #'SignatureAlgorithm'{algorithm = ?'id-RSASSA-PSS'}]). + +sig_schemes() -> + [rsa_pss_pss_sha256, + rsa_pss_pss_sha384, + rsa_pss_pss_sha512, + rsa_pkcs1_sha256, + rsa_pkcs1_sha384, + rsa_pkcs1_sha512, + ecdsa_secp256r1_sha256, + ecdsa_secp384r1_sha384, + ecdsa_secp521r1_sha512, + rsa_pkcs1_sha1, + rsa_pkcs1_sha1, + ecdsa_sha1, + eddsa_ed25519, + eddsa_ed448, + rsa_pkcs1_sha1, + rsa_pss_rsae, + rsa_pss_pss]. diff --git a/lib/ssl/test/ssl_api_SUITE.erl b/lib/ssl/test/ssl_api_SUITE.erl index ec831cfb90..22b199aa70 100644 --- a/lib/ssl/test/ssl_api_SUITE.erl +++ b/lib/ssl/test/ssl_api_SUITE.erl @@ -171,7 +171,13 @@ invalid_options_tls13/0, invalid_options_tls13/1, cookie/0, - cookie/1 + cookie/1, + warn_verify_none/0, + warn_verify_none/1, + suppress_warn_verify_none/0, + suppress_warn_verify_none/1, + check_random_nonce/0, + check_random_nonce/1 ]). %% Apply export @@ -192,6 +198,8 @@ tls_close/1, no_recv_no_active/1, ssl_getstat/1, + log/2, + get_connection_information/3, %%TODO Keep? run_error_server/1, run_error_server_close/1, @@ -292,7 +300,10 @@ gen_api_tests() -> invalid_options, cb_info, log_alert, - getstat + getstat, + warn_verify_none, + suppress_warn_verify_none, + check_random_nonce ]. handshake_paus_tests() -> @@ -405,6 +416,10 @@ init_per_testcase(conf_signature_algs, Config) -> sha -> {skip, "Tests needs certs with sha256"} end; +init_per_testcase(check_random_nonce, Config) -> + ssl_test_lib:ct_log_supported_protocol_versions(Config), + ct:timetrap({seconds, 20}), + Config; init_per_testcase(_TestCase, Config) -> ssl_test_lib:ct_log_supported_protocol_versions(Config), ct:timetrap({seconds, 10}), @@ -2485,7 +2500,128 @@ cookie(Config) when is_list(Config) -> cookie_extension(Config, true), cookie_extension(Config, false). +warn_verify_none() -> + [{doc, "Test that verify_none default generates warning."}]. +warn_verify_none(Config) when is_list(Config)-> + ok = logger:add_handler(?MODULE,?MODULE,#{config=>self()}), + ClientOpts = ssl_test_lib:ssl_options(client_rsa_opts, Config), + ServerOpts = ssl_test_lib:ssl_options(server_rsa_opts, Config), + + {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config), + + Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0}, + {from, self()}, + {options, ServerOpts}, + {mfa, {ssl_test_lib, no_result, []}}]), + Port = ssl_test_lib:inet_port(Server), + Client = ssl_test_lib:start_client([{node, ClientNode}, {port, Port}, + {from, self()}, + {host, Hostname}, + {options, ClientOpts}, + {mfa, {ssl_test_lib, no_result, []}}]), + receive + warning_generated -> + ok = logger:remove_handler(?MODULE) + after 500 -> + ok = logger:remove_handler(?MODULE), + ct:fail(no_warning) + end, + ssl_test_lib:close(Client), + ssl_test_lib:close(Server). + +suppress_warn_verify_none() -> + [{doc, "Test that explicit verify_none supresses warning."}]. +suppress_warn_verify_none(Config) when is_list(Config)-> + ok = logger:add_handler(?MODULE,?MODULE,#{config=>self()}), + + ClientOpts = ssl_test_lib:ssl_options(client_rsa_opts, Config), + ServerOpts = ssl_test_lib:ssl_options(server_rsa_opts, Config), + + {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config), + + Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0}, + {from, self()}, + {options, ServerOpts}, + {mfa, {ssl_test_lib, no_result, []}}]), + Port = ssl_test_lib:inet_port(Server), + Client = ssl_test_lib:start_client([{node, ClientNode}, {port, Port}, + {from, self()}, + {host, Hostname}, + {options, [{verify, verify_none} | ClientOpts]}, + {mfa, {ssl_test_lib, no_result, []}}]), + receive + warning_generated -> + ok = logger:remove_handler(?MODULE), + ct:fail(warning) + after 500 -> + ok = logger:remove_handler(?MODULE) + end, + ssl_test_lib:close(Client), + ssl_test_lib:close(Server). + +%%-------------------------------------------------------------------- +check_random_nonce() -> + [{doc,"Test random nonce - expecting 32B random for TLS1.3 and 4B UTC " + "epoch with 28B random for older version"}]. +check_random_nonce(Config) when is_list(Config) -> + ClientOpts = ssl_test_lib:ssl_options(client_rsa_opts, Config), + ServerOpts = ssl_test_lib:ssl_options(server_rsa_opts, Config), + {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config), + N = 10, + F = fun (Id) -> establish_connection(Id,ServerNode, ServerOpts, + ClientNode, ClientOpts, + Hostname) + end, + ConnectionPairs = [F(Id) || Id <- lists:seq(1, N)], + Randoms = lists:flatten([ssl_test_lib:get_result([Server, Client]) || + {Server, Client} <- ConnectionPairs]), + Deltas = [abs(FourBytes - SecsSince) || + {_Id, {_, <<FourBytes:32, _/binary>>}, SecsSince} <- Randoms], + MeanDelta = lists:sum(Deltas) div N, + case ?config(version, Config) of + 'tlsv1.3' -> + %% 4B "random" expected since TLS1.3 + RndThreshold = 10000, + true = MeanDelta > RndThreshold; + _ -> + %% 4 epoch based bytes expected pre TLS1.3 + EpochThreshold = 10, + true = MeanDelta < EpochThreshold + end, + [begin + ssl_test_lib:close(Server), + ssl_test_lib:close(Client) + end || {Server, Client} <- ConnectionPairs]. + +establish_connection(Id, ServerNode, ServerOpts, ClientNode, ClientOpts, Hostname) -> + Server = + ssl_test_lib:start_server([{node, ServerNode}, {port, 0}, + {from, self()}, + {mfa, {?MODULE, get_connection_information, + [Id, server_random]}}, + {options, [{verify, verify_peer} | ServerOpts]}]), + + ListenPort = ssl_test_lib:inet_port(Server), + Client = + ssl_test_lib:start_client([{node, ClientNode}, {port, ListenPort}, + {host, Hostname}, + {from, self()}, + {mfa, {?MODULE, get_connection_information, + [Id, client_random]}}, + {options, [{verify, verify_peer} |ClientOpts]}]), + + ct:log("Testcase ~p, Client ~p Server ~p ~n", + [self(), Client, Server]), + {Server, Client}. + %%% Checker functions +get_connection_information(Socket, ConnectionId, InfoType) -> + {ok, [ConnectionInfo]} = ssl:connection_information(Socket, [InfoType]), + {ConnectionId, ConnectionInfo, secs_since_1970()}. +secs_since_1970() -> + calendar:datetime_to_gregorian_seconds( + calendar:universal_time()) - 62167219200. + connection_information_result(Socket) -> {ok, Info = [_ | _]} = ssl:connection_information(Socket), case length(Info) > 3 of @@ -2909,3 +3045,8 @@ ssl_getstat(Socket) -> _ -> ok end. + +log(#{msg:={report,Report}},#{config:=Pid}) -> + Pid ! warning_generated; +log(_,_) -> + ok. diff --git a/lib/ssl/test/ssl_cert_SUITE.erl b/lib/ssl/test/ssl_cert_SUITE.erl index 42c44e4855..1eef4a48be 100644 --- a/lib/ssl/test/ssl_cert_SUITE.erl +++ b/lib/ssl/test/ssl_cert_SUITE.erl @@ -146,11 +146,11 @@ groups() -> {'tlsv1', [], ssl_protocol_groups()}, {'dtlsv1.2', [], tls_1_2_protocol_groups()}, {'dtlsv1', [], ssl_protocol_groups()}, - {rsa, [], all_version_tests() ++ rsa_tests() ++ pre_tls_1_3_rsa_tests()}, + {rsa, [], all_version_tests() ++ rsa_tests() ++ pre_tls_1_3_rsa_tests() ++ [client_auth_seelfsigned_peer]}, {ecdsa, [], all_version_tests()}, {dsa, [], all_version_tests()}, {rsa_1_3, [], all_version_tests() ++ rsa_tests() ++ - tls_1_3_tests() ++ tls_1_3_rsa_tests() ++ [basic_rsa_1024]}, + tls_1_3_tests() ++ tls_1_3_rsa_tests() ++ [client_auth_seelfsigned_peer, basic_rsa_1024]}, {rsa_pss_rsae, [], all_version_tests() ++ tls_1_2_rsa_tests()}, {rsa_pss_rsae_1_3, [], all_version_tests() ++ rsa_tests() ++ tls_1_3_tests() ++ tls_1_3_rsa_tests()}, {rsa_pss_pss, [], all_version_tests()}, @@ -226,7 +226,6 @@ all_version_tests() -> client_auth_do_not_allow_partial_chain, client_auth_partial_chain_fun_fail, client_auth_sni, - client_auth_seelfsigned_peer, missing_root_cert_no_auth, missing_root_cert_auth, missing_root_cert_auth_user_verify_fun_accept, @@ -296,37 +295,9 @@ do_init_per_group(Alg, Config) when Alg == rsa_pss_rsae; true -> #{client_config := COpts, server_config := SOpts} = ssl_test_lib:make_rsa_pss_pem(rsa_alg(Alg), [], Config, ""), - [{cert_key_alg, rsa_alg(Alg)}, - {extra_client, [{signature_algs, [rsa_pss_pss_sha512, - rsa_pss_pss_sha384, - rsa_pss_pss_sha256, - rsa_pss_rsae_sha512, - rsa_pss_rsae_sha384, - rsa_pss_rsae_sha256, - rsa_pkcs1_sha512, - rsa_pkcs1_sha384, - rsa_pkcs1_sha256, - rsa_pkcs1_sha1, - {sha512, rsa}, - {sha384, rsa}, - {sha256, rsa}, - {sha224, rsa} - ]}]}, - {extra_server, [{signature_algs, [rsa_pss_pss_sha512, - rsa_pss_pss_sha384, - rsa_pss_pss_sha256, - rsa_pss_rsae_sha512, - rsa_pss_rsae_sha384, - rsa_pss_rsae_sha256, - {sha512, ecdsa}, - {sha512, rsa}, - {sha384, ecdsa}, - {sha384, rsa}, - {sha256, ecdsa}, - {sha256, rsa}, - {sha224, ecdsa}, - {sha224, rsa} - ]}]} | + [{cert_key_alg, Alg}, + {extra_client, sig_algs(Alg)}, + {extra_server, sig_algs(Alg)} | lists:delete(cert_key_alg, [{client_cert_opts, COpts}, {server_cert_opts, SOpts} | @@ -1317,3 +1288,12 @@ chain_and_root(Config) -> {ok, ExtractedCAs} = ssl_pkix_db:extract_trusted_certs({der, proplists:get_value(cacerts, Config)}), {ok, Root, Chain} = ssl_certificate:certificate_chain(OwnCert, ets:new(foo, []), ExtractedCAs, [], encoded), {Chain, Root}. + +sig_algs(rsa_pss_pss) -> + [{signature_algs, [rsa_pss_pss_sha512, + rsa_pss_pss_sha384, + rsa_pss_pss_sha256]}]; +sig_algs(rsa_pss_rsae) -> + [{signature_algs, [rsa_pss_rsae_sha512, + rsa_pss_rsae_sha384, + rsa_pss_rsae_sha256]}]. diff --git a/lib/ssl/test/ssl_dist_SUITE.erl b/lib/ssl/test/ssl_dist_SUITE.erl index 67be91196f..5c8a8d9138 100644 --- a/lib/ssl/test/ssl_dist_SUITE.erl +++ b/lib/ssl/test/ssl_dist_SUITE.erl @@ -58,7 +58,9 @@ verify_fun_fail/0, verify_fun_fail/1, verify_fun_pass/0, - verify_fun_pass/1 + verify_fun_pass/1, + epmd_module/0, + epmd_module/1 ]). %% Apply export @@ -75,6 +77,12 @@ verify_pass_always/3, verify_fail_always/3]). +%% Epmd module export +-export([start_link/0, + register_node/2, + register_node/3, + port_please/2, + address_please/3]). -define(DEFAULT_TIMETRAP_SECS, 240). -define(AWAIT_SSL_NODE_UP_TIMEOUT, 30000). @@ -102,7 +110,8 @@ all() -> connect_options, use_interface, verify_fun_fail, - verify_fun_pass + verify_fun_pass, + epmd_module ]. init_per_suite(Config0) -> @@ -382,6 +391,53 @@ verify_fun_pass(Config) when is_list(Config) -> gen_dist_test(verify_fun_pass_test, [{tls_verify_opts, AddTLSVerifyOpts} | Config]). %%-------------------------------------------------------------------- +epmd_module() -> + [{doc,"Test that custom epmd_modules work"}]. +epmd_module(Config0) when is_list(Config0) -> + Config = [{hostname, "dummy"} | Config0], + NH1 = start_ssl_node(Config, "-epmd_module " ++ atom_to_list(?MODULE)), + NH2 = start_ssl_node(Config, "-epmd_module " ++ atom_to_list(?MODULE)), + + {ok, Port1} = apply_on_ssl_node(NH1, fun() -> application:get_env(kernel, dist_listen_port) end), + {ok, Port2} = apply_on_ssl_node(NH2, fun() -> application:get_env(kernel, dist_listen_port) end), + apply_on_ssl_node(NH1, fun() -> application:set_env(kernel, dist_connect_port, Port2) end), + apply_on_ssl_node(NH2, fun() -> application:set_env(kernel, dist_connect_port, Port1) end), + + try + basic_test(NH1, NH2, Config) + catch + _:Reason -> + stop_ssl_node(NH1), + stop_ssl_node(NH2), + ct:fail(Reason) + end, + stop_ssl_node(NH1), + stop_ssl_node(NH2), + success(Config). + +start_link() -> + ignore. + +register_node(Name, Port) -> + register_node(Name, Port, inet_tcp). +register_node(_Name, Port, _Driver) -> + %% Save the port number we're listening on. + application:set_env(kernel, dist_listen_port, Port), + Creation = rand:uniform(3), + {ok, Creation}. + +port_please(_Name, _Ip) -> + {ok, Port} = application:get_env(kernel, dist_connect_port), + {port, Port, 5}. + +address_please(_Name, "dummy", AddressFamily) -> + %% Use localhost. + {ok,Host} = inet:gethostname(), + inet:getaddr(Host, AddressFamily); +address_please(_, _, _) -> + {error, nxdomain}. + +%%-------------------------------------------------------------------- %%% Internal functions ----------------------------------------------- %%-------------------------------------------------------------------- gen_dist_test(Test, Config) -> @@ -653,11 +709,17 @@ start_ssl_node(Config, XArgs) -> mk_node_name(Config) -> N = erlang:unique_integer([positive]), Case = proplists:get_value(testcase, Config), + Hostname = + case proplists:get_value(hostname, Config) of + undefined -> ""; + Host -> "@" ++ Host + end, atom_to_list(?MODULE) ++ "_" ++ atom_to_list(Case) ++ "_" - ++ integer_to_list(N). + ++ integer_to_list(N) ++ Hostname. + setup_certs(Config) -> PrivDir = proplists:get_value(priv_dir, Config), diff --git a/lib/ssl/test/ssl_dist_test_lib.erl b/lib/ssl/test/ssl_dist_test_lib.erl index e85eddaf08..d0fda480b2 100644 --- a/lib/ssl/test/ssl_dist_test_lib.erl +++ b/lib/ssl/test/ssl_dist_test_lib.erl @@ -73,7 +73,9 @@ apply_on_ssl_node(Node, Ref, Msg) -> stop_ssl_node(#node_handle{connection_handler = Handler, socket = Socket, - name = Name}) -> + name = Name, + logfile = LogPath, + dumpfile = DumpPath}) -> test_server:format("Trying to stop ssl node ~s.~n", [Name]), Mon = erlang:monitor(process, Handler), unlink(Handler), @@ -93,13 +95,24 @@ stop_ssl_node(#node_handle{connection_handler = Handler, Error -> erlang:demonitor(Mon, [flush]), ct:pal("stop_ssl_node/1 ~s Warning ~p ~n", [Name,Error]) - end. + end, + case file:read_file(LogPath) of + {ok, Binary} -> + ct:pal("LogPath(~pB) = ~p~n~s", [filelib:file_size(LogPath), LogPath, + Binary]); + _ -> + ok + end, + ct:pal("DumpPath(~pB) = ~p~n", [filelib:file_size(DumpPath), DumpPath]). start_ssl_node(Name, Args) -> {ok, LSock} = gen_tcp:listen(0, [binary, {packet, 4}, {active, false}]), {ok, ListenPort} = inet:port(LSock), - CmdLine = mk_node_cmdline(ListenPort, Name, Args), + {ok, Pwd} = file:get_cwd(), + LogFilePath = filename:join([Pwd, "error_log." ++ Name]), + DumpFilePath = filename:join([Pwd, "erl_crash_dump." ++ Name]), + CmdLine = mk_node_cmdline(ListenPort, Name, Args, LogFilePath, DumpFilePath), test_server:format("Attempting to start ssl node ~ts: ~ts~n", [Name, CmdLine]), case open_port({spawn, CmdLine}, []) of Port when is_port(Port) -> @@ -108,8 +121,8 @@ start_ssl_node(Name, Args) -> case await_ssl_node_up(Name, LSock) of #node_handle{} = NodeHandle -> test_server:format("Ssl node ~s started.~n", [Name]), - NodeName = list_to_atom(Name ++ "@" ++ host_name()), - NodeHandle#node_handle{nodename = NodeName}; + NodeHandle#node_handle{logfile = LogFilePath, + dumpfile = DumpFilePath}; Error -> exit({failed_to_start_node, Name, Error}) end; @@ -123,7 +136,7 @@ host_name() -> %% atom_to_list(node())), Host. -mk_node_cmdline(ListenPort, Name, Args) -> +mk_node_cmdline(ListenPort, Name, Args, LogPath, DumpPath) -> Static = "-detached -noinput", Pa = filename:dirname(code:which(?MODULE)), Prog = case catch init:get_argument(progname) of @@ -134,7 +147,6 @@ mk_node_cmdline(ListenPort, Name, Args) -> false -> "-sname "; _ -> "-name " end, - {ok, Pwd} = file:get_cwd(), "\"" ++ Prog ++ "\" " ++ Static ++ " " ++ NameSw ++ " " ++ Name ++ " " @@ -145,10 +157,10 @@ mk_node_cmdline(ListenPort, Name, Args) -> ++ host_name() ++ " " ++ integer_to_list(ListenPort) ++ " " ++ Args ++ " " - ++ "-env ERL_CRASH_DUMP " ++ Pwd ++ "/erl_crash_dump." ++ Name ++ " " + ++ "-env ERL_CRASH_DUMP " ++ DumpPath ++ " " ++ "-kernel inet_dist_connect_options \"[{recbuf,12582912},{sndbuf,12582912},{high_watermark,8388608},{low_watermark,4194304}]\" " ++ "-kernel inet_dist_listen_options \"[{recbuf,12582912},{sndbuf,12582912},{high_watermark,8388608},{low_watermark,4194304}]\" " - ++ "-kernel error_logger \"{file,\\\"" ++ Pwd ++ "/error_log." ++ Name ++ "\\\"}\" " + ++ "-kernel error_logger \"{file,\\\"" ++ LogPath ++ "\\\"}\" " ++ "-setcookie " ++ atom_to_list(erlang:get_cookie()). %% @@ -173,31 +185,36 @@ await_ssl_node_up(Name, LSock) -> end. check_ssl_node_up(Socket, Name, Bin) -> + NodeName = + case string:find(Name,"@") of + nomatch -> + list_to_atom(Name++"@"++host_name()); + _ -> + list_to_atom(Name) + end, case catch binary_to_term(Bin) of {'EXIT', _} -> gen_tcp:close(Socket), exit({bad_data_received_from_ssl_node, Name, Bin}); {ssl_node_up, NodeName} -> - case list_to_atom(Name++"@"++host_name()) of - NodeName -> - Parent = self(), - Go = make_ref(), - %% Spawn connection handler on test server side - Pid = spawn( - fun () -> - link(group_leader()), - receive Go -> ok end, - process_flag(trap_exit, true), - tstsrvr_con_loop(Name, Socket, Parent) - end), - ok = gen_tcp:controlling_process(Socket, Pid), - Pid ! Go, - #node_handle{connection_handler = Pid, - socket = Socket, - name = Name}; - _ -> - exit({unexpected_ssl_node_connected, NodeName}) - end; + Parent = self(), + Go = make_ref(), + %% Spawn connection handler on test server side + Pid = spawn( + fun () -> + link(group_leader()), + receive Go -> ok end, + process_flag(trap_exit, true), + tstsrvr_con_loop(Name, Socket, Parent) + end), + ok = gen_tcp:controlling_process(Socket, Pid), + Pid ! Go, + #node_handle{connection_handler = Pid, + socket = Socket, + nodename = NodeName, + name = Name}; + {ssl_node_up, OtherNodeName} -> + exit({unexpected_ssl_node_connected, OtherNodeName}); Msg -> exit({unexpected_msg_instead_of_ssl_node_up, Name, Msg}) end. diff --git a/lib/ssl/test/ssl_dist_test_lib.hrl b/lib/ssl/test/ssl_dist_test_lib.hrl index 86b9b37026..9b1e1045ba 100644 --- a/lib/ssl/test/ssl_dist_test_lib.hrl +++ b/lib/ssl/test/ssl_dist_test_lib.hrl @@ -22,5 +22,7 @@ {connection_handler, socket, name, - nodename} + nodename, + logfile, + dumpfile} ). diff --git a/lib/ssl/test/ssl_eqc_SUITE.erl b/lib/ssl/test/ssl_eqc_SUITE.erl index 4bfff1585e..cf6185e591 100644 --- a/lib/ssl/test/ssl_eqc_SUITE.erl +++ b/lib/ssl/test/ssl_eqc_SUITE.erl @@ -36,6 +36,7 @@ tls_cipher_openssl_suite_names/1, tls_anon_cipher_suite_names/1, tls_anon_cipher_openssl_suite_names/1, + tls_signature_algs/1, tls_unorded_chains/1, tls_extraneous_chain/1, tls_extraneous_chains/1, @@ -54,6 +55,7 @@ all() -> tls_cipher_openssl_suite_names, tls_anon_cipher_suite_names, tls_anon_cipher_openssl_suite_names, + tls_signature_algs, tls_unorded_chains, tls_extraneous_chain, tls_extraneous_chains, @@ -101,6 +103,11 @@ tls_anon_cipher_openssl_suite_names(Config) when is_list(Config) -> true = ct_property_test:quickcheck(ssl_eqc_cipher_format:prop_tls_anon_cipher_suite_openssl_name(), Config). +tls_signature_algs(Config) when is_list(Config) -> + %% manual test: proper:quickcheck(ssl_eqc_handshake:prop_tls_signature_algs()). + true = ct_property_test:quickcheck(ssl_eqc_cipher_format:prop_tls_signature_algs(), + Config). + tls_unorded_chains(Config) when is_list(Config) -> %% manual test: proper:quickcheck(ssl_eqc_chain:prop_tls_ordered_path("/tmp") ssl:start(), diff --git a/lib/ssl/test/ssl_session_SUITE.erl b/lib/ssl/test/ssl_session_SUITE.erl index 8f2be84a84..c74052f6b4 100644 --- a/lib/ssl/test/ssl_session_SUITE.erl +++ b/lib/ssl/test/ssl_session_SUITE.erl @@ -159,6 +159,7 @@ init_per_testcase(server_max_session_table, Config) -> ct:timetrap({seconds, 30}), Config; init_per_testcase(_, Config) -> + ct:timetrap({seconds, 30}), Config. end_per_testcase(reuse_session_expired, Config) -> @@ -461,49 +462,49 @@ explicit_session_reuse_expired(Config) when is_list(Config) -> no_reuses_session_server_restart_new_cert() -> [{doc,"Check that a session is not reused if the server is restarted with a new cert."}]. no_reuses_session_server_restart_new_cert(Config) when is_list(Config) -> + ClientOpts = ssl_test_lib:ssl_options(client_rsa_der_opts, Config), + ServerOpts = ssl_test_lib:ssl_options(server_rsa_der_verify_opts, Config), + RSA1024ServerOpts = ssl_test_lib:ssl_options(server_rsa_1024_der_opts, Config), + RSA1024ClientOpts = ssl_test_lib:ssl_options(client_rsa_1024_der_opts, Config), - ClientOpts = ssl_test_lib:ssl_options(client_rsa_opts, Config), - ServerOpts = ssl_test_lib:ssl_options(server_rsa_verify_opts, Config), - RSA1024ServerOpts = ssl_test_lib:ssl_options(server_rsa_1024_opts, Config), - RSA1024ClientOpts = ssl_test_lib:ssl_options(client_rsa_1024_opts, Config), {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config), - Server = + Server0 = ssl_test_lib:start_server([{node, ServerNode}, {port, 0}, {from, self()}, - {mfa, {ssl_test_lib, session_info_result, []}}, + {mfa, {ssl_test_lib, session_info_result, []}}, {options, ServerOpts}]), - Port = ssl_test_lib:inet_port(Server), + Port = ssl_test_lib:inet_port(Server0), Client0 = ssl_test_lib:start_client([{node, ClientNode}, - {port, Port}, {host, Hostname}, - {mfa, {ssl_test_lib, no_result, []}}, - {from, self()}, {options, [{reuse_sessions, save} | ClientOpts]}]), - SessionInfo = - receive - {Server, Info} -> - Info - end, + {port, Port}, {host, Hostname}, + {mfa, {ssl_test_lib, session_info_result, []}}, + {from, self()}, {options, [{reuse_sessions, save} | ClientOpts]}]), + Info0 = receive {Server0, Info00} -> Info00 end, + Info0 = receive {Client0, Info01} -> Info01 end, - ssl_test_lib:close(Server), + ct:sleep(?SLEEP), ssl_test_lib:close(Client0), + ssl_test_lib:close(Server0), - Server1 = - ssl_test_lib:start_server([{node, ServerNode}, {port, Port}, - {from, self()}, - {mfa, {ssl_test_lib, no_result, []}}, - {options, [{reuseaddr, true} | RSA1024ServerOpts]}]), + Server1 = ssl_test_lib:start_server([{node, ServerNode}, {port, Port}, + {from, self()}, + {mfa, {ssl_test_lib, session_info_result, []}}, + {options, [{reuseaddr, true} | RSA1024ServerOpts]}]), + + Client1 = ssl_test_lib:start_client([{node, ClientNode}, + {port, Port}, {host, Hostname}, + {mfa, {ssl_test_lib, session_info_result, []}}, + {from, self()}, {options, RSA1024ClientOpts}]), + Info1 = receive {Server1, Info10} -> Info10 end, - Client1 = - ssl_test_lib:start_client([{node, ClientNode}, - {port, Port}, {host, Hostname}, - {mfa, {ssl_test_lib, session_info_result, []}}, - {from, self()}, {options, RSA1024ClientOpts}]), receive - {Client1, SessionInfo} -> + {Client1, Info0} -> ct:fail(session_reused_when_server_has_new_cert); - {Client1, _Other} -> - ok + {Client1, Info1} -> + ct:pal("First: ~p~nSecond ~p~n",[Info0, Info1]); + Unexpected -> + ct:fail({unexpected, Unexpected, {Client1, Info1}}) end, ssl_test_lib:close(Server1), ssl_test_lib:close(Client1). diff --git a/lib/ssl/test/ssl_test_lib.erl b/lib/ssl/test/ssl_test_lib.erl index e4c23c22cf..8b30819816 100644 --- a/lib/ssl/test/ssl_test_lib.erl +++ b/lib/ssl/test/ssl_test_lib.erl @@ -108,6 +108,7 @@ check_ok/1, check_result/4, check_result/2, + get_result/1, gen_check_result/4, basic_alert/4, session_id/1, @@ -409,7 +410,7 @@ run_where(_, ipv6) -> {ClientNode, ServerNode, Host}. node_to_hostip(Node, Role) -> - [_ , Host] = string:tokens(atom_to_list(Node), "@"), + Host = rpc:call(Node, net_adm, localhost, []), {ok, Address} = inet:getaddr(Host, inet), %% Convert client addresses in 127.0.0.0/24 subnet to the atom 'localhost'. %% This is a workaround for testcase problems caused by the fact that @@ -1041,7 +1042,15 @@ client_loop_core(Socket, Pid, Transport) -> {ssl_closed, Socket} -> ok; {gen_tcp, closed} -> - ok + ok; + {apply, From, Fun} -> + try + Res = Fun(Socket, Transport), + From ! {apply_res, Res} + catch E:R:ST -> + From ! {apply_res, {E,R,ST}} + end, + client_loop_core(Socket, Pid, Transport) end. client_cont_loop(_Node, Host, Port, Pid, Transport, Options, cancel, _Opts) -> @@ -1113,9 +1122,20 @@ close(Pid, Timeout) -> exit(Pid, kill) end. +get_result(Pids) -> + get_result(Pids, []). + +get_result([], Acc) -> + Acc; +get_result([Pid | Tail], Acc) -> + receive + {Pid, Msg} -> + get_result(Tail, [Msg | Acc]) + end. + check_result(Server, ServerMsg, Client, ClientMsg) -> {ClientIP, ClientPort} = get_ip_port(ServerMsg), - receive + receive {Server, ServerMsg} -> check_result(Client, ClientMsg); %% Workaround to accept local addresses (127.0.0.0/24) @@ -1710,16 +1730,20 @@ make_rsa_cert(Config) -> ServerChain = proplists:get_value(server_chain, Config, default_cert_chain_conf()), CertChainConf = gen_conf(rsa, rsa, ClientChain, ServerChain), GenCertData = public_key:pkix_test_data(CertChainConf), + #{client_config := ClientDerConf, server_config := ServerDerConf} = GenCertData, + [{server_config, ServerConf}, {client_config, ClientConf}] = x509_test:gen_pem_config_files(GenCertData, ClientFileBase, ServerFileBase), [{server_rsa_opts, [{reuseaddr, true} | ServerConf]}, - - {server_rsa_verify_opts, [{reuseaddr, true}, - {verify, verify_peer} | ServerConf]}, + {server_rsa_verify_opts, [{reuseaddr, true}, {verify, verify_peer} | ServerConf]}, {client_rsa_opts, ClientConf}, - {client_rsa_verify_opts, [{verify, verify_peer} |ClientConf]} - | Config]; + {client_rsa_verify_opts, [{verify, verify_peer} |ClientConf]}, + {server_rsa_der_opts, [{reuseaddr, true} | ServerDerConf]}, + {server_rsa_der_verify_opts, [{reuseaddr, true}, {verify, verify_peer} | ServerDerConf]}, + {client_rsa_der_opts, ClientDerConf}, + {client_rsa_der_verify_opts, [{verify, verify_peer} |ClientDerConf]} + | Config]; false -> Config end. @@ -1734,15 +1758,18 @@ make_rsa_1024_cert(Config) -> ServerChain = proplists:get_value(server_chain, Config, default_cert_chain_conf()), CertChainConf = gen_conf('rsa-1024', 'rsa-1024', ClientChain, ServerChain), GenCertData = public_key:pkix_test_data(CertChainConf), + #{client_config := ClientDerConf, server_config := ServerDerConf} = GenCertData, [{server_config, ServerConf}, {client_config, ClientConf}] = x509_test:gen_pem_config_files(GenCertData, ClientFileBase, ServerFileBase), [{server_rsa_1024_opts, [{ssl_imp, new},{reuseaddr, true} | ServerConf]}, - - {server_rsa_1024_verify_opts, [{ssl_imp, new}, {reuseaddr, true}, - {verify, verify_peer} | ServerConf]}, + {server_rsa_1024_verify_opts, [{ssl_imp, new}, {reuseaddr, true}, {verify, verify_peer} | ServerConf]}, {client_rsa_1024_opts, ClientConf}, - {client_rsa_1024_verify_opts, [{verify, verify_peer} |ClientConf]} + {client_rsa_1024_verify_opts, [{verify, verify_peer} |ClientConf]}, + {server_rsa_1024_der_opts, [{ssl_imp, new},{reuseaddr, true} | ServerDerConf]}, + {server_rsa_1024_der_verify_opts, [{ssl_imp, new}, {reuseaddr, true}, {verify, verify_peer} | ServerDerConf]}, + {client_rsa_1024_der_opts, ClientDerConf}, + {client_rsa_1024_der_verify_opts, [{verify, verify_peer} |ClientDerConf]} | Config]; false -> Config @@ -2080,6 +2107,7 @@ start_client(openssl, Port, ClientOpts, Config) -> Ciphers = proplists:get_value(ciphers, ClientOpts, ssl:cipher_suites(default,Version)), Groups0 = proplists:get_value(groups, ClientOpts), CertArgs = openssl_cert_options(ClientOpts, client), + SigAlgs = openssl_sigalgs(proplists:get_value(sigalgs, ClientOpts, undefined)), AlpnArgs = openssl_alpn_options(proplists:get_value(alpn, ClientOpts, undefined)), NpnArgs = openssl_npn_options(proplists:get_value(np, ClientOpts, undefined)), Reconnect = openssl_reconect_option(proplists:get_value(reconnect, ClientOpts, false)), @@ -2096,7 +2124,7 @@ start_client(openssl, Port, ClientOpts, Config) -> "-connect", hostname_format(HostName) ++ ":" ++ integer_to_list(Port), cipher_flag(Version), ciphers(Ciphers, Version), version_flag(Version)] - ++ CertArgs ++ AlpnArgs ++ NpnArgs ++ Reconnect ++ MaxFragLen ++ SessionArgs + ++ CertArgs ++ SigAlgs ++ AlpnArgs ++ NpnArgs ++ Reconnect ++ MaxFragLen ++ SessionArgs ++ Debug; Group -> ["s_client", @@ -2104,7 +2132,7 @@ start_client(openssl, Port, ClientOpts, Config) -> "-connect", hostname_format(HostName) ++ ":" ++ integer_to_list(Port), cipher_flag(Version), ciphers(Ciphers, Version), "-groups", Group, version_flag(Version)] - ++ CertArgs ++ AlpnArgs ++ NpnArgs ++ Reconnect ++ MaxFragLen ++ SessionArgs + ++ CertArgs ++ SigAlgs ++ AlpnArgs ++ NpnArgs ++ Reconnect ++ MaxFragLen ++ SessionArgs ++ Debug end, Args = maybe_force_ipv4(Args0), @@ -2347,6 +2375,11 @@ cert_option("-cert_chain", Value) -> cert_option(Opt, Value) -> [Opt, Value]. +openssl_sigalgs(undefined) -> + []; +openssl_sigalgs(SigAlgs) -> + ["-sigalgs", SigAlgs]. + supported_eccs(Opts) -> ToCheck = proplists:get_value(eccs, Opts, []), Supported = ssl:eccs(), diff --git a/lib/ssl/test/tls_1_3_record_SUITE.erl b/lib/ssl/test/tls_1_3_record_SUITE.erl index ab6d7bf33b..7b774ea1b9 100644 --- a/lib/ssl/test/tls_1_3_record_SUITE.erl +++ b/lib/ssl/test/tls_1_3_record_SUITE.erl @@ -36,7 +36,7 @@ -export([encode_decode/0, encode_decode/1, finished_verify_data/0, - finished_verify_data/1, + finished_verify_data/1, '1_RTT_handshake'/0, '1_RTT_handshake'/1, '0_RTT_handshake'/0, diff --git a/lib/ssl/test/tls_api_SUITE.erl b/lib/ssl/test/tls_api_SUITE.erl index 453ccf12ee..a5f4fe3709 100644 --- a/lib/ssl/test/tls_api_SUITE.erl +++ b/lib/ssl/test/tls_api_SUITE.erl @@ -512,8 +512,7 @@ tls_monitor_listener(Config) when is_list(Config) -> {options, tls_monitor_listen_opts(Version, ServerOpts)}]), _Port2 = ssl_test_lib:inet_port(Server2), - ProlistCounts1 = supervisor:count_children(ssl_listen_tracker_sup), - 2 = proplists:get_value(workers, ProlistCounts1), + 2 = count_children(workers, ssl_listen_tracker_sup), Sessions = session_info(Version), @@ -534,12 +533,11 @@ tls_monitor_listener(Config) when is_list(Config) -> ct:sleep(1000) end, - ProlistCounts2 = supervisor:count_children(ssl_listen_tracker_sup), Sessions1 = session_info(Version), true = (Sessions1 == 1), - - 1 = proplists:get_value(workers, ProlistCounts2). + + 1 = count_children(workers, ssl_listen_tracker_sup). %%-------------------------------------------------------------------- tls_tcp_msg() -> @@ -826,7 +824,7 @@ transport_close_in_inital_hello(Config) when is_list(Config) -> Sup = (whereis(tls_connection_sup)), - check_connection_workers(Sup, 2), + check_connection_processes(Sup, 2), Acceptor ! die, @@ -838,20 +836,20 @@ transport_close_in_inital_hello(Config) when is_list(Config) -> {'EXIT', Connector, _} -> ok end, - check_connection_workers(Sup, 0). + check_connection_processes(Sup, 0). -check_connection_workers(Sup, N) -> - check_connection_workers(Sup, N, 5). +check_connection_processes(Sup, N) -> + check_connection_processes(Sup, N, 5). -check_connection_workers(Sup, N, 0) -> - N = proplists:get_value(workers, supervisor:count_children(Sup)); -check_connection_workers(Sup, N, M) -> - case proplists:get_value(workers, supervisor:count_children(Sup)) of +check_connection_processes(Sup, N, 0) -> + N = count_children(supervisors, Sup); +check_connection_processes(Sup, N, M) -> + case count_children(supervisors, Sup) of N -> ok; _ -> ct:sleep(500), - check_connection_workers(Sup, N, M-1) + check_connection_processes(Sup, N, M-1) end. %%-------------------------------------------------------------------- @@ -1100,8 +1098,9 @@ tls_monitor_client_opts(_, Opts) -> Opts. session_info('tlsv1.3') -> - ProlistCounts = supervisor:count_children(tls_server_session_ticket_sup), - proplists:get_value(workers, ProlistCounts); + count_children(workers, tls_server_session_ticket_sup); session_info(_) -> - ProlistCounts = supervisor:count_children(ssl_server_session_cache_sup), - proplists:get_value(workers, ProlistCounts). + count_children(workers, ssl_server_session_cache_sup). + +count_children(ChildType, SupRef) -> + proplists:get_value(ChildType, supervisor:count_children(SupRef)). diff --git a/lib/ssl/vsn.mk b/lib/ssl/vsn.mk index 8f1847a5be..4064eb55f6 100644 --- a/lib/ssl/vsn.mk +++ b/lib/ssl/vsn.mk @@ -1 +1 @@ -SSL_VSN = 10.5.1 +SSL_VSN = 10.5.3 |