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