xref: /openbsd/usr.sbin/snmpd/snmpe.c (revision ce7279d8)
1 /*	$OpenBSD: snmpe.c,v 1.95 2024/05/21 05:00:48 jsg Exp $	*/
2 
3 /*
4  * Copyright (c) 2007, 2008, 2012 Reyk Floeter <reyk@openbsd.org>
5  * Copyright (c) 2017 Marco Pfatschbacher <mpf@openbsd.org>
6  *
7  * Permission to use, copy, modify, and distribute this software for any
8  * purpose with or without fee is hereby granted, provided that the above
9  * copyright notice and this permission notice appear in all copies.
10  *
11  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
12  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
13  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
14  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
15  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
16  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
17  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
18  */
19 
20 #include <sys/queue.h>
21 #include <sys/socket.h>
22 #include <sys/time.h>
23 #include <sys/tree.h>
24 #include <sys/types.h>
25 
26 #include <netinet/in.h>
27 
28 #include <ber.h>
29 #include <event.h>
30 #include <errno.h>
31 #include <fcntl.h>
32 #include <imsg.h>
33 #include <locale.h>
34 #include <stdlib.h>
35 #include <stdio.h>
36 #include <string.h>
37 #include <strings.h>
38 #include <unistd.h>
39 
40 #include "application.h"
41 #include "log.h"
42 #include "snmpd.h"
43 #include "snmpe.h"
44 #include "mib.h"
45 
46 void	 snmpe_init(struct privsep *, struct privsep_proc *, void *);
47 int	 snmpe_dispatch_parent(int, struct privsep_proc *, struct imsg *);
48 int	 snmpe_parse(struct snmp_message *);
49 void	 snmpe_tryparse(int, struct snmp_message *);
50 int	 snmpe_parsevarbinds(struct snmp_message *);
51 int	 snmpe_bind(struct address *);
52 void	 snmpe_recvmsg(int fd, short, void *);
53 void	 snmpe_readcb(int fd, short, void *);
54 void	 snmpe_writecb(int fd, short, void *);
55 void	 snmpe_acceptcb(int fd, short, void *);
56 void	 snmpe_prepare_read(struct snmp_message *, int);
57 int	 snmpe_encode(struct snmp_message *);
58 
59 struct imsgev	*iev_parent;
60 static const struct timeval	snmpe_tcp_timeout = { 10, 0 }; /* 10s */
61 
62 struct snmp_messages snmp_messages = RB_INITIALIZER(&snmp_messages);
63 
64 static struct privsep_proc procs[] = {
65 	{ "parent",	PROC_PARENT, snmpe_dispatch_parent }
66 };
67 
68 void
snmpe(struct privsep * ps,struct privsep_proc * p)69 snmpe(struct privsep *ps, struct privsep_proc *p)
70 {
71 	struct snmpd		*env = ps->ps_env;
72 	struct address		*h;
73 
74 	if ((setlocale(LC_CTYPE, "en_US.UTF-8")) == NULL)
75 		fatal("setlocale(LC_CTYPE, \"en_US.UTF-8\")");
76 
77 	appl();
78 
79 	/* bind SNMP UDP/TCP sockets */
80 	TAILQ_FOREACH(h, &env->sc_addresses, entry)
81 		if ((h->fd = snmpe_bind(h)) == -1)
82 			fatal("snmpe: failed to bind SNMP socket");
83 
84 	proc_run(ps, p, procs, nitems(procs), snmpe_init, NULL);
85 }
86 
87 void
snmpe_init(struct privsep * ps,struct privsep_proc * p,void * arg)88 snmpe_init(struct privsep *ps, struct privsep_proc *p, void *arg)
89 {
90 	struct snmpd		*env = ps->ps_env;
91 	struct address		*h;
92 
93 	usm_generate_keys();
94 	appl_init();
95 
96 	/* listen for incoming SNMP UDP/TCP messages */
97 	TAILQ_FOREACH(h, &env->sc_addresses, entry) {
98 		if (h->type == SOCK_STREAM) {
99 			if (listen(h->fd, 5) < 0)
100 				fatalx("snmpe: failed to listen on socket");
101 			event_set(&h->ev, h->fd, EV_READ, snmpe_acceptcb, h);
102 			evtimer_set(&h->evt, snmpe_acceptcb, h);
103 		} else {
104 			event_set(&h->ev, h->fd, EV_READ|EV_PERSIST,
105 			    snmpe_recvmsg, h);
106 		}
107 		event_add(&h->ev, NULL);
108 	}
109 
110 	/* no filesystem visibility */
111 	if (unveil("/", "") == -1)
112 		fatal("unveil /");
113 	if (pledge("stdio recvfd inet unix", NULL) == -1)
114 		fatal("pledge");
115 
116 	log_info("snmpe %s: ready",
117 	    tohexstr(env->sc_engineid, env->sc_engineid_len));
118 	trap_init();
119 }
120 
121 void
snmpe_shutdown(void)122 snmpe_shutdown(void)
123 {
124 	struct address *h;
125 
126 	TAILQ_FOREACH(h, &snmpd_env->sc_addresses, entry) {
127 		event_del(&h->ev);
128 		if (h->type == SOCK_STREAM)
129 			event_del(&h->evt);
130 		close(h->fd);
131 	}
132 	appl_shutdown();
133 }
134 
135 int
snmpe_dispatch_parent(int fd,struct privsep_proc * p,struct imsg * imsg)136 snmpe_dispatch_parent(int fd, struct privsep_proc *p, struct imsg *imsg)
137 {
138 	switch (imsg->hdr.type) {
139 	case IMSG_AX_FD:
140 		appl_agentx_backend(imsg_get_fd(imsg));
141 		return 0;
142 	default:
143 		return -1;
144 	}
145 }
146 
147 int
snmpe_bind(struct address * addr)148 snmpe_bind(struct address *addr)
149 {
150 	char	 buf[512];
151 	int	 val, s;
152 
153 	if ((s = snmpd_socket_af(&addr->ss, addr->type)) == -1)
154 		return (-1);
155 
156 	/*
157 	 * Socket options
158 	 */
159 	if (fcntl(s, F_SETFL, O_NONBLOCK) == -1)
160 		goto bad;
161 
162 	if (addr->type == SOCK_STREAM) {
163 		val = 1;
164 		if (setsockopt(s, SOL_SOCKET, SO_REUSEADDR,
165 		    &val, sizeof(val)) == -1)
166 			fatal("setsockopt SO_REUSEADDR");
167 	} else { /* UDP */
168 		switch (addr->ss.ss_family) {
169 		case AF_INET:
170 			val = 1;
171 			if (setsockopt(s, IPPROTO_IP, IP_RECVDSTADDR,
172 			    &val, sizeof(int)) == -1) {
173 				log_warn("%s: failed to set IPv4 packet info",
174 				    __func__);
175 				goto bad;
176 			}
177 			break;
178 		case AF_INET6:
179 			val = 1;
180 			if (setsockopt(s, IPPROTO_IPV6, IPV6_RECVPKTINFO,
181 			    &val, sizeof(int)) == -1) {
182 				log_warn("%s: failed to set IPv6 packet info",
183 				    __func__);
184 				goto bad;
185 			}
186 		}
187 	}
188 
189 	if (bind(s, (struct sockaddr *)&addr->ss, addr->ss.ss_len) == -1)
190 		goto bad;
191 
192 	if (print_host(&addr->ss, buf, sizeof(buf)) == NULL)
193 		goto bad;
194 
195 	log_info("snmpe: listening on %s %s:%d",
196 	    (addr->type == SOCK_STREAM) ? "tcp" : "udp", buf, addr->port);
197 
198 	return (s);
199 
200  bad:
201 	close(s);
202 	return (-1);
203 }
204 
205 const char *
snmpe_pdutype2string(enum snmp_pdutype pdutype)206 snmpe_pdutype2string(enum snmp_pdutype pdutype)
207 {
208 	static char unknown[sizeof("Unknown (4294967295)")];
209 
210 	switch (pdutype) {
211 	case SNMP_C_GETREQ:
212 		return "GetRequest";
213 	case SNMP_C_GETNEXTREQ:
214 		return "GetNextRequest";
215 	case SNMP_C_RESPONSE:
216 		return "Response";
217 	case SNMP_C_SETREQ:
218 		return "SetRequest";
219 	case SNMP_C_TRAP:
220 		return "Trap";
221 	case SNMP_C_GETBULKREQ:
222 		return "GetBulkRequest";
223 	case SNMP_C_INFORMREQ:
224 		return "InformRequest";
225 	case SNMP_C_TRAPV2:
226 		return "SNMPv2-Trap";
227 	case SNMP_C_REPORT:
228 		return "Report";
229 	}
230 
231 	snprintf(unknown, sizeof(unknown), "Unknown (%u)", pdutype);
232 	return unknown;
233 }
234 
235 int
snmpe_parse(struct snmp_message * msg)236 snmpe_parse(struct snmp_message *msg)
237 {
238 	struct snmpd		*env = snmpd_env;
239 	struct snmp_stats	*stats = &env->sc_stats;
240 	struct ber_element	*a;
241 	long long		 ver, req;
242 	long long		 errval, erridx;
243 	u_int			 class;
244 	char			*comn;
245 	char			*flagstr, *ctxname, *engineid;
246 	size_t			 len;
247 	struct sockaddr_storage *ss = &msg->sm_ss;
248 	struct ber_element	*root = msg->sm_req;
249 
250 	msg->sm_errstr = "invalid message";
251 
252 	do {
253 		msg->sm_transactionid = arc4random();
254 	} while (msg->sm_transactionid == 0 ||
255 	    RB_INSERT(snmp_messages, &snmp_messages, msg) != NULL);
256 
257 	if (ober_scanf_elements(root, "{ie", &ver, &a) != 0)
258 		goto parsefail;
259 
260 	/* SNMP version and community */
261 	msg->sm_version = ver;
262 	switch (msg->sm_version) {
263 	case SNMP_V1:
264 		if (!(msg->sm_aflags & ADDRESS_FLAG_SNMPV1)) {
265 			msg->sm_errstr = "SNMPv1 disabled";
266 			goto badversion;
267 		}
268 	case SNMP_V2:
269 		if (msg->sm_version == SNMP_V2 &&
270 		    !(msg->sm_aflags & ADDRESS_FLAG_SNMPV2)) {
271 			msg->sm_errstr = "SNMPv2c disabled";
272 			goto badversion;
273 		}
274 		if (ober_scanf_elements(a, "seS$", &comn, &msg->sm_pdu) != 0)
275 			goto parsefail;
276 		if (strlcpy(msg->sm_community, comn,
277 		    sizeof(msg->sm_community)) >= sizeof(msg->sm_community) ||
278 		    msg->sm_community[0] == '\0') {
279 			stats->snmp_inbadcommunitynames++;
280 			msg->sm_errstr = "invalid community name";
281 			goto fail;
282 		}
283 		break;
284 	case SNMP_V3:
285 		if (!(msg->sm_aflags & ADDRESS_FLAG_SNMPV3)) {
286 			msg->sm_errstr = "SNMPv3 disabled";
287 			goto badversion;
288 		}
289 		if (ober_scanf_elements(a, "{iisi$}e",
290 		    &msg->sm_msgid, &msg->sm_max_msg_size, &flagstr,
291 		    &msg->sm_secmodel, &a) != 0)
292 			goto parsefail;
293 
294 		msg->sm_flags = *flagstr;
295 		if ((a = usm_decode(msg, a, &msg->sm_errstr)) == NULL)
296 			goto parsefail;
297 
298 		if (MSG_SECLEVEL(msg) < env->sc_min_seclevel ||
299 		    msg->sm_secmodel != SNMP_SEC_USM) {
300 			/* XXX currently only USM supported */
301 			msg->sm_errstr = "unsupported security model";
302 			stats->snmp_usmbadseclevel++;
303 			msg->sm_usmerr = OIDVAL_usmErrSecLevel;
304 			goto parsefail;
305 		}
306 
307 		if (ober_scanf_elements(a, "{xxeS$}$",
308 		    &engineid, &msg->sm_ctxengineid_len, &ctxname, &len,
309 		    &msg->sm_pdu) != 0)
310 			goto parsefail;
311 		if (msg->sm_ctxengineid_len > sizeof(msg->sm_ctxengineid))
312 			goto parsefail;
313 		memcpy(msg->sm_ctxengineid, engineid, msg->sm_ctxengineid_len);
314 		if (len > SNMPD_MAXCONTEXNAMELEN)
315 			goto parsefail;
316 		memcpy(msg->sm_ctxname, ctxname, len);
317 		msg->sm_ctxname[len] = '\0';
318 		break;
319 	default:
320 		msg->sm_errstr = "unsupported snmp version";
321 badversion:
322 		stats->snmp_inbadversions++;
323 		goto fail;
324 	}
325 
326 	if (ober_scanf_elements(msg->sm_pdu, "t{e", &class, &(msg->sm_pdutype),
327 	    &a) != 0)
328 		goto parsefail;
329 
330 	/* SNMP PDU context */
331 	if (class != BER_CLASS_CONTEXT)
332 		goto parsefail;
333 
334 	switch (msg->sm_pdutype) {
335 	case SNMP_C_GETBULKREQ:
336 		if (msg->sm_version == SNMP_V1) {
337 			stats->snmp_inbadversions++;
338 			msg->sm_errstr =
339 			    "invalid request for protocol version 1";
340 			goto fail;
341 		}
342 		/* FALLTHROUGH */
343 
344 	case SNMP_C_GETREQ:
345 		stats->snmp_ingetrequests++;
346 		/* FALLTHROUGH */
347 
348 	case SNMP_C_GETNEXTREQ:
349 		if (msg->sm_pdutype == SNMP_C_GETNEXTREQ)
350 			stats->snmp_ingetnexts++;
351 		if (!(msg->sm_aflags & ADDRESS_FLAG_READ)) {
352 			msg->sm_errstr = "read requests disabled";
353 			goto fail;
354 		}
355 		if (msg->sm_version != SNMP_V3 &&
356 		    strcmp(env->sc_rdcommunity, msg->sm_community) != 0 &&
357 		    strcmp(env->sc_rwcommunity, msg->sm_community) != 0) {
358 			stats->snmp_inbadcommunitynames++;
359 			msg->sm_errstr = "wrong read community";
360 			goto fail;
361 		}
362 		break;
363 
364 	case SNMP_C_SETREQ:
365 		stats->snmp_insetrequests++;
366 		if (!(msg->sm_aflags & ADDRESS_FLAG_WRITE)) {
367 			msg->sm_errstr = "write requests disabled";
368 			goto fail;
369 		}
370 		if (msg->sm_version != SNMP_V3 &&
371 		    strcmp(env->sc_rwcommunity, msg->sm_community) != 0) {
372 			if (strcmp(env->sc_rdcommunity, msg->sm_community) != 0)
373 				stats->snmp_inbadcommunitynames++;
374 			else
375 				stats->snmp_inbadcommunityuses++;
376 			msg->sm_errstr = "wrong write community";
377 			goto fail;
378 		}
379 		break;
380 
381 	case SNMP_C_RESPONSE:
382 		stats->snmp_ingetresponses++;
383 		msg->sm_errstr = "response without request";
384 		goto parsefail;
385 
386 	case SNMP_C_TRAP:
387 		if (msg->sm_version != SNMP_V1) {
388 			msg->sm_errstr = "trapv1 request on !SNMPv1 message";
389 			goto parsefail;
390 		}
391 	case SNMP_C_TRAPV2:
392 		if (msg->sm_pdutype == SNMP_C_TRAPV2 &&
393 		    !(msg->sm_version == SNMP_V2 ||
394 		    msg->sm_version == SNMP_V3)) {
395 			msg->sm_errstr = "trapv2 request on !SNMPv2C or "
396 			    "!SNMPv3 message";
397 			goto parsefail;
398 		}
399 		if (!(msg->sm_aflags & ADDRESS_FLAG_NOTIFY)) {
400 			msg->sm_errstr = "notify requests disabled";
401 			goto fail;
402 		}
403 		if (msg->sm_version == SNMP_V3) {
404 			msg->sm_errstr = "SNMPv3 doesn't support traps yet";
405 			goto fail;
406 		}
407 		if (strcmp(env->sc_trcommunity, msg->sm_community) != 0) {
408 			stats->snmp_inbadcommunitynames++;
409 			msg->sm_errstr = "wrong trap community";
410 			goto fail;
411 		}
412 		stats->snmp_intraps++;
413 		/*
414 		 * This should probably go into parsevarbinds, but that's for a
415 		 * next refactor
416 		 */
417 		if (traphandler_parse(msg) == -1)
418 			goto fail;
419 		/* Shortcircuit */
420 		return 0;
421 	default:
422 		msg->sm_errstr = "invalid context";
423 		goto parsefail;
424 	}
425 
426 	/* SNMP PDU */
427 	if (ober_scanf_elements(a, "iiie{e{}}$",
428 	    &req, &errval, &erridx, &msg->sm_pduend,
429 	    &msg->sm_varbind) != 0) {
430 		stats->snmp_silentdrops++;
431 		msg->sm_errstr = "invalid PDU";
432 		goto fail;
433 	}
434 
435 	for (len = 0, a = msg->sm_varbind; a != NULL; a = a->be_next, len++) {
436 		if (ober_scanf_elements(a, "{oS$}", NULL) == -1)
437 			goto parsefail;
438 	}
439 	/*
440 	 * error-status == non-repeaters
441 	 * error-index == max-repetitions
442 	 */
443 	if (msg->sm_pdutype == SNMP_C_GETBULKREQ &&
444 	    (errval < 0 || errval > (long long)len ||
445 	    erridx < 1 || erridx > UINT16_MAX))
446 		goto parsefail;
447 
448 	msg->sm_request = req;
449 	msg->sm_error = errval;
450 	msg->sm_errorindex = erridx;
451 
452 	print_host(ss, msg->sm_host, sizeof(msg->sm_host));
453 	if (msg->sm_version == SNMP_V3)
454 		log_debug("%s: %s:%hd: SNMPv3 pdutype %s, flags %#x, "
455 		    "secmodel %lld, user '%s', ctx-engine %s, ctx-name '%s', "
456 		    "request %lld", __func__, msg->sm_host, msg->sm_port,
457 		    snmpe_pdutype2string(msg->sm_pdutype), msg->sm_flags,
458 		    msg->sm_secmodel, msg->sm_username,
459 		    tohexstr(msg->sm_ctxengineid, msg->sm_ctxengineid_len),
460 		    msg->sm_ctxname, msg->sm_request);
461 	else
462 		log_debug("%s: %s:%hd: SNMPv%d '%s' pdutype %s request %lld",
463 		    __func__, msg->sm_host, msg->sm_port, msg->sm_version + 1,
464 		    msg->sm_community, snmpe_pdutype2string(msg->sm_pdutype),
465 		    msg->sm_request);
466 
467 	return (0);
468 
469  parsefail:
470 	stats->snmp_inasnparseerrs++;
471  fail:
472 	print_host(ss, msg->sm_host, sizeof(msg->sm_host));
473 	log_debug("%s: %s:%hd: %s", __func__, msg->sm_host, msg->sm_port,
474 	    msg->sm_errstr);
475 	return (-1);
476 }
477 
478 int
snmpe_parsevarbinds(struct snmp_message * msg)479 snmpe_parsevarbinds(struct snmp_message *msg)
480 {
481 	appl_processpdu(msg, msg->sm_ctxname, msg->sm_version, msg->sm_pdu);
482 	return 0;
483 }
484 
485 void
snmpe_acceptcb(int fd,short type,void * arg)486 snmpe_acceptcb(int fd, short type, void *arg)
487 {
488 	struct address		*h = arg;
489 	struct sockaddr_storage	 ss;
490 	socklen_t		 len = sizeof(ss);
491 	struct snmp_message	*msg;
492 	int afd;
493 
494 	event_add(&h->ev, NULL);
495 	if ((type & EV_TIMEOUT))
496 		return;
497 
498 	if ((afd = accept4(fd, (struct sockaddr *)&ss, &len,
499 	    SOCK_NONBLOCK|SOCK_CLOEXEC)) < 0) {
500 		/* Pause accept if we are out of file descriptors  */
501 		if (errno == ENFILE || errno == EMFILE) {
502 			struct timeval evtpause = { 1, 0 };
503 
504 			event_del(&h->ev);
505 			evtimer_add(&h->evt, &evtpause);
506 		} else if (errno != EAGAIN && errno != EINTR)
507 			log_debug("%s: accept4", __func__);
508 		return;
509 	}
510 	if ((msg = calloc(1, sizeof(*msg))) == NULL)
511 		goto fail;
512 
513 	memcpy(&(msg->sm_ss), &ss, len);
514 	msg->sm_slen = len;
515 	msg->sm_aflags = h->flags;
516 	msg->sm_port = h->port;
517 	snmpe_prepare_read(msg, afd);
518 	return;
519 fail:
520 	free(msg);
521 	close(afd);
522 	return;
523 }
524 
525 void
snmpe_prepare_read(struct snmp_message * msg,int fd)526 snmpe_prepare_read(struct snmp_message *msg, int fd)
527 {
528 	msg->sm_sock = fd;
529 	msg->sm_sock_tcp = 1;
530 	event_del(&msg->sm_sockev);
531 	event_set(&msg->sm_sockev, fd, EV_READ,
532 	    snmpe_readcb, msg);
533 	event_add(&msg->sm_sockev, &snmpe_tcp_timeout);
534 }
535 
536 void
snmpe_tryparse(int fd,struct snmp_message * msg)537 snmpe_tryparse(int fd, struct snmp_message *msg)
538 {
539 	struct snmp_stats	*stats = &snmpd_env->sc_stats;
540 
541 	ober_set_application(&msg->sm_ber, smi_application);
542 	ober_set_readbuf(&msg->sm_ber, msg->sm_data, msg->sm_datalen);
543 	msg->sm_req = ober_read_elements(&msg->sm_ber, NULL);
544 	if (msg->sm_req == NULL) {
545 		if (errno == ECANCELED) {
546 			/* short read; try again */
547 			snmpe_prepare_read(msg, fd);
548 			return;
549 		}
550 		goto fail;
551 	}
552 
553 	if (snmpe_parse(msg) == -1) {
554 		if (msg->sm_usmerr && MSG_REPORT(msg)) {
555 			usm_make_report(msg);
556 			return;
557 		} else
558 			goto fail;
559 	}
560 	stats->snmp_inpkts++;
561 
562 	snmpe_dispatchmsg(msg);
563 	return;
564  fail:
565 	snmp_msgfree(msg);
566 	close(fd);
567 }
568 
569 void
snmpe_readcb(int fd,short type,void * arg)570 snmpe_readcb(int fd, short type, void *arg)
571 {
572 	struct snmp_message *msg = arg;
573 	ssize_t len;
574 
575 	if (type == EV_TIMEOUT || msg->sm_datalen >= sizeof(msg->sm_data))
576 		goto fail;
577 
578 	len = read(fd, msg->sm_data + msg->sm_datalen,
579 	    sizeof(msg->sm_data) - msg->sm_datalen);
580 	if (len <= 0) {
581 		if (errno != EAGAIN && errno != EINTR)
582 			goto fail;
583 		snmpe_prepare_read(msg, fd);
584 		return;
585 	}
586 
587 	msg->sm_datalen += (size_t)len;
588 	snmpe_tryparse(fd, msg);
589 	return;
590 
591  fail:
592 	snmp_msgfree(msg);
593 	close(fd);
594 }
595 
596 void
snmpe_writecb(int fd,short type,void * arg)597 snmpe_writecb(int fd, short type, void *arg)
598 {
599 	struct snmp_stats	*stats = &snmpd_env->sc_stats;
600 	struct snmp_message	*msg = arg;
601 	struct snmp_message	*nmsg;
602 	ssize_t			 len;
603 	size_t			 reqlen;
604 	struct ber		*ber = &msg->sm_ber;
605 
606 	if (type == EV_TIMEOUT)
607 		goto fail;
608 
609 	len = ber->br_wend - ber->br_wptr;
610 
611 	log_debug("%s: write fd %d len %zd", __func__, fd, len);
612 
613 	len = write(fd, ber->br_wptr, len);
614 	if (len == -1) {
615 		if (errno == EAGAIN || errno == EINTR)
616 			return;
617 		else
618 			goto fail;
619 	}
620 
621 	ber->br_wptr += len;
622 
623 	if (ber->br_wptr < ber->br_wend) {
624 		event_del(&msg->sm_sockev);
625 		event_set(&msg->sm_sockev, msg->sm_sock, EV_WRITE,
626 		    snmpe_writecb, msg);
627 		event_add(&msg->sm_sockev, &snmpe_tcp_timeout);
628 		return;
629 	}
630 
631 	stats->snmp_outpkts++;
632 
633 	if ((nmsg = calloc(1, sizeof(*nmsg))) == NULL)
634 		goto fail;
635 	memcpy(&(nmsg->sm_ss), &(msg->sm_ss), msg->sm_slen);
636 	nmsg->sm_slen = msg->sm_slen;
637 	nmsg->sm_aflags = msg->sm_aflags;
638 	nmsg->sm_port = msg->sm_port;
639 
640 	/*
641 	 * Reuse the connection.
642 	 * In case we already read data of the next message, copy it over.
643 	 */
644 	reqlen = ober_calc_len(msg->sm_req);
645 	if (msg->sm_datalen > reqlen) {
646 		memcpy(nmsg->sm_data, msg->sm_data + reqlen,
647 		    msg->sm_datalen - reqlen);
648 		nmsg->sm_datalen = msg->sm_datalen - reqlen;
649 		snmp_msgfree(msg);
650 		snmpe_tryparse(fd, nmsg);
651 	} else {
652 		snmp_msgfree(msg);
653 		snmpe_prepare_read(nmsg, fd);
654 	}
655 	return;
656 
657  fail:
658 	close(fd);
659 	snmp_msgfree(msg);
660 }
661 
662 void
snmpe_recvmsg(int fd,short sig,void * arg)663 snmpe_recvmsg(int fd, short sig, void *arg)
664 {
665 	struct address		*h = arg;
666 	struct snmp_stats	*stats = &snmpd_env->sc_stats;
667 	ssize_t			 len;
668 	struct snmp_message	*msg;
669 
670 	if ((msg = calloc(1, sizeof(*msg))) == NULL)
671 		return;
672 
673 	msg->sm_aflags = h->flags;
674 	msg->sm_sock = fd;
675 	msg->sm_slen = sizeof(msg->sm_ss);
676 	msg->sm_port = h->port;
677 	if ((len = recvfromto(fd, msg->sm_data, sizeof(msg->sm_data), 0,
678 	    (struct sockaddr *)&msg->sm_ss, &msg->sm_slen,
679 	    (struct sockaddr *)&msg->sm_local_ss, &msg->sm_local_slen)) < 1) {
680 		free(msg);
681 		return;
682 	}
683 
684 	stats->snmp_inpkts++;
685 	msg->sm_datalen = (size_t)len;
686 
687 	bzero(&msg->sm_ber, sizeof(msg->sm_ber));
688 	ober_set_application(&msg->sm_ber, smi_application);
689 	ober_set_readbuf(&msg->sm_ber, msg->sm_data, msg->sm_datalen);
690 
691 	msg->sm_req = ober_read_elements(&msg->sm_ber, NULL);
692 	if (msg->sm_req == NULL) {
693 		stats->snmp_inasnparseerrs++;
694 		snmp_msgfree(msg);
695 		return;
696 	}
697 
698 #ifdef DEBUG
699 	fprintf(stderr, "recv msg:\n");
700 	smi_debug_elements(msg->sm_req);
701 #endif
702 
703 	if (snmpe_parse(msg) == -1) {
704 		if (msg->sm_usmerr != 0 && MSG_REPORT(msg)) {
705 			usm_make_report(msg);
706 			return;
707 		} else {
708 			snmp_msgfree(msg);
709 			return;
710 		}
711 	}
712 
713 	snmpe_dispatchmsg(msg);
714 }
715 
716 void
snmpe_dispatchmsg(struct snmp_message * msg)717 snmpe_dispatchmsg(struct snmp_message *msg)
718 {
719 	if (msg->sm_pdutype == SNMP_C_TRAP ||
720 	    msg->sm_pdutype == SNMP_C_TRAPV2) {
721 		snmp_msgfree(msg);
722 		return;
723 	}
724 	/* dispatched to subagent */
725 	/* XXX Do proper error handling */
726 	(void) snmpe_parsevarbinds(msg);
727 
728 	return;
729 	/*
730 	 * Leave code here for now so it's easier to switch back in case of
731 	 * issues.
732 	 */
733 	/* respond directly */
734 	msg->sm_pdutype = SNMP_C_RESPONSE;
735 	snmpe_response(msg);
736 }
737 
738 void
snmpe_send(struct snmp_message * msg,enum snmp_pdutype type,int32_t requestid,int32_t error,uint32_t index,struct ber_element * varbindlist)739 snmpe_send(struct snmp_message *msg, enum snmp_pdutype type, int32_t requestid,
740     int32_t error, uint32_t index, struct ber_element *varbindlist)
741 {
742 	msg->sm_request = requestid;
743 	msg->sm_pdutype = type;
744 	msg->sm_error = error;
745 	msg->sm_errorindex = index;
746 	msg->sm_varbindresp = varbindlist;
747 
748 	snmpe_response(msg);
749 }
750 
751 void
snmpe_response(struct snmp_message * msg)752 snmpe_response(struct snmp_message *msg)
753 {
754 	struct snmp_stats	*stats = &snmpd_env->sc_stats;
755 	u_int8_t		*ptr = NULL;
756 	ssize_t			 len;
757 
758 	if (msg->sm_varbindresp == NULL && msg->sm_pduend != NULL)
759 		msg->sm_varbindresp = ober_unlink_elements(msg->sm_pduend);
760 
761 	switch (msg->sm_error) {
762 	case SNMP_ERROR_NONE:
763 		break;
764 	case SNMP_ERROR_TOOBIG:
765 		stats->snmp_intoobigs++;
766 		break;
767 	case SNMP_ERROR_NOSUCHNAME:
768 		stats->snmp_innosuchnames++;
769 		break;
770 	case SNMP_ERROR_BADVALUE:
771 		stats->snmp_inbadvalues++;
772 		break;
773 	case SNMP_ERROR_READONLY:
774 		stats->snmp_inreadonlys++;
775 		break;
776 	case SNMP_ERROR_GENERR:
777 	default:
778 		stats->snmp_ingenerrs++;
779 		break;
780 	}
781 
782 	/* Create new SNMP packet */
783 	if (snmpe_encode(msg) < 0)
784 		goto done;
785 
786 	len = ober_write_elements(&msg->sm_ber, msg->sm_resp);
787 	if (ober_get_writebuf(&msg->sm_ber, (void *)&ptr) == -1)
788 		goto done;
789 
790 	usm_finalize_digest(msg, ptr, len);
791 	if (msg->sm_sock_tcp) {
792 		msg->sm_ber.br_wptr = msg->sm_ber.br_wbuf;
793 		event_del(&msg->sm_sockev);
794 		event_set(&msg->sm_sockev, msg->sm_sock, EV_WRITE,
795 		    snmpe_writecb, msg);
796 		event_add(&msg->sm_sockev, &snmpe_tcp_timeout);
797 		return;
798 	} else {
799 		len = sendtofrom(msg->sm_sock, ptr, len, 0,
800 		    (struct sockaddr *)&msg->sm_ss, msg->sm_slen,
801 		    (struct sockaddr *)&msg->sm_local_ss, msg->sm_local_slen);
802 		if (len != -1)
803 			stats->snmp_outpkts++;
804 	}
805 
806  done:
807 	snmp_msgfree(msg);
808 }
809 
810 void
snmp_msgfree(struct snmp_message * msg)811 snmp_msgfree(struct snmp_message *msg)
812 {
813 	if (msg->sm_transactionid != 0)
814 		RB_REMOVE(snmp_messages, &snmp_messages, msg);
815 	event_del(&msg->sm_sockev);
816 	ober_free(&msg->sm_ber);
817 	if (msg->sm_req != NULL)
818 		ober_free_elements(msg->sm_req);
819 	if (msg->sm_resp != NULL)
820 		ober_free_elements(msg->sm_resp);
821 	free(msg);
822 }
823 
824 int
snmpe_encode(struct snmp_message * msg)825 snmpe_encode(struct snmp_message *msg)
826 {
827 	struct ber_element	*ehdr;
828 	struct ber_element	*pdu, *epdu;
829 
830 	msg->sm_resp = ober_add_sequence(NULL);
831 	if ((ehdr = ober_add_integer(msg->sm_resp, msg->sm_version)) == NULL)
832 		return -1;
833 	if (msg->sm_version == SNMP_V3) {
834 		char	f = MSG_SECLEVEL(msg);
835 
836 		if ((ehdr = ober_printf_elements(ehdr, "{iixi}", msg->sm_msgid,
837 		    msg->sm_max_msg_size, &f, sizeof(f),
838 		    msg->sm_secmodel)) == NULL)
839 			return -1;
840 
841 		/* XXX currently only USM supported */
842 		if ((ehdr = usm_encode(msg, ehdr)) == NULL)
843 			return -1;
844 	} else {
845 		if ((ehdr = ober_add_string(ehdr, msg->sm_community)) == NULL)
846 			return -1;
847 	}
848 
849 	pdu = epdu = ober_add_sequence(NULL);
850 	if (msg->sm_version == SNMP_V3) {
851 		if ((epdu = ober_printf_elements(epdu, "xs{",
852 		    snmpd_env->sc_engineid, snmpd_env->sc_engineid_len,
853 		    msg->sm_ctxname)) == NULL) {
854 			ober_free_elements(pdu);
855 			return -1;
856 		}
857 	}
858 
859 	if (!ober_printf_elements(epdu, "tiii{e}", BER_CLASS_CONTEXT,
860 	    msg->sm_pdutype, msg->sm_request,
861 	    msg->sm_error, msg->sm_errorindex,
862 	    msg->sm_varbindresp)) {
863 		ober_free_elements(pdu);
864 		return -1;
865 	}
866 
867 	if (MSG_HAS_PRIV(msg))
868 		pdu = usm_encrypt(msg, pdu);
869 	ober_link_elements(ehdr, pdu);
870 
871 #ifdef DEBUG
872 	fprintf(stderr, "resp msg:\n");
873 	smi_debug_elements(msg->sm_resp);
874 #endif
875 	return 0;
876 }
877 
878 int
snmp_messagecmp(struct snmp_message * m1,struct snmp_message * m2)879 snmp_messagecmp(struct snmp_message *m1, struct snmp_message *m2)
880 {
881 	return (m1->sm_transactionid < m2->sm_transactionid ? -1 :
882 	    m1->sm_transactionid > m2->sm_transactionid);
883 }
884 
885 RB_GENERATE(snmp_messages, snmp_message, sm_entry, snmp_messagecmp)
886