xref: /openbsd-src/usr.sbin/snmpd/smi.c (revision 5a38ef86d0b61900239c7913d24a05e7b88a58f0)
1 /*	$OpenBSD: smi.c,v 1.30 2021/10/21 15:08:15 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/queue.h>
20 #include <sys/types.h>
21 #include <sys/stat.h>
22 #include <sys/socket.h>
23 #include <sys/un.h>
24 #include <sys/tree.h>
25 #include <sys/sysctl.h>
26 
27 #include <net/if.h>
28 #include <net/if_dl.h>
29 #include <net/if_arp.h>
30 #include <net/if_media.h>
31 #include <net/route.h>
32 #include <netinet/in.h>
33 #include <netinet/if_ether.h>
34 #include <arpa/inet.h>
35 
36 #include <stdlib.h>
37 #include <stdio.h>
38 #include <errno.h>
39 #include <event.h>
40 #include <fcntl.h>
41 #include <string.h>
42 #include <unistd.h>
43 #include <limits.h>
44 #include <pwd.h>
45 #include <vis.h>
46 
47 #include "snmpd.h"
48 #include "mib.h"
49 
50 #define MINIMUM(a, b)	(((a) < (b)) ? (a) : (b))
51 
52 RB_HEAD(oidtree, oid);
53 RB_PROTOTYPE(oidtree, oid, o_element, smi_oid_cmp);
54 struct oidtree smi_oidtree;
55 
56 RB_HEAD(keytree, oid);
57 RB_PROTOTYPE(keytree, oid, o_keyword, smi_key_cmp);
58 struct keytree smi_keytree;
59 
60 u_long
61 smi_getticks(void)
62 {
63 	struct timeval	 now, run;
64 	u_long		 ticks;
65 
66 	gettimeofday(&now, NULL);
67 	if (timercmp(&now, &snmpd_env->sc_starttime, <=))
68 		return (0);
69 	timersub(&now, &snmpd_env->sc_starttime, &run);
70 	ticks = run.tv_sec * 100;
71 	if (run.tv_usec)
72 		ticks += run.tv_usec / 10000;
73 
74 	return (ticks);
75 }
76 
77 void
78 smi_oidlen(struct ber_oid *o)
79 {
80 	size_t	 i;
81 
82 	for (i = 0; i < BER_MAX_OID_LEN && o->bo_id[i] != 0; i++)
83 		;
84 	o->bo_n = i;
85 }
86 
87 void
88 smi_scalar_oidlen(struct ber_oid *o)
89 {
90 	smi_oidlen(o);
91 
92 	/* Append .0. */
93 	if (o->bo_n < BER_MAX_OID_LEN)
94 		o->bo_n++;
95 }
96 
97 char *
98 smi_oid2string(struct ber_oid *o, char *buf, size_t len, size_t skip)
99 {
100 	char		 str[256];
101 	struct oid	*value, key;
102 	size_t		 i, lookup = 1;
103 
104 	bzero(buf, len);
105 	bzero(&key, sizeof(key));
106 	bcopy(o, &key.o_id, sizeof(struct ber_oid));
107 	key.o_flags |= OID_KEY;		/* do not match wildcards */
108 
109 	if (snmpd_env->sc_flags & SNMPD_F_NONAMES)
110 		lookup = 0;
111 
112 	for (i = 0; i < o->bo_n; i++) {
113 		key.o_oidlen = i + 1;
114 		if (lookup && skip > i)
115 			continue;
116 		if (lookup &&
117 		    (value = RB_FIND(oidtree, &smi_oidtree, &key)) != NULL)
118 			snprintf(str, sizeof(str), "%s", value->o_name);
119 		else
120 			snprintf(str, sizeof(str), "%d", key.o_oid[i]);
121 		strlcat(buf, str, len);
122 		if (i < (o->bo_n - 1))
123 			strlcat(buf, ".", len);
124 	}
125 
126 	return (buf);
127 }
128 
129 int
130 smi_string2oid(const char *oidstr, struct ber_oid *o)
131 {
132 	char			*sp, *p, str[BUFSIZ];
133 	const char		*errstr;
134 	struct oid		*oid;
135 	struct ber_oid		 ko;
136 
137 	if (strlcpy(str, oidstr, sizeof(str)) >= sizeof(str))
138 		return (-1);
139 	bzero(o, sizeof(*o));
140 
141 	/*
142 	 * Parse OID strings in the common form n.n.n or n-n-n.
143 	 * Based on ober_string2oid with additional support for symbolic names.
144 	 */
145 	for (p = sp = str; p != NULL; sp = p) {
146 		if ((p = strpbrk(p, ".-")) != NULL)
147 			*p++ = '\0';
148 		if ((oid = smi_findkey(sp)) != NULL) {
149 			bcopy(&oid->o_id, &ko, sizeof(ko));
150 			if (o->bo_n && ober_oid_cmp(o, &ko) != 2)
151 				return (-1);
152 			bcopy(&ko, o, sizeof(*o));
153 			errstr = NULL;
154 		} else {
155 			o->bo_id[o->bo_n++] =
156 			    strtonum(sp, 0, UINT_MAX, &errstr);
157 		}
158 		if (errstr || o->bo_n > BER_MAX_OID_LEN)
159 			return (-1);
160 	}
161 
162 	return (0);
163 }
164 
165 void
166 smi_delete(struct oid *oid)
167 {
168 	struct oid	 key, *value;
169 
170 	bzero(&key, sizeof(key));
171 	bcopy(&oid->o_id, &key.o_id, sizeof(struct ber_oid));
172 	if ((value = RB_FIND(oidtree, &smi_oidtree, &key)) != NULL &&
173 	    value == oid)
174 		RB_REMOVE(oidtree, &smi_oidtree, value);
175 
176 	free(oid->o_data);
177 	if (oid->o_flags & OID_DYNAMIC) {
178 		free(oid->o_name);
179 		free(oid);
180 	}
181 }
182 
183 int
184 smi_insert(struct oid *oid)
185 {
186 	struct oid		 key, *value;
187 
188 	if ((oid->o_flags & OID_TABLE) && oid->o_get == NULL)
189 		fatalx("smi_insert: invalid MIB table");
190 
191 	bzero(&key, sizeof(key));
192 	bcopy(&oid->o_id, &key.o_id, sizeof(struct ber_oid));
193 	value = RB_FIND(oidtree, &smi_oidtree, &key);
194 	if (value != NULL)
195 		return (-1);
196 
197 	RB_INSERT(oidtree, &smi_oidtree, oid);
198 	return (0);
199 }
200 
201 void
202 smi_mibtree(struct oid *oids)
203 {
204 	struct oid	*oid, *decl;
205 	size_t		 i;
206 
207 	for (i = 0; oids[i].o_oid[0] != 0; i++) {
208 		oid = &oids[i];
209 		smi_oidlen(&oid->o_id);
210 		if (oid->o_name != NULL) {
211 			if ((oid->o_flags & OID_TABLE) && oid->o_get == NULL)
212 				fatalx("smi_mibtree: invalid MIB table");
213 			RB_INSERT(oidtree, &smi_oidtree, oid);
214 			RB_INSERT(keytree, &smi_keytree, oid);
215 			continue;
216 		}
217 		decl = RB_FIND(oidtree, &smi_oidtree, oid);
218 		if (decl == NULL)
219 			fatalx("smi_mibtree: undeclared MIB");
220 		decl->o_flags = oid->o_flags;
221 		decl->o_get = oid->o_get;
222 		decl->o_set = oid->o_set;
223 		decl->o_table = oid->o_table;
224 		decl->o_val = oid->o_val;
225 		decl->o_data = oid->o_data;
226 	}
227 }
228 
229 int
230 smi_init(void)
231 {
232 	/* Initialize the Structure of Managed Information (SMI) */
233 	RB_INIT(&smi_oidtree);
234 	mib_init();
235 	return (0);
236 }
237 
238 struct oid *
239 smi_find(struct oid *oid)
240 {
241 	return (RB_FIND(oidtree, &smi_oidtree, oid));
242 }
243 
244 struct oid *
245 smi_nfind(struct oid *oid)
246 {
247 	return (RB_NFIND(oidtree, &smi_oidtree, oid));
248 }
249 
250 struct oid *
251 smi_findkey(char *name)
252 {
253 	struct oid	oid;
254 	if (name == NULL)
255 		return (NULL);
256 	oid.o_name = name;
257 	return (RB_FIND(keytree, &smi_keytree, &oid));
258 }
259 
260 struct oid *
261 smi_next(struct oid *oid)
262 {
263 	return (RB_NEXT(oidtree, &smi_oidtree, oid));
264 }
265 
266 struct oid *
267 smi_foreach(struct oid *oid, u_int flags)
268 {
269 	/*
270 	 * Traverse the tree of MIBs with the option to check
271 	 * for specific OID flags.
272 	 */
273 	if (oid == NULL) {
274 		oid = RB_MIN(oidtree, &smi_oidtree);
275 		if (oid == NULL)
276 			return (NULL);
277 		if (flags == 0 || (oid->o_flags & flags))
278 			return (oid);
279 	}
280 	for (;;) {
281 		oid = RB_NEXT(oidtree, &smi_oidtree, oid);
282 		if (oid == NULL)
283 			break;
284 		if (flags == 0 || (oid->o_flags & flags))
285 			return (oid);
286 	}
287 
288 	return (oid);
289 }
290 
291 #ifdef DEBUG
292 void
293 smi_debug_elements(struct ber_element *root)
294 {
295 	static int	 indent = 0;
296 	char		*value;
297 	int		 constructed;
298 
299 	/* calculate lengths */
300 	ober_calc_len(root);
301 
302 	switch (root->be_encoding) {
303 	case BER_TYPE_SEQUENCE:
304 	case BER_TYPE_SET:
305 		constructed = root->be_encoding;
306 		break;
307 	default:
308 		constructed = 0;
309 		break;
310 	}
311 
312 	fprintf(stderr, "%*slen %lu ", indent, "", root->be_len);
313 	switch (root->be_class) {
314 	case BER_CLASS_UNIVERSAL:
315 		fprintf(stderr, "class: universal(%u) type: ", root->be_class);
316 		switch (root->be_type) {
317 		case BER_TYPE_EOC:
318 			fprintf(stderr, "end-of-content");
319 			break;
320 		case BER_TYPE_INTEGER:
321 			fprintf(stderr, "integer");
322 			break;
323 		case BER_TYPE_BITSTRING:
324 			fprintf(stderr, "bit-string");
325 			break;
326 		case BER_TYPE_OCTETSTRING:
327 			fprintf(stderr, "octet-string");
328 			break;
329 		case BER_TYPE_NULL:
330 			fprintf(stderr, "null");
331 			break;
332 		case BER_TYPE_OBJECT:
333 			fprintf(stderr, "object");
334 			break;
335 		case BER_TYPE_ENUMERATED:
336 			fprintf(stderr, "enumerated");
337 			break;
338 		case BER_TYPE_SEQUENCE:
339 			fprintf(stderr, "sequence");
340 			break;
341 		case BER_TYPE_SET:
342 			fprintf(stderr, "set");
343 			break;
344 		}
345 		break;
346 	case BER_CLASS_APPLICATION:
347 		fprintf(stderr, "class: application(%u) type: ",
348 		    root->be_class);
349 		switch (root->be_type) {
350 		case SNMP_T_IPADDR:
351 			fprintf(stderr, "ipaddr");
352 			break;
353 		case SNMP_T_COUNTER32:
354 			fprintf(stderr, "counter32");
355 			break;
356 		case SNMP_T_GAUGE32:
357 			fprintf(stderr, "gauge32");
358 			break;
359 		case SNMP_T_TIMETICKS:
360 			fprintf(stderr, "timeticks");
361 			break;
362 		case SNMP_T_OPAQUE:
363 			fprintf(stderr, "opaque");
364 			break;
365 		case SNMP_T_COUNTER64:
366 			fprintf(stderr, "counter64");
367 			break;
368 		}
369 		break;
370 	case BER_CLASS_CONTEXT:
371 		fprintf(stderr, "class: context(%u) type: ",
372 		    root->be_class);
373 		switch (root->be_type) {
374 		case SNMP_C_GETREQ:
375 			fprintf(stderr, "getreq");
376 			break;
377 		case SNMP_C_GETNEXTREQ:
378 			fprintf(stderr, "getnextreq");
379 			break;
380 		case SNMP_C_RESPONSE:
381 			fprintf(stderr, "response");
382 			break;
383 		case SNMP_C_SETREQ:
384 			fprintf(stderr, "setreq");
385 			break;
386 		case SNMP_C_TRAP:
387 			fprintf(stderr, "trap");
388 			break;
389 		case SNMP_C_GETBULKREQ:
390 			fprintf(stderr, "getbulkreq");
391 			break;
392 		case SNMP_C_INFORMREQ:
393 			fprintf(stderr, "informreq");
394 			break;
395 		case SNMP_C_TRAPV2:
396 			fprintf(stderr, "trapv2");
397 			break;
398 		case SNMP_C_REPORT:
399 			fprintf(stderr, "report");
400 			break;
401 		}
402 		break;
403 	case BER_CLASS_PRIVATE:
404 		fprintf(stderr, "class: private(%u) type: ", root->be_class);
405 		break;
406 	default:
407 		fprintf(stderr, "class: <INVALID>(%u) type: ", root->be_class);
408 		break;
409 	}
410 	fprintf(stderr, "(%u) encoding %u ",
411 	    root->be_type, root->be_encoding);
412 
413 	if ((value = smi_print_element(root)) == NULL)
414 		goto invalid;
415 
416 	switch (root->be_encoding) {
417 	case BER_TYPE_INTEGER:
418 	case BER_TYPE_ENUMERATED:
419 		fprintf(stderr, "value %s", value);
420 		break;
421 	case BER_TYPE_BITSTRING:
422 		fprintf(stderr, "hexdump %s", value);
423 		break;
424 	case BER_TYPE_OBJECT:
425 		fprintf(stderr, "oid %s", value);
426 		break;
427 	case BER_TYPE_OCTETSTRING:
428 		if (root->be_class == BER_CLASS_APPLICATION &&
429 		    root->be_type == SNMP_T_IPADDR) {
430 			fprintf(stderr, "addr %s", value);
431 		} else {
432 			fprintf(stderr, "string %s", value);
433 		}
434 		break;
435 	case BER_TYPE_NULL:	/* no payload */
436 	case BER_TYPE_EOC:
437 	case BER_TYPE_SEQUENCE:
438 	case BER_TYPE_SET:
439 	default:
440 		fprintf(stderr, "%s", value);
441 		break;
442 	}
443 
444  invalid:
445 	if (value == NULL)
446 		fprintf(stderr, "<INVALID>");
447 	else
448 		free(value);
449 	fprintf(stderr, "\n");
450 
451 	if (constructed)
452 		root->be_encoding = constructed;
453 
454 	if (constructed && root->be_sub) {
455 		indent += 2;
456 		smi_debug_elements(root->be_sub);
457 		indent -= 2;
458 	}
459 	if (root->be_next)
460 		smi_debug_elements(root->be_next);
461 }
462 #endif
463 
464 char *
465 smi_print_element(struct ber_element *root)
466 {
467 	char		*str = NULL, *buf, *p;
468 	size_t		 len, i;
469 	long long	 v;
470 	struct ber_oid	 o;
471 	char		 strbuf[BUFSIZ];
472 
473 	switch (root->be_encoding) {
474 	case BER_TYPE_INTEGER:
475 	case BER_TYPE_ENUMERATED:
476 		if (ober_get_integer(root, &v) == -1)
477 			goto fail;
478 		if (asprintf(&str, "%lld", v) == -1)
479 			goto fail;
480 		break;
481 	case BER_TYPE_BITSTRING:
482 		if (ober_get_bitstring(root, (void *)&buf, &len) == -1)
483 			goto fail;
484 		if ((str = calloc(1, len * 2 + 1)) == NULL)
485 			goto fail;
486 		for (p = str, i = 0; i < len; i++) {
487 			snprintf(p, 3, "%02x", buf[i]);
488 			p += 2;
489 		}
490 		break;
491 	case BER_TYPE_OBJECT:
492 		if (ober_get_oid(root, &o) == -1)
493 			goto fail;
494 		if (asprintf(&str, "%s",
495 		    smi_oid2string(&o, strbuf, sizeof(strbuf), 0)) == -1)
496 			goto fail;
497 		break;
498 	case BER_TYPE_OCTETSTRING:
499 		if (ober_get_string(root, &buf) == -1)
500 			goto fail;
501 		if (root->be_class == BER_CLASS_APPLICATION &&
502 		    root->be_type == SNMP_T_IPADDR) {
503 			if (asprintf(&str, "%s",
504 			    inet_ntoa(*(struct in_addr *)buf)) == -1)
505 				goto fail;
506 		} else {
507 			if ((p = reallocarray(NULL, 4, root->be_len + 1)) == NULL)
508 				goto fail;
509 			strvisx(p, buf, root->be_len, VIS_NL);
510 			if (asprintf(&str, "\"%s\"", p) == -1) {
511 				free(p);
512 				goto fail;
513 			}
514 			free(p);
515 		}
516 		break;
517 	case BER_TYPE_NULL:	/* no payload */
518 	case BER_TYPE_EOC:
519 	case BER_TYPE_SEQUENCE:
520 	case BER_TYPE_SET:
521 	default:
522 		str = strdup("");
523 		break;
524 	}
525 
526 	return (str);
527 
528  fail:
529 	free(str);
530 	return (NULL);
531 }
532 
533 unsigned int
534 smi_application(struct ber_element *elm)
535 {
536 	if (elm->be_class != BER_CLASS_APPLICATION)
537 		return (BER_TYPE_OCTETSTRING);
538 
539 	switch (elm->be_type) {
540 	case SNMP_T_IPADDR:
541 		return (BER_TYPE_OCTETSTRING);
542 	case SNMP_T_COUNTER32:
543 	case SNMP_T_GAUGE32:
544 	case SNMP_T_TIMETICKS:
545 	case SNMP_T_OPAQUE:
546 	case SNMP_T_COUNTER64:
547 		return (BER_TYPE_INTEGER);
548 	default:
549 		break;
550 	}
551 	return (BER_TYPE_OCTETSTRING);
552 }
553 
554 int
555 smi_oid_cmp(struct oid *a, struct oid *b)
556 {
557 	size_t	 i;
558 
559 	for (i = 0; i < MINIMUM(a->o_oidlen, b->o_oidlen); i++)
560 		if (a->o_oid[i] != b->o_oid[i])
561 			return (a->o_oid[i] - b->o_oid[i]);
562 
563 	/*
564 	 * Return success if the matched object is a table
565 	 * or a MIB registered by a subagent
566 	 * (it will match any sub-elements)
567 	 */
568 	if ((b->o_flags & OID_TABLE ||
569 	    b->o_flags & OID_REGISTERED) &&
570 	    (a->o_flags & OID_KEY) == 0 &&
571 	    (a->o_oidlen > b->o_oidlen))
572 		return (0);
573 
574 	return (a->o_oidlen - b->o_oidlen);
575 }
576 
577 RB_GENERATE(oidtree, oid, o_element, smi_oid_cmp);
578 
579 int
580 smi_key_cmp(struct oid *a, struct oid *b)
581 {
582 	if (a->o_name == NULL || b->o_name == NULL)
583 		return (-1);
584 	return (strcasecmp(a->o_name, b->o_name));
585 }
586 
587 RB_GENERATE(keytree, oid, o_keyword, smi_key_cmp);
588