blob: b5979bdf3172ef513c416249eb61c9fe6556dc6f [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;
566 ebpt_insert(&newlist->entries, &entry->node);
Willy Tarreau2b718102021-04-21 07:32:39 +0200567 LIST_APPEND(&newlist->ord_entries, &entry->by_crtlist);
568 LIST_APPEND(&ckchs->crtlist_entry, &entry->by_ckch_store);
William Lallemand6e9556b2020-05-12 17:52:44 +0200569
William Lallemand73404572020-11-20 18:23:40 +0100570 } else if (global_ssl.extra_files & SSL_GF_BUNDLE) {
William Lallemand47da8212020-09-10 19:13:27 +0200571 /* If we didn't find the file, this could be a
William Lallemand51f784b2020-10-02 18:08:18 +0200572 bundle, since 2.3 we don't support multiple
573 certificate in the same OpenSSL store, so we
574 emulate it by loading each file separately. To
575 do so we need to duplicate the entry in the
576 crt-list because it becomes independent */
William Lallemand47da8212020-09-10 19:13:27 +0200577 char fp[MAXPATHLEN+1] = {0};
578 int n = 0;
579 struct crtlist_entry *entry_dup = entry; /* use the previous created entry */
580 for (n = 0; n < SSL_SOCK_NUM_KEYTYPES; n++) {
581 struct stat buf;
582 int ret;
583
William Lallemand86c2dd62020-11-20 14:23:38 +0100584 ret = snprintf(fp, sizeof(fp), "%s.%s", crt_path, SSL_SOCK_KEYTYPE_NAMES[n]);
William Lallemand47da8212020-09-10 19:13:27 +0200585 if (ret > sizeof(fp))
586 continue;
587
588 ckchs = ckchs_lookup(fp);
William Lallemand77e1c6f2020-11-20 18:26:09 +0100589 if (!ckchs) {
590 if (stat(fp, &buf) == 0) {
591 ckchs = ckchs_load_cert_file(fp, err);
592 if (!ckchs) {
William Lallemand47da8212020-09-10 19:13:27 +0200593 cfgerr |= ERR_ALERT | ERR_FATAL;
594 goto error;
595 }
William Lallemand77e1c6f2020-11-20 18:26:09 +0100596 } else {
597 continue; /* didn't find this extension, skip */
598 }
599 }
600 found++;
601 linenum++; /* we duplicate the line for this entry in the bundle */
602 if (!entry_dup) { /* if the entry was used, duplicate one */
603 linenum++;
604 entry_dup = crtlist_entry_dup(entry);
605 if (!entry_dup) {
606 cfgerr |= ERR_ALERT | ERR_FATAL;
607 goto error;
William Lallemand47da8212020-09-10 19:13:27 +0200608 }
William Lallemand77e1c6f2020-11-20 18:26:09 +0100609 entry_dup->linenum = linenum;
610 }
William Lallemand47da8212020-09-10 19:13:27 +0200611
William Lallemand77e1c6f2020-11-20 18:26:09 +0100612 entry_dup->node.key = ckchs;
613 entry_dup->crtlist = newlist;
614 ebpt_insert(&newlist->entries, &entry_dup->node);
Willy Tarreau2b718102021-04-21 07:32:39 +0200615 LIST_APPEND(&newlist->ord_entries, &entry_dup->by_crtlist);
616 LIST_APPEND(&ckchs->crtlist_entry, &entry_dup->by_ckch_store);
William Lallemand47da8212020-09-10 19:13:27 +0200617
William Lallemand77e1c6f2020-11-20 18:26:09 +0100618 entry_dup = NULL; /* the entry was used, we need a new one next round */
William Lallemand47da8212020-09-10 19:13:27 +0200619 }
William Lallemandb7fdfdf2020-12-04 15:45:02 +0100620#if HA_OPENSSL_VERSION_NUMBER < 0x10101000L
621 if (found) {
622 memprintf(err, "%sCan't load '%s'. Loading a multi certificates bundle requires OpenSSL >= 1.1.1\n",
623 err && *err ? *err : "", crt_path);
624 cfgerr |= ERR_ALERT | ERR_FATAL;
625 }
626#endif
William Lallemand47da8212020-09-10 19:13:27 +0200627 }
William Lallemand77e1c6f2020-11-20 18:26:09 +0100628 if (!found) {
629 memprintf(err, "%sunable to stat SSL certificate from file '%s' : %s.\n",
630 err && *err ? *err : "", crt_path, strerror(errno));
631 cfgerr |= ERR_ALERT | ERR_FATAL;
632 }
633
William Lallemand50c03aa2020-11-06 16:24:07 +0100634 } else {
635 entry->node.key = ckchs;
636 entry->crtlist = newlist;
637 ebpt_insert(&newlist->entries, &entry->node);
Willy Tarreau2b718102021-04-21 07:32:39 +0200638 LIST_APPEND(&newlist->ord_entries, &entry->by_crtlist);
639 LIST_APPEND(&ckchs->crtlist_entry, &entry->by_ckch_store);
William Lallemand77e1c6f2020-11-20 18:26:09 +0100640 found++;
William Lallemand47da8212020-09-10 19:13:27 +0200641 }
William Lallemand6e9556b2020-05-12 17:52:44 +0200642 entry = NULL;
643 }
Tim Duesterhusb9f6acc2020-09-29 18:00:28 +0200644
645 if (missing_lf != -1) {
646 memprintf(err, "parsing [%s:%d]: Missing LF on last line, file might have been truncated at position %d.\n",
647 file, linenum, (missing_lf + 1));
648 cfgerr |= ERR_ALERT | ERR_FATAL;
649 }
650
William Lallemand6e9556b2020-05-12 17:52:44 +0200651 if (cfgerr & ERR_CODE)
652 goto error;
653
654 newlist->linecount = linenum;
655
656 fclose(f);
657 *crtlist = newlist;
658
659 return cfgerr;
660error:
661 crtlist_entry_free(entry);
662
663 fclose(f);
664 crtlist_free(newlist);
665 return cfgerr;
666}
667
668/* This function reads a directory and stores it in a struct crtlist, each file is a crtlist_entry structure
669 * Fill the <crtlist> argument with a pointer to a new crtlist struct
670 *
671 * This function tries to open and store certificate files.
672 */
673int crtlist_load_cert_dir(char *path, struct bind_conf *bind_conf, struct crtlist **crtlist, char **err)
674{
675 struct crtlist *dir;
676 struct dirent **de_list;
677 int i, n;
678 struct stat buf;
679 char *end;
680 char fp[MAXPATHLEN+1];
681 int cfgerr = 0;
682 struct ckch_store *ckchs;
William Lallemand6e9556b2020-05-12 17:52:44 +0200683
684 dir = crtlist_new(path, 1);
685 if (dir == NULL) {
686 memprintf(err, "not enough memory");
687 return ERR_ALERT | ERR_FATAL;
688 }
689
690 n = scandir(path, &de_list, 0, alphasort);
691 if (n < 0) {
692 memprintf(err, "%sunable to scan directory '%s' : %s.\n",
693 err && *err ? *err : "", path, strerror(errno));
694 cfgerr |= ERR_ALERT | ERR_FATAL;
695 }
696 else {
697 for (i = 0; i < n; i++) {
698 struct crtlist_entry *entry;
699 struct dirent *de = de_list[i];
700
701 end = strrchr(de->d_name, '.');
William Lallemand589570d2022-05-09 10:30:51 +0200702 if (end && (de->d_name[0] == '.' ||
703 strcmp(end, ".issuer") == 0 || strcmp(end, ".ocsp") == 0 ||
704 strcmp(end, ".sctl") == 0 || strcmp(end, ".key") == 0))
William Lallemand6e9556b2020-05-12 17:52:44 +0200705 goto ignore_entry;
706
707 snprintf(fp, sizeof(fp), "%s/%s", path, de->d_name);
708 if (stat(fp, &buf) != 0) {
709 memprintf(err, "%sunable to stat SSL certificate from file '%s' : %s.\n",
710 err && *err ? *err : "", fp, strerror(errno));
711 cfgerr |= ERR_ALERT | ERR_FATAL;
712 goto ignore_entry;
713 }
714 if (!S_ISREG(buf.st_mode))
715 goto ignore_entry;
716
717 entry = crtlist_entry_new();
718 if (entry == NULL) {
719 memprintf(err, "not enough memory '%s'", fp);
720 cfgerr |= ERR_ALERT | ERR_FATAL;
721 goto ignore_entry;
722 }
723
William Lallemand6e9556b2020-05-12 17:52:44 +0200724 ckchs = ckchs_lookup(fp);
725 if (ckchs == NULL)
William Lallemandbd8e6ed2020-09-16 16:08:08 +0200726 ckchs = ckchs_load_cert_file(fp, err);
William Lallemand6e9556b2020-05-12 17:52:44 +0200727 if (ckchs == NULL) {
728 free(de);
729 free(entry);
730 cfgerr |= ERR_ALERT | ERR_FATAL;
731 goto end;
732 }
733 entry->node.key = ckchs;
734 entry->crtlist = dir;
Willy Tarreau2b718102021-04-21 07:32:39 +0200735 LIST_APPEND(&ckchs->crtlist_entry, &entry->by_ckch_store);
736 LIST_APPEND(&dir->ord_entries, &entry->by_crtlist);
William Lallemand6e9556b2020-05-12 17:52:44 +0200737 ebpt_insert(&dir->entries, &entry->node);
738
739ignore_entry:
740 free(de);
741 }
742end:
743 free(de_list);
744 }
745
746 if (cfgerr & ERR_CODE) {
747 /* free the dir and entries on error */
748 crtlist_free(dir);
749 } else {
750 *crtlist = dir;
751 }
752 return cfgerr;
753
754}
755
William Lallemandc756bbd2020-05-13 17:23:59 +0200756/*
757 * Take an ssl_bind_conf structure and append the configuration line used to
758 * create it in the buffer
759 */
760static void dump_crtlist_sslconf(struct buffer *buf, const struct ssl_bind_conf *conf)
761{
762 int space = 0;
763
764 if (conf == NULL)
765 return;
766
767 chunk_appendf(buf, " [");
768#ifdef OPENSSL_NPN_NEGOTIATED
769 if (conf->npn_str) {
770 int len = conf->npn_len;
771 char *ptr = conf->npn_str;
772 int comma = 0;
773
774 if (space) chunk_appendf(buf, " ");
775 chunk_appendf(buf, "npn ");
776 while (len) {
777 unsigned short size;
778
779 size = *ptr;
780 ptr++;
781 if (comma)
782 chunk_memcat(buf, ",", 1);
783 chunk_memcat(buf, ptr, size);
784 ptr += size;
785 len -= size + 1;
786 comma = 1;
787 }
788 chunk_memcat(buf, "", 1); /* finish with a \0 */
789 space++;
790 }
791#endif
792#ifdef TLSEXT_TYPE_application_layer_protocol_negotiation
793 if (conf->alpn_str) {
794 int len = conf->alpn_len;
795 char *ptr = conf->alpn_str;
796 int comma = 0;
797
798 if (space) chunk_appendf(buf, " ");
799 chunk_appendf(buf, "alpn ");
800 while (len) {
801 unsigned short size;
802
803 size = *ptr;
804 ptr++;
805 if (comma)
806 chunk_memcat(buf, ",", 1);
807 chunk_memcat(buf, ptr, size);
808 ptr += size;
809 len -= size + 1;
810 comma = 1;
811 }
812 chunk_memcat(buf, "", 1); /* finish with a \0 */
813 space++;
814 }
815#endif
816 /* verify */
817 {
818 if (conf->verify == SSL_SOCK_VERIFY_NONE) {
819 if (space) chunk_appendf(buf, " ");
820 chunk_appendf(buf, "verify none");
821 space++;
822 } else if (conf->verify == SSL_SOCK_VERIFY_OPTIONAL) {
823 if (space) chunk_appendf(buf, " ");
824 chunk_appendf(buf, "verify optional");
825 space++;
826 } else if (conf->verify == SSL_SOCK_VERIFY_REQUIRED) {
827 if (space) chunk_appendf(buf, " ");
828 chunk_appendf(buf, "verify required");
829 space++;
830 }
831 }
832
833 if (conf->no_ca_names) {
834 if (space) chunk_appendf(buf, " ");
835 chunk_appendf(buf, "no-ca-names");
836 space++;
837 }
838
839 if (conf->early_data) {
840 if (space) chunk_appendf(buf, " ");
841 chunk_appendf(buf, "allow-0rtt");
842 space++;
843 }
844 if (conf->ca_file) {
845 if (space) chunk_appendf(buf, " ");
846 chunk_appendf(buf, "ca-file %s", conf->ca_file);
847 space++;
848 }
849 if (conf->crl_file) {
850 if (space) chunk_appendf(buf, " ");
851 chunk_appendf(buf, "crl-file %s", conf->crl_file);
852 space++;
853 }
854 if (conf->ciphers) {
855 if (space) chunk_appendf(buf, " ");
856 chunk_appendf(buf, "ciphers %s", conf->ciphers);
857 space++;
858 }
Ilya Shipitsinf34ed0b2020-11-21 14:37:34 +0500859#ifdef HAVE_SSL_CTX_SET_CIPHERSUITES
William Lallemandc756bbd2020-05-13 17:23:59 +0200860 if (conf->ciphersuites) {
861 if (space) chunk_appendf(buf, " ");
862 chunk_appendf(buf, "ciphersuites %s", conf->ciphersuites);
863 space++;
864 }
865#endif
866 if (conf->curves) {
867 if (space) chunk_appendf(buf, " ");
868 chunk_appendf(buf, "curves %s", conf->curves);
869 space++;
870 }
871 if (conf->ecdhe) {
872 if (space) chunk_appendf(buf, " ");
873 chunk_appendf(buf, "ecdhe %s", conf->ecdhe);
874 space++;
875 }
876
877 /* the crt-lists only support ssl-min-ver and ssl-max-ver */
William Lallemand8177ad92020-05-20 16:49:02 +0200878 if (conf->ssl_methods_cfg.min) {
William Lallemandc756bbd2020-05-13 17:23:59 +0200879 if (space) chunk_appendf(buf, " ");
William Lallemand8177ad92020-05-20 16:49:02 +0200880 chunk_appendf(buf, "ssl-min-ver %s", methodVersions[conf->ssl_methods_cfg.min].name);
William Lallemandc756bbd2020-05-13 17:23:59 +0200881 space++;
882 }
883
William Lallemand8177ad92020-05-20 16:49:02 +0200884 if (conf->ssl_methods_cfg.max) {
William Lallemandc756bbd2020-05-13 17:23:59 +0200885 if (space) chunk_appendf(buf, " ");
William Lallemand8177ad92020-05-20 16:49:02 +0200886 chunk_appendf(buf, "ssl-max-ver %s", methodVersions[conf->ssl_methods_cfg.max].name);
William Lallemandc756bbd2020-05-13 17:23:59 +0200887 space++;
888 }
889
890 chunk_appendf(buf, "]");
891
892 return;
893}
894
895/* dump a list of filters */
896static void dump_crtlist_filters(struct buffer *buf, struct crtlist_entry *entry)
897{
898 int i;
899
900 if (!entry->fcount)
901 return;
902
903 for (i = 0; i < entry->fcount; i++) {
904 chunk_appendf(buf, " %s", entry->filters[i]);
905 }
906 return;
907}
908
909/************************** CLI functions ****************************/
910
911
Willy Tarreaua2fcca02022-05-05 11:53:23 +0200912/* CLI IO handler for '(show|dump) ssl crt-list'.
913 * It uses show_crtlist_ctx for the context.
914 */
William Lallemandc756bbd2020-05-13 17:23:59 +0200915static int cli_io_handler_dump_crtlist(struct appctx *appctx)
916{
Willy Tarreaua2fcca02022-05-05 11:53:23 +0200917 struct show_crtlist_ctx *ctx = appctx->svcctx;
William Lallemandc756bbd2020-05-13 17:23:59 +0200918 struct buffer *trash = alloc_trash_chunk();
William Lallemandc756bbd2020-05-13 17:23:59 +0200919 struct ebmb_node *lnode;
920
921 if (trash == NULL)
922 return 1;
923
924 /* dump the list of crt-lists */
Willy Tarreaua2fcca02022-05-05 11:53:23 +0200925 lnode = ctx->crtlist_node;
William Lallemandc756bbd2020-05-13 17:23:59 +0200926 if (lnode == NULL)
927 lnode = ebmb_first(&crtlists_tree);
928 while (lnode) {
929 chunk_appendf(trash, "%s\n", lnode->key);
Willy Tarreaud0a06d52022-05-18 15:07:19 +0200930 if (applet_putchk(appctx, trash) == -1)
William Lallemandc756bbd2020-05-13 17:23:59 +0200931 goto yield;
William Lallemandc756bbd2020-05-13 17:23:59 +0200932 lnode = ebmb_next(lnode);
933 }
934 free_trash_chunk(trash);
935 return 1;
936yield:
Willy Tarreaua2fcca02022-05-05 11:53:23 +0200937 ctx->crtlist_node = lnode;
William Lallemandc756bbd2020-05-13 17:23:59 +0200938 free_trash_chunk(trash);
939 return 0;
940}
941
942/* CLI IO handler for '(show|dump) ssl crt-list <filename>' */
943static int cli_io_handler_dump_crtlist_entries(struct appctx *appctx)
944{
Willy Tarreaua2fcca02022-05-05 11:53:23 +0200945 struct show_crtlist_ctx *ctx = appctx->svcctx;
William Lallemandc756bbd2020-05-13 17:23:59 +0200946 struct buffer *trash = alloc_trash_chunk();
947 struct crtlist *crtlist;
William Lallemandc756bbd2020-05-13 17:23:59 +0200948 struct crtlist_entry *entry;
949
950 if (trash == NULL)
951 return 1;
952
Willy Tarreaua2fcca02022-05-05 11:53:23 +0200953 crtlist = ebmb_entry(ctx->crtlist_node, struct crtlist, node);
William Lallemandc756bbd2020-05-13 17:23:59 +0200954
Willy Tarreaua2fcca02022-05-05 11:53:23 +0200955 entry = ctx->entry;
William Lallemandc756bbd2020-05-13 17:23:59 +0200956 if (entry == NULL) {
957 entry = LIST_ELEM((crtlist->ord_entries).n, typeof(entry), by_crtlist);
958 chunk_appendf(trash, "# %s\n", crtlist->node.key);
Willy Tarreaud0a06d52022-05-18 15:07:19 +0200959 if (applet_putchk(appctx, trash) == -1)
William Lallemandc756bbd2020-05-13 17:23:59 +0200960 goto yield;
William Lallemandc756bbd2020-05-13 17:23:59 +0200961 }
962
963 list_for_each_entry_from(entry, &crtlist->ord_entries, by_crtlist) {
964 struct ckch_store *store;
965 const char *filename;
966
967 store = entry->node.key;
968 filename = store->path;
969 chunk_appendf(trash, "%s", filename);
Willy Tarreaua2fcca02022-05-05 11:53:23 +0200970 if (ctx->mode == 's') /* show */
William Lallemandc756bbd2020-05-13 17:23:59 +0200971 chunk_appendf(trash, ":%d", entry->linenum);
972 dump_crtlist_sslconf(trash, entry->ssl_conf);
973 dump_crtlist_filters(trash, entry);
974 chunk_appendf(trash, "\n");
975
Willy Tarreaud0a06d52022-05-18 15:07:19 +0200976 if (applet_putchk(appctx, trash) == -1)
William Lallemandc756bbd2020-05-13 17:23:59 +0200977 goto yield;
William Lallemandc756bbd2020-05-13 17:23:59 +0200978 }
979 free_trash_chunk(trash);
980 return 1;
981yield:
Willy Tarreaua2fcca02022-05-05 11:53:23 +0200982 ctx->entry = entry;
William Lallemandc756bbd2020-05-13 17:23:59 +0200983 free_trash_chunk(trash);
984 return 0;
985}
986
987/* CLI argument parser for '(show|dump) ssl crt-list' */
988static int cli_parse_dump_crtlist(char **args, char *payload, struct appctx *appctx, void *private)
989{
Willy Tarreaua2fcca02022-05-05 11:53:23 +0200990 struct show_crtlist_ctx *ctx = applet_reserve_svcctx(appctx, sizeof(*ctx));
William Lallemandc756bbd2020-05-13 17:23:59 +0200991 struct ebmb_node *lnode;
992 char *filename = NULL;
993 int mode;
William Lallemand99cc2182020-06-25 15:19:51 +0200994 char *end;
William Lallemandc756bbd2020-05-13 17:23:59 +0200995
996 if (!cli_has_level(appctx, ACCESS_LVL_ADMIN))
997 return 1;
998
Tim Duesterhuse5ff1412021-01-02 22:31:53 +0100999 if (*args[3] && strcmp(args[3], "-n") == 0) {
William Lallemandc756bbd2020-05-13 17:23:59 +02001000 mode = 's';
1001 filename = args[4];
1002 } else {
1003 mode = 'd';
1004 filename = args[3];
1005 }
1006
1007 if (mode == 's' && !*args[4])
1008 return cli_err(appctx, "'show ssl crt-list -n' expects a filename or a directory\n");
1009
1010 if (filename && *filename) {
William Lallemand99cc2182020-06-25 15:19:51 +02001011
1012
1013 /* strip trailing slashes, including first one */
1014 for (end = filename + strlen(filename) - 1; end >= filename && *end == '/'; end--)
1015 *end = 0;
1016
William Lallemandc756bbd2020-05-13 17:23:59 +02001017 lnode = ebst_lookup(&crtlists_tree, filename);
1018 if (lnode == NULL)
1019 return cli_err(appctx, "didn't find the specified filename\n");
1020
Willy Tarreaua2fcca02022-05-05 11:53:23 +02001021 ctx->crtlist_node = lnode;
William Lallemandc756bbd2020-05-13 17:23:59 +02001022 appctx->io_handler = cli_io_handler_dump_crtlist_entries;
1023 }
Willy Tarreaua2fcca02022-05-05 11:53:23 +02001024 ctx->mode = mode;
William Lallemandc756bbd2020-05-13 17:23:59 +02001025
1026 return 0;
1027}
1028
1029/* release function of the "add ssl crt-list' command, free things and unlock
Willy Tarreau6b6c3632022-05-05 13:43:49 +02001030 * the spinlock. It uses the add_crtlist_ctx.
1031 */
William Lallemandc756bbd2020-05-13 17:23:59 +02001032static void cli_release_add_crtlist(struct appctx *appctx)
1033{
Willy Tarreau6b6c3632022-05-05 13:43:49 +02001034 struct add_crtlist_ctx *ctx = appctx->svcctx;
1035 struct crtlist_entry *entry = ctx->entry;
William Lallemandc756bbd2020-05-13 17:23:59 +02001036
Christopher Fauletc642d7c2022-06-01 16:31:09 +02001037 if (entry) {
William Lallemandc756bbd2020-05-13 17:23:59 +02001038 struct ckch_inst *inst, *inst_s;
Christopher Fauletc642d7c2022-06-01 16:31:09 +02001039
William Lallemandc756bbd2020-05-13 17:23:59 +02001040 /* upon error free the ckch_inst and everything inside */
1041 ebpt_delete(&entry->node);
Willy Tarreau2b718102021-04-21 07:32:39 +02001042 LIST_DELETE(&entry->by_crtlist);
1043 LIST_DELETE(&entry->by_ckch_store);
William Lallemandc756bbd2020-05-13 17:23:59 +02001044
1045 list_for_each_entry_safe(inst, inst_s, &entry->ckch_inst, by_ckchs) {
1046 ckch_inst_free(inst);
1047 }
1048 crtlist_free_filters(entry->filters);
1049 ssl_sock_free_ssl_conf(entry->ssl_conf);
1050 free(entry->ssl_conf);
1051 free(entry);
1052 }
William Lallemandc756bbd2020-05-13 17:23:59 +02001053 HA_SPIN_UNLOCK(CKCH_LOCK, &ckch_lock);
Christopher Fauletc642d7c2022-06-01 16:31:09 +02001054 ha_free(&ctx->err);
William Lallemandc756bbd2020-05-13 17:23:59 +02001055}
1056
1057
1058/* IO Handler for the "add ssl crt-list" command It adds a new entry in the
1059 * crt-list and generates the ckch_insts for each bind_conf that uses this crt-list
1060 *
1061 * The logic is the same as the "commit ssl cert" command but without the
1062 * freeing of the old structures, because there are none.
Willy Tarreau6b6c3632022-05-05 13:43:49 +02001063 *
1064 * It uses the add_crtlist_ctx for the context.
William Lallemandc756bbd2020-05-13 17:23:59 +02001065 */
1066static int cli_io_handler_add_crtlist(struct appctx *appctx)
1067{
Willy Tarreau6b6c3632022-05-05 13:43:49 +02001068 struct add_crtlist_ctx *ctx = appctx->svcctx;
William Lallemandc756bbd2020-05-13 17:23:59 +02001069 struct bind_conf_list *bind_conf_node;
Willy Tarreauc12b3212022-05-27 11:08:15 +02001070 struct stconn *sc = appctx_sc(appctx);
Willy Tarreau6b6c3632022-05-05 13:43:49 +02001071 struct crtlist *crtlist = ctx->crtlist;
1072 struct crtlist_entry *entry = ctx->entry;
William Lallemandc756bbd2020-05-13 17:23:59 +02001073 struct ckch_store *store = entry->node.key;
William Lallemandc756bbd2020-05-13 17:23:59 +02001074 struct ckch_inst *new_inst;
William Lallemandc756bbd2020-05-13 17:23:59 +02001075 int i = 0;
1076 int errcode = 0;
1077
William Lallemandc756bbd2020-05-13 17:23:59 +02001078 /* for each bind_conf which use the crt-list, a new ckch_inst must be
1079 * created.
1080 */
Willy Tarreau475e4632022-05-27 10:26:46 +02001081 if (unlikely(sc_ic(sc)->flags & (CF_WRITE_ERROR|CF_SHUTW)))
Christopher Fauletc642d7c2022-06-01 16:31:09 +02001082 goto end;
William Lallemandc756bbd2020-05-13 17:23:59 +02001083
Willy Tarreaufa11df52022-05-05 13:48:40 +02001084 switch (ctx->state) {
1085 case ADDCRT_ST_INIT:
Willy Tarreau1b948ef2022-05-05 13:50:46 +02001086 /* This state just print the update message */
Christopher Fauletc642d7c2022-06-01 16:31:09 +02001087 chunk_printf(&trash, "Inserting certificate '%s' in crt-list '%s'", store->path, crtlist->node.key);
1088 if (applet_putchk(appctx, &trash) == -1)
Willy Tarreau1b948ef2022-05-05 13:50:46 +02001089 goto yield;
Willy Tarreaufa11df52022-05-05 13:48:40 +02001090 ctx->state = ADDCRT_ST_GEN;
Willy Tarreauaef84482022-11-14 07:03:16 +01001091 __fallthrough;
Willy Tarreaufa11df52022-05-05 13:48:40 +02001092 case ADDCRT_ST_GEN:
Willy Tarreau6b6c3632022-05-05 13:43:49 +02001093 bind_conf_node = ctx->bind_conf_node; /* get the previous ptr from the yield */
Willy Tarreau1b948ef2022-05-05 13:50:46 +02001094 if (bind_conf_node == NULL)
1095 bind_conf_node = crtlist->bind_conf;
1096 for (; bind_conf_node; bind_conf_node = bind_conf_node->next) {
1097 struct bind_conf *bind_conf = bind_conf_node->bind_conf;
1098 struct sni_ctx *sni;
1099
Christopher Fauletc642d7c2022-06-01 16:31:09 +02001100 ctx->bind_conf_node = bind_conf_node;
1101
Willy Tarreau1b948ef2022-05-05 13:50:46 +02001102 /* yield every 10 generations */
1103 if (i > 10) {
Christopher Fauletc642d7c2022-06-01 16:31:09 +02001104 applet_have_more_data(appctx); /* let's come back later */
Willy Tarreau1b948ef2022-05-05 13:50:46 +02001105 goto yield;
1106 }
William Lallemandc756bbd2020-05-13 17:23:59 +02001107
Christopher Fauletc642d7c2022-06-01 16:31:09 +02001108 /* display one dot for each new instance */
1109 if (applet_putstr(appctx, ".") == -1)
1110 goto yield;
1111
Willy Tarreau1b948ef2022-05-05 13:50:46 +02001112 /* we don't support multi-cert bundles, only simple ones */
Christopher Fauletc642d7c2022-06-01 16:31:09 +02001113 ctx->err = NULL;
1114 errcode |= ckch_inst_new_load_store(store->path, store, bind_conf, entry->ssl_conf, entry->filters, entry->fcount, &new_inst, &ctx->err);
1115 if (errcode & ERR_CODE) {
1116 ctx->state = ADDCRT_ST_ERROR;
Willy Tarreau1b948ef2022-05-05 13:50:46 +02001117 goto error;
Christopher Fauletc642d7c2022-06-01 16:31:09 +02001118 }
William Lallemandc756bbd2020-05-13 17:23:59 +02001119
Willy Tarreau1b948ef2022-05-05 13:50:46 +02001120 /* we need to initialize the SSL_CTX generated */
1121 /* this iterate on the newly generated SNIs in the new instance to prepare their SSL_CTX */
1122 list_for_each_entry(sni, &new_inst->sni_ctx, by_ckch_inst) {
1123 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 +02001124 ctx->err = NULL;
1125 errcode |= ssl_sock_prep_ctx_and_inst(bind_conf, new_inst->ssl_conf, sni->ctx, sni->ckch_inst, &ctx->err);
1126 if (errcode & ERR_CODE) {
1127 ctx->state = ADDCRT_ST_ERROR;
William Lallemandc756bbd2020-05-13 17:23:59 +02001128 goto error;
Christopher Fauletc642d7c2022-06-01 16:31:09 +02001129 }
William Lallemandc756bbd2020-05-13 17:23:59 +02001130 }
Willy Tarreau1b948ef2022-05-05 13:50:46 +02001131 }
Christopher Fauletc642d7c2022-06-01 16:31:09 +02001132
Willy Tarreau1b948ef2022-05-05 13:50:46 +02001133 i++;
1134 LIST_APPEND(&store->ckch_inst, &new_inst->by_ckchs);
1135 LIST_APPEND(&entry->ckch_inst, &new_inst->by_crtlist_entry);
1136 new_inst->crtlist_entry = entry;
William Lallemandc756bbd2020-05-13 17:23:59 +02001137 }
Willy Tarreaufa11df52022-05-05 13:48:40 +02001138 ctx->state = ADDCRT_ST_INSERT;
Willy Tarreauaef84482022-11-14 07:03:16 +01001139 __fallthrough;
Willy Tarreaufa11df52022-05-05 13:48:40 +02001140 case ADDCRT_ST_INSERT:
William Lallemandcb6c5f42022-06-20 16:51:53 +02001141 /* the insertion is called for every instance of the store, not
1142 * only the one we generated.
1143 * But the ssl_sock_load_cert_sni() skip the sni already
1144 * inserted. Not every instance has a bind_conf, it could be
1145 * the store of a server so we should be careful */
1146
Willy Tarreau1b948ef2022-05-05 13:50:46 +02001147 list_for_each_entry(new_inst, &store->ckch_inst, by_ckchs) {
William Lallemandcb6c5f42022-06-20 16:51:53 +02001148 if (!new_inst->bind_conf) /* this is a server instance */
1149 continue;
Willy Tarreau1b948ef2022-05-05 13:50:46 +02001150 HA_RWLOCK_WRLOCK(SNI_LOCK, &new_inst->bind_conf->sni_lock);
1151 ssl_sock_load_cert_sni(new_inst, new_inst->bind_conf);
1152 HA_RWLOCK_WRUNLOCK(SNI_LOCK, &new_inst->bind_conf->sni_lock);
1153 }
1154 entry->linenum = ++crtlist->linecount;
Christopher Fauletc642d7c2022-06-01 16:31:09 +02001155 ctx->entry = NULL;
1156 ctx->state = ADDCRT_ST_SUCCESS;
Willy Tarreauaef84482022-11-14 07:03:16 +01001157 __fallthrough;
Christopher Fauletc642d7c2022-06-01 16:31:09 +02001158 case ADDCRT_ST_SUCCESS:
1159 chunk_reset(&trash);
1160 chunk_appendf(&trash, "\n");
1161 if (ctx->err)
1162 chunk_appendf(&trash, "%s", ctx->err);
1163 chunk_appendf(&trash, "Success!\n");
1164 if (applet_putchk(appctx, &trash) == -1)
1165 goto yield;
1166 ctx->state = ADDCRT_ST_FIN;
1167 break;
1168
1169 case ADDCRT_ST_ERROR:
1170 error:
1171 chunk_printf(&trash, "\n%sFailed!\n", ctx->err);
1172 if (applet_putchk(appctx, &trash) == -1)
1173 goto yield;
1174 break;
1175
Willy Tarreaufa11df52022-05-05 13:48:40 +02001176 default:
1177 break;
William Lallemandc756bbd2020-05-13 17:23:59 +02001178 }
1179
Christopher Fauletc642d7c2022-06-01 16:31:09 +02001180end:
William Lallemandc756bbd2020-05-13 17:23:59 +02001181 /* success: call the release function and don't come back */
1182 return 1;
1183yield:
William Lallemandc756bbd2020-05-13 17:23:59 +02001184 return 0; /* should come back */
William Lallemandc756bbd2020-05-13 17:23:59 +02001185}
1186
1187
1188/*
1189 * Parse a "add ssl crt-list <crt-list> <certfile>" line.
Willy Tarreau6b6c3632022-05-05 13:43:49 +02001190 * Filters and option must be passed through payload.
1191 * It sets a struct add_crtlist_ctx.
William Lallemandc756bbd2020-05-13 17:23:59 +02001192 */
1193static int cli_parse_add_crtlist(char **args, char *payload, struct appctx *appctx, void *private)
1194{
Willy Tarreau6b6c3632022-05-05 13:43:49 +02001195 struct add_crtlist_ctx *ctx = applet_reserve_svcctx(appctx, sizeof(*ctx));
William Lallemandc756bbd2020-05-13 17:23:59 +02001196 int cfgerr = 0;
1197 struct ckch_store *store;
1198 char *err = NULL;
1199 char path[MAXPATHLEN+1];
1200 char *crtlist_path;
1201 char *cert_path = NULL;
1202 struct ebmb_node *eb;
1203 struct ebpt_node *inserted;
1204 struct crtlist *crtlist;
1205 struct crtlist_entry *entry = NULL;
William Lallemand99cc2182020-06-25 15:19:51 +02001206 char *end;
William Lallemandc756bbd2020-05-13 17:23:59 +02001207
1208 if (!cli_has_level(appctx, ACCESS_LVL_ADMIN))
1209 return 1;
1210
1211 if (!*args[3] || (!payload && !*args[4]))
1212 return cli_err(appctx, "'add ssl crtlist' expects a filename and a certificate name\n");
1213
1214 crtlist_path = args[3];
1215
William Lallemand99cc2182020-06-25 15:19:51 +02001216 /* strip trailing slashes, including first one */
1217 for (end = crtlist_path + strlen(crtlist_path) - 1; end >= crtlist_path && *end == '/'; end--)
1218 *end = 0;
1219
William Lallemandc756bbd2020-05-13 17:23:59 +02001220 if (HA_SPIN_TRYLOCK(CKCH_LOCK, &ckch_lock))
1221 return cli_err(appctx, "Operations on certificates are currently locked!\n");
1222
1223 eb = ebst_lookup(&crtlists_tree, crtlist_path);
1224 if (!eb) {
1225 memprintf(&err, "crt-list '%s' does not exist!", crtlist_path);
1226 goto error;
1227 }
1228 crtlist = ebmb_entry(eb, struct crtlist, node);
1229
1230 entry = crtlist_entry_new();
1231 if (entry == NULL) {
1232 memprintf(&err, "Not enough memory!");
1233 goto error;
1234 }
1235
1236 if (payload) {
1237 char *lf;
1238
1239 lf = strrchr(payload, '\n');
1240 if (lf) {
1241 memprintf(&err, "only one line of payload is supported!");
1242 goto error;
1243 }
1244 /* cert_path is filled here */
Remi Tricot-Le Bretonfb00f312021-03-23 16:41:53 +01001245 cfgerr |= crtlist_parse_line(payload, &cert_path, entry, "CLI", 1, 1, &err);
William Lallemandc756bbd2020-05-13 17:23:59 +02001246 if (cfgerr & ERR_CODE)
1247 goto error;
1248 } else {
1249 cert_path = args[4];
1250 }
1251
1252 if (!cert_path) {
1253 memprintf(&err, "'add ssl crtlist' should contain the certificate name in the payload");
1254 cfgerr |= ERR_ALERT | ERR_FATAL;
1255 goto error;
1256 }
1257
1258 if (eb_gettag(crtlist->entries.b[EB_RGHT])) {
1259 char *slash;
1260
1261 slash = strrchr(cert_path, '/');
1262 if (!slash) {
1263 memprintf(&err, "'%s' is a directory, certificate path '%s' must contain the directory path", (char *)crtlist->node.key, cert_path);
1264 goto error;
1265 }
1266 /* temporary replace / by 0 to do an strcmp */
1267 *slash = '\0';
1268 if (strcmp(cert_path, (char*)crtlist->node.key) != 0) {
1269 *slash = '/';
1270 memprintf(&err, "'%s' is a directory, certificate path '%s' must contain the directory path", (char *)crtlist->node.key, cert_path);
1271 goto error;
1272 }
1273 *slash = '/';
1274 }
1275
1276 if (*cert_path != '/' && global_ssl.crt_base) {
Willy Tarreau393e42a2022-05-09 10:31:28 +02001277 if ((strlen(global_ssl.crt_base) + 1 + strlen(cert_path)) > sizeof(path) ||
1278 snprintf(path, sizeof(path), "%s/%s", global_ssl.crt_base, cert_path) > sizeof(path)) {
William Lallemandc756bbd2020-05-13 17:23:59 +02001279 memprintf(&err, "'%s' : path too long", cert_path);
1280 cfgerr |= ERR_ALERT | ERR_FATAL;
1281 goto error;
1282 }
William Lallemandc756bbd2020-05-13 17:23:59 +02001283 cert_path = path;
1284 }
1285
1286 store = ckchs_lookup(cert_path);
1287 if (store == NULL) {
1288 memprintf(&err, "certificate '%s' does not exist!", cert_path);
1289 goto error;
1290 }
William Lallemandc756bbd2020-05-13 17:23:59 +02001291 if (store->ckch == NULL || store->ckch->cert == NULL) {
1292 memprintf(&err, "certificate '%s' is empty!", cert_path);
1293 goto error;
1294 }
1295
1296 /* check if it's possible to insert this new crtlist_entry */
1297 entry->node.key = store;
1298 inserted = ebpt_insert(&crtlist->entries, &entry->node);
1299 if (inserted != &entry->node) {
1300 memprintf(&err, "file already exists in this directory!");
1301 goto error;
1302 }
1303
1304 /* this is supposed to be a directory (EB_ROOT_UNIQUE), so no ssl_conf are allowed */
1305 if ((entry->ssl_conf || entry->filters) && eb_gettag(crtlist->entries.b[EB_RGHT])) {
1306 memprintf(&err, "this is a directory, SSL configuration and filters are not allowed");
1307 goto error;
1308 }
1309
Willy Tarreau2b718102021-04-21 07:32:39 +02001310 LIST_APPEND(&crtlist->ord_entries, &entry->by_crtlist);
William Lallemandc756bbd2020-05-13 17:23:59 +02001311 entry->crtlist = crtlist;
Willy Tarreau2b718102021-04-21 07:32:39 +02001312 LIST_APPEND(&store->crtlist_entry, &entry->by_ckch_store);
William Lallemandc756bbd2020-05-13 17:23:59 +02001313
Willy Tarreaufa11df52022-05-05 13:48:40 +02001314 ctx->state = ADDCRT_ST_INIT;
Willy Tarreau6b6c3632022-05-05 13:43:49 +02001315 ctx->crtlist = crtlist;
1316 ctx->entry = entry;
William Lallemandc756bbd2020-05-13 17:23:59 +02001317
1318 /* unlock is done in the release handler */
1319 return 0;
1320
1321error:
1322 crtlist_entry_free(entry);
1323 HA_SPIN_UNLOCK(CKCH_LOCK, &ckch_lock);
1324 err = memprintf(&err, "Can't edit the crt-list: %s\n", err ? err : "");
1325 return cli_dynerr(appctx, err);
1326}
1327
1328/* Parse a "del ssl crt-list <crt-list> <certfile>" line. */
1329static int cli_parse_del_crtlist(char **args, char *payload, struct appctx *appctx, void *private)
1330{
1331 struct ckch_store *store;
1332 char *err = NULL;
1333 char *crtlist_path, *cert_path;
1334 struct ebmb_node *ebmb;
1335 struct ebpt_node *ebpt;
1336 struct crtlist *crtlist;
1337 struct crtlist_entry *entry = NULL;
1338 struct ckch_inst *inst, *inst_s;
1339 int linenum = 0;
1340 char *colons;
William Lallemand99cc2182020-06-25 15:19:51 +02001341 char *end;
Remi Tricot-Le Bretonbc2c3862021-03-26 10:47:50 +01001342 int error_message_dumped = 0;
William Lallemandc756bbd2020-05-13 17:23:59 +02001343
1344 if (!cli_has_level(appctx, ACCESS_LVL_ADMIN))
1345 return 1;
1346
1347 if (!*args[3] || !*args[4])
1348 return cli_err(appctx, "'del ssl crtlist' expects a filename and a certificate name\n");
1349
1350 if (HA_SPIN_TRYLOCK(CKCH_LOCK, &ckch_lock))
1351 return cli_err(appctx, "Can't delete!\nOperations on certificates are currently locked!\n");
1352
1353 crtlist_path = args[3];
1354 cert_path = args[4];
1355
1356 colons = strchr(cert_path, ':');
1357 if (colons) {
1358 char *endptr;
1359
1360 linenum = strtol(colons + 1, &endptr, 10);
1361 if (colons + 1 == endptr || *endptr != '\0') {
1362 memprintf(&err, "wrong line number after colons in '%s'!", cert_path);
1363 goto error;
1364 }
1365 *colons = '\0';
1366 }
William Lallemand99cc2182020-06-25 15:19:51 +02001367
1368 /* strip trailing slashes, including first one */
1369 for (end = crtlist_path + strlen(crtlist_path) - 1; end >= crtlist_path && *end == '/'; end--)
1370 *end = 0;
1371
William Lallemandc756bbd2020-05-13 17:23:59 +02001372 /* look for crtlist */
1373 ebmb = ebst_lookup(&crtlists_tree, crtlist_path);
1374 if (!ebmb) {
1375 memprintf(&err, "crt-list '%s' does not exist!", crtlist_path);
1376 goto error;
1377 }
1378 crtlist = ebmb_entry(ebmb, struct crtlist, node);
1379
1380 /* look for store */
1381 store = ckchs_lookup(cert_path);
1382 if (store == NULL) {
1383 memprintf(&err, "certificate '%s' does not exist!", cert_path);
1384 goto error;
1385 }
William Lallemandc756bbd2020-05-13 17:23:59 +02001386 if (store->ckch == NULL || store->ckch->cert == NULL) {
1387 memprintf(&err, "certificate '%s' is empty!", cert_path);
1388 goto error;
1389 }
1390
1391 ebpt = ebpt_lookup(&crtlist->entries, store);
1392 if (!ebpt) {
1393 memprintf(&err, "certificate '%s' can't be found in crt-list '%s'!", cert_path, crtlist_path);
1394 goto error;
1395 }
1396
1397 /* list the line number of entries for errors in err, and select the right ebpt */
1398 for (; ebpt; ebpt = ebpt_next_dup(ebpt)) {
1399 struct crtlist_entry *tmp;
1400
1401 tmp = ebpt_entry(ebpt, struct crtlist_entry, node);
1402 memprintf(&err, "%s%s%d", err ? err : "", err ? ", " : "", tmp->linenum);
1403
1404 /* select the entry we wanted */
1405 if (linenum == 0 || tmp->linenum == linenum) {
1406 if (!entry)
1407 entry = tmp;
1408 }
1409 }
1410
1411 /* we didn't found the specified entry */
1412 if (!entry) {
1413 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);
1414 goto error;
1415 }
1416
1417 /* we didn't specified a line number but there were several entries */
1418 if (linenum == 0 && ebpt_next_dup(&entry->node)) {
1419 memprintf(&err, "found the certificate '%s' in several entries, please specify a line number preceded by colons (%s)!", cert_path, err ? err : NULL);
1420 goto error;
1421 }
1422
Remi Tricot-Le Bretonbc2c3862021-03-26 10:47:50 +01001423 /* Iterate over all the instances in order to see if any of them is a
1424 * default instance. If this is the case, the entry won't be suppressed. */
1425 list_for_each_entry_safe(inst, inst_s, &entry->ckch_inst, by_crtlist_entry) {
1426 if (inst->is_default && !inst->bind_conf->strict_sni) {
1427 if (!error_message_dumped) {
1428 memprintf(&err, "certificate '%s' cannot be deleted, it is used as default certificate by the following frontends:\n", cert_path);
1429 error_message_dumped = 1;
1430 }
1431 memprintf(&err, "%s\t- %s:%d\n", err, inst->bind_conf->file, inst->bind_conf->line);
1432 }
1433 }
1434 if (error_message_dumped)
1435 goto error;
1436
William Lallemandc756bbd2020-05-13 17:23:59 +02001437 /* upon error free the ckch_inst and everything inside */
1438
1439 ebpt_delete(&entry->node);
Willy Tarreau2b718102021-04-21 07:32:39 +02001440 LIST_DELETE(&entry->by_crtlist);
1441 LIST_DELETE(&entry->by_ckch_store);
William Lallemandc756bbd2020-05-13 17:23:59 +02001442
1443 list_for_each_entry_safe(inst, inst_s, &entry->ckch_inst, by_crtlist_entry) {
1444 struct sni_ctx *sni, *sni_s;
Remi Tricot-Le Breton4458b972021-02-19 17:41:55 +01001445 struct ckch_inst_link_ref *link_ref, *link_ref_s;
William Lallemandc756bbd2020-05-13 17:23:59 +02001446
1447 HA_RWLOCK_WRLOCK(SNI_LOCK, &inst->bind_conf->sni_lock);
1448 list_for_each_entry_safe(sni, sni_s, &inst->sni_ctx, by_ckch_inst) {
1449 ebmb_delete(&sni->name);
Willy Tarreau2b718102021-04-21 07:32:39 +02001450 LIST_DELETE(&sni->by_ckch_inst);
William Lallemandc756bbd2020-05-13 17:23:59 +02001451 SSL_CTX_free(sni->ctx);
1452 free(sni);
1453 }
1454 HA_RWLOCK_WRUNLOCK(SNI_LOCK, &inst->bind_conf->sni_lock);
Willy Tarreau2b718102021-04-21 07:32:39 +02001455 LIST_DELETE(&inst->by_ckchs);
Remi Tricot-Le Breton4458b972021-02-19 17:41:55 +01001456 list_for_each_entry_safe(link_ref, link_ref_s, &inst->cafile_link_refs, list) {
1457 LIST_DELETE(&link_ref->link->list);
1458 LIST_DELETE(&link_ref->list);
1459 free(link_ref);
1460 }
William Lallemandc756bbd2020-05-13 17:23:59 +02001461 free(inst);
1462 }
1463
1464 crtlist_free_filters(entry->filters);
1465 ssl_sock_free_ssl_conf(entry->ssl_conf);
1466 free(entry->ssl_conf);
1467 free(entry);
1468
1469 HA_SPIN_UNLOCK(CKCH_LOCK, &ckch_lock);
1470 err = memprintf(&err, "Entry '%s' deleted in crtlist '%s'!\n", cert_path, crtlist_path);
1471 return cli_dynmsg(appctx, LOG_NOTICE, err);
1472
1473error:
1474 HA_SPIN_UNLOCK(CKCH_LOCK, &ckch_lock);
1475 err = memprintf(&err, "Can't delete the entry: %s\n", err ? err : "");
1476 return cli_dynerr(appctx, err);
1477}
1478
1479
William Lallemandee8530c2020-06-23 18:19:42 +02001480/* unlink and free all crt-list and crt-list entries */
1481void crtlist_deinit()
1482{
1483 struct eb_node *node, *next;
1484 struct crtlist *crtlist;
1485
1486 node = eb_first(&crtlists_tree);
1487 while (node) {
1488 next = eb_next(node);
1489 crtlist = ebmb_entry(node, struct crtlist, node);
1490 crtlist_free(crtlist);
1491 node = next;
1492 }
1493}
1494
William Lallemandc756bbd2020-05-13 17:23:59 +02001495
1496/* register cli keywords */
1497static struct cli_kw_list cli_kws = {{ },{
Willy Tarreaub205bfd2021-05-07 11:38:37 +02001498 { { "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 },
1499 { { "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 },
1500 { { "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 +02001501 { { NULL }, NULL, NULL, NULL } }
1502};
1503
1504INITCALL1(STG_REGISTER, cli_register_kw, &cli_kws);
1505