diff options
Diffstat (limited to 'lib/ssl')
-rw-r--r-- | lib/ssl/doc/src/new_ssl.xml | 7 | ||||
-rw-r--r-- | lib/ssl/doc/src/notes.xml | 61 | ||||
-rw-r--r-- | lib/ssl/src/ssl.appup.src | 6 | ||||
-rw-r--r-- | lib/ssl/src/ssl.erl | 49 | ||||
-rw-r--r-- | lib/ssl/src/ssl_certificate.erl | 53 | ||||
-rw-r--r-- | lib/ssl/src/ssl_connection.erl | 321 | ||||
-rw-r--r-- | lib/ssl/src/ssl_handshake.erl | 31 | ||||
-rw-r--r-- | lib/ssl/src/ssl_internal.hrl | 2 | ||||
-rw-r--r-- | lib/ssl/test/ssl_basic_SUITE.erl | 669 | ||||
-rw-r--r-- | lib/ssl/test/ssl_packet_SUITE.erl | 174 | ||||
-rw-r--r-- | lib/ssl/test/ssl_test_lib.erl | 115 | ||||
-rw-r--r-- | lib/ssl/test/ssl_to_openssl_SUITE.erl | 120 | ||||
-rw-r--r-- | lib/ssl/vsn.mk | 7 |
13 files changed, 1036 insertions, 579 deletions
diff --git a/lib/ssl/doc/src/new_ssl.xml b/lib/ssl/doc/src/new_ssl.xml index a83c2d1383..08868a1b3c 100644 --- a/lib/ssl/doc/src/new_ssl.xml +++ b/lib/ssl/doc/src/new_ssl.xml @@ -495,12 +495,15 @@ end </func> <func> - <name>renegotiate(Socket) -> ok</name> + <name>renegotiate(Socket) -> ok | {error, Reason}</name> <fsummary> Initiates a new handshake.</fsummary> <type> <v>Socket = sslsocket()</v> </type> - <desc><p>Initiates a new handshake.</p> + <desc><p>Initiates a new handshake. A notable return value is + <c>{error, renegotiation_rejected}</c> indicating that the peer + refused to go through with the renegotiation but the connection + is still active using the previously negotiated session.</p> </desc> </func> diff --git a/lib/ssl/doc/src/notes.xml b/lib/ssl/doc/src/notes.xml index 2dd11bc88e..9d13427677 100644 --- a/lib/ssl/doc/src/notes.xml +++ b/lib/ssl/doc/src/notes.xml @@ -30,6 +30,67 @@ </header> <p>This document describes the changes made to the SSL application. </p> +<section><title>SSL 3.11</title> + + <section><title>Fixed Bugs and Malfunctions</title> + <list> + <item> + <p> + Fixes handling of the option fail_if_no_peer_cert and + some undocumented options. Thanks to Rory Byrne.</p> + <p> + Own Id: OTP-8557</p> + </item> + </list> + </section> + + <section><title>Improvements and New Features</title> + <list> + <item> + <p> + Support for Diffie-Hellman. ssl-3.11 requires + public_key-0.6.</p> + <p> + Own Id: OTP-7046</p> + </item> + <item> + <p> + New ssl now properly handles ssl renegotiation, and + initiates a renegotiation if ssl/ltls-sequence numbers + comes close to the max value. However RFC-5746 is not yet + supported, but will be in an upcoming release.</p> + <p> + Own Id: OTP-8517</p> + </item> + <item> + <p> + When gen_tcp is configured with the {packet,http} option, + it automatically switches to expect HTTP Headers after a + HTTP Request/Response line has been received. This update + fixes ssl to behave in the same way. Thanks to Rory + Byrne.</p> + <p> + Own Id: OTP-8545</p> + </item> + <item> + <p> + Ssl now correctly verifies the extended_key_usage + extension and also allows the user to verify application + specific extensions by supplying an appropriate fun.</p> + <p> + Own Id: OTP-8554 Aux Id: OTP-8553 </p> + </item> + <item> + <p> + Fixed ssl:transport_accept/2 to return properly when + socket is closed. Thanks to Rory Byrne.</p> + <p> + Own Id: OTP-8560</p> + </item> + </list> + </section> + +</section> <section><title>SSL 3.10.9</title> diff --git a/lib/ssl/src/ssl.appup.src b/lib/ssl/src/ssl.appup.src index fdda65021d..e8ae6846aa 100644 --- a/lib/ssl/src/ssl.appup.src +++ b/lib/ssl/src/ssl.appup.src @@ -9,7 +9,8 @@ {"3.10.5", [{restart_application, ssl}]}, {"3.10.6", [{restart_application, ssl}]}, {"3.10.7", [{restart_application, ssl}]}, - {"3.10.8", [{restart_application, ssl}]} + {"3.10.8", [{restart_application, ssl}]}, + {"3.10.9", [{restart_application, ssl}]} ], [ {"3.10", [{restart_application, ssl}]}, @@ -19,6 +20,7 @@ {"3.10.4", [{restart_application, ssl}]}, {"3.10.5", [{restart_application, ssl}]}, {"3.10.6", [{restart_application, ssl}]}, - {"3.10.8", [{restart_application, ssl}]} + {"3.10.8", [{restart_application, ssl}]}, + {"3.10.9", [{restart_application, ssl}]} ]}. diff --git a/lib/ssl/src/ssl.erl b/lib/ssl/src/ssl.erl index 965e40a109..3cd4c7fdbd 100644 --- a/lib/ssl/src/ssl.erl +++ b/lib/ssl/src/ssl.erl @@ -153,18 +153,23 @@ transport_accept(#sslsocket{pid = {ListenSocket, #config{cb=CbInfo, ssl=SslOpts} %% and options should be inherited. EmOptions = emulated_options(), {ok, InetValues} = inet:getopts(ListenSocket, EmOptions), - {CbModule,_,_} = CbInfo, - {ok, Socket} = CbModule:accept(ListenSocket, Timeout), - inet:setopts(Socket, internal_inet_values()), - {ok, Port} = inet:port(Socket), - case ssl_connection_sup:start_child([server, "localhost", Port, Socket, - {SslOpts, socket_options(InetValues)}, self(), - CbInfo]) of - {ok, Pid} -> - CbModule:controlling_process(Socket, Pid), - {ok, SslSocket#sslsocket{pid = Pid}}; - {error, Reason} -> - {error, Reason} + ok = inet:setopts(ListenSocket, internal_inet_values()), + {CbModule,_,_} = CbInfo, + case CbModule:accept(ListenSocket, Timeout) of + {ok, Socket} -> + ok = inet:setopts(ListenSocket, InetValues), + {ok, Port} = inet:port(Socket), + ConnArgs = [server, "localhost", Port, Socket, + {SslOpts, socket_options(InetValues)}, self(), CbInfo], + case ssl_connection_sup:start_child(ConnArgs) of + {ok, Pid} -> + CbModule:controlling_process(Socket, Pid), + {ok, SslSocket#sslsocket{pid = Pid}}; + {error, Reason} -> + {error, Reason} + end; + {error, Reason} -> + {error, Reason} end; transport_accept(#sslsocket{} = ListenSocket, Timeout) -> @@ -515,6 +520,9 @@ handle_options(Opts0, Role) -> end end, + UserFailIfNoPeerCert = validate_option(fail_if_no_peer_cert, + proplists:get_value(fail_if_no_peer_cert, Opts, false)), + {Verify, FailIfNoPeerCert, CaCertDefault} = %% Handle 0, 1, 2 for backwards compatibility case proplists:get_value(verify, Opts, verify_none) of @@ -527,9 +535,7 @@ handle_options(Opts0, Role) -> verify_none -> {verify_none, false, ca_cert_default(verify_none, Role)}; verify_peer -> - {verify_peer, proplists:get_value(fail_if_no_peer_cert, - Opts, false), - ca_cert_default(verify_peer, Role)}; + {verify_peer, UserFailIfNoPeerCert, ca_cert_default(verify_peer, Role)}; Value -> throw({error, {eoptions, {verify, Value}}}) end, @@ -540,9 +546,9 @@ handle_options(Opts0, Role) -> versions = handle_option(versions, Opts, []), verify = validate_option(verify, Verify), verify_fun = handle_option(verify_fun, Opts, VerifyFun), - fail_if_no_peer_cert = validate_option(fail_if_no_peer_cert, - FailIfNoPeerCert), + fail_if_no_peer_cert = FailIfNoPeerCert, verify_client_once = handle_option(verify_client_once, Opts, false), + validate_extensions_fun = handle_option(validate_extensions_fun, Opts, undefined), depth = handle_option(depth, Opts, 1), certfile = CertFile, keyfile = handle_option(keyfile, Opts, CertFile), @@ -559,7 +565,7 @@ handle_options(Opts0, Role) -> }, CbInfo = proplists:get_value(cb_info, Opts, {gen_tcp, tcp, tcp_closed}), - SslOptions = [versions, verify, verify_fun, + SslOptions = [versions, verify, verify_fun, validate_extensions_fun, fail_if_no_peer_cert, verify_client_once, depth, certfile, keyfile, key, password, cacertfile, dhfile, ciphers, @@ -594,6 +600,9 @@ validate_option(fail_if_no_peer_cert, Value) validate_option(verify_client_once, Value) when Value == true; Value == false -> Value; + +validate_option(validate_extensions_fun, Value) when Value == undefined; is_function(Value) -> + Value; validate_option(depth, Value) when is_integer(Value), Value >= 0, Value =< 255-> Value; @@ -623,6 +632,8 @@ validate_option(ciphers, Value) when is_list(Value) -> try cipher_suites(Version, Value) catch exit:_ -> + throw({error, {eoptions, {ciphers, Value}}}); + error:_-> throw({error, {eoptions, {ciphers, Value}}}) end; validate_option(reuse_session, Value) when is_function(Value) -> @@ -644,7 +655,7 @@ validate_versions([Version | Rest], Versions) when Version == 'tlsv1.1'; Version == tlsv1; Version == sslv3 -> validate_versions(Rest, Versions); -validate_versions(Ver, Versions) -> +validate_versions([Ver| _], Versions) -> throw({error, {eoptions, {Ver, {versions, Versions}}}}). validate_inet_option(mode, Value) diff --git a/lib/ssl/src/ssl_certificate.erl b/lib/ssl/src/ssl_certificate.erl index d97b61a5ce..686e90a70c 100644 --- a/lib/ssl/src/ssl_certificate.erl +++ b/lib/ssl/src/ssl_certificate.erl @@ -1,19 +1,19 @@ %% %% %CopyrightBegin% -%% -%% Copyright Ericsson AB 2007-2009. All Rights Reserved. -%% +%% +%% Copyright Ericsson AB 2007-2010. All Rights Reserved. +%% %% The contents of this file are subject to the Erlang Public License, %% Version 1.1, (the "License"); you may not use this file except in %% compliance with the License. You should have received a copy of the %% Erlang Public License along with this software. If not, it can be %% retrieved online at http://www.erlang.org/. -%% +%% %% Software distributed under the License is distributed on an "AS IS" %% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See %% the License for the specific language governing rights and limitations %% under the License. -%% +%% %% %CopyrightEnd% %% @@ -29,10 +29,12 @@ -include("ssl_alert.hrl"). -include("ssl_internal.hrl"). -include("ssl_debug.hrl"). +-include_lib("public_key/include/public_key.hrl"). -export([trusted_cert_and_path/3, certificate_chain/2, - file_to_certificats/1]). + file_to_certificats/1, + validate_extensions/6]). %%==================================================================== %% Internal application API @@ -87,6 +89,30 @@ file_to_certificats(File) -> {ok, List} = ssl_manager:cache_pem_file(File), [Bin || {cert, Bin, not_encrypted} <- List]. + +%% Validates ssl/tls specific extensions +validate_extensions([], ValidationState, UnknownExtensions, _, AccErr, _) -> + {UnknownExtensions, ValidationState, AccErr}; + +validate_extensions([#'Extension'{extnID = ?'id-ce-extKeyUsage', + extnValue = KeyUse, + critical = true} | Rest], + ValidationState, UnknownExtensions, Verify, AccErr0, Role) -> + case is_valid_extkey_usage(KeyUse, Role) of + true -> + validate_extensions(Rest, ValidationState, UnknownExtensions, + Verify, AccErr0, Role); + false -> + AccErr = + not_valid_extension({bad_cert, invalid_ext_key_usage}, Verify, AccErr0), + validate_extensions(Rest, ValidationState, UnknownExtensions, Verify, AccErr, Role) + end; + +validate_extensions([Extension | Rest], ValidationState, UnknownExtensions, + Verify, AccErr, Role) -> + validate_extensions(Rest, ValidationState, [Extension | UnknownExtensions], + Verify, AccErr, Role). + %%-------------------------------------------------------------------- %%% Internal functions %%-------------------------------------------------------------------- @@ -154,3 +180,18 @@ not_valid(Alert, true, _) -> throw(Alert); not_valid(_, false, {ErlCert, Path}) -> {ErlCert, Path, [{bad_cert, unknown_ca}]}. + +is_valid_extkey_usage(KeyUse, client) -> + %% Client wants to verify server + is_valid_key_usage(KeyUse,?'id-kp-serverAuth'); +is_valid_extkey_usage(KeyUse, server) -> + %% Server wants to verify client + is_valid_key_usage(KeyUse, ?'id-kp-clientAuth'). + +is_valid_key_usage(KeyUse, Use) -> + lists:member(Use, KeyUse). + +not_valid_extension(Error, true, _) -> + throw(Error); +not_valid_extension(Error, false, AccErrors) -> + [Error | AccErrors]. diff --git a/lib/ssl/src/ssl_connection.erl b/lib/ssl/src/ssl_connection.erl index dcf3331e6b..8ff001b172 100644 --- a/lib/ssl/src/ssl_connection.erl +++ b/lib/ssl/src/ssl_connection.erl @@ -87,7 +87,7 @@ user_data_buffer, % binary() %% tls_buffer, % Keeps a lookahead one packet if available log_alert, % boolean() - renegotiation, % boolean() + renegotiation, % {boolean(), From | internal | peer} recv_during_renegotiation, %boolean() send_queue % queue() }). @@ -222,7 +222,7 @@ peer_certificate(ConnectionPid) -> %% Description: %%-------------------------------------------------------------------- renegotiation(ConnectionPid) -> - send_all_state_event(ConnectionPid, renegotiate). + sync_send_all_state_event(ConnectionPid, renegotiate). %%==================================================================== %% ssl_connection_sup API @@ -383,32 +383,33 @@ abbreviated(Finished = #finished{}, #state{role = server, negotiated_version = Version, tls_handshake_hashes = Hashes, - session = #session{master_secret = MasterSecret}} = State) -> + session = #session{master_secret = MasterSecret}} = + State0) -> case ssl_handshake:verify_connection(Version, Finished, client, MasterSecret, Hashes) of verified -> - ack_connection(State), + State = ack_connection(State0), next_state_connection(State); #alert{} = Alert -> - handle_own_alert(Alert, Version, abbreviated, State), - {stop, normal, State} + handle_own_alert(Alert, Version, abbreviated, State0), + {stop, normal, State0} end; abbreviated(Finished = #finished{}, #state{role = client, tls_handshake_hashes = Hashes0, session = #session{master_secret = MasterSecret}, - negotiated_version = Version} = State) -> + negotiated_version = Version} = State0) -> case ssl_handshake:verify_connection(Version, Finished, server, MasterSecret, Hashes0) of verified -> - {ConnectionStates, Hashes} = finalize_client_handshake(State), - ack_connection(State), + {ConnectionStates, Hashes} = finalize_client_handshake(State0), + State = ack_connection(State0), next_state_connection(State#state{tls_handshake_hashes = Hashes, connection_states = ConnectionStates}); #alert{} = Alert -> - handle_own_alert(Alert, Version, abbreviated, State), - {stop, normal, State} + handle_own_alert(Alert, Version, abbreviated, State0), + {stop, normal, State0} end. certify(socket_control, #state{role = server} = State) -> @@ -435,11 +436,13 @@ certify(#certificate{asn1_certificates = []}, certify(#certificate{} = Cert, #state{negotiated_version = Version, + role = Role, cert_db_ref = CertDbRef, ssl_options = Opts} = State) -> case ssl_handshake:certify(Cert, CertDbRef, Opts#ssl_options.depth, Opts#ssl_options.verify, - Opts#ssl_options.verify_fun) of + Opts#ssl_options.verify_fun, + Opts#ssl_options.validate_extensions_fun, Role) of {PeerCert, PublicKeyInfo} -> handle_peer_cert(PeerCert, PublicKeyInfo, State#state{client_certificate_requested = false}); @@ -464,7 +467,7 @@ certify(#server_key_exchange{} = KeyExchangeMsg, certify(#server_key_exchange{}, State = #state{role = client, negotiated_version = Version, key_algorithm = Alg}) - when Alg == rsa; Alg == dh_dss; Alg == dh_rsa -> + when Alg == rsa; Alg == dh_dss; Alg == dh_rsa -> Alert = ?ALERT_REC(?FATAL, ?UNEXPECTED_MESSAGE), handle_own_alert(Alert, Version, certify_server_key_exchange, State), {stop, normal, State}; @@ -606,19 +609,19 @@ cipher(#certificate_verify{signature = Signature}, end; cipher(#finished{} = Finished, - State = #state{negotiated_version = Version, - host = Host, - port = Port, - role = Role, - session = #session{master_secret = MasterSecret} - = Session0, - tls_handshake_hashes = Hashes}) -> - + #state{negotiated_version = Version, + host = Host, + port = Port, + role = Role, + session = #session{master_secret = MasterSecret} + = Session0, + tls_handshake_hashes = Hashes} = State0) -> + case ssl_handshake:verify_connection(Version, Finished, opposite_role(Role), MasterSecret, Hashes) of verified -> - ack_connection(State), + State = ack_connection(State0), Session = register_session(Role, Host, Port, Session0), case Role of client -> @@ -627,15 +630,15 @@ cipher(#finished{} = Finished, {NewConnectionStates, NewHashes} = finalize_server_handshake(State#state{ session = Session}), - NewState = - State#state{connection_states = NewConnectionStates, - session = Session, - tls_handshake_hashes = NewHashes}, - next_state_connection(NewState) + next_state_connection(State#state{connection_states = + NewConnectionStates, + session = Session, + tls_handshake_hashes = + NewHashes}) end; #alert{} = Alert -> - handle_own_alert(Alert, Version, cipher, State), - {stop, normal, State} + handle_own_alert(Alert, Version, cipher, State0), + {stop, normal, State0} end. connection(socket_control, #state{role = server} = State) -> @@ -655,8 +658,7 @@ connection(#hello_request{}, State = #state{host = Host, port = Port, Transport:send(Socket, BinMsg), {next_state, hello, next_record(State#state{connection_states = ConnectionStates1, - tls_handshake_hashes = Hashes1, - renegotiation = true})}; + tls_handshake_hashes = Hashes1})}; connection(#client_hello{} = Hello, #state{role = server} = State) -> hello(Hello, State). @@ -681,16 +683,17 @@ handle_event(#ssl_tls{type = ?HANDSHAKE, fragment = Data}, %% This message should not be included in handshake %% message hashes. Starts new handshake (renegotiation) Hs0 = ssl_handshake:init_hashes(), - ?MODULE:SName(Packet, State#state{tls_handshake_hashes=Hs0}); + ?MODULE:SName(Packet, State#state{tls_handshake_hashes=Hs0, + renegotiation = {true, peer}}); ({#hello_request{} = Packet, _}, {next_state, SName, State}) -> %% This message should not be included in handshake - %% message hashes. If allready in negotiation it will be ignored! + %% message hashes. Already in negotiation so it will be ignored! ?MODULE:SName(Packet, State); ({#client_hello{} = Packet, Raw}, {next_state, connection = SName, State}) -> Hs0 = ssl_handshake:init_hashes(), Hs1 = ssl_handshake:update_hashes(Hs0, Raw), ?MODULE:SName(Packet, State#state{tls_handshake_hashes=Hs1, - renegotiation = true}); + renegotiation = {true, peer}}); ({Packet, Raw}, {next_state, SName, State = #state{tls_handshake_hashes=Hs0}}) -> Hs1 = ssl_handshake:update_hashes(Hs0, Raw), ?MODULE:SName(Packet, State#state{tls_handshake_hashes=Hs1}); @@ -758,18 +761,23 @@ handle_event(#alert{level = ?WARNING, description = ?CLOSE_NOTIFY} = Alert, _, #state{from = From, role = Role} = State) -> alert_user(From, Alert, Role), {stop, normal, State}; -handle_event(#alert{level = ?WARNING} = Alert, StateName, + +handle_event(#alert{level = ?WARNING, description = ?NO_RENEGOTIATION} = Alert, StateName, + #state{log_alert = Log, renegotiation = {true, internal}} = State) -> + log_alert(Log, StateName, Alert), + {stop, normal, State}; + +handle_event(#alert{level = ?WARNING, description = ?NO_RENEGOTIATION} = Alert, StateName, + #state{log_alert = Log, renegotiation = {true, From}} = State) -> + log_alert(Log, StateName, Alert), + gen_fsm:reply(From, {error, renegotiation_rejected}), + {next_state, connection, next_record(State)}; + +handle_event(#alert{level = ?WARNING, description = ?USER_CANCELED} = Alert, StateName, #state{log_alert = Log} = State) -> log_alert(Log, StateName, Alert), -%%TODO: Could be user_canceled or no_negotiation should the latter be - %% treated as fatal?! - {next_state, StateName, next_record(State)}; - -handle_event(renegotiate, connection, State) -> - renegotiate(State); -handle_event(renegotiate, StateName, State) -> - %% Already in renegotiate ignore - {next_state, StateName, State}. + {next_state, StateName, next_record(State)}. + %%-------------------------------------------------------------------- %% Function: %% handle_sync_event(Event, From, StateName, @@ -812,7 +820,8 @@ handle_sync_event({application_data, Data0}, From, connection, ok end, renegotiate(State#state{connection_states = ConnectionStates, - send_queue = queue:in_r({From, RestData}, SendQueue)}) + send_queue = queue:in_r({From, RestData}, SendQueue), + renegotiation = {true, internal}}) end catch throw:Error -> {reply, Error, connection, State} @@ -892,6 +901,12 @@ handle_sync_event({set_opts, Opts0}, _From, StateName, end end; +handle_sync_event(renegotiate, From, connection, State) -> + renegotiate(State#state{renegotiation = {true, From}}); + +handle_sync_event(renegotiate, _, StateName, State) -> + {reply, {error, already_renegotiating}, StateName, State}; + handle_sync_event(info, _, StateName, #state{negotiated_version = Version, session = #session{cipher_suite = Suite}} = State) -> @@ -940,34 +955,6 @@ handle_info({Protocol, _, Data}, StateName, State = {stop, normal, State} end; -%% %% This is the code for {packet,ssl} removed because it was slower -%% %% than handling it in erlang. -%% handle_info(Data = #ssl_tls{}, StateName, -%% State = #state{tls_buffer = Buffer, -%% socket = Socket, -%% connection_states = ConnectionStates0}) -> -%% case Buffer of -%% buffer -> -%% {next_state, StateName, State#state{tls_buffer = [Data]}}; -%% continue -> -%% inet:setopts(Socket, [{active,once}]), -%% {Plain, ConnectionStates} = -%% ssl_record:decode_cipher_text(Data, ConnectionStates0), -%% gen_fsm:send_all_state_event(self(), Plain), -%% {next_state, StateName, -%% State#state{tls_buffer = buffer, -%% connection_states = ConnectionStates}}; -%% List when is_list(List) -> -%% {next_state, StateName, -%% State#state{tls_buffer = Buffer ++ [Data]}} -%% end; - -%% handle_info(CloseMsg = {_, Socket}, StateName0, -%% #state{socket = Socket,tls_buffer = [Msg]} = State0) -> -%% %% Hmm we have a ssl_tls msg buffered, handle that first -%% %% and it proberbly is a close alert -%% {next_state, StateName0, State0#state{tls_buffer=[Msg,{ssl_close,CloseMsg}]}}; - handle_info({CloseTag, Socket}, _StateName, #state{socket = Socket, close_tag = CloseTag, negotiated_version = Version, host = Host, @@ -1001,18 +988,23 @@ handle_info(A, StateName, State) -> %% necessary cleaning up. When it returns, the gen_fsm terminates with %% Reason. The return value is ignored. %%-------------------------------------------------------------------- -terminate(_Reason, connection, _S=#state{negotiated_version = Version, +terminate(_Reason, connection, #state{negotiated_version = Version, connection_states = ConnectionStates, transport_cb = Transport, - socket = Socket}) -> + socket = Socket, send_queue = SendQueue, + renegotiation = Renegotiate}) -> + notify_senders(SendQueue), + notify_renegotiater(Renegotiate), {BinAlert, _} = encode_alert(?ALERT_REC(?WARNING,?CLOSE_NOTIFY), Version, ConnectionStates), Transport:send(Socket, BinAlert), Transport:close(Socket); -terminate(_Reason, _StateName, _S=#state{transport_cb = Transport, - socket = Socket}) -> - Transport:close(Socket), - ok. +terminate(_Reason, _StateName, #state{transport_cb = Transport, + socket = Socket, send_queue = SendQueue, + renegotiation = Renegotiate}) -> + notify_senders(SendQueue), + notify_renegotiater(Renegotiate), + Transport:close(Socket). %%-------------------------------------------------------------------- %% Function: @@ -1161,37 +1153,9 @@ sync_send_all_state_event(FsmPid, Event, Timeout) -> alert_event(Alert) -> send_all_state_event(self(), Alert). - -%% TODO: This clause is not yet supported. Do we need -%% to support it, not supported by openssl! -handle_peer_cert(PeerCert, {Algorithm, _, _} = PublicKeyInfo, - #state{negotiated_version = Version, - session = Session} = - State0) when Algorithm == dh_rsa; - Algorithm == dh_dss -> - case public_key:pkix_is_fixed_dh_cert(PeerCert) of - true -> - %% TODO: extract DH-params from cert and save - %% Keys and SharedSecret in state. - %% {P, G, ServerPublicDhKey}= extract ..... - %% Keys = {ClientDhPublicKey, _} = - %% public_key:gen_key(#'DHParameter'{prime = P, base = G}), - %%SharedSecret = dh_shared_secret(ServerPublicDhKey, - %% ClientDhPublicKey, - %% [P, G]), - %% ssl_handshake:master_secret ... - State = State0#state{session = - Session#session{peer_certificate - = PeerCert}, - public_key_info = PublicKeyInfo}, - {next_state, certify, next_record(State)}; - - false -> - Alert = ?ALERT_REC(?FATAL,?HANDSHAKE_FAILURE), - handle_own_alert(Alert, Version, certify_certificate, State0), - {stop, normal, State0} - end; - +%% We do currently not support cipher suites that use fixed DH. +%% If we want to implement that we should add a code +%% here to extract DH parameters form cert. handle_peer_cert(PeerCert, PublicKeyInfo, #state{session = Session} = State0) -> State = State0#state{session = @@ -1374,11 +1338,11 @@ key_exchange(#state{role = server, key_algorithm = Algo} = State) Algo == dh_rsa -> State; -key_exchange(#state{role = server, key_algorithm = rsa_export} = State) -> +%key_exchange(#state{role = server, key_algorithm = rsa_export} = State) -> %% TODO when the public key in the server certificate is %% less than or equal to 512 bits in length dont send key_exchange %% but do it otherwise - State; +% State; key_exchange(#state{role = server, key_algorithm = Algo, diffie_hellman_params = Params, @@ -1411,20 +1375,10 @@ key_exchange(#state{role = server, key_algorithm = Algo, diffie_hellman_keys = Keys, tls_handshake_hashes = Hashes1}; -%% TODO: Not yet supported should be by default disabled when supported. -%% key_exchange(#state{role = server, key_algorithm = dh_anon, -%% connection_states = ConnectionStates0, -%% negotiated_version = Version, -%% tls_handshake_hashes = Hashes0, -%% socket = Socket, -%% transport_cb = Transport -%% } = State) -> -%% Msg = ssl_handshake:key_exchange(server, anonymous), -%% {BinMsg, ConnectionStates1, Hashes1} = -%% encode_handshake(Msg, Version, ConnectionStates0, Hashes0), -%% Transport:send(Socket, BinMsg), -%% State#state{connection_states = ConnectionStates1, -%% tls_handshake_hashes = Hashes1}; + +%% key_algorithm = dh_anon is not supported. Should be by default disabled +%% if support is implemented and then we need a key_exchange clause for it +%% here. key_exchange(#state{role = client, connection_states = ConnectionStates0, @@ -1609,9 +1563,6 @@ handle_server_key( ?ALERT_REC(?FATAL,?HANDSHAKE_FAILURE) end. -%%handle_clinet_key(_KeyExchangeMsg, State) -> -%% State. - verify_dh_params(Signed, Hash, {?rsaEncryption, PubKey, _PubKeyparams}) -> case public_key:decrypt_public(Signed, PubKey, [{rsa_pad, rsa_pkcs1_padding}]) of @@ -1732,19 +1683,49 @@ get_data(#socket_options{active=Active, packet=Raw}, BytesToRead, Buffer) end; get_data(#socket_options{packet=Type, packet_size=Size}, _, Buffer) -> PacketOpts = [{packet_size, Size}], - case erlang:decode_packet(Type, Buffer, PacketOpts) of + case decode_packet(Type, Buffer, PacketOpts) of {more, _} -> {ok, <<>>, Buffer}; Decoded -> Decoded end. -deliver_app_data(SO = #socket_options{active=once}, Data, Pid, From) -> - send_or_reply(once, Pid, From, format_reply(SO, Data)), - SO#socket_options{active=false}; -deliver_app_data(SO= #socket_options{active=Active}, Data, Pid, From) -> - send_or_reply(Active, Pid, From, format_reply(SO, Data)), - SO. +decode_packet({http, headers}, Buffer, PacketOpts) -> + decode_packet(httph, Buffer, PacketOpts); +decode_packet({http_bin, headers}, Buffer, PacketOpts) -> + decode_packet(httph_bin, Buffer, PacketOpts); +decode_packet(Type, Buffer, PacketOpts) -> + erlang:decode_packet(Type, Buffer, PacketOpts). + +%% Just like with gen_tcp sockets, an ssl socket that has been configured with +%% {packet, http} (or {packet, http_bin}) will automatically switch to expect +%% HTTP headers after it sees a HTTP Request or HTTP Response line. We +%% represent the current state as follows: +%% #socket_options.packet =:= http: Expect a HTTP Request/Response line +%% #socket_options.packet =:= {http, headers}: Expect HTTP Headers +%% Note that if the user has explicitly configured the socket to expect +%% HTTP headers using the {packet, httph} option, we don't do any automatic +%% switching of states. +deliver_app_data(SOpts = #socket_options{active=Active, packet=Type}, + Data, Pid, From) -> + send_or_reply(Active, Pid, From, format_reply(SOpts, Data)), + SO = case Data of + {P, _, _, _} when ((P =:= http_request) or (P =:= http_response)), + ((Type =:= http) or (Type =:= http_bin)) -> + SOpts#socket_options{packet={Type, headers}}; + http_eoh when tuple_size(Type) =:= 2 -> + % End of headers - expect another Request/Response line + {Type1, headers} = Type, + SOpts#socket_options{packet=Type1}; + _ -> + SOpts + end, + case Active of + once -> + SO#socket_options{active=false}; + _ -> + SO + end. format_reply(#socket_options{active=false, mode=Mode, header=Header}, Data) -> {ok, format_reply(Mode, Header, Data)}; @@ -1785,34 +1766,6 @@ opposite_role(server) -> send_user(Pid, Msg) -> Pid ! Msg. -%% %% This is the code for {packet,ssl} removed because it was slower -%% %% than handling it in erlang. -%% next_record(#state{socket = Socket, -%% tls_buffer = [Msg|Rest], -%% connection_states = ConnectionStates0} = State) -> -%% Buffer = -%% case Rest of -%% [] -> -%% inet:setopts(Socket, [{active,once}]), -%% buffer; -%% _ -> Rest -%% end, -%% case Msg of -%% #ssl_tls{} -> -%% {Plain, ConnectionStates} = -%% ssl_record:decode_cipher_text(Msg, ConnectionStates0), -%% gen_fsm:send_all_state_event(self(), Plain), -%% State#state{tls_buffer=Buffer, connection_states = ConnectionStates}; -%% {ssl_close, Msg} -> -%% self() ! Msg, -%% State#state{tls_buffer=Buffer} -%% end; -%% next_record(#state{socket = Socket, tls_buffer = undefined} = State) -> -%% inet:setopts(Socket, [{active,once}]), -%% State#state{tls_buffer=continue}; -%% next_record(State) -> -%% State#state{tls_buffer=continue}. - next_record(#state{tls_cipher_texts = [], socket = Socket} = State) -> inet:setopts(Socket, [{active,once}]), State; @@ -1857,7 +1810,8 @@ next_state_connection(#state{send_queue = Queue0, ok end, renegotiate(State#state{connection_states = ConnectionStates, - send_queue = queue:in_r({From, RestData}, Queue)}) + send_queue = queue:in_r({From, RestData}, Queue), + renegotiation = {true, internal}}) end; {empty, Queue0} -> next_state_is_connection(State) @@ -1922,7 +1876,7 @@ initial_state(Role, Host, Port, Socket, {SSLOptions, SocketOptions}, User, user_data_buffer = <<>>, log_alert = true, session_cache_cb = SessionCacheCb, - renegotiation = false, + renegotiation = {false, first}, recv_during_renegotiation = false, send_queue = queue:new() }. @@ -1939,8 +1893,12 @@ get_socket_opts(Socket, [mode | Tags], SockOpts, Acc) -> get_socket_opts(Socket, Tags, SockOpts, [{mode, SockOpts#socket_options.mode} | Acc]); get_socket_opts(Socket, [packet | Tags], SockOpts, Acc) -> - get_socket_opts(Socket, Tags, SockOpts, - [{packet, SockOpts#socket_options.packet} | Acc]); + case SockOpts#socket_options.packet of + {Type, headers} -> + get_socket_opts(Socket, Tags, SockOpts, [{packet, Type} | Acc]); + Type -> + get_socket_opts(Socket, Tags, SockOpts, [{packet, Type} | Acc]) + end; get_socket_opts(Socket, [header | Tags], SockOpts, Acc) -> get_socket_opts(Socket, Tags, SockOpts, [{header, SockOpts#socket_options.header} | Acc]); @@ -2031,11 +1989,16 @@ mpint_binary(Binary) -> <<?UINT32(Size), Binary/binary>>. -ack_connection(#state{renegotiation = true}) -> - ok; -ack_connection(#state{renegotiation = false, from = From}) -> - gen_fsm:reply(From, connected). - +ack_connection(#state{renegotiation = {true, Initiater}} = State) + when Initiater == internal; + Initiater == peer -> + State#state{renegotiation = undefined}; +ack_connection(#state{renegotiation = {true, From}} = State) -> + gen_fsm:reply(From, ok), + State#state{renegotiation = undefined}; +ack_connection(#state{renegotiation = {false, first}, from = From} = State) -> + gen_fsm:reply(From, connected), + State#state{renegotiation = undefined}. renegotiate(#state{role = client} = State) -> %% Handle same way as if server requested @@ -2055,5 +2018,13 @@ renegotiate(#state{role = server, Transport:send(Socket, BinMsg), {next_state, hello, next_record(State#state{connection_states = ConnectionStates, - tls_handshake_hashes = Hs0, - renegotiation = true})}. + tls_handshake_hashes = Hs0})}. +notify_senders(SendQueue) -> + lists:foreach(fun({From, _}) -> + gen_fsm:reply(From, {error, closed}) + end, queue:to_list(SendQueue)). + +notify_renegotiater({true, From}) when not is_atom(From) -> + gen_fsm:reply(From, {error, closed}); +notify_renegotiater(_) -> + ok. diff --git a/lib/ssl/src/ssl_handshake.erl b/lib/ssl/src/ssl_handshake.erl index ff78375b9f..9f5ac7106a 100644 --- a/lib/ssl/src/ssl_handshake.erl +++ b/lib/ssl/src/ssl_handshake.erl @@ -32,7 +32,7 @@ -include_lib("public_key/include/public_key.hrl"). -export([master_secret/4, client_hello/4, server_hello/3, hello/2, - hello_request/0, certify/5, certificate/3, + hello_request/0, certify/7, certificate/3, client_certificate_verify/6, certificate_verify/6, certificate_request/2, key_exchange/2, server_key_exchange_hash/2, finished/4, @@ -161,10 +161,25 @@ hello(#client_hello{client_version = ClientVersion, random = Random} = Hello, %% Description: Handles a certificate handshake message %%-------------------------------------------------------------------- certify(#certificate{asn1_certificates = ASN1Certs}, CertDbRef, - MaxPathLen, Verify, VerifyFun) -> + MaxPathLen, Verify, VerifyFun, ValidateFun, Role) -> [PeerCert | _] = ASN1Certs, VerifyBool = verify_bool(Verify), - + + ValidateExtensionFun = + case ValidateFun of + undefined -> + fun(Extensions, ValidationState, Verify0, AccError) -> + ssl_certificate:validate_extensions(Extensions, ValidationState, + [], Verify0, AccError, Role) + end; + Fun -> + fun(Extensions, ValidationState, Verify0, AccError) -> + {NewExtensions, NewValidationState, NewAccError} + = ssl_certificate:validate_extensions(Extensions, ValidationState, + [], Verify0, AccError, Role), + Fun(NewExtensions, NewValidationState, Verify0, NewAccError) + end + end, try %% Allow missing root_cert and check that with VerifyFun ssl_certificate:trusted_cert_and_path(ASN1Certs, CertDbRef, false) of @@ -174,6 +189,8 @@ certify(#certificate{asn1_certificates = ASN1Certs}, CertDbRef, [{max_path_length, MaxPathLen}, {verify, VerifyBool}, + {validate_extensions_fun, + ValidateExtensionFun}, {acc_errors, VerifyErrors}]), case Result of @@ -455,7 +472,7 @@ get_tls_handshake_aux(<<?BYTE(Type), ?UINT24(Length), Body:Length/binary,Rest/binary>>, KeyAlg, Version, Acc) -> Raw = <<?BYTE(Type), ?UINT24(Length), Body/binary>>, - H = dec_hs(Type, Body, key_excahange_alg(KeyAlg), Version), + H = dec_hs(Type, Body, key_exchange_alg(KeyAlg), Version), get_tls_handshake_aux(Rest, KeyAlg, Version, [{H,Raw} | Acc]); get_tls_handshake_aux(Data, _KeyAlg, _Version, Acc) -> {lists:reverse(Acc), Data}. @@ -960,10 +977,10 @@ sig_alg(Alg) when Alg == dh_dss; Alg == dhe_dss -> sig_alg(_) -> ?NULL. -key_excahange_alg(rsa) -> +key_exchange_alg(rsa) -> ?KEY_EXCHANGE_RSA; -key_excahange_alg(Alg) when Alg == dhe_rsa; Alg == dhe_dss; +key_exchange_alg(Alg) when Alg == dhe_rsa; Alg == dhe_dss; Alg == dh_dss; Alg == dh_rsa; Alg == dh_anon -> ?KEY_EXCHANGE_DIFFIE_HELLMAN; -key_excahange_alg(_) -> +key_exchange_alg(_) -> ?NULL. diff --git a/lib/ssl/src/ssl_internal.hrl b/lib/ssl/src/ssl_internal.hrl index ab24c28b2f..8d19abfe1e 100644 --- a/lib/ssl/src/ssl_internal.hrl +++ b/lib/ssl/src/ssl_internal.hrl @@ -57,6 +57,8 @@ verify_fun, % fun(CertVerifyErrors) -> boolean() fail_if_no_peer_cert, % boolean() verify_client_once, % boolean() + %% fun(Extensions, State, Verify, AccError) -> {Extensions, State, AccError} + validate_extensions_fun, depth, % integer() certfile, % file() keyfile, % file() diff --git a/lib/ssl/test/ssl_basic_SUITE.erl b/lib/ssl/test/ssl_basic_SUITE.erl index d1ee2ed77f..7f33efd7e1 100644 --- a/lib/ssl/test/ssl_basic_SUITE.erl +++ b/lib/ssl/test/ssl_basic_SUITE.erl @@ -26,12 +26,14 @@ -include("test_server.hrl"). -include("test_server_line.hrl"). +-include_lib("public_key/include/public_key.hrl"). -define('24H_in_sec', 86400). -define(TIMEOUT, 60000). -define(EXPIRE, 10). -define(SLEEP, 500). + -behaviour(ssl_session_cache_api). %% For the session cache tests @@ -151,22 +153,26 @@ all(doc) -> all(suite) -> [app, connection_info, controlling_process, controller_dies, peercert, connect_dist, - peername, sockname, socket_options, valid_ssl_options, versions, cipher_suites, + peername, sockname, socket_options, misc_ssl_options, versions, cipher_suites, upgrade, upgrade_with_timeout, tcp_connect, ipv6, ekeyfile, ecertfile, ecacertfile, eoptions, shutdown, shutdown_write, shutdown_both, shutdown_error, ciphers, - send_close, dh_params, + send_close, close_transport_accept, dh_params, server_verify_peer_passive, server_verify_peer_active, server_verify_peer_active_once, server_verify_none_passive, server_verify_none_active, server_verify_none_active_once, server_verify_no_cacerts, server_require_peer_cert_ok, server_require_peer_cert_fail, + server_verify_client_once_passive, + server_verify_client_once_active, + server_verify_client_once_active_once, client_verify_none_passive, client_verify_none_active, client_verify_none_active_once %%, session_cache_process_list, session_cache_process_mnesia ,reuse_session, reuse_session_expired, server_does_not_want_to_reuse_session, client_renegotiate, server_renegotiate, - client_no_wrap_sequence_number, server_no_wrap_sequence_number + client_no_wrap_sequence_number, server_no_wrap_sequence_number, + extended_key_usage, validate_extensions_fun ]. %% Test cases starts here. @@ -311,7 +317,7 @@ controller_dies(Config) when is_list(Config) -> get_close(Client, ?LINE), %% Test that clients die when process disappear - Server ! listen, test_server:sleep(?SLEEP), + Server ! listen, Tester = self(), Connect = fun(Pid) -> {ok, Socket} = ssl:connect(Hostname, Port, @@ -325,7 +331,7 @@ controller_dies(Config) when is_list(Config) -> get_close(Client2, ?LINE), %% Test that clients die when the controlling process have changed - Server ! listen, test_server:sleep(?SLEEP), + Server ! listen, Client3 = spawn_link(fun() -> Connect(Tester) end), Controller = spawn_link(fun() -> receive die_nice -> normal end end), @@ -349,7 +355,7 @@ controller_dies(Config) when is_list(Config) -> get_close(Controller, ?LINE), %% Test that servers die - Server ! listen, test_server:sleep(?SLEEP), + Server ! listen, LastClient = ssl_test_lib:start_client([{node, ClientNode}, {port, Port}, {host, Hostname}, {from, self()}, @@ -488,9 +494,9 @@ peername(Config) when is_list(Config) -> Port = ssl_test_lib:inet_port(Server), Client = ssl_test_lib:start_client([{node, ClientNode}, {port, Port}, {host, Hostname}, - {from, self()}, - {mfa, {?MODULE, peername_result, []}}, - {options, [{port, 0} | ClientOpts]}]), + {from, self()}, + {mfa, {?MODULE, peername_result, []}}, + {options, [{port, 0} | ClientOpts]}]), ClientPort = ssl_test_lib:inet_port(Client), ServerIp = ssl_test_lib:node_to_hostip(ServerNode), @@ -530,6 +536,7 @@ sockname(Config) when is_list(Config) -> {from, self()}, {mfa, {?MODULE, sockname_result, []}}, {options, [{port, 0} | ClientOpts]}]), + ClientPort = ssl_test_lib:inet_port(Client), ServerIp = ssl_test_lib:node_to_hostip(ServerNode), ClientIp = ssl_test_lib:node_to_hostip(ClientNode), @@ -606,57 +613,44 @@ socket_options_result(Socket, Options, DefaultValues, NewOptions, NewValues) -> ok. %%-------------------------------------------------------------------- -valid_ssl_options(doc) -> +misc_ssl_options(doc) -> ["Test what happens when we give valid options"]; -valid_ssl_options(suite) -> +misc_ssl_options(suite) -> []; -valid_ssl_options(Config) when is_list(Config) -> - ClientOpts = [{reuseaddr, true} | ?config(client_opts, Config)], - ServerOpts = [{reuseaddr, true} | ?config(server_opts, Config)], +misc_ssl_options(Config) when is_list(Config) -> + ClientOpts = ?config(client_opts, Config), + ServerOpts = ?config(server_opts, Config), {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config), - Port = ssl_test_lib:inet_port(ServerNode), - - StartOk = - fun(Peer, Pid, TestOpt) -> - receive - {Pid, ok} when Peer =:= server -> - ok; - {Pid, {ok, _}} when Peer =:= client -> - ok; - {Pid, Error} -> - test_server:fail({Peer, - {option_being_tested, TestOpt}, - {got, Error}}) - end - end, - - %% The following contains both documented and undocumented options as - %% listed in ssl:handle_options/2. It excludes file options which are - %% tested elsewhere (cacertfile, certfile, keyfile). - TestOpts = [{versions, []}, {verify, verify_none}, {verify_fun, fun(_) -> false end}, - {fail_if_no_peer_cert, false}, {verify_client_once, false}, - {depth, 1}, {key, undefined}, {password, "secret"}, {ciphers, []}, - {reuse_sessions, true}, {reuse_session, fun(_,_,_,_) -> true end}, - {renegotiate_at, 1000000000}, {debug, []}, + + %% Chek that ssl options not tested elsewhere are filtered away e.i. not passed to inet. + TestOpts = [{depth, 1}, + {key, undefined}, + {password, []}, + {reuse_session, fun(_,_,_,_) -> true end}, + {debug, []}, {cb_info, {gen_tcp, tcp, tcp_closed}}], - [begin - Server = - ssl_test_lib:start_server_error([{node, ServerNode}, {port, Port}, - {from, self()}, - {options, [TestOpt | ServerOpts]}]), - Client = - ssl_test_lib:start_client_error([{node, ClientNode}, {port, Port}, - {host, Hostname}, {from, self()}, - {options, [TestOpt | ClientOpts]}]), - StartOk(server, Server, TestOpt), - StartOk(client, Client, TestOpt), - ssl_test_lib:close(Server), - ssl_test_lib:close(Client), - ok - end || TestOpt <- TestOpts], - ok. + + Server = + ssl_test_lib:start_server([{node, ServerNode}, {port, 0}, + {from, self()}, + {mfa, {?MODULE, send_recv_result_active, []}}, + {options, TestOpts ++ 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_active, []}}, + {options, TestOpts ++ ClientOpts}]), + + test_server:format("Testcase ~p, Client ~p Server ~p ~n", + [self(), Client, Server]), + + ssl_test_lib:check_result(Server, ok, Client, ok), + ssl_test_lib:close(Server), + ssl_test_lib:close(Client). %%-------------------------------------------------------------------- versions(doc) -> @@ -729,6 +723,32 @@ send_close(Config) when is_list(Config) -> gen_tcp:close(TcpS), {error, _} = ssl:send(SslS, "Hello world"), ssl_test_lib:close(Server). + +%%-------------------------------------------------------------------- +close_transport_accept(doc) -> + ["Tests closing ssl socket when waiting on ssl:transport_accept/1"]; + +close_transport_accept(suite) -> + []; + +close_transport_accept(Config) when is_list(Config) -> + ServerOpts = ?config(server_opts, Config), + {_ClientNode, ServerNode, _Hostname} = ssl_test_lib:run_where(Config), + + Port = 0, + Opts = [{active, false} | ServerOpts], + {ok, ListenSocket} = rpc:call(ServerNode, ssl, listen, [Port, Opts]), + spawn_link(fun() -> + test_server:sleep(?SLEEP), + rpc:call(ServerNode, ssl, close, [ListenSocket]) + end), + case rpc:call(ServerNode, ssl, transport_accept, [ListenSocket]) of + {error, closed} -> + ok; + Other -> + exit({?LINE, Other}) + end. + %%-------------------------------------------------------------------- dh_params(doc) -> ["Test to specify DH-params file in server."]; @@ -852,9 +872,8 @@ tcp_connect(suite) -> []; tcp_connect(Config) when is_list(Config) -> - ClientOpts = ?config(client_opts, Config), ServerOpts = ?config(server_opts, Config), - {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config), + {_, ServerNode, Hostname} = ssl_test_lib:run_where(Config), TcpOpts = [binary, {reuseaddr, true}], Server = ssl_test_lib:start_upgrade_server([{node, ServerNode}, {port, 0}, @@ -879,7 +898,7 @@ tcp_connect(Config) when is_list(Config) -> ssl_test_lib:close(Server). -dummy(Socket) -> +dummy(_Socket) -> %% Should not happen as the ssl connection will not be established %% due to fatal handshake failiure exit(kill). @@ -934,12 +953,14 @@ ekeyfile(Config) when is_list(Config) -> ClientOpts = ?config(client_opts, Config), BadOpts = ?config(server_bad_key, Config), {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config), - Port = ssl_test_lib:inet_port(ServerNode), - + Server = - ssl_test_lib:start_server_error([{node, ServerNode}, {port, Port}, + ssl_test_lib:start_server_error([{node, ServerNode}, {port, 0}, {from, self()}, {options, BadOpts}]), + + Port = ssl_test_lib:inet_port(Server), + Client = ssl_test_lib:start_client_error([{node, ClientNode}, {port, Port}, {host, Hostname}, @@ -960,19 +981,21 @@ ecertfile(Config) when is_list(Config) -> ClientOpts = ?config(client_opts, Config), ServerBadOpts = ?config(server_bad_cert, Config), {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config), - Port = ssl_test_lib:inet_port(ServerNode), - Server0 = - ssl_test_lib:start_server_error([{node, ServerNode}, {port, Port}, + Server = + ssl_test_lib:start_server_error([{node, ServerNode}, {port, 0}, {from, self()}, {options, ServerBadOpts}]), - Client0 = + + Port = ssl_test_lib:inet_port(Server), + + Client = ssl_test_lib:start_client_error([{node, ClientNode}, {port, Port}, {host, Hostname}, {from, self()}, {options, ClientOpts}]), - ssl_test_lib:check_result(Server0, {error, ecertfile}, Client0, + ssl_test_lib:check_result(Server, {error, ecertfile}, Client, {error, closed}). @@ -987,15 +1010,18 @@ ecacertfile(Config) when is_list(Config) -> ClientOpts = [{reuseaddr, true}|?config(client_opts, Config)], ServerBadOpts = [{reuseaddr, true}|?config(server_bad_ca, Config)], {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config), - Port = ssl_test_lib:inet_port(ServerNode), Server0 = ssl_test_lib:start_server_error([{node, ServerNode}, - {port, Port}, {from, self()}, + {port, 0}, {from, self()}, {options, ServerBadOpts}]), + + Port0 = ssl_test_lib:inet_port(Server0), + + Client0 = ssl_test_lib:start_client_error([{node, ClientNode}, - {port, Port}, {host, Hostname}, + {port, Port0}, {host, Hostname}, {from, self()}, {options, ClientOpts}]), @@ -1008,11 +1034,14 @@ ecacertfile(Config) when is_list(Config) -> Server1 = ssl_test_lib:start_server_error([{node, ServerNode}, - {port, Port}, {from, self()}, + {port, 0}, {from, self()}, {options, ServerBadOpts1}]), + + Port1 = ssl_test_lib:inet_port(Server1), + Client1 = ssl_test_lib:start_client_error([{node, ClientNode}, - {port, Port}, {host, Hostname}, + {port, Port1}, {host, Hostname}, {from, self()}, {options, ClientOpts}]), @@ -1033,198 +1062,58 @@ eoptions(Config) when is_list(Config) -> ClientOpts = ?config(client_opts, Config), ServerOpts = ?config(server_opts, Config), {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config), - Port = ssl_test_lib:inet_port(ServerNode), - - %% Emulated opts - Server0 = - ssl_test_lib:start_server_error([{node, ServerNode}, {port, Port}, - {from, self()}, - {options, [{active, trice} | ServerOpts]}]), - Client0 = - ssl_test_lib:start_client_error([{node, ClientNode}, - {port, Port}, {host, Hostname}, - {from, self()}, - {options, [{active, trice} | ClientOpts]}]), - ssl_test_lib:check_result(Server0, {error, {eoptions, {active,trice}}}, - Client0, {error, {eoptions, {active,trice}}}), - - test_server:sleep(?SLEEP), - Server1 = - ssl_test_lib:start_server_error([{node, ServerNode}, {port, Port}, - {from, self()}, - {options, [{header, a} | ServerOpts]}]), - Client1 = - ssl_test_lib:start_client_error([{node, ClientNode}, {port, Port}, - {host, Hostname}, - {from, self()}, - {options, [{header, a} | ClientOpts]}]), - ssl_test_lib:check_result(Server1, {error, {eoptions, {header, a}}}, - Client1, {error, {eoptions, {header, a}}}), - - test_server:sleep(?SLEEP), - - - Server2 = - ssl_test_lib:start_server_error([{node, ServerNode}, {port, Port}, - {from, self()}, - {options, [{mode, a} | ServerOpts]}]), - - Client2 = - ssl_test_lib:start_client_error([{node, ClientNode}, - {port, Port}, {host, Hostname}, - {from, self()}, - {options, [{mode, a} | ClientOpts]}]), - ssl_test_lib:check_result(Server2, {error, {eoptions, {mode, a}}}, - Client2, {error, {eoptions, {mode, a}}}), - - - test_server:sleep(?SLEEP), - - Server3 = - ssl_test_lib:start_server_error([{node, ServerNode}, {port, Port}, - {from, self()}, - {options, [{packet, 8.0} | ServerOpts]}]), - Client3 = - ssl_test_lib:start_client_error([{node, ClientNode}, - {port, Port}, {host, Hostname}, - {from, self()}, - {options, [{packet, 8.0} | ClientOpts]}]), - ssl_test_lib:check_result(Server3, {error, {eoptions, {packet, 8.0}}}, - Client3, {error, {eoptions, {packet, 8.0}}}), - - test_server:sleep(?SLEEP), - - %% ssl - Server4 = - ssl_test_lib:start_server_error([{node, ServerNode}, {port, Port}, - {from, self()}, - {options, [{verify, 4} | ServerOpts]}]), - Client4 = - ssl_test_lib:start_client_error([{node, ClientNode}, {port, Port}, - {host, Hostname}, - {from, self()}, - {options, [{verify, 4} | ClientOpts]}]), - ssl_test_lib:check_result(Server4, {error, {eoptions, {verify, 4}}}, - Client4, {error, {eoptions, {verify, 4}}}), - - test_server:sleep(?SLEEP), - - Server5 = - ssl_test_lib:start_server_error([{node, ServerNode}, {port, Port}, - {from, self()}, - {options, [{depth, four} | ServerOpts]}]), - Client5 = - ssl_test_lib:start_client_error([{node, ClientNode}, {port, Port}, - {host, Hostname}, - {from, self()}, - {options, [{depth, four} | ClientOpts]}]), - ssl_test_lib:check_result(Server5, {error, {eoptions, {depth, four}}}, - Client5, {error, {eoptions, {depth, four}}}), - - test_server:sleep(?SLEEP), - - Server6 = - ssl_test_lib:start_server_error([{node, ServerNode}, {port, Port}, - {from, self()}, - {options, [{cacertfile, ""} | ServerOpts]}]), - Client6 = - ssl_test_lib:start_client_error([{node, ClientNode}, {port, Port}, - {host, Hostname}, - {from, self()}, - {options, [{cacertfile, ""} | ClientOpts]}]), - ssl_test_lib:check_result(Server6, {error, {eoptions, {cacertfile, ""}}}, - Client6, {error, {eoptions, {cacertfile, ""}}}), - - - test_server:sleep(?SLEEP), - - Server7 = - ssl_test_lib:start_server_error([{node, ServerNode}, {port, Port}, - {from, self()}, - {options, [{certfile, 'cert.pem'} | ServerOpts]}]), - Client7 = - ssl_test_lib:start_client_error([{node, ClientNode}, {port, Port}, - {host, Hostname}, - {from, self()}, - {options, [{certfile, 'cert.pem'} | ClientOpts]}]), - ssl_test_lib:check_result(Server7, - {error, {eoptions, {certfile, 'cert.pem'}}}, - Client7, {error, {eoptions, {certfile, 'cert.pem'}}}), - - test_server:sleep(?SLEEP), - - Server8 = - ssl_test_lib:start_server_error([{node, ServerNode}, {port, Port}, - {from, self()}, - {options, [{keyfile,'key.pem' } | ServerOpts]}]), - Client8 = - ssl_test_lib:start_client_error([{node, ClientNode}, {port, Port}, - {host, Hostname}, - {from, self()}, {options, [{keyfile, 'key.pem'} - | ClientOpts]}]), - ssl_test_lib:check_result(Server8, - {error, {eoptions, {keyfile, 'key.pem'}}}, - Client8, {error, {eoptions, {keyfile, 'key.pem'}}}), - - test_server:sleep(?SLEEP), - - Server9 = - ssl_test_lib:start_server_error([{node, ServerNode}, {port, Port}, - {from, self()}, - {options, [{key, 'key.pem' } | ServerOpts]}]), - Client9 = - ssl_test_lib:start_client_error([{node, ClientNode}, {port, Port}, - {host, Hostname}, - {from, self()}, {options, [{key, 'key.pem'} - | ClientOpts]}]), - ssl_test_lib:check_result(Server9, {error, {eoptions, {key, 'key.pem'}}}, - Client9, {error, {eoptions, {key, 'key.pem'}}}), - - - test_server:sleep(?SLEEP), - - Server10 = - ssl_test_lib:start_server_error([{node, ServerNode}, {port, Port}, - {from, self()}, - {options, [{password, foo} | ServerOpts]}]), - Client10 = - ssl_test_lib:start_client_error([{node, ClientNode}, {port, Port}, - {host, Hostname}, - {from, self()}, - {options, [{password, foo} | ClientOpts]}]), - ssl_test_lib:check_result(Server10, {error, {eoptions, {password, foo}}}, - Client10, {error, {eoptions, {password, foo}}}), - - test_server:sleep(?SLEEP), - - %% Misc - Server11 = - ssl_test_lib:start_server_error([{node, ServerNode}, {port, Port}, - {from, self()}, - {options, [{ssl_imp, cool} | ServerOpts]}]), - Client11 = - ssl_test_lib:start_client_error([{node, ClientNode}, {port, Port}, - {host, Hostname}, - {from, self()}, - {options, [{ssl_imp, cool} | ClientOpts]}]), - ssl_test_lib:check_result(Server11, {error, {eoptions, {ssl_imp, cool}}}, - Client11, {error, {eoptions, {ssl_imp, cool}}}), + Check = fun(Client, Server, {versions, [sslv2, sslv3]} = Option) -> + ssl_test_lib:check_result(Server, + {error, {eoptions, {sslv2, Option}}}, + Client, + {error, {eoptions, {sslv2, Option}}}); + (Client, Server, Option) -> + ssl_test_lib:check_result(Server, + {error, {eoptions, Option}}, + Client, + {error, {eoptions, Option}}) + end, + TestOpts = [{versions, [sslv2, sslv3]}, + {ssl_imp, cool}, + {verify, 4}, + {verify_fun, function}, + {fail_if_no_peer_cert, 0}, + {verify_client_once, 1}, + {validate_extensions_fun, function}, + {depth, four}, + {certfile, 'cert.pem'}, + {keyfile,'key.pem' }, + {password, foo}, + {cacertfile, ""}, + {dhfile,'dh.pem' }, + {ciphers, [{foo, bar, sha, ignore}]}, + {reuse_session, foo}, + {reuse_sessions, 0}, + {renegotiate_at, "10"}, + {debug, 1}, + {mode, depech}, + {packet, 8.0}, + {packet_size, "2"}, + {header, a}, + {active, trice}, + {key, 'key.pem' }], - test_server:sleep(?SLEEP), - - Server12 = - ssl_test_lib:start_server_error([{node, ServerNode}, {port, Port}, - {from, self()}, - {options, [{debug, cool} | ServerOpts]}]), - Client12 = - ssl_test_lib:start_client_error([{node, ClientNode}, {port, Port}, - {host, Hostname}, - {from, self()}, - {options, [{debug, cool} | ClientOpts]}]), - ssl_test_lib:check_result(Server12, {error, {eoptions, {debug, cool}}}, - Client12, {error, {eoptions, {debug, cool}}}). + [begin + Server = + ssl_test_lib:start_server_error([{node, ServerNode}, {port, 0}, + {from, self()}, + {options, [TestOpt | ServerOpts]}]), + %% Will never reach a point where port is used. + Client = + ssl_test_lib:start_client_error([{node, ClientNode}, {port, 0}, + {host, Hostname}, {from, self()}, + {options, [TestOpt | ClientOpts]}]), + Check(Client, Server, TestOpt), + ok + end || TestOpt <- TestOpts], + ok. %%-------------------------------------------------------------------- shutdown(doc) -> @@ -1816,8 +1705,122 @@ server_verify_none_active_once(Config) when is_list(Config) -> ssl_test_lib:check_result(Server, ok, Client, ok), ssl_test_lib:close(Server), ssl_test_lib:close(Client). +%%-------------------------------------------------------------------- +server_verify_client_once_passive(doc) -> + ["Test server option verify_client_once"]; +server_verify_client_once_passive(suite) -> + []; + +server_verify_client_once_passive(Config) when is_list(Config) -> + ClientOpts = ?config(client_opts, Config), + ServerOpts = ?config(server_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, send_recv_result, []}}, + {options, [{active, false}, {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, {?MODULE, send_recv_result, []}}, + {options, [{active, false} | ClientOpts]}]), + + ssl_test_lib:check_result(Server, ok, Client0, ok), + ssl_test_lib:close(Client0), + Server ! listen, + Client1 = ssl_test_lib:start_client([{node, ClientNode}, {port, Port}, + {host, Hostname}, + {from, self()}, + {mfa, {?MODULE, result_ok, []}}, + {options, [{active, false} | ClientOpts]}]), + + ssl_test_lib:check_result(Client1, ok), + ssl_test_lib:close(Server), + ssl_test_lib:close(Client1). + +%%-------------------------------------------------------------------- + +server_verify_client_once_active(doc) -> + ["Test server option verify_client_once"]; + +server_verify_client_once_active(suite) -> + []; + +server_verify_client_once_active(Config) when is_list(Config) -> + ClientOpts = ?config(client_opts, Config), + ServerOpts = ?config(server_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, send_recv_result_active, []}}, + {options, [{active, once}, {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, {?MODULE, send_recv_result_active, []}}, + {options, [{active, true} | ClientOpts]}]), + + ssl_test_lib:check_result(Server, ok, Client0, ok), + ssl_test_lib:close(Client0), + Server ! listen, + Client1 = ssl_test_lib:start_client([{node, ClientNode}, {port, Port}, + {host, Hostname}, + {from, self()}, + {mfa, {?MODULE, result_ok, []}}, + {options, [{active, true} | ClientOpts]}]), + + ssl_test_lib:check_result(Client1, ok), + ssl_test_lib:close(Server), + ssl_test_lib:close(Client1). + + +%%-------------------------------------------------------------------- + +server_verify_client_once_active_once(doc) -> + ["Test server option verify_client_once"]; + +server_verify_client_once_active_once(suite) -> + []; + +server_verify_client_once_active_once(Config) when is_list(Config) -> + ClientOpts = ?config(client_opts, Config), + ServerOpts = ?config(server_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, send_recv_result_active_once, []}}, + {options, [{active, once}, {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, {?MODULE, send_recv_result_active_once, []}}, + {options, [{active, once} | ClientOpts]}]), + + ssl_test_lib:check_result(Server, ok, Client0, ok), + ssl_test_lib:close(Client0), + Server ! listen, + + Client1 = ssl_test_lib:start_client([{node, ClientNode}, {port, Port}, + {host, Hostname}, + {from, self()}, + {mfa, {?MODULE, result_ok, []}}, + {options, [{active, once} | ClientOpts]}]), + + ssl_test_lib:check_result(Client1, ok), + ssl_test_lib:close(Server), + ssl_test_lib:close(Client1). + %%-------------------------------------------------------------------- server_verify_no_cacerts(doc) -> @@ -1878,17 +1881,19 @@ server_require_peer_cert_fail(Config) when is_list(Config) -> | ?config(server_verification_opts, Config)], BadClientOpts = ?config(client_opts, Config), {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config), - Port = ssl_test_lib:inet_port(ServerNode), - - Server = ssl_test_lib:start_server_error([{node, ServerNode}, {port, Port}, + + Server = ssl_test_lib:start_server_error([{node, ServerNode}, {port, 0}, {from, self()}, - {mfa, {?MODULE, send_recv_result, []}}, + {mfa, {?MODULE, no_result, []}}, {options, [{active, false} | ServerOpts]}]), - Client = ssl_test_lib:start_client([{node, ClientNode}, {port, Port}, - {host, Hostname}, - {from, self()}, - {mfa, {?MODULE, send_recv_result, []}}, - {options, [{active, false} | BadClientOpts]}]), + + Port = ssl_test_lib:inet_port(Server), + + Client = ssl_test_lib:start_client_error([{node, ClientNode}, {port, Port}, + {host, Hostname}, + {from, self()}, + {mfa, {?MODULE, no_result, []}}, + {options, [{active, false} | BadClientOpts]}]), ssl_test_lib:check_result(Server, {error, esslaccept}, Client, {error, esslconnect}), @@ -2023,8 +2028,6 @@ client_renegotiate(Config) when is_list(Config) -> {options, ServerOpts}]), Port = ssl_test_lib:inet_port(Server), - test_server:sleep(?SLEEP), - Client = ssl_test_lib:start_client([{node, ClientNode}, {port, Port}, {host, Hostname}, {from, self()}, @@ -2061,8 +2064,6 @@ server_renegotiate(Config) when is_list(Config) -> {options, ServerOpts}]), Port = ssl_test_lib:inet_port(Server), - test_server:sleep(?SLEEP), - Client = ssl_test_lib:start_client([{node, ClientNode}, {port, Port}, {host, Hostname}, {from, self()}, @@ -2101,8 +2102,6 @@ client_no_wrap_sequence_number(Config) when is_list(Config) -> {options, ServerOpts}]), Port = ssl_test_lib:inet_port(Server), - test_server:sleep(?SLEEP), - Client = ssl_test_lib:start_client([{node, ClientNode}, {port, Port}, {host, Hostname}, {from, self()}, @@ -2144,8 +2143,6 @@ server_no_wrap_sequence_number(Config) when is_list(Config) -> {options, [{renegotiate_at, N} | ServerOpts]}]), Port = ssl_test_lib:inet_port(Server), - test_server:sleep(?SLEEP), - Client = ssl_test_lib:start_client([{node, ClientNode}, {port, Port}, {host, Hostname}, {from, self()}, @@ -2158,17 +2155,106 @@ server_no_wrap_sequence_number(Config) when is_list(Config) -> ok. %%-------------------------------------------------------------------- +extended_key_usage(doc) -> + ["Test cert that has a critical extended_key_usage extension"]; + +extended_key_usage(suite) -> + []; + +extended_key_usage(Config) when is_list(Config) -> + ClientOpts = ?config(client_opts, Config), + ServerOpts = ?config(server_opts, Config), + PrivDir = ?config(priv_dir, Config), + + CertFile = proplists:get_value(certfile, ServerOpts), + KeyFile = proplists:get_value(keyfile, ServerOpts), + NewCertFile = filename:join(PrivDir, "cert.pem"), + + {ok, [{cert, DerCert, _}]} = public_key:pem_to_der(CertFile), + + {ok, [KeyInfo]} = public_key:pem_to_der(KeyFile), + + {ok, Key} = public_key:decode_private_key(KeyInfo), + + {ok, OTPCert} = public_key:pkix_decode_cert(DerCert, otp), + + ExtKeyUsageExt = {'Extension', ?'id-ce-extKeyUsage', true, [?'id-kp-serverAuth']}, + + OTPTbsCert = OTPCert#'OTPCertificate'.tbsCertificate, + + Extensions = OTPTbsCert#'OTPTBSCertificate'.extensions, + + NewOTPTbsCert = OTPTbsCert#'OTPTBSCertificate'{extensions = [ExtKeyUsageExt |Extensions]}, + + NewDerCert = public_key:sign(NewOTPTbsCert, Key), + + public_key:der_to_pem(NewCertFile, [{cert, NewDerCert}]), + + NewServerOpts = [{certfile, NewCertFile} | proplists:delete(certfile, ServerOpts)], + + {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_active, []}}, + {options, NewServerOpts}]), + 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_active, []}}, + {options, ClientOpts}]), + + ssl_test_lib:check_result(Server, ok, Client, ok), + + ssl_test_lib:close(Server), + ssl_test_lib:close(Client). + +%%-------------------------------------------------------------------- +validate_extensions_fun(doc) -> + ["Test that it is possible to specify a validate_extensions_fun"]; + +validate_extensions_fun(suite) -> + []; + +validate_extensions_fun(Config) when is_list(Config) -> + ClientOpts = ?config(client_verification_opts, Config), + ServerOpts = ?config(server_verification_opts, Config), + + Fun = fun(Extensions, State, _, AccError) -> + {Extensions, State, AccError} + end, + + {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_active, []}}, + {options, [{validate_extensions_fun, Fun}, + {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, {?MODULE, send_recv_result_active, []}}, + {options,[{validate_extensions_fun, Fun} | ClientOpts]}]), + + ssl_test_lib:check_result(Server, ok, Client, ok), + + ssl_test_lib:close(Server), + ssl_test_lib:close(Client). + +%%-------------------------------------------------------------------- %%% Internal functions %%-------------------------------------------------------------------- send_recv_result(Socket) -> ssl:send(Socket, "Hello world"), - test_server:sleep(?SLEEP), {ok,"Hello world"} = ssl:recv(Socket, 11), ok. send_recv_result_active(Socket) -> ssl:send(Socket, "Hello world"), - test_server:sleep(?SLEEP), receive {ssl, Socket, "Hello world"} -> ok @@ -2176,26 +2262,31 @@ send_recv_result_active(Socket) -> send_recv_result_active_once(Socket) -> ssl:send(Socket, "Hello world"), - test_server:sleep(?SLEEP), receive {ssl, Socket, "Hello world"} -> ok end. +result_ok(_Socket) -> + ok. renegotiate(Socket, Data) -> - [{session_id, Id} | _ ] = ssl:session_info(Socket), - ssl:renegotiate(Socket), + test_server:format("Renegotiating ~n", []), + Result = ssl:renegotiate(Socket), + test_server:format("Result ~p~n", [Result]), ssl:send(Socket, Data), - test_server:sleep(1000), - case ssl:session_info(Socket) of - [{session_id, Id} | _ ] -> - fail_session_not_renegotiated; - _ -> - ok + case Result of + ok -> + ok; + %% It is not an error in erlang ssl + %% if peer rejects renegotiation. + %% Connection will stay up + {error, renegotiation_rejected} -> + ok; + Other -> + Other end. - session_cache_process_list(doc) -> ["Test reuse of sessions (short handshake)"]; @@ -2441,7 +2532,7 @@ erlang_ssl_receive(Socket, Data) -> ok; Other -> test_server:fail({unexpected_message, Other}) - after 4000 -> + after ?SLEEP * 3 -> test_server:fail({did_not_get, Data}) end. diff --git a/lib/ssl/test/ssl_packet_SUITE.erl b/lib/ssl/test/ssl_packet_SUITE.erl index f031552457..1bcb9a657b 100644 --- a/lib/ssl/test/ssl_packet_SUITE.erl +++ b/lib/ssl/test/ssl_packet_SUITE.erl @@ -144,7 +144,9 @@ all(suite) -> packet_wait_passive, packet_wait_active, packet_baddata_passive, packet_baddata_active, packet_size_passive, packet_size_active, - packet_erl_decode + packet_erl_decode, + packet_http_decode, + packet_http_bin_decode_multi ]. %% Test cases starts here. @@ -1466,6 +1468,173 @@ client_packet_decode(Socket, CDR) -> end, ok. +%%-------------------------------------------------------------------- +packet_http_decode(doc) -> + ["Test setting the packet option {packet, http}"]; +packet_http_decode(suite) -> + []; + +packet_http_decode(Config) when is_list(Config) -> + ClientOpts = ?config(client_opts, Config), + ServerOpts = ?config(server_opts, Config), + {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config), + + Request = "GET / HTTP/1.1\r\n" + "host: www.example.com\r\n" + "user-agent: HttpTester\r\n" + "\r\n", + Response = "HTTP/1.1 200 OK\r\n" + "\r\n" + "Hello!", + + Server = ssl_test_lib:start_server([{node, ClientNode}, {port, 0}, + {from, self()}, + {mfa, {?MODULE, server_http_decode, [Response]}}, + {options, [{active, true}, binary, {packet, http} | + ServerOpts]}]), + + Port = ssl_test_lib:inet_port(Server), + Client = ssl_test_lib:start_client([{node, ServerNode}, {port, Port}, + {host, Hostname}, + {from, self()}, + {mfa, {?MODULE, client_http_decode, [Request]}}, + {options, [{active, true}, binary, {packet, http} | + ClientOpts]}]), + + ssl_test_lib:check_result(Server, ok, Client, ok), + + ssl_test_lib:close(Server), + ssl_test_lib:close(Client). + + +server_http_decode(Socket, HttpResponse) -> + assert_packet_opt(Socket, http), + receive + {ssl, Socket, {http_request, 'GET', _, {1,1}}} -> ok; + Other1 -> exit({?LINE, Other1}) + end, + assert_packet_opt(Socket, http), + receive + {ssl, Socket, {http_header, _, 'Host', _, "www.example.com"}} -> ok; + Other2 -> exit({?LINE, Other2}) + end, + assert_packet_opt(Socket, http), + receive + {ssl, Socket, {http_header, _, 'User-Agent', _, "HttpTester"}} -> ok; + Other3 -> exit({?LINE, Other3}) + end, + assert_packet_opt(Socket, http), + receive + {ssl, Socket, http_eoh} -> ok; + Other4 -> exit({?LINE, Other4}) + end, + assert_packet_opt(Socket, http), + ok = ssl:send(Socket, HttpResponse), + ok. + +client_http_decode(Socket, HttpRequest) -> + ok = ssl:send(Socket, HttpRequest), + receive + {ssl, Socket, {http_response, {1,1}, 200, "OK"}} -> ok; + Other1 -> exit({?LINE, Other1}) + end, + receive + {ssl, Socket, http_eoh} -> ok; + Other2 -> exit({?LINE, Other2}) + end, + ok = ssl:setopts(Socket, [{packet, 0}]), + receive + {ssl, Socket, <<"Hello!">>} -> ok; + Other3 -> exit({?LINE, Other3}) + end, + ok. + +%%-------------------------------------------------------------------- +packet_http_bin_decode_multi(doc) -> + ["Test setting the packet option {packet, http_bin} with multiple requests"]; +packet_http_bin_decode_multi(suite) -> + []; + +packet_http_bin_decode_multi(Config) when is_list(Config) -> + ClientOpts = ?config(client_opts, Config), + ServerOpts = ?config(server_opts, Config), + {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config), + + Request = <<"GET / HTTP/1.1\r\n" + "host: www.example.com\r\n" + "user-agent: HttpTester\r\n" + "\r\n">>, + Response = <<"HTTP/1.1 200 OK\r\n" + "\r\n" + "Hello!">>, + NumMsgs = 3, + + Server = ssl_test_lib:start_server([{node, ClientNode}, {port, 0}, + {from, self()}, + {mfa, {?MODULE, server_http_bin_decode, [Response, NumMsgs]}}, + {options, [{active, true}, binary, {packet, http_bin} | + ServerOpts]}]), + + Port = ssl_test_lib:inet_port(Server), + Client = ssl_test_lib:start_client([{node, ServerNode}, {port, Port}, + {host, Hostname}, + {from, self()}, + {mfa, {?MODULE, client_http_bin_decode, [Request, NumMsgs]}}, + {options, [{active, true}, binary, {packet, http_bin} | + ClientOpts]}]), + + ssl_test_lib:check_result(Server, ok, Client, ok), + + ssl_test_lib:close(Server), + ssl_test_lib:close(Client). + + +server_http_bin_decode(Socket, HttpResponse, Count) when Count > 0 -> + assert_packet_opt(Socket, http_bin), + receive + {ssl, Socket, {http_request, 'GET', _, {1,1}}} -> ok; + Other1 -> exit({?LINE, Other1}) + end, + assert_packet_opt(Socket, http_bin), + receive + {ssl, Socket, {http_header, _, 'Host', _, <<"www.example.com">>}} -> ok; + Other2 -> exit({?LINE, Other2}) + end, + assert_packet_opt(Socket, http_bin), + receive + {ssl, Socket, {http_header, _, 'User-Agent', _, <<"HttpTester">>}} -> ok; + Other3 -> exit({?LINE, Other3}) + end, + assert_packet_opt(Socket, http_bin), + receive + {ssl, Socket, http_eoh} -> ok; + Other4 -> exit({?LINE, Other4}) + end, + assert_packet_opt(Socket, http_bin), + ok = ssl:send(Socket, HttpResponse), + server_http_bin_decode(Socket, HttpResponse, Count - 1); +server_http_bin_decode(_, _, _) -> + ok. + +client_http_bin_decode(Socket, HttpRequest, Count) when Count > 0 -> + ok = ssl:send(Socket, HttpRequest), + receive + {ssl, Socket, {http_response, {1,1}, 200, <<"OK">>}} -> ok; + Other1 -> exit({?LINE, Other1}) + end, + receive + {ssl, Socket, http_eoh} -> ok; + Other2 -> exit({?LINE, Other2}) + end, + ok = ssl:setopts(Socket, [{packet, 0}]), + receive + {ssl, Socket, <<"Hello!">>} -> ok; + Other3 -> exit({?LINE, Other3}) + end, + ok = ssl:setopts(Socket, [{packet, http_bin}]), + client_http_bin_decode(Socket, HttpRequest, Count - 1); +client_http_bin_decode(_, _, _) -> + ok. %%-------------------------------------------------------------------- %% Internal functions @@ -1572,3 +1741,6 @@ active_packet(Socket, Data, N) -> Other -> {other, Other, ssl:session_info(Socket),N} end. + +assert_packet_opt(Socket, Type) -> + {ok, [{packet, Type}]} = ssl:getopts(Socket, [packet]). diff --git a/lib/ssl/test/ssl_test_lib.erl b/lib/ssl/test/ssl_test_lib.erl index ee799c50c8..00c5350ad0 100644 --- a/lib/ssl/test/ssl_test_lib.erl +++ b/lib/ssl/test/ssl_test_lib.erl @@ -26,6 +26,7 @@ %% Note: This directive should only be used in test suites. -compile(export_all). +-record(sslsocket, { fd = nil, pid = nil}). timetrap(Time) -> Mul = try @@ -52,7 +53,11 @@ node_to_hostip(Node) -> Address. start_server(Args) -> - spawn_link(?MODULE, run_server, [Args]). + Result = spawn_link(?MODULE, run_server, [Args]), + receive + {listen, up} -> + Result + end. run_server(Opts) -> Node = proplists:get_value(node, Opts), @@ -61,13 +66,8 @@ run_server(Opts) -> Pid = proplists:get_value(from, Opts), test_server:format("ssl:listen(~p, ~p)~n", [Port, Options]), {ok, ListenSocket} = rpc:call(Node, ssl, listen, [Port, Options]), - case Port of - 0 -> - {ok, {_, NewPort}} = ssl:sockname(ListenSocket), - Pid ! {self(), {port, NewPort}}; - _ -> - ok - end, + Pid ! {listen, up}, + send_selected_port(Pid, Port, ListenSocket), run_server(ListenSocket, Opts). run_server(ListenSocket, Opts) -> @@ -124,7 +124,11 @@ remove_close_msg(ReconnectTimes) -> start_client(Args) -> - spawn_link(?MODULE, run_client, [Args]). + Result = spawn_link(?MODULE, run_client, [Args]), + receive + connected -> + Result + end. run_client(Opts) -> Node = proplists:get_value(node, Opts), @@ -135,14 +139,11 @@ run_client(Opts) -> test_server:format("ssl:connect(~p, ~p, ~p)~n", [Host, Port, Options]), case rpc:call(Node, ssl, connect, [Host, Port, Options]) of {ok, Socket} -> + Pid ! connected, test_server:format("Client: connected~n", []), - case proplists:get_value(port, Options) of - 0 -> - {ok, {_, NewPort}} = ssl:sockname(Socket), - Pid ! {self(), {port, NewPort}}; - _ -> - ok - end, + %% In specail cases we want to know the client port, it will + %% be indicated by sending {port, 0} in options list! + send_selected_port(Pid, proplists:get_value(port, Options), Socket), {Module, Function, Args} = proplists:get_value(mfa, Opts), test_server:format("Client: apply(~p,~p,~p)~n", [Module, Function, [Socket | Args]]), @@ -207,6 +208,26 @@ check_result(Pid, Msg) -> test_server:fail(Reason) end. +check_result_ignore_renegotiation_reject(Pid, Msg) -> + receive + {Pid, fail_session_fatal_alert_during_renegotiation} -> + test_server:comment("Server rejected old renegotiation"), + ok; + {ssl_error, _, esslconnect} -> + test_server:comment("Server rejected old renegotiation"), + ok; + {Pid, Msg} -> + ok; + {Port, {data,Debug}} when is_port(Port) -> + io:format("openssl ~s~n",[Debug]), + check_result(Pid,Msg); + Unexpected -> + Reason = {{expected, {Pid, Msg}}, + {got, Unexpected}}, + test_server:fail(Reason) + end. + + wait_for_result(Server, ServerMsg, Client, ClientMsg) -> receive {Server, ServerMsg} -> @@ -266,7 +287,7 @@ cert_options(Config) -> "badcert.pem"]), BadKeyFile = filename:join([?config(priv_dir, Config), "badkey.pem"]), - [{client_opts, [{ssl_imp, new}]}, + [{client_opts, [{ssl_imp, new},{reuseaddr, true}]}, {client_verification_opts, [{cacertfile, ClientCaCertFile}, {certfile, ClientCertFile}, {keyfile, ClientKeyFile}, @@ -298,7 +319,11 @@ cert_options(Config) -> start_upgrade_server(Args) -> - spawn_link(?MODULE, run_upgrade_server, [Args]). + Result = spawn_link(?MODULE, run_upgrade_server, [Args]), + receive + {listen, up} -> + Result + end. run_upgrade_server(Opts) -> Node = proplists:get_value(node, Opts), @@ -310,15 +335,8 @@ run_upgrade_server(Opts) -> test_server:format("gen_tcp:listen(~p, ~p)~n", [Port, TcpOptions]), {ok, ListenSocket} = rpc:call(Node, gen_tcp, listen, [Port, TcpOptions]), - - case Port of - 0 -> - {ok, {_, NewPort}} = inet:sockname(ListenSocket), - Pid ! {self(), {port, NewPort}}; - _ -> - ok - end, - + Pid ! {listen, up}, + send_selected_port(Pid, Port, ListenSocket), test_server:format("gen_tcp:accept(~p)~n", [ListenSocket]), {ok, AcceptSocket} = rpc:call(Node, gen_tcp, accept, [ListenSocket]), @@ -360,14 +378,8 @@ run_upgrade_client(Opts) -> test_server:format("gen_tcp:connect(~p, ~p, ~p)~n", [Host, Port, TcpOptions]), {ok, Socket} = rpc:call(Node, gen_tcp, connect, [Host, Port, TcpOptions]), - - case proplists:get_value(port, Opts) of - 0 -> - {ok, {_, NewPort}} = inet:sockname(Socket), - Pid ! {self(), {port, NewPort}}; - _ -> - ok - end, + + send_selected_port(Pid, Port, Socket), test_server:format("ssl:connect(~p, ~p)~n", [Socket, SslOptions]), {ok, SslSocket} = rpc:call(Node, ssl, connect, [Socket, SslOptions]), @@ -383,7 +395,11 @@ run_upgrade_client(Opts) -> end. start_server_error(Args) -> - spawn_link(?MODULE, run_server_error, [Args]). + Result = spawn_link(?MODULE, run_server_error, [Args]), + receive + {listen, up} -> + Result + end. run_server_error(Opts) -> Node = proplists:get_value(node, Opts), @@ -393,8 +409,10 @@ run_server_error(Opts) -> test_server:format("ssl:listen(~p, ~p)~n", [Port, Options]), case rpc:call(Node, ssl, listen, [Port, Options]) of {ok, ListenSocket} -> - test_server:sleep(2000), %% To make sure error_client will + %% To make sure error_client will %% get {error, closed} and not {error, connection_refused} + Pid ! {listen, up}, + send_selected_port(Pid, Port, ListenSocket), test_server:format("ssl:transport_accept(~p)~n", [ListenSocket]), case rpc:call(Node, ssl, transport_accept, [ListenSocket]) of {error, _} = Error -> @@ -405,6 +423,9 @@ run_server_error(Opts) -> Pid ! {self(), Error} end; Error -> + %% Not really true but as this is an error test + %% this is what we want. + Pid ! {listen, up}, Pid ! {self(), Error} end. @@ -429,8 +450,8 @@ inet_port(Pid) when is_pid(Pid)-> inet_port(Node) -> {Port, Socket} = do_inet_port(Node), - rpc:call(Node, gen_tcp, close, [Socket]), - Port. + rpc:call(Node, gen_tcp, close, [Socket]), + Port. do_inet_port(Node) -> {ok, Socket} = rpc:call(Node, gen_tcp, listen, [0, [{reuseaddr, true}]]), @@ -448,13 +469,10 @@ trigger_renegotiate(Socket, _, 0, Id) -> test_server:sleep(1000), case ssl:session_info(Socket) of [{session_id, Id} | _ ] -> - %% If a warning alert is received - %% from openssl this may not be - %% an error! fail_session_not_renegotiated; - %% Tests that uses this function will no reuse + %% Tests that uses this function will not reuse %% sessions so if we get a new session id the - %% renegotiation has seceded. + %% renegotiation has succeeded. [{session_id, _} | _ ] -> ok; {error, closed} -> @@ -467,3 +485,12 @@ trigger_renegotiate(Socket, ErlData, N, Id) -> ssl:send(Socket, ErlData), trigger_renegotiate(Socket, ErlData, N-1, Id). + +send_selected_port(Pid, 0, #sslsocket{} = Socket) -> + {ok, {_, NewPort}} = ssl:sockname(Socket), + Pid ! {self(), {port, NewPort}}; +send_selected_port(Pid, 0, Socket) -> + {ok, {_, NewPort}} = inet:sockname(Socket), + Pid ! {self(), {port, NewPort}}; +send_selected_port(_,_,_) -> + ok. diff --git a/lib/ssl/test/ssl_to_openssl_SUITE.erl b/lib/ssl/test/ssl_to_openssl_SUITE.erl index 624404b556..cbf0447bf0 100644 --- a/lib/ssl/test/ssl_to_openssl_SUITE.erl +++ b/lib/ssl/test/ssl_to_openssl_SUITE.erl @@ -32,6 +32,7 @@ -define(SLEEP, 1000). -define(OPENSSL_RENEGOTIATE, "r\n"). -define(OPENSSL_QUIT, "Q\n"). +-define(OPENSSL_GARBAGE, "P\n"). %% Test server callback functions %%-------------------------------------------------------------------- @@ -131,7 +132,8 @@ all(suite) -> tls1_erlang_client_openssl_server_client_cert, tls1_erlang_server_openssl_client_client_cert, tls1_erlang_server_erlang_client_client_cert, - ciphers + ciphers, + erlang_client_bad_openssl_server ]. %% Test cases starts here. @@ -161,7 +163,7 @@ erlang_client_openssl_server(Config) when is_list(Config) -> OpensslPort = open_port({spawn, Cmd}, [stderr_to_stdout]), - test_server:sleep(?SLEEP), + wait_for_openssl_server(), Client = ssl_test_lib:start_client([{node, ClientNode}, {port, Port}, {host, Hostname}, @@ -200,8 +202,6 @@ erlang_server_openssl_client(Config) when is_list(Config) -> {options, ServerOpts}]), Port = ssl_test_lib:inet_port(Server), - test_server:sleep(?SLEEP), - Cmd = "openssl s_client -port " ++ integer_to_list(Port) ++ " -host localhost", @@ -240,8 +240,6 @@ erlang_server_openssl_client_reuse_session(Config) when is_list(Config) -> {options, ServerOpts}]), Port = ssl_test_lib:inet_port(Server), - test_server:sleep(?SLEEP), - Cmd = "openssl s_client -port " ++ integer_to_list(Port) ++ " -host localhost -reconnect", @@ -286,7 +284,7 @@ erlang_client_openssl_server_renegotiate(Config) when is_list(Config) -> OpensslPort = open_port({spawn, Cmd}, [stderr_to_stdout]), - test_server:sleep(?SLEEP), + wait_for_openssl_server(), Client = ssl_test_lib:start_client([{node, ClientNode}, {port, Port}, {host, Hostname}, @@ -294,15 +292,16 @@ erlang_client_openssl_server_renegotiate(Config) when is_list(Config) -> {mfa, {?MODULE, delayed_send, [[ErlData, OpenSslData]]}}, {options, ClientOpts}]), - test_server:sleep(?SLEEP), port_command(OpensslPort, ?OPENSSL_RENEGOTIATE), - test_server:sleep(?SLEEP), - port_command(OpensslPort, OpenSslData), - ssl_test_lib:check_result(Client, ok), + %%ssl_test_lib:check_result(Client, ok), + %% Currently allow test case to not fail + %% if server requires secure renegotiation from RFC-5746 + %% This should be removed as soon as we have implemented it. + ssl_test_lib:check_result_ignore_renegotiation_reject(Client, ok), %% Clean close down! Server needs to be closed first !! close_port(OpensslPort), @@ -341,7 +340,7 @@ erlang_client_openssl_server_no_wrap_sequence_number(Config) when is_list(Config OpensslPort = open_port({spawn, Cmd}, [stderr_to_stdout]), - test_server:sleep(?SLEEP), + wait_for_openssl_server(), Client = ssl_test_lib:start_client([{node, ClientNode}, {port, Port}, {host, Hostname}, @@ -351,7 +350,11 @@ erlang_client_openssl_server_no_wrap_sequence_number(Config) when is_list(Config {options, [{reuse_sessions, false}, {renegotiate_at, N} | ClientOpts]}]), - ssl_test_lib:check_result(Client, ok), + %%ssl_test_lib:check_result(Client, ok), + %% Currently allow test case to not fail + %% if server requires secure renegotiation from RFC-5746 + %% This should be removed as soon as we have implemented it. + ssl_test_lib:check_result_ignore_renegotiation_reject(Client, ok), %% Clean close down! Server needs to be closed first !! close_port(OpensslPort), @@ -385,8 +388,6 @@ erlang_server_openssl_client_no_wrap_sequence_number(Config) when is_list(Config {options, [{renegotiate_at, N} | ServerOpts]}]), Port = ssl_test_lib:inet_port(Server), - test_server:sleep(?SLEEP), - Cmd = "openssl s_client -port " ++ integer_to_list(Port) ++ " -host localhost -msg", @@ -431,7 +432,7 @@ erlang_client_openssl_server_no_server_ca_cert(Config) when is_list(Config) -> OpensslPort = open_port({spawn, Cmd}, [stderr_to_stdout]), - test_server:sleep(?SLEEP), + wait_for_openssl_server(), Client = ssl_test_lib:start_client([{node, ClientNode}, {port, Port}, {host, Hostname}, @@ -473,7 +474,7 @@ ssl3_erlang_client_openssl_server(Config) when is_list(Config) -> OpensslPort = open_port({spawn, Cmd}, [stderr_to_stdout]), - test_server:sleep(?SLEEP), + wait_for_openssl_server(), Client = ssl_test_lib:start_client([{node, ClientNode}, {port, Port}, {host, Hostname}, @@ -508,8 +509,6 @@ ssl3_erlang_server_openssl_client(Config) when is_list(Config) -> {options, [{versions, [sslv3]} | ServerOpts]}]), Port = ssl_test_lib:inet_port(Server), - - test_server:sleep(?SLEEP), Cmd = "openssl s_client -port " ++ integer_to_list(Port) ++ " -host localhost -ssl3", @@ -552,7 +551,7 @@ ssl3_erlang_client_openssl_server_client_cert(Config) when is_list(Config) -> OpensslPort = open_port({spawn, Cmd}, [stderr_to_stdout]), - test_server:sleep(?SLEEP), + wait_for_openssl_server(), Client = ssl_test_lib:start_client([{node, ClientNode}, {port, Port}, {host, Hostname}, @@ -594,8 +593,6 @@ ssl3_erlang_server_openssl_client_client_cert(Config) when is_list(Config) -> | ServerOpts]}]), Port = ssl_test_lib:inet_port(Server), - test_server:sleep(?SLEEP), - CaCertFile = proplists:get_value(cacertfile, ClientOpts), CertFile = proplists:get_value(certfile, ClientOpts), KeyFile = proplists:get_value(keyfile, ClientOpts), @@ -642,8 +639,6 @@ ssl3_erlang_server_erlang_client_client_cert(Config) when is_list(Config) -> | ServerOpts]}]), Port = ssl_test_lib:inet_port(Server), - test_server:sleep(?SLEEP), - Client = ssl_test_lib:start_client([{node, ClientNode}, {port, Port}, {host, Hostname}, {from, self()}, @@ -685,7 +680,7 @@ tls1_erlang_client_openssl_server(Config) when is_list(Config) -> OpensslPort = open_port({spawn, Cmd}, [stderr_to_stdout]), - test_server:sleep(?SLEEP), + wait_for_openssl_server(), Client = ssl_test_lib:start_client([{node, ClientNode}, {port, Port}, {host, Hostname}, @@ -723,8 +718,6 @@ tls1_erlang_server_openssl_client(Config) when is_list(Config) -> [{versions, [tlsv1]} | ServerOpts]}]), Port = ssl_test_lib:inet_port(Server), - test_server:sleep(?SLEEP), - Cmd = "openssl s_client -port " ++ integer_to_list(Port) ++ " -host localhost -tls1", @@ -768,7 +761,7 @@ tls1_erlang_client_openssl_server_client_cert(Config) when is_list(Config) -> OpensslPort = open_port({spawn, Cmd}, [stderr_to_stdout]), - test_server:sleep(?SLEEP), + wait_for_openssl_server(), Client = ssl_test_lib:start_client([{node, ClientNode}, {port, Port}, {host, Hostname}, @@ -810,8 +803,6 @@ tls1_erlang_server_openssl_client_client_cert(Config) when is_list(Config) -> | ServerOpts]}]), Port = ssl_test_lib:inet_port(Server), - test_server:sleep(?SLEEP), - CaCertFile = proplists:get_value(cacertfile, ClientOpts), CertFile = proplists:get_value(certfile, ClientOpts), KeyFile = proplists:get_value(keyfile, ClientOpts), @@ -856,8 +847,6 @@ tls1_erlang_server_erlang_client_client_cert(Config) when is_list(Config) -> | ServerOpts]}]), Port = ssl_test_lib:inet_port(Server), - test_server:sleep(?SLEEP), - Client = ssl_test_lib:start_client([{node, ClientNode}, {port, Port}, {host, Hostname}, {from, self()}, @@ -913,7 +902,7 @@ cipher(CipherSuite, Version, Config) -> OpenSslPort = open_port({spawn, Cmd}, [stderr_to_stdout]), - test_server:sleep(?SLEEP), + wait_for_openssl_server(), Client = ssl_test_lib:start_client([{node, ClientNode}, {port, Port}, {host, Hostname}, @@ -945,6 +934,52 @@ cipher(CipherSuite, Version, Config) -> Return. %%-------------------------------------------------------------------- +erlang_client_bad_openssl_server(doc) -> + [""]; +erlang_client_bad_openssl_server(suite) -> + []; +erlang_client_bad_openssl_server(Config) when is_list(Config) -> + process_flag(trap_exit, true), + ServerOpts = ?config(server_verification_opts, Config), + ClientOpts = ?config(client_verification_opts, Config), + + {ClientNode, _, Hostname} = ssl_test_lib:run_where(Config), + + Port = ssl_test_lib:inet_port(node()), + CertFile = proplists:get_value(certfile, ServerOpts), + KeyFile = proplists:get_value(keyfile, ServerOpts), + + Cmd = "openssl s_server -accept " ++ integer_to_list(Port) ++ + " -cert " ++ CertFile ++ " -key " ++ KeyFile ++ "", + + test_server:format("openssl cmd: ~p~n", [Cmd]), + + OpensslPort = open_port({spawn, Cmd}, [stderr_to_stdout]), + + wait_for_openssl_server(), + + Client = ssl_test_lib:start_client([{node, ClientNode}, {port, Port}, + {host, Hostname}, + {from, self()}, + {mfa, {?MODULE, server_sent_garbage, []}}, + {options, + [{versions, [tlsv1]} | ClientOpts]}]), + + %% Send garbage + port_command(OpensslPort, ?OPENSSL_GARBAGE), + + test_server:sleep(?SLEEP), + + Client ! server_sent_garbage, + + ssl_test_lib:check_result(Client, true), + + ssl_test_lib:close(Client), + %% Clean close down! + close_port(OpensslPort), + process_flag(trap_exit, false), + ok. +%%-------------------------------------------------------------------- erlang_ssl_receive(Socket, Data) -> test_server:format("Connection info: ~p~n", @@ -1014,3 +1049,22 @@ close_loop(Port, Time, SentClose) -> io:format("Timeout~n",[]) end end. + + +server_sent_garbage(Socket) -> + receive + server_sent_garbage -> + {error, closed} == ssl:send(Socket, "data") + end. + +wait_for_openssl_server() -> + receive + {Port, {data, Debug}} when is_port(Port) -> + io:format("openssl ~s~n",[Debug]), + %% openssl has started make sure + %% it will be in accept. Parsing + %% output is too error prone. (Even + %% more so than sleep!) + test_server:sleep(?SLEEP) + end. + diff --git a/lib/ssl/vsn.mk b/lib/ssl/vsn.mk index 0f3f8e95ec..337ea4b380 100644 --- a/lib/ssl/vsn.mk +++ b/lib/ssl/vsn.mk @@ -19,7 +19,12 @@ SSL_VSN = 3.11 -TICKETS = OTP-8517 OTP-7046 OTP-8557 +TICKETS = OTP-8517 \ + OTP-7046 \ + OTP-8557 \ + OTP-8560 \ + OTP-8545 \ + OTP-8554 #TICKETS_3.10.9 = OTP-8510 |