blob: 66bd7555736d32ad476a10a2df05e126ec74ecee [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
45
William Lallemand03c331c2020-05-13 10:10:01 +020046/******************** cert_key_and_chain functions *************************
47 * These are the functions that fills a cert_key_and_chain structure. For the
48 * functions filling a SSL_CTX from a cert_key_and_chain, see ssl_sock.c
49 */
50
51/*
52 * Try to parse Signed Certificate Timestamp List structure. This function
53 * makes only basic test if the data seems like SCTL. No signature validation
54 * is performed.
55 */
56static int ssl_sock_parse_sctl(struct buffer *sctl)
57{
58 int ret = 1;
59 int len, pos, sct_len;
60 unsigned char *data;
61
62 if (sctl->data < 2)
63 goto out;
64
65 data = (unsigned char *) sctl->area;
66 len = (data[0] << 8) | data[1];
67
68 if (len + 2 != sctl->data)
69 goto out;
70
71 data = data + 2;
72 pos = 0;
73 while (pos < len) {
74 if (len - pos < 2)
75 goto out;
76
77 sct_len = (data[pos] << 8) | data[pos + 1];
78 if (pos + sct_len + 2 > len)
79 goto out;
80
81 pos += sct_len + 2;
82 }
83
84 ret = 0;
85
86out:
87 return ret;
88}
89
90/* Try to load a sctl from a buffer <buf> if not NULL, or read the file <sctl_path>
91 * It fills the ckch->sctl buffer
92 * return 0 on success or != 0 on failure */
93int ssl_sock_load_sctl_from_file(const char *sctl_path, char *buf, struct cert_key_and_chain *ckch, char **err)
94{
95 int fd = -1;
96 int r = 0;
97 int ret = 1;
98 struct buffer tmp;
99 struct buffer *src;
100 struct buffer *sctl;
101
102 if (buf) {
William Lallemand8d673942021-01-27 14:58:51 +0100103 chunk_initstr(&tmp, buf);
William Lallemand03c331c2020-05-13 10:10:01 +0200104 src = &tmp;
105 } else {
106 fd = open(sctl_path, O_RDONLY);
107 if (fd == -1)
108 goto end;
109
110 trash.data = 0;
111 while (trash.data < trash.size) {
112 r = read(fd, trash.area + trash.data, trash.size - trash.data);
113 if (r < 0) {
114 if (errno == EINTR)
115 continue;
116 goto end;
117 }
118 else if (r == 0) {
119 break;
120 }
121 trash.data += r;
122 }
123 src = &trash;
124 }
125
126 ret = ssl_sock_parse_sctl(src);
127 if (ret)
128 goto end;
129
130 sctl = calloc(1, sizeof(*sctl));
131 if (!chunk_dup(sctl, src)) {
Willy Tarreau61cfdf42021-02-20 10:46:51 +0100132 ha_free(&sctl);
William Lallemand03c331c2020-05-13 10:10:01 +0200133 goto end;
134 }
135 /* no error, fill ckch with new context, old context must be free */
136 if (ckch->sctl) {
Willy Tarreau61cfdf42021-02-20 10:46:51 +0100137 ha_free(&ckch->sctl->area);
William Lallemand03c331c2020-05-13 10:10:01 +0200138 free(ckch->sctl);
139 }
140 ckch->sctl = sctl;
141 ret = 0;
142end:
143 if (fd != -1)
144 close(fd);
145
146 return ret;
147}
148
149#if ((defined SSL_CTRL_SET_TLSEXT_STATUS_REQ_CB && !defined OPENSSL_NO_OCSP) || defined OPENSSL_IS_BORINGSSL)
150/*
Ilya Shipitsin46a030c2020-07-05 16:36:08 +0500151 * This function load the OCSP Response in DER format contained in file at
William Lallemand03c331c2020-05-13 10:10:01 +0200152 * path 'ocsp_path' or base64 in a buffer <buf>
153 *
154 * Returns 0 on success, 1 in error case.
155 */
156int ssl_sock_load_ocsp_response_from_file(const char *ocsp_path, char *buf, struct cert_key_and_chain *ckch, char **err)
157{
158 int fd = -1;
159 int r = 0;
160 int ret = 1;
161 struct buffer *ocsp_response;
162 struct buffer *src = NULL;
163
164 if (buf) {
165 int i, j;
166 /* if it's from a buffer it will be base64 */
167
168 /* remove \r and \n from the payload */
169 for (i = 0, j = 0; buf[i]; i++) {
170 if (buf[i] == '\r' || buf[i] == '\n')
171 continue;
172 buf[j++] = buf[i];
173 }
174 buf[j] = 0;
175
176 ret = base64dec(buf, j, trash.area, trash.size);
177 if (ret < 0) {
178 memprintf(err, "Error reading OCSP response in base64 format");
179 goto end;
180 }
181 trash.data = ret;
182 src = &trash;
183 } else {
184 fd = open(ocsp_path, O_RDONLY);
185 if (fd == -1) {
186 memprintf(err, "Error opening OCSP response file");
187 goto end;
188 }
189
190 trash.data = 0;
191 while (trash.data < trash.size) {
192 r = read(fd, trash.area + trash.data, trash.size - trash.data);
193 if (r < 0) {
194 if (errno == EINTR)
195 continue;
196
197 memprintf(err, "Error reading OCSP response from file");
198 goto end;
199 }
200 else if (r == 0) {
201 break;
202 }
203 trash.data += r;
204 }
205 close(fd);
206 fd = -1;
207 src = &trash;
208 }
209
210 ocsp_response = calloc(1, sizeof(*ocsp_response));
211 if (!chunk_dup(ocsp_response, src)) {
Willy Tarreau61cfdf42021-02-20 10:46:51 +0100212 ha_free(&ocsp_response);
William Lallemand03c331c2020-05-13 10:10:01 +0200213 goto end;
214 }
215 /* no error, fill ckch with new context, old context must be free */
216 if (ckch->ocsp_response) {
Willy Tarreau61cfdf42021-02-20 10:46:51 +0100217 ha_free(&ckch->ocsp_response->area);
William Lallemand03c331c2020-05-13 10:10:01 +0200218 free(ckch->ocsp_response);
219 }
220 ckch->ocsp_response = ocsp_response;
221 ret = 0;
222end:
223 if (fd != -1)
224 close(fd);
225
226 return ret;
227}
228#endif
229
230/*
231 * Try to load in a ckch every files related to a ckch.
232 * (PEM, sctl, ocsp, issuer etc.)
233 *
234 * This function is only used to load files during the configuration parsing,
235 * it is not used with the CLI.
236 *
237 * This allows us to carry the contents of the file without having to read the
238 * file multiple times. The caller must call
239 * ssl_sock_free_cert_key_and_chain_contents.
240 *
241 * returns:
242 * 0 on Success
243 * 1 on SSL Failure
244 */
245int ssl_sock_load_files_into_ckch(const char *path, struct cert_key_and_chain *ckch, char **err)
246{
William Lallemand8e8581e2020-10-20 17:36:46 +0200247 struct buffer *fp = NULL;
William Lallemand03c331c2020-05-13 10:10:01 +0200248 int ret = 1;
Remi Tricot-Le Breton9f4503d2022-05-09 11:07:13 +0200249 struct stat st;
William Lallemand03c331c2020-05-13 10:10:01 +0200250
251 /* try to load the PEM */
252 if (ssl_sock_load_pem_into_ckch(path, NULL, ckch , err) != 0) {
253 goto end;
254 }
255
William Lallemand8e8581e2020-10-20 17:36:46 +0200256 fp = alloc_trash_chunk();
257 if (!fp) {
258 memprintf(err, "%sCan't allocate memory\n", err && *err ? *err : "");
259 goto end;
260 }
261
262 if (!chunk_strcpy(fp, path) || (b_data(fp) > MAXPATHLEN)) {
263 memprintf(err, "%s '%s' filename too long'.\n",
264 err && *err ? *err : "", fp->area);
265 ret = 1;
266 goto end;
267 }
268
William Lallemand089c1382020-10-23 17:35:12 +0200269 /* remove the ".crt" extension */
William Lallemand8e8581e2020-10-20 17:36:46 +0200270 if (global_ssl.extra_files_noext) {
271 char *ext;
272
273 /* look for the extension */
274 if ((ext = strrchr(fp->area, '.'))) {
William Lallemand8e8581e2020-10-20 17:36:46 +0200275
Tim Duesterhuse5ff1412021-01-02 22:31:53 +0100276 if (strcmp(ext, ".crt") == 0) {
William Lallemand8e8581e2020-10-20 17:36:46 +0200277 *ext = '\0';
William Lallemand089c1382020-10-23 17:35:12 +0200278 fp->data = strlen(fp->area);
279 }
William Lallemand8e8581e2020-10-20 17:36:46 +0200280 }
281
282 }
283
Remi Tricot-Le Breton9f4503d2022-05-09 11:07:13 +0200284 /* If no private key was found yet and we cannot look for it in extra
285 * files, raise an error.
286 */
287 if ((ckch->key == NULL) && !(global_ssl.extra_files & SSL_GF_KEY)) {
288 memprintf(err, "%sNo Private Key found in '%s'.\n", err && *err ? *err : "", fp->area);
289 goto end;
290 }
William Lallemand03c331c2020-05-13 10:10:01 +0200291
Remi Tricot-Le Breton9f4503d2022-05-09 11:07:13 +0200292 /* try to load an external private key if it wasn't in the PEM */
293 if (!chunk_strcat(fp, ".key") || (b_data(fp) > MAXPATHLEN)) {
294 memprintf(err, "%s '%s' filename too long'.\n",
295 err && *err ? *err : "", fp->area);
296 ret = 1;
297 goto end;
298 }
William Lallemand8e8581e2020-10-20 17:36:46 +0200299
Remi Tricot-Le Breton9f4503d2022-05-09 11:07:13 +0200300 if (stat(fp->area, &st) == 0) {
301 if (ssl_sock_load_key_into_ckch(fp->area, NULL, ckch, err)) {
302 memprintf(err, "%s '%s' is present but cannot be read or parsed'.\n",
303 err && *err ? *err : "", fp->area);
William Lallemand8e8581e2020-10-20 17:36:46 +0200304 goto end;
305 }
Remi Tricot-Le Breton9f4503d2022-05-09 11:07:13 +0200306 }
William Lallemand03c331c2020-05-13 10:10:01 +0200307
Remi Tricot-Le Breton9f4503d2022-05-09 11:07:13 +0200308 if (ckch->key == NULL) {
309 memprintf(err, "%sNo Private Key found in '%s'.\n", err && *err ? *err : "", fp->area);
310 goto end;
William Lallemand03c331c2020-05-13 10:10:01 +0200311 }
Remi Tricot-Le Breton9f4503d2022-05-09 11:07:13 +0200312 /* remove the added extension */
313 *(fp->area + fp->data - strlen(".key")) = '\0';
314 b_sub(fp, strlen(".key"));
315
William Lallemand03c331c2020-05-13 10:10:01 +0200316
317 if (!X509_check_private_key(ckch->cert, ckch->key)) {
318 memprintf(err, "%sinconsistencies between private key and certificate loaded '%s'.\n",
319 err && *err ? *err : "", path);
320 goto end;
321 }
322
Ilya Shipitsinc47d6762021-02-13 11:45:33 +0500323#ifdef HAVE_SSL_SCTL
William Lallemand03c331c2020-05-13 10:10:01 +0200324 /* try to load the sctl file */
325 if (global_ssl.extra_files & SSL_GF_SCTL) {
William Lallemand03c331c2020-05-13 10:10:01 +0200326 struct stat st;
327
William Lallemand8e8581e2020-10-20 17:36:46 +0200328 if (!chunk_strcat(fp, ".sctl") || b_data(fp) > MAXPATHLEN) {
329 memprintf(err, "%s '%s' filename too long'.\n",
330 err && *err ? *err : "", fp->area);
331 ret = 1;
332 goto end;
333 }
334
335 if (stat(fp->area, &st) == 0) {
336 if (ssl_sock_load_sctl_from_file(fp->area, NULL, ckch, err)) {
William Lallemand03c331c2020-05-13 10:10:01 +0200337 memprintf(err, "%s '%s.sctl' is present but cannot be read or parsed'.\n",
William Lallemand8e8581e2020-10-20 17:36:46 +0200338 err && *err ? *err : "", fp->area);
William Lallemand03c331c2020-05-13 10:10:01 +0200339 ret = 1;
340 goto end;
341 }
342 }
William Lallemand8e8581e2020-10-20 17:36:46 +0200343 /* remove the added extension */
344 *(fp->area + fp->data - strlen(".sctl")) = '\0';
345 b_sub(fp, strlen(".sctl"));
William Lallemand03c331c2020-05-13 10:10:01 +0200346 }
347#endif
348
349 /* try to load an ocsp response file */
350 if (global_ssl.extra_files & SSL_GF_OCSP) {
William Lallemand03c331c2020-05-13 10:10:01 +0200351 struct stat st;
352
William Lallemand8e8581e2020-10-20 17:36:46 +0200353 if (!chunk_strcat(fp, ".ocsp") || b_data(fp) > MAXPATHLEN) {
354 memprintf(err, "%s '%s' filename too long'.\n",
355 err && *err ? *err : "", fp->area);
356 ret = 1;
357 goto end;
358 }
359
360 if (stat(fp->area, &st) == 0) {
361 if (ssl_sock_load_ocsp_response_from_file(fp->area, NULL, ckch, err)) {
William Lallemand03c331c2020-05-13 10:10:01 +0200362 ret = 1;
363 goto end;
364 }
365 }
William Lallemand8e8581e2020-10-20 17:36:46 +0200366 /* remove the added extension */
367 *(fp->area + fp->data - strlen(".ocsp")) = '\0';
368 b_sub(fp, strlen(".ocsp"));
William Lallemand03c331c2020-05-13 10:10:01 +0200369 }
370
371#ifndef OPENSSL_IS_BORINGSSL /* Useless for BoringSSL */
372 if (ckch->ocsp_response && (global_ssl.extra_files & SSL_GF_OCSP_ISSUER)) {
373 /* if no issuer was found, try to load an issuer from the .issuer */
374 if (!ckch->ocsp_issuer) {
375 struct stat st;
William Lallemand8e8581e2020-10-20 17:36:46 +0200376
377 if (!chunk_strcat(fp, ".issuer") || b_data(fp) > MAXPATHLEN) {
378 memprintf(err, "%s '%s' filename too long'.\n",
379 err && *err ? *err : "", fp->area);
380 ret = 1;
381 goto end;
382 }
William Lallemand03c331c2020-05-13 10:10:01 +0200383
William Lallemand8e8581e2020-10-20 17:36:46 +0200384 if (stat(fp->area, &st) == 0) {
385 if (ssl_sock_load_issuer_file_into_ckch(fp->area, NULL, ckch, err)) {
William Lallemand03c331c2020-05-13 10:10:01 +0200386 ret = 1;
387 goto end;
388 }
389
390 if (X509_check_issued(ckch->ocsp_issuer, ckch->cert) != X509_V_OK) {
391 memprintf(err, "%s '%s' is not an issuer'.\n",
William Lallemand8e8581e2020-10-20 17:36:46 +0200392 err && *err ? *err : "", fp->area);
William Lallemand03c331c2020-05-13 10:10:01 +0200393 ret = 1;
394 goto end;
395 }
396 }
William Lallemand8e8581e2020-10-20 17:36:46 +0200397 /* remove the added extension */
398 *(fp->area + fp->data - strlen(".issuer")) = '\0';
399 b_sub(fp, strlen(".issuer"));
William Lallemand03c331c2020-05-13 10:10:01 +0200400 }
401 }
402#endif
403
404 ret = 0;
405
406end:
407
408 ERR_clear_error();
409
410 /* Something went wrong in one of the reads */
411 if (ret != 0)
412 ssl_sock_free_cert_key_and_chain_contents(ckch);
413
William Lallemand8e8581e2020-10-20 17:36:46 +0200414 free_trash_chunk(fp);
415
William Lallemand03c331c2020-05-13 10:10:01 +0200416 return ret;
417}
418
419/*
420 * Try to load a private key file from a <path> or a buffer <buf>
421 *
422 * If it failed you should not attempt to use the ckch but free it.
423 *
424 * Return 0 on success or != 0 on failure
425 */
426int ssl_sock_load_key_into_ckch(const char *path, char *buf, struct cert_key_and_chain *ckch , char **err)
427{
428 BIO *in = NULL;
429 int ret = 1;
430 EVP_PKEY *key = NULL;
431
432 if (buf) {
433 /* reading from a buffer */
434 in = BIO_new_mem_buf(buf, -1);
435 if (in == NULL) {
436 memprintf(err, "%sCan't allocate memory\n", err && *err ? *err : "");
437 goto end;
438 }
439
440 } else {
441 /* reading from a file */
442 in = BIO_new(BIO_s_file());
443 if (in == NULL)
444 goto end;
445
446 if (BIO_read_filename(in, path) <= 0)
447 goto end;
448 }
449
450 /* Read Private Key */
451 key = PEM_read_bio_PrivateKey(in, NULL, NULL, NULL);
452 if (key == NULL) {
453 memprintf(err, "%sunable to load private key from file '%s'.\n",
454 err && *err ? *err : "", path);
455 goto end;
456 }
457
458 ret = 0;
459
460 SWAP(ckch->key, key);
461
462end:
463
464 ERR_clear_error();
465 if (in)
466 BIO_free(in);
467 if (key)
468 EVP_PKEY_free(key);
469
470 return ret;
471}
472
473/*
474 * Try to load a PEM file from a <path> or a buffer <buf>
475 * The PEM must contain at least a Certificate,
476 * It could contain a DH, a certificate chain and a PrivateKey.
477 *
478 * If it failed you should not attempt to use the ckch but free it.
479 *
480 * Return 0 on success or != 0 on failure
481 */
482int ssl_sock_load_pem_into_ckch(const char *path, char *buf, struct cert_key_and_chain *ckch , char **err)
483{
484 BIO *in = NULL;
485 int ret = 1;
486 X509 *ca;
487 X509 *cert = NULL;
488 EVP_PKEY *key = NULL;
489 DH *dh = NULL;
490 STACK_OF(X509) *chain = NULL;
491
492 if (buf) {
493 /* reading from a buffer */
494 in = BIO_new_mem_buf(buf, -1);
495 if (in == NULL) {
496 memprintf(err, "%sCan't allocate memory\n", err && *err ? *err : "");
497 goto end;
498 }
499
500 } else {
501 /* reading from a file */
502 in = BIO_new(BIO_s_file());
503 if (in == NULL) {
504 memprintf(err, "%sCan't allocate memory\n", err && *err ? *err : "");
505 goto end;
506 }
507
508 if (BIO_read_filename(in, path) <= 0) {
509 memprintf(err, "%scannot open the file '%s'.\n",
510 err && *err ? *err : "", path);
511 goto end;
512 }
513 }
514
515 /* Read Private Key */
516 key = PEM_read_bio_PrivateKey(in, NULL, NULL, NULL);
517 /* no need to check for errors here, because the private key could be loaded later */
518
519#ifndef OPENSSL_NO_DH
520 /* Seek back to beginning of file */
521 if (BIO_reset(in) == -1) {
522 memprintf(err, "%san error occurred while reading the file '%s'.\n",
523 err && *err ? *err : "", path);
524 goto end;
525 }
526
527 dh = PEM_read_bio_DHparams(in, NULL, NULL, NULL);
528 /* no need to return an error there, dh is not mandatory */
529#endif
530
531 /* Seek back to beginning of file */
532 if (BIO_reset(in) == -1) {
533 memprintf(err, "%san error occurred while reading the file '%s'.\n",
534 err && *err ? *err : "", path);
535 goto end;
536 }
537
538 /* Read Certificate */
539 cert = PEM_read_bio_X509_AUX(in, NULL, NULL, NULL);
540 if (cert == NULL) {
541 memprintf(err, "%sunable to load certificate from file '%s'.\n",
542 err && *err ? *err : "", path);
543 goto end;
544 }
545
546 /* Look for a Certificate Chain */
547 while ((ca = PEM_read_bio_X509(in, NULL, NULL, NULL))) {
548 if (chain == NULL)
549 chain = sk_X509_new_null();
550 if (!sk_X509_push(chain, ca)) {
551 X509_free(ca);
552 goto end;
553 }
554 }
555
556 ret = ERR_get_error();
557 if (ret && (ERR_GET_LIB(ret) != ERR_LIB_PEM && ERR_GET_REASON(ret) != PEM_R_NO_START_LINE)) {
558 memprintf(err, "%sunable to load certificate chain from file '%s'.\n",
559 err && *err ? *err : "", path);
560 goto end;
561 }
562
563 /* once it loaded the PEM, it should remove everything else in the ckch */
564 if (ckch->ocsp_response) {
Willy Tarreau61cfdf42021-02-20 10:46:51 +0100565 ha_free(&ckch->ocsp_response->area);
566 ha_free(&ckch->ocsp_response);
William Lallemand03c331c2020-05-13 10:10:01 +0200567 }
568
569 if (ckch->sctl) {
Willy Tarreau61cfdf42021-02-20 10:46:51 +0100570 ha_free(&ckch->sctl->area);
571 ha_free(&ckch->sctl);
William Lallemand03c331c2020-05-13 10:10:01 +0200572 }
573
574 if (ckch->ocsp_issuer) {
575 X509_free(ckch->ocsp_issuer);
576 ckch->ocsp_issuer = NULL;
577 }
578
579 /* no error, fill ckch with new context, old context will be free at end: */
580 SWAP(ckch->key, key);
581 SWAP(ckch->dh, dh);
582 SWAP(ckch->cert, cert);
583 SWAP(ckch->chain, chain);
584
585 ret = 0;
586
587end:
588
589 ERR_clear_error();
590 if (in)
591 BIO_free(in);
592 if (key)
593 EVP_PKEY_free(key);
594 if (dh)
595 DH_free(dh);
596 if (cert)
597 X509_free(cert);
598 if (chain)
599 sk_X509_pop_free(chain, X509_free);
600
601 return ret;
602}
603
604/* Frees the contents of a cert_key_and_chain
605 */
606void ssl_sock_free_cert_key_and_chain_contents(struct cert_key_and_chain *ckch)
607{
608 if (!ckch)
609 return;
610
611 /* Free the certificate and set pointer to NULL */
612 if (ckch->cert)
613 X509_free(ckch->cert);
614 ckch->cert = NULL;
615
616 /* Free the key and set pointer to NULL */
617 if (ckch->key)
618 EVP_PKEY_free(ckch->key);
619 ckch->key = NULL;
620
621 /* Free each certificate in the chain */
622 if (ckch->chain)
623 sk_X509_pop_free(ckch->chain, X509_free);
624 ckch->chain = NULL;
625
626 if (ckch->dh)
627 DH_free(ckch->dh);
628 ckch->dh = NULL;
629
630 if (ckch->sctl) {
Willy Tarreau61cfdf42021-02-20 10:46:51 +0100631 ha_free(&ckch->sctl->area);
632 ha_free(&ckch->sctl);
William Lallemand03c331c2020-05-13 10:10:01 +0200633 }
634
635 if (ckch->ocsp_response) {
Willy Tarreau61cfdf42021-02-20 10:46:51 +0100636 ha_free(&ckch->ocsp_response->area);
637 ha_free(&ckch->ocsp_response);
William Lallemand03c331c2020-05-13 10:10:01 +0200638 }
639
640 if (ckch->ocsp_issuer)
641 X509_free(ckch->ocsp_issuer);
642 ckch->ocsp_issuer = NULL;
643}
644
645/*
646 *
647 * This function copy a cert_key_and_chain in memory
648 *
649 * It's used to try to apply changes on a ckch before committing them, because
650 * most of the time it's not possible to revert those changes
651 *
652 * Return a the dst or NULL
653 */
654struct cert_key_and_chain *ssl_sock_copy_cert_key_and_chain(struct cert_key_and_chain *src,
655 struct cert_key_and_chain *dst)
656{
William Lallemand6c096142021-02-23 14:45:45 +0100657 if (!src || !dst)
658 return NULL;
659
William Lallemand03c331c2020-05-13 10:10:01 +0200660 if (src->cert) {
661 dst->cert = src->cert;
662 X509_up_ref(src->cert);
663 }
664
665 if (src->key) {
666 dst->key = src->key;
667 EVP_PKEY_up_ref(src->key);
668 }
669
670 if (src->chain) {
671 dst->chain = X509_chain_up_ref(src->chain);
672 }
673
674 if (src->dh) {
675 DH_up_ref(src->dh);
676 dst->dh = src->dh;
677 }
678
679 if (src->sctl) {
680 struct buffer *sctl;
681
682 sctl = calloc(1, sizeof(*sctl));
683 if (!chunk_dup(sctl, src->sctl)) {
Willy Tarreau61cfdf42021-02-20 10:46:51 +0100684 ha_free(&sctl);
William Lallemand03c331c2020-05-13 10:10:01 +0200685 goto error;
686 }
687 dst->sctl = sctl;
688 }
689
690 if (src->ocsp_response) {
691 struct buffer *ocsp_response;
692
693 ocsp_response = calloc(1, sizeof(*ocsp_response));
694 if (!chunk_dup(ocsp_response, src->ocsp_response)) {
Willy Tarreau61cfdf42021-02-20 10:46:51 +0100695 ha_free(&ocsp_response);
William Lallemand03c331c2020-05-13 10:10:01 +0200696 goto error;
697 }
698 dst->ocsp_response = ocsp_response;
699 }
700
701 if (src->ocsp_issuer) {
702 X509_up_ref(src->ocsp_issuer);
703 dst->ocsp_issuer = src->ocsp_issuer;
704 }
705
706 return dst;
707
708error:
709
710 /* free everything */
711 ssl_sock_free_cert_key_and_chain_contents(dst);
712
713 return NULL;
714}
715
716/*
717 * return 0 on success or != 0 on failure
718 */
719int ssl_sock_load_issuer_file_into_ckch(const char *path, char *buf, struct cert_key_and_chain *ckch, char **err)
720{
721 int ret = 1;
722 BIO *in = NULL;
723 X509 *issuer;
724
725 if (buf) {
726 /* reading from a buffer */
727 in = BIO_new_mem_buf(buf, -1);
728 if (in == NULL) {
729 memprintf(err, "%sCan't allocate memory\n", err && *err ? *err : "");
730 goto end;
731 }
732
733 } else {
734 /* reading from a file */
735 in = BIO_new(BIO_s_file());
736 if (in == NULL)
737 goto end;
738
739 if (BIO_read_filename(in, path) <= 0)
740 goto end;
741 }
742
743 issuer = PEM_read_bio_X509_AUX(in, NULL, NULL, NULL);
744 if (!issuer) {
745 memprintf(err, "%s'%s' cannot be read or parsed'.\n",
746 err && *err ? *err : "", path);
747 goto end;
748 }
749 /* no error, fill ckch with new context, old context must be free */
750 if (ckch->ocsp_issuer)
751 X509_free(ckch->ocsp_issuer);
752 ckch->ocsp_issuer = issuer;
753 ret = 0;
754
755end:
756
757 ERR_clear_error();
758 if (in)
759 BIO_free(in);
760
761 return ret;
762}
763
764/******************** ckch_store functions ***********************************
765 * The ckch_store is a structure used to cache and index the SSL files used in
766 * configuration
767 */
768
769/*
770 * Free a ckch_store, its ckch, its instances and remove it from the ebtree
771 */
772void ckch_store_free(struct ckch_store *store)
773{
774 struct ckch_inst *inst, *inst_s;
775
776 if (!store)
777 return;
778
William Lallemandbd8e6ed2020-09-16 16:08:08 +0200779 ssl_sock_free_cert_key_and_chain_contents(store->ckch);
William Lallemand03c331c2020-05-13 10:10:01 +0200780
Willy Tarreau61cfdf42021-02-20 10:46:51 +0100781 ha_free(&store->ckch);
William Lallemand03c331c2020-05-13 10:10:01 +0200782
783 list_for_each_entry_safe(inst, inst_s, &store->ckch_inst, by_ckchs) {
784 ckch_inst_free(inst);
785 }
786 ebmb_delete(&store->node);
787 free(store);
788}
789
790/*
791 * create and initialize a ckch_store
792 * <path> is the key name
793 * <nmemb> is the number of store->ckch objects to allocate
794 *
795 * Return a ckch_store or NULL upon failure.
796 */
William Lallemandbd8e6ed2020-09-16 16:08:08 +0200797struct ckch_store *ckch_store_new(const char *filename)
William Lallemand03c331c2020-05-13 10:10:01 +0200798{
799 struct ckch_store *store;
800 int pathlen;
801
802 pathlen = strlen(filename);
803 store = calloc(1, sizeof(*store) + pathlen + 1);
804 if (!store)
805 return NULL;
806
William Lallemand03c331c2020-05-13 10:10:01 +0200807 memcpy(store->path, filename, pathlen + 1);
808
809 LIST_INIT(&store->ckch_inst);
810 LIST_INIT(&store->crtlist_entry);
811
William Lallemandbd8e6ed2020-09-16 16:08:08 +0200812 store->ckch = calloc(1, sizeof(*store->ckch));
William Lallemand03c331c2020-05-13 10:10:01 +0200813 if (!store->ckch)
814 goto error;
815
816 return store;
817error:
818 ckch_store_free(store);
819 return NULL;
820}
821
822/* allocate and duplicate a ckch_store
823 * Return a new ckch_store or NULL */
824struct ckch_store *ckchs_dup(const struct ckch_store *src)
825{
826 struct ckch_store *dst;
827
William Lallemand6c096142021-02-23 14:45:45 +0100828 if (!src)
829 return NULL;
830
William Lallemandbd8e6ed2020-09-16 16:08:08 +0200831 dst = ckch_store_new(src->path);
Eric Salama6ac61e32021-02-23 16:50:57 +0100832 if (!dst)
833 return NULL;
William Lallemand03c331c2020-05-13 10:10:01 +0200834
William Lallemandbd8e6ed2020-09-16 16:08:08 +0200835 if (!ssl_sock_copy_cert_key_and_chain(src->ckch, dst->ckch))
836 goto error;
William Lallemand03c331c2020-05-13 10:10:01 +0200837
838 return dst;
839
840error:
841 ckch_store_free(dst);
842
843 return NULL;
844}
845
846/*
847 * lookup a path into the ckchs tree.
848 */
849struct ckch_store *ckchs_lookup(char *path)
850{
851 struct ebmb_node *eb;
852
853 eb = ebst_lookup(&ckchs_tree, path);
854 if (!eb)
855 return NULL;
856
857 return ebmb_entry(eb, struct ckch_store, node);
858}
859
860/*
861 * This function allocate a ckch_store and populate it with certificates from files.
862 */
William Lallemandbd8e6ed2020-09-16 16:08:08 +0200863struct ckch_store *ckchs_load_cert_file(char *path, char **err)
William Lallemand03c331c2020-05-13 10:10:01 +0200864{
865 struct ckch_store *ckchs;
866
William Lallemandbd8e6ed2020-09-16 16:08:08 +0200867 ckchs = ckch_store_new(path);
William Lallemand03c331c2020-05-13 10:10:01 +0200868 if (!ckchs) {
869 memprintf(err, "%sunable to allocate memory.\n", err && *err ? *err : "");
870 goto end;
871 }
William Lallemand03c331c2020-05-13 10:10:01 +0200872
William Lallemandbd8e6ed2020-09-16 16:08:08 +0200873 if (ssl_sock_load_files_into_ckch(path, ckchs->ckch, err) == 1)
874 goto end;
William Lallemand03c331c2020-05-13 10:10:01 +0200875
William Lallemandbd8e6ed2020-09-16 16:08:08 +0200876 /* insert into the ckchs tree */
877 memcpy(ckchs->path, path, strlen(path) + 1);
878 ebst_insert(&ckchs_tree, &ckchs->node);
William Lallemand03c331c2020-05-13 10:10:01 +0200879 return ckchs;
880
881end:
882 ckch_store_free(ckchs);
883
884 return NULL;
885}
886
William Lallemandfa1d8b42020-05-13 15:46:10 +0200887
888/******************** ckch_inst functions ******************************/
889
890/* unlink a ckch_inst, free all SNIs, free the ckch_inst */
891/* The caller must use the lock of the bind_conf if used with inserted SNIs */
892void ckch_inst_free(struct ckch_inst *inst)
893{
894 struct sni_ctx *sni, *sni_s;
895
896 if (inst == NULL)
897 return;
898
899 list_for_each_entry_safe(sni, sni_s, &inst->sni_ctx, by_ckch_inst) {
900 SSL_CTX_free(sni->ctx);
Willy Tarreau2b718102021-04-21 07:32:39 +0200901 LIST_DELETE(&sni->by_ckch_inst);
William Lallemandfa1d8b42020-05-13 15:46:10 +0200902 ebmb_delete(&sni->name);
903 free(sni);
904 }
Remi Tricot-Le Bretonf3eedfe2021-01-25 17:19:44 +0100905 SSL_CTX_free(inst->ctx);
906 inst->ctx = NULL;
Willy Tarreau2b718102021-04-21 07:32:39 +0200907 LIST_DELETE(&inst->by_ckchs);
908 LIST_DELETE(&inst->by_crtlist_entry);
William Lallemandfa1d8b42020-05-13 15:46:10 +0200909 free(inst);
910}
911
912/* Alloc and init a ckch_inst */
913struct ckch_inst *ckch_inst_new()
914{
915 struct ckch_inst *ckch_inst;
916
917 ckch_inst = calloc(1, sizeof *ckch_inst);
918 if (!ckch_inst)
919 return NULL;
920
921 LIST_INIT(&ckch_inst->sni_ctx);
922 LIST_INIT(&ckch_inst->by_ckchs);
923 LIST_INIT(&ckch_inst->by_crtlist_entry);
924
925 return ckch_inst;
926}
927
William Lallemandda8584c2020-05-14 10:14:37 +0200928/*************************** CLI commands ***********************/
929
930/* Type of SSL payloads that can be updated over the CLI */
931
932enum {
933 CERT_TYPE_PEM = 0,
934 CERT_TYPE_KEY,
935#if ((defined SSL_CTRL_SET_TLSEXT_STATUS_REQ_CB && !defined OPENSSL_NO_OCSP) || defined OPENSSL_IS_BORINGSSL)
936 CERT_TYPE_OCSP,
937#endif
938 CERT_TYPE_ISSUER,
Ilya Shipitsinc47d6762021-02-13 11:45:33 +0500939#ifdef HAVE_SSL_SCTL
William Lallemandda8584c2020-05-14 10:14:37 +0200940 CERT_TYPE_SCTL,
941#endif
942 CERT_TYPE_MAX,
943};
944
945struct {
946 const char *ext;
947 int type;
948 int (*load)(const char *path, char *payload, struct cert_key_and_chain *ckch, char **err);
949 /* add a parsing callback */
950} cert_exts[CERT_TYPE_MAX+1] = {
951 [CERT_TYPE_PEM] = { "", CERT_TYPE_PEM, &ssl_sock_load_pem_into_ckch }, /* default mode, no extensions */
952 [CERT_TYPE_KEY] = { "key", CERT_TYPE_KEY, &ssl_sock_load_key_into_ckch },
953#if ((defined SSL_CTRL_SET_TLSEXT_STATUS_REQ_CB && !defined OPENSSL_NO_OCSP) || defined OPENSSL_IS_BORINGSSL)
954 [CERT_TYPE_OCSP] = { "ocsp", CERT_TYPE_OCSP, &ssl_sock_load_ocsp_response_from_file },
955#endif
Ilya Shipitsinc47d6762021-02-13 11:45:33 +0500956#ifdef HAVE_SSL_SCTL
William Lallemandda8584c2020-05-14 10:14:37 +0200957 [CERT_TYPE_SCTL] = { "sctl", CERT_TYPE_SCTL, &ssl_sock_load_sctl_from_file },
958#endif
959 [CERT_TYPE_ISSUER] = { "issuer", CERT_TYPE_ISSUER, &ssl_sock_load_issuer_file_into_ckch },
960 [CERT_TYPE_MAX] = { NULL, CERT_TYPE_MAX, NULL },
961};
962
963
964/* release function of the `show ssl cert' command */
965static void cli_release_show_cert(struct appctx *appctx)
966{
967 HA_SPIN_UNLOCK(CKCH_LOCK, &ckch_lock);
968}
969
970/* IO handler of "show ssl cert <filename>" */
971static int cli_io_handler_show_cert(struct appctx *appctx)
972{
973 struct buffer *trash = alloc_trash_chunk();
974 struct ebmb_node *node;
975 struct stream_interface *si = appctx->owner;
976 struct ckch_store *ckchs;
977
978 if (trash == NULL)
979 return 1;
980
Christopher Faulet7f339cc2022-06-03 10:46:40 +0200981 if (!appctx->ctx.ssl.old_ckchs && ckchs_transaction.old_ckchs) {
982 ckchs = ckchs_transaction.old_ckchs;
983 chunk_appendf(trash, "# transaction\n");
984 chunk_appendf(trash, "*%s\n", ckchs->path);
985 if (ci_putchk(si_ic(si), trash) == -1)
986 goto yield;
987 appctx->ctx.ssl.old_ckchs = ckchs_transaction.old_ckchs;
William Lallemandda8584c2020-05-14 10:14:37 +0200988 }
989
990 if (!appctx->ctx.cli.p0) {
991 chunk_appendf(trash, "# filename\n");
992 node = ebmb_first(&ckchs_tree);
993 } else {
994 node = &((struct ckch_store *)appctx->ctx.cli.p0)->node;
995 }
996 while (node) {
997 ckchs = ebmb_entry(node, struct ckch_store, node);
William Lallemand5685ccf2020-09-16 16:12:25 +0200998 chunk_appendf(trash, "%s\n", ckchs->path);
William Lallemandda8584c2020-05-14 10:14:37 +0200999
1000 node = ebmb_next(node);
1001 if (ci_putchk(si_ic(si), trash) == -1) {
1002 si_rx_room_blk(si);
1003 goto yield;
1004 }
1005 }
1006
1007 appctx->ctx.cli.p0 = NULL;
1008 free_trash_chunk(trash);
1009 return 1;
1010yield:
1011
1012 free_trash_chunk(trash);
1013 appctx->ctx.cli.p0 = ckchs;
1014 return 0; /* should come back */
1015}
1016
1017/*
1018 * Extract and format the DNS SAN extensions and copy result into a chuink
1019 * Return 0;
1020 */
1021#ifdef SSL_CTRL_SET_TLSEXT_HOSTNAME
1022static int ssl_sock_get_san_oneline(X509 *cert, struct buffer *out)
1023{
1024 int i;
1025 char *str;
1026 STACK_OF(GENERAL_NAME) *names = NULL;
1027
1028 names = X509_get_ext_d2i(cert, NID_subject_alt_name, NULL, NULL);
1029 if (names) {
1030 for (i = 0; i < sk_GENERAL_NAME_num(names); i++) {
1031 GENERAL_NAME *name = sk_GENERAL_NAME_value(names, i);
1032 if (i > 0)
1033 chunk_appendf(out, ", ");
1034 if (name->type == GEN_DNS) {
1035 if (ASN1_STRING_to_UTF8((unsigned char **)&str, name->d.dNSName) >= 0) {
1036 chunk_appendf(out, "DNS:%s", str);
1037 OPENSSL_free(str);
1038 }
1039 }
1040 }
1041 sk_GENERAL_NAME_pop_free(names, GENERAL_NAME_free);
1042 }
1043 return 0;
1044}
1045#endif
1046
1047
1048
1049
1050/* IO handler of the details "show ssl cert <filename>" */
1051static int cli_io_handler_show_cert_detail(struct appctx *appctx)
1052{
1053 struct stream_interface *si = appctx->owner;
1054 struct ckch_store *ckchs = appctx->ctx.cli.p0;
1055 struct buffer *out = alloc_trash_chunk();
1056 struct buffer *tmp = alloc_trash_chunk();
1057 X509_NAME *name = NULL;
1058 STACK_OF(X509) *chain;
1059 unsigned int len = 0;
1060 int write = -1;
1061 BIO *bio = NULL;
1062 int i;
1063
1064 if (!tmp || !out)
1065 goto end_no_putchk;
1066
William Lallemand5685ccf2020-09-16 16:12:25 +02001067 chunk_appendf(out, "Filename: ");
1068 if (ckchs == ckchs_transaction.new_ckchs)
1069 chunk_appendf(out, "*");
1070 chunk_appendf(out, "%s\n", ckchs->path);
William Lallemandda8584c2020-05-14 10:14:37 +02001071
William Lallemand5685ccf2020-09-16 16:12:25 +02001072 chunk_appendf(out, "Status: ");
1073 if (ckchs->ckch->cert == NULL)
1074 chunk_appendf(out, "Empty\n");
1075 else if (LIST_ISEMPTY(&ckchs->ckch_inst))
1076 chunk_appendf(out, "Unused\n");
1077 else
1078 chunk_appendf(out, "Used\n");
William Lallemandda8584c2020-05-14 10:14:37 +02001079
William Lallemand5685ccf2020-09-16 16:12:25 +02001080 if (ckchs->ckch->cert == NULL)
1081 goto end;
William Lallemandda8584c2020-05-14 10:14:37 +02001082
William Lallemand5685ccf2020-09-16 16:12:25 +02001083 chain = ckchs->ckch->chain;
1084 if (chain == NULL) {
1085 struct issuer_chain *issuer;
1086 issuer = ssl_get0_issuer_chain(ckchs->ckch->cert);
1087 if (issuer) {
1088 chain = issuer->chain;
1089 chunk_appendf(out, "Chain Filename: ");
1090 chunk_appendf(out, "%s\n", issuer->path);
William Lallemandda8584c2020-05-14 10:14:37 +02001091 }
William Lallemand5685ccf2020-09-16 16:12:25 +02001092 }
1093 chunk_appendf(out, "Serial: ");
1094 if (ssl_sock_get_serial(ckchs->ckch->cert, tmp) == -1)
1095 goto end;
1096 dump_binary(out, tmp->area, tmp->data);
1097 chunk_appendf(out, "\n");
William Lallemandda8584c2020-05-14 10:14:37 +02001098
William Lallemand5685ccf2020-09-16 16:12:25 +02001099 chunk_appendf(out, "notBefore: ");
1100 chunk_reset(tmp);
1101 if ((bio = BIO_new(BIO_s_mem())) == NULL)
1102 goto end;
1103 if (ASN1_TIME_print(bio, X509_getm_notBefore(ckchs->ckch->cert)) == 0)
1104 goto end;
1105 write = BIO_read(bio, tmp->area, tmp->size-1);
1106 tmp->area[write] = '\0';
1107 BIO_free(bio);
1108 bio = NULL;
1109 chunk_appendf(out, "%s\n", tmp->area);
William Lallemandda8584c2020-05-14 10:14:37 +02001110
William Lallemand5685ccf2020-09-16 16:12:25 +02001111 chunk_appendf(out, "notAfter: ");
1112 chunk_reset(tmp);
1113 if ((bio = BIO_new(BIO_s_mem())) == NULL)
1114 goto end;
1115 if (ASN1_TIME_print(bio, X509_getm_notAfter(ckchs->ckch->cert)) == 0)
1116 goto end;
1117 if ((write = BIO_read(bio, tmp->area, tmp->size-1)) <= 0)
1118 goto end;
1119 tmp->area[write] = '\0';
1120 BIO_free(bio);
1121 bio = NULL;
1122 chunk_appendf(out, "%s\n", tmp->area);
William Lallemandda8584c2020-05-14 10:14:37 +02001123
1124#ifdef SSL_CTRL_SET_TLSEXT_HOSTNAME
William Lallemand5685ccf2020-09-16 16:12:25 +02001125 chunk_appendf(out, "Subject Alternative Name: ");
1126 if (ssl_sock_get_san_oneline(ckchs->ckch->cert, out) == -1)
1127 goto end;
1128 *(out->area + out->data) = '\0';
1129 chunk_appendf(out, "\n");
William Lallemandda8584c2020-05-14 10:14:37 +02001130#endif
William Lallemand5685ccf2020-09-16 16:12:25 +02001131 chunk_reset(tmp);
1132 chunk_appendf(out, "Algorithm: ");
1133 if (cert_get_pkey_algo(ckchs->ckch->cert, tmp) == 0)
1134 goto end;
1135 chunk_appendf(out, "%s\n", tmp->area);
William Lallemandda8584c2020-05-14 10:14:37 +02001136
William Lallemand5685ccf2020-09-16 16:12:25 +02001137 chunk_reset(tmp);
1138 chunk_appendf(out, "SHA1 FingerPrint: ");
1139 if (X509_digest(ckchs->ckch->cert, EVP_sha1(), (unsigned char *) tmp->area, &len) == 0)
1140 goto end;
1141 tmp->data = len;
1142 dump_binary(out, tmp->area, tmp->data);
1143 chunk_appendf(out, "\n");
William Lallemandda8584c2020-05-14 10:14:37 +02001144
William Lallemand5685ccf2020-09-16 16:12:25 +02001145 chunk_appendf(out, "Subject: ");
1146 if ((name = X509_get_subject_name(ckchs->ckch->cert)) == NULL)
1147 goto end;
1148 if ((ssl_sock_get_dn_oneline(name, tmp)) == -1)
1149 goto end;
1150 *(tmp->area + tmp->data) = '\0';
1151 chunk_appendf(out, "%s\n", tmp->area);
1152
1153 chunk_appendf(out, "Issuer: ");
1154 if ((name = X509_get_issuer_name(ckchs->ckch->cert)) == NULL)
1155 goto end;
1156 if ((ssl_sock_get_dn_oneline(name, tmp)) == -1)
1157 goto end;
1158 *(tmp->area + tmp->data) = '\0';
1159 chunk_appendf(out, "%s\n", tmp->area);
1160
1161 /* Displays subject of each certificate in the chain */
1162 for (i = 0; i < sk_X509_num(chain); i++) {
1163 X509 *ca = sk_X509_value(chain, i);
1164
1165 chunk_appendf(out, "Chain Subject: ");
1166 if ((name = X509_get_subject_name(ca)) == NULL)
William Lallemandda8584c2020-05-14 10:14:37 +02001167 goto end;
1168 if ((ssl_sock_get_dn_oneline(name, tmp)) == -1)
1169 goto end;
1170 *(tmp->area + tmp->data) = '\0';
1171 chunk_appendf(out, "%s\n", tmp->area);
1172
William Lallemand5685ccf2020-09-16 16:12:25 +02001173 chunk_appendf(out, "Chain Issuer: ");
1174 if ((name = X509_get_issuer_name(ca)) == NULL)
William Lallemandda8584c2020-05-14 10:14:37 +02001175 goto end;
1176 if ((ssl_sock_get_dn_oneline(name, tmp)) == -1)
1177 goto end;
1178 *(tmp->area + tmp->data) = '\0';
1179 chunk_appendf(out, "%s\n", tmp->area);
William Lallemandda8584c2020-05-14 10:14:37 +02001180 }
1181
1182end:
1183 if (ci_putchk(si_ic(si), out) == -1) {
1184 si_rx_room_blk(si);
1185 goto yield;
1186 }
1187
1188end_no_putchk:
1189 if (bio)
1190 BIO_free(bio);
1191 free_trash_chunk(tmp);
1192 free_trash_chunk(out);
1193 return 1;
1194yield:
1195 free_trash_chunk(tmp);
1196 free_trash_chunk(out);
1197 return 0; /* should come back */
1198}
1199
1200/* parsing function for 'show ssl cert [certfile]' */
1201static int cli_parse_show_cert(char **args, char *payload, struct appctx *appctx, void *private)
1202{
1203 struct ckch_store *ckchs;
1204
1205 if (!cli_has_level(appctx, ACCESS_LVL_OPER))
1206 return cli_err(appctx, "Can't allocate memory!\n");
1207
1208 /* The operations on the CKCH architecture are locked so we can
1209 * manipulate ckch_store and ckch_inst */
1210 if (HA_SPIN_TRYLOCK(CKCH_LOCK, &ckch_lock))
1211 return cli_err(appctx, "Can't show!\nOperations on certificates are currently locked!\n");
1212
1213 /* check if there is a certificate to lookup */
1214 if (*args[3]) {
1215 if (*args[3] == '*') {
1216 if (!ckchs_transaction.new_ckchs)
1217 goto error;
1218
1219 ckchs = ckchs_transaction.new_ckchs;
1220
Tim Duesterhuse5ff1412021-01-02 22:31:53 +01001221 if (strcmp(args[3] + 1, ckchs->path) != 0)
William Lallemandda8584c2020-05-14 10:14:37 +02001222 goto error;
1223
1224 } else {
1225 if ((ckchs = ckchs_lookup(args[3])) == NULL)
1226 goto error;
1227
1228 }
1229
William Lallemandda8584c2020-05-14 10:14:37 +02001230 appctx->ctx.cli.p0 = ckchs;
1231 /* use the IO handler that shows details */
1232 appctx->io_handler = cli_io_handler_show_cert_detail;
1233 }
1234
1235 return 0;
1236
1237error:
1238 HA_SPIN_UNLOCK(CKCH_LOCK, &ckch_lock);
1239 return cli_err(appctx, "Can't display the certificate: Not found or the certificate is a bundle!\n");
1240}
1241
1242/* release function of the `set ssl cert' command, free things and unlock the spinlock */
1243static void cli_release_commit_cert(struct appctx *appctx)
1244{
Christopher Faulet82ae56d2022-05-31 16:37:01 +02001245 struct ckch_store *new_ckchs = appctx->ctx.ssl.new_ckchs;
William Lallemandda8584c2020-05-14 10:14:37 +02001246
1247 HA_SPIN_UNLOCK(CKCH_LOCK, &ckch_lock);
1248
Christopher Faulet82ae56d2022-05-31 16:37:01 +02001249 /* free every new sni_ctx and the new store, which are not in the trees so no spinlock there */
1250 if (new_ckchs)
William Lallemandda8584c2020-05-14 10:14:37 +02001251 ckch_store_free(new_ckchs);
Christopher Faulet82ae56d2022-05-31 16:37:01 +02001252 ha_free(&appctx->ctx.ssl.err);
William Lallemandda8584c2020-05-14 10:14:37 +02001253}
1254
1255/*
1256 * This function tries to create the new ckch_inst and their SNIs
1257 */
1258static int cli_io_handler_commit_cert(struct appctx *appctx)
1259{
1260 struct stream_interface *si = appctx->owner;
1261 int y = 0;
William Lallemandda8584c2020-05-14 10:14:37 +02001262 int errcode = 0;
Remi Tricot-Le Breton43899ec2021-04-21 15:32:46 +02001263 int retval = 0;
William Lallemandda8584c2020-05-14 10:14:37 +02001264 struct ckch_store *old_ckchs, *new_ckchs = NULL;
1265 struct ckch_inst *ckchi, *ckchis;
William Lallemandda8584c2020-05-14 10:14:37 +02001266 struct sni_ctx *sc0, *sc0s;
1267 struct crtlist_entry *entry;
1268
William Lallemandda8584c2020-05-14 10:14:37 +02001269
1270 if (unlikely(si_ic(si)->flags & (CF_WRITE_ERROR|CF_SHUTW)))
Christopher Faulet82ae56d2022-05-31 16:37:01 +02001271 goto end;
William Lallemandda8584c2020-05-14 10:14:37 +02001272
1273 while (1) {
1274 switch (appctx->st2) {
1275 case SETCERT_ST_INIT:
1276 /* This state just print the update message */
Christopher Faulet82ae56d2022-05-31 16:37:01 +02001277 chunk_printf(&trash, "Committing %s", ckchs_transaction.path);
1278 if (ci_putchk(si_ic(si), &trash) == -1)
William Lallemandda8584c2020-05-14 10:14:37 +02001279 goto yield;
William Lallemandda8584c2020-05-14 10:14:37 +02001280 appctx->st2 = SETCERT_ST_GEN;
1281 /* fallthrough */
1282 case SETCERT_ST_GEN:
1283 /*
1284 * This state generates the ckch instances with their
1285 * sni_ctxs and SSL_CTX.
1286 *
1287 * Since the SSL_CTX generation can be CPU consumer, we
1288 * yield every 10 instances.
1289 */
1290
1291 old_ckchs = appctx->ctx.ssl.old_ckchs;
1292 new_ckchs = appctx->ctx.ssl.new_ckchs;
1293
William Lallemandda8584c2020-05-14 10:14:37 +02001294 /* get the next ckchi to regenerate */
1295 ckchi = appctx->ctx.ssl.next_ckchi;
1296 /* we didn't start yet, set it to the first elem */
1297 if (ckchi == NULL)
1298 ckchi = LIST_ELEM(old_ckchs->ckch_inst.n, typeof(ckchi), by_ckchs);
1299
1300 /* walk through the old ckch_inst and creates new ckch_inst using the updated ckchs */
1301 list_for_each_entry_from(ckchi, &old_ckchs->ckch_inst, by_ckchs) {
1302 struct ckch_inst *new_inst;
1303 char **sni_filter = NULL;
1304 int fcount = 0;
1305
Christopher Faulet82ae56d2022-05-31 16:37:01 +02001306 /* save the next ckchi to compute in case of yield */
1307 appctx->ctx.ssl.next_ckchi = ckchi;
1308
William Lallemandda8584c2020-05-14 10:14:37 +02001309 /* it takes a lot of CPU to creates SSL_CTXs, so we yield every 10 CKCH instances */
1310 if (y >= 10) {
Christopher Faulet82ae56d2022-05-31 16:37:01 +02001311 si_rx_endp_more(si);
William Lallemandda8584c2020-05-14 10:14:37 +02001312 goto yield;
1313 }
1314
Christopher Faulet82ae56d2022-05-31 16:37:01 +02001315 /* display one dot per new instance */
1316 if (ci_putstr(si_ic(si), ".") == -1)
1317 goto yield;
1318
William Lallemandda8584c2020-05-14 10:14:37 +02001319 if (ckchi->crtlist_entry) {
1320 sni_filter = ckchi->crtlist_entry->filters;
1321 fcount = ckchi->crtlist_entry->fcount;
1322 }
1323
Christopher Faulet82ae56d2022-05-31 16:37:01 +02001324 appctx->ctx.ssl.err = NULL;
Remi Tricot-Le Bretond817dc72021-01-25 17:19:43 +01001325 if (ckchi->is_server_instance)
Christopher Faulet82ae56d2022-05-31 16:37:01 +02001326 errcode |= ckch_inst_new_load_srv_store(new_ckchs->path, new_ckchs, &new_inst, &appctx->ctx.ssl.err);
Remi Tricot-Le Bretonf3eedfe2021-01-25 17:19:44 +01001327 else
Christopher Faulet82ae56d2022-05-31 16:37:01 +02001328 errcode |= ckch_inst_new_load_store(new_ckchs->path, new_ckchs, ckchi->bind_conf, ckchi->ssl_conf, sni_filter, fcount, &new_inst, &appctx->ctx.ssl.err);
William Lallemandda8584c2020-05-14 10:14:37 +02001329
Christopher Faulet82ae56d2022-05-31 16:37:01 +02001330 if (errcode & ERR_CODE) {
1331 appctx->st2 = SETCERT_ST_ERROR;
William Lallemandda8584c2020-05-14 10:14:37 +02001332 goto error;
Christopher Faulet82ae56d2022-05-31 16:37:01 +02001333 }
William Lallemandda8584c2020-05-14 10:14:37 +02001334
1335 /* if the previous ckchi was used as the default */
1336 if (ckchi->is_default)
1337 new_inst->is_default = 1;
1338
Remi Tricot-Le Bretonf3eedfe2021-01-25 17:19:44 +01001339 new_inst->is_server_instance = ckchi->is_server_instance;
1340 new_inst->server = ckchi->server;
1341 /* Create a new SSL_CTX and link it to the new instance. */
1342 if (new_inst->is_server_instance) {
Remi Tricot-Le Breton43899ec2021-04-21 15:32:46 +02001343 retval = ssl_sock_prepare_srv_ssl_ctx(ckchi->server, new_inst->ctx);
Christopher Faulet82ae56d2022-05-31 16:37:01 +02001344 if (retval) {
1345 appctx->st2 = SETCERT_ST_ERROR;
Remi Tricot-Le Bretonf3eedfe2021-01-25 17:19:44 +01001346 goto error;
Christopher Faulet82ae56d2022-05-31 16:37:01 +02001347 }
Remi Tricot-Le Bretonf3eedfe2021-01-25 17:19:44 +01001348 }
1349
William Lallemanda55685b2020-12-15 14:57:46 +01001350 /* create the link to the crtlist_entry */
1351 new_inst->crtlist_entry = ckchi->crtlist_entry;
1352
William Lallemandda8584c2020-05-14 10:14:37 +02001353 /* we need to initialize the SSL_CTX generated */
1354 /* this iterate on the newly generated SNIs in the new instance to prepare their SSL_CTX */
1355 list_for_each_entry_safe(sc0, sc0s, &new_inst->sni_ctx, by_ckch_inst) {
1356 if (!sc0->order) { /* we initialized only the first SSL_CTX because it's the same in the other sni_ctx's */
Christopher Faulet82ae56d2022-05-31 16:37:01 +02001357 appctx->ctx.ssl.err = NULL;
1358 errcode |= ssl_sock_prepare_ctx(ckchi->bind_conf, ckchi->ssl_conf, sc0->ctx, &appctx->ctx.ssl.err);
1359 if (errcode & ERR_CODE) {
1360 appctx->st2 = SETCERT_ST_ERROR;
William Lallemandda8584c2020-05-14 10:14:37 +02001361 goto error;
Christopher Faulet82ae56d2022-05-31 16:37:01 +02001362 }
William Lallemandda8584c2020-05-14 10:14:37 +02001363 }
1364 }
1365
William Lallemandda8584c2020-05-14 10:14:37 +02001366 /* link the new ckch_inst to the duplicate */
Willy Tarreau2b718102021-04-21 07:32:39 +02001367 LIST_APPEND(&new_ckchs->ckch_inst, &new_inst->by_ckchs);
William Lallemandda8584c2020-05-14 10:14:37 +02001368 y++;
1369 }
1370 appctx->st2 = SETCERT_ST_INSERT;
1371 /* fallthrough */
1372 case SETCERT_ST_INSERT:
1373 /* The generation is finished, we can insert everything */
1374
1375 old_ckchs = appctx->ctx.ssl.old_ckchs;
1376 new_ckchs = appctx->ctx.ssl.new_ckchs;
1377
William Lallemandda8584c2020-05-14 10:14:37 +02001378 /* get the list of crtlist_entry in the old store, and update the pointers to the store */
1379 LIST_SPLICE(&new_ckchs->crtlist_entry, &old_ckchs->crtlist_entry);
1380 list_for_each_entry(entry, &new_ckchs->crtlist_entry, by_ckch_store) {
1381 ebpt_delete(&entry->node);
1382 /* change the ptr and reinsert the node */
1383 entry->node.key = new_ckchs;
1384 ebpt_insert(&entry->crtlist->entries, &entry->node);
1385 }
1386
William Lallemanda55685b2020-12-15 14:57:46 +01001387 /* insert the new ckch_insts in the crtlist_entry */
1388 list_for_each_entry(ckchi, &new_ckchs->ckch_inst, by_ckchs) {
1389 if (ckchi->crtlist_entry)
Willy Tarreau2b718102021-04-21 07:32:39 +02001390 LIST_INSERT(&ckchi->crtlist_entry->ckch_inst, &ckchi->by_crtlist_entry);
William Lallemanda55685b2020-12-15 14:57:46 +01001391 }
1392
William Lallemandda8584c2020-05-14 10:14:37 +02001393 /* First, we insert every new SNIs in the trees, also replace the default_ctx */
1394 list_for_each_entry_safe(ckchi, ckchis, &new_ckchs->ckch_inst, by_ckchs) {
Remi Tricot-Le Bretonf3eedfe2021-01-25 17:19:44 +01001395 /* The bind_conf will be null on server ckch_instances. */
1396 if (ckchi->is_server_instance) {
William Lallemande0de0a62021-02-03 18:51:01 +01001397 int i;
William Lallemand3ce6eed2021-02-08 10:43:44 +01001398 /* a lock is needed here since we have to free the SSL cache */
1399 HA_RWLOCK_WRLOCK(SSL_SERVER_LOCK, &ckchi->server->ssl_ctx.lock);
William Lallemand1dedb0a2021-01-26 10:18:57 +01001400 /* free the server current SSL_CTX */
1401 SSL_CTX_free(ckchi->server->ssl_ctx.ctx);
Remi Tricot-Le Bretonf3eedfe2021-01-25 17:19:44 +01001402 /* Actual ssl context update */
William Lallemand1dedb0a2021-01-26 10:18:57 +01001403 SSL_CTX_up_ref(ckchi->ctx);
Remi Tricot-Le Bretonf3eedfe2021-01-25 17:19:44 +01001404 ckchi->server->ssl_ctx.ctx = ckchi->ctx;
William Lallemand1dedb0a2021-01-26 10:18:57 +01001405 ckchi->server->ssl_ctx.inst = ckchi;
Remi Tricot-Le Bretonf3eedfe2021-01-25 17:19:44 +01001406
William Lallemande0de0a62021-02-03 18:51:01 +01001407 /* flush the session cache of the server */
William Lallemandc78ac5a2021-11-17 02:59:21 +01001408 for (i = 0; i < global.nbthread; i++) {
William Lallemand6cd79952021-11-23 15:15:09 +01001409 ha_free(&ckchi->server->ssl_ctx.reused_sess[i].ptr);
William Lallemandc78ac5a2021-11-17 02:59:21 +01001410 ha_free(&ckchi->server->ssl_ctx.reused_sess[i].sni);
1411 }
Willy Tarreau61cfdf42021-02-20 10:46:51 +01001412
William Lallemand3ce6eed2021-02-08 10:43:44 +01001413 HA_RWLOCK_WRUNLOCK(SSL_SERVER_LOCK, &ckchi->server->ssl_ctx.lock);
William Lallemande0de0a62021-02-03 18:51:01 +01001414
William Lallemand1dedb0a2021-01-26 10:18:57 +01001415 } else {
Remi Tricot-Le Bretonf3eedfe2021-01-25 17:19:44 +01001416 HA_RWLOCK_WRLOCK(SNI_LOCK, &ckchi->bind_conf->sni_lock);
1417 ssl_sock_load_cert_sni(ckchi, ckchi->bind_conf);
1418 HA_RWLOCK_WRUNLOCK(SNI_LOCK, &ckchi->bind_conf->sni_lock);
1419 }
William Lallemandda8584c2020-05-14 10:14:37 +02001420 }
1421
1422 /* delete the old sni_ctx, the old ckch_insts and the ckch_store */
1423 list_for_each_entry_safe(ckchi, ckchis, &old_ckchs->ckch_inst, by_ckchs) {
William Lallemandda8584c2020-05-14 10:14:37 +02001424
William Lallemand1dedb0a2021-01-26 10:18:57 +01001425 if (ckchi->is_server_instance) {
1426 /* no lock for servers */
1427 ckch_inst_free(ckchi);
1428 } else {
1429 struct bind_conf __maybe_unused *bind_conf = ckchi->bind_conf;
1430
1431 HA_RWLOCK_WRLOCK(SNI_LOCK, &bind_conf->sni_lock);
1432 ckch_inst_free(ckchi);
1433 HA_RWLOCK_WRUNLOCK(SNI_LOCK, &bind_conf->sni_lock);
1434 }
William Lallemandda8584c2020-05-14 10:14:37 +02001435 }
1436
1437 /* Replace the old ckchs by the new one */
1438 ckch_store_free(old_ckchs);
1439 ebst_insert(&ckchs_tree, &new_ckchs->node);
Christopher Faulet82ae56d2022-05-31 16:37:01 +02001440 appctx->ctx.ssl.old_ckchs = appctx->ctx.ssl.new_ckchs = NULL;
1441 appctx->st2 = SETCERT_ST_SUCCESS;
1442 /* fallthrough */
1443 case SETCERT_ST_SUCCESS:
1444 if (ci_putstr(si_ic(si), "\nSuccess!\n") == -1)
1445 goto yield;
William Lallemandda8584c2020-05-14 10:14:37 +02001446 appctx->st2 = SETCERT_ST_FIN;
1447 /* fallthrough */
1448 case SETCERT_ST_FIN:
1449 /* we achieved the transaction, we can set everything to NULL */
William Lallemandda8584c2020-05-14 10:14:37 +02001450 ckchs_transaction.new_ckchs = NULL;
1451 ckchs_transaction.old_ckchs = NULL;
Christopher Faulet2ec78222022-05-31 18:07:59 +02001452 ckchs_transaction.path = NULL;
William Lallemandda8584c2020-05-14 10:14:37 +02001453 goto end;
Christopher Faulet82ae56d2022-05-31 16:37:01 +02001454
1455 case SETCERT_ST_ERROR:
1456 error:
1457 chunk_printf(&trash, "\n%sFailed!\n", appctx->ctx.ssl.err);
1458 if (ci_putchk(si_ic(si), &trash) == -1)
1459 goto yield;
1460 appctx->st2 = SETCERT_ST_FIN;
1461 break;
William Lallemandda8584c2020-05-14 10:14:37 +02001462 }
1463 }
1464end:
William Lallemandda8584c2020-05-14 10:14:37 +02001465 /* success: call the release function and don't come back */
1466 return 1;
Christopher Faulet82ae56d2022-05-31 16:37:01 +02001467
William Lallemandda8584c2020-05-14 10:14:37 +02001468yield:
Christopher Faulet82ae56d2022-05-31 16:37:01 +02001469 si_rx_room_blk(si);
William Lallemandda8584c2020-05-14 10:14:37 +02001470 return 0; /* should come back */
William Lallemandda8584c2020-05-14 10:14:37 +02001471}
1472
1473/*
1474 * Parsing function of 'commit ssl cert'
1475 */
1476static int cli_parse_commit_cert(char **args, char *payload, struct appctx *appctx, void *private)
1477{
1478 char *err = NULL;
1479
1480 if (!cli_has_level(appctx, ACCESS_LVL_ADMIN))
1481 return 1;
1482
1483 if (!*args[3])
1484 return cli_err(appctx, "'commit ssl cert expects a filename\n");
1485
1486 /* The operations on the CKCH architecture are locked so we can
1487 * manipulate ckch_store and ckch_inst */
1488 if (HA_SPIN_TRYLOCK(CKCH_LOCK, &ckch_lock))
1489 return cli_err(appctx, "Can't commit the certificate!\nOperations on certificates are currently locked!\n");
1490
1491 if (!ckchs_transaction.path) {
1492 memprintf(&err, "No ongoing transaction! !\n");
1493 goto error;
1494 }
1495
1496 if (strcmp(ckchs_transaction.path, args[3]) != 0) {
1497 memprintf(&err, "The ongoing transaction is about '%s' but you are trying to set '%s'\n", ckchs_transaction.path, args[3]);
1498 goto error;
1499 }
1500
William Lallemand5685ccf2020-09-16 16:12:25 +02001501 /* if a certificate is here, a private key must be here too */
1502 if (ckchs_transaction.new_ckchs->ckch->cert && !ckchs_transaction.new_ckchs->ckch->key) {
1503 memprintf(&err, "The transaction must contain at least a certificate and a private key!\n");
1504 goto error;
1505 }
William Lallemanda9419522020-06-24 16:26:41 +02001506
William Lallemand5685ccf2020-09-16 16:12:25 +02001507 if (!X509_check_private_key(ckchs_transaction.new_ckchs->ckch->cert, ckchs_transaction.new_ckchs->ckch->key)) {
1508 memprintf(&err, "inconsistencies between private key and certificate loaded '%s'.\n", ckchs_transaction.path);
1509 goto error;
William Lallemandda8584c2020-05-14 10:14:37 +02001510 }
1511
1512 /* init the appctx structure */
1513 appctx->st2 = SETCERT_ST_INIT;
1514 appctx->ctx.ssl.next_ckchi = NULL;
1515 appctx->ctx.ssl.new_ckchs = ckchs_transaction.new_ckchs;
1516 appctx->ctx.ssl.old_ckchs = ckchs_transaction.old_ckchs;
1517
1518 /* we don't unlock there, it will be unlock after the IO handler, in the release handler */
1519 return 0;
1520
1521error:
1522
1523 HA_SPIN_UNLOCK(CKCH_LOCK, &ckch_lock);
1524 err = memprintf(&err, "%sCan't commit %s!\n", err ? err : "", args[3]);
1525
1526 return cli_dynerr(appctx, err);
1527}
1528
1529
1530
1531
1532/*
1533 * Parsing function of `set ssl cert`, it updates or creates a temporary ckch.
1534 */
1535static int cli_parse_set_cert(char **args, char *payload, struct appctx *appctx, void *private)
1536{
1537 struct ckch_store *new_ckchs = NULL;
1538 struct ckch_store *old_ckchs = NULL;
1539 char *err = NULL;
1540 int i;
William Lallemandda8584c2020-05-14 10:14:37 +02001541 int errcode = 0;
1542 char *end;
1543 int type = CERT_TYPE_PEM;
1544 struct cert_key_and_chain *ckch;
1545 struct buffer *buf;
1546
1547 if (!cli_has_level(appctx, ACCESS_LVL_ADMIN))
1548 return 1;
1549
William Lallemandda8584c2020-05-14 10:14:37 +02001550 if (!*args[3] || !payload)
1551 return cli_err(appctx, "'set ssl cert expects a filename and a certificate as a payload\n");
1552
1553 /* The operations on the CKCH architecture are locked so we can
1554 * manipulate ckch_store and ckch_inst */
1555 if (HA_SPIN_TRYLOCK(CKCH_LOCK, &ckch_lock))
1556 return cli_err(appctx, "Can't update the certificate!\nOperations on certificates are currently locked!\n");
1557
William Lallemand5ba80d62021-05-04 16:17:27 +02001558 if ((buf = alloc_trash_chunk()) == NULL) {
1559 memprintf(&err, "%sCan't allocate memory\n", err ? err : "");
1560 errcode |= ERR_ALERT | ERR_FATAL;
1561 goto end;
1562 }
William Lallemande5ff4ad2020-06-08 09:40:37 +02001563
William Lallemandda8584c2020-05-14 10:14:37 +02001564 if (!chunk_strcpy(buf, args[3])) {
1565 memprintf(&err, "%sCan't allocate memory\n", err ? err : "");
1566 errcode |= ERR_ALERT | ERR_FATAL;
1567 goto end;
1568 }
1569
1570 /* check which type of file we want to update */
1571 for (i = 0; cert_exts[i].type < CERT_TYPE_MAX; i++) {
1572 end = strrchr(buf->area, '.');
Tim Duesterhuse5ff1412021-01-02 22:31:53 +01001573 if (end && *cert_exts[i].ext && (strcmp(end + 1, cert_exts[i].ext) == 0)) {
William Lallemandda8584c2020-05-14 10:14:37 +02001574 *end = '\0';
William Lallemand089c1382020-10-23 17:35:12 +02001575 buf->data = strlen(buf->area);
William Lallemandda8584c2020-05-14 10:14:37 +02001576 type = cert_exts[i].type;
1577 break;
1578 }
1579 }
1580
1581 appctx->ctx.ssl.old_ckchs = NULL;
1582 appctx->ctx.ssl.new_ckchs = NULL;
1583
1584 /* if there is an ongoing transaction */
1585 if (ckchs_transaction.path) {
William Lallemandda8584c2020-05-14 10:14:37 +02001586 /* if there is an ongoing transaction, check if this is the same file */
1587 if (strcmp(ckchs_transaction.path, buf->area) != 0) {
William Lallemand089c1382020-10-23 17:35:12 +02001588 /* we didn't find the transaction, must try more cases below */
1589
1590 /* if the del-ext option is activated we should try to take a look at a ".crt" too. */
1591 if (type != CERT_TYPE_PEM && global_ssl.extra_files_noext) {
1592 if (!chunk_strcat(buf, ".crt")) {
1593 memprintf(&err, "%sCan't allocate memory\n", err ? err : "");
1594 errcode |= ERR_ALERT | ERR_FATAL;
1595 goto end;
1596 }
1597
1598 if (strcmp(ckchs_transaction.path, buf->area) != 0) {
1599 /* remove .crt of the error message */
1600 *(b_orig(buf) + b_data(buf) + strlen(".crt")) = '\0';
1601 b_sub(buf, strlen(".crt"));
1602
1603 memprintf(&err, "The ongoing transaction is about '%s' but you are trying to set '%s'\n", ckchs_transaction.path, buf->area);
1604 errcode |= ERR_ALERT | ERR_FATAL;
1605 goto end;
1606 }
1607 }
William Lallemandda8584c2020-05-14 10:14:37 +02001608 }
1609
1610 appctx->ctx.ssl.old_ckchs = ckchs_transaction.new_ckchs;
1611
1612 } else {
William Lallemandda8584c2020-05-14 10:14:37 +02001613
William Lallemand95fefa12020-09-09 12:01:33 +02001614 /* lookup for the certificate in the tree */
1615 appctx->ctx.ssl.old_ckchs = ckchs_lookup(buf->area);
William Lallemand089c1382020-10-23 17:35:12 +02001616
1617 if (!appctx->ctx.ssl.old_ckchs) {
1618 /* if the del-ext option is activated we should try to take a look at a ".crt" too. */
1619 if (type != CERT_TYPE_PEM && global_ssl.extra_files_noext) {
1620 if (!chunk_strcat(buf, ".crt")) {
1621 memprintf(&err, "%sCan't allocate memory\n", err ? err : "");
1622 errcode |= ERR_ALERT | ERR_FATAL;
1623 goto end;
1624 }
1625 appctx->ctx.ssl.old_ckchs = ckchs_lookup(buf->area);
1626 }
1627 }
William Lallemandda8584c2020-05-14 10:14:37 +02001628 }
1629
1630 if (!appctx->ctx.ssl.old_ckchs) {
1631 memprintf(&err, "%sCan't replace a certificate which is not referenced by the configuration!\n",
1632 err ? err : "");
1633 errcode |= ERR_ALERT | ERR_FATAL;
1634 goto end;
1635 }
1636
William Lallemandda8584c2020-05-14 10:14:37 +02001637 old_ckchs = appctx->ctx.ssl.old_ckchs;
1638
1639 /* duplicate the ckch store */
1640 new_ckchs = ckchs_dup(old_ckchs);
1641 if (!new_ckchs) {
1642 memprintf(&err, "%sCannot allocate memory!\n",
1643 err ? err : "");
1644 errcode |= ERR_ALERT | ERR_FATAL;
1645 goto end;
1646 }
1647
William Lallemand95fefa12020-09-09 12:01:33 +02001648 ckch = new_ckchs->ckch;
William Lallemandda8584c2020-05-14 10:14:37 +02001649
1650 /* appply the change on the duplicate */
1651 if (cert_exts[type].load(buf->area, payload, ckch, &err) != 0) {
1652 memprintf(&err, "%sCan't load the payload\n", err ? err : "");
1653 errcode |= ERR_ALERT | ERR_FATAL;
1654 goto end;
1655 }
1656
1657 appctx->ctx.ssl.new_ckchs = new_ckchs;
1658
1659 /* we succeed, we can save the ckchs in the transaction */
1660
1661 /* if there wasn't a transaction, update the old ckchs */
1662 if (!ckchs_transaction.old_ckchs) {
1663 ckchs_transaction.old_ckchs = appctx->ctx.ssl.old_ckchs;
Christopher Faulet2ec78222022-05-31 18:07:59 +02001664 ckchs_transaction.path = appctx->ctx.ssl.old_ckchs->path;
William Lallemandda8584c2020-05-14 10:14:37 +02001665 err = memprintf(&err, "Transaction created for certificate %s!\n", ckchs_transaction.path);
1666 } else {
1667 err = memprintf(&err, "Transaction updated for certificate %s!\n", ckchs_transaction.path);
1668
1669 }
1670
1671 /* free the previous ckchs if there was a transaction */
1672 ckch_store_free(ckchs_transaction.new_ckchs);
1673
1674 ckchs_transaction.new_ckchs = appctx->ctx.ssl.new_ckchs;
1675
1676
1677 /* creates the SNI ctxs later in the IO handler */
1678
1679end:
1680 free_trash_chunk(buf);
1681
1682 if (errcode & ERR_CODE) {
1683
1684 ckch_store_free(appctx->ctx.ssl.new_ckchs);
1685 appctx->ctx.ssl.new_ckchs = NULL;
1686
1687 appctx->ctx.ssl.old_ckchs = NULL;
1688
William Lallemandda8584c2020-05-14 10:14:37 +02001689 HA_SPIN_UNLOCK(CKCH_LOCK, &ckch_lock);
1690 return cli_dynerr(appctx, memprintf(&err, "%sCan't update %s!\n", err ? err : "", args[3]));
1691 } else {
1692
1693 HA_SPIN_UNLOCK(CKCH_LOCK, &ckch_lock);
1694 return cli_dynmsg(appctx, LOG_NOTICE, err);
1695 }
1696 /* TODO: handle the ERR_WARN which are not handled because of the io_handler */
1697}
1698
1699/* parsing function of 'abort ssl cert' */
1700static int cli_parse_abort_cert(char **args, char *payload, struct appctx *appctx, void *private)
1701{
1702 char *err = NULL;
1703
1704 if (!cli_has_level(appctx, ACCESS_LVL_ADMIN))
1705 return 1;
1706
1707 if (!*args[3])
1708 return cli_err(appctx, "'abort ssl cert' expects a filename\n");
1709
1710 /* The operations on the CKCH architecture are locked so we can
1711 * manipulate ckch_store and ckch_inst */
1712 if (HA_SPIN_TRYLOCK(CKCH_LOCK, &ckch_lock))
1713 return cli_err(appctx, "Can't abort!\nOperations on certificates are currently locked!\n");
1714
1715 if (!ckchs_transaction.path) {
1716 memprintf(&err, "No ongoing transaction!\n");
1717 goto error;
1718 }
1719
1720 if (strcmp(ckchs_transaction.path, args[3]) != 0) {
1721 memprintf(&err, "The ongoing transaction is about '%s' but you are trying to abort a transaction for '%s'\n", ckchs_transaction.path, args[3]);
1722 goto error;
1723 }
1724
1725 /* Only free the ckchs there, because the SNI and instances were not generated yet */
1726 ckch_store_free(ckchs_transaction.new_ckchs);
1727 ckchs_transaction.new_ckchs = NULL;
William Lallemandda8584c2020-05-14 10:14:37 +02001728 ckchs_transaction.old_ckchs = NULL;
Christopher Faulet2ec78222022-05-31 18:07:59 +02001729 ckchs_transaction.path = NULL;
William Lallemandda8584c2020-05-14 10:14:37 +02001730
1731 HA_SPIN_UNLOCK(CKCH_LOCK, &ckch_lock);
1732
1733 err = memprintf(&err, "Transaction aborted for certificate '%s'!\n", args[3]);
1734 return cli_dynmsg(appctx, LOG_NOTICE, err);
1735
1736error:
1737 HA_SPIN_UNLOCK(CKCH_LOCK, &ckch_lock);
1738
1739 return cli_dynerr(appctx, err);
1740}
1741
1742/* parsing function of 'new ssl cert' */
1743static int cli_parse_new_cert(char **args, char *payload, struct appctx *appctx, void *private)
1744{
1745 struct ckch_store *store;
1746 char *err = NULL;
1747 char *path;
1748
1749 if (!cli_has_level(appctx, ACCESS_LVL_ADMIN))
1750 return 1;
1751
1752 if (!*args[3])
1753 return cli_err(appctx, "'new ssl cert' expects a filename\n");
1754
1755 path = args[3];
1756
1757 /* The operations on the CKCH architecture are locked so we can
1758 * manipulate ckch_store and ckch_inst */
1759 if (HA_SPIN_TRYLOCK(CKCH_LOCK, &ckch_lock))
1760 return cli_err(appctx, "Can't create a certificate!\nOperations on certificates are currently locked!\n");
1761
1762 store = ckchs_lookup(path);
1763 if (store != NULL) {
1764 memprintf(&err, "Certificate '%s' already exists!\n", path);
1765 store = NULL; /* we don't want to free it */
1766 goto error;
1767 }
1768 /* we won't support multi-certificate bundle here */
William Lallemandbd8e6ed2020-09-16 16:08:08 +02001769 store = ckch_store_new(path);
William Lallemandda8584c2020-05-14 10:14:37 +02001770 if (!store) {
1771 memprintf(&err, "unable to allocate memory.\n");
1772 goto error;
1773 }
1774
1775 /* insert into the ckchs tree */
1776 ebst_insert(&ckchs_tree, &store->node);
1777 memprintf(&err, "New empty certificate store '%s'!\n", args[3]);
1778
1779 HA_SPIN_UNLOCK(CKCH_LOCK, &ckch_lock);
1780 return cli_dynmsg(appctx, LOG_NOTICE, err);
1781error:
1782 free(store);
1783 HA_SPIN_UNLOCK(CKCH_LOCK, &ckch_lock);
1784 return cli_dynerr(appctx, err);
1785}
1786
1787/* parsing function of 'del ssl cert' */
1788static int cli_parse_del_cert(char **args, char *payload, struct appctx *appctx, void *private)
1789{
1790 struct ckch_store *store;
1791 char *err = NULL;
1792 char *filename;
1793
1794 if (!cli_has_level(appctx, ACCESS_LVL_ADMIN))
1795 return 1;
1796
1797 if (!*args[3])
1798 return cli_err(appctx, "'del ssl cert' expects a certificate name\n");
1799
1800 if (HA_SPIN_TRYLOCK(CKCH_LOCK, &ckch_lock))
1801 return cli_err(appctx, "Can't delete the certificate!\nOperations on certificates are currently locked!\n");
1802
1803 filename = args[3];
1804
Christopher Faulet2ecaf332022-05-31 18:04:25 +02001805 if (ckchs_transaction.path && strcmp(ckchs_transaction.path, filename) == 0) {
1806 memprintf(&err, "ongoing transaction for the certificate '%s'", filename);
1807 goto error;
1808 }
1809
William Lallemandda8584c2020-05-14 10:14:37 +02001810 store = ckchs_lookup(filename);
1811 if (store == NULL) {
1812 memprintf(&err, "certificate '%s' doesn't exist!\n", filename);
1813 goto error;
1814 }
1815 if (!LIST_ISEMPTY(&store->ckch_inst)) {
1816 memprintf(&err, "certificate '%s' in use, can't be deleted!\n", filename);
1817 goto error;
1818 }
1819
1820 ebmb_delete(&store->node);
1821 ckch_store_free(store);
1822
1823 memprintf(&err, "Certificate '%s' deleted!\n", filename);
1824
1825 HA_SPIN_UNLOCK(CKCH_LOCK, &ckch_lock);
1826 return cli_dynmsg(appctx, LOG_NOTICE, err);
1827
1828error:
1829 memprintf(&err, "Can't remove the certificate: %s\n", err ? err : "");
1830 HA_SPIN_UNLOCK(CKCH_LOCK, &ckch_lock);
1831 return cli_dynerr(appctx, err);
1832}
1833
William Lallemandee8530c2020-06-23 18:19:42 +02001834void ckch_deinit()
1835{
1836 struct eb_node *node, *next;
1837 struct ckch_store *store;
1838
1839 node = eb_first(&ckchs_tree);
1840 while (node) {
1841 next = eb_next(node);
1842 store = ebmb_entry(node, struct ckch_store, node);
1843 ckch_store_free(store);
1844 node = next;
1845 }
1846}
William Lallemandda8584c2020-05-14 10:14:37 +02001847
1848/* register cli keywords */
1849static struct cli_kw_list cli_kws = {{ },{
Willy Tarreaub205bfd2021-05-07 11:38:37 +02001850 { { "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 },
1851 { { "set", "ssl", "cert", NULL }, "set ssl cert <certfile> <payload> : replace a certificate file", cli_parse_set_cert, NULL, NULL },
1852 { { "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 },
1853 { { "abort", "ssl", "cert", NULL }, "abort ssl cert <certfile> : abort a transaction for a certificate file", cli_parse_abort_cert, NULL, NULL },
1854 { { "del", "ssl", "cert", NULL }, "del ssl cert <certfile> : delete an unused certificate file", cli_parse_del_cert, NULL, NULL },
1855 { { "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 },
William Lallemandda8584c2020-05-14 10:14:37 +02001856 { { NULL }, NULL, NULL, NULL }
1857}};
1858
1859INITCALL1(STG_REGISTER, cli_register_kw, &cli_kws);
1860