I'm having trouble with the openssl library. I'm calling the following function after sending EHLO and STARTTLS:
SSL_CTX *ctx = NULL;
SSL *ssl = NULL;
void CreateTLSSession(int sockfd)
{
printf("///////////////creating TLS Session/////////////////////\n");
SSL_library_init();
SSL_load_error_strings();
OpenSSL_add_all_algorithms();
ctx = SSL_CTX_new(SSLv23_client_method());
if (ctx == NULL)
{
printf("failed to initialize context\n");
return;
}
SSL_CTX_set_options(ctx, SSL_OP_NO_SSLv2);
ssl = SSL_new(ctx);
if (!SSL_set_fd(ssl, sockfd))
{
printf("failed to bind to socket fd\n");
return;
}
if (SSL_connect(ssl) < 1)
{
ERR_print_errors_fp(stdout);
fflush(stdout);
printf("SSL_connect failed\n");
return;
}
}
However, SSL_connect fails but does not print any errors!
Here's the output of my code: (including the replies of the server)
220 mx.google.com ESMTP x3sm39000551eep.17 - gsmtp
//////////////////////////EHLO//////////////////////////
250-mx.google.com at your service, [80.149.109.201]
250-SIZE 35882577
250-8BITMIME
250-STARTTLS
250-ENHANCEDSTATUSCODES
250 CHUNKING
//////////////////////////STARTTLS//////////////////////////
220 2.0.0 Ready to start TLS
///////////////creating TLS Session/////////////////////
SSL_connect failed
so ERR_print_errors_fp(stdout) is not doing anything!
Any idea why this is happening?
BTW, I'm trying to connect to smtp.gmail.com:587
I don't see a load of private key file or certificate file. use SSL_CTX_use_certificate_file and SSL_CTX_use_PrivateKey_file.
I managed to finally solve the problem. the underlying socket was non-blocking, so SSL_connect does not connect instantly. Using select solved the issue
Related
I am building a HTTPS service with Mongoose using using OPENSSL (openssl-1.0.2) on an embedded Linux. I tried it first using the following cipher list "AES128-SHA256:AES256-SHA256:AES128-GCM-SHA256:AES256-GCM-SHA384", and it works fine. However, when I try to use only "ECDHE-RSA-AES128-GCM-SHA256", the compilation still works, but when attempting actual HTTPS connection between server and client (Chrome on Windows 10), the connection always failed at handshake.
Here's the openssl supported cipher list:
# openssl ciphers | sed 's/:/\n/g'
ECDHE-RSA-AES256-GCM-SHA384
ECDHE-ECDSA-AES256-GCM-SHA384
ECDHE-RSA-AES256-SHA384
ECDHE-ECDSA-AES256-SHA384
ECDHE-RSA-AES256-SHA
ECDHE-ECDSA-AES256-SHA
...
ECDH-RSA-AES256-GCM-SHA384
ECDH-ECDSA-AES256-GCM-SHA384
ECDH-RSA-AES256-SHA384
ECDH-ECDSA-AES256-SHA384
ECDH-RSA-AES256-SHA
ECDH-ECDSA-AES256-SHA
ECDHE-RSA-AES128-GCM-SHA256
ECDHE-ECDSA-AES128-GCM-SHA256
ECDHE-RSA-AES128-SHA256
ECDHE-ECDSA-AES128-SHA256
ECDHE-RSA-AES128-SHA
ECDHE-ECDSA-AES128-SHA
ECDHE-RSA-DES-CBC3-SHA
ECDHE-ECDSA-DES-CBC3-SHA
...
#
According to the packet capture between server and client, the client does support ECDHE-RSA-AES128-GCM-SHA256, so logically the handshake should work. The certificate is signed using RSA Key.
Here's my code for setting up SSL connection.
const char *pem = ctx->config[SSL_CERTIFICATE];
const char *chain = ctx->config[SSL_CHAIN_FILE];
if (!load_dll(ctx, SSL_LIB, ssl_sw) || !load_dll(ctx, CRYPTO_LIB, crypto_sw))
return 0;
// Initialize SSL crap
SSL_library_init();
SSL_load_error_strings();
**if ((CTX = SSL_CTX_new(TLSv1_2_server_method())) == NULL)
printf("SSL_CTX_new error: %s", ssl_error());**
/** Set cipher list */
//char cipher[128]="AES128-SHA256:AES256-SHA256:AES128-GCM-SHA256:AES256-GCM-SHA384";
char cipher[128]="ECDHE-RSA-AES128-GCM-SHA256";
if (SSL_CTX_set_cipher_list(CTX, cipher) <= 0) {
printf("Failed setting the cipher list.\n");
return 0;
}
if (CTX != NULL && SSL_CTX_use_certificate_file(CTX, pem, SSL_FILETYPE_PEM) == 0) {
printf("%s: cannot open %s: %s", __func__, pem, ssl_error());
return 0;
}else if (CTX != NULL && SSL_CTX_use_PrivateKey_file(CTX, pem, SSL_FILETYPE_PEM) == 0) {
printf("%s: cannot open %s: %s", NULL, pem, ssl_error());
return 0;
}
if (CTX != NULL && chain != NULL && SSL_CTX_use_certificate_chain_file(CTX, chain) == 0) {
printf("%s: cannot open %s: %s", NULL, chain, ssl_error());
return 0;
}
All I did are,
Changing the method from SSLv23_server_method() to TLSv1_2_server_method(),
Changing the cipher list from "AES128-SHA256:AES256-SHA256:AES128-GCM-SHA256:AES256-GCM-SHA384" to "ECDHE-RSA-AES128-GCM-SHA256"
Any help would be helpful, thanks.
I am preparing a sample C application to stream file from one remote(FTP) location to another remote location(HTTP). In the application I want to download file using curl API & uploading the downloaded content using ssl API. I am getting segmentation-fault in SSL_connect(), Here is the init code,
SSL* ssl = NULL;//Global variable
SSL_CTX* ssl_ctx = NULL;//Global variable
/* initialize OpenSSL first */
SSL_library_init();
SSL_load_error_strings();
do
{
ssl_ctx = SSL_CTX_new(SSLv23_client_method());
if (!ssl_ctx)
{
fprintf(stderr, "Failed to SSL_CTX_new\n");
break;
}
SSL_CTX_set_verify(ssl_ctx, SSL_VERIFY_NONE, NULL);
ssl = SSL_new(ssl_ctx);
if (!ssl)
{
fprintf(stderr, "Failed to SSL_new\n");
break;
}
if (SSL_set_fd(ssl, sock) != 1)
{
fprintf(stderr, "Failed to SSL_set_fd\n");
break;
}
int err = SSL_connect(ssl);
if (err != 1)
{
fprintf(stderr, "Failed to SSL_connect\n");
break;
}
return_code = 0;
} while(0);
Here 'sock' is open socket connected with remote opened before this call.
I verified pointers passing & type-casting, Looks good to me.
Can anyone suggest any break-through?
Thanks in advance.
Try running gdb and set up a breakpoint at the line int err = SSL_connect(ssl);, check if ss1 is correctly initialized. Here is a simple tutorial on how to use gdb: https://cseweb.ucsd.edu/classes/fa09/cse141/tutorial_gcc_gdb.html
I am new to SSL socket programming and my first task is to get the SSL server client to be working and understanding.
I have obtained the server and client sources floating on the web and compiled them against my openssl library (compiled from source).
When I start the server I am able to create a normal clientfd using accept system call, however SSL_accept fails.
clientsocketfd = accept(serversocketfd, NULL, 0);
serverssl = SSL_new(ssl_server_ctx);
if(!serverssl)
{
printf("Error SSL_new\n");
return -1;
}
SSL_set_fd(serverssl, clientsocketfd);
if((ret = SSL_accept(serverssl))!= 1)
{
printf("Handshake Error %d\n", SSL_get_error(serverssl, ret));
return -1;
}
SSL_accept fails printing Handshake error 1.
Why is SSL_accept failing with error 1?
Server source:
/*---------------------------------------------------------------------*/
/*--- main - create SSL socket server. ---*/
/*---------------------------------------------------------------------*/
int main(int count, char *strings[])
{ SSL_CTX *ctx;
int server;
char *portnum;
if ( count != 2 )
{
printf("Usage: %s <portnum>\n", strings[0]);
exit(0);
}
portnum = strings[1];
SSL_library_init();
ctx = InitServerCTX(); /* initialize SSL */
LoadCertificates(ctx, "newreq.pem", "newreq.pem"); /* load certs */
server = OpenListener(atoi(portnum)); /* create server socket */
while (1)
{ struct sockaddr_in addr;
int len = sizeof(addr);
SSL *ssl;
int client = accept(server, (struct sockaddr*)&addr, &len); /* accept connection as usual */
printf("Connection: %s:%d\n",
inet_ntoa(addr.sin_addr), ntohs(addr.sin_port));
ssl = SSL_new(ctx); /* get new SSL state with context */
SSL_set_fd(ssl, client); /* set connection socket to SSL state */
Servlet(ssl); /* service connection */
}
close(server); /* close server socket */
SSL_CTX_free(ctx); /* release context */
}
/*---------------------------------------------------------------------*/
/*--- InitServerCTX - initialize SSL server and create context ---*/
/*---------------------------------------------------------------------*/
SSL_CTX* InitServerCTX(void)
{ SSL_METHOD *method;
SSL_CTX *ctx;
OpenSSL_add_all_algorithms(); /* load & register all cryptos, etc. */
SSL_load_error_strings(); /* load all error messages */
method = SSLv3_server_method(); /* create new server-method instance */
ctx = SSL_CTX_new(method); /* create new context from method */
if ( ctx == NULL )
{
ERR_print_errors_fp(stderr);
abort();
}
return ctx;
}
The OpenSSL version and compilation options are as folows:
/usr/bin/openssl version -a
OpenSSL 1.0.2g 1 Mar 2016
built on: reproducible build, date unspecified
platform: debian-amd64
options: bn(64,64) rc4(16x,int) des(idx,cisc,16,int) blowfish(idx)
compiler: cc -I. -I.. -I../include -fPIC -DOPENSSL_PIC -DOPENSSL_THREADS -D_REENTRANT
-DDSO_DLFCN -DHAVE_DLFCN_H -m64 -DL_ENDIAN -g -O2 -fstack-protector-strong
-Wformat -Werror=format-security -Wdate-time -D_FORTIFY_SOURCE=2 -Wl,
-Bsymbolic-functions -Wl,-z,relro -Wa,--noexecstack -Wall -DMD32_REG_T=int
-DOPENSSL_IA32_SSE2 -DOPENSSL_BN_ASM_MONT
-DOPENSSL_BN_ASM_MONT5 -DOPENSSL_BN_ASM_GF2m -DSHA1_ASM -DSHA256_ASM
-DSHA512_ASM -DMD5_ASM -DAES_ASM -DVPAES_ASM -DBSAES_ASM
-DWHIRLPOOL_ASM -DGHASH_ASM -DECP_NISTZ256_ASM
OPENSSLDIR: "/usr/lib/ssl"
Apparently, SSLv3 has been disabled in packages distributed with some Linux distros (e.g. Ubuntu 16.04):
openssl (1.0.2g-1ubuntu1) xenial; urgency=medium
Merge with Debian, remaining changes.
Disable SSLv3 without changing ABI:
debian/patches/no-sslv3.patch: Disable SSLv3 without using the
no-ssl3-method option
debian/rules: don't use no-ssl3-method, don't bump soname
debian/patches/engines-path.patch: don't bump soname
debian/patches/version-script.patch: don't bump soname
debian/patches/soname.patch: removed
debian/lib*: don't bump soname
Therefore you need to use another method:
method = SSLv3_server_method();
The man page for SSL_accept states:
If the underlying BIO is non-blocking, SSL_accept() will also return
when the underlying BIO could not satisfy the needs of SSL_accept() to
continue the handshake, indicating the problem by the return value -1.
In this case a call to SSL_get_error() with the return value of
SSL_accept() will yield SSL_ERROR_WANT_READ or SSL_ERROR_WANT_WRITE.
The calling process then must repeat the call after taking appropriate
action to satisfy the needs of SSL_accept()...
I believe that means your code should look something like:
while (1)
{
int acc = SSL_accept(ssl);
if (acc == 0)
{
/* Hard error */
exit(-1);
}
else if (acc == -1)
{
int err = SSL_get_error(ssl, ret);
if (err == SSL_ERROR_WANT_READ)
{
/* Wait for data to be read */
}
else if (err == SSL_ERROR_WANT_WRITE)
{
/* Write data to continue */
}
else if (err == SSL_ERROR_SYSCALL || err == SSL_ERROR_SSL)
{
/* Hard error */
exit(-1);
}
else if (err == SSL_ERROR_ZERO_RETURN)
{
/* Same as error */
exit(-1);
}
}
else
{
/* Continue */
break;
}
}
OpenSSL has sample code in <openssl src>/apps/s_client.c and <openssl src>/apps/s_server.c
I have a client and a server attempting to mutually authenticate one another and initiate a TLS connection. The certificates I'm using right now are self-signed.
In the server code I have SSL_VERIFY_FAIL_IF_NO_PEER_CERT set. The handshake succeeds, but SSL_get_peer_certificate returns a NULL pointer on the server side. If the client didn't return a certificate, why did the handshake not fail?
If I comment out the SSL_get_peer_certificate check on the server side, the client and server do connect and are able to communicate, but it isn't a TLS connection. When I watch them exchange packets over wireshark, I only see TCP traffic.
Server code:
BIO *acceptTLSConnection(char *port) {
BIO *sbio, *bbio, *acpt = NULL;
SSL_CTX *ctx = NULL;
SSL *ssl = NULL;
SSL_library_init();
ctx = SSL_CTX_new(TLSv1_server_method());
SSL_CTX_set_verify(ctx, SSL_VERIFY_PEER|SSL_VERIFY_FAIL_IF_NO_PEER_CERT, NULL);
if(!SSL_CTX_use_certificate_file(ctx,"servercert.pem",SSL_FILETYPE_PEM)
|| !SSL_CTX_use_PrivateKey_file(ctx,"serverkey.pem",SSL_FILETYPE_PEM)
|| !SSL_CTX_check_private_key(ctx)) {
ERR_print_errors_fp(stderr);
fatalError("Error setting up SSL_CTX.");
}
if(!SSL_CTX_load_verify_locations(ctx, "clientcert.pem", NULL))
fatalError("Could not load trusted CA certificates.");
sbio=BIO_new_ssl(ctx,0);
BIO_get_ssl(sbio, &ssl);
if(!ssl) {
fatalError("Can't locate BIO SSL pointer.");
}
SSL_set_mode(ssl, SSL_MODE_AUTO_RETRY);
bbio = BIO_new(BIO_f_buffer());
sbio = BIO_push(bbio, sbio);
acpt=BIO_new_accept(port);
BIO_set_accept_bios(acpt,sbio);
/* Setup accept BIO */
if(BIO_do_accept(acpt) <= 0) {
ERR_print_errors_fp(stderr);
fatalError("Error in setting up accept BIO");
}
/* Now wait for incoming connection */
if(BIO_do_accept(acpt) <= 0) {
ERR_print_errors_fp(stderr);
fatalError("Error in connection");
}
sbio = BIO_pop(acpt);
BIO_free_all(acpt);
if(BIO_do_handshake(sbio) <= 0) {
ERR_print_errors_fp(stderr);
fatalError("Error in SSL handshake");
}
/* Verify a client certificate was presented during the negotiation */
X509* cert = SSL_get_peer_certificate(ssl);
if(cert) { X509_free(cert); } /* Free immediately */
if(NULL == cert) fatalError("Client did not present a cert during handshake.");
/* Verify the result of chain verification */
int res = SSL_get_verify_result(ssl);
if(!(X509_V_OK == res)) fatalError("Cert presented by client couldn't be verified.");
return sbio;
}
Client code:
BIO *makeTLSConnection(char *servIP, char *servPort) {
char *servLoc = calloc(strlen(servIP) + strlen(servPort) + 2, sizeof(char));
strcat(servLoc, servIP);
strcat(servLoc, ":");
strcat(servLoc, servPort);
BIO *sbio = NULL;
SSL_CTX *ctx = NULL;
SSL *ssl = NULL;
SSL_library_init();
ctx = SSL_CTX_new(TLSv1_client_method());
SSL_CTX_set_verify(ctx, SSL_VERIFY_PEER, NULL);
if(!SSL_CTX_use_certificate_file(ctx,"clientcert.pem",SSL_FILETYPE_PEM)
|| !SSL_CTX_use_PrivateKey_file(ctx,"clientkey.pem",SSL_FILETYPE_PEM)
|| !SSL_CTX_check_private_key(ctx)) {
ERR_print_errors_fp(stderr);
fatalError("Error setting up SSL_CTX.");
}
if(!SSL_CTX_load_verify_locations(ctx, "servercert.pem", NULL))
fatalError("Could not load trusted CA certificates.");
sbio = BIO_new_ssl_connect(ctx);
BIO_get_ssl(sbio, &ssl);
if(!ssl) {
fatalError("Can't locate SSL pointer.");
}
BIO_set_conn_hostname(sbio, servLoc);
if(BIO_do_connect(sbio) <= 0) {
ERR_print_errors_fp(stderr);
fatalError("Error connecting to server.");
}
if(BIO_do_handshake(sbio) <= 0) {
ERR_print_errors_fp(stderr);
fatalError("Error establishing SSL connection.");
}
/* Verify a server certificate was presented during the negotiation */
X509* cert = SSL_get_peer_certificate(ssl);
if(cert) { X509_free(cert); } /* Free immediately */
if(NULL == cert) fatalError("Server did not present a cert during handshake.");
/* Verify the result of chain verification */
int res = SSL_get_verify_result(ssl);
if(!(X509_V_OK == res)) fatalError("Cert presented by server couldn't be verified.");
return sbio;
}
It looks like you are missing the call to SSL_CTX_use_certificate_chain_file and SSL_CTX_set_client_CA_list on the server. I believe SSL_CTX_set_client_CA_list triggers the machinery to perform client authentication (i.e., it elicits the ClientCertificate message from the RFC in section 7.4.6 during the exchange).
If SSL_CTX_set_client_CA_list causes the failure you are looking for, then I'm inclined to believe its a bug in the OpenSSL library. You should get a failure when specifying SSL_VERIFY_PEER and SSL_VERIFY_FAIL_IF_NO_PEER_CERT at the server because that's what the docs say.
Also see Is verification supposed to fail with SSL_VERIFY_PEER | SSL_VERIFY_FAIL_IF_NO_PEER_CERT without SSL_CTX_set_client_CA_list on the OpenSSL Users mailing list. Its in response to this question.
Also see Testing SSL/TLS Client Authentication with OpenSSL and OpenSSL client not sending client certificate.
libcurl has timeout options like these:
CURLOPT_CONNECTTIMEOUT - maximum time in seconds that you allow the connection to the server to take.
CURLOPT_TIMEOUT - maximum time in seconds that you allow the libcurl transfer operation to take.
I'd like to implement a similar timeout mechanism in OpenSSL.
What changes would be required in the code below so that a timeout value is applied to BIO_do_connect(), BIO_write() and BIO_read()?
I'm connecting to a server and sending/receiving data to/from the server using BIO_write()/BIO_read() that OpenSSL provides. My code is based on the following sample code available from here.
int main()
{
BIO * bio;
SSL * ssl;
SSL_CTX * ctx;
int p;
char * request = "GET / HTTP/1.1\x0D\x0AHost: www.verisign.com\x0D\x0A\x43onnection: Close\x0D\x0A\x0D\x0A";
char r[1024];
/* Set up the library */
ERR_load_BIO_strings();
SSL_load_error_strings();
OpenSSL_add_all_algorithms();
/* Set up the SSL context */
ctx = SSL_CTX_new(SSLv23_client_method());
/* Load the trust store */
if(! SSL_CTX_load_verify_locations(ctx, "TrustStore.pem", NULL))
{
fprintf(stderr, "Error loading trust store\n");
ERR_print_errors_fp(stderr);
SSL_CTX_free(ctx);
return 0;
}
/* Setup the connection */
bio = BIO_new_ssl_connect(ctx);
/* Set the SSL_MODE_AUTO_RETRY flag */
BIO_get_ssl(bio, & ssl);
SSL_set_mode(ssl, SSL_MODE_AUTO_RETRY);
/* Create and setup the connection */
BIO_set_conn_hostname(bio, "www.verisign.com:https");
if(BIO_do_connect(bio) <= 0)
{
fprintf(stderr, "Error attempting to connect\n");
ERR_print_errors_fp(stderr);
BIO_free_all(bio);
SSL_CTX_free(ctx);
return 0;
}
/* Check the certificate */
if(SSL_get_verify_result(ssl) != X509_V_OK)
{
fprintf(stderr, "Certificate verification error: %i\n", SSL_get_verify_result(ssl));
BIO_free_all(bio);
SSL_CTX_free(ctx);
return 0;
}
/* Send the request */
BIO_write(bio, request, strlen(request));
/* Read in the response */
for(;;)
{
p = BIO_read(bio, r, 1023);
if(p <= 0) break;
r[p] = 0;
printf("%s", r);
}
/* Close the connection and free the context */
BIO_free_all(bio);
SSL_CTX_free(ctx);
return 0;
}
I'm cross-compiling for ARM on Ubuntu (Eclipse with CodeSourcery Lite).
I ended up doing something like the following (pseudocode):
int nRet;
int fdSocket;
fd_set connectionfds;
struct timeval timeout;
BIO_set_nbio(pBio, 1);
nRet = BIO_do_connect(pBio);
if ((nRet <= 0) && !BIO_should_retry(pBio))
// failed to establish connection.
if (BIO_get_fd(pBio, &fdSocket) < 0)
// failed to get fd.
if (nRet <= 0)
{
FD_ZERO(&connectionfds);
FD_SET(fdSocket, &connectionfds);
timeout.tv_usec = 0;
timeout.tv_sec = 10;
nRet = select(fdSocket + 1, NULL, &connectionfds, NULL, &timeout);
if (nRet == 0)
// timeout has occurred.
}
You can use the same approach for BIO_read() too.
You might find this link useful.
For connecting, #jpen gave the best answer there. You have to mark the BIO as non-blocking and use select for determining whether it connected and/or timed out.
Reads are a little different. Because OpenSSL may buffer decrypted data (depending on the TLS cipher suite used), select may timeout when you are trying to read - even if data actually is available. The proper way to handle read timeouts is to first check SSL_pending or BIO_pending. If the pending function returns zero, then use select to set a timeout. If the pending function returns greater than zero, then just call SSL_read or BIO_read or any other read function.
Take a look at SSL_CTX_set_timeout () function, which does similar to libcurl's CURLOPT_TIMEOUT variable:
From http://www.openssl.org/docs/ssl/SSL_CTX_set_timeout.html :
SSL_CTX_set_timeout() sets the timeout for newly created sessions for ctx to t. The timeout value t must be given in seconds.
In your case you could add the following line after you create ctx object:
SSL_CTX_set_timeout (ctx, 60);
Hope it helps !