xref: /openbsd-src/usr.sbin/snmpd/parse.y (revision f2da64fbbbf1b03f09f390ab01267c93dfd77c4c)
1 /*	$OpenBSD: parse.y,v 1.39 2016/06/21 21:35:25 benno Exp $	*/
2 
3 /*
4  * Copyright (c) 2007, 2008, 2012 Reyk Floeter <reyk@openbsd.org>
5  * Copyright (c) 2004, 2005 Esben Norby <norby@openbsd.org>
6  * Copyright (c) 2004 Ryan McBride <mcbride@openbsd.org>
7  * Copyright (c) 2002, 2003, 2004 Henning Brauer <henning@openbsd.org>
8  * Copyright (c) 2001 Markus Friedl.  All rights reserved.
9  * Copyright (c) 2001 Daniel Hartmeier.  All rights reserved.
10  * Copyright (c) 2001 Theo de Raadt.  All rights reserved.
11  *
12  * Permission to use, copy, modify, and distribute this software for any
13  * purpose with or without fee is hereby granted, provided that the above
14  * copyright notice and this permission notice appear in all copies.
15  *
16  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
17  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
18  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
19  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
20  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
21  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
22  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
23  */
24 
25 %{
26 #include <sys/types.h>
27 #include <sys/socket.h>
28 #include <sys/stat.h>
29 #include <sys/queue.h>
30 #include <sys/tree.h>
31 
32 #include <netinet/in.h>
33 #include <net/if.h>
34 
35 #include <arpa/inet.h>
36 #include <arpa/nameser.h>
37 
38 #include <ctype.h>
39 #include <unistd.h>
40 #include <err.h>
41 #include <errno.h>
42 #include <event.h>
43 #include <limits.h>
44 #include <stdint.h>
45 #include <stdarg.h>
46 #include <stdio.h>
47 #include <netdb.h>
48 #include <string.h>
49 #include <syslog.h>
50 
51 #include "snmpd.h"
52 #include "mib.h"
53 
54 enum socktype {
55 	SOCK_TYPE_RESTRICTED = 1,
56 	SOCK_TYPE_AGENTX = 2
57 };
58 
59 TAILQ_HEAD(files, file)		 files = TAILQ_HEAD_INITIALIZER(files);
60 static struct file {
61 	TAILQ_ENTRY(file)	 entry;
62 	FILE			*stream;
63 	char			*name;
64 	int			 lineno;
65 	int			 errors;
66 } *file, *topfile;
67 struct file	*pushfile(const char *, int);
68 int		 popfile(void);
69 int		 check_file_secrecy(int, const char *);
70 int		 yyparse(void);
71 int		 yylex(void);
72 int		 yyerror(const char *, ...)
73     __attribute__((__format__ (printf, 1, 2)))
74     __attribute__((__nonnull__ (1)));
75 int		 kw_cmp(const void *, const void *);
76 int		 lookup(char *);
77 int		 lgetc(int);
78 int		 lungetc(int);
79 int		 findeol(void);
80 
81 TAILQ_HEAD(symhead, sym)	 symhead = TAILQ_HEAD_INITIALIZER(symhead);
82 struct sym {
83 	TAILQ_ENTRY(sym)	 entry;
84 	int			 used;
85 	int			 persist;
86 	char			*nam;
87 	char			*val;
88 };
89 int		 symset(const char *, const char *, int);
90 char		*symget(const char *);
91 
92 struct snmpd			*conf = NULL;
93 static int			 errors = 0;
94 static struct addresslist	*hlist;
95 static struct usmuser		*user = NULL;
96 static int			 nctlsocks = 0;
97 
98 struct address	*host_v4(const char *);
99 struct address	*host_v6(const char *);
100 int		 host_dns(const char *, struct addresslist *,
101 		    int, in_port_t, struct ber_oid *, char *);
102 int		 host(const char *, struct addresslist *,
103 		    int, in_port_t, struct ber_oid *, char *);
104 
105 typedef struct {
106 	union {
107 		int64_t		 number;
108 		char		*string;
109 		struct host	*host;
110 		struct timeval	 tv;
111 		struct ber_oid	*oid;
112 		struct {
113 			int		 type;
114 			void		*data;
115 			long long	 value;
116 		}		 data;
117 		enum usmauth	 auth;
118 		enum usmpriv	 enc;
119 	} v;
120 	int lineno;
121 } YYSTYPE;
122 
123 %}
124 
125 %token	INCLUDE
126 %token  LISTEN ON
127 %token	SYSTEM CONTACT DESCR LOCATION NAME OBJECTID SERVICES RTFILTER
128 %token	READONLY READWRITE OCTETSTRING INTEGER COMMUNITY TRAP RECEIVER
129 %token	SECLEVEL NONE AUTH ENC USER AUTHKEY ENCKEY ERROR DISABLED
130 %token	SOCKET RESTRICTED AGENTX HANDLE DEFAULT
131 %token	<v.string>	STRING
132 %token  <v.number>	NUMBER
133 %type	<v.string>	hostcmn
134 %type	<v.number>	optwrite yesno seclevel socktype
135 %type	<v.data>	objtype cmd
136 %type	<v.oid>		oid hostoid trapoid
137 %type	<v.auth>	auth
138 %type	<v.enc>		enc
139 
140 %%
141 
142 grammar		: /* empty */
143 		| grammar include '\n'
144 		| grammar '\n'
145 		| grammar varset '\n'
146 		| grammar main '\n'
147 		| grammar system '\n'
148 		| grammar mib '\n'
149 		| grammar error '\n'		{ file->errors++; }
150 		;
151 
152 include		: INCLUDE STRING		{
153 			struct file	*nfile;
154 
155 			if ((nfile = pushfile($2, 0)) == NULL) {
156 				yyerror("failed to include file %s", $2);
157 				free($2);
158 				YYERROR;
159 			}
160 			free($2);
161 
162 			file = nfile;
163 			lungetc('\n');
164 		}
165 		;
166 
167 varset		: STRING '=' STRING	{
168 			char *s = $1;
169 			while (*s++) {
170 				if (isspace((unsigned char)*s)) {
171 					yyerror("macro name cannot contain "
172 					    "whitespace");
173 					YYERROR;
174 				}
175 			}
176 			if (symset($1, $3, 0) == -1)
177 				fatal("cannot store variable");
178 			free($1);
179 			free($3);
180 		}
181 		;
182 
183 yesno		:  STRING			{
184 			if (!strcmp($1, "yes"))
185 				$$ = 1;
186 			else if (!strcmp($1, "no"))
187 				$$ = 0;
188 			else {
189 				yyerror("syntax error, "
190 				    "either yes or no expected");
191 				free($1);
192 				YYERROR;
193 			}
194 			free($1);
195 		}
196 		;
197 
198 main		: LISTEN ON STRING		{
199 			struct addresslist	 al;
200 			struct address		*h;
201 
202 			TAILQ_INIT(&al);
203 			if (host($3, &al, 1, SNMPD_PORT, NULL, NULL) <= 0) {
204 				yyerror("invalid ip address: %s", $3);
205 				free($3);
206 				YYERROR;
207 			}
208 			free($3);
209 			h = TAILQ_FIRST(&al);
210 			bcopy(&h->ss, &conf->sc_address.ss, sizeof(*h));
211 			conf->sc_address.port = h->port;
212 
213 			while ((h = TAILQ_FIRST(&al)) != NULL) {
214 				TAILQ_REMOVE(&al, h, entry);
215 				free(h);
216 			}
217 		}
218 		| READONLY COMMUNITY STRING	{
219 			if (strlcpy(conf->sc_rdcommunity, $3,
220 			    sizeof(conf->sc_rdcommunity)) >=
221 			    sizeof(conf->sc_rdcommunity)) {
222 				yyerror("r/o community name too long");
223 				free($3);
224 				YYERROR;
225 			}
226 			free($3);
227 		}
228 		| READWRITE COMMUNITY STRING	{
229 			if (strlcpy(conf->sc_rwcommunity, $3,
230 			    sizeof(conf->sc_rwcommunity)) >=
231 			    sizeof(conf->sc_rwcommunity)) {
232 				yyerror("r/w community name too long");
233 				free($3);
234 				YYERROR;
235 			}
236 			free($3);
237 		}
238 		| READWRITE DISABLED {
239 			conf->sc_readonly = 1;
240  		}
241 		| TRAP COMMUNITY STRING		{
242 			if (strlcpy(conf->sc_trcommunity, $3,
243 			    sizeof(conf->sc_trcommunity)) >=
244 			    sizeof(conf->sc_trcommunity)) {
245 				yyerror("trap community name too long");
246 				free($3);
247 				YYERROR;
248 			}
249 			free($3);
250 		}
251 		| TRAP RECEIVER			{
252 			hlist = &conf->sc_trapreceivers;
253 		} host				{
254 			hlist = NULL;
255 		}
256 		| TRAP HANDLE hostcmn trapoid cmd {
257 			struct trapcmd *cmd = $5.data;
258 
259 			cmd->cmd_oid = $4;
260 
261 			if (trapcmd_add(cmd) != 0) {
262 				free($4);
263 				free(cmd);
264 				yyerror("duplicate oid");
265 				YYERROR;
266 			}
267 			conf->sc_traphandler = 1;
268 		}
269 		| RTFILTER yesno		{
270 			if ($2 == 1)
271 				conf->sc_rtfilter = ROUTE_FILTER(RTM_NEWADDR) |
272 				    ROUTE_FILTER(RTM_DELADDR) |
273 				    ROUTE_FILTER(RTM_IFINFO) |
274 				    ROUTE_FILTER(RTM_IFANNOUNCE);
275 			else
276 				conf->sc_rtfilter = 0;
277 		}
278 		| SECLEVEL seclevel {
279 			conf->sc_min_seclevel = $2;
280 		}
281 		| USER STRING			{
282 			const char *errstr;
283 			user = usm_newuser($2, &errstr);
284 			if (user == NULL) {
285 				yyerror(errstr);
286 				free($2);
287 				YYERROR;
288 			}
289 		} userspecs {
290 			const char *errstr;
291 			if (usm_checkuser(user, &errstr) < 0) {
292 				yyerror(errstr);
293 				YYERROR;
294 			}
295 			user = NULL;
296 		}
297 		| SOCKET STRING socktype {
298 			if ($3) {
299 				struct control_sock *rcsock;
300 
301 				rcsock = calloc(1, sizeof(*rcsock));
302 				if (rcsock == NULL) {
303 					yyerror("calloc");
304 					YYERROR;
305 				}
306 				rcsock->cs_name = $2;
307 				if ($3 == SOCK_TYPE_RESTRICTED)
308 					rcsock->cs_restricted = 1;
309 				else if ($3 == SOCK_TYPE_AGENTX)
310 					rcsock->cs_agentx = 1;
311 				TAILQ_INSERT_TAIL(&conf->sc_ps.ps_rcsocks,
312 				    rcsock, cs_entry);
313 			} else {
314 				if (++nctlsocks > 1) {
315 					yyerror("multiple control "
316 					    "sockets specified");
317 					YYERROR;
318 				}
319 				conf->sc_ps.ps_csock.cs_name = $2;
320 			}
321 		}
322 		;
323 
324 system		: SYSTEM sysmib
325 		;
326 
327 sysmib		: CONTACT STRING		{
328 			struct ber_oid	 o = OID(MIB_sysContact);
329 			mps_set(&o, $2, strlen($2));
330 		}
331 		| DESCR STRING			{
332 			struct ber_oid	 o = OID(MIB_sysDescr);
333 			mps_set(&o, $2, strlen($2));
334 		}
335 		| LOCATION STRING		{
336 			struct ber_oid	 o = OID(MIB_sysLocation);
337 			mps_set(&o, $2, strlen($2));
338 		}
339 		| NAME STRING			{
340 			struct ber_oid	 o = OID(MIB_sysName);
341 			mps_set(&o, $2, strlen($2));
342 		}
343 		| OBJECTID oid			{
344 			struct ber_oid	 o = OID(MIB_sysOID);
345 			mps_set(&o, $2, sizeof(struct ber_oid));
346 		}
347 		| SERVICES NUMBER		{
348 			struct ber_oid	 o = OID(MIB_sysServices);
349 			mps_set(&o, NULL, $2);
350 		}
351 		;
352 
353 mib		: OBJECTID oid NAME STRING optwrite objtype	{
354 			struct oid	*oid;
355 			if ((oid = (struct oid *)
356 			    calloc(1, sizeof(*oid))) == NULL) {
357 				yyerror("calloc");
358 				free($2);
359 				free($6.data);
360 				YYERROR;
361 			}
362 
363 			smi_oidlen($2);
364 			bcopy($2, &oid->o_id, sizeof(struct ber_oid));
365 			free($2);
366 			oid->o_name = $4;
367 			oid->o_data = $6.data;
368 			oid->o_val = $6.value;
369 			switch ($6.type) {
370 			case 1:
371 				oid->o_get = mps_getint;
372 				oid->o_set = mps_setint;
373 				break;
374 			case 2:
375 				oid->o_get = mps_getstr;
376 				oid->o_set = mps_setstr;
377 				break;
378 			}
379 			oid->o_flags = OID_RD|OID_DYNAMIC;
380 			if ($5)
381 				oid->o_flags |= OID_WR;
382 
383 			if (smi_insert(oid) == -1) {
384 				yyerror("duplicate oid");
385 				free(oid->o_name);
386 				free(oid->o_data);
387 				YYERROR;
388 			}
389 		}
390 		;
391 
392 objtype		: INTEGER NUMBER			{
393 			$$.type = 1;
394 			$$.data = NULL;
395 			$$.value = $2;
396 		}
397 		| OCTETSTRING STRING			{
398 			$$.type = 2;
399 			$$.data = $2;
400 			$$.value = strlen($2);
401 		}
402 		;
403 
404 optwrite	: READONLY				{ $$ = 0; }
405 		| READWRITE				{ $$ = 1; }
406 		;
407 
408 oid		: STRING				{
409 			struct ber_oid	*sysoid;
410 			if ((sysoid =
411 			    calloc(1, sizeof(*sysoid))) == NULL) {
412 				yyerror("calloc");
413 				free($1);
414 				YYERROR;
415 			}
416 			if (ber_string2oid($1, sysoid) == -1) {
417 				yyerror("invalid OID: %s", $1);
418 				free(sysoid);
419 				free($1);
420 				YYERROR;
421 			}
422 			free($1);
423 			$$ = sysoid;
424 		}
425 		;
426 
427 trapoid		: oid					{ $$ = $1; }
428 		| DEFAULT				{
429 			struct ber_oid	*sysoid;
430 			if ((sysoid =
431 			    calloc(1, sizeof(*sysoid))) == NULL) {
432 				yyerror("calloc");
433 				YYERROR;
434 			}
435 			ber_string2oid("1.3", sysoid);
436 			$$ = sysoid;
437 		}
438 		;
439 
440 hostoid		: /* empty */				{ $$ = NULL; }
441 		| OBJECTID oid				{ $$ = $2; }
442 		;
443 
444 hostcmn		: /* empty */				{ $$ = NULL; }
445 		| COMMUNITY STRING			{ $$ = $2; }
446 		;
447 
448 hostdef		: STRING hostoid hostcmn		{
449 			if (host($1, hlist, 1,
450 			    SNMPD_TRAPPORT, $2, $3) <= 0) {
451 				yyerror("invalid host: %s", $1);
452 				free($1);
453 				YYERROR;
454 			}
455 			free($1);
456 		}
457 		;
458 
459 hostlist	: /* empty */
460 		| hostlist comma hostdef
461 		;
462 
463 host		: hostdef
464 		| '{' hostlist '}'
465 		;
466 
467 comma		: /* empty */
468 		| ','
469 		;
470 
471 seclevel	: NONE		{ $$ = 0; }
472 		| AUTH		{ $$ = SNMP_MSGFLAG_AUTH; }
473 		| ENC		{ $$ = SNMP_MSGFLAG_AUTH | SNMP_MSGFLAG_PRIV; }
474 		;
475 
476 userspecs	: /* empty */
477 		| userspecs userspec
478 		;
479 
480 userspec	: AUTHKEY STRING		{
481 			user->uu_authkey = $2;
482 		}
483 		| AUTH auth			{
484 			user->uu_auth = $2;
485 		}
486 		| ENCKEY STRING			{
487 			user->uu_privkey = $2;
488 		}
489 		| ENC enc			{
490 			user->uu_priv = $2;
491 		}
492 		;
493 
494 auth		: STRING			{
495 			if (strcasecmp($1, "hmac-md5") == 0 ||
496 			    strcasecmp($1, "hmac-md5-96") == 0)
497 				$$ = AUTH_MD5;
498 			else if (strcasecmp($1, "hmac-sha1") == 0 ||
499 			     strcasecmp($1, "hmac-sha1-96") == 0)
500 				$$ = AUTH_SHA1;
501 			else {
502 				yyerror("syntax error, bad auth hmac");
503 				free($1);
504 				YYERROR;
505 			}
506 			free($1);
507 		}
508 		;
509 
510 enc		: STRING			{
511 			if (strcasecmp($1, "des") == 0 ||
512 			    strcasecmp($1, "cbc-des") == 0)
513 				$$ = PRIV_DES;
514 			else if (strcasecmp($1, "aes") == 0 ||
515 			    strcasecmp($1, "cfb128-aes-128") == 0)
516 				$$ = PRIV_AES;
517 			else {
518 				yyerror("syntax error, bad encryption cipher");
519 				free($1);
520 				YYERROR;
521 			}
522 			free($1);
523 
524 		}
525 		;
526 
527 socktype	: RESTRICTED		{ $$ = SOCK_TYPE_RESTRICTED; }
528 		| AGENTX		{ $$ = SOCK_TYPE_AGENTX; }
529 		| /* nothing */		{ $$ = 0; }
530 		;
531 
532 cmd		: STRING		{
533 			struct		 trapcmd *cmd;
534 			size_t		 span, limit;
535 			char		*pos, **args, **args2;
536 			int		 nargs = 32;		/* XXX */
537 
538 			if ((cmd = calloc(1, sizeof(*cmd))) == NULL ||
539 			    (args = calloc(nargs, sizeof(char *))) == NULL) {
540 				free(cmd);
541 				free($1);
542 				YYERROR;
543 			}
544 
545 			pos = $1;
546 			limit = strlen($1);
547 
548 			while ((span = strcspn(pos, " \t")) != 0 &&
549 			    pos < $1 + limit) {
550 				pos[span] = '\0';
551 				args[cmd->cmd_argc] = strdup(pos);
552 				if (args[cmd->cmd_argc] == NULL) {
553 					trapcmd_free(cmd);
554 					free(args);
555 					free($1);
556 					YYERROR;
557 				}
558 				cmd->cmd_argc++;
559 				if (cmd->cmd_argc >= nargs - 1) {
560 					nargs *= 2;
561 					args2 = calloc(nargs, sizeof(char *));
562 					if (args2 == NULL) {
563 						trapcmd_free(cmd);
564 						free(args);
565 						free($1);
566 						YYERROR;
567 					}
568 					args = args2;
569 				}
570 				pos += span + 1;
571 			}
572 			free($1);
573 			cmd->cmd_argv = args;
574 			$$.data = cmd;
575 		}
576 		;
577 
578 %%
579 
580 struct keywords {
581 	const char	*k_name;
582 	int		 k_val;
583 };
584 
585 int
586 yyerror(const char *fmt, ...)
587 {
588 	va_list		 ap;
589 	char		*msg;
590 
591 	file->errors++;
592 	va_start(ap, fmt);
593 	if (vasprintf(&msg, fmt, ap) == -1)
594 		fatalx("yyerror vasprintf");
595 	va_end(ap);
596 	logit(LOG_CRIT, "%s:%d: %s", file->name, yylval.lineno, msg);
597 	free(msg);
598 	return (0);
599 }
600 
601 int
602 kw_cmp(const void *k, const void *e)
603 {
604 	return (strcmp(k, ((const struct keywords *)e)->k_name));
605 }
606 
607 int
608 lookup(char *s)
609 {
610 	/* this has to be sorted always */
611 	static const struct keywords keywords[] = {
612 		{ "agentx",		AGENTX },
613 		{ "auth",		AUTH },
614 		{ "authkey",		AUTHKEY },
615 		{ "community",		COMMUNITY },
616 		{ "contact",		CONTACT },
617 		{ "default",		DEFAULT },
618 		{ "description",	DESCR },
619 		{ "disabled",		DISABLED},
620 		{ "enc",		ENC },
621 		{ "enckey",		ENCKEY },
622 		{ "filter-routes",	RTFILTER },
623 		{ "handle",		HANDLE },
624 		{ "include",		INCLUDE },
625 		{ "integer",		INTEGER },
626 		{ "listen",		LISTEN },
627 		{ "location",		LOCATION },
628 		{ "name",		NAME },
629 		{ "none",		NONE },
630 		{ "oid",		OBJECTID },
631 		{ "on",			ON },
632 		{ "read-only",		READONLY },
633 		{ "read-write",		READWRITE },
634 		{ "receiver",		RECEIVER },
635 		{ "restricted",		RESTRICTED },
636 		{ "seclevel",		SECLEVEL },
637 		{ "services",		SERVICES },
638 		{ "socket",		SOCKET },
639 		{ "string",		OCTETSTRING },
640 		{ "system",		SYSTEM },
641 		{ "trap",		TRAP },
642 		{ "user",		USER }
643 	};
644 	const struct keywords	*p;
645 
646 	p = bsearch(s, keywords, sizeof(keywords)/sizeof(keywords[0]),
647 	    sizeof(keywords[0]), kw_cmp);
648 
649 	if (p)
650 		return (p->k_val);
651 	else
652 		return (STRING);
653 }
654 
655 #define MAXPUSHBACK	128
656 
657 u_char	*parsebuf;
658 int	 parseindex;
659 u_char	 pushback_buffer[MAXPUSHBACK];
660 int	 pushback_index = 0;
661 
662 int
663 lgetc(int quotec)
664 {
665 	int		c, next;
666 
667 	if (parsebuf) {
668 		/* Read character from the parsebuffer instead of input. */
669 		if (parseindex >= 0) {
670 			c = parsebuf[parseindex++];
671 			if (c != '\0')
672 				return (c);
673 			parsebuf = NULL;
674 		} else
675 			parseindex++;
676 	}
677 
678 	if (pushback_index)
679 		return (pushback_buffer[--pushback_index]);
680 
681 	if (quotec) {
682 		if ((c = getc(file->stream)) == EOF) {
683 			yyerror("reached end of file while parsing quoted string");
684 			if (file == topfile || popfile() == EOF)
685 				return (EOF);
686 			return (quotec);
687 		}
688 		return (c);
689 	}
690 
691 	while ((c = getc(file->stream)) == '\\') {
692 		next = getc(file->stream);
693 		if (next != '\n') {
694 			c = next;
695 			break;
696 		}
697 		yylval.lineno = file->lineno;
698 		file->lineno++;
699 	}
700 	if (c == '\t' || c == ' ') {
701 		/* Compress blanks to a single space. */
702 		do {
703 			c = getc(file->stream);
704 		} while (c == '\t' || c == ' ');
705 		ungetc(c, file->stream);
706 		c = ' ';
707 	}
708 
709 	while (c == EOF) {
710 		if (file == topfile || popfile() == EOF)
711 			return (EOF);
712 		c = getc(file->stream);
713 	}
714 	return (c);
715 }
716 
717 int
718 lungetc(int c)
719 {
720 	if (c == EOF)
721 		return (EOF);
722 	if (parsebuf) {
723 		parseindex--;
724 		if (parseindex >= 0)
725 			return (c);
726 	}
727 	if (pushback_index < MAXPUSHBACK-1)
728 		return (pushback_buffer[pushback_index++] = c);
729 	else
730 		return (EOF);
731 }
732 
733 int
734 findeol(void)
735 {
736 	int	c;
737 
738 	parsebuf = NULL;
739 
740 	/* skip to either EOF or the first real EOL */
741 	while (1) {
742 		if (pushback_index)
743 			c = pushback_buffer[--pushback_index];
744 		else
745 			c = lgetc(0);
746 		if (c == '\n') {
747 			file->lineno++;
748 			break;
749 		}
750 		if (c == EOF)
751 			break;
752 	}
753 	return (ERROR);
754 }
755 
756 int
757 yylex(void)
758 {
759 	u_char	 buf[8096];
760 	u_char	*p, *val;
761 	int	 quotec, next, c;
762 	int	 token;
763 
764 top:
765 	p = buf;
766 	while ((c = lgetc(0)) == ' ' || c == '\t')
767 		; /* nothing */
768 
769 	yylval.lineno = file->lineno;
770 	if (c == '#')
771 		while ((c = lgetc(0)) != '\n' && c != EOF)
772 			; /* nothing */
773 	if (c == '$' && parsebuf == NULL) {
774 		while (1) {
775 			if ((c = lgetc(0)) == EOF)
776 				return (0);
777 
778 			if (p + 1 >= buf + sizeof(buf) - 1) {
779 				yyerror("string too long");
780 				return (findeol());
781 			}
782 			if (isalnum(c) || c == '_') {
783 				*p++ = c;
784 				continue;
785 			}
786 			*p = '\0';
787 			lungetc(c);
788 			break;
789 		}
790 		val = symget(buf);
791 		if (val == NULL) {
792 			yyerror("macro '%s' not defined", buf);
793 			return (findeol());
794 		}
795 		parsebuf = val;
796 		parseindex = 0;
797 		goto top;
798 	}
799 
800 	switch (c) {
801 	case '\'':
802 	case '"':
803 		quotec = c;
804 		while (1) {
805 			if ((c = lgetc(quotec)) == EOF)
806 				return (0);
807 			if (c == '\n') {
808 				file->lineno++;
809 				continue;
810 			} else if (c == '\\') {
811 				if ((next = lgetc(quotec)) == EOF)
812 					return (0);
813 				if (next == quotec || c == ' ' || c == '\t')
814 					c = next;
815 				else if (next == '\n') {
816 					file->lineno++;
817 					continue;
818 				} else
819 					lungetc(next);
820 			} else if (c == quotec) {
821 				*p = '\0';
822 				break;
823 			} else if (c == '\0') {
824 				yyerror("syntax error");
825 				return (findeol());
826 			}
827 			if (p + 1 >= buf + sizeof(buf) - 1) {
828 				yyerror("string too long");
829 				return (findeol());
830 			}
831 			*p++ = c;
832 		}
833 		yylval.v.string = strdup(buf);
834 		if (yylval.v.string == NULL)
835 			err(1, "yylex: strdup");
836 		return (STRING);
837 	}
838 
839 #define allowed_to_end_number(x) \
840 	(isspace(x) || x == ')' || x ==',' || x == '/' || x == '}' || x == '=')
841 
842 	if (c == '-' || isdigit(c)) {
843 		do {
844 			*p++ = c;
845 			if ((unsigned)(p-buf) >= sizeof(buf)) {
846 				yyerror("string too long");
847 				return (findeol());
848 			}
849 		} while ((c = lgetc(0)) != EOF && isdigit(c));
850 		lungetc(c);
851 		if (p == buf + 1 && buf[0] == '-')
852 			goto nodigits;
853 		if (c == EOF || allowed_to_end_number(c)) {
854 			const char *errstr = NULL;
855 
856 			*p = '\0';
857 			yylval.v.number = strtonum(buf, LLONG_MIN,
858 			    LLONG_MAX, &errstr);
859 			if (errstr) {
860 				yyerror("\"%s\" invalid number: %s",
861 				    buf, errstr);
862 				return (findeol());
863 			}
864 			return (NUMBER);
865 		} else {
866 nodigits:
867 			while (p > buf + 1)
868 				lungetc(*--p);
869 			c = *--p;
870 			if (c == '-')
871 				return (c);
872 		}
873 	}
874 
875 #define allowed_in_string(x) \
876 	(isalnum(x) || (ispunct(x) && x != '(' && x != ')' && \
877 	x != '{' && x != '}' && \
878 	x != '!' && x != '=' && x != '#' && \
879 	x != ','))
880 
881 	if (isalnum(c) || c == ':' || c == '_') {
882 		do {
883 			*p++ = c;
884 			if ((unsigned)(p-buf) >= sizeof(buf)) {
885 				yyerror("string too long");
886 				return (findeol());
887 			}
888 		} while ((c = lgetc(0)) != EOF && (allowed_in_string(c)));
889 		lungetc(c);
890 		*p = '\0';
891 		if ((token = lookup(buf)) == STRING)
892 			if ((yylval.v.string = strdup(buf)) == NULL)
893 				err(1, "yylex: strdup");
894 		return (token);
895 	}
896 	if (c == '\n') {
897 		yylval.lineno = file->lineno;
898 		file->lineno++;
899 	}
900 	if (c == EOF)
901 		return (0);
902 	return (c);
903 }
904 
905 int
906 check_file_secrecy(int fd, const char *fname)
907 {
908 	struct stat	st;
909 
910 	if (fstat(fd, &st)) {
911 		log_warn("cannot stat %s", fname);
912 		return (-1);
913 	}
914 	if (st.st_uid != 0 && st.st_uid != getuid()) {
915 		log_warnx("%s: owner not root or current user", fname);
916 		return (-1);
917 	}
918 	if (st.st_mode & (S_IWGRP | S_IXGRP | S_IRWXO)) {
919 		log_warnx("%s: group writable or world read/writable", fname);
920 		return (-1);
921 	}
922 	return (0);
923 }
924 
925 struct file *
926 pushfile(const char *name, int secret)
927 {
928 	struct file	*nfile;
929 
930 	if ((nfile = calloc(1, sizeof(struct file))) == NULL) {
931 		log_warn("malloc");
932 		return (NULL);
933 	}
934 	if ((nfile->name = strdup(name)) == NULL) {
935 		log_warn("malloc");
936 		free(nfile);
937 		return (NULL);
938 	}
939 	if ((nfile->stream = fopen(nfile->name, "r")) == NULL) {
940 		log_warn("%s", nfile->name);
941 		free(nfile->name);
942 		free(nfile);
943 		return (NULL);
944 	} else if (secret &&
945 	    check_file_secrecy(fileno(nfile->stream), nfile->name)) {
946 		fclose(nfile->stream);
947 		free(nfile->name);
948 		free(nfile);
949 		return (NULL);
950 	}
951 	nfile->lineno = 1;
952 	TAILQ_INSERT_TAIL(&files, nfile, entry);
953 	return (nfile);
954 }
955 
956 int
957 popfile(void)
958 {
959 	struct file	*prev;
960 
961 	if ((prev = TAILQ_PREV(file, files, entry)) != NULL)
962 		prev->errors += file->errors;
963 
964 	TAILQ_REMOVE(&files, file, entry);
965 	fclose(file->stream);
966 	free(file->name);
967 	free(file);
968 	file = prev;
969 	return (file ? 0 : EOF);
970 }
971 
972 struct snmpd *
973 parse_config(const char *filename, u_int flags)
974 {
975 	struct sym	*sym, *next;
976 
977 	if ((conf = calloc(1, sizeof(*conf))) == NULL) {
978 		log_warn("cannot allocate memory");
979 		return (NULL);
980 	}
981 
982 	conf->sc_flags = flags;
983 	conf->sc_confpath = filename;
984 	conf->sc_address.ss.ss_family = AF_INET;
985 	conf->sc_address.port = SNMPD_PORT;
986 	conf->sc_ps.ps_csock.cs_name = SNMPD_SOCKET;
987 	TAILQ_INIT(&conf->sc_ps.ps_rcsocks);
988 	strlcpy(conf->sc_rdcommunity, "public", SNMPD_MAXCOMMUNITYLEN);
989 	strlcpy(conf->sc_rwcommunity, "private", SNMPD_MAXCOMMUNITYLEN);
990 	strlcpy(conf->sc_trcommunity, "public", SNMPD_MAXCOMMUNITYLEN);
991 	TAILQ_INIT(&conf->sc_trapreceivers);
992 
993 	if ((file = pushfile(filename, 0)) == NULL) {
994 		free(conf);
995 		return (NULL);
996 	}
997 	topfile = file;
998 	setservent(1);
999 
1000 	yyparse();
1001 	errors = file->errors;
1002 	popfile();
1003 
1004 	endservent();
1005 
1006 	/* Free macros and check which have not been used. */
1007 	for (sym = TAILQ_FIRST(&symhead); sym != NULL; sym = next) {
1008 		next = TAILQ_NEXT(sym, entry);
1009 		if ((conf->sc_flags & SNMPD_F_VERBOSE) && !sym->used)
1010 			fprintf(stderr, "warning: macro '%s' not "
1011 			    "used\n", sym->nam);
1012 		if (!sym->persist) {
1013 			free(sym->nam);
1014 			free(sym->val);
1015 			TAILQ_REMOVE(&symhead, sym, entry);
1016 			free(sym);
1017 		}
1018 	}
1019 
1020 	if (errors) {
1021 		free(conf);
1022 		return (NULL);
1023 	}
1024 
1025 	return (conf);
1026 }
1027 
1028 int
1029 symset(const char *nam, const char *val, int persist)
1030 {
1031 	struct sym	*sym;
1032 
1033 	for (sym = TAILQ_FIRST(&symhead); sym && strcmp(nam, sym->nam);
1034 	    sym = TAILQ_NEXT(sym, entry))
1035 		;	/* nothing */
1036 
1037 	if (sym != NULL) {
1038 		if (sym->persist == 1)
1039 			return (0);
1040 		else {
1041 			free(sym->nam);
1042 			free(sym->val);
1043 			TAILQ_REMOVE(&symhead, sym, entry);
1044 			free(sym);
1045 		}
1046 	}
1047 	if ((sym = calloc(1, sizeof(*sym))) == NULL)
1048 		return (-1);
1049 
1050 	sym->nam = strdup(nam);
1051 	if (sym->nam == NULL) {
1052 		free(sym);
1053 		return (-1);
1054 	}
1055 	sym->val = strdup(val);
1056 	if (sym->val == NULL) {
1057 		free(sym->nam);
1058 		free(sym);
1059 		return (-1);
1060 	}
1061 	sym->used = 0;
1062 	sym->persist = persist;
1063 	TAILQ_INSERT_TAIL(&symhead, sym, entry);
1064 	return (0);
1065 }
1066 
1067 int
1068 cmdline_symset(char *s)
1069 {
1070 	char	*sym, *val;
1071 	int	ret;
1072 	size_t	len;
1073 
1074 	if ((val = strrchr(s, '=')) == NULL)
1075 		return (-1);
1076 
1077 	len = strlen(s) - strlen(val) + 1;
1078 	if ((sym = malloc(len)) == NULL)
1079 		errx(1, "cmdline_symset: malloc");
1080 
1081 	(void)strlcpy(sym, s, len);
1082 
1083 	ret = symset(sym, val + 1, 1);
1084 	free(sym);
1085 
1086 	return (ret);
1087 }
1088 
1089 char *
1090 symget(const char *nam)
1091 {
1092 	struct sym	*sym;
1093 
1094 	TAILQ_FOREACH(sym, &symhead, entry)
1095 		if (strcmp(nam, sym->nam) == 0) {
1096 			sym->used = 1;
1097 			return (sym->val);
1098 		}
1099 	return (NULL);
1100 }
1101 
1102 struct address *
1103 host_v4(const char *s)
1104 {
1105 	struct in_addr		 ina;
1106 	struct sockaddr_in	*sain;
1107 	struct address		*h;
1108 
1109 	bzero(&ina, sizeof(ina));
1110 	if (inet_pton(AF_INET, s, &ina) != 1)
1111 		return (NULL);
1112 
1113 	if ((h = calloc(1, sizeof(*h))) == NULL)
1114 		fatal(__func__);
1115 	sain = (struct sockaddr_in *)&h->ss;
1116 	sain->sin_len = sizeof(struct sockaddr_in);
1117 	sain->sin_family = AF_INET;
1118 	sain->sin_addr.s_addr = ina.s_addr;
1119 
1120 	return (h);
1121 }
1122 
1123 struct address *
1124 host_v6(const char *s)
1125 {
1126 	struct addrinfo		 hints, *res;
1127 	struct sockaddr_in6	*sa_in6;
1128 	struct address		*h = NULL;
1129 
1130 	bzero(&hints, sizeof(hints));
1131 	hints.ai_family = AF_INET6;
1132 	hints.ai_socktype = SOCK_DGRAM; /* dummy */
1133 	hints.ai_flags = AI_NUMERICHOST;
1134 	if (getaddrinfo(s, "0", &hints, &res) == 0) {
1135 		if ((h = calloc(1, sizeof(*h))) == NULL)
1136 			fatal(__func__);
1137 		sa_in6 = (struct sockaddr_in6 *)&h->ss;
1138 		sa_in6->sin6_len = sizeof(struct sockaddr_in6);
1139 		sa_in6->sin6_family = AF_INET6;
1140 		memcpy(&sa_in6->sin6_addr,
1141 		    &((struct sockaddr_in6 *)res->ai_addr)->sin6_addr,
1142 		    sizeof(sa_in6->sin6_addr));
1143 		sa_in6->sin6_scope_id =
1144 		    ((struct sockaddr_in6 *)res->ai_addr)->sin6_scope_id;
1145 
1146 		freeaddrinfo(res);
1147 	}
1148 
1149 	return (h);
1150 }
1151 
1152 int
1153 host_dns(const char *s, struct addresslist *al, int max,
1154 	 in_port_t port, struct ber_oid *oid, char *cmn)
1155 {
1156 	struct addrinfo		 hints, *res0, *res;
1157 	int			 error, cnt = 0;
1158 	struct sockaddr_in	*sain;
1159 	struct sockaddr_in6	*sin6;
1160 	struct address		*h;
1161 
1162 	bzero(&hints, sizeof(hints));
1163 	hints.ai_family = PF_UNSPEC;
1164 	hints.ai_socktype = SOCK_DGRAM; /* DUMMY */
1165 	hints.ai_flags = AI_ADDRCONFIG;
1166 	error = getaddrinfo(s, NULL, &hints, &res0);
1167 	if (error == EAI_AGAIN || error == EAI_NODATA || error == EAI_NONAME)
1168 		return (0);
1169 	if (error) {
1170 		log_warnx("host_dns: could not parse \"%s\": %s", s,
1171 		    gai_strerror(error));
1172 		return (-1);
1173 	}
1174 
1175 	for (res = res0; res && cnt < max; res = res->ai_next) {
1176 		if (res->ai_family != AF_INET &&
1177 		    res->ai_family != AF_INET6)
1178 			continue;
1179 		if ((h = calloc(1, sizeof(*h))) == NULL)
1180 			fatal(__func__);
1181 
1182 		h->port = port;
1183 		if (oid != NULL) {
1184 			if ((h->sa_oid = calloc(1, sizeof(*oid))) == NULL)
1185 				fatal(__func__);
1186 			bcopy(oid, h->sa_oid, sizeof(*oid));
1187 		}
1188 		if (cmn != NULL) {
1189 			if ((h->sa_community = strdup(cmn)) == NULL)
1190 				fatal(__func__);
1191 		}
1192 
1193 		h->ss.ss_family = res->ai_family;
1194 		if (res->ai_family == AF_INET) {
1195 			sain = (struct sockaddr_in *)&h->ss;
1196 			sain->sin_len = sizeof(struct sockaddr_in);
1197 			sain->sin_addr.s_addr = ((struct sockaddr_in *)
1198 			    res->ai_addr)->sin_addr.s_addr;
1199 		} else {
1200 			sin6 = (struct sockaddr_in6 *)&h->ss;
1201 			sin6->sin6_len = sizeof(struct sockaddr_in6);
1202 			memcpy(&sin6->sin6_addr, &((struct sockaddr_in6 *)
1203 			    res->ai_addr)->sin6_addr, sizeof(struct in6_addr));
1204 		}
1205 
1206 		TAILQ_INSERT_HEAD(al, h, entry);
1207 		cnt++;
1208 	}
1209 	if (cnt == max && res) {
1210 		log_warnx("host_dns: %s resolves to more than %d hosts",
1211 		    s, max);
1212 	}
1213 	freeaddrinfo(res0);
1214 	if (oid != NULL)
1215 		free(oid);
1216 	if (cmn != NULL)
1217 		free(cmn);
1218 	return (cnt);
1219 }
1220 
1221 int
1222 host(const char *s, struct addresslist *al, int max,
1223     in_port_t port, struct ber_oid *oid, char *cmn)
1224 {
1225 	struct address	*h;
1226 
1227 	h = host_v4(s);
1228 
1229 	/* IPv6 address? */
1230 	if (h == NULL)
1231 		h = host_v6(s);
1232 
1233 	if (h != NULL) {
1234 		h->port = port;
1235 		h->sa_oid = oid;
1236 		h->sa_community = cmn;
1237 
1238 		TAILQ_INSERT_HEAD(al, h, entry);
1239 		return (1);
1240 	}
1241 
1242 	return (host_dns(s, al, max, port, oid, cmn));
1243 }
1244