diff options
Diffstat (limited to 'lib/ssl')
27 files changed, 1058 insertions, 1157 deletions
diff --git a/lib/ssl/doc/src/notes.xml b/lib/ssl/doc/src/notes.xml index 335896c60a..2f675560d6 100644 --- a/lib/ssl/doc/src/notes.xml +++ b/lib/ssl/doc/src/notes.xml @@ -27,6 +27,75 @@ </header> <p>This document describes the changes made to the SSL application.</p> +<section><title>SSL 9.4</title> + + <section><title>Fixed Bugs and Malfunctions</title> + <list> + <item> + <p> + Handling of zero size fragments in TLS could cause an + infinite loop. This has now been corrected.</p> + <p> + Own Id: OTP-15328 Aux Id: ERIERL-379 </p> + </item> + <item> + <p> + DTLS record check needs to consider that a resent hello + message can have a different version than the negotiated.</p> + <p> + Own Id: OTP-15807 Aux Id: ERL-920 </p> + </item> + </list> + </section> + + + <section><title>Improvements and New Features</title> + <list> + <item> + <p> + Basic support for TLS 1.3 Client for experimental use. + For more information see the Standards Compliance chapter + of the User's Guide.</p> + <p> + Own Id: OTP-15431</p> + </item> + <item> + <p> + Correct solution for retaining tcp flow control OTP-15802 + (ERL-934) as to not break ssl:recv as reported in + (ERL-938)</p> + <p> + Own Id: OTP-15823 Aux Id: ERL-934, ERL-938 </p> + </item> + <item> + <p> + Enhance dialyzer specs to reflect implementation better + and avoid dialyzer warnings for the user that wants to + use TLS with unix domain sockets.</p> + <p> + Own Id: OTP-15851 Aux Id: PR-2235 </p> + </item> + <item> + <p> + Add support for ECDSA signature algorithms in TLS 1.3.</p> + <p> + Own Id: OTP-15854</p> + </item> + <item> + <p> + Correct error handling of TLS downgrade, possible return + values form ssl:close/2 when downgrading is {ok, Port} or + {error, Reason}, it could happen that only ok was + returned instead of {error, closed} when downgrade failed + due to that the peer closed the TCP connection.</p> + <p> + Own Id: OTP-16027</p> + </item> + </list> + </section> + +</section> + <section><title>SSL 9.3.5</title> <section><title>Improvements and New Features</title> diff --git a/lib/ssl/src/dtls_connection.erl b/lib/ssl/src/dtls_connection.erl index b220691e79..f8fd42e36d 100644 --- a/lib/ssl/src/dtls_connection.erl +++ b/lib/ssl/src/dtls_connection.erl @@ -66,7 +66,7 @@ %%==================================================================== %% Setup %%==================================================================== -start_fsm(Role, Host, Port, Socket, {#ssl_options{erl_dist = false},_, Tracker} = Opts, +start_fsm(Role, Host, Port, Socket, {#{erl_dist := false},_, Tracker} = Opts, User, {CbModule, _, _, _, _} = CbInfo, Timeout) -> try @@ -316,10 +316,10 @@ queue_handshake(Handshake0, #state{handshake_env = #handshake_env{tls_handshake_ flight_buffer = #{handshakes := HsBuffer0, change_cipher_spec := undefined, next_sequence := Seq} = Flight0, - ssl_options = SslOpts} = State) -> + ssl_options = #{log_level := LogLevel}} = State) -> Handshake = dtls_handshake:encode_handshake(Handshake0, Version, Seq), Hist = update_handshake_history(Handshake0, Handshake, Hist0), - ssl_logger:debug(SslOpts#ssl_options.log_level, outbound, 'handshake', Handshake0), + ssl_logger:debug(LogLevel, outbound, 'handshake', Handshake0), State#state{flight_buffer = Flight0#{handshakes => [Handshake | HsBuffer0], next_sequence => Seq +1}, @@ -329,10 +329,10 @@ queue_handshake(Handshake0, #state{handshake_env = #handshake_env{tls_handshake_ connection_env = #connection_env{negotiated_version = Version}, flight_buffer = #{handshakes_after_change_cipher_spec := Buffer0, next_sequence := Seq} = Flight0, - ssl_options = SslOpts} = State) -> + ssl_options = #{log_level := LogLevel}} = State) -> Handshake = dtls_handshake:encode_handshake(Handshake0, Version, Seq), Hist = update_handshake_history(Handshake0, Handshake, Hist0), - ssl_logger:debug(SslOpts#ssl_options.log_level, outbound, 'handshake', Handshake0), + ssl_logger:debug(LogLevel, outbound, 'handshake', Handshake0), State#state{flight_buffer = Flight0#{handshakes_after_change_cipher_spec => [Handshake | Buffer0], next_sequence => Seq +1}, @@ -384,11 +384,11 @@ send_alert(Alert, #state{static_env = #static_env{socket = Socket, connection_env = #connection_env{negotiated_version = Version}, connection_states = ConnectionStates0, - ssl_options = SslOpts} = State0) -> + ssl_options = #{log_level := LogLevel}} = State0) -> {BinMsg, ConnectionStates} = encode_alert(Alert, Version, ConnectionStates0), send(Transport, Socket, BinMsg), - ssl_logger:debug(SslOpts#ssl_options.log_level, outbound, 'record', BinMsg), + ssl_logger:debug(LogLevel, outbound, 'record', BinMsg), State0#state{connection_states = ConnectionStates}. send_alert_in_connection(Alert, State) -> @@ -440,7 +440,7 @@ init({call, From}, {start, Timeout}, session_cache_cb = CacheCb}, handshake_env = #handshake_env{renegotiation = {Renegotiation, _}}, connection_env = CEnv, - ssl_options = SslOpts, + ssl_options = #{versions := Versions} = SslOpts, session = #session{own_certificate = Cert} = Session0, connection_states = ConnectionStates0 } = State0) -> @@ -448,7 +448,7 @@ init({call, From}, {start, Timeout}, Cache, CacheCb, Renegotiation, Cert), Version = Hello#client_hello.client_version, - HelloVersion = dtls_record:hello_version(Version, SslOpts#ssl_options.versions), + HelloVersion = dtls_record:hello_version(Version, Versions), State1 = prepare_flight(State0#state{connection_env = CEnv#connection_env{negotiated_version = Version}}), {State2, Actions} = send_handshake(Hello, State1#state{connection_env = CEnv#connection_env{negotiated_version = HelloVersion}}), State = State2#state{connection_env = CEnv#connection_env{negotiated_version = Version}, %% RequestedVersion @@ -547,14 +547,14 @@ hello(internal, #hello_verify_request{cookie = Cookie}, #state{static_env = #sta Hello#client_hello.session_id}}, next_event(?FUNCTION_NAME, no_record, State, Actions); hello(internal, #client_hello{extensions = Extensions} = Hello, - #state{ssl_options = #ssl_options{handshake = 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{extensions = Extensions} = Hello, - #state{ssl_options = #ssl_options{handshake = 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, @@ -705,7 +705,7 @@ connection(internal, #hello_request{}, #state{static_env = #static_env{host = Ho handshake_env = #handshake_env{ renegotiation = {Renegotiation, _}}, connection_env = CEnv, session = #session{own_certificate = Cert} = Session0, - ssl_options = SslOpts, + ssl_options = #{versions := Versions} = SslOpts, connection_states = ConnectionStates0, protocol_specific = PS } = State0) -> @@ -713,7 +713,7 @@ connection(internal, #hello_request{}, #state{static_env = #static_env{host = Ho Hello = dtls_handshake:client_hello(Host, Port, ConnectionStates0, SslOpts, Cache, CacheCb, Renegotiation, Cert), Version = Hello#client_hello.client_version, - HelloVersion = dtls_record:hello_version(Version, SslOpts#ssl_options.versions), + HelloVersion = dtls_record:hello_version(Version, Versions), State1 = prepare_flight(State0), {State2, Actions} = send_handshake(Hello, State1#state{connection_env = CEnv#connection_env{negotiated_version = HelloVersion}}), State = State2#state{protocol_specific = PS#{flight_state => initial_flight_state(DataTag)}, @@ -774,9 +774,10 @@ format_status(Type, Data) -> %%-------------------------------------------------------------------- %%% Internal functions %%-------------------------------------------------------------------- -initial_state(Role, Host, Port, Socket, {SSLOptions, SocketOptions, _}, User, +initial_state(Role, Host, Port, Socket, + {#{client_renegotiation := ClientRenegotiation} = SSLOptions, SocketOptions, _}, User, {CbModule, DataTag, CloseTag, ErrorTag, PassiveTag}) -> - #ssl_options{beast_mitigation = BeastMitigation} = SSLOptions, + #{beast_mitigation := BeastMitigation} = SSLOptions, ConnectionStates = dtls_record:init_connection_states(Role, BeastMitigation), SessionCacheCb = case application:get_env(ssl, session_cb) of @@ -810,13 +811,13 @@ initial_state(Role, Host, Port, Socket, {SSLOptions, SocketOptions, _}, User, handshake_env = #handshake_env{ tls_handshake_history = ssl_handshake:init_handshake_history(), renegotiation = {false, first}, - allow_renegotiate = SSLOptions#ssl_options.client_renegotiation + allow_renegotiate = ClientRenegotiation }, connection_env = #connection_env{user_application = {Monitor, User}}, socket_options = SocketOptions, %% We do not want to save the password in the state so that %% could be written in the clear into error logs. - ssl_options = SSLOptions#ssl_options{password = undefined}, + ssl_options = SSLOptions#{password => undefined}, session = #session{is_resumable = new}, connection_states = ConnectionStates, protocol_buffers = #protocol_buffers{}, @@ -989,12 +990,13 @@ handle_alerts([Alert | Alerts], {next_state, StateName, State}) -> handle_alerts([Alert | Alerts], {next_state, StateName, State, _Actions}) -> handle_alerts(Alerts, ssl_connection:handle_alert(Alert, StateName, State)). -handle_own_alert(Alert, Version, StateName, #state{static_env = #static_env{data_tag = udp, - role = Role}, - ssl_options = Options} = State0) -> +handle_own_alert(Alert, Version, StateName, + #state{static_env = #static_env{data_tag = udp, + role = Role}, + ssl_options = #{log_level := LogLevel}} = State0) -> case ignore_alert(Alert, State0) of {true, State} -> - log_ignore_alert(Options#ssl_options.log_level, StateName, Alert, Role), + log_ignore_alert(LogLevel, StateName, Alert, Role), {next_state, StateName, State}; {false, State} -> ssl_connection:handle_own_alert(Alert, Version, StateName, State) @@ -1105,7 +1107,7 @@ send_handshake_flight(#state{static_env = #static_env{socket = Socket, flight_buffer = #{handshakes := Flight, change_cipher_spec := undefined}, connection_states = ConnectionStates0, - ssl_options = #ssl_options{log_level = LogLevel}} = State0, + ssl_options = #{log_level := LogLevel}} = State0, Epoch) -> %% TODO remove hardcoded Max size {Encoded, ConnectionStates} = @@ -1121,7 +1123,7 @@ send_handshake_flight(#state{static_env = #static_env{socket = Socket, change_cipher_spec := ChangeCipher, handshakes_after_change_cipher_spec := []}, connection_states = ConnectionStates0, - ssl_options = #ssl_options{log_level = LogLevel}} = State0, + ssl_options = #{log_level := LogLevel}} = State0, Epoch) -> {HsBefore, ConnectionStates1} = encode_handshake_flight(lists:reverse(Flight0), Version, 1400, Epoch, ConnectionStates0), @@ -1139,7 +1141,7 @@ send_handshake_flight(#state{static_env = #static_env{socket = Socket, change_cipher_spec := ChangeCipher, handshakes_after_change_cipher_spec := Flight1}, connection_states = ConnectionStates0, - ssl_options = #ssl_options{log_level = LogLevel}} = State0, + ssl_options = #{log_level := LogLevel}} = State0, Epoch) -> {HsBefore, ConnectionStates1} = encode_handshake_flight(lists:reverse(Flight0), Version, 1400, Epoch-1, ConnectionStates0), @@ -1160,7 +1162,7 @@ send_handshake_flight(#state{static_env = #static_env{socket = Socket, change_cipher_spec := ChangeCipher, handshakes_after_change_cipher_spec := Flight1}, connection_states = ConnectionStates0, - ssl_options = #ssl_options{log_level = LogLevel}} = State0, + ssl_options = #{log_level := LogLevel}} = State0, Epoch) -> {EncChangeCipher, ConnectionStates1} = encode_change_cipher(ChangeCipher, Version, Epoch-1, ConnectionStates0), @@ -1220,8 +1222,8 @@ send_application_data(Data, From, _StateName, connection_env = #connection_env{negotiated_version = Version}, handshake_env = HsEnv, connection_states = ConnectionStates0, - ssl_options = #ssl_options{renegotiate_at = RenegotiateAt, - log_level = LogLevel}} = State0) -> + ssl_options = #{renegotiate_at := RenegotiateAt, + log_level := LogLevel}} = State0) -> case time_to_renegotiate(Data, ConnectionStates0, RenegotiateAt) of true -> diff --git a/lib/ssl/src/dtls_handshake.erl b/lib/ssl/src/dtls_handshake.erl index 4a381745d4..b2a5fcfa66 100644 --- a/lib/ssl/src/dtls_handshake.erl +++ b/lib/ssl/src/dtls_handshake.erl @@ -47,7 +47,7 @@ %%==================================================================== %%-------------------------------------------------------------------- -spec client_hello(ssl:host(), inet:port_number(), ssl_record:connection_states(), - #ssl_options{}, integer(), atom(), boolean(), der_cert()) -> + ssl_options(), integer(), atom(), boolean(), der_cert()) -> #client_hello{}. %% %% Description: Creates a client hello message. @@ -60,16 +60,15 @@ client_hello(Host, Port, ConnectionStates, SslOpts, %%-------------------------------------------------------------------- -spec client_hello(ssl:host(), inet:port_number(), term(), ssl_record:connection_states(), - #ssl_options{}, integer(), atom(), boolean(), der_cert()) -> + ssl_options(), integer(), atom(), boolean(), der_cert()) -> #client_hello{}. %% %% Description: Creates a client hello message. %%-------------------------------------------------------------------- client_hello(Host, Port, Cookie, ConnectionStates, - #ssl_options{versions = Versions, - ciphers = UserSuites, - fallback = Fallback - } = SslOpts, + #{versions := Versions, + ciphers := UserSuites, + fallback := Fallback} = SslOpts, Cache, CacheCb, Renegotiation, OwnCert) -> Version = dtls_record:highest_protocol_version(Versions), Pending = ssl_record:pending_connection_state(ConnectionStates, read), @@ -97,7 +96,7 @@ hello(#server_hello{server_version = Version, random = Random, cipher_suite = CipherSuite, compression_method = Compression, session_id = SessionId, extensions = HelloExt}, - #ssl_options{versions = SupportedVersions} = SslOpt, + #{versions := SupportedVersions} = SslOpt, ConnectionStates0, Renegotiation) -> case dtls_record:is_acceptable_version(Version, SupportedVersions) of true -> @@ -108,7 +107,7 @@ hello(#server_hello{server_version = Version, random = Random, ?ALERT_REC(?FATAL, ?PROTOCOL_VERSION) end; hello(#client_hello{client_version = ClientVersion} = Hello, - #ssl_options{versions = Versions} = SslOpts, + #{versions := Versions} = SslOpts, Info, Renegotiation) -> Version = ssl_handshake:select_version(dtls_record, ClientVersion, Versions), handle_client_hello(Version, Hello, SslOpts, Info, Renegotiation). @@ -151,7 +150,7 @@ encode_handshake(Handshake, Version, Seq) -> %%-------------------------------------------------------------------- %%-------------------------------------------------------------------- --spec get_dtls_handshake(ssl_record:ssl_version(), binary(), #protocol_buffers{}, #ssl_options{}) -> +-spec get_dtls_handshake(ssl_record:ssl_version(), binary(), #protocol_buffers{}, ssl_options()) -> {[dtls_handshake()], #protocol_buffers{}}. %% %% Description: Given buffered and new data from dtls_record, collects @@ -170,10 +169,10 @@ handle_client_hello(Version, compression_methods = Compressions, random = Random, extensions = HelloExt}, - #ssl_options{versions = Versions, - signature_algs = SupportedHashSigns, - eccs = SupportedECCs, - honor_ecc_order = ECCOrder} = SslOpts, + #{versions := Versions, + signature_algs := SupportedHashSigns, + eccs := SupportedECCs, + honor_ecc_order := ECCOrder} = SslOpts, {Port, Session0, Cache, CacheCb, ConnectionStates0, Cert, _}, Renegotiation) -> case dtls_record:is_acceptable_version(Version, Versions) of @@ -317,14 +316,14 @@ handle_fragments(Version, FragmentData, Buffers0, Options, Acc) -> do_handle_fragments(_, [], Buffers, _Options, Acc) -> {lists:reverse(Acc), Buffers}; -do_handle_fragments(Version, [Fragment | Fragments], Buffers0, Options, Acc) -> +do_handle_fragments(Version, [Fragment | Fragments], Buffers0, #{log_level := LogLevel} = Options, Acc) -> case reassemble(Version, Fragment, Buffers0) of {more_data, Buffers} when Fragments == [] -> {lists:reverse(Acc), Buffers}; {more_data, Buffers} -> do_handle_fragments(Version, Fragments, Buffers, Options, Acc); {{Handshake, _} = HsPacket, Buffers} -> - ssl_logger:debug(Options#ssl_options.log_level, inbound, 'handshake', Handshake), + ssl_logger:debug(LogLevel, inbound, 'handshake', Handshake), do_handle_fragments(Version, Fragments, Buffers, Options, [HsPacket | Acc]) end. diff --git a/lib/ssl/src/dtls_record.erl b/lib/ssl/src/dtls_record.erl index 8b8db7b2de..ee0ce2d22a 100644 --- a/lib/ssl/src/dtls_record.erl +++ b/lib/ssl/src/dtls_record.erl @@ -163,7 +163,7 @@ current_connection_state_epoch(#{current_write := #{epoch := Epoch}}, %%-------------------------------------------------------------------- -spec get_dtls_records(binary(), {atom(), atom(), ssl_record:ssl_version(), [ssl_record:ssl_version()]}, binary(), - #ssl_options{}) -> {[binary()], binary()} | #alert{}. + ssl_options()) -> {[binary()], binary()} | #alert{}. %% %% Description: Given old buffer and new data from UDP/SCTP, packs up a records %% and returns it as a list of tls_compressed binaries also returns leftover @@ -399,13 +399,13 @@ initial_connection_state(ConnectionEnd, BeastMitigation) -> get_dtls_records_aux({DataTag, StateName, _, Versions} = Vinfo, <<?BYTE(Type),?BYTE(MajVer),?BYTE(MinVer), ?UINT16(Epoch), ?UINT48(SequenceNumber), ?UINT16(Length), Data:Length/binary, Rest/binary>> = RawDTLSRecord, - Acc, SslOpts) when ((StateName == hello) orelse - ((StateName == certify) andalso (DataTag == udp)) orelse - ((StateName == abbreviated) andalso(DataTag == udp))) - andalso - ((Type == ?HANDSHAKE) orelse - (Type == ?ALERT)) -> - ssl_logger:debug(SslOpts#ssl_options.log_level, inbound, 'record', [RawDTLSRecord]), + Acc, #{log_level := LogLevel} = SslOpts) + when ((StateName == hello) + orelse ((StateName == certify) andalso (DataTag == udp)) + orelse ((StateName == abbreviated) andalso (DataTag == udp))) andalso ((Type == ?HANDSHAKE) + orelse + (Type == ?ALERT)) -> + ssl_logger:debug(LogLevel, inbound, 'record', [RawDTLSRecord]), case is_acceptable_version({MajVer, MinVer}, Versions) of true -> get_dtls_records_aux(Vinfo, Rest, [#ssl_tls{type = Type, @@ -418,11 +418,11 @@ get_dtls_records_aux({DataTag, StateName, _, Versions} = Vinfo, <<?BYTE(Type),?B get_dtls_records_aux({_, _, Version, _} = Vinfo, <<?BYTE(Type),?BYTE(MajVer),?BYTE(MinVer), ?UINT16(Epoch), ?UINT48(SequenceNumber), ?UINT16(Length), Data:Length/binary, Rest/binary>> = RawDTLSRecord, - Acc, SslOpts) when (Type == ?APPLICATION_DATA) orelse + Acc, #{log_level := LogLevel} = SslOpts) when (Type == ?APPLICATION_DATA) orelse (Type == ?HANDSHAKE) orelse (Type == ?ALERT) orelse (Type == ?CHANGE_CIPHER_SPEC) -> - ssl_logger:debug(SslOpts#ssl_options.log_level, inbound, 'record', [RawDTLSRecord]), + ssl_logger:debug(LogLevel, inbound, 'record', [RawDTLSRecord]), case {MajVer, MinVer} of Version -> get_dtls_records_aux(Vinfo, Rest, [#ssl_tls{type = Type, diff --git a/lib/ssl/src/ssl.erl b/lib/ssl/src/ssl.erl index 7ff9aed8ea..0d6501b5ee 100644 --- a/lib/ssl/src/ssl.erl +++ b/lib/ssl/src/ssl.erl @@ -97,7 +97,8 @@ new_ssl_options/3, suite_to_str/1, suite_to_openssl_str/1, - str_to_suite/1]). + str_to_suite/1, + options_to_map/1]). -deprecated({ssl_accept, 1, eventually}). -deprecated({ssl_accept, 2, eventually}). @@ -438,6 +439,7 @@ elliptic_curves => [public_key:oid()], sni => hostname()}. % exported %% ------------------------------------------------------------------------------------------------------- +-define(SSL_OPTIONS, record_info(fields, ssl_options)). %%%-------------------------------------------------------------------- %%% API @@ -784,7 +786,13 @@ close(#sslsocket{pid = [TLSPid|_]}, {Pid, Timeout} = DownGrade) when is_pid(TLSPid), is_pid(Pid), (is_integer(Timeout) andalso Timeout >= 0) or (Timeout == infinity) -> - ssl_connection:close(TLSPid, {close, DownGrade}); + case ssl_connection:close(TLSPid, {close, DownGrade}) of + ok -> %% In normal close {error, closed} is regarded as ok, as it is not interesting which side + %% that got to do the actual close. But in the downgrade case only {ok, Port} is a sucess. + {error, closed}; + Other -> + Other + end; close(#sslsocket{pid = [TLSPid|_]}, Timeout) when is_pid(TLSPid), (is_integer(Timeout) andalso Timeout >= 0) or (Timeout == infinity) -> ssl_connection:close(TLSPid, {close, Timeout}); @@ -1452,15 +1460,18 @@ do_listen(Port, #config{transport_info = {Transport, _, _, _,_}} = Config, tls_c do_listen(Port, Config, dtls_connection) -> dtls_socket:listen(Port, Config). -%% Handle extra ssl options given to ssl_accept --spec handle_options([any()], #ssl_options{}) -> #ssl_options{} - ; ([any()], client | server) -> {ok, #config{}}. + +-spec handle_options([any()], client | server) -> {ok, #config{}}; + ([any()], ssl_options()) -> ssl_options(). + handle_options(Opts, Role) -> handle_options(Opts, Role, undefined). -handle_options(Opts0, #ssl_options{protocol = Protocol, cacerts = CaCerts0, - cacertfile = CaCertFile0} = InheritedSslOpts, _) -> +%% Handle ssl options at handshake_continue +handle_options(Opts0, #{protocol := Protocol, + cacerts := CaCerts0, + cacertfile := CaCertFile0} = InheritedSslOpts, _) -> RecordCB = record_cb(Protocol), CaCerts = handle_option(cacerts, Opts0, CaCerts0), {Verify, FailIfNoPeerCert, CaCertDefault, VerifyFun, PartialChainHanlder, @@ -1472,13 +1483,13 @@ handle_options(Opts0, #ssl_options{protocol = Protocol, cacerts = CaCerts0, CAFile end, - NewVerifyOpts = InheritedSslOpts#ssl_options{cacerts = CaCerts, - cacertfile = CaCertFile, - verify = Verify, - verify_fun = VerifyFun, - partial_chain = PartialChainHanlder, - fail_if_no_peer_cert = FailIfNoPeerCert, - verify_client_once = VerifyClientOnce}, + NewVerifyOpts = InheritedSslOpts#{cacerts => CaCerts, + cacertfile => CaCertFile, + verify => Verify, + verify_fun => VerifyFun, + partial_chain => PartialChainHanlder, + fail_if_no_peer_cert => FailIfNoPeerCert, + verify_client_once => VerifyClientOnce}, SslOpts1 = lists:foldl(fun(Key, PropList) -> proplists:delete(Key, PropList) end, Opts0, [cacerts, cacertfile, verify, verify_fun, partial_chain, @@ -1490,10 +1501,10 @@ handle_options(Opts0, #ssl_options{protocol = Protocol, cacerts = CaCerts0, Versions0 = [RecordCB:protocol_version(Vsn) || Vsn <- Value], Versions1 = lists:sort(fun RecordCB:is_higher/2, Versions0), new_ssl_options(proplists:delete(versions, SslOpts1), - NewVerifyOpts#ssl_options{versions = Versions1}, record_cb(Protocol)) + NewVerifyOpts#{versions => Versions1}, record_cb(Protocol)) end; -%% Handle all options in listen and connect +%% Handle all options in listen, connect and handshake handle_options(Opts0, Role, Host) -> Opts = proplists:expand([{binary, [{mode, binary}]}, {list, [{mode, list}]}], Opts0), @@ -1544,36 +1555,21 @@ handle_options(Opts0, Role, Host) -> user_lookup_fun = handle_option(user_lookup_fun, Opts, undefined), psk_identity = handle_option(psk_identity, Opts, undefined), srp_identity = handle_option(srp_identity, Opts, undefined), - ciphers = handle_cipher_option(proplists:get_value(ciphers, Opts, []), - HighestVersion), - eccs = handle_eccs_option(proplists:get_value(eccs, Opts, eccs()), - HighestVersion), - supported_groups = handle_supported_groups_option( - proplists:get_value(supported_groups, Opts, groups(default)), - HighestVersion), - signature_algs = - handle_hashsigns_option( - proplists:get_value( - signature_algs, - Opts, - default_option_role_sign_algs(server, - tls_v1:default_signature_algs(HighestVersion), - Role, - HighestVersion)), - tls_version(HighestVersion)), - signature_algs_cert = - handle_signature_algorithms_option( - proplists:get_value( - signature_algs_cert, - Opts, - undefined), %% Do not send by default - tls_version(HighestVersion)), - reuse_sessions = handle_reuse_sessions_option(reuse_sessions, Opts, Role), - reuse_session = handle_reuse_session_option(reuse_session, Opts, Role), + ciphers = handle_option(ciphers, Opts, undefined, undefined, undefined, HighestVersion), + + eccs = handle_option(eccs, Opts, undefined, undefined, undefined, HighestVersion), + + supported_groups = handle_option(supported_groups, Opts, undefined, undefined, undefined, + HighestVersion), + signature_algs = handle_option(signature_algs, Opts, undefined, Role, undefined, HighestVersion), + signature_algs_cert = handle_option(signature_algs_cert, Opts, undefined, undefined, undefined, + HighestVersion), + reuse_sessions = handle_option(reuse_sessions, Opts, undefined, Role), + + reuse_session = handle_option(reuse_session, Opts, undefined, Role), + secure_renegotiate = handle_option(secure_renegotiate, Opts, true), - client_renegotiation = handle_option(client_renegotiation, Opts, - default_option_role(server, true, Role), - server, Role), + client_renegotiation = handle_option(client_renegotiation, Opts, undefined, Role), renegotiate_at = handle_option(renegotiate_at, Opts, ?DEFAULT_RENEGOTIATE_AT), hibernate_after = handle_option(hibernate_after, Opts, infinity), erl_dist = handle_option(erl_dist, Opts, false), @@ -1584,71 +1580,61 @@ handle_options(Opts0, Role, Host) -> next_protocols_advertised = handle_option(next_protocols_advertised, Opts, undefined), next_protocol_selector = - make_next_protocol_selector( - handle_option(client_preferred_next_protocols, Opts, undefined)), - server_name_indication = handle_option(server_name_indication, Opts, - default_option_role(client, - server_name_indication_default(Host), Role)), + handle_option(next_protocol_selector, Opts, undefined), + server_name_indication = + handle_option(server_name_indication, Opts, undefined, Role, Host), sni_hosts = handle_option(sni_hosts, Opts, []), sni_fun = handle_option(sni_fun, Opts, undefined), - honor_cipher_order = handle_option(honor_cipher_order, Opts, - default_option_role(server, false, Role), - server, Role), - honor_ecc_order = handle_option(honor_ecc_order, Opts, - default_option_role(server, false, Role), - server, Role), + honor_cipher_order = handle_option(honor_cipher_order, Opts, undefined, Role), + honor_ecc_order = handle_option(honor_ecc_order, Opts, undefined, Role), protocol = Protocol, padding_check = proplists:get_value(padding_check, Opts, true), - beast_mitigation = handle_option(beast_mitigation, Opts, one_n_minus_one), - fallback = handle_option(fallback, Opts, - proplists:get_value(fallback, Opts, - default_option_role(client, - false, Role)), - client, Role), + beast_mitigation = handle_option(beast_mitigation, Opts, one_n_minus_one), + fallback = handle_option(fallback, Opts, undefined, Role), + crl_check = handle_option(crl_check, Opts, false), crl_cache = handle_option(crl_cache, Opts, {ssl_crl_cache, {internal, []}}), max_handshake_size = handle_option(max_handshake_size, Opts, ?DEFAULT_MAX_HANDSHAKE_SIZE), handshake = handle_option(handshake, Opts, full), - customize_hostname_check = handle_option(customize_hostname_check, Opts, []) + customize_hostname_check = handle_option(customize_hostname_check, Opts, []) }, LogLevel = handle_option(log_alert, Opts, true), - SSLOptions = SSLOptions0#ssl_options{ + SSLOptions1 = SSLOptions0#ssl_options{ log_level = handle_option(log_level, Opts, LogLevel) }, CbInfo = handle_option(cb_info, Opts, default_cb_info(Protocol)), - SslOptions = [protocol, versions, verify, verify_fun, partial_chain, - fail_if_no_peer_cert, verify_client_once, - depth, cert, certfile, key, keyfile, - password, cacerts, cacertfile, dh, dhfile, - user_lookup_fun, psk_identity, srp_identity, ciphers, - reuse_session, reuse_sessions, ssl_imp, client_renegotiation, - cb_info, renegotiate_at, secure_renegotiate, hibernate_after, - erl_dist, alpn_advertised_protocols, sni_hosts, sni_fun, - alpn_preferred_protocols, next_protocols_advertised, - client_preferred_next_protocols, log_alert, log_level, - server_name_indication, honor_cipher_order, padding_check, crl_check, crl_cache, - fallback, signature_algs, signature_algs_cert, eccs, honor_ecc_order, - beast_mitigation, max_handshake_size, handshake, customize_hostname_check, - supported_groups], SockOpts = lists:foldl(fun(Key, PropList) -> proplists:delete(Key, PropList) - end, Opts, SslOptions), + end, Opts, ?SSL_OPTIONS ++ + [ssl_imp, %% TODO: remove ssl_imp + client_preferred_next_protocols, %% next_protocol_selector + log_alert, + cb_info]), {Sock, Emulated} = emulated_options(Protocol, SockOpts), ConnetionCb = connection_cb(Opts), - + SSLOptions = options_to_map(SSLOptions1), {ok, #config{ssl = SSLOptions, emulated = Emulated, inet_ssl = Sock, inet_user = Sock, transport_info = CbInfo, connection_cb = ConnetionCb }}. -handle_option(OptionName, Opts, Default, Role, Role) -> - handle_option(OptionName, Opts, Default); -handle_option(_, _, undefined = Value, _, _) -> - Value. +%% handle_option(OptionName, Opts, Default, Role, Role) -> +%% handle_option(OptionName, Opts, Default); +%% handle_option(_, _, undefined = Value, _, _) -> +%% Value. -handle_option(sni_fun, Opts, Default) -> +handle_option(OptionName, Opts, Default) -> + handle_option(OptionName, Opts, Default, undefined, undefined, undefined). +%% +handle_option(OptionName, Opts, Default, Role) -> + handle_option(OptionName, Opts, Default, Role, undefined, undefined). +%% +handle_option(OptionName, Opts, Default, Role, Host) -> + handle_option(OptionName, Opts, Default, Role, Host, undefined). +%% +handle_option(sni_fun, Opts, Default, _Role, _Host, _Version) -> OptFun = validate_option(sni_fun, proplists:get_value(sni_fun, Opts, Default)), OptHosts = proplists:get_value(sni_hosts, Opts, undefined), @@ -1660,14 +1646,91 @@ handle_option(sni_fun, Opts, Default) -> _ -> throw({error, {conflict_options, [sni_fun, sni_hosts]}}) end; -handle_option(cb_info, Opts, Default) -> +handle_option(cb_info, Opts, Default, _Role, _Host, _Version) -> CbInfo = proplists:get_value(cb_info, Opts, Default), true = validate_option(cb_info, CbInfo), handle_cb_info(CbInfo, Default); -handle_option(OptionName, Opts, Default) -> +handle_option(ciphers = Key, Opts, _Default, _Role, _Host, HighestVersion) -> + handle_cipher_option(proplists:get_value(Key, Opts, []), + HighestVersion); +handle_option(eccs = Key, Opts, _Default, _Role, _Host, HighestVersion) -> + handle_eccs_option(proplists:get_value(Key, Opts, eccs()), + HighestVersion); +handle_option(supported_groups = Key, Opts, _Default, _Role, _Host, HighestVersion) -> + handle_supported_groups_option(proplists:get_value(Key, Opts, groups(default)), + HighestVersion); +handle_option(signature_algs = Key, Opts, _Default, Role, _Host, HighestVersion) -> + handle_hashsigns_option( + proplists:get_value(Key, + Opts, + default_option_role_sign_algs(server, + tls_v1:default_signature_algs(HighestVersion), + Role, + HighestVersion)), + tls_version(HighestVersion)); +handle_option(signature_algs_cert = Key, Opts, _Default, _Role, _Host, HighestVersion) -> + handle_signature_algorithms_option( + proplists:get_value(Key, + Opts, + undefined), %% Do not send by default + tls_version(HighestVersion)); +handle_option(reuse_sessions = Key, Opts, _Default, client, _Host, _Version) -> + Value = proplists:get_value(Key, Opts, true), + validate_option(Key, Value), + Value; +handle_option(reuse_sessions = Key, Opts0, _Default, server, _Host, _Version) -> + Opts = proplists:delete({Key, save}, Opts0), + Value = proplists:get_value(Key, Opts, true), + validate_option(Key, Value), + Value; +handle_option(reuse_session = Key, Opts, _Default, client, _Host, _Version) -> + Value = proplists:get_value(Key, Opts, undefined), + validate_option(Key, Value), + Value; +handle_option(reuse_session = Key, Opts, _Default, server, _Host, _Version) -> + ReuseSessionFun = fun(_, _, _, _) -> true end, + Value = proplists:get_value(Key, Opts, ReuseSessionFun), + validate_option(Key, Value), + Value; +handle_option(fallback = Key, Opts, _Default, Role, _Host, _Version) -> + Value = proplists:get_value(Key, Opts, default_option_role(client, false, Role)), + assert_role(client_only, Role, Key, Value), + validate_option(Key, Value); +handle_option(client_renegotiation = Key, Opts, _Default, Role, _Host, _Version) -> + Value = proplists:get_value(Key, Opts, default_option_role(server, true, Role)), + assert_role(server_only, Role, Key, Value), + validate_option(Key, Value); +handle_option(honor_cipher_order = Key, Opts, _Default, Role, _Host, _Version) -> + Value = proplists:get_value(Key, Opts, default_option_role(server, false, Role)), + assert_role(server_only, Role, Key, Value), + validate_option(Key, Value); +handle_option(honor_ecc_order = Key, Opts, _Default, Role, _Host, _Version) -> + Value = proplists:get_value(Key, Opts, default_option_role(server, false, Role)), + assert_role(server_only, Role, Key, Value), + validate_option(Key, Value); +handle_option(next_protocol_selector = _Key, Opts, _Default, _Role, _Host, _Version) -> + make_next_protocol_selector( + handle_option(client_preferred_next_protocols, Opts, undefined, undefined, undefined, undefined)); +handle_option(server_name_indication = Key, Opts, _Default, Role, Host, _Version) -> + Default = default_option_role(client, server_name_indication_default(Host), Role), + validate_option(Key, proplists:get_value(Key, Opts, Default)); +handle_option(OptionName, Opts, Default, _Role, _Host, _Version) -> validate_option(OptionName, proplists:get_value(OptionName, Opts, Default)). + +assert_role(client_only, client, _, _) -> + ok; +assert_role(server_only, server, _, _) -> + ok; +assert_role(client_only, _, _, undefined) -> + ok; +assert_role(server_only, _, _, undefined) -> + ok; +assert_role(Type, _, Key, _) -> + throw({error, {option, Type, Key}}). + + validate_option(versions, Versions) -> validate_versions(Versions, Versions); validate_option(verify, Value) @@ -1913,6 +1976,13 @@ validate_option(cb_info, {V1, V2, V3, V4, V5}) when is_atom(V1), true; validate_option(cb_info, _) -> false; +validate_option(Opt, undefined = Value) -> + case lists:member(Opt, ?SSL_OPTIONS) of + true -> + Value; + false -> + throw({error, {options, {Opt, Value}}}) + end; validate_option(Opt, Value) -> throw({error, {options, {Opt, Value}}}). @@ -1957,26 +2027,6 @@ handle_signature_algorithms_option(Value, Version) when is_list(Value) handle_signature_algorithms_option(_, _Version) -> undefined. -handle_reuse_sessions_option(Key, Opts, client) -> - Value = proplists:get_value(Key, Opts, true), - validate_option(Key, Value), - Value; -handle_reuse_sessions_option(Key, Opts0, server) -> - Opts = proplists:delete({Key, save}, Opts0), - Value = proplists:get_value(Key, Opts, true), - validate_option(Key, Value), - Value. - -handle_reuse_session_option(Key, Opts, client) -> - Value = proplists:get_value(Key, Opts, undefined), - validate_option(Key, Value), - Value; -handle_reuse_session_option(Key, Opts, server) -> - ReuseSessionFun = fun(_, _, _, _) -> true end, - Value = proplists:get_value(Key, Opts, ReuseSessionFun), - validate_option(Key, Value), - Value. - validate_options([]) -> []; validate_options([{Opt, Value} | Tail]) -> @@ -2225,101 +2275,104 @@ assert_proplist([inet6 | Rest]) -> assert_proplist([Value | _]) -> throw({option_not_a_key_value_tuple, Value}). -new_ssl_options([], #ssl_options{} = Opts, _) -> +new_ssl_options([], #{} = Opts, _) -> Opts; -new_ssl_options([{verify_client_once, Value} | Rest], #ssl_options{} = Opts, RecordCB) -> - new_ssl_options(Rest, Opts#ssl_options{verify_client_once = - validate_option(verify_client_once, Value)}, RecordCB); -new_ssl_options([{depth, Value} | Rest], #ssl_options{} = Opts, RecordCB) -> - new_ssl_options(Rest, Opts#ssl_options{depth = validate_option(depth, Value)}, RecordCB); -new_ssl_options([{cert, Value} | Rest], #ssl_options{} = Opts, RecordCB) -> - new_ssl_options(Rest, Opts#ssl_options{cert = validate_option(cert, Value)}, RecordCB); -new_ssl_options([{certfile, Value} | Rest], #ssl_options{} = Opts, RecordCB) -> - new_ssl_options(Rest, Opts#ssl_options{certfile = validate_option(certfile, Value)}, RecordCB); -new_ssl_options([{key, Value} | Rest], #ssl_options{} = Opts, RecordCB) -> - new_ssl_options(Rest, Opts#ssl_options{key = validate_option(key, Value)}, RecordCB); -new_ssl_options([{keyfile, Value} | Rest], #ssl_options{} = Opts, RecordCB) -> - new_ssl_options(Rest, Opts#ssl_options{keyfile = validate_option(keyfile, Value)}, RecordCB); -new_ssl_options([{password, Value} | Rest], #ssl_options{} = Opts, RecordCB) -> - new_ssl_options(Rest, Opts#ssl_options{password = validate_option(password, Value)}, RecordCB); -new_ssl_options([{dh, Value} | Rest], #ssl_options{} = Opts, RecordCB) -> - new_ssl_options(Rest, Opts#ssl_options{dh = validate_option(dh, Value)}, RecordCB); -new_ssl_options([{dhfile, Value} | Rest], #ssl_options{} = Opts, RecordCB) -> - new_ssl_options(Rest, Opts#ssl_options{dhfile = validate_option(dhfile, Value)}, RecordCB); -new_ssl_options([{user_lookup_fun, Value} | Rest], #ssl_options{} = Opts, RecordCB) -> - new_ssl_options(Rest, Opts#ssl_options{user_lookup_fun = validate_option(user_lookup_fun, Value)}, RecordCB); -new_ssl_options([{psk_identity, Value} | Rest], #ssl_options{} = Opts, RecordCB) -> - new_ssl_options(Rest, Opts#ssl_options{psk_identity = validate_option(psk_identity, Value)}, RecordCB); -new_ssl_options([{srp_identity, Value} | Rest], #ssl_options{} = Opts, RecordCB) -> - new_ssl_options(Rest, Opts#ssl_options{srp_identity = validate_option(srp_identity, Value)}, RecordCB); -new_ssl_options([{ciphers, Value} | Rest], #ssl_options{versions = Versions} = Opts, RecordCB) -> +new_ssl_options([{verify_client_once, Value} | Rest], #{} = Opts, RecordCB) -> + new_ssl_options(Rest, Opts#{verify_client_once => + validate_option(verify_client_once, Value)}, RecordCB); +new_ssl_options([{depth, Value} | Rest], #{} = Opts, RecordCB) -> + new_ssl_options(Rest, Opts#{depth => validate_option(depth, Value)}, RecordCB); +new_ssl_options([{cert, Value} | Rest], #{} = Opts, RecordCB) -> + new_ssl_options(Rest, Opts#{cert => validate_option(cert, Value)}, RecordCB); +new_ssl_options([{certfile, Value} | Rest], #{} = Opts, RecordCB) -> + new_ssl_options(Rest, Opts#{certfile => validate_option(certfile, Value)}, RecordCB); +new_ssl_options([{key, Value} | Rest], #{} = Opts, RecordCB) -> + new_ssl_options(Rest, Opts#{key => validate_option(key, Value)}, RecordCB); +new_ssl_options([{keyfile, Value} | Rest], #{} = Opts, RecordCB) -> + new_ssl_options(Rest, Opts#{keyfile => validate_option(keyfile, Value)}, RecordCB); +new_ssl_options([{password, Value} | Rest], #{} = Opts, RecordCB) -> + new_ssl_options(Rest, Opts#{password => validate_option(password, Value)}, RecordCB); +new_ssl_options([{dh, Value} | Rest], #{} = Opts, RecordCB) -> + new_ssl_options(Rest, Opts#{dh => validate_option(dh, Value)}, RecordCB); +new_ssl_options([{dhfile, Value} | Rest], #{} = Opts, RecordCB) -> + new_ssl_options(Rest, Opts#{dhfile => validate_option(dhfile, Value)}, RecordCB); +new_ssl_options([{user_lookup_fun, Value} | Rest], #{} = Opts, RecordCB) -> + new_ssl_options(Rest, Opts#{user_lookup_fun => validate_option(user_lookup_fun, Value)}, RecordCB); +new_ssl_options([{psk_identity, Value} | Rest], #{} = Opts, RecordCB) -> + new_ssl_options(Rest, Opts#{psk_identity => validate_option(psk_identity, Value)}, RecordCB); +new_ssl_options([{srp_identity, Value} | Rest], #{} = Opts, RecordCB) -> + new_ssl_options(Rest, Opts#{srp_identity => validate_option(srp_identity, Value)}, RecordCB); +new_ssl_options([{ciphers, Value} | Rest], #{versions := Versions} = Opts, RecordCB) -> Ciphers = handle_cipher_option(Value, RecordCB:highest_protocol_version(Versions)), - new_ssl_options(Rest, - Opts#ssl_options{ciphers = Ciphers}, RecordCB); -new_ssl_options([{reuse_session, Value} | Rest], #ssl_options{} = Opts, RecordCB) -> - new_ssl_options(Rest, Opts#ssl_options{reuse_session = validate_option(reuse_session, Value)}, RecordCB); -new_ssl_options([{reuse_sessions, Value} | Rest], #ssl_options{} = Opts, RecordCB) -> - new_ssl_options(Rest, Opts#ssl_options{reuse_sessions = validate_option(reuse_sessions, Value)}, RecordCB); -new_ssl_options([{ssl_imp, _Value} | Rest], #ssl_options{} = Opts, RecordCB) -> %% Not used backwards compatibility + new_ssl_options(Rest, Opts#{ciphers => Ciphers}, RecordCB); +new_ssl_options([{reuse_session, Value} | Rest], #{} = Opts, RecordCB) -> + new_ssl_options(Rest, Opts#{reuse_session => validate_option(reuse_session, Value)}, RecordCB); +new_ssl_options([{reuse_sessions, Value} | Rest], #{} = Opts, RecordCB) -> + new_ssl_options(Rest, Opts#{reuse_sessions => validate_option(reuse_sessions, Value)}, RecordCB); +new_ssl_options([{ssl_imp, _Value} | Rest], #{} = Opts, RecordCB) -> %% Not used backwards compatibility new_ssl_options(Rest, Opts, RecordCB); -new_ssl_options([{renegotiate_at, Value} | Rest], #ssl_options{} = Opts, RecordCB) -> - new_ssl_options(Rest, Opts#ssl_options{ renegotiate_at = validate_option(renegotiate_at, Value)}, RecordCB); -new_ssl_options([{secure_renegotiate, Value} | Rest], #ssl_options{} = Opts, RecordCB) -> - new_ssl_options(Rest, Opts#ssl_options{secure_renegotiate = validate_option(secure_renegotiate, Value)}, RecordCB); -new_ssl_options([{client_renegotiation, Value} | Rest], #ssl_options{} = Opts, RecordCB) -> - new_ssl_options(Rest, Opts#ssl_options{client_renegotiation = validate_option(client_renegotiation, Value)}, RecordCB); -new_ssl_options([{hibernate_after, Value} | Rest], #ssl_options{} = Opts, RecordCB) -> - new_ssl_options(Rest, Opts#ssl_options{hibernate_after = validate_option(hibernate_after, Value)}, RecordCB); -new_ssl_options([{alpn_advertised_protocols, Value} | Rest], #ssl_options{} = Opts, RecordCB) -> - new_ssl_options(Rest, Opts#ssl_options{alpn_advertised_protocols = validate_option(alpn_advertised_protocols, Value)}, RecordCB); -new_ssl_options([{alpn_preferred_protocols, Value} | Rest], #ssl_options{} = Opts, RecordCB) -> - new_ssl_options(Rest, Opts#ssl_options{alpn_preferred_protocols = validate_option(alpn_preferred_protocols, Value)}, RecordCB); -new_ssl_options([{next_protocols_advertised, Value} | Rest], #ssl_options{} = Opts, RecordCB) -> - new_ssl_options(Rest, Opts#ssl_options{next_protocols_advertised = validate_option(next_protocols_advertised, Value)}, RecordCB); -new_ssl_options([{client_preferred_next_protocols, Value} | Rest], #ssl_options{} = Opts, RecordCB) -> - new_ssl_options(Rest, Opts#ssl_options{next_protocol_selector = - make_next_protocol_selector(validate_option(client_preferred_next_protocols, Value))}, RecordCB); -new_ssl_options([{log_alert, Value} | Rest], #ssl_options{} = Opts, RecordCB) -> - new_ssl_options(Rest, Opts#ssl_options{log_level = validate_option(log_alert, Value)}, RecordCB); -new_ssl_options([{log_level, Value} | Rest], #ssl_options{} = Opts, RecordCB) -> - new_ssl_options(Rest, Opts#ssl_options{log_level = validate_option(log_level, Value)}, RecordCB); -new_ssl_options([{server_name_indication, Value} | Rest], #ssl_options{} = Opts, RecordCB) -> - new_ssl_options(Rest, Opts#ssl_options{server_name_indication = validate_option(server_name_indication, Value)}, RecordCB); -new_ssl_options([{honor_cipher_order, Value} | Rest], #ssl_options{} = Opts, RecordCB) -> - new_ssl_options(Rest, Opts#ssl_options{honor_cipher_order = validate_option(honor_cipher_order, Value)}, RecordCB); -new_ssl_options([{honor_ecc_order, Value} | Rest], #ssl_options{} = Opts, RecordCB) -> - new_ssl_options(Rest, Opts#ssl_options{honor_ecc_order = validate_option(honor_ecc_order, Value)}, RecordCB); -new_ssl_options([{eccs, Value} | Rest], #ssl_options{} = Opts, RecordCB) -> +new_ssl_options([{renegotiate_at, Value} | Rest], #{} = Opts, RecordCB) -> + new_ssl_options(Rest, Opts#{renegotiate_at => validate_option(renegotiate_at, Value)}, RecordCB); +new_ssl_options([{secure_renegotiate, Value} | Rest], #{} = Opts, RecordCB) -> + new_ssl_options(Rest, Opts#{secure_renegotiate => validate_option(secure_renegotiate, Value)}, RecordCB); +new_ssl_options([{client_renegotiation, Value} | Rest], #{} = Opts, RecordCB) -> + new_ssl_options(Rest, Opts#{client_renegotiation => validate_option(client_renegotiation, Value)}, RecordCB); +new_ssl_options([{hibernate_after, Value} | Rest], #{} = Opts, RecordCB) -> + new_ssl_options(Rest, Opts#{hibernate_after => validate_option(hibernate_after, Value)}, RecordCB); +new_ssl_options([{alpn_advertised_protocols, Value} | Rest], #{} = Opts, RecordCB) -> + new_ssl_options(Rest, Opts#{alpn_advertised_protocols => validate_option(alpn_advertised_protocols, Value)}, + RecordCB); +new_ssl_options([{alpn_preferred_protocols, Value} | Rest], #{} = Opts, RecordCB) -> + new_ssl_options(Rest, Opts#{alpn_preferred_protocols => validate_option(alpn_preferred_protocols, Value)}, + RecordCB); +new_ssl_options([{next_protocols_advertised, Value} | Rest], #{} = Opts, RecordCB) -> + new_ssl_options(Rest, Opts#{next_protocols_advertised => validate_option(next_protocols_advertised, Value)}, + RecordCB); +new_ssl_options([{client_preferred_next_protocols, Value} | Rest], #{} = Opts, RecordCB) -> new_ssl_options(Rest, - Opts#ssl_options{eccs = - handle_eccs_option(Value, RecordCB:highest_protocol_version()) + Opts#{next_protocol_selector => + make_next_protocol_selector(validate_option(client_preferred_next_protocols, Value))}, + RecordCB); +new_ssl_options([{log_alert, Value} | Rest], #{} = Opts, RecordCB) -> + new_ssl_options(Rest, Opts#{log_level => validate_option(log_alert, Value)}, RecordCB); +new_ssl_options([{log_level, Value} | Rest], #{} = Opts, RecordCB) -> + new_ssl_options(Rest, Opts#{log_level => validate_option(log_level, Value)}, RecordCB); +new_ssl_options([{server_name_indication, Value} | Rest], #{} = Opts, RecordCB) -> + new_ssl_options(Rest, Opts#{server_name_indication => validate_option(server_name_indication, Value)}, RecordCB); +new_ssl_options([{honor_cipher_order, Value} | Rest], #{} = Opts, RecordCB) -> + new_ssl_options(Rest, Opts#{honor_cipher_order => validate_option(honor_cipher_order, Value)}, RecordCB); +new_ssl_options([{honor_ecc_order, Value} | Rest], #{} = Opts, RecordCB) -> + new_ssl_options(Rest, Opts#{honor_ecc_order => validate_option(honor_ecc_order, Value)}, RecordCB); +new_ssl_options([{eccs, Value} | Rest], #{} = Opts, RecordCB) -> + new_ssl_options(Rest, + Opts#{eccs => handle_eccs_option(Value, RecordCB:highest_protocol_version()) }, RecordCB); -new_ssl_options([{supported_groups, Value} | Rest], #ssl_options{} = Opts, RecordCB) -> +new_ssl_options([{supported_groups, Value} | Rest], #{} = Opts, RecordCB) -> new_ssl_options(Rest, - Opts#ssl_options{supported_groups = - handle_supported_groups_option(Value, RecordCB:highest_protocol_version()) + Opts#{supported_groups => + handle_supported_groups_option(Value, RecordCB:highest_protocol_version()) }, RecordCB); -new_ssl_options([{signature_algs, Value} | Rest], #ssl_options{} = Opts, RecordCB) -> - new_ssl_options(Rest, - Opts#ssl_options{signature_algs = - handle_hashsigns_option(Value, - tls_version(RecordCB:highest_protocol_version()))}, +new_ssl_options([{signature_algs, Value} | Rest], #{} = Opts, RecordCB) -> + new_ssl_options(Rest, + Opts#{signature_algs => + handle_hashsigns_option(Value, + tls_version(RecordCB:highest_protocol_version()))}, RecordCB); -new_ssl_options([{signature_algs_cert, Value} | Rest], #ssl_options{} = Opts, RecordCB) -> +new_ssl_options([{signature_algs_cert, Value} | Rest], #{} = Opts, RecordCB) -> new_ssl_options( Rest, - Opts#ssl_options{signature_algs_cert = - handle_signature_algorithms_option( - Value, - tls_version(RecordCB:highest_protocol_version()))}, + Opts#{signature_algs_cert => + handle_signature_algorithms_option( + Value, + tls_version(RecordCB:highest_protocol_version()))}, RecordCB); -new_ssl_options([{protocol, dtls = Value} | Rest], #ssl_options{} = Opts, dtls_record = RecordCB) -> - new_ssl_options(Rest, Opts#ssl_options{protocol = Value}, RecordCB); -new_ssl_options([{protocol, tls = Value} | Rest], #ssl_options{} = Opts, tls_record = RecordCB) -> - new_ssl_options(Rest, Opts#ssl_options{protocol = Value}, RecordCB); -new_ssl_options([{Key, Value} | _Rest], #ssl_options{}, _) -> +new_ssl_options([{protocol, dtls = Value} | Rest], #{} = Opts, dtls_record = RecordCB) -> + new_ssl_options(Rest, Opts#{protocol => Value}, RecordCB); +new_ssl_options([{protocol, tls = Value} | Rest], #{} = Opts, tls_record = RecordCB) -> + new_ssl_options(Rest, Opts#{protocol => Value}, RecordCB); +new_ssl_options([{Key, Value} | _Rest], #{}, _) -> throw({error, {options, {Key, Value}}}). @@ -2433,3 +2486,10 @@ add_filter(undefined, Filters) -> Filters; add_filter(Filter, Filters) -> [Filter | Filters]. + +%% Convert the record #ssl_options{} into a map for internal usage +options_to_map(Options) -> + Fields = record_info(fields, ssl_options), + [_Tag| Values] = tuple_to_list(Options), + L = lists:zip(Fields, Values), + maps:from_list(L). diff --git a/lib/ssl/src/ssl_config.erl b/lib/ssl/src/ssl_config.erl index 1e6dab9276..7d1e80c1cc 100644 --- a/lib/ssl/src/ssl_config.erl +++ b/lib/ssl/src/ssl_config.erl @@ -28,16 +28,20 @@ -export([init/2]). -init(SslOpts, Role) -> +init(#{erl_dist := ErlDist, + key := Key, + keyfile := KeyFile, + password := Password, + dh := DH, + dhfile := DHFile} = SslOpts, Role) -> - init_manager_name(SslOpts#ssl_options.erl_dist), + init_manager_name(ErlDist), {ok, #{pem_cache := PemCache} = Config} = init_certificates(SslOpts, Role), PrivateKey = - init_private_key(PemCache, SslOpts#ssl_options.key, SslOpts#ssl_options.keyfile, - SslOpts#ssl_options.password, Role), - DHParams = init_diffie_hellman(PemCache, SslOpts#ssl_options.dh, SslOpts#ssl_options.dhfile, Role), + init_private_key(PemCache, Key, KeyFile, Password, Role), + DHParams = init_diffie_hellman(PemCache, DH, DHFile, Role), {ok, Config#{private_key => PrivateKey, dh_params => DHParams}}. init_manager_name(false) -> @@ -47,12 +51,12 @@ init_manager_name(true) -> put(ssl_manager, ssl_manager:name(dist)), put(ssl_pem_cache, ssl_pem_cache:name(dist)). -init_certificates(#ssl_options{cacerts = CaCerts, - cacertfile = CACertFile, - certfile = CertFile, - cert = Cert, - crl_cache = CRLCache - }, Role) -> +init_certificates(#{cacerts := CaCerts, + cacertfile := CACertFile, + certfile := CertFile, + cert := Cert, + crl_cache := CRLCache + }, Role) -> {ok, Config} = try Certs = case CaCerts of diff --git a/lib/ssl/src/ssl_connection.erl b/lib/ssl/src/ssl_connection.erl index 2483509228..6789c2c23f 100644 --- a/lib/ssl/src/ssl_connection.erl +++ b/lib/ssl/src/ssl_connection.erl @@ -80,7 +80,7 @@ -spec connect(tls_connection | dtls_connection, ssl:host(), inet:port_number(), port() | {tuple(), port()}, %% TLS | DTLS - {#ssl_options{}, #socket_options{}, + {ssl_options(), #socket_options{}, %% Tracker only needed on server side undefined}, pid(), tuple(), timeout()) -> @@ -98,7 +98,7 @@ connect(Connection, Host, Port, Socket, Options, User, CbInfo, Timeout) -> %%-------------------------------------------------------------------- -spec handshake(tls_connection | dtls_connection, inet:port_number(), port(), - {#ssl_options{}, #socket_options{}, undefined | pid()}, + {ssl_options(), #socket_options{}, undefined | pid()}, pid(), tuple(), timeout()) -> {ok, #sslsocket{}} | {error, reason()}. %% @@ -130,7 +130,7 @@ handshake(#sslsocket{pid = [Pid|_]} = Socket, Timeout) -> end. %%-------------------------------------------------------------------- --spec handshake(#sslsocket{}, {#ssl_options{},#socket_options{}}, timeout()) -> +-spec handshake(#sslsocket{}, {ssl_options(),#socket_options{}}, timeout()) -> {ok, #sslsocket{}} | {ok, #sslsocket{}, map()} | {error, reason()}. %% %% Description: Starts ssl handshake with some new options @@ -331,7 +331,7 @@ prf(ConnectionPid, Secret, Label, Seed, WantedLength) -> handle_own_alert(Alert0, _, StateName, #state{static_env = #static_env{role = Role, protocol_cb = Connection}, - ssl_options = SslOpts} = State) -> + ssl_options = #{log_level := LogLevel}} = State) -> try %% Try to tell the other side send_alert(Alert0, StateName, State) catch _:_ -> %% Can crash if we are in a uninitialized state @@ -339,7 +339,7 @@ handle_own_alert(Alert0, _, StateName, end, try %% Try to tell the local user Alert = Alert0#alert{role = Role}, - log_alert(SslOpts#ssl_options.log_level, Role, Connection:protocol_name(), StateName, Alert), + log_alert(LogLevel, Role, Connection:protocol_name(), StateName, Alert), handle_normal_shutdown(Alert,StateName, State) catch _:_ -> ok @@ -376,13 +376,13 @@ handle_alert(#alert{level = ?FATAL} = Alert0, StateName, transport_cb = Transport, protocol_cb = Connection}, connection_env = #connection_env{user_application = {_Mon, Pid}}, - ssl_options = SslOpts, + ssl_options = #{log_level := LogLevel}, start_or_recv_from = From, session = Session, socket_options = Opts} = State) -> invalidate_session(Role, Host, Port, Session), Alert = Alert0#alert{role = opposite_role(Role)}, - log_alert(SslOpts#ssl_options.log_level, Role, Connection:protocol_name(), + log_alert(LogLevel, Role, Connection:protocol_name(), StateName, Alert), Pids = Connection:pids(State), alert_user(Pids, Transport, Tracker, Socket, StateName, Opts, Pid, From, Alert, Role, StateName, Connection), @@ -399,9 +399,9 @@ handle_alert(#alert{level = ?WARNING, description = ?NO_RENEGOTIATION} = Alert0, #state{static_env = #static_env{role = Role, protocol_cb = Connection}, handshake_env = #handshake_env{renegotiation = {true, internal}}, - ssl_options = SslOpts} = State) -> + ssl_options = #{log_level := LogLevel}} = State) -> Alert = Alert0#alert{role = opposite_role(Role)}, - log_alert(SslOpts#ssl_options.log_level, Role, + log_alert(LogLevel, Role, Connection:protocol_name(), StateName, Alert), handle_normal_shutdown(Alert, StateName, State), {stop,{shutdown, peer_close}, State}; @@ -410,9 +410,9 @@ handle_alert(#alert{level = ?WARNING, description = ?NO_RENEGOTIATION} = Alert, #state{static_env = #static_env{role = Role, protocol_cb = Connection}, handshake_env = #handshake_env{renegotiation = {true, From}} = HsEnv, - ssl_options = SslOpts + ssl_options = #{log_level := LogLevel} } = State0) -> - log_alert(SslOpts#ssl_options.log_level, Role, + log_alert(LogLevel, Role, Connection:protocol_name(), StateName, Alert#alert{role = opposite_role(Role)}), gen_statem:reply(From, {error, renegotiation_rejected}), State = Connection:reinit_handshake_data(State0), @@ -422,9 +422,9 @@ handle_alert(#alert{level = ?WARNING, description = ?NO_RENEGOTIATION} = Alert, #state{static_env = #static_env{role = Role, protocol_cb = Connection}, handshake_env = #handshake_env{renegotiation = {true, From}} = HsEnv, - ssl_options = SslOpts + ssl_options = #{log_level := LogLevel} } = State0) -> - log_alert(SslOpts#ssl_options.log_level, Role, + log_alert(LogLevel, Role, Connection:protocol_name(), StateName, Alert#alert{role = opposite_role(Role)}), gen_statem:reply(From, {error, renegotiation_rejected}), %% Go back to connection! @@ -435,8 +435,8 @@ handle_alert(#alert{level = ?WARNING, description = ?NO_RENEGOTIATION} = Alert, handle_alert(#alert{level = ?WARNING} = Alert, StateName, #state{static_env = #static_env{role = Role, protocol_cb = Connection}, - ssl_options = SslOpts} = State) -> - log_alert(SslOpts#ssl_options.log_level, Role, + ssl_options = #{log_level := LogLevel}} = State) -> + log_alert(LogLevel, Role, Connection:protocol_name(), StateName, Alert#alert{role = opposite_role(Role)}), Connection:next_event(StateName, no_record, State). @@ -773,7 +773,7 @@ handle_session(#server_hello{cipher_suite = CipherSuite, end. %%-------------------------------------------------------------------- --spec ssl_config(#ssl_options{}, client | server, #state{}) -> #state{}. +-spec ssl_config(ssl_options(), client | server, #state{}) -> #state{}. %%-------------------------------------------------------------------- ssl_config(Opts, Role, #state{static_env = InitStatEnv0, handshake_env = HsEnv, @@ -870,7 +870,7 @@ user_hello({call, From}, {handshake_continue, NewOptions, Timeout}, #state{static_env = #static_env{role = Role}, handshake_env = #handshake_env{hello = Hello}, ssl_options = Options0} = State0, _Connection) -> - Options = ssl:handle_options(NewOptions, Options0#ssl_options{handshake = full}), + Options = ssl:handle_options(NewOptions, Options0#{handshake => full}), State = ssl_config(Options, Role, State0), {next_state, hello, State#state{start_or_recv_from = From}, [{next_event, internal, Hello}, {{timeout, handshake}, Timeout, close}]}; @@ -962,21 +962,21 @@ certify(info, Msg, State, _) -> certify(internal, #certificate{asn1_certificates = []}, #state{static_env = #static_env{role = server}, connection_env = #connection_env{negotiated_version = Version}, - ssl_options = #ssl_options{verify = verify_peer, - fail_if_no_peer_cert = true}} = + ssl_options = #{verify := verify_peer, + fail_if_no_peer_cert := true}} = State, _) -> Alert = ?ALERT_REC(?FATAL,?HANDSHAKE_FAILURE), handle_own_alert(Alert, Version, ?FUNCTION_NAME, State); certify(internal, #certificate{asn1_certificates = []}, #state{static_env = #static_env{role = server}, - ssl_options = #ssl_options{verify = verify_peer, - fail_if_no_peer_cert = false}} = + ssl_options = #{verify := verify_peer, + fail_if_no_peer_cert := false}} = State0, Connection) -> 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 = #ssl_options{verify = verify_none}} = + ssl_options = #{verify := verify_none}} = State, _) -> Alert = ?ALERT_REC(?FATAL,?UNEXPECTED_MESSAGE, unrequested_certificate), handle_own_alert(Alert, Version, ?FUNCTION_NAME, State); @@ -1067,7 +1067,7 @@ certify(internal, #certificate_request{} = CertRequest, handshake_env = HsEnv, connection_env = #connection_env{negotiated_version = Version}, session = #session{own_certificate = Cert}, - ssl_options = #ssl_options{signature_algs = SupportedHashSigns}} = State, Connection) -> + ssl_options = #{signature_algs := SupportedHashSigns}} = State, Connection) -> case ssl_handshake:select_hashsign(CertRequest, Cert, SupportedHashSigns, ssl:tls_version(Version)) of #alert {} = Alert -> @@ -1085,7 +1085,7 @@ certify(internal, #server_hello_done{}, handshake_env = #handshake_env{kex_algorithm = KexAlg, premaster_secret = undefined, server_psk_identity = PSKIdentity} = HsEnv, - ssl_options = #ssl_options{user_lookup_fun = PSKLookup}} = State0, Connection) + ssl_options = #{user_lookup_fun := PSKLookup}} = State0, Connection) when KexAlg == psk -> case ssl_handshake:premaster_secret({KexAlg, PSKIdentity}, PSKLookup) of #alert{} = Alert -> @@ -1103,7 +1103,7 @@ certify(internal, #server_hello_done{}, premaster_secret = undefined, server_psk_identity = PSKIdentity} = HsEnv, session = #session{master_secret = undefined}, - ssl_options = #ssl_options{user_lookup_fun = PSKLookup}} = State0, Connection) + ssl_options = #{user_lookup_fun := PSKLookup}} = State0, Connection) when KexAlg == rsa_psk -> Rand = ssl_cipher:random_bytes(?NUM_OF_PREMASTERSECRET_BYTES-2), RSAPremasterSecret = <<?BYTE(Major), ?BYTE(Minor), Rand/binary>>, @@ -1152,7 +1152,7 @@ certify(internal, #server_hello_done{}, certify(internal = Type, #client_key_exchange{} = Msg, #state{static_env = #static_env{role = server}, client_certificate_requested = true, - ssl_options = #ssl_options{fail_if_no_peer_cert = true}} = State, + ssl_options = #{fail_if_no_peer_cert := true}} = State, Connection) -> %% We expect a certificate here handle_common_event(Type, Msg, ?FUNCTION_NAME, State, Connection); @@ -1297,7 +1297,7 @@ connection(cast, {internal_renegotiate, WriteState}, #state{static_env = #static Connection:renegotiate(State#state{handshake_env = HsEnv#handshake_env{renegotiation = {true, internal}}, connection_states = ConnectionStates#{current_write => WriteState}}, []); connection(cast, {dist_handshake_complete, DHandle}, - #state{ssl_options = #ssl_options{erl_dist = true}, + #state{ssl_options = #{erl_dist := true}, connection_env = CEnv, socket_options = SockOpts} = State0, Connection) -> process_flag(priority, normal), @@ -1506,7 +1506,7 @@ handle_info({ErrorTag, Socket, Reason}, StateName, #state{static_env = #static_e handle_info({'DOWN', MonitorRef, _, _, Reason}, _, #state{connection_env = #connection_env{user_application = {MonitorRef, _Pid}}, - ssl_options = #ssl_options{erl_dist = true}}) -> + ssl_options = #{erl_dist := true}}) -> {stop, {shutdown, Reason}}; handle_info({'DOWN', MonitorRef, _, _, _}, _, #state{connection_env = #connection_env{user_application = {MonitorRef, _Pid}}}) -> @@ -1572,7 +1572,7 @@ terminate(Reason, connection, #state{static_env = #static_env{ transport_cb = Transport, socket = Socket}, connection_states = ConnectionStates, - ssl_options = #ssl_options{padding_check = Check} + ssl_options = #{padding_check := Check} } = State) -> handle_trusted_certs_db(State), Alert = terminate_alert(Reason), @@ -1590,13 +1590,13 @@ format_status(normal, [_, StateName, State]) -> [{data, [{"State", {StateName, State}}]}]; format_status(terminate, [_, StateName, State]) -> SslOptions = (State#state.ssl_options), - NewOptions = SslOptions#ssl_options{password = ?SECRET_PRINTOUT, - cert = ?SECRET_PRINTOUT, - cacerts = ?SECRET_PRINTOUT, - key = ?SECRET_PRINTOUT, - dh = ?SECRET_PRINTOUT, - psk_identity = ?SECRET_PRINTOUT, - srp_identity = ?SECRET_PRINTOUT}, + NewOptions = SslOptions#{password => ?SECRET_PRINTOUT, + cert => ?SECRET_PRINTOUT, + cacerts => ?SECRET_PRINTOUT, + key => ?SECRET_PRINTOUT, + dh => ?SECRET_PRINTOUT, + psk_identity => ?SECRET_PRINTOUT, + srp_identity => ?SECRET_PRINTOUT}, [{data, [{"State", {StateName, State#state{connection_states = ?SECRET_PRINTOUT, protocol_buffers = ?SECRET_PRINTOUT, user_data_buffer = ?SECRET_PRINTOUT, @@ -1651,7 +1651,7 @@ do_server_hello(Type, #{next_protocol_negotiation := NextProtocols} = handshake_env = HsEnv, session = #session{session_id = SessId}, connection_states = ConnectionStates0, - ssl_options = #ssl_options{versions = [HighestVersion|_]}} + ssl_options = #{versions := [HighestVersion|_]}} = State0, Connection) when is_atom(Type) -> %% TLS 1.3 - Section 4.1.3 %% Override server random values for TLS 1.3 downgrade protection mechanism. @@ -1885,7 +1885,7 @@ certify_client_key_exchange(#client_ec_diffie_hellman_public{dh_public = ClientP calculate_master_secret(PremasterSecret, State, Connection, certify, cipher); certify_client_key_exchange(#client_psk_identity{} = ClientKey, #state{ssl_options = - #ssl_options{user_lookup_fun = PSKLookup}} = State0, + #{user_lookup_fun := PSKLookup}} = State0, Connection) -> PremasterSecret = ssl_handshake:premaster_secret(ClientKey, PSKLookup), calculate_master_secret(PremasterSecret, State0, Connection, certify, cipher); @@ -1893,7 +1893,7 @@ certify_client_key_exchange(#client_dhe_psk_identity{} = ClientKey, #state{handshake_env = #handshake_env{diffie_hellman_params = #'DHParameter'{} = Params, kex_keys = {_, ServerDhPrivateKey}}, ssl_options = - #ssl_options{user_lookup_fun = PSKLookup}} = State0, + #{user_lookup_fun := PSKLookup}} = State0, Connection) -> PremasterSecret = ssl_handshake:premaster_secret(ClientKey, ServerDhPrivateKey, Params, PSKLookup), @@ -1901,7 +1901,7 @@ certify_client_key_exchange(#client_dhe_psk_identity{} = ClientKey, certify_client_key_exchange(#client_ecdhe_psk_identity{} = ClientKey, #state{handshake_env = #handshake_env{kex_keys = ServerEcDhPrivateKey}, ssl_options = - #ssl_options{user_lookup_fun = PSKLookup}} = State, + #{user_lookup_fun := PSKLookup}} = State, Connection) -> PremasterSecret = ssl_handshake:premaster_secret(ClientKey, ServerEcDhPrivateKey, PSKLookup), @@ -1909,7 +1909,7 @@ certify_client_key_exchange(#client_ecdhe_psk_identity{} = ClientKey, certify_client_key_exchange(#client_rsa_psk_identity{} = ClientKey, #state{connection_env = #connection_env{private_key = Key}, ssl_options = - #ssl_options{user_lookup_fun = PSKLookup}} = State0, + #{user_lookup_fun := PSKLookup}} = State0, Connection) -> PremasterSecret = ssl_handshake:premaster_secret(ClientKey, Key, PSKLookup), calculate_master_secret(PremasterSecret, State0, Connection, certify, cipher); @@ -1995,10 +1995,10 @@ key_exchange(#state{static_env = #static_env{role = server}, State#state{handshake_env = HsEnv#handshake_env{kex_keys = ECDHKeys}}; key_exchange(#state{static_env = #static_env{role = server}, handshake_env = #handshake_env{kex_algorithm = psk}, - ssl_options = #ssl_options{psk_identity = undefined}} = State, _) -> + ssl_options = #{psk_identity := undefined}} = State, _) -> State; key_exchange(#state{static_env = #static_env{role = server}, - ssl_options = #ssl_options{psk_identity = PskIdentityHint}, + ssl_options = #{psk_identity := PskIdentityHint}, handshake_env = #handshake_env{kex_algorithm = psk, hashsign_algorithm = HashSignAlgo}, connection_env = #connection_env{negotiated_version = Version, @@ -2015,7 +2015,7 @@ key_exchange(#state{static_env = #static_env{role = server}, PrivateKey}), Connection:queue_handshake(Msg, State0); key_exchange(#state{static_env = #static_env{role = server}, - ssl_options = #ssl_options{psk_identity = PskIdentityHint}, + ssl_options = #{psk_identity := PskIdentityHint}, handshake_env = #handshake_env{kex_algorithm = dhe_psk, diffie_hellman_params = #'DHParameter'{} = Params, hashsign_algorithm = HashSignAlgo}, @@ -2037,7 +2037,7 @@ key_exchange(#state{static_env = #static_env{role = server}, #state{handshake_env = HsEnv} = State = Connection:queue_handshake(Msg, State0), State#state{handshake_env = HsEnv#handshake_env{kex_keys = DHKeys}}; key_exchange(#state{static_env = #static_env{role = server}, - ssl_options = #ssl_options{psk_identity = PskIdentityHint}, + ssl_options = #{psk_identity := PskIdentityHint}, handshake_env = #handshake_env{kex_algorithm = ecdhe_psk, hashsign_algorithm = HashSignAlgo}, connection_env = #connection_env{negotiated_version = Version, @@ -2060,10 +2060,10 @@ key_exchange(#state{static_env = #static_env{role = server}, State#state{handshake_env = HsEnv#handshake_env{kex_keys = ECDHKeys}}; key_exchange(#state{static_env = #static_env{role = server}, handshake_env = #handshake_env{kex_algorithm = rsa_psk}, - ssl_options = #ssl_options{psk_identity = undefined}} = State, _) -> + ssl_options = #{psk_identity := undefined}} = State, _) -> State; key_exchange(#state{static_env = #static_env{role = server}, - ssl_options = #ssl_options{psk_identity = PskIdentityHint}, + ssl_options = #{psk_identity := PskIdentityHint}, handshake_env = #handshake_env{kex_algorithm = rsa_psk, hashsign_algorithm = HashSignAlgo}, connection_env = #connection_env{negotiated_version = Version, @@ -2081,7 +2081,7 @@ key_exchange(#state{static_env = #static_env{role = server}, PrivateKey}), Connection:queue_handshake(Msg, State0); key_exchange(#state{static_env = #static_env{role = server}, - ssl_options = #ssl_options{user_lookup_fun = LookupFun}, + ssl_options = #{user_lookup_fun := LookupFun}, handshake_env = #handshake_env{kex_algorithm = KexAlg, hashsign_algorithm = HashSignAlgo}, connection_env = #connection_env{negotiated_version = Version, @@ -2146,28 +2146,28 @@ key_exchange(#state{static_env = #static_env{role = client}, key_exchange(#state{static_env = #static_env{role = client}, handshake_env = #handshake_env{kex_algorithm = psk}, connection_env = #connection_env{negotiated_version = Version}, - ssl_options = SslOpts} = State0, Connection) -> + ssl_options = #{psk_identity := PSKIdentity}} = State0, Connection) -> Msg = ssl_handshake:key_exchange(client, ssl:tls_version(Version), - {psk, SslOpts#ssl_options.psk_identity}), + {psk, PSKIdentity}), Connection:queue_handshake(Msg, State0); key_exchange(#state{static_env = #static_env{role = client}, handshake_env = #handshake_env{kex_algorithm = dhe_psk, kex_keys = {DhPubKey, _}}, connection_env = #connection_env{negotiated_version = Version}, - ssl_options = SslOpts} = State0, Connection) -> + ssl_options = #{psk_identity := PSKIdentity}} = State0, Connection) -> Msg = ssl_handshake:key_exchange(client, ssl:tls_version(Version), {dhe_psk, - SslOpts#ssl_options.psk_identity, DhPubKey}), + PSKIdentity, DhPubKey}), Connection:queue_handshake(Msg, State0); key_exchange(#state{static_env = #static_env{role = client}, handshake_env = #handshake_env{kex_algorithm = ecdhe_psk, kex_keys = ECDHKeys}, connection_env = #connection_env{negotiated_version = Version}, - ssl_options = SslOpts} = State0, Connection) -> + ssl_options = #{psk_identity := PSKIdentity}} = State0, Connection) -> Msg = ssl_handshake:key_exchange(client, ssl:tls_version(Version), {ecdhe_psk, - SslOpts#ssl_options.psk_identity, ECDHKeys}), + PSKIdentity, ECDHKeys}), Connection:queue_handshake(Msg, State0); key_exchange(#state{static_env = #static_env{role = client}, @@ -2175,9 +2175,9 @@ key_exchange(#state{static_env = #static_env{role = client}, public_key_info = PublicKeyInfo, premaster_secret = PremasterSecret}, connection_env = #connection_env{negotiated_version = Version}, - ssl_options = SslOpts} + ssl_options = #{psk_identity := PSKIdentity}} = State0, Connection) -> - Msg = rsa_psk_key_exchange(ssl:tls_version(Version), SslOpts#ssl_options.psk_identity, + Msg = rsa_psk_key_exchange(ssl:tls_version(Version), PSKIdentity, PremasterSecret, PublicKeyInfo), Connection:queue_handshake(Msg, State0); key_exchange(#state{static_env = #static_env{role = client}, @@ -2239,8 +2239,8 @@ request_client_cert(#state{handshake_env = #handshake_env{kex_algorithm = Alg}} request_client_cert(#state{static_env = #static_env{cert_db = CertDbHandle, cert_db_ref = CertDbRef}, connection_env = #connection_env{negotiated_version = Version}, - ssl_options = #ssl_options{verify = verify_peer, - signature_algs = SupportedHashSigns}, + ssl_options = #{verify := verify_peer, + signature_algs := SupportedHashSigns}, connection_states = ConnectionStates0} = State0, Connection) -> #{security_parameters := #security_parameters{cipher_suite = CipherSuite}} = @@ -2253,7 +2253,7 @@ request_client_cert(#state{static_env = #static_env{cert_db = CertDbHandle, State = Connection:queue_handshake(Msg, State0), State#state{client_certificate_requested = true}; -request_client_cert(#state{ssl_options = #ssl_options{verify = verify_none}} = +request_client_cert(#state{ssl_options = #{verify := verify_none}} = State, _) -> State. @@ -2353,7 +2353,7 @@ calculate_secret(#server_psk_params{ calculate_secret(#server_dhe_psk_params{ dh_params = #server_dh_params{dh_p = Prime, dh_g = Base}} = ServerKey, #state{handshake_env = HsEnv, - ssl_options = #ssl_options{user_lookup_fun = PSKLookup}} = + ssl_options = #{user_lookup_fun := PSKLookup}} = State, Connection) -> Keys = {_, PrivateDhKey} = crypto:generate_key(dh, [Prime, Base]), @@ -2363,7 +2363,7 @@ calculate_secret(#server_dhe_psk_params{ calculate_secret(#server_ecdhe_psk_params{ dh_params = #server_ecdh_params{curve = ECCurve}} = ServerKey, - #state{ssl_options = #ssl_options{user_lookup_fun = PSKLookup}} = + #state{ssl_options = #{user_lookup_fun := PSKLookup}} = #state{handshake_env = HsEnv, session = Session} = State, Connection) -> ECDHKeys = public_key:generate_key(ECCurve), @@ -2376,7 +2376,7 @@ calculate_secret(#server_ecdhe_psk_params{ calculate_secret(#server_srp_params{srp_n = Prime, srp_g = Generator} = ServerKey, #state{handshake_env = HsEnv, - ssl_options = #ssl_options{srp_identity = SRPId}} = State, + ssl_options = #{srp_identity := SRPId}} = State, Connection) -> Keys = generate_srp_client_keys(Generator, Prime, 0), PremasterSecret = ssl_handshake:premaster_secret(ServerKey, Keys, SRPId), @@ -2610,7 +2610,7 @@ set_socket_opts(ConnectionCb, Transport, Socket, [Opt | Opts], SockOpts, Other) hibernate_after(connection = StateName, - #state{ssl_options=#ssl_options{hibernate_after = HibernateAfter}} = State, + #state{ssl_options= #{hibernate_after := HibernateAfter}} = State, Actions) -> {next_state, StateName, State, [{timeout, HibernateAfter, hibernate} | Actions]}; hibernate_after(StateName, State, Actions) -> @@ -2626,12 +2626,12 @@ terminate_alert(_) -> ?ALERT_REC(?FATAL, ?INTERNAL_ERROR). handle_trusted_certs_db(#state{ssl_options = - #ssl_options{cacertfile = <<>>, cacerts = []}}) -> + #{cacertfile := <<>>, cacerts := []}}) -> %% No trusted certs specified ok; handle_trusted_certs_db(#state{static_env = #static_env{cert_db_ref = Ref, cert_db = CertDb}, - ssl_options = #ssl_options{cacertfile = <<>>}}) when CertDb =/= undefined -> + ssl_options = #{cacertfile := <<>>}}) when CertDb =/= undefined -> %% Certs provided as DER directly can not be shared %% with other connections and it is safe to delete them when the connection ends. ssl_pkix_db:remove_trusted_certs(Ref, CertDb); @@ -2641,7 +2641,7 @@ handle_trusted_certs_db(#state{static_env = #static_env{file_ref_db = undefined} ok; handle_trusted_certs_db(#state{static_env = #static_env{cert_db_ref = Ref, file_ref_db = RefDb}, - ssl_options = #ssl_options{cacertfile = File}}) -> + ssl_options = #{cacertfile := File}}) -> case ssl_pkix_db:ref_count(Ref, RefDb, -1) of 0 -> ssl_manager:clean_cert_db(Ref, File); @@ -2678,11 +2678,11 @@ session_handle_params(#server_ecdh_params{curve = ECCurve}, Session) -> session_handle_params(_, Session) -> Session. -handle_session(Role = server, #ssl_options{reuse_sessions = true} = SslOpts, +handle_session(Role = server, #{reuse_sessions := true} = SslOpts, Host, Port, Session0) -> register_session(Role, host_id(Role, Host, SslOpts), Port, Session0, true); -handle_session(Role = client, #ssl_options{verify = verify_peer, - reuse_sessions = Reuse} = SslOpts, +handle_session(Role = client, #{verify := verify_peer, + reuse_sessions := Reuse} = SslOpts, Host, Port, Session0) when Reuse =/= false -> register_session(Role, host_id(Role, Host, SslOpts), Port, Session0, reg_type(Reuse)); handle_session(server, _, Host, Port, Session) -> @@ -2709,7 +2709,7 @@ register_session(server, _, Port, #session{is_resumable = new} = Session0, _) -> register_session(_, _, _, Session, _) -> Session. %% Already registered -host_id(client, _Host, #ssl_options{server_name_indication = Hostname}) when is_list(Hostname) -> +host_id(client, _Host, #{server_name_indication := Hostname}) when is_list(Hostname) -> Hostname; host_id(_, Host, _) -> Host. @@ -2760,28 +2760,27 @@ negotiated_hashsign(HashSign = {_, _}, _, _, _) -> HashSign. ssl_options_list(SslOptions) -> - Fileds = record_info(fields, ssl_options), - Values = tl(tuple_to_list(SslOptions)), - ssl_options_list(Fileds, Values, []). + L = maps:to_list(SslOptions), + ssl_options_list(L, []). -ssl_options_list([],[], Acc) -> +ssl_options_list([], Acc) -> lists:reverse(Acc); %% Skip internal options, only return user options -ssl_options_list([protocol | Keys], [_ | Values], Acc) -> - ssl_options_list(Keys, Values, Acc); -ssl_options_list([erl_dist | Keys], [_ | Values], Acc) -> - ssl_options_list(Keys, Values, Acc); -ssl_options_list([renegotiate_at | Keys], [_ | Values], Acc) -> - ssl_options_list(Keys, Values, Acc); -ssl_options_list([ciphers = Key | Keys], [Value | Values], Acc) -> - ssl_options_list(Keys, Values, +ssl_options_list([{protocol, _}| T], Acc) -> + ssl_options_list(T, Acc); +ssl_options_list([{erl_dist, _}|T], Acc) -> + ssl_options_list(T, Acc); +ssl_options_list([{renegotiate_at, _}|T], Acc) -> + ssl_options_list(T, Acc); +ssl_options_list([{ciphers = Key, Value}|T], Acc) -> + ssl_options_list(T, [{Key, lists:map( fun(Suite) -> ssl_cipher_format:suite_bin_to_map(Suite) end, Value)} | Acc]); -ssl_options_list([Key | Keys], [Value | Values], Acc) -> - ssl_options_list(Keys, Values, [{Key, Value} | Acc]). +ssl_options_list([{Key, Value}|T], Acc) -> + ssl_options_list(T, [{Key, Value} | Acc]). handle_active_option(false, connection = StateName, To, Reply, State) -> hibernate_after(StateName, State, [{reply, To, Reply}]); @@ -3029,12 +3028,13 @@ handle_sni_extension(#sni{hostname = Hostname}, #state{static_env = #static_env{ } end. -update_ssl_options_from_sni(OrigSSLOptions, SNIHostname) -> +update_ssl_options_from_sni(#{sni_fun := SNIFun, + sni_hosts := SNIHosts} = OrigSSLOptions, SNIHostname) -> SSLOption = - case OrigSSLOptions#ssl_options.sni_fun of + case SNIFun of undefined -> proplists:get_value(SNIHostname, - OrigSSLOptions#ssl_options.sni_hosts); + SNIHosts); SNIFun -> SNIFun(SNIHostname) end, diff --git a/lib/ssl/src/ssl_connection.hrl b/lib/ssl/src/ssl_connection.hrl index 844368c761..be94fd05bb 100644 --- a/lib/ssl/src/ssl_connection.hrl +++ b/lib/ssl/src/ssl_connection.hrl @@ -92,7 +92,7 @@ -record(state, { static_env :: #static_env{}, connection_env :: #connection_env{} | secret_printout(), - ssl_options :: #ssl_options{}, + ssl_options :: ssl_options(), socket_options :: #socket_options{}, %% Hanshake %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% diff --git a/lib/ssl/src/ssl_handshake.erl b/lib/ssl/src/ssl_handshake.erl index bd2efa9fbb..1c8a2ca452 100644 --- a/lib/ssl/src/ssl_handshake.erl +++ b/lib/ssl/src/ssl_handshake.erl @@ -336,25 +336,30 @@ next_protocol(SelectedProtocol) -> %% Handle handshake messages %%==================================================================== %%-------------------------------------------------------------------- --spec certify(#certificate{}, db_handle(), certdb_ref(), #ssl_options{}, term(), +-spec certify(#certificate{}, db_handle(), certdb_ref(), ssl_options(), term(), client | server, inet:hostname() | inet:ip_address()) -> {der_cert(), public_key_info()} | #alert{}. %% %% Description: Handles a certificate handshake message %%-------------------------------------------------------------------- certify(#certificate{asn1_certificates = ASN1Certs}, CertDbHandle, CertDbRef, - Opts, CRLDbHandle, Role, Host) -> - - ServerName = server_name(Opts#ssl_options.server_name_indication, Host, Role), + #{server_name_indication := ServerNameIndication, + partial_chain := PartialChain, + verify_fun := VerifyFun, + customize_hostname_check := CustomizeHostnameCheck, + crl_check := CrlCheck, + depth := Depth} = Opts, CRLDbHandle, Role, Host) -> + + ServerName = server_name(ServerNameIndication, Host, Role), [PeerCert | ChainCerts ] = ASN1Certs, try {TrustedCert, CertPath} = ssl_certificate:trusted_cert_and_path(ASN1Certs, CertDbHandle, CertDbRef, - Opts#ssl_options.partial_chain), - ValidationFunAndState = validation_fun_and_state(Opts#ssl_options.verify_fun, Role, + PartialChain), + ValidationFunAndState = validation_fun_and_state(VerifyFun, Role, CertDbHandle, CertDbRef, ServerName, - Opts#ssl_options.customize_hostname_check, - Opts#ssl_options.crl_check, CRLDbHandle, CertPath), - Options = [{max_path_length, Opts#ssl_options.depth}, + CustomizeHostnameCheck, + CrlCheck, CRLDbHandle, CertPath), + Options = [{max_path_length, Depth}, {verify_fun, ValidationFunAndState}], case public_key:pkix_path_validation(TrustedCert, CertPath, Options) of {ok, {PublicKeyInfo,_}} -> @@ -945,7 +950,7 @@ prf({3,_N}, PRFAlgo, Secret, Label, Seed, WantedLength) -> select_session(SuggestedSessionId, CipherSuites, HashSigns, Compressions, Port, #session{ecc = ECCCurve0} = Session, Version, - #ssl_options{ciphers = UserSuites, honor_cipher_order = HonorCipherOrder} = SslOpts, + #{ciphers := UserSuites, honor_cipher_order := HonorCipherOrder} = SslOpts, Cache, CacheCb, Cert) -> {SessionId, Resumed} = ssl_session:server_id(Port, SuggestedSessionId, SslOpts, Cert, @@ -1071,27 +1076,29 @@ client_hello_extensions(Version, CipherSuites, SslOpts, ConnectionStates, Renego add_tls12_extensions(_Version, - SslOpts, + #{alpn_advertised_protocols := AlpnAdvertisedProtocols, + next_protocol_selector := NextProtocolSelector, + server_name_indication := ServerNameIndication} = SslOpts, ConnectionStates, Renegotiation) -> SRP = srp_user(SslOpts), #{renegotiation_info => renegotiation_info(tls_record, client, ConnectionStates, Renegotiation), srp => SRP, - alpn => encode_alpn(SslOpts#ssl_options.alpn_advertised_protocols, Renegotiation), + alpn => encode_alpn(AlpnAdvertisedProtocols, Renegotiation), next_protocol_negotiation => - encode_client_protocol_negotiation(SslOpts#ssl_options.next_protocol_selector, + encode_client_protocol_negotiation(NextProtocolSelector, Renegotiation), - sni => sni(SslOpts#ssl_options.server_name_indication) + sni => sni(ServerNameIndication) }. add_common_extensions({3,4}, HelloExtensions, _CipherSuites, - #ssl_options{eccs = SupportedECCs, - supported_groups = Groups, - signature_algs = SignatureSchemes}) -> + #{eccs := SupportedECCs, + supported_groups := Groups, + signature_algs := SignatureSchemes}) -> {EcPointFormats, _} = client_ecc_extensions(SupportedECCs), HelloExtensions#{ec_point_formats => EcPointFormats, @@ -1101,8 +1108,8 @@ add_common_extensions({3,4}, add_common_extensions(Version, HelloExtensions, CipherSuites, - #ssl_options{eccs = SupportedECCs, - signature_algs = SupportedHashSigns}) -> + #{eccs := SupportedECCs, + signature_algs := SupportedHashSigns}) -> {EcPointFormats, EllipticCurves} = case advertises_ec_ciphers( @@ -1120,8 +1127,8 @@ add_common_extensions(Version, maybe_add_tls13_extensions({3,4}, HelloExtensions0, - #ssl_options{signature_algs_cert = SignatureSchemes, - versions = SupportedVersions}, + #{signature_algs_cert := SignatureSchemes, + versions := SupportedVersions}, KeyShare) -> HelloExtensions = HelloExtensions0#{client_hello_versions => @@ -1223,8 +1230,8 @@ signature_algs_cert(SignatureSchemes) -> handle_client_hello_extensions(RecordCB, Random, ClientCipherSuites, Exts, Version, - #ssl_options{secure_renegotiate = SecureRenegotation, - alpn_preferred_protocols = ALPNPreferredProtocols} = Opts, + #{secure_renegotiate := SecureRenegotation, + alpn_preferred_protocols := ALPNPreferredProtocols} = Opts, #session{cipher_suite = NegotiatedCipherSuite, compression_method = Compression} = Session0, ConnectionStates0, Renegotiation) -> @@ -1259,8 +1266,8 @@ handle_client_hello_extensions(RecordCB, Random, ClientCipherSuites, handle_server_hello_extensions(RecordCB, Random, CipherSuite, Compression, Exts, Version, - #ssl_options{secure_renegotiate = SecureRenegotation, - next_protocol_selector = NextProtoSelector}, + #{secure_renegotiate := SecureRenegotation, + next_protocol_selector := NextProtoSelector}, ConnectionStates0, Renegotiation) -> ConnectionStates = handle_renegotiation_extension(client, RecordCB, Version, maps:get(renegotiation_info, Exts, undefined), Random, @@ -1477,7 +1484,7 @@ select_hashsign_algs(undefined, ?rsaEncryption, _) -> select_hashsign_algs(undefined, ?'id-dsa', _) -> {sha, dsa}. -srp_user(#ssl_options{srp_identity = {UserName, _}}) -> +srp_user(#{srp_identity := {UserName, _}}) -> #srp{username = UserName}; srp_user(_) -> undefined. @@ -1644,12 +1651,12 @@ handle_path_validation_error({bad_cert, unknown_ca} = Reason, PeerCert, Chain, Opts, Options, CertDbHandle, CertsDbRef) -> handle_incomplete_chain(PeerCert, Chain, Opts, Options, CertDbHandle, CertsDbRef, Reason); handle_path_validation_error({bad_cert, invalid_issuer} = Reason, PeerCert, Chain0, - Opts, Options, CertDbHandle, CertsDbRef) -> + #{partial_chain := PartialChain} = Opts, Options, CertDbHandle, CertsDbRef) -> case ssl_certificate:certificate_chain(PeerCert, CertDbHandle, CertsDbRef, Chain0) of {ok, _, [PeerCert | Chain] = OrdedChain} when Chain =/= Chain0 -> %% Chain appaears to be unorded {Trusted, Path} = ssl_certificate:trusted_cert_and_path(OrdedChain, CertDbHandle, CertsDbRef, - Opts#ssl_options.partial_chain), + PartialChain), case public_key:pkix_path_validation(Trusted, Path, Options) of {ok, {PublicKeyInfo,_}} -> {PeerCert, PublicKeyInfo}; @@ -1663,12 +1670,13 @@ handle_path_validation_error({bad_cert, invalid_issuer} = Reason, PeerCert, Chai handle_path_validation_error(Reason, _, _, _, _,_, _) -> path_validation_alert(Reason). -handle_incomplete_chain(PeerCert, Chain0, Opts, Options, CertDbHandle, CertsDbRef, PathError0) -> +handle_incomplete_chain(PeerCert, Chain0, + #{partial_chain := PartialChain}, Options, CertDbHandle, CertsDbRef, PathError0) -> case ssl_certificate:certificate_chain(PeerCert, CertDbHandle, CertsDbRef) of {ok, _, [PeerCert | _] = Chain} when Chain =/= Chain0 -> %% Chain candidate found {Trusted, Path} = ssl_certificate:trusted_cert_and_path(Chain, CertDbHandle, CertsDbRef, - Opts#ssl_options.partial_chain), + PartialChain), case public_key:pkix_path_validation(Trusted, Path, Options) of {ok, {PublicKeyInfo,_}} -> {PeerCert, PublicKeyInfo}; @@ -2834,7 +2842,7 @@ handle_next_protocol_on_server(undefined, _Renegotiation, _SslOpts) -> undefined; handle_next_protocol_on_server(#next_protocol_negotiation{extension_data = <<>>}, - false, #ssl_options{next_protocols_advertised = Protocols}) -> + false, #{next_protocols_advertised := Protocols}) -> Protocols; handle_next_protocol_on_server(_Hello, _Renegotiation, _SSLOpts) -> diff --git a/lib/ssl/src/ssl_internal.hrl b/lib/ssl/src/ssl_internal.hrl index 06c3ccae45..fc9f16a189 100644 --- a/lib/ssl/src/ssl_internal.hrl +++ b/lib/ssl/src/ssl_internal.hrl @@ -202,7 +202,7 @@ -type gen_fsm_state_return() :: {next_state, state_name(), any()} | {next_state, state_name(), any(), timeout()} | {stop, any(), any()}. --type ssl_options() :: #ssl_options{}. +-type ssl_options() :: map(). -endif. % -ifdef(ssl_internal). diff --git a/lib/ssl/src/ssl_session.erl b/lib/ssl/src/ssl_session.erl index 44305c65fe..fd012acd5e 100644 --- a/lib/ssl/src/ssl_session.erl +++ b/lib/ssl/src/ssl_session.erl @@ -48,13 +48,13 @@ is_new(_ClientSuggestion, _ServerDecision) -> true. %%-------------------------------------------------------------------- --spec client_id({ssl:host(), inet:port_number(), #ssl_options{}}, db_handle(), atom(), +-spec client_id({ssl:host(), inet:port_number(), ssl_options()}, db_handle(), atom(), undefined | binary()) -> binary(). %% %% Description: Should be called by the client side to get an id %% for the client hello message. %%-------------------------------------------------------------------- -client_id({Host, Port, #ssl_options{reuse_session = SessionId}}, Cache, CacheCb, _) when is_binary(SessionId)-> +client_id({Host, Port, #{reuse_session := SessionId}}, Cache, CacheCb, _) when is_binary(SessionId)-> case CacheCb:lookup(Cache, {{Host, Port}, SessionId}) of undefined -> <<>>; @@ -99,7 +99,7 @@ server_id(Port, SuggestedId, Options, Cert, Cache, CacheCb) -> %%-------------------------------------------------------------------- %%% Internal functions %%-------------------------------------------------------------------- -select_session({_, _, #ssl_options{reuse_sessions = Reuse}}, _Cache, _CacheCb, _OwnCert) when Reuse =/= true -> +select_session({_, _, #{reuse_sessions := Reuse}}, _Cache, _CacheCb, _OwnCert) when Reuse =/= true -> %% If reuse_sessions == true | save a new session should be created no_session; select_session({HostIP, Port, SslOpts}, Cache, CacheCb, OwnCert) -> @@ -108,7 +108,7 @@ select_session({HostIP, Port, SslOpts}, Cache, CacheCb, OwnCert) -> select_session([], _, _) -> no_session; -select_session(Sessions, #ssl_options{ciphers = Ciphers}, OwnCert) -> +select_session(Sessions, #{ciphers := Ciphers}, OwnCert) -> IsNotResumable = fun(Session) -> not (resumable(Session#session.is_resumable) andalso @@ -120,9 +120,9 @@ select_session(Sessions, #ssl_options{ciphers = Ciphers}, OwnCert) -> [Session | _] -> Session#session.session_id end. -is_resumable(_, _, #ssl_options{reuse_sessions = false}, _, _, _, _) -> +is_resumable(_, _, #{reuse_sessions := false}, _, _, _, _) -> {false, undefined}; -is_resumable(SuggestedSessionId, Port, #ssl_options{reuse_session = ReuseFun} = Options, Cache, +is_resumable(SuggestedSessionId, Port, #{reuse_session := ReuseFun} = Options, Cache, CacheCb, SecondLifeTime, OwnCert) -> case CacheCb:lookup(Cache, {Port, SuggestedSessionId}) of #session{cipher_suite = CipherSuite, @@ -149,8 +149,8 @@ resumable(new) -> resumable(IsResumable) -> IsResumable. -reusable_options(#ssl_options{fail_if_no_peer_cert = true, - verify = verify_peer}, Session) -> +reusable_options(#{fail_if_no_peer_cert := true, + verify := verify_peer}, Session) -> (Session#session.peer_certificate =/= undefined); reusable_options(_,_) -> true. diff --git a/lib/ssl/src/tls_connection.erl b/lib/ssl/src/tls_connection.erl index 3998f03519..cf104cd805 100644 --- a/lib/ssl/src/tls_connection.erl +++ b/lib/ssl/src/tls_connection.erl @@ -98,7 +98,7 @@ %%==================================================================== %% Setup %%==================================================================== -start_fsm(Role, Host, Port, Socket, {#ssl_options{erl_dist = false},_, Tracker} = Opts, +start_fsm(Role, Host, Port, Socket, {#{erl_dist := false},_, Tracker} = Opts, User, {CbModule, _,_, _, _} = CbInfo, Timeout) -> try @@ -112,7 +112,7 @@ start_fsm(Role, Host, Port, Socket, {#ssl_options{erl_dist = false},_, Tracker} Error end; -start_fsm(Role, Host, Port, Socket, {#ssl_options{erl_dist = true},_, Tracker} = Opts, +start_fsm(Role, Host, Port, Socket, {#{erl_dist := true},_, Tracker} = Opts, User, {CbModule, _,_, _, _} = CbInfo, Timeout) -> try @@ -136,10 +136,10 @@ start_fsm(Role, Host, Port, Socket, {#ssl_options{erl_dist = true},_, Tracker} = start_link(Role, Sender, Host, Port, Socket, Options, User, CbInfo) -> {ok, proc_lib:spawn_link(?MODULE, init, [[Role, Sender, Host, Port, Socket, Options, User, CbInfo]])}. -init([Role, Sender, Host, Port, Socket, {SslOpts, _, _} = Options, User, CbInfo]) -> +init([Role, Sender, Host, Port, Socket, {#{erl_dist := ErlDist}, _, _} = Options, User, CbInfo]) -> process_flag(trap_exit, true), link(Sender), - case SslOpts#ssl_options.erl_dist of + case ErlDist of true -> process_flag(priority, max); _ -> @@ -170,7 +170,7 @@ next_record(_, #state{handshake_env = next_record(_, #state{protocol_buffers = #protocol_buffers{tls_cipher_texts = [_|_] = CipherTexts}, connection_states = ConnectionStates, - ssl_options = #ssl_options{padding_check = Check}} = State) -> + ssl_options = #{padding_check := Check}} = State) -> next_record(State, CipherTexts, ConnectionStates, Check); next_record(connection, #state{protocol_buffers = #protocol_buffers{tls_cipher_texts = []}, protocol_specific = #{active_n_toggle := true} @@ -385,12 +385,12 @@ send_handshake(Handshake, State) -> queue_handshake(Handshake, #state{handshake_env = #handshake_env{tls_handshake_history = Hist0} = HsEnv, connection_env = #connection_env{negotiated_version = Version}, flight_buffer = Flight0, - ssl_options = SslOpts, + ssl_options = #{log_level := LogLevel}, connection_states = ConnectionStates0} = State0) -> {BinHandshake, ConnectionStates, Hist} = encode_handshake(Handshake, Version, ConnectionStates0, Hist0), - ssl_logger:debug(SslOpts#ssl_options.log_level, outbound, 'handshake', Handshake), - ssl_logger:debug(SslOpts#ssl_options.log_level, outbound, 'record', BinHandshake), + ssl_logger:debug(LogLevel, outbound, 'handshake', Handshake), + ssl_logger:debug(LogLevel, outbound, 'record', BinHandshake), State0#state{connection_states = ConnectionStates, handshake_env = HsEnv#handshake_env{tls_handshake_history = Hist}, @@ -406,11 +406,11 @@ send_handshake_flight(#state{static_env = #static_env{socket = Socket, queue_change_cipher(Msg, #state{connection_env = #connection_env{negotiated_version = Version}, flight_buffer = Flight0, - ssl_options = SslOpts, + ssl_options = #{log_level := LogLevel}, connection_states = ConnectionStates0} = State0) -> {BinChangeCipher, ConnectionStates} = encode_change_cipher(Msg, Version, ConnectionStates0), - ssl_logger:debug(SslOpts#ssl_options.log_level, outbound, 'record', BinChangeCipher), + ssl_logger:debug(LogLevel, outbound, 'record', BinChangeCipher), State0#state{connection_states = ConnectionStates, flight_buffer = Flight0 ++ [BinChangeCipher]}. @@ -454,12 +454,12 @@ encode_alert(#alert{} = Alert, Version, ConnectionStates) -> send_alert(Alert, #state{static_env = #static_env{socket = Socket, transport_cb = Transport}, connection_env = #connection_env{negotiated_version = Version}, - ssl_options = SslOpts, + ssl_options = #{log_level := LogLevel}, connection_states = ConnectionStates0} = StateData0) -> {BinMsg, ConnectionStates} = encode_alert(Alert, Version, ConnectionStates0), tls_socket:send(Transport, Socket, BinMsg), - ssl_logger:debug(SslOpts#ssl_options.log_level, outbound, 'record', BinMsg), + ssl_logger:debug(LogLevel, outbound, 'record', BinMsg), StateData0#state{connection_states = ConnectionStates}. %% If an ALERT sent in the connection state, should cause the TLS @@ -542,7 +542,8 @@ init({call, From}, {start, Timeout}, session_cache_cb = CacheCb}, handshake_env = #handshake_env{renegotiation = {Renegotiation, _}} = HsEnv, connection_env = CEnv, - ssl_options = SslOpts, + ssl_options = #{log_level := LogLevel, + versions := Versions} = SslOpts, session = #session{own_certificate = Cert} = Session0, connection_states = ConnectionStates0 } = State0) -> @@ -550,13 +551,13 @@ init({call, From}, {start, Timeout}, Hello = tls_handshake:client_hello(Host, Port, ConnectionStates0, SslOpts, Cache, CacheCb, Renegotiation, Cert, KeyShare), - HelloVersion = tls_record:hello_version(SslOpts#ssl_options.versions), + HelloVersion = tls_record:hello_version(Versions), Handshake0 = ssl_handshake:init_handshake_history(), {BinMsg, ConnectionStates, Handshake} = encode_handshake(Hello, HelloVersion, ConnectionStates0, Handshake0), tls_socket:send(Transport, Socket, BinMsg), - ssl_logger:debug(SslOpts#ssl_options.log_level, outbound, 'handshake', Hello), - ssl_logger:debug(SslOpts#ssl_options.log_level, outbound, 'record', BinMsg), + ssl_logger:debug(LogLevel, outbound, 'handshake', Hello), + ssl_logger:debug(LogLevel, outbound, 'record', BinMsg), State = State0#state{connection_states = ConnectionStates, connection_env = CEnv#connection_env{negotiated_version = HelloVersion}, %% Requested version @@ -592,14 +593,14 @@ error(_, _, _) -> gen_statem:state_function_result(). %%-------------------------------------------------------------------- hello(internal, #client_hello{extensions = Extensions} = Hello, - #state{ssl_options = #ssl_options{handshake = 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{extensions = Extensions} = Hello, - #state{ssl_options = #ssl_options{handshake = 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, @@ -982,8 +983,9 @@ code_change(_OldVsn, StateName, State, _) -> %%-------------------------------------------------------------------- initial_state(Role, Sender, Host, Port, Socket, {SSLOptions, SocketOptions, Tracker}, User, {CbModule, DataTag, CloseTag, ErrorTag, PassiveTag}) -> - #ssl_options{beast_mitigation = BeastMitigation, - erl_dist = IsErlDist} = SSLOptions, + #{beast_mitigation := BeastMitigation, + erl_dist := IsErlDist, + client_renegotiation := ClientRenegotiation} = SSLOptions, ConnectionStates = tls_record:init_connection_states(Role, BeastMitigation), SessionCacheCb = case application:get_env(ssl, session_cb) of {ok, Cb} when is_atom(Cb) -> @@ -1017,7 +1019,7 @@ initial_state(Role, Sender, Host, Port, Socket, {SSLOptions, SocketOptions, Trac handshake_env = #handshake_env{ tls_handshake_history = ssl_handshake:init_handshake_history(), renegotiation = {false, first}, - allow_renegotiate = SSLOptions#ssl_options.client_renegotiation + allow_renegotiate = ClientRenegotiation }, connection_env = #connection_env{user_application = {UserMonitor, User}}, socket_options = SocketOptions, @@ -1042,8 +1044,8 @@ initialize_tls_sender(#state{static_env = #static_env{ }, connection_env = #connection_env{negotiated_version = Version}, socket_options = SockOpts, - ssl_options = #ssl_options{renegotiate_at = RenegotiateAt, - log_level = LogLevel}, + ssl_options = #{renegotiate_at := RenegotiateAt, + log_level := LogLevel}, connection_states = #{current_write := ConnectionWriteState}, protocol_specific = #{sender := Sender}}) -> Init = #{current_write => ConnectionWriteState, @@ -1261,7 +1263,7 @@ unprocessed_events(Events) -> assert_buffer_sanity(<<?BYTE(_Type), ?UINT24(Length), Rest/binary>>, - #ssl_options{max_handshake_size = Max}) when + #{max_handshake_size := Max}) when Length =< Max -> case size(Rest) of N when N < Length -> @@ -1295,18 +1297,17 @@ ensure_sender_terminate(_, #state{protocol_specific = #{sender := Sender}}) -> end, spawn(Kill). -maybe_generate_client_shares(#ssl_options{ - versions = [Version|_], - supported_groups = - #supported_groups{ - supported_groups = [Group|_]}}) +maybe_generate_client_shares(#{versions := [Version|_], + supported_groups := + #supported_groups{ + supported_groups = [Group|_]}}) when Version =:= {3,4} -> %% Generate only key_share entry for the most preferred group ssl_cipher:generate_client_shares([Group]); maybe_generate_client_shares(_) -> undefined. -choose_tls_version(#ssl_options{versions = Versions}, +choose_tls_version(#{versions := Versions}, #client_hello{ extensions = #{client_hello_versions := #client_hello_versions{versions = ClientVersions} @@ -1322,7 +1323,7 @@ choose_tls_version(_, _) -> 'tls_v1.2'. -effective_version(undefined, #ssl_options{versions = [Version|_]}) -> +effective_version(undefined, #{versions := [Version|_]}) -> Version; effective_version(Version, _) -> Version. diff --git a/lib/ssl/src/tls_handshake.erl b/lib/ssl/src/tls_handshake.erl index 37265e0759..203f89a0b8 100644 --- a/lib/ssl/src/tls_handshake.erl +++ b/lib/ssl/src/tls_handshake.erl @@ -51,17 +51,17 @@ %%==================================================================== %%-------------------------------------------------------------------- -spec client_hello(ssl:host(), inet:port_number(), ssl_record:connection_states(), - #ssl_options{}, integer(), atom(), boolean(), der_cert(), + ssl_options(), integer(), atom(), boolean(), der_cert(), #key_share_client_hello{} | undefined) -> #client_hello{}. %% %% Description: Creates a client hello message. %%-------------------------------------------------------------------- client_hello(Host, Port, ConnectionStates, - #ssl_options{versions = Versions, - ciphers = UserSuites, - fallback = Fallback - } = SslOpts, + #{versions := Versions, + ciphers := UserSuites, + fallback := Fallback + } = SslOpts, Cache, CacheCb, Renegotiation, OwnCert, KeyShare) -> Version = tls_record:highest_protocol_version(Versions), @@ -95,7 +95,7 @@ client_hello(Host, Port, ConnectionStates, }. %%-------------------------------------------------------------------- --spec hello(#server_hello{} | #client_hello{}, #ssl_options{}, +-spec hello(#server_hello{} | #client_hello{}, ssl_options(), ssl_record:connection_states() | {inet:port_number(), #session{}, db_handle(), atom(), ssl_record:connection_states(), binary() | undefined, ssl:kex_algo()}, @@ -117,7 +117,7 @@ client_hello(Host, Port, ConnectionStates, %% values. hello(#server_hello{server_version = {Major, Minor}, random = <<_:24/binary,Down:8/binary>>}, - #ssl_options{versions = [{M,N}|_]}, _, _) + #{versions := [{M,N}|_]}, _, _) when (M > 3 orelse M =:= 3 andalso N >= 4) andalso %% TLS 1.3 client (Major =:= 3 andalso Minor =:= 3 andalso %% Negotiating TLS 1.2 Down =:= ?RANDOM_OVERRIDE_TLS12) orelse @@ -131,7 +131,7 @@ hello(#server_hello{server_version = {Major, Minor}, %% equal to the second value if the ServerHello indicates TLS 1.1 or below. hello(#server_hello{server_version = {Major, Minor}, random = <<_:24/binary,Down:8/binary>>}, - #ssl_options{versions = [{M,N}|_]}, _, _) + #{versions := [{M,N}|_]}, _, _) 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) -> @@ -156,7 +156,7 @@ hello(#server_hello{server_version = LegacyVersion, extensions = #{server_hello_selected_version := #server_hello_selected_version{selected_version = Version} = HelloExt} }, - #ssl_options{versions = SupportedVersions} = SslOpt, + #{versions := SupportedVersions} = SslOpt, ConnectionStates0, Renegotiation) -> %% In TLS 1.3, the TLS server indicates its version using the "supported_versions" extension %% (Section 4.2.1), and the legacy_version field MUST be set to 0x0303, which is the version @@ -190,7 +190,7 @@ hello(#server_hello{server_version = Version, compression_method = Compression, session_id = SessionId, extensions = HelloExt}, - #ssl_options{versions = SupportedVersions} = SslOpt, + #{versions := SupportedVersions} = SslOpt, ConnectionStates0, Renegotiation) -> case tls_record:is_acceptable_version(Version, SupportedVersions) of true -> @@ -221,7 +221,7 @@ hello(#client_hello{client_version = _ClientVersion, extensions = #{client_hello_versions := #client_hello_versions{versions = ClientVersions} }} = Hello, - #ssl_options{versions = Versions} = SslOpts, + #{versions := Versions} = SslOpts, Info, Renegotiation) -> try Version = ssl_handshake:select_supported_version(ClientVersions, Versions), @@ -233,7 +233,7 @@ hello(#client_hello{client_version = _ClientVersion, hello(#client_hello{client_version = ClientVersion, cipher_suites = CipherSuites} = Hello, - #ssl_options{versions = Versions} = SslOpts, + #{versions := Versions} = SslOpts, Info, Renegotiation) -> try Version = ssl_handshake:select_version(tls_record, ClientVersion, Versions), @@ -269,7 +269,7 @@ encode_handshake(Package, Version) -> %%-------------------------------------------------------------------- -spec get_tls_handshake(tls_record:tls_version(), binary(), binary() | iolist(), - #ssl_options{}) -> + ssl_options()) -> {[{tls_handshake(), binary()}], binary()}. %% %% Description: Given buffered and new data from ssl_record, collects @@ -290,10 +290,10 @@ handle_client_hello(Version, compression_methods = Compressions, random = Random, extensions = HelloExt}, - #ssl_options{versions = Versions, - signature_algs = SupportedHashSigns, - eccs = SupportedECCs, - honor_ecc_order = ECCOrder} = SslOpts, + #{versions := Versions, + signature_algs := SupportedHashSigns, + eccs := SupportedECCs, + honor_ecc_order := ECCOrder} = SslOpts, {Port, Session0, Cache, CacheCb, ConnectionStates0, Cert, _}, Renegotiation) -> case tls_record:is_acceptable_version(Version, Versions) of @@ -404,11 +404,11 @@ enc_handshake(HandshakeMsg, Version) -> %%-------------------------------------------------------------------- get_tls_handshake_aux(Version, <<?BYTE(Type), ?UINT24(Length), Body:Length/binary,Rest/binary>>, - Opts, Acc) -> + #{log_level := LogLevel} = Opts, Acc) -> Raw = <<?BYTE(Type), ?UINT24(Length), Body/binary>>, try decode_handshake(Version, Type, Body) of Handshake -> - ssl_logger:debug(Opts#ssl_options.log_level, inbound, 'handshake', Handshake), + ssl_logger:debug(LogLevel, inbound, 'handshake', Handshake), get_tls_handshake_aux(Version, Rest, Opts, [{Handshake,Raw} | Acc]) catch _:_ -> diff --git a/lib/ssl/src/tls_handshake_1_3.erl b/lib/ssl/src/tls_handshake_1_3.erl index c29366e717..5f4c0b9a4a 100644 --- a/lib/ssl/src/tls_handshake_1_3.erl +++ b/lib/ssl/src/tls_handshake_1_3.erl @@ -336,8 +336,9 @@ decode_handshake(?ENCRYPTED_EXTENSIONS, <<?UINT16(Size), EncExts:Size/binary>>) extensions = decode_extensions(EncExts, encrypted_extensions) }; decode_handshake(?NEW_SESSION_TICKET, <<?UINT32(LifeTime), ?UINT32(Age), - ?BYTE(Nonce), ?UINT16(TicketSize), Ticket:TicketSize/binary, - BinExts/binary>>) -> + ?BYTE(NonceSize), Nonce:NonceSize/binary, + ?UINT16(TicketSize), Ticket:TicketSize/binary, + ?UINT16(BinExtSize), BinExts:BinExtSize/binary>>) -> Exts = decode_extensions(BinExts, encrypted_extensions), #new_session_ticket{ticket_lifetime = LifeTime, ticket_age_add = Age, @@ -501,11 +502,11 @@ do_start(#client_hello{cipher_suites = ClientCiphers, session_id = SessionId, extensions = Extensions} = _Hello, #state{connection_states = _ConnectionStates0, - ssl_options = #ssl_options{ciphers = ServerCiphers, - signature_algs = ServerSignAlgs, - supported_groups = ServerGroups0, - alpn_preferred_protocols = ALPNPreferredProtocols, - honor_cipher_order = HonorCipherOrder}, + ssl_options = #{ciphers := ServerCiphers, + signature_algs := ServerSignAlgs, + supported_groups := ServerGroups0, + alpn_preferred_protocols := ALPNPreferredProtocols, + honor_cipher_order := HonorCipherOrder}, session = #session{own_certificate = Cert}} = State0) -> ClientGroups0 = maps:get(elliptic_curves, Extensions, undefined), ClientGroups = get_supported_groups(ClientGroups0), @@ -600,8 +601,10 @@ do_start(#server_hello{cipher_suite = SelectedCipherSuite, handshake_env = #handshake_env{renegotiation = {Renegotiation, _}, tls_handshake_history = _HHistory} = HsEnv, connection_env = CEnv, - ssl_options = #ssl_options{ciphers = ClientCiphers, - supported_groups = ClientGroups0} = SslOpts, + ssl_options = #{ciphers := ClientCiphers, + supported_groups := ClientGroups0, + versions := Versions, + log_level := LogLevel} = SslOpts, session = #session{own_certificate = Cert} = Session0, connection_states = ConnectionStates0 } = State0) -> @@ -632,7 +635,7 @@ do_start(#server_hello{cipher_suite = SelectedCipherSuite, Hello = tls_handshake:client_hello(Host, Port, ConnectionStates0, SslOpts, Cache, CacheCb, Renegotiation, Cert, ClientKeyShare), - HelloVersion = tls_record:hello_version(SslOpts#ssl_options.versions), + HelloVersion = tls_record:hello_version(Versions), %% Update state State1 = update_start_state(State0, @@ -648,8 +651,8 @@ do_start(#server_hello{cipher_suite = SelectedCipherSuite, {BinMsg, ConnectionStates, Handshake} = tls_connection:encode_handshake(Hello, HelloVersion, ConnectionStates0, HHistory), tls_socket:send(Transport, Socket, BinMsg), - ssl_logger:debug(SslOpts#ssl_options.log_level, outbound, 'handshake', Hello), - ssl_logger:debug(SslOpts#ssl_options.log_level, outbound, 'record', BinMsg), + ssl_logger:debug(LogLevel, outbound, 'handshake', Hello), + ssl_logger:debug(LogLevel, outbound, 'record', BinMsg), State = State2#state{ connection_states = ConnectionStates, @@ -673,7 +676,7 @@ do_negotiated(start_handshake, ecc = SelectedGroup, sign_alg = SignatureScheme, dh_public_value = ClientPublicKey}, - ssl_options = #ssl_options{} = SslOpts, + ssl_options = #{} = SslOpts, key_share = KeyShare, handshake_env = #handshake_env{tls_handshake_history = _HHistory0}, connection_env = #connection_env{private_key = CertPrivateKey}, @@ -831,8 +834,8 @@ do_wait_sh(#server_hello{cipher_suite = SelectedCipherSuite, session_id = SessionId, extensions = Extensions} = ServerHello, #state{key_share = ClientKeyShare0, - ssl_options = #ssl_options{ciphers = ClientCiphers, - supported_groups = ClientGroups0}} = State0) -> + ssl_options = #{ciphers := ClientCiphers, + supported_groups := ClientGroups0}} = State0) -> ClientGroups = get_supported_groups(ClientGroups0), ServerKeyShare0 = maps:get(key_share, Extensions, undefined), ClientKeyShare = get_key_shares(ClientKeyShare0), @@ -976,7 +979,7 @@ maybe_queue_cert_cert_cv(#state{client_certificate_requested = false} = State) - maybe_queue_cert_cert_cv(#state{connection_states = _ConnectionStates0, session = #session{session_id = _SessionId, own_certificate = OwnCert}, - ssl_options = #ssl_options{} = _SslOpts, + ssl_options = #{} = _SslOpts, key_share = _KeyShare, handshake_env = #handshake_env{tls_handshake_history = _HHistory0}, static_env = #static_env{ @@ -1063,12 +1066,11 @@ send_hello_retry_request(State0, _, _, _) -> {ok, {State0, negotiated}}. -maybe_send_certificate_request(State, #ssl_options{verify = verify_none}) -> +maybe_send_certificate_request(State, #{verify := verify_none}) -> {State, wait_finished}; -maybe_send_certificate_request(State, #ssl_options{ - verify = verify_peer, - signature_algs = SignAlgs, - signature_algs_cert = SignAlgsCert}) -> +maybe_send_certificate_request(State, #{verify := verify_peer, + signature_algs := SignAlgs, + signature_algs_cert := SignAlgsCert}) -> CertificateRequest = certificate_request(SignAlgs, SignAlgsCert), {tls_connection:queue_handshake(CertificateRequest, State), wait_cert}. @@ -1102,15 +1104,13 @@ process_certificate(#certificate_1_3{ certificate_request_context = <<>>, certificate_list = []}, #state{ssl_options = - #ssl_options{ - fail_if_no_peer_cert = false}} = State) -> + #{fail_if_no_peer_cert := false}} = State) -> {ok, {State, wait_finished}}; process_certificate(#certificate_1_3{ certificate_request_context = <<>>, certificate_list = []}, #state{ssl_options = - #ssl_options{ - fail_if_no_peer_cert = true}} = State0) -> + #{fail_if_no_peer_cert := true}} = State0) -> %% At this point the client believes that the connection is up and starts using %% its traffic secrets. In order to be able send an proper Alert to the client @@ -1121,8 +1121,8 @@ process_certificate(#certificate_1_3{ {error, {certificate_required, State}}; process_certificate(#certificate_1_3{certificate_list = Certs0}, #state{ssl_options = - #ssl_options{signature_algs = SignAlgs, - signature_algs_cert = SignAlgsCert} = SslOptions, + #{signature_algs := SignAlgs, + signature_algs_cert := SignAlgsCert} = SslOptions, static_env = #static_env{ role = Role, @@ -1180,19 +1180,26 @@ update_encryption_state(client, State) -> State. -validate_certificate_chain(Certs, CertDbHandle, CertDbRef, SslOptions, CRLDbHandle, Role, Host) -> - ServerName = ssl_handshake:server_name(SslOptions#ssl_options.server_name_indication, Host, Role), +validate_certificate_chain(Certs, CertDbHandle, CertDbRef, + #{server_name_indication := ServerNameIndication, + partial_chain := PartialChain, + verify_fun := VerifyFun, + customize_hostname_check := CustomizeHostnameCheck, + crl_check := CrlCheck, + depth := Depth} = SslOptions, + CRLDbHandle, Role, Host) -> + ServerName = ssl_handshake:server_name(ServerNameIndication, Host, Role), [PeerCert | ChainCerts ] = Certs, try {TrustedCert, CertPath} = ssl_certificate:trusted_cert_and_path(Certs, CertDbHandle, CertDbRef, - SslOptions#ssl_options.partial_chain), + PartialChain), ValidationFunAndState = - ssl_handshake:validation_fun_and_state(SslOptions#ssl_options.verify_fun, Role, + ssl_handshake:validation_fun_and_state(VerifyFun, Role, CertDbHandle, CertDbRef, ServerName, - SslOptions#ssl_options.customize_hostname_check, - SslOptions#ssl_options.crl_check, CRLDbHandle, CertPath), - Options = [{max_path_length, SslOptions#ssl_options.depth}, + CustomizeHostnameCheck, + CrlCheck, CRLDbHandle, CertPath), + Options = [{max_path_length, Depth}, {verify_fun, ValidationFunAndState}], %% TODO: Validate if Certificate is using a supported signature algorithm %% (signature_algs_cert)! @@ -1531,9 +1538,7 @@ get_handshake_context_client(L) -> %% CertificateRequest message. verify_signature_algorithm(#state{ static_env = #static_env{role = Role}, - ssl_options = - #ssl_options{ - signature_algs = LocalSignAlgs}} = State0, + ssl_options = #{signature_algs := LocalSignAlgs}} = State0, #certificate_verify_1_3{algorithm = PeerSignAlg}) -> case lists:member(PeerSignAlg, LocalSignAlgs) of true -> diff --git a/lib/ssl/src/tls_record.erl b/lib/ssl/src/tls_record.erl index 2aeab98929..cfd75076b3 100644 --- a/lib/ssl/src/tls_record.erl +++ b/lib/ssl/src/tls_record.erl @@ -83,7 +83,7 @@ init_connection_states(Role, BeastMitigation) -> binary(), [tls_version()] | tls_version(), Buffer0 :: binary() | {'undefined' | #ssl_tls{}, {[binary()],non_neg_integer(),[binary()]}}, - #ssl_options{}) -> + ssl_options()) -> {Records :: [#ssl_tls{}], Buffer :: {'undefined' | #ssl_tls{}, {[binary()],non_neg_integer(),[binary()]}}} | #alert{}. @@ -495,7 +495,9 @@ validate_tls_record_version(Versions, Q, SslOpts, Acc, Type, Version, Length) -> validate_tls_record_length(_Versions, Q, _SslOpts, Acc, Type, Version, undefined) -> {lists:reverse(Acc), {#ssl_tls{type = Type, version = Version, fragment = undefined}, Q}}; -validate_tls_record_length(Versions, {_,Size0,_} = Q0, SslOpts, Acc, Type, Version, Length) -> +validate_tls_record_length(Versions, {_,Size0,_} = Q0, + #{log_level := LogLevel} = SslOpts, + Acc, Type, Version, Length) -> if Length =< ?MAX_CIPHER_TEXT_LENGTH -> if @@ -503,7 +505,7 @@ validate_tls_record_length(Versions, {_,Size0,_} = Q0, SslOpts, Acc, Type, Versi %% Complete record {Fragment, Q} = binary_from_front(Length, Q0), Record = #ssl_tls{type = Type, version = Version, fragment = Fragment}, - ssl_logger:debug(SslOpts#ssl_options.log_level, inbound, 'record', Record), + ssl_logger:debug(LogLevel, inbound, 'record', Record), decode_tls_records(Versions, Q, SslOpts, [Record|Acc], undefined, undefined, undefined); true -> {lists:reverse(Acc), diff --git a/lib/ssl/src/tls_socket.erl b/lib/ssl/src/tls_socket.erl index 6c32e6fa04..acd907905c 100644 --- a/lib/ssl/src/tls_socket.erl +++ b/lib/ssl/src/tls_socket.erl @@ -210,9 +210,9 @@ internal_inet_values() -> default_inet_values() -> [{packet_size, 0}, {packet,0}, {header, 0}, {active, true}, {mode, list}]. -inherit_tracker(ListenSocket, EmOpts, #ssl_options{erl_dist = false} = SslOpts) -> +inherit_tracker(ListenSocket, EmOpts, #{erl_dist := false} = SslOpts) -> ssl_listen_tracker_sup:start_child([ListenSocket, EmOpts, SslOpts]); -inherit_tracker(ListenSocket, EmOpts, #ssl_options{erl_dist = true} = SslOpts) -> +inherit_tracker(ListenSocket, EmOpts, #{erl_dist := true} = SslOpts) -> ssl_listen_tracker_sup:start_child_dist([ListenSocket, EmOpts, SslOpts]). get_emulated_opts(TrackerPid) -> diff --git a/lib/ssl/test/Makefile b/lib/ssl/test/Makefile index 0925c0facc..ec0addac59 100644 --- a/lib/ssl/test/Makefile +++ b/lib/ssl/test/Makefile @@ -60,7 +60,6 @@ MODULES = \ ssl_cert_SUITE\ openssl_server_cert_SUITE\ openssl_client_cert_SUITE\ - ssl_certificate_verify_SUITE\ ssl_crl_SUITE\ ssl_dist_SUITE \ ssl_dist_bench_SUITE \ diff --git a/lib/ssl/test/openssl_alpn_SUITE.erl b/lib/ssl/test/openssl_alpn_SUITE.erl index 5008dba922..a54286c2cd 100644 --- a/lib/ssl/test/openssl_alpn_SUITE.erl +++ b/lib/ssl/test/openssl_alpn_SUITE.erl @@ -85,13 +85,11 @@ alpn_npn_coexist() -> erlang_server_alpn_npn_openssl_client_alpn_npn ]. rengotiation_tests() -> - case ssl_test_lib:sane_openssl_alpn_npn_renegotiate() of - true -> - [erlang_client_alpn_openssl_server_alpn_renegotiate, - erlang_server_alpn_openssl_client_alpn_renegotiate]; - false -> - [] - end. + [ + erlang_client_alpn_openssl_server_alpn_renegotiate, + erlang_server_alpn_openssl_client_alpn_renegotiate + ]. + init_per_suite(Config0) -> case os:find_executable("openssl") of false -> @@ -148,15 +146,17 @@ init_per_testcase(TestCase, Config) -> ct:timetrap({seconds, 10}), special_init(TestCase, Config). -special_init(TestCase, Config) - when TestCase == erlang_client_alpn_openssl_server_alpn_renegotiate; - TestCase == erlang_server_alpn_openssl_client_alpn_renegotiate -> - {ok, Version} = application:get_env(ssl, protocol_version), +special_init(erlang_client_alpn_openssl_server_alpn_renegotiate, Config) -> + {ok, Version} = application:get_env(ssl, protocol_version), ssl_test_lib:check_sane_openssl_renegotaite(Config, Version); -special_init(TestCase, Config) - when TestCase == erlang_client_alpn_npn_openssl_server_alpn_npn; - TestCase == erlang_server_alpn_npn_openssl_client_alpn_npn -> - ssl_test_lib:check_openssl_npn_support(Config); +special_init(erlang_server_alpn_openssl_client_alpn_renegotiate, Config) -> + {ok, Version} = application:get_env(ssl, protocol_version), + case ssl_test_lib:check_sane_openssl_renegotaite(Config, Version) of + Config -> + ssl_test_lib:openssl_allows_client_renegotaite(Config); + Skip -> + Skip + end; special_init(_, Config) -> Config. diff --git a/lib/ssl/test/openssl_npn_SUITE.erl b/lib/ssl/test/openssl_npn_SUITE.erl index 0294f4997f..ed3d81eba7 100644 --- a/lib/ssl/test/openssl_npn_SUITE.erl +++ b/lib/ssl/test/openssl_npn_SUITE.erl @@ -55,13 +55,10 @@ npn_tests() -> erlang_client_openssl_server_npn_only_server]. npn_renegotiate_tests() -> - case ssl_test_lib:sane_openssl_alpn_npn_renegotiate() of - true -> - [erlang_server_openssl_client_npn_renegotiate, - erlang_client_openssl_server_npn_renegotiate]; - false -> - [] - end. + [ + erlang_server_openssl_client_npn_renegotiate, + erlang_client_openssl_server_npn_renegotiate + ]. init_per_suite(Config0) -> case os:find_executable("openssl") of @@ -119,13 +116,19 @@ init_per_testcase(TestCase, Config) -> ct:timetrap({seconds, 10}), special_init(TestCase, Config). -special_init(TestCase, Config) - when TestCase == erlang_client_npn_openssl_server_npn_renegotiate; - TestCase == erlang_server_npn_openssl_client_npn_renegotiate -> - {ok, Version} = application:get_env(ssl, protocol_version), +special_init(erlang_client_openssl_server_npn_renegotiate, Config) -> + {ok, Version} = application:get_env(ssl, protocol_version), ssl_test_lib:check_sane_openssl_renegotaite(Config, Version); +special_init(erlang_server_openssl_client_npn_renegotiate, Config) -> + {ok, Version} = application:get_env(ssl, protocol_version), + case ssl_test_lib:check_sane_openssl_renegotaite(Config, Version) of + Config -> + ssl_test_lib:openssl_allows_client_renegotaite(Config); + Skip -> + Skip + end; special_init(_, Config) -> - Config. + Config. end_per_testcase(_, Config) -> Config. diff --git a/lib/ssl/test/openssl_renegotiate_SUITE.erl b/lib/ssl/test/openssl_renegotiate_SUITE.erl index 787b5208b8..a5c6056d63 100644 --- a/lib/ssl/test/openssl_renegotiate_SUITE.erl +++ b/lib/ssl/test/openssl_renegotiate_SUITE.erl @@ -104,7 +104,8 @@ init_per_group(GroupName, Config) -> true -> case ssl_test_lib:check_sane_openssl_version(GroupName) of true -> - ssl_test_lib:check_sane_openssl_renegotaite(ssl_test_lib:init_tls_version(GroupName, Config), + ssl_test_lib:check_sane_openssl_renegotaite(ssl_test_lib:init_tls_version(GroupName, + Config), GroupName); false -> {skip, openssl_does_not_support_version} @@ -123,21 +124,12 @@ end_per_group(GroupName, Config) -> false -> Config end. - +init_per_testcase(erlang_client_openssl_server_nowrap_seqnum, Config) -> + ct:timetrap({seconds, 10}), + ssl_test_lib:openssl_allows_client_renegotaite(Config); init_per_testcase(TestCase, Config) -> ct:timetrap({seconds, 10}), - special_init(TestCase, Config). - -special_init(TestCase, Config) - when TestCase == erlang_client_openssl_server_renegotiate; - TestCase == erlang_client_openssl_server_nowrap_seqnum; - TestCase == erlang_server_openssl_client_nowrap_seqnum; - TestCase == erlang_client_openssl_server_renegotiate_after_client_data - -> - {ok, Version} = application:get_env(ssl, protocol_version), - ssl_test_lib:check_sane_openssl_renegotaite(Config, Version); -special_init(_, Config) -> - Config. + Config. end_per_testcase(_, Config) -> Config. @@ -287,7 +279,7 @@ erlang_client_openssl_server_nowrap_seqnum(Config) when is_list(Config) -> process_flag(trap_exit, false). %%-------------------------------------------------------------------- erlang_server_openssl_client_nowrap_seqnum() -> - [{doc, "Test that erlang client will renegotiate session when", + [{doc, "Test that erlang server will renegotiate session when", "max sequence number celing is about to be reached. Although" "in the testcase we use the test option renegotiate_at" " to lower treashold substantially."}]. diff --git a/lib/ssl/test/ssl_api_SUITE.erl b/lib/ssl/test/ssl_api_SUITE.erl index 14e5024b91..c0f23cce58 100644 --- a/lib/ssl/test/ssl_api_SUITE.erl +++ b/lib/ssl/test/ssl_api_SUITE.erl @@ -1946,7 +1946,7 @@ honor_cipher_order(Config, Honor, ServerCiphers, ClientCiphers, Expected) -> {host, Hostname}, {from, self()}, {mfa, {?MODULE, connection_info_result, []}}, - {options, [{ciphers, ClientCiphers}, {honor_cipher_order, Honor} + {options, [{ciphers, ClientCiphers} | ClientOpts]}]), Version = ssl_test_lib:protocol_version(Config), diff --git a/lib/ssl/test/ssl_cert_SUITE.erl b/lib/ssl/test/ssl_cert_SUITE.erl index fb1695f38a..d5ca9bcf02 100644 --- a/lib/ssl/test/ssl_cert_SUITE.erl +++ b/lib/ssl/test/ssl_cert_SUITE.erl @@ -44,17 +44,16 @@ all() -> groups() -> [ {'tlsv1.3', [], tls_1_3_protocol_groups()}, - {'tlsv1.2', [], pre_tls_1_3_protocol_groups()}, - {'tlsv1.1', [], pre_tls_1_3_protocol_groups()}, - {'tlsv1', [], pre_tls_1_3_protocol_groups()}, + {'tlsv1.2', [], tls_1_2_protocol_groups()}, + {'tlsv1.1', [], ssl_protocol_groups()}, + {'tlsv1', [], ssl_protocol_groups()}, {'sslv3', [], ssl_protocol_groups()}, - {'dtlsv1.2', [], pre_tls_1_3_protocol_groups()}, - {'dtlsv1', [], pre_tls_1_3_protocol_groups()}, - {rsa, [], all_version_tests()}, + {'dtlsv1.2', [], tls_1_2_protocol_groups()}, + {'dtlsv1', [], ssl_protocol_groups()}, + {rsa, [], all_version_tests() ++ rsa_tests() ++ pre_tls_1_3_rsa_tests()}, {ecdsa, [], all_version_tests()}, {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_1_3, [], all_version_tests() ++ rsa_tests() ++ tls_1_3_tests() ++ tls_1_3_rsa_tests()}, {ecdsa_1_3, [], all_version_tests() ++ tls_1_3_tests()} ]. @@ -62,7 +61,7 @@ ssl_protocol_groups() -> [{group, rsa}, {group, dsa}]. -pre_tls_1_3_protocol_groups() -> +tls_1_2_protocol_groups() -> [{group, rsa}, {group, ecdsa}, {group, dsa}]. @@ -80,6 +79,22 @@ tls_1_3_tests() -> hello_retry_client_auth_empty_cert_rejected ]. +pre_tls_1_3_rsa_tests() -> + [ + key_auth_ext_sign_only + ]. + +rsa_tests() -> + [ + longer_chain + ]. + +tls_1_3_rsa_tests() -> + [ + unsupported_sign_algo_client_auth, + unsupported_sign_algo_cert_client_auth + ]. + all_version_tests() -> [ no_auth, @@ -96,8 +111,18 @@ all_version_tests() -> missing_root_cert_auth_user_verify_fun_reject, verify_fun_always_run_client, verify_fun_always_run_server, - incomplete_chain_auth - %%invalid_signature_client + incomplete_chain_auth, + invalid_signature_client, + invalid_signature_server, + critical_extension_auth, + critical_extension_client_auth, + critical_extension_no_auth, + extended_key_usage_auth, + extended_key_usage_client_auth, + cert_expired, + client_auth_once, + no_auth_key_identifier_ext, + no_auth_key_identifier_ext_keyEncipherment ]. init_per_suite(Config) -> @@ -352,7 +377,8 @@ incomplete_chain_auth(Config) when is_list(Config) -> %%-------------------------------------------------------------------- verify_fun_always_run_client() -> - [{doc,"Verify that user verify_fun is always run (for valid and valid_peer not only unknown_extension)"}]. + [{doc,"Verify that user verify_fun is always run (for valid and " + "valid_peer not only unknown_extension)"}]. verify_fun_always_run_client(Config) when is_list(Config) -> ClientOpts = ssl_test_lib:ssl_options(client_cert_opts, Config), @@ -392,7 +418,8 @@ verify_fun_always_run_client(Config) when is_list(Config) -> %%-------------------------------------------------------------------- verify_fun_always_run_server() -> - [{doc,"Verify that user verify_fun is always run (for valid and valid_peer not only unknown_extension)"}]. + [{doc,"Verify that user verify_fun is always run (for valid and " + "valid_peer not only unknown_extension)"}]. verify_fun_always_run_server(Config) when is_list(Config) -> ClientOpts = ssl_test_lib:ssl_options(client_cert_opts, Config), ServerOpts = ssl_test_lib:ssl_options(server_cert_opts, Config), @@ -434,12 +461,318 @@ verify_fun_always_run_server(Config) when is_list(Config) -> invalid_signature_client() -> ssl_cert_tests:invalid_signature_client(). invalid_signature_client(Config) when is_list(Config) -> + ssl:clear_pem_cache(), ssl_cert_tests:invalid_signature_client(Config). %%-------------------------------------------------------------------- invalid_signature_server() -> - ssl_cert_tests:invalid_signature_client(). + ssl_cert_tests:invalid_signature_server(). invalid_signature_server(Config) when is_list(Config) -> - ssl_cert_tests:invalid_signature_client(Config). + ssl:clear_pem_cache(), + ssl_cert_tests:invalid_signature_server(Config). + +%%-------------------------------------------------------------------- +critical_extension_auth() -> + [{doc,"Test cert that has a critical unknown extension in verify_peer mode"}]. + +critical_extension_auth(Config) when is_list(Config) -> + DefaultCertConf = ssl_test_lib:default_cert_chain_conf(), + Ext = x509_test:extensions([{{2,16,840,1,113730,1,1}, <<3,2,6,192>>, true}]), + #{client_config := ClientOpts0, + server_config := ServerOpts0} = ssl_test_lib:make_cert_chains_der(proplists:get_value(cert_key_alg, Config), + [{server_chain, + [[],[],[{extensions, Ext}]]}, + {client_chain, DefaultCertConf}]), + ClientOpts = ssl_test_lib:ssl_options(ClientOpts0, Config), + ServerOpts = ssl_test_lib:ssl_options(ServerOpts0, Config), + + {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config), + + Server = ssl_test_lib:start_server_error( + [{node, ServerNode}, {port, 0}, + {from, self()}, + {mfa, {ssl_test_lib, no_result, []}}, + {options, [{verify, verify_none} | ServerOpts]}]), + Port = ssl_test_lib:inet_port(Server), + Client = ssl_test_lib:start_client_error( + [{node, ClientNode}, {port, Port}, + {host, Hostname}, + {from, self()}, + {mfa, {ssl_test_lib, no_result, []}}, + {options, [{verify, verify_peer} | ClientOpts]}]), + + ssl_test_lib:check_client_alert(Server, Client, unsupported_certificate). + +%%-------------------------------------------------------------------- +critical_extension_client_auth() -> + [{doc,"Test cert that has a critical unknown extension in verify_peer mode"}]. + +critical_extension_client_auth(Config) when is_list(Config) -> + DefaultCertConf = ssl_test_lib:default_cert_chain_conf(), + Ext = x509_test:extensions([{{2,16,840,1,113730,1,1}, <<3,2,6,192>>, true}]), + #{client_config := ClientOpts0, + server_config := ServerOpts0} = ssl_test_lib:make_cert_chains_der(proplists:get_value(cert_key_alg, Config), + [{client_chain, + [[],[],[{extensions, Ext}]]}, + {server_chain, DefaultCertConf}]), + ClientOpts = ssl_test_lib:ssl_options(ClientOpts0, Config), + ServerOpts = ssl_test_lib:ssl_options(ServerOpts0, Config), + + {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config), + + Server = ssl_test_lib:start_server_error( + [{node, ServerNode}, {port, 0}, + {from, self()}, + {mfa, {ssl_test_lib, no_result, []}}, + {options, [{verify, verify_peer} | ServerOpts]}]), + Port = ssl_test_lib:inet_port(Server), + Client = ssl_test_lib:start_client_error( + [{node, ClientNode}, {port, Port}, + {host, Hostname}, + {from, self()}, + {mfa, {ssl_test_lib, no_result, []}}, + {options, [{verify, verify_none} | ClientOpts]}]), + + %% This certificate has a critical extension that we don't + %% understand. Therefore, verification should fail. + ssl_test_lib:check_server_alert(Server, Client, unsupported_certificate). + +%%-------------------------------------------------------------------- +critical_extension_no_auth() -> + [{doc,"Test cert that has a critical unknown extension in verify_none mode"}]. + +critical_extension_no_auth(Config) when is_list(Config) -> + DefaultCertConf = ssl_test_lib:default_cert_chain_conf(), + Ext = x509_test:extensions([{{2,16,840,1,113730,1,1}, <<3,2,6,192>>, true}]), + #{client_config := ClientOpts0, + server_config := ServerOpts0} = ssl_test_lib:make_cert_chains_der(proplists:get_value(cert_key_alg, Config), + [{server_chain, + [[],[], [{extensions, Ext}]]}, + {client_chain, DefaultCertConf}]), + ClientOpts = [{verify, verify_none} | ssl_test_lib:ssl_options(ClientOpts0, Config)], + ServerOpts = [{verify, verify_none} | ssl_test_lib:ssl_options(ServerOpts0, Config)], + + ssl_test_lib:basic_test(ClientOpts, ServerOpts, Config). + + +%%-------------------------------------------------------------------- +extended_key_usage_auth() -> + [{doc,"Test cert that has a critical extended_key_usage extension in server cert"}]. + +extended_key_usage_auth(Config) when is_list(Config) -> + DefaultCertConf = ssl_test_lib:default_cert_chain_conf(), + Ext = x509_test:extensions([{?'id-ce-extKeyUsage', + [?'id-kp-serverAuth'], true}]), + #{client_config := ClientOpts0, + server_config := ServerOpts0} = ssl_test_lib:make_cert_chains_der(proplists:get_value(cert_key_alg, Config), + [{server_chain, + [[],[], [{extensions, Ext}]]}, + {client_chain, DefaultCertConf} + ]), + ClientOpts = ssl_test_lib:ssl_options(ClientOpts0, Config), + ServerOpts = ssl_test_lib:ssl_options(ServerOpts0, 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, send_recv_result_active, []}}, + {options, [{verify, verify_none} | ServerOpts]}]), + Port = ssl_test_lib:inet_port(Server), + Client = ssl_test_lib:start_client([{node, ClientNode}, {port, Port}, + {host, Hostname}, + {from, self()}, + {mfa, {ssl_test_lib, send_recv_result_active, []}}, + {options, [{verify, verify_peer} | + ClientOpts]}]), + + ssl_test_lib:check_result(Server, ok, Client, ok), + + ssl_test_lib:close(Server), + ssl_test_lib:close(Client). + +%%-------------------------------------------------------------------- +extended_key_usage_client_auth() -> + [{doc,"Test cert that has a critical extended_key_usage extension in client and server cert"}]. + +extended_key_usage_client_auth(Config) when is_list(Config) -> + ServerExt = x509_test:extensions([{?'id-ce-extKeyUsage', + [?'id-kp-serverAuth'], true}]), + ClientExt = x509_test:extensions([{?'id-ce-extKeyUsage', + [?'id-kp-clientAuth'], true}]), + #{client_config := ClientOpts0, + server_config := ServerOpts0} = ssl_test_lib:make_cert_chains_der(proplists:get_value(cert_key_alg, Config), + [{client_chain, [[],[],[{extensions, ClientExt}]]}, + {server_chain, [[],[],[{extensions, ServerExt}]]}]), + ClientOpts = ssl_test_lib:ssl_options(ClientOpts0, Config), + ServerOpts = ssl_test_lib:ssl_options(ServerOpts0, 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, send_recv_result_active, []}}, + {options, [{verify, verify_peer} | ServerOpts]}]), + Port = ssl_test_lib:inet_port(Server), + Client = ssl_test_lib:start_client([{node, ClientNode}, {port, Port}, + {host, Hostname}, + {from, self()}, + {mfa, {ssl_test_lib, send_recv_result_active, []}}, + {options, [{verify, verify_peer} | ClientOpts]}]), + + ssl_test_lib:check_result(Server, ok, Client, ok), + + ssl_test_lib:close(Server), + ssl_test_lib:close(Client). + +%%-------------------------------------------------------------------- +cert_expired() -> + [{doc,"Test server with expired certificate"}]. + +cert_expired(Config) when is_list(Config) -> + DefaultCertConf = ssl_test_lib:default_cert_chain_conf(), + {Year, Month, Day} = date(), + #{client_config := ClientOpts0, + server_config := ServerOpts0} = ssl_test_lib:make_cert_chains_der(proplists:get_value(cert_key_alg, Config), + [{server_chain, + [[], + [{validity, {{Year-2, Month, Day}, + {Year-1, Month, Day}}}], + [] + ]}, + {client_chain, DefaultCertConf}]), + ClientOpts = ssl_test_lib:ssl_options(ClientOpts0, Config), + ServerOpts = ssl_test_lib:ssl_options(ServerOpts0, Config), + + {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config), + + Server = ssl_test_lib:start_server_error([{node, ServerNode}, {port, 0}, + {from, self()}, + {options, ServerOpts}]), + Port = ssl_test_lib:inet_port(Server), + Client = ssl_test_lib:start_client_error([{node, ClientNode}, {port, Port}, + {host, Hostname}, + {from, self()}, + {options, [{verify, verify_peer} | ClientOpts]}]), + + ssl_test_lib:check_client_alert(Server, Client, certificate_expired). + +%%-------------------------------------------------------------------- +client_auth_once() -> + [{doc,"Test server option verify_client_once"}]. + +client_auth_once(Config) when is_list(Config) -> + ClientOpts = ssl_test_lib:ssl_options(client_cert_opts, Config), + ServerOpts = ssl_test_lib:ssl_options(server_cert_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, send_recv_result_active, []}}, + {options, [{verify, verify_peer}, + {verify_client_once, true} + | ServerOpts]}]), + Port = ssl_test_lib:inet_port(Server), + Client0 = ssl_test_lib:start_client([{node, ClientNode}, {port, Port}, + {host, Hostname}, + {from, self()}, + {mfa, {ssl_test_lib, send_recv_result_active, []}}, + {options, ClientOpts}]), + + ssl_test_lib:check_result(Server, ok, Client0, ok), + Server ! {listen, {mfa, {ssl_test_lib, send_recv_result_active, []}}}, + ssl_test_lib:close(Client0), + Client1 = ssl_test_lib:start_client([{node, ClientNode}, {port, Port}, + {host, Hostname}, + {from, self()}, + {mfa, {ssl_test_lib, send_recv_result_active, []}}, + {options, ClientOpts}]), + + ssl_test_lib:check_result(Client1, ok, Server, ok), + ssl_test_lib:close(Server), + ssl_test_lib:close(Client1). + +%%-------------------------------------------------------------------- +no_auth_key_identifier_ext() -> + [{doc, "Test cert that does not have authorityKeyIdentifier extension"}]. + +no_auth_key_identifier_ext(Config) when is_list(Config) -> + DefaultCertConf = ssl_test_lib:default_cert_chain_conf(), + #{client_config := ClientOpts0, + server_config := ServerOpts0} = + ssl_test_lib:make_cert_chains_der(proplists:get_value(cert_key_alg, Config), + [{client_chain, DefaultCertConf}, + {server_chain, DefaultCertConf}]), + ClientOpts = [{verify, verify_peer} | ssl_test_lib:ssl_options(ClientOpts0, Config)], + ServerOpts = [{verify, verify_peer} | ssl_test_lib:ssl_options(ServerOpts0, Config)], + + ssl_test_lib:basic_test(ClientOpts, ServerOpts, Config). + +%%-------------------------------------------------------------------- +no_auth_key_identifier_ext_keyEncipherment() -> + [{doc, "Test cert with keyEncipherment key_usage an no" + " authorityKeyIdentifier extension"}]. + +no_auth_key_identifier_ext_keyEncipherment(Config) when is_list(Config) -> + DefaultCertConf = ssl_test_lib:default_cert_chain_conf(), + ClientExt = x509_test:extensions([{key_usage, [digitalSignature, keyEncipherment]}]), + #{client_config := ClientOpts0, + server_config := ServerOpts0} = + ssl_test_lib:make_cert_chains_der(proplists:get_value(cert_key_alg, Config), + [{client_chain, + [[],[],[{extensions, ClientExt}]]}, + {server_chain, DefaultCertConf} + ]), + ClientOpts = [{verify, verify_peer} | ssl_test_lib:ssl_options(ClientOpts0, Config)], + ServerOpts = [{verify, verify_peer} | ssl_test_lib:ssl_options(ServerOpts0, Config)], + + ssl_test_lib:basic_test(ClientOpts, ServerOpts, Config). + +%%-------------------------------------------------------------------- +key_auth_ext_sign_only() -> + [{doc, "Test that client with a certificate without keyEncipherment usage " + " extension can connect to a server with restricted cipher suites "}]. +key_auth_ext_sign_only(Config) when is_list(Config) -> + DefaultCertConf = ssl_test_lib:default_cert_chain_conf(), + ClientExt = x509_test:extensions([{key_usage, [digitalSignature]}]), + #{client_config := ClientOpts0, + server_config := ServerOpts0} = + ssl_test_lib:make_cert_chains_der(proplists:get_value(cert_key_alg, Config), + [{client_chain, + [[],[],[{extensions, ClientExt}]]}, + {server_chain, DefaultCertConf} + ]), + Version = proplists:get_value(version, Config), + ClientOpts = [{verify, verify_peer} | ssl_test_lib:ssl_options(ClientOpts0, Config)], + ServerOpts = [{verify, verify_peer}, {ciphers, + ssl_test_lib:rsa_non_signed_suites(n_version(Version))} + | ssl_test_lib:ssl_options(ServerOpts0, Config)], + + ssl_test_lib:basic_test(ClientOpts, ServerOpts, Config). + +%%-------------------------------------------------------------------- +longer_chain() -> + [{doc,"Test depth option"}]. +longer_chain(Config) when is_list(Config) -> + #{server_config := ServerOpts0, + client_config := ClientOpts0} = + public_key:pkix_test_data(#{server_chain => #{root => [{key, ssl_test_lib:hardcode_rsa_key(1)}], + intermediates => [[{key, ssl_test_lib:hardcode_rsa_key(2)}], + [{key, ssl_test_lib:hardcode_rsa_key(3)}], + [{key, ssl_test_lib:hardcode_rsa_key(4)}]], + peer => [{key, ssl_test_lib:hardcode_rsa_key(5)}]}, + client_chain => #{root => [{key, ssl_test_lib:hardcode_rsa_key(3)}], + intermediates => [[{key, ssl_test_lib:hardcode_rsa_key(2)}]], + peer => [{key, ssl_test_lib:hardcode_rsa_key(1)}]}}), + [ServerRoot| _] = ServerCas = proplists:get_value(cacerts, ServerOpts0), + ClientCas = proplists:get_value(cacerts, ClientOpts0), + + ServerOpts = ssl_test_lib:ssl_options([{verify, verify_peer}, {cacerts, [ServerRoot]} | + proplists:delete(cacerts, ServerOpts0)], Config), + ClientOpts = ssl_test_lib:ssl_options([{verify, verify_peer}, + {depth, 5}, + {cacerts, ServerCas ++ ClientCas} | + proplists:delete(cacerts, ClientOpts0)], Config), + ssl_test_lib:basic_test(ClientOpts, ServerOpts, Config). %%-------------------------------------------------------------------- %% TLS 1.3 Test cases ----------------------------------------------- @@ -454,7 +787,7 @@ hello_retry_request(Config) -> ServerOpts = [{versions, ['tlsv1.2','tlsv1.3']}, {supported_groups, [x448, x25519]}|ServerOpts0], ClientOpts = [{versions, ['tlsv1.2','tlsv1.3']}, - {supported_groups, [secp256r1, x25519]}|ClientOpts0], + {supported_groups, [secp256r1, x25519]} | ClientOpts0], ssl_test_lib:basic_test(ClientOpts, ServerOpts, Config). %%-------------------------------------------------------------------- custom_groups() -> @@ -561,3 +894,29 @@ hello_retry_client_auth_empty_cert_rejected(Config) -> {supported_groups, [secp256r1, x25519]}|ClientOpts2], ssl_test_lib:basic_alert(ClientOpts, ServerOpts, Config, certificate_required). + +%%-------------------------------------------------------------------- +%% Internal functions ----------------------------------------------- +%%-------------------------------------------------------------------- +two_digits_str(N) when N < 10 -> + lists:flatten(io_lib:format("0~p", [N])); +two_digits_str(N) -> + lists:flatten(io_lib:format("~p", [N])). + +delete_authority_key_extension([], Acc) -> + lists:reverse(Acc); +delete_authority_key_extension([#'Extension'{extnID = ?'id-ce-authorityKeyIdentifier'} | Rest], + Acc) -> + delete_authority_key_extension(Rest, Acc); +delete_authority_key_extension([Head | Rest], Acc) -> + delete_authority_key_extension(Rest, [Head | Acc]). + +n_version(Version) when Version == 'tlsv1.2'; + Version == 'tlsv1.1'; + Version == 'tlsv1'; + Version == 'sslv3' + -> + tls_record:protocol_version(Version); +n_version(Version) when Version == 'dtlsv1.2'; + Version == 'dtlsv1' -> + dtls_record:protocol_version(Version). diff --git a/lib/ssl/test/ssl_certificate_verify_SUITE.erl b/lib/ssl/test/ssl_certificate_verify_SUITE.erl deleted file mode 100644 index f38858e0bf..0000000000 --- a/lib/ssl/test/ssl_certificate_verify_SUITE.erl +++ /dev/null @@ -1,608 +0,0 @@ -%% -%% %CopyrightBegin% -%% -%% Copyright Ericsson AB 2012-2018. 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% -%% - -%% --module(ssl_certificate_verify_SUITE). - -%% Note: This directive should only be used in test suites. --compile(export_all). - --include_lib("common_test/include/ct.hrl"). --include_lib("public_key/include/public_key.hrl"). - --include("ssl_internal.hrl"). --include("ssl_alert.hrl"). --include("ssl_internal.hrl"). --include("tls_record.hrl"). --include("tls_handshake.hrl"). - --define(LONG_TIMEOUT, 600000). - -%%-------------------------------------------------------------------- -%% Common Test interface functions ----------------------------------- -%%-------------------------------------------------------------------- -all() -> - [ - {group, 'tlsv1.3'}, - {group, 'tlsv1.2'}, - {group, 'tlsv1.1'}, - {group, 'tlsv1'}, - {group, 'sslv3'}, - {group, 'dtlsv1.2'}, - {group, 'dtlsv1'} - ]. - -groups() -> - [ - {'tlsv1.3', [], all_protocol_groups()}, - {'tlsv1.2', [], all_protocol_groups()}, - {'tlsv1.1', [], all_protocol_groups()}, - {'tlsv1', [], all_protocol_groups()}, - {'sslv3', [], all_protocol_groups()}, - {'dtlsv1.2', [], all_protocol_groups()}, - {'dtlsv1', [], all_protocol_groups()}, - {active, [], tests()}, - {active_once, [], tests()}, - {passive, [], tests()}, - {error_handling, [],error_handling_tests()} - ]. - -all_protocol_groups() -> - [{group, active}, - {group, passive}, - {group, active_once}, - {group, error_handling}]. - -tests() -> - [cert_expired, - %invalid_signature_client, - %%invalid_signature_server, - extended_key_usage_verify_both, - extended_key_usage_verify_server, - critical_extension_verify_client, - critical_extension_verify_server, - critical_extension_verify_none, - long_chain - ]. - -error_handling_tests()-> - [client_with_cert_cipher_suites_handshake, - %%unknown_server_ca_accept_backwardscompatibility, - no_authority_key_identifier, - no_authority_key_identifier_keyEncipherment]. - -init_per_suite(Config) -> - catch crypto:stop(), - try crypto:start() of - ok -> - ssl_test_lib:clean_start(), - ssl_test_lib:make_rsa_cert(Config) - catch _:_ -> - {skip, "Crypto did not start"} - end. - -end_per_suite(_Config) -> - ssl:stop(), - application:stop(crypto). - -init_per_group(active, Config) -> - [{active, true}, {receive_function, send_recv_result_active} | Config]; -init_per_group(active_once, Config) -> - [{active, once}, {receive_function, send_recv_result_active_once} | Config]; -init_per_group(passive, Config) -> - [{active, false}, {receive_function, send_recv_result} | Config]; -init_per_group(error_handling, Config) -> - [{active, false}, {receive_function, send_recv_result} | Config]; -init_per_group(GroupName, Config) -> - case ssl_test_lib:is_tls_version(GroupName) of - true -> - case ssl_test_lib:sufficient_crypto_support(GroupName) of - true -> - [{version, GroupName} | ssl_test_lib:init_tls_version(GroupName, Config)]; - false -> - {skip, "Missing crypto support"} - end - end. - -end_per_group(GroupName, Config) -> - case ssl_test_lib:is_tls_version(GroupName) of - true -> - ssl_test_lib:clean_tls_version(Config); - false -> - Config - end. - -init_per_testcase(_TestCase, Config) -> - ssl:stop(), - ssl:start(), - ssl_test_lib:ct_log_supported_protocol_versions(Config), - ct:pal(" ~p", [ dtls_record:supported_protocol_versions()]), - ct:timetrap({seconds, 10}), - Config. - -end_per_testcase(_TestCase, Config) -> - Config. - -%%-------------------------------------------------------------------- -%% Test Cases -------------------------------------------------------- -%%-------------------------------------------------------------------- -server_verify_client_once() -> - [{doc,"Test server option verify_client_once"}]. - -server_verify_client_once(Config) when is_list(Config) -> - ClientOpts = ssl_test_lib:ssl_options(client_rsa_opts, []), - ServerOpts = ssl_test_lib:ssl_options(server_rsa_opts, Config), - Active = proplists:get_value(active, Config), - ReceiveFunction = proplists:get_value(receive_function, 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, ReceiveFunction, []}}, - {options, [{active, Active}, {verify, verify_peer}, - {verify_client_once, true} - | ServerOpts]}]), - Port = ssl_test_lib:inet_port(Server), - Client0 = ssl_test_lib:start_client([{node, ClientNode}, {port, Port}, - {host, Hostname}, - {from, self()}, - {mfa, {ssl_test_lib, ReceiveFunction, []}}, - {options, [{active, Active} | ClientOpts]}]), - - ssl_test_lib:check_result(Server, ok, Client0, ok), - Server ! {listen, {mfa, {ssl_test_lib, no_result, []}}}, - ssl_test_lib:close(Client0), - Client1 = ssl_test_lib:start_client([{node, ClientNode}, {port, Port}, - {host, Hostname}, - {from, self()}, - {mfa, {?MODULE, result_ok, []}}, - {options, [{active, Active} | ClientOpts]}]), - - ssl_test_lib:check_result(Client1, ok), - ssl_test_lib:close(Server), - ssl_test_lib:close(Client1). - -%%-------------------------------------------------------------------- - -cert_expired() -> - [{doc,"Test server with expired certificate"}]. - -cert_expired(Config) when is_list(Config) -> - {Year, Month, Day} = date(), - Active = proplists:get_value(active, Config), - {ClientOpts0, ServerOpts0} = ssl_test_lib:make_rsa_cert_chains([{server_chain, - [[], - [{validity, {{Year-2, Month, Day}, - {Year-1, Month, Day}}}], - [] - ]}], - Config, "_expired"), - ClientOpts = ssl_test_lib:ssl_options(ClientOpts0, Config), - ServerOpts = ssl_test_lib:ssl_options(ServerOpts0, Config), - - {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config), - - Server = ssl_test_lib:start_server_error([{node, ServerNode}, {port, 0}, - {from, self()}, - {options, [{active, Active}| ServerOpts]}]), - Port = ssl_test_lib:inet_port(Server), - Client = ssl_test_lib:start_client_error([{node, ClientNode}, {port, Port}, - {host, Hostname}, - {from, self()}, - {options, [{verify, verify_peer}, {active, Active} | ClientOpts]}]), - - ssl_test_lib:check_client_alert(Server, Client, certificate_expired). - -two_digits_str(N) when N < 10 -> - lists:flatten(io_lib:format("0~p", [N])); -two_digits_str(N) -> - lists:flatten(io_lib:format("~p", [N])). - -%%-------------------------------------------------------------------- -extended_key_usage_verify_server() -> - [{doc,"Test cert that has a critical extended_key_usage extension in server cert"}]. - -extended_key_usage_verify_server(Config) when is_list(Config) -> - Ext = x509_test:extensions([{?'id-ce-extKeyUsage', - [?'id-kp-serverAuth'], true}]), - {ClientOpts0, ServerOpts0} = ssl_test_lib:make_rsa_cert_chains([{server_chain, - [[],[], [{extensions, Ext}]]}], Config, - "_keyusage_server"), - ClientOpts = ssl_test_lib:ssl_options(ClientOpts0, Config), - ServerOpts = ssl_test_lib:ssl_options(ServerOpts0, Config), - Active = proplists:get_value(active, Config), - ReceiveFunction = proplists:get_value(receive_function, 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, ReceiveFunction, []}}, - {options, [{verify, verify_none}, {active, Active} | ServerOpts]}]), - Port = ssl_test_lib:inet_port(Server), - Client = ssl_test_lib:start_client([{node, ClientNode}, {port, Port}, - {host, Hostname}, - {from, self()}, - {mfa, {ssl_test_lib, ReceiveFunction, []}}, - {options, [{verify, verify_peer}, {active, Active} | - ClientOpts]}]), - - ssl_test_lib:check_result(Server, ok, Client, ok), - - ssl_test_lib:close(Server), - ssl_test_lib:close(Client). - -%%-------------------------------------------------------------------- -extended_key_usage_verify_both() -> - [{doc,"Test cert that has a critical extended_key_usage extension in client verify_peer mode"}]. - -extended_key_usage_verify_both(Config) when is_list(Config) -> - ServerExt = x509_test:extensions([{?'id-ce-extKeyUsage', - [?'id-kp-serverAuth'], true}]), - ClientExt = x509_test:extensions([{?'id-ce-extKeyUsage', - [?'id-kp-clientAuth'], true}]), - {ClientOpts0, ServerOpts0} = ssl_test_lib:make_rsa_cert_chains([{client_chain, [[],[],[{extensions, ClientExt}]]}, - {server_chain, [[],[],[{extensions, ServerExt}]]}], - Config, "_keyusage_both"), - ClientOpts = ssl_test_lib:ssl_options(ClientOpts0, Config), - ServerOpts = ssl_test_lib:ssl_options(ServerOpts0, Config), - Active = proplists:get_value(active, Config), - ReceiveFunction = proplists:get_value(receive_function, 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, ReceiveFunction, []}}, - {options, [{verify, verify_peer}, {active, Active} | ServerOpts]}]), - Port = ssl_test_lib:inet_port(Server), - Client = ssl_test_lib:start_client([{node, ClientNode}, {port, Port}, - {host, Hostname}, - {from, self()}, - {mfa, {ssl_test_lib, ReceiveFunction, []}}, - {options, [{verify, verify_peer}, {active, Active} | ClientOpts]}]), - - ssl_test_lib:check_result(Server, ok, Client, ok), - - ssl_test_lib:close(Server), - ssl_test_lib:close(Client). - -%%-------------------------------------------------------------------- -critical_extension_verify_server() -> - [{doc,"Test cert that has a critical unknown extension in verify_peer mode"}]. - -critical_extension_verify_server(Config) when is_list(Config) -> - Ext = x509_test:extensions([{{2,16,840,1,113730,1,1}, <<3,2,6,192>>, true}]), - {ClientOpts0, ServerOpts0} = ssl_test_lib:make_rsa_cert_chains([{client_chain, - [[],[], [{extensions, Ext}]]}], - Config, "_client_unknown_extension"), - ClientOpts = ssl_test_lib:ssl_options(ClientOpts0, Config), - ServerOpts = ssl_test_lib:ssl_options(ServerOpts0, Config), - Active = proplists:get_value(active, Config), - ReceiveFunction = proplists:get_value(receive_function, Config), - - {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config), - - Server = ssl_test_lib:start_server_error( - [{node, ServerNode}, {port, 0}, - {from, self()}, - {mfa, {ssl_test_lib, ReceiveFunction, []}}, - {options, [{verify, verify_peer}, {active, Active} | ServerOpts]}]), - Port = ssl_test_lib:inet_port(Server), - Client = ssl_test_lib:start_client_error( - [{node, ClientNode}, {port, Port}, - {host, Hostname}, - {from, self()}, - {mfa, {ssl_test_lib, ReceiveFunction, []}}, - {options, [{verify, verify_none}, {active, Active} | ClientOpts]}]), - - %% This certificate has a critical extension that we don't - %% understand. Therefore, verification should fail. - ssl_test_lib:check_server_alert(Server, Client, unsupported_certificate). -%%-------------------------------------------------------------------- - -critical_extension_verify_client() -> - [{doc,"Test cert that has a critical unknown extension in verify_peer mode"}]. - -critical_extension_verify_client(Config) when is_list(Config) -> - Ext = x509_test:extensions([{{2,16,840,1,113730,1,1}, <<3,2,6,192>>, true}]), - {ClientOpts0, ServerOpts0} = ssl_test_lib:make_rsa_cert_chains([{server_chain, - [[],[],[{extensions, Ext}]]}], - Config, "_server_unknown_extensions"), - ClientOpts = ssl_test_lib:ssl_options(ClientOpts0, Config), - ServerOpts = ssl_test_lib:ssl_options(ServerOpts0, Config), - Active = proplists:get_value(active, Config), - ReceiveFunction = proplists:get_value(receive_function, Config), - - {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config), - - Server = ssl_test_lib:start_server_error( - [{node, ServerNode}, {port, 0}, - {from, self()}, - {mfa, {ssl_test_lib, ReceiveFunction, []}}, - {options, [{verify, verify_none}, {active, Active} | ServerOpts]}]), - Port = ssl_test_lib:inet_port(Server), - Client = ssl_test_lib:start_client_error( - [{node, ClientNode}, {port, Port}, - {host, Hostname}, - {from, self()}, - {mfa, {ssl_test_lib, ReceiveFunction, []}}, - {options, [{verify, verify_peer}, {active, Active} | ClientOpts]}]), - - ssl_test_lib:check_client_alert(Server, Client, unsupported_certificate). - -%%-------------------------------------------------------------------- -critical_extension_verify_none() -> - [{doc,"Test cert that has a critical unknown extension in verify_none mode"}]. - -critical_extension_verify_none(Config) when is_list(Config) -> - Ext = x509_test:extensions([{{2,16,840,1,113730,1,1}, <<3,2,6,192>>, true}]), - {ClientOpts0, ServerOpts0} = ssl_test_lib:make_rsa_cert_chains([{server_chain, - [[],[], [{extensions, Ext}]]}], - Config, "_unknown_extensions"), - ClientOpts = ssl_test_lib:ssl_options(ClientOpts0, Config), - ServerOpts = ssl_test_lib:ssl_options(ServerOpts0, Config), - Active = proplists:get_value(active, Config), - ReceiveFunction = proplists:get_value(receive_function, 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, ReceiveFunction, []}}, - {options, [{verify, verify_none}, {active, Active} | ServerOpts]}]), - Port = ssl_test_lib:inet_port(Server), - Client = ssl_test_lib:start_client( - [{node, ClientNode}, {port, Port}, - {host, Hostname}, - {from, self()}, - {mfa, {ssl_test_lib, ReceiveFunction, []}}, - {options, [{verify, verify_none}, {active, Active} | ClientOpts]}]), - - %% This certificate has a critical extension that we don't - %% understand. But we're using `verify_none', so verification - %% shouldn't fail. - ssl_test_lib:check_result(Server, ok, Client, ok), - - ssl_test_lib:close(Server), - ssl_test_lib:close(Client). - -%%-------------------------------------------------------------------- -no_authority_key_identifier() -> - [{doc, "Test cert that does not have authorityKeyIdentifier extension" - " but are present in trusted certs db."}]. - -no_authority_key_identifier(Config) when is_list(Config) -> - {ClientOpts0, ServerOpts0} = ssl_test_lib:make_rsa_cert_chains([], Config, "_peer_no_auth_key_id"), - ClientOpts = ssl_test_lib:ssl_options(ClientOpts0, Config), - ServerOpts = ssl_test_lib:ssl_options(ServerOpts0, 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, send_recv_result_active, []}}, - {options, ServerOpts}]), - Port = ssl_test_lib:inet_port(Server), - Client = ssl_test_lib:start_client([{node, ClientNode}, {port, Port}, - {host, Hostname}, - {from, self()}, - {mfa, {ssl_test_lib, send_recv_result_active, []}}, - {options, [{verify, verify_peer} | ClientOpts]}]), - - ssl_test_lib:check_result(Server, ok, Client, ok), - - ssl_test_lib:close(Server), - ssl_test_lib:close(Client). - -delete_authority_key_extension([], Acc) -> - lists:reverse(Acc); -delete_authority_key_extension([#'Extension'{extnID = ?'id-ce-authorityKeyIdentifier'} | Rest], - Acc) -> - delete_authority_key_extension(Rest, Acc); -delete_authority_key_extension([Head | Rest], Acc) -> - delete_authority_key_extension(Rest, [Head | Acc]). - -%%-------------------------------------------------------------------- - -no_authority_key_identifier_keyEncipherment() -> - [{doc, "Test cert with keyEncipherment key_usage an no" - " authorityKeyIdentifier extension, but are present in trusted certs db."}]. - -no_authority_key_identifier_keyEncipherment(Config) when is_list(Config) -> - ClientExt = x509_test:extensions([{key_usage, [digitalSignature, keyEncipherment]}]), - {ClientOpts0, ServerOpts0} = ssl_test_lib:make_rsa_cert_chains([{client_chain, - [[],[],[{extensions, ClientExt}]]}], - Config, "_peer_keyEncipherment"), - ClientOpts = ssl_test_lib:ssl_options(ClientOpts0, Config), - ServerOpts = ssl_test_lib:ssl_options(ServerOpts0, 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, - send_recv_result_active, []}}, - {options, [{active, true} | ServerOpts]}]), - Port = ssl_test_lib:inet_port(Server), - Client = ssl_test_lib:start_client([{node, ClientNode}, {port, Port}, - {host, Hostname}, - {from, self()}, - {mfa, {ssl_test_lib, - send_recv_result_active, []}}, - {options, [{verify, verify_peer} | ClientOpts]}]), - ssl_test_lib:check_result(Server, ok, Client, ok), - ssl_test_lib:close(Server), - ssl_test_lib:close(Client). - - -%%-------------------------------------------------------------------- - -invalid_signature_server() -> - [{doc,"Test client with invalid signature"}]. - -invalid_signature_server(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), - PrivDir = proplists:get_value(priv_dir, Config), - - KeyFile = proplists:get_value(keyfile, ServerOpts), - [KeyEntry] = ssl_test_lib:pem_to_der(KeyFile), - Key = ssl_test_lib:public_key(public_key:pem_entry_decode(KeyEntry)), - - ServerCertFile = proplists:get_value(certfile, ServerOpts), - NewServerCertFile = filename:join(PrivDir, "server_invalid_cert.pem"), - [{'Certificate', ServerDerCert, _}] = ssl_test_lib:pem_to_der(ServerCertFile), - ServerOTPCert = public_key:pkix_decode_cert(ServerDerCert, otp), - ServerOTPTbsCert = ServerOTPCert#'OTPCertificate'.tbsCertificate, - NewServerDerCert = public_key:pkix_sign(ServerOTPTbsCert, Key), - ssl_test_lib:der_to_pem(NewServerCertFile, [{'Certificate', NewServerDerCert, not_encrypted}]), - NewServerOpts = [{certfile, NewServerCertFile} | proplists:delete(certfile, ServerOpts)], - - {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config), - - Server = ssl_test_lib:start_server_error([{node, ServerNode}, {port, 0}, - {from, self()}, - {options, NewServerOpts}]), - Port = ssl_test_lib:inet_port(Server), - Client = ssl_test_lib:start_client_error([{node, ClientNode}, {port, Port}, - {host, Hostname}, - {from, self()}, - {options, [{verify, verify_peer} | ClientOpts]}]), - ssl_test_lib:check_server_alert(Server, Client, unknown_ca). - -%%-------------------------------------------------------------------- - -invalid_signature_client() -> - [{doc,"Test server with invalid signature"}]. - -invalid_signature_client(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), - PrivDir = proplists:get_value(priv_dir, Config), - - KeyFile = proplists:get_value(keyfile, ClientOpts), - [KeyEntry] = ssl_test_lib:pem_to_der(KeyFile), - Key = ssl_test_lib:public_key(public_key:pem_entry_decode(KeyEntry)), - - ClientCertFile = proplists:get_value(certfile, ClientOpts), - NewClientCertFile = filename:join(PrivDir, "client_invalid_cert.pem"), - [{'Certificate', ClientDerCert, _}] = ssl_test_lib:pem_to_der(ClientCertFile), - ClientOTPCert = public_key:pkix_decode_cert(ClientDerCert, otp), - ClientOTPTbsCert = ClientOTPCert#'OTPCertificate'.tbsCertificate, - NewClientDerCert = public_key:pkix_sign(ClientOTPTbsCert, Key), - ssl_test_lib:der_to_pem(NewClientCertFile, [{'Certificate', NewClientDerCert, not_encrypted}]), - NewClientOpts = [{certfile, NewClientCertFile} | proplists:delete(certfile, ClientOpts)], - - {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config), - - Server = ssl_test_lib:start_server_error([{node, ServerNode}, {port, 0}, - {from, self()}, - {options, [{verify, verify_peer} | ServerOpts]}]), - Port = ssl_test_lib:inet_port(Server), - Client = ssl_test_lib:start_client_error([{node, ClientNode}, {port, Port}, - {host, Hostname}, - {from, self()}, - {options, NewClientOpts}]), - - ssl_test_lib:check_client_alert(Server, Client, unknown_ca). - -%%-------------------------------------------------------------------- - -client_with_cert_cipher_suites_handshake() -> - [{doc, "Test that client with a certificate without keyEncipherment usage " - " extension can connect to a server with restricted cipher suites "}]. -client_with_cert_cipher_suites_handshake(Config) when is_list(Config) -> - Ext = x509_test:extensions([{key_usage, [digitalSignature]}]), - {ClientOpts0, ServerOpts0} = ssl_test_lib:make_rsa_cert_chains([{client_chain, - [[], [], [{extensions, Ext}]]}], - Config, "_sign_only_extensions"), - ClientOpts = ssl_test_lib:ssl_options(ClientOpts0, Config), - ServerOpts = ssl_test_lib:ssl_options(ServerOpts0, Config), - TLSVersion = ssl_test_lib:protocol_version(Config, tuple), - - {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, - send_recv_result_active, []}}, - {options, [{active, true}, - {ciphers, - ssl_test_lib:rsa_non_signed_suites(TLSVersion)} - | ServerOpts]}]), - Port = ssl_test_lib:inet_port(Server), - Client = ssl_test_lib:start_client([{node, ClientNode}, {port, Port}, - {host, Hostname}, - {from, self()}, - {mfa, {ssl_test_lib, - send_recv_result_active, []}}, - {options, [{active, true} - | ClientOpts]}]), - - ssl_test_lib:check_result(Server, ok, Client, ok), - ssl_test_lib:close(Server), - ssl_test_lib:close(Client). - -%%-------------------------------------------------------------------- - - -long_chain() -> - [{doc,"Test option verify_peer"}]. -long_chain(Config) when is_list(Config) -> - #{server_config := ServerConf, - client_config := ClientConf} = public_key:pkix_test_data(#{server_chain => #{root => [{key, ssl_test_lib:hardcode_rsa_key(1)}], - intermediates => [[{key, ssl_test_lib:hardcode_rsa_key(2)}], - [{key, ssl_test_lib:hardcode_rsa_key(3)}], - [{key, ssl_test_lib:hardcode_rsa_key(4)}]], - peer => [{key, ssl_test_lib:hardcode_rsa_key(5)}]}, - client_chain => #{root => [{key, ssl_test_lib:hardcode_rsa_key(3)}], - intermediates => [[{key, ssl_test_lib:hardcode_rsa_key(2)}]], - peer => [{key, ssl_test_lib:hardcode_rsa_key(1)}]}}), - [ServerRoot| _] = ServerCas = proplists:get_value(cacerts, ServerConf), - ClientCas = proplists:get_value(cacerts, ClientConf), - - Active = proplists:get_value(active, Config), - ReceiveFunction = proplists:get_value(receive_function, 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, ReceiveFunction, []}}, - {options, [{active, Active}, {verify, verify_peer}, - {cacerts, [ServerRoot]} | - proplists:delete(cacerts, ServerConf)]}]), - Port = ssl_test_lib:inet_port(Server), - Client = ssl_test_lib:start_client([{node, ClientNode}, {port, Port}, - {host, Hostname}, - {from, self()}, - {mfa, {ssl_test_lib, ReceiveFunction, []}}, - {options, [{active, Active}, - {verify, verify_peer}, - {depth, 5}, - {cacerts, ServerCas ++ ClientCas} | - proplists:delete(cacerts, ClientConf)]}]), - ssl_test_lib:check_result(Server, ok, Client, ok), - ssl_test_lib:close(Server), - ssl_test_lib:close(Client). - - -%%-------------------------------------------------------------------- -%% Internal functions ------------------------------------------------ -%%-------------------------------------------------------------------- - diff --git a/lib/ssl/test/ssl_handshake_SUITE.erl b/lib/ssl/test/ssl_handshake_SUITE.erl index 2750a4a9dc..e4a2ebb583 100644 --- a/lib/ssl/test/ssl_handshake_SUITE.erl +++ b/lib/ssl/test/ssl_handshake_SUITE.erl @@ -106,7 +106,7 @@ decode_hello_handshake(_Config) -> Version = {3, 0}, {Records, _Buffer} = tls_handshake:get_tls_handshake(Version, HelloPacket, <<>>, - #ssl_options{}), + ssl:options_to_map(#ssl_options{})), {Hello, _Data} = hd(Records), Extensions = Hello#server_hello.extensions, diff --git a/lib/ssl/test/ssl_npn_hello_SUITE.erl b/lib/ssl/test/ssl_npn_hello_SUITE.erl index 7dbc0c5134..e098190ece 100644 --- a/lib/ssl/test/ssl_npn_hello_SUITE.erl +++ b/lib/ssl/test/ssl_npn_hello_SUITE.erl @@ -71,7 +71,8 @@ encode_and_decode_client_hello_test(Config) -> HandShakeData = create_client_handshake(undefined), Version = ssl_test_lib:protocol_version(Config), {[{DecodedHandshakeMessage, _Raw}], _} = - tls_handshake:get_tls_handshake(Version, list_to_binary(HandShakeData), <<>>, #ssl_options{}), + tls_handshake:get_tls_handshake(Version, list_to_binary(HandShakeData), <<>>, + ssl:options_to_map(#ssl_options{})), Extensions = DecodedHandshakeMessage#client_hello.extensions, #{next_protocol_negotiation := undefined} = Extensions. %%-------------------------------------------------------------------- @@ -79,7 +80,8 @@ encode_and_decode_npn_client_hello_test(Config) -> HandShakeData = create_client_handshake(#next_protocol_negotiation{extension_data = <<>>}), Version = ssl_test_lib:protocol_version(Config), {[{DecodedHandshakeMessage, _Raw}], _} = - tls_handshake:get_tls_handshake(Version, list_to_binary(HandShakeData), <<>>, #ssl_options{}), + tls_handshake:get_tls_handshake(Version, list_to_binary(HandShakeData), <<>>, + ssl:options_to_map(#ssl_options{})), Extensions = DecodedHandshakeMessage#client_hello.extensions, #{next_protocol_negotiation := #next_protocol_negotiation{extension_data = <<>>}} = Extensions. %%-------------------------------------------------------------------- @@ -87,7 +89,8 @@ encode_and_decode_server_hello_test(Config) -> HandShakeData = create_server_handshake(undefined), Version = ssl_test_lib:protocol_version(Config), {[{DecodedHandshakeMessage, _Raw}], _} = - tls_handshake:get_tls_handshake(Version, list_to_binary(HandShakeData), <<>>, #ssl_options{}), + tls_handshake:get_tls_handshake(Version, list_to_binary(HandShakeData), <<>>, + ssl:options_to_map(#ssl_options{})), Extensions = DecodedHandshakeMessage#server_hello.extensions, #{next_protocol_negotiation := undefined} = Extensions. @@ -96,7 +99,8 @@ encode_and_decode_npn_server_hello_test(Config) -> HandShakeData = create_server_handshake(#next_protocol_negotiation{extension_data = <<6, "spdy/2">>}), Version = ssl_test_lib:protocol_version(Config), {[{DecodedHandshakeMessage, _Raw}], _} = - tls_handshake:get_tls_handshake(Version, list_to_binary(HandShakeData), <<>>, #ssl_options{}), + tls_handshake:get_tls_handshake(Version, list_to_binary(HandShakeData), <<>>, + ssl:options_to_map(#ssl_options{})), Extensions = DecodedHandshakeMessage#server_hello.extensions, ct:log("~p ~n", [Extensions]), #{next_protocol_negotiation := #next_protocol_negotiation{extension_data = <<6, "spdy/2">>}} = Extensions. diff --git a/lib/ssl/test/ssl_test_lib.erl b/lib/ssl/test/ssl_test_lib.erl index c4f294771a..cb528f185a 100644 --- a/lib/ssl/test/ssl_test_lib.erl +++ b/lib/ssl/test/ssl_test_lib.erl @@ -2208,10 +2208,23 @@ check_sane_openssl_renegotaite(Config) -> {skip, "Known renegotiation bug in OpenSSL"}; "OpenSSL 0.9.7" ++ _ -> {skip, "Known renegotiation bug in OpenSSL"}; + "LibreSSL 2." ++ _ -> + {skip, "Known renegotiation bug in LibreSSL"}; + _ -> Config end. +openssl_allows_client_renegotaite(Config) -> + case os:cmd("openssl version") of + "OpenSSL 1.1" ++ _ -> + {skip, "OpenSSL does not allow client renegotiation"}; + "LibreSSL 2" ++ _ -> + {skip, "LibreSSL does not allow client renegotiation"}; + _ -> + Config + end. + workaround_openssl_s_clinent() -> %% http://bugs.debian.org/cgi-bin/bugreport.cgi?bug=683159 %% https://bugs.archlinux.org/task/33919 @@ -2777,17 +2790,6 @@ new_config(PrivDir, ServerOpts0) -> [{cacertfile, NewCaCertFile}, {certfile, NewCertFile}, {keyfile, NewKeyFile} | ServerOpts]. -sane_openssl_alpn_npn_renegotiate() -> - case os:cmd("openssl version") of - "LibreSSL 2.9.1" ++ _ -> - false; - "LibreSSL 2.6.4" ++ _ -> - false; - "OpenSSL 1.1.1a-freebsd" ++ _ -> - false; - _ -> - true - end. openssl_sane_dtls_alpn() -> case os:cmd("openssl version") of diff --git a/lib/ssl/vsn.mk b/lib/ssl/vsn.mk index c9547cae36..c7404c0169 100644 --- a/lib/ssl/vsn.mk +++ b/lib/ssl/vsn.mk @@ -1 +1 @@ -SSL_VSN = 9.3.5 +SSL_VSN = 9.4 |