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