I use the below server.c source, i generated
sinful-host-cert.pem
sinful-host.key
as described here: Elliptic Curve CA Guide
When running the program get the following errors:
140722397161136:error:10071065:elliptic curve routines:func(113):reason(101):ec_lib.c:995:
140722397161136:error:0B080075:x509 certificate routines:func(128):reason(117):x509_cmp.c:346:
I compiled using:
gcc server.c -ldl -lcrypto -lssl -o Server
The error occurs at this line I think
if (SSL_CTX_use_PrivateKey_file(ctx, KeyFile, SSL_FILETYPE_PEM) <= 0)
server.c
#include <errno.h>
#include <unistd.h>
#include <malloc.h>
#include <string.h>
#include <arpa/inet.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <resolv.h>
#include "openssl/ssl.h"
#include "openssl/err.h"
#define FAIL -1
int OpenListener(int port)
{ int sd;
struct sockaddr_in addr;
sd = socket(PF_INET, SOCK_STREAM, 0);
bzero(&addr, sizeof(addr));
inet_aton("10.8.0.26", &addr.sin_addr);
addr.sin_family = AF_INET;
addr.sin_port = htons(port);
//addr.sin_addr.s_addr = INADDR_ANY;
if ( bind(sd, (struct sockaddr*)&addr, sizeof(addr)) != 0 )
{
perror("can't bind port");
abort();
}
if ( listen(sd, 10) != 0 )
{
perror("Can't configure listening port");
abort();
}
return sd;
}
SSL_CTX* InitServerCTX(void)
{ const SSL_METHOD *method;
SSL_CTX *ctx;
OpenSSL_add_all_algorithms(); /* load & register all cryptos, etc. */
SSL_load_error_strings(); /* load all error messages */
//method = SSLv23_server_method();
method = TLSv1_2_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;
}
void LoadCertificates(SSL_CTX* ctx, char* CertFile, char* KeyFile)
{
//New lines
if (SSL_CTX_set_cipher_list(ctx, "ECDHE-ECDSA-AES128-GCM-SHA256") != 1)
ERR_print_errors_fp(stderr);
if (SSL_CTX_load_verify_locations(ctx, CertFile, KeyFile) != 1)
ERR_print_errors_fp(stderr);
if (SSL_CTX_set_default_verify_paths(ctx) != 1)
ERR_print_errors_fp(stderr);
//End new lines
/* set the local certificate from CertFile */
if (SSL_CTX_use_certificate_file(ctx, CertFile, SSL_FILETYPE_PEM) <= 0)
{
ERR_print_errors_fp(stderr);
abort();
}
printf("FFFF\n");
/* set the private key from KeyFile (may be the same as CertFile) */
if (SSL_CTX_use_PrivateKey_file(ctx, KeyFile, SSL_FILETYPE_PEM) <= 0)
{
ERR_print_errors_fp(stderr);
abort();
}
printf("GGGG\n");
/* verify private key */
if (!SSL_CTX_check_private_key(ctx))
{
fprintf(stderr, "Private key does not match the public certificate\n");
abort();
}
//New lines - Force the client-side have a certificate
//SSL_CTX_set_verify(ctx, SSL_VERIFY_PEER | SSL_VERIFY_FAIL_IF_NO_PEER_CERT, NULL);
//SSL_CTX_set_verify_depth(ctx, 4);
//End new lines
}
void ShowCerts(SSL* ssl)
{ X509 *cert;
char *line;
cert = SSL_get_peer_certificate(ssl); /* Get certificates (if available) */
if ( cert != NULL )
{
printf("Server certificates:\n");
line = X509_NAME_oneline(X509_get_subject_name(cert), 0, 0);
printf("Subject: %s\n", line);
free(line);
line = X509_NAME_oneline(X509_get_issuer_name(cert), 0, 0);
printf("Issuer: %s\n", line);
free(line);
X509_free(cert);
}
else
printf("No certificates.\n");
}
void Servlet(SSL* ssl) /* Serve the connection -- threadable */
{ char buf[1024];
char reply[1024];
int sd, bytes, err;
const char* HTMLecho="<html><body><pre>%s</pre></body></html>\n\n";
printf("huhupre\n");
err = SSL_accept(ssl);
if ( err <= 0 ) { /* do SSL-protocol accept */
printf("%d\n",err);
ERR_print_errors_fp(stderr);
}
else
{
printf("XXXXXX\n");
//SSL_write(ssl, "huhu\n\r", 8);
ShowCerts(ssl); /* get any certificates */
bytes = SSL_read(ssl, buf, sizeof(buf)); /* get request */
if ( bytes > 0 )
{
buf[bytes] = 0;
printf("Client msg: \"%s\"\n", buf);
sprintf(reply, HTMLecho, buf); /* construct reply */
SSL_write(ssl, reply, strlen(reply)); /* send reply */
}
else
ERR_print_errors_fp(stderr);
}
sd = SSL_get_fd(ssl); /* get socket connection */
SSL_free(ssl); /* release SSL state */
close(sd); /* close connection */
}
int main()
{ SSL_CTX *ctx;
int server;
char portnum[]="5000";
char CertFile[] = "sinful-host-cert.pem";
char KeyFile[] = "sinful-host.key";
SSL_library_init();
ctx = InitServerCTX(); /* initialize SSL */
LoadCertificates(ctx, CertFile, KeyFile); /* load certs */
server = OpenListener(atoi(portnum)); /* create server socket */
printf("%d while\n", server);
while (1)
{ struct sockaddr_in addr;
socklen_t 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 */
if (ssl == NULL) {
ERR_print_errors_fp(stderr);
return 0;
}
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 */
}
as described here: Elliptic Curve CA Guide...
This page has so many errors and omissions I would discard it. The first red flag is the white text and black background. That tells me someone less experienced is providing the page...
From the page:
openssl ecparam -list-curves
This should be -list_curves, not -list-curves.
From the page:
openssl ecparam -out sinful.key -name sect283k1 -genkey
This should be:
openssl ecparam -param_enc named_curve -out sinful.key -name sect283k1 -genkey
If you don't use a named curve, then you will have lots of problems later, like when a client attempts to connect to the server. Here, named curve is the OID for a curve like secp256k1, and not the domain parameters like p, a, b, G, etc.
The "lots of problems later" is documented at the OpenSSL wiki Elliptic Curve Cryptography, Named Curves. Here are some of the problems you will experience:
Client: 139925962778272:error:14094410:SSL routines:SSL3_READ_BYTES:sslv3 alert handshake failure:s3_pkt.c:1256:SSL alert number 40
Client: 139925962778272:error:1409E0E5:SSL routines:SSL3_WRITE_BYTES:ssl handshake failure:s3_pkt.c:596
Server: 140339533272744:error:1408A0C1:SSL routines:SSL3_GET_CLIENT_HELLO:no shared cipher:s3_srvr.c:1353
Also, for maximum interoperability, you should use secp256k1. A close second is secp521r1.
Also, use of/lack of -*form in the openssl ecparam and openssl req commands are discussed below.
SSL_CTX* InitServerCTX(void) { ... }
This code block has quite a few problems. The most notable is lack of the ECDH callback. Where are you setting the SSL_CTX_set_tmp_ecdh callback (OpenSSL 1.0.1 and below), or where is the call to SSL_CTX_set_ecdh_auto (OpenSSL 1.0.2 and above)?
Others include the default protocol, the default cipher list, weak and wounded ciphers, the inclusion of anonymous protocols, and compression. For a partial example of code to provide a server context, see 'No Shared Cipher' Error with EDH-RSA-DES-CBC3-SHA.
The error occurs at this line I think
if (SSL_CTX_use_PrivateKey_file(ctx, KeyFile, SSL_FILETYPE_PEM) <= 0)
I think that traces back to that defective page you referenced. This:
openssl req -x509 -new -key sinful.key -out sinful-ca.pem -outform PEM -days 3650
Should probably be (note the addition of -keyform)
openssl req -x509 -new -key sinful.key -keyform PEM -out sinful-ca.pem -outform PEM -days 3650
Or
if (SSL_CTX_use_PrivateKey_file(ctx, KeyFile, SSL_FILETYPE_ASN1) <= 0)
In general, always use the *form option for a command, whether its -keyform, -certform, -inform, -outform, etc. OpenSSL does not always get it right (even though its supposed to use PEM by default).
The error occurs at this line I think
if (SSL_CTX_use_PrivateKey_file(ctx, KeyFile, SSL_FILETYPE_PEM) <= 0)
If the private key has a password, then you will need to provide a Password Callback or strip the password from the file.
Its OK to strip the password because there's no difference in storing a plaintext private key; or a encrypted private key with the passphrase in a configuration file next to the key. In both cases, the only effective security you have is the filesystem ACLs.
Related, this is known as the Unattended Key Storage problem. Guttman discusses it in his book Engineering Security. Its a problem without a solution.
Here's some more complete error information... It looks like you are using an old version of OpenSSL, and that does not provide the newer error codes.
When running the program get the following errors:
140722397161136:error:10071065:elliptic curve routines:func(113):reason(101):ec_lib.c:995
140722397161136:error:0B080075:x509 certificate routines:func(128):reason(117):x509_cmp.c:346
First, the 0x10071065 error:
$ /usr/local/ssl/macosx-x64/bin/openssl errstr 0x10071065
error:10071065:elliptic curve routines:EC_POINT_cmp:incompatible objects
The 0x10071065 usually means the client and the server are using incompatible EC fields. In this case, you should use either secp256k1 or secp521r1.
Second, the 0x0B080075 error:
$ /usr/local/ssl/macosx-x64/bin/openssl errstr 0x0B080075
error:0B080075:x509 certificate routines:X509_check_private_key:unknown key type
I'm guessing that there's a mismatch in the certificate and private key. But its only a guess. I would (1) clear the named curve issue, (2) clear the sect283k1 issue, and (3) clear the down level library issue (see below). After clearing those issues, then see if this issue remains.
It looks like you are using an old version of OpenSSL, and that does not provide the newer error codes...
Be sure you are running OpenSSL 1.0.0 or above. 0.9.8 had limited EC support, but it was not really cut-in in force until 1.0.0. Better, use OpenSSL 1.0.2.
OpenSSL_add_all_algorithms(); /* load & register all cryptos, etc. */
SSL_load_error_strings(); /* load all error messages */
Also see Library Initialization on the OpenSSL wiki.
if (SSL_CTX_set_cipher_list(ctx, "ECDHE-ECDSA-AES128-GCM-SHA256")
This will get you into trouble on some versions of OS X and iOS due to a bug in the SecureTransport library. Apple only fixed it on some versions of their operating systems.
If you plan on servicing Apple hardwarez, then you will need one additional non-ECDHE-ECDSA cipher. And you need to use the server side context option SSL_OP_SAFARI_ECDHE_ECDSA_BUG.
Related, Apple is pretty bold about not fixing their security bugs. You have the broken ECDHE-ECDSA cipher suites; and gems like CVE-2015-1130 (Hidden Backdoor with Root).
Here's what my ECDH callback looks like in OpenSSL 1.0.1 and below. OpenSSL 1.0.2 should use SSL_CTX_set_ecdh_auto. Its C++ code, but its easy enough to convert back to C code. Also see SL_CTX_set_tmp_ecdh_callback semantics in 1.0.1 on the OpenSSL mailing list.
The code below could be more robust. The callback should fetch the certificate with SSL_get_certificate (not SSL_get_peer_certificate), query the certificate for the EC field, and then provide a temporary key in the appropriate field, like secp256k1 or secp571k1. (It works because my certificates use secp256, and EcdhCallback uses secp256 as its default).
SSL_get_certificate is not documented. But it is used in <openssl src>/apps/s_cb.c. That's the "self documenting" code OpenSSL is famous for.
using SSL_ptr = std::shared_ptr<SSL>;
using SSL_CTX_ptr = std::shared_ptr<SSL_CTX>;
using EC_KEY_ptr = std::unique_ptr<EC_KEY, decltype(&::EC_KEY_free)>;
using EC_GROUP_ptr = std::unique_ptr<EC_GROUP, decltype(&::EC_GROUP_free)>;
using EC_POINT_ptr = std::unique_ptr<EC_POINT, decltype(&::EC_POINT_free)>;
using EVP_PKEY_ptr = std::unique_ptr<EVP_PKEY, decltype(&::EVP_PKEY_free)>;
using BIO_MEM_ptr = std::unique_ptr<BIO, decltype(&::BIO_free)>;
using BIO_FILE_ptr = std::unique_ptr<BIO, decltype(&::BIO_free)>;
...
SSL_CTX* CreateServerContext(const string & domain)
{
const SSL_METHOD* method = SSLv23_server_method();
ASSERT(method != NULL);
SSL_CTX_ptr t(SSL_CTX_new(method), ::SSL_CTX_free);
ASSERT(t.get() != NULL);
long flags = SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3;
flags |= SSL_OP_NO_COMPRESSION;
flags |= SSL_OP_SAFARI_ECDHE_ECDSA_BUG;
flags |= SSL_OP_CIPHER_SERVER_PREFERENCE;
SSL_CTX_set_options(t.get(), flags);
string ciphers = "HIGH:!aNULL:!RC4:!MD5";
rc = SSL_CTX_set_cipher_list(t.get(), ciphers.c_str());
...
LogDebug("GetServerContext: setting ECDH callback");
SSL_CTX_set_tmp_ecdh_callback(t.get(), EcdhCallback);
...
return t.release();
}
EC_KEY* EcdhCallback(SSL *ssl, int is_export, int keylength)
{
UNUSED(ssl);
UNUSED(is_export);
UNUSED(keylength);
/* This callback is OK, but OpenSSL calls it in a broken fashion. */
/* With 1.0.1e and 1.0.1f, the value is 1024-bits. That is more */
/* appropriate for RSA.... We'll try and rewrite it here. */
if (keylength >= 1024)
{
keylength = 256;
LogRelevant("EcdhCallback: field size is wrong, using 256-bit group");
}
#if defined(ALLOW_ECDH_192_PARAMS)
if (keylength <= 192 + 4)
return ECDH192();
#endif
if (keylength <= 224 + 4)
return ECDH224();
else if (keylength <= 256 + 4)
return ECDH256();
else if (keylength <= 384 + 4)
return ECDH384();
else if (keylength <= 521 + 4)
return ECDH521();
return ECDH521();
}
#if defined(ALLOW_ECDH_192_PARAMS)
static EC_KEY* ECDH192()
{
static EC_KEY_ptr key(NULL, NULL);
static once_flag flag;
call_once(flag, []()
{
key = EC_KEY_ptr(InitEcdhkey(192), ::EC_KEY_free);
ASSERT(key.get());
if(!key.get())
LogError("ECDH192: InitEcdhkey failed");
});
return key.get();
}
#endif
static EC_KEY* ECDH224()
{
static EC_KEY_ptr key(NULL, NULL);
static once_flag flag;
call_once(flag, []()
{
key = EC_KEY_ptr(InitEcdhkey(224), ::EC_KEY_free);
ASSERT(key.get());
if(!key.get())
LogError("ECDH224: InitEcdhkey failed");
});
return key.get();
}
static EC_KEY* ECDH256()
{
static EC_KEY_ptr key(NULL, NULL);
static once_flag flag;
call_once(flag, []()
{
key = EC_KEY_ptr(InitEcdhkey(256), ::EC_KEY_free);
ASSERT(key.get());
if(!key.get())
LogError("ECDH256: InitEcdhkey failed");
});
return key.get();
}
static EC_KEY* ECDH384()
{
static EC_KEY_ptr key(NULL, NULL);
static once_flag flag;
call_once(flag, []()
{
key = EC_KEY_ptr(InitEcdhkey(384), ::EC_KEY_free);
ASSERT(key.get());
if(!key.get())
LogError("ECDH384: InitEcdhkey failed");
});
return key.get();
}
static EC_KEY* ECDH521()
{
static EC_KEY_ptr key(NULL, NULL);
static once_flag flag;
call_once(flag, []()
{
key = EC_KEY_ptr(InitEcdhkey(521), ::EC_KEY_free);
ASSERT(key.get());
if(!key.get())
LogError("ECDH521: InitEcdhkey failed");
});
return key.get();
}
static EC_KEY* InitEcdhkey(int bits)
{
if (bits <= 160 + 4)
bits = 160;
else if (bits <= 192 + 4)
bits = 192;
else if (bits <= 224 + 4)
bits = 224;
else if (bits <= 256 + 4)
bits = 256;
else if (bits <= 384 + 4)
bits = 384;
else if (bits <= 521 + 4)
bits = 521;
else
bits = 521;
EC_KEY* key = EC_KEY_new_by_curve_name(CurveToNidByBits(bits));
unsigned long err = ERR_get_error();
ASSERT(key != NULL);
if (key == NULL)
{
ostringstream oss;
oss << "InitEcdhkey: EC_KEY_new_by_curve_name failed for ";
oss << bits << "-bit key, error " << err << ", 0x" << err;
LogError(oss);
}
return key;
}
Related
I'm producing my 1st projet in c. I want to build a mail client.
I used the socket to do it.
But have some trouble with that methode. I can't send a mail with a gmail smtp server because of an encoding protocol.
So after some research, i understood that i haven't lot's of choice. I have to use the openssl library.
And that's the problem. I understood how to use that library (I hope, you can judge it with my code attached) but i never understand how to add the library.
On stackoverflow i find solutions for linux and android so i create a new post.
So that's my question:
What should I do with the zip file that i download from openssl official website?. And even which zip file do I need to download ?
SOCKET connexion(char* server_name,unsigned short port)
{
char buf[1024]={0};
int res_l= 0;
int nbrecv;
struct sockaddr_in serverSockAddr;
struct hostent * serverHostEnt;
SOCKET to_server_socket = 0;
memset(&serverSockAddr,0, sizeof(serverSockAddr) );
serverHostEnt = gethostbyname( server_name );
if ( serverHostEnt == NULL )
{
res_l = h_errno;
return (SOCKET)-1;
}
memcpy(&serverSockAddr.sin_addr,serverHostEnt->h_addr, serverHostEnt->h_length );
serverSockAddr.sin_port = htons( port );
serverSockAddr.sin_family = AF_INET;
to_server_socket = socket( AF_INET, SOCK_STREAM, 0 );
if( connect( to_server_socket, ( struct sockaddr * ) &serverSockAddr,sizeof( serverSockAddr ) ) < 0 ) return (SOCKET)-3;
while( !buf[0] ) nbrecv = recv( to_server_socket, buf, 1024, 0 );
printf("Welcome message : %s\n",buf);
return to_server_socket;
}
void SendAndReceiveSSL(SSL * ssl, char * messagesend, int n){
{
char bufreceive[1024];
int size,retVal,nbrecv;
size = (int)strlen( messagesend );
retVal = SSL_write( ssl, messagesend, size);
printf("Envoye : %s\n",messagesend) ;
memset(bufreceive,0,1024);
if (n!=1) //n=0 if i don't want an answer of the server.
{
while(!bufreceive[0]) nbrecv = SSL_read( ssl, bufreceive,1024);
printf("Recu : %s\n",bufreceive);
}
}
int mail_ssl(SOCKET sock, SSL ssl, const char* from,const char* to,const char* body)
{
#define SIZEMAX 1000
char buffer[SIZEMAX]; int n=0;
SendAndReceive(sock, "EHLO localhost\r\n",n); //no ssl for the 2 1st sentences//EHLO localhose ... wait 250
SendAndReceive(sock, "STARTTLS\r\n,n)
SendAndReceiveSSL(ssl, "EHLO localhost\r\n",n); //sll for the next
sprintf(buffer,"MAIL FROM: <%s>\r\n",from);
SendAndReceiveSSL(ssl, buffer,n); //MAIL FROM:<******> wait 250
sprintf(buffer,"RCPT TO: <%s>\r\n",to);
SendAndReceiveSSL(ssl, buffer,n); //RCPT TO:<******> wait 250
SendAndReceiveSSL(ssl, "DATA\r\n",n); //DATA wait 340
sprintf(buffer, "Subject: %s\r\n",body); n=1;
SendAndReceiveSSL(ssl, buffer,n); n=0; //Subject : subject \r\n body\r\n ///// DON'T WAIT
SendAndReceiveSSL(ssl, ".\r\n",n); //.\r\n wait 250
SendAndReceiveSSL(ssl, "QUIT\r\n",n);
return 0;
}
SSL_CTX* InitCTX(void)
{
SSL_METHOD *method;
SSL_CTX *ctx;
OpenSSL_add_all_algorithms(); // Load cryptos, et.al.
SSL_load_error_strings(); // Bring in and register error messages
method = TLSv1_2_client_method(); // Create new client-method instance
ctx = SSL_CTX_new(method); // Create new context
if ( ctx == NULL )
{
ERR_print_errors_fp(stderr);
abort();
}
return ctx;
}
void ShowCerts(SSL* ssl)
{
X509 *cert;
char *line;
cert = SSL_get_peer_certificate(ssl); // get the server's certificate
if ( cert != NULL )
{
printf("Server certificates:\n");
line = X509_NAME_oneline(X509_get_subject_name(cert), 0, 0);
printf("Subject: %s\n", line);
free(line); // free the malloc'ed string
line = X509_NAME_oneline(X509_get_issuer_name(cert), 0, 0);
printf("Issuer: %s\n", line);
free(line); // free the malloc'ed string
X509_free(cert); // free the malloc'ed certificate copy
}
else printf("Info: No client certificates configured.\n");
}
int authSSL(SOCKET sock, const char* from,const char* to,const char* body)
{
SSL_CTX *ctx;
SSL *ssl;
SSL_library_init();
ctx = InitCTX();
ssl = SSL_new(ctx);
SSL_set_fd(ssl, sock);
if ( SSL_connect(ssl) == FAIL )
ERR_print_errors_fp(stderr);
else
{
printf("Connected with %s encryption\n", SSL_get_cipher(ssl));
ShowCerts(ssl);
SSL_write(ssl, msg, strlen(msg));
mail_ssl(sock, ssl, from, to, body); //In this function i collect
//all the informations to create the mail and i use all the function above
SSL_free(ssl);
}
close(server); // close socket
SSL_CTX_free(ctx); // release context
return 0;
}
you can use VCPKG package manager, it will download and configure your envirument if you are using Visual Studio
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've been trying to make a simple client/server echo application for the sake of getting to know OpenSSL. I've been following Network Security with OpenSSL by John Viega, Matt Messier, Pravir Chandra (O'Reilly). Their example shows how to set up a root certificate authority, a server CA signed with the root CA, then server certificate signed with the server CA and, finally, a client certificate signed with the root CA and then import them in the C program.
I have made small changes to the program (such as not using threads at all) but most of it is their. My idea was to learn as I try this out but I've come to a problem.
When I try to connect the two, I get an error message saying "unable to get local issuer certificate" (error 20).
I would also like to state that the "previous version" worked (meaning that they progress in their examples).
Not sure if the problem is in the code, the certificates or both.
My OpenSSL version is OpenSSL 1.0.1f 6 Jan 2014. (as returned by the openssl version command)
My knowledge in this matter is very shallow at the moment and I've not had much luck finding an answer. I realize that I still have much reading to do and I will do so but this is a problem I need to overcome.
int seed_prng(int bytes) function is mine, a fix to the problem of not finding the seed_prng(void) function in all these libraries (also one of the things I found that is not fitting).
Perhaps, as this tutorial is 14 years old (at least), it is outdated and I should follow another one? If so, please recommend a tutorial with a high detail level, since the documentation of OpenSSL is not very friendly, in my experience.
Below, I provide all the files included in the process (except for the Makefile.)
The flags I use for the compilation are: -L/usr/lib -lssl -lcrypto -Wall
common.h :
#include <openssl/bio.h>
#include <openssl/err.h>
#include <openssl/rand.h>
#include <openssl/ssl.h>
#include <openssl/x509v3.h>
#include <openssl/rsa.h>
#include <openssl/objects.h>
#define PORT "8081"
#define SERVER "localhost"
#define CLIENT "localhost"
#define int_error(msg) handle_error(__FILE__, __LINE__, msg)
//Do not confuse with SSL_METHOD ; TLSv1_method() is also a viable option here
#define SSL_METHOD_ SSLv23_method()
#define SEED_PRNG_() seed_prng(30)
#define DEFAULT_DEPTH_ 4
#define DATA_SIZE_ 256
void handle_error(const char *file, int lineno, const char *msg) ;
void init_OpenSSL(void) ;
int seed_prng(int bytes) ;
int verify_callback(int ok, X509_STORE_CTX *store) ;
long post_connection_check(SSL *ssl, char *host) ;
common.c :
#include "common.h"
void handle_error(const char *file, int lineno, const char *msg)
{
fprintf(stderr, "** %s:%i %s\n", file, lineno, msg);
ERR_print_errors_fp(stderr);
exit(-1);
}
void init_OpenSSL(void)
{
if (!SSL_library_init())
{
fprintf(stderr, "** OpenSSL initialization failed!\n");
exit(-1);
}
SSL_load_error_strings();
}
//Not sure if this is good idea! Have to do some research
int seed_prng(int bytes)
{
if (!RAND_load_file("/dev/urandom", bytes))
{
return 0;
}
return 1;
}
int verify_callback(int ok, X509_STORE_CTX *store)
{
char data[DATA_SIZE_];
if (!ok)
{
X509 *cert = X509_STORE_CTX_get_current_cert(store);
int depth = X509_STORE_CTX_get_error_depth(store);
int err = X509_STORE_CTX_get_error(store);
fprintf(stderr, "-Error with certificate at depth: %i\n", depth);
X509_NAME_oneline(X509_get_issuer_name(cert), data, 256);
fprintf(stderr, " issuer = %s\n", data);
X509_NAME_oneline(X509_get_subject_name(cert), data, 256);
fprintf(stderr, " subject = %s\n", data);
fprintf(stderr, " err %i:%s\n", err,
X509_verify_cert_error_string(err));
}
return ok;
}
long post_connection_check(SSL *ssl, char *host)
{
X509 *cert;
X509_NAME *subj;
char data[DATA_SIZE_];
int extcount;
int ok = 0;
if (!(cert = SSL_get_peer_certificate(ssl)) || !host)
{
if (cert)
{
X509_free(cert);
}
return X509_V_ERR_APPLICATION_VERIFICATION;
}
if ((extcount = X509_get_ext_count(cert)) > 0)
{
int i;
for (i = 0; i < extcount; i++)
{
const char *extstr;
X509_EXTENSION *ext;
ext = X509_get_ext(cert, i);
extstr = OBJ_nid2sn(OBJ_obj2nid(X509_EXTENSION_get_object(ext)));
if (!strcmp(extstr, "subjectAltName"))
{
int j;
unsigned char *data;
STACK_OF(CONF_VALUE) *val;
CONF_VALUE *nval;
const X509V3_EXT_METHOD *meth;
if (!(meth = X509V3_EXT_get(ext)))
{
break;
}
data = ext->value->data;
val = meth->i2v(meth,(meth->d2i(NULL, (const unsigned char **)(&data), ext->value->length)), NULL);
for (j = 0; j < sk_CONF_VALUE_num(val); j++)
{
nval = sk_CONF_VALUE_value(val, j);
if (!strcmp(nval->name, "DNS") && !strcmp(nval->value, host))
{
ok = 1;
break;
}
}
}
if (ok)
{
break;
}
}
}
if (!ok && (subj = X509_get_subject_name(cert)) && X509_NAME_get_text_by_NID(subj, NID_commonName, data, DATA_SIZE_) > 0)
{
data[DEFAULT_DEPTH_ - 1] = 0;
if (strcasecmp(data, host) != 0)
{
if (cert)
{
X509_free(cert);
}
return X509_V_ERR_APPLICATION_VERIFICATION;
}
}
X509_free(cert);
return SSL_get_verify_result(ssl);
}
server.c :
#include "common.h"
#define BUF_SIZE_ 80
#define CERTFILE "certificates/serverNOKEY.pem"
#define CAFILE "certificates/rootcert.pem"
#define CADIR NULL
SSL_CTX *setup_server_ctx(void)
{
SSL_CTX *ctx;
//This specifies that either SSL or TLS can be used
//Later, we will "filter" out SSLv2
ctx = SSL_CTX_new(SSLv23_method());
//These two functions are used to load trusted CAs
if (SSL_CTX_load_verify_locations(ctx, CAFILE, CADIR) != 1)
{
int_error("Error loading CA file and/or directory");
}
if (SSL_CTX_set_default_verify_paths(ctx) != 1)
{
int_error("Error loading default CA file and/or directory");
}
//This loads a certificate from a file
if (SSL_CTX_use_certificate_chain_file(ctx, CERTFILE) != 1)
{
int_error("Error loading certificate from file");
}
//This loads a private key (in our code, from the same file but I think that it is not necessary)
if (SSL_CTX_use_PrivateKey_file(ctx, CERTFILE, SSL_FILETYPE_PEM) != 1)
{
int_error("Error loading private key from file");
}
SSL_CTX_set_verify(ctx, SSL_VERIFY_PEER|SSL_VERIFY_FAIL_IF_NO_PEER_CERT, verify_callback);
SSL_CTX_set_verify_depth(ctx, DEFAULT_DEPTH_);
return ctx;
}
int do_server_loop(SSL *ssl)
{
int err, nread;
char buf[BUF_SIZE_];
//I think that this reading should be redone 'cause there's something wrong
do
{
for (nread = 0; nread < sizeof(buf) - 1; nread += err)
{
err = SSL_read(ssl, buf + nread, sizeof(buf) - nread);
printf("read %d bytes\n", err);
if (err <= 0)
{
break;
}
}
fwrite(buf, sizeof(char), nread, stdout);
} while (err > 0);
return (SSL_get_shutdown(ssl) & SSL_RECEIVED_SHUTDOWN) ? 1 : 0;
}
void server_part(SSL *ssl)
{
long err;
//accepting connection from ssl object (structure)
if (SSL_accept(ssl) <= 0)
{
int_error("Error accepting SSL connection");
}
if ((err = post_connection_check(ssl, CLIENT)) != X509_V_OK)
{
fprintf(stderr, "-Error: peer certificate: %s\n",
X509_verify_cert_error_string(err));
int_error("Error checking SSL object after connection");
}
fprintf(stderr, "SSL Connection opened\n");
//As I figured, SSL_shutdown is a clean way to go but SSL_clear
//will force the closing of the communication if it wasn't closed cleanly
if (do_server_loop(ssl))
{
//See this https://www.openssl.org/docs/manmaster/ssl/SSL_shutdown.html
SSL_shutdown(ssl);
}
else
{
// https://www.openssl.org/docs/manmaster/ssl/SSL_clear.html
SSL_clear(ssl);
}
fprintf(stderr, "SSL Connection closed\n");
SSL_free(ssl);
}
int main(int argc, char *argv[])
{
BIO *acc, *client;
SSL *ssl;
SSL_CTX *ctx;
init_OpenSSL();
//This is my function, gotta investigate it and see what should be there (maybe I got it right?)
SEED_PRNG_();
ctx = setup_server_ctx();
acc = BIO_new_accept(PORT);
if (!acc)
{
int_error("Error creating server socket");
}
if (BIO_do_accept(acc) <= 0)
{
int_error("Error binding server socket");
}
for (;;)
{
if (BIO_do_accept(acc) <= 0)
{
int_error("Error accepting connection");
}
client = BIO_pop(acc);
if (!(ssl = SSL_new(ctx)))
{
int_error("Error creating SSL context");
}
SSL_set_bio(ssl, client, client);
server_part(ssl);
}
SSL_CTX_free(ctx);
BIO_free(acc);
return 0;
}
client.c :
#include "common.h"
#define BUF_SIZE_ 80
#define CERTFILE "certificates/clientNOKEY.pem"
#define CAFILE "certificates/rootcert.pem"
#define CADIR NULL
SSL_CTX *setup_client_ctx(void)
{
SSL_CTX *ctx;
ctx = SSL_CTX_new(SSL_METHOD_);
if (SSL_CTX_load_verify_locations(ctx, CAFILE, CADIR) != 1)
{
int_error("Error loading CA file and/or directory");
}
if (SSL_CTX_set_default_verify_paths(ctx) != 1)
{
int_error("Error loading default CA file and/or directory");
}
if (SSL_CTX_use_certificate_chain_file(ctx, CERTFILE) != 1)
{
int_error("Error loading certificate from file");
}
if (SSL_CTX_use_PrivateKey_file(ctx, CERTFILE, SSL_FILETYPE_PEM) != 1)
{
int_error("Error loading private key from file");
}
SSL_CTX_set_verify(ctx, SSL_VERIFY_PEER, verify_callback);
SSL_CTX_set_verify_depth(ctx, DEFAULT_DEPTH_);
return ctx;
}
int do_client_loop(SSL *ssl)
{
int err, nwritten;
char buf[BUF_SIZE_];
for (;;)
{
if (!fgets(buf, sizeof(buf), stdin))
{
break;
}
//printf("ucitao: %s\n", buf);
for (nwritten = 0; nwritten < sizeof(buf); nwritten += err)
{
err = SSL_write(ssl, buf + nwritten, strlen(buf) - nwritten);
if (err <= 0)
{
return 0;
}
else
{
printf("Sent: %s\n", buf);
}
}
}
return 1;
}
int main(int argc, char *argv[])
{
BIO *conn;
SSL *ssl;
SSL_CTX *ctx;
long err;
init_OpenSSL();
SEED_PRNG_();
ctx = setup_client_ctx();
conn = BIO_new_connect(SERVER ":" PORT);
if (!conn)
{
int_error("Error creating connection BIO");
}
if (BIO_do_connect(conn) <= 0)
{
int_error("Error connecting to remote machine");
}
if (!(ssl = SSL_new(ctx)))
int_error("Error creating an SSL context");
SSL_set_bio(ssl, conn, conn);
if (SSL_connect(ssl) <= 0)
{
int_error("Error connecting SSL object");
}
if ((err = post_connection_check(ssl, SERVER)) != X509_V_OK)
{
fprintf(stderr, "-Error: peer certificate: %s\n",
X509_verify_cert_error_string(err));
int_error("Error checking SSL object after connection");
}
fprintf(stderr, "SSL Connection opened\n");
if (do_client_loop(ssl))
{
SSL_shutdown(ssl);
}
else
{
SSL_clear(ssl);
}
fprintf(stderr, "SSL Connection closed\n");
SSL_free(ssl);
SSL_CTX_free(ctx);
return 0;
}
The script I used to create the CAs, certificates and sign them:
#! /bin/bash
echo "Begin"
#the line below ensures that the script finishes after an unsuccessful command, not trying to execute any next command,
#since it's the assumption that every previous command was successful
set -e
echo "Creating the root CA"
openssl req -newkey rsa:1024 -sha1 -keyout rootkey.pem -out rootreq.pem
openssl x509 -req -in rootreq.pem -sha1 -extfile myopenssl.cnf -extensions v3_ca -signkey rootkey.pem -out rootcert.pem
cat rootcert.pem rootkey.pem > root.pem
openssl x509 -subject -issuer -noout -in root.pem
echo "Creating the server CA and signing it with the root CA"
openssl req -newkey rsa:1024 -sha1 -keyout serverCAkey.pem -out serverCAreq.pem
openssl x509 -req -in serverCAreq.pem -sha1 -extfile myopenssl.cnf -extensions v3_ca -CA root.pem -CAkey root.pem -CAcreateserial -out serverCAcert.pem
cat serverCAcert.pem serverCAkey.pem rootcert.pem > serverCA.pem
openssl x509 -subject -issuer -noout -in serverCA.pem
echo "Creating the server\'s certificate and signing it with the server CA"
openssl req -newkey rsa:1024 -sha1 -keyout serverkey.pem -out serverreq.pem
openssl x509 -req -in serverreq.pem -sha1 -extfile myopenssl.cnf -extensions usr_cert -CA serverCA.pem -CAkey serverCA.pem -CAcreateserial -out servercert.pem
cat servercert.pem serverkey.pem serverCAcert.pem rootcert.pem > server.pem
openssl x509 -subject -issuer -noout -in server.pem
echo "Creating the client certificate and signing it with the root CA"
openssl req -newkey rsa:1024 -sha1 -keyout clientkey.pem -out clientreq.pem
openssl x509 -req -in clientreq.pem -sha1 -extfile myopenssl.cnf -extensions usr_cert -CA root.pem -CAkey root.pem -CAcreateserial -out clientcert.pem
cat clientcert.pem clientkey.pem rootcert.pem > client.pem
openssl x509 -subject -issuer -noout -in client.pem
echo "creating dh512.pem and dh1024.pem"
openssl dhparam -check -text -5 512 -out dh512.pem
openssl dhparam -check -text -5 1024 -out dh1024.pem
echo "Making new keys which do not require passkey"
echo "Creating no passkey server key"
openssl rsa -in server.pem -out serverNOKEY.pem
openssl x509 -in server.pem >>serverNOKEY.pem
echo "serverNOKEY.pem created"
echo "Creating no passkey client key"
openssl rsa -in client.pem -out clientNOKEY.pem
openssl x509 -in client.pem >>clientNOKEY.pem
echo "clientNOKEY.pem created"
echo "Done! Success!"
I faced similar issue when I was setting up SSL Communications between java applications. To determine whether its a code issue or certificate issue itself you can run below openssl command. If certificate isn't getting verified it means there is problems in verifying certificates using root CA.
Verify a certificate
openssl verify certificate_name
Reference .
Also, as it seems you are working on self-signed certificate you can switch off verification of certificates in your code and proceed. In production environment when certificates are signed by Certificate Authority (CA) such errors are less likely to appear.
I'm looking for a way to restrict client certificates to specific set of self-signed certificates on the server side using the OpenSSL API.
There is a set of trusted self-signed certificates, say ./dir/*.pem. I want to reject connections, if they don't supply one of those certificates.
I can achieve almost desired behaviour by comparing server and client certificate fingerprints in the SSL context verification callback:
SSL_CTX *ctx;
...
SSL_CTX_set_verify(ctx, SSL_VERIFY_PEER | SSL_VERIFY_FAIL_IF_NO_PEER_CERT, verify_callback);
static inline int get_fingerprint(X509* cert, unsigned char *md, unsigned int *n)
{
return X509_digest(cert, EVP_sha1(), md, n);
}
static inline int compare_certificates(X509 *c1, X509 *c2)
{
unsigned char md1[EVP_MAX_MD_SIZE], md2[EVP_MAX_MD_SIZE];
unsigned int n1, n2;
if (!(get_fingerprint(c1, md1, &n1) && get_fingerprint(c2, md2, &n2))) {
return -1;
}
return memcmp(md1, md2, n1);
}
static int verify_callback(int preverify_ok, X509_STORE_CTX *ctx)
{
SSL *ssl = X509_STORE_CTX_get_ex_data(ctx, SSL_get_ex_data_X509_STORE_CTX_idx());
int err = X509_STORE_CTX_get_error(ctx);
/* Allow self-signed certificates */
if (!preverify_ok && err == X509_V_ERR_DEPTH_ZERO_SELF_SIGNED_CERT) {
preverify_ok = 1;
}
if (0 != compare_certificates(ctx->current_cert, SSL_CTX_get0_certificate(ssl->ctx))) {
/* Peer certificate doesn't match the server certificate */
preverify_ok = 0;
}
/* More checks ... */
return preverify_ok;
}
So if the server and client certificate fingerprints match, verification passes. Otherwise, connection is closed by the server.
I might compute fingerprints of the trusted certificates somewhere in initialization phase, then check them in a loop within the verify_callback. However, I don't like this idea. There should be easier way to do this.
I thought SSL_CTX_load_verify_locations() is just what I was looking for(but it looks like it's not; i'll explain why):
SSL_CTX_load_verify_locations() specifies the locations for ctx, at which CA certificates for verification purposes are located.
...
If CAfile is not NULL, it points to a file of CA certificates in PEM format. The file can contain several CA certificates...
The certificates in CApath are only looked up when required, e.g. when building the certificate chain or when actually performing the verification of a peer certificate.
(man 3 SSL_CTX_load_verify_locations)
Well, I guess SSL_VERIFY_FAIL_IF_NO_PEER_CERT implies verifying the peer certificate. Then it looks like all I need to do is to make a bundle of trusted certificates and pass it to SSL_CTX_load_verify_locations():
bundle_file=CAbundle.pem
cd ./dir
rm -f $bundle_file
for i in *.pem; do
openssl x509 -in $i -text >> $bundle_file
done
c_rehash .
SSL_CTX *ctx;
const char *cafile = "dir/CAbundle.pem";
const char *capath = NULL;
...
if (!SSL_CTX_load_verify_locations(ctx, cafile, capath)) {
/* Unable to set verify locations ... */
}
cert_names = SSL_load_client_CA_file(cafile);
if (cert_names != NULL) {
SSL_CTX_set_client_CA_list(ctx, cert_names);
} else {
/* Handle error ... */
}
All looks good. But the server still accepts connections with different peer certificates.
I've reproduced this behaviour using standard OpenSSL utilities here: https://gist.github.com/rosmanov/d960a5d58a96bdb730303c5b8e86f951
So my question is: how do I configure the server to accept only peers providing only specific certificates?
Update
I've found that the "whitelist" of certificates (CA bundle) actually works,
when I remove the following from the verify_callback:
if (!preverify_ok && err == X509_V_ERR_DEPTH_ZERO_SELF_SIGNED_CERT) {
preverify_ok = 1;
}
So without this block everything just works. The server responds to a client connected with one of certificates listed in CAbundle.pem. If a client connects with different certificate, the server closes connection.
However, there is a strange thing. In both cases openssl s_client outputs:
Verify return code: 18 (self signed certificate)
Then maybe
if (!preverify_ok
&& err == X509_V_ERR_DEPTH_ZERO_SELF_SIGNED_CERT
&& allow_self_signed
&& !cafile
&& !capath) {
preverify_ok = 1;
}
?
Update 2
Now I understand why openssl s_client outputs Verify return code: 18 (self signed...). It doesn't trust server's certificate unless -CAfile or -CApath contains the server certificate. And the server certificate is self-signed.
An explanation (for commandline) and a halfanswer (for library):
I (this time fully) redid your gist and was reminded of an inconsistency here. Commandline openssl xxx utilities are mostly designed as test/debugging tools, and in particular:
s_client normally (except anonymous, SRP, etc) receives a cert chain from the server, but uses a callback that only logs what it got and ignores/overrides any error; this is the block
depth=0 C = AU, ST = StateA, L = CityA, O = CompanyA, CN = localhost, emailAddress = a#gmail.com
verify error:num=18:self signed certificate
verify return:1
depth=0 C = AU, ST = StateA, L = CityA, O = CompanyA, CN = localhost, emailAddress = a#gmail.com
verify return:1
just after CONNECTED(fd) in your s_client output, but as you see in spite of the error it continues with the handshake resulting in a usable connection.
s_server is a more complicated. It does not request a cert from client by default, only if you specify -verify or -Verify (which set SSL_VERIFY_PEER which is not the default for server), and if it does request a cert client has the option whether to send one (with associated proof in CertVerify). If client does send chain, s_server uses the same callback as s_client which overrides any error and continues with the connection; this in your s_server output with the same verify error:num-18... which actually means 'root (including selfsigned which is its own root) in received chain but not in local truststore'. If client does not send chain, -verify continues, but -Verify (which also sets SSL_VERIFY_FAIL_IF_NO_PEER_CERT) aborts the handshake with alert 40 and returns an error, so the s_server output is very different:
verify depth is 0, must return a certificate
Using default temp DH parameters
Using default temp ECDH parameters
ACCEPT
ERROR
140679792887624:error:140890C7:SSL routines:SSL3_GET_CLIENT_CERTIFICATE:peer did not return a certificate:s3_srvr.c:3271:
shutting down SSL
CONNECTION CLOSED
ACCEPT
But a program using the library should work. I hacked up this simple test from parts of some other programs (hence the odd indentation):
/* SO36821430 2016-04-25 */
#include <stdio.h>
#if defined(_WIN32)&&!defined(WIN32)
#define WIN32 /*anything*/
#endif
#ifdef WIN32
#include <winsock2.h>
typedef int socklen_t;
#define SOCKERR WSAGetLastError()
#include "openssl/applink.c"
#else
#include <unistd.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#ifndef INADDR_NONE
#define INADDR_NONE (ipaddr_t)-1
#endif
typedef int SOCKET;
enum { INVALID_SOCKET = -1, SOCKET_ERROR = -1 };
#define SOCKERR errno
#define closesocket close
#endif
#include "openssl/ssl.h"
#include "openssl/err.h"
#include "openssl/rand.h"
void sockerr (const char *what){
fprintf (stderr, "%s %d %s\n", what, SOCKERR, strerror(SOCKERR));
}
void sslerrn (const char *what){
fprintf (stderr, "* %s failed:\n", what);
ERR_print_errors_fp (stderr);
}
void sslerr (const char *what, int rv){
fprintf (stderr, "* %s return %d:\n", what, rv);
ERR_print_errors_fp (stderr);
}
void sslerrx (SSL * ssl, const char *what, int rv){
int rc = SSL_get_error (ssl, rv);
if( rv == -1 && rc == SSL_ERROR_SYSCALL ) sockerr (what);
else fprintf (stderr, "* %s return %d,%d\n", what, rv, rc);
ERR_print_errors_fp (stderr);
}
void subj_oneline (X509 * cert, FILE *fp){
X509_NAME * subj = X509_get_subject_name (cert);
BIO *bmem = BIO_new (BIO_s_mem()); char *ptr; int n;
X509_NAME_print_ex (bmem, subj, 0, XN_FLAG_ONELINE);
n = (int) BIO_get_mem_data (bmem, &ptr);
if( n <= 0 ) ptr = "?", n = 1;
fwrite (ptr,1,n,fp);
}
const char * inaddr;
int inport;
char buf [9999];
int main (int argc, char* argv[] )
{
int rv;
struct sockaddr_in sin; socklen_t sinlen;
SOCKET s1, s2; SSL_CTX *ctx = NULL;
time_t now; struct tm * tm;
#ifdef WIN32
struct WSAData wsa;
rv = WSAStartup (MAKEWORD(1,1), &wsa);
if(rv){ printf ("WSAStartup %d\n", rv); exit(1); }
#endif
if( argc < 2 || argc > 6 )
printf ("usage: %s port key cert CAcerts\n", argv[0]), exit(1);
sin.sin_addr.s_addr = INADDR_ANY;
sin.sin_port = htons (atoi(argv[1]));
sin.sin_family = AF_INET;
/**/
SSL_library_init();
SSL_load_error_strings();
ctx = SSL_CTX_new (SSLv23_server_method());
if( !ctx ){ sslerrn("CTX_new"); exit(1); }
SSL_CTX_set_options (ctx, SSL_OP_NO_SSLv2|SSL_OP_NO_SSLv3);
rv = SSL_CTX_use_PrivateKey_file (ctx, argv[2], SSL_FILETYPE_PEM);
if( rv != 1 ){ sslerr ("use_PrivateKey_file",rv); exit(1); }
rv = SSL_CTX_use_certificate_file (ctx, argv[3], SSL_FILETYPE_PEM);
if( rv != 1 ){ sslerr ("use_certificate_file",rv); exit(1); }
SSL_CTX_set_verify (ctx, SSL_VERIFY_PEER | SSL_VERIFY_FAIL_IF_NO_PEER_CERT, NULL);
if( !SSL_CTX_load_verify_locations (ctx, argv[4], NULL) ){
sslerrn ("load_verify_locations"); exit(1); }
SSL_CTX_set_client_CA_list (ctx, SSL_load_client_CA_file (argv[4]));
/**/
if( (s1 = socket (AF_INET,SOCK_STREAM,0)) == INVALID_SOCKET ){
sockerr ("socket()"); exit(1); }
if( bind (s1, (struct sockaddr*)&sin, sizeof sin) < 0 ){
sockerr ("bind()"); exit(1); }
if( listen (s1, 5) < 0 ){
sockerr ("listen()"); exit(1); }
do{
sinlen = sizeof sin;
if( (s2 = accept (s1, (struct sockaddr*)&sin, &sinlen)) == INVALID_SOCKET ){
sockerr ("accept()"); exit(1); }
now = time(NULL); tm = localtime(&now);
printf ("+ %s %u #%02d.%02d.%02d\n", inet_ntoa (sin.sin_addr),
ntohs (sin.sin_port), tm->tm_hour, tm->tm_min, tm->tm_sec);
/**/
SSL * ssl = SSL_new (ctx);
if( !ssl ){ sslerrn("SSL_new"); goto next; }
SSL_set_fd (ssl, s2);
if( (rv = SSL_accept(ssl)) < 0 ){
sslerrx (ssl, "SSL_accept", rv); goto next; }
{ X509 * cert = SSL_get_peer_certificate (ssl);
/*EVP_PKEY * key = cert? X509_get_pubkey (cert): NULL;*/
fprintf (stdout, "=%ld", SSL_get_verify_result (ssl));
if( cert ) putchar (':'), subj_oneline (cert, stdout);
putchar ('\n');
}
while( (rv = SSL_read (ssl, buf, sizeof buf)) > 0 )
printf ("%d: %.*s\n", rv, rv, buf);
sslerrx (ssl, "SSL_read", rv);
next:
if( ssl ) SSL_free (ssl);
/**/
now = time(NULL); tm = localtime(&now);
printf ("- %s %u #%02d.%02d.%02d\n", inet_ntoa (sin.sin_addr),
ntohs (sin.sin_port), tm->tm_hour, tm->tm_min, tm->tm_sec);
closesocket (s2);
} while (1);
return 0;
}
When run with $port cert1.key cert1.pem CAbundle.pem and connected from client using cert2.key & cert2.pem this aborts the handshake with alert 48 unknown_ca and returns an error as desired:
+ 127.0.0.1 46765 #22.07.36
* SSL_accept return -1,1
140240689366696:error:14089086:SSL routines:ssl3_get_client_certificate:certificate verify failed:s3_srvr.c:3270:
- 127.0.0.1 46765 #22.07.36
HTH.
If you want a whitelist of specific client certificates, you can prepare an indexed list in memory when you initialize.
For example, you can use PEM_X509_INFO_read to read a concatenated file of all client certificates in PEM format. This will give you a STACK_OF(X509_INFO)* of certificates. The number of certificates can be found with sk_X509_INFO_num, and you can see each certificate at sk_X509_INFO_value(..)->x509.
Then, for example, you can simply build an in-memory index and qsort by compare_x509.
Now, when your verify callback is called, just do a bsearch on your index by compare_x509, and either the certificate is on your whitelist, or it isn't.
You can accept the match on the result of compare_x5099, or of course you could double-check by verifying the full certificate once the search finds a match in the index.
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 !