blob: 1bba5531b5cf204a8a136d3fc9313d52e7794535 [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 Breton543af602022-06-07 16:29:44 +0200284 if (ckch->key == NULL) {
285 /* If no private key was found yet and we cannot look for it in extra
286 * files, raise an error.
287 */
288 if (!(global_ssl.extra_files & SSL_GF_KEY)) {
289 memprintf(err, "%sNo Private Key found in '%s'.\n", err && *err ? *err : "", fp->area);
290 goto end;
291 }
William Lallemand8e8581e2020-10-20 17:36:46 +0200292
Remi Tricot-Le Breton543af602022-06-07 16:29:44 +0200293 /* try to load an external private key if it wasn't in the PEM */
294 if (!chunk_strcat(fp, ".key") || (b_data(fp) > MAXPATHLEN)) {
295 memprintf(err, "%s '%s' filename too long'.\n",
Remi Tricot-Le Breton9f4503d2022-05-09 11:07:13 +0200296 err && *err ? *err : "", fp->area);
Remi Tricot-Le Breton543af602022-06-07 16:29:44 +0200297 ret = 1;
William Lallemand8e8581e2020-10-20 17:36:46 +0200298 goto end;
299 }
William Lallemand03c331c2020-05-13 10:10:01 +0200300
Remi Tricot-Le Breton543af602022-06-07 16:29:44 +0200301 if (stat(fp->area, &st) == 0) {
302 if (ssl_sock_load_key_into_ckch(fp->area, NULL, ckch, err)) {
303 memprintf(err, "%s '%s' is present but cannot be read or parsed'.\n",
304 err && *err ? *err : "", fp->area);
305 goto end;
306 }
307 }
308
309 if (ckch->key == NULL) {
310 memprintf(err, "%sNo Private Key found in '%s'.\n", err && *err ? *err : "", fp->area);
311 goto end;
312 }
313 /* remove the added extension */
314 *(fp->area + fp->data - strlen(".key")) = '\0';
315 b_sub(fp, strlen(".key"));
William Lallemand03c331c2020-05-13 10:10:01 +0200316 }
Remi Tricot-Le Breton9f4503d2022-05-09 11:07:13 +0200317
William Lallemand03c331c2020-05-13 10:10:01 +0200318
319 if (!X509_check_private_key(ckch->cert, ckch->key)) {
320 memprintf(err, "%sinconsistencies between private key and certificate loaded '%s'.\n",
321 err && *err ? *err : "", path);
322 goto end;
323 }
324
Ilya Shipitsinc47d6762021-02-13 11:45:33 +0500325#ifdef HAVE_SSL_SCTL
William Lallemand03c331c2020-05-13 10:10:01 +0200326 /* try to load the sctl file */
327 if (global_ssl.extra_files & SSL_GF_SCTL) {
William Lallemand03c331c2020-05-13 10:10:01 +0200328 struct stat st;
329
William Lallemand8e8581e2020-10-20 17:36:46 +0200330 if (!chunk_strcat(fp, ".sctl") || b_data(fp) > MAXPATHLEN) {
331 memprintf(err, "%s '%s' filename too long'.\n",
332 err && *err ? *err : "", fp->area);
333 ret = 1;
334 goto end;
335 }
336
337 if (stat(fp->area, &st) == 0) {
338 if (ssl_sock_load_sctl_from_file(fp->area, NULL, ckch, err)) {
William Lallemand03c331c2020-05-13 10:10:01 +0200339 memprintf(err, "%s '%s.sctl' is present but cannot be read or parsed'.\n",
William Lallemand8e8581e2020-10-20 17:36:46 +0200340 err && *err ? *err : "", fp->area);
William Lallemand03c331c2020-05-13 10:10:01 +0200341 ret = 1;
342 goto end;
343 }
344 }
William Lallemand8e8581e2020-10-20 17:36:46 +0200345 /* remove the added extension */
346 *(fp->area + fp->data - strlen(".sctl")) = '\0';
347 b_sub(fp, strlen(".sctl"));
William Lallemand03c331c2020-05-13 10:10:01 +0200348 }
349#endif
350
351 /* try to load an ocsp response file */
352 if (global_ssl.extra_files & SSL_GF_OCSP) {
William Lallemand03c331c2020-05-13 10:10:01 +0200353 struct stat st;
354
William Lallemand8e8581e2020-10-20 17:36:46 +0200355 if (!chunk_strcat(fp, ".ocsp") || b_data(fp) > MAXPATHLEN) {
356 memprintf(err, "%s '%s' filename too long'.\n",
357 err && *err ? *err : "", fp->area);
358 ret = 1;
359 goto end;
360 }
361
362 if (stat(fp->area, &st) == 0) {
363 if (ssl_sock_load_ocsp_response_from_file(fp->area, NULL, ckch, err)) {
William Lallemand03c331c2020-05-13 10:10:01 +0200364 ret = 1;
365 goto end;
366 }
367 }
William Lallemand8e8581e2020-10-20 17:36:46 +0200368 /* remove the added extension */
369 *(fp->area + fp->data - strlen(".ocsp")) = '\0';
370 b_sub(fp, strlen(".ocsp"));
William Lallemand03c331c2020-05-13 10:10:01 +0200371 }
372
373#ifndef OPENSSL_IS_BORINGSSL /* Useless for BoringSSL */
374 if (ckch->ocsp_response && (global_ssl.extra_files & SSL_GF_OCSP_ISSUER)) {
375 /* if no issuer was found, try to load an issuer from the .issuer */
376 if (!ckch->ocsp_issuer) {
377 struct stat st;
William Lallemand8e8581e2020-10-20 17:36:46 +0200378
379 if (!chunk_strcat(fp, ".issuer") || b_data(fp) > MAXPATHLEN) {
380 memprintf(err, "%s '%s' filename too long'.\n",
381 err && *err ? *err : "", fp->area);
382 ret = 1;
383 goto end;
384 }
William Lallemand03c331c2020-05-13 10:10:01 +0200385
William Lallemand8e8581e2020-10-20 17:36:46 +0200386 if (stat(fp->area, &st) == 0) {
387 if (ssl_sock_load_issuer_file_into_ckch(fp->area, NULL, ckch, err)) {
William Lallemand03c331c2020-05-13 10:10:01 +0200388 ret = 1;
389 goto end;
390 }
391
392 if (X509_check_issued(ckch->ocsp_issuer, ckch->cert) != X509_V_OK) {
393 memprintf(err, "%s '%s' is not an issuer'.\n",
William Lallemand8e8581e2020-10-20 17:36:46 +0200394 err && *err ? *err : "", fp->area);
William Lallemand03c331c2020-05-13 10:10:01 +0200395 ret = 1;
396 goto end;
397 }
398 }
William Lallemand8e8581e2020-10-20 17:36:46 +0200399 /* remove the added extension */
400 *(fp->area + fp->data - strlen(".issuer")) = '\0';
401 b_sub(fp, strlen(".issuer"));
William Lallemand03c331c2020-05-13 10:10:01 +0200402 }
403 }
404#endif
405
406 ret = 0;
407
408end:
409
410 ERR_clear_error();
411
412 /* Something went wrong in one of the reads */
413 if (ret != 0)
414 ssl_sock_free_cert_key_and_chain_contents(ckch);
415
William Lallemand8e8581e2020-10-20 17:36:46 +0200416 free_trash_chunk(fp);
417
William Lallemand03c331c2020-05-13 10:10:01 +0200418 return ret;
419}
420
421/*
422 * Try to load a private key file from a <path> or a buffer <buf>
423 *
424 * If it failed you should not attempt to use the ckch but free it.
425 *
426 * Return 0 on success or != 0 on failure
427 */
428int ssl_sock_load_key_into_ckch(const char *path, char *buf, struct cert_key_and_chain *ckch , char **err)
429{
430 BIO *in = NULL;
431 int ret = 1;
432 EVP_PKEY *key = NULL;
433
434 if (buf) {
435 /* reading from a buffer */
436 in = BIO_new_mem_buf(buf, -1);
437 if (in == NULL) {
438 memprintf(err, "%sCan't allocate memory\n", err && *err ? *err : "");
439 goto end;
440 }
441
442 } else {
443 /* reading from a file */
444 in = BIO_new(BIO_s_file());
445 if (in == NULL)
446 goto end;
447
448 if (BIO_read_filename(in, path) <= 0)
449 goto end;
450 }
451
452 /* Read Private Key */
453 key = PEM_read_bio_PrivateKey(in, NULL, NULL, NULL);
454 if (key == NULL) {
455 memprintf(err, "%sunable to load private key from file '%s'.\n",
456 err && *err ? *err : "", path);
457 goto end;
458 }
459
460 ret = 0;
461
462 SWAP(ckch->key, key);
463
464end:
465
466 ERR_clear_error();
467 if (in)
468 BIO_free(in);
469 if (key)
470 EVP_PKEY_free(key);
471
472 return ret;
473}
474
475/*
476 * Try to load a PEM file from a <path> or a buffer <buf>
477 * The PEM must contain at least a Certificate,
478 * It could contain a DH, a certificate chain and a PrivateKey.
479 *
480 * If it failed you should not attempt to use the ckch but free it.
481 *
482 * Return 0 on success or != 0 on failure
483 */
484int ssl_sock_load_pem_into_ckch(const char *path, char *buf, struct cert_key_and_chain *ckch , char **err)
485{
486 BIO *in = NULL;
487 int ret = 1;
488 X509 *ca;
489 X509 *cert = NULL;
490 EVP_PKEY *key = NULL;
491 DH *dh = NULL;
492 STACK_OF(X509) *chain = NULL;
493
494 if (buf) {
495 /* reading from a buffer */
496 in = BIO_new_mem_buf(buf, -1);
497 if (in == NULL) {
498 memprintf(err, "%sCan't allocate memory\n", err && *err ? *err : "");
499 goto end;
500 }
501
502 } else {
503 /* reading from a file */
504 in = BIO_new(BIO_s_file());
505 if (in == NULL) {
506 memprintf(err, "%sCan't allocate memory\n", err && *err ? *err : "");
507 goto end;
508 }
509
510 if (BIO_read_filename(in, path) <= 0) {
511 memprintf(err, "%scannot open the file '%s'.\n",
512 err && *err ? *err : "", path);
513 goto end;
514 }
515 }
516
517 /* Read Private Key */
518 key = PEM_read_bio_PrivateKey(in, NULL, NULL, NULL);
519 /* no need to check for errors here, because the private key could be loaded later */
520
521#ifndef OPENSSL_NO_DH
522 /* Seek back to beginning of file */
523 if (BIO_reset(in) == -1) {
524 memprintf(err, "%san error occurred while reading the file '%s'.\n",
525 err && *err ? *err : "", path);
526 goto end;
527 }
528
529 dh = PEM_read_bio_DHparams(in, NULL, NULL, NULL);
530 /* no need to return an error there, dh is not mandatory */
531#endif
532
533 /* Seek back to beginning of file */
534 if (BIO_reset(in) == -1) {
535 memprintf(err, "%san error occurred while reading the file '%s'.\n",
536 err && *err ? *err : "", path);
537 goto end;
538 }
539
540 /* Read Certificate */
541 cert = PEM_read_bio_X509_AUX(in, NULL, NULL, NULL);
542 if (cert == NULL) {
543 memprintf(err, "%sunable to load certificate from file '%s'.\n",
544 err && *err ? *err : "", path);
545 goto end;
546 }
547
548 /* Look for a Certificate Chain */
549 while ((ca = PEM_read_bio_X509(in, NULL, NULL, NULL))) {
550 if (chain == NULL)
551 chain = sk_X509_new_null();
552 if (!sk_X509_push(chain, ca)) {
553 X509_free(ca);
554 goto end;
555 }
556 }
557
558 ret = ERR_get_error();
559 if (ret && (ERR_GET_LIB(ret) != ERR_LIB_PEM && ERR_GET_REASON(ret) != PEM_R_NO_START_LINE)) {
560 memprintf(err, "%sunable to load certificate chain from file '%s'.\n",
561 err && *err ? *err : "", path);
562 goto end;
563 }
564
565 /* once it loaded the PEM, it should remove everything else in the ckch */
566 if (ckch->ocsp_response) {
Willy Tarreau61cfdf42021-02-20 10:46:51 +0100567 ha_free(&ckch->ocsp_response->area);
568 ha_free(&ckch->ocsp_response);
William Lallemand03c331c2020-05-13 10:10:01 +0200569 }
570
571 if (ckch->sctl) {
Willy Tarreau61cfdf42021-02-20 10:46:51 +0100572 ha_free(&ckch->sctl->area);
573 ha_free(&ckch->sctl);
William Lallemand03c331c2020-05-13 10:10:01 +0200574 }
575
576 if (ckch->ocsp_issuer) {
577 X509_free(ckch->ocsp_issuer);
578 ckch->ocsp_issuer = NULL;
579 }
580
581 /* no error, fill ckch with new context, old context will be free at end: */
582 SWAP(ckch->key, key);
583 SWAP(ckch->dh, dh);
584 SWAP(ckch->cert, cert);
585 SWAP(ckch->chain, chain);
586
587 ret = 0;
588
589end:
590
591 ERR_clear_error();
592 if (in)
593 BIO_free(in);
594 if (key)
595 EVP_PKEY_free(key);
596 if (dh)
597 DH_free(dh);
598 if (cert)
599 X509_free(cert);
600 if (chain)
601 sk_X509_pop_free(chain, X509_free);
602
603 return ret;
604}
605
606/* Frees the contents of a cert_key_and_chain
607 */
608void ssl_sock_free_cert_key_and_chain_contents(struct cert_key_and_chain *ckch)
609{
610 if (!ckch)
611 return;
612
613 /* Free the certificate and set pointer to NULL */
614 if (ckch->cert)
615 X509_free(ckch->cert);
616 ckch->cert = NULL;
617
618 /* Free the key and set pointer to NULL */
619 if (ckch->key)
620 EVP_PKEY_free(ckch->key);
621 ckch->key = NULL;
622
623 /* Free each certificate in the chain */
624 if (ckch->chain)
625 sk_X509_pop_free(ckch->chain, X509_free);
626 ckch->chain = NULL;
627
628 if (ckch->dh)
629 DH_free(ckch->dh);
630 ckch->dh = NULL;
631
632 if (ckch->sctl) {
Willy Tarreau61cfdf42021-02-20 10:46:51 +0100633 ha_free(&ckch->sctl->area);
634 ha_free(&ckch->sctl);
William Lallemand03c331c2020-05-13 10:10:01 +0200635 }
636
637 if (ckch->ocsp_response) {
Willy Tarreau61cfdf42021-02-20 10:46:51 +0100638 ha_free(&ckch->ocsp_response->area);
639 ha_free(&ckch->ocsp_response);
William Lallemand03c331c2020-05-13 10:10:01 +0200640 }
641
642 if (ckch->ocsp_issuer)
643 X509_free(ckch->ocsp_issuer);
644 ckch->ocsp_issuer = NULL;
645}
646
647/*
648 *
649 * This function copy a cert_key_and_chain in memory
650 *
651 * It's used to try to apply changes on a ckch before committing them, because
652 * most of the time it's not possible to revert those changes
653 *
654 * Return a the dst or NULL
655 */
656struct cert_key_and_chain *ssl_sock_copy_cert_key_and_chain(struct cert_key_and_chain *src,
657 struct cert_key_and_chain *dst)
658{
William Lallemand6c096142021-02-23 14:45:45 +0100659 if (!src || !dst)
660 return NULL;
661
William Lallemand03c331c2020-05-13 10:10:01 +0200662 if (src->cert) {
663 dst->cert = src->cert;
664 X509_up_ref(src->cert);
665 }
666
667 if (src->key) {
668 dst->key = src->key;
669 EVP_PKEY_up_ref(src->key);
670 }
671
672 if (src->chain) {
673 dst->chain = X509_chain_up_ref(src->chain);
674 }
675
676 if (src->dh) {
677 DH_up_ref(src->dh);
678 dst->dh = src->dh;
679 }
680
681 if (src->sctl) {
682 struct buffer *sctl;
683
684 sctl = calloc(1, sizeof(*sctl));
685 if (!chunk_dup(sctl, src->sctl)) {
Willy Tarreau61cfdf42021-02-20 10:46:51 +0100686 ha_free(&sctl);
William Lallemand03c331c2020-05-13 10:10:01 +0200687 goto error;
688 }
689 dst->sctl = sctl;
690 }
691
692 if (src->ocsp_response) {
693 struct buffer *ocsp_response;
694
695 ocsp_response = calloc(1, sizeof(*ocsp_response));
696 if (!chunk_dup(ocsp_response, src->ocsp_response)) {
Willy Tarreau61cfdf42021-02-20 10:46:51 +0100697 ha_free(&ocsp_response);
William Lallemand03c331c2020-05-13 10:10:01 +0200698 goto error;
699 }
700 dst->ocsp_response = ocsp_response;
701 }
702
703 if (src->ocsp_issuer) {
704 X509_up_ref(src->ocsp_issuer);
705 dst->ocsp_issuer = src->ocsp_issuer;
706 }
707
708 return dst;
709
710error:
711
712 /* free everything */
713 ssl_sock_free_cert_key_and_chain_contents(dst);
714
715 return NULL;
716}
717
718/*
719 * return 0 on success or != 0 on failure
720 */
721int ssl_sock_load_issuer_file_into_ckch(const char *path, char *buf, struct cert_key_and_chain *ckch, char **err)
722{
723 int ret = 1;
724 BIO *in = NULL;
725 X509 *issuer;
726
727 if (buf) {
728 /* reading from a buffer */
729 in = BIO_new_mem_buf(buf, -1);
730 if (in == NULL) {
731 memprintf(err, "%sCan't allocate memory\n", err && *err ? *err : "");
732 goto end;
733 }
734
735 } else {
736 /* reading from a file */
737 in = BIO_new(BIO_s_file());
738 if (in == NULL)
739 goto end;
740
741 if (BIO_read_filename(in, path) <= 0)
742 goto end;
743 }
744
745 issuer = PEM_read_bio_X509_AUX(in, NULL, NULL, NULL);
746 if (!issuer) {
747 memprintf(err, "%s'%s' cannot be read or parsed'.\n",
748 err && *err ? *err : "", path);
749 goto end;
750 }
751 /* no error, fill ckch with new context, old context must be free */
752 if (ckch->ocsp_issuer)
753 X509_free(ckch->ocsp_issuer);
754 ckch->ocsp_issuer = issuer;
755 ret = 0;
756
757end:
758
759 ERR_clear_error();
760 if (in)
761 BIO_free(in);
762
763 return ret;
764}
765
766/******************** ckch_store functions ***********************************
767 * The ckch_store is a structure used to cache and index the SSL files used in
768 * configuration
769 */
770
771/*
772 * Free a ckch_store, its ckch, its instances and remove it from the ebtree
773 */
774void ckch_store_free(struct ckch_store *store)
775{
776 struct ckch_inst *inst, *inst_s;
777
778 if (!store)
779 return;
780
William Lallemandbd8e6ed2020-09-16 16:08:08 +0200781 ssl_sock_free_cert_key_and_chain_contents(store->ckch);
William Lallemand03c331c2020-05-13 10:10:01 +0200782
Willy Tarreau61cfdf42021-02-20 10:46:51 +0100783 ha_free(&store->ckch);
William Lallemand03c331c2020-05-13 10:10:01 +0200784
785 list_for_each_entry_safe(inst, inst_s, &store->ckch_inst, by_ckchs) {
786 ckch_inst_free(inst);
787 }
788 ebmb_delete(&store->node);
789 free(store);
790}
791
792/*
793 * create and initialize a ckch_store
794 * <path> is the key name
795 * <nmemb> is the number of store->ckch objects to allocate
796 *
797 * Return a ckch_store or NULL upon failure.
798 */
William Lallemandbd8e6ed2020-09-16 16:08:08 +0200799struct ckch_store *ckch_store_new(const char *filename)
William Lallemand03c331c2020-05-13 10:10:01 +0200800{
801 struct ckch_store *store;
802 int pathlen;
803
804 pathlen = strlen(filename);
805 store = calloc(1, sizeof(*store) + pathlen + 1);
806 if (!store)
807 return NULL;
808
William Lallemand03c331c2020-05-13 10:10:01 +0200809 memcpy(store->path, filename, pathlen + 1);
810
811 LIST_INIT(&store->ckch_inst);
812 LIST_INIT(&store->crtlist_entry);
813
William Lallemandbd8e6ed2020-09-16 16:08:08 +0200814 store->ckch = calloc(1, sizeof(*store->ckch));
William Lallemand03c331c2020-05-13 10:10:01 +0200815 if (!store->ckch)
816 goto error;
817
818 return store;
819error:
820 ckch_store_free(store);
821 return NULL;
822}
823
824/* allocate and duplicate a ckch_store
825 * Return a new ckch_store or NULL */
826struct ckch_store *ckchs_dup(const struct ckch_store *src)
827{
828 struct ckch_store *dst;
829
William Lallemand6c096142021-02-23 14:45:45 +0100830 if (!src)
831 return NULL;
832
William Lallemandbd8e6ed2020-09-16 16:08:08 +0200833 dst = ckch_store_new(src->path);
Eric Salama6ac61e32021-02-23 16:50:57 +0100834 if (!dst)
835 return NULL;
William Lallemand03c331c2020-05-13 10:10:01 +0200836
William Lallemandbd8e6ed2020-09-16 16:08:08 +0200837 if (!ssl_sock_copy_cert_key_and_chain(src->ckch, dst->ckch))
838 goto error;
William Lallemand03c331c2020-05-13 10:10:01 +0200839
840 return dst;
841
842error:
843 ckch_store_free(dst);
844
845 return NULL;
846}
847
848/*
849 * lookup a path into the ckchs tree.
850 */
851struct ckch_store *ckchs_lookup(char *path)
852{
853 struct ebmb_node *eb;
854
855 eb = ebst_lookup(&ckchs_tree, path);
856 if (!eb)
857 return NULL;
858
859 return ebmb_entry(eb, struct ckch_store, node);
860}
861
862/*
863 * This function allocate a ckch_store and populate it with certificates from files.
864 */
William Lallemandbd8e6ed2020-09-16 16:08:08 +0200865struct ckch_store *ckchs_load_cert_file(char *path, char **err)
William Lallemand03c331c2020-05-13 10:10:01 +0200866{
867 struct ckch_store *ckchs;
868
William Lallemandbd8e6ed2020-09-16 16:08:08 +0200869 ckchs = ckch_store_new(path);
William Lallemand03c331c2020-05-13 10:10:01 +0200870 if (!ckchs) {
871 memprintf(err, "%sunable to allocate memory.\n", err && *err ? *err : "");
872 goto end;
873 }
William Lallemand03c331c2020-05-13 10:10:01 +0200874
William Lallemandbd8e6ed2020-09-16 16:08:08 +0200875 if (ssl_sock_load_files_into_ckch(path, ckchs->ckch, err) == 1)
876 goto end;
William Lallemand03c331c2020-05-13 10:10:01 +0200877
William Lallemandbd8e6ed2020-09-16 16:08:08 +0200878 /* insert into the ckchs tree */
879 memcpy(ckchs->path, path, strlen(path) + 1);
880 ebst_insert(&ckchs_tree, &ckchs->node);
William Lallemand03c331c2020-05-13 10:10:01 +0200881 return ckchs;
882
883end:
884 ckch_store_free(ckchs);
885
886 return NULL;
887}
888
William Lallemandfa1d8b42020-05-13 15:46:10 +0200889
890/******************** ckch_inst functions ******************************/
891
892/* unlink a ckch_inst, free all SNIs, free the ckch_inst */
893/* The caller must use the lock of the bind_conf if used with inserted SNIs */
894void ckch_inst_free(struct ckch_inst *inst)
895{
896 struct sni_ctx *sni, *sni_s;
897
898 if (inst == NULL)
899 return;
900
901 list_for_each_entry_safe(sni, sni_s, &inst->sni_ctx, by_ckch_inst) {
902 SSL_CTX_free(sni->ctx);
Willy Tarreau2b718102021-04-21 07:32:39 +0200903 LIST_DELETE(&sni->by_ckch_inst);
William Lallemandfa1d8b42020-05-13 15:46:10 +0200904 ebmb_delete(&sni->name);
905 free(sni);
906 }
Remi Tricot-Le Bretonf3eedfe2021-01-25 17:19:44 +0100907 SSL_CTX_free(inst->ctx);
908 inst->ctx = NULL;
Willy Tarreau2b718102021-04-21 07:32:39 +0200909 LIST_DELETE(&inst->by_ckchs);
910 LIST_DELETE(&inst->by_crtlist_entry);
William Lallemandfa1d8b42020-05-13 15:46:10 +0200911 free(inst);
912}
913
914/* Alloc and init a ckch_inst */
915struct ckch_inst *ckch_inst_new()
916{
917 struct ckch_inst *ckch_inst;
918
919 ckch_inst = calloc(1, sizeof *ckch_inst);
920 if (!ckch_inst)
921 return NULL;
922
923 LIST_INIT(&ckch_inst->sni_ctx);
924 LIST_INIT(&ckch_inst->by_ckchs);
925 LIST_INIT(&ckch_inst->by_crtlist_entry);
926
927 return ckch_inst;
928}
929
William Lallemandda8584c2020-05-14 10:14:37 +0200930/*************************** CLI commands ***********************/
931
932/* Type of SSL payloads that can be updated over the CLI */
933
934enum {
935 CERT_TYPE_PEM = 0,
936 CERT_TYPE_KEY,
937#if ((defined SSL_CTRL_SET_TLSEXT_STATUS_REQ_CB && !defined OPENSSL_NO_OCSP) || defined OPENSSL_IS_BORINGSSL)
938 CERT_TYPE_OCSP,
939#endif
940 CERT_TYPE_ISSUER,
Ilya Shipitsinc47d6762021-02-13 11:45:33 +0500941#ifdef HAVE_SSL_SCTL
William Lallemandda8584c2020-05-14 10:14:37 +0200942 CERT_TYPE_SCTL,
943#endif
944 CERT_TYPE_MAX,
945};
946
947struct {
948 const char *ext;
949 int type;
950 int (*load)(const char *path, char *payload, struct cert_key_and_chain *ckch, char **err);
951 /* add a parsing callback */
952} cert_exts[CERT_TYPE_MAX+1] = {
953 [CERT_TYPE_PEM] = { "", CERT_TYPE_PEM, &ssl_sock_load_pem_into_ckch }, /* default mode, no extensions */
954 [CERT_TYPE_KEY] = { "key", CERT_TYPE_KEY, &ssl_sock_load_key_into_ckch },
955#if ((defined SSL_CTRL_SET_TLSEXT_STATUS_REQ_CB && !defined OPENSSL_NO_OCSP) || defined OPENSSL_IS_BORINGSSL)
956 [CERT_TYPE_OCSP] = { "ocsp", CERT_TYPE_OCSP, &ssl_sock_load_ocsp_response_from_file },
957#endif
Ilya Shipitsinc47d6762021-02-13 11:45:33 +0500958#ifdef HAVE_SSL_SCTL
William Lallemandda8584c2020-05-14 10:14:37 +0200959 [CERT_TYPE_SCTL] = { "sctl", CERT_TYPE_SCTL, &ssl_sock_load_sctl_from_file },
960#endif
961 [CERT_TYPE_ISSUER] = { "issuer", CERT_TYPE_ISSUER, &ssl_sock_load_issuer_file_into_ckch },
962 [CERT_TYPE_MAX] = { NULL, CERT_TYPE_MAX, NULL },
963};
964
965
966/* release function of the `show ssl cert' command */
967static void cli_release_show_cert(struct appctx *appctx)
968{
969 HA_SPIN_UNLOCK(CKCH_LOCK, &ckch_lock);
970}
971
972/* IO handler of "show ssl cert <filename>" */
973static int cli_io_handler_show_cert(struct appctx *appctx)
974{
975 struct buffer *trash = alloc_trash_chunk();
976 struct ebmb_node *node;
977 struct stream_interface *si = appctx->owner;
Christopher Faulet83b3f8a2022-06-03 16:24:02 +0200978 struct ckch_store *ckchs = NULL;
William Lallemandda8584c2020-05-14 10:14:37 +0200979
980 if (trash == NULL)
981 return 1;
982
Christopher Faulet7f339cc2022-06-03 10:46:40 +0200983 if (!appctx->ctx.ssl.old_ckchs && ckchs_transaction.old_ckchs) {
984 ckchs = ckchs_transaction.old_ckchs;
985 chunk_appendf(trash, "# transaction\n");
986 chunk_appendf(trash, "*%s\n", ckchs->path);
987 if (ci_putchk(si_ic(si), trash) == -1)
988 goto yield;
989 appctx->ctx.ssl.old_ckchs = ckchs_transaction.old_ckchs;
William Lallemandda8584c2020-05-14 10:14:37 +0200990 }
991
992 if (!appctx->ctx.cli.p0) {
993 chunk_appendf(trash, "# filename\n");
994 node = ebmb_first(&ckchs_tree);
995 } else {
996 node = &((struct ckch_store *)appctx->ctx.cli.p0)->node;
997 }
998 while (node) {
999 ckchs = ebmb_entry(node, struct ckch_store, node);
William Lallemand5685ccf2020-09-16 16:12:25 +02001000 chunk_appendf(trash, "%s\n", ckchs->path);
William Lallemandda8584c2020-05-14 10:14:37 +02001001
1002 node = ebmb_next(node);
1003 if (ci_putchk(si_ic(si), trash) == -1) {
1004 si_rx_room_blk(si);
1005 goto yield;
1006 }
1007 }
1008
1009 appctx->ctx.cli.p0 = NULL;
1010 free_trash_chunk(trash);
1011 return 1;
1012yield:
1013
1014 free_trash_chunk(trash);
1015 appctx->ctx.cli.p0 = ckchs;
1016 return 0; /* should come back */
1017}
1018
1019/*
1020 * Extract and format the DNS SAN extensions and copy result into a chuink
1021 * Return 0;
1022 */
1023#ifdef SSL_CTRL_SET_TLSEXT_HOSTNAME
1024static int ssl_sock_get_san_oneline(X509 *cert, struct buffer *out)
1025{
1026 int i;
1027 char *str;
1028 STACK_OF(GENERAL_NAME) *names = NULL;
1029
1030 names = X509_get_ext_d2i(cert, NID_subject_alt_name, NULL, NULL);
1031 if (names) {
1032 for (i = 0; i < sk_GENERAL_NAME_num(names); i++) {
1033 GENERAL_NAME *name = sk_GENERAL_NAME_value(names, i);
1034 if (i > 0)
1035 chunk_appendf(out, ", ");
1036 if (name->type == GEN_DNS) {
1037 if (ASN1_STRING_to_UTF8((unsigned char **)&str, name->d.dNSName) >= 0) {
1038 chunk_appendf(out, "DNS:%s", str);
1039 OPENSSL_free(str);
1040 }
1041 }
1042 }
1043 sk_GENERAL_NAME_pop_free(names, GENERAL_NAME_free);
1044 }
1045 return 0;
1046}
1047#endif
1048
1049
1050
1051
1052/* IO handler of the details "show ssl cert <filename>" */
1053static int cli_io_handler_show_cert_detail(struct appctx *appctx)
1054{
1055 struct stream_interface *si = appctx->owner;
1056 struct ckch_store *ckchs = appctx->ctx.cli.p0;
1057 struct buffer *out = alloc_trash_chunk();
1058 struct buffer *tmp = alloc_trash_chunk();
1059 X509_NAME *name = NULL;
1060 STACK_OF(X509) *chain;
1061 unsigned int len = 0;
1062 int write = -1;
1063 BIO *bio = NULL;
1064 int i;
1065
1066 if (!tmp || !out)
1067 goto end_no_putchk;
1068
William Lallemand5685ccf2020-09-16 16:12:25 +02001069 chunk_appendf(out, "Filename: ");
1070 if (ckchs == ckchs_transaction.new_ckchs)
1071 chunk_appendf(out, "*");
1072 chunk_appendf(out, "%s\n", ckchs->path);
William Lallemandda8584c2020-05-14 10:14:37 +02001073
William Lallemand5685ccf2020-09-16 16:12:25 +02001074 chunk_appendf(out, "Status: ");
1075 if (ckchs->ckch->cert == NULL)
1076 chunk_appendf(out, "Empty\n");
1077 else if (LIST_ISEMPTY(&ckchs->ckch_inst))
1078 chunk_appendf(out, "Unused\n");
1079 else
1080 chunk_appendf(out, "Used\n");
William Lallemandda8584c2020-05-14 10:14:37 +02001081
William Lallemand5685ccf2020-09-16 16:12:25 +02001082 if (ckchs->ckch->cert == NULL)
1083 goto end;
William Lallemandda8584c2020-05-14 10:14:37 +02001084
William Lallemand5685ccf2020-09-16 16:12:25 +02001085 chain = ckchs->ckch->chain;
1086 if (chain == NULL) {
1087 struct issuer_chain *issuer;
1088 issuer = ssl_get0_issuer_chain(ckchs->ckch->cert);
1089 if (issuer) {
1090 chain = issuer->chain;
1091 chunk_appendf(out, "Chain Filename: ");
1092 chunk_appendf(out, "%s\n", issuer->path);
William Lallemandda8584c2020-05-14 10:14:37 +02001093 }
William Lallemand5685ccf2020-09-16 16:12:25 +02001094 }
1095 chunk_appendf(out, "Serial: ");
1096 if (ssl_sock_get_serial(ckchs->ckch->cert, tmp) == -1)
1097 goto end;
1098 dump_binary(out, tmp->area, tmp->data);
1099 chunk_appendf(out, "\n");
William Lallemandda8584c2020-05-14 10:14:37 +02001100
William Lallemand5685ccf2020-09-16 16:12:25 +02001101 chunk_appendf(out, "notBefore: ");
1102 chunk_reset(tmp);
1103 if ((bio = BIO_new(BIO_s_mem())) == NULL)
1104 goto end;
1105 if (ASN1_TIME_print(bio, X509_getm_notBefore(ckchs->ckch->cert)) == 0)
1106 goto end;
1107 write = BIO_read(bio, tmp->area, tmp->size-1);
1108 tmp->area[write] = '\0';
1109 BIO_free(bio);
1110 bio = NULL;
1111 chunk_appendf(out, "%s\n", tmp->area);
William Lallemandda8584c2020-05-14 10:14:37 +02001112
William Lallemand5685ccf2020-09-16 16:12:25 +02001113 chunk_appendf(out, "notAfter: ");
1114 chunk_reset(tmp);
1115 if ((bio = BIO_new(BIO_s_mem())) == NULL)
1116 goto end;
1117 if (ASN1_TIME_print(bio, X509_getm_notAfter(ckchs->ckch->cert)) == 0)
1118 goto end;
1119 if ((write = BIO_read(bio, tmp->area, tmp->size-1)) <= 0)
1120 goto end;
1121 tmp->area[write] = '\0';
1122 BIO_free(bio);
1123 bio = NULL;
1124 chunk_appendf(out, "%s\n", tmp->area);
William Lallemandda8584c2020-05-14 10:14:37 +02001125
1126#ifdef SSL_CTRL_SET_TLSEXT_HOSTNAME
William Lallemand5685ccf2020-09-16 16:12:25 +02001127 chunk_appendf(out, "Subject Alternative Name: ");
1128 if (ssl_sock_get_san_oneline(ckchs->ckch->cert, out) == -1)
1129 goto end;
1130 *(out->area + out->data) = '\0';
1131 chunk_appendf(out, "\n");
William Lallemandda8584c2020-05-14 10:14:37 +02001132#endif
William Lallemand5685ccf2020-09-16 16:12:25 +02001133 chunk_reset(tmp);
1134 chunk_appendf(out, "Algorithm: ");
1135 if (cert_get_pkey_algo(ckchs->ckch->cert, tmp) == 0)
1136 goto end;
1137 chunk_appendf(out, "%s\n", tmp->area);
William Lallemandda8584c2020-05-14 10:14:37 +02001138
William Lallemand5685ccf2020-09-16 16:12:25 +02001139 chunk_reset(tmp);
1140 chunk_appendf(out, "SHA1 FingerPrint: ");
1141 if (X509_digest(ckchs->ckch->cert, EVP_sha1(), (unsigned char *) tmp->area, &len) == 0)
1142 goto end;
1143 tmp->data = len;
1144 dump_binary(out, tmp->area, tmp->data);
1145 chunk_appendf(out, "\n");
William Lallemandda8584c2020-05-14 10:14:37 +02001146
William Lallemand5685ccf2020-09-16 16:12:25 +02001147 chunk_appendf(out, "Subject: ");
1148 if ((name = X509_get_subject_name(ckchs->ckch->cert)) == NULL)
1149 goto end;
1150 if ((ssl_sock_get_dn_oneline(name, tmp)) == -1)
1151 goto end;
1152 *(tmp->area + tmp->data) = '\0';
1153 chunk_appendf(out, "%s\n", tmp->area);
1154
1155 chunk_appendf(out, "Issuer: ");
1156 if ((name = X509_get_issuer_name(ckchs->ckch->cert)) == NULL)
1157 goto end;
1158 if ((ssl_sock_get_dn_oneline(name, tmp)) == -1)
1159 goto end;
1160 *(tmp->area + tmp->data) = '\0';
1161 chunk_appendf(out, "%s\n", tmp->area);
1162
1163 /* Displays subject of each certificate in the chain */
1164 for (i = 0; i < sk_X509_num(chain); i++) {
1165 X509 *ca = sk_X509_value(chain, i);
1166
1167 chunk_appendf(out, "Chain Subject: ");
1168 if ((name = X509_get_subject_name(ca)) == NULL)
William Lallemandda8584c2020-05-14 10:14:37 +02001169 goto end;
1170 if ((ssl_sock_get_dn_oneline(name, tmp)) == -1)
1171 goto end;
1172 *(tmp->area + tmp->data) = '\0';
1173 chunk_appendf(out, "%s\n", tmp->area);
1174
William Lallemand5685ccf2020-09-16 16:12:25 +02001175 chunk_appendf(out, "Chain Issuer: ");
1176 if ((name = X509_get_issuer_name(ca)) == NULL)
William Lallemandda8584c2020-05-14 10:14:37 +02001177 goto end;
1178 if ((ssl_sock_get_dn_oneline(name, tmp)) == -1)
1179 goto end;
1180 *(tmp->area + tmp->data) = '\0';
1181 chunk_appendf(out, "%s\n", tmp->area);
William Lallemandda8584c2020-05-14 10:14:37 +02001182 }
1183
1184end:
1185 if (ci_putchk(si_ic(si), out) == -1) {
1186 si_rx_room_blk(si);
1187 goto yield;
1188 }
1189
1190end_no_putchk:
1191 if (bio)
1192 BIO_free(bio);
1193 free_trash_chunk(tmp);
1194 free_trash_chunk(out);
1195 return 1;
1196yield:
1197 free_trash_chunk(tmp);
1198 free_trash_chunk(out);
1199 return 0; /* should come back */
1200}
1201
1202/* parsing function for 'show ssl cert [certfile]' */
1203static int cli_parse_show_cert(char **args, char *payload, struct appctx *appctx, void *private)
1204{
1205 struct ckch_store *ckchs;
1206
1207 if (!cli_has_level(appctx, ACCESS_LVL_OPER))
1208 return cli_err(appctx, "Can't allocate memory!\n");
1209
1210 /* The operations on the CKCH architecture are locked so we can
1211 * manipulate ckch_store and ckch_inst */
1212 if (HA_SPIN_TRYLOCK(CKCH_LOCK, &ckch_lock))
1213 return cli_err(appctx, "Can't show!\nOperations on certificates are currently locked!\n");
1214
1215 /* check if there is a certificate to lookup */
1216 if (*args[3]) {
1217 if (*args[3] == '*') {
1218 if (!ckchs_transaction.new_ckchs)
1219 goto error;
1220
1221 ckchs = ckchs_transaction.new_ckchs;
1222
Tim Duesterhuse5ff1412021-01-02 22:31:53 +01001223 if (strcmp(args[3] + 1, ckchs->path) != 0)
William Lallemandda8584c2020-05-14 10:14:37 +02001224 goto error;
1225
1226 } else {
1227 if ((ckchs = ckchs_lookup(args[3])) == NULL)
1228 goto error;
1229
1230 }
1231
William Lallemandda8584c2020-05-14 10:14:37 +02001232 appctx->ctx.cli.p0 = ckchs;
1233 /* use the IO handler that shows details */
1234 appctx->io_handler = cli_io_handler_show_cert_detail;
1235 }
1236
1237 return 0;
1238
1239error:
1240 HA_SPIN_UNLOCK(CKCH_LOCK, &ckch_lock);
1241 return cli_err(appctx, "Can't display the certificate: Not found or the certificate is a bundle!\n");
1242}
1243
1244/* release function of the `set ssl cert' command, free things and unlock the spinlock */
1245static void cli_release_commit_cert(struct appctx *appctx)
1246{
Christopher Faulet82ae56d2022-05-31 16:37:01 +02001247 struct ckch_store *new_ckchs = appctx->ctx.ssl.new_ckchs;
William Lallemandda8584c2020-05-14 10:14:37 +02001248
1249 HA_SPIN_UNLOCK(CKCH_LOCK, &ckch_lock);
1250
Christopher Faulet82ae56d2022-05-31 16:37:01 +02001251 /* free every new sni_ctx and the new store, which are not in the trees so no spinlock there */
1252 if (new_ckchs)
William Lallemandda8584c2020-05-14 10:14:37 +02001253 ckch_store_free(new_ckchs);
Christopher Faulet82ae56d2022-05-31 16:37:01 +02001254 ha_free(&appctx->ctx.ssl.err);
William Lallemandda8584c2020-05-14 10:14:37 +02001255}
1256
1257/*
1258 * This function tries to create the new ckch_inst and their SNIs
1259 */
1260static int cli_io_handler_commit_cert(struct appctx *appctx)
1261{
1262 struct stream_interface *si = appctx->owner;
1263 int y = 0;
William Lallemandda8584c2020-05-14 10:14:37 +02001264 int errcode = 0;
Remi Tricot-Le Breton43899ec2021-04-21 15:32:46 +02001265 int retval = 0;
William Lallemandda8584c2020-05-14 10:14:37 +02001266 struct ckch_store *old_ckchs, *new_ckchs = NULL;
1267 struct ckch_inst *ckchi, *ckchis;
William Lallemandda8584c2020-05-14 10:14:37 +02001268 struct sni_ctx *sc0, *sc0s;
1269 struct crtlist_entry *entry;
1270
William Lallemandda8584c2020-05-14 10:14:37 +02001271
1272 if (unlikely(si_ic(si)->flags & (CF_WRITE_ERROR|CF_SHUTW)))
Christopher Faulet82ae56d2022-05-31 16:37:01 +02001273 goto end;
William Lallemandda8584c2020-05-14 10:14:37 +02001274
1275 while (1) {
1276 switch (appctx->st2) {
1277 case SETCERT_ST_INIT:
1278 /* This state just print the update message */
Christopher Faulet82ae56d2022-05-31 16:37:01 +02001279 chunk_printf(&trash, "Committing %s", ckchs_transaction.path);
1280 if (ci_putchk(si_ic(si), &trash) == -1)
William Lallemandda8584c2020-05-14 10:14:37 +02001281 goto yield;
William Lallemandda8584c2020-05-14 10:14:37 +02001282 appctx->st2 = SETCERT_ST_GEN;
1283 /* fallthrough */
1284 case SETCERT_ST_GEN:
1285 /*
1286 * This state generates the ckch instances with their
1287 * sni_ctxs and SSL_CTX.
1288 *
1289 * Since the SSL_CTX generation can be CPU consumer, we
1290 * yield every 10 instances.
1291 */
1292
1293 old_ckchs = appctx->ctx.ssl.old_ckchs;
1294 new_ckchs = appctx->ctx.ssl.new_ckchs;
1295
William Lallemandda8584c2020-05-14 10:14:37 +02001296 /* get the next ckchi to regenerate */
1297 ckchi = appctx->ctx.ssl.next_ckchi;
1298 /* we didn't start yet, set it to the first elem */
1299 if (ckchi == NULL)
1300 ckchi = LIST_ELEM(old_ckchs->ckch_inst.n, typeof(ckchi), by_ckchs);
1301
1302 /* walk through the old ckch_inst and creates new ckch_inst using the updated ckchs */
1303 list_for_each_entry_from(ckchi, &old_ckchs->ckch_inst, by_ckchs) {
1304 struct ckch_inst *new_inst;
1305 char **sni_filter = NULL;
1306 int fcount = 0;
1307
Christopher Faulet82ae56d2022-05-31 16:37:01 +02001308 /* save the next ckchi to compute in case of yield */
1309 appctx->ctx.ssl.next_ckchi = ckchi;
1310
William Lallemandda8584c2020-05-14 10:14:37 +02001311 /* it takes a lot of CPU to creates SSL_CTXs, so we yield every 10 CKCH instances */
1312 if (y >= 10) {
Christopher Faulet82ae56d2022-05-31 16:37:01 +02001313 si_rx_endp_more(si);
William Lallemandda8584c2020-05-14 10:14:37 +02001314 goto yield;
1315 }
1316
Christopher Faulet82ae56d2022-05-31 16:37:01 +02001317 /* display one dot per new instance */
1318 if (ci_putstr(si_ic(si), ".") == -1)
1319 goto yield;
1320
William Lallemandda8584c2020-05-14 10:14:37 +02001321 if (ckchi->crtlist_entry) {
1322 sni_filter = ckchi->crtlist_entry->filters;
1323 fcount = ckchi->crtlist_entry->fcount;
1324 }
1325
Christopher Faulet82ae56d2022-05-31 16:37:01 +02001326 appctx->ctx.ssl.err = NULL;
Remi Tricot-Le Bretond817dc72021-01-25 17:19:43 +01001327 if (ckchi->is_server_instance)
Christopher Faulet82ae56d2022-05-31 16:37:01 +02001328 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 +01001329 else
Christopher Faulet82ae56d2022-05-31 16:37:01 +02001330 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 +02001331
Christopher Faulet82ae56d2022-05-31 16:37:01 +02001332 if (errcode & ERR_CODE) {
1333 appctx->st2 = SETCERT_ST_ERROR;
William Lallemandda8584c2020-05-14 10:14:37 +02001334 goto error;
Christopher Faulet82ae56d2022-05-31 16:37:01 +02001335 }
William Lallemandda8584c2020-05-14 10:14:37 +02001336
1337 /* if the previous ckchi was used as the default */
1338 if (ckchi->is_default)
1339 new_inst->is_default = 1;
1340
Remi Tricot-Le Bretonf3eedfe2021-01-25 17:19:44 +01001341 new_inst->is_server_instance = ckchi->is_server_instance;
1342 new_inst->server = ckchi->server;
1343 /* Create a new SSL_CTX and link it to the new instance. */
1344 if (new_inst->is_server_instance) {
Remi Tricot-Le Breton43899ec2021-04-21 15:32:46 +02001345 retval = ssl_sock_prepare_srv_ssl_ctx(ckchi->server, new_inst->ctx);
Christopher Faulet82ae56d2022-05-31 16:37:01 +02001346 if (retval) {
1347 appctx->st2 = SETCERT_ST_ERROR;
Remi Tricot-Le Bretonf3eedfe2021-01-25 17:19:44 +01001348 goto error;
Christopher Faulet82ae56d2022-05-31 16:37:01 +02001349 }
Remi Tricot-Le Bretonf3eedfe2021-01-25 17:19:44 +01001350 }
1351
William Lallemanda55685b2020-12-15 14:57:46 +01001352 /* create the link to the crtlist_entry */
1353 new_inst->crtlist_entry = ckchi->crtlist_entry;
1354
William Lallemandda8584c2020-05-14 10:14:37 +02001355 /* we need to initialize the SSL_CTX generated */
1356 /* this iterate on the newly generated SNIs in the new instance to prepare their SSL_CTX */
1357 list_for_each_entry_safe(sc0, sc0s, &new_inst->sni_ctx, by_ckch_inst) {
1358 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 +02001359 appctx->ctx.ssl.err = NULL;
1360 errcode |= ssl_sock_prepare_ctx(ckchi->bind_conf, ckchi->ssl_conf, sc0->ctx, &appctx->ctx.ssl.err);
1361 if (errcode & ERR_CODE) {
1362 appctx->st2 = SETCERT_ST_ERROR;
William Lallemandda8584c2020-05-14 10:14:37 +02001363 goto error;
Christopher Faulet82ae56d2022-05-31 16:37:01 +02001364 }
William Lallemandda8584c2020-05-14 10:14:37 +02001365 }
1366 }
1367
William Lallemandda8584c2020-05-14 10:14:37 +02001368 /* link the new ckch_inst to the duplicate */
Willy Tarreau2b718102021-04-21 07:32:39 +02001369 LIST_APPEND(&new_ckchs->ckch_inst, &new_inst->by_ckchs);
William Lallemandda8584c2020-05-14 10:14:37 +02001370 y++;
1371 }
1372 appctx->st2 = SETCERT_ST_INSERT;
1373 /* fallthrough */
1374 case SETCERT_ST_INSERT:
1375 /* The generation is finished, we can insert everything */
1376
1377 old_ckchs = appctx->ctx.ssl.old_ckchs;
1378 new_ckchs = appctx->ctx.ssl.new_ckchs;
1379
William Lallemandda8584c2020-05-14 10:14:37 +02001380 /* get the list of crtlist_entry in the old store, and update the pointers to the store */
1381 LIST_SPLICE(&new_ckchs->crtlist_entry, &old_ckchs->crtlist_entry);
1382 list_for_each_entry(entry, &new_ckchs->crtlist_entry, by_ckch_store) {
1383 ebpt_delete(&entry->node);
1384 /* change the ptr and reinsert the node */
1385 entry->node.key = new_ckchs;
1386 ebpt_insert(&entry->crtlist->entries, &entry->node);
1387 }
1388
William Lallemanda55685b2020-12-15 14:57:46 +01001389 /* insert the new ckch_insts in the crtlist_entry */
1390 list_for_each_entry(ckchi, &new_ckchs->ckch_inst, by_ckchs) {
1391 if (ckchi->crtlist_entry)
Willy Tarreau2b718102021-04-21 07:32:39 +02001392 LIST_INSERT(&ckchi->crtlist_entry->ckch_inst, &ckchi->by_crtlist_entry);
William Lallemanda55685b2020-12-15 14:57:46 +01001393 }
1394
William Lallemandda8584c2020-05-14 10:14:37 +02001395 /* First, we insert every new SNIs in the trees, also replace the default_ctx */
1396 list_for_each_entry_safe(ckchi, ckchis, &new_ckchs->ckch_inst, by_ckchs) {
Remi Tricot-Le Bretonf3eedfe2021-01-25 17:19:44 +01001397 /* The bind_conf will be null on server ckch_instances. */
1398 if (ckchi->is_server_instance) {
William Lallemande0de0a62021-02-03 18:51:01 +01001399 int i;
William Lallemand3ce6eed2021-02-08 10:43:44 +01001400 /* a lock is needed here since we have to free the SSL cache */
1401 HA_RWLOCK_WRLOCK(SSL_SERVER_LOCK, &ckchi->server->ssl_ctx.lock);
William Lallemand1dedb0a2021-01-26 10:18:57 +01001402 /* free the server current SSL_CTX */
1403 SSL_CTX_free(ckchi->server->ssl_ctx.ctx);
Remi Tricot-Le Bretonf3eedfe2021-01-25 17:19:44 +01001404 /* Actual ssl context update */
William Lallemand1dedb0a2021-01-26 10:18:57 +01001405 SSL_CTX_up_ref(ckchi->ctx);
Remi Tricot-Le Bretonf3eedfe2021-01-25 17:19:44 +01001406 ckchi->server->ssl_ctx.ctx = ckchi->ctx;
William Lallemand1dedb0a2021-01-26 10:18:57 +01001407 ckchi->server->ssl_ctx.inst = ckchi;
Remi Tricot-Le Bretonf3eedfe2021-01-25 17:19:44 +01001408
William Lallemande0de0a62021-02-03 18:51:01 +01001409 /* flush the session cache of the server */
William Lallemandc78ac5a2021-11-17 02:59:21 +01001410 for (i = 0; i < global.nbthread; i++) {
William Lallemand6cd79952021-11-23 15:15:09 +01001411 ha_free(&ckchi->server->ssl_ctx.reused_sess[i].ptr);
William Lallemandc78ac5a2021-11-17 02:59:21 +01001412 ha_free(&ckchi->server->ssl_ctx.reused_sess[i].sni);
1413 }
Willy Tarreau61cfdf42021-02-20 10:46:51 +01001414
William Lallemand3ce6eed2021-02-08 10:43:44 +01001415 HA_RWLOCK_WRUNLOCK(SSL_SERVER_LOCK, &ckchi->server->ssl_ctx.lock);
William Lallemande0de0a62021-02-03 18:51:01 +01001416
William Lallemand1dedb0a2021-01-26 10:18:57 +01001417 } else {
Remi Tricot-Le Bretonf3eedfe2021-01-25 17:19:44 +01001418 HA_RWLOCK_WRLOCK(SNI_LOCK, &ckchi->bind_conf->sni_lock);
1419 ssl_sock_load_cert_sni(ckchi, ckchi->bind_conf);
1420 HA_RWLOCK_WRUNLOCK(SNI_LOCK, &ckchi->bind_conf->sni_lock);
1421 }
William Lallemandda8584c2020-05-14 10:14:37 +02001422 }
1423
1424 /* delete the old sni_ctx, the old ckch_insts and the ckch_store */
1425 list_for_each_entry_safe(ckchi, ckchis, &old_ckchs->ckch_inst, by_ckchs) {
William Lallemandda8584c2020-05-14 10:14:37 +02001426
William Lallemand1dedb0a2021-01-26 10:18:57 +01001427 if (ckchi->is_server_instance) {
1428 /* no lock for servers */
1429 ckch_inst_free(ckchi);
1430 } else {
1431 struct bind_conf __maybe_unused *bind_conf = ckchi->bind_conf;
1432
1433 HA_RWLOCK_WRLOCK(SNI_LOCK, &bind_conf->sni_lock);
1434 ckch_inst_free(ckchi);
1435 HA_RWLOCK_WRUNLOCK(SNI_LOCK, &bind_conf->sni_lock);
1436 }
William Lallemandda8584c2020-05-14 10:14:37 +02001437 }
1438
1439 /* Replace the old ckchs by the new one */
1440 ckch_store_free(old_ckchs);
1441 ebst_insert(&ckchs_tree, &new_ckchs->node);
Christopher Faulet82ae56d2022-05-31 16:37:01 +02001442 appctx->ctx.ssl.old_ckchs = appctx->ctx.ssl.new_ckchs = NULL;
1443 appctx->st2 = SETCERT_ST_SUCCESS;
1444 /* fallthrough */
1445 case SETCERT_ST_SUCCESS:
1446 if (ci_putstr(si_ic(si), "\nSuccess!\n") == -1)
1447 goto yield;
William Lallemandda8584c2020-05-14 10:14:37 +02001448 appctx->st2 = SETCERT_ST_FIN;
1449 /* fallthrough */
1450 case SETCERT_ST_FIN:
1451 /* we achieved the transaction, we can set everything to NULL */
William Lallemandda8584c2020-05-14 10:14:37 +02001452 ckchs_transaction.new_ckchs = NULL;
1453 ckchs_transaction.old_ckchs = NULL;
Christopher Faulet2ec78222022-05-31 18:07:59 +02001454 ckchs_transaction.path = NULL;
William Lallemandda8584c2020-05-14 10:14:37 +02001455 goto end;
Christopher Faulet82ae56d2022-05-31 16:37:01 +02001456
1457 case SETCERT_ST_ERROR:
1458 error:
1459 chunk_printf(&trash, "\n%sFailed!\n", appctx->ctx.ssl.err);
1460 if (ci_putchk(si_ic(si), &trash) == -1)
1461 goto yield;
1462 appctx->st2 = SETCERT_ST_FIN;
1463 break;
William Lallemandda8584c2020-05-14 10:14:37 +02001464 }
1465 }
1466end:
William Lallemandda8584c2020-05-14 10:14:37 +02001467 /* success: call the release function and don't come back */
1468 return 1;
Christopher Faulet82ae56d2022-05-31 16:37:01 +02001469
William Lallemandda8584c2020-05-14 10:14:37 +02001470yield:
Christopher Faulet82ae56d2022-05-31 16:37:01 +02001471 si_rx_room_blk(si);
William Lallemandda8584c2020-05-14 10:14:37 +02001472 return 0; /* should come back */
William Lallemandda8584c2020-05-14 10:14:37 +02001473}
1474
1475/*
1476 * Parsing function of 'commit ssl cert'
1477 */
1478static int cli_parse_commit_cert(char **args, char *payload, struct appctx *appctx, void *private)
1479{
1480 char *err = NULL;
1481
1482 if (!cli_has_level(appctx, ACCESS_LVL_ADMIN))
1483 return 1;
1484
1485 if (!*args[3])
1486 return cli_err(appctx, "'commit ssl cert expects a filename\n");
1487
1488 /* The operations on the CKCH architecture are locked so we can
1489 * manipulate ckch_store and ckch_inst */
1490 if (HA_SPIN_TRYLOCK(CKCH_LOCK, &ckch_lock))
1491 return cli_err(appctx, "Can't commit the certificate!\nOperations on certificates are currently locked!\n");
1492
1493 if (!ckchs_transaction.path) {
1494 memprintf(&err, "No ongoing transaction! !\n");
1495 goto error;
1496 }
1497
1498 if (strcmp(ckchs_transaction.path, args[3]) != 0) {
1499 memprintf(&err, "The ongoing transaction is about '%s' but you are trying to set '%s'\n", ckchs_transaction.path, args[3]);
1500 goto error;
1501 }
1502
William Lallemand5685ccf2020-09-16 16:12:25 +02001503 /* if a certificate is here, a private key must be here too */
1504 if (ckchs_transaction.new_ckchs->ckch->cert && !ckchs_transaction.new_ckchs->ckch->key) {
1505 memprintf(&err, "The transaction must contain at least a certificate and a private key!\n");
1506 goto error;
1507 }
William Lallemanda9419522020-06-24 16:26:41 +02001508
William Lallemand5685ccf2020-09-16 16:12:25 +02001509 if (!X509_check_private_key(ckchs_transaction.new_ckchs->ckch->cert, ckchs_transaction.new_ckchs->ckch->key)) {
1510 memprintf(&err, "inconsistencies between private key and certificate loaded '%s'.\n", ckchs_transaction.path);
1511 goto error;
William Lallemandda8584c2020-05-14 10:14:37 +02001512 }
1513
1514 /* init the appctx structure */
1515 appctx->st2 = SETCERT_ST_INIT;
1516 appctx->ctx.ssl.next_ckchi = NULL;
1517 appctx->ctx.ssl.new_ckchs = ckchs_transaction.new_ckchs;
1518 appctx->ctx.ssl.old_ckchs = ckchs_transaction.old_ckchs;
1519
1520 /* we don't unlock there, it will be unlock after the IO handler, in the release handler */
1521 return 0;
1522
1523error:
1524
1525 HA_SPIN_UNLOCK(CKCH_LOCK, &ckch_lock);
1526 err = memprintf(&err, "%sCan't commit %s!\n", err ? err : "", args[3]);
1527
1528 return cli_dynerr(appctx, err);
1529}
1530
1531
1532
1533
1534/*
1535 * Parsing function of `set ssl cert`, it updates or creates a temporary ckch.
1536 */
1537static int cli_parse_set_cert(char **args, char *payload, struct appctx *appctx, void *private)
1538{
1539 struct ckch_store *new_ckchs = NULL;
1540 struct ckch_store *old_ckchs = NULL;
1541 char *err = NULL;
1542 int i;
William Lallemandda8584c2020-05-14 10:14:37 +02001543 int errcode = 0;
1544 char *end;
1545 int type = CERT_TYPE_PEM;
1546 struct cert_key_and_chain *ckch;
1547 struct buffer *buf;
1548
1549 if (!cli_has_level(appctx, ACCESS_LVL_ADMIN))
1550 return 1;
1551
William Lallemandda8584c2020-05-14 10:14:37 +02001552 if (!*args[3] || !payload)
1553 return cli_err(appctx, "'set ssl cert expects a filename and a certificate as a payload\n");
1554
1555 /* The operations on the CKCH architecture are locked so we can
1556 * manipulate ckch_store and ckch_inst */
1557 if (HA_SPIN_TRYLOCK(CKCH_LOCK, &ckch_lock))
1558 return cli_err(appctx, "Can't update the certificate!\nOperations on certificates are currently locked!\n");
1559
William Lallemand5ba80d62021-05-04 16:17:27 +02001560 if ((buf = alloc_trash_chunk()) == NULL) {
1561 memprintf(&err, "%sCan't allocate memory\n", err ? err : "");
1562 errcode |= ERR_ALERT | ERR_FATAL;
1563 goto end;
1564 }
William Lallemande5ff4ad2020-06-08 09:40:37 +02001565
William Lallemandda8584c2020-05-14 10:14:37 +02001566 if (!chunk_strcpy(buf, args[3])) {
1567 memprintf(&err, "%sCan't allocate memory\n", err ? err : "");
1568 errcode |= ERR_ALERT | ERR_FATAL;
1569 goto end;
1570 }
1571
1572 /* check which type of file we want to update */
1573 for (i = 0; cert_exts[i].type < CERT_TYPE_MAX; i++) {
1574 end = strrchr(buf->area, '.');
Tim Duesterhuse5ff1412021-01-02 22:31:53 +01001575 if (end && *cert_exts[i].ext && (strcmp(end + 1, cert_exts[i].ext) == 0)) {
William Lallemandda8584c2020-05-14 10:14:37 +02001576 *end = '\0';
William Lallemand089c1382020-10-23 17:35:12 +02001577 buf->data = strlen(buf->area);
William Lallemandda8584c2020-05-14 10:14:37 +02001578 type = cert_exts[i].type;
1579 break;
1580 }
1581 }
1582
1583 appctx->ctx.ssl.old_ckchs = NULL;
1584 appctx->ctx.ssl.new_ckchs = NULL;
1585
1586 /* if there is an ongoing transaction */
1587 if (ckchs_transaction.path) {
William Lallemandda8584c2020-05-14 10:14:37 +02001588 /* if there is an ongoing transaction, check if this is the same file */
1589 if (strcmp(ckchs_transaction.path, buf->area) != 0) {
William Lallemand089c1382020-10-23 17:35:12 +02001590 /* we didn't find the transaction, must try more cases below */
1591
1592 /* if the del-ext option is activated we should try to take a look at a ".crt" too. */
1593 if (type != CERT_TYPE_PEM && global_ssl.extra_files_noext) {
1594 if (!chunk_strcat(buf, ".crt")) {
1595 memprintf(&err, "%sCan't allocate memory\n", err ? err : "");
1596 errcode |= ERR_ALERT | ERR_FATAL;
1597 goto end;
1598 }
1599
1600 if (strcmp(ckchs_transaction.path, buf->area) != 0) {
1601 /* remove .crt of the error message */
1602 *(b_orig(buf) + b_data(buf) + strlen(".crt")) = '\0';
1603 b_sub(buf, strlen(".crt"));
1604
1605 memprintf(&err, "The ongoing transaction is about '%s' but you are trying to set '%s'\n", ckchs_transaction.path, buf->area);
1606 errcode |= ERR_ALERT | ERR_FATAL;
1607 goto end;
1608 }
1609 }
William Lallemandda8584c2020-05-14 10:14:37 +02001610 }
1611
1612 appctx->ctx.ssl.old_ckchs = ckchs_transaction.new_ckchs;
1613
1614 } else {
William Lallemandda8584c2020-05-14 10:14:37 +02001615
William Lallemand95fefa12020-09-09 12:01:33 +02001616 /* lookup for the certificate in the tree */
1617 appctx->ctx.ssl.old_ckchs = ckchs_lookup(buf->area);
William Lallemand089c1382020-10-23 17:35:12 +02001618
1619 if (!appctx->ctx.ssl.old_ckchs) {
1620 /* if the del-ext option is activated we should try to take a look at a ".crt" too. */
1621 if (type != CERT_TYPE_PEM && global_ssl.extra_files_noext) {
1622 if (!chunk_strcat(buf, ".crt")) {
1623 memprintf(&err, "%sCan't allocate memory\n", err ? err : "");
1624 errcode |= ERR_ALERT | ERR_FATAL;
1625 goto end;
1626 }
1627 appctx->ctx.ssl.old_ckchs = ckchs_lookup(buf->area);
1628 }
1629 }
William Lallemandda8584c2020-05-14 10:14:37 +02001630 }
1631
1632 if (!appctx->ctx.ssl.old_ckchs) {
1633 memprintf(&err, "%sCan't replace a certificate which is not referenced by the configuration!\n",
1634 err ? err : "");
1635 errcode |= ERR_ALERT | ERR_FATAL;
1636 goto end;
1637 }
1638
William Lallemandda8584c2020-05-14 10:14:37 +02001639 old_ckchs = appctx->ctx.ssl.old_ckchs;
1640
1641 /* duplicate the ckch store */
1642 new_ckchs = ckchs_dup(old_ckchs);
1643 if (!new_ckchs) {
1644 memprintf(&err, "%sCannot allocate memory!\n",
1645 err ? err : "");
1646 errcode |= ERR_ALERT | ERR_FATAL;
1647 goto end;
1648 }
1649
William Lallemand95fefa12020-09-09 12:01:33 +02001650 ckch = new_ckchs->ckch;
William Lallemandda8584c2020-05-14 10:14:37 +02001651
1652 /* appply the change on the duplicate */
1653 if (cert_exts[type].load(buf->area, payload, ckch, &err) != 0) {
1654 memprintf(&err, "%sCan't load the payload\n", err ? err : "");
1655 errcode |= ERR_ALERT | ERR_FATAL;
1656 goto end;
1657 }
1658
1659 appctx->ctx.ssl.new_ckchs = new_ckchs;
1660
1661 /* we succeed, we can save the ckchs in the transaction */
1662
1663 /* if there wasn't a transaction, update the old ckchs */
1664 if (!ckchs_transaction.old_ckchs) {
1665 ckchs_transaction.old_ckchs = appctx->ctx.ssl.old_ckchs;
Christopher Faulet2ec78222022-05-31 18:07:59 +02001666 ckchs_transaction.path = appctx->ctx.ssl.old_ckchs->path;
William Lallemandda8584c2020-05-14 10:14:37 +02001667 err = memprintf(&err, "Transaction created for certificate %s!\n", ckchs_transaction.path);
1668 } else {
1669 err = memprintf(&err, "Transaction updated for certificate %s!\n", ckchs_transaction.path);
1670
1671 }
1672
1673 /* free the previous ckchs if there was a transaction */
1674 ckch_store_free(ckchs_transaction.new_ckchs);
1675
1676 ckchs_transaction.new_ckchs = appctx->ctx.ssl.new_ckchs;
1677
1678
1679 /* creates the SNI ctxs later in the IO handler */
1680
1681end:
1682 free_trash_chunk(buf);
1683
1684 if (errcode & ERR_CODE) {
1685
1686 ckch_store_free(appctx->ctx.ssl.new_ckchs);
1687 appctx->ctx.ssl.new_ckchs = NULL;
1688
1689 appctx->ctx.ssl.old_ckchs = NULL;
1690
William Lallemandda8584c2020-05-14 10:14:37 +02001691 HA_SPIN_UNLOCK(CKCH_LOCK, &ckch_lock);
1692 return cli_dynerr(appctx, memprintf(&err, "%sCan't update %s!\n", err ? err : "", args[3]));
1693 } else {
1694
1695 HA_SPIN_UNLOCK(CKCH_LOCK, &ckch_lock);
1696 return cli_dynmsg(appctx, LOG_NOTICE, err);
1697 }
1698 /* TODO: handle the ERR_WARN which are not handled because of the io_handler */
1699}
1700
1701/* parsing function of 'abort ssl cert' */
1702static int cli_parse_abort_cert(char **args, char *payload, struct appctx *appctx, void *private)
1703{
1704 char *err = NULL;
1705
1706 if (!cli_has_level(appctx, ACCESS_LVL_ADMIN))
1707 return 1;
1708
1709 if (!*args[3])
1710 return cli_err(appctx, "'abort ssl cert' expects a filename\n");
1711
1712 /* The operations on the CKCH architecture are locked so we can
1713 * manipulate ckch_store and ckch_inst */
1714 if (HA_SPIN_TRYLOCK(CKCH_LOCK, &ckch_lock))
1715 return cli_err(appctx, "Can't abort!\nOperations on certificates are currently locked!\n");
1716
1717 if (!ckchs_transaction.path) {
1718 memprintf(&err, "No ongoing transaction!\n");
1719 goto error;
1720 }
1721
1722 if (strcmp(ckchs_transaction.path, args[3]) != 0) {
1723 memprintf(&err, "The ongoing transaction is about '%s' but you are trying to abort a transaction for '%s'\n", ckchs_transaction.path, args[3]);
1724 goto error;
1725 }
1726
1727 /* Only free the ckchs there, because the SNI and instances were not generated yet */
1728 ckch_store_free(ckchs_transaction.new_ckchs);
1729 ckchs_transaction.new_ckchs = NULL;
William Lallemandda8584c2020-05-14 10:14:37 +02001730 ckchs_transaction.old_ckchs = NULL;
Christopher Faulet2ec78222022-05-31 18:07:59 +02001731 ckchs_transaction.path = NULL;
William Lallemandda8584c2020-05-14 10:14:37 +02001732
1733 HA_SPIN_UNLOCK(CKCH_LOCK, &ckch_lock);
1734
1735 err = memprintf(&err, "Transaction aborted for certificate '%s'!\n", args[3]);
1736 return cli_dynmsg(appctx, LOG_NOTICE, err);
1737
1738error:
1739 HA_SPIN_UNLOCK(CKCH_LOCK, &ckch_lock);
1740
1741 return cli_dynerr(appctx, err);
1742}
1743
1744/* parsing function of 'new ssl cert' */
1745static int cli_parse_new_cert(char **args, char *payload, struct appctx *appctx, void *private)
1746{
1747 struct ckch_store *store;
1748 char *err = NULL;
1749 char *path;
1750
1751 if (!cli_has_level(appctx, ACCESS_LVL_ADMIN))
1752 return 1;
1753
1754 if (!*args[3])
1755 return cli_err(appctx, "'new ssl cert' expects a filename\n");
1756
1757 path = args[3];
1758
1759 /* The operations on the CKCH architecture are locked so we can
1760 * manipulate ckch_store and ckch_inst */
1761 if (HA_SPIN_TRYLOCK(CKCH_LOCK, &ckch_lock))
1762 return cli_err(appctx, "Can't create a certificate!\nOperations on certificates are currently locked!\n");
1763
1764 store = ckchs_lookup(path);
1765 if (store != NULL) {
1766 memprintf(&err, "Certificate '%s' already exists!\n", path);
1767 store = NULL; /* we don't want to free it */
1768 goto error;
1769 }
1770 /* we won't support multi-certificate bundle here */
William Lallemandbd8e6ed2020-09-16 16:08:08 +02001771 store = ckch_store_new(path);
William Lallemandda8584c2020-05-14 10:14:37 +02001772 if (!store) {
1773 memprintf(&err, "unable to allocate memory.\n");
1774 goto error;
1775 }
1776
1777 /* insert into the ckchs tree */
1778 ebst_insert(&ckchs_tree, &store->node);
1779 memprintf(&err, "New empty certificate store '%s'!\n", args[3]);
1780
1781 HA_SPIN_UNLOCK(CKCH_LOCK, &ckch_lock);
1782 return cli_dynmsg(appctx, LOG_NOTICE, err);
1783error:
1784 free(store);
1785 HA_SPIN_UNLOCK(CKCH_LOCK, &ckch_lock);
1786 return cli_dynerr(appctx, err);
1787}
1788
1789/* parsing function of 'del ssl cert' */
1790static int cli_parse_del_cert(char **args, char *payload, struct appctx *appctx, void *private)
1791{
1792 struct ckch_store *store;
1793 char *err = NULL;
1794 char *filename;
1795
1796 if (!cli_has_level(appctx, ACCESS_LVL_ADMIN))
1797 return 1;
1798
1799 if (!*args[3])
1800 return cli_err(appctx, "'del ssl cert' expects a certificate name\n");
1801
1802 if (HA_SPIN_TRYLOCK(CKCH_LOCK, &ckch_lock))
1803 return cli_err(appctx, "Can't delete the certificate!\nOperations on certificates are currently locked!\n");
1804
1805 filename = args[3];
1806
Christopher Faulet2ecaf332022-05-31 18:04:25 +02001807 if (ckchs_transaction.path && strcmp(ckchs_transaction.path, filename) == 0) {
1808 memprintf(&err, "ongoing transaction for the certificate '%s'", filename);
1809 goto error;
1810 }
1811
William Lallemandda8584c2020-05-14 10:14:37 +02001812 store = ckchs_lookup(filename);
1813 if (store == NULL) {
1814 memprintf(&err, "certificate '%s' doesn't exist!\n", filename);
1815 goto error;
1816 }
1817 if (!LIST_ISEMPTY(&store->ckch_inst)) {
1818 memprintf(&err, "certificate '%s' in use, can't be deleted!\n", filename);
1819 goto error;
1820 }
1821
1822 ebmb_delete(&store->node);
1823 ckch_store_free(store);
1824
1825 memprintf(&err, "Certificate '%s' deleted!\n", filename);
1826
1827 HA_SPIN_UNLOCK(CKCH_LOCK, &ckch_lock);
1828 return cli_dynmsg(appctx, LOG_NOTICE, err);
1829
1830error:
1831 memprintf(&err, "Can't remove the certificate: %s\n", err ? err : "");
1832 HA_SPIN_UNLOCK(CKCH_LOCK, &ckch_lock);
1833 return cli_dynerr(appctx, err);
1834}
1835
William Lallemandee8530c2020-06-23 18:19:42 +02001836void ckch_deinit()
1837{
1838 struct eb_node *node, *next;
1839 struct ckch_store *store;
1840
1841 node = eb_first(&ckchs_tree);
1842 while (node) {
1843 next = eb_next(node);
1844 store = ebmb_entry(node, struct ckch_store, node);
1845 ckch_store_free(store);
1846 node = next;
1847 }
1848}
William Lallemandda8584c2020-05-14 10:14:37 +02001849
1850/* register cli keywords */
1851static struct cli_kw_list cli_kws = {{ },{
Willy Tarreaub205bfd2021-05-07 11:38:37 +02001852 { { "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 },
1853 { { "set", "ssl", "cert", NULL }, "set ssl cert <certfile> <payload> : replace a certificate file", cli_parse_set_cert, NULL, NULL },
1854 { { "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 },
1855 { { "abort", "ssl", "cert", NULL }, "abort ssl cert <certfile> : abort a transaction for a certificate file", cli_parse_abort_cert, NULL, NULL },
1856 { { "del", "ssl", "cert", NULL }, "del ssl cert <certfile> : delete an unused certificate file", cli_parse_del_cert, NULL, NULL },
1857 { { "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 +02001858 { { NULL }, NULL, NULL, NULL }
1859}};
1860
1861INITCALL1(STG_REGISTER, cli_register_kw, &cli_kws);
1862