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 %{
25 #include "compat.h"
27 #include <sys/stat.h>
29 #include <ctype.h>
30 #include <err.h>
31 #include <errno.h>
32 #include <event.h>
33 #include <inttypes.h>
34 #include <limits.h>
35 #include <stdarg.h>
36 #include <stdio.h>
37 #include <stdlib.h>
38 #include <string.h>
39 #include <syslog.h>
40 #include <unistd.h>
42 #include "log.h"
43 #include "kamid.h"
44 #include "table.h"
45 #include "utils.h"
47 TAILQ_HEAD(files, file) files = TAILQ_HEAD_INITIALIZER(files);
48 static struct file {
49 TAILQ_ENTRY(file) entry;
50 FILE *stream;
51 char *name;
52 size_t ungetpos;
53 size_t ungetsize;
54 u_char *ungetbuf;
55 int eof_reached;
56 int lineno;
57 int errors;
58 } *file, *topfile;
59 struct file *pushfile(const char *, int);
60 int popfile(void);
61 int check_file_secrecy(int, const char *);
62 int yyparse(void);
63 int yylex(void);
64 int yyerror(const char *, ...)
65 __attribute__((__format__ (printf, 1, 2)))
66 __attribute__((__nonnull__ (1)));
67 int kw_cmp(const void *, const void *);
68 int lookup(char *);
69 int igetc(void);
70 int lgetc(int);
71 void lungetc(int);
72 int findeol(void);
74 TAILQ_HEAD(symhead, sym) symhead = TAILQ_HEAD_INITIALIZER(symhead);
75 struct sym {
76 TAILQ_ENTRY(sym) entry;
77 int used;
78 int persist;
79 char *nam;
80 char *val;
81 };
83 int symset(const char *, const char *, int);
84 char *symget(const char *);
86 void clear_config(struct kd_conf *xconf);
88 static void add_table(const char *, const char *, const char *);
89 static struct table *findtable(const char *name);
90 static void add_cert(const char *, const char *);
91 static void add_key(const char *, const char *);
92 static struct kd_listen_conf *listen_new(void);
94 static uint32_t counter;
95 static struct table *table;
96 static struct kd_listen_conf *listener;
97 static struct kd_conf *conf;
98 static int errors;
100 typedef struct {
101 union {
102 int64_t number;
103 char *string;
104 struct table *table;
105 } v;
106 int lineno;
107 } YYSTYPE;
109 %}
111 %token AUTH
112 %token CERT
113 %token ERROR
114 %token INCLUDE
115 %token KEY
116 %token LISTEN
117 %token NO
118 %token ON
119 %token PKI PORT
120 %token TABLE TLS
121 %token YES
123 %token <v.string> STRING
124 %token <v.number> NUMBER
125 %type <v.number> yesno
126 %type <v.string> string
127 %type <v.table> tableref
129 %%
131 grammar : /* empty */
132 | grammar include '\n'
133 | grammar '\n'
134 | grammar table '\n'
135 | grammar pki '\n'
136 | grammar listen '\n'
137 | grammar varset '\n'
138 | grammar error '\n' { file->errors++; }
141 include : INCLUDE STRING {
142 struct file *nfile;
144 if ((nfile = pushfile($2, 0)) == NULL) {
145 yyerror("failed to include file %s", $2);
146 free($2);
147 YYERROR;
149 free($2);
151 file = nfile;
152 lungetc('\n');
156 string : string STRING {
157 if (asprintf(&$$, "%s %s", $1, $2) == -1) {
158 free($1);
159 free($2);
160 yyerror("string: asprintf");
161 YYERROR;
163 free($1);
164 free($2);
166 | STRING
169 yesno : YES { $$ = 1; }
170 | NO { $$ = 0; }
173 optnl : '\n' optnl /* zero or more newlines */
174 | /*empty*/
177 nl : '\n' optnl /* one or more newlines */
180 arrow : '=' '>' ;
182 comma : ',' optnl
183 | nl
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 {
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 } '{' 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");
277 listener = NULL;
278 };
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;
304 %%
306 struct keywords {
307 const char *k_name;
308 int k_val;
309 };
311 int
312 yyerror(const char *fmt, ...)
314 va_list ap;
315 char *msg;
317 file->errors++;
318 va_start(ap, fmt);
319 if (vasprintf(&msg, fmt, ap) == -1)
320 fatalx("yyerror vasprintf");
321 va_end(ap);
322 logit(LOG_CRIT, "%s:%d: %s", file->name, yylval.lineno, msg);
323 free(msg);
324 return 0;
327 int
328 kw_cmp(const void *k, const void *e)
330 return strcmp(k, ((const struct keywords *)e)->k_name);
333 int
334 lookup(char *s)
336 /* This has to be sorted always. */
337 static const struct keywords keywords[] = {
338 {"auth", AUTH},
339 {"cert", CERT},
340 {"include", INCLUDE},
341 {"key", KEY},
342 {"listen", LISTEN},
343 {"no", NO},
344 {"on", ON},
345 {"pki", PKI},
346 {"port", PORT},
347 {"table", TABLE},
348 {"tls", TLS},
349 {"yes", YES},
350 };
351 const struct keywords *p;
353 p = bsearch(s, keywords, sizeof(keywords)/sizeof(keywords[0]),
354 sizeof(keywords[0]), kw_cmp);
356 if (p)
357 return p->k_val;
358 else
359 return STRING;
362 #define START_EXPAND 1
363 #define DONE_EXPAND 2
365 static int expanding;
367 int
368 igetc(void)
370 int c;
372 while (1) {
373 if (file->ungetpos > 0)
374 c = file->ungetbuf[--file->ungetpos];
375 else
376 c = getc(file->stream);
378 if (c == START_EXPAND)
379 expanding = 1;
380 else if (c == DONE_EXPAND)
381 expanding = 0;
382 else
383 break;
385 return c;
388 int
389 lgetc(int quotec)
391 int c, next;
393 if (quotec) {
394 if ((c = igetc()) == EOF) {
395 yyerror("reached end of file while parsing "
396 "quoted string");
397 if (file == topfile || popfile() == EOF)
398 return EOF;
399 return quotec;
401 return c;
404 while ((c = igetc()) == '\\') {
405 next = igetc();
406 if (next != '\n') {
407 c = next;
408 break;
410 yylval.lineno = file->lineno;
411 file->lineno++;
414 if (c == EOF) {
415 /*
416 * Fake EOL when hit EOF for the first time. This gets line
417 * count right if last line in included file is syntactically
418 * invalid and has no newline.
419 */
420 if (file->eof_reached == 0) {
421 file->eof_reached = 1;
422 return '\n';
424 while (c == EOF) {
425 if (file == topfile || popfile() == EOF)
426 return EOF;
427 c = igetc();
430 return c;
433 void
434 lungetc(int c)
436 if (c == EOF)
437 return;
439 if (file->ungetpos >= file->ungetsize) {
440 void *p = reallocarray(file->ungetbuf, file->ungetsize, 2);
441 if (p == NULL)
442 err(1, "lungetc");
443 file->ungetbuf = p;
444 file->ungetsize *= 2;
446 file->ungetbuf[file->ungetpos++] = c;
449 int
450 findeol(void)
452 int c;
454 /* Skip to either EOF or the first real EOL. */
455 while (1) {
456 c = lgetc(0);
457 if (c == '\n') {
458 file->lineno++;
459 break;
461 if (c == EOF)
462 break;
464 return ERROR;
467 #if 0
468 int my_yylex(void);
470 int
471 yylex(void)
473 int x;
475 switch (x = my_yylex()) {
476 case AUTH:
477 puts("auth");
478 break;
479 case CERT:
480 puts("cert");
481 break;
482 case ERROR:
483 puts("error");
484 break;
485 case INCLUDE:
486 puts("include");
487 break;
488 case KEY:
489 puts("key");
490 break;
491 case LISTEN:
492 puts("listen");
493 break;
494 case NO:
495 puts("no");
496 break;
497 case ON:
498 puts("on");
499 break;
500 case PKI:
501 puts("pki");
502 break;
503 case PORT:
504 puts("port");
505 break;
506 case TABLE:
507 puts("table");
508 break;
509 case TLS:
510 puts("tls");
511 break;
512 case YES:
513 puts("yes");
514 break;
515 case STRING:
516 printf("string \"%s\"\n", yylval.v.string);
517 break;
518 case NUMBER:
519 printf("number %"PRIi64"\n", yylval.v.number);
520 default:
521 printf("character ");
522 if (x == '\n')
523 printf("\\n");
524 else
525 printf("%c", x);
526 printf(" [0x%x]", x);
527 printf("\n");
528 break;
531 return x;
534 int
535 my_yylex(void)
536 #else
537 int
538 yylex(void)
539 #endif
541 unsigned char buf[8096];
542 unsigned char *p, *val;
543 int quotec, next, c;
544 int token;
546 top:
547 p = buf;
548 while ((c = lgetc(0)) == ' ' || c == '\t')
549 ; /* nothing */
551 yylval.lineno = file->lineno;
552 if (c == '#')
553 while ((c = lgetc(0)) != '\n' && c != EOF)
554 ; /* nothing */
555 if (c == '$' && !expanding) {
556 while (1) {
557 if ((c = lgetc(0)) == EOF)
558 return 0;
560 if (p + 1 >= buf + sizeof(buf) - 1) {
561 yyerror("string too long");
562 return findeol();
564 if (isalnum(c) || c == '_') {
565 *p++ = c;
566 continue;
568 *p = '\0';
569 lungetc(c);
570 break;
572 val = symget(buf);
573 if (val == NULL) {
574 yyerror("macro '%s' not defined", buf);
575 return findeol();
577 p = val + strlen(val) - 1;
578 lungetc(DONE_EXPAND);
579 while (p >= val) {
580 lungetc(*p);
581 p--;
583 lungetc(START_EXPAND);
584 goto top;
587 switch (c) {
588 case '\'':
589 case '"':
590 quotec = c;
591 while (1) {
592 if ((c = lgetc(quotec)) == EOF)
593 return 0;
594 if (c == '\n') {
595 file->lineno++;
596 continue;
597 } else if (c == '\\') {
598 if ((next = lgetc(quotec)) == EOF)
599 return (0);
600 if (next == quotec || next == ' ' ||
601 next == '\t')
602 c = next;
603 else if (next == '\n') {
604 file->lineno++;
605 continue;
606 } else
607 lungetc(next);
608 } else if (c == quotec) {
609 *p = '\0';
610 break;
611 } else if (c == '\0') {
612 yyerror("syntax error");
613 return findeol();
615 if (p + 1 >= buf + sizeof(buf) - 1) {
616 yyerror("string too long");
617 return findeol();
619 *p++ = c;
621 yylval.v.string = strdup(buf);
622 if (yylval.v.string == NULL)
623 err(1, "yylex: strdup");
624 return STRING;
627 #define allowed_to_end_number(x) \
628 (isspace(x) || x == ')' || x ==',' || x == '/' || x == '}' || x == '=')
630 if (c == '-' || isdigit(c)) {
631 do {
632 *p++ = c;
633 if ((size_t)(p-buf) >= sizeof(buf)) {
634 yyerror("string too long");
635 return findeol();
637 } while ((c = lgetc(0)) != EOF && isdigit(c));
638 lungetc(c);
639 if (p == buf + 1 && buf[0] == '-')
640 goto nodigits;
641 if (c == EOF || allowed_to_end_number(c)) {
642 const char *errstr = NULL;
644 *p = '\0';
645 yylval.v.number = strtonum(buf, LLONG_MIN,
646 LLONG_MAX, &errstr);
647 if (errstr) {
648 yyerror("\"%s\" invalid number: %s",
649 buf, errstr);
650 return findeol();
652 return NUMBER;
653 } else {
654 nodigits:
655 while (p > buf + 1)
656 lungetc(*--p);
657 c = *--p;
658 if (c == '-')
659 return c;
663 #define allowed_in_string(x) \
664 (isalnum(x) || (ispunct(x) && x != '(' && x != ')' && \
665 x != '{' && x != '}' && \
666 x != '!' && x != '=' && x != '#' && \
667 x != ',' && x != '>'))
669 if (isalnum(c) || c == ':' || c == '_') {
670 do {
671 *p++ = c;
672 if ((size_t)(p-buf) >= sizeof(buf)) {
673 yyerror("string too long");
674 return findeol();
676 } while ((c = lgetc(0)) != EOF && (allowed_in_string(c)));
677 lungetc(c);
678 *p = '\0';
679 if ((token = lookup(buf)) == STRING)
680 if ((yylval.v.string = strdup(buf)) == NULL)
681 err(1, "yylex: strdup");
682 return token;
684 if (c == '\n') {
685 yylval.lineno = file->lineno;
686 file->lineno++;
688 if (c == EOF)
689 return 0;
690 return c;
693 int
694 check_file_secrecy(int fd, const char *fname)
696 struct stat st;
698 if (fstat(fd, &st)) {
699 log_warn("cannot stat %s", fname);
700 return -1;
702 if (st.st_uid != 0 && st.st_uid != getuid()) {
703 log_warnx("%s: owner not root or current user", fname);
704 return -1;
706 if (st.st_mode & (S_IWGRP | S_IXGRP | S_IRWXO)) {
707 log_warnx("%s: group writable or world read/writable", fname);
708 return -1;
710 return 0;
713 struct file *
714 pushfile(const char *name, int secret)
716 struct file *nfile;
718 if ((nfile = calloc(1, sizeof(struct file))) == NULL) {
719 log_warn("calloc");
720 return NULL;
722 if ((nfile->name = strdup(name)) == NULL) {
723 log_warn("strdup");
724 free(nfile);
725 return NULL;
727 if ((nfile->stream = fopen(nfile->name, "r")) == NULL) {
728 log_warn("%s", nfile->name);
729 free(nfile->name);
730 free(nfile);
731 return NULL;
732 } else if (secret &&
733 check_file_secrecy(fileno(nfile->stream), nfile->name)) {
734 fclose(nfile->stream);
735 free(nfile->name);
736 free(nfile);
737 return NULL;
739 nfile->lineno = TAILQ_EMPTY(&files) ? 1 : 0;
740 nfile->ungetsize = 16;
741 nfile->ungetbuf = malloc(nfile->ungetsize);
742 if (nfile->ungetbuf == NULL) {
743 log_warn("malloc");
744 fclose(nfile->stream);
745 free(nfile->name);
746 free(nfile);
747 return NULL;
749 TAILQ_INSERT_TAIL(&files, nfile, entry);
750 return nfile;
753 int
754 popfile(void)
756 struct file *prev;
758 if ((prev = TAILQ_PREV(file, files, entry)) != NULL)
759 prev->errors += file->errors;
761 TAILQ_REMOVE(&files, file, entry);
762 fclose(file->stream);
763 free(file->name);
764 free(file->ungetbuf);
765 free(file);
766 file = prev;
767 return file ? 0 : EOF;
770 struct kd_conf *
771 parse_config(const char *filename)
773 struct sym *sym, *next;
775 counter = 0;
776 conf = config_new_empty();
778 file = pushfile(filename, 0);
779 if (file == NULL) {
780 free(conf);
781 return NULL;
783 topfile = file;
785 yyparse();
786 errors = file->errors;
787 popfile();
789 /* Free macros and check which have not been used. */
790 TAILQ_FOREACH_SAFE(sym, &symhead, entry, next) {
791 if (verbose && !sym->used)
792 fprintf(stderr, "warning: macro '%s' not used\n",
793 sym->nam);
794 if (!sym->persist) {
795 free(sym->nam);
796 free(sym->val);
797 TAILQ_REMOVE(&symhead, sym, entry);
798 free(sym);
802 if (errors) {
803 clear_config(conf);
804 return NULL;
807 return conf;
810 int
811 symset(const char *nam, const char *val, int persist)
813 struct sym *sym;
815 TAILQ_FOREACH(sym, &symhead, entry) {
816 if (strcmp(nam, sym->nam) == 0)
817 break;
820 if (sym != NULL) {
821 if (sym->persist == 1)
822 return 0;
823 else {
824 free(sym->nam);
825 free(sym->val);
826 TAILQ_REMOVE(&symhead, sym, entry);
827 free(sym);
830 if ((sym = calloc(1, sizeof(*sym))) == NULL)
831 return -1;
833 sym->nam = strdup(nam);
834 if (sym->nam == NULL) {
835 free(sym);
836 return -1;
838 sym->val = strdup(val);
839 if (sym->val == NULL) {
840 free(sym->nam);
841 free(sym);
842 return -1;
844 sym->used = 0;
845 sym->persist = persist;
846 TAILQ_INSERT_TAIL(&symhead, sym, entry);
847 return 0;
850 int
851 cmdline_symset(char *s)
853 char *sym, *val;
854 int ret;
856 if ((val = strrchr(s, '=')) == NULL)
857 return -1;
858 sym = strndup(s, val - s);
859 if (sym == NULL)
860 errx(1, "%s: strndup", __func__);
861 ret = symset(sym, val + 1, 1);
862 free(sym);
864 return ret;
867 char *
868 symget(const char *nam)
870 struct sym *sym;
872 TAILQ_FOREACH(sym, &symhead, entry) {
873 if (strcmp(nam, sym->nam) == 0) {
874 sym->used = 1;
875 return sym->val;
878 return NULL;
881 void
882 clear_config(struct kd_conf *xconf)
884 /* free stuff? */
886 free(xconf);
889 static void
890 add_table(const char *name, const char *type, const char *path)
892 if (table_open(conf, name, type, path) == -1)
893 yyerror("can't initialize table %s", name);
894 table = STAILQ_FIRST(&conf->table_head)->table;
897 static struct table *
898 findtable(const char *name)
900 struct kd_tables_conf *i;
902 STAILQ_FOREACH(i, &conf->table_head, entry) {
903 if (!strcmp(i->table->t_name, name))
904 return i->table;
907 yyerror("unknown table %s", name);
908 return NULL;
911 static void
912 add_cert(const char *name, const char *path)
914 struct kd_pki_conf *pki;
916 STAILQ_FOREACH(pki, &conf->pki_head, entry) {
917 if (strcmp(name, pki->name) != 0)
918 continue;
920 if (pki->cert != NULL) {
921 yyerror("duplicate `pki %s cert'", name);
922 return;
925 goto set;
928 pki = xcalloc(1, sizeof(*pki));
929 strlcpy(pki->name, name, sizeof(pki->name));
930 STAILQ_INSERT_HEAD(&conf->pki_head, pki, entry);
932 set:
933 if ((pki->cert = tls_load_file(path, &pki->certlen, NULL)) == NULL)
934 fatal(NULL);
937 static void
938 add_key(const char *name, const char *path)
940 struct kd_pki_conf *pki;
942 STAILQ_FOREACH(pki, &conf->pki_head, entry) {
943 if (strcmp(name, pki->name) != 0)
944 continue;
946 if (pki->key != NULL) {
947 yyerror("duplicate `pki %s key'", name);
948 return;
951 goto set;
954 pki = xcalloc(1, sizeof(*pki));
955 strlcpy(pki->name, name, sizeof(pki->name));
956 STAILQ_INSERT_HEAD(&conf->pki_head, pki, entry);
958 set:
959 if ((pki->key = tls_load_file(path, &pki->keylen, NULL)) == NULL)
960 fatal(NULL);
963 static struct kd_listen_conf *
964 listen_new(void)
966 struct kd_listen_conf *l;
968 l = xcalloc(1, sizeof(*l));
969 l->id = counter++;
970 l->fd = -1;
972 STAILQ_INSERT_HEAD(&conf->listen_head, l, entry);
973 return l;