blob: 174ab0a96c12d11a6f9c363b2745e3241c185fd9 [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
47/******************** cert_key_and_chain functions *************************
48 * These are the functions that fills a cert_key_and_chain structure. For the
49 * functions filling a SSL_CTX from a cert_key_and_chain, see ssl_sock.c
50 */
51
52/*
53 * Try to parse Signed Certificate Timestamp List structure. This function
54 * makes only basic test if the data seems like SCTL. No signature validation
55 * is performed.
56 */
57static int ssl_sock_parse_sctl(struct buffer *sctl)
58{
59 int ret = 1;
60 int len, pos, sct_len;
61 unsigned char *data;
62
63 if (sctl->data < 2)
64 goto out;
65
66 data = (unsigned char *) sctl->area;
67 len = (data[0] << 8) | data[1];
68
69 if (len + 2 != sctl->data)
70 goto out;
71
72 data = data + 2;
73 pos = 0;
74 while (pos < len) {
75 if (len - pos < 2)
76 goto out;
77
78 sct_len = (data[pos] << 8) | data[pos + 1];
79 if (pos + sct_len + 2 > len)
80 goto out;
81
82 pos += sct_len + 2;
83 }
84
85 ret = 0;
86
87out:
88 return ret;
89}
90
91/* Try to load a sctl from a buffer <buf> if not NULL, or read the file <sctl_path>
92 * It fills the ckch->sctl buffer
93 * return 0 on success or != 0 on failure */
94int ssl_sock_load_sctl_from_file(const char *sctl_path, char *buf, struct cert_key_and_chain *ckch, char **err)
95{
96 int fd = -1;
97 int r = 0;
98 int ret = 1;
99 struct buffer tmp;
100 struct buffer *src;
101 struct buffer *sctl;
102
103 if (buf) {
William Lallemand8d673942021-01-27 14:58:51 +0100104 chunk_initstr(&tmp, buf);
William Lallemand03c331c2020-05-13 10:10:01 +0200105 src = &tmp;
106 } else {
107 fd = open(sctl_path, O_RDONLY);
108 if (fd == -1)
109 goto end;
110
111 trash.data = 0;
112 while (trash.data < trash.size) {
113 r = read(fd, trash.area + trash.data, trash.size - trash.data);
114 if (r < 0) {
115 if (errno == EINTR)
116 continue;
117 goto end;
118 }
119 else if (r == 0) {
120 break;
121 }
122 trash.data += r;
123 }
124 src = &trash;
125 }
126
127 ret = ssl_sock_parse_sctl(src);
128 if (ret)
129 goto end;
130
131 sctl = calloc(1, sizeof(*sctl));
132 if (!chunk_dup(sctl, src)) {
133 free(sctl);
134 sctl = NULL;
135 goto end;
136 }
137 /* no error, fill ckch with new context, old context must be free */
138 if (ckch->sctl) {
139 free(ckch->sctl->area);
140 ckch->sctl->area = NULL;
141 free(ckch->sctl);
142 }
143 ckch->sctl = sctl;
144 ret = 0;
145end:
146 if (fd != -1)
147 close(fd);
148
149 return ret;
150}
151
152#if ((defined SSL_CTRL_SET_TLSEXT_STATUS_REQ_CB && !defined OPENSSL_NO_OCSP) || defined OPENSSL_IS_BORINGSSL)
153/*
Ilya Shipitsin46a030c2020-07-05 16:36:08 +0500154 * This function load the OCSP Response in DER format contained in file at
William Lallemand03c331c2020-05-13 10:10:01 +0200155 * path 'ocsp_path' or base64 in a buffer <buf>
156 *
157 * Returns 0 on success, 1 in error case.
158 */
159int ssl_sock_load_ocsp_response_from_file(const char *ocsp_path, char *buf, struct cert_key_and_chain *ckch, char **err)
160{
161 int fd = -1;
162 int r = 0;
163 int ret = 1;
164 struct buffer *ocsp_response;
165 struct buffer *src = NULL;
166
167 if (buf) {
168 int i, j;
169 /* if it's from a buffer it will be base64 */
170
171 /* remove \r and \n from the payload */
172 for (i = 0, j = 0; buf[i]; i++) {
173 if (buf[i] == '\r' || buf[i] == '\n')
174 continue;
175 buf[j++] = buf[i];
176 }
177 buf[j] = 0;
178
179 ret = base64dec(buf, j, trash.area, trash.size);
180 if (ret < 0) {
181 memprintf(err, "Error reading OCSP response in base64 format");
182 goto end;
183 }
184 trash.data = ret;
185 src = &trash;
186 } else {
187 fd = open(ocsp_path, O_RDONLY);
188 if (fd == -1) {
189 memprintf(err, "Error opening OCSP response file");
190 goto end;
191 }
192
193 trash.data = 0;
194 while (trash.data < trash.size) {
195 r = read(fd, trash.area + trash.data, trash.size - trash.data);
196 if (r < 0) {
197 if (errno == EINTR)
198 continue;
199
200 memprintf(err, "Error reading OCSP response from file");
201 goto end;
202 }
203 else if (r == 0) {
204 break;
205 }
206 trash.data += r;
207 }
208 close(fd);
209 fd = -1;
210 src = &trash;
211 }
212
213 ocsp_response = calloc(1, sizeof(*ocsp_response));
214 if (!chunk_dup(ocsp_response, src)) {
215 free(ocsp_response);
216 ocsp_response = NULL;
217 goto end;
218 }
219 /* no error, fill ckch with new context, old context must be free */
220 if (ckch->ocsp_response) {
221 free(ckch->ocsp_response->area);
222 ckch->ocsp_response->area = NULL;
223 free(ckch->ocsp_response);
224 }
225 ckch->ocsp_response = ocsp_response;
226 ret = 0;
227end:
228 if (fd != -1)
229 close(fd);
230
231 return ret;
232}
233#endif
234
235/*
236 * Try to load in a ckch every files related to a ckch.
237 * (PEM, sctl, ocsp, issuer etc.)
238 *
239 * This function is only used to load files during the configuration parsing,
240 * it is not used with the CLI.
241 *
242 * This allows us to carry the contents of the file without having to read the
243 * file multiple times. The caller must call
244 * ssl_sock_free_cert_key_and_chain_contents.
245 *
246 * returns:
247 * 0 on Success
248 * 1 on SSL Failure
249 */
250int ssl_sock_load_files_into_ckch(const char *path, struct cert_key_and_chain *ckch, char **err)
251{
William Lallemand8e8581e2020-10-20 17:36:46 +0200252 struct buffer *fp = NULL;
William Lallemand03c331c2020-05-13 10:10:01 +0200253 int ret = 1;
254
255 /* try to load the PEM */
256 if (ssl_sock_load_pem_into_ckch(path, NULL, ckch , err) != 0) {
257 goto end;
258 }
259
William Lallemand8e8581e2020-10-20 17:36:46 +0200260 fp = alloc_trash_chunk();
261 if (!fp) {
262 memprintf(err, "%sCan't allocate memory\n", err && *err ? *err : "");
263 goto end;
264 }
265
266 if (!chunk_strcpy(fp, path) || (b_data(fp) > MAXPATHLEN)) {
267 memprintf(err, "%s '%s' filename too long'.\n",
268 err && *err ? *err : "", fp->area);
269 ret = 1;
270 goto end;
271 }
272
William Lallemand089c1382020-10-23 17:35:12 +0200273 /* remove the ".crt" extension */
William Lallemand8e8581e2020-10-20 17:36:46 +0200274 if (global_ssl.extra_files_noext) {
275 char *ext;
276
277 /* look for the extension */
278 if ((ext = strrchr(fp->area, '.'))) {
William Lallemand8e8581e2020-10-20 17:36:46 +0200279
Tim Duesterhuse5ff1412021-01-02 22:31:53 +0100280 if (strcmp(ext, ".crt") == 0) {
William Lallemand8e8581e2020-10-20 17:36:46 +0200281 *ext = '\0';
William Lallemand089c1382020-10-23 17:35:12 +0200282 fp->data = strlen(fp->area);
283 }
William Lallemand8e8581e2020-10-20 17:36:46 +0200284 }
285
286 }
287
William Lallemand03c331c2020-05-13 10:10:01 +0200288 /* try to load an external private key if it wasn't in the PEM */
289 if ((ckch->key == NULL) && (global_ssl.extra_files & SSL_GF_KEY)) {
William Lallemand03c331c2020-05-13 10:10:01 +0200290 struct stat st;
291
William Lallemand8e8581e2020-10-20 17:36:46 +0200292
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 }
299
300 if (stat(fp->area, &st) == 0) {
301 if (ssl_sock_load_key_into_ckch(fp->area, NULL, ckch, err)) {
William Lallemand03c331c2020-05-13 10:10:01 +0200302 memprintf(err, "%s '%s' is present but cannot be read or parsed'.\n",
William Lallemand8e8581e2020-10-20 17:36:46 +0200303 err && *err ? *err : "", fp->area);
William Lallemand03c331c2020-05-13 10:10:01 +0200304 goto end;
305 }
306 }
William Lallemand03c331c2020-05-13 10:10:01 +0200307
William Lallemand8e8581e2020-10-20 17:36:46 +0200308 if (ckch->key == NULL) {
309 memprintf(err, "%sNo Private Key found in '%s'.\n", err && *err ? *err : "", fp->area);
310 goto end;
311 }
312 /* remove the added extension */
313 *(fp->area + fp->data - strlen(".key")) = '\0';
314 b_sub(fp, strlen(".key"));
William Lallemand03c331c2020-05-13 10:10:01 +0200315 }
316
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) {
565 free(ckch->ocsp_response->area);
566 ckch->ocsp_response->area = NULL;
567 free(ckch->ocsp_response);
568 ckch->ocsp_response = NULL;
569 }
570
571 if (ckch->sctl) {
572 free(ckch->sctl->area);
573 ckch->sctl->area = NULL;
574 free(ckch->sctl);
575 ckch->sctl = NULL;
576 }
577
578 if (ckch->ocsp_issuer) {
579 X509_free(ckch->ocsp_issuer);
580 ckch->ocsp_issuer = NULL;
581 }
582
583 /* no error, fill ckch with new context, old context will be free at end: */
584 SWAP(ckch->key, key);
585 SWAP(ckch->dh, dh);
586 SWAP(ckch->cert, cert);
587 SWAP(ckch->chain, chain);
588
589 ret = 0;
590
591end:
592
593 ERR_clear_error();
594 if (in)
595 BIO_free(in);
596 if (key)
597 EVP_PKEY_free(key);
598 if (dh)
599 DH_free(dh);
600 if (cert)
601 X509_free(cert);
602 if (chain)
603 sk_X509_pop_free(chain, X509_free);
604
605 return ret;
606}
607
608/* Frees the contents of a cert_key_and_chain
609 */
610void ssl_sock_free_cert_key_and_chain_contents(struct cert_key_and_chain *ckch)
611{
612 if (!ckch)
613 return;
614
615 /* Free the certificate and set pointer to NULL */
616 if (ckch->cert)
617 X509_free(ckch->cert);
618 ckch->cert = NULL;
619
620 /* Free the key and set pointer to NULL */
621 if (ckch->key)
622 EVP_PKEY_free(ckch->key);
623 ckch->key = NULL;
624
625 /* Free each certificate in the chain */
626 if (ckch->chain)
627 sk_X509_pop_free(ckch->chain, X509_free);
628 ckch->chain = NULL;
629
630 if (ckch->dh)
631 DH_free(ckch->dh);
632 ckch->dh = NULL;
633
634 if (ckch->sctl) {
635 free(ckch->sctl->area);
636 ckch->sctl->area = NULL;
637 free(ckch->sctl);
638 ckch->sctl = NULL;
639 }
640
641 if (ckch->ocsp_response) {
642 free(ckch->ocsp_response->area);
643 ckch->ocsp_response->area = NULL;
644 free(ckch->ocsp_response);
645 ckch->ocsp_response = NULL;
646 }
647
648 if (ckch->ocsp_issuer)
649 X509_free(ckch->ocsp_issuer);
650 ckch->ocsp_issuer = NULL;
651}
652
653/*
654 *
655 * This function copy a cert_key_and_chain in memory
656 *
657 * It's used to try to apply changes on a ckch before committing them, because
658 * most of the time it's not possible to revert those changes
659 *
660 * Return a the dst or NULL
661 */
662struct cert_key_and_chain *ssl_sock_copy_cert_key_and_chain(struct cert_key_and_chain *src,
663 struct cert_key_and_chain *dst)
664{
William Lallemand6c096142021-02-23 14:45:45 +0100665 if (!src || !dst)
666 return NULL;
667
William Lallemand03c331c2020-05-13 10:10:01 +0200668 if (src->cert) {
669 dst->cert = src->cert;
670 X509_up_ref(src->cert);
671 }
672
673 if (src->key) {
674 dst->key = src->key;
675 EVP_PKEY_up_ref(src->key);
676 }
677
678 if (src->chain) {
679 dst->chain = X509_chain_up_ref(src->chain);
680 }
681
682 if (src->dh) {
683 DH_up_ref(src->dh);
684 dst->dh = src->dh;
685 }
686
687 if (src->sctl) {
688 struct buffer *sctl;
689
690 sctl = calloc(1, sizeof(*sctl));
691 if (!chunk_dup(sctl, src->sctl)) {
692 free(sctl);
693 sctl = NULL;
694 goto error;
695 }
696 dst->sctl = sctl;
697 }
698
699 if (src->ocsp_response) {
700 struct buffer *ocsp_response;
701
702 ocsp_response = calloc(1, sizeof(*ocsp_response));
703 if (!chunk_dup(ocsp_response, src->ocsp_response)) {
704 free(ocsp_response);
705 ocsp_response = NULL;
706 goto error;
707 }
708 dst->ocsp_response = ocsp_response;
709 }
710
711 if (src->ocsp_issuer) {
712 X509_up_ref(src->ocsp_issuer);
713 dst->ocsp_issuer = src->ocsp_issuer;
714 }
715
716 return dst;
717
718error:
719
720 /* free everything */
721 ssl_sock_free_cert_key_and_chain_contents(dst);
722
723 return NULL;
724}
725
726/*
727 * return 0 on success or != 0 on failure
728 */
729int ssl_sock_load_issuer_file_into_ckch(const char *path, char *buf, struct cert_key_and_chain *ckch, char **err)
730{
731 int ret = 1;
732 BIO *in = NULL;
733 X509 *issuer;
734
735 if (buf) {
736 /* reading from a buffer */
737 in = BIO_new_mem_buf(buf, -1);
738 if (in == NULL) {
739 memprintf(err, "%sCan't allocate memory\n", err && *err ? *err : "");
740 goto end;
741 }
742
743 } else {
744 /* reading from a file */
745 in = BIO_new(BIO_s_file());
746 if (in == NULL)
747 goto end;
748
749 if (BIO_read_filename(in, path) <= 0)
750 goto end;
751 }
752
753 issuer = PEM_read_bio_X509_AUX(in, NULL, NULL, NULL);
754 if (!issuer) {
755 memprintf(err, "%s'%s' cannot be read or parsed'.\n",
756 err && *err ? *err : "", path);
757 goto end;
758 }
759 /* no error, fill ckch with new context, old context must be free */
760 if (ckch->ocsp_issuer)
761 X509_free(ckch->ocsp_issuer);
762 ckch->ocsp_issuer = issuer;
763 ret = 0;
764
765end:
766
767 ERR_clear_error();
768 if (in)
769 BIO_free(in);
770
771 return ret;
772}
773
774/******************** ckch_store functions ***********************************
775 * The ckch_store is a structure used to cache and index the SSL files used in
776 * configuration
777 */
778
779/*
780 * Free a ckch_store, its ckch, its instances and remove it from the ebtree
781 */
782void ckch_store_free(struct ckch_store *store)
783{
784 struct ckch_inst *inst, *inst_s;
785
786 if (!store)
787 return;
788
William Lallemandbd8e6ed2020-09-16 16:08:08 +0200789 ssl_sock_free_cert_key_and_chain_contents(store->ckch);
William Lallemand03c331c2020-05-13 10:10:01 +0200790
791 free(store->ckch);
792 store->ckch = NULL;
793
794 list_for_each_entry_safe(inst, inst_s, &store->ckch_inst, by_ckchs) {
795 ckch_inst_free(inst);
796 }
797 ebmb_delete(&store->node);
798 free(store);
799}
800
801/*
802 * create and initialize a ckch_store
803 * <path> is the key name
804 * <nmemb> is the number of store->ckch objects to allocate
805 *
806 * Return a ckch_store or NULL upon failure.
807 */
William Lallemandbd8e6ed2020-09-16 16:08:08 +0200808struct ckch_store *ckch_store_new(const char *filename)
William Lallemand03c331c2020-05-13 10:10:01 +0200809{
810 struct ckch_store *store;
811 int pathlen;
812
813 pathlen = strlen(filename);
814 store = calloc(1, sizeof(*store) + pathlen + 1);
815 if (!store)
816 return NULL;
817
William Lallemand03c331c2020-05-13 10:10:01 +0200818 memcpy(store->path, filename, pathlen + 1);
819
820 LIST_INIT(&store->ckch_inst);
821 LIST_INIT(&store->crtlist_entry);
822
William Lallemandbd8e6ed2020-09-16 16:08:08 +0200823 store->ckch = calloc(1, sizeof(*store->ckch));
William Lallemand03c331c2020-05-13 10:10:01 +0200824 if (!store->ckch)
825 goto error;
826
827 return store;
828error:
829 ckch_store_free(store);
830 return NULL;
831}
832
833/* allocate and duplicate a ckch_store
834 * Return a new ckch_store or NULL */
835struct ckch_store *ckchs_dup(const struct ckch_store *src)
836{
837 struct ckch_store *dst;
838
William Lallemand6c096142021-02-23 14:45:45 +0100839 if (!src)
840 return NULL;
841
William Lallemandbd8e6ed2020-09-16 16:08:08 +0200842 dst = ckch_store_new(src->path);
William Lallemand03c331c2020-05-13 10:10:01 +0200843
William Lallemandbd8e6ed2020-09-16 16:08:08 +0200844 if (!ssl_sock_copy_cert_key_and_chain(src->ckch, dst->ckch))
845 goto error;
William Lallemand03c331c2020-05-13 10:10:01 +0200846
847 return dst;
848
849error:
850 ckch_store_free(dst);
851
852 return NULL;
853}
854
855/*
856 * lookup a path into the ckchs tree.
857 */
858struct ckch_store *ckchs_lookup(char *path)
859{
860 struct ebmb_node *eb;
861
862 eb = ebst_lookup(&ckchs_tree, path);
863 if (!eb)
864 return NULL;
865
866 return ebmb_entry(eb, struct ckch_store, node);
867}
868
869/*
870 * This function allocate a ckch_store and populate it with certificates from files.
871 */
William Lallemandbd8e6ed2020-09-16 16:08:08 +0200872struct ckch_store *ckchs_load_cert_file(char *path, char **err)
William Lallemand03c331c2020-05-13 10:10:01 +0200873{
874 struct ckch_store *ckchs;
875
William Lallemandbd8e6ed2020-09-16 16:08:08 +0200876 ckchs = ckch_store_new(path);
William Lallemand03c331c2020-05-13 10:10:01 +0200877 if (!ckchs) {
878 memprintf(err, "%sunable to allocate memory.\n", err && *err ? *err : "");
879 goto end;
880 }
William Lallemand03c331c2020-05-13 10:10:01 +0200881
William Lallemandbd8e6ed2020-09-16 16:08:08 +0200882 if (ssl_sock_load_files_into_ckch(path, ckchs->ckch, err) == 1)
883 goto end;
William Lallemand03c331c2020-05-13 10:10:01 +0200884
William Lallemandbd8e6ed2020-09-16 16:08:08 +0200885 /* insert into the ckchs tree */
886 memcpy(ckchs->path, path, strlen(path) + 1);
887 ebst_insert(&ckchs_tree, &ckchs->node);
William Lallemand03c331c2020-05-13 10:10:01 +0200888 return ckchs;
889
890end:
891 ckch_store_free(ckchs);
892
893 return NULL;
894}
895
William Lallemandfa1d8b42020-05-13 15:46:10 +0200896
897/******************** ckch_inst functions ******************************/
898
899/* unlink a ckch_inst, free all SNIs, free the ckch_inst */
900/* The caller must use the lock of the bind_conf if used with inserted SNIs */
901void ckch_inst_free(struct ckch_inst *inst)
902{
903 struct sni_ctx *sni, *sni_s;
904
905 if (inst == NULL)
906 return;
907
908 list_for_each_entry_safe(sni, sni_s, &inst->sni_ctx, by_ckch_inst) {
909 SSL_CTX_free(sni->ctx);
910 LIST_DEL(&sni->by_ckch_inst);
911 ebmb_delete(&sni->name);
912 free(sni);
913 }
Remi Tricot-Le Bretonf3eedfe2021-01-25 17:19:44 +0100914 SSL_CTX_free(inst->ctx);
915 inst->ctx = NULL;
William Lallemandfa1d8b42020-05-13 15:46:10 +0200916 LIST_DEL(&inst->by_ckchs);
917 LIST_DEL(&inst->by_crtlist_entry);
918 free(inst);
919}
920
921/* Alloc and init a ckch_inst */
922struct ckch_inst *ckch_inst_new()
923{
924 struct ckch_inst *ckch_inst;
925
926 ckch_inst = calloc(1, sizeof *ckch_inst);
927 if (!ckch_inst)
928 return NULL;
929
930 LIST_INIT(&ckch_inst->sni_ctx);
931 LIST_INIT(&ckch_inst->by_ckchs);
932 LIST_INIT(&ckch_inst->by_crtlist_entry);
933
934 return ckch_inst;
935}
936
William Lallemandda8584c2020-05-14 10:14:37 +0200937/*************************** CLI commands ***********************/
938
939/* Type of SSL payloads that can be updated over the CLI */
940
941enum {
942 CERT_TYPE_PEM = 0,
943 CERT_TYPE_KEY,
944#if ((defined SSL_CTRL_SET_TLSEXT_STATUS_REQ_CB && !defined OPENSSL_NO_OCSP) || defined OPENSSL_IS_BORINGSSL)
945 CERT_TYPE_OCSP,
946#endif
947 CERT_TYPE_ISSUER,
Ilya Shipitsinc47d6762021-02-13 11:45:33 +0500948#ifdef HAVE_SSL_SCTL
William Lallemandda8584c2020-05-14 10:14:37 +0200949 CERT_TYPE_SCTL,
950#endif
951 CERT_TYPE_MAX,
952};
953
954struct {
955 const char *ext;
956 int type;
957 int (*load)(const char *path, char *payload, struct cert_key_and_chain *ckch, char **err);
958 /* add a parsing callback */
959} cert_exts[CERT_TYPE_MAX+1] = {
960 [CERT_TYPE_PEM] = { "", CERT_TYPE_PEM, &ssl_sock_load_pem_into_ckch }, /* default mode, no extensions */
961 [CERT_TYPE_KEY] = { "key", CERT_TYPE_KEY, &ssl_sock_load_key_into_ckch },
962#if ((defined SSL_CTRL_SET_TLSEXT_STATUS_REQ_CB && !defined OPENSSL_NO_OCSP) || defined OPENSSL_IS_BORINGSSL)
963 [CERT_TYPE_OCSP] = { "ocsp", CERT_TYPE_OCSP, &ssl_sock_load_ocsp_response_from_file },
964#endif
Ilya Shipitsinc47d6762021-02-13 11:45:33 +0500965#ifdef HAVE_SSL_SCTL
William Lallemandda8584c2020-05-14 10:14:37 +0200966 [CERT_TYPE_SCTL] = { "sctl", CERT_TYPE_SCTL, &ssl_sock_load_sctl_from_file },
967#endif
968 [CERT_TYPE_ISSUER] = { "issuer", CERT_TYPE_ISSUER, &ssl_sock_load_issuer_file_into_ckch },
969 [CERT_TYPE_MAX] = { NULL, CERT_TYPE_MAX, NULL },
970};
971
972
973/* release function of the `show ssl cert' command */
974static void cli_release_show_cert(struct appctx *appctx)
975{
976 HA_SPIN_UNLOCK(CKCH_LOCK, &ckch_lock);
977}
978
979/* IO handler of "show ssl cert <filename>" */
980static int cli_io_handler_show_cert(struct appctx *appctx)
981{
982 struct buffer *trash = alloc_trash_chunk();
983 struct ebmb_node *node;
984 struct stream_interface *si = appctx->owner;
985 struct ckch_store *ckchs;
986
987 if (trash == NULL)
988 return 1;
989
990 if (!appctx->ctx.ssl.old_ckchs) {
991 if (ckchs_transaction.old_ckchs) {
992 ckchs = ckchs_transaction.old_ckchs;
993 chunk_appendf(trash, "# transaction\n");
William Lallemand5685ccf2020-09-16 16:12:25 +0200994 chunk_appendf(trash, "*%s\n", ckchs->path);
William Lallemandda8584c2020-05-14 10:14:37 +0200995 }
996 }
997
998 if (!appctx->ctx.cli.p0) {
999 chunk_appendf(trash, "# filename\n");
1000 node = ebmb_first(&ckchs_tree);
1001 } else {
1002 node = &((struct ckch_store *)appctx->ctx.cli.p0)->node;
1003 }
1004 while (node) {
1005 ckchs = ebmb_entry(node, struct ckch_store, node);
William Lallemand5685ccf2020-09-16 16:12:25 +02001006 chunk_appendf(trash, "%s\n", ckchs->path);
William Lallemandda8584c2020-05-14 10:14:37 +02001007
1008 node = ebmb_next(node);
1009 if (ci_putchk(si_ic(si), trash) == -1) {
1010 si_rx_room_blk(si);
1011 goto yield;
1012 }
1013 }
1014
1015 appctx->ctx.cli.p0 = NULL;
1016 free_trash_chunk(trash);
1017 return 1;
1018yield:
1019
1020 free_trash_chunk(trash);
1021 appctx->ctx.cli.p0 = ckchs;
1022 return 0; /* should come back */
1023}
1024
1025/*
1026 * Extract and format the DNS SAN extensions and copy result into a chuink
1027 * Return 0;
1028 */
1029#ifdef SSL_CTRL_SET_TLSEXT_HOSTNAME
1030static int ssl_sock_get_san_oneline(X509 *cert, struct buffer *out)
1031{
1032 int i;
1033 char *str;
1034 STACK_OF(GENERAL_NAME) *names = NULL;
1035
1036 names = X509_get_ext_d2i(cert, NID_subject_alt_name, NULL, NULL);
1037 if (names) {
1038 for (i = 0; i < sk_GENERAL_NAME_num(names); i++) {
1039 GENERAL_NAME *name = sk_GENERAL_NAME_value(names, i);
1040 if (i > 0)
1041 chunk_appendf(out, ", ");
1042 if (name->type == GEN_DNS) {
1043 if (ASN1_STRING_to_UTF8((unsigned char **)&str, name->d.dNSName) >= 0) {
1044 chunk_appendf(out, "DNS:%s", str);
1045 OPENSSL_free(str);
1046 }
1047 }
1048 }
1049 sk_GENERAL_NAME_pop_free(names, GENERAL_NAME_free);
1050 }
1051 return 0;
1052}
1053#endif
1054
1055
1056
1057
1058/* IO handler of the details "show ssl cert <filename>" */
1059static int cli_io_handler_show_cert_detail(struct appctx *appctx)
1060{
1061 struct stream_interface *si = appctx->owner;
1062 struct ckch_store *ckchs = appctx->ctx.cli.p0;
1063 struct buffer *out = alloc_trash_chunk();
1064 struct buffer *tmp = alloc_trash_chunk();
1065 X509_NAME *name = NULL;
1066 STACK_OF(X509) *chain;
1067 unsigned int len = 0;
1068 int write = -1;
1069 BIO *bio = NULL;
1070 int i;
1071
1072 if (!tmp || !out)
1073 goto end_no_putchk;
1074
William Lallemand5685ccf2020-09-16 16:12:25 +02001075 chunk_appendf(out, "Filename: ");
1076 if (ckchs == ckchs_transaction.new_ckchs)
1077 chunk_appendf(out, "*");
1078 chunk_appendf(out, "%s\n", ckchs->path);
William Lallemandda8584c2020-05-14 10:14:37 +02001079
William Lallemand5685ccf2020-09-16 16:12:25 +02001080 chunk_appendf(out, "Status: ");
1081 if (ckchs->ckch->cert == NULL)
1082 chunk_appendf(out, "Empty\n");
1083 else if (LIST_ISEMPTY(&ckchs->ckch_inst))
1084 chunk_appendf(out, "Unused\n");
1085 else
1086 chunk_appendf(out, "Used\n");
William Lallemandda8584c2020-05-14 10:14:37 +02001087
William Lallemand5685ccf2020-09-16 16:12:25 +02001088 if (ckchs->ckch->cert == NULL)
1089 goto end;
William Lallemandda8584c2020-05-14 10:14:37 +02001090
William Lallemand5685ccf2020-09-16 16:12:25 +02001091 chain = ckchs->ckch->chain;
1092 if (chain == NULL) {
1093 struct issuer_chain *issuer;
1094 issuer = ssl_get0_issuer_chain(ckchs->ckch->cert);
1095 if (issuer) {
1096 chain = issuer->chain;
1097 chunk_appendf(out, "Chain Filename: ");
1098 chunk_appendf(out, "%s\n", issuer->path);
William Lallemandda8584c2020-05-14 10:14:37 +02001099 }
William Lallemand5685ccf2020-09-16 16:12:25 +02001100 }
1101 chunk_appendf(out, "Serial: ");
1102 if (ssl_sock_get_serial(ckchs->ckch->cert, tmp) == -1)
1103 goto end;
1104 dump_binary(out, tmp->area, tmp->data);
1105 chunk_appendf(out, "\n");
William Lallemandda8584c2020-05-14 10:14:37 +02001106
William Lallemand5685ccf2020-09-16 16:12:25 +02001107 chunk_appendf(out, "notBefore: ");
1108 chunk_reset(tmp);
1109 if ((bio = BIO_new(BIO_s_mem())) == NULL)
1110 goto end;
1111 if (ASN1_TIME_print(bio, X509_getm_notBefore(ckchs->ckch->cert)) == 0)
1112 goto end;
1113 write = BIO_read(bio, tmp->area, tmp->size-1);
1114 tmp->area[write] = '\0';
1115 BIO_free(bio);
1116 bio = NULL;
1117 chunk_appendf(out, "%s\n", tmp->area);
William Lallemandda8584c2020-05-14 10:14:37 +02001118
William Lallemand5685ccf2020-09-16 16:12:25 +02001119 chunk_appendf(out, "notAfter: ");
1120 chunk_reset(tmp);
1121 if ((bio = BIO_new(BIO_s_mem())) == NULL)
1122 goto end;
1123 if (ASN1_TIME_print(bio, X509_getm_notAfter(ckchs->ckch->cert)) == 0)
1124 goto end;
1125 if ((write = BIO_read(bio, tmp->area, tmp->size-1)) <= 0)
1126 goto end;
1127 tmp->area[write] = '\0';
1128 BIO_free(bio);
1129 bio = NULL;
1130 chunk_appendf(out, "%s\n", tmp->area);
William Lallemandda8584c2020-05-14 10:14:37 +02001131
1132#ifdef SSL_CTRL_SET_TLSEXT_HOSTNAME
William Lallemand5685ccf2020-09-16 16:12:25 +02001133 chunk_appendf(out, "Subject Alternative Name: ");
1134 if (ssl_sock_get_san_oneline(ckchs->ckch->cert, out) == -1)
1135 goto end;
1136 *(out->area + out->data) = '\0';
1137 chunk_appendf(out, "\n");
William Lallemandda8584c2020-05-14 10:14:37 +02001138#endif
William Lallemand5685ccf2020-09-16 16:12:25 +02001139 chunk_reset(tmp);
1140 chunk_appendf(out, "Algorithm: ");
1141 if (cert_get_pkey_algo(ckchs->ckch->cert, tmp) == 0)
1142 goto end;
1143 chunk_appendf(out, "%s\n", tmp->area);
William Lallemandda8584c2020-05-14 10:14:37 +02001144
William Lallemand5685ccf2020-09-16 16:12:25 +02001145 chunk_reset(tmp);
1146 chunk_appendf(out, "SHA1 FingerPrint: ");
1147 if (X509_digest(ckchs->ckch->cert, EVP_sha1(), (unsigned char *) tmp->area, &len) == 0)
1148 goto end;
1149 tmp->data = len;
1150 dump_binary(out, tmp->area, tmp->data);
1151 chunk_appendf(out, "\n");
William Lallemandda8584c2020-05-14 10:14:37 +02001152
William Lallemand5685ccf2020-09-16 16:12:25 +02001153 chunk_appendf(out, "Subject: ");
1154 if ((name = X509_get_subject_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 chunk_appendf(out, "Issuer: ");
1162 if ((name = X509_get_issuer_name(ckchs->ckch->cert)) == NULL)
1163 goto end;
1164 if ((ssl_sock_get_dn_oneline(name, tmp)) == -1)
1165 goto end;
1166 *(tmp->area + tmp->data) = '\0';
1167 chunk_appendf(out, "%s\n", tmp->area);
1168
1169 /* Displays subject of each certificate in the chain */
1170 for (i = 0; i < sk_X509_num(chain); i++) {
1171 X509 *ca = sk_X509_value(chain, i);
1172
1173 chunk_appendf(out, "Chain Subject: ");
1174 if ((name = X509_get_subject_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);
1180
William Lallemand5685ccf2020-09-16 16:12:25 +02001181 chunk_appendf(out, "Chain Issuer: ");
1182 if ((name = X509_get_issuer_name(ca)) == NULL)
William Lallemandda8584c2020-05-14 10:14:37 +02001183 goto end;
1184 if ((ssl_sock_get_dn_oneline(name, tmp)) == -1)
1185 goto end;
1186 *(tmp->area + tmp->data) = '\0';
1187 chunk_appendf(out, "%s\n", tmp->area);
William Lallemandda8584c2020-05-14 10:14:37 +02001188 }
1189
1190end:
1191 if (ci_putchk(si_ic(si), out) == -1) {
1192 si_rx_room_blk(si);
1193 goto yield;
1194 }
1195
1196end_no_putchk:
1197 if (bio)
1198 BIO_free(bio);
1199 free_trash_chunk(tmp);
1200 free_trash_chunk(out);
1201 return 1;
1202yield:
1203 free_trash_chunk(tmp);
1204 free_trash_chunk(out);
1205 return 0; /* should come back */
1206}
1207
1208/* parsing function for 'show ssl cert [certfile]' */
1209static int cli_parse_show_cert(char **args, char *payload, struct appctx *appctx, void *private)
1210{
1211 struct ckch_store *ckchs;
1212
1213 if (!cli_has_level(appctx, ACCESS_LVL_OPER))
1214 return cli_err(appctx, "Can't allocate memory!\n");
1215
1216 /* The operations on the CKCH architecture are locked so we can
1217 * manipulate ckch_store and ckch_inst */
1218 if (HA_SPIN_TRYLOCK(CKCH_LOCK, &ckch_lock))
1219 return cli_err(appctx, "Can't show!\nOperations on certificates are currently locked!\n");
1220
1221 /* check if there is a certificate to lookup */
1222 if (*args[3]) {
1223 if (*args[3] == '*') {
1224 if (!ckchs_transaction.new_ckchs)
1225 goto error;
1226
1227 ckchs = ckchs_transaction.new_ckchs;
1228
Tim Duesterhuse5ff1412021-01-02 22:31:53 +01001229 if (strcmp(args[3] + 1, ckchs->path) != 0)
William Lallemandda8584c2020-05-14 10:14:37 +02001230 goto error;
1231
1232 } else {
1233 if ((ckchs = ckchs_lookup(args[3])) == NULL)
1234 goto error;
1235
1236 }
1237
William Lallemandda8584c2020-05-14 10:14:37 +02001238 appctx->ctx.cli.p0 = ckchs;
1239 /* use the IO handler that shows details */
1240 appctx->io_handler = cli_io_handler_show_cert_detail;
1241 }
1242
1243 return 0;
1244
1245error:
1246 HA_SPIN_UNLOCK(CKCH_LOCK, &ckch_lock);
1247 return cli_err(appctx, "Can't display the certificate: Not found or the certificate is a bundle!\n");
1248}
1249
1250/* release function of the `set ssl cert' command, free things and unlock the spinlock */
1251static void cli_release_commit_cert(struct appctx *appctx)
1252{
1253 struct ckch_store *new_ckchs;
1254
1255 HA_SPIN_UNLOCK(CKCH_LOCK, &ckch_lock);
1256
1257 if (appctx->st2 != SETCERT_ST_FIN) {
1258 /* free every new sni_ctx and the new store, which are not in the trees so no spinlock there */
1259 new_ckchs = appctx->ctx.ssl.new_ckchs;
1260
1261 /* if the allocation failed, we need to free everything from the temporary list */
1262 ckch_store_free(new_ckchs);
1263 }
1264}
1265
1266/*
1267 * This function tries to create the new ckch_inst and their SNIs
1268 */
1269static int cli_io_handler_commit_cert(struct appctx *appctx)
1270{
1271 struct stream_interface *si = appctx->owner;
1272 int y = 0;
1273 char *err = NULL;
1274 int errcode = 0;
1275 struct ckch_store *old_ckchs, *new_ckchs = NULL;
1276 struct ckch_inst *ckchi, *ckchis;
1277 struct buffer *trash = alloc_trash_chunk();
1278 struct sni_ctx *sc0, *sc0s;
1279 struct crtlist_entry *entry;
1280
1281 if (trash == NULL)
1282 goto error;
1283
1284 if (unlikely(si_ic(si)->flags & (CF_WRITE_ERROR|CF_SHUTW)))
1285 goto error;
1286
1287 while (1) {
1288 switch (appctx->st2) {
1289 case SETCERT_ST_INIT:
1290 /* This state just print the update message */
1291 chunk_printf(trash, "Committing %s", ckchs_transaction.path);
1292 if (ci_putchk(si_ic(si), trash) == -1) {
1293 si_rx_room_blk(si);
1294 goto yield;
1295 }
1296 appctx->st2 = SETCERT_ST_GEN;
1297 /* fallthrough */
1298 case SETCERT_ST_GEN:
1299 /*
1300 * This state generates the ckch instances with their
1301 * sni_ctxs and SSL_CTX.
1302 *
1303 * Since the SSL_CTX generation can be CPU consumer, we
1304 * yield every 10 instances.
1305 */
1306
1307 old_ckchs = appctx->ctx.ssl.old_ckchs;
1308 new_ckchs = appctx->ctx.ssl.new_ckchs;
1309
1310 if (!new_ckchs)
1311 continue;
1312
1313 /* get the next ckchi to regenerate */
1314 ckchi = appctx->ctx.ssl.next_ckchi;
1315 /* we didn't start yet, set it to the first elem */
1316 if (ckchi == NULL)
1317 ckchi = LIST_ELEM(old_ckchs->ckch_inst.n, typeof(ckchi), by_ckchs);
1318
1319 /* walk through the old ckch_inst and creates new ckch_inst using the updated ckchs */
1320 list_for_each_entry_from(ckchi, &old_ckchs->ckch_inst, by_ckchs) {
1321 struct ckch_inst *new_inst;
1322 char **sni_filter = NULL;
1323 int fcount = 0;
1324
1325 /* it takes a lot of CPU to creates SSL_CTXs, so we yield every 10 CKCH instances */
1326 if (y >= 10) {
1327 /* save the next ckchi to compute */
1328 appctx->ctx.ssl.next_ckchi = ckchi;
1329 goto yield;
1330 }
1331
1332 if (ckchi->crtlist_entry) {
1333 sni_filter = ckchi->crtlist_entry->filters;
1334 fcount = ckchi->crtlist_entry->fcount;
1335 }
1336
Remi Tricot-Le Bretond817dc72021-01-25 17:19:43 +01001337 if (ckchi->is_server_instance)
William Lallemand795bd9b2021-01-26 11:27:42 +01001338 errcode |= ckch_inst_new_load_srv_store(new_ckchs->path, new_ckchs, &new_inst, &err);
Remi Tricot-Le Bretonf3eedfe2021-01-25 17:19:44 +01001339 else
1340 errcode |= ckch_inst_new_load_store(new_ckchs->path, new_ckchs, ckchi->bind_conf, ckchi->ssl_conf, sni_filter, fcount, &new_inst, &err);
William Lallemandda8584c2020-05-14 10:14:37 +02001341
1342 if (errcode & ERR_CODE)
1343 goto error;
1344
1345 /* if the previous ckchi was used as the default */
1346 if (ckchi->is_default)
1347 new_inst->is_default = 1;
1348
Remi Tricot-Le Bretonf3eedfe2021-01-25 17:19:44 +01001349 new_inst->is_server_instance = ckchi->is_server_instance;
1350 new_inst->server = ckchi->server;
1351 /* Create a new SSL_CTX and link it to the new instance. */
1352 if (new_inst->is_server_instance) {
William Lallemand795bd9b2021-01-26 11:27:42 +01001353 errcode |= ssl_sock_prepare_srv_ssl_ctx(ckchi->server, new_inst->ctx);
Remi Tricot-Le Bretonf3eedfe2021-01-25 17:19:44 +01001354 if (errcode & ERR_CODE)
1355 goto error;
Remi Tricot-Le Bretonf3eedfe2021-01-25 17:19:44 +01001356 }
1357
William Lallemanda55685b2020-12-15 14:57:46 +01001358 /* create the link to the crtlist_entry */
1359 new_inst->crtlist_entry = ckchi->crtlist_entry;
1360
William Lallemandda8584c2020-05-14 10:14:37 +02001361 /* we need to initialize the SSL_CTX generated */
1362 /* this iterate on the newly generated SNIs in the new instance to prepare their SSL_CTX */
1363 list_for_each_entry_safe(sc0, sc0s, &new_inst->sni_ctx, by_ckch_inst) {
1364 if (!sc0->order) { /* we initialized only the first SSL_CTX because it's the same in the other sni_ctx's */
1365 errcode |= ssl_sock_prepare_ctx(ckchi->bind_conf, ckchi->ssl_conf, sc0->ctx, &err);
1366 if (errcode & ERR_CODE)
1367 goto error;
1368 }
1369 }
1370
1371
1372 /* display one dot per new instance */
1373 chunk_appendf(trash, ".");
1374 /* link the new ckch_inst to the duplicate */
1375 LIST_ADDQ(&new_ckchs->ckch_inst, &new_inst->by_ckchs);
1376 y++;
1377 }
1378 appctx->st2 = SETCERT_ST_INSERT;
1379 /* fallthrough */
1380 case SETCERT_ST_INSERT:
1381 /* The generation is finished, we can insert everything */
1382
1383 old_ckchs = appctx->ctx.ssl.old_ckchs;
1384 new_ckchs = appctx->ctx.ssl.new_ckchs;
1385
1386 if (!new_ckchs)
1387 continue;
1388
1389 /* get the list of crtlist_entry in the old store, and update the pointers to the store */
1390 LIST_SPLICE(&new_ckchs->crtlist_entry, &old_ckchs->crtlist_entry);
1391 list_for_each_entry(entry, &new_ckchs->crtlist_entry, by_ckch_store) {
1392 ebpt_delete(&entry->node);
1393 /* change the ptr and reinsert the node */
1394 entry->node.key = new_ckchs;
1395 ebpt_insert(&entry->crtlist->entries, &entry->node);
1396 }
1397
William Lallemanda55685b2020-12-15 14:57:46 +01001398 /* insert the new ckch_insts in the crtlist_entry */
1399 list_for_each_entry(ckchi, &new_ckchs->ckch_inst, by_ckchs) {
1400 if (ckchi->crtlist_entry)
1401 LIST_ADD(&ckchi->crtlist_entry->ckch_inst, &ckchi->by_crtlist_entry);
1402 }
1403
William Lallemandda8584c2020-05-14 10:14:37 +02001404 /* First, we insert every new SNIs in the trees, also replace the default_ctx */
1405 list_for_each_entry_safe(ckchi, ckchis, &new_ckchs->ckch_inst, by_ckchs) {
Remi Tricot-Le Bretonf3eedfe2021-01-25 17:19:44 +01001406 /* The bind_conf will be null on server ckch_instances. */
1407 if (ckchi->is_server_instance) {
William Lallemande0de0a62021-02-03 18:51:01 +01001408 int i;
William Lallemand3ce6eed2021-02-08 10:43:44 +01001409 /* a lock is needed here since we have to free the SSL cache */
1410 HA_RWLOCK_WRLOCK(SSL_SERVER_LOCK, &ckchi->server->ssl_ctx.lock);
William Lallemand1dedb0a2021-01-26 10:18:57 +01001411 /* free the server current SSL_CTX */
1412 SSL_CTX_free(ckchi->server->ssl_ctx.ctx);
Remi Tricot-Le Bretonf3eedfe2021-01-25 17:19:44 +01001413 /* Actual ssl context update */
William Lallemand1dedb0a2021-01-26 10:18:57 +01001414 SSL_CTX_up_ref(ckchi->ctx);
Remi Tricot-Le Bretonf3eedfe2021-01-25 17:19:44 +01001415 ckchi->server->ssl_ctx.ctx = ckchi->ctx;
William Lallemand1dedb0a2021-01-26 10:18:57 +01001416 ckchi->server->ssl_ctx.inst = ckchi;
Remi Tricot-Le Bretonf3eedfe2021-01-25 17:19:44 +01001417
William Lallemande0de0a62021-02-03 18:51:01 +01001418 /* flush the session cache of the server */
1419 for (i = 0; i < global.nbthread; i++) {
1420 free(ckchi->server->ssl_ctx.reused_sess[i].ptr);
1421 ckchi->server->ssl_ctx.reused_sess[i].ptr = NULL;
1422 }
William Lallemand3ce6eed2021-02-08 10:43:44 +01001423 HA_RWLOCK_WRUNLOCK(SSL_SERVER_LOCK, &ckchi->server->ssl_ctx.lock);
William Lallemande0de0a62021-02-03 18:51:01 +01001424
William Lallemand1dedb0a2021-01-26 10:18:57 +01001425 } else {
Remi Tricot-Le Bretonf3eedfe2021-01-25 17:19:44 +01001426 HA_RWLOCK_WRLOCK(SNI_LOCK, &ckchi->bind_conf->sni_lock);
1427 ssl_sock_load_cert_sni(ckchi, ckchi->bind_conf);
1428 HA_RWLOCK_WRUNLOCK(SNI_LOCK, &ckchi->bind_conf->sni_lock);
1429 }
William Lallemandda8584c2020-05-14 10:14:37 +02001430 }
1431
1432 /* delete the old sni_ctx, the old ckch_insts and the ckch_store */
1433 list_for_each_entry_safe(ckchi, ckchis, &old_ckchs->ckch_inst, by_ckchs) {
William Lallemandda8584c2020-05-14 10:14:37 +02001434
William Lallemand1dedb0a2021-01-26 10:18:57 +01001435 if (ckchi->is_server_instance) {
1436 /* no lock for servers */
1437 ckch_inst_free(ckchi);
1438 } else {
1439 struct bind_conf __maybe_unused *bind_conf = ckchi->bind_conf;
1440
1441 HA_RWLOCK_WRLOCK(SNI_LOCK, &bind_conf->sni_lock);
1442 ckch_inst_free(ckchi);
1443 HA_RWLOCK_WRUNLOCK(SNI_LOCK, &bind_conf->sni_lock);
1444 }
William Lallemandda8584c2020-05-14 10:14:37 +02001445 }
1446
1447 /* Replace the old ckchs by the new one */
1448 ckch_store_free(old_ckchs);
1449 ebst_insert(&ckchs_tree, &new_ckchs->node);
1450 appctx->st2 = SETCERT_ST_FIN;
1451 /* fallthrough */
1452 case SETCERT_ST_FIN:
1453 /* we achieved the transaction, we can set everything to NULL */
1454 free(ckchs_transaction.path);
1455 ckchs_transaction.path = NULL;
1456 ckchs_transaction.new_ckchs = NULL;
1457 ckchs_transaction.old_ckchs = NULL;
1458 goto end;
1459 }
1460 }
1461end:
1462
1463 chunk_appendf(trash, "\n");
1464 if (errcode & ERR_WARN)
1465 chunk_appendf(trash, "%s", err);
1466 chunk_appendf(trash, "Success!\n");
1467 if (ci_putchk(si_ic(si), trash) == -1)
1468 si_rx_room_blk(si);
1469 free_trash_chunk(trash);
1470 /* success: call the release function and don't come back */
1471 return 1;
1472yield:
1473 /* store the state */
1474 if (ci_putchk(si_ic(si), trash) == -1)
1475 si_rx_room_blk(si);
1476 free_trash_chunk(trash);
1477 si_rx_endp_more(si); /* let's come back later */
1478 return 0; /* should come back */
1479
1480error:
1481 /* spin unlock and free are done in the release function */
1482 if (trash) {
1483 chunk_appendf(trash, "\n%sFailed!\n", err);
1484 if (ci_putchk(si_ic(si), trash) == -1)
1485 si_rx_room_blk(si);
1486 free_trash_chunk(trash);
1487 }
1488 /* error: call the release function and don't come back */
1489 return 1;
1490}
1491
1492/*
1493 * Parsing function of 'commit ssl cert'
1494 */
1495static int cli_parse_commit_cert(char **args, char *payload, struct appctx *appctx, void *private)
1496{
1497 char *err = NULL;
1498
1499 if (!cli_has_level(appctx, ACCESS_LVL_ADMIN))
1500 return 1;
1501
1502 if (!*args[3])
1503 return cli_err(appctx, "'commit ssl cert expects a filename\n");
1504
1505 /* The operations on the CKCH architecture are locked so we can
1506 * manipulate ckch_store and ckch_inst */
1507 if (HA_SPIN_TRYLOCK(CKCH_LOCK, &ckch_lock))
1508 return cli_err(appctx, "Can't commit the certificate!\nOperations on certificates are currently locked!\n");
1509
1510 if (!ckchs_transaction.path) {
1511 memprintf(&err, "No ongoing transaction! !\n");
1512 goto error;
1513 }
1514
1515 if (strcmp(ckchs_transaction.path, args[3]) != 0) {
1516 memprintf(&err, "The ongoing transaction is about '%s' but you are trying to set '%s'\n", ckchs_transaction.path, args[3]);
1517 goto error;
1518 }
1519
William Lallemand5685ccf2020-09-16 16:12:25 +02001520 /* if a certificate is here, a private key must be here too */
1521 if (ckchs_transaction.new_ckchs->ckch->cert && !ckchs_transaction.new_ckchs->ckch->key) {
1522 memprintf(&err, "The transaction must contain at least a certificate and a private key!\n");
1523 goto error;
1524 }
William Lallemanda9419522020-06-24 16:26:41 +02001525
William Lallemand5685ccf2020-09-16 16:12:25 +02001526 if (!X509_check_private_key(ckchs_transaction.new_ckchs->ckch->cert, ckchs_transaction.new_ckchs->ckch->key)) {
1527 memprintf(&err, "inconsistencies between private key and certificate loaded '%s'.\n", ckchs_transaction.path);
1528 goto error;
William Lallemandda8584c2020-05-14 10:14:37 +02001529 }
1530
1531 /* init the appctx structure */
1532 appctx->st2 = SETCERT_ST_INIT;
1533 appctx->ctx.ssl.next_ckchi = NULL;
1534 appctx->ctx.ssl.new_ckchs = ckchs_transaction.new_ckchs;
1535 appctx->ctx.ssl.old_ckchs = ckchs_transaction.old_ckchs;
1536
1537 /* we don't unlock there, it will be unlock after the IO handler, in the release handler */
1538 return 0;
1539
1540error:
1541
1542 HA_SPIN_UNLOCK(CKCH_LOCK, &ckch_lock);
1543 err = memprintf(&err, "%sCan't commit %s!\n", err ? err : "", args[3]);
1544
1545 return cli_dynerr(appctx, err);
1546}
1547
1548
1549
1550
1551/*
1552 * Parsing function of `set ssl cert`, it updates or creates a temporary ckch.
1553 */
1554static int cli_parse_set_cert(char **args, char *payload, struct appctx *appctx, void *private)
1555{
1556 struct ckch_store *new_ckchs = NULL;
1557 struct ckch_store *old_ckchs = NULL;
1558 char *err = NULL;
1559 int i;
William Lallemandda8584c2020-05-14 10:14:37 +02001560 int errcode = 0;
1561 char *end;
1562 int type = CERT_TYPE_PEM;
1563 struct cert_key_and_chain *ckch;
1564 struct buffer *buf;
1565
1566 if (!cli_has_level(appctx, ACCESS_LVL_ADMIN))
1567 return 1;
1568
William Lallemandda8584c2020-05-14 10:14:37 +02001569 if (!*args[3] || !payload)
1570 return cli_err(appctx, "'set ssl cert expects a filename and a certificate as a payload\n");
1571
1572 /* The operations on the CKCH architecture are locked so we can
1573 * manipulate ckch_store and ckch_inst */
1574 if (HA_SPIN_TRYLOCK(CKCH_LOCK, &ckch_lock))
1575 return cli_err(appctx, "Can't update the certificate!\nOperations on certificates are currently locked!\n");
1576
William Lallemande5ff4ad2020-06-08 09:40:37 +02001577 if ((buf = alloc_trash_chunk()) == NULL)
1578 return cli_err(appctx, "Can't allocate memory\n");
1579
William Lallemandda8584c2020-05-14 10:14:37 +02001580 if (!chunk_strcpy(buf, args[3])) {
1581 memprintf(&err, "%sCan't allocate memory\n", err ? err : "");
1582 errcode |= ERR_ALERT | ERR_FATAL;
1583 goto end;
1584 }
1585
1586 /* check which type of file we want to update */
1587 for (i = 0; cert_exts[i].type < CERT_TYPE_MAX; i++) {
1588 end = strrchr(buf->area, '.');
Tim Duesterhuse5ff1412021-01-02 22:31:53 +01001589 if (end && *cert_exts[i].ext && (strcmp(end + 1, cert_exts[i].ext) == 0)) {
William Lallemandda8584c2020-05-14 10:14:37 +02001590 *end = '\0';
William Lallemand089c1382020-10-23 17:35:12 +02001591 buf->data = strlen(buf->area);
William Lallemandda8584c2020-05-14 10:14:37 +02001592 type = cert_exts[i].type;
1593 break;
1594 }
1595 }
1596
1597 appctx->ctx.ssl.old_ckchs = NULL;
1598 appctx->ctx.ssl.new_ckchs = NULL;
1599
1600 /* if there is an ongoing transaction */
1601 if (ckchs_transaction.path) {
William Lallemandda8584c2020-05-14 10:14:37 +02001602 /* if there is an ongoing transaction, check if this is the same file */
1603 if (strcmp(ckchs_transaction.path, buf->area) != 0) {
William Lallemand089c1382020-10-23 17:35:12 +02001604 /* we didn't find the transaction, must try more cases below */
1605
1606 /* if the del-ext option is activated we should try to take a look at a ".crt" too. */
1607 if (type != CERT_TYPE_PEM && global_ssl.extra_files_noext) {
1608 if (!chunk_strcat(buf, ".crt")) {
1609 memprintf(&err, "%sCan't allocate memory\n", err ? err : "");
1610 errcode |= ERR_ALERT | ERR_FATAL;
1611 goto end;
1612 }
1613
1614 if (strcmp(ckchs_transaction.path, buf->area) != 0) {
1615 /* remove .crt of the error message */
1616 *(b_orig(buf) + b_data(buf) + strlen(".crt")) = '\0';
1617 b_sub(buf, strlen(".crt"));
1618
1619 memprintf(&err, "The ongoing transaction is about '%s' but you are trying to set '%s'\n", ckchs_transaction.path, buf->area);
1620 errcode |= ERR_ALERT | ERR_FATAL;
1621 goto end;
1622 }
1623 }
William Lallemandda8584c2020-05-14 10:14:37 +02001624 }
1625
1626 appctx->ctx.ssl.old_ckchs = ckchs_transaction.new_ckchs;
1627
1628 } else {
William Lallemandda8584c2020-05-14 10:14:37 +02001629
William Lallemand95fefa12020-09-09 12:01:33 +02001630 /* lookup for the certificate in the tree */
1631 appctx->ctx.ssl.old_ckchs = ckchs_lookup(buf->area);
William Lallemand089c1382020-10-23 17:35:12 +02001632
1633 if (!appctx->ctx.ssl.old_ckchs) {
1634 /* if the del-ext option is activated we should try to take a look at a ".crt" too. */
1635 if (type != CERT_TYPE_PEM && global_ssl.extra_files_noext) {
1636 if (!chunk_strcat(buf, ".crt")) {
1637 memprintf(&err, "%sCan't allocate memory\n", err ? err : "");
1638 errcode |= ERR_ALERT | ERR_FATAL;
1639 goto end;
1640 }
1641 appctx->ctx.ssl.old_ckchs = ckchs_lookup(buf->area);
1642 }
1643 }
William Lallemandda8584c2020-05-14 10:14:37 +02001644 }
1645
1646 if (!appctx->ctx.ssl.old_ckchs) {
1647 memprintf(&err, "%sCan't replace a certificate which is not referenced by the configuration!\n",
1648 err ? err : "");
1649 errcode |= ERR_ALERT | ERR_FATAL;
1650 goto end;
1651 }
1652
1653 if (!appctx->ctx.ssl.path) {
1654 /* this is a new transaction, set the path of the transaction */
1655 appctx->ctx.ssl.path = strdup(appctx->ctx.ssl.old_ckchs->path);
1656 if (!appctx->ctx.ssl.path) {
1657 memprintf(&err, "%sCan't allocate memory\n", err ? err : "");
1658 errcode |= ERR_ALERT | ERR_FATAL;
1659 goto end;
1660 }
1661 }
1662
1663 old_ckchs = appctx->ctx.ssl.old_ckchs;
1664
1665 /* duplicate the ckch store */
1666 new_ckchs = ckchs_dup(old_ckchs);
1667 if (!new_ckchs) {
1668 memprintf(&err, "%sCannot allocate memory!\n",
1669 err ? err : "");
1670 errcode |= ERR_ALERT | ERR_FATAL;
1671 goto end;
1672 }
1673
William Lallemand95fefa12020-09-09 12:01:33 +02001674 ckch = new_ckchs->ckch;
William Lallemandda8584c2020-05-14 10:14:37 +02001675
1676 /* appply the change on the duplicate */
1677 if (cert_exts[type].load(buf->area, payload, ckch, &err) != 0) {
1678 memprintf(&err, "%sCan't load the payload\n", err ? err : "");
1679 errcode |= ERR_ALERT | ERR_FATAL;
1680 goto end;
1681 }
1682
1683 appctx->ctx.ssl.new_ckchs = new_ckchs;
1684
1685 /* we succeed, we can save the ckchs in the transaction */
1686
1687 /* if there wasn't a transaction, update the old ckchs */
1688 if (!ckchs_transaction.old_ckchs) {
1689 ckchs_transaction.old_ckchs = appctx->ctx.ssl.old_ckchs;
1690 ckchs_transaction.path = appctx->ctx.ssl.path;
1691 err = memprintf(&err, "Transaction created for certificate %s!\n", ckchs_transaction.path);
1692 } else {
1693 err = memprintf(&err, "Transaction updated for certificate %s!\n", ckchs_transaction.path);
1694
1695 }
1696
1697 /* free the previous ckchs if there was a transaction */
1698 ckch_store_free(ckchs_transaction.new_ckchs);
1699
1700 ckchs_transaction.new_ckchs = appctx->ctx.ssl.new_ckchs;
1701
1702
1703 /* creates the SNI ctxs later in the IO handler */
1704
1705end:
1706 free_trash_chunk(buf);
1707
1708 if (errcode & ERR_CODE) {
1709
1710 ckch_store_free(appctx->ctx.ssl.new_ckchs);
1711 appctx->ctx.ssl.new_ckchs = NULL;
1712
1713 appctx->ctx.ssl.old_ckchs = NULL;
1714
1715 free(appctx->ctx.ssl.path);
1716 appctx->ctx.ssl.path = NULL;
1717
1718 HA_SPIN_UNLOCK(CKCH_LOCK, &ckch_lock);
1719 return cli_dynerr(appctx, memprintf(&err, "%sCan't update %s!\n", err ? err : "", args[3]));
1720 } else {
1721
1722 HA_SPIN_UNLOCK(CKCH_LOCK, &ckch_lock);
1723 return cli_dynmsg(appctx, LOG_NOTICE, err);
1724 }
1725 /* TODO: handle the ERR_WARN which are not handled because of the io_handler */
1726}
1727
1728/* parsing function of 'abort ssl cert' */
1729static int cli_parse_abort_cert(char **args, char *payload, struct appctx *appctx, void *private)
1730{
1731 char *err = NULL;
1732
1733 if (!cli_has_level(appctx, ACCESS_LVL_ADMIN))
1734 return 1;
1735
1736 if (!*args[3])
1737 return cli_err(appctx, "'abort ssl cert' expects a filename\n");
1738
1739 /* The operations on the CKCH architecture are locked so we can
1740 * manipulate ckch_store and ckch_inst */
1741 if (HA_SPIN_TRYLOCK(CKCH_LOCK, &ckch_lock))
1742 return cli_err(appctx, "Can't abort!\nOperations on certificates are currently locked!\n");
1743
1744 if (!ckchs_transaction.path) {
1745 memprintf(&err, "No ongoing transaction!\n");
1746 goto error;
1747 }
1748
1749 if (strcmp(ckchs_transaction.path, args[3]) != 0) {
1750 memprintf(&err, "The ongoing transaction is about '%s' but you are trying to abort a transaction for '%s'\n", ckchs_transaction.path, args[3]);
1751 goto error;
1752 }
1753
1754 /* Only free the ckchs there, because the SNI and instances were not generated yet */
1755 ckch_store_free(ckchs_transaction.new_ckchs);
1756 ckchs_transaction.new_ckchs = NULL;
William Lallemandda8584c2020-05-14 10:14:37 +02001757 ckchs_transaction.old_ckchs = NULL;
1758 free(ckchs_transaction.path);
1759 ckchs_transaction.path = NULL;
1760
1761 HA_SPIN_UNLOCK(CKCH_LOCK, &ckch_lock);
1762
1763 err = memprintf(&err, "Transaction aborted for certificate '%s'!\n", args[3]);
1764 return cli_dynmsg(appctx, LOG_NOTICE, err);
1765
1766error:
1767 HA_SPIN_UNLOCK(CKCH_LOCK, &ckch_lock);
1768
1769 return cli_dynerr(appctx, err);
1770}
1771
1772/* parsing function of 'new ssl cert' */
1773static int cli_parse_new_cert(char **args, char *payload, struct appctx *appctx, void *private)
1774{
1775 struct ckch_store *store;
1776 char *err = NULL;
1777 char *path;
1778
1779 if (!cli_has_level(appctx, ACCESS_LVL_ADMIN))
1780 return 1;
1781
1782 if (!*args[3])
1783 return cli_err(appctx, "'new ssl cert' expects a filename\n");
1784
1785 path = args[3];
1786
1787 /* The operations on the CKCH architecture are locked so we can
1788 * manipulate ckch_store and ckch_inst */
1789 if (HA_SPIN_TRYLOCK(CKCH_LOCK, &ckch_lock))
1790 return cli_err(appctx, "Can't create a certificate!\nOperations on certificates are currently locked!\n");
1791
1792 store = ckchs_lookup(path);
1793 if (store != NULL) {
1794 memprintf(&err, "Certificate '%s' already exists!\n", path);
1795 store = NULL; /* we don't want to free it */
1796 goto error;
1797 }
1798 /* we won't support multi-certificate bundle here */
William Lallemandbd8e6ed2020-09-16 16:08:08 +02001799 store = ckch_store_new(path);
William Lallemandda8584c2020-05-14 10:14:37 +02001800 if (!store) {
1801 memprintf(&err, "unable to allocate memory.\n");
1802 goto error;
1803 }
1804
1805 /* insert into the ckchs tree */
1806 ebst_insert(&ckchs_tree, &store->node);
1807 memprintf(&err, "New empty certificate store '%s'!\n", args[3]);
1808
1809 HA_SPIN_UNLOCK(CKCH_LOCK, &ckch_lock);
1810 return cli_dynmsg(appctx, LOG_NOTICE, err);
1811error:
1812 free(store);
1813 HA_SPIN_UNLOCK(CKCH_LOCK, &ckch_lock);
1814 return cli_dynerr(appctx, err);
1815}
1816
1817/* parsing function of 'del ssl cert' */
1818static int cli_parse_del_cert(char **args, char *payload, struct appctx *appctx, void *private)
1819{
1820 struct ckch_store *store;
1821 char *err = NULL;
1822 char *filename;
1823
1824 if (!cli_has_level(appctx, ACCESS_LVL_ADMIN))
1825 return 1;
1826
1827 if (!*args[3])
1828 return cli_err(appctx, "'del ssl cert' expects a certificate name\n");
1829
1830 if (HA_SPIN_TRYLOCK(CKCH_LOCK, &ckch_lock))
1831 return cli_err(appctx, "Can't delete the certificate!\nOperations on certificates are currently locked!\n");
1832
1833 filename = args[3];
1834
1835 store = ckchs_lookup(filename);
1836 if (store == NULL) {
1837 memprintf(&err, "certificate '%s' doesn't exist!\n", filename);
1838 goto error;
1839 }
1840 if (!LIST_ISEMPTY(&store->ckch_inst)) {
1841 memprintf(&err, "certificate '%s' in use, can't be deleted!\n", filename);
1842 goto error;
1843 }
1844
1845 ebmb_delete(&store->node);
1846 ckch_store_free(store);
1847
1848 memprintf(&err, "Certificate '%s' deleted!\n", filename);
1849
1850 HA_SPIN_UNLOCK(CKCH_LOCK, &ckch_lock);
1851 return cli_dynmsg(appctx, LOG_NOTICE, err);
1852
1853error:
1854 memprintf(&err, "Can't remove the certificate: %s\n", err ? err : "");
1855 HA_SPIN_UNLOCK(CKCH_LOCK, &ckch_lock);
1856 return cli_dynerr(appctx, err);
1857}
1858
William Lallemandee8530c2020-06-23 18:19:42 +02001859void ckch_deinit()
1860{
1861 struct eb_node *node, *next;
1862 struct ckch_store *store;
1863
1864 node = eb_first(&ckchs_tree);
1865 while (node) {
1866 next = eb_next(node);
1867 store = ebmb_entry(node, struct ckch_store, node);
1868 ckch_store_free(store);
1869 node = next;
1870 }
1871}
William Lallemandda8584c2020-05-14 10:14:37 +02001872
1873/* register cli keywords */
1874static struct cli_kw_list cli_kws = {{ },{
1875 { { "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 },
1876 { { "set", "ssl", "cert", NULL }, "set ssl cert <certfile> <payload> : replace a certificate file", cli_parse_set_cert, NULL, NULL },
1877 { { "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 },
1878 { { "abort", "ssl", "cert", NULL }, "abort ssl cert <certfile> : abort a transaction for a certificate file", cli_parse_abort_cert, NULL, NULL },
1879 { { "del", "ssl", "cert", NULL }, "del ssl cert <certfile> : delete an unused certificate file", cli_parse_del_cert, NULL, NULL },
1880 { { "show", "ssl", "cert", NULL }, "show ssl cert [<certfile>] : display the SSL certificates used in memory, or the details of a <certfile>", cli_parse_show_cert, cli_io_handler_show_cert, cli_release_show_cert },
1881 { { NULL }, NULL, NULL, NULL }
1882}};
1883
1884INITCALL1(STG_REGISTER, cli_register_kw, &cli_kws);
1885