#include <pthread.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <openssl/err.h>
#include <openssl/ssl.h>
int opt = 0;
while ((opt = getopt(argc, argv, "V:")) != EOF) {
if (opt == 'V') {
if (!strncmp("LOG_NULL", optarg, 8))
else if (!strncmp("LOG_NOTICE", optarg, 10))
else if (!strncmp("LOG_INFO", optarg, 8))
else if (!strncmp("LOG_ERR", optarg, 7))
else if (!strncmp("LOG_DEBUG", optarg, 9))
}
}
}
#if N_OSSL_OPENSSL_ATLEAST(0x10101000L)
if (!ok) {
} else {
}
}
typedef struct {
const char* what;
int max_version;
const char* ciphers;
int expect_ok;
const char* expect_v;
} client_case;
static const client_case cases[] = {
{"TLS 1.1 client refused", TLS1_1_VERSION, NULL, 0, NULL},
{"TLS 1.2 RSA key exchange (no forward secrecy) refused", TLS1_2_VERSION, "AES128-GCM-SHA256:AES256-GCM-SHA384", 0, NULL},
{"TLS 1.2 CBC cipher refused", TLS1_2_VERSION, "ECDHE-RSA-AES128-SHA:ECDHE-RSA-AES256-SHA", 0, NULL},
{"TLS 1.2 ECDHE-GCM accepted", TLS1_2_VERSION, "ECDHE-RSA-AES128-GCM-SHA256", 1, "TLSv1.2"},
{"default client gets TLS 1.3", 0, NULL, 1, "TLSv1.3"},
};
static SSL_CTX* client_ctx(const client_case* c) {
SSL_CTX* ctx = SSL_CTX_new(TLS_client_method());
if (!ctx)
return NULL;
SSL_CTX_set_security_level(ctx, 0);
SSL_CTX_set_min_proto_version(ctx, TLS1_VERSION);
if (c->max_version)
SSL_CTX_set_max_proto_version(ctx, c->max_version);
if (c->ciphers)
SSL_CTX_set_cipher_list(ctx, c->ciphers);
SSL_CTX_set_verify(ctx, SSL_VERIFY_NONE, NULL);
return ctx;
}
static int bio_handshake(SSL_CTX* sctx, SSL_CTX* cctx, char* version, size_t vsz) {
SSL* s = SSL_new(sctx);
SSL* c = SSL_new(cctx);
BIO *bs = NULL, *bc = NULL;
int i, done_s = 0, done_c = 0, ok = 0;
if (!s || !c || BIO_new_bio_pair(&bs, 0, &bc, 0) != 1)
goto out;
SSL_set_bio(s, bs, bs);
SSL_set_bio(c, bc, bc);
SSL_set_accept_state(s);
SSL_set_connect_state(c);
for (i = 0; i < 64 && !(done_s && done_c); i++) {
int rc = SSL_do_handshake(c), rs;
if (rc == 1)
done_c = 1;
else if (SSL_get_error(c, rc) != SSL_ERROR_WANT_READ && SSL_get_error(c, rc) != SSL_ERROR_WANT_WRITE)
break;
rs = SSL_do_handshake(s);
if (rs == 1)
done_s = 1;
else if (SSL_get_error(s, rs) != SSL_ERROR_WANT_READ && SSL_get_error(s, rs) != SSL_ERROR_WANT_WRITE)
break;
}
ok = done_s && done_c;
if (ok)
snprintf(version, vsz, "%s", SSL_get_version(c));
out:
ERR_clear_error();
SSL_free(s);
SSL_free(c);
return ok;
}
static N_STR *ca_cert = NULL, *ca_key = NULL, *leaf_cert = NULL, *leaf_key = NULL;
static void test_pem_context(void) {
int p;
for (p = 18400; p < 18500 && !srv; p++) {
}
if (!srv)
return;
STACK_OF(SSL_CIPHER)* list = SSL_CTX_get_ciphers(srv->
ctx);
int i, only_good = 1;
expect_true(
"minimum version is TLS 1.2", SSL_CTX_get_min_proto_version(srv->
ctx) == TLS1_2_VERSION);
expect_true(
"no compression", (SSL_CTX_get_options(srv->
ctx) & SSL_OP_NO_COMPRESSION) != 0);
expect_true(
"no session tickets", (SSL_CTX_get_options(srv->
ctx) & SSL_OP_NO_TICKET) != 0);
expect_true(
"server cipher order", (SSL_CTX_get_options(srv->
ctx) & SSL_OP_CIPHER_SERVER_PREFERENCE) != 0);
#ifdef SSL_OP_NO_RENEGOTIATION
expect_true(
"no renegotiation", (SSL_CTX_get_options(srv->
ctx) & SSL_OP_NO_RENEGOTIATION) != 0);
#endif
for (i = 0; list && i < sk_SSL_CIPHER_num(list); i++) {
const char* name = SSL_CIPHER_get_name(sk_SSL_CIPHER_value(list, i));
int tls13 = strncmp(name, "TLS_", 4) == 0;
int good12 = strncmp(name, "ECDHE-", 6) == 0 && (strstr(name, "GCM") || strstr(name, "CHACHA20"));
if (!tls13 && !good12) {
only_good = 0;
}
}
expect_true(
"only forward-secret AEAD ciphers", only_good && list && sk_SSL_CIPHER_num(list) > 0);
for (i = 0; i < (int)(sizeof(cases) / sizeof(cases[0])); i++) {
char v[32] = "";
SSL_CTX* cctx = client_ctx(&cases[i]);
int ok = cctx && bio_handshake(srv->
ctx, cctx, v,
sizeof(v));
expect_true(cases[i].what, ok == cases[i].expect_ok && (!ok || !cases[i].expect_v || strcmp(v, cases[i].expect_v) == 0));
SSL_CTX_free(cctx);
}
}
}
typedef struct {
int accepted;
} sni_srv;
static int pick_cb(
const char* sni,
N_STR** cert_pem,
N_STR** key_pem,
void* user_data) {
(void)sni;
(void)user_data;
return (*cert_pem && *key_pem) ? 0 : -1;
}
static void* sni_server(void* p) {
sni_srv* s = (sni_srv*)p;
s->accepted = c != NULL;
if (c) {
char b[4];
SSL_read(c->
ssl, b,
sizeof(b));
}
return NULL;
}
static void test_sni_path(void) {
int p, i;
for (p = 18500; p < 18600 && !listen; p++) {
}
if (!listen)
return;
for (i = 0; i < (int)(sizeof(cases) / sizeof(cases[0])); i++) {
sni_srv s = {listen, 0};
pthread_t th;
SSL_CTX* cctx = client_ctx(&cases[i]);
int ok = 0;
char v[32] = "";
pthread_create(&th, NULL, sni_server, &s);
SSL* ssl = SSL_new(cctx);
SSL_set_tlsext_host_name(ssl, "localhost");
ok = SSL_connect(ssl) == 1;
if (ok)
snprintf(v, sizeof(v), "%s", SSL_get_version(ssl));
SSL_shutdown(ssl);
SSL_free(ssl);
ERR_clear_error();
}
pthread_join(th, NULL);
{
char what[160];
snprintf(what, sizeof(what), "SNI path: %s", cases[i].what);
expect_true(what, ok == cases[i].expect_ok && s.accepted == cases[i].expect_ok &&
(!ok || !cases[i].expect_v || strcmp(v, cases[i].expect_v) == 0));
}
SSL_CTX_free(cctx);
}
}
#endif
int main(
int argc,
char** argv) {
#if !N_OSSL_OPENSSL_ATLEAST(0x10101000L)
n_log(
LOG_NOTICE,
"ex_network_tls_defaults: skipped, needs OpenSSL 1.1.1 or later");
return 0;
#else
return 1;
}
test_pem_context();
test_sni_path();
return 1;
}
return 0;
#endif
}
void process_args(int argc, char **argv)
static void expect_true(const char *label, int cond)
static int pick_cb(const char *sni, N_STR **cert_pem, N_STR **key_pem, void *user_data)
#define n_log(__LEVEL__,...)
Logging function wrapper to get line and func.
#define LOG_DEBUG
debug-level messages
#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_NOTICE
normal but significant condition
#define LOG_NULL
no log output
#define LOG_INFO
informational
#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.
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.
N_SOCKET link
networking socket
SOCKET sock
a normal socket
SSL_CTX * ctx
SSL context holder.
int netw_set_crypto_pem(NETWORK *netw, const char *key_pem, const char *cert_pem)
activate SSL encryption using PEM-formatted key and certificate strings loaded from memory
int netw_init_openssl(void)
Do not directly use, internal api.
int netw_make_listening(NETWORK **netw, char *addr, char *port, int nbpending, int ip_version)
Make a NETWORK be a Listening network.
int netw_unload_openssl(void)
Do not directly use, internal api.
#define NETWORK_IPALL
Flag for auto detection by OS of ip version to use.
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(NETWORK **netw, char *host, char *port, int ip_version)
Use this to connect a NETWORK to any listening one, unrestricted send/recv lists.
Common headers and low-level functions & define.
N_STR and string function declaration.
X.509 helpers: self-signed CA generation and per-host leaf minting.