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