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