1 /* $OpenBSD: snmpe.c,v 1.78 2021/10/21 14:33:13 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_RESPONSE: 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, *engineid; 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 &engineid, &msg->sm_ctxengineid_len, &ctxname, &len, 304 &msg->sm_pdu) != 0) 305 goto parsefail; 306 if (msg->sm_ctxengineid_len > sizeof(msg->sm_ctxengineid)) 307 goto parsefail; 308 memcpy(msg->sm_ctxengineid, engineid, msg->sm_ctxengineid_len); 309 if (len > SNMPD_MAXCONTEXNAMELEN) 310 goto parsefail; 311 memcpy(msg->sm_ctxname, ctxname, len); 312 msg->sm_ctxname[len] = '\0'; 313 break; 314 default: 315 msg->sm_errstr = "unsupported snmp version"; 316 badversion: 317 stats->snmp_inbadversions++; 318 goto fail; 319 } 320 321 if (ober_scanf_elements(msg->sm_pdu, "t{e", &class, &(msg->sm_pdutype), 322 &a) != 0) 323 goto parsefail; 324 325 /* SNMP PDU context */ 326 if (class != BER_CLASS_CONTEXT) 327 goto parsefail; 328 329 switch (msg->sm_pdutype) { 330 case SNMP_C_GETBULKREQ: 331 if (msg->sm_version == SNMP_V1) { 332 stats->snmp_inbadversions++; 333 msg->sm_errstr = 334 "invalid request for protocol version 1"; 335 goto fail; 336 } 337 /* FALLTHROUGH */ 338 339 case SNMP_C_GETREQ: 340 stats->snmp_ingetrequests++; 341 /* FALLTHROUGH */ 342 343 case SNMP_C_GETNEXTREQ: 344 if (msg->sm_pdutype == SNMP_C_GETNEXTREQ) 345 stats->snmp_ingetnexts++; 346 if (!(msg->sm_aflags & ADDRESS_FLAG_READ)) { 347 msg->sm_errstr = "read requests disabled"; 348 goto fail; 349 } 350 if (msg->sm_version != SNMP_V3 && 351 strcmp(env->sc_rdcommunity, msg->sm_community) != 0 && 352 strcmp(env->sc_rwcommunity, msg->sm_community) != 0) { 353 stats->snmp_inbadcommunitynames++; 354 msg->sm_errstr = "wrong read community"; 355 goto fail; 356 } 357 break; 358 359 case SNMP_C_SETREQ: 360 stats->snmp_insetrequests++; 361 if (!(msg->sm_aflags & ADDRESS_FLAG_WRITE)) { 362 msg->sm_errstr = "write requests disabled"; 363 goto fail; 364 } 365 if (msg->sm_version != SNMP_V3 && 366 strcmp(env->sc_rwcommunity, msg->sm_community) != 0) { 367 if (strcmp(env->sc_rdcommunity, msg->sm_community) != 0) 368 stats->snmp_inbadcommunitynames++; 369 else 370 stats->snmp_inbadcommunityuses++; 371 msg->sm_errstr = "wrong write community"; 372 goto fail; 373 } 374 break; 375 376 case SNMP_C_RESPONSE: 377 stats->snmp_ingetresponses++; 378 msg->sm_errstr = "response without request"; 379 goto parsefail; 380 381 case SNMP_C_TRAP: 382 if (msg->sm_version != SNMP_V1) { 383 msg->sm_errstr = "trapv1 request on !SNMPv1 message"; 384 goto parsefail; 385 } 386 case SNMP_C_TRAPV2: 387 if (msg->sm_pdutype == SNMP_C_TRAPV2 && 388 !(msg->sm_version == SNMP_V2 || 389 msg->sm_version == SNMP_V3)) { 390 msg->sm_errstr = "trapv2 request on !SNMPv2C or " 391 "!SNMPv3 message"; 392 goto parsefail; 393 } 394 if (!(msg->sm_aflags & ADDRESS_FLAG_NOTIFY)) { 395 msg->sm_errstr = "notify requests disabled"; 396 goto fail; 397 } 398 if (msg->sm_version == SNMP_V3) { 399 msg->sm_errstr = "SNMPv3 doesn't support traps yet"; 400 goto fail; 401 } 402 if (strcmp(env->sc_trcommunity, msg->sm_community) != 0) { 403 stats->snmp_inbadcommunitynames++; 404 msg->sm_errstr = "wrong trap community"; 405 goto fail; 406 } 407 stats->snmp_intraps++; 408 /* 409 * This should probably go into parsevarbinds, but that's for a 410 * next refactor 411 */ 412 if (traphandler_parse(msg) == -1) 413 goto fail; 414 /* Shortcircuit */ 415 return 0; 416 default: 417 msg->sm_errstr = "invalid context"; 418 goto parsefail; 419 } 420 421 /* SNMP PDU */ 422 if (ober_scanf_elements(a, "iiie{e{}}$", 423 &req, &errval, &erridx, &msg->sm_pduend, 424 &msg->sm_varbind) != 0) { 425 stats->snmp_silentdrops++; 426 msg->sm_errstr = "invalid PDU"; 427 goto fail; 428 } 429 430 msg->sm_request = req; 431 msg->sm_error = errval; 432 msg->sm_errorindex = erridx; 433 434 print_host(ss, msg->sm_host, sizeof(msg->sm_host)); 435 if (msg->sm_version == SNMP_V3) 436 log_debug("%s: %s:%hd: SNMPv3 pdutype %s, flags %#x, " 437 "secmodel %lld, user '%s', ctx-engine %s, ctx-name '%s', " 438 "request %lld", __func__, msg->sm_host, msg->sm_port, 439 snmpe_pdutype2string(msg->sm_pdutype), msg->sm_flags, 440 msg->sm_secmodel, msg->sm_username, 441 tohexstr(msg->sm_ctxengineid, msg->sm_ctxengineid_len), 442 msg->sm_ctxname, msg->sm_request); 443 else 444 log_debug("%s: %s:%hd: SNMPv%d '%s' pdutype %s request %lld", 445 __func__, msg->sm_host, msg->sm_port, msg->sm_version + 1, 446 msg->sm_community, snmpe_pdutype2string(msg->sm_pdutype), 447 msg->sm_request); 448 449 return (0); 450 451 parsefail: 452 stats->snmp_inasnparseerrs++; 453 fail: 454 print_host(ss, msg->sm_host, sizeof(msg->sm_host)); 455 log_debug("%s: %s:%hd: %s", __func__, msg->sm_host, msg->sm_port, 456 msg->sm_errstr); 457 return (-1); 458 } 459 460 int 461 snmpe_parsevarbinds(struct snmp_message *msg) 462 { 463 struct snmp_stats *stats = &snmpd_env->sc_stats; 464 struct ber_element *varbind, *value, *rvarbind = NULL; 465 struct ber_element *pvarbind = NULL, *end; 466 char buf[BUFSIZ]; 467 struct ber_oid o; 468 int i; 469 470 msg->sm_errstr = "invalid varbind element"; 471 472 varbind = msg->sm_varbind; 473 msg->sm_varbindresp = NULL; 474 end = NULL; 475 476 for (i = 1; varbind != NULL && i < SNMPD_MAXVARBIND; 477 varbind = varbind->be_next, i++) { 478 if (ober_scanf_elements(varbind, "{oeS$}", &o, &value) == -1) { 479 stats->snmp_inasnparseerrs++; 480 msg->sm_errstr = "invalid varbind"; 481 goto varfail; 482 } 483 if (o.bo_n < BER_MIN_OID_LEN || o.bo_n > BER_MAX_OID_LEN) 484 goto varfail; 485 486 log_debug("%s: %s:%hd: oid %s", __func__, msg->sm_host, 487 msg->sm_port, smi_oid2string(&o, buf, sizeof(buf), 0)); 488 489 /* 490 * XXX intotalreqvars should only be incremented after all are 491 * succeeded 492 */ 493 switch (msg->sm_pdutype) { 494 case SNMP_C_GETNEXTREQ: 495 if ((rvarbind = ober_add_sequence(NULL)) == NULL) 496 goto varfail; 497 if (mps_getnextreq(msg, rvarbind, &o) != 0) { 498 msg->sm_error = SNMP_ERROR_NOSUCHNAME; 499 ober_free_elements(rvarbind); 500 goto varfail; 501 } 502 stats->snmp_intotalreqvars++; 503 break; 504 case SNMP_C_GETREQ: 505 if ((rvarbind = ober_add_sequence(NULL)) == NULL) 506 goto varfail; 507 if (mps_getreq(msg, rvarbind, &o, 508 msg->sm_version) != 0) { 509 msg->sm_error = SNMP_ERROR_NOSUCHNAME; 510 ober_free_elements(rvarbind); 511 goto varfail; 512 } 513 stats->snmp_intotalreqvars++; 514 break; 515 case SNMP_C_SETREQ: 516 /* 517 * XXX A set varbind should only be committed if 518 * all variables are staged 519 */ 520 if (mps_setreq(msg, value, &o) == 0) { 521 stats->snmp_intotalsetvars++; 522 break; 523 } 524 msg->sm_error = SNMP_ERROR_READONLY; 525 goto varfail; 526 case SNMP_C_GETBULKREQ: 527 rvarbind = NULL; 528 if (mps_getbulkreq(msg, &rvarbind, &end, &o, 529 (i <= msg->sm_nonrepeaters) 530 ? 1 : msg->sm_maxrepetitions) != 0) { 531 msg->sm_error = SNMP_ERROR_NOSUCHNAME; 532 goto varfail; 533 } 534 /* 535 * XXX This should be the amount of returned 536 * vars 537 */ 538 stats->snmp_intotalreqvars++; 539 break; 540 541 default: 542 goto varfail; 543 } 544 if (rvarbind == NULL) 545 break; 546 if (pvarbind == NULL) 547 msg->sm_varbindresp = rvarbind; 548 else 549 ober_link_elements(pvarbind, rvarbind); 550 pvarbind = end == NULL ? rvarbind : end; 551 } 552 553 msg->sm_errstr = "none"; 554 msg->sm_error = 0; 555 msg->sm_errorindex = 0; 556 557 return 0; 558 varfail: 559 log_debug("%s: %s:%hd: %s, error index %d", __func__, 560 msg->sm_host, msg->sm_port, msg->sm_errstr, i); 561 if (msg->sm_error == 0) 562 msg->sm_error = SNMP_ERROR_GENERR; 563 msg->sm_errorindex = i; 564 return -1; 565 } 566 567 void 568 snmpe_acceptcb(int fd, short type, void *arg) 569 { 570 struct address *h = arg; 571 struct sockaddr_storage ss; 572 socklen_t len = sizeof(ss); 573 struct snmp_message *msg; 574 int afd; 575 576 event_add(&h->ev, NULL); 577 if ((type & EV_TIMEOUT)) 578 return; 579 580 if ((afd = accept4(fd, (struct sockaddr *)&ss, &len, 581 SOCK_NONBLOCK|SOCK_CLOEXEC)) < 0) { 582 /* Pause accept if we are out of file descriptors */ 583 if (errno == ENFILE || errno == EMFILE) { 584 struct timeval evtpause = { 1, 0 }; 585 586 event_del(&h->ev); 587 evtimer_add(&h->evt, &evtpause); 588 } else if (errno != EAGAIN && errno != EINTR) 589 log_debug("%s: accept4", __func__); 590 return; 591 } 592 if ((msg = calloc(1, sizeof(*msg))) == NULL) 593 goto fail; 594 595 memcpy(&(msg->sm_ss), &ss, len); 596 msg->sm_slen = len; 597 msg->sm_aflags = h->flags; 598 msg->sm_port = h->port; 599 snmpe_prepare_read(msg, afd); 600 return; 601 fail: 602 free(msg); 603 close(afd); 604 return; 605 } 606 607 void 608 snmpe_prepare_read(struct snmp_message *msg, int fd) 609 { 610 msg->sm_sock = fd; 611 msg->sm_sock_tcp = 1; 612 event_del(&msg->sm_sockev); 613 event_set(&msg->sm_sockev, fd, EV_READ, 614 snmpe_readcb, msg); 615 event_add(&msg->sm_sockev, &snmpe_tcp_timeout); 616 } 617 618 void 619 snmpe_tryparse(int fd, struct snmp_message *msg) 620 { 621 struct snmp_stats *stats = &snmpd_env->sc_stats; 622 623 ober_set_application(&msg->sm_ber, smi_application); 624 ober_set_readbuf(&msg->sm_ber, msg->sm_data, msg->sm_datalen); 625 msg->sm_req = ober_read_elements(&msg->sm_ber, NULL); 626 if (msg->sm_req == NULL) { 627 if (errno == ECANCELED) { 628 /* short read; try again */ 629 snmpe_prepare_read(msg, fd); 630 return; 631 } 632 goto fail; 633 } 634 635 if (snmpe_parse(msg) == -1) { 636 if (msg->sm_usmerr && MSG_REPORT(msg)) { 637 usm_make_report(msg); 638 snmpe_response(msg); 639 return; 640 } else 641 goto fail; 642 } 643 stats->snmp_inpkts++; 644 645 snmpe_dispatchmsg(msg); 646 return; 647 fail: 648 snmp_msgfree(msg); 649 close(fd); 650 } 651 652 void 653 snmpe_readcb(int fd, short type, void *arg) 654 { 655 struct snmp_message *msg = arg; 656 ssize_t len; 657 658 if (type == EV_TIMEOUT || msg->sm_datalen >= sizeof(msg->sm_data)) 659 goto fail; 660 661 len = read(fd, msg->sm_data + msg->sm_datalen, 662 sizeof(msg->sm_data) - msg->sm_datalen); 663 if (len <= 0) { 664 if (errno != EAGAIN && errno != EINTR) 665 goto fail; 666 snmpe_prepare_read(msg, fd); 667 return; 668 } 669 670 msg->sm_datalen += (size_t)len; 671 snmpe_tryparse(fd, msg); 672 return; 673 674 fail: 675 snmp_msgfree(msg); 676 close(fd); 677 } 678 679 void 680 snmpe_writecb(int fd, short type, void *arg) 681 { 682 struct snmp_stats *stats = &snmpd_env->sc_stats; 683 struct snmp_message *msg = arg; 684 struct snmp_message *nmsg; 685 ssize_t len; 686 size_t reqlen; 687 struct ber *ber = &msg->sm_ber; 688 689 if (type == EV_TIMEOUT) 690 goto fail; 691 692 len = ber->br_wend - ber->br_wbuf; 693 ber->br_wptr = ber->br_wbuf; 694 695 log_debug("%s: write fd %d len %zd", __func__, fd, len); 696 697 len = write(fd, ber->br_wptr, len); 698 if (len == -1) { 699 if (errno == EAGAIN || errno == EINTR) 700 return; 701 else 702 goto fail; 703 } 704 705 ber->br_wptr += len; 706 707 if (ber->br_wptr < ber->br_wend) { 708 event_del(&msg->sm_sockev); 709 event_set(&msg->sm_sockev, msg->sm_sock, EV_WRITE, 710 snmpe_writecb, msg); 711 event_add(&msg->sm_sockev, &snmpe_tcp_timeout); 712 return; 713 } 714 715 stats->snmp_outpkts++; 716 717 if ((nmsg = calloc(1, sizeof(*nmsg))) == NULL) 718 goto fail; 719 memcpy(&(nmsg->sm_ss), &(msg->sm_ss), msg->sm_slen); 720 nmsg->sm_slen = msg->sm_slen; 721 nmsg->sm_aflags = msg->sm_aflags; 722 nmsg->sm_port = msg->sm_port; 723 724 /* 725 * Reuse the connection. 726 * In case we already read data of the next message, copy it over. 727 */ 728 reqlen = ober_calc_len(msg->sm_req); 729 if (msg->sm_datalen > reqlen) { 730 memcpy(nmsg->sm_data, msg->sm_data + reqlen, 731 msg->sm_datalen - reqlen); 732 nmsg->sm_datalen = msg->sm_datalen - reqlen; 733 snmp_msgfree(msg); 734 snmpe_prepare_read(nmsg, fd); 735 snmpe_tryparse(fd, nmsg); 736 } else { 737 snmp_msgfree(msg); 738 snmpe_prepare_read(nmsg, fd); 739 } 740 return; 741 742 fail: 743 close(fd); 744 snmp_msgfree(msg); 745 } 746 747 void 748 snmpe_recvmsg(int fd, short sig, void *arg) 749 { 750 struct address *h = arg; 751 struct snmp_stats *stats = &snmpd_env->sc_stats; 752 ssize_t len; 753 struct snmp_message *msg; 754 755 if ((msg = calloc(1, sizeof(*msg))) == NULL) 756 return; 757 758 msg->sm_aflags = h->flags; 759 msg->sm_sock = fd; 760 msg->sm_slen = sizeof(msg->sm_ss); 761 msg->sm_port = h->port; 762 if ((len = recvfromto(fd, msg->sm_data, sizeof(msg->sm_data), 0, 763 (struct sockaddr *)&msg->sm_ss, &msg->sm_slen, 764 (struct sockaddr *)&msg->sm_local_ss, &msg->sm_local_slen)) < 1) { 765 free(msg); 766 return; 767 } 768 769 stats->snmp_inpkts++; 770 msg->sm_datalen = (size_t)len; 771 772 bzero(&msg->sm_ber, sizeof(msg->sm_ber)); 773 ober_set_application(&msg->sm_ber, smi_application); 774 ober_set_readbuf(&msg->sm_ber, msg->sm_data, msg->sm_datalen); 775 776 msg->sm_req = ober_read_elements(&msg->sm_ber, NULL); 777 if (msg->sm_req == NULL) { 778 stats->snmp_inasnparseerrs++; 779 snmp_msgfree(msg); 780 return; 781 } 782 783 #ifdef DEBUG 784 fprintf(stderr, "recv msg:\n"); 785 smi_debug_elements(msg->sm_req); 786 #endif 787 788 if (snmpe_parse(msg) == -1) { 789 if (msg->sm_usmerr != 0 && MSG_REPORT(msg)) { 790 usm_make_report(msg); 791 snmpe_response(msg); 792 return; 793 } else { 794 snmp_msgfree(msg); 795 return; 796 } 797 } 798 799 snmpe_dispatchmsg(msg); 800 } 801 802 void 803 snmpe_dispatchmsg(struct snmp_message *msg) 804 { 805 if (msg->sm_pdutype == SNMP_C_TRAP || 806 msg->sm_pdutype == SNMP_C_TRAPV2) { 807 snmp_msgfree(msg); 808 return; 809 } 810 /* dispatched to subagent */ 811 /* XXX Do proper error handling */ 812 (void) snmpe_parsevarbinds(msg); 813 814 /* respond directly */ 815 msg->sm_pdutype = SNMP_C_RESPONSE; 816 snmpe_response(msg); 817 } 818 819 void 820 snmpe_response(struct snmp_message *msg) 821 { 822 struct snmp_stats *stats = &snmpd_env->sc_stats; 823 u_int8_t *ptr = NULL; 824 ssize_t len; 825 826 if (msg->sm_varbindresp == NULL && msg->sm_pduend != NULL) 827 msg->sm_varbindresp = ober_unlink_elements(msg->sm_pduend); 828 829 switch (msg->sm_error) { 830 case SNMP_ERROR_NONE: 831 break; 832 case SNMP_ERROR_TOOBIG: 833 stats->snmp_intoobigs++; 834 break; 835 case SNMP_ERROR_NOSUCHNAME: 836 stats->snmp_innosuchnames++; 837 break; 838 case SNMP_ERROR_BADVALUE: 839 stats->snmp_inbadvalues++; 840 break; 841 case SNMP_ERROR_READONLY: 842 stats->snmp_inreadonlys++; 843 break; 844 case SNMP_ERROR_GENERR: 845 default: 846 stats->snmp_ingenerrs++; 847 break; 848 } 849 850 /* Create new SNMP packet */ 851 if (snmpe_encode(msg) < 0) 852 goto done; 853 854 len = ober_write_elements(&msg->sm_ber, msg->sm_resp); 855 if (ober_get_writebuf(&msg->sm_ber, (void *)&ptr) == -1) 856 goto done; 857 858 usm_finalize_digest(msg, ptr, len); 859 if (msg->sm_sock_tcp) { 860 event_del(&msg->sm_sockev); 861 event_set(&msg->sm_sockev, msg->sm_sock, EV_WRITE, 862 snmpe_writecb, msg); 863 event_add(&msg->sm_sockev, &snmpe_tcp_timeout); 864 return; 865 } else { 866 len = sendtofrom(msg->sm_sock, ptr, len, 0, 867 (struct sockaddr *)&msg->sm_ss, msg->sm_slen, 868 (struct sockaddr *)&msg->sm_local_ss, msg->sm_local_slen); 869 if (len != -1) 870 stats->snmp_outpkts++; 871 } 872 873 done: 874 snmp_msgfree(msg); 875 } 876 877 void 878 snmp_msgfree(struct snmp_message *msg) 879 { 880 if (msg->sm_transactionid != 0) 881 RB_REMOVE(snmp_messages, &snmp_messages, msg); 882 event_del(&msg->sm_sockev); 883 ober_free(&msg->sm_ber); 884 if (msg->sm_req != NULL) 885 ober_free_elements(msg->sm_req); 886 if (msg->sm_resp != NULL) 887 ober_free_elements(msg->sm_resp); 888 free(msg); 889 } 890 891 int 892 snmpe_encode(struct snmp_message *msg) 893 { 894 struct ber_element *ehdr; 895 struct ber_element *pdu, *epdu; 896 897 msg->sm_resp = ober_add_sequence(NULL); 898 if ((ehdr = ober_add_integer(msg->sm_resp, msg->sm_version)) == NULL) 899 return -1; 900 if (msg->sm_version == SNMP_V3) { 901 char f = MSG_SECLEVEL(msg); 902 903 if ((ehdr = ober_printf_elements(ehdr, "{iixi}", msg->sm_msgid, 904 msg->sm_max_msg_size, &f, sizeof(f), 905 msg->sm_secmodel)) == NULL) 906 return -1; 907 908 /* XXX currently only USM supported */ 909 if ((ehdr = usm_encode(msg, ehdr)) == NULL) 910 return -1; 911 } else { 912 if ((ehdr = ober_add_string(ehdr, msg->sm_community)) == NULL) 913 return -1; 914 } 915 916 pdu = epdu = ober_add_sequence(NULL); 917 if (msg->sm_version == SNMP_V3) { 918 if ((epdu = ober_printf_elements(epdu, "xs{", 919 snmpd_env->sc_engineid, snmpd_env->sc_engineid_len, 920 msg->sm_ctxname)) == NULL) { 921 ober_free_elements(pdu); 922 return -1; 923 } 924 } 925 926 if (!ober_printf_elements(epdu, "tiii{e}", BER_CLASS_CONTEXT, 927 msg->sm_pdutype, msg->sm_request, 928 msg->sm_error, msg->sm_errorindex, 929 msg->sm_varbindresp)) { 930 ober_free_elements(pdu); 931 return -1; 932 } 933 934 if (MSG_HAS_PRIV(msg)) 935 pdu = usm_encrypt(msg, pdu); 936 ober_link_elements(ehdr, pdu); 937 938 #ifdef DEBUG 939 fprintf(stderr, "resp msg:\n"); 940 smi_debug_elements(msg->sm_resp); 941 #endif 942 return 0; 943 } 944 945 int 946 snmp_messagecmp(struct snmp_message *m1, struct snmp_message *m2) 947 { 948 return (m1->sm_transactionid < m2->sm_transactionid ? -1 : 949 m1->sm_transactionid > m2->sm_transactionid); 950 } 951 952 RB_GENERATE(snmp_messages, snmp_message, sm_entry, snmp_messagecmp) 953