xref: /openbsd/usr.sbin/ldapd/parse.y (revision 08f6ba19)
1 /*	$OpenBSD: parse.y,v 1.43 2021/10/15 15:01:28 naddy 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 
50 TAILQ_HEAD(files, file)		 files = TAILQ_HEAD_INITIALIZER(files);
51 static 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;
62 struct file	*pushfile(const char *, int);
63 int		 popfile(void);
64 int		 check_file_secrecy(int, const char *);
65 int		 yyparse(void);
66 int		 yylex(void);
67 int		 yyerror(const char *, ...)
68     __attribute__((__format__ (printf, 1, 2)))
69     __attribute__((__nonnull__ (1)));
70 int		 kw_cmp(const void *, const void *);
71 int		 lookup(char *);
72 int		 igetc(void);
73 int		 lgetc(int);
74 void		 lungetc(int);
75 int		 findeol(void);
76 
77 struct listener *host_unix(const char *path);
78 struct listener	*host_v4(const char *, in_port_t);
79 struct listener	*host_v6(const char *, in_port_t);
80 int		 host_dns(const char *, const char *,
81 		    struct listenerlist *, in_port_t, u_int8_t);
82 int		 host(const char *, const char *,
83 		    struct listenerlist *, in_port_t, u_int8_t);
84 int		 interface(const char *, const char *,
85 		    struct listenerlist *, in_port_t, u_int8_t);
86 int		 load_certfile(struct ldapd_config *, const char *, u_int8_t, u_int8_t);
87 
88 TAILQ_HEAD(symhead, sym)	 symhead = TAILQ_HEAD_INITIALIZER(symhead);
89 struct sym {
90 	TAILQ_ENTRY(sym)	 entry;
91 	int			 used;
92 	int			 persist;
93 	char			*nam;
94 	char			*val;
95 };
96 int		 symset(const char *, const char *, int);
97 char		*symget(const char *);
98 
99 struct ldapd_config	*conf;
100 
101 SPLAY_GENERATE(ssltree, ssl, ssl_nodes, ssl_cmp);
102 
103 static struct aci	*mk_aci(int type, int rights, enum scope scope,
104 				char *target, char *subject, char *attr);
105 
106 typedef struct {
107 	union {
108 		int64_t		 number;
109 		char		*string;
110 		struct aci	*aci;
111 	} v;
112 	int lineno;
113 } YYSTYPE;
114 
115 static 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 
133 grammar		: /* 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 
146 legacy		: /* empty */			{ $$ = 0; }
147 		| LEGACY			{ $$ = F_LEGACY; }
148 		;
149 
150 protocol	: /* empty */			{ $$ = 0; }
151 		| TLS				{ $$ = F_STARTTLS; }
152 		| LDAPS				{ $$ = F_LDAPS; }
153 		| SECURE			{ $$ = F_SECURE; }
154 		;
155 
156 ssl		: legacy protocol		{ $$ = $1 | $2; }
157 		;
158 
159 certname	: /* empty */			{ $$ = NULL; }
160 		| CERTIFICATE STRING		{ $$ = $2; }
161 		;
162 
163 port		: 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 
187 conf_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) != 1) {
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 
237 namespace	: 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 
245 boolean		: 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 
263 ns_opts		: /* empty */
264 		| ns_opts '\n'
265 		| ns_opts ns_opt '\n'
266 		;
267 
268 ns_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 
305 comp_level	: /* empty */			{ $$ = 6; }
306 		| LEVEL NUMBER			{ $$ = $2; }
307 		;
308 
309 aci		: 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 
324 aci_type	: DENY				{ $$ = ACI_DENY; }
325 		| ALLOW				{ $$ = ACI_ALLOW; }
326 		;
327 
328 aci_access	: /* empty */			{ $$ = ACI_ALL; }
329 		| ACCESS			{ $$ = ACI_ALL; }
330 		| aci_rights ACCESS		{ $$ = $1; }
331 		;
332 
333 aci_rights	: aci_right			{ $$ = $1; }
334 		| aci_rights ',' aci_right	{ $$ = $1 | $3; }
335 		;
336 
337 aci_right	: READ				{ $$ = ACI_READ; }
338 		| WRITE				{ $$ = ACI_WRITE; }
339 		| BIND				{ $$ = ACI_BIND; }
340 		;
341 
342 
343 aci_scope	: /* empty */			{ $$ = LDAP_SCOPE_BASE; }
344 		| SUBTREE			{ $$ = LDAP_SCOPE_SUBTREE; }
345 		| CHILDREN OF			{ $$ = LDAP_SCOPE_ONELEVEL; }
346 		;
347 
348 aci_target	: ANY				{ $$ = NULL; }
349 		| ROOT				{ $$ = strdup(""); }
350 		| STRING			{ $$ = $1; normalize_dn($$); }
351 		;
352 
353 aci_attr	: /* empty */			{ $$ = NULL; }
354 		| ATTRIBUTE STRING		{ $$ = $2; }
355 		;
356 
357 aci_subject	: /* empty */			{ $$ = NULL; }
358 		| BY ANY			{ $$ = NULL; }
359 		| BY STRING			{ $$ = $2; normalize_dn($$); }
360 		| BY SELF			{ $$ = strdup("@"); }
361 		;
362 
363 include		: 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 
378 varset		: 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 
396 schema		: 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 
409 struct keywords {
410 	const char	*k_name;
411 	int		 k_val;
412 };
413 
414 int
yyerror(const char * fmt,...)415 yyerror(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 
430 int
kw_cmp(const void * k,const void * e)431 kw_cmp(const void *k, const void *e)
432 {
433 	return (strcmp(k, ((const struct keywords *)e)->k_name));
434 }
435 
436 int
lookup(char * s)437 lookup(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 
496 static int	expanding;
497 
498 int
igetc(void)499 igetc(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 
519 int
lgetc(int quotec)520 lgetc(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 
564 void
lungetc(int c)565 lungetc(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 
580 int
findeol(void)581 findeol(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 
598 int
yylex(void)599 yylex(void)
600 {
601 	char	 buf[4096];
602 	char	*p, *val;
603 	int	 quotec, next, c;
604 	int	 token;
605 
606 top:
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((unsigned char)*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 {
714 nodigits:
715 			while (p > buf + 1)
716 				lungetc((unsigned char)*--p);
717 			c = (unsigned char)*--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 
753 int
check_file_secrecy(int fd,const char * fname)754 check_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 
773 struct file *
pushfile(const char * name,int secret)774 pushfile(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 
816 int
popfile(void)817 popfile(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 
833 int
parse_config(char * filename)834 parse_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 
878 int
symset(const char * nam,const char * val,int persist)879 symset(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 
918 int
cmdline_symset(char * s)919 cmdline_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 
935 char *
symget(const char * nam)936 symget(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 
949 struct listener *
host_unix(const char * path)950 host_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 
971 struct listener *
host_v4(const char * s,in_port_t port)972 host_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 
993 struct listener *
host_v6(const char * s,in_port_t port)994 host_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 
1015 int
host_dns(const char * s,const char * cert,struct listenerlist * al,in_port_t port,u_int8_t flags)1016 host_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 
1072 int
host(const char * s,const char * cert,struct listenerlist * al,in_port_t port,u_int8_t flags)1073 host(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 
1104 int
interface(const char * s,const char * cert,struct listenerlist * al,in_port_t port,u_int8_t flags)1105 interface(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 
1173 static struct aci *
mk_aci(int type,int rights,enum scope scope,char * target,char * attr,char * subject)1174 mk_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 
1202 struct namespace *
namespace_new(const char * suffix)1203 namespace_new(const char *suffix)
1204 {
1205 	struct namespace		*ns;
1206 
1207 	if ((ns = calloc(1, sizeof(*ns))) == NULL)
1208 		return NULL;
1209 	ns->sync = 1;
1210 	ns->cache_size = 1024;
1211 	ns->index_cache_size = 512;
1212 	ns->suffix = strdup(suffix);
1213 	if (ns->suffix == NULL) {
1214 		free(ns->suffix);
1215 		free(ns);
1216 		return NULL;
1217 	}
1218 	normalize_dn(ns->suffix);
1219 	TAILQ_INIT(&ns->indices);
1220 	TAILQ_INIT(&ns->request_queue);
1221 	SIMPLEQ_INIT(&ns->acl);
1222 	SLIST_INIT(&ns->referrals);
1223 
1224 	return ns;
1225 }
1226 
1227 int
ssl_cmp(struct ssl * s1,struct ssl * s2)1228 ssl_cmp(struct ssl *s1, struct ssl *s2)
1229 {
1230 	return (strcmp(s1->ssl_name, s2->ssl_name));
1231 }
1232 
1233 int
load_certfile(struct ldapd_config * env,const char * name,u_int8_t flags,u_int8_t protocol)1234 load_certfile(struct ldapd_config *env, const char *name, u_int8_t flags,
1235     u_int8_t protocol)
1236 {
1237 	struct ssl	*s;
1238 	struct ssl	 key;
1239 	char		 certfile[PATH_MAX];
1240 	uint32_t	 tls_protocols = TLS_PROTOCOLS_DEFAULT;
1241 	const char	*tls_ciphers = "default";
1242 
1243 	if (strlcpy(key.ssl_name, name, sizeof(key.ssl_name))
1244 	    >= sizeof(key.ssl_name)) {
1245 		log_warn("load_certfile: certificate name truncated");
1246 		return -1;
1247 	}
1248 
1249 	s = SPLAY_FIND(ssltree, env->sc_ssl, &key);
1250 	if (s != NULL) {
1251 		s->flags |= flags;
1252 		return 0;
1253 	}
1254 
1255 	if ((s = calloc(1, sizeof(*s))) == NULL)
1256 		fatal(NULL);
1257 
1258 	s->flags = flags;
1259 	(void)strlcpy(s->ssl_name, key.ssl_name, sizeof(s->ssl_name));
1260 
1261 	s->config = tls_config_new();
1262 	if (s->config == NULL)
1263 		goto err;
1264 
1265 	if (protocol & F_LEGACY) {
1266 		tls_protocols = TLS_PROTOCOLS_ALL;
1267 		tls_ciphers = "all";
1268 	}
1269 	if (tls_config_set_protocols(s->config, tls_protocols) != 0) {
1270 		log_warn("load_certfile: failed to set tls protocols: %s",
1271 		    tls_config_error(s->config));
1272 		goto err;
1273 	}
1274 	if (tls_config_set_ciphers(s->config, tls_ciphers)) {
1275 		log_warn("load_certfile: failed to set tls ciphers: %s",
1276 		    tls_config_error(s->config));
1277 		goto err;
1278 	}
1279 
1280 	if (name[0] == '/') {
1281 		if (!bsnprintf(certfile, sizeof(certfile), "%s.crt", name)) {
1282 			log_warn("load_certfile: path truncated");
1283 			goto err;
1284 		}
1285 	} else {
1286 		if (!bsnprintf(certfile, sizeof(certfile),
1287 		    "/etc/ldap/certs/%s.crt", name)) {
1288 			log_warn("load_certfile: path truncated");
1289 			goto err;
1290 		}
1291 	}
1292 
1293 	log_debug("loading certificate file %s", certfile);
1294 	s->ssl_cert = tls_load_file(certfile, &s->ssl_cert_len, NULL);
1295 	if (s->ssl_cert == NULL)
1296 		goto err;
1297 
1298 	if (tls_config_set_cert_mem(s->config, s->ssl_cert, s->ssl_cert_len)) {
1299 		log_warn("load_certfile: failed to set tls certificate: %s",
1300 		    tls_config_error(s->config));
1301 		goto err;
1302 	}
1303 
1304 	if (name[0] == '/') {
1305 		if (!bsnprintf(certfile, sizeof(certfile), "%s.key", name)) {
1306 			log_warn("load_certfile: path truncated");
1307 			goto err;
1308 		}
1309 	} else {
1310 		if (!bsnprintf(certfile, sizeof(certfile),
1311 		    "/etc/ldap/certs/%s.key", name)) {
1312 			log_warn("load_certfile: path truncated");
1313 			goto err;
1314 		}
1315 	}
1316 
1317 	log_debug("loading key file %s", certfile);
1318 	s->ssl_key = tls_load_file(certfile, &s->ssl_key_len, NULL);
1319 	if (s->ssl_key == NULL)
1320 		goto err;
1321 
1322 	if (tls_config_set_key_mem(s->config, s->ssl_key, s->ssl_key_len)) {
1323 		log_warn("load_certfile: failed to set tls key: %s",
1324 		    tls_config_error(s->config));
1325 		goto err;
1326 	}
1327 
1328 	SPLAY_INSERT(ssltree, env->sc_ssl, s);
1329 
1330 	return (0);
1331 err:
1332 	free(s->ssl_cert);
1333 	free(s->ssl_key);
1334 	tls_config_free(s->config);
1335 	free(s);
1336 	return (-1);
1337 }
1338