Blob


1 /*
2 * Copyright (c) 2021 Omar Polo <op@omarpolo.com>
3 * Copyright (c) 2018 Florian Obser <florian@openbsd.org>
4 * Copyright (c) 2004, 2005 Esben Norby <norby@openbsd.org>
5 * Copyright (c) 2004 Ryan McBride <mcbride@openbsd.org>
6 * Copyright (c) 2002, 2003, 2004 Henning Brauer <henning@openbsd.org>
7 * Copyright (c) 2001 Markus Friedl. All rights reserved.
8 * Copyright (c) 2001 Daniel Hartmeier. All rights reserved.
9 * Copyright (c) 2001 Theo de Raadt. All rights reserved.
10 *
11 * Permission to use, copy, modify, and distribute this software for any
12 * purpose with or without fee is hereby granted, provided that the above
13 * copyright notice and this permission notice appear in all copies.
14 *
15 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
16 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
17 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
18 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
19 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
20 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
21 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
22 */
24 %{
26 #include "compat.h"
28 #include <sys/stat.h>
30 #include <ctype.h>
31 #include <errno.h>
32 #include <inttypes.h>
33 #include <limits.h>
34 #include <stdarg.h>
35 #include <stdio.h>
36 #include <stdlib.h>
37 #include <string.h>
38 #include <syslog.h>
39 #include <unistd.h>
41 #include "log.h"
42 #include "kamid.h"
43 #include "table.h"
44 #include "utils.h"
46 TAILQ_HEAD(files, file) files = TAILQ_HEAD_INITIALIZER(files);
47 static struct file {
48 TAILQ_ENTRY(file) entry;
49 FILE *stream;
50 char *name;
51 size_t ungetpos;
52 size_t ungetsize;
53 u_char *ungetbuf;
54 int eof_reached;
55 int lineno;
56 int errors;
57 } *file, *topfile;
58 struct file *pushfile(const char *, int);
59 int popfile(void);
60 int check_file_secrecy(int, const char *);
61 int yyparse(void);
62 int yylex(void);
63 int yyerror(const char *, ...)
64 __attribute__((__format__ (printf, 1, 2)))
65 __attribute__((__nonnull__ (1)));
66 int kw_cmp(const void *, const void *);
67 int lookup(char *);
68 int igetc(void);
69 int lgetc(int);
70 void lungetc(int);
71 int findeol(void);
73 TAILQ_HEAD(symhead, sym) symhead = TAILQ_HEAD_INITIALIZER(symhead);
74 struct sym {
75 TAILQ_ENTRY(sym) entry;
76 int used;
77 int persist;
78 char *nam;
79 char *val;
80 };
82 int symset(const char *, const char *, int);
83 char *symget(const char *);
85 void clear_config(struct kd_conf *xconf);
87 static void add_table(const char *, const char *, const char *);
88 static struct table *findtable(const char *name);
89 static void add_cert(const char *, const char *);
90 static void add_key(const char *, const char *);
91 static struct kd_listen_conf *listen_new(void);
93 static uint32_t counter;
94 static struct table *table;
95 static struct kd_listen_conf *listener;
96 static struct kd_conf *conf;
97 static int errors;
99 typedef struct {
100 union {
101 int64_t number;
102 char *string;
103 struct table *table;
104 } v;
105 int lineno;
106 } YYSTYPE;
108 %}
110 %token AUTH
111 %token CERT
112 %token ERROR
113 %token INCLUDE
114 %token KEY
115 %token LISTEN
116 %token NO
117 %token ON
118 %token PKI PORT
119 %token TABLE TLS
120 %token USERDATA
121 %token VIRTUAL
122 %token YES
124 %token <v.string> STRING
125 %token <v.number> NUMBER
126 %type <v.number> yesno
127 %type <v.string> string
128 %type <v.table> tableref
130 %%
132 grammar : /* empty */
133 | grammar include '\n'
134 | grammar '\n'
135 | grammar table '\n'
136 | grammar pki '\n'
137 | grammar listen '\n'
138 | grammar varset '\n'
139 | grammar error '\n' { file->errors++; }
142 include : INCLUDE STRING {
143 struct file *nfile;
145 if ((nfile = pushfile($2, 0)) == NULL) {
146 yyerror("failed to include file %s", $2);
147 free($2);
148 YYERROR;
150 free($2);
152 file = nfile;
153 lungetc('\n');
157 string : string STRING {
158 if (asprintf(&$$, "%s %s", $1, $2) == -1) {
159 free($1);
160 free($2);
161 yyerror("string: asprintf");
162 YYERROR;
164 free($1);
165 free($2);
167 | STRING
170 yesno : YES { $$ = 1; }
171 | NO { $$ = 0; }
174 optnl : '\n' optnl /* zero or more newlines */
175 | /*empty*/
178 nl : '\n' optnl /* one or more newlines */
181 arrow : '=' '>' ;
183 comma : ',' optnl
186 varset : STRING '=' string {
187 char *s = $1;
188 if (verbose)
189 printf("%s = \"%s\"\n", $1, $3);
190 while (*s++) {
191 if (isspace((unsigned char)*s)) {
192 yyerror("macro name cannot contain "
193 "whitespace");
194 free($1);
195 free($3);
196 YYERROR;
199 if (symset($1, $3, 0) == -1)
200 fatal("cannot store variable");
201 free($1);
202 free($3);
206 pki : PKI STRING CERT STRING { add_cert($2, $4); }
207 | PKI STRING KEY STRING { add_key($2, $4); }
210 table_kp : string arrow string optnl {
211 if (table_add(table, $1, $3) == -1)
212 yyerror("can't add to table %s",
213 table->t_name);
214 free($1);
215 free($3);
219 table_kps : table_kp
220 | table_kp comma table_kps
223 stringel : STRING {
224 if (table_add(table, $1, NULL) == -1)
225 yyerror("can't add to table %s",
226 table->t_name);
227 free($1);
231 string_list : stringel
232 | stringel comma string_list
235 table_vals : table_kps
236 | string_list
239 table : TABLE STRING STRING {
240 char *p;
242 if ((p = strchr($3, ':')) == NULL) {
243 yyerror("invalid table %s", $2);
244 YYERROR;
247 *p = '\0';
248 add_table($2, $3, p+1);
249 free($2);
250 free($3);
252 | TABLE STRING {
253 add_table($2, "static", NULL);
254 } '{' optnl table_vals '}' {
255 table = NULL;
259 tableref : '<' STRING '>' {
260 struct table *t;
262 t = findtable($2);
263 free($2);
264 if (t == NULL)
265 YYERROR;
266 $$ = t;
270 listen : LISTEN { listener = listen_new(); }
271 listen_opts {
272 if (listener->auth_table == NULL)
273 yyerror("missing auth table");
274 if (!(listener->flags & L_TLS))
275 yyerror("can't define a non-tls listener");
276 listener = NULL;
280 listen_opts : listen_opt
281 | listen_opt listen_opts
284 listen_opt : ON STRING PORT NUMBER {
285 if (*listener->iface != '\0')
286 yyerror("listen address and port already"
287 " defined");
288 strlcpy(listener->iface, $2, sizeof(listener->iface));
289 listener->port = $4;
291 | TLS PKI STRING {
292 if (*listener->pki != '\0')
293 yyerror("listen tls pki already defined");
294 listener->flags |= L_TLS;
295 strlcpy(listener->pki, $3, sizeof(listener->pki));
297 | AUTH tableref {
298 if (listener->auth_table != NULL)
299 yyerror("listen auth already defined");
300 listener->auth_table = $2;
302 | USERDATA tableref {
303 if (listener->userdata_table != NULL)
304 yyerror("userdata table already defined");
305 listener->userdata_table = $2;
307 | VIRTUAL tableref {
308 if (listener->virtual_table != NULL)
309 yyerror("virtual table already defined");
310 listener->virtual_table = $2;
314 %%
316 struct keywords {
317 const char *k_name;
318 int k_val;
319 };
321 int
322 yyerror(const char *fmt, ...)
324 va_list ap;
325 char *msg;
327 file->errors++;
328 va_start(ap, fmt);
329 if (vasprintf(&msg, fmt, ap) == -1)
330 fatalx("yyerror vasprintf");
331 va_end(ap);
332 logit(LOG_CRIT, "%s:%d: %s", file->name, yylval.lineno, msg);
333 free(msg);
334 return 0;
337 int
338 kw_cmp(const void *k, const void *e)
340 return strcmp(k, ((const struct keywords *)e)->k_name);
343 int
344 lookup(char *s)
346 /* This has to be sorted always. */
347 static const struct keywords keywords[] = {
348 {"auth", AUTH},
349 {"cert", CERT},
350 {"include", INCLUDE},
351 {"key", KEY},
352 {"listen", LISTEN},
353 {"no", NO},
354 {"on", ON},
355 {"pki", PKI},
356 {"port", PORT},
357 {"table", TABLE},
358 {"tls", TLS},
359 {"userdata", USERDATA},
360 {"virtual", VIRTUAL},
361 {"yes", YES},
362 };
363 const struct keywords *p;
365 p = bsearch(s, keywords, sizeof(keywords)/sizeof(keywords[0]),
366 sizeof(keywords[0]), kw_cmp);
368 if (p)
369 return p->k_val;
370 else
371 return STRING;
374 #define START_EXPAND 1
375 #define DONE_EXPAND 2
377 static int expanding;
379 int
380 igetc(void)
382 int c;
384 while (1) {
385 if (file->ungetpos > 0)
386 c = file->ungetbuf[--file->ungetpos];
387 else
388 c = getc(file->stream);
390 if (c == START_EXPAND)
391 expanding = 1;
392 else if (c == DONE_EXPAND)
393 expanding = 0;
394 else
395 break;
397 return c;
400 int
401 lgetc(int quotec)
403 int c, next;
405 if (quotec) {
406 if ((c = igetc()) == EOF) {
407 yyerror("reached end of file while parsing "
408 "quoted string");
409 if (file == topfile || popfile() == EOF)
410 return EOF;
411 return quotec;
413 return c;
416 while ((c = igetc()) == '\\') {
417 next = igetc();
418 if (next != '\n') {
419 c = next;
420 break;
422 yylval.lineno = file->lineno;
423 file->lineno++;
426 if (c == EOF) {
427 /*
428 * Fake EOL when hit EOF for the first time. This gets line
429 * count right if last line in included file is syntactically
430 * invalid and has no newline.
431 */
432 if (file->eof_reached == 0) {
433 file->eof_reached = 1;
434 return '\n';
436 while (c == EOF) {
437 if (file == topfile || popfile() == EOF)
438 return EOF;
439 c = igetc();
442 return c;
445 void
446 lungetc(int c)
448 if (c == EOF)
449 return;
451 if (file->ungetpos >= file->ungetsize) {
452 void *p = reallocarray(file->ungetbuf, file->ungetsize, 2);
453 if (p == NULL)
454 err(1, "lungetc");
455 file->ungetbuf = p;
456 file->ungetsize *= 2;
458 file->ungetbuf[file->ungetpos++] = c;
461 int
462 findeol(void)
464 int c;
466 /* Skip to either EOF or the first real EOL. */
467 while (1) {
468 c = lgetc(0);
469 if (c == '\n') {
470 file->lineno++;
471 break;
473 if (c == EOF)
474 break;
476 return ERROR;
479 #if 0
480 int my_yylex(void);
482 int
483 yylex(void)
485 int x;
487 switch (x = my_yylex()) {
488 case AUTH:
489 puts("auth");
490 break;
491 case CERT:
492 puts("cert");
493 break;
494 case ERROR:
495 puts("error");
496 break;
497 case INCLUDE:
498 puts("include");
499 break;
500 case KEY:
501 puts("key");
502 break;
503 case LISTEN:
504 puts("listen");
505 break;
506 case NO:
507 puts("no");
508 break;
509 case ON:
510 puts("on");
511 break;
512 case PKI:
513 puts("pki");
514 break;
515 case PORT:
516 puts("port");
517 break;
518 case TABLE:
519 puts("table");
520 break;
521 case TLS:
522 puts("tls");
523 break;
524 case YES:
525 puts("yes");
526 break;
527 case STRING:
528 printf("string \"%s\"\n", yylval.v.string);
529 break;
530 case NUMBER:
531 printf("number %"PRIi64"\n", yylval.v.number);
532 default:
533 printf("character ");
534 if (x == '\n')
535 printf("\\n");
536 else
537 printf("%c", x);
538 printf(" [0x%x]", x);
539 printf("\n");
540 break;
543 return x;
546 int
547 my_yylex(void)
548 #else
549 int
550 yylex(void)
551 #endif
553 char buf[8096];
554 char *p, *val;
555 int quotec, next, c;
556 int token;
558 top:
559 p = buf;
560 while ((c = lgetc(0)) == ' ' || c == '\t')
561 ; /* nothing */
563 yylval.lineno = file->lineno;
564 if (c == '#')
565 while ((c = lgetc(0)) != '\n' && c != EOF)
566 ; /* nothing */
567 if (c == '$' && !expanding) {
568 while (1) {
569 if ((c = lgetc(0)) == EOF)
570 return 0;
572 if (p + 1 >= buf + sizeof(buf) - 1) {
573 yyerror("string too long");
574 return findeol();
576 if (isalnum(c) || c == '_') {
577 *p++ = c;
578 continue;
580 *p = '\0';
581 lungetc(c);
582 break;
584 val = symget(buf);
585 if (val == NULL) {
586 yyerror("macro '%s' not defined", buf);
587 return findeol();
589 p = val + strlen(val) - 1;
590 lungetc(DONE_EXPAND);
591 while (p >= val) {
592 lungetc((unsigned char)*p);
593 p--;
595 lungetc(START_EXPAND);
596 goto top;
599 switch (c) {
600 case '\'':
601 case '"':
602 quotec = c;
603 while (1) {
604 if ((c = lgetc(quotec)) == EOF)
605 return 0;
606 if (c == '\n') {
607 file->lineno++;
608 continue;
609 } else if (c == '\\') {
610 if ((next = lgetc(quotec)) == EOF)
611 return (0);
612 if (next == quotec || next == ' ' ||
613 next == '\t')
614 c = next;
615 else if (next == '\n') {
616 file->lineno++;
617 continue;
618 } else
619 lungetc(next);
620 } else if (c == quotec) {
621 *p = '\0';
622 break;
623 } else if (c == '\0') {
624 yyerror("syntax error");
625 return findeol();
627 if (p + 1 >= buf + sizeof(buf) - 1) {
628 yyerror("string too long");
629 return findeol();
631 *p++ = c;
633 yylval.v.string = strdup(buf);
634 if (yylval.v.string == NULL)
635 err(1, "yylex: strdup");
636 return STRING;
639 #define allowed_to_end_number(x) \
640 (isspace(x) || x == ')' || x ==',' || x == '/' || x == '}' || x == '=')
642 if (c == '-' || isdigit(c)) {
643 do {
644 *p++ = c;
645 if ((size_t)(p-buf) >= sizeof(buf)) {
646 yyerror("string too long");
647 return findeol();
649 } while ((c = lgetc(0)) != EOF && isdigit(c));
650 lungetc(c);
651 if (p == buf + 1 && buf[0] == '-')
652 goto nodigits;
653 if (c == EOF || allowed_to_end_number(c)) {
654 const char *errstr = NULL;
656 *p = '\0';
657 yylval.v.number = strtonum(buf, LLONG_MIN,
658 LLONG_MAX, &errstr);
659 if (errstr) {
660 yyerror("\"%s\" invalid number: %s",
661 buf, errstr);
662 return findeol();
664 return NUMBER;
665 } else {
666 nodigits:
667 while (p > buf + 1)
668 lungetc((unsigned char)*--p);
669 c = (unsigned char)*--p;
670 if (c == '-')
671 return c;
675 #define allowed_in_string(x) \
676 (isalnum(x) || (ispunct(x) && x != '(' && x != ')' && \
677 x != '{' && x != '}' && \
678 x != '!' && x != '=' && x != '#' && \
679 x != ',' && x != '>'))
681 if (isalnum(c) || c == ':' || c == '_') {
682 do {
683 *p++ = c;
684 if ((size_t)(p-buf) >= sizeof(buf)) {
685 yyerror("string too long");
686 return findeol();
688 } while ((c = lgetc(0)) != EOF && (allowed_in_string(c)));
689 lungetc(c);
690 *p = '\0';
691 if ((token = lookup(buf)) == STRING)
692 if ((yylval.v.string = strdup(buf)) == NULL)
693 err(1, "yylex: strdup");
694 return token;
696 if (c == '\n') {
697 yylval.lineno = file->lineno;
698 file->lineno++;
700 if (c == EOF)
701 return 0;
702 return c;
705 int
706 check_file_secrecy(int fd, const char *fname)
708 struct stat st;
710 if (fstat(fd, &st)) {
711 log_warn("cannot stat %s", fname);
712 return -1;
714 if (st.st_uid != 0 && st.st_uid != getuid()) {
715 log_warnx("%s: owner not root or current user", fname);
716 return -1;
718 if (st.st_mode & (S_IWGRP | S_IXGRP | S_IRWXO)) {
719 log_warnx("%s: group writable or world read/writable", fname);
720 return -1;
722 return 0;
725 struct file *
726 pushfile(const char *name, int secret)
728 struct file *nfile;
730 if ((nfile = calloc(1, sizeof(struct file))) == NULL) {
731 log_warn("calloc");
732 return NULL;
734 if ((nfile->name = strdup(name)) == NULL) {
735 log_warn("strdup");
736 free(nfile);
737 return NULL;
739 if ((nfile->stream = fopen(nfile->name, "r")) == NULL) {
740 log_warn("%s", nfile->name);
741 free(nfile->name);
742 free(nfile);
743 return NULL;
744 } else if (secret &&
745 check_file_secrecy(fileno(nfile->stream), nfile->name)) {
746 fclose(nfile->stream);
747 free(nfile->name);
748 free(nfile);
749 return NULL;
751 nfile->lineno = TAILQ_EMPTY(&files) ? 1 : 0;
752 nfile->ungetsize = 16;
753 nfile->ungetbuf = malloc(nfile->ungetsize);
754 if (nfile->ungetbuf == NULL) {
755 log_warn("malloc");
756 fclose(nfile->stream);
757 free(nfile->name);
758 free(nfile);
759 return NULL;
761 TAILQ_INSERT_TAIL(&files, nfile, entry);
762 return nfile;
765 int
766 popfile(void)
768 struct file *prev;
770 if ((prev = TAILQ_PREV(file, files, entry)) != NULL)
771 prev->errors += file->errors;
773 TAILQ_REMOVE(&files, file, entry);
774 fclose(file->stream);
775 free(file->name);
776 free(file->ungetbuf);
777 free(file);
778 file = prev;
779 return file ? 0 : EOF;
782 struct kd_conf *
783 parse_config(const char *filename)
785 struct sym *sym, *next;
787 counter = 0;
788 conf = config_new_empty();
790 file = pushfile(filename, 0);
791 if (file == NULL) {
792 free(conf);
793 return NULL;
795 topfile = file;
797 yyparse();
798 errors = file->errors;
799 popfile();
801 /* Free macros and check which have not been used. */
802 TAILQ_FOREACH_SAFE(sym, &symhead, entry, next) {
803 if (verbose && !sym->used)
804 fprintf(stderr, "warning: macro '%s' not used\n",
805 sym->nam);
806 if (!sym->persist) {
807 free(sym->nam);
808 free(sym->val);
809 TAILQ_REMOVE(&symhead, sym, entry);
810 free(sym);
814 if (errors) {
815 clear_config(conf);
816 return NULL;
819 return conf;
822 int
823 symset(const char *nam, const char *val, int persist)
825 struct sym *sym;
827 TAILQ_FOREACH(sym, &symhead, entry) {
828 if (strcmp(nam, sym->nam) == 0)
829 break;
832 if (sym != NULL) {
833 if (sym->persist == 1)
834 return 0;
835 else {
836 free(sym->nam);
837 free(sym->val);
838 TAILQ_REMOVE(&symhead, sym, entry);
839 free(sym);
842 if ((sym = calloc(1, sizeof(*sym))) == NULL)
843 return -1;
845 sym->nam = strdup(nam);
846 if (sym->nam == NULL) {
847 free(sym);
848 return -1;
850 sym->val = strdup(val);
851 if (sym->val == NULL) {
852 free(sym->nam);
853 free(sym);
854 return -1;
856 sym->used = 0;
857 sym->persist = persist;
858 TAILQ_INSERT_TAIL(&symhead, sym, entry);
859 return 0;
862 int
863 cmdline_symset(char *s)
865 char *sym, *val;
866 int ret;
868 if ((val = strrchr(s, '=')) == NULL)
869 return -1;
870 sym = strndup(s, val - s);
871 if (sym == NULL)
872 errx(1, "%s: strndup", __func__);
873 ret = symset(sym, val + 1, 1);
874 free(sym);
876 return ret;
879 char *
880 symget(const char *nam)
882 struct sym *sym;
884 TAILQ_FOREACH(sym, &symhead, entry) {
885 if (strcmp(nam, sym->nam) == 0) {
886 sym->used = 1;
887 return sym->val;
890 return NULL;
893 void
894 clear_config(struct kd_conf *xconf)
896 struct kd_pki_conf *p;
897 struct kd_tables_conf *t;
898 struct kd_listen_conf *l;
900 if (xconf == NULL)
901 return;
903 while (!STAILQ_EMPTY(&xconf->pki_head)) {
904 p = STAILQ_FIRST(&xconf->pki_head);
905 STAILQ_REMOVE_HEAD(&xconf->pki_head, entry);
907 if (p->cert != NULL)
908 tls_unload_file(p->cert, p->certlen);
909 if (p->key != NULL)
910 tls_unload_file(p->key, p->keylen);
911 if (p->tlsconf != NULL)
912 tls_config_free(p->tlsconf);
913 free(p);
916 while (!STAILQ_EMPTY(&xconf->table_head)) {
917 t = STAILQ_FIRST(&xconf->table_head);
918 STAILQ_REMOVE_HEAD(&xconf->table_head, entry);
920 table_close(t->table);
921 free(t->table);
922 free(t);
925 while (!STAILQ_EMPTY(&xconf->listen_head)) {
926 l = STAILQ_FIRST(&xconf->listen_head);
927 STAILQ_REMOVE_HEAD(&xconf->listen_head, entry);
929 if (l->ctx != NULL)
930 tls_free(l->ctx);
931 if (l->fd != -1) {
932 event_del(&l->ev);
933 close(l->fd);
936 free(l);
939 free(xconf);
942 static void
943 add_table(const char *name, const char *type, const char *path)
945 if (table_open(conf, name, type, path) == -1)
946 yyerror("can't initialize table %s", name);
947 table = STAILQ_FIRST(&conf->table_head)->table;
950 static struct table *
951 findtable(const char *name)
953 struct kd_tables_conf *i;
955 STAILQ_FOREACH(i, &conf->table_head, entry) {
956 if (!strcmp(i->table->t_name, name))
957 return i->table;
960 yyerror("unknown table %s", name);
961 return NULL;
964 static void
965 add_cert(const char *name, const char *path)
967 struct kd_pki_conf *pki;
969 STAILQ_FOREACH(pki, &conf->pki_head, entry) {
970 if (strcmp(name, pki->name) != 0)
971 continue;
973 if (pki->cert != NULL) {
974 yyerror("duplicate `pki %s cert'", name);
975 return;
978 goto set;
981 pki = xcalloc(1, sizeof(*pki));
982 strlcpy(pki->name, name, sizeof(pki->name));
983 STAILQ_INSERT_HEAD(&conf->pki_head, pki, entry);
985 set:
986 if ((pki->cert = tls_load_file(path, &pki->certlen, NULL)) == NULL)
987 fatal("can't open %s", path);
990 static void
991 add_key(const char *name, const char *path)
993 struct kd_pki_conf *pki;
995 STAILQ_FOREACH(pki, &conf->pki_head, entry) {
996 if (strcmp(name, pki->name) != 0)
997 continue;
999 if (pki->key != NULL) {
1000 yyerror("duplicate `pki %s key'", name);
1001 return;
1004 goto set;
1007 pki = xcalloc(1, sizeof(*pki));
1008 strlcpy(pki->name, name, sizeof(pki->name));
1009 STAILQ_INSERT_HEAD(&conf->pki_head, pki, entry);
1011 set:
1012 if ((pki->key = tls_load_file(path, &pki->keylen, NULL)) == NULL)
1013 fatal("can't open %s", path);
1016 static struct kd_listen_conf *
1017 listen_new(void)
1019 struct kd_listen_conf *l;
1021 l = xcalloc(1, sizeof(*l));
1022 l->id = counter++;
1023 l->fd = -1;
1025 STAILQ_INSERT_HEAD(&conf->listen_head, l, entry);
1026 return l;