I made the following piece of software that sends a Https get request towards google's homepage via SSL:
#include <stdio.h>
#include<string.h>
#include <openssl/ssl.h>
#include <openssl/err.h>
#include<sys/types.h>
#include<sys/socket.h>
#include<netinet/in.h>
#include<arpa/inet.h>
#include <netdb.h>
#include <unistd.h>
#include <stdlib.h>
#ifndef INADDR_NONE
#define INADDR_NONE 0xffffffff
#endif //INADDR_NONE
typedef int SOCKET;
SOCKET OpenConnection(char* hostname, char* port){
struct hostent *phe;
struct sockaddr_in sin;
unsigned int port_as_integer = htons((unsigned short)atoi(port));
int sock;
memset(&sin,0,sizeof(sin));
sin.sin_family = AF_INET;
if(port_as_integer == 0){
return -1;
}
sin.sin_port = port_as_integer;
if(phe = gethostbyname(hostname)){
memcpy(&sin.sin_addr,phe->h_addr, phe->h_length);
} else if((sin.sin_addr.s_addr = inet_addr(hostname)) == INADDR_NONE){
return -1;
}
sock = socket(PF_INET,SOCK_STREAM,6);
if(socket < 0 || connect(sock, (const struct sockaddr *)&sin, sizeof(sin) ) < 0 ){
return -1;
}
return (SOCKET) sock;
}
int verifyCerts( SSL_CTX* ctx )
{
const char *path = getenv(X509_get_default_cert_dir_env());
if (!path){
path = X509_get_default_cert_dir();
}
return SSL_CTX_load_verify_locations(ctx,NULL,path);
}
void releaseSocket( SSL_CTX* ctx, int server)
{
/* close socket */
close(server);
/* release context */
SSL_CTX_free(ctx);
putchar('\n');
}
SSL_CTX* InitCTX(void)
{
OpenSSL_add_all_algorithms();
SSL_load_error_strings();
const SSL_METHOD* method = SSLv23_method();
SSL_CTX* ctx = SSL_CTX_new(method);
SSL_CTX_set_options(ctx, SSL_OP_ALL | SSL_OP_NO_TLSv1_1 | SSL_OP_NO_TLSv1);
if (ctx == NULL)
{
ERR_print_errors_fp(stderr);
abort();
}
int value = verifyCerts( ctx );
if(value == 0) {
printf("Certificate error\n");
exit(1);
}
SSL_CTX_set_verify(ctx,SSL_VERIFY_PEER,NULL);
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);
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 argc, char* argv[])
{
printf("Initializing Connection");
char buf[1024];
SSL_library_init();
char* hostname = "google.com";
char* portnum = "443";
int bytes=0,error=0;
const char* cpRequestMessage = "GET / HTTP/1.1\r\nHost: www.google.com\r\nUser-Agent: Mozilla/5.0 (Android 4.4; Tablet; rv:41.0) Gecko/41.0 Firefox/41.0\r\nConnection: close\r\nAccept: text/html;UTF-8\r\nAccept-Lang: gr\r\n\r\n";
SSL_CTX* ctx = InitCTX();
int server = OpenConnection(hostname, portnum);
SSL* ssl = SSL_new(ctx); /* create new SSL connection state */
SSL_set_fd(ssl, server); /* attach the socket descriptor */
if (SSL_connect(ssl) == FAIL) {
ERR_print_errors_fp(stderr);
} else {
printf("\n\nConnected with %s encryption\n", SSL_get_cipher(ssl));
/* encrypt & send message */
printf("REQUEST:\n\n%s\n",cpRequestMessage);
SSL_write(ssl, cpRequestMessage, strlen(cpRequestMessage));
do {
int bytes = SSL_read(ssl, buf, sizeof(buf));
int error = SSL_get_error(ssl,bytes);
switch (error)
{
case SSL_ERROR_SSL:
puts("SSL ERROR SSL");
releaseSocket(ctx,server);
return 1;
case SSL_ERROR_SYSCALL:
puts("SSL ERROR SYSCALL");
releaseSocket(ctx,server);
return 1;
case SSL_ERROR_WANT_ASYNC_JOB:
puts("SSL ERROR WANT ASYNC_LOOKUP");
releaseSocket(ctx,server);
return 1;
case SSL_ERROR_WANT_ASYNC:
puts("SSL ERROR WANT X509_LOOKUP");
releaseSocket(ctx,server);
return 1;
case SSL_ERROR_WANT_X509_LOOKUP:
puts("SSL ERROR WANT X509_LOOKUP");
releaseSocket(ctx,server);
return 1;
case SSL_ERROR_WANT_WRITE:
puts("SSL ERROR WANT WRITE");
releaseSocket(ctx,server);
return 1;
case SSL_ERROR_WANT_READ:
puts("SSL ERROR WANT READ");
releaseSocket(ctx,server);
return 1;
case SSL_ERROR_ZERO_RETURN:
puts("SSL ERROR SSL_ERROR_ZERO_RETURN");
releaseSocket(ctx,server);
return 1;
case SSL_ERROR_NONE:
default:
break;
}
puts(buf);
} while (bytes>0);
/* release connection state */
SSL_free(ssl);
}
releaseSocket(ctx,server);
putchar('\n');
return 0;
}
The verification happens using system's CA path folder. But for some reason SSL certificates are not verified:
39690623349184:error:1416F086:SSL routines:tls_process_server_certificate:certificate verify failed:../ssl/statem/statem_clnt.c:1924:
Initializing Connection
Do you know the reason why that happens?
I also tried to manually place the system's certificate path:
First I detected the path (despite the command that fails I just need to find the location of the certs.
update-ca-certificates --verbose
Updating certificates in /etc/ssl/certs...
/usr/sbin/update-ca-certificates: 101: /usr/sbin/update-ca-certificates: cannot create /etc/ssl/certs/ca-certificates.crt.new: Permission denied
Then I tried doing the following:
int verifyCerts( SSL_CTX* ctx )
{
return SSL_CTX_load_verify_locations(ctx,NULL,"/etc/ssl/certs");
}
By manually specifying the path as /etc/ssl/certs
. The rationale is to check whether or not I can verify using a hardcoded location for the certificates provided from my system.
Once I rebuilt my application, I found out that still fails:
139850890965440:error:1416F086:SSL routines:tls_process_server_certificate:certificate verify failed:../ssl/statem/statem_clnt.c:1924:
Initializing Connection
But the path is full of certificates:
$ ls -l /etc/ssl/certs/ | head
σύνολο 560
lrwxrwxrwx 1 root root 23 Ιουν 9 21:35 002c0b4f.0 -> GlobalSign_Root_R46.pem
lrwxrwxrwx 1 root root 45 Μαΐ 8 2020 02265526.0 -> Entrust_Root_Certification_Authority_-_G2.pem
lrwxrwxrwx 1 root root 36 Μαΐ 8 2020 03179a64.0 -> Staat_der_Nederlanden_EV_Root_CA.pem
lrwxrwxrwx 1 root root 27 Μαΐ 8 2020 062cdee6.0 -> GlobalSign_Root_CA_-_R3.pem
lrwxrwxrwx 1 root root 25 Μαΐ 8 2020 064e0aa9.0 -> QuoVadis_Root_CA_2_G3.pem
lrwxrwxrwx 1 root root 50 Μαΐ 8 2020 06dc52d5.0 -> SSL.com_EV_Root_Certification_Authority_RSA_R2.pem
lrwxrwxrwx 1 root root 54 Μαΐ 8 2020 09789157.0 -> Starfield_Services_Root_Certificate_Authority_-_G2.pem
lrwxrwxrwx 1 root root 15 Οκτ 30 2020 0a775a30.0 -> GTS_Root_R3.pem
lrwxrwxrwx 1 root root 16 Μαΐ 8 2020 0b1b94ef.0 -> CFCA_EV_ROOT.pem
All of them symlinked from various paths.
You are unable to verify the certificate because you need to place the following line (as specified upon this answer) to main function:
int res = SSL_set_tlsext_host_name(ssl, hostname);
if(res == 0) {
ERR_print_errors_fp(stderr);
}
Resulting this main:
int main(int argc, char* argv[])
{
printf("Initializing Connection");
char buf[1024];
SSL_library_init();
char* hostname = "google.com";
char* portnum = "443";
int bytes=0,error=0;
const char* cpRequestMessage = "GET / HTTP/1.1\r\nHost: www.google.com\r\nUser-Agent: Mozilla/5.0 (Android 4.4; Tablet; rv:41.0) Gecko/41.0 Firefox/41.0\r\nConnection: close\r\nAccept: text/html;UTF-8\r\nAccept-Lang: gr\r\n\r\n";
SSL_CTX* ctx = InitCTX();
int server = OpenConnection(hostname, portnum);
SSL* ssl = SSL_new(ctx); /* create new SSL connection state */
SSL_set_fd(ssl, server); /* attach the socket descriptor */
int res = SSL_set_tlsext_host_name(ssl, hostname);
if(res == 0) {
ERR_print_errors_fp(stderr);
}
if (SSL_connect(ssl) == FAIL) {
ERR_print_errors_fp(stderr);
} else {
printf("\n\nConnected with %s encryption\n", SSL_get_cipher(ssl));
/* encrypt & send message */
printf("REQUEST:\n\n%s\n",cpRequestMessage);
SSL_write(ssl, cpRequestMessage, strlen(cpRequestMessage));
do {
int bytes = SSL_read(ssl, buf, sizeof(buf));
int error = SSL_get_error(ssl,bytes);
switch (error)
{
case SSL_ERROR_SSL:
puts("SSL ERROR SSL");
releaseSocket(ctx,server);
return 1;
case SSL_ERROR_SYSCALL:
puts("SSL ERROR SYSCALL");
releaseSocket(ctx,server);
return 1;
case SSL_ERROR_WANT_ASYNC_JOB:
puts("SSL ERROR WANT ASYNC_LOOKUP");
releaseSocket(ctx,server);
return 1;
case SSL_ERROR_WANT_ASYNC:
puts("SSL ERROR WANT X509_LOOKUP");
releaseSocket(ctx,server);
return 1;
case SSL_ERROR_WANT_X509_LOOKUP:
puts("SSL ERROR WANT X509_LOOKUP");
releaseSocket(ctx,server);
return 1;
case SSL_ERROR_WANT_WRITE:
puts("SSL ERROR WANT WRITE");
releaseSocket(ctx,server);
return 1;
case SSL_ERROR_WANT_READ:
puts("SSL ERROR WANT READ");
releaseSocket(ctx,server);
return 1;
case SSL_ERROR_ZERO_RETURN:
puts("SSL ERROR SSL_ERROR_ZERO_RETURN");
releaseSocket(ctx,server);
return 1;
case SSL_ERROR_NONE:
default:
break;
}
puts(buf);
} while (bytes>0);
/* release connection state */
SSL_free(ssl);
}
releaseSocket(ctx,server);
putchar('\n');
return 0;
}
Using that you can verify using systems certificates. The verifyCerts
works fine as seems here:
int verifyCerts( SSL_CTX* ctx )
{
const char *path = getenv(X509_get_default_cert_dir_env());
if (!path){
path = X509_get_default_cert_dir();
}
return SSL_CTX_load_verify_locations(ctx,NULL,path);
}