1 /* $OpenBSD: snmpe.c,v 1.42 2016/08/16 18:41:57 tedu Exp $ */ 2 3 /* 4 * Copyright (c) 2007, 2008, 2012 Reyk Floeter <reyk@openbsd.org> 5 * 6 * Permission to use, copy, modify, and distribute this software for any 7 * purpose with or without fee is hereby granted, provided that the above 8 * copyright notice and this permission notice appear in all copies. 9 * 10 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 11 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 12 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 13 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 14 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 15 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 16 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 17 */ 18 19 #include <sys/queue.h> 20 #include <sys/types.h> 21 #include <sys/stat.h> 22 #include <sys/socket.h> 23 #include <sys/un.h> 24 #include <sys/tree.h> 25 26 #include <net/if.h> 27 #include <netinet/in.h> 28 #include <arpa/inet.h> 29 30 #include <stdlib.h> 31 #include <stdio.h> 32 #include <errno.h> 33 #include <event.h> 34 #include <fcntl.h> 35 #include <string.h> 36 #include <unistd.h> 37 #include <pwd.h> 38 39 #include "snmpd.h" 40 #include "mib.h" 41 42 void snmpe_init(struct privsep *, struct privsep_proc *, void *); 43 int snmpe_parse(struct snmp_message *); 44 int snmpe_parsevarbinds(struct snmp_message *); 45 void snmpe_response(int, struct snmp_message *); 46 unsigned long 47 snmpe_application(struct ber_element *); 48 void snmpe_sig_handler(int sig, short, void *); 49 int snmpe_dispatch_parent(int, struct privsep_proc *, struct imsg *); 50 int snmpe_bind(struct address *); 51 void snmpe_recvmsg(int fd, short, void *); 52 int snmpe_encode(struct snmp_message *); 53 void snmp_msgfree(struct snmp_message *); 54 55 struct snmpd *env = NULL; 56 57 struct imsgev *iev_parent; 58 59 static struct privsep_proc procs[] = { 60 { "parent", PROC_PARENT, snmpe_dispatch_parent } 61 }; 62 63 pid_t 64 snmpe(struct privsep *ps, struct privsep_proc *p) 65 { 66 #ifdef DEBUG 67 char buf[BUFSIZ]; 68 struct oid *oid; 69 #endif 70 71 env = ps->ps_env; 72 73 #ifdef DEBUG 74 for (oid = NULL; (oid = smi_foreach(oid, 0)) != NULL;) { 75 smi_oid2string(&oid->o_id, buf, sizeof(buf), 0); 76 log_debug("oid %s", buf); 77 } 78 #endif 79 80 /* bind SNMP UDP socket */ 81 if ((env->sc_sock = snmpe_bind(&env->sc_address)) == -1) 82 fatalx("snmpe: failed to bind SNMP UDP socket"); 83 84 return (proc_run(ps, p, procs, nitems(procs), snmpe_init, NULL)); 85 } 86 87 /* ARGSUSED */ 88 void 89 snmpe_init(struct privsep *ps, struct privsep_proc *p, void *arg) 90 { 91 kr_init(); 92 trap_init(); 93 timer_init(); 94 usm_generate_keys(); 95 96 /* listen for incoming SNMP UDP messages */ 97 event_set(&env->sc_ev, env->sc_sock, EV_READ|EV_PERSIST, 98 snmpe_recvmsg, env); 99 event_add(&env->sc_ev, NULL); 100 } 101 102 void 103 snmpe_shutdown(void) 104 { 105 kr_shutdown(); 106 } 107 108 int 109 snmpe_dispatch_parent(int fd, struct privsep_proc *p, struct imsg *imsg) 110 { 111 switch (imsg->hdr.type) { 112 default: 113 break; 114 } 115 116 return (-1); 117 } 118 119 int 120 snmpe_bind(struct address *addr) 121 { 122 char buf[512]; 123 int s; 124 125 if ((s = snmpd_socket_af(&addr->ss, htons(addr->port))) == -1) 126 return (-1); 127 128 /* 129 * Socket options 130 */ 131 if (fcntl(s, F_SETFL, O_NONBLOCK) == -1) 132 goto bad; 133 134 if (bind(s, (struct sockaddr *)&addr->ss, addr->ss.ss_len) == -1) 135 goto bad; 136 137 if (print_host(&addr->ss, buf, sizeof(buf)) == NULL) 138 goto bad; 139 140 log_info("snmpe_bind: binding to address %s:%d", buf, addr->port); 141 142 return (s); 143 144 bad: 145 close(s); 146 return (-1); 147 } 148 149 int 150 snmpe_parse(struct snmp_message *msg) 151 { 152 struct snmp_stats *stats = &env->sc_stats; 153 struct ber_element *a; 154 long long ver, req; 155 long long errval, erridx; 156 unsigned long type; 157 u_int class; 158 char *comn; 159 char *flagstr, *ctxname; 160 size_t len; 161 struct sockaddr_storage *ss = &msg->sm_ss; 162 struct ber_element *root = msg->sm_req; 163 164 msg->sm_errstr = "invalid message"; 165 166 if (ber_scanf_elements(root, "{ie", &ver, &a) != 0) 167 goto parsefail; 168 169 /* SNMP version and community */ 170 msg->sm_version = ver; 171 switch (msg->sm_version) { 172 case SNMP_V1: 173 case SNMP_V2: 174 if (env->sc_min_seclevel != 0) 175 goto badversion; 176 if (ber_scanf_elements(a, "se", &comn, &msg->sm_pdu) != 0) 177 goto parsefail; 178 if (strlcpy(msg->sm_community, comn, 179 sizeof(msg->sm_community)) >= sizeof(msg->sm_community)) { 180 stats->snmp_inbadcommunitynames++; 181 msg->sm_errstr = "community name too long"; 182 goto fail; 183 } 184 break; 185 case SNMP_V3: 186 if (ber_scanf_elements(a, "{iisi}e", 187 &msg->sm_msgid, &msg->sm_max_msg_size, &flagstr, 188 &msg->sm_secmodel, &a) != 0) 189 goto parsefail; 190 191 msg->sm_flags = *flagstr; 192 if (MSG_SECLEVEL(msg) < env->sc_min_seclevel || 193 msg->sm_secmodel != SNMP_SEC_USM) { 194 /* XXX currently only USM supported */ 195 msg->sm_errstr = "unsupported security model"; 196 stats->snmp_usmbadseclevel++; 197 msg->sm_usmerr = OIDVAL_usmErrSecLevel; 198 goto parsefail; 199 } 200 201 if ((a = usm_decode(msg, a, &msg->sm_errstr)) == NULL) 202 goto parsefail; 203 204 if (ber_scanf_elements(a, "{xxe", 205 &msg->sm_ctxengineid, &msg->sm_ctxengineid_len, 206 &ctxname, &len, &msg->sm_pdu) != 0) 207 goto parsefail; 208 if (len > SNMPD_MAXCONTEXNAMELEN) 209 goto parsefail; 210 memcpy(msg->sm_ctxname, ctxname, len); 211 msg->sm_ctxname[len] = '\0'; 212 break; 213 default: 214 badversion: 215 stats->snmp_inbadversions++; 216 msg->sm_errstr = "bad snmp version"; 217 goto fail; 218 } 219 220 if (ber_scanf_elements(msg->sm_pdu, "t{e", &class, &type, &a) != 0) 221 goto parsefail; 222 223 /* SNMP PDU context */ 224 if (class != BER_CLASS_CONTEXT) 225 goto parsefail; 226 227 switch (type) { 228 case SNMP_C_GETBULKREQ: 229 if (msg->sm_version == SNMP_V1) { 230 stats->snmp_inbadversions++; 231 msg->sm_errstr = 232 "invalid request for protocol version 1"; 233 goto fail; 234 } 235 /* FALLTHROUGH */ 236 237 case SNMP_C_GETREQ: 238 stats->snmp_ingetrequests++; 239 /* FALLTHROUGH */ 240 241 case SNMP_C_GETNEXTREQ: 242 if (type == SNMP_C_GETNEXTREQ) 243 stats->snmp_ingetnexts++; 244 if (msg->sm_version != SNMP_V3 && 245 strcmp(env->sc_rdcommunity, msg->sm_community) != 0 && 246 strcmp(env->sc_rwcommunity, msg->sm_community) != 0) { 247 stats->snmp_inbadcommunitynames++; 248 msg->sm_errstr = "wrong read community"; 249 goto fail; 250 } 251 msg->sm_context = type; 252 break; 253 254 case SNMP_C_SETREQ: 255 stats->snmp_insetrequests++; 256 if (msg->sm_version != SNMP_V3 && 257 strcmp(env->sc_rwcommunity, msg->sm_community) != 0) { 258 if (strcmp(env->sc_rdcommunity, msg->sm_community) != 0) 259 stats->snmp_inbadcommunitynames++; 260 else 261 stats->snmp_inbadcommunityuses++; 262 msg->sm_errstr = "wrong write community"; 263 goto fail; 264 } 265 msg->sm_context = type; 266 break; 267 268 case SNMP_C_GETRESP: 269 stats->snmp_ingetresponses++; 270 msg->sm_errstr = "response without request"; 271 goto parsefail; 272 273 case SNMP_C_TRAP: 274 case SNMP_C_TRAPV2: 275 if (msg->sm_version != SNMP_V3 && 276 strcmp(env->sc_trcommunity, msg->sm_community) != 0) { 277 stats->snmp_inbadcommunitynames++; 278 msg->sm_errstr = "wrong trap community"; 279 goto fail; 280 } 281 stats->snmp_intraps++; 282 msg->sm_errstr = "received trap"; 283 goto fail; 284 285 default: 286 msg->sm_errstr = "invalid context"; 287 goto parsefail; 288 } 289 290 /* SNMP PDU */ 291 if (ber_scanf_elements(a, "iiie{et", 292 &req, &errval, &erridx, &msg->sm_pduend, 293 &msg->sm_varbind, &class, &type) != 0) { 294 stats->snmp_silentdrops++; 295 msg->sm_errstr = "invalid PDU"; 296 goto fail; 297 } 298 if (class != BER_CLASS_UNIVERSAL || type != BER_TYPE_SEQUENCE) { 299 stats->snmp_silentdrops++; 300 msg->sm_errstr = "invalid varbind"; 301 goto fail; 302 } 303 304 msg->sm_request = req; 305 msg->sm_error = errval; 306 msg->sm_errorindex = erridx; 307 308 print_host(ss, msg->sm_host, sizeof(msg->sm_host)); 309 if (msg->sm_version == SNMP_V3) 310 log_debug("%s: %s: SNMPv3 context %d, flags %#x, " 311 "secmodel %lld, user '%s', ctx-engine %s, ctx-name '%s', " 312 "request %lld", __func__, msg->sm_host, msg->sm_context, 313 msg->sm_flags, msg->sm_secmodel, msg->sm_username, 314 tohexstr(msg->sm_ctxengineid, msg->sm_ctxengineid_len), 315 msg->sm_ctxname, msg->sm_request); 316 else 317 log_debug("%s: %s: SNMPv%d '%s' context %d request %lld", 318 __func__, msg->sm_host, msg->sm_version + 1, 319 msg->sm_community, msg->sm_context, msg->sm_request); 320 321 return (0); 322 323 parsefail: 324 stats->snmp_inasnparseerrs++; 325 fail: 326 print_host(ss, msg->sm_host, sizeof(msg->sm_host)); 327 log_debug("%s: %s: %s", __func__, msg->sm_host, msg->sm_errstr); 328 return (-1); 329 } 330 331 int 332 snmpe_parsevarbinds(struct snmp_message *msg) 333 { 334 struct snmp_stats *stats = &env->sc_stats; 335 char buf[BUFSIZ]; 336 struct ber_oid o; 337 int ret = 0; 338 339 msg->sm_errstr = "invalid varbind element"; 340 341 if (msg->sm_i == 0) { 342 msg->sm_i = 1; 343 msg->sm_a = msg->sm_varbind; 344 msg->sm_last = NULL; 345 } 346 347 for (; msg->sm_a != NULL && msg->sm_i < SNMPD_MAXVARBIND; 348 msg->sm_a = msg->sm_next, msg->sm_i++) { 349 msg->sm_next = msg->sm_a->be_next; 350 351 if (msg->sm_a->be_class != BER_CLASS_UNIVERSAL || 352 msg->sm_a->be_type != BER_TYPE_SEQUENCE) 353 continue; 354 if ((msg->sm_b = msg->sm_a->be_sub) == NULL) 355 continue; 356 357 for (msg->sm_state = 0; msg->sm_state < 2 && msg->sm_b != NULL; 358 msg->sm_b = msg->sm_b->be_next) { 359 switch (msg->sm_state++) { 360 case 0: 361 if (ber_get_oid(msg->sm_b, &o) != 0) 362 goto varfail; 363 if (o.bo_n < BER_MIN_OID_LEN || 364 o.bo_n > BER_MAX_OID_LEN) 365 goto varfail; 366 if (msg->sm_context == SNMP_C_SETREQ) 367 stats->snmp_intotalsetvars++; 368 else 369 stats->snmp_intotalreqvars++; 370 log_debug("%s: %s: oid %s", 371 __func__, msg->sm_host, 372 smi_oid2string(&o, buf, sizeof(buf), 0)); 373 break; 374 case 1: 375 msg->sm_c = NULL; 376 msg->sm_end = NULL; 377 378 switch (msg->sm_context) { 379 380 case SNMP_C_GETNEXTREQ: 381 msg->sm_c = ber_add_sequence(NULL); 382 ret = mps_getnextreq(msg, msg->sm_c, 383 &o); 384 if (ret == 0 || ret == 1) 385 break; 386 ber_free_elements(msg->sm_c); 387 msg->sm_error = SNMP_ERROR_NOSUCHNAME; 388 goto varfail; 389 390 case SNMP_C_GETREQ: 391 msg->sm_c = ber_add_sequence(NULL); 392 ret = mps_getreq(msg, msg->sm_c, &o, 393 msg->sm_version); 394 if (ret == 0 || ret == 1) 395 break; 396 msg->sm_error = SNMP_ERROR_NOSUCHNAME; 397 ber_free_elements(msg->sm_c); 398 goto varfail; 399 400 case SNMP_C_SETREQ: 401 if (env->sc_readonly == 0) { 402 ret = mps_setreq(msg, 403 msg->sm_b, &o); 404 if (ret == 0) 405 break; 406 } 407 msg->sm_error = SNMP_ERROR_READONLY; 408 goto varfail; 409 410 case SNMP_C_GETBULKREQ: 411 ret = mps_getbulkreq(msg, &msg->sm_c, 412 &msg->sm_end, &o, 413 msg->sm_maxrepetitions); 414 if (ret == 0 || ret == 1) 415 break; 416 msg->sm_error = SNMP_ERROR_NOSUCHNAME; 417 goto varfail; 418 419 default: 420 goto varfail; 421 } 422 if (msg->sm_c == NULL) 423 break; 424 if (msg->sm_end == NULL) 425 msg->sm_end = msg->sm_c; 426 if (msg->sm_last == NULL) 427 msg->sm_varbindresp = msg->sm_c; 428 else 429 ber_link_elements(msg->sm_last, msg->sm_c); 430 msg->sm_last = msg->sm_end; 431 break; 432 } 433 } 434 if (msg->sm_state < 2) { 435 log_debug("%s: state %d", __func__, msg->sm_state); 436 goto varfail; 437 } 438 } 439 440 msg->sm_errstr = "none"; 441 442 return (ret); 443 varfail: 444 log_debug("%s: %s: %s, error index %d", __func__, 445 msg->sm_host, msg->sm_errstr, msg->sm_i); 446 if (msg->sm_error == 0) 447 msg->sm_error = SNMP_ERROR_GENERR; 448 msg->sm_errorindex = msg->sm_i; 449 return (-1); 450 } 451 452 void 453 snmpe_recvmsg(int fd, short sig, void *arg) 454 { 455 struct snmp_stats *stats = &env->sc_stats; 456 ssize_t len; 457 struct snmp_message *msg; 458 459 if ((msg = calloc(1, sizeof(*msg))) == NULL) 460 return; 461 462 msg->sm_slen = sizeof(msg->sm_ss); 463 if ((len = recvfrom(fd, msg->sm_data, sizeof(msg->sm_data), 0, 464 (struct sockaddr *)&msg->sm_ss, &msg->sm_slen)) < 1) { 465 free(msg); 466 return; 467 } 468 469 stats->snmp_inpkts++; 470 msg->sm_datalen = (size_t)len; 471 472 bzero(&msg->sm_ber, sizeof(msg->sm_ber)); 473 msg->sm_ber.fd = -1; 474 ber_set_application(&msg->sm_ber, smi_application); 475 ber_set_readbuf(&msg->sm_ber, msg->sm_data, msg->sm_datalen); 476 477 msg->sm_req = ber_read_elements(&msg->sm_ber, NULL); 478 if (msg->sm_req == NULL) { 479 stats->snmp_inasnparseerrs++; 480 snmp_msgfree(msg); 481 return; 482 } 483 484 #ifdef DEBUG 485 fprintf(stderr, "recv msg:\n"); 486 smi_debug_elements(msg->sm_req); 487 #endif 488 489 if (snmpe_parse(msg) == -1) { 490 if (msg->sm_usmerr != 0 && MSG_REPORT(msg)) { 491 usm_make_report(msg); 492 snmpe_response(fd, msg); 493 return; 494 } else { 495 snmp_msgfree(msg); 496 return; 497 } 498 } 499 500 snmpe_dispatchmsg(msg); 501 } 502 503 void 504 snmpe_dispatchmsg(struct snmp_message *msg) 505 { 506 if (snmpe_parsevarbinds(msg) == 1) 507 return; 508 509 /* not dispatched to subagent; respond directly */ 510 msg->sm_context = SNMP_C_GETRESP; 511 snmpe_response(env->sc_sock, msg); 512 } 513 514 void 515 snmpe_response(int fd, struct snmp_message *msg) 516 { 517 struct snmp_stats *stats = &env->sc_stats; 518 u_int8_t *ptr = NULL; 519 ssize_t len; 520 521 if (msg->sm_varbindresp == NULL && msg->sm_pduend != NULL) 522 msg->sm_varbindresp = ber_unlink_elements(msg->sm_pduend); 523 524 switch (msg->sm_error) { 525 case SNMP_ERROR_TOOBIG: 526 stats->snmp_intoobigs++; 527 break; 528 case SNMP_ERROR_NOSUCHNAME: 529 stats->snmp_innosuchnames++; 530 break; 531 case SNMP_ERROR_BADVALUE: 532 stats->snmp_inbadvalues++; 533 break; 534 case SNMP_ERROR_READONLY: 535 stats->snmp_inreadonlys++; 536 break; 537 case SNMP_ERROR_GENERR: 538 default: 539 stats->snmp_ingenerrs++; 540 break; 541 } 542 543 /* Create new SNMP packet */ 544 if (snmpe_encode(msg) < 0) 545 goto done; 546 547 len = ber_write_elements(&msg->sm_ber, msg->sm_resp); 548 if (ber_get_writebuf(&msg->sm_ber, (void *)&ptr) == -1) 549 goto done; 550 551 usm_finalize_digest(msg, ptr, len); 552 len = sendto(fd, ptr, len, 0, (struct sockaddr *)&msg->sm_ss, 553 msg->sm_slen); 554 if (len != -1) 555 stats->snmp_outpkts++; 556 557 done: 558 snmp_msgfree(msg); 559 } 560 561 void 562 snmp_msgfree(struct snmp_message *msg) 563 { 564 ber_free(&msg->sm_ber); 565 if (msg->sm_req != NULL) 566 ber_free_elements(msg->sm_req); 567 if (msg->sm_resp != NULL) 568 ber_free_elements(msg->sm_resp); 569 free(msg); 570 } 571 572 int 573 snmpe_encode(struct snmp_message *msg) 574 { 575 struct ber_element *ehdr; 576 struct ber_element *pdu, *epdu; 577 578 msg->sm_resp = ber_add_sequence(NULL); 579 if ((ehdr = ber_add_integer(msg->sm_resp, msg->sm_version)) == NULL) 580 return -1; 581 if (msg->sm_version == SNMP_V3) { 582 char f = MSG_SECLEVEL(msg); 583 584 if ((ehdr = ber_printf_elements(ehdr, "{iixi}", msg->sm_msgid, 585 msg->sm_max_msg_size, &f, sizeof(f), 586 msg->sm_secmodel)) == NULL) 587 return -1; 588 589 /* XXX currently only USM supported */ 590 if ((ehdr = usm_encode(msg, ehdr)) == NULL) 591 return -1; 592 } else { 593 if ((ehdr = ber_add_string(ehdr, msg->sm_community)) == NULL) 594 return -1; 595 } 596 597 pdu = epdu = ber_add_sequence(NULL); 598 if (msg->sm_version == SNMP_V3) { 599 if ((epdu = ber_printf_elements(epdu, "xs{", env->sc_engineid, 600 env->sc_engineid_len, msg->sm_ctxname)) == NULL) { 601 ber_free_elements(pdu); 602 return -1; 603 } 604 } 605 606 if (!ber_printf_elements(epdu, "tiii{e}.", BER_CLASS_CONTEXT, 607 msg->sm_context, msg->sm_request, 608 msg->sm_error, msg->sm_errorindex, 609 msg->sm_varbindresp)) { 610 ber_free_elements(pdu); 611 return -1; 612 } 613 614 if (MSG_HAS_PRIV(msg)) 615 pdu = usm_encrypt(msg, pdu); 616 ber_link_elements(ehdr, pdu); 617 618 #ifdef DEBUG 619 fprintf(stderr, "resp msg:\n"); 620 smi_debug_elements(msg->sm_resp); 621 #endif 622 return 0; 623 } 624