blob: 10123b4fab90fee9c3fe360412a265b78575beb3 [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;
250
251 /* try to load the PEM */
252 if (ssl_sock_load_pem_into_ckch(path, NULL, ckch , err) != 0) {
253 goto end;
254 }
255
William Lallemand8e8581e2020-10-20 17:36:46 +0200256 fp = alloc_trash_chunk();
257 if (!fp) {
258 memprintf(err, "%sCan't allocate memory\n", err && *err ? *err : "");
259 goto end;
260 }
261
262 if (!chunk_strcpy(fp, path) || (b_data(fp) > MAXPATHLEN)) {
263 memprintf(err, "%s '%s' filename too long'.\n",
264 err && *err ? *err : "", fp->area);
265 ret = 1;
266 goto end;
267 }
268
William Lallemand089c1382020-10-23 17:35:12 +0200269 /* remove the ".crt" extension */
William Lallemand8e8581e2020-10-20 17:36:46 +0200270 if (global_ssl.extra_files_noext) {
271 char *ext;
272
273 /* look for the extension */
274 if ((ext = strrchr(fp->area, '.'))) {
William Lallemand8e8581e2020-10-20 17:36:46 +0200275
Tim Duesterhuse5ff1412021-01-02 22:31:53 +0100276 if (strcmp(ext, ".crt") == 0) {
William Lallemand8e8581e2020-10-20 17:36:46 +0200277 *ext = '\0';
William Lallemand089c1382020-10-23 17:35:12 +0200278 fp->data = strlen(fp->area);
279 }
William Lallemand8e8581e2020-10-20 17:36:46 +0200280 }
281
282 }
283
William Lallemand03c331c2020-05-13 10:10:01 +0200284 /* try to load an external private key if it wasn't in the PEM */
285 if ((ckch->key == NULL) && (global_ssl.extra_files & SSL_GF_KEY)) {
William Lallemand03c331c2020-05-13 10:10:01 +0200286 struct stat st;
287
William Lallemand8e8581e2020-10-20 17:36:46 +0200288
289 if (!chunk_strcat(fp, ".key") || (b_data(fp) > MAXPATHLEN)) {
290 memprintf(err, "%s '%s' filename too long'.\n",
291 err && *err ? *err : "", fp->area);
292 ret = 1;
293 goto end;
294 }
295
296 if (stat(fp->area, &st) == 0) {
297 if (ssl_sock_load_key_into_ckch(fp->area, NULL, ckch, err)) {
William Lallemand03c331c2020-05-13 10:10:01 +0200298 memprintf(err, "%s '%s' is present but cannot be read or parsed'.\n",
William Lallemand8e8581e2020-10-20 17:36:46 +0200299 err && *err ? *err : "", fp->area);
William Lallemand03c331c2020-05-13 10:10:01 +0200300 goto end;
301 }
302 }
William Lallemand03c331c2020-05-13 10:10:01 +0200303
William Lallemand8e8581e2020-10-20 17:36:46 +0200304 if (ckch->key == NULL) {
305 memprintf(err, "%sNo Private Key found in '%s'.\n", err && *err ? *err : "", fp->area);
306 goto end;
307 }
308 /* remove the added extension */
309 *(fp->area + fp->data - strlen(".key")) = '\0';
310 b_sub(fp, strlen(".key"));
William Lallemand03c331c2020-05-13 10:10:01 +0200311 }
312
313 if (!X509_check_private_key(ckch->cert, ckch->key)) {
314 memprintf(err, "%sinconsistencies between private key and certificate loaded '%s'.\n",
315 err && *err ? *err : "", path);
316 goto end;
317 }
318
Ilya Shipitsinc47d6762021-02-13 11:45:33 +0500319#ifdef HAVE_SSL_SCTL
William Lallemand03c331c2020-05-13 10:10:01 +0200320 /* try to load the sctl file */
321 if (global_ssl.extra_files & SSL_GF_SCTL) {
William Lallemand03c331c2020-05-13 10:10:01 +0200322 struct stat st;
323
William Lallemand8e8581e2020-10-20 17:36:46 +0200324 if (!chunk_strcat(fp, ".sctl") || b_data(fp) > MAXPATHLEN) {
325 memprintf(err, "%s '%s' filename too long'.\n",
326 err && *err ? *err : "", fp->area);
327 ret = 1;
328 goto end;
329 }
330
331 if (stat(fp->area, &st) == 0) {
332 if (ssl_sock_load_sctl_from_file(fp->area, NULL, ckch, err)) {
William Lallemand03c331c2020-05-13 10:10:01 +0200333 memprintf(err, "%s '%s.sctl' is present but cannot be read or parsed'.\n",
William Lallemand8e8581e2020-10-20 17:36:46 +0200334 err && *err ? *err : "", fp->area);
William Lallemand03c331c2020-05-13 10:10:01 +0200335 ret = 1;
336 goto end;
337 }
338 }
William Lallemand8e8581e2020-10-20 17:36:46 +0200339 /* remove the added extension */
340 *(fp->area + fp->data - strlen(".sctl")) = '\0';
341 b_sub(fp, strlen(".sctl"));
William Lallemand03c331c2020-05-13 10:10:01 +0200342 }
343#endif
344
345 /* try to load an ocsp response file */
346 if (global_ssl.extra_files & SSL_GF_OCSP) {
William Lallemand03c331c2020-05-13 10:10:01 +0200347 struct stat st;
348
William Lallemand8e8581e2020-10-20 17:36:46 +0200349 if (!chunk_strcat(fp, ".ocsp") || b_data(fp) > MAXPATHLEN) {
350 memprintf(err, "%s '%s' filename too long'.\n",
351 err && *err ? *err : "", fp->area);
352 ret = 1;
353 goto end;
354 }
355
356 if (stat(fp->area, &st) == 0) {
357 if (ssl_sock_load_ocsp_response_from_file(fp->area, NULL, ckch, err)) {
William Lallemand03c331c2020-05-13 10:10:01 +0200358 ret = 1;
359 goto end;
360 }
361 }
William Lallemand8e8581e2020-10-20 17:36:46 +0200362 /* remove the added extension */
363 *(fp->area + fp->data - strlen(".ocsp")) = '\0';
364 b_sub(fp, strlen(".ocsp"));
William Lallemand03c331c2020-05-13 10:10:01 +0200365 }
366
367#ifndef OPENSSL_IS_BORINGSSL /* Useless for BoringSSL */
368 if (ckch->ocsp_response && (global_ssl.extra_files & SSL_GF_OCSP_ISSUER)) {
369 /* if no issuer was found, try to load an issuer from the .issuer */
370 if (!ckch->ocsp_issuer) {
371 struct stat st;
William Lallemand8e8581e2020-10-20 17:36:46 +0200372
373 if (!chunk_strcat(fp, ".issuer") || b_data(fp) > MAXPATHLEN) {
374 memprintf(err, "%s '%s' filename too long'.\n",
375 err && *err ? *err : "", fp->area);
376 ret = 1;
377 goto end;
378 }
William Lallemand03c331c2020-05-13 10:10:01 +0200379
William Lallemand8e8581e2020-10-20 17:36:46 +0200380 if (stat(fp->area, &st) == 0) {
381 if (ssl_sock_load_issuer_file_into_ckch(fp->area, NULL, ckch, err)) {
William Lallemand03c331c2020-05-13 10:10:01 +0200382 ret = 1;
383 goto end;
384 }
385
386 if (X509_check_issued(ckch->ocsp_issuer, ckch->cert) != X509_V_OK) {
387 memprintf(err, "%s '%s' is not an issuer'.\n",
William Lallemand8e8581e2020-10-20 17:36:46 +0200388 err && *err ? *err : "", fp->area);
William Lallemand03c331c2020-05-13 10:10:01 +0200389 ret = 1;
390 goto end;
391 }
392 }
William Lallemand8e8581e2020-10-20 17:36:46 +0200393 /* remove the added extension */
394 *(fp->area + fp->data - strlen(".issuer")) = '\0';
395 b_sub(fp, strlen(".issuer"));
William Lallemand03c331c2020-05-13 10:10:01 +0200396 }
397 }
398#endif
399
400 ret = 0;
401
402end:
403
404 ERR_clear_error();
405
406 /* Something went wrong in one of the reads */
407 if (ret != 0)
408 ssl_sock_free_cert_key_and_chain_contents(ckch);
409
William Lallemand8e8581e2020-10-20 17:36:46 +0200410 free_trash_chunk(fp);
411
William Lallemand03c331c2020-05-13 10:10:01 +0200412 return ret;
413}
414
415/*
416 * Try to load a private key file from a <path> or a buffer <buf>
417 *
418 * If it failed you should not attempt to use the ckch but free it.
419 *
420 * Return 0 on success or != 0 on failure
421 */
422int ssl_sock_load_key_into_ckch(const char *path, char *buf, struct cert_key_and_chain *ckch , char **err)
423{
424 BIO *in = NULL;
425 int ret = 1;
426 EVP_PKEY *key = NULL;
427
428 if (buf) {
429 /* reading from a buffer */
430 in = BIO_new_mem_buf(buf, -1);
431 if (in == NULL) {
432 memprintf(err, "%sCan't allocate memory\n", err && *err ? *err : "");
433 goto end;
434 }
435
436 } else {
437 /* reading from a file */
438 in = BIO_new(BIO_s_file());
439 if (in == NULL)
440 goto end;
441
442 if (BIO_read_filename(in, path) <= 0)
443 goto end;
444 }
445
446 /* Read Private Key */
447 key = PEM_read_bio_PrivateKey(in, NULL, NULL, NULL);
448 if (key == NULL) {
449 memprintf(err, "%sunable to load private key from file '%s'.\n",
450 err && *err ? *err : "", path);
451 goto end;
452 }
453
454 ret = 0;
455
456 SWAP(ckch->key, key);
457
458end:
459
460 ERR_clear_error();
461 if (in)
462 BIO_free(in);
463 if (key)
464 EVP_PKEY_free(key);
465
466 return ret;
467}
468
469/*
470 * Try to load a PEM file from a <path> or a buffer <buf>
471 * The PEM must contain at least a Certificate,
472 * It could contain a DH, a certificate chain and a PrivateKey.
473 *
474 * If it failed you should not attempt to use the ckch but free it.
475 *
476 * Return 0 on success or != 0 on failure
477 */
478int ssl_sock_load_pem_into_ckch(const char *path, char *buf, struct cert_key_and_chain *ckch , char **err)
479{
480 BIO *in = NULL;
481 int ret = 1;
482 X509 *ca;
483 X509 *cert = NULL;
484 EVP_PKEY *key = NULL;
485 DH *dh = NULL;
486 STACK_OF(X509) *chain = NULL;
487
488 if (buf) {
489 /* reading from a buffer */
490 in = BIO_new_mem_buf(buf, -1);
491 if (in == NULL) {
492 memprintf(err, "%sCan't allocate memory\n", err && *err ? *err : "");
493 goto end;
494 }
495
496 } else {
497 /* reading from a file */
498 in = BIO_new(BIO_s_file());
499 if (in == NULL) {
500 memprintf(err, "%sCan't allocate memory\n", err && *err ? *err : "");
501 goto end;
502 }
503
504 if (BIO_read_filename(in, path) <= 0) {
505 memprintf(err, "%scannot open the file '%s'.\n",
506 err && *err ? *err : "", path);
507 goto end;
508 }
509 }
510
511 /* Read Private Key */
512 key = PEM_read_bio_PrivateKey(in, NULL, NULL, NULL);
513 /* no need to check for errors here, because the private key could be loaded later */
514
515#ifndef OPENSSL_NO_DH
516 /* Seek back to beginning of file */
517 if (BIO_reset(in) == -1) {
518 memprintf(err, "%san error occurred while reading the file '%s'.\n",
519 err && *err ? *err : "", path);
520 goto end;
521 }
522
523 dh = PEM_read_bio_DHparams(in, NULL, NULL, NULL);
524 /* no need to return an error there, dh is not mandatory */
525#endif
526
527 /* Seek back to beginning of file */
528 if (BIO_reset(in) == -1) {
529 memprintf(err, "%san error occurred while reading the file '%s'.\n",
530 err && *err ? *err : "", path);
531 goto end;
532 }
533
534 /* Read Certificate */
535 cert = PEM_read_bio_X509_AUX(in, NULL, NULL, NULL);
536 if (cert == NULL) {
537 memprintf(err, "%sunable to load certificate from file '%s'.\n",
538 err && *err ? *err : "", path);
539 goto end;
540 }
541
542 /* Look for a Certificate Chain */
543 while ((ca = PEM_read_bio_X509(in, NULL, NULL, NULL))) {
544 if (chain == NULL)
545 chain = sk_X509_new_null();
546 if (!sk_X509_push(chain, ca)) {
547 X509_free(ca);
548 goto end;
549 }
550 }
551
552 ret = ERR_get_error();
553 if (ret && (ERR_GET_LIB(ret) != ERR_LIB_PEM && ERR_GET_REASON(ret) != PEM_R_NO_START_LINE)) {
554 memprintf(err, "%sunable to load certificate chain from file '%s'.\n",
555 err && *err ? *err : "", path);
556 goto end;
557 }
558
559 /* once it loaded the PEM, it should remove everything else in the ckch */
560 if (ckch->ocsp_response) {
Willy Tarreau61cfdf42021-02-20 10:46:51 +0100561 ha_free(&ckch->ocsp_response->area);
562 ha_free(&ckch->ocsp_response);
William Lallemand03c331c2020-05-13 10:10:01 +0200563 }
564
565 if (ckch->sctl) {
Willy Tarreau61cfdf42021-02-20 10:46:51 +0100566 ha_free(&ckch->sctl->area);
567 ha_free(&ckch->sctl);
William Lallemand03c331c2020-05-13 10:10:01 +0200568 }
569
570 if (ckch->ocsp_issuer) {
571 X509_free(ckch->ocsp_issuer);
572 ckch->ocsp_issuer = NULL;
573 }
574
575 /* no error, fill ckch with new context, old context will be free at end: */
576 SWAP(ckch->key, key);
577 SWAP(ckch->dh, dh);
578 SWAP(ckch->cert, cert);
579 SWAP(ckch->chain, chain);
580
581 ret = 0;
582
583end:
584
585 ERR_clear_error();
586 if (in)
587 BIO_free(in);
588 if (key)
589 EVP_PKEY_free(key);
590 if (dh)
591 DH_free(dh);
592 if (cert)
593 X509_free(cert);
594 if (chain)
595 sk_X509_pop_free(chain, X509_free);
596
597 return ret;
598}
599
600/* Frees the contents of a cert_key_and_chain
601 */
602void ssl_sock_free_cert_key_and_chain_contents(struct cert_key_and_chain *ckch)
603{
604 if (!ckch)
605 return;
606
607 /* Free the certificate and set pointer to NULL */
608 if (ckch->cert)
609 X509_free(ckch->cert);
610 ckch->cert = NULL;
611
612 /* Free the key and set pointer to NULL */
613 if (ckch->key)
614 EVP_PKEY_free(ckch->key);
615 ckch->key = NULL;
616
617 /* Free each certificate in the chain */
618 if (ckch->chain)
619 sk_X509_pop_free(ckch->chain, X509_free);
620 ckch->chain = NULL;
621
622 if (ckch->dh)
623 DH_free(ckch->dh);
624 ckch->dh = NULL;
625
626 if (ckch->sctl) {
Willy Tarreau61cfdf42021-02-20 10:46:51 +0100627 ha_free(&ckch->sctl->area);
628 ha_free(&ckch->sctl);
William Lallemand03c331c2020-05-13 10:10:01 +0200629 }
630
631 if (ckch->ocsp_response) {
Willy Tarreau61cfdf42021-02-20 10:46:51 +0100632 ha_free(&ckch->ocsp_response->area);
633 ha_free(&ckch->ocsp_response);
William Lallemand03c331c2020-05-13 10:10:01 +0200634 }
635
636 if (ckch->ocsp_issuer)
637 X509_free(ckch->ocsp_issuer);
638 ckch->ocsp_issuer = NULL;
639}
640
641/*
642 *
643 * This function copy a cert_key_and_chain in memory
644 *
645 * It's used to try to apply changes on a ckch before committing them, because
646 * most of the time it's not possible to revert those changes
647 *
648 * Return a the dst or NULL
649 */
650struct cert_key_and_chain *ssl_sock_copy_cert_key_and_chain(struct cert_key_and_chain *src,
651 struct cert_key_and_chain *dst)
652{
William Lallemand6c096142021-02-23 14:45:45 +0100653 if (!src || !dst)
654 return NULL;
655
William Lallemand03c331c2020-05-13 10:10:01 +0200656 if (src->cert) {
657 dst->cert = src->cert;
658 X509_up_ref(src->cert);
659 }
660
661 if (src->key) {
662 dst->key = src->key;
663 EVP_PKEY_up_ref(src->key);
664 }
665
666 if (src->chain) {
667 dst->chain = X509_chain_up_ref(src->chain);
668 }
669
670 if (src->dh) {
671 DH_up_ref(src->dh);
672 dst->dh = src->dh;
673 }
674
675 if (src->sctl) {
676 struct buffer *sctl;
677
678 sctl = calloc(1, sizeof(*sctl));
679 if (!chunk_dup(sctl, src->sctl)) {
Willy Tarreau61cfdf42021-02-20 10:46:51 +0100680 ha_free(&sctl);
William Lallemand03c331c2020-05-13 10:10:01 +0200681 goto error;
682 }
683 dst->sctl = sctl;
684 }
685
686 if (src->ocsp_response) {
687 struct buffer *ocsp_response;
688
689 ocsp_response = calloc(1, sizeof(*ocsp_response));
690 if (!chunk_dup(ocsp_response, src->ocsp_response)) {
Willy Tarreau61cfdf42021-02-20 10:46:51 +0100691 ha_free(&ocsp_response);
William Lallemand03c331c2020-05-13 10:10:01 +0200692 goto error;
693 }
694 dst->ocsp_response = ocsp_response;
695 }
696
697 if (src->ocsp_issuer) {
698 X509_up_ref(src->ocsp_issuer);
699 dst->ocsp_issuer = src->ocsp_issuer;
700 }
701
702 return dst;
703
704error:
705
706 /* free everything */
707 ssl_sock_free_cert_key_and_chain_contents(dst);
708
709 return NULL;
710}
711
712/*
713 * return 0 on success or != 0 on failure
714 */
715int ssl_sock_load_issuer_file_into_ckch(const char *path, char *buf, struct cert_key_and_chain *ckch, char **err)
716{
717 int ret = 1;
718 BIO *in = NULL;
719 X509 *issuer;
720
721 if (buf) {
722 /* reading from a buffer */
723 in = BIO_new_mem_buf(buf, -1);
724 if (in == NULL) {
725 memprintf(err, "%sCan't allocate memory\n", err && *err ? *err : "");
726 goto end;
727 }
728
729 } else {
730 /* reading from a file */
731 in = BIO_new(BIO_s_file());
732 if (in == NULL)
733 goto end;
734
735 if (BIO_read_filename(in, path) <= 0)
736 goto end;
737 }
738
739 issuer = PEM_read_bio_X509_AUX(in, NULL, NULL, NULL);
740 if (!issuer) {
741 memprintf(err, "%s'%s' cannot be read or parsed'.\n",
742 err && *err ? *err : "", path);
743 goto end;
744 }
745 /* no error, fill ckch with new context, old context must be free */
746 if (ckch->ocsp_issuer)
747 X509_free(ckch->ocsp_issuer);
748 ckch->ocsp_issuer = issuer;
749 ret = 0;
750
751end:
752
753 ERR_clear_error();
754 if (in)
755 BIO_free(in);
756
757 return ret;
758}
759
760/******************** ckch_store functions ***********************************
761 * The ckch_store is a structure used to cache and index the SSL files used in
762 * configuration
763 */
764
765/*
766 * Free a ckch_store, its ckch, its instances and remove it from the ebtree
767 */
768void ckch_store_free(struct ckch_store *store)
769{
770 struct ckch_inst *inst, *inst_s;
771
772 if (!store)
773 return;
774
William Lallemandbd8e6ed2020-09-16 16:08:08 +0200775 ssl_sock_free_cert_key_and_chain_contents(store->ckch);
William Lallemand03c331c2020-05-13 10:10:01 +0200776
Willy Tarreau61cfdf42021-02-20 10:46:51 +0100777 ha_free(&store->ckch);
William Lallemand03c331c2020-05-13 10:10:01 +0200778
779 list_for_each_entry_safe(inst, inst_s, &store->ckch_inst, by_ckchs) {
780 ckch_inst_free(inst);
781 }
782 ebmb_delete(&store->node);
783 free(store);
784}
785
786/*
787 * create and initialize a ckch_store
788 * <path> is the key name
789 * <nmemb> is the number of store->ckch objects to allocate
790 *
791 * Return a ckch_store or NULL upon failure.
792 */
William Lallemandbd8e6ed2020-09-16 16:08:08 +0200793struct ckch_store *ckch_store_new(const char *filename)
William Lallemand03c331c2020-05-13 10:10:01 +0200794{
795 struct ckch_store *store;
796 int pathlen;
797
798 pathlen = strlen(filename);
799 store = calloc(1, sizeof(*store) + pathlen + 1);
800 if (!store)
801 return NULL;
802
William Lallemand03c331c2020-05-13 10:10:01 +0200803 memcpy(store->path, filename, pathlen + 1);
804
805 LIST_INIT(&store->ckch_inst);
806 LIST_INIT(&store->crtlist_entry);
807
William Lallemandbd8e6ed2020-09-16 16:08:08 +0200808 store->ckch = calloc(1, sizeof(*store->ckch));
William Lallemand03c331c2020-05-13 10:10:01 +0200809 if (!store->ckch)
810 goto error;
811
812 return store;
813error:
814 ckch_store_free(store);
815 return NULL;
816}
817
818/* allocate and duplicate a ckch_store
819 * Return a new ckch_store or NULL */
820struct ckch_store *ckchs_dup(const struct ckch_store *src)
821{
822 struct ckch_store *dst;
823
William Lallemand6c096142021-02-23 14:45:45 +0100824 if (!src)
825 return NULL;
826
William Lallemandbd8e6ed2020-09-16 16:08:08 +0200827 dst = ckch_store_new(src->path);
Eric Salama6ac61e32021-02-23 16:50:57 +0100828 if (!dst)
829 return NULL;
William Lallemand03c331c2020-05-13 10:10:01 +0200830
William Lallemandbd8e6ed2020-09-16 16:08:08 +0200831 if (!ssl_sock_copy_cert_key_and_chain(src->ckch, dst->ckch))
832 goto error;
William Lallemand03c331c2020-05-13 10:10:01 +0200833
834 return dst;
835
836error:
837 ckch_store_free(dst);
838
839 return NULL;
840}
841
842/*
843 * lookup a path into the ckchs tree.
844 */
845struct ckch_store *ckchs_lookup(char *path)
846{
847 struct ebmb_node *eb;
848
849 eb = ebst_lookup(&ckchs_tree, path);
850 if (!eb)
851 return NULL;
852
853 return ebmb_entry(eb, struct ckch_store, node);
854}
855
856/*
857 * This function allocate a ckch_store and populate it with certificates from files.
858 */
William Lallemandbd8e6ed2020-09-16 16:08:08 +0200859struct ckch_store *ckchs_load_cert_file(char *path, char **err)
William Lallemand03c331c2020-05-13 10:10:01 +0200860{
861 struct ckch_store *ckchs;
862
William Lallemandbd8e6ed2020-09-16 16:08:08 +0200863 ckchs = ckch_store_new(path);
William Lallemand03c331c2020-05-13 10:10:01 +0200864 if (!ckchs) {
865 memprintf(err, "%sunable to allocate memory.\n", err && *err ? *err : "");
866 goto end;
867 }
William Lallemand03c331c2020-05-13 10:10:01 +0200868
William Lallemandbd8e6ed2020-09-16 16:08:08 +0200869 if (ssl_sock_load_files_into_ckch(path, ckchs->ckch, err) == 1)
870 goto end;
William Lallemand03c331c2020-05-13 10:10:01 +0200871
William Lallemandbd8e6ed2020-09-16 16:08:08 +0200872 /* insert into the ckchs tree */
873 memcpy(ckchs->path, path, strlen(path) + 1);
874 ebst_insert(&ckchs_tree, &ckchs->node);
William Lallemand03c331c2020-05-13 10:10:01 +0200875 return ckchs;
876
877end:
878 ckch_store_free(ckchs);
879
880 return NULL;
881}
882
William Lallemandfa1d8b42020-05-13 15:46:10 +0200883
884/******************** ckch_inst functions ******************************/
885
886/* unlink a ckch_inst, free all SNIs, free the ckch_inst */
887/* The caller must use the lock of the bind_conf if used with inserted SNIs */
888void ckch_inst_free(struct ckch_inst *inst)
889{
890 struct sni_ctx *sni, *sni_s;
Remi Tricot-Le Breton4458b972021-02-19 17:41:55 +0100891 struct ckch_inst_link_ref *link_ref, *link_ref_s;
William Lallemandfa1d8b42020-05-13 15:46:10 +0200892
893 if (inst == NULL)
894 return;
895
896 list_for_each_entry_safe(sni, sni_s, &inst->sni_ctx, by_ckch_inst) {
897 SSL_CTX_free(sni->ctx);
Willy Tarreau2b718102021-04-21 07:32:39 +0200898 LIST_DELETE(&sni->by_ckch_inst);
William Lallemandfa1d8b42020-05-13 15:46:10 +0200899 ebmb_delete(&sni->name);
900 free(sni);
901 }
Remi Tricot-Le Bretonf3eedfe2021-01-25 17:19:44 +0100902 SSL_CTX_free(inst->ctx);
903 inst->ctx = NULL;
Willy Tarreau2b718102021-04-21 07:32:39 +0200904 LIST_DELETE(&inst->by_ckchs);
905 LIST_DELETE(&inst->by_crtlist_entry);
Remi Tricot-Le Breton4458b972021-02-19 17:41:55 +0100906
907 list_for_each_entry_safe(link_ref, link_ref_s, &inst->cafile_link_refs, list) {
908 LIST_DELETE(&link_ref->link->list);
909 LIST_DELETE(&link_ref->list);
910 free(link_ref);
911 }
912
William Lallemandfa1d8b42020-05-13 15:46:10 +0200913 free(inst);
914}
915
916/* Alloc and init a ckch_inst */
917struct ckch_inst *ckch_inst_new()
918{
919 struct ckch_inst *ckch_inst;
920
921 ckch_inst = calloc(1, sizeof *ckch_inst);
922 if (!ckch_inst)
923 return NULL;
924
925 LIST_INIT(&ckch_inst->sni_ctx);
926 LIST_INIT(&ckch_inst->by_ckchs);
927 LIST_INIT(&ckch_inst->by_crtlist_entry);
Remi Tricot-Le Breton4458b972021-02-19 17:41:55 +0100928 LIST_INIT(&ckch_inst->cafile_link_refs);
William Lallemandfa1d8b42020-05-13 15:46:10 +0200929
930 return ckch_inst;
931}
932
Remi Tricot-Le Bretonaf8820a2021-04-13 10:10:37 +0200933
934/******************** ssl_store functions ******************************/
Remi Tricot-Le Breton9f0c9362021-02-19 15:06:28 +0100935struct eb_root cafile_tree = EB_ROOT;
Remi Tricot-Le Bretonaf8820a2021-04-13 10:10:37 +0200936
Remi Tricot-Le Breton9f0c9362021-02-19 15:06:28 +0100937/*
938 * Returns the cafile_entry found in the cafile_tree indexed by the path 'path'.
939 * If 'oldest_entry' is 1, returns the "original" cafile_entry (since
940 * during a set cafile/commit cafile cycle there might be two entries for any
941 * given path, the original one and the new one set via the CLI but not
942 * committed yet).
943 */
Remi Tricot-Le Breton4458b972021-02-19 17:41:55 +0100944struct cafile_entry *ssl_store_get_cafile_entry(char *path, int oldest_entry)
Remi Tricot-Le Bretonaf8820a2021-04-13 10:10:37 +0200945{
Remi Tricot-Le Breton9f0c9362021-02-19 15:06:28 +0100946 struct cafile_entry *ca_e = NULL;
Remi Tricot-Le Bretonaf8820a2021-04-13 10:10:37 +0200947 struct ebmb_node *eb;
948
949 eb = ebst_lookup(&cafile_tree, path);
Remi Tricot-Le Breton9f0c9362021-02-19 15:06:28 +0100950 while (eb) {
Remi Tricot-Le Bretonaf8820a2021-04-13 10:10:37 +0200951 ca_e = ebmb_entry(eb, struct cafile_entry, node);
Remi Tricot-Le Breton9f0c9362021-02-19 15:06:28 +0100952 /* The ebst_lookup in a tree that has duplicates returns the
953 * oldest entry first. If we want the latest entry, we need to
954 * iterate over all the duplicates until we find the last one
955 * (in our case there should never be more than two entries for
956 * any given path). */
957 if (oldest_entry)
958 return ca_e;
959 eb = ebmb_next_dup(eb);
Remi Tricot-Le Bretonaf8820a2021-04-13 10:10:37 +0200960 }
Remi Tricot-Le Breton9f0c9362021-02-19 15:06:28 +0100961 return ca_e;
962}
963
964X509_STORE* ssl_store_get0_locations_file(char *path)
965{
966 struct cafile_entry *ca_e = ssl_store_get_cafile_entry(path, 0);
967
968 if (ca_e)
969 return ca_e->ca_store;
970
Remi Tricot-Le Bretonaf8820a2021-04-13 10:10:37 +0200971 return NULL;
972}
973
Remi Tricot-Le Breton5daff3c2021-02-22 15:54:55 +0100974/* Create a cafile_entry object, without adding it to the cafile_tree. */
975struct cafile_entry *ssl_store_create_cafile_entry(char *path, X509_STORE *store)
976{
977 struct cafile_entry *ca_e;
978 int pathlen;
979
980 pathlen = strlen(path);
981
982 ca_e = calloc(1, sizeof(*ca_e) + pathlen + 1);
983 if (ca_e) {
984 memcpy(ca_e->path, path, pathlen + 1);
985 ca_e->ca_store = store;
986 LIST_INIT(&ca_e->ckch_inst_link);
987 }
988 return ca_e;
989}
990
991/* Delete a cafile_entry. The caller is responsible from removing this entry
992 * from the cafile_tree first if is was previously added into it. */
993void ssl_store_delete_cafile_entry(struct cafile_entry *ca_e)
994{
995 struct ckch_inst_link *link, *link_s;
996 if (!ca_e)
997 return;
998
999 X509_STORE_free(ca_e->ca_store);
1000
1001 list_for_each_entry_safe(link, link_s, &ca_e->ckch_inst_link, list) {
1002 struct ckch_inst *inst = link->ckch_inst;
1003 struct ckch_inst_link_ref *link_ref, *link_ref_s;
1004 list_for_each_entry_safe(link_ref, link_ref_s, &inst->cafile_link_refs, list) {
1005 if (link_ref->link == link) {
1006 LIST_DELETE(&link_ref->list);
1007 free(link_ref);
1008 break;
1009 }
1010 }
1011 LIST_DELETE(&link->list);
1012 free(link);
1013 }
1014
1015 free(ca_e);
1016}
1017
Remi Tricot-Le Breton383fb142021-02-22 18:26:14 +01001018/*
1019 * Build a cafile_entry out of a buffer instead of out of a file.
1020 * This function is used when the "commit ssl ca-file" cli command is used.
1021 * It can parse CERTIFICATE sections as well as CRL ones.
1022 * Returns 0 in case of success, 1 otherwise.
1023 */
1024int ssl_store_load_ca_from_buf(struct cafile_entry *ca_e, char *cert_buf)
1025{
1026 int retval = 0;
1027
1028 if (!ca_e)
1029 return 1;
1030
1031 if (!ca_e->ca_store) {
1032 ca_e->ca_store = X509_STORE_new();
1033 if (ca_e->ca_store) {
1034 BIO *bio = BIO_new_mem_buf(cert_buf, strlen(cert_buf));
1035 if (bio) {
1036 X509_INFO *info;
1037 int i;
1038 STACK_OF(X509_INFO) *infos = PEM_X509_INFO_read_bio(bio, NULL, NULL, NULL);
1039 if (!infos)
1040 {
1041 BIO_free(bio);
1042 return 1;
1043 }
1044
1045 for (i = 0; i < sk_X509_INFO_num(infos) && !retval; i++) {
1046 info = sk_X509_INFO_value(infos, i);
1047 /* X509_STORE_add_cert and X509_STORE_add_crl return 1 on success */
1048 if (info->x509) {
1049 retval = !X509_STORE_add_cert(ca_e->ca_store, info->x509);
1050 }
1051 if (!retval && info->crl) {
1052 retval = !X509_STORE_add_crl(ca_e->ca_store, info->crl);
1053 }
1054 }
1055 retval = retval || (i != sk_X509_INFO_num(infos));
1056
1057 /* Cleanup */
1058 sk_X509_INFO_pop_free(infos, X509_INFO_free);
1059 BIO_free(bio);
1060 }
1061 }
1062 }
1063
1064 return retval;
1065}
1066
Remi Tricot-Le Bretonaf8820a2021-04-13 10:10:37 +02001067int ssl_store_load_locations_file(char *path, int create_if_none)
1068{
1069 X509_STORE *store = ssl_store_get0_locations_file(path);
1070
1071 /* If this function is called by the CLI, we should not call the
1072 * X509_STORE_load_locations function because it performs forbidden disk
1073 * accesses. */
1074 if (!store && create_if_none) {
1075 struct cafile_entry *ca_e;
1076 store = X509_STORE_new();
1077 if (X509_STORE_load_locations(store, path, NULL)) {
Remi Tricot-Le Breton5daff3c2021-02-22 15:54:55 +01001078 ca_e = ssl_store_create_cafile_entry(path, store);
Remi Tricot-Le Bretonaf8820a2021-04-13 10:10:37 +02001079 if (ca_e) {
Remi Tricot-Le Bretonaf8820a2021-04-13 10:10:37 +02001080 ebst_insert(&cafile_tree, &ca_e->node);
1081 }
1082 } else {
1083 X509_STORE_free(store);
1084 store = NULL;
1085 }
1086 }
1087 return (store != NULL);
1088}
1089
1090
William Lallemandda8584c2020-05-14 10:14:37 +02001091/*************************** CLI commands ***********************/
1092
1093/* Type of SSL payloads that can be updated over the CLI */
1094
1095enum {
1096 CERT_TYPE_PEM = 0,
1097 CERT_TYPE_KEY,
1098#if ((defined SSL_CTRL_SET_TLSEXT_STATUS_REQ_CB && !defined OPENSSL_NO_OCSP) || defined OPENSSL_IS_BORINGSSL)
1099 CERT_TYPE_OCSP,
1100#endif
1101 CERT_TYPE_ISSUER,
Ilya Shipitsinc47d6762021-02-13 11:45:33 +05001102#ifdef HAVE_SSL_SCTL
William Lallemandda8584c2020-05-14 10:14:37 +02001103 CERT_TYPE_SCTL,
1104#endif
1105 CERT_TYPE_MAX,
1106};
1107
1108struct {
1109 const char *ext;
1110 int type;
1111 int (*load)(const char *path, char *payload, struct cert_key_and_chain *ckch, char **err);
1112 /* add a parsing callback */
1113} cert_exts[CERT_TYPE_MAX+1] = {
1114 [CERT_TYPE_PEM] = { "", CERT_TYPE_PEM, &ssl_sock_load_pem_into_ckch }, /* default mode, no extensions */
1115 [CERT_TYPE_KEY] = { "key", CERT_TYPE_KEY, &ssl_sock_load_key_into_ckch },
1116#if ((defined SSL_CTRL_SET_TLSEXT_STATUS_REQ_CB && !defined OPENSSL_NO_OCSP) || defined OPENSSL_IS_BORINGSSL)
1117 [CERT_TYPE_OCSP] = { "ocsp", CERT_TYPE_OCSP, &ssl_sock_load_ocsp_response_from_file },
1118#endif
Ilya Shipitsinc47d6762021-02-13 11:45:33 +05001119#ifdef HAVE_SSL_SCTL
William Lallemandda8584c2020-05-14 10:14:37 +02001120 [CERT_TYPE_SCTL] = { "sctl", CERT_TYPE_SCTL, &ssl_sock_load_sctl_from_file },
1121#endif
1122 [CERT_TYPE_ISSUER] = { "issuer", CERT_TYPE_ISSUER, &ssl_sock_load_issuer_file_into_ckch },
1123 [CERT_TYPE_MAX] = { NULL, CERT_TYPE_MAX, NULL },
1124};
1125
1126
1127/* release function of the `show ssl cert' command */
1128static void cli_release_show_cert(struct appctx *appctx)
1129{
1130 HA_SPIN_UNLOCK(CKCH_LOCK, &ckch_lock);
1131}
1132
1133/* IO handler of "show ssl cert <filename>" */
1134static int cli_io_handler_show_cert(struct appctx *appctx)
1135{
1136 struct buffer *trash = alloc_trash_chunk();
1137 struct ebmb_node *node;
1138 struct stream_interface *si = appctx->owner;
1139 struct ckch_store *ckchs;
1140
1141 if (trash == NULL)
1142 return 1;
1143
1144 if (!appctx->ctx.ssl.old_ckchs) {
1145 if (ckchs_transaction.old_ckchs) {
1146 ckchs = ckchs_transaction.old_ckchs;
1147 chunk_appendf(trash, "# transaction\n");
William Lallemand5685ccf2020-09-16 16:12:25 +02001148 chunk_appendf(trash, "*%s\n", ckchs->path);
William Lallemandda8584c2020-05-14 10:14:37 +02001149 }
1150 }
1151
1152 if (!appctx->ctx.cli.p0) {
1153 chunk_appendf(trash, "# filename\n");
1154 node = ebmb_first(&ckchs_tree);
1155 } else {
1156 node = &((struct ckch_store *)appctx->ctx.cli.p0)->node;
1157 }
1158 while (node) {
1159 ckchs = ebmb_entry(node, struct ckch_store, node);
William Lallemand5685ccf2020-09-16 16:12:25 +02001160 chunk_appendf(trash, "%s\n", ckchs->path);
William Lallemandda8584c2020-05-14 10:14:37 +02001161
1162 node = ebmb_next(node);
1163 if (ci_putchk(si_ic(si), trash) == -1) {
1164 si_rx_room_blk(si);
1165 goto yield;
1166 }
1167 }
1168
1169 appctx->ctx.cli.p0 = NULL;
1170 free_trash_chunk(trash);
1171 return 1;
1172yield:
1173
1174 free_trash_chunk(trash);
1175 appctx->ctx.cli.p0 = ckchs;
1176 return 0; /* should come back */
1177}
1178
1179/*
1180 * Extract and format the DNS SAN extensions and copy result into a chuink
1181 * Return 0;
1182 */
1183#ifdef SSL_CTRL_SET_TLSEXT_HOSTNAME
1184static int ssl_sock_get_san_oneline(X509 *cert, struct buffer *out)
1185{
1186 int i;
1187 char *str;
1188 STACK_OF(GENERAL_NAME) *names = NULL;
1189
1190 names = X509_get_ext_d2i(cert, NID_subject_alt_name, NULL, NULL);
1191 if (names) {
1192 for (i = 0; i < sk_GENERAL_NAME_num(names); i++) {
1193 GENERAL_NAME *name = sk_GENERAL_NAME_value(names, i);
1194 if (i > 0)
1195 chunk_appendf(out, ", ");
1196 if (name->type == GEN_DNS) {
1197 if (ASN1_STRING_to_UTF8((unsigned char **)&str, name->d.dNSName) >= 0) {
1198 chunk_appendf(out, "DNS:%s", str);
1199 OPENSSL_free(str);
1200 }
1201 }
1202 }
1203 sk_GENERAL_NAME_pop_free(names, GENERAL_NAME_free);
1204 }
1205 return 0;
1206}
1207#endif
1208
Remi Tricot-Le Breton4458b972021-02-19 17:41:55 +01001209/*
1210 * Build the ckch_inst_link that will be chained in the CA file entry and the
1211 * corresponding ckch_inst_link_ref that will be chained in the ckch instance.
1212 * Return 0 in case of success.
1213 */
1214static int do_chain_inst_and_cafile(struct cafile_entry *cafile_entry, struct ckch_inst *ckch_inst)
1215{
1216 struct ckch_inst_link *new_link;
1217 if (!LIST_ISEMPTY(&cafile_entry->ckch_inst_link)) {
1218 struct ckch_inst_link *link = LIST_ELEM(cafile_entry->ckch_inst_link.n,
1219 typeof(link), list);
1220 /* Do not add multiple references to the same
1221 * instance in a cafile_entry */
1222 if (link->ckch_inst == ckch_inst) {
1223 return 1;
1224 }
1225 }
1226
1227 new_link = calloc(1, sizeof(*new_link));
1228 if (new_link) {
1229 struct ckch_inst_link_ref *new_link_ref = calloc(1, sizeof(*new_link_ref));
1230 if (!new_link_ref) {
1231 free(new_link);
1232 return 1;
1233 }
1234
1235 new_link->ckch_inst = ckch_inst;
1236 new_link_ref->link = new_link;
1237 LIST_INIT(&new_link->list);
1238 LIST_INIT(&new_link_ref->list);
1239
1240 LIST_APPEND(&cafile_entry->ckch_inst_link, &new_link->list);
1241 LIST_APPEND(&ckch_inst->cafile_link_refs, &new_link_ref->list);
1242 }
1243
1244 return 0;
1245}
1246
1247
1248/*
1249 * Link a CA file tree entry to the ckch instance that uses it.
1250 * To determine if and which CA file tree entries need to be linked to the
1251 * instance, we follow the same logic performed in ssl_sock_prepare_ctx when
1252 * processing the verify option.
1253 * This function works for a frontend as well as for a backend, depending on the
1254 * configuration parameters given (bind_conf or server).
1255 */
1256void ckch_inst_add_cafile_link(struct ckch_inst *ckch_inst, struct bind_conf *bind_conf,
1257 struct ssl_bind_conf *ssl_conf, const struct server *srv)
1258{
1259 int verify = SSL_VERIFY_NONE;
1260
1261 if (srv) {
1262
1263 if (global.ssl_server_verify == SSL_SERVER_VERIFY_REQUIRED)
1264 verify = SSL_VERIFY_PEER;
1265 switch (srv->ssl_ctx.verify) {
1266 case SSL_SOCK_VERIFY_NONE:
1267 verify = SSL_VERIFY_NONE;
1268 break;
1269 case SSL_SOCK_VERIFY_REQUIRED:
1270 verify = SSL_VERIFY_PEER;
1271 break;
1272 }
1273 }
1274 else {
1275 switch ((ssl_conf && ssl_conf->verify) ? ssl_conf->verify : bind_conf->ssl_conf.verify) {
1276 case SSL_SOCK_VERIFY_NONE:
1277 verify = SSL_VERIFY_NONE;
1278 break;
1279 case SSL_SOCK_VERIFY_OPTIONAL:
1280 verify = SSL_VERIFY_PEER;
1281 break;
1282 case SSL_SOCK_VERIFY_REQUIRED:
1283 verify = SSL_VERIFY_PEER|SSL_VERIFY_FAIL_IF_NO_PEER_CERT;
1284 break;
1285 }
1286 }
1287
1288 if (verify & SSL_VERIFY_PEER) {
1289 struct cafile_entry *ca_file_entry = NULL;
1290 struct cafile_entry *ca_verify_file_entry = NULL;
1291 if (srv) {
1292 if (srv->ssl_ctx.ca_file) {
1293 ca_file_entry = ssl_store_get_cafile_entry(srv->ssl_ctx.ca_file, 0);
1294
1295 }
1296 }
1297 else {
1298 char *ca_file = (ssl_conf && ssl_conf->ca_file) ? ssl_conf->ca_file : bind_conf->ssl_conf.ca_file;
1299 char *ca_verify_file = (ssl_conf && ssl_conf->ca_verify_file) ? ssl_conf->ca_verify_file : bind_conf->ssl_conf.ca_verify_file;
1300
1301 if (ca_file)
1302 ca_file_entry = ssl_store_get_cafile_entry(ca_file, 0);
1303 if (ca_verify_file)
1304 ca_verify_file_entry = ssl_store_get_cafile_entry(ca_verify_file, 0);
1305 }
1306
1307 if (ca_file_entry) {
1308 /* If we have a ckch instance that is not already in the
1309 * cafile_entry's list, add it to it. */
1310 if (do_chain_inst_and_cafile(ca_file_entry, ckch_inst))
1311 return;
1312
1313 }
1314 if (ca_verify_file_entry && (ca_file_entry != ca_verify_file_entry)) {
1315 /* If we have a ckch instance that is not already in the
1316 * cafile_entry's list, add it to it. */
1317 if (do_chain_inst_and_cafile(ca_verify_file_entry, ckch_inst))
1318 return;
1319 }
1320 }
1321}
1322
William Lallemandda8584c2020-05-14 10:14:37 +02001323
1324
1325
1326/* IO handler of the details "show ssl cert <filename>" */
1327static int cli_io_handler_show_cert_detail(struct appctx *appctx)
1328{
1329 struct stream_interface *si = appctx->owner;
1330 struct ckch_store *ckchs = appctx->ctx.cli.p0;
1331 struct buffer *out = alloc_trash_chunk();
1332 struct buffer *tmp = alloc_trash_chunk();
1333 X509_NAME *name = NULL;
1334 STACK_OF(X509) *chain;
1335 unsigned int len = 0;
1336 int write = -1;
1337 BIO *bio = NULL;
1338 int i;
1339
1340 if (!tmp || !out)
1341 goto end_no_putchk;
1342
William Lallemand5685ccf2020-09-16 16:12:25 +02001343 chunk_appendf(out, "Filename: ");
1344 if (ckchs == ckchs_transaction.new_ckchs)
1345 chunk_appendf(out, "*");
1346 chunk_appendf(out, "%s\n", ckchs->path);
William Lallemandda8584c2020-05-14 10:14:37 +02001347
William Lallemand5685ccf2020-09-16 16:12:25 +02001348 chunk_appendf(out, "Status: ");
1349 if (ckchs->ckch->cert == NULL)
1350 chunk_appendf(out, "Empty\n");
1351 else if (LIST_ISEMPTY(&ckchs->ckch_inst))
1352 chunk_appendf(out, "Unused\n");
1353 else
1354 chunk_appendf(out, "Used\n");
William Lallemandda8584c2020-05-14 10:14:37 +02001355
William Lallemand5685ccf2020-09-16 16:12:25 +02001356 if (ckchs->ckch->cert == NULL)
1357 goto end;
William Lallemandda8584c2020-05-14 10:14:37 +02001358
William Lallemand5685ccf2020-09-16 16:12:25 +02001359 chain = ckchs->ckch->chain;
1360 if (chain == NULL) {
1361 struct issuer_chain *issuer;
1362 issuer = ssl_get0_issuer_chain(ckchs->ckch->cert);
1363 if (issuer) {
1364 chain = issuer->chain;
1365 chunk_appendf(out, "Chain Filename: ");
1366 chunk_appendf(out, "%s\n", issuer->path);
William Lallemandda8584c2020-05-14 10:14:37 +02001367 }
William Lallemand5685ccf2020-09-16 16:12:25 +02001368 }
1369 chunk_appendf(out, "Serial: ");
1370 if (ssl_sock_get_serial(ckchs->ckch->cert, tmp) == -1)
1371 goto end;
1372 dump_binary(out, tmp->area, tmp->data);
1373 chunk_appendf(out, "\n");
William Lallemandda8584c2020-05-14 10:14:37 +02001374
William Lallemand5685ccf2020-09-16 16:12:25 +02001375 chunk_appendf(out, "notBefore: ");
1376 chunk_reset(tmp);
1377 if ((bio = BIO_new(BIO_s_mem())) == NULL)
1378 goto end;
1379 if (ASN1_TIME_print(bio, X509_getm_notBefore(ckchs->ckch->cert)) == 0)
1380 goto end;
1381 write = BIO_read(bio, tmp->area, tmp->size-1);
1382 tmp->area[write] = '\0';
1383 BIO_free(bio);
1384 bio = NULL;
1385 chunk_appendf(out, "%s\n", tmp->area);
William Lallemandda8584c2020-05-14 10:14:37 +02001386
William Lallemand5685ccf2020-09-16 16:12:25 +02001387 chunk_appendf(out, "notAfter: ");
1388 chunk_reset(tmp);
1389 if ((bio = BIO_new(BIO_s_mem())) == NULL)
1390 goto end;
1391 if (ASN1_TIME_print(bio, X509_getm_notAfter(ckchs->ckch->cert)) == 0)
1392 goto end;
1393 if ((write = BIO_read(bio, tmp->area, tmp->size-1)) <= 0)
1394 goto end;
1395 tmp->area[write] = '\0';
1396 BIO_free(bio);
1397 bio = NULL;
1398 chunk_appendf(out, "%s\n", tmp->area);
William Lallemandda8584c2020-05-14 10:14:37 +02001399
1400#ifdef SSL_CTRL_SET_TLSEXT_HOSTNAME
William Lallemand5685ccf2020-09-16 16:12:25 +02001401 chunk_appendf(out, "Subject Alternative Name: ");
1402 if (ssl_sock_get_san_oneline(ckchs->ckch->cert, out) == -1)
1403 goto end;
1404 *(out->area + out->data) = '\0';
1405 chunk_appendf(out, "\n");
William Lallemandda8584c2020-05-14 10:14:37 +02001406#endif
William Lallemand5685ccf2020-09-16 16:12:25 +02001407 chunk_reset(tmp);
1408 chunk_appendf(out, "Algorithm: ");
1409 if (cert_get_pkey_algo(ckchs->ckch->cert, tmp) == 0)
1410 goto end;
1411 chunk_appendf(out, "%s\n", tmp->area);
William Lallemandda8584c2020-05-14 10:14:37 +02001412
William Lallemand5685ccf2020-09-16 16:12:25 +02001413 chunk_reset(tmp);
1414 chunk_appendf(out, "SHA1 FingerPrint: ");
1415 if (X509_digest(ckchs->ckch->cert, EVP_sha1(), (unsigned char *) tmp->area, &len) == 0)
1416 goto end;
1417 tmp->data = len;
1418 dump_binary(out, tmp->area, tmp->data);
1419 chunk_appendf(out, "\n");
William Lallemandda8584c2020-05-14 10:14:37 +02001420
William Lallemand5685ccf2020-09-16 16:12:25 +02001421 chunk_appendf(out, "Subject: ");
1422 if ((name = X509_get_subject_name(ckchs->ckch->cert)) == NULL)
1423 goto end;
1424 if ((ssl_sock_get_dn_oneline(name, tmp)) == -1)
1425 goto end;
1426 *(tmp->area + tmp->data) = '\0';
1427 chunk_appendf(out, "%s\n", tmp->area);
1428
1429 chunk_appendf(out, "Issuer: ");
1430 if ((name = X509_get_issuer_name(ckchs->ckch->cert)) == NULL)
1431 goto end;
1432 if ((ssl_sock_get_dn_oneline(name, tmp)) == -1)
1433 goto end;
1434 *(tmp->area + tmp->data) = '\0';
1435 chunk_appendf(out, "%s\n", tmp->area);
1436
1437 /* Displays subject of each certificate in the chain */
1438 for (i = 0; i < sk_X509_num(chain); i++) {
1439 X509 *ca = sk_X509_value(chain, i);
1440
1441 chunk_appendf(out, "Chain Subject: ");
1442 if ((name = X509_get_subject_name(ca)) == NULL)
William Lallemandda8584c2020-05-14 10:14:37 +02001443 goto end;
1444 if ((ssl_sock_get_dn_oneline(name, tmp)) == -1)
1445 goto end;
1446 *(tmp->area + tmp->data) = '\0';
1447 chunk_appendf(out, "%s\n", tmp->area);
1448
William Lallemand5685ccf2020-09-16 16:12:25 +02001449 chunk_appendf(out, "Chain Issuer: ");
1450 if ((name = X509_get_issuer_name(ca)) == NULL)
William Lallemandda8584c2020-05-14 10:14:37 +02001451 goto end;
1452 if ((ssl_sock_get_dn_oneline(name, tmp)) == -1)
1453 goto end;
1454 *(tmp->area + tmp->data) = '\0';
1455 chunk_appendf(out, "%s\n", tmp->area);
William Lallemandda8584c2020-05-14 10:14:37 +02001456 }
1457
1458end:
1459 if (ci_putchk(si_ic(si), out) == -1) {
1460 si_rx_room_blk(si);
1461 goto yield;
1462 }
1463
1464end_no_putchk:
1465 if (bio)
1466 BIO_free(bio);
1467 free_trash_chunk(tmp);
1468 free_trash_chunk(out);
1469 return 1;
1470yield:
1471 free_trash_chunk(tmp);
1472 free_trash_chunk(out);
1473 return 0; /* should come back */
1474}
1475
1476/* parsing function for 'show ssl cert [certfile]' */
1477static int cli_parse_show_cert(char **args, char *payload, struct appctx *appctx, void *private)
1478{
1479 struct ckch_store *ckchs;
1480
1481 if (!cli_has_level(appctx, ACCESS_LVL_OPER))
1482 return cli_err(appctx, "Can't allocate memory!\n");
1483
1484 /* The operations on the CKCH architecture are locked so we can
1485 * manipulate ckch_store and ckch_inst */
1486 if (HA_SPIN_TRYLOCK(CKCH_LOCK, &ckch_lock))
1487 return cli_err(appctx, "Can't show!\nOperations on certificates are currently locked!\n");
1488
1489 /* check if there is a certificate to lookup */
1490 if (*args[3]) {
1491 if (*args[3] == '*') {
1492 if (!ckchs_transaction.new_ckchs)
1493 goto error;
1494
1495 ckchs = ckchs_transaction.new_ckchs;
1496
Tim Duesterhuse5ff1412021-01-02 22:31:53 +01001497 if (strcmp(args[3] + 1, ckchs->path) != 0)
William Lallemandda8584c2020-05-14 10:14:37 +02001498 goto error;
1499
1500 } else {
1501 if ((ckchs = ckchs_lookup(args[3])) == NULL)
1502 goto error;
1503
1504 }
1505
William Lallemandda8584c2020-05-14 10:14:37 +02001506 appctx->ctx.cli.p0 = ckchs;
1507 /* use the IO handler that shows details */
1508 appctx->io_handler = cli_io_handler_show_cert_detail;
1509 }
1510
1511 return 0;
1512
1513error:
1514 HA_SPIN_UNLOCK(CKCH_LOCK, &ckch_lock);
1515 return cli_err(appctx, "Can't display the certificate: Not found or the certificate is a bundle!\n");
1516}
1517
1518/* release function of the `set ssl cert' command, free things and unlock the spinlock */
1519static void cli_release_commit_cert(struct appctx *appctx)
1520{
1521 struct ckch_store *new_ckchs;
1522
1523 HA_SPIN_UNLOCK(CKCH_LOCK, &ckch_lock);
1524
1525 if (appctx->st2 != SETCERT_ST_FIN) {
1526 /* free every new sni_ctx and the new store, which are not in the trees so no spinlock there */
1527 new_ckchs = appctx->ctx.ssl.new_ckchs;
1528
1529 /* if the allocation failed, we need to free everything from the temporary list */
1530 ckch_store_free(new_ckchs);
1531 }
1532}
1533
1534/*
1535 * This function tries to create the new ckch_inst and their SNIs
1536 */
1537static int cli_io_handler_commit_cert(struct appctx *appctx)
1538{
1539 struct stream_interface *si = appctx->owner;
1540 int y = 0;
1541 char *err = NULL;
1542 int errcode = 0;
Remi Tricot-Le Breton43899ec2021-04-21 15:32:46 +02001543 int retval = 0;
William Lallemandda8584c2020-05-14 10:14:37 +02001544 struct ckch_store *old_ckchs, *new_ckchs = NULL;
1545 struct ckch_inst *ckchi, *ckchis;
1546 struct buffer *trash = alloc_trash_chunk();
1547 struct sni_ctx *sc0, *sc0s;
1548 struct crtlist_entry *entry;
1549
1550 if (trash == NULL)
1551 goto error;
1552
1553 if (unlikely(si_ic(si)->flags & (CF_WRITE_ERROR|CF_SHUTW)))
1554 goto error;
1555
1556 while (1) {
1557 switch (appctx->st2) {
1558 case SETCERT_ST_INIT:
1559 /* This state just print the update message */
1560 chunk_printf(trash, "Committing %s", ckchs_transaction.path);
1561 if (ci_putchk(si_ic(si), trash) == -1) {
1562 si_rx_room_blk(si);
1563 goto yield;
1564 }
1565 appctx->st2 = SETCERT_ST_GEN;
1566 /* fallthrough */
1567 case SETCERT_ST_GEN:
1568 /*
1569 * This state generates the ckch instances with their
1570 * sni_ctxs and SSL_CTX.
1571 *
1572 * Since the SSL_CTX generation can be CPU consumer, we
1573 * yield every 10 instances.
1574 */
1575
1576 old_ckchs = appctx->ctx.ssl.old_ckchs;
1577 new_ckchs = appctx->ctx.ssl.new_ckchs;
1578
1579 if (!new_ckchs)
1580 continue;
1581
1582 /* get the next ckchi to regenerate */
1583 ckchi = appctx->ctx.ssl.next_ckchi;
1584 /* we didn't start yet, set it to the first elem */
1585 if (ckchi == NULL)
1586 ckchi = LIST_ELEM(old_ckchs->ckch_inst.n, typeof(ckchi), by_ckchs);
1587
1588 /* walk through the old ckch_inst and creates new ckch_inst using the updated ckchs */
1589 list_for_each_entry_from(ckchi, &old_ckchs->ckch_inst, by_ckchs) {
1590 struct ckch_inst *new_inst;
1591 char **sni_filter = NULL;
1592 int fcount = 0;
1593
1594 /* it takes a lot of CPU to creates SSL_CTXs, so we yield every 10 CKCH instances */
1595 if (y >= 10) {
1596 /* save the next ckchi to compute */
1597 appctx->ctx.ssl.next_ckchi = ckchi;
1598 goto yield;
1599 }
1600
1601 if (ckchi->crtlist_entry) {
1602 sni_filter = ckchi->crtlist_entry->filters;
1603 fcount = ckchi->crtlist_entry->fcount;
1604 }
1605
Remi Tricot-Le Bretond817dc72021-01-25 17:19:43 +01001606 if (ckchi->is_server_instance)
William Lallemand795bd9b2021-01-26 11:27:42 +01001607 errcode |= ckch_inst_new_load_srv_store(new_ckchs->path, new_ckchs, &new_inst, &err);
Remi Tricot-Le Bretonf3eedfe2021-01-25 17:19:44 +01001608 else
1609 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 +02001610
1611 if (errcode & ERR_CODE)
1612 goto error;
1613
1614 /* if the previous ckchi was used as the default */
1615 if (ckchi->is_default)
1616 new_inst->is_default = 1;
1617
Remi Tricot-Le Bretonf3eedfe2021-01-25 17:19:44 +01001618 new_inst->is_server_instance = ckchi->is_server_instance;
1619 new_inst->server = ckchi->server;
1620 /* Create a new SSL_CTX and link it to the new instance. */
1621 if (new_inst->is_server_instance) {
Remi Tricot-Le Breton4458b972021-02-19 17:41:55 +01001622 retval = ssl_sock_prep_srv_ctx_and_inst(ckchi->server, new_inst->ctx, new_inst);
Remi Tricot-Le Breton43899ec2021-04-21 15:32:46 +02001623 if (retval)
Remi Tricot-Le Bretonf3eedfe2021-01-25 17:19:44 +01001624 goto error;
Remi Tricot-Le Bretonf3eedfe2021-01-25 17:19:44 +01001625 }
1626
William Lallemanda55685b2020-12-15 14:57:46 +01001627 /* create the link to the crtlist_entry */
1628 new_inst->crtlist_entry = ckchi->crtlist_entry;
1629
William Lallemandda8584c2020-05-14 10:14:37 +02001630 /* we need to initialize the SSL_CTX generated */
1631 /* this iterate on the newly generated SNIs in the new instance to prepare their SSL_CTX */
1632 list_for_each_entry_safe(sc0, sc0s, &new_inst->sni_ctx, by_ckch_inst) {
1633 if (!sc0->order) { /* we initialized only the first SSL_CTX because it's the same in the other sni_ctx's */
Remi Tricot-Le Breton4458b972021-02-19 17:41:55 +01001634 errcode |= ssl_sock_prep_ctx_and_inst(ckchi->bind_conf, ckchi->ssl_conf, sc0->ctx, sc0->ckch_inst, &err);
William Lallemandda8584c2020-05-14 10:14:37 +02001635 if (errcode & ERR_CODE)
1636 goto error;
1637 }
1638 }
1639
1640
1641 /* display one dot per new instance */
1642 chunk_appendf(trash, ".");
1643 /* link the new ckch_inst to the duplicate */
Willy Tarreau2b718102021-04-21 07:32:39 +02001644 LIST_APPEND(&new_ckchs->ckch_inst, &new_inst->by_ckchs);
William Lallemandda8584c2020-05-14 10:14:37 +02001645 y++;
1646 }
1647 appctx->st2 = SETCERT_ST_INSERT;
1648 /* fallthrough */
1649 case SETCERT_ST_INSERT:
1650 /* The generation is finished, we can insert everything */
1651
1652 old_ckchs = appctx->ctx.ssl.old_ckchs;
1653 new_ckchs = appctx->ctx.ssl.new_ckchs;
1654
1655 if (!new_ckchs)
1656 continue;
1657
1658 /* get the list of crtlist_entry in the old store, and update the pointers to the store */
1659 LIST_SPLICE(&new_ckchs->crtlist_entry, &old_ckchs->crtlist_entry);
1660 list_for_each_entry(entry, &new_ckchs->crtlist_entry, by_ckch_store) {
1661 ebpt_delete(&entry->node);
1662 /* change the ptr and reinsert the node */
1663 entry->node.key = new_ckchs;
1664 ebpt_insert(&entry->crtlist->entries, &entry->node);
1665 }
1666
William Lallemanda55685b2020-12-15 14:57:46 +01001667 /* insert the new ckch_insts in the crtlist_entry */
1668 list_for_each_entry(ckchi, &new_ckchs->ckch_inst, by_ckchs) {
1669 if (ckchi->crtlist_entry)
Willy Tarreau2b718102021-04-21 07:32:39 +02001670 LIST_INSERT(&ckchi->crtlist_entry->ckch_inst, &ckchi->by_crtlist_entry);
William Lallemanda55685b2020-12-15 14:57:46 +01001671 }
1672
William Lallemandda8584c2020-05-14 10:14:37 +02001673 /* First, we insert every new SNIs in the trees, also replace the default_ctx */
1674 list_for_each_entry_safe(ckchi, ckchis, &new_ckchs->ckch_inst, by_ckchs) {
Remi Tricot-Le Bretonf3eedfe2021-01-25 17:19:44 +01001675 /* The bind_conf will be null on server ckch_instances. */
1676 if (ckchi->is_server_instance) {
William Lallemande0de0a62021-02-03 18:51:01 +01001677 int i;
William Lallemand3ce6eed2021-02-08 10:43:44 +01001678 /* a lock is needed here since we have to free the SSL cache */
1679 HA_RWLOCK_WRLOCK(SSL_SERVER_LOCK, &ckchi->server->ssl_ctx.lock);
William Lallemand1dedb0a2021-01-26 10:18:57 +01001680 /* free the server current SSL_CTX */
1681 SSL_CTX_free(ckchi->server->ssl_ctx.ctx);
Remi Tricot-Le Bretonf3eedfe2021-01-25 17:19:44 +01001682 /* Actual ssl context update */
William Lallemand1dedb0a2021-01-26 10:18:57 +01001683 SSL_CTX_up_ref(ckchi->ctx);
Remi Tricot-Le Bretonf3eedfe2021-01-25 17:19:44 +01001684 ckchi->server->ssl_ctx.ctx = ckchi->ctx;
William Lallemand1dedb0a2021-01-26 10:18:57 +01001685 ckchi->server->ssl_ctx.inst = ckchi;
Remi Tricot-Le Bretonf3eedfe2021-01-25 17:19:44 +01001686
William Lallemande0de0a62021-02-03 18:51:01 +01001687 /* flush the session cache of the server */
Willy Tarreau61cfdf42021-02-20 10:46:51 +01001688 for (i = 0; i < global.nbthread; i++)
1689 ha_free(&ckchi->server->ssl_ctx.reused_sess[i].ptr);
1690
William Lallemand3ce6eed2021-02-08 10:43:44 +01001691 HA_RWLOCK_WRUNLOCK(SSL_SERVER_LOCK, &ckchi->server->ssl_ctx.lock);
William Lallemande0de0a62021-02-03 18:51:01 +01001692
William Lallemand1dedb0a2021-01-26 10:18:57 +01001693 } else {
Remi Tricot-Le Bretonf3eedfe2021-01-25 17:19:44 +01001694 HA_RWLOCK_WRLOCK(SNI_LOCK, &ckchi->bind_conf->sni_lock);
1695 ssl_sock_load_cert_sni(ckchi, ckchi->bind_conf);
1696 HA_RWLOCK_WRUNLOCK(SNI_LOCK, &ckchi->bind_conf->sni_lock);
1697 }
William Lallemandda8584c2020-05-14 10:14:37 +02001698 }
1699
1700 /* delete the old sni_ctx, the old ckch_insts and the ckch_store */
1701 list_for_each_entry_safe(ckchi, ckchis, &old_ckchs->ckch_inst, by_ckchs) {
William Lallemandda8584c2020-05-14 10:14:37 +02001702
William Lallemand1dedb0a2021-01-26 10:18:57 +01001703 if (ckchi->is_server_instance) {
1704 /* no lock for servers */
1705 ckch_inst_free(ckchi);
1706 } else {
1707 struct bind_conf __maybe_unused *bind_conf = ckchi->bind_conf;
1708
1709 HA_RWLOCK_WRLOCK(SNI_LOCK, &bind_conf->sni_lock);
1710 ckch_inst_free(ckchi);
1711 HA_RWLOCK_WRUNLOCK(SNI_LOCK, &bind_conf->sni_lock);
1712 }
William Lallemandda8584c2020-05-14 10:14:37 +02001713 }
1714
1715 /* Replace the old ckchs by the new one */
1716 ckch_store_free(old_ckchs);
1717 ebst_insert(&ckchs_tree, &new_ckchs->node);
1718 appctx->st2 = SETCERT_ST_FIN;
1719 /* fallthrough */
1720 case SETCERT_ST_FIN:
1721 /* we achieved the transaction, we can set everything to NULL */
Willy Tarreau61cfdf42021-02-20 10:46:51 +01001722 ha_free(&ckchs_transaction.path);
William Lallemandda8584c2020-05-14 10:14:37 +02001723 ckchs_transaction.new_ckchs = NULL;
1724 ckchs_transaction.old_ckchs = NULL;
1725 goto end;
1726 }
1727 }
1728end:
1729
1730 chunk_appendf(trash, "\n");
1731 if (errcode & ERR_WARN)
1732 chunk_appendf(trash, "%s", err);
1733 chunk_appendf(trash, "Success!\n");
1734 if (ci_putchk(si_ic(si), trash) == -1)
1735 si_rx_room_blk(si);
1736 free_trash_chunk(trash);
1737 /* success: call the release function and don't come back */
1738 return 1;
1739yield:
1740 /* store the state */
1741 if (ci_putchk(si_ic(si), trash) == -1)
1742 si_rx_room_blk(si);
1743 free_trash_chunk(trash);
1744 si_rx_endp_more(si); /* let's come back later */
1745 return 0; /* should come back */
1746
1747error:
1748 /* spin unlock and free are done in the release function */
1749 if (trash) {
1750 chunk_appendf(trash, "\n%sFailed!\n", err);
1751 if (ci_putchk(si_ic(si), trash) == -1)
1752 si_rx_room_blk(si);
1753 free_trash_chunk(trash);
1754 }
1755 /* error: call the release function and don't come back */
1756 return 1;
1757}
1758
1759/*
1760 * Parsing function of 'commit ssl cert'
1761 */
1762static int cli_parse_commit_cert(char **args, char *payload, struct appctx *appctx, void *private)
1763{
1764 char *err = NULL;
1765
1766 if (!cli_has_level(appctx, ACCESS_LVL_ADMIN))
1767 return 1;
1768
1769 if (!*args[3])
1770 return cli_err(appctx, "'commit ssl cert expects a filename\n");
1771
1772 /* The operations on the CKCH architecture are locked so we can
1773 * manipulate ckch_store and ckch_inst */
1774 if (HA_SPIN_TRYLOCK(CKCH_LOCK, &ckch_lock))
1775 return cli_err(appctx, "Can't commit the certificate!\nOperations on certificates are currently locked!\n");
1776
1777 if (!ckchs_transaction.path) {
1778 memprintf(&err, "No ongoing transaction! !\n");
1779 goto error;
1780 }
1781
1782 if (strcmp(ckchs_transaction.path, args[3]) != 0) {
1783 memprintf(&err, "The ongoing transaction is about '%s' but you are trying to set '%s'\n", ckchs_transaction.path, args[3]);
1784 goto error;
1785 }
1786
William Lallemand5685ccf2020-09-16 16:12:25 +02001787 /* if a certificate is here, a private key must be here too */
1788 if (ckchs_transaction.new_ckchs->ckch->cert && !ckchs_transaction.new_ckchs->ckch->key) {
1789 memprintf(&err, "The transaction must contain at least a certificate and a private key!\n");
1790 goto error;
1791 }
William Lallemanda9419522020-06-24 16:26:41 +02001792
William Lallemand5685ccf2020-09-16 16:12:25 +02001793 if (!X509_check_private_key(ckchs_transaction.new_ckchs->ckch->cert, ckchs_transaction.new_ckchs->ckch->key)) {
1794 memprintf(&err, "inconsistencies between private key and certificate loaded '%s'.\n", ckchs_transaction.path);
1795 goto error;
William Lallemandda8584c2020-05-14 10:14:37 +02001796 }
1797
1798 /* init the appctx structure */
1799 appctx->st2 = SETCERT_ST_INIT;
1800 appctx->ctx.ssl.next_ckchi = NULL;
1801 appctx->ctx.ssl.new_ckchs = ckchs_transaction.new_ckchs;
1802 appctx->ctx.ssl.old_ckchs = ckchs_transaction.old_ckchs;
1803
1804 /* we don't unlock there, it will be unlock after the IO handler, in the release handler */
1805 return 0;
1806
1807error:
1808
1809 HA_SPIN_UNLOCK(CKCH_LOCK, &ckch_lock);
1810 err = memprintf(&err, "%sCan't commit %s!\n", err ? err : "", args[3]);
1811
1812 return cli_dynerr(appctx, err);
1813}
1814
1815
1816
1817
1818/*
1819 * Parsing function of `set ssl cert`, it updates or creates a temporary ckch.
1820 */
1821static int cli_parse_set_cert(char **args, char *payload, struct appctx *appctx, void *private)
1822{
1823 struct ckch_store *new_ckchs = NULL;
1824 struct ckch_store *old_ckchs = NULL;
1825 char *err = NULL;
1826 int i;
William Lallemandda8584c2020-05-14 10:14:37 +02001827 int errcode = 0;
1828 char *end;
1829 int type = CERT_TYPE_PEM;
1830 struct cert_key_and_chain *ckch;
1831 struct buffer *buf;
1832
1833 if (!cli_has_level(appctx, ACCESS_LVL_ADMIN))
1834 return 1;
1835
William Lallemandda8584c2020-05-14 10:14:37 +02001836 if (!*args[3] || !payload)
1837 return cli_err(appctx, "'set ssl cert expects a filename and a certificate as a payload\n");
1838
1839 /* The operations on the CKCH architecture are locked so we can
1840 * manipulate ckch_store and ckch_inst */
1841 if (HA_SPIN_TRYLOCK(CKCH_LOCK, &ckch_lock))
1842 return cli_err(appctx, "Can't update the certificate!\nOperations on certificates are currently locked!\n");
1843
William Lallemand5ba80d62021-05-04 16:17:27 +02001844 if ((buf = alloc_trash_chunk()) == NULL) {
1845 memprintf(&err, "%sCan't allocate memory\n", err ? err : "");
1846 errcode |= ERR_ALERT | ERR_FATAL;
1847 goto end;
1848 }
William Lallemande5ff4ad2020-06-08 09:40:37 +02001849
William Lallemandda8584c2020-05-14 10:14:37 +02001850 if (!chunk_strcpy(buf, args[3])) {
1851 memprintf(&err, "%sCan't allocate memory\n", err ? err : "");
1852 errcode |= ERR_ALERT | ERR_FATAL;
1853 goto end;
1854 }
1855
1856 /* check which type of file we want to update */
1857 for (i = 0; cert_exts[i].type < CERT_TYPE_MAX; i++) {
1858 end = strrchr(buf->area, '.');
Tim Duesterhuse5ff1412021-01-02 22:31:53 +01001859 if (end && *cert_exts[i].ext && (strcmp(end + 1, cert_exts[i].ext) == 0)) {
William Lallemandda8584c2020-05-14 10:14:37 +02001860 *end = '\0';
William Lallemand089c1382020-10-23 17:35:12 +02001861 buf->data = strlen(buf->area);
William Lallemandda8584c2020-05-14 10:14:37 +02001862 type = cert_exts[i].type;
1863 break;
1864 }
1865 }
1866
1867 appctx->ctx.ssl.old_ckchs = NULL;
1868 appctx->ctx.ssl.new_ckchs = NULL;
1869
1870 /* if there is an ongoing transaction */
1871 if (ckchs_transaction.path) {
William Lallemandda8584c2020-05-14 10:14:37 +02001872 /* if there is an ongoing transaction, check if this is the same file */
1873 if (strcmp(ckchs_transaction.path, buf->area) != 0) {
William Lallemand089c1382020-10-23 17:35:12 +02001874 /* we didn't find the transaction, must try more cases below */
1875
1876 /* if the del-ext option is activated we should try to take a look at a ".crt" too. */
1877 if (type != CERT_TYPE_PEM && global_ssl.extra_files_noext) {
1878 if (!chunk_strcat(buf, ".crt")) {
1879 memprintf(&err, "%sCan't allocate memory\n", err ? err : "");
1880 errcode |= ERR_ALERT | ERR_FATAL;
1881 goto end;
1882 }
1883
1884 if (strcmp(ckchs_transaction.path, buf->area) != 0) {
1885 /* remove .crt of the error message */
1886 *(b_orig(buf) + b_data(buf) + strlen(".crt")) = '\0';
1887 b_sub(buf, strlen(".crt"));
1888
1889 memprintf(&err, "The ongoing transaction is about '%s' but you are trying to set '%s'\n", ckchs_transaction.path, buf->area);
1890 errcode |= ERR_ALERT | ERR_FATAL;
1891 goto end;
1892 }
1893 }
William Lallemandda8584c2020-05-14 10:14:37 +02001894 }
1895
1896 appctx->ctx.ssl.old_ckchs = ckchs_transaction.new_ckchs;
1897
1898 } else {
William Lallemandda8584c2020-05-14 10:14:37 +02001899
William Lallemand95fefa12020-09-09 12:01:33 +02001900 /* lookup for the certificate in the tree */
1901 appctx->ctx.ssl.old_ckchs = ckchs_lookup(buf->area);
William Lallemand089c1382020-10-23 17:35:12 +02001902
1903 if (!appctx->ctx.ssl.old_ckchs) {
1904 /* if the del-ext option is activated we should try to take a look at a ".crt" too. */
1905 if (type != CERT_TYPE_PEM && global_ssl.extra_files_noext) {
1906 if (!chunk_strcat(buf, ".crt")) {
1907 memprintf(&err, "%sCan't allocate memory\n", err ? err : "");
1908 errcode |= ERR_ALERT | ERR_FATAL;
1909 goto end;
1910 }
1911 appctx->ctx.ssl.old_ckchs = ckchs_lookup(buf->area);
1912 }
1913 }
William Lallemandda8584c2020-05-14 10:14:37 +02001914 }
1915
1916 if (!appctx->ctx.ssl.old_ckchs) {
1917 memprintf(&err, "%sCan't replace a certificate which is not referenced by the configuration!\n",
1918 err ? err : "");
1919 errcode |= ERR_ALERT | ERR_FATAL;
1920 goto end;
1921 }
1922
1923 if (!appctx->ctx.ssl.path) {
1924 /* this is a new transaction, set the path of the transaction */
1925 appctx->ctx.ssl.path = strdup(appctx->ctx.ssl.old_ckchs->path);
1926 if (!appctx->ctx.ssl.path) {
1927 memprintf(&err, "%sCan't allocate memory\n", err ? err : "");
1928 errcode |= ERR_ALERT | ERR_FATAL;
1929 goto end;
1930 }
1931 }
1932
1933 old_ckchs = appctx->ctx.ssl.old_ckchs;
1934
1935 /* duplicate the ckch store */
1936 new_ckchs = ckchs_dup(old_ckchs);
1937 if (!new_ckchs) {
1938 memprintf(&err, "%sCannot allocate memory!\n",
1939 err ? err : "");
1940 errcode |= ERR_ALERT | ERR_FATAL;
1941 goto end;
1942 }
1943
William Lallemand95fefa12020-09-09 12:01:33 +02001944 ckch = new_ckchs->ckch;
William Lallemandda8584c2020-05-14 10:14:37 +02001945
1946 /* appply the change on the duplicate */
1947 if (cert_exts[type].load(buf->area, payload, ckch, &err) != 0) {
1948 memprintf(&err, "%sCan't load the payload\n", err ? err : "");
1949 errcode |= ERR_ALERT | ERR_FATAL;
1950 goto end;
1951 }
1952
1953 appctx->ctx.ssl.new_ckchs = new_ckchs;
1954
1955 /* we succeed, we can save the ckchs in the transaction */
1956
1957 /* if there wasn't a transaction, update the old ckchs */
1958 if (!ckchs_transaction.old_ckchs) {
1959 ckchs_transaction.old_ckchs = appctx->ctx.ssl.old_ckchs;
1960 ckchs_transaction.path = appctx->ctx.ssl.path;
1961 err = memprintf(&err, "Transaction created for certificate %s!\n", ckchs_transaction.path);
1962 } else {
1963 err = memprintf(&err, "Transaction updated for certificate %s!\n", ckchs_transaction.path);
1964
1965 }
1966
1967 /* free the previous ckchs if there was a transaction */
1968 ckch_store_free(ckchs_transaction.new_ckchs);
1969
1970 ckchs_transaction.new_ckchs = appctx->ctx.ssl.new_ckchs;
1971
1972
1973 /* creates the SNI ctxs later in the IO handler */
1974
1975end:
1976 free_trash_chunk(buf);
1977
1978 if (errcode & ERR_CODE) {
1979
1980 ckch_store_free(appctx->ctx.ssl.new_ckchs);
1981 appctx->ctx.ssl.new_ckchs = NULL;
1982
1983 appctx->ctx.ssl.old_ckchs = NULL;
1984
Willy Tarreau61cfdf42021-02-20 10:46:51 +01001985 ha_free(&appctx->ctx.ssl.path);
William Lallemandda8584c2020-05-14 10:14:37 +02001986
1987 HA_SPIN_UNLOCK(CKCH_LOCK, &ckch_lock);
1988 return cli_dynerr(appctx, memprintf(&err, "%sCan't update %s!\n", err ? err : "", args[3]));
1989 } else {
1990
1991 HA_SPIN_UNLOCK(CKCH_LOCK, &ckch_lock);
1992 return cli_dynmsg(appctx, LOG_NOTICE, err);
1993 }
1994 /* TODO: handle the ERR_WARN which are not handled because of the io_handler */
1995}
1996
1997/* parsing function of 'abort ssl cert' */
1998static int cli_parse_abort_cert(char **args, char *payload, struct appctx *appctx, void *private)
1999{
2000 char *err = NULL;
2001
2002 if (!cli_has_level(appctx, ACCESS_LVL_ADMIN))
2003 return 1;
2004
2005 if (!*args[3])
2006 return cli_err(appctx, "'abort ssl cert' expects a filename\n");
2007
2008 /* The operations on the CKCH architecture are locked so we can
2009 * manipulate ckch_store and ckch_inst */
2010 if (HA_SPIN_TRYLOCK(CKCH_LOCK, &ckch_lock))
2011 return cli_err(appctx, "Can't abort!\nOperations on certificates are currently locked!\n");
2012
2013 if (!ckchs_transaction.path) {
2014 memprintf(&err, "No ongoing transaction!\n");
2015 goto error;
2016 }
2017
2018 if (strcmp(ckchs_transaction.path, args[3]) != 0) {
2019 memprintf(&err, "The ongoing transaction is about '%s' but you are trying to abort a transaction for '%s'\n", ckchs_transaction.path, args[3]);
2020 goto error;
2021 }
2022
2023 /* Only free the ckchs there, because the SNI and instances were not generated yet */
2024 ckch_store_free(ckchs_transaction.new_ckchs);
2025 ckchs_transaction.new_ckchs = NULL;
William Lallemandda8584c2020-05-14 10:14:37 +02002026 ckchs_transaction.old_ckchs = NULL;
Willy Tarreau61cfdf42021-02-20 10:46:51 +01002027 ha_free(&ckchs_transaction.path);
William Lallemandda8584c2020-05-14 10:14:37 +02002028
2029 HA_SPIN_UNLOCK(CKCH_LOCK, &ckch_lock);
2030
2031 err = memprintf(&err, "Transaction aborted for certificate '%s'!\n", args[3]);
2032 return cli_dynmsg(appctx, LOG_NOTICE, err);
2033
2034error:
2035 HA_SPIN_UNLOCK(CKCH_LOCK, &ckch_lock);
2036
2037 return cli_dynerr(appctx, err);
2038}
2039
2040/* parsing function of 'new ssl cert' */
2041static int cli_parse_new_cert(char **args, char *payload, struct appctx *appctx, void *private)
2042{
2043 struct ckch_store *store;
2044 char *err = NULL;
2045 char *path;
2046
2047 if (!cli_has_level(appctx, ACCESS_LVL_ADMIN))
2048 return 1;
2049
2050 if (!*args[3])
2051 return cli_err(appctx, "'new ssl cert' expects a filename\n");
2052
2053 path = args[3];
2054
2055 /* The operations on the CKCH architecture are locked so we can
2056 * manipulate ckch_store and ckch_inst */
2057 if (HA_SPIN_TRYLOCK(CKCH_LOCK, &ckch_lock))
2058 return cli_err(appctx, "Can't create a certificate!\nOperations on certificates are currently locked!\n");
2059
2060 store = ckchs_lookup(path);
2061 if (store != NULL) {
2062 memprintf(&err, "Certificate '%s' already exists!\n", path);
2063 store = NULL; /* we don't want to free it */
2064 goto error;
2065 }
2066 /* we won't support multi-certificate bundle here */
William Lallemandbd8e6ed2020-09-16 16:08:08 +02002067 store = ckch_store_new(path);
William Lallemandda8584c2020-05-14 10:14:37 +02002068 if (!store) {
2069 memprintf(&err, "unable to allocate memory.\n");
2070 goto error;
2071 }
2072
2073 /* insert into the ckchs tree */
2074 ebst_insert(&ckchs_tree, &store->node);
2075 memprintf(&err, "New empty certificate store '%s'!\n", args[3]);
2076
2077 HA_SPIN_UNLOCK(CKCH_LOCK, &ckch_lock);
2078 return cli_dynmsg(appctx, LOG_NOTICE, err);
2079error:
2080 free(store);
2081 HA_SPIN_UNLOCK(CKCH_LOCK, &ckch_lock);
2082 return cli_dynerr(appctx, err);
2083}
2084
2085/* parsing function of 'del ssl cert' */
2086static int cli_parse_del_cert(char **args, char *payload, struct appctx *appctx, void *private)
2087{
2088 struct ckch_store *store;
2089 char *err = NULL;
2090 char *filename;
2091
2092 if (!cli_has_level(appctx, ACCESS_LVL_ADMIN))
2093 return 1;
2094
2095 if (!*args[3])
2096 return cli_err(appctx, "'del ssl cert' expects a certificate name\n");
2097
2098 if (HA_SPIN_TRYLOCK(CKCH_LOCK, &ckch_lock))
2099 return cli_err(appctx, "Can't delete the certificate!\nOperations on certificates are currently locked!\n");
2100
2101 filename = args[3];
2102
2103 store = ckchs_lookup(filename);
2104 if (store == NULL) {
2105 memprintf(&err, "certificate '%s' doesn't exist!\n", filename);
2106 goto error;
2107 }
2108 if (!LIST_ISEMPTY(&store->ckch_inst)) {
2109 memprintf(&err, "certificate '%s' in use, can't be deleted!\n", filename);
2110 goto error;
2111 }
2112
2113 ebmb_delete(&store->node);
2114 ckch_store_free(store);
2115
2116 memprintf(&err, "Certificate '%s' deleted!\n", filename);
2117
2118 HA_SPIN_UNLOCK(CKCH_LOCK, &ckch_lock);
2119 return cli_dynmsg(appctx, LOG_NOTICE, err);
2120
2121error:
2122 memprintf(&err, "Can't remove the certificate: %s\n", err ? err : "");
2123 HA_SPIN_UNLOCK(CKCH_LOCK, &ckch_lock);
2124 return cli_dynerr(appctx, err);
2125}
2126
William Lallemandee8530c2020-06-23 18:19:42 +02002127void ckch_deinit()
2128{
2129 struct eb_node *node, *next;
2130 struct ckch_store *store;
2131
2132 node = eb_first(&ckchs_tree);
2133 while (node) {
2134 next = eb_next(node);
2135 store = ebmb_entry(node, struct ckch_store, node);
2136 ckch_store_free(store);
2137 node = next;
2138 }
2139}
William Lallemandda8584c2020-05-14 10:14:37 +02002140
2141/* register cli keywords */
2142static struct cli_kw_list cli_kws = {{ },{
Willy Tarreaub205bfd2021-05-07 11:38:37 +02002143 { { "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 },
2144 { { "set", "ssl", "cert", NULL }, "set ssl cert <certfile> <payload> : replace a certificate file", cli_parse_set_cert, NULL, NULL },
2145 { { "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 },
2146 { { "abort", "ssl", "cert", NULL }, "abort ssl cert <certfile> : abort a transaction for a certificate file", cli_parse_abort_cert, NULL, NULL },
2147 { { "del", "ssl", "cert", NULL }, "del ssl cert <certfile> : delete an unused certificate file", cli_parse_del_cert, NULL, NULL },
2148 { { "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 +02002149 { { NULL }, NULL, NULL, NULL }
2150}};
2151
2152INITCALL1(STG_REGISTER, cli_register_kw, &cli_kws);
2153