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