16

I have a C++ client app that uses Boost ASIO to make SSL connections to various servers. But against 2 specific servers, the SSL connection cannot be established. It hangs in the call to boost::asio::ssl::stream::handshake().

I've used Wireshark to observe the conversation between client and server. A working SSL connection seems to do this:

sslsocket.lowest_layer().connect( endpoint, ec );
C ->    SYN    -> S
C <-  SYN ACK  <- S
C ->    ACK    -> S
sslsocket.handshake( SSLSocket::client, ec );
C -> 209 bytes -> S
C <- 690 bytes <- S
C -> 198 bytes -> S
C <- 415 bytes <- S

...and at this point the ASIO handshake() call returns indicating all is well, and the SSL socket connection works fine.

But against 2 different servers [*], the handshake looks like this:

sslsocket.lowest_layer().connect( endpoint, ec );
C ->    SYN    -> S
C <-  SYN ACK  <- S
C ->    ACK    -> S
sslsocket.handshake( SSLSocket::client, ec );
C -> 209 bytes -> S
...2 minute pause...
C <-    RST    <- S

Looking at log files on these servers, it seems as if after the initial 209 bytes are sent in the handshake, the server considered the SSL connection fully established. But the client is still sitting in the Boost ASIO handshake() call, and eventually returns ec=104 when the connection is reset.

So I'm thinking maybe there are different types of SSL handshakes, and maybe there is a "simpler" one I should be using?

[*] I know someone will want to know: one of the servers causing this problem with the client app is FileZilla Server for Windows setup to use SSL/TLS [FTPS], and the other is a proprietary service running on Linux.)


UPDATE: Sam Miller asked that I post my code describing how the ssl context is setup:

Class (.hpp file) contains this:

typedef boost::asio::ssl::stream<boost::asio::ip::tcp::socket> SSLSocket;
boost::asio::io_service    ioservice;
boost::asio::ssl::context  sslcontext;
SSLSocket                  sslDataSocket;
boost::system::error_code  ec;

Constructor has these initializers:

ioservice      ( 2 ),
sslcontext     ( ioservice, boost::asio::ssl::context::sslv23 ),
sslDataSocket  ( ioservice, sslcontext ),

...and this code:

sslcontext.set_options( boost::asio::ssl::context::default_workarounds |
                        boost::asio::ssl::context::verify_none         );

And this is the code where the SSL socket is established and the handshake hangs:

std::cout << "connecting SSL socket to endpoint " << buffer << std::endl;
sslDataSocket.lowest_layer().connect( tcpEndpoint, ec );
std::cout << "connect() done, ec=" << ec.value() << std::endl;
if ( ec ) throw "test 1";

std::cout << "starting ssl handshake" << std::endl;
sslDataSocket.handshake( SSLSocket::client, ec );
std::cout << "handshake done, ec=" << ec.value() << std::endl;
if ( ec ) throw "test 2";
Stéphane
  • 19,459
  • 24
  • 95
  • 136
  • Very well done question. – Luc Danton Mar 04 '12 at 10:54
  • Recently I had a problem with SSL [link](http://stackoverflow.com/questions/9411506/boosts-asio-ssl-dont-work-in-some-conditions). The command `openssl s_client -connect server_ip:server_port` helped me to understand the cause. May be it will help you also. – megabyte1024 Mar 04 '12 at 10:58
  • please post your code describing how the ssl context is setup – Sam Miller Mar 04 '12 at 16:21
  • I've amended the question to include the lines from the .hpp and the connection setup. – Stéphane Mar 04 '12 at 18:11
  • does `openssl s_client -ssl2 -ssl3` reproduce the same behavior? – Sam Miller Mar 05 '12 at 02:30
  • Yes, openssl s_client does the same thing. It hangs, then eventually ends with "SSL handshake has read 0 bytes and written 0 bytes. I was reading through the FileZilla client source code, and it seems as if they prime gnutls with the primary socket's certificate information. In functions like `CTlsSocket::Handshake()` I see comments such as "implicitly trust certificate of primary socket" followed by calls to `gnutls_certificate_get_peers()`, `gnutls_session_get_data( fromPrimarySocket )`, and `gnutls_session_set_data( newSession )`. Maybe this is how the handshake is effectively skipped? – Stéphane Mar 05 '12 at 06:41
  • 2
    So I'm reading this question and answer, trying to debug a Boost+OpenSSL issue. Scroll back up to the top to see who the author is. Oh dear...I must be losing it. 8 years later, I didn't even remember my own question nor answer! Why is ASIO and OpenSSL so unfriendly? – Stéphane Sep 04 '20 at 07:07

1 Answers1

12

I figured it out. This SSL tutorial (http://h71000.www7.hp.com/doc/83final/ba554_90007/ch04s03.html) contained the key that finally got this working for me. Quote:

You can reuse the information from an already established SSL session to create a new SSL connection. Because the new SSL connection is reusing the same master secret, the SSL handshake can be performed more quickly. As a result, SSL session resumption can reduce the load of a server that is accepting many SSL connections.

So here is how I got this working with Boost ASIO:

  • setup the normal SSL control socket (lots of examples, including this question)
  • when you need to setup the 2nd SSL data socket, do this:

    sslSocket2.lowest_layer().connect( tcpEndpoint, ec );
    SSLSocket::impl_type impl1 = sslSocket1.impl();
    SSLSocket::impl_type impl2 = sslSocket2.impl();
    SSL_SESSION *savedSession = SSL_get1_session( impl1->ssl );
    SSL_set_session( impl2->ssl, savedSession );
    SSL_connect( impl2->ssl );

That's it. At this point, no need to call sslSocket2.handshake(). Just read and write to the socket knowing the connection has been established.

Stéphane
  • 19,459
  • 24
  • 95
  • 136
  • 1
    Also see: http://stackoverflow.com/questions/7786352/ssl-session-resume-on-ftp-transfer-connection-with-openssl – Stéphane Apr 08 '12 at 07:07
  • 1
    You probable want to call sslSocket2.async_handshake instead of directly calling SSL_connect if you are using asynchronous functions. async_handshake calls SSL_connect internally. – John Mar 08 '15 at 05:20