blob: 825f38047552a81dd8948499861cf7811c1bde41 [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) {
406 ssl_conf = calloc(1, sizeof *ssl_conf);
407 if (!ssl_conf) {
408 memprintf(err, "not enough memory!");
409 cfgerr |= ERR_ALERT | ERR_FATAL;
410 goto error;
411 }
412 }
Remi Tricot-Le Bretonfb00f312021-03-23 16:41:53 +0100413
William Lallemand6e9556b2020-05-12 17:52:44 +0200414 cur_arg = ssl_b ? ssl_b : 1;
415 while (cur_arg < ssl_e) {
416 newarg = 0;
417 for (i = 0; ssl_bind_kws[i].kw != NULL; i++) {
418 if (strcmp(ssl_bind_kws[i].kw, args[cur_arg]) == 0) {
419 newarg = 1;
Remi Tricot-Le Bretonfb00f312021-03-23 16:41:53 +0100420 cfgerr |= ssl_bind_kws[i].parse(args, cur_arg, NULL, ssl_conf, from_cli, err);
William Lallemand6e9556b2020-05-12 17:52:44 +0200421 if (cur_arg + 1 + ssl_bind_kws[i].skip > ssl_e) {
Tim Duesterhus6d07fae2020-09-29 18:00:27 +0200422 memprintf(err, "parsing [%s:%d]: ssl args out of '[]' for %s",
423 file, linenum, args[cur_arg]);
William Lallemand6e9556b2020-05-12 17:52:44 +0200424 cfgerr |= ERR_ALERT | ERR_FATAL;
425 goto error;
426 }
427 cur_arg += 1 + ssl_bind_kws[i].skip;
428 break;
429 }
430 }
431 if (!cfgerr && !newarg) {
Tim Duesterhus6d07fae2020-09-29 18:00:27 +0200432 memprintf(err, "parsing [%s:%d]: unknown ssl keyword %s",
433 file, linenum, args[cur_arg]);
William Lallemand6e9556b2020-05-12 17:52:44 +0200434 cfgerr |= ERR_ALERT | ERR_FATAL;
435 goto error;
436 }
437 }
438 entry->linenum = linenum;
439 entry->ssl_conf = ssl_conf;
440 entry->filters = crtlist_dup_filters(&args[cur_arg], arg - cur_arg - 1);
441 entry->fcount = arg - cur_arg - 1;
442
443 return cfgerr;
444
445error:
446 crtlist_free_filters(entry->filters);
447 entry->filters = NULL;
448 ssl_sock_free_ssl_conf(entry->ssl_conf);
Willy Tarreau61cfdf42021-02-20 10:46:51 +0100449 ha_free(&entry->ssl_conf);
William Lallemand6e9556b2020-05-12 17:52:44 +0200450 return cfgerr;
451}
452
453
454
455/* This function parse a crt-list file and store it in a struct crtlist, each line is a crtlist_entry structure
456 * Fill the <crtlist> argument with a pointer to a new crtlist struct
457 *
458 * This function tries to open and store certificate files.
459 */
460int crtlist_parse_file(char *file, struct bind_conf *bind_conf, struct proxy *curproxy, struct crtlist **crtlist, char **err)
461{
462 struct crtlist *newlist;
463 struct crtlist_entry *entry = NULL;
464 char thisline[CRT_LINESIZE];
William Lallemand6e9556b2020-05-12 17:52:44 +0200465 FILE *f;
466 struct stat buf;
467 int linenum = 0;
468 int cfgerr = 0;
Tim Duesterhusb9f6acc2020-09-29 18:00:28 +0200469 int missing_lf = -1;
William Lallemand6e9556b2020-05-12 17:52:44 +0200470
471 if ((f = fopen(file, "r")) == NULL) {
472 memprintf(err, "cannot open file '%s' : %s", file, strerror(errno));
473 return ERR_ALERT | ERR_FATAL;
474 }
475
476 newlist = crtlist_new(file, 0);
477 if (newlist == NULL) {
478 memprintf(err, "Not enough memory!");
479 cfgerr |= ERR_ALERT | ERR_FATAL;
480 goto error;
481 }
482
483 while (fgets(thisline, sizeof(thisline), f) != NULL) {
484 char *end;
485 char *line = thisline;
486 char *crt_path;
William Lallemand86c2dd62020-11-20 14:23:38 +0100487 char path[MAXPATHLEN+1];
William Lallemand6e9556b2020-05-12 17:52:44 +0200488 struct ckch_store *ckchs;
William Lallemand77e1c6f2020-11-20 18:26:09 +0100489 int found = 0;
William Lallemand6e9556b2020-05-12 17:52:44 +0200490
Tim Duesterhusb9f6acc2020-09-29 18:00:28 +0200491 if (missing_lf != -1) {
492 memprintf(err, "parsing [%s:%d]: Stray NUL character at position %d.\n",
493 file, linenum, (missing_lf + 1));
494 cfgerr |= ERR_ALERT | ERR_FATAL;
495 missing_lf = -1;
496 break;
497 }
498
William Lallemand6e9556b2020-05-12 17:52:44 +0200499 linenum++;
500 end = line + strlen(line);
501 if (end-line == sizeof(thisline)-1 && *(end-1) != '\n') {
502 /* Check if we reached the limit and the last char is not \n.
503 * Watch out for the last line without the terminating '\n'!
504 */
Tim Duesterhus6d07fae2020-09-29 18:00:27 +0200505 memprintf(err, "parsing [%s:%d]: line too long, limit is %d characters",
506 file, linenum, (int)sizeof(thisline)-1);
William Lallemand6e9556b2020-05-12 17:52:44 +0200507 cfgerr |= ERR_ALERT | ERR_FATAL;
508 break;
509 }
510
511 if (*line == '#' || *line == '\n' || *line == '\r')
512 continue;
513
Tim Duesterhusb9f6acc2020-09-29 18:00:28 +0200514 if (end > line && *(end-1) == '\n') {
515 /* kill trailing LF */
516 *(end - 1) = 0;
517 }
518 else {
519 /* mark this line as truncated */
520 missing_lf = end - line;
521 }
522
William Lallemand6e9556b2020-05-12 17:52:44 +0200523 entry = crtlist_entry_new();
524 if (entry == NULL) {
525 memprintf(err, "Not enough memory!");
526 cfgerr |= ERR_ALERT | ERR_FATAL;
527 goto error;
528 }
Tim Duesterhusb9f6acc2020-09-29 18:00:28 +0200529
Remi Tricot-Le Bretonfb00f312021-03-23 16:41:53 +0100530 cfgerr |= crtlist_parse_line(thisline, &crt_path, entry, file, linenum, 0, err);
William Lallemand20b0fed2020-09-28 15:45:16 +0200531 if (cfgerr & ERR_CODE)
William Lallemand6e9556b2020-05-12 17:52:44 +0200532 goto error;
533
534 /* empty line */
535 if (!crt_path || !*crt_path) {
536 crtlist_entry_free(entry);
537 entry = NULL;
538 continue;
539 }
540
541 if (*crt_path != '/' && global_ssl.crt_base) {
Willy Tarreau393e42a2022-05-09 10:31:28 +0200542 if ((strlen(global_ssl.crt_base) + 1 + strlen(crt_path)) > sizeof(path) ||
Willy Tarreau63fc9002022-05-09 21:14:04 +0200543 snprintf(path, sizeof(path), "%s/%s", global_ssl.crt_base, crt_path) > sizeof(path)) {
Tim Duesterhus6d07fae2020-09-29 18:00:27 +0200544 memprintf(err, "parsing [%s:%d]: '%s' : path too long",
545 file, linenum, crt_path);
William Lallemand6e9556b2020-05-12 17:52:44 +0200546 cfgerr |= ERR_ALERT | ERR_FATAL;
547 goto error;
548 }
William Lallemand6e9556b2020-05-12 17:52:44 +0200549 crt_path = path;
550 }
551
552 /* Look for a ckch_store or create one */
553 ckchs = ckchs_lookup(crt_path);
554 if (ckchs == NULL) {
William Lallemand47da8212020-09-10 19:13:27 +0200555 if (stat(crt_path, &buf) == 0) {
William Lallemand77e1c6f2020-11-20 18:26:09 +0100556 found++;
William Lallemand47da8212020-09-10 19:13:27 +0200557
William Lallemandbd8e6ed2020-09-16 16:08:08 +0200558 ckchs = ckchs_load_cert_file(crt_path, err);
William Lallemand47da8212020-09-10 19:13:27 +0200559 if (ckchs == NULL) {
560 cfgerr |= ERR_ALERT | ERR_FATAL;
561 goto error;
562 }
563
564 entry->node.key = ckchs;
565 entry->crtlist = newlist;
Remi Tricot-Le Bretonfb2b9982022-12-20 11:11:11 +0100566 if (entry->ssl_conf)
567 ckchs->data->ocsp_update_mode = entry->ssl_conf->ocsp_update;
William Lallemand47da8212020-09-10 19:13:27 +0200568 ebpt_insert(&newlist->entries, &entry->node);
Willy Tarreau2b718102021-04-21 07:32:39 +0200569 LIST_APPEND(&newlist->ord_entries, &entry->by_crtlist);
570 LIST_APPEND(&ckchs->crtlist_entry, &entry->by_ckch_store);
William Lallemand6e9556b2020-05-12 17:52:44 +0200571
William Lallemand73404572020-11-20 18:23:40 +0100572 } else if (global_ssl.extra_files & SSL_GF_BUNDLE) {
William Lallemand47da8212020-09-10 19:13:27 +0200573 /* If we didn't find the file, this could be a
William Lallemand51f784b2020-10-02 18:08:18 +0200574 bundle, since 2.3 we don't support multiple
575 certificate in the same OpenSSL store, so we
576 emulate it by loading each file separately. To
577 do so we need to duplicate the entry in the
578 crt-list because it becomes independent */
William Lallemand47da8212020-09-10 19:13:27 +0200579 char fp[MAXPATHLEN+1] = {0};
580 int n = 0;
581 struct crtlist_entry *entry_dup = entry; /* use the previous created entry */
582 for (n = 0; n < SSL_SOCK_NUM_KEYTYPES; n++) {
583 struct stat buf;
584 int ret;
585
William Lallemand86c2dd62020-11-20 14:23:38 +0100586 ret = snprintf(fp, sizeof(fp), "%s.%s", crt_path, SSL_SOCK_KEYTYPE_NAMES[n]);
William Lallemand47da8212020-09-10 19:13:27 +0200587 if (ret > sizeof(fp))
588 continue;
589
590 ckchs = ckchs_lookup(fp);
William Lallemand77e1c6f2020-11-20 18:26:09 +0100591 if (!ckchs) {
592 if (stat(fp, &buf) == 0) {
593 ckchs = ckchs_load_cert_file(fp, err);
594 if (!ckchs) {
William Lallemand47da8212020-09-10 19:13:27 +0200595 cfgerr |= ERR_ALERT | ERR_FATAL;
596 goto error;
597 }
William Lallemand77e1c6f2020-11-20 18:26:09 +0100598 } else {
599 continue; /* didn't find this extension, skip */
600 }
601 }
602 found++;
603 linenum++; /* we duplicate the line for this entry in the bundle */
604 if (!entry_dup) { /* if the entry was used, duplicate one */
605 linenum++;
606 entry_dup = crtlist_entry_dup(entry);
607 if (!entry_dup) {
608 cfgerr |= ERR_ALERT | ERR_FATAL;
609 goto error;
William Lallemand47da8212020-09-10 19:13:27 +0200610 }
William Lallemand77e1c6f2020-11-20 18:26:09 +0100611 entry_dup->linenum = linenum;
612 }
William Lallemand47da8212020-09-10 19:13:27 +0200613
William Lallemand77e1c6f2020-11-20 18:26:09 +0100614 entry_dup->node.key = ckchs;
615 entry_dup->crtlist = newlist;
Remi Tricot-Le Bretonfc92b8b2023-01-09 12:02:46 +0100616 if (ckchs->data->ocsp_update_mode != SSL_SOCK_OCSP_UPDATE_DFLT || entry->ssl_conf) {
617 if ((!entry->ssl_conf && ckchs->data->ocsp_update_mode == SSL_SOCK_OCSP_UPDATE_ON)
Remi Tricot-Le Breton8c990812023-01-10 11:44:15 +0100618 || (entry->ssl_conf && ckchs->data->ocsp_update_mode != entry->ssl_conf->ocsp_update)) {
Remi Tricot-Le Bretonfb2b9982022-12-20 11:11:11 +0100619 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 +0100620 cfgerr |= ERR_ALERT | ERR_FATAL;
Remi Tricot-Le Bretonfb2b9982022-12-20 11:11:11 +0100621 }
Remi Tricot-Le Bretonfb2b9982022-12-20 11:11:11 +0100622 }
Remi Tricot-Le Bretonfc92b8b2023-01-09 12:02:46 +0100623 if (entry->ssl_conf)
624 ckchs->data->ocsp_update_mode = entry->ssl_conf->ocsp_update;
William Lallemand77e1c6f2020-11-20 18:26:09 +0100625 ebpt_insert(&newlist->entries, &entry_dup->node);
Willy Tarreau2b718102021-04-21 07:32:39 +0200626 LIST_APPEND(&newlist->ord_entries, &entry_dup->by_crtlist);
627 LIST_APPEND(&ckchs->crtlist_entry, &entry_dup->by_ckch_store);
William Lallemand47da8212020-09-10 19:13:27 +0200628
William Lallemand77e1c6f2020-11-20 18:26:09 +0100629 entry_dup = NULL; /* the entry was used, we need a new one next round */
William Lallemand47da8212020-09-10 19:13:27 +0200630 }
William Lallemandb7fdfdf2020-12-04 15:45:02 +0100631#if HA_OPENSSL_VERSION_NUMBER < 0x10101000L
632 if (found) {
633 memprintf(err, "%sCan't load '%s'. Loading a multi certificates bundle requires OpenSSL >= 1.1.1\n",
634 err && *err ? *err : "", crt_path);
635 cfgerr |= ERR_ALERT | ERR_FATAL;
636 }
637#endif
William Lallemand47da8212020-09-10 19:13:27 +0200638 }
William Lallemand77e1c6f2020-11-20 18:26:09 +0100639 if (!found) {
640 memprintf(err, "%sunable to stat SSL certificate from file '%s' : %s.\n",
641 err && *err ? *err : "", crt_path, strerror(errno));
642 cfgerr |= ERR_ALERT | ERR_FATAL;
643 }
644
William Lallemand50c03aa2020-11-06 16:24:07 +0100645 } else {
646 entry->node.key = ckchs;
647 entry->crtlist = newlist;
Remi Tricot-Le Bretonfc92b8b2023-01-09 12:02:46 +0100648 if (ckchs->data->ocsp_update_mode != SSL_SOCK_OCSP_UPDATE_DFLT || entry->ssl_conf) {
649 if ((!entry->ssl_conf && ckchs->data->ocsp_update_mode == SSL_SOCK_OCSP_UPDATE_ON)
Remi Tricot-Le Breton8c990812023-01-10 11:44:15 +0100650 || (entry->ssl_conf && ckchs->data->ocsp_update_mode != entry->ssl_conf->ocsp_update)) {
Remi Tricot-Le Bretonfb2b9982022-12-20 11:11:11 +0100651 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 +0100652 cfgerr |= ERR_ALERT | ERR_FATAL;
Remi Tricot-Le Bretonfb2b9982022-12-20 11:11:11 +0100653 }
Remi Tricot-Le Bretonfb2b9982022-12-20 11:11:11 +0100654 }
Remi Tricot-Le Bretonfc92b8b2023-01-09 12:02:46 +0100655 if (entry->ssl_conf)
656 ckchs->data->ocsp_update_mode = entry->ssl_conf->ocsp_update;
William Lallemand50c03aa2020-11-06 16:24:07 +0100657 ebpt_insert(&newlist->entries, &entry->node);
Willy Tarreau2b718102021-04-21 07:32:39 +0200658 LIST_APPEND(&newlist->ord_entries, &entry->by_crtlist);
659 LIST_APPEND(&ckchs->crtlist_entry, &entry->by_ckch_store);
William Lallemand77e1c6f2020-11-20 18:26:09 +0100660 found++;
William Lallemand47da8212020-09-10 19:13:27 +0200661 }
William Lallemand6e9556b2020-05-12 17:52:44 +0200662 entry = NULL;
663 }
Tim Duesterhusb9f6acc2020-09-29 18:00:28 +0200664
665 if (missing_lf != -1) {
666 memprintf(err, "parsing [%s:%d]: Missing LF on last line, file might have been truncated at position %d.\n",
667 file, linenum, (missing_lf + 1));
668 cfgerr |= ERR_ALERT | ERR_FATAL;
669 }
670
William Lallemand6e9556b2020-05-12 17:52:44 +0200671 if (cfgerr & ERR_CODE)
672 goto error;
673
674 newlist->linecount = linenum;
675
676 fclose(f);
677 *crtlist = newlist;
678
679 return cfgerr;
680error:
681 crtlist_entry_free(entry);
682
683 fclose(f);
684 crtlist_free(newlist);
685 return cfgerr;
686}
687
688/* This function reads a directory and stores it in a struct crtlist, each file is a crtlist_entry structure
689 * Fill the <crtlist> argument with a pointer to a new crtlist struct
690 *
691 * This function tries to open and store certificate files.
692 */
693int crtlist_load_cert_dir(char *path, struct bind_conf *bind_conf, struct crtlist **crtlist, char **err)
694{
695 struct crtlist *dir;
696 struct dirent **de_list;
697 int i, n;
698 struct stat buf;
699 char *end;
700 char fp[MAXPATHLEN+1];
701 int cfgerr = 0;
702 struct ckch_store *ckchs;
William Lallemand6e9556b2020-05-12 17:52:44 +0200703
704 dir = crtlist_new(path, 1);
705 if (dir == NULL) {
706 memprintf(err, "not enough memory");
707 return ERR_ALERT | ERR_FATAL;
708 }
709
710 n = scandir(path, &de_list, 0, alphasort);
711 if (n < 0) {
712 memprintf(err, "%sunable to scan directory '%s' : %s.\n",
713 err && *err ? *err : "", path, strerror(errno));
714 cfgerr |= ERR_ALERT | ERR_FATAL;
715 }
716 else {
717 for (i = 0; i < n; i++) {
718 struct crtlist_entry *entry;
719 struct dirent *de = de_list[i];
720
721 end = strrchr(de->d_name, '.');
William Lallemand589570d2022-05-09 10:30:51 +0200722 if (end && (de->d_name[0] == '.' ||
723 strcmp(end, ".issuer") == 0 || strcmp(end, ".ocsp") == 0 ||
724 strcmp(end, ".sctl") == 0 || strcmp(end, ".key") == 0))
William Lallemand6e9556b2020-05-12 17:52:44 +0200725 goto ignore_entry;
726
727 snprintf(fp, sizeof(fp), "%s/%s", path, de->d_name);
728 if (stat(fp, &buf) != 0) {
729 memprintf(err, "%sunable to stat SSL certificate from file '%s' : %s.\n",
730 err && *err ? *err : "", fp, strerror(errno));
731 cfgerr |= ERR_ALERT | ERR_FATAL;
732 goto ignore_entry;
733 }
734 if (!S_ISREG(buf.st_mode))
735 goto ignore_entry;
736
737 entry = crtlist_entry_new();
738 if (entry == NULL) {
739 memprintf(err, "not enough memory '%s'", fp);
740 cfgerr |= ERR_ALERT | ERR_FATAL;
741 goto ignore_entry;
742 }
743
William Lallemand6e9556b2020-05-12 17:52:44 +0200744 ckchs = ckchs_lookup(fp);
745 if (ckchs == NULL)
William Lallemandbd8e6ed2020-09-16 16:08:08 +0200746 ckchs = ckchs_load_cert_file(fp, err);
William Lallemand6e9556b2020-05-12 17:52:44 +0200747 if (ckchs == NULL) {
748 free(de);
749 free(entry);
750 cfgerr |= ERR_ALERT | ERR_FATAL;
751 goto end;
752 }
753 entry->node.key = ckchs;
754 entry->crtlist = dir;
Willy Tarreau2b718102021-04-21 07:32:39 +0200755 LIST_APPEND(&ckchs->crtlist_entry, &entry->by_ckch_store);
756 LIST_APPEND(&dir->ord_entries, &entry->by_crtlist);
William Lallemand6e9556b2020-05-12 17:52:44 +0200757 ebpt_insert(&dir->entries, &entry->node);
758
759ignore_entry:
760 free(de);
761 }
762end:
763 free(de_list);
764 }
765
766 if (cfgerr & ERR_CODE) {
767 /* free the dir and entries on error */
768 crtlist_free(dir);
769 } else {
770 *crtlist = dir;
771 }
772 return cfgerr;
773
774}
775
William Lallemandc756bbd2020-05-13 17:23:59 +0200776/*
777 * Take an ssl_bind_conf structure and append the configuration line used to
778 * create it in the buffer
779 */
780static void dump_crtlist_sslconf(struct buffer *buf, const struct ssl_bind_conf *conf)
781{
782 int space = 0;
783
784 if (conf == NULL)
785 return;
786
787 chunk_appendf(buf, " [");
788#ifdef OPENSSL_NPN_NEGOTIATED
789 if (conf->npn_str) {
790 int len = conf->npn_len;
791 char *ptr = conf->npn_str;
792 int comma = 0;
793
794 if (space) chunk_appendf(buf, " ");
795 chunk_appendf(buf, "npn ");
796 while (len) {
797 unsigned short size;
798
799 size = *ptr;
800 ptr++;
801 if (comma)
802 chunk_memcat(buf, ",", 1);
803 chunk_memcat(buf, ptr, size);
804 ptr += size;
805 len -= size + 1;
806 comma = 1;
807 }
808 chunk_memcat(buf, "", 1); /* finish with a \0 */
809 space++;
810 }
811#endif
812#ifdef TLSEXT_TYPE_application_layer_protocol_negotiation
813 if (conf->alpn_str) {
814 int len = conf->alpn_len;
815 char *ptr = conf->alpn_str;
816 int comma = 0;
817
818 if (space) chunk_appendf(buf, " ");
819 chunk_appendf(buf, "alpn ");
820 while (len) {
821 unsigned short size;
822
823 size = *ptr;
824 ptr++;
825 if (comma)
826 chunk_memcat(buf, ",", 1);
827 chunk_memcat(buf, ptr, size);
828 ptr += size;
829 len -= size + 1;
830 comma = 1;
831 }
832 chunk_memcat(buf, "", 1); /* finish with a \0 */
833 space++;
834 }
835#endif
836 /* verify */
837 {
838 if (conf->verify == SSL_SOCK_VERIFY_NONE) {
839 if (space) chunk_appendf(buf, " ");
840 chunk_appendf(buf, "verify none");
841 space++;
842 } else if (conf->verify == SSL_SOCK_VERIFY_OPTIONAL) {
843 if (space) chunk_appendf(buf, " ");
844 chunk_appendf(buf, "verify optional");
845 space++;
846 } else if (conf->verify == SSL_SOCK_VERIFY_REQUIRED) {
847 if (space) chunk_appendf(buf, " ");
848 chunk_appendf(buf, "verify required");
849 space++;
850 }
851 }
852
853 if (conf->no_ca_names) {
854 if (space) chunk_appendf(buf, " ");
855 chunk_appendf(buf, "no-ca-names");
856 space++;
857 }
858
859 if (conf->early_data) {
860 if (space) chunk_appendf(buf, " ");
861 chunk_appendf(buf, "allow-0rtt");
862 space++;
863 }
864 if (conf->ca_file) {
865 if (space) chunk_appendf(buf, " ");
866 chunk_appendf(buf, "ca-file %s", conf->ca_file);
867 space++;
868 }
869 if (conf->crl_file) {
870 if (space) chunk_appendf(buf, " ");
871 chunk_appendf(buf, "crl-file %s", conf->crl_file);
872 space++;
873 }
874 if (conf->ciphers) {
875 if (space) chunk_appendf(buf, " ");
876 chunk_appendf(buf, "ciphers %s", conf->ciphers);
877 space++;
878 }
Ilya Shipitsinf34ed0b2020-11-21 14:37:34 +0500879#ifdef HAVE_SSL_CTX_SET_CIPHERSUITES
William Lallemandc756bbd2020-05-13 17:23:59 +0200880 if (conf->ciphersuites) {
881 if (space) chunk_appendf(buf, " ");
882 chunk_appendf(buf, "ciphersuites %s", conf->ciphersuites);
883 space++;
884 }
885#endif
886 if (conf->curves) {
887 if (space) chunk_appendf(buf, " ");
888 chunk_appendf(buf, "curves %s", conf->curves);
889 space++;
890 }
891 if (conf->ecdhe) {
892 if (space) chunk_appendf(buf, " ");
893 chunk_appendf(buf, "ecdhe %s", conf->ecdhe);
894 space++;
895 }
896
897 /* the crt-lists only support ssl-min-ver and ssl-max-ver */
William Lallemand8177ad92020-05-20 16:49:02 +0200898 if (conf->ssl_methods_cfg.min) {
William Lallemandc756bbd2020-05-13 17:23:59 +0200899 if (space) chunk_appendf(buf, " ");
William Lallemand8177ad92020-05-20 16:49:02 +0200900 chunk_appendf(buf, "ssl-min-ver %s", methodVersions[conf->ssl_methods_cfg.min].name);
William Lallemandc756bbd2020-05-13 17:23:59 +0200901 space++;
902 }
903
William Lallemand8177ad92020-05-20 16:49:02 +0200904 if (conf->ssl_methods_cfg.max) {
William Lallemandc756bbd2020-05-13 17:23:59 +0200905 if (space) chunk_appendf(buf, " ");
William Lallemand8177ad92020-05-20 16:49:02 +0200906 chunk_appendf(buf, "ssl-max-ver %s", methodVersions[conf->ssl_methods_cfg.max].name);
William Lallemandc756bbd2020-05-13 17:23:59 +0200907 space++;
908 }
909
910 chunk_appendf(buf, "]");
911
912 return;
913}
914
915/* dump a list of filters */
916static void dump_crtlist_filters(struct buffer *buf, struct crtlist_entry *entry)
917{
918 int i;
919
920 if (!entry->fcount)
921 return;
922
923 for (i = 0; i < entry->fcount; i++) {
924 chunk_appendf(buf, " %s", entry->filters[i]);
925 }
926 return;
927}
928
929/************************** CLI functions ****************************/
930
931
Willy Tarreaua2fcca02022-05-05 11:53:23 +0200932/* CLI IO handler for '(show|dump) ssl crt-list'.
933 * It uses show_crtlist_ctx for the context.
934 */
William Lallemandc756bbd2020-05-13 17:23:59 +0200935static int cli_io_handler_dump_crtlist(struct appctx *appctx)
936{
Willy Tarreaua2fcca02022-05-05 11:53:23 +0200937 struct show_crtlist_ctx *ctx = appctx->svcctx;
William Lallemandc756bbd2020-05-13 17:23:59 +0200938 struct buffer *trash = alloc_trash_chunk();
William Lallemandc756bbd2020-05-13 17:23:59 +0200939 struct ebmb_node *lnode;
940
941 if (trash == NULL)
942 return 1;
943
944 /* dump the list of crt-lists */
Willy Tarreaua2fcca02022-05-05 11:53:23 +0200945 lnode = ctx->crtlist_node;
William Lallemandc756bbd2020-05-13 17:23:59 +0200946 if (lnode == NULL)
947 lnode = ebmb_first(&crtlists_tree);
948 while (lnode) {
949 chunk_appendf(trash, "%s\n", lnode->key);
Willy Tarreaud0a06d52022-05-18 15:07:19 +0200950 if (applet_putchk(appctx, trash) == -1)
William Lallemandc756bbd2020-05-13 17:23:59 +0200951 goto yield;
William Lallemandc756bbd2020-05-13 17:23:59 +0200952 lnode = ebmb_next(lnode);
953 }
954 free_trash_chunk(trash);
955 return 1;
956yield:
Willy Tarreaua2fcca02022-05-05 11:53:23 +0200957 ctx->crtlist_node = lnode;
William Lallemandc756bbd2020-05-13 17:23:59 +0200958 free_trash_chunk(trash);
959 return 0;
960}
961
962/* CLI IO handler for '(show|dump) ssl crt-list <filename>' */
963static int cli_io_handler_dump_crtlist_entries(struct appctx *appctx)
964{
Willy Tarreaua2fcca02022-05-05 11:53:23 +0200965 struct show_crtlist_ctx *ctx = appctx->svcctx;
William Lallemandc756bbd2020-05-13 17:23:59 +0200966 struct buffer *trash = alloc_trash_chunk();
967 struct crtlist *crtlist;
William Lallemandc756bbd2020-05-13 17:23:59 +0200968 struct crtlist_entry *entry;
969
970 if (trash == NULL)
971 return 1;
972
Willy Tarreaua2fcca02022-05-05 11:53:23 +0200973 crtlist = ebmb_entry(ctx->crtlist_node, struct crtlist, node);
William Lallemandc756bbd2020-05-13 17:23:59 +0200974
Willy Tarreaua2fcca02022-05-05 11:53:23 +0200975 entry = ctx->entry;
William Lallemandc756bbd2020-05-13 17:23:59 +0200976 if (entry == NULL) {
977 entry = LIST_ELEM((crtlist->ord_entries).n, typeof(entry), by_crtlist);
978 chunk_appendf(trash, "# %s\n", crtlist->node.key);
Willy Tarreaud0a06d52022-05-18 15:07:19 +0200979 if (applet_putchk(appctx, trash) == -1)
William Lallemandc756bbd2020-05-13 17:23:59 +0200980 goto yield;
William Lallemandc756bbd2020-05-13 17:23:59 +0200981 }
982
983 list_for_each_entry_from(entry, &crtlist->ord_entries, by_crtlist) {
984 struct ckch_store *store;
985 const char *filename;
986
987 store = entry->node.key;
988 filename = store->path;
989 chunk_appendf(trash, "%s", filename);
Willy Tarreaua2fcca02022-05-05 11:53:23 +0200990 if (ctx->mode == 's') /* show */
William Lallemandc756bbd2020-05-13 17:23:59 +0200991 chunk_appendf(trash, ":%d", entry->linenum);
992 dump_crtlist_sslconf(trash, entry->ssl_conf);
993 dump_crtlist_filters(trash, entry);
994 chunk_appendf(trash, "\n");
995
Willy Tarreaud0a06d52022-05-18 15:07:19 +0200996 if (applet_putchk(appctx, trash) == -1)
William Lallemandc756bbd2020-05-13 17:23:59 +0200997 goto yield;
William Lallemandc756bbd2020-05-13 17:23:59 +0200998 }
999 free_trash_chunk(trash);
1000 return 1;
1001yield:
Willy Tarreaua2fcca02022-05-05 11:53:23 +02001002 ctx->entry = entry;
William Lallemandc756bbd2020-05-13 17:23:59 +02001003 free_trash_chunk(trash);
1004 return 0;
1005}
1006
1007/* CLI argument parser for '(show|dump) ssl crt-list' */
1008static int cli_parse_dump_crtlist(char **args, char *payload, struct appctx *appctx, void *private)
1009{
Willy Tarreaua2fcca02022-05-05 11:53:23 +02001010 struct show_crtlist_ctx *ctx = applet_reserve_svcctx(appctx, sizeof(*ctx));
William Lallemandc756bbd2020-05-13 17:23:59 +02001011 struct ebmb_node *lnode;
1012 char *filename = NULL;
1013 int mode;
William Lallemand99cc2182020-06-25 15:19:51 +02001014 char *end;
William Lallemandc756bbd2020-05-13 17:23:59 +02001015
1016 if (!cli_has_level(appctx, ACCESS_LVL_ADMIN))
1017 return 1;
1018
Tim Duesterhuse5ff1412021-01-02 22:31:53 +01001019 if (*args[3] && strcmp(args[3], "-n") == 0) {
William Lallemandc756bbd2020-05-13 17:23:59 +02001020 mode = 's';
1021 filename = args[4];
1022 } else {
1023 mode = 'd';
1024 filename = args[3];
1025 }
1026
1027 if (mode == 's' && !*args[4])
1028 return cli_err(appctx, "'show ssl crt-list -n' expects a filename or a directory\n");
1029
1030 if (filename && *filename) {
William Lallemand99cc2182020-06-25 15:19:51 +02001031
1032
1033 /* strip trailing slashes, including first one */
1034 for (end = filename + strlen(filename) - 1; end >= filename && *end == '/'; end--)
1035 *end = 0;
1036
William Lallemandc756bbd2020-05-13 17:23:59 +02001037 lnode = ebst_lookup(&crtlists_tree, filename);
1038 if (lnode == NULL)
1039 return cli_err(appctx, "didn't find the specified filename\n");
1040
Willy Tarreaua2fcca02022-05-05 11:53:23 +02001041 ctx->crtlist_node = lnode;
William Lallemandc756bbd2020-05-13 17:23:59 +02001042 appctx->io_handler = cli_io_handler_dump_crtlist_entries;
1043 }
Willy Tarreaua2fcca02022-05-05 11:53:23 +02001044 ctx->mode = mode;
William Lallemandc756bbd2020-05-13 17:23:59 +02001045
1046 return 0;
1047}
1048
1049/* release function of the "add ssl crt-list' command, free things and unlock
Willy Tarreau6b6c3632022-05-05 13:43:49 +02001050 * the spinlock. It uses the add_crtlist_ctx.
1051 */
William Lallemandc756bbd2020-05-13 17:23:59 +02001052static void cli_release_add_crtlist(struct appctx *appctx)
1053{
Willy Tarreau6b6c3632022-05-05 13:43:49 +02001054 struct add_crtlist_ctx *ctx = appctx->svcctx;
1055 struct crtlist_entry *entry = ctx->entry;
William Lallemandc756bbd2020-05-13 17:23:59 +02001056
Christopher Fauletc642d7c2022-06-01 16:31:09 +02001057 if (entry) {
William Lallemandc756bbd2020-05-13 17:23:59 +02001058 struct ckch_inst *inst, *inst_s;
Christopher Fauletc642d7c2022-06-01 16:31:09 +02001059
William Lallemandc756bbd2020-05-13 17:23:59 +02001060 /* upon error free the ckch_inst and everything inside */
1061 ebpt_delete(&entry->node);
Willy Tarreau2b718102021-04-21 07:32:39 +02001062 LIST_DELETE(&entry->by_crtlist);
1063 LIST_DELETE(&entry->by_ckch_store);
William Lallemandc756bbd2020-05-13 17:23:59 +02001064
1065 list_for_each_entry_safe(inst, inst_s, &entry->ckch_inst, by_ckchs) {
1066 ckch_inst_free(inst);
1067 }
1068 crtlist_free_filters(entry->filters);
1069 ssl_sock_free_ssl_conf(entry->ssl_conf);
1070 free(entry->ssl_conf);
1071 free(entry);
1072 }
William Lallemandc756bbd2020-05-13 17:23:59 +02001073 HA_SPIN_UNLOCK(CKCH_LOCK, &ckch_lock);
Christopher Fauletc642d7c2022-06-01 16:31:09 +02001074 ha_free(&ctx->err);
William Lallemandc756bbd2020-05-13 17:23:59 +02001075}
1076
1077
1078/* IO Handler for the "add ssl crt-list" command It adds a new entry in the
1079 * crt-list and generates the ckch_insts for each bind_conf that uses this crt-list
1080 *
1081 * The logic is the same as the "commit ssl cert" command but without the
1082 * freeing of the old structures, because there are none.
Willy Tarreau6b6c3632022-05-05 13:43:49 +02001083 *
1084 * It uses the add_crtlist_ctx for the context.
William Lallemandc756bbd2020-05-13 17:23:59 +02001085 */
1086static int cli_io_handler_add_crtlist(struct appctx *appctx)
1087{
Willy Tarreau6b6c3632022-05-05 13:43:49 +02001088 struct add_crtlist_ctx *ctx = appctx->svcctx;
William Lallemandc756bbd2020-05-13 17:23:59 +02001089 struct bind_conf_list *bind_conf_node;
Willy Tarreauc12b3212022-05-27 11:08:15 +02001090 struct stconn *sc = appctx_sc(appctx);
Willy Tarreau6b6c3632022-05-05 13:43:49 +02001091 struct crtlist *crtlist = ctx->crtlist;
1092 struct crtlist_entry *entry = ctx->entry;
William Lallemandc756bbd2020-05-13 17:23:59 +02001093 struct ckch_store *store = entry->node.key;
William Lallemandc756bbd2020-05-13 17:23:59 +02001094 struct ckch_inst *new_inst;
William Lallemandc756bbd2020-05-13 17:23:59 +02001095 int i = 0;
1096 int errcode = 0;
1097
William Lallemandc756bbd2020-05-13 17:23:59 +02001098 /* for each bind_conf which use the crt-list, a new ckch_inst must be
1099 * created.
1100 */
Christopher Fauletda89e9b2023-01-04 14:11:10 +01001101 if (unlikely(sc_ic(sc)->flags & CF_SHUTW))
Christopher Fauletc642d7c2022-06-01 16:31:09 +02001102 goto end;
William Lallemandc756bbd2020-05-13 17:23:59 +02001103
Willy Tarreaufa11df52022-05-05 13:48:40 +02001104 switch (ctx->state) {
1105 case ADDCRT_ST_INIT:
Willy Tarreau1b948ef2022-05-05 13:50:46 +02001106 /* This state just print the update message */
Christopher Fauletc642d7c2022-06-01 16:31:09 +02001107 chunk_printf(&trash, "Inserting certificate '%s' in crt-list '%s'", store->path, crtlist->node.key);
1108 if (applet_putchk(appctx, &trash) == -1)
Willy Tarreau1b948ef2022-05-05 13:50:46 +02001109 goto yield;
Willy Tarreaufa11df52022-05-05 13:48:40 +02001110 ctx->state = ADDCRT_ST_GEN;
Willy Tarreauaef84482022-11-14 07:03:16 +01001111 __fallthrough;
Willy Tarreaufa11df52022-05-05 13:48:40 +02001112 case ADDCRT_ST_GEN:
Willy Tarreau6b6c3632022-05-05 13:43:49 +02001113 bind_conf_node = ctx->bind_conf_node; /* get the previous ptr from the yield */
Willy Tarreau1b948ef2022-05-05 13:50:46 +02001114 if (bind_conf_node == NULL)
1115 bind_conf_node = crtlist->bind_conf;
1116 for (; bind_conf_node; bind_conf_node = bind_conf_node->next) {
1117 struct bind_conf *bind_conf = bind_conf_node->bind_conf;
1118 struct sni_ctx *sni;
1119
Christopher Fauletc642d7c2022-06-01 16:31:09 +02001120 ctx->bind_conf_node = bind_conf_node;
1121
Willy Tarreau1b948ef2022-05-05 13:50:46 +02001122 /* yield every 10 generations */
1123 if (i > 10) {
Christopher Fauletc642d7c2022-06-01 16:31:09 +02001124 applet_have_more_data(appctx); /* let's come back later */
Willy Tarreau1b948ef2022-05-05 13:50:46 +02001125 goto yield;
1126 }
William Lallemandc756bbd2020-05-13 17:23:59 +02001127
Christopher Fauletc642d7c2022-06-01 16:31:09 +02001128 /* display one dot for each new instance */
1129 if (applet_putstr(appctx, ".") == -1)
1130 goto yield;
1131
Willy Tarreau1b948ef2022-05-05 13:50:46 +02001132 /* we don't support multi-cert bundles, only simple ones */
Christopher Fauletc642d7c2022-06-01 16:31:09 +02001133 ctx->err = NULL;
1134 errcode |= ckch_inst_new_load_store(store->path, store, bind_conf, entry->ssl_conf, entry->filters, entry->fcount, &new_inst, &ctx->err);
1135 if (errcode & ERR_CODE) {
1136 ctx->state = ADDCRT_ST_ERROR;
Willy Tarreau1b948ef2022-05-05 13:50:46 +02001137 goto error;
Christopher Fauletc642d7c2022-06-01 16:31:09 +02001138 }
William Lallemandc756bbd2020-05-13 17:23:59 +02001139
Willy Tarreau1b948ef2022-05-05 13:50:46 +02001140 /* we need to initialize the SSL_CTX generated */
1141 /* this iterate on the newly generated SNIs in the new instance to prepare their SSL_CTX */
1142 list_for_each_entry(sni, &new_inst->sni_ctx, by_ckch_inst) {
1143 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 +02001144 ctx->err = NULL;
1145 errcode |= ssl_sock_prep_ctx_and_inst(bind_conf, new_inst->ssl_conf, sni->ctx, sni->ckch_inst, &ctx->err);
1146 if (errcode & ERR_CODE) {
1147 ctx->state = ADDCRT_ST_ERROR;
William Lallemandc756bbd2020-05-13 17:23:59 +02001148 goto error;
Christopher Fauletc642d7c2022-06-01 16:31:09 +02001149 }
William Lallemandc756bbd2020-05-13 17:23:59 +02001150 }
Willy Tarreau1b948ef2022-05-05 13:50:46 +02001151 }
Christopher Fauletc642d7c2022-06-01 16:31:09 +02001152
Willy Tarreau1b948ef2022-05-05 13:50:46 +02001153 i++;
1154 LIST_APPEND(&store->ckch_inst, &new_inst->by_ckchs);
1155 LIST_APPEND(&entry->ckch_inst, &new_inst->by_crtlist_entry);
1156 new_inst->crtlist_entry = entry;
William Lallemandc756bbd2020-05-13 17:23:59 +02001157 }
Willy Tarreaufa11df52022-05-05 13:48:40 +02001158 ctx->state = ADDCRT_ST_INSERT;
Willy Tarreauaef84482022-11-14 07:03:16 +01001159 __fallthrough;
Willy Tarreaufa11df52022-05-05 13:48:40 +02001160 case ADDCRT_ST_INSERT:
William Lallemandcb6c5f42022-06-20 16:51:53 +02001161 /* the insertion is called for every instance of the store, not
1162 * only the one we generated.
1163 * But the ssl_sock_load_cert_sni() skip the sni already
1164 * inserted. Not every instance has a bind_conf, it could be
1165 * the store of a server so we should be careful */
1166
Willy Tarreau1b948ef2022-05-05 13:50:46 +02001167 list_for_each_entry(new_inst, &store->ckch_inst, by_ckchs) {
William Lallemandcb6c5f42022-06-20 16:51:53 +02001168 if (!new_inst->bind_conf) /* this is a server instance */
1169 continue;
Willy Tarreau1b948ef2022-05-05 13:50:46 +02001170 HA_RWLOCK_WRLOCK(SNI_LOCK, &new_inst->bind_conf->sni_lock);
1171 ssl_sock_load_cert_sni(new_inst, new_inst->bind_conf);
1172 HA_RWLOCK_WRUNLOCK(SNI_LOCK, &new_inst->bind_conf->sni_lock);
1173 }
1174 entry->linenum = ++crtlist->linecount;
Christopher Fauletc642d7c2022-06-01 16:31:09 +02001175 ctx->entry = NULL;
1176 ctx->state = ADDCRT_ST_SUCCESS;
Willy Tarreauaef84482022-11-14 07:03:16 +01001177 __fallthrough;
Christopher Fauletc642d7c2022-06-01 16:31:09 +02001178 case ADDCRT_ST_SUCCESS:
1179 chunk_reset(&trash);
1180 chunk_appendf(&trash, "\n");
1181 if (ctx->err)
1182 chunk_appendf(&trash, "%s", ctx->err);
1183 chunk_appendf(&trash, "Success!\n");
1184 if (applet_putchk(appctx, &trash) == -1)
1185 goto yield;
1186 ctx->state = ADDCRT_ST_FIN;
1187 break;
1188
1189 case ADDCRT_ST_ERROR:
1190 error:
1191 chunk_printf(&trash, "\n%sFailed!\n", ctx->err);
1192 if (applet_putchk(appctx, &trash) == -1)
1193 goto yield;
1194 break;
1195
Willy Tarreaufa11df52022-05-05 13:48:40 +02001196 default:
1197 break;
William Lallemandc756bbd2020-05-13 17:23:59 +02001198 }
1199
Christopher Fauletc642d7c2022-06-01 16:31:09 +02001200end:
William Lallemandc756bbd2020-05-13 17:23:59 +02001201 /* success: call the release function and don't come back */
1202 return 1;
1203yield:
William Lallemandc756bbd2020-05-13 17:23:59 +02001204 return 0; /* should come back */
William Lallemandc756bbd2020-05-13 17:23:59 +02001205}
1206
1207
1208/*
1209 * Parse a "add ssl crt-list <crt-list> <certfile>" line.
Willy Tarreau6b6c3632022-05-05 13:43:49 +02001210 * Filters and option must be passed through payload.
1211 * It sets a struct add_crtlist_ctx.
William Lallemandc756bbd2020-05-13 17:23:59 +02001212 */
1213static int cli_parse_add_crtlist(char **args, char *payload, struct appctx *appctx, void *private)
1214{
Willy Tarreau6b6c3632022-05-05 13:43:49 +02001215 struct add_crtlist_ctx *ctx = applet_reserve_svcctx(appctx, sizeof(*ctx));
William Lallemandc756bbd2020-05-13 17:23:59 +02001216 int cfgerr = 0;
1217 struct ckch_store *store;
1218 char *err = NULL;
1219 char path[MAXPATHLEN+1];
1220 char *crtlist_path;
1221 char *cert_path = NULL;
1222 struct ebmb_node *eb;
1223 struct ebpt_node *inserted;
1224 struct crtlist *crtlist;
1225 struct crtlist_entry *entry = NULL;
William Lallemand99cc2182020-06-25 15:19:51 +02001226 char *end;
William Lallemandc756bbd2020-05-13 17:23:59 +02001227
1228 if (!cli_has_level(appctx, ACCESS_LVL_ADMIN))
1229 return 1;
1230
1231 if (!*args[3] || (!payload && !*args[4]))
1232 return cli_err(appctx, "'add ssl crtlist' expects a filename and a certificate name\n");
1233
1234 crtlist_path = args[3];
1235
William Lallemand99cc2182020-06-25 15:19:51 +02001236 /* strip trailing slashes, including first one */
1237 for (end = crtlist_path + strlen(crtlist_path) - 1; end >= crtlist_path && *end == '/'; end--)
1238 *end = 0;
1239
William Lallemandc756bbd2020-05-13 17:23:59 +02001240 if (HA_SPIN_TRYLOCK(CKCH_LOCK, &ckch_lock))
1241 return cli_err(appctx, "Operations on certificates are currently locked!\n");
1242
1243 eb = ebst_lookup(&crtlists_tree, crtlist_path);
1244 if (!eb) {
1245 memprintf(&err, "crt-list '%s' does not exist!", crtlist_path);
1246 goto error;
1247 }
1248 crtlist = ebmb_entry(eb, struct crtlist, node);
1249
1250 entry = crtlist_entry_new();
1251 if (entry == NULL) {
1252 memprintf(&err, "Not enough memory!");
1253 goto error;
1254 }
1255
1256 if (payload) {
1257 char *lf;
1258
1259 lf = strrchr(payload, '\n');
1260 if (lf) {
1261 memprintf(&err, "only one line of payload is supported!");
1262 goto error;
1263 }
1264 /* cert_path is filled here */
Remi Tricot-Le Bretonfb00f312021-03-23 16:41:53 +01001265 cfgerr |= crtlist_parse_line(payload, &cert_path, entry, "CLI", 1, 1, &err);
William Lallemandc756bbd2020-05-13 17:23:59 +02001266 if (cfgerr & ERR_CODE)
1267 goto error;
1268 } else {
1269 cert_path = args[4];
1270 }
1271
1272 if (!cert_path) {
1273 memprintf(&err, "'add ssl crtlist' should contain the certificate name in the payload");
1274 cfgerr |= ERR_ALERT | ERR_FATAL;
1275 goto error;
1276 }
1277
1278 if (eb_gettag(crtlist->entries.b[EB_RGHT])) {
1279 char *slash;
1280
1281 slash = strrchr(cert_path, '/');
1282 if (!slash) {
1283 memprintf(&err, "'%s' is a directory, certificate path '%s' must contain the directory path", (char *)crtlist->node.key, cert_path);
1284 goto error;
1285 }
1286 /* temporary replace / by 0 to do an strcmp */
1287 *slash = '\0';
1288 if (strcmp(cert_path, (char*)crtlist->node.key) != 0) {
1289 *slash = '/';
1290 memprintf(&err, "'%s' is a directory, certificate path '%s' must contain the directory path", (char *)crtlist->node.key, cert_path);
1291 goto error;
1292 }
1293 *slash = '/';
1294 }
1295
1296 if (*cert_path != '/' && global_ssl.crt_base) {
Willy Tarreau393e42a2022-05-09 10:31:28 +02001297 if ((strlen(global_ssl.crt_base) + 1 + strlen(cert_path)) > sizeof(path) ||
1298 snprintf(path, sizeof(path), "%s/%s", global_ssl.crt_base, cert_path) > sizeof(path)) {
William Lallemandc756bbd2020-05-13 17:23:59 +02001299 memprintf(&err, "'%s' : path too long", cert_path);
1300 cfgerr |= ERR_ALERT | ERR_FATAL;
1301 goto error;
1302 }
William Lallemandc756bbd2020-05-13 17:23:59 +02001303 cert_path = path;
1304 }
1305
1306 store = ckchs_lookup(cert_path);
1307 if (store == NULL) {
1308 memprintf(&err, "certificate '%s' does not exist!", cert_path);
1309 goto error;
1310 }
William Lallemand52ddd992022-11-22 11:51:53 +01001311 if (store->data == NULL || store->data->cert == NULL) {
William Lallemandc756bbd2020-05-13 17:23:59 +02001312 memprintf(&err, "certificate '%s' is empty!", cert_path);
1313 goto error;
1314 }
1315
1316 /* check if it's possible to insert this new crtlist_entry */
1317 entry->node.key = store;
1318 inserted = ebpt_insert(&crtlist->entries, &entry->node);
1319 if (inserted != &entry->node) {
1320 memprintf(&err, "file already exists in this directory!");
1321 goto error;
1322 }
1323
1324 /* this is supposed to be a directory (EB_ROOT_UNIQUE), so no ssl_conf are allowed */
1325 if ((entry->ssl_conf || entry->filters) && eb_gettag(crtlist->entries.b[EB_RGHT])) {
1326 memprintf(&err, "this is a directory, SSL configuration and filters are not allowed");
1327 goto error;
1328 }
1329
Willy Tarreau2b718102021-04-21 07:32:39 +02001330 LIST_APPEND(&crtlist->ord_entries, &entry->by_crtlist);
William Lallemandc756bbd2020-05-13 17:23:59 +02001331 entry->crtlist = crtlist;
Willy Tarreau2b718102021-04-21 07:32:39 +02001332 LIST_APPEND(&store->crtlist_entry, &entry->by_ckch_store);
William Lallemandc756bbd2020-05-13 17:23:59 +02001333
Willy Tarreaufa11df52022-05-05 13:48:40 +02001334 ctx->state = ADDCRT_ST_INIT;
Willy Tarreau6b6c3632022-05-05 13:43:49 +02001335 ctx->crtlist = crtlist;
1336 ctx->entry = entry;
William Lallemandc756bbd2020-05-13 17:23:59 +02001337
1338 /* unlock is done in the release handler */
1339 return 0;
1340
1341error:
1342 crtlist_entry_free(entry);
1343 HA_SPIN_UNLOCK(CKCH_LOCK, &ckch_lock);
1344 err = memprintf(&err, "Can't edit the crt-list: %s\n", err ? err : "");
1345 return cli_dynerr(appctx, err);
1346}
1347
1348/* Parse a "del ssl crt-list <crt-list> <certfile>" line. */
1349static int cli_parse_del_crtlist(char **args, char *payload, struct appctx *appctx, void *private)
1350{
1351 struct ckch_store *store;
1352 char *err = NULL;
1353 char *crtlist_path, *cert_path;
1354 struct ebmb_node *ebmb;
1355 struct ebpt_node *ebpt;
1356 struct crtlist *crtlist;
1357 struct crtlist_entry *entry = NULL;
1358 struct ckch_inst *inst, *inst_s;
1359 int linenum = 0;
1360 char *colons;
William Lallemand99cc2182020-06-25 15:19:51 +02001361 char *end;
Remi Tricot-Le Bretonbc2c3862021-03-26 10:47:50 +01001362 int error_message_dumped = 0;
William Lallemandc756bbd2020-05-13 17:23:59 +02001363
1364 if (!cli_has_level(appctx, ACCESS_LVL_ADMIN))
1365 return 1;
1366
1367 if (!*args[3] || !*args[4])
1368 return cli_err(appctx, "'del ssl crtlist' expects a filename and a certificate name\n");
1369
1370 if (HA_SPIN_TRYLOCK(CKCH_LOCK, &ckch_lock))
1371 return cli_err(appctx, "Can't delete!\nOperations on certificates are currently locked!\n");
1372
1373 crtlist_path = args[3];
1374 cert_path = args[4];
1375
1376 colons = strchr(cert_path, ':');
1377 if (colons) {
1378 char *endptr;
1379
1380 linenum = strtol(colons + 1, &endptr, 10);
1381 if (colons + 1 == endptr || *endptr != '\0') {
1382 memprintf(&err, "wrong line number after colons in '%s'!", cert_path);
1383 goto error;
1384 }
1385 *colons = '\0';
1386 }
William Lallemand99cc2182020-06-25 15:19:51 +02001387
1388 /* strip trailing slashes, including first one */
1389 for (end = crtlist_path + strlen(crtlist_path) - 1; end >= crtlist_path && *end == '/'; end--)
1390 *end = 0;
1391
William Lallemandc756bbd2020-05-13 17:23:59 +02001392 /* look for crtlist */
1393 ebmb = ebst_lookup(&crtlists_tree, crtlist_path);
1394 if (!ebmb) {
1395 memprintf(&err, "crt-list '%s' does not exist!", crtlist_path);
1396 goto error;
1397 }
1398 crtlist = ebmb_entry(ebmb, struct crtlist, node);
1399
1400 /* look for store */
1401 store = ckchs_lookup(cert_path);
1402 if (store == NULL) {
1403 memprintf(&err, "certificate '%s' does not exist!", cert_path);
1404 goto error;
1405 }
William Lallemand52ddd992022-11-22 11:51:53 +01001406 if (store->data == NULL || store->data->cert == NULL) {
William Lallemandc756bbd2020-05-13 17:23:59 +02001407 memprintf(&err, "certificate '%s' is empty!", cert_path);
1408 goto error;
1409 }
1410
1411 ebpt = ebpt_lookup(&crtlist->entries, store);
1412 if (!ebpt) {
1413 memprintf(&err, "certificate '%s' can't be found in crt-list '%s'!", cert_path, crtlist_path);
1414 goto error;
1415 }
1416
1417 /* list the line number of entries for errors in err, and select the right ebpt */
1418 for (; ebpt; ebpt = ebpt_next_dup(ebpt)) {
1419 struct crtlist_entry *tmp;
1420
1421 tmp = ebpt_entry(ebpt, struct crtlist_entry, node);
1422 memprintf(&err, "%s%s%d", err ? err : "", err ? ", " : "", tmp->linenum);
1423
1424 /* select the entry we wanted */
1425 if (linenum == 0 || tmp->linenum == linenum) {
1426 if (!entry)
1427 entry = tmp;
1428 }
1429 }
1430
1431 /* we didn't found the specified entry */
1432 if (!entry) {
1433 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);
1434 goto error;
1435 }
1436
1437 /* we didn't specified a line number but there were several entries */
1438 if (linenum == 0 && ebpt_next_dup(&entry->node)) {
1439 memprintf(&err, "found the certificate '%s' in several entries, please specify a line number preceded by colons (%s)!", cert_path, err ? err : NULL);
1440 goto error;
1441 }
1442
Remi Tricot-Le Bretonbc2c3862021-03-26 10:47:50 +01001443 /* Iterate over all the instances in order to see if any of them is a
1444 * default instance. If this is the case, the entry won't be suppressed. */
1445 list_for_each_entry_safe(inst, inst_s, &entry->ckch_inst, by_crtlist_entry) {
1446 if (inst->is_default && !inst->bind_conf->strict_sni) {
1447 if (!error_message_dumped) {
1448 memprintf(&err, "certificate '%s' cannot be deleted, it is used as default certificate by the following frontends:\n", cert_path);
1449 error_message_dumped = 1;
1450 }
1451 memprintf(&err, "%s\t- %s:%d\n", err, inst->bind_conf->file, inst->bind_conf->line);
1452 }
1453 }
1454 if (error_message_dumped)
1455 goto error;
1456
William Lallemandc756bbd2020-05-13 17:23:59 +02001457 /* upon error free the ckch_inst and everything inside */
1458
1459 ebpt_delete(&entry->node);
Willy Tarreau2b718102021-04-21 07:32:39 +02001460 LIST_DELETE(&entry->by_crtlist);
1461 LIST_DELETE(&entry->by_ckch_store);
William Lallemandc756bbd2020-05-13 17:23:59 +02001462
1463 list_for_each_entry_safe(inst, inst_s, &entry->ckch_inst, by_crtlist_entry) {
1464 struct sni_ctx *sni, *sni_s;
Remi Tricot-Le Breton4458b972021-02-19 17:41:55 +01001465 struct ckch_inst_link_ref *link_ref, *link_ref_s;
William Lallemandc756bbd2020-05-13 17:23:59 +02001466
1467 HA_RWLOCK_WRLOCK(SNI_LOCK, &inst->bind_conf->sni_lock);
1468 list_for_each_entry_safe(sni, sni_s, &inst->sni_ctx, by_ckch_inst) {
1469 ebmb_delete(&sni->name);
Willy Tarreau2b718102021-04-21 07:32:39 +02001470 LIST_DELETE(&sni->by_ckch_inst);
William Lallemandc756bbd2020-05-13 17:23:59 +02001471 SSL_CTX_free(sni->ctx);
1472 free(sni);
1473 }
1474 HA_RWLOCK_WRUNLOCK(SNI_LOCK, &inst->bind_conf->sni_lock);
Willy Tarreau2b718102021-04-21 07:32:39 +02001475 LIST_DELETE(&inst->by_ckchs);
Remi Tricot-Le Breton4458b972021-02-19 17:41:55 +01001476 list_for_each_entry_safe(link_ref, link_ref_s, &inst->cafile_link_refs, list) {
1477 LIST_DELETE(&link_ref->link->list);
1478 LIST_DELETE(&link_ref->list);
1479 free(link_ref);
1480 }
William Lallemandc756bbd2020-05-13 17:23:59 +02001481 free(inst);
1482 }
1483
1484 crtlist_free_filters(entry->filters);
1485 ssl_sock_free_ssl_conf(entry->ssl_conf);
1486 free(entry->ssl_conf);
1487 free(entry);
1488
1489 HA_SPIN_UNLOCK(CKCH_LOCK, &ckch_lock);
1490 err = memprintf(&err, "Entry '%s' deleted in crtlist '%s'!\n", cert_path, crtlist_path);
1491 return cli_dynmsg(appctx, LOG_NOTICE, err);
1492
1493error:
1494 HA_SPIN_UNLOCK(CKCH_LOCK, &ckch_lock);
1495 err = memprintf(&err, "Can't delete the entry: %s\n", err ? err : "");
1496 return cli_dynerr(appctx, err);
1497}
1498
1499
William Lallemandee8530c2020-06-23 18:19:42 +02001500/* unlink and free all crt-list and crt-list entries */
1501void crtlist_deinit()
1502{
1503 struct eb_node *node, *next;
1504 struct crtlist *crtlist;
1505
1506 node = eb_first(&crtlists_tree);
1507 while (node) {
1508 next = eb_next(node);
1509 crtlist = ebmb_entry(node, struct crtlist, node);
1510 crtlist_free(crtlist);
1511 node = next;
1512 }
1513}
1514
William Lallemandc756bbd2020-05-13 17:23:59 +02001515
1516/* register cli keywords */
1517static struct cli_kw_list cli_kws = {{ },{
Willy Tarreaub205bfd2021-05-07 11:38:37 +02001518 { { "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 },
1519 { { "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 },
1520 { { "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 +02001521 { { NULL }, NULL, NULL, NULL } }
1522};
1523
1524INITCALL1(STG_REGISTER, cli_register_kw, &cli_kws);
1525