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