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