GCC Code Coverage Report
Directory: ./ Exec Total Coverage
File: usr.sbin/ypldap/parse.y Lines: 0 263 0.0 %
Date: 2017-11-07 Branches: 0 205 0.0 %

Line Branch Exec Source
1
/*	$OpenBSD: parse.y,v 1.23 2017/08/28 06:00:05 florian Exp $	*/
2
3
/*
4
 * Copyright (c) 2008 Pierre-Yves Ritschard <pyr@openbsd.org>
5
 * Copyright (c) 2007, 2008 Reyk Floeter <reyk@openbsd.org>
6
 * Copyright (c) 2004, 2005 Esben Norby <norby@openbsd.org>
7
 * Copyright (c) 2004 Ryan McBride <mcbride@openbsd.org>
8
 * Copyright (c) 2002, 2003, 2004 Henning Brauer <henning@openbsd.org>
9
 * Copyright (c) 2001 Markus Friedl.  All rights reserved.
10
 * Copyright (c) 2001 Daniel Hartmeier.  All rights reserved.
11
 * Copyright (c) 2001 Theo de Raadt.  All rights reserved.
12
 *
13
 * Permission to use, copy, modify, and distribute this software for any
14
 * purpose with or without fee is hereby granted, provided that the above
15
 * copyright notice and this permission notice appear in all copies.
16
 *
17
 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
18
 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
19
 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
20
 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
21
 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
22
 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
23
 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
24
 */
25
26
%{
27
#include <sys/types.h>
28
#include <sys/time.h>
29
#include <sys/queue.h>
30
#include <sys/tree.h>
31
#include <sys/socket.h>
32
#include <sys/stat.h>
33
34
#include <netinet/in.h>
35
#include <arpa/inet.h>
36
37
#include <ctype.h>
38
#include <err.h>
39
#include <errno.h>
40
#include <event.h>
41
#include <fcntl.h>
42
#include <limits.h>
43
#include <netdb.h>
44
#include <pwd.h>
45
#include <stdarg.h>
46
#include <stdio.h>
47
#include <stdlib.h>
48
#include <string.h>
49
#include <syslog.h>
50
#include <unistd.h>
51
52
#include "ypldap.h"
53
54
TAILQ_HEAD(files, file)		 files = TAILQ_HEAD_INITIALIZER(files);
55
static struct file {
56
	TAILQ_ENTRY(file)	 entry;
57
	FILE			*stream;
58
	char			*name;
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		 lgetc(int);
73
int		 lungetc(int);
74
int		 findeol(void);
75
76
TAILQ_HEAD(symhead, sym)	 symhead = TAILQ_HEAD_INITIALIZER(symhead);
77
struct sym {
78
	TAILQ_ENTRY(sym)	 entry;
79
	int			 used;
80
	int			 persist;
81
	char			*nam;
82
	char			*val;
83
};
84
int		 symset(const char *, const char *, int);
85
char		*symget(const char *);
86
87
struct env		*conf = NULL;
88
struct idm		*idm = NULL;
89
static int		 errors = 0;
90
91
typedef struct {
92
	union {
93
		int64_t		 number;
94
		char		*string;
95
	} v;
96
	int lineno;
97
} YYSTYPE;
98
99
%}
100
101
%token	SERVER FILTER ATTRIBUTE BASEDN BINDDN GROUPDN BINDCRED MAPS CHANGE DOMAIN PROVIDE
102
%token	USER GROUP TO EXPIRE HOME SHELL GECOS UID GID INTERVAL
103
%token	PASSWD NAME FIXED LIST GROUPNAME GROUPPASSWD GROUPGID MAP
104
%token	INCLUDE DIRECTORY CLASS PORT ERROR GROUPMEMBERS LDAPS TLS CAFILE
105
%token	<v.string>	STRING
106
%token  <v.number>	NUMBER
107
%type	<v.number>	opcode attribute
108
%type	<v.number>	port
109
%type	<v.number>	ssl
110
111
%%
112
113
grammar		: /* empty */
114
		| grammar '\n'
115
		| grammar include '\n'
116
		| grammar varset '\n'
117
		| grammar directory '\n'
118
		| grammar main '\n'
119
		| grammar error '\n'			{ file->errors++; }
120
		;
121
122
nl		: '\n' optnl
123
		;
124
125
optnl		: '\n' optnl
126
		| /* empty */
127
		;
128
129
130
include		: INCLUDE STRING			{
131
			struct file	*nfile;
132
133
			if ((nfile = pushfile($2, 0)) == NULL) {
134
				yyerror("failed to include file %s", $2);
135
				free($2);
136
				YYERROR;
137
			}
138
			free($2);
139
140
			file = nfile;
141
			lungetc('\n');
142
		}
143
		;
144
145
varset		: STRING '=' STRING			{
146
			char *s = $1;
147
			while (*s++) {
148
				if (isspace((unsigned char)*s)) {
149
					yyerror("macro name cannot contain "
150
					    "whitespace");
151
					YYERROR;
152
				}
153
			}
154
			if (symset($1, $3, 0) == -1)
155
				fatal("cannot store variable");
156
			free($1);
157
			free($3);
158
		}
159
		;
160
161
port		: PORT STRING				{
162
			struct servent *servent;
163
164
			servent = getservbyname($2, "tcp");
165
			if (servent == NULL) {
166
				yyerror("port %s is invalid", $2);
167
				free($2);
168
				YYERROR;
169
			}
170
			$$ = ntohs(servent->s_port);
171
			free($2);
172
		}
173
		| PORT NUMBER				{
174
			if ($2 <= 0 || $2 > (int)USHRT_MAX) {
175
				yyerror("invalid port: %lld", $2);
176
				YYERROR;
177
			}
178
			$$ = $2;
179
		}
180
		| /* empty */				{
181
			$$ = 0;
182
		}
183
		;
184
185
opcode		: GROUP					{ $$ = 0; }
186
		| PASSWD				{ $$ = 1; }
187
		;
188
189
190
attribute	: NAME					{ $$ = 0; }
191
		| PASSWD				{ $$ = 1; }
192
		| UID					{ $$ = 2; }
193
		| GID					{ $$ = 3; }
194
		| CLASS					{ $$ = 4; }
195
		| CHANGE				{ $$ = 5; }
196
		| EXPIRE				{ $$ = 6; }
197
		| GECOS					{ $$ = 7; }
198
		| HOME					{ $$ = 8; }
199
		| SHELL					{ $$ = 9; }
200
		| GROUPNAME				{ $$ = 10; }
201
		| GROUPPASSWD				{ $$ = 11; }
202
		| GROUPGID				{ $$ = 12; }
203
		| GROUPMEMBERS				{ $$ = 13; }
204
		;
205
206
diropt		: BINDDN STRING				{
207
			idm->idm_flags |= F_NEEDAUTH;
208
			if (strlcpy(idm->idm_binddn, $2,
209
			    sizeof(idm->idm_binddn)) >=
210
			    sizeof(idm->idm_binddn)) {
211
				yyerror("directory binddn truncated");
212
				free($2);
213
				YYERROR;
214
			}
215
			free($2);
216
		}
217
		| BINDCRED STRING			{
218
			idm->idm_flags |= F_NEEDAUTH;
219
			if (strlcpy(idm->idm_bindcred, $2,
220
			    sizeof(idm->idm_bindcred)) >=
221
			    sizeof(idm->idm_bindcred)) {
222
				yyerror("directory bindcred truncated");
223
				free($2);
224
				YYERROR;
225
			}
226
			free($2);
227
		}
228
		| BASEDN STRING			{
229
			if (strlcpy(idm->idm_basedn, $2,
230
			    sizeof(idm->idm_basedn)) >=
231
			    sizeof(idm->idm_basedn)) {
232
				yyerror("directory basedn truncated");
233
				free($2);
234
				YYERROR;
235
			}
236
			free($2);
237
		}
238
		| GROUPDN STRING		{
239
			if(strlcpy(idm->idm_groupdn, $2,
240
			    sizeof(idm->idm_groupdn)) >=
241
			    sizeof(idm->idm_groupdn)) {
242
				yyerror("directory groupdn truncated");
243
				free($2);
244
				YYERROR;
245
			}
246
			free($2);
247
		}
248
		| opcode FILTER STRING			{
249
			if (strlcpy(idm->idm_filters[$1], $3,
250
			    sizeof(idm->idm_filters[$1])) >=
251
			    sizeof(idm->idm_filters[$1])) {
252
				yyerror("filter truncated");
253
				free($3);
254
				YYERROR;
255
			}
256
			free($3);
257
		}
258
		| ATTRIBUTE attribute MAPS TO STRING	{
259
			if (strlcpy(idm->idm_attrs[$2], $5,
260
			    sizeof(idm->idm_attrs[$2])) >=
261
			    sizeof(idm->idm_attrs[$2])) {
262
				yyerror("attribute truncated");
263
				free($5);
264
				YYERROR;
265
			}
266
			free($5);
267
		}
268
		| FIXED ATTRIBUTE attribute STRING	{
269
			if (strlcpy(idm->idm_attrs[$3], $4,
270
			    sizeof(idm->idm_attrs[$3])) >=
271
			    sizeof(idm->idm_attrs[$3])) {
272
				yyerror("attribute truncated");
273
				free($4);
274
				YYERROR;
275
			}
276
			idm->idm_flags |= F_FIXED_ATTR($3);
277
			free($4);
278
		}
279
		| LIST attribute MAPS TO STRING	{
280
			if (strlcpy(idm->idm_attrs[$2], $5,
281
			    sizeof(idm->idm_attrs[$2])) >=
282
			    sizeof(idm->idm_attrs[$2])) {
283
				yyerror("attribute truncated");
284
				free($5);
285
				YYERROR;
286
			}
287
			idm->idm_list |= F_LIST($2);
288
			free($5);
289
		}
290
		;
291
292
ssl		: /* empty */				{ $$ = 0; }
293
		| LDAPS					{ $$ = F_SSL; }
294
		| TLS					{ $$ = F_STARTTLS; }
295
		;
296
297
directory	: DIRECTORY STRING port ssl {
298
			if ((idm = calloc(1, sizeof(*idm))) == NULL)
299
				fatal(NULL);
300
			idm->idm_id = conf->sc_maxid++;
301
302
			if (strlcpy(idm->idm_name, $2,
303
			    sizeof(idm->idm_name)) >=
304
			    sizeof(idm->idm_name)) {
305
				yyerror("attribute truncated");
306
				free($2);
307
				YYERROR;
308
			}
309
			free($2);
310
311
			idm->idm_port = $3;
312
313
			if ($4 != 0) {
314
				if (tls_init()) {
315
					yyerror("tls init failed");
316
					YYERROR;
317
				}
318
319
				idm->idm_flags |= $4;
320
				idm->idm_tls_config = tls_config_new();
321
				if (idm->idm_tls_config == NULL) {
322
					yyerror("tls config failed");
323
					YYERROR;
324
				}
325
326
				if (tls_config_set_protocols(
327
				    idm->idm_tls_config,
328
				    TLS_PROTOCOLS_ALL) == -1) {
329
					yyerror("tls set protocols failed: %s",
330
					    tls_config_error(
331
					    idm->idm_tls_config));
332
					tls_config_free(idm->idm_tls_config);
333
					idm->idm_tls_config = NULL;
334
					YYERROR;
335
				}
336
				if (tls_config_set_ciphers(idm->idm_tls_config,
337
				    "compat") == -1) {
338
					yyerror("tls set ciphers failed: %s",
339
					    tls_config_error(
340
					    idm->idm_tls_config));
341
					tls_config_free(idm->idm_tls_config);
342
					idm->idm_tls_config = NULL;
343
					YYERROR;
344
				}
345
346
				if (tls_config_set_ca_file(idm->idm_tls_config,
347
				    conf->sc_cafile) == -1) {
348
					yyerror("tls set CA bundle failed: %s",
349
					    tls_config_error(
350
					    idm->idm_tls_config));
351
					tls_config_free(idm->idm_tls_config);
352
					idm->idm_tls_config = NULL;
353
					YYERROR;
354
				}
355
			}
356
357
		} '{' optnl diropts '}'			{
358
			TAILQ_INSERT_TAIL(&conf->sc_idms, idm, idm_entry);
359
			idm = NULL;
360
		}
361
		;
362
363
main		: INTERVAL NUMBER			{
364
			conf->sc_conf_tv.tv_sec = $2;
365
			conf->sc_conf_tv.tv_usec = 0;
366
		}
367
		| DOMAIN STRING				{
368
			if (strlcpy(conf->sc_domainname, $2,
369
			    sizeof(conf->sc_domainname)) >=
370
			    sizeof(conf->sc_domainname)) {
371
				yyerror("domainname truncated");
372
				free($2);
373
				YYERROR;
374
			}
375
			free($2);
376
		}
377
		| PROVIDE MAP STRING			{
378
			if (strcmp($3, "passwd.byname") == 0)
379
				conf->sc_flags |= YPMAP_PASSWD_BYNAME;
380
			else if (strcmp($3, "passwd.byuid") == 0)
381
				conf->sc_flags |= YPMAP_PASSWD_BYUID;
382
			else if (strcmp($3, "master.passwd.byname") == 0)
383
				conf->sc_flags |= YPMAP_MASTER_PASSWD_BYNAME;
384
			else if (strcmp($3, "master.passwd.byuid") == 0)
385
				conf->sc_flags |= YPMAP_MASTER_PASSWD_BYUID;
386
			else if (strcmp($3, "group.byname") == 0)
387
				conf->sc_flags |= YPMAP_GROUP_BYNAME;
388
			else if (strcmp($3, "group.bygid") == 0)
389
				conf->sc_flags |= YPMAP_GROUP_BYGID;
390
			else if (strcmp($3, "netid.byname") == 0)
391
				conf->sc_flags |= YPMAP_NETID_BYNAME;
392
			else {
393
				yyerror("unsupported map type: %s", $3);
394
				free($3);
395
				YYERROR;
396
			}
397
			free($3);
398
		}
399
		| CAFILE STRING				{
400
			free(conf->sc_cafile);
401
			conf->sc_cafile = $2;
402
		}
403
		;
404
405
diropts		: diropts diropt nl
406
		| diropt optnl
407
		;
408
409
%%
410
411
struct keywords {
412
	const char	*k_name;
413
	int		 k_val;
414
};
415
416
int
417
yyerror(const char *fmt, ...)
418
{
419
	va_list		 ap;
420
	char		*msg;
421
422
	file->errors++;
423
	va_start(ap, fmt);
424
	if (vasprintf(&msg, fmt, ap) == -1)
425
		fatalx("yyerror vasprintf");
426
	va_end(ap);
427
	logit(LOG_CRIT, "%s:%d: %s", file->name, yylval.lineno, msg);
428
	free(msg);
429
	return (0);
430
}
431
432
int
433
kw_cmp(const void *k, const void *e)
434
{
435
	return (strcmp(k, ((const struct keywords *)e)->k_name));
436
}
437
438
int
439
lookup(char *s)
440
{
441
	/* this has to be sorted always */
442
	static const struct keywords keywords[] = {
443
		{ "attribute",		ATTRIBUTE },
444
		{ "basedn",		BASEDN },
445
		{ "bindcred",		BINDCRED },
446
		{ "binddn",		BINDDN },
447
		{ "cafile",		CAFILE },
448
		{ "change",		CHANGE },
449
		{ "class",		CLASS },
450
		{ "directory",		DIRECTORY },
451
		{ "domain",		DOMAIN },
452
		{ "expire",		EXPIRE },
453
		{ "filter",		FILTER },
454
		{ "fixed",		FIXED },
455
		{ "gecos",		GECOS },
456
		{ "gid",		GID },
457
		{ "group",		GROUP },
458
		{ "groupdn",		GROUPDN },
459
		{ "groupgid",		GROUPGID },
460
		{ "groupmembers",	GROUPMEMBERS },
461
		{ "groupname",		GROUPNAME },
462
		{ "grouppasswd",	GROUPPASSWD },
463
		{ "home",		HOME },
464
		{ "include",		INCLUDE },
465
		{ "interval",		INTERVAL },
466
		{ "ldaps",		LDAPS },
467
		{ "list",		LIST },
468
		{ "map",		MAP },
469
		{ "maps",		MAPS },
470
		{ "name",		NAME },
471
		{ "passwd",		PASSWD },
472
		{ "port",		PORT },
473
		{ "provide",		PROVIDE },
474
		{ "server",		SERVER },
475
		{ "shell",		SHELL },
476
		{ "tls",		TLS },
477
		{ "to",			TO },
478
		{ "uid",		UID },
479
		{ "user",		USER },
480
	};
481
	const struct keywords	*p;
482
483
	p = bsearch(s, keywords, sizeof(keywords)/sizeof(keywords[0]),
484
	    sizeof(keywords[0]), kw_cmp);
485
486
	if (p)
487
		return (p->k_val);
488
	else
489
		return (STRING);
490
}
491
492
#define MAXPUSHBACK	128
493
494
u_char	*parsebuf;
495
int	 parseindex;
496
u_char	 pushback_buffer[MAXPUSHBACK];
497
int	 pushback_index = 0;
498
499
int
500
lgetc(int quotec)
501
{
502
	int		c, next;
503
504
	if (parsebuf) {
505
		/* Read character from the parsebuffer instead of input. */
506
		if (parseindex >= 0) {
507
			c = parsebuf[parseindex++];
508
			if (c != '\0')
509
				return (c);
510
			parsebuf = NULL;
511
		} else
512
			parseindex++;
513
	}
514
515
	if (pushback_index)
516
		return (pushback_buffer[--pushback_index]);
517
518
	if (quotec) {
519
		if ((c = getc(file->stream)) == EOF) {
520
			yyerror("reached end of file while parsing "
521
			    "quoted string");
522
			if (file == topfile || popfile() == EOF)
523
				return (EOF);
524
			return (quotec);
525
		}
526
		return (c);
527
	}
528
529
	while ((c = getc(file->stream)) == '\\') {
530
		next = getc(file->stream);
531
		if (next != '\n') {
532
			c = next;
533
			break;
534
		}
535
		yylval.lineno = file->lineno;
536
		file->lineno++;
537
	}
538
539
	while (c == EOF) {
540
		if (file == topfile || popfile() == EOF)
541
			return (EOF);
542
		c = getc(file->stream);
543
	}
544
	return (c);
545
}
546
547
int
548
lungetc(int c)
549
{
550
	if (c == EOF)
551
		return (EOF);
552
	if (parsebuf) {
553
		parseindex--;
554
		if (parseindex >= 0)
555
			return (c);
556
	}
557
	if (pushback_index < MAXPUSHBACK-1)
558
		return (pushback_buffer[pushback_index++] = c);
559
	else
560
		return (EOF);
561
}
562
563
int
564
findeol(void)
565
{
566
	int	c;
567
568
	parsebuf = NULL;
569
570
	/* skip to either EOF or the first real EOL */
571
	while (1) {
572
		if (pushback_index)
573
			c = pushback_buffer[--pushback_index];
574
		else
575
			c = lgetc(0);
576
		if (c == '\n') {
577
			file->lineno++;
578
			break;
579
		}
580
		if (c == EOF)
581
			break;
582
	}
583
	return (ERROR);
584
}
585
586
int
587
yylex(void)
588
{
589
	u_char	 buf[8096];
590
	u_char	*p, *val;
591
	int	 quotec, next, c;
592
	int	 token;
593
594
top:
595
	p = buf;
596
	while ((c = lgetc(0)) == ' ' || c == '\t')
597
		; /* nothing */
598
599
	yylval.lineno = file->lineno;
600
	if (c == '#')
601
		while ((c = lgetc(0)) != '\n' && c != EOF)
602
			; /* nothing */
603
	if (c == '$' && parsebuf == NULL) {
604
		while (1) {
605
			if ((c = lgetc(0)) == EOF)
606
				return (0);
607
608
			if (p + 1 >= buf + sizeof(buf) - 1) {
609
				yyerror("string too long");
610
				return (findeol());
611
			}
612
			if (isalnum(c) || c == '_') {
613
				*p++ = c;
614
				continue;
615
			}
616
			*p = '\0';
617
			lungetc(c);
618
			break;
619
		}
620
		val = symget(buf);
621
		if (val == NULL) {
622
			yyerror("macro '%s' not defined", buf);
623
			return (findeol());
624
		}
625
		parsebuf = val;
626
		parseindex = 0;
627
		goto top;
628
	}
629
630
	switch (c) {
631
	case '\'':
632
	case '"':
633
		quotec = c;
634
		while (1) {
635
			if ((c = lgetc(quotec)) == EOF)
636
				return (0);
637
			if (c == '\n') {
638
				file->lineno++;
639
				continue;
640
			} else if (c == '\\') {
641
				if ((next = lgetc(quotec)) == EOF)
642
					return (0);
643
				if (next == quotec || c == ' ' || c == '\t')
644
					c = next;
645
				else if (next == '\n') {
646
					file->lineno++;
647
					continue;
648
				} else
649
					lungetc(next);
650
			} else if (c == quotec) {
651
				*p = '\0';
652
				break;
653
			} else if (c == '\0') {
654
				yyerror("syntax error");
655
				return (findeol());
656
			}
657
			if (p + 1 >= buf + sizeof(buf) - 1) {
658
				yyerror("string too long");
659
				return (findeol());
660
			}
661
			*p++ = c;
662
		}
663
		yylval.v.string = strdup(buf);
664
		if (yylval.v.string == NULL)
665
			err(1, "yylex: strdup");
666
		return (STRING);
667
	}
668
669
#define allowed_to_end_number(x) \
670
	(isspace(x) || x == ')' || x ==',' || x == '/' || x == '}' || x == '=')
671
672
	if (c == '-' || isdigit(c)) {
673
		do {
674
			*p++ = c;
675
			if ((unsigned)(p-buf) >= sizeof(buf)) {
676
				yyerror("string too long");
677
				return (findeol());
678
			}
679
		} while ((c = lgetc(0)) != EOF && isdigit(c));
680
		lungetc(c);
681
		if (p == buf + 1 && buf[0] == '-')
682
			goto nodigits;
683
		if (c == EOF || allowed_to_end_number(c)) {
684
			const char *errstr = NULL;
685
686
			*p = '\0';
687
			yylval.v.number = strtonum(buf, LLONG_MIN,
688
			    LLONG_MAX, &errstr);
689
			if (errstr) {
690
				yyerror("\"%s\" invalid number: %s",
691
				    buf, errstr);
692
				return (findeol());
693
			}
694
			return (NUMBER);
695
		} else {
696
nodigits:
697
			while (p > buf + 1)
698
				lungetc(*--p);
699
			c = *--p;
700
			if (c == '-')
701
				return (c);
702
		}
703
	}
704
705
#define allowed_in_string(x) \
706
	(isalnum(x) || (ispunct(x) && x != '(' && x != ')' && \
707
	x != '{' && x != '}' && x != '<' && x != '>' && \
708
	x != '!' && x != '=' && x != '#' && \
709
	x != ','))
710
711
	if (isalnum(c) || c == ':' || c == '_') {
712
		do {
713
			*p++ = c;
714
			if ((unsigned)(p-buf) >= sizeof(buf)) {
715
				yyerror("string too long");
716
				return (findeol());
717
			}
718
		} while ((c = lgetc(0)) != EOF && (allowed_in_string(c)));
719
		lungetc(c);
720
		*p = '\0';
721
		if ((token = lookup(buf)) == STRING)
722
			if ((yylval.v.string = strdup(buf)) == NULL)
723
				err(1, "yylex: strdup");
724
		return (token);
725
	}
726
	if (c == '\n') {
727
		yylval.lineno = file->lineno;
728
		file->lineno++;
729
	}
730
	if (c == EOF)
731
		return (0);
732
	return (c);
733
}
734
735
int
736
check_file_secrecy(int fd, const char *fname)
737
{
738
	struct stat	st;
739
740
	if (fstat(fd, &st)) {
741
		log_warn("cannot stat %s", fname);
742
		return (-1);
743
	}
744
	if (st.st_uid != 0 && st.st_uid != getuid()) {
745
		log_warnx("%s: owner not root or current user", fname);
746
		return (-1);
747
	}
748
	if (st.st_mode & (S_IWGRP | S_IXGRP | S_IRWXO)) {
749
		log_warnx("%s: group writable or world read/writable", fname);
750
		return (-1);
751
	}
752
	return (0);
753
}
754
755
struct file *
756
pushfile(const char *name, int secret)
757
{
758
	struct file	*nfile;
759
760
	if ((nfile = calloc(1, sizeof(struct file))) == NULL) {
761
		log_warn("malloc");
762
		return (NULL);
763
	}
764
	if ((nfile->name = strdup(name)) == NULL) {
765
		log_warn("malloc");
766
		free(nfile);
767
		return (NULL);
768
	}
769
	if ((nfile->stream = fopen(nfile->name, "r")) == NULL) {
770
		log_warn("%s", nfile->name);
771
		free(nfile->name);
772
		free(nfile);
773
		return (NULL);
774
	} else if (secret &&
775
	    check_file_secrecy(fileno(nfile->stream), nfile->name)) {
776
		fclose(nfile->stream);
777
		free(nfile->name);
778
		free(nfile);
779
		return (NULL);
780
	}
781
	nfile->lineno = 1;
782
	TAILQ_INSERT_TAIL(&files, nfile, entry);
783
	return (nfile);
784
}
785
786
int
787
popfile(void)
788
{
789
	struct file	*prev;
790
791
	if ((prev = TAILQ_PREV(file, files, entry)) != NULL)
792
		prev->errors += file->errors;
793
794
	TAILQ_REMOVE(&files, file, entry);
795
	fclose(file->stream);
796
	free(file->name);
797
	free(file);
798
	file = prev;
799
	return (file ? 0 : EOF);
800
}
801
802
int
803
parse_config(struct env *x_conf, const char *filename, int opts)
804
{
805
	struct sym	*sym, *next;
806
807
	conf = x_conf;
808
	bzero(conf, sizeof(*conf));
809
810
	TAILQ_INIT(&conf->sc_idms);
811
	conf->sc_conf_tv.tv_sec = DEFAULT_INTERVAL;
812
	conf->sc_conf_tv.tv_usec = 0;
813
	conf->sc_cafile = strdup(YPLDAP_CERT_FILE);
814
	if (conf->sc_cafile == NULL) {
815
		log_warn("malloc");
816
		return (-1);
817
	}
818
819
	errors = 0;
820
821
	if ((file = pushfile(filename, 1)) == NULL) {
822
		return (-1);
823
	}
824
	topfile = file;
825
826
	/*
827
	 * parse configuration
828
	 */
829
	setservent(1);
830
	yyparse();
831
	endservent();
832
	errors = file->errors;
833
	popfile();
834
835
	/* Free macros and check which have not been used. */
836
	TAILQ_FOREACH_SAFE(sym, &symhead, entry, next) {
837
		if ((opts & YPLDAP_OPT_VERBOSE) && !sym->used)
838
			fprintf(stderr, "warning: macro '%s' not "
839
			    "used\n", sym->nam);
840
		if (!sym->persist) {
841
			free(sym->nam);
842
			free(sym->val);
843
			TAILQ_REMOVE(&symhead, sym, entry);
844
			free(sym);
845
		}
846
	}
847
848
	if (errors) {
849
		return (-1);
850
	}
851
852
	return (0);
853
}
854
855
int
856
symset(const char *nam, const char *val, int persist)
857
{
858
	struct sym	*sym;
859
860
	TAILQ_FOREACH(sym, &symhead, entry) {
861
		if (strcmp(nam, sym->nam) == 0)
862
			break;
863
	}
864
865
	if (sym != NULL) {
866
		if (sym->persist == 1)
867
			return (0);
868
		else {
869
			free(sym->nam);
870
			free(sym->val);
871
			TAILQ_REMOVE(&symhead, sym, entry);
872
			free(sym);
873
		}
874
	}
875
	if ((sym = calloc(1, sizeof(*sym))) == NULL)
876
		return (-1);
877
878
	sym->nam = strdup(nam);
879
	if (sym->nam == NULL) {
880
		free(sym);
881
		return (-1);
882
	}
883
	sym->val = strdup(val);
884
	if (sym->val == NULL) {
885
		free(sym->nam);
886
		free(sym);
887
		return (-1);
888
	}
889
	sym->used = 0;
890
	sym->persist = persist;
891
	TAILQ_INSERT_TAIL(&symhead, sym, entry);
892
	return (0);
893
}
894
895
int
896
cmdline_symset(char *s)
897
{
898
	char	*sym, *val;
899
	int	ret;
900
	size_t	len;
901
902
	if ((val = strrchr(s, '=')) == NULL)
903
		return (-1);
904
905
	len = strlen(s) - strlen(val) + 1;
906
	if ((sym = malloc(len)) == NULL)
907
		errx(1, "cmdline_symset: malloc");
908
909
	(void)strlcpy(sym, s, len);
910
911
	ret = symset(sym, val + 1, 1);
912
	free(sym);
913
914
	return (ret);
915
}
916
917
char *
918
symget(const char *nam)
919
{
920
	struct sym	*sym;
921
922
	TAILQ_FOREACH(sym, &symhead, entry) {
923
		if (strcmp(nam, sym->nam) == 0) {
924
			sym->used = 1;
925
			return (sym->val);
926
		}
927
	}
928
	return (NULL);
929
}