xref: /openbsd/usr.sbin/snmpd/parse.y (revision 510d2225)
1 /*	$OpenBSD: parse.y,v 1.83 2023/11/21 08:47:04 martijn 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 #include <sys/utsname.h>
32 
33 #include <netinet/in.h>
34 #include <net/if.h>
35 
36 #include <arpa/inet.h>
37 
38 #include <openssl/sha.h>
39 
40 #include <ctype.h>
41 #include <unistd.h>
42 #include <err.h>
43 #include <errno.h>
44 #include <event.h>
45 #include <grp.h>
46 #include <inttypes.h>
47 #include <limits.h>
48 #include <stdint.h>
49 #include <stdarg.h>
50 #include <stdlib.h>
51 #include <stdio.h>
52 #include <netdb.h>
53 #include <pwd.h>
54 #include <string.h>
55 #include <syslog.h>
56 
57 #include "application.h"
58 #include "snmpd.h"
59 #include "mib.h"
60 
61 TAILQ_HEAD(files, file)		 files = TAILQ_HEAD_INITIALIZER(files);
62 static struct file {
63 	TAILQ_ENTRY(file)	 entry;
64 	FILE			*stream;
65 	char			*name;
66 	size_t			 ungetpos;
67 	size_t			 ungetsize;
68 	u_char			*ungetbuf;
69 	int			 eof_reached;
70 	int			 lineno;
71 	int			 errors;
72 } *file, *topfile;
73 struct file	*pushfile(const char *, int);
74 int		 popfile(void);
75 int		 check_file_secrecy(int, const char *);
76 int		 yyparse(void);
77 int		 yylex(void);
78 int		 yyerror(const char *, ...)
79     __attribute__((__format__ (printf, 1, 2)))
80     __attribute__((__nonnull__ (1)));
81 int		 kw_cmp(const void *, const void *);
82 int		 lookup(char *);
83 int		 igetc(void);
84 int		 lgetc(int);
85 void		 lungetc(int);
86 int		 findeol(void);
87 
88 TAILQ_HEAD(symhead, sym)	 symhead = TAILQ_HEAD_INITIALIZER(symhead);
89 struct sym {
90 	TAILQ_ENTRY(sym)	 entry;
91 	int			 used;
92 	int			 persist;
93 	char			*nam;
94 	char			*val;
95 };
96 int		 symset(const char *, const char *, int);
97 char		*symget(const char *);
98 
99 struct snmpd			*conf = NULL;
100 static int			 errors = 0;
101 static struct usmuser		*user = NULL;
102 static struct ber_oid		*smi_object;
103 
104 static uint8_t			 engineid[SNMPD_MAXENGINEIDLEN];
105 static int32_t			 enginepen;
106 static size_t			 engineidlen;
107 
108 int		 host(const char *, const char *, int,
109 		    struct sockaddr_storage *, int);
110 int		 listen_add(struct sockaddr_storage *, int, int);
111 
112 typedef struct {
113 	union {
114 		int64_t		 number;
115 		char		*string;
116 		struct host	*host;
117 		struct timeval	 tv;
118 		struct ber_oid	*oid;
119 		struct agentx_master ax;
120 		struct {
121 			int		 type;
122 			void		*data;
123 			long long	 value;
124 		}		 data;
125 		enum usmauth	 auth;
126 		enum usmpriv	 enc;
127 	} v;
128 	int lineno;
129 } YYSTYPE;
130 
131 #define axm_opts	axm_fd
132 #define AXM_PATH	1 << 0
133 #define AXM_OWNER	1 << 1
134 #define AXM_GROUP	1 << 2
135 #define AXM_MODE	1 << 3
136 
137 %}
138 
139 %token	INCLUDE
140 %token	LISTEN ON READ WRITE NOTIFY SNMPV1 SNMPV2 SNMPV3
141 %token	AGENTX PATH OWNER GROUP MODE
142 %token	ENGINEID PEN OPENBSD IP4 IP6 MAC TEXT OCTETS AGENTID HOSTHASH
143 %token	SYSTEM CONTACT DESCR LOCATION NAME OBJECTID SERVICES RTFILTER
144 %token	READONLY READWRITE OCTETSTRING INTEGER COMMUNITY TRAP RECEIVER
145 %token	SECLEVEL NONE AUTH ENC USER AUTHKEY ENCKEY ERROR
146 %token	HANDLE DEFAULT SRCADDR TCP UDP BLOCKLIST PORT
147 %token	<v.string>	STRING
148 %token	<v.number>	NUMBER
149 %type	<v.string>	usmuser community optcommunity
150 %type	<v.number>	listenproto listenflag listenflags
151 %type	<v.string>	srcaddr port
152 %type	<v.number>	optwrite yesno seclevel
153 %type	<v.data>	cmd hostauth hostauthv3 usmauthopts usmauthopt
154 %type	<v.oid>		oid hostoid trapoid
155 %type	<v.auth>	auth
156 %type	<v.enc>		enc
157 %type	<v.ax>		agentxopts agentxopt
158 
159 %%
160 
161 grammar		: /* empty */
162 		| grammar include '\n'
163 		| grammar '\n'
164 		| grammar varset '\n'
165 		| grammar main '\n'
166 		| grammar system '\n'
167 		| grammar object '\n'
168 		| grammar error '\n'		{ file->errors++; }
169 		;
170 
171 include		: INCLUDE STRING		{
172 			struct file	*nfile;
173 
174 			if ((nfile = pushfile($2, 0)) == NULL) {
175 				yyerror("failed to include file %s", $2);
176 				free($2);
177 				YYERROR;
178 			}
179 			free($2);
180 
181 			file = nfile;
182 			lungetc('\n');
183 		}
184 		;
185 
186 varset		: STRING '=' STRING	{
187 			char *s = $1;
188 			while (*s++) {
189 				if (isspace((unsigned char)*s)) {
190 					yyerror("macro name cannot contain "
191 					    "whitespace");
192 					free($1);
193 					free($3);
194 					YYERROR;
195 				}
196 			}
197 			if (symset($1, $3, 0) == -1)
198 				fatal("cannot store variable");
199 			free($1);
200 			free($3);
201 		}
202 		;
203 
204 yesno		:  STRING			{
205 			if (!strcmp($1, "yes"))
206 				$$ = 1;
207 			else if (!strcmp($1, "no"))
208 				$$ = 0;
209 			else {
210 				yyerror("syntax error, "
211 				    "either yes or no expected");
212 				free($1);
213 				YYERROR;
214 			}
215 			free($1);
216 		}
217 		;
218 
219 main		: LISTEN ON listen_udptcp
220 		| AGENTX agentxopts {
221 			struct agentx_master *master, *test;
222 			struct group *grp;
223 
224 			if ((master = malloc(sizeof(*master))) == NULL) {
225 				yyerror("malloc");
226 				YYERROR;
227 			}
228 			master->axm_fd = -1;
229 			master->axm_sun.sun_len = sizeof(master->axm_sun);
230 			master->axm_sun.sun_family = AF_UNIX;
231 			if ($2.axm_opts & AXM_PATH)
232 				strlcpy(master->axm_sun.sun_path,
233 				    $2.axm_sun.sun_path,
234 				    sizeof(master->axm_sun.sun_path));
235 			else
236 				strlcpy(master->axm_sun.sun_path,
237 				    AGENTX_MASTER_PATH,
238 				    sizeof(master->axm_sun.sun_path));
239 			master->axm_owner = $2.axm_opts & AXM_OWNER ?
240 			    $2.axm_owner : 0;
241 			if ($2.axm_opts & AXM_GROUP)
242 				master->axm_group = $2.axm_group;
243 			else {
244 				if ((grp = getgrnam(AGENTX_GROUP)) == NULL) {
245 					yyerror("agentx: group %s not found",
246 					    AGENTX_GROUP);
247 					YYERROR;
248 				}
249 				master->axm_group = grp->gr_gid;
250 			}
251 			master->axm_mode = $2.axm_opts & AXM_MODE ?
252 			    $2.axm_mode : 0660;
253 
254 			TAILQ_FOREACH(test, &conf->sc_agentx_masters,
255 			    axm_entry) {
256 				if (strcmp(test->axm_sun.sun_path,
257 				    master->axm_sun.sun_path) == 0) {
258 					yyerror("agentx: duplicate entry: %s",
259 					    test->axm_sun.sun_path);
260 					YYERROR;
261 				}
262 			}
263 
264 			TAILQ_INSERT_TAIL(&conf->sc_agentx_masters, master,
265 			    axm_entry);
266 		}
267 		| engineid_local {
268 			if (conf->sc_engineid_len != 0) {
269 				yyerror("Redefinition of engineid");
270 				YYERROR;
271 			}
272 			memcpy(conf->sc_engineid, engineid, engineidlen);
273 			conf->sc_engineid_len = engineidlen;
274 		}
275 		| READONLY COMMUNITY STRING	{
276 			if (strlcpy(conf->sc_rdcommunity, $3,
277 			    sizeof(conf->sc_rdcommunity)) >=
278 			    sizeof(conf->sc_rdcommunity)) {
279 				yyerror("r/o community name too long");
280 				free($3);
281 				YYERROR;
282 			}
283 			free($3);
284 		}
285 		| READWRITE COMMUNITY STRING	{
286 			if (strlcpy(conf->sc_rwcommunity, $3,
287 			    sizeof(conf->sc_rwcommunity)) >=
288 			    sizeof(conf->sc_rwcommunity)) {
289 				yyerror("r/w community name too long");
290 				free($3);
291 				YYERROR;
292 			}
293 			free($3);
294 		}
295 		| TRAP COMMUNITY STRING		{
296 			if (strlcpy(conf->sc_trcommunity, $3,
297 			    sizeof(conf->sc_trcommunity)) >=
298 			    sizeof(conf->sc_trcommunity)) {
299 				yyerror("trap community name too long");
300 				free($3);
301 				YYERROR;
302 			}
303 			free($3);
304 		}
305 		| TRAP RECEIVER	host
306 		| TRAP HANDLE trapoid cmd {
307 			struct trapcmd *cmd = $4.data;
308 
309 			cmd->cmd_oid = $3;
310 
311 			if (trapcmd_add(cmd) != 0) {
312 				free($3);
313 				free(cmd);
314 				yyerror("duplicate oid");
315 				YYERROR;
316 			}
317 			conf->sc_traphandler = 1;
318 		}
319 		| BLOCKLIST oid {
320 			struct ber_oid *blocklist;
321 
322 			blocklist = recallocarray(conf->sc_blocklist,
323 			    conf->sc_nblocklist, conf->sc_nblocklist + 1,
324 			    sizeof(*blocklist));
325 			if (blocklist == NULL) {
326 				yyerror("malloc");
327 				YYERROR;
328 			}
329 			conf->sc_blocklist = blocklist;
330 			blocklist[conf->sc_nblocklist++] = *$2;
331 			free($2);
332 		}
333 		| RTFILTER yesno		{
334 			conf->sc_rtfilter = $2;
335 		}
336 		| seclevel {
337 			conf->sc_min_seclevel = $1;
338 		}
339 		| USER STRING			{
340 			const char *errstr;
341 			user = usm_newuser($2, &errstr);
342 			if (user == NULL) {
343 				yyerror("%s", errstr);
344 				free($2);
345 				YYERROR;
346 			}
347 		} userspecs {
348 			const char *errstr;
349 			if (usm_checkuser(user, &errstr) < 0) {
350 				yyerror("%s", errstr);
351 				YYERROR;
352 			}
353 			user = NULL;
354 		}
355 		;
356 
357 listenproto	: /* empty */			{ $$ = SOCK_DGRAM; }
358 		| UDP 				{ $$ = SOCK_DGRAM; }
359 		| TCP				{ $$ = SOCK_STREAM; }
360 		;
361 
362 listenflags	: /* empty */ { $$ = 0; }
363 		| listenflags listenflag { $$ |= $2; }
364 		;
365 
366 listenflag	: READ { $$ = ADDRESS_FLAG_READ; }
367 		| WRITE { $$ = ADDRESS_FLAG_WRITE; }
368 		| NOTIFY { $$ = ADDRESS_FLAG_NOTIFY; }
369 		| SNMPV1 { $$ = ADDRESS_FLAG_SNMPV1; }
370 		| SNMPV2 { $$ = ADDRESS_FLAG_SNMPV2; }
371 		| SNMPV3 { $$ = ADDRESS_FLAG_SNMPV3; }
372 		;
373 
374 listen_udptcp	: listenproto STRING port listenflags	{
375 			struct sockaddr_storage ss[16];
376 			int nhosts, j;
377 			char *address[2], *port = $3;
378 			size_t addresslen = 1, i;
379 
380 			if (port == NULL) {
381 				if (($4 & ADDRESS_FLAG_PERM) ==
382 				    ADDRESS_FLAG_NOTIFY)
383 					port = SNMPTRAP_PORT;
384 				else
385 					port = SNMP_PORT;
386 			}
387 
388 			if (strcmp($2, "any") == 0) {
389 				addresslen = 2;
390 				address[0] = "0.0.0.0";
391 				address[1] = "::";
392 			} else {
393 				addresslen = 1;
394 				address[0] = $2;
395 			}
396 
397 			for (i = 0; i < addresslen; i++) {
398 				nhosts = host(address[i], port, $1, ss, nitems(ss));
399 				if (nhosts < 1) {
400 					yyerror("invalid address: %s", $2);
401 					free($2);
402 					free($3);
403 					YYERROR;
404 				}
405 				if (nhosts > (int)nitems(ss))
406 					log_warn("%s:%s resolves to more than "
407 					    "%zu hosts", $2, port, nitems(ss));
408 
409 				for (j = 0; j < nhosts; j++) {
410 					if (listen_add(&(ss[j]), $1, $4) == -1) {
411 						yyerror("calloc");
412 						YYERROR;
413 					}
414 				}
415 			}
416 			free($2);
417 			free($3);
418 		}
419 		;
420 
421 port		: /* empty */			{
422 			$$ = NULL;
423 		}
424 		| PORT STRING			{
425 			$$ = $2;
426 		}
427 		| PORT NUMBER			{
428 			char *number;
429 
430 			if ($2 > UINT16_MAX) {
431 				yyerror("port number too large");
432 				YYERROR;
433 			}
434 			if ($2 < 1) {
435 				yyerror("port number too small");
436 				YYERROR;
437 			}
438 			if (asprintf(&number, "%"PRId64, $2) == -1) {
439 				yyerror("malloc");
440 				YYERROR;
441 			}
442 			$$ = number;
443 		}
444 		;
445 
446 agentxopt	: PATH STRING			{
447 			if ($2[0] != '/') {
448 				yyerror("agentx path: must be absolute");
449 				YYERROR;
450 			}
451 			if (strlcpy($$.axm_sun.sun_path, $2,
452 			    sizeof($$.axm_sun.sun_path)) >=
453 			    sizeof($$.axm_sun.sun_path)) {
454 				yyerror("agentx path: too long");
455 				YYERROR;
456 			}
457 			$$.axm_opts = AXM_PATH;
458 		}
459 		| OWNER NUMBER			{
460 			if ($2 > UID_MAX) {
461 				yyerror("agentx owner: too large");
462 				YYERROR;
463 			}
464 			$$.axm_owner = $2;
465 			$$.axm_opts = AXM_OWNER;
466 		}
467 		| OWNER STRING			{
468 			struct passwd *pw;
469 			const char *errstr;
470 
471 			$$.axm_owner = strtonum($2, 0, UID_MAX, &errstr);
472 			if (errstr != NULL && errno == ERANGE) {
473 				yyerror("agentx owner: %s", errstr);
474 				YYERROR;
475 			}
476 			if ((pw = getpwnam($2)) == NULL) {
477 				yyerror("agentx owner: user not found");
478 				YYERROR;
479 			}
480 			$$.axm_owner = pw->pw_uid;
481 			$$.axm_opts = AXM_OWNER;
482 		}
483 		| GROUP NUMBER			{
484 			if ($2 > GID_MAX) {
485 				yyerror("agentx group: too large");
486 				YYERROR;
487 			}
488 			$$.axm_group = $2;
489 			$$.axm_opts = AXM_GROUP;
490 		}
491 		| GROUP STRING			{
492 			struct group *gr;
493 			const char *errstr;
494 
495 			$$.axm_group = strtonum($2, 0, GID_MAX, &errstr);
496 			if (errstr != NULL && errno == ERANGE) {
497 				yyerror("agentx group: %s", errstr);
498 				YYERROR;
499 			}
500 			if ((gr = getgrnam($2)) == NULL) {
501 				yyerror("agentx group: group not found");
502 				YYERROR;
503 			}
504 			$$.axm_group = gr->gr_gid;
505 			$$.axm_opts = AXM_GROUP;
506 		}
507 		| MODE NUMBER			{
508 			long mode;
509 			char *endptr, str[21];
510 
511 			snprintf(str, sizeof(str), "%lld", $2);
512 			errno = 0;
513 			mode = strtol(str, &endptr, 8);
514 			if (errno != 0 || endptr[0] != '\0' ||
515 			    mode <= 0 || mode > 0777) {
516 				yyerror("agentx mode: invalid");
517 				YYERROR;
518 			}
519 			$$.axm_mode = mode;
520 			$$.axm_opts = AXM_MODE;
521 		}
522 		;
523 
524 agentxopts	: /* empty */			{
525 			bzero(&$$, sizeof($$));
526 		}
527 		| agentxopts agentxopt {
528 			if ($$.axm_opts & $2.axm_opts) {
529 				yyerror("agentx: duplicate option");
530 				YYERROR;
531 			}
532 			switch ($2.axm_opts) {
533 			case AXM_PATH:
534 				strlcpy($$.axm_sun.sun_path,
535 				    $2.axm_sun.sun_path,
536 				    sizeof($$.axm_sun.sun_path));
537 				break;
538 			case AXM_OWNER:
539 				$$.axm_owner = $2.axm_owner;
540 				break;
541 			case AXM_GROUP:
542 				$$.axm_group = $2.axm_group;
543 				break;
544 			case AXM_MODE:
545 				$$.axm_mode = $2.axm_mode;
546 				break;
547 			}
548 			$$.axm_opts |= $2.axm_opts;
549 		}
550 		;
551 
552 enginefmt	: IP4 STRING			{
553 			struct in_addr addr;
554 
555 			engineid[engineidlen++] = SNMP_ENGINEID_FMT_IPv4;
556 			if (inet_pton(AF_INET, $2, &addr) != 1) {
557 				yyerror("Invalid ipv4 address: %s", $2);
558 				free($2);
559 				YYERROR;
560 			}
561 			memcpy(engineid + engineidlen, &addr,
562 			    sizeof(engineid) - engineidlen);
563 			engineid[0] |= SNMP_ENGINEID_NEW;
564 			engineidlen += sizeof(addr);
565 			free($2);
566 		}
567 		| IP6 STRING			{
568 			struct in6_addr addr;
569 
570 			engineid[engineidlen++] = SNMP_ENGINEID_FMT_IPv6;
571 			if (inet_pton(AF_INET6, $2, &addr) != 1) {
572 				yyerror("Invalid ipv6 address: %s", $2);
573 				free($2);
574 				YYERROR;
575 			}
576 			memcpy(engineid + engineidlen, &addr,
577 			    sizeof(engineid) - engineidlen);
578 			engineid[0] |= SNMP_ENGINEID_NEW;
579 			engineidlen += sizeof(addr);
580 			free($2);
581 		}
582 		| MAC STRING			{
583 			size_t i;
584 
585 			if (strlen($2) != 5 * 3 + 2) {
586 				yyerror("Invalid mac address: %s", $2);
587 				free($2);
588 				YYERROR;
589 			}
590 			engineid[engineidlen++] = SNMP_ENGINEID_FMT_MAC;
591 			for (i = 0; i < 6; i++) {
592 				if (fromhexstr(engineid + engineidlen + i,
593 				    $2 + (i * 3), 2) == NULL ||
594 				    $2[i * 3 + 2] != (i < 5 ? ':' : '\0')) {
595 					yyerror("Invalid mac address: %s", $2);
596 					free($2);
597 					YYERROR;
598 				}
599 			}
600 			engineid[0] |= SNMP_ENGINEID_NEW;
601 			engineidlen += 6;
602 			free($2);
603 		}
604 		| TEXT STRING			{
605 			size_t i, fmtstart;
606 
607 			engineid[engineidlen++] = SNMP_ENGINEID_FMT_TEXT;
608 			for (i = 0, fmtstart = engineidlen;
609 			    i < sizeof(engineid) - engineidlen && $2[i] != '\0';
610 			    i++) {
611 				if (!isprint($2[i])) {
612 					yyerror("invalid text character");
613 					free($2);
614 					YYERROR;
615 				}
616 				engineid[fmtstart + i] = $2[i];
617 				engineidlen++;
618 			}
619 			if (i == 0 || $2[i] != '\0') {
620 				yyerror("Invalid text length: %s", $2);
621 				free($2);
622 				YYERROR;
623 			}
624 			engineid[0] |= SNMP_ENGINEID_NEW;
625 			free($2);
626 		}
627 		| OCTETS STRING			{
628 			if (strlen($2) / 2 > sizeof(engineid) - 1) {
629 				yyerror("Invalid octets length: %s", $2);
630 				free($2);
631 				YYERROR;
632 			}
633 
634 			engineid[engineidlen++] = SNMP_ENGINEID_FMT_OCT;
635 			if (fromhexstr(engineid + engineidlen, $2,
636 			    strlen($2)) == NULL) {
637 				yyerror("Invalid octets: %s", $2);
638 				free($2);
639 				YYERROR;
640 			}
641 			engineidlen += strlen($2) / 2;
642 			engineid[0] |= SNMP_ENGINEID_NEW;
643 			free($2);
644 		}
645 		| AGENTID STRING		{
646 			if (strlen($2) / 2 != 8) {
647 				yyerror("Invalid agentid length: %s", $2);
648 				free($2);
649 				YYERROR;
650 			}
651 
652 			if (fromhexstr(engineid + engineidlen, $2,
653 			    strlen($2)) == NULL) {
654 				yyerror("Invalid agentid: %s", $2);
655 				free($2);
656 				YYERROR;
657 			}
658 			engineidlen += 8;
659 			engineid[0] |= SNMP_ENGINEID_OLD;
660 			free($2);
661 		}
662 		| HOSTHASH STRING		{
663 			if (enginepen != PEN_OPENBSD) {
664 				yyerror("hosthash only allowed for pen "
665 				    "openbsd");
666 				YYERROR;
667 			}
668 			engineid[engineidlen++] = SNMP_ENGINEID_FMT_HH;
669 			memcpy(engineid + engineidlen,
670 			    SHA256($2, strlen($2), NULL),
671 			    sizeof(engineid) - engineidlen);
672 			engineidlen = sizeof(engineid);
673 			engineid[0] |= SNMP_ENGINEID_NEW;
674 			free($2);
675 		}
676 		| NUMBER STRING			{
677 			if (enginepen == PEN_OPENBSD) {
678 				yyerror("%lld is only allowed when pen is not "
679 				    "openbsd", $1);
680 				YYERROR;
681 			}
682 
683 			if ($1 < 128 || $1 > 255) {
684 				yyerror("Invalid format number: %lld\n", $1);
685 				YYERROR;
686 			}
687 			if (strlen($2) / 2 > sizeof(engineid) - 1) {
688 				yyerror("Invalid octets length: %s", $2);
689 				free($2);
690 				YYERROR;
691 			}
692 
693 			engineid[engineidlen++] = (uint8_t)$1;
694 			if (fromhexstr(engineid + engineidlen, $2,
695 			    strlen($2)) == NULL) {
696 				yyerror("Invalid octets: %s", $2);
697 				free($2);
698 				YYERROR;
699 			}
700 			engineidlen += strlen($2) / 2;
701 			engineid[0] |= SNMP_ENGINEID_NEW;
702 			free($2);
703 		}
704 		;
705 
706 enginefmt_local	: enginefmt
707 		| HOSTHASH			{
708 			char hostname[HOST_NAME_MAX + 1];
709 
710 			if (enginepen != PEN_OPENBSD) {
711 				yyerror("hosthash only allowed for pen "
712 				    "openbsd");
713 				YYERROR;
714 			}
715 
716 			if (gethostname(hostname, sizeof(hostname)) == -1) {
717 				yyerror("gethostname: %s", strerror(errno));
718 				YYERROR;
719 			}
720 
721 			engineid[engineidlen++] = SNMP_ENGINEID_FMT_HH;
722 			memcpy(engineid + engineidlen,
723 			    SHA256(hostname, strlen(hostname), NULL),
724 			    sizeof(engineid) - engineidlen);
725 			engineidlen = sizeof(engineid);
726 			engineid[0] |= SNMP_ENGINEID_NEW;
727 		}
728 		;
729 
730 pen		: /* empty */			{
731 			enginepen = PEN_OPENBSD;
732 		}
733 		| PEN NUMBER			{
734 			if ($2 > INT32_MAX) {
735 				yyerror("pen number too large");
736 				YYERROR;
737 			}
738 			if ($2 <= 0) {
739 				yyerror("pen number too small");
740 				YYERROR;
741 			}
742 			enginepen = $2;
743 		}
744 		| PEN OPENBSD			{
745 			enginepen = PEN_OPENBSD;
746 		}
747 		;
748 
749 engineid_local	: ENGINEID pen			{
750 			uint32_t npen = htonl(enginepen);
751 
752 			memcpy(engineid, &npen, sizeof(enginepen));
753 			engineidlen = sizeof(enginepen);
754 		} enginefmt_local
755 
756 system		: SYSTEM sysmib
757 		;
758 
759 sysmib		: CONTACT STRING		{
760 			if (conf->sc_system.sys_contact[0] != '\0') {
761 				yyerror("system contact already defined");
762 				free($2);
763 				YYERROR;
764 			}
765 			if (strlcpy(conf->sc_system.sys_contact, $2,
766 			    sizeof(conf->sc_system.sys_contact)) >=
767 			    sizeof(conf->sc_system.sys_contact)) {
768 				yyerror("system contact too long");
769 				free($2);
770 				YYERROR;
771 			}
772 			free($2);
773 		}
774 		| DESCR STRING			{
775 			if (conf->sc_system.sys_descr[0] != '\0') {
776 				yyerror("system description already defined");
777 				free($2);
778 				YYERROR;
779 			}
780 			if (strlcpy(conf->sc_system.sys_descr, $2,
781 			    sizeof(conf->sc_system.sys_descr)) >=
782 			    sizeof(conf->sc_system.sys_descr)) {
783 				yyerror("system description too long");
784 				free($2);
785 				YYERROR;
786 			}
787 			free($2);
788 		}
789 		| LOCATION STRING		{
790 			if (conf->sc_system.sys_location[0] != '\0') {
791 				yyerror("system location already defined");
792 				free($2);
793 				YYERROR;
794 			}
795 			if (strlcpy(conf->sc_system.sys_location, $2,
796 			    sizeof(conf->sc_system.sys_location)) >=
797 			    sizeof(conf->sc_system.sys_location)) {
798 				yyerror("system location too long");
799 				free($2);
800 				YYERROR;
801 			}
802 			free($2);
803 		}
804 		| NAME STRING			{
805 			if (conf->sc_system.sys_name[0] != '\0') {
806 				yyerror("system name already defined");
807 				free($2);
808 				YYERROR;
809 			}
810 			if (strlcpy(conf->sc_system.sys_name, $2,
811 			    sizeof(conf->sc_system.sys_name)) >=
812 			    sizeof(conf->sc_system.sys_name)) {
813 				yyerror("system name too long");
814 				free($2);
815 				YYERROR;
816 			}
817 			free($2);
818 		}
819 		| OBJECTID oid			{
820 			if (conf->sc_system.sys_oid.bo_n != 0) {
821 				yyerror("system oid already defined");
822 				free($2);
823 				YYERROR;
824 			}
825 			conf->sc_system.sys_oid = *$2;
826 			free($2);
827 		}
828 		| SERVICES NUMBER		{
829 			if (conf->sc_system.sys_services != -1) {
830 				yyerror("system services already defined");
831 				YYERROR;
832 			}
833 			if ($2 < 0) {
834 				yyerror("system services too small");
835 				YYERROR;
836 			} else if ($2 > 127) {
837 				yyerror("system services too large");
838 				YYERROR;
839 			}
840 			conf->sc_system.sys_services = $2;
841 		}
842 		;
843 
844 object		: OBJECTID oid NAME STRING optwrite 	{
845 			const char *error;
846 
847 			smi_object = $2;
848 			error = smi_insert($2, $4);
849 			free($4);
850 			if (error != NULL) {
851 				yyerror("%s", error);
852 				free($2);
853 				YYERROR;
854 			}
855 		} objectvalue {
856 			free(smi_object);
857 		}
858 		;
859 
860 objectvalue	: INTEGER NUMBER			{
861 			const char *error;
862 
863 			if ($2 < INT32_MIN) {
864 				yyerror("number too small");
865 				YYERROR;
866 			}
867 			if ($2 > INT32_MAX) {
868 				yyerror("number too large");
869 				YYERROR;
870 			}
871 			error = appl_internal_object_int(smi_object, $2);
872 			if (error != NULL) {
873 				yyerror("%s", error);
874 				YYERROR;
875 			}
876 		}
877 		| OCTETSTRING STRING			{
878 			const char *error;
879 
880 			error = appl_internal_object_string(smi_object, $2);
881 			if (error != NULL) {
882 				yyerror("%s", error);
883 				free($2);
884 				YYERROR;
885 			}
886 
887 		}
888 		;
889 
890 optwrite	: READONLY				{ $$ = 0; }
891 		| READWRITE				{ $$ = 1; }
892 		;
893 
894 oid		: STRING				{
895 			struct ber_oid	*oid;
896 			if ((oid = calloc(1, sizeof(*oid))) == NULL) {
897 				yyerror("calloc");
898 				free($1);
899 				YYERROR;
900 			}
901 			if (smi_string2oid($1, oid) == -1) {
902 				yyerror("invalid OID: %s", $1);
903 				free(oid);
904 				free($1);
905 				YYERROR;
906 			}
907 			free($1);
908 			$$ = oid;
909 		}
910 		;
911 
912 trapoid		: oid					{ $$ = $1; }
913 		| DEFAULT				{
914 			struct ber_oid	*sysoid;
915 			if ((sysoid =
916 			    calloc(1, sizeof(*sysoid))) == NULL) {
917 				yyerror("calloc");
918 				YYERROR;
919 			}
920 			ober_string2oid("1.3", sysoid);
921 			$$ = sysoid;
922 		}
923 		;
924 
925 hostoid		: /* empty */				{ $$ = NULL; }
926 		| OBJECTID oid				{ $$ = $2; }
927 		;
928 
929 usmuser		: USER STRING				{
930 			if (strlen($2) > SNMPD_MAXUSERNAMELEN) {
931 				yyerror("User name too long: %s", $2);
932 				free($2);
933 				YYERROR;
934 			}
935 			$$ = $2;
936 		}
937 		;
938 
939 community	: COMMUNITY STRING			{
940 			if (strlen($2) > SNMPD_MAXCOMMUNITYLEN) {
941 				yyerror("Community too long: %s", $2);
942 				free($2);
943 				YYERROR;
944 			}
945 			$$ = $2;
946 		}
947 		;
948 
949 optcommunity	: /* empty */				{ $$ = NULL; }
950 		| community				{ $$ = $1; }
951 		;
952 
953 usmauthopt	: usmuser				{
954 			$$.data = $1;
955 			$$.value = -1;
956 		}
957 		| seclevel				{
958 			$$.data = 0;
959 			$$.value = $1;
960 		}
961 		;
962 
963 usmauthopts	: /* empty */				{
964 			$$.data = NULL;
965 			$$.value = -1;
966 		}
967 		| usmauthopts usmauthopt		{
968 			if ($2.data != NULL) {
969 				if ($$.data != NULL) {
970 					yyerror("user redefined");
971 					free($2.data);
972 					YYERROR;
973 				}
974 				$$.data = $2.data;
975 			} else {
976 				if ($$.value != -1) {
977 					yyerror("seclevel redefined");
978 					YYERROR;
979 				}
980 				$$.value = $2.value;
981 			}
982 		}
983 		;
984 
985 hostauthv3	: usmauthopts				{
986 			if ($1.data == NULL) {
987 				yyerror("user missing");
988 				YYERROR;
989 			}
990 			$$.data = $1.data;
991 			$$.value = $1.value;
992 		}
993 		;
994 
995 hostauth	: hostauthv3				{
996 			$$.type = SNMP_V3;
997 			$$.data = $1.data;
998 			$$.value = $1.value;
999 		}
1000 		| SNMPV2 optcommunity			{
1001 			$$.type = SNMP_V2;
1002 			$$.data = $2;
1003 		}
1004 		| SNMPV3 hostauthv3			{
1005 			$$.type = SNMP_V3;
1006 			$$.data = $2.data;
1007 			$$.value = $2.value;
1008 		}
1009 		;
1010 
1011 srcaddr		: /* empty */				{ $$ = NULL; }
1012 		| SRCADDR STRING			{ $$ = $2; }
1013 		;
1014 
1015 hostdef		: STRING hostoid hostauth srcaddr	{
1016 			struct sockaddr_storage ss;
1017 			struct trap_address *tr;
1018 
1019 			if ((tr = calloc(1, sizeof(*tr))) == NULL) {
1020 				yyerror("calloc");
1021 				YYERROR;
1022 			}
1023 
1024 			if (host($1, SNMPTRAP_PORT, SOCK_DGRAM, &ss, 1) <= 0) {
1025 				yyerror("invalid host: %s", $1);
1026 				free($1);
1027 				free($2);
1028 				free($3.data);
1029 				free($4);
1030 				free(tr);
1031 				YYERROR;
1032 			}
1033 			free($1);
1034 			memcpy(&(tr->ta_ss), &ss, sizeof(ss));
1035 			if ($4 != NULL) {
1036 				if (host($1, "0", SOCK_DGRAM, &ss, 1) <= 0) {
1037 					yyerror("invalid host: %s", $1);
1038 					free($2);
1039 					free($3.data);
1040 					free($4);
1041 					free(tr);
1042 					YYERROR;
1043 				}
1044 				free($4);
1045 				memcpy(&(tr->ta_sslocal), &ss, sizeof(ss));
1046 			}
1047 			tr->ta_oid = $2;
1048 			tr->ta_version = $3.type;
1049 			if ($3.type == SNMP_V2) {
1050 				(void)strlcpy(tr->ta_community, $3.data,
1051 				    sizeof(tr->ta_community));
1052 				free($3.data);
1053 			} else {
1054 				tr->ta_usmusername = $3.data;
1055 				tr->ta_seclevel = $3.value;
1056 			}
1057 			TAILQ_INSERT_TAIL(&(conf->sc_trapreceivers), tr, entry);
1058 		}
1059 		;
1060 
1061 hostlist	: /* empty */
1062 		| hostlist comma hostdef
1063 		;
1064 
1065 host		: hostdef
1066 		| '{' hostlist '}'
1067 		;
1068 
1069 comma		: /* empty */
1070 		| ','
1071 		;
1072 
1073 seclevel	: SECLEVEL NONE	{ $$ = 0; }
1074 		| SECLEVEL AUTH	{ $$ = SNMP_MSGFLAG_AUTH; }
1075 		| SECLEVEL ENC	{ $$ = SNMP_MSGFLAG_AUTH | SNMP_MSGFLAG_PRIV; }
1076 		;
1077 
1078 userspecs	: /* empty */
1079 		| userspecs userspec
1080 		;
1081 
1082 userspec	: AUTHKEY STRING		{
1083 			user->uu_authkey = $2;
1084 		}
1085 		| AUTH auth			{
1086 			user->uu_auth = $2;
1087 		}
1088 		| ENCKEY STRING			{
1089 			user->uu_privkey = $2;
1090 		}
1091 		| ENC enc			{
1092 			user->uu_priv = $2;
1093 		}
1094 		;
1095 
1096 auth		: STRING			{
1097 			if (strcasecmp($1, "hmac-md5") == 0 ||
1098 			    strcasecmp($1, "hmac-md5-96") == 0)
1099 				$$ = AUTH_MD5;
1100 			else if (strcasecmp($1, "hmac-sha1") == 0 ||
1101 			     strcasecmp($1, "hmac-sha1-96") == 0)
1102 				$$ = AUTH_SHA1;
1103 			else if (strcasecmp($1, "hmac-sha224") == 0 ||
1104 			    strcasecmp($1, "usmHMAC128SHA224AuthProtocol") == 0)
1105 				$$ = AUTH_SHA224;
1106 			else if (strcasecmp($1, "hmac-sha256") == 0 ||
1107 			    strcasecmp($1, "usmHMAC192SHA256AuthProtocol") == 0)
1108 				$$ = AUTH_SHA256;
1109 			else if (strcasecmp($1, "hmac-sha384") == 0 ||
1110 			    strcasecmp($1, "usmHMAC256SHA384AuthProtocol") == 0)
1111 				$$ = AUTH_SHA384;
1112 			else if (strcasecmp($1, "hmac-sha512") == 0 ||
1113 			    strcasecmp($1, "usmHMAC384SHA512AuthProtocol") == 0)
1114 				$$ = AUTH_SHA512;
1115 			else {
1116 				yyerror("syntax error, bad auth hmac");
1117 				free($1);
1118 				YYERROR;
1119 			}
1120 			free($1);
1121 		}
1122 		;
1123 
1124 enc		: STRING			{
1125 			if (strcasecmp($1, "des") == 0 ||
1126 			    strcasecmp($1, "cbc-des") == 0)
1127 				$$ = PRIV_DES;
1128 			else if (strcasecmp($1, "aes") == 0 ||
1129 			    strcasecmp($1, "cfb128-aes-128") == 0)
1130 				$$ = PRIV_AES;
1131 			else {
1132 				yyerror("syntax error, bad encryption cipher");
1133 				free($1);
1134 				YYERROR;
1135 			}
1136 			free($1);
1137 
1138 		}
1139 		;
1140 
1141 cmd		: STRING		{
1142 			struct trapcmd	*cmd;
1143 			size_t		 span, limit;
1144 			char		*pos, **args, **args2;
1145 			int		 nargs = 32;		/* XXX */
1146 
1147 			if ((cmd = calloc(1, sizeof(*cmd))) == NULL ||
1148 			    (args = calloc(nargs, sizeof(char *))) == NULL) {
1149 				free(cmd);
1150 				free($1);
1151 				YYERROR;
1152 			}
1153 
1154 			pos = $1;
1155 			limit = strlen($1);
1156 
1157 			while (pos < $1 + limit &&
1158 			    (span = strcspn(pos, " \t")) != 0) {
1159 				pos[span] = '\0';
1160 				args[cmd->cmd_argc] = strdup(pos);
1161 				if (args[cmd->cmd_argc] == NULL) {
1162 					trapcmd_free(cmd);
1163 					free(args);
1164 					free($1);
1165 					YYERROR;
1166 				}
1167 				cmd->cmd_argc++;
1168 				if (cmd->cmd_argc >= nargs - 1) {
1169 					nargs *= 2;
1170 					args2 = calloc(nargs, sizeof(char *));
1171 					if (args2 == NULL) {
1172 						trapcmd_free(cmd);
1173 						free(args);
1174 						free($1);
1175 						YYERROR;
1176 					}
1177 					args = args2;
1178 				}
1179 				pos += span + 1;
1180 			}
1181 			free($1);
1182 			cmd->cmd_argv = args;
1183 			$$.data = cmd;
1184 		}
1185 		;
1186 
1187 %%
1188 
1189 struct keywords {
1190 	const char	*k_name;
1191 	int		 k_val;
1192 };
1193 
1194 int
1195 yyerror(const char *fmt, ...)
1196 {
1197 	va_list		 ap;
1198 	char		*msg;
1199 
1200 	file->errors++;
1201 	va_start(ap, fmt);
1202 	if (vasprintf(&msg, fmt, ap) == -1)
1203 		fatalx("yyerror vasprintf");
1204 	va_end(ap);
1205 	logit(LOG_CRIT, "%s:%d: %s", file->name, yylval.lineno, msg);
1206 	free(msg);
1207 	return (0);
1208 }
1209 
1210 int
1211 kw_cmp(const void *k, const void *e)
1212 {
1213 	return (strcmp(k, ((const struct keywords *)e)->k_name));
1214 }
1215 
1216 int
1217 lookup(char *s)
1218 {
1219 	/* this has to be sorted always */
1220 	static const struct keywords keywords[] = {
1221 		{ "agentid",			AGENTID },
1222 		{ "agentx",			AGENTX },
1223 		{ "auth",			AUTH },
1224 		{ "authkey",			AUTHKEY },
1225 		{ "blocklist",			BLOCKLIST },
1226 		{ "community",			COMMUNITY },
1227 		{ "contact",			CONTACT },
1228 		{ "default",			DEFAULT },
1229 		{ "description",		DESCR },
1230 		{ "enc",			ENC },
1231 		{ "enckey",			ENCKEY },
1232 		{ "engineid",			ENGINEID },
1233 		{ "filter-routes",		RTFILTER },
1234 		{ "group",			GROUP },
1235 		{ "handle",			HANDLE },
1236 		{ "hosthash",			HOSTHASH },
1237 		{ "include",			INCLUDE },
1238 		{ "integer",			INTEGER },
1239 		{ "ipv4",			IP4 },
1240 		{ "ipv6",			IP6 },
1241 		{ "listen",			LISTEN },
1242 		{ "location",			LOCATION },
1243 		{ "mac",			MAC },
1244 		{ "mode",			MODE },
1245 		{ "name",			NAME },
1246 		{ "none",			NONE },
1247 		{ "notify",			NOTIFY },
1248 		{ "octets",			OCTETS },
1249 		{ "oid",			OBJECTID },
1250 		{ "on",				ON },
1251 		{ "openbsd",			OPENBSD },
1252 		{ "owner",			OWNER },
1253 		{ "path",			PATH },
1254 		{ "pen",			PEN },
1255 		{ "port",			PORT },
1256 		{ "read",			READ },
1257 		{ "read-only",			READONLY },
1258 		{ "read-write",			READWRITE },
1259 		{ "receiver",			RECEIVER },
1260 		{ "seclevel",			SECLEVEL },
1261 		{ "services",			SERVICES },
1262 		{ "snmpv1",			SNMPV1 },
1263 		{ "snmpv2c",			SNMPV2 },
1264 		{ "snmpv3",			SNMPV3 },
1265 		{ "source-address",		SRCADDR },
1266 		{ "string",			OCTETSTRING },
1267 		{ "system",			SYSTEM },
1268 		{ "tcp",			TCP },
1269 		{ "text",			TEXT },
1270 		{ "trap",			TRAP },
1271 		{ "udp",			UDP },
1272 		{ "user",			USER },
1273 		{ "write",			WRITE }
1274 	};
1275 	const struct keywords	*p;
1276 
1277 	p = bsearch(s, keywords, sizeof(keywords)/sizeof(keywords[0]),
1278 	    sizeof(keywords[0]), kw_cmp);
1279 
1280 	if (p)
1281 		return (p->k_val);
1282 	else
1283 		return (STRING);
1284 }
1285 
1286 #define START_EXPAND	1
1287 #define DONE_EXPAND	2
1288 
1289 static int	expanding;
1290 
1291 int
1292 igetc(void)
1293 {
1294 	int	c;
1295 
1296 	while (1) {
1297 		if (file->ungetpos > 0)
1298 			c = file->ungetbuf[--file->ungetpos];
1299 		else c = getc(file->stream);
1300 
1301 		if (c == START_EXPAND)
1302 			expanding = 1;
1303 		else if (c == DONE_EXPAND)
1304 			expanding = 0;
1305 		else
1306 			break;
1307 	}
1308 	return (c);
1309 }
1310 
1311 int
1312 lgetc(int quotec)
1313 {
1314 	int		c, next;
1315 
1316 	if (quotec) {
1317 		if ((c = igetc()) == EOF) {
1318 			yyerror("reached end of file while parsing quoted string");
1319 			if (file == topfile || popfile() == EOF)
1320 				return (EOF);
1321 			return (quotec);
1322 		}
1323 		return (c);
1324 	}
1325 
1326 	while ((c = igetc()) == '\\') {
1327 		next = igetc();
1328 		if (next != '\n') {
1329 			c = next;
1330 			break;
1331 		}
1332 		yylval.lineno = file->lineno;
1333 		file->lineno++;
1334 	}
1335 	if (c == '\t' || c == ' ') {
1336 		/* Compress blanks to a single space. */
1337 		do {
1338 			c = getc(file->stream);
1339 		} while (c == '\t' || c == ' ');
1340 		ungetc(c, file->stream);
1341 		c = ' ';
1342 	}
1343 
1344 	if (c == EOF) {
1345 		/*
1346 		 * Fake EOL when hit EOF for the first time. This gets line
1347 		 * count right if last line in included file is syntactically
1348 		 * invalid and has no newline.
1349 		 */
1350 		if (file->eof_reached == 0) {
1351 			file->eof_reached = 1;
1352 			return ('\n');
1353 		}
1354 		while (c == EOF) {
1355 			if (file == topfile || popfile() == EOF)
1356 				return (EOF);
1357 			c = igetc();
1358 		}
1359 	}
1360 	return (c);
1361 }
1362 
1363 void
1364 lungetc(int c)
1365 {
1366 	if (c == EOF)
1367 		return;
1368 
1369 	if (file->ungetpos >= file->ungetsize) {
1370 		void *p = reallocarray(file->ungetbuf, file->ungetsize, 2);
1371 		if (p == NULL)
1372 			err(1, "%s", __func__);
1373 		file->ungetbuf = p;
1374 		file->ungetsize *= 2;
1375 	}
1376 	file->ungetbuf[file->ungetpos++] = c;
1377 }
1378 
1379 int
1380 findeol(void)
1381 {
1382 	int	c;
1383 
1384 	/* skip to either EOF or the first real EOL */
1385 	while (1) {
1386 		c = lgetc(0);
1387 		if (c == '\n') {
1388 			file->lineno++;
1389 			break;
1390 		}
1391 		if (c == EOF)
1392 			break;
1393 	}
1394 	return (ERROR);
1395 }
1396 
1397 int
1398 yylex(void)
1399 {
1400 	char	 buf[8096];
1401 	char	*p, *val;
1402 	int	 quotec, next, c;
1403 	int	 token;
1404 
1405 top:
1406 	p = buf;
1407 	while ((c = lgetc(0)) == ' ' || c == '\t')
1408 		; /* nothing */
1409 
1410 	yylval.lineno = file->lineno;
1411 	if (c == '#')
1412 		while ((c = lgetc(0)) != '\n' && c != EOF)
1413 			; /* nothing */
1414 	if (c == '$' && !expanding) {
1415 		while (1) {
1416 			if ((c = lgetc(0)) == EOF)
1417 				return (0);
1418 
1419 			if (p + 1 >= buf + sizeof(buf) - 1) {
1420 				yyerror("string too long");
1421 				return (findeol());
1422 			}
1423 			if (isalnum(c) || c == '_') {
1424 				*p++ = c;
1425 				continue;
1426 			}
1427 			*p = '\0';
1428 			lungetc(c);
1429 			break;
1430 		}
1431 		val = symget(buf);
1432 		if (val == NULL) {
1433 			yyerror("macro '%s' not defined", buf);
1434 			return (findeol());
1435 		}
1436 		p = val + strlen(val) - 1;
1437 		lungetc(DONE_EXPAND);
1438 		while (p >= val) {
1439 			lungetc((unsigned char)*p);
1440 			p--;
1441 		}
1442 		lungetc(START_EXPAND);
1443 		goto top;
1444 	}
1445 
1446 	switch (c) {
1447 	case '\'':
1448 	case '"':
1449 		quotec = c;
1450 		while (1) {
1451 			if ((c = lgetc(quotec)) == EOF)
1452 				return (0);
1453 			if (c == '\n') {
1454 				file->lineno++;
1455 				continue;
1456 			} else if (c == '\\') {
1457 				if ((next = lgetc(quotec)) == EOF)
1458 					return (0);
1459 				if (next == quotec || next == ' ' ||
1460 				    next == '\t')
1461 					c = next;
1462 				else if (next == '\n') {
1463 					file->lineno++;
1464 					continue;
1465 				} else
1466 					lungetc(next);
1467 			} else if (c == quotec) {
1468 				*p = '\0';
1469 				break;
1470 			} else if (c == '\0') {
1471 				yyerror("syntax error");
1472 				return (findeol());
1473 			}
1474 			if (p + 1 >= buf + sizeof(buf) - 1) {
1475 				yyerror("string too long");
1476 				return (findeol());
1477 			}
1478 			*p++ = c;
1479 		}
1480 		yylval.v.string = strdup(buf);
1481 		if (yylval.v.string == NULL)
1482 			err(1, "%s", __func__);
1483 		return (STRING);
1484 	}
1485 
1486 #define allowed_to_end_number(x) \
1487 	(isspace(x) || x == ')' || x ==',' || x == '/' || x == '}' || x == '=')
1488 
1489 	if (c == '-' || isdigit(c)) {
1490 		do {
1491 			*p++ = c;
1492 			if ((size_t)(p-buf) >= sizeof(buf)) {
1493 				yyerror("string too long");
1494 				return (findeol());
1495 			}
1496 		} while ((c = lgetc(0)) != EOF && isdigit(c));
1497 		lungetc(c);
1498 		if (p == buf + 1 && buf[0] == '-')
1499 			goto nodigits;
1500 		if (c == EOF || allowed_to_end_number(c)) {
1501 			const char *errstr = NULL;
1502 
1503 			*p = '\0';
1504 			yylval.v.number = strtonum(buf, LLONG_MIN,
1505 			    LLONG_MAX, &errstr);
1506 			if (errstr) {
1507 				yyerror("\"%s\" invalid number: %s",
1508 				    buf, errstr);
1509 				return (findeol());
1510 			}
1511 			return (NUMBER);
1512 		} else {
1513 nodigits:
1514 			while (p > buf + 1)
1515 				lungetc((unsigned char)*--p);
1516 			c = (unsigned char)*--p;
1517 			if (c == '-')
1518 				return (c);
1519 		}
1520 	}
1521 
1522 #define allowed_in_string(x) \
1523 	(isalnum(x) || (ispunct(x) && x != '(' && x != ')' && \
1524 	x != '{' && x != '}' && \
1525 	x != '!' && x != '=' && x != '#' && \
1526 	x != ','))
1527 
1528 	if (isalnum(c) || c == ':' || c == '_') {
1529 		do {
1530 			*p++ = c;
1531 			if ((size_t)(p-buf) >= sizeof(buf)) {
1532 				yyerror("string too long");
1533 				return (findeol());
1534 			}
1535 		} while ((c = lgetc(0)) != EOF && (allowed_in_string(c)));
1536 		lungetc(c);
1537 		*p = '\0';
1538 		if ((token = lookup(buf)) == STRING)
1539 			if ((yylval.v.string = strdup(buf)) == NULL)
1540 				err(1, "%s", __func__);
1541 		return (token);
1542 	}
1543 	if (c == '\n') {
1544 		yylval.lineno = file->lineno;
1545 		file->lineno++;
1546 	}
1547 	if (c == EOF)
1548 		return (0);
1549 	return (c);
1550 }
1551 
1552 int
1553 check_file_secrecy(int fd, const char *fname)
1554 {
1555 	struct stat	st;
1556 
1557 	if (fstat(fd, &st)) {
1558 		log_warn("cannot stat %s", fname);
1559 		return (-1);
1560 	}
1561 	if (st.st_uid != 0 && st.st_uid != getuid()) {
1562 		log_warnx("%s: owner not root or current user", fname);
1563 		return (-1);
1564 	}
1565 	if (st.st_mode & (S_IWGRP | S_IXGRP | S_IRWXO)) {
1566 		log_warnx("%s: group writable or world read/writable", fname);
1567 		return (-1);
1568 	}
1569 	return (0);
1570 }
1571 
1572 struct file *
1573 pushfile(const char *name, int secret)
1574 {
1575 	struct file	*nfile;
1576 
1577 	if ((nfile = calloc(1, sizeof(struct file))) == NULL) {
1578 		log_warn("%s", __func__);
1579 		return (NULL);
1580 	}
1581 	if ((nfile->name = strdup(name)) == NULL) {
1582 		log_warn("%s", __func__);
1583 		free(nfile);
1584 		return (NULL);
1585 	}
1586 	if ((nfile->stream = fopen(nfile->name, "r")) == NULL) {
1587 		log_warn("%s: %s", __func__, nfile->name);
1588 		free(nfile->name);
1589 		free(nfile);
1590 		return (NULL);
1591 	} else if (secret &&
1592 	    check_file_secrecy(fileno(nfile->stream), nfile->name)) {
1593 		fclose(nfile->stream);
1594 		free(nfile->name);
1595 		free(nfile);
1596 		return (NULL);
1597 	}
1598 	nfile->lineno = TAILQ_EMPTY(&files) ? 1 : 0;
1599 	nfile->ungetsize = 16;
1600 	nfile->ungetbuf = malloc(nfile->ungetsize);
1601 	if (nfile->ungetbuf == NULL) {
1602 		log_warn("%s", __func__);
1603 		fclose(nfile->stream);
1604 		free(nfile->name);
1605 		free(nfile);
1606 		return (NULL);
1607 	}
1608 	TAILQ_INSERT_TAIL(&files, nfile, entry);
1609 	return (nfile);
1610 }
1611 
1612 int
1613 popfile(void)
1614 {
1615 	struct file	*prev;
1616 
1617 	if ((prev = TAILQ_PREV(file, files, entry)) != NULL)
1618 		prev->errors += file->errors;
1619 
1620 	TAILQ_REMOVE(&files, file, entry);
1621 	fclose(file->stream);
1622 	free(file->name);
1623 	free(file->ungetbuf);
1624 	free(file);
1625 	file = prev;
1626 	return (file ? 0 : EOF);
1627 }
1628 
1629 struct snmpd *
1630 parse_config(const char *filename, u_int flags)
1631 {
1632 	struct sockaddr_storage ss;
1633 	struct utsname u;
1634 	struct sym	*sym, *next;
1635 	struct address	*h;
1636 	struct trap_address	*tr;
1637 	const struct usmuser	*up;
1638 	const char	*errstr;
1639 	char		 hostname[HOST_NAME_MAX + 1];
1640 	int		 found;
1641 	uint32_t	 npen = htonl(PEN_OPENBSD);
1642 
1643 	if ((conf = calloc(1, sizeof(*conf))) == NULL) {
1644 		log_warn("%s", __func__);
1645 		return (NULL);
1646 	}
1647 
1648 	conf->sc_system.sys_services = -1;
1649 	conf->sc_flags = flags;
1650 	conf->sc_confpath = filename;
1651 	TAILQ_INIT(&conf->sc_addresses);
1652 	TAILQ_INIT(&conf->sc_agentx_masters);
1653 	TAILQ_INIT(&conf->sc_trapreceivers);
1654 	conf->sc_min_seclevel = SNMP_MSGFLAG_AUTH | SNMP_MSGFLAG_PRIV;
1655 
1656 	if ((file = pushfile(filename, 0)) == NULL) {
1657 		free(conf);
1658 		return (NULL);
1659 	}
1660 	topfile = file;
1661 	setservent(1);
1662 
1663 	yyparse();
1664 	errors = file->errors;
1665 	popfile();
1666 
1667 	endservent();
1668 
1669 	if (uname(&u) == -1)
1670 		fatal("uname");
1671 
1672 	if (conf->sc_system.sys_descr[0] == '\0')
1673 		snprintf(conf->sc_system.sys_descr,
1674 		    sizeof(conf->sc_system.sys_descr), "%s %s %s %s %s",
1675 		    u.sysname, u.nodename, u.release, u.version, u.machine);
1676 	if (conf->sc_system.sys_oid.bo_n == 0)
1677 		conf->sc_system.sys_oid = OID(MIB_SYSOID_DEFAULT);
1678 	if (conf->sc_system.sys_contact[0] == '\0')
1679 		snprintf(conf->sc_system.sys_contact,
1680 		    sizeof(conf->sc_system.sys_contact), "root@%s", u.nodename);
1681 	if (conf->sc_system.sys_name[0] == '\0')
1682 		snprintf(conf->sc_system.sys_name,
1683 		    sizeof(conf->sc_system.sys_name), "%s", u.nodename);
1684 	if (conf->sc_system.sys_services == -1)
1685 		conf->sc_system.sys_services = 0;
1686 
1687 
1688 	/* Must be identical to enginefmt_local:HOSTHASH */
1689 	if (conf->sc_engineid_len == 0) {
1690 		if (gethostname(hostname, sizeof(hostname)) == -1)
1691 			fatal("gethostname");
1692 		memcpy(conf->sc_engineid, &npen, sizeof(npen));
1693 		conf->sc_engineid_len += sizeof(npen);
1694 		conf->sc_engineid[conf->sc_engineid_len++] |=
1695 		    SNMP_ENGINEID_FMT_HH;
1696 		memcpy(conf->sc_engineid + conf->sc_engineid_len,
1697 		    SHA256(hostname, strlen(hostname), NULL),
1698 		    sizeof(conf->sc_engineid) - conf->sc_engineid_len);
1699 		conf->sc_engineid_len = sizeof(conf->sc_engineid);
1700 		conf->sc_engineid[0] |= SNMP_ENGINEID_NEW;
1701 	}
1702 
1703 	/* Setup default listen addresses */
1704 	if (TAILQ_EMPTY(&conf->sc_addresses)) {
1705 		if (host("0.0.0.0", SNMP_PORT, SOCK_DGRAM, &ss, 1) != 1)
1706 			fatal("Unexpected resolving of 0.0.0.0");
1707 		if (listen_add(&ss, SOCK_DGRAM, 0) == -1)
1708 			fatal("calloc");
1709 		if (host("::", SNMP_PORT, SOCK_DGRAM, &ss, 1) != 1)
1710 			fatal("Unexpected resolving of ::");
1711 		if (listen_add(&ss, SOCK_DGRAM, 0) == -1)
1712 			fatal("calloc");
1713 	}
1714 
1715 	if ((up = usm_check_mincred(conf->sc_min_seclevel, &errstr)) != NULL)
1716 		fatalx("user '%s': %s", up->uu_name, errstr);
1717 
1718 	found = 0;
1719 	TAILQ_FOREACH(h, &conf->sc_addresses, entry) {
1720 		if (h->flags & ADDRESS_FLAG_NOTIFY)
1721 			found = 1;
1722 	}
1723 	if (conf->sc_traphandler && !found) {
1724 		log_warnx("trap handler needs at least one notify listener");
1725 		free(conf);
1726 		return (NULL);
1727 	}
1728 	if (!conf->sc_traphandler && found) {
1729 		log_warnx("notify listener needs at least one trap handler");
1730 		free(conf);
1731 		return (NULL);
1732 	}
1733 
1734 	TAILQ_FOREACH(tr, &conf->sc_trapreceivers, entry) {
1735 		if (tr->ta_version == SNMP_V2 &&
1736 		    tr->ta_community[0] == '\0' &&
1737 		    conf->sc_trcommunity[0] == '\0') {
1738 			log_warnx("trap receiver: missing community");
1739 			free(conf);
1740 			return (NULL);
1741 		}
1742 		if (tr->ta_version == SNMP_V3) {
1743 			tr->ta_usmuser = usm_finduser(tr->ta_usmusername);
1744 			if (tr->ta_usmuser == NULL) {
1745 				log_warnx("trap receiver: user not defined: %s",
1746 				    tr->ta_usmusername);
1747 				free(conf);
1748 				return (NULL);
1749 			}
1750 		}
1751 	}
1752 
1753 	/* Free macros and check which have not been used. */
1754 	TAILQ_FOREACH_SAFE(sym, &symhead, entry, next) {
1755 		if ((conf->sc_flags & SNMPD_F_VERBOSE) && !sym->used)
1756 			fprintf(stderr, "warning: macro '%s' not "
1757 			    "used\n", sym->nam);
1758 		if (!sym->persist) {
1759 			free(sym->nam);
1760 			free(sym->val);
1761 			TAILQ_REMOVE(&symhead, sym, entry);
1762 			free(sym);
1763 		}
1764 	}
1765 
1766 	if (errors) {
1767 		free(conf);
1768 		return (NULL);
1769 	}
1770 
1771 	return (conf);
1772 }
1773 
1774 int
1775 symset(const char *nam, const char *val, int persist)
1776 {
1777 	struct sym	*sym;
1778 
1779 	TAILQ_FOREACH(sym, &symhead, entry) {
1780 		if (strcmp(nam, sym->nam) == 0)
1781 			break;
1782 	}
1783 
1784 	if (sym != NULL) {
1785 		if (sym->persist == 1)
1786 			return (0);
1787 		else {
1788 			free(sym->nam);
1789 			free(sym->val);
1790 			TAILQ_REMOVE(&symhead, sym, entry);
1791 			free(sym);
1792 		}
1793 	}
1794 	if ((sym = calloc(1, sizeof(*sym))) == NULL)
1795 		return (-1);
1796 
1797 	sym->nam = strdup(nam);
1798 	if (sym->nam == NULL) {
1799 		free(sym);
1800 		return (-1);
1801 	}
1802 	sym->val = strdup(val);
1803 	if (sym->val == NULL) {
1804 		free(sym->nam);
1805 		free(sym);
1806 		return (-1);
1807 	}
1808 	sym->used = 0;
1809 	sym->persist = persist;
1810 	TAILQ_INSERT_TAIL(&symhead, sym, entry);
1811 	return (0);
1812 }
1813 
1814 int
1815 cmdline_symset(char *s)
1816 {
1817 	char	*sym, *val;
1818 	int	ret;
1819 
1820 	if ((val = strrchr(s, '=')) == NULL)
1821 		return (-1);
1822 	sym = strndup(s, val - s);
1823 	if (sym == NULL)
1824 		errx(1, "%s: strndup", __func__);
1825 	ret = symset(sym, val + 1, 1);
1826 	free(sym);
1827 
1828 	return (ret);
1829 }
1830 
1831 char *
1832 symget(const char *nam)
1833 {
1834 	struct sym	*sym;
1835 
1836 	TAILQ_FOREACH(sym, &symhead, entry) {
1837 		if (strcmp(nam, sym->nam) == 0) {
1838 			sym->used = 1;
1839 			return (sym->val);
1840 		}
1841 	}
1842 	return (NULL);
1843 }
1844 
1845 int
1846 host(const char *s, const char *port, int type, struct sockaddr_storage *ss,
1847     int max)
1848 {
1849 	struct addrinfo		 hints, *res0, *res;
1850 	int			 error, i;
1851 
1852 	bzero(&hints, sizeof(hints));
1853 	hints.ai_family = PF_UNSPEC;
1854 	hints.ai_socktype = type;
1855 	/*
1856 	 * Without AI_NUMERICHOST getaddrinfo might not resolve ip addresses
1857 	 * for families not specified in the "family" statement in resolv.conf.
1858 	 */
1859 	hints.ai_flags = AI_NUMERICHOST;
1860 	error = getaddrinfo(s, port, &hints, &res0);
1861 	if (error == EAI_NONAME) {
1862 		hints.ai_flags = 0;
1863 		error = getaddrinfo(s, port, &hints, &res0);
1864 	}
1865 	if (error == EAI_AGAIN || error == EAI_NODATA || error == EAI_NONAME)
1866 		return 0;
1867 	if (error) {
1868 		log_warnx("Could not parse \"%s\": %s", s, gai_strerror(error));
1869 		return -1;
1870 	}
1871 
1872 	for (i = 0, res = res0; res; res = res->ai_next, i++) {
1873 		if (res->ai_family != AF_INET &&
1874 		    res->ai_family != AF_INET6)
1875 			continue;
1876 		if (i >= max)
1877 			continue;
1878 
1879 		bcopy(res->ai_addr, &(ss[i]), res->ai_addrlen);
1880 	}
1881 	freeaddrinfo(res0);
1882 
1883 	return i;
1884 }
1885 
1886 int
1887 listen_add(struct sockaddr_storage *ss, int type, int flags)
1888 {
1889 	struct sockaddr_in *sin;
1890 	struct sockaddr_in6 *sin6;
1891 	struct address *h;
1892 
1893 	if ((h = calloc(1, sizeof(*h))) == NULL)
1894 		return -1;
1895 	bcopy(ss, &(h->ss), sizeof(*ss));
1896 	if (ss->ss_family == AF_INET) {
1897 		sin = (struct sockaddr_in *)ss;
1898 		h->port = ntohs(sin->sin_port);
1899 	} else {
1900 		sin6 = (struct sockaddr_in6*)ss;
1901 		h->port = ntohs(sin6->sin6_port);
1902 	}
1903 	h->type = type;
1904 	if (((h->flags = flags) & ADDRESS_FLAG_PERM) == 0) {
1905 		if (h->port == 162)
1906 			h->flags |= ADDRESS_FLAG_NOTIFY;
1907 		else
1908 			h->flags |= ADDRESS_FLAG_READ | ADDRESS_FLAG_WRITE;
1909 	}
1910 	if ((h->flags & ADDRESS_FLAG_MPS) == 0)
1911 		h->flags |= ADDRESS_FLAG_SNMPV3;
1912 	TAILQ_INSERT_TAIL(&(conf->sc_addresses), h, entry);
1913 
1914 	return 0;
1915 }
1916