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