xref: /openbsd/usr.sbin/snmpd/smi.c (revision f0bcdb5c)
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