Annotation of src/usr.bin/snmp/smi.c, Revision 1.6
1.6 ! tb 1: /* $OpenBSD: smi.c,v 1.5 2019/10/11 14:48:30 jsg Exp $ */
1.1 martijn 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:
39: int smi_oid_cmp(struct oid *, struct oid *);
40: int smi_key_cmp(struct oid *, struct oid *);
41: struct oid * smi_findkey(char *);
42:
43: RB_HEAD(oidtree, oid);
44: RB_PROTOTYPE(oidtree, oid, o_element, smi_oid_cmp)
45: struct oidtree smi_oidtree;
46:
47: RB_HEAD(keytree, oid);
48: RB_PROTOTYPE(keytree, oid, o_keyword, smi_key_cmp)
49: struct keytree smi_keytree;
50:
51: int
52: smi_init(void)
53: {
54: /* Initialize the Structure of Managed Information (SMI) */
55: RB_INIT(&smi_oidtree);
56: mib_init();
57: return (0);
58: }
59:
60: void
61: smi_debug_elements(struct ber_element *root)
62: {
63: static int indent = 0;
64: char *value;
65: int constructed;
66:
67: /* calculate lengths */
1.6 ! tb 68: ober_calc_len(root);
1.1 martijn 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:
1.3 deraadt 184: if ((value = smi_print_element(root, 1, smi_os_default,
185: smi_oidl_numeric)) == NULL)
1.1 martijn 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:
238: char *
239: smi_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;
1.5 jsg 243: size_t len, i, slen;
1.1 martijn 244: long long v, ticks;
245: int d;
1.4 martijn 246: int is_hex = 0, ret;
1.1 martijn 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:
1.6 ! tb 254: if (ober_get_boolean(root, &d) == -1)
1.1 martijn 255: goto fail;
256: if (print_hint) {
257: if (asprintf(&str, "INTEGER: %s(%d)",
258: d ? "true" : "false", d) == -1)
259: goto fail;
1.3 deraadt 260: } else
1.1 martijn 261: if (asprintf(&str, "%s", d ? "true" : "false") == -1)
262: goto fail;
263: break;
264: case BER_TYPE_INTEGER:
265: case BER_TYPE_ENUMERATED:
1.6 ! tb 266: if (ober_get_integer(root, &v) == -1)
1.1 martijn 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: }
1.3 deraadt 332: if (asprintf(&str, "%s%lld", print_hint ? hint : "", v) == -1)
1.1 martijn 333: goto fail;
334: break;
335: case BER_TYPE_BITSTRING:
1.6 ! tb 336: if (ober_get_bitstring(root, (void *)&buf, &len) == -1)
1.1 martijn 337: goto fail;
1.5 jsg 338: slen = len * 2 + 1 + sizeof("BITS: ");
339: if ((str = calloc(1, slen)) == NULL)
1.1 martijn 340: goto fail;
341: p = str;
342: if (print_hint) {
1.5 jsg 343: strlcpy(str, "BITS: ", slen);
1.1 martijn 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:
1.6 ! tb 352: if (ober_get_oid(root, &o) == -1)
1.1 martijn 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:
1.6 ! tb 360: if (ober_get_string(root, &buf) == -1)
1.1 martijn 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");
1.2 deraadt 371: } else {
1.1 martijn 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: */
1.4 martijn 385: len = output_string == smi_os_hex ? 3 : 2;
386: p = str = reallocarray(NULL, root->be_len + 2, len);
387: if (p == NULL)
1.1 martijn 388: goto fail;
1.4 martijn 389: len *= root->be_len + 2;
390: if (is_hex) {
1.1 martijn 391: *str++ = '"';
1.4 martijn 392: len--;
393: }
1.1 martijn 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: */
1.4 martijn 403: if (len < 2)
404: goto fail;
405: if (is_hex && buf[i] == '\\') {
1.1 martijn 406: *str++ = '\\';
1.4 martijn 407: len--;
408: }
1.1 martijn 409: *str++ = isprint(buf[i]) ? buf[i] : '.';
1.4 martijn 410: len--;
1.1 martijn 411: break;
412: case smi_os_hex:
1.4 martijn 413: ret = snprintf(str, len, "%s%02hhX",
1.1 martijn 414: i == 0 ? "" :
415: i % 16 == 0 ? "\n" : " ", buf[i]);
1.4 martijn 416: if (ret == -1 || ret > (int) len)
417: goto fail;
418: len -= ret;
419: str += ret;
1.1 martijn 420: break;
421: }
422: }
1.4 martijn 423: if (is_hex) {
424: if (len < 2)
425: goto fail;
1.1 martijn 426: *str++ = '"';
1.4 martijn 427: len--;
428: }
429: if (len == 0)
430: goto fail;
1.1 martijn 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:
460: int
461: smi_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.
1.6 ! tb 474: * Based on ober_string2oid with additional support for symbolic names.
1.1 martijn 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));
1.6 ! tb 482: if (o->bo_n && ober_oid_cmp(o, &ko) != 2)
1.1 martijn 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:
497: unsigned int
498: smi_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:
519: char *
520: smi_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:
552: void
553: smi_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:
575: struct oid *
576: smi_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:
585: struct oid *
586: smi_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:
610: int
611: smi_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:
634: int
635: smi_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:
642: RB_GENERATE(oidtree, oid, o_element, smi_oid_cmp)
643: RB_GENERATE(keytree, oid, o_keyword, smi_key_cmp)