GCC Code Coverage Report
Directory: ./ Exec Total Coverage
File: usr.sbin/snmpd/parse.y Lines: 232 397 58.4 %
Date: 2017-11-07 Branches: 133 275 48.4 %

Line Branch Exec Source
1
/*	$OpenBSD: parse.y,v 1.45 2017/08/20 07:03:45 tb 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
		    struct address *);
103
int		 host(const char *, struct addresslist *,
104
		    int, in_port_t, struct ber_oid *, char *, char *);
105
106
typedef struct {
107
	union {
108
		int64_t		 number;
109
		char		*string;
110
		struct host	*host;
111
		struct timeval	 tv;
112
		struct ber_oid	*oid;
113
		struct {
114
			int		 type;
115
			void		*data;
116
			long long	 value;
117
		}		 data;
118
		enum usmauth	 auth;
119
		enum usmpriv	 enc;
120
	} v;
121
	int lineno;
122
} YYSTYPE;
123
124
%}
125
126
%token	INCLUDE
127
%token  LISTEN ON
128
%token	SYSTEM CONTACT DESCR LOCATION NAME OBJECTID SERVICES RTFILTER
129
%token	READONLY READWRITE OCTETSTRING INTEGER COMMUNITY TRAP RECEIVER
130
%token	SECLEVEL NONE AUTH ENC USER AUTHKEY ENCKEY ERROR DISABLED
131
%token	SOCKET RESTRICTED AGENTX HANDLE DEFAULT SRCADDR
132
%token	<v.string>	STRING
133
%token  <v.number>	NUMBER
134
%type	<v.string>	hostcmn
135
%type	<v.string>	srcaddr
136
%type	<v.number>	optwrite yesno seclevel socktype
137
%type	<v.data>	objtype cmd
138
%type	<v.oid>		oid hostoid trapoid
139
%type	<v.auth>	auth
140
%type	<v.enc>		enc
141
142
%%
143
144
grammar		: /* empty */
145
		| grammar include '\n'
146
		| grammar '\n'
147
		| grammar varset '\n'
148
		| grammar main '\n'
149
		| grammar system '\n'
150
		| grammar mib '\n'
151
		| grammar error '\n'		{ file->errors++; }
152
		;
153
154
include		: INCLUDE STRING		{
155
			struct file	*nfile;
156
157
			if ((nfile = pushfile($2, 0)) == NULL) {
158
				yyerror("failed to include file %s", $2);
159
				free($2);
160
				YYERROR;
161
			}
162
			free($2);
163
164
			file = nfile;
165
			lungetc('\n');
166
		}
167
		;
168
169
varset		: STRING '=' STRING	{
170
			char *s = $1;
171
			while (*s++) {
172
				if (isspace((unsigned char)*s)) {
173
					yyerror("macro name cannot contain "
174
					    "whitespace");
175
					YYERROR;
176
				}
177
			}
178
			if (symset($1, $3, 0) == -1)
179
				fatal("cannot store variable");
180
			free($1);
181
			free($3);
182
		}
183
		;
184
185
yesno		:  STRING			{
186
			if (!strcmp($1, "yes"))
187
				$$ = 1;
188
			else if (!strcmp($1, "no"))
189
				$$ = 0;
190
			else {
191
				yyerror("syntax error, "
192
				    "either yes or no expected");
193
				free($1);
194
				YYERROR;
195
			}
196
			free($1);
197
		}
198
		;
199
200
main		: LISTEN ON STRING		{
201
			if (host($3, &conf->sc_addresses, 16, SNMPD_PORT, NULL,
202
			    NULL, NULL) <= 0) {
203
				yyerror("invalid ip address: %s", $3);
204
				free($3);
205
				YYERROR;
206
			}
207
			free($3);
208
		}
209
		| READONLY COMMUNITY STRING	{
210
			if (strlcpy(conf->sc_rdcommunity, $3,
211
			    sizeof(conf->sc_rdcommunity)) >=
212
			    sizeof(conf->sc_rdcommunity)) {
213
				yyerror("r/o community name too long");
214
				free($3);
215
				YYERROR;
216
			}
217
			free($3);
218
		}
219
		| READWRITE COMMUNITY STRING	{
220
			if (strlcpy(conf->sc_rwcommunity, $3,
221
			    sizeof(conf->sc_rwcommunity)) >=
222
			    sizeof(conf->sc_rwcommunity)) {
223
				yyerror("r/w community name too long");
224
				free($3);
225
				YYERROR;
226
			}
227
			free($3);
228
		}
229
		| READWRITE DISABLED {
230
			conf->sc_readonly = 1;
231
 		}
232
		| TRAP COMMUNITY STRING		{
233
			if (strlcpy(conf->sc_trcommunity, $3,
234
			    sizeof(conf->sc_trcommunity)) >=
235
			    sizeof(conf->sc_trcommunity)) {
236
				yyerror("trap community name too long");
237
				free($3);
238
				YYERROR;
239
			}
240
			free($3);
241
		}
242
		| TRAP RECEIVER			{
243
			hlist = &conf->sc_trapreceivers;
244
		} host				{
245
			hlist = NULL;
246
		}
247
		| TRAP HANDLE hostcmn trapoid cmd {
248
			struct trapcmd *cmd = $5.data;
249
250
			cmd->cmd_oid = $4;
251
252
			if (trapcmd_add(cmd) != 0) {
253
				free($4);
254
				free(cmd);
255
				yyerror("duplicate oid");
256
				YYERROR;
257
			}
258
			conf->sc_traphandler = 1;
259
		}
260
		| RTFILTER yesno		{
261
			if ($2 == 1)
262
				conf->sc_rtfilter = ROUTE_FILTER(RTM_NEWADDR) |
263
				    ROUTE_FILTER(RTM_DELADDR) |
264
				    ROUTE_FILTER(RTM_IFINFO) |
265
				    ROUTE_FILTER(RTM_IFANNOUNCE);
266
			else
267
				conf->sc_rtfilter = 0;
268
		}
269
		| SECLEVEL seclevel {
270
			conf->sc_min_seclevel = $2;
271
		}
272
		| USER STRING			{
273
			const char *errstr;
274
			user = usm_newuser($2, &errstr);
275
			if (user == NULL) {
276
				yyerror("%s", errstr);
277
				free($2);
278
				YYERROR;
279
			}
280
		} userspecs {
281
			const char *errstr;
282
			if (usm_checkuser(user, &errstr) < 0) {
283
				yyerror("%s", errstr);
284
				YYERROR;
285
			}
286
			user = NULL;
287
		}
288
		| SOCKET STRING socktype {
289
			if ($3) {
290
				struct control_sock *rcsock;
291
292
				rcsock = calloc(1, sizeof(*rcsock));
293
				if (rcsock == NULL) {
294
					yyerror("calloc");
295
					YYERROR;
296
				}
297
				rcsock->cs_name = $2;
298
				if ($3 == SOCK_TYPE_RESTRICTED)
299
					rcsock->cs_restricted = 1;
300
				else if ($3 == SOCK_TYPE_AGENTX)
301
					rcsock->cs_agentx = 1;
302
				TAILQ_INSERT_TAIL(&conf->sc_ps.ps_rcsocks,
303
				    rcsock, cs_entry);
304
			} else {
305
				if (++nctlsocks > 1) {
306
					yyerror("multiple control "
307
					    "sockets specified");
308
					YYERROR;
309
				}
310
				conf->sc_ps.ps_csock.cs_name = $2;
311
			}
312
		}
313
		;
314
315
system		: SYSTEM sysmib
316
		;
317
318
sysmib		: CONTACT STRING		{
319
			struct ber_oid	 o = OID(MIB_sysContact);
320
			mps_set(&o, $2, strlen($2));
321
		}
322
		| DESCR STRING			{
323
			struct ber_oid	 o = OID(MIB_sysDescr);
324
			mps_set(&o, $2, strlen($2));
325
		}
326
		| LOCATION STRING		{
327
			struct ber_oid	 o = OID(MIB_sysLocation);
328
			mps_set(&o, $2, strlen($2));
329
		}
330
		| NAME STRING			{
331
			struct ber_oid	 o = OID(MIB_sysName);
332
			mps_set(&o, $2, strlen($2));
333
		}
334
		| OBJECTID oid			{
335
			struct ber_oid	 o = OID(MIB_sysOID);
336
			mps_set(&o, $2, sizeof(struct ber_oid));
337
		}
338
		| SERVICES NUMBER		{
339
			struct ber_oid	 o = OID(MIB_sysServices);
340
			mps_set(&o, NULL, $2);
341
		}
342
		;
343
344
mib		: OBJECTID oid NAME STRING optwrite objtype	{
345
			struct oid	*oid;
346
			if ((oid = (struct oid *)
347
			    calloc(1, sizeof(*oid))) == NULL) {
348
				yyerror("calloc");
349
				free($2);
350
				free($6.data);
351
				YYERROR;
352
			}
353
354
			smi_oidlen($2);
355
			bcopy($2, &oid->o_id, sizeof(struct ber_oid));
356
			free($2);
357
			oid->o_name = $4;
358
			oid->o_data = $6.data;
359
			oid->o_val = $6.value;
360
			switch ($6.type) {
361
			case 1:
362
				oid->o_get = mps_getint;
363
				oid->o_set = mps_setint;
364
				break;
365
			case 2:
366
				oid->o_get = mps_getstr;
367
				oid->o_set = mps_setstr;
368
				break;
369
			}
370
			oid->o_flags = OID_RD|OID_DYNAMIC;
371
			if ($5)
372
				oid->o_flags |= OID_WR;
373
374
			if (smi_insert(oid) == -1) {
375
				yyerror("duplicate oid");
376
				free(oid->o_name);
377
				free(oid->o_data);
378
				YYERROR;
379
			}
380
		}
381
		;
382
383
objtype		: INTEGER NUMBER			{
384
			$$.type = 1;
385
			$$.data = NULL;
386
			$$.value = $2;
387
		}
388
		| OCTETSTRING STRING			{
389
			$$.type = 2;
390
			$$.data = $2;
391
			$$.value = strlen($2);
392
		}
393
		;
394
395
optwrite	: READONLY				{ $$ = 0; }
396
		| READWRITE				{ $$ = 1; }
397
		;
398
399
oid		: STRING				{
400
			struct ber_oid	*sysoid;
401
			if ((sysoid =
402
			    calloc(1, sizeof(*sysoid))) == NULL) {
403
				yyerror("calloc");
404
				free($1);
405
				YYERROR;
406
			}
407
			if (ber_string2oid($1, sysoid) == -1) {
408
				yyerror("invalid OID: %s", $1);
409
				free(sysoid);
410
				free($1);
411
				YYERROR;
412
			}
413
			free($1);
414
			$$ = sysoid;
415
		}
416
		;
417
418
trapoid		: oid					{ $$ = $1; }
419
		| DEFAULT				{
420
			struct ber_oid	*sysoid;
421
			if ((sysoid =
422
			    calloc(1, sizeof(*sysoid))) == NULL) {
423
				yyerror("calloc");
424
				YYERROR;
425
			}
426
			ber_string2oid("1.3", sysoid);
427
			$$ = sysoid;
428
		}
429
		;
430
431
hostoid		: /* empty */				{ $$ = NULL; }
432
		| OBJECTID oid				{ $$ = $2; }
433
		;
434
435
hostcmn		: /* empty */				{ $$ = NULL; }
436
		| COMMUNITY STRING			{ $$ = $2; }
437
		;
438
439
srcaddr		: /* empty */				{ $$ = NULL; }
440
		| SRCADDR STRING			{ $$ = $2; }
441
		;
442
443
hostdef		: STRING hostoid hostcmn srcaddr	{
444
			if (host($1, hlist, 1,
445
			    SNMPD_TRAPPORT, $2, $3, $4) <= 0) {
446
				yyerror("invalid host: %s", $1);
447
				free($1);
448
				YYERROR;
449
			}
450
			free($1);
451
		}
452
		;
453
454
hostlist	: /* empty */
455
		| hostlist comma hostdef
456
		;
457
458
host		: hostdef
459
		| '{' hostlist '}'
460
		;
461
462
comma		: /* empty */
463
		| ','
464
		;
465
466
seclevel	: NONE		{ $$ = 0; }
467
		| AUTH		{ $$ = SNMP_MSGFLAG_AUTH; }
468
		| ENC		{ $$ = SNMP_MSGFLAG_AUTH | SNMP_MSGFLAG_PRIV; }
469
		;
470
471
userspecs	: /* empty */
472
		| userspecs userspec
473
		;
474
475
userspec	: AUTHKEY STRING		{
476
			user->uu_authkey = $2;
477
		}
478
		| AUTH auth			{
479
			user->uu_auth = $2;
480
		}
481
		| ENCKEY STRING			{
482
			user->uu_privkey = $2;
483
		}
484
		| ENC enc			{
485
			user->uu_priv = $2;
486
		}
487
		;
488
489
auth		: STRING			{
490
			if (strcasecmp($1, "hmac-md5") == 0 ||
491
			    strcasecmp($1, "hmac-md5-96") == 0)
492
				$$ = AUTH_MD5;
493
			else if (strcasecmp($1, "hmac-sha1") == 0 ||
494
			     strcasecmp($1, "hmac-sha1-96") == 0)
495
				$$ = AUTH_SHA1;
496
			else {
497
				yyerror("syntax error, bad auth hmac");
498
				free($1);
499
				YYERROR;
500
			}
501
			free($1);
502
		}
503
		;
504
505
enc		: STRING			{
506
			if (strcasecmp($1, "des") == 0 ||
507
			    strcasecmp($1, "cbc-des") == 0)
508
				$$ = PRIV_DES;
509
			else if (strcasecmp($1, "aes") == 0 ||
510
			    strcasecmp($1, "cfb128-aes-128") == 0)
511
				$$ = PRIV_AES;
512
			else {
513
				yyerror("syntax error, bad encryption cipher");
514
				free($1);
515
				YYERROR;
516
			}
517
			free($1);
518
519
		}
520
		;
521
522
socktype	: RESTRICTED		{ $$ = SOCK_TYPE_RESTRICTED; }
523
		| AGENTX		{ $$ = SOCK_TYPE_AGENTX; }
524
		| /* nothing */		{ $$ = 0; }
525
		;
526
527
cmd		: STRING		{
528
			struct trapcmd	*cmd;
529
			size_t		 span, limit;
530
			char		*pos, **args, **args2;
531
			int		 nargs = 32;		/* XXX */
532
533
			if ((cmd = calloc(1, sizeof(*cmd))) == NULL ||
534
			    (args = calloc(nargs, sizeof(char *))) == NULL) {
535
				free(cmd);
536
				free($1);
537
				YYERROR;
538
			}
539
540
			pos = $1;
541
			limit = strlen($1);
542
543
			while (pos < $1 + limit &&
544
			    (span = strcspn(pos, " \t")) != 0) {
545
				pos[span] = '\0';
546
				args[cmd->cmd_argc] = strdup(pos);
547
				if (args[cmd->cmd_argc] == NULL) {
548
					trapcmd_free(cmd);
549
					free(args);
550
					free($1);
551
					YYERROR;
552
				}
553
				cmd->cmd_argc++;
554
				if (cmd->cmd_argc >= nargs - 1) {
555
					nargs *= 2;
556
					args2 = calloc(nargs, sizeof(char *));
557
					if (args2 == NULL) {
558
						trapcmd_free(cmd);
559
						free(args);
560
						free($1);
561
						YYERROR;
562
					}
563
					args = args2;
564
				}
565
				pos += span + 1;
566
			}
567
			free($1);
568
			cmd->cmd_argv = args;
569
			$$.data = cmd;
570
		}
571
		;
572
573
%%
574
575
struct keywords {
576
	const char	*k_name;
577
	int		 k_val;
578
};
579
580
int
581
yyerror(const char *fmt, ...)
582
{
583
	va_list		 ap;
584
	char		*msg;
585
586
	file->errors++;
587
	va_start(ap, fmt);
588
	if (vasprintf(&msg, fmt, ap) == -1)
589
		fatalx("yyerror vasprintf");
590
	va_end(ap);
591
	logit(LOG_CRIT, "%s:%d: %s", file->name, yylval.lineno, msg);
592
	free(msg);
593
	return (0);
594
}
595
596
int
597
kw_cmp(const void *k, const void *e)
598
{
599
1792
	return (strcmp(k, ((const struct keywords *)e)->k_name));
600
}
601
602
int
603
lookup(char *s)
604
{
605
	/* this has to be sorted always */
606
	static const struct keywords keywords[] = {
607
		{ "agentx",		AGENTX },
608
		{ "auth",		AUTH },
609
		{ "authkey",		AUTHKEY },
610
		{ "community",		COMMUNITY },
611
		{ "contact",		CONTACT },
612
		{ "default",		DEFAULT },
613
		{ "description",	DESCR },
614
		{ "disabled",		DISABLED},
615
		{ "enc",		ENC },
616
		{ "enckey",		ENCKEY },
617
		{ "filter-routes",	RTFILTER },
618
		{ "handle",		HANDLE },
619
		{ "include",		INCLUDE },
620
		{ "integer",		INTEGER },
621
		{ "listen",		LISTEN },
622
		{ "location",		LOCATION },
623
		{ "name",		NAME },
624
		{ "none",		NONE },
625
		{ "oid",		OBJECTID },
626
		{ "on",			ON },
627
		{ "read-only",		READONLY },
628
		{ "read-write",		READWRITE },
629
		{ "receiver",		RECEIVER },
630
		{ "restricted",		RESTRICTED },
631
		{ "seclevel",		SECLEVEL },
632
		{ "services",		SERVICES },
633
		{ "socket",		SOCKET },
634
		{ "source-address",	SRCADDR },
635
		{ "string",		OCTETSTRING },
636
		{ "system",		SYSTEM },
637
		{ "trap",		TRAP },
638
		{ "user",		USER }
639
	};
640
	const struct keywords	*p;
641
642
408
	p = bsearch(s, keywords, sizeof(keywords)/sizeof(keywords[0]),
643
	    sizeof(keywords[0]), kw_cmp);
644
645
204
	if (p)
646
136
		return (p->k_val);
647
	else
648
68
		return (STRING);
649
204
}
650
651
#define MAXPUSHBACK	128
652
653
u_char	*parsebuf;
654
int	 parseindex;
655
u_char	 pushback_buffer[MAXPUSHBACK];
656
int	 pushback_index = 0;
657
658
int
659
lgetc(int quotec)
660
{
661
	int		c, next;
662
663
9432
	if (parsebuf) {
664
		/* Read character from the parsebuffer instead of input. */
665
208
		if (parseindex >= 0) {
666
208
			c = parsebuf[parseindex++];
667
208
			if (c != '\0')
668
192
				return (c);
669
16
			parsebuf = NULL;
670
16
		} else
671
			parseindex++;
672
	}
673
674
4524
	if (pushback_index)
675
236
		return (pushback_buffer[--pushback_index]);
676
677
4288
	if (quotec) {
678

2176
		if ((c = getc(file->stream)) == EOF) {
679
			yyerror("reached end of file while parsing quoted string");
680
			if (file == topfile || popfile() == EOF)
681
				return (EOF);
682
			return (quotec);
683
		}
684
544
		return (c);
685
	}
686
687

14976
	while ((c = getc(file->stream)) == '\\') {
688
		next = getc(file->stream);
689
		if (next != '\n') {
690
			c = next;
691
			break;
692
		}
693
		yylval.lineno = file->lineno;
694
		file->lineno++;
695
	}
696
3744
	if (c == '\t' || c == ' ') {
697
		/* Compress blanks to a single space. */
698
		do {
699

1776
			c = getc(file->stream);
700
444
		} while (c == '\t' || c == ' ');
701
444
		ungetc(c, file->stream);
702
		c = ' ';
703
444
	}
704
705
3744
	while (c == EOF) {
706

16
		if (file == topfile || popfile() == EOF)
707
16
			return (EOF);
708
		c = getc(file->stream);
709
	}
710
3728
	return (c);
711
4716
}
712
713
int
714
lungetc(int c)
715
{
716
568
	if (c == EOF)
717
		return (EOF);
718
284
	if (parsebuf) {
719
48
		parseindex--;
720
48
		if (parseindex >= 0)
721
48
			return (c);
722
	}
723
236
	if (pushback_index < MAXPUSHBACK-1)
724
236
		return (pushback_buffer[pushback_index++] = c);
725
	else
726
		return (EOF);
727
284
}
728
729
int
730
findeol(void)
731
{
732
	int	c;
733
734
	parsebuf = NULL;
735
736
	/* skip to either EOF or the first real EOL */
737
	while (1) {
738
		if (pushback_index)
739
			c = pushback_buffer[--pushback_index];
740
		else
741
			c = lgetc(0);
742
		if (c == '\n') {
743
			file->lineno++;
744
			break;
745
		}
746
		if (c == EOF)
747
			break;
748
	}
749
	return (ERROR);
750
}
751
752
int
753
yylex(void)
754
{
755
888
	u_char	 buf[8096];
756
	u_char	*p, *val;
757
	int	 quotec, next, c;
758
444
	int	 token;
759
760
top:
761
460
	p = buf;
762
1084
	while ((c = lgetc(0)) == ' ' || c == '\t')
763
		; /* nothing */
764
765
460
	yylval.lineno = file->lineno;
766
460
	if (c == '#')
767
1792
		while ((c = lgetc(0)) != '\n' && c != EOF)
768
			; /* nothing */
769
460
	if (c == '$' && parsebuf == NULL) {
770
		while (1) {
771
192
			if ((c = lgetc(0)) == EOF)
772
				return (0);
773
774
192
			if (p + 1 >= buf + sizeof(buf) - 1) {
775
				yyerror("string too long");
776
				return (findeol());
777
			}
778
192
			if (isalnum(c) || c == '_') {
779
176
				*p++ = c;
780
176
				continue;
781
			}
782
16
			*p = '\0';
783
16
			lungetc(c);
784
			break;
785
		}
786
16
		val = symget(buf);
787
16
		if (val == NULL) {
788
			yyerror("macro '%s' not defined", buf);
789
			return (findeol());
790
		}
791
16
		parsebuf = val;
792
16
		parseindex = 0;
793
16
		goto top;
794
	}
795
796
444
	switch (c) {
797
	case '\'':
798
	case '"':
799
		quotec = c;
800
544
		while (1) {
801
544
			if ((c = lgetc(quotec)) == EOF)
802
				return (0);
803
544
			if (c == '\n') {
804
				file->lineno++;
805
				continue;
806
544
			} else if (c == '\\') {
807
				if ((next = lgetc(quotec)) == EOF)
808
					return (0);
809
				if (next == quotec || c == ' ' || c == '\t')
810
					c = next;
811
				else if (next == '\n') {
812
					file->lineno++;
813
					continue;
814
				} else
815
					lungetc(next);
816
544
			} else if (c == quotec) {
817
44
				*p = '\0';
818
				break;
819
500
			} else if (c == '\0') {
820
				yyerror("syntax error");
821
				return (findeol());
822
			}
823
500
			if (p + 1 >= buf + sizeof(buf) - 1) {
824
				yyerror("string too long");
825
				return (findeol());
826
			}
827
500
			*p++ = c;
828
		}
829
44
		yylval.v.string = strdup(buf);
830
44
		if (yylval.v.string == NULL)
831
			err(1, "yylex: strdup");
832
44
		return (STRING);
833
	}
834
835
#define allowed_to_end_number(x) \
836
	(isspace(x) || x == ')' || x ==',' || x == '/' || x == '}' || x == '=')
837
838

800
	if (c == '-' || isdigit(c)) {
839
		do {
840
64
			*p++ = c;
841
64
			if ((unsigned)(p-buf) >= sizeof(buf)) {
842
				yyerror("string too long");
843
				return (findeol());
844
			}
845

128
		} while ((c = lgetc(0)) != EOF && isdigit(c));
846
32
		lungetc(c);
847

48
		if (p == buf + 1 && buf[0] == '-')
848
			goto nodigits;
849

64
		if (c == EOF || allowed_to_end_number(c)) {
850
4
			const char *errstr = NULL;
851
852
4
			*p = '\0';
853
4
			yylval.v.number = strtonum(buf, LLONG_MIN,
854
			    LLONG_MAX, &errstr);
855
4
			if (errstr) {
856
				yyerror("\"%s\" invalid number: %s",
857
				    buf, errstr);
858
				return (findeol());
859
			}
860
4
			return (NUMBER);
861
4
		} else {
862
nodigits:
863
92
			while (p > buf + 1)
864
32
				lungetc(*--p);
865
			c = *--p;
866
28
			if (c == '-')
867
				return (c);
868
		}
869
	}
870
871
#define allowed_in_string(x) \
872
	(isalnum(x) || (ispunct(x) && x != '(' && x != ')' && \
873
	x != '{' && x != '}' && \
874
	x != '!' && x != '=' && x != '#' && \
875
	x != ','))
876
877
396
	if (isalnum(c) || c == ':' || c == '_') {
878
		do {
879
1500
			*p++ = c;
880
1500
			if ((unsigned)(p-buf) >= sizeof(buf)) {
881
				yyerror("string too long");
882
				return (findeol());
883
			}
884

3376
		} while ((c = lgetc(0)) != EOF && (allowed_in_string(c)));
885
204
		lungetc(c);
886
204
		*p = '\0';
887
204
		if ((token = lookup(buf)) == STRING)
888
68
			if ((yylval.v.string = strdup(buf)) == NULL)
889
				err(1, "yylex: strdup");
890
204
		return (token);
891
	}
892
192
	if (c == '\n') {
893
160
		yylval.lineno = file->lineno;
894
160
		file->lineno++;
895
160
	}
896
192
	if (c == EOF)
897
16
		return (0);
898
176
	return (c);
899
444
}
900
901
int
902
check_file_secrecy(int fd, const char *fname)
903
{
904
	struct stat	st;
905
906
	if (fstat(fd, &st)) {
907
		log_warn("cannot stat %s", fname);
908
		return (-1);
909
	}
910
	if (st.st_uid != 0 && st.st_uid != getuid()) {
911
		log_warnx("%s: owner not root or current user", fname);
912
		return (-1);
913
	}
914
	if (st.st_mode & (S_IWGRP | S_IXGRP | S_IRWXO)) {
915
		log_warnx("%s: group writable or world read/writable", fname);
916
		return (-1);
917
	}
918
	return (0);
919
}
920
921
struct file *
922
pushfile(const char *name, int secret)
923
{
924
	struct file	*nfile;
925
926
32
	if ((nfile = calloc(1, sizeof(struct file))) == NULL) {
927
		log_warn("malloc");
928
		return (NULL);
929
	}
930
16
	if ((nfile->name = strdup(name)) == NULL) {
931
		log_warn("malloc");
932
		free(nfile);
933
		return (NULL);
934
	}
935
16
	if ((nfile->stream = fopen(nfile->name, "r")) == NULL) {
936
		log_warn("%s", nfile->name);
937
		free(nfile->name);
938
		free(nfile);
939
		return (NULL);
940

16
	} else if (secret &&
941
	    check_file_secrecy(fileno(nfile->stream), nfile->name)) {
942
		fclose(nfile->stream);
943
		free(nfile->name);
944
		free(nfile);
945
		return (NULL);
946
	}
947
16
	nfile->lineno = 1;
948
16
	TAILQ_INSERT_TAIL(&files, nfile, entry);
949
16
	return (nfile);
950
16
}
951
952
int
953
popfile(void)
954
{
955
	struct file	*prev;
956
957
32
	if ((prev = TAILQ_PREV(file, files, entry)) != NULL)
958
		prev->errors += file->errors;
959
960
32
	TAILQ_REMOVE(&files, file, entry);
961
16
	fclose(file->stream);
962
16
	free(file->name);
963
16
	free(file);
964
16
	file = prev;
965
16
	return (file ? 0 : EOF);
966
}
967
968
struct snmpd *
969
parse_config(const char *filename, u_int flags)
970
{
971
	struct sym	*sym, *next;
972
973
32
	if ((conf = calloc(1, sizeof(*conf))) == NULL) {
974
		log_warn("cannot allocate memory");
975
		return (NULL);
976
	}
977
978
16
	conf->sc_flags = flags;
979
16
	conf->sc_confpath = filename;
980
16
	TAILQ_INIT(&conf->sc_addresses);
981
16
	TAILQ_INIT(&conf->sc_sockets);
982
16
	conf->sc_ps.ps_csock.cs_name = SNMPD_SOCKET;
983
16
	TAILQ_INIT(&conf->sc_ps.ps_rcsocks);
984
16
	strlcpy(conf->sc_rdcommunity, "public", SNMPD_MAXCOMMUNITYLEN);
985
16
	strlcpy(conf->sc_rwcommunity, "private", SNMPD_MAXCOMMUNITYLEN);
986
16
	strlcpy(conf->sc_trcommunity, "public", SNMPD_MAXCOMMUNITYLEN);
987
16
	TAILQ_INIT(&conf->sc_trapreceivers);
988
989
16
	if ((file = pushfile(filename, 0)) == NULL) {
990
		free(conf);
991
		return (NULL);
992
	}
993
16
	topfile = file;
994
16
	setservent(1);
995
996
16
	yyparse();
997
16
	errors = file->errors;
998
16
	popfile();
999
1000
16
	endservent();
1001
1002
16
	if (TAILQ_EMPTY(&conf->sc_addresses)) {
1003
		struct address		*h;
1004
		if ((h = calloc(1, sizeof(*h))) == NULL)
1005
			fatal("snmpe: %s", __func__);
1006
		h->ss.ss_family = AF_INET;
1007
		h->port = SNMPD_PORT;
1008
		TAILQ_INSERT_TAIL(&conf->sc_addresses, h, entry);
1009
		if ((h = calloc(1, sizeof(*h))) == NULL)
1010
			fatal("snmpe: %s", __func__);
1011
		h->ss.ss_family = AF_INET6;
1012
		h->port = SNMPD_PORT;
1013
		TAILQ_INSERT_TAIL(&conf->sc_addresses, h, entry);
1014
	}
1015
1016
	/* Free macros and check which have not been used. */
1017
80
	TAILQ_FOREACH_SAFE(sym, &symhead, entry, next) {
1018

32
		if ((conf->sc_flags & SNMPD_F_VERBOSE) && !sym->used)
1019
			fprintf(stderr, "warning: macro '%s' not "
1020
			    "used\n", sym->nam);
1021
16
		if (!sym->persist) {
1022
16
			free(sym->nam);
1023
16
			free(sym->val);
1024
32
			TAILQ_REMOVE(&symhead, sym, entry);
1025
16
			free(sym);
1026
16
		}
1027
	}
1028
1029
16
	if (errors) {
1030
		free(conf);
1031
		return (NULL);
1032
	}
1033
1034
16
	return (conf);
1035
16
}
1036
1037
int
1038
symset(const char *nam, const char *val, int persist)
1039
{
1040
	struct sym	*sym;
1041
1042
48
	TAILQ_FOREACH(sym, &symhead, entry) {
1043
		if (strcmp(nam, sym->nam) == 0)
1044
			break;
1045
	}
1046
1047
16
	if (sym != NULL) {
1048
		if (sym->persist == 1)
1049
			return (0);
1050
		else {
1051
			free(sym->nam);
1052
			free(sym->val);
1053
			TAILQ_REMOVE(&symhead, sym, entry);
1054
			free(sym);
1055
		}
1056
	}
1057
16
	if ((sym = calloc(1, sizeof(*sym))) == NULL)
1058
		return (-1);
1059
1060
16
	sym->nam = strdup(nam);
1061
16
	if (sym->nam == NULL) {
1062
		free(sym);
1063
		return (-1);
1064
	}
1065
16
	sym->val = strdup(val);
1066
16
	if (sym->val == NULL) {
1067
		free(sym->nam);
1068
		free(sym);
1069
		return (-1);
1070
	}
1071
16
	sym->used = 0;
1072
16
	sym->persist = persist;
1073
16
	TAILQ_INSERT_TAIL(&symhead, sym, entry);
1074
16
	return (0);
1075
16
}
1076
1077
int
1078
cmdline_symset(char *s)
1079
{
1080
	char	*sym, *val;
1081
	int	ret;
1082
	size_t	len;
1083
1084
	if ((val = strrchr(s, '=')) == NULL)
1085
		return (-1);
1086
1087
	len = strlen(s) - strlen(val) + 1;
1088
	if ((sym = malloc(len)) == NULL)
1089
		errx(1, "cmdline_symset: malloc");
1090
1091
	(void)strlcpy(sym, s, len);
1092
1093
	ret = symset(sym, val + 1, 1);
1094
	free(sym);
1095
1096
	return (ret);
1097
}
1098
1099
char *
1100
symget(const char *nam)
1101
{
1102
	struct sym	*sym;
1103
1104
48
	TAILQ_FOREACH(sym, &symhead, entry) {
1105
16
		if (strcmp(nam, sym->nam) == 0) {
1106
16
			sym->used = 1;
1107
16
			return (sym->val);
1108
		}
1109
	}
1110
	return (NULL);
1111
16
}
1112
1113
struct address *
1114
host_v4(const char *s)
1115
{
1116
40
	struct in_addr		 ina;
1117
	struct sockaddr_in	*sain;
1118
	struct address		*h;
1119
1120
20
	bzero(&ina, sizeof(ina));
1121
20
	if (inet_pton(AF_INET, s, &ina) != 1)
1122
4
		return (NULL);
1123
1124
16
	if ((h = calloc(1, sizeof(*h))) == NULL)
1125
		fatal(__func__);
1126
16
	sain = (struct sockaddr_in *)&h->ss;
1127
16
	sain->sin_len = sizeof(struct sockaddr_in);
1128
16
	sain->sin_family = AF_INET;
1129
16
	sain->sin_addr.s_addr = ina.s_addr;
1130
1131
16
	return (h);
1132
20
}
1133
1134
struct address *
1135
host_v6(const char *s)
1136
{
1137
8
	struct addrinfo		 hints, *res;
1138
	struct sockaddr_in6	*sa_in6;
1139
	struct address		*h = NULL;
1140
1141
4
	bzero(&hints, sizeof(hints));
1142
4
	hints.ai_family = AF_INET6;
1143
4
	hints.ai_socktype = SOCK_DGRAM; /* dummy */
1144
4
	hints.ai_flags = AI_NUMERICHOST;
1145
4
	if (getaddrinfo(s, "0", &hints, &res) == 0) {
1146
		if ((h = calloc(1, sizeof(*h))) == NULL)
1147
			fatal(__func__);
1148
		sa_in6 = (struct sockaddr_in6 *)&h->ss;
1149
		sa_in6->sin6_len = sizeof(struct sockaddr_in6);
1150
		sa_in6->sin6_family = AF_INET6;
1151
		memcpy(&sa_in6->sin6_addr,
1152
		    &((struct sockaddr_in6 *)res->ai_addr)->sin6_addr,
1153
		    sizeof(sa_in6->sin6_addr));
1154
		sa_in6->sin6_scope_id =
1155
		    ((struct sockaddr_in6 *)res->ai_addr)->sin6_scope_id;
1156
1157
		freeaddrinfo(res);
1158
	}
1159
1160
4
	return (h);
1161
4
}
1162
1163
int
1164
host_dns(const char *s, struct addresslist *al, int max,
1165
	in_port_t port, struct ber_oid *oid, char *cmn, struct address *src)
1166
{
1167
8
	struct addrinfo		 hints, *res0, *res;
1168
	int			 error, cnt = 0;
1169
	struct sockaddr_in	*sain;
1170
	struct sockaddr_in6	*sin6;
1171
	struct address		*h;
1172
1173
4
	bzero(&hints, sizeof(hints));
1174
4
	hints.ai_family = PF_UNSPEC;
1175
4
	hints.ai_socktype = SOCK_DGRAM; /* DUMMY */
1176
4
	hints.ai_flags = AI_ADDRCONFIG;
1177
4
	error = getaddrinfo(s, NULL, &hints, &res0);
1178
4
	if (error == EAI_AGAIN || error == EAI_NODATA || error == EAI_NONAME)
1179
		return (0);
1180
4
	if (error) {
1181
		log_warnx("host_dns: could not parse \"%s\": %s", s,
1182
		    gai_strerror(error));
1183
		return (-1);
1184
	}
1185
1186

24
	for (res = res0; res && cnt < max; res = res->ai_next) {
1187

4
		if (res->ai_family != AF_INET &&
1188
		    res->ai_family != AF_INET6)
1189
			continue;
1190

4
		if (src != NULL && src->ss.ss_family != res->ai_family)
1191
			continue;
1192
4
		if ((h = calloc(1, sizeof(*h))) == NULL)
1193
			fatal(__func__);
1194
1195
4
		h->port = port;
1196
4
		if (oid != NULL) {
1197
			if ((h->sa_oid = calloc(1, sizeof(*oid))) == NULL)
1198
				fatal(__func__);
1199
			bcopy(oid, h->sa_oid, sizeof(*oid));
1200
		}
1201
4
		if (cmn != NULL) {
1202
			if ((h->sa_community = strdup(cmn)) == NULL)
1203
				fatal(__func__);
1204
		}
1205
1206
4
		h->ss.ss_family = res->ai_family;
1207
4
		if (res->ai_family == AF_INET) {
1208
4
			sain = (struct sockaddr_in *)&h->ss;
1209
4
			sain->sin_len = sizeof(struct sockaddr_in);
1210
4
			sain->sin_addr.s_addr = ((struct sockaddr_in *)
1211
4
			    res->ai_addr)->sin_addr.s_addr;
1212
4
		} else {
1213
			sin6 = (struct sockaddr_in6 *)&h->ss;
1214
			sin6->sin6_len = sizeof(struct sockaddr_in6);
1215
			memcpy(&sin6->sin6_addr, &((struct sockaddr_in6 *)
1216
			    res->ai_addr)->sin6_addr, sizeof(struct in6_addr));
1217
		}
1218
1219
4
		h->sa_srcaddr = src;
1220
1221
4
		TAILQ_INSERT_TAIL(al, h, entry);
1222
4
		cnt++;
1223
4
	}
1224
4
	if (cnt == max && res) {
1225
4
		log_warnx("host_dns: %s resolves to more than %d hosts",
1226
		    s, max);
1227
4
	}
1228
4
	freeaddrinfo(res0);
1229
4
	if (oid != NULL)
1230
		free(oid);
1231
4
	if (cmn != NULL)
1232
		free(cmn);
1233
4
	return (cnt);
1234
4
}
1235
1236
int
1237
host(const char *s, struct addresslist *al, int max,
1238
    in_port_t port, struct ber_oid *oid, char *cmn, char *srcaddr)
1239
{
1240
	struct address	*h, *src = NULL;
1241
1242
40
	if (srcaddr != NULL) {
1243
		src = host_v4(srcaddr);
1244
		if (src == NULL)
1245
			src = host_v6(srcaddr);
1246
		if (src == NULL) {
1247
			log_warnx("invalid source-address %s", srcaddr);
1248
			return (-1);
1249
		}
1250
	}
1251
1252
20
	h = host_v4(s);
1253
1254
	/* IPv6 address? */
1255
20
	if (h == NULL)
1256
4
		h = host_v6(s);
1257
1258
20
	if (h != NULL) {
1259
16
		h->port = port;
1260
16
		h->sa_oid = oid;
1261
16
		h->sa_community = cmn;
1262

16
		if (src != NULL && h->ss.ss_family != src->ss.ss_family) {
1263
			log_warnx("host and source-address family mismatch");
1264
			return (-1);
1265
		}
1266
16
		h->sa_srcaddr = src;
1267
1268
16
		TAILQ_INSERT_TAIL(al, h, entry);
1269
16
		return (1);
1270
	}
1271
1272
4
	return (host_dns(s, al, max, port, oid, cmn, src));
1273
20
}