Nilorea Library
C utilities for networking, threading, graphics
Loading...
Searching...
No Matches
ex_network_tls_defaults.c
Go to the documentation of this file.
1/*
2 * Nilorea Library
3 * Copyright (C) 2005-2026 Castagnier Mickael
4 *
5 * Licensed under the Apache License, Version 2.0 (the "License");
6 * you may not use this file except in compliance with the License.
7 * You may obtain a copy of the License at
8 *
9 * http://www.apache.org/licenses/LICENSE-2.0
10 *
11 * Unless required by applicable law or agreed to in writing, software
12 * distributed under the License is distributed on an "AS IS" BASIS,
13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
14 * implied. See the License for the specific language governing
15 * permissions and limitations under the License.
16 *
17 * SPDX-License-Identifier: Apache-2.0
18 */
19
30
31#include "nilorea/n_common.h"
32#include "nilorea/n_log.h"
33#include "nilorea/n_network.h"
34#include "nilorea/n_str.h"
35#include "nilorea/n_x509.h"
36
37#include <pthread.h>
38#include <stdio.h>
39#include <stdlib.h>
40#include <string.h>
41#include <unistd.h>
42
43#include <openssl/err.h>
44#include <openssl/ssl.h>
45
49static void process_args(int argc, char** argv) {
50 int opt = 0;
51 while ((opt = getopt(argc, argv, "V:")) != EOF) {
52 if (opt == 'V') {
53 if (!strncmp("LOG_NULL", optarg, 8))
55 else if (!strncmp("LOG_NOTICE", optarg, 10))
57 else if (!strncmp("LOG_INFO", optarg, 8))
59 else if (!strncmp("LOG_ERR", optarg, 7))
61 else if (!strncmp("LOG_DEBUG", optarg, 9))
63 }
64 }
65}
66
67/* The checks need the OpenSSL 1.1.1 API (version bounds, TLS 1.3, security
68 * levels); older OpenSSL and LibreSSL builds compile a stub that says so. */
69#if N_OSSL_OPENSSL_ATLEAST(0x10101000L)
70
72static int failures = 0;
73
77static void expect_true(const char* what, int ok) {
78 if (!ok) {
79 n_log(LOG_ERR, "FAIL: %s", what);
80 failures++;
81 } else {
82 n_log(LOG_INFO, "ok: %s", what);
83 }
84}
85
87typedef struct {
88 const char* what;
89 int max_version;
90 const char* ciphers;
91 int expect_ok;
92 const char* expect_v;
93} client_case;
94
96static const client_case cases[] = {
97 {"TLS 1.1 client refused", TLS1_1_VERSION, NULL, 0, NULL},
98 {"TLS 1.2 RSA key exchange (no forward secrecy) refused", TLS1_2_VERSION, "AES128-GCM-SHA256:AES256-GCM-SHA384", 0, NULL},
99 {"TLS 1.2 CBC cipher refused", TLS1_2_VERSION, "ECDHE-RSA-AES128-SHA:ECDHE-RSA-AES256-SHA", 0, NULL},
100 {"TLS 1.2 ECDHE-GCM accepted", TLS1_2_VERSION, "ECDHE-RSA-AES128-GCM-SHA256", 1, "TLSv1.2"},
101 {"default client gets TLS 1.3", 0, NULL, 1, "TLSv1.3"},
102};
103
107static SSL_CTX* client_ctx(const client_case* c) {
108 SSL_CTX* ctx = SSL_CTX_new(TLS_client_method());
109 if (!ctx)
110 return NULL;
111 /* the client side of this test must be able to offer what the server
112 * refuses, whatever the system openssl.cnf says */
113 SSL_CTX_set_security_level(ctx, 0);
114 SSL_CTX_set_min_proto_version(ctx, TLS1_VERSION);
115 if (c->max_version)
116 SSL_CTX_set_max_proto_version(ctx, c->max_version);
117 if (c->ciphers)
118 SSL_CTX_set_cipher_list(ctx, c->ciphers);
119 SSL_CTX_set_verify(ctx, SSL_VERIFY_NONE, NULL);
120 return ctx;
121}
122
129static int bio_handshake(SSL_CTX* sctx, SSL_CTX* cctx, char* version, size_t vsz) {
130 SSL* s = SSL_new(sctx);
131 SSL* c = SSL_new(cctx);
132 BIO *bs = NULL, *bc = NULL;
133 int i, done_s = 0, done_c = 0, ok = 0;
134 if (!s || !c || BIO_new_bio_pair(&bs, 0, &bc, 0) != 1)
135 goto out;
136 SSL_set_bio(s, bs, bs);
137 SSL_set_bio(c, bc, bc);
138 SSL_set_accept_state(s);
139 SSL_set_connect_state(c);
140 for (i = 0; i < 64 && !(done_s && done_c); i++) {
141 int rc = SSL_do_handshake(c), rs;
142 if (rc == 1)
143 done_c = 1;
144 else if (SSL_get_error(c, rc) != SSL_ERROR_WANT_READ && SSL_get_error(c, rc) != SSL_ERROR_WANT_WRITE)
145 break;
146 rs = SSL_do_handshake(s);
147 if (rs == 1)
148 done_s = 1;
149 else if (SSL_get_error(s, rs) != SSL_ERROR_WANT_READ && SSL_get_error(s, rs) != SSL_ERROR_WANT_WRITE)
150 break;
151 }
152 ok = done_s && done_c;
153 if (ok)
154 snprintf(version, vsz, "%s", SSL_get_version(c));
155out:
156 ERR_clear_error();
157 SSL_free(s);
158 SSL_free(c);
159 return ok;
160}
161
163static N_STR *ca_cert = NULL, *ca_key = NULL, *leaf_cert = NULL, *leaf_key = NULL;
164
166static void test_pem_context(void) {
167 NETWORK* srv = NULL;
168 char port[16];
169 int p;
170 for (p = 18400; p < 18500 && !srv; p++) {
171 snprintf(port, sizeof(port), "%d", p);
172 if (netw_make_listening(&srv, "127.0.0.1", port, 5, NETWORK_IPALL) != TRUE && srv)
173 netw_close(&srv);
174 }
175 expect_true("listening socket", srv != NULL);
176 if (!srv)
177 return;
178 expect_true("netw_set_crypto_pem", netw_set_crypto_pem(srv, leaf_key->data, leaf_cert->data) == TRUE);
179 if (srv->ctx) {
180 STACK_OF(SSL_CIPHER)* list = SSL_CTX_get_ciphers(srv->ctx);
181 int i, only_good = 1;
182 expect_true("minimum version is TLS 1.2", SSL_CTX_get_min_proto_version(srv->ctx) == TLS1_2_VERSION);
183 expect_true("no compression", (SSL_CTX_get_options(srv->ctx) & SSL_OP_NO_COMPRESSION) != 0);
184 expect_true("no session tickets", (SSL_CTX_get_options(srv->ctx) & SSL_OP_NO_TICKET) != 0);
185 expect_true("server cipher order", (SSL_CTX_get_options(srv->ctx) & SSL_OP_CIPHER_SERVER_PREFERENCE) != 0);
186#ifdef SSL_OP_NO_RENEGOTIATION
187 expect_true("no renegotiation", (SSL_CTX_get_options(srv->ctx) & SSL_OP_NO_RENEGOTIATION) != 0);
188#endif
189 for (i = 0; list && i < sk_SSL_CIPHER_num(list); i++) {
190 const char* name = SSL_CIPHER_get_name(sk_SSL_CIPHER_value(list, i));
191 int tls13 = strncmp(name, "TLS_", 4) == 0;
192 int good12 = strncmp(name, "ECDHE-", 6) == 0 && (strstr(name, "GCM") || strstr(name, "CHACHA20"));
193 if (!tls13 && !good12) {
194 n_log(LOG_ERR, "unexpected cipher enabled: %s", name);
195 only_good = 0;
196 }
197 }
198 expect_true("only forward-secret AEAD ciphers", only_good && list && sk_SSL_CIPHER_num(list) > 0);
199 for (i = 0; i < (int)(sizeof(cases) / sizeof(cases[0])); i++) {
200 char v[32] = "";
201 SSL_CTX* cctx = client_ctx(&cases[i]);
202 int ok = cctx && bio_handshake(srv->ctx, cctx, v, sizeof(v));
203 expect_true(cases[i].what, ok == cases[i].expect_ok && (!ok || !cases[i].expect_v || strcmp(v, cases[i].expect_v) == 0));
204 SSL_CTX_free(cctx);
205 }
206 }
207 netw_close(&srv);
208}
209
211typedef struct {
212 NETWORK* listen;
213 int accepted;
214} sni_srv;
215
222static int pick_cb(const char* sni, N_STR** cert_pem, N_STR** key_pem, void* user_data) {
223 (void)sni;
224 (void)user_data;
225 *cert_pem = nstrdup(leaf_cert);
226 *key_pem = nstrdup(leaf_key);
227 return (*cert_pem && *key_pem) ? 0 : -1;
228}
229
233static void* sni_server(void* p) {
234 sni_srv* s = (sni_srv*)p;
235 NETWORK* c = netw_accept_ssl_with_sni_cb(s->listen, pick_cb, NULL);
236 s->accepted = c != NULL;
237 if (c) {
238 char b[4];
239 /* wait for the client to close, so it reads the whole handshake */
240 SSL_read(c->ssl, b, sizeof(b));
241 netw_close(&c);
242 }
243 return NULL;
244}
245
247static void test_sni_path(void) {
248 NETWORK* listen = NULL;
249 char port[16];
250 int p, i;
251 for (p = 18500; p < 18600 && !listen; p++) {
252 snprintf(port, sizeof(port), "%d", p);
253 if (netw_make_listening(&listen, "127.0.0.1", port, 5, NETWORK_IPALL) != TRUE && listen)
254 netw_close(&listen);
255 }
256 expect_true("SNI listening socket", listen != NULL);
257 if (!listen)
258 return;
259 for (i = 0; i < (int)(sizeof(cases) / sizeof(cases[0])); i++) {
260 sni_srv s = {listen, 0};
261 pthread_t th;
262 NETWORK* cli = NULL;
263 SSL_CTX* cctx = client_ctx(&cases[i]);
264 int ok = 0;
265 char v[32] = "";
266 pthread_create(&th, NULL, sni_server, &s);
267 if (cctx && netw_connect(&cli, "127.0.0.1", port, NETWORK_IPALL) == TRUE && cli) {
268 SSL* ssl = SSL_new(cctx);
269 SSL_set_fd(ssl, (int)cli->link.sock);
270 SSL_set_tlsext_host_name(ssl, "localhost");
271 ok = SSL_connect(ssl) == 1;
272 if (ok)
273 snprintf(v, sizeof(v), "%s", SSL_get_version(ssl));
274 SSL_shutdown(ssl);
275 SSL_free(ssl);
276 ERR_clear_error();
277 }
278 netw_close(&cli);
279 pthread_join(th, NULL);
280 {
281 char what[160];
282 snprintf(what, sizeof(what), "SNI path: %s", cases[i].what);
283 expect_true(what, ok == cases[i].expect_ok && s.accepted == cases[i].expect_ok &&
284 (!ok || !cases[i].expect_v || strcmp(v, cases[i].expect_v) == 0));
285 }
286 SSL_CTX_free(cctx);
287 }
288 netw_close(&listen);
289}
290
291#endif /* N_OSSL_OPENSSL_ATLEAST(0x10101000L) */
292
297int main(int argc, char** argv) {
299 process_args(argc, argv);
300#if !N_OSSL_OPENSSL_ATLEAST(0x10101000L)
301 n_log(LOG_NOTICE, "ex_network_tls_defaults: skipped, needs OpenSSL 1.1.1 or later");
302 return 0;
303#else
305
306 if (n_x509_generate_ca("Nilorea TLS Defaults Test CA", 30, &ca_cert, &ca_key) != 0 ||
307 n_x509_mint_host_cert("localhost", ca_cert, ca_key, 30, &leaf_cert, &leaf_key) != 0) {
308 n_log(LOG_ERR, "could not mint the test certificates");
309 return 1;
310 }
311 test_pem_context();
312 test_sni_path();
313
314 free_nstr(&ca_cert);
315 free_nstr(&ca_key);
316 free_nstr(&leaf_cert);
317 free_nstr(&leaf_key);
319
320 if (failures) {
321 n_log(LOG_ERR, "ex_network_tls_defaults: %d failure(s)", failures);
322 return 1;
323 }
324 n_log(LOG_NOTICE, "ex_network_tls_defaults: all checks passed");
325 return 0;
326#endif
327}
void process_args(int argc, char **argv)
static int failures
Definition ex_atlas.c:51
int main(void)
Definition ex_atlas.c:117
static void expect_true(const char *label, int cond)
Definition ex_atlas.c:53
static int pick_cb(const char *sni, N_STR **cert_pem, N_STR **key_pem, void *user_data)
char * port
#define n_log(__LEVEL__,...)
Logging function wrapper to get line and func.
Definition n_log.h:113
#define LOG_DEBUG
debug-level messages
Definition n_log.h:108
#define LOG_ERR
error conditions
Definition n_log.h:100
void set_log_level(const int log_level)
Set the global log level value ( static int LOG_LEVEL ).
Definition n_log.c:123
#define LOG_NOTICE
normal but significant condition
Definition n_log.h:104
#define LOG_NULL
no log output
Definition n_log.h:70
#define LOG_INFO
informational
Definition n_log.h:106
#define free_nstr(__ptr)
free a N_STR structure and set the pointer to NULL
Definition n_str.h:203
N_STR * nstrdup(N_STR *str)
Duplicate a N_STR.
Definition n_str.c:723
A box including a string and his lenght.
Definition n_str.h:61
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.
Definition n_x509.c:226
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.
Definition n_x509.c:285
N_SOCKET link
networking socket
Definition n_network.h:454
SOCKET sock
a normal socket
Definition n_network.h:356
SSL_CTX * ctx
SSL context holder.
Definition n_network.h:444
SSL * ssl
SSL handle.
Definition n_network.h:446
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
Definition n_network.c:1597
int netw_init_openssl(void)
Do not directly use, internal api.
Definition n_network.c:1335
int netw_make_listening(NETWORK **netw, char *addr, char *port, int nbpending, int ip_version)
Make a NETWORK be a Listening network.
Definition n_network.c:3602
int netw_unload_openssl(void)
Do not directly use, internal api.
Definition n_network.c:1368
#define NETWORK_IPALL
Flag for auto detection by OS of ip version to use.
Definition n_network.h:48
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
Definition n_network.c:2116
int netw_close(NETWORK **netw)
Closing a specified Network, destroy queues, free the structure.
Definition n_network.c:3367
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.
Definition n_network.c:2898
Structure of a NETWORK.
Definition n_network.h:375
Common headers and low-level functions & define.
Generic log system.
Network Engine.
N_STR and string function declaration.
X.509 helpers: self-signed CA generation and per-host leaf minting.