blob: 31428d63b5ad13e22f93ebcefe0157b907782033 [file] [log] [blame]
William Lallemand6e9556b2020-05-12 17:52:44 +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 */
Willy Tarreaub2551052020-06-09 09:07:15 +020011#include <sys/stat.h>
12#include <sys/types.h>
William Lallemand6e9556b2020-05-12 17:52:44 +020013
Willy Tarreaub2551052020-06-09 09:07:15 +020014#include <dirent.h>
William Lallemand212e9932020-05-18 08:33:09 +020015#include <errno.h>
William Lallemand6e9556b2020-05-12 17:52:44 +020016#include <stdlib.h>
17#include <string.h>
Willy Tarreauaeed4a82020-06-04 22:01:04 +020018#include <syslog.h>
Willy Tarreaub2551052020-06-09 09:07:15 +020019
20#include <import/ebpttree.h>
21#include <import/ebsttree.h>
William Lallemand6e9556b2020-05-12 17:52:44 +020022
Willy Tarreaua2fcca02022-05-05 11:53:23 +020023#include <haproxy/applet.h>
Willy Tarreauf1d32c42020-06-04 21:07:02 +020024#include <haproxy/channel.h>
Willy Tarreau83487a82020-06-04 20:19:54 +020025#include <haproxy/cli.h>
Willy Tarreau8d366972020-05-27 16:10:29 +020026#include <haproxy/errors.h>
Willy Tarreau5edca2f2022-05-27 09:25:10 +020027#include <haproxy/sc_strm.h>
Willy Tarreau47d7f902020-06-04 14:25:47 +020028#include <haproxy/ssl_ckch.h>
Willy Tarreau52d88722020-06-04 14:29:23 +020029#include <haproxy/ssl_crtlist.h>
Willy Tarreau209108d2020-06-04 20:30:20 +020030#include <haproxy/ssl_sock.h>
Willy Tarreaucb086c62022-05-27 09:47:12 +020031#include <haproxy/stconn.h>
Willy Tarreau48fbcae2020-06-03 18:09:46 +020032#include <haproxy/tools.h>
William Lallemand6e9556b2020-05-12 17:52:44 +020033
Willy Tarreaua2fcca02022-05-05 11:53:23 +020034/* CLI context for "show ssl crt-list" or "dump ssl crt-list" */
35struct show_crtlist_ctx {
36 struct ebmb_node *crtlist_node; /* ebmb_node for the current crtlist */
37 struct crtlist_entry *entry; /* current entry */
38 int mode; /* 'd' for dump, 's' for show */
39};
William Lallemand6e9556b2020-05-12 17:52:44 +020040
Willy Tarreau6b6c3632022-05-05 13:43:49 +020041/* CLI context for "add ssl crt-list" */
42struct add_crtlist_ctx {
43 struct crtlist *crtlist;
44 struct crtlist_entry *entry;
45 struct bind_conf_list *bind_conf_node;
Christopher Fauletc642d7c2022-06-01 16:31:09 +020046 char *err;
Willy Tarreaufa11df52022-05-05 13:48:40 +020047 enum {
48 ADDCRT_ST_INIT = 0,
49 ADDCRT_ST_GEN,
50 ADDCRT_ST_INSERT,
Christopher Fauletc642d7c2022-06-01 16:31:09 +020051 ADDCRT_ST_SUCCESS,
52 ADDCRT_ST_ERROR,
Willy Tarreaufa11df52022-05-05 13:48:40 +020053 ADDCRT_ST_FIN,
54 } state;
Willy Tarreau6b6c3632022-05-05 13:43:49 +020055};
56
William Lallemand6e9556b2020-05-12 17:52:44 +020057/* release ssl bind conf */
58void ssl_sock_free_ssl_conf(struct ssl_bind_conf *conf)
59{
60 if (conf) {
61#if defined(OPENSSL_NPN_NEGOTIATED) && !defined(OPENSSL_NO_NEXTPROTONEG)
Willy Tarreau61cfdf42021-02-20 10:46:51 +010062 ha_free(&conf->npn_str);
William Lallemand6e9556b2020-05-12 17:52:44 +020063#endif
64#ifdef TLSEXT_TYPE_application_layer_protocol_negotiation
Willy Tarreau61cfdf42021-02-20 10:46:51 +010065 ha_free(&conf->alpn_str);
William Lallemand6e9556b2020-05-12 17:52:44 +020066#endif
Willy Tarreau61cfdf42021-02-20 10:46:51 +010067 ha_free(&conf->ca_file);
68 ha_free(&conf->ca_verify_file);
69 ha_free(&conf->crl_file);
70 ha_free(&conf->ciphers);
Ilya Shipitsinf34ed0b2020-11-21 14:37:34 +050071#ifdef HAVE_SSL_CTX_SET_CIPHERSUITES
Willy Tarreau61cfdf42021-02-20 10:46:51 +010072 ha_free(&conf->ciphersuites);
William Lallemand6e9556b2020-05-12 17:52:44 +020073#endif
Willy Tarreau61cfdf42021-02-20 10:46:51 +010074 ha_free(&conf->curves);
75 ha_free(&conf->ecdhe);
William Lallemand6e9556b2020-05-12 17:52:44 +020076 }
77}
78
William Lallemand82f2d2f2020-09-10 19:06:43 +020079/*
80 * Allocate and copy a ssl_bind_conf structure
81 */
82struct ssl_bind_conf *crtlist_dup_ssl_conf(struct ssl_bind_conf *src)
83{
84 struct ssl_bind_conf *dst;
85
86 if (!src)
87 return NULL;
88
89 dst = calloc(1, sizeof(*dst));
90 if (!dst)
91 return NULL;
92
93#if defined(OPENSSL_NPN_NEGOTIATED) && !defined(OPENSSL_NO_NEXTPROTONEG)
94 if (src->npn_str) {
95 dst->npn_str = strdup(src->npn_str);
96 if (!dst->npn_str)
97 goto error;
98 }
99#endif
100#ifdef TLSEXT_TYPE_application_layer_protocol_negotiation
101 if (src->alpn_str) {
102 dst->alpn_str = strdup(src->alpn_str);
103 if (!dst->alpn_str)
104 goto error;
105 }
106#endif
107 if (src->ca_file) {
108 dst->ca_file = strdup(src->ca_file);
109 if (!dst->ca_file)
110 goto error;
111 }
112 if (src->ca_verify_file) {
113 dst->ca_verify_file = strdup(src->ca_verify_file);
114 if (!dst->ca_verify_file)
115 goto error;
116 }
117 if (src->crl_file) {
118 dst->crl_file = strdup(src->crl_file);
119 if (!dst->crl_file)
120 goto error;
121 }
122 if (src->ciphers) {
123 dst->ciphers = strdup(src->ciphers);
124 if (!dst->ciphers)
125 goto error;
126 }
Ilya Shipitsinf34ed0b2020-11-21 14:37:34 +0500127#ifdef HAVE_SSL_CTX_SET_CIPHERSUITES
William Lallemand82f2d2f2020-09-10 19:06:43 +0200128 if (src->ciphersuites) {
129 dst->ciphersuites = strdup(src->ciphersuites);
130 if (!dst->ciphersuites)
131 goto error;
132 }
133#endif
134 if (src->curves) {
135 dst->curves = strdup(src->curves);
136 if (!dst->curves)
137 goto error;
138 }
139 if (src->ecdhe) {
140 dst->ecdhe = strdup(src->ecdhe);
141 if (!dst->ecdhe)
142 goto error;
143 }
144 return dst;
145
146error:
147 ssl_sock_free_ssl_conf(dst);
148 free(dst);
149
150 return NULL;
151}
William Lallemand6e9556b2020-05-12 17:52:44 +0200152
153/* free sni filters */
154void crtlist_free_filters(char **args)
155{
156 int i;
157
158 if (!args)
159 return;
160
161 for (i = 0; args[i]; i++)
162 free(args[i]);
163
164 free(args);
165}
166
167/* Alloc and duplicate a char ** array */
168char **crtlist_dup_filters(char **args, int fcount)
169{
170 char **dst;
171 int i;
172
173 if (fcount == 0)
174 return NULL;
175
176 dst = calloc(fcount + 1, sizeof(*dst));
177 if (!dst)
178 return NULL;
179
180 for (i = 0; i < fcount; i++) {
181 dst[i] = strdup(args[i]);
182 if (!dst[i])
183 goto error;
184 }
185 return dst;
186
187error:
188 crtlist_free_filters(dst);
189 return NULL;
190}
191
192/*
193 * Detach and free a crtlist_entry.
194 * Free the filters, the ssl_conf and call ckch_inst_free() for each ckch_inst
195 */
196void crtlist_entry_free(struct crtlist_entry *entry)
197{
198 struct ckch_inst *inst, *inst_s;
199
200 if (entry == NULL)
201 return;
202
203 ebpt_delete(&entry->node);
Willy Tarreau2b718102021-04-21 07:32:39 +0200204 LIST_DELETE(&entry->by_crtlist);
205 LIST_DELETE(&entry->by_ckch_store);
William Lallemand6e9556b2020-05-12 17:52:44 +0200206 crtlist_free_filters(entry->filters);
207 ssl_sock_free_ssl_conf(entry->ssl_conf);
208 free(entry->ssl_conf);
209 list_for_each_entry_safe(inst, inst_s, &entry->ckch_inst, by_crtlist_entry) {
210 ckch_inst_free(inst);
211 }
212 free(entry);
213}
William Lallemand5622c452020-09-10 19:08:49 +0200214/*
215 * Duplicate a crt_list entry and its content (ssl_conf, filters/fcount)
216 * Return a pointer to the new entry
217 */
218struct crtlist_entry *crtlist_entry_dup(struct crtlist_entry *src)
219{
220 struct crtlist_entry *entry;
221
222 if (src == NULL)
223 return NULL;
224
225 entry = crtlist_entry_new();
226 if (entry == NULL)
227 return NULL;
228
229 if (src->filters) {
230 entry->filters = crtlist_dup_filters(src->filters, src->fcount);
231 if (!entry->filters)
232 goto error;
233 }
234 entry->fcount = src->fcount;
235 if (src->ssl_conf) {
236 entry->ssl_conf = crtlist_dup_ssl_conf(src->ssl_conf);
237 if (!entry->ssl_conf)
238 goto error;
239 }
240 entry->crtlist = src->crtlist;
241
242 return entry;
243
244error:
245
246 crtlist_free_filters(entry->filters);
247 ssl_sock_free_ssl_conf(entry->ssl_conf);
248 free(entry->ssl_conf);
249 free(entry);
250
251 return NULL;
252}
William Lallemand6e9556b2020-05-12 17:52:44 +0200253
254/*
255 * Allocate and initialize a crtlist_entry
256 */
257struct crtlist_entry *crtlist_entry_new()
258{
259 struct crtlist_entry *entry;
260
261 entry = calloc(1, sizeof(*entry));
262 if (entry == NULL)
263 return NULL;
264
265 LIST_INIT(&entry->ckch_inst);
266
Willy Tarreau2b718102021-04-21 07:32:39 +0200267 /* initialize the nodes so we can LIST_DELETE in any cases */
William Lallemand6e9556b2020-05-12 17:52:44 +0200268 LIST_INIT(&entry->by_crtlist);
269 LIST_INIT(&entry->by_ckch_store);
270
271 return entry;
272}
273
274/* Free a crtlist, from the crt_entry to the content of the ssl_conf */
275void crtlist_free(struct crtlist *crtlist)
276{
277 struct crtlist_entry *entry, *s_entry;
William Lallemand6a3168a2020-06-23 11:43:35 +0200278 struct bind_conf_list *bind_conf_node;
William Lallemand6e9556b2020-05-12 17:52:44 +0200279
280 if (crtlist == NULL)
281 return;
282
William Lallemand6a3168a2020-06-23 11:43:35 +0200283 bind_conf_node = crtlist->bind_conf;
284 while (bind_conf_node) {
285 struct bind_conf_list *next = bind_conf_node->next;
286 free(bind_conf_node);
287 bind_conf_node = next;
288 }
289
William Lallemand6e9556b2020-05-12 17:52:44 +0200290 list_for_each_entry_safe(entry, s_entry, &crtlist->ord_entries, by_crtlist) {
291 crtlist_entry_free(entry);
292 }
293 ebmb_delete(&crtlist->node);
294 free(crtlist);
295}
296
297/* Alloc and initialize a struct crtlist
298 * <filename> is the key of the ebmb_node
299 * <unique> initialize the list of entries to be unique (1) or not (0)
300 */
301struct crtlist *crtlist_new(const char *filename, int unique)
302{
303 struct crtlist *newlist;
304
305 newlist = calloc(1, sizeof(*newlist) + strlen(filename) + 1);
306 if (newlist == NULL)
307 return NULL;
308
309 memcpy(newlist->node.key, filename, strlen(filename) + 1);
310 if (unique)
311 newlist->entries = EB_ROOT_UNIQUE;
312 else
313 newlist->entries = EB_ROOT;
314
315 LIST_INIT(&newlist->ord_entries);
316
317 return newlist;
318}
319
320/*
321 * Read a single crt-list line. /!\ alter the <line> string.
322 * Fill <crt_path> and <crtlist_entry>
323 * <crtlist_entry> must be alloc and free by the caller
324 * <crtlist_entry->ssl_conf> is alloc by the function
325 * <crtlist_entry->filters> is alloc by the function
326 * <crt_path> is a ptr in <line>
327 * Return an error code
328 */
Remi Tricot-Le Bretonfb00f312021-03-23 16:41:53 +0100329int crtlist_parse_line(char *line, char **crt_path, struct crtlist_entry *entry, const char *file, int linenum, int from_cli, char **err)
William Lallemand6e9556b2020-05-12 17:52:44 +0200330{
331 int cfgerr = 0;
332 int arg, newarg, cur_arg, i, ssl_b = 0, ssl_e = 0;
333 char *end;
334 char *args[MAX_CRT_ARGS + 1];
335 struct ssl_bind_conf *ssl_conf = NULL;
336
337 if (!line || !crt_path || !entry)
338 return ERR_ALERT | ERR_FATAL;
339
340 end = line + strlen(line);
341 if (end-line >= CRT_LINESIZE-1 && *(end-1) != '\n') {
342 /* Check if we reached the limit and the last char is not \n.
343 * Watch out for the last line without the terminating '\n'!
344 */
Tim Duesterhus6d07fae2020-09-29 18:00:27 +0200345 memprintf(err, "parsing [%s:%d]: line too long, limit is %d characters",
346 file, linenum, CRT_LINESIZE-1);
William Lallemand6e9556b2020-05-12 17:52:44 +0200347 cfgerr |= ERR_ALERT | ERR_FATAL;
348 goto error;
349 }
350 arg = 0;
351 newarg = 1;
352 while (*line) {
353 if (isspace((unsigned char)*line)) {
354 newarg = 1;
355 *line = 0;
356 } else if (*line == '[') {
357 if (ssl_b) {
Tim Duesterhus6d07fae2020-09-29 18:00:27 +0200358 memprintf(err, "parsing [%s:%d]: too many '['", file, linenum);
William Lallemand6e9556b2020-05-12 17:52:44 +0200359 cfgerr |= ERR_ALERT | ERR_FATAL;
360 goto error;
361 }
362 if (!arg) {
Tim Duesterhus6d07fae2020-09-29 18:00:27 +0200363 memprintf(err, "parsing [%s:%d]: file must start with a cert", file, linenum);
William Lallemand6e9556b2020-05-12 17:52:44 +0200364 cfgerr |= ERR_ALERT | ERR_FATAL;
365 goto error;
366 }
367 ssl_b = arg;
368 newarg = 1;
369 *line = 0;
370 } else if (*line == ']') {
371 if (ssl_e) {
Tim Duesterhus6d07fae2020-09-29 18:00:27 +0200372 memprintf(err, "parsing [%s:%d]: too many ']'", file, linenum);
William Lallemand6e9556b2020-05-12 17:52:44 +0200373 cfgerr |= ERR_ALERT | ERR_FATAL;
374 goto error;
375 }
376 if (!ssl_b) {
Tim Duesterhus6d07fae2020-09-29 18:00:27 +0200377 memprintf(err, "parsing [%s:%d]: missing '['", file, linenum);
William Lallemand6e9556b2020-05-12 17:52:44 +0200378 cfgerr |= ERR_ALERT | ERR_FATAL;
379 goto error;
380 }
381 ssl_e = arg;
382 newarg = 1;
383 *line = 0;
384 } else if (newarg) {
385 if (arg == MAX_CRT_ARGS) {
Tim Duesterhus6d07fae2020-09-29 18:00:27 +0200386 memprintf(err, "parsing [%s:%d]: too many args ", file, linenum);
William Lallemand6e9556b2020-05-12 17:52:44 +0200387 cfgerr |= ERR_ALERT | ERR_FATAL;
388 goto error;
389 }
390 newarg = 0;
391 args[arg++] = line;
392 }
393 line++;
394 }
395 args[arg++] = line;
396
397 /* empty line */
398 if (!*args[0]) {
399 cfgerr |= ERR_NONE;
400 goto error;
401 }
402
403 *crt_path = args[0];
404
405 if (ssl_b) {
William Lallemandd85227f2023-02-07 17:06:35 +0100406 if (ssl_b > 1) {
407 memprintf(err, "parsing [%s:%d]: malformated line, filters can't be between filename and options!", file, linenum);
408 cfgerr |= ERR_WARN;
409 }
410
William Lallemand6e9556b2020-05-12 17:52:44 +0200411 ssl_conf = calloc(1, sizeof *ssl_conf);
412 if (!ssl_conf) {
413 memprintf(err, "not enough memory!");
414 cfgerr |= ERR_ALERT | ERR_FATAL;
415 goto error;
416 }
417 }
Remi Tricot-Le Bretonfb00f312021-03-23 16:41:53 +0100418
William Lallemand6e9556b2020-05-12 17:52:44 +0200419 cur_arg = ssl_b ? ssl_b : 1;
420 while (cur_arg < ssl_e) {
421 newarg = 0;
422 for (i = 0; ssl_bind_kws[i].kw != NULL; i++) {
423 if (strcmp(ssl_bind_kws[i].kw, args[cur_arg]) == 0) {
424 newarg = 1;
Remi Tricot-Le Bretonfb00f312021-03-23 16:41:53 +0100425 cfgerr |= ssl_bind_kws[i].parse(args, cur_arg, NULL, ssl_conf, from_cli, err);
William Lallemand6e9556b2020-05-12 17:52:44 +0200426 if (cur_arg + 1 + ssl_bind_kws[i].skip > ssl_e) {
Tim Duesterhus6d07fae2020-09-29 18:00:27 +0200427 memprintf(err, "parsing [%s:%d]: ssl args out of '[]' for %s",
428 file, linenum, args[cur_arg]);
William Lallemand6e9556b2020-05-12 17:52:44 +0200429 cfgerr |= ERR_ALERT | ERR_FATAL;
430 goto error;
431 }
432 cur_arg += 1 + ssl_bind_kws[i].skip;
433 break;
434 }
435 }
436 if (!cfgerr && !newarg) {
Tim Duesterhus6d07fae2020-09-29 18:00:27 +0200437 memprintf(err, "parsing [%s:%d]: unknown ssl keyword %s",
438 file, linenum, args[cur_arg]);
William Lallemand6e9556b2020-05-12 17:52:44 +0200439 cfgerr |= ERR_ALERT | ERR_FATAL;
440 goto error;
441 }
442 }
443 entry->linenum = linenum;
444 entry->ssl_conf = ssl_conf;
445 entry->filters = crtlist_dup_filters(&args[cur_arg], arg - cur_arg - 1);
446 entry->fcount = arg - cur_arg - 1;
447
448 return cfgerr;
449
450error:
451 crtlist_free_filters(entry->filters);
452 entry->filters = NULL;
453 ssl_sock_free_ssl_conf(entry->ssl_conf);
Willy Tarreau61cfdf42021-02-20 10:46:51 +0100454 ha_free(&entry->ssl_conf);
William Lallemand6e9556b2020-05-12 17:52:44 +0200455 return cfgerr;
456}
457
458
459
460/* This function parse a crt-list file and store it in a struct crtlist, each line is a crtlist_entry structure
461 * Fill the <crtlist> argument with a pointer to a new crtlist struct
462 *
463 * This function tries to open and store certificate files.
464 */
465int crtlist_parse_file(char *file, struct bind_conf *bind_conf, struct proxy *curproxy, struct crtlist **crtlist, char **err)
466{
467 struct crtlist *newlist;
468 struct crtlist_entry *entry = NULL;
469 char thisline[CRT_LINESIZE];
William Lallemand6e9556b2020-05-12 17:52:44 +0200470 FILE *f;
471 struct stat buf;
472 int linenum = 0;
473 int cfgerr = 0;
Tim Duesterhusb9f6acc2020-09-29 18:00:28 +0200474 int missing_lf = -1;
William Lallemand6e9556b2020-05-12 17:52:44 +0200475
476 if ((f = fopen(file, "r")) == NULL) {
477 memprintf(err, "cannot open file '%s' : %s", file, strerror(errno));
478 return ERR_ALERT | ERR_FATAL;
479 }
480
481 newlist = crtlist_new(file, 0);
482 if (newlist == NULL) {
483 memprintf(err, "Not enough memory!");
484 cfgerr |= ERR_ALERT | ERR_FATAL;
485 goto error;
486 }
487
488 while (fgets(thisline, sizeof(thisline), f) != NULL) {
489 char *end;
490 char *line = thisline;
491 char *crt_path;
William Lallemand86c2dd62020-11-20 14:23:38 +0100492 char path[MAXPATHLEN+1];
William Lallemand6e9556b2020-05-12 17:52:44 +0200493 struct ckch_store *ckchs;
William Lallemand77e1c6f2020-11-20 18:26:09 +0100494 int found = 0;
William Lallemand6e9556b2020-05-12 17:52:44 +0200495
Tim Duesterhusb9f6acc2020-09-29 18:00:28 +0200496 if (missing_lf != -1) {
497 memprintf(err, "parsing [%s:%d]: Stray NUL character at position %d.\n",
498 file, linenum, (missing_lf + 1));
499 cfgerr |= ERR_ALERT | ERR_FATAL;
500 missing_lf = -1;
501 break;
502 }
503
William Lallemand6e9556b2020-05-12 17:52:44 +0200504 linenum++;
505 end = line + strlen(line);
506 if (end-line == sizeof(thisline)-1 && *(end-1) != '\n') {
507 /* Check if we reached the limit and the last char is not \n.
508 * Watch out for the last line without the terminating '\n'!
509 */
Tim Duesterhus6d07fae2020-09-29 18:00:27 +0200510 memprintf(err, "parsing [%s:%d]: line too long, limit is %d characters",
511 file, linenum, (int)sizeof(thisline)-1);
William Lallemand6e9556b2020-05-12 17:52:44 +0200512 cfgerr |= ERR_ALERT | ERR_FATAL;
513 break;
514 }
515
516 if (*line == '#' || *line == '\n' || *line == '\r')
517 continue;
518
Tim Duesterhusb9f6acc2020-09-29 18:00:28 +0200519 if (end > line && *(end-1) == '\n') {
520 /* kill trailing LF */
521 *(end - 1) = 0;
522 }
523 else {
524 /* mark this line as truncated */
525 missing_lf = end - line;
526 }
527
William Lallemand6e9556b2020-05-12 17:52:44 +0200528 entry = crtlist_entry_new();
529 if (entry == NULL) {
530 memprintf(err, "Not enough memory!");
531 cfgerr |= ERR_ALERT | ERR_FATAL;
532 goto error;
533 }
Tim Duesterhusb9f6acc2020-09-29 18:00:28 +0200534
Remi Tricot-Le Bretonfb00f312021-03-23 16:41:53 +0100535 cfgerr |= crtlist_parse_line(thisline, &crt_path, entry, file, linenum, 0, err);
William Lallemand20b0fed2020-09-28 15:45:16 +0200536 if (cfgerr & ERR_CODE)
William Lallemand6e9556b2020-05-12 17:52:44 +0200537 goto error;
538
539 /* empty line */
540 if (!crt_path || !*crt_path) {
541 crtlist_entry_free(entry);
542 entry = NULL;
543 continue;
544 }
545
546 if (*crt_path != '/' && global_ssl.crt_base) {
Willy Tarreau393e42a2022-05-09 10:31:28 +0200547 if ((strlen(global_ssl.crt_base) + 1 + strlen(crt_path)) > sizeof(path) ||
Willy Tarreau63fc9002022-05-09 21:14:04 +0200548 snprintf(path, sizeof(path), "%s/%s", global_ssl.crt_base, crt_path) > sizeof(path)) {
Tim Duesterhus6d07fae2020-09-29 18:00:27 +0200549 memprintf(err, "parsing [%s:%d]: '%s' : path too long",
550 file, linenum, crt_path);
William Lallemand6e9556b2020-05-12 17:52:44 +0200551 cfgerr |= ERR_ALERT | ERR_FATAL;
552 goto error;
553 }
William Lallemand6e9556b2020-05-12 17:52:44 +0200554 crt_path = path;
555 }
556
557 /* Look for a ckch_store or create one */
558 ckchs = ckchs_lookup(crt_path);
559 if (ckchs == NULL) {
William Lallemand47da8212020-09-10 19:13:27 +0200560 if (stat(crt_path, &buf) == 0) {
William Lallemand77e1c6f2020-11-20 18:26:09 +0100561 found++;
William Lallemand47da8212020-09-10 19:13:27 +0200562
William Lallemandbd8e6ed2020-09-16 16:08:08 +0200563 ckchs = ckchs_load_cert_file(crt_path, err);
William Lallemand47da8212020-09-10 19:13:27 +0200564 if (ckchs == NULL) {
565 cfgerr |= ERR_ALERT | ERR_FATAL;
566 goto error;
567 }
568
569 entry->node.key = ckchs;
570 entry->crtlist = newlist;
Remi Tricot-Le Bretonfb2b9982022-12-20 11:11:11 +0100571 if (entry->ssl_conf)
572 ckchs->data->ocsp_update_mode = entry->ssl_conf->ocsp_update;
William Lallemand47da8212020-09-10 19:13:27 +0200573 ebpt_insert(&newlist->entries, &entry->node);
Willy Tarreau2b718102021-04-21 07:32:39 +0200574 LIST_APPEND(&newlist->ord_entries, &entry->by_crtlist);
575 LIST_APPEND(&ckchs->crtlist_entry, &entry->by_ckch_store);
William Lallemand6e9556b2020-05-12 17:52:44 +0200576
William Lallemand73404572020-11-20 18:23:40 +0100577 } else if (global_ssl.extra_files & SSL_GF_BUNDLE) {
William Lallemand47da8212020-09-10 19:13:27 +0200578 /* If we didn't find the file, this could be a
William Lallemand51f784b2020-10-02 18:08:18 +0200579 bundle, since 2.3 we don't support multiple
580 certificate in the same OpenSSL store, so we
581 emulate it by loading each file separately. To
582 do so we need to duplicate the entry in the
583 crt-list because it becomes independent */
William Lallemand47da8212020-09-10 19:13:27 +0200584 char fp[MAXPATHLEN+1] = {0};
585 int n = 0;
586 struct crtlist_entry *entry_dup = entry; /* use the previous created entry */
587 for (n = 0; n < SSL_SOCK_NUM_KEYTYPES; n++) {
588 struct stat buf;
589 int ret;
590
William Lallemand86c2dd62020-11-20 14:23:38 +0100591 ret = snprintf(fp, sizeof(fp), "%s.%s", crt_path, SSL_SOCK_KEYTYPE_NAMES[n]);
William Lallemand47da8212020-09-10 19:13:27 +0200592 if (ret > sizeof(fp))
593 continue;
594
595 ckchs = ckchs_lookup(fp);
William Lallemand77e1c6f2020-11-20 18:26:09 +0100596 if (!ckchs) {
597 if (stat(fp, &buf) == 0) {
598 ckchs = ckchs_load_cert_file(fp, err);
599 if (!ckchs) {
William Lallemand47da8212020-09-10 19:13:27 +0200600 cfgerr |= ERR_ALERT | ERR_FATAL;
601 goto error;
602 }
William Lallemand77e1c6f2020-11-20 18:26:09 +0100603 } else {
604 continue; /* didn't find this extension, skip */
605 }
606 }
607 found++;
608 linenum++; /* we duplicate the line for this entry in the bundle */
609 if (!entry_dup) { /* if the entry was used, duplicate one */
610 linenum++;
611 entry_dup = crtlist_entry_dup(entry);
612 if (!entry_dup) {
613 cfgerr |= ERR_ALERT | ERR_FATAL;
614 goto error;
William Lallemand47da8212020-09-10 19:13:27 +0200615 }
William Lallemand77e1c6f2020-11-20 18:26:09 +0100616 entry_dup->linenum = linenum;
617 }
William Lallemand47da8212020-09-10 19:13:27 +0200618
William Lallemand77e1c6f2020-11-20 18:26:09 +0100619 entry_dup->node.key = ckchs;
620 entry_dup->crtlist = newlist;
Remi Tricot-Le Bretonfc92b8b2023-01-09 12:02:46 +0100621 if (ckchs->data->ocsp_update_mode != SSL_SOCK_OCSP_UPDATE_DFLT || entry->ssl_conf) {
622 if ((!entry->ssl_conf && ckchs->data->ocsp_update_mode == SSL_SOCK_OCSP_UPDATE_ON)
Remi Tricot-Le Breton8c990812023-01-10 11:44:15 +0100623 || (entry->ssl_conf && ckchs->data->ocsp_update_mode != entry->ssl_conf->ocsp_update)) {
Remi Tricot-Le Bretonfb2b9982022-12-20 11:11:11 +0100624 memprintf(err, "%sIncompatibilities found in OCSP update mode for certificate %s\n", err && *err ? *err : "", crt_path);
Remi Tricot-Le Breton474f6142023-01-12 09:49:09 +0100625 cfgerr |= ERR_ALERT | ERR_FATAL;
Remi Tricot-Le Bretonfb2b9982022-12-20 11:11:11 +0100626 }
Remi Tricot-Le Bretonfb2b9982022-12-20 11:11:11 +0100627 }
Remi Tricot-Le Bretonfc92b8b2023-01-09 12:02:46 +0100628 if (entry->ssl_conf)
629 ckchs->data->ocsp_update_mode = entry->ssl_conf->ocsp_update;
William Lallemand77e1c6f2020-11-20 18:26:09 +0100630 ebpt_insert(&newlist->entries, &entry_dup->node);
Willy Tarreau2b718102021-04-21 07:32:39 +0200631 LIST_APPEND(&newlist->ord_entries, &entry_dup->by_crtlist);
632 LIST_APPEND(&ckchs->crtlist_entry, &entry_dup->by_ckch_store);
William Lallemand47da8212020-09-10 19:13:27 +0200633
William Lallemand77e1c6f2020-11-20 18:26:09 +0100634 entry_dup = NULL; /* the entry was used, we need a new one next round */
William Lallemand47da8212020-09-10 19:13:27 +0200635 }
William Lallemandb7fdfdf2020-12-04 15:45:02 +0100636#if HA_OPENSSL_VERSION_NUMBER < 0x10101000L
637 if (found) {
638 memprintf(err, "%sCan't load '%s'. Loading a multi certificates bundle requires OpenSSL >= 1.1.1\n",
639 err && *err ? *err : "", crt_path);
640 cfgerr |= ERR_ALERT | ERR_FATAL;
641 }
642#endif
William Lallemand47da8212020-09-10 19:13:27 +0200643 }
William Lallemand77e1c6f2020-11-20 18:26:09 +0100644 if (!found) {
645 memprintf(err, "%sunable to stat SSL certificate from file '%s' : %s.\n",
646 err && *err ? *err : "", crt_path, strerror(errno));
647 cfgerr |= ERR_ALERT | ERR_FATAL;
648 }
649
William Lallemand50c03aa2020-11-06 16:24:07 +0100650 } else {
651 entry->node.key = ckchs;
652 entry->crtlist = newlist;
Remi Tricot-Le Bretonfc92b8b2023-01-09 12:02:46 +0100653 if (ckchs->data->ocsp_update_mode != SSL_SOCK_OCSP_UPDATE_DFLT || entry->ssl_conf) {
654 if ((!entry->ssl_conf && ckchs->data->ocsp_update_mode == SSL_SOCK_OCSP_UPDATE_ON)
Remi Tricot-Le Breton8c990812023-01-10 11:44:15 +0100655 || (entry->ssl_conf && ckchs->data->ocsp_update_mode != entry->ssl_conf->ocsp_update)) {
Remi Tricot-Le Bretonfb2b9982022-12-20 11:11:11 +0100656 memprintf(err, "%sIncompatibilities found in OCSP update mode for certificate %s\n", err && *err ? *err : "", crt_path);
Remi Tricot-Le Breton474f6142023-01-12 09:49:09 +0100657 cfgerr |= ERR_ALERT | ERR_FATAL;
Remi Tricot-Le Bretonfb2b9982022-12-20 11:11:11 +0100658 }
Remi Tricot-Le Bretonfb2b9982022-12-20 11:11:11 +0100659 }
Remi Tricot-Le Bretonfc92b8b2023-01-09 12:02:46 +0100660 if (entry->ssl_conf)
661 ckchs->data->ocsp_update_mode = entry->ssl_conf->ocsp_update;
William Lallemand50c03aa2020-11-06 16:24:07 +0100662 ebpt_insert(&newlist->entries, &entry->node);
Willy Tarreau2b718102021-04-21 07:32:39 +0200663 LIST_APPEND(&newlist->ord_entries, &entry->by_crtlist);
664 LIST_APPEND(&ckchs->crtlist_entry, &entry->by_ckch_store);
William Lallemand77e1c6f2020-11-20 18:26:09 +0100665 found++;
William Lallemand47da8212020-09-10 19:13:27 +0200666 }
William Lallemand6e9556b2020-05-12 17:52:44 +0200667 entry = NULL;
668 }
Tim Duesterhusb9f6acc2020-09-29 18:00:28 +0200669
670 if (missing_lf != -1) {
671 memprintf(err, "parsing [%s:%d]: Missing LF on last line, file might have been truncated at position %d.\n",
672 file, linenum, (missing_lf + 1));
673 cfgerr |= ERR_ALERT | ERR_FATAL;
674 }
675
William Lallemand6e9556b2020-05-12 17:52:44 +0200676 if (cfgerr & ERR_CODE)
677 goto error;
678
679 newlist->linecount = linenum;
680
681 fclose(f);
682 *crtlist = newlist;
683
684 return cfgerr;
685error:
686 crtlist_entry_free(entry);
687
688 fclose(f);
689 crtlist_free(newlist);
690 return cfgerr;
691}
692
693/* This function reads a directory and stores it in a struct crtlist, each file is a crtlist_entry structure
694 * Fill the <crtlist> argument with a pointer to a new crtlist struct
695 *
696 * This function tries to open and store certificate files.
697 */
698int crtlist_load_cert_dir(char *path, struct bind_conf *bind_conf, struct crtlist **crtlist, char **err)
699{
700 struct crtlist *dir;
701 struct dirent **de_list;
702 int i, n;
703 struct stat buf;
704 char *end;
705 char fp[MAXPATHLEN+1];
706 int cfgerr = 0;
707 struct ckch_store *ckchs;
William Lallemand6e9556b2020-05-12 17:52:44 +0200708
709 dir = crtlist_new(path, 1);
710 if (dir == NULL) {
711 memprintf(err, "not enough memory");
712 return ERR_ALERT | ERR_FATAL;
713 }
714
715 n = scandir(path, &de_list, 0, alphasort);
716 if (n < 0) {
717 memprintf(err, "%sunable to scan directory '%s' : %s.\n",
718 err && *err ? *err : "", path, strerror(errno));
719 cfgerr |= ERR_ALERT | ERR_FATAL;
720 }
721 else {
722 for (i = 0; i < n; i++) {
723 struct crtlist_entry *entry;
724 struct dirent *de = de_list[i];
725
726 end = strrchr(de->d_name, '.');
William Lallemand589570d2022-05-09 10:30:51 +0200727 if (end && (de->d_name[0] == '.' ||
728 strcmp(end, ".issuer") == 0 || strcmp(end, ".ocsp") == 0 ||
729 strcmp(end, ".sctl") == 0 || strcmp(end, ".key") == 0))
William Lallemand6e9556b2020-05-12 17:52:44 +0200730 goto ignore_entry;
731
732 snprintf(fp, sizeof(fp), "%s/%s", path, de->d_name);
733 if (stat(fp, &buf) != 0) {
734 memprintf(err, "%sunable to stat SSL certificate from file '%s' : %s.\n",
735 err && *err ? *err : "", fp, strerror(errno));
736 cfgerr |= ERR_ALERT | ERR_FATAL;
737 goto ignore_entry;
738 }
739 if (!S_ISREG(buf.st_mode))
740 goto ignore_entry;
741
742 entry = crtlist_entry_new();
743 if (entry == NULL) {
744 memprintf(err, "not enough memory '%s'", fp);
745 cfgerr |= ERR_ALERT | ERR_FATAL;
746 goto ignore_entry;
747 }
748
William Lallemand6e9556b2020-05-12 17:52:44 +0200749 ckchs = ckchs_lookup(fp);
750 if (ckchs == NULL)
William Lallemandbd8e6ed2020-09-16 16:08:08 +0200751 ckchs = ckchs_load_cert_file(fp, err);
William Lallemand6e9556b2020-05-12 17:52:44 +0200752 if (ckchs == NULL) {
753 free(de);
754 free(entry);
755 cfgerr |= ERR_ALERT | ERR_FATAL;
756 goto end;
757 }
758 entry->node.key = ckchs;
759 entry->crtlist = dir;
Willy Tarreau2b718102021-04-21 07:32:39 +0200760 LIST_APPEND(&ckchs->crtlist_entry, &entry->by_ckch_store);
761 LIST_APPEND(&dir->ord_entries, &entry->by_crtlist);
William Lallemand6e9556b2020-05-12 17:52:44 +0200762 ebpt_insert(&dir->entries, &entry->node);
763
764ignore_entry:
765 free(de);
766 }
767end:
768 free(de_list);
769 }
770
771 if (cfgerr & ERR_CODE) {
772 /* free the dir and entries on error */
773 crtlist_free(dir);
774 } else {
775 *crtlist = dir;
776 }
777 return cfgerr;
778
779}
780
William Lallemandc756bbd2020-05-13 17:23:59 +0200781/*
782 * Take an ssl_bind_conf structure and append the configuration line used to
783 * create it in the buffer
784 */
785static void dump_crtlist_sslconf(struct buffer *buf, const struct ssl_bind_conf *conf)
786{
787 int space = 0;
788
789 if (conf == NULL)
790 return;
791
792 chunk_appendf(buf, " [");
793#ifdef OPENSSL_NPN_NEGOTIATED
794 if (conf->npn_str) {
795 int len = conf->npn_len;
796 char *ptr = conf->npn_str;
797 int comma = 0;
798
799 if (space) chunk_appendf(buf, " ");
800 chunk_appendf(buf, "npn ");
801 while (len) {
802 unsigned short size;
803
804 size = *ptr;
805 ptr++;
806 if (comma)
807 chunk_memcat(buf, ",", 1);
808 chunk_memcat(buf, ptr, size);
809 ptr += size;
810 len -= size + 1;
811 comma = 1;
812 }
813 chunk_memcat(buf, "", 1); /* finish with a \0 */
814 space++;
815 }
816#endif
817#ifdef TLSEXT_TYPE_application_layer_protocol_negotiation
818 if (conf->alpn_str) {
819 int len = conf->alpn_len;
820 char *ptr = conf->alpn_str;
821 int comma = 0;
822
823 if (space) chunk_appendf(buf, " ");
824 chunk_appendf(buf, "alpn ");
825 while (len) {
826 unsigned short size;
827
828 size = *ptr;
829 ptr++;
830 if (comma)
831 chunk_memcat(buf, ",", 1);
832 chunk_memcat(buf, ptr, size);
833 ptr += size;
834 len -= size + 1;
835 comma = 1;
836 }
837 chunk_memcat(buf, "", 1); /* finish with a \0 */
838 space++;
839 }
840#endif
841 /* verify */
842 {
843 if (conf->verify == SSL_SOCK_VERIFY_NONE) {
844 if (space) chunk_appendf(buf, " ");
845 chunk_appendf(buf, "verify none");
846 space++;
847 } else if (conf->verify == SSL_SOCK_VERIFY_OPTIONAL) {
848 if (space) chunk_appendf(buf, " ");
849 chunk_appendf(buf, "verify optional");
850 space++;
851 } else if (conf->verify == SSL_SOCK_VERIFY_REQUIRED) {
852 if (space) chunk_appendf(buf, " ");
853 chunk_appendf(buf, "verify required");
854 space++;
855 }
856 }
857
858 if (conf->no_ca_names) {
859 if (space) chunk_appendf(buf, " ");
860 chunk_appendf(buf, "no-ca-names");
861 space++;
862 }
863
864 if (conf->early_data) {
865 if (space) chunk_appendf(buf, " ");
866 chunk_appendf(buf, "allow-0rtt");
867 space++;
868 }
869 if (conf->ca_file) {
870 if (space) chunk_appendf(buf, " ");
871 chunk_appendf(buf, "ca-file %s", conf->ca_file);
872 space++;
873 }
874 if (conf->crl_file) {
875 if (space) chunk_appendf(buf, " ");
876 chunk_appendf(buf, "crl-file %s", conf->crl_file);
877 space++;
878 }
879 if (conf->ciphers) {
880 if (space) chunk_appendf(buf, " ");
881 chunk_appendf(buf, "ciphers %s", conf->ciphers);
882 space++;
883 }
Ilya Shipitsinf34ed0b2020-11-21 14:37:34 +0500884#ifdef HAVE_SSL_CTX_SET_CIPHERSUITES
William Lallemandc756bbd2020-05-13 17:23:59 +0200885 if (conf->ciphersuites) {
886 if (space) chunk_appendf(buf, " ");
887 chunk_appendf(buf, "ciphersuites %s", conf->ciphersuites);
888 space++;
889 }
890#endif
891 if (conf->curves) {
892 if (space) chunk_appendf(buf, " ");
893 chunk_appendf(buf, "curves %s", conf->curves);
894 space++;
895 }
896 if (conf->ecdhe) {
897 if (space) chunk_appendf(buf, " ");
898 chunk_appendf(buf, "ecdhe %s", conf->ecdhe);
899 space++;
900 }
901
902 /* the crt-lists only support ssl-min-ver and ssl-max-ver */
William Lallemand8177ad92020-05-20 16:49:02 +0200903 if (conf->ssl_methods_cfg.min) {
William Lallemandc756bbd2020-05-13 17:23:59 +0200904 if (space) chunk_appendf(buf, " ");
William Lallemand8177ad92020-05-20 16:49:02 +0200905 chunk_appendf(buf, "ssl-min-ver %s", methodVersions[conf->ssl_methods_cfg.min].name);
William Lallemandc756bbd2020-05-13 17:23:59 +0200906 space++;
907 }
908
William Lallemand8177ad92020-05-20 16:49:02 +0200909 if (conf->ssl_methods_cfg.max) {
William Lallemandc756bbd2020-05-13 17:23:59 +0200910 if (space) chunk_appendf(buf, " ");
William Lallemand8177ad92020-05-20 16:49:02 +0200911 chunk_appendf(buf, "ssl-max-ver %s", methodVersions[conf->ssl_methods_cfg.max].name);
William Lallemandc756bbd2020-05-13 17:23:59 +0200912 space++;
913 }
914
915 chunk_appendf(buf, "]");
916
917 return;
918}
919
920/* dump a list of filters */
921static void dump_crtlist_filters(struct buffer *buf, struct crtlist_entry *entry)
922{
923 int i;
924
925 if (!entry->fcount)
926 return;
927
928 for (i = 0; i < entry->fcount; i++) {
929 chunk_appendf(buf, " %s", entry->filters[i]);
930 }
931 return;
932}
933
934/************************** CLI functions ****************************/
935
936
Willy Tarreaua2fcca02022-05-05 11:53:23 +0200937/* CLI IO handler for '(show|dump) ssl crt-list'.
938 * It uses show_crtlist_ctx for the context.
939 */
William Lallemandc756bbd2020-05-13 17:23:59 +0200940static int cli_io_handler_dump_crtlist(struct appctx *appctx)
941{
Willy Tarreaua2fcca02022-05-05 11:53:23 +0200942 struct show_crtlist_ctx *ctx = appctx->svcctx;
William Lallemandc756bbd2020-05-13 17:23:59 +0200943 struct buffer *trash = alloc_trash_chunk();
William Lallemandc756bbd2020-05-13 17:23:59 +0200944 struct ebmb_node *lnode;
945
946 if (trash == NULL)
947 return 1;
948
949 /* dump the list of crt-lists */
Willy Tarreaua2fcca02022-05-05 11:53:23 +0200950 lnode = ctx->crtlist_node;
William Lallemandc756bbd2020-05-13 17:23:59 +0200951 if (lnode == NULL)
952 lnode = ebmb_first(&crtlists_tree);
953 while (lnode) {
954 chunk_appendf(trash, "%s\n", lnode->key);
Willy Tarreaud0a06d52022-05-18 15:07:19 +0200955 if (applet_putchk(appctx, trash) == -1)
William Lallemandc756bbd2020-05-13 17:23:59 +0200956 goto yield;
William Lallemandc756bbd2020-05-13 17:23:59 +0200957 lnode = ebmb_next(lnode);
958 }
959 free_trash_chunk(trash);
960 return 1;
961yield:
Willy Tarreaua2fcca02022-05-05 11:53:23 +0200962 ctx->crtlist_node = lnode;
William Lallemandc756bbd2020-05-13 17:23:59 +0200963 free_trash_chunk(trash);
964 return 0;
965}
966
967/* CLI IO handler for '(show|dump) ssl crt-list <filename>' */
968static int cli_io_handler_dump_crtlist_entries(struct appctx *appctx)
969{
Willy Tarreaua2fcca02022-05-05 11:53:23 +0200970 struct show_crtlist_ctx *ctx = appctx->svcctx;
William Lallemandc756bbd2020-05-13 17:23:59 +0200971 struct buffer *trash = alloc_trash_chunk();
972 struct crtlist *crtlist;
William Lallemandc756bbd2020-05-13 17:23:59 +0200973 struct crtlist_entry *entry;
974
975 if (trash == NULL)
976 return 1;
977
Willy Tarreaua2fcca02022-05-05 11:53:23 +0200978 crtlist = ebmb_entry(ctx->crtlist_node, struct crtlist, node);
William Lallemandc756bbd2020-05-13 17:23:59 +0200979
Willy Tarreaua2fcca02022-05-05 11:53:23 +0200980 entry = ctx->entry;
William Lallemandc756bbd2020-05-13 17:23:59 +0200981 if (entry == NULL) {
982 entry = LIST_ELEM((crtlist->ord_entries).n, typeof(entry), by_crtlist);
983 chunk_appendf(trash, "# %s\n", crtlist->node.key);
Willy Tarreaud0a06d52022-05-18 15:07:19 +0200984 if (applet_putchk(appctx, trash) == -1)
William Lallemandc756bbd2020-05-13 17:23:59 +0200985 goto yield;
William Lallemandc756bbd2020-05-13 17:23:59 +0200986 }
987
988 list_for_each_entry_from(entry, &crtlist->ord_entries, by_crtlist) {
989 struct ckch_store *store;
990 const char *filename;
991
992 store = entry->node.key;
993 filename = store->path;
994 chunk_appendf(trash, "%s", filename);
Willy Tarreaua2fcca02022-05-05 11:53:23 +0200995 if (ctx->mode == 's') /* show */
William Lallemandc756bbd2020-05-13 17:23:59 +0200996 chunk_appendf(trash, ":%d", entry->linenum);
997 dump_crtlist_sslconf(trash, entry->ssl_conf);
998 dump_crtlist_filters(trash, entry);
999 chunk_appendf(trash, "\n");
1000
Willy Tarreaud0a06d52022-05-18 15:07:19 +02001001 if (applet_putchk(appctx, trash) == -1)
William Lallemandc756bbd2020-05-13 17:23:59 +02001002 goto yield;
William Lallemandc756bbd2020-05-13 17:23:59 +02001003 }
1004 free_trash_chunk(trash);
1005 return 1;
1006yield:
Willy Tarreaua2fcca02022-05-05 11:53:23 +02001007 ctx->entry = entry;
William Lallemandc756bbd2020-05-13 17:23:59 +02001008 free_trash_chunk(trash);
1009 return 0;
1010}
1011
1012/* CLI argument parser for '(show|dump) ssl crt-list' */
1013static int cli_parse_dump_crtlist(char **args, char *payload, struct appctx *appctx, void *private)
1014{
Willy Tarreaua2fcca02022-05-05 11:53:23 +02001015 struct show_crtlist_ctx *ctx = applet_reserve_svcctx(appctx, sizeof(*ctx));
William Lallemandc756bbd2020-05-13 17:23:59 +02001016 struct ebmb_node *lnode;
1017 char *filename = NULL;
1018 int mode;
William Lallemand99cc2182020-06-25 15:19:51 +02001019 char *end;
William Lallemandc756bbd2020-05-13 17:23:59 +02001020
1021 if (!cli_has_level(appctx, ACCESS_LVL_ADMIN))
1022 return 1;
1023
Tim Duesterhuse5ff1412021-01-02 22:31:53 +01001024 if (*args[3] && strcmp(args[3], "-n") == 0) {
William Lallemandc756bbd2020-05-13 17:23:59 +02001025 mode = 's';
1026 filename = args[4];
1027 } else {
1028 mode = 'd';
1029 filename = args[3];
1030 }
1031
1032 if (mode == 's' && !*args[4])
1033 return cli_err(appctx, "'show ssl crt-list -n' expects a filename or a directory\n");
1034
1035 if (filename && *filename) {
William Lallemand99cc2182020-06-25 15:19:51 +02001036
1037
1038 /* strip trailing slashes, including first one */
1039 for (end = filename + strlen(filename) - 1; end >= filename && *end == '/'; end--)
1040 *end = 0;
1041
William Lallemandc756bbd2020-05-13 17:23:59 +02001042 lnode = ebst_lookup(&crtlists_tree, filename);
1043 if (lnode == NULL)
1044 return cli_err(appctx, "didn't find the specified filename\n");
1045
Willy Tarreaua2fcca02022-05-05 11:53:23 +02001046 ctx->crtlist_node = lnode;
William Lallemandc756bbd2020-05-13 17:23:59 +02001047 appctx->io_handler = cli_io_handler_dump_crtlist_entries;
1048 }
Willy Tarreaua2fcca02022-05-05 11:53:23 +02001049 ctx->mode = mode;
William Lallemandc756bbd2020-05-13 17:23:59 +02001050
1051 return 0;
1052}
1053
1054/* release function of the "add ssl crt-list' command, free things and unlock
Willy Tarreau6b6c3632022-05-05 13:43:49 +02001055 * the spinlock. It uses the add_crtlist_ctx.
1056 */
William Lallemandc756bbd2020-05-13 17:23:59 +02001057static void cli_release_add_crtlist(struct appctx *appctx)
1058{
Willy Tarreau6b6c3632022-05-05 13:43:49 +02001059 struct add_crtlist_ctx *ctx = appctx->svcctx;
1060 struct crtlist_entry *entry = ctx->entry;
William Lallemandc756bbd2020-05-13 17:23:59 +02001061
Christopher Fauletc642d7c2022-06-01 16:31:09 +02001062 if (entry) {
William Lallemandc756bbd2020-05-13 17:23:59 +02001063 struct ckch_inst *inst, *inst_s;
Christopher Fauletc642d7c2022-06-01 16:31:09 +02001064
William Lallemandc756bbd2020-05-13 17:23:59 +02001065 /* upon error free the ckch_inst and everything inside */
1066 ebpt_delete(&entry->node);
Willy Tarreau2b718102021-04-21 07:32:39 +02001067 LIST_DELETE(&entry->by_crtlist);
1068 LIST_DELETE(&entry->by_ckch_store);
William Lallemandc756bbd2020-05-13 17:23:59 +02001069
1070 list_for_each_entry_safe(inst, inst_s, &entry->ckch_inst, by_ckchs) {
1071 ckch_inst_free(inst);
1072 }
1073 crtlist_free_filters(entry->filters);
1074 ssl_sock_free_ssl_conf(entry->ssl_conf);
1075 free(entry->ssl_conf);
1076 free(entry);
1077 }
William Lallemandc756bbd2020-05-13 17:23:59 +02001078 HA_SPIN_UNLOCK(CKCH_LOCK, &ckch_lock);
Christopher Fauletc642d7c2022-06-01 16:31:09 +02001079 ha_free(&ctx->err);
William Lallemandc756bbd2020-05-13 17:23:59 +02001080}
1081
1082
1083/* IO Handler for the "add ssl crt-list" command It adds a new entry in the
1084 * crt-list and generates the ckch_insts for each bind_conf that uses this crt-list
1085 *
1086 * The logic is the same as the "commit ssl cert" command but without the
1087 * freeing of the old structures, because there are none.
Willy Tarreau6b6c3632022-05-05 13:43:49 +02001088 *
1089 * It uses the add_crtlist_ctx for the context.
William Lallemandc756bbd2020-05-13 17:23:59 +02001090 */
1091static int cli_io_handler_add_crtlist(struct appctx *appctx)
1092{
Willy Tarreau6b6c3632022-05-05 13:43:49 +02001093 struct add_crtlist_ctx *ctx = appctx->svcctx;
William Lallemandc756bbd2020-05-13 17:23:59 +02001094 struct bind_conf_list *bind_conf_node;
Willy Tarreauc12b3212022-05-27 11:08:15 +02001095 struct stconn *sc = appctx_sc(appctx);
Willy Tarreau6b6c3632022-05-05 13:43:49 +02001096 struct crtlist *crtlist = ctx->crtlist;
1097 struct crtlist_entry *entry = ctx->entry;
William Lallemandc756bbd2020-05-13 17:23:59 +02001098 struct ckch_store *store = entry->node.key;
William Lallemandc756bbd2020-05-13 17:23:59 +02001099 struct ckch_inst *new_inst;
William Lallemandc756bbd2020-05-13 17:23:59 +02001100 int i = 0;
1101 int errcode = 0;
1102
William Lallemandc756bbd2020-05-13 17:23:59 +02001103 /* for each bind_conf which use the crt-list, a new ckch_inst must be
1104 * created.
1105 */
Christopher Fauletda89e9b2023-01-04 14:11:10 +01001106 if (unlikely(sc_ic(sc)->flags & CF_SHUTW))
Christopher Fauletc642d7c2022-06-01 16:31:09 +02001107 goto end;
William Lallemandc756bbd2020-05-13 17:23:59 +02001108
Willy Tarreaufa11df52022-05-05 13:48:40 +02001109 switch (ctx->state) {
1110 case ADDCRT_ST_INIT:
Willy Tarreau1b948ef2022-05-05 13:50:46 +02001111 /* This state just print the update message */
Christopher Fauletc642d7c2022-06-01 16:31:09 +02001112 chunk_printf(&trash, "Inserting certificate '%s' in crt-list '%s'", store->path, crtlist->node.key);
1113 if (applet_putchk(appctx, &trash) == -1)
Willy Tarreau1b948ef2022-05-05 13:50:46 +02001114 goto yield;
Willy Tarreaufa11df52022-05-05 13:48:40 +02001115 ctx->state = ADDCRT_ST_GEN;
Willy Tarreauaef84482022-11-14 07:03:16 +01001116 __fallthrough;
Willy Tarreaufa11df52022-05-05 13:48:40 +02001117 case ADDCRT_ST_GEN:
Willy Tarreau6b6c3632022-05-05 13:43:49 +02001118 bind_conf_node = ctx->bind_conf_node; /* get the previous ptr from the yield */
Willy Tarreau1b948ef2022-05-05 13:50:46 +02001119 if (bind_conf_node == NULL)
1120 bind_conf_node = crtlist->bind_conf;
1121 for (; bind_conf_node; bind_conf_node = bind_conf_node->next) {
1122 struct bind_conf *bind_conf = bind_conf_node->bind_conf;
1123 struct sni_ctx *sni;
1124
Christopher Fauletc642d7c2022-06-01 16:31:09 +02001125 ctx->bind_conf_node = bind_conf_node;
1126
Willy Tarreau1b948ef2022-05-05 13:50:46 +02001127 /* yield every 10 generations */
1128 if (i > 10) {
Christopher Fauletc642d7c2022-06-01 16:31:09 +02001129 applet_have_more_data(appctx); /* let's come back later */
Willy Tarreau1b948ef2022-05-05 13:50:46 +02001130 goto yield;
1131 }
William Lallemandc756bbd2020-05-13 17:23:59 +02001132
Christopher Fauletc642d7c2022-06-01 16:31:09 +02001133 /* display one dot for each new instance */
1134 if (applet_putstr(appctx, ".") == -1)
1135 goto yield;
1136
Willy Tarreau1b948ef2022-05-05 13:50:46 +02001137 /* we don't support multi-cert bundles, only simple ones */
Christopher Fauletc642d7c2022-06-01 16:31:09 +02001138 ctx->err = NULL;
1139 errcode |= ckch_inst_new_load_store(store->path, store, bind_conf, entry->ssl_conf, entry->filters, entry->fcount, &new_inst, &ctx->err);
1140 if (errcode & ERR_CODE) {
1141 ctx->state = ADDCRT_ST_ERROR;
Willy Tarreau1b948ef2022-05-05 13:50:46 +02001142 goto error;
Christopher Fauletc642d7c2022-06-01 16:31:09 +02001143 }
William Lallemandc756bbd2020-05-13 17:23:59 +02001144
Willy Tarreau1b948ef2022-05-05 13:50:46 +02001145 /* we need to initialize the SSL_CTX generated */
1146 /* this iterate on the newly generated SNIs in the new instance to prepare their SSL_CTX */
1147 list_for_each_entry(sni, &new_inst->sni_ctx, by_ckch_inst) {
1148 if (!sni->order) { /* we initialized only the first SSL_CTX because it's the same in the other sni_ctx's */
Christopher Fauletc642d7c2022-06-01 16:31:09 +02001149 ctx->err = NULL;
1150 errcode |= ssl_sock_prep_ctx_and_inst(bind_conf, new_inst->ssl_conf, sni->ctx, sni->ckch_inst, &ctx->err);
1151 if (errcode & ERR_CODE) {
1152 ctx->state = ADDCRT_ST_ERROR;
William Lallemandc756bbd2020-05-13 17:23:59 +02001153 goto error;
Christopher Fauletc642d7c2022-06-01 16:31:09 +02001154 }
William Lallemandc756bbd2020-05-13 17:23:59 +02001155 }
Willy Tarreau1b948ef2022-05-05 13:50:46 +02001156 }
Christopher Fauletc642d7c2022-06-01 16:31:09 +02001157
Willy Tarreau1b948ef2022-05-05 13:50:46 +02001158 i++;
1159 LIST_APPEND(&store->ckch_inst, &new_inst->by_ckchs);
1160 LIST_APPEND(&entry->ckch_inst, &new_inst->by_crtlist_entry);
1161 new_inst->crtlist_entry = entry;
William Lallemandc756bbd2020-05-13 17:23:59 +02001162 }
Willy Tarreaufa11df52022-05-05 13:48:40 +02001163 ctx->state = ADDCRT_ST_INSERT;
Willy Tarreauaef84482022-11-14 07:03:16 +01001164 __fallthrough;
Willy Tarreaufa11df52022-05-05 13:48:40 +02001165 case ADDCRT_ST_INSERT:
William Lallemandcb6c5f42022-06-20 16:51:53 +02001166 /* the insertion is called for every instance of the store, not
1167 * only the one we generated.
1168 * But the ssl_sock_load_cert_sni() skip the sni already
1169 * inserted. Not every instance has a bind_conf, it could be
1170 * the store of a server so we should be careful */
1171
Willy Tarreau1b948ef2022-05-05 13:50:46 +02001172 list_for_each_entry(new_inst, &store->ckch_inst, by_ckchs) {
William Lallemandcb6c5f42022-06-20 16:51:53 +02001173 if (!new_inst->bind_conf) /* this is a server instance */
1174 continue;
Willy Tarreau1b948ef2022-05-05 13:50:46 +02001175 HA_RWLOCK_WRLOCK(SNI_LOCK, &new_inst->bind_conf->sni_lock);
1176 ssl_sock_load_cert_sni(new_inst, new_inst->bind_conf);
1177 HA_RWLOCK_WRUNLOCK(SNI_LOCK, &new_inst->bind_conf->sni_lock);
1178 }
1179 entry->linenum = ++crtlist->linecount;
Christopher Fauletc642d7c2022-06-01 16:31:09 +02001180 ctx->entry = NULL;
1181 ctx->state = ADDCRT_ST_SUCCESS;
Willy Tarreauaef84482022-11-14 07:03:16 +01001182 __fallthrough;
Christopher Fauletc642d7c2022-06-01 16:31:09 +02001183 case ADDCRT_ST_SUCCESS:
1184 chunk_reset(&trash);
1185 chunk_appendf(&trash, "\n");
1186 if (ctx->err)
1187 chunk_appendf(&trash, "%s", ctx->err);
1188 chunk_appendf(&trash, "Success!\n");
1189 if (applet_putchk(appctx, &trash) == -1)
1190 goto yield;
1191 ctx->state = ADDCRT_ST_FIN;
1192 break;
1193
1194 case ADDCRT_ST_ERROR:
1195 error:
1196 chunk_printf(&trash, "\n%sFailed!\n", ctx->err);
1197 if (applet_putchk(appctx, &trash) == -1)
1198 goto yield;
1199 break;
1200
Willy Tarreaufa11df52022-05-05 13:48:40 +02001201 default:
1202 break;
William Lallemandc756bbd2020-05-13 17:23:59 +02001203 }
1204
Christopher Fauletc642d7c2022-06-01 16:31:09 +02001205end:
William Lallemandc756bbd2020-05-13 17:23:59 +02001206 /* success: call the release function and don't come back */
1207 return 1;
1208yield:
William Lallemandc756bbd2020-05-13 17:23:59 +02001209 return 0; /* should come back */
William Lallemandc756bbd2020-05-13 17:23:59 +02001210}
1211
1212
1213/*
1214 * Parse a "add ssl crt-list <crt-list> <certfile>" line.
Willy Tarreau6b6c3632022-05-05 13:43:49 +02001215 * Filters and option must be passed through payload.
1216 * It sets a struct add_crtlist_ctx.
William Lallemandc756bbd2020-05-13 17:23:59 +02001217 */
1218static int cli_parse_add_crtlist(char **args, char *payload, struct appctx *appctx, void *private)
1219{
Willy Tarreau6b6c3632022-05-05 13:43:49 +02001220 struct add_crtlist_ctx *ctx = applet_reserve_svcctx(appctx, sizeof(*ctx));
William Lallemandc756bbd2020-05-13 17:23:59 +02001221 int cfgerr = 0;
1222 struct ckch_store *store;
1223 char *err = NULL;
1224 char path[MAXPATHLEN+1];
1225 char *crtlist_path;
1226 char *cert_path = NULL;
1227 struct ebmb_node *eb;
1228 struct ebpt_node *inserted;
1229 struct crtlist *crtlist;
1230 struct crtlist_entry *entry = NULL;
William Lallemand99cc2182020-06-25 15:19:51 +02001231 char *end;
William Lallemandc756bbd2020-05-13 17:23:59 +02001232
1233 if (!cli_has_level(appctx, ACCESS_LVL_ADMIN))
1234 return 1;
1235
1236 if (!*args[3] || (!payload && !*args[4]))
1237 return cli_err(appctx, "'add ssl crtlist' expects a filename and a certificate name\n");
1238
1239 crtlist_path = args[3];
1240
William Lallemand99cc2182020-06-25 15:19:51 +02001241 /* strip trailing slashes, including first one */
1242 for (end = crtlist_path + strlen(crtlist_path) - 1; end >= crtlist_path && *end == '/'; end--)
1243 *end = 0;
1244
William Lallemandc756bbd2020-05-13 17:23:59 +02001245 if (HA_SPIN_TRYLOCK(CKCH_LOCK, &ckch_lock))
1246 return cli_err(appctx, "Operations on certificates are currently locked!\n");
1247
1248 eb = ebst_lookup(&crtlists_tree, crtlist_path);
1249 if (!eb) {
1250 memprintf(&err, "crt-list '%s' does not exist!", crtlist_path);
1251 goto error;
1252 }
1253 crtlist = ebmb_entry(eb, struct crtlist, node);
1254
1255 entry = crtlist_entry_new();
1256 if (entry == NULL) {
1257 memprintf(&err, "Not enough memory!");
1258 goto error;
1259 }
1260
1261 if (payload) {
1262 char *lf;
1263
1264 lf = strrchr(payload, '\n');
1265 if (lf) {
1266 memprintf(&err, "only one line of payload is supported!");
1267 goto error;
1268 }
1269 /* cert_path is filled here */
Remi Tricot-Le Bretonfb00f312021-03-23 16:41:53 +01001270 cfgerr |= crtlist_parse_line(payload, &cert_path, entry, "CLI", 1, 1, &err);
William Lallemandc756bbd2020-05-13 17:23:59 +02001271 if (cfgerr & ERR_CODE)
1272 goto error;
1273 } else {
1274 cert_path = args[4];
1275 }
1276
1277 if (!cert_path) {
1278 memprintf(&err, "'add ssl crtlist' should contain the certificate name in the payload");
1279 cfgerr |= ERR_ALERT | ERR_FATAL;
1280 goto error;
1281 }
1282
1283 if (eb_gettag(crtlist->entries.b[EB_RGHT])) {
1284 char *slash;
1285
1286 slash = strrchr(cert_path, '/');
1287 if (!slash) {
1288 memprintf(&err, "'%s' is a directory, certificate path '%s' must contain the directory path", (char *)crtlist->node.key, cert_path);
1289 goto error;
1290 }
1291 /* temporary replace / by 0 to do an strcmp */
1292 *slash = '\0';
1293 if (strcmp(cert_path, (char*)crtlist->node.key) != 0) {
1294 *slash = '/';
1295 memprintf(&err, "'%s' is a directory, certificate path '%s' must contain the directory path", (char *)crtlist->node.key, cert_path);
1296 goto error;
1297 }
1298 *slash = '/';
1299 }
1300
1301 if (*cert_path != '/' && global_ssl.crt_base) {
Willy Tarreau393e42a2022-05-09 10:31:28 +02001302 if ((strlen(global_ssl.crt_base) + 1 + strlen(cert_path)) > sizeof(path) ||
1303 snprintf(path, sizeof(path), "%s/%s", global_ssl.crt_base, cert_path) > sizeof(path)) {
William Lallemandc756bbd2020-05-13 17:23:59 +02001304 memprintf(&err, "'%s' : path too long", cert_path);
1305 cfgerr |= ERR_ALERT | ERR_FATAL;
1306 goto error;
1307 }
William Lallemandc756bbd2020-05-13 17:23:59 +02001308 cert_path = path;
1309 }
1310
1311 store = ckchs_lookup(cert_path);
1312 if (store == NULL) {
1313 memprintf(&err, "certificate '%s' does not exist!", cert_path);
1314 goto error;
1315 }
William Lallemand52ddd992022-11-22 11:51:53 +01001316 if (store->data == NULL || store->data->cert == NULL) {
William Lallemandc756bbd2020-05-13 17:23:59 +02001317 memprintf(&err, "certificate '%s' is empty!", cert_path);
1318 goto error;
1319 }
1320
1321 /* check if it's possible to insert this new crtlist_entry */
1322 entry->node.key = store;
1323 inserted = ebpt_insert(&crtlist->entries, &entry->node);
1324 if (inserted != &entry->node) {
1325 memprintf(&err, "file already exists in this directory!");
1326 goto error;
1327 }
1328
1329 /* this is supposed to be a directory (EB_ROOT_UNIQUE), so no ssl_conf are allowed */
1330 if ((entry->ssl_conf || entry->filters) && eb_gettag(crtlist->entries.b[EB_RGHT])) {
1331 memprintf(&err, "this is a directory, SSL configuration and filters are not allowed");
1332 goto error;
1333 }
1334
Willy Tarreau2b718102021-04-21 07:32:39 +02001335 LIST_APPEND(&crtlist->ord_entries, &entry->by_crtlist);
William Lallemandc756bbd2020-05-13 17:23:59 +02001336 entry->crtlist = crtlist;
Willy Tarreau2b718102021-04-21 07:32:39 +02001337 LIST_APPEND(&store->crtlist_entry, &entry->by_ckch_store);
William Lallemandc756bbd2020-05-13 17:23:59 +02001338
Willy Tarreaufa11df52022-05-05 13:48:40 +02001339 ctx->state = ADDCRT_ST_INIT;
Willy Tarreau6b6c3632022-05-05 13:43:49 +02001340 ctx->crtlist = crtlist;
1341 ctx->entry = entry;
William Lallemandc756bbd2020-05-13 17:23:59 +02001342
1343 /* unlock is done in the release handler */
1344 return 0;
1345
1346error:
1347 crtlist_entry_free(entry);
1348 HA_SPIN_UNLOCK(CKCH_LOCK, &ckch_lock);
1349 err = memprintf(&err, "Can't edit the crt-list: %s\n", err ? err : "");
1350 return cli_dynerr(appctx, err);
1351}
1352
1353/* Parse a "del ssl crt-list <crt-list> <certfile>" line. */
1354static int cli_parse_del_crtlist(char **args, char *payload, struct appctx *appctx, void *private)
1355{
1356 struct ckch_store *store;
1357 char *err = NULL;
1358 char *crtlist_path, *cert_path;
1359 struct ebmb_node *ebmb;
1360 struct ebpt_node *ebpt;
1361 struct crtlist *crtlist;
1362 struct crtlist_entry *entry = NULL;
1363 struct ckch_inst *inst, *inst_s;
1364 int linenum = 0;
1365 char *colons;
William Lallemand99cc2182020-06-25 15:19:51 +02001366 char *end;
Remi Tricot-Le Bretonbc2c3862021-03-26 10:47:50 +01001367 int error_message_dumped = 0;
William Lallemandc756bbd2020-05-13 17:23:59 +02001368
1369 if (!cli_has_level(appctx, ACCESS_LVL_ADMIN))
1370 return 1;
1371
1372 if (!*args[3] || !*args[4])
1373 return cli_err(appctx, "'del ssl crtlist' expects a filename and a certificate name\n");
1374
1375 if (HA_SPIN_TRYLOCK(CKCH_LOCK, &ckch_lock))
1376 return cli_err(appctx, "Can't delete!\nOperations on certificates are currently locked!\n");
1377
1378 crtlist_path = args[3];
1379 cert_path = args[4];
1380
1381 colons = strchr(cert_path, ':');
1382 if (colons) {
1383 char *endptr;
1384
1385 linenum = strtol(colons + 1, &endptr, 10);
1386 if (colons + 1 == endptr || *endptr != '\0') {
1387 memprintf(&err, "wrong line number after colons in '%s'!", cert_path);
1388 goto error;
1389 }
1390 *colons = '\0';
1391 }
William Lallemand99cc2182020-06-25 15:19:51 +02001392
1393 /* strip trailing slashes, including first one */
1394 for (end = crtlist_path + strlen(crtlist_path) - 1; end >= crtlist_path && *end == '/'; end--)
1395 *end = 0;
1396
William Lallemandc756bbd2020-05-13 17:23:59 +02001397 /* look for crtlist */
1398 ebmb = ebst_lookup(&crtlists_tree, crtlist_path);
1399 if (!ebmb) {
1400 memprintf(&err, "crt-list '%s' does not exist!", crtlist_path);
1401 goto error;
1402 }
1403 crtlist = ebmb_entry(ebmb, struct crtlist, node);
1404
1405 /* look for store */
1406 store = ckchs_lookup(cert_path);
1407 if (store == NULL) {
1408 memprintf(&err, "certificate '%s' does not exist!", cert_path);
1409 goto error;
1410 }
William Lallemand52ddd992022-11-22 11:51:53 +01001411 if (store->data == NULL || store->data->cert == NULL) {
William Lallemandc756bbd2020-05-13 17:23:59 +02001412 memprintf(&err, "certificate '%s' is empty!", cert_path);
1413 goto error;
1414 }
1415
1416 ebpt = ebpt_lookup(&crtlist->entries, store);
1417 if (!ebpt) {
1418 memprintf(&err, "certificate '%s' can't be found in crt-list '%s'!", cert_path, crtlist_path);
1419 goto error;
1420 }
1421
1422 /* list the line number of entries for errors in err, and select the right ebpt */
1423 for (; ebpt; ebpt = ebpt_next_dup(ebpt)) {
1424 struct crtlist_entry *tmp;
1425
1426 tmp = ebpt_entry(ebpt, struct crtlist_entry, node);
1427 memprintf(&err, "%s%s%d", err ? err : "", err ? ", " : "", tmp->linenum);
1428
1429 /* select the entry we wanted */
1430 if (linenum == 0 || tmp->linenum == linenum) {
1431 if (!entry)
1432 entry = tmp;
1433 }
1434 }
1435
1436 /* we didn't found the specified entry */
1437 if (!entry) {
1438 memprintf(&err, "found a certificate '%s' but the line number is incorrect, please specify a correct line number preceded by colons (%s)!", cert_path, err ? err : NULL);
1439 goto error;
1440 }
1441
1442 /* we didn't specified a line number but there were several entries */
1443 if (linenum == 0 && ebpt_next_dup(&entry->node)) {
1444 memprintf(&err, "found the certificate '%s' in several entries, please specify a line number preceded by colons (%s)!", cert_path, err ? err : NULL);
1445 goto error;
1446 }
1447
Remi Tricot-Le Bretonbc2c3862021-03-26 10:47:50 +01001448 /* Iterate over all the instances in order to see if any of them is a
1449 * default instance. If this is the case, the entry won't be suppressed. */
1450 list_for_each_entry_safe(inst, inst_s, &entry->ckch_inst, by_crtlist_entry) {
1451 if (inst->is_default && !inst->bind_conf->strict_sni) {
1452 if (!error_message_dumped) {
1453 memprintf(&err, "certificate '%s' cannot be deleted, it is used as default certificate by the following frontends:\n", cert_path);
1454 error_message_dumped = 1;
1455 }
1456 memprintf(&err, "%s\t- %s:%d\n", err, inst->bind_conf->file, inst->bind_conf->line);
1457 }
1458 }
1459 if (error_message_dumped)
1460 goto error;
1461
William Lallemandc756bbd2020-05-13 17:23:59 +02001462 /* upon error free the ckch_inst and everything inside */
1463
1464 ebpt_delete(&entry->node);
Willy Tarreau2b718102021-04-21 07:32:39 +02001465 LIST_DELETE(&entry->by_crtlist);
1466 LIST_DELETE(&entry->by_ckch_store);
William Lallemandc756bbd2020-05-13 17:23:59 +02001467
1468 list_for_each_entry_safe(inst, inst_s, &entry->ckch_inst, by_crtlist_entry) {
1469 struct sni_ctx *sni, *sni_s;
Remi Tricot-Le Breton4458b972021-02-19 17:41:55 +01001470 struct ckch_inst_link_ref *link_ref, *link_ref_s;
William Lallemandc756bbd2020-05-13 17:23:59 +02001471
1472 HA_RWLOCK_WRLOCK(SNI_LOCK, &inst->bind_conf->sni_lock);
1473 list_for_each_entry_safe(sni, sni_s, &inst->sni_ctx, by_ckch_inst) {
1474 ebmb_delete(&sni->name);
Willy Tarreau2b718102021-04-21 07:32:39 +02001475 LIST_DELETE(&sni->by_ckch_inst);
William Lallemandc756bbd2020-05-13 17:23:59 +02001476 SSL_CTX_free(sni->ctx);
1477 free(sni);
1478 }
1479 HA_RWLOCK_WRUNLOCK(SNI_LOCK, &inst->bind_conf->sni_lock);
Willy Tarreau2b718102021-04-21 07:32:39 +02001480 LIST_DELETE(&inst->by_ckchs);
Remi Tricot-Le Breton4458b972021-02-19 17:41:55 +01001481 list_for_each_entry_safe(link_ref, link_ref_s, &inst->cafile_link_refs, list) {
1482 LIST_DELETE(&link_ref->link->list);
1483 LIST_DELETE(&link_ref->list);
1484 free(link_ref);
1485 }
William Lallemandc756bbd2020-05-13 17:23:59 +02001486 free(inst);
1487 }
1488
1489 crtlist_free_filters(entry->filters);
1490 ssl_sock_free_ssl_conf(entry->ssl_conf);
1491 free(entry->ssl_conf);
1492 free(entry);
1493
1494 HA_SPIN_UNLOCK(CKCH_LOCK, &ckch_lock);
1495 err = memprintf(&err, "Entry '%s' deleted in crtlist '%s'!\n", cert_path, crtlist_path);
1496 return cli_dynmsg(appctx, LOG_NOTICE, err);
1497
1498error:
1499 HA_SPIN_UNLOCK(CKCH_LOCK, &ckch_lock);
1500 err = memprintf(&err, "Can't delete the entry: %s\n", err ? err : "");
1501 return cli_dynerr(appctx, err);
1502}
1503
1504
William Lallemandee8530c2020-06-23 18:19:42 +02001505/* unlink and free all crt-list and crt-list entries */
1506void crtlist_deinit()
1507{
1508 struct eb_node *node, *next;
1509 struct crtlist *crtlist;
1510
1511 node = eb_first(&crtlists_tree);
1512 while (node) {
1513 next = eb_next(node);
1514 crtlist = ebmb_entry(node, struct crtlist, node);
1515 crtlist_free(crtlist);
1516 node = next;
1517 }
1518}
1519
William Lallemandc756bbd2020-05-13 17:23:59 +02001520
1521/* register cli keywords */
1522static struct cli_kw_list cli_kws = {{ },{
Willy Tarreaub205bfd2021-05-07 11:38:37 +02001523 { { "add", "ssl", "crt-list", NULL }, "add ssl crt-list <list> <cert> [opts]* : add to crt-list file <list> a line <cert> or a payload", cli_parse_add_crtlist, cli_io_handler_add_crtlist, cli_release_add_crtlist },
1524 { { "del", "ssl", "crt-list", NULL }, "del ssl crt-list <list> <cert[:line]> : delete a line <cert> from crt-list file <list>", cli_parse_del_crtlist, NULL, NULL },
1525 { { "show", "ssl", "crt-list", NULL }, "show ssl crt-list [-n] [<list>] : show the list of crt-lists or the content of a crt-list file <list>", cli_parse_dump_crtlist, cli_io_handler_dump_crtlist, NULL },
William Lallemandc756bbd2020-05-13 17:23:59 +02001526 { { NULL }, NULL, NULL, NULL } }
1527};
1528
1529INITCALL1(STG_REGISTER, cli_register_kw, &cli_kws);
1530