diff options
Diffstat (limited to 'lib/ssl')
-rw-r--r-- | lib/ssl/doc/src/notes.xml | 49 | ||||
-rw-r--r-- | lib/ssl/src/Makefile | 20 | ||||
-rw-r--r-- | lib/ssl/src/ssl.appup.src | 19 | ||||
-rw-r--r-- | lib/ssl/src/ssl.erl | 12 | ||||
-rw-r--r-- | lib/ssl/src/ssl_connection.erl | 279 | ||||
-rw-r--r-- | lib/ssl/src/ssl_handshake.erl | 1 | ||||
-rw-r--r-- | lib/ssl/src/ssl_manager.erl | 2 | ||||
-rw-r--r-- | lib/ssl/src/ssl_session.erl | 18 | ||||
-rw-r--r-- | lib/ssl/test/erl_make_certs.erl | 4 | ||||
-rw-r--r-- | lib/ssl/test/ssl_basic_SUITE.erl | 191 | ||||
-rw-r--r-- | lib/ssl/test/ssl_test_lib.erl | 27 | ||||
-rw-r--r-- | lib/ssl/test/ssl_to_openssl_SUITE.erl | 13 | ||||
-rw-r--r-- | lib/ssl/vsn.mk | 2 |
13 files changed, 481 insertions, 156 deletions
diff --git a/lib/ssl/doc/src/notes.xml b/lib/ssl/doc/src/notes.xml index 6c01954010..49bbd5d27d 100644 --- a/lib/ssl/doc/src/notes.xml +++ b/lib/ssl/doc/src/notes.xml @@ -30,7 +30,54 @@ </header> <p>This document describes the changes made to the SSL application.</p> - <section><title>SSL 5.1</title> + <section><title>SSL 5.1.2</title> + + <section><title>Fixed Bugs and Malfunctions</title> + <list> + <item> + <p> + ssl:ssl_accept/2 timeout is no longer ignored</p> + <p> + Own Id: OTP-10600</p> + </item> + </list> + </section> + +</section> + +<section><title>SSL 5.1.1</title> + + <section><title>Fixed Bugs and Malfunctions</title> + <list> + <item> + <p> + ssl:recv/3 could "loose" data when the timeout occurs. If + the timout in ssl:connect or ssl:ssl_accept expired the + ssl connection process was not terminated as it should, + this due to gen_fsm:send_all_state_event timout is a + client side time out. These timouts are now handled by + the gen_fsm-procss instead.</p> + <p> + Own Id: OTP-10569</p> + </item> + </list> + </section> + + + <section><title>Improvements and New Features</title> + <list> + <item> + <p> + Better termination handling that avoids hanging.</p> + <p> + Own Id: OTP-10574</p> + </item> + </list> + </section> + +</section> + +<section><title>SSL 5.1</title> <section><title>Fixed Bugs and Malfunctions</title> <list> diff --git a/lib/ssl/src/Makefile b/lib/ssl/src/Makefile index c5c5bf593a..6be8a1456e 100644 --- a/lib/ssl/src/Makefile +++ b/lib/ssl/src/Makefile @@ -130,3 +130,23 @@ release_spec: opt release_docs_spec: +# ---------------------------------------------------- +# Dependencies +# ---------------------------------------------------- +$(EBIN)/inet_tls_dist.$(EMULATOR): ../../kernel/include/net_address.hrl ../../kernel/include/dist.hrl ../../kernel/include/dist_util.hrl +$(EBIN)/ssl.$(EMULATOR): ssl_internal.hrl ssl_record.hrl ssl_cipher.hrl ssl_handshake.hrl ../../public_key/include/public_key.hrl +$(EBIN)/ssl_alert.$(EMULATOR): ssl_alert.hrl ssl_record.hrl +$(EBIN)/ssl_certificate.$(EMULATOR): ssl_internal.hrl ssl_alert.hrl ssl_handshake.hrl ../../public_key/include/public_key.hrl +$(EBIN)/ssl_certificate_db.$(EMULATOR): ssl_internal.hrl ../../public_key/include/public_key.hrl ../../kernel/include/file.hrl +$(EBIN)/ssl_cipher.$(EMULATOR): ssl_internal.hrl ssl_record.hrl ssl_cipher.hrl ssl_handshake.hrl ssl_alert.hrl ../../public_key/include/public_key.hrl +$(EBIN)/ssl_connection.$(EMULATOR): ssl_internal.hrl ssl_record.hrl ssl_cipher.hrl ssl_handshake.hrl ssl_alert.hrl ../../public_key/include/public_key.hrl +$(EBIN)/ssl_handshake.$(EMULATOR): ssl_internal.hrl ssl_record.hrl ssl_cipher.hrl ssl_handshake.hrl ssl_alert.hrl ../../public_key/include/public_key.hrl +$(EBIN)/ssl_manager.$(EMULATOR): ssl_internal.hrl ssl_handshake.hrl ../../kernel/include/file.hrl +$(EBIN)/ssl_record.$(EMULATOR): ssl_internal.hrl ssl_record.hrl ssl_cipher.hrl ssl_handshake.hrl ssl_alert.hrl +$(EBIN)/ssl_session.$(EMULATOR): ssl_internal.hrl ssl_handshake.hrl +$(EBIN)/ssl_session_cache.$(EMULATOR): ssl_internal.hrl ssl_handshake.hrl +$(EBIN)/ssl_session_cache_api.$(EMULATOR): ssl_internal.hrl ssl_handshake.hrl +$(EBIN)/ssl_ssl3.$(EMULATOR): ssl_internal.hrl ssl_record.hrl ssl_cipher.hrl +$(EBIN)/ssl_tls1.$(EMULATOR): ssl_internal.hrl ssl_record.hrl ssl_cipher.hrl + + diff --git a/lib/ssl/src/ssl.appup.src b/lib/ssl/src/ssl.appup.src index 76550fa04b..9b1227fa7f 100644 --- a/lib/ssl/src/ssl.appup.src +++ b/lib/ssl/src/ssl.appup.src @@ -1,14 +1,25 @@ %% -*- erlang -*- {"%VSN%", [ - {"5.0.1", [{restart_application, ssl}]}, - {"5.0", [{restart_application, ssl}]}, + {"5.1.1", [{restart_application, ssl}] + }, + {"5.1", [ + {load_module, ssl_connection, soft_purge, soft_purge, []} + ] + }, + {<<"5.0\\*">>, [{restart_application, ssl}]}, {<<"4\\.*">>, [{restart_application, ssl}]}, {<<"3\\.*">>, [{restart_application, ssl}]} ], [ - {"5.0.1", [{restart_application, ssl}]}, - {"5.0", [{restart_application, ssl}]}, + {"5.1.1", [{restart_application, ssl}] + }, + {"5.1", [ + {load_module, ssl_connection, soft_purge, soft_purge, []} + ] + }, + {"5.1", [{restart_application, ssl}]}, + {<<"5.0\\*">>, [{restart_application, ssl}]}, {<<"4\\.*">>, [{restart_application, ssl}]}, {<<"3\\.*">>, [{restart_application, ssl}]} ]}. diff --git a/lib/ssl/src/ssl.erl b/lib/ssl/src/ssl.erl index 7788f758ac..6224334a6e 100644 --- a/lib/ssl/src/ssl.erl +++ b/lib/ssl/src/ssl.erl @@ -47,7 +47,7 @@ -export_type([connect_option/0, listen_option/0, ssl_option/0, transport_option/0, erl_cipher_suite/0, %% From ssl_cipher.hrl tls_atom_version/0, %% From ssl_internal.hrl - prf_random/0]). + prf_random/0, sslsocket/0]). -record(config, {ssl, %% SSL parameters inet_user, %% User set inet options @@ -55,6 +55,8 @@ inet_ssl, %% inet options for internal ssl socket cb %% Callback info }). + +-type sslsocket() :: #sslsocket{}. -type connect_option() :: socket_connect_option() | ssl_option() | transport_option(). -type socket_connect_option() :: gen_tcp:connect_option(). -type listen_option() :: socket_listen_option() | ssl_option() | transport_option(). @@ -869,10 +871,10 @@ internal_inet_values() -> socket_options(InetValues) -> #socket_options{ - mode = proplists:get_value(mode, InetValues), - header = proplists:get_value(header, InetValues), - active = proplists:get_value(active, InetValues), - packet = proplists:get_value(packet, InetValues), + mode = proplists:get_value(mode, InetValues, lists), + header = proplists:get_value(header, InetValues, 0), + active = proplists:get_value(active, InetValues, active), + packet = proplists:get_value(packet, InetValues, 0), packet_size = proplists:get_value(packet_size, InetValues) }. diff --git a/lib/ssl/src/ssl_connection.erl b/lib/ssl/src/ssl_connection.erl index 1319b54d6b..cde13069b5 100644 --- a/lib/ssl/src/ssl_connection.erl +++ b/lib/ssl/src/ssl_connection.erl @@ -89,6 +89,7 @@ log_alert, % boolean() renegotiation, % {boolean(), From | internal | peer} start_or_recv_from, % "gen_fsm From" + timer, % start_or_recv_timer send_queue, % queue() terminated = false, % allow_renegotiate = true, @@ -119,7 +120,7 @@ send(Pid, Data) -> sync_send_all_state_event(Pid, {application_data, %% iolist_to_binary should really %% be called iodata_to_binary() - erlang:iolist_to_binary(Data)}, infinity). + erlang:iolist_to_binary(Data)}). %%-------------------------------------------------------------------- -spec recv(pid(), integer(), timeout()) -> @@ -128,7 +129,7 @@ send(Pid, Data) -> %% Description: Receives data when active = false %%-------------------------------------------------------------------- recv(Pid, Length, Timeout) -> - sync_send_all_state_event(Pid, {recv, Length}, Timeout). + sync_send_all_state_event(Pid, {recv, Length, Timeout}). %%-------------------------------------------------------------------- -spec connect(host(), inet:port_number(), port(), {#ssl_options{}, #socket_options{}}, pid(), tuple(), timeout()) -> @@ -165,7 +166,7 @@ ssl_accept(Port, Socket, Opts, User, CbInfo, Timeout) -> %% Description: Starts ssl handshake. %%-------------------------------------------------------------------- handshake(#sslsocket{pid = Pid}, Timeout) -> - case sync_send_all_state_event(Pid, start, Timeout) of + case sync_send_all_state_event(Pid, {start, Timeout}) of connected -> ok; Error -> @@ -331,15 +332,15 @@ init([Role, Host, Port, Socket, {SSLOpts0, _} = Options, User, CbInfo]) -> #state{}) -> gen_fsm_state_return(). %%-------------------------------------------------------------------- hello(start, #state{host = Host, port = Port, role = client, - ssl_options = SslOpts, - session = #session{own_certificate = Cert} = Session0, - session_cache = Cache, session_cache_cb = CacheCb, - transport_cb = Transport, socket = Socket, - connection_states = ConnectionStates0, - renegotiation = {Renegotiation, _}} = State0) -> + ssl_options = SslOpts, + session = #session{own_certificate = Cert} = Session0, + session_cache = Cache, session_cache_cb = CacheCb, + transport_cb = Transport, socket = Socket, + connection_states = ConnectionStates0, + renegotiation = {Renegotiation, _}} = State0) -> Hello = ssl_handshake:client_hello(Host, Port, ConnectionStates0, SslOpts, Cache, CacheCb, Renegotiation, Cert), - + Version = Hello#client_hello.client_version, Handshake0 = ssl_handshake:init_handshake_history(), {BinMsg, ConnectionStates, Handshake} = @@ -370,23 +371,22 @@ hello(#server_hello{cipher_suite = CipherSuite, renegotiation = {Renegotiation, _}, ssl_options = SslOptions} = State0) -> case ssl_handshake:hello(Hello, SslOptions, ConnectionStates0, Renegotiation) of - #alert{} = Alert -> - handle_own_alert(Alert, ReqVersion, hello, State0), - {stop, normal, State0}; - + #alert{} = Alert -> + handle_own_alert(Alert, ReqVersion, hello, State0), + {stop, {shutdown, own_alert}, State0}; {Version, NewId, ConnectionStates, NextProtocol} -> {KeyAlgorithm, _, _, _} = ssl_cipher:suite_definition(CipherSuite), PremasterSecret = make_premaster_secret(ReqVersion, KeyAlgorithm), - - NewNextProtocol = case NextProtocol of - undefined -> - State0#state.next_protocol; - _ -> - NextProtocol - end, - + + NewNextProtocol = case NextProtocol of + undefined -> + State0#state.next_protocol; + _ -> + NextProtocol + end, + State = State0#state{key_algorithm = KeyAlgorithm, hashsign_algorithm = default_hashsign(Version, KeyAlgorithm), negotiated_version = Version, @@ -394,13 +394,13 @@ hello(#server_hello{cipher_suite = CipherSuite, premaster_secret = PremasterSecret, expecting_next_protocol_negotiation = NextProtocol =/= undefined, next_protocol = NewNextProtocol}, - + case ssl_session:is_new(OldId, NewId) of true -> handle_new_session(NewId, CipherSuite, Compression, State#state{connection_states = ConnectionStates}); false -> - handle_resumed_session(NewId, State#state{connection_states = ConnectionStates}) + handle_resumed_session(NewId, State#state{connection_states = ConnectionStates}) end end; @@ -419,8 +419,7 @@ hello(Hello = #client_hello{client_version = ClientVersion}, negotiated_version = Version, session = Session}); #alert{} = Alert -> - handle_own_alert(Alert, ClientVersion, hello, State), - {stop, normal, State} + handle_own_alert(Alert, ClientVersion, hello, State) end; hello(timeout, State) -> @@ -451,8 +450,7 @@ abbreviated(#finished{verify_data = Data} = Finished, next_state_connection(abbreviated, ack_connection(State#state{connection_states = ConnectionStates})); #alert{} = Alert -> - handle_own_alert(Alert, Version, abbreviated, State), - {stop, normal, State} + handle_own_alert(Alert, Version, abbreviated, State) end; abbreviated(#finished{verify_data = Data} = Finished, @@ -472,8 +470,7 @@ abbreviated(#finished{verify_data = Data} = Finished, connection_states = ConnectionStates})); #alert{} = Alert -> - handle_own_alert(Alert, Version, abbreviated, State), - {stop, normal, State} + handle_own_alert(Alert, Version, abbreviated, State) end; abbreviated(timeout, State) -> @@ -497,8 +494,7 @@ certify(#certificate{asn1_certificates = []}, fail_if_no_peer_cert = true}} = State) -> Alert = ?ALERT_REC(?FATAL,?HANDSHAKE_FAILURE), - handle_own_alert(Alert, Version, certify, State), - {stop, normal, State}; + handle_own_alert(Alert, Version, certify, State); certify(#certificate{asn1_certificates = []}, #state{role = server, @@ -521,8 +517,7 @@ certify(#certificate{} = Cert, handle_peer_cert(PeerCert, PublicKeyInfo, State#state{client_certificate_requested = false}); #alert{} = Alert -> - handle_own_alert(Alert, Version, certify, State), - {stop, normal, State} + handle_own_alert(Alert, Version, certify, State) end; certify(#server_key_exchange{} = KeyExchangeMsg, @@ -534,8 +529,7 @@ certify(#server_key_exchange{} = KeyExchangeMsg, {Record, State} = next_record(State1), next_state(certify, certify, Record, State); #alert{} = Alert -> - handle_own_alert(Alert, Version, certify, State0), - {stop, normal, State0} + handle_own_alert(Alert, Version, certify, State0) end; certify(#server_key_exchange{} = Msg, @@ -559,8 +553,7 @@ certify(#server_hello_done{}, State = State0#state{connection_states = ConnectionStates}, client_certify_and_key_exchange(State); #alert{} = Alert -> - handle_own_alert(Alert, Version, certify, State0), - {stop, normal, State0} + handle_own_alert(Alert, Version, certify, State0) end; %% Master secret is calculated from premaster_secret @@ -578,8 +571,7 @@ certify(#server_hello_done{}, session = Session}, client_certify_and_key_exchange(State); #alert{} = Alert -> - handle_own_alert(Alert, Version, certify, State0), - {stop, normal, State0} + handle_own_alert(Alert, Version, certify, State0) end; certify(#client_key_exchange{} = Msg, @@ -595,8 +587,7 @@ certify(#client_key_exchange{exchange_keys = Keys}, certify_client_key_exchange(ssl_handshake:decode_client_key(Keys, KeyAlg, Version), State) catch #alert{} = Alert -> - handle_own_alert(Alert, Version, certify, State), - {stop, normal, State} + handle_own_alert(Alert, Version, certify, State) end; @@ -621,8 +612,7 @@ certify_client_key_exchange(#encrypted_premaster_secret{premaster_secret= EncPMS {Record, State} = next_record(State1), next_state(certify, cipher, Record, State); #alert{} = Alert -> - handle_own_alert(Alert, Version, certify, State0), - {stop, normal, State0} + handle_own_alert(Alert, Version, certify, State0) end; certify_client_key_exchange(#client_diffie_hellman_public{dh_public = ClientPublicDhKey}, @@ -635,8 +625,7 @@ certify_client_key_exchange(#client_diffie_hellman_public{dh_public = ClientPubl {Record, State} = next_record(State1), next_state(certify, cipher, Record, State); #alert{} = Alert -> - handle_own_alert(Alert, Version, certify, State0), - {stop, normal, State0} + handle_own_alert(Alert, Version, certify, State0) end. %%-------------------------------------------------------------------- @@ -665,8 +654,7 @@ cipher(#certificate_verify{signature = Signature, hashsign_algorithm = CertHashS {Record, State} = next_record(State0), next_state(cipher, cipher, Record, State); #alert{} = Alert -> - handle_own_alert(Alert, Version, cipher, State0), - {stop, normal, State0} + handle_own_alert(Alert, Version, cipher, State0) end; % client must send a next protocol message if we are expecting it @@ -692,8 +680,7 @@ cipher(#finished{verify_data = Data} = Finished, Session = register_session(Role, Host, Port, Session0), cipher_role(Role, Data, Session, State); #alert{} = Alert -> - handle_own_alert(Alert, Version, cipher, State), - {stop, normal, State} + handle_own_alert(Alert, Version, cipher, State) end; % only allowed to send next_protocol message after change cipher spec @@ -788,8 +775,10 @@ handle_sync_event({application_data, Data}, From, StateName, State#state{send_queue = queue:in({From, Data}, Queue)}, get_timeout(State)}; -handle_sync_event(start, StartFrom, hello, State) -> - hello(start, State#state{start_or_recv_from = StartFrom}); +handle_sync_event({start, Timeout}, StartFrom, hello, State) -> + Timer = start_or_recv_cancel_timer(Timeout, StartFrom), + hello(start, State#state{start_or_recv_from = StartFrom, + timer = Timer}); %% The two clauses below could happen if a server upgrades a socket in %% active mode. Note that in this case we are lucky that @@ -798,13 +787,16 @@ handle_sync_event(start, StartFrom, hello, State) -> %% mode before telling the client that it is willing to upgrade %% and before calling ssl:ssl_accept/2. These clauses are %% here to make sure it is the users problem and not owers if -%% they upgrade a active socket. -handle_sync_event(start, _, connection, State) -> +%% they upgrade an active socket. +handle_sync_event({start,_}, _, connection, State) -> {reply, connected, connection, State, get_timeout(State)}; -handle_sync_event(start, _From, error, {Error, State = #state{}}) -> +handle_sync_event({start,_}, _From, error, {Error, State = #state{}}) -> {stop, {shutdown, Error}, {error, Error}, State}; -handle_sync_event(start, StartFrom, StateName, State) -> - {next_state, StateName, State#state{start_or_recv_from = StartFrom}, get_timeout(State)}; + +handle_sync_event({start, Timeout}, StartFrom, StateName, State) -> + Timer = start_or_recv_cancel_timer(Timeout, StartFrom), + {next_state, StateName, State#state{start_or_recv_from = StartFrom, + timer = Timer}, get_timeout(State)}; handle_sync_event(close, _, StateName, State) -> %% Run terminate before returning @@ -835,13 +827,17 @@ handle_sync_event({shutdown, How0}, _, StateName, {stop, normal, Error, State} end; -handle_sync_event({recv, N}, RecvFrom, connection = StateName, State0) -> - passive_receive(State0#state{bytes_to_read = N, start_or_recv_from = RecvFrom}, StateName); +handle_sync_event({recv, N, Timeout}, RecvFrom, connection = StateName, State0) -> + Timer = start_or_recv_cancel_timer(Timeout, RecvFrom), + passive_receive(State0#state{bytes_to_read = N, + start_or_recv_from = RecvFrom, timer = Timer}, StateName); %% Doing renegotiate wait with handling request until renegotiate is %% finished. Will be handled by next_state_is_connection/2. -handle_sync_event({recv, N}, RecvFrom, StateName, State) -> - {next_state, StateName, State#state{bytes_to_read = N, start_or_recv_from = RecvFrom}, +handle_sync_event({recv, N, Timeout}, RecvFrom, StateName, State) -> + Timer = start_or_recv_cancel_timer(Timeout, RecvFrom), + {next_state, StateName, State#state{bytes_to_read = N, start_or_recv_from = RecvFrom, + timer = Timer}, get_timeout(State)}; handle_sync_event({new_user, User}, _From, StateName, @@ -963,7 +959,7 @@ handle_info({Protocol, _, Data}, StateName, next_state(StateName, StateName, Record, State); #alert{} = Alert -> handle_normal_shutdown(Alert, StateName, State0), - {stop, normal, State0} + {stop, {shutdown, own_alert}, State0} end; handle_info({CloseTag, Socket}, StateName, @@ -984,7 +980,7 @@ handle_info({CloseTag, Socket}, StateName, ok end, handle_normal_shutdown(?ALERT_REC(?FATAL, ?CLOSE_NOTIFY), StateName, State), - {stop, normal, State}; + {stop, {shutdown, transport_closed}, State}; handle_info({ErrorTag, Socket, econnaborted}, StateName, #state{socket = Socket, start_or_recv_from = StartFrom, role = Role, @@ -1005,7 +1001,21 @@ handle_info({'DOWN', MonitorRef, _, _, _}, _, handle_info(allow_renegotiate, StateName, State) -> {next_state, StateName, State#state{allow_renegotiate = true}, get_timeout(State)}; - + +handle_info({cancel_start_or_recv, StartFrom}, StateName, + #state{renegotiation = {false, first}} = State) when StateName =/= connection -> + gen_fsm:reply(StartFrom, {error, timeout}), + {stop, {shutdown, user_timeout}, State#state{timer = undefined}}; + +handle_info({cancel_start_or_recv, RecvFrom}, StateName, #state{start_or_recv_from = RecvFrom} = State) -> + gen_fsm:reply(RecvFrom, {error, timeout}), + {next_state, StateName, State#state{start_or_recv_from = undefined, + bytes_to_read = undefined, + timer = undefined}, get_timeout(State)}; + +handle_info({cancel_start_or_recv, _RecvFrom}, StateName, State) -> + {next_state, StateName, State#state{timer = undefined}, get_timeout(State)}; + handle_info(Msg, StateName, State) -> Report = io_lib:format("SSL: Got unexpected info: ~p ~n", [Msg]), error_logger:info_report(Report), @@ -1022,6 +1032,20 @@ terminate(_, _, #state{terminated = true}) -> %% we want to guarantee that Transport:close has been called %% when ssl:close/1 returns. ok; + +terminate({shutdown, transport_closed}, StateName, #state{send_queue = SendQueue, + renegotiation = Renegotiate} = State) -> + handle_unrecv_data(StateName, State), + handle_trusted_certs_db(State), + notify_senders(SendQueue), + notify_renegotiater(Renegotiate); + +terminate({shutdown, own_alert}, _StateName, #state{send_queue = SendQueue, + renegotiation = Renegotiate} = State) -> + handle_trusted_certs_db(State), + notify_senders(SendQueue), + notify_renegotiater(Renegotiate); + terminate(Reason, connection, #state{negotiated_version = Version, connection_states = ConnectionStates, transport_cb = Transport, @@ -1032,16 +1056,14 @@ terminate(Reason, connection, #state{negotiated_version = Version, notify_renegotiater(Renegotiate), BinAlert = terminate_alert(Reason, Version, ConnectionStates), Transport:send(Socket, BinAlert), - workaround_transport_delivery_problems(Socket, Transport, Reason), - Transport:close(Socket); + workaround_transport_delivery_problems(Socket, Transport); -terminate(Reason, _StateName, #state{transport_cb = Transport, +terminate(_Reason, _StateName, #state{transport_cb = Transport, socket = Socket, send_queue = SendQueue, renegotiation = Renegotiate} = State) -> handle_trusted_certs_db(State), notify_senders(SendQueue), notify_renegotiater(Renegotiate), - workaround_transport_delivery_problems(Socket, Transport, Reason), Transport:close(Socket). %%-------------------------------------------------------------------- @@ -1216,18 +1238,13 @@ init_diffie_hellman(DbHandle,_, DHParamFile, server) -> end. sync_send_all_state_event(FsmPid, Event) -> - sync_send_all_state_event(FsmPid, Event, infinity). - -sync_send_all_state_event(FsmPid, Event, Timeout) -> - try gen_fsm:sync_send_all_state_event(FsmPid, Event, Timeout) + try gen_fsm:sync_send_all_state_event(FsmPid, Event, infinity) catch exit:{noproc, _} -> {error, closed}; - exit:{timeout, _} -> - {error, timeout}; exit:{normal, _} -> {error, closed}; - exit:{shutdown, _} -> + exit:{{shutdown, _},_} -> {error, closed} end. @@ -1324,8 +1341,7 @@ new_server_hello(#server_hello{cipher_suite = CipherSuite, next_state(hello, certify, Record, State) catch #alert{} = Alert -> - handle_own_alert(Alert, Version, hello, State0), - {stop, normal, State0} + handle_own_alert(Alert, Version, hello, State0) end. resumed_server_hello(#state{session = Session, @@ -1345,8 +1361,7 @@ resumed_server_hello(#state{session = Session, {Record, State} = next_record(State2), next_state(hello, abbreviated, Record, State); #alert{} = Alert -> - handle_own_alert(Alert, Version, hello, State0), - {stop, normal, State0} + handle_own_alert(Alert, Version, hello, State0) end. handle_new_session(NewId, CipherSuite, Compression, #state{session = Session0} = State0) -> @@ -1371,8 +1386,7 @@ handle_resumed_session(SessId, #state{connection_states = ConnectionStates0, session = Session}), next_state(hello, abbreviated, Record, State); #alert{} = Alert -> - handle_own_alert(Alert, Version, hello, State0), - {stop, normal, State0} + handle_own_alert(Alert, Version, hello, State0) end. @@ -1389,8 +1403,7 @@ client_certify_and_key_exchange(#state{negotiated_version = Version} = next_state(certify, cipher, Record, State) catch throw:#alert{} = Alert -> - handle_own_alert(Alert, Version, certify, State0), - {stop, normal, State0} + handle_own_alert(Alert, Version, certify, State0) end. do_client_certify_and_key_exchange(State0) -> @@ -1769,6 +1782,7 @@ read_application_data(Data, #state{user_application = {_Mon, Pid}, socket_options = SOpts, bytes_to_read = BytesToRead, start_or_recv_from = RecvFrom, + timer = Timer, user_data_buffer = Buffer0} = State0) -> Buffer1 = if Buffer0 =:= <<>> -> Data; @@ -1778,9 +1792,11 @@ read_application_data(Data, #state{user_application = {_Mon, Pid}, case get_data(SOpts, BytesToRead, Buffer1) of {ok, ClientData, Buffer} -> % Send data SocketOpt = deliver_app_data(Socket, SOpts, ClientData, Pid, RecvFrom), + cancel_timer(Timer), State = State0#state{user_data_buffer = Buffer, start_or_recv_from = undefined, - bytes_to_read = 0, + timer = undefined, + bytes_to_read = undefined, socket_options = SocketOpt }, if @@ -1793,6 +1809,8 @@ read_application_data(Data, #state{user_application = {_Mon, Pid}, end; {more, Buffer} -> % no reply, we need more data next_record(State0#state{user_data_buffer = Buffer}); + {passive, Buffer} -> + next_record_if_active(State0#state{user_data_buffer = Buffer}); {error,_Reason} -> %% Invalid packet in packet mode deliver_packet_error(Socket, SOpts, Buffer1, Pid, RecvFrom), {stop, normal, State0} @@ -1834,6 +1852,9 @@ is_time_to_renegotiate(_,_) -> %% Picks ClientData get_data(_, _, <<>>) -> {more, <<>>}; +%% Recv timed out save buffer data until next recv +get_data(#socket_options{active=false}, undefined, Buffer) -> + {passive, Buffer}; get_data(#socket_options{active=Active, packet=Raw}, BytesToRead, Buffer) when Raw =:= raw; Raw =:= 0 -> %% Raw Mode if @@ -1962,8 +1983,7 @@ handle_tls_handshake(Handle, StateName, #state{tls_packets = [Packet | Packets]} end. next_state(Current,_, #alert{} = Alert, #state{negotiated_version = Version} = State) -> - handle_own_alert(Alert, Version, Current, State), - {stop, normal, State}; + handle_own_alert(Alert, Version, Current, State); next_state(_,Next, no_record, State) -> {next_state, Next, State, get_timeout(State)}; @@ -2001,8 +2021,7 @@ next_state(Current, Next, #ssl_tls{type = ?HANDSHAKE, fragment = Data}, State = State0#state{tls_packets = Packets, tls_handshake_buffer = Buf}, handle_tls_handshake(Handle, Next, State) catch throw:#alert{} = Alert -> - handle_own_alert(Alert, Version, Current, State0), - {stop, normal, State0} + handle_own_alert(Alert, Version, Current, State0) end; next_state(_, StateName, #ssl_tls{type = ?APPLICATION_DATA, fragment = Data}, State0) -> @@ -2141,7 +2160,6 @@ initial_state(Role, Host, Port, Socket, {SSLOptions, SocketOptions}, User, tls_record_buffer = <<>>, tls_cipher_texts = [], user_application = {Monitor, User}, - bytes_to_read = 0, user_data_buffer = <<>>, log_alert = true, session_cache_cb = SessionCacheCb, @@ -2258,13 +2276,13 @@ handle_alert(#alert{level = ?FATAL} = Alert, StateName, handle_alert(#alert{level = ?WARNING, description = ?CLOSE_NOTIFY} = Alert, StateName, State) -> handle_normal_shutdown(Alert, StateName, State), - {stop, normal, State}; + {stop, {shutdown, peer_close}, State}; handle_alert(#alert{level = ?WARNING, description = ?NO_RENEGOTIATION} = Alert, StateName, #state{log_alert = Log, renegotiation = {true, internal}} = State) -> log_alert(Log, StateName, Alert), handle_normal_shutdown(Alert, StateName, State), - {stop, normal, State}; + {stop, {shutdown, peer_close}, State}; handle_alert(#alert{level = ?WARNING, description = ?NO_RENEGOTIATION} = Alert, StateName, #state{log_alert = Log, renegotiation = {true, From}} = State0) -> @@ -2317,8 +2335,8 @@ handle_own_alert(Alert, Version, StateName, try %% Try to tell the other side {BinMsg, _} = encode_alert(Alert, Version, ConnectionStates), - linux_workaround_transport_delivery_problems(Alert, Socket), - Transport:send(Socket, BinMsg) + Transport:send(Socket, BinMsg), + workaround_transport_delivery_problems(Socket, Transport) catch _:_ -> %% Can crash if we are in a uninitialized state ignore end, @@ -2327,7 +2345,8 @@ handle_own_alert(Alert, Version, StateName, handle_normal_shutdown(Alert,StateName, State) catch _:_ -> ok - end. + end, + {stop, {shutdown, own_alert}, State}. handle_normal_shutdown(Alert, _, #state{socket = Socket, start_or_recv_from = StartFrom, @@ -2342,8 +2361,7 @@ handle_normal_shutdown(Alert, StateName, #state{socket = Socket, handle_unexpected_message(Msg, Info, #state{negotiated_version = Version} = State) -> Alert = ?ALERT_REC(?FATAL,?UNEXPECTED_MESSAGE), - handle_own_alert(Alert, Version, {Info, Msg}, State), - {stop, normal, State}. + handle_own_alert(Alert, Version, {Info, Msg}, State). make_premaster_secret({MajVer, MinVer}, rsa) -> Rand = ssl:random_bytes(?NUM_OF_PREMASTERSECRET_BYTES-2), @@ -2364,9 +2382,11 @@ ack_connection(#state{renegotiation = {true, From}} = State) -> gen_fsm:reply(From, ok), State#state{renegotiation = undefined}; ack_connection(#state{renegotiation = {false, first}, - start_or_recv_from = StartFrom} = State) when StartFrom =/= undefined -> + start_or_recv_from = StartFrom, + timer = Timer} = State) when StartFrom =/= undefined -> gen_fsm:reply(StartFrom, connected), - State#state{renegotiation = undefined, start_or_recv_from = undefined}; + cancel_timer(Timer), + State#state{renegotiation = undefined, start_or_recv_from = undefined, timer = undefined}; ack_connection(State) -> State. @@ -2401,36 +2421,35 @@ notify_renegotiater({true, From}) when not is_atom(From) -> notify_renegotiater(_) -> ok. -terminate_alert(Reason, Version, ConnectionStates) when Reason == normal; Reason == shutdown; +terminate_alert(Reason, Version, ConnectionStates) when Reason == normal; Reason == user_close -> {BinAlert, _} = encode_alert(?ALERT_REC(?WARNING, ?CLOSE_NOTIFY), Version, ConnectionStates), BinAlert; +terminate_alert({shutdown, _}, Version, ConnectionStates) -> + {BinAlert, _} = encode_alert(?ALERT_REC(?WARNING, ?CLOSE_NOTIFY), + Version, ConnectionStates), + BinAlert; + terminate_alert(_, Version, ConnectionStates) -> {BinAlert, _} = encode_alert(?ALERT_REC(?FATAL, ?INTERNAL_ERROR), Version, ConnectionStates), BinAlert. -workaround_transport_delivery_problems(_,_, user_close) -> - ok; -workaround_transport_delivery_problems(Socket, Transport, _) -> +workaround_transport_delivery_problems(Socket, gen_tcp = Transport) -> %% Standard trick to try to make sure all - %% data sent to to tcp port is really sent - %% before tcp port is closed so that the peer will - %% get a correct error message. + %% data sent to the tcp port is really delivered to the + %% peer application before tcp port is closed so that the peer will + %% get the correct TLS alert message and not only a transport close. inet:setopts(Socket, [{active, false}]), Transport:shutdown(Socket, write), - Transport:recv(Socket, 0). - -linux_workaround_transport_delivery_problems(#alert{level = ?FATAL}, Socket) -> - case os:type() of - {unix, linux} -> - inet:setopts(Socket, [{nodelay, true}]); - _ -> - ok - end; -linux_workaround_transport_delivery_problems(_, _) -> - ok. + %% Will return when other side has closed or after 30 s + %% e.g. we do not want to hang if something goes wrong + %% with the network but we want to maximise the odds that + %% peer application gets all data sent on the tcp connection. + Transport:recv(Socket, 0, 30000); +workaround_transport_delivery_problems(Socket, Transport) -> + Transport:close(Socket). get_timeout(#state{ssl_options=#ssl_options{hibernate_after = undefined}}) -> infinity; @@ -2503,3 +2522,31 @@ default_hashsign(_Version, KeyExchange) default_hashsign(_Version, KeyExchange) when KeyExchange == dh_anon -> {null, anon}. + +start_or_recv_cancel_timer(infinity, _RecvFrom) -> + undefined; +start_or_recv_cancel_timer(Timeout, RecvFrom) -> + erlang:send_after(Timeout, self(), {cancel_start_or_recv, RecvFrom}). + +cancel_timer(undefined) -> + ok; +cancel_timer(Timer) -> + erlang:cancel_timer(Timer). + +handle_unrecv_data(StateName, #state{socket = Socket, transport_cb = Transport} = State) -> + inet:setopts(Socket, [{active, false}]), + case Transport:recv(Socket, 0, 0) of + {error, closed} -> + ok; + {ok, Data} -> + handle_close_alert(Data, StateName, State) + end. + +handle_close_alert(Data, StateName, State0) -> + case next_tls_record(Data, State0) of + {#ssl_tls{type = ?ALERT, fragment = EncAlerts}, State} -> + [Alert|_] = decode_alerts(EncAlerts), + handle_normal_shutdown(Alert, StateName, State); + _ -> + ok + end. diff --git a/lib/ssl/src/ssl_handshake.erl b/lib/ssl/src/ssl_handshake.erl index fa1784714f..db21dac942 100644 --- a/lib/ssl/src/ssl_handshake.erl +++ b/lib/ssl/src/ssl_handshake.erl @@ -1401,6 +1401,7 @@ default_hash_signs() -> [?TLSEXT_SIGALG(sha512), ?TLSEXT_SIGALG(sha384), ?TLSEXT_SIGALG(sha256), + ?TLSEXT_SIGALG(sha224), ?TLSEXT_SIGALG(sha), ?TLSEXT_SIGALG_DSA(sha), ?TLSEXT_SIGALG_RSA(md5)]}. diff --git a/lib/ssl/src/ssl_manager.erl b/lib/ssl/src/ssl_manager.erl index 0cf4f2ce33..13689ce7d8 100644 --- a/lib/ssl/src/ssl_manager.erl +++ b/lib/ssl/src/ssl_manager.erl @@ -24,8 +24,6 @@ -module(ssl_manager). -behaviour(gen_server). --include("ssl_internal.hrl"). - %% Internal application API -export([start_link/1, start_link_dist/1, connection_init/2, cache_pem_file/2, diff --git a/lib/ssl/src/ssl_session.erl b/lib/ssl/src/ssl_session.erl index 2ad422fc03..a24b2d9444 100644 --- a/lib/ssl/src/ssl_session.erl +++ b/lib/ssl/src/ssl_session.erl @@ -72,15 +72,12 @@ valid_session(#session{time_stamp = TimeStamp}, LifeTime) -> server_id(Port, <<>>, _SslOpts, _Cert, _, _) -> {ssl_manager:new_session_id(Port), undefined}; -server_id(Port, SuggestedId, - #ssl_options{reuse_sessions = ReuseEnabled, - reuse_session = ReuseFun}, - Cert, Cache, CacheCb) -> +server_id(Port, SuggestedId, Options, Cert, Cache, CacheCb) -> LifeTime = case application:get_env(ssl, session_lifetime) of {ok, Time} when is_integer(Time) -> Time; _ -> ?'24H_in_sec' end, - case is_resumable(SuggestedId, Port, ReuseEnabled,ReuseFun, + case is_resumable(SuggestedId, Port, Options, Cache, CacheCb, LifeTime, Cert) of {true, Resumed} -> @@ -112,9 +109,9 @@ select_session(Sessions, #ssl_options{ciphers = Ciphers}, OwnCert) -> [[Id, _]|_] -> Id end. -is_resumable(_, _, false, _, _, _, _, _) -> +is_resumable(_, _, #ssl_options{reuse_sessions = false}, _, _, _, _) -> {false, undefined}; -is_resumable(SuggestedSessionId, Port, true, ReuseFun, Cache, +is_resumable(SuggestedSessionId, Port, #ssl_options{reuse_session = ReuseFun} = Options, Cache, CacheCb, SecondLifeTime, OwnCert) -> case CacheCb:lookup(Cache, {Port, SuggestedSessionId}) of #session{cipher_suite = CipherSuite, @@ -125,6 +122,7 @@ is_resumable(SuggestedSessionId, Port, true, ReuseFun, Cache, case resumable(IsResumable) andalso (OwnCert == SessionOwnCert) andalso valid_session(Session, SecondLifeTime) + andalso reusable_options(Options, Session) andalso ReuseFun(SuggestedSessionId, PeerCert, Compression, CipherSuite) of @@ -139,3 +137,9 @@ resumable(new) -> false; resumable(IsResumable) -> IsResumable. + +reusable_options(#ssl_options{fail_if_no_peer_cert = true, + verify = verify_peer}, Session) -> + (Session#session.peer_certificate =/= undefined); +reusable_options(_,_) -> + true. diff --git a/lib/ssl/test/erl_make_certs.erl b/lib/ssl/test/erl_make_certs.erl index 254aa6d2f9..d6bdd05d01 100644 --- a/lib/ssl/test/erl_make_certs.erl +++ b/lib/ssl/test/erl_make_certs.erl @@ -137,10 +137,10 @@ decode_key(PemBin, Pw) -> encode_key(Key = #'RSAPrivateKey'{}) -> {ok, Der} = 'OTP-PUB-KEY':encode('RSAPrivateKey', Key), - {'RSAPrivateKey', list_to_binary(Der), not_encrypted}; + {'RSAPrivateKey', Der, not_encrypted}; encode_key(Key = #'DSAPrivateKey'{}) -> {ok, Der} = 'OTP-PUB-KEY':encode('DSAPrivateKey', Key), - {'DSAPrivateKey', list_to_binary(Der), not_encrypted}. + {'DSAPrivateKey', Der, not_encrypted}. make_tbs(SubjectKey, Opts) -> Version = list_to_atom("v"++integer_to_list(proplists:get_value(version, Opts, 3))), diff --git a/lib/ssl/test/ssl_basic_SUITE.erl b/lib/ssl/test/ssl_basic_SUITE.erl index a202aca943..faed91e559 100644 --- a/lib/ssl/test/ssl_basic_SUITE.erl +++ b/lib/ssl/test/ssl_basic_SUITE.erl @@ -248,6 +248,7 @@ api_tests() -> [connection_info, peername, peercert, + peercert_with_client_cert, sockname, versions, controlling_process, @@ -258,7 +259,9 @@ api_tests() -> shutdown_both, shutdown_error, hibernate, - listen_socket + listen_socket, + ssl_accept_timeout, + ssl_recv_timeout ]. certificate_verify_tests() -> @@ -274,6 +277,7 @@ certificate_verify_tests() -> server_verify_client_once_passive, server_verify_client_once_active, server_verify_client_once_active_once, + new_server_wants_peer_cert, client_verify_none_passive, client_verify_none_active, client_verify_none_active_once, @@ -788,6 +792,43 @@ peercert(Config) when is_list(Config) -> peercert_result(Socket) -> ssl:peercert(Socket). +%%-------------------------------------------------------------------- + +peercert_with_client_cert(doc) -> + [""]; +peercert_with_client_cert(suite) -> + []; +peercert_with_client_cert(Config) when is_list(Config) -> + ClientOpts = ?config(client_dsa_opts, Config), + ServerOpts = ?config(server_dsa_verify_opts, Config), + {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config), + + Server = ssl_test_lib:start_server([{node, ClientNode}, {port, 0}, + {from, self()}, + {mfa, {?MODULE, peercert_result, []}}, + {options, ServerOpts}]), + Port = ssl_test_lib:inet_port(Server), + Client = ssl_test_lib:start_client([{node, ServerNode}, {port, Port}, + {host, Hostname}, + {from, self()}, + {mfa, {?MODULE, peercert_result, []}}, + {options, ClientOpts}]), + + ServerCertFile = proplists:get_value(certfile, ServerOpts), + [{'Certificate', ServerBinCert, _}]= ssl_test_lib:pem_to_der(ServerCertFile), + ClientCertFile = proplists:get_value(certfile, ClientOpts), + [{'Certificate', ClientBinCert, _}]= ssl_test_lib:pem_to_der(ClientCertFile), + + ServerMsg = {ok, ClientBinCert}, + ClientMsg = {ok, ServerBinCert}, + + test_server:format("Testcase ~p, Client ~p Server ~p ~n", + [self(), Client, Server]), + + ssl_test_lib:check_result(Server, ServerMsg, Client, ClientMsg), + + ssl_test_lib:close(Server), + ssl_test_lib:close(Client). %%-------------------------------------------------------------------- sockname(doc) -> @@ -3610,9 +3651,14 @@ no_reuses_session_server_restart_new_cert(Config) when is_list(Config) -> %% Make sure session is registered test_server:sleep(?SLEEP), + Monitor = erlang:monitor(process, Server), ssl_test_lib:close(Server), ssl_test_lib:close(Client0), - + receive + {'DOWN', Monitor, _, _, _} -> + ok + end, + Server1 = ssl_test_lib:start_server([{node, ServerNode}, {port, Port}, {from, self()}, @@ -3719,10 +3765,14 @@ reuseaddr(Config) when is_list(Config) -> {from, self()}, {mfa, {ssl_test_lib, no_result, []}}, {options, [{active, false} | ClientOpts]}]), - test_server:sleep(?SLEEP), + Monitor = erlang:monitor(process, Server), ssl_test_lib:close(Server), ssl_test_lib:close(Client), - + receive + {'DOWN', Monitor, _, _, _} -> + ok + end, + Server1 = ssl_test_lib:start_server([{node, ServerNode}, {port, Port}, {from, self()}, @@ -3805,6 +3855,61 @@ listen_socket(Config) -> {error, enotconn} = ssl:shutdown(ListenSocket, read_write), ok = ssl:close(ListenSocket). +%%-------------------------------------------------------------------- +ssl_accept_timeout(doc) -> + ["Test ssl:ssl_accept timeout"]; +ssl_accept_timeout(suite) -> + []; +ssl_accept_timeout(Config) -> + process_flag(trap_exit, true), + ServerOpts = ?config(server_opts, Config), + {_, ServerNode, Hostname} = ssl_test_lib:run_where(Config), + Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0}, + {from, self()}, + {timeout, 5000}, + {mfa, {ssl_test_lib, + no_result_msg, []}}, + {options, ServerOpts}]), + Port = ssl_test_lib:inet_port(Server), + {ok, CSocket} = gen_tcp:connect(Hostname, Port, [binary, {active, true}]), + + receive + {tcp_closed, CSocket} -> + ssl_test_lib:check_result(Server, {error, timeout}), + receive + {'EXIT', Server, _} -> + [] = supervisor:which_children(ssl_connection_sup) + end + end. + +%%-------------------------------------------------------------------- +ssl_recv_timeout(doc) -> + ["Test ssl:ssl_accept timeout"]; +ssl_recv_timeout(suite) -> + []; +ssl_recv_timeout(Config) -> + ServerOpts = ?config(server_opts, Config), + ClientOpts = ?config(client_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, send_recv_result_timeout_server, []}}, + {options, [{active, false} | ServerOpts]}]), + Port = ssl_test_lib:inet_port(Server), + + Client = ssl_test_lib:start_client([{node, ClientNode}, {port, Port}, + {host, Hostname}, + {from, self()}, + {mfa, {?MODULE, + send_recv_result_timeout_client, []}}, + {options, [{active, false} | ClientOpts]}]), + + ssl_test_lib:check_result(Client, ok, Server, ok), + ssl_test_lib:close(Server), + ssl_test_lib:close(Client). %%-------------------------------------------------------------------- @@ -4041,6 +4146,67 @@ client_server_opts({KeyAlgo,_,_}, Config) when KeyAlgo == dss orelse KeyAlgo == {?config(client_dsa_opts, Config), ?config(server_dsa_opts, Config)}. + +%%-------------------------------------------------------------------- + +new_server_wants_peer_cert(doc) -> + ["Test that server configured to do client certification does" + " not reuse session without a client certificate."]; +new_server_wants_peer_cert(suite) -> + []; +new_server_wants_peer_cert(Config) when is_list(Config) -> + ServerOpts = ?config(server_opts, Config), + VServerOpts = [{verify, verify_peer}, {fail_if_no_peer_cert, true} + | ?config(server_verification_opts, Config)], + ClientOpts = ?config(client_verification_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, peercert_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, {ssl_test_lib, no_result, []}}, + {options, ClientOpts}]), + + Monitor = erlang:monitor(process, Server), + ssl_test_lib:close(Server), + ssl_test_lib:close(Client), + receive + {'DOWN', Monitor, _, _, _} -> + ok + end, + + Server1 = ssl_test_lib:start_server([{node, ServerNode}, {port, Port}, + {from, self()}, + {mfa, {?MODULE, peercert_result, []}}, + {options, VServerOpts}]), + Client1 = + ssl_test_lib:start_client([{node, ClientNode}, {port, Port}, + {host, Hostname}, + {from, self()}, + {mfa, {ssl_test_lib, no_result, []}}, + {options, [ClientOpts]}]), + + CertFile = proplists:get_value(certfile, ClientOpts), + [{'Certificate', BinCert, _}]= ssl_test_lib:pem_to_der(CertFile), + + ServerMsg = {error, no_peercert}, + Sever1Msg = {ok, BinCert}, + + ssl_test_lib:check_result(Server, ServerMsg, Server1, Sever1Msg), + + ssl_test_lib:close(Server1), + ssl_test_lib:close(Client), + ssl_test_lib:close(Client1). + + %%-------------------------------------------------------------------- %%% Internal functions %%-------------------------------------------------------------------- @@ -4049,6 +4215,23 @@ send_recv_result(Socket) -> {ok,"Hello world"} = ssl:recv(Socket, 11), ok. +send_recv_result_timeout_client(Socket) -> + {error, timeout} = ssl:recv(Socket, 11, 500), + ssl:send(Socket, "Hello world"), + receive + Msg -> + io:format("Msg ~p~n",[Msg]) + after 500 -> + ok + end, + {ok, "Hello world"} = ssl:recv(Socket, 11, 500), + ok. +send_recv_result_timeout_server(Socket) -> + ssl:send(Socket, "Hello"), + {ok, "Hello world"} = ssl:recv(Socket, 11), + ssl:send(Socket, " world"), + ok. + recv_close(Socket) -> {error, closed} = ssl:recv(Socket, 11), receive diff --git a/lib/ssl/test/ssl_test_lib.erl b/lib/ssl/test/ssl_test_lib.erl index 63731ee25c..f1f5b9ae0a 100644 --- a/lib/ssl/test/ssl_test_lib.erl +++ b/lib/ssl/test/ssl_test_lib.erl @@ -72,7 +72,13 @@ run_server(Opts) -> run_server(ListenSocket, Opts). run_server(ListenSocket, Opts) -> - AcceptSocket = connect(ListenSocket, Opts), + do_run_server(ListenSocket, connect(ListenSocket, Opts), Opts). + +do_run_server(_, {error, timeout} = Result, Opts) -> + Pid = proplists:get_value(from, Opts), + Pid ! {self(), Result}; + +do_run_server(ListenSocket, AcceptSocket, Opts) -> Node = proplists:get_value(node, Opts), Pid = proplists:get_value(from, Opts), {Module, Function, Args} = proplists:get_value(mfa, Opts), @@ -102,7 +108,8 @@ run_server(ListenSocket, Opts) -> connect(ListenSocket, Opts) -> Node = proplists:get_value(node, Opts), ReconnectTimes = proplists:get_value(reconnect_times, Opts, 0), - AcceptSocket = connect(ListenSocket, Node, 1 + ReconnectTimes, dummy), + Timeout = proplists:get_value(timeout, Opts, infinity), + AcceptSocket = connect(ListenSocket, Node, 1 + ReconnectTimes, dummy, Timeout), case ReconnectTimes of 0 -> AcceptSocket; @@ -111,15 +118,21 @@ connect(ListenSocket, Opts) -> AcceptSocket end. -connect(_, _, 0, AcceptSocket) -> +connect(_, _, 0, AcceptSocket, _) -> AcceptSocket; -connect(ListenSocket, Node, N, _) -> +connect(ListenSocket, Node, N, _, Timeout) -> test_server:format("ssl:transport_accept(~p)~n", [ListenSocket]), {ok, AcceptSocket} = rpc:call(Node, ssl, transport_accept, [ListenSocket]), - test_server:format("ssl:ssl_accept(~p)~n", [AcceptSocket]), - ok = rpc:call(Node, ssl, ssl_accept, [AcceptSocket]), - connect(ListenSocket, Node, N-1, AcceptSocket). + test_server:format("ssl:ssl_accept(~p, ~p)~n", [AcceptSocket, Timeout]), + + case rpc:call(Node, ssl, ssl_accept, [AcceptSocket, Timeout]) of + ok -> + connect(ListenSocket, Node, N-1, AcceptSocket, Timeout); + Result -> + Result + end. + remove_close_msg(0) -> ok; diff --git a/lib/ssl/test/ssl_to_openssl_SUITE.erl b/lib/ssl/test/ssl_to_openssl_SUITE.erl index 98ef050b14..f4e19b3f87 100644 --- a/lib/ssl/test/ssl_to_openssl_SUITE.erl +++ b/lib/ssl/test/ssl_to_openssl_SUITE.erl @@ -1080,14 +1080,13 @@ ssl2_erlang_server_openssl_client(Config) when is_list(Config) -> OpenSslPort = open_port({spawn, Cmd}, [stderr_to_stdout]), port_command(OpenSslPort, Data), - + receive + {'EXIT', OpenSslPort, _} -> + ok + + end, ssl_test_lib:check_result(Server, {error,"protocol version"}), - - %% Clean close down! Server needs to be closed first !! - ssl_test_lib:close(Server), - close_port(OpenSslPort), - process_flag(trap_exit, false), - ok. + process_flag(trap_exit, false). %%-------------------------------------------------------------------- erlang_client_openssl_server_npn(doc) -> diff --git a/lib/ssl/vsn.mk b/lib/ssl/vsn.mk index e381b73c27..adfb29e639 100644 --- a/lib/ssl/vsn.mk +++ b/lib/ssl/vsn.mk @@ -1 +1 @@ -SSL_VSN = 5.1 +SSL_VSN = 5.1.2 |