blob: 3d3ae407e3efc284654df5613911c6a05d2f1c65 [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 Lallemand1d3c8222023-05-04 15:33:55 +020077#if defined(SSL_CTX_set1_sigalgs_list)
78 ha_free(&conf->sigalgs);
79#endif
William Lallemand6e9556b2020-05-12 17:52:44 +020080 }
81}
82
William Lallemand82f2d2f2020-09-10 19:06:43 +020083/*
84 * Allocate and copy a ssl_bind_conf structure
85 */
86struct ssl_bind_conf *crtlist_dup_ssl_conf(struct ssl_bind_conf *src)
87{
88 struct ssl_bind_conf *dst;
89
90 if (!src)
91 return NULL;
92
93 dst = calloc(1, sizeof(*dst));
94 if (!dst)
95 return NULL;
96
97#if defined(OPENSSL_NPN_NEGOTIATED) && !defined(OPENSSL_NO_NEXTPROTONEG)
98 if (src->npn_str) {
99 dst->npn_str = strdup(src->npn_str);
100 if (!dst->npn_str)
101 goto error;
102 }
103#endif
104#ifdef TLSEXT_TYPE_application_layer_protocol_negotiation
105 if (src->alpn_str) {
106 dst->alpn_str = strdup(src->alpn_str);
107 if (!dst->alpn_str)
108 goto error;
109 }
110#endif
111 if (src->ca_file) {
112 dst->ca_file = strdup(src->ca_file);
113 if (!dst->ca_file)
114 goto error;
115 }
116 if (src->ca_verify_file) {
117 dst->ca_verify_file = strdup(src->ca_verify_file);
118 if (!dst->ca_verify_file)
119 goto error;
120 }
121 if (src->crl_file) {
122 dst->crl_file = strdup(src->crl_file);
123 if (!dst->crl_file)
124 goto error;
125 }
126 if (src->ciphers) {
127 dst->ciphers = strdup(src->ciphers);
128 if (!dst->ciphers)
129 goto error;
130 }
Ilya Shipitsinf34ed0b2020-11-21 14:37:34 +0500131#ifdef HAVE_SSL_CTX_SET_CIPHERSUITES
William Lallemand82f2d2f2020-09-10 19:06:43 +0200132 if (src->ciphersuites) {
133 dst->ciphersuites = strdup(src->ciphersuites);
134 if (!dst->ciphersuites)
135 goto error;
136 }
137#endif
138 if (src->curves) {
139 dst->curves = strdup(src->curves);
140 if (!dst->curves)
141 goto error;
142 }
143 if (src->ecdhe) {
144 dst->ecdhe = strdup(src->ecdhe);
145 if (!dst->ecdhe)
146 goto error;
147 }
Remi Tricot-Le Breton6549f532023-03-14 17:22:24 +0100148
149 dst->ssl_methods_cfg.flags = src->ssl_methods_cfg.flags;
150 dst->ssl_methods_cfg.min = src->ssl_methods_cfg.min;
151 dst->ssl_methods_cfg.max = src->ssl_methods_cfg.max;
152
153 dst->ssl_methods.flags = src->ssl_methods.flags;
154 dst->ssl_methods.min = src->ssl_methods.min;
155 dst->ssl_methods.max = src->ssl_methods.max;
156
William Lallemand1d3c8222023-05-04 15:33:55 +0200157#if defined(SSL_CTX_set1_sigalgs_list)
158 if (src->sigalgs) {
159 dst->sigalgs = strdup(src->sigalgs);
160 if (!dst->sigalgs)
161 goto error;
162 }
163#endif
William Lallemand82f2d2f2020-09-10 19:06:43 +0200164 return dst;
165
166error:
167 ssl_sock_free_ssl_conf(dst);
168 free(dst);
169
170 return NULL;
171}
William Lallemand6e9556b2020-05-12 17:52:44 +0200172
173/* free sni filters */
174void crtlist_free_filters(char **args)
175{
176 int i;
177
178 if (!args)
179 return;
180
181 for (i = 0; args[i]; i++)
182 free(args[i]);
183
184 free(args);
185}
186
187/* Alloc and duplicate a char ** array */
188char **crtlist_dup_filters(char **args, int fcount)
189{
190 char **dst;
191 int i;
192
193 if (fcount == 0)
194 return NULL;
195
196 dst = calloc(fcount + 1, sizeof(*dst));
197 if (!dst)
198 return NULL;
199
200 for (i = 0; i < fcount; i++) {
201 dst[i] = strdup(args[i]);
202 if (!dst[i])
203 goto error;
204 }
205 return dst;
206
207error:
208 crtlist_free_filters(dst);
209 return NULL;
210}
211
212/*
213 * Detach and free a crtlist_entry.
214 * Free the filters, the ssl_conf and call ckch_inst_free() for each ckch_inst
215 */
216void crtlist_entry_free(struct crtlist_entry *entry)
217{
218 struct ckch_inst *inst, *inst_s;
219
220 if (entry == NULL)
221 return;
222
223 ebpt_delete(&entry->node);
Willy Tarreau2b718102021-04-21 07:32:39 +0200224 LIST_DELETE(&entry->by_crtlist);
225 LIST_DELETE(&entry->by_ckch_store);
William Lallemand6e9556b2020-05-12 17:52:44 +0200226 crtlist_free_filters(entry->filters);
227 ssl_sock_free_ssl_conf(entry->ssl_conf);
228 free(entry->ssl_conf);
229 list_for_each_entry_safe(inst, inst_s, &entry->ckch_inst, by_crtlist_entry) {
230 ckch_inst_free(inst);
231 }
232 free(entry);
233}
William Lallemand5622c452020-09-10 19:08:49 +0200234/*
235 * Duplicate a crt_list entry and its content (ssl_conf, filters/fcount)
236 * Return a pointer to the new entry
237 */
238struct crtlist_entry *crtlist_entry_dup(struct crtlist_entry *src)
239{
240 struct crtlist_entry *entry;
241
242 if (src == NULL)
243 return NULL;
244
245 entry = crtlist_entry_new();
246 if (entry == NULL)
247 return NULL;
248
249 if (src->filters) {
250 entry->filters = crtlist_dup_filters(src->filters, src->fcount);
251 if (!entry->filters)
252 goto error;
253 }
254 entry->fcount = src->fcount;
255 if (src->ssl_conf) {
256 entry->ssl_conf = crtlist_dup_ssl_conf(src->ssl_conf);
257 if (!entry->ssl_conf)
258 goto error;
259 }
260 entry->crtlist = src->crtlist;
261
262 return entry;
263
264error:
265
266 crtlist_free_filters(entry->filters);
267 ssl_sock_free_ssl_conf(entry->ssl_conf);
268 free(entry->ssl_conf);
269 free(entry);
270
271 return NULL;
272}
William Lallemand6e9556b2020-05-12 17:52:44 +0200273
274/*
275 * Allocate and initialize a crtlist_entry
276 */
277struct crtlist_entry *crtlist_entry_new()
278{
279 struct crtlist_entry *entry;
280
281 entry = calloc(1, sizeof(*entry));
282 if (entry == NULL)
283 return NULL;
284
285 LIST_INIT(&entry->ckch_inst);
286
Willy Tarreau2b718102021-04-21 07:32:39 +0200287 /* initialize the nodes so we can LIST_DELETE in any cases */
William Lallemand6e9556b2020-05-12 17:52:44 +0200288 LIST_INIT(&entry->by_crtlist);
289 LIST_INIT(&entry->by_ckch_store);
290
291 return entry;
292}
293
294/* Free a crtlist, from the crt_entry to the content of the ssl_conf */
295void crtlist_free(struct crtlist *crtlist)
296{
297 struct crtlist_entry *entry, *s_entry;
William Lallemand6a3168a2020-06-23 11:43:35 +0200298 struct bind_conf_list *bind_conf_node;
William Lallemand6e9556b2020-05-12 17:52:44 +0200299
300 if (crtlist == NULL)
301 return;
302
William Lallemand6a3168a2020-06-23 11:43:35 +0200303 bind_conf_node = crtlist->bind_conf;
304 while (bind_conf_node) {
305 struct bind_conf_list *next = bind_conf_node->next;
306 free(bind_conf_node);
307 bind_conf_node = next;
308 }
309
William Lallemand6e9556b2020-05-12 17:52:44 +0200310 list_for_each_entry_safe(entry, s_entry, &crtlist->ord_entries, by_crtlist) {
311 crtlist_entry_free(entry);
312 }
313 ebmb_delete(&crtlist->node);
314 free(crtlist);
315}
316
317/* Alloc and initialize a struct crtlist
318 * <filename> is the key of the ebmb_node
319 * <unique> initialize the list of entries to be unique (1) or not (0)
320 */
321struct crtlist *crtlist_new(const char *filename, int unique)
322{
323 struct crtlist *newlist;
324
325 newlist = calloc(1, sizeof(*newlist) + strlen(filename) + 1);
326 if (newlist == NULL)
327 return NULL;
328
329 memcpy(newlist->node.key, filename, strlen(filename) + 1);
330 if (unique)
331 newlist->entries = EB_ROOT_UNIQUE;
332 else
333 newlist->entries = EB_ROOT;
334
335 LIST_INIT(&newlist->ord_entries);
336
337 return newlist;
338}
339
340/*
341 * Read a single crt-list line. /!\ alter the <line> string.
342 * Fill <crt_path> and <crtlist_entry>
343 * <crtlist_entry> must be alloc and free by the caller
344 * <crtlist_entry->ssl_conf> is alloc by the function
345 * <crtlist_entry->filters> is alloc by the function
346 * <crt_path> is a ptr in <line>
347 * Return an error code
348 */
Remi Tricot-Le Bretonfb00f312021-03-23 16:41:53 +0100349int 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 +0200350{
351 int cfgerr = 0;
352 int arg, newarg, cur_arg, i, ssl_b = 0, ssl_e = 0;
353 char *end;
354 char *args[MAX_CRT_ARGS + 1];
355 struct ssl_bind_conf *ssl_conf = NULL;
356
357 if (!line || !crt_path || !entry)
358 return ERR_ALERT | ERR_FATAL;
359
360 end = line + strlen(line);
361 if (end-line >= CRT_LINESIZE-1 && *(end-1) != '\n') {
362 /* Check if we reached the limit and the last char is not \n.
363 * Watch out for the last line without the terminating '\n'!
364 */
Tim Duesterhus6d07fae2020-09-29 18:00:27 +0200365 memprintf(err, "parsing [%s:%d]: line too long, limit is %d characters",
366 file, linenum, CRT_LINESIZE-1);
William Lallemand6e9556b2020-05-12 17:52:44 +0200367 cfgerr |= ERR_ALERT | ERR_FATAL;
368 goto error;
369 }
370 arg = 0;
371 newarg = 1;
372 while (*line) {
373 if (isspace((unsigned char)*line)) {
374 newarg = 1;
375 *line = 0;
376 } else if (*line == '[') {
377 if (ssl_b) {
Tim Duesterhus6d07fae2020-09-29 18:00:27 +0200378 memprintf(err, "parsing [%s:%d]: too many '['", file, linenum);
William Lallemand6e9556b2020-05-12 17:52:44 +0200379 cfgerr |= ERR_ALERT | ERR_FATAL;
380 goto error;
381 }
382 if (!arg) {
Tim Duesterhus6d07fae2020-09-29 18:00:27 +0200383 memprintf(err, "parsing [%s:%d]: file must start with a cert", file, linenum);
William Lallemand6e9556b2020-05-12 17:52:44 +0200384 cfgerr |= ERR_ALERT | ERR_FATAL;
385 goto error;
386 }
387 ssl_b = arg;
388 newarg = 1;
389 *line = 0;
390 } else if (*line == ']') {
391 if (ssl_e) {
Tim Duesterhus6d07fae2020-09-29 18:00:27 +0200392 memprintf(err, "parsing [%s:%d]: too many ']'", file, linenum);
William Lallemand6e9556b2020-05-12 17:52:44 +0200393 cfgerr |= ERR_ALERT | ERR_FATAL;
394 goto error;
395 }
396 if (!ssl_b) {
Tim Duesterhus6d07fae2020-09-29 18:00:27 +0200397 memprintf(err, "parsing [%s:%d]: missing '['", file, linenum);
William Lallemand6e9556b2020-05-12 17:52:44 +0200398 cfgerr |= ERR_ALERT | ERR_FATAL;
399 goto error;
400 }
401 ssl_e = arg;
402 newarg = 1;
403 *line = 0;
404 } else if (newarg) {
405 if (arg == MAX_CRT_ARGS) {
Tim Duesterhus6d07fae2020-09-29 18:00:27 +0200406 memprintf(err, "parsing [%s:%d]: too many args ", file, linenum);
William Lallemand6e9556b2020-05-12 17:52:44 +0200407 cfgerr |= ERR_ALERT | ERR_FATAL;
408 goto error;
409 }
410 newarg = 0;
411 args[arg++] = line;
412 }
413 line++;
414 }
415 args[arg++] = line;
416
417 /* empty line */
418 if (!*args[0]) {
419 cfgerr |= ERR_NONE;
420 goto error;
421 }
422
423 *crt_path = args[0];
424
425 if (ssl_b) {
William Lallemandd85227f2023-02-07 17:06:35 +0100426 if (ssl_b > 1) {
427 memprintf(err, "parsing [%s:%d]: malformated line, filters can't be between filename and options!", file, linenum);
428 cfgerr |= ERR_WARN;
429 }
430
William Lallemand6e9556b2020-05-12 17:52:44 +0200431 ssl_conf = calloc(1, sizeof *ssl_conf);
432 if (!ssl_conf) {
433 memprintf(err, "not enough memory!");
434 cfgerr |= ERR_ALERT | ERR_FATAL;
435 goto error;
436 }
437 }
Remi Tricot-Le Bretonfb00f312021-03-23 16:41:53 +0100438
William Lallemand6e9556b2020-05-12 17:52:44 +0200439 cur_arg = ssl_b ? ssl_b : 1;
440 while (cur_arg < ssl_e) {
441 newarg = 0;
William Lallemandaf678062023-02-13 10:58:13 +0100442 for (i = 0; ssl_crtlist_kws[i].kw != NULL; i++) {
443 if (strcmp(ssl_crtlist_kws[i].kw, args[cur_arg]) == 0) {
William Lallemand6e9556b2020-05-12 17:52:44 +0200444 newarg = 1;
William Lallemandaf678062023-02-13 10:58:13 +0100445 cfgerr |= ssl_crtlist_kws[i].parse(args, cur_arg, NULL, ssl_conf, from_cli, err);
446 if (cur_arg + 1 + ssl_crtlist_kws[i].skip > ssl_e) {
Tim Duesterhus6d07fae2020-09-29 18:00:27 +0200447 memprintf(err, "parsing [%s:%d]: ssl args out of '[]' for %s",
448 file, linenum, args[cur_arg]);
William Lallemand6e9556b2020-05-12 17:52:44 +0200449 cfgerr |= ERR_ALERT | ERR_FATAL;
450 goto error;
451 }
William Lallemandaf678062023-02-13 10:58:13 +0100452 cur_arg += 1 + ssl_crtlist_kws[i].skip;
William Lallemand6e9556b2020-05-12 17:52:44 +0200453 break;
454 }
455 }
456 if (!cfgerr && !newarg) {
Tim Duesterhus6d07fae2020-09-29 18:00:27 +0200457 memprintf(err, "parsing [%s:%d]: unknown ssl keyword %s",
458 file, linenum, args[cur_arg]);
William Lallemand6e9556b2020-05-12 17:52:44 +0200459 cfgerr |= ERR_ALERT | ERR_FATAL;
460 goto error;
461 }
462 }
463 entry->linenum = linenum;
464 entry->ssl_conf = ssl_conf;
465 entry->filters = crtlist_dup_filters(&args[cur_arg], arg - cur_arg - 1);
466 entry->fcount = arg - cur_arg - 1;
467
468 return cfgerr;
469
470error:
471 crtlist_free_filters(entry->filters);
472 entry->filters = NULL;
473 ssl_sock_free_ssl_conf(entry->ssl_conf);
Willy Tarreau61cfdf42021-02-20 10:46:51 +0100474 ha_free(&entry->ssl_conf);
William Lallemand6e9556b2020-05-12 17:52:44 +0200475 return cfgerr;
476}
477
478
479
480/* This function parse a crt-list file and store it in a struct crtlist, each line is a crtlist_entry structure
481 * Fill the <crtlist> argument with a pointer to a new crtlist struct
482 *
483 * This function tries to open and store certificate files.
484 */
485int crtlist_parse_file(char *file, struct bind_conf *bind_conf, struct proxy *curproxy, struct crtlist **crtlist, char **err)
486{
487 struct crtlist *newlist;
488 struct crtlist_entry *entry = NULL;
489 char thisline[CRT_LINESIZE];
William Lallemand6e9556b2020-05-12 17:52:44 +0200490 FILE *f;
491 struct stat buf;
492 int linenum = 0;
493 int cfgerr = 0;
Tim Duesterhusb9f6acc2020-09-29 18:00:28 +0200494 int missing_lf = -1;
William Lallemand6e9556b2020-05-12 17:52:44 +0200495
496 if ((f = fopen(file, "r")) == NULL) {
497 memprintf(err, "cannot open file '%s' : %s", file, strerror(errno));
498 return ERR_ALERT | ERR_FATAL;
499 }
500
501 newlist = crtlist_new(file, 0);
502 if (newlist == NULL) {
503 memprintf(err, "Not enough memory!");
504 cfgerr |= ERR_ALERT | ERR_FATAL;
505 goto error;
506 }
507
508 while (fgets(thisline, sizeof(thisline), f) != NULL) {
509 char *end;
510 char *line = thisline;
511 char *crt_path;
William Lallemand86c2dd62020-11-20 14:23:38 +0100512 char path[MAXPATHLEN+1];
William Lallemand6e9556b2020-05-12 17:52:44 +0200513 struct ckch_store *ckchs;
William Lallemand77e1c6f2020-11-20 18:26:09 +0100514 int found = 0;
William Lallemand6e9556b2020-05-12 17:52:44 +0200515
Tim Duesterhusb9f6acc2020-09-29 18:00:28 +0200516 if (missing_lf != -1) {
517 memprintf(err, "parsing [%s:%d]: Stray NUL character at position %d.\n",
518 file, linenum, (missing_lf + 1));
519 cfgerr |= ERR_ALERT | ERR_FATAL;
520 missing_lf = -1;
521 break;
522 }
523
William Lallemand6e9556b2020-05-12 17:52:44 +0200524 linenum++;
525 end = line + strlen(line);
526 if (end-line == sizeof(thisline)-1 && *(end-1) != '\n') {
527 /* Check if we reached the limit and the last char is not \n.
528 * Watch out for the last line without the terminating '\n'!
529 */
Tim Duesterhus6d07fae2020-09-29 18:00:27 +0200530 memprintf(err, "parsing [%s:%d]: line too long, limit is %d characters",
531 file, linenum, (int)sizeof(thisline)-1);
William Lallemand6e9556b2020-05-12 17:52:44 +0200532 cfgerr |= ERR_ALERT | ERR_FATAL;
533 break;
534 }
535
536 if (*line == '#' || *line == '\n' || *line == '\r')
537 continue;
538
Tim Duesterhusb9f6acc2020-09-29 18:00:28 +0200539 if (end > line && *(end-1) == '\n') {
540 /* kill trailing LF */
541 *(end - 1) = 0;
542 }
543 else {
544 /* mark this line as truncated */
545 missing_lf = end - line;
546 }
547
William Lallemand6e9556b2020-05-12 17:52:44 +0200548 entry = crtlist_entry_new();
549 if (entry == NULL) {
550 memprintf(err, "Not enough memory!");
551 cfgerr |= ERR_ALERT | ERR_FATAL;
552 goto error;
553 }
Tim Duesterhusb9f6acc2020-09-29 18:00:28 +0200554
Remi Tricot-Le Bretonfb00f312021-03-23 16:41:53 +0100555 cfgerr |= crtlist_parse_line(thisline, &crt_path, entry, file, linenum, 0, err);
William Lallemand20b0fed2020-09-28 15:45:16 +0200556 if (cfgerr & ERR_CODE)
William Lallemand6e9556b2020-05-12 17:52:44 +0200557 goto error;
558
559 /* empty line */
560 if (!crt_path || !*crt_path) {
561 crtlist_entry_free(entry);
562 entry = NULL;
563 continue;
564 }
565
566 if (*crt_path != '/' && global_ssl.crt_base) {
Willy Tarreau393e42a2022-05-09 10:31:28 +0200567 if ((strlen(global_ssl.crt_base) + 1 + strlen(crt_path)) > sizeof(path) ||
Willy Tarreau63fc9002022-05-09 21:14:04 +0200568 snprintf(path, sizeof(path), "%s/%s", global_ssl.crt_base, crt_path) > sizeof(path)) {
Tim Duesterhus6d07fae2020-09-29 18:00:27 +0200569 memprintf(err, "parsing [%s:%d]: '%s' : path too long",
570 file, linenum, crt_path);
William Lallemand6e9556b2020-05-12 17:52:44 +0200571 cfgerr |= ERR_ALERT | ERR_FATAL;
572 goto error;
573 }
William Lallemand6e9556b2020-05-12 17:52:44 +0200574 crt_path = path;
575 }
576
577 /* Look for a ckch_store or create one */
578 ckchs = ckchs_lookup(crt_path);
579 if (ckchs == NULL) {
William Lallemand47da8212020-09-10 19:13:27 +0200580 if (stat(crt_path, &buf) == 0) {
William Lallemand77e1c6f2020-11-20 18:26:09 +0100581 found++;
William Lallemand47da8212020-09-10 19:13:27 +0200582
William Lallemandbd8e6ed2020-09-16 16:08:08 +0200583 ckchs = ckchs_load_cert_file(crt_path, err);
William Lallemand47da8212020-09-10 19:13:27 +0200584 if (ckchs == NULL) {
585 cfgerr |= ERR_ALERT | ERR_FATAL;
586 goto error;
587 }
588
589 entry->node.key = ckchs;
590 entry->crtlist = newlist;
Remi Tricot-Le Bretonfb2b9982022-12-20 11:11:11 +0100591 if (entry->ssl_conf)
592 ckchs->data->ocsp_update_mode = entry->ssl_conf->ocsp_update;
William Lallemand47da8212020-09-10 19:13:27 +0200593 ebpt_insert(&newlist->entries, &entry->node);
Willy Tarreau2b718102021-04-21 07:32:39 +0200594 LIST_APPEND(&newlist->ord_entries, &entry->by_crtlist);
595 LIST_APPEND(&ckchs->crtlist_entry, &entry->by_ckch_store);
William Lallemand6e9556b2020-05-12 17:52:44 +0200596
William Lallemand73404572020-11-20 18:23:40 +0100597 } else if (global_ssl.extra_files & SSL_GF_BUNDLE) {
William Lallemand47da8212020-09-10 19:13:27 +0200598 /* If we didn't find the file, this could be a
William Lallemand51f784b2020-10-02 18:08:18 +0200599 bundle, since 2.3 we don't support multiple
600 certificate in the same OpenSSL store, so we
601 emulate it by loading each file separately. To
602 do so we need to duplicate the entry in the
603 crt-list because it becomes independent */
William Lallemand47da8212020-09-10 19:13:27 +0200604 char fp[MAXPATHLEN+1] = {0};
605 int n = 0;
606 struct crtlist_entry *entry_dup = entry; /* use the previous created entry */
607 for (n = 0; n < SSL_SOCK_NUM_KEYTYPES; n++) {
608 struct stat buf;
609 int ret;
610
William Lallemand86c2dd62020-11-20 14:23:38 +0100611 ret = snprintf(fp, sizeof(fp), "%s.%s", crt_path, SSL_SOCK_KEYTYPE_NAMES[n]);
William Lallemand47da8212020-09-10 19:13:27 +0200612 if (ret > sizeof(fp))
613 continue;
614
615 ckchs = ckchs_lookup(fp);
William Lallemand77e1c6f2020-11-20 18:26:09 +0100616 if (!ckchs) {
617 if (stat(fp, &buf) == 0) {
618 ckchs = ckchs_load_cert_file(fp, err);
619 if (!ckchs) {
William Lallemand47da8212020-09-10 19:13:27 +0200620 cfgerr |= ERR_ALERT | ERR_FATAL;
621 goto error;
622 }
William Lallemand77e1c6f2020-11-20 18:26:09 +0100623 } else {
624 continue; /* didn't find this extension, skip */
625 }
626 }
627 found++;
628 linenum++; /* we duplicate the line for this entry in the bundle */
629 if (!entry_dup) { /* if the entry was used, duplicate one */
630 linenum++;
631 entry_dup = crtlist_entry_dup(entry);
632 if (!entry_dup) {
633 cfgerr |= ERR_ALERT | ERR_FATAL;
634 goto error;
William Lallemand47da8212020-09-10 19:13:27 +0200635 }
William Lallemand77e1c6f2020-11-20 18:26:09 +0100636 entry_dup->linenum = linenum;
637 }
William Lallemand47da8212020-09-10 19:13:27 +0200638
William Lallemand77e1c6f2020-11-20 18:26:09 +0100639 entry_dup->node.key = ckchs;
640 entry_dup->crtlist = newlist;
William Lallemanda14686d2023-02-07 18:38:05 +0100641
642 cfgerr |= ocsp_update_check_cfg_consistency(ckchs, entry, crt_path, err);
643 if (cfgerr & ERR_FATAL)
644 goto error;
645
Remi Tricot-Le Bretonfc92b8b2023-01-09 12:02:46 +0100646 if (entry->ssl_conf)
647 ckchs->data->ocsp_update_mode = entry->ssl_conf->ocsp_update;
William Lallemand77e1c6f2020-11-20 18:26:09 +0100648 ebpt_insert(&newlist->entries, &entry_dup->node);
Willy Tarreau2b718102021-04-21 07:32:39 +0200649 LIST_APPEND(&newlist->ord_entries, &entry_dup->by_crtlist);
650 LIST_APPEND(&ckchs->crtlist_entry, &entry_dup->by_ckch_store);
William Lallemand47da8212020-09-10 19:13:27 +0200651
William Lallemand77e1c6f2020-11-20 18:26:09 +0100652 entry_dup = NULL; /* the entry was used, we need a new one next round */
William Lallemand47da8212020-09-10 19:13:27 +0200653 }
William Lallemandb7fdfdf2020-12-04 15:45:02 +0100654#if HA_OPENSSL_VERSION_NUMBER < 0x10101000L
655 if (found) {
656 memprintf(err, "%sCan't load '%s'. Loading a multi certificates bundle requires OpenSSL >= 1.1.1\n",
657 err && *err ? *err : "", crt_path);
658 cfgerr |= ERR_ALERT | ERR_FATAL;
659 }
660#endif
William Lallemand47da8212020-09-10 19:13:27 +0200661 }
William Lallemand77e1c6f2020-11-20 18:26:09 +0100662 if (!found) {
663 memprintf(err, "%sunable to stat SSL certificate from file '%s' : %s.\n",
664 err && *err ? *err : "", crt_path, strerror(errno));
665 cfgerr |= ERR_ALERT | ERR_FATAL;
666 }
667
William Lallemand50c03aa2020-11-06 16:24:07 +0100668 } else {
669 entry->node.key = ckchs;
670 entry->crtlist = newlist;
William Lallemanda14686d2023-02-07 18:38:05 +0100671
672 cfgerr |= ocsp_update_check_cfg_consistency(ckchs, entry, crt_path, err);
673 if (cfgerr & ERR_FATAL)
674 goto error;
675
Remi Tricot-Le Bretonfc92b8b2023-01-09 12:02:46 +0100676 if (entry->ssl_conf)
677 ckchs->data->ocsp_update_mode = entry->ssl_conf->ocsp_update;
William Lallemand50c03aa2020-11-06 16:24:07 +0100678 ebpt_insert(&newlist->entries, &entry->node);
Willy Tarreau2b718102021-04-21 07:32:39 +0200679 LIST_APPEND(&newlist->ord_entries, &entry->by_crtlist);
680 LIST_APPEND(&ckchs->crtlist_entry, &entry->by_ckch_store);
William Lallemand77e1c6f2020-11-20 18:26:09 +0100681 found++;
William Lallemand47da8212020-09-10 19:13:27 +0200682 }
William Lallemand6e9556b2020-05-12 17:52:44 +0200683 entry = NULL;
684 }
Tim Duesterhusb9f6acc2020-09-29 18:00:28 +0200685
686 if (missing_lf != -1) {
687 memprintf(err, "parsing [%s:%d]: Missing LF on last line, file might have been truncated at position %d.\n",
688 file, linenum, (missing_lf + 1));
689 cfgerr |= ERR_ALERT | ERR_FATAL;
690 }
691
William Lallemand6e9556b2020-05-12 17:52:44 +0200692 if (cfgerr & ERR_CODE)
693 goto error;
694
695 newlist->linecount = linenum;
696
697 fclose(f);
698 *crtlist = newlist;
699
700 return cfgerr;
701error:
702 crtlist_entry_free(entry);
703
704 fclose(f);
705 crtlist_free(newlist);
706 return cfgerr;
707}
708
709/* This function reads a directory and stores it in a struct crtlist, each file is a crtlist_entry structure
710 * Fill the <crtlist> argument with a pointer to a new crtlist struct
711 *
712 * This function tries to open and store certificate files.
713 */
714int crtlist_load_cert_dir(char *path, struct bind_conf *bind_conf, struct crtlist **crtlist, char **err)
715{
716 struct crtlist *dir;
717 struct dirent **de_list;
718 int i, n;
719 struct stat buf;
720 char *end;
721 char fp[MAXPATHLEN+1];
722 int cfgerr = 0;
723 struct ckch_store *ckchs;
William Lallemand6e9556b2020-05-12 17:52:44 +0200724
725 dir = crtlist_new(path, 1);
726 if (dir == NULL) {
727 memprintf(err, "not enough memory");
728 return ERR_ALERT | ERR_FATAL;
729 }
730
731 n = scandir(path, &de_list, 0, alphasort);
732 if (n < 0) {
733 memprintf(err, "%sunable to scan directory '%s' : %s.\n",
734 err && *err ? *err : "", path, strerror(errno));
735 cfgerr |= ERR_ALERT | ERR_FATAL;
736 }
737 else {
738 for (i = 0; i < n; i++) {
739 struct crtlist_entry *entry;
740 struct dirent *de = de_list[i];
741
742 end = strrchr(de->d_name, '.');
William Lallemand589570d2022-05-09 10:30:51 +0200743 if (end && (de->d_name[0] == '.' ||
744 strcmp(end, ".issuer") == 0 || strcmp(end, ".ocsp") == 0 ||
745 strcmp(end, ".sctl") == 0 || strcmp(end, ".key") == 0))
William Lallemand6e9556b2020-05-12 17:52:44 +0200746 goto ignore_entry;
747
748 snprintf(fp, sizeof(fp), "%s/%s", path, de->d_name);
749 if (stat(fp, &buf) != 0) {
750 memprintf(err, "%sunable to stat SSL certificate from file '%s' : %s.\n",
751 err && *err ? *err : "", fp, strerror(errno));
752 cfgerr |= ERR_ALERT | ERR_FATAL;
753 goto ignore_entry;
754 }
755 if (!S_ISREG(buf.st_mode))
756 goto ignore_entry;
757
758 entry = crtlist_entry_new();
759 if (entry == NULL) {
760 memprintf(err, "not enough memory '%s'", fp);
761 cfgerr |= ERR_ALERT | ERR_FATAL;
762 goto ignore_entry;
763 }
764
William Lallemand6e9556b2020-05-12 17:52:44 +0200765 ckchs = ckchs_lookup(fp);
766 if (ckchs == NULL)
William Lallemandbd8e6ed2020-09-16 16:08:08 +0200767 ckchs = ckchs_load_cert_file(fp, err);
William Lallemand6e9556b2020-05-12 17:52:44 +0200768 if (ckchs == NULL) {
769 free(de);
770 free(entry);
771 cfgerr |= ERR_ALERT | ERR_FATAL;
772 goto end;
773 }
774 entry->node.key = ckchs;
775 entry->crtlist = dir;
Willy Tarreau2b718102021-04-21 07:32:39 +0200776 LIST_APPEND(&ckchs->crtlist_entry, &entry->by_ckch_store);
777 LIST_APPEND(&dir->ord_entries, &entry->by_crtlist);
William Lallemand6e9556b2020-05-12 17:52:44 +0200778 ebpt_insert(&dir->entries, &entry->node);
779
780ignore_entry:
781 free(de);
782 }
783end:
784 free(de_list);
785 }
786
787 if (cfgerr & ERR_CODE) {
788 /* free the dir and entries on error */
789 crtlist_free(dir);
790 } else {
791 *crtlist = dir;
792 }
793 return cfgerr;
794
795}
796
William Lallemandc756bbd2020-05-13 17:23:59 +0200797/*
798 * Take an ssl_bind_conf structure and append the configuration line used to
799 * create it in the buffer
800 */
801static void dump_crtlist_sslconf(struct buffer *buf, const struct ssl_bind_conf *conf)
802{
803 int space = 0;
804
805 if (conf == NULL)
806 return;
807
808 chunk_appendf(buf, " [");
809#ifdef OPENSSL_NPN_NEGOTIATED
810 if (conf->npn_str) {
811 int len = conf->npn_len;
812 char *ptr = conf->npn_str;
813 int comma = 0;
814
815 if (space) chunk_appendf(buf, " ");
816 chunk_appendf(buf, "npn ");
817 while (len) {
818 unsigned short size;
819
820 size = *ptr;
821 ptr++;
822 if (comma)
823 chunk_memcat(buf, ",", 1);
824 chunk_memcat(buf, ptr, size);
825 ptr += size;
826 len -= size + 1;
827 comma = 1;
828 }
829 chunk_memcat(buf, "", 1); /* finish with a \0 */
830 space++;
831 }
832#endif
833#ifdef TLSEXT_TYPE_application_layer_protocol_negotiation
834 if (conf->alpn_str) {
835 int len = conf->alpn_len;
836 char *ptr = conf->alpn_str;
837 int comma = 0;
838
839 if (space) chunk_appendf(buf, " ");
Willy Tarreaude85de62023-04-19 09:07:47 +0200840 if (len)
841 chunk_appendf(buf, "alpn ");
842 else
843 chunk_appendf(buf, "no-alpn");
William Lallemandc756bbd2020-05-13 17:23:59 +0200844 while (len) {
845 unsigned short size;
846
847 size = *ptr;
848 ptr++;
849 if (comma)
850 chunk_memcat(buf, ",", 1);
851 chunk_memcat(buf, ptr, size);
852 ptr += size;
853 len -= size + 1;
854 comma = 1;
855 }
856 chunk_memcat(buf, "", 1); /* finish with a \0 */
857 space++;
858 }
859#endif
860 /* verify */
861 {
862 if (conf->verify == SSL_SOCK_VERIFY_NONE) {
863 if (space) chunk_appendf(buf, " ");
864 chunk_appendf(buf, "verify none");
865 space++;
866 } else if (conf->verify == SSL_SOCK_VERIFY_OPTIONAL) {
867 if (space) chunk_appendf(buf, " ");
868 chunk_appendf(buf, "verify optional");
869 space++;
870 } else if (conf->verify == SSL_SOCK_VERIFY_REQUIRED) {
871 if (space) chunk_appendf(buf, " ");
872 chunk_appendf(buf, "verify required");
873 space++;
874 }
875 }
876
877 if (conf->no_ca_names) {
878 if (space) chunk_appendf(buf, " ");
879 chunk_appendf(buf, "no-ca-names");
880 space++;
881 }
882
883 if (conf->early_data) {
884 if (space) chunk_appendf(buf, " ");
885 chunk_appendf(buf, "allow-0rtt");
886 space++;
887 }
888 if (conf->ca_file) {
889 if (space) chunk_appendf(buf, " ");
890 chunk_appendf(buf, "ca-file %s", conf->ca_file);
891 space++;
892 }
893 if (conf->crl_file) {
894 if (space) chunk_appendf(buf, " ");
895 chunk_appendf(buf, "crl-file %s", conf->crl_file);
896 space++;
897 }
898 if (conf->ciphers) {
899 if (space) chunk_appendf(buf, " ");
900 chunk_appendf(buf, "ciphers %s", conf->ciphers);
901 space++;
902 }
Ilya Shipitsinf34ed0b2020-11-21 14:37:34 +0500903#ifdef HAVE_SSL_CTX_SET_CIPHERSUITES
William Lallemandc756bbd2020-05-13 17:23:59 +0200904 if (conf->ciphersuites) {
905 if (space) chunk_appendf(buf, " ");
906 chunk_appendf(buf, "ciphersuites %s", conf->ciphersuites);
907 space++;
908 }
909#endif
910 if (conf->curves) {
911 if (space) chunk_appendf(buf, " ");
912 chunk_appendf(buf, "curves %s", conf->curves);
913 space++;
914 }
915 if (conf->ecdhe) {
916 if (space) chunk_appendf(buf, " ");
917 chunk_appendf(buf, "ecdhe %s", conf->ecdhe);
918 space++;
919 }
920
921 /* the crt-lists only support ssl-min-ver and ssl-max-ver */
William Lallemand8177ad92020-05-20 16:49:02 +0200922 if (conf->ssl_methods_cfg.min) {
William Lallemandc756bbd2020-05-13 17:23:59 +0200923 if (space) chunk_appendf(buf, " ");
William Lallemand8177ad92020-05-20 16:49:02 +0200924 chunk_appendf(buf, "ssl-min-ver %s", methodVersions[conf->ssl_methods_cfg.min].name);
William Lallemandc756bbd2020-05-13 17:23:59 +0200925 space++;
926 }
927
William Lallemand8177ad92020-05-20 16:49:02 +0200928 if (conf->ssl_methods_cfg.max) {
William Lallemandc756bbd2020-05-13 17:23:59 +0200929 if (space) chunk_appendf(buf, " ");
William Lallemand8177ad92020-05-20 16:49:02 +0200930 chunk_appendf(buf, "ssl-max-ver %s", methodVersions[conf->ssl_methods_cfg.max].name);
William Lallemandc756bbd2020-05-13 17:23:59 +0200931 space++;
932 }
933
Remi Tricot-Le Bretonca0c84a2023-03-02 15:49:52 +0100934 if (conf->ocsp_update != SSL_SOCK_OCSP_UPDATE_DFLT) {
935 if (space) chunk_appendf(buf, " ");
936 chunk_appendf(buf, "ocsp-update %s",
937 conf->ocsp_update == SSL_SOCK_OCSP_UPDATE_OFF ? "off" : "on");
938 space++;
939 }
940
William Lallemandc756bbd2020-05-13 17:23:59 +0200941 chunk_appendf(buf, "]");
942
943 return;
944}
945
946/* dump a list of filters */
947static void dump_crtlist_filters(struct buffer *buf, struct crtlist_entry *entry)
948{
949 int i;
950
951 if (!entry->fcount)
952 return;
953
954 for (i = 0; i < entry->fcount; i++) {
955 chunk_appendf(buf, " %s", entry->filters[i]);
956 }
957 return;
958}
959
960/************************** CLI functions ****************************/
961
962
Willy Tarreaua2fcca02022-05-05 11:53:23 +0200963/* CLI IO handler for '(show|dump) ssl crt-list'.
964 * It uses show_crtlist_ctx for the context.
965 */
William Lallemandc756bbd2020-05-13 17:23:59 +0200966static int cli_io_handler_dump_crtlist(struct appctx *appctx)
967{
Willy Tarreaua2fcca02022-05-05 11:53:23 +0200968 struct show_crtlist_ctx *ctx = appctx->svcctx;
William Lallemandc756bbd2020-05-13 17:23:59 +0200969 struct buffer *trash = alloc_trash_chunk();
William Lallemandc756bbd2020-05-13 17:23:59 +0200970 struct ebmb_node *lnode;
971
972 if (trash == NULL)
973 return 1;
974
975 /* dump the list of crt-lists */
Willy Tarreaua2fcca02022-05-05 11:53:23 +0200976 lnode = ctx->crtlist_node;
William Lallemandc756bbd2020-05-13 17:23:59 +0200977 if (lnode == NULL)
978 lnode = ebmb_first(&crtlists_tree);
979 while (lnode) {
980 chunk_appendf(trash, "%s\n", lnode->key);
Willy Tarreaud0a06d52022-05-18 15:07:19 +0200981 if (applet_putchk(appctx, trash) == -1)
William Lallemandc756bbd2020-05-13 17:23:59 +0200982 goto yield;
William Lallemandc756bbd2020-05-13 17:23:59 +0200983 lnode = ebmb_next(lnode);
984 }
985 free_trash_chunk(trash);
986 return 1;
987yield:
Willy Tarreaua2fcca02022-05-05 11:53:23 +0200988 ctx->crtlist_node = lnode;
William Lallemandc756bbd2020-05-13 17:23:59 +0200989 free_trash_chunk(trash);
990 return 0;
991}
992
993/* CLI IO handler for '(show|dump) ssl crt-list <filename>' */
994static int cli_io_handler_dump_crtlist_entries(struct appctx *appctx)
995{
Willy Tarreaua2fcca02022-05-05 11:53:23 +0200996 struct show_crtlist_ctx *ctx = appctx->svcctx;
William Lallemandc756bbd2020-05-13 17:23:59 +0200997 struct buffer *trash = alloc_trash_chunk();
998 struct crtlist *crtlist;
William Lallemandc756bbd2020-05-13 17:23:59 +0200999 struct crtlist_entry *entry;
1000
1001 if (trash == NULL)
1002 return 1;
1003
Willy Tarreaua2fcca02022-05-05 11:53:23 +02001004 crtlist = ebmb_entry(ctx->crtlist_node, struct crtlist, node);
William Lallemandc756bbd2020-05-13 17:23:59 +02001005
Willy Tarreaua2fcca02022-05-05 11:53:23 +02001006 entry = ctx->entry;
William Lallemandc756bbd2020-05-13 17:23:59 +02001007 if (entry == NULL) {
1008 entry = LIST_ELEM((crtlist->ord_entries).n, typeof(entry), by_crtlist);
1009 chunk_appendf(trash, "# %s\n", crtlist->node.key);
Willy Tarreaud0a06d52022-05-18 15:07:19 +02001010 if (applet_putchk(appctx, trash) == -1)
William Lallemandc756bbd2020-05-13 17:23:59 +02001011 goto yield;
William Lallemandc756bbd2020-05-13 17:23:59 +02001012 }
1013
1014 list_for_each_entry_from(entry, &crtlist->ord_entries, by_crtlist) {
1015 struct ckch_store *store;
1016 const char *filename;
1017
1018 store = entry->node.key;
1019 filename = store->path;
1020 chunk_appendf(trash, "%s", filename);
Willy Tarreaua2fcca02022-05-05 11:53:23 +02001021 if (ctx->mode == 's') /* show */
William Lallemandc756bbd2020-05-13 17:23:59 +02001022 chunk_appendf(trash, ":%d", entry->linenum);
1023 dump_crtlist_sslconf(trash, entry->ssl_conf);
1024 dump_crtlist_filters(trash, entry);
1025 chunk_appendf(trash, "\n");
1026
Willy Tarreaud0a06d52022-05-18 15:07:19 +02001027 if (applet_putchk(appctx, trash) == -1)
William Lallemandc756bbd2020-05-13 17:23:59 +02001028 goto yield;
William Lallemandc756bbd2020-05-13 17:23:59 +02001029 }
1030 free_trash_chunk(trash);
1031 return 1;
1032yield:
Willy Tarreaua2fcca02022-05-05 11:53:23 +02001033 ctx->entry = entry;
William Lallemandc756bbd2020-05-13 17:23:59 +02001034 free_trash_chunk(trash);
1035 return 0;
1036}
1037
1038/* CLI argument parser for '(show|dump) ssl crt-list' */
1039static int cli_parse_dump_crtlist(char **args, char *payload, struct appctx *appctx, void *private)
1040{
Willy Tarreaua2fcca02022-05-05 11:53:23 +02001041 struct show_crtlist_ctx *ctx = applet_reserve_svcctx(appctx, sizeof(*ctx));
William Lallemandc756bbd2020-05-13 17:23:59 +02001042 struct ebmb_node *lnode;
1043 char *filename = NULL;
1044 int mode;
William Lallemand99cc2182020-06-25 15:19:51 +02001045 char *end;
William Lallemandc756bbd2020-05-13 17:23:59 +02001046
1047 if (!cli_has_level(appctx, ACCESS_LVL_ADMIN))
1048 return 1;
1049
Tim Duesterhuse5ff1412021-01-02 22:31:53 +01001050 if (*args[3] && strcmp(args[3], "-n") == 0) {
William Lallemandc756bbd2020-05-13 17:23:59 +02001051 mode = 's';
1052 filename = args[4];
1053 } else {
1054 mode = 'd';
1055 filename = args[3];
1056 }
1057
1058 if (mode == 's' && !*args[4])
1059 return cli_err(appctx, "'show ssl crt-list -n' expects a filename or a directory\n");
1060
1061 if (filename && *filename) {
William Lallemand99cc2182020-06-25 15:19:51 +02001062
1063
1064 /* strip trailing slashes, including first one */
1065 for (end = filename + strlen(filename) - 1; end >= filename && *end == '/'; end--)
1066 *end = 0;
1067
William Lallemandc756bbd2020-05-13 17:23:59 +02001068 lnode = ebst_lookup(&crtlists_tree, filename);
1069 if (lnode == NULL)
1070 return cli_err(appctx, "didn't find the specified filename\n");
1071
Willy Tarreaua2fcca02022-05-05 11:53:23 +02001072 ctx->crtlist_node = lnode;
William Lallemandc756bbd2020-05-13 17:23:59 +02001073 appctx->io_handler = cli_io_handler_dump_crtlist_entries;
1074 }
Willy Tarreaua2fcca02022-05-05 11:53:23 +02001075 ctx->mode = mode;
William Lallemandc756bbd2020-05-13 17:23:59 +02001076
1077 return 0;
1078}
1079
1080/* release function of the "add ssl crt-list' command, free things and unlock
Willy Tarreau6b6c3632022-05-05 13:43:49 +02001081 * the spinlock. It uses the add_crtlist_ctx.
1082 */
William Lallemandc756bbd2020-05-13 17:23:59 +02001083static void cli_release_add_crtlist(struct appctx *appctx)
1084{
Willy Tarreau6b6c3632022-05-05 13:43:49 +02001085 struct add_crtlist_ctx *ctx = appctx->svcctx;
1086 struct crtlist_entry *entry = ctx->entry;
William Lallemandc756bbd2020-05-13 17:23:59 +02001087
Christopher Fauletc642d7c2022-06-01 16:31:09 +02001088 if (entry) {
William Lallemandc756bbd2020-05-13 17:23:59 +02001089 struct ckch_inst *inst, *inst_s;
Christopher Fauletc642d7c2022-06-01 16:31:09 +02001090
William Lallemandc756bbd2020-05-13 17:23:59 +02001091 /* upon error free the ckch_inst and everything inside */
1092 ebpt_delete(&entry->node);
Willy Tarreau2b718102021-04-21 07:32:39 +02001093 LIST_DELETE(&entry->by_crtlist);
1094 LIST_DELETE(&entry->by_ckch_store);
William Lallemandc756bbd2020-05-13 17:23:59 +02001095
1096 list_for_each_entry_safe(inst, inst_s, &entry->ckch_inst, by_ckchs) {
1097 ckch_inst_free(inst);
1098 }
1099 crtlist_free_filters(entry->filters);
1100 ssl_sock_free_ssl_conf(entry->ssl_conf);
1101 free(entry->ssl_conf);
1102 free(entry);
1103 }
William Lallemandc756bbd2020-05-13 17:23:59 +02001104 HA_SPIN_UNLOCK(CKCH_LOCK, &ckch_lock);
Christopher Fauletc642d7c2022-06-01 16:31:09 +02001105 ha_free(&ctx->err);
William Lallemandc756bbd2020-05-13 17:23:59 +02001106}
1107
1108
1109/* IO Handler for the "add ssl crt-list" command It adds a new entry in the
1110 * crt-list and generates the ckch_insts for each bind_conf that uses this crt-list
1111 *
1112 * The logic is the same as the "commit ssl cert" command but without the
1113 * freeing of the old structures, because there are none.
Willy Tarreau6b6c3632022-05-05 13:43:49 +02001114 *
1115 * It uses the add_crtlist_ctx for the context.
William Lallemandc756bbd2020-05-13 17:23:59 +02001116 */
1117static int cli_io_handler_add_crtlist(struct appctx *appctx)
1118{
Willy Tarreau6b6c3632022-05-05 13:43:49 +02001119 struct add_crtlist_ctx *ctx = appctx->svcctx;
William Lallemandc756bbd2020-05-13 17:23:59 +02001120 struct bind_conf_list *bind_conf_node;
Willy Tarreauc12b3212022-05-27 11:08:15 +02001121 struct stconn *sc = appctx_sc(appctx);
Willy Tarreau6b6c3632022-05-05 13:43:49 +02001122 struct crtlist *crtlist = ctx->crtlist;
1123 struct crtlist_entry *entry = ctx->entry;
William Lallemandc756bbd2020-05-13 17:23:59 +02001124 struct ckch_store *store = entry->node.key;
William Lallemandc756bbd2020-05-13 17:23:59 +02001125 struct ckch_inst *new_inst;
William Lallemandc756bbd2020-05-13 17:23:59 +02001126 int i = 0;
1127 int errcode = 0;
1128
William Lallemandc756bbd2020-05-13 17:23:59 +02001129 /* for each bind_conf which use the crt-list, a new ckch_inst must be
1130 * created.
1131 */
Christopher Faulet87633c32023-04-03 18:32:50 +02001132 /* FIXME: Don't watch the other side !*/
Christopher Faulet208c7122023-04-13 16:16:15 +02001133 if (unlikely(sc_opposite(sc)->flags & SC_FL_SHUT_DONE))
Christopher Fauletc642d7c2022-06-01 16:31:09 +02001134 goto end;
William Lallemandc756bbd2020-05-13 17:23:59 +02001135
Willy Tarreaufa11df52022-05-05 13:48:40 +02001136 switch (ctx->state) {
1137 case ADDCRT_ST_INIT:
Willy Tarreau1b948ef2022-05-05 13:50:46 +02001138 /* This state just print the update message */
Christopher Fauletc642d7c2022-06-01 16:31:09 +02001139 chunk_printf(&trash, "Inserting certificate '%s' in crt-list '%s'", store->path, crtlist->node.key);
1140 if (applet_putchk(appctx, &trash) == -1)
Willy Tarreau1b948ef2022-05-05 13:50:46 +02001141 goto yield;
Willy Tarreaufa11df52022-05-05 13:48:40 +02001142 ctx->state = ADDCRT_ST_GEN;
Willy Tarreauaef84482022-11-14 07:03:16 +01001143 __fallthrough;
Willy Tarreaufa11df52022-05-05 13:48:40 +02001144 case ADDCRT_ST_GEN:
Willy Tarreau6b6c3632022-05-05 13:43:49 +02001145 bind_conf_node = ctx->bind_conf_node; /* get the previous ptr from the yield */
Willy Tarreau1b948ef2022-05-05 13:50:46 +02001146 if (bind_conf_node == NULL)
1147 bind_conf_node = crtlist->bind_conf;
1148 for (; bind_conf_node; bind_conf_node = bind_conf_node->next) {
1149 struct bind_conf *bind_conf = bind_conf_node->bind_conf;
1150 struct sni_ctx *sni;
1151
Christopher Fauletc642d7c2022-06-01 16:31:09 +02001152 ctx->bind_conf_node = bind_conf_node;
1153
Willy Tarreau1b948ef2022-05-05 13:50:46 +02001154 /* yield every 10 generations */
1155 if (i > 10) {
Christopher Fauletc642d7c2022-06-01 16:31:09 +02001156 applet_have_more_data(appctx); /* let's come back later */
Willy Tarreau1b948ef2022-05-05 13:50:46 +02001157 goto yield;
1158 }
William Lallemandc756bbd2020-05-13 17:23:59 +02001159
Christopher Fauletc642d7c2022-06-01 16:31:09 +02001160 /* display one dot for each new instance */
1161 if (applet_putstr(appctx, ".") == -1)
1162 goto yield;
1163
Willy Tarreau1b948ef2022-05-05 13:50:46 +02001164 /* we don't support multi-cert bundles, only simple ones */
Christopher Fauletc642d7c2022-06-01 16:31:09 +02001165 ctx->err = NULL;
1166 errcode |= ckch_inst_new_load_store(store->path, store, bind_conf, entry->ssl_conf, entry->filters, entry->fcount, &new_inst, &ctx->err);
1167 if (errcode & ERR_CODE) {
1168 ctx->state = ADDCRT_ST_ERROR;
Willy Tarreau1b948ef2022-05-05 13:50:46 +02001169 goto error;
Christopher Fauletc642d7c2022-06-01 16:31:09 +02001170 }
William Lallemandc756bbd2020-05-13 17:23:59 +02001171
Willy Tarreau1b948ef2022-05-05 13:50:46 +02001172 /* we need to initialize the SSL_CTX generated */
1173 /* this iterate on the newly generated SNIs in the new instance to prepare their SSL_CTX */
1174 list_for_each_entry(sni, &new_inst->sni_ctx, by_ckch_inst) {
1175 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 +02001176 ctx->err = NULL;
1177 errcode |= ssl_sock_prep_ctx_and_inst(bind_conf, new_inst->ssl_conf, sni->ctx, sni->ckch_inst, &ctx->err);
1178 if (errcode & ERR_CODE) {
1179 ctx->state = ADDCRT_ST_ERROR;
William Lallemandc756bbd2020-05-13 17:23:59 +02001180 goto error;
Christopher Fauletc642d7c2022-06-01 16:31:09 +02001181 }
William Lallemandc756bbd2020-05-13 17:23:59 +02001182 }
Willy Tarreau1b948ef2022-05-05 13:50:46 +02001183 }
Christopher Fauletc642d7c2022-06-01 16:31:09 +02001184
Willy Tarreau1b948ef2022-05-05 13:50:46 +02001185 i++;
1186 LIST_APPEND(&store->ckch_inst, &new_inst->by_ckchs);
1187 LIST_APPEND(&entry->ckch_inst, &new_inst->by_crtlist_entry);
1188 new_inst->crtlist_entry = entry;
William Lallemandc756bbd2020-05-13 17:23:59 +02001189 }
Willy Tarreaufa11df52022-05-05 13:48:40 +02001190 ctx->state = ADDCRT_ST_INSERT;
Willy Tarreauaef84482022-11-14 07:03:16 +01001191 __fallthrough;
Willy Tarreaufa11df52022-05-05 13:48:40 +02001192 case ADDCRT_ST_INSERT:
William Lallemandcb6c5f42022-06-20 16:51:53 +02001193 /* the insertion is called for every instance of the store, not
1194 * only the one we generated.
1195 * But the ssl_sock_load_cert_sni() skip the sni already
1196 * inserted. Not every instance has a bind_conf, it could be
1197 * the store of a server so we should be careful */
1198
Willy Tarreau1b948ef2022-05-05 13:50:46 +02001199 list_for_each_entry(new_inst, &store->ckch_inst, by_ckchs) {
William Lallemandcb6c5f42022-06-20 16:51:53 +02001200 if (!new_inst->bind_conf) /* this is a server instance */
1201 continue;
Willy Tarreau1b948ef2022-05-05 13:50:46 +02001202 HA_RWLOCK_WRLOCK(SNI_LOCK, &new_inst->bind_conf->sni_lock);
1203 ssl_sock_load_cert_sni(new_inst, new_inst->bind_conf);
1204 HA_RWLOCK_WRUNLOCK(SNI_LOCK, &new_inst->bind_conf->sni_lock);
1205 }
1206 entry->linenum = ++crtlist->linecount;
Christopher Fauletc642d7c2022-06-01 16:31:09 +02001207 ctx->entry = NULL;
1208 ctx->state = ADDCRT_ST_SUCCESS;
Willy Tarreauaef84482022-11-14 07:03:16 +01001209 __fallthrough;
Christopher Fauletc642d7c2022-06-01 16:31:09 +02001210 case ADDCRT_ST_SUCCESS:
1211 chunk_reset(&trash);
1212 chunk_appendf(&trash, "\n");
1213 if (ctx->err)
1214 chunk_appendf(&trash, "%s", ctx->err);
1215 chunk_appendf(&trash, "Success!\n");
1216 if (applet_putchk(appctx, &trash) == -1)
1217 goto yield;
1218 ctx->state = ADDCRT_ST_FIN;
1219 break;
1220
1221 case ADDCRT_ST_ERROR:
1222 error:
1223 chunk_printf(&trash, "\n%sFailed!\n", ctx->err);
1224 if (applet_putchk(appctx, &trash) == -1)
1225 goto yield;
1226 break;
1227
Willy Tarreaufa11df52022-05-05 13:48:40 +02001228 default:
1229 break;
William Lallemandc756bbd2020-05-13 17:23:59 +02001230 }
1231
Christopher Fauletc642d7c2022-06-01 16:31:09 +02001232end:
William Lallemandc756bbd2020-05-13 17:23:59 +02001233 /* success: call the release function and don't come back */
1234 return 1;
1235yield:
William Lallemandc756bbd2020-05-13 17:23:59 +02001236 return 0; /* should come back */
William Lallemandc756bbd2020-05-13 17:23:59 +02001237}
1238
1239
1240/*
1241 * Parse a "add ssl crt-list <crt-list> <certfile>" line.
Willy Tarreau6b6c3632022-05-05 13:43:49 +02001242 * Filters and option must be passed through payload.
1243 * It sets a struct add_crtlist_ctx.
William Lallemandc756bbd2020-05-13 17:23:59 +02001244 */
1245static int cli_parse_add_crtlist(char **args, char *payload, struct appctx *appctx, void *private)
1246{
Willy Tarreau6b6c3632022-05-05 13:43:49 +02001247 struct add_crtlist_ctx *ctx = applet_reserve_svcctx(appctx, sizeof(*ctx));
William Lallemandc756bbd2020-05-13 17:23:59 +02001248 int cfgerr = 0;
1249 struct ckch_store *store;
1250 char *err = NULL;
1251 char path[MAXPATHLEN+1];
1252 char *crtlist_path;
1253 char *cert_path = NULL;
1254 struct ebmb_node *eb;
1255 struct ebpt_node *inserted;
1256 struct crtlist *crtlist;
1257 struct crtlist_entry *entry = NULL;
William Lallemand99cc2182020-06-25 15:19:51 +02001258 char *end;
William Lallemandc756bbd2020-05-13 17:23:59 +02001259
1260 if (!cli_has_level(appctx, ACCESS_LVL_ADMIN))
1261 return 1;
1262
1263 if (!*args[3] || (!payload && !*args[4]))
1264 return cli_err(appctx, "'add ssl crtlist' expects a filename and a certificate name\n");
1265
1266 crtlist_path = args[3];
1267
William Lallemand99cc2182020-06-25 15:19:51 +02001268 /* strip trailing slashes, including first one */
1269 for (end = crtlist_path + strlen(crtlist_path) - 1; end >= crtlist_path && *end == '/'; end--)
1270 *end = 0;
1271
William Lallemandc756bbd2020-05-13 17:23:59 +02001272 if (HA_SPIN_TRYLOCK(CKCH_LOCK, &ckch_lock))
1273 return cli_err(appctx, "Operations on certificates are currently locked!\n");
1274
1275 eb = ebst_lookup(&crtlists_tree, crtlist_path);
1276 if (!eb) {
1277 memprintf(&err, "crt-list '%s' does not exist!", crtlist_path);
1278 goto error;
1279 }
1280 crtlist = ebmb_entry(eb, struct crtlist, node);
1281
1282 entry = crtlist_entry_new();
1283 if (entry == NULL) {
1284 memprintf(&err, "Not enough memory!");
1285 goto error;
1286 }
1287
1288 if (payload) {
1289 char *lf;
1290
1291 lf = strrchr(payload, '\n');
1292 if (lf) {
1293 memprintf(&err, "only one line of payload is supported!");
1294 goto error;
1295 }
1296 /* cert_path is filled here */
Remi Tricot-Le Bretonfb00f312021-03-23 16:41:53 +01001297 cfgerr |= crtlist_parse_line(payload, &cert_path, entry, "CLI", 1, 1, &err);
William Lallemandc756bbd2020-05-13 17:23:59 +02001298 if (cfgerr & ERR_CODE)
1299 goto error;
1300 } else {
1301 cert_path = args[4];
1302 }
1303
1304 if (!cert_path) {
1305 memprintf(&err, "'add ssl crtlist' should contain the certificate name in the payload");
1306 cfgerr |= ERR_ALERT | ERR_FATAL;
1307 goto error;
1308 }
1309
1310 if (eb_gettag(crtlist->entries.b[EB_RGHT])) {
1311 char *slash;
1312
1313 slash = strrchr(cert_path, '/');
1314 if (!slash) {
1315 memprintf(&err, "'%s' is a directory, certificate path '%s' must contain the directory path", (char *)crtlist->node.key, cert_path);
1316 goto error;
1317 }
1318 /* temporary replace / by 0 to do an strcmp */
1319 *slash = '\0';
1320 if (strcmp(cert_path, (char*)crtlist->node.key) != 0) {
1321 *slash = '/';
1322 memprintf(&err, "'%s' is a directory, certificate path '%s' must contain the directory path", (char *)crtlist->node.key, cert_path);
1323 goto error;
1324 }
1325 *slash = '/';
1326 }
1327
1328 if (*cert_path != '/' && global_ssl.crt_base) {
Willy Tarreau393e42a2022-05-09 10:31:28 +02001329 if ((strlen(global_ssl.crt_base) + 1 + strlen(cert_path)) > sizeof(path) ||
1330 snprintf(path, sizeof(path), "%s/%s", global_ssl.crt_base, cert_path) > sizeof(path)) {
William Lallemandc756bbd2020-05-13 17:23:59 +02001331 memprintf(&err, "'%s' : path too long", cert_path);
1332 cfgerr |= ERR_ALERT | ERR_FATAL;
1333 goto error;
1334 }
William Lallemandc756bbd2020-05-13 17:23:59 +02001335 cert_path = path;
1336 }
1337
1338 store = ckchs_lookup(cert_path);
1339 if (store == NULL) {
1340 memprintf(&err, "certificate '%s' does not exist!", cert_path);
1341 goto error;
1342 }
William Lallemand52ddd992022-11-22 11:51:53 +01001343 if (store->data == NULL || store->data->cert == NULL) {
William Lallemandc756bbd2020-05-13 17:23:59 +02001344 memprintf(&err, "certificate '%s' is empty!", cert_path);
1345 goto error;
1346 }
1347
Remi Tricot-Le Breton86d1e0b2023-03-02 15:49:53 +01001348 /* No need to check 'ocsp-update' inconsistency on a store that is not
1349 * used yet (it was just added through the CLI for instance).
1350 */
1351 if (!LIST_ISEMPTY(&store->ckch_inst) &&
1352 ocsp_update_check_cfg_consistency(store, entry, cert_path, &err))
1353 goto error;
1354
1355 if (entry->ssl_conf)
1356 store->data->ocsp_update_mode = entry->ssl_conf->ocsp_update;
1357
William Lallemandc756bbd2020-05-13 17:23:59 +02001358 /* check if it's possible to insert this new crtlist_entry */
1359 entry->node.key = store;
1360 inserted = ebpt_insert(&crtlist->entries, &entry->node);
1361 if (inserted != &entry->node) {
1362 memprintf(&err, "file already exists in this directory!");
1363 goto error;
1364 }
1365
1366 /* this is supposed to be a directory (EB_ROOT_UNIQUE), so no ssl_conf are allowed */
1367 if ((entry->ssl_conf || entry->filters) && eb_gettag(crtlist->entries.b[EB_RGHT])) {
1368 memprintf(&err, "this is a directory, SSL configuration and filters are not allowed");
1369 goto error;
1370 }
1371
Willy Tarreau2b718102021-04-21 07:32:39 +02001372 LIST_APPEND(&crtlist->ord_entries, &entry->by_crtlist);
William Lallemandc756bbd2020-05-13 17:23:59 +02001373 entry->crtlist = crtlist;
Willy Tarreau2b718102021-04-21 07:32:39 +02001374 LIST_APPEND(&store->crtlist_entry, &entry->by_ckch_store);
William Lallemandc756bbd2020-05-13 17:23:59 +02001375
Willy Tarreaufa11df52022-05-05 13:48:40 +02001376 ctx->state = ADDCRT_ST_INIT;
Willy Tarreau6b6c3632022-05-05 13:43:49 +02001377 ctx->crtlist = crtlist;
1378 ctx->entry = entry;
William Lallemandc756bbd2020-05-13 17:23:59 +02001379
1380 /* unlock is done in the release handler */
1381 return 0;
1382
1383error:
1384 crtlist_entry_free(entry);
1385 HA_SPIN_UNLOCK(CKCH_LOCK, &ckch_lock);
1386 err = memprintf(&err, "Can't edit the crt-list: %s\n", err ? err : "");
1387 return cli_dynerr(appctx, err);
1388}
1389
1390/* Parse a "del ssl crt-list <crt-list> <certfile>" line. */
1391static int cli_parse_del_crtlist(char **args, char *payload, struct appctx *appctx, void *private)
1392{
1393 struct ckch_store *store;
1394 char *err = NULL;
1395 char *crtlist_path, *cert_path;
1396 struct ebmb_node *ebmb;
1397 struct ebpt_node *ebpt;
1398 struct crtlist *crtlist;
1399 struct crtlist_entry *entry = NULL;
1400 struct ckch_inst *inst, *inst_s;
1401 int linenum = 0;
1402 char *colons;
William Lallemand99cc2182020-06-25 15:19:51 +02001403 char *end;
Remi Tricot-Le Bretonbc2c3862021-03-26 10:47:50 +01001404 int error_message_dumped = 0;
William Lallemandc756bbd2020-05-13 17:23:59 +02001405
1406 if (!cli_has_level(appctx, ACCESS_LVL_ADMIN))
1407 return 1;
1408
1409 if (!*args[3] || !*args[4])
1410 return cli_err(appctx, "'del ssl crtlist' expects a filename and a certificate name\n");
1411
1412 if (HA_SPIN_TRYLOCK(CKCH_LOCK, &ckch_lock))
1413 return cli_err(appctx, "Can't delete!\nOperations on certificates are currently locked!\n");
1414
1415 crtlist_path = args[3];
1416 cert_path = args[4];
1417
1418 colons = strchr(cert_path, ':');
1419 if (colons) {
1420 char *endptr;
1421
1422 linenum = strtol(colons + 1, &endptr, 10);
1423 if (colons + 1 == endptr || *endptr != '\0') {
1424 memprintf(&err, "wrong line number after colons in '%s'!", cert_path);
1425 goto error;
1426 }
1427 *colons = '\0';
1428 }
William Lallemand99cc2182020-06-25 15:19:51 +02001429
1430 /* strip trailing slashes, including first one */
1431 for (end = crtlist_path + strlen(crtlist_path) - 1; end >= crtlist_path && *end == '/'; end--)
1432 *end = 0;
1433
William Lallemandc756bbd2020-05-13 17:23:59 +02001434 /* look for crtlist */
1435 ebmb = ebst_lookup(&crtlists_tree, crtlist_path);
1436 if (!ebmb) {
1437 memprintf(&err, "crt-list '%s' does not exist!", crtlist_path);
1438 goto error;
1439 }
1440 crtlist = ebmb_entry(ebmb, struct crtlist, node);
1441
1442 /* look for store */
1443 store = ckchs_lookup(cert_path);
1444 if (store == NULL) {
1445 memprintf(&err, "certificate '%s' does not exist!", cert_path);
1446 goto error;
1447 }
William Lallemand52ddd992022-11-22 11:51:53 +01001448 if (store->data == NULL || store->data->cert == NULL) {
William Lallemandc756bbd2020-05-13 17:23:59 +02001449 memprintf(&err, "certificate '%s' is empty!", cert_path);
1450 goto error;
1451 }
1452
1453 ebpt = ebpt_lookup(&crtlist->entries, store);
1454 if (!ebpt) {
1455 memprintf(&err, "certificate '%s' can't be found in crt-list '%s'!", cert_path, crtlist_path);
1456 goto error;
1457 }
1458
1459 /* list the line number of entries for errors in err, and select the right ebpt */
1460 for (; ebpt; ebpt = ebpt_next_dup(ebpt)) {
1461 struct crtlist_entry *tmp;
1462
1463 tmp = ebpt_entry(ebpt, struct crtlist_entry, node);
1464 memprintf(&err, "%s%s%d", err ? err : "", err ? ", " : "", tmp->linenum);
1465
1466 /* select the entry we wanted */
1467 if (linenum == 0 || tmp->linenum == linenum) {
1468 if (!entry)
1469 entry = tmp;
1470 }
1471 }
1472
1473 /* we didn't found the specified entry */
1474 if (!entry) {
1475 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);
1476 goto error;
1477 }
1478
1479 /* we didn't specified a line number but there were several entries */
1480 if (linenum == 0 && ebpt_next_dup(&entry->node)) {
1481 memprintf(&err, "found the certificate '%s' in several entries, please specify a line number preceded by colons (%s)!", cert_path, err ? err : NULL);
1482 goto error;
1483 }
1484
Remi Tricot-Le Bretonbc2c3862021-03-26 10:47:50 +01001485 /* Iterate over all the instances in order to see if any of them is a
1486 * default instance. If this is the case, the entry won't be suppressed. */
1487 list_for_each_entry_safe(inst, inst_s, &entry->ckch_inst, by_crtlist_entry) {
1488 if (inst->is_default && !inst->bind_conf->strict_sni) {
1489 if (!error_message_dumped) {
1490 memprintf(&err, "certificate '%s' cannot be deleted, it is used as default certificate by the following frontends:\n", cert_path);
1491 error_message_dumped = 1;
1492 }
1493 memprintf(&err, "%s\t- %s:%d\n", err, inst->bind_conf->file, inst->bind_conf->line);
1494 }
1495 }
1496 if (error_message_dumped)
1497 goto error;
1498
William Lallemandc756bbd2020-05-13 17:23:59 +02001499 /* upon error free the ckch_inst and everything inside */
1500
1501 ebpt_delete(&entry->node);
Willy Tarreau2b718102021-04-21 07:32:39 +02001502 LIST_DELETE(&entry->by_crtlist);
1503 LIST_DELETE(&entry->by_ckch_store);
William Lallemandc756bbd2020-05-13 17:23:59 +02001504
1505 list_for_each_entry_safe(inst, inst_s, &entry->ckch_inst, by_crtlist_entry) {
1506 struct sni_ctx *sni, *sni_s;
Remi Tricot-Le Breton4458b972021-02-19 17:41:55 +01001507 struct ckch_inst_link_ref *link_ref, *link_ref_s;
William Lallemandc756bbd2020-05-13 17:23:59 +02001508
1509 HA_RWLOCK_WRLOCK(SNI_LOCK, &inst->bind_conf->sni_lock);
1510 list_for_each_entry_safe(sni, sni_s, &inst->sni_ctx, by_ckch_inst) {
1511 ebmb_delete(&sni->name);
Willy Tarreau2b718102021-04-21 07:32:39 +02001512 LIST_DELETE(&sni->by_ckch_inst);
William Lallemandc756bbd2020-05-13 17:23:59 +02001513 SSL_CTX_free(sni->ctx);
1514 free(sni);
1515 }
1516 HA_RWLOCK_WRUNLOCK(SNI_LOCK, &inst->bind_conf->sni_lock);
Willy Tarreau2b718102021-04-21 07:32:39 +02001517 LIST_DELETE(&inst->by_ckchs);
Remi Tricot-Le Breton4458b972021-02-19 17:41:55 +01001518 list_for_each_entry_safe(link_ref, link_ref_s, &inst->cafile_link_refs, list) {
1519 LIST_DELETE(&link_ref->link->list);
1520 LIST_DELETE(&link_ref->list);
1521 free(link_ref);
1522 }
William Lallemandc756bbd2020-05-13 17:23:59 +02001523 free(inst);
1524 }
1525
1526 crtlist_free_filters(entry->filters);
1527 ssl_sock_free_ssl_conf(entry->ssl_conf);
1528 free(entry->ssl_conf);
1529 free(entry);
1530
1531 HA_SPIN_UNLOCK(CKCH_LOCK, &ckch_lock);
1532 err = memprintf(&err, "Entry '%s' deleted in crtlist '%s'!\n", cert_path, crtlist_path);
1533 return cli_dynmsg(appctx, LOG_NOTICE, err);
1534
1535error:
1536 HA_SPIN_UNLOCK(CKCH_LOCK, &ckch_lock);
1537 err = memprintf(&err, "Can't delete the entry: %s\n", err ? err : "");
1538 return cli_dynerr(appctx, err);
1539}
1540
1541
William Lallemandee8530c2020-06-23 18:19:42 +02001542/* unlink and free all crt-list and crt-list entries */
1543void crtlist_deinit()
1544{
1545 struct eb_node *node, *next;
1546 struct crtlist *crtlist;
1547
1548 node = eb_first(&crtlists_tree);
1549 while (node) {
1550 next = eb_next(node);
1551 crtlist = ebmb_entry(node, struct crtlist, node);
1552 crtlist_free(crtlist);
1553 node = next;
1554 }
1555}
1556
William Lallemandc756bbd2020-05-13 17:23:59 +02001557
1558/* register cli keywords */
1559static struct cli_kw_list cli_kws = {{ },{
Willy Tarreaub205bfd2021-05-07 11:38:37 +02001560 { { "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 },
1561 { { "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 },
1562 { { "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 +02001563 { { NULL }, NULL, NULL, NULL } }
1564};
1565
1566INITCALL1(STG_REGISTER, cli_register_kw, &cli_kws);
1567