blob: 3bd061090b5d410403f16c879338ab933e7ab09c [file] [log] [blame]
William Lallemand03c331c2020-05-13 10:10:01 +02001/*
2 *
3 * Copyright (C) 2020 HAProxy Technologies, William Lallemand <wlallemand@haproxy.com>
4 *
5 * This program is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU General Public License
7 * as published by the Free Software Foundation; either version
8 * 2 of the License, or (at your option) any later version.
9 *
10 */
11
12#define _GNU_SOURCE
13#include <ctype.h>
William Lallemand87fd9942022-04-01 20:12:03 +020014#include <dirent.h>
William Lallemand03c331c2020-05-13 10:10:01 +020015#include <errno.h>
16#include <fcntl.h>
17#include <stdio.h>
18#include <stdlib.h>
19#include <string.h>
Willy Tarreauaeed4a82020-06-04 22:01:04 +020020#include <syslog.h>
William Lallemand03c331c2020-05-13 10:10:01 +020021#include <unistd.h>
22
23#include <sys/stat.h>
24#include <sys/types.h>
25
Willy Tarreau74f24562021-10-06 17:54:12 +020026#include <import/ebpttree.h>
Willy Tarreaub2551052020-06-09 09:07:15 +020027#include <import/ebsttree.h>
28
Willy Tarreau8d366972020-05-27 16:10:29 +020029#include <haproxy/base64.h>
Willy Tarreauf1d32c42020-06-04 21:07:02 +020030#include <haproxy/channel.h>
Willy Tarreau83487a82020-06-04 20:19:54 +020031#include <haproxy/cli.h>
Willy Tarreau8d366972020-05-27 16:10:29 +020032#include <haproxy/errors.h>
Willy Tarreau47d7f902020-06-04 14:25:47 +020033#include <haproxy/ssl_ckch.h>
Willy Tarreau209108d2020-06-04 20:30:20 +020034#include <haproxy/ssl_sock.h>
Willy Tarreaub2bd8652020-06-04 14:21:22 +020035#include <haproxy/ssl_utils.h>
Willy Tarreau5e539c92020-06-04 20:45:39 +020036#include <haproxy/stream_interface.h>
Willy Tarreau48fbcae2020-06-03 18:09:46 +020037#include <haproxy/tools.h>
William Lallemand03c331c2020-05-13 10:10:01 +020038
William Lallemandda8584c2020-05-14 10:14:37 +020039/* Uncommitted CKCH transaction */
40
41static struct {
42 struct ckch_store *new_ckchs;
43 struct ckch_store *old_ckchs;
44 char *path;
45} ckchs_transaction;
46
Remi Tricot-Le Bretona32a68b2021-02-24 17:35:43 +010047/* Uncommitted CA file transaction */
48
49static struct {
50 struct cafile_entry *old_cafile_entry;
51 struct cafile_entry *new_cafile_entry;
52 char *path;
53} cafile_transaction;
William Lallemandda8584c2020-05-14 10:14:37 +020054
Remi Tricot-Le Bretona51b3392021-04-20 17:38:14 +020055/* Uncommitted CRL file transaction */
56
57static struct {
58 struct cafile_entry *old_crlfile_entry;
59 struct cafile_entry *new_crlfile_entry;
60 char *path;
61} crlfile_transaction;
62
William Lallemand03c331c2020-05-13 10:10:01 +020063
Remi Tricot-Le Bretona32a68b2021-02-24 17:35:43 +010064
William Lallemand03c331c2020-05-13 10:10:01 +020065/******************** cert_key_and_chain functions *************************
66 * These are the functions that fills a cert_key_and_chain structure. For the
67 * functions filling a SSL_CTX from a cert_key_and_chain, see ssl_sock.c
68 */
69
70/*
71 * Try to parse Signed Certificate Timestamp List structure. This function
72 * makes only basic test if the data seems like SCTL. No signature validation
73 * is performed.
74 */
75static int ssl_sock_parse_sctl(struct buffer *sctl)
76{
77 int ret = 1;
78 int len, pos, sct_len;
79 unsigned char *data;
80
81 if (sctl->data < 2)
82 goto out;
83
84 data = (unsigned char *) sctl->area;
85 len = (data[0] << 8) | data[1];
86
87 if (len + 2 != sctl->data)
88 goto out;
89
90 data = data + 2;
91 pos = 0;
92 while (pos < len) {
93 if (len - pos < 2)
94 goto out;
95
96 sct_len = (data[pos] << 8) | data[pos + 1];
97 if (pos + sct_len + 2 > len)
98 goto out;
99
100 pos += sct_len + 2;
101 }
102
103 ret = 0;
104
105out:
106 return ret;
107}
108
109/* Try to load a sctl from a buffer <buf> if not NULL, or read the file <sctl_path>
110 * It fills the ckch->sctl buffer
111 * return 0 on success or != 0 on failure */
112int ssl_sock_load_sctl_from_file(const char *sctl_path, char *buf, struct cert_key_and_chain *ckch, char **err)
113{
114 int fd = -1;
115 int r = 0;
116 int ret = 1;
117 struct buffer tmp;
118 struct buffer *src;
119 struct buffer *sctl;
120
121 if (buf) {
William Lallemand8d673942021-01-27 14:58:51 +0100122 chunk_initstr(&tmp, buf);
William Lallemand03c331c2020-05-13 10:10:01 +0200123 src = &tmp;
124 } else {
125 fd = open(sctl_path, O_RDONLY);
126 if (fd == -1)
127 goto end;
128
129 trash.data = 0;
130 while (trash.data < trash.size) {
131 r = read(fd, trash.area + trash.data, trash.size - trash.data);
132 if (r < 0) {
133 if (errno == EINTR)
134 continue;
135 goto end;
136 }
137 else if (r == 0) {
138 break;
139 }
140 trash.data += r;
141 }
142 src = &trash;
143 }
144
145 ret = ssl_sock_parse_sctl(src);
146 if (ret)
147 goto end;
148
149 sctl = calloc(1, sizeof(*sctl));
150 if (!chunk_dup(sctl, src)) {
Willy Tarreau61cfdf42021-02-20 10:46:51 +0100151 ha_free(&sctl);
William Lallemand03c331c2020-05-13 10:10:01 +0200152 goto end;
153 }
154 /* no error, fill ckch with new context, old context must be free */
155 if (ckch->sctl) {
Willy Tarreau61cfdf42021-02-20 10:46:51 +0100156 ha_free(&ckch->sctl->area);
William Lallemand03c331c2020-05-13 10:10:01 +0200157 free(ckch->sctl);
158 }
159 ckch->sctl = sctl;
160 ret = 0;
161end:
162 if (fd != -1)
163 close(fd);
164
165 return ret;
166}
167
168#if ((defined SSL_CTRL_SET_TLSEXT_STATUS_REQ_CB && !defined OPENSSL_NO_OCSP) || defined OPENSSL_IS_BORINGSSL)
169/*
Ilya Shipitsin46a030c2020-07-05 16:36:08 +0500170 * This function load the OCSP Response in DER format contained in file at
William Lallemand03c331c2020-05-13 10:10:01 +0200171 * path 'ocsp_path' or base64 in a buffer <buf>
172 *
173 * Returns 0 on success, 1 in error case.
174 */
175int ssl_sock_load_ocsp_response_from_file(const char *ocsp_path, char *buf, struct cert_key_and_chain *ckch, char **err)
176{
177 int fd = -1;
178 int r = 0;
179 int ret = 1;
180 struct buffer *ocsp_response;
181 struct buffer *src = NULL;
182
183 if (buf) {
184 int i, j;
185 /* if it's from a buffer it will be base64 */
186
187 /* remove \r and \n from the payload */
188 for (i = 0, j = 0; buf[i]; i++) {
189 if (buf[i] == '\r' || buf[i] == '\n')
190 continue;
191 buf[j++] = buf[i];
192 }
193 buf[j] = 0;
194
195 ret = base64dec(buf, j, trash.area, trash.size);
196 if (ret < 0) {
197 memprintf(err, "Error reading OCSP response in base64 format");
198 goto end;
199 }
200 trash.data = ret;
201 src = &trash;
202 } else {
203 fd = open(ocsp_path, O_RDONLY);
204 if (fd == -1) {
205 memprintf(err, "Error opening OCSP response file");
206 goto end;
207 }
208
209 trash.data = 0;
210 while (trash.data < trash.size) {
211 r = read(fd, trash.area + trash.data, trash.size - trash.data);
212 if (r < 0) {
213 if (errno == EINTR)
214 continue;
215
216 memprintf(err, "Error reading OCSP response from file");
217 goto end;
218 }
219 else if (r == 0) {
220 break;
221 }
222 trash.data += r;
223 }
224 close(fd);
225 fd = -1;
226 src = &trash;
227 }
228
229 ocsp_response = calloc(1, sizeof(*ocsp_response));
230 if (!chunk_dup(ocsp_response, src)) {
Willy Tarreau61cfdf42021-02-20 10:46:51 +0100231 ha_free(&ocsp_response);
William Lallemand03c331c2020-05-13 10:10:01 +0200232 goto end;
233 }
234 /* no error, fill ckch with new context, old context must be free */
235 if (ckch->ocsp_response) {
Willy Tarreau61cfdf42021-02-20 10:46:51 +0100236 ha_free(&ckch->ocsp_response->area);
William Lallemand03c331c2020-05-13 10:10:01 +0200237 free(ckch->ocsp_response);
238 }
239 ckch->ocsp_response = ocsp_response;
240 ret = 0;
241end:
242 if (fd != -1)
243 close(fd);
244
245 return ret;
246}
247#endif
248
249/*
250 * Try to load in a ckch every files related to a ckch.
251 * (PEM, sctl, ocsp, issuer etc.)
252 *
253 * This function is only used to load files during the configuration parsing,
254 * it is not used with the CLI.
255 *
256 * This allows us to carry the contents of the file without having to read the
257 * file multiple times. The caller must call
258 * ssl_sock_free_cert_key_and_chain_contents.
259 *
260 * returns:
261 * 0 on Success
262 * 1 on SSL Failure
263 */
264int ssl_sock_load_files_into_ckch(const char *path, struct cert_key_and_chain *ckch, char **err)
265{
William Lallemand8e8581e2020-10-20 17:36:46 +0200266 struct buffer *fp = NULL;
William Lallemand03c331c2020-05-13 10:10:01 +0200267 int ret = 1;
268
269 /* try to load the PEM */
270 if (ssl_sock_load_pem_into_ckch(path, NULL, ckch , err) != 0) {
271 goto end;
272 }
273
William Lallemand8e8581e2020-10-20 17:36:46 +0200274 fp = alloc_trash_chunk();
275 if (!fp) {
276 memprintf(err, "%sCan't allocate memory\n", err && *err ? *err : "");
277 goto end;
278 }
279
280 if (!chunk_strcpy(fp, path) || (b_data(fp) > MAXPATHLEN)) {
281 memprintf(err, "%s '%s' filename too long'.\n",
282 err && *err ? *err : "", fp->area);
283 ret = 1;
284 goto end;
285 }
286
William Lallemand089c1382020-10-23 17:35:12 +0200287 /* remove the ".crt" extension */
William Lallemand8e8581e2020-10-20 17:36:46 +0200288 if (global_ssl.extra_files_noext) {
289 char *ext;
290
291 /* look for the extension */
292 if ((ext = strrchr(fp->area, '.'))) {
William Lallemand8e8581e2020-10-20 17:36:46 +0200293
Tim Duesterhuse5ff1412021-01-02 22:31:53 +0100294 if (strcmp(ext, ".crt") == 0) {
William Lallemand8e8581e2020-10-20 17:36:46 +0200295 *ext = '\0';
William Lallemand089c1382020-10-23 17:35:12 +0200296 fp->data = strlen(fp->area);
297 }
William Lallemand8e8581e2020-10-20 17:36:46 +0200298 }
299
300 }
301
William Lallemand03c331c2020-05-13 10:10:01 +0200302 /* try to load an external private key if it wasn't in the PEM */
303 if ((ckch->key == NULL) && (global_ssl.extra_files & SSL_GF_KEY)) {
William Lallemand03c331c2020-05-13 10:10:01 +0200304 struct stat st;
305
William Lallemand8e8581e2020-10-20 17:36:46 +0200306
307 if (!chunk_strcat(fp, ".key") || (b_data(fp) > MAXPATHLEN)) {
308 memprintf(err, "%s '%s' filename too long'.\n",
309 err && *err ? *err : "", fp->area);
310 ret = 1;
311 goto end;
312 }
313
314 if (stat(fp->area, &st) == 0) {
315 if (ssl_sock_load_key_into_ckch(fp->area, NULL, ckch, err)) {
William Lallemand03c331c2020-05-13 10:10:01 +0200316 memprintf(err, "%s '%s' is present but cannot be read or parsed'.\n",
William Lallemand8e8581e2020-10-20 17:36:46 +0200317 err && *err ? *err : "", fp->area);
William Lallemand03c331c2020-05-13 10:10:01 +0200318 goto end;
319 }
320 }
William Lallemand03c331c2020-05-13 10:10:01 +0200321
William Lallemand8e8581e2020-10-20 17:36:46 +0200322 if (ckch->key == NULL) {
323 memprintf(err, "%sNo Private Key found in '%s'.\n", err && *err ? *err : "", fp->area);
324 goto end;
325 }
326 /* remove the added extension */
327 *(fp->area + fp->data - strlen(".key")) = '\0';
328 b_sub(fp, strlen(".key"));
William Lallemand03c331c2020-05-13 10:10:01 +0200329 }
330
331 if (!X509_check_private_key(ckch->cert, ckch->key)) {
332 memprintf(err, "%sinconsistencies between private key and certificate loaded '%s'.\n",
333 err && *err ? *err : "", path);
334 goto end;
335 }
336
Ilya Shipitsinc47d6762021-02-13 11:45:33 +0500337#ifdef HAVE_SSL_SCTL
William Lallemand03c331c2020-05-13 10:10:01 +0200338 /* try to load the sctl file */
339 if (global_ssl.extra_files & SSL_GF_SCTL) {
William Lallemand03c331c2020-05-13 10:10:01 +0200340 struct stat st;
341
William Lallemand8e8581e2020-10-20 17:36:46 +0200342 if (!chunk_strcat(fp, ".sctl") || b_data(fp) > MAXPATHLEN) {
343 memprintf(err, "%s '%s' filename too long'.\n",
344 err && *err ? *err : "", fp->area);
345 ret = 1;
346 goto end;
347 }
348
349 if (stat(fp->area, &st) == 0) {
350 if (ssl_sock_load_sctl_from_file(fp->area, NULL, ckch, err)) {
William Lallemand03c331c2020-05-13 10:10:01 +0200351 memprintf(err, "%s '%s.sctl' is present but cannot be read or parsed'.\n",
William Lallemand8e8581e2020-10-20 17:36:46 +0200352 err && *err ? *err : "", fp->area);
William Lallemand03c331c2020-05-13 10:10:01 +0200353 ret = 1;
354 goto end;
355 }
356 }
William Lallemand8e8581e2020-10-20 17:36:46 +0200357 /* remove the added extension */
358 *(fp->area + fp->data - strlen(".sctl")) = '\0';
359 b_sub(fp, strlen(".sctl"));
William Lallemand03c331c2020-05-13 10:10:01 +0200360 }
361#endif
362
363 /* try to load an ocsp response file */
364 if (global_ssl.extra_files & SSL_GF_OCSP) {
William Lallemand03c331c2020-05-13 10:10:01 +0200365 struct stat st;
366
William Lallemand8e8581e2020-10-20 17:36:46 +0200367 if (!chunk_strcat(fp, ".ocsp") || b_data(fp) > MAXPATHLEN) {
368 memprintf(err, "%s '%s' filename too long'.\n",
369 err && *err ? *err : "", fp->area);
370 ret = 1;
371 goto end;
372 }
373
374 if (stat(fp->area, &st) == 0) {
375 if (ssl_sock_load_ocsp_response_from_file(fp->area, NULL, ckch, err)) {
William Lallemand03c331c2020-05-13 10:10:01 +0200376 ret = 1;
377 goto end;
378 }
379 }
William Lallemand8e8581e2020-10-20 17:36:46 +0200380 /* remove the added extension */
381 *(fp->area + fp->data - strlen(".ocsp")) = '\0';
382 b_sub(fp, strlen(".ocsp"));
William Lallemand03c331c2020-05-13 10:10:01 +0200383 }
384
385#ifndef OPENSSL_IS_BORINGSSL /* Useless for BoringSSL */
386 if (ckch->ocsp_response && (global_ssl.extra_files & SSL_GF_OCSP_ISSUER)) {
387 /* if no issuer was found, try to load an issuer from the .issuer */
388 if (!ckch->ocsp_issuer) {
389 struct stat st;
William Lallemand8e8581e2020-10-20 17:36:46 +0200390
391 if (!chunk_strcat(fp, ".issuer") || b_data(fp) > MAXPATHLEN) {
392 memprintf(err, "%s '%s' filename too long'.\n",
393 err && *err ? *err : "", fp->area);
394 ret = 1;
395 goto end;
396 }
William Lallemand03c331c2020-05-13 10:10:01 +0200397
William Lallemand8e8581e2020-10-20 17:36:46 +0200398 if (stat(fp->area, &st) == 0) {
399 if (ssl_sock_load_issuer_file_into_ckch(fp->area, NULL, ckch, err)) {
William Lallemand03c331c2020-05-13 10:10:01 +0200400 ret = 1;
401 goto end;
402 }
403
404 if (X509_check_issued(ckch->ocsp_issuer, ckch->cert) != X509_V_OK) {
405 memprintf(err, "%s '%s' is not an issuer'.\n",
William Lallemand8e8581e2020-10-20 17:36:46 +0200406 err && *err ? *err : "", fp->area);
William Lallemand03c331c2020-05-13 10:10:01 +0200407 ret = 1;
408 goto end;
409 }
410 }
William Lallemand8e8581e2020-10-20 17:36:46 +0200411 /* remove the added extension */
412 *(fp->area + fp->data - strlen(".issuer")) = '\0';
413 b_sub(fp, strlen(".issuer"));
William Lallemand03c331c2020-05-13 10:10:01 +0200414 }
415 }
416#endif
417
418 ret = 0;
419
420end:
421
422 ERR_clear_error();
423
424 /* Something went wrong in one of the reads */
425 if (ret != 0)
426 ssl_sock_free_cert_key_and_chain_contents(ckch);
427
William Lallemand8e8581e2020-10-20 17:36:46 +0200428 free_trash_chunk(fp);
429
William Lallemand03c331c2020-05-13 10:10:01 +0200430 return ret;
431}
432
433/*
434 * Try to load a private key file from a <path> or a buffer <buf>
435 *
436 * If it failed you should not attempt to use the ckch but free it.
437 *
438 * Return 0 on success or != 0 on failure
439 */
440int ssl_sock_load_key_into_ckch(const char *path, char *buf, struct cert_key_and_chain *ckch , char **err)
441{
442 BIO *in = NULL;
443 int ret = 1;
444 EVP_PKEY *key = NULL;
445
446 if (buf) {
447 /* reading from a buffer */
448 in = BIO_new_mem_buf(buf, -1);
449 if (in == NULL) {
450 memprintf(err, "%sCan't allocate memory\n", err && *err ? *err : "");
451 goto end;
452 }
453
454 } else {
455 /* reading from a file */
456 in = BIO_new(BIO_s_file());
457 if (in == NULL)
458 goto end;
459
460 if (BIO_read_filename(in, path) <= 0)
461 goto end;
462 }
463
464 /* Read Private Key */
465 key = PEM_read_bio_PrivateKey(in, NULL, NULL, NULL);
466 if (key == NULL) {
467 memprintf(err, "%sunable to load private key from file '%s'.\n",
468 err && *err ? *err : "", path);
469 goto end;
470 }
471
472 ret = 0;
473
474 SWAP(ckch->key, key);
475
476end:
477
478 ERR_clear_error();
479 if (in)
480 BIO_free(in);
481 if (key)
482 EVP_PKEY_free(key);
483
484 return ret;
485}
486
487/*
488 * Try to load a PEM file from a <path> or a buffer <buf>
489 * The PEM must contain at least a Certificate,
490 * It could contain a DH, a certificate chain and a PrivateKey.
491 *
492 * If it failed you should not attempt to use the ckch but free it.
493 *
494 * Return 0 on success or != 0 on failure
495 */
496int ssl_sock_load_pem_into_ckch(const char *path, char *buf, struct cert_key_and_chain *ckch , char **err)
497{
498 BIO *in = NULL;
499 int ret = 1;
500 X509 *ca;
501 X509 *cert = NULL;
502 EVP_PKEY *key = NULL;
Remi Tricot-Le Bretonc76c3c42022-02-11 12:04:55 +0100503 HASSL_DH *dh = NULL;
William Lallemand03c331c2020-05-13 10:10:01 +0200504 STACK_OF(X509) *chain = NULL;
505
506 if (buf) {
507 /* reading from a buffer */
508 in = BIO_new_mem_buf(buf, -1);
509 if (in == NULL) {
510 memprintf(err, "%sCan't allocate memory\n", err && *err ? *err : "");
511 goto end;
512 }
513
514 } else {
515 /* reading from a file */
516 in = BIO_new(BIO_s_file());
517 if (in == NULL) {
518 memprintf(err, "%sCan't allocate memory\n", err && *err ? *err : "");
519 goto end;
520 }
521
522 if (BIO_read_filename(in, path) <= 0) {
523 memprintf(err, "%scannot open the file '%s'.\n",
524 err && *err ? *err : "", path);
525 goto end;
526 }
527 }
528
529 /* Read Private Key */
530 key = PEM_read_bio_PrivateKey(in, NULL, NULL, NULL);
531 /* no need to check for errors here, because the private key could be loaded later */
532
533#ifndef OPENSSL_NO_DH
534 /* Seek back to beginning of file */
535 if (BIO_reset(in) == -1) {
536 memprintf(err, "%san error occurred while reading the file '%s'.\n",
537 err && *err ? *err : "", path);
538 goto end;
539 }
540
Remi Tricot-Le Bretonc76c3c42022-02-11 12:04:55 +0100541 dh = ssl_sock_get_dh_from_bio(in);
542 ERR_clear_error();
William Lallemand03c331c2020-05-13 10:10:01 +0200543 /* no need to return an error there, dh is not mandatory */
544#endif
545
546 /* Seek back to beginning of file */
547 if (BIO_reset(in) == -1) {
548 memprintf(err, "%san error occurred while reading the file '%s'.\n",
549 err && *err ? *err : "", path);
550 goto end;
551 }
552
553 /* Read Certificate */
554 cert = PEM_read_bio_X509_AUX(in, NULL, NULL, NULL);
555 if (cert == NULL) {
556 memprintf(err, "%sunable to load certificate from file '%s'.\n",
557 err && *err ? *err : "", path);
558 goto end;
559 }
560
561 /* Look for a Certificate Chain */
562 while ((ca = PEM_read_bio_X509(in, NULL, NULL, NULL))) {
563 if (chain == NULL)
564 chain = sk_X509_new_null();
565 if (!sk_X509_push(chain, ca)) {
566 X509_free(ca);
567 goto end;
568 }
569 }
570
571 ret = ERR_get_error();
572 if (ret && (ERR_GET_LIB(ret) != ERR_LIB_PEM && ERR_GET_REASON(ret) != PEM_R_NO_START_LINE)) {
573 memprintf(err, "%sunable to load certificate chain from file '%s'.\n",
574 err && *err ? *err : "", path);
575 goto end;
576 }
577
578 /* once it loaded the PEM, it should remove everything else in the ckch */
579 if (ckch->ocsp_response) {
Willy Tarreau61cfdf42021-02-20 10:46:51 +0100580 ha_free(&ckch->ocsp_response->area);
581 ha_free(&ckch->ocsp_response);
William Lallemand03c331c2020-05-13 10:10:01 +0200582 }
583
584 if (ckch->sctl) {
Willy Tarreau61cfdf42021-02-20 10:46:51 +0100585 ha_free(&ckch->sctl->area);
586 ha_free(&ckch->sctl);
William Lallemand03c331c2020-05-13 10:10:01 +0200587 }
588
589 if (ckch->ocsp_issuer) {
590 X509_free(ckch->ocsp_issuer);
591 ckch->ocsp_issuer = NULL;
592 }
593
594 /* no error, fill ckch with new context, old context will be free at end: */
595 SWAP(ckch->key, key);
596 SWAP(ckch->dh, dh);
597 SWAP(ckch->cert, cert);
598 SWAP(ckch->chain, chain);
599
600 ret = 0;
601
602end:
603
604 ERR_clear_error();
605 if (in)
606 BIO_free(in);
607 if (key)
608 EVP_PKEY_free(key);
609 if (dh)
Remi Tricot-Le Bretonc76c3c42022-02-11 12:04:55 +0100610 HASSL_DH_free(dh);
William Lallemand03c331c2020-05-13 10:10:01 +0200611 if (cert)
612 X509_free(cert);
613 if (chain)
614 sk_X509_pop_free(chain, X509_free);
615
616 return ret;
617}
618
619/* Frees the contents of a cert_key_and_chain
620 */
621void ssl_sock_free_cert_key_and_chain_contents(struct cert_key_and_chain *ckch)
622{
623 if (!ckch)
624 return;
625
626 /* Free the certificate and set pointer to NULL */
627 if (ckch->cert)
628 X509_free(ckch->cert);
629 ckch->cert = NULL;
630
631 /* Free the key and set pointer to NULL */
632 if (ckch->key)
633 EVP_PKEY_free(ckch->key);
634 ckch->key = NULL;
635
636 /* Free each certificate in the chain */
637 if (ckch->chain)
638 sk_X509_pop_free(ckch->chain, X509_free);
639 ckch->chain = NULL;
640
641 if (ckch->dh)
Remi Tricot-Le Bretonc76c3c42022-02-11 12:04:55 +0100642 HASSL_DH_free(ckch->dh);
William Lallemand03c331c2020-05-13 10:10:01 +0200643 ckch->dh = NULL;
644
645 if (ckch->sctl) {
Willy Tarreau61cfdf42021-02-20 10:46:51 +0100646 ha_free(&ckch->sctl->area);
647 ha_free(&ckch->sctl);
William Lallemand03c331c2020-05-13 10:10:01 +0200648 }
649
650 if (ckch->ocsp_response) {
Willy Tarreau61cfdf42021-02-20 10:46:51 +0100651 ha_free(&ckch->ocsp_response->area);
652 ha_free(&ckch->ocsp_response);
William Lallemand03c331c2020-05-13 10:10:01 +0200653 }
654
655 if (ckch->ocsp_issuer)
656 X509_free(ckch->ocsp_issuer);
657 ckch->ocsp_issuer = NULL;
658}
659
660/*
661 *
662 * This function copy a cert_key_and_chain in memory
663 *
664 * It's used to try to apply changes on a ckch before committing them, because
665 * most of the time it's not possible to revert those changes
666 *
667 * Return a the dst or NULL
668 */
669struct cert_key_and_chain *ssl_sock_copy_cert_key_and_chain(struct cert_key_and_chain *src,
670 struct cert_key_and_chain *dst)
671{
William Lallemand6c096142021-02-23 14:45:45 +0100672 if (!src || !dst)
673 return NULL;
674
William Lallemand03c331c2020-05-13 10:10:01 +0200675 if (src->cert) {
676 dst->cert = src->cert;
677 X509_up_ref(src->cert);
678 }
679
680 if (src->key) {
681 dst->key = src->key;
682 EVP_PKEY_up_ref(src->key);
683 }
684
685 if (src->chain) {
686 dst->chain = X509_chain_up_ref(src->chain);
687 }
688
689 if (src->dh) {
Remi Tricot-Le Bretonc76c3c42022-02-11 12:04:55 +0100690 HASSL_DH_up_ref(src->dh);
William Lallemand03c331c2020-05-13 10:10:01 +0200691 dst->dh = src->dh;
692 }
693
694 if (src->sctl) {
695 struct buffer *sctl;
696
697 sctl = calloc(1, sizeof(*sctl));
698 if (!chunk_dup(sctl, src->sctl)) {
Willy Tarreau61cfdf42021-02-20 10:46:51 +0100699 ha_free(&sctl);
William Lallemand03c331c2020-05-13 10:10:01 +0200700 goto error;
701 }
702 dst->sctl = sctl;
703 }
704
705 if (src->ocsp_response) {
706 struct buffer *ocsp_response;
707
708 ocsp_response = calloc(1, sizeof(*ocsp_response));
709 if (!chunk_dup(ocsp_response, src->ocsp_response)) {
Willy Tarreau61cfdf42021-02-20 10:46:51 +0100710 ha_free(&ocsp_response);
William Lallemand03c331c2020-05-13 10:10:01 +0200711 goto error;
712 }
713 dst->ocsp_response = ocsp_response;
714 }
715
716 if (src->ocsp_issuer) {
717 X509_up_ref(src->ocsp_issuer);
718 dst->ocsp_issuer = src->ocsp_issuer;
719 }
720
721 return dst;
722
723error:
724
725 /* free everything */
726 ssl_sock_free_cert_key_and_chain_contents(dst);
727
728 return NULL;
729}
730
731/*
732 * return 0 on success or != 0 on failure
733 */
734int ssl_sock_load_issuer_file_into_ckch(const char *path, char *buf, struct cert_key_and_chain *ckch, char **err)
735{
736 int ret = 1;
737 BIO *in = NULL;
738 X509 *issuer;
739
740 if (buf) {
741 /* reading from a buffer */
742 in = BIO_new_mem_buf(buf, -1);
743 if (in == NULL) {
744 memprintf(err, "%sCan't allocate memory\n", err && *err ? *err : "");
745 goto end;
746 }
747
748 } else {
749 /* reading from a file */
750 in = BIO_new(BIO_s_file());
751 if (in == NULL)
752 goto end;
753
754 if (BIO_read_filename(in, path) <= 0)
755 goto end;
756 }
757
758 issuer = PEM_read_bio_X509_AUX(in, NULL, NULL, NULL);
759 if (!issuer) {
760 memprintf(err, "%s'%s' cannot be read or parsed'.\n",
761 err && *err ? *err : "", path);
762 goto end;
763 }
764 /* no error, fill ckch with new context, old context must be free */
765 if (ckch->ocsp_issuer)
766 X509_free(ckch->ocsp_issuer);
767 ckch->ocsp_issuer = issuer;
768 ret = 0;
769
770end:
771
772 ERR_clear_error();
773 if (in)
774 BIO_free(in);
775
776 return ret;
777}
778
779/******************** ckch_store functions ***********************************
780 * The ckch_store is a structure used to cache and index the SSL files used in
781 * configuration
782 */
783
784/*
785 * Free a ckch_store, its ckch, its instances and remove it from the ebtree
786 */
787void ckch_store_free(struct ckch_store *store)
788{
789 struct ckch_inst *inst, *inst_s;
790
791 if (!store)
792 return;
793
William Lallemandbd8e6ed2020-09-16 16:08:08 +0200794 ssl_sock_free_cert_key_and_chain_contents(store->ckch);
William Lallemand03c331c2020-05-13 10:10:01 +0200795
Willy Tarreau61cfdf42021-02-20 10:46:51 +0100796 ha_free(&store->ckch);
William Lallemand03c331c2020-05-13 10:10:01 +0200797
798 list_for_each_entry_safe(inst, inst_s, &store->ckch_inst, by_ckchs) {
799 ckch_inst_free(inst);
800 }
801 ebmb_delete(&store->node);
802 free(store);
803}
804
805/*
806 * create and initialize a ckch_store
807 * <path> is the key name
808 * <nmemb> is the number of store->ckch objects to allocate
809 *
810 * Return a ckch_store or NULL upon failure.
811 */
William Lallemandbd8e6ed2020-09-16 16:08:08 +0200812struct ckch_store *ckch_store_new(const char *filename)
William Lallemand03c331c2020-05-13 10:10:01 +0200813{
814 struct ckch_store *store;
815 int pathlen;
816
817 pathlen = strlen(filename);
818 store = calloc(1, sizeof(*store) + pathlen + 1);
819 if (!store)
820 return NULL;
821
William Lallemand03c331c2020-05-13 10:10:01 +0200822 memcpy(store->path, filename, pathlen + 1);
823
824 LIST_INIT(&store->ckch_inst);
825 LIST_INIT(&store->crtlist_entry);
826
William Lallemandbd8e6ed2020-09-16 16:08:08 +0200827 store->ckch = calloc(1, sizeof(*store->ckch));
William Lallemand03c331c2020-05-13 10:10:01 +0200828 if (!store->ckch)
829 goto error;
830
831 return store;
832error:
833 ckch_store_free(store);
834 return NULL;
835}
836
837/* allocate and duplicate a ckch_store
838 * Return a new ckch_store or NULL */
839struct ckch_store *ckchs_dup(const struct ckch_store *src)
840{
841 struct ckch_store *dst;
842
William Lallemand6c096142021-02-23 14:45:45 +0100843 if (!src)
844 return NULL;
845
William Lallemandbd8e6ed2020-09-16 16:08:08 +0200846 dst = ckch_store_new(src->path);
Eric Salama6ac61e32021-02-23 16:50:57 +0100847 if (!dst)
848 return NULL;
William Lallemand03c331c2020-05-13 10:10:01 +0200849
William Lallemandbd8e6ed2020-09-16 16:08:08 +0200850 if (!ssl_sock_copy_cert_key_and_chain(src->ckch, dst->ckch))
851 goto error;
William Lallemand03c331c2020-05-13 10:10:01 +0200852
853 return dst;
854
855error:
856 ckch_store_free(dst);
857
858 return NULL;
859}
860
861/*
862 * lookup a path into the ckchs tree.
863 */
864struct ckch_store *ckchs_lookup(char *path)
865{
866 struct ebmb_node *eb;
867
868 eb = ebst_lookup(&ckchs_tree, path);
869 if (!eb)
870 return NULL;
871
872 return ebmb_entry(eb, struct ckch_store, node);
873}
874
875/*
876 * This function allocate a ckch_store and populate it with certificates from files.
877 */
William Lallemandbd8e6ed2020-09-16 16:08:08 +0200878struct ckch_store *ckchs_load_cert_file(char *path, char **err)
William Lallemand03c331c2020-05-13 10:10:01 +0200879{
880 struct ckch_store *ckchs;
881
William Lallemandbd8e6ed2020-09-16 16:08:08 +0200882 ckchs = ckch_store_new(path);
William Lallemand03c331c2020-05-13 10:10:01 +0200883 if (!ckchs) {
884 memprintf(err, "%sunable to allocate memory.\n", err && *err ? *err : "");
885 goto end;
886 }
William Lallemand03c331c2020-05-13 10:10:01 +0200887
William Lallemandbd8e6ed2020-09-16 16:08:08 +0200888 if (ssl_sock_load_files_into_ckch(path, ckchs->ckch, err) == 1)
889 goto end;
William Lallemand03c331c2020-05-13 10:10:01 +0200890
William Lallemandbd8e6ed2020-09-16 16:08:08 +0200891 /* insert into the ckchs tree */
892 memcpy(ckchs->path, path, strlen(path) + 1);
893 ebst_insert(&ckchs_tree, &ckchs->node);
William Lallemand03c331c2020-05-13 10:10:01 +0200894 return ckchs;
895
896end:
897 ckch_store_free(ckchs);
898
899 return NULL;
900}
901
William Lallemandfa1d8b42020-05-13 15:46:10 +0200902
903/******************** ckch_inst functions ******************************/
904
905/* unlink a ckch_inst, free all SNIs, free the ckch_inst */
906/* The caller must use the lock of the bind_conf if used with inserted SNIs */
907void ckch_inst_free(struct ckch_inst *inst)
908{
909 struct sni_ctx *sni, *sni_s;
Remi Tricot-Le Breton4458b972021-02-19 17:41:55 +0100910 struct ckch_inst_link_ref *link_ref, *link_ref_s;
William Lallemandfa1d8b42020-05-13 15:46:10 +0200911
912 if (inst == NULL)
913 return;
914
915 list_for_each_entry_safe(sni, sni_s, &inst->sni_ctx, by_ckch_inst) {
916 SSL_CTX_free(sni->ctx);
Willy Tarreau2b718102021-04-21 07:32:39 +0200917 LIST_DELETE(&sni->by_ckch_inst);
William Lallemandfa1d8b42020-05-13 15:46:10 +0200918 ebmb_delete(&sni->name);
919 free(sni);
920 }
Remi Tricot-Le Bretonf3eedfe2021-01-25 17:19:44 +0100921 SSL_CTX_free(inst->ctx);
922 inst->ctx = NULL;
Willy Tarreau2b718102021-04-21 07:32:39 +0200923 LIST_DELETE(&inst->by_ckchs);
924 LIST_DELETE(&inst->by_crtlist_entry);
Remi Tricot-Le Breton4458b972021-02-19 17:41:55 +0100925
926 list_for_each_entry_safe(link_ref, link_ref_s, &inst->cafile_link_refs, list) {
927 LIST_DELETE(&link_ref->link->list);
928 LIST_DELETE(&link_ref->list);
929 free(link_ref);
930 }
931
William Lallemandfa1d8b42020-05-13 15:46:10 +0200932 free(inst);
933}
934
935/* Alloc and init a ckch_inst */
936struct ckch_inst *ckch_inst_new()
937{
938 struct ckch_inst *ckch_inst;
939
940 ckch_inst = calloc(1, sizeof *ckch_inst);
941 if (!ckch_inst)
942 return NULL;
943
944 LIST_INIT(&ckch_inst->sni_ctx);
945 LIST_INIT(&ckch_inst->by_ckchs);
946 LIST_INIT(&ckch_inst->by_crtlist_entry);
Remi Tricot-Le Breton4458b972021-02-19 17:41:55 +0100947 LIST_INIT(&ckch_inst->cafile_link_refs);
William Lallemandfa1d8b42020-05-13 15:46:10 +0200948
949 return ckch_inst;
950}
951
Remi Tricot-Le Bretonaf8820a2021-04-13 10:10:37 +0200952
953/******************** ssl_store functions ******************************/
Remi Tricot-Le Breton9f0c9362021-02-19 15:06:28 +0100954struct eb_root cafile_tree = EB_ROOT;
Remi Tricot-Le Bretonaf8820a2021-04-13 10:10:37 +0200955
Remi Tricot-Le Breton9f0c9362021-02-19 15:06:28 +0100956/*
957 * Returns the cafile_entry found in the cafile_tree indexed by the path 'path'.
958 * If 'oldest_entry' is 1, returns the "original" cafile_entry (since
959 * during a set cafile/commit cafile cycle there might be two entries for any
960 * given path, the original one and the new one set via the CLI but not
961 * committed yet).
962 */
Remi Tricot-Le Breton4458b972021-02-19 17:41:55 +0100963struct cafile_entry *ssl_store_get_cafile_entry(char *path, int oldest_entry)
Remi Tricot-Le Bretonaf8820a2021-04-13 10:10:37 +0200964{
Remi Tricot-Le Breton9f0c9362021-02-19 15:06:28 +0100965 struct cafile_entry *ca_e = NULL;
Remi Tricot-Le Bretonaf8820a2021-04-13 10:10:37 +0200966 struct ebmb_node *eb;
967
968 eb = ebst_lookup(&cafile_tree, path);
Remi Tricot-Le Breton9f0c9362021-02-19 15:06:28 +0100969 while (eb) {
Remi Tricot-Le Bretonaf8820a2021-04-13 10:10:37 +0200970 ca_e = ebmb_entry(eb, struct cafile_entry, node);
Remi Tricot-Le Breton9f0c9362021-02-19 15:06:28 +0100971 /* The ebst_lookup in a tree that has duplicates returns the
972 * oldest entry first. If we want the latest entry, we need to
973 * iterate over all the duplicates until we find the last one
974 * (in our case there should never be more than two entries for
975 * any given path). */
976 if (oldest_entry)
977 return ca_e;
978 eb = ebmb_next_dup(eb);
Remi Tricot-Le Bretonaf8820a2021-04-13 10:10:37 +0200979 }
Remi Tricot-Le Breton9f0c9362021-02-19 15:06:28 +0100980 return ca_e;
981}
982
Remi Tricot-Le Breton38c999b2021-02-23 16:28:43 +0100983int ssl_store_add_uncommitted_cafile_entry(struct cafile_entry *entry)
984{
985 return (ebst_insert(&cafile_tree, &entry->node) != &entry->node);
986}
987
Remi Tricot-Le Breton9f0c9362021-02-19 15:06:28 +0100988X509_STORE* ssl_store_get0_locations_file(char *path)
989{
990 struct cafile_entry *ca_e = ssl_store_get_cafile_entry(path, 0);
991
992 if (ca_e)
993 return ca_e->ca_store;
994
Remi Tricot-Le Bretonaf8820a2021-04-13 10:10:37 +0200995 return NULL;
996}
997
Remi Tricot-Le Breton5daff3c2021-02-22 15:54:55 +0100998/* Create a cafile_entry object, without adding it to the cafile_tree. */
Remi Tricot-Le Breton0bb48242021-04-16 17:59:23 +0200999struct cafile_entry *ssl_store_create_cafile_entry(char *path, X509_STORE *store, enum cafile_type type)
Remi Tricot-Le Breton5daff3c2021-02-22 15:54:55 +01001000{
1001 struct cafile_entry *ca_e;
1002 int pathlen;
1003
1004 pathlen = strlen(path);
1005
1006 ca_e = calloc(1, sizeof(*ca_e) + pathlen + 1);
1007 if (ca_e) {
1008 memcpy(ca_e->path, path, pathlen + 1);
1009 ca_e->ca_store = store;
Remi Tricot-Le Breton0bb48242021-04-16 17:59:23 +02001010 ca_e->type = type;
Remi Tricot-Le Breton5daff3c2021-02-22 15:54:55 +01001011 LIST_INIT(&ca_e->ckch_inst_link);
1012 }
1013 return ca_e;
1014}
1015
1016/* Delete a cafile_entry. The caller is responsible from removing this entry
1017 * from the cafile_tree first if is was previously added into it. */
1018void ssl_store_delete_cafile_entry(struct cafile_entry *ca_e)
1019{
1020 struct ckch_inst_link *link, *link_s;
1021 if (!ca_e)
1022 return;
1023
1024 X509_STORE_free(ca_e->ca_store);
1025
1026 list_for_each_entry_safe(link, link_s, &ca_e->ckch_inst_link, list) {
1027 struct ckch_inst *inst = link->ckch_inst;
1028 struct ckch_inst_link_ref *link_ref, *link_ref_s;
1029 list_for_each_entry_safe(link_ref, link_ref_s, &inst->cafile_link_refs, list) {
1030 if (link_ref->link == link) {
1031 LIST_DELETE(&link_ref->list);
1032 free(link_ref);
1033 break;
1034 }
1035 }
1036 LIST_DELETE(&link->list);
1037 free(link);
1038 }
1039
1040 free(ca_e);
1041}
1042
Remi Tricot-Le Breton383fb142021-02-22 18:26:14 +01001043/*
1044 * Build a cafile_entry out of a buffer instead of out of a file.
1045 * This function is used when the "commit ssl ca-file" cli command is used.
1046 * It can parse CERTIFICATE sections as well as CRL ones.
1047 * Returns 0 in case of success, 1 otherwise.
1048 */
1049int ssl_store_load_ca_from_buf(struct cafile_entry *ca_e, char *cert_buf)
1050{
1051 int retval = 0;
1052
1053 if (!ca_e)
1054 return 1;
1055
1056 if (!ca_e->ca_store) {
1057 ca_e->ca_store = X509_STORE_new();
1058 if (ca_e->ca_store) {
1059 BIO *bio = BIO_new_mem_buf(cert_buf, strlen(cert_buf));
1060 if (bio) {
1061 X509_INFO *info;
1062 int i;
1063 STACK_OF(X509_INFO) *infos = PEM_X509_INFO_read_bio(bio, NULL, NULL, NULL);
1064 if (!infos)
1065 {
1066 BIO_free(bio);
1067 return 1;
1068 }
1069
1070 for (i = 0; i < sk_X509_INFO_num(infos) && !retval; i++) {
1071 info = sk_X509_INFO_value(infos, i);
1072 /* X509_STORE_add_cert and X509_STORE_add_crl return 1 on success */
1073 if (info->x509) {
1074 retval = !X509_STORE_add_cert(ca_e->ca_store, info->x509);
1075 }
1076 if (!retval && info->crl) {
1077 retval = !X509_STORE_add_crl(ca_e->ca_store, info->crl);
1078 }
1079 }
1080 retval = retval || (i != sk_X509_INFO_num(infos));
1081
1082 /* Cleanup */
1083 sk_X509_INFO_pop_free(infos, X509_INFO_free);
1084 BIO_free(bio);
1085 }
1086 }
1087 }
1088
1089 return retval;
1090}
1091
Remi Tricot-Le Breton0bb48242021-04-16 17:59:23 +02001092int ssl_store_load_locations_file(char *path, int create_if_none, enum cafile_type type)
Remi Tricot-Le Bretonaf8820a2021-04-13 10:10:37 +02001093{
1094 X509_STORE *store = ssl_store_get0_locations_file(path);
1095
1096 /* If this function is called by the CLI, we should not call the
1097 * X509_STORE_load_locations function because it performs forbidden disk
1098 * accesses. */
1099 if (!store && create_if_none) {
William Lallemand87fd9942022-04-01 20:12:03 +02001100 STACK_OF(X509_OBJECT) *objs;
1101 int cert_count = 0;
1102 struct stat buf;
Remi Tricot-Le Bretonaf8820a2021-04-13 10:10:37 +02001103 struct cafile_entry *ca_e;
William Lallemandc6b17632022-04-01 23:39:37 +02001104 const char *file = NULL;
1105 const char *dir = NULL;
William Lallemand87fd9942022-04-01 20:12:03 +02001106
Remi Tricot-Le Bretonaf8820a2021-04-13 10:10:37 +02001107 store = X509_STORE_new();
William Lallemand87fd9942022-04-01 20:12:03 +02001108
William Lallemandc6b17632022-04-01 23:39:37 +02001109 if (strcmp(path, "@system-ca") == 0) {
1110 dir = X509_get_default_cert_dir();
William Lallemand87fd9942022-04-01 20:12:03 +02001111
William Lallemandc6b17632022-04-01 23:39:37 +02001112 } else {
1113
1114 if (stat(path, &buf))
1115 goto err;
1116
1117 if (S_ISDIR(buf.st_mode))
1118 dir = path;
1119 else
1120 file = path;
1121 }
William Lallemand87fd9942022-04-01 20:12:03 +02001122
1123 if (file) {
1124 if (!X509_STORE_load_locations(store, file, NULL)) {
1125 goto err;
Remi Tricot-Le Bretonaf8820a2021-04-13 10:10:37 +02001126 }
William Lallemand80296b42022-04-05 10:19:30 +02001127 } else if (dir) {
William Lallemand87fd9942022-04-01 20:12:03 +02001128 int n, i;
1129 struct dirent **de_list;
1130
1131 n = scandir(dir, &de_list, 0, alphasort);
1132 if (n < 0)
1133 goto err;
1134
1135 for (i= 0; i < n; i++) {
1136 char *end;
1137 struct dirent *de = de_list[i];
1138 BIO *in = NULL;
1139 X509 *ca = NULL;;
1140
1141 /* we try to load the files that would have
1142 * been loaded in an hashed directory loaded by
1143 * X509_LOOKUP_hash_dir, so according to "man 1
1144 * c_rehash", we should load ".pem", ".crt",
1145 * ".cer", or ".crl"
1146 */
1147 end = strrchr(de->d_name, '.');
1148 if (!end || (strcmp(end, ".pem") != 0 &&
1149 strcmp(end, ".crt") != 0 &&
1150 strcmp(end, ".cer") != 0 &&
1151 strcmp(end, ".crl") != 0)) {
1152 free(de);
1153 continue;
1154 }
1155 in = BIO_new(BIO_s_file());
1156 if (in == NULL)
1157 goto scandir_err;
1158
William Lallemandc6b17632022-04-01 23:39:37 +02001159 chunk_printf(&trash, "%s/%s", dir, de->d_name);
William Lallemand87fd9942022-04-01 20:12:03 +02001160
1161 if (BIO_read_filename(in, trash.area) == 0)
1162 goto scandir_err;
1163
1164 if (PEM_read_bio_X509_AUX(in, &ca, NULL, NULL) == NULL)
1165 goto scandir_err;
1166
1167 if (X509_STORE_add_cert(store, ca) == 0)
1168 goto scandir_err;
1169
1170 BIO_free(in);
1171 free(de);
1172 continue;
1173
1174scandir_err:
1175 BIO_free(in);
1176 free(de);
1177 ha_warning("ca-file: '%s' couldn't load '%s'\n", path, trash.area);
William Lallemand87fd9942022-04-01 20:12:03 +02001178
1179 }
1180 free(de_list);
William Lallemand80296b42022-04-05 10:19:30 +02001181 } else {
1182 ha_alert("ca-file: couldn't load '%s'\n", path);
1183 goto err;
Remi Tricot-Le Bretonaf8820a2021-04-13 10:10:37 +02001184 }
William Lallemand87fd9942022-04-01 20:12:03 +02001185
1186 objs = X509_STORE_get0_objects(store);
1187 cert_count = sk_X509_OBJECT_num(objs);
1188 if (cert_count == 0)
1189 ha_warning("ca-file: 0 CA were loaded from '%s'\n", path);
1190
1191 ca_e = ssl_store_create_cafile_entry(path, store, type);
1192 if (!ca_e)
1193 goto err;
1194 ebst_insert(&cafile_tree, &ca_e->node);
Remi Tricot-Le Bretonaf8820a2021-04-13 10:10:37 +02001195 }
1196 return (store != NULL);
William Lallemand87fd9942022-04-01 20:12:03 +02001197
1198err:
1199 X509_STORE_free(store);
1200 store = NULL;
1201 return 0;
1202
Remi Tricot-Le Bretonaf8820a2021-04-13 10:10:37 +02001203}
1204
1205
William Lallemandda8584c2020-05-14 10:14:37 +02001206/*************************** CLI commands ***********************/
1207
1208/* Type of SSL payloads that can be updated over the CLI */
1209
William Lallemandff8bf982022-03-29 10:44:23 +02001210struct cert_exts cert_exts[] = {
1211 { "", CERT_TYPE_PEM, &ssl_sock_load_pem_into_ckch }, /* default mode, no extensions */
William Lallemand26654e72022-03-30 12:01:32 +02001212 { "crt", CERT_TYPE_CRT, &ssl_sock_load_pem_into_ckch },
William Lallemandff8bf982022-03-29 10:44:23 +02001213 { "key", CERT_TYPE_KEY, &ssl_sock_load_key_into_ckch },
William Lallemandda8584c2020-05-14 10:14:37 +02001214#if ((defined SSL_CTRL_SET_TLSEXT_STATUS_REQ_CB && !defined OPENSSL_NO_OCSP) || defined OPENSSL_IS_BORINGSSL)
William Lallemandff8bf982022-03-29 10:44:23 +02001215 { "ocsp", CERT_TYPE_OCSP, &ssl_sock_load_ocsp_response_from_file },
William Lallemandda8584c2020-05-14 10:14:37 +02001216#endif
Ilya Shipitsinc47d6762021-02-13 11:45:33 +05001217#ifdef HAVE_SSL_SCTL
William Lallemandff8bf982022-03-29 10:44:23 +02001218 { "sctl", CERT_TYPE_SCTL, &ssl_sock_load_sctl_from_file },
William Lallemandda8584c2020-05-14 10:14:37 +02001219#endif
William Lallemandff8bf982022-03-29 10:44:23 +02001220 { "issuer", CERT_TYPE_ISSUER, &ssl_sock_load_issuer_file_into_ckch },
1221 { NULL, CERT_TYPE_MAX, NULL },
William Lallemandda8584c2020-05-14 10:14:37 +02001222};
1223
1224
1225/* release function of the `show ssl cert' command */
1226static void cli_release_show_cert(struct appctx *appctx)
1227{
1228 HA_SPIN_UNLOCK(CKCH_LOCK, &ckch_lock);
1229}
1230
1231/* IO handler of "show ssl cert <filename>" */
1232static int cli_io_handler_show_cert(struct appctx *appctx)
1233{
1234 struct buffer *trash = alloc_trash_chunk();
1235 struct ebmb_node *node;
Christopher Faulet86e1c332021-12-20 17:09:39 +01001236 struct stream_interface *si = cs_si(appctx->owner);
William Lallemandda8584c2020-05-14 10:14:37 +02001237 struct ckch_store *ckchs;
1238
1239 if (trash == NULL)
1240 return 1;
1241
1242 if (!appctx->ctx.ssl.old_ckchs) {
1243 if (ckchs_transaction.old_ckchs) {
1244 ckchs = ckchs_transaction.old_ckchs;
1245 chunk_appendf(trash, "# transaction\n");
William Lallemand5685ccf2020-09-16 16:12:25 +02001246 chunk_appendf(trash, "*%s\n", ckchs->path);
William Lallemandda8584c2020-05-14 10:14:37 +02001247 }
1248 }
1249
1250 if (!appctx->ctx.cli.p0) {
1251 chunk_appendf(trash, "# filename\n");
1252 node = ebmb_first(&ckchs_tree);
1253 } else {
1254 node = &((struct ckch_store *)appctx->ctx.cli.p0)->node;
1255 }
1256 while (node) {
1257 ckchs = ebmb_entry(node, struct ckch_store, node);
William Lallemand5685ccf2020-09-16 16:12:25 +02001258 chunk_appendf(trash, "%s\n", ckchs->path);
William Lallemandda8584c2020-05-14 10:14:37 +02001259
1260 node = ebmb_next(node);
1261 if (ci_putchk(si_ic(si), trash) == -1) {
1262 si_rx_room_blk(si);
1263 goto yield;
1264 }
1265 }
1266
1267 appctx->ctx.cli.p0 = NULL;
1268 free_trash_chunk(trash);
1269 return 1;
1270yield:
1271
1272 free_trash_chunk(trash);
1273 appctx->ctx.cli.p0 = ckchs;
1274 return 0; /* should come back */
1275}
1276
1277/*
1278 * Extract and format the DNS SAN extensions and copy result into a chuink
1279 * Return 0;
1280 */
1281#ifdef SSL_CTRL_SET_TLSEXT_HOSTNAME
1282static int ssl_sock_get_san_oneline(X509 *cert, struct buffer *out)
1283{
1284 int i;
1285 char *str;
1286 STACK_OF(GENERAL_NAME) *names = NULL;
1287
1288 names = X509_get_ext_d2i(cert, NID_subject_alt_name, NULL, NULL);
1289 if (names) {
1290 for (i = 0; i < sk_GENERAL_NAME_num(names); i++) {
1291 GENERAL_NAME *name = sk_GENERAL_NAME_value(names, i);
1292 if (i > 0)
1293 chunk_appendf(out, ", ");
1294 if (name->type == GEN_DNS) {
1295 if (ASN1_STRING_to_UTF8((unsigned char **)&str, name->d.dNSName) >= 0) {
1296 chunk_appendf(out, "DNS:%s", str);
1297 OPENSSL_free(str);
1298 }
1299 }
1300 }
1301 sk_GENERAL_NAME_pop_free(names, GENERAL_NAME_free);
1302 }
1303 return 0;
1304}
1305#endif
1306
Remi Tricot-Le Breton4458b972021-02-19 17:41:55 +01001307/*
1308 * Build the ckch_inst_link that will be chained in the CA file entry and the
1309 * corresponding ckch_inst_link_ref that will be chained in the ckch instance.
1310 * Return 0 in case of success.
1311 */
1312static int do_chain_inst_and_cafile(struct cafile_entry *cafile_entry, struct ckch_inst *ckch_inst)
1313{
1314 struct ckch_inst_link *new_link;
1315 if (!LIST_ISEMPTY(&cafile_entry->ckch_inst_link)) {
1316 struct ckch_inst_link *link = LIST_ELEM(cafile_entry->ckch_inst_link.n,
1317 typeof(link), list);
1318 /* Do not add multiple references to the same
1319 * instance in a cafile_entry */
1320 if (link->ckch_inst == ckch_inst) {
1321 return 1;
1322 }
1323 }
1324
1325 new_link = calloc(1, sizeof(*new_link));
1326 if (new_link) {
1327 struct ckch_inst_link_ref *new_link_ref = calloc(1, sizeof(*new_link_ref));
1328 if (!new_link_ref) {
1329 free(new_link);
1330 return 1;
1331 }
1332
1333 new_link->ckch_inst = ckch_inst;
1334 new_link_ref->link = new_link;
1335 LIST_INIT(&new_link->list);
1336 LIST_INIT(&new_link_ref->list);
1337
1338 LIST_APPEND(&cafile_entry->ckch_inst_link, &new_link->list);
1339 LIST_APPEND(&ckch_inst->cafile_link_refs, &new_link_ref->list);
1340 }
1341
1342 return 0;
1343}
1344
1345
1346/*
1347 * Link a CA file tree entry to the ckch instance that uses it.
1348 * To determine if and which CA file tree entries need to be linked to the
1349 * instance, we follow the same logic performed in ssl_sock_prepare_ctx when
1350 * processing the verify option.
1351 * This function works for a frontend as well as for a backend, depending on the
1352 * configuration parameters given (bind_conf or server).
1353 */
1354void ckch_inst_add_cafile_link(struct ckch_inst *ckch_inst, struct bind_conf *bind_conf,
1355 struct ssl_bind_conf *ssl_conf, const struct server *srv)
1356{
1357 int verify = SSL_VERIFY_NONE;
1358
1359 if (srv) {
1360
1361 if (global.ssl_server_verify == SSL_SERVER_VERIFY_REQUIRED)
1362 verify = SSL_VERIFY_PEER;
1363 switch (srv->ssl_ctx.verify) {
1364 case SSL_SOCK_VERIFY_NONE:
1365 verify = SSL_VERIFY_NONE;
1366 break;
1367 case SSL_SOCK_VERIFY_REQUIRED:
1368 verify = SSL_VERIFY_PEER;
1369 break;
1370 }
1371 }
1372 else {
1373 switch ((ssl_conf && ssl_conf->verify) ? ssl_conf->verify : bind_conf->ssl_conf.verify) {
1374 case SSL_SOCK_VERIFY_NONE:
1375 verify = SSL_VERIFY_NONE;
1376 break;
1377 case SSL_SOCK_VERIFY_OPTIONAL:
1378 verify = SSL_VERIFY_PEER;
1379 break;
1380 case SSL_SOCK_VERIFY_REQUIRED:
1381 verify = SSL_VERIFY_PEER|SSL_VERIFY_FAIL_IF_NO_PEER_CERT;
1382 break;
1383 }
1384 }
1385
1386 if (verify & SSL_VERIFY_PEER) {
1387 struct cafile_entry *ca_file_entry = NULL;
1388 struct cafile_entry *ca_verify_file_entry = NULL;
Remi Tricot-Le Bretonf81c70c2021-04-20 16:54:21 +02001389 struct cafile_entry *crl_file_entry = NULL;
Remi Tricot-Le Breton4458b972021-02-19 17:41:55 +01001390 if (srv) {
1391 if (srv->ssl_ctx.ca_file) {
1392 ca_file_entry = ssl_store_get_cafile_entry(srv->ssl_ctx.ca_file, 0);
1393
1394 }
Remi Tricot-Le Bretonf81c70c2021-04-20 16:54:21 +02001395 if (srv->ssl_ctx.crl_file) {
1396 crl_file_entry = ssl_store_get_cafile_entry(srv->ssl_ctx.crl_file, 0);
1397 }
Remi Tricot-Le Breton4458b972021-02-19 17:41:55 +01001398 }
1399 else {
1400 char *ca_file = (ssl_conf && ssl_conf->ca_file) ? ssl_conf->ca_file : bind_conf->ssl_conf.ca_file;
1401 char *ca_verify_file = (ssl_conf && ssl_conf->ca_verify_file) ? ssl_conf->ca_verify_file : bind_conf->ssl_conf.ca_verify_file;
Remi Tricot-Le Bretonf81c70c2021-04-20 16:54:21 +02001402 char *crl_file = (ssl_conf && ssl_conf->crl_file) ? ssl_conf->crl_file : bind_conf->ssl_conf.crl_file;
Remi Tricot-Le Breton4458b972021-02-19 17:41:55 +01001403
1404 if (ca_file)
1405 ca_file_entry = ssl_store_get_cafile_entry(ca_file, 0);
1406 if (ca_verify_file)
1407 ca_verify_file_entry = ssl_store_get_cafile_entry(ca_verify_file, 0);
Remi Tricot-Le Bretonf81c70c2021-04-20 16:54:21 +02001408 if (crl_file)
1409 crl_file_entry = ssl_store_get_cafile_entry(crl_file, 0);
Remi Tricot-Le Breton4458b972021-02-19 17:41:55 +01001410 }
1411
1412 if (ca_file_entry) {
1413 /* If we have a ckch instance that is not already in the
1414 * cafile_entry's list, add it to it. */
1415 if (do_chain_inst_and_cafile(ca_file_entry, ckch_inst))
1416 return;
1417
1418 }
1419 if (ca_verify_file_entry && (ca_file_entry != ca_verify_file_entry)) {
1420 /* If we have a ckch instance that is not already in the
1421 * cafile_entry's list, add it to it. */
1422 if (do_chain_inst_and_cafile(ca_verify_file_entry, ckch_inst))
1423 return;
1424 }
Remi Tricot-Le Bretonf81c70c2021-04-20 16:54:21 +02001425 if (crl_file_entry) {
1426 /* If we have a ckch instance that is not already in the
1427 * cafile_entry's list, add it to it. */
1428 if (do_chain_inst_and_cafile(crl_file_entry, ckch_inst))
1429 return;
1430 }
Remi Tricot-Le Breton4458b972021-02-19 17:41:55 +01001431 }
1432}
1433
William Lallemandda8584c2020-05-14 10:14:37 +02001434
1435
Remi Tricot-Le Breton523f0e42021-03-16 10:11:44 +01001436static int show_cert_detail(X509 *cert, STACK_OF(X509) *chain, struct buffer *out)
William Lallemandda8584c2020-05-14 10:14:37 +02001437{
William Lallemandda8584c2020-05-14 10:14:37 +02001438 BIO *bio = NULL;
Remi Tricot-Le Breton523f0e42021-03-16 10:11:44 +01001439 struct buffer *tmp = alloc_trash_chunk();
William Lallemandda8584c2020-05-14 10:14:37 +02001440 int i;
Remi Tricot-Le Breton523f0e42021-03-16 10:11:44 +01001441 int write = -1;
1442 unsigned int len = 0;
1443 X509_NAME *name = NULL;
William Lallemandda8584c2020-05-14 10:14:37 +02001444
Remi Tricot-Le Breton523f0e42021-03-16 10:11:44 +01001445 if (!tmp)
1446 return -1;
William Lallemandda8584c2020-05-14 10:14:37 +02001447
Remi Tricot-Le Breton523f0e42021-03-16 10:11:44 +01001448 if (!cert)
William Lallemand5685ccf2020-09-16 16:12:25 +02001449 goto end;
William Lallemandda8584c2020-05-14 10:14:37 +02001450
William Lallemand5685ccf2020-09-16 16:12:25 +02001451 if (chain == NULL) {
1452 struct issuer_chain *issuer;
Remi Tricot-Le Breton523f0e42021-03-16 10:11:44 +01001453 issuer = ssl_get0_issuer_chain(cert);
William Lallemand5685ccf2020-09-16 16:12:25 +02001454 if (issuer) {
1455 chain = issuer->chain;
1456 chunk_appendf(out, "Chain Filename: ");
1457 chunk_appendf(out, "%s\n", issuer->path);
William Lallemandda8584c2020-05-14 10:14:37 +02001458 }
William Lallemand5685ccf2020-09-16 16:12:25 +02001459 }
1460 chunk_appendf(out, "Serial: ");
Remi Tricot-Le Breton523f0e42021-03-16 10:11:44 +01001461 if (ssl_sock_get_serial(cert, tmp) == -1)
William Lallemand5685ccf2020-09-16 16:12:25 +02001462 goto end;
1463 dump_binary(out, tmp->area, tmp->data);
1464 chunk_appendf(out, "\n");
William Lallemandda8584c2020-05-14 10:14:37 +02001465
William Lallemand5685ccf2020-09-16 16:12:25 +02001466 chunk_appendf(out, "notBefore: ");
1467 chunk_reset(tmp);
1468 if ((bio = BIO_new(BIO_s_mem())) == NULL)
1469 goto end;
Remi Tricot-Le Breton523f0e42021-03-16 10:11:44 +01001470 if (ASN1_TIME_print(bio, X509_getm_notBefore(cert)) == 0)
William Lallemand5685ccf2020-09-16 16:12:25 +02001471 goto end;
1472 write = BIO_read(bio, tmp->area, tmp->size-1);
1473 tmp->area[write] = '\0';
1474 BIO_free(bio);
1475 bio = NULL;
1476 chunk_appendf(out, "%s\n", tmp->area);
William Lallemandda8584c2020-05-14 10:14:37 +02001477
William Lallemand5685ccf2020-09-16 16:12:25 +02001478 chunk_appendf(out, "notAfter: ");
1479 chunk_reset(tmp);
1480 if ((bio = BIO_new(BIO_s_mem())) == NULL)
1481 goto end;
Remi Tricot-Le Breton523f0e42021-03-16 10:11:44 +01001482 if (ASN1_TIME_print(bio, X509_getm_notAfter(cert)) == 0)
William Lallemand5685ccf2020-09-16 16:12:25 +02001483 goto end;
1484 if ((write = BIO_read(bio, tmp->area, tmp->size-1)) <= 0)
1485 goto end;
1486 tmp->area[write] = '\0';
1487 BIO_free(bio);
1488 bio = NULL;
1489 chunk_appendf(out, "%s\n", tmp->area);
William Lallemandda8584c2020-05-14 10:14:37 +02001490
1491#ifdef SSL_CTRL_SET_TLSEXT_HOSTNAME
William Lallemand5685ccf2020-09-16 16:12:25 +02001492 chunk_appendf(out, "Subject Alternative Name: ");
Remi Tricot-Le Breton523f0e42021-03-16 10:11:44 +01001493 if (ssl_sock_get_san_oneline(cert, out) == -1)
William Lallemand5685ccf2020-09-16 16:12:25 +02001494 goto end;
1495 *(out->area + out->data) = '\0';
1496 chunk_appendf(out, "\n");
William Lallemandda8584c2020-05-14 10:14:37 +02001497#endif
William Lallemand5685ccf2020-09-16 16:12:25 +02001498 chunk_reset(tmp);
1499 chunk_appendf(out, "Algorithm: ");
Remi Tricot-Le Breton523f0e42021-03-16 10:11:44 +01001500 if (cert_get_pkey_algo(cert, tmp) == 0)
William Lallemand5685ccf2020-09-16 16:12:25 +02001501 goto end;
1502 chunk_appendf(out, "%s\n", tmp->area);
William Lallemandda8584c2020-05-14 10:14:37 +02001503
William Lallemand5685ccf2020-09-16 16:12:25 +02001504 chunk_reset(tmp);
1505 chunk_appendf(out, "SHA1 FingerPrint: ");
Remi Tricot-Le Breton523f0e42021-03-16 10:11:44 +01001506 if (X509_digest(cert, EVP_sha1(), (unsigned char *) tmp->area, &len) == 0)
William Lallemand5685ccf2020-09-16 16:12:25 +02001507 goto end;
1508 tmp->data = len;
1509 dump_binary(out, tmp->area, tmp->data);
1510 chunk_appendf(out, "\n");
William Lallemandda8584c2020-05-14 10:14:37 +02001511
William Lallemand5685ccf2020-09-16 16:12:25 +02001512 chunk_appendf(out, "Subject: ");
Remi Tricot-Le Breton523f0e42021-03-16 10:11:44 +01001513 if ((name = X509_get_subject_name(cert)) == NULL)
William Lallemand5685ccf2020-09-16 16:12:25 +02001514 goto end;
1515 if ((ssl_sock_get_dn_oneline(name, tmp)) == -1)
1516 goto end;
1517 *(tmp->area + tmp->data) = '\0';
1518 chunk_appendf(out, "%s\n", tmp->area);
1519
1520 chunk_appendf(out, "Issuer: ");
Remi Tricot-Le Breton523f0e42021-03-16 10:11:44 +01001521 if ((name = X509_get_issuer_name(cert)) == NULL)
William Lallemand5685ccf2020-09-16 16:12:25 +02001522 goto end;
1523 if ((ssl_sock_get_dn_oneline(name, tmp)) == -1)
1524 goto end;
1525 *(tmp->area + tmp->data) = '\0';
1526 chunk_appendf(out, "%s\n", tmp->area);
1527
1528 /* Displays subject of each certificate in the chain */
1529 for (i = 0; i < sk_X509_num(chain); i++) {
1530 X509 *ca = sk_X509_value(chain, i);
1531
1532 chunk_appendf(out, "Chain Subject: ");
1533 if ((name = X509_get_subject_name(ca)) == NULL)
William Lallemandda8584c2020-05-14 10:14:37 +02001534 goto end;
1535 if ((ssl_sock_get_dn_oneline(name, tmp)) == -1)
1536 goto end;
1537 *(tmp->area + tmp->data) = '\0';
1538 chunk_appendf(out, "%s\n", tmp->area);
1539
William Lallemand5685ccf2020-09-16 16:12:25 +02001540 chunk_appendf(out, "Chain Issuer: ");
1541 if ((name = X509_get_issuer_name(ca)) == NULL)
William Lallemandda8584c2020-05-14 10:14:37 +02001542 goto end;
1543 if ((ssl_sock_get_dn_oneline(name, tmp)) == -1)
1544 goto end;
1545 *(tmp->area + tmp->data) = '\0';
1546 chunk_appendf(out, "%s\n", tmp->area);
William Lallemandda8584c2020-05-14 10:14:37 +02001547 }
1548
1549end:
Remi Tricot-Le Breton523f0e42021-03-16 10:11:44 +01001550 if (bio)
1551 BIO_free(bio);
1552 free_trash_chunk(tmp);
1553
1554 return 0;
1555}
1556
Remi Tricot-Le Breton3faf0cb2021-06-10 18:10:32 +02001557#if ((defined SSL_CTRL_SET_TLSEXT_STATUS_REQ_CB && !defined OPENSSL_NO_OCSP) && !defined OPENSSL_IS_BORINGSSL)
Remi Tricot-Le Bretonda968f62021-06-10 13:51:14 +02001558/*
1559 * Build the OCSP tree entry's key for a given ckch_store.
1560 * Returns a negative value in case of error.
1561 */
1562static int ckch_store_build_certid(struct ckch_store *ckch_store, unsigned char certid[128], unsigned int *key_length)
1563{
1564 OCSP_RESPONSE *resp;
1565 OCSP_BASICRESP *bs = NULL;
1566 OCSP_SINGLERESP *sr;
1567 OCSP_CERTID *id;
1568 unsigned char *p = NULL;
1569
1570 if (!key_length)
1571 return -1;
1572
1573 *key_length = 0;
1574
1575 if (!ckch_store->ckch->ocsp_response)
1576 return 0;
1577
1578 p = (unsigned char *) ckch_store->ckch->ocsp_response->area;
1579
1580 resp = d2i_OCSP_RESPONSE(NULL, (const unsigned char **)&p,
1581 ckch_store->ckch->ocsp_response->data);
1582 if (!resp) {
1583 goto end;
1584 }
1585
1586 bs = OCSP_response_get1_basic(resp);
1587 if (!bs) {
1588 goto end;
1589 }
1590
1591 sr = OCSP_resp_get0(bs, 0);
1592 if (!sr) {
1593 goto end;
1594 }
1595
1596 id = (OCSP_CERTID*)OCSP_SINGLERESP_get0_id(sr);
1597
1598 p = certid;
1599 *key_length = i2d_OCSP_CERTID(id, &p);
1600
1601end:
1602 return *key_length > 0;
1603}
1604#endif
1605
1606/*
1607 * Dump the OCSP certificate key (if it exists) of certificate <ckch> into
1608 * buffer <out>.
1609 * Returns 0 in case of success.
1610 */
1611static int ckch_store_show_ocsp_certid(struct ckch_store *ckch_store, struct buffer *out)
1612{
Remi Tricot-Le Breton3faf0cb2021-06-10 18:10:32 +02001613#if ((defined SSL_CTRL_SET_TLSEXT_STATUS_REQ_CB && !defined OPENSSL_NO_OCSP) && !defined OPENSSL_IS_BORINGSSL)
Remi Tricot-Le Bretonda968f62021-06-10 13:51:14 +02001614 unsigned char key[OCSP_MAX_CERTID_ASN1_LENGTH] = {};
1615 unsigned int key_length = 0;
1616 int i;
1617
1618 if (ckch_store_build_certid(ckch_store, (unsigned char*)key, &key_length) >= 0) {
1619 /* Dump the CERTID info */
1620 chunk_appendf(out, "OCSP Response Key: ");
1621 for (i = 0; i < key_length; ++i) {
1622 chunk_appendf(out, "%02x", key[i]);
1623 }
1624 chunk_appendf(out, "\n");
1625 }
1626#endif
1627
1628 return 0;
1629}
1630
Remi Tricot-Le Breton523f0e42021-03-16 10:11:44 +01001631
1632/* IO handler of the details "show ssl cert <filename>" */
1633static int cli_io_handler_show_cert_detail(struct appctx *appctx)
1634{
Christopher Faulet86e1c332021-12-20 17:09:39 +01001635 struct stream_interface *si = cs_si(appctx->owner);
Remi Tricot-Le Breton523f0e42021-03-16 10:11:44 +01001636 struct ckch_store *ckchs = appctx->ctx.cli.p0;
1637 struct buffer *out = alloc_trash_chunk();
1638 int retval = 0;
1639
1640 if (!out)
1641 goto end_no_putchk;
1642
1643 chunk_appendf(out, "Filename: ");
1644 if (ckchs == ckchs_transaction.new_ckchs)
1645 chunk_appendf(out, "*");
1646 chunk_appendf(out, "%s\n", ckchs->path);
1647
1648 chunk_appendf(out, "Status: ");
1649 if (ckchs->ckch->cert == NULL)
1650 chunk_appendf(out, "Empty\n");
1651 else if (LIST_ISEMPTY(&ckchs->ckch_inst))
1652 chunk_appendf(out, "Unused\n");
1653 else
1654 chunk_appendf(out, "Used\n");
1655
1656 retval = show_cert_detail(ckchs->ckch->cert, ckchs->ckch->chain, out);
1657 if (retval < 0)
1658 goto end_no_putchk;
1659 else if (retval)
1660 goto end;
1661
Remi Tricot-Le Bretonda968f62021-06-10 13:51:14 +02001662 ckch_store_show_ocsp_certid(ckchs, out);
1663
Remi Tricot-Le Breton523f0e42021-03-16 10:11:44 +01001664end:
William Lallemandda8584c2020-05-14 10:14:37 +02001665 if (ci_putchk(si_ic(si), out) == -1) {
1666 si_rx_room_blk(si);
1667 goto yield;
1668 }
1669
1670end_no_putchk:
William Lallemandda8584c2020-05-14 10:14:37 +02001671 free_trash_chunk(out);
1672 return 1;
1673yield:
William Lallemandda8584c2020-05-14 10:14:37 +02001674 free_trash_chunk(out);
1675 return 0; /* should come back */
1676}
1677
Remi Tricot-Le Breton6056e612021-06-10 13:51:15 +02001678
1679/* IO handler of the details "show ssl cert <filename.ocsp>" */
1680static int cli_io_handler_show_cert_ocsp_detail(struct appctx *appctx)
1681{
Remi Tricot-Le Breton3faf0cb2021-06-10 18:10:32 +02001682#if ((defined SSL_CTRL_SET_TLSEXT_STATUS_REQ_CB && !defined OPENSSL_NO_OCSP) && !defined OPENSSL_IS_BORINGSSL)
Christopher Faulet86e1c332021-12-20 17:09:39 +01001683 struct stream_interface *si = cs_si(appctx->owner);
Remi Tricot-Le Breton6056e612021-06-10 13:51:15 +02001684 struct ckch_store *ckchs = appctx->ctx.cli.p0;
1685 struct buffer *out = alloc_trash_chunk();
1686 int from_transaction = appctx->ctx.cli.i0;
1687
1688 if (!out)
1689 goto end_no_putchk;
1690
1691 /* If we try to display an ongoing transaction's OCSP response, we
1692 * need to dump the ckch's ocsp_response buffer directly.
1693 * Otherwise, we must rebuild the certificate's certid in order to
1694 * look for the current OCSP response in the tree. */
1695 if (from_transaction && ckchs->ckch->ocsp_response) {
Remi Tricot-Le Bretona9a591a2022-02-16 14:42:22 +01001696 if (ssl_ocsp_response_print(ckchs->ckch->ocsp_response, out))
1697 goto end_no_putchk;
Remi Tricot-Le Breton6056e612021-06-10 13:51:15 +02001698 }
1699 else {
1700 unsigned char key[OCSP_MAX_CERTID_ASN1_LENGTH] = {};
1701 unsigned int key_length = 0;
1702
1703 if (ckch_store_build_certid(ckchs, (unsigned char*)key, &key_length) < 0)
1704 goto end_no_putchk;
1705
Remi Tricot-Le Bretona9a591a2022-02-16 14:42:22 +01001706 if (ssl_get_ocspresponse_detail(key, out))
1707 goto end_no_putchk;
Remi Tricot-Le Breton6056e612021-06-10 13:51:15 +02001708 }
1709
1710 if (ci_putchk(si_ic(si), out) == -1) {
1711 si_rx_room_blk(si);
1712 goto yield;
1713 }
1714
1715end_no_putchk:
1716 free_trash_chunk(out);
1717 return 1;
1718yield:
1719 free_trash_chunk(out);
1720 return 0; /* should come back */
1721#else
1722 return cli_err(appctx, "HAProxy was compiled against a version of OpenSSL that doesn't support OCSP stapling.\n");
1723#endif
1724}
1725
William Lallemandda8584c2020-05-14 10:14:37 +02001726/* parsing function for 'show ssl cert [certfile]' */
1727static int cli_parse_show_cert(char **args, char *payload, struct appctx *appctx, void *private)
1728{
1729 struct ckch_store *ckchs;
1730
1731 if (!cli_has_level(appctx, ACCESS_LVL_OPER))
1732 return cli_err(appctx, "Can't allocate memory!\n");
1733
1734 /* The operations on the CKCH architecture are locked so we can
1735 * manipulate ckch_store and ckch_inst */
1736 if (HA_SPIN_TRYLOCK(CKCH_LOCK, &ckch_lock))
1737 return cli_err(appctx, "Can't show!\nOperations on certificates are currently locked!\n");
1738
1739 /* check if there is a certificate to lookup */
1740 if (*args[3]) {
Remi Tricot-Le Breton6056e612021-06-10 13:51:15 +02001741 int show_ocsp_detail = 0;
1742 int from_transaction = 0;
1743 char *end;
1744
1745 /* We manage the special case "certname.ocsp" through which we
1746 * can show the details of an OCSP response. */
1747 end = strrchr(args[3], '.');
1748 if (end && strcmp(end+1, "ocsp") == 0) {
1749 *end = '\0';
1750 show_ocsp_detail = 1;
1751 }
1752
William Lallemandda8584c2020-05-14 10:14:37 +02001753 if (*args[3] == '*') {
Remi Tricot-Le Breton6056e612021-06-10 13:51:15 +02001754 from_transaction = 1;
William Lallemandda8584c2020-05-14 10:14:37 +02001755 if (!ckchs_transaction.new_ckchs)
1756 goto error;
1757
1758 ckchs = ckchs_transaction.new_ckchs;
1759
Tim Duesterhuse5ff1412021-01-02 22:31:53 +01001760 if (strcmp(args[3] + 1, ckchs->path) != 0)
William Lallemandda8584c2020-05-14 10:14:37 +02001761 goto error;
1762
1763 } else {
1764 if ((ckchs = ckchs_lookup(args[3])) == NULL)
1765 goto error;
1766
1767 }
1768
William Lallemandda8584c2020-05-14 10:14:37 +02001769 appctx->ctx.cli.p0 = ckchs;
1770 /* use the IO handler that shows details */
Remi Tricot-Le Breton6056e612021-06-10 13:51:15 +02001771 if (show_ocsp_detail) {
1772 appctx->ctx.cli.i0 = from_transaction;
1773 appctx->io_handler = cli_io_handler_show_cert_ocsp_detail;
1774 }
1775 else
1776 appctx->io_handler = cli_io_handler_show_cert_detail;
William Lallemandda8584c2020-05-14 10:14:37 +02001777 }
1778
1779 return 0;
1780
1781error:
1782 HA_SPIN_UNLOCK(CKCH_LOCK, &ckch_lock);
1783 return cli_err(appctx, "Can't display the certificate: Not found or the certificate is a bundle!\n");
1784}
1785
1786/* release function of the `set ssl cert' command, free things and unlock the spinlock */
1787static void cli_release_commit_cert(struct appctx *appctx)
1788{
1789 struct ckch_store *new_ckchs;
1790
1791 HA_SPIN_UNLOCK(CKCH_LOCK, &ckch_lock);
1792
1793 if (appctx->st2 != SETCERT_ST_FIN) {
1794 /* free every new sni_ctx and the new store, which are not in the trees so no spinlock there */
1795 new_ckchs = appctx->ctx.ssl.new_ckchs;
1796
1797 /* if the allocation failed, we need to free everything from the temporary list */
1798 ckch_store_free(new_ckchs);
1799 }
1800}
1801
Remi Tricot-Le Bretonbfadc022021-02-24 12:20:48 +01001802
1803/*
1804 * Rebuild a new instance 'new_inst' based on an old instance 'ckchi' and a
1805 * specific ckch_store.
1806 * Returns 0 in case of success, 1 otherwise.
1807 */
William Lallemande60c7d62022-03-30 11:26:15 +02001808int ckch_inst_rebuild(struct ckch_store *ckch_store, struct ckch_inst *ckchi,
1809 struct ckch_inst **new_inst, char **err)
Remi Tricot-Le Bretonbfadc022021-02-24 12:20:48 +01001810{
1811 int retval = 0;
1812 int errcode = 0;
1813 struct sni_ctx *sc0, *sc0s;
1814 char **sni_filter = NULL;
1815 int fcount = 0;
1816
1817 if (ckchi->crtlist_entry) {
1818 sni_filter = ckchi->crtlist_entry->filters;
1819 fcount = ckchi->crtlist_entry->fcount;
1820 }
1821
1822 if (ckchi->is_server_instance)
1823 errcode |= ckch_inst_new_load_srv_store(ckch_store->path, ckch_store, new_inst, err);
1824 else
1825 errcode |= ckch_inst_new_load_store(ckch_store->path, ckch_store, ckchi->bind_conf, ckchi->ssl_conf, sni_filter, fcount, new_inst, err);
1826
1827 if (errcode & ERR_CODE)
1828 return 1;
1829
1830 /* if the previous ckchi was used as the default */
1831 if (ckchi->is_default)
1832 (*new_inst)->is_default = 1;
1833
1834 (*new_inst)->is_server_instance = ckchi->is_server_instance;
1835 (*new_inst)->server = ckchi->server;
1836 /* Create a new SSL_CTX and link it to the new instance. */
1837 if ((*new_inst)->is_server_instance) {
1838 retval = ssl_sock_prep_srv_ctx_and_inst(ckchi->server, (*new_inst)->ctx, (*new_inst));
1839 if (retval)
1840 return 1;
1841 }
1842
1843 /* create the link to the crtlist_entry */
1844 (*new_inst)->crtlist_entry = ckchi->crtlist_entry;
1845
1846 /* we need to initialize the SSL_CTX generated */
1847 /* this iterate on the newly generated SNIs in the new instance to prepare their SSL_CTX */
1848 list_for_each_entry_safe(sc0, sc0s, &(*new_inst)->sni_ctx, by_ckch_inst) {
1849 if (!sc0->order) { /* we initialized only the first SSL_CTX because it's the same in the other sni_ctx's */
1850 errcode |= ssl_sock_prep_ctx_and_inst(ckchi->bind_conf, ckchi->ssl_conf, sc0->ctx, *new_inst, err);
1851 if (errcode & ERR_CODE)
1852 return 1;
1853 }
1854 }
1855
1856 return 0;
1857}
1858
1859/*
1860 * Load all the new SNIs of a newly built ckch instance in the trees, or replace
1861 * a server's main ckch instance.
1862 */
1863static void __ssl_sock_load_new_ckch_instance(struct ckch_inst *ckchi)
1864{
1865 /* The bind_conf will be null on server ckch_instances. */
1866 if (ckchi->is_server_instance) {
1867 int i;
1868 /* a lock is needed here since we have to free the SSL cache */
1869 HA_RWLOCK_WRLOCK(SSL_SERVER_LOCK, &ckchi->server->ssl_ctx.lock);
1870 /* free the server current SSL_CTX */
1871 SSL_CTX_free(ckchi->server->ssl_ctx.ctx);
1872 /* Actual ssl context update */
1873 SSL_CTX_up_ref(ckchi->ctx);
1874 ckchi->server->ssl_ctx.ctx = ckchi->ctx;
1875 ckchi->server->ssl_ctx.inst = ckchi;
1876
1877 /* flush the session cache of the server */
1878 for (i = 0; i < global.nbthread; i++) {
William Lallemandce990332021-11-23 15:15:09 +01001879 ha_free(&ckchi->server->ssl_ctx.reused_sess[i].sni);
Remi Tricot-Le Bretonbfadc022021-02-24 12:20:48 +01001880 ha_free(&ckchi->server->ssl_ctx.reused_sess[i].ptr);
1881 }
1882 HA_RWLOCK_WRUNLOCK(SSL_SERVER_LOCK, &ckchi->server->ssl_ctx.lock);
1883
1884 } else {
1885 HA_RWLOCK_WRLOCK(SNI_LOCK, &ckchi->bind_conf->sni_lock);
1886 ssl_sock_load_cert_sni(ckchi, ckchi->bind_conf);
1887 HA_RWLOCK_WRUNLOCK(SNI_LOCK, &ckchi->bind_conf->sni_lock);
1888 }
1889}
1890
1891/*
1892 * Delete a ckch instance that was replaced after a CLI command.
1893 */
1894static void __ckch_inst_free_locked(struct ckch_inst *ckchi)
1895{
1896 if (ckchi->is_server_instance) {
1897 /* no lock for servers */
1898 ckch_inst_free(ckchi);
1899 } else {
1900 struct bind_conf __maybe_unused *bind_conf = ckchi->bind_conf;
1901
1902 HA_RWLOCK_WRLOCK(SNI_LOCK, &bind_conf->sni_lock);
1903 ckch_inst_free(ckchi);
1904 HA_RWLOCK_WRUNLOCK(SNI_LOCK, &bind_conf->sni_lock);
1905 }
1906}
1907
William Lallemand3b5a3a62022-03-29 14:29:31 +02001908/* Replace a ckch_store in the ckch tree and insert the whole dependencies,
1909* then free the previous dependencies and store.
1910* Used in the case of a certificate update.
1911*
1912* Every dependencies must allocated before using this function.
1913*
1914* This function can't fail as it only update pointers, and does not alloc anything.
1915*
1916* /!\ This function must be used under the ckch lock. /!\
1917*
1918* - Insert every dependencies (SNI, crtlist_entry, ckch_inst, etc)
1919* - Delete the old ckch_store from the tree
1920* - Insert the new ckch_store
1921* - Free the old dependencies and the old ckch_store
1922*/
1923void ckch_store_replace(struct ckch_store *old_ckchs, struct ckch_store *new_ckchs)
1924{
1925 struct crtlist_entry *entry;
1926 struct ckch_inst *ckchi, *ckchis;
1927
1928 LIST_SPLICE(&new_ckchs->crtlist_entry, &old_ckchs->crtlist_entry);
1929 list_for_each_entry(entry, &new_ckchs->crtlist_entry, by_ckch_store) {
1930 ebpt_delete(&entry->node);
1931 /* change the ptr and reinsert the node */
1932 entry->node.key = new_ckchs;
1933 ebpt_insert(&entry->crtlist->entries, &entry->node);
1934 }
1935 /* insert the new ckch_insts in the crtlist_entry */
1936 list_for_each_entry(ckchi, &new_ckchs->ckch_inst, by_ckchs) {
1937 if (ckchi->crtlist_entry)
1938 LIST_INSERT(&ckchi->crtlist_entry->ckch_inst, &ckchi->by_crtlist_entry);
1939 }
1940 /* First, we insert every new SNIs in the trees, also replace the default_ctx */
1941 list_for_each_entry_safe(ckchi, ckchis, &new_ckchs->ckch_inst, by_ckchs) {
1942 __ssl_sock_load_new_ckch_instance(ckchi);
1943 }
1944 /* delete the old sni_ctx, the old ckch_insts and the ckch_store */
1945 list_for_each_entry_safe(ckchi, ckchis, &old_ckchs->ckch_inst, by_ckchs) {
1946 __ckch_inst_free_locked(ckchi);
1947 }
1948
1949 ckch_store_free(old_ckchs);
1950 ebst_insert(&ckchs_tree, &new_ckchs->node);
1951}
1952
Remi Tricot-Le Bretonbfadc022021-02-24 12:20:48 +01001953
William Lallemandda8584c2020-05-14 10:14:37 +02001954/*
1955 * This function tries to create the new ckch_inst and their SNIs
William Lallemand30fcca12022-03-30 12:03:12 +02001956 *
1957 * /!\ don't forget to update __hlua_ckch_commit() if you changes things there. /!\
William Lallemandda8584c2020-05-14 10:14:37 +02001958 */
1959static int cli_io_handler_commit_cert(struct appctx *appctx)
1960{
Christopher Faulet86e1c332021-12-20 17:09:39 +01001961 struct stream_interface *si = cs_si(appctx->owner);
William Lallemandda8584c2020-05-14 10:14:37 +02001962 int y = 0;
1963 char *err = NULL;
William Lallemandda8584c2020-05-14 10:14:37 +02001964 struct ckch_store *old_ckchs, *new_ckchs = NULL;
William Lallemand3b5a3a62022-03-29 14:29:31 +02001965 struct ckch_inst *ckchi;
William Lallemandda8584c2020-05-14 10:14:37 +02001966 struct buffer *trash = alloc_trash_chunk();
William Lallemandda8584c2020-05-14 10:14:37 +02001967
1968 if (trash == NULL)
1969 goto error;
1970
1971 if (unlikely(si_ic(si)->flags & (CF_WRITE_ERROR|CF_SHUTW)))
1972 goto error;
1973
1974 while (1) {
1975 switch (appctx->st2) {
1976 case SETCERT_ST_INIT:
1977 /* This state just print the update message */
1978 chunk_printf(trash, "Committing %s", ckchs_transaction.path);
1979 if (ci_putchk(si_ic(si), trash) == -1) {
1980 si_rx_room_blk(si);
1981 goto yield;
1982 }
1983 appctx->st2 = SETCERT_ST_GEN;
1984 /* fallthrough */
1985 case SETCERT_ST_GEN:
1986 /*
1987 * This state generates the ckch instances with their
1988 * sni_ctxs and SSL_CTX.
1989 *
1990 * Since the SSL_CTX generation can be CPU consumer, we
1991 * yield every 10 instances.
1992 */
1993
1994 old_ckchs = appctx->ctx.ssl.old_ckchs;
1995 new_ckchs = appctx->ctx.ssl.new_ckchs;
1996
1997 if (!new_ckchs)
1998 continue;
1999
2000 /* get the next ckchi to regenerate */
2001 ckchi = appctx->ctx.ssl.next_ckchi;
2002 /* we didn't start yet, set it to the first elem */
2003 if (ckchi == NULL)
2004 ckchi = LIST_ELEM(old_ckchs->ckch_inst.n, typeof(ckchi), by_ckchs);
2005
2006 /* walk through the old ckch_inst and creates new ckch_inst using the updated ckchs */
2007 list_for_each_entry_from(ckchi, &old_ckchs->ckch_inst, by_ckchs) {
2008 struct ckch_inst *new_inst;
William Lallemandda8584c2020-05-14 10:14:37 +02002009
2010 /* it takes a lot of CPU to creates SSL_CTXs, so we yield every 10 CKCH instances */
2011 if (y >= 10) {
2012 /* save the next ckchi to compute */
2013 appctx->ctx.ssl.next_ckchi = ckchi;
2014 goto yield;
2015 }
2016
Remi Tricot-Le Bretonbfadc022021-02-24 12:20:48 +01002017 if (ckch_inst_rebuild(new_ckchs, ckchi, &new_inst, &err))
William Lallemandda8584c2020-05-14 10:14:37 +02002018 goto error;
2019
William Lallemandda8584c2020-05-14 10:14:37 +02002020 /* display one dot per new instance */
2021 chunk_appendf(trash, ".");
2022 /* link the new ckch_inst to the duplicate */
Willy Tarreau2b718102021-04-21 07:32:39 +02002023 LIST_APPEND(&new_ckchs->ckch_inst, &new_inst->by_ckchs);
William Lallemandda8584c2020-05-14 10:14:37 +02002024 y++;
2025 }
2026 appctx->st2 = SETCERT_ST_INSERT;
2027 /* fallthrough */
2028 case SETCERT_ST_INSERT:
2029 /* The generation is finished, we can insert everything */
2030
2031 old_ckchs = appctx->ctx.ssl.old_ckchs;
2032 new_ckchs = appctx->ctx.ssl.new_ckchs;
2033
2034 if (!new_ckchs)
2035 continue;
2036
William Lallemand3b5a3a62022-03-29 14:29:31 +02002037 /* insert everything and remove the previous objects */
2038 ckch_store_replace(old_ckchs, new_ckchs);
William Lallemandda8584c2020-05-14 10:14:37 +02002039
William Lallemandda8584c2020-05-14 10:14:37 +02002040 appctx->st2 = SETCERT_ST_FIN;
2041 /* fallthrough */
2042 case SETCERT_ST_FIN:
2043 /* we achieved the transaction, we can set everything to NULL */
Willy Tarreau61cfdf42021-02-20 10:46:51 +01002044 ha_free(&ckchs_transaction.path);
William Lallemandda8584c2020-05-14 10:14:37 +02002045 ckchs_transaction.new_ckchs = NULL;
2046 ckchs_transaction.old_ckchs = NULL;
2047 goto end;
2048 }
2049 }
2050end:
2051
2052 chunk_appendf(trash, "\n");
William Lallemandda8584c2020-05-14 10:14:37 +02002053 chunk_appendf(trash, "Success!\n");
2054 if (ci_putchk(si_ic(si), trash) == -1)
2055 si_rx_room_blk(si);
2056 free_trash_chunk(trash);
2057 /* success: call the release function and don't come back */
2058 return 1;
2059yield:
2060 /* store the state */
2061 if (ci_putchk(si_ic(si), trash) == -1)
2062 si_rx_room_blk(si);
2063 free_trash_chunk(trash);
2064 si_rx_endp_more(si); /* let's come back later */
2065 return 0; /* should come back */
2066
2067error:
2068 /* spin unlock and free are done in the release function */
2069 if (trash) {
2070 chunk_appendf(trash, "\n%sFailed!\n", err);
2071 if (ci_putchk(si_ic(si), trash) == -1)
2072 si_rx_room_blk(si);
2073 free_trash_chunk(trash);
2074 }
2075 /* error: call the release function and don't come back */
2076 return 1;
2077}
2078
2079/*
2080 * Parsing function of 'commit ssl cert'
2081 */
2082static int cli_parse_commit_cert(char **args, char *payload, struct appctx *appctx, void *private)
2083{
2084 char *err = NULL;
2085
2086 if (!cli_has_level(appctx, ACCESS_LVL_ADMIN))
2087 return 1;
2088
2089 if (!*args[3])
2090 return cli_err(appctx, "'commit ssl cert expects a filename\n");
2091
2092 /* The operations on the CKCH architecture are locked so we can
2093 * manipulate ckch_store and ckch_inst */
2094 if (HA_SPIN_TRYLOCK(CKCH_LOCK, &ckch_lock))
2095 return cli_err(appctx, "Can't commit the certificate!\nOperations on certificates are currently locked!\n");
2096
2097 if (!ckchs_transaction.path) {
2098 memprintf(&err, "No ongoing transaction! !\n");
2099 goto error;
2100 }
2101
2102 if (strcmp(ckchs_transaction.path, args[3]) != 0) {
2103 memprintf(&err, "The ongoing transaction is about '%s' but you are trying to set '%s'\n", ckchs_transaction.path, args[3]);
2104 goto error;
2105 }
2106
William Lallemand5685ccf2020-09-16 16:12:25 +02002107 /* if a certificate is here, a private key must be here too */
2108 if (ckchs_transaction.new_ckchs->ckch->cert && !ckchs_transaction.new_ckchs->ckch->key) {
2109 memprintf(&err, "The transaction must contain at least a certificate and a private key!\n");
2110 goto error;
2111 }
William Lallemanda9419522020-06-24 16:26:41 +02002112
William Lallemand5685ccf2020-09-16 16:12:25 +02002113 if (!X509_check_private_key(ckchs_transaction.new_ckchs->ckch->cert, ckchs_transaction.new_ckchs->ckch->key)) {
2114 memprintf(&err, "inconsistencies between private key and certificate loaded '%s'.\n", ckchs_transaction.path);
2115 goto error;
William Lallemandda8584c2020-05-14 10:14:37 +02002116 }
2117
2118 /* init the appctx structure */
2119 appctx->st2 = SETCERT_ST_INIT;
2120 appctx->ctx.ssl.next_ckchi = NULL;
2121 appctx->ctx.ssl.new_ckchs = ckchs_transaction.new_ckchs;
2122 appctx->ctx.ssl.old_ckchs = ckchs_transaction.old_ckchs;
2123
2124 /* we don't unlock there, it will be unlock after the IO handler, in the release handler */
2125 return 0;
2126
2127error:
2128
2129 HA_SPIN_UNLOCK(CKCH_LOCK, &ckch_lock);
2130 err = memprintf(&err, "%sCan't commit %s!\n", err ? err : "", args[3]);
2131
2132 return cli_dynerr(appctx, err);
2133}
2134
2135
2136
2137
2138/*
2139 * Parsing function of `set ssl cert`, it updates or creates a temporary ckch.
2140 */
2141static int cli_parse_set_cert(char **args, char *payload, struct appctx *appctx, void *private)
2142{
2143 struct ckch_store *new_ckchs = NULL;
2144 struct ckch_store *old_ckchs = NULL;
2145 char *err = NULL;
2146 int i;
William Lallemandda8584c2020-05-14 10:14:37 +02002147 int errcode = 0;
2148 char *end;
William Lallemandff8bf982022-03-29 10:44:23 +02002149 struct cert_exts *cert_ext = &cert_exts[0]; /* default one, PEM */
William Lallemandda8584c2020-05-14 10:14:37 +02002150 struct cert_key_and_chain *ckch;
2151 struct buffer *buf;
2152
2153 if (!cli_has_level(appctx, ACCESS_LVL_ADMIN))
2154 return 1;
2155
William Lallemandda8584c2020-05-14 10:14:37 +02002156 if (!*args[3] || !payload)
2157 return cli_err(appctx, "'set ssl cert expects a filename and a certificate as a payload\n");
2158
2159 /* The operations on the CKCH architecture are locked so we can
2160 * manipulate ckch_store and ckch_inst */
2161 if (HA_SPIN_TRYLOCK(CKCH_LOCK, &ckch_lock))
2162 return cli_err(appctx, "Can't update the certificate!\nOperations on certificates are currently locked!\n");
2163
William Lallemand5ba80d62021-05-04 16:17:27 +02002164 if ((buf = alloc_trash_chunk()) == NULL) {
2165 memprintf(&err, "%sCan't allocate memory\n", err ? err : "");
2166 errcode |= ERR_ALERT | ERR_FATAL;
2167 goto end;
2168 }
William Lallemande5ff4ad2020-06-08 09:40:37 +02002169
William Lallemandda8584c2020-05-14 10:14:37 +02002170 if (!chunk_strcpy(buf, args[3])) {
2171 memprintf(&err, "%sCan't allocate memory\n", err ? err : "");
2172 errcode |= ERR_ALERT | ERR_FATAL;
2173 goto end;
2174 }
2175
2176 /* check which type of file we want to update */
William Lallemandff8bf982022-03-29 10:44:23 +02002177 for (i = 0; cert_exts[i].ext != NULL; i++) {
William Lallemandda8584c2020-05-14 10:14:37 +02002178 end = strrchr(buf->area, '.');
Tim Duesterhuse5ff1412021-01-02 22:31:53 +01002179 if (end && *cert_exts[i].ext && (strcmp(end + 1, cert_exts[i].ext) == 0)) {
William Lallemandda8584c2020-05-14 10:14:37 +02002180 *end = '\0';
William Lallemand089c1382020-10-23 17:35:12 +02002181 buf->data = strlen(buf->area);
William Lallemandff8bf982022-03-29 10:44:23 +02002182 cert_ext = &cert_exts[i];
William Lallemandda8584c2020-05-14 10:14:37 +02002183 break;
2184 }
2185 }
2186
2187 appctx->ctx.ssl.old_ckchs = NULL;
2188 appctx->ctx.ssl.new_ckchs = NULL;
2189
2190 /* if there is an ongoing transaction */
2191 if (ckchs_transaction.path) {
William Lallemandda8584c2020-05-14 10:14:37 +02002192 /* if there is an ongoing transaction, check if this is the same file */
2193 if (strcmp(ckchs_transaction.path, buf->area) != 0) {
William Lallemand089c1382020-10-23 17:35:12 +02002194 /* we didn't find the transaction, must try more cases below */
2195
2196 /* if the del-ext option is activated we should try to take a look at a ".crt" too. */
William Lallemandff8bf982022-03-29 10:44:23 +02002197 if (cert_ext->type != CERT_TYPE_PEM && global_ssl.extra_files_noext) {
William Lallemand089c1382020-10-23 17:35:12 +02002198 if (!chunk_strcat(buf, ".crt")) {
2199 memprintf(&err, "%sCan't allocate memory\n", err ? err : "");
2200 errcode |= ERR_ALERT | ERR_FATAL;
2201 goto end;
2202 }
2203
2204 if (strcmp(ckchs_transaction.path, buf->area) != 0) {
2205 /* remove .crt of the error message */
2206 *(b_orig(buf) + b_data(buf) + strlen(".crt")) = '\0';
2207 b_sub(buf, strlen(".crt"));
2208
2209 memprintf(&err, "The ongoing transaction is about '%s' but you are trying to set '%s'\n", ckchs_transaction.path, buf->area);
2210 errcode |= ERR_ALERT | ERR_FATAL;
2211 goto end;
2212 }
2213 }
William Lallemandda8584c2020-05-14 10:14:37 +02002214 }
2215
2216 appctx->ctx.ssl.old_ckchs = ckchs_transaction.new_ckchs;
2217
2218 } else {
William Lallemandda8584c2020-05-14 10:14:37 +02002219
William Lallemand95fefa12020-09-09 12:01:33 +02002220 /* lookup for the certificate in the tree */
2221 appctx->ctx.ssl.old_ckchs = ckchs_lookup(buf->area);
William Lallemand089c1382020-10-23 17:35:12 +02002222
2223 if (!appctx->ctx.ssl.old_ckchs) {
2224 /* if the del-ext option is activated we should try to take a look at a ".crt" too. */
William Lallemandff8bf982022-03-29 10:44:23 +02002225 if (cert_ext->type != CERT_TYPE_PEM && global_ssl.extra_files_noext) {
William Lallemand089c1382020-10-23 17:35:12 +02002226 if (!chunk_strcat(buf, ".crt")) {
2227 memprintf(&err, "%sCan't allocate memory\n", err ? err : "");
2228 errcode |= ERR_ALERT | ERR_FATAL;
2229 goto end;
2230 }
2231 appctx->ctx.ssl.old_ckchs = ckchs_lookup(buf->area);
2232 }
2233 }
William Lallemandda8584c2020-05-14 10:14:37 +02002234 }
2235
2236 if (!appctx->ctx.ssl.old_ckchs) {
2237 memprintf(&err, "%sCan't replace a certificate which is not referenced by the configuration!\n",
2238 err ? err : "");
2239 errcode |= ERR_ALERT | ERR_FATAL;
2240 goto end;
2241 }
2242
2243 if (!appctx->ctx.ssl.path) {
2244 /* this is a new transaction, set the path of the transaction */
2245 appctx->ctx.ssl.path = strdup(appctx->ctx.ssl.old_ckchs->path);
2246 if (!appctx->ctx.ssl.path) {
2247 memprintf(&err, "%sCan't allocate memory\n", err ? err : "");
2248 errcode |= ERR_ALERT | ERR_FATAL;
2249 goto end;
2250 }
2251 }
2252
2253 old_ckchs = appctx->ctx.ssl.old_ckchs;
2254
2255 /* duplicate the ckch store */
2256 new_ckchs = ckchs_dup(old_ckchs);
2257 if (!new_ckchs) {
2258 memprintf(&err, "%sCannot allocate memory!\n",
2259 err ? err : "");
2260 errcode |= ERR_ALERT | ERR_FATAL;
2261 goto end;
2262 }
2263
William Lallemand95fefa12020-09-09 12:01:33 +02002264 ckch = new_ckchs->ckch;
William Lallemandda8584c2020-05-14 10:14:37 +02002265
2266 /* appply the change on the duplicate */
William Lallemandff8bf982022-03-29 10:44:23 +02002267 if (cert_ext->load(buf->area, payload, ckch, &err) != 0) {
William Lallemandda8584c2020-05-14 10:14:37 +02002268 memprintf(&err, "%sCan't load the payload\n", err ? err : "");
2269 errcode |= ERR_ALERT | ERR_FATAL;
2270 goto end;
2271 }
2272
2273 appctx->ctx.ssl.new_ckchs = new_ckchs;
2274
2275 /* we succeed, we can save the ckchs in the transaction */
2276
2277 /* if there wasn't a transaction, update the old ckchs */
2278 if (!ckchs_transaction.old_ckchs) {
2279 ckchs_transaction.old_ckchs = appctx->ctx.ssl.old_ckchs;
2280 ckchs_transaction.path = appctx->ctx.ssl.path;
2281 err = memprintf(&err, "Transaction created for certificate %s!\n", ckchs_transaction.path);
2282 } else {
2283 err = memprintf(&err, "Transaction updated for certificate %s!\n", ckchs_transaction.path);
2284
2285 }
2286
2287 /* free the previous ckchs if there was a transaction */
2288 ckch_store_free(ckchs_transaction.new_ckchs);
2289
2290 ckchs_transaction.new_ckchs = appctx->ctx.ssl.new_ckchs;
2291
2292
2293 /* creates the SNI ctxs later in the IO handler */
2294
2295end:
2296 free_trash_chunk(buf);
2297
2298 if (errcode & ERR_CODE) {
2299
2300 ckch_store_free(appctx->ctx.ssl.new_ckchs);
2301 appctx->ctx.ssl.new_ckchs = NULL;
2302
2303 appctx->ctx.ssl.old_ckchs = NULL;
2304
Willy Tarreau61cfdf42021-02-20 10:46:51 +01002305 ha_free(&appctx->ctx.ssl.path);
William Lallemandda8584c2020-05-14 10:14:37 +02002306
2307 HA_SPIN_UNLOCK(CKCH_LOCK, &ckch_lock);
2308 return cli_dynerr(appctx, memprintf(&err, "%sCan't update %s!\n", err ? err : "", args[3]));
2309 } else {
2310
2311 HA_SPIN_UNLOCK(CKCH_LOCK, &ckch_lock);
2312 return cli_dynmsg(appctx, LOG_NOTICE, err);
2313 }
2314 /* TODO: handle the ERR_WARN which are not handled because of the io_handler */
2315}
2316
2317/* parsing function of 'abort ssl cert' */
2318static int cli_parse_abort_cert(char **args, char *payload, struct appctx *appctx, void *private)
2319{
2320 char *err = NULL;
2321
2322 if (!cli_has_level(appctx, ACCESS_LVL_ADMIN))
2323 return 1;
2324
2325 if (!*args[3])
2326 return cli_err(appctx, "'abort ssl cert' expects a filename\n");
2327
2328 /* The operations on the CKCH architecture are locked so we can
2329 * manipulate ckch_store and ckch_inst */
2330 if (HA_SPIN_TRYLOCK(CKCH_LOCK, &ckch_lock))
2331 return cli_err(appctx, "Can't abort!\nOperations on certificates are currently locked!\n");
2332
2333 if (!ckchs_transaction.path) {
2334 memprintf(&err, "No ongoing transaction!\n");
2335 goto error;
2336 }
2337
2338 if (strcmp(ckchs_transaction.path, args[3]) != 0) {
2339 memprintf(&err, "The ongoing transaction is about '%s' but you are trying to abort a transaction for '%s'\n", ckchs_transaction.path, args[3]);
2340 goto error;
2341 }
2342
2343 /* Only free the ckchs there, because the SNI and instances were not generated yet */
2344 ckch_store_free(ckchs_transaction.new_ckchs);
2345 ckchs_transaction.new_ckchs = NULL;
William Lallemandda8584c2020-05-14 10:14:37 +02002346 ckchs_transaction.old_ckchs = NULL;
Willy Tarreau61cfdf42021-02-20 10:46:51 +01002347 ha_free(&ckchs_transaction.path);
William Lallemandda8584c2020-05-14 10:14:37 +02002348
2349 HA_SPIN_UNLOCK(CKCH_LOCK, &ckch_lock);
2350
2351 err = memprintf(&err, "Transaction aborted for certificate '%s'!\n", args[3]);
2352 return cli_dynmsg(appctx, LOG_NOTICE, err);
2353
2354error:
2355 HA_SPIN_UNLOCK(CKCH_LOCK, &ckch_lock);
2356
2357 return cli_dynerr(appctx, err);
2358}
2359
2360/* parsing function of 'new ssl cert' */
2361static int cli_parse_new_cert(char **args, char *payload, struct appctx *appctx, void *private)
2362{
2363 struct ckch_store *store;
2364 char *err = NULL;
2365 char *path;
2366
2367 if (!cli_has_level(appctx, ACCESS_LVL_ADMIN))
2368 return 1;
2369
2370 if (!*args[3])
2371 return cli_err(appctx, "'new ssl cert' expects a filename\n");
2372
2373 path = args[3];
2374
2375 /* The operations on the CKCH architecture are locked so we can
2376 * manipulate ckch_store and ckch_inst */
2377 if (HA_SPIN_TRYLOCK(CKCH_LOCK, &ckch_lock))
2378 return cli_err(appctx, "Can't create a certificate!\nOperations on certificates are currently locked!\n");
2379
2380 store = ckchs_lookup(path);
2381 if (store != NULL) {
2382 memprintf(&err, "Certificate '%s' already exists!\n", path);
2383 store = NULL; /* we don't want to free it */
2384 goto error;
2385 }
2386 /* we won't support multi-certificate bundle here */
William Lallemandbd8e6ed2020-09-16 16:08:08 +02002387 store = ckch_store_new(path);
William Lallemandda8584c2020-05-14 10:14:37 +02002388 if (!store) {
2389 memprintf(&err, "unable to allocate memory.\n");
2390 goto error;
2391 }
2392
2393 /* insert into the ckchs tree */
2394 ebst_insert(&ckchs_tree, &store->node);
2395 memprintf(&err, "New empty certificate store '%s'!\n", args[3]);
2396
2397 HA_SPIN_UNLOCK(CKCH_LOCK, &ckch_lock);
2398 return cli_dynmsg(appctx, LOG_NOTICE, err);
2399error:
2400 free(store);
2401 HA_SPIN_UNLOCK(CKCH_LOCK, &ckch_lock);
2402 return cli_dynerr(appctx, err);
2403}
2404
2405/* parsing function of 'del ssl cert' */
2406static int cli_parse_del_cert(char **args, char *payload, struct appctx *appctx, void *private)
2407{
2408 struct ckch_store *store;
2409 char *err = NULL;
2410 char *filename;
2411
2412 if (!cli_has_level(appctx, ACCESS_LVL_ADMIN))
2413 return 1;
2414
2415 if (!*args[3])
2416 return cli_err(appctx, "'del ssl cert' expects a certificate name\n");
2417
2418 if (HA_SPIN_TRYLOCK(CKCH_LOCK, &ckch_lock))
2419 return cli_err(appctx, "Can't delete the certificate!\nOperations on certificates are currently locked!\n");
2420
2421 filename = args[3];
2422
2423 store = ckchs_lookup(filename);
2424 if (store == NULL) {
2425 memprintf(&err, "certificate '%s' doesn't exist!\n", filename);
2426 goto error;
2427 }
2428 if (!LIST_ISEMPTY(&store->ckch_inst)) {
2429 memprintf(&err, "certificate '%s' in use, can't be deleted!\n", filename);
2430 goto error;
2431 }
2432
2433 ebmb_delete(&store->node);
2434 ckch_store_free(store);
2435
2436 memprintf(&err, "Certificate '%s' deleted!\n", filename);
2437
2438 HA_SPIN_UNLOCK(CKCH_LOCK, &ckch_lock);
2439 return cli_dynmsg(appctx, LOG_NOTICE, err);
2440
2441error:
2442 memprintf(&err, "Can't remove the certificate: %s\n", err ? err : "");
2443 HA_SPIN_UNLOCK(CKCH_LOCK, &ckch_lock);
2444 return cli_dynerr(appctx, err);
2445}
2446
Remi Tricot-Le Bretona32a68b2021-02-24 17:35:43 +01002447
Remi Tricot-Le Breton9f40fe02021-03-16 16:21:27 +01002448
2449/* parsing function of 'new ssl ca-file' */
2450static int cli_parse_new_cafile(char **args, char *payload, struct appctx *appctx, void *private)
2451{
2452 struct cafile_entry *cafile_entry;
2453 char *err = NULL;
2454 char *path;
2455
2456 if (!cli_has_level(appctx, ACCESS_LVL_ADMIN))
2457 return 1;
2458
2459 if (!*args[3])
2460 return cli_err(appctx, "'new ssl ca-file' expects a filename\n");
2461
2462 path = args[3];
2463
2464 /* The operations on the CKCH architecture are locked so we can
2465 * manipulate ckch_store and ckch_inst */
2466 if (HA_SPIN_TRYLOCK(CKCH_LOCK, &ckch_lock))
2467 return cli_err(appctx, "Can't create a CA file!\nOperations on certificates are currently locked!\n");
2468
2469 cafile_entry = ssl_store_get_cafile_entry(path, 0);
2470 if (cafile_entry) {
2471 memprintf(&err, "CA file '%s' already exists!\n", path);
2472 goto error;
2473 }
2474
2475 cafile_entry = ssl_store_create_cafile_entry(path, NULL, CAFILE_CERT);
2476 if (!cafile_entry) {
2477 memprintf(&err, "%sCannot allocate memory!\n",
2478 err ? err : "");
2479 goto error;
2480 }
2481
2482 /* Add the newly created cafile_entry to the tree so that
2483 * any new ckch instance created from now can use it. */
2484 if (ssl_store_add_uncommitted_cafile_entry(cafile_entry))
2485 goto error;
2486
2487 memprintf(&err, "New CA file created '%s'!\n", path);
2488
2489 HA_SPIN_UNLOCK(CKCH_LOCK, &ckch_lock);
2490 return cli_dynmsg(appctx, LOG_NOTICE, err);
2491error:
2492 HA_SPIN_UNLOCK(CKCH_LOCK, &ckch_lock);
2493 return cli_dynerr(appctx, err);
2494}
2495
Remi Tricot-Le Bretona32a68b2021-02-24 17:35:43 +01002496/*
2497 * Parsing function of `set ssl ca-file`
2498 */
2499static int cli_parse_set_cafile(char **args, char *payload, struct appctx *appctx, void *private)
2500{
2501 char *err = NULL;
2502 int errcode = 0;
2503 struct buffer *buf;
2504
2505 if (!cli_has_level(appctx, ACCESS_LVL_ADMIN))
2506 return 1;
2507
2508 if (!*args[3] || !payload)
2509 return cli_err(appctx, "'set ssl ca-file expects a filename and CAs as a payload\n");
2510
2511 /* The operations on the CKCH architecture are locked so we can
2512 * manipulate ckch_store and ckch_inst */
2513 if (HA_SPIN_TRYLOCK(CKCH_LOCK, &ckch_lock))
2514 return cli_err(appctx, "Can't update the CA file!\nOperations on certificates are currently locked!\n");
2515
2516 if ((buf = alloc_trash_chunk()) == NULL) {
2517 memprintf(&err, "%sCan't allocate memory\n", err ? err : "");
2518 errcode |= ERR_ALERT | ERR_FATAL;
2519 goto end;
2520 }
2521
2522 if (!chunk_strcpy(buf, args[3])) {
2523 memprintf(&err, "%sCan't allocate memory\n", err ? err : "");
2524 errcode |= ERR_ALERT | ERR_FATAL;
2525 goto end;
2526 }
2527
2528 appctx->ctx.ssl.old_cafile_entry = NULL;
2529 appctx->ctx.ssl.new_cafile_entry = NULL;
2530
2531 /* if there is an ongoing transaction */
2532 if (cafile_transaction.path) {
2533 /* if there is an ongoing transaction, check if this is the same file */
2534 if (strcmp(cafile_transaction.path, buf->area) != 0) {
2535 memprintf(&err, "The ongoing transaction is about '%s' but you are trying to set '%s'\n", cafile_transaction.path, buf->area);
2536 errcode |= ERR_ALERT | ERR_FATAL;
2537 goto end;
2538 }
2539 appctx->ctx.ssl.old_cafile_entry = cafile_transaction.old_cafile_entry;
2540 }
2541 else {
2542 /* lookup for the certificate in the tree */
2543 appctx->ctx.ssl.old_cafile_entry = ssl_store_get_cafile_entry(buf->area, 0);
2544 }
2545
2546 if (!appctx->ctx.ssl.old_cafile_entry) {
2547 memprintf(&err, "%sCan't replace a CA file which is not referenced by the configuration!\n",
2548 err ? err : "");
2549 errcode |= ERR_ALERT | ERR_FATAL;
2550 goto end;
2551 }
2552
2553 if (!appctx->ctx.ssl.path) {
2554 /* this is a new transaction, set the path of the transaction */
2555 appctx->ctx.ssl.path = strdup(appctx->ctx.ssl.old_cafile_entry->path);
2556 if (!appctx->ctx.ssl.path) {
2557 memprintf(&err, "%sCan't allocate memory\n", err ? err : "");
2558 errcode |= ERR_ALERT | ERR_FATAL;
2559 goto end;
2560 }
2561 }
2562
2563 if (appctx->ctx.ssl.new_cafile_entry)
2564 ssl_store_delete_cafile_entry(appctx->ctx.ssl.new_cafile_entry);
2565
2566 /* Create a new cafile_entry without adding it to the cafile tree. */
Remi Tricot-Le Breton0bb48242021-04-16 17:59:23 +02002567 appctx->ctx.ssl.new_cafile_entry = ssl_store_create_cafile_entry(appctx->ctx.ssl.path, NULL, CAFILE_CERT);
Remi Tricot-Le Bretona32a68b2021-02-24 17:35:43 +01002568 if (!appctx->ctx.ssl.new_cafile_entry) {
2569 memprintf(&err, "%sCannot allocate memory!\n",
2570 err ? err : "");
2571 errcode |= ERR_ALERT | ERR_FATAL;
2572 goto end;
2573 }
2574
2575 /* Fill the new entry with the new CAs. */
2576 if (ssl_store_load_ca_from_buf(appctx->ctx.ssl.new_cafile_entry, payload)) {
2577 memprintf(&err, "%sInvalid payload\n", err ? err : "");
2578 errcode |= ERR_ALERT | ERR_FATAL;
2579 goto end;
2580 }
2581
2582 /* we succeed, we can save the ca in the transaction */
2583
2584 /* if there wasn't a transaction, update the old CA */
2585 if (!cafile_transaction.old_cafile_entry) {
2586 cafile_transaction.old_cafile_entry = appctx->ctx.ssl.old_cafile_entry;
2587 cafile_transaction.path = appctx->ctx.ssl.path;
2588 err = memprintf(&err, "transaction created for CA %s!\n", cafile_transaction.path);
2589 } else {
2590 err = memprintf(&err, "transaction updated for CA %s!\n", cafile_transaction.path);
2591 }
2592
2593 /* free the previous CA if there was a transaction */
2594 ssl_store_delete_cafile_entry(cafile_transaction.new_cafile_entry);
2595
2596 cafile_transaction.new_cafile_entry = appctx->ctx.ssl.new_cafile_entry;
2597
2598 /* creates the SNI ctxs later in the IO handler */
2599
2600end:
2601 free_trash_chunk(buf);
2602
2603 if (errcode & ERR_CODE) {
2604 ssl_store_delete_cafile_entry(appctx->ctx.ssl.new_cafile_entry);
2605 appctx->ctx.ssl.new_cafile_entry = NULL;
2606 appctx->ctx.ssl.old_cafile_entry = NULL;
2607
Tim Duesterhus025b93e2021-11-04 21:03:52 +01002608 ha_free(&appctx->ctx.ssl.path);
Remi Tricot-Le Bretona32a68b2021-02-24 17:35:43 +01002609
2610 HA_SPIN_UNLOCK(CKCH_LOCK, &ckch_lock);
2611 return cli_dynerr(appctx, memprintf(&err, "%sCan't update %s!\n", err ? err : "", args[3]));
2612 } else {
2613
2614 HA_SPIN_UNLOCK(CKCH_LOCK, &ckch_lock);
2615 return cli_dynmsg(appctx, LOG_NOTICE, err);
2616 }
2617}
2618
2619
2620/*
2621 * Parsing function of 'commit ssl ca-file'
2622 */
2623static int cli_parse_commit_cafile(char **args, char *payload, struct appctx *appctx, void *private)
2624{
2625 char *err = NULL;
2626
2627 if (!cli_has_level(appctx, ACCESS_LVL_ADMIN))
2628 return 1;
2629
2630 if (!*args[3])
2631 return cli_err(appctx, "'commit ssl ca-file expects a filename\n");
2632
2633 /* The operations on the CKCH architecture are locked so we can
2634 * manipulate ckch_store and ckch_inst */
2635 if (HA_SPIN_TRYLOCK(CKCH_LOCK, &ckch_lock))
2636 return cli_err(appctx, "Can't commit the CA file!\nOperations on certificates are currently locked!\n");
2637
2638 if (!cafile_transaction.path) {
2639 memprintf(&err, "No ongoing transaction! !\n");
2640 goto error;
2641 }
2642
2643 if (strcmp(cafile_transaction.path, args[3]) != 0) {
2644 memprintf(&err, "The ongoing transaction is about '%s' but you are trying to set '%s'\n", cafile_transaction.path, args[3]);
2645 goto error;
2646 }
2647 /* init the appctx structure */
2648 appctx->st2 = SETCERT_ST_INIT;
2649 appctx->ctx.ssl.next_ckchi_link = NULL;
2650 appctx->ctx.ssl.old_cafile_entry = cafile_transaction.old_cafile_entry;
2651 appctx->ctx.ssl.new_cafile_entry = cafile_transaction.new_cafile_entry;
Remi Tricot-Le Bretona51b3392021-04-20 17:38:14 +02002652 appctx->ctx.ssl.cafile_type = CAFILE_CERT;
Remi Tricot-Le Bretona32a68b2021-02-24 17:35:43 +01002653
2654 return 0;
2655
2656error:
2657
2658 HA_SPIN_UNLOCK(CKCH_LOCK, &ckch_lock);
2659 err = memprintf(&err, "%sCan't commit %s!\n", err ? err : "", args[3]);
2660
2661 return cli_dynerr(appctx, err);
2662}
2663
2664enum {
2665 CREATE_NEW_INST_OK = 0,
2666 CREATE_NEW_INST_YIELD = -1,
2667 CREATE_NEW_INST_ERR = -2
2668};
2669
2670static inline int __create_new_instance(struct appctx *appctx, struct ckch_inst *ckchi, int *count,
Remi Tricot-Le Bretond75b99e2021-05-17 11:45:55 +02002671 struct buffer *trash, char **err)
Remi Tricot-Le Bretona32a68b2021-02-24 17:35:43 +01002672{
2673 struct ckch_inst *new_inst;
2674
2675 /* it takes a lot of CPU to creates SSL_CTXs, so we yield every 10 CKCH instances */
2676 if (*count >= 10) {
2677 /* save the next ckchi to compute */
2678 appctx->ctx.ssl.next_ckchi = ckchi;
2679 return CREATE_NEW_INST_YIELD;
2680 }
2681
2682 /* Rebuild a new ckch instance that uses the same ckch_store
2683 * than a reference ckchi instance but will use a new CA file. */
Remi Tricot-Le Bretond75b99e2021-05-17 11:45:55 +02002684 if (ckch_inst_rebuild(ckchi->ckch_store, ckchi, &new_inst, err))
Remi Tricot-Le Bretona32a68b2021-02-24 17:35:43 +01002685 return CREATE_NEW_INST_ERR;
2686
2687 /* display one dot per new instance */
2688 chunk_appendf(trash, ".");
2689 ++(*count);
2690
2691 return CREATE_NEW_INST_OK;
2692}
2693
2694/*
2695 * This function tries to create new ckch instances and their SNIs using a newly
Remi Tricot-Le Bretona51b3392021-04-20 17:38:14 +02002696 * set certificate authority (CA file) or a newly set Certificate Revocation
2697 * List (CRL), depending on the command being called.
Remi Tricot-Le Bretona32a68b2021-02-24 17:35:43 +01002698 */
Remi Tricot-Le Bretona51b3392021-04-20 17:38:14 +02002699static int cli_io_handler_commit_cafile_crlfile(struct appctx *appctx)
Remi Tricot-Le Bretona32a68b2021-02-24 17:35:43 +01002700{
Christopher Faulet86e1c332021-12-20 17:09:39 +01002701 struct stream_interface *si = cs_si(appctx->owner);
Remi Tricot-Le Bretona32a68b2021-02-24 17:35:43 +01002702 int y = 0;
2703 char *err = NULL;
Remi Tricot-Le Bretona6b27842021-05-18 10:06:00 +02002704 struct cafile_entry *old_cafile_entry = NULL, *new_cafile_entry = NULL;
Remi Tricot-Le Bretona32a68b2021-02-24 17:35:43 +01002705 struct ckch_inst_link *ckchi_link;
2706 struct buffer *trash = alloc_trash_chunk();
2707
2708 if (trash == NULL)
2709 goto error;
2710
2711 if (unlikely(si_ic(si)->flags & (CF_WRITE_ERROR|CF_SHUTW)))
2712 goto error;
2713
2714 while (1) {
2715 switch (appctx->st2) {
2716 case SETCERT_ST_INIT:
2717 /* This state just print the update message */
Remi Tricot-Le Bretona51b3392021-04-20 17:38:14 +02002718 switch (appctx->ctx.ssl.cafile_type) {
2719 case CAFILE_CERT:
2720 chunk_printf(trash, "Committing %s", cafile_transaction.path);
2721 break;
2722 case CAFILE_CRL:
2723 chunk_printf(trash, "Committing %s", crlfile_transaction.path);
2724 break;
2725 default:
2726 goto error;
2727 }
Remi Tricot-Le Bretona32a68b2021-02-24 17:35:43 +01002728 if (ci_putchk(si_ic(si), trash) == -1) {
2729 si_rx_room_blk(si);
2730 goto yield;
2731 }
2732 appctx->st2 = SETCERT_ST_GEN;
2733 /* fallthrough */
2734 case SETCERT_ST_GEN:
2735 /*
2736 * This state generates the ckch instances with their
2737 * sni_ctxs and SSL_CTX.
2738 *
2739 * Since the SSL_CTX generation can be CPU consumer, we
2740 * yield every 10 instances.
2741 */
Remi Tricot-Le Bretona51b3392021-04-20 17:38:14 +02002742 switch (appctx->ctx.ssl.cafile_type) {
2743 case CAFILE_CERT:
2744 old_cafile_entry = appctx->ctx.ssl.old_cafile_entry;
2745 new_cafile_entry = appctx->ctx.ssl.new_cafile_entry;
2746 break;
2747 case CAFILE_CRL:
2748 old_cafile_entry = appctx->ctx.ssl.old_crlfile_entry;
2749 new_cafile_entry = appctx->ctx.ssl.new_crlfile_entry;
2750 break;
2751 }
Remi Tricot-Le Bretona32a68b2021-02-24 17:35:43 +01002752 if (!new_cafile_entry)
2753 continue;
2754
2755 /* get the next ckchi to regenerate */
2756 ckchi_link = appctx->ctx.ssl.next_ckchi_link;
2757 /* we didn't start yet, set it to the first elem */
2758 if (ckchi_link == NULL) {
2759 ckchi_link = LIST_ELEM(old_cafile_entry->ckch_inst_link.n, typeof(ckchi_link), list);
2760 /* Add the newly created cafile_entry to the tree so that
2761 * any new ckch instance created from now can use it. */
2762 if (ssl_store_add_uncommitted_cafile_entry(new_cafile_entry))
2763 goto error;
2764 }
2765
2766 list_for_each_entry_from(ckchi_link, &old_cafile_entry->ckch_inst_link, list) {
Remi Tricot-Le Bretond75b99e2021-05-17 11:45:55 +02002767 switch (__create_new_instance(appctx, ckchi_link->ckch_inst, &y, trash, &err)) {
Remi Tricot-Le Bretona32a68b2021-02-24 17:35:43 +01002768 case CREATE_NEW_INST_YIELD:
2769 appctx->ctx.ssl.next_ckchi_link = ckchi_link;
2770 goto yield;
2771 case CREATE_NEW_INST_ERR:
2772 goto error;
2773 default: break;
2774 }
2775 }
2776
2777 appctx->st2 = SETCERT_ST_INSERT;
2778 /* fallthrough */
2779 case SETCERT_ST_INSERT:
2780 /* The generation is finished, we can insert everything */
Remi Tricot-Le Bretona51b3392021-04-20 17:38:14 +02002781 switch (appctx->ctx.ssl.cafile_type) {
2782 case CAFILE_CERT:
2783 old_cafile_entry = appctx->ctx.ssl.old_cafile_entry;
2784 new_cafile_entry = appctx->ctx.ssl.new_cafile_entry;
2785 break;
2786 case CAFILE_CRL:
2787 old_cafile_entry = appctx->ctx.ssl.old_crlfile_entry;
2788 new_cafile_entry = appctx->ctx.ssl.new_crlfile_entry;
2789 break;
2790 }
Remi Tricot-Le Bretona32a68b2021-02-24 17:35:43 +01002791 if (!new_cafile_entry)
2792 continue;
2793
2794 /* insert the new ckch_insts in the crtlist_entry */
2795 list_for_each_entry(ckchi_link, &new_cafile_entry->ckch_inst_link, list) {
2796 if (ckchi_link->ckch_inst->crtlist_entry)
2797 LIST_INSERT(&ckchi_link->ckch_inst->crtlist_entry->ckch_inst,
2798 &ckchi_link->ckch_inst->by_crtlist_entry);
2799 }
2800
2801 /* First, we insert every new SNIs in the trees, also replace the default_ctx */
2802 list_for_each_entry(ckchi_link, &new_cafile_entry->ckch_inst_link, list) {
2803 __ssl_sock_load_new_ckch_instance(ckchi_link->ckch_inst);
2804 }
2805
2806 /* delete the old sni_ctx, the old ckch_insts and the ckch_store */
2807 list_for_each_entry(ckchi_link, &old_cafile_entry->ckch_inst_link, list) {
2808 __ckch_inst_free_locked(ckchi_link->ckch_inst);
2809 }
2810
2811
2812 /* Remove the old cafile entry from the tree */
2813 ebmb_delete(&old_cafile_entry->node);
2814 ssl_store_delete_cafile_entry(old_cafile_entry);
2815
2816 appctx->st2 = SETCERT_ST_FIN;
2817 /* fallthrough */
2818 case SETCERT_ST_FIN:
2819 /* we achieved the transaction, we can set everything to NULL */
Remi Tricot-Le Bretona51b3392021-04-20 17:38:14 +02002820 switch (appctx->ctx.ssl.cafile_type) {
2821 case CAFILE_CERT:
2822 ha_free(&cafile_transaction.path);
2823 cafile_transaction.old_cafile_entry = NULL;
2824 cafile_transaction.new_cafile_entry = NULL;
2825 break;
2826 case CAFILE_CRL:
2827 ha_free(&crlfile_transaction.path);
2828 crlfile_transaction.old_crlfile_entry = NULL;
2829 crlfile_transaction.new_crlfile_entry = NULL;
2830 break;
2831 }
Remi Tricot-Le Bretona32a68b2021-02-24 17:35:43 +01002832 goto end;
2833 }
2834 }
2835end:
2836
2837 chunk_appendf(trash, "\n");
Remi Tricot-Le Bretona32a68b2021-02-24 17:35:43 +01002838 chunk_appendf(trash, "Success!\n");
2839 if (ci_putchk(si_ic(si), trash) == -1)
2840 si_rx_room_blk(si);
2841 free_trash_chunk(trash);
2842 /* success: call the release function and don't come back */
2843 return 1;
2844yield:
2845 /* store the state */
2846 if (ci_putchk(si_ic(si), trash) == -1)
2847 si_rx_room_blk(si);
2848 free_trash_chunk(trash);
2849 si_rx_endp_more(si); /* let's come back later */
2850 return 0; /* should come back */
2851
2852error:
2853 /* spin unlock and free are done in the release function */
2854 if (trash) {
2855 chunk_appendf(trash, "\n%sFailed!\n", err);
2856 if (ci_putchk(si_ic(si), trash) == -1)
2857 si_rx_room_blk(si);
2858 free_trash_chunk(trash);
2859 }
2860 /* error: call the release function and don't come back */
2861 return 1;
2862}
2863
Remi Tricot-Le Bretond5fd09d2021-03-11 10:22:52 +01002864
2865/* parsing function of 'abort ssl ca-file' */
2866static int cli_parse_abort_cafile(char **args, char *payload, struct appctx *appctx, void *private)
2867{
2868 char *err = NULL;
2869
2870 if (!cli_has_level(appctx, ACCESS_LVL_ADMIN))
2871 return 1;
2872
2873 if (!*args[3])
2874 return cli_err(appctx, "'abort ssl ca-file' expects a filename\n");
2875
2876 /* The operations on the CKCH architecture are locked so we can
2877 * manipulate ckch_store and ckch_inst */
2878 if (HA_SPIN_TRYLOCK(CKCH_LOCK, &ckch_lock))
2879 return cli_err(appctx, "Can't abort!\nOperations on certificates are currently locked!\n");
2880
2881 if (!cafile_transaction.path) {
2882 memprintf(&err, "No ongoing transaction!\n");
2883 goto error;
2884 }
2885
2886 if (strcmp(cafile_transaction.path, args[3]) != 0) {
2887 memprintf(&err, "The ongoing transaction is about '%s' but you are trying to abort a transaction for '%s'\n", cafile_transaction.path, args[3]);
2888 goto error;
2889 }
2890
2891 /* Only free the uncommitted cafile_entry here, because the SNI and instances were not generated yet */
2892 ssl_store_delete_cafile_entry(cafile_transaction.new_cafile_entry);
2893 cafile_transaction.new_cafile_entry = NULL;
2894 cafile_transaction.old_cafile_entry = NULL;
2895 ha_free(&cafile_transaction.path);
2896
2897 HA_SPIN_UNLOCK(CKCH_LOCK, &ckch_lock);
2898
2899 err = memprintf(&err, "Transaction aborted for certificate '%s'!\n", args[3]);
2900 return cli_dynmsg(appctx, LOG_NOTICE, err);
2901
2902error:
2903 HA_SPIN_UNLOCK(CKCH_LOCK, &ckch_lock);
2904
2905 return cli_dynerr(appctx, err);
2906}
2907
Remi Tricot-Le Bretona32a68b2021-02-24 17:35:43 +01002908/* release function of the `commit ssl ca-file' command, free things and unlock the spinlock */
2909static void cli_release_commit_cafile(struct appctx *appctx)
2910{
2911 if (appctx->st2 != SETCERT_ST_FIN) {
2912 struct cafile_entry *new_cafile_entry = appctx->ctx.ssl.new_cafile_entry;
2913
2914 /* Remove the uncommitted cafile_entry from the tree. */
2915 ebmb_delete(&new_cafile_entry->node);
2916 ssl_store_delete_cafile_entry(new_cafile_entry);
2917 }
2918 HA_SPIN_UNLOCK(CKCH_LOCK, &ckch_lock);
2919}
2920
2921
Remi Tricot-Le Breton2a22e162021-03-16 11:19:33 +01002922/* IO handler of details "show ssl ca-file <filename[:index]>" */
2923static int cli_io_handler_show_cafile_detail(struct appctx *appctx)
2924{
Christopher Faulet86e1c332021-12-20 17:09:39 +01002925 struct stream_interface *si = cs_si(appctx->owner);
Remi Tricot-Le Breton2a22e162021-03-16 11:19:33 +01002926 struct cafile_entry *cafile_entry = appctx->ctx.cli.p0;
2927 struct buffer *out = alloc_trash_chunk();
2928 int i;
2929 X509 *cert;
2930 STACK_OF(X509_OBJECT) *objs;
2931 int retval = 0;
2932 long ca_index = (long)appctx->ctx.cli.p1;
2933
2934 if (!out)
2935 goto end_no_putchk;
2936
2937 chunk_appendf(out, "Filename: ");
2938 if (cafile_entry == cafile_transaction.new_cafile_entry)
2939 chunk_appendf(out, "*");
2940 chunk_appendf(out, "%s\n", cafile_entry->path);
2941
2942 chunk_appendf(out, "Status: ");
2943 if (!cafile_entry->ca_store)
2944 chunk_appendf(out, "Empty\n");
2945 else if (LIST_ISEMPTY(&cafile_entry->ckch_inst_link))
2946 chunk_appendf(out, "Unused\n");
2947 else
2948 chunk_appendf(out, "Used\n");
2949
2950 if (!cafile_entry->ca_store)
2951 goto end;
2952
2953 objs = X509_STORE_get0_objects(cafile_entry->ca_store);
2954 for (i = 0; i < sk_X509_OBJECT_num(objs); i++) {
2955 cert = X509_OBJECT_get0_X509(sk_X509_OBJECT_value(objs, i));
2956 if (!cert)
2957 continue;
2958
2959 /* Certificate indexes start at 1 on the CLI output. */
2960 if (ca_index && ca_index-1 != i)
2961 continue;
2962
Remi Tricot-Le Bretone8041fe2022-04-05 16:44:21 +02002963 chunk_appendf(out, " \nCertificate #%d:\n", i+1);
Remi Tricot-Le Breton2a22e162021-03-16 11:19:33 +01002964 retval = show_cert_detail(cert, NULL, out);
2965 if (retval < 0)
2966 goto end_no_putchk;
2967 else if (retval || ca_index)
2968 goto end;
2969 }
2970
2971end:
2972 if (ci_putchk(si_ic(si), out) == -1) {
2973 si_rx_room_blk(si);
2974 goto yield;
2975 }
2976
2977end_no_putchk:
2978 free_trash_chunk(out);
2979 return 1;
2980yield:
2981 free_trash_chunk(out);
2982 return 0; /* should come back */
2983}
2984
2985
2986/* parsing function for 'show ssl ca-file [cafile[:index]]' */
2987static int cli_parse_show_cafile(char **args, char *payload, struct appctx *appctx, void *private)
2988{
2989 struct cafile_entry *cafile_entry;
2990 long ca_index = 0;
2991 char *colons;
2992 char *err = NULL;
2993
2994 if (!cli_has_level(appctx, ACCESS_LVL_OPER))
2995 return cli_err(appctx, "Can't allocate memory!\n");
2996
2997 /* The operations on the CKCH architecture are locked so we can
2998 * manipulate ckch_store and ckch_inst */
2999 if (HA_SPIN_TRYLOCK(CKCH_LOCK, &ckch_lock))
3000 return cli_err(appctx, "Can't show!\nOperations on certificates are currently locked!\n");
3001
3002 /* check if there is a certificate to lookup */
3003 if (*args[3]) {
3004
3005 /* Look for an optional CA index after the CA file name */
3006 colons = strchr(args[3], ':');
3007 if (colons) {
3008 char *endptr;
3009
3010 ca_index = strtol(colons + 1, &endptr, 10);
3011 /* Indexes start at 1 */
3012 if (colons + 1 == endptr || *endptr != '\0' || ca_index <= 0) {
3013 memprintf(&err, "wrong CA index after colons in '%s'!", args[3]);
3014 goto error;
3015 }
3016 *colons = '\0';
3017 }
3018
3019 if (*args[3] == '*') {
3020 if (!cafile_transaction.new_cafile_entry)
3021 goto error;
3022
3023 cafile_entry = cafile_transaction.new_cafile_entry;
3024
3025 if (strcmp(args[3] + 1, cafile_entry->path) != 0)
3026 goto error;
3027
3028 } else {
3029 /* Get the "original" cafile_entry and not the
3030 * uncommitted one if it exists. */
3031 if ((cafile_entry = ssl_store_get_cafile_entry(args[3], 1)) == NULL || cafile_entry->type != CAFILE_CERT)
3032 goto error;
3033 }
3034
3035 appctx->ctx.cli.p0 = cafile_entry;
3036 appctx->ctx.cli.p1 = (void*)ca_index;
3037 /* use the IO handler that shows details */
3038 appctx->io_handler = cli_io_handler_show_cafile_detail;
3039 }
3040
3041 return 0;
3042
3043error:
3044 HA_SPIN_UNLOCK(CKCH_LOCK, &ckch_lock);
3045 if (err)
3046 return cli_dynerr(appctx, err);
3047 return cli_err(appctx, "Can't display the CA file : Not found!\n");
3048}
3049
3050
3051/* release function of the 'show ssl ca-file' command */
3052static void cli_release_show_cafile(struct appctx *appctx)
3053{
3054 HA_SPIN_UNLOCK(CKCH_LOCK, &ckch_lock);
3055}
3056
3057
3058/* This function returns the number of certificates in a cafile_entry. */
3059static int get_certificate_count(struct cafile_entry *cafile_entry)
3060{
3061 int cert_count = 0;
3062 STACK_OF(X509_OBJECT) *objs;
3063
3064 if (cafile_entry && cafile_entry->ca_store) {
3065 objs = X509_STORE_get0_objects(cafile_entry->ca_store);
3066 if (objs)
3067 cert_count = sk_X509_OBJECT_num(objs);
3068 }
3069 return cert_count;
3070}
3071
3072/* IO handler of "show ssl ca-file". The command taking a specific CA file name
3073 * is managed in cli_io_handler_show_cafile_detail. */
3074static int cli_io_handler_show_cafile(struct appctx *appctx)
3075{
3076 struct buffer *trash = alloc_trash_chunk();
3077 struct ebmb_node *node;
Christopher Faulet86e1c332021-12-20 17:09:39 +01003078 struct stream_interface *si = cs_si(appctx->owner);
Remi Tricot-Le Breton2a22e162021-03-16 11:19:33 +01003079 struct cafile_entry *cafile_entry;
3080
3081 if (trash == NULL)
3082 return 1;
3083
3084 if (!appctx->ctx.ssl.old_cafile_entry) {
3085 if (cafile_transaction.old_cafile_entry) {
3086 chunk_appendf(trash, "# transaction\n");
3087 chunk_appendf(trash, "*%s", cafile_transaction.old_cafile_entry->path);
3088
3089 chunk_appendf(trash, " - %d certificate(s)\n", get_certificate_count(cafile_transaction.new_cafile_entry));
3090 }
3091 }
3092
3093 /* First time in this io_handler. */
3094 if (!appctx->ctx.cli.p0) {
3095 chunk_appendf(trash, "# filename\n");
3096 node = ebmb_first(&cafile_tree);
3097 } else {
3098 /* We yielded during a previous call. */
3099 node = &((struct cafile_entry*)appctx->ctx.cli.p0)->node;
3100 }
3101
3102 while (node) {
3103 cafile_entry = ebmb_entry(node, struct cafile_entry, node);
3104 if (cafile_entry->type == CAFILE_CERT) {
3105 chunk_appendf(trash, "%s", cafile_entry->path);
3106
3107 chunk_appendf(trash, " - %d certificate(s)\n", get_certificate_count(cafile_entry));
3108 }
3109
3110 node = ebmb_next(node);
3111 if (ci_putchk(si_ic(si), trash) == -1) {
3112 si_rx_room_blk(si);
3113 goto yield;
3114 }
3115 }
3116
3117 appctx->ctx.cli.p0 = NULL;
3118 free_trash_chunk(trash);
3119 return 1;
3120yield:
3121
3122 free_trash_chunk(trash);
3123 appctx->ctx.cli.p0 = cafile_entry;
3124 return 0; /* should come back */
3125}
3126
Remi Tricot-Le Bretonc3a84772021-03-25 18:13:57 +01003127/* parsing function of 'del ssl ca-file' */
3128static int cli_parse_del_cafile(char **args, char *payload, struct appctx *appctx, void *private)
3129{
3130 struct cafile_entry *cafile_entry;
3131 char *err = NULL;
3132 char *filename;
3133
3134 if (!cli_has_level(appctx, ACCESS_LVL_ADMIN))
3135 return 1;
3136
3137 if (!*args[3])
3138 return cli_err(appctx, "'del ssl ca-file' expects a CA file name\n");
3139
3140 if (HA_SPIN_TRYLOCK(CKCH_LOCK, &ckch_lock))
3141 return cli_err(appctx, "Can't delete the CA file!\nOperations on certificates are currently locked!\n");
3142
3143 filename = args[3];
3144
3145 cafile_entry = ssl_store_get_cafile_entry(filename, 0);
3146 if (!cafile_entry) {
3147 memprintf(&err, "CA file '%s' doesn't exist!\n", filename);
3148 goto error;
3149 }
3150
3151 if (!LIST_ISEMPTY(&cafile_entry->ckch_inst_link)) {
3152 memprintf(&err, "CA file '%s' in use, can't be deleted!\n", filename);
3153 goto error;
3154 }
3155
3156 /* Remove the cafile_entry from the tree */
3157 ebmb_delete(&cafile_entry->node);
3158 ssl_store_delete_cafile_entry(cafile_entry);
3159
3160 memprintf(&err, "CA file '%s' deleted!\n", filename);
3161
3162 HA_SPIN_UNLOCK(CKCH_LOCK, &ckch_lock);
3163 return cli_dynmsg(appctx, LOG_NOTICE, err);
3164
3165error:
3166 memprintf(&err, "Can't remove the CA file: %s\n", err ? err : "");
3167 HA_SPIN_UNLOCK(CKCH_LOCK, &ckch_lock);
3168 return cli_dynerr(appctx, err);
3169}
3170
Remi Tricot-Le Breton720e3b92021-04-26 11:00:42 +02003171/* parsing function of 'new ssl crl-file' */
3172static int cli_parse_new_crlfile(char **args, char *payload, struct appctx *appctx, void *private)
3173{
3174 struct cafile_entry *cafile_entry;
3175 char *err = NULL;
3176 char *path;
3177
3178 if (!cli_has_level(appctx, ACCESS_LVL_ADMIN))
3179 return 1;
3180
3181 if (!*args[3])
3182 return cli_err(appctx, "'new ssl crl-file' expects a filename\n");
3183
3184 path = args[3];
3185
3186 /* The operations on the CKCH architecture are locked so we can
3187 * manipulate ckch_store and ckch_inst */
3188 if (HA_SPIN_TRYLOCK(CKCH_LOCK, &ckch_lock))
3189 return cli_err(appctx, "Can't create a CA file!\nOperations on certificates are currently locked!\n");
3190
3191 cafile_entry = ssl_store_get_cafile_entry(path, 0);
3192 if (cafile_entry) {
3193 memprintf(&err, "CRL file '%s' already exists!\n", path);
3194 goto error;
3195 }
3196
3197 cafile_entry = ssl_store_create_cafile_entry(path, NULL, CAFILE_CRL);
3198 if (!cafile_entry) {
3199 memprintf(&err, "%sCannot allocate memory!\n", err ? err : "");
3200 goto error;
3201 }
3202
3203 /* Add the newly created cafile_entry to the tree so that
3204 * any new ckch instance created from now can use it. */
3205 if (ssl_store_add_uncommitted_cafile_entry(cafile_entry))
3206 goto error;
3207
3208 memprintf(&err, "New CRL file created '%s'!\n", path);
3209
3210 HA_SPIN_UNLOCK(CKCH_LOCK, &ckch_lock);
3211 return cli_dynmsg(appctx, LOG_NOTICE, err);
3212error:
3213 HA_SPIN_UNLOCK(CKCH_LOCK, &ckch_lock);
3214 return cli_dynerr(appctx, err);
3215}
3216
Remi Tricot-Le Bretona51b3392021-04-20 17:38:14 +02003217/* Parsing function of `set ssl crl-file` */
3218static int cli_parse_set_crlfile(char **args, char *payload, struct appctx *appctx, void *private)
3219{
3220 char *err = NULL;
3221 int errcode = 0;
3222 struct buffer *buf;
3223
3224 if (!cli_has_level(appctx, ACCESS_LVL_ADMIN))
3225 return 1;
3226
3227 if (!*args[3] || !payload)
3228 return cli_err(appctx, "'set ssl crl-file expects a filename and CAs as a payload\n");
3229
3230 /* The operations on the CKCH architecture are locked so we can
3231 * manipulate ckch_store and ckch_inst */
3232 if (HA_SPIN_TRYLOCK(CKCH_LOCK, &ckch_lock))
3233 return cli_err(appctx, "Can't update the CRL file!\nOperations on certificates are currently locked!\n");
3234
3235 if ((buf = alloc_trash_chunk()) == NULL) {
3236 memprintf(&err, "%sCan't allocate memory\n", err ? err : "");
3237 errcode |= ERR_ALERT | ERR_FATAL;
3238 goto end;
3239 }
3240
3241 if (!chunk_strcpy(buf, args[3])) {
3242 memprintf(&err, "%sCan't allocate memory\n", err ? err : "");
3243 errcode |= ERR_ALERT | ERR_FATAL;
3244 goto end;
3245 }
3246
3247 appctx->ctx.ssl.old_crlfile_entry = NULL;
3248 appctx->ctx.ssl.new_crlfile_entry = NULL;
3249
3250 /* if there is an ongoing transaction */
3251 if (crlfile_transaction.path) {
3252 /* if there is an ongoing transaction, check if this is the same file */
3253 if (strcmp(crlfile_transaction.path, buf->area) != 0) {
3254 memprintf(&err, "The ongoing transaction is about '%s' but you are trying to set '%s'\n", crlfile_transaction.path, buf->area);
3255 errcode |= ERR_ALERT | ERR_FATAL;
3256 goto end;
3257 }
3258 appctx->ctx.ssl.old_crlfile_entry = crlfile_transaction.old_crlfile_entry;
3259 }
3260 else {
3261 /* lookup for the certificate in the tree */
3262 appctx->ctx.ssl.old_crlfile_entry = ssl_store_get_cafile_entry(buf->area, 0);
3263 }
3264
3265 if (!appctx->ctx.ssl.old_crlfile_entry) {
3266 memprintf(&err, "%sCan't replace a CRL file which is not referenced by the configuration!\n",
3267 err ? err : "");
3268 errcode |= ERR_ALERT | ERR_FATAL;
3269 goto end;
3270 }
3271
3272 if (!appctx->ctx.ssl.path) {
3273 /* this is a new transaction, set the path of the transaction */
3274 appctx->ctx.ssl.path = strdup(appctx->ctx.ssl.old_crlfile_entry->path);
3275 if (!appctx->ctx.ssl.path) {
3276 memprintf(&err, "%sCan't allocate memory\n", err ? err : "");
3277 errcode |= ERR_ALERT | ERR_FATAL;
3278 goto end;
3279 }
3280 }
3281
3282 if (appctx->ctx.ssl.new_crlfile_entry)
3283 ssl_store_delete_cafile_entry(appctx->ctx.ssl.new_crlfile_entry);
3284
3285 /* Create a new cafile_entry without adding it to the cafile tree. */
3286 appctx->ctx.ssl.new_crlfile_entry = ssl_store_create_cafile_entry(appctx->ctx.ssl.path, NULL, CAFILE_CRL);
3287 if (!appctx->ctx.ssl.new_crlfile_entry) {
3288 memprintf(&err, "%sCannot allocate memory!\n", err ? err : "");
3289 errcode |= ERR_ALERT | ERR_FATAL;
3290 goto end;
3291 }
3292
3293 /* Fill the new entry with the new CRL. */
3294 if (ssl_store_load_ca_from_buf(appctx->ctx.ssl.new_crlfile_entry, payload)) {
3295 memprintf(&err, "%sInvalid payload\n", err ? err : "");
3296 errcode |= ERR_ALERT | ERR_FATAL;
3297 goto end;
3298 }
3299
3300 /* we succeed, we can save the crl in the transaction */
3301
3302 /* if there wasn't a transaction, update the old CA */
3303 if (!crlfile_transaction.old_crlfile_entry) {
3304 crlfile_transaction.old_crlfile_entry = appctx->ctx.ssl.old_crlfile_entry;
3305 crlfile_transaction.path = appctx->ctx.ssl.path;
3306 err = memprintf(&err, "transaction created for CA %s!\n", crlfile_transaction.path);
3307 } else {
3308 err = memprintf(&err, "transaction updated for CA %s!\n", crlfile_transaction.path);
3309 }
3310
3311 /* free the previous CRL file if there was a transaction */
3312 ssl_store_delete_cafile_entry(crlfile_transaction.new_crlfile_entry);
3313
3314 crlfile_transaction.new_crlfile_entry = appctx->ctx.ssl.new_crlfile_entry;
3315
3316 /* creates the SNI ctxs later in the IO handler */
3317
3318end:
3319 free_trash_chunk(buf);
3320
3321 if (errcode & ERR_CODE) {
3322 ssl_store_delete_cafile_entry(appctx->ctx.ssl.new_crlfile_entry);
3323 appctx->ctx.ssl.new_crlfile_entry = NULL;
3324 appctx->ctx.ssl.old_crlfile_entry = NULL;
3325
Tim Duesterhus025b93e2021-11-04 21:03:52 +01003326 ha_free(&appctx->ctx.ssl.path);
Remi Tricot-Le Bretona51b3392021-04-20 17:38:14 +02003327
3328 HA_SPIN_UNLOCK(CKCH_LOCK, &ckch_lock);
3329 return cli_dynerr(appctx, memprintf(&err, "%sCan't update %s!\n", err ? err : "", args[3]));
3330 } else {
3331
3332 HA_SPIN_UNLOCK(CKCH_LOCK, &ckch_lock);
3333 return cli_dynmsg(appctx, LOG_NOTICE, err);
3334 }
3335}
3336
3337/* Parsing function of 'commit ssl crl-file' */
3338static int cli_parse_commit_crlfile(char **args, char *payload, struct appctx *appctx, void *private)
3339{
3340 char *err = NULL;
3341
3342 if (!cli_has_level(appctx, ACCESS_LVL_ADMIN))
3343 return 1;
3344
3345 if (!*args[3])
3346 return cli_err(appctx, "'commit ssl ca-file expects a filename\n");
3347
3348 /* The operations on the CKCH architecture are locked so we can
3349 * manipulate ckch_store and ckch_inst */
3350 if (HA_SPIN_TRYLOCK(CKCH_LOCK, &ckch_lock))
3351 return cli_err(appctx, "Can't commit the CRL file!\nOperations on certificates are currently locked!\n");
3352
3353 if (!crlfile_transaction.path) {
3354 memprintf(&err, "No ongoing transaction! !\n");
3355 goto error;
3356 }
3357
3358 if (strcmp(crlfile_transaction.path, args[3]) != 0) {
3359 memprintf(&err, "The ongoing transaction is about '%s' but you are trying to set '%s'\n", crlfile_transaction.path, args[3]);
3360 goto error;
3361 }
3362 /* init the appctx structure */
3363 appctx->st2 = SETCERT_ST_INIT;
3364 appctx->ctx.ssl.next_ckchi = NULL;
3365 appctx->ctx.ssl.old_crlfile_entry = crlfile_transaction.old_crlfile_entry;
3366 appctx->ctx.ssl.new_crlfile_entry = crlfile_transaction.new_crlfile_entry;
3367 appctx->ctx.ssl.cafile_type = CAFILE_CRL;
3368
3369 return 0;
3370
3371error:
3372
3373 HA_SPIN_UNLOCK(CKCH_LOCK, &ckch_lock);
3374 err = memprintf(&err, "%sCan't commit %s!\n", err ? err : "", args[3]);
3375
3376 return cli_dynerr(appctx, err);
3377}
3378
3379
3380/* release function of the `commit ssl crl-file' command, free things and unlock the spinlock */
3381static void cli_release_commit_crlfile(struct appctx *appctx)
3382{
3383 if (appctx->st2 != SETCERT_ST_FIN) {
3384 struct cafile_entry *new_crlfile_entry = appctx->ctx.ssl.new_crlfile_entry;
3385
3386 /* Remove the uncommitted cafile_entry from the tree. */
3387 ebmb_delete(&new_crlfile_entry->node);
3388 ssl_store_delete_cafile_entry(new_crlfile_entry);
3389 }
3390 HA_SPIN_UNLOCK(CKCH_LOCK, &ckch_lock);
3391}
3392
Remi Tricot-Le Breton720e3b92021-04-26 11:00:42 +02003393/* parsing function of 'del ssl crl-file' */
3394static int cli_parse_del_crlfile(char **args, char *payload, struct appctx *appctx, void *private)
3395{
3396 struct cafile_entry *cafile_entry;
3397 char *err = NULL;
3398 char *filename;
3399
3400 if (!cli_has_level(appctx, ACCESS_LVL_ADMIN))
3401 return 1;
3402
3403 if (!*args[3])
3404 return cli_err(appctx, "'del ssl crl-file' expects a CRL file name\n");
3405
3406 if (HA_SPIN_TRYLOCK(CKCH_LOCK, &ckch_lock))
3407 return cli_err(appctx, "Can't delete the CRL file!\nOperations on certificates are currently locked!\n");
3408
3409 filename = args[3];
3410
3411 cafile_entry = ssl_store_get_cafile_entry(filename, 0);
3412 if (!cafile_entry) {
3413 memprintf(&err, "CRL file '%s' doesn't exist!\n", filename);
3414 goto error;
3415 }
3416 if (cafile_entry->type != CAFILE_CRL) {
3417 memprintf(&err, "'del ssl crl-file' does not work on CA files!\n");
3418 goto error;
3419 }
3420
3421 if (!LIST_ISEMPTY(&cafile_entry->ckch_inst_link)) {
3422 memprintf(&err, "CRL file '%s' in use, can't be deleted!\n", filename);
3423 goto error;
3424 }
3425
3426 /* Remove the cafile_entry from the tree */
3427 ebmb_delete(&cafile_entry->node);
3428 ssl_store_delete_cafile_entry(cafile_entry);
3429
3430 memprintf(&err, "CRL file '%s' deleted!\n", filename);
3431
3432 HA_SPIN_UNLOCK(CKCH_LOCK, &ckch_lock);
3433 return cli_dynmsg(appctx, LOG_NOTICE, err);
3434
3435error:
3436 memprintf(&err, "Can't remove the CRL file: %s\n", err ? err : "");
3437 HA_SPIN_UNLOCK(CKCH_LOCK, &ckch_lock);
3438 return cli_dynerr(appctx, err);
3439}
3440
Remi Tricot-Le Bretoneef8e7b2021-04-20 17:42:02 +02003441/* parsing function of 'abort ssl crl-file' */
3442static int cli_parse_abort_crlfile(char **args, char *payload, struct appctx *appctx, void *private)
3443{
3444 char *err = NULL;
3445
3446 if (!cli_has_level(appctx, ACCESS_LVL_ADMIN))
3447 return 1;
3448
3449 if (!*args[3])
3450 return cli_err(appctx, "'abort ssl crl-file' expects a filename\n");
3451
3452 /* The operations on the CKCH architecture are locked so we can
3453 * manipulate ckch_store and ckch_inst */
3454 if (HA_SPIN_TRYLOCK(CKCH_LOCK, &ckch_lock))
3455 return cli_err(appctx, "Can't abort!\nOperations on certificates are currently locked!\n");
3456
3457 if (!crlfile_transaction.path) {
3458 memprintf(&err, "No ongoing transaction!\n");
3459 goto error;
3460 }
3461
3462 if (strcmp(crlfile_transaction.path, args[3]) != 0) {
3463 memprintf(&err, "The ongoing transaction is about '%s' but you are trying to abort a transaction for '%s'\n", crlfile_transaction.path, args[3]);
3464 goto error;
3465 }
3466
3467 /* Only free the uncommitted cafile_entry here, because the SNI and instances were not generated yet */
3468 ssl_store_delete_cafile_entry(crlfile_transaction.new_crlfile_entry);
3469 crlfile_transaction.new_crlfile_entry = NULL;
3470 crlfile_transaction.old_crlfile_entry = NULL;
3471 ha_free(&crlfile_transaction.path);
3472
3473 HA_SPIN_UNLOCK(CKCH_LOCK, &ckch_lock);
3474
3475 err = memprintf(&err, "Transaction aborted for certificate '%s'!\n", args[3]);
3476 return cli_dynmsg(appctx, LOG_NOTICE, err);
3477
3478error:
3479 HA_SPIN_UNLOCK(CKCH_LOCK, &ckch_lock);
3480
3481 return cli_dynerr(appctx, err);
3482}
3483
Remi Tricot-Le Breton2a22e162021-03-16 11:19:33 +01003484
Remi Tricot-Le Breton51e28b62021-04-20 17:58:01 +02003485/*
3486 * Display a Certificate Resignation List's information.
3487 * The information displayed is inspired by the output of 'openssl crl -in
3488 * crl.pem -text'.
3489 * Returns 0 in case of success.
3490 */
3491static int show_crl_detail(X509_CRL *crl, struct buffer *out)
3492{
3493 BIO *bio = NULL;
3494 struct buffer *tmp = alloc_trash_chunk();
3495 long version;
3496 X509_NAME *issuer;
3497 int write = -1;
3498 STACK_OF(X509_REVOKED) *rev = NULL;
3499 X509_REVOKED *rev_entry = NULL;
3500 int i;
3501
3502 if (!tmp)
3503 return -1;
3504
3505 if ((bio = BIO_new(BIO_s_mem())) == NULL)
3506 goto end;
3507
3508 /* Version (as displayed by 'openssl crl') */
3509 version = X509_CRL_get_version(crl);
3510 chunk_appendf(out, "Version %ld\n", version + 1);
3511
3512 /* Signature Algorithm */
3513 chunk_appendf(out, "Signature Algorithm: %s\n", OBJ_nid2ln(X509_CRL_get_signature_nid(crl)));
3514
3515 /* Issuer */
3516 chunk_appendf(out, "Issuer: ");
3517 if ((issuer = X509_CRL_get_issuer(crl)) == NULL)
3518 goto end;
3519 if ((ssl_sock_get_dn_oneline(issuer, tmp)) == -1)
3520 goto end;
3521 *(tmp->area + tmp->data) = '\0';
3522 chunk_appendf(out, "%s\n", tmp->area);
3523
3524 /* Last Update */
3525 chunk_appendf(out, "Last Update: ");
3526 chunk_reset(tmp);
Remi Tricot-Le Bretond75b99e2021-05-17 11:45:55 +02003527 if (BIO_reset(bio) == -1)
3528 goto end;
Remi Tricot-Le Breton51e28b62021-04-20 17:58:01 +02003529 if (ASN1_TIME_print(bio, X509_CRL_get0_lastUpdate(crl)) == 0)
3530 goto end;
3531 write = BIO_read(bio, tmp->area, tmp->size-1);
3532 tmp->area[write] = '\0';
3533 chunk_appendf(out, "%s\n", tmp->area);
3534
3535
3536 /* Next Update */
3537 chunk_appendf(out, "Next Update: ");
3538 chunk_reset(tmp);
Remi Tricot-Le Bretond75b99e2021-05-17 11:45:55 +02003539 if (BIO_reset(bio) == -1)
3540 goto end;
Remi Tricot-Le Breton51e28b62021-04-20 17:58:01 +02003541 if (ASN1_TIME_print(bio, X509_CRL_get0_nextUpdate(crl)) == 0)
3542 goto end;
3543 write = BIO_read(bio, tmp->area, tmp->size-1);
3544 tmp->area[write] = '\0';
3545 chunk_appendf(out, "%s\n", tmp->area);
3546
3547
3548 /* Revoked Certificates */
3549 rev = X509_CRL_get_REVOKED(crl);
3550 if (sk_X509_REVOKED_num(rev) > 0)
3551 chunk_appendf(out, "Revoked Certificates:\n");
3552 else
3553 chunk_appendf(out, "No Revoked Certificates.\n");
3554
3555 for (i = 0; i < sk_X509_REVOKED_num(rev); i++) {
3556 rev_entry = sk_X509_REVOKED_value(rev, i);
3557
3558 /* Serial Number and Revocation Date */
Remi Tricot-Le Bretond75b99e2021-05-17 11:45:55 +02003559 if (BIO_reset(bio) == -1)
3560 goto end;
Remi Tricot-Le Breton51e28b62021-04-20 17:58:01 +02003561 BIO_printf(bio , " Serial Number: ");
Remi Tricot-Le Breton18c7d832021-05-17 18:38:34 +02003562 i2a_ASN1_INTEGER(bio, (ASN1_INTEGER*)X509_REVOKED_get0_serialNumber(rev_entry));
Remi Tricot-Le Breton51e28b62021-04-20 17:58:01 +02003563 BIO_printf(bio, "\n Revocation Date: ");
Remi Tricot-Le Bretona6b27842021-05-18 10:06:00 +02003564 if (ASN1_TIME_print(bio, X509_REVOKED_get0_revocationDate(rev_entry)) == 0)
3565 goto end;
Remi Tricot-Le Breton51e28b62021-04-20 17:58:01 +02003566 BIO_printf(bio, "\n");
3567
3568 write = BIO_read(bio, tmp->area, tmp->size-1);
3569 tmp->area[write] = '\0';
3570 chunk_appendf(out, "%s", tmp->area);
3571 }
3572
3573end:
3574 free_trash_chunk(tmp);
3575 if (bio)
3576 BIO_free(bio);
3577
3578 return 0;
3579}
3580
3581/* IO handler of details "show ssl crl-file <filename[:index]>" */
3582static int cli_io_handler_show_crlfile_detail(struct appctx *appctx)
3583{
Christopher Faulet86e1c332021-12-20 17:09:39 +01003584 struct stream_interface *si = cs_si(appctx->owner);
Remi Tricot-Le Breton51e28b62021-04-20 17:58:01 +02003585 struct cafile_entry *cafile_entry = appctx->ctx.cli.p0;
3586 struct buffer *out = alloc_trash_chunk();
3587 int i;
3588 X509_CRL *crl;
3589 STACK_OF(X509_OBJECT) *objs;
3590 int retval = 0;
3591 long index = (long)appctx->ctx.cli.p1;
3592
3593 if (!out)
3594 goto end_no_putchk;
3595
3596 chunk_appendf(out, "Filename: ");
3597 if (cafile_entry == crlfile_transaction.new_crlfile_entry)
3598 chunk_appendf(out, "*");
3599 chunk_appendf(out, "%s\n", cafile_entry->path);
3600
3601 chunk_appendf(out, "Status: ");
3602 if (!cafile_entry->ca_store)
3603 chunk_appendf(out, "Empty\n");
3604 else if (LIST_ISEMPTY(&cafile_entry->ckch_inst_link))
3605 chunk_appendf(out, "Unused\n");
3606 else
3607 chunk_appendf(out, "Used\n");
3608
3609 if (!cafile_entry->ca_store)
3610 goto end;
3611
3612 objs = X509_STORE_get0_objects(cafile_entry->ca_store);
3613 for (i = 0; i < sk_X509_OBJECT_num(objs); i++) {
3614 crl = X509_OBJECT_get0_X509_CRL(sk_X509_OBJECT_value(objs, i));
3615 if (!crl)
3616 continue;
3617
3618 /* CRL indexes start at 1 on the CLI output. */
3619 if (index && index-1 != i)
3620 continue;
3621
Remi Tricot-Le Bretone8041fe2022-04-05 16:44:21 +02003622 chunk_appendf(out, " \nCertificate Revocation List #%d:\n", i+1);
Remi Tricot-Le Breton51e28b62021-04-20 17:58:01 +02003623 retval = show_crl_detail(crl, out);
3624 if (retval < 0)
3625 goto end_no_putchk;
3626 else if (retval || index)
3627 goto end;
3628 }
3629
3630end:
3631 if (ci_putchk(si_ic(si), out) == -1) {
3632 si_rx_room_blk(si);
3633 goto yield;
3634 }
3635
3636end_no_putchk:
3637 free_trash_chunk(out);
3638 return 1;
3639yield:
3640 free_trash_chunk(out);
3641 return 0; /* should come back */
3642}
3643
3644/* parsing function for 'show ssl crl-file [crlfile[:index]]' */
3645static int cli_parse_show_crlfile(char **args, char *payload, struct appctx *appctx, void *private)
3646{
3647 struct cafile_entry *cafile_entry;
3648 long index = 0;
3649 char *colons;
3650 char *err = NULL;
3651
3652 if (!cli_has_level(appctx, ACCESS_LVL_OPER))
3653 return cli_err(appctx, "Can't allocate memory!\n");
3654
3655 /* The operations on the CKCH architecture are locked so we can
3656 * manipulate ckch_store and ckch_inst */
3657 if (HA_SPIN_TRYLOCK(CKCH_LOCK, &ckch_lock))
3658 return cli_err(appctx, "Can't show!\nOperations on certificates are currently locked!\n");
3659
3660 /* check if there is a certificate to lookup */
3661 if (*args[3]) {
3662
3663 /* Look for an optional index after the CRL file name */
3664 colons = strchr(args[3], ':');
3665 if (colons) {
3666 char *endptr;
3667
3668 index = strtol(colons + 1, &endptr, 10);
3669 /* Indexes start at 1 */
3670 if (colons + 1 == endptr || *endptr != '\0' || index <= 0) {
3671 memprintf(&err, "wrong CRL index after colons in '%s'!", args[3]);
3672 goto error;
3673 }
3674 *colons = '\0';
3675 }
3676
3677 if (*args[3] == '*') {
3678 if (!crlfile_transaction.new_crlfile_entry)
3679 goto error;
3680
3681 cafile_entry = crlfile_transaction.new_crlfile_entry;
3682
3683 if (strcmp(args[3] + 1, cafile_entry->path) != 0)
3684 goto error;
3685
3686 } else {
3687 /* Get the "original" cafile_entry and not the
3688 * uncommitted one if it exists. */
3689 if ((cafile_entry = ssl_store_get_cafile_entry(args[3], 1)) == NULL || cafile_entry->type != CAFILE_CRL)
3690 goto error;
3691 }
3692
3693 appctx->ctx.cli.p0 = cafile_entry;
3694 appctx->ctx.cli.p1 = (void*)index;
3695 /* use the IO handler that shows details */
3696 appctx->io_handler = cli_io_handler_show_crlfile_detail;
3697 }
3698
3699 return 0;
3700
3701error:
3702 HA_SPIN_UNLOCK(CKCH_LOCK, &ckch_lock);
3703 if (err)
3704 return cli_dynerr(appctx, err);
3705 return cli_err(appctx, "Can't display the CA file : Not found!\n");
3706}
3707
3708/* IO handler of "show ssl crl-file". The command taking a specific CRL file name
3709 * is managed in cli_io_handler_show_crlfile_detail. */
3710static int cli_io_handler_show_crlfile(struct appctx *appctx)
3711{
3712 struct buffer *trash = alloc_trash_chunk();
3713 struct ebmb_node *node;
Christopher Faulet86e1c332021-12-20 17:09:39 +01003714 struct stream_interface *si = cs_si(appctx->owner);
Remi Tricot-Le Breton51e28b62021-04-20 17:58:01 +02003715 struct cafile_entry *cafile_entry;
3716
3717 if (trash == NULL)
3718 return 1;
3719
3720 if (!appctx->ctx.ssl.old_crlfile_entry) {
3721 if (crlfile_transaction.old_crlfile_entry) {
3722 chunk_appendf(trash, "# transaction\n");
3723 chunk_appendf(trash, "*%s\n", crlfile_transaction.old_crlfile_entry->path);
3724 }
3725 }
3726
3727 /* First time in this io_handler. */
3728 if (!appctx->ctx.cli.p0) {
3729 chunk_appendf(trash, "# filename\n");
3730 node = ebmb_first(&cafile_tree);
3731 } else {
3732 /* We yielded during a previous call. */
3733 node = &((struct cafile_entry*)appctx->ctx.cli.p0)->node;
3734 }
3735
3736 while (node) {
3737 cafile_entry = ebmb_entry(node, struct cafile_entry, node);
3738 if (cafile_entry->type == CAFILE_CRL) {
3739 chunk_appendf(trash, "%s\n", cafile_entry->path);
3740 }
3741
3742 node = ebmb_next(node);
3743 if (ci_putchk(si_ic(si), trash) == -1) {
3744 si_rx_room_blk(si);
3745 goto yield;
3746 }
3747 }
3748
3749 appctx->ctx.cli.p0 = NULL;
3750 free_trash_chunk(trash);
3751 return 1;
3752yield:
3753
3754 free_trash_chunk(trash);
3755 appctx->ctx.cli.p0 = cafile_entry;
3756 return 0; /* should come back */
3757}
3758
3759
3760/* release function of the 'show ssl crl-file' command */
3761static void cli_release_show_crlfile(struct appctx *appctx)
3762{
3763 HA_SPIN_UNLOCK(CKCH_LOCK, &ckch_lock);
3764}
3765
3766
William Lallemandee8530c2020-06-23 18:19:42 +02003767void ckch_deinit()
3768{
3769 struct eb_node *node, *next;
3770 struct ckch_store *store;
3771
3772 node = eb_first(&ckchs_tree);
3773 while (node) {
3774 next = eb_next(node);
3775 store = ebmb_entry(node, struct ckch_store, node);
3776 ckch_store_free(store);
3777 node = next;
3778 }
3779}
William Lallemandda8584c2020-05-14 10:14:37 +02003780
3781/* register cli keywords */
3782static struct cli_kw_list cli_kws = {{ },{
Remi Tricot-Le Bretona32a68b2021-02-24 17:35:43 +01003783 { { "new", "ssl", "cert", NULL }, "new ssl cert <certfile> : create a new certificate file to be used in a crt-list or a directory", cli_parse_new_cert, NULL, NULL },
3784 { { "set", "ssl", "cert", NULL }, "set ssl cert <certfile> <payload> : replace a certificate file", cli_parse_set_cert, NULL, NULL },
3785 { { "commit", "ssl", "cert", NULL }, "commit ssl cert <certfile> : commit a certificate file", cli_parse_commit_cert, cli_io_handler_commit_cert, cli_release_commit_cert },
3786 { { "abort", "ssl", "cert", NULL }, "abort ssl cert <certfile> : abort a transaction for a certificate file", cli_parse_abort_cert, NULL, NULL },
3787 { { "del", "ssl", "cert", NULL }, "del ssl cert <certfile> : delete an unused certificate file", cli_parse_del_cert, NULL, NULL },
3788 { { "show", "ssl", "cert", NULL }, "show ssl cert [<certfile>] : display the SSL certificates used in memory, or the details of a file", cli_parse_show_cert, cli_io_handler_show_cert, cli_release_show_cert },
3789
Amaury Denoyelleb11ad9e2021-05-21 11:01:10 +02003790 { { "new", "ssl", "ca-file", NULL }, "new ssl ca-file <cafile> : create a new CA file to be used in a crt-list", cli_parse_new_cafile, NULL, NULL },
Remi Tricot-Le Bretona32a68b2021-02-24 17:35:43 +01003791 { { "set", "ssl", "ca-file", NULL }, "set ssl ca-file <cafile> <payload> : replace a CA file", cli_parse_set_cafile, NULL, NULL },
Remi Tricot-Le Bretona51b3392021-04-20 17:38:14 +02003792 { { "commit", "ssl", "ca-file", NULL }, "commit ssl ca-file <cafile> : commit a CA file", cli_parse_commit_cafile, cli_io_handler_commit_cafile_crlfile, cli_release_commit_cafile },
Remi Tricot-Le Bretond5fd09d2021-03-11 10:22:52 +01003793 { { "abort", "ssl", "ca-file", NULL }, "abort ssl ca-file <cafile> : abort a transaction for a CA file", cli_parse_abort_cafile, NULL, NULL },
Remi Tricot-Le Bretonc3a84772021-03-25 18:13:57 +01003794 { { "del", "ssl", "ca-file", NULL }, "del ssl ca-file <cafile> : delete an unused CA file", cli_parse_del_cafile, NULL, NULL },
Remi Tricot-Le Breton2a22e162021-03-16 11:19:33 +01003795 { { "show", "ssl", "ca-file", NULL }, "show ssl ca-file [<cafile>[:<index>]] : display the SSL CA files used in memory, or the details of a <cafile>, or a single certificate of index <index> of a CA file <cafile>", cli_parse_show_cafile, cli_io_handler_show_cafile, cli_release_show_cafile },
Remi Tricot-Le Bretona51b3392021-04-20 17:38:14 +02003796
Remi Tricot-Le Breton720e3b92021-04-26 11:00:42 +02003797 { { "new", "ssl", "crl-file", NULL }, "new ssl crlfile <crlfile> : create a new CRL file to be used in a crt-list", cli_parse_new_crlfile, NULL, NULL },
Remi Tricot-Le Bretona51b3392021-04-20 17:38:14 +02003798 { { "set", "ssl", "crl-file", NULL }, "set ssl crl-file <crlfile> <payload> : replace a CRL file", cli_parse_set_crlfile, NULL, NULL },
3799 { { "commit", "ssl", "crl-file", NULL },"commit ssl crl-file <crlfile> : commit a CRL file", cli_parse_commit_crlfile, cli_io_handler_commit_cafile_crlfile, cli_release_commit_crlfile },
Remi Tricot-Le Bretoneef8e7b2021-04-20 17:42:02 +02003800 { { "abort", "ssl", "crl-file", NULL }, "abort ssl crl-file <crlfile> : abort a transaction for a CRL file", cli_parse_abort_crlfile, NULL, NULL },
Remi Tricot-Le Breton720e3b92021-04-26 11:00:42 +02003801 { { "del", "ssl", "crl-file", NULL }, "del ssl crl-file <crlfile> : delete an unused CRL file", cli_parse_del_crlfile, NULL, NULL },
Remi Tricot-Le Breton51e28b62021-04-20 17:58:01 +02003802 { { "show", "ssl", "crl-file", NULL }, "show ssl crl-file [<crlfile[:<index>>]] : display the SSL CRL files used in memory, or the details of a <crlfile>, or a single CRL of index <index> of CRL file <crlfile>", cli_parse_show_crlfile, cli_io_handler_show_crlfile, cli_release_show_crlfile },
William Lallemandda8584c2020-05-14 10:14:37 +02003803 { { NULL }, NULL, NULL, NULL }
3804}};
3805
3806INITCALL1(STG_REGISTER, cli_register_kw, &cli_kws);
3807