xref: /openbsd-src/usr.sbin/snmpd/smi.c (revision 46035553bfdd96e63c94e32da0210227ec2e3cf1)
1 /*	$OpenBSD: smi.c,v 1.27 2019/10/24 12:39:27 tb 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_BOOLEAN:
321 			fprintf(stderr, "boolean");
322 			break;
323 		case BER_TYPE_INTEGER:
324 			fprintf(stderr, "integer");
325 			break;
326 		case BER_TYPE_BITSTRING:
327 			fprintf(stderr, "bit-string");
328 			break;
329 		case BER_TYPE_OCTETSTRING:
330 			fprintf(stderr, "octet-string");
331 			break;
332 		case BER_TYPE_NULL:
333 			fprintf(stderr, "null");
334 			break;
335 		case BER_TYPE_OBJECT:
336 			fprintf(stderr, "object");
337 			break;
338 		case BER_TYPE_ENUMERATED:
339 			fprintf(stderr, "enumerated");
340 			break;
341 		case BER_TYPE_SEQUENCE:
342 			fprintf(stderr, "sequence");
343 			break;
344 		case BER_TYPE_SET:
345 			fprintf(stderr, "set");
346 			break;
347 		}
348 		break;
349 	case BER_CLASS_APPLICATION:
350 		fprintf(stderr, "class: application(%u) type: ",
351 		    root->be_class);
352 		switch (root->be_type) {
353 		case SNMP_T_IPADDR:
354 			fprintf(stderr, "ipaddr");
355 			break;
356 		case SNMP_T_COUNTER32:
357 			fprintf(stderr, "counter32");
358 			break;
359 		case SNMP_T_GAUGE32:
360 			fprintf(stderr, "gauge32");
361 			break;
362 		case SNMP_T_TIMETICKS:
363 			fprintf(stderr, "timeticks");
364 			break;
365 		case SNMP_T_OPAQUE:
366 			fprintf(stderr, "opaque");
367 			break;
368 		case SNMP_T_COUNTER64:
369 			fprintf(stderr, "counter64");
370 			break;
371 		}
372 		break;
373 	case BER_CLASS_CONTEXT:
374 		fprintf(stderr, "class: context(%u) type: ",
375 		    root->be_class);
376 		switch (root->be_type) {
377 		case SNMP_C_GETREQ:
378 			fprintf(stderr, "getreq");
379 			break;
380 		case SNMP_C_GETNEXTREQ:
381 			fprintf(stderr, "nextreq");
382 			break;
383 		case SNMP_C_GETRESP:
384 			fprintf(stderr, "getresp");
385 			break;
386 		case SNMP_C_SETREQ:
387 			fprintf(stderr, "setreq");
388 			break;
389 		case SNMP_C_TRAP:
390 			fprintf(stderr, "trap");
391 			break;
392 		case SNMP_C_GETBULKREQ:
393 			fprintf(stderr, "getbulkreq");
394 			break;
395 		case SNMP_C_INFORMREQ:
396 			fprintf(stderr, "informreq");
397 			break;
398 		case SNMP_C_TRAPV2:
399 			fprintf(stderr, "trapv2");
400 			break;
401 		case SNMP_C_REPORT:
402 			fprintf(stderr, "report");
403 			break;
404 		}
405 		break;
406 	case BER_CLASS_PRIVATE:
407 		fprintf(stderr, "class: private(%u) type: ", root->be_class);
408 		break;
409 	default:
410 		fprintf(stderr, "class: <INVALID>(%u) type: ", root->be_class);
411 		break;
412 	}
413 	fprintf(stderr, "(%u) encoding %u ",
414 	    root->be_type, root->be_encoding);
415 
416 	if ((value = smi_print_element(root)) == NULL)
417 		goto invalid;
418 
419 	switch (root->be_encoding) {
420 	case BER_TYPE_BOOLEAN:
421 		fprintf(stderr, "%s", value);
422 		break;
423 	case BER_TYPE_INTEGER:
424 	case BER_TYPE_ENUMERATED:
425 		fprintf(stderr, "value %s", value);
426 		break;
427 	case BER_TYPE_BITSTRING:
428 		fprintf(stderr, "hexdump %s", value);
429 		break;
430 	case BER_TYPE_OBJECT:
431 		fprintf(stderr, "oid %s", value);
432 		break;
433 	case BER_TYPE_OCTETSTRING:
434 		if (root->be_class == BER_CLASS_APPLICATION &&
435 		    root->be_type == SNMP_T_IPADDR) {
436 			fprintf(stderr, "addr %s", value);
437 		} else {
438 			fprintf(stderr, "string %s", value);
439 		}
440 		break;
441 	case BER_TYPE_NULL:	/* no payload */
442 	case BER_TYPE_EOC:
443 	case BER_TYPE_SEQUENCE:
444 	case BER_TYPE_SET:
445 	default:
446 		fprintf(stderr, "%s", value);
447 		break;
448 	}
449 
450  invalid:
451 	if (value == NULL)
452 		fprintf(stderr, "<INVALID>");
453 	else
454 		free(value);
455 	fprintf(stderr, "\n");
456 
457 	if (constructed)
458 		root->be_encoding = constructed;
459 
460 	if (constructed && root->be_sub) {
461 		indent += 2;
462 		smi_debug_elements(root->be_sub);
463 		indent -= 2;
464 	}
465 	if (root->be_next)
466 		smi_debug_elements(root->be_next);
467 }
468 #endif
469 
470 char *
471 smi_print_element(struct ber_element *root)
472 {
473 	char		*str = NULL, *buf, *p;
474 	size_t		 len, i;
475 	long long	 v;
476 	int		 d;
477 	struct ber_oid	 o;
478 	char		 strbuf[BUFSIZ];
479 
480 	switch (root->be_encoding) {
481 	case BER_TYPE_BOOLEAN:
482 		if (ober_get_boolean(root, &d) == -1)
483 			goto fail;
484 		if (asprintf(&str, "%s(%d)", d ? "true" : "false", d) == -1)
485 			goto fail;
486 		break;
487 	case BER_TYPE_INTEGER:
488 	case BER_TYPE_ENUMERATED:
489 		if (ober_get_integer(root, &v) == -1)
490 			goto fail;
491 		if (asprintf(&str, "%lld", v) == -1)
492 			goto fail;
493 		break;
494 	case BER_TYPE_BITSTRING:
495 		if (ober_get_bitstring(root, (void *)&buf, &len) == -1)
496 			goto fail;
497 		if ((str = calloc(1, len * 2 + 1)) == NULL)
498 			goto fail;
499 		for (p = str, i = 0; i < len; i++) {
500 			snprintf(p, 3, "%02x", buf[i]);
501 			p += 2;
502 		}
503 		break;
504 	case BER_TYPE_OBJECT:
505 		if (ober_get_oid(root, &o) == -1)
506 			goto fail;
507 		if (asprintf(&str, "%s",
508 		    smi_oid2string(&o, strbuf, sizeof(strbuf), 0)) == -1)
509 			goto fail;
510 		break;
511 	case BER_TYPE_OCTETSTRING:
512 		if (ober_get_string(root, &buf) == -1)
513 			goto fail;
514 		if (root->be_class == BER_CLASS_APPLICATION &&
515 		    root->be_type == SNMP_T_IPADDR) {
516 			if (asprintf(&str, "%s",
517 			    inet_ntoa(*(struct in_addr *)buf)) == -1)
518 				goto fail;
519 		} else {
520 			if ((p = reallocarray(NULL, 4, root->be_len + 1)) == NULL)
521 				goto fail;
522 			strvisx(p, buf, root->be_len, VIS_NL);
523 			if (asprintf(&str, "\"%s\"", p) == -1) {
524 				free(p);
525 				goto fail;
526 			}
527 			free(p);
528 		}
529 		break;
530 	case BER_TYPE_NULL:	/* no payload */
531 	case BER_TYPE_EOC:
532 	case BER_TYPE_SEQUENCE:
533 	case BER_TYPE_SET:
534 	default:
535 		str = strdup("");
536 		break;
537 	}
538 
539 	return (str);
540 
541  fail:
542 	free(str);
543 	return (NULL);
544 }
545 
546 unsigned int
547 smi_application(struct ber_element *elm)
548 {
549 	if (elm->be_class != BER_CLASS_APPLICATION)
550 		return (BER_TYPE_OCTETSTRING);
551 
552 	switch (elm->be_type) {
553 	case SNMP_T_IPADDR:
554 		return (BER_TYPE_OCTETSTRING);
555 	case SNMP_T_COUNTER32:
556 	case SNMP_T_GAUGE32:
557 	case SNMP_T_TIMETICKS:
558 	case SNMP_T_OPAQUE:
559 	case SNMP_T_COUNTER64:
560 		return (BER_TYPE_INTEGER);
561 	default:
562 		break;
563 	}
564 	return (BER_TYPE_OCTETSTRING);
565 }
566 
567 int
568 smi_oid_cmp(struct oid *a, struct oid *b)
569 {
570 	size_t	 i;
571 
572 	for (i = 0; i < MINIMUM(a->o_oidlen, b->o_oidlen); i++)
573 		if (a->o_oid[i] != b->o_oid[i])
574 			return (a->o_oid[i] - b->o_oid[i]);
575 
576 	/*
577 	 * Return success if the matched object is a table
578 	 * or a MIB registered by a subagent
579 	 * (it will match any sub-elements)
580 	 */
581 	if ((b->o_flags & OID_TABLE ||
582 	    b->o_flags & OID_REGISTERED) &&
583 	    (a->o_flags & OID_KEY) == 0 &&
584 	    (a->o_oidlen > b->o_oidlen))
585 		return (0);
586 
587 	return (a->o_oidlen - b->o_oidlen);
588 }
589 
590 RB_GENERATE(oidtree, oid, o_element, smi_oid_cmp);
591 
592 int
593 smi_key_cmp(struct oid *a, struct oid *b)
594 {
595 	if (a->o_name == NULL || b->o_name == NULL)
596 		return (-1);
597 	return (strcasecmp(a->o_name, b->o_name));
598 }
599 
600 RB_GENERATE(keytree, oid, o_keyword, smi_key_cmp);
601