diff options
Diffstat (limited to 'lib/ssl')
-rw-r--r-- | lib/ssl/doc/src/ssl.xml | 57 | ||||
-rw-r--r-- | lib/ssl/src/ssl.erl | 104 | ||||
-rw-r--r-- | lib/ssl/src/ssl_cipher.erl | 36 | ||||
-rw-r--r-- | lib/ssl/src/ssl_connection.erl | 38 | ||||
-rw-r--r-- | lib/ssl/test/ssl_basic_SUITE.erl | 61 | ||||
-rw-r--r-- | lib/ssl/test/ssl_sni_SUITE.erl | 8 | ||||
-rw-r--r-- | lib/ssl/test/ssl_test_lib.erl | 4 | ||||
-rw-r--r-- | lib/ssl/test/ssl_to_openssl_SUITE.erl | 8 |
8 files changed, 213 insertions, 103 deletions
diff --git a/lib/ssl/doc/src/ssl.xml b/lib/ssl/doc/src/ssl.xml index aaf03d1cd8..d3881ad117 100644 --- a/lib/ssl/doc/src/ssl.xml +++ b/lib/ssl/doc/src/ssl.xml @@ -144,7 +144,9 @@ <p>According to old API.</p></item> <tag><c>ciphersuite() =</c></tag> - <item><p><c>{key_exchange(), cipher(), hash()}</c></p></item> + + <item><p><c>{key_exchange(), cipher(), MAC::hash()} | + {key_exchange(), cipher(), MAC::hash(), PRF::hash()}</c></p></item> <tag><c>key_exchange()=</c></tag> <item><p><c>rsa | dhe_dss | dhe_rsa | dh_anon | psk | dhe_psk @@ -156,7 +158,7 @@ | aes_128_cbc | aes_256_cbc | aes_128_gcm | aes_256_gcm</c></p></item> <tag><c>hash() =</c></tag> - <item><p><c>md5 | sha</c></p></item> + <item><p><c>md5 | sha | sha224 | sha256 | sha348 | sha512</c></p></item> <tag><c>prf_random() =</c></tag> <item><p><c>client_random | server_random</c></p></item> @@ -754,20 +756,7 @@ fun(srp, Username :: string(), UserState :: term()) -> the following options set: <c>[{active, false}, {packet, 0}, {mode, binary}]</c></p> </desc> </func> - - <func> - <name>connection_info(SslSocket) -> - {ok, {ProtocolVersion, CipherSuite}} | {error, Reason}</name> - <fsummary>Returns the Negotiated Protocol version and cipher suite. - </fsummary> - <type> - <v>CipherSuite = ciphersuite()</v> - <v>ProtocolVersion = protocol()</v> - </type> - <desc><p>Returns the Negotiated Protocol version and cipher suite.</p> - </desc> - </func> - + <func> <name>controlling_process(SslSocket, NewOwner) -> ok | {error, Reason}</name> @@ -786,40 +775,36 @@ fun(srp, Username :: string(), UserState :: term()) -> <func> <name>connection_information(SslSocket) -> - {ok, Info} | {error, Reason} </name> + {ok, Result} | {error, Reason} </name> <fsummary>Returns all the connection information. </fsummary> <type> - <v>Info = [InfoTuple]</v> - <v>InfoTuple = {protocol, Protocol} | {cipher_suite, CipherSuite} | {sni_hostname, SNIHostname}</v> - <v>CipherSuite = ciphersuite()</v> - <v>ProtocolVersion = protocol()</v> - <v>SNIHostname = string()</v> + <v>Item = protocol | cipher_suite | sni_hostname | atom()</v> + <d>Meaningful atoms, not specified above, are the ssl option names.</d> + <v>Result = [{Item::atom(), Value::term()}]</v> <v>Reason = term()</v> </type> - <desc><p>Return all the connection information containing negotiated protocol version, cipher suite, and the hostname of SNI extension. - Info will be a proplists containing all the connection information on success, otherwise <c>{error, Reason}</c> will be returned.</p> + <desc><p>Returns all relevant information about the connection, ssl options that + are undefined will be filtered out.</p> </desc> </func> <func> <name>connection_information(SslSocket, Items) -> - {ok, Info} | {error, Reason} </name> + {ok, Result} | {error, Reason} </name> <fsummary>Returns the requested connection information. </fsummary> <type> - <v>Items = [Item]</v> - <v>Item = protocol | cipher_suite | sni_hostname</v> - <v>Info = [InfoTuple]</v> - <v>InfoTuple = {protocol, Protocol} | {cipher_suite, CipherSuite} | {sni_hostname, SNIHostname}</v> - <v>CipherSuite = ciphersuite()</v> - <v>ProtocolVersion = protocol()</v> - <v>SNIHostname = string()</v> + <v>Items = [Item]</v> + <v>Item = protocol | cipher_suite | sni_hostname | atom()</v> + <d>Meaningful atoms, not specified above, are the ssl option names.</d> + <v>Result = [{Item::atom(), Value::term()}]</v> <v>Reason = term()</v> </type> - <desc><p>Returns the connection information you requested. The connection information you can request contains protocol, cipher_suite, and sni_hostname. - <c>{ok, Info}</c> will be returned if it executes successfully. <c>Info</c> is a proplist containing the information you requested. - Otherwise, <c>{error, Reason}</c> will be returned.</p> + <desc><p>Returns the requested information items about the connection, + if they are defined.</p> + <note><p>If only undefined options are requested the + resulting list can be empty.</p></note> </desc> </func> @@ -1146,7 +1131,7 @@ fun(srp, Username :: string(), UserState :: term()) -> <seealso marker="#listen-2"> listen/2</seealso>, and <seealso marker="#ssl_accept-2">ssl_accept/[1,2,3]</seealso>. For the negotiated TLS/SSL version, see <seealso - marker="#connection_info-1">ssl:connection_info/1 + marker="#connection_information-1">ssl:connection_information/1 </seealso>.</item> <tag><c>available</c></tag> diff --git a/lib/ssl/src/ssl.erl b/lib/ssl/src/ssl.erl index 6551308935..c1bc90559e 100644 --- a/lib/ssl/src/ssl.erl +++ b/lib/ssl/src/ssl.erl @@ -37,7 +37,7 @@ close/1, close/2, shutdown/2, recv/2, recv/3, send/2, getopts/2, setopts/2 ]). %% SSL/TLS protocol handling --export([cipher_suites/0, cipher_suites/1, suite_definition/1, +-export([cipher_suites/0, cipher_suites/1, connection_info/1, versions/0, session_info/1, format_error/1, renegotiate/1, prf/5, negotiated_protocol/1, negotiated_next_protocol/1, connection_information/1, connection_information/2]). @@ -105,7 +105,7 @@ connect(Socket, SslOptions0, Timeout) when is_port(Socket), {gen_tcp, tcp, tcp_closed, tcp_error}), EmulatedOptions = ssl_socket:emulated_options(), {ok, SocketValues} = ssl_socket:getopts(Transport, Socket, EmulatedOptions), - try handle_options(SslOptions0 ++ SocketValues) of + try handle_options(SslOptions0 ++ SocketValues, client) of {ok, #config{transport_info = CbInfo, ssl = SslOptions, emulated = EmOpts, connection_cb = ConnectionCb}} -> @@ -127,7 +127,7 @@ connect(Host, Port, Options) -> connect(Host, Port, Options, infinity). connect(Host, Port, Options, Timeout) when (is_integer(Timeout) andalso Timeout > 0) or (Timeout == infinity) -> - try handle_options(Options) of + try handle_options(Options, client) of {ok, Config} -> do_connect(Host,Port,Config,Timeout) catch @@ -145,7 +145,7 @@ listen(_Port, []) -> {error, nooptions}; listen(Port, Options0) -> try - {ok, Config} = handle_options(Options0), + {ok, Config} = handle_options(Options0, server), ConnectionCb = connection_cb(Options0), #config{transport_info = {Transport, _, _, _}, inet_user = Options, connection_cb = ConnectionCb, ssl = SslOpts, emulated = EmOpts} = Config, @@ -233,7 +233,7 @@ ssl_accept(Socket, SslOptions, Timeout) when is_port(Socket), EmulatedOptions = ssl_socket:emulated_options(), {ok, SocketValues} = ssl_socket:getopts(Transport, Socket, EmulatedOptions), ConnetionCb = connection_cb(SslOptions), - try handle_options(SslOptions ++ SocketValues) of + try handle_options(SslOptions ++ SocketValues, server) of {ok, #config{transport_info = CbInfo, ssl = SslOpts, emulated = EmOpts}} -> ok = ssl_socket:setopts(Transport, Socket, ssl_socket:internal_inet_values()), {ok, Port} = ssl_socket:port(Transport, Socket), @@ -315,24 +315,32 @@ controlling_process(#sslsocket{pid = {Listen, %% %% Description: Return SSL information for the connection %%-------------------------------------------------------------------- -connection_information(#sslsocket{pid = Pid}) when is_pid(Pid) -> ssl_connection:connection_information(Pid); -connection_information(#sslsocket{pid = {Listen, _}}) when is_port(Listen) -> {error, enotconn}. - +connection_information(#sslsocket{pid = Pid}) when is_pid(Pid) -> + case ssl_connection:connection_information(Pid) of + {ok, Info} -> + {ok, [Item || Item = {_Key, Value} <- Info, Value =/= undefined]}; + Error -> + Error + end; +connection_information(#sslsocket{pid = {Listen, _}}) when is_port(Listen) -> + {error, enotconn}. %%-------------------------------------------------------------------- --spec connection_information(#sslsocket{}, [atom]) -> {ok, list()} | {error, reason()}. +-spec connection_information(#sslsocket{}, [atom()]) -> {ok, list()} | {error, reason()}. %% %% Description: Return SSL information for the connection %%-------------------------------------------------------------------- connection_information(#sslsocket{} = SSLSocket, Items) -> case connection_information(SSLSocket) of - {ok, I} -> - {ok, lists:filter(fun({K, _}) -> lists:foldl(fun(K1, Acc) when K1 =:= K -> Acc + 1; (_, Acc) -> Acc end, 0, Items) > 0 end, I)}; - E -> - E + {ok, Info} -> + {ok, [Item || Item = {Key, Value} <- Info, lists:member(Key, Items), + Value =/= undefined]}; + Error -> + Error end. %%-------------------------------------------------------------------- +%% Deprecated -spec connection_info(#sslsocket{}) -> {ok, {tls_record:tls_atom_version(), ssl_cipher:erl_cipher_suite()}} | {error, reason()}. %% @@ -372,15 +380,6 @@ peercert(#sslsocket{pid = {Listen, _}}) when is_port(Listen) -> {error, enotconn}. %%-------------------------------------------------------------------- --spec suite_definition(ssl_cipher:cipher_suite()) -> ssl_cipher:erl_cipher_suite(). -%% -%% Description: Return erlang cipher suite definition. -%%-------------------------------------------------------------------- -suite_definition(S) -> - {KeyExchange, Cipher, Hash, _} = ssl_cipher:suite_definition(S), - {KeyExchange, Cipher, Hash}. - -%%-------------------------------------------------------------------- -spec negotiated_protocol(#sslsocket{}) -> {ok, binary()} | {error, reason()}. %% %% Description: Returns the protocol that has been negotiated. If no @@ -410,7 +409,7 @@ negotiated_next_protocol(Socket) -> %%-------------------------------------------------------------------- cipher_suites(erlang) -> Version = tls_record:highest_protocol_version([]), - ssl_cipher:filter_suites([suite_definition(S) + ssl_cipher:filter_suites([ssl_cipher:erl_suite_definition(S) || S <- ssl_cipher:suites(Version)]); cipher_suites(openssl) -> Version = tls_record:highest_protocol_version([]), @@ -418,7 +417,7 @@ cipher_suites(openssl) -> || S <- ssl_cipher:filter_suites(ssl_cipher:suites(Version))]; cipher_suites(all) -> Version = tls_record:highest_protocol_version([]), - ssl_cipher:filter_suites([suite_definition(S) + ssl_cipher:filter_suites([ssl_cipher:erl_suite_definition(S) || S <-ssl_cipher:all_suites(Version)]). cipher_suites() -> cipher_suites(erlang). @@ -630,7 +629,8 @@ handle_options(Opts0, #ssl_options{protocol = Protocol, cacerts = CaCerts0, cacertfile = CaCertFile0} = InheritedSslOpts) -> RecordCB = record_cb(Protocol), CaCerts = handle_option(cacerts, Opts0, CaCerts0), - {Verify, FailIfNoPeerCert, CaCertDefault, VerifyFun, PartialChainHanlder} = handle_verify_options(Opts0, CaCerts), + {Verify, FailIfNoPeerCert, CaCertDefault, VerifyFun, PartialChainHanlder, + VerifyClientOnce} = handle_verify_options(Opts0, CaCerts), CaCertFile = case proplists:get_value(cacertfile, Opts0, CaCertFile0) of undefined -> CaCertDefault; @@ -643,11 +643,12 @@ handle_options(Opts0, #ssl_options{protocol = Protocol, cacerts = CaCerts0, verify = Verify, verify_fun = VerifyFun, partial_chain = PartialChainHanlder, - fail_if_no_peer_cert = FailIfNoPeerCert}, + 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, - fail_if_no_peer_cert]), + fail_if_no_peer_cert, verify_client_once]), case handle_option(versions, SslOpts1, []) of [] -> new_ssl_options(SslOpts1, NewVerifyOpts, RecordCB); @@ -655,10 +656,10 @@ handle_options(Opts0, #ssl_options{protocol = Protocol, cacerts = CaCerts0, Versions = [RecordCB:protocol_version(Vsn) || Vsn <- Value], new_ssl_options(proplists:delete(versions, SslOpts1), NewVerifyOpts#ssl_options{versions = Versions}, record_cb(Protocol)) - end. + end; %% Handle all options in listen and connect -handle_options(Opts0) -> +handle_options(Opts0, Role) -> Opts = proplists:expand([{binary, [{mode, binary}]}, {list, [{mode, list}]}], Opts0), assert_proplist(Opts), @@ -667,7 +668,7 @@ handle_options(Opts0) -> ReuseSessionFun = fun(_, _, _, _) -> true end, CaCerts = handle_option(cacerts, Opts, undefined), - {Verify, FailIfNoPeerCert, CaCertDefault, VerifyFun, PartialChainHanlder} = + {Verify, FailIfNoPeerCert, CaCertDefault, VerifyFun, PartialChainHanlder, VerifyClientOnce} = handle_verify_options(Opts, CaCerts), CertFile = handle_option(certfile, Opts, <<>>), @@ -686,7 +687,7 @@ handle_options(Opts0) -> verify_fun = VerifyFun, partial_chain = PartialChainHanlder, fail_if_no_peer_cert = FailIfNoPeerCert, - verify_client_once = handle_option(verify_client_once, Opts, false), + verify_client_once = VerifyClientOnce, depth = handle_option(depth, Opts, 1), cert = handle_option(cert, Opts, undefined), certfile = CertFile, @@ -706,7 +707,9 @@ handle_options(Opts0) -> reuse_session = handle_option(reuse_session, Opts, ReuseSessionFun), reuse_sessions = handle_option(reuse_sessions, Opts, true), secure_renegotiate = handle_option(secure_renegotiate, Opts, false), - client_renegotiation = handle_option(client_renegotiation, Opts, true), + client_renegotiation = handle_option(client_renegotiation, Opts, + default_option_role(server, true, Role), + server, Role), renegotiate_at = handle_option(renegotiate_at, Opts, ?DEFAULT_RENEGOTIATE_AT), hibernate_after = handle_option(hibernate_after, Opts, undefined), erl_dist = handle_option(erl_dist, Opts, false), @@ -723,10 +726,16 @@ handle_options(Opts0) -> server_name_indication = handle_option(server_name_indication, Opts, undefined), sni_hosts = handle_option(sni_hosts, Opts, []), sni_fun = handle_option(sni_fun, Opts, undefined), - honor_cipher_order = handle_option(honor_cipher_order, Opts, false), + honor_cipher_order = handle_option(honor_cipher_order, Opts, + default_option_role(server, false, Role), + server, Role), protocol = proplists:get_value(protocol, Opts, tls), padding_check = proplists:get_value(padding_check, Opts, true), - fallback = proplists:get_value(fallback, Opts, false), + fallback = handle_option(fallback, Opts, + proplists:get_value(fallback, Opts, + default_option_role(client, + false, Role)), + client, Role), crl_check = handle_option(crl_check, Opts, false), crl_cache = handle_option(crl_cache, Opts, {ssl_crl_cache, {internal, []}}) }, @@ -756,6 +765,13 @@ handle_options(Opts0) -> inet_user = SockOpts, 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(sni_fun, Opts, Default) -> OptFun = validate_option(sni_fun, proplists:get_value(sni_fun, Opts, Default)), @@ -772,7 +788,6 @@ handle_option(OptionName, Opts, Default) -> validate_option(OptionName, proplists:get_value(OptionName, Opts, Default)). - validate_option(versions, Versions) -> validate_versions(Versions, Versions); validate_option(verify, Value) @@ -1216,7 +1231,8 @@ emulated_socket_options(InetValues, #socket_options{ new_ssl_options([], #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(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) -> @@ -1295,29 +1311,35 @@ handle_verify_options(Opts, CaCerts) -> PartialChainHanlder = handle_option(partial_chain, Opts, fun(_) -> unknown_ca end), + VerifyClientOnce = handle_option(verify_client_once, Opts, false), + %% Handle 0, 1, 2 for backwards compatibility case proplists:get_value(verify, Opts, verify_none) of 0 -> {verify_none, false, ca_cert_default(verify_none, VerifyNoneFun, CaCerts), - VerifyNoneFun, PartialChainHanlder}; + VerifyNoneFun, PartialChainHanlder, VerifyClientOnce}; 1 -> {verify_peer, false, ca_cert_default(verify_peer, UserVerifyFun, CaCerts), - UserVerifyFun, PartialChainHanlder}; + UserVerifyFun, PartialChainHanlder, VerifyClientOnce}; 2 -> {verify_peer, true, ca_cert_default(verify_peer, UserVerifyFun, CaCerts), - UserVerifyFun, PartialChainHanlder}; + UserVerifyFun, PartialChainHanlder, VerifyClientOnce}; verify_none -> {verify_none, false, ca_cert_default(verify_none, VerifyNoneFun, CaCerts), - VerifyNoneFun, PartialChainHanlder}; + VerifyNoneFun, PartialChainHanlder, VerifyClientOnce}; verify_peer -> {verify_peer, UserFailIfNoPeerCert, ca_cert_default(verify_peer, UserVerifyFun, CaCerts), - UserVerifyFun, PartialChainHanlder}; + UserVerifyFun, PartialChainHanlder, VerifyClientOnce}; Value -> throw({error, {options, {verify, Value}}}) end. +default_option_role(Role, Value, Role) -> + Value; +default_option_role(_,_,_) -> + undefined. diff --git a/lib/ssl/src/ssl_cipher.erl b/lib/ssl/src/ssl_cipher.erl index 8c2a16ba96..974a6ec6b5 100644 --- a/lib/ssl/src/ssl_cipher.erl +++ b/lib/ssl/src/ssl_cipher.erl @@ -34,6 +34,7 @@ -include_lib("public_key/include/public_key.hrl"). -export([security_parameters/2, security_parameters/3, suite_definition/1, + erl_suite_definition/1, cipher_init/3, decipher/6, cipher/5, decipher_aead/6, cipher_aead/6, suite/1, suites/1, all_suites/1, ec_keyed_suites/0, anonymous_suites/1, psk_suites/1, srp_suites/0, @@ -48,8 +49,11 @@ | aes_128_cbc | aes_256_cbc | aes_128_gcm | aes_256_gcm | chacha20_poly1305. -type hash() :: null | sha | md5 | sha224 | sha256 | sha384 | sha512. -type key_algo() :: null | rsa | dhe_rsa | dhe_dss | ecdhe_ecdsa| ecdh_ecdsa | ecdh_rsa| srp_rsa| srp_dss | psk | dhe_psk | rsa_psk | dh_anon | ecdh_anon | srp_anon. --type erl_cipher_suite() :: {key_algo(), cipher(), hash()}. --type int_cipher_suite() :: {key_algo(), cipher(), hash(), hash() | default_prf}. +-type erl_cipher_suite() :: {key_algo(), cipher(), hash()} % Pre TLS 1.2 + %% TLS 1.2, internally PRE TLS 1.2 will use default_prf + | {key_algo(), cipher(), hash(), hash() | default_prf}. + + -type cipher_suite() :: binary(). -type cipher_enum() :: integer(). -type openssl_cipher_suite() :: string(). @@ -417,7 +421,7 @@ rc4_suites({3, N}) when N =< 3 -> ?TLS_ECDH_RSA_WITH_RC4_128_SHA]. %%-------------------------------------------------------------------- --spec suite_definition(cipher_suite()) -> int_cipher_suite(). +-spec suite_definition(cipher_suite()) -> erl_cipher_suite(). %% %% Description: Return erlang cipher suite definition. %% Note: Currently not supported suites are commented away. @@ -722,6 +726,20 @@ suite_definition(?TLS_DHE_RSA_WITH_CHACHA20_POLY1305_SHA256) -> {dhe_rsa, chacha20_poly1305, null, sha256}. %%-------------------------------------------------------------------- +-spec erl_suite_definition(cipher_suite()) -> erl_cipher_suite(). +%% +%% Description: Return erlang cipher suite definition. Filters last value +%% for now (compatibility reasons). +%%-------------------------------------------------------------------- +erl_suite_definition(S) -> + case suite_definition(S) of + {KeyExchange, Cipher, Hash, default_prf} -> + {KeyExchange, Cipher, Hash}; + Suite -> + Suite + end. + +%%-------------------------------------------------------------------- -spec suite(erl_cipher_suite()) -> cipher_suite(). %% %% Description: Return TLS cipher suite definition. @@ -1384,18 +1402,14 @@ filter(DerCert, Ciphers) -> %% %% Description: Filter suites for algorithms supported by crypto. %%------------------------------------------------------------------- -filter_suites(Suites = [{_,_,_}|_]) -> +filter_suites(Suites = [Value|_]) when is_tuple(Value) -> Algos = crypto:supports(), + Hashs = proplists:get_value(hashs, Algos), lists:filter(fun({KeyExchange, Cipher, Hash}) -> is_acceptable_keyexchange(KeyExchange, proplists:get_value(public_keys, Algos)) andalso is_acceptable_cipher(Cipher, proplists:get_value(ciphers, Algos)) andalso - is_acceptable_hash(Hash, proplists:get_value(hashs, Algos)) - end, Suites); - -filter_suites(Suites = [{_,_,_,_}|_]) -> - Algos = crypto:supports(), - Hashs = proplists:get_value(hashs, Algos), - lists:filter(fun({KeyExchange, Cipher, Hash, Prf}) -> + is_acceptable_hash(Hash, proplists:get_value(hashs, Algos)); + ({KeyExchange, Cipher, Hash, Prf}) -> is_acceptable_keyexchange(KeyExchange, proplists:get_value(public_keys, Algos)) andalso is_acceptable_cipher(Cipher, proplists:get_value(ciphers, Algos)) andalso is_acceptable_hash(Hash, Hashs) andalso diff --git a/lib/ssl/src/ssl_connection.erl b/lib/ssl/src/ssl_connection.erl index 241871dc38..ec7d086934 100644 --- a/lib/ssl/src/ssl_connection.erl +++ b/lib/ssl/src/ssl_connection.erl @@ -836,15 +836,22 @@ handle_sync_event(session_info, _, StateName, #state{session = #session{session_id = Id, cipher_suite = Suite}} = State) -> {reply, [{session_id, Id}, - {cipher_suite, ssl:suite_definition(Suite)}], + {cipher_suite, ssl_cipher:erl_suite_definition(Suite)}], StateName, State, get_timeout(State)}; handle_sync_event(peer_certificate, _, StateName, #state{session = #session{peer_certificate = Cert}} = State) -> {reply, {ok, Cert}, StateName, State, get_timeout(State)}; -handle_sync_event(connection_information, _, StateName, #state{sni_hostname = SNIHostname, session = #session{cipher_suite = CipherSuite}, negotiated_version = Version} = State) -> - {reply, {ok, [{protocol, tls_record:protocol_version(Version)}, {cipher_suite, ssl:suite_definition(CipherSuite)}, {sni_hostname, SNIHostname}]}, StateName, State, get_timeout(State)}. +handle_sync_event(connection_information, _, StateName, State) -> + Info = connection_info(State), + {reply, {ok, Info}, StateName, State, get_timeout(State)}. +connection_info(#state{sni_hostname = SNIHostname, + session = #session{cipher_suite = CipherSuite}, + negotiated_version = Version, ssl_options = Opts}) -> + [{protocol, tls_record:protocol_version(Version)}, + {cipher_suite, ssl_cipher:erl_suite_definition(CipherSuite)}, + {sni_hostname, SNIHostname}] ++ ssl_options_list(Opts). handle_info({ErrorTag, Socket, econnaborted}, StateName, #state{socket = Socket, transport_cb = Transport, @@ -1885,3 +1892,28 @@ negotiated_hashsign(undefined, Alg, Version) -> 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, []). + +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, + [{Key, lists:map( + fun(Suite) -> + ssl_cipher:erl_suite_definition(Suite) + end, Value)} + | Acc]); +ssl_options_list([Key | Keys], [Value | Values], Acc) -> + ssl_options_list(Keys, Values, [{Key, Value} | Acc]). + + diff --git a/lib/ssl/test/ssl_basic_SUITE.erl b/lib/ssl/test/ssl_basic_SUITE.erl index 05b040a2ab..1a864edb8b 100644 --- a/lib/ssl/test/ssl_basic_SUITE.erl +++ b/lib/ssl/test/ssl_basic_SUITE.erl @@ -121,6 +121,7 @@ options_tests() -> api_tests() -> [connection_info, + connection_information, peername, peercert, peercert_with_client_cert, @@ -461,6 +462,37 @@ connection_info(Config) when is_list(Config) -> ssl_test_lib:close(Client). %%-------------------------------------------------------------------- + +connection_information() -> + [{doc,"Test the API function ssl:connection_information/1"}]. +connection_information(Config) when is_list(Config) -> + ClientOpts = ?config(client_opts, Config), + ServerOpts = ?config(server_opts, Config), + {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config), + Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0}, + {from, self()}, + {mfa, {?MODULE, connection_information_result, []}}, + {options, ServerOpts}]), + + Port = ssl_test_lib:inet_port(Server), + Client = ssl_test_lib:start_client([{node, ClientNode}, {port, Port}, + {host, Hostname}, + {from, self()}, + {mfa, {?MODULE, connection_information_result, []}}, + {options, ClientOpts}]), + + ct:log("Testcase ~p, Client ~p Server ~p ~n", + [self(), Client, Server]), + + ServerMsg = ClientMsg = ok, + + ssl_test_lib:check_result(Server, ServerMsg, Client, ClientMsg), + + ssl_test_lib:close(Server), + ssl_test_lib:close(Client). + + +%%-------------------------------------------------------------------- protocol_versions() -> [{doc,"Test to set a list of protocol versions in app environment."}]. @@ -3989,7 +4021,7 @@ run_suites(Ciphers, Version, Config, Type) -> end. erlang_cipher_suite(Suite) when is_list(Suite)-> - ssl:suite_definition(ssl_cipher:openssl_suite(Suite)); + ssl_cipher:erl_suite_definition(ssl_cipher:openssl_suite(Suite)); erlang_cipher_suite(Suite) -> Suite. @@ -4010,11 +4042,11 @@ cipher(CipherSuite, Version, Config, ClientOpts, 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, cipher_result, [ConnectionInfo]}}, - {options, - [{ciphers,[CipherSuite]} | - ClientOpts]}]), + {from, self()}, + {mfa, {ssl_test_lib, cipher_result, [ConnectionInfo]}}, + {options, + [{ciphers,[CipherSuite]} | + ClientOpts]}]), Result = ssl_test_lib:wait_for_result(Server, ok, Client, ok), @@ -4028,6 +4060,17 @@ cipher(CipherSuite, Version, Config, ClientOpts, ServerOpts) -> [{ErlangCipherSuite, Error}] end. +connection_information_result(Socket) -> + {ok, Info = [_ | _]} = ssl:connection_information(Socket), + case length(Info) > 3 of + true -> + %% Atleast one ssloption() is set + ct:log("Info ~p", [Info]), + ok; + false -> + ct:fail(no_ssl_options_returned) + end. + connection_info_result(Socket) -> {ok, Info} = ssl:connection_information(Socket, [protocol, cipher_suite]), {ok, {proplists:get_value(protocol, Info), proplists:get_value(cipher_suite, Info)}}. @@ -4154,6 +4197,12 @@ first_rsa_suite([{dhe_rsa, _, _} = Suite| _]) -> Suite; first_rsa_suite([{rsa, _, _} = Suite| _]) -> Suite; +first_rsa_suite([{ecdhe_rsa, _, _, _} = Suite | _]) -> + Suite; +first_rsa_suite([{dhe_rsa, _, _, _} = Suite| _]) -> + Suite; +first_rsa_suite([{rsa, _, _, _} = Suite| _]) -> + Suite; first_rsa_suite([_ | Rest]) -> first_rsa_suite(Rest). diff --git a/lib/ssl/test/ssl_sni_SUITE.erl b/lib/ssl/test/ssl_sni_SUITE.erl index f6ffe91027..90c2a49e61 100644 --- a/lib/ssl/test/ssl_sni_SUITE.erl +++ b/lib/ssl/test/ssl_sni_SUITE.erl @@ -108,8 +108,12 @@ ssl_recv(SSLSocket, CurrentData, ExpectedData) -> send_and_hostname(SSLSocket) -> ssl:send(SSLSocket, "OK"), - {ok, [{sni_hostname, Hostname}]} = ssl:connection_information(SSLSocket, [sni_hostname]), - Hostname. + case ssl:connection_information(SSLSocket, [sni_hostname]) of + {ok, [{sni_hostname, Hostname}]} -> + Hostname; + {ok, []} -> + undefined + end. rdnPart([[#'AttributeTypeAndValue'{type=Type, value=Value} | _] | _], Type) -> Value; diff --git a/lib/ssl/test/ssl_test_lib.erl b/lib/ssl/test/ssl_test_lib.erl index afd21f0d2f..90fcd193cc 100644 --- a/lib/ssl/test/ssl_test_lib.erl +++ b/lib/ssl/test/ssl_test_lib.erl @@ -825,7 +825,7 @@ common_ciphers(crypto) -> common_ciphers(openssl) -> OpenSslSuites = string:tokens(string:strip(os:cmd("openssl ciphers"), right, $\n), ":"), - [ssl:suite_definition(S) + [ssl_cipher:erl_suite_definition(S) || S <- ssl_cipher:suites(tls_record:highest_protocol_version([])), lists:member(ssl_cipher:openssl_suite_name(S), OpenSslSuites) ]. @@ -1224,7 +1224,7 @@ filter_suites(Ciphers0) -> ++ ssl_cipher:srp_suites() ++ ssl_cipher:rc4_suites(Version), Supported1 = ssl_cipher:filter_suites(Supported0), - Supported2 = [ssl:suite_definition(S) || S <- Supported1], + Supported2 = [ssl_cipher:erl_suite_definition(S) || S <- Supported1], [Cipher || Cipher <- Ciphers0, lists:member(Cipher, Supported2)]. -define(OPENSSL_QUIT, "Q\n"). diff --git a/lib/ssl/test/ssl_to_openssl_SUITE.erl b/lib/ssl/test/ssl_to_openssl_SUITE.erl index ecf6c4d6b8..6934d7f851 100644 --- a/lib/ssl/test/ssl_to_openssl_SUITE.erl +++ b/lib/ssl/test/ssl_to_openssl_SUITE.erl @@ -1268,8 +1268,12 @@ client_check_result(Port, DataExpected) -> send_and_hostname(SSLSocket) -> ssl:send(SSLSocket, "OK"), - {ok, [{sni_hostname, Hostname}]} = ssl:connection_information(SSLSocket, [sni_hostname]), - Hostname. + case ssl:connection_information(SSLSocket, [sni_hostname]) of + {ok, []} -> + undefined; + {ok, [{sni_hostname, Hostname}]} -> + Hostname + end. erlang_server_openssl_client_sni_test(Config, SNIHostname, ExpectedSNIHostname, ExpectedCN) -> ct:log("Start running handshake, Config: ~p, SNIHostname: ~p, ExpectedSNIHostname: ~p, ExpectedCN: ~p", [Config, SNIHostname, ExpectedSNIHostname, ExpectedCN]), |