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