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