blob: e53e89f05fa0c3358def31b5f5838b33bbb1d0fa [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>
14#include <errno.h>
15#include <fcntl.h>
16#include <stdio.h>
17#include <stdlib.h>
18#include <string.h>
Willy Tarreauaeed4a82020-06-04 22:01:04 +020019#include <syslog.h>
William Lallemand03c331c2020-05-13 10:10:01 +020020#include <unistd.h>
21
22#include <sys/stat.h>
23#include <sys/types.h>
24
Willy Tarreaub2551052020-06-09 09:07:15 +020025#include <import/ebsttree.h>
26
Willy Tarreau8d366972020-05-27 16:10:29 +020027#include <haproxy/base64.h>
Willy Tarreauf1d32c42020-06-04 21:07:02 +020028#include <haproxy/channel.h>
Willy Tarreau83487a82020-06-04 20:19:54 +020029#include <haproxy/cli.h>
Willy Tarreau8d366972020-05-27 16:10:29 +020030#include <haproxy/errors.h>
Willy Tarreau47d7f902020-06-04 14:25:47 +020031#include <haproxy/ssl_ckch.h>
Willy Tarreau209108d2020-06-04 20:30:20 +020032#include <haproxy/ssl_sock.h>
Willy Tarreaub2bd8652020-06-04 14:21:22 +020033#include <haproxy/ssl_utils.h>
Willy Tarreau5e539c92020-06-04 20:45:39 +020034#include <haproxy/stream_interface.h>
Willy Tarreau48fbcae2020-06-03 18:09:46 +020035#include <haproxy/tools.h>
William Lallemand03c331c2020-05-13 10:10:01 +020036
William Lallemandda8584c2020-05-14 10:14:37 +020037/* Uncommitted CKCH transaction */
38
39static struct {
40 struct ckch_store *new_ckchs;
41 struct ckch_store *old_ckchs;
42 char *path;
43} ckchs_transaction;
44
Remi Tricot-Le Bretona32a68b2021-02-24 17:35:43 +010045/* Uncommitted CA file transaction */
46
47static struct {
48 struct cafile_entry *old_cafile_entry;
49 struct cafile_entry *new_cafile_entry;
50 char *path;
51} cafile_transaction;
William Lallemandda8584c2020-05-14 10:14:37 +020052
William Lallemand03c331c2020-05-13 10:10:01 +020053
Remi Tricot-Le Bretona32a68b2021-02-24 17:35:43 +010054
William Lallemand03c331c2020-05-13 10:10:01 +020055/******************** cert_key_and_chain functions *************************
56 * These are the functions that fills a cert_key_and_chain structure. For the
57 * functions filling a SSL_CTX from a cert_key_and_chain, see ssl_sock.c
58 */
59
60/*
61 * Try to parse Signed Certificate Timestamp List structure. This function
62 * makes only basic test if the data seems like SCTL. No signature validation
63 * is performed.
64 */
65static int ssl_sock_parse_sctl(struct buffer *sctl)
66{
67 int ret = 1;
68 int len, pos, sct_len;
69 unsigned char *data;
70
71 if (sctl->data < 2)
72 goto out;
73
74 data = (unsigned char *) sctl->area;
75 len = (data[0] << 8) | data[1];
76
77 if (len + 2 != sctl->data)
78 goto out;
79
80 data = data + 2;
81 pos = 0;
82 while (pos < len) {
83 if (len - pos < 2)
84 goto out;
85
86 sct_len = (data[pos] << 8) | data[pos + 1];
87 if (pos + sct_len + 2 > len)
88 goto out;
89
90 pos += sct_len + 2;
91 }
92
93 ret = 0;
94
95out:
96 return ret;
97}
98
99/* Try to load a sctl from a buffer <buf> if not NULL, or read the file <sctl_path>
100 * It fills the ckch->sctl buffer
101 * return 0 on success or != 0 on failure */
102int ssl_sock_load_sctl_from_file(const char *sctl_path, char *buf, struct cert_key_and_chain *ckch, char **err)
103{
104 int fd = -1;
105 int r = 0;
106 int ret = 1;
107 struct buffer tmp;
108 struct buffer *src;
109 struct buffer *sctl;
110
111 if (buf) {
William Lallemand8d673942021-01-27 14:58:51 +0100112 chunk_initstr(&tmp, buf);
William Lallemand03c331c2020-05-13 10:10:01 +0200113 src = &tmp;
114 } else {
115 fd = open(sctl_path, O_RDONLY);
116 if (fd == -1)
117 goto end;
118
119 trash.data = 0;
120 while (trash.data < trash.size) {
121 r = read(fd, trash.area + trash.data, trash.size - trash.data);
122 if (r < 0) {
123 if (errno == EINTR)
124 continue;
125 goto end;
126 }
127 else if (r == 0) {
128 break;
129 }
130 trash.data += r;
131 }
132 src = &trash;
133 }
134
135 ret = ssl_sock_parse_sctl(src);
136 if (ret)
137 goto end;
138
139 sctl = calloc(1, sizeof(*sctl));
140 if (!chunk_dup(sctl, src)) {
Willy Tarreau61cfdf42021-02-20 10:46:51 +0100141 ha_free(&sctl);
William Lallemand03c331c2020-05-13 10:10:01 +0200142 goto end;
143 }
144 /* no error, fill ckch with new context, old context must be free */
145 if (ckch->sctl) {
Willy Tarreau61cfdf42021-02-20 10:46:51 +0100146 ha_free(&ckch->sctl->area);
William Lallemand03c331c2020-05-13 10:10:01 +0200147 free(ckch->sctl);
148 }
149 ckch->sctl = sctl;
150 ret = 0;
151end:
152 if (fd != -1)
153 close(fd);
154
155 return ret;
156}
157
158#if ((defined SSL_CTRL_SET_TLSEXT_STATUS_REQ_CB && !defined OPENSSL_NO_OCSP) || defined OPENSSL_IS_BORINGSSL)
159/*
Ilya Shipitsin46a030c2020-07-05 16:36:08 +0500160 * This function load the OCSP Response in DER format contained in file at
William Lallemand03c331c2020-05-13 10:10:01 +0200161 * path 'ocsp_path' or base64 in a buffer <buf>
162 *
163 * Returns 0 on success, 1 in error case.
164 */
165int ssl_sock_load_ocsp_response_from_file(const char *ocsp_path, char *buf, struct cert_key_and_chain *ckch, char **err)
166{
167 int fd = -1;
168 int r = 0;
169 int ret = 1;
170 struct buffer *ocsp_response;
171 struct buffer *src = NULL;
172
173 if (buf) {
174 int i, j;
175 /* if it's from a buffer it will be base64 */
176
177 /* remove \r and \n from the payload */
178 for (i = 0, j = 0; buf[i]; i++) {
179 if (buf[i] == '\r' || buf[i] == '\n')
180 continue;
181 buf[j++] = buf[i];
182 }
183 buf[j] = 0;
184
185 ret = base64dec(buf, j, trash.area, trash.size);
186 if (ret < 0) {
187 memprintf(err, "Error reading OCSP response in base64 format");
188 goto end;
189 }
190 trash.data = ret;
191 src = &trash;
192 } else {
193 fd = open(ocsp_path, O_RDONLY);
194 if (fd == -1) {
195 memprintf(err, "Error opening OCSP response file");
196 goto end;
197 }
198
199 trash.data = 0;
200 while (trash.data < trash.size) {
201 r = read(fd, trash.area + trash.data, trash.size - trash.data);
202 if (r < 0) {
203 if (errno == EINTR)
204 continue;
205
206 memprintf(err, "Error reading OCSP response from file");
207 goto end;
208 }
209 else if (r == 0) {
210 break;
211 }
212 trash.data += r;
213 }
214 close(fd);
215 fd = -1;
216 src = &trash;
217 }
218
219 ocsp_response = calloc(1, sizeof(*ocsp_response));
220 if (!chunk_dup(ocsp_response, src)) {
Willy Tarreau61cfdf42021-02-20 10:46:51 +0100221 ha_free(&ocsp_response);
William Lallemand03c331c2020-05-13 10:10:01 +0200222 goto end;
223 }
224 /* no error, fill ckch with new context, old context must be free */
225 if (ckch->ocsp_response) {
Willy Tarreau61cfdf42021-02-20 10:46:51 +0100226 ha_free(&ckch->ocsp_response->area);
William Lallemand03c331c2020-05-13 10:10:01 +0200227 free(ckch->ocsp_response);
228 }
229 ckch->ocsp_response = ocsp_response;
230 ret = 0;
231end:
232 if (fd != -1)
233 close(fd);
234
235 return ret;
236}
237#endif
238
239/*
240 * Try to load in a ckch every files related to a ckch.
241 * (PEM, sctl, ocsp, issuer etc.)
242 *
243 * This function is only used to load files during the configuration parsing,
244 * it is not used with the CLI.
245 *
246 * This allows us to carry the contents of the file without having to read the
247 * file multiple times. The caller must call
248 * ssl_sock_free_cert_key_and_chain_contents.
249 *
250 * returns:
251 * 0 on Success
252 * 1 on SSL Failure
253 */
254int ssl_sock_load_files_into_ckch(const char *path, struct cert_key_and_chain *ckch, char **err)
255{
William Lallemand8e8581e2020-10-20 17:36:46 +0200256 struct buffer *fp = NULL;
William Lallemand03c331c2020-05-13 10:10:01 +0200257 int ret = 1;
258
259 /* try to load the PEM */
260 if (ssl_sock_load_pem_into_ckch(path, NULL, ckch , err) != 0) {
261 goto end;
262 }
263
William Lallemand8e8581e2020-10-20 17:36:46 +0200264 fp = alloc_trash_chunk();
265 if (!fp) {
266 memprintf(err, "%sCan't allocate memory\n", err && *err ? *err : "");
267 goto end;
268 }
269
270 if (!chunk_strcpy(fp, path) || (b_data(fp) > MAXPATHLEN)) {
271 memprintf(err, "%s '%s' filename too long'.\n",
272 err && *err ? *err : "", fp->area);
273 ret = 1;
274 goto end;
275 }
276
William Lallemand089c1382020-10-23 17:35:12 +0200277 /* remove the ".crt" extension */
William Lallemand8e8581e2020-10-20 17:36:46 +0200278 if (global_ssl.extra_files_noext) {
279 char *ext;
280
281 /* look for the extension */
282 if ((ext = strrchr(fp->area, '.'))) {
William Lallemand8e8581e2020-10-20 17:36:46 +0200283
Tim Duesterhuse5ff1412021-01-02 22:31:53 +0100284 if (strcmp(ext, ".crt") == 0) {
William Lallemand8e8581e2020-10-20 17:36:46 +0200285 *ext = '\0';
William Lallemand089c1382020-10-23 17:35:12 +0200286 fp->data = strlen(fp->area);
287 }
William Lallemand8e8581e2020-10-20 17:36:46 +0200288 }
289
290 }
291
William Lallemand03c331c2020-05-13 10:10:01 +0200292 /* try to load an external private key if it wasn't in the PEM */
293 if ((ckch->key == NULL) && (global_ssl.extra_files & SSL_GF_KEY)) {
William Lallemand03c331c2020-05-13 10:10:01 +0200294 struct stat st;
295
William Lallemand8e8581e2020-10-20 17:36:46 +0200296
297 if (!chunk_strcat(fp, ".key") || (b_data(fp) > MAXPATHLEN)) {
298 memprintf(err, "%s '%s' filename too long'.\n",
299 err && *err ? *err : "", fp->area);
300 ret = 1;
301 goto end;
302 }
303
304 if (stat(fp->area, &st) == 0) {
305 if (ssl_sock_load_key_into_ckch(fp->area, NULL, ckch, err)) {
William Lallemand03c331c2020-05-13 10:10:01 +0200306 memprintf(err, "%s '%s' is present but cannot be read or parsed'.\n",
William Lallemand8e8581e2020-10-20 17:36:46 +0200307 err && *err ? *err : "", fp->area);
William Lallemand03c331c2020-05-13 10:10:01 +0200308 goto end;
309 }
310 }
William Lallemand03c331c2020-05-13 10:10:01 +0200311
William Lallemand8e8581e2020-10-20 17:36:46 +0200312 if (ckch->key == NULL) {
313 memprintf(err, "%sNo Private Key found in '%s'.\n", err && *err ? *err : "", fp->area);
314 goto end;
315 }
316 /* remove the added extension */
317 *(fp->area + fp->data - strlen(".key")) = '\0';
318 b_sub(fp, strlen(".key"));
William Lallemand03c331c2020-05-13 10:10:01 +0200319 }
320
321 if (!X509_check_private_key(ckch->cert, ckch->key)) {
322 memprintf(err, "%sinconsistencies between private key and certificate loaded '%s'.\n",
323 err && *err ? *err : "", path);
324 goto end;
325 }
326
Ilya Shipitsinc47d6762021-02-13 11:45:33 +0500327#ifdef HAVE_SSL_SCTL
William Lallemand03c331c2020-05-13 10:10:01 +0200328 /* try to load the sctl file */
329 if (global_ssl.extra_files & SSL_GF_SCTL) {
William Lallemand03c331c2020-05-13 10:10:01 +0200330 struct stat st;
331
William Lallemand8e8581e2020-10-20 17:36:46 +0200332 if (!chunk_strcat(fp, ".sctl") || b_data(fp) > MAXPATHLEN) {
333 memprintf(err, "%s '%s' filename too long'.\n",
334 err && *err ? *err : "", fp->area);
335 ret = 1;
336 goto end;
337 }
338
339 if (stat(fp->area, &st) == 0) {
340 if (ssl_sock_load_sctl_from_file(fp->area, NULL, ckch, err)) {
William Lallemand03c331c2020-05-13 10:10:01 +0200341 memprintf(err, "%s '%s.sctl' is present but cannot be read or parsed'.\n",
William Lallemand8e8581e2020-10-20 17:36:46 +0200342 err && *err ? *err : "", fp->area);
William Lallemand03c331c2020-05-13 10:10:01 +0200343 ret = 1;
344 goto end;
345 }
346 }
William Lallemand8e8581e2020-10-20 17:36:46 +0200347 /* remove the added extension */
348 *(fp->area + fp->data - strlen(".sctl")) = '\0';
349 b_sub(fp, strlen(".sctl"));
William Lallemand03c331c2020-05-13 10:10:01 +0200350 }
351#endif
352
353 /* try to load an ocsp response file */
354 if (global_ssl.extra_files & SSL_GF_OCSP) {
William Lallemand03c331c2020-05-13 10:10:01 +0200355 struct stat st;
356
William Lallemand8e8581e2020-10-20 17:36:46 +0200357 if (!chunk_strcat(fp, ".ocsp") || b_data(fp) > MAXPATHLEN) {
358 memprintf(err, "%s '%s' filename too long'.\n",
359 err && *err ? *err : "", fp->area);
360 ret = 1;
361 goto end;
362 }
363
364 if (stat(fp->area, &st) == 0) {
365 if (ssl_sock_load_ocsp_response_from_file(fp->area, NULL, ckch, err)) {
William Lallemand03c331c2020-05-13 10:10:01 +0200366 ret = 1;
367 goto end;
368 }
369 }
William Lallemand8e8581e2020-10-20 17:36:46 +0200370 /* remove the added extension */
371 *(fp->area + fp->data - strlen(".ocsp")) = '\0';
372 b_sub(fp, strlen(".ocsp"));
William Lallemand03c331c2020-05-13 10:10:01 +0200373 }
374
375#ifndef OPENSSL_IS_BORINGSSL /* Useless for BoringSSL */
376 if (ckch->ocsp_response && (global_ssl.extra_files & SSL_GF_OCSP_ISSUER)) {
377 /* if no issuer was found, try to load an issuer from the .issuer */
378 if (!ckch->ocsp_issuer) {
379 struct stat st;
William Lallemand8e8581e2020-10-20 17:36:46 +0200380
381 if (!chunk_strcat(fp, ".issuer") || b_data(fp) > MAXPATHLEN) {
382 memprintf(err, "%s '%s' filename too long'.\n",
383 err && *err ? *err : "", fp->area);
384 ret = 1;
385 goto end;
386 }
William Lallemand03c331c2020-05-13 10:10:01 +0200387
William Lallemand8e8581e2020-10-20 17:36:46 +0200388 if (stat(fp->area, &st) == 0) {
389 if (ssl_sock_load_issuer_file_into_ckch(fp->area, NULL, ckch, err)) {
William Lallemand03c331c2020-05-13 10:10:01 +0200390 ret = 1;
391 goto end;
392 }
393
394 if (X509_check_issued(ckch->ocsp_issuer, ckch->cert) != X509_V_OK) {
395 memprintf(err, "%s '%s' is not an issuer'.\n",
William Lallemand8e8581e2020-10-20 17:36:46 +0200396 err && *err ? *err : "", fp->area);
William Lallemand03c331c2020-05-13 10:10:01 +0200397 ret = 1;
398 goto end;
399 }
400 }
William Lallemand8e8581e2020-10-20 17:36:46 +0200401 /* remove the added extension */
402 *(fp->area + fp->data - strlen(".issuer")) = '\0';
403 b_sub(fp, strlen(".issuer"));
William Lallemand03c331c2020-05-13 10:10:01 +0200404 }
405 }
406#endif
407
408 ret = 0;
409
410end:
411
412 ERR_clear_error();
413
414 /* Something went wrong in one of the reads */
415 if (ret != 0)
416 ssl_sock_free_cert_key_and_chain_contents(ckch);
417
William Lallemand8e8581e2020-10-20 17:36:46 +0200418 free_trash_chunk(fp);
419
William Lallemand03c331c2020-05-13 10:10:01 +0200420 return ret;
421}
422
423/*
424 * Try to load a private key file from a <path> or a buffer <buf>
425 *
426 * If it failed you should not attempt to use the ckch but free it.
427 *
428 * Return 0 on success or != 0 on failure
429 */
430int ssl_sock_load_key_into_ckch(const char *path, char *buf, struct cert_key_and_chain *ckch , char **err)
431{
432 BIO *in = NULL;
433 int ret = 1;
434 EVP_PKEY *key = NULL;
435
436 if (buf) {
437 /* reading from a buffer */
438 in = BIO_new_mem_buf(buf, -1);
439 if (in == NULL) {
440 memprintf(err, "%sCan't allocate memory\n", err && *err ? *err : "");
441 goto end;
442 }
443
444 } else {
445 /* reading from a file */
446 in = BIO_new(BIO_s_file());
447 if (in == NULL)
448 goto end;
449
450 if (BIO_read_filename(in, path) <= 0)
451 goto end;
452 }
453
454 /* Read Private Key */
455 key = PEM_read_bio_PrivateKey(in, NULL, NULL, NULL);
456 if (key == NULL) {
457 memprintf(err, "%sunable to load private key from file '%s'.\n",
458 err && *err ? *err : "", path);
459 goto end;
460 }
461
462 ret = 0;
463
464 SWAP(ckch->key, key);
465
466end:
467
468 ERR_clear_error();
469 if (in)
470 BIO_free(in);
471 if (key)
472 EVP_PKEY_free(key);
473
474 return ret;
475}
476
477/*
478 * Try to load a PEM file from a <path> or a buffer <buf>
479 * The PEM must contain at least a Certificate,
480 * It could contain a DH, a certificate chain and a PrivateKey.
481 *
482 * If it failed you should not attempt to use the ckch but free it.
483 *
484 * Return 0 on success or != 0 on failure
485 */
486int ssl_sock_load_pem_into_ckch(const char *path, char *buf, struct cert_key_and_chain *ckch , char **err)
487{
488 BIO *in = NULL;
489 int ret = 1;
490 X509 *ca;
491 X509 *cert = NULL;
492 EVP_PKEY *key = NULL;
493 DH *dh = NULL;
494 STACK_OF(X509) *chain = NULL;
495
496 if (buf) {
497 /* reading from a buffer */
498 in = BIO_new_mem_buf(buf, -1);
499 if (in == NULL) {
500 memprintf(err, "%sCan't allocate memory\n", err && *err ? *err : "");
501 goto end;
502 }
503
504 } else {
505 /* reading from a file */
506 in = BIO_new(BIO_s_file());
507 if (in == NULL) {
508 memprintf(err, "%sCan't allocate memory\n", err && *err ? *err : "");
509 goto end;
510 }
511
512 if (BIO_read_filename(in, path) <= 0) {
513 memprintf(err, "%scannot open the file '%s'.\n",
514 err && *err ? *err : "", path);
515 goto end;
516 }
517 }
518
519 /* Read Private Key */
520 key = PEM_read_bio_PrivateKey(in, NULL, NULL, NULL);
521 /* no need to check for errors here, because the private key could be loaded later */
522
523#ifndef OPENSSL_NO_DH
524 /* Seek back to beginning of file */
525 if (BIO_reset(in) == -1) {
526 memprintf(err, "%san error occurred while reading the file '%s'.\n",
527 err && *err ? *err : "", path);
528 goto end;
529 }
530
531 dh = PEM_read_bio_DHparams(in, NULL, NULL, NULL);
532 /* no need to return an error there, dh is not mandatory */
533#endif
534
535 /* Seek back to beginning of file */
536 if (BIO_reset(in) == -1) {
537 memprintf(err, "%san error occurred while reading the file '%s'.\n",
538 err && *err ? *err : "", path);
539 goto end;
540 }
541
542 /* Read Certificate */
543 cert = PEM_read_bio_X509_AUX(in, NULL, NULL, NULL);
544 if (cert == NULL) {
545 memprintf(err, "%sunable to load certificate from file '%s'.\n",
546 err && *err ? *err : "", path);
547 goto end;
548 }
549
550 /* Look for a Certificate Chain */
551 while ((ca = PEM_read_bio_X509(in, NULL, NULL, NULL))) {
552 if (chain == NULL)
553 chain = sk_X509_new_null();
554 if (!sk_X509_push(chain, ca)) {
555 X509_free(ca);
556 goto end;
557 }
558 }
559
560 ret = ERR_get_error();
561 if (ret && (ERR_GET_LIB(ret) != ERR_LIB_PEM && ERR_GET_REASON(ret) != PEM_R_NO_START_LINE)) {
562 memprintf(err, "%sunable to load certificate chain from file '%s'.\n",
563 err && *err ? *err : "", path);
564 goto end;
565 }
566
567 /* once it loaded the PEM, it should remove everything else in the ckch */
568 if (ckch->ocsp_response) {
Willy Tarreau61cfdf42021-02-20 10:46:51 +0100569 ha_free(&ckch->ocsp_response->area);
570 ha_free(&ckch->ocsp_response);
William Lallemand03c331c2020-05-13 10:10:01 +0200571 }
572
573 if (ckch->sctl) {
Willy Tarreau61cfdf42021-02-20 10:46:51 +0100574 ha_free(&ckch->sctl->area);
575 ha_free(&ckch->sctl);
William Lallemand03c331c2020-05-13 10:10:01 +0200576 }
577
578 if (ckch->ocsp_issuer) {
579 X509_free(ckch->ocsp_issuer);
580 ckch->ocsp_issuer = NULL;
581 }
582
583 /* no error, fill ckch with new context, old context will be free at end: */
584 SWAP(ckch->key, key);
585 SWAP(ckch->dh, dh);
586 SWAP(ckch->cert, cert);
587 SWAP(ckch->chain, chain);
588
589 ret = 0;
590
591end:
592
593 ERR_clear_error();
594 if (in)
595 BIO_free(in);
596 if (key)
597 EVP_PKEY_free(key);
598 if (dh)
599 DH_free(dh);
600 if (cert)
601 X509_free(cert);
602 if (chain)
603 sk_X509_pop_free(chain, X509_free);
604
605 return ret;
606}
607
608/* Frees the contents of a cert_key_and_chain
609 */
610void ssl_sock_free_cert_key_and_chain_contents(struct cert_key_and_chain *ckch)
611{
612 if (!ckch)
613 return;
614
615 /* Free the certificate and set pointer to NULL */
616 if (ckch->cert)
617 X509_free(ckch->cert);
618 ckch->cert = NULL;
619
620 /* Free the key and set pointer to NULL */
621 if (ckch->key)
622 EVP_PKEY_free(ckch->key);
623 ckch->key = NULL;
624
625 /* Free each certificate in the chain */
626 if (ckch->chain)
627 sk_X509_pop_free(ckch->chain, X509_free);
628 ckch->chain = NULL;
629
630 if (ckch->dh)
631 DH_free(ckch->dh);
632 ckch->dh = NULL;
633
634 if (ckch->sctl) {
Willy Tarreau61cfdf42021-02-20 10:46:51 +0100635 ha_free(&ckch->sctl->area);
636 ha_free(&ckch->sctl);
William Lallemand03c331c2020-05-13 10:10:01 +0200637 }
638
639 if (ckch->ocsp_response) {
Willy Tarreau61cfdf42021-02-20 10:46:51 +0100640 ha_free(&ckch->ocsp_response->area);
641 ha_free(&ckch->ocsp_response);
William Lallemand03c331c2020-05-13 10:10:01 +0200642 }
643
644 if (ckch->ocsp_issuer)
645 X509_free(ckch->ocsp_issuer);
646 ckch->ocsp_issuer = NULL;
647}
648
649/*
650 *
651 * This function copy a cert_key_and_chain in memory
652 *
653 * It's used to try to apply changes on a ckch before committing them, because
654 * most of the time it's not possible to revert those changes
655 *
656 * Return a the dst or NULL
657 */
658struct cert_key_and_chain *ssl_sock_copy_cert_key_and_chain(struct cert_key_and_chain *src,
659 struct cert_key_and_chain *dst)
660{
William Lallemand6c096142021-02-23 14:45:45 +0100661 if (!src || !dst)
662 return NULL;
663
William Lallemand03c331c2020-05-13 10:10:01 +0200664 if (src->cert) {
665 dst->cert = src->cert;
666 X509_up_ref(src->cert);
667 }
668
669 if (src->key) {
670 dst->key = src->key;
671 EVP_PKEY_up_ref(src->key);
672 }
673
674 if (src->chain) {
675 dst->chain = X509_chain_up_ref(src->chain);
676 }
677
678 if (src->dh) {
679 DH_up_ref(src->dh);
680 dst->dh = src->dh;
681 }
682
683 if (src->sctl) {
684 struct buffer *sctl;
685
686 sctl = calloc(1, sizeof(*sctl));
687 if (!chunk_dup(sctl, src->sctl)) {
Willy Tarreau61cfdf42021-02-20 10:46:51 +0100688 ha_free(&sctl);
William Lallemand03c331c2020-05-13 10:10:01 +0200689 goto error;
690 }
691 dst->sctl = sctl;
692 }
693
694 if (src->ocsp_response) {
695 struct buffer *ocsp_response;
696
697 ocsp_response = calloc(1, sizeof(*ocsp_response));
698 if (!chunk_dup(ocsp_response, src->ocsp_response)) {
Willy Tarreau61cfdf42021-02-20 10:46:51 +0100699 ha_free(&ocsp_response);
William Lallemand03c331c2020-05-13 10:10:01 +0200700 goto error;
701 }
702 dst->ocsp_response = ocsp_response;
703 }
704
705 if (src->ocsp_issuer) {
706 X509_up_ref(src->ocsp_issuer);
707 dst->ocsp_issuer = src->ocsp_issuer;
708 }
709
710 return dst;
711
712error:
713
714 /* free everything */
715 ssl_sock_free_cert_key_and_chain_contents(dst);
716
717 return NULL;
718}
719
720/*
721 * return 0 on success or != 0 on failure
722 */
723int ssl_sock_load_issuer_file_into_ckch(const char *path, char *buf, struct cert_key_and_chain *ckch, char **err)
724{
725 int ret = 1;
726 BIO *in = NULL;
727 X509 *issuer;
728
729 if (buf) {
730 /* reading from a buffer */
731 in = BIO_new_mem_buf(buf, -1);
732 if (in == NULL) {
733 memprintf(err, "%sCan't allocate memory\n", err && *err ? *err : "");
734 goto end;
735 }
736
737 } else {
738 /* reading from a file */
739 in = BIO_new(BIO_s_file());
740 if (in == NULL)
741 goto end;
742
743 if (BIO_read_filename(in, path) <= 0)
744 goto end;
745 }
746
747 issuer = PEM_read_bio_X509_AUX(in, NULL, NULL, NULL);
748 if (!issuer) {
749 memprintf(err, "%s'%s' cannot be read or parsed'.\n",
750 err && *err ? *err : "", path);
751 goto end;
752 }
753 /* no error, fill ckch with new context, old context must be free */
754 if (ckch->ocsp_issuer)
755 X509_free(ckch->ocsp_issuer);
756 ckch->ocsp_issuer = issuer;
757 ret = 0;
758
759end:
760
761 ERR_clear_error();
762 if (in)
763 BIO_free(in);
764
765 return ret;
766}
767
768/******************** ckch_store functions ***********************************
769 * The ckch_store is a structure used to cache and index the SSL files used in
770 * configuration
771 */
772
773/*
774 * Free a ckch_store, its ckch, its instances and remove it from the ebtree
775 */
776void ckch_store_free(struct ckch_store *store)
777{
778 struct ckch_inst *inst, *inst_s;
779
780 if (!store)
781 return;
782
William Lallemandbd8e6ed2020-09-16 16:08:08 +0200783 ssl_sock_free_cert_key_and_chain_contents(store->ckch);
William Lallemand03c331c2020-05-13 10:10:01 +0200784
Willy Tarreau61cfdf42021-02-20 10:46:51 +0100785 ha_free(&store->ckch);
William Lallemand03c331c2020-05-13 10:10:01 +0200786
787 list_for_each_entry_safe(inst, inst_s, &store->ckch_inst, by_ckchs) {
788 ckch_inst_free(inst);
789 }
790 ebmb_delete(&store->node);
791 free(store);
792}
793
794/*
795 * create and initialize a ckch_store
796 * <path> is the key name
797 * <nmemb> is the number of store->ckch objects to allocate
798 *
799 * Return a ckch_store or NULL upon failure.
800 */
William Lallemandbd8e6ed2020-09-16 16:08:08 +0200801struct ckch_store *ckch_store_new(const char *filename)
William Lallemand03c331c2020-05-13 10:10:01 +0200802{
803 struct ckch_store *store;
804 int pathlen;
805
806 pathlen = strlen(filename);
807 store = calloc(1, sizeof(*store) + pathlen + 1);
808 if (!store)
809 return NULL;
810
William Lallemand03c331c2020-05-13 10:10:01 +0200811 memcpy(store->path, filename, pathlen + 1);
812
813 LIST_INIT(&store->ckch_inst);
814 LIST_INIT(&store->crtlist_entry);
815
William Lallemandbd8e6ed2020-09-16 16:08:08 +0200816 store->ckch = calloc(1, sizeof(*store->ckch));
William Lallemand03c331c2020-05-13 10:10:01 +0200817 if (!store->ckch)
818 goto error;
819
820 return store;
821error:
822 ckch_store_free(store);
823 return NULL;
824}
825
826/* allocate and duplicate a ckch_store
827 * Return a new ckch_store or NULL */
828struct ckch_store *ckchs_dup(const struct ckch_store *src)
829{
830 struct ckch_store *dst;
831
William Lallemand6c096142021-02-23 14:45:45 +0100832 if (!src)
833 return NULL;
834
William Lallemandbd8e6ed2020-09-16 16:08:08 +0200835 dst = ckch_store_new(src->path);
Eric Salama6ac61e32021-02-23 16:50:57 +0100836 if (!dst)
837 return NULL;
William Lallemand03c331c2020-05-13 10:10:01 +0200838
William Lallemandbd8e6ed2020-09-16 16:08:08 +0200839 if (!ssl_sock_copy_cert_key_and_chain(src->ckch, dst->ckch))
840 goto error;
William Lallemand03c331c2020-05-13 10:10:01 +0200841
842 return dst;
843
844error:
845 ckch_store_free(dst);
846
847 return NULL;
848}
849
850/*
851 * lookup a path into the ckchs tree.
852 */
853struct ckch_store *ckchs_lookup(char *path)
854{
855 struct ebmb_node *eb;
856
857 eb = ebst_lookup(&ckchs_tree, path);
858 if (!eb)
859 return NULL;
860
861 return ebmb_entry(eb, struct ckch_store, node);
862}
863
864/*
865 * This function allocate a ckch_store and populate it with certificates from files.
866 */
William Lallemandbd8e6ed2020-09-16 16:08:08 +0200867struct ckch_store *ckchs_load_cert_file(char *path, char **err)
William Lallemand03c331c2020-05-13 10:10:01 +0200868{
869 struct ckch_store *ckchs;
870
William Lallemandbd8e6ed2020-09-16 16:08:08 +0200871 ckchs = ckch_store_new(path);
William Lallemand03c331c2020-05-13 10:10:01 +0200872 if (!ckchs) {
873 memprintf(err, "%sunable to allocate memory.\n", err && *err ? *err : "");
874 goto end;
875 }
William Lallemand03c331c2020-05-13 10:10:01 +0200876
William Lallemandbd8e6ed2020-09-16 16:08:08 +0200877 if (ssl_sock_load_files_into_ckch(path, ckchs->ckch, err) == 1)
878 goto end;
William Lallemand03c331c2020-05-13 10:10:01 +0200879
William Lallemandbd8e6ed2020-09-16 16:08:08 +0200880 /* insert into the ckchs tree */
881 memcpy(ckchs->path, path, strlen(path) + 1);
882 ebst_insert(&ckchs_tree, &ckchs->node);
William Lallemand03c331c2020-05-13 10:10:01 +0200883 return ckchs;
884
885end:
886 ckch_store_free(ckchs);
887
888 return NULL;
889}
890
William Lallemandfa1d8b42020-05-13 15:46:10 +0200891
892/******************** ckch_inst functions ******************************/
893
894/* unlink a ckch_inst, free all SNIs, free the ckch_inst */
895/* The caller must use the lock of the bind_conf if used with inserted SNIs */
896void ckch_inst_free(struct ckch_inst *inst)
897{
898 struct sni_ctx *sni, *sni_s;
Remi Tricot-Le Breton4458b972021-02-19 17:41:55 +0100899 struct ckch_inst_link_ref *link_ref, *link_ref_s;
William Lallemandfa1d8b42020-05-13 15:46:10 +0200900
901 if (inst == NULL)
902 return;
903
904 list_for_each_entry_safe(sni, sni_s, &inst->sni_ctx, by_ckch_inst) {
905 SSL_CTX_free(sni->ctx);
Willy Tarreau2b718102021-04-21 07:32:39 +0200906 LIST_DELETE(&sni->by_ckch_inst);
William Lallemandfa1d8b42020-05-13 15:46:10 +0200907 ebmb_delete(&sni->name);
908 free(sni);
909 }
Remi Tricot-Le Bretonf3eedfe2021-01-25 17:19:44 +0100910 SSL_CTX_free(inst->ctx);
911 inst->ctx = NULL;
Willy Tarreau2b718102021-04-21 07:32:39 +0200912 LIST_DELETE(&inst->by_ckchs);
913 LIST_DELETE(&inst->by_crtlist_entry);
Remi Tricot-Le Breton4458b972021-02-19 17:41:55 +0100914
915 list_for_each_entry_safe(link_ref, link_ref_s, &inst->cafile_link_refs, list) {
916 LIST_DELETE(&link_ref->link->list);
917 LIST_DELETE(&link_ref->list);
918 free(link_ref);
919 }
920
William Lallemandfa1d8b42020-05-13 15:46:10 +0200921 free(inst);
922}
923
924/* Alloc and init a ckch_inst */
925struct ckch_inst *ckch_inst_new()
926{
927 struct ckch_inst *ckch_inst;
928
929 ckch_inst = calloc(1, sizeof *ckch_inst);
930 if (!ckch_inst)
931 return NULL;
932
933 LIST_INIT(&ckch_inst->sni_ctx);
934 LIST_INIT(&ckch_inst->by_ckchs);
935 LIST_INIT(&ckch_inst->by_crtlist_entry);
Remi Tricot-Le Breton4458b972021-02-19 17:41:55 +0100936 LIST_INIT(&ckch_inst->cafile_link_refs);
William Lallemandfa1d8b42020-05-13 15:46:10 +0200937
938 return ckch_inst;
939}
940
Remi Tricot-Le Bretonaf8820a2021-04-13 10:10:37 +0200941
942/******************** ssl_store functions ******************************/
Remi Tricot-Le Breton9f0c9362021-02-19 15:06:28 +0100943struct eb_root cafile_tree = EB_ROOT;
Remi Tricot-Le Bretonaf8820a2021-04-13 10:10:37 +0200944
Remi Tricot-Le Breton9f0c9362021-02-19 15:06:28 +0100945/*
946 * Returns the cafile_entry found in the cafile_tree indexed by the path 'path'.
947 * If 'oldest_entry' is 1, returns the "original" cafile_entry (since
948 * during a set cafile/commit cafile cycle there might be two entries for any
949 * given path, the original one and the new one set via the CLI but not
950 * committed yet).
951 */
Remi Tricot-Le Breton4458b972021-02-19 17:41:55 +0100952struct cafile_entry *ssl_store_get_cafile_entry(char *path, int oldest_entry)
Remi Tricot-Le Bretonaf8820a2021-04-13 10:10:37 +0200953{
Remi Tricot-Le Breton9f0c9362021-02-19 15:06:28 +0100954 struct cafile_entry *ca_e = NULL;
Remi Tricot-Le Bretonaf8820a2021-04-13 10:10:37 +0200955 struct ebmb_node *eb;
956
957 eb = ebst_lookup(&cafile_tree, path);
Remi Tricot-Le Breton9f0c9362021-02-19 15:06:28 +0100958 while (eb) {
Remi Tricot-Le Bretonaf8820a2021-04-13 10:10:37 +0200959 ca_e = ebmb_entry(eb, struct cafile_entry, node);
Remi Tricot-Le Breton9f0c9362021-02-19 15:06:28 +0100960 /* The ebst_lookup in a tree that has duplicates returns the
961 * oldest entry first. If we want the latest entry, we need to
962 * iterate over all the duplicates until we find the last one
963 * (in our case there should never be more than two entries for
964 * any given path). */
965 if (oldest_entry)
966 return ca_e;
967 eb = ebmb_next_dup(eb);
Remi Tricot-Le Bretonaf8820a2021-04-13 10:10:37 +0200968 }
Remi Tricot-Le Breton9f0c9362021-02-19 15:06:28 +0100969 return ca_e;
970}
971
Remi Tricot-Le Breton38c999b2021-02-23 16:28:43 +0100972int ssl_store_add_uncommitted_cafile_entry(struct cafile_entry *entry)
973{
974 return (ebst_insert(&cafile_tree, &entry->node) != &entry->node);
975}
976
Remi Tricot-Le Breton9f0c9362021-02-19 15:06:28 +0100977X509_STORE* ssl_store_get0_locations_file(char *path)
978{
979 struct cafile_entry *ca_e = ssl_store_get_cafile_entry(path, 0);
980
981 if (ca_e)
982 return ca_e->ca_store;
983
Remi Tricot-Le Bretonaf8820a2021-04-13 10:10:37 +0200984 return NULL;
985}
986
Remi Tricot-Le Breton5daff3c2021-02-22 15:54:55 +0100987/* Create a cafile_entry object, without adding it to the cafile_tree. */
Remi Tricot-Le Breton0bb48242021-04-16 17:59:23 +0200988struct 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 +0100989{
990 struct cafile_entry *ca_e;
991 int pathlen;
992
993 pathlen = strlen(path);
994
995 ca_e = calloc(1, sizeof(*ca_e) + pathlen + 1);
996 if (ca_e) {
997 memcpy(ca_e->path, path, pathlen + 1);
998 ca_e->ca_store = store;
Remi Tricot-Le Breton0bb48242021-04-16 17:59:23 +0200999 ca_e->type = type;
Remi Tricot-Le Breton5daff3c2021-02-22 15:54:55 +01001000 LIST_INIT(&ca_e->ckch_inst_link);
1001 }
1002 return ca_e;
1003}
1004
1005/* Delete a cafile_entry. The caller is responsible from removing this entry
1006 * from the cafile_tree first if is was previously added into it. */
1007void ssl_store_delete_cafile_entry(struct cafile_entry *ca_e)
1008{
1009 struct ckch_inst_link *link, *link_s;
1010 if (!ca_e)
1011 return;
1012
1013 X509_STORE_free(ca_e->ca_store);
1014
1015 list_for_each_entry_safe(link, link_s, &ca_e->ckch_inst_link, list) {
1016 struct ckch_inst *inst = link->ckch_inst;
1017 struct ckch_inst_link_ref *link_ref, *link_ref_s;
1018 list_for_each_entry_safe(link_ref, link_ref_s, &inst->cafile_link_refs, list) {
1019 if (link_ref->link == link) {
1020 LIST_DELETE(&link_ref->list);
1021 free(link_ref);
1022 break;
1023 }
1024 }
1025 LIST_DELETE(&link->list);
1026 free(link);
1027 }
1028
1029 free(ca_e);
1030}
1031
Remi Tricot-Le Breton383fb142021-02-22 18:26:14 +01001032/*
1033 * Build a cafile_entry out of a buffer instead of out of a file.
1034 * This function is used when the "commit ssl ca-file" cli command is used.
1035 * It can parse CERTIFICATE sections as well as CRL ones.
1036 * Returns 0 in case of success, 1 otherwise.
1037 */
1038int ssl_store_load_ca_from_buf(struct cafile_entry *ca_e, char *cert_buf)
1039{
1040 int retval = 0;
1041
1042 if (!ca_e)
1043 return 1;
1044
1045 if (!ca_e->ca_store) {
1046 ca_e->ca_store = X509_STORE_new();
1047 if (ca_e->ca_store) {
1048 BIO *bio = BIO_new_mem_buf(cert_buf, strlen(cert_buf));
1049 if (bio) {
1050 X509_INFO *info;
1051 int i;
1052 STACK_OF(X509_INFO) *infos = PEM_X509_INFO_read_bio(bio, NULL, NULL, NULL);
1053 if (!infos)
1054 {
1055 BIO_free(bio);
1056 return 1;
1057 }
1058
1059 for (i = 0; i < sk_X509_INFO_num(infos) && !retval; i++) {
1060 info = sk_X509_INFO_value(infos, i);
1061 /* X509_STORE_add_cert and X509_STORE_add_crl return 1 on success */
1062 if (info->x509) {
1063 retval = !X509_STORE_add_cert(ca_e->ca_store, info->x509);
1064 }
1065 if (!retval && info->crl) {
1066 retval = !X509_STORE_add_crl(ca_e->ca_store, info->crl);
1067 }
1068 }
1069 retval = retval || (i != sk_X509_INFO_num(infos));
1070
1071 /* Cleanup */
1072 sk_X509_INFO_pop_free(infos, X509_INFO_free);
1073 BIO_free(bio);
1074 }
1075 }
1076 }
1077
1078 return retval;
1079}
1080
Remi Tricot-Le Breton0bb48242021-04-16 17:59:23 +02001081int ssl_store_load_locations_file(char *path, int create_if_none, enum cafile_type type)
Remi Tricot-Le Bretonaf8820a2021-04-13 10:10:37 +02001082{
1083 X509_STORE *store = ssl_store_get0_locations_file(path);
1084
1085 /* If this function is called by the CLI, we should not call the
1086 * X509_STORE_load_locations function because it performs forbidden disk
1087 * accesses. */
1088 if (!store && create_if_none) {
1089 struct cafile_entry *ca_e;
1090 store = X509_STORE_new();
1091 if (X509_STORE_load_locations(store, path, NULL)) {
Remi Tricot-Le Breton0bb48242021-04-16 17:59:23 +02001092 ca_e = ssl_store_create_cafile_entry(path, store, type);
Remi Tricot-Le Bretonaf8820a2021-04-13 10:10:37 +02001093 if (ca_e) {
Remi Tricot-Le Bretonaf8820a2021-04-13 10:10:37 +02001094 ebst_insert(&cafile_tree, &ca_e->node);
1095 }
1096 } else {
1097 X509_STORE_free(store);
1098 store = NULL;
1099 }
1100 }
1101 return (store != NULL);
1102}
1103
1104
William Lallemandda8584c2020-05-14 10:14:37 +02001105/*************************** CLI commands ***********************/
1106
1107/* Type of SSL payloads that can be updated over the CLI */
1108
1109enum {
1110 CERT_TYPE_PEM = 0,
1111 CERT_TYPE_KEY,
1112#if ((defined SSL_CTRL_SET_TLSEXT_STATUS_REQ_CB && !defined OPENSSL_NO_OCSP) || defined OPENSSL_IS_BORINGSSL)
1113 CERT_TYPE_OCSP,
1114#endif
1115 CERT_TYPE_ISSUER,
Ilya Shipitsinc47d6762021-02-13 11:45:33 +05001116#ifdef HAVE_SSL_SCTL
William Lallemandda8584c2020-05-14 10:14:37 +02001117 CERT_TYPE_SCTL,
1118#endif
1119 CERT_TYPE_MAX,
1120};
1121
1122struct {
1123 const char *ext;
1124 int type;
1125 int (*load)(const char *path, char *payload, struct cert_key_and_chain *ckch, char **err);
1126 /* add a parsing callback */
1127} cert_exts[CERT_TYPE_MAX+1] = {
1128 [CERT_TYPE_PEM] = { "", CERT_TYPE_PEM, &ssl_sock_load_pem_into_ckch }, /* default mode, no extensions */
1129 [CERT_TYPE_KEY] = { "key", CERT_TYPE_KEY, &ssl_sock_load_key_into_ckch },
1130#if ((defined SSL_CTRL_SET_TLSEXT_STATUS_REQ_CB && !defined OPENSSL_NO_OCSP) || defined OPENSSL_IS_BORINGSSL)
1131 [CERT_TYPE_OCSP] = { "ocsp", CERT_TYPE_OCSP, &ssl_sock_load_ocsp_response_from_file },
1132#endif
Ilya Shipitsinc47d6762021-02-13 11:45:33 +05001133#ifdef HAVE_SSL_SCTL
William Lallemandda8584c2020-05-14 10:14:37 +02001134 [CERT_TYPE_SCTL] = { "sctl", CERT_TYPE_SCTL, &ssl_sock_load_sctl_from_file },
1135#endif
1136 [CERT_TYPE_ISSUER] = { "issuer", CERT_TYPE_ISSUER, &ssl_sock_load_issuer_file_into_ckch },
1137 [CERT_TYPE_MAX] = { NULL, CERT_TYPE_MAX, NULL },
1138};
1139
1140
1141/* release function of the `show ssl cert' command */
1142static void cli_release_show_cert(struct appctx *appctx)
1143{
1144 HA_SPIN_UNLOCK(CKCH_LOCK, &ckch_lock);
1145}
1146
1147/* IO handler of "show ssl cert <filename>" */
1148static int cli_io_handler_show_cert(struct appctx *appctx)
1149{
1150 struct buffer *trash = alloc_trash_chunk();
1151 struct ebmb_node *node;
1152 struct stream_interface *si = appctx->owner;
1153 struct ckch_store *ckchs;
1154
1155 if (trash == NULL)
1156 return 1;
1157
1158 if (!appctx->ctx.ssl.old_ckchs) {
1159 if (ckchs_transaction.old_ckchs) {
1160 ckchs = ckchs_transaction.old_ckchs;
1161 chunk_appendf(trash, "# transaction\n");
William Lallemand5685ccf2020-09-16 16:12:25 +02001162 chunk_appendf(trash, "*%s\n", ckchs->path);
William Lallemandda8584c2020-05-14 10:14:37 +02001163 }
1164 }
1165
1166 if (!appctx->ctx.cli.p0) {
1167 chunk_appendf(trash, "# filename\n");
1168 node = ebmb_first(&ckchs_tree);
1169 } else {
1170 node = &((struct ckch_store *)appctx->ctx.cli.p0)->node;
1171 }
1172 while (node) {
1173 ckchs = ebmb_entry(node, struct ckch_store, node);
William Lallemand5685ccf2020-09-16 16:12:25 +02001174 chunk_appendf(trash, "%s\n", ckchs->path);
William Lallemandda8584c2020-05-14 10:14:37 +02001175
1176 node = ebmb_next(node);
1177 if (ci_putchk(si_ic(si), trash) == -1) {
1178 si_rx_room_blk(si);
1179 goto yield;
1180 }
1181 }
1182
1183 appctx->ctx.cli.p0 = NULL;
1184 free_trash_chunk(trash);
1185 return 1;
1186yield:
1187
1188 free_trash_chunk(trash);
1189 appctx->ctx.cli.p0 = ckchs;
1190 return 0; /* should come back */
1191}
1192
1193/*
1194 * Extract and format the DNS SAN extensions and copy result into a chuink
1195 * Return 0;
1196 */
1197#ifdef SSL_CTRL_SET_TLSEXT_HOSTNAME
1198static int ssl_sock_get_san_oneline(X509 *cert, struct buffer *out)
1199{
1200 int i;
1201 char *str;
1202 STACK_OF(GENERAL_NAME) *names = NULL;
1203
1204 names = X509_get_ext_d2i(cert, NID_subject_alt_name, NULL, NULL);
1205 if (names) {
1206 for (i = 0; i < sk_GENERAL_NAME_num(names); i++) {
1207 GENERAL_NAME *name = sk_GENERAL_NAME_value(names, i);
1208 if (i > 0)
1209 chunk_appendf(out, ", ");
1210 if (name->type == GEN_DNS) {
1211 if (ASN1_STRING_to_UTF8((unsigned char **)&str, name->d.dNSName) >= 0) {
1212 chunk_appendf(out, "DNS:%s", str);
1213 OPENSSL_free(str);
1214 }
1215 }
1216 }
1217 sk_GENERAL_NAME_pop_free(names, GENERAL_NAME_free);
1218 }
1219 return 0;
1220}
1221#endif
1222
Remi Tricot-Le Breton4458b972021-02-19 17:41:55 +01001223/*
1224 * Build the ckch_inst_link that will be chained in the CA file entry and the
1225 * corresponding ckch_inst_link_ref that will be chained in the ckch instance.
1226 * Return 0 in case of success.
1227 */
1228static int do_chain_inst_and_cafile(struct cafile_entry *cafile_entry, struct ckch_inst *ckch_inst)
1229{
1230 struct ckch_inst_link *new_link;
1231 if (!LIST_ISEMPTY(&cafile_entry->ckch_inst_link)) {
1232 struct ckch_inst_link *link = LIST_ELEM(cafile_entry->ckch_inst_link.n,
1233 typeof(link), list);
1234 /* Do not add multiple references to the same
1235 * instance in a cafile_entry */
1236 if (link->ckch_inst == ckch_inst) {
1237 return 1;
1238 }
1239 }
1240
1241 new_link = calloc(1, sizeof(*new_link));
1242 if (new_link) {
1243 struct ckch_inst_link_ref *new_link_ref = calloc(1, sizeof(*new_link_ref));
1244 if (!new_link_ref) {
1245 free(new_link);
1246 return 1;
1247 }
1248
1249 new_link->ckch_inst = ckch_inst;
1250 new_link_ref->link = new_link;
1251 LIST_INIT(&new_link->list);
1252 LIST_INIT(&new_link_ref->list);
1253
1254 LIST_APPEND(&cafile_entry->ckch_inst_link, &new_link->list);
1255 LIST_APPEND(&ckch_inst->cafile_link_refs, &new_link_ref->list);
1256 }
1257
1258 return 0;
1259}
1260
1261
1262/*
1263 * Link a CA file tree entry to the ckch instance that uses it.
1264 * To determine if and which CA file tree entries need to be linked to the
1265 * instance, we follow the same logic performed in ssl_sock_prepare_ctx when
1266 * processing the verify option.
1267 * This function works for a frontend as well as for a backend, depending on the
1268 * configuration parameters given (bind_conf or server).
1269 */
1270void ckch_inst_add_cafile_link(struct ckch_inst *ckch_inst, struct bind_conf *bind_conf,
1271 struct ssl_bind_conf *ssl_conf, const struct server *srv)
1272{
1273 int verify = SSL_VERIFY_NONE;
1274
1275 if (srv) {
1276
1277 if (global.ssl_server_verify == SSL_SERVER_VERIFY_REQUIRED)
1278 verify = SSL_VERIFY_PEER;
1279 switch (srv->ssl_ctx.verify) {
1280 case SSL_SOCK_VERIFY_NONE:
1281 verify = SSL_VERIFY_NONE;
1282 break;
1283 case SSL_SOCK_VERIFY_REQUIRED:
1284 verify = SSL_VERIFY_PEER;
1285 break;
1286 }
1287 }
1288 else {
1289 switch ((ssl_conf && ssl_conf->verify) ? ssl_conf->verify : bind_conf->ssl_conf.verify) {
1290 case SSL_SOCK_VERIFY_NONE:
1291 verify = SSL_VERIFY_NONE;
1292 break;
1293 case SSL_SOCK_VERIFY_OPTIONAL:
1294 verify = SSL_VERIFY_PEER;
1295 break;
1296 case SSL_SOCK_VERIFY_REQUIRED:
1297 verify = SSL_VERIFY_PEER|SSL_VERIFY_FAIL_IF_NO_PEER_CERT;
1298 break;
1299 }
1300 }
1301
1302 if (verify & SSL_VERIFY_PEER) {
1303 struct cafile_entry *ca_file_entry = NULL;
1304 struct cafile_entry *ca_verify_file_entry = NULL;
1305 if (srv) {
1306 if (srv->ssl_ctx.ca_file) {
1307 ca_file_entry = ssl_store_get_cafile_entry(srv->ssl_ctx.ca_file, 0);
1308
1309 }
1310 }
1311 else {
1312 char *ca_file = (ssl_conf && ssl_conf->ca_file) ? ssl_conf->ca_file : bind_conf->ssl_conf.ca_file;
1313 char *ca_verify_file = (ssl_conf && ssl_conf->ca_verify_file) ? ssl_conf->ca_verify_file : bind_conf->ssl_conf.ca_verify_file;
1314
1315 if (ca_file)
1316 ca_file_entry = ssl_store_get_cafile_entry(ca_file, 0);
1317 if (ca_verify_file)
1318 ca_verify_file_entry = ssl_store_get_cafile_entry(ca_verify_file, 0);
1319 }
1320
1321 if (ca_file_entry) {
1322 /* If we have a ckch instance that is not already in the
1323 * cafile_entry's list, add it to it. */
1324 if (do_chain_inst_and_cafile(ca_file_entry, ckch_inst))
1325 return;
1326
1327 }
1328 if (ca_verify_file_entry && (ca_file_entry != ca_verify_file_entry)) {
1329 /* If we have a ckch instance that is not already in the
1330 * cafile_entry's list, add it to it. */
1331 if (do_chain_inst_and_cafile(ca_verify_file_entry, ckch_inst))
1332 return;
1333 }
1334 }
1335}
1336
William Lallemandda8584c2020-05-14 10:14:37 +02001337
1338
Remi Tricot-Le Breton523f0e42021-03-16 10:11:44 +01001339static int show_cert_detail(X509 *cert, STACK_OF(X509) *chain, struct buffer *out)
William Lallemandda8584c2020-05-14 10:14:37 +02001340{
William Lallemandda8584c2020-05-14 10:14:37 +02001341 BIO *bio = NULL;
Remi Tricot-Le Breton523f0e42021-03-16 10:11:44 +01001342 struct buffer *tmp = alloc_trash_chunk();
William Lallemandda8584c2020-05-14 10:14:37 +02001343 int i;
Remi Tricot-Le Breton523f0e42021-03-16 10:11:44 +01001344 int write = -1;
1345 unsigned int len = 0;
1346 X509_NAME *name = NULL;
William Lallemandda8584c2020-05-14 10:14:37 +02001347
Remi Tricot-Le Breton523f0e42021-03-16 10:11:44 +01001348 if (!tmp)
1349 return -1;
William Lallemandda8584c2020-05-14 10:14:37 +02001350
Remi Tricot-Le Breton523f0e42021-03-16 10:11:44 +01001351 if (!cert)
William Lallemand5685ccf2020-09-16 16:12:25 +02001352 goto end;
William Lallemandda8584c2020-05-14 10:14:37 +02001353
William Lallemand5685ccf2020-09-16 16:12:25 +02001354 if (chain == NULL) {
1355 struct issuer_chain *issuer;
Remi Tricot-Le Breton523f0e42021-03-16 10:11:44 +01001356 issuer = ssl_get0_issuer_chain(cert);
William Lallemand5685ccf2020-09-16 16:12:25 +02001357 if (issuer) {
1358 chain = issuer->chain;
1359 chunk_appendf(out, "Chain Filename: ");
1360 chunk_appendf(out, "%s\n", issuer->path);
William Lallemandda8584c2020-05-14 10:14:37 +02001361 }
William Lallemand5685ccf2020-09-16 16:12:25 +02001362 }
1363 chunk_appendf(out, "Serial: ");
Remi Tricot-Le Breton523f0e42021-03-16 10:11:44 +01001364 if (ssl_sock_get_serial(cert, tmp) == -1)
William Lallemand5685ccf2020-09-16 16:12:25 +02001365 goto end;
1366 dump_binary(out, tmp->area, tmp->data);
1367 chunk_appendf(out, "\n");
William Lallemandda8584c2020-05-14 10:14:37 +02001368
William Lallemand5685ccf2020-09-16 16:12:25 +02001369 chunk_appendf(out, "notBefore: ");
1370 chunk_reset(tmp);
1371 if ((bio = BIO_new(BIO_s_mem())) == NULL)
1372 goto end;
Remi Tricot-Le Breton523f0e42021-03-16 10:11:44 +01001373 if (ASN1_TIME_print(bio, X509_getm_notBefore(cert)) == 0)
William Lallemand5685ccf2020-09-16 16:12:25 +02001374 goto end;
1375 write = BIO_read(bio, tmp->area, tmp->size-1);
1376 tmp->area[write] = '\0';
1377 BIO_free(bio);
1378 bio = NULL;
1379 chunk_appendf(out, "%s\n", tmp->area);
William Lallemandda8584c2020-05-14 10:14:37 +02001380
William Lallemand5685ccf2020-09-16 16:12:25 +02001381 chunk_appendf(out, "notAfter: ");
1382 chunk_reset(tmp);
1383 if ((bio = BIO_new(BIO_s_mem())) == NULL)
1384 goto end;
Remi Tricot-Le Breton523f0e42021-03-16 10:11:44 +01001385 if (ASN1_TIME_print(bio, X509_getm_notAfter(cert)) == 0)
William Lallemand5685ccf2020-09-16 16:12:25 +02001386 goto end;
1387 if ((write = BIO_read(bio, tmp->area, tmp->size-1)) <= 0)
1388 goto end;
1389 tmp->area[write] = '\0';
1390 BIO_free(bio);
1391 bio = NULL;
1392 chunk_appendf(out, "%s\n", tmp->area);
William Lallemandda8584c2020-05-14 10:14:37 +02001393
1394#ifdef SSL_CTRL_SET_TLSEXT_HOSTNAME
William Lallemand5685ccf2020-09-16 16:12:25 +02001395 chunk_appendf(out, "Subject Alternative Name: ");
Remi Tricot-Le Breton523f0e42021-03-16 10:11:44 +01001396 if (ssl_sock_get_san_oneline(cert, out) == -1)
William Lallemand5685ccf2020-09-16 16:12:25 +02001397 goto end;
1398 *(out->area + out->data) = '\0';
1399 chunk_appendf(out, "\n");
William Lallemandda8584c2020-05-14 10:14:37 +02001400#endif
William Lallemand5685ccf2020-09-16 16:12:25 +02001401 chunk_reset(tmp);
1402 chunk_appendf(out, "Algorithm: ");
Remi Tricot-Le Breton523f0e42021-03-16 10:11:44 +01001403 if (cert_get_pkey_algo(cert, tmp) == 0)
William Lallemand5685ccf2020-09-16 16:12:25 +02001404 goto end;
1405 chunk_appendf(out, "%s\n", tmp->area);
William Lallemandda8584c2020-05-14 10:14:37 +02001406
William Lallemand5685ccf2020-09-16 16:12:25 +02001407 chunk_reset(tmp);
1408 chunk_appendf(out, "SHA1 FingerPrint: ");
Remi Tricot-Le Breton523f0e42021-03-16 10:11:44 +01001409 if (X509_digest(cert, EVP_sha1(), (unsigned char *) tmp->area, &len) == 0)
William Lallemand5685ccf2020-09-16 16:12:25 +02001410 goto end;
1411 tmp->data = len;
1412 dump_binary(out, tmp->area, tmp->data);
1413 chunk_appendf(out, "\n");
William Lallemandda8584c2020-05-14 10:14:37 +02001414
William Lallemand5685ccf2020-09-16 16:12:25 +02001415 chunk_appendf(out, "Subject: ");
Remi Tricot-Le Breton523f0e42021-03-16 10:11:44 +01001416 if ((name = X509_get_subject_name(cert)) == NULL)
William Lallemand5685ccf2020-09-16 16:12:25 +02001417 goto end;
1418 if ((ssl_sock_get_dn_oneline(name, tmp)) == -1)
1419 goto end;
1420 *(tmp->area + tmp->data) = '\0';
1421 chunk_appendf(out, "%s\n", tmp->area);
1422
1423 chunk_appendf(out, "Issuer: ");
Remi Tricot-Le Breton523f0e42021-03-16 10:11:44 +01001424 if ((name = X509_get_issuer_name(cert)) == NULL)
William Lallemand5685ccf2020-09-16 16:12:25 +02001425 goto end;
1426 if ((ssl_sock_get_dn_oneline(name, tmp)) == -1)
1427 goto end;
1428 *(tmp->area + tmp->data) = '\0';
1429 chunk_appendf(out, "%s\n", tmp->area);
1430
1431 /* Displays subject of each certificate in the chain */
1432 for (i = 0; i < sk_X509_num(chain); i++) {
1433 X509 *ca = sk_X509_value(chain, i);
1434
1435 chunk_appendf(out, "Chain Subject: ");
1436 if ((name = X509_get_subject_name(ca)) == NULL)
William Lallemandda8584c2020-05-14 10:14:37 +02001437 goto end;
1438 if ((ssl_sock_get_dn_oneline(name, tmp)) == -1)
1439 goto end;
1440 *(tmp->area + tmp->data) = '\0';
1441 chunk_appendf(out, "%s\n", tmp->area);
1442
William Lallemand5685ccf2020-09-16 16:12:25 +02001443 chunk_appendf(out, "Chain Issuer: ");
1444 if ((name = X509_get_issuer_name(ca)) == NULL)
William Lallemandda8584c2020-05-14 10:14:37 +02001445 goto end;
1446 if ((ssl_sock_get_dn_oneline(name, tmp)) == -1)
1447 goto end;
1448 *(tmp->area + tmp->data) = '\0';
1449 chunk_appendf(out, "%s\n", tmp->area);
William Lallemandda8584c2020-05-14 10:14:37 +02001450 }
1451
1452end:
Remi Tricot-Le Breton523f0e42021-03-16 10:11:44 +01001453 if (bio)
1454 BIO_free(bio);
1455 free_trash_chunk(tmp);
1456
1457 return 0;
1458}
1459
1460
1461/* IO handler of the details "show ssl cert <filename>" */
1462static int cli_io_handler_show_cert_detail(struct appctx *appctx)
1463{
1464 struct stream_interface *si = appctx->owner;
1465 struct ckch_store *ckchs = appctx->ctx.cli.p0;
1466 struct buffer *out = alloc_trash_chunk();
1467 int retval = 0;
1468
1469 if (!out)
1470 goto end_no_putchk;
1471
1472 chunk_appendf(out, "Filename: ");
1473 if (ckchs == ckchs_transaction.new_ckchs)
1474 chunk_appendf(out, "*");
1475 chunk_appendf(out, "%s\n", ckchs->path);
1476
1477 chunk_appendf(out, "Status: ");
1478 if (ckchs->ckch->cert == NULL)
1479 chunk_appendf(out, "Empty\n");
1480 else if (LIST_ISEMPTY(&ckchs->ckch_inst))
1481 chunk_appendf(out, "Unused\n");
1482 else
1483 chunk_appendf(out, "Used\n");
1484
1485 retval = show_cert_detail(ckchs->ckch->cert, ckchs->ckch->chain, out);
1486 if (retval < 0)
1487 goto end_no_putchk;
1488 else if (retval)
1489 goto end;
1490
1491end:
William Lallemandda8584c2020-05-14 10:14:37 +02001492 if (ci_putchk(si_ic(si), out) == -1) {
1493 si_rx_room_blk(si);
1494 goto yield;
1495 }
1496
1497end_no_putchk:
William Lallemandda8584c2020-05-14 10:14:37 +02001498 free_trash_chunk(out);
1499 return 1;
1500yield:
William Lallemandda8584c2020-05-14 10:14:37 +02001501 free_trash_chunk(out);
1502 return 0; /* should come back */
1503}
1504
1505/* parsing function for 'show ssl cert [certfile]' */
1506static int cli_parse_show_cert(char **args, char *payload, struct appctx *appctx, void *private)
1507{
1508 struct ckch_store *ckchs;
1509
1510 if (!cli_has_level(appctx, ACCESS_LVL_OPER))
1511 return cli_err(appctx, "Can't allocate memory!\n");
1512
1513 /* The operations on the CKCH architecture are locked so we can
1514 * manipulate ckch_store and ckch_inst */
1515 if (HA_SPIN_TRYLOCK(CKCH_LOCK, &ckch_lock))
1516 return cli_err(appctx, "Can't show!\nOperations on certificates are currently locked!\n");
1517
1518 /* check if there is a certificate to lookup */
1519 if (*args[3]) {
1520 if (*args[3] == '*') {
1521 if (!ckchs_transaction.new_ckchs)
1522 goto error;
1523
1524 ckchs = ckchs_transaction.new_ckchs;
1525
Tim Duesterhuse5ff1412021-01-02 22:31:53 +01001526 if (strcmp(args[3] + 1, ckchs->path) != 0)
William Lallemandda8584c2020-05-14 10:14:37 +02001527 goto error;
1528
1529 } else {
1530 if ((ckchs = ckchs_lookup(args[3])) == NULL)
1531 goto error;
1532
1533 }
1534
William Lallemandda8584c2020-05-14 10:14:37 +02001535 appctx->ctx.cli.p0 = ckchs;
1536 /* use the IO handler that shows details */
1537 appctx->io_handler = cli_io_handler_show_cert_detail;
1538 }
1539
1540 return 0;
1541
1542error:
1543 HA_SPIN_UNLOCK(CKCH_LOCK, &ckch_lock);
1544 return cli_err(appctx, "Can't display the certificate: Not found or the certificate is a bundle!\n");
1545}
1546
1547/* release function of the `set ssl cert' command, free things and unlock the spinlock */
1548static void cli_release_commit_cert(struct appctx *appctx)
1549{
1550 struct ckch_store *new_ckchs;
1551
1552 HA_SPIN_UNLOCK(CKCH_LOCK, &ckch_lock);
1553
1554 if (appctx->st2 != SETCERT_ST_FIN) {
1555 /* free every new sni_ctx and the new store, which are not in the trees so no spinlock there */
1556 new_ckchs = appctx->ctx.ssl.new_ckchs;
1557
1558 /* if the allocation failed, we need to free everything from the temporary list */
1559 ckch_store_free(new_ckchs);
1560 }
1561}
1562
Remi Tricot-Le Bretonbfadc022021-02-24 12:20:48 +01001563
1564/*
1565 * Rebuild a new instance 'new_inst' based on an old instance 'ckchi' and a
1566 * specific ckch_store.
1567 * Returns 0 in case of success, 1 otherwise.
1568 */
1569static int ckch_inst_rebuild(struct ckch_store *ckch_store, struct ckch_inst *ckchi,
1570 struct ckch_inst **new_inst, char **err)
1571{
1572 int retval = 0;
1573 int errcode = 0;
1574 struct sni_ctx *sc0, *sc0s;
1575 char **sni_filter = NULL;
1576 int fcount = 0;
1577
1578 if (ckchi->crtlist_entry) {
1579 sni_filter = ckchi->crtlist_entry->filters;
1580 fcount = ckchi->crtlist_entry->fcount;
1581 }
1582
1583 if (ckchi->is_server_instance)
1584 errcode |= ckch_inst_new_load_srv_store(ckch_store->path, ckch_store, new_inst, err);
1585 else
1586 errcode |= ckch_inst_new_load_store(ckch_store->path, ckch_store, ckchi->bind_conf, ckchi->ssl_conf, sni_filter, fcount, new_inst, err);
1587
1588 if (errcode & ERR_CODE)
1589 return 1;
1590
1591 /* if the previous ckchi was used as the default */
1592 if (ckchi->is_default)
1593 (*new_inst)->is_default = 1;
1594
1595 (*new_inst)->is_server_instance = ckchi->is_server_instance;
1596 (*new_inst)->server = ckchi->server;
1597 /* Create a new SSL_CTX and link it to the new instance. */
1598 if ((*new_inst)->is_server_instance) {
1599 retval = ssl_sock_prep_srv_ctx_and_inst(ckchi->server, (*new_inst)->ctx, (*new_inst));
1600 if (retval)
1601 return 1;
1602 }
1603
1604 /* create the link to the crtlist_entry */
1605 (*new_inst)->crtlist_entry = ckchi->crtlist_entry;
1606
1607 /* we need to initialize the SSL_CTX generated */
1608 /* this iterate on the newly generated SNIs in the new instance to prepare their SSL_CTX */
1609 list_for_each_entry_safe(sc0, sc0s, &(*new_inst)->sni_ctx, by_ckch_inst) {
1610 if (!sc0->order) { /* we initialized only the first SSL_CTX because it's the same in the other sni_ctx's */
1611 errcode |= ssl_sock_prep_ctx_and_inst(ckchi->bind_conf, ckchi->ssl_conf, sc0->ctx, *new_inst, err);
1612 if (errcode & ERR_CODE)
1613 return 1;
1614 }
1615 }
1616
1617 return 0;
1618}
1619
1620/*
1621 * Load all the new SNIs of a newly built ckch instance in the trees, or replace
1622 * a server's main ckch instance.
1623 */
1624static void __ssl_sock_load_new_ckch_instance(struct ckch_inst *ckchi)
1625{
1626 /* The bind_conf will be null on server ckch_instances. */
1627 if (ckchi->is_server_instance) {
1628 int i;
1629 /* a lock is needed here since we have to free the SSL cache */
1630 HA_RWLOCK_WRLOCK(SSL_SERVER_LOCK, &ckchi->server->ssl_ctx.lock);
1631 /* free the server current SSL_CTX */
1632 SSL_CTX_free(ckchi->server->ssl_ctx.ctx);
1633 /* Actual ssl context update */
1634 SSL_CTX_up_ref(ckchi->ctx);
1635 ckchi->server->ssl_ctx.ctx = ckchi->ctx;
1636 ckchi->server->ssl_ctx.inst = ckchi;
1637
1638 /* flush the session cache of the server */
1639 for (i = 0; i < global.nbthread; i++) {
1640 ha_free(&ckchi->server->ssl_ctx.reused_sess[i].ptr);
1641 }
1642 HA_RWLOCK_WRUNLOCK(SSL_SERVER_LOCK, &ckchi->server->ssl_ctx.lock);
1643
1644 } else {
1645 HA_RWLOCK_WRLOCK(SNI_LOCK, &ckchi->bind_conf->sni_lock);
1646 ssl_sock_load_cert_sni(ckchi, ckchi->bind_conf);
1647 HA_RWLOCK_WRUNLOCK(SNI_LOCK, &ckchi->bind_conf->sni_lock);
1648 }
1649}
1650
1651/*
1652 * Delete a ckch instance that was replaced after a CLI command.
1653 */
1654static void __ckch_inst_free_locked(struct ckch_inst *ckchi)
1655{
1656 if (ckchi->is_server_instance) {
1657 /* no lock for servers */
1658 ckch_inst_free(ckchi);
1659 } else {
1660 struct bind_conf __maybe_unused *bind_conf = ckchi->bind_conf;
1661
1662 HA_RWLOCK_WRLOCK(SNI_LOCK, &bind_conf->sni_lock);
1663 ckch_inst_free(ckchi);
1664 HA_RWLOCK_WRUNLOCK(SNI_LOCK, &bind_conf->sni_lock);
1665 }
1666}
1667
1668
William Lallemandda8584c2020-05-14 10:14:37 +02001669/*
1670 * This function tries to create the new ckch_inst and their SNIs
1671 */
1672static int cli_io_handler_commit_cert(struct appctx *appctx)
1673{
1674 struct stream_interface *si = appctx->owner;
1675 int y = 0;
1676 char *err = NULL;
1677 int errcode = 0;
1678 struct ckch_store *old_ckchs, *new_ckchs = NULL;
1679 struct ckch_inst *ckchi, *ckchis;
1680 struct buffer *trash = alloc_trash_chunk();
William Lallemandda8584c2020-05-14 10:14:37 +02001681 struct crtlist_entry *entry;
1682
1683 if (trash == NULL)
1684 goto error;
1685
1686 if (unlikely(si_ic(si)->flags & (CF_WRITE_ERROR|CF_SHUTW)))
1687 goto error;
1688
1689 while (1) {
1690 switch (appctx->st2) {
1691 case SETCERT_ST_INIT:
1692 /* This state just print the update message */
1693 chunk_printf(trash, "Committing %s", ckchs_transaction.path);
1694 if (ci_putchk(si_ic(si), trash) == -1) {
1695 si_rx_room_blk(si);
1696 goto yield;
1697 }
1698 appctx->st2 = SETCERT_ST_GEN;
1699 /* fallthrough */
1700 case SETCERT_ST_GEN:
1701 /*
1702 * This state generates the ckch instances with their
1703 * sni_ctxs and SSL_CTX.
1704 *
1705 * Since the SSL_CTX generation can be CPU consumer, we
1706 * yield every 10 instances.
1707 */
1708
1709 old_ckchs = appctx->ctx.ssl.old_ckchs;
1710 new_ckchs = appctx->ctx.ssl.new_ckchs;
1711
1712 if (!new_ckchs)
1713 continue;
1714
1715 /* get the next ckchi to regenerate */
1716 ckchi = appctx->ctx.ssl.next_ckchi;
1717 /* we didn't start yet, set it to the first elem */
1718 if (ckchi == NULL)
1719 ckchi = LIST_ELEM(old_ckchs->ckch_inst.n, typeof(ckchi), by_ckchs);
1720
1721 /* walk through the old ckch_inst and creates new ckch_inst using the updated ckchs */
1722 list_for_each_entry_from(ckchi, &old_ckchs->ckch_inst, by_ckchs) {
1723 struct ckch_inst *new_inst;
William Lallemandda8584c2020-05-14 10:14:37 +02001724
1725 /* it takes a lot of CPU to creates SSL_CTXs, so we yield every 10 CKCH instances */
1726 if (y >= 10) {
1727 /* save the next ckchi to compute */
1728 appctx->ctx.ssl.next_ckchi = ckchi;
1729 goto yield;
1730 }
1731
Remi Tricot-Le Bretonbfadc022021-02-24 12:20:48 +01001732 if (ckch_inst_rebuild(new_ckchs, ckchi, &new_inst, &err))
William Lallemandda8584c2020-05-14 10:14:37 +02001733 goto error;
1734
William Lallemandda8584c2020-05-14 10:14:37 +02001735 /* display one dot per new instance */
1736 chunk_appendf(trash, ".");
1737 /* link the new ckch_inst to the duplicate */
Willy Tarreau2b718102021-04-21 07:32:39 +02001738 LIST_APPEND(&new_ckchs->ckch_inst, &new_inst->by_ckchs);
William Lallemandda8584c2020-05-14 10:14:37 +02001739 y++;
1740 }
1741 appctx->st2 = SETCERT_ST_INSERT;
1742 /* fallthrough */
1743 case SETCERT_ST_INSERT:
1744 /* The generation is finished, we can insert everything */
1745
1746 old_ckchs = appctx->ctx.ssl.old_ckchs;
1747 new_ckchs = appctx->ctx.ssl.new_ckchs;
1748
1749 if (!new_ckchs)
1750 continue;
1751
1752 /* get the list of crtlist_entry in the old store, and update the pointers to the store */
1753 LIST_SPLICE(&new_ckchs->crtlist_entry, &old_ckchs->crtlist_entry);
1754 list_for_each_entry(entry, &new_ckchs->crtlist_entry, by_ckch_store) {
1755 ebpt_delete(&entry->node);
1756 /* change the ptr and reinsert the node */
1757 entry->node.key = new_ckchs;
1758 ebpt_insert(&entry->crtlist->entries, &entry->node);
1759 }
1760
William Lallemanda55685b2020-12-15 14:57:46 +01001761 /* insert the new ckch_insts in the crtlist_entry */
1762 list_for_each_entry(ckchi, &new_ckchs->ckch_inst, by_ckchs) {
1763 if (ckchi->crtlist_entry)
Willy Tarreau2b718102021-04-21 07:32:39 +02001764 LIST_INSERT(&ckchi->crtlist_entry->ckch_inst, &ckchi->by_crtlist_entry);
William Lallemanda55685b2020-12-15 14:57:46 +01001765 }
1766
William Lallemandda8584c2020-05-14 10:14:37 +02001767 /* First, we insert every new SNIs in the trees, also replace the default_ctx */
1768 list_for_each_entry_safe(ckchi, ckchis, &new_ckchs->ckch_inst, by_ckchs) {
Remi Tricot-Le Bretonbfadc022021-02-24 12:20:48 +01001769 __ssl_sock_load_new_ckch_instance(ckchi);
William Lallemandda8584c2020-05-14 10:14:37 +02001770 }
1771
1772 /* delete the old sni_ctx, the old ckch_insts and the ckch_store */
1773 list_for_each_entry_safe(ckchi, ckchis, &old_ckchs->ckch_inst, by_ckchs) {
Remi Tricot-Le Bretonbfadc022021-02-24 12:20:48 +01001774 __ckch_inst_free_locked(ckchi);
William Lallemandda8584c2020-05-14 10:14:37 +02001775 }
1776
1777 /* Replace the old ckchs by the new one */
1778 ckch_store_free(old_ckchs);
1779 ebst_insert(&ckchs_tree, &new_ckchs->node);
1780 appctx->st2 = SETCERT_ST_FIN;
1781 /* fallthrough */
1782 case SETCERT_ST_FIN:
1783 /* we achieved the transaction, we can set everything to NULL */
Willy Tarreau61cfdf42021-02-20 10:46:51 +01001784 ha_free(&ckchs_transaction.path);
William Lallemandda8584c2020-05-14 10:14:37 +02001785 ckchs_transaction.new_ckchs = NULL;
1786 ckchs_transaction.old_ckchs = NULL;
1787 goto end;
1788 }
1789 }
1790end:
1791
1792 chunk_appendf(trash, "\n");
1793 if (errcode & ERR_WARN)
1794 chunk_appendf(trash, "%s", err);
1795 chunk_appendf(trash, "Success!\n");
1796 if (ci_putchk(si_ic(si), trash) == -1)
1797 si_rx_room_blk(si);
1798 free_trash_chunk(trash);
1799 /* success: call the release function and don't come back */
1800 return 1;
1801yield:
1802 /* store the state */
1803 if (ci_putchk(si_ic(si), trash) == -1)
1804 si_rx_room_blk(si);
1805 free_trash_chunk(trash);
1806 si_rx_endp_more(si); /* let's come back later */
1807 return 0; /* should come back */
1808
1809error:
1810 /* spin unlock and free are done in the release function */
1811 if (trash) {
1812 chunk_appendf(trash, "\n%sFailed!\n", err);
1813 if (ci_putchk(si_ic(si), trash) == -1)
1814 si_rx_room_blk(si);
1815 free_trash_chunk(trash);
1816 }
1817 /* error: call the release function and don't come back */
1818 return 1;
1819}
1820
1821/*
1822 * Parsing function of 'commit ssl cert'
1823 */
1824static int cli_parse_commit_cert(char **args, char *payload, struct appctx *appctx, void *private)
1825{
1826 char *err = NULL;
1827
1828 if (!cli_has_level(appctx, ACCESS_LVL_ADMIN))
1829 return 1;
1830
1831 if (!*args[3])
1832 return cli_err(appctx, "'commit ssl cert expects a filename\n");
1833
1834 /* The operations on the CKCH architecture are locked so we can
1835 * manipulate ckch_store and ckch_inst */
1836 if (HA_SPIN_TRYLOCK(CKCH_LOCK, &ckch_lock))
1837 return cli_err(appctx, "Can't commit the certificate!\nOperations on certificates are currently locked!\n");
1838
1839 if (!ckchs_transaction.path) {
1840 memprintf(&err, "No ongoing transaction! !\n");
1841 goto error;
1842 }
1843
1844 if (strcmp(ckchs_transaction.path, args[3]) != 0) {
1845 memprintf(&err, "The ongoing transaction is about '%s' but you are trying to set '%s'\n", ckchs_transaction.path, args[3]);
1846 goto error;
1847 }
1848
William Lallemand5685ccf2020-09-16 16:12:25 +02001849 /* if a certificate is here, a private key must be here too */
1850 if (ckchs_transaction.new_ckchs->ckch->cert && !ckchs_transaction.new_ckchs->ckch->key) {
1851 memprintf(&err, "The transaction must contain at least a certificate and a private key!\n");
1852 goto error;
1853 }
William Lallemanda9419522020-06-24 16:26:41 +02001854
William Lallemand5685ccf2020-09-16 16:12:25 +02001855 if (!X509_check_private_key(ckchs_transaction.new_ckchs->ckch->cert, ckchs_transaction.new_ckchs->ckch->key)) {
1856 memprintf(&err, "inconsistencies between private key and certificate loaded '%s'.\n", ckchs_transaction.path);
1857 goto error;
William Lallemandda8584c2020-05-14 10:14:37 +02001858 }
1859
1860 /* init the appctx structure */
1861 appctx->st2 = SETCERT_ST_INIT;
1862 appctx->ctx.ssl.next_ckchi = NULL;
1863 appctx->ctx.ssl.new_ckchs = ckchs_transaction.new_ckchs;
1864 appctx->ctx.ssl.old_ckchs = ckchs_transaction.old_ckchs;
1865
1866 /* we don't unlock there, it will be unlock after the IO handler, in the release handler */
1867 return 0;
1868
1869error:
1870
1871 HA_SPIN_UNLOCK(CKCH_LOCK, &ckch_lock);
1872 err = memprintf(&err, "%sCan't commit %s!\n", err ? err : "", args[3]);
1873
1874 return cli_dynerr(appctx, err);
1875}
1876
1877
1878
1879
1880/*
1881 * Parsing function of `set ssl cert`, it updates or creates a temporary ckch.
1882 */
1883static int cli_parse_set_cert(char **args, char *payload, struct appctx *appctx, void *private)
1884{
1885 struct ckch_store *new_ckchs = NULL;
1886 struct ckch_store *old_ckchs = NULL;
1887 char *err = NULL;
1888 int i;
William Lallemandda8584c2020-05-14 10:14:37 +02001889 int errcode = 0;
1890 char *end;
1891 int type = CERT_TYPE_PEM;
1892 struct cert_key_and_chain *ckch;
1893 struct buffer *buf;
1894
1895 if (!cli_has_level(appctx, ACCESS_LVL_ADMIN))
1896 return 1;
1897
William Lallemandda8584c2020-05-14 10:14:37 +02001898 if (!*args[3] || !payload)
1899 return cli_err(appctx, "'set ssl cert expects a filename and a certificate as a payload\n");
1900
1901 /* The operations on the CKCH architecture are locked so we can
1902 * manipulate ckch_store and ckch_inst */
1903 if (HA_SPIN_TRYLOCK(CKCH_LOCK, &ckch_lock))
1904 return cli_err(appctx, "Can't update the certificate!\nOperations on certificates are currently locked!\n");
1905
William Lallemand5ba80d62021-05-04 16:17:27 +02001906 if ((buf = alloc_trash_chunk()) == NULL) {
1907 memprintf(&err, "%sCan't allocate memory\n", err ? err : "");
1908 errcode |= ERR_ALERT | ERR_FATAL;
1909 goto end;
1910 }
William Lallemande5ff4ad2020-06-08 09:40:37 +02001911
William Lallemandda8584c2020-05-14 10:14:37 +02001912 if (!chunk_strcpy(buf, args[3])) {
1913 memprintf(&err, "%sCan't allocate memory\n", err ? err : "");
1914 errcode |= ERR_ALERT | ERR_FATAL;
1915 goto end;
1916 }
1917
1918 /* check which type of file we want to update */
1919 for (i = 0; cert_exts[i].type < CERT_TYPE_MAX; i++) {
1920 end = strrchr(buf->area, '.');
Tim Duesterhuse5ff1412021-01-02 22:31:53 +01001921 if (end && *cert_exts[i].ext && (strcmp(end + 1, cert_exts[i].ext) == 0)) {
William Lallemandda8584c2020-05-14 10:14:37 +02001922 *end = '\0';
William Lallemand089c1382020-10-23 17:35:12 +02001923 buf->data = strlen(buf->area);
William Lallemandda8584c2020-05-14 10:14:37 +02001924 type = cert_exts[i].type;
1925 break;
1926 }
1927 }
1928
1929 appctx->ctx.ssl.old_ckchs = NULL;
1930 appctx->ctx.ssl.new_ckchs = NULL;
1931
1932 /* if there is an ongoing transaction */
1933 if (ckchs_transaction.path) {
William Lallemandda8584c2020-05-14 10:14:37 +02001934 /* if there is an ongoing transaction, check if this is the same file */
1935 if (strcmp(ckchs_transaction.path, buf->area) != 0) {
William Lallemand089c1382020-10-23 17:35:12 +02001936 /* we didn't find the transaction, must try more cases below */
1937
1938 /* if the del-ext option is activated we should try to take a look at a ".crt" too. */
1939 if (type != CERT_TYPE_PEM && global_ssl.extra_files_noext) {
1940 if (!chunk_strcat(buf, ".crt")) {
1941 memprintf(&err, "%sCan't allocate memory\n", err ? err : "");
1942 errcode |= ERR_ALERT | ERR_FATAL;
1943 goto end;
1944 }
1945
1946 if (strcmp(ckchs_transaction.path, buf->area) != 0) {
1947 /* remove .crt of the error message */
1948 *(b_orig(buf) + b_data(buf) + strlen(".crt")) = '\0';
1949 b_sub(buf, strlen(".crt"));
1950
1951 memprintf(&err, "The ongoing transaction is about '%s' but you are trying to set '%s'\n", ckchs_transaction.path, buf->area);
1952 errcode |= ERR_ALERT | ERR_FATAL;
1953 goto end;
1954 }
1955 }
William Lallemandda8584c2020-05-14 10:14:37 +02001956 }
1957
1958 appctx->ctx.ssl.old_ckchs = ckchs_transaction.new_ckchs;
1959
1960 } else {
William Lallemandda8584c2020-05-14 10:14:37 +02001961
William Lallemand95fefa12020-09-09 12:01:33 +02001962 /* lookup for the certificate in the tree */
1963 appctx->ctx.ssl.old_ckchs = ckchs_lookup(buf->area);
William Lallemand089c1382020-10-23 17:35:12 +02001964
1965 if (!appctx->ctx.ssl.old_ckchs) {
1966 /* if the del-ext option is activated we should try to take a look at a ".crt" too. */
1967 if (type != CERT_TYPE_PEM && global_ssl.extra_files_noext) {
1968 if (!chunk_strcat(buf, ".crt")) {
1969 memprintf(&err, "%sCan't allocate memory\n", err ? err : "");
1970 errcode |= ERR_ALERT | ERR_FATAL;
1971 goto end;
1972 }
1973 appctx->ctx.ssl.old_ckchs = ckchs_lookup(buf->area);
1974 }
1975 }
William Lallemandda8584c2020-05-14 10:14:37 +02001976 }
1977
1978 if (!appctx->ctx.ssl.old_ckchs) {
1979 memprintf(&err, "%sCan't replace a certificate which is not referenced by the configuration!\n",
1980 err ? err : "");
1981 errcode |= ERR_ALERT | ERR_FATAL;
1982 goto end;
1983 }
1984
1985 if (!appctx->ctx.ssl.path) {
1986 /* this is a new transaction, set the path of the transaction */
1987 appctx->ctx.ssl.path = strdup(appctx->ctx.ssl.old_ckchs->path);
1988 if (!appctx->ctx.ssl.path) {
1989 memprintf(&err, "%sCan't allocate memory\n", err ? err : "");
1990 errcode |= ERR_ALERT | ERR_FATAL;
1991 goto end;
1992 }
1993 }
1994
1995 old_ckchs = appctx->ctx.ssl.old_ckchs;
1996
1997 /* duplicate the ckch store */
1998 new_ckchs = ckchs_dup(old_ckchs);
1999 if (!new_ckchs) {
2000 memprintf(&err, "%sCannot allocate memory!\n",
2001 err ? err : "");
2002 errcode |= ERR_ALERT | ERR_FATAL;
2003 goto end;
2004 }
2005
William Lallemand95fefa12020-09-09 12:01:33 +02002006 ckch = new_ckchs->ckch;
William Lallemandda8584c2020-05-14 10:14:37 +02002007
2008 /* appply the change on the duplicate */
2009 if (cert_exts[type].load(buf->area, payload, ckch, &err) != 0) {
2010 memprintf(&err, "%sCan't load the payload\n", err ? err : "");
2011 errcode |= ERR_ALERT | ERR_FATAL;
2012 goto end;
2013 }
2014
2015 appctx->ctx.ssl.new_ckchs = new_ckchs;
2016
2017 /* we succeed, we can save the ckchs in the transaction */
2018
2019 /* if there wasn't a transaction, update the old ckchs */
2020 if (!ckchs_transaction.old_ckchs) {
2021 ckchs_transaction.old_ckchs = appctx->ctx.ssl.old_ckchs;
2022 ckchs_transaction.path = appctx->ctx.ssl.path;
2023 err = memprintf(&err, "Transaction created for certificate %s!\n", ckchs_transaction.path);
2024 } else {
2025 err = memprintf(&err, "Transaction updated for certificate %s!\n", ckchs_transaction.path);
2026
2027 }
2028
2029 /* free the previous ckchs if there was a transaction */
2030 ckch_store_free(ckchs_transaction.new_ckchs);
2031
2032 ckchs_transaction.new_ckchs = appctx->ctx.ssl.new_ckchs;
2033
2034
2035 /* creates the SNI ctxs later in the IO handler */
2036
2037end:
2038 free_trash_chunk(buf);
2039
2040 if (errcode & ERR_CODE) {
2041
2042 ckch_store_free(appctx->ctx.ssl.new_ckchs);
2043 appctx->ctx.ssl.new_ckchs = NULL;
2044
2045 appctx->ctx.ssl.old_ckchs = NULL;
2046
Willy Tarreau61cfdf42021-02-20 10:46:51 +01002047 ha_free(&appctx->ctx.ssl.path);
William Lallemandda8584c2020-05-14 10:14:37 +02002048
2049 HA_SPIN_UNLOCK(CKCH_LOCK, &ckch_lock);
2050 return cli_dynerr(appctx, memprintf(&err, "%sCan't update %s!\n", err ? err : "", args[3]));
2051 } else {
2052
2053 HA_SPIN_UNLOCK(CKCH_LOCK, &ckch_lock);
2054 return cli_dynmsg(appctx, LOG_NOTICE, err);
2055 }
2056 /* TODO: handle the ERR_WARN which are not handled because of the io_handler */
2057}
2058
2059/* parsing function of 'abort ssl cert' */
2060static int cli_parse_abort_cert(char **args, char *payload, struct appctx *appctx, void *private)
2061{
2062 char *err = NULL;
2063
2064 if (!cli_has_level(appctx, ACCESS_LVL_ADMIN))
2065 return 1;
2066
2067 if (!*args[3])
2068 return cli_err(appctx, "'abort ssl cert' expects a filename\n");
2069
2070 /* The operations on the CKCH architecture are locked so we can
2071 * manipulate ckch_store and ckch_inst */
2072 if (HA_SPIN_TRYLOCK(CKCH_LOCK, &ckch_lock))
2073 return cli_err(appctx, "Can't abort!\nOperations on certificates are currently locked!\n");
2074
2075 if (!ckchs_transaction.path) {
2076 memprintf(&err, "No ongoing transaction!\n");
2077 goto error;
2078 }
2079
2080 if (strcmp(ckchs_transaction.path, args[3]) != 0) {
2081 memprintf(&err, "The ongoing transaction is about '%s' but you are trying to abort a transaction for '%s'\n", ckchs_transaction.path, args[3]);
2082 goto error;
2083 }
2084
2085 /* Only free the ckchs there, because the SNI and instances were not generated yet */
2086 ckch_store_free(ckchs_transaction.new_ckchs);
2087 ckchs_transaction.new_ckchs = NULL;
William Lallemandda8584c2020-05-14 10:14:37 +02002088 ckchs_transaction.old_ckchs = NULL;
Willy Tarreau61cfdf42021-02-20 10:46:51 +01002089 ha_free(&ckchs_transaction.path);
William Lallemandda8584c2020-05-14 10:14:37 +02002090
2091 HA_SPIN_UNLOCK(CKCH_LOCK, &ckch_lock);
2092
2093 err = memprintf(&err, "Transaction aborted for certificate '%s'!\n", args[3]);
2094 return cli_dynmsg(appctx, LOG_NOTICE, err);
2095
2096error:
2097 HA_SPIN_UNLOCK(CKCH_LOCK, &ckch_lock);
2098
2099 return cli_dynerr(appctx, err);
2100}
2101
2102/* parsing function of 'new ssl cert' */
2103static int cli_parse_new_cert(char **args, char *payload, struct appctx *appctx, void *private)
2104{
2105 struct ckch_store *store;
2106 char *err = NULL;
2107 char *path;
2108
2109 if (!cli_has_level(appctx, ACCESS_LVL_ADMIN))
2110 return 1;
2111
2112 if (!*args[3])
2113 return cli_err(appctx, "'new ssl cert' expects a filename\n");
2114
2115 path = args[3];
2116
2117 /* The operations on the CKCH architecture are locked so we can
2118 * manipulate ckch_store and ckch_inst */
2119 if (HA_SPIN_TRYLOCK(CKCH_LOCK, &ckch_lock))
2120 return cli_err(appctx, "Can't create a certificate!\nOperations on certificates are currently locked!\n");
2121
2122 store = ckchs_lookup(path);
2123 if (store != NULL) {
2124 memprintf(&err, "Certificate '%s' already exists!\n", path);
2125 store = NULL; /* we don't want to free it */
2126 goto error;
2127 }
2128 /* we won't support multi-certificate bundle here */
William Lallemandbd8e6ed2020-09-16 16:08:08 +02002129 store = ckch_store_new(path);
William Lallemandda8584c2020-05-14 10:14:37 +02002130 if (!store) {
2131 memprintf(&err, "unable to allocate memory.\n");
2132 goto error;
2133 }
2134
2135 /* insert into the ckchs tree */
2136 ebst_insert(&ckchs_tree, &store->node);
2137 memprintf(&err, "New empty certificate store '%s'!\n", args[3]);
2138
2139 HA_SPIN_UNLOCK(CKCH_LOCK, &ckch_lock);
2140 return cli_dynmsg(appctx, LOG_NOTICE, err);
2141error:
2142 free(store);
2143 HA_SPIN_UNLOCK(CKCH_LOCK, &ckch_lock);
2144 return cli_dynerr(appctx, err);
2145}
2146
2147/* parsing function of 'del ssl cert' */
2148static int cli_parse_del_cert(char **args, char *payload, struct appctx *appctx, void *private)
2149{
2150 struct ckch_store *store;
2151 char *err = NULL;
2152 char *filename;
2153
2154 if (!cli_has_level(appctx, ACCESS_LVL_ADMIN))
2155 return 1;
2156
2157 if (!*args[3])
2158 return cli_err(appctx, "'del ssl cert' expects a certificate name\n");
2159
2160 if (HA_SPIN_TRYLOCK(CKCH_LOCK, &ckch_lock))
2161 return cli_err(appctx, "Can't delete the certificate!\nOperations on certificates are currently locked!\n");
2162
2163 filename = args[3];
2164
2165 store = ckchs_lookup(filename);
2166 if (store == NULL) {
2167 memprintf(&err, "certificate '%s' doesn't exist!\n", filename);
2168 goto error;
2169 }
2170 if (!LIST_ISEMPTY(&store->ckch_inst)) {
2171 memprintf(&err, "certificate '%s' in use, can't be deleted!\n", filename);
2172 goto error;
2173 }
2174
2175 ebmb_delete(&store->node);
2176 ckch_store_free(store);
2177
2178 memprintf(&err, "Certificate '%s' deleted!\n", filename);
2179
2180 HA_SPIN_UNLOCK(CKCH_LOCK, &ckch_lock);
2181 return cli_dynmsg(appctx, LOG_NOTICE, err);
2182
2183error:
2184 memprintf(&err, "Can't remove the certificate: %s\n", err ? err : "");
2185 HA_SPIN_UNLOCK(CKCH_LOCK, &ckch_lock);
2186 return cli_dynerr(appctx, err);
2187}
2188
Remi Tricot-Le Bretona32a68b2021-02-24 17:35:43 +01002189
Remi Tricot-Le Breton9f40fe02021-03-16 16:21:27 +01002190
2191/* parsing function of 'new ssl ca-file' */
2192static int cli_parse_new_cafile(char **args, char *payload, struct appctx *appctx, void *private)
2193{
2194 struct cafile_entry *cafile_entry;
2195 char *err = NULL;
2196 char *path;
2197
2198 if (!cli_has_level(appctx, ACCESS_LVL_ADMIN))
2199 return 1;
2200
2201 if (!*args[3])
2202 return cli_err(appctx, "'new ssl ca-file' expects a filename\n");
2203
2204 path = args[3];
2205
2206 /* The operations on the CKCH architecture are locked so we can
2207 * manipulate ckch_store and ckch_inst */
2208 if (HA_SPIN_TRYLOCK(CKCH_LOCK, &ckch_lock))
2209 return cli_err(appctx, "Can't create a CA file!\nOperations on certificates are currently locked!\n");
2210
2211 cafile_entry = ssl_store_get_cafile_entry(path, 0);
2212 if (cafile_entry) {
2213 memprintf(&err, "CA file '%s' already exists!\n", path);
2214 goto error;
2215 }
2216
2217 cafile_entry = ssl_store_create_cafile_entry(path, NULL, CAFILE_CERT);
2218 if (!cafile_entry) {
2219 memprintf(&err, "%sCannot allocate memory!\n",
2220 err ? err : "");
2221 goto error;
2222 }
2223
2224 /* Add the newly created cafile_entry to the tree so that
2225 * any new ckch instance created from now can use it. */
2226 if (ssl_store_add_uncommitted_cafile_entry(cafile_entry))
2227 goto error;
2228
2229 memprintf(&err, "New CA file created '%s'!\n", path);
2230
2231 HA_SPIN_UNLOCK(CKCH_LOCK, &ckch_lock);
2232 return cli_dynmsg(appctx, LOG_NOTICE, err);
2233error:
2234 HA_SPIN_UNLOCK(CKCH_LOCK, &ckch_lock);
2235 return cli_dynerr(appctx, err);
2236}
2237
Remi Tricot-Le Bretona32a68b2021-02-24 17:35:43 +01002238/*
2239 * Parsing function of `set ssl ca-file`
2240 */
2241static int cli_parse_set_cafile(char **args, char *payload, struct appctx *appctx, void *private)
2242{
2243 char *err = NULL;
2244 int errcode = 0;
2245 struct buffer *buf;
2246
2247 if (!cli_has_level(appctx, ACCESS_LVL_ADMIN))
2248 return 1;
2249
2250 if (!*args[3] || !payload)
2251 return cli_err(appctx, "'set ssl ca-file expects a filename and CAs as a payload\n");
2252
2253 /* The operations on the CKCH architecture are locked so we can
2254 * manipulate ckch_store and ckch_inst */
2255 if (HA_SPIN_TRYLOCK(CKCH_LOCK, &ckch_lock))
2256 return cli_err(appctx, "Can't update the CA file!\nOperations on certificates are currently locked!\n");
2257
2258 if ((buf = alloc_trash_chunk()) == NULL) {
2259 memprintf(&err, "%sCan't allocate memory\n", err ? err : "");
2260 errcode |= ERR_ALERT | ERR_FATAL;
2261 goto end;
2262 }
2263
2264 if (!chunk_strcpy(buf, args[3])) {
2265 memprintf(&err, "%sCan't allocate memory\n", err ? err : "");
2266 errcode |= ERR_ALERT | ERR_FATAL;
2267 goto end;
2268 }
2269
2270 appctx->ctx.ssl.old_cafile_entry = NULL;
2271 appctx->ctx.ssl.new_cafile_entry = NULL;
2272
2273 /* if there is an ongoing transaction */
2274 if (cafile_transaction.path) {
2275 /* if there is an ongoing transaction, check if this is the same file */
2276 if (strcmp(cafile_transaction.path, buf->area) != 0) {
2277 memprintf(&err, "The ongoing transaction is about '%s' but you are trying to set '%s'\n", cafile_transaction.path, buf->area);
2278 errcode |= ERR_ALERT | ERR_FATAL;
2279 goto end;
2280 }
2281 appctx->ctx.ssl.old_cafile_entry = cafile_transaction.old_cafile_entry;
2282 }
2283 else {
2284 /* lookup for the certificate in the tree */
2285 appctx->ctx.ssl.old_cafile_entry = ssl_store_get_cafile_entry(buf->area, 0);
2286 }
2287
2288 if (!appctx->ctx.ssl.old_cafile_entry) {
2289 memprintf(&err, "%sCan't replace a CA file which is not referenced by the configuration!\n",
2290 err ? err : "");
2291 errcode |= ERR_ALERT | ERR_FATAL;
2292 goto end;
2293 }
2294
2295 if (!appctx->ctx.ssl.path) {
2296 /* this is a new transaction, set the path of the transaction */
2297 appctx->ctx.ssl.path = strdup(appctx->ctx.ssl.old_cafile_entry->path);
2298 if (!appctx->ctx.ssl.path) {
2299 memprintf(&err, "%sCan't allocate memory\n", err ? err : "");
2300 errcode |= ERR_ALERT | ERR_FATAL;
2301 goto end;
2302 }
2303 }
2304
2305 if (appctx->ctx.ssl.new_cafile_entry)
2306 ssl_store_delete_cafile_entry(appctx->ctx.ssl.new_cafile_entry);
2307
2308 /* Create a new cafile_entry without adding it to the cafile tree. */
Remi Tricot-Le Breton0bb48242021-04-16 17:59:23 +02002309 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 +01002310 if (!appctx->ctx.ssl.new_cafile_entry) {
2311 memprintf(&err, "%sCannot allocate memory!\n",
2312 err ? err : "");
2313 errcode |= ERR_ALERT | ERR_FATAL;
2314 goto end;
2315 }
2316
2317 /* Fill the new entry with the new CAs. */
2318 if (ssl_store_load_ca_from_buf(appctx->ctx.ssl.new_cafile_entry, payload)) {
2319 memprintf(&err, "%sInvalid payload\n", err ? err : "");
2320 errcode |= ERR_ALERT | ERR_FATAL;
2321 goto end;
2322 }
2323
2324 /* we succeed, we can save the ca in the transaction */
2325
2326 /* if there wasn't a transaction, update the old CA */
2327 if (!cafile_transaction.old_cafile_entry) {
2328 cafile_transaction.old_cafile_entry = appctx->ctx.ssl.old_cafile_entry;
2329 cafile_transaction.path = appctx->ctx.ssl.path;
2330 err = memprintf(&err, "transaction created for CA %s!\n", cafile_transaction.path);
2331 } else {
2332 err = memprintf(&err, "transaction updated for CA %s!\n", cafile_transaction.path);
2333 }
2334
2335 /* free the previous CA if there was a transaction */
2336 ssl_store_delete_cafile_entry(cafile_transaction.new_cafile_entry);
2337
2338 cafile_transaction.new_cafile_entry = appctx->ctx.ssl.new_cafile_entry;
2339
2340 /* creates the SNI ctxs later in the IO handler */
2341
2342end:
2343 free_trash_chunk(buf);
2344
2345 if (errcode & ERR_CODE) {
2346 ssl_store_delete_cafile_entry(appctx->ctx.ssl.new_cafile_entry);
2347 appctx->ctx.ssl.new_cafile_entry = NULL;
2348 appctx->ctx.ssl.old_cafile_entry = NULL;
2349
2350 free(appctx->ctx.ssl.path);
2351 appctx->ctx.ssl.path = NULL;
2352
2353 HA_SPIN_UNLOCK(CKCH_LOCK, &ckch_lock);
2354 return cli_dynerr(appctx, memprintf(&err, "%sCan't update %s!\n", err ? err : "", args[3]));
2355 } else {
2356
2357 HA_SPIN_UNLOCK(CKCH_LOCK, &ckch_lock);
2358 return cli_dynmsg(appctx, LOG_NOTICE, err);
2359 }
2360}
2361
2362
2363/*
2364 * Parsing function of 'commit ssl ca-file'
2365 */
2366static int cli_parse_commit_cafile(char **args, char *payload, struct appctx *appctx, void *private)
2367{
2368 char *err = NULL;
2369
2370 if (!cli_has_level(appctx, ACCESS_LVL_ADMIN))
2371 return 1;
2372
2373 if (!*args[3])
2374 return cli_err(appctx, "'commit ssl ca-file expects a filename\n");
2375
2376 /* The operations on the CKCH architecture are locked so we can
2377 * manipulate ckch_store and ckch_inst */
2378 if (HA_SPIN_TRYLOCK(CKCH_LOCK, &ckch_lock))
2379 return cli_err(appctx, "Can't commit the CA file!\nOperations on certificates are currently locked!\n");
2380
2381 if (!cafile_transaction.path) {
2382 memprintf(&err, "No ongoing transaction! !\n");
2383 goto error;
2384 }
2385
2386 if (strcmp(cafile_transaction.path, args[3]) != 0) {
2387 memprintf(&err, "The ongoing transaction is about '%s' but you are trying to set '%s'\n", cafile_transaction.path, args[3]);
2388 goto error;
2389 }
2390 /* init the appctx structure */
2391 appctx->st2 = SETCERT_ST_INIT;
2392 appctx->ctx.ssl.next_ckchi_link = NULL;
2393 appctx->ctx.ssl.old_cafile_entry = cafile_transaction.old_cafile_entry;
2394 appctx->ctx.ssl.new_cafile_entry = cafile_transaction.new_cafile_entry;
2395
2396 return 0;
2397
2398error:
2399
2400 HA_SPIN_UNLOCK(CKCH_LOCK, &ckch_lock);
2401 err = memprintf(&err, "%sCan't commit %s!\n", err ? err : "", args[3]);
2402
2403 return cli_dynerr(appctx, err);
2404}
2405
2406enum {
2407 CREATE_NEW_INST_OK = 0,
2408 CREATE_NEW_INST_YIELD = -1,
2409 CREATE_NEW_INST_ERR = -2
2410};
2411
2412static inline int __create_new_instance(struct appctx *appctx, struct ckch_inst *ckchi, int *count,
2413 struct buffer *trash, char *err)
2414{
2415 struct ckch_inst *new_inst;
2416
2417 /* it takes a lot of CPU to creates SSL_CTXs, so we yield every 10 CKCH instances */
2418 if (*count >= 10) {
2419 /* save the next ckchi to compute */
2420 appctx->ctx.ssl.next_ckchi = ckchi;
2421 return CREATE_NEW_INST_YIELD;
2422 }
2423
2424 /* Rebuild a new ckch instance that uses the same ckch_store
2425 * than a reference ckchi instance but will use a new CA file. */
2426 if (ckch_inst_rebuild(ckchi->ckch_store, ckchi, &new_inst, &err))
2427 return CREATE_NEW_INST_ERR;
2428
2429 /* display one dot per new instance */
2430 chunk_appendf(trash, ".");
2431 ++(*count);
2432
2433 return CREATE_NEW_INST_OK;
2434}
2435
2436/*
2437 * This function tries to create new ckch instances and their SNIs using a newly
2438 * set certificate authority (CA file)
2439 */
2440static int cli_io_handler_commit_cafile(struct appctx *appctx)
2441{
2442 struct stream_interface *si = appctx->owner;
2443 int y = 0;
2444 char *err = NULL;
2445 int errcode = 0;
2446 struct cafile_entry *old_cafile_entry, *new_cafile_entry;
2447 struct ckch_inst_link *ckchi_link;
2448 struct buffer *trash = alloc_trash_chunk();
2449
2450 if (trash == NULL)
2451 goto error;
2452
2453 if (unlikely(si_ic(si)->flags & (CF_WRITE_ERROR|CF_SHUTW)))
2454 goto error;
2455
2456 while (1) {
2457 switch (appctx->st2) {
2458 case SETCERT_ST_INIT:
2459 /* This state just print the update message */
2460 chunk_printf(trash, "Committing %s", cafile_transaction.path);
2461 if (ci_putchk(si_ic(si), trash) == -1) {
2462 si_rx_room_blk(si);
2463 goto yield;
2464 }
2465 appctx->st2 = SETCERT_ST_GEN;
2466 /* fallthrough */
2467 case SETCERT_ST_GEN:
2468 /*
2469 * This state generates the ckch instances with their
2470 * sni_ctxs and SSL_CTX.
2471 *
2472 * Since the SSL_CTX generation can be CPU consumer, we
2473 * yield every 10 instances.
2474 */
2475 old_cafile_entry = appctx->ctx.ssl.old_cafile_entry;
2476 new_cafile_entry = appctx->ctx.ssl.new_cafile_entry;
2477
2478 if (!new_cafile_entry)
2479 continue;
2480
2481 /* get the next ckchi to regenerate */
2482 ckchi_link = appctx->ctx.ssl.next_ckchi_link;
2483 /* we didn't start yet, set it to the first elem */
2484 if (ckchi_link == NULL) {
2485 ckchi_link = LIST_ELEM(old_cafile_entry->ckch_inst_link.n, typeof(ckchi_link), list);
2486 /* Add the newly created cafile_entry to the tree so that
2487 * any new ckch instance created from now can use it. */
2488 if (ssl_store_add_uncommitted_cafile_entry(new_cafile_entry))
2489 goto error;
2490 }
2491
2492 list_for_each_entry_from(ckchi_link, &old_cafile_entry->ckch_inst_link, list) {
2493 switch (__create_new_instance(appctx, ckchi_link->ckch_inst, &y, trash, err)) {
2494 case CREATE_NEW_INST_YIELD:
2495 appctx->ctx.ssl.next_ckchi_link = ckchi_link;
2496 goto yield;
2497 case CREATE_NEW_INST_ERR:
2498 goto error;
2499 default: break;
2500 }
2501 }
2502
2503 appctx->st2 = SETCERT_ST_INSERT;
2504 /* fallthrough */
2505 case SETCERT_ST_INSERT:
2506 /* The generation is finished, we can insert everything */
2507
2508 old_cafile_entry = appctx->ctx.ssl.old_cafile_entry;
2509 new_cafile_entry = appctx->ctx.ssl.new_cafile_entry;
2510
2511 if (!new_cafile_entry)
2512 continue;
2513
2514 /* insert the new ckch_insts in the crtlist_entry */
2515 list_for_each_entry(ckchi_link, &new_cafile_entry->ckch_inst_link, list) {
2516 if (ckchi_link->ckch_inst->crtlist_entry)
2517 LIST_INSERT(&ckchi_link->ckch_inst->crtlist_entry->ckch_inst,
2518 &ckchi_link->ckch_inst->by_crtlist_entry);
2519 }
2520
2521 /* First, we insert every new SNIs in the trees, also replace the default_ctx */
2522 list_for_each_entry(ckchi_link, &new_cafile_entry->ckch_inst_link, list) {
2523 __ssl_sock_load_new_ckch_instance(ckchi_link->ckch_inst);
2524 }
2525
2526 /* delete the old sni_ctx, the old ckch_insts and the ckch_store */
2527 list_for_each_entry(ckchi_link, &old_cafile_entry->ckch_inst_link, list) {
2528 __ckch_inst_free_locked(ckchi_link->ckch_inst);
2529 }
2530
2531
2532 /* Remove the old cafile entry from the tree */
2533 ebmb_delete(&old_cafile_entry->node);
2534 ssl_store_delete_cafile_entry(old_cafile_entry);
2535
2536 appctx->st2 = SETCERT_ST_FIN;
2537 /* fallthrough */
2538 case SETCERT_ST_FIN:
2539 /* we achieved the transaction, we can set everything to NULL */
2540 ha_free(&cafile_transaction.path);
2541 cafile_transaction.old_cafile_entry = NULL;
2542 cafile_transaction.new_cafile_entry = NULL;
2543 goto end;
2544 }
2545 }
2546end:
2547
2548 chunk_appendf(trash, "\n");
2549 if (errcode & ERR_WARN)
2550 chunk_appendf(trash, "%s", err);
2551 chunk_appendf(trash, "Success!\n");
2552 if (ci_putchk(si_ic(si), trash) == -1)
2553 si_rx_room_blk(si);
2554 free_trash_chunk(trash);
2555 /* success: call the release function and don't come back */
2556 return 1;
2557yield:
2558 /* store the state */
2559 if (ci_putchk(si_ic(si), trash) == -1)
2560 si_rx_room_blk(si);
2561 free_trash_chunk(trash);
2562 si_rx_endp_more(si); /* let's come back later */
2563 return 0; /* should come back */
2564
2565error:
2566 /* spin unlock and free are done in the release function */
2567 if (trash) {
2568 chunk_appendf(trash, "\n%sFailed!\n", err);
2569 if (ci_putchk(si_ic(si), trash) == -1)
2570 si_rx_room_blk(si);
2571 free_trash_chunk(trash);
2572 }
2573 /* error: call the release function and don't come back */
2574 return 1;
2575}
2576
Remi Tricot-Le Bretond5fd09d2021-03-11 10:22:52 +01002577
2578/* parsing function of 'abort ssl ca-file' */
2579static int cli_parse_abort_cafile(char **args, char *payload, struct appctx *appctx, void *private)
2580{
2581 char *err = NULL;
2582
2583 if (!cli_has_level(appctx, ACCESS_LVL_ADMIN))
2584 return 1;
2585
2586 if (!*args[3])
2587 return cli_err(appctx, "'abort ssl ca-file' expects a filename\n");
2588
2589 /* The operations on the CKCH architecture are locked so we can
2590 * manipulate ckch_store and ckch_inst */
2591 if (HA_SPIN_TRYLOCK(CKCH_LOCK, &ckch_lock))
2592 return cli_err(appctx, "Can't abort!\nOperations on certificates are currently locked!\n");
2593
2594 if (!cafile_transaction.path) {
2595 memprintf(&err, "No ongoing transaction!\n");
2596 goto error;
2597 }
2598
2599 if (strcmp(cafile_transaction.path, args[3]) != 0) {
2600 memprintf(&err, "The ongoing transaction is about '%s' but you are trying to abort a transaction for '%s'\n", cafile_transaction.path, args[3]);
2601 goto error;
2602 }
2603
2604 /* Only free the uncommitted cafile_entry here, because the SNI and instances were not generated yet */
2605 ssl_store_delete_cafile_entry(cafile_transaction.new_cafile_entry);
2606 cafile_transaction.new_cafile_entry = NULL;
2607 cafile_transaction.old_cafile_entry = NULL;
2608 ha_free(&cafile_transaction.path);
2609
2610 HA_SPIN_UNLOCK(CKCH_LOCK, &ckch_lock);
2611
2612 err = memprintf(&err, "Transaction aborted for certificate '%s'!\n", args[3]);
2613 return cli_dynmsg(appctx, LOG_NOTICE, err);
2614
2615error:
2616 HA_SPIN_UNLOCK(CKCH_LOCK, &ckch_lock);
2617
2618 return cli_dynerr(appctx, err);
2619}
2620
Remi Tricot-Le Bretona32a68b2021-02-24 17:35:43 +01002621/* release function of the `commit ssl ca-file' command, free things and unlock the spinlock */
2622static void cli_release_commit_cafile(struct appctx *appctx)
2623{
2624 if (appctx->st2 != SETCERT_ST_FIN) {
2625 struct cafile_entry *new_cafile_entry = appctx->ctx.ssl.new_cafile_entry;
2626
2627 /* Remove the uncommitted cafile_entry from the tree. */
2628 ebmb_delete(&new_cafile_entry->node);
2629 ssl_store_delete_cafile_entry(new_cafile_entry);
2630 }
2631 HA_SPIN_UNLOCK(CKCH_LOCK, &ckch_lock);
2632}
2633
2634
Remi Tricot-Le Breton2a22e162021-03-16 11:19:33 +01002635/* IO handler of details "show ssl ca-file <filename[:index]>" */
2636static int cli_io_handler_show_cafile_detail(struct appctx *appctx)
2637{
2638 struct stream_interface *si = appctx->owner;
2639 struct cafile_entry *cafile_entry = appctx->ctx.cli.p0;
2640 struct buffer *out = alloc_trash_chunk();
2641 int i;
2642 X509 *cert;
2643 STACK_OF(X509_OBJECT) *objs;
2644 int retval = 0;
2645 long ca_index = (long)appctx->ctx.cli.p1;
2646
2647 if (!out)
2648 goto end_no_putchk;
2649
2650 chunk_appendf(out, "Filename: ");
2651 if (cafile_entry == cafile_transaction.new_cafile_entry)
2652 chunk_appendf(out, "*");
2653 chunk_appendf(out, "%s\n", cafile_entry->path);
2654
2655 chunk_appendf(out, "Status: ");
2656 if (!cafile_entry->ca_store)
2657 chunk_appendf(out, "Empty\n");
2658 else if (LIST_ISEMPTY(&cafile_entry->ckch_inst_link))
2659 chunk_appendf(out, "Unused\n");
2660 else
2661 chunk_appendf(out, "Used\n");
2662
2663 if (!cafile_entry->ca_store)
2664 goto end;
2665
2666 objs = X509_STORE_get0_objects(cafile_entry->ca_store);
2667 for (i = 0; i < sk_X509_OBJECT_num(objs); i++) {
2668 cert = X509_OBJECT_get0_X509(sk_X509_OBJECT_value(objs, i));
2669 if (!cert)
2670 continue;
2671
2672 /* Certificate indexes start at 1 on the CLI output. */
2673 if (ca_index && ca_index-1 != i)
2674 continue;
2675
2676 chunk_appendf(out, "\nCertificate #%d:\n", i+1);
2677 retval = show_cert_detail(cert, NULL, out);
2678 if (retval < 0)
2679 goto end_no_putchk;
2680 else if (retval || ca_index)
2681 goto end;
2682 }
2683
2684end:
2685 if (ci_putchk(si_ic(si), out) == -1) {
2686 si_rx_room_blk(si);
2687 goto yield;
2688 }
2689
2690end_no_putchk:
2691 free_trash_chunk(out);
2692 return 1;
2693yield:
2694 free_trash_chunk(out);
2695 return 0; /* should come back */
2696}
2697
2698
2699/* parsing function for 'show ssl ca-file [cafile[:index]]' */
2700static int cli_parse_show_cafile(char **args, char *payload, struct appctx *appctx, void *private)
2701{
2702 struct cafile_entry *cafile_entry;
2703 long ca_index = 0;
2704 char *colons;
2705 char *err = NULL;
2706
2707 if (!cli_has_level(appctx, ACCESS_LVL_OPER))
2708 return cli_err(appctx, "Can't allocate memory!\n");
2709
2710 /* The operations on the CKCH architecture are locked so we can
2711 * manipulate ckch_store and ckch_inst */
2712 if (HA_SPIN_TRYLOCK(CKCH_LOCK, &ckch_lock))
2713 return cli_err(appctx, "Can't show!\nOperations on certificates are currently locked!\n");
2714
2715 /* check if there is a certificate to lookup */
2716 if (*args[3]) {
2717
2718 /* Look for an optional CA index after the CA file name */
2719 colons = strchr(args[3], ':');
2720 if (colons) {
2721 char *endptr;
2722
2723 ca_index = strtol(colons + 1, &endptr, 10);
2724 /* Indexes start at 1 */
2725 if (colons + 1 == endptr || *endptr != '\0' || ca_index <= 0) {
2726 memprintf(&err, "wrong CA index after colons in '%s'!", args[3]);
2727 goto error;
2728 }
2729 *colons = '\0';
2730 }
2731
2732 if (*args[3] == '*') {
2733 if (!cafile_transaction.new_cafile_entry)
2734 goto error;
2735
2736 cafile_entry = cafile_transaction.new_cafile_entry;
2737
2738 if (strcmp(args[3] + 1, cafile_entry->path) != 0)
2739 goto error;
2740
2741 } else {
2742 /* Get the "original" cafile_entry and not the
2743 * uncommitted one if it exists. */
2744 if ((cafile_entry = ssl_store_get_cafile_entry(args[3], 1)) == NULL || cafile_entry->type != CAFILE_CERT)
2745 goto error;
2746 }
2747
2748 appctx->ctx.cli.p0 = cafile_entry;
2749 appctx->ctx.cli.p1 = (void*)ca_index;
2750 /* use the IO handler that shows details */
2751 appctx->io_handler = cli_io_handler_show_cafile_detail;
2752 }
2753
2754 return 0;
2755
2756error:
2757 HA_SPIN_UNLOCK(CKCH_LOCK, &ckch_lock);
2758 if (err)
2759 return cli_dynerr(appctx, err);
2760 return cli_err(appctx, "Can't display the CA file : Not found!\n");
2761}
2762
2763
2764/* release function of the 'show ssl ca-file' command */
2765static void cli_release_show_cafile(struct appctx *appctx)
2766{
2767 HA_SPIN_UNLOCK(CKCH_LOCK, &ckch_lock);
2768}
2769
2770
2771/* This function returns the number of certificates in a cafile_entry. */
2772static int get_certificate_count(struct cafile_entry *cafile_entry)
2773{
2774 int cert_count = 0;
2775 STACK_OF(X509_OBJECT) *objs;
2776
2777 if (cafile_entry && cafile_entry->ca_store) {
2778 objs = X509_STORE_get0_objects(cafile_entry->ca_store);
2779 if (objs)
2780 cert_count = sk_X509_OBJECT_num(objs);
2781 }
2782 return cert_count;
2783}
2784
2785/* IO handler of "show ssl ca-file". The command taking a specific CA file name
2786 * is managed in cli_io_handler_show_cafile_detail. */
2787static int cli_io_handler_show_cafile(struct appctx *appctx)
2788{
2789 struct buffer *trash = alloc_trash_chunk();
2790 struct ebmb_node *node;
2791 struct stream_interface *si = appctx->owner;
2792 struct cafile_entry *cafile_entry;
2793
2794 if (trash == NULL)
2795 return 1;
2796
2797 if (!appctx->ctx.ssl.old_cafile_entry) {
2798 if (cafile_transaction.old_cafile_entry) {
2799 chunk_appendf(trash, "# transaction\n");
2800 chunk_appendf(trash, "*%s", cafile_transaction.old_cafile_entry->path);
2801
2802 chunk_appendf(trash, " - %d certificate(s)\n", get_certificate_count(cafile_transaction.new_cafile_entry));
2803 }
2804 }
2805
2806 /* First time in this io_handler. */
2807 if (!appctx->ctx.cli.p0) {
2808 chunk_appendf(trash, "# filename\n");
2809 node = ebmb_first(&cafile_tree);
2810 } else {
2811 /* We yielded during a previous call. */
2812 node = &((struct cafile_entry*)appctx->ctx.cli.p0)->node;
2813 }
2814
2815 while (node) {
2816 cafile_entry = ebmb_entry(node, struct cafile_entry, node);
2817 if (cafile_entry->type == CAFILE_CERT) {
2818 chunk_appendf(trash, "%s", cafile_entry->path);
2819
2820 chunk_appendf(trash, " - %d certificate(s)\n", get_certificate_count(cafile_entry));
2821 }
2822
2823 node = ebmb_next(node);
2824 if (ci_putchk(si_ic(si), trash) == -1) {
2825 si_rx_room_blk(si);
2826 goto yield;
2827 }
2828 }
2829
2830 appctx->ctx.cli.p0 = NULL;
2831 free_trash_chunk(trash);
2832 return 1;
2833yield:
2834
2835 free_trash_chunk(trash);
2836 appctx->ctx.cli.p0 = cafile_entry;
2837 return 0; /* should come back */
2838}
2839
2840
William Lallemandee8530c2020-06-23 18:19:42 +02002841void ckch_deinit()
2842{
2843 struct eb_node *node, *next;
2844 struct ckch_store *store;
2845
2846 node = eb_first(&ckchs_tree);
2847 while (node) {
2848 next = eb_next(node);
2849 store = ebmb_entry(node, struct ckch_store, node);
2850 ckch_store_free(store);
2851 node = next;
2852 }
2853}
William Lallemandda8584c2020-05-14 10:14:37 +02002854
2855/* register cli keywords */
2856static struct cli_kw_list cli_kws = {{ },{
Remi Tricot-Le Bretona32a68b2021-02-24 17:35:43 +01002857 { { "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 },
2858 { { "set", "ssl", "cert", NULL }, "set ssl cert <certfile> <payload> : replace a certificate file", cli_parse_set_cert, NULL, NULL },
2859 { { "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 },
2860 { { "abort", "ssl", "cert", NULL }, "abort ssl cert <certfile> : abort a transaction for a certificate file", cli_parse_abort_cert, NULL, NULL },
2861 { { "del", "ssl", "cert", NULL }, "del ssl cert <certfile> : delete an unused certificate file", cli_parse_del_cert, NULL, NULL },
2862 { { "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 },
2863
Remi Tricot-Le Breton9f40fe02021-03-16 16:21:27 +01002864 { { "new", "ssl", "ca-file", NULL }, "new ssl cafile <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 +01002865 { { "set", "ssl", "ca-file", NULL }, "set ssl ca-file <cafile> <payload> : replace a CA file", cli_parse_set_cafile, NULL, NULL },
2866 { { "commit", "ssl", "ca-file", NULL }, "commit ssl ca-file <cafile> : commit a CA file", cli_parse_commit_cafile, cli_io_handler_commit_cafile, cli_release_commit_cafile },
Remi Tricot-Le Bretond5fd09d2021-03-11 10:22:52 +01002867 { { "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 Breton2a22e162021-03-16 11:19:33 +01002868 { { "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 },
William Lallemandda8584c2020-05-14 10:14:37 +02002869 { { NULL }, NULL, NULL, NULL }
2870}};
2871
2872INITCALL1(STG_REGISTER, cli_register_kw, &cli_kws);
2873