1 /* $OpenBSD: snmp.c,v 1.11 2020/08/02 20:14:10 deraadt 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 166 return snmp_resolve(agent, pdu, 1); 167 fail: 168 ober_free_elements(pdu); 169 return NULL; 170 } 171 172 struct ber_element * 173 snmp_getnext(struct snmp_agent *agent, struct ber_oid *oid, size_t len) 174 { 175 struct ber_element *pdu, *varbind; 176 size_t i; 177 178 if ((pdu = ober_add_sequence(NULL)) == NULL) 179 return NULL; 180 if ((varbind = ober_printf_elements(pdu, "tddd{", BER_CLASS_CONTEXT, 181 SNMP_C_GETNEXTREQ, arc4random() & 0x7fffffff, 0, 0)) == NULL) 182 goto fail; 183 for (i = 0; i < len; i++) { 184 varbind = ober_printf_elements(varbind, "{O0}", &oid[i]); 185 if (varbind == NULL) 186 goto fail; 187 } 188 189 return snmp_resolve(agent, pdu, 1); 190 fail: 191 ober_free_elements(pdu); 192 return NULL; 193 } 194 195 int 196 snmp_trap(struct snmp_agent *agent, struct timespec *uptime, 197 struct ber_oid *oid, struct ber_element *custvarbind) 198 { 199 struct ber_element *pdu, *varbind; 200 struct ber_oid sysuptime, trap; 201 long long ticks; 202 203 if ((pdu = ober_add_sequence(NULL)) == NULL) 204 return -1; 205 if ((varbind = ober_printf_elements(pdu, "tddd{", BER_CLASS_CONTEXT, 206 SNMP_C_TRAPV2, arc4random() & 0x7fffffff, 0, 0)) == NULL) 207 goto fail; 208 209 ticks = uptime->tv_sec * 100; 210 ticks += uptime->tv_nsec / 10000000; 211 if (smi_string2oid("sysUpTime.0", &sysuptime) == -1) 212 goto fail; 213 if ((varbind = ober_printf_elements(varbind, "{Oit}", &sysuptime, ticks, 214 BER_CLASS_APPLICATION, SNMP_T_TIMETICKS)) == NULL) 215 goto fail; 216 if (smi_string2oid("snmpTrapOID.0", &trap) == -1) 217 goto fail; 218 if ((varbind = ober_printf_elements(varbind, "{OO}", &trap, oid)) == NULL) 219 goto fail; 220 if (custvarbind != NULL) 221 ober_link_elements(varbind, custvarbind); 222 223 snmp_resolve(agent, pdu, 0); 224 return 0; 225 fail: 226 ober_free_elements(pdu); 227 return -1; 228 } 229 230 struct ber_element * 231 snmp_getbulk(struct snmp_agent *agent, struct ber_oid *oid, size_t len, 232 int non_repeaters, int max_repetitions) 233 { 234 struct ber_element *pdu, *varbind; 235 size_t i; 236 237 if ((pdu = ober_add_sequence(NULL)) == NULL) 238 return NULL; 239 if ((varbind = ober_printf_elements(pdu, "tddd{", BER_CLASS_CONTEXT, 240 SNMP_C_GETBULKREQ, arc4random() & 0x7fffffff, non_repeaters, 241 max_repetitions)) == NULL) 242 goto fail; 243 for (i = 0; i < len; i++) { 244 varbind = ober_printf_elements(varbind, "{O0}", &oid[i]); 245 if (varbind == NULL) 246 goto fail; 247 } 248 249 return snmp_resolve(agent, pdu, 1); 250 fail: 251 ober_free_elements(pdu); 252 return NULL; 253 } 254 255 struct ber_element * 256 snmp_set(struct snmp_agent *agent, struct ber_element *vblist) 257 { 258 struct ber_element *pdu; 259 260 if ((pdu = ober_add_sequence(NULL)) == NULL) 261 return NULL; 262 if (ober_printf_elements(pdu, "tddd{e", BER_CLASS_CONTEXT, 263 SNMP_C_SETREQ, arc4random() & 0x7fffffff, 0, 0, vblist) == NULL) { 264 ober_free_elements(pdu); 265 ober_free_elements(vblist); 266 return NULL; 267 } 268 269 return snmp_resolve(agent, pdu, 1); 270 } 271 272 static struct ber_element * 273 snmp_resolve(struct snmp_agent *agent, struct ber_element *pdu, int reply) 274 { 275 struct ber_element *varbind; 276 struct ber_oid oid; 277 struct timespec start, now; 278 struct pollfd pfd; 279 char *message; 280 ssize_t len; 281 long long reqid, rreqid; 282 short direction; 283 int to, nfds, ret; 284 int tries; 285 char buf[READ_BUF_SIZE]; 286 287 if (ober_scanf_elements(pdu, "{i", &reqid) != 0) { 288 errno = EINVAL; 289 ober_free_elements(pdu); 290 return NULL; 291 } 292 293 if ((message = snmp_package(agent, pdu, &len)) == NULL) 294 return NULL; 295 296 clock_gettime(CLOCK_MONOTONIC, &start); 297 memcpy(&now, &start, sizeof(now)); 298 direction = POLLOUT; 299 tries = agent->retries + 1; 300 while (tries) { 301 pfd.fd = agent->fd; 302 pfd.events = direction; 303 if (agent->timeout > 0) { 304 to = (agent->timeout - (now.tv_sec - start.tv_sec)) * 1000; 305 to -= (now.tv_nsec - start.tv_nsec) / 1000000; 306 } else 307 to = INFTIM; 308 nfds = poll(&pfd, 1, to); 309 if (nfds == 0) { 310 errno = ETIMEDOUT; 311 direction = POLLOUT; 312 tries--; 313 continue; 314 } 315 if (nfds == -1) { 316 if (errno == EINTR) 317 continue; 318 else 319 goto fail; 320 } 321 if (direction == POLLOUT) { 322 ret = send(agent->fd, message, len, MSG_DONTWAIT); 323 if (ret == -1) 324 goto fail; 325 if (ret < len) { 326 errno = EBADMSG; 327 goto fail; 328 } 329 if (!reply) 330 return NULL; 331 direction = POLLIN; 332 continue; 333 } 334 ret = recv(agent->fd, buf, sizeof(buf), MSG_DONTWAIT); 335 if (ret == 0) 336 errno = ECONNRESET; 337 if (ret <= 0) 338 goto fail; 339 if ((pdu = snmp_unpackage(agent, buf, ret)) == NULL) { 340 tries--; 341 direction = POLLOUT; 342 errno = EPROTO; 343 continue; 344 } 345 /* Validate pdu format and check request id */ 346 if (ober_scanf_elements(pdu, "{iSSe", &rreqid, &varbind) != 0 || 347 varbind->be_encoding != BER_TYPE_SEQUENCE) { 348 errno = EPROTO; 349 direction = POLLOUT; 350 tries--; 351 continue; 352 } 353 if (rreqid != reqid && rreqid != 0) { 354 errno = EPROTO; 355 direction = POLLOUT; 356 tries--; 357 continue; 358 } 359 for (varbind = varbind->be_sub; varbind != NULL; 360 varbind = varbind->be_next) { 361 if (ober_scanf_elements(varbind, "{oS}", &oid) != 0) { 362 errno = EPROTO; 363 direction = POLLOUT; 364 tries--; 365 break; 366 } 367 } 368 if (varbind != NULL) 369 continue; 370 371 free(message); 372 return pdu; 373 } 374 375 fail: 376 free(message); 377 return NULL; 378 } 379 380 static char * 381 snmp_package(struct snmp_agent *agent, struct ber_element *pdu, size_t *len) 382 { 383 struct ber ber; 384 struct ber_element *message, *scopedpdu = NULL, *secparams, *encpdu; 385 ssize_t securitysize, ret; 386 size_t secparamsoffset; 387 char *securityparams = NULL, *packet = NULL; 388 long long msgid; 389 void *cookie = NULL; 390 391 bzero(&ber, sizeof(ber)); 392 ober_set_application(&ber, smi_application); 393 394 if ((message = ober_add_sequence(NULL)) == NULL) { 395 ober_free_elements(pdu); 396 goto fail; 397 } 398 399 switch (agent->version) { 400 case SNMP_V1: 401 case SNMP_V2C: 402 if (ober_printf_elements(message, "dse", agent->version, 403 agent->community, pdu) == NULL) { 404 ober_free_elements(pdu); 405 goto fail; 406 } 407 break; 408 case SNMP_V3: 409 msgid = arc4random_uniform(2147483647); 410 if ((scopedpdu = ober_add_sequence(NULL)) == NULL) { 411 ober_free_elements(pdu); 412 goto fail; 413 } 414 if (ober_printf_elements(scopedpdu, "xxe", 415 agent->v3->engineid, agent->v3->engineidlen, 416 agent->v3->ctxname, agent->v3->ctxnamelen, pdu) == NULL) { 417 ober_free_elements(pdu); 418 ober_free_elements(scopedpdu); 419 goto fail; 420 } 421 pdu = NULL; 422 if ((securityparams = agent->v3->sec->genparams(agent, 423 &securitysize, &cookie)) == NULL) { 424 ober_free_elements(scopedpdu); 425 goto fail; 426 } 427 if (agent->v3->level & SNMP_MSGFLAG_PRIV) { 428 if ((encpdu = agent->v3->sec->encpdu(agent, scopedpdu, 429 cookie)) == NULL) 430 goto fail; 431 ober_free_elements(scopedpdu); 432 scopedpdu = encpdu; 433 } 434 if (ober_printf_elements(message, "d{idxd}xe", 435 agent->version, msgid, UDP_MAXPACKET, &(agent->v3->level), 436 (size_t) 1, agent->v3->sec->model, securityparams, 437 securitysize, scopedpdu) == NULL) { 438 ober_free_elements(scopedpdu); 439 goto fail; 440 } 441 if (ober_scanf_elements(message, "{SSe", &secparams) == -1) 442 goto fail; 443 ober_set_writecallback(secparams, snmp_v3_secparamsoffset, 444 &secparamsoffset); 445 break; 446 } 447 448 if (ober_write_elements(&ber, message) == -1) 449 goto fail; 450 ret = ber_copy_writebuf(&ber, (void **)&packet); 451 452 *len = (size_t) ret; 453 ober_free(&ber); 454 455 if (agent->version == SNMP_V3 && packet != NULL) { 456 if (agent->v3->sec->finalparams(agent, packet, 457 ret, secparamsoffset, cookie) == -1) { 458 free(packet); 459 packet = NULL; 460 } 461 } 462 463 fail: 464 if (agent->version == SNMP_V3) 465 agent->v3->sec->freecookie(cookie); 466 ober_free_elements(message); 467 free(securityparams); 468 return packet; 469 } 470 471 static struct ber_element * 472 snmp_unpackage(struct snmp_agent *agent, char *buf, size_t buflen) 473 { 474 struct ber ber; 475 enum snmp_version version; 476 char *community; 477 struct ber_element *pdu; 478 long long msgid, model; 479 int msgsz; 480 char *msgflags, *secparams; 481 size_t msgflagslen, secparamslen; 482 struct ber_element *message = NULL, *payload, *scopedpdu, *ctxname; 483 off_t secparamsoffset; 484 char *encpdu, *engineid; 485 size_t encpdulen, engineidlen; 486 void *cookie = NULL; 487 488 bzero(&ber, sizeof(ber)); 489 ober_set_application(&ber, smi_application); 490 491 ober_set_readbuf(&ber, buf, buflen); 492 if ((message = ober_read_elements(&ber, NULL)) == NULL) 493 return NULL; 494 ober_free(&ber); 495 496 if (ober_scanf_elements(message, "{de", &version, &payload) != 0) 497 goto fail; 498 499 if (version != agent->version) 500 goto fail; 501 502 switch (version) { 503 case SNMP_V1: 504 case SNMP_V2C: 505 if (ober_scanf_elements(payload, "se", &community, &pdu) == -1) 506 goto fail; 507 if (strcmp(community, agent->community) != 0) 508 goto fail; 509 ober_unlink_elements(payload); 510 ober_free_elements(message); 511 return pdu; 512 case SNMP_V3: 513 if (ober_scanf_elements(payload, "{idxi}pxe", &msgid, &msgsz, 514 &msgflags, &msgflagslen, &model, &secparamsoffset, 515 &secparams, &secparamslen, &scopedpdu) == -1) 516 goto fail; 517 if (msgflagslen != 1) 518 goto fail; 519 if (agent->v3->sec->parseparams(agent, buf, buflen, 520 secparamsoffset, secparams, secparamslen, msgflags[0], 521 &cookie) == -1) { 522 cookie = NULL; 523 goto fail; 524 } 525 if (msgflags[0] & SNMP_MSGFLAG_PRIV) { 526 if (ober_scanf_elements(scopedpdu, "x", &encpdu, 527 &encpdulen) == -1) 528 goto fail; 529 if ((scopedpdu = agent->v3->sec->decpdu(agent, encpdu, 530 encpdulen, cookie)) == NULL) 531 goto fail; 532 } 533 if (ober_scanf_elements(scopedpdu, "{xeS{", &engineid, 534 &engineidlen, &ctxname) == -1) 535 goto fail; 536 if (!agent->v3->engineidset) { 537 if (snmp_v3_setengineid(agent->v3, engineid, 538 engineidlen) == -1) 539 goto fail; 540 } 541 pdu = ober_unlink_elements(ctxname); 542 /* Accept reports, so we can continue if possible */ 543 if (pdu->be_type != SNMP_C_REPORT) { 544 if ((msgflags[0] & SNMP_MSGFLAG_SECMASK) != 545 (agent->v3->level & SNMP_MSGFLAG_SECMASK)) 546 goto fail; 547 } 548 549 ober_free_elements(message); 550 agent->v3->sec->freecookie(cookie); 551 return pdu; 552 } 553 /* NOTREACHED */ 554 555 fail: 556 if (version == SNMP_V3) 557 agent->v3->sec->freecookie(cookie); 558 ober_free_elements(message); 559 return NULL; 560 } 561 562 static void 563 snmp_v3_secparamsoffset(void *cookie, size_t offset) 564 { 565 size_t *spoffset = cookie; 566 567 *spoffset = offset; 568 } 569 570 ssize_t 571 ber_copy_writebuf(struct ber *ber, void **buf) 572 { 573 char *bbuf; 574 ssize_t ret; 575 576 *buf = NULL; 577 if ((ret = ober_get_writebuf(ber, (void **)&bbuf)) == -1) 578 return -1; 579 if ((*buf = malloc(ret)) == NULL) 580 return -1; 581 memcpy(*buf, bbuf, ret); 582 return ret; 583 } 584