blob: ed89fb25071c660fbed331e4b4f81d8c5fd2865a [file] [log] [blame]
Radek Krejci5da708a2015-09-01 17:33:23 +02001/**
Michal Vasko95ea9ff2021-11-09 12:29:14 +01002 * @file session_server_tls.c
3 * @author Michal Vasko <mvasko@cesnet.cz>
roman3f9b65c2023-06-05 14:26:58 +02004 * @author Roman Janota <janota@cesnet.cz>
Michal Vasko95ea9ff2021-11-09 12:29:14 +01005 * @brief libnetconf2 TLS server session manipulation functions
Radek Krejci5da708a2015-09-01 17:33:23 +02006 *
Michal Vasko95ea9ff2021-11-09 12:29:14 +01007 * @copyright
roman3f9b65c2023-06-05 14:26:58 +02008 * Copyright (c) 2015 - 2023 CESNET, z.s.p.o.
Radek Krejci5da708a2015-09-01 17:33:23 +02009 *
Radek Krejci9b81f5b2016-02-24 13:14:49 +010010 * This source code is licensed under BSD 3-Clause License (the "License").
11 * You may not use this file except in compliance with the License.
12 * You may obtain a copy of the License at
Michal Vaskoafd416b2016-02-25 14:51:46 +010013 *
Radek Krejci9b81f5b2016-02-24 13:14:49 +010014 * https://opensource.org/licenses/BSD-3-Clause
Radek Krejci5da708a2015-09-01 17:33:23 +020015 */
16
Michal Vaskoc14e3c82016-01-11 16:14:30 +010017#define _GNU_SOURCE
18
Michal Vaskoc14e3c82016-01-11 16:14:30 +010019#include <poll.h>
roman3f9b65c2023-06-05 14:26:58 +020020#include <stdint.h>
21#include <stdio.h>
22#include <stdlib.h>
Michal Vaskob83a3fa2021-05-26 09:53:42 +020023#include <string.h>
Michal Vaskof0537d82016-01-29 14:42:38 +010024#include <unistd.h>
Michal Vaskoc14e3c82016-01-11 16:14:30 +010025
roman3f9b65c2023-06-05 14:26:58 +020026#include <curl/curl.h>
Michal Vaskoc14e3c82016-01-11 16:14:30 +010027
Michal Vasko316c9632020-04-15 11:06:57 +020028#include "compat.h"
roman3f9b65c2023-06-05 14:26:58 +020029#include "config.h"
30#include "log_p.h"
31#include "session.h"
32#include "session_p.h"
roman69962562024-04-05 12:33:39 +020033#include "session_wrapper.h"
Rosen Penev4f552d62019-06-26 16:10:43 -070034
Michal Vasko3031aae2016-01-27 16:07:18 +010035struct nc_server_tls_opts tls_ch_opts;
Michal Vaskoc14e3c82016-01-11 16:14:30 +010036extern struct nc_server_opts server_opts;
Michal Vaskoc61c4492016-01-25 11:13:34 +010037
roman3f9b65c2023-06-05 14:26:58 +020038static int
39nc_server_tls_ks_ref_get_cert_key(const char *referenced_key_name, const char *referenced_cert_name,
40 char **privkey_data, NC_PRIVKEY_FORMAT *privkey_type, char **cert_data)
Andrew Langefeld440b6c72018-08-27 16:26:20 -050041{
roman3f9b65c2023-06-05 14:26:58 +020042 uint16_t i, j;
43 struct nc_keystore *ks = &server_opts.keystore;
Andrew Langefeld440b6c72018-08-27 16:26:20 -050044
roman3f9b65c2023-06-05 14:26:58 +020045 *privkey_data = NULL;
46 *cert_data = NULL;
Andrew Langefeld440b6c72018-08-27 16:26:20 -050047
roman3f9b65c2023-06-05 14:26:58 +020048 /* lookup name */
49 for (i = 0; i < ks->asym_key_count; i++) {
50 if (!strcmp(referenced_key_name, ks->asym_keys[i].name)) {
51 break;
Andrew Langefeld440b6c72018-08-27 16:26:20 -050052 }
53 }
roman3f9b65c2023-06-05 14:26:58 +020054 if (i == ks->asym_key_count) {
55 ERR(NULL, "Keystore entry \"%s\" not found.", referenced_key_name);
Andrew Langefeld440b6c72018-08-27 16:26:20 -050056 return -1;
57 }
58
roman3f9b65c2023-06-05 14:26:58 +020059 for (j = 0; j < ks->asym_keys[i].cert_count; j++) {
roman78df0fa2023-11-02 10:33:57 +010060 if (!strcmp(referenced_cert_name, ks->asym_keys[i].certs[j].name)) {
roman3f9b65c2023-06-05 14:26:58 +020061 break;
Andrew Langefeld440b6c72018-08-27 16:26:20 -050062 }
63 }
roman3f9b65c2023-06-05 14:26:58 +020064 if (j == ks->asym_keys[i].cert_count) {
65 ERR(NULL, "Keystore certificate entry \"%s\" associated with the key \"%s\" not found.",
66 referenced_cert_name, referenced_key_name);
67 return -1;
68 }
Andrew Langefeld440b6c72018-08-27 16:26:20 -050069
roman3f9b65c2023-06-05 14:26:58 +020070 *privkey_data = ks->asym_keys[i].privkey_data;
71 *privkey_type = ks->asym_keys[i].privkey_type;
72 *cert_data = ks->asym_keys[i].certs[j].data;
73 return 0;
Andrew Langefeld440b6c72018-08-27 16:26:20 -050074}
75
Michal Vasko4c1fb492017-01-30 14:31:07 +010076static int
roman646a3f12024-07-09 14:25:31 +020077nc_server_tls_truststore_ref_get_certs(const char *referenced_name, struct nc_certificate **certs, uint16_t *cert_count)
roman69962562024-04-05 12:33:39 +020078{
79 uint16_t i;
80 struct nc_truststore *ts = &server_opts.truststore;
81
82 *certs = NULL;
83 *cert_count = 0;
84
85 /* lookup name */
86 for (i = 0; i < ts->cert_bag_count; i++) {
87 if (!strcmp(referenced_name, ts->cert_bags[i].name)) {
88 break;
89 }
90 }
91
92 if (i == ts->cert_bag_count) {
93 ERR(NULL, "Truststore entry \"%s\" not found.", referenced_name);
94 return -1;
95 }
96
97 *certs = ts->cert_bags[i].certs;
98 *cert_count = ts->cert_bags[i].cert_count;
99 return 0;
100}
101
102static void *
romanebc7bba2024-04-15 14:58:39 +0200103nc_base64der_to_privkey(const char *in, const char *key_str)
roman69962562024-04-05 12:33:39 +0200104{
romanebc7bba2024-04-15 14:58:39 +0200105 char *buf = NULL;
roman69962562024-04-05 12:33:39 +0200106 void *pkey;
107
108 NC_CHECK_ARG_RET(NULL, in, NULL);
109
roman04a65d02024-04-25 15:07:00 +0200110 if (asprintf(&buf, "%s%s%s%s%s%s%s", "-----BEGIN", key_str, "PRIVATE KEY-----\n", in, "\n-----END",
111 key_str, "PRIVATE KEY-----") == -1) {
roman69962562024-04-05 12:33:39 +0200112 ERRMEM;
113 return NULL;
114 }
115
116 pkey = nc_tls_pem_to_privkey_wrap(buf);
117 free(buf);
118 return pkey;
119}
120
romanebc7bba2024-04-15 14:58:39 +0200121static char *
roman69962562024-04-05 12:33:39 +0200122nc_server_tls_digest_to_hex(const unsigned char *digest, unsigned int digest_len)
123{
124 unsigned int i;
125 char *hex;
126
127 hex = malloc(digest_len * 3);
128 NC_CHECK_ERRMEM_RET(!hex, NULL);
129
130 for (i = 0; i < digest_len - 1; ++i) {
131 sprintf(hex + (i * 3), "%02x:", digest[i]);
132 }
133 sprintf(hex + (i * 3), "%02x", digest[i]);
134
135 return hex;
136}
137
romanebc7bba2024-04-15 14:58:39 +0200138static char *
roman69962562024-04-05 12:33:39 +0200139nc_server_tls_md5(void *cert)
140{
141 int rc;
142 unsigned int buf_len = 16;
143 unsigned char buf[buf_len];
144
145 /* compute MD-5 hash of cert and store it in buf */
146 rc = nc_server_tls_md5_wrap(cert, buf);
147 if (rc) {
148 return NULL;
149 }
150
151 /* convert the hash to hex */
152 return nc_server_tls_digest_to_hex(buf, buf_len);
153}
154
romanebc7bba2024-04-15 14:58:39 +0200155static char *
roman69962562024-04-05 12:33:39 +0200156nc_server_tls_sha1(void *cert)
157{
158 int rc;
159 unsigned int buf_len = 20;
160 unsigned char buf[buf_len];
161
162 /* compute SHA-1 hash of cert and store it in buf */
163 rc = nc_server_tls_sha1_wrap(cert, buf);
164 if (rc) {
165 return NULL;
166 }
167
168 /* convert the hash to hex */
169 return nc_server_tls_digest_to_hex(buf, buf_len);
170}
171
romanebc7bba2024-04-15 14:58:39 +0200172static char *
roman69962562024-04-05 12:33:39 +0200173nc_server_tls_sha224(void *cert)
174{
175 int rc;
176 unsigned int buf_len = 28;
177 unsigned char buf[buf_len];
178
179 /* compute SHA-224 hash of cert and store it in buf */
180 rc = nc_server_tls_sha224_wrap(cert, buf);
181 if (rc) {
182 return NULL;
183 }
184
185 /* convert the hash to hex */
186 return nc_server_tls_digest_to_hex(buf, buf_len);
187}
188
romanebc7bba2024-04-15 14:58:39 +0200189static char *
roman69962562024-04-05 12:33:39 +0200190nc_server_tls_sha256(void *cert)
191{
192 int rc;
193 unsigned int buf_len = 32;
194 unsigned char buf[buf_len];
195
196 /* compute SHA-256 hash of cert and store it in buf */
197 rc = nc_server_tls_sha256_wrap(cert, buf);
198 if (rc) {
199 return NULL;
200 }
201
202 /* convert the hash to hex */
203 return nc_server_tls_digest_to_hex(buf, buf_len);
204}
205
romanebc7bba2024-04-15 14:58:39 +0200206static char *
roman69962562024-04-05 12:33:39 +0200207nc_server_tls_sha384(void *cert)
208{
209 int rc;
210 unsigned int buf_len = 48;
211 unsigned char buf[buf_len];
212
213 /* compute SHA-384 hash of cert and store it in buf */
214 rc = nc_server_tls_sha384_wrap(cert, buf);
215 if (rc) {
216 return NULL;
217 }
218
219 /* convert the hash to hex */
220 return nc_server_tls_digest_to_hex(buf, buf_len);
221}
222
romanebc7bba2024-04-15 14:58:39 +0200223static char *
roman69962562024-04-05 12:33:39 +0200224nc_server_tls_sha512(void *cert)
225{
226 int rc;
227 unsigned int buf_len = 64;
228 unsigned char buf[buf_len];
229
230 /* compute SHA-512 hash of cert and store it in buf */
231 rc = nc_server_tls_sha512_wrap(cert, buf);
232 if (rc) {
233 return NULL;
234 }
235
236 /* convert the hash to hex */
237 return nc_server_tls_digest_to_hex(buf, buf_len);
238}
239
240static int
roman7251b0d2024-07-04 13:25:02 +0200241nc_server_tls_get_username(void *cert, struct nc_ctn *ctn, char **username)
romanebc7bba2024-04-15 14:58:39 +0200242{
romance435112024-04-23 15:12:09 +0200243 char *subject, *cn, *san_value = NULL, rdn_separator;
romanebc7bba2024-04-15 14:58:39 +0200244 void *sans;
245 int i, nsans = 0, rc;
246 NC_TLS_CTN_MAPTYPE san_type = 0;
247
248#ifdef HAVE_LIBMEDTLS
romance435112024-04-23 15:12:09 +0200249 rdn_separator = ',';
romanebc7bba2024-04-15 14:58:39 +0200250#else
romance435112024-04-23 15:12:09 +0200251 rdn_separator = '/';
romanebc7bba2024-04-15 14:58:39 +0200252#endif
253
roman7251b0d2024-07-04 13:25:02 +0200254 if (ctn->map_type == NC_TLS_CTN_SPECIFIED) {
255 *username = strdup(ctn->name);
256 NC_CHECK_ERRMEM_RET(!*username, -1);
257 } else if (ctn->map_type == NC_TLS_CTN_COMMON_NAME) {
romanebc7bba2024-04-15 14:58:39 +0200258 subject = nc_server_tls_get_subject_wrap(cert);
259 if (!subject) {
260 return -1;
261 }
262
263 cn = strstr(subject, "CN=");
264 if (!cn) {
265 WRN(NULL, "Certificate does not include the commonName field.");
266 free(subject);
267 return 1;
268 }
269
270 /* skip "CN=" */
271 cn += 3;
272 if (strchr(cn, rdn_separator)) {
273 *strchr(cn, rdn_separator) = '\0';
274 }
275 *username = strdup(cn);
276 free(subject);
277 NC_CHECK_ERRMEM_RET(!*username, -1);
278 } else {
279 sans = nc_tls_get_sans_wrap(cert);
280 if (!sans) {
281 WRN(NULL, "Certificate has no SANs or failed to retrieve them.");
282 return 1;
283 }
284 nsans = nc_tls_get_num_sans_wrap(sans);
285
286 for (i = 0; i < nsans; i++) {
287 if ((rc = nc_tls_get_san_value_type_wrap(sans, i, &san_value, &san_type))) {
288 if (rc == -1) {
289 /* fatal error */
290 nc_tls_sans_destroy_wrap(sans);
291 return -1;
292 }
293
294 /* got a type that we dont care about */
295 continue;
296 }
297
roman7251b0d2024-07-04 13:25:02 +0200298 if ((ctn->map_type == NC_TLS_CTN_SAN_ANY) || (ctn->map_type == san_type)) {
romanebc7bba2024-04-15 14:58:39 +0200299 /* found a match */
300 *username = san_value;
301 break;
302 }
303 free(san_value);
304 }
305
306 nc_tls_sans_destroy_wrap(sans);
307
308 if (i == nsans) {
roman7251b0d2024-07-04 13:25:02 +0200309 switch (ctn->map_type) {
romanebc7bba2024-04-15 14:58:39 +0200310 case NC_TLS_CTN_SAN_RFC822_NAME:
311 WRN(NULL, "Certificate does not include the SAN rfc822Name field.");
312 break;
313 case NC_TLS_CTN_SAN_DNS_NAME:
314 WRN(NULL, "Certificate does not include the SAN dNSName field.");
315 break;
316 case NC_TLS_CTN_SAN_IP_ADDRESS:
317 WRN(NULL, "Certificate does not include the SAN iPAddress field.");
318 break;
319 case NC_TLS_CTN_SAN_ANY:
320 WRN(NULL, "Certificate does not include any relevant SAN fields.");
321 break;
322 default:
323 break;
324 }
325 return 1;
326 }
327 }
328
329 return 0;
330}
331
romance435112024-04-23 15:12:09 +0200332static int
roman7251b0d2024-07-04 13:25:02 +0200333nc_server_tls_cert_to_name(struct nc_ctn *ctn, void *cert_chain, char **username)
334{
335 int ret = 1, i, cert_count, fingerprint_match;
336 char *digest_md5 = NULL, *digest_sha1 = NULL, *digest_sha224 = NULL;
337 char *digest_sha256 = NULL, *digest_sha384 = NULL, *digest_sha512 = NULL;
338 void *cert;
339
340 /* first make sure the entry is valid */
341 if (!ctn->map_type || ((ctn->map_type == NC_TLS_CTN_SPECIFIED) && !ctn->name)) {
342 VRB(NULL, "Cert verify CTN: entry with id %u not valid, skipping.", ctn->id);
343 return 1;
344 }
345
346 cert_count = nc_tls_get_num_certs_wrap(cert_chain);
347 for (i = 0; i < cert_count; i++) {
348 /* reset the flag */
349 fingerprint_match = 0;
350
351 /*get next cert */
352 nc_tls_get_cert_wrap(cert_chain, i, &cert);
353 if (!cert) {
354 ERR(NULL, "Failed to get certificate from the chain.");
355 ret = -1;
356 goto cleanup;
357 }
358
359 if (!ctn->fingerprint) {
360 /* if ctn has no fingerprint, it will match any certificate */
361 fingerprint_match = 1;
362
363 /* MD5 */
364 } else if (!strncmp(ctn->fingerprint, "01", 2)) {
365 digest_md5 = nc_server_tls_md5(cert);
366 if (!digest_md5) {
367 ret = -1;
368 goto cleanup;
369 }
370
371 if (!strcasecmp(ctn->fingerprint + 3, digest_md5)) {
372 /* we got ourselves a potential winner! */
373 VRB(NULL, "Cert verify CTN: entry with a matching fingerprint found.");
374 fingerprint_match = 1;
375 }
376 free(digest_md5);
377 digest_md5 = NULL;
378
379 /* SHA-1 */
380 } else if (!strncmp(ctn->fingerprint, "02", 2)) {
381 digest_sha1 = nc_server_tls_sha1(cert);
382 if (!digest_sha1) {
383 ret = -1;
384 goto cleanup;
385 }
386
387 if (!strcasecmp(ctn->fingerprint + 3, digest_sha1)) {
388 /* we got ourselves a potential winner! */
389 VRB(NULL, "Cert verify CTN: entry with a matching fingerprint found.");
390 fingerprint_match = 1;
391 }
392 free(digest_sha1);
393 digest_sha1 = NULL;
394
395 /* SHA-224 */
396 } else if (!strncmp(ctn->fingerprint, "03", 2)) {
397 digest_sha224 = nc_server_tls_sha224(cert);
398 if (!digest_sha224) {
399 ret = -1;
400 goto cleanup;
401 }
402
403 if (!strcasecmp(ctn->fingerprint + 3, digest_sha224)) {
404 /* we got ourselves a potential winner! */
405 VRB(NULL, "Cert verify CTN: entry with a matching fingerprint found.");
406 fingerprint_match = 1;
407 }
408 free(digest_sha224);
409 digest_sha224 = NULL;
410
411 /* SHA-256 */
412 } else if (!strncmp(ctn->fingerprint, "04", 2)) {
413 digest_sha256 = nc_server_tls_sha256(cert);
414 if (!digest_sha256) {
415 ret = -1;
416 goto cleanup;
417 }
418
419 if (!strcasecmp(ctn->fingerprint + 3, digest_sha256)) {
420 /* we got ourselves a potential winner! */
421 VRB(NULL, "Cert verify CTN: entry with a matching fingerprint found.");
422 fingerprint_match = 1;
423 }
424 free(digest_sha256);
425 digest_sha256 = NULL;
426
427 /* SHA-384 */
428 } else if (!strncmp(ctn->fingerprint, "05", 2)) {
429 digest_sha384 = nc_server_tls_sha384(cert);
430 if (!digest_sha384) {
431 ret = -1;
432 goto cleanup;
433 }
434
435 if (!strcasecmp(ctn->fingerprint + 3, digest_sha384)) {
436 /* we got ourselves a potential winner! */
437 VRB(NULL, "Cert verify CTN: entry with a matching fingerprint found.");
438 fingerprint_match = 1;
439 }
440 free(digest_sha384);
441 digest_sha384 = NULL;
442
443 /* SHA-512 */
444 } else if (!strncmp(ctn->fingerprint, "06", 2)) {
445 digest_sha512 = nc_server_tls_sha512(cert);
446 if (!digest_sha512) {
447 ret = -1;
448 goto cleanup;
449 }
450
451 if (!strcasecmp(ctn->fingerprint + 3, digest_sha512)) {
452 /* we got ourselves a potential winner! */
453 VRB(NULL, "Cert verify CTN: entry with a matching fingerprint found.");
454 fingerprint_match = 1;
455 }
456 free(digest_sha512);
457 digest_sha512 = NULL;
458
459 /* unknown */
460 } else {
461 WRN(NULL, "Unknown fingerprint algorithm used (%s), skipping.", ctn->fingerprint);
462 continue;
463 }
464
465 if (fingerprint_match) {
466 /* found a fingerprint match, try to obtain the username */
467 ret = nc_server_tls_get_username(cert, ctn, username);
468 if (ret == -1) {
469 /* fatal error */
470 goto cleanup;
471 } else if (!ret) {
472 /* username found */
473 goto cleanup;
474 }
475 }
476 }
477
478cleanup:
479 return ret;
480}
481
482static int
483_nc_server_tls_cert_to_name(struct nc_server_tls_opts *opts, void *cert_chain, char **username)
484{
485 int ret = 1;
486 struct nc_endpt *referenced_endpt;
487 struct nc_ctn *ctn;
488
489 for (ctn = opts->ctn; ctn; ctn = ctn->next) {
490 ret = nc_server_tls_cert_to_name(ctn, cert_chain, username);
491 if (ret != 1) {
492 /* fatal error or success */
493 goto cleanup;
494 }
495 }
496
497 /* do the same for referenced endpoint's ctn entries */
498 if (opts->referenced_endpt_name) {
499 if (nc_server_get_referenced_endpt(opts->referenced_endpt_name, &referenced_endpt)) {
500 ERRINT;
501 ret = -1;
502 goto cleanup;
503 }
504
505 for (ctn = referenced_endpt->opts.tls->ctn; ctn; ctn = ctn->next) {
506 ret = nc_server_tls_cert_to_name(ctn, cert_chain, username);
507 if (ret != 1) {
508 /* fatal error or success */
509 goto cleanup;
510 }
511 }
512 }
513
514cleanup:
515 return ret;
516}
517
518static int
romance435112024-04-23 15:12:09 +0200519_nc_server_tls_verify_peer_cert(void *peer_cert, struct nc_cert_grouping *ee_certs)
520{
521 int i, ret;
522 void *cert;
523 struct nc_certificate *certs;
524 uint16_t cert_count;
525
526 if (ee_certs->store == NC_STORE_LOCAL) {
527 /* local definition */
528 certs = ee_certs->certs;
529 cert_count = ee_certs->cert_count;
530 } else {
531 /* truststore reference */
roman646a3f12024-07-09 14:25:31 +0200532 if (nc_server_tls_truststore_ref_get_certs(ee_certs->ts_ref, &certs, &cert_count)) {
romance435112024-04-23 15:12:09 +0200533 ERR(NULL, "Error getting end-entity certificates from the truststore reference \"%s\".", ee_certs->ts_ref);
534 return -1;
535 }
536 }
537
538 for (i = 0; i < cert_count; i++) {
539 /* import stored cert */
540 cert = nc_base64der_to_cert(certs[i].data);
541
542 /* compare stored with received */
543 ret = nc_server_tls_certs_match_wrap(peer_cert, cert);
544 nc_tls_cert_destroy_wrap(cert);
545 if (ret) {
546 /* found a match */
547 VRB(NULL, "Cert verify: fail, but the end-entity certificate is trusted, continuing.");
548 return 0;
549 }
550 }
551
552 return 1;
553}
554
555int
556nc_server_tls_verify_peer_cert(void *peer_cert, struct nc_server_tls_opts *opts)
557{
558 int rc;
559 struct nc_endpt *referenced_endpt;
560
561 rc = _nc_server_tls_verify_peer_cert(peer_cert, &opts->ee_certs);
562 if (!rc) {
563 return 0;
564 }
565
566 if (opts->referenced_endpt_name) {
567 if (nc_server_get_referenced_endpt(opts->referenced_endpt_name, &referenced_endpt)) {
568 ERRINT;
569 return -1;
570 }
571
572 rc = _nc_server_tls_verify_peer_cert(peer_cert, &referenced_endpt->opts.tls->ee_certs);
573 if (!rc) {
574 return 0;
575 }
576 }
577
578 return 1;
579}
580
romanebc7bba2024-04-15 14:58:39 +0200581int
roman7ffd2fc2024-05-13 13:48:34 +0200582nc_server_tls_verify_cert(void *cert, int depth, int trusted, struct nc_tls_verify_cb_data *cb_data)
roman69962562024-04-05 12:33:39 +0200583{
roman7251b0d2024-07-04 13:25:02 +0200584 int ret = 0;
roman69962562024-04-05 12:33:39 +0200585 char *subject = NULL, *issuer = NULL;
586 struct nc_server_tls_opts *opts = cb_data->opts;
587 struct nc_session *session = cb_data->session;
roman7251b0d2024-07-04 13:25:02 +0200588 void *cert_chain = cb_data->chain;
roman69962562024-04-05 12:33:39 +0200589
roman7ffd2fc2024-05-13 13:48:34 +0200590 if (session->username) {
591 /* already verified */
592 return 0;
593 }
594
roman69962562024-04-05 12:33:39 +0200595 subject = nc_server_tls_get_subject_wrap(cert);
596 issuer = nc_server_tls_get_issuer_wrap(cert);
597 if (!subject || !issuer) {
598 ERR(session, "Failed to get certificate's subject or issuer.");
599 ret = -1;
600 goto cleanup;
601 }
602
603 VRB(session, "Cert verify: depth %d.", depth);
604 VRB(session, "Cert verify: subject: %s.", subject);
605 VRB(session, "Cert verify: issuer: %s.", issuer);
606
607 if (depth == 0) {
roman7ffd2fc2024-05-13 13:48:34 +0200608 if (!trusted) {
roman69962562024-04-05 12:33:39 +0200609 /* peer cert is not trusted, so it must match any configured end-entity cert
romanebc7bba2024-04-15 14:58:39 +0200610 * on the given endpoint in order for the client to be authenticated */
romance435112024-04-23 15:12:09 +0200611 ret = nc_server_tls_verify_peer_cert(cert, opts);
roman69962562024-04-05 12:33:39 +0200612 if (ret) {
romance435112024-04-23 15:12:09 +0200613 ERR(session, "Cert verify: fail (Client certificate not trusted and does not match any configured end-entity certificate).");
614 goto cleanup;
roman69962562024-04-05 12:33:39 +0200615 }
616 }
roman69962562024-04-05 12:33:39 +0200617
roman7251b0d2024-07-04 13:25:02 +0200618 /* get username since we are at depth 0 and have the whole cert chain,
619 * the whole chain is needed in order to comply with the following issue:
620 * https://github.com/CESNET/netopeer2/issues/1596
621 */
622 ret = _nc_server_tls_cert_to_name(opts, cert_chain, &session->username);
roman69962562024-04-05 12:33:39 +0200623 if (ret == -1) {
624 /* fatal error */
625 goto cleanup;
626 }
roman69962562024-04-05 12:33:39 +0200627
romanebc7bba2024-04-15 14:58:39 +0200628 if (session->username) {
629 VRB(NULL, "Cert verify CTN: new client username recognized as \"%s\".", session->username);
630 } else {
631 VRB(NULL, "Cert verify CTN: unsuccessful, dropping the new client.");
632 ret = 1;
633 goto cleanup;
634 }
roman69962562024-04-05 12:33:39 +0200635
roman7251b0d2024-07-04 13:25:02 +0200636 if (server_opts.user_verify_clb && !server_opts.user_verify_clb(session)) {
637 VRB(session, "Cert verify: user verify callback revoked authorization.");
638 ret = 1;
639 goto cleanup;
640 }
roman69962562024-04-05 12:33:39 +0200641 }
642
643cleanup:
644 free(subject);
645 free(issuer);
646 return ret;
647}
648
649API const void *
650nc_session_get_client_cert(const struct nc_session *session)
651{
652 if (!session || (session->side != NC_SERVER)) {
653 ERRARG(session, "session");
654 return NULL;
655 }
656
657 return session->opts.server.client_cert;
658}
659
660API void
661nc_server_tls_set_verify_clb(int (*verify_clb)(const struct nc_session *session))
662{
663 server_opts.user_verify_clb = verify_clb;
664}
665
666int
667nc_server_tls_load_server_cert_key(struct nc_server_tls_opts *opts, void **srv_cert, void **srv_pkey)
Michal Vasko4c1fb492017-01-30 14:31:07 +0100668{
roman3f9b65c2023-06-05 14:26:58 +0200669 char *privkey_data = NULL, *cert_data = NULL;
roman3f9b65c2023-06-05 14:26:58 +0200670 NC_PRIVKEY_FORMAT privkey_type;
roman69962562024-04-05 12:33:39 +0200671 void *cert = NULL;
672 void *pkey = NULL;
Michal Vasko4c1fb492017-01-30 14:31:07 +0100673
romanebc7bba2024-04-15 14:58:39 +0200674 *srv_cert = *srv_pkey = NULL;
675
roman3f9b65c2023-06-05 14:26:58 +0200676 /* get data needed for setting the server cert */
677 if (opts->store == NC_STORE_LOCAL) {
678 /* local definition */
679 cert_data = opts->cert_data;
680 privkey_data = opts->privkey_data;
681 privkey_type = opts->privkey_type;
Michal Vasko4c1fb492017-01-30 14:31:07 +0100682 } else {
roman3f9b65c2023-06-05 14:26:58 +0200683 /* keystore */
roman69962562024-04-05 12:33:39 +0200684 if (nc_server_tls_ks_ref_get_cert_key(opts->key_ref, opts->cert_ref, &privkey_data, &privkey_type, &cert_data)) {
roman3f9b65c2023-06-05 14:26:58 +0200685 ERR(NULL, "Getting server certificate from the keystore reference \"%s\" failed.", opts->key_ref);
roman69962562024-04-05 12:33:39 +0200686 return 1;
Michal Vasko4c1fb492017-01-30 14:31:07 +0100687 }
688 }
romana55fee02024-03-21 16:00:48 +0100689 if (!cert_data || !privkey_data) {
690 ERR(NULL, "Server certificate not configured.");
romanbc541a82024-04-25 14:56:35 +0200691 return 1;
romana55fee02024-03-21 16:00:48 +0100692 }
Michal Vasko4c1fb492017-01-30 14:31:07 +0100693
roman69962562024-04-05 12:33:39 +0200694 cert = nc_base64der_to_cert(cert_data);
roman3f9b65c2023-06-05 14:26:58 +0200695 if (!cert) {
roman69962562024-04-05 12:33:39 +0200696 return 1;
roman3f9b65c2023-06-05 14:26:58 +0200697 }
698
romanebc7bba2024-04-15 14:58:39 +0200699 pkey = nc_base64der_to_privkey(privkey_data, nc_privkey_format_to_str(privkey_type));
roman3f9b65c2023-06-05 14:26:58 +0200700 if (!pkey) {
roman69962562024-04-05 12:33:39 +0200701 nc_tls_cert_destroy_wrap(cert);
702 return 1;
Michal Vasko4c1fb492017-01-30 14:31:07 +0100703 }
704
roman69962562024-04-05 12:33:39 +0200705 *srv_cert = cert;
706 *srv_pkey = pkey;
roman3f9b65c2023-06-05 14:26:58 +0200707 return 0;
Michal Vasko4c1fb492017-01-30 14:31:07 +0100708}
709
roman69962562024-04-05 12:33:39 +0200710int
711nc_server_tls_load_trusted_certs(struct nc_cert_grouping *ca_certs, void *cert_store)
712{
713 struct nc_certificate *certs;
714 uint16_t i, cert_count;
romance435112024-04-23 15:12:09 +0200715 void *cert;
roman69962562024-04-05 12:33:39 +0200716
717 if (ca_certs->store == NC_STORE_LOCAL) {
718 /* local definition */
719 certs = ca_certs->certs;
720 cert_count = ca_certs->cert_count;
721 } else {
722 /* truststore */
roman646a3f12024-07-09 14:25:31 +0200723 if (nc_server_tls_truststore_ref_get_certs(ca_certs->ts_ref, &certs, &cert_count)) {
roman69962562024-04-05 12:33:39 +0200724 ERR(NULL, "Error getting certificate-authority certificates from the truststore reference \"%s\".", ca_certs->ts_ref);
725 return 1;
726 }
727 }
728
729 for (i = 0; i < cert_count; i++) {
romance435112024-04-23 15:12:09 +0200730 /* parse data into cert */
731 cert = nc_base64der_to_cert(certs[i].data);
732 if (!cert) {
733 return 1;
734 }
735
736 /* store cert in cert store */
737 if (nc_tls_add_cert_to_store_wrap(cert, cert_store)) {
738 nc_tls_cert_destroy_wrap(cert);
roman69962562024-04-05 12:33:39 +0200739 return 1;
740 }
741 }
742
743 return 0;
roman3f9b65c2023-06-05 14:26:58 +0200744}
745
746static int
roman69962562024-04-05 12:33:39 +0200747nc_server_tls_accept_check(int accept_ret, void *tls_session)
Michal Vasko9af52322022-07-25 14:39:30 +0200748{
roman69962562024-04-05 12:33:39 +0200749 uint32_t verify;
romanebc7bba2024-04-15 14:58:39 +0200750 char *err;
Michal Vasko9af52322022-07-25 14:39:30 +0200751
752 /* check certificate verification result */
roman69962562024-04-05 12:33:39 +0200753 verify = nc_tls_get_verify_result_wrap(tls_session);
roman008cfe72024-04-05 12:36:18 +0200754 if (!verify && (accept_ret == 1)) {
roman69962562024-04-05 12:33:39 +0200755 VRB(NULL, "Client certificate verified.");
romanebc7bba2024-04-15 14:58:39 +0200756 } else if (verify) {
757 err = nc_tls_verify_error_string_wrap(verify);
758 ERR(NULL, "Client certificate error (%s).", err);
759 free(err);
Michal Vasko9af52322022-07-25 14:39:30 +0200760 }
761
762 if (accept_ret != 1) {
romance435112024-04-23 15:12:09 +0200763 nc_server_tls_print_accept_err_wrap(accept_ret, tls_session);
Michal Vasko9af52322022-07-25 14:39:30 +0200764 }
765
766 return accept_ret;
767}
768
romanf1d2a542024-08-22 16:16:53 +0200769/**
770 * @brief Get the number of certificates in a certificate grouping.
771 *
772 * @param[in] certs_grp Certificate grouping to get the number of certificates from.
773 * @return Number of certificates in the grouping, or -1 on error.
774 */
775static uint16_t
776nc_server_tls_get_num_certs(struct nc_cert_grouping *certs_grp)
777{
778 uint16_t count = 0;
779 struct nc_certificate *certs;
780
781 if (certs_grp->store == NC_STORE_LOCAL) {
782 count = certs_grp->cert_count;
783 } else if (certs_grp->store == NC_STORE_TRUSTSTORE) {
784 if (nc_server_tls_truststore_ref_get_certs(certs_grp->ts_ref, &certs, &count)) {
785 ERR(NULL, "Getting CA certificates from the truststore reference \"%s\" failed.", certs_grp->ts_ref);
786 return -1;
787 }
788 }
789
790 return count;
791}
792
Michal Vasko3031aae2016-01-27 16:07:18 +0100793int
roman3f9b65c2023-06-05 14:26:58 +0200794nc_accept_tls_session(struct nc_session *session, struct nc_server_tls_opts *opts, int sock, int timeout)
Michal Vasko3031aae2016-01-27 16:07:18 +0100795{
roman69962562024-04-05 12:33:39 +0200796 int rc, timeouted = 0;
roman6ece9c52022-06-22 09:29:17 +0200797 struct timespec ts_timeout;
roman69962562024-04-05 12:33:39 +0200798 struct nc_tls_verify_cb_data cb_data = {0};
799 struct nc_endpt *referenced_endpt;
800 void *tls_cfg, *srv_cert, *srv_pkey, *cert_store, *crl_store;
romanf1d2a542024-08-22 16:16:53 +0200801 uint32_t cert_count = 0;
Michal Vaskoc61c4492016-01-25 11:13:34 +0100802
roman69962562024-04-05 12:33:39 +0200803 tls_cfg = srv_cert = srv_pkey = cert_store = crl_store = NULL;
roman3f9b65c2023-06-05 14:26:58 +0200804
roman69962562024-04-05 12:33:39 +0200805 /* set verify cb data */
806 cb_data.session = session;
807 cb_data.opts = opts;
Michal Vaskoe2713da2016-08-22 16:06:40 +0200808
roman69962562024-04-05 12:33:39 +0200809 /* prepare TLS context from which a session will be created */
romance435112024-04-23 15:12:09 +0200810 tls_cfg = nc_tls_config_new_wrap(NC_SERVER);
roman69962562024-04-05 12:33:39 +0200811 if (!tls_cfg) {
812 goto fail;
roman3f9b65c2023-06-05 14:26:58 +0200813 }
814
roman69962562024-04-05 12:33:39 +0200815 /* opaque CA/CRL certificate store */
816 cert_store = nc_tls_cert_store_new_wrap();
817 if (!cert_store) {
818 goto fail;
819 }
820
821 /* load server's key and certificate */
822 if (nc_server_tls_load_server_cert_key(opts, &srv_cert, &srv_pkey)) {
823 ERR(session, "Loading server certificate and/or private key failed.");
824 goto fail;
825 }
826
827 /* load trusted CA certificates */
828 if (nc_server_tls_load_trusted_certs(&opts->ca_certs, cert_store)) {
829 ERR(session, "Loading server CA certs failed.");
830 goto fail;
831 }
832
833 /* load referenced endpoint's trusted CA certs if set */
roman78df0fa2023-11-02 10:33:57 +0100834 if (opts->referenced_endpt_name) {
romanebc7bba2024-04-15 14:58:39 +0200835 if (nc_server_get_referenced_endpt(opts->referenced_endpt_name, &referenced_endpt)) {
836 ERR(session, "Referenced endpoint \"%s\" not found.", opts->referenced_endpt_name);
837 goto fail;
838 }
839
roman69962562024-04-05 12:33:39 +0200840 if (nc_server_tls_load_trusted_certs(&referenced_endpt->opts.tls->ca_certs, cert_store)) {
841 ERR(session, "Loading server CA certs from referenced endpoint failed.");
842 goto fail;
Michal Vaskoe2713da2016-08-22 16:06:40 +0200843 }
roman3f9b65c2023-06-05 14:26:58 +0200844 }
Michal Vaskoe2713da2016-08-22 16:06:40 +0200845
romanf1d2a542024-08-22 16:16:53 +0200846 /* Check if there are no CA/end entity certs configured, which is a valid config.
847 * However, that would imply not using TLS for auth, which is not (yet) supported */
848 if (!opts->referenced_endpt_name) {
849 cert_count = nc_server_tls_get_num_certs(&opts->ca_certs) + nc_server_tls_get_num_certs(&opts->ee_certs);
850 } else {
851 cert_count = nc_server_tls_get_num_certs(&opts->ca_certs) + nc_server_tls_get_num_certs(&opts->ee_certs) +
852 nc_server_tls_get_num_certs(&referenced_endpt->opts.tls->ca_certs) +
853 nc_server_tls_get_num_certs(&referenced_endpt->opts.tls->ee_certs);
854 }
855 if (cert_count <= 0) {
856 ERR(session, "Neither CA nor end-entity certificates configured.");
857 goto fail;
858 }
859
roman15fbc592024-07-02 16:01:23 +0200860 if (nc_session_tls_crl_from_cert_ext_fetch(srv_cert, cert_store, &crl_store)) {
861 ERR(session, "Loading server CRL failed.");
862 goto fail;
Michal Vaskoe2713da2016-08-22 16:06:40 +0200863 }
864
roman69962562024-04-05 12:33:39 +0200865 /* set supported TLS versions */
roman3f9b65c2023-06-05 14:26:58 +0200866 if (opts->tls_versions) {
roman69962562024-04-05 12:33:39 +0200867 if (nc_server_tls_set_tls_versions_wrap(tls_cfg, opts->tls_versions)) {
868 ERR(session, "Setting supported server TLS versions failed.");
869 goto fail;
roman3f9b65c2023-06-05 14:26:58 +0200870 }
871 }
872
romanebc7bba2024-04-15 14:58:39 +0200873 /* set supported cipher suites */
874 if (opts->ciphers) {
875 nc_server_tls_set_cipher_suites_wrap(tls_cfg, opts->ciphers);
876 }
877
romance435112024-04-23 15:12:09 +0200878 /* set verify flags, callback and its data */
879 nc_server_tls_set_verify_wrap(tls_cfg, &cb_data);
880
roman69962562024-04-05 12:33:39 +0200881 /* init TLS context and store data which may be needed later in it */
romance435112024-04-23 15:12:09 +0200882 if (nc_tls_init_ctx_wrap(sock, srv_cert, srv_pkey, cert_store, crl_store, &session->ti.tls.ctx)) {
roman69962562024-04-05 12:33:39 +0200883 goto fail;
roman3f9b65c2023-06-05 14:26:58 +0200884 }
885
roman69962562024-04-05 12:33:39 +0200886 /* memory is managed by context now */
887 srv_cert = srv_pkey = cert_store = crl_store = NULL;
888
889 /* setup config from ctx */
romance435112024-04-23 15:12:09 +0200890 if (nc_tls_setup_config_from_ctx_wrap(&session->ti.tls.ctx, NC_SERVER, tls_cfg)) {
roman69962562024-04-05 12:33:39 +0200891 goto fail;
romance435112024-04-23 15:12:09 +0200892 }
romanebc7bba2024-04-15 14:58:39 +0200893 session->ti.tls.config = tls_cfg;
894 tls_cfg = NULL;
roman69962562024-04-05 12:33:39 +0200895
896 /* fill session data and create TLS session from config */
romanebc7bba2024-04-15 14:58:39 +0200897 session->ti_type = NC_TI_TLS;
898 if (!(session->ti.tls.session = nc_tls_session_new_wrap(session->ti.tls.config))) {
roman69962562024-04-05 12:33:39 +0200899 goto fail;
900 }
roman69962562024-04-05 12:33:39 +0200901
roman69962562024-04-05 12:33:39 +0200902 /* set session fd */
903 nc_server_tls_set_fd_wrap(session->ti.tls.session, sock, &session->ti.tls.ctx);
904
Michal Vaskoe2713da2016-08-22 16:06:40 +0200905 sock = -1;
Michal Vaskoc14e3c82016-01-11 16:14:30 +0100906
roman69962562024-04-05 12:33:39 +0200907 /* do the handshake */
Michal Vasko36c7be82017-02-22 13:37:59 +0100908 if (timeout > -1) {
Michal Vaskod8a74192023-02-06 15:51:50 +0100909 nc_timeouttime_get(&ts_timeout, timeout);
Michal Vasko36c7be82017-02-22 13:37:59 +0100910 }
roman69962562024-04-05 12:33:39 +0200911 while ((rc = nc_server_tls_handshake_step_wrap(session->ti.tls.session)) == 0) {
Michal Vasko0190bc32016-03-02 15:47:49 +0100912 usleep(NC_TIMEOUT_STEP);
Michal Vaskod8a74192023-02-06 15:51:50 +0100913 if ((timeout > -1) && (nc_timeouttime_cur_diff(&ts_timeout) < 1)) {
roman69962562024-04-05 12:33:39 +0200914 ERR(session, "TLS accept timeout.");
915 timeouted = 1;
916 goto fail;
Michal Vasko0190bc32016-03-02 15:47:49 +0100917 }
918 }
roman69962562024-04-05 12:33:39 +0200919
920 /* check if handshake was ok */
921 if (nc_server_tls_accept_check(rc, session->ti.tls.session) != 1) {
922 goto fail;
Michal Vaskoc14e3c82016-01-11 16:14:30 +0100923 }
924
Michal Vasko1a38c862016-01-15 15:50:07 +0100925 return 1;
Michal Vaskoe2713da2016-08-22 16:06:40 +0200926
roman69962562024-04-05 12:33:39 +0200927fail:
Michal Vaskoe2713da2016-08-22 16:06:40 +0200928 if (sock > -1) {
929 close(sock);
930 }
roman69962562024-04-05 12:33:39 +0200931
romanebc7bba2024-04-15 14:58:39 +0200932 nc_tls_config_destroy_wrap(tls_cfg);
933 nc_tls_cert_destroy_wrap(srv_cert);
934 nc_tls_privkey_destroy_wrap(srv_pkey);
935 nc_tls_cert_store_destroy_wrap(cert_store);
936 nc_tls_crl_store_destroy_wrap(crl_store);
roman69962562024-04-05 12:33:39 +0200937
938 if (timeouted) {
939 return 0;
940 } else {
941 return -1;
942 }
Michal Vasko9e036d52016-01-08 10:49:26 +0100943}