xref: /openbsd-src/usr.sbin/snmpd/snmpe.c (revision f2da64fbbbf1b03f09f390ab01267c93dfd77c4c)
1 /*	$OpenBSD: snmpe.c,v 1.42 2016/08/16 18:41:57 tedu Exp $	*/
2 
3 /*
4  * Copyright (c) 2007, 2008, 2012 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 
26 #include <net/if.h>
27 #include <netinet/in.h>
28 #include <arpa/inet.h>
29 
30 #include <stdlib.h>
31 #include <stdio.h>
32 #include <errno.h>
33 #include <event.h>
34 #include <fcntl.h>
35 #include <string.h>
36 #include <unistd.h>
37 #include <pwd.h>
38 
39 #include "snmpd.h"
40 #include "mib.h"
41 
42 void	 snmpe_init(struct privsep *, struct privsep_proc *, void *);
43 int	 snmpe_parse(struct snmp_message *);
44 int	 snmpe_parsevarbinds(struct snmp_message *);
45 void	 snmpe_response(int, struct snmp_message *);
46 unsigned long
47 	 snmpe_application(struct ber_element *);
48 void	 snmpe_sig_handler(int sig, short, void *);
49 int	 snmpe_dispatch_parent(int, struct privsep_proc *, struct imsg *);
50 int	 snmpe_bind(struct address *);
51 void	 snmpe_recvmsg(int fd, short, void *);
52 int	 snmpe_encode(struct snmp_message *);
53 void	 snmp_msgfree(struct snmp_message *);
54 
55 struct snmpd	*env = NULL;
56 
57 struct imsgev	*iev_parent;
58 
59 static struct privsep_proc procs[] = {
60 	{ "parent",	PROC_PARENT,	snmpe_dispatch_parent }
61 };
62 
63 pid_t
64 snmpe(struct privsep *ps, struct privsep_proc *p)
65 {
66 #ifdef DEBUG
67 	char		 buf[BUFSIZ];
68 	struct oid	*oid;
69 #endif
70 
71 	env = ps->ps_env;
72 
73 #ifdef DEBUG
74 	for (oid = NULL; (oid = smi_foreach(oid, 0)) != NULL;) {
75 		smi_oid2string(&oid->o_id, buf, sizeof(buf), 0);
76 		log_debug("oid %s", buf);
77 	}
78 #endif
79 
80 	/* bind SNMP UDP socket */
81 	if ((env->sc_sock = snmpe_bind(&env->sc_address)) == -1)
82 		fatalx("snmpe: failed to bind SNMP UDP socket");
83 
84 	return (proc_run(ps, p, procs, nitems(procs), snmpe_init, NULL));
85 }
86 
87 /* ARGSUSED */
88 void
89 snmpe_init(struct privsep *ps, struct privsep_proc *p, void *arg)
90 {
91 	kr_init();
92 	trap_init();
93 	timer_init();
94 	usm_generate_keys();
95 
96 	/* listen for incoming SNMP UDP messages */
97 	event_set(&env->sc_ev, env->sc_sock, EV_READ|EV_PERSIST,
98 	    snmpe_recvmsg, env);
99 	event_add(&env->sc_ev, NULL);
100 }
101 
102 void
103 snmpe_shutdown(void)
104 {
105 	kr_shutdown();
106 }
107 
108 int
109 snmpe_dispatch_parent(int fd, struct privsep_proc *p, struct imsg *imsg)
110 {
111 	switch (imsg->hdr.type) {
112 	default:
113 		break;
114 	}
115 
116 	return (-1);
117 }
118 
119 int
120 snmpe_bind(struct address *addr)
121 {
122 	char	 buf[512];
123 	int	 s;
124 
125 	if ((s = snmpd_socket_af(&addr->ss, htons(addr->port))) == -1)
126 		return (-1);
127 
128 	/*
129 	 * Socket options
130 	 */
131 	if (fcntl(s, F_SETFL, O_NONBLOCK) == -1)
132 		goto bad;
133 
134 	if (bind(s, (struct sockaddr *)&addr->ss, addr->ss.ss_len) == -1)
135 		goto bad;
136 
137 	if (print_host(&addr->ss, buf, sizeof(buf)) == NULL)
138 		goto bad;
139 
140 	log_info("snmpe_bind: binding to address %s:%d", buf, addr->port);
141 
142 	return (s);
143 
144  bad:
145 	close(s);
146 	return (-1);
147 }
148 
149 int
150 snmpe_parse(struct snmp_message *msg)
151 {
152 	struct snmp_stats	*stats = &env->sc_stats;
153 	struct ber_element	*a;
154 	long long		 ver, req;
155 	long long		 errval, erridx;
156 	unsigned long		 type;
157 	u_int			 class;
158 	char			*comn;
159 	char			*flagstr, *ctxname;
160 	size_t			 len;
161 	struct sockaddr_storage *ss = &msg->sm_ss;
162 	struct ber_element	*root = msg->sm_req;
163 
164 	msg->sm_errstr = "invalid message";
165 
166 	if (ber_scanf_elements(root, "{ie", &ver, &a) != 0)
167 		goto parsefail;
168 
169 	/* SNMP version and community */
170 	msg->sm_version = ver;
171 	switch (msg->sm_version) {
172 	case SNMP_V1:
173 	case SNMP_V2:
174 		if (env->sc_min_seclevel != 0)
175 			goto badversion;
176 		if (ber_scanf_elements(a, "se", &comn, &msg->sm_pdu) != 0)
177 			goto parsefail;
178 		if (strlcpy(msg->sm_community, comn,
179 		    sizeof(msg->sm_community)) >= sizeof(msg->sm_community)) {
180 			stats->snmp_inbadcommunitynames++;
181 			msg->sm_errstr = "community name too long";
182 			goto fail;
183 		}
184 		break;
185 	case SNMP_V3:
186 		if (ber_scanf_elements(a, "{iisi}e",
187 		    &msg->sm_msgid, &msg->sm_max_msg_size, &flagstr,
188 		    &msg->sm_secmodel, &a) != 0)
189 			goto parsefail;
190 
191 		msg->sm_flags = *flagstr;
192 		if (MSG_SECLEVEL(msg) < env->sc_min_seclevel ||
193 		    msg->sm_secmodel != SNMP_SEC_USM) {
194 			/* XXX currently only USM supported */
195 			msg->sm_errstr = "unsupported security model";
196 			stats->snmp_usmbadseclevel++;
197 			msg->sm_usmerr = OIDVAL_usmErrSecLevel;
198 			goto parsefail;
199 		}
200 
201 		if ((a = usm_decode(msg, a, &msg->sm_errstr)) == NULL)
202 			goto parsefail;
203 
204 		if (ber_scanf_elements(a, "{xxe",
205 		    &msg->sm_ctxengineid, &msg->sm_ctxengineid_len,
206 		    &ctxname, &len, &msg->sm_pdu) != 0)
207 			goto parsefail;
208 		if (len > SNMPD_MAXCONTEXNAMELEN)
209 			goto parsefail;
210 		memcpy(msg->sm_ctxname, ctxname, len);
211 		msg->sm_ctxname[len] = '\0';
212 		break;
213 	default:
214 	badversion:
215 		stats->snmp_inbadversions++;
216 		msg->sm_errstr = "bad snmp version";
217 		goto fail;
218 	}
219 
220 	if (ber_scanf_elements(msg->sm_pdu, "t{e", &class, &type, &a) != 0)
221 		goto parsefail;
222 
223 	/* SNMP PDU context */
224 	if (class != BER_CLASS_CONTEXT)
225 		goto parsefail;
226 
227 	switch (type) {
228 	case SNMP_C_GETBULKREQ:
229 		if (msg->sm_version == SNMP_V1) {
230 			stats->snmp_inbadversions++;
231 			msg->sm_errstr =
232 			    "invalid request for protocol version 1";
233 			goto fail;
234 		}
235 		/* FALLTHROUGH */
236 
237 	case SNMP_C_GETREQ:
238 		stats->snmp_ingetrequests++;
239 		/* FALLTHROUGH */
240 
241 	case SNMP_C_GETNEXTREQ:
242 		if (type == SNMP_C_GETNEXTREQ)
243 			stats->snmp_ingetnexts++;
244 		if (msg->sm_version != SNMP_V3 &&
245 		    strcmp(env->sc_rdcommunity, msg->sm_community) != 0 &&
246 		    strcmp(env->sc_rwcommunity, msg->sm_community) != 0) {
247 			stats->snmp_inbadcommunitynames++;
248 			msg->sm_errstr = "wrong read community";
249 			goto fail;
250 		}
251 		msg->sm_context = type;
252 		break;
253 
254 	case SNMP_C_SETREQ:
255 		stats->snmp_insetrequests++;
256 		if (msg->sm_version != SNMP_V3 &&
257 		    strcmp(env->sc_rwcommunity, msg->sm_community) != 0) {
258 			if (strcmp(env->sc_rdcommunity, msg->sm_community) != 0)
259 				stats->snmp_inbadcommunitynames++;
260 			else
261 				stats->snmp_inbadcommunityuses++;
262 			msg->sm_errstr = "wrong write community";
263 			goto fail;
264 		}
265 		msg->sm_context = type;
266 		break;
267 
268 	case SNMP_C_GETRESP:
269 		stats->snmp_ingetresponses++;
270 		msg->sm_errstr = "response without request";
271 		goto parsefail;
272 
273 	case SNMP_C_TRAP:
274 	case SNMP_C_TRAPV2:
275 		if (msg->sm_version != SNMP_V3 &&
276 		    strcmp(env->sc_trcommunity, msg->sm_community) != 0) {
277 			stats->snmp_inbadcommunitynames++;
278 			msg->sm_errstr = "wrong trap community";
279 			goto fail;
280 		}
281 		stats->snmp_intraps++;
282 		msg->sm_errstr = "received trap";
283 		goto fail;
284 
285 	default:
286 		msg->sm_errstr = "invalid context";
287 		goto parsefail;
288 	}
289 
290 	/* SNMP PDU */
291 	if (ber_scanf_elements(a, "iiie{et",
292 	    &req, &errval, &erridx, &msg->sm_pduend,
293 	    &msg->sm_varbind, &class, &type) != 0) {
294 		stats->snmp_silentdrops++;
295 		msg->sm_errstr = "invalid PDU";
296 		goto fail;
297 	}
298 	if (class != BER_CLASS_UNIVERSAL || type != BER_TYPE_SEQUENCE) {
299 		stats->snmp_silentdrops++;
300 		msg->sm_errstr = "invalid varbind";
301 		goto fail;
302 	}
303 
304 	msg->sm_request = req;
305 	msg->sm_error = errval;
306 	msg->sm_errorindex = erridx;
307 
308 	print_host(ss, msg->sm_host, sizeof(msg->sm_host));
309 	if (msg->sm_version == SNMP_V3)
310 		log_debug("%s: %s: SNMPv3 context %d, flags %#x, "
311 		    "secmodel %lld, user '%s', ctx-engine %s, ctx-name '%s', "
312 		    "request %lld", __func__, msg->sm_host, msg->sm_context,
313 		    msg->sm_flags, msg->sm_secmodel, msg->sm_username,
314 		    tohexstr(msg->sm_ctxengineid, msg->sm_ctxengineid_len),
315 		    msg->sm_ctxname, msg->sm_request);
316 	else
317 		log_debug("%s: %s: SNMPv%d '%s' context %d request %lld",
318 		    __func__, msg->sm_host, msg->sm_version + 1,
319 		    msg->sm_community, msg->sm_context, msg->sm_request);
320 
321 	return (0);
322 
323  parsefail:
324 	stats->snmp_inasnparseerrs++;
325  fail:
326 	print_host(ss, msg->sm_host, sizeof(msg->sm_host));
327 	log_debug("%s: %s: %s", __func__, msg->sm_host, msg->sm_errstr);
328 	return (-1);
329 }
330 
331 int
332 snmpe_parsevarbinds(struct snmp_message *msg)
333 {
334 	struct snmp_stats	*stats = &env->sc_stats;
335 	char			 buf[BUFSIZ];
336 	struct ber_oid		 o;
337 	int			 ret = 0;
338 
339 	msg->sm_errstr = "invalid varbind element";
340 
341 	if (msg->sm_i == 0) {
342 		msg->sm_i = 1;
343 		msg->sm_a = msg->sm_varbind;
344 		msg->sm_last = NULL;
345 	}
346 
347 	 for (; msg->sm_a != NULL && msg->sm_i < SNMPD_MAXVARBIND;
348 	    msg->sm_a = msg->sm_next, msg->sm_i++) {
349 		msg->sm_next = msg->sm_a->be_next;
350 
351 		if (msg->sm_a->be_class != BER_CLASS_UNIVERSAL ||
352 		    msg->sm_a->be_type != BER_TYPE_SEQUENCE)
353 			continue;
354 		if ((msg->sm_b = msg->sm_a->be_sub) == NULL)
355 			continue;
356 
357 		for (msg->sm_state = 0; msg->sm_state < 2 && msg->sm_b != NULL;
358 		    msg->sm_b = msg->sm_b->be_next) {
359 			switch (msg->sm_state++) {
360 			case 0:
361 				if (ber_get_oid(msg->sm_b, &o) != 0)
362 					goto varfail;
363 				if (o.bo_n < BER_MIN_OID_LEN ||
364 				    o.bo_n > BER_MAX_OID_LEN)
365 					goto varfail;
366 				if (msg->sm_context == SNMP_C_SETREQ)
367 					stats->snmp_intotalsetvars++;
368 				else
369 					stats->snmp_intotalreqvars++;
370 				log_debug("%s: %s: oid %s",
371 				    __func__, msg->sm_host,
372 				    smi_oid2string(&o, buf, sizeof(buf), 0));
373 				break;
374 			case 1:
375 				msg->sm_c = NULL;
376 				msg->sm_end = NULL;
377 
378 				switch (msg->sm_context) {
379 
380 				case SNMP_C_GETNEXTREQ:
381 					msg->sm_c = ber_add_sequence(NULL);
382 					ret = mps_getnextreq(msg, msg->sm_c,
383 					    &o);
384 					if (ret == 0 || ret == 1)
385 						break;
386 					ber_free_elements(msg->sm_c);
387 					msg->sm_error = SNMP_ERROR_NOSUCHNAME;
388 					goto varfail;
389 
390 				case SNMP_C_GETREQ:
391 					msg->sm_c = ber_add_sequence(NULL);
392 					ret = mps_getreq(msg, msg->sm_c, &o,
393 					    msg->sm_version);
394 					if (ret == 0 || ret == 1)
395 						break;
396 					msg->sm_error = SNMP_ERROR_NOSUCHNAME;
397 					ber_free_elements(msg->sm_c);
398 					goto varfail;
399 
400 				case SNMP_C_SETREQ:
401 					if (env->sc_readonly == 0) {
402 						ret = mps_setreq(msg,
403 						    msg->sm_b, &o);
404 						if (ret == 0)
405 							break;
406 					}
407 					msg->sm_error = SNMP_ERROR_READONLY;
408 					goto varfail;
409 
410 				case SNMP_C_GETBULKREQ:
411 					ret = mps_getbulkreq(msg, &msg->sm_c,
412 					    &msg->sm_end, &o,
413 					    msg->sm_maxrepetitions);
414 					if (ret == 0 || ret == 1)
415 						break;
416 					msg->sm_error = SNMP_ERROR_NOSUCHNAME;
417 					goto varfail;
418 
419 				default:
420 					goto varfail;
421 				}
422 				if (msg->sm_c == NULL)
423 					break;
424 				if (msg->sm_end == NULL)
425 					msg->sm_end = msg->sm_c;
426 				if (msg->sm_last == NULL)
427 					msg->sm_varbindresp = msg->sm_c;
428 				else
429 					ber_link_elements(msg->sm_last, msg->sm_c);
430 				msg->sm_last = msg->sm_end;
431 				break;
432 			}
433 		}
434 		if (msg->sm_state < 2)  {
435 			log_debug("%s: state %d", __func__, msg->sm_state);
436 			goto varfail;
437 		}
438 	}
439 
440 	msg->sm_errstr = "none";
441 
442 	return (ret);
443  varfail:
444 	log_debug("%s: %s: %s, error index %d", __func__,
445 	    msg->sm_host, msg->sm_errstr, msg->sm_i);
446 	if (msg->sm_error == 0)
447 		msg->sm_error = SNMP_ERROR_GENERR;
448 	msg->sm_errorindex = msg->sm_i;
449 	return (-1);
450 }
451 
452 void
453 snmpe_recvmsg(int fd, short sig, void *arg)
454 {
455 	struct snmp_stats	*stats = &env->sc_stats;
456 	ssize_t			 len;
457 	struct snmp_message	*msg;
458 
459 	if ((msg = calloc(1, sizeof(*msg))) == NULL)
460 		return;
461 
462 	msg->sm_slen = sizeof(msg->sm_ss);
463 	if ((len = recvfrom(fd, msg->sm_data, sizeof(msg->sm_data), 0,
464 	    (struct sockaddr *)&msg->sm_ss, &msg->sm_slen)) < 1) {
465 		free(msg);
466 		return;
467 	}
468 
469 	stats->snmp_inpkts++;
470 	msg->sm_datalen = (size_t)len;
471 
472 	bzero(&msg->sm_ber, sizeof(msg->sm_ber));
473 	msg->sm_ber.fd = -1;
474 	ber_set_application(&msg->sm_ber, smi_application);
475 	ber_set_readbuf(&msg->sm_ber, msg->sm_data, msg->sm_datalen);
476 
477 	msg->sm_req = ber_read_elements(&msg->sm_ber, NULL);
478 	if (msg->sm_req == NULL) {
479 		stats->snmp_inasnparseerrs++;
480 		snmp_msgfree(msg);
481 		return;
482 	}
483 
484 #ifdef DEBUG
485 	fprintf(stderr, "recv msg:\n");
486 	smi_debug_elements(msg->sm_req);
487 #endif
488 
489 	if (snmpe_parse(msg) == -1) {
490 		if (msg->sm_usmerr != 0 && MSG_REPORT(msg)) {
491 			usm_make_report(msg);
492 			snmpe_response(fd, msg);
493 			return;
494 		} else {
495 			snmp_msgfree(msg);
496 			return;
497 		}
498 	}
499 
500 	snmpe_dispatchmsg(msg);
501 }
502 
503 void
504 snmpe_dispatchmsg(struct snmp_message *msg)
505 {
506 	if (snmpe_parsevarbinds(msg) == 1)
507 		return;
508 
509 	/* not dispatched to subagent; respond directly */
510 	msg->sm_context = SNMP_C_GETRESP;
511 	snmpe_response(env->sc_sock, msg);
512 }
513 
514 void
515 snmpe_response(int fd, struct snmp_message *msg)
516 {
517 	struct snmp_stats	*stats = &env->sc_stats;
518 	u_int8_t		*ptr = NULL;
519 	ssize_t			 len;
520 
521 	if (msg->sm_varbindresp == NULL && msg->sm_pduend != NULL)
522 		msg->sm_varbindresp = ber_unlink_elements(msg->sm_pduend);
523 
524 	switch (msg->sm_error) {
525 	case SNMP_ERROR_TOOBIG:
526 		stats->snmp_intoobigs++;
527 		break;
528 	case SNMP_ERROR_NOSUCHNAME:
529 		stats->snmp_innosuchnames++;
530 		break;
531 	case SNMP_ERROR_BADVALUE:
532 		stats->snmp_inbadvalues++;
533 		break;
534 	case SNMP_ERROR_READONLY:
535 		stats->snmp_inreadonlys++;
536 		break;
537 	case SNMP_ERROR_GENERR:
538 	default:
539 		stats->snmp_ingenerrs++;
540 		break;
541 	}
542 
543 	/* Create new SNMP packet */
544 	if (snmpe_encode(msg) < 0)
545 		goto done;
546 
547 	len = ber_write_elements(&msg->sm_ber, msg->sm_resp);
548 	if (ber_get_writebuf(&msg->sm_ber, (void *)&ptr) == -1)
549 		goto done;
550 
551 	usm_finalize_digest(msg, ptr, len);
552 	len = sendto(fd, ptr, len, 0, (struct sockaddr *)&msg->sm_ss,
553 	    msg->sm_slen);
554 	if (len != -1)
555 		stats->snmp_outpkts++;
556 
557  done:
558 	snmp_msgfree(msg);
559 }
560 
561 void
562 snmp_msgfree(struct snmp_message *msg)
563 {
564 	ber_free(&msg->sm_ber);
565 	if (msg->sm_req != NULL)
566 		ber_free_elements(msg->sm_req);
567 	if (msg->sm_resp != NULL)
568 		ber_free_elements(msg->sm_resp);
569 	free(msg);
570 }
571 
572 int
573 snmpe_encode(struct snmp_message *msg)
574 {
575 	struct ber_element	*ehdr;
576 	struct ber_element	*pdu, *epdu;
577 
578 	msg->sm_resp = ber_add_sequence(NULL);
579 	if ((ehdr = ber_add_integer(msg->sm_resp, msg->sm_version)) == NULL)
580 		return -1;
581 	if (msg->sm_version == SNMP_V3) {
582 		char	f = MSG_SECLEVEL(msg);
583 
584 		if ((ehdr = ber_printf_elements(ehdr, "{iixi}", msg->sm_msgid,
585 		    msg->sm_max_msg_size, &f, sizeof(f),
586 		    msg->sm_secmodel)) == NULL)
587 			return -1;
588 
589 		/* XXX currently only USM supported */
590 		if ((ehdr = usm_encode(msg, ehdr)) == NULL)
591 			return -1;
592 	} else {
593 		if ((ehdr = ber_add_string(ehdr, msg->sm_community)) == NULL)
594 			return -1;
595 	}
596 
597 	pdu = epdu = ber_add_sequence(NULL);
598 	if (msg->sm_version == SNMP_V3) {
599 		if ((epdu = ber_printf_elements(epdu, "xs{", env->sc_engineid,
600 		    env->sc_engineid_len, msg->sm_ctxname)) == NULL) {
601 			ber_free_elements(pdu);
602 			return -1;
603 		}
604 	}
605 
606 	if (!ber_printf_elements(epdu, "tiii{e}.", BER_CLASS_CONTEXT,
607 	    msg->sm_context, msg->sm_request,
608 	    msg->sm_error, msg->sm_errorindex,
609 	    msg->sm_varbindresp)) {
610 		ber_free_elements(pdu);
611 		return -1;
612 	}
613 
614 	if (MSG_HAS_PRIV(msg))
615 		pdu = usm_encrypt(msg, pdu);
616 	ber_link_elements(ehdr, pdu);
617 
618 #ifdef DEBUG
619 	fprintf(stderr, "resp msg:\n");
620 	smi_debug_elements(msg->sm_resp);
621 #endif
622 	return 0;
623 }
624