blob: 741e6aed6e91009ffb7526a328dea3b93487162a [file] [log] [blame]
William Lallemand6e9556b2020-05-12 17:52:44 +02001/*
2 *
3 * Copyright (C) 2020 HAProxy Technologies, William Lallemand <wlallemand@haproxy.com>
4 *
5 * This program is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU General Public License
7 * as published by the Free Software Foundation; either version
8 * 2 of the License, or (at your option) any later version.
9 *
10 */
Willy Tarreaub2551052020-06-09 09:07:15 +020011#include <sys/stat.h>
12#include <sys/types.h>
William Lallemand6e9556b2020-05-12 17:52:44 +020013
Willy Tarreaub2551052020-06-09 09:07:15 +020014#include <dirent.h>
William Lallemand212e9932020-05-18 08:33:09 +020015#include <errno.h>
William Lallemand6e9556b2020-05-12 17:52:44 +020016#include <stdlib.h>
17#include <string.h>
Willy Tarreauaeed4a82020-06-04 22:01:04 +020018#include <syslog.h>
Willy Tarreaub2551052020-06-09 09:07:15 +020019
20#include <import/ebpttree.h>
21#include <import/ebsttree.h>
William Lallemand6e9556b2020-05-12 17:52:44 +020022
Willy Tarreaua2fcca02022-05-05 11:53:23 +020023#include <haproxy/applet.h>
Willy Tarreauf1d32c42020-06-04 21:07:02 +020024#include <haproxy/channel.h>
Willy Tarreau83487a82020-06-04 20:19:54 +020025#include <haproxy/cli.h>
Willy Tarreau8d366972020-05-27 16:10:29 +020026#include <haproxy/errors.h>
Willy Tarreau5edca2f2022-05-27 09:25:10 +020027#include <haproxy/sc_strm.h>
Willy Tarreau47d7f902020-06-04 14:25:47 +020028#include <haproxy/ssl_ckch.h>
Willy Tarreau52d88722020-06-04 14:29:23 +020029#include <haproxy/ssl_crtlist.h>
William Lallemanda14686d2023-02-07 18:38:05 +010030#include <haproxy/ssl_ocsp.h>
Willy Tarreau209108d2020-06-04 20:30:20 +020031#include <haproxy/ssl_sock.h>
Willy Tarreaucb086c62022-05-27 09:47:12 +020032#include <haproxy/stconn.h>
Willy Tarreau48fbcae2020-06-03 18:09:46 +020033#include <haproxy/tools.h>
William Lallemand6e9556b2020-05-12 17:52:44 +020034
Willy Tarreaua2fcca02022-05-05 11:53:23 +020035/* CLI context for "show ssl crt-list" or "dump ssl crt-list" */
36struct show_crtlist_ctx {
37 struct ebmb_node *crtlist_node; /* ebmb_node for the current crtlist */
38 struct crtlist_entry *entry; /* current entry */
39 int mode; /* 'd' for dump, 's' for show */
40};
William Lallemand6e9556b2020-05-12 17:52:44 +020041
Willy Tarreau6b6c3632022-05-05 13:43:49 +020042/* CLI context for "add ssl crt-list" */
43struct add_crtlist_ctx {
44 struct crtlist *crtlist;
45 struct crtlist_entry *entry;
46 struct bind_conf_list *bind_conf_node;
Christopher Fauletc642d7c2022-06-01 16:31:09 +020047 char *err;
Willy Tarreaufa11df52022-05-05 13:48:40 +020048 enum {
49 ADDCRT_ST_INIT = 0,
50 ADDCRT_ST_GEN,
51 ADDCRT_ST_INSERT,
Christopher Fauletc642d7c2022-06-01 16:31:09 +020052 ADDCRT_ST_SUCCESS,
53 ADDCRT_ST_ERROR,
Willy Tarreaufa11df52022-05-05 13:48:40 +020054 ADDCRT_ST_FIN,
55 } state;
Willy Tarreau6b6c3632022-05-05 13:43:49 +020056};
57
William Lallemand6e9556b2020-05-12 17:52:44 +020058/* release ssl bind conf */
59void ssl_sock_free_ssl_conf(struct ssl_bind_conf *conf)
60{
61 if (conf) {
62#if defined(OPENSSL_NPN_NEGOTIATED) && !defined(OPENSSL_NO_NEXTPROTONEG)
Willy Tarreau61cfdf42021-02-20 10:46:51 +010063 ha_free(&conf->npn_str);
William Lallemand6e9556b2020-05-12 17:52:44 +020064#endif
65#ifdef TLSEXT_TYPE_application_layer_protocol_negotiation
Willy Tarreau61cfdf42021-02-20 10:46:51 +010066 ha_free(&conf->alpn_str);
William Lallemand6e9556b2020-05-12 17:52:44 +020067#endif
Willy Tarreau61cfdf42021-02-20 10:46:51 +010068 ha_free(&conf->ca_file);
69 ha_free(&conf->ca_verify_file);
70 ha_free(&conf->crl_file);
71 ha_free(&conf->ciphers);
Ilya Shipitsinf34ed0b2020-11-21 14:37:34 +050072#ifdef HAVE_SSL_CTX_SET_CIPHERSUITES
Willy Tarreau61cfdf42021-02-20 10:46:51 +010073 ha_free(&conf->ciphersuites);
William Lallemand6e9556b2020-05-12 17:52:44 +020074#endif
Willy Tarreau61cfdf42021-02-20 10:46:51 +010075 ha_free(&conf->curves);
76 ha_free(&conf->ecdhe);
William Lallemand6e9556b2020-05-12 17:52:44 +020077 }
78}
79
William Lallemand82f2d2f2020-09-10 19:06:43 +020080/*
81 * Allocate and copy a ssl_bind_conf structure
82 */
83struct ssl_bind_conf *crtlist_dup_ssl_conf(struct ssl_bind_conf *src)
84{
85 struct ssl_bind_conf *dst;
86
87 if (!src)
88 return NULL;
89
90 dst = calloc(1, sizeof(*dst));
91 if (!dst)
92 return NULL;
93
94#if defined(OPENSSL_NPN_NEGOTIATED) && !defined(OPENSSL_NO_NEXTPROTONEG)
95 if (src->npn_str) {
96 dst->npn_str = strdup(src->npn_str);
97 if (!dst->npn_str)
98 goto error;
99 }
100#endif
101#ifdef TLSEXT_TYPE_application_layer_protocol_negotiation
102 if (src->alpn_str) {
103 dst->alpn_str = strdup(src->alpn_str);
104 if (!dst->alpn_str)
105 goto error;
106 }
107#endif
108 if (src->ca_file) {
109 dst->ca_file = strdup(src->ca_file);
110 if (!dst->ca_file)
111 goto error;
112 }
113 if (src->ca_verify_file) {
114 dst->ca_verify_file = strdup(src->ca_verify_file);
115 if (!dst->ca_verify_file)
116 goto error;
117 }
118 if (src->crl_file) {
119 dst->crl_file = strdup(src->crl_file);
120 if (!dst->crl_file)
121 goto error;
122 }
123 if (src->ciphers) {
124 dst->ciphers = strdup(src->ciphers);
125 if (!dst->ciphers)
126 goto error;
127 }
Ilya Shipitsinf34ed0b2020-11-21 14:37:34 +0500128#ifdef HAVE_SSL_CTX_SET_CIPHERSUITES
William Lallemand82f2d2f2020-09-10 19:06:43 +0200129 if (src->ciphersuites) {
130 dst->ciphersuites = strdup(src->ciphersuites);
131 if (!dst->ciphersuites)
132 goto error;
133 }
134#endif
135 if (src->curves) {
136 dst->curves = strdup(src->curves);
137 if (!dst->curves)
138 goto error;
139 }
140 if (src->ecdhe) {
141 dst->ecdhe = strdup(src->ecdhe);
142 if (!dst->ecdhe)
143 goto error;
144 }
Remi Tricot-Le Breton6549f532023-03-14 17:22:24 +0100145
146 dst->ssl_methods_cfg.flags = src->ssl_methods_cfg.flags;
147 dst->ssl_methods_cfg.min = src->ssl_methods_cfg.min;
148 dst->ssl_methods_cfg.max = src->ssl_methods_cfg.max;
149
150 dst->ssl_methods.flags = src->ssl_methods.flags;
151 dst->ssl_methods.min = src->ssl_methods.min;
152 dst->ssl_methods.max = src->ssl_methods.max;
153
William Lallemand82f2d2f2020-09-10 19:06:43 +0200154 return dst;
155
156error:
157 ssl_sock_free_ssl_conf(dst);
158 free(dst);
159
160 return NULL;
161}
William Lallemand6e9556b2020-05-12 17:52:44 +0200162
163/* free sni filters */
164void crtlist_free_filters(char **args)
165{
166 int i;
167
168 if (!args)
169 return;
170
171 for (i = 0; args[i]; i++)
172 free(args[i]);
173
174 free(args);
175}
176
177/* Alloc and duplicate a char ** array */
178char **crtlist_dup_filters(char **args, int fcount)
179{
180 char **dst;
181 int i;
182
183 if (fcount == 0)
184 return NULL;
185
186 dst = calloc(fcount + 1, sizeof(*dst));
187 if (!dst)
188 return NULL;
189
190 for (i = 0; i < fcount; i++) {
191 dst[i] = strdup(args[i]);
192 if (!dst[i])
193 goto error;
194 }
195 return dst;
196
197error:
198 crtlist_free_filters(dst);
199 return NULL;
200}
201
202/*
203 * Detach and free a crtlist_entry.
204 * Free the filters, the ssl_conf and call ckch_inst_free() for each ckch_inst
205 */
206void crtlist_entry_free(struct crtlist_entry *entry)
207{
208 struct ckch_inst *inst, *inst_s;
209
210 if (entry == NULL)
211 return;
212
213 ebpt_delete(&entry->node);
Willy Tarreau2b718102021-04-21 07:32:39 +0200214 LIST_DELETE(&entry->by_crtlist);
215 LIST_DELETE(&entry->by_ckch_store);
William Lallemand6e9556b2020-05-12 17:52:44 +0200216 crtlist_free_filters(entry->filters);
217 ssl_sock_free_ssl_conf(entry->ssl_conf);
218 free(entry->ssl_conf);
219 list_for_each_entry_safe(inst, inst_s, &entry->ckch_inst, by_crtlist_entry) {
220 ckch_inst_free(inst);
221 }
222 free(entry);
223}
William Lallemand5622c452020-09-10 19:08:49 +0200224/*
225 * Duplicate a crt_list entry and its content (ssl_conf, filters/fcount)
226 * Return a pointer to the new entry
227 */
228struct crtlist_entry *crtlist_entry_dup(struct crtlist_entry *src)
229{
230 struct crtlist_entry *entry;
231
232 if (src == NULL)
233 return NULL;
234
235 entry = crtlist_entry_new();
236 if (entry == NULL)
237 return NULL;
238
239 if (src->filters) {
240 entry->filters = crtlist_dup_filters(src->filters, src->fcount);
241 if (!entry->filters)
242 goto error;
243 }
244 entry->fcount = src->fcount;
245 if (src->ssl_conf) {
246 entry->ssl_conf = crtlist_dup_ssl_conf(src->ssl_conf);
247 if (!entry->ssl_conf)
248 goto error;
249 }
250 entry->crtlist = src->crtlist;
251
252 return entry;
253
254error:
255
256 crtlist_free_filters(entry->filters);
257 ssl_sock_free_ssl_conf(entry->ssl_conf);
258 free(entry->ssl_conf);
259 free(entry);
260
261 return NULL;
262}
William Lallemand6e9556b2020-05-12 17:52:44 +0200263
264/*
265 * Allocate and initialize a crtlist_entry
266 */
267struct crtlist_entry *crtlist_entry_new()
268{
269 struct crtlist_entry *entry;
270
271 entry = calloc(1, sizeof(*entry));
272 if (entry == NULL)
273 return NULL;
274
275 LIST_INIT(&entry->ckch_inst);
276
Willy Tarreau2b718102021-04-21 07:32:39 +0200277 /* initialize the nodes so we can LIST_DELETE in any cases */
William Lallemand6e9556b2020-05-12 17:52:44 +0200278 LIST_INIT(&entry->by_crtlist);
279 LIST_INIT(&entry->by_ckch_store);
280
281 return entry;
282}
283
284/* Free a crtlist, from the crt_entry to the content of the ssl_conf */
285void crtlist_free(struct crtlist *crtlist)
286{
287 struct crtlist_entry *entry, *s_entry;
William Lallemand6a3168a2020-06-23 11:43:35 +0200288 struct bind_conf_list *bind_conf_node;
William Lallemand6e9556b2020-05-12 17:52:44 +0200289
290 if (crtlist == NULL)
291 return;
292
William Lallemand6a3168a2020-06-23 11:43:35 +0200293 bind_conf_node = crtlist->bind_conf;
294 while (bind_conf_node) {
295 struct bind_conf_list *next = bind_conf_node->next;
296 free(bind_conf_node);
297 bind_conf_node = next;
298 }
299
William Lallemand6e9556b2020-05-12 17:52:44 +0200300 list_for_each_entry_safe(entry, s_entry, &crtlist->ord_entries, by_crtlist) {
301 crtlist_entry_free(entry);
302 }
303 ebmb_delete(&crtlist->node);
304 free(crtlist);
305}
306
307/* Alloc and initialize a struct crtlist
308 * <filename> is the key of the ebmb_node
309 * <unique> initialize the list of entries to be unique (1) or not (0)
310 */
311struct crtlist *crtlist_new(const char *filename, int unique)
312{
313 struct crtlist *newlist;
314
315 newlist = calloc(1, sizeof(*newlist) + strlen(filename) + 1);
316 if (newlist == NULL)
317 return NULL;
318
319 memcpy(newlist->node.key, filename, strlen(filename) + 1);
320 if (unique)
321 newlist->entries = EB_ROOT_UNIQUE;
322 else
323 newlist->entries = EB_ROOT;
324
325 LIST_INIT(&newlist->ord_entries);
326
327 return newlist;
328}
329
330/*
331 * Read a single crt-list line. /!\ alter the <line> string.
332 * Fill <crt_path> and <crtlist_entry>
333 * <crtlist_entry> must be alloc and free by the caller
334 * <crtlist_entry->ssl_conf> is alloc by the function
335 * <crtlist_entry->filters> is alloc by the function
336 * <crt_path> is a ptr in <line>
337 * Return an error code
338 */
Remi Tricot-Le Bretonfb00f312021-03-23 16:41:53 +0100339int crtlist_parse_line(char *line, char **crt_path, struct crtlist_entry *entry, const char *file, int linenum, int from_cli, char **err)
William Lallemand6e9556b2020-05-12 17:52:44 +0200340{
341 int cfgerr = 0;
342 int arg, newarg, cur_arg, i, ssl_b = 0, ssl_e = 0;
343 char *end;
344 char *args[MAX_CRT_ARGS + 1];
345 struct ssl_bind_conf *ssl_conf = NULL;
346
347 if (!line || !crt_path || !entry)
348 return ERR_ALERT | ERR_FATAL;
349
350 end = line + strlen(line);
351 if (end-line >= CRT_LINESIZE-1 && *(end-1) != '\n') {
352 /* Check if we reached the limit and the last char is not \n.
353 * Watch out for the last line without the terminating '\n'!
354 */
Tim Duesterhus6d07fae2020-09-29 18:00:27 +0200355 memprintf(err, "parsing [%s:%d]: line too long, limit is %d characters",
356 file, linenum, CRT_LINESIZE-1);
William Lallemand6e9556b2020-05-12 17:52:44 +0200357 cfgerr |= ERR_ALERT | ERR_FATAL;
358 goto error;
359 }
360 arg = 0;
361 newarg = 1;
362 while (*line) {
363 if (isspace((unsigned char)*line)) {
364 newarg = 1;
365 *line = 0;
366 } else if (*line == '[') {
367 if (ssl_b) {
Tim Duesterhus6d07fae2020-09-29 18:00:27 +0200368 memprintf(err, "parsing [%s:%d]: too many '['", file, linenum);
William Lallemand6e9556b2020-05-12 17:52:44 +0200369 cfgerr |= ERR_ALERT | ERR_FATAL;
370 goto error;
371 }
372 if (!arg) {
Tim Duesterhus6d07fae2020-09-29 18:00:27 +0200373 memprintf(err, "parsing [%s:%d]: file must start with a cert", file, linenum);
William Lallemand6e9556b2020-05-12 17:52:44 +0200374 cfgerr |= ERR_ALERT | ERR_FATAL;
375 goto error;
376 }
377 ssl_b = arg;
378 newarg = 1;
379 *line = 0;
380 } else if (*line == ']') {
381 if (ssl_e) {
Tim Duesterhus6d07fae2020-09-29 18:00:27 +0200382 memprintf(err, "parsing [%s:%d]: too many ']'", file, linenum);
William Lallemand6e9556b2020-05-12 17:52:44 +0200383 cfgerr |= ERR_ALERT | ERR_FATAL;
384 goto error;
385 }
386 if (!ssl_b) {
Tim Duesterhus6d07fae2020-09-29 18:00:27 +0200387 memprintf(err, "parsing [%s:%d]: missing '['", file, linenum);
William Lallemand6e9556b2020-05-12 17:52:44 +0200388 cfgerr |= ERR_ALERT | ERR_FATAL;
389 goto error;
390 }
391 ssl_e = arg;
392 newarg = 1;
393 *line = 0;
394 } else if (newarg) {
395 if (arg == MAX_CRT_ARGS) {
Tim Duesterhus6d07fae2020-09-29 18:00:27 +0200396 memprintf(err, "parsing [%s:%d]: too many args ", file, linenum);
William Lallemand6e9556b2020-05-12 17:52:44 +0200397 cfgerr |= ERR_ALERT | ERR_FATAL;
398 goto error;
399 }
400 newarg = 0;
401 args[arg++] = line;
402 }
403 line++;
404 }
405 args[arg++] = line;
406
407 /* empty line */
408 if (!*args[0]) {
409 cfgerr |= ERR_NONE;
410 goto error;
411 }
412
413 *crt_path = args[0];
414
415 if (ssl_b) {
William Lallemandd85227f2023-02-07 17:06:35 +0100416 if (ssl_b > 1) {
417 memprintf(err, "parsing [%s:%d]: malformated line, filters can't be between filename and options!", file, linenum);
418 cfgerr |= ERR_WARN;
419 }
420
William Lallemand6e9556b2020-05-12 17:52:44 +0200421 ssl_conf = calloc(1, sizeof *ssl_conf);
422 if (!ssl_conf) {
423 memprintf(err, "not enough memory!");
424 cfgerr |= ERR_ALERT | ERR_FATAL;
425 goto error;
426 }
427 }
Remi Tricot-Le Bretonfb00f312021-03-23 16:41:53 +0100428
William Lallemand6e9556b2020-05-12 17:52:44 +0200429 cur_arg = ssl_b ? ssl_b : 1;
430 while (cur_arg < ssl_e) {
431 newarg = 0;
William Lallemandaf678062023-02-13 10:58:13 +0100432 for (i = 0; ssl_crtlist_kws[i].kw != NULL; i++) {
433 if (strcmp(ssl_crtlist_kws[i].kw, args[cur_arg]) == 0) {
William Lallemand6e9556b2020-05-12 17:52:44 +0200434 newarg = 1;
William Lallemandaf678062023-02-13 10:58:13 +0100435 cfgerr |= ssl_crtlist_kws[i].parse(args, cur_arg, NULL, ssl_conf, from_cli, err);
436 if (cur_arg + 1 + ssl_crtlist_kws[i].skip > ssl_e) {
Tim Duesterhus6d07fae2020-09-29 18:00:27 +0200437 memprintf(err, "parsing [%s:%d]: ssl args out of '[]' for %s",
438 file, linenum, args[cur_arg]);
William Lallemand6e9556b2020-05-12 17:52:44 +0200439 cfgerr |= ERR_ALERT | ERR_FATAL;
440 goto error;
441 }
William Lallemandaf678062023-02-13 10:58:13 +0100442 cur_arg += 1 + ssl_crtlist_kws[i].skip;
William Lallemand6e9556b2020-05-12 17:52:44 +0200443 break;
444 }
445 }
446 if (!cfgerr && !newarg) {
Tim Duesterhus6d07fae2020-09-29 18:00:27 +0200447 memprintf(err, "parsing [%s:%d]: unknown ssl keyword %s",
448 file, linenum, args[cur_arg]);
William Lallemand6e9556b2020-05-12 17:52:44 +0200449 cfgerr |= ERR_ALERT | ERR_FATAL;
450 goto error;
451 }
452 }
453 entry->linenum = linenum;
454 entry->ssl_conf = ssl_conf;
455 entry->filters = crtlist_dup_filters(&args[cur_arg], arg - cur_arg - 1);
456 entry->fcount = arg - cur_arg - 1;
457
458 return cfgerr;
459
460error:
461 crtlist_free_filters(entry->filters);
462 entry->filters = NULL;
463 ssl_sock_free_ssl_conf(entry->ssl_conf);
Willy Tarreau61cfdf42021-02-20 10:46:51 +0100464 ha_free(&entry->ssl_conf);
William Lallemand6e9556b2020-05-12 17:52:44 +0200465 return cfgerr;
466}
467
468
469
470/* This function parse a crt-list file and store it in a struct crtlist, each line is a crtlist_entry structure
471 * Fill the <crtlist> argument with a pointer to a new crtlist struct
472 *
473 * This function tries to open and store certificate files.
474 */
475int crtlist_parse_file(char *file, struct bind_conf *bind_conf, struct proxy *curproxy, struct crtlist **crtlist, char **err)
476{
477 struct crtlist *newlist;
478 struct crtlist_entry *entry = NULL;
479 char thisline[CRT_LINESIZE];
William Lallemand6e9556b2020-05-12 17:52:44 +0200480 FILE *f;
481 struct stat buf;
482 int linenum = 0;
483 int cfgerr = 0;
Tim Duesterhusb9f6acc2020-09-29 18:00:28 +0200484 int missing_lf = -1;
William Lallemand6e9556b2020-05-12 17:52:44 +0200485
486 if ((f = fopen(file, "r")) == NULL) {
487 memprintf(err, "cannot open file '%s' : %s", file, strerror(errno));
488 return ERR_ALERT | ERR_FATAL;
489 }
490
491 newlist = crtlist_new(file, 0);
492 if (newlist == NULL) {
493 memprintf(err, "Not enough memory!");
494 cfgerr |= ERR_ALERT | ERR_FATAL;
495 goto error;
496 }
497
498 while (fgets(thisline, sizeof(thisline), f) != NULL) {
499 char *end;
500 char *line = thisline;
501 char *crt_path;
William Lallemand86c2dd62020-11-20 14:23:38 +0100502 char path[MAXPATHLEN+1];
William Lallemand6e9556b2020-05-12 17:52:44 +0200503 struct ckch_store *ckchs;
William Lallemand77e1c6f2020-11-20 18:26:09 +0100504 int found = 0;
William Lallemand6e9556b2020-05-12 17:52:44 +0200505
Tim Duesterhusb9f6acc2020-09-29 18:00:28 +0200506 if (missing_lf != -1) {
507 memprintf(err, "parsing [%s:%d]: Stray NUL character at position %d.\n",
508 file, linenum, (missing_lf + 1));
509 cfgerr |= ERR_ALERT | ERR_FATAL;
510 missing_lf = -1;
511 break;
512 }
513
William Lallemand6e9556b2020-05-12 17:52:44 +0200514 linenum++;
515 end = line + strlen(line);
516 if (end-line == sizeof(thisline)-1 && *(end-1) != '\n') {
517 /* Check if we reached the limit and the last char is not \n.
518 * Watch out for the last line without the terminating '\n'!
519 */
Tim Duesterhus6d07fae2020-09-29 18:00:27 +0200520 memprintf(err, "parsing [%s:%d]: line too long, limit is %d characters",
521 file, linenum, (int)sizeof(thisline)-1);
William Lallemand6e9556b2020-05-12 17:52:44 +0200522 cfgerr |= ERR_ALERT | ERR_FATAL;
523 break;
524 }
525
526 if (*line == '#' || *line == '\n' || *line == '\r')
527 continue;
528
Tim Duesterhusb9f6acc2020-09-29 18:00:28 +0200529 if (end > line && *(end-1) == '\n') {
530 /* kill trailing LF */
531 *(end - 1) = 0;
532 }
533 else {
534 /* mark this line as truncated */
535 missing_lf = end - line;
536 }
537
William Lallemand6e9556b2020-05-12 17:52:44 +0200538 entry = crtlist_entry_new();
539 if (entry == NULL) {
540 memprintf(err, "Not enough memory!");
541 cfgerr |= ERR_ALERT | ERR_FATAL;
542 goto error;
543 }
Tim Duesterhusb9f6acc2020-09-29 18:00:28 +0200544
Remi Tricot-Le Bretonfb00f312021-03-23 16:41:53 +0100545 cfgerr |= crtlist_parse_line(thisline, &crt_path, entry, file, linenum, 0, err);
William Lallemand20b0fed2020-09-28 15:45:16 +0200546 if (cfgerr & ERR_CODE)
William Lallemand6e9556b2020-05-12 17:52:44 +0200547 goto error;
548
549 /* empty line */
550 if (!crt_path || !*crt_path) {
551 crtlist_entry_free(entry);
552 entry = NULL;
553 continue;
554 }
555
556 if (*crt_path != '/' && global_ssl.crt_base) {
Willy Tarreau393e42a2022-05-09 10:31:28 +0200557 if ((strlen(global_ssl.crt_base) + 1 + strlen(crt_path)) > sizeof(path) ||
Willy Tarreau63fc9002022-05-09 21:14:04 +0200558 snprintf(path, sizeof(path), "%s/%s", global_ssl.crt_base, crt_path) > sizeof(path)) {
Tim Duesterhus6d07fae2020-09-29 18:00:27 +0200559 memprintf(err, "parsing [%s:%d]: '%s' : path too long",
560 file, linenum, crt_path);
William Lallemand6e9556b2020-05-12 17:52:44 +0200561 cfgerr |= ERR_ALERT | ERR_FATAL;
562 goto error;
563 }
William Lallemand6e9556b2020-05-12 17:52:44 +0200564 crt_path = path;
565 }
566
567 /* Look for a ckch_store or create one */
568 ckchs = ckchs_lookup(crt_path);
569 if (ckchs == NULL) {
William Lallemand47da8212020-09-10 19:13:27 +0200570 if (stat(crt_path, &buf) == 0) {
William Lallemand77e1c6f2020-11-20 18:26:09 +0100571 found++;
William Lallemand47da8212020-09-10 19:13:27 +0200572
William Lallemandbd8e6ed2020-09-16 16:08:08 +0200573 ckchs = ckchs_load_cert_file(crt_path, err);
William Lallemand47da8212020-09-10 19:13:27 +0200574 if (ckchs == NULL) {
575 cfgerr |= ERR_ALERT | ERR_FATAL;
576 goto error;
577 }
578
579 entry->node.key = ckchs;
580 entry->crtlist = newlist;
Remi Tricot-Le Bretonfb2b9982022-12-20 11:11:11 +0100581 if (entry->ssl_conf)
582 ckchs->data->ocsp_update_mode = entry->ssl_conf->ocsp_update;
William Lallemand47da8212020-09-10 19:13:27 +0200583 ebpt_insert(&newlist->entries, &entry->node);
Willy Tarreau2b718102021-04-21 07:32:39 +0200584 LIST_APPEND(&newlist->ord_entries, &entry->by_crtlist);
585 LIST_APPEND(&ckchs->crtlist_entry, &entry->by_ckch_store);
William Lallemand6e9556b2020-05-12 17:52:44 +0200586
William Lallemand73404572020-11-20 18:23:40 +0100587 } else if (global_ssl.extra_files & SSL_GF_BUNDLE) {
William Lallemand47da8212020-09-10 19:13:27 +0200588 /* If we didn't find the file, this could be a
William Lallemand51f784b2020-10-02 18:08:18 +0200589 bundle, since 2.3 we don't support multiple
590 certificate in the same OpenSSL store, so we
591 emulate it by loading each file separately. To
592 do so we need to duplicate the entry in the
593 crt-list because it becomes independent */
William Lallemand47da8212020-09-10 19:13:27 +0200594 char fp[MAXPATHLEN+1] = {0};
595 int n = 0;
596 struct crtlist_entry *entry_dup = entry; /* use the previous created entry */
597 for (n = 0; n < SSL_SOCK_NUM_KEYTYPES; n++) {
598 struct stat buf;
599 int ret;
600
William Lallemand86c2dd62020-11-20 14:23:38 +0100601 ret = snprintf(fp, sizeof(fp), "%s.%s", crt_path, SSL_SOCK_KEYTYPE_NAMES[n]);
William Lallemand47da8212020-09-10 19:13:27 +0200602 if (ret > sizeof(fp))
603 continue;
604
605 ckchs = ckchs_lookup(fp);
William Lallemand77e1c6f2020-11-20 18:26:09 +0100606 if (!ckchs) {
607 if (stat(fp, &buf) == 0) {
608 ckchs = ckchs_load_cert_file(fp, err);
609 if (!ckchs) {
William Lallemand47da8212020-09-10 19:13:27 +0200610 cfgerr |= ERR_ALERT | ERR_FATAL;
611 goto error;
612 }
William Lallemand77e1c6f2020-11-20 18:26:09 +0100613 } else {
614 continue; /* didn't find this extension, skip */
615 }
616 }
617 found++;
618 linenum++; /* we duplicate the line for this entry in the bundle */
619 if (!entry_dup) { /* if the entry was used, duplicate one */
620 linenum++;
621 entry_dup = crtlist_entry_dup(entry);
622 if (!entry_dup) {
623 cfgerr |= ERR_ALERT | ERR_FATAL;
624 goto error;
William Lallemand47da8212020-09-10 19:13:27 +0200625 }
William Lallemand77e1c6f2020-11-20 18:26:09 +0100626 entry_dup->linenum = linenum;
627 }
William Lallemand47da8212020-09-10 19:13:27 +0200628
William Lallemand77e1c6f2020-11-20 18:26:09 +0100629 entry_dup->node.key = ckchs;
630 entry_dup->crtlist = newlist;
William Lallemanda14686d2023-02-07 18:38:05 +0100631
632 cfgerr |= ocsp_update_check_cfg_consistency(ckchs, entry, crt_path, err);
633 if (cfgerr & ERR_FATAL)
634 goto error;
635
Remi Tricot-Le Bretonfc92b8b2023-01-09 12:02:46 +0100636 if (entry->ssl_conf)
637 ckchs->data->ocsp_update_mode = entry->ssl_conf->ocsp_update;
William Lallemand77e1c6f2020-11-20 18:26:09 +0100638 ebpt_insert(&newlist->entries, &entry_dup->node);
Willy Tarreau2b718102021-04-21 07:32:39 +0200639 LIST_APPEND(&newlist->ord_entries, &entry_dup->by_crtlist);
640 LIST_APPEND(&ckchs->crtlist_entry, &entry_dup->by_ckch_store);
William Lallemand47da8212020-09-10 19:13:27 +0200641
William Lallemand77e1c6f2020-11-20 18:26:09 +0100642 entry_dup = NULL; /* the entry was used, we need a new one next round */
William Lallemand47da8212020-09-10 19:13:27 +0200643 }
William Lallemandb7fdfdf2020-12-04 15:45:02 +0100644#if HA_OPENSSL_VERSION_NUMBER < 0x10101000L
645 if (found) {
646 memprintf(err, "%sCan't load '%s'. Loading a multi certificates bundle requires OpenSSL >= 1.1.1\n",
647 err && *err ? *err : "", crt_path);
648 cfgerr |= ERR_ALERT | ERR_FATAL;
649 }
650#endif
William Lallemand47da8212020-09-10 19:13:27 +0200651 }
William Lallemand77e1c6f2020-11-20 18:26:09 +0100652 if (!found) {
653 memprintf(err, "%sunable to stat SSL certificate from file '%s' : %s.\n",
654 err && *err ? *err : "", crt_path, strerror(errno));
655 cfgerr |= ERR_ALERT | ERR_FATAL;
656 }
657
William Lallemand50c03aa2020-11-06 16:24:07 +0100658 } else {
659 entry->node.key = ckchs;
660 entry->crtlist = newlist;
William Lallemanda14686d2023-02-07 18:38:05 +0100661
662 cfgerr |= ocsp_update_check_cfg_consistency(ckchs, entry, crt_path, err);
663 if (cfgerr & ERR_FATAL)
664 goto error;
665
Remi Tricot-Le Bretonfc92b8b2023-01-09 12:02:46 +0100666 if (entry->ssl_conf)
667 ckchs->data->ocsp_update_mode = entry->ssl_conf->ocsp_update;
William Lallemand50c03aa2020-11-06 16:24:07 +0100668 ebpt_insert(&newlist->entries, &entry->node);
Willy Tarreau2b718102021-04-21 07:32:39 +0200669 LIST_APPEND(&newlist->ord_entries, &entry->by_crtlist);
670 LIST_APPEND(&ckchs->crtlist_entry, &entry->by_ckch_store);
William Lallemand77e1c6f2020-11-20 18:26:09 +0100671 found++;
William Lallemand47da8212020-09-10 19:13:27 +0200672 }
William Lallemand6e9556b2020-05-12 17:52:44 +0200673 entry = NULL;
674 }
Tim Duesterhusb9f6acc2020-09-29 18:00:28 +0200675
676 if (missing_lf != -1) {
677 memprintf(err, "parsing [%s:%d]: Missing LF on last line, file might have been truncated at position %d.\n",
678 file, linenum, (missing_lf + 1));
679 cfgerr |= ERR_ALERT | ERR_FATAL;
680 }
681
William Lallemand6e9556b2020-05-12 17:52:44 +0200682 if (cfgerr & ERR_CODE)
683 goto error;
684
685 newlist->linecount = linenum;
686
687 fclose(f);
688 *crtlist = newlist;
689
690 return cfgerr;
691error:
692 crtlist_entry_free(entry);
693
694 fclose(f);
695 crtlist_free(newlist);
696 return cfgerr;
697}
698
699/* This function reads a directory and stores it in a struct crtlist, each file is a crtlist_entry structure
700 * Fill the <crtlist> argument with a pointer to a new crtlist struct
701 *
702 * This function tries to open and store certificate files.
703 */
704int crtlist_load_cert_dir(char *path, struct bind_conf *bind_conf, struct crtlist **crtlist, char **err)
705{
706 struct crtlist *dir;
707 struct dirent **de_list;
708 int i, n;
709 struct stat buf;
710 char *end;
711 char fp[MAXPATHLEN+1];
712 int cfgerr = 0;
713 struct ckch_store *ckchs;
William Lallemand6e9556b2020-05-12 17:52:44 +0200714
715 dir = crtlist_new(path, 1);
716 if (dir == NULL) {
717 memprintf(err, "not enough memory");
718 return ERR_ALERT | ERR_FATAL;
719 }
720
721 n = scandir(path, &de_list, 0, alphasort);
722 if (n < 0) {
723 memprintf(err, "%sunable to scan directory '%s' : %s.\n",
724 err && *err ? *err : "", path, strerror(errno));
725 cfgerr |= ERR_ALERT | ERR_FATAL;
726 }
727 else {
728 for (i = 0; i < n; i++) {
729 struct crtlist_entry *entry;
730 struct dirent *de = de_list[i];
731
732 end = strrchr(de->d_name, '.');
William Lallemand589570d2022-05-09 10:30:51 +0200733 if (end && (de->d_name[0] == '.' ||
734 strcmp(end, ".issuer") == 0 || strcmp(end, ".ocsp") == 0 ||
735 strcmp(end, ".sctl") == 0 || strcmp(end, ".key") == 0))
William Lallemand6e9556b2020-05-12 17:52:44 +0200736 goto ignore_entry;
737
738 snprintf(fp, sizeof(fp), "%s/%s", path, de->d_name);
739 if (stat(fp, &buf) != 0) {
740 memprintf(err, "%sunable to stat SSL certificate from file '%s' : %s.\n",
741 err && *err ? *err : "", fp, strerror(errno));
742 cfgerr |= ERR_ALERT | ERR_FATAL;
743 goto ignore_entry;
744 }
745 if (!S_ISREG(buf.st_mode))
746 goto ignore_entry;
747
748 entry = crtlist_entry_new();
749 if (entry == NULL) {
750 memprintf(err, "not enough memory '%s'", fp);
751 cfgerr |= ERR_ALERT | ERR_FATAL;
752 goto ignore_entry;
753 }
754
William Lallemand6e9556b2020-05-12 17:52:44 +0200755 ckchs = ckchs_lookup(fp);
756 if (ckchs == NULL)
William Lallemandbd8e6ed2020-09-16 16:08:08 +0200757 ckchs = ckchs_load_cert_file(fp, err);
William Lallemand6e9556b2020-05-12 17:52:44 +0200758 if (ckchs == NULL) {
759 free(de);
760 free(entry);
761 cfgerr |= ERR_ALERT | ERR_FATAL;
762 goto end;
763 }
764 entry->node.key = ckchs;
765 entry->crtlist = dir;
Willy Tarreau2b718102021-04-21 07:32:39 +0200766 LIST_APPEND(&ckchs->crtlist_entry, &entry->by_ckch_store);
767 LIST_APPEND(&dir->ord_entries, &entry->by_crtlist);
William Lallemand6e9556b2020-05-12 17:52:44 +0200768 ebpt_insert(&dir->entries, &entry->node);
769
770ignore_entry:
771 free(de);
772 }
773end:
774 free(de_list);
775 }
776
777 if (cfgerr & ERR_CODE) {
778 /* free the dir and entries on error */
779 crtlist_free(dir);
780 } else {
781 *crtlist = dir;
782 }
783 return cfgerr;
784
785}
786
William Lallemandc756bbd2020-05-13 17:23:59 +0200787/*
788 * Take an ssl_bind_conf structure and append the configuration line used to
789 * create it in the buffer
790 */
791static void dump_crtlist_sslconf(struct buffer *buf, const struct ssl_bind_conf *conf)
792{
793 int space = 0;
794
795 if (conf == NULL)
796 return;
797
798 chunk_appendf(buf, " [");
799#ifdef OPENSSL_NPN_NEGOTIATED
800 if (conf->npn_str) {
801 int len = conf->npn_len;
802 char *ptr = conf->npn_str;
803 int comma = 0;
804
805 if (space) chunk_appendf(buf, " ");
806 chunk_appendf(buf, "npn ");
807 while (len) {
808 unsigned short size;
809
810 size = *ptr;
811 ptr++;
812 if (comma)
813 chunk_memcat(buf, ",", 1);
814 chunk_memcat(buf, ptr, size);
815 ptr += size;
816 len -= size + 1;
817 comma = 1;
818 }
819 chunk_memcat(buf, "", 1); /* finish with a \0 */
820 space++;
821 }
822#endif
823#ifdef TLSEXT_TYPE_application_layer_protocol_negotiation
824 if (conf->alpn_str) {
825 int len = conf->alpn_len;
826 char *ptr = conf->alpn_str;
827 int comma = 0;
828
829 if (space) chunk_appendf(buf, " ");
Willy Tarreaude85de62023-04-19 09:07:47 +0200830 if (len)
831 chunk_appendf(buf, "alpn ");
832 else
833 chunk_appendf(buf, "no-alpn");
William Lallemandc756bbd2020-05-13 17:23:59 +0200834 while (len) {
835 unsigned short size;
836
837 size = *ptr;
838 ptr++;
839 if (comma)
840 chunk_memcat(buf, ",", 1);
841 chunk_memcat(buf, ptr, size);
842 ptr += size;
843 len -= size + 1;
844 comma = 1;
845 }
846 chunk_memcat(buf, "", 1); /* finish with a \0 */
847 space++;
848 }
849#endif
850 /* verify */
851 {
852 if (conf->verify == SSL_SOCK_VERIFY_NONE) {
853 if (space) chunk_appendf(buf, " ");
854 chunk_appendf(buf, "verify none");
855 space++;
856 } else if (conf->verify == SSL_SOCK_VERIFY_OPTIONAL) {
857 if (space) chunk_appendf(buf, " ");
858 chunk_appendf(buf, "verify optional");
859 space++;
860 } else if (conf->verify == SSL_SOCK_VERIFY_REQUIRED) {
861 if (space) chunk_appendf(buf, " ");
862 chunk_appendf(buf, "verify required");
863 space++;
864 }
865 }
866
867 if (conf->no_ca_names) {
868 if (space) chunk_appendf(buf, " ");
869 chunk_appendf(buf, "no-ca-names");
870 space++;
871 }
872
873 if (conf->early_data) {
874 if (space) chunk_appendf(buf, " ");
875 chunk_appendf(buf, "allow-0rtt");
876 space++;
877 }
878 if (conf->ca_file) {
879 if (space) chunk_appendf(buf, " ");
880 chunk_appendf(buf, "ca-file %s", conf->ca_file);
881 space++;
882 }
883 if (conf->crl_file) {
884 if (space) chunk_appendf(buf, " ");
885 chunk_appendf(buf, "crl-file %s", conf->crl_file);
886 space++;
887 }
888 if (conf->ciphers) {
889 if (space) chunk_appendf(buf, " ");
890 chunk_appendf(buf, "ciphers %s", conf->ciphers);
891 space++;
892 }
Ilya Shipitsinf34ed0b2020-11-21 14:37:34 +0500893#ifdef HAVE_SSL_CTX_SET_CIPHERSUITES
William Lallemandc756bbd2020-05-13 17:23:59 +0200894 if (conf->ciphersuites) {
895 if (space) chunk_appendf(buf, " ");
896 chunk_appendf(buf, "ciphersuites %s", conf->ciphersuites);
897 space++;
898 }
899#endif
900 if (conf->curves) {
901 if (space) chunk_appendf(buf, " ");
902 chunk_appendf(buf, "curves %s", conf->curves);
903 space++;
904 }
905 if (conf->ecdhe) {
906 if (space) chunk_appendf(buf, " ");
907 chunk_appendf(buf, "ecdhe %s", conf->ecdhe);
908 space++;
909 }
910
911 /* the crt-lists only support ssl-min-ver and ssl-max-ver */
William Lallemand8177ad92020-05-20 16:49:02 +0200912 if (conf->ssl_methods_cfg.min) {
William Lallemandc756bbd2020-05-13 17:23:59 +0200913 if (space) chunk_appendf(buf, " ");
William Lallemand8177ad92020-05-20 16:49:02 +0200914 chunk_appendf(buf, "ssl-min-ver %s", methodVersions[conf->ssl_methods_cfg.min].name);
William Lallemandc756bbd2020-05-13 17:23:59 +0200915 space++;
916 }
917
William Lallemand8177ad92020-05-20 16:49:02 +0200918 if (conf->ssl_methods_cfg.max) {
William Lallemandc756bbd2020-05-13 17:23:59 +0200919 if (space) chunk_appendf(buf, " ");
William Lallemand8177ad92020-05-20 16:49:02 +0200920 chunk_appendf(buf, "ssl-max-ver %s", methodVersions[conf->ssl_methods_cfg.max].name);
William Lallemandc756bbd2020-05-13 17:23:59 +0200921 space++;
922 }
923
Remi Tricot-Le Bretonca0c84a2023-03-02 15:49:52 +0100924 if (conf->ocsp_update != SSL_SOCK_OCSP_UPDATE_DFLT) {
925 if (space) chunk_appendf(buf, " ");
926 chunk_appendf(buf, "ocsp-update %s",
927 conf->ocsp_update == SSL_SOCK_OCSP_UPDATE_OFF ? "off" : "on");
928 space++;
929 }
930
William Lallemandc756bbd2020-05-13 17:23:59 +0200931 chunk_appendf(buf, "]");
932
933 return;
934}
935
936/* dump a list of filters */
937static void dump_crtlist_filters(struct buffer *buf, struct crtlist_entry *entry)
938{
939 int i;
940
941 if (!entry->fcount)
942 return;
943
944 for (i = 0; i < entry->fcount; i++) {
945 chunk_appendf(buf, " %s", entry->filters[i]);
946 }
947 return;
948}
949
950/************************** CLI functions ****************************/
951
952
Willy Tarreaua2fcca02022-05-05 11:53:23 +0200953/* CLI IO handler for '(show|dump) ssl crt-list'.
954 * It uses show_crtlist_ctx for the context.
955 */
William Lallemandc756bbd2020-05-13 17:23:59 +0200956static int cli_io_handler_dump_crtlist(struct appctx *appctx)
957{
Willy Tarreaua2fcca02022-05-05 11:53:23 +0200958 struct show_crtlist_ctx *ctx = appctx->svcctx;
William Lallemandc756bbd2020-05-13 17:23:59 +0200959 struct buffer *trash = alloc_trash_chunk();
William Lallemandc756bbd2020-05-13 17:23:59 +0200960 struct ebmb_node *lnode;
961
962 if (trash == NULL)
963 return 1;
964
965 /* dump the list of crt-lists */
Willy Tarreaua2fcca02022-05-05 11:53:23 +0200966 lnode = ctx->crtlist_node;
William Lallemandc756bbd2020-05-13 17:23:59 +0200967 if (lnode == NULL)
968 lnode = ebmb_first(&crtlists_tree);
969 while (lnode) {
970 chunk_appendf(trash, "%s\n", lnode->key);
Willy Tarreaud0a06d52022-05-18 15:07:19 +0200971 if (applet_putchk(appctx, trash) == -1)
William Lallemandc756bbd2020-05-13 17:23:59 +0200972 goto yield;
William Lallemandc756bbd2020-05-13 17:23:59 +0200973 lnode = ebmb_next(lnode);
974 }
975 free_trash_chunk(trash);
976 return 1;
977yield:
Willy Tarreaua2fcca02022-05-05 11:53:23 +0200978 ctx->crtlist_node = lnode;
William Lallemandc756bbd2020-05-13 17:23:59 +0200979 free_trash_chunk(trash);
980 return 0;
981}
982
983/* CLI IO handler for '(show|dump) ssl crt-list <filename>' */
984static int cli_io_handler_dump_crtlist_entries(struct appctx *appctx)
985{
Willy Tarreaua2fcca02022-05-05 11:53:23 +0200986 struct show_crtlist_ctx *ctx = appctx->svcctx;
William Lallemandc756bbd2020-05-13 17:23:59 +0200987 struct buffer *trash = alloc_trash_chunk();
988 struct crtlist *crtlist;
William Lallemandc756bbd2020-05-13 17:23:59 +0200989 struct crtlist_entry *entry;
990
991 if (trash == NULL)
992 return 1;
993
Willy Tarreaua2fcca02022-05-05 11:53:23 +0200994 crtlist = ebmb_entry(ctx->crtlist_node, struct crtlist, node);
William Lallemandc756bbd2020-05-13 17:23:59 +0200995
Willy Tarreaua2fcca02022-05-05 11:53:23 +0200996 entry = ctx->entry;
William Lallemandc756bbd2020-05-13 17:23:59 +0200997 if (entry == NULL) {
998 entry = LIST_ELEM((crtlist->ord_entries).n, typeof(entry), by_crtlist);
999 chunk_appendf(trash, "# %s\n", crtlist->node.key);
Willy Tarreaud0a06d52022-05-18 15:07:19 +02001000 if (applet_putchk(appctx, trash) == -1)
William Lallemandc756bbd2020-05-13 17:23:59 +02001001 goto yield;
William Lallemandc756bbd2020-05-13 17:23:59 +02001002 }
1003
1004 list_for_each_entry_from(entry, &crtlist->ord_entries, by_crtlist) {
1005 struct ckch_store *store;
1006 const char *filename;
1007
1008 store = entry->node.key;
1009 filename = store->path;
1010 chunk_appendf(trash, "%s", filename);
Willy Tarreaua2fcca02022-05-05 11:53:23 +02001011 if (ctx->mode == 's') /* show */
William Lallemandc756bbd2020-05-13 17:23:59 +02001012 chunk_appendf(trash, ":%d", entry->linenum);
1013 dump_crtlist_sslconf(trash, entry->ssl_conf);
1014 dump_crtlist_filters(trash, entry);
1015 chunk_appendf(trash, "\n");
1016
Willy Tarreaud0a06d52022-05-18 15:07:19 +02001017 if (applet_putchk(appctx, trash) == -1)
William Lallemandc756bbd2020-05-13 17:23:59 +02001018 goto yield;
William Lallemandc756bbd2020-05-13 17:23:59 +02001019 }
1020 free_trash_chunk(trash);
1021 return 1;
1022yield:
Willy Tarreaua2fcca02022-05-05 11:53:23 +02001023 ctx->entry = entry;
William Lallemandc756bbd2020-05-13 17:23:59 +02001024 free_trash_chunk(trash);
1025 return 0;
1026}
1027
1028/* CLI argument parser for '(show|dump) ssl crt-list' */
1029static int cli_parse_dump_crtlist(char **args, char *payload, struct appctx *appctx, void *private)
1030{
Willy Tarreaua2fcca02022-05-05 11:53:23 +02001031 struct show_crtlist_ctx *ctx = applet_reserve_svcctx(appctx, sizeof(*ctx));
William Lallemandc756bbd2020-05-13 17:23:59 +02001032 struct ebmb_node *lnode;
1033 char *filename = NULL;
1034 int mode;
William Lallemand99cc2182020-06-25 15:19:51 +02001035 char *end;
William Lallemandc756bbd2020-05-13 17:23:59 +02001036
1037 if (!cli_has_level(appctx, ACCESS_LVL_ADMIN))
1038 return 1;
1039
Tim Duesterhuse5ff1412021-01-02 22:31:53 +01001040 if (*args[3] && strcmp(args[3], "-n") == 0) {
William Lallemandc756bbd2020-05-13 17:23:59 +02001041 mode = 's';
1042 filename = args[4];
1043 } else {
1044 mode = 'd';
1045 filename = args[3];
1046 }
1047
1048 if (mode == 's' && !*args[4])
1049 return cli_err(appctx, "'show ssl crt-list -n' expects a filename or a directory\n");
1050
1051 if (filename && *filename) {
William Lallemand99cc2182020-06-25 15:19:51 +02001052
1053
1054 /* strip trailing slashes, including first one */
1055 for (end = filename + strlen(filename) - 1; end >= filename && *end == '/'; end--)
1056 *end = 0;
1057
William Lallemandc756bbd2020-05-13 17:23:59 +02001058 lnode = ebst_lookup(&crtlists_tree, filename);
1059 if (lnode == NULL)
1060 return cli_err(appctx, "didn't find the specified filename\n");
1061
Willy Tarreaua2fcca02022-05-05 11:53:23 +02001062 ctx->crtlist_node = lnode;
William Lallemandc756bbd2020-05-13 17:23:59 +02001063 appctx->io_handler = cli_io_handler_dump_crtlist_entries;
1064 }
Willy Tarreaua2fcca02022-05-05 11:53:23 +02001065 ctx->mode = mode;
William Lallemandc756bbd2020-05-13 17:23:59 +02001066
1067 return 0;
1068}
1069
1070/* release function of the "add ssl crt-list' command, free things and unlock
Willy Tarreau6b6c3632022-05-05 13:43:49 +02001071 * the spinlock. It uses the add_crtlist_ctx.
1072 */
William Lallemandc756bbd2020-05-13 17:23:59 +02001073static void cli_release_add_crtlist(struct appctx *appctx)
1074{
Willy Tarreau6b6c3632022-05-05 13:43:49 +02001075 struct add_crtlist_ctx *ctx = appctx->svcctx;
1076 struct crtlist_entry *entry = ctx->entry;
William Lallemandc756bbd2020-05-13 17:23:59 +02001077
Christopher Fauletc642d7c2022-06-01 16:31:09 +02001078 if (entry) {
William Lallemandc756bbd2020-05-13 17:23:59 +02001079 struct ckch_inst *inst, *inst_s;
Christopher Fauletc642d7c2022-06-01 16:31:09 +02001080
William Lallemandc756bbd2020-05-13 17:23:59 +02001081 /* upon error free the ckch_inst and everything inside */
1082 ebpt_delete(&entry->node);
Willy Tarreau2b718102021-04-21 07:32:39 +02001083 LIST_DELETE(&entry->by_crtlist);
1084 LIST_DELETE(&entry->by_ckch_store);
William Lallemandc756bbd2020-05-13 17:23:59 +02001085
1086 list_for_each_entry_safe(inst, inst_s, &entry->ckch_inst, by_ckchs) {
1087 ckch_inst_free(inst);
1088 }
1089 crtlist_free_filters(entry->filters);
1090 ssl_sock_free_ssl_conf(entry->ssl_conf);
1091 free(entry->ssl_conf);
1092 free(entry);
1093 }
William Lallemandc756bbd2020-05-13 17:23:59 +02001094 HA_SPIN_UNLOCK(CKCH_LOCK, &ckch_lock);
Christopher Fauletc642d7c2022-06-01 16:31:09 +02001095 ha_free(&ctx->err);
William Lallemandc756bbd2020-05-13 17:23:59 +02001096}
1097
1098
1099/* IO Handler for the "add ssl crt-list" command It adds a new entry in the
1100 * crt-list and generates the ckch_insts for each bind_conf that uses this crt-list
1101 *
1102 * The logic is the same as the "commit ssl cert" command but without the
1103 * freeing of the old structures, because there are none.
Willy Tarreau6b6c3632022-05-05 13:43:49 +02001104 *
1105 * It uses the add_crtlist_ctx for the context.
William Lallemandc756bbd2020-05-13 17:23:59 +02001106 */
1107static int cli_io_handler_add_crtlist(struct appctx *appctx)
1108{
Willy Tarreau6b6c3632022-05-05 13:43:49 +02001109 struct add_crtlist_ctx *ctx = appctx->svcctx;
William Lallemandc756bbd2020-05-13 17:23:59 +02001110 struct bind_conf_list *bind_conf_node;
Willy Tarreauc12b3212022-05-27 11:08:15 +02001111 struct stconn *sc = appctx_sc(appctx);
Willy Tarreau6b6c3632022-05-05 13:43:49 +02001112 struct crtlist *crtlist = ctx->crtlist;
1113 struct crtlist_entry *entry = ctx->entry;
William Lallemandc756bbd2020-05-13 17:23:59 +02001114 struct ckch_store *store = entry->node.key;
William Lallemandc756bbd2020-05-13 17:23:59 +02001115 struct ckch_inst *new_inst;
William Lallemandc756bbd2020-05-13 17:23:59 +02001116 int i = 0;
1117 int errcode = 0;
1118
William Lallemandc756bbd2020-05-13 17:23:59 +02001119 /* for each bind_conf which use the crt-list, a new ckch_inst must be
1120 * created.
1121 */
Christopher Faulet87633c32023-04-03 18:32:50 +02001122 /* FIXME: Don't watch the other side !*/
Christopher Faulet208c7122023-04-13 16:16:15 +02001123 if (unlikely(sc_opposite(sc)->flags & SC_FL_SHUT_DONE))
Christopher Fauletc642d7c2022-06-01 16:31:09 +02001124 goto end;
William Lallemandc756bbd2020-05-13 17:23:59 +02001125
Willy Tarreaufa11df52022-05-05 13:48:40 +02001126 switch (ctx->state) {
1127 case ADDCRT_ST_INIT:
Willy Tarreau1b948ef2022-05-05 13:50:46 +02001128 /* This state just print the update message */
Christopher Fauletc642d7c2022-06-01 16:31:09 +02001129 chunk_printf(&trash, "Inserting certificate '%s' in crt-list '%s'", store->path, crtlist->node.key);
1130 if (applet_putchk(appctx, &trash) == -1)
Willy Tarreau1b948ef2022-05-05 13:50:46 +02001131 goto yield;
Willy Tarreaufa11df52022-05-05 13:48:40 +02001132 ctx->state = ADDCRT_ST_GEN;
Willy Tarreauaef84482022-11-14 07:03:16 +01001133 __fallthrough;
Willy Tarreaufa11df52022-05-05 13:48:40 +02001134 case ADDCRT_ST_GEN:
Willy Tarreau6b6c3632022-05-05 13:43:49 +02001135 bind_conf_node = ctx->bind_conf_node; /* get the previous ptr from the yield */
Willy Tarreau1b948ef2022-05-05 13:50:46 +02001136 if (bind_conf_node == NULL)
1137 bind_conf_node = crtlist->bind_conf;
1138 for (; bind_conf_node; bind_conf_node = bind_conf_node->next) {
1139 struct bind_conf *bind_conf = bind_conf_node->bind_conf;
1140 struct sni_ctx *sni;
1141
Christopher Fauletc642d7c2022-06-01 16:31:09 +02001142 ctx->bind_conf_node = bind_conf_node;
1143
Willy Tarreau1b948ef2022-05-05 13:50:46 +02001144 /* yield every 10 generations */
1145 if (i > 10) {
Christopher Fauletc642d7c2022-06-01 16:31:09 +02001146 applet_have_more_data(appctx); /* let's come back later */
Willy Tarreau1b948ef2022-05-05 13:50:46 +02001147 goto yield;
1148 }
William Lallemandc756bbd2020-05-13 17:23:59 +02001149
Christopher Fauletc642d7c2022-06-01 16:31:09 +02001150 /* display one dot for each new instance */
1151 if (applet_putstr(appctx, ".") == -1)
1152 goto yield;
1153
Willy Tarreau1b948ef2022-05-05 13:50:46 +02001154 /* we don't support multi-cert bundles, only simple ones */
Christopher Fauletc642d7c2022-06-01 16:31:09 +02001155 ctx->err = NULL;
1156 errcode |= ckch_inst_new_load_store(store->path, store, bind_conf, entry->ssl_conf, entry->filters, entry->fcount, &new_inst, &ctx->err);
1157 if (errcode & ERR_CODE) {
1158 ctx->state = ADDCRT_ST_ERROR;
Willy Tarreau1b948ef2022-05-05 13:50:46 +02001159 goto error;
Christopher Fauletc642d7c2022-06-01 16:31:09 +02001160 }
William Lallemandc756bbd2020-05-13 17:23:59 +02001161
Willy Tarreau1b948ef2022-05-05 13:50:46 +02001162 /* we need to initialize the SSL_CTX generated */
1163 /* this iterate on the newly generated SNIs in the new instance to prepare their SSL_CTX */
1164 list_for_each_entry(sni, &new_inst->sni_ctx, by_ckch_inst) {
1165 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 +02001166 ctx->err = NULL;
1167 errcode |= ssl_sock_prep_ctx_and_inst(bind_conf, new_inst->ssl_conf, sni->ctx, sni->ckch_inst, &ctx->err);
1168 if (errcode & ERR_CODE) {
1169 ctx->state = ADDCRT_ST_ERROR;
William Lallemandc756bbd2020-05-13 17:23:59 +02001170 goto error;
Christopher Fauletc642d7c2022-06-01 16:31:09 +02001171 }
William Lallemandc756bbd2020-05-13 17:23:59 +02001172 }
Willy Tarreau1b948ef2022-05-05 13:50:46 +02001173 }
Christopher Fauletc642d7c2022-06-01 16:31:09 +02001174
Willy Tarreau1b948ef2022-05-05 13:50:46 +02001175 i++;
1176 LIST_APPEND(&store->ckch_inst, &new_inst->by_ckchs);
1177 LIST_APPEND(&entry->ckch_inst, &new_inst->by_crtlist_entry);
1178 new_inst->crtlist_entry = entry;
William Lallemandc756bbd2020-05-13 17:23:59 +02001179 }
Willy Tarreaufa11df52022-05-05 13:48:40 +02001180 ctx->state = ADDCRT_ST_INSERT;
Willy Tarreauaef84482022-11-14 07:03:16 +01001181 __fallthrough;
Willy Tarreaufa11df52022-05-05 13:48:40 +02001182 case ADDCRT_ST_INSERT:
William Lallemandcb6c5f42022-06-20 16:51:53 +02001183 /* the insertion is called for every instance of the store, not
1184 * only the one we generated.
1185 * But the ssl_sock_load_cert_sni() skip the sni already
1186 * inserted. Not every instance has a bind_conf, it could be
1187 * the store of a server so we should be careful */
1188
Willy Tarreau1b948ef2022-05-05 13:50:46 +02001189 list_for_each_entry(new_inst, &store->ckch_inst, by_ckchs) {
William Lallemandcb6c5f42022-06-20 16:51:53 +02001190 if (!new_inst->bind_conf) /* this is a server instance */
1191 continue;
Willy Tarreau1b948ef2022-05-05 13:50:46 +02001192 HA_RWLOCK_WRLOCK(SNI_LOCK, &new_inst->bind_conf->sni_lock);
1193 ssl_sock_load_cert_sni(new_inst, new_inst->bind_conf);
1194 HA_RWLOCK_WRUNLOCK(SNI_LOCK, &new_inst->bind_conf->sni_lock);
1195 }
1196 entry->linenum = ++crtlist->linecount;
Christopher Fauletc642d7c2022-06-01 16:31:09 +02001197 ctx->entry = NULL;
1198 ctx->state = ADDCRT_ST_SUCCESS;
Willy Tarreauaef84482022-11-14 07:03:16 +01001199 __fallthrough;
Christopher Fauletc642d7c2022-06-01 16:31:09 +02001200 case ADDCRT_ST_SUCCESS:
1201 chunk_reset(&trash);
1202 chunk_appendf(&trash, "\n");
1203 if (ctx->err)
1204 chunk_appendf(&trash, "%s", ctx->err);
1205 chunk_appendf(&trash, "Success!\n");
1206 if (applet_putchk(appctx, &trash) == -1)
1207 goto yield;
1208 ctx->state = ADDCRT_ST_FIN;
1209 break;
1210
1211 case ADDCRT_ST_ERROR:
1212 error:
1213 chunk_printf(&trash, "\n%sFailed!\n", ctx->err);
1214 if (applet_putchk(appctx, &trash) == -1)
1215 goto yield;
1216 break;
1217
Willy Tarreaufa11df52022-05-05 13:48:40 +02001218 default:
1219 break;
William Lallemandc756bbd2020-05-13 17:23:59 +02001220 }
1221
Christopher Fauletc642d7c2022-06-01 16:31:09 +02001222end:
William Lallemandc756bbd2020-05-13 17:23:59 +02001223 /* success: call the release function and don't come back */
1224 return 1;
1225yield:
William Lallemandc756bbd2020-05-13 17:23:59 +02001226 return 0; /* should come back */
William Lallemandc756bbd2020-05-13 17:23:59 +02001227}
1228
1229
1230/*
1231 * Parse a "add ssl crt-list <crt-list> <certfile>" line.
Willy Tarreau6b6c3632022-05-05 13:43:49 +02001232 * Filters and option must be passed through payload.
1233 * It sets a struct add_crtlist_ctx.
William Lallemandc756bbd2020-05-13 17:23:59 +02001234 */
1235static int cli_parse_add_crtlist(char **args, char *payload, struct appctx *appctx, void *private)
1236{
Willy Tarreau6b6c3632022-05-05 13:43:49 +02001237 struct add_crtlist_ctx *ctx = applet_reserve_svcctx(appctx, sizeof(*ctx));
William Lallemandc756bbd2020-05-13 17:23:59 +02001238 int cfgerr = 0;
1239 struct ckch_store *store;
1240 char *err = NULL;
1241 char path[MAXPATHLEN+1];
1242 char *crtlist_path;
1243 char *cert_path = NULL;
1244 struct ebmb_node *eb;
1245 struct ebpt_node *inserted;
1246 struct crtlist *crtlist;
1247 struct crtlist_entry *entry = NULL;
William Lallemand99cc2182020-06-25 15:19:51 +02001248 char *end;
William Lallemandc756bbd2020-05-13 17:23:59 +02001249
1250 if (!cli_has_level(appctx, ACCESS_LVL_ADMIN))
1251 return 1;
1252
1253 if (!*args[3] || (!payload && !*args[4]))
1254 return cli_err(appctx, "'add ssl crtlist' expects a filename and a certificate name\n");
1255
1256 crtlist_path = args[3];
1257
William Lallemand99cc2182020-06-25 15:19:51 +02001258 /* strip trailing slashes, including first one */
1259 for (end = crtlist_path + strlen(crtlist_path) - 1; end >= crtlist_path && *end == '/'; end--)
1260 *end = 0;
1261
William Lallemandc756bbd2020-05-13 17:23:59 +02001262 if (HA_SPIN_TRYLOCK(CKCH_LOCK, &ckch_lock))
1263 return cli_err(appctx, "Operations on certificates are currently locked!\n");
1264
1265 eb = ebst_lookup(&crtlists_tree, crtlist_path);
1266 if (!eb) {
1267 memprintf(&err, "crt-list '%s' does not exist!", crtlist_path);
1268 goto error;
1269 }
1270 crtlist = ebmb_entry(eb, struct crtlist, node);
1271
1272 entry = crtlist_entry_new();
1273 if (entry == NULL) {
1274 memprintf(&err, "Not enough memory!");
1275 goto error;
1276 }
1277
1278 if (payload) {
1279 char *lf;
1280
1281 lf = strrchr(payload, '\n');
1282 if (lf) {
1283 memprintf(&err, "only one line of payload is supported!");
1284 goto error;
1285 }
1286 /* cert_path is filled here */
Remi Tricot-Le Bretonfb00f312021-03-23 16:41:53 +01001287 cfgerr |= crtlist_parse_line(payload, &cert_path, entry, "CLI", 1, 1, &err);
William Lallemandc756bbd2020-05-13 17:23:59 +02001288 if (cfgerr & ERR_CODE)
1289 goto error;
1290 } else {
1291 cert_path = args[4];
1292 }
1293
1294 if (!cert_path) {
1295 memprintf(&err, "'add ssl crtlist' should contain the certificate name in the payload");
1296 cfgerr |= ERR_ALERT | ERR_FATAL;
1297 goto error;
1298 }
1299
1300 if (eb_gettag(crtlist->entries.b[EB_RGHT])) {
1301 char *slash;
1302
1303 slash = strrchr(cert_path, '/');
1304 if (!slash) {
1305 memprintf(&err, "'%s' is a directory, certificate path '%s' must contain the directory path", (char *)crtlist->node.key, cert_path);
1306 goto error;
1307 }
1308 /* temporary replace / by 0 to do an strcmp */
1309 *slash = '\0';
1310 if (strcmp(cert_path, (char*)crtlist->node.key) != 0) {
1311 *slash = '/';
1312 memprintf(&err, "'%s' is a directory, certificate path '%s' must contain the directory path", (char *)crtlist->node.key, cert_path);
1313 goto error;
1314 }
1315 *slash = '/';
1316 }
1317
1318 if (*cert_path != '/' && global_ssl.crt_base) {
Willy Tarreau393e42a2022-05-09 10:31:28 +02001319 if ((strlen(global_ssl.crt_base) + 1 + strlen(cert_path)) > sizeof(path) ||
1320 snprintf(path, sizeof(path), "%s/%s", global_ssl.crt_base, cert_path) > sizeof(path)) {
William Lallemandc756bbd2020-05-13 17:23:59 +02001321 memprintf(&err, "'%s' : path too long", cert_path);
1322 cfgerr |= ERR_ALERT | ERR_FATAL;
1323 goto error;
1324 }
William Lallemandc756bbd2020-05-13 17:23:59 +02001325 cert_path = path;
1326 }
1327
1328 store = ckchs_lookup(cert_path);
1329 if (store == NULL) {
1330 memprintf(&err, "certificate '%s' does not exist!", cert_path);
1331 goto error;
1332 }
William Lallemand52ddd992022-11-22 11:51:53 +01001333 if (store->data == NULL || store->data->cert == NULL) {
William Lallemandc756bbd2020-05-13 17:23:59 +02001334 memprintf(&err, "certificate '%s' is empty!", cert_path);
1335 goto error;
1336 }
1337
Remi Tricot-Le Breton86d1e0b2023-03-02 15:49:53 +01001338 /* No need to check 'ocsp-update' inconsistency on a store that is not
1339 * used yet (it was just added through the CLI for instance).
1340 */
1341 if (!LIST_ISEMPTY(&store->ckch_inst) &&
1342 ocsp_update_check_cfg_consistency(store, entry, cert_path, &err))
1343 goto error;
1344
1345 if (entry->ssl_conf)
1346 store->data->ocsp_update_mode = entry->ssl_conf->ocsp_update;
1347
William Lallemandc756bbd2020-05-13 17:23:59 +02001348 /* check if it's possible to insert this new crtlist_entry */
1349 entry->node.key = store;
1350 inserted = ebpt_insert(&crtlist->entries, &entry->node);
1351 if (inserted != &entry->node) {
1352 memprintf(&err, "file already exists in this directory!");
1353 goto error;
1354 }
1355
1356 /* this is supposed to be a directory (EB_ROOT_UNIQUE), so no ssl_conf are allowed */
1357 if ((entry->ssl_conf || entry->filters) && eb_gettag(crtlist->entries.b[EB_RGHT])) {
1358 memprintf(&err, "this is a directory, SSL configuration and filters are not allowed");
1359 goto error;
1360 }
1361
Willy Tarreau2b718102021-04-21 07:32:39 +02001362 LIST_APPEND(&crtlist->ord_entries, &entry->by_crtlist);
William Lallemandc756bbd2020-05-13 17:23:59 +02001363 entry->crtlist = crtlist;
Willy Tarreau2b718102021-04-21 07:32:39 +02001364 LIST_APPEND(&store->crtlist_entry, &entry->by_ckch_store);
William Lallemandc756bbd2020-05-13 17:23:59 +02001365
Willy Tarreaufa11df52022-05-05 13:48:40 +02001366 ctx->state = ADDCRT_ST_INIT;
Willy Tarreau6b6c3632022-05-05 13:43:49 +02001367 ctx->crtlist = crtlist;
1368 ctx->entry = entry;
William Lallemandc756bbd2020-05-13 17:23:59 +02001369
1370 /* unlock is done in the release handler */
1371 return 0;
1372
1373error:
1374 crtlist_entry_free(entry);
1375 HA_SPIN_UNLOCK(CKCH_LOCK, &ckch_lock);
1376 err = memprintf(&err, "Can't edit the crt-list: %s\n", err ? err : "");
1377 return cli_dynerr(appctx, err);
1378}
1379
1380/* Parse a "del ssl crt-list <crt-list> <certfile>" line. */
1381static int cli_parse_del_crtlist(char **args, char *payload, struct appctx *appctx, void *private)
1382{
1383 struct ckch_store *store;
1384 char *err = NULL;
1385 char *crtlist_path, *cert_path;
1386 struct ebmb_node *ebmb;
1387 struct ebpt_node *ebpt;
1388 struct crtlist *crtlist;
1389 struct crtlist_entry *entry = NULL;
1390 struct ckch_inst *inst, *inst_s;
1391 int linenum = 0;
1392 char *colons;
William Lallemand99cc2182020-06-25 15:19:51 +02001393 char *end;
Remi Tricot-Le Bretonbc2c3862021-03-26 10:47:50 +01001394 int error_message_dumped = 0;
William Lallemandc756bbd2020-05-13 17:23:59 +02001395
1396 if (!cli_has_level(appctx, ACCESS_LVL_ADMIN))
1397 return 1;
1398
1399 if (!*args[3] || !*args[4])
1400 return cli_err(appctx, "'del ssl crtlist' expects a filename and a certificate name\n");
1401
1402 if (HA_SPIN_TRYLOCK(CKCH_LOCK, &ckch_lock))
1403 return cli_err(appctx, "Can't delete!\nOperations on certificates are currently locked!\n");
1404
1405 crtlist_path = args[3];
1406 cert_path = args[4];
1407
1408 colons = strchr(cert_path, ':');
1409 if (colons) {
1410 char *endptr;
1411
1412 linenum = strtol(colons + 1, &endptr, 10);
1413 if (colons + 1 == endptr || *endptr != '\0') {
1414 memprintf(&err, "wrong line number after colons in '%s'!", cert_path);
1415 goto error;
1416 }
1417 *colons = '\0';
1418 }
William Lallemand99cc2182020-06-25 15:19:51 +02001419
1420 /* strip trailing slashes, including first one */
1421 for (end = crtlist_path + strlen(crtlist_path) - 1; end >= crtlist_path && *end == '/'; end--)
1422 *end = 0;
1423
William Lallemandc756bbd2020-05-13 17:23:59 +02001424 /* look for crtlist */
1425 ebmb = ebst_lookup(&crtlists_tree, crtlist_path);
1426 if (!ebmb) {
1427 memprintf(&err, "crt-list '%s' does not exist!", crtlist_path);
1428 goto error;
1429 }
1430 crtlist = ebmb_entry(ebmb, struct crtlist, node);
1431
1432 /* look for store */
1433 store = ckchs_lookup(cert_path);
1434 if (store == NULL) {
1435 memprintf(&err, "certificate '%s' does not exist!", cert_path);
1436 goto error;
1437 }
William Lallemand52ddd992022-11-22 11:51:53 +01001438 if (store->data == NULL || store->data->cert == NULL) {
William Lallemandc756bbd2020-05-13 17:23:59 +02001439 memprintf(&err, "certificate '%s' is empty!", cert_path);
1440 goto error;
1441 }
1442
1443 ebpt = ebpt_lookup(&crtlist->entries, store);
1444 if (!ebpt) {
1445 memprintf(&err, "certificate '%s' can't be found in crt-list '%s'!", cert_path, crtlist_path);
1446 goto error;
1447 }
1448
1449 /* list the line number of entries for errors in err, and select the right ebpt */
1450 for (; ebpt; ebpt = ebpt_next_dup(ebpt)) {
1451 struct crtlist_entry *tmp;
1452
1453 tmp = ebpt_entry(ebpt, struct crtlist_entry, node);
1454 memprintf(&err, "%s%s%d", err ? err : "", err ? ", " : "", tmp->linenum);
1455
1456 /* select the entry we wanted */
1457 if (linenum == 0 || tmp->linenum == linenum) {
1458 if (!entry)
1459 entry = tmp;
1460 }
1461 }
1462
1463 /* we didn't found the specified entry */
1464 if (!entry) {
1465 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);
1466 goto error;
1467 }
1468
1469 /* we didn't specified a line number but there were several entries */
1470 if (linenum == 0 && ebpt_next_dup(&entry->node)) {
1471 memprintf(&err, "found the certificate '%s' in several entries, please specify a line number preceded by colons (%s)!", cert_path, err ? err : NULL);
1472 goto error;
1473 }
1474
Remi Tricot-Le Bretonbc2c3862021-03-26 10:47:50 +01001475 /* Iterate over all the instances in order to see if any of them is a
1476 * default instance. If this is the case, the entry won't be suppressed. */
1477 list_for_each_entry_safe(inst, inst_s, &entry->ckch_inst, by_crtlist_entry) {
1478 if (inst->is_default && !inst->bind_conf->strict_sni) {
1479 if (!error_message_dumped) {
1480 memprintf(&err, "certificate '%s' cannot be deleted, it is used as default certificate by the following frontends:\n", cert_path);
1481 error_message_dumped = 1;
1482 }
1483 memprintf(&err, "%s\t- %s:%d\n", err, inst->bind_conf->file, inst->bind_conf->line);
1484 }
1485 }
1486 if (error_message_dumped)
1487 goto error;
1488
William Lallemandc756bbd2020-05-13 17:23:59 +02001489 /* upon error free the ckch_inst and everything inside */
1490
1491 ebpt_delete(&entry->node);
Willy Tarreau2b718102021-04-21 07:32:39 +02001492 LIST_DELETE(&entry->by_crtlist);
1493 LIST_DELETE(&entry->by_ckch_store);
William Lallemandc756bbd2020-05-13 17:23:59 +02001494
1495 list_for_each_entry_safe(inst, inst_s, &entry->ckch_inst, by_crtlist_entry) {
1496 struct sni_ctx *sni, *sni_s;
Remi Tricot-Le Breton4458b972021-02-19 17:41:55 +01001497 struct ckch_inst_link_ref *link_ref, *link_ref_s;
William Lallemandc756bbd2020-05-13 17:23:59 +02001498
1499 HA_RWLOCK_WRLOCK(SNI_LOCK, &inst->bind_conf->sni_lock);
1500 list_for_each_entry_safe(sni, sni_s, &inst->sni_ctx, by_ckch_inst) {
1501 ebmb_delete(&sni->name);
Willy Tarreau2b718102021-04-21 07:32:39 +02001502 LIST_DELETE(&sni->by_ckch_inst);
William Lallemandc756bbd2020-05-13 17:23:59 +02001503 SSL_CTX_free(sni->ctx);
1504 free(sni);
1505 }
1506 HA_RWLOCK_WRUNLOCK(SNI_LOCK, &inst->bind_conf->sni_lock);
Willy Tarreau2b718102021-04-21 07:32:39 +02001507 LIST_DELETE(&inst->by_ckchs);
Remi Tricot-Le Breton4458b972021-02-19 17:41:55 +01001508 list_for_each_entry_safe(link_ref, link_ref_s, &inst->cafile_link_refs, list) {
1509 LIST_DELETE(&link_ref->link->list);
1510 LIST_DELETE(&link_ref->list);
1511 free(link_ref);
1512 }
William Lallemandc756bbd2020-05-13 17:23:59 +02001513 free(inst);
1514 }
1515
1516 crtlist_free_filters(entry->filters);
1517 ssl_sock_free_ssl_conf(entry->ssl_conf);
1518 free(entry->ssl_conf);
1519 free(entry);
1520
1521 HA_SPIN_UNLOCK(CKCH_LOCK, &ckch_lock);
1522 err = memprintf(&err, "Entry '%s' deleted in crtlist '%s'!\n", cert_path, crtlist_path);
1523 return cli_dynmsg(appctx, LOG_NOTICE, err);
1524
1525error:
1526 HA_SPIN_UNLOCK(CKCH_LOCK, &ckch_lock);
1527 err = memprintf(&err, "Can't delete the entry: %s\n", err ? err : "");
1528 return cli_dynerr(appctx, err);
1529}
1530
1531
William Lallemandee8530c2020-06-23 18:19:42 +02001532/* unlink and free all crt-list and crt-list entries */
1533void crtlist_deinit()
1534{
1535 struct eb_node *node, *next;
1536 struct crtlist *crtlist;
1537
1538 node = eb_first(&crtlists_tree);
1539 while (node) {
1540 next = eb_next(node);
1541 crtlist = ebmb_entry(node, struct crtlist, node);
1542 crtlist_free(crtlist);
1543 node = next;
1544 }
1545}
1546
William Lallemandc756bbd2020-05-13 17:23:59 +02001547
1548/* register cli keywords */
1549static struct cli_kw_list cli_kws = {{ },{
Willy Tarreaub205bfd2021-05-07 11:38:37 +02001550 { { "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 },
1551 { { "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 },
1552 { { "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 +02001553 { { NULL }, NULL, NULL, NULL } }
1554};
1555
1556INITCALL1(STG_REGISTER, cli_register_kw, &cli_kws);
1557