Boost logo

Boost Users :

Subject: [Boost-users] [boost::asio] [stream::ssl] stackful coroutine yield from SSL handshake in servername callback
From: SamJam Liddicott (samjam_at_[hidden])
Date: 2015-11-03 10:07:43


I'm testing a spoofing MITM SSL proxy based on boost::asio and
boost::asio::stream::ssl, using boost 1.55

I register the tlsext_servername_callback, so that I can observe the called
name during SSL handshake with the client.

In the callback, I have the information needed to either get/create an
appropriate SSL_CTX with certificates to use with the client, or start an
SSL handshake with the server, to obtain the server certificate for
re-signing and then put that back into the SSL_CTX to be passed to the
client, before returning the tlsext_servername callback.

It is clear that I must block the tlsext_servername callback from returning
until I have the new SSL_CTX ready, which could take some time if I need to
wait on a handshake with the real server.

I can block return by performing a blocking handshake with the server, but
then I consume one of the io_service threads merely blocking, and I don't
want to waste them like that.

I can successfully run it all as a new thread using blocking IO, in this
form:

ssl_connect(...) {
  boost::thread([...]() ({
    sni_callback = [...](std::string server_name) {
      server.handshake(...);
      SSL_set_SSL_CTX(client.native_handle(), server.ctx));
    };

    client_socket.handshake(...);
    handler(); // really done by io_service.post
  }).detach();
}

But I want to use stackful coroutines in the belief that thay are less
costly and that I will be able to have more connections in progress (though
I may have to link with BoringSSL).

Using a coroutine spawn, I can convert the handshake with the server, like
this:

ssl_connect(...) {
  boost::asio::spawn(my_strand, [...](boost::asio::yield_context yield) {
    sni_callback = [...](std::string server_name) {
      server.async_handshake(..., yield[ec]); // <---- works nicely
      SSL_set_SSL_CTX(client.native_handle(), server.ctx));
    };

    client_socket.handshake(...);
    handler(); // really done by io_service.post
  });
}

but the handshake with the client must remain blocking, otherwise, with:

    client_socket.async_handshake(..., yield[ec]);

I get segfaults in the coroutine with unhelpful stack traces, mentioning
only some SSL cleanup functions. However if the client connection hangs,
then the client handshake can still hang and block a worker thread.

My guess is that if the handshake with the client is not blocking, the SNI
callback does not occur on the coroutine, and so I cannot properly block
the return and possibly the use of yield in the SNI callback is invalid.

If there is a solution that doesn't involve patching either libssl or
stream::ssl, it will involve some asio_invoke_handler hooks.

However, for that to work, the coroutine would have to be activated, but
not to complete the async_handshake call. Instead it would be activated to
handle the handshake read completion, leading to the parsing, SNI callback
& return, further handshake write, and yield back to the io_service. I
don't know if the coroutine/strand implementation can even do that -- be
activated for work and not for return.

So my first question is: is it likely possible to make coroutine do this?

Looking in boost source detail/io.hpp for case engine::want_input_and_retry
I notice:

            next_layer_.async_read_some(
                boost::asio::buffer(core_.input_buffer_),
                BOOST_ASIO_MOVE_CAST(io_op)(*this));

which seems to make no attempt to use asio_hanlder_invoke to allow it to be
hooked, so that might need fixing.

What sort of asio_handler_invoke hook would cause the handler to be invoked
on the coroutine? Could strand.dispatch() do it? If it would, I'm not sure
how to write such a hook either to work with argument dependent lookup, or
 to invoke in the coroutine.

Otherwise I may as well stick to starting a scope-limited thread, and using
blocking handshake operations.

But I like to know the limits...

Samjam



Boost-users list run by williamkempf at hotmail.com, kalb at libertysoft.com, bjorn.karlsson at readsoft.com, gregod at cs.rpi.edu, wekempf at cox.net