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.
Related
I have this simple program:
int main ()
{
/* INITIALIZING OPENSSL */
SSL_library_init();
SSL_load_error_strings();
ERR_load_BIO_strings();
OpenSSL_add_all_algorithms();
BIO *bio;
connectServerSSL(bio);
login(bio);
}
And this functions:
void connectServerSSL (BIO *bio)
{
SSL_CTX * ctx = SSL_CTX_new(SSLv23_client_method());
SSL * ssl;
if(! SSL_CTX_load_verify_locations(ctx, NULL, "/etc/ssl/certs"))
{
callError(ERR_LOADCERT);
}
bio = BIO_new_ssl_connect(ctx);
BIO_get_ssl(bio, &ssl);
SSL_set_mode(ssl, SSL_MODE_AUTO_RETRY);
BIO_set_conn_hostname(bio, hostnamePort);
if(BIO_do_connect(bio) <= 0)
{
callError(ERR_CONNECTION);
}
if(SSL_get_verify_result(ssl) != X509_V_OK)
{
callError(ERR_VALIDCERT);
}
}
When I use this:
BIO_write(bio, request.c_str(), request.size())
In function connectServerSSL it works OK.
But when i want to use it in some other function:
void login (BIO *bio)
{
BIO_write(bio, request.c_str(), request.size());
}
I get Segmentation fault (core dumped).
In C and C++, even pointers are passed by value. So you need to either change the parameter of connectServerSSL to a BIO *& or else redefine it in the C-style of things:
void connectServerSSL (BIO ** bio_ptr)
{
SSL_CTX * ctx = SSL_CTX_new(SSLv23_client_method());
SSL * ssl;
if(! SSL_CTX_load_verify_locations(ctx, NULL, "/etc/ssl/certs"))
{
callError(ERR_LOADCERT);
}
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, hostnamePort);
if(BIO_do_connect(bio) <= 0)
{
callError(ERR_CONNECTION);
}
if(SSL_get_verify_result(ssl) != X509_V_OK)
{
callError(ERR_VALIDCERT);
}
*bio_ptr = bio;
}
// Example usage:
void example()
{
BIO * bio;
connectServerSSL(&bio);
BIO_write(bio, request.c_str(), request.size());
}
Your connectServerSSL() function has a parameter named bio, and it writes only to this temporary variable, not to the bio variable in main(), which is uninitialized.
Change the signature of connectServerSSL() to BIO* connectServerSSL(void) and call it with bio = connectServerSSL(). You could also call the function with a BIO** newbio argument, call it with &bio, and set *newbio, which will update the bio variable in main().
Some good habits to help avoid bugs like this: initialize your variables to default values, use assertions to check that your inputs are valid, and single-step through in a debugger. If you’d initialized the variable in main() to BIO* bio = NULL, or better yet, BIO* const bio = connectServerSSL(), then it would have been obvious in the debugger that it was still uninitialized on return.
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.
I am using openssl and zmq to write a server and a client.
My client and server need mutual authentication.
but after I set SSL_CTX_set_verify(ssl_ctx,SSL_VERIFY_FAIL_IF_NO_PEER_CERT,NULL) on server, the handshake always successes whether the client send the certificate or not.
In addition, SSL_get_peer_certificate(tls->get_ssl_()) return null and SSL_get_verify_result(tls->get_ssl_()) return 0 which means X509_V_OK.
I am really confused and desperate now. Any suggestions or corrections?
This is part of my code:
OpenSSL_add_all_algorithms();
SSL_library_init();
SSL_load_error_strings();
ERR_load_BIO_strings();
const SSL_METHOD *meth;
SSL_CTX *ssl_ctx;
//**************************part of client************************
{
meth = SSLv23_client_method();
ssl_ctx = SSL_CTX_new(meth);
SSL_CTX_set_verify(ssl_ctx,SSL_VERIFY_PEER,NULL);
int rc1 = SSL_CTX_load_verify_locations(ssl_ctx, ".\\demoCA\\private\\server_chain.pem",".\\demoCA\\private\\");///
SSL_CTX_set_default_passwd_cb_userdata(ssl_ctx,"pw");
std::string cert_chain(".\\demoCA\\private\\client_chain.pem");
std::string cert(".\\demoCA\\private\\client_crt.pem");
std::string key(".\\demoCA\\private\\client_key.pem");
int code = SSL_CTX_use_certificate_chain_file(ssl_ctx,cert_chain.c_str());
if (code != 1)
{
std::cout<<"error1\n";
//throw TLSException("failed to read credentials.");
}
code = SSL_CTX_use_PrivateKey_file(ssl_ctx,key.c_str(),SSL_FILETYPE_PEM);
i f (code != 1)
{
std::cout<<"error2\n";
//throw TLSException("failed to read credentials.");
}
if(!SSL_CTX_check_private_key(ssl_ctx))
{
std::cout<<"key wrong";
system("pause");
exit(0);
}
}
//*****************part of server****************************
{
meth = SSLv23_server_method();
ssl_ctx = SSL_CTX_new(meth);
SSL_CTX_set_verify(ssl_ctx,SSL_VERIFY_FAIL_IF_NO_PEER_CERT,NULL)
SSL_CTX_set_client_CA_list(ssl_ctx,SSL_load_client_CA_file(".\\demoCA\\private\\client_chain.pem"));//
SSL_CTX_set_default_passwd_cb_userdata(ssl_ctx,"pw");
std::string cert_chain(".\\demoCA\\private\\server_chain.pem");
std::string cert(".\\demoCA\\private\\server_crt.pem");
std::string key(".\\demoCA\\private\\server_key.pem");
int rc = SSL_CTX_use_certificate_file(ssl_ctx,cert.c_str(),SSL_FILETYPE_PEM);
if (rc!=1)
{
//throw TLSException("failed to read credentials.");
std::cout<<"error1\n";
}
rc = SSL_CTX_use_PrivateKey_file(ssl_ctx,key.c_str(),SSL_FILETYPE_PEM);
if (rc!=1)
{
//throw TLSException("failed to read credentials.");
std::cout<<"error2\n";
}
int rcode = SSL_CTX_check_private_key(ssl_ctx);
if(rcode!=1)
{
std::cout<<"key wrong";
system("pause");
//exit(0);
}
}
From the documentation of SSL_CTX_set_verify:
SSL_VERIFY_FAIL_IF_NO_PEER_CERT
Server mode: if the client did not return a certificate, the TLS/SSL handshake is immediately terminated with a "handshake failure" alert. This flag must be used together with SSL_VERIFY_PEER.
You did not use it together with SSL_VERIFY_PEER as described in the documentation and thus it has no effect.
Here is my procedure in OpenSSL Server Mode,
Initialization Part of SSL and BIO variables:
map<int, SSL> m_SSLMap;
map<int, BIO> m_BioWriteMap;
map<int, BIO> m_BioReadMap;
int InitializeServerNegotiationMode(int iFd)
{
SSL *pServSslFd;
BIO *pWb, *pRb;
pServSslFd = SSL_new(m_pCtx);
assert(pServSslFd);
if ( SSL_version(pServSslFd) == DTLS1_VERSION)
{
pWb = BIO_new(BIO_s_mem());
pRb = BIO_new(BIO_s_mem());
assert(pWb);
assert(pRb);
SSL_set_bio(pServSslFd, pRb, pWb);
SSL_set_accept_state(pServSslFd);
}
m_SSLMap[iFd] = *pServSslFd;
m_BioReadMap[iFd] = *pRb;
m_BioWriteMap[iFd] = *pWb;
return INITIALIZATION_SUCCESS;
}
Server Mode Negotiation Operations when DTLS data comes to the server:
int ServerModeDTLSNegotiation(int iChannel, const char *pBuff, const int iLen, int iFd)
{
SSL *pServSslFd;
BIO *pRbio;
BIO *pWbio;
pServSslFd = &m_SSLMap[iFd];
pRbio = &m_BioReadMap[iFd];
pWbio = &m_BioWriteMap[iFd];
char buff[4096];
memset(buff, 0, strlen(buff));
BIO_write(pRbio, pBuff, iLen);
if(!SSL_is_init_finished(pServSslFd))
{
int iRet = SSL_do_handshake(pServSslFd);
}
int iNewLen = BIO_read(pWbio, buff, 2048);
if(iNewLen>0)
{
char *pNewData = new char[iNewLen+1];
for(int i=0;i<iNewLen;i++)
pNewData[i] = buff[i];
m_pEventHandler->SendReply(iChannel, (unsigned char *)pNewData, iNewLen);
}
else
{
printf("[DTLS]:: HandShaking Response failed for this data,
return -1;
}
return NEGOTIATION_SUCCESS;
}
Here I am attaching Wireshark TCP-Dump for better monitoring about the issue.
https://www.dropbox.com/s/quidcs6gilnvt2o/WebRTC%20DTLS%20Handshake%20Failure.pcapng?dl=0
Now, I am confident about my initialization of SSL_CTX variable. Because, Sometimes Handshake successfully negotiate for every port. But sometimes Handshake fails for one or two port. I am working for 5 days to solve WebRTC DTLS Server Mode Negotiation for Google Chrome. But I haven't found the root cause for this problem.
The link for TCP-Dump is not working.
Anyway, it seems your solution should work.
As it's a server program, it's definitely multi threaded. But it's really dangerous to initialize SSL variables or to perform handshake procedure without locking. In that case so many things can happen if these two methods are processed by multiple thread.
My suggestion is to add locking mechanism for these methods.
I'm trying to make simple IMAP client using winsock/OpenSSL, is this possible without additional libraries? If so, how to send request and get responce? Server is responding only once,even without request,after that i'm sending request and application stuck at SSL_read(..) function,than timeout and BYE.How to make proper request?
bool RequestQueue(const char* serverName)
{
SOCKET hSocket = INVALID_SOCKET;
char receiveBuf[512];
ZeroMemory(receiveBuf,512);
char requestBuf[512];
ZeroMemory(requestBuf,512);
sockaddr_in sockAddr = {0};
bool bSuccess = true;
//SSL
SSL* ssl;
SSL_CTX* ctx;
try
{
//Look up hostname and fill sockaddr_in structure
cout<< "Looking up hostname "<<serverName<<"...";
FillSockAddr(&sockAddr,serverName,IMAP_SERVER_PORT);
cout<< "found.\n";
//creating socket
cout<<"Creating socket..";
if((hSocket = socket(AF_INET,SOCK_STREAM,IPPROTO_TCP)) == INVALID_SOCKET)
throw exception("could not create socket!");
cout<<"created.\n";
//Connect to server
cout<<"Attempting to connect to "<< inet_ntoa(sockAddr.sin_addr)
<<":"<<IMAP_SERVER_PORT<<" ...";
if(connect(hSocket,(sockaddr*)(&sockAddr),sizeof(sockAddr))!= 0)
throw exception("could not connect!");
cout<<"connected\n";
ctx = SSL_CTX_new(SSLv23_client_method());
if(!ctx)
throw exception("SSL_CTX_new error");
ssl = SSL_new(ctx);
SSL_CTX_free(ctx);
if(!ssl)
throw exception("ssl initializing error");
SSL_set_fd(ssl,hSocket);
if(SSL_connect(ssl) != 1)
throw exception("SSL_connect error");
int reqLen;
int retLen;
cout<<"==============\n";
cout<<"====begin=====\n";
cout<<"==============\n";
retLen = SSL_read(ssl,receiveBuf,sizeof(receiveBuf));
if(retLen<0)
throw exception("SSL_read error.");
cout<<"S: "<<receiveBuf;
strcpy(requestBuf,"a001 CAPABILITY");
reqLen = strlen(requestBuf);
SSL_write(ssl,requestBuf,reqLen);
cout<<"C: "<<requestBuf<<endl;
ZeroMemory(receiveBuf,sizeof(receiveBuf));
retLen = SSL_read(ssl,receiveBuf,sizeof(receiveBuf));
if(retLen<0)
throw exception("SSL_read error.");
cout<<"S: "<<receiveBuf;
}
catch(exception e)
{
cout<<"Error : "<<e.what()<<endl;
}
if(hSocket != INVALID_SOCKET)
{
SSL_shutdown(ssl);
closesocket(hSocket);
SSL_free(ssl);
}
return bSuccess;
}
I didn't analyze your code deeply, but I can see one obvious issue.
According to the RFC 3501:
All interactions transmitted by client and server are in the form of
lines, that is, strings that end with a CRLF. The protocol receiver
of an IMAP4rev1 client or server is either reading a line, or is
reading a sequence of octets with a known count followed by a line.
In your code you're not terminating the command with CRLF. Try replacing
strcpy(requestBuf,"a001 CAPABILITY");
with
strcpy(requestBuf,"a001 CAPABILITY\r\n");
Also, you'd better start without SSL and add it later - this will simplify debugging a lot (i.e. with Wireshark).