blob: e5a5c24547ec23e84021b4e1f26b3ce871a1549c [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>
William Lallemanda14686d2023-02-07 18:38:05 +010030#include <haproxy/ssl_ocsp.h>
Willy Tarreau209108d2020-06-04 20:30:20 +020031#include <haproxy/ssl_sock.h>
Willy Tarreaucb086c62022-05-27 09:47:12 +020032#include <haproxy/stconn.h>
Willy Tarreau48fbcae2020-06-03 18:09:46 +020033#include <haproxy/tools.h>
William Lallemand6e9556b2020-05-12 17:52:44 +020034
Willy Tarreaua2fcca02022-05-05 11:53:23 +020035/* CLI context for "show ssl crt-list" or "dump ssl crt-list" */
36struct show_crtlist_ctx {
37 struct ebmb_node *crtlist_node; /* ebmb_node for the current crtlist */
38 struct crtlist_entry *entry; /* current entry */
39 int mode; /* 'd' for dump, 's' for show */
40};
William Lallemand6e9556b2020-05-12 17:52:44 +020041
Willy Tarreau6b6c3632022-05-05 13:43:49 +020042/* CLI context for "add ssl crt-list" */
43struct add_crtlist_ctx {
44 struct crtlist *crtlist;
45 struct crtlist_entry *entry;
46 struct bind_conf_list *bind_conf_node;
Christopher Fauletc642d7c2022-06-01 16:31:09 +020047 char *err;
Willy Tarreaufa11df52022-05-05 13:48:40 +020048 enum {
49 ADDCRT_ST_INIT = 0,
50 ADDCRT_ST_GEN,
51 ADDCRT_ST_INSERT,
Christopher Fauletc642d7c2022-06-01 16:31:09 +020052 ADDCRT_ST_SUCCESS,
53 ADDCRT_ST_ERROR,
Willy Tarreaufa11df52022-05-05 13:48:40 +020054 ADDCRT_ST_FIN,
55 } state;
Willy Tarreau6b6c3632022-05-05 13:43:49 +020056};
57
William Lallemand6e9556b2020-05-12 17:52:44 +020058/* release ssl bind conf */
59void ssl_sock_free_ssl_conf(struct ssl_bind_conf *conf)
60{
61 if (conf) {
62#if defined(OPENSSL_NPN_NEGOTIATED) && !defined(OPENSSL_NO_NEXTPROTONEG)
Willy Tarreau61cfdf42021-02-20 10:46:51 +010063 ha_free(&conf->npn_str);
William Lallemand6e9556b2020-05-12 17:52:44 +020064#endif
65#ifdef TLSEXT_TYPE_application_layer_protocol_negotiation
Willy Tarreau61cfdf42021-02-20 10:46:51 +010066 ha_free(&conf->alpn_str);
William Lallemand6e9556b2020-05-12 17:52:44 +020067#endif
Willy Tarreau61cfdf42021-02-20 10:46:51 +010068 ha_free(&conf->ca_file);
69 ha_free(&conf->ca_verify_file);
70 ha_free(&conf->crl_file);
71 ha_free(&conf->ciphers);
Ilya Shipitsinf34ed0b2020-11-21 14:37:34 +050072#ifdef HAVE_SSL_CTX_SET_CIPHERSUITES
Willy Tarreau61cfdf42021-02-20 10:46:51 +010073 ha_free(&conf->ciphersuites);
William Lallemand6e9556b2020-05-12 17:52:44 +020074#endif
Willy Tarreau61cfdf42021-02-20 10:46:51 +010075 ha_free(&conf->curves);
76 ha_free(&conf->ecdhe);
William Lallemand6e9556b2020-05-12 17:52:44 +020077 }
78}
79
William Lallemand82f2d2f2020-09-10 19:06:43 +020080/*
81 * Allocate and copy a ssl_bind_conf structure
82 */
83struct ssl_bind_conf *crtlist_dup_ssl_conf(struct ssl_bind_conf *src)
84{
85 struct ssl_bind_conf *dst;
86
87 if (!src)
88 return NULL;
89
90 dst = calloc(1, sizeof(*dst));
91 if (!dst)
92 return NULL;
93
94#if defined(OPENSSL_NPN_NEGOTIATED) && !defined(OPENSSL_NO_NEXTPROTONEG)
95 if (src->npn_str) {
96 dst->npn_str = strdup(src->npn_str);
97 if (!dst->npn_str)
98 goto error;
99 }
100#endif
101#ifdef TLSEXT_TYPE_application_layer_protocol_negotiation
102 if (src->alpn_str) {
103 dst->alpn_str = strdup(src->alpn_str);
104 if (!dst->alpn_str)
105 goto error;
106 }
107#endif
108 if (src->ca_file) {
109 dst->ca_file = strdup(src->ca_file);
110 if (!dst->ca_file)
111 goto error;
112 }
113 if (src->ca_verify_file) {
114 dst->ca_verify_file = strdup(src->ca_verify_file);
115 if (!dst->ca_verify_file)
116 goto error;
117 }
118 if (src->crl_file) {
119 dst->crl_file = strdup(src->crl_file);
120 if (!dst->crl_file)
121 goto error;
122 }
123 if (src->ciphers) {
124 dst->ciphers = strdup(src->ciphers);
125 if (!dst->ciphers)
126 goto error;
127 }
Ilya Shipitsinf34ed0b2020-11-21 14:37:34 +0500128#ifdef HAVE_SSL_CTX_SET_CIPHERSUITES
William Lallemand82f2d2f2020-09-10 19:06:43 +0200129 if (src->ciphersuites) {
130 dst->ciphersuites = strdup(src->ciphersuites);
131 if (!dst->ciphersuites)
132 goto error;
133 }
134#endif
135 if (src->curves) {
136 dst->curves = strdup(src->curves);
137 if (!dst->curves)
138 goto error;
139 }
140 if (src->ecdhe) {
141 dst->ecdhe = strdup(src->ecdhe);
142 if (!dst->ecdhe)
143 goto error;
144 }
145 return dst;
146
147error:
148 ssl_sock_free_ssl_conf(dst);
149 free(dst);
150
151 return NULL;
152}
William Lallemand6e9556b2020-05-12 17:52:44 +0200153
154/* free sni filters */
155void crtlist_free_filters(char **args)
156{
157 int i;
158
159 if (!args)
160 return;
161
162 for (i = 0; args[i]; i++)
163 free(args[i]);
164
165 free(args);
166}
167
168/* Alloc and duplicate a char ** array */
169char **crtlist_dup_filters(char **args, int fcount)
170{
171 char **dst;
172 int i;
173
174 if (fcount == 0)
175 return NULL;
176
177 dst = calloc(fcount + 1, sizeof(*dst));
178 if (!dst)
179 return NULL;
180
181 for (i = 0; i < fcount; i++) {
182 dst[i] = strdup(args[i]);
183 if (!dst[i])
184 goto error;
185 }
186 return dst;
187
188error:
189 crtlist_free_filters(dst);
190 return NULL;
191}
192
193/*
194 * Detach and free a crtlist_entry.
195 * Free the filters, the ssl_conf and call ckch_inst_free() for each ckch_inst
196 */
197void crtlist_entry_free(struct crtlist_entry *entry)
198{
199 struct ckch_inst *inst, *inst_s;
200
201 if (entry == NULL)
202 return;
203
204 ebpt_delete(&entry->node);
Willy Tarreau2b718102021-04-21 07:32:39 +0200205 LIST_DELETE(&entry->by_crtlist);
206 LIST_DELETE(&entry->by_ckch_store);
William Lallemand6e9556b2020-05-12 17:52:44 +0200207 crtlist_free_filters(entry->filters);
208 ssl_sock_free_ssl_conf(entry->ssl_conf);
209 free(entry->ssl_conf);
210 list_for_each_entry_safe(inst, inst_s, &entry->ckch_inst, by_crtlist_entry) {
211 ckch_inst_free(inst);
212 }
213 free(entry);
214}
William Lallemand5622c452020-09-10 19:08:49 +0200215/*
216 * Duplicate a crt_list entry and its content (ssl_conf, filters/fcount)
217 * Return a pointer to the new entry
218 */
219struct crtlist_entry *crtlist_entry_dup(struct crtlist_entry *src)
220{
221 struct crtlist_entry *entry;
222
223 if (src == NULL)
224 return NULL;
225
226 entry = crtlist_entry_new();
227 if (entry == NULL)
228 return NULL;
229
230 if (src->filters) {
231 entry->filters = crtlist_dup_filters(src->filters, src->fcount);
232 if (!entry->filters)
233 goto error;
234 }
235 entry->fcount = src->fcount;
236 if (src->ssl_conf) {
237 entry->ssl_conf = crtlist_dup_ssl_conf(src->ssl_conf);
238 if (!entry->ssl_conf)
239 goto error;
240 }
241 entry->crtlist = src->crtlist;
242
243 return entry;
244
245error:
246
247 crtlist_free_filters(entry->filters);
248 ssl_sock_free_ssl_conf(entry->ssl_conf);
249 free(entry->ssl_conf);
250 free(entry);
251
252 return NULL;
253}
William Lallemand6e9556b2020-05-12 17:52:44 +0200254
255/*
256 * Allocate and initialize a crtlist_entry
257 */
258struct crtlist_entry *crtlist_entry_new()
259{
260 struct crtlist_entry *entry;
261
262 entry = calloc(1, sizeof(*entry));
263 if (entry == NULL)
264 return NULL;
265
266 LIST_INIT(&entry->ckch_inst);
267
Willy Tarreau2b718102021-04-21 07:32:39 +0200268 /* initialize the nodes so we can LIST_DELETE in any cases */
William Lallemand6e9556b2020-05-12 17:52:44 +0200269 LIST_INIT(&entry->by_crtlist);
270 LIST_INIT(&entry->by_ckch_store);
271
272 return entry;
273}
274
275/* Free a crtlist, from the crt_entry to the content of the ssl_conf */
276void crtlist_free(struct crtlist *crtlist)
277{
278 struct crtlist_entry *entry, *s_entry;
William Lallemand6a3168a2020-06-23 11:43:35 +0200279 struct bind_conf_list *bind_conf_node;
William Lallemand6e9556b2020-05-12 17:52:44 +0200280
281 if (crtlist == NULL)
282 return;
283
William Lallemand6a3168a2020-06-23 11:43:35 +0200284 bind_conf_node = crtlist->bind_conf;
285 while (bind_conf_node) {
286 struct bind_conf_list *next = bind_conf_node->next;
287 free(bind_conf_node);
288 bind_conf_node = next;
289 }
290
William Lallemand6e9556b2020-05-12 17:52:44 +0200291 list_for_each_entry_safe(entry, s_entry, &crtlist->ord_entries, by_crtlist) {
292 crtlist_entry_free(entry);
293 }
294 ebmb_delete(&crtlist->node);
295 free(crtlist);
296}
297
298/* Alloc and initialize a struct crtlist
299 * <filename> is the key of the ebmb_node
300 * <unique> initialize the list of entries to be unique (1) or not (0)
301 */
302struct crtlist *crtlist_new(const char *filename, int unique)
303{
304 struct crtlist *newlist;
305
306 newlist = calloc(1, sizeof(*newlist) + strlen(filename) + 1);
307 if (newlist == NULL)
308 return NULL;
309
310 memcpy(newlist->node.key, filename, strlen(filename) + 1);
311 if (unique)
312 newlist->entries = EB_ROOT_UNIQUE;
313 else
314 newlist->entries = EB_ROOT;
315
316 LIST_INIT(&newlist->ord_entries);
317
318 return newlist;
319}
320
321/*
322 * Read a single crt-list line. /!\ alter the <line> string.
323 * Fill <crt_path> and <crtlist_entry>
324 * <crtlist_entry> must be alloc and free by the caller
325 * <crtlist_entry->ssl_conf> is alloc by the function
326 * <crtlist_entry->filters> is alloc by the function
327 * <crt_path> is a ptr in <line>
328 * Return an error code
329 */
Remi Tricot-Le Bretonfb00f312021-03-23 16:41:53 +0100330int 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 +0200331{
332 int cfgerr = 0;
333 int arg, newarg, cur_arg, i, ssl_b = 0, ssl_e = 0;
334 char *end;
335 char *args[MAX_CRT_ARGS + 1];
336 struct ssl_bind_conf *ssl_conf = NULL;
337
338 if (!line || !crt_path || !entry)
339 return ERR_ALERT | ERR_FATAL;
340
341 end = line + strlen(line);
342 if (end-line >= CRT_LINESIZE-1 && *(end-1) != '\n') {
343 /* Check if we reached the limit and the last char is not \n.
344 * Watch out for the last line without the terminating '\n'!
345 */
Tim Duesterhus6d07fae2020-09-29 18:00:27 +0200346 memprintf(err, "parsing [%s:%d]: line too long, limit is %d characters",
347 file, linenum, CRT_LINESIZE-1);
William Lallemand6e9556b2020-05-12 17:52:44 +0200348 cfgerr |= ERR_ALERT | ERR_FATAL;
349 goto error;
350 }
351 arg = 0;
352 newarg = 1;
353 while (*line) {
354 if (isspace((unsigned char)*line)) {
355 newarg = 1;
356 *line = 0;
357 } else if (*line == '[') {
358 if (ssl_b) {
Tim Duesterhus6d07fae2020-09-29 18:00:27 +0200359 memprintf(err, "parsing [%s:%d]: too many '['", file, linenum);
William Lallemand6e9556b2020-05-12 17:52:44 +0200360 cfgerr |= ERR_ALERT | ERR_FATAL;
361 goto error;
362 }
363 if (!arg) {
Tim Duesterhus6d07fae2020-09-29 18:00:27 +0200364 memprintf(err, "parsing [%s:%d]: file must start with a cert", file, linenum);
William Lallemand6e9556b2020-05-12 17:52:44 +0200365 cfgerr |= ERR_ALERT | ERR_FATAL;
366 goto error;
367 }
368 ssl_b = arg;
369 newarg = 1;
370 *line = 0;
371 } else if (*line == ']') {
372 if (ssl_e) {
Tim Duesterhus6d07fae2020-09-29 18:00:27 +0200373 memprintf(err, "parsing [%s:%d]: too many ']'", file, linenum);
William Lallemand6e9556b2020-05-12 17:52:44 +0200374 cfgerr |= ERR_ALERT | ERR_FATAL;
375 goto error;
376 }
377 if (!ssl_b) {
Tim Duesterhus6d07fae2020-09-29 18:00:27 +0200378 memprintf(err, "parsing [%s:%d]: missing '['", file, linenum);
William Lallemand6e9556b2020-05-12 17:52:44 +0200379 cfgerr |= ERR_ALERT | ERR_FATAL;
380 goto error;
381 }
382 ssl_e = arg;
383 newarg = 1;
384 *line = 0;
385 } else if (newarg) {
386 if (arg == MAX_CRT_ARGS) {
Tim Duesterhus6d07fae2020-09-29 18:00:27 +0200387 memprintf(err, "parsing [%s:%d]: too many args ", file, linenum);
William Lallemand6e9556b2020-05-12 17:52:44 +0200388 cfgerr |= ERR_ALERT | ERR_FATAL;
389 goto error;
390 }
391 newarg = 0;
392 args[arg++] = line;
393 }
394 line++;
395 }
396 args[arg++] = line;
397
398 /* empty line */
399 if (!*args[0]) {
400 cfgerr |= ERR_NONE;
401 goto error;
402 }
403
404 *crt_path = args[0];
405
406 if (ssl_b) {
William Lallemandd85227f2023-02-07 17:06:35 +0100407 if (ssl_b > 1) {
408 memprintf(err, "parsing [%s:%d]: malformated line, filters can't be between filename and options!", file, linenum);
409 cfgerr |= ERR_WARN;
410 }
411
William Lallemand6e9556b2020-05-12 17:52:44 +0200412 ssl_conf = calloc(1, sizeof *ssl_conf);
413 if (!ssl_conf) {
414 memprintf(err, "not enough memory!");
415 cfgerr |= ERR_ALERT | ERR_FATAL;
416 goto error;
417 }
418 }
Remi Tricot-Le Bretonfb00f312021-03-23 16:41:53 +0100419
William Lallemand6e9556b2020-05-12 17:52:44 +0200420 cur_arg = ssl_b ? ssl_b : 1;
421 while (cur_arg < ssl_e) {
422 newarg = 0;
William Lallemandaf678062023-02-13 10:58:13 +0100423 for (i = 0; ssl_crtlist_kws[i].kw != NULL; i++) {
424 if (strcmp(ssl_crtlist_kws[i].kw, args[cur_arg]) == 0) {
William Lallemand6e9556b2020-05-12 17:52:44 +0200425 newarg = 1;
William Lallemandaf678062023-02-13 10:58:13 +0100426 cfgerr |= ssl_crtlist_kws[i].parse(args, cur_arg, NULL, ssl_conf, from_cli, err);
427 if (cur_arg + 1 + ssl_crtlist_kws[i].skip > ssl_e) {
Tim Duesterhus6d07fae2020-09-29 18:00:27 +0200428 memprintf(err, "parsing [%s:%d]: ssl args out of '[]' for %s",
429 file, linenum, args[cur_arg]);
William Lallemand6e9556b2020-05-12 17:52:44 +0200430 cfgerr |= ERR_ALERT | ERR_FATAL;
431 goto error;
432 }
William Lallemandaf678062023-02-13 10:58:13 +0100433 cur_arg += 1 + ssl_crtlist_kws[i].skip;
William Lallemand6e9556b2020-05-12 17:52:44 +0200434 break;
435 }
436 }
437 if (!cfgerr && !newarg) {
Tim Duesterhus6d07fae2020-09-29 18:00:27 +0200438 memprintf(err, "parsing [%s:%d]: unknown ssl keyword %s",
439 file, linenum, args[cur_arg]);
William Lallemand6e9556b2020-05-12 17:52:44 +0200440 cfgerr |= ERR_ALERT | ERR_FATAL;
441 goto error;
442 }
443 }
444 entry->linenum = linenum;
445 entry->ssl_conf = ssl_conf;
446 entry->filters = crtlist_dup_filters(&args[cur_arg], arg - cur_arg - 1);
447 entry->fcount = arg - cur_arg - 1;
448
449 return cfgerr;
450
451error:
452 crtlist_free_filters(entry->filters);
453 entry->filters = NULL;
454 ssl_sock_free_ssl_conf(entry->ssl_conf);
Willy Tarreau61cfdf42021-02-20 10:46:51 +0100455 ha_free(&entry->ssl_conf);
William Lallemand6e9556b2020-05-12 17:52:44 +0200456 return cfgerr;
457}
458
459
460
461/* This function parse a crt-list file and store it in a struct crtlist, each line is a crtlist_entry structure
462 * Fill the <crtlist> argument with a pointer to a new crtlist struct
463 *
464 * This function tries to open and store certificate files.
465 */
466int crtlist_parse_file(char *file, struct bind_conf *bind_conf, struct proxy *curproxy, struct crtlist **crtlist, char **err)
467{
468 struct crtlist *newlist;
469 struct crtlist_entry *entry = NULL;
470 char thisline[CRT_LINESIZE];
William Lallemand6e9556b2020-05-12 17:52:44 +0200471 FILE *f;
472 struct stat buf;
473 int linenum = 0;
474 int cfgerr = 0;
Tim Duesterhusb9f6acc2020-09-29 18:00:28 +0200475 int missing_lf = -1;
William Lallemand6e9556b2020-05-12 17:52:44 +0200476
477 if ((f = fopen(file, "r")) == NULL) {
478 memprintf(err, "cannot open file '%s' : %s", file, strerror(errno));
479 return ERR_ALERT | ERR_FATAL;
480 }
481
482 newlist = crtlist_new(file, 0);
483 if (newlist == NULL) {
484 memprintf(err, "Not enough memory!");
485 cfgerr |= ERR_ALERT | ERR_FATAL;
486 goto error;
487 }
488
489 while (fgets(thisline, sizeof(thisline), f) != NULL) {
490 char *end;
491 char *line = thisline;
492 char *crt_path;
William Lallemand86c2dd62020-11-20 14:23:38 +0100493 char path[MAXPATHLEN+1];
William Lallemand6e9556b2020-05-12 17:52:44 +0200494 struct ckch_store *ckchs;
William Lallemand77e1c6f2020-11-20 18:26:09 +0100495 int found = 0;
William Lallemand6e9556b2020-05-12 17:52:44 +0200496
Tim Duesterhusb9f6acc2020-09-29 18:00:28 +0200497 if (missing_lf != -1) {
498 memprintf(err, "parsing [%s:%d]: Stray NUL character at position %d.\n",
499 file, linenum, (missing_lf + 1));
500 cfgerr |= ERR_ALERT | ERR_FATAL;
501 missing_lf = -1;
502 break;
503 }
504
William Lallemand6e9556b2020-05-12 17:52:44 +0200505 linenum++;
506 end = line + strlen(line);
507 if (end-line == sizeof(thisline)-1 && *(end-1) != '\n') {
508 /* Check if we reached the limit and the last char is not \n.
509 * Watch out for the last line without the terminating '\n'!
510 */
Tim Duesterhus6d07fae2020-09-29 18:00:27 +0200511 memprintf(err, "parsing [%s:%d]: line too long, limit is %d characters",
512 file, linenum, (int)sizeof(thisline)-1);
William Lallemand6e9556b2020-05-12 17:52:44 +0200513 cfgerr |= ERR_ALERT | ERR_FATAL;
514 break;
515 }
516
517 if (*line == '#' || *line == '\n' || *line == '\r')
518 continue;
519
Tim Duesterhusb9f6acc2020-09-29 18:00:28 +0200520 if (end > line && *(end-1) == '\n') {
521 /* kill trailing LF */
522 *(end - 1) = 0;
523 }
524 else {
525 /* mark this line as truncated */
526 missing_lf = end - line;
527 }
528
William Lallemand6e9556b2020-05-12 17:52:44 +0200529 entry = crtlist_entry_new();
530 if (entry == NULL) {
531 memprintf(err, "Not enough memory!");
532 cfgerr |= ERR_ALERT | ERR_FATAL;
533 goto error;
534 }
Tim Duesterhusb9f6acc2020-09-29 18:00:28 +0200535
Remi Tricot-Le Bretonfb00f312021-03-23 16:41:53 +0100536 cfgerr |= crtlist_parse_line(thisline, &crt_path, entry, file, linenum, 0, err);
William Lallemand20b0fed2020-09-28 15:45:16 +0200537 if (cfgerr & ERR_CODE)
William Lallemand6e9556b2020-05-12 17:52:44 +0200538 goto error;
539
540 /* empty line */
541 if (!crt_path || !*crt_path) {
542 crtlist_entry_free(entry);
543 entry = NULL;
544 continue;
545 }
546
547 if (*crt_path != '/' && global_ssl.crt_base) {
Willy Tarreau393e42a2022-05-09 10:31:28 +0200548 if ((strlen(global_ssl.crt_base) + 1 + strlen(crt_path)) > sizeof(path) ||
Willy Tarreau63fc9002022-05-09 21:14:04 +0200549 snprintf(path, sizeof(path), "%s/%s", global_ssl.crt_base, crt_path) > sizeof(path)) {
Tim Duesterhus6d07fae2020-09-29 18:00:27 +0200550 memprintf(err, "parsing [%s:%d]: '%s' : path too long",
551 file, linenum, crt_path);
William Lallemand6e9556b2020-05-12 17:52:44 +0200552 cfgerr |= ERR_ALERT | ERR_FATAL;
553 goto error;
554 }
William Lallemand6e9556b2020-05-12 17:52:44 +0200555 crt_path = path;
556 }
557
558 /* Look for a ckch_store or create one */
559 ckchs = ckchs_lookup(crt_path);
560 if (ckchs == NULL) {
William Lallemand47da8212020-09-10 19:13:27 +0200561 if (stat(crt_path, &buf) == 0) {
William Lallemand77e1c6f2020-11-20 18:26:09 +0100562 found++;
William Lallemand47da8212020-09-10 19:13:27 +0200563
William Lallemandbd8e6ed2020-09-16 16:08:08 +0200564 ckchs = ckchs_load_cert_file(crt_path, err);
William Lallemand47da8212020-09-10 19:13:27 +0200565 if (ckchs == NULL) {
566 cfgerr |= ERR_ALERT | ERR_FATAL;
567 goto error;
568 }
569
570 entry->node.key = ckchs;
571 entry->crtlist = newlist;
Remi Tricot-Le Bretonfb2b9982022-12-20 11:11:11 +0100572 if (entry->ssl_conf)
573 ckchs->data->ocsp_update_mode = entry->ssl_conf->ocsp_update;
William Lallemand47da8212020-09-10 19:13:27 +0200574 ebpt_insert(&newlist->entries, &entry->node);
Willy Tarreau2b718102021-04-21 07:32:39 +0200575 LIST_APPEND(&newlist->ord_entries, &entry->by_crtlist);
576 LIST_APPEND(&ckchs->crtlist_entry, &entry->by_ckch_store);
William Lallemand6e9556b2020-05-12 17:52:44 +0200577
William Lallemand73404572020-11-20 18:23:40 +0100578 } else if (global_ssl.extra_files & SSL_GF_BUNDLE) {
William Lallemand47da8212020-09-10 19:13:27 +0200579 /* If we didn't find the file, this could be a
William Lallemand51f784b2020-10-02 18:08:18 +0200580 bundle, since 2.3 we don't support multiple
581 certificate in the same OpenSSL store, so we
582 emulate it by loading each file separately. To
583 do so we need to duplicate the entry in the
584 crt-list because it becomes independent */
William Lallemand47da8212020-09-10 19:13:27 +0200585 char fp[MAXPATHLEN+1] = {0};
586 int n = 0;
587 struct crtlist_entry *entry_dup = entry; /* use the previous created entry */
588 for (n = 0; n < SSL_SOCK_NUM_KEYTYPES; n++) {
589 struct stat buf;
590 int ret;
591
William Lallemand86c2dd62020-11-20 14:23:38 +0100592 ret = snprintf(fp, sizeof(fp), "%s.%s", crt_path, SSL_SOCK_KEYTYPE_NAMES[n]);
William Lallemand47da8212020-09-10 19:13:27 +0200593 if (ret > sizeof(fp))
594 continue;
595
596 ckchs = ckchs_lookup(fp);
William Lallemand77e1c6f2020-11-20 18:26:09 +0100597 if (!ckchs) {
598 if (stat(fp, &buf) == 0) {
599 ckchs = ckchs_load_cert_file(fp, err);
600 if (!ckchs) {
William Lallemand47da8212020-09-10 19:13:27 +0200601 cfgerr |= ERR_ALERT | ERR_FATAL;
602 goto error;
603 }
William Lallemand77e1c6f2020-11-20 18:26:09 +0100604 } else {
605 continue; /* didn't find this extension, skip */
606 }
607 }
608 found++;
609 linenum++; /* we duplicate the line for this entry in the bundle */
610 if (!entry_dup) { /* if the entry was used, duplicate one */
611 linenum++;
612 entry_dup = crtlist_entry_dup(entry);
613 if (!entry_dup) {
614 cfgerr |= ERR_ALERT | ERR_FATAL;
615 goto error;
William Lallemand47da8212020-09-10 19:13:27 +0200616 }
William Lallemand77e1c6f2020-11-20 18:26:09 +0100617 entry_dup->linenum = linenum;
618 }
William Lallemand47da8212020-09-10 19:13:27 +0200619
William Lallemand77e1c6f2020-11-20 18:26:09 +0100620 entry_dup->node.key = ckchs;
621 entry_dup->crtlist = newlist;
William Lallemanda14686d2023-02-07 18:38:05 +0100622
623 cfgerr |= ocsp_update_check_cfg_consistency(ckchs, entry, crt_path, err);
624 if (cfgerr & ERR_FATAL)
625 goto error;
626
Remi Tricot-Le Bretonfc92b8b2023-01-09 12:02:46 +0100627 if (entry->ssl_conf)
628 ckchs->data->ocsp_update_mode = entry->ssl_conf->ocsp_update;
William Lallemand77e1c6f2020-11-20 18:26:09 +0100629 ebpt_insert(&newlist->entries, &entry_dup->node);
Willy Tarreau2b718102021-04-21 07:32:39 +0200630 LIST_APPEND(&newlist->ord_entries, &entry_dup->by_crtlist);
631 LIST_APPEND(&ckchs->crtlist_entry, &entry_dup->by_ckch_store);
William Lallemand47da8212020-09-10 19:13:27 +0200632
William Lallemand77e1c6f2020-11-20 18:26:09 +0100633 entry_dup = NULL; /* the entry was used, we need a new one next round */
William Lallemand47da8212020-09-10 19:13:27 +0200634 }
William Lallemandb7fdfdf2020-12-04 15:45:02 +0100635#if HA_OPENSSL_VERSION_NUMBER < 0x10101000L
636 if (found) {
637 memprintf(err, "%sCan't load '%s'. Loading a multi certificates bundle requires OpenSSL >= 1.1.1\n",
638 err && *err ? *err : "", crt_path);
639 cfgerr |= ERR_ALERT | ERR_FATAL;
640 }
641#endif
William Lallemand47da8212020-09-10 19:13:27 +0200642 }
William Lallemand77e1c6f2020-11-20 18:26:09 +0100643 if (!found) {
644 memprintf(err, "%sunable to stat SSL certificate from file '%s' : %s.\n",
645 err && *err ? *err : "", crt_path, strerror(errno));
646 cfgerr |= ERR_ALERT | ERR_FATAL;
647 }
648
William Lallemand50c03aa2020-11-06 16:24:07 +0100649 } else {
650 entry->node.key = ckchs;
651 entry->crtlist = newlist;
William Lallemanda14686d2023-02-07 18:38:05 +0100652
653 cfgerr |= ocsp_update_check_cfg_consistency(ckchs, entry, crt_path, err);
654 if (cfgerr & ERR_FATAL)
655 goto error;
656
Remi Tricot-Le Bretonfc92b8b2023-01-09 12:02:46 +0100657 if (entry->ssl_conf)
658 ckchs->data->ocsp_update_mode = entry->ssl_conf->ocsp_update;
William Lallemand50c03aa2020-11-06 16:24:07 +0100659 ebpt_insert(&newlist->entries, &entry->node);
Willy Tarreau2b718102021-04-21 07:32:39 +0200660 LIST_APPEND(&newlist->ord_entries, &entry->by_crtlist);
661 LIST_APPEND(&ckchs->crtlist_entry, &entry->by_ckch_store);
William Lallemand77e1c6f2020-11-20 18:26:09 +0100662 found++;
William Lallemand47da8212020-09-10 19:13:27 +0200663 }
William Lallemand6e9556b2020-05-12 17:52:44 +0200664 entry = NULL;
665 }
Tim Duesterhusb9f6acc2020-09-29 18:00:28 +0200666
667 if (missing_lf != -1) {
668 memprintf(err, "parsing [%s:%d]: Missing LF on last line, file might have been truncated at position %d.\n",
669 file, linenum, (missing_lf + 1));
670 cfgerr |= ERR_ALERT | ERR_FATAL;
671 }
672
William Lallemand6e9556b2020-05-12 17:52:44 +0200673 if (cfgerr & ERR_CODE)
674 goto error;
675
676 newlist->linecount = linenum;
677
678 fclose(f);
679 *crtlist = newlist;
680
681 return cfgerr;
682error:
683 crtlist_entry_free(entry);
684
685 fclose(f);
686 crtlist_free(newlist);
687 return cfgerr;
688}
689
690/* This function reads a directory and stores it in a struct crtlist, each file is a crtlist_entry structure
691 * Fill the <crtlist> argument with a pointer to a new crtlist struct
692 *
693 * This function tries to open and store certificate files.
694 */
695int crtlist_load_cert_dir(char *path, struct bind_conf *bind_conf, struct crtlist **crtlist, char **err)
696{
697 struct crtlist *dir;
698 struct dirent **de_list;
699 int i, n;
700 struct stat buf;
701 char *end;
702 char fp[MAXPATHLEN+1];
703 int cfgerr = 0;
704 struct ckch_store *ckchs;
William Lallemand6e9556b2020-05-12 17:52:44 +0200705
706 dir = crtlist_new(path, 1);
707 if (dir == NULL) {
708 memprintf(err, "not enough memory");
709 return ERR_ALERT | ERR_FATAL;
710 }
711
712 n = scandir(path, &de_list, 0, alphasort);
713 if (n < 0) {
714 memprintf(err, "%sunable to scan directory '%s' : %s.\n",
715 err && *err ? *err : "", path, strerror(errno));
716 cfgerr |= ERR_ALERT | ERR_FATAL;
717 }
718 else {
719 for (i = 0; i < n; i++) {
720 struct crtlist_entry *entry;
721 struct dirent *de = de_list[i];
722
723 end = strrchr(de->d_name, '.');
William Lallemand589570d2022-05-09 10:30:51 +0200724 if (end && (de->d_name[0] == '.' ||
725 strcmp(end, ".issuer") == 0 || strcmp(end, ".ocsp") == 0 ||
726 strcmp(end, ".sctl") == 0 || strcmp(end, ".key") == 0))
William Lallemand6e9556b2020-05-12 17:52:44 +0200727 goto ignore_entry;
728
729 snprintf(fp, sizeof(fp), "%s/%s", path, de->d_name);
730 if (stat(fp, &buf) != 0) {
731 memprintf(err, "%sunable to stat SSL certificate from file '%s' : %s.\n",
732 err && *err ? *err : "", fp, strerror(errno));
733 cfgerr |= ERR_ALERT | ERR_FATAL;
734 goto ignore_entry;
735 }
736 if (!S_ISREG(buf.st_mode))
737 goto ignore_entry;
738
739 entry = crtlist_entry_new();
740 if (entry == NULL) {
741 memprintf(err, "not enough memory '%s'", fp);
742 cfgerr |= ERR_ALERT | ERR_FATAL;
743 goto ignore_entry;
744 }
745
William Lallemand6e9556b2020-05-12 17:52:44 +0200746 ckchs = ckchs_lookup(fp);
747 if (ckchs == NULL)
William Lallemandbd8e6ed2020-09-16 16:08:08 +0200748 ckchs = ckchs_load_cert_file(fp, err);
William Lallemand6e9556b2020-05-12 17:52:44 +0200749 if (ckchs == NULL) {
750 free(de);
751 free(entry);
752 cfgerr |= ERR_ALERT | ERR_FATAL;
753 goto end;
754 }
755 entry->node.key = ckchs;
756 entry->crtlist = dir;
Willy Tarreau2b718102021-04-21 07:32:39 +0200757 LIST_APPEND(&ckchs->crtlist_entry, &entry->by_ckch_store);
758 LIST_APPEND(&dir->ord_entries, &entry->by_crtlist);
William Lallemand6e9556b2020-05-12 17:52:44 +0200759 ebpt_insert(&dir->entries, &entry->node);
760
761ignore_entry:
762 free(de);
763 }
764end:
765 free(de_list);
766 }
767
768 if (cfgerr & ERR_CODE) {
769 /* free the dir and entries on error */
770 crtlist_free(dir);
771 } else {
772 *crtlist = dir;
773 }
774 return cfgerr;
775
776}
777
William Lallemandc756bbd2020-05-13 17:23:59 +0200778/*
779 * Take an ssl_bind_conf structure and append the configuration line used to
780 * create it in the buffer
781 */
782static void dump_crtlist_sslconf(struct buffer *buf, const struct ssl_bind_conf *conf)
783{
784 int space = 0;
785
786 if (conf == NULL)
787 return;
788
789 chunk_appendf(buf, " [");
790#ifdef OPENSSL_NPN_NEGOTIATED
791 if (conf->npn_str) {
792 int len = conf->npn_len;
793 char *ptr = conf->npn_str;
794 int comma = 0;
795
796 if (space) chunk_appendf(buf, " ");
797 chunk_appendf(buf, "npn ");
798 while (len) {
799 unsigned short size;
800
801 size = *ptr;
802 ptr++;
803 if (comma)
804 chunk_memcat(buf, ",", 1);
805 chunk_memcat(buf, ptr, size);
806 ptr += size;
807 len -= size + 1;
808 comma = 1;
809 }
810 chunk_memcat(buf, "", 1); /* finish with a \0 */
811 space++;
812 }
813#endif
814#ifdef TLSEXT_TYPE_application_layer_protocol_negotiation
815 if (conf->alpn_str) {
816 int len = conf->alpn_len;
817 char *ptr = conf->alpn_str;
818 int comma = 0;
819
820 if (space) chunk_appendf(buf, " ");
821 chunk_appendf(buf, "alpn ");
822 while (len) {
823 unsigned short size;
824
825 size = *ptr;
826 ptr++;
827 if (comma)
828 chunk_memcat(buf, ",", 1);
829 chunk_memcat(buf, ptr, size);
830 ptr += size;
831 len -= size + 1;
832 comma = 1;
833 }
834 chunk_memcat(buf, "", 1); /* finish with a \0 */
835 space++;
836 }
837#endif
838 /* verify */
839 {
840 if (conf->verify == SSL_SOCK_VERIFY_NONE) {
841 if (space) chunk_appendf(buf, " ");
842 chunk_appendf(buf, "verify none");
843 space++;
844 } else if (conf->verify == SSL_SOCK_VERIFY_OPTIONAL) {
845 if (space) chunk_appendf(buf, " ");
846 chunk_appendf(buf, "verify optional");
847 space++;
848 } else if (conf->verify == SSL_SOCK_VERIFY_REQUIRED) {
849 if (space) chunk_appendf(buf, " ");
850 chunk_appendf(buf, "verify required");
851 space++;
852 }
853 }
854
855 if (conf->no_ca_names) {
856 if (space) chunk_appendf(buf, " ");
857 chunk_appendf(buf, "no-ca-names");
858 space++;
859 }
860
861 if (conf->early_data) {
862 if (space) chunk_appendf(buf, " ");
863 chunk_appendf(buf, "allow-0rtt");
864 space++;
865 }
866 if (conf->ca_file) {
867 if (space) chunk_appendf(buf, " ");
868 chunk_appendf(buf, "ca-file %s", conf->ca_file);
869 space++;
870 }
871 if (conf->crl_file) {
872 if (space) chunk_appendf(buf, " ");
873 chunk_appendf(buf, "crl-file %s", conf->crl_file);
874 space++;
875 }
876 if (conf->ciphers) {
877 if (space) chunk_appendf(buf, " ");
878 chunk_appendf(buf, "ciphers %s", conf->ciphers);
879 space++;
880 }
Ilya Shipitsinf34ed0b2020-11-21 14:37:34 +0500881#ifdef HAVE_SSL_CTX_SET_CIPHERSUITES
William Lallemandc756bbd2020-05-13 17:23:59 +0200882 if (conf->ciphersuites) {
883 if (space) chunk_appendf(buf, " ");
884 chunk_appendf(buf, "ciphersuites %s", conf->ciphersuites);
885 space++;
886 }
887#endif
888 if (conf->curves) {
889 if (space) chunk_appendf(buf, " ");
890 chunk_appendf(buf, "curves %s", conf->curves);
891 space++;
892 }
893 if (conf->ecdhe) {
894 if (space) chunk_appendf(buf, " ");
895 chunk_appendf(buf, "ecdhe %s", conf->ecdhe);
896 space++;
897 }
898
899 /* the crt-lists only support ssl-min-ver and ssl-max-ver */
William Lallemand8177ad92020-05-20 16:49:02 +0200900 if (conf->ssl_methods_cfg.min) {
William Lallemandc756bbd2020-05-13 17:23:59 +0200901 if (space) chunk_appendf(buf, " ");
William Lallemand8177ad92020-05-20 16:49:02 +0200902 chunk_appendf(buf, "ssl-min-ver %s", methodVersions[conf->ssl_methods_cfg.min].name);
William Lallemandc756bbd2020-05-13 17:23:59 +0200903 space++;
904 }
905
William Lallemand8177ad92020-05-20 16:49:02 +0200906 if (conf->ssl_methods_cfg.max) {
William Lallemandc756bbd2020-05-13 17:23:59 +0200907 if (space) chunk_appendf(buf, " ");
William Lallemand8177ad92020-05-20 16:49:02 +0200908 chunk_appendf(buf, "ssl-max-ver %s", methodVersions[conf->ssl_methods_cfg.max].name);
William Lallemandc756bbd2020-05-13 17:23:59 +0200909 space++;
910 }
911
Remi Tricot-Le Bretonca0c84a2023-03-02 15:49:52 +0100912 if (conf->ocsp_update != SSL_SOCK_OCSP_UPDATE_DFLT) {
913 if (space) chunk_appendf(buf, " ");
914 chunk_appendf(buf, "ocsp-update %s",
915 conf->ocsp_update == SSL_SOCK_OCSP_UPDATE_OFF ? "off" : "on");
916 space++;
917 }
918
William Lallemandc756bbd2020-05-13 17:23:59 +0200919 chunk_appendf(buf, "]");
920
921 return;
922}
923
924/* dump a list of filters */
925static void dump_crtlist_filters(struct buffer *buf, struct crtlist_entry *entry)
926{
927 int i;
928
929 if (!entry->fcount)
930 return;
931
932 for (i = 0; i < entry->fcount; i++) {
933 chunk_appendf(buf, " %s", entry->filters[i]);
934 }
935 return;
936}
937
938/************************** CLI functions ****************************/
939
940
Willy Tarreaua2fcca02022-05-05 11:53:23 +0200941/* CLI IO handler for '(show|dump) ssl crt-list'.
942 * It uses show_crtlist_ctx for the context.
943 */
William Lallemandc756bbd2020-05-13 17:23:59 +0200944static int cli_io_handler_dump_crtlist(struct appctx *appctx)
945{
Willy Tarreaua2fcca02022-05-05 11:53:23 +0200946 struct show_crtlist_ctx *ctx = appctx->svcctx;
William Lallemandc756bbd2020-05-13 17:23:59 +0200947 struct buffer *trash = alloc_trash_chunk();
William Lallemandc756bbd2020-05-13 17:23:59 +0200948 struct ebmb_node *lnode;
949
950 if (trash == NULL)
951 return 1;
952
953 /* dump the list of crt-lists */
Willy Tarreaua2fcca02022-05-05 11:53:23 +0200954 lnode = ctx->crtlist_node;
William Lallemandc756bbd2020-05-13 17:23:59 +0200955 if (lnode == NULL)
956 lnode = ebmb_first(&crtlists_tree);
957 while (lnode) {
958 chunk_appendf(trash, "%s\n", lnode->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 lnode = ebmb_next(lnode);
962 }
963 free_trash_chunk(trash);
964 return 1;
965yield:
Willy Tarreaua2fcca02022-05-05 11:53:23 +0200966 ctx->crtlist_node = lnode;
William Lallemandc756bbd2020-05-13 17:23:59 +0200967 free_trash_chunk(trash);
968 return 0;
969}
970
971/* CLI IO handler for '(show|dump) ssl crt-list <filename>' */
972static int cli_io_handler_dump_crtlist_entries(struct appctx *appctx)
973{
Willy Tarreaua2fcca02022-05-05 11:53:23 +0200974 struct show_crtlist_ctx *ctx = appctx->svcctx;
William Lallemandc756bbd2020-05-13 17:23:59 +0200975 struct buffer *trash = alloc_trash_chunk();
976 struct crtlist *crtlist;
William Lallemandc756bbd2020-05-13 17:23:59 +0200977 struct crtlist_entry *entry;
978
979 if (trash == NULL)
980 return 1;
981
Willy Tarreaua2fcca02022-05-05 11:53:23 +0200982 crtlist = ebmb_entry(ctx->crtlist_node, struct crtlist, node);
William Lallemandc756bbd2020-05-13 17:23:59 +0200983
Willy Tarreaua2fcca02022-05-05 11:53:23 +0200984 entry = ctx->entry;
William Lallemandc756bbd2020-05-13 17:23:59 +0200985 if (entry == NULL) {
986 entry = LIST_ELEM((crtlist->ord_entries).n, typeof(entry), by_crtlist);
987 chunk_appendf(trash, "# %s\n", crtlist->node.key);
Willy Tarreaud0a06d52022-05-18 15:07:19 +0200988 if (applet_putchk(appctx, trash) == -1)
William Lallemandc756bbd2020-05-13 17:23:59 +0200989 goto yield;
William Lallemandc756bbd2020-05-13 17:23:59 +0200990 }
991
992 list_for_each_entry_from(entry, &crtlist->ord_entries, by_crtlist) {
993 struct ckch_store *store;
994 const char *filename;
995
996 store = entry->node.key;
997 filename = store->path;
998 chunk_appendf(trash, "%s", filename);
Willy Tarreaua2fcca02022-05-05 11:53:23 +0200999 if (ctx->mode == 's') /* show */
William Lallemandc756bbd2020-05-13 17:23:59 +02001000 chunk_appendf(trash, ":%d", entry->linenum);
1001 dump_crtlist_sslconf(trash, entry->ssl_conf);
1002 dump_crtlist_filters(trash, entry);
1003 chunk_appendf(trash, "\n");
1004
Willy Tarreaud0a06d52022-05-18 15:07:19 +02001005 if (applet_putchk(appctx, trash) == -1)
William Lallemandc756bbd2020-05-13 17:23:59 +02001006 goto yield;
William Lallemandc756bbd2020-05-13 17:23:59 +02001007 }
1008 free_trash_chunk(trash);
1009 return 1;
1010yield:
Willy Tarreaua2fcca02022-05-05 11:53:23 +02001011 ctx->entry = entry;
William Lallemandc756bbd2020-05-13 17:23:59 +02001012 free_trash_chunk(trash);
1013 return 0;
1014}
1015
1016/* CLI argument parser for '(show|dump) ssl crt-list' */
1017static int cli_parse_dump_crtlist(char **args, char *payload, struct appctx *appctx, void *private)
1018{
Willy Tarreaua2fcca02022-05-05 11:53:23 +02001019 struct show_crtlist_ctx *ctx = applet_reserve_svcctx(appctx, sizeof(*ctx));
William Lallemandc756bbd2020-05-13 17:23:59 +02001020 struct ebmb_node *lnode;
1021 char *filename = NULL;
1022 int mode;
William Lallemand99cc2182020-06-25 15:19:51 +02001023 char *end;
William Lallemandc756bbd2020-05-13 17:23:59 +02001024
1025 if (!cli_has_level(appctx, ACCESS_LVL_ADMIN))
1026 return 1;
1027
Tim Duesterhuse5ff1412021-01-02 22:31:53 +01001028 if (*args[3] && strcmp(args[3], "-n") == 0) {
William Lallemandc756bbd2020-05-13 17:23:59 +02001029 mode = 's';
1030 filename = args[4];
1031 } else {
1032 mode = 'd';
1033 filename = args[3];
1034 }
1035
1036 if (mode == 's' && !*args[4])
1037 return cli_err(appctx, "'show ssl crt-list -n' expects a filename or a directory\n");
1038
1039 if (filename && *filename) {
William Lallemand99cc2182020-06-25 15:19:51 +02001040
1041
1042 /* strip trailing slashes, including first one */
1043 for (end = filename + strlen(filename) - 1; end >= filename && *end == '/'; end--)
1044 *end = 0;
1045
William Lallemandc756bbd2020-05-13 17:23:59 +02001046 lnode = ebst_lookup(&crtlists_tree, filename);
1047 if (lnode == NULL)
1048 return cli_err(appctx, "didn't find the specified filename\n");
1049
Willy Tarreaua2fcca02022-05-05 11:53:23 +02001050 ctx->crtlist_node = lnode;
William Lallemandc756bbd2020-05-13 17:23:59 +02001051 appctx->io_handler = cli_io_handler_dump_crtlist_entries;
1052 }
Willy Tarreaua2fcca02022-05-05 11:53:23 +02001053 ctx->mode = mode;
William Lallemandc756bbd2020-05-13 17:23:59 +02001054
1055 return 0;
1056}
1057
1058/* release function of the "add ssl crt-list' command, free things and unlock
Willy Tarreau6b6c3632022-05-05 13:43:49 +02001059 * the spinlock. It uses the add_crtlist_ctx.
1060 */
William Lallemandc756bbd2020-05-13 17:23:59 +02001061static void cli_release_add_crtlist(struct appctx *appctx)
1062{
Willy Tarreau6b6c3632022-05-05 13:43:49 +02001063 struct add_crtlist_ctx *ctx = appctx->svcctx;
1064 struct crtlist_entry *entry = ctx->entry;
William Lallemandc756bbd2020-05-13 17:23:59 +02001065
Christopher Fauletc642d7c2022-06-01 16:31:09 +02001066 if (entry) {
William Lallemandc756bbd2020-05-13 17:23:59 +02001067 struct ckch_inst *inst, *inst_s;
Christopher Fauletc642d7c2022-06-01 16:31:09 +02001068
William Lallemandc756bbd2020-05-13 17:23:59 +02001069 /* upon error free the ckch_inst and everything inside */
1070 ebpt_delete(&entry->node);
Willy Tarreau2b718102021-04-21 07:32:39 +02001071 LIST_DELETE(&entry->by_crtlist);
1072 LIST_DELETE(&entry->by_ckch_store);
William Lallemandc756bbd2020-05-13 17:23:59 +02001073
1074 list_for_each_entry_safe(inst, inst_s, &entry->ckch_inst, by_ckchs) {
1075 ckch_inst_free(inst);
1076 }
1077 crtlist_free_filters(entry->filters);
1078 ssl_sock_free_ssl_conf(entry->ssl_conf);
1079 free(entry->ssl_conf);
1080 free(entry);
1081 }
William Lallemandc756bbd2020-05-13 17:23:59 +02001082 HA_SPIN_UNLOCK(CKCH_LOCK, &ckch_lock);
Christopher Fauletc642d7c2022-06-01 16:31:09 +02001083 ha_free(&ctx->err);
William Lallemandc756bbd2020-05-13 17:23:59 +02001084}
1085
1086
1087/* IO Handler for the "add ssl crt-list" command It adds a new entry in the
1088 * crt-list and generates the ckch_insts for each bind_conf that uses this crt-list
1089 *
1090 * The logic is the same as the "commit ssl cert" command but without the
1091 * freeing of the old structures, because there are none.
Willy Tarreau6b6c3632022-05-05 13:43:49 +02001092 *
1093 * It uses the add_crtlist_ctx for the context.
William Lallemandc756bbd2020-05-13 17:23:59 +02001094 */
1095static int cli_io_handler_add_crtlist(struct appctx *appctx)
1096{
Willy Tarreau6b6c3632022-05-05 13:43:49 +02001097 struct add_crtlist_ctx *ctx = appctx->svcctx;
William Lallemandc756bbd2020-05-13 17:23:59 +02001098 struct bind_conf_list *bind_conf_node;
Willy Tarreauc12b3212022-05-27 11:08:15 +02001099 struct stconn *sc = appctx_sc(appctx);
Willy Tarreau6b6c3632022-05-05 13:43:49 +02001100 struct crtlist *crtlist = ctx->crtlist;
1101 struct crtlist_entry *entry = ctx->entry;
William Lallemandc756bbd2020-05-13 17:23:59 +02001102 struct ckch_store *store = entry->node.key;
William Lallemandc756bbd2020-05-13 17:23:59 +02001103 struct ckch_inst *new_inst;
William Lallemandc756bbd2020-05-13 17:23:59 +02001104 int i = 0;
1105 int errcode = 0;
1106
William Lallemandc756bbd2020-05-13 17:23:59 +02001107 /* for each bind_conf which use the crt-list, a new ckch_inst must be
1108 * created.
1109 */
Christopher Fauletda89e9b2023-01-04 14:11:10 +01001110 if (unlikely(sc_ic(sc)->flags & CF_SHUTW))
Christopher Fauletc642d7c2022-06-01 16:31:09 +02001111 goto end;
William Lallemandc756bbd2020-05-13 17:23:59 +02001112
Willy Tarreaufa11df52022-05-05 13:48:40 +02001113 switch (ctx->state) {
1114 case ADDCRT_ST_INIT:
Willy Tarreau1b948ef2022-05-05 13:50:46 +02001115 /* This state just print the update message */
Christopher Fauletc642d7c2022-06-01 16:31:09 +02001116 chunk_printf(&trash, "Inserting certificate '%s' in crt-list '%s'", store->path, crtlist->node.key);
1117 if (applet_putchk(appctx, &trash) == -1)
Willy Tarreau1b948ef2022-05-05 13:50:46 +02001118 goto yield;
Willy Tarreaufa11df52022-05-05 13:48:40 +02001119 ctx->state = ADDCRT_ST_GEN;
Willy Tarreauaef84482022-11-14 07:03:16 +01001120 __fallthrough;
Willy Tarreaufa11df52022-05-05 13:48:40 +02001121 case ADDCRT_ST_GEN:
Willy Tarreau6b6c3632022-05-05 13:43:49 +02001122 bind_conf_node = ctx->bind_conf_node; /* get the previous ptr from the yield */
Willy Tarreau1b948ef2022-05-05 13:50:46 +02001123 if (bind_conf_node == NULL)
1124 bind_conf_node = crtlist->bind_conf;
1125 for (; bind_conf_node; bind_conf_node = bind_conf_node->next) {
1126 struct bind_conf *bind_conf = bind_conf_node->bind_conf;
1127 struct sni_ctx *sni;
1128
Christopher Fauletc642d7c2022-06-01 16:31:09 +02001129 ctx->bind_conf_node = bind_conf_node;
1130
Willy Tarreau1b948ef2022-05-05 13:50:46 +02001131 /* yield every 10 generations */
1132 if (i > 10) {
Christopher Fauletc642d7c2022-06-01 16:31:09 +02001133 applet_have_more_data(appctx); /* let's come back later */
Willy Tarreau1b948ef2022-05-05 13:50:46 +02001134 goto yield;
1135 }
William Lallemandc756bbd2020-05-13 17:23:59 +02001136
Christopher Fauletc642d7c2022-06-01 16:31:09 +02001137 /* display one dot for each new instance */
1138 if (applet_putstr(appctx, ".") == -1)
1139 goto yield;
1140
Willy Tarreau1b948ef2022-05-05 13:50:46 +02001141 /* we don't support multi-cert bundles, only simple ones */
Christopher Fauletc642d7c2022-06-01 16:31:09 +02001142 ctx->err = NULL;
1143 errcode |= ckch_inst_new_load_store(store->path, store, bind_conf, entry->ssl_conf, entry->filters, entry->fcount, &new_inst, &ctx->err);
1144 if (errcode & ERR_CODE) {
1145 ctx->state = ADDCRT_ST_ERROR;
Willy Tarreau1b948ef2022-05-05 13:50:46 +02001146 goto error;
Christopher Fauletc642d7c2022-06-01 16:31:09 +02001147 }
William Lallemandc756bbd2020-05-13 17:23:59 +02001148
Willy Tarreau1b948ef2022-05-05 13:50:46 +02001149 /* we need to initialize the SSL_CTX generated */
1150 /* this iterate on the newly generated SNIs in the new instance to prepare their SSL_CTX */
1151 list_for_each_entry(sni, &new_inst->sni_ctx, by_ckch_inst) {
1152 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 +02001153 ctx->err = NULL;
1154 errcode |= ssl_sock_prep_ctx_and_inst(bind_conf, new_inst->ssl_conf, sni->ctx, sni->ckch_inst, &ctx->err);
1155 if (errcode & ERR_CODE) {
1156 ctx->state = ADDCRT_ST_ERROR;
William Lallemandc756bbd2020-05-13 17:23:59 +02001157 goto error;
Christopher Fauletc642d7c2022-06-01 16:31:09 +02001158 }
William Lallemandc756bbd2020-05-13 17:23:59 +02001159 }
Willy Tarreau1b948ef2022-05-05 13:50:46 +02001160 }
Christopher Fauletc642d7c2022-06-01 16:31:09 +02001161
Willy Tarreau1b948ef2022-05-05 13:50:46 +02001162 i++;
1163 LIST_APPEND(&store->ckch_inst, &new_inst->by_ckchs);
1164 LIST_APPEND(&entry->ckch_inst, &new_inst->by_crtlist_entry);
1165 new_inst->crtlist_entry = entry;
William Lallemandc756bbd2020-05-13 17:23:59 +02001166 }
Willy Tarreaufa11df52022-05-05 13:48:40 +02001167 ctx->state = ADDCRT_ST_INSERT;
Willy Tarreauaef84482022-11-14 07:03:16 +01001168 __fallthrough;
Willy Tarreaufa11df52022-05-05 13:48:40 +02001169 case ADDCRT_ST_INSERT:
William Lallemandcb6c5f42022-06-20 16:51:53 +02001170 /* the insertion is called for every instance of the store, not
1171 * only the one we generated.
1172 * But the ssl_sock_load_cert_sni() skip the sni already
1173 * inserted. Not every instance has a bind_conf, it could be
1174 * the store of a server so we should be careful */
1175
Willy Tarreau1b948ef2022-05-05 13:50:46 +02001176 list_for_each_entry(new_inst, &store->ckch_inst, by_ckchs) {
William Lallemandcb6c5f42022-06-20 16:51:53 +02001177 if (!new_inst->bind_conf) /* this is a server instance */
1178 continue;
Willy Tarreau1b948ef2022-05-05 13:50:46 +02001179 HA_RWLOCK_WRLOCK(SNI_LOCK, &new_inst->bind_conf->sni_lock);
1180 ssl_sock_load_cert_sni(new_inst, new_inst->bind_conf);
1181 HA_RWLOCK_WRUNLOCK(SNI_LOCK, &new_inst->bind_conf->sni_lock);
1182 }
1183 entry->linenum = ++crtlist->linecount;
Christopher Fauletc642d7c2022-06-01 16:31:09 +02001184 ctx->entry = NULL;
1185 ctx->state = ADDCRT_ST_SUCCESS;
Willy Tarreauaef84482022-11-14 07:03:16 +01001186 __fallthrough;
Christopher Fauletc642d7c2022-06-01 16:31:09 +02001187 case ADDCRT_ST_SUCCESS:
1188 chunk_reset(&trash);
1189 chunk_appendf(&trash, "\n");
1190 if (ctx->err)
1191 chunk_appendf(&trash, "%s", ctx->err);
1192 chunk_appendf(&trash, "Success!\n");
1193 if (applet_putchk(appctx, &trash) == -1)
1194 goto yield;
1195 ctx->state = ADDCRT_ST_FIN;
1196 break;
1197
1198 case ADDCRT_ST_ERROR:
1199 error:
1200 chunk_printf(&trash, "\n%sFailed!\n", ctx->err);
1201 if (applet_putchk(appctx, &trash) == -1)
1202 goto yield;
1203 break;
1204
Willy Tarreaufa11df52022-05-05 13:48:40 +02001205 default:
1206 break;
William Lallemandc756bbd2020-05-13 17:23:59 +02001207 }
1208
Christopher Fauletc642d7c2022-06-01 16:31:09 +02001209end:
William Lallemandc756bbd2020-05-13 17:23:59 +02001210 /* success: call the release function and don't come back */
1211 return 1;
1212yield:
William Lallemandc756bbd2020-05-13 17:23:59 +02001213 return 0; /* should come back */
William Lallemandc756bbd2020-05-13 17:23:59 +02001214}
1215
1216
1217/*
1218 * Parse a "add ssl crt-list <crt-list> <certfile>" line.
Willy Tarreau6b6c3632022-05-05 13:43:49 +02001219 * Filters and option must be passed through payload.
1220 * It sets a struct add_crtlist_ctx.
William Lallemandc756bbd2020-05-13 17:23:59 +02001221 */
1222static int cli_parse_add_crtlist(char **args, char *payload, struct appctx *appctx, void *private)
1223{
Willy Tarreau6b6c3632022-05-05 13:43:49 +02001224 struct add_crtlist_ctx *ctx = applet_reserve_svcctx(appctx, sizeof(*ctx));
William Lallemandc756bbd2020-05-13 17:23:59 +02001225 int cfgerr = 0;
1226 struct ckch_store *store;
1227 char *err = NULL;
1228 char path[MAXPATHLEN+1];
1229 char *crtlist_path;
1230 char *cert_path = NULL;
1231 struct ebmb_node *eb;
1232 struct ebpt_node *inserted;
1233 struct crtlist *crtlist;
1234 struct crtlist_entry *entry = NULL;
William Lallemand99cc2182020-06-25 15:19:51 +02001235 char *end;
William Lallemandc756bbd2020-05-13 17:23:59 +02001236
1237 if (!cli_has_level(appctx, ACCESS_LVL_ADMIN))
1238 return 1;
1239
1240 if (!*args[3] || (!payload && !*args[4]))
1241 return cli_err(appctx, "'add ssl crtlist' expects a filename and a certificate name\n");
1242
1243 crtlist_path = args[3];
1244
William Lallemand99cc2182020-06-25 15:19:51 +02001245 /* strip trailing slashes, including first one */
1246 for (end = crtlist_path + strlen(crtlist_path) - 1; end >= crtlist_path && *end == '/'; end--)
1247 *end = 0;
1248
William Lallemandc756bbd2020-05-13 17:23:59 +02001249 if (HA_SPIN_TRYLOCK(CKCH_LOCK, &ckch_lock))
1250 return cli_err(appctx, "Operations on certificates are currently locked!\n");
1251
1252 eb = ebst_lookup(&crtlists_tree, crtlist_path);
1253 if (!eb) {
1254 memprintf(&err, "crt-list '%s' does not exist!", crtlist_path);
1255 goto error;
1256 }
1257 crtlist = ebmb_entry(eb, struct crtlist, node);
1258
1259 entry = crtlist_entry_new();
1260 if (entry == NULL) {
1261 memprintf(&err, "Not enough memory!");
1262 goto error;
1263 }
1264
1265 if (payload) {
1266 char *lf;
1267
1268 lf = strrchr(payload, '\n');
1269 if (lf) {
1270 memprintf(&err, "only one line of payload is supported!");
1271 goto error;
1272 }
1273 /* cert_path is filled here */
Remi Tricot-Le Bretonfb00f312021-03-23 16:41:53 +01001274 cfgerr |= crtlist_parse_line(payload, &cert_path, entry, "CLI", 1, 1, &err);
William Lallemandc756bbd2020-05-13 17:23:59 +02001275 if (cfgerr & ERR_CODE)
1276 goto error;
1277 } else {
1278 cert_path = args[4];
1279 }
1280
1281 if (!cert_path) {
1282 memprintf(&err, "'add ssl crtlist' should contain the certificate name in the payload");
1283 cfgerr |= ERR_ALERT | ERR_FATAL;
1284 goto error;
1285 }
1286
1287 if (eb_gettag(crtlist->entries.b[EB_RGHT])) {
1288 char *slash;
1289
1290 slash = strrchr(cert_path, '/');
1291 if (!slash) {
1292 memprintf(&err, "'%s' is a directory, certificate path '%s' must contain the directory path", (char *)crtlist->node.key, cert_path);
1293 goto error;
1294 }
1295 /* temporary replace / by 0 to do an strcmp */
1296 *slash = '\0';
1297 if (strcmp(cert_path, (char*)crtlist->node.key) != 0) {
1298 *slash = '/';
1299 memprintf(&err, "'%s' is a directory, certificate path '%s' must contain the directory path", (char *)crtlist->node.key, cert_path);
1300 goto error;
1301 }
1302 *slash = '/';
1303 }
1304
1305 if (*cert_path != '/' && global_ssl.crt_base) {
Willy Tarreau393e42a2022-05-09 10:31:28 +02001306 if ((strlen(global_ssl.crt_base) + 1 + strlen(cert_path)) > sizeof(path) ||
1307 snprintf(path, sizeof(path), "%s/%s", global_ssl.crt_base, cert_path) > sizeof(path)) {
William Lallemandc756bbd2020-05-13 17:23:59 +02001308 memprintf(&err, "'%s' : path too long", cert_path);
1309 cfgerr |= ERR_ALERT | ERR_FATAL;
1310 goto error;
1311 }
William Lallemandc756bbd2020-05-13 17:23:59 +02001312 cert_path = path;
1313 }
1314
1315 store = ckchs_lookup(cert_path);
1316 if (store == NULL) {
1317 memprintf(&err, "certificate '%s' does not exist!", cert_path);
1318 goto error;
1319 }
William Lallemand52ddd992022-11-22 11:51:53 +01001320 if (store->data == NULL || store->data->cert == NULL) {
William Lallemandc756bbd2020-05-13 17:23:59 +02001321 memprintf(&err, "certificate '%s' is empty!", cert_path);
1322 goto error;
1323 }
1324
1325 /* check if it's possible to insert this new crtlist_entry */
1326 entry->node.key = store;
1327 inserted = ebpt_insert(&crtlist->entries, &entry->node);
1328 if (inserted != &entry->node) {
1329 memprintf(&err, "file already exists in this directory!");
1330 goto error;
1331 }
1332
1333 /* this is supposed to be a directory (EB_ROOT_UNIQUE), so no ssl_conf are allowed */
1334 if ((entry->ssl_conf || entry->filters) && eb_gettag(crtlist->entries.b[EB_RGHT])) {
1335 memprintf(&err, "this is a directory, SSL configuration and filters are not allowed");
1336 goto error;
1337 }
1338
Willy Tarreau2b718102021-04-21 07:32:39 +02001339 LIST_APPEND(&crtlist->ord_entries, &entry->by_crtlist);
William Lallemandc756bbd2020-05-13 17:23:59 +02001340 entry->crtlist = crtlist;
Willy Tarreau2b718102021-04-21 07:32:39 +02001341 LIST_APPEND(&store->crtlist_entry, &entry->by_ckch_store);
William Lallemandc756bbd2020-05-13 17:23:59 +02001342
Willy Tarreaufa11df52022-05-05 13:48:40 +02001343 ctx->state = ADDCRT_ST_INIT;
Willy Tarreau6b6c3632022-05-05 13:43:49 +02001344 ctx->crtlist = crtlist;
1345 ctx->entry = entry;
William Lallemandc756bbd2020-05-13 17:23:59 +02001346
1347 /* unlock is done in the release handler */
1348 return 0;
1349
1350error:
1351 crtlist_entry_free(entry);
1352 HA_SPIN_UNLOCK(CKCH_LOCK, &ckch_lock);
1353 err = memprintf(&err, "Can't edit the crt-list: %s\n", err ? err : "");
1354 return cli_dynerr(appctx, err);
1355}
1356
1357/* Parse a "del ssl crt-list <crt-list> <certfile>" line. */
1358static int cli_parse_del_crtlist(char **args, char *payload, struct appctx *appctx, void *private)
1359{
1360 struct ckch_store *store;
1361 char *err = NULL;
1362 char *crtlist_path, *cert_path;
1363 struct ebmb_node *ebmb;
1364 struct ebpt_node *ebpt;
1365 struct crtlist *crtlist;
1366 struct crtlist_entry *entry = NULL;
1367 struct ckch_inst *inst, *inst_s;
1368 int linenum = 0;
1369 char *colons;
William Lallemand99cc2182020-06-25 15:19:51 +02001370 char *end;
Remi Tricot-Le Bretonbc2c3862021-03-26 10:47:50 +01001371 int error_message_dumped = 0;
William Lallemandc756bbd2020-05-13 17:23:59 +02001372
1373 if (!cli_has_level(appctx, ACCESS_LVL_ADMIN))
1374 return 1;
1375
1376 if (!*args[3] || !*args[4])
1377 return cli_err(appctx, "'del ssl crtlist' expects a filename and a certificate name\n");
1378
1379 if (HA_SPIN_TRYLOCK(CKCH_LOCK, &ckch_lock))
1380 return cli_err(appctx, "Can't delete!\nOperations on certificates are currently locked!\n");
1381
1382 crtlist_path = args[3];
1383 cert_path = args[4];
1384
1385 colons = strchr(cert_path, ':');
1386 if (colons) {
1387 char *endptr;
1388
1389 linenum = strtol(colons + 1, &endptr, 10);
1390 if (colons + 1 == endptr || *endptr != '\0') {
1391 memprintf(&err, "wrong line number after colons in '%s'!", cert_path);
1392 goto error;
1393 }
1394 *colons = '\0';
1395 }
William Lallemand99cc2182020-06-25 15:19:51 +02001396
1397 /* strip trailing slashes, including first one */
1398 for (end = crtlist_path + strlen(crtlist_path) - 1; end >= crtlist_path && *end == '/'; end--)
1399 *end = 0;
1400
William Lallemandc756bbd2020-05-13 17:23:59 +02001401 /* look for crtlist */
1402 ebmb = ebst_lookup(&crtlists_tree, crtlist_path);
1403 if (!ebmb) {
1404 memprintf(&err, "crt-list '%s' does not exist!", crtlist_path);
1405 goto error;
1406 }
1407 crtlist = ebmb_entry(ebmb, struct crtlist, node);
1408
1409 /* look for store */
1410 store = ckchs_lookup(cert_path);
1411 if (store == NULL) {
1412 memprintf(&err, "certificate '%s' does not exist!", cert_path);
1413 goto error;
1414 }
William Lallemand52ddd992022-11-22 11:51:53 +01001415 if (store->data == NULL || store->data->cert == NULL) {
William Lallemandc756bbd2020-05-13 17:23:59 +02001416 memprintf(&err, "certificate '%s' is empty!", cert_path);
1417 goto error;
1418 }
1419
1420 ebpt = ebpt_lookup(&crtlist->entries, store);
1421 if (!ebpt) {
1422 memprintf(&err, "certificate '%s' can't be found in crt-list '%s'!", cert_path, crtlist_path);
1423 goto error;
1424 }
1425
1426 /* list the line number of entries for errors in err, and select the right ebpt */
1427 for (; ebpt; ebpt = ebpt_next_dup(ebpt)) {
1428 struct crtlist_entry *tmp;
1429
1430 tmp = ebpt_entry(ebpt, struct crtlist_entry, node);
1431 memprintf(&err, "%s%s%d", err ? err : "", err ? ", " : "", tmp->linenum);
1432
1433 /* select the entry we wanted */
1434 if (linenum == 0 || tmp->linenum == linenum) {
1435 if (!entry)
1436 entry = tmp;
1437 }
1438 }
1439
1440 /* we didn't found the specified entry */
1441 if (!entry) {
1442 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);
1443 goto error;
1444 }
1445
1446 /* we didn't specified a line number but there were several entries */
1447 if (linenum == 0 && ebpt_next_dup(&entry->node)) {
1448 memprintf(&err, "found the certificate '%s' in several entries, please specify a line number preceded by colons (%s)!", cert_path, err ? err : NULL);
1449 goto error;
1450 }
1451
Remi Tricot-Le Bretonbc2c3862021-03-26 10:47:50 +01001452 /* Iterate over all the instances in order to see if any of them is a
1453 * default instance. If this is the case, the entry won't be suppressed. */
1454 list_for_each_entry_safe(inst, inst_s, &entry->ckch_inst, by_crtlist_entry) {
1455 if (inst->is_default && !inst->bind_conf->strict_sni) {
1456 if (!error_message_dumped) {
1457 memprintf(&err, "certificate '%s' cannot be deleted, it is used as default certificate by the following frontends:\n", cert_path);
1458 error_message_dumped = 1;
1459 }
1460 memprintf(&err, "%s\t- %s:%d\n", err, inst->bind_conf->file, inst->bind_conf->line);
1461 }
1462 }
1463 if (error_message_dumped)
1464 goto error;
1465
William Lallemandc756bbd2020-05-13 17:23:59 +02001466 /* upon error free the ckch_inst and everything inside */
1467
1468 ebpt_delete(&entry->node);
Willy Tarreau2b718102021-04-21 07:32:39 +02001469 LIST_DELETE(&entry->by_crtlist);
1470 LIST_DELETE(&entry->by_ckch_store);
William Lallemandc756bbd2020-05-13 17:23:59 +02001471
1472 list_for_each_entry_safe(inst, inst_s, &entry->ckch_inst, by_crtlist_entry) {
1473 struct sni_ctx *sni, *sni_s;
Remi Tricot-Le Breton4458b972021-02-19 17:41:55 +01001474 struct ckch_inst_link_ref *link_ref, *link_ref_s;
William Lallemandc756bbd2020-05-13 17:23:59 +02001475
1476 HA_RWLOCK_WRLOCK(SNI_LOCK, &inst->bind_conf->sni_lock);
1477 list_for_each_entry_safe(sni, sni_s, &inst->sni_ctx, by_ckch_inst) {
1478 ebmb_delete(&sni->name);
Willy Tarreau2b718102021-04-21 07:32:39 +02001479 LIST_DELETE(&sni->by_ckch_inst);
William Lallemandc756bbd2020-05-13 17:23:59 +02001480 SSL_CTX_free(sni->ctx);
1481 free(sni);
1482 }
1483 HA_RWLOCK_WRUNLOCK(SNI_LOCK, &inst->bind_conf->sni_lock);
Willy Tarreau2b718102021-04-21 07:32:39 +02001484 LIST_DELETE(&inst->by_ckchs);
Remi Tricot-Le Breton4458b972021-02-19 17:41:55 +01001485 list_for_each_entry_safe(link_ref, link_ref_s, &inst->cafile_link_refs, list) {
1486 LIST_DELETE(&link_ref->link->list);
1487 LIST_DELETE(&link_ref->list);
1488 free(link_ref);
1489 }
William Lallemandc756bbd2020-05-13 17:23:59 +02001490 free(inst);
1491 }
1492
1493 crtlist_free_filters(entry->filters);
1494 ssl_sock_free_ssl_conf(entry->ssl_conf);
1495 free(entry->ssl_conf);
1496 free(entry);
1497
1498 HA_SPIN_UNLOCK(CKCH_LOCK, &ckch_lock);
1499 err = memprintf(&err, "Entry '%s' deleted in crtlist '%s'!\n", cert_path, crtlist_path);
1500 return cli_dynmsg(appctx, LOG_NOTICE, err);
1501
1502error:
1503 HA_SPIN_UNLOCK(CKCH_LOCK, &ckch_lock);
1504 err = memprintf(&err, "Can't delete the entry: %s\n", err ? err : "");
1505 return cli_dynerr(appctx, err);
1506}
1507
1508
William Lallemandee8530c2020-06-23 18:19:42 +02001509/* unlink and free all crt-list and crt-list entries */
1510void crtlist_deinit()
1511{
1512 struct eb_node *node, *next;
1513 struct crtlist *crtlist;
1514
1515 node = eb_first(&crtlists_tree);
1516 while (node) {
1517 next = eb_next(node);
1518 crtlist = ebmb_entry(node, struct crtlist, node);
1519 crtlist_free(crtlist);
1520 node = next;
1521 }
1522}
1523
William Lallemandc756bbd2020-05-13 17:23:59 +02001524
1525/* register cli keywords */
1526static struct cli_kw_list cli_kws = {{ },{
Willy Tarreaub205bfd2021-05-07 11:38:37 +02001527 { { "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 },
1528 { { "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 },
1529 { { "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 +02001530 { { NULL }, NULL, NULL, NULL } }
1531};
1532
1533INITCALL1(STG_REGISTER, cli_register_kw, &cli_kws);
1534