1 /* $OpenBSD: smi.c,v 1.31 2022/06/30 09:42:19 martijn Exp $ */ 2 3 /* 4 * Copyright (c) 2007, 2008 Reyk Floeter <reyk@openbsd.org> 5 * 6 * Permission to use, copy, modify, and distribute this software for any 7 * purpose with or without fee is hereby granted, provided that the above 8 * copyright notice and this permission notice appear in all copies. 9 * 10 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 11 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 12 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 13 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 14 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 15 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 16 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 17 */ 18 19 #include <sys/queue.h> 20 #include <sys/types.h> 21 #include <sys/stat.h> 22 #include <sys/socket.h> 23 #include <sys/un.h> 24 #include <sys/tree.h> 25 #include <sys/sysctl.h> 26 27 #include <net/if.h> 28 #include <net/if_dl.h> 29 #include <net/if_arp.h> 30 #include <net/if_media.h> 31 #include <net/route.h> 32 #include <netinet/in.h> 33 #include <netinet/if_ether.h> 34 #include <arpa/inet.h> 35 36 #include <stdlib.h> 37 #include <stdio.h> 38 #include <errno.h> 39 #include <event.h> 40 #include <fcntl.h> 41 #include <string.h> 42 #include <unistd.h> 43 #include <limits.h> 44 #include <pwd.h> 45 #include <vis.h> 46 47 #include "snmpd.h" 48 #include "mib.h" 49 #include "application.h" 50 51 #define MINIMUM(a, b) (((a) < (b)) ? (a) : (b)) 52 53 RB_HEAD(oidtree, oid); 54 RB_PROTOTYPE(oidtree, oid, o_element, smi_oid_cmp); 55 struct oidtree smi_oidtree; 56 57 RB_HEAD(keytree, oid); 58 RB_PROTOTYPE(keytree, oid, o_keyword, smi_key_cmp); 59 struct keytree smi_keytree; 60 61 u_long 62 smi_getticks(void) 63 { 64 struct timeval now, run; 65 u_long ticks; 66 67 gettimeofday(&now, NULL); 68 if (timercmp(&now, &snmpd_env->sc_starttime, <=)) 69 return (0); 70 timersub(&now, &snmpd_env->sc_starttime, &run); 71 ticks = run.tv_sec * 100; 72 if (run.tv_usec) 73 ticks += run.tv_usec / 10000; 74 75 return (ticks); 76 } 77 78 void 79 smi_oidlen(struct ber_oid *o) 80 { 81 size_t i; 82 83 for (i = 0; i < BER_MAX_OID_LEN && o->bo_id[i] != 0; i++) 84 ; 85 o->bo_n = i; 86 } 87 88 void 89 smi_scalar_oidlen(struct ber_oid *o) 90 { 91 smi_oidlen(o); 92 93 /* Append .0. */ 94 if (o->bo_n < BER_MAX_OID_LEN) 95 o->bo_n++; 96 } 97 98 char * 99 smi_oid2string(struct ber_oid *o, char *buf, size_t len, size_t skip) 100 { 101 char str[256]; 102 struct oid *value, key; 103 size_t i, lookup = 1; 104 105 bzero(buf, len); 106 bzero(&key, sizeof(key)); 107 bcopy(o, &key.o_id, sizeof(struct ber_oid)); 108 key.o_flags |= OID_KEY; /* do not match wildcards */ 109 110 if (snmpd_env->sc_flags & SNMPD_F_NONAMES) 111 lookup = 0; 112 113 for (i = 0; i < o->bo_n; i++) { 114 key.o_oidlen = i + 1; 115 if (lookup && skip > i) 116 continue; 117 if (lookup && 118 (value = RB_FIND(oidtree, &smi_oidtree, &key)) != NULL) 119 snprintf(str, sizeof(str), "%s", value->o_name); 120 else 121 snprintf(str, sizeof(str), "%d", key.o_oid[i]); 122 strlcat(buf, str, len); 123 if (i < (o->bo_n - 1)) 124 strlcat(buf, ".", len); 125 } 126 127 return (buf); 128 } 129 130 int 131 smi_string2oid(const char *oidstr, struct ber_oid *o) 132 { 133 char *sp, *p, str[BUFSIZ]; 134 const char *errstr; 135 struct oid *oid; 136 struct ber_oid ko; 137 138 if (strlcpy(str, oidstr, sizeof(str)) >= sizeof(str)) 139 return (-1); 140 bzero(o, sizeof(*o)); 141 142 /* 143 * Parse OID strings in the common form n.n.n or n-n-n. 144 * Based on ober_string2oid with additional support for symbolic names. 145 */ 146 for (p = sp = str; p != NULL; sp = p) { 147 if ((p = strpbrk(p, ".-")) != NULL) 148 *p++ = '\0'; 149 if ((oid = smi_findkey(sp)) != NULL) { 150 bcopy(&oid->o_id, &ko, sizeof(ko)); 151 if (o->bo_n && ober_oid_cmp(o, &ko) != 2) 152 return (-1); 153 bcopy(&ko, o, sizeof(*o)); 154 errstr = NULL; 155 } else { 156 o->bo_id[o->bo_n++] = 157 strtonum(sp, 0, UINT_MAX, &errstr); 158 } 159 if (errstr || o->bo_n > BER_MAX_OID_LEN) 160 return (-1); 161 } 162 163 return (0); 164 } 165 166 void 167 smi_delete(struct oid *oid) 168 { 169 struct oid key, *value; 170 171 bzero(&key, sizeof(key)); 172 bcopy(&oid->o_id, &key.o_id, sizeof(struct ber_oid)); 173 if ((value = RB_FIND(oidtree, &smi_oidtree, &key)) != NULL && 174 value == oid) 175 RB_REMOVE(oidtree, &smi_oidtree, value); 176 177 free(oid->o_data); 178 if (oid->o_flags & OID_DYNAMIC) { 179 free(oid->o_name); 180 free(oid); 181 } 182 } 183 184 int 185 smi_insert(struct oid *oid) 186 { 187 struct oid key, *value; 188 189 if ((oid->o_flags & OID_TABLE) && oid->o_get == NULL) 190 fatalx("smi_insert: invalid MIB table"); 191 192 bzero(&key, sizeof(key)); 193 bcopy(&oid->o_id, &key.o_id, sizeof(struct ber_oid)); 194 value = RB_FIND(oidtree, &smi_oidtree, &key); 195 if (value != NULL) 196 return (-1); 197 198 RB_INSERT(oidtree, &smi_oidtree, oid); 199 return (0); 200 } 201 202 void 203 smi_mibtree(struct oid *oids) 204 { 205 struct oid *oid, *decl; 206 size_t i; 207 208 for (i = 0; oids[i].o_oid[0] != 0; i++) { 209 oid = &oids[i]; 210 smi_oidlen(&oid->o_id); 211 if (oid->o_name != NULL) { 212 if ((oid->o_flags & OID_TABLE) && oid->o_get == NULL) 213 fatalx("smi_mibtree: invalid MIB table"); 214 RB_INSERT(oidtree, &smi_oidtree, oid); 215 RB_INSERT(keytree, &smi_keytree, oid); 216 continue; 217 } 218 decl = RB_FIND(oidtree, &smi_oidtree, oid); 219 if (decl == NULL) 220 fatalx("smi_mibtree: undeclared MIB"); 221 decl->o_flags = oid->o_flags; 222 decl->o_get = oid->o_get; 223 decl->o_set = oid->o_set; 224 decl->o_table = oid->o_table; 225 decl->o_val = oid->o_val; 226 decl->o_data = oid->o_data; 227 } 228 } 229 230 int 231 smi_init(void) 232 { 233 /* Initialize the Structure of Managed Information (SMI) */ 234 RB_INIT(&smi_oidtree); 235 mib_init(); 236 return (0); 237 } 238 239 struct oid * 240 smi_find(struct oid *oid) 241 { 242 return (RB_FIND(oidtree, &smi_oidtree, oid)); 243 } 244 245 struct oid * 246 smi_nfind(struct oid *oid) 247 { 248 return (RB_NFIND(oidtree, &smi_oidtree, oid)); 249 } 250 251 struct oid * 252 smi_findkey(char *name) 253 { 254 struct oid oid; 255 if (name == NULL) 256 return (NULL); 257 oid.o_name = name; 258 return (RB_FIND(keytree, &smi_keytree, &oid)); 259 } 260 261 struct oid * 262 smi_next(struct oid *oid) 263 { 264 return (RB_NEXT(oidtree, &smi_oidtree, oid)); 265 } 266 267 struct oid * 268 smi_foreach(struct oid *oid, u_int flags) 269 { 270 /* 271 * Traverse the tree of MIBs with the option to check 272 * for specific OID flags. 273 */ 274 if (oid == NULL) { 275 oid = RB_MIN(oidtree, &smi_oidtree); 276 if (oid == NULL) 277 return (NULL); 278 if (flags == 0 || (oid->o_flags & flags)) 279 return (oid); 280 } 281 for (;;) { 282 oid = RB_NEXT(oidtree, &smi_oidtree, oid); 283 if (oid == NULL) 284 break; 285 if (flags == 0 || (oid->o_flags & flags)) 286 return (oid); 287 } 288 289 return (oid); 290 } 291 292 #ifdef DEBUG 293 void 294 smi_debug_elements(struct ber_element *root) 295 { 296 static int indent = 0; 297 char *value; 298 int constructed; 299 300 /* calculate lengths */ 301 ober_calc_len(root); 302 303 switch (root->be_encoding) { 304 case BER_TYPE_SEQUENCE: 305 case BER_TYPE_SET: 306 constructed = root->be_encoding; 307 break; 308 default: 309 constructed = 0; 310 break; 311 } 312 313 fprintf(stderr, "%*slen %lu ", indent, "", root->be_len); 314 switch (root->be_class) { 315 case BER_CLASS_UNIVERSAL: 316 fprintf(stderr, "class: universal(%u) type: ", root->be_class); 317 switch (root->be_type) { 318 case BER_TYPE_EOC: 319 fprintf(stderr, "end-of-content"); 320 break; 321 case BER_TYPE_INTEGER: 322 fprintf(stderr, "integer"); 323 break; 324 case BER_TYPE_BITSTRING: 325 fprintf(stderr, "bit-string"); 326 break; 327 case BER_TYPE_OCTETSTRING: 328 fprintf(stderr, "octet-string"); 329 break; 330 case BER_TYPE_NULL: 331 fprintf(stderr, "null"); 332 break; 333 case BER_TYPE_OBJECT: 334 fprintf(stderr, "object"); 335 break; 336 case BER_TYPE_ENUMERATED: 337 fprintf(stderr, "enumerated"); 338 break; 339 case BER_TYPE_SEQUENCE: 340 fprintf(stderr, "sequence"); 341 break; 342 case BER_TYPE_SET: 343 fprintf(stderr, "set"); 344 break; 345 } 346 break; 347 case BER_CLASS_APPLICATION: 348 fprintf(stderr, "class: application(%u) type: ", 349 root->be_class); 350 switch (root->be_type) { 351 case SNMP_T_IPADDR: 352 fprintf(stderr, "ipaddr"); 353 break; 354 case SNMP_T_COUNTER32: 355 fprintf(stderr, "counter32"); 356 break; 357 case SNMP_T_GAUGE32: 358 fprintf(stderr, "gauge32"); 359 break; 360 case SNMP_T_TIMETICKS: 361 fprintf(stderr, "timeticks"); 362 break; 363 case SNMP_T_OPAQUE: 364 fprintf(stderr, "opaque"); 365 break; 366 case SNMP_T_COUNTER64: 367 fprintf(stderr, "counter64"); 368 break; 369 } 370 break; 371 case BER_CLASS_CONTEXT: 372 fprintf(stderr, "class: context(%u) type: ", 373 root->be_class); 374 switch (root->be_type) { 375 case SNMP_C_GETREQ: 376 fprintf(stderr, "getreq"); 377 break; 378 case SNMP_C_GETNEXTREQ: 379 fprintf(stderr, "getnextreq"); 380 break; 381 case SNMP_C_RESPONSE: 382 fprintf(stderr, "response"); 383 break; 384 case SNMP_C_SETREQ: 385 fprintf(stderr, "setreq"); 386 break; 387 case SNMP_C_TRAP: 388 fprintf(stderr, "trap"); 389 break; 390 case SNMP_C_GETBULKREQ: 391 fprintf(stderr, "getbulkreq"); 392 break; 393 case SNMP_C_INFORMREQ: 394 fprintf(stderr, "informreq"); 395 break; 396 case SNMP_C_TRAPV2: 397 fprintf(stderr, "trapv2"); 398 break; 399 case SNMP_C_REPORT: 400 fprintf(stderr, "report"); 401 break; 402 } 403 break; 404 case BER_CLASS_PRIVATE: 405 fprintf(stderr, "class: private(%u) type: ", root->be_class); 406 break; 407 default: 408 fprintf(stderr, "class: <INVALID>(%u) type: ", root->be_class); 409 break; 410 } 411 fprintf(stderr, "(%u) encoding %u ", 412 root->be_type, root->be_encoding); 413 414 if ((value = smi_print_element(root)) == NULL) 415 goto invalid; 416 417 switch (root->be_encoding) { 418 case BER_TYPE_INTEGER: 419 case BER_TYPE_ENUMERATED: 420 fprintf(stderr, "value %s", value); 421 break; 422 case BER_TYPE_BITSTRING: 423 fprintf(stderr, "hexdump %s", value); 424 break; 425 case BER_TYPE_OBJECT: 426 fprintf(stderr, "oid %s", value); 427 break; 428 case BER_TYPE_OCTETSTRING: 429 if (root->be_class == BER_CLASS_APPLICATION && 430 root->be_type == SNMP_T_IPADDR) { 431 fprintf(stderr, "addr %s", value); 432 } else { 433 fprintf(stderr, "string %s", value); 434 } 435 break; 436 case BER_TYPE_NULL: /* no payload */ 437 case BER_TYPE_EOC: 438 case BER_TYPE_SEQUENCE: 439 case BER_TYPE_SET: 440 default: 441 fprintf(stderr, "%s", value); 442 break; 443 } 444 445 invalid: 446 if (value == NULL) 447 fprintf(stderr, "<INVALID>"); 448 else 449 free(value); 450 fprintf(stderr, "\n"); 451 452 if (constructed) 453 root->be_encoding = constructed; 454 455 if (constructed && root->be_sub) { 456 indent += 2; 457 smi_debug_elements(root->be_sub); 458 indent -= 2; 459 } 460 if (root->be_next) 461 smi_debug_elements(root->be_next); 462 } 463 #endif 464 465 /* Keep around so trap handle scripts don't break */ 466 char * 467 smi_print_element_legacy(struct ber_element *root) 468 { 469 char *str = NULL, *buf, *p; 470 size_t len, i; 471 long long v; 472 struct ber_oid o; 473 char strbuf[BUFSIZ]; 474 475 switch (root->be_encoding) { 476 case BER_TYPE_INTEGER: 477 case BER_TYPE_ENUMERATED: 478 if (ober_get_integer(root, &v) == -1) 479 goto fail; 480 if (asprintf(&str, "%lld", v) == -1) 481 goto fail; 482 break; 483 case BER_TYPE_BITSTRING: 484 if (ober_get_bitstring(root, (void *)&buf, &len) == -1) 485 goto fail; 486 if ((str = calloc(1, len * 2 + 1)) == NULL) 487 goto fail; 488 for (p = str, i = 0; i < len; i++) { 489 snprintf(p, 3, "%02x", buf[i]); 490 p += 2; 491 } 492 break; 493 case BER_TYPE_OBJECT: 494 if (ober_get_oid(root, &o) == -1) 495 goto fail; 496 if (asprintf(&str, "%s", 497 smi_oid2string(&o, strbuf, sizeof(strbuf), 0)) == -1) 498 goto fail; 499 break; 500 case BER_TYPE_OCTETSTRING: 501 if (ober_get_string(root, &buf) == -1) 502 goto fail; 503 if (root->be_class == BER_CLASS_APPLICATION && 504 root->be_type == SNMP_T_IPADDR) { 505 if (asprintf(&str, "%s", 506 inet_ntoa(*(struct in_addr *)buf)) == -1) 507 goto fail; 508 } else { 509 if ((p = reallocarray(NULL, 4, root->be_len + 1)) == NULL) 510 goto fail; 511 strvisx(p, buf, root->be_len, VIS_NL); 512 if (asprintf(&str, "\"%s\"", p) == -1) { 513 free(p); 514 goto fail; 515 } 516 free(p); 517 } 518 break; 519 case BER_TYPE_NULL: /* no payload */ 520 case BER_TYPE_EOC: 521 case BER_TYPE_SEQUENCE: 522 case BER_TYPE_SET: 523 default: 524 str = strdup(""); 525 break; 526 } 527 528 return (str); 529 530 fail: 531 free(str); 532 return (NULL); 533 } 534 535 char * 536 smi_print_element(struct ber_element *root) 537 { 538 char *str = NULL, *buf, *p; 539 long long v; 540 struct ber_oid o; 541 char strbuf[BUFSIZ]; 542 543 switch (root->be_class) { 544 case BER_CLASS_UNIVERSAL: 545 switch (root->be_type) { 546 case BER_TYPE_INTEGER: 547 if (ober_get_integer(root, &v) == -1) 548 goto fail; 549 if (asprintf(&str, "%lld", v) == -1) 550 goto fail; 551 break; 552 case BER_TYPE_OBJECT: 553 if (ober_get_oid(root, &o) == -1) 554 goto fail; 555 if (asprintf(&str, "%s", smi_oid2string(&o, strbuf, 556 sizeof(strbuf), 0)) == -1) 557 goto fail; 558 break; 559 case BER_TYPE_OCTETSTRING: 560 if (ober_get_string(root, &buf) == -1) 561 goto fail; 562 p = reallocarray(NULL, 4, root->be_len + 1); 563 if (p == NULL) 564 goto fail; 565 strvisx(p, buf, root->be_len, VIS_NL); 566 if (asprintf(&str, "\"%s\"", p) == -1) { 567 free(p); 568 goto fail; 569 } 570 free(p); 571 break; 572 case BER_TYPE_NULL: 573 if (asprintf(&str, "null") == -1) 574 goto fail; 575 break; 576 default: 577 /* Should not happen in a valid SNMP packet */ 578 if (asprintf(&str, "[U/%u]", root->be_type) == -1) 579 goto fail; 580 break; 581 } 582 break; 583 case BER_CLASS_APPLICATION: 584 switch (root->be_type) { 585 case SNMP_T_IPADDR: 586 if (ober_get_string(root, &buf) == -1) 587 goto fail; 588 if (asprintf(&str, "%s", 589 inet_ntoa(*(struct in_addr *)buf)) == -1) 590 goto fail; 591 break; 592 case SNMP_T_COUNTER32: 593 if (ober_get_integer(root, &v) == -1) 594 goto fail; 595 if (asprintf(&str, "%lld(c32)", v) == -1) 596 goto fail; 597 break; 598 case SNMP_T_GAUGE32: 599 if (ober_get_integer(root, &v) == -1) 600 goto fail; 601 if (asprintf(&str, "%lld(g32)", v) == -1) 602 goto fail; 603 break; 604 case SNMP_T_TIMETICKS: 605 if (ober_get_integer(root, &v) == -1) 606 goto fail; 607 if (asprintf(&str, "%lld.%llds", v/100, v%100) == -1) 608 goto fail; 609 break; 610 case SNMP_T_OPAQUE: 611 if (ober_get_string(root, &buf) == -1) 612 goto fail; 613 p = reallocarray(NULL, 4, root->be_len + 1); 614 if (p == NULL) 615 goto fail; 616 strvisx(p, buf, root->be_len, VIS_NL); 617 if (asprintf(&str, "\"%s\"(opaque)", p) == -1) { 618 free(p); 619 goto fail; 620 } 621 free(p); 622 break; 623 case SNMP_T_COUNTER64: 624 if (ober_get_integer(root, &v) == -1) 625 goto fail; 626 if (asprintf(&str, "%lld(c64)", v) == -1) 627 goto fail; 628 break; 629 default: 630 /* Should not happen in a valid SNMP packet */ 631 if (asprintf(&str, "[A/%u]", root->be_type) == -1) 632 goto fail; 633 break; 634 } 635 break; 636 case BER_CLASS_CONTEXT: 637 switch (root->be_type) { 638 case SNMP_V_NOSUCHOBJECT: 639 str = strdup("noSuchObject"); 640 break; 641 case SNMP_V_NOSUCHINSTANCE: 642 str = strdup("noSuchInstance"); 643 break; 644 case SNMP_V_ENDOFMIBVIEW: 645 str = strdup("endOfMibView"); 646 break; 647 default: 648 /* Should not happen in a valid SNMP packet */ 649 if (asprintf(&str, "[C/%u]", root->be_type) == -1) 650 goto fail; 651 break; 652 } 653 break; 654 default: 655 /* Should not happen in a valid SNMP packet */ 656 if (asprintf(&str, "[%hhu/%u]", root->be_class, 657 root->be_type) == -1) 658 goto fail; 659 break; 660 } 661 662 return (str); 663 664 fail: 665 free(str); 666 return (NULL); 667 } 668 669 unsigned int 670 smi_application(struct ber_element *elm) 671 { 672 if (elm->be_class != BER_CLASS_APPLICATION) 673 return (BER_TYPE_OCTETSTRING); 674 675 switch (elm->be_type) { 676 case SNMP_T_IPADDR: 677 return (BER_TYPE_OCTETSTRING); 678 case SNMP_T_COUNTER32: 679 case SNMP_T_GAUGE32: 680 case SNMP_T_TIMETICKS: 681 case SNMP_T_OPAQUE: 682 case SNMP_T_COUNTER64: 683 return (BER_TYPE_INTEGER); 684 default: 685 break; 686 } 687 return (BER_TYPE_OCTETSTRING); 688 } 689 690 int 691 smi_oid_cmp(struct oid *a, struct oid *b) 692 { 693 size_t i; 694 695 for (i = 0; i < MINIMUM(a->o_oidlen, b->o_oidlen); i++) 696 if (a->o_oid[i] != b->o_oid[i]) 697 return (a->o_oid[i] - b->o_oid[i]); 698 699 /* 700 * Return success if the matched object is a table 701 * or a MIB registered by a subagent 702 * (it will match any sub-elements) 703 */ 704 if ((b->o_flags & OID_TABLE || 705 b->o_flags & OID_REGISTERED) && 706 (a->o_flags & OID_KEY) == 0 && 707 (a->o_oidlen > b->o_oidlen)) 708 return (0); 709 710 return (a->o_oidlen - b->o_oidlen); 711 } 712 713 RB_GENERATE(oidtree, oid, o_element, smi_oid_cmp); 714 715 int 716 smi_key_cmp(struct oid *a, struct oid *b) 717 { 718 if (a->o_name == NULL || b->o_name == NULL) 719 return (-1); 720 return (strcasecmp(a->o_name, b->o_name)); 721 } 722 723 RB_GENERATE(keytree, oid, o_keyword, smi_key_cmp); 724