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