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