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