1 /* $OpenBSD: snmp.c,v 1.9 2019/10/24 12:39:26 tb Exp $ */ 2 3 /* 4 * Copyright (c) 2019 Martijn van Duren <martijn@openbsd.org> 5 * Copyright (c) 2013 Reyk Floeter <reyk@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/socket.h> 21 22 #include <errno.h> 23 #include <poll.h> 24 #include <stdlib.h> 25 #include <string.h> 26 #include <stdio.h> 27 #include <time.h> 28 29 #include "ber.h" 30 #include "smi.h" 31 #include "snmp.h" 32 33 #define UDP_MAXPACKET 65535 34 35 static struct ber_element * 36 snmp_resolve(struct snmp_agent *, struct ber_element *, int); 37 static char * 38 snmp_package(struct snmp_agent *, struct ber_element *, size_t *); 39 static struct ber_element * 40 snmp_unpackage(struct snmp_agent *, char *, size_t); 41 static void snmp_v3_free(struct snmp_v3 *); 42 static void snmp_v3_secparamsoffset(void *, size_t); 43 44 struct snmp_v3 * 45 snmp_v3_init(int level, const char *ctxname, size_t ctxnamelen, 46 struct snmp_sec *sec) 47 { 48 struct snmp_v3 *v3; 49 50 if ((level & (SNMP_MSGFLAG_SECMASK | SNMP_MSGFLAG_REPORT)) != level || 51 sec == NULL) { 52 errno = EINVAL; 53 return NULL; 54 } 55 if ((v3 = calloc(1, sizeof(*v3))) == NULL) 56 return NULL; 57 58 v3->level = level | SNMP_MSGFLAG_REPORT; 59 v3->ctxnamelen = ctxnamelen; 60 if (ctxnamelen != 0) { 61 if ((v3->ctxname = malloc(ctxnamelen)) == NULL) { 62 free(v3); 63 return NULL; 64 } 65 memcpy(v3->ctxname, ctxname, ctxnamelen); 66 } 67 v3->sec = sec; 68 return v3; 69 } 70 71 int 72 snmp_v3_setengineid(struct snmp_v3 *v3, char *engineid, size_t engineidlen) 73 { 74 if (v3->engineidset) 75 free(v3->engineid); 76 if ((v3->engineid = malloc(engineidlen)) == NULL) 77 return -1; 78 memcpy(v3->engineid, engineid, engineidlen); 79 v3->engineidlen = engineidlen; 80 v3->engineidset = 1; 81 return 0; 82 } 83 84 struct snmp_agent * 85 snmp_connect_v12(int fd, enum snmp_version version, const char *community) 86 { 87 struct snmp_agent *agent; 88 89 if (version != SNMP_V1 && version != SNMP_V2C) { 90 errno = EINVAL; 91 return NULL; 92 } 93 if ((agent = malloc(sizeof(*agent))) == NULL) 94 return NULL; 95 agent->fd = fd; 96 agent->version = version; 97 if ((agent->community = strdup(community)) == NULL) 98 goto fail; 99 agent->timeout = 1; 100 agent->retries = 5; 101 agent->v3 = NULL; 102 return agent; 103 104 fail: 105 free(agent); 106 return NULL; 107 } 108 109 struct snmp_agent * 110 snmp_connect_v3(int fd, struct snmp_v3 *v3) 111 { 112 struct snmp_agent *agent; 113 114 if ((agent = malloc(sizeof(*agent))) == NULL) 115 return NULL; 116 agent->fd = fd; 117 agent->version = SNMP_V3; 118 agent->v3 = v3; 119 agent->timeout = 1; 120 agent->retries = 5; 121 agent->community = NULL; 122 123 if (v3->sec->init(agent) == -1) { 124 snmp_free_agent(agent); 125 return NULL; 126 } 127 return agent; 128 } 129 130 void 131 snmp_free_agent(struct snmp_agent *agent) 132 { 133 free(agent->community); 134 if (agent->v3 != NULL) 135 snmp_v3_free(agent->v3); 136 free(agent); 137 } 138 139 static void 140 snmp_v3_free(struct snmp_v3 *v3) 141 { 142 v3->sec->free(v3->sec->data); 143 free(v3->sec); 144 free(v3->ctxname); 145 free(v3->engineid); 146 free(v3); 147 } 148 149 struct ber_element * 150 snmp_get(struct snmp_agent *agent, struct ber_oid *oid, size_t len) 151 { 152 struct ber_element *pdu, *varbind; 153 size_t i; 154 155 if ((pdu = ober_add_sequence(NULL)) == NULL) 156 return NULL; 157 if ((varbind = ober_printf_elements(pdu, "tddd{", BER_CLASS_CONTEXT, 158 SNMP_C_GETREQ, arc4random() & 0x7fffffff, 0, 0)) == NULL) 159 goto fail; 160 for (i = 0; i < len; i++) 161 varbind = ober_printf_elements(varbind, "{O0}", &oid[i]); 162 if (varbind == NULL) 163 goto fail; 164 165 return snmp_resolve(agent, pdu, 1); 166 fail: 167 ober_free_elements(pdu); 168 return NULL; 169 } 170 171 struct ber_element * 172 snmp_getnext(struct snmp_agent *agent, struct ber_oid *oid, size_t len) 173 { 174 struct ber_element *pdu, *varbind; 175 size_t i; 176 177 if ((pdu = ober_add_sequence(NULL)) == NULL) 178 return NULL; 179 if ((varbind = ober_printf_elements(pdu, "tddd{", BER_CLASS_CONTEXT, 180 SNMP_C_GETNEXTREQ, arc4random() & 0x7fffffff, 0, 0)) == NULL) 181 goto fail; 182 for (i = 0; i < len; i++) 183 varbind = ober_printf_elements(varbind, "{O0}", &oid[i]); 184 if (varbind == NULL) 185 goto fail; 186 187 return snmp_resolve(agent, pdu, 1); 188 fail: 189 ober_free_elements(pdu); 190 return NULL; 191 } 192 193 int 194 snmp_trap(struct snmp_agent *agent, struct timespec *uptime, 195 struct ber_oid *oid, struct ber_element *custvarbind) 196 { 197 struct ber_element *pdu, *varbind; 198 struct ber_oid sysuptime, trap; 199 long long ticks; 200 201 if ((pdu = ober_add_sequence(NULL)) == NULL) 202 return -1; 203 if ((varbind = ober_printf_elements(pdu, "tddd{", BER_CLASS_CONTEXT, 204 SNMP_C_TRAPV2, arc4random() & 0x7fffffff, 0, 0)) == NULL) 205 goto fail; 206 207 ticks = uptime->tv_sec * 100; 208 ticks += uptime->tv_nsec / 10000000; 209 if (smi_string2oid("sysUpTime.0", &sysuptime) == -1) 210 goto fail; 211 if ((varbind = ober_printf_elements(varbind, "{Oit}", &sysuptime, ticks, 212 BER_CLASS_APPLICATION, SNMP_T_TIMETICKS)) == NULL) 213 goto fail; 214 if (smi_string2oid("snmpTrapOID.0", &trap) == -1) 215 goto fail; 216 if ((varbind = ober_printf_elements(varbind, "{OO}", &trap, oid)) == NULL) 217 goto fail; 218 if (custvarbind != NULL) 219 ober_link_elements(varbind, custvarbind); 220 221 snmp_resolve(agent, pdu, 0); 222 return 0; 223 fail: 224 ober_free_elements(pdu); 225 return -1; 226 } 227 228 struct ber_element * 229 snmp_getbulk(struct snmp_agent *agent, struct ber_oid *oid, size_t len, 230 int non_repeaters, int max_repetitions) 231 { 232 struct ber_element *pdu, *varbind; 233 size_t i; 234 235 if ((pdu = ober_add_sequence(NULL)) == NULL) 236 return NULL; 237 if ((varbind = ober_printf_elements(pdu, "tddd{", BER_CLASS_CONTEXT, 238 SNMP_C_GETBULKREQ, arc4random() & 0x7fffffff, non_repeaters, 239 max_repetitions)) == NULL) 240 goto fail; 241 for (i = 0; i < len; i++) 242 varbind = ober_printf_elements(varbind, "{O0}", &oid[i]); 243 if (varbind == NULL) 244 goto fail; 245 246 return snmp_resolve(agent, pdu, 1); 247 fail: 248 ober_free_elements(pdu); 249 return NULL; 250 } 251 252 struct ber_element * 253 snmp_set(struct snmp_agent *agent, struct ber_element *vblist) 254 { 255 struct ber_element *pdu; 256 257 if ((pdu = ober_add_sequence(NULL)) == NULL) 258 return NULL; 259 if (ober_printf_elements(pdu, "tddd{e", BER_CLASS_CONTEXT, 260 SNMP_C_SETREQ, arc4random() & 0x7fffffff, 0, 0, vblist) == NULL) { 261 ober_free_elements(pdu); 262 ober_free_elements(vblist); 263 return NULL; 264 } 265 266 return snmp_resolve(agent, pdu, 1); 267 } 268 269 static struct ber_element * 270 snmp_resolve(struct snmp_agent *agent, struct ber_element *pdu, int reply) 271 { 272 struct ber_element *varbind; 273 struct ber_oid oid; 274 struct timespec start, now; 275 struct pollfd pfd; 276 char *message; 277 ssize_t len; 278 long long reqid, rreqid; 279 short direction; 280 int to, nfds, ret; 281 int tries; 282 char buf[READ_BUF_SIZE]; 283 284 if (ober_scanf_elements(pdu, "{i", &reqid) != 0) { 285 errno = EINVAL; 286 ober_free_elements(pdu); 287 return NULL; 288 } 289 290 if ((message = snmp_package(agent, pdu, &len)) == NULL) 291 return NULL; 292 293 clock_gettime(CLOCK_MONOTONIC, &start); 294 memcpy(&now, &start, sizeof(now)); 295 direction = POLLOUT; 296 tries = agent->retries + 1; 297 while (tries) { 298 pfd.fd = agent->fd; 299 pfd.events = direction; 300 if (agent->timeout > 0) { 301 to = (agent->timeout - (now.tv_sec - start.tv_sec)) * 1000; 302 to -= (now.tv_nsec - start.tv_nsec) / 1000000; 303 } else 304 to = INFTIM; 305 nfds = poll(&pfd, 1, to); 306 if (nfds == 0) { 307 errno = ETIMEDOUT; 308 direction = POLLOUT; 309 tries--; 310 continue; 311 } 312 if (nfds == -1) { 313 if (errno == EINTR) 314 continue; 315 else 316 goto fail; 317 } 318 if (direction == POLLOUT) { 319 ret = send(agent->fd, message, len, MSG_DONTWAIT); 320 if (ret == -1) 321 goto fail; 322 if (ret < len) { 323 errno = EBADMSG; 324 goto fail; 325 } 326 if (!reply) 327 return NULL; 328 direction = POLLIN; 329 continue; 330 } 331 ret = recv(agent->fd, buf, sizeof(buf), MSG_DONTWAIT); 332 if (ret == 0) 333 errno = ECONNRESET; 334 if (ret <= 0) 335 goto fail; 336 if ((pdu = snmp_unpackage(agent, buf, ret)) == NULL) { 337 tries--; 338 direction = POLLOUT; 339 errno = EPROTO; 340 continue; 341 } 342 /* Validate pdu format and check request id */ 343 if (ober_scanf_elements(pdu, "{iSSe", &rreqid, &varbind) != 0 || 344 varbind->be_encoding != BER_TYPE_SEQUENCE) { 345 errno = EPROTO; 346 direction = POLLOUT; 347 tries--; 348 continue; 349 } 350 if (rreqid != reqid && rreqid != 0) { 351 errno = EPROTO; 352 direction = POLLOUT; 353 tries--; 354 continue; 355 } 356 for (varbind = varbind->be_sub; varbind != NULL; 357 varbind = varbind->be_next) { 358 if (ober_scanf_elements(varbind, "{oS}", &oid) != 0) { 359 errno = EPROTO; 360 direction = POLLOUT; 361 tries--; 362 continue; 363 } 364 } 365 366 free(message); 367 return pdu; 368 } 369 370 fail: 371 free(message); 372 return NULL; 373 } 374 375 static char * 376 snmp_package(struct snmp_agent *agent, struct ber_element *pdu, size_t *len) 377 { 378 struct ber ber; 379 struct ber_element *message, *scopedpdu = NULL, *secparams, *encpdu; 380 ssize_t securitysize, ret; 381 size_t secparamsoffset; 382 char *securityparams = NULL, *packet = NULL; 383 long long msgid; 384 void *cookie = NULL; 385 386 bzero(&ber, sizeof(ber)); 387 ober_set_application(&ber, smi_application); 388 389 if ((message = ober_add_sequence(NULL)) == NULL) { 390 ober_free_elements(pdu); 391 goto fail; 392 } 393 394 switch (agent->version) { 395 case SNMP_V1: 396 case SNMP_V2C: 397 if (ober_printf_elements(message, "dse", agent->version, 398 agent->community, pdu) == NULL) { 399 ober_free_elements(pdu); 400 goto fail; 401 } 402 break; 403 case SNMP_V3: 404 msgid = arc4random_uniform(2147483647); 405 if ((scopedpdu = ober_add_sequence(NULL)) == NULL) { 406 ober_free_elements(pdu); 407 goto fail; 408 } 409 if (ober_printf_elements(scopedpdu, "xxe", 410 agent->v3->engineid, agent->v3->engineidlen, 411 agent->v3->ctxname, agent->v3->ctxnamelen, pdu) == NULL) { 412 ober_free_elements(pdu); 413 ober_free_elements(scopedpdu); 414 goto fail; 415 } 416 pdu = NULL; 417 if ((securityparams = agent->v3->sec->genparams(agent, 418 &securitysize, &cookie)) == NULL) { 419 ober_free_elements(scopedpdu); 420 goto fail; 421 } 422 if (agent->v3->level & SNMP_MSGFLAG_PRIV) { 423 if ((encpdu = agent->v3->sec->encpdu(agent, scopedpdu, 424 cookie)) == NULL) 425 goto fail; 426 ober_free_elements(scopedpdu); 427 scopedpdu = encpdu; 428 } 429 if (ober_printf_elements(message, "d{idxd}xe", 430 agent->version, msgid, UDP_MAXPACKET, &(agent->v3->level), 431 (size_t) 1, agent->v3->sec->model, securityparams, 432 securitysize, scopedpdu) == NULL) { 433 ober_free_elements(scopedpdu); 434 goto fail; 435 } 436 if (ober_scanf_elements(message, "{SSe", &secparams) == -1) 437 goto fail; 438 ober_set_writecallback(secparams, snmp_v3_secparamsoffset, 439 &secparamsoffset); 440 break; 441 } 442 443 if (ober_write_elements(&ber, message) == -1) 444 goto fail; 445 ret = ber_copy_writebuf(&ber, (void **)&packet); 446 447 *len = (size_t) ret; 448 ober_free(&ber); 449 450 if (agent->version == SNMP_V3 && packet != NULL) { 451 if (agent->v3->sec->finalparams(agent, packet, 452 ret, secparamsoffset, cookie) == -1) { 453 free(packet); 454 packet = NULL; 455 } 456 } 457 458 fail: 459 if (agent->version == SNMP_V3) 460 agent->v3->sec->freecookie(cookie); 461 ober_free_elements(message); 462 free(securityparams); 463 return packet; 464 } 465 466 static struct ber_element * 467 snmp_unpackage(struct snmp_agent *agent, char *buf, size_t buflen) 468 { 469 struct ber ber; 470 enum snmp_version version; 471 char *community; 472 struct ber_element *pdu; 473 long long msgid, model; 474 int msgsz; 475 char *msgflags, *secparams; 476 size_t msgflagslen, secparamslen; 477 struct ber_element *message = NULL, *payload, *scopedpdu, *ctxname; 478 off_t secparamsoffset; 479 char *encpdu, *engineid; 480 size_t encpdulen, engineidlen; 481 void *cookie = NULL; 482 483 bzero(&ber, sizeof(ber)); 484 ober_set_application(&ber, smi_application); 485 486 ober_set_readbuf(&ber, buf, buflen); 487 if ((message = ober_read_elements(&ber, NULL)) == NULL) 488 return NULL; 489 ober_free(&ber); 490 491 if (ober_scanf_elements(message, "{de", &version, &payload) != 0) 492 goto fail; 493 494 if (version != agent->version) 495 goto fail; 496 497 switch (version) { 498 case SNMP_V1: 499 case SNMP_V2C: 500 if (ober_scanf_elements(payload, "se", &community, &pdu) == -1) 501 goto fail; 502 if (strcmp(community, agent->community) != 0) 503 goto fail; 504 ober_unlink_elements(payload); 505 ober_free_elements(message); 506 return pdu; 507 case SNMP_V3: 508 if (ober_scanf_elements(payload, "{idxi}pxe", &msgid, &msgsz, 509 &msgflags, &msgflagslen, &model, &secparamsoffset, 510 &secparams, &secparamslen, &scopedpdu) == -1) 511 goto fail; 512 if (msgflagslen != 1) 513 goto fail; 514 if (agent->v3->sec->parseparams(agent, buf, buflen, 515 secparamsoffset, secparams, secparamslen, msgflags[0], 516 &cookie) == -1) { 517 cookie = NULL; 518 goto fail; 519 } 520 if (msgflags[0] & SNMP_MSGFLAG_PRIV) { 521 if (ober_scanf_elements(scopedpdu, "x", &encpdu, 522 &encpdulen) == -1) 523 goto fail; 524 if ((scopedpdu = agent->v3->sec->decpdu(agent, encpdu, 525 encpdulen, cookie)) == NULL) 526 goto fail; 527 } 528 if (ober_scanf_elements(scopedpdu, "{xeS{", &engineid, 529 &engineidlen, &ctxname) == -1) 530 goto fail; 531 if (!agent->v3->engineidset) { 532 if (snmp_v3_setengineid(agent->v3, engineid, 533 engineidlen) == -1) 534 goto fail; 535 } 536 pdu = ober_unlink_elements(ctxname); 537 /* Accept reports, so we can continue if possible */ 538 if (pdu->be_type != SNMP_C_REPORT) { 539 if ((msgflags[0] & SNMP_MSGFLAG_SECMASK) != 540 (agent->v3->level & SNMP_MSGFLAG_SECMASK)) 541 goto fail; 542 } 543 544 ober_free_elements(message); 545 agent->v3->sec->freecookie(cookie); 546 return pdu; 547 } 548 /* NOTREACHED */ 549 550 fail: 551 if (version == SNMP_V3) 552 agent->v3->sec->freecookie(cookie); 553 ober_free_elements(message); 554 return NULL; 555 } 556 557 static void 558 snmp_v3_secparamsoffset(void *cookie, size_t offset) 559 { 560 size_t *spoffset = cookie; 561 562 *spoffset = offset; 563 } 564 565 ssize_t 566 ber_copy_writebuf(struct ber *ber, void **buf) 567 { 568 char *bbuf; 569 ssize_t ret; 570 571 *buf = NULL; 572 if ((ret = ober_get_writebuf(ber, (void **)&bbuf)) == -1) 573 return -1; 574 if ((*buf = malloc(ret)) == NULL) 575 return -1; 576 memcpy(*buf, bbuf, ret); 577 return ret; 578 } 579