1 /* $OpenBSD: snmp.c,v 1.10 2020/03/24 14:09:14 martijn 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 break; 363 } 364 } 365 if (varbind != NULL) 366 continue; 367 368 free(message); 369 return pdu; 370 } 371 372 fail: 373 free(message); 374 return NULL; 375 } 376 377 static char * 378 snmp_package(struct snmp_agent *agent, struct ber_element *pdu, size_t *len) 379 { 380 struct ber ber; 381 struct ber_element *message, *scopedpdu = NULL, *secparams, *encpdu; 382 ssize_t securitysize, ret; 383 size_t secparamsoffset; 384 char *securityparams = NULL, *packet = NULL; 385 long long msgid; 386 void *cookie = NULL; 387 388 bzero(&ber, sizeof(ber)); 389 ober_set_application(&ber, smi_application); 390 391 if ((message = ober_add_sequence(NULL)) == NULL) { 392 ober_free_elements(pdu); 393 goto fail; 394 } 395 396 switch (agent->version) { 397 case SNMP_V1: 398 case SNMP_V2C: 399 if (ober_printf_elements(message, "dse", agent->version, 400 agent->community, pdu) == NULL) { 401 ober_free_elements(pdu); 402 goto fail; 403 } 404 break; 405 case SNMP_V3: 406 msgid = arc4random_uniform(2147483647); 407 if ((scopedpdu = ober_add_sequence(NULL)) == NULL) { 408 ober_free_elements(pdu); 409 goto fail; 410 } 411 if (ober_printf_elements(scopedpdu, "xxe", 412 agent->v3->engineid, agent->v3->engineidlen, 413 agent->v3->ctxname, agent->v3->ctxnamelen, pdu) == NULL) { 414 ober_free_elements(pdu); 415 ober_free_elements(scopedpdu); 416 goto fail; 417 } 418 pdu = NULL; 419 if ((securityparams = agent->v3->sec->genparams(agent, 420 &securitysize, &cookie)) == NULL) { 421 ober_free_elements(scopedpdu); 422 goto fail; 423 } 424 if (agent->v3->level & SNMP_MSGFLAG_PRIV) { 425 if ((encpdu = agent->v3->sec->encpdu(agent, scopedpdu, 426 cookie)) == NULL) 427 goto fail; 428 ober_free_elements(scopedpdu); 429 scopedpdu = encpdu; 430 } 431 if (ober_printf_elements(message, "d{idxd}xe", 432 agent->version, msgid, UDP_MAXPACKET, &(agent->v3->level), 433 (size_t) 1, agent->v3->sec->model, securityparams, 434 securitysize, scopedpdu) == NULL) { 435 ober_free_elements(scopedpdu); 436 goto fail; 437 } 438 if (ober_scanf_elements(message, "{SSe", &secparams) == -1) 439 goto fail; 440 ober_set_writecallback(secparams, snmp_v3_secparamsoffset, 441 &secparamsoffset); 442 break; 443 } 444 445 if (ober_write_elements(&ber, message) == -1) 446 goto fail; 447 ret = ber_copy_writebuf(&ber, (void **)&packet); 448 449 *len = (size_t) ret; 450 ober_free(&ber); 451 452 if (agent->version == SNMP_V3 && packet != NULL) { 453 if (agent->v3->sec->finalparams(agent, packet, 454 ret, secparamsoffset, cookie) == -1) { 455 free(packet); 456 packet = NULL; 457 } 458 } 459 460 fail: 461 if (agent->version == SNMP_V3) 462 agent->v3->sec->freecookie(cookie); 463 ober_free_elements(message); 464 free(securityparams); 465 return packet; 466 } 467 468 static struct ber_element * 469 snmp_unpackage(struct snmp_agent *agent, char *buf, size_t buflen) 470 { 471 struct ber ber; 472 enum snmp_version version; 473 char *community; 474 struct ber_element *pdu; 475 long long msgid, model; 476 int msgsz; 477 char *msgflags, *secparams; 478 size_t msgflagslen, secparamslen; 479 struct ber_element *message = NULL, *payload, *scopedpdu, *ctxname; 480 off_t secparamsoffset; 481 char *encpdu, *engineid; 482 size_t encpdulen, engineidlen; 483 void *cookie = NULL; 484 485 bzero(&ber, sizeof(ber)); 486 ober_set_application(&ber, smi_application); 487 488 ober_set_readbuf(&ber, buf, buflen); 489 if ((message = ober_read_elements(&ber, NULL)) == NULL) 490 return NULL; 491 ober_free(&ber); 492 493 if (ober_scanf_elements(message, "{de", &version, &payload) != 0) 494 goto fail; 495 496 if (version != agent->version) 497 goto fail; 498 499 switch (version) { 500 case SNMP_V1: 501 case SNMP_V2C: 502 if (ober_scanf_elements(payload, "se", &community, &pdu) == -1) 503 goto fail; 504 if (strcmp(community, agent->community) != 0) 505 goto fail; 506 ober_unlink_elements(payload); 507 ober_free_elements(message); 508 return pdu; 509 case SNMP_V3: 510 if (ober_scanf_elements(payload, "{idxi}pxe", &msgid, &msgsz, 511 &msgflags, &msgflagslen, &model, &secparamsoffset, 512 &secparams, &secparamslen, &scopedpdu) == -1) 513 goto fail; 514 if (msgflagslen != 1) 515 goto fail; 516 if (agent->v3->sec->parseparams(agent, buf, buflen, 517 secparamsoffset, secparams, secparamslen, msgflags[0], 518 &cookie) == -1) { 519 cookie = NULL; 520 goto fail; 521 } 522 if (msgflags[0] & SNMP_MSGFLAG_PRIV) { 523 if (ober_scanf_elements(scopedpdu, "x", &encpdu, 524 &encpdulen) == -1) 525 goto fail; 526 if ((scopedpdu = agent->v3->sec->decpdu(agent, encpdu, 527 encpdulen, cookie)) == NULL) 528 goto fail; 529 } 530 if (ober_scanf_elements(scopedpdu, "{xeS{", &engineid, 531 &engineidlen, &ctxname) == -1) 532 goto fail; 533 if (!agent->v3->engineidset) { 534 if (snmp_v3_setengineid(agent->v3, engineid, 535 engineidlen) == -1) 536 goto fail; 537 } 538 pdu = ober_unlink_elements(ctxname); 539 /* Accept reports, so we can continue if possible */ 540 if (pdu->be_type != SNMP_C_REPORT) { 541 if ((msgflags[0] & SNMP_MSGFLAG_SECMASK) != 542 (agent->v3->level & SNMP_MSGFLAG_SECMASK)) 543 goto fail; 544 } 545 546 ober_free_elements(message); 547 agent->v3->sec->freecookie(cookie); 548 return pdu; 549 } 550 /* NOTREACHED */ 551 552 fail: 553 if (version == SNMP_V3) 554 agent->v3->sec->freecookie(cookie); 555 ober_free_elements(message); 556 return NULL; 557 } 558 559 static void 560 snmp_v3_secparamsoffset(void *cookie, size_t offset) 561 { 562 size_t *spoffset = cookie; 563 564 *spoffset = offset; 565 } 566 567 ssize_t 568 ber_copy_writebuf(struct ber *ber, void **buf) 569 { 570 char *bbuf; 571 ssize_t ret; 572 573 *buf = NULL; 574 if ((ret = ober_get_writebuf(ber, (void **)&bbuf)) == -1) 575 return -1; 576 if ((*buf = malloc(ret)) == NULL) 577 return -1; 578 memcpy(*buf, bbuf, ret); 579 return ret; 580 } 581