blob: 529611806634f4047dd2a774f6ced1c93499e9d4 [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 Faulet87633c32023-04-03 18:32:50 +02001119 /* FIXME: Don't watch the other side !*/
Christopher Faulet208c7122023-04-13 16:16:15 +02001120 if (unlikely(sc_opposite(sc)->flags & SC_FL_SHUT_DONE))
Christopher Fauletc642d7c2022-06-01 16:31:09 +02001121 goto end;
William Lallemandc756bbd2020-05-13 17:23:59 +02001122
Willy Tarreaufa11df52022-05-05 13:48:40 +02001123 switch (ctx->state) {
1124 case ADDCRT_ST_INIT:
Willy Tarreau1b948ef2022-05-05 13:50:46 +02001125 /* This state just print the update message */
Christopher Fauletc642d7c2022-06-01 16:31:09 +02001126 chunk_printf(&trash, "Inserting certificate '%s' in crt-list '%s'", store->path, crtlist->node.key);
1127 if (applet_putchk(appctx, &trash) == -1)
Willy Tarreau1b948ef2022-05-05 13:50:46 +02001128 goto yield;
Willy Tarreaufa11df52022-05-05 13:48:40 +02001129 ctx->state = ADDCRT_ST_GEN;
Willy Tarreauaef84482022-11-14 07:03:16 +01001130 __fallthrough;
Willy Tarreaufa11df52022-05-05 13:48:40 +02001131 case ADDCRT_ST_GEN:
Willy Tarreau6b6c3632022-05-05 13:43:49 +02001132 bind_conf_node = ctx->bind_conf_node; /* get the previous ptr from the yield */
Willy Tarreau1b948ef2022-05-05 13:50:46 +02001133 if (bind_conf_node == NULL)
1134 bind_conf_node = crtlist->bind_conf;
1135 for (; bind_conf_node; bind_conf_node = bind_conf_node->next) {
1136 struct bind_conf *bind_conf = bind_conf_node->bind_conf;
1137 struct sni_ctx *sni;
1138
Christopher Fauletc642d7c2022-06-01 16:31:09 +02001139 ctx->bind_conf_node = bind_conf_node;
1140
Willy Tarreau1b948ef2022-05-05 13:50:46 +02001141 /* yield every 10 generations */
1142 if (i > 10) {
Christopher Fauletc642d7c2022-06-01 16:31:09 +02001143 applet_have_more_data(appctx); /* let's come back later */
Willy Tarreau1b948ef2022-05-05 13:50:46 +02001144 goto yield;
1145 }
William Lallemandc756bbd2020-05-13 17:23:59 +02001146
Christopher Fauletc642d7c2022-06-01 16:31:09 +02001147 /* display one dot for each new instance */
1148 if (applet_putstr(appctx, ".") == -1)
1149 goto yield;
1150
Willy Tarreau1b948ef2022-05-05 13:50:46 +02001151 /* we don't support multi-cert bundles, only simple ones */
Christopher Fauletc642d7c2022-06-01 16:31:09 +02001152 ctx->err = NULL;
1153 errcode |= ckch_inst_new_load_store(store->path, store, bind_conf, entry->ssl_conf, entry->filters, entry->fcount, &new_inst, &ctx->err);
1154 if (errcode & ERR_CODE) {
1155 ctx->state = ADDCRT_ST_ERROR;
Willy Tarreau1b948ef2022-05-05 13:50:46 +02001156 goto error;
Christopher Fauletc642d7c2022-06-01 16:31:09 +02001157 }
William Lallemandc756bbd2020-05-13 17:23:59 +02001158
Willy Tarreau1b948ef2022-05-05 13:50:46 +02001159 /* we need to initialize the SSL_CTX generated */
1160 /* this iterate on the newly generated SNIs in the new instance to prepare their SSL_CTX */
1161 list_for_each_entry(sni, &new_inst->sni_ctx, by_ckch_inst) {
1162 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 +02001163 ctx->err = NULL;
1164 errcode |= ssl_sock_prep_ctx_and_inst(bind_conf, new_inst->ssl_conf, sni->ctx, sni->ckch_inst, &ctx->err);
1165 if (errcode & ERR_CODE) {
1166 ctx->state = ADDCRT_ST_ERROR;
William Lallemandc756bbd2020-05-13 17:23:59 +02001167 goto error;
Christopher Fauletc642d7c2022-06-01 16:31:09 +02001168 }
William Lallemandc756bbd2020-05-13 17:23:59 +02001169 }
Willy Tarreau1b948ef2022-05-05 13:50:46 +02001170 }
Christopher Fauletc642d7c2022-06-01 16:31:09 +02001171
Willy Tarreau1b948ef2022-05-05 13:50:46 +02001172 i++;
1173 LIST_APPEND(&store->ckch_inst, &new_inst->by_ckchs);
1174 LIST_APPEND(&entry->ckch_inst, &new_inst->by_crtlist_entry);
1175 new_inst->crtlist_entry = entry;
William Lallemandc756bbd2020-05-13 17:23:59 +02001176 }
Willy Tarreaufa11df52022-05-05 13:48:40 +02001177 ctx->state = ADDCRT_ST_INSERT;
Willy Tarreauaef84482022-11-14 07:03:16 +01001178 __fallthrough;
Willy Tarreaufa11df52022-05-05 13:48:40 +02001179 case ADDCRT_ST_INSERT:
William Lallemandcb6c5f42022-06-20 16:51:53 +02001180 /* the insertion is called for every instance of the store, not
1181 * only the one we generated.
1182 * But the ssl_sock_load_cert_sni() skip the sni already
1183 * inserted. Not every instance has a bind_conf, it could be
1184 * the store of a server so we should be careful */
1185
Willy Tarreau1b948ef2022-05-05 13:50:46 +02001186 list_for_each_entry(new_inst, &store->ckch_inst, by_ckchs) {
William Lallemandcb6c5f42022-06-20 16:51:53 +02001187 if (!new_inst->bind_conf) /* this is a server instance */
1188 continue;
Willy Tarreau1b948ef2022-05-05 13:50:46 +02001189 HA_RWLOCK_WRLOCK(SNI_LOCK, &new_inst->bind_conf->sni_lock);
1190 ssl_sock_load_cert_sni(new_inst, new_inst->bind_conf);
1191 HA_RWLOCK_WRUNLOCK(SNI_LOCK, &new_inst->bind_conf->sni_lock);
1192 }
1193 entry->linenum = ++crtlist->linecount;
Christopher Fauletc642d7c2022-06-01 16:31:09 +02001194 ctx->entry = NULL;
1195 ctx->state = ADDCRT_ST_SUCCESS;
Willy Tarreauaef84482022-11-14 07:03:16 +01001196 __fallthrough;
Christopher Fauletc642d7c2022-06-01 16:31:09 +02001197 case ADDCRT_ST_SUCCESS:
1198 chunk_reset(&trash);
1199 chunk_appendf(&trash, "\n");
1200 if (ctx->err)
1201 chunk_appendf(&trash, "%s", ctx->err);
1202 chunk_appendf(&trash, "Success!\n");
1203 if (applet_putchk(appctx, &trash) == -1)
1204 goto yield;
1205 ctx->state = ADDCRT_ST_FIN;
1206 break;
1207
1208 case ADDCRT_ST_ERROR:
1209 error:
1210 chunk_printf(&trash, "\n%sFailed!\n", ctx->err);
1211 if (applet_putchk(appctx, &trash) == -1)
1212 goto yield;
1213 break;
1214
Willy Tarreaufa11df52022-05-05 13:48:40 +02001215 default:
1216 break;
William Lallemandc756bbd2020-05-13 17:23:59 +02001217 }
1218
Christopher Fauletc642d7c2022-06-01 16:31:09 +02001219end:
William Lallemandc756bbd2020-05-13 17:23:59 +02001220 /* success: call the release function and don't come back */
1221 return 1;
1222yield:
William Lallemandc756bbd2020-05-13 17:23:59 +02001223 return 0; /* should come back */
William Lallemandc756bbd2020-05-13 17:23:59 +02001224}
1225
1226
1227/*
1228 * Parse a "add ssl crt-list <crt-list> <certfile>" line.
Willy Tarreau6b6c3632022-05-05 13:43:49 +02001229 * Filters and option must be passed through payload.
1230 * It sets a struct add_crtlist_ctx.
William Lallemandc756bbd2020-05-13 17:23:59 +02001231 */
1232static int cli_parse_add_crtlist(char **args, char *payload, struct appctx *appctx, void *private)
1233{
Willy Tarreau6b6c3632022-05-05 13:43:49 +02001234 struct add_crtlist_ctx *ctx = applet_reserve_svcctx(appctx, sizeof(*ctx));
William Lallemandc756bbd2020-05-13 17:23:59 +02001235 int cfgerr = 0;
1236 struct ckch_store *store;
1237 char *err = NULL;
1238 char path[MAXPATHLEN+1];
1239 char *crtlist_path;
1240 char *cert_path = NULL;
1241 struct ebmb_node *eb;
1242 struct ebpt_node *inserted;
1243 struct crtlist *crtlist;
1244 struct crtlist_entry *entry = NULL;
William Lallemand99cc2182020-06-25 15:19:51 +02001245 char *end;
William Lallemandc756bbd2020-05-13 17:23:59 +02001246
1247 if (!cli_has_level(appctx, ACCESS_LVL_ADMIN))
1248 return 1;
1249
1250 if (!*args[3] || (!payload && !*args[4]))
1251 return cli_err(appctx, "'add ssl crtlist' expects a filename and a certificate name\n");
1252
1253 crtlist_path = args[3];
1254
William Lallemand99cc2182020-06-25 15:19:51 +02001255 /* strip trailing slashes, including first one */
1256 for (end = crtlist_path + strlen(crtlist_path) - 1; end >= crtlist_path && *end == '/'; end--)
1257 *end = 0;
1258
William Lallemandc756bbd2020-05-13 17:23:59 +02001259 if (HA_SPIN_TRYLOCK(CKCH_LOCK, &ckch_lock))
1260 return cli_err(appctx, "Operations on certificates are currently locked!\n");
1261
1262 eb = ebst_lookup(&crtlists_tree, crtlist_path);
1263 if (!eb) {
1264 memprintf(&err, "crt-list '%s' does not exist!", crtlist_path);
1265 goto error;
1266 }
1267 crtlist = ebmb_entry(eb, struct crtlist, node);
1268
1269 entry = crtlist_entry_new();
1270 if (entry == NULL) {
1271 memprintf(&err, "Not enough memory!");
1272 goto error;
1273 }
1274
1275 if (payload) {
1276 char *lf;
1277
1278 lf = strrchr(payload, '\n');
1279 if (lf) {
1280 memprintf(&err, "only one line of payload is supported!");
1281 goto error;
1282 }
1283 /* cert_path is filled here */
Remi Tricot-Le Bretonfb00f312021-03-23 16:41:53 +01001284 cfgerr |= crtlist_parse_line(payload, &cert_path, entry, "CLI", 1, 1, &err);
William Lallemandc756bbd2020-05-13 17:23:59 +02001285 if (cfgerr & ERR_CODE)
1286 goto error;
1287 } else {
1288 cert_path = args[4];
1289 }
1290
1291 if (!cert_path) {
1292 memprintf(&err, "'add ssl crtlist' should contain the certificate name in the payload");
1293 cfgerr |= ERR_ALERT | ERR_FATAL;
1294 goto error;
1295 }
1296
1297 if (eb_gettag(crtlist->entries.b[EB_RGHT])) {
1298 char *slash;
1299
1300 slash = strrchr(cert_path, '/');
1301 if (!slash) {
1302 memprintf(&err, "'%s' is a directory, certificate path '%s' must contain the directory path", (char *)crtlist->node.key, cert_path);
1303 goto error;
1304 }
1305 /* temporary replace / by 0 to do an strcmp */
1306 *slash = '\0';
1307 if (strcmp(cert_path, (char*)crtlist->node.key) != 0) {
1308 *slash = '/';
1309 memprintf(&err, "'%s' is a directory, certificate path '%s' must contain the directory path", (char *)crtlist->node.key, cert_path);
1310 goto error;
1311 }
1312 *slash = '/';
1313 }
1314
1315 if (*cert_path != '/' && global_ssl.crt_base) {
Willy Tarreau393e42a2022-05-09 10:31:28 +02001316 if ((strlen(global_ssl.crt_base) + 1 + strlen(cert_path)) > sizeof(path) ||
1317 snprintf(path, sizeof(path), "%s/%s", global_ssl.crt_base, cert_path) > sizeof(path)) {
William Lallemandc756bbd2020-05-13 17:23:59 +02001318 memprintf(&err, "'%s' : path too long", cert_path);
1319 cfgerr |= ERR_ALERT | ERR_FATAL;
1320 goto error;
1321 }
William Lallemandc756bbd2020-05-13 17:23:59 +02001322 cert_path = path;
1323 }
1324
1325 store = ckchs_lookup(cert_path);
1326 if (store == NULL) {
1327 memprintf(&err, "certificate '%s' does not exist!", cert_path);
1328 goto error;
1329 }
William Lallemand52ddd992022-11-22 11:51:53 +01001330 if (store->data == NULL || store->data->cert == NULL) {
William Lallemandc756bbd2020-05-13 17:23:59 +02001331 memprintf(&err, "certificate '%s' is empty!", cert_path);
1332 goto error;
1333 }
1334
Remi Tricot-Le Breton86d1e0b2023-03-02 15:49:53 +01001335 /* No need to check 'ocsp-update' inconsistency on a store that is not
1336 * used yet (it was just added through the CLI for instance).
1337 */
1338 if (!LIST_ISEMPTY(&store->ckch_inst) &&
1339 ocsp_update_check_cfg_consistency(store, entry, cert_path, &err))
1340 goto error;
1341
1342 if (entry->ssl_conf)
1343 store->data->ocsp_update_mode = entry->ssl_conf->ocsp_update;
1344
William Lallemandc756bbd2020-05-13 17:23:59 +02001345 /* check if it's possible to insert this new crtlist_entry */
1346 entry->node.key = store;
1347 inserted = ebpt_insert(&crtlist->entries, &entry->node);
1348 if (inserted != &entry->node) {
1349 memprintf(&err, "file already exists in this directory!");
1350 goto error;
1351 }
1352
1353 /* this is supposed to be a directory (EB_ROOT_UNIQUE), so no ssl_conf are allowed */
1354 if ((entry->ssl_conf || entry->filters) && eb_gettag(crtlist->entries.b[EB_RGHT])) {
1355 memprintf(&err, "this is a directory, SSL configuration and filters are not allowed");
1356 goto error;
1357 }
1358
Willy Tarreau2b718102021-04-21 07:32:39 +02001359 LIST_APPEND(&crtlist->ord_entries, &entry->by_crtlist);
William Lallemandc756bbd2020-05-13 17:23:59 +02001360 entry->crtlist = crtlist;
Willy Tarreau2b718102021-04-21 07:32:39 +02001361 LIST_APPEND(&store->crtlist_entry, &entry->by_ckch_store);
William Lallemandc756bbd2020-05-13 17:23:59 +02001362
Willy Tarreaufa11df52022-05-05 13:48:40 +02001363 ctx->state = ADDCRT_ST_INIT;
Willy Tarreau6b6c3632022-05-05 13:43:49 +02001364 ctx->crtlist = crtlist;
1365 ctx->entry = entry;
William Lallemandc756bbd2020-05-13 17:23:59 +02001366
1367 /* unlock is done in the release handler */
1368 return 0;
1369
1370error:
1371 crtlist_entry_free(entry);
1372 HA_SPIN_UNLOCK(CKCH_LOCK, &ckch_lock);
1373 err = memprintf(&err, "Can't edit the crt-list: %s\n", err ? err : "");
1374 return cli_dynerr(appctx, err);
1375}
1376
1377/* Parse a "del ssl crt-list <crt-list> <certfile>" line. */
1378static int cli_parse_del_crtlist(char **args, char *payload, struct appctx *appctx, void *private)
1379{
1380 struct ckch_store *store;
1381 char *err = NULL;
1382 char *crtlist_path, *cert_path;
1383 struct ebmb_node *ebmb;
1384 struct ebpt_node *ebpt;
1385 struct crtlist *crtlist;
1386 struct crtlist_entry *entry = NULL;
1387 struct ckch_inst *inst, *inst_s;
1388 int linenum = 0;
1389 char *colons;
William Lallemand99cc2182020-06-25 15:19:51 +02001390 char *end;
Remi Tricot-Le Bretonbc2c3862021-03-26 10:47:50 +01001391 int error_message_dumped = 0;
William Lallemandc756bbd2020-05-13 17:23:59 +02001392
1393 if (!cli_has_level(appctx, ACCESS_LVL_ADMIN))
1394 return 1;
1395
1396 if (!*args[3] || !*args[4])
1397 return cli_err(appctx, "'del ssl crtlist' expects a filename and a certificate name\n");
1398
1399 if (HA_SPIN_TRYLOCK(CKCH_LOCK, &ckch_lock))
1400 return cli_err(appctx, "Can't delete!\nOperations on certificates are currently locked!\n");
1401
1402 crtlist_path = args[3];
1403 cert_path = args[4];
1404
1405 colons = strchr(cert_path, ':');
1406 if (colons) {
1407 char *endptr;
1408
1409 linenum = strtol(colons + 1, &endptr, 10);
1410 if (colons + 1 == endptr || *endptr != '\0') {
1411 memprintf(&err, "wrong line number after colons in '%s'!", cert_path);
1412 goto error;
1413 }
1414 *colons = '\0';
1415 }
William Lallemand99cc2182020-06-25 15:19:51 +02001416
1417 /* strip trailing slashes, including first one */
1418 for (end = crtlist_path + strlen(crtlist_path) - 1; end >= crtlist_path && *end == '/'; end--)
1419 *end = 0;
1420
William Lallemandc756bbd2020-05-13 17:23:59 +02001421 /* look for crtlist */
1422 ebmb = ebst_lookup(&crtlists_tree, crtlist_path);
1423 if (!ebmb) {
1424 memprintf(&err, "crt-list '%s' does not exist!", crtlist_path);
1425 goto error;
1426 }
1427 crtlist = ebmb_entry(ebmb, struct crtlist, node);
1428
1429 /* look for store */
1430 store = ckchs_lookup(cert_path);
1431 if (store == NULL) {
1432 memprintf(&err, "certificate '%s' does not exist!", cert_path);
1433 goto error;
1434 }
William Lallemand52ddd992022-11-22 11:51:53 +01001435 if (store->data == NULL || store->data->cert == NULL) {
William Lallemandc756bbd2020-05-13 17:23:59 +02001436 memprintf(&err, "certificate '%s' is empty!", cert_path);
1437 goto error;
1438 }
1439
1440 ebpt = ebpt_lookup(&crtlist->entries, store);
1441 if (!ebpt) {
1442 memprintf(&err, "certificate '%s' can't be found in crt-list '%s'!", cert_path, crtlist_path);
1443 goto error;
1444 }
1445
1446 /* list the line number of entries for errors in err, and select the right ebpt */
1447 for (; ebpt; ebpt = ebpt_next_dup(ebpt)) {
1448 struct crtlist_entry *tmp;
1449
1450 tmp = ebpt_entry(ebpt, struct crtlist_entry, node);
1451 memprintf(&err, "%s%s%d", err ? err : "", err ? ", " : "", tmp->linenum);
1452
1453 /* select the entry we wanted */
1454 if (linenum == 0 || tmp->linenum == linenum) {
1455 if (!entry)
1456 entry = tmp;
1457 }
1458 }
1459
1460 /* we didn't found the specified entry */
1461 if (!entry) {
1462 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);
1463 goto error;
1464 }
1465
1466 /* we didn't specified a line number but there were several entries */
1467 if (linenum == 0 && ebpt_next_dup(&entry->node)) {
1468 memprintf(&err, "found the certificate '%s' in several entries, please specify a line number preceded by colons (%s)!", cert_path, err ? err : NULL);
1469 goto error;
1470 }
1471
Remi Tricot-Le Bretonbc2c3862021-03-26 10:47:50 +01001472 /* Iterate over all the instances in order to see if any of them is a
1473 * default instance. If this is the case, the entry won't be suppressed. */
1474 list_for_each_entry_safe(inst, inst_s, &entry->ckch_inst, by_crtlist_entry) {
1475 if (inst->is_default && !inst->bind_conf->strict_sni) {
1476 if (!error_message_dumped) {
1477 memprintf(&err, "certificate '%s' cannot be deleted, it is used as default certificate by the following frontends:\n", cert_path);
1478 error_message_dumped = 1;
1479 }
1480 memprintf(&err, "%s\t- %s:%d\n", err, inst->bind_conf->file, inst->bind_conf->line);
1481 }
1482 }
1483 if (error_message_dumped)
1484 goto error;
1485
William Lallemandc756bbd2020-05-13 17:23:59 +02001486 /* upon error free the ckch_inst and everything inside */
1487
1488 ebpt_delete(&entry->node);
Willy Tarreau2b718102021-04-21 07:32:39 +02001489 LIST_DELETE(&entry->by_crtlist);
1490 LIST_DELETE(&entry->by_ckch_store);
William Lallemandc756bbd2020-05-13 17:23:59 +02001491
1492 list_for_each_entry_safe(inst, inst_s, &entry->ckch_inst, by_crtlist_entry) {
1493 struct sni_ctx *sni, *sni_s;
Remi Tricot-Le Breton4458b972021-02-19 17:41:55 +01001494 struct ckch_inst_link_ref *link_ref, *link_ref_s;
William Lallemandc756bbd2020-05-13 17:23:59 +02001495
1496 HA_RWLOCK_WRLOCK(SNI_LOCK, &inst->bind_conf->sni_lock);
1497 list_for_each_entry_safe(sni, sni_s, &inst->sni_ctx, by_ckch_inst) {
1498 ebmb_delete(&sni->name);
Willy Tarreau2b718102021-04-21 07:32:39 +02001499 LIST_DELETE(&sni->by_ckch_inst);
William Lallemandc756bbd2020-05-13 17:23:59 +02001500 SSL_CTX_free(sni->ctx);
1501 free(sni);
1502 }
1503 HA_RWLOCK_WRUNLOCK(SNI_LOCK, &inst->bind_conf->sni_lock);
Willy Tarreau2b718102021-04-21 07:32:39 +02001504 LIST_DELETE(&inst->by_ckchs);
Remi Tricot-Le Breton4458b972021-02-19 17:41:55 +01001505 list_for_each_entry_safe(link_ref, link_ref_s, &inst->cafile_link_refs, list) {
1506 LIST_DELETE(&link_ref->link->list);
1507 LIST_DELETE(&link_ref->list);
1508 free(link_ref);
1509 }
William Lallemandc756bbd2020-05-13 17:23:59 +02001510 free(inst);
1511 }
1512
1513 crtlist_free_filters(entry->filters);
1514 ssl_sock_free_ssl_conf(entry->ssl_conf);
1515 free(entry->ssl_conf);
1516 free(entry);
1517
1518 HA_SPIN_UNLOCK(CKCH_LOCK, &ckch_lock);
1519 err = memprintf(&err, "Entry '%s' deleted in crtlist '%s'!\n", cert_path, crtlist_path);
1520 return cli_dynmsg(appctx, LOG_NOTICE, err);
1521
1522error:
1523 HA_SPIN_UNLOCK(CKCH_LOCK, &ckch_lock);
1524 err = memprintf(&err, "Can't delete the entry: %s\n", err ? err : "");
1525 return cli_dynerr(appctx, err);
1526}
1527
1528
William Lallemandee8530c2020-06-23 18:19:42 +02001529/* unlink and free all crt-list and crt-list entries */
1530void crtlist_deinit()
1531{
1532 struct eb_node *node, *next;
1533 struct crtlist *crtlist;
1534
1535 node = eb_first(&crtlists_tree);
1536 while (node) {
1537 next = eb_next(node);
1538 crtlist = ebmb_entry(node, struct crtlist, node);
1539 crtlist_free(crtlist);
1540 node = next;
1541 }
1542}
1543
William Lallemandc756bbd2020-05-13 17:23:59 +02001544
1545/* register cli keywords */
1546static struct cli_kw_list cli_kws = {{ },{
Willy Tarreaub205bfd2021-05-07 11:38:37 +02001547 { { "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 },
1548 { { "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 },
1549 { { "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 +02001550 { { NULL }, NULL, NULL, NULL } }
1551};
1552
1553INITCALL1(STG_REGISTER, cli_register_kw, &cli_kws);
1554