blob: 266949e59d5ba595e93bace4e58be7214d4a8d16 [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 Tarreauf1d32c42020-06-04 21:07:02 +020023#include <haproxy/channel.h>
Willy Tarreau83487a82020-06-04 20:19:54 +020024#include <haproxy/cli.h>
Willy Tarreau8d366972020-05-27 16:10:29 +020025#include <haproxy/errors.h>
Willy Tarreau47d7f902020-06-04 14:25:47 +020026#include <haproxy/ssl_ckch.h>
Willy Tarreau52d88722020-06-04 14:29:23 +020027#include <haproxy/ssl_crtlist.h>
Willy Tarreau209108d2020-06-04 20:30:20 +020028#include <haproxy/ssl_sock.h>
Willy Tarreau5e539c92020-06-04 20:45:39 +020029#include <haproxy/stream_interface.h>
Willy Tarreau48fbcae2020-06-03 18:09:46 +020030#include <haproxy/tools.h>
William Lallemand6e9556b2020-05-12 17:52:44 +020031
William Lallemand6e9556b2020-05-12 17:52:44 +020032
William Lallemand6e9556b2020-05-12 17:52:44 +020033/* release ssl bind conf */
34void ssl_sock_free_ssl_conf(struct ssl_bind_conf *conf)
35{
36 if (conf) {
37#if defined(OPENSSL_NPN_NEGOTIATED) && !defined(OPENSSL_NO_NEXTPROTONEG)
38 free(conf->npn_str);
39 conf->npn_str = NULL;
40#endif
41#ifdef TLSEXT_TYPE_application_layer_protocol_negotiation
42 free(conf->alpn_str);
43 conf->alpn_str = NULL;
44#endif
45 free(conf->ca_file);
46 conf->ca_file = NULL;
47 free(conf->ca_verify_file);
48 conf->ca_verify_file = NULL;
49 free(conf->crl_file);
50 conf->crl_file = NULL;
51 free(conf->ciphers);
52 conf->ciphers = NULL;
53#if (HA_OPENSSL_VERSION_NUMBER >= 0x10101000L)
54 free(conf->ciphersuites);
55 conf->ciphersuites = NULL;
56#endif
57 free(conf->curves);
58 conf->curves = NULL;
59 free(conf->ecdhe);
60 conf->ecdhe = NULL;
61 }
62}
63
64
65/* free sni filters */
66void crtlist_free_filters(char **args)
67{
68 int i;
69
70 if (!args)
71 return;
72
73 for (i = 0; args[i]; i++)
74 free(args[i]);
75
76 free(args);
77}
78
79/* Alloc and duplicate a char ** array */
80char **crtlist_dup_filters(char **args, int fcount)
81{
82 char **dst;
83 int i;
84
85 if (fcount == 0)
86 return NULL;
87
88 dst = calloc(fcount + 1, sizeof(*dst));
89 if (!dst)
90 return NULL;
91
92 for (i = 0; i < fcount; i++) {
93 dst[i] = strdup(args[i]);
94 if (!dst[i])
95 goto error;
96 }
97 return dst;
98
99error:
100 crtlist_free_filters(dst);
101 return NULL;
102}
103
104/*
105 * Detach and free a crtlist_entry.
106 * Free the filters, the ssl_conf and call ckch_inst_free() for each ckch_inst
107 */
108void crtlist_entry_free(struct crtlist_entry *entry)
109{
110 struct ckch_inst *inst, *inst_s;
111
112 if (entry == NULL)
113 return;
114
115 ebpt_delete(&entry->node);
116 LIST_DEL(&entry->by_crtlist);
117 LIST_DEL(&entry->by_ckch_store);
118 crtlist_free_filters(entry->filters);
119 ssl_sock_free_ssl_conf(entry->ssl_conf);
120 free(entry->ssl_conf);
121 list_for_each_entry_safe(inst, inst_s, &entry->ckch_inst, by_crtlist_entry) {
122 ckch_inst_free(inst);
123 }
124 free(entry);
125}
126
127/*
128 * Allocate and initialize a crtlist_entry
129 */
130struct crtlist_entry *crtlist_entry_new()
131{
132 struct crtlist_entry *entry;
133
134 entry = calloc(1, sizeof(*entry));
135 if (entry == NULL)
136 return NULL;
137
138 LIST_INIT(&entry->ckch_inst);
139
140 /* initialize the nodes so we can LIST_DEL in any cases */
141 LIST_INIT(&entry->by_crtlist);
142 LIST_INIT(&entry->by_ckch_store);
143
144 return entry;
145}
146
147/* Free a crtlist, from the crt_entry to the content of the ssl_conf */
148void crtlist_free(struct crtlist *crtlist)
149{
150 struct crtlist_entry *entry, *s_entry;
151
152 if (crtlist == NULL)
153 return;
154
155 list_for_each_entry_safe(entry, s_entry, &crtlist->ord_entries, by_crtlist) {
156 crtlist_entry_free(entry);
157 }
158 ebmb_delete(&crtlist->node);
159 free(crtlist);
160}
161
162/* Alloc and initialize a struct crtlist
163 * <filename> is the key of the ebmb_node
164 * <unique> initialize the list of entries to be unique (1) or not (0)
165 */
166struct crtlist *crtlist_new(const char *filename, int unique)
167{
168 struct crtlist *newlist;
169
170 newlist = calloc(1, sizeof(*newlist) + strlen(filename) + 1);
171 if (newlist == NULL)
172 return NULL;
173
174 memcpy(newlist->node.key, filename, strlen(filename) + 1);
175 if (unique)
176 newlist->entries = EB_ROOT_UNIQUE;
177 else
178 newlist->entries = EB_ROOT;
179
180 LIST_INIT(&newlist->ord_entries);
181
182 return newlist;
183}
184
185/*
186 * Read a single crt-list line. /!\ alter the <line> string.
187 * Fill <crt_path> and <crtlist_entry>
188 * <crtlist_entry> must be alloc and free by the caller
189 * <crtlist_entry->ssl_conf> is alloc by the function
190 * <crtlist_entry->filters> is alloc by the function
191 * <crt_path> is a ptr in <line>
192 * Return an error code
193 */
194int crtlist_parse_line(char *line, char **crt_path, struct crtlist_entry *entry, const char *file, int linenum, char **err)
195{
196 int cfgerr = 0;
197 int arg, newarg, cur_arg, i, ssl_b = 0, ssl_e = 0;
198 char *end;
199 char *args[MAX_CRT_ARGS + 1];
200 struct ssl_bind_conf *ssl_conf = NULL;
201
202 if (!line || !crt_path || !entry)
203 return ERR_ALERT | ERR_FATAL;
204
205 end = line + strlen(line);
206 if (end-line >= CRT_LINESIZE-1 && *(end-1) != '\n') {
207 /* Check if we reached the limit and the last char is not \n.
208 * Watch out for the last line without the terminating '\n'!
209 */
210 memprintf(err, "line %d too long in file '%s', limit is %d characters",
211 linenum, file, CRT_LINESIZE-1);
212 cfgerr |= ERR_ALERT | ERR_FATAL;
213 goto error;
214 }
215 arg = 0;
216 newarg = 1;
217 while (*line) {
218 if (isspace((unsigned char)*line)) {
219 newarg = 1;
220 *line = 0;
221 } else if (*line == '[') {
222 if (ssl_b) {
223 memprintf(err, "too many '[' on line %d in file '%s'.", linenum, file);
224 cfgerr |= ERR_ALERT | ERR_FATAL;
225 goto error;
226 }
227 if (!arg) {
228 memprintf(err, "file must start with a cert on line %d in file '%s'", linenum, file);
229 cfgerr |= ERR_ALERT | ERR_FATAL;
230 goto error;
231 }
232 ssl_b = arg;
233 newarg = 1;
234 *line = 0;
235 } else if (*line == ']') {
236 if (ssl_e) {
237 memprintf(err, "too many ']' on line %d in file '%s'.", linenum, file);
238 cfgerr |= ERR_ALERT | ERR_FATAL;
239 goto error;
240 }
241 if (!ssl_b) {
242 memprintf(err, "missing '[' in line %d in file '%s'.", linenum, file);
243 cfgerr |= ERR_ALERT | ERR_FATAL;
244 goto error;
245 }
246 ssl_e = arg;
247 newarg = 1;
248 *line = 0;
249 } else if (newarg) {
250 if (arg == MAX_CRT_ARGS) {
251 memprintf(err, "too many args on line %d in file '%s'.", linenum, file);
252 cfgerr |= ERR_ALERT | ERR_FATAL;
253 goto error;
254 }
255 newarg = 0;
256 args[arg++] = line;
257 }
258 line++;
259 }
260 args[arg++] = line;
261
262 /* empty line */
263 if (!*args[0]) {
264 cfgerr |= ERR_NONE;
265 goto error;
266 }
267
268 *crt_path = args[0];
269
270 if (ssl_b) {
271 ssl_conf = calloc(1, sizeof *ssl_conf);
272 if (!ssl_conf) {
273 memprintf(err, "not enough memory!");
274 cfgerr |= ERR_ALERT | ERR_FATAL;
275 goto error;
276 }
277 }
278 cur_arg = ssl_b ? ssl_b : 1;
279 while (cur_arg < ssl_e) {
280 newarg = 0;
281 for (i = 0; ssl_bind_kws[i].kw != NULL; i++) {
282 if (strcmp(ssl_bind_kws[i].kw, args[cur_arg]) == 0) {
283 newarg = 1;
284 cfgerr |= ssl_bind_kws[i].parse(args, cur_arg, NULL, ssl_conf, err);
285 if (cur_arg + 1 + ssl_bind_kws[i].skip > ssl_e) {
286 memprintf(err, "ssl args out of '[]' for %s on line %d in file '%s'",
287 args[cur_arg], linenum, file);
288 cfgerr |= ERR_ALERT | ERR_FATAL;
289 goto error;
290 }
291 cur_arg += 1 + ssl_bind_kws[i].skip;
292 break;
293 }
294 }
295 if (!cfgerr && !newarg) {
296 memprintf(err, "unknown ssl keyword %s on line %d in file '%s'.",
297 args[cur_arg], linenum, file);
298 cfgerr |= ERR_ALERT | ERR_FATAL;
299 goto error;
300 }
301 }
302 entry->linenum = linenum;
303 entry->ssl_conf = ssl_conf;
304 entry->filters = crtlist_dup_filters(&args[cur_arg], arg - cur_arg - 1);
305 entry->fcount = arg - cur_arg - 1;
306
307 return cfgerr;
308
309error:
310 crtlist_free_filters(entry->filters);
311 entry->filters = NULL;
312 ssl_sock_free_ssl_conf(entry->ssl_conf);
313 free(entry->ssl_conf);
314 entry->ssl_conf = NULL;
315 return cfgerr;
316}
317
318
319
320/* This function parse a crt-list file and store it in a struct crtlist, each line is a crtlist_entry structure
321 * Fill the <crtlist> argument with a pointer to a new crtlist struct
322 *
323 * This function tries to open and store certificate files.
324 */
325int crtlist_parse_file(char *file, struct bind_conf *bind_conf, struct proxy *curproxy, struct crtlist **crtlist, char **err)
326{
327 struct crtlist *newlist;
328 struct crtlist_entry *entry = NULL;
329 char thisline[CRT_LINESIZE];
330 char path[MAXPATHLEN+1];
331 FILE *f;
332 struct stat buf;
333 int linenum = 0;
334 int cfgerr = 0;
335
336 if ((f = fopen(file, "r")) == NULL) {
337 memprintf(err, "cannot open file '%s' : %s", file, strerror(errno));
338 return ERR_ALERT | ERR_FATAL;
339 }
340
341 newlist = crtlist_new(file, 0);
342 if (newlist == NULL) {
343 memprintf(err, "Not enough memory!");
344 cfgerr |= ERR_ALERT | ERR_FATAL;
345 goto error;
346 }
347
348 while (fgets(thisline, sizeof(thisline), f) != NULL) {
349 char *end;
350 char *line = thisline;
351 char *crt_path;
352 struct ckch_store *ckchs;
353
354 linenum++;
355 end = line + strlen(line);
356 if (end-line == sizeof(thisline)-1 && *(end-1) != '\n') {
357 /* Check if we reached the limit and the last char is not \n.
358 * Watch out for the last line without the terminating '\n'!
359 */
360 memprintf(err, "line %d too long in file '%s', limit is %d characters",
361 linenum, file, (int)sizeof(thisline)-1);
362 cfgerr |= ERR_ALERT | ERR_FATAL;
363 break;
364 }
365
366 if (*line == '#' || *line == '\n' || *line == '\r')
367 continue;
368
369 entry = crtlist_entry_new();
370 if (entry == NULL) {
371 memprintf(err, "Not enough memory!");
372 cfgerr |= ERR_ALERT | ERR_FATAL;
373 goto error;
374 }
375
376 *(end - 1) = '\0'; /* line parser mustn't receive any \n */
377 cfgerr |= crtlist_parse_line(thisline, &crt_path, entry, file, linenum, err);
378 if (cfgerr)
379 goto error;
380
381 /* empty line */
382 if (!crt_path || !*crt_path) {
383 crtlist_entry_free(entry);
384 entry = NULL;
385 continue;
386 }
387
388 if (*crt_path != '/' && global_ssl.crt_base) {
389 if ((strlen(global_ssl.crt_base) + 1 + strlen(crt_path)) > MAXPATHLEN) {
390 memprintf(err, "'%s' : path too long on line %d in file '%s'",
391 crt_path, linenum, file);
392 cfgerr |= ERR_ALERT | ERR_FATAL;
393 goto error;
394 }
395 snprintf(path, sizeof(path), "%s/%s", global_ssl.crt_base, crt_path);
396 crt_path = path;
397 }
398
399 /* Look for a ckch_store or create one */
400 ckchs = ckchs_lookup(crt_path);
401 if (ckchs == NULL) {
402 if (stat(crt_path, &buf) == 0)
403 ckchs = ckchs_load_cert_file(crt_path, 0, err);
404 else
405 ckchs = ckchs_load_cert_file(crt_path, 1, err);
406 }
407 if (ckchs == NULL)
408 cfgerr |= ERR_ALERT | ERR_FATAL;
409
410 if (cfgerr & ERR_CODE)
411 goto error;
412
413 entry->node.key = ckchs;
414 entry->crtlist = newlist;
415 ebpt_insert(&newlist->entries, &entry->node);
416 LIST_ADDQ(&newlist->ord_entries, &entry->by_crtlist);
417 LIST_ADDQ(&ckchs->crtlist_entry, &entry->by_ckch_store);
418
419 entry = NULL;
420 }
421 if (cfgerr & ERR_CODE)
422 goto error;
423
424 newlist->linecount = linenum;
425
426 fclose(f);
427 *crtlist = newlist;
428
429 return cfgerr;
430error:
431 crtlist_entry_free(entry);
432
433 fclose(f);
434 crtlist_free(newlist);
435 return cfgerr;
436}
437
438/* This function reads a directory and stores it in a struct crtlist, each file is a crtlist_entry structure
439 * Fill the <crtlist> argument with a pointer to a new crtlist struct
440 *
441 * This function tries to open and store certificate files.
442 */
443int crtlist_load_cert_dir(char *path, struct bind_conf *bind_conf, struct crtlist **crtlist, char **err)
444{
445 struct crtlist *dir;
446 struct dirent **de_list;
447 int i, n;
448 struct stat buf;
449 char *end;
450 char fp[MAXPATHLEN+1];
451 int cfgerr = 0;
452 struct ckch_store *ckchs;
453#if HA_OPENSSL_VERSION_NUMBER >= 0x1000200fL
454 int is_bundle;
455 int j;
456#endif
457
458 dir = crtlist_new(path, 1);
459 if (dir == NULL) {
460 memprintf(err, "not enough memory");
461 return ERR_ALERT | ERR_FATAL;
462 }
463
464 n = scandir(path, &de_list, 0, alphasort);
465 if (n < 0) {
466 memprintf(err, "%sunable to scan directory '%s' : %s.\n",
467 err && *err ? *err : "", path, strerror(errno));
468 cfgerr |= ERR_ALERT | ERR_FATAL;
469 }
470 else {
471 for (i = 0; i < n; i++) {
472 struct crtlist_entry *entry;
473 struct dirent *de = de_list[i];
474
475 end = strrchr(de->d_name, '.');
476 if (end && (!strcmp(end, ".issuer") || !strcmp(end, ".ocsp") || !strcmp(end, ".sctl") || !strcmp(end, ".key")))
477 goto ignore_entry;
478
479 snprintf(fp, sizeof(fp), "%s/%s", path, de->d_name);
480 if (stat(fp, &buf) != 0) {
481 memprintf(err, "%sunable to stat SSL certificate from file '%s' : %s.\n",
482 err && *err ? *err : "", fp, strerror(errno));
483 cfgerr |= ERR_ALERT | ERR_FATAL;
484 goto ignore_entry;
485 }
486 if (!S_ISREG(buf.st_mode))
487 goto ignore_entry;
488
489 entry = crtlist_entry_new();
490 if (entry == NULL) {
491 memprintf(err, "not enough memory '%s'", fp);
492 cfgerr |= ERR_ALERT | ERR_FATAL;
493 goto ignore_entry;
494 }
495
496#if HA_OPENSSL_VERSION_NUMBER >= 0x1000200fL
497 is_bundle = 0;
498 /* Check if current entry in directory is part of a multi-cert bundle */
499
500 if ((global_ssl.extra_files & SSL_GF_BUNDLE) && end) {
501 for (j = 0; j < SSL_SOCK_NUM_KEYTYPES; j++) {
502 if (!strcmp(end + 1, SSL_SOCK_KEYTYPE_NAMES[j])) {
503 is_bundle = 1;
504 break;
505 }
506 }
507
508 if (is_bundle) {
509 int dp_len;
510
511 dp_len = end - de->d_name;
512
513 /* increment i and free de until we get to a non-bundle cert
514 * Note here that we look at de_list[i + 1] before freeing de
515 * this is important since ignore_entry will free de. This also
516 * guarantees that de->d_name continues to hold the same prefix.
517 */
518 while (i + 1 < n && !strncmp(de_list[i + 1]->d_name, de->d_name, dp_len)) {
519 free(de);
520 i++;
521 de = de_list[i];
522 }
523
524 snprintf(fp, sizeof(fp), "%s/%.*s", path, dp_len, de->d_name);
525 ckchs = ckchs_lookup(fp);
526 if (ckchs == NULL)
527 ckchs = ckchs_load_cert_file(fp, 1, err);
528 if (ckchs == NULL) {
529 free(de);
530 free(entry);
531 cfgerr |= ERR_ALERT | ERR_FATAL;
532 goto end;
533 }
534 entry->node.key = ckchs;
535 entry->crtlist = dir;
536 LIST_ADDQ(&ckchs->crtlist_entry, &entry->by_ckch_store);
537 LIST_ADDQ(&dir->ord_entries, &entry->by_crtlist);
538 ebpt_insert(&dir->entries, &entry->node);
539
540 /* Successfully processed the bundle */
541 goto ignore_entry;
542 }
543 }
544
545#endif
546 ckchs = ckchs_lookup(fp);
547 if (ckchs == NULL)
548 ckchs = ckchs_load_cert_file(fp, 0, err);
549 if (ckchs == NULL) {
550 free(de);
551 free(entry);
552 cfgerr |= ERR_ALERT | ERR_FATAL;
553 goto end;
554 }
555 entry->node.key = ckchs;
556 entry->crtlist = dir;
557 LIST_ADDQ(&ckchs->crtlist_entry, &entry->by_ckch_store);
558 LIST_ADDQ(&dir->ord_entries, &entry->by_crtlist);
559 ebpt_insert(&dir->entries, &entry->node);
560
561ignore_entry:
562 free(de);
563 }
564end:
565 free(de_list);
566 }
567
568 if (cfgerr & ERR_CODE) {
569 /* free the dir and entries on error */
570 crtlist_free(dir);
571 } else {
572 *crtlist = dir;
573 }
574 return cfgerr;
575
576}
577
William Lallemandc756bbd2020-05-13 17:23:59 +0200578/*
579 * Take an ssl_bind_conf structure and append the configuration line used to
580 * create it in the buffer
581 */
582static void dump_crtlist_sslconf(struct buffer *buf, const struct ssl_bind_conf *conf)
583{
584 int space = 0;
585
586 if (conf == NULL)
587 return;
588
589 chunk_appendf(buf, " [");
590#ifdef OPENSSL_NPN_NEGOTIATED
591 if (conf->npn_str) {
592 int len = conf->npn_len;
593 char *ptr = conf->npn_str;
594 int comma = 0;
595
596 if (space) chunk_appendf(buf, " ");
597 chunk_appendf(buf, "npn ");
598 while (len) {
599 unsigned short size;
600
601 size = *ptr;
602 ptr++;
603 if (comma)
604 chunk_memcat(buf, ",", 1);
605 chunk_memcat(buf, ptr, size);
606 ptr += size;
607 len -= size + 1;
608 comma = 1;
609 }
610 chunk_memcat(buf, "", 1); /* finish with a \0 */
611 space++;
612 }
613#endif
614#ifdef TLSEXT_TYPE_application_layer_protocol_negotiation
615 if (conf->alpn_str) {
616 int len = conf->alpn_len;
617 char *ptr = conf->alpn_str;
618 int comma = 0;
619
620 if (space) chunk_appendf(buf, " ");
621 chunk_appendf(buf, "alpn ");
622 while (len) {
623 unsigned short size;
624
625 size = *ptr;
626 ptr++;
627 if (comma)
628 chunk_memcat(buf, ",", 1);
629 chunk_memcat(buf, ptr, size);
630 ptr += size;
631 len -= size + 1;
632 comma = 1;
633 }
634 chunk_memcat(buf, "", 1); /* finish with a \0 */
635 space++;
636 }
637#endif
638 /* verify */
639 {
640 if (conf->verify == SSL_SOCK_VERIFY_NONE) {
641 if (space) chunk_appendf(buf, " ");
642 chunk_appendf(buf, "verify none");
643 space++;
644 } else if (conf->verify == SSL_SOCK_VERIFY_OPTIONAL) {
645 if (space) chunk_appendf(buf, " ");
646 chunk_appendf(buf, "verify optional");
647 space++;
648 } else if (conf->verify == SSL_SOCK_VERIFY_REQUIRED) {
649 if (space) chunk_appendf(buf, " ");
650 chunk_appendf(buf, "verify required");
651 space++;
652 }
653 }
654
655 if (conf->no_ca_names) {
656 if (space) chunk_appendf(buf, " ");
657 chunk_appendf(buf, "no-ca-names");
658 space++;
659 }
660
661 if (conf->early_data) {
662 if (space) chunk_appendf(buf, " ");
663 chunk_appendf(buf, "allow-0rtt");
664 space++;
665 }
666 if (conf->ca_file) {
667 if (space) chunk_appendf(buf, " ");
668 chunk_appendf(buf, "ca-file %s", conf->ca_file);
669 space++;
670 }
671 if (conf->crl_file) {
672 if (space) chunk_appendf(buf, " ");
673 chunk_appendf(buf, "crl-file %s", conf->crl_file);
674 space++;
675 }
676 if (conf->ciphers) {
677 if (space) chunk_appendf(buf, " ");
678 chunk_appendf(buf, "ciphers %s", conf->ciphers);
679 space++;
680 }
681#if (HA_OPENSSL_VERSION_NUMBER >= 0x10101000L && !defined OPENSSL_IS_BORINGSSL && !defined LIBRESSL_VERSION_NUMBER)
682 if (conf->ciphersuites) {
683 if (space) chunk_appendf(buf, " ");
684 chunk_appendf(buf, "ciphersuites %s", conf->ciphersuites);
685 space++;
686 }
687#endif
688 if (conf->curves) {
689 if (space) chunk_appendf(buf, " ");
690 chunk_appendf(buf, "curves %s", conf->curves);
691 space++;
692 }
693 if (conf->ecdhe) {
694 if (space) chunk_appendf(buf, " ");
695 chunk_appendf(buf, "ecdhe %s", conf->ecdhe);
696 space++;
697 }
698
699 /* the crt-lists only support ssl-min-ver and ssl-max-ver */
William Lallemand8177ad92020-05-20 16:49:02 +0200700 if (conf->ssl_methods_cfg.min) {
William Lallemandc756bbd2020-05-13 17:23:59 +0200701 if (space) chunk_appendf(buf, " ");
William Lallemand8177ad92020-05-20 16:49:02 +0200702 chunk_appendf(buf, "ssl-min-ver %s", methodVersions[conf->ssl_methods_cfg.min].name);
William Lallemandc756bbd2020-05-13 17:23:59 +0200703 space++;
704 }
705
William Lallemand8177ad92020-05-20 16:49:02 +0200706 if (conf->ssl_methods_cfg.max) {
William Lallemandc756bbd2020-05-13 17:23:59 +0200707 if (space) chunk_appendf(buf, " ");
William Lallemand8177ad92020-05-20 16:49:02 +0200708 chunk_appendf(buf, "ssl-max-ver %s", methodVersions[conf->ssl_methods_cfg.max].name);
William Lallemandc756bbd2020-05-13 17:23:59 +0200709 space++;
710 }
711
712 chunk_appendf(buf, "]");
713
714 return;
715}
716
717/* dump a list of filters */
718static void dump_crtlist_filters(struct buffer *buf, struct crtlist_entry *entry)
719{
720 int i;
721
722 if (!entry->fcount)
723 return;
724
725 for (i = 0; i < entry->fcount; i++) {
726 chunk_appendf(buf, " %s", entry->filters[i]);
727 }
728 return;
729}
730
731/************************** CLI functions ****************************/
732
733
734/* CLI IO handler for '(show|dump) ssl crt-list' */
735static int cli_io_handler_dump_crtlist(struct appctx *appctx)
736{
737 struct buffer *trash = alloc_trash_chunk();
738 struct stream_interface *si = appctx->owner;
739 struct ebmb_node *lnode;
740
741 if (trash == NULL)
742 return 1;
743
744 /* dump the list of crt-lists */
745 lnode = appctx->ctx.cli.p1;
746 if (lnode == NULL)
747 lnode = ebmb_first(&crtlists_tree);
748 while (lnode) {
749 chunk_appendf(trash, "%s\n", lnode->key);
750 if (ci_putchk(si_ic(si), trash) == -1) {
751 si_rx_room_blk(si);
752 goto yield;
753 }
754 lnode = ebmb_next(lnode);
755 }
756 free_trash_chunk(trash);
757 return 1;
758yield:
759 appctx->ctx.cli.p1 = lnode;
760 free_trash_chunk(trash);
761 return 0;
762}
763
764/* CLI IO handler for '(show|dump) ssl crt-list <filename>' */
765static int cli_io_handler_dump_crtlist_entries(struct appctx *appctx)
766{
767 struct buffer *trash = alloc_trash_chunk();
768 struct crtlist *crtlist;
769 struct stream_interface *si = appctx->owner;
770 struct crtlist_entry *entry;
771
772 if (trash == NULL)
773 return 1;
774
775 crtlist = ebmb_entry(appctx->ctx.cli.p0, struct crtlist, node);
776
777 entry = appctx->ctx.cli.p1;
778 if (entry == NULL) {
779 entry = LIST_ELEM((crtlist->ord_entries).n, typeof(entry), by_crtlist);
780 chunk_appendf(trash, "# %s\n", crtlist->node.key);
781 if (ci_putchk(si_ic(si), trash) == -1) {
782 si_rx_room_blk(si);
783 goto yield;
784 }
785 }
786
787 list_for_each_entry_from(entry, &crtlist->ord_entries, by_crtlist) {
788 struct ckch_store *store;
789 const char *filename;
790
791 store = entry->node.key;
792 filename = store->path;
793 chunk_appendf(trash, "%s", filename);
794 if (appctx->ctx.cli.i0 == 's') /* show */
795 chunk_appendf(trash, ":%d", entry->linenum);
796 dump_crtlist_sslconf(trash, entry->ssl_conf);
797 dump_crtlist_filters(trash, entry);
798 chunk_appendf(trash, "\n");
799
800 if (ci_putchk(si_ic(si), trash) == -1) {
801 si_rx_room_blk(si);
802 goto yield;
803 }
804 }
805 free_trash_chunk(trash);
806 return 1;
807yield:
808 appctx->ctx.cli.p1 = entry;
809 free_trash_chunk(trash);
810 return 0;
811}
812
813/* CLI argument parser for '(show|dump) ssl crt-list' */
814static int cli_parse_dump_crtlist(char **args, char *payload, struct appctx *appctx, void *private)
815{
816 struct ebmb_node *lnode;
817 char *filename = NULL;
818 int mode;
819
820 if (!cli_has_level(appctx, ACCESS_LVL_ADMIN))
821 return 1;
822
823 appctx->ctx.cli.p0 = NULL;
824 appctx->ctx.cli.p1 = NULL;
825
826 if (*args[3] && !strcmp(args[3], "-n")) {
827 mode = 's';
828 filename = args[4];
829 } else {
830 mode = 'd';
831 filename = args[3];
832 }
833
834 if (mode == 's' && !*args[4])
835 return cli_err(appctx, "'show ssl crt-list -n' expects a filename or a directory\n");
836
837 if (filename && *filename) {
838 lnode = ebst_lookup(&crtlists_tree, filename);
839 if (lnode == NULL)
840 return cli_err(appctx, "didn't find the specified filename\n");
841
842 appctx->ctx.cli.p0 = lnode;
843 appctx->io_handler = cli_io_handler_dump_crtlist_entries;
844 }
845 appctx->ctx.cli.i0 = mode;
846
847 return 0;
848}
849
850/* release function of the "add ssl crt-list' command, free things and unlock
851 the spinlock */
852static void cli_release_add_crtlist(struct appctx *appctx)
853{
854 struct crtlist_entry *entry = appctx->ctx.cli.p1;
855
856 if (appctx->st2 != SETCERT_ST_FIN) {
857 struct ckch_inst *inst, *inst_s;
858 /* upon error free the ckch_inst and everything inside */
859 ebpt_delete(&entry->node);
860 LIST_DEL(&entry->by_crtlist);
861 LIST_DEL(&entry->by_ckch_store);
862
863 list_for_each_entry_safe(inst, inst_s, &entry->ckch_inst, by_ckchs) {
864 ckch_inst_free(inst);
865 }
866 crtlist_free_filters(entry->filters);
867 ssl_sock_free_ssl_conf(entry->ssl_conf);
868 free(entry->ssl_conf);
869 free(entry);
870 }
871
872 HA_SPIN_UNLOCK(CKCH_LOCK, &ckch_lock);
873}
874
875
876/* IO Handler for the "add ssl crt-list" command It adds a new entry in the
877 * crt-list and generates the ckch_insts for each bind_conf that uses this crt-list
878 *
879 * The logic is the same as the "commit ssl cert" command but without the
880 * freeing of the old structures, because there are none.
881 */
882static int cli_io_handler_add_crtlist(struct appctx *appctx)
883{
884 struct bind_conf_list *bind_conf_node;
885 struct stream_interface *si = appctx->owner;
886 struct crtlist *crtlist = appctx->ctx.cli.p0;
887 struct crtlist_entry *entry = appctx->ctx.cli.p1;
888 struct ckch_store *store = entry->node.key;
889 struct buffer *trash = alloc_trash_chunk();
890 struct ckch_inst *new_inst;
891 char *err = NULL;
892 int i = 0;
893 int errcode = 0;
894
895 if (trash == NULL)
896 goto error;
897
898 /* for each bind_conf which use the crt-list, a new ckch_inst must be
899 * created.
900 */
901 if (unlikely(si_ic(si)->flags & (CF_WRITE_ERROR|CF_SHUTW)))
902 goto error;
903
904 while (1) {
905 switch (appctx->st2) {
906 case SETCERT_ST_INIT:
907 /* This state just print the update message */
908 chunk_printf(trash, "Inserting certificate '%s' in crt-list '%s'", store->path, crtlist->node.key);
909 if (ci_putchk(si_ic(si), trash) == -1) {
910 si_rx_room_blk(si);
911 goto yield;
912 }
913 appctx->st2 = SETCERT_ST_GEN;
914 /* fallthrough */
915 case SETCERT_ST_GEN:
916 bind_conf_node = appctx->ctx.cli.p2; /* get the previous ptr from the yield */
917 if (bind_conf_node == NULL)
918 bind_conf_node = crtlist->bind_conf;
919 for (; bind_conf_node; bind_conf_node = bind_conf_node->next) {
920 struct bind_conf *bind_conf = bind_conf_node->bind_conf;
921 struct sni_ctx *sni;
922
923 /* yield every 10 generations */
924 if (i > 10) {
925 appctx->ctx.cli.p2 = bind_conf_node;
926 goto yield;
927 }
928
929 /* we don't support multi-cert bundles, only simple ones */
930 errcode |= ckch_inst_new_load_store(store->path, store, bind_conf, entry->ssl_conf, entry->filters, entry->fcount, &new_inst, &err);
931 if (errcode & ERR_CODE)
932 goto error;
933
934 /* we need to initialize the SSL_CTX generated */
935 /* this iterate on the newly generated SNIs in the new instance to prepare their SSL_CTX */
936 list_for_each_entry(sni, &new_inst->sni_ctx, by_ckch_inst) {
937 if (!sni->order) { /* we initialized only the first SSL_CTX because it's the same in the other sni_ctx's */
938 errcode |= ssl_sock_prepare_ctx(bind_conf, new_inst->ssl_conf, sni->ctx, &err);
939 if (errcode & ERR_CODE)
940 goto error;
941 }
942 }
943 /* display one dot for each new instance */
944 chunk_appendf(trash, ".");
945 i++;
946 LIST_ADDQ(&store->ckch_inst, &new_inst->by_ckchs);
947 }
948 appctx->st2 = SETCERT_ST_INSERT;
949 /* fallthrough */
950 case SETCERT_ST_INSERT:
951 /* insert SNIs in bind_conf */
952 list_for_each_entry(new_inst, &store->ckch_inst, by_ckchs) {
953 HA_RWLOCK_WRLOCK(SNI_LOCK, &new_inst->bind_conf->sni_lock);
954 ssl_sock_load_cert_sni(new_inst, new_inst->bind_conf);
955 HA_RWLOCK_WRUNLOCK(SNI_LOCK, &new_inst->bind_conf->sni_lock);
956 }
957 entry->linenum = ++crtlist->linecount;
958 appctx->st2 = SETCERT_ST_FIN;
959 goto end;
960 }
961 }
962
963end:
964 chunk_appendf(trash, "\n");
965 if (errcode & ERR_WARN)
966 chunk_appendf(trash, "%s", err);
967 chunk_appendf(trash, "Success!\n");
968 if (ci_putchk(si_ic(si), trash) == -1)
969 si_rx_room_blk(si);
970 free_trash_chunk(trash);
971 /* success: call the release function and don't come back */
972 return 1;
973yield:
974 /* store the state */
975 if (ci_putchk(si_ic(si), trash) == -1)
976 si_rx_room_blk(si);
977 free_trash_chunk(trash);
978 si_rx_endp_more(si); /* let's come back later */
979 return 0; /* should come back */
980
981error:
982 /* spin unlock and free are done in the release function */
983 if (trash) {
984 chunk_appendf(trash, "\n%sFailed!\n", err);
985 if (ci_putchk(si_ic(si), trash) == -1)
986 si_rx_room_blk(si);
987 free_trash_chunk(trash);
988 }
989 /* error: call the release function and don't come back */
990 return 1;
991}
992
993
994/*
995 * Parse a "add ssl crt-list <crt-list> <certfile>" line.
996 * Filters and option must be passed through payload:
997 */
998static int cli_parse_add_crtlist(char **args, char *payload, struct appctx *appctx, void *private)
999{
1000 int cfgerr = 0;
1001 struct ckch_store *store;
1002 char *err = NULL;
1003 char path[MAXPATHLEN+1];
1004 char *crtlist_path;
1005 char *cert_path = NULL;
1006 struct ebmb_node *eb;
1007 struct ebpt_node *inserted;
1008 struct crtlist *crtlist;
1009 struct crtlist_entry *entry = NULL;
1010
1011 if (!cli_has_level(appctx, ACCESS_LVL_ADMIN))
1012 return 1;
1013
1014 if (!*args[3] || (!payload && !*args[4]))
1015 return cli_err(appctx, "'add ssl crtlist' expects a filename and a certificate name\n");
1016
1017 crtlist_path = args[3];
1018
1019 if (HA_SPIN_TRYLOCK(CKCH_LOCK, &ckch_lock))
1020 return cli_err(appctx, "Operations on certificates are currently locked!\n");
1021
1022 eb = ebst_lookup(&crtlists_tree, crtlist_path);
1023 if (!eb) {
1024 memprintf(&err, "crt-list '%s' does not exist!", crtlist_path);
1025 goto error;
1026 }
1027 crtlist = ebmb_entry(eb, struct crtlist, node);
1028
1029 entry = crtlist_entry_new();
1030 if (entry == NULL) {
1031 memprintf(&err, "Not enough memory!");
1032 goto error;
1033 }
1034
1035 if (payload) {
1036 char *lf;
1037
1038 lf = strrchr(payload, '\n');
1039 if (lf) {
1040 memprintf(&err, "only one line of payload is supported!");
1041 goto error;
1042 }
1043 /* cert_path is filled here */
1044 cfgerr |= crtlist_parse_line(payload, &cert_path, entry, "CLI", 1, &err);
1045 if (cfgerr & ERR_CODE)
1046 goto error;
1047 } else {
1048 cert_path = args[4];
1049 }
1050
1051 if (!cert_path) {
1052 memprintf(&err, "'add ssl crtlist' should contain the certificate name in the payload");
1053 cfgerr |= ERR_ALERT | ERR_FATAL;
1054 goto error;
1055 }
1056
1057 if (eb_gettag(crtlist->entries.b[EB_RGHT])) {
1058 char *slash;
1059
1060 slash = strrchr(cert_path, '/');
1061 if (!slash) {
1062 memprintf(&err, "'%s' is a directory, certificate path '%s' must contain the directory path", (char *)crtlist->node.key, cert_path);
1063 goto error;
1064 }
1065 /* temporary replace / by 0 to do an strcmp */
1066 *slash = '\0';
1067 if (strcmp(cert_path, (char*)crtlist->node.key) != 0) {
1068 *slash = '/';
1069 memprintf(&err, "'%s' is a directory, certificate path '%s' must contain the directory path", (char *)crtlist->node.key, cert_path);
1070 goto error;
1071 }
1072 *slash = '/';
1073 }
1074
1075 if (*cert_path != '/' && global_ssl.crt_base) {
1076 if ((strlen(global_ssl.crt_base) + 1 + strlen(cert_path)) > MAXPATHLEN) {
1077 memprintf(&err, "'%s' : path too long", cert_path);
1078 cfgerr |= ERR_ALERT | ERR_FATAL;
1079 goto error;
1080 }
1081 snprintf(path, sizeof(path), "%s/%s", global_ssl.crt_base, cert_path);
1082 cert_path = path;
1083 }
1084
1085 store = ckchs_lookup(cert_path);
1086 if (store == NULL) {
1087 memprintf(&err, "certificate '%s' does not exist!", cert_path);
1088 goto error;
1089 }
1090 if (store->multi) {
1091 memprintf(&err, "certificate '%s' is a bundle. You can disable the bundle merging with the directive 'ssl-load-extra-files' in the global section.", cert_path);
1092 goto error;
1093 }
1094 if (store->ckch == NULL || store->ckch->cert == NULL) {
1095 memprintf(&err, "certificate '%s' is empty!", cert_path);
1096 goto error;
1097 }
1098
1099 /* check if it's possible to insert this new crtlist_entry */
1100 entry->node.key = store;
1101 inserted = ebpt_insert(&crtlist->entries, &entry->node);
1102 if (inserted != &entry->node) {
1103 memprintf(&err, "file already exists in this directory!");
1104 goto error;
1105 }
1106
1107 /* this is supposed to be a directory (EB_ROOT_UNIQUE), so no ssl_conf are allowed */
1108 if ((entry->ssl_conf || entry->filters) && eb_gettag(crtlist->entries.b[EB_RGHT])) {
1109 memprintf(&err, "this is a directory, SSL configuration and filters are not allowed");
1110 goto error;
1111 }
1112
1113 LIST_ADDQ(&crtlist->ord_entries, &entry->by_crtlist);
1114 entry->crtlist = crtlist;
1115 LIST_ADDQ(&store->crtlist_entry, &entry->by_ckch_store);
1116
1117 appctx->st2 = SETCERT_ST_INIT;
1118 appctx->ctx.cli.p0 = crtlist;
1119 appctx->ctx.cli.p1 = entry;
1120
1121 /* unlock is done in the release handler */
1122 return 0;
1123
1124error:
1125 crtlist_entry_free(entry);
1126 HA_SPIN_UNLOCK(CKCH_LOCK, &ckch_lock);
1127 err = memprintf(&err, "Can't edit the crt-list: %s\n", err ? err : "");
1128 return cli_dynerr(appctx, err);
1129}
1130
1131/* Parse a "del ssl crt-list <crt-list> <certfile>" line. */
1132static int cli_parse_del_crtlist(char **args, char *payload, struct appctx *appctx, void *private)
1133{
1134 struct ckch_store *store;
1135 char *err = NULL;
1136 char *crtlist_path, *cert_path;
1137 struct ebmb_node *ebmb;
1138 struct ebpt_node *ebpt;
1139 struct crtlist *crtlist;
1140 struct crtlist_entry *entry = NULL;
1141 struct ckch_inst *inst, *inst_s;
1142 int linenum = 0;
1143 char *colons;
1144
1145 if (!cli_has_level(appctx, ACCESS_LVL_ADMIN))
1146 return 1;
1147
1148 if (!*args[3] || !*args[4])
1149 return cli_err(appctx, "'del ssl crtlist' expects a filename and a certificate name\n");
1150
1151 if (HA_SPIN_TRYLOCK(CKCH_LOCK, &ckch_lock))
1152 return cli_err(appctx, "Can't delete!\nOperations on certificates are currently locked!\n");
1153
1154 crtlist_path = args[3];
1155 cert_path = args[4];
1156
1157 colons = strchr(cert_path, ':');
1158 if (colons) {
1159 char *endptr;
1160
1161 linenum = strtol(colons + 1, &endptr, 10);
1162 if (colons + 1 == endptr || *endptr != '\0') {
1163 memprintf(&err, "wrong line number after colons in '%s'!", cert_path);
1164 goto error;
1165 }
1166 *colons = '\0';
1167 }
1168 /* look for crtlist */
1169 ebmb = ebst_lookup(&crtlists_tree, crtlist_path);
1170 if (!ebmb) {
1171 memprintf(&err, "crt-list '%s' does not exist!", crtlist_path);
1172 goto error;
1173 }
1174 crtlist = ebmb_entry(ebmb, struct crtlist, node);
1175
1176 /* look for store */
1177 store = ckchs_lookup(cert_path);
1178 if (store == NULL) {
1179 memprintf(&err, "certificate '%s' does not exist!", cert_path);
1180 goto error;
1181 }
1182 if (store->multi) {
1183 memprintf(&err, "certificate '%s' is a bundle. You can disable the bundle merging with the directive 'ssl-load-extra-files' in the global section.", cert_path);
1184 goto error;
1185 }
1186 if (store->ckch == NULL || store->ckch->cert == NULL) {
1187 memprintf(&err, "certificate '%s' is empty!", cert_path);
1188 goto error;
1189 }
1190
1191 ebpt = ebpt_lookup(&crtlist->entries, store);
1192 if (!ebpt) {
1193 memprintf(&err, "certificate '%s' can't be found in crt-list '%s'!", cert_path, crtlist_path);
1194 goto error;
1195 }
1196
1197 /* list the line number of entries for errors in err, and select the right ebpt */
1198 for (; ebpt; ebpt = ebpt_next_dup(ebpt)) {
1199 struct crtlist_entry *tmp;
1200
1201 tmp = ebpt_entry(ebpt, struct crtlist_entry, node);
1202 memprintf(&err, "%s%s%d", err ? err : "", err ? ", " : "", tmp->linenum);
1203
1204 /* select the entry we wanted */
1205 if (linenum == 0 || tmp->linenum == linenum) {
1206 if (!entry)
1207 entry = tmp;
1208 }
1209 }
1210
1211 /* we didn't found the specified entry */
1212 if (!entry) {
1213 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);
1214 goto error;
1215 }
1216
1217 /* we didn't specified a line number but there were several entries */
1218 if (linenum == 0 && ebpt_next_dup(&entry->node)) {
1219 memprintf(&err, "found the certificate '%s' in several entries, please specify a line number preceded by colons (%s)!", cert_path, err ? err : NULL);
1220 goto error;
1221 }
1222
1223 /* upon error free the ckch_inst and everything inside */
1224
1225 ebpt_delete(&entry->node);
1226 LIST_DEL(&entry->by_crtlist);
1227 LIST_DEL(&entry->by_ckch_store);
1228
1229 list_for_each_entry_safe(inst, inst_s, &entry->ckch_inst, by_crtlist_entry) {
1230 struct sni_ctx *sni, *sni_s;
1231
1232 HA_RWLOCK_WRLOCK(SNI_LOCK, &inst->bind_conf->sni_lock);
1233 list_for_each_entry_safe(sni, sni_s, &inst->sni_ctx, by_ckch_inst) {
1234 ebmb_delete(&sni->name);
1235 LIST_DEL(&sni->by_ckch_inst);
1236 SSL_CTX_free(sni->ctx);
1237 free(sni);
1238 }
1239 HA_RWLOCK_WRUNLOCK(SNI_LOCK, &inst->bind_conf->sni_lock);
1240 LIST_DEL(&inst->by_ckchs);
1241 free(inst);
1242 }
1243
1244 crtlist_free_filters(entry->filters);
1245 ssl_sock_free_ssl_conf(entry->ssl_conf);
1246 free(entry->ssl_conf);
1247 free(entry);
1248
1249 HA_SPIN_UNLOCK(CKCH_LOCK, &ckch_lock);
1250 err = memprintf(&err, "Entry '%s' deleted in crtlist '%s'!\n", cert_path, crtlist_path);
1251 return cli_dynmsg(appctx, LOG_NOTICE, err);
1252
1253error:
1254 HA_SPIN_UNLOCK(CKCH_LOCK, &ckch_lock);
1255 err = memprintf(&err, "Can't delete the entry: %s\n", err ? err : "");
1256 return cli_dynerr(appctx, err);
1257}
1258
1259
1260
1261/* register cli keywords */
1262static struct cli_kw_list cli_kws = {{ },{
1263 { { "add", "ssl", "crt-list", NULL }, "add ssl crt-list <filename> <certfile> [options] : add a line <certfile> to a crt-list <filename>", cli_parse_add_crtlist, cli_io_handler_add_crtlist, cli_release_add_crtlist },
1264 { { "del", "ssl", "crt-list", NULL }, "del ssl crt-list <filename> <certfile[:line]> : delete a line <certfile> in a crt-list <filename>", cli_parse_del_crtlist, NULL, NULL },
1265 { { "show", "ssl", "crt-list", NULL }, "show ssl crt-list [-n] [<filename>] : show the list of crt-lists or the content of a crt-list <filename>", cli_parse_dump_crtlist, cli_io_handler_dump_crtlist, NULL },
1266 { { NULL }, NULL, NULL, NULL } }
1267};
1268
1269INITCALL1(STG_REGISTER, cli_register_kw, &cli_kws);
1270