1 /* $OpenBSD: smi.c,v 1.40 2024/02/06 12:44:27 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/tree.h> 20 #include <sys/time.h> 21 #include <sys/types.h> 22 23 #include <arpa/inet.h> 24 25 #include <ber.h> 26 #include <errno.h> 27 #include <limits.h> 28 #include <stdio.h> 29 #include <stdlib.h> 30 #include <string.h> 31 #include <strings.h> 32 #include <vis.h> 33 34 #include "log.h" 35 #include "mib.h" 36 #include "smi.h" 37 #include "snmp.h" 38 #include "snmpd.h" 39 40 struct oid { 41 struct ber_oid o_id; 42 #define o_oid o_id.bo_id 43 #define o_oidlen o_id.bo_n 44 45 char *o_name; 46 47 RB_ENTRY(oid) o_element; 48 RB_ENTRY(oid) o_keyword; 49 }; 50 51 void smi_mibtree(struct oid *); 52 struct oid *smi_findkey(char *); 53 int smi_oid_cmp(struct oid *, struct oid *); 54 int smi_key_cmp(struct oid *, struct oid *); 55 56 RB_HEAD(oidtree, oid); 57 RB_PROTOTYPE(oidtree, oid, o_element, smi_oid_cmp); 58 struct oidtree smi_oidtree; 59 static struct oid smi_objects[] = MIB_TREE; 60 61 RB_HEAD(keytree, oid); 62 RB_PROTOTYPE(keytree, oid, o_keyword, smi_key_cmp); 63 struct keytree smi_keytree; 64 65 u_long 66 smi_getticks(void) 67 { 68 struct timeval now, run; 69 u_long ticks; 70 71 gettimeofday(&now, NULL); 72 if (timercmp(&now, &snmpd_env->sc_starttime, <=)) 73 return (0); 74 timersub(&now, &snmpd_env->sc_starttime, &run); 75 ticks = run.tv_sec * 100; 76 if (run.tv_usec) 77 ticks += run.tv_usec / 10000; 78 79 return (ticks); 80 } 81 82 char * 83 smi_oid2string(struct ber_oid *o, char *buf, size_t len, size_t skip) 84 { 85 char str[256]; 86 struct oid *value, key; 87 size_t i, lookup = 1; 88 89 bzero(buf, len); 90 bzero(&key, sizeof(key)); 91 bcopy(o, &key.o_id, sizeof(struct ber_oid)); 92 93 if (snmpd_env->sc_flags & SNMPD_F_NONAMES) 94 lookup = 0; 95 96 for (i = 0; i < o->bo_n; i++) { 97 key.o_oidlen = i + 1; 98 if (lookup && skip > i) 99 continue; 100 if (lookup && 101 (value = RB_FIND(oidtree, &smi_oidtree, &key)) != NULL) 102 snprintf(str, sizeof(str), "%s", value->o_name); 103 else 104 snprintf(str, sizeof(str), "%d", key.o_oid[i]); 105 strlcat(buf, str, len); 106 if (i < (o->bo_n - 1)) 107 strlcat(buf, ".", len); 108 } 109 110 return (buf); 111 } 112 113 int 114 smi_string2oid(const char *oidstr, struct ber_oid *o) 115 { 116 char *sp, *p, str[BUFSIZ]; 117 const char *errstr; 118 struct oid *oid; 119 struct ber_oid ko; 120 121 if (strlcpy(str, oidstr, sizeof(str)) >= sizeof(str)) 122 return (-1); 123 bzero(o, sizeof(*o)); 124 125 /* 126 * Parse OID strings in the common form n.n.n or n-n-n. 127 * Based on ober_string2oid with additional support for symbolic names. 128 */ 129 for (p = sp = str; p != NULL; sp = p) { 130 if ((p = strpbrk(p, ".-")) != NULL) 131 *p++ = '\0'; 132 if ((oid = smi_findkey(sp)) != NULL) { 133 bcopy(&oid->o_id, &ko, sizeof(ko)); 134 if (o->bo_n && ober_oid_cmp(o, &ko) != 2) 135 return (-1); 136 bcopy(&ko, o, sizeof(*o)); 137 errstr = NULL; 138 } else { 139 o->bo_id[o->bo_n++] = 140 strtonum(sp, 0, UINT_MAX, &errstr); 141 } 142 if (errstr || o->bo_n > BER_MAX_OID_LEN) 143 return (-1); 144 } 145 146 return (0); 147 } 148 149 const char * 150 smi_insert(struct ber_oid *oid, const char *name) 151 { 152 struct oid *object; 153 154 if ((object = calloc(1, sizeof(*object))) == NULL) 155 return strerror(errno); 156 157 object->o_id = *oid; 158 if ((object->o_name = strdup(name)) == NULL) { 159 free(object); 160 return strerror(errno); 161 } 162 163 if (RB_INSERT(oidtree, &smi_oidtree, object) != NULL) { 164 free(object->o_name); 165 free(object); 166 return "duplicate oid"; 167 } 168 169 return NULL; 170 } 171 172 void 173 smi_mibtree(struct oid *oids) 174 { 175 struct oid *oid, *decl; 176 size_t i; 177 178 for (i = 0; oids[i].o_oid[0] != 0; i++) { 179 oid = &oids[i]; 180 if (oid->o_name != NULL) { 181 RB_INSERT(oidtree, &smi_oidtree, oid); 182 RB_INSERT(keytree, &smi_keytree, oid); 183 continue; 184 } 185 decl = RB_FIND(oidtree, &smi_oidtree, oid); 186 if (decl == NULL) 187 fatalx("smi_mibtree: undeclared MIB"); 188 } 189 } 190 191 int 192 smi_init(void) 193 { 194 /* Initialize the Structure of Managed Information (SMI) */ 195 RB_INIT(&smi_oidtree); 196 smi_mibtree(smi_objects); 197 return (0); 198 } 199 200 struct oid * 201 smi_findkey(char *name) 202 { 203 struct oid oid; 204 if (name == NULL) 205 return (NULL); 206 oid.o_name = name; 207 return (RB_FIND(keytree, &smi_keytree, &oid)); 208 } 209 210 #ifdef DEBUG 211 void 212 smi_debug_elements(struct ber_element *root) 213 { 214 static int indent = 0; 215 char *value; 216 int constructed; 217 218 /* calculate lengths */ 219 ober_calc_len(root); 220 221 switch (root->be_encoding) { 222 case BER_TYPE_SEQUENCE: 223 case BER_TYPE_SET: 224 constructed = root->be_encoding; 225 break; 226 default: 227 constructed = 0; 228 break; 229 } 230 231 fprintf(stderr, "%*slen %lu ", indent, "", root->be_len); 232 switch (root->be_class) { 233 case BER_CLASS_UNIVERSAL: 234 fprintf(stderr, "class: universal(%u) type: ", root->be_class); 235 switch (root->be_type) { 236 case BER_TYPE_EOC: 237 fprintf(stderr, "end-of-content"); 238 break; 239 case BER_TYPE_INTEGER: 240 fprintf(stderr, "integer"); 241 break; 242 case BER_TYPE_BITSTRING: 243 fprintf(stderr, "bit-string"); 244 break; 245 case BER_TYPE_OCTETSTRING: 246 fprintf(stderr, "octet-string"); 247 break; 248 case BER_TYPE_NULL: 249 fprintf(stderr, "null"); 250 break; 251 case BER_TYPE_OBJECT: 252 fprintf(stderr, "object"); 253 break; 254 case BER_TYPE_ENUMERATED: 255 fprintf(stderr, "enumerated"); 256 break; 257 case BER_TYPE_SEQUENCE: 258 fprintf(stderr, "sequence"); 259 break; 260 case BER_TYPE_SET: 261 fprintf(stderr, "set"); 262 break; 263 } 264 break; 265 case BER_CLASS_APPLICATION: 266 fprintf(stderr, "class: application(%u) type: ", 267 root->be_class); 268 switch (root->be_type) { 269 case SNMP_T_IPADDR: 270 fprintf(stderr, "ipaddr"); 271 break; 272 case SNMP_T_COUNTER32: 273 fprintf(stderr, "counter32"); 274 break; 275 case SNMP_T_GAUGE32: 276 fprintf(stderr, "gauge32"); 277 break; 278 case SNMP_T_TIMETICKS: 279 fprintf(stderr, "timeticks"); 280 break; 281 case SNMP_T_OPAQUE: 282 fprintf(stderr, "opaque"); 283 break; 284 case SNMP_T_COUNTER64: 285 fprintf(stderr, "counter64"); 286 break; 287 } 288 break; 289 case BER_CLASS_CONTEXT: 290 fprintf(stderr, "class: context(%u) type: ", 291 root->be_class); 292 switch (root->be_type) { 293 case SNMP_C_GETREQ: 294 fprintf(stderr, "getreq"); 295 break; 296 case SNMP_C_GETNEXTREQ: 297 fprintf(stderr, "getnextreq"); 298 break; 299 case SNMP_C_RESPONSE: 300 fprintf(stderr, "response"); 301 break; 302 case SNMP_C_SETREQ: 303 fprintf(stderr, "setreq"); 304 break; 305 case SNMP_C_TRAP: 306 fprintf(stderr, "trap"); 307 break; 308 case SNMP_C_GETBULKREQ: 309 fprintf(stderr, "getbulkreq"); 310 break; 311 case SNMP_C_INFORMREQ: 312 fprintf(stderr, "informreq"); 313 break; 314 case SNMP_C_TRAPV2: 315 fprintf(stderr, "trapv2"); 316 break; 317 case SNMP_C_REPORT: 318 fprintf(stderr, "report"); 319 break; 320 } 321 break; 322 case BER_CLASS_PRIVATE: 323 fprintf(stderr, "class: private(%u) type: ", root->be_class); 324 break; 325 default: 326 fprintf(stderr, "class: <INVALID>(%u) type: ", root->be_class); 327 break; 328 } 329 fprintf(stderr, "(%u) encoding %u ", 330 root->be_type, root->be_encoding); 331 332 if ((value = smi_print_element(root)) == NULL) 333 goto invalid; 334 335 switch (root->be_encoding) { 336 case BER_TYPE_INTEGER: 337 case BER_TYPE_ENUMERATED: 338 fprintf(stderr, "value %s", value); 339 break; 340 case BER_TYPE_BITSTRING: 341 fprintf(stderr, "hexdump %s", value); 342 break; 343 case BER_TYPE_OBJECT: 344 fprintf(stderr, "oid %s", value); 345 break; 346 case BER_TYPE_OCTETSTRING: 347 if (root->be_class == BER_CLASS_APPLICATION && 348 root->be_type == SNMP_T_IPADDR) { 349 fprintf(stderr, "addr %s", value); 350 } else { 351 fprintf(stderr, "string %s", value); 352 } 353 break; 354 case BER_TYPE_NULL: /* no payload */ 355 case BER_TYPE_EOC: 356 case BER_TYPE_SEQUENCE: 357 case BER_TYPE_SET: 358 default: 359 fprintf(stderr, "%s", value); 360 break; 361 } 362 363 invalid: 364 if (value == NULL) 365 fprintf(stderr, "<INVALID>"); 366 else 367 free(value); 368 fprintf(stderr, "\n"); 369 370 if (constructed) 371 root->be_encoding = constructed; 372 373 if (constructed && root->be_sub) { 374 indent += 2; 375 smi_debug_elements(root->be_sub); 376 indent -= 2; 377 } 378 if (root->be_next) 379 smi_debug_elements(root->be_next); 380 } 381 #endif 382 383 /* Keep around so trap handle scripts don't break */ 384 char * 385 smi_print_element_legacy(struct ber_element *root) 386 { 387 char *str = NULL, *buf, *p; 388 size_t len, i; 389 long long v; 390 struct ber_oid o; 391 char strbuf[BUFSIZ]; 392 393 switch (root->be_encoding) { 394 case BER_TYPE_INTEGER: 395 case BER_TYPE_ENUMERATED: 396 if (ober_get_integer(root, &v) == -1) 397 goto fail; 398 if (asprintf(&str, "%lld", v) == -1) 399 goto fail; 400 break; 401 case BER_TYPE_BITSTRING: 402 if (ober_get_bitstring(root, (void *)&buf, &len) == -1) 403 goto fail; 404 if ((str = calloc(1, len * 2 + 1)) == NULL) 405 goto fail; 406 for (p = str, i = 0; i < len; i++) { 407 snprintf(p, 3, "%02x", buf[i]); 408 p += 2; 409 } 410 break; 411 case BER_TYPE_OBJECT: 412 if (ober_get_oid(root, &o) == -1) 413 goto fail; 414 if (asprintf(&str, "%s", 415 smi_oid2string(&o, strbuf, sizeof(strbuf), 0)) == -1) 416 goto fail; 417 break; 418 case BER_TYPE_OCTETSTRING: 419 if (ober_get_string(root, &buf) == -1) 420 goto fail; 421 if (root->be_class == BER_CLASS_APPLICATION && 422 root->be_type == SNMP_T_IPADDR) { 423 if (asprintf(&str, "%s", 424 inet_ntoa(*(struct in_addr *)buf)) == -1) 425 goto fail; 426 } else { 427 if ((p = reallocarray(NULL, 4, root->be_len + 1)) == NULL) 428 goto fail; 429 strvisx(p, buf, root->be_len, VIS_NL); 430 if (asprintf(&str, "\"%s\"", p) == -1) { 431 free(p); 432 goto fail; 433 } 434 free(p); 435 } 436 break; 437 case BER_TYPE_NULL: /* no payload */ 438 case BER_TYPE_EOC: 439 case BER_TYPE_SEQUENCE: 440 case BER_TYPE_SET: 441 default: 442 str = strdup(""); 443 break; 444 } 445 446 return (str); 447 448 fail: 449 free(str); 450 return (NULL); 451 } 452 453 char * 454 smi_print_element(struct ber_element *root) 455 { 456 char *str = NULL, *buf, *p; 457 long long v; 458 struct ber_oid o; 459 char strbuf[BUFSIZ]; 460 461 switch (root->be_class) { 462 case BER_CLASS_UNIVERSAL: 463 switch (root->be_type) { 464 case BER_TYPE_INTEGER: 465 if (ober_get_integer(root, &v) == -1) 466 goto fail; 467 if (asprintf(&str, "%lld", v) == -1) 468 goto fail; 469 break; 470 case BER_TYPE_OBJECT: 471 if (ober_get_oid(root, &o) == -1) 472 goto fail; 473 if (asprintf(&str, "%s", mib_oid2string(&o, strbuf, 474 sizeof(strbuf), snmpd_env->sc_oidfmt)) == -1) 475 goto fail; 476 break; 477 case BER_TYPE_OCTETSTRING: 478 if (ober_get_string(root, &buf) == -1) 479 goto fail; 480 p = reallocarray(NULL, 4, root->be_len + 1); 481 if (p == NULL) 482 goto fail; 483 strvisx(p, buf, root->be_len, VIS_NL); 484 if (asprintf(&str, "\"%s\"", p) == -1) { 485 free(p); 486 goto fail; 487 } 488 free(p); 489 break; 490 case BER_TYPE_NULL: 491 if (asprintf(&str, "null") == -1) 492 goto fail; 493 break; 494 default: 495 /* Should not happen in a valid SNMP packet */ 496 if (asprintf(&str, "[U/%u]", root->be_type) == -1) 497 goto fail; 498 break; 499 } 500 break; 501 case BER_CLASS_APPLICATION: 502 switch (root->be_type) { 503 case SNMP_T_IPADDR: 504 if (ober_get_string(root, &buf) == -1) 505 goto fail; 506 if (asprintf(&str, "%s", 507 inet_ntoa(*(struct in_addr *)buf)) == -1) 508 goto fail; 509 break; 510 case SNMP_T_COUNTER32: 511 if (ober_get_integer(root, &v) == -1) 512 goto fail; 513 if (asprintf(&str, "%lld(c32)", v) == -1) 514 goto fail; 515 break; 516 case SNMP_T_GAUGE32: 517 if (ober_get_integer(root, &v) == -1) 518 goto fail; 519 if (asprintf(&str, "%lld(g32)", v) == -1) 520 goto fail; 521 break; 522 case SNMP_T_TIMETICKS: 523 if (ober_get_integer(root, &v) == -1) 524 goto fail; 525 if (asprintf(&str, "%lld.%llds", v/100, v%100) == -1) 526 goto fail; 527 break; 528 case SNMP_T_OPAQUE: 529 if (ober_get_string(root, &buf) == -1) 530 goto fail; 531 p = reallocarray(NULL, 4, root->be_len + 1); 532 if (p == NULL) 533 goto fail; 534 strvisx(p, buf, root->be_len, VIS_NL); 535 if (asprintf(&str, "\"%s\"(opaque)", p) == -1) { 536 free(p); 537 goto fail; 538 } 539 free(p); 540 break; 541 case SNMP_T_COUNTER64: 542 if (ober_get_integer(root, &v) == -1) 543 goto fail; 544 if (asprintf(&str, "%lld(c64)", v) == -1) 545 goto fail; 546 break; 547 default: 548 /* Should not happen in a valid SNMP packet */ 549 if (asprintf(&str, "[A/%u]", root->be_type) == -1) 550 goto fail; 551 break; 552 } 553 break; 554 case BER_CLASS_CONTEXT: 555 switch (root->be_type) { 556 case SNMP_V_NOSUCHOBJECT: 557 str = strdup("noSuchObject"); 558 break; 559 case SNMP_V_NOSUCHINSTANCE: 560 str = strdup("noSuchInstance"); 561 break; 562 case SNMP_V_ENDOFMIBVIEW: 563 str = strdup("endOfMibView"); 564 break; 565 default: 566 /* Should not happen in a valid SNMP packet */ 567 if (asprintf(&str, "[C/%u]", root->be_type) == -1) 568 goto fail; 569 break; 570 } 571 break; 572 default: 573 /* Should not happen in a valid SNMP packet */ 574 if (asprintf(&str, "[%hhu/%u]", root->be_class, 575 root->be_type) == -1) 576 goto fail; 577 break; 578 } 579 580 return (str); 581 582 fail: 583 free(str); 584 return (NULL); 585 } 586 587 unsigned int 588 smi_application(struct ber_element *elm) 589 { 590 if (elm->be_class != BER_CLASS_APPLICATION) 591 return (BER_TYPE_OCTETSTRING); 592 593 switch (elm->be_type) { 594 case SNMP_T_IPADDR: 595 return (BER_TYPE_OCTETSTRING); 596 case SNMP_T_COUNTER32: 597 case SNMP_T_GAUGE32: 598 case SNMP_T_TIMETICKS: 599 case SNMP_T_OPAQUE: 600 case SNMP_T_COUNTER64: 601 return (BER_TYPE_INTEGER); 602 default: 603 break; 604 } 605 return (BER_TYPE_OCTETSTRING); 606 } 607 608 int 609 smi_oid_cmp(struct oid *a, struct oid *b) 610 { 611 return ober_oid_cmp(&a->o_id, &b->o_id); 612 } 613 614 RB_GENERATE(oidtree, oid, o_element, smi_oid_cmp); 615 616 int 617 smi_key_cmp(struct oid *a, struct oid *b) 618 { 619 if (a->o_name == NULL || b->o_name == NULL) 620 return (-1); 621 return (strcasecmp(a->o_name, b->o_name)); 622 } 623 624 RB_GENERATE(keytree, oid, o_keyword, smi_key_cmp); 625