blob: be7bd2971b1ee40014c5d1ac8a165fdfb8f72ba3 [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) {
104 tmp.area = buf;
105 tmp.data = strlen(buf);
106 tmp.size = tmp.data + 1;
107 src = &tmp;
108 } else {
109 fd = open(sctl_path, O_RDONLY);
110 if (fd == -1)
111 goto end;
112
113 trash.data = 0;
114 while (trash.data < trash.size) {
115 r = read(fd, trash.area + trash.data, trash.size - trash.data);
116 if (r < 0) {
117 if (errno == EINTR)
118 continue;
119 goto end;
120 }
121 else if (r == 0) {
122 break;
123 }
124 trash.data += r;
125 }
126 src = &trash;
127 }
128
129 ret = ssl_sock_parse_sctl(src);
130 if (ret)
131 goto end;
132
133 sctl = calloc(1, sizeof(*sctl));
134 if (!chunk_dup(sctl, src)) {
135 free(sctl);
136 sctl = NULL;
137 goto end;
138 }
139 /* no error, fill ckch with new context, old context must be free */
140 if (ckch->sctl) {
141 free(ckch->sctl->area);
142 ckch->sctl->area = NULL;
143 free(ckch->sctl);
144 }
145 ckch->sctl = sctl;
146 ret = 0;
147end:
148 if (fd != -1)
149 close(fd);
150
151 return ret;
152}
153
154#if ((defined SSL_CTRL_SET_TLSEXT_STATUS_REQ_CB && !defined OPENSSL_NO_OCSP) || defined OPENSSL_IS_BORINGSSL)
155/*
Ilya Shipitsin46a030c2020-07-05 16:36:08 +0500156 * This function load the OCSP Response in DER format contained in file at
William Lallemand03c331c2020-05-13 10:10:01 +0200157 * path 'ocsp_path' or base64 in a buffer <buf>
158 *
159 * Returns 0 on success, 1 in error case.
160 */
161int ssl_sock_load_ocsp_response_from_file(const char *ocsp_path, char *buf, struct cert_key_and_chain *ckch, char **err)
162{
163 int fd = -1;
164 int r = 0;
165 int ret = 1;
166 struct buffer *ocsp_response;
167 struct buffer *src = NULL;
168
169 if (buf) {
170 int i, j;
171 /* if it's from a buffer it will be base64 */
172
173 /* remove \r and \n from the payload */
174 for (i = 0, j = 0; buf[i]; i++) {
175 if (buf[i] == '\r' || buf[i] == '\n')
176 continue;
177 buf[j++] = buf[i];
178 }
179 buf[j] = 0;
180
181 ret = base64dec(buf, j, trash.area, trash.size);
182 if (ret < 0) {
183 memprintf(err, "Error reading OCSP response in base64 format");
184 goto end;
185 }
186 trash.data = ret;
187 src = &trash;
188 } else {
189 fd = open(ocsp_path, O_RDONLY);
190 if (fd == -1) {
191 memprintf(err, "Error opening OCSP response file");
192 goto end;
193 }
194
195 trash.data = 0;
196 while (trash.data < trash.size) {
197 r = read(fd, trash.area + trash.data, trash.size - trash.data);
198 if (r < 0) {
199 if (errno == EINTR)
200 continue;
201
202 memprintf(err, "Error reading OCSP response from file");
203 goto end;
204 }
205 else if (r == 0) {
206 break;
207 }
208 trash.data += r;
209 }
210 close(fd);
211 fd = -1;
212 src = &trash;
213 }
214
215 ocsp_response = calloc(1, sizeof(*ocsp_response));
216 if (!chunk_dup(ocsp_response, src)) {
217 free(ocsp_response);
218 ocsp_response = NULL;
219 goto end;
220 }
221 /* no error, fill ckch with new context, old context must be free */
222 if (ckch->ocsp_response) {
223 free(ckch->ocsp_response->area);
224 ckch->ocsp_response->area = NULL;
225 free(ckch->ocsp_response);
226 }
227 ckch->ocsp_response = ocsp_response;
228 ret = 0;
229end:
230 if (fd != -1)
231 close(fd);
232
233 return ret;
234}
235#endif
236
237/*
238 * Try to load in a ckch every files related to a ckch.
239 * (PEM, sctl, ocsp, issuer etc.)
240 *
241 * This function is only used to load files during the configuration parsing,
242 * it is not used with the CLI.
243 *
244 * This allows us to carry the contents of the file without having to read the
245 * file multiple times. The caller must call
246 * ssl_sock_free_cert_key_and_chain_contents.
247 *
248 * returns:
249 * 0 on Success
250 * 1 on SSL Failure
251 */
252int ssl_sock_load_files_into_ckch(const char *path, struct cert_key_and_chain *ckch, char **err)
253{
William Lallemand8e8581e2020-10-20 17:36:46 +0200254 struct buffer *fp = NULL;
William Lallemand03c331c2020-05-13 10:10:01 +0200255 int ret = 1;
256
257 /* try to load the PEM */
258 if (ssl_sock_load_pem_into_ckch(path, NULL, ckch , err) != 0) {
259 goto end;
260 }
261
William Lallemand8e8581e2020-10-20 17:36:46 +0200262 fp = alloc_trash_chunk();
263 if (!fp) {
264 memprintf(err, "%sCan't allocate memory\n", err && *err ? *err : "");
265 goto end;
266 }
267
268 if (!chunk_strcpy(fp, path) || (b_data(fp) > MAXPATHLEN)) {
269 memprintf(err, "%s '%s' filename too long'.\n",
270 err && *err ? *err : "", fp->area);
271 ret = 1;
272 goto end;
273 }
274
275 /* remove the extension */
276 if (global_ssl.extra_files_noext) {
277 char *ext;
278
279 /* look for the extension */
280 if ((ext = strrchr(fp->area, '.'))) {
281 int n;
282 int found_ext = 0; /* bundle extension found ? */
283
284 ext++; /* we need to compare the ext after the dot */
285
286 for (n = 0; n < SSL_SOCK_NUM_KEYTYPES; n++) {
287 if (!strcmp(ext, SSL_SOCK_KEYTYPE_NAMES[n])) {
288 found_ext = 1;
289 }
290 }
291
292 ext--;
293 if (!found_ext) /* if it wasn't a bundle extension we remove it */
294 *ext = '\0';
295
296 fp->data = strlen(fp->area);
297 }
298
299 }
300
William Lallemand03c331c2020-05-13 10:10:01 +0200301 /* try to load an external private key if it wasn't in the PEM */
302 if ((ckch->key == NULL) && (global_ssl.extra_files & SSL_GF_KEY)) {
William Lallemand03c331c2020-05-13 10:10:01 +0200303 struct stat st;
304
William Lallemand8e8581e2020-10-20 17:36:46 +0200305
306 if (!chunk_strcat(fp, ".key") || (b_data(fp) > MAXPATHLEN)) {
307 memprintf(err, "%s '%s' filename too long'.\n",
308 err && *err ? *err : "", fp->area);
309 ret = 1;
310 goto end;
311 }
312
313 if (stat(fp->area, &st) == 0) {
314 if (ssl_sock_load_key_into_ckch(fp->area, NULL, ckch, err)) {
William Lallemand03c331c2020-05-13 10:10:01 +0200315 memprintf(err, "%s '%s' is present but cannot be read or parsed'.\n",
William Lallemand8e8581e2020-10-20 17:36:46 +0200316 err && *err ? *err : "", fp->area);
William Lallemand03c331c2020-05-13 10:10:01 +0200317 goto end;
318 }
319 }
William Lallemand03c331c2020-05-13 10:10:01 +0200320
William Lallemand8e8581e2020-10-20 17:36:46 +0200321 if (ckch->key == NULL) {
322 memprintf(err, "%sNo Private Key found in '%s'.\n", err && *err ? *err : "", fp->area);
323 goto end;
324 }
325 /* remove the added extension */
326 *(fp->area + fp->data - strlen(".key")) = '\0';
327 b_sub(fp, strlen(".key"));
William Lallemand03c331c2020-05-13 10:10:01 +0200328 }
329
330 if (!X509_check_private_key(ckch->cert, ckch->key)) {
331 memprintf(err, "%sinconsistencies between private key and certificate loaded '%s'.\n",
332 err && *err ? *err : "", path);
333 goto end;
334 }
335
336#if (HA_OPENSSL_VERSION_NUMBER >= 0x1000200fL && !defined OPENSSL_NO_TLSEXT && !defined OPENSSL_IS_BORINGSSL)
337 /* try to load the sctl file */
338 if (global_ssl.extra_files & SSL_GF_SCTL) {
William Lallemand03c331c2020-05-13 10:10:01 +0200339 struct stat st;
340
William Lallemand8e8581e2020-10-20 17:36:46 +0200341 if (!chunk_strcat(fp, ".sctl") || b_data(fp) > MAXPATHLEN) {
342 memprintf(err, "%s '%s' filename too long'.\n",
343 err && *err ? *err : "", fp->area);
344 ret = 1;
345 goto end;
346 }
347
348 if (stat(fp->area, &st) == 0) {
349 if (ssl_sock_load_sctl_from_file(fp->area, NULL, ckch, err)) {
William Lallemand03c331c2020-05-13 10:10:01 +0200350 memprintf(err, "%s '%s.sctl' is present but cannot be read or parsed'.\n",
William Lallemand8e8581e2020-10-20 17:36:46 +0200351 err && *err ? *err : "", fp->area);
William Lallemand03c331c2020-05-13 10:10:01 +0200352 ret = 1;
353 goto end;
354 }
355 }
William Lallemand8e8581e2020-10-20 17:36:46 +0200356 /* remove the added extension */
357 *(fp->area + fp->data - strlen(".sctl")) = '\0';
358 b_sub(fp, strlen(".sctl"));
William Lallemand03c331c2020-05-13 10:10:01 +0200359 }
360#endif
361
362 /* try to load an ocsp response file */
363 if (global_ssl.extra_files & SSL_GF_OCSP) {
William Lallemand03c331c2020-05-13 10:10:01 +0200364 struct stat st;
365
William Lallemand8e8581e2020-10-20 17:36:46 +0200366 if (!chunk_strcat(fp, ".ocsp") || b_data(fp) > MAXPATHLEN) {
367 memprintf(err, "%s '%s' filename too long'.\n",
368 err && *err ? *err : "", fp->area);
369 ret = 1;
370 goto end;
371 }
372
373 if (stat(fp->area, &st) == 0) {
374 if (ssl_sock_load_ocsp_response_from_file(fp->area, NULL, ckch, err)) {
William Lallemand03c331c2020-05-13 10:10:01 +0200375 ret = 1;
376 goto end;
377 }
378 }
William Lallemand8e8581e2020-10-20 17:36:46 +0200379 /* remove the added extension */
380 *(fp->area + fp->data - strlen(".ocsp")) = '\0';
381 b_sub(fp, strlen(".ocsp"));
William Lallemand03c331c2020-05-13 10:10:01 +0200382 }
383
384#ifndef OPENSSL_IS_BORINGSSL /* Useless for BoringSSL */
385 if (ckch->ocsp_response && (global_ssl.extra_files & SSL_GF_OCSP_ISSUER)) {
386 /* if no issuer was found, try to load an issuer from the .issuer */
387 if (!ckch->ocsp_issuer) {
388 struct stat st;
William Lallemand8e8581e2020-10-20 17:36:46 +0200389
390 if (!chunk_strcat(fp, ".issuer") || b_data(fp) > MAXPATHLEN) {
391 memprintf(err, "%s '%s' filename too long'.\n",
392 err && *err ? *err : "", fp->area);
393 ret = 1;
394 goto end;
395 }
William Lallemand03c331c2020-05-13 10:10:01 +0200396
William Lallemand8e8581e2020-10-20 17:36:46 +0200397 if (stat(fp->area, &st) == 0) {
398 if (ssl_sock_load_issuer_file_into_ckch(fp->area, NULL, ckch, err)) {
William Lallemand03c331c2020-05-13 10:10:01 +0200399 ret = 1;
400 goto end;
401 }
402
403 if (X509_check_issued(ckch->ocsp_issuer, ckch->cert) != X509_V_OK) {
404 memprintf(err, "%s '%s' is not an issuer'.\n",
William Lallemand8e8581e2020-10-20 17:36:46 +0200405 err && *err ? *err : "", fp->area);
William Lallemand03c331c2020-05-13 10:10:01 +0200406 ret = 1;
407 goto end;
408 }
409 }
William Lallemand8e8581e2020-10-20 17:36:46 +0200410 /* remove the added extension */
411 *(fp->area + fp->data - strlen(".issuer")) = '\0';
412 b_sub(fp, strlen(".issuer"));
William Lallemand03c331c2020-05-13 10:10:01 +0200413 }
414 }
415#endif
416
417 ret = 0;
418
419end:
420
421 ERR_clear_error();
422
423 /* Something went wrong in one of the reads */
424 if (ret != 0)
425 ssl_sock_free_cert_key_and_chain_contents(ckch);
426
William Lallemand8e8581e2020-10-20 17:36:46 +0200427 free_trash_chunk(fp);
428
William Lallemand03c331c2020-05-13 10:10:01 +0200429 return ret;
430}
431
432/*
433 * Try to load a private key file from a <path> or a buffer <buf>
434 *
435 * If it failed you should not attempt to use the ckch but free it.
436 *
437 * Return 0 on success or != 0 on failure
438 */
439int ssl_sock_load_key_into_ckch(const char *path, char *buf, struct cert_key_and_chain *ckch , char **err)
440{
441 BIO *in = NULL;
442 int ret = 1;
443 EVP_PKEY *key = NULL;
444
445 if (buf) {
446 /* reading from a buffer */
447 in = BIO_new_mem_buf(buf, -1);
448 if (in == NULL) {
449 memprintf(err, "%sCan't allocate memory\n", err && *err ? *err : "");
450 goto end;
451 }
452
453 } else {
454 /* reading from a file */
455 in = BIO_new(BIO_s_file());
456 if (in == NULL)
457 goto end;
458
459 if (BIO_read_filename(in, path) <= 0)
460 goto end;
461 }
462
463 /* Read Private Key */
464 key = PEM_read_bio_PrivateKey(in, NULL, NULL, NULL);
465 if (key == NULL) {
466 memprintf(err, "%sunable to load private key from file '%s'.\n",
467 err && *err ? *err : "", path);
468 goto end;
469 }
470
471 ret = 0;
472
473 SWAP(ckch->key, key);
474
475end:
476
477 ERR_clear_error();
478 if (in)
479 BIO_free(in);
480 if (key)
481 EVP_PKEY_free(key);
482
483 return ret;
484}
485
486/*
487 * Try to load a PEM file from a <path> or a buffer <buf>
488 * The PEM must contain at least a Certificate,
489 * It could contain a DH, a certificate chain and a PrivateKey.
490 *
491 * If it failed you should not attempt to use the ckch but free it.
492 *
493 * Return 0 on success or != 0 on failure
494 */
495int ssl_sock_load_pem_into_ckch(const char *path, char *buf, struct cert_key_and_chain *ckch , char **err)
496{
497 BIO *in = NULL;
498 int ret = 1;
499 X509 *ca;
500 X509 *cert = NULL;
501 EVP_PKEY *key = NULL;
502 DH *dh = NULL;
503 STACK_OF(X509) *chain = NULL;
504
505 if (buf) {
506 /* reading from a buffer */
507 in = BIO_new_mem_buf(buf, -1);
508 if (in == NULL) {
509 memprintf(err, "%sCan't allocate memory\n", err && *err ? *err : "");
510 goto end;
511 }
512
513 } else {
514 /* reading from a file */
515 in = BIO_new(BIO_s_file());
516 if (in == NULL) {
517 memprintf(err, "%sCan't allocate memory\n", err && *err ? *err : "");
518 goto end;
519 }
520
521 if (BIO_read_filename(in, path) <= 0) {
522 memprintf(err, "%scannot open the file '%s'.\n",
523 err && *err ? *err : "", path);
524 goto end;
525 }
526 }
527
528 /* Read Private Key */
529 key = PEM_read_bio_PrivateKey(in, NULL, NULL, NULL);
530 /* no need to check for errors here, because the private key could be loaded later */
531
532#ifndef OPENSSL_NO_DH
533 /* Seek back to beginning of file */
534 if (BIO_reset(in) == -1) {
535 memprintf(err, "%san error occurred while reading the file '%s'.\n",
536 err && *err ? *err : "", path);
537 goto end;
538 }
539
540 dh = PEM_read_bio_DHparams(in, NULL, NULL, NULL);
541 /* no need to return an error there, dh is not mandatory */
542#endif
543
544 /* Seek back to beginning of file */
545 if (BIO_reset(in) == -1) {
546 memprintf(err, "%san error occurred while reading the file '%s'.\n",
547 err && *err ? *err : "", path);
548 goto end;
549 }
550
551 /* Read Certificate */
552 cert = PEM_read_bio_X509_AUX(in, NULL, NULL, NULL);
553 if (cert == NULL) {
554 memprintf(err, "%sunable to load certificate from file '%s'.\n",
555 err && *err ? *err : "", path);
556 goto end;
557 }
558
559 /* Look for a Certificate Chain */
560 while ((ca = PEM_read_bio_X509(in, NULL, NULL, NULL))) {
561 if (chain == NULL)
562 chain = sk_X509_new_null();
563 if (!sk_X509_push(chain, ca)) {
564 X509_free(ca);
565 goto end;
566 }
567 }
568
569 ret = ERR_get_error();
570 if (ret && (ERR_GET_LIB(ret) != ERR_LIB_PEM && ERR_GET_REASON(ret) != PEM_R_NO_START_LINE)) {
571 memprintf(err, "%sunable to load certificate chain from file '%s'.\n",
572 err && *err ? *err : "", path);
573 goto end;
574 }
575
576 /* once it loaded the PEM, it should remove everything else in the ckch */
577 if (ckch->ocsp_response) {
578 free(ckch->ocsp_response->area);
579 ckch->ocsp_response->area = NULL;
580 free(ckch->ocsp_response);
581 ckch->ocsp_response = NULL;
582 }
583
584 if (ckch->sctl) {
585 free(ckch->sctl->area);
586 ckch->sctl->area = NULL;
587 free(ckch->sctl);
588 ckch->sctl = NULL;
589 }
590
591 if (ckch->ocsp_issuer) {
592 X509_free(ckch->ocsp_issuer);
593 ckch->ocsp_issuer = NULL;
594 }
595
596 /* no error, fill ckch with new context, old context will be free at end: */
597 SWAP(ckch->key, key);
598 SWAP(ckch->dh, dh);
599 SWAP(ckch->cert, cert);
600 SWAP(ckch->chain, chain);
601
602 ret = 0;
603
604end:
605
606 ERR_clear_error();
607 if (in)
608 BIO_free(in);
609 if (key)
610 EVP_PKEY_free(key);
611 if (dh)
612 DH_free(dh);
613 if (cert)
614 X509_free(cert);
615 if (chain)
616 sk_X509_pop_free(chain, X509_free);
617
618 return ret;
619}
620
621/* Frees the contents of a cert_key_and_chain
622 */
623void ssl_sock_free_cert_key_and_chain_contents(struct cert_key_and_chain *ckch)
624{
625 if (!ckch)
626 return;
627
628 /* Free the certificate and set pointer to NULL */
629 if (ckch->cert)
630 X509_free(ckch->cert);
631 ckch->cert = NULL;
632
633 /* Free the key and set pointer to NULL */
634 if (ckch->key)
635 EVP_PKEY_free(ckch->key);
636 ckch->key = NULL;
637
638 /* Free each certificate in the chain */
639 if (ckch->chain)
640 sk_X509_pop_free(ckch->chain, X509_free);
641 ckch->chain = NULL;
642
643 if (ckch->dh)
644 DH_free(ckch->dh);
645 ckch->dh = NULL;
646
647 if (ckch->sctl) {
648 free(ckch->sctl->area);
649 ckch->sctl->area = NULL;
650 free(ckch->sctl);
651 ckch->sctl = NULL;
652 }
653
654 if (ckch->ocsp_response) {
655 free(ckch->ocsp_response->area);
656 ckch->ocsp_response->area = NULL;
657 free(ckch->ocsp_response);
658 ckch->ocsp_response = NULL;
659 }
660
661 if (ckch->ocsp_issuer)
662 X509_free(ckch->ocsp_issuer);
663 ckch->ocsp_issuer = NULL;
664}
665
666/*
667 *
668 * This function copy a cert_key_and_chain in memory
669 *
670 * It's used to try to apply changes on a ckch before committing them, because
671 * most of the time it's not possible to revert those changes
672 *
673 * Return a the dst or NULL
674 */
675struct cert_key_and_chain *ssl_sock_copy_cert_key_and_chain(struct cert_key_and_chain *src,
676 struct cert_key_and_chain *dst)
677{
678 if (src->cert) {
679 dst->cert = src->cert;
680 X509_up_ref(src->cert);
681 }
682
683 if (src->key) {
684 dst->key = src->key;
685 EVP_PKEY_up_ref(src->key);
686 }
687
688 if (src->chain) {
689 dst->chain = X509_chain_up_ref(src->chain);
690 }
691
692 if (src->dh) {
693 DH_up_ref(src->dh);
694 dst->dh = src->dh;
695 }
696
697 if (src->sctl) {
698 struct buffer *sctl;
699
700 sctl = calloc(1, sizeof(*sctl));
701 if (!chunk_dup(sctl, src->sctl)) {
702 free(sctl);
703 sctl = NULL;
704 goto error;
705 }
706 dst->sctl = sctl;
707 }
708
709 if (src->ocsp_response) {
710 struct buffer *ocsp_response;
711
712 ocsp_response = calloc(1, sizeof(*ocsp_response));
713 if (!chunk_dup(ocsp_response, src->ocsp_response)) {
714 free(ocsp_response);
715 ocsp_response = NULL;
716 goto error;
717 }
718 dst->ocsp_response = ocsp_response;
719 }
720
721 if (src->ocsp_issuer) {
722 X509_up_ref(src->ocsp_issuer);
723 dst->ocsp_issuer = src->ocsp_issuer;
724 }
725
726 return dst;
727
728error:
729
730 /* free everything */
731 ssl_sock_free_cert_key_and_chain_contents(dst);
732
733 return NULL;
734}
735
736/*
737 * return 0 on success or != 0 on failure
738 */
739int ssl_sock_load_issuer_file_into_ckch(const char *path, char *buf, struct cert_key_and_chain *ckch, char **err)
740{
741 int ret = 1;
742 BIO *in = NULL;
743 X509 *issuer;
744
745 if (buf) {
746 /* reading from a buffer */
747 in = BIO_new_mem_buf(buf, -1);
748 if (in == NULL) {
749 memprintf(err, "%sCan't allocate memory\n", err && *err ? *err : "");
750 goto end;
751 }
752
753 } else {
754 /* reading from a file */
755 in = BIO_new(BIO_s_file());
756 if (in == NULL)
757 goto end;
758
759 if (BIO_read_filename(in, path) <= 0)
760 goto end;
761 }
762
763 issuer = PEM_read_bio_X509_AUX(in, NULL, NULL, NULL);
764 if (!issuer) {
765 memprintf(err, "%s'%s' cannot be read or parsed'.\n",
766 err && *err ? *err : "", path);
767 goto end;
768 }
769 /* no error, fill ckch with new context, old context must be free */
770 if (ckch->ocsp_issuer)
771 X509_free(ckch->ocsp_issuer);
772 ckch->ocsp_issuer = issuer;
773 ret = 0;
774
775end:
776
777 ERR_clear_error();
778 if (in)
779 BIO_free(in);
780
781 return ret;
782}
783
784/******************** ckch_store functions ***********************************
785 * The ckch_store is a structure used to cache and index the SSL files used in
786 * configuration
787 */
788
789/*
790 * Free a ckch_store, its ckch, its instances and remove it from the ebtree
791 */
792void ckch_store_free(struct ckch_store *store)
793{
794 struct ckch_inst *inst, *inst_s;
795
796 if (!store)
797 return;
798
William Lallemandbd8e6ed2020-09-16 16:08:08 +0200799 ssl_sock_free_cert_key_and_chain_contents(store->ckch);
William Lallemand03c331c2020-05-13 10:10:01 +0200800
801 free(store->ckch);
802 store->ckch = NULL;
803
804 list_for_each_entry_safe(inst, inst_s, &store->ckch_inst, by_ckchs) {
805 ckch_inst_free(inst);
806 }
807 ebmb_delete(&store->node);
808 free(store);
809}
810
811/*
812 * create and initialize a ckch_store
813 * <path> is the key name
814 * <nmemb> is the number of store->ckch objects to allocate
815 *
816 * Return a ckch_store or NULL upon failure.
817 */
William Lallemandbd8e6ed2020-09-16 16:08:08 +0200818struct ckch_store *ckch_store_new(const char *filename)
William Lallemand03c331c2020-05-13 10:10:01 +0200819{
820 struct ckch_store *store;
821 int pathlen;
822
823 pathlen = strlen(filename);
824 store = calloc(1, sizeof(*store) + pathlen + 1);
825 if (!store)
826 return NULL;
827
William Lallemand03c331c2020-05-13 10:10:01 +0200828 memcpy(store->path, filename, pathlen + 1);
829
830 LIST_INIT(&store->ckch_inst);
831 LIST_INIT(&store->crtlist_entry);
832
William Lallemandbd8e6ed2020-09-16 16:08:08 +0200833 store->ckch = calloc(1, sizeof(*store->ckch));
William Lallemand03c331c2020-05-13 10:10:01 +0200834 if (!store->ckch)
835 goto error;
836
837 return store;
838error:
839 ckch_store_free(store);
840 return NULL;
841}
842
843/* allocate and duplicate a ckch_store
844 * Return a new ckch_store or NULL */
845struct ckch_store *ckchs_dup(const struct ckch_store *src)
846{
847 struct ckch_store *dst;
848
William Lallemandbd8e6ed2020-09-16 16:08:08 +0200849 dst = ckch_store_new(src->path);
William Lallemand03c331c2020-05-13 10:10:01 +0200850
William Lallemandbd8e6ed2020-09-16 16:08:08 +0200851 if (!ssl_sock_copy_cert_key_and_chain(src->ckch, dst->ckch))
852 goto error;
William Lallemand03c331c2020-05-13 10:10:01 +0200853
854 return dst;
855
856error:
857 ckch_store_free(dst);
858
859 return NULL;
860}
861
862/*
863 * lookup a path into the ckchs tree.
864 */
865struct ckch_store *ckchs_lookup(char *path)
866{
867 struct ebmb_node *eb;
868
869 eb = ebst_lookup(&ckchs_tree, path);
870 if (!eb)
871 return NULL;
872
873 return ebmb_entry(eb, struct ckch_store, node);
874}
875
876/*
877 * This function allocate a ckch_store and populate it with certificates from files.
878 */
William Lallemandbd8e6ed2020-09-16 16:08:08 +0200879struct ckch_store *ckchs_load_cert_file(char *path, char **err)
William Lallemand03c331c2020-05-13 10:10:01 +0200880{
881 struct ckch_store *ckchs;
882
William Lallemandbd8e6ed2020-09-16 16:08:08 +0200883 ckchs = ckch_store_new(path);
William Lallemand03c331c2020-05-13 10:10:01 +0200884 if (!ckchs) {
885 memprintf(err, "%sunable to allocate memory.\n", err && *err ? *err : "");
886 goto end;
887 }
William Lallemand03c331c2020-05-13 10:10:01 +0200888
William Lallemandbd8e6ed2020-09-16 16:08:08 +0200889 if (ssl_sock_load_files_into_ckch(path, ckchs->ckch, err) == 1)
890 goto end;
William Lallemand03c331c2020-05-13 10:10:01 +0200891
William Lallemandbd8e6ed2020-09-16 16:08:08 +0200892 /* insert into the ckchs tree */
893 memcpy(ckchs->path, path, strlen(path) + 1);
894 ebst_insert(&ckchs_tree, &ckchs->node);
William Lallemand03c331c2020-05-13 10:10:01 +0200895 return ckchs;
896
897end:
898 ckch_store_free(ckchs);
899
900 return NULL;
901}
902
William Lallemandfa1d8b42020-05-13 15:46:10 +0200903
904/******************** ckch_inst functions ******************************/
905
906/* unlink a ckch_inst, free all SNIs, free the ckch_inst */
907/* The caller must use the lock of the bind_conf if used with inserted SNIs */
908void ckch_inst_free(struct ckch_inst *inst)
909{
910 struct sni_ctx *sni, *sni_s;
911
912 if (inst == NULL)
913 return;
914
915 list_for_each_entry_safe(sni, sni_s, &inst->sni_ctx, by_ckch_inst) {
916 SSL_CTX_free(sni->ctx);
917 LIST_DEL(&sni->by_ckch_inst);
918 ebmb_delete(&sni->name);
919 free(sni);
920 }
921 LIST_DEL(&inst->by_ckchs);
922 LIST_DEL(&inst->by_crtlist_entry);
923 free(inst);
924}
925
926/* Alloc and init a ckch_inst */
927struct ckch_inst *ckch_inst_new()
928{
929 struct ckch_inst *ckch_inst;
930
931 ckch_inst = calloc(1, sizeof *ckch_inst);
932 if (!ckch_inst)
933 return NULL;
934
935 LIST_INIT(&ckch_inst->sni_ctx);
936 LIST_INIT(&ckch_inst->by_ckchs);
937 LIST_INIT(&ckch_inst->by_crtlist_entry);
938
939 return ckch_inst;
940}
941
William Lallemandda8584c2020-05-14 10:14:37 +0200942/*************************** CLI commands ***********************/
943
944/* Type of SSL payloads that can be updated over the CLI */
945
946enum {
947 CERT_TYPE_PEM = 0,
948 CERT_TYPE_KEY,
949#if ((defined SSL_CTRL_SET_TLSEXT_STATUS_REQ_CB && !defined OPENSSL_NO_OCSP) || defined OPENSSL_IS_BORINGSSL)
950 CERT_TYPE_OCSP,
951#endif
952 CERT_TYPE_ISSUER,
953#if (HA_OPENSSL_VERSION_NUMBER >= 0x1000200fL && !defined OPENSSL_NO_TLSEXT && !defined OPENSSL_IS_BORINGSSL)
954 CERT_TYPE_SCTL,
955#endif
956 CERT_TYPE_MAX,
957};
958
959struct {
960 const char *ext;
961 int type;
962 int (*load)(const char *path, char *payload, struct cert_key_and_chain *ckch, char **err);
963 /* add a parsing callback */
964} cert_exts[CERT_TYPE_MAX+1] = {
965 [CERT_TYPE_PEM] = { "", CERT_TYPE_PEM, &ssl_sock_load_pem_into_ckch }, /* default mode, no extensions */
966 [CERT_TYPE_KEY] = { "key", CERT_TYPE_KEY, &ssl_sock_load_key_into_ckch },
967#if ((defined SSL_CTRL_SET_TLSEXT_STATUS_REQ_CB && !defined OPENSSL_NO_OCSP) || defined OPENSSL_IS_BORINGSSL)
968 [CERT_TYPE_OCSP] = { "ocsp", CERT_TYPE_OCSP, &ssl_sock_load_ocsp_response_from_file },
969#endif
970#if (HA_OPENSSL_VERSION_NUMBER >= 0x1000200fL && !defined OPENSSL_NO_TLSEXT && !defined OPENSSL_IS_BORINGSSL)
971 [CERT_TYPE_SCTL] = { "sctl", CERT_TYPE_SCTL, &ssl_sock_load_sctl_from_file },
972#endif
973 [CERT_TYPE_ISSUER] = { "issuer", CERT_TYPE_ISSUER, &ssl_sock_load_issuer_file_into_ckch },
974 [CERT_TYPE_MAX] = { NULL, CERT_TYPE_MAX, NULL },
975};
976
977
978/* release function of the `show ssl cert' command */
979static void cli_release_show_cert(struct appctx *appctx)
980{
981 HA_SPIN_UNLOCK(CKCH_LOCK, &ckch_lock);
982}
983
984/* IO handler of "show ssl cert <filename>" */
985static int cli_io_handler_show_cert(struct appctx *appctx)
986{
987 struct buffer *trash = alloc_trash_chunk();
988 struct ebmb_node *node;
989 struct stream_interface *si = appctx->owner;
990 struct ckch_store *ckchs;
991
992 if (trash == NULL)
993 return 1;
994
995 if (!appctx->ctx.ssl.old_ckchs) {
996 if (ckchs_transaction.old_ckchs) {
997 ckchs = ckchs_transaction.old_ckchs;
998 chunk_appendf(trash, "# transaction\n");
William Lallemand5685ccf2020-09-16 16:12:25 +0200999 chunk_appendf(trash, "*%s\n", ckchs->path);
William Lallemandda8584c2020-05-14 10:14:37 +02001000 }
1001 }
1002
1003 if (!appctx->ctx.cli.p0) {
1004 chunk_appendf(trash, "# filename\n");
1005 node = ebmb_first(&ckchs_tree);
1006 } else {
1007 node = &((struct ckch_store *)appctx->ctx.cli.p0)->node;
1008 }
1009 while (node) {
1010 ckchs = ebmb_entry(node, struct ckch_store, node);
William Lallemand5685ccf2020-09-16 16:12:25 +02001011 chunk_appendf(trash, "%s\n", ckchs->path);
William Lallemandda8584c2020-05-14 10:14:37 +02001012
1013 node = ebmb_next(node);
1014 if (ci_putchk(si_ic(si), trash) == -1) {
1015 si_rx_room_blk(si);
1016 goto yield;
1017 }
1018 }
1019
1020 appctx->ctx.cli.p0 = NULL;
1021 free_trash_chunk(trash);
1022 return 1;
1023yield:
1024
1025 free_trash_chunk(trash);
1026 appctx->ctx.cli.p0 = ckchs;
1027 return 0; /* should come back */
1028}
1029
1030/*
1031 * Extract and format the DNS SAN extensions and copy result into a chuink
1032 * Return 0;
1033 */
1034#ifdef SSL_CTRL_SET_TLSEXT_HOSTNAME
1035static int ssl_sock_get_san_oneline(X509 *cert, struct buffer *out)
1036{
1037 int i;
1038 char *str;
1039 STACK_OF(GENERAL_NAME) *names = NULL;
1040
1041 names = X509_get_ext_d2i(cert, NID_subject_alt_name, NULL, NULL);
1042 if (names) {
1043 for (i = 0; i < sk_GENERAL_NAME_num(names); i++) {
1044 GENERAL_NAME *name = sk_GENERAL_NAME_value(names, i);
1045 if (i > 0)
1046 chunk_appendf(out, ", ");
1047 if (name->type == GEN_DNS) {
1048 if (ASN1_STRING_to_UTF8((unsigned char **)&str, name->d.dNSName) >= 0) {
1049 chunk_appendf(out, "DNS:%s", str);
1050 OPENSSL_free(str);
1051 }
1052 }
1053 }
1054 sk_GENERAL_NAME_pop_free(names, GENERAL_NAME_free);
1055 }
1056 return 0;
1057}
1058#endif
1059
1060
1061
1062
1063/* IO handler of the details "show ssl cert <filename>" */
1064static int cli_io_handler_show_cert_detail(struct appctx *appctx)
1065{
1066 struct stream_interface *si = appctx->owner;
1067 struct ckch_store *ckchs = appctx->ctx.cli.p0;
1068 struct buffer *out = alloc_trash_chunk();
1069 struct buffer *tmp = alloc_trash_chunk();
1070 X509_NAME *name = NULL;
1071 STACK_OF(X509) *chain;
1072 unsigned int len = 0;
1073 int write = -1;
1074 BIO *bio = NULL;
1075 int i;
1076
1077 if (!tmp || !out)
1078 goto end_no_putchk;
1079
William Lallemand5685ccf2020-09-16 16:12:25 +02001080 chunk_appendf(out, "Filename: ");
1081 if (ckchs == ckchs_transaction.new_ckchs)
1082 chunk_appendf(out, "*");
1083 chunk_appendf(out, "%s\n", ckchs->path);
William Lallemandda8584c2020-05-14 10:14:37 +02001084
William Lallemand5685ccf2020-09-16 16:12:25 +02001085 chunk_appendf(out, "Status: ");
1086 if (ckchs->ckch->cert == NULL)
1087 chunk_appendf(out, "Empty\n");
1088 else if (LIST_ISEMPTY(&ckchs->ckch_inst))
1089 chunk_appendf(out, "Unused\n");
1090 else
1091 chunk_appendf(out, "Used\n");
William Lallemandda8584c2020-05-14 10:14:37 +02001092
William Lallemand5685ccf2020-09-16 16:12:25 +02001093 if (ckchs->ckch->cert == NULL)
1094 goto end;
William Lallemandda8584c2020-05-14 10:14:37 +02001095
William Lallemand5685ccf2020-09-16 16:12:25 +02001096 chain = ckchs->ckch->chain;
1097 if (chain == NULL) {
1098 struct issuer_chain *issuer;
1099 issuer = ssl_get0_issuer_chain(ckchs->ckch->cert);
1100 if (issuer) {
1101 chain = issuer->chain;
1102 chunk_appendf(out, "Chain Filename: ");
1103 chunk_appendf(out, "%s\n", issuer->path);
William Lallemandda8584c2020-05-14 10:14:37 +02001104 }
William Lallemand5685ccf2020-09-16 16:12:25 +02001105 }
1106 chunk_appendf(out, "Serial: ");
1107 if (ssl_sock_get_serial(ckchs->ckch->cert, tmp) == -1)
1108 goto end;
1109 dump_binary(out, tmp->area, tmp->data);
1110 chunk_appendf(out, "\n");
William Lallemandda8584c2020-05-14 10:14:37 +02001111
William Lallemand5685ccf2020-09-16 16:12:25 +02001112 chunk_appendf(out, "notBefore: ");
1113 chunk_reset(tmp);
1114 if ((bio = BIO_new(BIO_s_mem())) == NULL)
1115 goto end;
1116 if (ASN1_TIME_print(bio, X509_getm_notBefore(ckchs->ckch->cert)) == 0)
1117 goto end;
1118 write = BIO_read(bio, tmp->area, tmp->size-1);
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
William Lallemand5685ccf2020-09-16 16:12:25 +02001124 chunk_appendf(out, "notAfter: ");
1125 chunk_reset(tmp);
1126 if ((bio = BIO_new(BIO_s_mem())) == NULL)
1127 goto end;
1128 if (ASN1_TIME_print(bio, X509_getm_notAfter(ckchs->ckch->cert)) == 0)
1129 goto end;
1130 if ((write = BIO_read(bio, tmp->area, tmp->size-1)) <= 0)
1131 goto end;
1132 tmp->area[write] = '\0';
1133 BIO_free(bio);
1134 bio = NULL;
1135 chunk_appendf(out, "%s\n", tmp->area);
William Lallemandda8584c2020-05-14 10:14:37 +02001136
1137#ifdef SSL_CTRL_SET_TLSEXT_HOSTNAME
William Lallemand5685ccf2020-09-16 16:12:25 +02001138 chunk_appendf(out, "Subject Alternative Name: ");
1139 if (ssl_sock_get_san_oneline(ckchs->ckch->cert, out) == -1)
1140 goto end;
1141 *(out->area + out->data) = '\0';
1142 chunk_appendf(out, "\n");
William Lallemandda8584c2020-05-14 10:14:37 +02001143#endif
William Lallemand5685ccf2020-09-16 16:12:25 +02001144 chunk_reset(tmp);
1145 chunk_appendf(out, "Algorithm: ");
1146 if (cert_get_pkey_algo(ckchs->ckch->cert, tmp) == 0)
1147 goto end;
1148 chunk_appendf(out, "%s\n", tmp->area);
William Lallemandda8584c2020-05-14 10:14:37 +02001149
William Lallemand5685ccf2020-09-16 16:12:25 +02001150 chunk_reset(tmp);
1151 chunk_appendf(out, "SHA1 FingerPrint: ");
1152 if (X509_digest(ckchs->ckch->cert, EVP_sha1(), (unsigned char *) tmp->area, &len) == 0)
1153 goto end;
1154 tmp->data = len;
1155 dump_binary(out, tmp->area, tmp->data);
1156 chunk_appendf(out, "\n");
William Lallemandda8584c2020-05-14 10:14:37 +02001157
William Lallemand5685ccf2020-09-16 16:12:25 +02001158 chunk_appendf(out, "Subject: ");
1159 if ((name = X509_get_subject_name(ckchs->ckch->cert)) == NULL)
1160 goto end;
1161 if ((ssl_sock_get_dn_oneline(name, tmp)) == -1)
1162 goto end;
1163 *(tmp->area + tmp->data) = '\0';
1164 chunk_appendf(out, "%s\n", tmp->area);
1165
1166 chunk_appendf(out, "Issuer: ");
1167 if ((name = X509_get_issuer_name(ckchs->ckch->cert)) == NULL)
1168 goto end;
1169 if ((ssl_sock_get_dn_oneline(name, tmp)) == -1)
1170 goto end;
1171 *(tmp->area + tmp->data) = '\0';
1172 chunk_appendf(out, "%s\n", tmp->area);
1173
1174 /* Displays subject of each certificate in the chain */
1175 for (i = 0; i < sk_X509_num(chain); i++) {
1176 X509 *ca = sk_X509_value(chain, i);
1177
1178 chunk_appendf(out, "Chain Subject: ");
1179 if ((name = X509_get_subject_name(ca)) == NULL)
William Lallemandda8584c2020-05-14 10:14:37 +02001180 goto end;
1181 if ((ssl_sock_get_dn_oneline(name, tmp)) == -1)
1182 goto end;
1183 *(tmp->area + tmp->data) = '\0';
1184 chunk_appendf(out, "%s\n", tmp->area);
1185
William Lallemand5685ccf2020-09-16 16:12:25 +02001186 chunk_appendf(out, "Chain Issuer: ");
1187 if ((name = X509_get_issuer_name(ca)) == NULL)
William Lallemandda8584c2020-05-14 10:14:37 +02001188 goto end;
1189 if ((ssl_sock_get_dn_oneline(name, tmp)) == -1)
1190 goto end;
1191 *(tmp->area + tmp->data) = '\0';
1192 chunk_appendf(out, "%s\n", tmp->area);
William Lallemandda8584c2020-05-14 10:14:37 +02001193 }
1194
1195end:
1196 if (ci_putchk(si_ic(si), out) == -1) {
1197 si_rx_room_blk(si);
1198 goto yield;
1199 }
1200
1201end_no_putchk:
1202 if (bio)
1203 BIO_free(bio);
1204 free_trash_chunk(tmp);
1205 free_trash_chunk(out);
1206 return 1;
1207yield:
1208 free_trash_chunk(tmp);
1209 free_trash_chunk(out);
1210 return 0; /* should come back */
1211}
1212
1213/* parsing function for 'show ssl cert [certfile]' */
1214static int cli_parse_show_cert(char **args, char *payload, struct appctx *appctx, void *private)
1215{
1216 struct ckch_store *ckchs;
1217
1218 if (!cli_has_level(appctx, ACCESS_LVL_OPER))
1219 return cli_err(appctx, "Can't allocate memory!\n");
1220
1221 /* The operations on the CKCH architecture are locked so we can
1222 * manipulate ckch_store and ckch_inst */
1223 if (HA_SPIN_TRYLOCK(CKCH_LOCK, &ckch_lock))
1224 return cli_err(appctx, "Can't show!\nOperations on certificates are currently locked!\n");
1225
1226 /* check if there is a certificate to lookup */
1227 if (*args[3]) {
1228 if (*args[3] == '*') {
1229 if (!ckchs_transaction.new_ckchs)
1230 goto error;
1231
1232 ckchs = ckchs_transaction.new_ckchs;
1233
1234 if (strcmp(args[3] + 1, ckchs->path))
1235 goto error;
1236
1237 } else {
1238 if ((ckchs = ckchs_lookup(args[3])) == NULL)
1239 goto error;
1240
1241 }
1242
William Lallemandda8584c2020-05-14 10:14:37 +02001243 appctx->ctx.cli.p0 = ckchs;
1244 /* use the IO handler that shows details */
1245 appctx->io_handler = cli_io_handler_show_cert_detail;
1246 }
1247
1248 return 0;
1249
1250error:
1251 HA_SPIN_UNLOCK(CKCH_LOCK, &ckch_lock);
1252 return cli_err(appctx, "Can't display the certificate: Not found or the certificate is a bundle!\n");
1253}
1254
1255/* release function of the `set ssl cert' command, free things and unlock the spinlock */
1256static void cli_release_commit_cert(struct appctx *appctx)
1257{
1258 struct ckch_store *new_ckchs;
1259
1260 HA_SPIN_UNLOCK(CKCH_LOCK, &ckch_lock);
1261
1262 if (appctx->st2 != SETCERT_ST_FIN) {
1263 /* free every new sni_ctx and the new store, which are not in the trees so no spinlock there */
1264 new_ckchs = appctx->ctx.ssl.new_ckchs;
1265
1266 /* if the allocation failed, we need to free everything from the temporary list */
1267 ckch_store_free(new_ckchs);
1268 }
1269}
1270
1271/*
1272 * This function tries to create the new ckch_inst and their SNIs
1273 */
1274static int cli_io_handler_commit_cert(struct appctx *appctx)
1275{
1276 struct stream_interface *si = appctx->owner;
1277 int y = 0;
1278 char *err = NULL;
1279 int errcode = 0;
1280 struct ckch_store *old_ckchs, *new_ckchs = NULL;
1281 struct ckch_inst *ckchi, *ckchis;
1282 struct buffer *trash = alloc_trash_chunk();
1283 struct sni_ctx *sc0, *sc0s;
1284 struct crtlist_entry *entry;
1285
1286 if (trash == NULL)
1287 goto error;
1288
1289 if (unlikely(si_ic(si)->flags & (CF_WRITE_ERROR|CF_SHUTW)))
1290 goto error;
1291
1292 while (1) {
1293 switch (appctx->st2) {
1294 case SETCERT_ST_INIT:
1295 /* This state just print the update message */
1296 chunk_printf(trash, "Committing %s", ckchs_transaction.path);
1297 if (ci_putchk(si_ic(si), trash) == -1) {
1298 si_rx_room_blk(si);
1299 goto yield;
1300 }
1301 appctx->st2 = SETCERT_ST_GEN;
1302 /* fallthrough */
1303 case SETCERT_ST_GEN:
1304 /*
1305 * This state generates the ckch instances with their
1306 * sni_ctxs and SSL_CTX.
1307 *
1308 * Since the SSL_CTX generation can be CPU consumer, we
1309 * yield every 10 instances.
1310 */
1311
1312 old_ckchs = appctx->ctx.ssl.old_ckchs;
1313 new_ckchs = appctx->ctx.ssl.new_ckchs;
1314
1315 if (!new_ckchs)
1316 continue;
1317
1318 /* get the next ckchi to regenerate */
1319 ckchi = appctx->ctx.ssl.next_ckchi;
1320 /* we didn't start yet, set it to the first elem */
1321 if (ckchi == NULL)
1322 ckchi = LIST_ELEM(old_ckchs->ckch_inst.n, typeof(ckchi), by_ckchs);
1323
1324 /* walk through the old ckch_inst and creates new ckch_inst using the updated ckchs */
1325 list_for_each_entry_from(ckchi, &old_ckchs->ckch_inst, by_ckchs) {
1326 struct ckch_inst *new_inst;
1327 char **sni_filter = NULL;
1328 int fcount = 0;
1329
1330 /* it takes a lot of CPU to creates SSL_CTXs, so we yield every 10 CKCH instances */
1331 if (y >= 10) {
1332 /* save the next ckchi to compute */
1333 appctx->ctx.ssl.next_ckchi = ckchi;
1334 goto yield;
1335 }
1336
1337 if (ckchi->crtlist_entry) {
1338 sni_filter = ckchi->crtlist_entry->filters;
1339 fcount = ckchi->crtlist_entry->fcount;
1340 }
1341
William Lallemand95fefa12020-09-09 12:01:33 +02001342 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 +02001343
1344 if (errcode & ERR_CODE)
1345 goto error;
1346
1347 /* if the previous ckchi was used as the default */
1348 if (ckchi->is_default)
1349 new_inst->is_default = 1;
1350
1351 /* we need to initialize the SSL_CTX generated */
1352 /* this iterate on the newly generated SNIs in the new instance to prepare their SSL_CTX */
1353 list_for_each_entry_safe(sc0, sc0s, &new_inst->sni_ctx, by_ckch_inst) {
1354 if (!sc0->order) { /* we initialized only the first SSL_CTX because it's the same in the other sni_ctx's */
1355 errcode |= ssl_sock_prepare_ctx(ckchi->bind_conf, ckchi->ssl_conf, sc0->ctx, &err);
1356 if (errcode & ERR_CODE)
1357 goto error;
1358 }
1359 }
1360
1361
1362 /* display one dot per new instance */
1363 chunk_appendf(trash, ".");
1364 /* link the new ckch_inst to the duplicate */
1365 LIST_ADDQ(&new_ckchs->ckch_inst, &new_inst->by_ckchs);
1366 y++;
1367 }
1368 appctx->st2 = SETCERT_ST_INSERT;
1369 /* fallthrough */
1370 case SETCERT_ST_INSERT:
1371 /* The generation is finished, we can insert everything */
1372
1373 old_ckchs = appctx->ctx.ssl.old_ckchs;
1374 new_ckchs = appctx->ctx.ssl.new_ckchs;
1375
1376 if (!new_ckchs)
1377 continue;
1378
1379 /* get the list of crtlist_entry in the old store, and update the pointers to the store */
1380 LIST_SPLICE(&new_ckchs->crtlist_entry, &old_ckchs->crtlist_entry);
1381 list_for_each_entry(entry, &new_ckchs->crtlist_entry, by_ckch_store) {
1382 ebpt_delete(&entry->node);
1383 /* change the ptr and reinsert the node */
1384 entry->node.key = new_ckchs;
1385 ebpt_insert(&entry->crtlist->entries, &entry->node);
1386 }
1387
1388 /* First, we insert every new SNIs in the trees, also replace the default_ctx */
1389 list_for_each_entry_safe(ckchi, ckchis, &new_ckchs->ckch_inst, by_ckchs) {
1390 HA_RWLOCK_WRLOCK(SNI_LOCK, &ckchi->bind_conf->sni_lock);
1391 ssl_sock_load_cert_sni(ckchi, ckchi->bind_conf);
1392 HA_RWLOCK_WRUNLOCK(SNI_LOCK, &ckchi->bind_conf->sni_lock);
1393 }
1394
1395 /* delete the old sni_ctx, the old ckch_insts and the ckch_store */
1396 list_for_each_entry_safe(ckchi, ckchis, &old_ckchs->ckch_inst, by_ckchs) {
1397 struct bind_conf __maybe_unused *bind_conf = ckchi->bind_conf;
1398
1399 HA_RWLOCK_WRLOCK(SNI_LOCK, &bind_conf->sni_lock);
1400 ckch_inst_free(ckchi);
1401 HA_RWLOCK_WRUNLOCK(SNI_LOCK, &bind_conf->sni_lock);
1402 }
1403
1404 /* Replace the old ckchs by the new one */
1405 ckch_store_free(old_ckchs);
1406 ebst_insert(&ckchs_tree, &new_ckchs->node);
1407 appctx->st2 = SETCERT_ST_FIN;
1408 /* fallthrough */
1409 case SETCERT_ST_FIN:
1410 /* we achieved the transaction, we can set everything to NULL */
1411 free(ckchs_transaction.path);
1412 ckchs_transaction.path = NULL;
1413 ckchs_transaction.new_ckchs = NULL;
1414 ckchs_transaction.old_ckchs = NULL;
1415 goto end;
1416 }
1417 }
1418end:
1419
1420 chunk_appendf(trash, "\n");
1421 if (errcode & ERR_WARN)
1422 chunk_appendf(trash, "%s", err);
1423 chunk_appendf(trash, "Success!\n");
1424 if (ci_putchk(si_ic(si), trash) == -1)
1425 si_rx_room_blk(si);
1426 free_trash_chunk(trash);
1427 /* success: call the release function and don't come back */
1428 return 1;
1429yield:
1430 /* store the state */
1431 if (ci_putchk(si_ic(si), trash) == -1)
1432 si_rx_room_blk(si);
1433 free_trash_chunk(trash);
1434 si_rx_endp_more(si); /* let's come back later */
1435 return 0; /* should come back */
1436
1437error:
1438 /* spin unlock and free are done in the release function */
1439 if (trash) {
1440 chunk_appendf(trash, "\n%sFailed!\n", err);
1441 if (ci_putchk(si_ic(si), trash) == -1)
1442 si_rx_room_blk(si);
1443 free_trash_chunk(trash);
1444 }
1445 /* error: call the release function and don't come back */
1446 return 1;
1447}
1448
1449/*
1450 * Parsing function of 'commit ssl cert'
1451 */
1452static int cli_parse_commit_cert(char **args, char *payload, struct appctx *appctx, void *private)
1453{
1454 char *err = NULL;
1455
1456 if (!cli_has_level(appctx, ACCESS_LVL_ADMIN))
1457 return 1;
1458
1459 if (!*args[3])
1460 return cli_err(appctx, "'commit ssl cert expects a filename\n");
1461
1462 /* The operations on the CKCH architecture are locked so we can
1463 * manipulate ckch_store and ckch_inst */
1464 if (HA_SPIN_TRYLOCK(CKCH_LOCK, &ckch_lock))
1465 return cli_err(appctx, "Can't commit the certificate!\nOperations on certificates are currently locked!\n");
1466
1467 if (!ckchs_transaction.path) {
1468 memprintf(&err, "No ongoing transaction! !\n");
1469 goto error;
1470 }
1471
1472 if (strcmp(ckchs_transaction.path, args[3]) != 0) {
1473 memprintf(&err, "The ongoing transaction is about '%s' but you are trying to set '%s'\n", ckchs_transaction.path, args[3]);
1474 goto error;
1475 }
1476
William Lallemand5685ccf2020-09-16 16:12:25 +02001477 /* if a certificate is here, a private key must be here too */
1478 if (ckchs_transaction.new_ckchs->ckch->cert && !ckchs_transaction.new_ckchs->ckch->key) {
1479 memprintf(&err, "The transaction must contain at least a certificate and a private key!\n");
1480 goto error;
1481 }
William Lallemanda9419522020-06-24 16:26:41 +02001482
William Lallemand5685ccf2020-09-16 16:12:25 +02001483 if (!X509_check_private_key(ckchs_transaction.new_ckchs->ckch->cert, ckchs_transaction.new_ckchs->ckch->key)) {
1484 memprintf(&err, "inconsistencies between private key and certificate loaded '%s'.\n", ckchs_transaction.path);
1485 goto error;
William Lallemandda8584c2020-05-14 10:14:37 +02001486 }
1487
1488 /* init the appctx structure */
1489 appctx->st2 = SETCERT_ST_INIT;
1490 appctx->ctx.ssl.next_ckchi = NULL;
1491 appctx->ctx.ssl.new_ckchs = ckchs_transaction.new_ckchs;
1492 appctx->ctx.ssl.old_ckchs = ckchs_transaction.old_ckchs;
1493
1494 /* we don't unlock there, it will be unlock after the IO handler, in the release handler */
1495 return 0;
1496
1497error:
1498
1499 HA_SPIN_UNLOCK(CKCH_LOCK, &ckch_lock);
1500 err = memprintf(&err, "%sCan't commit %s!\n", err ? err : "", args[3]);
1501
1502 return cli_dynerr(appctx, err);
1503}
1504
1505
1506
1507
1508/*
1509 * Parsing function of `set ssl cert`, it updates or creates a temporary ckch.
1510 */
1511static int cli_parse_set_cert(char **args, char *payload, struct appctx *appctx, void *private)
1512{
1513 struct ckch_store *new_ckchs = NULL;
1514 struct ckch_store *old_ckchs = NULL;
1515 char *err = NULL;
1516 int i;
William Lallemandda8584c2020-05-14 10:14:37 +02001517 int errcode = 0;
1518 char *end;
1519 int type = CERT_TYPE_PEM;
1520 struct cert_key_and_chain *ckch;
1521 struct buffer *buf;
1522
1523 if (!cli_has_level(appctx, ACCESS_LVL_ADMIN))
1524 return 1;
1525
William Lallemandda8584c2020-05-14 10:14:37 +02001526 if (!*args[3] || !payload)
1527 return cli_err(appctx, "'set ssl cert expects a filename and a certificate as a payload\n");
1528
1529 /* The operations on the CKCH architecture are locked so we can
1530 * manipulate ckch_store and ckch_inst */
1531 if (HA_SPIN_TRYLOCK(CKCH_LOCK, &ckch_lock))
1532 return cli_err(appctx, "Can't update the certificate!\nOperations on certificates are currently locked!\n");
1533
William Lallemande5ff4ad2020-06-08 09:40:37 +02001534 if ((buf = alloc_trash_chunk()) == NULL)
1535 return cli_err(appctx, "Can't allocate memory\n");
1536
William Lallemandda8584c2020-05-14 10:14:37 +02001537 if (!chunk_strcpy(buf, args[3])) {
1538 memprintf(&err, "%sCan't allocate memory\n", err ? err : "");
1539 errcode |= ERR_ALERT | ERR_FATAL;
1540 goto end;
1541 }
1542
1543 /* check which type of file we want to update */
1544 for (i = 0; cert_exts[i].type < CERT_TYPE_MAX; i++) {
1545 end = strrchr(buf->area, '.');
1546 if (end && *cert_exts[i].ext && (!strcmp(end + 1, cert_exts[i].ext))) {
1547 *end = '\0';
1548 type = cert_exts[i].type;
1549 break;
1550 }
1551 }
1552
1553 appctx->ctx.ssl.old_ckchs = NULL;
1554 appctx->ctx.ssl.new_ckchs = NULL;
1555
1556 /* if there is an ongoing transaction */
1557 if (ckchs_transaction.path) {
William Lallemandda8584c2020-05-14 10:14:37 +02001558 /* if there is an ongoing transaction, check if this is the same file */
1559 if (strcmp(ckchs_transaction.path, buf->area) != 0) {
1560 memprintf(&err, "The ongoing transaction is about '%s' but you are trying to set '%s'\n", ckchs_transaction.path, buf->area);
1561 errcode |= ERR_ALERT | ERR_FATAL;
1562 goto end;
1563 }
1564
1565 appctx->ctx.ssl.old_ckchs = ckchs_transaction.new_ckchs;
1566
1567 } else {
William Lallemandda8584c2020-05-14 10:14:37 +02001568
William Lallemand95fefa12020-09-09 12:01:33 +02001569 /* lookup for the certificate in the tree */
1570 appctx->ctx.ssl.old_ckchs = ckchs_lookup(buf->area);
William Lallemandda8584c2020-05-14 10:14:37 +02001571 }
1572
1573 if (!appctx->ctx.ssl.old_ckchs) {
1574 memprintf(&err, "%sCan't replace a certificate which is not referenced by the configuration!\n",
1575 err ? err : "");
1576 errcode |= ERR_ALERT | ERR_FATAL;
1577 goto end;
1578 }
1579
1580 if (!appctx->ctx.ssl.path) {
1581 /* this is a new transaction, set the path of the transaction */
1582 appctx->ctx.ssl.path = strdup(appctx->ctx.ssl.old_ckchs->path);
1583 if (!appctx->ctx.ssl.path) {
1584 memprintf(&err, "%sCan't allocate memory\n", err ? err : "");
1585 errcode |= ERR_ALERT | ERR_FATAL;
1586 goto end;
1587 }
1588 }
1589
1590 old_ckchs = appctx->ctx.ssl.old_ckchs;
1591
1592 /* duplicate the ckch store */
1593 new_ckchs = ckchs_dup(old_ckchs);
1594 if (!new_ckchs) {
1595 memprintf(&err, "%sCannot allocate memory!\n",
1596 err ? err : "");
1597 errcode |= ERR_ALERT | ERR_FATAL;
1598 goto end;
1599 }
1600
William Lallemand95fefa12020-09-09 12:01:33 +02001601 ckch = new_ckchs->ckch;
William Lallemandda8584c2020-05-14 10:14:37 +02001602
1603 /* appply the change on the duplicate */
1604 if (cert_exts[type].load(buf->area, payload, ckch, &err) != 0) {
1605 memprintf(&err, "%sCan't load the payload\n", err ? err : "");
1606 errcode |= ERR_ALERT | ERR_FATAL;
1607 goto end;
1608 }
1609
1610 appctx->ctx.ssl.new_ckchs = new_ckchs;
1611
1612 /* we succeed, we can save the ckchs in the transaction */
1613
1614 /* if there wasn't a transaction, update the old ckchs */
1615 if (!ckchs_transaction.old_ckchs) {
1616 ckchs_transaction.old_ckchs = appctx->ctx.ssl.old_ckchs;
1617 ckchs_transaction.path = appctx->ctx.ssl.path;
1618 err = memprintf(&err, "Transaction created for certificate %s!\n", ckchs_transaction.path);
1619 } else {
1620 err = memprintf(&err, "Transaction updated for certificate %s!\n", ckchs_transaction.path);
1621
1622 }
1623
1624 /* free the previous ckchs if there was a transaction */
1625 ckch_store_free(ckchs_transaction.new_ckchs);
1626
1627 ckchs_transaction.new_ckchs = appctx->ctx.ssl.new_ckchs;
1628
1629
1630 /* creates the SNI ctxs later in the IO handler */
1631
1632end:
1633 free_trash_chunk(buf);
1634
1635 if (errcode & ERR_CODE) {
1636
1637 ckch_store_free(appctx->ctx.ssl.new_ckchs);
1638 appctx->ctx.ssl.new_ckchs = NULL;
1639
1640 appctx->ctx.ssl.old_ckchs = NULL;
1641
1642 free(appctx->ctx.ssl.path);
1643 appctx->ctx.ssl.path = NULL;
1644
1645 HA_SPIN_UNLOCK(CKCH_LOCK, &ckch_lock);
1646 return cli_dynerr(appctx, memprintf(&err, "%sCan't update %s!\n", err ? err : "", args[3]));
1647 } else {
1648
1649 HA_SPIN_UNLOCK(CKCH_LOCK, &ckch_lock);
1650 return cli_dynmsg(appctx, LOG_NOTICE, err);
1651 }
1652 /* TODO: handle the ERR_WARN which are not handled because of the io_handler */
1653}
1654
1655/* parsing function of 'abort ssl cert' */
1656static int cli_parse_abort_cert(char **args, char *payload, struct appctx *appctx, void *private)
1657{
1658 char *err = NULL;
1659
1660 if (!cli_has_level(appctx, ACCESS_LVL_ADMIN))
1661 return 1;
1662
1663 if (!*args[3])
1664 return cli_err(appctx, "'abort ssl cert' expects a filename\n");
1665
1666 /* The operations on the CKCH architecture are locked so we can
1667 * manipulate ckch_store and ckch_inst */
1668 if (HA_SPIN_TRYLOCK(CKCH_LOCK, &ckch_lock))
1669 return cli_err(appctx, "Can't abort!\nOperations on certificates are currently locked!\n");
1670
1671 if (!ckchs_transaction.path) {
1672 memprintf(&err, "No ongoing transaction!\n");
1673 goto error;
1674 }
1675
1676 if (strcmp(ckchs_transaction.path, args[3]) != 0) {
1677 memprintf(&err, "The ongoing transaction is about '%s' but you are trying to abort a transaction for '%s'\n", ckchs_transaction.path, args[3]);
1678 goto error;
1679 }
1680
1681 /* Only free the ckchs there, because the SNI and instances were not generated yet */
1682 ckch_store_free(ckchs_transaction.new_ckchs);
1683 ckchs_transaction.new_ckchs = NULL;
1684 ckch_store_free(ckchs_transaction.old_ckchs);
1685 ckchs_transaction.old_ckchs = NULL;
1686 free(ckchs_transaction.path);
1687 ckchs_transaction.path = NULL;
1688
1689 HA_SPIN_UNLOCK(CKCH_LOCK, &ckch_lock);
1690
1691 err = memprintf(&err, "Transaction aborted for certificate '%s'!\n", args[3]);
1692 return cli_dynmsg(appctx, LOG_NOTICE, err);
1693
1694error:
1695 HA_SPIN_UNLOCK(CKCH_LOCK, &ckch_lock);
1696
1697 return cli_dynerr(appctx, err);
1698}
1699
1700/* parsing function of 'new ssl cert' */
1701static int cli_parse_new_cert(char **args, char *payload, struct appctx *appctx, void *private)
1702{
1703 struct ckch_store *store;
1704 char *err = NULL;
1705 char *path;
1706
1707 if (!cli_has_level(appctx, ACCESS_LVL_ADMIN))
1708 return 1;
1709
1710 if (!*args[3])
1711 return cli_err(appctx, "'new ssl cert' expects a filename\n");
1712
1713 path = args[3];
1714
1715 /* The operations on the CKCH architecture are locked so we can
1716 * manipulate ckch_store and ckch_inst */
1717 if (HA_SPIN_TRYLOCK(CKCH_LOCK, &ckch_lock))
1718 return cli_err(appctx, "Can't create a certificate!\nOperations on certificates are currently locked!\n");
1719
1720 store = ckchs_lookup(path);
1721 if (store != NULL) {
1722 memprintf(&err, "Certificate '%s' already exists!\n", path);
1723 store = NULL; /* we don't want to free it */
1724 goto error;
1725 }
1726 /* we won't support multi-certificate bundle here */
William Lallemandbd8e6ed2020-09-16 16:08:08 +02001727 store = ckch_store_new(path);
William Lallemandda8584c2020-05-14 10:14:37 +02001728 if (!store) {
1729 memprintf(&err, "unable to allocate memory.\n");
1730 goto error;
1731 }
1732
1733 /* insert into the ckchs tree */
1734 ebst_insert(&ckchs_tree, &store->node);
1735 memprintf(&err, "New empty certificate store '%s'!\n", args[3]);
1736
1737 HA_SPIN_UNLOCK(CKCH_LOCK, &ckch_lock);
1738 return cli_dynmsg(appctx, LOG_NOTICE, err);
1739error:
1740 free(store);
1741 HA_SPIN_UNLOCK(CKCH_LOCK, &ckch_lock);
1742 return cli_dynerr(appctx, err);
1743}
1744
1745/* parsing function of 'del ssl cert' */
1746static int cli_parse_del_cert(char **args, char *payload, struct appctx *appctx, void *private)
1747{
1748 struct ckch_store *store;
1749 char *err = NULL;
1750 char *filename;
1751
1752 if (!cli_has_level(appctx, ACCESS_LVL_ADMIN))
1753 return 1;
1754
1755 if (!*args[3])
1756 return cli_err(appctx, "'del ssl cert' expects a certificate name\n");
1757
1758 if (HA_SPIN_TRYLOCK(CKCH_LOCK, &ckch_lock))
1759 return cli_err(appctx, "Can't delete the certificate!\nOperations on certificates are currently locked!\n");
1760
1761 filename = args[3];
1762
1763 store = ckchs_lookup(filename);
1764 if (store == NULL) {
1765 memprintf(&err, "certificate '%s' doesn't exist!\n", filename);
1766 goto error;
1767 }
1768 if (!LIST_ISEMPTY(&store->ckch_inst)) {
1769 memprintf(&err, "certificate '%s' in use, can't be deleted!\n", filename);
1770 goto error;
1771 }
1772
1773 ebmb_delete(&store->node);
1774 ckch_store_free(store);
1775
1776 memprintf(&err, "Certificate '%s' deleted!\n", filename);
1777
1778 HA_SPIN_UNLOCK(CKCH_LOCK, &ckch_lock);
1779 return cli_dynmsg(appctx, LOG_NOTICE, err);
1780
1781error:
1782 memprintf(&err, "Can't remove the certificate: %s\n", err ? err : "");
1783 HA_SPIN_UNLOCK(CKCH_LOCK, &ckch_lock);
1784 return cli_dynerr(appctx, err);
1785}
1786
William Lallemandee8530c2020-06-23 18:19:42 +02001787void ckch_deinit()
1788{
1789 struct eb_node *node, *next;
1790 struct ckch_store *store;
1791
1792 node = eb_first(&ckchs_tree);
1793 while (node) {
1794 next = eb_next(node);
1795 store = ebmb_entry(node, struct ckch_store, node);
1796 ckch_store_free(store);
1797 node = next;
1798 }
1799}
William Lallemandda8584c2020-05-14 10:14:37 +02001800
1801/* register cli keywords */
1802static struct cli_kw_list cli_kws = {{ },{
1803 { { "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 },
1804 { { "set", "ssl", "cert", NULL }, "set ssl cert <certfile> <payload> : replace a certificate file", cli_parse_set_cert, NULL, NULL },
1805 { { "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 },
1806 { { "abort", "ssl", "cert", NULL }, "abort ssl cert <certfile> : abort a transaction for a certificate file", cli_parse_abort_cert, NULL, NULL },
1807 { { "del", "ssl", "cert", NULL }, "del ssl cert <certfile> : delete an unused certificate file", cli_parse_del_cert, NULL, NULL },
1808 { { "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 },
1809 { { NULL }, NULL, NULL, NULL }
1810}};
1811
1812INITCALL1(STG_REGISTER, cli_register_kw, &cli_kws);
1813