Client returns -1 on BIO_do_connect - c++

I'm trying to create a basic server and client using OpenSSL and its BIOs but BIO_do_connect returns -1. ERR_get_error returns 0 after that.
I've tried to minimize the code below by just writing // check [condition]. In my real code I'm doing the same thing with an if check and then I print out the error returned by ERR_get_error. (so if condition is true I'm printing an error msg)
This is my code for the server:
// init OpenSSL
SSL_load_error_strings();
ERR_load_BIO_strings();
SSL_library_init();
OpenSSL_add_all_algorithms();
SSL_CTX *ctx = SSL_CTX_new(SSLv23_server_method());
SSL_CTX_set_default_passwd_cb(ctx, &myPasswordCallback);
int certState = SSL_CTX_use_certificate_file(ctx, "../certs/cert.pem", SSL_FILETYPE_PEM);
// check certState < 0
int keyState = SSL_CTX_use_PrivateKey_file(ctx, "../certs/key.pem", SSL_FILETYPE_PEM);
// check keyState < 0
BIO *serverBio = BIO_new_ssl(ctx, 0);
// check serverBio == nullptr
SSL *serverSsl = nullptr;
BIO_get_ssl(serverBio, &serverSsl);
// check serverSsl == nullptr
SSL_set_mode(serverSsl, SSL_MODE_AUTO_RETRY);
BIO *acceptBio = BIO_new_accept("6672");
// check acceptBio == nullptr
int setupAcceptResult = BIO_do_accept(acceptBio);
// check setupAcceptResult <= 0
int acceptResult = BIO_do_accept(acceptBio);
// check acceptResult <= 0
BIO *clientBio = BIO_pop(acceptBio);
// check clientBio == nullptr
BIO_free_all(clientBio);
BIO_free_all(acceptBio);
BIO_free_all(serverBio);
// cleanup OpenSSL
SSL_CTX_free(ctx);
EVP_cleanup();
ERR_free_strings();
This server runs fine but my client fails to connect to it:
// init OpenSSL
SSL_load_error_strings();
ERR_load_BIO_strings();
SSL_library_init();
OpenSSL_add_all_algorithms();
SSL_CTX *ctx = SSL_CTX_new(SSLv23_client_method());
SSL_CTX_set_default_passwd_cb(ctx, &myPasswordCallback);
int certState = SSL_CTX_use_certificate_file(ctx, "../certs/cert.pem", SSL_FILETYPE_PEM);
// check certState < 0
int keyState = SSL_CTX_use_PrivateKey_file(ctx, "../certs/key.pem", SSL_FILETYPE_PEM);
// check keyState < 0
BIO *clientBio = BIO_new_ssl_connect(ctx);
SSL *clientSsl = nullptr;
BIO_get_ssl(clientBio, &clientSsl);
// check clientSsl == nullptr
SSL_set_mode(clientSsl, SSL_MODE_AUTO_RETRY);
BIO_set_conn_hostname(clientBio, "localhost:6672");
long connectionState = BIO_do_connect(clientBio);
// check connectionState <= 0
// here it fails; connectionState is -1
long sslState = SSL_get_verify_result(clientSsl);
// check sslState != X509_V_OK
BIO_free_all(clientBio);
SSL_CTX_free(ctx);
EVP_cleanup();
ERR_free_strings();
I'm sorry for posting so much code. I didn't really find a complete example of OpenSSL server/client using BIOs.

You server code is essentially this:
setup serverBio as SSL
create a new BIO acceptBio without SSL
accept the connection connection -> clientBio
free everything
The server is not doing any SSL handshake here since the serverBio gets not used for the newly created TCP connection clientBio.
Apart from that I recommend that you test your server and client first against known good client and server so that you can faster figure out where the problem is. openssl s_client and openssl s_server provide such test client and server. Also packet capturing (wireshark) helps to find out what happens between server and client.

Related

SSL_connect fails with SSL_ERROR_SSL - How can I investigate what is going wrong?

Simple socket program which is trying to connect to a valid https server (can browse with FireFox/Chrome etc etc).
Code is:
// Register the error strings for libcrypto & libssl
SSL_load_error_strings();
// Register the available ciphers and digests
SSL_library_init();
// load all algos
OpenSSL_add_all_algorithms();
// create context (new context for each connection)
ssl_ctx_ = SSL_CTX_new(SSLv23_client_method());
if(!ssl_ctx_) {
throw std::runtime_error("Can't initialize ssl context");
}
// create handle
ssl_h_ = SSL_new(ssl_ctx_);
if(!ssl_h_) {
SSL_CTX_free(ssl_ctx_);
throw std::runtime_error("Can't initialize new ssl handle");
}
// sd_ is a valid socket connected to a host on port 443,
// i.e. www.repubblica.it:443
// bind socket
if (!SSL_set_fd(ssl_h_, sd_)) {
SSL_free(ssl_h_);
SSL_CTX_free(ssl_ctx_);
throw std::runtime_error("Can't set sd to ssl handle");
}
// set blocking
// this api makes the socket blocking
fd_block(sd_);
// perform handshake
const int r = SSL_connect(ssl_h_);
// r is now -1
if(1 != r) {
// find out the error
const int err = SSL_get_error(ssl_h_, r);
SSL_free(ssl_h_);
SSL_CTX_free(ssl_ctx_);
throw std::runtime_error(std::string("Can't perform ssl handshake, err code: ") + ssl_err_human(err));
}
And I get the following error: Exception: Can't perform ssl handshake, err code: SSL_ERROR_SSL which is not really insightful.
What can I do next to understand what I'm doing wrong and then fix it?
Thanks!
Ps. running on Ubuntu 22.04 with default libssl-dev package

Trying to make a secure handshake between server and client with OpenSSL

I'm trying to make a basic Client-Server program that can exchange messages using the OpenSSL library. I'm very new to OpenSSL and cryptography and I'm trying to understand exactly how to make sure that the connection between my client and server is secure. Currently I'm using self signed certificates for both client and server but the certificate verification fails when the client tries to connect to the server with this error:
140336190395008:error:1417C086:SSL routines:tls_process_client_certificate:certificate verify failed:../ssl/statem/statem_srvr.c:3711:
In the main method of my server program, I first set verify for the CTX and use the following flags:
SSL_CTX_set_verify(ctx, SSL_VERIFY_PEER | SSL_VERIFY_FAIL_IF_NO_PEER_CERT, NULL);
Then when setting up the servlet I call the following method to show certificates:
void ShowCerts(SSL* ssl) /*show the ceritficates to client and match them*/ {
X509 *cert;
long int verify;
char *line;
cert = SSL_get_peer_certificate(ssl); /* Get certificates (if available) */
verify = SSL_get_verify_result(ssl);
if (verify == X509_V_OK) {
printf("Yay it worked\n");
}
if ( cert != NULL ) {
printf("Server certificates:\n");
line = X509_NAME_oneline(X509_get_subject_name(cert), 0, 0);
printf("Server: %s\n", line); /*server certifcates*/
free(line);
line = X509_NAME_oneline(X509_get_issuer_name(cert), 0, 0);
printf("client: %s\n", line); /*client certificates*/
free(line);
X509_free(cert);
} else {
printf("No certificates.\n");
}
}
As I said I'm very new to this so I might be missing something basic here. Also apologies in advance if I missed any important info, this is my first time asking a question here.

SSL_connect() and SSL_accept() calls

I have been struggling with this for a few days now figuring out how they work. I have read the documentation and looked at some examples but I am still in need of guidance.
Specifically, when the client calls connect() and successfully connects to the server host, should I issue the SSL_connect() right after it to Initiate the handshake ? The client then tries to write some bytes to the socket using SSL_write().
On the other hand, the server uses pselect() to monitor any read fds ready for read and issues the accept() call successfully for the incoming connection. Should I issue the SSL_accept() call right after the accept() returns to complete the handshake ?
I have noticed that the SSL_connect() returns SSL_ERROR_WANT_READ (this is when the SSL_connect() is issued after a select() call to monitor the write fd set and returns and as per the Openssl documentation).
What is the right procedure here on issuing the calls and in what order ?
Edit to add snippet of code -
Client side :
err = connect(fd, addr, addrlen);
if ( err == -1 && errno == EINPROGRESS )
{
// check if this is a true error,
// or wait until connect times out
fd_set fdset;
FD_ZERO(&fdset);
FD_SET(sfd, &fdset);
timeval tv = {F_sockwaitconnect, 0}; TEMP_FAILURE_RETRY(err = select(fd + 1,\
NULL,\
&fdset,\
NULL,\
&tv));
// what happened?
if ( err == 1 )
{
connect was successful
}
else
return 0;
const SSL_METHOD *method;
SSL_CTX *cctx;
SSL *cssl;
FILE *fp;
fp = stdout;
ERR_clear_error();
method = TLSv1_client_method();
cctx = SSL_CTX_new(method);
if ( cctx == NULL )
{
ERR_print_errors_fp(stdout);
return 0;
}
SSL_CTX_set_verify(cctx, SSL_VERIFY_PEER, NULL);
SSL_CTX_set_verify_depth(cctx, 4);
if (SSL_CTX_load_verify_locations(cctx, "mycert.pem", NULL) == 0)
return 0;
SSL_CTX_set_options(cctx, SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3 | SSL_OP_NO_TLSv1_1 | SSL_OP_NO_COMPRESSION);
ERR_clear_error();
cssl = SSL_new(cctx); /* create new SSL connection state */
SSL_set_fd(cssl, fd); * attach the socket descriptor */
ERR_clear_error();
int rconnect = SSL_ERROR_WANT_READ;
while ( rconnect == SSL_ERROR_WANT_READ || rconnect == SSL_ERROR_WANT_WRITE )
{
char *buf = (char *) malloc(124);
ERR_error_string(SSL_get_error(cssl, rconnect), buf);
ERR_clear_error();
if ( rconnect == SSL_ERROR_WANT_READ ) {
int err = 0;
fd_set fdset;
FD_ZERO(&fdset);
FD_SET(fd, &fdset);
timeval tv = {F_sockwaitconnect, 0};
TEMP_FAILURE_RETRY(err1 = select(fd + 1,\
&fdset,\
NULL,\
NULL,\
&tv));
// what happened?
if ( err == 1 )
{
rconnect = SSL_connect(cssl);
}
}
}
X509 *cert;
cert = SSL_get_peer_certificate(cssl);
char line[2000+1];
if ( cert != NULL )
{
X509_NAME_oneline(X509_get_subject_name(cert), line, MAX_SIZE);
X509_NAME_oneline(X509_get_issuer_name(cert), line1, MAX_SIZE);
X509_free(cert);
}
ERR_clear_error();
r = SSL_write(cssl, buffer, len);
< check error >
Server side :
int res = pselect(max_fd + 1, // host socket file descriptor
&fd_setw, // set of ds wait 4 incoming data
NULL, // no write operations
NULL, // no exception operations
&tm, // how much time to wait
&sig_set); // block all signals
if ( event on listening socket )
{
client = accept(sfd, &peer, &peerl);
}
else // incoming data to receive on existing connection
{
SSL *ssl;
FILE *fp = stdout;
if ( !ctx )
{
return 0;
}
ERR_clear_error();
ssl = SSL_new(ctx);
SSL_set_fd(ssl, soc);
int ret = SSL_accept(ssl);
while (ret <= 0) {
ERR_print_errors_fp(fp);
char *buf = (char *) malloc(124);
ERR_error_string(SSL_get_error(ssl, ret), buf);
ERR_clear_error();
ret = SSL_accept(ssl);
}
X509 *cert;
cert = SSL_get_peer_certificate(ssl);
char line[2000+1];
if ( cert != NULL )
{
X509_NAME_oneline(X509_get_subject_name(cert), line, MAX_SIZE);
X509_NAME_oneline(X509_get_issuer_name(cert), line1, MAX_SIZE);
X509_free(cert);
}
// get data and analyze result
int rc = 0;
bool recv_called = false;
rc = SSL_read(ssl, buffer, len);
< check error >
}
Before all the above, the server opens, binds and listens on a non-blocking socket for any new incoming client connections.
When I run the above, the client does the connect() and the server does the accept().
The server is now waiting at pselect() for any fd's to be ready to receive data.
The client on the other hand is at the SSL_connect() and keeps getting the SSL_ERROR_WANT_READ error. The select() returns the socket is ready to read.
My guess is the client is waiting for the SSL_accept() part of the handshake ? I do not know why the server is waiting at pselect(). The code around SSL_accept() is wrong i.e it loops and does not look for the WANT_READ and WANT_WRITE errors but I do not get to that point in the code.
SSL_connect can be called whenever the connect is finished. Since both connect and SSL_connect need to exchange data with the peer they might not succeed immediately when using non-blocking sockets. If SSL_connect returns with an error of SSL_WANT_READ or SSL_WANT_WRITE you have to call it again after new data got available on the socket (SSL_WANT_READ) or the socket is writable (SSL_WANT_WRITE). You can check or wait for this with select, pselect, poll, epoll, kqueue or whatever API your OS provides for this.
SSL_accept and accept are similar, i.e. SSL_accept can be called directly after a successful accept, might not succeed immediately since data exchange is needed with the SSL client and thus you have to call it again if it returns an error of SSL_WANT_READ or SSL_WANT_WRITE.
Note that SSL_write and SSL_read might also result in such errors. i.e. you need to deal with SSL_WANT_READ and SSL_WANT_WRITE also for these functions and also the same way as with SSL_connect and SSL_accept. It might even be that a SSL_read results in a SSL_WANT_WRITE since a SSL renegotiation might happen even if the SSL session was already established.

OpenSSL CRYPTO_malloc leaking, How do i free it up?

I have a very simple application that goes through a list of hostnames and connects to each one of them on the HTTPS port to obtain fresh server data for client identified data.
In order to obtain the data i use OpenSSL but it seems like it is leaking the memory everytime.
Class responsible for connecting/putting/receivng the SSL data.
class CConnector
{
public:
static std::string GetData (const std::string& strHostName)
{
// Initialize malloc, free, etc for OpenSSL's use
CRYPTO_malloc_init();
// Initialize OpenSSL's SSL libraries
SSL_library_init();
// Load all available encryption algorithms
OpenSSL_add_all_algorithms();
//
std::string strRequest="GET /\r\n";
// Set up a SSL_CTX object, which will tell our BIO object how to do its work
SSL_CTX* ctx = SSL_CTX_new(SSLv23_client_method());
// Create our BIO object for SSL connections.
BIO* bio = BIO_new_ssl_connect(ctx);
// Create a SSL object pointer, which our BIO object will provide.
SSL* ssl = NULL;
// Failure?
if (bio == NULL)
{
CLogger::Instance()->Write(XLOGEVENT_LOCATION,CLogger::eState::ERROR, "BIO");
ERR_print_errors_fp(stderr);
if(ctx!=NULL)SSL_CTX_free(ctx);
if(bio!=NULL)BIO_free_all(bio);
return "";
}
// Makes ssl point to bio's SSL object.
BIO_get_ssl(bio, &ssl);
// Set the SSL to automatically retry on failure.
SSL_set_mode(ssl, SSL_MODE_AUTO_RETRY);
// We're connection to google.com on port 443.
std::string strHost = GetHostFromURL(strHostName);
strHost+=":https";
//
BIO_set_conn_hostname(bio, strHost.data());
// Same as before, try to connect.
if (BIO_do_connect(bio) <= 0)
{
CLogger::Instance()->Write(XLOGEVENT_LOCATION,CLogger::eState::ERROR, "cannot connect");
if(ctx!=NULL)SSL_CTX_free(ctx);
if(bio!=NULL)BIO_free_all(bio);
return "";
}
// Now we need to do the SSL handshake, so we can communicate.
if (BIO_do_handshake(bio) <= 0)
{
CLogger::Instance()->Write(XLOGEVENT_LOCATION,CLogger::eState::ERROR, "SSL Handshake");
if(ctx!=NULL)SSL_CTX_free(ctx);
if(bio!=NULL)BIO_free_all(bio);
return "";
}
// Create a buffer for grabbing information from the page.
char buf[1024];
memset(buf, 0, sizeof(buf));
// BIO_puts sends a null-terminated string to the server.
BIO_puts(bio, strRequest.c_str());
int iChars = 0;
while (1)
{
iChars = BIO_read(bio, buf, sizeof(buf)-1);
// Close reading
if (iChars <= 0)
break;
// Terminate the string
buf[iChars] = 0;
// Add to the final output
strOutput.append(buf);
}
SSL_shutdown(ssl);
SSL_CTX_free(ctx);
BIO_free_all(bio);
}
private:
};
And the main program calling the class method
while(1)
{
for(int a = 0; a < m_vHostNames.size(); a++)
{
std::string strOutput = CConnector::GetData(m_vHostNames[a]);
// Process the data
}
sleep(10000);
}
The debugger/profiler output:
Question:
Do i free the OpenSSL correctly? Or there is something else required?
Thank you for any input into this.

openssl SSL_get_verify_result returns error 20

I am writing a C++ program that connects using SSL. The certificate chain checks out using:
openssl verify -CAfile test.pem private.pem
where test.pem contains the intermediate and root certificate. My test program does not verify the certificate chain.
if ( !SSL_CTX_load_verify_locations( ctx, "c:/Certs/test.pem", NULL ) ) {
// Failure message and cleanup goes here.
}
SSL* ssl;
BIO* bio = BIO_new_ssl_connect( ctx );
BIO_get_ssl( bio, &ssl );
SSL_set_mode( ssl, SSL_MODE_AUTO_RETRY );
BIO_set_conn_hostname( bio, "url.com:https" );
if ( BIO_do_connect( bio ) <= 0 ) {
// Failure message and cleanup goes here.
}
if ( SSL_get_verify_result( ssl ) != X509_V_OK ){
// Here is where I get the error 20...
// Free all resources and exit.
}
OpenSSL documentation describes error 20 as:
X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT_LOCALLY: unable to get local issuer certificate.
The issuer certificate could not be found: this occurs if the issuer certificate of an
untrusted certificate cannot be found.
I need help identifying the problem and how to solve it. I am certain the certificates I have are correct.
It seems the certificate or certificate chain is not trusted.
You can load your own from a pem file before trying to connect by using:
int rc = SSL_CTX_load_verify_locations(ssl_context, file_name, NULL);
if (rc != 1) { // verify authentication result
g_warning("Load of certificates failed!: %s", X509_verify_cert_error_string(ERR_get_error()));
return FALSE;
}
Additionally you can load from memory directly.
With something like this:
char *chain_certs = "------- BEGIN CERTIFICAT...."; /// <<< YOUR CERTIFICATE CHAIN
// Load chain of certs
X509 *cacert=NULL;
BIO *mem = BIO_new_mem_buf(chain_certs,strlen(chain_certs));
X509_STORE *cert_store = SSL_CTX_get_cert_store(ssl_context);
if(cert_store!=NULL){
int index = 0;
while ((cacert = PEM_read_bio_X509(mem, NULL, 0, NULL))!=NULL) {
if(cacert) {
g_debug("Our certificate name is %s", cacert->name);
X509_STORE_add_cert(cert_store, cacert);
X509_free(cacert);
cacert=NULL;
} /* Free immediately */
index++;
}
}
BIO_free(mem);