netw_ssl_do_handshake checks the NAME on the peer certificate, not only that its chain is trusted.
netw_ssl_do_handshake checks the NAME on the peer certificate, not only that its chain is trusted. SNI says which certificate the server should send. It says nothing about which names are acceptable on it. Without the peer identity that netw_ssl_do_handshake now sets, SSL_VERIFY_PEER validates the chain and stops there, so a certificate issued for any domain the holder controls is accepted for EVERY host: exactly the interception a TLS client exists to detect.
This mints one CA, issues a leaf for "right.test" from it, serves that leaf, and connects twice against the same CA: once asking for "right.test" (accepted) and once asking for "wrong.test" (refused). With verification off the handshake completes either way and the mismatch is reported by netw_ssl_get_verify_result.
#include <pthread.h>
#include <stdio.h>
#include <string.h>
#define SSL_HOSTNAME_PORT "27433"
#define SSL_HOSTNAME_RIGHT "right.test"
#define SSL_HOSTNAME_WRONG "wrong.test"
typedef struct {
int ready;
(void)sni;
return (*cert_pem && *key_pem) ? 0 : -1;
}
return NULL;
}
if (peer)
}
return NULL;
}
static int try_connect(
const char* ask,
const char* ca_path,
int verify,
int* out_verify_ok) {
char err[256] = "";
int shook;
*out_verify_ok = 0;
return 0;
if (shook)
return shook;
}
pthread_t th;
const char* ca_path = "/tmp/nilorea_ex_ssl_hostname_ca.pem";
int errors = 0;
int shook, vok;
return 1;
}
{
FILE* cf = fopen(ca_path, "wb");
if (!cf || fwrite(ca_cert->
data, 1, (
size_t)ca_cert->
written, cf) != (
size_t)ca_cert->
written) {
if (cf)
fclose(cf);
n_log(
LOG_ERR,
"could not write the test CA to %s", ca_path);
return 1;
}
fclose(cf);
}
memset(&sa, 0, sizeof(sa));
return 1;
}
return 1;
}
if (shook) {
n_log(
LOG_INFO,
"PASS(verify on, right name): the handshake completed");
} else {
errors++;
}
if (!shook) {
n_log(
LOG_INFO,
"PASS(verify on, wrong name): the handshake was refused");
} else {
errors++;
}
if (shook) {
n_log(
LOG_INFO,
"PASS(verify off, wrong name): the handshake still completed");
} else {
n_log(
LOG_ERR,
"FAIL(verify off, wrong name): setting the identity broke a non-verifying caller");
errors++;
}
{
}
pthread_join(th, NULL);
remove(ca_path);
if (errors > 0) {
n_log(
LOG_ERR,
"ex_network_ssl_hostname: %d check(s) failed", errors);
exit(1);
}
exit(0);
}
static void * server_thread(void *arg)
Thread function that runs the server accept loop.
#define SSL_HOSTNAME_RIGHT
Name the served leaf certificate is actually issued for.
static int try_connect(const char *ask, const char *ca_path, int verify, int *out_verify_ok)
Connect asking for ask and report what verification concluded.
#define SSL_HOSTNAME_WRONG
Name that certificate does NOT carry.
static int pick_cert(const char *sni, N_STR **cert_pem, N_STR **key_pem, void *user_data)
SNI pick callback: always serve the one leaf this test minted.
#define SSL_HOSTNAME_PORT
Port the local TLS listener binds for this test.
Carries the served leaf certificate to the SNI pick callback.
volatile char key[227]
TRUE while the key is held.
#define n_log(__LEVEL__,...)
Logging function wrapper to get line and func.
#define LOG_ERR
error conditions
void set_log_level(const int log_level)
Set the global log level value ( static int LOG_LEVEL ).
#define LOG_INFO
informational
size_t written
number of meaningful bytes in data, excluding the null terminator; the size including the null termin...
#define free_nstr(__ptr)
free a N_STR structure and set the pointer to NULL
N_STR * nstrdup(N_STR *str)
Duplicate a N_STR.
A box including a string and his lenght.
void u_sleep(unsigned int usec)
wrapper around usleep for API consistency
int n_x509_generate_ca(const char *cn, int days, N_STR **ca_cert_pem, N_STR **ca_key_pem)
Generate a self-signed certificate authority (CA) keypair and cert.
int n_x509_mint_host_cert(const char *host, const N_STR *ca_cert_pem, const N_STR *ca_key_pem, int days, N_STR **leaf_cert_pem, N_STR **leaf_key_pem)
Mint a per-host leaf certificate signed by the given CA.
int netw_ssl_get_verify_result(NETWORK *netw, const char *expected_host, char *errbuf, size_t errsz)
evaluate the peer certificate of a completed TLS client connection
int netw_ssl_set_verify(NETWORK *netw, int enable)
enable or disable SSL peer certificate verification
int netw_ssl_set_ca(NETWORK *netw, const char *ca_file, const char *ca_path)
set custom CA verify location for SSL context
int netw_make_listening(NETWORK **netw, char *addr, char *port, int nbpending, int ip_version)
Make a NETWORK be a Listening network.
int netw_ssl_do_handshake(NETWORK *netw, const char *sni_hostname)
Complete the SSL handshake on an already-connected NETWORK.
#define NETWORK_IPV4
Flag to force IPV4.
int netw_unload_openssl(void)
Do not directly use, internal api.
int netw_ssl_connect_client_to(NETWORK **netw, char *host, char *port, int ip_version, int connect_timeout_ms)
Connect as an SSL client without providing a client certificate.
NETWORK * netw_accept_ssl_with_sni_cb(NETWORK *listen, netw_sni_pick_cb pick, void *user_data)
accept a TLS connection, selecting the server certificate per client SNI
int netw_close(NETWORK **netw)
Closing a specified Network, destroy queues, free the structure.
int netw_connect_to(NETWORK **netw, char *host, char *port, int ip_version, int connect_timeout_ms)
Connect a NETWORK with a bounded connection-establishment time.
N_STR and string function declaration.
X.509 helpers: self-signed CA generation and per-host leaf minting.