blob: 8aa4eae2a6e508b2f0ea6a761254bca94a9f1557 [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>
Willy Tarreau209108d2020-06-04 20:30:20 +020030#include <haproxy/ssl_sock.h>
Willy Tarreaucb086c62022-05-27 09:47:12 +020031#include <haproxy/stconn.h>
Willy Tarreau48fbcae2020-06-03 18:09:46 +020032#include <haproxy/tools.h>
William Lallemand6e9556b2020-05-12 17:52:44 +020033
Willy Tarreaua2fcca02022-05-05 11:53:23 +020034/* CLI context for "show ssl crt-list" or "dump ssl crt-list" */
35struct show_crtlist_ctx {
36 struct ebmb_node *crtlist_node; /* ebmb_node for the current crtlist */
37 struct crtlist_entry *entry; /* current entry */
38 int mode; /* 'd' for dump, 's' for show */
39};
William Lallemand6e9556b2020-05-12 17:52:44 +020040
Willy Tarreau6b6c3632022-05-05 13:43:49 +020041/* CLI context for "add ssl crt-list" */
42struct add_crtlist_ctx {
43 struct crtlist *crtlist;
44 struct crtlist_entry *entry;
45 struct bind_conf_list *bind_conf_node;
Christopher Fauletc642d7c2022-06-01 16:31:09 +020046 char *err;
Willy Tarreaufa11df52022-05-05 13:48:40 +020047 enum {
48 ADDCRT_ST_INIT = 0,
49 ADDCRT_ST_GEN,
50 ADDCRT_ST_INSERT,
Christopher Fauletc642d7c2022-06-01 16:31:09 +020051 ADDCRT_ST_SUCCESS,
52 ADDCRT_ST_ERROR,
Willy Tarreaufa11df52022-05-05 13:48:40 +020053 ADDCRT_ST_FIN,
54 } state;
Willy Tarreau6b6c3632022-05-05 13:43:49 +020055};
56
William Lallemand6e9556b2020-05-12 17:52:44 +020057/* release ssl bind conf */
58void ssl_sock_free_ssl_conf(struct ssl_bind_conf *conf)
59{
60 if (conf) {
61#if defined(OPENSSL_NPN_NEGOTIATED) && !defined(OPENSSL_NO_NEXTPROTONEG)
Willy Tarreau61cfdf42021-02-20 10:46:51 +010062 ha_free(&conf->npn_str);
William Lallemand6e9556b2020-05-12 17:52:44 +020063#endif
64#ifdef TLSEXT_TYPE_application_layer_protocol_negotiation
Willy Tarreau61cfdf42021-02-20 10:46:51 +010065 ha_free(&conf->alpn_str);
William Lallemand6e9556b2020-05-12 17:52:44 +020066#endif
Willy Tarreau61cfdf42021-02-20 10:46:51 +010067 ha_free(&conf->ca_file);
68 ha_free(&conf->ca_verify_file);
69 ha_free(&conf->crl_file);
70 ha_free(&conf->ciphers);
Ilya Shipitsinf34ed0b2020-11-21 14:37:34 +050071#ifdef HAVE_SSL_CTX_SET_CIPHERSUITES
Willy Tarreau61cfdf42021-02-20 10:46:51 +010072 ha_free(&conf->ciphersuites);
William Lallemand6e9556b2020-05-12 17:52:44 +020073#endif
Willy Tarreau61cfdf42021-02-20 10:46:51 +010074 ha_free(&conf->curves);
75 ha_free(&conf->ecdhe);
William Lallemand6e9556b2020-05-12 17:52:44 +020076 }
77}
78
William Lallemand82f2d2f2020-09-10 19:06:43 +020079/*
80 * Allocate and copy a ssl_bind_conf structure
81 */
82struct ssl_bind_conf *crtlist_dup_ssl_conf(struct ssl_bind_conf *src)
83{
84 struct ssl_bind_conf *dst;
85
86 if (!src)
87 return NULL;
88
89 dst = calloc(1, sizeof(*dst));
90 if (!dst)
91 return NULL;
92
93#if defined(OPENSSL_NPN_NEGOTIATED) && !defined(OPENSSL_NO_NEXTPROTONEG)
94 if (src->npn_str) {
95 dst->npn_str = strdup(src->npn_str);
96 if (!dst->npn_str)
97 goto error;
98 }
99#endif
100#ifdef TLSEXT_TYPE_application_layer_protocol_negotiation
101 if (src->alpn_str) {
102 dst->alpn_str = strdup(src->alpn_str);
103 if (!dst->alpn_str)
104 goto error;
105 }
106#endif
107 if (src->ca_file) {
108 dst->ca_file = strdup(src->ca_file);
109 if (!dst->ca_file)
110 goto error;
111 }
112 if (src->ca_verify_file) {
113 dst->ca_verify_file = strdup(src->ca_verify_file);
114 if (!dst->ca_verify_file)
115 goto error;
116 }
117 if (src->crl_file) {
118 dst->crl_file = strdup(src->crl_file);
119 if (!dst->crl_file)
120 goto error;
121 }
122 if (src->ciphers) {
123 dst->ciphers = strdup(src->ciphers);
124 if (!dst->ciphers)
125 goto error;
126 }
Ilya Shipitsinf34ed0b2020-11-21 14:37:34 +0500127#ifdef HAVE_SSL_CTX_SET_CIPHERSUITES
William Lallemand82f2d2f2020-09-10 19:06:43 +0200128 if (src->ciphersuites) {
129 dst->ciphersuites = strdup(src->ciphersuites);
130 if (!dst->ciphersuites)
131 goto error;
132 }
133#endif
134 if (src->curves) {
135 dst->curves = strdup(src->curves);
136 if (!dst->curves)
137 goto error;
138 }
139 if (src->ecdhe) {
140 dst->ecdhe = strdup(src->ecdhe);
141 if (!dst->ecdhe)
142 goto error;
143 }
144 return dst;
145
146error:
147 ssl_sock_free_ssl_conf(dst);
148 free(dst);
149
150 return NULL;
151}
William Lallemand6e9556b2020-05-12 17:52:44 +0200152
153/* free sni filters */
154void crtlist_free_filters(char **args)
155{
156 int i;
157
158 if (!args)
159 return;
160
161 for (i = 0; args[i]; i++)
162 free(args[i]);
163
164 free(args);
165}
166
167/* Alloc and duplicate a char ** array */
168char **crtlist_dup_filters(char **args, int fcount)
169{
170 char **dst;
171 int i;
172
173 if (fcount == 0)
174 return NULL;
175
176 dst = calloc(fcount + 1, sizeof(*dst));
177 if (!dst)
178 return NULL;
179
180 for (i = 0; i < fcount; i++) {
181 dst[i] = strdup(args[i]);
182 if (!dst[i])
183 goto error;
184 }
185 return dst;
186
187error:
188 crtlist_free_filters(dst);
189 return NULL;
190}
191
192/*
193 * Detach and free a crtlist_entry.
194 * Free the filters, the ssl_conf and call ckch_inst_free() for each ckch_inst
195 */
196void crtlist_entry_free(struct crtlist_entry *entry)
197{
198 struct ckch_inst *inst, *inst_s;
199
200 if (entry == NULL)
201 return;
202
203 ebpt_delete(&entry->node);
Willy Tarreau2b718102021-04-21 07:32:39 +0200204 LIST_DELETE(&entry->by_crtlist);
205 LIST_DELETE(&entry->by_ckch_store);
William Lallemand6e9556b2020-05-12 17:52:44 +0200206 crtlist_free_filters(entry->filters);
207 ssl_sock_free_ssl_conf(entry->ssl_conf);
208 free(entry->ssl_conf);
209 list_for_each_entry_safe(inst, inst_s, &entry->ckch_inst, by_crtlist_entry) {
210 ckch_inst_free(inst);
211 }
212 free(entry);
213}
William Lallemand5622c452020-09-10 19:08:49 +0200214/*
215 * Duplicate a crt_list entry and its content (ssl_conf, filters/fcount)
216 * Return a pointer to the new entry
217 */
218struct crtlist_entry *crtlist_entry_dup(struct crtlist_entry *src)
219{
220 struct crtlist_entry *entry;
221
222 if (src == NULL)
223 return NULL;
224
225 entry = crtlist_entry_new();
226 if (entry == NULL)
227 return NULL;
228
229 if (src->filters) {
230 entry->filters = crtlist_dup_filters(src->filters, src->fcount);
231 if (!entry->filters)
232 goto error;
233 }
234 entry->fcount = src->fcount;
235 if (src->ssl_conf) {
236 entry->ssl_conf = crtlist_dup_ssl_conf(src->ssl_conf);
237 if (!entry->ssl_conf)
238 goto error;
239 }
240 entry->crtlist = src->crtlist;
241
242 return entry;
243
244error:
245
246 crtlist_free_filters(entry->filters);
247 ssl_sock_free_ssl_conf(entry->ssl_conf);
248 free(entry->ssl_conf);
249 free(entry);
250
251 return NULL;
252}
William Lallemand6e9556b2020-05-12 17:52:44 +0200253
254/*
255 * Allocate and initialize a crtlist_entry
256 */
257struct crtlist_entry *crtlist_entry_new()
258{
259 struct crtlist_entry *entry;
260
261 entry = calloc(1, sizeof(*entry));
262 if (entry == NULL)
263 return NULL;
264
265 LIST_INIT(&entry->ckch_inst);
266
Willy Tarreau2b718102021-04-21 07:32:39 +0200267 /* initialize the nodes so we can LIST_DELETE in any cases */
William Lallemand6e9556b2020-05-12 17:52:44 +0200268 LIST_INIT(&entry->by_crtlist);
269 LIST_INIT(&entry->by_ckch_store);
270
271 return entry;
272}
273
274/* Free a crtlist, from the crt_entry to the content of the ssl_conf */
275void crtlist_free(struct crtlist *crtlist)
276{
277 struct crtlist_entry *entry, *s_entry;
William Lallemand6a3168a2020-06-23 11:43:35 +0200278 struct bind_conf_list *bind_conf_node;
William Lallemand6e9556b2020-05-12 17:52:44 +0200279
280 if (crtlist == NULL)
281 return;
282
William Lallemand6a3168a2020-06-23 11:43:35 +0200283 bind_conf_node = crtlist->bind_conf;
284 while (bind_conf_node) {
285 struct bind_conf_list *next = bind_conf_node->next;
286 free(bind_conf_node);
287 bind_conf_node = next;
288 }
289
William Lallemand6e9556b2020-05-12 17:52:44 +0200290 list_for_each_entry_safe(entry, s_entry, &crtlist->ord_entries, by_crtlist) {
291 crtlist_entry_free(entry);
292 }
293 ebmb_delete(&crtlist->node);
294 free(crtlist);
295}
296
297/* Alloc and initialize a struct crtlist
298 * <filename> is the key of the ebmb_node
299 * <unique> initialize the list of entries to be unique (1) or not (0)
300 */
301struct crtlist *crtlist_new(const char *filename, int unique)
302{
303 struct crtlist *newlist;
304
305 newlist = calloc(1, sizeof(*newlist) + strlen(filename) + 1);
306 if (newlist == NULL)
307 return NULL;
308
309 memcpy(newlist->node.key, filename, strlen(filename) + 1);
310 if (unique)
311 newlist->entries = EB_ROOT_UNIQUE;
312 else
313 newlist->entries = EB_ROOT;
314
315 LIST_INIT(&newlist->ord_entries);
316
317 return newlist;
318}
319
320/*
321 * Read a single crt-list line. /!\ alter the <line> string.
322 * Fill <crt_path> and <crtlist_entry>
323 * <crtlist_entry> must be alloc and free by the caller
324 * <crtlist_entry->ssl_conf> is alloc by the function
325 * <crtlist_entry->filters> is alloc by the function
326 * <crt_path> is a ptr in <line>
327 * Return an error code
328 */
Remi Tricot-Le Bretonfb00f312021-03-23 16:41:53 +0100329int 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 +0200330{
331 int cfgerr = 0;
332 int arg, newarg, cur_arg, i, ssl_b = 0, ssl_e = 0;
333 char *end;
334 char *args[MAX_CRT_ARGS + 1];
335 struct ssl_bind_conf *ssl_conf = NULL;
336
337 if (!line || !crt_path || !entry)
338 return ERR_ALERT | ERR_FATAL;
339
340 end = line + strlen(line);
341 if (end-line >= CRT_LINESIZE-1 && *(end-1) != '\n') {
342 /* Check if we reached the limit and the last char is not \n.
343 * Watch out for the last line without the terminating '\n'!
344 */
Tim Duesterhus6d07fae2020-09-29 18:00:27 +0200345 memprintf(err, "parsing [%s:%d]: line too long, limit is %d characters",
346 file, linenum, CRT_LINESIZE-1);
William Lallemand6e9556b2020-05-12 17:52:44 +0200347 cfgerr |= ERR_ALERT | ERR_FATAL;
348 goto error;
349 }
350 arg = 0;
351 newarg = 1;
352 while (*line) {
353 if (isspace((unsigned char)*line)) {
354 newarg = 1;
355 *line = 0;
356 } else if (*line == '[') {
357 if (ssl_b) {
Tim Duesterhus6d07fae2020-09-29 18:00:27 +0200358 memprintf(err, "parsing [%s:%d]: too many '['", file, linenum);
William Lallemand6e9556b2020-05-12 17:52:44 +0200359 cfgerr |= ERR_ALERT | ERR_FATAL;
360 goto error;
361 }
362 if (!arg) {
Tim Duesterhus6d07fae2020-09-29 18:00:27 +0200363 memprintf(err, "parsing [%s:%d]: file must start with a cert", file, linenum);
William Lallemand6e9556b2020-05-12 17:52:44 +0200364 cfgerr |= ERR_ALERT | ERR_FATAL;
365 goto error;
366 }
367 ssl_b = arg;
368 newarg = 1;
369 *line = 0;
370 } else if (*line == ']') {
371 if (ssl_e) {
Tim Duesterhus6d07fae2020-09-29 18:00:27 +0200372 memprintf(err, "parsing [%s:%d]: too many ']'", file, linenum);
William Lallemand6e9556b2020-05-12 17:52:44 +0200373 cfgerr |= ERR_ALERT | ERR_FATAL;
374 goto error;
375 }
376 if (!ssl_b) {
Tim Duesterhus6d07fae2020-09-29 18:00:27 +0200377 memprintf(err, "parsing [%s:%d]: missing '['", file, linenum);
William Lallemand6e9556b2020-05-12 17:52:44 +0200378 cfgerr |= ERR_ALERT | ERR_FATAL;
379 goto error;
380 }
381 ssl_e = arg;
382 newarg = 1;
383 *line = 0;
384 } else if (newarg) {
385 if (arg == MAX_CRT_ARGS) {
Tim Duesterhus6d07fae2020-09-29 18:00:27 +0200386 memprintf(err, "parsing [%s:%d]: too many args ", file, linenum);
William Lallemand6e9556b2020-05-12 17:52:44 +0200387 cfgerr |= ERR_ALERT | ERR_FATAL;
388 goto error;
389 }
390 newarg = 0;
391 args[arg++] = line;
392 }
393 line++;
394 }
395 args[arg++] = line;
396
397 /* empty line */
398 if (!*args[0]) {
399 cfgerr |= ERR_NONE;
400 goto error;
401 }
402
403 *crt_path = args[0];
404
405 if (ssl_b) {
406 ssl_conf = calloc(1, sizeof *ssl_conf);
407 if (!ssl_conf) {
408 memprintf(err, "not enough memory!");
409 cfgerr |= ERR_ALERT | ERR_FATAL;
410 goto error;
411 }
412 }
Remi Tricot-Le Bretonfb00f312021-03-23 16:41:53 +0100413
William Lallemand6e9556b2020-05-12 17:52:44 +0200414 cur_arg = ssl_b ? ssl_b : 1;
415 while (cur_arg < ssl_e) {
416 newarg = 0;
417 for (i = 0; ssl_bind_kws[i].kw != NULL; i++) {
418 if (strcmp(ssl_bind_kws[i].kw, args[cur_arg]) == 0) {
419 newarg = 1;
Remi Tricot-Le Bretonfb00f312021-03-23 16:41:53 +0100420 cfgerr |= ssl_bind_kws[i].parse(args, cur_arg, NULL, ssl_conf, from_cli, err);
William Lallemand6e9556b2020-05-12 17:52:44 +0200421 if (cur_arg + 1 + ssl_bind_kws[i].skip > ssl_e) {
Tim Duesterhus6d07fae2020-09-29 18:00:27 +0200422 memprintf(err, "parsing [%s:%d]: ssl args out of '[]' for %s",
423 file, linenum, args[cur_arg]);
William Lallemand6e9556b2020-05-12 17:52:44 +0200424 cfgerr |= ERR_ALERT | ERR_FATAL;
425 goto error;
426 }
427 cur_arg += 1 + ssl_bind_kws[i].skip;
428 break;
429 }
430 }
431 if (!cfgerr && !newarg) {
Tim Duesterhus6d07fae2020-09-29 18:00:27 +0200432 memprintf(err, "parsing [%s:%d]: unknown ssl keyword %s",
433 file, linenum, args[cur_arg]);
William Lallemand6e9556b2020-05-12 17:52:44 +0200434 cfgerr |= ERR_ALERT | ERR_FATAL;
435 goto error;
436 }
437 }
438 entry->linenum = linenum;
439 entry->ssl_conf = ssl_conf;
440 entry->filters = crtlist_dup_filters(&args[cur_arg], arg - cur_arg - 1);
441 entry->fcount = arg - cur_arg - 1;
442
443 return cfgerr;
444
445error:
446 crtlist_free_filters(entry->filters);
447 entry->filters = NULL;
448 ssl_sock_free_ssl_conf(entry->ssl_conf);
Willy Tarreau61cfdf42021-02-20 10:46:51 +0100449 ha_free(&entry->ssl_conf);
William Lallemand6e9556b2020-05-12 17:52:44 +0200450 return cfgerr;
451}
452
453
454
455/* This function parse a crt-list file and store it in a struct crtlist, each line is a crtlist_entry structure
456 * Fill the <crtlist> argument with a pointer to a new crtlist struct
457 *
458 * This function tries to open and store certificate files.
459 */
460int crtlist_parse_file(char *file, struct bind_conf *bind_conf, struct proxy *curproxy, struct crtlist **crtlist, char **err)
461{
462 struct crtlist *newlist;
463 struct crtlist_entry *entry = NULL;
464 char thisline[CRT_LINESIZE];
William Lallemand6e9556b2020-05-12 17:52:44 +0200465 FILE *f;
466 struct stat buf;
467 int linenum = 0;
468 int cfgerr = 0;
Tim Duesterhusb9f6acc2020-09-29 18:00:28 +0200469 int missing_lf = -1;
William Lallemand6e9556b2020-05-12 17:52:44 +0200470
471 if ((f = fopen(file, "r")) == NULL) {
472 memprintf(err, "cannot open file '%s' : %s", file, strerror(errno));
473 return ERR_ALERT | ERR_FATAL;
474 }
475
476 newlist = crtlist_new(file, 0);
477 if (newlist == NULL) {
478 memprintf(err, "Not enough memory!");
479 cfgerr |= ERR_ALERT | ERR_FATAL;
480 goto error;
481 }
482
483 while (fgets(thisline, sizeof(thisline), f) != NULL) {
484 char *end;
485 char *line = thisline;
486 char *crt_path;
William Lallemand86c2dd62020-11-20 14:23:38 +0100487 char path[MAXPATHLEN+1];
William Lallemand6e9556b2020-05-12 17:52:44 +0200488 struct ckch_store *ckchs;
William Lallemand77e1c6f2020-11-20 18:26:09 +0100489 int found = 0;
William Lallemand6e9556b2020-05-12 17:52:44 +0200490
Tim Duesterhusb9f6acc2020-09-29 18:00:28 +0200491 if (missing_lf != -1) {
492 memprintf(err, "parsing [%s:%d]: Stray NUL character at position %d.\n",
493 file, linenum, (missing_lf + 1));
494 cfgerr |= ERR_ALERT | ERR_FATAL;
495 missing_lf = -1;
496 break;
497 }
498
William Lallemand6e9556b2020-05-12 17:52:44 +0200499 linenum++;
500 end = line + strlen(line);
501 if (end-line == sizeof(thisline)-1 && *(end-1) != '\n') {
502 /* Check if we reached the limit and the last char is not \n.
503 * Watch out for the last line without the terminating '\n'!
504 */
Tim Duesterhus6d07fae2020-09-29 18:00:27 +0200505 memprintf(err, "parsing [%s:%d]: line too long, limit is %d characters",
506 file, linenum, (int)sizeof(thisline)-1);
William Lallemand6e9556b2020-05-12 17:52:44 +0200507 cfgerr |= ERR_ALERT | ERR_FATAL;
508 break;
509 }
510
511 if (*line == '#' || *line == '\n' || *line == '\r')
512 continue;
513
Tim Duesterhusb9f6acc2020-09-29 18:00:28 +0200514 if (end > line && *(end-1) == '\n') {
515 /* kill trailing LF */
516 *(end - 1) = 0;
517 }
518 else {
519 /* mark this line as truncated */
520 missing_lf = end - line;
521 }
522
William Lallemand6e9556b2020-05-12 17:52:44 +0200523 entry = crtlist_entry_new();
524 if (entry == NULL) {
525 memprintf(err, "Not enough memory!");
526 cfgerr |= ERR_ALERT | ERR_FATAL;
527 goto error;
528 }
Tim Duesterhusb9f6acc2020-09-29 18:00:28 +0200529
Remi Tricot-Le Bretonfb00f312021-03-23 16:41:53 +0100530 cfgerr |= crtlist_parse_line(thisline, &crt_path, entry, file, linenum, 0, err);
William Lallemand20b0fed2020-09-28 15:45:16 +0200531 if (cfgerr & ERR_CODE)
William Lallemand6e9556b2020-05-12 17:52:44 +0200532 goto error;
533
534 /* empty line */
535 if (!crt_path || !*crt_path) {
536 crtlist_entry_free(entry);
537 entry = NULL;
538 continue;
539 }
540
541 if (*crt_path != '/' && global_ssl.crt_base) {
Willy Tarreau393e42a2022-05-09 10:31:28 +0200542 if ((strlen(global_ssl.crt_base) + 1 + strlen(crt_path)) > sizeof(path) ||
Willy Tarreau63fc9002022-05-09 21:14:04 +0200543 snprintf(path, sizeof(path), "%s/%s", global_ssl.crt_base, crt_path) > sizeof(path)) {
Tim Duesterhus6d07fae2020-09-29 18:00:27 +0200544 memprintf(err, "parsing [%s:%d]: '%s' : path too long",
545 file, linenum, crt_path);
William Lallemand6e9556b2020-05-12 17:52:44 +0200546 cfgerr |= ERR_ALERT | ERR_FATAL;
547 goto error;
548 }
William Lallemand6e9556b2020-05-12 17:52:44 +0200549 crt_path = path;
550 }
551
552 /* Look for a ckch_store or create one */
553 ckchs = ckchs_lookup(crt_path);
554 if (ckchs == NULL) {
William Lallemand47da8212020-09-10 19:13:27 +0200555 if (stat(crt_path, &buf) == 0) {
William Lallemand77e1c6f2020-11-20 18:26:09 +0100556 found++;
William Lallemand47da8212020-09-10 19:13:27 +0200557
William Lallemandbd8e6ed2020-09-16 16:08:08 +0200558 ckchs = ckchs_load_cert_file(crt_path, err);
William Lallemand47da8212020-09-10 19:13:27 +0200559 if (ckchs == NULL) {
560 cfgerr |= ERR_ALERT | ERR_FATAL;
561 goto error;
562 }
563
564 entry->node.key = ckchs;
565 entry->crtlist = newlist;
566 ebpt_insert(&newlist->entries, &entry->node);
Willy Tarreau2b718102021-04-21 07:32:39 +0200567 LIST_APPEND(&newlist->ord_entries, &entry->by_crtlist);
568 LIST_APPEND(&ckchs->crtlist_entry, &entry->by_ckch_store);
William Lallemand6e9556b2020-05-12 17:52:44 +0200569
William Lallemand73404572020-11-20 18:23:40 +0100570 } else if (global_ssl.extra_files & SSL_GF_BUNDLE) {
William Lallemand47da8212020-09-10 19:13:27 +0200571 /* If we didn't find the file, this could be a
William Lallemand51f784b2020-10-02 18:08:18 +0200572 bundle, since 2.3 we don't support multiple
573 certificate in the same OpenSSL store, so we
574 emulate it by loading each file separately. To
575 do so we need to duplicate the entry in the
576 crt-list because it becomes independent */
William Lallemand47da8212020-09-10 19:13:27 +0200577 char fp[MAXPATHLEN+1] = {0};
578 int n = 0;
579 struct crtlist_entry *entry_dup = entry; /* use the previous created entry */
580 for (n = 0; n < SSL_SOCK_NUM_KEYTYPES; n++) {
581 struct stat buf;
582 int ret;
583
William Lallemand86c2dd62020-11-20 14:23:38 +0100584 ret = snprintf(fp, sizeof(fp), "%s.%s", crt_path, SSL_SOCK_KEYTYPE_NAMES[n]);
William Lallemand47da8212020-09-10 19:13:27 +0200585 if (ret > sizeof(fp))
586 continue;
587
588 ckchs = ckchs_lookup(fp);
William Lallemand77e1c6f2020-11-20 18:26:09 +0100589 if (!ckchs) {
590 if (stat(fp, &buf) == 0) {
591 ckchs = ckchs_load_cert_file(fp, err);
592 if (!ckchs) {
William Lallemand47da8212020-09-10 19:13:27 +0200593 cfgerr |= ERR_ALERT | ERR_FATAL;
594 goto error;
595 }
William Lallemand77e1c6f2020-11-20 18:26:09 +0100596 } else {
597 continue; /* didn't find this extension, skip */
598 }
599 }
600 found++;
601 linenum++; /* we duplicate the line for this entry in the bundle */
602 if (!entry_dup) { /* if the entry was used, duplicate one */
603 linenum++;
604 entry_dup = crtlist_entry_dup(entry);
605 if (!entry_dup) {
606 cfgerr |= ERR_ALERT | ERR_FATAL;
607 goto error;
William Lallemand47da8212020-09-10 19:13:27 +0200608 }
William Lallemand77e1c6f2020-11-20 18:26:09 +0100609 entry_dup->linenum = linenum;
610 }
William Lallemand47da8212020-09-10 19:13:27 +0200611
William Lallemand77e1c6f2020-11-20 18:26:09 +0100612 entry_dup->node.key = ckchs;
613 entry_dup->crtlist = newlist;
614 ebpt_insert(&newlist->entries, &entry_dup->node);
Willy Tarreau2b718102021-04-21 07:32:39 +0200615 LIST_APPEND(&newlist->ord_entries, &entry_dup->by_crtlist);
616 LIST_APPEND(&ckchs->crtlist_entry, &entry_dup->by_ckch_store);
William Lallemand47da8212020-09-10 19:13:27 +0200617
William Lallemand77e1c6f2020-11-20 18:26:09 +0100618 entry_dup = NULL; /* the entry was used, we need a new one next round */
William Lallemand47da8212020-09-10 19:13:27 +0200619 }
William Lallemandb7fdfdf2020-12-04 15:45:02 +0100620#if HA_OPENSSL_VERSION_NUMBER < 0x10101000L
621 if (found) {
622 memprintf(err, "%sCan't load '%s'. Loading a multi certificates bundle requires OpenSSL >= 1.1.1\n",
623 err && *err ? *err : "", crt_path);
624 cfgerr |= ERR_ALERT | ERR_FATAL;
625 }
626#endif
William Lallemand47da8212020-09-10 19:13:27 +0200627 }
William Lallemand77e1c6f2020-11-20 18:26:09 +0100628 if (!found) {
629 memprintf(err, "%sunable to stat SSL certificate from file '%s' : %s.\n",
630 err && *err ? *err : "", crt_path, strerror(errno));
631 cfgerr |= ERR_ALERT | ERR_FATAL;
632 }
633
William Lallemand50c03aa2020-11-06 16:24:07 +0100634 } else {
635 entry->node.key = ckchs;
636 entry->crtlist = newlist;
637 ebpt_insert(&newlist->entries, &entry->node);
Willy Tarreau2b718102021-04-21 07:32:39 +0200638 LIST_APPEND(&newlist->ord_entries, &entry->by_crtlist);
639 LIST_APPEND(&ckchs->crtlist_entry, &entry->by_ckch_store);
William Lallemand77e1c6f2020-11-20 18:26:09 +0100640 found++;
William Lallemand47da8212020-09-10 19:13:27 +0200641 }
William Lallemand6e9556b2020-05-12 17:52:44 +0200642 entry = NULL;
643 }
Tim Duesterhusb9f6acc2020-09-29 18:00:28 +0200644
645 if (missing_lf != -1) {
646 memprintf(err, "parsing [%s:%d]: Missing LF on last line, file might have been truncated at position %d.\n",
647 file, linenum, (missing_lf + 1));
648 cfgerr |= ERR_ALERT | ERR_FATAL;
649 }
650
William Lallemand6e9556b2020-05-12 17:52:44 +0200651 if (cfgerr & ERR_CODE)
652 goto error;
653
654 newlist->linecount = linenum;
655
656 fclose(f);
657 *crtlist = newlist;
658
659 return cfgerr;
660error:
661 crtlist_entry_free(entry);
662
663 fclose(f);
664 crtlist_free(newlist);
665 return cfgerr;
666}
667
668/* This function reads a directory and stores it in a struct crtlist, each file is a crtlist_entry structure
669 * Fill the <crtlist> argument with a pointer to a new crtlist struct
670 *
671 * This function tries to open and store certificate files.
672 */
673int crtlist_load_cert_dir(char *path, struct bind_conf *bind_conf, struct crtlist **crtlist, char **err)
674{
675 struct crtlist *dir;
676 struct dirent **de_list;
677 int i, n;
678 struct stat buf;
679 char *end;
680 char fp[MAXPATHLEN+1];
681 int cfgerr = 0;
682 struct ckch_store *ckchs;
William Lallemand6e9556b2020-05-12 17:52:44 +0200683
684 dir = crtlist_new(path, 1);
685 if (dir == NULL) {
686 memprintf(err, "not enough memory");
687 return ERR_ALERT | ERR_FATAL;
688 }
689
690 n = scandir(path, &de_list, 0, alphasort);
691 if (n < 0) {
692 memprintf(err, "%sunable to scan directory '%s' : %s.\n",
693 err && *err ? *err : "", path, strerror(errno));
694 cfgerr |= ERR_ALERT | ERR_FATAL;
695 }
696 else {
697 for (i = 0; i < n; i++) {
698 struct crtlist_entry *entry;
699 struct dirent *de = de_list[i];
700
701 end = strrchr(de->d_name, '.');
William Lallemand589570d2022-05-09 10:30:51 +0200702 if (end && (de->d_name[0] == '.' ||
703 strcmp(end, ".issuer") == 0 || strcmp(end, ".ocsp") == 0 ||
704 strcmp(end, ".sctl") == 0 || strcmp(end, ".key") == 0))
William Lallemand6e9556b2020-05-12 17:52:44 +0200705 goto ignore_entry;
706
707 snprintf(fp, sizeof(fp), "%s/%s", path, de->d_name);
708 if (stat(fp, &buf) != 0) {
709 memprintf(err, "%sunable to stat SSL certificate from file '%s' : %s.\n",
710 err && *err ? *err : "", fp, strerror(errno));
711 cfgerr |= ERR_ALERT | ERR_FATAL;
712 goto ignore_entry;
713 }
714 if (!S_ISREG(buf.st_mode))
715 goto ignore_entry;
716
717 entry = crtlist_entry_new();
718 if (entry == NULL) {
719 memprintf(err, "not enough memory '%s'", fp);
720 cfgerr |= ERR_ALERT | ERR_FATAL;
721 goto ignore_entry;
722 }
723
William Lallemand6e9556b2020-05-12 17:52:44 +0200724 ckchs = ckchs_lookup(fp);
725 if (ckchs == NULL)
William Lallemandbd8e6ed2020-09-16 16:08:08 +0200726 ckchs = ckchs_load_cert_file(fp, err);
William Lallemand6e9556b2020-05-12 17:52:44 +0200727 if (ckchs == NULL) {
728 free(de);
729 free(entry);
730 cfgerr |= ERR_ALERT | ERR_FATAL;
731 goto end;
732 }
733 entry->node.key = ckchs;
734 entry->crtlist = dir;
Willy Tarreau2b718102021-04-21 07:32:39 +0200735 LIST_APPEND(&ckchs->crtlist_entry, &entry->by_ckch_store);
736 LIST_APPEND(&dir->ord_entries, &entry->by_crtlist);
William Lallemand6e9556b2020-05-12 17:52:44 +0200737 ebpt_insert(&dir->entries, &entry->node);
738
739ignore_entry:
740 free(de);
741 }
742end:
743 free(de_list);
744 }
745
746 if (cfgerr & ERR_CODE) {
747 /* free the dir and entries on error */
748 crtlist_free(dir);
749 } else {
750 *crtlist = dir;
751 }
752 return cfgerr;
753
754}
755
William Lallemandc756bbd2020-05-13 17:23:59 +0200756/*
757 * Take an ssl_bind_conf structure and append the configuration line used to
758 * create it in the buffer
759 */
760static void dump_crtlist_sslconf(struct buffer *buf, const struct ssl_bind_conf *conf)
761{
762 int space = 0;
763
764 if (conf == NULL)
765 return;
766
767 chunk_appendf(buf, " [");
768#ifdef OPENSSL_NPN_NEGOTIATED
769 if (conf->npn_str) {
770 int len = conf->npn_len;
771 char *ptr = conf->npn_str;
772 int comma = 0;
773
774 if (space) chunk_appendf(buf, " ");
775 chunk_appendf(buf, "npn ");
776 while (len) {
777 unsigned short size;
778
779 size = *ptr;
780 ptr++;
781 if (comma)
782 chunk_memcat(buf, ",", 1);
783 chunk_memcat(buf, ptr, size);
784 ptr += size;
785 len -= size + 1;
786 comma = 1;
787 }
788 chunk_memcat(buf, "", 1); /* finish with a \0 */
789 space++;
790 }
791#endif
792#ifdef TLSEXT_TYPE_application_layer_protocol_negotiation
793 if (conf->alpn_str) {
794 int len = conf->alpn_len;
795 char *ptr = conf->alpn_str;
796 int comma = 0;
797
798 if (space) chunk_appendf(buf, " ");
799 chunk_appendf(buf, "alpn ");
800 while (len) {
801 unsigned short size;
802
803 size = *ptr;
804 ptr++;
805 if (comma)
806 chunk_memcat(buf, ",", 1);
807 chunk_memcat(buf, ptr, size);
808 ptr += size;
809 len -= size + 1;
810 comma = 1;
811 }
812 chunk_memcat(buf, "", 1); /* finish with a \0 */
813 space++;
814 }
815#endif
816 /* verify */
817 {
818 if (conf->verify == SSL_SOCK_VERIFY_NONE) {
819 if (space) chunk_appendf(buf, " ");
820 chunk_appendf(buf, "verify none");
821 space++;
822 } else if (conf->verify == SSL_SOCK_VERIFY_OPTIONAL) {
823 if (space) chunk_appendf(buf, " ");
824 chunk_appendf(buf, "verify optional");
825 space++;
826 } else if (conf->verify == SSL_SOCK_VERIFY_REQUIRED) {
827 if (space) chunk_appendf(buf, " ");
828 chunk_appendf(buf, "verify required");
829 space++;
830 }
831 }
832
833 if (conf->no_ca_names) {
834 if (space) chunk_appendf(buf, " ");
835 chunk_appendf(buf, "no-ca-names");
836 space++;
837 }
838
839 if (conf->early_data) {
840 if (space) chunk_appendf(buf, " ");
841 chunk_appendf(buf, "allow-0rtt");
842 space++;
843 }
844 if (conf->ca_file) {
845 if (space) chunk_appendf(buf, " ");
846 chunk_appendf(buf, "ca-file %s", conf->ca_file);
847 space++;
848 }
849 if (conf->crl_file) {
850 if (space) chunk_appendf(buf, " ");
851 chunk_appendf(buf, "crl-file %s", conf->crl_file);
852 space++;
853 }
854 if (conf->ciphers) {
855 if (space) chunk_appendf(buf, " ");
856 chunk_appendf(buf, "ciphers %s", conf->ciphers);
857 space++;
858 }
Ilya Shipitsinf34ed0b2020-11-21 14:37:34 +0500859#ifdef HAVE_SSL_CTX_SET_CIPHERSUITES
William Lallemandc756bbd2020-05-13 17:23:59 +0200860 if (conf->ciphersuites) {
861 if (space) chunk_appendf(buf, " ");
862 chunk_appendf(buf, "ciphersuites %s", conf->ciphersuites);
863 space++;
864 }
865#endif
866 if (conf->curves) {
867 if (space) chunk_appendf(buf, " ");
868 chunk_appendf(buf, "curves %s", conf->curves);
869 space++;
870 }
871 if (conf->ecdhe) {
872 if (space) chunk_appendf(buf, " ");
873 chunk_appendf(buf, "ecdhe %s", conf->ecdhe);
874 space++;
875 }
876
877 /* the crt-lists only support ssl-min-ver and ssl-max-ver */
William Lallemand8177ad92020-05-20 16:49:02 +0200878 if (conf->ssl_methods_cfg.min) {
William Lallemandc756bbd2020-05-13 17:23:59 +0200879 if (space) chunk_appendf(buf, " ");
William Lallemand8177ad92020-05-20 16:49:02 +0200880 chunk_appendf(buf, "ssl-min-ver %s", methodVersions[conf->ssl_methods_cfg.min].name);
William Lallemandc756bbd2020-05-13 17:23:59 +0200881 space++;
882 }
883
William Lallemand8177ad92020-05-20 16:49:02 +0200884 if (conf->ssl_methods_cfg.max) {
William Lallemandc756bbd2020-05-13 17:23:59 +0200885 if (space) chunk_appendf(buf, " ");
William Lallemand8177ad92020-05-20 16:49:02 +0200886 chunk_appendf(buf, "ssl-max-ver %s", methodVersions[conf->ssl_methods_cfg.max].name);
William Lallemandc756bbd2020-05-13 17:23:59 +0200887 space++;
888 }
889
890 chunk_appendf(buf, "]");
891
892 return;
893}
894
895/* dump a list of filters */
896static void dump_crtlist_filters(struct buffer *buf, struct crtlist_entry *entry)
897{
898 int i;
899
900 if (!entry->fcount)
901 return;
902
903 for (i = 0; i < entry->fcount; i++) {
904 chunk_appendf(buf, " %s", entry->filters[i]);
905 }
906 return;
907}
908
909/************************** CLI functions ****************************/
910
911
Willy Tarreaua2fcca02022-05-05 11:53:23 +0200912/* CLI IO handler for '(show|dump) ssl crt-list'.
913 * It uses show_crtlist_ctx for the context.
914 */
William Lallemandc756bbd2020-05-13 17:23:59 +0200915static int cli_io_handler_dump_crtlist(struct appctx *appctx)
916{
Willy Tarreaua2fcca02022-05-05 11:53:23 +0200917 struct show_crtlist_ctx *ctx = appctx->svcctx;
William Lallemandc756bbd2020-05-13 17:23:59 +0200918 struct buffer *trash = alloc_trash_chunk();
William Lallemandc756bbd2020-05-13 17:23:59 +0200919 struct ebmb_node *lnode;
920
921 if (trash == NULL)
922 return 1;
923
924 /* dump the list of crt-lists */
Willy Tarreaua2fcca02022-05-05 11:53:23 +0200925 lnode = ctx->crtlist_node;
William Lallemandc756bbd2020-05-13 17:23:59 +0200926 if (lnode == NULL)
927 lnode = ebmb_first(&crtlists_tree);
928 while (lnode) {
929 chunk_appendf(trash, "%s\n", lnode->key);
Willy Tarreaud0a06d52022-05-18 15:07:19 +0200930 if (applet_putchk(appctx, trash) == -1)
William Lallemandc756bbd2020-05-13 17:23:59 +0200931 goto yield;
William Lallemandc756bbd2020-05-13 17:23:59 +0200932 lnode = ebmb_next(lnode);
933 }
934 free_trash_chunk(trash);
935 return 1;
936yield:
Willy Tarreaua2fcca02022-05-05 11:53:23 +0200937 ctx->crtlist_node = lnode;
William Lallemandc756bbd2020-05-13 17:23:59 +0200938 free_trash_chunk(trash);
939 return 0;
940}
941
942/* CLI IO handler for '(show|dump) ssl crt-list <filename>' */
943static int cli_io_handler_dump_crtlist_entries(struct appctx *appctx)
944{
Willy Tarreaua2fcca02022-05-05 11:53:23 +0200945 struct show_crtlist_ctx *ctx = appctx->svcctx;
William Lallemandc756bbd2020-05-13 17:23:59 +0200946 struct buffer *trash = alloc_trash_chunk();
947 struct crtlist *crtlist;
William Lallemandc756bbd2020-05-13 17:23:59 +0200948 struct crtlist_entry *entry;
949
950 if (trash == NULL)
951 return 1;
952
Willy Tarreaua2fcca02022-05-05 11:53:23 +0200953 crtlist = ebmb_entry(ctx->crtlist_node, struct crtlist, node);
William Lallemandc756bbd2020-05-13 17:23:59 +0200954
Willy Tarreaua2fcca02022-05-05 11:53:23 +0200955 entry = ctx->entry;
William Lallemandc756bbd2020-05-13 17:23:59 +0200956 if (entry == NULL) {
957 entry = LIST_ELEM((crtlist->ord_entries).n, typeof(entry), by_crtlist);
958 chunk_appendf(trash, "# %s\n", crtlist->node.key);
Willy Tarreaud0a06d52022-05-18 15:07:19 +0200959 if (applet_putchk(appctx, trash) == -1)
William Lallemandc756bbd2020-05-13 17:23:59 +0200960 goto yield;
William Lallemandc756bbd2020-05-13 17:23:59 +0200961 }
962
963 list_for_each_entry_from(entry, &crtlist->ord_entries, by_crtlist) {
964 struct ckch_store *store;
965 const char *filename;
966
967 store = entry->node.key;
968 filename = store->path;
969 chunk_appendf(trash, "%s", filename);
Willy Tarreaua2fcca02022-05-05 11:53:23 +0200970 if (ctx->mode == 's') /* show */
William Lallemandc756bbd2020-05-13 17:23:59 +0200971 chunk_appendf(trash, ":%d", entry->linenum);
972 dump_crtlist_sslconf(trash, entry->ssl_conf);
973 dump_crtlist_filters(trash, entry);
974 chunk_appendf(trash, "\n");
975
Willy Tarreaud0a06d52022-05-18 15:07:19 +0200976 if (applet_putchk(appctx, trash) == -1)
William Lallemandc756bbd2020-05-13 17:23:59 +0200977 goto yield;
William Lallemandc756bbd2020-05-13 17:23:59 +0200978 }
979 free_trash_chunk(trash);
980 return 1;
981yield:
Willy Tarreaua2fcca02022-05-05 11:53:23 +0200982 ctx->entry = entry;
William Lallemandc756bbd2020-05-13 17:23:59 +0200983 free_trash_chunk(trash);
984 return 0;
985}
986
987/* CLI argument parser for '(show|dump) ssl crt-list' */
988static int cli_parse_dump_crtlist(char **args, char *payload, struct appctx *appctx, void *private)
989{
Willy Tarreaua2fcca02022-05-05 11:53:23 +0200990 struct show_crtlist_ctx *ctx = applet_reserve_svcctx(appctx, sizeof(*ctx));
William Lallemandc756bbd2020-05-13 17:23:59 +0200991 struct ebmb_node *lnode;
992 char *filename = NULL;
993 int mode;
William Lallemand99cc2182020-06-25 15:19:51 +0200994 char *end;
William Lallemandc756bbd2020-05-13 17:23:59 +0200995
996 if (!cli_has_level(appctx, ACCESS_LVL_ADMIN))
997 return 1;
998
Tim Duesterhuse5ff1412021-01-02 22:31:53 +0100999 if (*args[3] && strcmp(args[3], "-n") == 0) {
William Lallemandc756bbd2020-05-13 17:23:59 +02001000 mode = 's';
1001 filename = args[4];
1002 } else {
1003 mode = 'd';
1004 filename = args[3];
1005 }
1006
1007 if (mode == 's' && !*args[4])
1008 return cli_err(appctx, "'show ssl crt-list -n' expects a filename or a directory\n");
1009
1010 if (filename && *filename) {
William Lallemand99cc2182020-06-25 15:19:51 +02001011
1012
1013 /* strip trailing slashes, including first one */
1014 for (end = filename + strlen(filename) - 1; end >= filename && *end == '/'; end--)
1015 *end = 0;
1016
William Lallemandc756bbd2020-05-13 17:23:59 +02001017 lnode = ebst_lookup(&crtlists_tree, filename);
1018 if (lnode == NULL)
1019 return cli_err(appctx, "didn't find the specified filename\n");
1020
Willy Tarreaua2fcca02022-05-05 11:53:23 +02001021 ctx->crtlist_node = lnode;
William Lallemandc756bbd2020-05-13 17:23:59 +02001022 appctx->io_handler = cli_io_handler_dump_crtlist_entries;
1023 }
Willy Tarreaua2fcca02022-05-05 11:53:23 +02001024 ctx->mode = mode;
William Lallemandc756bbd2020-05-13 17:23:59 +02001025
1026 return 0;
1027}
1028
1029/* release function of the "add ssl crt-list' command, free things and unlock
Willy Tarreau6b6c3632022-05-05 13:43:49 +02001030 * the spinlock. It uses the add_crtlist_ctx.
1031 */
William Lallemandc756bbd2020-05-13 17:23:59 +02001032static void cli_release_add_crtlist(struct appctx *appctx)
1033{
Willy Tarreau6b6c3632022-05-05 13:43:49 +02001034 struct add_crtlist_ctx *ctx = appctx->svcctx;
1035 struct crtlist_entry *entry = ctx->entry;
William Lallemandc756bbd2020-05-13 17:23:59 +02001036
Christopher Fauletc642d7c2022-06-01 16:31:09 +02001037 if (entry) {
William Lallemandc756bbd2020-05-13 17:23:59 +02001038 struct ckch_inst *inst, *inst_s;
Christopher Fauletc642d7c2022-06-01 16:31:09 +02001039
William Lallemandc756bbd2020-05-13 17:23:59 +02001040 /* upon error free the ckch_inst and everything inside */
1041 ebpt_delete(&entry->node);
Willy Tarreau2b718102021-04-21 07:32:39 +02001042 LIST_DELETE(&entry->by_crtlist);
1043 LIST_DELETE(&entry->by_ckch_store);
William Lallemandc756bbd2020-05-13 17:23:59 +02001044
1045 list_for_each_entry_safe(inst, inst_s, &entry->ckch_inst, by_ckchs) {
1046 ckch_inst_free(inst);
1047 }
1048 crtlist_free_filters(entry->filters);
1049 ssl_sock_free_ssl_conf(entry->ssl_conf);
1050 free(entry->ssl_conf);
1051 free(entry);
1052 }
William Lallemandc756bbd2020-05-13 17:23:59 +02001053 HA_SPIN_UNLOCK(CKCH_LOCK, &ckch_lock);
Christopher Fauletc642d7c2022-06-01 16:31:09 +02001054 ha_free(&ctx->err);
William Lallemandc756bbd2020-05-13 17:23:59 +02001055}
1056
1057
1058/* IO Handler for the "add ssl crt-list" command It adds a new entry in the
1059 * crt-list and generates the ckch_insts for each bind_conf that uses this crt-list
1060 *
1061 * The logic is the same as the "commit ssl cert" command but without the
1062 * freeing of the old structures, because there are none.
Willy Tarreau6b6c3632022-05-05 13:43:49 +02001063 *
1064 * It uses the add_crtlist_ctx for the context.
William Lallemandc756bbd2020-05-13 17:23:59 +02001065 */
1066static int cli_io_handler_add_crtlist(struct appctx *appctx)
1067{
Willy Tarreau6b6c3632022-05-05 13:43:49 +02001068 struct add_crtlist_ctx *ctx = appctx->svcctx;
William Lallemandc756bbd2020-05-13 17:23:59 +02001069 struct bind_conf_list *bind_conf_node;
Willy Tarreauc12b3212022-05-27 11:08:15 +02001070 struct stconn *sc = appctx_sc(appctx);
Willy Tarreau6b6c3632022-05-05 13:43:49 +02001071 struct crtlist *crtlist = ctx->crtlist;
1072 struct crtlist_entry *entry = ctx->entry;
William Lallemandc756bbd2020-05-13 17:23:59 +02001073 struct ckch_store *store = entry->node.key;
William Lallemandc756bbd2020-05-13 17:23:59 +02001074 struct ckch_inst *new_inst;
William Lallemandc756bbd2020-05-13 17:23:59 +02001075 int i = 0;
1076 int errcode = 0;
1077
William Lallemandc756bbd2020-05-13 17:23:59 +02001078 /* for each bind_conf which use the crt-list, a new ckch_inst must be
1079 * created.
1080 */
Willy Tarreau475e4632022-05-27 10:26:46 +02001081 if (unlikely(sc_ic(sc)->flags & (CF_WRITE_ERROR|CF_SHUTW)))
Christopher Fauletc642d7c2022-06-01 16:31:09 +02001082 goto end;
William Lallemandc756bbd2020-05-13 17:23:59 +02001083
Willy Tarreaufa11df52022-05-05 13:48:40 +02001084 switch (ctx->state) {
1085 case ADDCRT_ST_INIT:
Willy Tarreau1b948ef2022-05-05 13:50:46 +02001086 /* This state just print the update message */
Christopher Fauletc642d7c2022-06-01 16:31:09 +02001087 chunk_printf(&trash, "Inserting certificate '%s' in crt-list '%s'", store->path, crtlist->node.key);
1088 if (applet_putchk(appctx, &trash) == -1)
Willy Tarreau1b948ef2022-05-05 13:50:46 +02001089 goto yield;
Willy Tarreaufa11df52022-05-05 13:48:40 +02001090 ctx->state = ADDCRT_ST_GEN;
Willy Tarreau1b948ef2022-05-05 13:50:46 +02001091 /* fallthrough */
Willy Tarreaufa11df52022-05-05 13:48:40 +02001092 case ADDCRT_ST_GEN:
Willy Tarreau6b6c3632022-05-05 13:43:49 +02001093 bind_conf_node = ctx->bind_conf_node; /* get the previous ptr from the yield */
Willy Tarreau1b948ef2022-05-05 13:50:46 +02001094 if (bind_conf_node == NULL)
1095 bind_conf_node = crtlist->bind_conf;
1096 for (; bind_conf_node; bind_conf_node = bind_conf_node->next) {
1097 struct bind_conf *bind_conf = bind_conf_node->bind_conf;
1098 struct sni_ctx *sni;
1099
Christopher Fauletc642d7c2022-06-01 16:31:09 +02001100 ctx->bind_conf_node = bind_conf_node;
1101
Willy Tarreau1b948ef2022-05-05 13:50:46 +02001102 /* yield every 10 generations */
1103 if (i > 10) {
Christopher Fauletc642d7c2022-06-01 16:31:09 +02001104 applet_have_more_data(appctx); /* let's come back later */
Willy Tarreau1b948ef2022-05-05 13:50:46 +02001105 goto yield;
1106 }
William Lallemandc756bbd2020-05-13 17:23:59 +02001107
Christopher Fauletc642d7c2022-06-01 16:31:09 +02001108 /* display one dot for each new instance */
1109 if (applet_putstr(appctx, ".") == -1)
1110 goto yield;
1111
Willy Tarreau1b948ef2022-05-05 13:50:46 +02001112 /* we don't support multi-cert bundles, only simple ones */
Christopher Fauletc642d7c2022-06-01 16:31:09 +02001113 ctx->err = NULL;
1114 errcode |= ckch_inst_new_load_store(store->path, store, bind_conf, entry->ssl_conf, entry->filters, entry->fcount, &new_inst, &ctx->err);
1115 if (errcode & ERR_CODE) {
1116 ctx->state = ADDCRT_ST_ERROR;
Willy Tarreau1b948ef2022-05-05 13:50:46 +02001117 goto error;
Christopher Fauletc642d7c2022-06-01 16:31:09 +02001118 }
William Lallemandc756bbd2020-05-13 17:23:59 +02001119
Willy Tarreau1b948ef2022-05-05 13:50:46 +02001120 /* we need to initialize the SSL_CTX generated */
1121 /* this iterate on the newly generated SNIs in the new instance to prepare their SSL_CTX */
1122 list_for_each_entry(sni, &new_inst->sni_ctx, by_ckch_inst) {
1123 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 +02001124 ctx->err = NULL;
1125 errcode |= ssl_sock_prep_ctx_and_inst(bind_conf, new_inst->ssl_conf, sni->ctx, sni->ckch_inst, &ctx->err);
1126 if (errcode & ERR_CODE) {
1127 ctx->state = ADDCRT_ST_ERROR;
William Lallemandc756bbd2020-05-13 17:23:59 +02001128 goto error;
Christopher Fauletc642d7c2022-06-01 16:31:09 +02001129 }
William Lallemandc756bbd2020-05-13 17:23:59 +02001130 }
Willy Tarreau1b948ef2022-05-05 13:50:46 +02001131 }
Christopher Fauletc642d7c2022-06-01 16:31:09 +02001132
Willy Tarreau1b948ef2022-05-05 13:50:46 +02001133 i++;
1134 LIST_APPEND(&store->ckch_inst, &new_inst->by_ckchs);
1135 LIST_APPEND(&entry->ckch_inst, &new_inst->by_crtlist_entry);
1136 new_inst->crtlist_entry = entry;
William Lallemandc756bbd2020-05-13 17:23:59 +02001137 }
Willy Tarreaufa11df52022-05-05 13:48:40 +02001138 ctx->state = ADDCRT_ST_INSERT;
Willy Tarreau1b948ef2022-05-05 13:50:46 +02001139 /* fallthrough */
Willy Tarreaufa11df52022-05-05 13:48:40 +02001140 case ADDCRT_ST_INSERT:
Willy Tarreau1b948ef2022-05-05 13:50:46 +02001141 /* insert SNIs in bind_conf */
1142 list_for_each_entry(new_inst, &store->ckch_inst, by_ckchs) {
1143 HA_RWLOCK_WRLOCK(SNI_LOCK, &new_inst->bind_conf->sni_lock);
1144 ssl_sock_load_cert_sni(new_inst, new_inst->bind_conf);
1145 HA_RWLOCK_WRUNLOCK(SNI_LOCK, &new_inst->bind_conf->sni_lock);
1146 }
1147 entry->linenum = ++crtlist->linecount;
Christopher Fauletc642d7c2022-06-01 16:31:09 +02001148 ctx->entry = NULL;
1149 ctx->state = ADDCRT_ST_SUCCESS;
Willy Tarreaufa11df52022-05-05 13:48:40 +02001150 /* fallthrough */
Christopher Fauletc642d7c2022-06-01 16:31:09 +02001151 case ADDCRT_ST_SUCCESS:
1152 chunk_reset(&trash);
1153 chunk_appendf(&trash, "\n");
1154 if (ctx->err)
1155 chunk_appendf(&trash, "%s", ctx->err);
1156 chunk_appendf(&trash, "Success!\n");
1157 if (applet_putchk(appctx, &trash) == -1)
1158 goto yield;
1159 ctx->state = ADDCRT_ST_FIN;
1160 break;
1161
1162 case ADDCRT_ST_ERROR:
1163 error:
1164 chunk_printf(&trash, "\n%sFailed!\n", ctx->err);
1165 if (applet_putchk(appctx, &trash) == -1)
1166 goto yield;
1167 break;
1168
Willy Tarreaufa11df52022-05-05 13:48:40 +02001169 default:
1170 break;
William Lallemandc756bbd2020-05-13 17:23:59 +02001171 }
1172
Christopher Fauletc642d7c2022-06-01 16:31:09 +02001173end:
William Lallemandc756bbd2020-05-13 17:23:59 +02001174 /* success: call the release function and don't come back */
1175 return 1;
1176yield:
William Lallemandc756bbd2020-05-13 17:23:59 +02001177 return 0; /* should come back */
William Lallemandc756bbd2020-05-13 17:23:59 +02001178}
1179
1180
1181/*
1182 * Parse a "add ssl crt-list <crt-list> <certfile>" line.
Willy Tarreau6b6c3632022-05-05 13:43:49 +02001183 * Filters and option must be passed through payload.
1184 * It sets a struct add_crtlist_ctx.
William Lallemandc756bbd2020-05-13 17:23:59 +02001185 */
1186static int cli_parse_add_crtlist(char **args, char *payload, struct appctx *appctx, void *private)
1187{
Willy Tarreau6b6c3632022-05-05 13:43:49 +02001188 struct add_crtlist_ctx *ctx = applet_reserve_svcctx(appctx, sizeof(*ctx));
William Lallemandc756bbd2020-05-13 17:23:59 +02001189 int cfgerr = 0;
1190 struct ckch_store *store;
1191 char *err = NULL;
1192 char path[MAXPATHLEN+1];
1193 char *crtlist_path;
1194 char *cert_path = NULL;
1195 struct ebmb_node *eb;
1196 struct ebpt_node *inserted;
1197 struct crtlist *crtlist;
1198 struct crtlist_entry *entry = NULL;
William Lallemand99cc2182020-06-25 15:19:51 +02001199 char *end;
William Lallemandc756bbd2020-05-13 17:23:59 +02001200
1201 if (!cli_has_level(appctx, ACCESS_LVL_ADMIN))
1202 return 1;
1203
1204 if (!*args[3] || (!payload && !*args[4]))
1205 return cli_err(appctx, "'add ssl crtlist' expects a filename and a certificate name\n");
1206
1207 crtlist_path = args[3];
1208
William Lallemand99cc2182020-06-25 15:19:51 +02001209 /* strip trailing slashes, including first one */
1210 for (end = crtlist_path + strlen(crtlist_path) - 1; end >= crtlist_path && *end == '/'; end--)
1211 *end = 0;
1212
William Lallemandc756bbd2020-05-13 17:23:59 +02001213 if (HA_SPIN_TRYLOCK(CKCH_LOCK, &ckch_lock))
1214 return cli_err(appctx, "Operations on certificates are currently locked!\n");
1215
1216 eb = ebst_lookup(&crtlists_tree, crtlist_path);
1217 if (!eb) {
1218 memprintf(&err, "crt-list '%s' does not exist!", crtlist_path);
1219 goto error;
1220 }
1221 crtlist = ebmb_entry(eb, struct crtlist, node);
1222
1223 entry = crtlist_entry_new();
1224 if (entry == NULL) {
1225 memprintf(&err, "Not enough memory!");
1226 goto error;
1227 }
1228
1229 if (payload) {
1230 char *lf;
1231
1232 lf = strrchr(payload, '\n');
1233 if (lf) {
1234 memprintf(&err, "only one line of payload is supported!");
1235 goto error;
1236 }
1237 /* cert_path is filled here */
Remi Tricot-Le Bretonfb00f312021-03-23 16:41:53 +01001238 cfgerr |= crtlist_parse_line(payload, &cert_path, entry, "CLI", 1, 1, &err);
William Lallemandc756bbd2020-05-13 17:23:59 +02001239 if (cfgerr & ERR_CODE)
1240 goto error;
1241 } else {
1242 cert_path = args[4];
1243 }
1244
1245 if (!cert_path) {
1246 memprintf(&err, "'add ssl crtlist' should contain the certificate name in the payload");
1247 cfgerr |= ERR_ALERT | ERR_FATAL;
1248 goto error;
1249 }
1250
1251 if (eb_gettag(crtlist->entries.b[EB_RGHT])) {
1252 char *slash;
1253
1254 slash = strrchr(cert_path, '/');
1255 if (!slash) {
1256 memprintf(&err, "'%s' is a directory, certificate path '%s' must contain the directory path", (char *)crtlist->node.key, cert_path);
1257 goto error;
1258 }
1259 /* temporary replace / by 0 to do an strcmp */
1260 *slash = '\0';
1261 if (strcmp(cert_path, (char*)crtlist->node.key) != 0) {
1262 *slash = '/';
1263 memprintf(&err, "'%s' is a directory, certificate path '%s' must contain the directory path", (char *)crtlist->node.key, cert_path);
1264 goto error;
1265 }
1266 *slash = '/';
1267 }
1268
1269 if (*cert_path != '/' && global_ssl.crt_base) {
Willy Tarreau393e42a2022-05-09 10:31:28 +02001270 if ((strlen(global_ssl.crt_base) + 1 + strlen(cert_path)) > sizeof(path) ||
1271 snprintf(path, sizeof(path), "%s/%s", global_ssl.crt_base, cert_path) > sizeof(path)) {
William Lallemandc756bbd2020-05-13 17:23:59 +02001272 memprintf(&err, "'%s' : path too long", cert_path);
1273 cfgerr |= ERR_ALERT | ERR_FATAL;
1274 goto error;
1275 }
William Lallemandc756bbd2020-05-13 17:23:59 +02001276 cert_path = path;
1277 }
1278
1279 store = ckchs_lookup(cert_path);
1280 if (store == NULL) {
1281 memprintf(&err, "certificate '%s' does not exist!", cert_path);
1282 goto error;
1283 }
William Lallemandc756bbd2020-05-13 17:23:59 +02001284 if (store->ckch == NULL || store->ckch->cert == NULL) {
1285 memprintf(&err, "certificate '%s' is empty!", cert_path);
1286 goto error;
1287 }
1288
1289 /* check if it's possible to insert this new crtlist_entry */
1290 entry->node.key = store;
1291 inserted = ebpt_insert(&crtlist->entries, &entry->node);
1292 if (inserted != &entry->node) {
1293 memprintf(&err, "file already exists in this directory!");
1294 goto error;
1295 }
1296
1297 /* this is supposed to be a directory (EB_ROOT_UNIQUE), so no ssl_conf are allowed */
1298 if ((entry->ssl_conf || entry->filters) && eb_gettag(crtlist->entries.b[EB_RGHT])) {
1299 memprintf(&err, "this is a directory, SSL configuration and filters are not allowed");
1300 goto error;
1301 }
1302
Willy Tarreau2b718102021-04-21 07:32:39 +02001303 LIST_APPEND(&crtlist->ord_entries, &entry->by_crtlist);
William Lallemandc756bbd2020-05-13 17:23:59 +02001304 entry->crtlist = crtlist;
Willy Tarreau2b718102021-04-21 07:32:39 +02001305 LIST_APPEND(&store->crtlist_entry, &entry->by_ckch_store);
William Lallemandc756bbd2020-05-13 17:23:59 +02001306
Willy Tarreaufa11df52022-05-05 13:48:40 +02001307 ctx->state = ADDCRT_ST_INIT;
Willy Tarreau6b6c3632022-05-05 13:43:49 +02001308 ctx->crtlist = crtlist;
1309 ctx->entry = entry;
William Lallemandc756bbd2020-05-13 17:23:59 +02001310
1311 /* unlock is done in the release handler */
1312 return 0;
1313
1314error:
1315 crtlist_entry_free(entry);
1316 HA_SPIN_UNLOCK(CKCH_LOCK, &ckch_lock);
1317 err = memprintf(&err, "Can't edit the crt-list: %s\n", err ? err : "");
1318 return cli_dynerr(appctx, err);
1319}
1320
1321/* Parse a "del ssl crt-list <crt-list> <certfile>" line. */
1322static int cli_parse_del_crtlist(char **args, char *payload, struct appctx *appctx, void *private)
1323{
1324 struct ckch_store *store;
1325 char *err = NULL;
1326 char *crtlist_path, *cert_path;
1327 struct ebmb_node *ebmb;
1328 struct ebpt_node *ebpt;
1329 struct crtlist *crtlist;
1330 struct crtlist_entry *entry = NULL;
1331 struct ckch_inst *inst, *inst_s;
1332 int linenum = 0;
1333 char *colons;
William Lallemand99cc2182020-06-25 15:19:51 +02001334 char *end;
Remi Tricot-Le Bretonbc2c3862021-03-26 10:47:50 +01001335 int error_message_dumped = 0;
William Lallemandc756bbd2020-05-13 17:23:59 +02001336
1337 if (!cli_has_level(appctx, ACCESS_LVL_ADMIN))
1338 return 1;
1339
1340 if (!*args[3] || !*args[4])
1341 return cli_err(appctx, "'del ssl crtlist' expects a filename and a certificate name\n");
1342
1343 if (HA_SPIN_TRYLOCK(CKCH_LOCK, &ckch_lock))
1344 return cli_err(appctx, "Can't delete!\nOperations on certificates are currently locked!\n");
1345
1346 crtlist_path = args[3];
1347 cert_path = args[4];
1348
1349 colons = strchr(cert_path, ':');
1350 if (colons) {
1351 char *endptr;
1352
1353 linenum = strtol(colons + 1, &endptr, 10);
1354 if (colons + 1 == endptr || *endptr != '\0') {
1355 memprintf(&err, "wrong line number after colons in '%s'!", cert_path);
1356 goto error;
1357 }
1358 *colons = '\0';
1359 }
William Lallemand99cc2182020-06-25 15:19:51 +02001360
1361 /* strip trailing slashes, including first one */
1362 for (end = crtlist_path + strlen(crtlist_path) - 1; end >= crtlist_path && *end == '/'; end--)
1363 *end = 0;
1364
William Lallemandc756bbd2020-05-13 17:23:59 +02001365 /* look for crtlist */
1366 ebmb = ebst_lookup(&crtlists_tree, crtlist_path);
1367 if (!ebmb) {
1368 memprintf(&err, "crt-list '%s' does not exist!", crtlist_path);
1369 goto error;
1370 }
1371 crtlist = ebmb_entry(ebmb, struct crtlist, node);
1372
1373 /* look for store */
1374 store = ckchs_lookup(cert_path);
1375 if (store == NULL) {
1376 memprintf(&err, "certificate '%s' does not exist!", cert_path);
1377 goto error;
1378 }
William Lallemandc756bbd2020-05-13 17:23:59 +02001379 if (store->ckch == NULL || store->ckch->cert == NULL) {
1380 memprintf(&err, "certificate '%s' is empty!", cert_path);
1381 goto error;
1382 }
1383
1384 ebpt = ebpt_lookup(&crtlist->entries, store);
1385 if (!ebpt) {
1386 memprintf(&err, "certificate '%s' can't be found in crt-list '%s'!", cert_path, crtlist_path);
1387 goto error;
1388 }
1389
1390 /* list the line number of entries for errors in err, and select the right ebpt */
1391 for (; ebpt; ebpt = ebpt_next_dup(ebpt)) {
1392 struct crtlist_entry *tmp;
1393
1394 tmp = ebpt_entry(ebpt, struct crtlist_entry, node);
1395 memprintf(&err, "%s%s%d", err ? err : "", err ? ", " : "", tmp->linenum);
1396
1397 /* select the entry we wanted */
1398 if (linenum == 0 || tmp->linenum == linenum) {
1399 if (!entry)
1400 entry = tmp;
1401 }
1402 }
1403
1404 /* we didn't found the specified entry */
1405 if (!entry) {
1406 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);
1407 goto error;
1408 }
1409
1410 /* we didn't specified a line number but there were several entries */
1411 if (linenum == 0 && ebpt_next_dup(&entry->node)) {
1412 memprintf(&err, "found the certificate '%s' in several entries, please specify a line number preceded by colons (%s)!", cert_path, err ? err : NULL);
1413 goto error;
1414 }
1415
Remi Tricot-Le Bretonbc2c3862021-03-26 10:47:50 +01001416 /* Iterate over all the instances in order to see if any of them is a
1417 * default instance. If this is the case, the entry won't be suppressed. */
1418 list_for_each_entry_safe(inst, inst_s, &entry->ckch_inst, by_crtlist_entry) {
1419 if (inst->is_default && !inst->bind_conf->strict_sni) {
1420 if (!error_message_dumped) {
1421 memprintf(&err, "certificate '%s' cannot be deleted, it is used as default certificate by the following frontends:\n", cert_path);
1422 error_message_dumped = 1;
1423 }
1424 memprintf(&err, "%s\t- %s:%d\n", err, inst->bind_conf->file, inst->bind_conf->line);
1425 }
1426 }
1427 if (error_message_dumped)
1428 goto error;
1429
William Lallemandc756bbd2020-05-13 17:23:59 +02001430 /* upon error free the ckch_inst and everything inside */
1431
1432 ebpt_delete(&entry->node);
Willy Tarreau2b718102021-04-21 07:32:39 +02001433 LIST_DELETE(&entry->by_crtlist);
1434 LIST_DELETE(&entry->by_ckch_store);
William Lallemandc756bbd2020-05-13 17:23:59 +02001435
1436 list_for_each_entry_safe(inst, inst_s, &entry->ckch_inst, by_crtlist_entry) {
1437 struct sni_ctx *sni, *sni_s;
Remi Tricot-Le Breton4458b972021-02-19 17:41:55 +01001438 struct ckch_inst_link_ref *link_ref, *link_ref_s;
William Lallemandc756bbd2020-05-13 17:23:59 +02001439
1440 HA_RWLOCK_WRLOCK(SNI_LOCK, &inst->bind_conf->sni_lock);
1441 list_for_each_entry_safe(sni, sni_s, &inst->sni_ctx, by_ckch_inst) {
1442 ebmb_delete(&sni->name);
Willy Tarreau2b718102021-04-21 07:32:39 +02001443 LIST_DELETE(&sni->by_ckch_inst);
William Lallemandc756bbd2020-05-13 17:23:59 +02001444 SSL_CTX_free(sni->ctx);
1445 free(sni);
1446 }
1447 HA_RWLOCK_WRUNLOCK(SNI_LOCK, &inst->bind_conf->sni_lock);
Willy Tarreau2b718102021-04-21 07:32:39 +02001448 LIST_DELETE(&inst->by_ckchs);
Remi Tricot-Le Breton4458b972021-02-19 17:41:55 +01001449 list_for_each_entry_safe(link_ref, link_ref_s, &inst->cafile_link_refs, list) {
1450 LIST_DELETE(&link_ref->link->list);
1451 LIST_DELETE(&link_ref->list);
1452 free(link_ref);
1453 }
William Lallemandc756bbd2020-05-13 17:23:59 +02001454 free(inst);
1455 }
1456
1457 crtlist_free_filters(entry->filters);
1458 ssl_sock_free_ssl_conf(entry->ssl_conf);
1459 free(entry->ssl_conf);
1460 free(entry);
1461
1462 HA_SPIN_UNLOCK(CKCH_LOCK, &ckch_lock);
1463 err = memprintf(&err, "Entry '%s' deleted in crtlist '%s'!\n", cert_path, crtlist_path);
1464 return cli_dynmsg(appctx, LOG_NOTICE, err);
1465
1466error:
1467 HA_SPIN_UNLOCK(CKCH_LOCK, &ckch_lock);
1468 err = memprintf(&err, "Can't delete the entry: %s\n", err ? err : "");
1469 return cli_dynerr(appctx, err);
1470}
1471
1472
William Lallemandee8530c2020-06-23 18:19:42 +02001473/* unlink and free all crt-list and crt-list entries */
1474void crtlist_deinit()
1475{
1476 struct eb_node *node, *next;
1477 struct crtlist *crtlist;
1478
1479 node = eb_first(&crtlists_tree);
1480 while (node) {
1481 next = eb_next(node);
1482 crtlist = ebmb_entry(node, struct crtlist, node);
1483 crtlist_free(crtlist);
1484 node = next;
1485 }
1486}
1487
William Lallemandc756bbd2020-05-13 17:23:59 +02001488
1489/* register cli keywords */
1490static struct cli_kw_list cli_kws = {{ },{
Willy Tarreaub205bfd2021-05-07 11:38:37 +02001491 { { "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 },
1492 { { "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 },
1493 { { "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 +02001494 { { NULL }, NULL, NULL, NULL } }
1495};
1496
1497INITCALL1(STG_REGISTER, cli_register_kw, &cli_kws);
1498