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