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