Boost SSL-Server fails with SSLv3-error [duplicate] - c++

This question already has an answer here:
SSL operation failed with code 1: dh key too small
(1 answer)
Closed 6 years ago.
I am programming a "Terminal" in C++ to which a client can connect using SSL to encrypt the connection. I use Boost::asio to handle the sockets and SSL.
I am starting the SSL-Context like this:
boost::asio::ssl::context context_(io_service_, boost::asio::ssl::context::tlsv1);
As you can see, I set the SSL-Version to TLSv1.
This are the context-options:
context_.set_options(boost::asio::ssl::context::default_workarounds
| boost::asio::ssl::context::no_sslv2
| boost::asio::ssl::context::single_dh_use);
context_.use_certificate_chain_file("CERTS/server.crt");
context_.use_private_key_file("CERTS/server.key", boost::asio::ssl::context::pem);
context_.use_tmp_dh_file("CERTS/dh512.pem");
When I now conntect to my server with openssl s_client -connect localhost:8000 -tls1 the handshake fails on the serverside with the error:
"sslv3 alert handshake failure"
Also, I noticed a strange line on the client side:
"140030998197920:error:14082174:SSL routines:SSL3_CHECK_CERT_AND_ALGORITHM:dh key too small:s3_clnt.c:3329:"
What does this mean? Did I make a mistake when I created the certificate? I did it exactly as described in the answer to this question.

context_.use_tmp_dh_file("CERTS/dh512.pem");
... dh key too small:s3_clnt.c:3329:"
You are using a DH key of only 512 bit. Such keys are considered too weak and the handshake will fail with newer versions of TLS libraries. You should better use a 2048 bit DH key instead or even better use ciphers with ECDHE.
For more details on the problem see Logjam Attack.

Related

Boost ASIO SSL handshake failure

When attempting to securely connect to a remote IMAP server using Boost ASIO, the server handshake fails on every connection. The exception message reads:
handshake: unregistered scheme (STORE routines) [asio.ssl:369098857]
My code is below (url is a std::string_view containing the host URL):
using boost::asio::ip::tcp;
namespace ssl = boost::asio::ssl;
using SSLSocket = ssl::stream<tcp::socket>;
boost::asio::io_context context;
ssl::context ssl_context(ssl::context::tls);
SSLSocket socket(context, ssl_context);
ssl_context.set_default_verify_paths();
tcp::resolver resolver(context);
auto endpoints = resolver.resolve(url, "993");
boost::asio::connect(socket.next_layer(), endpoints);
socket.set_verify_mode(ssl::verify_peer);
socket.set_verify_callback(ssl::host_name_verification(url.data()));
socket.handshake(SSLSocket::client);
The code immediately throws an exception on the final line, which is a blocking synchronous handshake.
The prior two lines set up host name verification, similar to how it's done in the official ASIO tutorial. These checks seem to be causing an issue, however, because when they are removed the handshake succeeds. Obviously, this is not a good solution.
After stepping through some of ASIO's internals, I found that the last three lines of the above snippet could be replaced by:
SSL_set_verify(socket.native_handle(), SSL_VERIFY_PEER, nullptr);
socket.handshake(SSLSocket::client);
and the same error occurs. SSL_set_verify is an OpenSSL function, and the fact that setting a null callback directly causes the same issue makes me think that the issue is with my system's OpenSSL environment and not ASIO or the host name verification callback. However, I have not been able to determine what exactly the error means and what could be causing it.
Here is a list of things I have tried while troubleshooting:
Load the system's certificate (.pem) file explicitly Thinking maybe ASIO and/or OpenSSL's were not able to load the right certificates to do the validation, I found my system's (a Mac) certificate file at /private/etc/ssl/cert.pem. I then inserted the following line:
ssl_context.load_verify_file("/private/etc/ssl/cert.pem");
directly after set_default_verify_paths() is called. My program loads this certificate file without complaining, but it doesn't fix the handshake error.
Use a different version of OpenSSL At first I was using Apple's system version of OpenSSL (which is really LibreSSL 2.8.3). I then rebuilt my code using the Homebrew package manager's version of OpenSSL (OpenSSL 3.0.4). This also did not fix the issue, even when I tried calling load_verify_file as in point 1.
Sanity check using the OpenSSL command-line tool To make sure my network connection and URL/port number were correct, I tried connecting to the IMAP server over SSL using the following command:
openssl s_client -connect my.url.com:993 -crlf -verify 1
and it worked fine, connecting to the IMAP server and enabling me to send/receive IMAP responses.
Has anyone encountered similar issues when using OpenSSL and ASIO? I'm not very familiar with setting up an SSL/TLS connection, but I don't see what could be causing the problem.
Thanks for your help!
Given that openssl s_client -connect my.url.com:993 -crlf -verify 1 succeeds there is not a lot that seems wrong. One thing catches my eye: I'd configure the context before constructing an SSL stream from it:
ssl::context ssl_context(ssl::context::tls);
ssl_context.set_default_verify_paths();
SSLSocket socket(context, ssl_context);
Also, openssl likely uses SNI extensions:
// Set SNI Hostname (many hosts need this to handshake successfully)
if(! SSL_set_tlsext_host_name(socket.native_handle(), hostname.c_str()))
{
throw boost::system::system_error(
::ERR_get_error(), boost::asio::error::get_ssl_category());
}
Finally, make sure the url string view contains correct data, notably that it's a valid hostname and null-terminated string. In this case I'd prefer to use a string representation that guarantees null-termination:
Summary
#include <boost/asio.hpp>
#include <boost/asio/ssl.hpp>
using boost::asio::ip::tcp;
namespace ssl = boost::asio::ssl;
using SSLSocket = ssl::stream<tcp::socket>;
int main() {
boost::asio::io_context context;
ssl::context ssl_context(ssl::context::tls);
ssl_context.set_default_verify_paths();
SSLSocket socket(context, ssl_context);
tcp::resolver r(context);
std::string hostname = "www.example.com";
auto endpoints = r.resolve(hostname, "443");
boost::asio::connect(socket.next_layer(), endpoints);
socket.set_verify_mode(ssl::verify_peer);
socket.set_verify_callback(ssl::host_name_verification(hostname));
// Set SNI Hostname (many hosts need this to handshake successfully)
if(! SSL_set_tlsext_host_name(socket.native_handle(), hostname.c_str()))
{
throw boost::system::system_error(
::ERR_get_error(), boost::asio::error::get_ssl_category());
}
socket.handshake(SSLSocket::client);
}
In my case I manged to make it work with this:
ssl_context.load_verify_file("/etc/ssl/certs/ca-bundle.crt");
so pointing to the ca-bundle file instead of the cert file.

C++ openssl: setting list of ciphers

I have very basic C++ application that uses openssl library. Application sends request to server and the list of ciphersuites have to be the next:
4865-4866-4867-49195-49199-49196-49200-52393-52392-49171-49172-156-157-47-53
Using SSL_set_cipher_list and SSL_set_ciphersuites I am setting list of ciphers. But when I use next list:
TLS_AES_128_GCM_SHA256:TLS_AES_256_GCM_SHA384
TLS_CHACHA20_POLY1305_SHA256
TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256
TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256
TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384
TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384
TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256
TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256
TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA
TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA
TLS_RSA_WITH_AES_128_GCM_SHA256
TLS_RSA_WITH_AES_256_GCM_SHA384
TLS_RSA_WITH_AES_128_CBC_SHA
TLS_RSA_WITH_AES_256_CBC_SHA
I am getting 4865-4866-4867-49195-49199-49196-49200-52393-52392-49171-49172-156-157-47-53-255. But I cannot understand where 255 in the end come from ? It shouldn't appear.
255 is a special cipher suit identifier. Reading RFCs when you deal with security is always useful.
RFC5746
3.3. Renegotiation Protection Request Signaling Cipher Suite Value
Both the SSLv3 and TLS 1.0/TLS 1.1 specifications require implementations to ignore data following the ClientHello (i.e., extensions) if they do not understand it. However, some SSLv3 and TLS 1.0 implementations incorrectly fail the handshake in such a case. This means that clients that offer the "renegotiation_info" extension may encounter handshake failures. In order to enhance compatibility with such servers, this document defines a second signaling mechanism via a special Signaling Cipher Suite Value (SCSV) "TLS_EMPTY_RENEGOTIATION_INFO_SCSV", with code point {0x00, 0xFF}. This SCSV is not a true cipher suite (it does not correspond to any valid set of algorithms) and cannot be negotiated. Instead, it has the same semantics as an empty "renegotiation_info" extension, as described in the following sections. Because SSLv3 and TLS implementations reliably ignore unknown cipher suites, the SCSV may be safely sent to any server. The SCSV can also be included in the SSLv2 backward compatible CLIENT-HELLO (see Appendix E.2 of [RFC5246]).
Now you know the name TLS_EMPTY_RENEGOTIATION_INFO_SCSV and you can try to exclude it. But this may be not working.

C++ Boost asio (OpenSSL) get cipher and TLS/SSL version of active connection

We have some SSL communication using Boost asio. Connecting and communication works fine, but now we'd like to show the actual TLS version and cipher used by the connection.
The Boost asio objects we use for the SSL connection are defined like this:
typedef boost::asio::ssl::stream<boost::asio::ip::tcp::socket> ssl_socket;
boost::shared_ptr<ssl_socket> m_psslsocket;
boost::asio::ssl::context* m_pcontext;
It looks like Boost asio doesn't really have an interface to query the connection for the cipher once it's set up.
I can get the OpenSSL SSL_CTX* pointer by calling this:
SSL_CTX* ctx = pContext->impl();
Or the OpenSSL SSL* by calling this:
m_psslsocket->native_handle()
But from the OpenSSL documentation I can't figure out how to get the used cipher and TLS version from this.
OpenSSL documentation says:
SSL_client_version() returns the numeric protocol version advertised by the client in the legacy_version field of the ClientHello when initiating the connection. Note that, for TLS, this value will never indicate a version greater than TLSv1.2 even if TLSv1.3 is subsequently negotiated. SSL_get_version() returns the name of the protocol used for the connection. SSL_version() returns the numeric protocol version used for the connection. They should only be called after the initial handshake has been completed. Prior to that the results returned from these functions may be unreliable.
So the answer is:
SSL_get_version(SSL*)

C++ OpenSSL Fails to perform handshake when accepting in non-blocking mode. What is the proper way?

I'm trying to implement OpenSSL into my application which uses raw C sockets and the only issue I'm having is the SSL_accept / SSL_connect part of the code which starts the KeyExchange phase but does not seem to complete it on the serverside.
I've had a look at countless websites and Q&A's here on StackOverflow to get myself through the OpenSSL API since this is basically the first time I'm attempting to implement SSL into an application but the only thing I could not find yet was how to properly manage failed handshakes.
Basically, running process A which serves as a server will listen for incoming connections. Once I run process B, which acts as a client, it will successfully connect to process A but SSL_accept (on the server) fails with error code -2 SSL_ERROR_WANT_READ.
According to openssl handshake failed, the problem is "easily" worked around by calling SSL_accept within a loop until it finally returns 1 (It successfully connects and completes the handshake). However, I do not believe that this is the proper way of doing things as it looks like a dirty trick. The reason for why I believe it is a dirty trick is because I tried to run a small application I found on https://www.cs.utah.edu/~swalton/listings/articles/ (ssl_client and ssl_server) and magically, everything works just fine. There are no multiple calls to SSL_accept and the handshake is completed right away.
Here's some code where I'm accepting the SSL connection on the server:
if (SSL_accept(conn.ssl) == -1)
{
fprintf(stderr, "Connection failed.\n");
fprintf(stderr, "SSL State: %s [%d]\n", SSL_state_string_long(conn.ssl), SSL_state(conn.ssl));
ERR_print_errors_fp(stderr);
PrintSSLError(conn.ssl, -1, "SSL_accept");
return -1;
}
else
{
fprintf(stderr, "Connection accepted.\n");
fprintf(stderr, "Server -> Client handshake completed");
}
This is the output of PrintSSLError:
SSL State: SSLv3 read client hello B [8465]
[DEBUG] SSL_accept : Failed with return -1
[DEBUG] SSL_get_error() returned : 2
[DEBUG] Error string : error:00000002:lib(0):func(0):system lib
[DEBUG] ERR_get_error() returned : 0
[DEBUG] errno returned : Resource temporarily unavailable
And here's the client side snippet which connects to the server:
if (SSL_connect(conn.ssl) == -1)
{
fprintf(stderr, "Connection failed.\n");
ERR_print_errors_fp(stderr);
PrintSSLError(conn.ssl, -1, "SSL_connect");
return -1;
}
else
{
fprintf(stderr, "Connection established.\n");
fprintf(stderr, "Client -> Server handshake completed");
PrintSSLInfo(conn.ssl);
}
The connection is successfully enstablished client-side (SSL_connect does not return -1) and PrintSSLInfo outputs:
Connection established.
Cipher: DHE-RSA-AES256-GCM-SHA384
SSL State: SSL negotiation finished successfully [3]
And this is how I wrap the C Socket into SSL:
SSLConnection conn;
conn.fd = fd;
conn.ctx = sslContext;
conn.ssl = SSL_new(conn.ctx);
SSL_set_fd(conn.ssl, conn.fd);
The code snippet here resides within a function that takes a file-descriptor of the accepted incoming connection on the raw socket and the SSL Context to use.
To initialize the SSL Contexts I use TLSv1_2_server_method() and TLSv1_2_client_method(). Yes, I know that this will prevent clients from connecting if they do not support TLS 1.2 but this is exactly what I want. Whoever connects to my application will have to do it through my client anyway.
Either way, what am I doing wrong? I'd like to avoid loops in the authentication phase to avoid possible hang ups/slow downs of the application due to unexpected infinite loops since OpenSSL does not specify how many attempts it might take.
The workaround that worked, but that I'd like to avoid, is this:
while ((accept = SSL_accept(conn.ssl)) != 1)
And inside the while loop I check for the return code stored inside accept.
Things I've tried to workaround the SSL_ERROR_WANT_READ error:
Added usleep(50) inside the while loop (still takes several cycles to complete)
Added SSL_do_handshake(conn.ssl) after SSL_connect and SSL_accept (didn't change anything on the end-result)
Had a look at the code shown on roxlu.com (search on Google for "Using OpenSSL with memory BIOs - Roxlu") to guide me through the handshaking phase but since I'm new to this, and I don't directly use BIOs in my code but simply wrap my native C sockets into SSL, it was kind of confusing. I'm also unable to re-write the Networking part of the application as it'd would be too much work for me right now.
I've done some tests with the openssl command-line as well to troubleshoot the issue but it gives no error. The handshake appears to be successful as no errors such as:
24069864:error:1409E0E5:SSL routines:ssl3_write_bytes:ssl handshake failure:s3_pkt.c:656
appear. Here's the whole output of the command
openssl s_client -connect IP:Port -tls1_2 -prexit -msg
http://pastebin.com/9u1bfuf4
Things to note:
1. I'm using the latest OpenSSL version 1.0.2h
2. Application runs on a Unix system
3. Using self-signed certificates to encrypt the network traffic
Thanks everyone who's going to help me out.
Edit:
I forgot to mention that the sockets are in non-blocking mode since the application serves multiple clients in one-go. Though, client-side they are in blocking mode.
Edit2:
Leaving this here for future reference: jmarshall.com/stuff/handling-nbio-errors-in-openssl.html
You have clarified that the socket question is non-blocking.
Well, that's your answer. Obviously, when the socket is in a non-blocking mode, the handshake cannot be immediately completed. The handshake involves an exchange of protocol packets between the client and the server, with each one having to wait to receive the response from its peer. This works fine when the socket is in its default blocking mode. The library simply read()s and write()s, which blocks and waits until the message gets succesfully read or written. This obviously can't happen when the socket is in the non-blocking mode. Either the read() or write() immediately succeeds, or fails, if there's nothing to read or if the socket's output buffer is full.
The manual pages for SSL_accept() and SSL-connect() explain the procedure you must implement to execute the SSL handshake when the underlying socket is in a non-blocking mode. Rather than repeating the whole thing here, you should read the manual pages yourself. The capsule summary is to use SSL_get_error() to determine if the handshake actually failed, or if the library wants to read or write to/from the socket; and in that eventuality call poll() or select(), accordingly, then call SSL_accept() and SSL_connect() again.
Any other approach, like sprinkling silly sleep() calls, here and there, will result in an unreliable house of cards, that will fail randomly.

BIO_do_connect() fails seemingly because SSL v3 is used, is there a way to get more diagnostics?

I wrote a class to handle SSL connections that I use to communicate with Paypal.
It worked just fine for about a year and now it fails right away. From the error I get, it would seem that this happens because they (finally) turned off the SSLv3 cipher. Yet, I thought it was turned off on my end already.
There is the error I get when attempting my connection with my code:
139673112286976:error:14094410:SSL routines:SSL3_READ_BYTES:sslv3 alert handshake failure:s3_pkt.c:1262:SSL alert number 40
I get the exact same error if I try to connect with -ssl3 using the command line tool:
openssl s_client -connect api.sandbox.paypal.com:443 -ssl3
Note that the command line without the -ssl3 option works as expected.
And I have seen a couple of posts that say that the error means there is a problem with the cipher used, leading me to think that is the problem I'm running into.
For those interested, the whole class is found in our snapcpp git (go up one to get the .h and other files from the snapwebsites library).
There is the relevant code. I removed the error checking happening before the one that triggers the current failure:
std::shared_ptr<SSL_CTX> ssl_ctx(SSL_CTX_new(TLSv1_client_method()), ssl_ctx_deleter);
SSL_CTX_set_verify_depth(ssl_ctx.get(), 4);
SSL_CTX_set_options(ssl_ctx.get(), SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3 | SSL_OP_NO_TLSv1 | SSL_OP_NO_COMPRESSION);
SSL_CTX_set_cipher_list(ssl_ctx.get(), "HIGH:!aNULL:!kRSA:!PSK:!SRP:!MD5:!RC4");
SSL_CTX_load_verify_locations(ssl_ctx.get(), NULL, "/etc/ssl/certs");
std::shared_ptr<BIO> bio(BIO_new_ssl_connect(ssl_ctx.get()), bio_deleter);
SSL * ssl(nullptr);
BIO_get_ssl(bio.get(), &ssl);
SSL_set_mode(ssl, SSL_MODE_AUTO_RETRY);
BIO_set_conn_hostname(bio.get(), const_cast<char *>(addr.c_str()));
BIO_set_conn_int_port(bio.get(), &port);
if(BIO_do_connect(bio.get()) <= 0)
{
ERR_print_errors_fp(stderr);
throw tcp_client_server_initialization_error("failed connecting BIO object to server");
}
So my class throws because the BIO_do_connect() gets the error I mentioned earlier. Yet I would think that these options:
SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3 | SSL_OP_NO_TLSv1
would be enough to avoid SSLv3.
What else could be the culprit?
You stated SSL_OP_NO_TLSv1 yet you use TLSv1 method.
Try changing TLSv1_client_method() to SSLv23_client_method(). This will alow usage of any method (in fact it will negotiate the highest available SSL/TLS version). You can limit it with SSL_CTX_set_options as you did in your code.
For some reasons the documentation says that SSLv23_method() is deprecated and one should use TLS_method(). Howerer, there isn't such thing in their newest (OpenSSL 1.0.2e) code.