Experimenting with SSL connections - c

I have written a simple SSL Client/Server set of programs from a few tutorials I have found on the net - and these work fine. What I cant get my head around is the client side of things (See below)
It looks as if from the code the client connects to the SSL server, blindly accepts the certificate it provides then then uses it to encrypt/decrypt the data sent to and from the pair.
Should there not be something client side that validates the servers certificate for use? I mean I can change / swap the server side certificate with any new one and have it connect without so much as a wimper? How is this a secure method of connection? (or am I - as I suspect - missing something )
Many thanks
FR
void LoadCertificates(SSL_CTX* ctx, char* CertFile, char* KeyFile);
int OpenConnection(const char *hostname, int port);
void ShowCerts(SSL* ssl);
SSL_CTX* InitCTX(void);
int main(int count, char *strings[]) {
char *hostname, *portnum;
char buf[1024];
SSL_CTX *ctx;
SSL *ssl;
int server;
int bytes;
if ( count != 3 ) {
printf("usage: %s <hostname> <portnum>\n", strings[0]);
exit(0);
} // if
hostname=strings[1];
portnum=strings[2];
printf("\nSSL Client 0.1\n~~~~~~~~~~~~~~~\n\n");
// Init. the SSL lib
SSL_library_init();
ctx = InitCTX();
printf("Client SSL lib init complete\n");
// Open the connection as normal
server = OpenConnection(hostname, atoi(portnum));
// Create new SSL connection state
ssl = SSL_new(ctx);
// Attach the socket descriptor
SSL_set_fd(ssl, server);
// Perform the connection
if ( SSL_connect(ssl) != FAIL ) {
char *msg = "Here is some data";
printf("Connected with %s encryption\n", SSL_get_cipher(ssl));
// Print any certs
ShowCerts(ssl);
// Encrypt & send message */
SSL_write(ssl, msg, strlen(msg));
// Get reply & decrypt
bytes = SSL_read(ssl, buf, sizeof(buf));
buf[bytes] = 0;
printf("Received: '%s'\n\n", buf);
// Release connection state
SSL_free(ssl);
} // if
else ERR_print_errors_fp(stderr);
// Close socket
close(server);
// Release context
SSL_CTX_free(ctx);
return 0;
} // main
SSL_CTX* InitCTX(void) {
SSL_METHOD const *method;
SSL_CTX *ctx;
// Load cryptos, et.al.
OpenSSL_add_all_algorithms();
// Bring in and register error messages
SSL_load_error_strings();
// Create new client-method instance
method = SSLv3_client_method();
// Create new context
ctx = SSL_CTX_new(method);
if ( ctx == NULL ) {
ERR_print_errors_fp(stderr);
abort();
} // if
return ctx;
} //InitCTX
int OpenConnection(const char *hostname, int port) {
int sd;
struct hostent *host;
struct sockaddr_in addr;
if ( (host = gethostbyname(hostname)) == NULL ) {
perror(hostname);
abort();
} // if
sd = socket(PF_INET, SOCK_STREAM, 0);
bzero(&addr, sizeof(addr));
addr.sin_family = AF_INET;
addr.sin_port = htons(port);
addr.sin_addr.s_addr = *(long*)(host->h_addr);
if ( connect(sd, (struct sockaddr*)&addr, sizeof(addr)) != 0 ) {
close(sd);
perror(hostname);
abort();
} // if
return sd;
} // OpenConnection
void ShowCerts(SSL* ssl) {
X509 *cert;
char *line;
cert = SSL_get_peer_certificate(ssl); /* get the server's certificate */
if ( cert != NULL ) {
printf("\nServer certificate:\n");
line = X509_NAME_oneline(X509_get_subject_name(cert), 0, 0);
printf("Subject: %s\n", line);
// Free the malloc'ed string
free(line);
line = X509_NAME_oneline(X509_get_issuer_name(cert), 0, 0);
printf("Issuer: %s\n", line);
// Free the malloc'ed string
free(line);
// Free the malloc'ed certificate copy
X509_free(cert);
} // if
else printf("No certificates.\n");
} // ShowCerts

You are correct. The connection is secure (from eavesdropping), has integrity (protection against injection, truncation, and modification), and authentication (peer is who he says he is in his certificate), all done automatically for you by the transport, but it still lacks authorization (is this the person I wanted to talk to, and what exactly is this person allowed to do in my application). Only the application can do that, inherently, so it is up to you to get the peer certificate, get its SubjectDN, relate that to some local user database, check for existence, check its roles, etc.

A "Secure Connection" can be seen as consisting of two things:
1 Authentication: Be sure to be connected to the partner (here: server) one expects to connect to
2 Encryption: Have the data (transferred by the connection) be protected against being read during transmission
The certificate received from the server can be used to accomplish 1. The certificate is not necessarily involved in 2.
On how to add verification of a server's certificate to your client, you might like to check out this SO answer: https://stackoverflow.com/a/12245023/694576 (which you could have done in the first place anyway ... ;->)
For details on TLS (successor to SSL and former SSL 3.x) please see here: http://en.wikipedia.org/wiki/Transport_Layer_Security

Related

Issue with network packet read on TLS1.3 | Openssl C program

openssl_1.1.1_perl_dump.txt
We are trying to upgrade on Openssl version 1.1.1, with security layer from TLS 1.2 to TLS 1.3.
Our code base performs SSL handshake, and later use send & read/recv function to transfer data. *Note - it is working fine for TLS 1.2.
However when we switch to TLS 1.3, and data packets are not received for send & read/recv, but working only when we use SSL_write & SSL_read.
Below we have tried to make sample server & client program, where we use send & read/recv functions after successful SSL connection, here also it works fine for TLS 1.2, but in case of TLS 1.3 there is lag in packets received, the first response from server is blank, please refer outputs below. For out put of 'perl configdata.pm --dump' refer attachment.
Used steps mentioned as per this reference article https://help.ubuntu.com/community/OpenSSL - to create SSL certificates.
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 "/usr/include/openssl/ssl.h"
#include "/usr/include/openssl/crypto.h"
#include "/usr/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));
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;
}
int isRoot()
{
if (getuid() != 0)
{
return 0;
}
else
{
return 1;
}
}
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 */
char *imethod=getenv("TLS_METHOD");
if(imethod!=NULL && strncmp(imethod, "1.3", 3)==0){
printf("\nSetting TLS1.3 method\n");
fflush(stdout);
method = TLS_server_method(); /* create new server-method instance */
}
else{
printf("\nSetting TLS1.2 method\n");
fflush(stdout);
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();
}
if(imethod!=NULL && strncmp(imethod, "1.3", 3)==0)
SSL_CTX_set_options(ctx, SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3 | SSL_OP_NO_TLSv1 | SSL_OP_NO_TLSv1_1 | SSL_OP_NO_TLSv1_2);
return ctx;
}
void LoadCertificates(SSL_CTX* ctx, char* CertFile, char* KeyFile)
{
SSL_CTX_load_verify_locations(ctx, "01.pem", "/home/qarun/aparopka/myCA/signedcerts");
/* set the local certificate from CertFile */
if ( SSL_CTX_use_certificate_file(ctx, CertFile, SSL_FILETYPE_PEM) <= 0 )
{
ERR_print_errors_fp(stderr);
abort();
}
/* 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();
}
/* 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_CLIENT_ONCE, NULL);
SSL_CTX_set_verify_depth(ctx, 2);*/
//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;
const char* HTMLecho="<html><body><pre>%s</pre></body></html>\n\n";
sd = SSL_get_fd(ssl); /* get socket connection */
printf("\nsd: <%d>", sd);
if ( SSL_accept(ssl) == FAIL ) /* do SSL-protocol accept */
ERR_print_errors_fp(stderr);
else
{
/*if(SSL_do_handshake(ssl) <= 0){
printf("\n SSL_do_handshake failed....\n");
}*/
ShowCerts(ssl); /* get any certificates */
do{
//bytes = SSL_read(ssl, buf, sizeof(buf)); /* get request */
//bytes = read(sd, buf, sizeof(buf));
bytes = recv(sd, buf, sizeof(buf), 0);
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 */
send(sd, reply, strlen(reply), 0); // MSG_NOSIGNAL | MSG_DONTWAIT); //MSG_CONFIRM);
}
else
ERR_print_errors_fp(stderr);
}while(bytes>0);
}
SSL_free(ssl); /* release SSL state */
close(sd); /* close connection */
}
int main(int count, char *strings[])
{ SSL_CTX *ctx;
int server;
char *portnum;
if(!isRoot())
{
printf("This program must be run as root/sudo user!!");
//exit(0);
}
if ( count != 2 )
{
printf("Usage: %s <portnum>\n", strings[0]);
exit(0);
}
SSL_library_init();
portnum = strings[1];
ctx = InitServerCTX(); /* initialize SSL */
LoadCertificates(ctx, "/home/qarun/aparopka/myCA/server_crt.pem", "/home/qarun/aparopka/myCA/server_key.pem"); /* load certs */
server = OpenListener(atoi(portnum)); /* create server socket */
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 */
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 */
}
client.c
#include <stdio.h>
#include <errno.h>
#include <unistd.h>
#include <malloc.h>
#include <string.h>
#include <sys/socket.h>
#include <resolv.h>
#include <netdb.h>
#include <openssl/ssl.h>
#include <openssl/err.h>
#define FAIL -1
//Added the LoadCertificates how in the server-side makes.
void LoadCertificates(SSL_CTX* ctx, char* CertFile, char* KeyFile)
{
/* set the local certificate from CertFile */
if ( SSL_CTX_use_certificate_chain_file(ctx, CertFile ) <= 0 )
{
ERR_print_errors_fp(stderr);
abort();
}
/* 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();
}
/* verify private key */
if ( !SSL_CTX_check_private_key(ctx) )
{
fprintf(stderr, "Private key does not match the public certificate\n");
abort();
}
}
int OpenConnection(const char *hostname, int port)
{ int sd;
struct hostent *host;
struct sockaddr_in addr;
if ( (host = gethostbyname(hostname)) == NULL )
{
perror(hostname);
abort();
}
sd = socket(PF_INET, SOCK_STREAM, 0);
bzero(&addr, sizeof(addr));
addr.sin_family = AF_INET;
addr.sin_port = htons(port);
addr.sin_addr.s_addr = *(long*)(host->h_addr);
if ( connect(sd, (struct sockaddr*)&addr, sizeof(addr)) != 0 )
{
close(sd);
perror(hostname);
abort();
}
return sd;
}
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 */
char *imethod=getenv("TLS_METHOD");
if(imethod!=NULL && strncmp(imethod, "1.3", 3)==0){
printf("\nSetting TLS1.3 method\n");
fflush(stdout);
method = TLS_client_method(); /* create new server-method instance */
}
else{
printf("\nSetting TLS1.2 method\n");
fflush(stdout);
method = TLSv1_2_client_method(); /* create new server-method instance */
}
ctx = SSL_CTX_new(method); /* Create new context */
if ( ctx == NULL )
{
ERR_print_errors_fp(stderr);
abort();
}
if(imethod!=NULL && strncmp(imethod, "1.3", 3)==0)
SSL_CTX_set_options(ctx, SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3 | SSL_OP_NO_TLSv1 | SSL_OP_NO_TLSv1_1 | SSL_OP_NO_TLSv1_2);
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 main(int count, char *strings[])
{ SSL_CTX *ctx;
int server;
SSL *ssl;
char buf[1024];
int bytes;
char *hostname, *portnum;
if ( count != 3 )
{
printf("usage: %s <hostname> <portnum>\n", strings[0]);
exit(0);
}
SSL_library_init();
hostname=strings[1];
portnum=strings[2];
ctx = InitCTX();
LoadCertificates(ctx, "/home/qarun/aparopka/myCA/server_crt.pem", "/home/qarun/aparopka/myCA/server_key.pem");
server = OpenConnection(hostname, atoi(portnum));
ssl = SSL_new(ctx); /* create new SSL connection state */
SSL_set_fd(ssl, server); /* attach the socket descriptor */
int sd = SSL_get_fd(ssl);
int flip=1;
printf("\nsd: <%d>", sd);
if ( SSL_connect(ssl) == FAIL ) /* perform the connection */
ERR_print_errors_fp(stderr);
else
{ //char *msg = "Hello???";
if(SSL_do_handshake(ssl) <= 0){
printf("\n client SSL_do_handshake failed....\n");
}
char msg[1024];
printf("Connected with %s encryption\n", SSL_get_cipher(ssl));
ShowCerts(ssl); /* get any certs */
while(1){
bzero(msg, 1024);
bzero(buf, 1024);
printf("\nEnter message: ");
fgets(msg, 1024, stdin);
//SSL_write(ssl, msg, strlen(msg)); /* encrypt & send message */
send(sd, msg, strlen(msg), 0); //MSG_NOSIGNAL | MSG_DONTWAIT); //MSG_CONFIRM);
//bytes = SSL_read(ssl, buf, sizeof(buf)); /* get reply & decrypt */
//bytes = read(sd, buf, sizeof(buf));
bytes = recv(sd, buf, sizeof(buf), 0);
/* read again for TLS 1.3 only for first time */
/*char *imethod=getenv("TLS_METHOD");
if(imethod!=NULL && strncmp(imethod, "1.3", 3)==0
&& flip){
bytes = read(sd, buf, sizeof(buf));
flip=0;
}*/
buf[bytes] = 0;
printf("Received: \"%s\"\n", buf);
}
SSL_free(ssl); /* release connection state */
}
close(server); /* close socket */
SSL_CTX_free(ctx); /* release context */
return 0;
}
Server & client Output with TLS 1.2
**$ ./ssl_server_working 2023**
Setting TLS1.2 method
Connection: 10.250.14.23:41152
sd: <4>No certificates.
Client msg: "first
"
Client msg: "second
"
Client msg: "third
"
**./ssl_client_working 10.250.14.23 2023**
Setting TLS1.2 method
sd: <3>Connected with ECDHE-RSA-AES256-GCM-SHA384 encryption
Server certificates:
Subject: /CN=MyOwn Root Certificate Authority/ST=CA/C=US/emailAddress=amit.paropkari#quest.com/O=SharePlex/OU=IT Department
Issuer: /CN=MyOwn Root Certificate Authority/ST=CA/C=US/emailAddress=amit.paropkari#quest.com/O=SharePlex/OU=IT Department
Enter message: first
Received: "<html><body><pre>first
</pre></body></html>
"
Enter message: second
Received: "<html><body><pre>second
</pre></body></html>
"
Enter message: third
Received: "<html><body><pre>third
</pre></body></html>
"
Server & client output for TLS 1.3 : Note the response for first client request is blank, which is received in next response for second request.
export TLS_METHOD=1.3
**./ssl_server_working 2024**
Setting TLS1.3 method
Connection: 10.250.14.23:34012
sd: <4>No certificates.
Client msg: "first
"
Client msg: "second
"
Client msg: "third
"
**./ssl_client_working 10.250.14.23 2024**
Setting TLS1.3 method
sd: <3>Connected with TLS_AES_256_GCM_SHA384 encryption
Server certificates:
Subject: /CN=MyOwn Root Certificate Authority/ST=CA/C=US/emailAddress=amit.paropkari#quest.com/O=SharePlex/OU=IT Department
Issuer: /CN=MyOwn Root Certificate Authority/ST=CA/C=US/emailAddress=amit.paropkari#quest.com/O=SharePlex/OU=IT Department
Enter message: first
Received: ""
Enter message: second
Received: "<html><body><pre>first
</pre></body></html>
"
Enter message: third
Received: "<html><body><pre>second
</pre></body></html>
"
We got the answer for this. Turns out TLS 1.3 has added one more handshake step, where after server Finish message Client again sends Server finish message, server verifies it, & sends post-handshake protocol level message, called as 'NewSessionTicket message'. We can disable it with SSL_CTX_set_num_tickets method, before initiating SSL server.
SSL_set_num_tickets(ssl, 0); /* Disable post-handshake token */
sd = SSL_get_fd(ssl); /* get socket connection */
if ( SSL_accept(ssl) == FAIL ) /* do SSL-protocol accept */
ERR_print_errors_fp(stderr);
As for people concerned about this not being secure, in our case of requirement we only need initial secure authentication, and then we do session tunneling. After that if we use SSL_write SSL_read for every transaction (which are in huge volume & time constraint) , the encryption decryption step would degrade the performance. So we are using send & recv for sending data.

OpenSSL in C - routines:ssl3_get_record:wrong version number when trying to connect to server using TLS1_2

I am trying to make a "curl" client that can send requests to servers in C.
I've done creating a TCP socket (using slides from college, tested):
int connect_to_host(char * hostname, int port) {
// AF_INET: create socket that uses IPv4; SOCK_STREAM: type of sequenced, reliable
// 2-way connection-based byte streams ~ TCP (as opposed to datagrams ~ UDP)
int socket_fd;
struct sockaddr_in server_addr;
struct hostent * hp;
if ((socket_fd = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
return -1; // remember to broadcast error, saved in errno (extern int errno)
}
// geting address (contained in struct hostent) from DNS
if ((hp = gethostbyname(hostname)) == NULL) {
return -2; // can't find hostname from DNS
}
// per documentation, must zero out bytes in the 'server' socket address before filling values
bzero((char *)&server_addr, sizeof(server_addr));
// filling necessary values of the 'server' socket address
server_addr.sin_family = AF_INET; // using IPv4
server_addr.sin_port = htons(port); // assign port value, remember to change int (host order byte)
// to network order byte. htons = host-to-network-short (short int)
bcopy((char *)hp->h_addr_list[0], (char *)&server_addr.sin_addr.s_addr, hp->h_length); // IP address, got from DNS
if (connect(socket_fd, (struct sockaddr *)&server_addr, sizeof(server_addr)) < 0) {
return -1; // error, as above
}
return socket_fd;
}
And created a TLS-version-flexible SSL/TLS context:
SSL_CTX * init_CTX() {
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 = TLS_client_method(); // create new client-method instance
if ((ctx = SSL_CTX_new(method)) == NULL) {; // create new context
return NULL;
}
return ctx;
}
My next step is to create an SSL connection state and "bind" it with the file descriptor return by the TCP function (connect_to_host()) above. Then I will try to make SSL_connect()
if (*tls_mode == 1) {
ctx = init_CTX();
ssl = SSL_new(ctx); // create new SSL connection state
SSL_set_fd(ssl, clientfd); // bind socket descriptor to ssl state
if (SSL_connect(ssl) == -1) {
printf("SSL connection failed\n");
ERR_print_errors_fp(stderr);
return -1;
}
printf("debug tls: connect\n");
if (show_cert) SSL_show_certs(ssl);
}
At this point I receive the error in the title. I have checked stackoverflow and places but I don't see questions using C and the OpenSSL library. If anyone can give me a hint, I would really appreciate it.
So for anyone who checks back on this later, I made a mistake with my port number, and connect to port 80 instead of 443, causing the error.

SSL_read() returns 0 and SSL_get_error return 6. Why is connection shutdown during SSL_read()?

I am looking to download or read a file from a server over HTTPS, and I'm using openSSL for it.
I see the connection succeeds, however SSL_read() returns 0. SSL_get_error() returns 6 which refers to SSL_ERROR_ZERO_RETURN macro and seems to be a normal behavior but I'm not sure why was the connection shutdown while something was being read? And this might be why it's reading 0 bytes?
#define CHECK_NULL(x) if ((x)==NULL) exit (1)
#define CHECK_ERR(err,s) if ((err)==-1) { perror(s); exit(1); }
#define CHECK_SSL(err) if ((err)==-1) { ERR_print_errors_fp(stderr); exit(2); }
void ServerConnectAndRcv(uint8_t *str)
{
int err;
int sd;
struct sockaddr_in sa;
SSL_CTX* ctx;
SSL* ssl;
char* str;
char buf [4096];
const SSL_METHOD *meth;
SSLeay_add_ssl_algorithms();
meth = TLSv1_2_client_method();
SSL_load_error_strings();
ctx = SSL_CTX_new (meth);
SSL_CTX_set_mode(ctx, SSL_MODE_AUTO_RETRY);
// create a socket and connect to the server
sd = socket (AF_INET, SOCK_STREAM, 0);
CHK_ERR(sd, "socket");
memset (&sa, '\0', sizeof(sa));
sa.sin_family = AF_INET;
sa.sin_addr.s_addr = inet_addr (<SERVER-IP>); /* Server IP */
sa.sin_port = htons (<SERVER-PORT>); /* Server Port number */
ssl = SSL_new (ctx);
CHECK_NULL(ssl);
SSL_set_fd (ssl, sd);
err = SSL_connect (ssl);
CHECK_SSL(err);
// read from the server
err = SSL_read (ssl, buf, sizeof(buf) - 1);
CHECK_SSL(err);
if (err <= 0)
{
printf ("Error reading: %d\n", SSL_get_error(ssl, err));
}
buf[err] = '\0';
printf ("Received % bytes\n", err); // Received 0 bytes
SSL_shutdown (ssl);
}
As one of the commenters pointed out, I needed to send a GET request to be able to receive a response back from the server.
I sent pszResourcePath via SSL_write() and SSL_read() read the entire response just fine.
char pszRequest[100]= {0};
char pszResourcePath[]="<resourcePath>";
char pszHostAddress[]="<serverIP>";
sprintf(pszRequest, "GET /%s HTTP/1.1\r\nHost: %s\r\nConnection: Keep-Alive\r\n\r\n", pszResourcePath, pszHostAddress);

Openssl API Client Hello callback function is never called

I am trying to use the openssl api to implement the JA3 method (TLS fingerprinting method) on a personnal project. To do that I need to get some informations on the Client Hello packet and I am trying to do that with the openssl api https://www.openssl.org/docs/man1.1.1/man3/ . Firstly I downloaded a code which set up a TLS connection between my computer and a website and retrieved the certificat information. This program is running well, the tls connection is using TLS 1.3 and the certificat information are ok. Then I tried to retrieved the Client Hello packet to start implement the JA3 method. After few researches I found many function that may allow me to get the information I need : https://www.openssl.org/docs/man1.1.1/man3/SSL_client_hello_cb_fn.html . All of those function can only be call on the client hello callback function. This function can be called thanks to the SSL_CTX_set_client_hello_cb function. But in my case my function is never called. I am starting to be hopeless it's why I need your help. I really searched a lot to debug that but I didn't find a flag that can allow the callback function or something like that. Here is the program :
#include <sys/socket.h>
#include <resolv.h>
#include <netdb.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <string.h>
#include <openssl/bio.h>
#include <openssl/ssl.h>
#include <openssl/err.h>
#include <openssl/pem.h>
#include <openssl/x509.h>
#include <openssl/x509_vfy.h>
int create_socket(char[], BIO *);
int callback(SSL *s, int *al, void *arg)
{
int * number = arg;
*number = (*number) + 1;
printf("\nWe are in the callback function !\n");
return SSL_CLIENT_HELLO_SUCCESS;
}
int main() {
char dest_url[] = "https://www.hp.com";
BIO *certbio = NULL;
BIO *outbio = NULL;
X509 *cert = NULL;
X509_NAME *certname = NULL;
const SSL_METHOD *method;
SSL_CTX *ctx;
struct ssl_st *ssl;
int server = 0;
OpenSSL_add_all_algorithms();
ERR_load_BIO_strings();
ERR_load_crypto_strings();
SSL_load_error_strings();
certbio = BIO_new(BIO_s_file());
outbio = BIO_new_fp(stdout, BIO_NOCLOSE);
if(SSL_library_init() < 0)
BIO_printf(outbio, "Could not initialize the OpenSSL library !\n");
method = TLS_client_method();
if ( (ctx = SSL_CTX_new(method)) == NULL)
BIO_printf(outbio, "Unable to create a new SSL context structure.\n");
int hope = 12;
SSL_CTX_set_client_hello_cb(ctx, &callback, (void *)&hope);
ssl = SSL_new(ctx);
server = create_socket(dest_url, outbio);
if(server != 0)
BIO_printf(outbio, "Successfully made the TCP connection to: %s.\n", dest_url);
SSL_set_fd(ssl, server);
if ( SSL_connect(ssl) != 1 )
BIO_printf(outbio, "Error: Could not build a SSL session to: %s.\n", dest_url);
else
BIO_printf(outbio, "Successfully enabled SSL/TLS session to: %s.\n", dest_url);
cert = SSL_get_peer_certificate(ssl);
if (cert == NULL)
BIO_printf(outbio, "Error: Could not get a certificate from: %s.\n", dest_url);
else
BIO_printf(outbio, "Retrieved the server's certificate from: %s.\n", dest_url);
certname = X509_NAME_new();
certname = X509_get_subject_name(cert);
BIO_printf(outbio, "Displaying the certificate subject data:\n");
X509_NAME_print_ex(outbio, certname, 0, 0);
BIO_printf(outbio, "\n");
SSL_free(ssl);
close(server);
X509_free(cert);
SSL_CTX_free(ctx);
BIO_printf(outbio, "Finished SSL/TLS connection with server: %s.\n", dest_url);
printf("Hope value : %d \n", hope);
return(0);
}
int create_socket(char url_str[], BIO *out) {
int sockfd;
char hostname[256] = "";
char portnum[6] = "443";
char proto[6] = "";
char *tmp_ptr = NULL;
int port;
struct hostent *host;
struct sockaddr_in dest_addr;
if(url_str[strlen(url_str)] == '/')
url_str[strlen(url_str)] = '\0';
strncpy(proto, url_str, (strchr(url_str, ':')-url_str));
strncpy(hostname, strstr(url_str, "://")+3, sizeof(hostname));
if(strchr(hostname, ':')) {
tmp_ptr = strchr(hostname, ':');
/* the last : starts the port number, if avail, i.e. 8443 */
strncpy(portnum, tmp_ptr+1, sizeof(portnum));
*tmp_ptr = '\0';
}
port = atoi(portnum);
if ( (host = gethostbyname(hostname)) == NULL ) {
BIO_printf(out, "Error: Cannot resolve hostname %s.\n", hostname);
abort();
}
sockfd = socket(AF_INET, SOCK_STREAM, 0);
dest_addr.sin_family=AF_INET;
dest_addr.sin_port=htons(port);
dest_addr.sin_addr.s_addr = *(long*)(host->h_addr);
memset(&(dest_addr.sin_zero), '\0', 8);
tmp_ptr = inet_ntoa(dest_addr.sin_addr);
if ( connect(sockfd, (struct sockaddr *) &dest_addr,
sizeof(struct sockaddr)) == -1 ) {
BIO_printf(out, "Error: Cannot connect to host %s [%s] on port %d.\n",
hostname, tmp_ptr, port);
}
return sockfd;
}
I am waiting for your answer thanks guys.
The hint for what is going wrong is from the first line of the description in the doc that you linked to:
SSL_CTX_set_client_hello_cb() sets the callback function, which is automatically called during the early stages of ClientHello processing on the server.
This is called by a server when processing the received ClientHello. You have written a client and therefore it is never called.
As an alternative I suggest you look at SSL_CTX_set_msg_callback():
https://www.openssl.org/docs/man1.1.1/man3/SSL_CTX_set_msg_callback.html
From the docs:
SSL_CTX_set_msg_callback() or SSL_set_msg_callback() can be used to define a message callback function cb for observing all SSL/TLS protocol messages (such as handshake messages) that are received or sent, as well as other events that occur during processing.

SMTP client in C blocks on SSL_read

I have been working on a small project which is a SMTP client written in C with SSL.
I am trying to get it working with GMail. I have enabled the allow unsecure apps in GMail settings.
When I try to authenticate using the OpenSSL CLI with the command openssl s_client -connect smtp.gmail.com:465 it works just fine I get proper responses for HELO and EHLO commands (see the output below)
220 smtp.gmail.com ESMTP o3-v6sm18136158pgv.53 - gsmtp
HELO smtp.gmail.com
250 smtp.gmail.com at your service
AUTH LOGIN
334 VXNlcm5hbWU6
But once I run this with my C client it hangs on SSL_read after I send HELO smtp.gmail.com and it does not even give me any output for HELO smtp.gmail.com.
But if I send just HELO I get a proper response from the server saying that empty HELO isn't allowed
MAIN
int main(int argc, char **argv) {
setup("smtp.gmail.com", 465);
printf("[INFO] -- %s\n", recv_secure());
send_secure("HELO smtp.gmail.com\r\n");
printf("[INFO] -- %s\n", recv_secure());
send_secure("AUTH LOGIN\r\n");
printf("[INFO] -- %s\n", recv_secure());
close_secure();
return 0;
}
setup
int setup(char *hostname, int port) {
SSL_CTX *ctx = init();
struct hostent *host;
struct sockaddr_in addr;
int sock_fd;
if((host = gethostbyname(hostname)) == NULL) {
fprintf(stderr, "[ERROR] Unable to get host by name: %s", hostname);
return -1;
}
sock_fd = socket(AF_INET, SOCK_STREAM, IPPROTO_IP);
bzero(&addr, sizeof(addr));
addr.sin_family = AF_INET;
addr.sin_port = htons(port);
addr.sin_addr.s_addr = *(long*)host->h_addr_list[0];
if(connect(sock_fd, (struct sockaddr*) &addr, sizeof(addr)) != 0) {
close(sock_fd);
fprintf(stderr, "[ERROR] Unable to connect to remote server: %s\n", hostname);
return -1;
}
ssl = SSL_new(ctx);
SSL_set_fd(ssl, sock_fd);
if(SSL_connect(ssl) == -1) {
fprintf(stderr, "[ERROR] Unable to establish secure connection\n");
close(sock_fd);
return -1;
}
return sock_fd;
}
send_secure
void send_secure(const char* buffer){
SSL_write(ssl, buffer, sizeof(buffer));
}
recv_secure
char* recv_secure() {
int bytes = SSL_read(ssl, rbuffer, SIZE);
rbuffer[bytes] = 0;
fprintf(stderr, "[DEBUG] -- Received: %s\n", rbuffer);
return rbuffer;
}
I ran ltrace with it and it seems to be blocked at SSL_read but SSL_pending return value is 0
I cannot seem to understand what am I missing here.
void send_secure(const char* buffer){
SSL_write(ssl, mbuffer, sizeof(mbuffer));
}
Assuming that mbuffer is a typo and buffer was meant instead, this will write either 8 or 4 bytes (the size of a pointer, depending on your platform), not the length of the buffer as you probably expected (which will be wrong, too since sizeof, when applied to a literal string, also includes the terminating null byte -- which will NOT be accepted by the smtp server).
Changing sizeof to strlen may let you proceed further, but your code has a lot of problems, for instance:
int bytes = SSL_read(ssl, rbuffer, SIZE);
rbuffer[bytes] = 0;
If SSL_read returns -1, this will (in the best case ;-)) crash your program.

Resources