smi.c revision 1.6
1/* $OpenBSD: smi.c,v 1.6 2019/10/24 12:39:26 tb 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 root->be_type == BER_TYPE_EOC) { 370 str = strdup("No Such Object available on this agent at this OID"); 371 } else { 372 for (i = 0; i < root->be_len; i++) { 373 if (!isprint(buf[i])) { 374 if (output_string == smi_os_default) 375 output_string = smi_os_hex; 376 else if (output_string == smi_os_ascii) 377 is_hex = 1; 378 break; 379 } 380 } 381 /* 382 * hex is 3 * n (2 digits + n - 1 spaces + NUL-byte) 383 * ascii can be max (2 * n) + 2 quotes + NUL-byte 384 */ 385 len = output_string == smi_os_hex ? 3 : 2; 386 p = str = reallocarray(NULL, root->be_len + 2, len); 387 if (p == NULL) 388 goto fail; 389 len *= root->be_len + 2; 390 if (is_hex) { 391 *str++ = '"'; 392 len--; 393 } 394 for (i = 0; i < root->be_len; i++) { 395 switch (output_string) { 396 case smi_os_default: 397 /* FALLTHROUGH */ 398 case smi_os_ascii: 399 /* 400 * There's probably more edgecases here, 401 * not fully investigated 402 */ 403 if (len < 2) 404 goto fail; 405 if (is_hex && buf[i] == '\\') { 406 *str++ = '\\'; 407 len--; 408 } 409 *str++ = isprint(buf[i]) ? buf[i] : '.'; 410 len--; 411 break; 412 case smi_os_hex: 413 ret = snprintf(str, len, "%s%02hhX", 414 i == 0 ? "" : 415 i % 16 == 0 ? "\n" : " ", buf[i]); 416 if (ret == -1 || ret > (int) len) 417 goto fail; 418 len -= ret; 419 str += ret; 420 break; 421 } 422 } 423 if (is_hex) { 424 if (len < 2) 425 goto fail; 426 *str++ = '"'; 427 len--; 428 } 429 if (len == 0) 430 goto fail; 431 *str = '\0'; 432 str = NULL; 433 if (asprintf(&str, "%s%s", 434 print_hint ? 435 output_string == smi_os_hex ? "Hex-STRING: " : 436 "STRING: " : 437 "", p) == -1) { 438 free(p); 439 goto fail; 440 } 441 free(p); 442 } 443 break; 444 case BER_TYPE_NULL: /* no payload */ 445 case BER_TYPE_EOC: 446 case BER_TYPE_SEQUENCE: 447 case BER_TYPE_SET: 448 default: 449 str = strdup(""); 450 break; 451 } 452 453 return (str); 454 455 fail: 456 free(str); 457 return (NULL); 458} 459 460int 461smi_string2oid(const char *oidstr, struct ber_oid *o) 462{ 463 char *sp, *p, str[BUFSIZ]; 464 const char *errstr; 465 struct oid *oid; 466 struct ber_oid ko; 467 468 if (strlcpy(str, oidstr, sizeof(str)) >= sizeof(str)) 469 return (-1); 470 bzero(o, sizeof(*o)); 471 472 /* 473 * Parse OID strings in the common form n.n.n or n-n-n. 474 * Based on ober_string2oid with additional support for symbolic names. 475 */ 476 p = sp = str[0] == '.' ? str + 1 : str; 477 for (; p != NULL; sp = p) { 478 if ((p = strpbrk(p, ".-")) != NULL) 479 *p++ = '\0'; 480 if ((oid = smi_findkey(sp)) != NULL) { 481 bcopy(&oid->o_id, &ko, sizeof(ko)); 482 if (o->bo_n && ober_oid_cmp(o, &ko) != 2) 483 return (-1); 484 bcopy(&ko, o, sizeof(*o)); 485 errstr = NULL; 486 } else { 487 o->bo_id[o->bo_n++] = 488 strtonum(sp, 0, UINT_MAX, &errstr); 489 } 490 if (errstr || o->bo_n > BER_MAX_OID_LEN) 491 return (-1); 492 } 493 494 return (0); 495} 496 497unsigned int 498smi_application(struct ber_element *elm) 499{ 500 if (elm->be_class != BER_CLASS_APPLICATION) 501 return (BER_TYPE_OCTETSTRING); 502 503 switch (elm->be_type) { 504 case SNMP_T_IPADDR: 505 return (BER_TYPE_OCTETSTRING); 506 case SNMP_T_COUNTER32: 507 case SNMP_T_GAUGE32: 508 case SNMP_T_TIMETICKS: 509 case SNMP_T_OPAQUE: 510 case SNMP_T_COUNTER64: 511 return (BER_TYPE_INTEGER); 512 default: 513 break; 514 } 515 return (BER_TYPE_OCTETSTRING); 516 517} 518 519char * 520smi_oid2string(struct ber_oid *o, char *buf, size_t len, 521 enum smi_oid_lookup lookup) 522{ 523 char str[256]; 524 struct oid *value, key; 525 size_t i; 526 527 bzero(buf, len); 528 bzero(&key, sizeof(key)); 529 bcopy(o, &key.o_id, sizeof(struct ber_oid)); 530 key.o_flags |= OID_KEY; /* do not match wildcards */ 531 532 for (i = 0; i < o->bo_n; i++) { 533 key.o_oidlen = i + 1; 534 if (lookup != smi_oidl_numeric && 535 (value = RB_FIND(oidtree, &smi_oidtree, &key)) != NULL) { 536 snprintf(str, sizeof(str), "%s", value->o_name); 537 if (lookup == smi_oidl_short && i + 1 < o->bo_n) { 538 key.o_oidlen = i + 2; 539 if (RB_FIND(oidtree, &smi_oidtree, &key) != NULL) 540 continue; 541 } 542 } else 543 snprintf(str, sizeof(str), "%d", key.o_oid[i]); 544 if (*buf != '\0' || i == 0) 545 strlcat(buf, ".", len); 546 strlcat(buf, str, len); 547 } 548 549 return (buf); 550} 551 552void 553smi_mibtree(struct oid *oids) 554{ 555 struct oid *oid, *decl; 556 size_t i; 557 558 for (i = 0; oids[i].o_oid[0] != 0; i++) { 559 oid = &oids[i]; 560 if (oid->o_name != NULL) { 561 RB_INSERT(oidtree, &smi_oidtree, oid); 562 RB_INSERT(keytree, &smi_keytree, oid); 563 continue; 564 } 565 decl = RB_FIND(oidtree, &smi_oidtree, oid); 566 decl->o_flags = oid->o_flags; 567 decl->o_get = oid->o_get; 568 decl->o_set = oid->o_set; 569 decl->o_table = oid->o_table; 570 decl->o_val = oid->o_val; 571 decl->o_data = oid->o_data; 572 } 573} 574 575struct oid * 576smi_findkey(char *name) 577{ 578 struct oid oid; 579 if (name == NULL) 580 return (NULL); 581 oid.o_name = name; 582 return (RB_FIND(keytree, &smi_keytree, &oid)); 583} 584 585struct oid * 586smi_foreach(struct oid *oid, u_int flags) 587{ 588 /* 589 * Traverse the tree of MIBs with the option to check 590 * for specific OID flags. 591 */ 592 if (oid == NULL) { 593 oid = RB_MIN(oidtree, &smi_oidtree); 594 if (oid == NULL) 595 return (NULL); 596 if (flags == 0 || (oid->o_flags & flags)) 597 return (oid); 598 } 599 for (;;) { 600 oid = RB_NEXT(oidtree, &smi_oidtree, oid); 601 if (oid == NULL) 602 break; 603 if (flags == 0 || (oid->o_flags & flags)) 604 return (oid); 605 } 606 607 return (oid); 608} 609 610int 611smi_oid_cmp(struct oid *a, struct oid *b) 612{ 613 size_t i; 614 615 for (i = 0; i < MINIMUM(a->o_oidlen, b->o_oidlen); i++) { 616 if (a->o_oid[i] != b->o_oid[i]) 617 return (a->o_oid[i] - b->o_oid[i]); 618 } 619 620 /* 621 * Return success if the matched object is a table 622 * or a MIB registered by a subagent 623 * (it will match any sub-elements) 624 */ 625 if ((b->o_flags & OID_TABLE || 626 b->o_flags & OID_REGISTERED) && 627 (a->o_flags & OID_KEY) == 0 && 628 (a->o_oidlen > b->o_oidlen)) 629 return (0); 630 631 return (a->o_oidlen - b->o_oidlen); 632} 633 634int 635smi_key_cmp(struct oid *a, struct oid *b) 636{ 637 if (a->o_name == NULL || b->o_name == NULL) 638 return (-1); 639 return (strcasecmp(a->o_name, b->o_name)); 640} 641 642RB_GENERATE(oidtree, oid, o_element, smi_oid_cmp) 643RB_GENERATE(keytree, oid, o_keyword, smi_key_cmp) 644