blob: 9ea5ea0e123d9135fbaf8f9fd845d238b2aed663 [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 Lallemandb6ae2aa2023-05-05 00:05:46 +020080#if defined(SSL_CTX_set1_client_sigalgs_list)
81 ha_free(&conf->client_sigalgs);
82#endif
William Lallemand6e9556b2020-05-12 17:52:44 +020083 }
84}
85
William Lallemand82f2d2f2020-09-10 19:06:43 +020086/*
87 * Allocate and copy a ssl_bind_conf structure
88 */
89struct ssl_bind_conf *crtlist_dup_ssl_conf(struct ssl_bind_conf *src)
90{
91 struct ssl_bind_conf *dst;
92
93 if (!src)
94 return NULL;
95
96 dst = calloc(1, sizeof(*dst));
97 if (!dst)
98 return NULL;
99
100#if defined(OPENSSL_NPN_NEGOTIATED) && !defined(OPENSSL_NO_NEXTPROTONEG)
101 if (src->npn_str) {
102 dst->npn_str = strdup(src->npn_str);
103 if (!dst->npn_str)
104 goto error;
105 }
106#endif
107#ifdef TLSEXT_TYPE_application_layer_protocol_negotiation
108 if (src->alpn_str) {
109 dst->alpn_str = strdup(src->alpn_str);
110 if (!dst->alpn_str)
111 goto error;
112 }
113#endif
114 if (src->ca_file) {
115 dst->ca_file = strdup(src->ca_file);
116 if (!dst->ca_file)
117 goto error;
118 }
119 if (src->ca_verify_file) {
120 dst->ca_verify_file = strdup(src->ca_verify_file);
121 if (!dst->ca_verify_file)
122 goto error;
123 }
124 if (src->crl_file) {
125 dst->crl_file = strdup(src->crl_file);
126 if (!dst->crl_file)
127 goto error;
128 }
129 if (src->ciphers) {
130 dst->ciphers = strdup(src->ciphers);
131 if (!dst->ciphers)
132 goto error;
133 }
Ilya Shipitsinf34ed0b2020-11-21 14:37:34 +0500134#ifdef HAVE_SSL_CTX_SET_CIPHERSUITES
William Lallemand82f2d2f2020-09-10 19:06:43 +0200135 if (src->ciphersuites) {
136 dst->ciphersuites = strdup(src->ciphersuites);
137 if (!dst->ciphersuites)
138 goto error;
139 }
140#endif
141 if (src->curves) {
142 dst->curves = strdup(src->curves);
143 if (!dst->curves)
144 goto error;
145 }
146 if (src->ecdhe) {
147 dst->ecdhe = strdup(src->ecdhe);
148 if (!dst->ecdhe)
149 goto error;
150 }
Remi Tricot-Le Breton6549f532023-03-14 17:22:24 +0100151
152 dst->ssl_methods_cfg.flags = src->ssl_methods_cfg.flags;
153 dst->ssl_methods_cfg.min = src->ssl_methods_cfg.min;
154 dst->ssl_methods_cfg.max = src->ssl_methods_cfg.max;
155
156 dst->ssl_methods.flags = src->ssl_methods.flags;
157 dst->ssl_methods.min = src->ssl_methods.min;
158 dst->ssl_methods.max = src->ssl_methods.max;
159
William Lallemand1d3c8222023-05-04 15:33:55 +0200160#if defined(SSL_CTX_set1_sigalgs_list)
161 if (src->sigalgs) {
162 dst->sigalgs = strdup(src->sigalgs);
163 if (!dst->sigalgs)
164 goto error;
165 }
166#endif
William Lallemandb6ae2aa2023-05-05 00:05:46 +0200167#if defined(SSL_CTX_set1_client_sigalgs_list)
168 if (src->client_sigalgs) {
169 dst->client_sigalgs = strdup(src->client_sigalgs);
170 if (!dst->client_sigalgs)
171 goto error;
172 }
173#endif
William Lallemand82f2d2f2020-09-10 19:06:43 +0200174 return dst;
175
176error:
177 ssl_sock_free_ssl_conf(dst);
178 free(dst);
179
180 return NULL;
181}
William Lallemand6e9556b2020-05-12 17:52:44 +0200182
183/* free sni filters */
184void crtlist_free_filters(char **args)
185{
186 int i;
187
188 if (!args)
189 return;
190
191 for (i = 0; args[i]; i++)
192 free(args[i]);
193
194 free(args);
195}
196
197/* Alloc and duplicate a char ** array */
198char **crtlist_dup_filters(char **args, int fcount)
199{
200 char **dst;
201 int i;
202
203 if (fcount == 0)
204 return NULL;
205
206 dst = calloc(fcount + 1, sizeof(*dst));
207 if (!dst)
208 return NULL;
209
210 for (i = 0; i < fcount; i++) {
211 dst[i] = strdup(args[i]);
212 if (!dst[i])
213 goto error;
214 }
215 return dst;
216
217error:
218 crtlist_free_filters(dst);
219 return NULL;
220}
221
222/*
223 * Detach and free a crtlist_entry.
224 * Free the filters, the ssl_conf and call ckch_inst_free() for each ckch_inst
225 */
226void crtlist_entry_free(struct crtlist_entry *entry)
227{
228 struct ckch_inst *inst, *inst_s;
229
230 if (entry == NULL)
231 return;
232
233 ebpt_delete(&entry->node);
Willy Tarreau2b718102021-04-21 07:32:39 +0200234 LIST_DELETE(&entry->by_crtlist);
235 LIST_DELETE(&entry->by_ckch_store);
William Lallemand6e9556b2020-05-12 17:52:44 +0200236 crtlist_free_filters(entry->filters);
237 ssl_sock_free_ssl_conf(entry->ssl_conf);
238 free(entry->ssl_conf);
239 list_for_each_entry_safe(inst, inst_s, &entry->ckch_inst, by_crtlist_entry) {
240 ckch_inst_free(inst);
241 }
242 free(entry);
243}
William Lallemand5622c452020-09-10 19:08:49 +0200244/*
245 * Duplicate a crt_list entry and its content (ssl_conf, filters/fcount)
246 * Return a pointer to the new entry
247 */
248struct crtlist_entry *crtlist_entry_dup(struct crtlist_entry *src)
249{
250 struct crtlist_entry *entry;
251
252 if (src == NULL)
253 return NULL;
254
255 entry = crtlist_entry_new();
256 if (entry == NULL)
257 return NULL;
258
259 if (src->filters) {
260 entry->filters = crtlist_dup_filters(src->filters, src->fcount);
261 if (!entry->filters)
262 goto error;
263 }
264 entry->fcount = src->fcount;
265 if (src->ssl_conf) {
266 entry->ssl_conf = crtlist_dup_ssl_conf(src->ssl_conf);
267 if (!entry->ssl_conf)
268 goto error;
269 }
270 entry->crtlist = src->crtlist;
271
272 return entry;
273
274error:
275
276 crtlist_free_filters(entry->filters);
277 ssl_sock_free_ssl_conf(entry->ssl_conf);
278 free(entry->ssl_conf);
279 free(entry);
280
281 return NULL;
282}
William Lallemand6e9556b2020-05-12 17:52:44 +0200283
284/*
285 * Allocate and initialize a crtlist_entry
286 */
287struct crtlist_entry *crtlist_entry_new()
288{
289 struct crtlist_entry *entry;
290
291 entry = calloc(1, sizeof(*entry));
292 if (entry == NULL)
293 return NULL;
294
295 LIST_INIT(&entry->ckch_inst);
296
Willy Tarreau2b718102021-04-21 07:32:39 +0200297 /* initialize the nodes so we can LIST_DELETE in any cases */
William Lallemand6e9556b2020-05-12 17:52:44 +0200298 LIST_INIT(&entry->by_crtlist);
299 LIST_INIT(&entry->by_ckch_store);
300
301 return entry;
302}
303
304/* Free a crtlist, from the crt_entry to the content of the ssl_conf */
305void crtlist_free(struct crtlist *crtlist)
306{
307 struct crtlist_entry *entry, *s_entry;
William Lallemand6a3168a2020-06-23 11:43:35 +0200308 struct bind_conf_list *bind_conf_node;
William Lallemand6e9556b2020-05-12 17:52:44 +0200309
310 if (crtlist == NULL)
311 return;
312
William Lallemand6a3168a2020-06-23 11:43:35 +0200313 bind_conf_node = crtlist->bind_conf;
314 while (bind_conf_node) {
315 struct bind_conf_list *next = bind_conf_node->next;
316 free(bind_conf_node);
317 bind_conf_node = next;
318 }
319
William Lallemand6e9556b2020-05-12 17:52:44 +0200320 list_for_each_entry_safe(entry, s_entry, &crtlist->ord_entries, by_crtlist) {
321 crtlist_entry_free(entry);
322 }
323 ebmb_delete(&crtlist->node);
324 free(crtlist);
325}
326
327/* Alloc and initialize a struct crtlist
328 * <filename> is the key of the ebmb_node
329 * <unique> initialize the list of entries to be unique (1) or not (0)
330 */
331struct crtlist *crtlist_new(const char *filename, int unique)
332{
333 struct crtlist *newlist;
334
335 newlist = calloc(1, sizeof(*newlist) + strlen(filename) + 1);
336 if (newlist == NULL)
337 return NULL;
338
339 memcpy(newlist->node.key, filename, strlen(filename) + 1);
340 if (unique)
341 newlist->entries = EB_ROOT_UNIQUE;
342 else
343 newlist->entries = EB_ROOT;
344
345 LIST_INIT(&newlist->ord_entries);
346
347 return newlist;
348}
349
350/*
351 * Read a single crt-list line. /!\ alter the <line> string.
352 * Fill <crt_path> and <crtlist_entry>
353 * <crtlist_entry> must be alloc and free by the caller
354 * <crtlist_entry->ssl_conf> is alloc by the function
355 * <crtlist_entry->filters> is alloc by the function
356 * <crt_path> is a ptr in <line>
357 * Return an error code
358 */
Remi Tricot-Le Bretonfb00f312021-03-23 16:41:53 +0100359int 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 +0200360{
361 int cfgerr = 0;
362 int arg, newarg, cur_arg, i, ssl_b = 0, ssl_e = 0;
363 char *end;
364 char *args[MAX_CRT_ARGS + 1];
365 struct ssl_bind_conf *ssl_conf = NULL;
366
367 if (!line || !crt_path || !entry)
368 return ERR_ALERT | ERR_FATAL;
369
370 end = line + strlen(line);
371 if (end-line >= CRT_LINESIZE-1 && *(end-1) != '\n') {
372 /* Check if we reached the limit and the last char is not \n.
373 * Watch out for the last line without the terminating '\n'!
374 */
Tim Duesterhus6d07fae2020-09-29 18:00:27 +0200375 memprintf(err, "parsing [%s:%d]: line too long, limit is %d characters",
376 file, linenum, CRT_LINESIZE-1);
William Lallemand6e9556b2020-05-12 17:52:44 +0200377 cfgerr |= ERR_ALERT | ERR_FATAL;
378 goto error;
379 }
380 arg = 0;
381 newarg = 1;
382 while (*line) {
383 if (isspace((unsigned char)*line)) {
384 newarg = 1;
385 *line = 0;
386 } else if (*line == '[') {
387 if (ssl_b) {
Tim Duesterhus6d07fae2020-09-29 18:00:27 +0200388 memprintf(err, "parsing [%s:%d]: too many '['", file, linenum);
William Lallemand6e9556b2020-05-12 17:52:44 +0200389 cfgerr |= ERR_ALERT | ERR_FATAL;
390 goto error;
391 }
392 if (!arg) {
Tim Duesterhus6d07fae2020-09-29 18:00:27 +0200393 memprintf(err, "parsing [%s:%d]: file must start with a cert", file, linenum);
William Lallemand6e9556b2020-05-12 17:52:44 +0200394 cfgerr |= ERR_ALERT | ERR_FATAL;
395 goto error;
396 }
397 ssl_b = arg;
398 newarg = 1;
399 *line = 0;
400 } else if (*line == ']') {
401 if (ssl_e) {
Tim Duesterhus6d07fae2020-09-29 18:00:27 +0200402 memprintf(err, "parsing [%s:%d]: too many ']'", file, linenum);
William Lallemand6e9556b2020-05-12 17:52:44 +0200403 cfgerr |= ERR_ALERT | ERR_FATAL;
404 goto error;
405 }
406 if (!ssl_b) {
Tim Duesterhus6d07fae2020-09-29 18:00:27 +0200407 memprintf(err, "parsing [%s:%d]: missing '['", file, linenum);
William Lallemand6e9556b2020-05-12 17:52:44 +0200408 cfgerr |= ERR_ALERT | ERR_FATAL;
409 goto error;
410 }
411 ssl_e = arg;
412 newarg = 1;
413 *line = 0;
414 } else if (newarg) {
415 if (arg == MAX_CRT_ARGS) {
Tim Duesterhus6d07fae2020-09-29 18:00:27 +0200416 memprintf(err, "parsing [%s:%d]: too many args ", file, linenum);
William Lallemand6e9556b2020-05-12 17:52:44 +0200417 cfgerr |= ERR_ALERT | ERR_FATAL;
418 goto error;
419 }
420 newarg = 0;
421 args[arg++] = line;
422 }
423 line++;
424 }
425 args[arg++] = line;
426
427 /* empty line */
428 if (!*args[0]) {
429 cfgerr |= ERR_NONE;
430 goto error;
431 }
432
433 *crt_path = args[0];
434
435 if (ssl_b) {
William Lallemandd85227f2023-02-07 17:06:35 +0100436 if (ssl_b > 1) {
437 memprintf(err, "parsing [%s:%d]: malformated line, filters can't be between filename and options!", file, linenum);
438 cfgerr |= ERR_WARN;
439 }
440
William Lallemand6e9556b2020-05-12 17:52:44 +0200441 ssl_conf = calloc(1, sizeof *ssl_conf);
442 if (!ssl_conf) {
443 memprintf(err, "not enough memory!");
444 cfgerr |= ERR_ALERT | ERR_FATAL;
445 goto error;
446 }
447 }
Remi Tricot-Le Bretonfb00f312021-03-23 16:41:53 +0100448
William Lallemand6e9556b2020-05-12 17:52:44 +0200449 cur_arg = ssl_b ? ssl_b : 1;
450 while (cur_arg < ssl_e) {
451 newarg = 0;
William Lallemandaf678062023-02-13 10:58:13 +0100452 for (i = 0; ssl_crtlist_kws[i].kw != NULL; i++) {
453 if (strcmp(ssl_crtlist_kws[i].kw, args[cur_arg]) == 0) {
William Lallemand6e9556b2020-05-12 17:52:44 +0200454 newarg = 1;
William Lallemandaf678062023-02-13 10:58:13 +0100455 cfgerr |= ssl_crtlist_kws[i].parse(args, cur_arg, NULL, ssl_conf, from_cli, err);
456 if (cur_arg + 1 + ssl_crtlist_kws[i].skip > ssl_e) {
Tim Duesterhus6d07fae2020-09-29 18:00:27 +0200457 memprintf(err, "parsing [%s:%d]: ssl args out of '[]' for %s",
458 file, linenum, args[cur_arg]);
William Lallemand6e9556b2020-05-12 17:52:44 +0200459 cfgerr |= ERR_ALERT | ERR_FATAL;
460 goto error;
461 }
William Lallemandaf678062023-02-13 10:58:13 +0100462 cur_arg += 1 + ssl_crtlist_kws[i].skip;
William Lallemand6e9556b2020-05-12 17:52:44 +0200463 break;
464 }
465 }
466 if (!cfgerr && !newarg) {
Tim Duesterhus6d07fae2020-09-29 18:00:27 +0200467 memprintf(err, "parsing [%s:%d]: unknown ssl keyword %s",
468 file, linenum, args[cur_arg]);
William Lallemand6e9556b2020-05-12 17:52:44 +0200469 cfgerr |= ERR_ALERT | ERR_FATAL;
470 goto error;
471 }
472 }
473 entry->linenum = linenum;
474 entry->ssl_conf = ssl_conf;
475 entry->filters = crtlist_dup_filters(&args[cur_arg], arg - cur_arg - 1);
476 entry->fcount = arg - cur_arg - 1;
477
478 return cfgerr;
479
480error:
481 crtlist_free_filters(entry->filters);
482 entry->filters = NULL;
483 ssl_sock_free_ssl_conf(entry->ssl_conf);
Willy Tarreau61cfdf42021-02-20 10:46:51 +0100484 ha_free(&entry->ssl_conf);
William Lallemand6e9556b2020-05-12 17:52:44 +0200485 return cfgerr;
486}
487
488
489
490/* This function parse a crt-list file and store it in a struct crtlist, each line is a crtlist_entry structure
491 * Fill the <crtlist> argument with a pointer to a new crtlist struct
492 *
493 * This function tries to open and store certificate files.
494 */
495int crtlist_parse_file(char *file, struct bind_conf *bind_conf, struct proxy *curproxy, struct crtlist **crtlist, char **err)
496{
497 struct crtlist *newlist;
498 struct crtlist_entry *entry = NULL;
499 char thisline[CRT_LINESIZE];
William Lallemand6e9556b2020-05-12 17:52:44 +0200500 FILE *f;
501 struct stat buf;
502 int linenum = 0;
503 int cfgerr = 0;
Tim Duesterhusb9f6acc2020-09-29 18:00:28 +0200504 int missing_lf = -1;
William Lallemand6e9556b2020-05-12 17:52:44 +0200505
506 if ((f = fopen(file, "r")) == NULL) {
507 memprintf(err, "cannot open file '%s' : %s", file, strerror(errno));
508 return ERR_ALERT | ERR_FATAL;
509 }
510
511 newlist = crtlist_new(file, 0);
512 if (newlist == NULL) {
513 memprintf(err, "Not enough memory!");
514 cfgerr |= ERR_ALERT | ERR_FATAL;
515 goto error;
516 }
517
518 while (fgets(thisline, sizeof(thisline), f) != NULL) {
519 char *end;
520 char *line = thisline;
521 char *crt_path;
William Lallemand86c2dd62020-11-20 14:23:38 +0100522 char path[MAXPATHLEN+1];
William Lallemand6e9556b2020-05-12 17:52:44 +0200523 struct ckch_store *ckchs;
William Lallemand77e1c6f2020-11-20 18:26:09 +0100524 int found = 0;
William Lallemand6e9556b2020-05-12 17:52:44 +0200525
Tim Duesterhusb9f6acc2020-09-29 18:00:28 +0200526 if (missing_lf != -1) {
527 memprintf(err, "parsing [%s:%d]: Stray NUL character at position %d.\n",
528 file, linenum, (missing_lf + 1));
529 cfgerr |= ERR_ALERT | ERR_FATAL;
530 missing_lf = -1;
531 break;
532 }
533
William Lallemand6e9556b2020-05-12 17:52:44 +0200534 linenum++;
535 end = line + strlen(line);
536 if (end-line == sizeof(thisline)-1 && *(end-1) != '\n') {
537 /* Check if we reached the limit and the last char is not \n.
538 * Watch out for the last line without the terminating '\n'!
539 */
Tim Duesterhus6d07fae2020-09-29 18:00:27 +0200540 memprintf(err, "parsing [%s:%d]: line too long, limit is %d characters",
541 file, linenum, (int)sizeof(thisline)-1);
William Lallemand6e9556b2020-05-12 17:52:44 +0200542 cfgerr |= ERR_ALERT | ERR_FATAL;
543 break;
544 }
545
546 if (*line == '#' || *line == '\n' || *line == '\r')
547 continue;
548
Tim Duesterhusb9f6acc2020-09-29 18:00:28 +0200549 if (end > line && *(end-1) == '\n') {
550 /* kill trailing LF */
551 *(end - 1) = 0;
552 }
553 else {
554 /* mark this line as truncated */
555 missing_lf = end - line;
556 }
557
William Lallemand6e9556b2020-05-12 17:52:44 +0200558 entry = crtlist_entry_new();
559 if (entry == NULL) {
560 memprintf(err, "Not enough memory!");
561 cfgerr |= ERR_ALERT | ERR_FATAL;
562 goto error;
563 }
Tim Duesterhusb9f6acc2020-09-29 18:00:28 +0200564
Remi Tricot-Le Bretonfb00f312021-03-23 16:41:53 +0100565 cfgerr |= crtlist_parse_line(thisline, &crt_path, entry, file, linenum, 0, err);
William Lallemand20b0fed2020-09-28 15:45:16 +0200566 if (cfgerr & ERR_CODE)
William Lallemand6e9556b2020-05-12 17:52:44 +0200567 goto error;
568
569 /* empty line */
570 if (!crt_path || !*crt_path) {
571 crtlist_entry_free(entry);
572 entry = NULL;
573 continue;
574 }
575
576 if (*crt_path != '/' && global_ssl.crt_base) {
Willy Tarreau393e42a2022-05-09 10:31:28 +0200577 if ((strlen(global_ssl.crt_base) + 1 + strlen(crt_path)) > sizeof(path) ||
Willy Tarreau63fc9002022-05-09 21:14:04 +0200578 snprintf(path, sizeof(path), "%s/%s", global_ssl.crt_base, crt_path) > sizeof(path)) {
Tim Duesterhus6d07fae2020-09-29 18:00:27 +0200579 memprintf(err, "parsing [%s:%d]: '%s' : path too long",
580 file, linenum, crt_path);
William Lallemand6e9556b2020-05-12 17:52:44 +0200581 cfgerr |= ERR_ALERT | ERR_FATAL;
582 goto error;
583 }
William Lallemand6e9556b2020-05-12 17:52:44 +0200584 crt_path = path;
585 }
586
587 /* Look for a ckch_store or create one */
588 ckchs = ckchs_lookup(crt_path);
589 if (ckchs == NULL) {
William Lallemand47da8212020-09-10 19:13:27 +0200590 if (stat(crt_path, &buf) == 0) {
William Lallemand77e1c6f2020-11-20 18:26:09 +0100591 found++;
William Lallemand47da8212020-09-10 19:13:27 +0200592
William Lallemandbd8e6ed2020-09-16 16:08:08 +0200593 ckchs = ckchs_load_cert_file(crt_path, err);
William Lallemand47da8212020-09-10 19:13:27 +0200594 if (ckchs == NULL) {
595 cfgerr |= ERR_ALERT | ERR_FATAL;
596 goto error;
597 }
598
599 entry->node.key = ckchs;
600 entry->crtlist = newlist;
Remi Tricot-Le Bretonfb2b9982022-12-20 11:11:11 +0100601 if (entry->ssl_conf)
602 ckchs->data->ocsp_update_mode = entry->ssl_conf->ocsp_update;
William Lallemand47da8212020-09-10 19:13:27 +0200603 ebpt_insert(&newlist->entries, &entry->node);
Willy Tarreau2b718102021-04-21 07:32:39 +0200604 LIST_APPEND(&newlist->ord_entries, &entry->by_crtlist);
605 LIST_APPEND(&ckchs->crtlist_entry, &entry->by_ckch_store);
William Lallemand6e9556b2020-05-12 17:52:44 +0200606
William Lallemand73404572020-11-20 18:23:40 +0100607 } else if (global_ssl.extra_files & SSL_GF_BUNDLE) {
William Lallemand47da8212020-09-10 19:13:27 +0200608 /* If we didn't find the file, this could be a
William Lallemand51f784b2020-10-02 18:08:18 +0200609 bundle, since 2.3 we don't support multiple
610 certificate in the same OpenSSL store, so we
611 emulate it by loading each file separately. To
612 do so we need to duplicate the entry in the
613 crt-list because it becomes independent */
William Lallemand47da8212020-09-10 19:13:27 +0200614 char fp[MAXPATHLEN+1] = {0};
615 int n = 0;
616 struct crtlist_entry *entry_dup = entry; /* use the previous created entry */
617 for (n = 0; n < SSL_SOCK_NUM_KEYTYPES; n++) {
618 struct stat buf;
619 int ret;
620
William Lallemand86c2dd62020-11-20 14:23:38 +0100621 ret = snprintf(fp, sizeof(fp), "%s.%s", crt_path, SSL_SOCK_KEYTYPE_NAMES[n]);
William Lallemand47da8212020-09-10 19:13:27 +0200622 if (ret > sizeof(fp))
623 continue;
624
625 ckchs = ckchs_lookup(fp);
William Lallemand77e1c6f2020-11-20 18:26:09 +0100626 if (!ckchs) {
627 if (stat(fp, &buf) == 0) {
628 ckchs = ckchs_load_cert_file(fp, err);
629 if (!ckchs) {
William Lallemand47da8212020-09-10 19:13:27 +0200630 cfgerr |= ERR_ALERT | ERR_FATAL;
631 goto error;
632 }
William Lallemand77e1c6f2020-11-20 18:26:09 +0100633 } else {
634 continue; /* didn't find this extension, skip */
635 }
636 }
637 found++;
638 linenum++; /* we duplicate the line for this entry in the bundle */
639 if (!entry_dup) { /* if the entry was used, duplicate one */
640 linenum++;
641 entry_dup = crtlist_entry_dup(entry);
642 if (!entry_dup) {
643 cfgerr |= ERR_ALERT | ERR_FATAL;
644 goto error;
William Lallemand47da8212020-09-10 19:13:27 +0200645 }
William Lallemand77e1c6f2020-11-20 18:26:09 +0100646 entry_dup->linenum = linenum;
647 }
William Lallemand47da8212020-09-10 19:13:27 +0200648
William Lallemand77e1c6f2020-11-20 18:26:09 +0100649 entry_dup->node.key = ckchs;
650 entry_dup->crtlist = newlist;
William Lallemanda14686d2023-02-07 18:38:05 +0100651
652 cfgerr |= ocsp_update_check_cfg_consistency(ckchs, entry, crt_path, err);
653 if (cfgerr & ERR_FATAL)
654 goto error;
655
Remi Tricot-Le Bretonfc92b8b2023-01-09 12:02:46 +0100656 if (entry->ssl_conf)
657 ckchs->data->ocsp_update_mode = entry->ssl_conf->ocsp_update;
William Lallemand77e1c6f2020-11-20 18:26:09 +0100658 ebpt_insert(&newlist->entries, &entry_dup->node);
Willy Tarreau2b718102021-04-21 07:32:39 +0200659 LIST_APPEND(&newlist->ord_entries, &entry_dup->by_crtlist);
660 LIST_APPEND(&ckchs->crtlist_entry, &entry_dup->by_ckch_store);
William Lallemand47da8212020-09-10 19:13:27 +0200661
William Lallemand77e1c6f2020-11-20 18:26:09 +0100662 entry_dup = NULL; /* the entry was used, we need a new one next round */
William Lallemand47da8212020-09-10 19:13:27 +0200663 }
William Lallemandb7fdfdf2020-12-04 15:45:02 +0100664#if HA_OPENSSL_VERSION_NUMBER < 0x10101000L
665 if (found) {
666 memprintf(err, "%sCan't load '%s'. Loading a multi certificates bundle requires OpenSSL >= 1.1.1\n",
667 err && *err ? *err : "", crt_path);
668 cfgerr |= ERR_ALERT | ERR_FATAL;
669 }
670#endif
William Lallemand47da8212020-09-10 19:13:27 +0200671 }
William Lallemand77e1c6f2020-11-20 18:26:09 +0100672 if (!found) {
673 memprintf(err, "%sunable to stat SSL certificate from file '%s' : %s.\n",
674 err && *err ? *err : "", crt_path, strerror(errno));
675 cfgerr |= ERR_ALERT | ERR_FATAL;
676 }
677
William Lallemand50c03aa2020-11-06 16:24:07 +0100678 } else {
679 entry->node.key = ckchs;
680 entry->crtlist = newlist;
William Lallemanda14686d2023-02-07 18:38:05 +0100681
682 cfgerr |= ocsp_update_check_cfg_consistency(ckchs, entry, crt_path, err);
683 if (cfgerr & ERR_FATAL)
684 goto error;
685
Remi Tricot-Le Bretonfc92b8b2023-01-09 12:02:46 +0100686 if (entry->ssl_conf)
687 ckchs->data->ocsp_update_mode = entry->ssl_conf->ocsp_update;
William Lallemand50c03aa2020-11-06 16:24:07 +0100688 ebpt_insert(&newlist->entries, &entry->node);
Willy Tarreau2b718102021-04-21 07:32:39 +0200689 LIST_APPEND(&newlist->ord_entries, &entry->by_crtlist);
690 LIST_APPEND(&ckchs->crtlist_entry, &entry->by_ckch_store);
William Lallemand77e1c6f2020-11-20 18:26:09 +0100691 found++;
William Lallemand47da8212020-09-10 19:13:27 +0200692 }
William Lallemand6e9556b2020-05-12 17:52:44 +0200693 entry = NULL;
694 }
Tim Duesterhusb9f6acc2020-09-29 18:00:28 +0200695
696 if (missing_lf != -1) {
697 memprintf(err, "parsing [%s:%d]: Missing LF on last line, file might have been truncated at position %d.\n",
698 file, linenum, (missing_lf + 1));
699 cfgerr |= ERR_ALERT | ERR_FATAL;
700 }
701
William Lallemand6e9556b2020-05-12 17:52:44 +0200702 if (cfgerr & ERR_CODE)
703 goto error;
704
705 newlist->linecount = linenum;
706
707 fclose(f);
708 *crtlist = newlist;
709
710 return cfgerr;
711error:
712 crtlist_entry_free(entry);
713
714 fclose(f);
715 crtlist_free(newlist);
716 return cfgerr;
717}
718
719/* This function reads a directory and stores it in a struct crtlist, each file is a crtlist_entry structure
720 * Fill the <crtlist> argument with a pointer to a new crtlist struct
721 *
722 * This function tries to open and store certificate files.
723 */
724int crtlist_load_cert_dir(char *path, struct bind_conf *bind_conf, struct crtlist **crtlist, char **err)
725{
726 struct crtlist *dir;
727 struct dirent **de_list;
728 int i, n;
729 struct stat buf;
730 char *end;
731 char fp[MAXPATHLEN+1];
732 int cfgerr = 0;
733 struct ckch_store *ckchs;
William Lallemand6e9556b2020-05-12 17:52:44 +0200734
735 dir = crtlist_new(path, 1);
736 if (dir == NULL) {
737 memprintf(err, "not enough memory");
738 return ERR_ALERT | ERR_FATAL;
739 }
740
741 n = scandir(path, &de_list, 0, alphasort);
742 if (n < 0) {
743 memprintf(err, "%sunable to scan directory '%s' : %s.\n",
744 err && *err ? *err : "", path, strerror(errno));
745 cfgerr |= ERR_ALERT | ERR_FATAL;
746 }
747 else {
748 for (i = 0; i < n; i++) {
749 struct crtlist_entry *entry;
750 struct dirent *de = de_list[i];
751
752 end = strrchr(de->d_name, '.');
William Lallemand589570d2022-05-09 10:30:51 +0200753 if (end && (de->d_name[0] == '.' ||
754 strcmp(end, ".issuer") == 0 || strcmp(end, ".ocsp") == 0 ||
755 strcmp(end, ".sctl") == 0 || strcmp(end, ".key") == 0))
William Lallemand6e9556b2020-05-12 17:52:44 +0200756 goto ignore_entry;
757
758 snprintf(fp, sizeof(fp), "%s/%s", path, de->d_name);
759 if (stat(fp, &buf) != 0) {
760 memprintf(err, "%sunable to stat SSL certificate from file '%s' : %s.\n",
761 err && *err ? *err : "", fp, strerror(errno));
762 cfgerr |= ERR_ALERT | ERR_FATAL;
763 goto ignore_entry;
764 }
765 if (!S_ISREG(buf.st_mode))
766 goto ignore_entry;
767
768 entry = crtlist_entry_new();
769 if (entry == NULL) {
770 memprintf(err, "not enough memory '%s'", fp);
771 cfgerr |= ERR_ALERT | ERR_FATAL;
772 goto ignore_entry;
773 }
774
William Lallemand6e9556b2020-05-12 17:52:44 +0200775 ckchs = ckchs_lookup(fp);
776 if (ckchs == NULL)
William Lallemandbd8e6ed2020-09-16 16:08:08 +0200777 ckchs = ckchs_load_cert_file(fp, err);
William Lallemand6e9556b2020-05-12 17:52:44 +0200778 if (ckchs == NULL) {
779 free(de);
780 free(entry);
781 cfgerr |= ERR_ALERT | ERR_FATAL;
782 goto end;
783 }
784 entry->node.key = ckchs;
785 entry->crtlist = dir;
Willy Tarreau2b718102021-04-21 07:32:39 +0200786 LIST_APPEND(&ckchs->crtlist_entry, &entry->by_ckch_store);
787 LIST_APPEND(&dir->ord_entries, &entry->by_crtlist);
William Lallemand6e9556b2020-05-12 17:52:44 +0200788 ebpt_insert(&dir->entries, &entry->node);
789
790ignore_entry:
791 free(de);
792 }
793end:
794 free(de_list);
795 }
796
797 if (cfgerr & ERR_CODE) {
798 /* free the dir and entries on error */
799 crtlist_free(dir);
800 } else {
801 *crtlist = dir;
802 }
803 return cfgerr;
804
805}
806
William Lallemandc756bbd2020-05-13 17:23:59 +0200807/*
808 * Take an ssl_bind_conf structure and append the configuration line used to
809 * create it in the buffer
810 */
811static void dump_crtlist_sslconf(struct buffer *buf, const struct ssl_bind_conf *conf)
812{
813 int space = 0;
814
815 if (conf == NULL)
816 return;
817
818 chunk_appendf(buf, " [");
819#ifdef OPENSSL_NPN_NEGOTIATED
820 if (conf->npn_str) {
821 int len = conf->npn_len;
822 char *ptr = conf->npn_str;
823 int comma = 0;
824
825 if (space) chunk_appendf(buf, " ");
826 chunk_appendf(buf, "npn ");
827 while (len) {
828 unsigned short size;
829
830 size = *ptr;
831 ptr++;
832 if (comma)
833 chunk_memcat(buf, ",", 1);
834 chunk_memcat(buf, ptr, size);
835 ptr += size;
836 len -= size + 1;
837 comma = 1;
838 }
839 chunk_memcat(buf, "", 1); /* finish with a \0 */
840 space++;
841 }
842#endif
843#ifdef TLSEXT_TYPE_application_layer_protocol_negotiation
844 if (conf->alpn_str) {
845 int len = conf->alpn_len;
846 char *ptr = conf->alpn_str;
847 int comma = 0;
848
849 if (space) chunk_appendf(buf, " ");
Willy Tarreaude85de62023-04-19 09:07:47 +0200850 if (len)
851 chunk_appendf(buf, "alpn ");
852 else
853 chunk_appendf(buf, "no-alpn");
William Lallemandc756bbd2020-05-13 17:23:59 +0200854 while (len) {
855 unsigned short size;
856
857 size = *ptr;
858 ptr++;
859 if (comma)
860 chunk_memcat(buf, ",", 1);
861 chunk_memcat(buf, ptr, size);
862 ptr += size;
863 len -= size + 1;
864 comma = 1;
865 }
866 chunk_memcat(buf, "", 1); /* finish with a \0 */
867 space++;
868 }
869#endif
870 /* verify */
871 {
872 if (conf->verify == SSL_SOCK_VERIFY_NONE) {
873 if (space) chunk_appendf(buf, " ");
874 chunk_appendf(buf, "verify none");
875 space++;
876 } else if (conf->verify == SSL_SOCK_VERIFY_OPTIONAL) {
877 if (space) chunk_appendf(buf, " ");
878 chunk_appendf(buf, "verify optional");
879 space++;
880 } else if (conf->verify == SSL_SOCK_VERIFY_REQUIRED) {
881 if (space) chunk_appendf(buf, " ");
882 chunk_appendf(buf, "verify required");
883 space++;
884 }
885 }
886
887 if (conf->no_ca_names) {
888 if (space) chunk_appendf(buf, " ");
889 chunk_appendf(buf, "no-ca-names");
890 space++;
891 }
892
893 if (conf->early_data) {
894 if (space) chunk_appendf(buf, " ");
895 chunk_appendf(buf, "allow-0rtt");
896 space++;
897 }
898 if (conf->ca_file) {
899 if (space) chunk_appendf(buf, " ");
900 chunk_appendf(buf, "ca-file %s", conf->ca_file);
901 space++;
902 }
903 if (conf->crl_file) {
904 if (space) chunk_appendf(buf, " ");
905 chunk_appendf(buf, "crl-file %s", conf->crl_file);
906 space++;
907 }
908 if (conf->ciphers) {
909 if (space) chunk_appendf(buf, " ");
910 chunk_appendf(buf, "ciphers %s", conf->ciphers);
911 space++;
912 }
Ilya Shipitsinf34ed0b2020-11-21 14:37:34 +0500913#ifdef HAVE_SSL_CTX_SET_CIPHERSUITES
William Lallemandc756bbd2020-05-13 17:23:59 +0200914 if (conf->ciphersuites) {
915 if (space) chunk_appendf(buf, " ");
916 chunk_appendf(buf, "ciphersuites %s", conf->ciphersuites);
917 space++;
918 }
919#endif
920 if (conf->curves) {
921 if (space) chunk_appendf(buf, " ");
922 chunk_appendf(buf, "curves %s", conf->curves);
923 space++;
924 }
925 if (conf->ecdhe) {
926 if (space) chunk_appendf(buf, " ");
927 chunk_appendf(buf, "ecdhe %s", conf->ecdhe);
928 space++;
929 }
930
931 /* the crt-lists only support ssl-min-ver and ssl-max-ver */
William Lallemand8177ad92020-05-20 16:49:02 +0200932 if (conf->ssl_methods_cfg.min) {
William Lallemandc756bbd2020-05-13 17:23:59 +0200933 if (space) chunk_appendf(buf, " ");
William Lallemand8177ad92020-05-20 16:49:02 +0200934 chunk_appendf(buf, "ssl-min-ver %s", methodVersions[conf->ssl_methods_cfg.min].name);
William Lallemandc756bbd2020-05-13 17:23:59 +0200935 space++;
936 }
937
William Lallemand8177ad92020-05-20 16:49:02 +0200938 if (conf->ssl_methods_cfg.max) {
William Lallemandc756bbd2020-05-13 17:23:59 +0200939 if (space) chunk_appendf(buf, " ");
William Lallemand8177ad92020-05-20 16:49:02 +0200940 chunk_appendf(buf, "ssl-max-ver %s", methodVersions[conf->ssl_methods_cfg.max].name);
William Lallemandc756bbd2020-05-13 17:23:59 +0200941 space++;
942 }
943
Remi Tricot-Le Bretonca0c84a2023-03-02 15:49:52 +0100944 if (conf->ocsp_update != SSL_SOCK_OCSP_UPDATE_DFLT) {
945 if (space) chunk_appendf(buf, " ");
946 chunk_appendf(buf, "ocsp-update %s",
947 conf->ocsp_update == SSL_SOCK_OCSP_UPDATE_OFF ? "off" : "on");
948 space++;
949 }
950
William Lallemandc756bbd2020-05-13 17:23:59 +0200951 chunk_appendf(buf, "]");
952
953 return;
954}
955
956/* dump a list of filters */
957static void dump_crtlist_filters(struct buffer *buf, struct crtlist_entry *entry)
958{
959 int i;
960
961 if (!entry->fcount)
962 return;
963
964 for (i = 0; i < entry->fcount; i++) {
965 chunk_appendf(buf, " %s", entry->filters[i]);
966 }
967 return;
968}
969
970/************************** CLI functions ****************************/
971
972
Willy Tarreaua2fcca02022-05-05 11:53:23 +0200973/* CLI IO handler for '(show|dump) ssl crt-list'.
974 * It uses show_crtlist_ctx for the context.
975 */
William Lallemandc756bbd2020-05-13 17:23:59 +0200976static int cli_io_handler_dump_crtlist(struct appctx *appctx)
977{
Willy Tarreaua2fcca02022-05-05 11:53:23 +0200978 struct show_crtlist_ctx *ctx = appctx->svcctx;
William Lallemandc756bbd2020-05-13 17:23:59 +0200979 struct buffer *trash = alloc_trash_chunk();
William Lallemandc756bbd2020-05-13 17:23:59 +0200980 struct ebmb_node *lnode;
981
982 if (trash == NULL)
983 return 1;
984
985 /* dump the list of crt-lists */
Willy Tarreaua2fcca02022-05-05 11:53:23 +0200986 lnode = ctx->crtlist_node;
William Lallemandc756bbd2020-05-13 17:23:59 +0200987 if (lnode == NULL)
988 lnode = ebmb_first(&crtlists_tree);
989 while (lnode) {
990 chunk_appendf(trash, "%s\n", lnode->key);
Willy Tarreaud0a06d52022-05-18 15:07:19 +0200991 if (applet_putchk(appctx, trash) == -1)
William Lallemandc756bbd2020-05-13 17:23:59 +0200992 goto yield;
William Lallemandc756bbd2020-05-13 17:23:59 +0200993 lnode = ebmb_next(lnode);
994 }
995 free_trash_chunk(trash);
996 return 1;
997yield:
Willy Tarreaua2fcca02022-05-05 11:53:23 +0200998 ctx->crtlist_node = lnode;
William Lallemandc756bbd2020-05-13 17:23:59 +0200999 free_trash_chunk(trash);
1000 return 0;
1001}
1002
1003/* CLI IO handler for '(show|dump) ssl crt-list <filename>' */
1004static int cli_io_handler_dump_crtlist_entries(struct appctx *appctx)
1005{
Willy Tarreaua2fcca02022-05-05 11:53:23 +02001006 struct show_crtlist_ctx *ctx = appctx->svcctx;
William Lallemandc756bbd2020-05-13 17:23:59 +02001007 struct buffer *trash = alloc_trash_chunk();
1008 struct crtlist *crtlist;
William Lallemandc756bbd2020-05-13 17:23:59 +02001009 struct crtlist_entry *entry;
1010
1011 if (trash == NULL)
1012 return 1;
1013
Willy Tarreaua2fcca02022-05-05 11:53:23 +02001014 crtlist = ebmb_entry(ctx->crtlist_node, struct crtlist, node);
William Lallemandc756bbd2020-05-13 17:23:59 +02001015
Willy Tarreaua2fcca02022-05-05 11:53:23 +02001016 entry = ctx->entry;
William Lallemandc756bbd2020-05-13 17:23:59 +02001017 if (entry == NULL) {
1018 entry = LIST_ELEM((crtlist->ord_entries).n, typeof(entry), by_crtlist);
1019 chunk_appendf(trash, "# %s\n", crtlist->node.key);
Willy Tarreaud0a06d52022-05-18 15:07:19 +02001020 if (applet_putchk(appctx, trash) == -1)
William Lallemandc756bbd2020-05-13 17:23:59 +02001021 goto yield;
William Lallemandc756bbd2020-05-13 17:23:59 +02001022 }
1023
1024 list_for_each_entry_from(entry, &crtlist->ord_entries, by_crtlist) {
1025 struct ckch_store *store;
1026 const char *filename;
1027
1028 store = entry->node.key;
1029 filename = store->path;
1030 chunk_appendf(trash, "%s", filename);
Willy Tarreaua2fcca02022-05-05 11:53:23 +02001031 if (ctx->mode == 's') /* show */
William Lallemandc756bbd2020-05-13 17:23:59 +02001032 chunk_appendf(trash, ":%d", entry->linenum);
1033 dump_crtlist_sslconf(trash, entry->ssl_conf);
1034 dump_crtlist_filters(trash, entry);
1035 chunk_appendf(trash, "\n");
1036
Willy Tarreaud0a06d52022-05-18 15:07:19 +02001037 if (applet_putchk(appctx, trash) == -1)
William Lallemandc756bbd2020-05-13 17:23:59 +02001038 goto yield;
William Lallemandc756bbd2020-05-13 17:23:59 +02001039 }
1040 free_trash_chunk(trash);
1041 return 1;
1042yield:
Willy Tarreaua2fcca02022-05-05 11:53:23 +02001043 ctx->entry = entry;
William Lallemandc756bbd2020-05-13 17:23:59 +02001044 free_trash_chunk(trash);
1045 return 0;
1046}
1047
1048/* CLI argument parser for '(show|dump) ssl crt-list' */
1049static int cli_parse_dump_crtlist(char **args, char *payload, struct appctx *appctx, void *private)
1050{
Willy Tarreaua2fcca02022-05-05 11:53:23 +02001051 struct show_crtlist_ctx *ctx = applet_reserve_svcctx(appctx, sizeof(*ctx));
William Lallemandc756bbd2020-05-13 17:23:59 +02001052 struct ebmb_node *lnode;
1053 char *filename = NULL;
1054 int mode;
William Lallemand99cc2182020-06-25 15:19:51 +02001055 char *end;
William Lallemandc756bbd2020-05-13 17:23:59 +02001056
1057 if (!cli_has_level(appctx, ACCESS_LVL_ADMIN))
1058 return 1;
1059
Tim Duesterhuse5ff1412021-01-02 22:31:53 +01001060 if (*args[3] && strcmp(args[3], "-n") == 0) {
William Lallemandc756bbd2020-05-13 17:23:59 +02001061 mode = 's';
1062 filename = args[4];
1063 } else {
1064 mode = 'd';
1065 filename = args[3];
1066 }
1067
1068 if (mode == 's' && !*args[4])
1069 return cli_err(appctx, "'show ssl crt-list -n' expects a filename or a directory\n");
1070
1071 if (filename && *filename) {
William Lallemand99cc2182020-06-25 15:19:51 +02001072
1073
1074 /* strip trailing slashes, including first one */
1075 for (end = filename + strlen(filename) - 1; end >= filename && *end == '/'; end--)
1076 *end = 0;
1077
William Lallemandc756bbd2020-05-13 17:23:59 +02001078 lnode = ebst_lookup(&crtlists_tree, filename);
1079 if (lnode == NULL)
1080 return cli_err(appctx, "didn't find the specified filename\n");
1081
Willy Tarreaua2fcca02022-05-05 11:53:23 +02001082 ctx->crtlist_node = lnode;
William Lallemandc756bbd2020-05-13 17:23:59 +02001083 appctx->io_handler = cli_io_handler_dump_crtlist_entries;
1084 }
Willy Tarreaua2fcca02022-05-05 11:53:23 +02001085 ctx->mode = mode;
William Lallemandc756bbd2020-05-13 17:23:59 +02001086
1087 return 0;
1088}
1089
1090/* release function of the "add ssl crt-list' command, free things and unlock
Willy Tarreau6b6c3632022-05-05 13:43:49 +02001091 * the spinlock. It uses the add_crtlist_ctx.
1092 */
William Lallemandc756bbd2020-05-13 17:23:59 +02001093static void cli_release_add_crtlist(struct appctx *appctx)
1094{
Willy Tarreau6b6c3632022-05-05 13:43:49 +02001095 struct add_crtlist_ctx *ctx = appctx->svcctx;
1096 struct crtlist_entry *entry = ctx->entry;
William Lallemandc756bbd2020-05-13 17:23:59 +02001097
Christopher Fauletc642d7c2022-06-01 16:31:09 +02001098 if (entry) {
William Lallemandc756bbd2020-05-13 17:23:59 +02001099 struct ckch_inst *inst, *inst_s;
Christopher Fauletc642d7c2022-06-01 16:31:09 +02001100
William Lallemandc756bbd2020-05-13 17:23:59 +02001101 /* upon error free the ckch_inst and everything inside */
1102 ebpt_delete(&entry->node);
Willy Tarreau2b718102021-04-21 07:32:39 +02001103 LIST_DELETE(&entry->by_crtlist);
1104 LIST_DELETE(&entry->by_ckch_store);
William Lallemandc756bbd2020-05-13 17:23:59 +02001105
1106 list_for_each_entry_safe(inst, inst_s, &entry->ckch_inst, by_ckchs) {
1107 ckch_inst_free(inst);
1108 }
1109 crtlist_free_filters(entry->filters);
1110 ssl_sock_free_ssl_conf(entry->ssl_conf);
1111 free(entry->ssl_conf);
1112 free(entry);
1113 }
William Lallemandc756bbd2020-05-13 17:23:59 +02001114 HA_SPIN_UNLOCK(CKCH_LOCK, &ckch_lock);
Christopher Fauletc642d7c2022-06-01 16:31:09 +02001115 ha_free(&ctx->err);
William Lallemandc756bbd2020-05-13 17:23:59 +02001116}
1117
1118
1119/* IO Handler for the "add ssl crt-list" command It adds a new entry in the
1120 * crt-list and generates the ckch_insts for each bind_conf that uses this crt-list
1121 *
1122 * The logic is the same as the "commit ssl cert" command but without the
1123 * freeing of the old structures, because there are none.
Willy Tarreau6b6c3632022-05-05 13:43:49 +02001124 *
1125 * It uses the add_crtlist_ctx for the context.
William Lallemandc756bbd2020-05-13 17:23:59 +02001126 */
1127static int cli_io_handler_add_crtlist(struct appctx *appctx)
1128{
Willy Tarreau6b6c3632022-05-05 13:43:49 +02001129 struct add_crtlist_ctx *ctx = appctx->svcctx;
William Lallemandc756bbd2020-05-13 17:23:59 +02001130 struct bind_conf_list *bind_conf_node;
Willy Tarreauc12b3212022-05-27 11:08:15 +02001131 struct stconn *sc = appctx_sc(appctx);
Willy Tarreau6b6c3632022-05-05 13:43:49 +02001132 struct crtlist *crtlist = ctx->crtlist;
1133 struct crtlist_entry *entry = ctx->entry;
William Lallemandc756bbd2020-05-13 17:23:59 +02001134 struct ckch_store *store = entry->node.key;
William Lallemandc756bbd2020-05-13 17:23:59 +02001135 struct ckch_inst *new_inst;
William Lallemandc756bbd2020-05-13 17:23:59 +02001136 int i = 0;
1137 int errcode = 0;
1138
William Lallemandc756bbd2020-05-13 17:23:59 +02001139 /* for each bind_conf which use the crt-list, a new ckch_inst must be
1140 * created.
1141 */
Christopher Faulet87633c32023-04-03 18:32:50 +02001142 /* FIXME: Don't watch the other side !*/
Christopher Faulet208c7122023-04-13 16:16:15 +02001143 if (unlikely(sc_opposite(sc)->flags & SC_FL_SHUT_DONE))
Christopher Fauletc642d7c2022-06-01 16:31:09 +02001144 goto end;
William Lallemandc756bbd2020-05-13 17:23:59 +02001145
Willy Tarreaufa11df52022-05-05 13:48:40 +02001146 switch (ctx->state) {
1147 case ADDCRT_ST_INIT:
Willy Tarreau1b948ef2022-05-05 13:50:46 +02001148 /* This state just print the update message */
Christopher Fauletc642d7c2022-06-01 16:31:09 +02001149 chunk_printf(&trash, "Inserting certificate '%s' in crt-list '%s'", store->path, crtlist->node.key);
1150 if (applet_putchk(appctx, &trash) == -1)
Willy Tarreau1b948ef2022-05-05 13:50:46 +02001151 goto yield;
Willy Tarreaufa11df52022-05-05 13:48:40 +02001152 ctx->state = ADDCRT_ST_GEN;
Willy Tarreauaef84482022-11-14 07:03:16 +01001153 __fallthrough;
Willy Tarreaufa11df52022-05-05 13:48:40 +02001154 case ADDCRT_ST_GEN:
Willy Tarreau6b6c3632022-05-05 13:43:49 +02001155 bind_conf_node = ctx->bind_conf_node; /* get the previous ptr from the yield */
Willy Tarreau1b948ef2022-05-05 13:50:46 +02001156 if (bind_conf_node == NULL)
1157 bind_conf_node = crtlist->bind_conf;
1158 for (; bind_conf_node; bind_conf_node = bind_conf_node->next) {
1159 struct bind_conf *bind_conf = bind_conf_node->bind_conf;
1160 struct sni_ctx *sni;
1161
Christopher Fauletc642d7c2022-06-01 16:31:09 +02001162 ctx->bind_conf_node = bind_conf_node;
1163
Willy Tarreau1b948ef2022-05-05 13:50:46 +02001164 /* yield every 10 generations */
1165 if (i > 10) {
Christopher Fauletc642d7c2022-06-01 16:31:09 +02001166 applet_have_more_data(appctx); /* let's come back later */
Willy Tarreau1b948ef2022-05-05 13:50:46 +02001167 goto yield;
1168 }
William Lallemandc756bbd2020-05-13 17:23:59 +02001169
Christopher Fauletc642d7c2022-06-01 16:31:09 +02001170 /* display one dot for each new instance */
1171 if (applet_putstr(appctx, ".") == -1)
1172 goto yield;
1173
Willy Tarreau1b948ef2022-05-05 13:50:46 +02001174 /* we don't support multi-cert bundles, only simple ones */
Christopher Fauletc642d7c2022-06-01 16:31:09 +02001175 ctx->err = NULL;
1176 errcode |= ckch_inst_new_load_store(store->path, store, bind_conf, entry->ssl_conf, entry->filters, entry->fcount, &new_inst, &ctx->err);
1177 if (errcode & ERR_CODE) {
1178 ctx->state = ADDCRT_ST_ERROR;
Willy Tarreau1b948ef2022-05-05 13:50:46 +02001179 goto error;
Christopher Fauletc642d7c2022-06-01 16:31:09 +02001180 }
William Lallemandc756bbd2020-05-13 17:23:59 +02001181
Willy Tarreau1b948ef2022-05-05 13:50:46 +02001182 /* we need to initialize the SSL_CTX generated */
1183 /* this iterate on the newly generated SNIs in the new instance to prepare their SSL_CTX */
1184 list_for_each_entry(sni, &new_inst->sni_ctx, by_ckch_inst) {
1185 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 +02001186 ctx->err = NULL;
1187 errcode |= ssl_sock_prep_ctx_and_inst(bind_conf, new_inst->ssl_conf, sni->ctx, sni->ckch_inst, &ctx->err);
1188 if (errcode & ERR_CODE) {
1189 ctx->state = ADDCRT_ST_ERROR;
William Lallemandc756bbd2020-05-13 17:23:59 +02001190 goto error;
Christopher Fauletc642d7c2022-06-01 16:31:09 +02001191 }
William Lallemandc756bbd2020-05-13 17:23:59 +02001192 }
Willy Tarreau1b948ef2022-05-05 13:50:46 +02001193 }
Christopher Fauletc642d7c2022-06-01 16:31:09 +02001194
Willy Tarreau1b948ef2022-05-05 13:50:46 +02001195 i++;
1196 LIST_APPEND(&store->ckch_inst, &new_inst->by_ckchs);
1197 LIST_APPEND(&entry->ckch_inst, &new_inst->by_crtlist_entry);
1198 new_inst->crtlist_entry = entry;
William Lallemandc756bbd2020-05-13 17:23:59 +02001199 }
Willy Tarreaufa11df52022-05-05 13:48:40 +02001200 ctx->state = ADDCRT_ST_INSERT;
Willy Tarreauaef84482022-11-14 07:03:16 +01001201 __fallthrough;
Willy Tarreaufa11df52022-05-05 13:48:40 +02001202 case ADDCRT_ST_INSERT:
William Lallemandcb6c5f42022-06-20 16:51:53 +02001203 /* the insertion is called for every instance of the store, not
1204 * only the one we generated.
1205 * But the ssl_sock_load_cert_sni() skip the sni already
1206 * inserted. Not every instance has a bind_conf, it could be
1207 * the store of a server so we should be careful */
1208
Willy Tarreau1b948ef2022-05-05 13:50:46 +02001209 list_for_each_entry(new_inst, &store->ckch_inst, by_ckchs) {
William Lallemandcb6c5f42022-06-20 16:51:53 +02001210 if (!new_inst->bind_conf) /* this is a server instance */
1211 continue;
Willy Tarreau1b948ef2022-05-05 13:50:46 +02001212 HA_RWLOCK_WRLOCK(SNI_LOCK, &new_inst->bind_conf->sni_lock);
1213 ssl_sock_load_cert_sni(new_inst, new_inst->bind_conf);
1214 HA_RWLOCK_WRUNLOCK(SNI_LOCK, &new_inst->bind_conf->sni_lock);
1215 }
1216 entry->linenum = ++crtlist->linecount;
Christopher Fauletc642d7c2022-06-01 16:31:09 +02001217 ctx->entry = NULL;
1218 ctx->state = ADDCRT_ST_SUCCESS;
Willy Tarreauaef84482022-11-14 07:03:16 +01001219 __fallthrough;
Christopher Fauletc642d7c2022-06-01 16:31:09 +02001220 case ADDCRT_ST_SUCCESS:
1221 chunk_reset(&trash);
1222 chunk_appendf(&trash, "\n");
1223 if (ctx->err)
1224 chunk_appendf(&trash, "%s", ctx->err);
1225 chunk_appendf(&trash, "Success!\n");
1226 if (applet_putchk(appctx, &trash) == -1)
1227 goto yield;
1228 ctx->state = ADDCRT_ST_FIN;
1229 break;
1230
1231 case ADDCRT_ST_ERROR:
1232 error:
1233 chunk_printf(&trash, "\n%sFailed!\n", ctx->err);
1234 if (applet_putchk(appctx, &trash) == -1)
1235 goto yield;
1236 break;
1237
Willy Tarreaufa11df52022-05-05 13:48:40 +02001238 default:
1239 break;
William Lallemandc756bbd2020-05-13 17:23:59 +02001240 }
1241
Christopher Fauletc642d7c2022-06-01 16:31:09 +02001242end:
William Lallemandc756bbd2020-05-13 17:23:59 +02001243 /* success: call the release function and don't come back */
1244 return 1;
1245yield:
William Lallemandc756bbd2020-05-13 17:23:59 +02001246 return 0; /* should come back */
William Lallemandc756bbd2020-05-13 17:23:59 +02001247}
1248
1249
1250/*
1251 * Parse a "add ssl crt-list <crt-list> <certfile>" line.
Willy Tarreau6b6c3632022-05-05 13:43:49 +02001252 * Filters and option must be passed through payload.
1253 * It sets a struct add_crtlist_ctx.
William Lallemandc756bbd2020-05-13 17:23:59 +02001254 */
1255static int cli_parse_add_crtlist(char **args, char *payload, struct appctx *appctx, void *private)
1256{
Willy Tarreau6b6c3632022-05-05 13:43:49 +02001257 struct add_crtlist_ctx *ctx = applet_reserve_svcctx(appctx, sizeof(*ctx));
William Lallemandc756bbd2020-05-13 17:23:59 +02001258 int cfgerr = 0;
1259 struct ckch_store *store;
1260 char *err = NULL;
1261 char path[MAXPATHLEN+1];
1262 char *crtlist_path;
1263 char *cert_path = NULL;
1264 struct ebmb_node *eb;
1265 struct ebpt_node *inserted;
1266 struct crtlist *crtlist;
1267 struct crtlist_entry *entry = NULL;
William Lallemand99cc2182020-06-25 15:19:51 +02001268 char *end;
William Lallemandc756bbd2020-05-13 17:23:59 +02001269
1270 if (!cli_has_level(appctx, ACCESS_LVL_ADMIN))
1271 return 1;
1272
1273 if (!*args[3] || (!payload && !*args[4]))
1274 return cli_err(appctx, "'add ssl crtlist' expects a filename and a certificate name\n");
1275
1276 crtlist_path = args[3];
1277
William Lallemand99cc2182020-06-25 15:19:51 +02001278 /* strip trailing slashes, including first one */
1279 for (end = crtlist_path + strlen(crtlist_path) - 1; end >= crtlist_path && *end == '/'; end--)
1280 *end = 0;
1281
William Lallemandc756bbd2020-05-13 17:23:59 +02001282 if (HA_SPIN_TRYLOCK(CKCH_LOCK, &ckch_lock))
1283 return cli_err(appctx, "Operations on certificates are currently locked!\n");
1284
1285 eb = ebst_lookup(&crtlists_tree, crtlist_path);
1286 if (!eb) {
1287 memprintf(&err, "crt-list '%s' does not exist!", crtlist_path);
1288 goto error;
1289 }
1290 crtlist = ebmb_entry(eb, struct crtlist, node);
1291
1292 entry = crtlist_entry_new();
1293 if (entry == NULL) {
1294 memprintf(&err, "Not enough memory!");
1295 goto error;
1296 }
1297
1298 if (payload) {
1299 char *lf;
1300
1301 lf = strrchr(payload, '\n');
1302 if (lf) {
1303 memprintf(&err, "only one line of payload is supported!");
1304 goto error;
1305 }
1306 /* cert_path is filled here */
Remi Tricot-Le Bretonfb00f312021-03-23 16:41:53 +01001307 cfgerr |= crtlist_parse_line(payload, &cert_path, entry, "CLI", 1, 1, &err);
William Lallemandc756bbd2020-05-13 17:23:59 +02001308 if (cfgerr & ERR_CODE)
1309 goto error;
1310 } else {
1311 cert_path = args[4];
1312 }
1313
1314 if (!cert_path) {
1315 memprintf(&err, "'add ssl crtlist' should contain the certificate name in the payload");
1316 cfgerr |= ERR_ALERT | ERR_FATAL;
1317 goto error;
1318 }
1319
1320 if (eb_gettag(crtlist->entries.b[EB_RGHT])) {
1321 char *slash;
1322
1323 slash = strrchr(cert_path, '/');
1324 if (!slash) {
1325 memprintf(&err, "'%s' is a directory, certificate path '%s' must contain the directory path", (char *)crtlist->node.key, cert_path);
1326 goto error;
1327 }
1328 /* temporary replace / by 0 to do an strcmp */
1329 *slash = '\0';
1330 if (strcmp(cert_path, (char*)crtlist->node.key) != 0) {
1331 *slash = '/';
1332 memprintf(&err, "'%s' is a directory, certificate path '%s' must contain the directory path", (char *)crtlist->node.key, cert_path);
1333 goto error;
1334 }
1335 *slash = '/';
1336 }
1337
1338 if (*cert_path != '/' && global_ssl.crt_base) {
Willy Tarreau393e42a2022-05-09 10:31:28 +02001339 if ((strlen(global_ssl.crt_base) + 1 + strlen(cert_path)) > sizeof(path) ||
1340 snprintf(path, sizeof(path), "%s/%s", global_ssl.crt_base, cert_path) > sizeof(path)) {
William Lallemandc756bbd2020-05-13 17:23:59 +02001341 memprintf(&err, "'%s' : path too long", cert_path);
1342 cfgerr |= ERR_ALERT | ERR_FATAL;
1343 goto error;
1344 }
William Lallemandc756bbd2020-05-13 17:23:59 +02001345 cert_path = path;
1346 }
1347
1348 store = ckchs_lookup(cert_path);
1349 if (store == NULL) {
1350 memprintf(&err, "certificate '%s' does not exist!", cert_path);
1351 goto error;
1352 }
William Lallemand52ddd992022-11-22 11:51:53 +01001353 if (store->data == NULL || store->data->cert == NULL) {
William Lallemandc756bbd2020-05-13 17:23:59 +02001354 memprintf(&err, "certificate '%s' is empty!", cert_path);
1355 goto error;
1356 }
1357
Remi Tricot-Le Breton86d1e0b2023-03-02 15:49:53 +01001358 /* No need to check 'ocsp-update' inconsistency on a store that is not
1359 * used yet (it was just added through the CLI for instance).
1360 */
1361 if (!LIST_ISEMPTY(&store->ckch_inst) &&
1362 ocsp_update_check_cfg_consistency(store, entry, cert_path, &err))
1363 goto error;
1364
1365 if (entry->ssl_conf)
1366 store->data->ocsp_update_mode = entry->ssl_conf->ocsp_update;
1367
William Lallemandc756bbd2020-05-13 17:23:59 +02001368 /* check if it's possible to insert this new crtlist_entry */
1369 entry->node.key = store;
1370 inserted = ebpt_insert(&crtlist->entries, &entry->node);
1371 if (inserted != &entry->node) {
1372 memprintf(&err, "file already exists in this directory!");
1373 goto error;
1374 }
1375
1376 /* this is supposed to be a directory (EB_ROOT_UNIQUE), so no ssl_conf are allowed */
1377 if ((entry->ssl_conf || entry->filters) && eb_gettag(crtlist->entries.b[EB_RGHT])) {
1378 memprintf(&err, "this is a directory, SSL configuration and filters are not allowed");
1379 goto error;
1380 }
1381
Willy Tarreau2b718102021-04-21 07:32:39 +02001382 LIST_APPEND(&crtlist->ord_entries, &entry->by_crtlist);
William Lallemandc756bbd2020-05-13 17:23:59 +02001383 entry->crtlist = crtlist;
Willy Tarreau2b718102021-04-21 07:32:39 +02001384 LIST_APPEND(&store->crtlist_entry, &entry->by_ckch_store);
William Lallemandc756bbd2020-05-13 17:23:59 +02001385
Willy Tarreaufa11df52022-05-05 13:48:40 +02001386 ctx->state = ADDCRT_ST_INIT;
Willy Tarreau6b6c3632022-05-05 13:43:49 +02001387 ctx->crtlist = crtlist;
1388 ctx->entry = entry;
William Lallemandc756bbd2020-05-13 17:23:59 +02001389
1390 /* unlock is done in the release handler */
1391 return 0;
1392
1393error:
1394 crtlist_entry_free(entry);
1395 HA_SPIN_UNLOCK(CKCH_LOCK, &ckch_lock);
1396 err = memprintf(&err, "Can't edit the crt-list: %s\n", err ? err : "");
1397 return cli_dynerr(appctx, err);
1398}
1399
1400/* Parse a "del ssl crt-list <crt-list> <certfile>" line. */
1401static int cli_parse_del_crtlist(char **args, char *payload, struct appctx *appctx, void *private)
1402{
1403 struct ckch_store *store;
1404 char *err = NULL;
1405 char *crtlist_path, *cert_path;
1406 struct ebmb_node *ebmb;
1407 struct ebpt_node *ebpt;
1408 struct crtlist *crtlist;
1409 struct crtlist_entry *entry = NULL;
1410 struct ckch_inst *inst, *inst_s;
1411 int linenum = 0;
1412 char *colons;
William Lallemand99cc2182020-06-25 15:19:51 +02001413 char *end;
Remi Tricot-Le Bretonbc2c3862021-03-26 10:47:50 +01001414 int error_message_dumped = 0;
William Lallemandc756bbd2020-05-13 17:23:59 +02001415
1416 if (!cli_has_level(appctx, ACCESS_LVL_ADMIN))
1417 return 1;
1418
1419 if (!*args[3] || !*args[4])
1420 return cli_err(appctx, "'del ssl crtlist' expects a filename and a certificate name\n");
1421
1422 if (HA_SPIN_TRYLOCK(CKCH_LOCK, &ckch_lock))
1423 return cli_err(appctx, "Can't delete!\nOperations on certificates are currently locked!\n");
1424
1425 crtlist_path = args[3];
1426 cert_path = args[4];
1427
1428 colons = strchr(cert_path, ':');
1429 if (colons) {
1430 char *endptr;
1431
1432 linenum = strtol(colons + 1, &endptr, 10);
1433 if (colons + 1 == endptr || *endptr != '\0') {
1434 memprintf(&err, "wrong line number after colons in '%s'!", cert_path);
1435 goto error;
1436 }
1437 *colons = '\0';
1438 }
William Lallemand99cc2182020-06-25 15:19:51 +02001439
1440 /* strip trailing slashes, including first one */
1441 for (end = crtlist_path + strlen(crtlist_path) - 1; end >= crtlist_path && *end == '/'; end--)
1442 *end = 0;
1443
William Lallemandc756bbd2020-05-13 17:23:59 +02001444 /* look for crtlist */
1445 ebmb = ebst_lookup(&crtlists_tree, crtlist_path);
1446 if (!ebmb) {
1447 memprintf(&err, "crt-list '%s' does not exist!", crtlist_path);
1448 goto error;
1449 }
1450 crtlist = ebmb_entry(ebmb, struct crtlist, node);
1451
1452 /* look for store */
1453 store = ckchs_lookup(cert_path);
1454 if (store == NULL) {
1455 memprintf(&err, "certificate '%s' does not exist!", cert_path);
1456 goto error;
1457 }
William Lallemand52ddd992022-11-22 11:51:53 +01001458 if (store->data == NULL || store->data->cert == NULL) {
William Lallemandc756bbd2020-05-13 17:23:59 +02001459 memprintf(&err, "certificate '%s' is empty!", cert_path);
1460 goto error;
1461 }
1462
1463 ebpt = ebpt_lookup(&crtlist->entries, store);
1464 if (!ebpt) {
1465 memprintf(&err, "certificate '%s' can't be found in crt-list '%s'!", cert_path, crtlist_path);
1466 goto error;
1467 }
1468
1469 /* list the line number of entries for errors in err, and select the right ebpt */
1470 for (; ebpt; ebpt = ebpt_next_dup(ebpt)) {
1471 struct crtlist_entry *tmp;
1472
1473 tmp = ebpt_entry(ebpt, struct crtlist_entry, node);
1474 memprintf(&err, "%s%s%d", err ? err : "", err ? ", " : "", tmp->linenum);
1475
1476 /* select the entry we wanted */
1477 if (linenum == 0 || tmp->linenum == linenum) {
1478 if (!entry)
1479 entry = tmp;
1480 }
1481 }
1482
1483 /* we didn't found the specified entry */
1484 if (!entry) {
1485 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);
1486 goto error;
1487 }
1488
1489 /* we didn't specified a line number but there were several entries */
1490 if (linenum == 0 && ebpt_next_dup(&entry->node)) {
1491 memprintf(&err, "found the certificate '%s' in several entries, please specify a line number preceded by colons (%s)!", cert_path, err ? err : NULL);
1492 goto error;
1493 }
1494
Remi Tricot-Le Bretonbc2c3862021-03-26 10:47:50 +01001495 /* Iterate over all the instances in order to see if any of them is a
1496 * default instance. If this is the case, the entry won't be suppressed. */
1497 list_for_each_entry_safe(inst, inst_s, &entry->ckch_inst, by_crtlist_entry) {
1498 if (inst->is_default && !inst->bind_conf->strict_sni) {
1499 if (!error_message_dumped) {
1500 memprintf(&err, "certificate '%s' cannot be deleted, it is used as default certificate by the following frontends:\n", cert_path);
1501 error_message_dumped = 1;
1502 }
1503 memprintf(&err, "%s\t- %s:%d\n", err, inst->bind_conf->file, inst->bind_conf->line);
1504 }
1505 }
1506 if (error_message_dumped)
1507 goto error;
1508
William Lallemandc756bbd2020-05-13 17:23:59 +02001509 /* upon error free the ckch_inst and everything inside */
1510
1511 ebpt_delete(&entry->node);
Willy Tarreau2b718102021-04-21 07:32:39 +02001512 LIST_DELETE(&entry->by_crtlist);
1513 LIST_DELETE(&entry->by_ckch_store);
William Lallemandc756bbd2020-05-13 17:23:59 +02001514
1515 list_for_each_entry_safe(inst, inst_s, &entry->ckch_inst, by_crtlist_entry) {
1516 struct sni_ctx *sni, *sni_s;
Remi Tricot-Le Breton4458b972021-02-19 17:41:55 +01001517 struct ckch_inst_link_ref *link_ref, *link_ref_s;
William Lallemandc756bbd2020-05-13 17:23:59 +02001518
1519 HA_RWLOCK_WRLOCK(SNI_LOCK, &inst->bind_conf->sni_lock);
1520 list_for_each_entry_safe(sni, sni_s, &inst->sni_ctx, by_ckch_inst) {
1521 ebmb_delete(&sni->name);
Willy Tarreau2b718102021-04-21 07:32:39 +02001522 LIST_DELETE(&sni->by_ckch_inst);
William Lallemandc756bbd2020-05-13 17:23:59 +02001523 SSL_CTX_free(sni->ctx);
1524 free(sni);
1525 }
1526 HA_RWLOCK_WRUNLOCK(SNI_LOCK, &inst->bind_conf->sni_lock);
Willy Tarreau2b718102021-04-21 07:32:39 +02001527 LIST_DELETE(&inst->by_ckchs);
Remi Tricot-Le Breton4458b972021-02-19 17:41:55 +01001528 list_for_each_entry_safe(link_ref, link_ref_s, &inst->cafile_link_refs, list) {
1529 LIST_DELETE(&link_ref->link->list);
1530 LIST_DELETE(&link_ref->list);
1531 free(link_ref);
1532 }
William Lallemandc756bbd2020-05-13 17:23:59 +02001533 free(inst);
1534 }
1535
1536 crtlist_free_filters(entry->filters);
1537 ssl_sock_free_ssl_conf(entry->ssl_conf);
1538 free(entry->ssl_conf);
1539 free(entry);
1540
1541 HA_SPIN_UNLOCK(CKCH_LOCK, &ckch_lock);
1542 err = memprintf(&err, "Entry '%s' deleted in crtlist '%s'!\n", cert_path, crtlist_path);
1543 return cli_dynmsg(appctx, LOG_NOTICE, err);
1544
1545error:
1546 HA_SPIN_UNLOCK(CKCH_LOCK, &ckch_lock);
1547 err = memprintf(&err, "Can't delete the entry: %s\n", err ? err : "");
1548 return cli_dynerr(appctx, err);
1549}
1550
1551
William Lallemandee8530c2020-06-23 18:19:42 +02001552/* unlink and free all crt-list and crt-list entries */
1553void crtlist_deinit()
1554{
1555 struct eb_node *node, *next;
1556 struct crtlist *crtlist;
1557
1558 node = eb_first(&crtlists_tree);
1559 while (node) {
1560 next = eb_next(node);
1561 crtlist = ebmb_entry(node, struct crtlist, node);
1562 crtlist_free(crtlist);
1563 node = next;
1564 }
1565}
1566
William Lallemandc756bbd2020-05-13 17:23:59 +02001567
1568/* register cli keywords */
1569static struct cli_kw_list cli_kws = {{ },{
Willy Tarreaub205bfd2021-05-07 11:38:37 +02001570 { { "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 },
1571 { { "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 },
1572 { { "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 +02001573 { { NULL }, NULL, NULL, NULL } }
1574};
1575
1576INITCALL1(STG_REGISTER, cli_register_kw, &cli_kws);
1577