1290931Srodrigc/*	$OpenBSD: parse.y,v 1.18 2015/01/16 06:40:22 deraadt Exp $	*/
2290931Srodrigc/*	$FreeBSD: stable/11/usr.sbin/ypldap/parse.y 330965 2018-03-15 02:25:28Z eadler $ */
3290931Srodrigc
4290931Srodrigc/*
5290931Srodrigc * Copyright (c) 2008 Pierre-Yves Ritschard <pyr@openbsd.org>
6290931Srodrigc * Copyright (c) 2007, 2008 Reyk Floeter <reyk@openbsd.org>
7290931Srodrigc * Copyright (c) 2004, 2005 Esben Norby <norby@openbsd.org>
8290931Srodrigc * Copyright (c) 2004 Ryan McBride <mcbride@openbsd.org>
9290931Srodrigc * Copyright (c) 2002, 2003, 2004 Henning Brauer <henning@openbsd.org>
10290931Srodrigc * Copyright (c) 2001 Markus Friedl.  All rights reserved.
11290931Srodrigc * Copyright (c) 2001 Daniel Hartmeier.  All rights reserved.
12290931Srodrigc * Copyright (c) 2001 Theo de Raadt.  All rights reserved.
13290931Srodrigc *
14290931Srodrigc * Permission to use, copy, modify, and distribute this software for any
15290931Srodrigc * purpose with or without fee is hereby granted, provided that the above
16290931Srodrigc * copyright notice and this permission notice appear in all copies.
17290931Srodrigc *
18290931Srodrigc * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
19290931Srodrigc * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
20290931Srodrigc * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
21290931Srodrigc * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
22290931Srodrigc * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
23290931Srodrigc * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
24290931Srodrigc * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
25290931Srodrigc */
26290931Srodrigc
27290931Srodrigc%{
28290931Srodrigc#include <sys/types.h>
29290937Srodrigc#include <sys/param.h>
30290931Srodrigc#include <sys/time.h>
31290931Srodrigc#include <sys/queue.h>
32290931Srodrigc#include <sys/tree.h>
33290931Srodrigc#include <sys/socket.h>
34290931Srodrigc#include <sys/stat.h>
35290931Srodrigc
36290931Srodrigc#include <netinet/in.h>
37290931Srodrigc#include <arpa/inet.h>
38290931Srodrigc
39290931Srodrigc#include <ctype.h>
40290931Srodrigc#include <err.h>
41290931Srodrigc#include <errno.h>
42290931Srodrigc#include <event.h>
43290931Srodrigc#include <fcntl.h>
44290931Srodrigc#include <limits.h>
45290931Srodrigc#include <netdb.h>
46290931Srodrigc#include <pwd.h>
47290931Srodrigc#include <stdarg.h>
48290931Srodrigc#include <stdio.h>
49290931Srodrigc#include <stdlib.h>
50290931Srodrigc#include <string.h>
51290931Srodrigc#include <syslog.h>
52290931Srodrigc#include <unistd.h>
53290931Srodrigc
54290931Srodrigc#include "ypldap.h"
55290931Srodrigc
56290931SrodrigcTAILQ_HEAD(files, file)		 files = TAILQ_HEAD_INITIALIZER(files);
57290931Srodrigcstatic struct file {
58290931Srodrigc	TAILQ_ENTRY(file)	 entry;
59290931Srodrigc	FILE			*stream;
60290931Srodrigc	char			*name;
61290931Srodrigc	int			 lineno;
62290931Srodrigc	int			 errors;
63290931Srodrigc} *file, *topfile;
64290931Srodrigcstruct file	*pushfile(const char *, int);
65290931Srodrigcint		 popfile(void);
66290931Srodrigcint		 check_file_secrecy(int, const char *);
67290931Srodrigcint		 yyparse(void);
68290931Srodrigcint		 yylex(void);
69290931Srodrigcint		 yyerror(const char *, ...)
70290931Srodrigc    __attribute__((__format__ (printf, 1, 2)))
71290931Srodrigc    __attribute__((__nonnull__ (1)));
72290931Srodrigcint		 kw_cmp(const void *, const void *);
73290931Srodrigcint		 lookup(char *);
74290931Srodrigcint		 lgetc(int);
75290931Srodrigcint		 lungetc(int);
76290931Srodrigcint		 findeol(void);
77290931Srodrigc
78290931SrodrigcTAILQ_HEAD(symhead, sym)	 symhead = TAILQ_HEAD_INITIALIZER(symhead);
79290931Srodrigcstruct sym {
80290931Srodrigc	TAILQ_ENTRY(sym)	 entry;
81290931Srodrigc	int			 used;
82290931Srodrigc	int			 persist;
83290931Srodrigc	char			*nam;
84290931Srodrigc	char			*val;
85290931Srodrigc};
86290931Srodrigcint		 symset(const char *, const char *, int);
87290931Srodrigcchar		*symget(const char *);
88290931Srodrigc
89290931Srodrigcstruct env		*conf = NULL;
90290931Srodrigcstruct idm		*idm = NULL;
91290931Srodrigcstatic int		 errors = 0;
92290931Srodrigc
93290931Srodrigctypedef struct {
94290931Srodrigc	union {
95290931Srodrigc		int64_t		 number;
96290931Srodrigc		char		*string;
97290931Srodrigc	} v;
98290931Srodrigc	int lineno;
99290931Srodrigc} YYSTYPE;
100290931Srodrigc
101290931Srodrigc%}
102290931Srodrigc
103290931Srodrigc%token	SERVER FILTER ATTRIBUTE BASEDN BINDDN GROUPDN BINDCRED MAPS CHANGE DOMAIN PROVIDE
104290931Srodrigc%token	USER GROUP TO EXPIRE HOME SHELL GECOS UID GID INTERVAL
105290931Srodrigc%token	PASSWD NAME FIXED LIST GROUPNAME GROUPPASSWD GROUPGID MAP
106290931Srodrigc%token	INCLUDE DIRECTORY CLASS PORT ERROR GROUPMEMBERS
107290931Srodrigc%token	<v.string>	STRING
108290931Srodrigc%token  <v.number>	NUMBER
109290931Srodrigc%type	<v.number>	opcode attribute
110290931Srodrigc%type	<v.string>	port
111290931Srodrigc
112290931Srodrigc%%
113290931Srodrigc
114290931Srodrigcgrammar		: /* empty */
115290931Srodrigc		| grammar '\n'
116290931Srodrigc		| grammar include '\n'
117290931Srodrigc		| grammar varset '\n'
118290931Srodrigc		| grammar directory '\n'
119290931Srodrigc		| grammar main '\n'
120290931Srodrigc		| grammar error '\n'			{ file->errors++; }
121290931Srodrigc		;
122290931Srodrigc
123290931Srodrigcnl		: '\n' optnl
124290931Srodrigc		;
125290931Srodrigc
126290931Srodrigcoptnl		: '\n' optnl
127290931Srodrigc		| /* empty */
128290931Srodrigc		;
129290931Srodrigc
130290931Srodrigc
131290931Srodrigcinclude		: INCLUDE STRING			{
132290931Srodrigc			struct file	*nfile;
133290931Srodrigc
134290931Srodrigc			if ((nfile = pushfile($2, 0)) == NULL) {
135290931Srodrigc				yyerror("failed to include file %s", $2);
136290931Srodrigc				free($2);
137290931Srodrigc				YYERROR;
138290931Srodrigc			}
139290931Srodrigc			free($2);
140290931Srodrigc
141290931Srodrigc			file = nfile;
142290931Srodrigc			lungetc('\n');
143290931Srodrigc		}
144290931Srodrigc		;
145290931Srodrigc
146290931Srodrigcvarset		: STRING '=' STRING			{
147330965Seadler			char *s = $1;
148330965Seadler			while (*s++) {
149330965Seadler				if (isspace((unsigned char) *s)) {
150330965Seadler					yyerror("macro name cannot contain "
151330965Seadler					  "whitespace");
152330965Seadler					YYERROR;
153330965Seadler				}
154330965Seadler			}
155290931Srodrigc			if (symset($1, $3, 0) == -1)
156290931Srodrigc				fatal("cannot store variable");
157290931Srodrigc			free($1);
158290931Srodrigc			free($3);
159290931Srodrigc		}
160290931Srodrigc		;
161290931Srodrigc
162290931Srodrigcport		: /* empty */	{ $$ = NULL; }
163290931Srodrigc		| PORT STRING	{ $$ = $2; }
164290931Srodrigc		;
165290931Srodrigc
166290931Srodrigcopcode		: GROUP					{ $$ = 0; }
167290931Srodrigc		| PASSWD				{ $$ = 1; }
168290931Srodrigc		;
169290931Srodrigc
170290931Srodrigc
171290931Srodrigcattribute	: NAME					{ $$ = 0; }
172290931Srodrigc		| PASSWD				{ $$ = 1; }
173290931Srodrigc		| UID					{ $$ = 2; }
174290931Srodrigc		| GID					{ $$ = 3; }
175290931Srodrigc		| CLASS					{ $$ = 4; }
176290931Srodrigc		| CHANGE				{ $$ = 5; }
177290931Srodrigc		| EXPIRE				{ $$ = 6; }
178290931Srodrigc		| GECOS					{ $$ = 7; }
179290931Srodrigc		| HOME					{ $$ = 8; }
180290931Srodrigc		| SHELL					{ $$ = 9; }
181290931Srodrigc		| GROUPNAME				{ $$ = 10; }
182290931Srodrigc		| GROUPPASSWD				{ $$ = 11; }
183290931Srodrigc		| GROUPGID				{ $$ = 12; }
184290931Srodrigc		| GROUPMEMBERS				{ $$ = 13; }
185290931Srodrigc		;
186290931Srodrigc
187290931Srodrigcdiropt		: BINDDN STRING				{
188290931Srodrigc			idm->idm_flags |= F_NEEDAUTH;
189290931Srodrigc			if (strlcpy(idm->idm_binddn, $2,
190290931Srodrigc			    sizeof(idm->idm_binddn)) >=
191290931Srodrigc			    sizeof(idm->idm_binddn)) {
192290931Srodrigc				yyerror("directory binddn truncated");
193290931Srodrigc				free($2);
194290931Srodrigc				YYERROR;
195290931Srodrigc			}
196290931Srodrigc			free($2);
197290931Srodrigc		}
198290931Srodrigc		| BINDCRED STRING			{
199290931Srodrigc			idm->idm_flags |= F_NEEDAUTH;
200290931Srodrigc			if (strlcpy(idm->idm_bindcred, $2,
201290931Srodrigc			    sizeof(idm->idm_bindcred)) >=
202290931Srodrigc			    sizeof(idm->idm_bindcred)) {
203290931Srodrigc				yyerror("directory bindcred truncated");
204290931Srodrigc				free($2);
205290931Srodrigc				YYERROR;
206290931Srodrigc			}
207290931Srodrigc			free($2);
208290931Srodrigc		}
209290931Srodrigc		| BASEDN STRING			{
210290931Srodrigc			if (strlcpy(idm->idm_basedn, $2,
211290931Srodrigc			    sizeof(idm->idm_basedn)) >=
212290931Srodrigc			    sizeof(idm->idm_basedn)) {
213290931Srodrigc				yyerror("directory basedn truncated");
214290931Srodrigc				free($2);
215290931Srodrigc				YYERROR;
216290931Srodrigc			}
217290931Srodrigc			free($2);
218290931Srodrigc		}
219290931Srodrigc		| GROUPDN STRING		{
220290931Srodrigc			if(strlcpy(idm->idm_groupdn, $2,
221290931Srodrigc			    sizeof(idm->idm_groupdn)) >=
222290931Srodrigc			    sizeof(idm->idm_groupdn)) {
223290931Srodrigc				yyerror("directory groupdn truncated");
224290931Srodrigc				free($2);
225290931Srodrigc				YYERROR;
226290931Srodrigc			}
227290931Srodrigc			free($2);
228290931Srodrigc		}
229290931Srodrigc		| opcode FILTER STRING			{
230290931Srodrigc			if (strlcpy(idm->idm_filters[$1], $3,
231290931Srodrigc			    sizeof(idm->idm_filters[$1])) >=
232290931Srodrigc			    sizeof(idm->idm_filters[$1])) {
233290931Srodrigc				yyerror("filter truncated");
234290931Srodrigc				free($3);
235290931Srodrigc				YYERROR;
236290931Srodrigc			}
237290931Srodrigc			free($3);
238290931Srodrigc		}
239290931Srodrigc		| ATTRIBUTE attribute MAPS TO STRING	{
240290931Srodrigc			if (strlcpy(idm->idm_attrs[$2], $5,
241290931Srodrigc			    sizeof(idm->idm_attrs[$2])) >=
242290931Srodrigc			    sizeof(idm->idm_attrs[$2])) {
243290931Srodrigc				yyerror("attribute truncated");
244290931Srodrigc				free($5);
245290931Srodrigc				YYERROR;
246290931Srodrigc			}
247290931Srodrigc			free($5);
248290931Srodrigc		}
249290931Srodrigc		| FIXED ATTRIBUTE attribute STRING	{
250290931Srodrigc			if (strlcpy(idm->idm_attrs[$3], $4,
251290931Srodrigc			    sizeof(idm->idm_attrs[$3])) >=
252290931Srodrigc			    sizeof(idm->idm_attrs[$3])) {
253290931Srodrigc				yyerror("attribute truncated");
254290931Srodrigc				free($4);
255290931Srodrigc				YYERROR;
256290931Srodrigc			}
257290931Srodrigc			idm->idm_flags |= F_FIXED_ATTR($3);
258290931Srodrigc			free($4);
259290931Srodrigc		}
260290931Srodrigc		| LIST attribute MAPS TO STRING	{
261290931Srodrigc			if (strlcpy(idm->idm_attrs[$2], $5,
262290931Srodrigc			    sizeof(idm->idm_attrs[$2])) >=
263290931Srodrigc			    sizeof(idm->idm_attrs[$2])) {
264290931Srodrigc				yyerror("attribute truncated");
265290931Srodrigc				free($5);
266290931Srodrigc				YYERROR;
267290931Srodrigc			}
268290931Srodrigc			idm->idm_list |= F_LIST($2);
269290931Srodrigc			free($5);
270290931Srodrigc		}
271290931Srodrigc		;
272290931Srodrigc
273290931Srodrigcdirectory	: DIRECTORY STRING port {
274290931Srodrigc			if ((idm = calloc(1, sizeof(*idm))) == NULL)
275290931Srodrigc				fatal(NULL);
276290931Srodrigc			idm->idm_id = conf->sc_maxid++;
277290931Srodrigc
278290931Srodrigc			if (strlcpy(idm->idm_name, $2,
279290931Srodrigc			    sizeof(idm->idm_name)) >=
280290931Srodrigc			    sizeof(idm->idm_name)) {
281290931Srodrigc				yyerror("attribute truncated");
282290931Srodrigc				free($2);
283290931Srodrigc				YYERROR;
284290931Srodrigc			}
285290931Srodrigc
286290931Srodrigc			free($2);
287290931Srodrigc		} '{' optnl diropts '}'			{
288290931Srodrigc			TAILQ_INSERT_TAIL(&conf->sc_idms, idm, idm_entry);
289290931Srodrigc			idm = NULL;
290290931Srodrigc		}
291290931Srodrigc		;
292290931Srodrigc
293290931Srodrigcmain		: INTERVAL NUMBER			{
294290931Srodrigc			conf->sc_conf_tv.tv_sec = $2;
295290931Srodrigc			conf->sc_conf_tv.tv_usec = 0;
296290931Srodrigc		}
297290931Srodrigc		| DOMAIN STRING				{
298290931Srodrigc			if (strlcpy(conf->sc_domainname, $2,
299290931Srodrigc			    sizeof(conf->sc_domainname)) >=
300290931Srodrigc			    sizeof(conf->sc_domainname)) {
301290931Srodrigc				yyerror("domainname truncated");
302290931Srodrigc				free($2);
303290931Srodrigc				YYERROR;
304290931Srodrigc			}
305290931Srodrigc			free($2);
306290931Srodrigc		}
307290931Srodrigc		| PROVIDE MAP STRING			{
308290931Srodrigc			if (strcmp($3, "passwd.byname") == 0)
309290931Srodrigc				conf->sc_flags |= YPMAP_PASSWD_BYNAME;
310290931Srodrigc			else if (strcmp($3, "passwd.byuid") == 0)
311290931Srodrigc				conf->sc_flags |= YPMAP_PASSWD_BYUID;
312290931Srodrigc			else if (strcmp($3, "master.passwd.byname") == 0)
313290931Srodrigc				conf->sc_flags |= YPMAP_MASTER_PASSWD_BYNAME;
314290931Srodrigc			else if (strcmp($3, "master.passwd.byuid") == 0)
315290931Srodrigc				conf->sc_flags |= YPMAP_MASTER_PASSWD_BYUID;
316290931Srodrigc			else if (strcmp($3, "group.byname") == 0)
317290931Srodrigc				conf->sc_flags |= YPMAP_GROUP_BYNAME;
318290931Srodrigc			else if (strcmp($3, "group.bygid") == 0)
319290931Srodrigc				conf->sc_flags |= YPMAP_GROUP_BYGID;
320290931Srodrigc			else if (strcmp($3, "netid.byname") == 0)
321290931Srodrigc				conf->sc_flags |= YPMAP_NETID_BYNAME;
322290931Srodrigc			else {
323290931Srodrigc				yyerror("unsupported map type: %s", $3);
324290931Srodrigc				free($3);
325290931Srodrigc				YYERROR;
326290931Srodrigc			}
327290931Srodrigc			free($3);
328290931Srodrigc		}
329290931Srodrigc		;
330290931Srodrigc
331290931Srodrigcdiropts		: diropts diropt nl
332290931Srodrigc		| diropt optnl
333290931Srodrigc		;
334290931Srodrigc
335290931Srodrigc%%
336290931Srodrigc
337290931Srodrigcstruct keywords {
338290931Srodrigc	const char	*k_name;
339290931Srodrigc	int		 k_val;
340290931Srodrigc};
341290931Srodrigc
342290931Srodrigcint
343290931Srodrigcyyerror(const char *fmt, ...)
344290931Srodrigc{
345290931Srodrigc	va_list		 ap;
346290931Srodrigc	char		*msg;
347290931Srodrigc
348290931Srodrigc	file->errors++;
349290931Srodrigc	va_start(ap, fmt);
350290931Srodrigc	if (vasprintf(&msg, fmt, ap) == -1)
351290931Srodrigc		fatalx("yyerror vasprintf");
352290931Srodrigc	va_end(ap);
353290931Srodrigc	logit(LOG_CRIT, "%s:%d: %s", file->name, yylval.lineno, msg);
354290931Srodrigc	free(msg);
355290931Srodrigc	return (0);
356290931Srodrigc}
357290931Srodrigc
358290931Srodrigcint
359290931Srodrigckw_cmp(const void *k, const void *e)
360290931Srodrigc{
361290931Srodrigc	return (strcmp(k, ((const struct keywords *)e)->k_name));
362290931Srodrigc}
363290931Srodrigc
364290931Srodrigcint
365290931Srodrigclookup(char *s)
366290931Srodrigc{
367290931Srodrigc	/* this has to be sorted always */
368290931Srodrigc	static const struct keywords keywords[] = {
369290931Srodrigc		{ "attribute",		ATTRIBUTE },
370290931Srodrigc		{ "basedn",		BASEDN },
371290931Srodrigc		{ "bindcred",		BINDCRED },
372290931Srodrigc		{ "binddn",		BINDDN },
373290931Srodrigc		{ "change",		CHANGE },
374290931Srodrigc		{ "class",		CLASS },
375290931Srodrigc		{ "directory",		DIRECTORY },
376290931Srodrigc		{ "domain",		DOMAIN },
377290931Srodrigc		{ "expire",		EXPIRE },
378290931Srodrigc		{ "filter",		FILTER },
379290931Srodrigc		{ "fixed",		FIXED },
380290931Srodrigc		{ "gecos",		GECOS },
381290931Srodrigc		{ "gid",		GID },
382290931Srodrigc		{ "group",		GROUP },
383290931Srodrigc		{ "groupdn",		GROUPDN },
384290931Srodrigc		{ "groupgid",		GROUPGID },
385290931Srodrigc		{ "groupmembers",	GROUPMEMBERS },
386290931Srodrigc		{ "groupname",		GROUPNAME },
387290931Srodrigc		{ "grouppasswd",	GROUPPASSWD },
388290931Srodrigc		{ "home",		HOME },
389290931Srodrigc		{ "include",		INCLUDE },
390290931Srodrigc		{ "interval",		INTERVAL },
391290931Srodrigc		{ "list",		LIST },
392290931Srodrigc		{ "map",		MAP },
393290931Srodrigc		{ "maps",		MAPS },
394290931Srodrigc		{ "name",		NAME },
395290931Srodrigc		{ "passwd",		PASSWD },
396290931Srodrigc		{ "port",		PORT },
397290931Srodrigc		{ "provide",		PROVIDE },
398290931Srodrigc		{ "server",		SERVER },
399290931Srodrigc		{ "shell",		SHELL },
400290931Srodrigc		{ "to",			TO },
401290931Srodrigc		{ "uid",		UID },
402290931Srodrigc		{ "user",		USER },
403290931Srodrigc	};
404290931Srodrigc	const struct keywords	*p;
405290931Srodrigc
406290931Srodrigc	p = bsearch(s, keywords, sizeof(keywords)/sizeof(keywords[0]),
407290931Srodrigc	    sizeof(keywords[0]), kw_cmp);
408290931Srodrigc
409290931Srodrigc	if (p)
410290931Srodrigc		return (p->k_val);
411290931Srodrigc	else
412290931Srodrigc		return (STRING);
413290931Srodrigc}
414290931Srodrigc
415290931Srodrigc#define MAXPUSHBACK	128
416290931Srodrigc
417290931Srodrigcu_char	*parsebuf;
418290931Srodrigcint	 parseindex;
419290931Srodrigcu_char	 pushback_buffer[MAXPUSHBACK];
420290931Srodrigcint	 pushback_index = 0;
421290931Srodrigc
422290931Srodrigcint
423290931Srodrigclgetc(int quotec)
424290931Srodrigc{
425290931Srodrigc	int		c, next;
426290931Srodrigc
427290931Srodrigc	if (parsebuf) {
428290931Srodrigc		/* Read character from the parsebuffer instead of input. */
429290931Srodrigc		if (parseindex >= 0) {
430290931Srodrigc			c = parsebuf[parseindex++];
431290931Srodrigc			if (c != '\0')
432290931Srodrigc				return (c);
433290931Srodrigc			parsebuf = NULL;
434290931Srodrigc		} else
435290931Srodrigc			parseindex++;
436290931Srodrigc	}
437290931Srodrigc
438290931Srodrigc	if (pushback_index)
439290931Srodrigc		return (pushback_buffer[--pushback_index]);
440290931Srodrigc
441290931Srodrigc	if (quotec) {
442290931Srodrigc		if ((c = getc(file->stream)) == EOF) {
443290931Srodrigc			yyerror("reached end of file while parsing "
444290931Srodrigc			    "quoted string");
445290931Srodrigc			if (file == topfile || popfile() == EOF)
446290931Srodrigc				return (EOF);
447290931Srodrigc			return (quotec);
448290931Srodrigc		}
449290931Srodrigc		return (c);
450290931Srodrigc	}
451290931Srodrigc
452290931Srodrigc	while ((c = getc(file->stream)) == '\\') {
453290931Srodrigc		next = getc(file->stream);
454290931Srodrigc		if (next != '\n') {
455290931Srodrigc			c = next;
456290931Srodrigc			break;
457290931Srodrigc		}
458290931Srodrigc		yylval.lineno = file->lineno;
459290931Srodrigc		file->lineno++;
460290931Srodrigc	}
461290931Srodrigc
462290931Srodrigc	while (c == EOF) {
463290931Srodrigc		if (file == topfile || popfile() == EOF)
464290931Srodrigc			return (EOF);
465290931Srodrigc		c = getc(file->stream);
466290931Srodrigc	}
467290931Srodrigc	return (c);
468290931Srodrigc}
469290931Srodrigc
470290931Srodrigcint
471290931Srodrigclungetc(int c)
472290931Srodrigc{
473290931Srodrigc	if (c == EOF)
474290931Srodrigc		return (EOF);
475290931Srodrigc	if (parsebuf) {
476290931Srodrigc		parseindex--;
477290931Srodrigc		if (parseindex >= 0)
478290931Srodrigc			return (c);
479290931Srodrigc	}
480290931Srodrigc	if (pushback_index < MAXPUSHBACK-1)
481290931Srodrigc		return (pushback_buffer[pushback_index++] = c);
482290931Srodrigc	else
483290931Srodrigc		return (EOF);
484290931Srodrigc}
485290931Srodrigc
486290931Srodrigcint
487290931Srodrigcfindeol(void)
488290931Srodrigc{
489290931Srodrigc	int	c;
490290931Srodrigc
491290931Srodrigc	parsebuf = NULL;
492290931Srodrigc
493290931Srodrigc	/* skip to either EOF or the first real EOL */
494290931Srodrigc	while (1) {
495290931Srodrigc		if (pushback_index)
496290931Srodrigc			c = pushback_buffer[--pushback_index];
497290931Srodrigc		else
498290931Srodrigc			c = lgetc(0);
499290931Srodrigc		if (c == '\n') {
500290931Srodrigc			file->lineno++;
501290931Srodrigc			break;
502290931Srodrigc		}
503290931Srodrigc		if (c == EOF)
504290931Srodrigc			break;
505290931Srodrigc	}
506290931Srodrigc	return (ERROR);
507290931Srodrigc}
508290931Srodrigc
509290931Srodrigcint
510290931Srodrigcyylex(void)
511290931Srodrigc{
512290931Srodrigc	u_char	 buf[8096];
513290931Srodrigc	u_char	*p, *val;
514290931Srodrigc	int	 quotec, next, c;
515290931Srodrigc	int	 token;
516290931Srodrigc
517290931Srodrigctop:
518290931Srodrigc	p = buf;
519290931Srodrigc	while ((c = lgetc(0)) == ' ' || c == '\t')
520290931Srodrigc		; /* nothing */
521290931Srodrigc
522290931Srodrigc	yylval.lineno = file->lineno;
523290931Srodrigc	if (c == '#')
524290931Srodrigc		while ((c = lgetc(0)) != '\n' && c != EOF)
525290931Srodrigc			; /* nothing */
526290931Srodrigc	if (c == '$' && parsebuf == NULL) {
527290931Srodrigc		while (1) {
528290931Srodrigc			if ((c = lgetc(0)) == EOF)
529290931Srodrigc				return (0);
530290931Srodrigc
531290931Srodrigc			if (p + 1 >= buf + sizeof(buf) - 1) {
532290931Srodrigc				yyerror("string too long");
533290931Srodrigc				return (findeol());
534290931Srodrigc			}
535290931Srodrigc			if (isalnum(c) || c == '_') {
536290931Srodrigc				*p++ = c;
537290931Srodrigc				continue;
538290931Srodrigc			}
539290931Srodrigc			*p = '\0';
540290931Srodrigc			lungetc(c);
541290931Srodrigc			break;
542290931Srodrigc		}
543290931Srodrigc		val = symget(buf);
544290931Srodrigc		if (val == NULL) {
545290931Srodrigc			yyerror("macro '%s' not defined", buf);
546290931Srodrigc			return (findeol());
547290931Srodrigc		}
548290931Srodrigc		parsebuf = val;
549290931Srodrigc		parseindex = 0;
550290931Srodrigc		goto top;
551290931Srodrigc	}
552290931Srodrigc
553290931Srodrigc	switch (c) {
554290931Srodrigc	case '\'':
555290931Srodrigc	case '"':
556290931Srodrigc		quotec = c;
557290931Srodrigc		while (1) {
558290931Srodrigc			if ((c = lgetc(quotec)) == EOF)
559290931Srodrigc				return (0);
560290931Srodrigc			if (c == '\n') {
561290931Srodrigc				file->lineno++;
562290931Srodrigc				continue;
563290931Srodrigc			} else if (c == '\\') {
564290931Srodrigc				if ((next = lgetc(quotec)) == EOF)
565290931Srodrigc					return (0);
566290931Srodrigc				if (next == quotec || c == ' ' || c == '\t')
567290931Srodrigc					c = next;
568290931Srodrigc				else if (next == '\n') {
569290931Srodrigc					file->lineno++;
570290931Srodrigc					continue;
571290931Srodrigc				} else
572290931Srodrigc					lungetc(next);
573290931Srodrigc			} else if (c == quotec) {
574290931Srodrigc				*p = '\0';
575290931Srodrigc				break;
576290931Srodrigc			} else if (c == '\0') {
577290931Srodrigc				yyerror("syntax error");
578290931Srodrigc				return (findeol());
579290931Srodrigc			}
580290931Srodrigc			if (p + 1 >= buf + sizeof(buf) - 1) {
581290931Srodrigc				yyerror("string too long");
582290931Srodrigc				return (findeol());
583290931Srodrigc			}
584290931Srodrigc			*p++ = c;
585290931Srodrigc		}
586290931Srodrigc		yylval.v.string = strdup(buf);
587290931Srodrigc		if (yylval.v.string == NULL)
588290931Srodrigc			err(1, "yylex: strdup");
589290931Srodrigc		return (STRING);
590290931Srodrigc	}
591290931Srodrigc
592290931Srodrigc#define allowed_to_end_number(x) \
593290931Srodrigc	(isspace(x) || x == ')' || x ==',' || x == '/' || x == '}' || x == '=')
594290931Srodrigc
595290931Srodrigc	if (c == '-' || isdigit(c)) {
596290931Srodrigc		do {
597290931Srodrigc			*p++ = c;
598290931Srodrigc			if ((unsigned)(p-buf) >= sizeof(buf)) {
599290931Srodrigc				yyerror("string too long");
600290931Srodrigc				return (findeol());
601290931Srodrigc			}
602290931Srodrigc		} while ((c = lgetc(0)) != EOF && isdigit(c));
603290931Srodrigc		lungetc(c);
604290931Srodrigc		if (p == buf + 1 && buf[0] == '-')
605290931Srodrigc			goto nodigits;
606290931Srodrigc		if (c == EOF || allowed_to_end_number(c)) {
607290931Srodrigc			const char *errstr = NULL;
608290931Srodrigc
609290931Srodrigc			*p = '\0';
610290931Srodrigc			yylval.v.number = strtonum(buf, LLONG_MIN,
611290931Srodrigc			    LLONG_MAX, &errstr);
612290931Srodrigc			if (errstr) {
613290931Srodrigc				yyerror("\"%s\" invalid number: %s",
614290931Srodrigc				    buf, errstr);
615290931Srodrigc				return (findeol());
616290931Srodrigc			}
617290931Srodrigc			return (NUMBER);
618290931Srodrigc		} else {
619290931Srodrigcnodigits:
620290931Srodrigc			while (p > buf + 1)
621290931Srodrigc				lungetc(*--p);
622290931Srodrigc			c = *--p;
623290931Srodrigc			if (c == '-')
624290931Srodrigc				return (c);
625290931Srodrigc		}
626290931Srodrigc	}
627290931Srodrigc
628290931Srodrigc#define allowed_in_string(x) \
629290931Srodrigc	(isalnum(x) || (ispunct(x) && x != '(' && x != ')' && \
630290931Srodrigc	x != '{' && x != '}' && x != '<' && x != '>' && \
631290931Srodrigc	x != '!' && x != '=' && x != '#' && \
632290931Srodrigc	x != ','))
633290931Srodrigc
634290931Srodrigc	if (isalnum(c) || c == ':' || c == '_') {
635290931Srodrigc		do {
636290931Srodrigc			*p++ = c;
637290931Srodrigc			if ((unsigned)(p-buf) >= sizeof(buf)) {
638290931Srodrigc				yyerror("string too long");
639290931Srodrigc				return (findeol());
640290931Srodrigc			}
641290931Srodrigc		} while ((c = lgetc(0)) != EOF && (allowed_in_string(c)));
642290931Srodrigc		lungetc(c);
643290931Srodrigc		*p = '\0';
644290931Srodrigc		if ((token = lookup(buf)) == STRING)
645290931Srodrigc			if ((yylval.v.string = strdup(buf)) == NULL)
646290931Srodrigc				err(1, "yylex: strdup");
647290931Srodrigc		return (token);
648290931Srodrigc	}
649290931Srodrigc	if (c == '\n') {
650290931Srodrigc		yylval.lineno = file->lineno;
651290931Srodrigc		file->lineno++;
652290931Srodrigc	}
653290931Srodrigc	if (c == EOF)
654290931Srodrigc		return (0);
655290931Srodrigc	return (c);
656290931Srodrigc}
657290931Srodrigc
658290931Srodrigcint
659290931Srodrigccheck_file_secrecy(int fd, const char *fname)
660290931Srodrigc{
661290931Srodrigc	struct stat	st;
662290931Srodrigc
663290931Srodrigc	if (fstat(fd, &st)) {
664290931Srodrigc		log_warn("cannot stat %s", fname);
665290931Srodrigc		return (-1);
666290931Srodrigc	}
667290931Srodrigc	if (st.st_uid != 0 && st.st_uid != getuid()) {
668290931Srodrigc		log_warnx("%s: owner not root or current user", fname);
669290931Srodrigc		return (-1);
670290931Srodrigc	}
671290931Srodrigc	if (st.st_mode & (S_IWGRP | S_IXGRP | S_IRWXO)) {
672290931Srodrigc		log_warnx("%s: group writable or world read/writable", fname);
673290931Srodrigc		return (-1);
674290931Srodrigc	}
675290931Srodrigc	return (0);
676290931Srodrigc}
677290931Srodrigc
678290931Srodrigcstruct file *
679290931Srodrigcpushfile(const char *name, int secret)
680290931Srodrigc{
681290931Srodrigc	struct file	*nfile;
682290931Srodrigc
683290931Srodrigc	if ((nfile = calloc(1, sizeof(struct file))) == NULL) {
684290931Srodrigc		log_warn("malloc");
685290931Srodrigc		return (NULL);
686290931Srodrigc	}
687290931Srodrigc	if ((nfile->name = strdup(name)) == NULL) {
688290931Srodrigc		log_warn("malloc");
689290931Srodrigc		free(nfile);
690290931Srodrigc		return (NULL);
691290931Srodrigc	}
692290931Srodrigc	if ((nfile->stream = fopen(nfile->name, "r")) == NULL) {
693290931Srodrigc		log_warn("%s", nfile->name);
694290931Srodrigc		free(nfile->name);
695290931Srodrigc		free(nfile);
696290931Srodrigc		return (NULL);
697290931Srodrigc	} else if (secret &&
698290931Srodrigc	    check_file_secrecy(fileno(nfile->stream), nfile->name)) {
699290931Srodrigc		fclose(nfile->stream);
700290931Srodrigc		free(nfile->name);
701290931Srodrigc		free(nfile);
702290931Srodrigc		return (NULL);
703290931Srodrigc	}
704290931Srodrigc	nfile->lineno = 1;
705290931Srodrigc	TAILQ_INSERT_TAIL(&files, nfile, entry);
706290931Srodrigc	return (nfile);
707290931Srodrigc}
708290931Srodrigc
709290931Srodrigcint
710290931Srodrigcpopfile(void)
711290931Srodrigc{
712290931Srodrigc	struct file	*prev;
713290931Srodrigc
714290931Srodrigc	if ((prev = TAILQ_PREV(file, files, entry)) != NULL)
715290931Srodrigc		prev->errors += file->errors;
716290931Srodrigc
717290931Srodrigc	TAILQ_REMOVE(&files, file, entry);
718290931Srodrigc	fclose(file->stream);
719290931Srodrigc	free(file->name);
720290931Srodrigc	free(file);
721290931Srodrigc	file = prev;
722290931Srodrigc	return (file ? 0 : EOF);
723290931Srodrigc}
724290931Srodrigc
725290931Srodrigcint
726290931Srodrigcparse_config(struct env *x_conf, const char *filename, int opts)
727290931Srodrigc{
728290931Srodrigc	struct sym	*sym, *next;
729290931Srodrigc
730290931Srodrigc	conf = x_conf;
731290931Srodrigc	bzero(conf, sizeof(*conf));
732290931Srodrigc
733290931Srodrigc	TAILQ_INIT(&conf->sc_idms);
734290931Srodrigc	conf->sc_conf_tv.tv_sec = DEFAULT_INTERVAL;
735290931Srodrigc	conf->sc_conf_tv.tv_usec = 0;
736290931Srodrigc
737290931Srodrigc	errors = 0;
738290931Srodrigc
739290931Srodrigc	if ((file = pushfile(filename, 1)) == NULL) {
740290931Srodrigc		return (-1);
741290931Srodrigc	}
742290931Srodrigc	topfile = file;
743290931Srodrigc
744290931Srodrigc	/*
745290931Srodrigc	 * parse configuration
746290931Srodrigc	 */
747290931Srodrigc	setservent(1);
748290931Srodrigc	yyparse();
749290931Srodrigc	endservent();
750290931Srodrigc	errors = file->errors;
751290931Srodrigc	popfile();
752290931Srodrigc
753290931Srodrigc	/* Free macros and check which have not been used. */
754290931Srodrigc	for (sym = TAILQ_FIRST(&symhead); sym != NULL; sym = next) {
755290931Srodrigc		next = TAILQ_NEXT(sym, entry);
756290931Srodrigc		if ((opts & YPLDAP_OPT_VERBOSE) && !sym->used)
757290931Srodrigc			fprintf(stderr, "warning: macro '%s' not "
758290931Srodrigc			    "used\n", sym->nam);
759290931Srodrigc		if (!sym->persist) {
760290931Srodrigc			free(sym->nam);
761290931Srodrigc			free(sym->val);
762290931Srodrigc			TAILQ_REMOVE(&symhead, sym, entry);
763290931Srodrigc			free(sym);
764290931Srodrigc		}
765290931Srodrigc	}
766290931Srodrigc
767290931Srodrigc	if (errors) {
768290931Srodrigc		return (-1);
769290931Srodrigc	}
770290931Srodrigc
771290931Srodrigc	return (0);
772290931Srodrigc}
773290931Srodrigc
774290931Srodrigcint
775290931Srodrigcsymset(const char *nam, const char *val, int persist)
776290931Srodrigc{
777290931Srodrigc	struct sym	*sym;
778290931Srodrigc
779290931Srodrigc	for (sym = TAILQ_FIRST(&symhead); sym && strcmp(nam, sym->nam);
780290931Srodrigc	    sym = TAILQ_NEXT(sym, entry))
781290931Srodrigc		;	/* nothing */
782290931Srodrigc
783290931Srodrigc	if (sym != NULL) {
784290931Srodrigc		if (sym->persist == 1)
785290931Srodrigc			return (0);
786290931Srodrigc		else {
787290931Srodrigc			free(sym->nam);
788290931Srodrigc			free(sym->val);
789290931Srodrigc			TAILQ_REMOVE(&symhead, sym, entry);
790290931Srodrigc			free(sym);
791290931Srodrigc		}
792290931Srodrigc	}
793290931Srodrigc	if ((sym = calloc(1, sizeof(*sym))) == NULL)
794290931Srodrigc		return (-1);
795290931Srodrigc
796290931Srodrigc	sym->nam = strdup(nam);
797290931Srodrigc	if (sym->nam == NULL) {
798290931Srodrigc		free(sym);
799290931Srodrigc		return (-1);
800290931Srodrigc	}
801290931Srodrigc	sym->val = strdup(val);
802290931Srodrigc	if (sym->val == NULL) {
803290931Srodrigc		free(sym->nam);
804290931Srodrigc		free(sym);
805290931Srodrigc		return (-1);
806290931Srodrigc	}
807290931Srodrigc	sym->used = 0;
808290931Srodrigc	sym->persist = persist;
809290931Srodrigc	TAILQ_INSERT_TAIL(&symhead, sym, entry);
810290931Srodrigc	return (0);
811290931Srodrigc}
812290931Srodrigc
813290931Srodrigcint
814290931Srodrigccmdline_symset(char *s)
815290931Srodrigc{
816290931Srodrigc	char	*sym, *val;
817290931Srodrigc	int	ret;
818290931Srodrigc	size_t	len;
819290931Srodrigc
820290931Srodrigc	if ((val = strrchr(s, '=')) == NULL)
821290931Srodrigc		return (-1);
822290931Srodrigc
823290931Srodrigc	len = strlen(s) - strlen(val) + 1;
824290931Srodrigc	if ((sym = malloc(len)) == NULL)
825290931Srodrigc		errx(1, "cmdline_symset: malloc");
826290931Srodrigc
827290931Srodrigc	(void)strlcpy(sym, s, len);
828290931Srodrigc
829290931Srodrigc	ret = symset(sym, val + 1, 1);
830290931Srodrigc	free(sym);
831290931Srodrigc
832290931Srodrigc	return (ret);
833290931Srodrigc}
834290931Srodrigc
835290931Srodrigcchar *
836290931Srodrigcsymget(const char *nam)
837290931Srodrigc{
838290931Srodrigc	struct sym	*sym;
839290931Srodrigc
840290931Srodrigc	TAILQ_FOREACH(sym, &symhead, entry)
841290931Srodrigc		if (strcmp(nam, sym->nam) == 0) {
842290931Srodrigc			sym->used = 1;
843290931Srodrigc			return (sym->val);
844290931Srodrigc		}
845290931Srodrigc	return (NULL);
846290931Srodrigc}
847