parse.y revision 1.40
1/*	$OpenBSD: parse.y,v 1.40 2021/05/02 14:39:05 martijn Exp $ */
2
3/*
4 * Copyright (c) 2009, 2010 Martin Hedenfalk <martinh@openbsd.org>
5 * Copyright (c) 2008 Gilles Chehade <gilles@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 */
23
24%{
25#include <sys/types.h>
26#include <sys/queue.h>
27#include <sys/tree.h>
28#include <sys/socket.h>
29#include <sys/stat.h>
30#include <sys/un.h>
31#include <netinet/in.h>
32#include <arpa/inet.h>
33
34#include <ctype.h>
35#include <err.h>
36#include <errno.h>
37#include <ifaddrs.h>
38#include <limits.h>
39#include <netdb.h>
40#include <stdarg.h>
41#include <stdio.h>
42#include <stdlib.h>
43#include <string.h>
44#include <syslog.h>
45#include <unistd.h>
46
47#include "ldapd.h"
48#include "log.h"
49
50TAILQ_HEAD(files, file)		 files = TAILQ_HEAD_INITIALIZER(files);
51static struct file {
52	TAILQ_ENTRY(file)	 entry;
53	FILE			*stream;
54	char			*name;
55	size_t			 ungetpos;
56	size_t			 ungetsize;
57	u_char			*ungetbuf;
58	int			 eof_reached;
59	int			 lineno;
60	int			 errors;
61} *file, *topfile;
62struct file	*pushfile(const char *, int);
63int		 popfile(void);
64int		 check_file_secrecy(int, const char *);
65int		 yyparse(void);
66int		 yylex(void);
67int		 yyerror(const char *, ...)
68    __attribute__((__format__ (printf, 1, 2)))
69    __attribute__((__nonnull__ (1)));
70int		 kw_cmp(const void *, const void *);
71int		 lookup(char *);
72int		 igetc(void);
73int		 lgetc(int);
74void		 lungetc(int);
75int		 findeol(void);
76
77struct listener *host_unix(const char *path);
78struct listener	*host_v4(const char *, in_port_t);
79struct listener	*host_v6(const char *, in_port_t);
80int		 host_dns(const char *, const char *,
81		    struct listenerlist *, in_port_t, u_int8_t);
82int		 host(const char *, const char *,
83		    struct listenerlist *, in_port_t, u_int8_t);
84int		 interface(const char *, const char *,
85		    struct listenerlist *, in_port_t, u_int8_t);
86int		 load_certfile(struct ldapd_config *, const char *, u_int8_t, u_int8_t);
87
88TAILQ_HEAD(symhead, sym)	 symhead = TAILQ_HEAD_INITIALIZER(symhead);
89struct sym {
90	TAILQ_ENTRY(sym)	 entry;
91	int			 used;
92	int			 persist;
93	char			*nam;
94	char			*val;
95};
96int		 symset(const char *, const char *, int);
97char		*symget(const char *);
98
99struct ldapd_config	*conf;
100
101SPLAY_GENERATE(ssltree, ssl, ssl_nodes, ssl_cmp);
102
103static struct aci	*mk_aci(int type, int rights, enum scope scope,
104				char *target, char *subject, char *attr);
105
106typedef struct {
107	union {
108		int64_t		 number;
109		char		*string;
110		struct aci	*aci;
111	} v;
112	int lineno;
113} YYSTYPE;
114
115static struct namespace *current_ns = NULL;
116
117%}
118
119%token	ERROR LISTEN ON LEGACY TLS LDAPS PORT NAMESPACE ROOTDN ROOTPW INDEX
120%token	SECURE RELAX STRICT SCHEMA USE COMPRESSION LEVEL
121%token	INCLUDE CERTIFICATE FSYNC CACHE_SIZE INDEX_CACHE_SIZE
122%token	DENY ALLOW READ WRITE BIND ACCESS TO ROOT REFERRAL
123%token	ANY CHILDREN OF ATTRIBUTE IN SUBTREE BY SELF
124%token	<v.string>	STRING
125%token  <v.number>	NUMBER
126%type	<v.number>	port ssl boolean comp_level legacy protocol
127%type	<v.number>	aci_type aci_access aci_rights aci_right aci_scope
128%type	<v.string>	aci_target aci_attr aci_subject certname
129%type	<v.aci>		aci
130
131%%
132
133grammar		: /* empty */
134		| grammar '\n'
135		| grammar include '\n'
136		| grammar varset '\n'
137		| grammar conf_main '\n'
138		| grammar error '\n'		{ file->errors++; }
139		| grammar namespace '\n'
140		| grammar aci '\n'		{
141			SIMPLEQ_INSERT_TAIL(&conf->acl, $2, entry);
142		}
143		| grammar schema '\n'
144		;
145
146legacy		: /* empty */			{ $$ = 0; }
147		| LEGACY			{ $$ = F_LEGACY; }
148		;
149
150protocol	: /* empty */			{ $$ = 0; }
151		| TLS				{ $$ = F_STARTTLS; }
152		| LDAPS				{ $$ = F_LDAPS; }
153		| SECURE			{ $$ = F_SECURE; }
154		;
155
156ssl		: legacy protocol		{ $$ = $1 | $2; }
157		;
158
159certname	: /* empty */			{ $$ = NULL; }
160		| CERTIFICATE STRING		{ $$ = $2; }
161		;
162
163port		: PORT STRING			{
164			struct servent	*servent;
165
166			servent = getservbyname($2, "tcp");
167			if (servent == NULL) {
168				yyerror("port %s is invalid", $2);
169				free($2);
170				YYERROR;
171			}
172			$$ = servent->s_port;
173			free($2);
174		}
175		| PORT NUMBER			{
176			if ($2 <= 0 || $2 > (int)USHRT_MAX) {
177				yyerror("invalid port: %lld", $2);
178				YYERROR;
179			}
180			$$ = htons($2);
181		}
182		| /* empty */			{
183			$$ = 0;
184		}
185		;
186
187conf_main	: LISTEN ON STRING port ssl certname	{
188			char			*cert;
189
190			if ($4 == 0) {
191				if ($5 & F_LDAPS)
192					$4 = htons(LDAPS_PORT);
193				else
194					$4 = htons(LDAP_PORT);
195			}
196
197			cert = ($6 != NULL) ? $6 : $3;
198
199			if (($5 & F_SSL) &&
200			    load_certfile(conf, cert, F_SCERT, $5) < 0) {
201				yyerror("cannot load certificate: %s", cert);
202				free($6);
203				free($3);
204				YYERROR;
205			}
206
207			if (! interface($3, cert, &conf->listeners,
208			    $4, $5)) {
209				if (host($3, cert, &conf->listeners,
210				    $4, $5) <= 0) {
211					yyerror("invalid virtual ip or interface: %s", $3);
212					free($6);
213					free($3);
214					YYERROR;
215				}
216			}
217			free($6);
218			free($3);
219		}
220		| REFERRAL STRING		{
221			struct referral	*ref;
222			if ((ref = calloc(1, sizeof(*ref))) == NULL) {
223				yyerror("calloc");
224				free($2);
225				YYERROR;
226			}
227			ref->url = $2;
228			SLIST_INSERT_HEAD(&conf->referrals, ref, next);
229		}
230		| ROOTDN STRING			{
231			conf->rootdn = $2;
232			normalize_dn(conf->rootdn);
233		}
234		| ROOTPW STRING			{ conf->rootpw = $2; }
235		;
236
237namespace	: NAMESPACE STRING '{' '\n'		{
238			log_debug("parsing namespace %s", $2);
239			current_ns = namespace_new($2);
240			free($2);
241			TAILQ_INSERT_TAIL(&conf->namespaces, current_ns, next);
242		} ns_opts '}'			{ current_ns = NULL; }
243		;
244
245boolean		: STRING			{
246			if (strcasecmp($1, "true") == 0 ||
247			    strcasecmp($1, "yes") == 0)
248				$$ = 1;
249			else if (strcasecmp($1, "false") == 0 ||
250			    strcasecmp($1, "off") == 0 ||
251			    strcasecmp($1, "no") == 0)
252				$$ = 0;
253			else {
254				yyerror("invalid boolean value '%s'", $1);
255				free($1);
256				YYERROR;
257			}
258			free($1);
259		}
260		| ON				{ $$ = 1; }
261		;
262
263ns_opts		: /* empty */
264		| ns_opts '\n'
265		| ns_opts ns_opt '\n'
266		;
267
268ns_opt		: ROOTDN STRING			{
269			current_ns->rootdn = $2;
270			normalize_dn(current_ns->rootdn);
271		}
272		| ROOTPW STRING			{ current_ns->rootpw = $2; }
273		| INDEX STRING			{
274			struct attr_index	*ai;
275			if ((ai = calloc(1, sizeof(*ai))) == NULL) {
276				yyerror("calloc");
277                                free($2);
278				YYERROR;
279			}
280			ai->attr = $2;
281			ai->type = INDEX_EQUAL;
282			TAILQ_INSERT_TAIL(&current_ns->indices, ai, next);
283		}
284		| CACHE_SIZE NUMBER		{ current_ns->cache_size = $2; }
285		| INDEX_CACHE_SIZE NUMBER	{ current_ns->index_cache_size = $2; }
286		| FSYNC boolean			{ current_ns->sync = $2; }
287		| aci				{
288			SIMPLEQ_INSERT_TAIL(&current_ns->acl, $1, entry);
289		}
290		| RELAX SCHEMA			{ current_ns->relax = 1; }
291		| STRICT SCHEMA			{ current_ns->relax = 0; }
292		| USE COMPRESSION comp_level	{ current_ns->compression_level = $3; }
293		| REFERRAL STRING		{
294			struct referral	*ref;
295			if ((ref = calloc(1, sizeof(*ref))) == NULL) {
296				yyerror("calloc");
297				free($2);
298				YYERROR;
299			}
300			ref->url = $2;
301			SLIST_INSERT_HEAD(&current_ns->referrals, ref, next);
302		}
303		;
304
305comp_level	: /* empty */			{ $$ = 6; }
306		| LEVEL NUMBER			{ $$ = $2; }
307		;
308
309aci		: aci_type aci_access TO aci_scope aci_target aci_attr aci_subject {
310			if (($$ = mk_aci($1, $2, $4, $5, $6, $7)) == NULL) {
311				free($5);
312				free($6);
313				YYERROR;
314			}
315		}
316		| aci_type aci_access {
317			if (($$ = mk_aci($1, $2, LDAP_SCOPE_SUBTREE, NULL,
318			    NULL, NULL)) == NULL) {
319				YYERROR;
320			}
321		}
322		;
323
324aci_type	: DENY				{ $$ = ACI_DENY; }
325		| ALLOW				{ $$ = ACI_ALLOW; }
326		;
327
328aci_access	: /* empty */			{ $$ = ACI_ALL; }
329		| ACCESS			{ $$ = ACI_ALL; }
330		| aci_rights ACCESS		{ $$ = $1; }
331		;
332
333aci_rights	: aci_right			{ $$ = $1; }
334		| aci_rights ',' aci_right	{ $$ = $1 | $3; }
335		;
336
337aci_right	: READ				{ $$ = ACI_READ; }
338		| WRITE				{ $$ = ACI_WRITE; }
339		| BIND				{ $$ = ACI_BIND; }
340		;
341
342
343aci_scope	: /* empty */			{ $$ = LDAP_SCOPE_BASE; }
344		| SUBTREE			{ $$ = LDAP_SCOPE_SUBTREE; }
345		| CHILDREN OF			{ $$ = LDAP_SCOPE_ONELEVEL; }
346		;
347
348aci_target	: ANY				{ $$ = NULL; }
349		| ROOT				{ $$ = strdup(""); }
350		| STRING			{ $$ = $1; normalize_dn($$); }
351		;
352
353aci_attr	: /* empty */			{ $$ = NULL; }
354		| ATTRIBUTE STRING		{ $$ = $2; }
355		;
356
357aci_subject	: /* empty */			{ $$ = NULL; }
358		| BY ANY			{ $$ = NULL; }
359		| BY STRING			{ $$ = $2; normalize_dn($$); }
360		| BY SELF			{ $$ = strdup("@"); }
361		;
362
363include		: INCLUDE STRING		{
364			struct file	*nfile;
365
366			if ((nfile = pushfile($2, 1)) == NULL) {
367				yyerror("failed to include file %s", $2);
368				free($2);
369				YYERROR;
370			}
371			free($2);
372
373			file = nfile;
374			lungetc('\n');
375		}
376		;
377
378varset		: STRING '=' STRING		{
379			char *s = $1;
380			while (*s++) {
381				if (isspace((unsigned char)*s)) {
382					yyerror("macro name cannot contain "
383					    "whitespace");
384					free($1);
385					free($3);
386					YYERROR;
387				}
388			}
389			if (symset($1, $3, 0) == -1)
390				fatal("cannot store variable");
391			free($1);
392			free($3);
393		}
394		;
395
396schema		: SCHEMA STRING			{
397			int	 ret;
398
399			ret = schema_parse(conf->schema, $2);
400			free($2);
401			if (ret != 0) {
402				YYERROR;
403			}
404		}
405		;
406
407%%
408
409struct keywords {
410	const char	*k_name;
411	int		 k_val;
412};
413
414int
415yyerror(const char *fmt, ...)
416{
417	va_list		 ap;
418	char		*msg;
419
420	file->errors++;
421	va_start(ap, fmt);
422	if (vasprintf(&msg, fmt, ap) == -1)
423		fatalx("yyerror vasprintf");
424	va_end(ap);
425	logit(LOG_CRIT, "%s:%d: %s", file->name, yylval.lineno, msg);
426	free(msg);
427	return (0);
428}
429
430int
431kw_cmp(const void *k, const void *e)
432{
433	return (strcmp(k, ((const struct keywords *)e)->k_name));
434}
435
436int
437lookup(char *s)
438{
439	/* this has to be sorted always */
440	static const struct keywords keywords[] = {
441		{ "access",		ACCESS },
442		{ "allow",		ALLOW },
443		{ "any",		ANY },
444		{ "attribute",		ATTRIBUTE },
445		{ "bind",		BIND },
446		{ "by",			BY },
447		{ "cache-size",		CACHE_SIZE },
448		{ "certificate",	CERTIFICATE },
449		{ "children",		CHILDREN },
450		{ "compression",	COMPRESSION },
451		{ "deny",		DENY },
452		{ "fsync",		FSYNC },
453		{ "in",			IN },
454		{ "include",		INCLUDE },
455		{ "index",		INDEX },
456		{ "index-cache-size",	INDEX_CACHE_SIZE },
457		{ "ldaps",		LDAPS },
458		{ "legacy",		LEGACY },
459		{ "level",		LEVEL },
460		{ "listen",		LISTEN },
461		{ "namespace",		NAMESPACE },
462		{ "of",			OF },
463		{ "on",			ON },
464		{ "port",		PORT },
465		{ "read",		READ },
466		{ "referral",		REFERRAL },
467		{ "relax",		RELAX },
468		{ "root",		ROOT },
469		{ "rootdn",		ROOTDN },
470		{ "rootpw",		ROOTPW },
471		{ "schema",		SCHEMA },
472		{ "secure",		SECURE },
473		{ "self",		SELF },
474		{ "strict",		STRICT },
475		{ "subtree",		SUBTREE },
476		{ "tls",		TLS },
477		{ "to",			TO },
478		{ "use",		USE },
479		{ "write",		WRITE },
480
481	};
482	const struct keywords	*p;
483
484	p = bsearch(s, keywords, sizeof(keywords)/sizeof(keywords[0]),
485	    sizeof(keywords[0]), kw_cmp);
486
487	if (p)
488		return (p->k_val);
489	else
490		return (STRING);
491}
492
493#define	START_EXPAND	1
494#define	DONE_EXPAND	2
495
496static int	expanding;
497
498int
499igetc(void)
500{
501	int	c;
502
503	while (1) {
504		if (file->ungetpos > 0)
505			c = file->ungetbuf[--file->ungetpos];
506		else
507			c = getc(file->stream);
508
509		if (c == START_EXPAND)
510			expanding = 1;
511		else if (c == DONE_EXPAND)
512			expanding = 0;
513		else
514			break;
515	}
516	return (c);
517}
518
519int
520lgetc(int quotec)
521{
522	int		c, next;
523
524	if (quotec) {
525		if ((c = igetc()) == EOF) {
526			yyerror("reached end of file while parsing "
527			    "quoted string");
528			if (file == topfile || popfile() == EOF)
529				return (EOF);
530			return (quotec);
531		}
532		return (c);
533	}
534
535	while ((c = igetc()) == '\\') {
536		next = igetc();
537		if (next != '\n') {
538			c = next;
539			break;
540		}
541		yylval.lineno = file->lineno;
542		file->lineno++;
543	}
544
545	if (c == EOF) {
546		/*
547		 * Fake EOL when hit EOF for the first time. This gets line
548		 * count right if last line in included file is syntactically
549		 * invalid and has no newline.
550		 */
551		if (file->eof_reached == 0) {
552			file->eof_reached = 1;
553			return ('\n');
554		}
555		while (c == EOF) {
556			if (file == topfile || popfile() == EOF)
557				return (EOF);
558			c = igetc();
559		}
560	}
561	return (c);
562}
563
564void
565lungetc(int c)
566{
567	if (c == EOF)
568		return;
569
570	if (file->ungetpos >= file->ungetsize) {
571		void *p = reallocarray(file->ungetbuf, file->ungetsize, 2);
572		if (p == NULL)
573			err(1, "%s", __func__);
574		file->ungetbuf = p;
575		file->ungetsize *= 2;
576	}
577	file->ungetbuf[file->ungetpos++] = c;
578}
579
580int
581findeol(void)
582{
583	int	c;
584
585	/* skip to either EOF or the first real EOL */
586	while (1) {
587		c = lgetc(0);
588		if (c == '\n') {
589			file->lineno++;
590			break;
591		}
592		if (c == EOF)
593			break;
594	}
595	return (ERROR);
596}
597
598int
599yylex(void)
600{
601	u_char	 buf[4096];
602	u_char	*p, *val;
603	int	 quotec, next, c;
604	int	 token;
605
606top:
607	p = buf;
608	while ((c = lgetc(0)) == ' ' || c == '\t')
609		; /* nothing */
610
611	yylval.lineno = file->lineno;
612	if (c == '#')
613		while ((c = lgetc(0)) != '\n' && c != EOF)
614			; /* nothing */
615	if (c == '$' && !expanding) {
616		while (1) {
617			if ((c = lgetc(0)) == EOF)
618				return (0);
619
620			if (p + 1 >= buf + sizeof(buf) - 1) {
621				yyerror("string too long");
622				return (findeol());
623			}
624			if (isalnum(c) || c == '_') {
625				*p++ = c;
626				continue;
627			}
628			*p = '\0';
629			lungetc(c);
630			break;
631		}
632		val = symget(buf);
633		if (val == NULL) {
634			yyerror("macro '%s' not defined", buf);
635			return (findeol());
636		}
637		p = val + strlen(val) - 1;
638		lungetc(DONE_EXPAND);
639		while (p >= val) {
640			lungetc(*p);
641			p--;
642		}
643		lungetc(START_EXPAND);
644		goto top;
645	}
646
647	switch (c) {
648	case '\'':
649	case '"':
650		quotec = c;
651		while (1) {
652			if ((c = lgetc(quotec)) == EOF)
653				return (0);
654			if (c == '\n') {
655				file->lineno++;
656				continue;
657			} else if (c == '\\') {
658				if ((next = lgetc(quotec)) == EOF)
659					return (0);
660				if (next == quotec || next == ' ' ||
661				    next == '\t')
662					c = next;
663				else if (next == '\n') {
664					file->lineno++;
665					continue;
666				} else
667					lungetc(next);
668			} else if (c == quotec) {
669				*p = '\0';
670				break;
671			} else if (c == '\0') {
672				yyerror("syntax error");
673				return (findeol());
674			}
675			if (p + 1 >= buf + sizeof(buf) - 1) {
676				log_warnx("string too long");
677				return (findeol());
678			}
679			*p++ = c;
680		}
681		yylval.v.string = strdup(buf);
682		if (yylval.v.string == NULL)
683			fatal("yylex: strdup");
684		return (STRING);
685	}
686
687#define allowed_to_end_number(x) \
688	(isspace(x) || x == ')' || x ==',' || x == '/' || x == '}' || x == '=')
689
690	if (c == '-' || isdigit(c)) {
691		do {
692			*p++ = c;
693			if ((size_t)(p-buf) >= sizeof(buf)) {
694				yyerror("string too long");
695				return (findeol());
696			}
697		} while ((c = lgetc(0)) != EOF && isdigit(c));
698		lungetc(c);
699		if (p == buf + 1 && buf[0] == '-')
700			goto nodigits;
701		if (c == EOF || allowed_to_end_number(c)) {
702			const char *errstr = NULL;
703
704			*p = '\0';
705			yylval.v.number = strtonum(buf, LLONG_MIN,
706			    LLONG_MAX, &errstr);
707			if (errstr) {
708				yyerror("\"%s\" invalid number: %s",
709				    buf, errstr);
710				return (findeol());
711			}
712			return (NUMBER);
713		} else {
714nodigits:
715			while (p > buf + 1)
716				lungetc(*--p);
717			c = *--p;
718			if (c == '-')
719				return (c);
720		}
721	}
722
723#define allowed_in_string(x) \
724	(isalnum(x) || (ispunct(x) && x != '(' && x != ')' && \
725	x != '{' && x != '}' && x != '<' && x != '>' && \
726	x != '!' && x != '=' && x != '/' && x != '#' && \
727	x != ','))
728
729	if (isalnum(c) || c == ':' || c == '_' || c == '*') {
730		do {
731			*p++ = c;
732			if ((size_t)(p-buf) >= sizeof(buf)) {
733				yyerror("string too long");
734				return (findeol());
735			}
736		} while ((c = lgetc(0)) != EOF && (allowed_in_string(c)));
737		lungetc(c);
738		*p = '\0';
739		if ((token = lookup(buf)) == STRING)
740			if ((yylval.v.string = strdup(buf)) == NULL)
741				fatal("yylex: strdup");
742		return (token);
743	}
744	if (c == '\n') {
745		yylval.lineno = file->lineno;
746		file->lineno++;
747	}
748	if (c == EOF)
749		return (0);
750	return (c);
751}
752
753int
754check_file_secrecy(int fd, const char *fname)
755{
756	struct stat	st;
757
758	if (fstat(fd, &st)) {
759		log_warn("cannot stat %s", fname);
760		return (-1);
761	}
762	if (st.st_uid != 0 && st.st_uid != getuid()) {
763		log_warnx("%s: owner not root or current user", fname);
764		return (-1);
765	}
766	if (st.st_mode & (S_IWGRP | S_IXGRP | S_IRWXO)) {
767		log_warnx("%s: group writable or world read/writable", fname);
768		return (-1);
769	}
770	return (0);
771}
772
773struct file *
774pushfile(const char *name, int secret)
775{
776	struct file	*nfile;
777
778	log_debug("parsing config %s", name);
779
780	if ((nfile = calloc(1, sizeof(struct file))) == NULL) {
781		log_warn("%s", __func__);
782		return (NULL);
783	}
784	if ((nfile->name = strdup(name)) == NULL) {
785		log_warn("%s", __func__);
786		free(nfile);
787		return (NULL);
788	}
789	if ((nfile->stream = fopen(nfile->name, "r")) == NULL) {
790		log_warn("%s: %s", __func__, nfile->name);
791		free(nfile->name);
792		free(nfile);
793		return (NULL);
794	}
795	if (secret &&
796	    check_file_secrecy(fileno(nfile->stream), nfile->name)) {
797		fclose(nfile->stream);
798		free(nfile->name);
799		free(nfile);
800		return (NULL);
801	}
802	nfile->lineno = TAILQ_EMPTY(&files) ? 1 : 0;
803	nfile->ungetsize = 16;
804	nfile->ungetbuf = malloc(nfile->ungetsize);
805	if (nfile->ungetbuf == NULL) {
806		log_warn("%s", __func__);
807		fclose(nfile->stream);
808		free(nfile->name);
809		free(nfile);
810		return (NULL);
811	}
812	TAILQ_INSERT_TAIL(&files, nfile, entry);
813	return (nfile);
814}
815
816int
817popfile(void)
818{
819	struct file	*prev;
820
821	if ((prev = TAILQ_PREV(file, files, entry)) != NULL)
822		prev->errors += file->errors;
823
824	TAILQ_REMOVE(&files, file, entry);
825	fclose(file->stream);
826	free(file->name);
827	free(file->ungetbuf);
828	free(file);
829	file = prev;
830	return (file ? 0 : EOF);
831}
832
833int
834parse_config(char *filename)
835{
836	struct sym		*sym, *next;
837	int			 errors = 0;
838
839	if ((conf = calloc(1, sizeof(struct ldapd_config))) == NULL)
840		fatal(NULL);
841
842	conf->schema = schema_new();
843	if (conf->schema == NULL)
844		fatal("schema_new");
845
846	TAILQ_INIT(&conf->namespaces);
847	TAILQ_INIT(&conf->listeners);
848	if ((conf->sc_ssl = calloc(1, sizeof(*conf->sc_ssl))) == NULL)
849		fatal(NULL);
850	SPLAY_INIT(conf->sc_ssl);
851	SIMPLEQ_INIT(&conf->acl);
852	SLIST_INIT(&conf->referrals);
853
854	if ((file = pushfile(filename, 1)) == NULL) {
855		free(conf);
856		return (-1);
857	}
858	topfile = file;
859
860	yyparse();
861	errors = file->errors;
862	popfile();
863
864	/* Free macros and check which have not been used. */
865	TAILQ_FOREACH_SAFE(sym, &symhead, entry, next) {
866		log_debug("warning: macro \"%s\" not used", sym->nam);
867		if (!sym->persist) {
868			free(sym->nam);
869			free(sym->val);
870			TAILQ_REMOVE(&symhead, sym, entry);
871			free(sym);
872		}
873	}
874
875	return (errors ? -1 : 0);
876}
877
878int
879symset(const char *nam, const char *val, int persist)
880{
881	struct sym	*sym;
882
883	TAILQ_FOREACH(sym, &symhead, entry) {
884		if (strcmp(nam, sym->nam) == 0)
885			break;
886	}
887
888	if (sym != NULL) {
889		if (sym->persist == 1)
890			return (0);
891		else {
892			free(sym->nam);
893			free(sym->val);
894			TAILQ_REMOVE(&symhead, sym, entry);
895			free(sym);
896		}
897	}
898	if ((sym = calloc(1, sizeof(*sym))) == NULL)
899		return (-1);
900
901	sym->nam = strdup(nam);
902	if (sym->nam == NULL) {
903		free(sym);
904		return (-1);
905	}
906	sym->val = strdup(val);
907	if (sym->val == NULL) {
908		free(sym->nam);
909		free(sym);
910		return (-1);
911	}
912	sym->used = 0;
913	sym->persist = persist;
914	TAILQ_INSERT_TAIL(&symhead, sym, entry);
915	return (0);
916}
917
918int
919cmdline_symset(char *s)
920{
921	char	*sym, *val;
922	int	ret;
923
924	if ((val = strrchr(s, '=')) == NULL)
925		return (-1);
926	sym = strndup(s, val - s);
927	if (sym == NULL)
928		fatal("%s: strndup", __func__);
929	ret = symset(sym, val + 1, 1);
930	free(sym);
931
932	return (ret);
933}
934
935char *
936symget(const char *nam)
937{
938	struct sym	*sym;
939
940	TAILQ_FOREACH(sym, &symhead, entry) {
941		if (strcmp(nam, sym->nam) == 0) {
942			sym->used = 1;
943			return (sym->val);
944		}
945	}
946	return (NULL);
947}
948
949struct listener *
950host_unix(const char *path)
951{
952	struct sockaddr_un	*saun;
953	struct listener		*h;
954
955	if (*path != '/')
956		return (NULL);
957
958	if ((h = calloc(1, sizeof(*h))) == NULL)
959		fatal(NULL);
960	saun = (struct sockaddr_un *)&h->ss;
961	saun->sun_len = sizeof(struct sockaddr_un);
962	saun->sun_family = AF_UNIX;
963	if (strlcpy(saun->sun_path, path, sizeof(saun->sun_path)) >=
964	    sizeof(saun->sun_path))
965		fatal("socket path too long");
966	h->flags = F_SECURE;
967
968	return (h);
969}
970
971struct listener *
972host_v4(const char *s, in_port_t port)
973{
974	struct in_addr		 ina;
975	struct sockaddr_in	*sain;
976	struct listener		*h;
977
978	memset(&ina, 0, sizeof(ina));
979	if (inet_pton(AF_INET, s, &ina) != 1)
980		return (NULL);
981
982	if ((h = calloc(1, sizeof(*h))) == NULL)
983		fatal(NULL);
984	sain = (struct sockaddr_in *)&h->ss;
985	sain->sin_len = sizeof(struct sockaddr_in);
986	sain->sin_family = AF_INET;
987	sain->sin_addr.s_addr = ina.s_addr;
988	sain->sin_port = port;
989
990	return (h);
991}
992
993struct listener *
994host_v6(const char *s, in_port_t port)
995{
996	struct in6_addr		 ina6;
997	struct sockaddr_in6	*sin6;
998	struct listener		*h;
999
1000	memset(&ina6, 0, sizeof(ina6));
1001	if (inet_pton(AF_INET6, s, &ina6) != 1)
1002		return (NULL);
1003
1004	if ((h = calloc(1, sizeof(*h))) == NULL)
1005		fatal(NULL);
1006	sin6 = (struct sockaddr_in6 *)&h->ss;
1007	sin6->sin6_len = sizeof(struct sockaddr_in6);
1008	sin6->sin6_family = AF_INET6;
1009	sin6->sin6_port = port;
1010	memcpy(&sin6->sin6_addr, &ina6, sizeof(ina6));
1011
1012	return (h);
1013}
1014
1015int
1016host_dns(const char *s, const char *cert,
1017    struct listenerlist *al, in_port_t port, u_int8_t flags)
1018{
1019	struct addrinfo		 hints, *res0, *res;
1020	int			 error;
1021	struct sockaddr_in	*sain;
1022	struct sockaddr_in6	*sin6;
1023	struct listener		*h;
1024
1025	memset(&hints, 0, sizeof(hints));
1026	hints.ai_family = PF_UNSPEC;
1027	hints.ai_socktype = SOCK_DGRAM; /* DUMMY */
1028	error = getaddrinfo(s, NULL, &hints, &res0);
1029	if (error == EAI_AGAIN || error == EAI_NODATA || error == EAI_NONAME)
1030		return (0);
1031	if (error) {
1032		log_warnx("host_dns: could not parse \"%s\": %s", s,
1033		    gai_strerror(error));
1034		return (-1);
1035	}
1036
1037	for (res = res0; res; res = res->ai_next) {
1038		if (res->ai_family != AF_INET &&
1039		    res->ai_family != AF_INET6)
1040			continue;
1041		if ((h = calloc(1, sizeof(*h))) == NULL)
1042			fatal(NULL);
1043
1044		h->port = port;
1045		h->flags = flags;
1046		h->ss.ss_family = res->ai_family;
1047		h->ssl = NULL;
1048		h->ssl_cert_name[0] = '\0';
1049		if (cert != NULL)
1050			(void)strlcpy(h->ssl_cert_name, cert, sizeof(h->ssl_cert_name));
1051
1052		if (res->ai_family == AF_INET) {
1053			sain = (struct sockaddr_in *)&h->ss;
1054			sain->sin_len = sizeof(struct sockaddr_in);
1055			sain->sin_addr.s_addr = ((struct sockaddr_in *)
1056			    res->ai_addr)->sin_addr.s_addr;
1057			sain->sin_port = port;
1058		} else {
1059			sin6 = (struct sockaddr_in6 *)&h->ss;
1060			sin6->sin6_len = sizeof(struct sockaddr_in6);
1061			memcpy(&sin6->sin6_addr, &((struct sockaddr_in6 *)
1062			    res->ai_addr)->sin6_addr, sizeof(struct in6_addr));
1063			sin6->sin6_port = port;
1064		}
1065
1066		TAILQ_INSERT_HEAD(al, h, entry);
1067	}
1068	freeaddrinfo(res0);
1069	return 1;
1070}
1071
1072int
1073host(const char *s, const char *cert, struct listenerlist *al,
1074    in_port_t port, u_int8_t flags)
1075{
1076	struct listener *h;
1077
1078	/* Unix socket path? */
1079	h = host_unix(s);
1080
1081	/* IPv4 address? */
1082	if (h == NULL)
1083		h = host_v4(s, port);
1084
1085	/* IPv6 address? */
1086	if (h == NULL)
1087		h = host_v6(s, port);
1088
1089	if (h != NULL) {
1090		h->port = port;
1091		h->flags |= flags;
1092		h->ssl = NULL;
1093		h->ssl_cert_name[0] = '\0';
1094		if (cert != NULL)
1095			strlcpy(h->ssl_cert_name, cert, sizeof(h->ssl_cert_name));
1096
1097		TAILQ_INSERT_HEAD(al, h, entry);
1098		return (1);
1099	}
1100
1101	return (host_dns(s, cert, al, port, flags));
1102}
1103
1104int
1105interface(const char *s, const char *cert,
1106    struct listenerlist *al, in_port_t port, u_int8_t flags)
1107{
1108	int			 ret = 0;
1109	struct ifaddrs		*ifap, *p;
1110	struct sockaddr_in	*sain;
1111	struct sockaddr_in6	*sin6;
1112	struct listener		*h;
1113
1114	if (getifaddrs(&ifap) == -1)
1115		fatal("getifaddrs");
1116
1117	for (p = ifap; p != NULL; p = p->ifa_next) {
1118		if (strcmp(s, p->ifa_name) != 0)
1119			continue;
1120		if (p->ifa_addr == NULL)
1121			continue;
1122
1123		switch (p->ifa_addr->sa_family) {
1124		case AF_INET:
1125			if ((h = calloc(1, sizeof(*h))) == NULL)
1126				fatal(NULL);
1127			sain = (struct sockaddr_in *)&h->ss;
1128			*sain = *(struct sockaddr_in *)p->ifa_addr;
1129			sain->sin_len = sizeof(struct sockaddr_in);
1130			sain->sin_port = port;
1131
1132			h->fd = -1;
1133			h->port = port;
1134			h->flags = flags;
1135			h->ssl = NULL;
1136			h->ssl_cert_name[0] = '\0';
1137			if (cert != NULL)
1138				(void)strlcpy(h->ssl_cert_name, cert, sizeof(h->ssl_cert_name));
1139
1140			ret = 1;
1141			TAILQ_INSERT_HEAD(al, h, entry);
1142
1143			break;
1144
1145		case AF_INET6:
1146			if ((h = calloc(1, sizeof(*h))) == NULL)
1147				fatal(NULL);
1148			sin6 = (struct sockaddr_in6 *)&h->ss;
1149			*sin6 = *(struct sockaddr_in6 *)p->ifa_addr;
1150			sin6->sin6_len = sizeof(struct sockaddr_in6);
1151			sin6->sin6_port = port;
1152
1153			h->fd = -1;
1154			h->port = port;
1155			h->flags = flags;
1156			h->ssl = NULL;
1157			h->ssl_cert_name[0] = '\0';
1158			if (cert != NULL)
1159				(void)strlcpy(h->ssl_cert_name, cert, sizeof(h->ssl_cert_name));
1160
1161			ret = 1;
1162			TAILQ_INSERT_HEAD(al, h, entry);
1163
1164			break;
1165		}
1166	}
1167
1168	freeifaddrs(ifap);
1169
1170	return ret;
1171}
1172
1173static struct aci *
1174mk_aci(int type, int rights, enum scope scope, char *target, char *attr,
1175    char *subject)
1176{
1177	struct aci	*aci;
1178
1179	if ((aci = calloc(1, sizeof(*aci))) == NULL) {
1180		yyerror("calloc");
1181		return NULL;
1182	}
1183	aci->type = type;
1184	aci->rights = rights;
1185	aci->scope = scope;
1186	aci->target = target;
1187	aci->attribute = attr;
1188	aci->subject = subject;
1189
1190	log_debug("%s %02X access to %s%s%s scope %d by %s",
1191	    aci->type == ACI_DENY ? "deny" : "allow",
1192	    aci->rights,
1193	    aci->target ? aci->target : "any",
1194	    aci->attribute ? " attribute " : "",
1195	    aci->attribute ? aci->attribute : "",
1196	    aci->scope,
1197	    aci->subject ? aci->subject : "any");
1198
1199	return aci;
1200}
1201
1202struct namespace *
1203namespace_new(const char *suffix)
1204{
1205	struct namespace		*ns;
1206
1207	if ((ns = calloc(1, sizeof(*ns))) == NULL)
1208		return NULL;
1209	ns->suffix = strdup(suffix);
1210	ns->sync = 1;
1211	ns->cache_size = 1024;
1212	ns->index_cache_size = 512;
1213	if (ns->suffix == NULL) {
1214		free(ns->suffix);
1215		free(ns);
1216		return NULL;
1217	}
1218	TAILQ_INIT(&ns->indices);
1219	TAILQ_INIT(&ns->request_queue);
1220	SIMPLEQ_INIT(&ns->acl);
1221	SLIST_INIT(&ns->referrals);
1222
1223	return ns;
1224}
1225
1226int
1227ssl_cmp(struct ssl *s1, struct ssl *s2)
1228{
1229	return (strcmp(s1->ssl_name, s2->ssl_name));
1230}
1231
1232int
1233load_certfile(struct ldapd_config *env, const char *name, u_int8_t flags,
1234    u_int8_t protocol)
1235{
1236	struct ssl	*s;
1237	struct ssl	 key;
1238	char		 certfile[PATH_MAX];
1239	uint32_t	 tls_protocols = TLS_PROTOCOLS_DEFAULT;
1240	const char	*tls_ciphers = "default";
1241
1242	if (strlcpy(key.ssl_name, name, sizeof(key.ssl_name))
1243	    >= sizeof(key.ssl_name)) {
1244		log_warn("load_certfile: certificate name truncated");
1245		return -1;
1246	}
1247
1248	s = SPLAY_FIND(ssltree, env->sc_ssl, &key);
1249	if (s != NULL) {
1250		s->flags |= flags;
1251		return 0;
1252	}
1253
1254	if ((s = calloc(1, sizeof(*s))) == NULL)
1255		fatal(NULL);
1256
1257	s->flags = flags;
1258	(void)strlcpy(s->ssl_name, key.ssl_name, sizeof(s->ssl_name));
1259
1260	s->config = tls_config_new();
1261	if (s->config == NULL)
1262		goto err;
1263
1264	if (protocol & F_LEGACY) {
1265		tls_protocols = TLS_PROTOCOLS_ALL;
1266		tls_ciphers = "all";
1267	}
1268	if (tls_config_set_protocols(s->config, tls_protocols) != 0) {
1269		log_warn("load_certfile: failed to set tls protocols: %s",
1270		    tls_config_error(s->config));
1271		goto err;
1272	}
1273	if (tls_config_set_ciphers(s->config, tls_ciphers)) {
1274		log_warn("load_certfile: failed to set tls ciphers: %s",
1275		    tls_config_error(s->config));
1276		goto err;
1277	}
1278
1279	if (name[0] == '/') {
1280		if (!bsnprintf(certfile, sizeof(certfile), "%s.crt", name)) {
1281			log_warn("load_certfile: path truncated");
1282			goto err;
1283		}
1284	} else {
1285		if (!bsnprintf(certfile, sizeof(certfile),
1286		    "/etc/ldap/certs/%s.crt", name)) {
1287			log_warn("load_certfile: path truncated");
1288			goto err;
1289		}
1290	}
1291
1292	log_debug("loading certificate file %s", certfile);
1293	s->ssl_cert = tls_load_file(certfile, &s->ssl_cert_len, NULL);
1294	if (s->ssl_cert == NULL)
1295		goto err;
1296
1297	if (tls_config_set_cert_mem(s->config, s->ssl_cert, s->ssl_cert_len)) {
1298		log_warn("load_certfile: failed to set tls certificate: %s",
1299		    tls_config_error(s->config));
1300		goto err;
1301	}
1302
1303	if (name[0] == '/') {
1304		if (!bsnprintf(certfile, sizeof(certfile), "%s.key", name)) {
1305			log_warn("load_certfile: path truncated");
1306			goto err;
1307		}
1308	} else {
1309		if (!bsnprintf(certfile, sizeof(certfile),
1310		    "/etc/ldap/certs/%s.key", name)) {
1311			log_warn("load_certfile: path truncated");
1312			goto err;
1313		}
1314	}
1315
1316	log_debug("loading key file %s", certfile);
1317	s->ssl_key = tls_load_file(certfile, &s->ssl_key_len, NULL);
1318	if (s->ssl_key == NULL)
1319		goto err;
1320
1321	if (tls_config_set_key_mem(s->config, s->ssl_key, s->ssl_key_len)) {
1322		log_warn("load_certfile: failed to set tls key: %s",
1323		    tls_config_error(s->config));
1324		goto err;
1325	}
1326
1327	SPLAY_INSERT(ssltree, env->sc_ssl, s);
1328
1329	return (0);
1330err:
1331	free(s->ssl_cert);
1332	free(s->ssl_key);
1333	tls_config_free(s->config);
1334	free(s);
1335	return (-1);
1336}
1337