blob: 9743532119d0c7abc75b47fb211f2bf6904e45b2 [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)) {
Willy Tarreau61cfdf42021-02-20 10:46:51 +0100133 ha_free(&sctl);
William Lallemand03c331c2020-05-13 10:10:01 +0200134 goto end;
135 }
136 /* no error, fill ckch with new context, old context must be free */
137 if (ckch->sctl) {
Willy Tarreau61cfdf42021-02-20 10:46:51 +0100138 ha_free(&ckch->sctl->area);
William Lallemand03c331c2020-05-13 10:10:01 +0200139 free(ckch->sctl);
140 }
141 ckch->sctl = sctl;
142 ret = 0;
143end:
144 if (fd != -1)
145 close(fd);
146
147 return ret;
148}
149
150#if ((defined SSL_CTRL_SET_TLSEXT_STATUS_REQ_CB && !defined OPENSSL_NO_OCSP) || defined OPENSSL_IS_BORINGSSL)
151/*
Ilya Shipitsin46a030c2020-07-05 16:36:08 +0500152 * This function load the OCSP Response in DER format contained in file at
William Lallemand03c331c2020-05-13 10:10:01 +0200153 * path 'ocsp_path' or base64 in a buffer <buf>
154 *
155 * Returns 0 on success, 1 in error case.
156 */
157int ssl_sock_load_ocsp_response_from_file(const char *ocsp_path, char *buf, struct cert_key_and_chain *ckch, char **err)
158{
159 int fd = -1;
160 int r = 0;
161 int ret = 1;
162 struct buffer *ocsp_response;
163 struct buffer *src = NULL;
164
165 if (buf) {
166 int i, j;
167 /* if it's from a buffer it will be base64 */
168
169 /* remove \r and \n from the payload */
170 for (i = 0, j = 0; buf[i]; i++) {
171 if (buf[i] == '\r' || buf[i] == '\n')
172 continue;
173 buf[j++] = buf[i];
174 }
175 buf[j] = 0;
176
177 ret = base64dec(buf, j, trash.area, trash.size);
178 if (ret < 0) {
179 memprintf(err, "Error reading OCSP response in base64 format");
180 goto end;
181 }
182 trash.data = ret;
183 src = &trash;
184 } else {
185 fd = open(ocsp_path, O_RDONLY);
186 if (fd == -1) {
187 memprintf(err, "Error opening OCSP response file");
188 goto end;
189 }
190
191 trash.data = 0;
192 while (trash.data < trash.size) {
193 r = read(fd, trash.area + trash.data, trash.size - trash.data);
194 if (r < 0) {
195 if (errno == EINTR)
196 continue;
197
198 memprintf(err, "Error reading OCSP response from file");
199 goto end;
200 }
201 else if (r == 0) {
202 break;
203 }
204 trash.data += r;
205 }
206 close(fd);
207 fd = -1;
208 src = &trash;
209 }
210
211 ocsp_response = calloc(1, sizeof(*ocsp_response));
212 if (!chunk_dup(ocsp_response, src)) {
Willy Tarreau61cfdf42021-02-20 10:46:51 +0100213 ha_free(&ocsp_response);
William Lallemand03c331c2020-05-13 10:10:01 +0200214 goto end;
215 }
216 /* no error, fill ckch with new context, old context must be free */
217 if (ckch->ocsp_response) {
Willy Tarreau61cfdf42021-02-20 10:46:51 +0100218 ha_free(&ckch->ocsp_response->area);
William Lallemand03c331c2020-05-13 10:10:01 +0200219 free(ckch->ocsp_response);
220 }
221 ckch->ocsp_response = ocsp_response;
222 ret = 0;
223end:
224 if (fd != -1)
225 close(fd);
226
227 return ret;
228}
229#endif
230
231/*
232 * Try to load in a ckch every files related to a ckch.
233 * (PEM, sctl, ocsp, issuer etc.)
234 *
235 * This function is only used to load files during the configuration parsing,
236 * it is not used with the CLI.
237 *
238 * This allows us to carry the contents of the file without having to read the
239 * file multiple times. The caller must call
240 * ssl_sock_free_cert_key_and_chain_contents.
241 *
242 * returns:
243 * 0 on Success
244 * 1 on SSL Failure
245 */
246int ssl_sock_load_files_into_ckch(const char *path, struct cert_key_and_chain *ckch, char **err)
247{
William Lallemand8e8581e2020-10-20 17:36:46 +0200248 struct buffer *fp = NULL;
William Lallemand03c331c2020-05-13 10:10:01 +0200249 int ret = 1;
Remi Tricot-Le Breton9f4503d2022-05-09 11:07:13 +0200250 struct stat st;
William Lallemand03c331c2020-05-13 10:10:01 +0200251
252 /* try to load the PEM */
253 if (ssl_sock_load_pem_into_ckch(path, NULL, ckch , err) != 0) {
254 goto end;
255 }
256
William Lallemand8e8581e2020-10-20 17:36:46 +0200257 fp = alloc_trash_chunk();
258 if (!fp) {
259 memprintf(err, "%sCan't allocate memory\n", err && *err ? *err : "");
260 goto end;
261 }
262
263 if (!chunk_strcpy(fp, path) || (b_data(fp) > MAXPATHLEN)) {
264 memprintf(err, "%s '%s' filename too long'.\n",
265 err && *err ? *err : "", fp->area);
266 ret = 1;
267 goto end;
268 }
269
William Lallemand089c1382020-10-23 17:35:12 +0200270 /* remove the ".crt" extension */
William Lallemand8e8581e2020-10-20 17:36:46 +0200271 if (global_ssl.extra_files_noext) {
272 char *ext;
273
274 /* look for the extension */
275 if ((ext = strrchr(fp->area, '.'))) {
William Lallemand8e8581e2020-10-20 17:36:46 +0200276
Tim Duesterhuse5ff1412021-01-02 22:31:53 +0100277 if (strcmp(ext, ".crt") == 0) {
William Lallemand8e8581e2020-10-20 17:36:46 +0200278 *ext = '\0';
William Lallemand089c1382020-10-23 17:35:12 +0200279 fp->data = strlen(fp->area);
280 }
William Lallemand8e8581e2020-10-20 17:36:46 +0200281 }
282
283 }
284
Remi Tricot-Le Breton9f4503d2022-05-09 11:07:13 +0200285 /* If no private key was found yet and we cannot look for it in extra
286 * files, raise an error.
287 */
288 if ((ckch->key == NULL) && !(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 Lallemand03c331c2020-05-13 10:10:01 +0200292
Remi Tricot-Le Breton9f4503d2022-05-09 11:07:13 +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",
296 err && *err ? *err : "", fp->area);
297 ret = 1;
298 goto end;
299 }
William Lallemand8e8581e2020-10-20 17:36:46 +0200300
Remi Tricot-Le Breton9f4503d2022-05-09 11:07:13 +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);
William Lallemand8e8581e2020-10-20 17:36:46 +0200305 goto end;
306 }
Remi Tricot-Le Breton9f4503d2022-05-09 11:07:13 +0200307 }
William Lallemand03c331c2020-05-13 10:10:01 +0200308
Remi Tricot-Le Breton9f4503d2022-05-09 11:07:13 +0200309 if (ckch->key == NULL) {
310 memprintf(err, "%sNo Private Key found in '%s'.\n", err && *err ? *err : "", fp->area);
311 goto end;
William Lallemand03c331c2020-05-13 10:10:01 +0200312 }
Remi Tricot-Le Breton9f4503d2022-05-09 11:07:13 +0200313 /* remove the added extension */
314 *(fp->area + fp->data - strlen(".key")) = '\0';
315 b_sub(fp, strlen(".key"));
316
William Lallemand03c331c2020-05-13 10:10:01 +0200317
318 if (!X509_check_private_key(ckch->cert, ckch->key)) {
319 memprintf(err, "%sinconsistencies between private key and certificate loaded '%s'.\n",
320 err && *err ? *err : "", path);
321 goto end;
322 }
323
Ilya Shipitsinc47d6762021-02-13 11:45:33 +0500324#ifdef HAVE_SSL_SCTL
William Lallemand03c331c2020-05-13 10:10:01 +0200325 /* try to load the sctl file */
326 if (global_ssl.extra_files & SSL_GF_SCTL) {
William Lallemand03c331c2020-05-13 10:10:01 +0200327 struct stat st;
328
William Lallemand8e8581e2020-10-20 17:36:46 +0200329 if (!chunk_strcat(fp, ".sctl") || b_data(fp) > MAXPATHLEN) {
330 memprintf(err, "%s '%s' filename too long'.\n",
331 err && *err ? *err : "", fp->area);
332 ret = 1;
333 goto end;
334 }
335
336 if (stat(fp->area, &st) == 0) {
337 if (ssl_sock_load_sctl_from_file(fp->area, NULL, ckch, err)) {
William Lallemand03c331c2020-05-13 10:10:01 +0200338 memprintf(err, "%s '%s.sctl' is present but cannot be read or parsed'.\n",
William Lallemand8e8581e2020-10-20 17:36:46 +0200339 err && *err ? *err : "", fp->area);
William Lallemand03c331c2020-05-13 10:10:01 +0200340 ret = 1;
341 goto end;
342 }
343 }
William Lallemand8e8581e2020-10-20 17:36:46 +0200344 /* remove the added extension */
345 *(fp->area + fp->data - strlen(".sctl")) = '\0';
346 b_sub(fp, strlen(".sctl"));
William Lallemand03c331c2020-05-13 10:10:01 +0200347 }
348#endif
349
350 /* try to load an ocsp response file */
351 if (global_ssl.extra_files & SSL_GF_OCSP) {
William Lallemand03c331c2020-05-13 10:10:01 +0200352 struct stat st;
353
William Lallemand8e8581e2020-10-20 17:36:46 +0200354 if (!chunk_strcat(fp, ".ocsp") || b_data(fp) > MAXPATHLEN) {
355 memprintf(err, "%s '%s' filename too long'.\n",
356 err && *err ? *err : "", fp->area);
357 ret = 1;
358 goto end;
359 }
360
361 if (stat(fp->area, &st) == 0) {
362 if (ssl_sock_load_ocsp_response_from_file(fp->area, NULL, ckch, err)) {
William Lallemand03c331c2020-05-13 10:10:01 +0200363 ret = 1;
364 goto end;
365 }
366 }
William Lallemand8e8581e2020-10-20 17:36:46 +0200367 /* remove the added extension */
368 *(fp->area + fp->data - strlen(".ocsp")) = '\0';
369 b_sub(fp, strlen(".ocsp"));
William Lallemand03c331c2020-05-13 10:10:01 +0200370 }
371
372#ifndef OPENSSL_IS_BORINGSSL /* Useless for BoringSSL */
373 if (ckch->ocsp_response && (global_ssl.extra_files & SSL_GF_OCSP_ISSUER)) {
374 /* if no issuer was found, try to load an issuer from the .issuer */
375 if (!ckch->ocsp_issuer) {
376 struct stat st;
William Lallemand8e8581e2020-10-20 17:36:46 +0200377
378 if (!chunk_strcat(fp, ".issuer") || b_data(fp) > MAXPATHLEN) {
379 memprintf(err, "%s '%s' filename too long'.\n",
380 err && *err ? *err : "", fp->area);
381 ret = 1;
382 goto end;
383 }
William Lallemand03c331c2020-05-13 10:10:01 +0200384
William Lallemand8e8581e2020-10-20 17:36:46 +0200385 if (stat(fp->area, &st) == 0) {
386 if (ssl_sock_load_issuer_file_into_ckch(fp->area, NULL, ckch, err)) {
William Lallemand03c331c2020-05-13 10:10:01 +0200387 ret = 1;
388 goto end;
389 }
390
391 if (X509_check_issued(ckch->ocsp_issuer, ckch->cert) != X509_V_OK) {
392 memprintf(err, "%s '%s' is not an issuer'.\n",
William Lallemand8e8581e2020-10-20 17:36:46 +0200393 err && *err ? *err : "", fp->area);
William Lallemand03c331c2020-05-13 10:10:01 +0200394 ret = 1;
395 goto end;
396 }
397 }
William Lallemand8e8581e2020-10-20 17:36:46 +0200398 /* remove the added extension */
399 *(fp->area + fp->data - strlen(".issuer")) = '\0';
400 b_sub(fp, strlen(".issuer"));
William Lallemand03c331c2020-05-13 10:10:01 +0200401 }
402 }
403#endif
404
405 ret = 0;
406
407end:
408
409 ERR_clear_error();
410
411 /* Something went wrong in one of the reads */
412 if (ret != 0)
413 ssl_sock_free_cert_key_and_chain_contents(ckch);
414
William Lallemand8e8581e2020-10-20 17:36:46 +0200415 free_trash_chunk(fp);
416
William Lallemand03c331c2020-05-13 10:10:01 +0200417 return ret;
418}
419
420/*
421 * Try to load a private key file from a <path> or a buffer <buf>
422 *
423 * If it failed you should not attempt to use the ckch but free it.
424 *
425 * Return 0 on success or != 0 on failure
426 */
427int ssl_sock_load_key_into_ckch(const char *path, char *buf, struct cert_key_and_chain *ckch , char **err)
428{
429 BIO *in = NULL;
430 int ret = 1;
431 EVP_PKEY *key = NULL;
432
433 if (buf) {
434 /* reading from a buffer */
435 in = BIO_new_mem_buf(buf, -1);
436 if (in == NULL) {
437 memprintf(err, "%sCan't allocate memory\n", err && *err ? *err : "");
438 goto end;
439 }
440
441 } else {
442 /* reading from a file */
443 in = BIO_new(BIO_s_file());
444 if (in == NULL)
445 goto end;
446
447 if (BIO_read_filename(in, path) <= 0)
448 goto end;
449 }
450
451 /* Read Private Key */
452 key = PEM_read_bio_PrivateKey(in, NULL, NULL, NULL);
453 if (key == NULL) {
454 memprintf(err, "%sunable to load private key from file '%s'.\n",
455 err && *err ? *err : "", path);
456 goto end;
457 }
458
459 ret = 0;
460
461 SWAP(ckch->key, key);
462
463end:
464
465 ERR_clear_error();
466 if (in)
467 BIO_free(in);
468 if (key)
469 EVP_PKEY_free(key);
470
471 return ret;
472}
473
474/*
475 * Try to load a PEM file from a <path> or a buffer <buf>
476 * The PEM must contain at least a Certificate,
477 * It could contain a DH, a certificate chain and a PrivateKey.
478 *
479 * If it failed you should not attempt to use the ckch but free it.
480 *
481 * Return 0 on success or != 0 on failure
482 */
483int ssl_sock_load_pem_into_ckch(const char *path, char *buf, struct cert_key_and_chain *ckch , char **err)
484{
485 BIO *in = NULL;
486 int ret = 1;
487 X509 *ca;
488 X509 *cert = NULL;
489 EVP_PKEY *key = NULL;
490 DH *dh = NULL;
491 STACK_OF(X509) *chain = NULL;
492
493 if (buf) {
494 /* reading from a buffer */
495 in = BIO_new_mem_buf(buf, -1);
496 if (in == NULL) {
497 memprintf(err, "%sCan't allocate memory\n", err && *err ? *err : "");
498 goto end;
499 }
500
501 } else {
502 /* reading from a file */
503 in = BIO_new(BIO_s_file());
504 if (in == NULL) {
505 memprintf(err, "%sCan't allocate memory\n", err && *err ? *err : "");
506 goto end;
507 }
508
509 if (BIO_read_filename(in, path) <= 0) {
510 memprintf(err, "%scannot open the file '%s'.\n",
511 err && *err ? *err : "", path);
512 goto end;
513 }
514 }
515
516 /* Read Private Key */
517 key = PEM_read_bio_PrivateKey(in, NULL, NULL, NULL);
518 /* no need to check for errors here, because the private key could be loaded later */
519
520#ifndef OPENSSL_NO_DH
521 /* Seek back to beginning of file */
522 if (BIO_reset(in) == -1) {
523 memprintf(err, "%san error occurred while reading the file '%s'.\n",
524 err && *err ? *err : "", path);
525 goto end;
526 }
527
528 dh = PEM_read_bio_DHparams(in, NULL, NULL, NULL);
529 /* no need to return an error there, dh is not mandatory */
530#endif
531
532 /* Seek back to beginning of file */
533 if (BIO_reset(in) == -1) {
534 memprintf(err, "%san error occurred while reading the file '%s'.\n",
535 err && *err ? *err : "", path);
536 goto end;
537 }
538
539 /* Read Certificate */
540 cert = PEM_read_bio_X509_AUX(in, NULL, NULL, NULL);
541 if (cert == NULL) {
542 memprintf(err, "%sunable to load certificate from file '%s'.\n",
543 err && *err ? *err : "", path);
544 goto end;
545 }
546
547 /* Look for a Certificate Chain */
548 while ((ca = PEM_read_bio_X509(in, NULL, NULL, NULL))) {
549 if (chain == NULL)
550 chain = sk_X509_new_null();
551 if (!sk_X509_push(chain, ca)) {
552 X509_free(ca);
553 goto end;
554 }
555 }
556
557 ret = ERR_get_error();
558 if (ret && (ERR_GET_LIB(ret) != ERR_LIB_PEM && ERR_GET_REASON(ret) != PEM_R_NO_START_LINE)) {
559 memprintf(err, "%sunable to load certificate chain from file '%s'.\n",
560 err && *err ? *err : "", path);
561 goto end;
562 }
563
564 /* once it loaded the PEM, it should remove everything else in the ckch */
565 if (ckch->ocsp_response) {
Willy Tarreau61cfdf42021-02-20 10:46:51 +0100566 ha_free(&ckch->ocsp_response->area);
567 ha_free(&ckch->ocsp_response);
William Lallemand03c331c2020-05-13 10:10:01 +0200568 }
569
570 if (ckch->sctl) {
Willy Tarreau61cfdf42021-02-20 10:46:51 +0100571 ha_free(&ckch->sctl->area);
572 ha_free(&ckch->sctl);
William Lallemand03c331c2020-05-13 10:10:01 +0200573 }
574
575 if (ckch->ocsp_issuer) {
576 X509_free(ckch->ocsp_issuer);
577 ckch->ocsp_issuer = NULL;
578 }
579
580 /* no error, fill ckch with new context, old context will be free at end: */
581 SWAP(ckch->key, key);
582 SWAP(ckch->dh, dh);
583 SWAP(ckch->cert, cert);
584 SWAP(ckch->chain, chain);
585
586 ret = 0;
587
588end:
589
590 ERR_clear_error();
591 if (in)
592 BIO_free(in);
593 if (key)
594 EVP_PKEY_free(key);
595 if (dh)
596 DH_free(dh);
597 if (cert)
598 X509_free(cert);
599 if (chain)
600 sk_X509_pop_free(chain, X509_free);
601
602 return ret;
603}
604
605/* Frees the contents of a cert_key_and_chain
606 */
607void ssl_sock_free_cert_key_and_chain_contents(struct cert_key_and_chain *ckch)
608{
609 if (!ckch)
610 return;
611
612 /* Free the certificate and set pointer to NULL */
613 if (ckch->cert)
614 X509_free(ckch->cert);
615 ckch->cert = NULL;
616
617 /* Free the key and set pointer to NULL */
618 if (ckch->key)
619 EVP_PKEY_free(ckch->key);
620 ckch->key = NULL;
621
622 /* Free each certificate in the chain */
623 if (ckch->chain)
624 sk_X509_pop_free(ckch->chain, X509_free);
625 ckch->chain = NULL;
626
627 if (ckch->dh)
628 DH_free(ckch->dh);
629 ckch->dh = NULL;
630
631 if (ckch->sctl) {
Willy Tarreau61cfdf42021-02-20 10:46:51 +0100632 ha_free(&ckch->sctl->area);
633 ha_free(&ckch->sctl);
William Lallemand03c331c2020-05-13 10:10:01 +0200634 }
635
636 if (ckch->ocsp_response) {
Willy Tarreau61cfdf42021-02-20 10:46:51 +0100637 ha_free(&ckch->ocsp_response->area);
638 ha_free(&ckch->ocsp_response);
William Lallemand03c331c2020-05-13 10:10:01 +0200639 }
640
641 if (ckch->ocsp_issuer)
642 X509_free(ckch->ocsp_issuer);
643 ckch->ocsp_issuer = NULL;
644}
645
646/*
647 *
648 * This function copy a cert_key_and_chain in memory
649 *
650 * It's used to try to apply changes on a ckch before committing them, because
651 * most of the time it's not possible to revert those changes
652 *
653 * Return a the dst or NULL
654 */
655struct cert_key_and_chain *ssl_sock_copy_cert_key_and_chain(struct cert_key_and_chain *src,
656 struct cert_key_and_chain *dst)
657{
William Lallemand6c096142021-02-23 14:45:45 +0100658 if (!src || !dst)
659 return NULL;
660
William Lallemand03c331c2020-05-13 10:10:01 +0200661 if (src->cert) {
662 dst->cert = src->cert;
663 X509_up_ref(src->cert);
664 }
665
666 if (src->key) {
667 dst->key = src->key;
668 EVP_PKEY_up_ref(src->key);
669 }
670
671 if (src->chain) {
672 dst->chain = X509_chain_up_ref(src->chain);
673 }
674
675 if (src->dh) {
676 DH_up_ref(src->dh);
677 dst->dh = src->dh;
678 }
679
680 if (src->sctl) {
681 struct buffer *sctl;
682
683 sctl = calloc(1, sizeof(*sctl));
684 if (!chunk_dup(sctl, src->sctl)) {
Willy Tarreau61cfdf42021-02-20 10:46:51 +0100685 ha_free(&sctl);
William Lallemand03c331c2020-05-13 10:10:01 +0200686 goto error;
687 }
688 dst->sctl = sctl;
689 }
690
691 if (src->ocsp_response) {
692 struct buffer *ocsp_response;
693
694 ocsp_response = calloc(1, sizeof(*ocsp_response));
695 if (!chunk_dup(ocsp_response, src->ocsp_response)) {
Willy Tarreau61cfdf42021-02-20 10:46:51 +0100696 ha_free(&ocsp_response);
William Lallemand03c331c2020-05-13 10:10:01 +0200697 goto error;
698 }
699 dst->ocsp_response = ocsp_response;
700 }
701
702 if (src->ocsp_issuer) {
703 X509_up_ref(src->ocsp_issuer);
704 dst->ocsp_issuer = src->ocsp_issuer;
705 }
706
707 return dst;
708
709error:
710
711 /* free everything */
712 ssl_sock_free_cert_key_and_chain_contents(dst);
713
714 return NULL;
715}
716
717/*
718 * return 0 on success or != 0 on failure
719 */
720int ssl_sock_load_issuer_file_into_ckch(const char *path, char *buf, struct cert_key_and_chain *ckch, char **err)
721{
722 int ret = 1;
723 BIO *in = NULL;
724 X509 *issuer;
725
726 if (buf) {
727 /* reading from a buffer */
728 in = BIO_new_mem_buf(buf, -1);
729 if (in == NULL) {
730 memprintf(err, "%sCan't allocate memory\n", err && *err ? *err : "");
731 goto end;
732 }
733
734 } else {
735 /* reading from a file */
736 in = BIO_new(BIO_s_file());
737 if (in == NULL)
738 goto end;
739
740 if (BIO_read_filename(in, path) <= 0)
741 goto end;
742 }
743
744 issuer = PEM_read_bio_X509_AUX(in, NULL, NULL, NULL);
745 if (!issuer) {
746 memprintf(err, "%s'%s' cannot be read or parsed'.\n",
747 err && *err ? *err : "", path);
748 goto end;
749 }
750 /* no error, fill ckch with new context, old context must be free */
751 if (ckch->ocsp_issuer)
752 X509_free(ckch->ocsp_issuer);
753 ckch->ocsp_issuer = issuer;
754 ret = 0;
755
756end:
757
758 ERR_clear_error();
759 if (in)
760 BIO_free(in);
761
762 return ret;
763}
764
765/******************** ckch_store functions ***********************************
766 * The ckch_store is a structure used to cache and index the SSL files used in
767 * configuration
768 */
769
770/*
771 * Free a ckch_store, its ckch, its instances and remove it from the ebtree
772 */
773void ckch_store_free(struct ckch_store *store)
774{
775 struct ckch_inst *inst, *inst_s;
776
777 if (!store)
778 return;
779
William Lallemandbd8e6ed2020-09-16 16:08:08 +0200780 ssl_sock_free_cert_key_and_chain_contents(store->ckch);
William Lallemand03c331c2020-05-13 10:10:01 +0200781
Willy Tarreau61cfdf42021-02-20 10:46:51 +0100782 ha_free(&store->ckch);
William Lallemand03c331c2020-05-13 10:10:01 +0200783
784 list_for_each_entry_safe(inst, inst_s, &store->ckch_inst, by_ckchs) {
785 ckch_inst_free(inst);
786 }
787 ebmb_delete(&store->node);
788 free(store);
789}
790
791/*
792 * create and initialize a ckch_store
793 * <path> is the key name
794 * <nmemb> is the number of store->ckch objects to allocate
795 *
796 * Return a ckch_store or NULL upon failure.
797 */
William Lallemandbd8e6ed2020-09-16 16:08:08 +0200798struct ckch_store *ckch_store_new(const char *filename)
William Lallemand03c331c2020-05-13 10:10:01 +0200799{
800 struct ckch_store *store;
801 int pathlen;
802
803 pathlen = strlen(filename);
804 store = calloc(1, sizeof(*store) + pathlen + 1);
805 if (!store)
806 return NULL;
807
William Lallemand03c331c2020-05-13 10:10:01 +0200808 memcpy(store->path, filename, pathlen + 1);
809
810 LIST_INIT(&store->ckch_inst);
811 LIST_INIT(&store->crtlist_entry);
812
William Lallemandbd8e6ed2020-09-16 16:08:08 +0200813 store->ckch = calloc(1, sizeof(*store->ckch));
William Lallemand03c331c2020-05-13 10:10:01 +0200814 if (!store->ckch)
815 goto error;
816
817 return store;
818error:
819 ckch_store_free(store);
820 return NULL;
821}
822
823/* allocate and duplicate a ckch_store
824 * Return a new ckch_store or NULL */
825struct ckch_store *ckchs_dup(const struct ckch_store *src)
826{
827 struct ckch_store *dst;
828
William Lallemand6c096142021-02-23 14:45:45 +0100829 if (!src)
830 return NULL;
831
William Lallemandbd8e6ed2020-09-16 16:08:08 +0200832 dst = ckch_store_new(src->path);
Eric Salama6ac61e32021-02-23 16:50:57 +0100833 if (!dst)
834 return NULL;
William Lallemand03c331c2020-05-13 10:10:01 +0200835
William Lallemandbd8e6ed2020-09-16 16:08:08 +0200836 if (!ssl_sock_copy_cert_key_and_chain(src->ckch, dst->ckch))
837 goto error;
William Lallemand03c331c2020-05-13 10:10:01 +0200838
839 return dst;
840
841error:
842 ckch_store_free(dst);
843
844 return NULL;
845}
846
847/*
848 * lookup a path into the ckchs tree.
849 */
850struct ckch_store *ckchs_lookup(char *path)
851{
852 struct ebmb_node *eb;
853
854 eb = ebst_lookup(&ckchs_tree, path);
855 if (!eb)
856 return NULL;
857
858 return ebmb_entry(eb, struct ckch_store, node);
859}
860
861/*
862 * This function allocate a ckch_store and populate it with certificates from files.
863 */
William Lallemandbd8e6ed2020-09-16 16:08:08 +0200864struct ckch_store *ckchs_load_cert_file(char *path, char **err)
William Lallemand03c331c2020-05-13 10:10:01 +0200865{
866 struct ckch_store *ckchs;
867
William Lallemandbd8e6ed2020-09-16 16:08:08 +0200868 ckchs = ckch_store_new(path);
William Lallemand03c331c2020-05-13 10:10:01 +0200869 if (!ckchs) {
870 memprintf(err, "%sunable to allocate memory.\n", err && *err ? *err : "");
871 goto end;
872 }
William Lallemand03c331c2020-05-13 10:10:01 +0200873
William Lallemandbd8e6ed2020-09-16 16:08:08 +0200874 if (ssl_sock_load_files_into_ckch(path, ckchs->ckch, err) == 1)
875 goto end;
William Lallemand03c331c2020-05-13 10:10:01 +0200876
William Lallemandbd8e6ed2020-09-16 16:08:08 +0200877 /* insert into the ckchs tree */
878 memcpy(ckchs->path, path, strlen(path) + 1);
879 ebst_insert(&ckchs_tree, &ckchs->node);
William Lallemand03c331c2020-05-13 10:10:01 +0200880 return ckchs;
881
882end:
883 ckch_store_free(ckchs);
884
885 return NULL;
886}
887
William Lallemandfa1d8b42020-05-13 15:46:10 +0200888
889/******************** ckch_inst functions ******************************/
890
891/* unlink a ckch_inst, free all SNIs, free the ckch_inst */
892/* The caller must use the lock of the bind_conf if used with inserted SNIs */
893void ckch_inst_free(struct ckch_inst *inst)
894{
895 struct sni_ctx *sni, *sni_s;
896
897 if (inst == NULL)
898 return;
899
900 list_for_each_entry_safe(sni, sni_s, &inst->sni_ctx, by_ckch_inst) {
901 SSL_CTX_free(sni->ctx);
Willy Tarreau2b718102021-04-21 07:32:39 +0200902 LIST_DELETE(&sni->by_ckch_inst);
William Lallemandfa1d8b42020-05-13 15:46:10 +0200903 ebmb_delete(&sni->name);
904 free(sni);
905 }
Remi Tricot-Le Bretonf3eedfe2021-01-25 17:19:44 +0100906 SSL_CTX_free(inst->ctx);
907 inst->ctx = NULL;
Willy Tarreau2b718102021-04-21 07:32:39 +0200908 LIST_DELETE(&inst->by_ckchs);
909 LIST_DELETE(&inst->by_crtlist_entry);
William Lallemandfa1d8b42020-05-13 15:46:10 +0200910 free(inst);
911}
912
913/* Alloc and init a ckch_inst */
914struct ckch_inst *ckch_inst_new()
915{
916 struct ckch_inst *ckch_inst;
917
918 ckch_inst = calloc(1, sizeof *ckch_inst);
919 if (!ckch_inst)
920 return NULL;
921
922 LIST_INIT(&ckch_inst->sni_ctx);
923 LIST_INIT(&ckch_inst->by_ckchs);
924 LIST_INIT(&ckch_inst->by_crtlist_entry);
925
926 return ckch_inst;
927}
928
William Lallemandda8584c2020-05-14 10:14:37 +0200929/*************************** CLI commands ***********************/
930
931/* Type of SSL payloads that can be updated over the CLI */
932
933enum {
934 CERT_TYPE_PEM = 0,
935 CERT_TYPE_KEY,
936#if ((defined SSL_CTRL_SET_TLSEXT_STATUS_REQ_CB && !defined OPENSSL_NO_OCSP) || defined OPENSSL_IS_BORINGSSL)
937 CERT_TYPE_OCSP,
938#endif
939 CERT_TYPE_ISSUER,
Ilya Shipitsinc47d6762021-02-13 11:45:33 +0500940#ifdef HAVE_SSL_SCTL
William Lallemandda8584c2020-05-14 10:14:37 +0200941 CERT_TYPE_SCTL,
942#endif
943 CERT_TYPE_MAX,
944};
945
946struct {
947 const char *ext;
948 int type;
949 int (*load)(const char *path, char *payload, struct cert_key_and_chain *ckch, char **err);
950 /* add a parsing callback */
951} cert_exts[CERT_TYPE_MAX+1] = {
952 [CERT_TYPE_PEM] = { "", CERT_TYPE_PEM, &ssl_sock_load_pem_into_ckch }, /* default mode, no extensions */
953 [CERT_TYPE_KEY] = { "key", CERT_TYPE_KEY, &ssl_sock_load_key_into_ckch },
954#if ((defined SSL_CTRL_SET_TLSEXT_STATUS_REQ_CB && !defined OPENSSL_NO_OCSP) || defined OPENSSL_IS_BORINGSSL)
955 [CERT_TYPE_OCSP] = { "ocsp", CERT_TYPE_OCSP, &ssl_sock_load_ocsp_response_from_file },
956#endif
Ilya Shipitsinc47d6762021-02-13 11:45:33 +0500957#ifdef HAVE_SSL_SCTL
William Lallemandda8584c2020-05-14 10:14:37 +0200958 [CERT_TYPE_SCTL] = { "sctl", CERT_TYPE_SCTL, &ssl_sock_load_sctl_from_file },
959#endif
960 [CERT_TYPE_ISSUER] = { "issuer", CERT_TYPE_ISSUER, &ssl_sock_load_issuer_file_into_ckch },
961 [CERT_TYPE_MAX] = { NULL, CERT_TYPE_MAX, NULL },
962};
963
964
965/* release function of the `show ssl cert' command */
966static void cli_release_show_cert(struct appctx *appctx)
967{
968 HA_SPIN_UNLOCK(CKCH_LOCK, &ckch_lock);
969}
970
971/* IO handler of "show ssl cert <filename>" */
972static int cli_io_handler_show_cert(struct appctx *appctx)
973{
974 struct buffer *trash = alloc_trash_chunk();
975 struct ebmb_node *node;
976 struct stream_interface *si = appctx->owner;
977 struct ckch_store *ckchs;
978
979 if (trash == NULL)
980 return 1;
981
982 if (!appctx->ctx.ssl.old_ckchs) {
983 if (ckchs_transaction.old_ckchs) {
984 ckchs = ckchs_transaction.old_ckchs;
985 chunk_appendf(trash, "# transaction\n");
William Lallemand5685ccf2020-09-16 16:12:25 +0200986 chunk_appendf(trash, "*%s\n", ckchs->path);
William Lallemandda8584c2020-05-14 10:14:37 +0200987 }
988 }
989
990 if (!appctx->ctx.cli.p0) {
991 chunk_appendf(trash, "# filename\n");
992 node = ebmb_first(&ckchs_tree);
993 } else {
994 node = &((struct ckch_store *)appctx->ctx.cli.p0)->node;
995 }
996 while (node) {
997 ckchs = ebmb_entry(node, struct ckch_store, node);
William Lallemand5685ccf2020-09-16 16:12:25 +0200998 chunk_appendf(trash, "%s\n", ckchs->path);
William Lallemandda8584c2020-05-14 10:14:37 +0200999
1000 node = ebmb_next(node);
1001 if (ci_putchk(si_ic(si), trash) == -1) {
1002 si_rx_room_blk(si);
1003 goto yield;
1004 }
1005 }
1006
1007 appctx->ctx.cli.p0 = NULL;
1008 free_trash_chunk(trash);
1009 return 1;
1010yield:
1011
1012 free_trash_chunk(trash);
1013 appctx->ctx.cli.p0 = ckchs;
1014 return 0; /* should come back */
1015}
1016
1017/*
1018 * Extract and format the DNS SAN extensions and copy result into a chuink
1019 * Return 0;
1020 */
1021#ifdef SSL_CTRL_SET_TLSEXT_HOSTNAME
1022static int ssl_sock_get_san_oneline(X509 *cert, struct buffer *out)
1023{
1024 int i;
1025 char *str;
1026 STACK_OF(GENERAL_NAME) *names = NULL;
1027
1028 names = X509_get_ext_d2i(cert, NID_subject_alt_name, NULL, NULL);
1029 if (names) {
1030 for (i = 0; i < sk_GENERAL_NAME_num(names); i++) {
1031 GENERAL_NAME *name = sk_GENERAL_NAME_value(names, i);
1032 if (i > 0)
1033 chunk_appendf(out, ", ");
1034 if (name->type == GEN_DNS) {
1035 if (ASN1_STRING_to_UTF8((unsigned char **)&str, name->d.dNSName) >= 0) {
1036 chunk_appendf(out, "DNS:%s", str);
1037 OPENSSL_free(str);
1038 }
1039 }
1040 }
1041 sk_GENERAL_NAME_pop_free(names, GENERAL_NAME_free);
1042 }
1043 return 0;
1044}
1045#endif
1046
1047
1048
1049
1050/* IO handler of the details "show ssl cert <filename>" */
1051static int cli_io_handler_show_cert_detail(struct appctx *appctx)
1052{
1053 struct stream_interface *si = appctx->owner;
1054 struct ckch_store *ckchs = appctx->ctx.cli.p0;
1055 struct buffer *out = alloc_trash_chunk();
1056 struct buffer *tmp = alloc_trash_chunk();
1057 X509_NAME *name = NULL;
1058 STACK_OF(X509) *chain;
1059 unsigned int len = 0;
1060 int write = -1;
1061 BIO *bio = NULL;
1062 int i;
1063
1064 if (!tmp || !out)
1065 goto end_no_putchk;
1066
William Lallemand5685ccf2020-09-16 16:12:25 +02001067 chunk_appendf(out, "Filename: ");
1068 if (ckchs == ckchs_transaction.new_ckchs)
1069 chunk_appendf(out, "*");
1070 chunk_appendf(out, "%s\n", ckchs->path);
William Lallemandda8584c2020-05-14 10:14:37 +02001071
William Lallemand5685ccf2020-09-16 16:12:25 +02001072 chunk_appendf(out, "Status: ");
1073 if (ckchs->ckch->cert == NULL)
1074 chunk_appendf(out, "Empty\n");
1075 else if (LIST_ISEMPTY(&ckchs->ckch_inst))
1076 chunk_appendf(out, "Unused\n");
1077 else
1078 chunk_appendf(out, "Used\n");
William Lallemandda8584c2020-05-14 10:14:37 +02001079
William Lallemand5685ccf2020-09-16 16:12:25 +02001080 if (ckchs->ckch->cert == NULL)
1081 goto end;
William Lallemandda8584c2020-05-14 10:14:37 +02001082
William Lallemand5685ccf2020-09-16 16:12:25 +02001083 chain = ckchs->ckch->chain;
1084 if (chain == NULL) {
1085 struct issuer_chain *issuer;
1086 issuer = ssl_get0_issuer_chain(ckchs->ckch->cert);
1087 if (issuer) {
1088 chain = issuer->chain;
1089 chunk_appendf(out, "Chain Filename: ");
1090 chunk_appendf(out, "%s\n", issuer->path);
William Lallemandda8584c2020-05-14 10:14:37 +02001091 }
William Lallemand5685ccf2020-09-16 16:12:25 +02001092 }
1093 chunk_appendf(out, "Serial: ");
1094 if (ssl_sock_get_serial(ckchs->ckch->cert, tmp) == -1)
1095 goto end;
1096 dump_binary(out, tmp->area, tmp->data);
1097 chunk_appendf(out, "\n");
William Lallemandda8584c2020-05-14 10:14:37 +02001098
William Lallemand5685ccf2020-09-16 16:12:25 +02001099 chunk_appendf(out, "notBefore: ");
1100 chunk_reset(tmp);
1101 if ((bio = BIO_new(BIO_s_mem())) == NULL)
1102 goto end;
1103 if (ASN1_TIME_print(bio, X509_getm_notBefore(ckchs->ckch->cert)) == 0)
1104 goto end;
1105 write = BIO_read(bio, tmp->area, tmp->size-1);
1106 tmp->area[write] = '\0';
1107 BIO_free(bio);
1108 bio = NULL;
1109 chunk_appendf(out, "%s\n", tmp->area);
William Lallemandda8584c2020-05-14 10:14:37 +02001110
William Lallemand5685ccf2020-09-16 16:12:25 +02001111 chunk_appendf(out, "notAfter: ");
1112 chunk_reset(tmp);
1113 if ((bio = BIO_new(BIO_s_mem())) == NULL)
1114 goto end;
1115 if (ASN1_TIME_print(bio, X509_getm_notAfter(ckchs->ckch->cert)) == 0)
1116 goto end;
1117 if ((write = BIO_read(bio, tmp->area, tmp->size-1)) <= 0)
1118 goto end;
1119 tmp->area[write] = '\0';
1120 BIO_free(bio);
1121 bio = NULL;
1122 chunk_appendf(out, "%s\n", tmp->area);
William Lallemandda8584c2020-05-14 10:14:37 +02001123
1124#ifdef SSL_CTRL_SET_TLSEXT_HOSTNAME
William Lallemand5685ccf2020-09-16 16:12:25 +02001125 chunk_appendf(out, "Subject Alternative Name: ");
1126 if (ssl_sock_get_san_oneline(ckchs->ckch->cert, out) == -1)
1127 goto end;
1128 *(out->area + out->data) = '\0';
1129 chunk_appendf(out, "\n");
William Lallemandda8584c2020-05-14 10:14:37 +02001130#endif
William Lallemand5685ccf2020-09-16 16:12:25 +02001131 chunk_reset(tmp);
1132 chunk_appendf(out, "Algorithm: ");
1133 if (cert_get_pkey_algo(ckchs->ckch->cert, tmp) == 0)
1134 goto end;
1135 chunk_appendf(out, "%s\n", tmp->area);
William Lallemandda8584c2020-05-14 10:14:37 +02001136
William Lallemand5685ccf2020-09-16 16:12:25 +02001137 chunk_reset(tmp);
1138 chunk_appendf(out, "SHA1 FingerPrint: ");
1139 if (X509_digest(ckchs->ckch->cert, EVP_sha1(), (unsigned char *) tmp->area, &len) == 0)
1140 goto end;
1141 tmp->data = len;
1142 dump_binary(out, tmp->area, tmp->data);
1143 chunk_appendf(out, "\n");
William Lallemandda8584c2020-05-14 10:14:37 +02001144
William Lallemand5685ccf2020-09-16 16:12:25 +02001145 chunk_appendf(out, "Subject: ");
1146 if ((name = X509_get_subject_name(ckchs->ckch->cert)) == NULL)
1147 goto end;
1148 if ((ssl_sock_get_dn_oneline(name, tmp)) == -1)
1149 goto end;
1150 *(tmp->area + tmp->data) = '\0';
1151 chunk_appendf(out, "%s\n", tmp->area);
1152
1153 chunk_appendf(out, "Issuer: ");
1154 if ((name = X509_get_issuer_name(ckchs->ckch->cert)) == NULL)
1155 goto end;
1156 if ((ssl_sock_get_dn_oneline(name, tmp)) == -1)
1157 goto end;
1158 *(tmp->area + tmp->data) = '\0';
1159 chunk_appendf(out, "%s\n", tmp->area);
1160
1161 /* Displays subject of each certificate in the chain */
1162 for (i = 0; i < sk_X509_num(chain); i++) {
1163 X509 *ca = sk_X509_value(chain, i);
1164
1165 chunk_appendf(out, "Chain Subject: ");
1166 if ((name = X509_get_subject_name(ca)) == NULL)
William Lallemandda8584c2020-05-14 10:14:37 +02001167 goto end;
1168 if ((ssl_sock_get_dn_oneline(name, tmp)) == -1)
1169 goto end;
1170 *(tmp->area + tmp->data) = '\0';
1171 chunk_appendf(out, "%s\n", tmp->area);
1172
William Lallemand5685ccf2020-09-16 16:12:25 +02001173 chunk_appendf(out, "Chain Issuer: ");
1174 if ((name = X509_get_issuer_name(ca)) == NULL)
William Lallemandda8584c2020-05-14 10:14:37 +02001175 goto end;
1176 if ((ssl_sock_get_dn_oneline(name, tmp)) == -1)
1177 goto end;
1178 *(tmp->area + tmp->data) = '\0';
1179 chunk_appendf(out, "%s\n", tmp->area);
William Lallemandda8584c2020-05-14 10:14:37 +02001180 }
1181
1182end:
1183 if (ci_putchk(si_ic(si), out) == -1) {
1184 si_rx_room_blk(si);
1185 goto yield;
1186 }
1187
1188end_no_putchk:
1189 if (bio)
1190 BIO_free(bio);
1191 free_trash_chunk(tmp);
1192 free_trash_chunk(out);
1193 return 1;
1194yield:
1195 free_trash_chunk(tmp);
1196 free_trash_chunk(out);
1197 return 0; /* should come back */
1198}
1199
1200/* parsing function for 'show ssl cert [certfile]' */
1201static int cli_parse_show_cert(char **args, char *payload, struct appctx *appctx, void *private)
1202{
1203 struct ckch_store *ckchs;
1204
1205 if (!cli_has_level(appctx, ACCESS_LVL_OPER))
1206 return cli_err(appctx, "Can't allocate memory!\n");
1207
1208 /* The operations on the CKCH architecture are locked so we can
1209 * manipulate ckch_store and ckch_inst */
1210 if (HA_SPIN_TRYLOCK(CKCH_LOCK, &ckch_lock))
1211 return cli_err(appctx, "Can't show!\nOperations on certificates are currently locked!\n");
1212
1213 /* check if there is a certificate to lookup */
1214 if (*args[3]) {
1215 if (*args[3] == '*') {
1216 if (!ckchs_transaction.new_ckchs)
1217 goto error;
1218
1219 ckchs = ckchs_transaction.new_ckchs;
1220
Tim Duesterhuse5ff1412021-01-02 22:31:53 +01001221 if (strcmp(args[3] + 1, ckchs->path) != 0)
William Lallemandda8584c2020-05-14 10:14:37 +02001222 goto error;
1223
1224 } else {
1225 if ((ckchs = ckchs_lookup(args[3])) == NULL)
1226 goto error;
1227
1228 }
1229
William Lallemandda8584c2020-05-14 10:14:37 +02001230 appctx->ctx.cli.p0 = ckchs;
1231 /* use the IO handler that shows details */
1232 appctx->io_handler = cli_io_handler_show_cert_detail;
1233 }
1234
1235 return 0;
1236
1237error:
1238 HA_SPIN_UNLOCK(CKCH_LOCK, &ckch_lock);
1239 return cli_err(appctx, "Can't display the certificate: Not found or the certificate is a bundle!\n");
1240}
1241
1242/* release function of the `set ssl cert' command, free things and unlock the spinlock */
1243static void cli_release_commit_cert(struct appctx *appctx)
1244{
1245 struct ckch_store *new_ckchs;
1246
1247 HA_SPIN_UNLOCK(CKCH_LOCK, &ckch_lock);
1248
1249 if (appctx->st2 != SETCERT_ST_FIN) {
1250 /* free every new sni_ctx and the new store, which are not in the trees so no spinlock there */
1251 new_ckchs = appctx->ctx.ssl.new_ckchs;
1252
1253 /* if the allocation failed, we need to free everything from the temporary list */
1254 ckch_store_free(new_ckchs);
1255 }
1256}
1257
1258/*
1259 * This function tries to create the new ckch_inst and their SNIs
1260 */
1261static int cli_io_handler_commit_cert(struct appctx *appctx)
1262{
1263 struct stream_interface *si = appctx->owner;
1264 int y = 0;
1265 char *err = NULL;
1266 int errcode = 0;
Remi Tricot-Le Breton43899ec2021-04-21 15:32:46 +02001267 int retval = 0;
William Lallemandda8584c2020-05-14 10:14:37 +02001268 struct ckch_store *old_ckchs, *new_ckchs = NULL;
1269 struct ckch_inst *ckchi, *ckchis;
1270 struct buffer *trash = alloc_trash_chunk();
1271 struct sni_ctx *sc0, *sc0s;
1272 struct crtlist_entry *entry;
1273
1274 if (trash == NULL)
1275 goto error;
1276
1277 if (unlikely(si_ic(si)->flags & (CF_WRITE_ERROR|CF_SHUTW)))
1278 goto error;
1279
1280 while (1) {
1281 switch (appctx->st2) {
1282 case SETCERT_ST_INIT:
1283 /* This state just print the update message */
1284 chunk_printf(trash, "Committing %s", ckchs_transaction.path);
1285 if (ci_putchk(si_ic(si), trash) == -1) {
1286 si_rx_room_blk(si);
1287 goto yield;
1288 }
1289 appctx->st2 = SETCERT_ST_GEN;
1290 /* fallthrough */
1291 case SETCERT_ST_GEN:
1292 /*
1293 * This state generates the ckch instances with their
1294 * sni_ctxs and SSL_CTX.
1295 *
1296 * Since the SSL_CTX generation can be CPU consumer, we
1297 * yield every 10 instances.
1298 */
1299
1300 old_ckchs = appctx->ctx.ssl.old_ckchs;
1301 new_ckchs = appctx->ctx.ssl.new_ckchs;
1302
1303 if (!new_ckchs)
1304 continue;
1305
1306 /* get the next ckchi to regenerate */
1307 ckchi = appctx->ctx.ssl.next_ckchi;
1308 /* we didn't start yet, set it to the first elem */
1309 if (ckchi == NULL)
1310 ckchi = LIST_ELEM(old_ckchs->ckch_inst.n, typeof(ckchi), by_ckchs);
1311
1312 /* walk through the old ckch_inst and creates new ckch_inst using the updated ckchs */
1313 list_for_each_entry_from(ckchi, &old_ckchs->ckch_inst, by_ckchs) {
1314 struct ckch_inst *new_inst;
1315 char **sni_filter = NULL;
1316 int fcount = 0;
1317
1318 /* it takes a lot of CPU to creates SSL_CTXs, so we yield every 10 CKCH instances */
1319 if (y >= 10) {
1320 /* save the next ckchi to compute */
1321 appctx->ctx.ssl.next_ckchi = ckchi;
1322 goto yield;
1323 }
1324
1325 if (ckchi->crtlist_entry) {
1326 sni_filter = ckchi->crtlist_entry->filters;
1327 fcount = ckchi->crtlist_entry->fcount;
1328 }
1329
Remi Tricot-Le Bretond817dc72021-01-25 17:19:43 +01001330 if (ckchi->is_server_instance)
William Lallemand795bd9b2021-01-26 11:27:42 +01001331 errcode |= ckch_inst_new_load_srv_store(new_ckchs->path, new_ckchs, &new_inst, &err);
Remi Tricot-Le Bretonf3eedfe2021-01-25 17:19:44 +01001332 else
1333 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 +02001334
1335 if (errcode & ERR_CODE)
1336 goto error;
1337
1338 /* if the previous ckchi was used as the default */
1339 if (ckchi->is_default)
1340 new_inst->is_default = 1;
1341
Remi Tricot-Le Bretonf3eedfe2021-01-25 17:19:44 +01001342 new_inst->is_server_instance = ckchi->is_server_instance;
1343 new_inst->server = ckchi->server;
1344 /* Create a new SSL_CTX and link it to the new instance. */
1345 if (new_inst->is_server_instance) {
Remi Tricot-Le Breton43899ec2021-04-21 15:32:46 +02001346 retval = ssl_sock_prepare_srv_ssl_ctx(ckchi->server, new_inst->ctx);
1347 if (retval)
Remi Tricot-Le Bretonf3eedfe2021-01-25 17:19:44 +01001348 goto error;
Remi Tricot-Le Bretonf3eedfe2021-01-25 17:19:44 +01001349 }
1350
William Lallemanda55685b2020-12-15 14:57:46 +01001351 /* create the link to the crtlist_entry */
1352 new_inst->crtlist_entry = ckchi->crtlist_entry;
1353
William Lallemandda8584c2020-05-14 10:14:37 +02001354 /* we need to initialize the SSL_CTX generated */
1355 /* this iterate on the newly generated SNIs in the new instance to prepare their SSL_CTX */
1356 list_for_each_entry_safe(sc0, sc0s, &new_inst->sni_ctx, by_ckch_inst) {
1357 if (!sc0->order) { /* we initialized only the first SSL_CTX because it's the same in the other sni_ctx's */
1358 errcode |= ssl_sock_prepare_ctx(ckchi->bind_conf, ckchi->ssl_conf, sc0->ctx, &err);
1359 if (errcode & ERR_CODE)
1360 goto error;
1361 }
1362 }
1363
1364
1365 /* display one dot per new instance */
1366 chunk_appendf(trash, ".");
1367 /* link the new ckch_inst to the duplicate */
Willy Tarreau2b718102021-04-21 07:32:39 +02001368 LIST_APPEND(&new_ckchs->ckch_inst, &new_inst->by_ckchs);
William Lallemandda8584c2020-05-14 10:14:37 +02001369 y++;
1370 }
1371 appctx->st2 = SETCERT_ST_INSERT;
1372 /* fallthrough */
1373 case SETCERT_ST_INSERT:
1374 /* The generation is finished, we can insert everything */
1375
1376 old_ckchs = appctx->ctx.ssl.old_ckchs;
1377 new_ckchs = appctx->ctx.ssl.new_ckchs;
1378
1379 if (!new_ckchs)
1380 continue;
1381
1382 /* get the list of crtlist_entry in the old store, and update the pointers to the store */
1383 LIST_SPLICE(&new_ckchs->crtlist_entry, &old_ckchs->crtlist_entry);
1384 list_for_each_entry(entry, &new_ckchs->crtlist_entry, by_ckch_store) {
1385 ebpt_delete(&entry->node);
1386 /* change the ptr and reinsert the node */
1387 entry->node.key = new_ckchs;
1388 ebpt_insert(&entry->crtlist->entries, &entry->node);
1389 }
1390
William Lallemanda55685b2020-12-15 14:57:46 +01001391 /* insert the new ckch_insts in the crtlist_entry */
1392 list_for_each_entry(ckchi, &new_ckchs->ckch_inst, by_ckchs) {
1393 if (ckchi->crtlist_entry)
Willy Tarreau2b718102021-04-21 07:32:39 +02001394 LIST_INSERT(&ckchi->crtlist_entry->ckch_inst, &ckchi->by_crtlist_entry);
William Lallemanda55685b2020-12-15 14:57:46 +01001395 }
1396
William Lallemandda8584c2020-05-14 10:14:37 +02001397 /* First, we insert every new SNIs in the trees, also replace the default_ctx */
1398 list_for_each_entry_safe(ckchi, ckchis, &new_ckchs->ckch_inst, by_ckchs) {
Remi Tricot-Le Bretonf3eedfe2021-01-25 17:19:44 +01001399 /* The bind_conf will be null on server ckch_instances. */
1400 if (ckchi->is_server_instance) {
William Lallemande0de0a62021-02-03 18:51:01 +01001401 int i;
William Lallemand3ce6eed2021-02-08 10:43:44 +01001402 /* a lock is needed here since we have to free the SSL cache */
1403 HA_RWLOCK_WRLOCK(SSL_SERVER_LOCK, &ckchi->server->ssl_ctx.lock);
William Lallemand1dedb0a2021-01-26 10:18:57 +01001404 /* free the server current SSL_CTX */
1405 SSL_CTX_free(ckchi->server->ssl_ctx.ctx);
Remi Tricot-Le Bretonf3eedfe2021-01-25 17:19:44 +01001406 /* Actual ssl context update */
William Lallemand1dedb0a2021-01-26 10:18:57 +01001407 SSL_CTX_up_ref(ckchi->ctx);
Remi Tricot-Le Bretonf3eedfe2021-01-25 17:19:44 +01001408 ckchi->server->ssl_ctx.ctx = ckchi->ctx;
William Lallemand1dedb0a2021-01-26 10:18:57 +01001409 ckchi->server->ssl_ctx.inst = ckchi;
Remi Tricot-Le Bretonf3eedfe2021-01-25 17:19:44 +01001410
William Lallemande0de0a62021-02-03 18:51:01 +01001411 /* flush the session cache of the server */
William Lallemandc78ac5a2021-11-17 02:59:21 +01001412 for (i = 0; i < global.nbthread; i++) {
William Lallemand6cd79952021-11-23 15:15:09 +01001413 ha_free(&ckchi->server->ssl_ctx.reused_sess[i].ptr);
William Lallemandc78ac5a2021-11-17 02:59:21 +01001414 ha_free(&ckchi->server->ssl_ctx.reused_sess[i].sni);
1415 }
Willy Tarreau61cfdf42021-02-20 10:46:51 +01001416
William Lallemand3ce6eed2021-02-08 10:43:44 +01001417 HA_RWLOCK_WRUNLOCK(SSL_SERVER_LOCK, &ckchi->server->ssl_ctx.lock);
William Lallemande0de0a62021-02-03 18:51:01 +01001418
William Lallemand1dedb0a2021-01-26 10:18:57 +01001419 } else {
Remi Tricot-Le Bretonf3eedfe2021-01-25 17:19:44 +01001420 HA_RWLOCK_WRLOCK(SNI_LOCK, &ckchi->bind_conf->sni_lock);
1421 ssl_sock_load_cert_sni(ckchi, ckchi->bind_conf);
1422 HA_RWLOCK_WRUNLOCK(SNI_LOCK, &ckchi->bind_conf->sni_lock);
1423 }
William Lallemandda8584c2020-05-14 10:14:37 +02001424 }
1425
1426 /* delete the old sni_ctx, the old ckch_insts and the ckch_store */
1427 list_for_each_entry_safe(ckchi, ckchis, &old_ckchs->ckch_inst, by_ckchs) {
William Lallemandda8584c2020-05-14 10:14:37 +02001428
William Lallemand1dedb0a2021-01-26 10:18:57 +01001429 if (ckchi->is_server_instance) {
1430 /* no lock for servers */
1431 ckch_inst_free(ckchi);
1432 } else {
1433 struct bind_conf __maybe_unused *bind_conf = ckchi->bind_conf;
1434
1435 HA_RWLOCK_WRLOCK(SNI_LOCK, &bind_conf->sni_lock);
1436 ckch_inst_free(ckchi);
1437 HA_RWLOCK_WRUNLOCK(SNI_LOCK, &bind_conf->sni_lock);
1438 }
William Lallemandda8584c2020-05-14 10:14:37 +02001439 }
1440
1441 /* Replace the old ckchs by the new one */
1442 ckch_store_free(old_ckchs);
1443 ebst_insert(&ckchs_tree, &new_ckchs->node);
1444 appctx->st2 = SETCERT_ST_FIN;
1445 /* fallthrough */
1446 case SETCERT_ST_FIN:
1447 /* we achieved the transaction, we can set everything to NULL */
Willy Tarreau61cfdf42021-02-20 10:46:51 +01001448 ha_free(&ckchs_transaction.path);
William Lallemandda8584c2020-05-14 10:14:37 +02001449 ckchs_transaction.new_ckchs = NULL;
1450 ckchs_transaction.old_ckchs = NULL;
1451 goto end;
1452 }
1453 }
1454end:
1455
1456 chunk_appendf(trash, "\n");
1457 if (errcode & ERR_WARN)
1458 chunk_appendf(trash, "%s", err);
1459 chunk_appendf(trash, "Success!\n");
1460 if (ci_putchk(si_ic(si), trash) == -1)
1461 si_rx_room_blk(si);
1462 free_trash_chunk(trash);
1463 /* success: call the release function and don't come back */
1464 return 1;
1465yield:
1466 /* store the state */
1467 if (ci_putchk(si_ic(si), trash) == -1)
1468 si_rx_room_blk(si);
1469 free_trash_chunk(trash);
1470 si_rx_endp_more(si); /* let's come back later */
1471 return 0; /* should come back */
1472
1473error:
1474 /* spin unlock and free are done in the release function */
1475 if (trash) {
1476 chunk_appendf(trash, "\n%sFailed!\n", err);
1477 if (ci_putchk(si_ic(si), trash) == -1)
1478 si_rx_room_blk(si);
1479 free_trash_chunk(trash);
1480 }
1481 /* error: call the release function and don't come back */
1482 return 1;
1483}
1484
1485/*
1486 * Parsing function of 'commit ssl cert'
1487 */
1488static int cli_parse_commit_cert(char **args, char *payload, struct appctx *appctx, void *private)
1489{
1490 char *err = NULL;
1491
1492 if (!cli_has_level(appctx, ACCESS_LVL_ADMIN))
1493 return 1;
1494
1495 if (!*args[3])
1496 return cli_err(appctx, "'commit ssl cert expects a filename\n");
1497
1498 /* The operations on the CKCH architecture are locked so we can
1499 * manipulate ckch_store and ckch_inst */
1500 if (HA_SPIN_TRYLOCK(CKCH_LOCK, &ckch_lock))
1501 return cli_err(appctx, "Can't commit the certificate!\nOperations on certificates are currently locked!\n");
1502
1503 if (!ckchs_transaction.path) {
1504 memprintf(&err, "No ongoing transaction! !\n");
1505 goto error;
1506 }
1507
1508 if (strcmp(ckchs_transaction.path, args[3]) != 0) {
1509 memprintf(&err, "The ongoing transaction is about '%s' but you are trying to set '%s'\n", ckchs_transaction.path, args[3]);
1510 goto error;
1511 }
1512
William Lallemand5685ccf2020-09-16 16:12:25 +02001513 /* if a certificate is here, a private key must be here too */
1514 if (ckchs_transaction.new_ckchs->ckch->cert && !ckchs_transaction.new_ckchs->ckch->key) {
1515 memprintf(&err, "The transaction must contain at least a certificate and a private key!\n");
1516 goto error;
1517 }
William Lallemanda9419522020-06-24 16:26:41 +02001518
William Lallemand5685ccf2020-09-16 16:12:25 +02001519 if (!X509_check_private_key(ckchs_transaction.new_ckchs->ckch->cert, ckchs_transaction.new_ckchs->ckch->key)) {
1520 memprintf(&err, "inconsistencies between private key and certificate loaded '%s'.\n", ckchs_transaction.path);
1521 goto error;
William Lallemandda8584c2020-05-14 10:14:37 +02001522 }
1523
1524 /* init the appctx structure */
1525 appctx->st2 = SETCERT_ST_INIT;
1526 appctx->ctx.ssl.next_ckchi = NULL;
1527 appctx->ctx.ssl.new_ckchs = ckchs_transaction.new_ckchs;
1528 appctx->ctx.ssl.old_ckchs = ckchs_transaction.old_ckchs;
1529
1530 /* we don't unlock there, it will be unlock after the IO handler, in the release handler */
1531 return 0;
1532
1533error:
1534
1535 HA_SPIN_UNLOCK(CKCH_LOCK, &ckch_lock);
1536 err = memprintf(&err, "%sCan't commit %s!\n", err ? err : "", args[3]);
1537
1538 return cli_dynerr(appctx, err);
1539}
1540
1541
1542
1543
1544/*
1545 * Parsing function of `set ssl cert`, it updates or creates a temporary ckch.
1546 */
1547static int cli_parse_set_cert(char **args, char *payload, struct appctx *appctx, void *private)
1548{
1549 struct ckch_store *new_ckchs = NULL;
1550 struct ckch_store *old_ckchs = NULL;
1551 char *err = NULL;
1552 int i;
William Lallemandda8584c2020-05-14 10:14:37 +02001553 int errcode = 0;
1554 char *end;
1555 int type = CERT_TYPE_PEM;
1556 struct cert_key_and_chain *ckch;
1557 struct buffer *buf;
1558
1559 if (!cli_has_level(appctx, ACCESS_LVL_ADMIN))
1560 return 1;
1561
William Lallemandda8584c2020-05-14 10:14:37 +02001562 if (!*args[3] || !payload)
1563 return cli_err(appctx, "'set ssl cert expects a filename and a certificate as a payload\n");
1564
1565 /* The operations on the CKCH architecture are locked so we can
1566 * manipulate ckch_store and ckch_inst */
1567 if (HA_SPIN_TRYLOCK(CKCH_LOCK, &ckch_lock))
1568 return cli_err(appctx, "Can't update the certificate!\nOperations on certificates are currently locked!\n");
1569
William Lallemand5ba80d62021-05-04 16:17:27 +02001570 if ((buf = alloc_trash_chunk()) == NULL) {
1571 memprintf(&err, "%sCan't allocate memory\n", err ? err : "");
1572 errcode |= ERR_ALERT | ERR_FATAL;
1573 goto end;
1574 }
William Lallemande5ff4ad2020-06-08 09:40:37 +02001575
William Lallemandda8584c2020-05-14 10:14:37 +02001576 if (!chunk_strcpy(buf, args[3])) {
1577 memprintf(&err, "%sCan't allocate memory\n", err ? err : "");
1578 errcode |= ERR_ALERT | ERR_FATAL;
1579 goto end;
1580 }
1581
1582 /* check which type of file we want to update */
1583 for (i = 0; cert_exts[i].type < CERT_TYPE_MAX; i++) {
1584 end = strrchr(buf->area, '.');
Tim Duesterhuse5ff1412021-01-02 22:31:53 +01001585 if (end && *cert_exts[i].ext && (strcmp(end + 1, cert_exts[i].ext) == 0)) {
William Lallemandda8584c2020-05-14 10:14:37 +02001586 *end = '\0';
William Lallemand089c1382020-10-23 17:35:12 +02001587 buf->data = strlen(buf->area);
William Lallemandda8584c2020-05-14 10:14:37 +02001588 type = cert_exts[i].type;
1589 break;
1590 }
1591 }
1592
1593 appctx->ctx.ssl.old_ckchs = NULL;
1594 appctx->ctx.ssl.new_ckchs = NULL;
1595
1596 /* if there is an ongoing transaction */
1597 if (ckchs_transaction.path) {
William Lallemandda8584c2020-05-14 10:14:37 +02001598 /* if there is an ongoing transaction, check if this is the same file */
1599 if (strcmp(ckchs_transaction.path, buf->area) != 0) {
William Lallemand089c1382020-10-23 17:35:12 +02001600 /* we didn't find the transaction, must try more cases below */
1601
1602 /* if the del-ext option is activated we should try to take a look at a ".crt" too. */
1603 if (type != CERT_TYPE_PEM && global_ssl.extra_files_noext) {
1604 if (!chunk_strcat(buf, ".crt")) {
1605 memprintf(&err, "%sCan't allocate memory\n", err ? err : "");
1606 errcode |= ERR_ALERT | ERR_FATAL;
1607 goto end;
1608 }
1609
1610 if (strcmp(ckchs_transaction.path, buf->area) != 0) {
1611 /* remove .crt of the error message */
1612 *(b_orig(buf) + b_data(buf) + strlen(".crt")) = '\0';
1613 b_sub(buf, strlen(".crt"));
1614
1615 memprintf(&err, "The ongoing transaction is about '%s' but you are trying to set '%s'\n", ckchs_transaction.path, buf->area);
1616 errcode |= ERR_ALERT | ERR_FATAL;
1617 goto end;
1618 }
1619 }
William Lallemandda8584c2020-05-14 10:14:37 +02001620 }
1621
1622 appctx->ctx.ssl.old_ckchs = ckchs_transaction.new_ckchs;
1623
1624 } else {
William Lallemandda8584c2020-05-14 10:14:37 +02001625
William Lallemand95fefa12020-09-09 12:01:33 +02001626 /* lookup for the certificate in the tree */
1627 appctx->ctx.ssl.old_ckchs = ckchs_lookup(buf->area);
William Lallemand089c1382020-10-23 17:35:12 +02001628
1629 if (!appctx->ctx.ssl.old_ckchs) {
1630 /* if the del-ext option is activated we should try to take a look at a ".crt" too. */
1631 if (type != CERT_TYPE_PEM && global_ssl.extra_files_noext) {
1632 if (!chunk_strcat(buf, ".crt")) {
1633 memprintf(&err, "%sCan't allocate memory\n", err ? err : "");
1634 errcode |= ERR_ALERT | ERR_FATAL;
1635 goto end;
1636 }
1637 appctx->ctx.ssl.old_ckchs = ckchs_lookup(buf->area);
1638 }
1639 }
William Lallemandda8584c2020-05-14 10:14:37 +02001640 }
1641
1642 if (!appctx->ctx.ssl.old_ckchs) {
1643 memprintf(&err, "%sCan't replace a certificate which is not referenced by the configuration!\n",
1644 err ? err : "");
1645 errcode |= ERR_ALERT | ERR_FATAL;
1646 goto end;
1647 }
1648
1649 if (!appctx->ctx.ssl.path) {
1650 /* this is a new transaction, set the path of the transaction */
1651 appctx->ctx.ssl.path = strdup(appctx->ctx.ssl.old_ckchs->path);
1652 if (!appctx->ctx.ssl.path) {
1653 memprintf(&err, "%sCan't allocate memory\n", err ? err : "");
1654 errcode |= ERR_ALERT | ERR_FATAL;
1655 goto end;
1656 }
1657 }
1658
1659 old_ckchs = appctx->ctx.ssl.old_ckchs;
1660
1661 /* duplicate the ckch store */
1662 new_ckchs = ckchs_dup(old_ckchs);
1663 if (!new_ckchs) {
1664 memprintf(&err, "%sCannot allocate memory!\n",
1665 err ? err : "");
1666 errcode |= ERR_ALERT | ERR_FATAL;
1667 goto end;
1668 }
1669
William Lallemand95fefa12020-09-09 12:01:33 +02001670 ckch = new_ckchs->ckch;
William Lallemandda8584c2020-05-14 10:14:37 +02001671
1672 /* appply the change on the duplicate */
1673 if (cert_exts[type].load(buf->area, payload, ckch, &err) != 0) {
1674 memprintf(&err, "%sCan't load the payload\n", err ? err : "");
1675 errcode |= ERR_ALERT | ERR_FATAL;
1676 goto end;
1677 }
1678
1679 appctx->ctx.ssl.new_ckchs = new_ckchs;
1680
1681 /* we succeed, we can save the ckchs in the transaction */
1682
1683 /* if there wasn't a transaction, update the old ckchs */
1684 if (!ckchs_transaction.old_ckchs) {
1685 ckchs_transaction.old_ckchs = appctx->ctx.ssl.old_ckchs;
1686 ckchs_transaction.path = appctx->ctx.ssl.path;
1687 err = memprintf(&err, "Transaction created for certificate %s!\n", ckchs_transaction.path);
1688 } else {
1689 err = memprintf(&err, "Transaction updated for certificate %s!\n", ckchs_transaction.path);
1690
1691 }
1692
1693 /* free the previous ckchs if there was a transaction */
1694 ckch_store_free(ckchs_transaction.new_ckchs);
1695
1696 ckchs_transaction.new_ckchs = appctx->ctx.ssl.new_ckchs;
1697
1698
1699 /* creates the SNI ctxs later in the IO handler */
1700
1701end:
1702 free_trash_chunk(buf);
1703
1704 if (errcode & ERR_CODE) {
1705
1706 ckch_store_free(appctx->ctx.ssl.new_ckchs);
1707 appctx->ctx.ssl.new_ckchs = NULL;
1708
1709 appctx->ctx.ssl.old_ckchs = NULL;
1710
Willy Tarreau61cfdf42021-02-20 10:46:51 +01001711 ha_free(&appctx->ctx.ssl.path);
William Lallemandda8584c2020-05-14 10:14:37 +02001712
1713 HA_SPIN_UNLOCK(CKCH_LOCK, &ckch_lock);
1714 return cli_dynerr(appctx, memprintf(&err, "%sCan't update %s!\n", err ? err : "", args[3]));
1715 } else {
1716
1717 HA_SPIN_UNLOCK(CKCH_LOCK, &ckch_lock);
1718 return cli_dynmsg(appctx, LOG_NOTICE, err);
1719 }
1720 /* TODO: handle the ERR_WARN which are not handled because of the io_handler */
1721}
1722
1723/* parsing function of 'abort ssl cert' */
1724static int cli_parse_abort_cert(char **args, char *payload, struct appctx *appctx, void *private)
1725{
1726 char *err = NULL;
1727
1728 if (!cli_has_level(appctx, ACCESS_LVL_ADMIN))
1729 return 1;
1730
1731 if (!*args[3])
1732 return cli_err(appctx, "'abort ssl cert' expects a filename\n");
1733
1734 /* The operations on the CKCH architecture are locked so we can
1735 * manipulate ckch_store and ckch_inst */
1736 if (HA_SPIN_TRYLOCK(CKCH_LOCK, &ckch_lock))
1737 return cli_err(appctx, "Can't abort!\nOperations on certificates are currently locked!\n");
1738
1739 if (!ckchs_transaction.path) {
1740 memprintf(&err, "No ongoing transaction!\n");
1741 goto error;
1742 }
1743
1744 if (strcmp(ckchs_transaction.path, args[3]) != 0) {
1745 memprintf(&err, "The ongoing transaction is about '%s' but you are trying to abort a transaction for '%s'\n", ckchs_transaction.path, args[3]);
1746 goto error;
1747 }
1748
1749 /* Only free the ckchs there, because the SNI and instances were not generated yet */
1750 ckch_store_free(ckchs_transaction.new_ckchs);
1751 ckchs_transaction.new_ckchs = NULL;
William Lallemandda8584c2020-05-14 10:14:37 +02001752 ckchs_transaction.old_ckchs = NULL;
Willy Tarreau61cfdf42021-02-20 10:46:51 +01001753 ha_free(&ckchs_transaction.path);
William Lallemandda8584c2020-05-14 10:14:37 +02001754
1755 HA_SPIN_UNLOCK(CKCH_LOCK, &ckch_lock);
1756
1757 err = memprintf(&err, "Transaction aborted for certificate '%s'!\n", args[3]);
1758 return cli_dynmsg(appctx, LOG_NOTICE, err);
1759
1760error:
1761 HA_SPIN_UNLOCK(CKCH_LOCK, &ckch_lock);
1762
1763 return cli_dynerr(appctx, err);
1764}
1765
1766/* parsing function of 'new ssl cert' */
1767static int cli_parse_new_cert(char **args, char *payload, struct appctx *appctx, void *private)
1768{
1769 struct ckch_store *store;
1770 char *err = NULL;
1771 char *path;
1772
1773 if (!cli_has_level(appctx, ACCESS_LVL_ADMIN))
1774 return 1;
1775
1776 if (!*args[3])
1777 return cli_err(appctx, "'new ssl cert' expects a filename\n");
1778
1779 path = args[3];
1780
1781 /* The operations on the CKCH architecture are locked so we can
1782 * manipulate ckch_store and ckch_inst */
1783 if (HA_SPIN_TRYLOCK(CKCH_LOCK, &ckch_lock))
1784 return cli_err(appctx, "Can't create a certificate!\nOperations on certificates are currently locked!\n");
1785
1786 store = ckchs_lookup(path);
1787 if (store != NULL) {
1788 memprintf(&err, "Certificate '%s' already exists!\n", path);
1789 store = NULL; /* we don't want to free it */
1790 goto error;
1791 }
1792 /* we won't support multi-certificate bundle here */
William Lallemandbd8e6ed2020-09-16 16:08:08 +02001793 store = ckch_store_new(path);
William Lallemandda8584c2020-05-14 10:14:37 +02001794 if (!store) {
1795 memprintf(&err, "unable to allocate memory.\n");
1796 goto error;
1797 }
1798
1799 /* insert into the ckchs tree */
1800 ebst_insert(&ckchs_tree, &store->node);
1801 memprintf(&err, "New empty certificate store '%s'!\n", args[3]);
1802
1803 HA_SPIN_UNLOCK(CKCH_LOCK, &ckch_lock);
1804 return cli_dynmsg(appctx, LOG_NOTICE, err);
1805error:
1806 free(store);
1807 HA_SPIN_UNLOCK(CKCH_LOCK, &ckch_lock);
1808 return cli_dynerr(appctx, err);
1809}
1810
1811/* parsing function of 'del ssl cert' */
1812static int cli_parse_del_cert(char **args, char *payload, struct appctx *appctx, void *private)
1813{
1814 struct ckch_store *store;
1815 char *err = NULL;
1816 char *filename;
1817
1818 if (!cli_has_level(appctx, ACCESS_LVL_ADMIN))
1819 return 1;
1820
1821 if (!*args[3])
1822 return cli_err(appctx, "'del ssl cert' expects a certificate name\n");
1823
1824 if (HA_SPIN_TRYLOCK(CKCH_LOCK, &ckch_lock))
1825 return cli_err(appctx, "Can't delete the certificate!\nOperations on certificates are currently locked!\n");
1826
1827 filename = args[3];
1828
1829 store = ckchs_lookup(filename);
1830 if (store == NULL) {
1831 memprintf(&err, "certificate '%s' doesn't exist!\n", filename);
1832 goto error;
1833 }
1834 if (!LIST_ISEMPTY(&store->ckch_inst)) {
1835 memprintf(&err, "certificate '%s' in use, can't be deleted!\n", filename);
1836 goto error;
1837 }
1838
1839 ebmb_delete(&store->node);
1840 ckch_store_free(store);
1841
1842 memprintf(&err, "Certificate '%s' deleted!\n", filename);
1843
1844 HA_SPIN_UNLOCK(CKCH_LOCK, &ckch_lock);
1845 return cli_dynmsg(appctx, LOG_NOTICE, err);
1846
1847error:
1848 memprintf(&err, "Can't remove the certificate: %s\n", err ? err : "");
1849 HA_SPIN_UNLOCK(CKCH_LOCK, &ckch_lock);
1850 return cli_dynerr(appctx, err);
1851}
1852
William Lallemandee8530c2020-06-23 18:19:42 +02001853void ckch_deinit()
1854{
1855 struct eb_node *node, *next;
1856 struct ckch_store *store;
1857
1858 node = eb_first(&ckchs_tree);
1859 while (node) {
1860 next = eb_next(node);
1861 store = ebmb_entry(node, struct ckch_store, node);
1862 ckch_store_free(store);
1863 node = next;
1864 }
1865}
William Lallemandda8584c2020-05-14 10:14:37 +02001866
1867/* register cli keywords */
1868static struct cli_kw_list cli_kws = {{ },{
Willy Tarreaub205bfd2021-05-07 11:38:37 +02001869 { { "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 },
1870 { { "set", "ssl", "cert", NULL }, "set ssl cert <certfile> <payload> : replace a certificate file", cli_parse_set_cert, NULL, NULL },
1871 { { "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 },
1872 { { "abort", "ssl", "cert", NULL }, "abort ssl cert <certfile> : abort a transaction for a certificate file", cli_parse_abort_cert, NULL, NULL },
1873 { { "del", "ssl", "cert", NULL }, "del ssl cert <certfile> : delete an unused certificate file", cli_parse_del_cert, NULL, NULL },
1874 { { "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 +02001875 { { NULL }, NULL, NULL, NULL }
1876}};
1877
1878INITCALL1(STG_REGISTER, cli_register_kw, &cli_kws);
1879