blob: c31714d4e22ed9d368d6ad4f0122c79f5079d82b [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 }
Remi Tricot-Le Breton6549f532023-03-14 17:22:24 +0100145
146 dst->ssl_methods_cfg.flags = src->ssl_methods_cfg.flags;
147 dst->ssl_methods_cfg.min = src->ssl_methods_cfg.min;
148 dst->ssl_methods_cfg.max = src->ssl_methods_cfg.max;
149
150 dst->ssl_methods.flags = src->ssl_methods.flags;
151 dst->ssl_methods.min = src->ssl_methods.min;
152 dst->ssl_methods.max = src->ssl_methods.max;
153
William Lallemand82f2d2f2020-09-10 19:06:43 +0200154 return dst;
155
156error:
157 ssl_sock_free_ssl_conf(dst);
158 free(dst);
159
160 return NULL;
161}
William Lallemand6e9556b2020-05-12 17:52:44 +0200162
163/* free sni filters */
164void crtlist_free_filters(char **args)
165{
166 int i;
167
168 if (!args)
169 return;
170
171 for (i = 0; args[i]; i++)
172 free(args[i]);
173
174 free(args);
175}
176
177/* Alloc and duplicate a char ** array */
178char **crtlist_dup_filters(char **args, int fcount)
179{
180 char **dst;
181 int i;
182
183 if (fcount == 0)
184 return NULL;
185
186 dst = calloc(fcount + 1, sizeof(*dst));
187 if (!dst)
188 return NULL;
189
190 for (i = 0; i < fcount; i++) {
191 dst[i] = strdup(args[i]);
192 if (!dst[i])
193 goto error;
194 }
195 return dst;
196
197error:
198 crtlist_free_filters(dst);
199 return NULL;
200}
201
202/*
203 * Detach and free a crtlist_entry.
204 * Free the filters, the ssl_conf and call ckch_inst_free() for each ckch_inst
205 */
206void crtlist_entry_free(struct crtlist_entry *entry)
207{
208 struct ckch_inst *inst, *inst_s;
209
210 if (entry == NULL)
211 return;
212
213 ebpt_delete(&entry->node);
Willy Tarreau2b718102021-04-21 07:32:39 +0200214 LIST_DELETE(&entry->by_crtlist);
215 LIST_DELETE(&entry->by_ckch_store);
William Lallemand6e9556b2020-05-12 17:52:44 +0200216 crtlist_free_filters(entry->filters);
217 ssl_sock_free_ssl_conf(entry->ssl_conf);
218 free(entry->ssl_conf);
219 list_for_each_entry_safe(inst, inst_s, &entry->ckch_inst, by_crtlist_entry) {
220 ckch_inst_free(inst);
221 }
222 free(entry);
223}
William Lallemand5622c452020-09-10 19:08:49 +0200224/*
225 * Duplicate a crt_list entry and its content (ssl_conf, filters/fcount)
226 * Return a pointer to the new entry
227 */
228struct crtlist_entry *crtlist_entry_dup(struct crtlist_entry *src)
229{
230 struct crtlist_entry *entry;
231
232 if (src == NULL)
233 return NULL;
234
235 entry = crtlist_entry_new();
236 if (entry == NULL)
237 return NULL;
238
239 if (src->filters) {
240 entry->filters = crtlist_dup_filters(src->filters, src->fcount);
241 if (!entry->filters)
242 goto error;
243 }
244 entry->fcount = src->fcount;
245 if (src->ssl_conf) {
246 entry->ssl_conf = crtlist_dup_ssl_conf(src->ssl_conf);
247 if (!entry->ssl_conf)
248 goto error;
249 }
250 entry->crtlist = src->crtlist;
251
252 return entry;
253
254error:
255
256 crtlist_free_filters(entry->filters);
257 ssl_sock_free_ssl_conf(entry->ssl_conf);
258 free(entry->ssl_conf);
259 free(entry);
260
261 return NULL;
262}
William Lallemand6e9556b2020-05-12 17:52:44 +0200263
264/*
265 * Allocate and initialize a crtlist_entry
266 */
267struct crtlist_entry *crtlist_entry_new()
268{
269 struct crtlist_entry *entry;
270
271 entry = calloc(1, sizeof(*entry));
272 if (entry == NULL)
273 return NULL;
274
275 LIST_INIT(&entry->ckch_inst);
276
Willy Tarreau2b718102021-04-21 07:32:39 +0200277 /* initialize the nodes so we can LIST_DELETE in any cases */
William Lallemand6e9556b2020-05-12 17:52:44 +0200278 LIST_INIT(&entry->by_crtlist);
279 LIST_INIT(&entry->by_ckch_store);
280
281 return entry;
282}
283
284/* Free a crtlist, from the crt_entry to the content of the ssl_conf */
285void crtlist_free(struct crtlist *crtlist)
286{
287 struct crtlist_entry *entry, *s_entry;
William Lallemand6a3168a2020-06-23 11:43:35 +0200288 struct bind_conf_list *bind_conf_node;
William Lallemand6e9556b2020-05-12 17:52:44 +0200289
290 if (crtlist == NULL)
291 return;
292
William Lallemand6a3168a2020-06-23 11:43:35 +0200293 bind_conf_node = crtlist->bind_conf;
294 while (bind_conf_node) {
295 struct bind_conf_list *next = bind_conf_node->next;
296 free(bind_conf_node);
297 bind_conf_node = next;
298 }
299
William Lallemand6e9556b2020-05-12 17:52:44 +0200300 list_for_each_entry_safe(entry, s_entry, &crtlist->ord_entries, by_crtlist) {
301 crtlist_entry_free(entry);
302 }
303 ebmb_delete(&crtlist->node);
304 free(crtlist);
305}
306
307/* Alloc and initialize a struct crtlist
308 * <filename> is the key of the ebmb_node
309 * <unique> initialize the list of entries to be unique (1) or not (0)
310 */
311struct crtlist *crtlist_new(const char *filename, int unique)
312{
313 struct crtlist *newlist;
314
315 newlist = calloc(1, sizeof(*newlist) + strlen(filename) + 1);
316 if (newlist == NULL)
317 return NULL;
318
319 memcpy(newlist->node.key, filename, strlen(filename) + 1);
320 if (unique)
321 newlist->entries = EB_ROOT_UNIQUE;
322 else
323 newlist->entries = EB_ROOT;
324
325 LIST_INIT(&newlist->ord_entries);
326
327 return newlist;
328}
329
330/*
331 * Read a single crt-list line. /!\ alter the <line> string.
332 * Fill <crt_path> and <crtlist_entry>
333 * <crtlist_entry> must be alloc and free by the caller
334 * <crtlist_entry->ssl_conf> is alloc by the function
335 * <crtlist_entry->filters> is alloc by the function
336 * <crt_path> is a ptr in <line>
337 * Return an error code
338 */
Remi Tricot-Le Bretonfb00f312021-03-23 16:41:53 +0100339int 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 +0200340{
341 int cfgerr = 0;
342 int arg, newarg, cur_arg, i, ssl_b = 0, ssl_e = 0;
343 char *end;
344 char *args[MAX_CRT_ARGS + 1];
345 struct ssl_bind_conf *ssl_conf = NULL;
346
347 if (!line || !crt_path || !entry)
348 return ERR_ALERT | ERR_FATAL;
349
350 end = line + strlen(line);
351 if (end-line >= CRT_LINESIZE-1 && *(end-1) != '\n') {
352 /* Check if we reached the limit and the last char is not \n.
353 * Watch out for the last line without the terminating '\n'!
354 */
Tim Duesterhus6d07fae2020-09-29 18:00:27 +0200355 memprintf(err, "parsing [%s:%d]: line too long, limit is %d characters",
356 file, linenum, CRT_LINESIZE-1);
William Lallemand6e9556b2020-05-12 17:52:44 +0200357 cfgerr |= ERR_ALERT | ERR_FATAL;
358 goto error;
359 }
360 arg = 0;
361 newarg = 1;
362 while (*line) {
363 if (isspace((unsigned char)*line)) {
364 newarg = 1;
365 *line = 0;
366 } else if (*line == '[') {
367 if (ssl_b) {
Tim Duesterhus6d07fae2020-09-29 18:00:27 +0200368 memprintf(err, "parsing [%s:%d]: too many '['", file, linenum);
William Lallemand6e9556b2020-05-12 17:52:44 +0200369 cfgerr |= ERR_ALERT | ERR_FATAL;
370 goto error;
371 }
372 if (!arg) {
Tim Duesterhus6d07fae2020-09-29 18:00:27 +0200373 memprintf(err, "parsing [%s:%d]: file must start with a cert", file, linenum);
William Lallemand6e9556b2020-05-12 17:52:44 +0200374 cfgerr |= ERR_ALERT | ERR_FATAL;
375 goto error;
376 }
377 ssl_b = arg;
378 newarg = 1;
379 *line = 0;
380 } else if (*line == ']') {
381 if (ssl_e) {
Tim Duesterhus6d07fae2020-09-29 18:00:27 +0200382 memprintf(err, "parsing [%s:%d]: too many ']'", file, linenum);
William Lallemand6e9556b2020-05-12 17:52:44 +0200383 cfgerr |= ERR_ALERT | ERR_FATAL;
384 goto error;
385 }
386 if (!ssl_b) {
Tim Duesterhus6d07fae2020-09-29 18:00:27 +0200387 memprintf(err, "parsing [%s:%d]: missing '['", file, linenum);
William Lallemand6e9556b2020-05-12 17:52:44 +0200388 cfgerr |= ERR_ALERT | ERR_FATAL;
389 goto error;
390 }
391 ssl_e = arg;
392 newarg = 1;
393 *line = 0;
394 } else if (newarg) {
395 if (arg == MAX_CRT_ARGS) {
Tim Duesterhus6d07fae2020-09-29 18:00:27 +0200396 memprintf(err, "parsing [%s:%d]: too many args ", file, linenum);
William Lallemand6e9556b2020-05-12 17:52:44 +0200397 cfgerr |= ERR_ALERT | ERR_FATAL;
398 goto error;
399 }
400 newarg = 0;
401 args[arg++] = line;
402 }
403 line++;
404 }
405 args[arg++] = line;
406
407 /* empty line */
408 if (!*args[0]) {
409 cfgerr |= ERR_NONE;
410 goto error;
411 }
412
413 *crt_path = args[0];
414
415 if (ssl_b) {
William Lallemandd85227f2023-02-07 17:06:35 +0100416 if (ssl_b > 1) {
417 memprintf(err, "parsing [%s:%d]: malformated line, filters can't be between filename and options!", file, linenum);
418 cfgerr |= ERR_WARN;
419 }
420
William Lallemand6e9556b2020-05-12 17:52:44 +0200421 ssl_conf = calloc(1, sizeof *ssl_conf);
422 if (!ssl_conf) {
423 memprintf(err, "not enough memory!");
424 cfgerr |= ERR_ALERT | ERR_FATAL;
425 goto error;
426 }
427 }
Remi Tricot-Le Bretonfb00f312021-03-23 16:41:53 +0100428
William Lallemand6e9556b2020-05-12 17:52:44 +0200429 cur_arg = ssl_b ? ssl_b : 1;
430 while (cur_arg < ssl_e) {
431 newarg = 0;
William Lallemandaf678062023-02-13 10:58:13 +0100432 for (i = 0; ssl_crtlist_kws[i].kw != NULL; i++) {
433 if (strcmp(ssl_crtlist_kws[i].kw, args[cur_arg]) == 0) {
William Lallemand6e9556b2020-05-12 17:52:44 +0200434 newarg = 1;
William Lallemandaf678062023-02-13 10:58:13 +0100435 cfgerr |= ssl_crtlist_kws[i].parse(args, cur_arg, NULL, ssl_conf, from_cli, err);
436 if (cur_arg + 1 + ssl_crtlist_kws[i].skip > ssl_e) {
Tim Duesterhus6d07fae2020-09-29 18:00:27 +0200437 memprintf(err, "parsing [%s:%d]: ssl args out of '[]' for %s",
438 file, linenum, args[cur_arg]);
William Lallemand6e9556b2020-05-12 17:52:44 +0200439 cfgerr |= ERR_ALERT | ERR_FATAL;
440 goto error;
441 }
William Lallemandaf678062023-02-13 10:58:13 +0100442 cur_arg += 1 + ssl_crtlist_kws[i].skip;
William Lallemand6e9556b2020-05-12 17:52:44 +0200443 break;
444 }
445 }
446 if (!cfgerr && !newarg) {
Tim Duesterhus6d07fae2020-09-29 18:00:27 +0200447 memprintf(err, "parsing [%s:%d]: unknown ssl keyword %s",
448 file, linenum, args[cur_arg]);
William Lallemand6e9556b2020-05-12 17:52:44 +0200449 cfgerr |= ERR_ALERT | ERR_FATAL;
450 goto error;
451 }
452 }
453 entry->linenum = linenum;
454 entry->ssl_conf = ssl_conf;
455 entry->filters = crtlist_dup_filters(&args[cur_arg], arg - cur_arg - 1);
456 entry->fcount = arg - cur_arg - 1;
457
458 return cfgerr;
459
460error:
461 crtlist_free_filters(entry->filters);
462 entry->filters = NULL;
463 ssl_sock_free_ssl_conf(entry->ssl_conf);
Willy Tarreau61cfdf42021-02-20 10:46:51 +0100464 ha_free(&entry->ssl_conf);
William Lallemand6e9556b2020-05-12 17:52:44 +0200465 return cfgerr;
466}
467
468
469
470/* This function parse a crt-list file and store it in a struct crtlist, each line is a crtlist_entry structure
471 * Fill the <crtlist> argument with a pointer to a new crtlist struct
472 *
473 * This function tries to open and store certificate files.
474 */
475int crtlist_parse_file(char *file, struct bind_conf *bind_conf, struct proxy *curproxy, struct crtlist **crtlist, char **err)
476{
477 struct crtlist *newlist;
478 struct crtlist_entry *entry = NULL;
479 char thisline[CRT_LINESIZE];
William Lallemand6e9556b2020-05-12 17:52:44 +0200480 FILE *f;
481 struct stat buf;
482 int linenum = 0;
483 int cfgerr = 0;
Tim Duesterhusb9f6acc2020-09-29 18:00:28 +0200484 int missing_lf = -1;
William Lallemand6e9556b2020-05-12 17:52:44 +0200485
486 if ((f = fopen(file, "r")) == NULL) {
487 memprintf(err, "cannot open file '%s' : %s", file, strerror(errno));
488 return ERR_ALERT | ERR_FATAL;
489 }
490
491 newlist = crtlist_new(file, 0);
492 if (newlist == NULL) {
493 memprintf(err, "Not enough memory!");
494 cfgerr |= ERR_ALERT | ERR_FATAL;
495 goto error;
496 }
497
498 while (fgets(thisline, sizeof(thisline), f) != NULL) {
499 char *end;
500 char *line = thisline;
501 char *crt_path;
William Lallemand86c2dd62020-11-20 14:23:38 +0100502 char path[MAXPATHLEN+1];
William Lallemand6e9556b2020-05-12 17:52:44 +0200503 struct ckch_store *ckchs;
William Lallemand77e1c6f2020-11-20 18:26:09 +0100504 int found = 0;
William Lallemand6e9556b2020-05-12 17:52:44 +0200505
Tim Duesterhusb9f6acc2020-09-29 18:00:28 +0200506 if (missing_lf != -1) {
507 memprintf(err, "parsing [%s:%d]: Stray NUL character at position %d.\n",
508 file, linenum, (missing_lf + 1));
509 cfgerr |= ERR_ALERT | ERR_FATAL;
510 missing_lf = -1;
511 break;
512 }
513
William Lallemand6e9556b2020-05-12 17:52:44 +0200514 linenum++;
515 end = line + strlen(line);
516 if (end-line == sizeof(thisline)-1 && *(end-1) != '\n') {
517 /* Check if we reached the limit and the last char is not \n.
518 * Watch out for the last line without the terminating '\n'!
519 */
Tim Duesterhus6d07fae2020-09-29 18:00:27 +0200520 memprintf(err, "parsing [%s:%d]: line too long, limit is %d characters",
521 file, linenum, (int)sizeof(thisline)-1);
William Lallemand6e9556b2020-05-12 17:52:44 +0200522 cfgerr |= ERR_ALERT | ERR_FATAL;
523 break;
524 }
525
526 if (*line == '#' || *line == '\n' || *line == '\r')
527 continue;
528
Tim Duesterhusb9f6acc2020-09-29 18:00:28 +0200529 if (end > line && *(end-1) == '\n') {
530 /* kill trailing LF */
531 *(end - 1) = 0;
532 }
533 else {
534 /* mark this line as truncated */
535 missing_lf = end - line;
536 }
537
William Lallemand6e9556b2020-05-12 17:52:44 +0200538 entry = crtlist_entry_new();
539 if (entry == NULL) {
540 memprintf(err, "Not enough memory!");
541 cfgerr |= ERR_ALERT | ERR_FATAL;
542 goto error;
543 }
Tim Duesterhusb9f6acc2020-09-29 18:00:28 +0200544
Remi Tricot-Le Bretonfb00f312021-03-23 16:41:53 +0100545 cfgerr |= crtlist_parse_line(thisline, &crt_path, entry, file, linenum, 0, err);
William Lallemand20b0fed2020-09-28 15:45:16 +0200546 if (cfgerr & ERR_CODE)
William Lallemand6e9556b2020-05-12 17:52:44 +0200547 goto error;
548
549 /* empty line */
550 if (!crt_path || !*crt_path) {
551 crtlist_entry_free(entry);
552 entry = NULL;
553 continue;
554 }
555
556 if (*crt_path != '/' && global_ssl.crt_base) {
Willy Tarreau393e42a2022-05-09 10:31:28 +0200557 if ((strlen(global_ssl.crt_base) + 1 + strlen(crt_path)) > sizeof(path) ||
Willy Tarreau63fc9002022-05-09 21:14:04 +0200558 snprintf(path, sizeof(path), "%s/%s", global_ssl.crt_base, crt_path) > sizeof(path)) {
Tim Duesterhus6d07fae2020-09-29 18:00:27 +0200559 memprintf(err, "parsing [%s:%d]: '%s' : path too long",
560 file, linenum, crt_path);
William Lallemand6e9556b2020-05-12 17:52:44 +0200561 cfgerr |= ERR_ALERT | ERR_FATAL;
562 goto error;
563 }
William Lallemand6e9556b2020-05-12 17:52:44 +0200564 crt_path = path;
565 }
566
567 /* Look for a ckch_store or create one */
568 ckchs = ckchs_lookup(crt_path);
569 if (ckchs == NULL) {
William Lallemand47da8212020-09-10 19:13:27 +0200570 if (stat(crt_path, &buf) == 0) {
William Lallemand77e1c6f2020-11-20 18:26:09 +0100571 found++;
William Lallemand47da8212020-09-10 19:13:27 +0200572
William Lallemandbd8e6ed2020-09-16 16:08:08 +0200573 ckchs = ckchs_load_cert_file(crt_path, err);
William Lallemand47da8212020-09-10 19:13:27 +0200574 if (ckchs == NULL) {
575 cfgerr |= ERR_ALERT | ERR_FATAL;
576 goto error;
577 }
578
579 entry->node.key = ckchs;
580 entry->crtlist = newlist;
Remi Tricot-Le Bretonfb2b9982022-12-20 11:11:11 +0100581 if (entry->ssl_conf)
582 ckchs->data->ocsp_update_mode = entry->ssl_conf->ocsp_update;
William Lallemand47da8212020-09-10 19:13:27 +0200583 ebpt_insert(&newlist->entries, &entry->node);
Willy Tarreau2b718102021-04-21 07:32:39 +0200584 LIST_APPEND(&newlist->ord_entries, &entry->by_crtlist);
585 LIST_APPEND(&ckchs->crtlist_entry, &entry->by_ckch_store);
William Lallemand6e9556b2020-05-12 17:52:44 +0200586
William Lallemand73404572020-11-20 18:23:40 +0100587 } else if (global_ssl.extra_files & SSL_GF_BUNDLE) {
William Lallemand47da8212020-09-10 19:13:27 +0200588 /* If we didn't find the file, this could be a
William Lallemand51f784b2020-10-02 18:08:18 +0200589 bundle, since 2.3 we don't support multiple
590 certificate in the same OpenSSL store, so we
591 emulate it by loading each file separately. To
592 do so we need to duplicate the entry in the
593 crt-list because it becomes independent */
William Lallemand47da8212020-09-10 19:13:27 +0200594 char fp[MAXPATHLEN+1] = {0};
595 int n = 0;
596 struct crtlist_entry *entry_dup = entry; /* use the previous created entry */
597 for (n = 0; n < SSL_SOCK_NUM_KEYTYPES; n++) {
598 struct stat buf;
599 int ret;
600
William Lallemand86c2dd62020-11-20 14:23:38 +0100601 ret = snprintf(fp, sizeof(fp), "%s.%s", crt_path, SSL_SOCK_KEYTYPE_NAMES[n]);
William Lallemand47da8212020-09-10 19:13:27 +0200602 if (ret > sizeof(fp))
603 continue;
604
605 ckchs = ckchs_lookup(fp);
William Lallemand77e1c6f2020-11-20 18:26:09 +0100606 if (!ckchs) {
607 if (stat(fp, &buf) == 0) {
608 ckchs = ckchs_load_cert_file(fp, err);
609 if (!ckchs) {
William Lallemand47da8212020-09-10 19:13:27 +0200610 cfgerr |= ERR_ALERT | ERR_FATAL;
611 goto error;
612 }
William Lallemand77e1c6f2020-11-20 18:26:09 +0100613 } else {
614 continue; /* didn't find this extension, skip */
615 }
616 }
617 found++;
618 linenum++; /* we duplicate the line for this entry in the bundle */
619 if (!entry_dup) { /* if the entry was used, duplicate one */
620 linenum++;
621 entry_dup = crtlist_entry_dup(entry);
622 if (!entry_dup) {
623 cfgerr |= ERR_ALERT | ERR_FATAL;
624 goto error;
William Lallemand47da8212020-09-10 19:13:27 +0200625 }
William Lallemand77e1c6f2020-11-20 18:26:09 +0100626 entry_dup->linenum = linenum;
627 }
William Lallemand47da8212020-09-10 19:13:27 +0200628
William Lallemand77e1c6f2020-11-20 18:26:09 +0100629 entry_dup->node.key = ckchs;
630 entry_dup->crtlist = newlist;
William Lallemanda14686d2023-02-07 18:38:05 +0100631
632 cfgerr |= ocsp_update_check_cfg_consistency(ckchs, entry, crt_path, err);
633 if (cfgerr & ERR_FATAL)
634 goto error;
635
Remi Tricot-Le Bretonfc92b8b2023-01-09 12:02:46 +0100636 if (entry->ssl_conf)
637 ckchs->data->ocsp_update_mode = entry->ssl_conf->ocsp_update;
William Lallemand77e1c6f2020-11-20 18:26:09 +0100638 ebpt_insert(&newlist->entries, &entry_dup->node);
Willy Tarreau2b718102021-04-21 07:32:39 +0200639 LIST_APPEND(&newlist->ord_entries, &entry_dup->by_crtlist);
640 LIST_APPEND(&ckchs->crtlist_entry, &entry_dup->by_ckch_store);
William Lallemand47da8212020-09-10 19:13:27 +0200641
William Lallemand77e1c6f2020-11-20 18:26:09 +0100642 entry_dup = NULL; /* the entry was used, we need a new one next round */
William Lallemand47da8212020-09-10 19:13:27 +0200643 }
William Lallemandb7fdfdf2020-12-04 15:45:02 +0100644#if HA_OPENSSL_VERSION_NUMBER < 0x10101000L
645 if (found) {
646 memprintf(err, "%sCan't load '%s'. Loading a multi certificates bundle requires OpenSSL >= 1.1.1\n",
647 err && *err ? *err : "", crt_path);
648 cfgerr |= ERR_ALERT | ERR_FATAL;
649 }
650#endif
William Lallemand47da8212020-09-10 19:13:27 +0200651 }
William Lallemand77e1c6f2020-11-20 18:26:09 +0100652 if (!found) {
653 memprintf(err, "%sunable to stat SSL certificate from file '%s' : %s.\n",
654 err && *err ? *err : "", crt_path, strerror(errno));
655 cfgerr |= ERR_ALERT | ERR_FATAL;
656 }
657
William Lallemand50c03aa2020-11-06 16:24:07 +0100658 } else {
659 entry->node.key = ckchs;
660 entry->crtlist = newlist;
William Lallemanda14686d2023-02-07 18:38:05 +0100661
662 cfgerr |= ocsp_update_check_cfg_consistency(ckchs, entry, crt_path, err);
663 if (cfgerr & ERR_FATAL)
664 goto error;
665
Remi Tricot-Le Bretonfc92b8b2023-01-09 12:02:46 +0100666 if (entry->ssl_conf)
667 ckchs->data->ocsp_update_mode = entry->ssl_conf->ocsp_update;
William Lallemand50c03aa2020-11-06 16:24:07 +0100668 ebpt_insert(&newlist->entries, &entry->node);
Willy Tarreau2b718102021-04-21 07:32:39 +0200669 LIST_APPEND(&newlist->ord_entries, &entry->by_crtlist);
670 LIST_APPEND(&ckchs->crtlist_entry, &entry->by_ckch_store);
William Lallemand77e1c6f2020-11-20 18:26:09 +0100671 found++;
William Lallemand47da8212020-09-10 19:13:27 +0200672 }
William Lallemand6e9556b2020-05-12 17:52:44 +0200673 entry = NULL;
674 }
Tim Duesterhusb9f6acc2020-09-29 18:00:28 +0200675
676 if (missing_lf != -1) {
677 memprintf(err, "parsing [%s:%d]: Missing LF on last line, file might have been truncated at position %d.\n",
678 file, linenum, (missing_lf + 1));
679 cfgerr |= ERR_ALERT | ERR_FATAL;
680 }
681
William Lallemand6e9556b2020-05-12 17:52:44 +0200682 if (cfgerr & ERR_CODE)
683 goto error;
684
685 newlist->linecount = linenum;
686
687 fclose(f);
688 *crtlist = newlist;
689
690 return cfgerr;
691error:
692 crtlist_entry_free(entry);
693
694 fclose(f);
695 crtlist_free(newlist);
696 return cfgerr;
697}
698
699/* This function reads a directory and stores it in a struct crtlist, each file is a crtlist_entry structure
700 * Fill the <crtlist> argument with a pointer to a new crtlist struct
701 *
702 * This function tries to open and store certificate files.
703 */
704int crtlist_load_cert_dir(char *path, struct bind_conf *bind_conf, struct crtlist **crtlist, char **err)
705{
706 struct crtlist *dir;
707 struct dirent **de_list;
708 int i, n;
709 struct stat buf;
710 char *end;
711 char fp[MAXPATHLEN+1];
712 int cfgerr = 0;
713 struct ckch_store *ckchs;
William Lallemand6e9556b2020-05-12 17:52:44 +0200714
715 dir = crtlist_new(path, 1);
716 if (dir == NULL) {
717 memprintf(err, "not enough memory");
718 return ERR_ALERT | ERR_FATAL;
719 }
720
721 n = scandir(path, &de_list, 0, alphasort);
722 if (n < 0) {
723 memprintf(err, "%sunable to scan directory '%s' : %s.\n",
724 err && *err ? *err : "", path, strerror(errno));
725 cfgerr |= ERR_ALERT | ERR_FATAL;
726 }
727 else {
728 for (i = 0; i < n; i++) {
729 struct crtlist_entry *entry;
730 struct dirent *de = de_list[i];
731
732 end = strrchr(de->d_name, '.');
William Lallemand589570d2022-05-09 10:30:51 +0200733 if (end && (de->d_name[0] == '.' ||
734 strcmp(end, ".issuer") == 0 || strcmp(end, ".ocsp") == 0 ||
735 strcmp(end, ".sctl") == 0 || strcmp(end, ".key") == 0))
William Lallemand6e9556b2020-05-12 17:52:44 +0200736 goto ignore_entry;
737
738 snprintf(fp, sizeof(fp), "%s/%s", path, de->d_name);
739 if (stat(fp, &buf) != 0) {
740 memprintf(err, "%sunable to stat SSL certificate from file '%s' : %s.\n",
741 err && *err ? *err : "", fp, strerror(errno));
742 cfgerr |= ERR_ALERT | ERR_FATAL;
743 goto ignore_entry;
744 }
745 if (!S_ISREG(buf.st_mode))
746 goto ignore_entry;
747
748 entry = crtlist_entry_new();
749 if (entry == NULL) {
750 memprintf(err, "not enough memory '%s'", fp);
751 cfgerr |= ERR_ALERT | ERR_FATAL;
752 goto ignore_entry;
753 }
754
William Lallemand6e9556b2020-05-12 17:52:44 +0200755 ckchs = ckchs_lookup(fp);
756 if (ckchs == NULL)
William Lallemandbd8e6ed2020-09-16 16:08:08 +0200757 ckchs = ckchs_load_cert_file(fp, err);
William Lallemand6e9556b2020-05-12 17:52:44 +0200758 if (ckchs == NULL) {
759 free(de);
760 free(entry);
761 cfgerr |= ERR_ALERT | ERR_FATAL;
762 goto end;
763 }
764 entry->node.key = ckchs;
765 entry->crtlist = dir;
Willy Tarreau2b718102021-04-21 07:32:39 +0200766 LIST_APPEND(&ckchs->crtlist_entry, &entry->by_ckch_store);
767 LIST_APPEND(&dir->ord_entries, &entry->by_crtlist);
William Lallemand6e9556b2020-05-12 17:52:44 +0200768 ebpt_insert(&dir->entries, &entry->node);
769
770ignore_entry:
771 free(de);
772 }
773end:
774 free(de_list);
775 }
776
777 if (cfgerr & ERR_CODE) {
778 /* free the dir and entries on error */
779 crtlist_free(dir);
780 } else {
781 *crtlist = dir;
782 }
783 return cfgerr;
784
785}
786
William Lallemandc756bbd2020-05-13 17:23:59 +0200787/*
788 * Take an ssl_bind_conf structure and append the configuration line used to
789 * create it in the buffer
790 */
791static void dump_crtlist_sslconf(struct buffer *buf, const struct ssl_bind_conf *conf)
792{
793 int space = 0;
794
795 if (conf == NULL)
796 return;
797
798 chunk_appendf(buf, " [");
799#ifdef OPENSSL_NPN_NEGOTIATED
800 if (conf->npn_str) {
801 int len = conf->npn_len;
802 char *ptr = conf->npn_str;
803 int comma = 0;
804
805 if (space) chunk_appendf(buf, " ");
806 chunk_appendf(buf, "npn ");
807 while (len) {
808 unsigned short size;
809
810 size = *ptr;
811 ptr++;
812 if (comma)
813 chunk_memcat(buf, ",", 1);
814 chunk_memcat(buf, ptr, size);
815 ptr += size;
816 len -= size + 1;
817 comma = 1;
818 }
819 chunk_memcat(buf, "", 1); /* finish with a \0 */
820 space++;
821 }
822#endif
823#ifdef TLSEXT_TYPE_application_layer_protocol_negotiation
824 if (conf->alpn_str) {
825 int len = conf->alpn_len;
826 char *ptr = conf->alpn_str;
827 int comma = 0;
828
829 if (space) chunk_appendf(buf, " ");
830 chunk_appendf(buf, "alpn ");
831 while (len) {
832 unsigned short size;
833
834 size = *ptr;
835 ptr++;
836 if (comma)
837 chunk_memcat(buf, ",", 1);
838 chunk_memcat(buf, ptr, size);
839 ptr += size;
840 len -= size + 1;
841 comma = 1;
842 }
843 chunk_memcat(buf, "", 1); /* finish with a \0 */
844 space++;
845 }
846#endif
847 /* verify */
848 {
849 if (conf->verify == SSL_SOCK_VERIFY_NONE) {
850 if (space) chunk_appendf(buf, " ");
851 chunk_appendf(buf, "verify none");
852 space++;
853 } else if (conf->verify == SSL_SOCK_VERIFY_OPTIONAL) {
854 if (space) chunk_appendf(buf, " ");
855 chunk_appendf(buf, "verify optional");
856 space++;
857 } else if (conf->verify == SSL_SOCK_VERIFY_REQUIRED) {
858 if (space) chunk_appendf(buf, " ");
859 chunk_appendf(buf, "verify required");
860 space++;
861 }
862 }
863
864 if (conf->no_ca_names) {
865 if (space) chunk_appendf(buf, " ");
866 chunk_appendf(buf, "no-ca-names");
867 space++;
868 }
869
870 if (conf->early_data) {
871 if (space) chunk_appendf(buf, " ");
872 chunk_appendf(buf, "allow-0rtt");
873 space++;
874 }
875 if (conf->ca_file) {
876 if (space) chunk_appendf(buf, " ");
877 chunk_appendf(buf, "ca-file %s", conf->ca_file);
878 space++;
879 }
880 if (conf->crl_file) {
881 if (space) chunk_appendf(buf, " ");
882 chunk_appendf(buf, "crl-file %s", conf->crl_file);
883 space++;
884 }
885 if (conf->ciphers) {
886 if (space) chunk_appendf(buf, " ");
887 chunk_appendf(buf, "ciphers %s", conf->ciphers);
888 space++;
889 }
Ilya Shipitsinf34ed0b2020-11-21 14:37:34 +0500890#ifdef HAVE_SSL_CTX_SET_CIPHERSUITES
William Lallemandc756bbd2020-05-13 17:23:59 +0200891 if (conf->ciphersuites) {
892 if (space) chunk_appendf(buf, " ");
893 chunk_appendf(buf, "ciphersuites %s", conf->ciphersuites);
894 space++;
895 }
896#endif
897 if (conf->curves) {
898 if (space) chunk_appendf(buf, " ");
899 chunk_appendf(buf, "curves %s", conf->curves);
900 space++;
901 }
902 if (conf->ecdhe) {
903 if (space) chunk_appendf(buf, " ");
904 chunk_appendf(buf, "ecdhe %s", conf->ecdhe);
905 space++;
906 }
907
908 /* the crt-lists only support ssl-min-ver and ssl-max-ver */
William Lallemand8177ad92020-05-20 16:49:02 +0200909 if (conf->ssl_methods_cfg.min) {
William Lallemandc756bbd2020-05-13 17:23:59 +0200910 if (space) chunk_appendf(buf, " ");
William Lallemand8177ad92020-05-20 16:49:02 +0200911 chunk_appendf(buf, "ssl-min-ver %s", methodVersions[conf->ssl_methods_cfg.min].name);
William Lallemandc756bbd2020-05-13 17:23:59 +0200912 space++;
913 }
914
William Lallemand8177ad92020-05-20 16:49:02 +0200915 if (conf->ssl_methods_cfg.max) {
William Lallemandc756bbd2020-05-13 17:23:59 +0200916 if (space) chunk_appendf(buf, " ");
William Lallemand8177ad92020-05-20 16:49:02 +0200917 chunk_appendf(buf, "ssl-max-ver %s", methodVersions[conf->ssl_methods_cfg.max].name);
William Lallemandc756bbd2020-05-13 17:23:59 +0200918 space++;
919 }
920
Remi Tricot-Le Bretonca0c84a2023-03-02 15:49:52 +0100921 if (conf->ocsp_update != SSL_SOCK_OCSP_UPDATE_DFLT) {
922 if (space) chunk_appendf(buf, " ");
923 chunk_appendf(buf, "ocsp-update %s",
924 conf->ocsp_update == SSL_SOCK_OCSP_UPDATE_OFF ? "off" : "on");
925 space++;
926 }
927
William Lallemandc756bbd2020-05-13 17:23:59 +0200928 chunk_appendf(buf, "]");
929
930 return;
931}
932
933/* dump a list of filters */
934static void dump_crtlist_filters(struct buffer *buf, struct crtlist_entry *entry)
935{
936 int i;
937
938 if (!entry->fcount)
939 return;
940
941 for (i = 0; i < entry->fcount; i++) {
942 chunk_appendf(buf, " %s", entry->filters[i]);
943 }
944 return;
945}
946
947/************************** CLI functions ****************************/
948
949
Willy Tarreaua2fcca02022-05-05 11:53:23 +0200950/* CLI IO handler for '(show|dump) ssl crt-list'.
951 * It uses show_crtlist_ctx for the context.
952 */
William Lallemandc756bbd2020-05-13 17:23:59 +0200953static int cli_io_handler_dump_crtlist(struct appctx *appctx)
954{
Willy Tarreaua2fcca02022-05-05 11:53:23 +0200955 struct show_crtlist_ctx *ctx = appctx->svcctx;
William Lallemandc756bbd2020-05-13 17:23:59 +0200956 struct buffer *trash = alloc_trash_chunk();
William Lallemandc756bbd2020-05-13 17:23:59 +0200957 struct ebmb_node *lnode;
958
959 if (trash == NULL)
960 return 1;
961
962 /* dump the list of crt-lists */
Willy Tarreaua2fcca02022-05-05 11:53:23 +0200963 lnode = ctx->crtlist_node;
William Lallemandc756bbd2020-05-13 17:23:59 +0200964 if (lnode == NULL)
965 lnode = ebmb_first(&crtlists_tree);
966 while (lnode) {
967 chunk_appendf(trash, "%s\n", lnode->key);
Willy Tarreaud0a06d52022-05-18 15:07:19 +0200968 if (applet_putchk(appctx, trash) == -1)
William Lallemandc756bbd2020-05-13 17:23:59 +0200969 goto yield;
William Lallemandc756bbd2020-05-13 17:23:59 +0200970 lnode = ebmb_next(lnode);
971 }
972 free_trash_chunk(trash);
973 return 1;
974yield:
Willy Tarreaua2fcca02022-05-05 11:53:23 +0200975 ctx->crtlist_node = lnode;
William Lallemandc756bbd2020-05-13 17:23:59 +0200976 free_trash_chunk(trash);
977 return 0;
978}
979
980/* CLI IO handler for '(show|dump) ssl crt-list <filename>' */
981static int cli_io_handler_dump_crtlist_entries(struct appctx *appctx)
982{
Willy Tarreaua2fcca02022-05-05 11:53:23 +0200983 struct show_crtlist_ctx *ctx = appctx->svcctx;
William Lallemandc756bbd2020-05-13 17:23:59 +0200984 struct buffer *trash = alloc_trash_chunk();
985 struct crtlist *crtlist;
William Lallemandc756bbd2020-05-13 17:23:59 +0200986 struct crtlist_entry *entry;
987
988 if (trash == NULL)
989 return 1;
990
Willy Tarreaua2fcca02022-05-05 11:53:23 +0200991 crtlist = ebmb_entry(ctx->crtlist_node, struct crtlist, node);
William Lallemandc756bbd2020-05-13 17:23:59 +0200992
Willy Tarreaua2fcca02022-05-05 11:53:23 +0200993 entry = ctx->entry;
William Lallemandc756bbd2020-05-13 17:23:59 +0200994 if (entry == NULL) {
995 entry = LIST_ELEM((crtlist->ord_entries).n, typeof(entry), by_crtlist);
996 chunk_appendf(trash, "# %s\n", crtlist->node.key);
Willy Tarreaud0a06d52022-05-18 15:07:19 +0200997 if (applet_putchk(appctx, trash) == -1)
William Lallemandc756bbd2020-05-13 17:23:59 +0200998 goto yield;
William Lallemandc756bbd2020-05-13 17:23:59 +0200999 }
1000
1001 list_for_each_entry_from(entry, &crtlist->ord_entries, by_crtlist) {
1002 struct ckch_store *store;
1003 const char *filename;
1004
1005 store = entry->node.key;
1006 filename = store->path;
1007 chunk_appendf(trash, "%s", filename);
Willy Tarreaua2fcca02022-05-05 11:53:23 +02001008 if (ctx->mode == 's') /* show */
William Lallemandc756bbd2020-05-13 17:23:59 +02001009 chunk_appendf(trash, ":%d", entry->linenum);
1010 dump_crtlist_sslconf(trash, entry->ssl_conf);
1011 dump_crtlist_filters(trash, entry);
1012 chunk_appendf(trash, "\n");
1013
Willy Tarreaud0a06d52022-05-18 15:07:19 +02001014 if (applet_putchk(appctx, trash) == -1)
William Lallemandc756bbd2020-05-13 17:23:59 +02001015 goto yield;
William Lallemandc756bbd2020-05-13 17:23:59 +02001016 }
1017 free_trash_chunk(trash);
1018 return 1;
1019yield:
Willy Tarreaua2fcca02022-05-05 11:53:23 +02001020 ctx->entry = entry;
William Lallemandc756bbd2020-05-13 17:23:59 +02001021 free_trash_chunk(trash);
1022 return 0;
1023}
1024
1025/* CLI argument parser for '(show|dump) ssl crt-list' */
1026static int cli_parse_dump_crtlist(char **args, char *payload, struct appctx *appctx, void *private)
1027{
Willy Tarreaua2fcca02022-05-05 11:53:23 +02001028 struct show_crtlist_ctx *ctx = applet_reserve_svcctx(appctx, sizeof(*ctx));
William Lallemandc756bbd2020-05-13 17:23:59 +02001029 struct ebmb_node *lnode;
1030 char *filename = NULL;
1031 int mode;
William Lallemand99cc2182020-06-25 15:19:51 +02001032 char *end;
William Lallemandc756bbd2020-05-13 17:23:59 +02001033
1034 if (!cli_has_level(appctx, ACCESS_LVL_ADMIN))
1035 return 1;
1036
Tim Duesterhuse5ff1412021-01-02 22:31:53 +01001037 if (*args[3] && strcmp(args[3], "-n") == 0) {
William Lallemandc756bbd2020-05-13 17:23:59 +02001038 mode = 's';
1039 filename = args[4];
1040 } else {
1041 mode = 'd';
1042 filename = args[3];
1043 }
1044
1045 if (mode == 's' && !*args[4])
1046 return cli_err(appctx, "'show ssl crt-list -n' expects a filename or a directory\n");
1047
1048 if (filename && *filename) {
William Lallemand99cc2182020-06-25 15:19:51 +02001049
1050
1051 /* strip trailing slashes, including first one */
1052 for (end = filename + strlen(filename) - 1; end >= filename && *end == '/'; end--)
1053 *end = 0;
1054
William Lallemandc756bbd2020-05-13 17:23:59 +02001055 lnode = ebst_lookup(&crtlists_tree, filename);
1056 if (lnode == NULL)
1057 return cli_err(appctx, "didn't find the specified filename\n");
1058
Willy Tarreaua2fcca02022-05-05 11:53:23 +02001059 ctx->crtlist_node = lnode;
William Lallemandc756bbd2020-05-13 17:23:59 +02001060 appctx->io_handler = cli_io_handler_dump_crtlist_entries;
1061 }
Willy Tarreaua2fcca02022-05-05 11:53:23 +02001062 ctx->mode = mode;
William Lallemandc756bbd2020-05-13 17:23:59 +02001063
1064 return 0;
1065}
1066
1067/* release function of the "add ssl crt-list' command, free things and unlock
Willy Tarreau6b6c3632022-05-05 13:43:49 +02001068 * the spinlock. It uses the add_crtlist_ctx.
1069 */
William Lallemandc756bbd2020-05-13 17:23:59 +02001070static void cli_release_add_crtlist(struct appctx *appctx)
1071{
Willy Tarreau6b6c3632022-05-05 13:43:49 +02001072 struct add_crtlist_ctx *ctx = appctx->svcctx;
1073 struct crtlist_entry *entry = ctx->entry;
William Lallemandc756bbd2020-05-13 17:23:59 +02001074
Christopher Fauletc642d7c2022-06-01 16:31:09 +02001075 if (entry) {
William Lallemandc756bbd2020-05-13 17:23:59 +02001076 struct ckch_inst *inst, *inst_s;
Christopher Fauletc642d7c2022-06-01 16:31:09 +02001077
William Lallemandc756bbd2020-05-13 17:23:59 +02001078 /* upon error free the ckch_inst and everything inside */
1079 ebpt_delete(&entry->node);
Willy Tarreau2b718102021-04-21 07:32:39 +02001080 LIST_DELETE(&entry->by_crtlist);
1081 LIST_DELETE(&entry->by_ckch_store);
William Lallemandc756bbd2020-05-13 17:23:59 +02001082
1083 list_for_each_entry_safe(inst, inst_s, &entry->ckch_inst, by_ckchs) {
1084 ckch_inst_free(inst);
1085 }
1086 crtlist_free_filters(entry->filters);
1087 ssl_sock_free_ssl_conf(entry->ssl_conf);
1088 free(entry->ssl_conf);
1089 free(entry);
1090 }
William Lallemandc756bbd2020-05-13 17:23:59 +02001091 HA_SPIN_UNLOCK(CKCH_LOCK, &ckch_lock);
Christopher Fauletc642d7c2022-06-01 16:31:09 +02001092 ha_free(&ctx->err);
William Lallemandc756bbd2020-05-13 17:23:59 +02001093}
1094
1095
1096/* IO Handler for the "add ssl crt-list" command It adds a new entry in the
1097 * crt-list and generates the ckch_insts for each bind_conf that uses this crt-list
1098 *
1099 * The logic is the same as the "commit ssl cert" command but without the
1100 * freeing of the old structures, because there are none.
Willy Tarreau6b6c3632022-05-05 13:43:49 +02001101 *
1102 * It uses the add_crtlist_ctx for the context.
William Lallemandc756bbd2020-05-13 17:23:59 +02001103 */
1104static int cli_io_handler_add_crtlist(struct appctx *appctx)
1105{
Willy Tarreau6b6c3632022-05-05 13:43:49 +02001106 struct add_crtlist_ctx *ctx = appctx->svcctx;
William Lallemandc756bbd2020-05-13 17:23:59 +02001107 struct bind_conf_list *bind_conf_node;
Willy Tarreauc12b3212022-05-27 11:08:15 +02001108 struct stconn *sc = appctx_sc(appctx);
Willy Tarreau6b6c3632022-05-05 13:43:49 +02001109 struct crtlist *crtlist = ctx->crtlist;
1110 struct crtlist_entry *entry = ctx->entry;
William Lallemandc756bbd2020-05-13 17:23:59 +02001111 struct ckch_store *store = entry->node.key;
William Lallemandc756bbd2020-05-13 17:23:59 +02001112 struct ckch_inst *new_inst;
William Lallemandc756bbd2020-05-13 17:23:59 +02001113 int i = 0;
1114 int errcode = 0;
1115
William Lallemandc756bbd2020-05-13 17:23:59 +02001116 /* for each bind_conf which use the crt-list, a new ckch_inst must be
1117 * created.
1118 */
Christopher Fauletda89e9b2023-01-04 14:11:10 +01001119 if (unlikely(sc_ic(sc)->flags & CF_SHUTW))
Christopher Fauletc642d7c2022-06-01 16:31:09 +02001120 goto end;
William Lallemandc756bbd2020-05-13 17:23:59 +02001121
Willy Tarreaufa11df52022-05-05 13:48:40 +02001122 switch (ctx->state) {
1123 case ADDCRT_ST_INIT:
Willy Tarreau1b948ef2022-05-05 13:50:46 +02001124 /* This state just print the update message */
Christopher Fauletc642d7c2022-06-01 16:31:09 +02001125 chunk_printf(&trash, "Inserting certificate '%s' in crt-list '%s'", store->path, crtlist->node.key);
1126 if (applet_putchk(appctx, &trash) == -1)
Willy Tarreau1b948ef2022-05-05 13:50:46 +02001127 goto yield;
Willy Tarreaufa11df52022-05-05 13:48:40 +02001128 ctx->state = ADDCRT_ST_GEN;
Willy Tarreauaef84482022-11-14 07:03:16 +01001129 __fallthrough;
Willy Tarreaufa11df52022-05-05 13:48:40 +02001130 case ADDCRT_ST_GEN:
Willy Tarreau6b6c3632022-05-05 13:43:49 +02001131 bind_conf_node = ctx->bind_conf_node; /* get the previous ptr from the yield */
Willy Tarreau1b948ef2022-05-05 13:50:46 +02001132 if (bind_conf_node == NULL)
1133 bind_conf_node = crtlist->bind_conf;
1134 for (; bind_conf_node; bind_conf_node = bind_conf_node->next) {
1135 struct bind_conf *bind_conf = bind_conf_node->bind_conf;
1136 struct sni_ctx *sni;
1137
Christopher Fauletc642d7c2022-06-01 16:31:09 +02001138 ctx->bind_conf_node = bind_conf_node;
1139
Willy Tarreau1b948ef2022-05-05 13:50:46 +02001140 /* yield every 10 generations */
1141 if (i > 10) {
Christopher Fauletc642d7c2022-06-01 16:31:09 +02001142 applet_have_more_data(appctx); /* let's come back later */
Willy Tarreau1b948ef2022-05-05 13:50:46 +02001143 goto yield;
1144 }
William Lallemandc756bbd2020-05-13 17:23:59 +02001145
Christopher Fauletc642d7c2022-06-01 16:31:09 +02001146 /* display one dot for each new instance */
1147 if (applet_putstr(appctx, ".") == -1)
1148 goto yield;
1149
Willy Tarreau1b948ef2022-05-05 13:50:46 +02001150 /* we don't support multi-cert bundles, only simple ones */
Christopher Fauletc642d7c2022-06-01 16:31:09 +02001151 ctx->err = NULL;
1152 errcode |= ckch_inst_new_load_store(store->path, store, bind_conf, entry->ssl_conf, entry->filters, entry->fcount, &new_inst, &ctx->err);
1153 if (errcode & ERR_CODE) {
1154 ctx->state = ADDCRT_ST_ERROR;
Willy Tarreau1b948ef2022-05-05 13:50:46 +02001155 goto error;
Christopher Fauletc642d7c2022-06-01 16:31:09 +02001156 }
William Lallemandc756bbd2020-05-13 17:23:59 +02001157
Willy Tarreau1b948ef2022-05-05 13:50:46 +02001158 /* we need to initialize the SSL_CTX generated */
1159 /* this iterate on the newly generated SNIs in the new instance to prepare their SSL_CTX */
1160 list_for_each_entry(sni, &new_inst->sni_ctx, by_ckch_inst) {
1161 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 +02001162 ctx->err = NULL;
1163 errcode |= ssl_sock_prep_ctx_and_inst(bind_conf, new_inst->ssl_conf, sni->ctx, sni->ckch_inst, &ctx->err);
1164 if (errcode & ERR_CODE) {
1165 ctx->state = ADDCRT_ST_ERROR;
William Lallemandc756bbd2020-05-13 17:23:59 +02001166 goto error;
Christopher Fauletc642d7c2022-06-01 16:31:09 +02001167 }
William Lallemandc756bbd2020-05-13 17:23:59 +02001168 }
Willy Tarreau1b948ef2022-05-05 13:50:46 +02001169 }
Christopher Fauletc642d7c2022-06-01 16:31:09 +02001170
Willy Tarreau1b948ef2022-05-05 13:50:46 +02001171 i++;
1172 LIST_APPEND(&store->ckch_inst, &new_inst->by_ckchs);
1173 LIST_APPEND(&entry->ckch_inst, &new_inst->by_crtlist_entry);
1174 new_inst->crtlist_entry = entry;
William Lallemandc756bbd2020-05-13 17:23:59 +02001175 }
Willy Tarreaufa11df52022-05-05 13:48:40 +02001176 ctx->state = ADDCRT_ST_INSERT;
Willy Tarreauaef84482022-11-14 07:03:16 +01001177 __fallthrough;
Willy Tarreaufa11df52022-05-05 13:48:40 +02001178 case ADDCRT_ST_INSERT:
William Lallemandcb6c5f42022-06-20 16:51:53 +02001179 /* the insertion is called for every instance of the store, not
1180 * only the one we generated.
1181 * But the ssl_sock_load_cert_sni() skip the sni already
1182 * inserted. Not every instance has a bind_conf, it could be
1183 * the store of a server so we should be careful */
1184
Willy Tarreau1b948ef2022-05-05 13:50:46 +02001185 list_for_each_entry(new_inst, &store->ckch_inst, by_ckchs) {
William Lallemandcb6c5f42022-06-20 16:51:53 +02001186 if (!new_inst->bind_conf) /* this is a server instance */
1187 continue;
Willy Tarreau1b948ef2022-05-05 13:50:46 +02001188 HA_RWLOCK_WRLOCK(SNI_LOCK, &new_inst->bind_conf->sni_lock);
1189 ssl_sock_load_cert_sni(new_inst, new_inst->bind_conf);
1190 HA_RWLOCK_WRUNLOCK(SNI_LOCK, &new_inst->bind_conf->sni_lock);
1191 }
1192 entry->linenum = ++crtlist->linecount;
Christopher Fauletc642d7c2022-06-01 16:31:09 +02001193 ctx->entry = NULL;
1194 ctx->state = ADDCRT_ST_SUCCESS;
Willy Tarreauaef84482022-11-14 07:03:16 +01001195 __fallthrough;
Christopher Fauletc642d7c2022-06-01 16:31:09 +02001196 case ADDCRT_ST_SUCCESS:
1197 chunk_reset(&trash);
1198 chunk_appendf(&trash, "\n");
1199 if (ctx->err)
1200 chunk_appendf(&trash, "%s", ctx->err);
1201 chunk_appendf(&trash, "Success!\n");
1202 if (applet_putchk(appctx, &trash) == -1)
1203 goto yield;
1204 ctx->state = ADDCRT_ST_FIN;
1205 break;
1206
1207 case ADDCRT_ST_ERROR:
1208 error:
1209 chunk_printf(&trash, "\n%sFailed!\n", ctx->err);
1210 if (applet_putchk(appctx, &trash) == -1)
1211 goto yield;
1212 break;
1213
Willy Tarreaufa11df52022-05-05 13:48:40 +02001214 default:
1215 break;
William Lallemandc756bbd2020-05-13 17:23:59 +02001216 }
1217
Christopher Fauletc642d7c2022-06-01 16:31:09 +02001218end:
William Lallemandc756bbd2020-05-13 17:23:59 +02001219 /* success: call the release function and don't come back */
1220 return 1;
1221yield:
William Lallemandc756bbd2020-05-13 17:23:59 +02001222 return 0; /* should come back */
William Lallemandc756bbd2020-05-13 17:23:59 +02001223}
1224
1225
1226/*
1227 * Parse a "add ssl crt-list <crt-list> <certfile>" line.
Willy Tarreau6b6c3632022-05-05 13:43:49 +02001228 * Filters and option must be passed through payload.
1229 * It sets a struct add_crtlist_ctx.
William Lallemandc756bbd2020-05-13 17:23:59 +02001230 */
1231static int cli_parse_add_crtlist(char **args, char *payload, struct appctx *appctx, void *private)
1232{
Willy Tarreau6b6c3632022-05-05 13:43:49 +02001233 struct add_crtlist_ctx *ctx = applet_reserve_svcctx(appctx, sizeof(*ctx));
William Lallemandc756bbd2020-05-13 17:23:59 +02001234 int cfgerr = 0;
1235 struct ckch_store *store;
1236 char *err = NULL;
1237 char path[MAXPATHLEN+1];
1238 char *crtlist_path;
1239 char *cert_path = NULL;
1240 struct ebmb_node *eb;
1241 struct ebpt_node *inserted;
1242 struct crtlist *crtlist;
1243 struct crtlist_entry *entry = NULL;
William Lallemand99cc2182020-06-25 15:19:51 +02001244 char *end;
William Lallemandc756bbd2020-05-13 17:23:59 +02001245
1246 if (!cli_has_level(appctx, ACCESS_LVL_ADMIN))
1247 return 1;
1248
1249 if (!*args[3] || (!payload && !*args[4]))
1250 return cli_err(appctx, "'add ssl crtlist' expects a filename and a certificate name\n");
1251
1252 crtlist_path = args[3];
1253
William Lallemand99cc2182020-06-25 15:19:51 +02001254 /* strip trailing slashes, including first one */
1255 for (end = crtlist_path + strlen(crtlist_path) - 1; end >= crtlist_path && *end == '/'; end--)
1256 *end = 0;
1257
William Lallemandc756bbd2020-05-13 17:23:59 +02001258 if (HA_SPIN_TRYLOCK(CKCH_LOCK, &ckch_lock))
1259 return cli_err(appctx, "Operations on certificates are currently locked!\n");
1260
1261 eb = ebst_lookup(&crtlists_tree, crtlist_path);
1262 if (!eb) {
1263 memprintf(&err, "crt-list '%s' does not exist!", crtlist_path);
1264 goto error;
1265 }
1266 crtlist = ebmb_entry(eb, struct crtlist, node);
1267
1268 entry = crtlist_entry_new();
1269 if (entry == NULL) {
1270 memprintf(&err, "Not enough memory!");
1271 goto error;
1272 }
1273
1274 if (payload) {
1275 char *lf;
1276
1277 lf = strrchr(payload, '\n');
1278 if (lf) {
1279 memprintf(&err, "only one line of payload is supported!");
1280 goto error;
1281 }
1282 /* cert_path is filled here */
Remi Tricot-Le Bretonfb00f312021-03-23 16:41:53 +01001283 cfgerr |= crtlist_parse_line(payload, &cert_path, entry, "CLI", 1, 1, &err);
William Lallemandc756bbd2020-05-13 17:23:59 +02001284 if (cfgerr & ERR_CODE)
1285 goto error;
1286 } else {
1287 cert_path = args[4];
1288 }
1289
1290 if (!cert_path) {
1291 memprintf(&err, "'add ssl crtlist' should contain the certificate name in the payload");
1292 cfgerr |= ERR_ALERT | ERR_FATAL;
1293 goto error;
1294 }
1295
1296 if (eb_gettag(crtlist->entries.b[EB_RGHT])) {
1297 char *slash;
1298
1299 slash = strrchr(cert_path, '/');
1300 if (!slash) {
1301 memprintf(&err, "'%s' is a directory, certificate path '%s' must contain the directory path", (char *)crtlist->node.key, cert_path);
1302 goto error;
1303 }
1304 /* temporary replace / by 0 to do an strcmp */
1305 *slash = '\0';
1306 if (strcmp(cert_path, (char*)crtlist->node.key) != 0) {
1307 *slash = '/';
1308 memprintf(&err, "'%s' is a directory, certificate path '%s' must contain the directory path", (char *)crtlist->node.key, cert_path);
1309 goto error;
1310 }
1311 *slash = '/';
1312 }
1313
1314 if (*cert_path != '/' && global_ssl.crt_base) {
Willy Tarreau393e42a2022-05-09 10:31:28 +02001315 if ((strlen(global_ssl.crt_base) + 1 + strlen(cert_path)) > sizeof(path) ||
1316 snprintf(path, sizeof(path), "%s/%s", global_ssl.crt_base, cert_path) > sizeof(path)) {
William Lallemandc756bbd2020-05-13 17:23:59 +02001317 memprintf(&err, "'%s' : path too long", cert_path);
1318 cfgerr |= ERR_ALERT | ERR_FATAL;
1319 goto error;
1320 }
William Lallemandc756bbd2020-05-13 17:23:59 +02001321 cert_path = path;
1322 }
1323
1324 store = ckchs_lookup(cert_path);
1325 if (store == NULL) {
1326 memprintf(&err, "certificate '%s' does not exist!", cert_path);
1327 goto error;
1328 }
William Lallemand52ddd992022-11-22 11:51:53 +01001329 if (store->data == NULL || store->data->cert == NULL) {
William Lallemandc756bbd2020-05-13 17:23:59 +02001330 memprintf(&err, "certificate '%s' is empty!", cert_path);
1331 goto error;
1332 }
1333
Remi Tricot-Le Breton86d1e0b2023-03-02 15:49:53 +01001334 /* No need to check 'ocsp-update' inconsistency on a store that is not
1335 * used yet (it was just added through the CLI for instance).
1336 */
1337 if (!LIST_ISEMPTY(&store->ckch_inst) &&
1338 ocsp_update_check_cfg_consistency(store, entry, cert_path, &err))
1339 goto error;
1340
1341 if (entry->ssl_conf)
1342 store->data->ocsp_update_mode = entry->ssl_conf->ocsp_update;
1343
William Lallemandc756bbd2020-05-13 17:23:59 +02001344 /* check if it's possible to insert this new crtlist_entry */
1345 entry->node.key = store;
1346 inserted = ebpt_insert(&crtlist->entries, &entry->node);
1347 if (inserted != &entry->node) {
1348 memprintf(&err, "file already exists in this directory!");
1349 goto error;
1350 }
1351
1352 /* this is supposed to be a directory (EB_ROOT_UNIQUE), so no ssl_conf are allowed */
1353 if ((entry->ssl_conf || entry->filters) && eb_gettag(crtlist->entries.b[EB_RGHT])) {
1354 memprintf(&err, "this is a directory, SSL configuration and filters are not allowed");
1355 goto error;
1356 }
1357
Willy Tarreau2b718102021-04-21 07:32:39 +02001358 LIST_APPEND(&crtlist->ord_entries, &entry->by_crtlist);
William Lallemandc756bbd2020-05-13 17:23:59 +02001359 entry->crtlist = crtlist;
Willy Tarreau2b718102021-04-21 07:32:39 +02001360 LIST_APPEND(&store->crtlist_entry, &entry->by_ckch_store);
William Lallemandc756bbd2020-05-13 17:23:59 +02001361
Willy Tarreaufa11df52022-05-05 13:48:40 +02001362 ctx->state = ADDCRT_ST_INIT;
Willy Tarreau6b6c3632022-05-05 13:43:49 +02001363 ctx->crtlist = crtlist;
1364 ctx->entry = entry;
William Lallemandc756bbd2020-05-13 17:23:59 +02001365
1366 /* unlock is done in the release handler */
1367 return 0;
1368
1369error:
1370 crtlist_entry_free(entry);
1371 HA_SPIN_UNLOCK(CKCH_LOCK, &ckch_lock);
1372 err = memprintf(&err, "Can't edit the crt-list: %s\n", err ? err : "");
1373 return cli_dynerr(appctx, err);
1374}
1375
1376/* Parse a "del ssl crt-list <crt-list> <certfile>" line. */
1377static int cli_parse_del_crtlist(char **args, char *payload, struct appctx *appctx, void *private)
1378{
1379 struct ckch_store *store;
1380 char *err = NULL;
1381 char *crtlist_path, *cert_path;
1382 struct ebmb_node *ebmb;
1383 struct ebpt_node *ebpt;
1384 struct crtlist *crtlist;
1385 struct crtlist_entry *entry = NULL;
1386 struct ckch_inst *inst, *inst_s;
1387 int linenum = 0;
1388 char *colons;
William Lallemand99cc2182020-06-25 15:19:51 +02001389 char *end;
Remi Tricot-Le Bretonbc2c3862021-03-26 10:47:50 +01001390 int error_message_dumped = 0;
William Lallemandc756bbd2020-05-13 17:23:59 +02001391
1392 if (!cli_has_level(appctx, ACCESS_LVL_ADMIN))
1393 return 1;
1394
1395 if (!*args[3] || !*args[4])
1396 return cli_err(appctx, "'del ssl crtlist' expects a filename and a certificate name\n");
1397
1398 if (HA_SPIN_TRYLOCK(CKCH_LOCK, &ckch_lock))
1399 return cli_err(appctx, "Can't delete!\nOperations on certificates are currently locked!\n");
1400
1401 crtlist_path = args[3];
1402 cert_path = args[4];
1403
1404 colons = strchr(cert_path, ':');
1405 if (colons) {
1406 char *endptr;
1407
1408 linenum = strtol(colons + 1, &endptr, 10);
1409 if (colons + 1 == endptr || *endptr != '\0') {
1410 memprintf(&err, "wrong line number after colons in '%s'!", cert_path);
1411 goto error;
1412 }
1413 *colons = '\0';
1414 }
William Lallemand99cc2182020-06-25 15:19:51 +02001415
1416 /* strip trailing slashes, including first one */
1417 for (end = crtlist_path + strlen(crtlist_path) - 1; end >= crtlist_path && *end == '/'; end--)
1418 *end = 0;
1419
William Lallemandc756bbd2020-05-13 17:23:59 +02001420 /* look for crtlist */
1421 ebmb = ebst_lookup(&crtlists_tree, crtlist_path);
1422 if (!ebmb) {
1423 memprintf(&err, "crt-list '%s' does not exist!", crtlist_path);
1424 goto error;
1425 }
1426 crtlist = ebmb_entry(ebmb, struct crtlist, node);
1427
1428 /* look for store */
1429 store = ckchs_lookup(cert_path);
1430 if (store == NULL) {
1431 memprintf(&err, "certificate '%s' does not exist!", cert_path);
1432 goto error;
1433 }
William Lallemand52ddd992022-11-22 11:51:53 +01001434 if (store->data == NULL || store->data->cert == NULL) {
William Lallemandc756bbd2020-05-13 17:23:59 +02001435 memprintf(&err, "certificate '%s' is empty!", cert_path);
1436 goto error;
1437 }
1438
1439 ebpt = ebpt_lookup(&crtlist->entries, store);
1440 if (!ebpt) {
1441 memprintf(&err, "certificate '%s' can't be found in crt-list '%s'!", cert_path, crtlist_path);
1442 goto error;
1443 }
1444
1445 /* list the line number of entries for errors in err, and select the right ebpt */
1446 for (; ebpt; ebpt = ebpt_next_dup(ebpt)) {
1447 struct crtlist_entry *tmp;
1448
1449 tmp = ebpt_entry(ebpt, struct crtlist_entry, node);
1450 memprintf(&err, "%s%s%d", err ? err : "", err ? ", " : "", tmp->linenum);
1451
1452 /* select the entry we wanted */
1453 if (linenum == 0 || tmp->linenum == linenum) {
1454 if (!entry)
1455 entry = tmp;
1456 }
1457 }
1458
1459 /* we didn't found the specified entry */
1460 if (!entry) {
1461 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);
1462 goto error;
1463 }
1464
1465 /* we didn't specified a line number but there were several entries */
1466 if (linenum == 0 && ebpt_next_dup(&entry->node)) {
1467 memprintf(&err, "found the certificate '%s' in several entries, please specify a line number preceded by colons (%s)!", cert_path, err ? err : NULL);
1468 goto error;
1469 }
1470
Remi Tricot-Le Bretonbc2c3862021-03-26 10:47:50 +01001471 /* Iterate over all the instances in order to see if any of them is a
1472 * default instance. If this is the case, the entry won't be suppressed. */
1473 list_for_each_entry_safe(inst, inst_s, &entry->ckch_inst, by_crtlist_entry) {
1474 if (inst->is_default && !inst->bind_conf->strict_sni) {
1475 if (!error_message_dumped) {
1476 memprintf(&err, "certificate '%s' cannot be deleted, it is used as default certificate by the following frontends:\n", cert_path);
1477 error_message_dumped = 1;
1478 }
1479 memprintf(&err, "%s\t- %s:%d\n", err, inst->bind_conf->file, inst->bind_conf->line);
1480 }
1481 }
1482 if (error_message_dumped)
1483 goto error;
1484
William Lallemandc756bbd2020-05-13 17:23:59 +02001485 /* upon error free the ckch_inst and everything inside */
1486
1487 ebpt_delete(&entry->node);
Willy Tarreau2b718102021-04-21 07:32:39 +02001488 LIST_DELETE(&entry->by_crtlist);
1489 LIST_DELETE(&entry->by_ckch_store);
William Lallemandc756bbd2020-05-13 17:23:59 +02001490
1491 list_for_each_entry_safe(inst, inst_s, &entry->ckch_inst, by_crtlist_entry) {
1492 struct sni_ctx *sni, *sni_s;
Remi Tricot-Le Breton4458b972021-02-19 17:41:55 +01001493 struct ckch_inst_link_ref *link_ref, *link_ref_s;
William Lallemandc756bbd2020-05-13 17:23:59 +02001494
1495 HA_RWLOCK_WRLOCK(SNI_LOCK, &inst->bind_conf->sni_lock);
1496 list_for_each_entry_safe(sni, sni_s, &inst->sni_ctx, by_ckch_inst) {
1497 ebmb_delete(&sni->name);
Willy Tarreau2b718102021-04-21 07:32:39 +02001498 LIST_DELETE(&sni->by_ckch_inst);
William Lallemandc756bbd2020-05-13 17:23:59 +02001499 SSL_CTX_free(sni->ctx);
1500 free(sni);
1501 }
1502 HA_RWLOCK_WRUNLOCK(SNI_LOCK, &inst->bind_conf->sni_lock);
Willy Tarreau2b718102021-04-21 07:32:39 +02001503 LIST_DELETE(&inst->by_ckchs);
Remi Tricot-Le Breton4458b972021-02-19 17:41:55 +01001504 list_for_each_entry_safe(link_ref, link_ref_s, &inst->cafile_link_refs, list) {
1505 LIST_DELETE(&link_ref->link->list);
1506 LIST_DELETE(&link_ref->list);
1507 free(link_ref);
1508 }
William Lallemandc756bbd2020-05-13 17:23:59 +02001509 free(inst);
1510 }
1511
1512 crtlist_free_filters(entry->filters);
1513 ssl_sock_free_ssl_conf(entry->ssl_conf);
1514 free(entry->ssl_conf);
1515 free(entry);
1516
1517 HA_SPIN_UNLOCK(CKCH_LOCK, &ckch_lock);
1518 err = memprintf(&err, "Entry '%s' deleted in crtlist '%s'!\n", cert_path, crtlist_path);
1519 return cli_dynmsg(appctx, LOG_NOTICE, err);
1520
1521error:
1522 HA_SPIN_UNLOCK(CKCH_LOCK, &ckch_lock);
1523 err = memprintf(&err, "Can't delete the entry: %s\n", err ? err : "");
1524 return cli_dynerr(appctx, err);
1525}
1526
1527
William Lallemandee8530c2020-06-23 18:19:42 +02001528/* unlink and free all crt-list and crt-list entries */
1529void crtlist_deinit()
1530{
1531 struct eb_node *node, *next;
1532 struct crtlist *crtlist;
1533
1534 node = eb_first(&crtlists_tree);
1535 while (node) {
1536 next = eb_next(node);
1537 crtlist = ebmb_entry(node, struct crtlist, node);
1538 crtlist_free(crtlist);
1539 node = next;
1540 }
1541}
1542
William Lallemandc756bbd2020-05-13 17:23:59 +02001543
1544/* register cli keywords */
1545static struct cli_kw_list cli_kws = {{ },{
Willy Tarreaub205bfd2021-05-07 11:38:37 +02001546 { { "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 },
1547 { { "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 },
1548 { { "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 +02001549 { { NULL }, NULL, NULL, NULL } }
1550};
1551
1552INITCALL1(STG_REGISTER, cli_register_kw, &cli_kws);
1553