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