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