smi.c revision 1.1
1/* $OpenBSD: smi.c,v 1.1 2019/08/09 06:17:59 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 ber_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, smi_oidl_numeric)) == NULL) 185 goto invalid; 186 187 switch (root->be_encoding) { 188 case BER_TYPE_BOOLEAN: 189 fprintf(stderr, "%s", value); 190 break; 191 case BER_TYPE_INTEGER: 192 case BER_TYPE_ENUMERATED: 193 fprintf(stderr, "value %s", value); 194 break; 195 case BER_TYPE_BITSTRING: 196 fprintf(stderr, "hexdump %s", value); 197 break; 198 case BER_TYPE_OBJECT: 199 fprintf(stderr, "oid %s", value); 200 break; 201 case BER_TYPE_OCTETSTRING: 202 if (root->be_class == BER_CLASS_APPLICATION && 203 root->be_type == SNMP_T_IPADDR) { 204 fprintf(stderr, "addr %s", value); 205 } else { 206 fprintf(stderr, "string %s", value); 207 } 208 break; 209 case BER_TYPE_NULL: /* no payload */ 210 case BER_TYPE_EOC: 211 case BER_TYPE_SEQUENCE: 212 case BER_TYPE_SET: 213 default: 214 fprintf(stderr, "%s", value); 215 break; 216 } 217 218 invalid: 219 if (value == NULL) 220 fprintf(stderr, "<INVALID>"); 221 else 222 free(value); 223 fprintf(stderr, "\n"); 224 225 if (constructed) 226 root->be_encoding = constructed; 227 228 if (constructed && root->be_sub) { 229 indent += 2; 230 smi_debug_elements(root->be_sub); 231 indent -= 2; 232 } 233 if (root->be_next) 234 smi_debug_elements(root->be_next); 235} 236 237char * 238smi_print_element(struct ber_element *root, int print_hint, 239 enum smi_output_string output_string, enum smi_oid_lookup lookup) 240{ 241 char *str = NULL, *buf, *p; 242 size_t len, i; 243 long long v, ticks; 244 int d; 245 int is_hex = 0; 246 struct ber_oid o; 247 char strbuf[BUFSIZ]; 248 char *hint; 249 int days, hours, min, sec, csec; 250 251 switch (root->be_encoding) { 252 case BER_TYPE_BOOLEAN: 253 if (ber_get_boolean(root, &d) == -1) 254 goto fail; 255 if (print_hint) { 256 if (asprintf(&str, "INTEGER: %s(%d)", 257 d ? "true" : "false", d) == -1) 258 goto fail; 259 } 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 (ber_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) 333 == -1) 334 goto fail; 335 break; 336 case BER_TYPE_BITSTRING: 337 if (ber_get_bitstring(root, (void *)&buf, &len) == -1) 338 goto fail; 339 if ((str = calloc(1, len * 2 + 1 + sizeof("BITS: "))) == NULL) 340 goto fail; 341 p = str; 342 if (print_hint) { 343 strlcpy(str, "BITS: ", sizeof(str)); 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 (ber_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 (ber_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 root->be_type == BER_TYPE_EOC) { 370 str = strdup("No Such Object available on this agent at this OID"); 371 372 }else { 373 for (i = 0; i < root->be_len; i++) { 374 if (!isprint(buf[i])) { 375 if (output_string == smi_os_default) 376 output_string = smi_os_hex; 377 else if (output_string == smi_os_ascii) 378 is_hex = 1; 379 break; 380 } 381 } 382 /* 383 * hex is 3 * n (2 digits + n - 1 spaces + NUL-byte) 384 * ascii can be max (2 * n) + 2 quotes + NUL-byte 385 */ 386 if ((p = str = reallocarray(NULL, 387 output_string == smi_os_hex ? 3 : 2, 388 root->be_len + 2)) == NULL) 389 goto fail; 390 if (is_hex) 391 *str++ = '"'; 392 for (i = 0; i < root->be_len; i++) { 393 switch (output_string) { 394 case smi_os_default: 395 /* FALLTHROUGH */ 396 case smi_os_ascii: 397 /* 398 * There's probably more edgecases here, 399 * not fully investigated 400 */ 401 if (is_hex && buf[i] == '\\') 402 *str++ = '\\'; 403 *str++ = isprint(buf[i]) ? buf[i] : '.'; 404 break; 405 case smi_os_hex: 406 sprintf(str, "%s%02hhX", 407 i == 0 ? "" : 408 i % 16 == 0 ? "\n" : " ", buf[i]); 409 str += i == 0 ? 2 : 3; 410 break; 411 } 412 } 413 if (is_hex) 414 *str++ = '"'; 415 *str = '\0'; 416 str = NULL; 417 if (asprintf(&str, "%s%s", 418 print_hint ? 419 output_string == smi_os_hex ? "Hex-STRING: " : 420 "STRING: " : 421 "", p) == -1) { 422 free(p); 423 goto fail; 424 } 425 free(p); 426 } 427 break; 428 case BER_TYPE_NULL: /* no payload */ 429 case BER_TYPE_EOC: 430 case BER_TYPE_SEQUENCE: 431 case BER_TYPE_SET: 432 default: 433 str = strdup(""); 434 break; 435 } 436 437 return (str); 438 439 fail: 440 free(str); 441 return (NULL); 442} 443 444int 445smi_string2oid(const char *oidstr, struct ber_oid *o) 446{ 447 char *sp, *p, str[BUFSIZ]; 448 const char *errstr; 449 struct oid *oid; 450 struct ber_oid ko; 451 452 if (strlcpy(str, oidstr, sizeof(str)) >= sizeof(str)) 453 return (-1); 454 bzero(o, sizeof(*o)); 455 456 /* 457 * Parse OID strings in the common form n.n.n or n-n-n. 458 * Based on ber_string2oid with additional support for symbolic names. 459 */ 460 p = sp = str[0] == '.' ? str + 1 : str; 461 for (; p != NULL; sp = p) { 462 if ((p = strpbrk(p, ".-")) != NULL) 463 *p++ = '\0'; 464 if ((oid = smi_findkey(sp)) != NULL) { 465 bcopy(&oid->o_id, &ko, sizeof(ko)); 466 if (o->bo_n && ber_oid_cmp(o, &ko) != 2) 467 return (-1); 468 bcopy(&ko, o, sizeof(*o)); 469 errstr = NULL; 470 } else { 471 o->bo_id[o->bo_n++] = 472 strtonum(sp, 0, UINT_MAX, &errstr); 473 } 474 if (errstr || o->bo_n > BER_MAX_OID_LEN) 475 return (-1); 476 } 477 478 return (0); 479} 480 481unsigned int 482smi_application(struct ber_element *elm) 483{ 484 if (elm->be_class != BER_CLASS_APPLICATION) 485 return (BER_TYPE_OCTETSTRING); 486 487 switch (elm->be_type) { 488 case SNMP_T_IPADDR: 489 return (BER_TYPE_OCTETSTRING); 490 case SNMP_T_COUNTER32: 491 case SNMP_T_GAUGE32: 492 case SNMP_T_TIMETICKS: 493 case SNMP_T_OPAQUE: 494 case SNMP_T_COUNTER64: 495 return (BER_TYPE_INTEGER); 496 default: 497 break; 498 } 499 return (BER_TYPE_OCTETSTRING); 500 501} 502 503char * 504smi_oid2string(struct ber_oid *o, char *buf, size_t len, 505 enum smi_oid_lookup lookup) 506{ 507 char str[256]; 508 struct oid *value, key; 509 size_t i; 510 511 bzero(buf, len); 512 bzero(&key, sizeof(key)); 513 bcopy(o, &key.o_id, sizeof(struct ber_oid)); 514 key.o_flags |= OID_KEY; /* do not match wildcards */ 515 516 for (i = 0; i < o->bo_n; i++) { 517 key.o_oidlen = i + 1; 518 if (lookup != smi_oidl_numeric && 519 (value = RB_FIND(oidtree, &smi_oidtree, &key)) != NULL) { 520 snprintf(str, sizeof(str), "%s", value->o_name); 521 if (lookup == smi_oidl_short && i + 1 < o->bo_n) { 522 key.o_oidlen = i + 2; 523 if (RB_FIND(oidtree, &smi_oidtree, &key) != NULL) 524 continue; 525 } 526 } else 527 snprintf(str, sizeof(str), "%d", key.o_oid[i]); 528 if (*buf != '\0' || i == 0) 529 strlcat(buf, ".", len); 530 strlcat(buf, str, len); 531 } 532 533 return (buf); 534} 535 536void 537smi_mibtree(struct oid *oids) 538{ 539 struct oid *oid, *decl; 540 size_t i; 541 542 for (i = 0; oids[i].o_oid[0] != 0; i++) { 543 oid = &oids[i]; 544 if (oid->o_name != NULL) { 545 RB_INSERT(oidtree, &smi_oidtree, oid); 546 RB_INSERT(keytree, &smi_keytree, oid); 547 continue; 548 } 549 decl = RB_FIND(oidtree, &smi_oidtree, oid); 550 decl->o_flags = oid->o_flags; 551 decl->o_get = oid->o_get; 552 decl->o_set = oid->o_set; 553 decl->o_table = oid->o_table; 554 decl->o_val = oid->o_val; 555 decl->o_data = oid->o_data; 556 } 557} 558 559struct oid * 560smi_findkey(char *name) 561{ 562 struct oid oid; 563 if (name == NULL) 564 return (NULL); 565 oid.o_name = name; 566 return (RB_FIND(keytree, &smi_keytree, &oid)); 567} 568 569struct oid * 570smi_foreach(struct oid *oid, u_int flags) 571{ 572 /* 573 * Traverse the tree of MIBs with the option to check 574 * for specific OID flags. 575 */ 576 if (oid == NULL) { 577 oid = RB_MIN(oidtree, &smi_oidtree); 578 if (oid == NULL) 579 return (NULL); 580 if (flags == 0 || (oid->o_flags & flags)) 581 return (oid); 582 } 583 for (;;) { 584 oid = RB_NEXT(oidtree, &smi_oidtree, oid); 585 if (oid == NULL) 586 break; 587 if (flags == 0 || (oid->o_flags & flags)) 588 return (oid); 589 } 590 591 return (oid); 592} 593 594int 595smi_oid_cmp(struct oid *a, struct oid *b) 596{ 597 size_t i; 598 599 for (i = 0; i < MINIMUM(a->o_oidlen, b->o_oidlen); i++) { 600 if (a->o_oid[i] != b->o_oid[i]) 601 return (a->o_oid[i] - b->o_oid[i]); 602 } 603 604 /* 605 * Return success if the matched object is a table 606 * or a MIB registered by a subagent 607 * (it will match any sub-elements) 608 */ 609 if ((b->o_flags & OID_TABLE || 610 b->o_flags & OID_REGISTERED) && 611 (a->o_flags & OID_KEY) == 0 && 612 (a->o_oidlen > b->o_oidlen)) 613 return (0); 614 615 return (a->o_oidlen - b->o_oidlen); 616} 617 618int 619smi_key_cmp(struct oid *a, struct oid *b) 620{ 621 if (a->o_name == NULL || b->o_name == NULL) 622 return (-1); 623 return (strcasecmp(a->o_name, b->o_name)); 624} 625 626RB_GENERATE(oidtree, oid, o_element, smi_oid_cmp) 627RB_GENERATE(keytree, oid, o_keyword, smi_key_cmp) 628