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?
Edit 1
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.
CodePudding user response:
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);
}