smi.c revision 1.8
1/* $OpenBSD: smi.c,v 1.8 2020/05/19 13:41:01 martijn Exp $ */ 2 3/* 4 * Copyright (c) 2019 Martijn van Duren <martijn@openbsd.org> 5 * Copyright (c) 2007, 2008 Reyk Floeter <reyk@openbsd.org> 6 * 7 * Permission to use, copy, modify, and distribute this software for any 8 * purpose with or without fee is hereby granted, provided that the above 9 * copyright notice and this permission notice appear in all copies. 10 * 11 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 12 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 13 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 14 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 15 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 16 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 17 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 18 */ 19 20#include <sys/limits.h> 21#include <sys/tree.h> 22#include <sys/queue.h> 23 24#include <arpa/inet.h> 25 26#include <ctype.h> 27#include <stdlib.h> 28#include <stdio.h> 29#include <string.h> 30#include <strings.h> 31 32#include "ber.h" 33#include "mib.h" 34#include "snmp.h" 35#include "smi.h" 36 37#define MINIMUM(a, b) (((a) < (b)) ? (a) : (b)) 38 39int smi_oid_cmp(struct oid *, struct oid *); 40int smi_key_cmp(struct oid *, struct oid *); 41struct oid * smi_findkey(char *); 42 43RB_HEAD(oidtree, oid); 44RB_PROTOTYPE(oidtree, oid, o_element, smi_oid_cmp) 45struct oidtree smi_oidtree; 46 47RB_HEAD(keytree, oid); 48RB_PROTOTYPE(keytree, oid, o_keyword, smi_key_cmp) 49struct keytree smi_keytree; 50 51int 52smi_init(void) 53{ 54 /* Initialize the Structure of Managed Information (SMI) */ 55 RB_INIT(&smi_oidtree); 56 mib_init(); 57 return (0); 58} 59 60void 61smi_debug_elements(struct ber_element *root) 62{ 63 static int indent = 0; 64 char *value; 65 int constructed; 66 67 /* calculate lengths */ 68 ober_calc_len(root); 69 70 switch (root->be_encoding) { 71 case BER_TYPE_SEQUENCE: 72 case BER_TYPE_SET: 73 constructed = root->be_encoding; 74 break; 75 default: 76 constructed = 0; 77 break; 78 } 79 80 fprintf(stderr, "%*slen %lu ", indent, "", root->be_len); 81 switch (root->be_class) { 82 case BER_CLASS_UNIVERSAL: 83 fprintf(stderr, "class: universal(%u) type: ", root->be_class); 84 switch (root->be_type) { 85 case BER_TYPE_EOC: 86 fprintf(stderr, "end-of-content"); 87 break; 88 case BER_TYPE_BOOLEAN: 89 fprintf(stderr, "boolean"); 90 break; 91 case BER_TYPE_INTEGER: 92 fprintf(stderr, "integer"); 93 break; 94 case BER_TYPE_BITSTRING: 95 fprintf(stderr, "bit-string"); 96 break; 97 case BER_TYPE_OCTETSTRING: 98 fprintf(stderr, "octet-string"); 99 break; 100 case BER_TYPE_NULL: 101 fprintf(stderr, "null"); 102 break; 103 case BER_TYPE_OBJECT: 104 fprintf(stderr, "object"); 105 break; 106 case BER_TYPE_ENUMERATED: 107 fprintf(stderr, "enumerated"); 108 break; 109 case BER_TYPE_SEQUENCE: 110 fprintf(stderr, "sequence"); 111 break; 112 case BER_TYPE_SET: 113 fprintf(stderr, "set"); 114 break; 115 } 116 break; 117 case BER_CLASS_APPLICATION: 118 fprintf(stderr, "class: application(%u) type: ", 119 root->be_class); 120 switch (root->be_type) { 121 case SNMP_T_IPADDR: 122 fprintf(stderr, "ipaddr"); 123 break; 124 case SNMP_T_COUNTER32: 125 fprintf(stderr, "counter32"); 126 break; 127 case SNMP_T_GAUGE32: 128 fprintf(stderr, "gauge32"); 129 break; 130 case SNMP_T_TIMETICKS: 131 fprintf(stderr, "timeticks"); 132 break; 133 case SNMP_T_OPAQUE: 134 fprintf(stderr, "opaque"); 135 break; 136 case SNMP_T_COUNTER64: 137 fprintf(stderr, "counter64"); 138 break; 139 } 140 break; 141 case BER_CLASS_CONTEXT: 142 fprintf(stderr, "class: context(%u) type: ", 143 root->be_class); 144 switch (root->be_type) { 145 case SNMP_C_GETREQ: 146 fprintf(stderr, "getreq"); 147 break; 148 case SNMP_C_GETNEXTREQ: 149 fprintf(stderr, "nextreq"); 150 break; 151 case SNMP_C_GETRESP: 152 fprintf(stderr, "getresp"); 153 break; 154 case SNMP_C_SETREQ: 155 fprintf(stderr, "setreq"); 156 break; 157 case SNMP_C_TRAP: 158 fprintf(stderr, "trap"); 159 break; 160 case SNMP_C_GETBULKREQ: 161 fprintf(stderr, "getbulkreq"); 162 break; 163 case SNMP_C_INFORMREQ: 164 fprintf(stderr, "informreq"); 165 break; 166 case SNMP_C_TRAPV2: 167 fprintf(stderr, "trapv2"); 168 break; 169 case SNMP_C_REPORT: 170 fprintf(stderr, "report"); 171 break; 172 } 173 break; 174 case BER_CLASS_PRIVATE: 175 fprintf(stderr, "class: private(%u) type: ", root->be_class); 176 break; 177 default: 178 fprintf(stderr, "class: <INVALID>(%u) type: ", root->be_class); 179 break; 180 } 181 fprintf(stderr, "(%u) encoding %u ", 182 root->be_type, root->be_encoding); 183 184 if ((value = smi_print_element(root, 1, smi_os_default, 185 smi_oidl_numeric)) == NULL) 186 goto invalid; 187 188 switch (root->be_encoding) { 189 case BER_TYPE_BOOLEAN: 190 fprintf(stderr, "%s", value); 191 break; 192 case BER_TYPE_INTEGER: 193 case BER_TYPE_ENUMERATED: 194 fprintf(stderr, "value %s", value); 195 break; 196 case BER_TYPE_BITSTRING: 197 fprintf(stderr, "hexdump %s", value); 198 break; 199 case BER_TYPE_OBJECT: 200 fprintf(stderr, "oid %s", value); 201 break; 202 case BER_TYPE_OCTETSTRING: 203 if (root->be_class == BER_CLASS_APPLICATION && 204 root->be_type == SNMP_T_IPADDR) { 205 fprintf(stderr, "addr %s", value); 206 } else { 207 fprintf(stderr, "string %s", value); 208 } 209 break; 210 case BER_TYPE_NULL: /* no payload */ 211 case BER_TYPE_EOC: 212 case BER_TYPE_SEQUENCE: 213 case BER_TYPE_SET: 214 default: 215 fprintf(stderr, "%s", value); 216 break; 217 } 218 219 invalid: 220 if (value == NULL) 221 fprintf(stderr, "<INVALID>"); 222 else 223 free(value); 224 fprintf(stderr, "\n"); 225 226 if (constructed) 227 root->be_encoding = constructed; 228 229 if (constructed && root->be_sub) { 230 indent += 2; 231 smi_debug_elements(root->be_sub); 232 indent -= 2; 233 } 234 if (root->be_next) 235 smi_debug_elements(root->be_next); 236} 237 238char * 239smi_print_element(struct ber_element *root, int print_hint, 240 enum smi_output_string output_string, enum smi_oid_lookup lookup) 241{ 242 char *str = NULL, *buf, *p; 243 size_t len, i, slen; 244 long long v, ticks; 245 int d; 246 int is_hex = 0, ret; 247 struct ber_oid o; 248 char strbuf[BUFSIZ]; 249 char *hint; 250 int days, hours, min, sec, csec; 251 252 switch (root->be_encoding) { 253 case BER_TYPE_BOOLEAN: 254 if (ober_get_boolean(root, &d) == -1) 255 goto fail; 256 if (print_hint) { 257 if (asprintf(&str, "INTEGER: %s(%d)", 258 d ? "true" : "false", d) == -1) 259 goto fail; 260 } else 261 if (asprintf(&str, "%s", d ? "true" : "false") == -1) 262 goto fail; 263 break; 264 case BER_TYPE_INTEGER: 265 case BER_TYPE_ENUMERATED: 266 if (ober_get_integer(root, &v) == -1) 267 goto fail; 268 if (root->be_class == BER_CLASS_APPLICATION && 269 root->be_type == SNMP_T_TIMETICKS) { 270 ticks = v; 271 days = ticks / (60 * 60 * 24 * 100); 272 ticks %= (60 * 60 * 24 * 100); 273 hours = ticks / (60 * 60 * 100); 274 ticks %= (60 * 60 * 100); 275 min = ticks / (60 * 100); 276 ticks %= (60 * 100); 277 sec = ticks / 100; 278 ticks %= 100; 279 csec = ticks; 280 281 if (print_hint) { 282 if (days == 0) { 283 if (asprintf(&str, 284 "Timeticks: (%lld) " 285 "%d:%02d:%02d.%02d", 286 v, hours, min, sec, csec) == -1) 287 goto fail; 288 } else if (days == 1) { 289 if (asprintf(&str, 290 "Timeticks: (%lld) " 291 "1 day %d:%02d:%02d.%02d", 292 v, hours, min, sec, csec) == -1) 293 goto fail; 294 } else { 295 if (asprintf(&str, 296 "Timeticks: (%lld) " 297 "%d day %d:%02d:%02d.%02d", 298 v, days, hours, min, sec, csec) == 299 -1) 300 goto fail; 301 } 302 } else { 303 if (days == 0) { 304 if (asprintf(&str, "%d:%02d:%02d.%02d", 305 hours, min, sec, csec) == -1) 306 goto fail; 307 } else if (days == 1) { 308 if (asprintf(&str, 309 "1 day %d:%02d:%02d.%02d", 310 hours, min, sec, csec) == -1) 311 goto fail; 312 } else { 313 if (asprintf(&str, 314 "%d day %d:%02d:%02d.%02d", 315 days, hours, min, sec, csec) == -1) 316 goto fail; 317 } 318 } 319 break; 320 } 321 hint = "INTEGER: "; 322 if (root->be_class == BER_CLASS_APPLICATION) { 323 if (root->be_type == SNMP_T_COUNTER32) 324 hint = "Counter32: "; 325 else if (root->be_type == SNMP_T_GAUGE32) 326 hint = "Gauge32: "; 327 else if (root->be_type == SNMP_T_OPAQUE) 328 hint = "Opaque: "; 329 else if (root->be_type == SNMP_T_COUNTER64) 330 hint = "Counter64: "; 331 } 332 if (asprintf(&str, "%s%lld", print_hint ? hint : "", v) == -1) 333 goto fail; 334 break; 335 case BER_TYPE_BITSTRING: 336 if (ober_get_bitstring(root, (void *)&buf, &len) == -1) 337 goto fail; 338 slen = len * 2 + 1 + sizeof("BITS: "); 339 if ((str = calloc(1, slen)) == NULL) 340 goto fail; 341 p = str; 342 if (print_hint) { 343 strlcpy(str, "BITS: ", slen); 344 p += sizeof("BITS: "); 345 } 346 for (i = 0; i < len; i++) { 347 snprintf(p, 3, "%02x", buf[i]); 348 p += 2; 349 } 350 break; 351 case BER_TYPE_OBJECT: 352 if (ober_get_oid(root, &o) == -1) 353 goto fail; 354 if (asprintf(&str, "%s%s", 355 print_hint ? "OID: " : "", 356 smi_oid2string(&o, strbuf, sizeof(strbuf), lookup)) == -1) 357 goto fail; 358 break; 359 case BER_TYPE_OCTETSTRING: 360 if (ober_get_string(root, &buf) == -1) 361 goto fail; 362 if (root->be_class == BER_CLASS_APPLICATION && 363 root->be_type == SNMP_T_IPADDR) { 364 if (asprintf(&str, "%s%s", 365 print_hint ? "IpAddress: " : "", 366 inet_ntoa(*(struct in_addr *)buf)) == -1) 367 goto fail; 368 } else if (root->be_class == BER_CLASS_CONTEXT) { 369 if (root->be_type == SNMP_E_NOSUCHOBJECT) 370 str = strdup("No Such Object available on this " 371 "agent at this OID"); 372 else if (root->be_type == SNMP_E_NOSUCHINSTANCE) 373 str = strdup("No Such Instance currently " 374 "exists at this OID"); 375 else if (root->be_type == SNMP_E_ENDOFMIB) 376 str = strdup("No more variables left in this " 377 "MIB View (It is past the end of the MIB " 378 "tree)"); 379 else 380 str = strdup("Unknown status at this OID"); 381 } else { 382 for (i = 0; i < root->be_len; i++) { 383 if (!isprint(buf[i])) { 384 if (output_string == smi_os_default) 385 output_string = smi_os_hex; 386 else if (output_string == smi_os_ascii) 387 is_hex = 1; 388 break; 389 } 390 } 391 /* 392 * hex is 3 * n (2 digits + n - 1 spaces + NUL-byte) 393 * ascii can be max (2 * n) + 2 quotes + NUL-byte 394 */ 395 len = output_string == smi_os_hex ? 3 : 2; 396 p = str = reallocarray(NULL, root->be_len + 2, len); 397 if (p == NULL) 398 goto fail; 399 len *= root->be_len + 2; 400 if (is_hex) { 401 *str++ = '"'; 402 len--; 403 } 404 for (i = 0; i < root->be_len; i++) { 405 switch (output_string) { 406 case smi_os_default: 407 /* FALLTHROUGH */ 408 case smi_os_ascii: 409 /* 410 * There's probably more edgecases here, 411 * not fully investigated 412 */ 413 if (len < 2) 414 goto fail; 415 if (is_hex && buf[i] == '\\') { 416 *str++ = '\\'; 417 len--; 418 } 419 *str++ = isprint(buf[i]) ? buf[i] : '.'; 420 len--; 421 break; 422 case smi_os_hex: 423 ret = snprintf(str, len, "%s%02hhX", 424 i == 0 ? "" : 425 i % 16 == 0 ? "\n" : " ", buf[i]); 426 if (ret == -1 || ret > (int) len) 427 goto fail; 428 len -= ret; 429 str += ret; 430 break; 431 } 432 } 433 if (is_hex) { 434 if (len < 2) 435 goto fail; 436 *str++ = '"'; 437 len--; 438 } 439 if (len == 0) 440 goto fail; 441 *str = '\0'; 442 str = NULL; 443 if (asprintf(&str, "%s%s", 444 print_hint ? 445 output_string == smi_os_hex ? "Hex-STRING: " : 446 "STRING: " : 447 "", p) == -1) { 448 free(p); 449 goto fail; 450 } 451 free(p); 452 } 453 break; 454 case BER_TYPE_NULL: /* no payload */ 455 case BER_TYPE_EOC: 456 case BER_TYPE_SEQUENCE: 457 case BER_TYPE_SET: 458 default: 459 str = strdup(""); 460 break; 461 } 462 463 return (str); 464 465 fail: 466 free(str); 467 return (NULL); 468} 469 470int 471smi_string2oid(const char *oidstr, struct ber_oid *o) 472{ 473 char *sp, *p, str[BUFSIZ]; 474 const char *errstr; 475 struct oid *oid; 476 struct ber_oid ko; 477 478 if (strlcpy(str, oidstr, sizeof(str)) >= sizeof(str)) 479 return (-1); 480 bzero(o, sizeof(*o)); 481 482 /* 483 * Parse OID strings in the common form n.n.n or n-n-n. 484 * Based on ober_string2oid with additional support for symbolic names. 485 */ 486 p = sp = str[0] == '.' ? str + 1 : str; 487 for (; p != NULL; sp = p) { 488 if ((p = strpbrk(p, ".-")) != NULL) 489 *p++ = '\0'; 490 if ((oid = smi_findkey(sp)) != NULL) { 491 bcopy(&oid->o_id, &ko, sizeof(ko)); 492 if (o->bo_n && ober_oid_cmp(o, &ko) != 2) 493 return (-1); 494 bcopy(&ko, o, sizeof(*o)); 495 errstr = NULL; 496 } else { 497 o->bo_id[o->bo_n++] = 498 strtonum(sp, 0, UINT_MAX, &errstr); 499 } 500 if (errstr || o->bo_n > BER_MAX_OID_LEN) 501 return (-1); 502 } 503 504 return (0); 505} 506 507unsigned int 508smi_application(struct ber_element *elm) 509{ 510 if (elm->be_class != BER_CLASS_APPLICATION) 511 return (BER_TYPE_OCTETSTRING); 512 513 switch (elm->be_type) { 514 case SNMP_T_IPADDR: 515 return (BER_TYPE_OCTETSTRING); 516 case SNMP_T_COUNTER32: 517 case SNMP_T_GAUGE32: 518 case SNMP_T_TIMETICKS: 519 case SNMP_T_OPAQUE: 520 case SNMP_T_COUNTER64: 521 return (BER_TYPE_INTEGER); 522 default: 523 break; 524 } 525 return (BER_TYPE_OCTETSTRING); 526 527} 528 529char * 530smi_oid2string(struct ber_oid *o, char *buf, size_t len, 531 enum smi_oid_lookup lookup) 532{ 533 char str[256]; 534 struct oid *value, key; 535 size_t i; 536 537 bzero(buf, len); 538 bzero(&key, sizeof(key)); 539 bcopy(o, &key.o_id, sizeof(struct ber_oid)); 540 541 for (i = 0; i < o->bo_n; i++) { 542 key.o_oidlen = i + 1; 543 if (lookup != smi_oidl_numeric && 544 (value = RB_FIND(oidtree, &smi_oidtree, &key)) != NULL) { 545 snprintf(str, sizeof(str), "%s", value->o_name); 546 if (lookup == smi_oidl_short && i + 1 < o->bo_n) { 547 key.o_oidlen = i + 2; 548 if (RB_FIND(oidtree, &smi_oidtree, &key) != NULL) 549 continue; 550 } 551 } else 552 snprintf(str, sizeof(str), "%d", key.o_oid[i]); 553 if (*buf != '\0' || i == 0) 554 strlcat(buf, ".", len); 555 strlcat(buf, str, len); 556 } 557 558 return (buf); 559} 560 561void 562smi_mibtree(struct oid *oids) 563{ 564 struct oid *oid, *decl; 565 size_t i; 566 567 for (i = 0; oids[i].o_oid[0] != 0; i++) { 568 oid = &oids[i]; 569 if (oid->o_name != NULL) { 570 RB_INSERT(oidtree, &smi_oidtree, oid); 571 RB_INSERT(keytree, &smi_keytree, oid); 572 continue; 573 } 574 decl = RB_FIND(oidtree, &smi_oidtree, oid); 575 } 576} 577 578struct oid * 579smi_findkey(char *name) 580{ 581 struct oid oid; 582 if (name == NULL) 583 return (NULL); 584 oid.o_name = name; 585 return (RB_FIND(keytree, &smi_keytree, &oid)); 586} 587 588struct oid * 589smi_foreach(struct oid *oid) 590{ 591 /* 592 * Traverse the tree of MIBs with the option to check 593 * for specific OID flags. 594 */ 595 if (oid == NULL) 596 return RB_MIN(oidtree, &smi_oidtree); 597 return RB_NEXT(oidtree, &smi_oidtree, oid); 598} 599 600int 601smi_oid_cmp(struct oid *a, struct oid *b) 602{ 603 size_t i; 604 605 for (i = 0; i < MINIMUM(a->o_oidlen, b->o_oidlen); i++) { 606 if (a->o_oid[i] != b->o_oid[i]) 607 return (a->o_oid[i] - b->o_oid[i]); 608 } 609 610 return (a->o_oidlen - b->o_oidlen); 611} 612 613int 614smi_key_cmp(struct oid *a, struct oid *b) 615{ 616 if (a->o_name == NULL || b->o_name == NULL) 617 return (-1); 618 return (strcasecmp(a->o_name, b->o_name)); 619} 620 621RB_GENERATE(oidtree, oid, o_element, smi_oid_cmp) 622RB_GENERATE(keytree, oid, o_keyword, smi_key_cmp) 623