[erlang-questions] Errors in SSL handshake (weird client)

Ingela Andin ingela.andin@REDACTED
Thu Apr 17 17:04:30 CEST 2014


Hi!

I pushed some more patches to my gitrep on the branches:

ia/ssl/recv/OTP-11878  (error handling of  recv on an active socket)

ia/ssl/decrypt-alert   (correct decryption alert handling)

Regards Ingela Erlang/OTP team - Ericsson AB




2014-04-17 11:19 GMT+02:00 Ingela Andin <ingela.andin@REDACTED>:

> Hi Danil!
>
>
> 2014-04-17 8:52 GMT+02:00 Danil Zagoskin <z@REDACTED>:
>
> Hi, Ingela!
>>
>> The patch works like a charm, thank you! It completely fixes the initial
>> problem with buggy client.
>>
>>
> One bug down!
>
>
>
>>
>> But there are some sad news, too:
>>
>>
> Well reproducible is always good, means we can haunt it down and fix it :)
>
>
>
>> While playing with my dummy server (
>> https://github.com/stolen/ssldump/blob/master/src/ssldump.erl#L70) I
>> accidentally reproduced badarg in erlang:size once more:
>>
>> =ERROR REPORT==== 17-Apr-2014::07:34:58 ===
>> ** State machine <0.58.0> terminating
>> ** Last message in was {tcp,#Port<0.1498>,
>>
>> [22,3,1,0,173,1,0,0,169,3,3,83,79,75,226,78,81,8,
>>
>>  77,79,97,154,127,165,92,183,20,239,70,183,158,
>>
>>  193,222,139,77,116,13,128,12,66,120,63,110,0,0,
>>
>>  94,0,255,192,36,192,35,192,10,192,9,192,7,192,8,
>>
>>  192,40,192,39,192,20,192,19,192,17,192,18,192,38,
>>
>>  192,37,192,42,192,41,192,5,192,4,192,2,192,3,192,
>>
>>  15,192,14,192,12,192,13,0,61,0,60,0,47,0,5,0,4,0,
>>
>>  53,0,10,0,103,0,107,0,51,0,57,0,22,0,175,0,174,0,
>>
>>  141,0,140,0,138,0,139,0,177,0,176,0,44,0,59,1,0,
>>
>>  0,34,0,10,0,8,0,6,0,23,0,24,0,25,0,11,0,2,1,0,0,
>>                              13,0,12,0,10,5,1,4,1,2,1,4,3,2,3]}
>> (meaningless state omitted)
>> ** Reason for termination =
>> ** {badarg,[{erlang,size,
>>
>> [[22,3,1,0,173,1,0,0,169,3,3,83,79,75,226,78,81,8,77,79,
>>
>> 97,154,127,165,92,183,20,239,70,183,158,193,222,139,77,
>>
>> 116,13,128,12,66,120,63,110,0,0,94,0,255,192,36,192,35,
>>
>> 192,10,192,9,192,7,192,8,192,40,192,39,192,20,192,19,
>>
>> 192,17,192,18,192,38,192,37,192,42,192,41,192,5,192,4,
>>
>> 192,2,192,3,192,15,192,14,192,12,192,13,0,61,0,60,0,47,
>>
>> 0,5,0,4,0,53,0,10,0,103,0,107,0,51,0,57,0,22,0,175,0,
>>
>> 174,0,141,0,140,0,138,0,139,0,177,0,176,0,44,0,59,1,0,0,
>>
>> 34,0,10,0,8,0,6,0,23,0,24,0,25,0,11,0,2,1,0,0,13,0,12,0,
>>                       10,5,1,4,1,2,1,4,3,2,3]],
>>                     []},
>>             {tls_record,get_tls_records_aux,2,
>>                         [{file,"tls_record.erl"},{line,122}]},
>>             {tls_connection,next_tls_record,2,
>>                             [{file,"tls_connection.erl"},{line,484}]},
>>             {tls_connection,handle_info,3,
>>                             [{file,"tls_connection.erl"},{line,307}]},
>>              {gen_fsm,handle_msg,7,[{file,"gen_fsm.erl"},{line,503}]},
>>
>> {proc_lib,init_p_do_apply,3,[{file,"proc_lib.erl"},{line,239}]}]}
>>
>> The client used was simple curl under mac os, and this crash did not
>> reproduce.
>> As you see, the packet received as list instead of binary is client_hello
>> which is the first message ssl process gets.
>> Maybe there is some race condition when changing mode on just
>> transport_accepted socket.
>>
>>
>>
>> Also, the same server when working with the same curl may stall at this
>> line:
>>   {ok, _} = ssl:recv(Socket, 0, 10000),
>> process_info showed that the worker has {ssl,#sslsocket{}, Data} in its
>> mailbox.
>>
>> I've tuned the server to reproduce this stall easier:
>> https://github.com/stolen/ssldump/blob/b3eebc2fa85ca0bd8ed8470c400b972aa737bffc/src/ssldump.erl(master may change)
>>
>> The client is manually invoked curl which is manually interrupted after
>> sufficiently large time of inactivity. The console log looks like this:
>>
>> stolen@REDACTED:~/ssldump$ ./dummyserver.sh 9999 ../cert/pipe.trololo
>> Erlang/OTP 17 [erts-6.0] [source-07b8f44] [smp:2:2] [async-threads:10]
>> [hipe] [kernel-poll:false]
>>
>> Eshell V6.0  (abort with ^G)
>> 1> Accepting SSL in <0.46.0>
>> Socket state: connection, options: {socket_options,list,0,0,0,true}
>>
>> =ERROR REPORT==== 17-Apr-2014::09:08:01 ===
>> Error in process <0.46.0> with exit value:
>> {{badmatch,{error,timeout}},[{ssldump,ssl_acceptor,1,[{file,"src/ssldump.erl"},{line,98}]}]}
>>
>> Accepting SSL in <0.47.0>
>> Socket state: connection, options: {socket_options,binary,0,0,0,false}
>> Accepting SSL in <0.48.0>
>> Socket state: connection, options: {socket_options,binary,0,0,0,false}
>> Accepting SSL in <0.51.0>
>> Socket state: connection, options: {socket_options,list,0,0,0,true}
>>
>> =ERROR REPORT==== 17-Apr-2014::09:09:17 ===
>> Error in process <0.51.0> with exit value:
>> {{badmatch,{error,closed}},[{ssldump,ssl_acceptor,1,[{file,"src/ssldump.erl"},{line,98}]}]}
>>
>> Accepting SSL in <0.53.0>
>> Socket state: connection, options: {socket_options,binary,0,0,0,false}
>> Accepting SSL in <0.55.0>
>> Socket state: connection, options: {socket_options,binary,0,0,0,false}
>> Accepting SSL in <0.57.0>
>> Socket state: connection, options: {socket_options,list,0,0,0,true}
>>
>> =ERROR REPORT==== 17-Apr-2014::09:11:26 ===
>> Error in process <0.57.0> with exit value:
>> {{badmatch,{error,closed}},[{ssldump,ssl_acceptor,1,[{file,"src/ssldump.erl"},{line,98}]}]}
>>
>>
>> My interpretation:
>>   - Given pool size X, the first of each X requests fails.
>>   - First of failing requests catches timeout as expected, other ones
>> just stall.
>>   - Failing requests have emulated options [{mode, list}, {active, true}]
>> while working ones have the opposite [{mode, binary}, {active, false}].
>>
>> After couple of further experiments I discovered that only every (X*N +
>> 1)th acceptor inherits listening socket options, other ones always have
>> [{mode, binary}, {active, false}].
>>
>>
>>
> Humm...   I am not sure if this is what happens in your case but the
> following  should fix a potential inheritance problem
>
> diff --git a/lib/ssl/src/ssl.erl b/lib/ssl/src/ssl.erl
> index 743753b..7768bbb 100644
> --- a/lib/ssl/src/ssl.erl
> +++ b/lib/ssl/src/ssl.erl
> @@ -187,6 +187,7 @@ transport_accept(#sslsocket{pid = {ListenSocket,
>              {error, Reason}
>          end;
>      {error, Reason} ->
> +        ok = ssl_socket:setopts(Transport, ListenSocket, SocketValues),
>          {error, Reason}
>      end.
>
>
>
>
>> And, apart from eventual option inheritance, shouldn't ssl:recv on active
>> socket return {error, einval} like gen_tcp:recv does?
>>
>>
>>
> I think it should.
>
>
> Regards Ingela Erlang/OTP Team Ericssson AB
>
>
>
>
>
>>  2014-04-16 19:36 GMT+04:00 Ingela Andin <ingela.andin@REDACTED>:
>>
>> Hi!
>>>
>>> 2014-04-16 15:56 GMT+02:00 Danil Zagoskin <z@REDACTED>:
>>>
>>> Thank you for the patch. I'll check it.
>>>>
>>>
>>>
>>> New patch, better tested ;),  on branch
>>> ia/ssl/user-suites-match-negotiated-version at
>>> https://github.com/IngelaAndin/otp
>>>
>>>
>>>>
>>>> (Style question: is lists:filtermap better than list comprehension?)
>>>>
>>>
>>>
>>> I think just a matter of taste!
>>>
>>>
>>>> > #ssl_options{] is an internal representation of the proplists input
>>>> list and should not be a visible in the API, records are generally not a
>>>> good API choice as they enforce compile time dependencies.
>>>>
>>>> Yes, I understand that. I wanted to start broken client to see how
>>>> server would work with it.
>>>> The option to do that could be using sys:replace_state, but as far as I
>>>> understand the code, client_hello is sent immediately after starting a
>>>> process, so it seems there is no way to send crafted hello and continue
>>>> handshake with couple of lines instead of ssl:connect.
>>>>
>>>> Given that possibility it could be much easier to reproduce server
>>>> crashes when working with buggy clients.
>>>> For example, How do I write a test for badmatch in tls_record:decode_cipher_text?
>>>> The function takes #connection_states{}, and creating that record
>>>> relies on #ssl_options{} which cannot be created without opening a
>>>> socket and calling gen_fsm:enter_loop.
>>>>
>>>>
>>> Yes  you have a good point.  I do not have a good short answer. Testing
>>> error cases especially when the protocol sometimes
>>> tries to hide the real error reason due to security issues and
>>> shortcutting the API and creating raw protocol command is not trivial.
>>>
>>> Regards Ingela Erlang/OTP team - Ericsson AB
>>>
>>>
>>>
>>>
>>>
>>>>
>>>> 2014-04-16 17:07 GMT+04:00 Ingela Andin <ingela.andin@REDACTED>:
>>>>
>>>> Hi!
>>>>>
>>>>> 2014-04-16 0:07 GMT+02:00 Danil Zagoskin <z@REDACTED>:
>>>>>
>>>>> Hi, Ingela.
>>>>>>
>>>>>> I finally had some time for investigating this problem.
>>>>>>
>>>>>> I didn't see badarg in erlang:size again, but it definitely appeared
>>>>>> in 17.0 with simple accept method described as minimal example at
>>>>>> http://www.erlang.org/doc/apps/ssl/using_ssl.html (
>>>>>>
>>>>>
>>>>>
>>>>>
>>>>>> the difference was transport_accept and ssl_accept were run in
>>>>>> separate process)
>>>>>>
>>>>>
>>>>> That  should be ok, you are suppose to be able to do that.
>>>>>
>>>>>
>>>>>
>>>>>> Today I met some new crash:
>>>>>> ** {{badmatch,{alert,2,20,{"ssl_cipher.erl",215}}},
>>>>>>
>>>>>> [{tls_record,decode_cipher_text,2,[{file,"tls_record.erl"},{line,157}]},
>>>>>>
>>>>>>  {tls_connection,next_record,1,[{file,"tls_connection.erl"},{line,503}]},
>>>>>>
>>>>>>  {tls_connection,next_state,4,[{file,"tls_connection.erl"},{line,475}]},
>>>>>>      {gen_fsm,handle_msg,7,[{file,"gen_fsm.erl"},{line,503}]},
>>>>>>      {proc_lib,init_p_do_apply,3,[{file,"proc_lib.erl"},{line,239}]}]}
>>>>>>
>>>>>> That's because ssl_record:decipher/3 may return #alert{}, but
>>>>>> tls_record:decode_cipher_text/2 isn't expecting that.
>>>>>>
>>>>>>
>>>>> Hum, looks like a bug. We will look into that.
>>>>>
>>>>>
>>>>>>
>>>>>> As for {case_clause,{4}} in ssl_v3:mac_hash/3 I found that
>>>>>> precondition for crash is easily reproducible:
>>>>>> https://gist.github.com/stolen/10780653
>>>>>> Ordinary servers negotiate with sha hash or close socket while ssl in
>>>>>> 17.0 negotiates with sha256 hash.
>>>>>> I don't know the protocol good enough to perform further states, but
>>>>>> I hope this helps.
>>>>>>
>>>>>>
>>>>> Okej, the following patch should take care of that problem.
>>>>>
>>>>> diff --git a/lib/ssl/src/ssl.erl b/lib/ssl/src/ssl.erl
>>>>> index 743753b..093d4af 100644
>>>>> --- a/lib/ssl/src/ssl.erl
>>>>> +++ b/lib/ssl/src/ssl.erl
>>>>> @@ -953,8 +953,8 @@ handle_cipher_option(Value, Version)  when
>>>>> is_list(Value) ->
>>>>>      error:_->
>>>>>          throw({error, {options, {ciphers, Value}}})
>>>>>      end.
>>>>> -binary_cipher_suites(Version, []) -> %% Defaults to all supported
>>>>> suits
>>>>> -    ssl_cipher:suites(Version);
>>>>> +binary_cipher_suites(Version, []) ->
>>>>> +    [];
>>>>>  binary_cipher_suites(Version, [{_,_,_,_}| _] = Ciphers0) -> %%
>>>>> Backwards compatibility
>>>>>      Ciphers = [{KeyExchange, Cipher, Hash} || {KeyExchange, Cipher,
>>>>> Hash, _} <- Ciphers0],
>>>>>      binary_cipher_suites(Version, Ciphers);
>>>>> diff --git a/lib/ssl/src/ssl_handshake.erl
>>>>> b/lib/ssl/src/ssl_handshake.erl
>>>>> index 1108edc..93d4efd 100644
>>>>> --- a/lib/ssl/src/ssl_handshake.erl
>>>>> +++ b/lib/ssl/src/ssl_handshake.erl
>>>>> @@ -1017,11 +1017,14 @@ decode_suites('3_bytes', Dec) ->
>>>>>  %%-------------Cipeher suite handling --------------------------------
>>>>>
>>>>>  available_suites(UserSuites, Version) ->
>>>>> +    VersionSupportedSuites = ssl_cipher:suites(Version),
>>>>>      case UserSuites of
>>>>>      [] ->
>>>>> -        ssl_cipher:suites(Version);
>>>>> +        VersionSupportedSuites;
>>>>>      _ ->
>>>>> -        UserSuites
>>>>> +        lists:filtermap(fun(Suite) ->
>>>>> +                    lists:member(Suite, VersionSupportedSuites)
>>>>> +                end, UserSuites)
>>>>>      end.
>>>>>
>>>>>  available_suites(ServerCert, UserSuites, Version, Curve) ->
>>>>>
>>>>>
>>>>>
>>>>>
>>>>>> Unfortunately, I don't understand how this code should be tested —
>>>>>> most of functions require #state{} or #connection_states{} which require
>>>>>> #ssl_options{} and other stuff, and at I have found no exported function
>>>>>> creating #ssl_options{} without side-effects.
>>>>>>
>>>>>
>>>>> #ssl_options{] is an internal representation of the proplists input
>>>>> list and should not be a visible in the API, records are generally not a
>>>>> good API choice as they enforce compile time dependencies.
>>>>>
>>>>>
>>>>> Regards Ingela - Erlang/OTP team - Ericsson AB
>>>>>
>>>>>
>>>>>
>>>>>>
>>>>>> 2014-04-15 16:52 GMT+04:00 Ingela Andin <ingela.andin@REDACTED>:
>>>>>>
>>>>>> Hello again!
>>>>>>>
>>>>>>>
>>>>>>>>  sometimes badarg in
>>>>>>>> erlang:size([22,3,1,0,158,1,0,0,154,3,1,83,74|...]) at tls_record.erl:122.
>>>>>>>>
>>>>>>>
>>>>>>> Same symptom was recently reported on erlang-bugs and it turned out
>>>>>>> to be due to upgrading a gen_tcp socket in active mode. When upgrading a
>>>>>>> gen_tcp socket to an ssl socket it must be put in passive mode ({active,
>>>>>>> false}) before the client
>>>>>>> is allowed to start the handshake.  Normaly if the upgrade is
>>>>>>> negotiated this is not a problem for the server to set the option
>>>>>>> before signaling to the client to go ahead with  the handshake. If
>>>>>>> the upgrade is only performed on the server side instead of calling the ssl
>>>>>>> API ( some people may do that due to the previously lack of possibility to
>>>>>>> specify ssl options when calling ssl:ssl_accept with an "sslsocket", this
>>>>>>> is no longer the case in 17.0) the listen socket needs to be put in passive
>>>>>>> mode (listen options are inherited by the acceptsocket) to make sure that
>>>>>>> the it will work, otherwhise it will work sometimes and sometime result in
>>>>>>> the error above.
>>>>>>>
>>>>>>> Regards Ingela Erlang/OTP team - Ericsson AB
>>>>>>>
>>>>>>
>>>>>>
>>>>>>
>>>>>> --
>>>>>> Danil Zagoskin | z@REDACTED
>>>>>>
>>>>>
>>>>>
>>>>
>>>>
>>>> --
>>>> Danil Zagoskin | z@REDACTED
>>>>
>>>
>>>
>>
>>
>> --
>> Danil Zagoskin | z@REDACTED
>>
>
>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://erlang.org/pipermail/erlang-questions/attachments/20140417/62e0ffe2/attachment.htm>


More information about the erlang-questions mailing list