1 /* $OpenBSD: parse.y,v 1.39 2016/06/21 21:35:25 benno Exp $ */ 2 3 /* 4 * Copyright (c) 2007, 2008, 2012 Reyk Floeter <reyk@openbsd.org> 5 * Copyright (c) 2004, 2005 Esben Norby <norby@openbsd.org> 6 * Copyright (c) 2004 Ryan McBride <mcbride@openbsd.org> 7 * Copyright (c) 2002, 2003, 2004 Henning Brauer <henning@openbsd.org> 8 * Copyright (c) 2001 Markus Friedl. All rights reserved. 9 * Copyright (c) 2001 Daniel Hartmeier. All rights reserved. 10 * Copyright (c) 2001 Theo de Raadt. All rights reserved. 11 * 12 * Permission to use, copy, modify, and distribute this software for any 13 * purpose with or without fee is hereby granted, provided that the above 14 * copyright notice and this permission notice appear in all copies. 15 * 16 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 17 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 18 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 19 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 20 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 21 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 22 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 23 */ 24 25 %{ 26 #include <sys/types.h> 27 #include <sys/socket.h> 28 #include <sys/stat.h> 29 #include <sys/queue.h> 30 #include <sys/tree.h> 31 32 #include <netinet/in.h> 33 #include <net/if.h> 34 35 #include <arpa/inet.h> 36 #include <arpa/nameser.h> 37 38 #include <ctype.h> 39 #include <unistd.h> 40 #include <err.h> 41 #include <errno.h> 42 #include <event.h> 43 #include <limits.h> 44 #include <stdint.h> 45 #include <stdarg.h> 46 #include <stdio.h> 47 #include <netdb.h> 48 #include <string.h> 49 #include <syslog.h> 50 51 #include "snmpd.h" 52 #include "mib.h" 53 54 enum socktype { 55 SOCK_TYPE_RESTRICTED = 1, 56 SOCK_TYPE_AGENTX = 2 57 }; 58 59 TAILQ_HEAD(files, file) files = TAILQ_HEAD_INITIALIZER(files); 60 static struct file { 61 TAILQ_ENTRY(file) entry; 62 FILE *stream; 63 char *name; 64 int lineno; 65 int errors; 66 } *file, *topfile; 67 struct file *pushfile(const char *, int); 68 int popfile(void); 69 int check_file_secrecy(int, const char *); 70 int yyparse(void); 71 int yylex(void); 72 int yyerror(const char *, ...) 73 __attribute__((__format__ (printf, 1, 2))) 74 __attribute__((__nonnull__ (1))); 75 int kw_cmp(const void *, const void *); 76 int lookup(char *); 77 int lgetc(int); 78 int lungetc(int); 79 int findeol(void); 80 81 TAILQ_HEAD(symhead, sym) symhead = TAILQ_HEAD_INITIALIZER(symhead); 82 struct sym { 83 TAILQ_ENTRY(sym) entry; 84 int used; 85 int persist; 86 char *nam; 87 char *val; 88 }; 89 int symset(const char *, const char *, int); 90 char *symget(const char *); 91 92 struct snmpd *conf = NULL; 93 static int errors = 0; 94 static struct addresslist *hlist; 95 static struct usmuser *user = NULL; 96 static int nctlsocks = 0; 97 98 struct address *host_v4(const char *); 99 struct address *host_v6(const char *); 100 int host_dns(const char *, struct addresslist *, 101 int, in_port_t, struct ber_oid *, char *); 102 int host(const char *, struct addresslist *, 103 int, in_port_t, struct ber_oid *, char *); 104 105 typedef struct { 106 union { 107 int64_t number; 108 char *string; 109 struct host *host; 110 struct timeval tv; 111 struct ber_oid *oid; 112 struct { 113 int type; 114 void *data; 115 long long value; 116 } data; 117 enum usmauth auth; 118 enum usmpriv enc; 119 } v; 120 int lineno; 121 } YYSTYPE; 122 123 %} 124 125 %token INCLUDE 126 %token LISTEN ON 127 %token SYSTEM CONTACT DESCR LOCATION NAME OBJECTID SERVICES RTFILTER 128 %token READONLY READWRITE OCTETSTRING INTEGER COMMUNITY TRAP RECEIVER 129 %token SECLEVEL NONE AUTH ENC USER AUTHKEY ENCKEY ERROR DISABLED 130 %token SOCKET RESTRICTED AGENTX HANDLE DEFAULT 131 %token <v.string> STRING 132 %token <v.number> NUMBER 133 %type <v.string> hostcmn 134 %type <v.number> optwrite yesno seclevel socktype 135 %type <v.data> objtype cmd 136 %type <v.oid> oid hostoid trapoid 137 %type <v.auth> auth 138 %type <v.enc> enc 139 140 %% 141 142 grammar : /* empty */ 143 | grammar include '\n' 144 | grammar '\n' 145 | grammar varset '\n' 146 | grammar main '\n' 147 | grammar system '\n' 148 | grammar mib '\n' 149 | grammar error '\n' { file->errors++; } 150 ; 151 152 include : INCLUDE STRING { 153 struct file *nfile; 154 155 if ((nfile = pushfile($2, 0)) == NULL) { 156 yyerror("failed to include file %s", $2); 157 free($2); 158 YYERROR; 159 } 160 free($2); 161 162 file = nfile; 163 lungetc('\n'); 164 } 165 ; 166 167 varset : STRING '=' STRING { 168 char *s = $1; 169 while (*s++) { 170 if (isspace((unsigned char)*s)) { 171 yyerror("macro name cannot contain " 172 "whitespace"); 173 YYERROR; 174 } 175 } 176 if (symset($1, $3, 0) == -1) 177 fatal("cannot store variable"); 178 free($1); 179 free($3); 180 } 181 ; 182 183 yesno : STRING { 184 if (!strcmp($1, "yes")) 185 $$ = 1; 186 else if (!strcmp($1, "no")) 187 $$ = 0; 188 else { 189 yyerror("syntax error, " 190 "either yes or no expected"); 191 free($1); 192 YYERROR; 193 } 194 free($1); 195 } 196 ; 197 198 main : LISTEN ON STRING { 199 struct addresslist al; 200 struct address *h; 201 202 TAILQ_INIT(&al); 203 if (host($3, &al, 1, SNMPD_PORT, NULL, NULL) <= 0) { 204 yyerror("invalid ip address: %s", $3); 205 free($3); 206 YYERROR; 207 } 208 free($3); 209 h = TAILQ_FIRST(&al); 210 bcopy(&h->ss, &conf->sc_address.ss, sizeof(*h)); 211 conf->sc_address.port = h->port; 212 213 while ((h = TAILQ_FIRST(&al)) != NULL) { 214 TAILQ_REMOVE(&al, h, entry); 215 free(h); 216 } 217 } 218 | READONLY COMMUNITY STRING { 219 if (strlcpy(conf->sc_rdcommunity, $3, 220 sizeof(conf->sc_rdcommunity)) >= 221 sizeof(conf->sc_rdcommunity)) { 222 yyerror("r/o community name too long"); 223 free($3); 224 YYERROR; 225 } 226 free($3); 227 } 228 | READWRITE COMMUNITY STRING { 229 if (strlcpy(conf->sc_rwcommunity, $3, 230 sizeof(conf->sc_rwcommunity)) >= 231 sizeof(conf->sc_rwcommunity)) { 232 yyerror("r/w community name too long"); 233 free($3); 234 YYERROR; 235 } 236 free($3); 237 } 238 | READWRITE DISABLED { 239 conf->sc_readonly = 1; 240 } 241 | TRAP COMMUNITY STRING { 242 if (strlcpy(conf->sc_trcommunity, $3, 243 sizeof(conf->sc_trcommunity)) >= 244 sizeof(conf->sc_trcommunity)) { 245 yyerror("trap community name too long"); 246 free($3); 247 YYERROR; 248 } 249 free($3); 250 } 251 | TRAP RECEIVER { 252 hlist = &conf->sc_trapreceivers; 253 } host { 254 hlist = NULL; 255 } 256 | TRAP HANDLE hostcmn trapoid cmd { 257 struct trapcmd *cmd = $5.data; 258 259 cmd->cmd_oid = $4; 260 261 if (trapcmd_add(cmd) != 0) { 262 free($4); 263 free(cmd); 264 yyerror("duplicate oid"); 265 YYERROR; 266 } 267 conf->sc_traphandler = 1; 268 } 269 | RTFILTER yesno { 270 if ($2 == 1) 271 conf->sc_rtfilter = ROUTE_FILTER(RTM_NEWADDR) | 272 ROUTE_FILTER(RTM_DELADDR) | 273 ROUTE_FILTER(RTM_IFINFO) | 274 ROUTE_FILTER(RTM_IFANNOUNCE); 275 else 276 conf->sc_rtfilter = 0; 277 } 278 | SECLEVEL seclevel { 279 conf->sc_min_seclevel = $2; 280 } 281 | USER STRING { 282 const char *errstr; 283 user = usm_newuser($2, &errstr); 284 if (user == NULL) { 285 yyerror(errstr); 286 free($2); 287 YYERROR; 288 } 289 } userspecs { 290 const char *errstr; 291 if (usm_checkuser(user, &errstr) < 0) { 292 yyerror(errstr); 293 YYERROR; 294 } 295 user = NULL; 296 } 297 | SOCKET STRING socktype { 298 if ($3) { 299 struct control_sock *rcsock; 300 301 rcsock = calloc(1, sizeof(*rcsock)); 302 if (rcsock == NULL) { 303 yyerror("calloc"); 304 YYERROR; 305 } 306 rcsock->cs_name = $2; 307 if ($3 == SOCK_TYPE_RESTRICTED) 308 rcsock->cs_restricted = 1; 309 else if ($3 == SOCK_TYPE_AGENTX) 310 rcsock->cs_agentx = 1; 311 TAILQ_INSERT_TAIL(&conf->sc_ps.ps_rcsocks, 312 rcsock, cs_entry); 313 } else { 314 if (++nctlsocks > 1) { 315 yyerror("multiple control " 316 "sockets specified"); 317 YYERROR; 318 } 319 conf->sc_ps.ps_csock.cs_name = $2; 320 } 321 } 322 ; 323 324 system : SYSTEM sysmib 325 ; 326 327 sysmib : CONTACT STRING { 328 struct ber_oid o = OID(MIB_sysContact); 329 mps_set(&o, $2, strlen($2)); 330 } 331 | DESCR STRING { 332 struct ber_oid o = OID(MIB_sysDescr); 333 mps_set(&o, $2, strlen($2)); 334 } 335 | LOCATION STRING { 336 struct ber_oid o = OID(MIB_sysLocation); 337 mps_set(&o, $2, strlen($2)); 338 } 339 | NAME STRING { 340 struct ber_oid o = OID(MIB_sysName); 341 mps_set(&o, $2, strlen($2)); 342 } 343 | OBJECTID oid { 344 struct ber_oid o = OID(MIB_sysOID); 345 mps_set(&o, $2, sizeof(struct ber_oid)); 346 } 347 | SERVICES NUMBER { 348 struct ber_oid o = OID(MIB_sysServices); 349 mps_set(&o, NULL, $2); 350 } 351 ; 352 353 mib : OBJECTID oid NAME STRING optwrite objtype { 354 struct oid *oid; 355 if ((oid = (struct oid *) 356 calloc(1, sizeof(*oid))) == NULL) { 357 yyerror("calloc"); 358 free($2); 359 free($6.data); 360 YYERROR; 361 } 362 363 smi_oidlen($2); 364 bcopy($2, &oid->o_id, sizeof(struct ber_oid)); 365 free($2); 366 oid->o_name = $4; 367 oid->o_data = $6.data; 368 oid->o_val = $6.value; 369 switch ($6.type) { 370 case 1: 371 oid->o_get = mps_getint; 372 oid->o_set = mps_setint; 373 break; 374 case 2: 375 oid->o_get = mps_getstr; 376 oid->o_set = mps_setstr; 377 break; 378 } 379 oid->o_flags = OID_RD|OID_DYNAMIC; 380 if ($5) 381 oid->o_flags |= OID_WR; 382 383 if (smi_insert(oid) == -1) { 384 yyerror("duplicate oid"); 385 free(oid->o_name); 386 free(oid->o_data); 387 YYERROR; 388 } 389 } 390 ; 391 392 objtype : INTEGER NUMBER { 393 $$.type = 1; 394 $$.data = NULL; 395 $$.value = $2; 396 } 397 | OCTETSTRING STRING { 398 $$.type = 2; 399 $$.data = $2; 400 $$.value = strlen($2); 401 } 402 ; 403 404 optwrite : READONLY { $$ = 0; } 405 | READWRITE { $$ = 1; } 406 ; 407 408 oid : STRING { 409 struct ber_oid *sysoid; 410 if ((sysoid = 411 calloc(1, sizeof(*sysoid))) == NULL) { 412 yyerror("calloc"); 413 free($1); 414 YYERROR; 415 } 416 if (ber_string2oid($1, sysoid) == -1) { 417 yyerror("invalid OID: %s", $1); 418 free(sysoid); 419 free($1); 420 YYERROR; 421 } 422 free($1); 423 $$ = sysoid; 424 } 425 ; 426 427 trapoid : oid { $$ = $1; } 428 | DEFAULT { 429 struct ber_oid *sysoid; 430 if ((sysoid = 431 calloc(1, sizeof(*sysoid))) == NULL) { 432 yyerror("calloc"); 433 YYERROR; 434 } 435 ber_string2oid("1.3", sysoid); 436 $$ = sysoid; 437 } 438 ; 439 440 hostoid : /* empty */ { $$ = NULL; } 441 | OBJECTID oid { $$ = $2; } 442 ; 443 444 hostcmn : /* empty */ { $$ = NULL; } 445 | COMMUNITY STRING { $$ = $2; } 446 ; 447 448 hostdef : STRING hostoid hostcmn { 449 if (host($1, hlist, 1, 450 SNMPD_TRAPPORT, $2, $3) <= 0) { 451 yyerror("invalid host: %s", $1); 452 free($1); 453 YYERROR; 454 } 455 free($1); 456 } 457 ; 458 459 hostlist : /* empty */ 460 | hostlist comma hostdef 461 ; 462 463 host : hostdef 464 | '{' hostlist '}' 465 ; 466 467 comma : /* empty */ 468 | ',' 469 ; 470 471 seclevel : NONE { $$ = 0; } 472 | AUTH { $$ = SNMP_MSGFLAG_AUTH; } 473 | ENC { $$ = SNMP_MSGFLAG_AUTH | SNMP_MSGFLAG_PRIV; } 474 ; 475 476 userspecs : /* empty */ 477 | userspecs userspec 478 ; 479 480 userspec : AUTHKEY STRING { 481 user->uu_authkey = $2; 482 } 483 | AUTH auth { 484 user->uu_auth = $2; 485 } 486 | ENCKEY STRING { 487 user->uu_privkey = $2; 488 } 489 | ENC enc { 490 user->uu_priv = $2; 491 } 492 ; 493 494 auth : STRING { 495 if (strcasecmp($1, "hmac-md5") == 0 || 496 strcasecmp($1, "hmac-md5-96") == 0) 497 $$ = AUTH_MD5; 498 else if (strcasecmp($1, "hmac-sha1") == 0 || 499 strcasecmp($1, "hmac-sha1-96") == 0) 500 $$ = AUTH_SHA1; 501 else { 502 yyerror("syntax error, bad auth hmac"); 503 free($1); 504 YYERROR; 505 } 506 free($1); 507 } 508 ; 509 510 enc : STRING { 511 if (strcasecmp($1, "des") == 0 || 512 strcasecmp($1, "cbc-des") == 0) 513 $$ = PRIV_DES; 514 else if (strcasecmp($1, "aes") == 0 || 515 strcasecmp($1, "cfb128-aes-128") == 0) 516 $$ = PRIV_AES; 517 else { 518 yyerror("syntax error, bad encryption cipher"); 519 free($1); 520 YYERROR; 521 } 522 free($1); 523 524 } 525 ; 526 527 socktype : RESTRICTED { $$ = SOCK_TYPE_RESTRICTED; } 528 | AGENTX { $$ = SOCK_TYPE_AGENTX; } 529 | /* nothing */ { $$ = 0; } 530 ; 531 532 cmd : STRING { 533 struct trapcmd *cmd; 534 size_t span, limit; 535 char *pos, **args, **args2; 536 int nargs = 32; /* XXX */ 537 538 if ((cmd = calloc(1, sizeof(*cmd))) == NULL || 539 (args = calloc(nargs, sizeof(char *))) == NULL) { 540 free(cmd); 541 free($1); 542 YYERROR; 543 } 544 545 pos = $1; 546 limit = strlen($1); 547 548 while ((span = strcspn(pos, " \t")) != 0 && 549 pos < $1 + limit) { 550 pos[span] = '\0'; 551 args[cmd->cmd_argc] = strdup(pos); 552 if (args[cmd->cmd_argc] == NULL) { 553 trapcmd_free(cmd); 554 free(args); 555 free($1); 556 YYERROR; 557 } 558 cmd->cmd_argc++; 559 if (cmd->cmd_argc >= nargs - 1) { 560 nargs *= 2; 561 args2 = calloc(nargs, sizeof(char *)); 562 if (args2 == NULL) { 563 trapcmd_free(cmd); 564 free(args); 565 free($1); 566 YYERROR; 567 } 568 args = args2; 569 } 570 pos += span + 1; 571 } 572 free($1); 573 cmd->cmd_argv = args; 574 $$.data = cmd; 575 } 576 ; 577 578 %% 579 580 struct keywords { 581 const char *k_name; 582 int k_val; 583 }; 584 585 int 586 yyerror(const char *fmt, ...) 587 { 588 va_list ap; 589 char *msg; 590 591 file->errors++; 592 va_start(ap, fmt); 593 if (vasprintf(&msg, fmt, ap) == -1) 594 fatalx("yyerror vasprintf"); 595 va_end(ap); 596 logit(LOG_CRIT, "%s:%d: %s", file->name, yylval.lineno, msg); 597 free(msg); 598 return (0); 599 } 600 601 int 602 kw_cmp(const void *k, const void *e) 603 { 604 return (strcmp(k, ((const struct keywords *)e)->k_name)); 605 } 606 607 int 608 lookup(char *s) 609 { 610 /* this has to be sorted always */ 611 static const struct keywords keywords[] = { 612 { "agentx", AGENTX }, 613 { "auth", AUTH }, 614 { "authkey", AUTHKEY }, 615 { "community", COMMUNITY }, 616 { "contact", CONTACT }, 617 { "default", DEFAULT }, 618 { "description", DESCR }, 619 { "disabled", DISABLED}, 620 { "enc", ENC }, 621 { "enckey", ENCKEY }, 622 { "filter-routes", RTFILTER }, 623 { "handle", HANDLE }, 624 { "include", INCLUDE }, 625 { "integer", INTEGER }, 626 { "listen", LISTEN }, 627 { "location", LOCATION }, 628 { "name", NAME }, 629 { "none", NONE }, 630 { "oid", OBJECTID }, 631 { "on", ON }, 632 { "read-only", READONLY }, 633 { "read-write", READWRITE }, 634 { "receiver", RECEIVER }, 635 { "restricted", RESTRICTED }, 636 { "seclevel", SECLEVEL }, 637 { "services", SERVICES }, 638 { "socket", SOCKET }, 639 { "string", OCTETSTRING }, 640 { "system", SYSTEM }, 641 { "trap", TRAP }, 642 { "user", USER } 643 }; 644 const struct keywords *p; 645 646 p = bsearch(s, keywords, sizeof(keywords)/sizeof(keywords[0]), 647 sizeof(keywords[0]), kw_cmp); 648 649 if (p) 650 return (p->k_val); 651 else 652 return (STRING); 653 } 654 655 #define MAXPUSHBACK 128 656 657 u_char *parsebuf; 658 int parseindex; 659 u_char pushback_buffer[MAXPUSHBACK]; 660 int pushback_index = 0; 661 662 int 663 lgetc(int quotec) 664 { 665 int c, next; 666 667 if (parsebuf) { 668 /* Read character from the parsebuffer instead of input. */ 669 if (parseindex >= 0) { 670 c = parsebuf[parseindex++]; 671 if (c != '\0') 672 return (c); 673 parsebuf = NULL; 674 } else 675 parseindex++; 676 } 677 678 if (pushback_index) 679 return (pushback_buffer[--pushback_index]); 680 681 if (quotec) { 682 if ((c = getc(file->stream)) == EOF) { 683 yyerror("reached end of file while parsing quoted string"); 684 if (file == topfile || popfile() == EOF) 685 return (EOF); 686 return (quotec); 687 } 688 return (c); 689 } 690 691 while ((c = getc(file->stream)) == '\\') { 692 next = getc(file->stream); 693 if (next != '\n') { 694 c = next; 695 break; 696 } 697 yylval.lineno = file->lineno; 698 file->lineno++; 699 } 700 if (c == '\t' || c == ' ') { 701 /* Compress blanks to a single space. */ 702 do { 703 c = getc(file->stream); 704 } while (c == '\t' || c == ' '); 705 ungetc(c, file->stream); 706 c = ' '; 707 } 708 709 while (c == EOF) { 710 if (file == topfile || popfile() == EOF) 711 return (EOF); 712 c = getc(file->stream); 713 } 714 return (c); 715 } 716 717 int 718 lungetc(int c) 719 { 720 if (c == EOF) 721 return (EOF); 722 if (parsebuf) { 723 parseindex--; 724 if (parseindex >= 0) 725 return (c); 726 } 727 if (pushback_index < MAXPUSHBACK-1) 728 return (pushback_buffer[pushback_index++] = c); 729 else 730 return (EOF); 731 } 732 733 int 734 findeol(void) 735 { 736 int c; 737 738 parsebuf = NULL; 739 740 /* skip to either EOF or the first real EOL */ 741 while (1) { 742 if (pushback_index) 743 c = pushback_buffer[--pushback_index]; 744 else 745 c = lgetc(0); 746 if (c == '\n') { 747 file->lineno++; 748 break; 749 } 750 if (c == EOF) 751 break; 752 } 753 return (ERROR); 754 } 755 756 int 757 yylex(void) 758 { 759 u_char buf[8096]; 760 u_char *p, *val; 761 int quotec, next, c; 762 int token; 763 764 top: 765 p = buf; 766 while ((c = lgetc(0)) == ' ' || c == '\t') 767 ; /* nothing */ 768 769 yylval.lineno = file->lineno; 770 if (c == '#') 771 while ((c = lgetc(0)) != '\n' && c != EOF) 772 ; /* nothing */ 773 if (c == '$' && parsebuf == NULL) { 774 while (1) { 775 if ((c = lgetc(0)) == EOF) 776 return (0); 777 778 if (p + 1 >= buf + sizeof(buf) - 1) { 779 yyerror("string too long"); 780 return (findeol()); 781 } 782 if (isalnum(c) || c == '_') { 783 *p++ = c; 784 continue; 785 } 786 *p = '\0'; 787 lungetc(c); 788 break; 789 } 790 val = symget(buf); 791 if (val == NULL) { 792 yyerror("macro '%s' not defined", buf); 793 return (findeol()); 794 } 795 parsebuf = val; 796 parseindex = 0; 797 goto top; 798 } 799 800 switch (c) { 801 case '\'': 802 case '"': 803 quotec = c; 804 while (1) { 805 if ((c = lgetc(quotec)) == EOF) 806 return (0); 807 if (c == '\n') { 808 file->lineno++; 809 continue; 810 } else if (c == '\\') { 811 if ((next = lgetc(quotec)) == EOF) 812 return (0); 813 if (next == quotec || c == ' ' || c == '\t') 814 c = next; 815 else if (next == '\n') { 816 file->lineno++; 817 continue; 818 } else 819 lungetc(next); 820 } else if (c == quotec) { 821 *p = '\0'; 822 break; 823 } else if (c == '\0') { 824 yyerror("syntax error"); 825 return (findeol()); 826 } 827 if (p + 1 >= buf + sizeof(buf) - 1) { 828 yyerror("string too long"); 829 return (findeol()); 830 } 831 *p++ = c; 832 } 833 yylval.v.string = strdup(buf); 834 if (yylval.v.string == NULL) 835 err(1, "yylex: strdup"); 836 return (STRING); 837 } 838 839 #define allowed_to_end_number(x) \ 840 (isspace(x) || x == ')' || x ==',' || x == '/' || x == '}' || x == '=') 841 842 if (c == '-' || isdigit(c)) { 843 do { 844 *p++ = c; 845 if ((unsigned)(p-buf) >= sizeof(buf)) { 846 yyerror("string too long"); 847 return (findeol()); 848 } 849 } while ((c = lgetc(0)) != EOF && isdigit(c)); 850 lungetc(c); 851 if (p == buf + 1 && buf[0] == '-') 852 goto nodigits; 853 if (c == EOF || allowed_to_end_number(c)) { 854 const char *errstr = NULL; 855 856 *p = '\0'; 857 yylval.v.number = strtonum(buf, LLONG_MIN, 858 LLONG_MAX, &errstr); 859 if (errstr) { 860 yyerror("\"%s\" invalid number: %s", 861 buf, errstr); 862 return (findeol()); 863 } 864 return (NUMBER); 865 } else { 866 nodigits: 867 while (p > buf + 1) 868 lungetc(*--p); 869 c = *--p; 870 if (c == '-') 871 return (c); 872 } 873 } 874 875 #define allowed_in_string(x) \ 876 (isalnum(x) || (ispunct(x) && x != '(' && x != ')' && \ 877 x != '{' && x != '}' && \ 878 x != '!' && x != '=' && x != '#' && \ 879 x != ',')) 880 881 if (isalnum(c) || c == ':' || c == '_') { 882 do { 883 *p++ = c; 884 if ((unsigned)(p-buf) >= sizeof(buf)) { 885 yyerror("string too long"); 886 return (findeol()); 887 } 888 } while ((c = lgetc(0)) != EOF && (allowed_in_string(c))); 889 lungetc(c); 890 *p = '\0'; 891 if ((token = lookup(buf)) == STRING) 892 if ((yylval.v.string = strdup(buf)) == NULL) 893 err(1, "yylex: strdup"); 894 return (token); 895 } 896 if (c == '\n') { 897 yylval.lineno = file->lineno; 898 file->lineno++; 899 } 900 if (c == EOF) 901 return (0); 902 return (c); 903 } 904 905 int 906 check_file_secrecy(int fd, const char *fname) 907 { 908 struct stat st; 909 910 if (fstat(fd, &st)) { 911 log_warn("cannot stat %s", fname); 912 return (-1); 913 } 914 if (st.st_uid != 0 && st.st_uid != getuid()) { 915 log_warnx("%s: owner not root or current user", fname); 916 return (-1); 917 } 918 if (st.st_mode & (S_IWGRP | S_IXGRP | S_IRWXO)) { 919 log_warnx("%s: group writable or world read/writable", fname); 920 return (-1); 921 } 922 return (0); 923 } 924 925 struct file * 926 pushfile(const char *name, int secret) 927 { 928 struct file *nfile; 929 930 if ((nfile = calloc(1, sizeof(struct file))) == NULL) { 931 log_warn("malloc"); 932 return (NULL); 933 } 934 if ((nfile->name = strdup(name)) == NULL) { 935 log_warn("malloc"); 936 free(nfile); 937 return (NULL); 938 } 939 if ((nfile->stream = fopen(nfile->name, "r")) == NULL) { 940 log_warn("%s", nfile->name); 941 free(nfile->name); 942 free(nfile); 943 return (NULL); 944 } else if (secret && 945 check_file_secrecy(fileno(nfile->stream), nfile->name)) { 946 fclose(nfile->stream); 947 free(nfile->name); 948 free(nfile); 949 return (NULL); 950 } 951 nfile->lineno = 1; 952 TAILQ_INSERT_TAIL(&files, nfile, entry); 953 return (nfile); 954 } 955 956 int 957 popfile(void) 958 { 959 struct file *prev; 960 961 if ((prev = TAILQ_PREV(file, files, entry)) != NULL) 962 prev->errors += file->errors; 963 964 TAILQ_REMOVE(&files, file, entry); 965 fclose(file->stream); 966 free(file->name); 967 free(file); 968 file = prev; 969 return (file ? 0 : EOF); 970 } 971 972 struct snmpd * 973 parse_config(const char *filename, u_int flags) 974 { 975 struct sym *sym, *next; 976 977 if ((conf = calloc(1, sizeof(*conf))) == NULL) { 978 log_warn("cannot allocate memory"); 979 return (NULL); 980 } 981 982 conf->sc_flags = flags; 983 conf->sc_confpath = filename; 984 conf->sc_address.ss.ss_family = AF_INET; 985 conf->sc_address.port = SNMPD_PORT; 986 conf->sc_ps.ps_csock.cs_name = SNMPD_SOCKET; 987 TAILQ_INIT(&conf->sc_ps.ps_rcsocks); 988 strlcpy(conf->sc_rdcommunity, "public", SNMPD_MAXCOMMUNITYLEN); 989 strlcpy(conf->sc_rwcommunity, "private", SNMPD_MAXCOMMUNITYLEN); 990 strlcpy(conf->sc_trcommunity, "public", SNMPD_MAXCOMMUNITYLEN); 991 TAILQ_INIT(&conf->sc_trapreceivers); 992 993 if ((file = pushfile(filename, 0)) == NULL) { 994 free(conf); 995 return (NULL); 996 } 997 topfile = file; 998 setservent(1); 999 1000 yyparse(); 1001 errors = file->errors; 1002 popfile(); 1003 1004 endservent(); 1005 1006 /* Free macros and check which have not been used. */ 1007 for (sym = TAILQ_FIRST(&symhead); sym != NULL; sym = next) { 1008 next = TAILQ_NEXT(sym, entry); 1009 if ((conf->sc_flags & SNMPD_F_VERBOSE) && !sym->used) 1010 fprintf(stderr, "warning: macro '%s' not " 1011 "used\n", sym->nam); 1012 if (!sym->persist) { 1013 free(sym->nam); 1014 free(sym->val); 1015 TAILQ_REMOVE(&symhead, sym, entry); 1016 free(sym); 1017 } 1018 } 1019 1020 if (errors) { 1021 free(conf); 1022 return (NULL); 1023 } 1024 1025 return (conf); 1026 } 1027 1028 int 1029 symset(const char *nam, const char *val, int persist) 1030 { 1031 struct sym *sym; 1032 1033 for (sym = TAILQ_FIRST(&symhead); sym && strcmp(nam, sym->nam); 1034 sym = TAILQ_NEXT(sym, entry)) 1035 ; /* nothing */ 1036 1037 if (sym != NULL) { 1038 if (sym->persist == 1) 1039 return (0); 1040 else { 1041 free(sym->nam); 1042 free(sym->val); 1043 TAILQ_REMOVE(&symhead, sym, entry); 1044 free(sym); 1045 } 1046 } 1047 if ((sym = calloc(1, sizeof(*sym))) == NULL) 1048 return (-1); 1049 1050 sym->nam = strdup(nam); 1051 if (sym->nam == NULL) { 1052 free(sym); 1053 return (-1); 1054 } 1055 sym->val = strdup(val); 1056 if (sym->val == NULL) { 1057 free(sym->nam); 1058 free(sym); 1059 return (-1); 1060 } 1061 sym->used = 0; 1062 sym->persist = persist; 1063 TAILQ_INSERT_TAIL(&symhead, sym, entry); 1064 return (0); 1065 } 1066 1067 int 1068 cmdline_symset(char *s) 1069 { 1070 char *sym, *val; 1071 int ret; 1072 size_t len; 1073 1074 if ((val = strrchr(s, '=')) == NULL) 1075 return (-1); 1076 1077 len = strlen(s) - strlen(val) + 1; 1078 if ((sym = malloc(len)) == NULL) 1079 errx(1, "cmdline_symset: malloc"); 1080 1081 (void)strlcpy(sym, s, len); 1082 1083 ret = symset(sym, val + 1, 1); 1084 free(sym); 1085 1086 return (ret); 1087 } 1088 1089 char * 1090 symget(const char *nam) 1091 { 1092 struct sym *sym; 1093 1094 TAILQ_FOREACH(sym, &symhead, entry) 1095 if (strcmp(nam, sym->nam) == 0) { 1096 sym->used = 1; 1097 return (sym->val); 1098 } 1099 return (NULL); 1100 } 1101 1102 struct address * 1103 host_v4(const char *s) 1104 { 1105 struct in_addr ina; 1106 struct sockaddr_in *sain; 1107 struct address *h; 1108 1109 bzero(&ina, sizeof(ina)); 1110 if (inet_pton(AF_INET, s, &ina) != 1) 1111 return (NULL); 1112 1113 if ((h = calloc(1, sizeof(*h))) == NULL) 1114 fatal(__func__); 1115 sain = (struct sockaddr_in *)&h->ss; 1116 sain->sin_len = sizeof(struct sockaddr_in); 1117 sain->sin_family = AF_INET; 1118 sain->sin_addr.s_addr = ina.s_addr; 1119 1120 return (h); 1121 } 1122 1123 struct address * 1124 host_v6(const char *s) 1125 { 1126 struct addrinfo hints, *res; 1127 struct sockaddr_in6 *sa_in6; 1128 struct address *h = NULL; 1129 1130 bzero(&hints, sizeof(hints)); 1131 hints.ai_family = AF_INET6; 1132 hints.ai_socktype = SOCK_DGRAM; /* dummy */ 1133 hints.ai_flags = AI_NUMERICHOST; 1134 if (getaddrinfo(s, "0", &hints, &res) == 0) { 1135 if ((h = calloc(1, sizeof(*h))) == NULL) 1136 fatal(__func__); 1137 sa_in6 = (struct sockaddr_in6 *)&h->ss; 1138 sa_in6->sin6_len = sizeof(struct sockaddr_in6); 1139 sa_in6->sin6_family = AF_INET6; 1140 memcpy(&sa_in6->sin6_addr, 1141 &((struct sockaddr_in6 *)res->ai_addr)->sin6_addr, 1142 sizeof(sa_in6->sin6_addr)); 1143 sa_in6->sin6_scope_id = 1144 ((struct sockaddr_in6 *)res->ai_addr)->sin6_scope_id; 1145 1146 freeaddrinfo(res); 1147 } 1148 1149 return (h); 1150 } 1151 1152 int 1153 host_dns(const char *s, struct addresslist *al, int max, 1154 in_port_t port, struct ber_oid *oid, char *cmn) 1155 { 1156 struct addrinfo hints, *res0, *res; 1157 int error, cnt = 0; 1158 struct sockaddr_in *sain; 1159 struct sockaddr_in6 *sin6; 1160 struct address *h; 1161 1162 bzero(&hints, sizeof(hints)); 1163 hints.ai_family = PF_UNSPEC; 1164 hints.ai_socktype = SOCK_DGRAM; /* DUMMY */ 1165 hints.ai_flags = AI_ADDRCONFIG; 1166 error = getaddrinfo(s, NULL, &hints, &res0); 1167 if (error == EAI_AGAIN || error == EAI_NODATA || error == EAI_NONAME) 1168 return (0); 1169 if (error) { 1170 log_warnx("host_dns: could not parse \"%s\": %s", s, 1171 gai_strerror(error)); 1172 return (-1); 1173 } 1174 1175 for (res = res0; res && cnt < max; res = res->ai_next) { 1176 if (res->ai_family != AF_INET && 1177 res->ai_family != AF_INET6) 1178 continue; 1179 if ((h = calloc(1, sizeof(*h))) == NULL) 1180 fatal(__func__); 1181 1182 h->port = port; 1183 if (oid != NULL) { 1184 if ((h->sa_oid = calloc(1, sizeof(*oid))) == NULL) 1185 fatal(__func__); 1186 bcopy(oid, h->sa_oid, sizeof(*oid)); 1187 } 1188 if (cmn != NULL) { 1189 if ((h->sa_community = strdup(cmn)) == NULL) 1190 fatal(__func__); 1191 } 1192 1193 h->ss.ss_family = res->ai_family; 1194 if (res->ai_family == AF_INET) { 1195 sain = (struct sockaddr_in *)&h->ss; 1196 sain->sin_len = sizeof(struct sockaddr_in); 1197 sain->sin_addr.s_addr = ((struct sockaddr_in *) 1198 res->ai_addr)->sin_addr.s_addr; 1199 } else { 1200 sin6 = (struct sockaddr_in6 *)&h->ss; 1201 sin6->sin6_len = sizeof(struct sockaddr_in6); 1202 memcpy(&sin6->sin6_addr, &((struct sockaddr_in6 *) 1203 res->ai_addr)->sin6_addr, sizeof(struct in6_addr)); 1204 } 1205 1206 TAILQ_INSERT_HEAD(al, h, entry); 1207 cnt++; 1208 } 1209 if (cnt == max && res) { 1210 log_warnx("host_dns: %s resolves to more than %d hosts", 1211 s, max); 1212 } 1213 freeaddrinfo(res0); 1214 if (oid != NULL) 1215 free(oid); 1216 if (cmn != NULL) 1217 free(cmn); 1218 return (cnt); 1219 } 1220 1221 int 1222 host(const char *s, struct addresslist *al, int max, 1223 in_port_t port, struct ber_oid *oid, char *cmn) 1224 { 1225 struct address *h; 1226 1227 h = host_v4(s); 1228 1229 /* IPv6 address? */ 1230 if (h == NULL) 1231 h = host_v6(s); 1232 1233 if (h != NULL) { 1234 h->port = port; 1235 h->sa_oid = oid; 1236 h->sa_community = cmn; 1237 1238 TAILQ_INSERT_HEAD(al, h, entry); 1239 return (1); 1240 } 1241 1242 return (host_dns(s, al, max, port, oid, cmn)); 1243 } 1244