1 /* $OpenBSD: smi.c,v 1.32 2022/10/06 14:41:08 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_table = oid->o_table; 224 decl->o_val = oid->o_val; 225 decl->o_data = oid->o_data; 226 } 227 } 228 229 int 230 smi_init(void) 231 { 232 /* Initialize the Structure of Managed Information (SMI) */ 233 RB_INIT(&smi_oidtree); 234 mib_init(); 235 return (0); 236 } 237 238 struct oid * 239 smi_find(struct oid *oid) 240 { 241 return (RB_FIND(oidtree, &smi_oidtree, oid)); 242 } 243 244 struct oid * 245 smi_nfind(struct oid *oid) 246 { 247 return (RB_NFIND(oidtree, &smi_oidtree, oid)); 248 } 249 250 struct oid * 251 smi_findkey(char *name) 252 { 253 struct oid oid; 254 if (name == NULL) 255 return (NULL); 256 oid.o_name = name; 257 return (RB_FIND(keytree, &smi_keytree, &oid)); 258 } 259 260 struct oid * 261 smi_next(struct oid *oid) 262 { 263 return (RB_NEXT(oidtree, &smi_oidtree, oid)); 264 } 265 266 struct oid * 267 smi_foreach(struct oid *oid, u_int flags) 268 { 269 /* 270 * Traverse the tree of MIBs with the option to check 271 * for specific OID flags. 272 */ 273 if (oid == NULL) { 274 oid = RB_MIN(oidtree, &smi_oidtree); 275 if (oid == NULL) 276 return (NULL); 277 if (flags == 0 || (oid->o_flags & flags)) 278 return (oid); 279 } 280 for (;;) { 281 oid = RB_NEXT(oidtree, &smi_oidtree, oid); 282 if (oid == NULL) 283 break; 284 if (flags == 0 || (oid->o_flags & flags)) 285 return (oid); 286 } 287 288 return (oid); 289 } 290 291 #ifdef DEBUG 292 void 293 smi_debug_elements(struct ber_element *root) 294 { 295 static int indent = 0; 296 char *value; 297 int constructed; 298 299 /* calculate lengths */ 300 ober_calc_len(root); 301 302 switch (root->be_encoding) { 303 case BER_TYPE_SEQUENCE: 304 case BER_TYPE_SET: 305 constructed = root->be_encoding; 306 break; 307 default: 308 constructed = 0; 309 break; 310 } 311 312 fprintf(stderr, "%*slen %lu ", indent, "", root->be_len); 313 switch (root->be_class) { 314 case BER_CLASS_UNIVERSAL: 315 fprintf(stderr, "class: universal(%u) type: ", root->be_class); 316 switch (root->be_type) { 317 case BER_TYPE_EOC: 318 fprintf(stderr, "end-of-content"); 319 break; 320 case BER_TYPE_INTEGER: 321 fprintf(stderr, "integer"); 322 break; 323 case BER_TYPE_BITSTRING: 324 fprintf(stderr, "bit-string"); 325 break; 326 case BER_TYPE_OCTETSTRING: 327 fprintf(stderr, "octet-string"); 328 break; 329 case BER_TYPE_NULL: 330 fprintf(stderr, "null"); 331 break; 332 case BER_TYPE_OBJECT: 333 fprintf(stderr, "object"); 334 break; 335 case BER_TYPE_ENUMERATED: 336 fprintf(stderr, "enumerated"); 337 break; 338 case BER_TYPE_SEQUENCE: 339 fprintf(stderr, "sequence"); 340 break; 341 case BER_TYPE_SET: 342 fprintf(stderr, "set"); 343 break; 344 } 345 break; 346 case BER_CLASS_APPLICATION: 347 fprintf(stderr, "class: application(%u) type: ", 348 root->be_class); 349 switch (root->be_type) { 350 case SNMP_T_IPADDR: 351 fprintf(stderr, "ipaddr"); 352 break; 353 case SNMP_T_COUNTER32: 354 fprintf(stderr, "counter32"); 355 break; 356 case SNMP_T_GAUGE32: 357 fprintf(stderr, "gauge32"); 358 break; 359 case SNMP_T_TIMETICKS: 360 fprintf(stderr, "timeticks"); 361 break; 362 case SNMP_T_OPAQUE: 363 fprintf(stderr, "opaque"); 364 break; 365 case SNMP_T_COUNTER64: 366 fprintf(stderr, "counter64"); 367 break; 368 } 369 break; 370 case BER_CLASS_CONTEXT: 371 fprintf(stderr, "class: context(%u) type: ", 372 root->be_class); 373 switch (root->be_type) { 374 case SNMP_C_GETREQ: 375 fprintf(stderr, "getreq"); 376 break; 377 case SNMP_C_GETNEXTREQ: 378 fprintf(stderr, "getnextreq"); 379 break; 380 case SNMP_C_RESPONSE: 381 fprintf(stderr, "response"); 382 break; 383 case SNMP_C_SETREQ: 384 fprintf(stderr, "setreq"); 385 break; 386 case SNMP_C_TRAP: 387 fprintf(stderr, "trap"); 388 break; 389 case SNMP_C_GETBULKREQ: 390 fprintf(stderr, "getbulkreq"); 391 break; 392 case SNMP_C_INFORMREQ: 393 fprintf(stderr, "informreq"); 394 break; 395 case SNMP_C_TRAPV2: 396 fprintf(stderr, "trapv2"); 397 break; 398 case SNMP_C_REPORT: 399 fprintf(stderr, "report"); 400 break; 401 } 402 break; 403 case BER_CLASS_PRIVATE: 404 fprintf(stderr, "class: private(%u) type: ", root->be_class); 405 break; 406 default: 407 fprintf(stderr, "class: <INVALID>(%u) type: ", root->be_class); 408 break; 409 } 410 fprintf(stderr, "(%u) encoding %u ", 411 root->be_type, root->be_encoding); 412 413 if ((value = smi_print_element(root)) == NULL) 414 goto invalid; 415 416 switch (root->be_encoding) { 417 case BER_TYPE_INTEGER: 418 case BER_TYPE_ENUMERATED: 419 fprintf(stderr, "value %s", value); 420 break; 421 case BER_TYPE_BITSTRING: 422 fprintf(stderr, "hexdump %s", value); 423 break; 424 case BER_TYPE_OBJECT: 425 fprintf(stderr, "oid %s", value); 426 break; 427 case BER_TYPE_OCTETSTRING: 428 if (root->be_class == BER_CLASS_APPLICATION && 429 root->be_type == SNMP_T_IPADDR) { 430 fprintf(stderr, "addr %s", value); 431 } else { 432 fprintf(stderr, "string %s", value); 433 } 434 break; 435 case BER_TYPE_NULL: /* no payload */ 436 case BER_TYPE_EOC: 437 case BER_TYPE_SEQUENCE: 438 case BER_TYPE_SET: 439 default: 440 fprintf(stderr, "%s", value); 441 break; 442 } 443 444 invalid: 445 if (value == NULL) 446 fprintf(stderr, "<INVALID>"); 447 else 448 free(value); 449 fprintf(stderr, "\n"); 450 451 if (constructed) 452 root->be_encoding = constructed; 453 454 if (constructed && root->be_sub) { 455 indent += 2; 456 smi_debug_elements(root->be_sub); 457 indent -= 2; 458 } 459 if (root->be_next) 460 smi_debug_elements(root->be_next); 461 } 462 #endif 463 464 /* Keep around so trap handle scripts don't break */ 465 char * 466 smi_print_element_legacy(struct ber_element *root) 467 { 468 char *str = NULL, *buf, *p; 469 size_t len, i; 470 long long v; 471 struct ber_oid o; 472 char strbuf[BUFSIZ]; 473 474 switch (root->be_encoding) { 475 case BER_TYPE_INTEGER: 476 case BER_TYPE_ENUMERATED: 477 if (ober_get_integer(root, &v) == -1) 478 goto fail; 479 if (asprintf(&str, "%lld", v) == -1) 480 goto fail; 481 break; 482 case BER_TYPE_BITSTRING: 483 if (ober_get_bitstring(root, (void *)&buf, &len) == -1) 484 goto fail; 485 if ((str = calloc(1, len * 2 + 1)) == NULL) 486 goto fail; 487 for (p = str, i = 0; i < len; i++) { 488 snprintf(p, 3, "%02x", buf[i]); 489 p += 2; 490 } 491 break; 492 case BER_TYPE_OBJECT: 493 if (ober_get_oid(root, &o) == -1) 494 goto fail; 495 if (asprintf(&str, "%s", 496 smi_oid2string(&o, strbuf, sizeof(strbuf), 0)) == -1) 497 goto fail; 498 break; 499 case BER_TYPE_OCTETSTRING: 500 if (ober_get_string(root, &buf) == -1) 501 goto fail; 502 if (root->be_class == BER_CLASS_APPLICATION && 503 root->be_type == SNMP_T_IPADDR) { 504 if (asprintf(&str, "%s", 505 inet_ntoa(*(struct in_addr *)buf)) == -1) 506 goto fail; 507 } else { 508 if ((p = reallocarray(NULL, 4, root->be_len + 1)) == NULL) 509 goto fail; 510 strvisx(p, buf, root->be_len, VIS_NL); 511 if (asprintf(&str, "\"%s\"", p) == -1) { 512 free(p); 513 goto fail; 514 } 515 free(p); 516 } 517 break; 518 case BER_TYPE_NULL: /* no payload */ 519 case BER_TYPE_EOC: 520 case BER_TYPE_SEQUENCE: 521 case BER_TYPE_SET: 522 default: 523 str = strdup(""); 524 break; 525 } 526 527 return (str); 528 529 fail: 530 free(str); 531 return (NULL); 532 } 533 534 char * 535 smi_print_element(struct ber_element *root) 536 { 537 char *str = NULL, *buf, *p; 538 long long v; 539 struct ber_oid o; 540 char strbuf[BUFSIZ]; 541 542 switch (root->be_class) { 543 case BER_CLASS_UNIVERSAL: 544 switch (root->be_type) { 545 case BER_TYPE_INTEGER: 546 if (ober_get_integer(root, &v) == -1) 547 goto fail; 548 if (asprintf(&str, "%lld", v) == -1) 549 goto fail; 550 break; 551 case BER_TYPE_OBJECT: 552 if (ober_get_oid(root, &o) == -1) 553 goto fail; 554 if (asprintf(&str, "%s", smi_oid2string(&o, strbuf, 555 sizeof(strbuf), 0)) == -1) 556 goto fail; 557 break; 558 case BER_TYPE_OCTETSTRING: 559 if (ober_get_string(root, &buf) == -1) 560 goto fail; 561 p = reallocarray(NULL, 4, root->be_len + 1); 562 if (p == NULL) 563 goto fail; 564 strvisx(p, buf, root->be_len, VIS_NL); 565 if (asprintf(&str, "\"%s\"", p) == -1) { 566 free(p); 567 goto fail; 568 } 569 free(p); 570 break; 571 case BER_TYPE_NULL: 572 if (asprintf(&str, "null") == -1) 573 goto fail; 574 break; 575 default: 576 /* Should not happen in a valid SNMP packet */ 577 if (asprintf(&str, "[U/%u]", root->be_type) == -1) 578 goto fail; 579 break; 580 } 581 break; 582 case BER_CLASS_APPLICATION: 583 switch (root->be_type) { 584 case SNMP_T_IPADDR: 585 if (ober_get_string(root, &buf) == -1) 586 goto fail; 587 if (asprintf(&str, "%s", 588 inet_ntoa(*(struct in_addr *)buf)) == -1) 589 goto fail; 590 break; 591 case SNMP_T_COUNTER32: 592 if (ober_get_integer(root, &v) == -1) 593 goto fail; 594 if (asprintf(&str, "%lld(c32)", v) == -1) 595 goto fail; 596 break; 597 case SNMP_T_GAUGE32: 598 if (ober_get_integer(root, &v) == -1) 599 goto fail; 600 if (asprintf(&str, "%lld(g32)", v) == -1) 601 goto fail; 602 break; 603 case SNMP_T_TIMETICKS: 604 if (ober_get_integer(root, &v) == -1) 605 goto fail; 606 if (asprintf(&str, "%lld.%llds", v/100, v%100) == -1) 607 goto fail; 608 break; 609 case SNMP_T_OPAQUE: 610 if (ober_get_string(root, &buf) == -1) 611 goto fail; 612 p = reallocarray(NULL, 4, root->be_len + 1); 613 if (p == NULL) 614 goto fail; 615 strvisx(p, buf, root->be_len, VIS_NL); 616 if (asprintf(&str, "\"%s\"(opaque)", p) == -1) { 617 free(p); 618 goto fail; 619 } 620 free(p); 621 break; 622 case SNMP_T_COUNTER64: 623 if (ober_get_integer(root, &v) == -1) 624 goto fail; 625 if (asprintf(&str, "%lld(c64)", v) == -1) 626 goto fail; 627 break; 628 default: 629 /* Should not happen in a valid SNMP packet */ 630 if (asprintf(&str, "[A/%u]", root->be_type) == -1) 631 goto fail; 632 break; 633 } 634 break; 635 case BER_CLASS_CONTEXT: 636 switch (root->be_type) { 637 case SNMP_V_NOSUCHOBJECT: 638 str = strdup("noSuchObject"); 639 break; 640 case SNMP_V_NOSUCHINSTANCE: 641 str = strdup("noSuchInstance"); 642 break; 643 case SNMP_V_ENDOFMIBVIEW: 644 str = strdup("endOfMibView"); 645 break; 646 default: 647 /* Should not happen in a valid SNMP packet */ 648 if (asprintf(&str, "[C/%u]", root->be_type) == -1) 649 goto fail; 650 break; 651 } 652 break; 653 default: 654 /* Should not happen in a valid SNMP packet */ 655 if (asprintf(&str, "[%hhu/%u]", root->be_class, 656 root->be_type) == -1) 657 goto fail; 658 break; 659 } 660 661 return (str); 662 663 fail: 664 free(str); 665 return (NULL); 666 } 667 668 unsigned int 669 smi_application(struct ber_element *elm) 670 { 671 if (elm->be_class != BER_CLASS_APPLICATION) 672 return (BER_TYPE_OCTETSTRING); 673 674 switch (elm->be_type) { 675 case SNMP_T_IPADDR: 676 return (BER_TYPE_OCTETSTRING); 677 case SNMP_T_COUNTER32: 678 case SNMP_T_GAUGE32: 679 case SNMP_T_TIMETICKS: 680 case SNMP_T_OPAQUE: 681 case SNMP_T_COUNTER64: 682 return (BER_TYPE_INTEGER); 683 default: 684 break; 685 } 686 return (BER_TYPE_OCTETSTRING); 687 } 688 689 int 690 smi_oid_cmp(struct oid *a, struct oid *b) 691 { 692 size_t i; 693 694 for (i = 0; i < MINIMUM(a->o_oidlen, b->o_oidlen); i++) 695 if (a->o_oid[i] != b->o_oid[i]) 696 return (a->o_oid[i] - b->o_oid[i]); 697 698 /* 699 * Return success if the matched object is a table 700 * or a MIB registered by a subagent 701 * (it will match any sub-elements) 702 */ 703 if (b->o_flags & OID_TABLE && 704 (a->o_flags & OID_KEY) == 0 && 705 (a->o_oidlen > b->o_oidlen)) 706 return (0); 707 708 return (a->o_oidlen - b->o_oidlen); 709 } 710 711 RB_GENERATE(oidtree, oid, o_element, smi_oid_cmp); 712 713 int 714 smi_key_cmp(struct oid *a, struct oid *b) 715 { 716 if (a->o_name == NULL || b->o_name == NULL) 717 return (-1); 718 return (strcasecmp(a->o_name, b->o_name)); 719 } 720 721 RB_GENERATE(keytree, oid, o_keyword, smi_key_cmp); 722