1 /* $OpenBSD: usm.c,v 1.7 2022/01/05 16:41:07 tb Exp $ */ 2 3 /* 4 * Copyright (c) 2019 Martijn van Duren <martijn@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/time.h> 20 21 #include <openssl/evp.h> 22 #include <openssl/hmac.h> 23 24 #include <ber.h> 25 #include <errno.h> 26 #include <string.h> 27 #include <time.h> 28 29 #include "smi.h" 30 #include "snmp.h" 31 #include "usm.h" 32 33 #define USM_MAX_DIGESTLEN 48 34 #define USM_MAX_TIMEWINDOW 150 35 #define USM_SALTOFFSET 8 36 37 struct usm_sec { 38 struct snmp_sec snmp; 39 char *user; 40 size_t userlen; 41 int engineidset; 42 char *engineid; 43 size_t engineidlen; 44 enum usm_key_level authlevel; 45 const EVP_MD *digest; 46 char *authkey; 47 enum usm_key_level privlevel; 48 const EVP_CIPHER *cipher; 49 char *privkey; 50 int bootsset; 51 uint32_t boots; 52 int timeset; 53 uint32_t time; 54 struct timespec timecheck; 55 }; 56 57 struct usm_cookie { 58 size_t digestoffset; 59 long long salt; 60 uint32_t boots; 61 uint32_t time; 62 }; 63 64 static int usm_doinit(struct snmp_agent *); 65 static char *usm_genparams(struct snmp_agent *, size_t *, void **); 66 static int usm_finalparams(struct snmp_agent *, char *, size_t, size_t, void *); 67 static struct ber_element *usm_encpdu(struct snmp_agent *agent, 68 struct ber_element *pdu, void *cookie); 69 static char *usm_crypt(const EVP_CIPHER *, int, char *, struct usm_cookie *, 70 char *, size_t, size_t *); 71 static int usm_parseparams(struct snmp_agent *, char *, size_t, off_t, char *, 72 size_t, uint8_t, void **); 73 struct ber_element *usm_decpdu(struct snmp_agent *, char *, size_t, void *); 74 static void usm_digest_pos(void *, size_t); 75 static void usm_free(void *); 76 static char *usm_passwd2mkey(const EVP_MD *, const char *); 77 static char *usm_mkey2lkey(struct usm_sec *, const EVP_MD *, const char *); 78 static size_t usm_digestlen(const EVP_MD *); 79 80 struct snmp_sec * 81 usm_init(const char *user, size_t userlen) 82 { 83 struct snmp_sec *sec; 84 struct usm_sec *usm; 85 86 if (user == NULL || user[0] == '\0') { 87 errno = EINVAL; 88 return NULL; 89 } 90 91 if ((sec = malloc(sizeof(*sec))) == NULL) 92 return NULL; 93 94 if ((usm = calloc(1, sizeof(struct usm_sec))) == NULL) { 95 free(sec); 96 return NULL; 97 } 98 if ((usm->user = malloc(userlen)) == NULL) { 99 free(sec); 100 free(usm); 101 return NULL; 102 } 103 memcpy(usm->user, user, userlen); 104 usm->userlen = userlen; 105 106 sec->model = SNMP_SEC_USM; 107 sec->init = usm_doinit; 108 sec->genparams = usm_genparams; 109 sec->encpdu = usm_encpdu; 110 sec->parseparams = usm_parseparams; 111 sec->decpdu = usm_decpdu; 112 sec->finalparams = usm_finalparams; 113 sec->free = usm_free; 114 sec->freecookie = free; 115 sec->data = usm; 116 return sec; 117 } 118 119 static int 120 usm_doinit(struct snmp_agent *agent) 121 { 122 struct ber_element *ber; 123 struct usm_sec *usm = agent->v3->sec->data; 124 int level; 125 size_t userlen; 126 127 if (usm->engineidset && usm->bootsset && usm->timeset) 128 return 0; 129 130 level = agent->v3->level; 131 agent->v3->level = SNMP_MSGFLAG_REPORT; 132 userlen = usm->userlen; 133 usm->userlen = 0; 134 135 if ((ber = snmp_get(agent, NULL, 0)) == NULL) { 136 agent->v3->level = level; 137 usm->userlen = userlen; 138 return -1; 139 } 140 ober_free_element(ber); 141 142 agent->v3->level = level; 143 usm->userlen = userlen; 144 145 /* 146 * Ugly hack for HP Laserjet: 147 * This device returns the engineid on probing, but only returns boots 148 * and time after a packet has been sent with full auth/enc. 149 */ 150 if (!usm->engineidset || !usm->bootsset || !usm->timeset) { 151 if ((ber = snmp_get(agent, NULL, 0)) == NULL) 152 return -1; 153 ober_free_element(ber); 154 } 155 return 0; 156 } 157 158 static char * 159 usm_genparams(struct snmp_agent *agent, size_t *len, void **cookie) 160 { 161 struct ber ber; 162 struct ber_element *params, *digestelm; 163 struct usm_sec *usm = agent->v3->sec->data; 164 char digest[USM_MAX_DIGESTLEN]; 165 size_t digestlen = 0, saltlen = 0; 166 char *secparams = NULL; 167 ssize_t berlen = 0; 168 struct usm_cookie *usmcookie; 169 struct timespec now, timediff; 170 171 bzero(digest, sizeof(digest)); 172 173 if ((usmcookie = calloc(1, sizeof(*usmcookie))) == NULL) 174 return NULL; 175 *cookie = usmcookie; 176 177 arc4random_buf(&(usmcookie->salt), sizeof(usmcookie->salt)); 178 if (usm->timeset) { 179 if (clock_gettime(CLOCK_MONOTONIC, &now) == -1) { 180 free(usmcookie); 181 return NULL; 182 } 183 timespecsub(&now, &(usm->timecheck), &timediff); 184 usmcookie->time = usm->time + timediff.tv_sec; 185 } else 186 usmcookie->time = 0; 187 usmcookie->boots = usm->boots; 188 189 if (agent->v3->level & SNMP_MSGFLAG_AUTH) 190 digestlen = usm_digestlen(usm->digest); 191 if (agent->v3->level & SNMP_MSGFLAG_PRIV) 192 saltlen = sizeof(usmcookie->salt); 193 194 if ((params = ober_printf_elements(NULL, "{xddxxx}", usm->engineid, 195 usm->engineidlen, usmcookie->boots, usmcookie->time, usm->user, 196 usm->userlen, digest, digestlen, &(usmcookie->salt), 197 saltlen)) == NULL) { 198 free(usmcookie); 199 return NULL; 200 } 201 202 if (ober_scanf_elements(params, "{SSSSe", &digestelm) == -1) { 203 ober_free_element(params); 204 free(usmcookie); 205 return NULL; 206 } 207 208 ober_set_writecallback(digestelm, usm_digest_pos, usmcookie); 209 210 bzero(&ber, sizeof(ber)); 211 ober_set_application(&ber, smi_application); 212 if (ober_write_elements(&ber, params) != -1) 213 berlen = ber_copy_writebuf(&ber, (void **)&secparams); 214 215 *len = berlen; 216 ober_free_element(params); 217 ober_free(&ber); 218 return secparams; 219 } 220 221 static struct ber_element * 222 usm_encpdu(struct snmp_agent *agent, struct ber_element *pdu, void *cookie) 223 { 224 struct usm_sec *usm = agent->v3->sec->data; 225 struct usm_cookie *usmcookie = cookie; 226 struct ber ber; 227 struct ber_element *retpdu; 228 char *serialpdu, *encpdu; 229 ssize_t pdulen; 230 size_t encpdulen; 231 232 bzero(&ber, sizeof(ber)); 233 ober_set_application(&ber, smi_application); 234 pdulen = ober_write_elements(&ber, pdu); 235 if (pdulen == -1) 236 return NULL; 237 238 ober_get_writebuf(&ber, (void **)&serialpdu); 239 240 encpdu = usm_crypt(usm->cipher, 1, usm->privkey, usmcookie, serialpdu, 241 pdulen, &encpdulen); 242 ober_free(&ber); 243 if (encpdu == NULL) 244 return NULL; 245 246 retpdu = ober_add_nstring(NULL, encpdu, encpdulen); 247 free(encpdu); 248 return retpdu; 249 } 250 251 static char * 252 usm_crypt(const EVP_CIPHER *cipher, int do_enc, char *key, 253 struct usm_cookie *cookie, char *serialpdu, size_t pdulen, size_t *outlen) 254 { 255 EVP_CIPHER_CTX *ctx; 256 size_t i; 257 char iv[EVP_MAX_IV_LENGTH]; 258 char *salt = (char *)&(cookie->salt); 259 char *outtext; 260 int len, len2, bs; 261 uint32_t ivv; 262 263 switch (EVP_CIPHER_type(cipher)) { 264 case NID_des_cbc: 265 /* RFC3414, chap 8.1.1.1. */ 266 for (i = 0; i < 8; i++) 267 iv[i] = salt[i] ^ key[USM_SALTOFFSET + i]; 268 break; 269 case NID_aes_128_cfb128: 270 /* RFC3826, chap 3.1.2.1. */ 271 ivv = htobe32(cookie->boots); 272 memcpy(iv, &ivv, sizeof(ivv)); 273 ivv = htobe32(cookie->time); 274 memcpy(iv + sizeof(ivv), &ivv, sizeof(ivv)); 275 memcpy(iv + 2 * sizeof(ivv), &(cookie->salt), 276 sizeof(cookie->salt)); 277 break; 278 default: 279 return NULL; 280 } 281 282 if ((ctx = EVP_CIPHER_CTX_new()) == NULL) 283 return NULL; 284 285 if (!EVP_CipherInit(ctx, cipher, key, iv, do_enc)) { 286 EVP_CIPHER_CTX_free(ctx); 287 return NULL; 288 } 289 290 EVP_CIPHER_CTX_set_padding(ctx, do_enc); 291 292 bs = EVP_CIPHER_block_size(cipher); 293 /* Maximum output size */ 294 *outlen = pdulen + (bs - (pdulen % bs)); 295 296 if ((outtext = malloc(*outlen)) == NULL) { 297 EVP_CIPHER_CTX_free(ctx); 298 return NULL; 299 } 300 301 if (EVP_CipherUpdate(ctx, outtext, &len, serialpdu, pdulen) && 302 EVP_CipherFinal_ex(ctx, outtext + len, &len2)) 303 *outlen = len + len2; 304 else { 305 free(outtext); 306 outtext = NULL; 307 } 308 309 EVP_CIPHER_CTX_free(ctx); 310 311 return outtext; 312 } 313 314 static int 315 usm_finalparams(struct snmp_agent *agent, char *buf, size_t buflen, 316 size_t secparamsoffset, void *cookie) 317 { 318 struct usm_sec *usm = agent->v3->sec->data; 319 struct usm_cookie *usmcookie = cookie; 320 u_char digest[EVP_MAX_MD_SIZE]; 321 322 if ((agent->v3->level & SNMP_MSGFLAG_AUTH) == 0) 323 return 0; 324 325 if (usm->authlevel != USM_KEY_LOCALIZED) 326 return -1; 327 328 if (HMAC(usm->digest, usm->authkey, EVP_MD_size(usm->digest), buf, 329 buflen, digest, NULL) == NULL) 330 return -1; 331 332 memcpy(buf + secparamsoffset + usmcookie->digestoffset, digest, 333 usm_digestlen(usm->digest)); 334 return 0; 335 } 336 337 static int 338 usm_parseparams(struct snmp_agent *agent, char *packet, size_t packetlen, 339 off_t secparamsoffset, char *buf, size_t buflen, uint8_t level, 340 void **cookie) 341 { 342 struct usm_sec *usm = agent->v3->sec->data; 343 struct ber ber; 344 struct ber_element *secparams; 345 char *engineid, *user, *digest, *salt; 346 size_t engineidlen, userlen, digestlen, saltlen; 347 struct timespec now, timediff; 348 off_t digestoffset; 349 char exp_digest[EVP_MAX_MD_SIZE]; 350 struct usm_cookie *usmcookie; 351 352 bzero(&ber, sizeof(ber)); 353 bzero(exp_digest, sizeof(exp_digest)); 354 355 ober_set_application(&ber, smi_application); 356 ober_set_readbuf(&ber, buf, buflen); 357 if ((secparams = ober_read_elements(&ber, NULL)) == NULL) 358 return -1; 359 ober_free(&ber); 360 361 if ((usmcookie = malloc(sizeof(*usmcookie))) == NULL) 362 goto fail; 363 *cookie = usmcookie; 364 365 if (ober_scanf_elements(secparams, "{xddxpxx}", &engineid, &engineidlen, 366 &(usmcookie->boots), &(usmcookie->time), &user, &userlen, 367 &digestoffset, &digest, &digestlen, &salt, &saltlen) == -1) 368 goto fail; 369 if (saltlen != sizeof(usmcookie->salt) && saltlen != 0) 370 goto fail; 371 memcpy(&(usmcookie->salt), salt, saltlen); 372 373 if (!usm->engineidset) { 374 if (usm_setengineid(agent->v3->sec, engineid, 375 engineidlen) == -1) 376 goto fail; 377 } else { 378 if (usm->engineidlen != engineidlen) 379 goto fail; 380 if (memcmp(usm->engineid, engineid, engineidlen) != 0) 381 goto fail; 382 } 383 384 if (!usm->bootsset) { 385 usm->boots = usmcookie->boots; 386 usm->bootsset = 1; 387 } else { 388 if (usmcookie->boots < usm->boots) 389 goto fail; 390 if (usmcookie->boots > usm->boots) { 391 usm->bootsset = 0; 392 usm->timeset = 0; 393 usm_doinit(agent); 394 goto fail; 395 } 396 } 397 398 if (!usm->timeset) { 399 usm->time = usmcookie->time; 400 if (clock_gettime(CLOCK_MONOTONIC, &usm->timecheck) == -1) 401 goto fail; 402 usm->timeset = 1; 403 } else { 404 if (clock_gettime(CLOCK_MONOTONIC, &now) == -1) 405 goto fail; 406 timespecsub(&now, &(usm->timecheck), &timediff); 407 if (usmcookie->time < 408 usm->time + timediff.tv_sec - USM_MAX_TIMEWINDOW || 409 usmcookie->time > 410 usm->time + timediff.tv_sec + USM_MAX_TIMEWINDOW) { 411 usm->bootsset = 0; 412 usm->timeset = 0; 413 usm_doinit(agent); 414 goto fail; 415 } 416 } 417 /* 418 * Don't assume these are set if both are zero. 419 * Ugly hack for HP Laserjet 420 */ 421 if (usm->boots == 0 && usm->time == 0) { 422 usm->bootsset = 0; 423 usm->timeset = 0; 424 } 425 426 if (userlen != usm->userlen || 427 memcmp(user, usm->user, userlen) != 0) 428 goto fail; 429 430 if (level & SNMP_MSGFLAG_AUTH) { 431 if (digestlen != usm_digestlen(usm->digest)) 432 goto fail; 433 } 434 if ((agent->v3->level & SNMP_MSGFLAG_AUTH)) { 435 bzero(packet + secparamsoffset + digestoffset, digestlen); 436 if (HMAC(usm->digest, usm->authkey, EVP_MD_size(usm->digest), packet, 437 packetlen, exp_digest, NULL) == NULL) 438 goto fail; 439 440 if (memcmp(exp_digest, digest, digestlen) != 0) 441 goto fail; 442 } else 443 if (digestlen != 0) 444 goto fail; 445 446 ober_free_element(secparams); 447 return 0; 448 449 fail: 450 free(usmcookie); 451 ober_free_element(secparams); 452 return -1; 453 } 454 455 struct ber_element * 456 usm_decpdu(struct snmp_agent *agent, char *encpdu, size_t encpdulen, void *cookie) 457 { 458 struct usm_sec *usm = agent->v3->sec->data; 459 struct usm_cookie *usmcookie = cookie; 460 struct ber ber; 461 struct ber_element *scopedpdu; 462 char *rawpdu; 463 size_t rawpdulen; 464 465 if ((rawpdu = usm_crypt(usm->cipher, 0, usm->privkey, usmcookie, 466 encpdu, encpdulen, &rawpdulen)) == NULL) 467 return NULL; 468 469 bzero(&ber, sizeof(ber)); 470 ober_set_application(&ber, smi_application); 471 ober_set_readbuf(&ber, rawpdu, rawpdulen); 472 scopedpdu = ober_read_elements(&ber, NULL); 473 ober_free(&ber); 474 free(rawpdu); 475 476 return scopedpdu; 477 } 478 479 static void 480 usm_digest_pos(void *data, size_t offset) 481 { 482 struct usm_cookie *usmcookie = data; 483 484 usmcookie->digestoffset = offset; 485 } 486 487 static void 488 usm_free(void *data) 489 { 490 struct usm_sec *usm = data; 491 492 free(usm->user); 493 free(usm->authkey); 494 free(usm->privkey); 495 free(usm->engineid); 496 free(usm); 497 } 498 499 int 500 usm_setauth(struct snmp_sec *sec, const EVP_MD *digest, const char *key, 501 size_t keylen, enum usm_key_level level) 502 { 503 struct usm_sec *usm = sec->data; 504 char *lkey; 505 506 /* 507 * We could transform a master key to a local key here if we already 508 * have usm_setengineid called. Sine snmpc.c is the only caller at 509 * the moment there's no need, since it always calls this function 510 * first. 511 */ 512 if (level == USM_KEY_PASSWORD) { 513 if ((usm->authkey = usm_passwd2mkey(digest, key)) == NULL) 514 return -1; 515 level = USM_KEY_MASTER; 516 keylen = EVP_MD_size(digest); 517 } else { 518 if (keylen != (size_t)EVP_MD_size(digest)) { 519 errno = EINVAL; 520 return -1; 521 } 522 if ((lkey = malloc(keylen)) == NULL) 523 return -1; 524 memcpy(lkey, key, keylen); 525 usm->authkey = lkey; 526 } 527 usm->digest = digest; 528 usm->authlevel = level; 529 return 0; 530 } 531 532 int 533 usm_setpriv(struct snmp_sec *sec, const EVP_CIPHER *cipher, const char *key, 534 size_t keylen, enum usm_key_level level) 535 { 536 struct usm_sec *usm = sec->data; 537 char *lkey; 538 539 if (usm->digest == NULL) { 540 errno = EINVAL; 541 return -1; 542 } 543 544 /* 545 * We could transform a master key to a local key here if we already 546 * have usm_setengineid called. Sine snmpc.c is the only caller at 547 * the moment there's no need, since it always calls us first. 548 */ 549 if (level == USM_KEY_PASSWORD) { 550 if ((usm->privkey = usm_passwd2mkey(usm->digest, key)) == NULL) 551 return -1; 552 level = USM_KEY_MASTER; 553 keylen = EVP_MD_size(usm->digest); 554 } else { 555 if (keylen != (size_t)EVP_MD_size(usm->digest)) { 556 errno = EINVAL; 557 return -1; 558 } 559 if ((lkey = malloc(keylen)) == NULL) 560 return -1; 561 memcpy(lkey, key, keylen); 562 usm->privkey = lkey; 563 } 564 usm->cipher = cipher; 565 usm->privlevel = level; 566 return 0; 567 } 568 569 int 570 usm_setengineid(struct snmp_sec *sec, char *engineid, size_t engineidlen) 571 { 572 struct usm_sec *usm = sec->data; 573 char *mkey; 574 575 if (usm->engineid != NULL) 576 free(usm->engineid); 577 if ((usm->engineid = malloc(engineidlen)) == NULL) 578 return -1; 579 memcpy(usm->engineid, engineid, engineidlen); 580 usm->engineidlen = engineidlen; 581 usm->engineidset = 1; 582 583 if (usm->authlevel == USM_KEY_MASTER) { 584 mkey = usm->authkey; 585 if ((usm->authkey = usm_mkey2lkey(usm, usm->digest, 586 mkey)) == NULL) { 587 usm->authkey = mkey; 588 return -1; 589 } 590 free(mkey); 591 usm->authlevel = USM_KEY_LOCALIZED; 592 } 593 if (usm->privlevel == USM_KEY_MASTER) { 594 mkey = usm->privkey; 595 if ((usm->privkey = usm_mkey2lkey(usm, usm->digest, 596 mkey)) == NULL) { 597 usm->privkey = mkey; 598 return -1; 599 } 600 free(mkey); 601 usm->privlevel = USM_KEY_LOCALIZED; 602 } 603 604 return 0; 605 } 606 607 int 608 usm_setbootstime(struct snmp_sec *sec, uint32_t boots, uint32_t time) 609 { 610 struct usm_sec *usm = sec->data; 611 612 if (clock_gettime(CLOCK_MONOTONIC, &(usm->timecheck)) == -1) 613 return -1; 614 615 usm->boots = boots; 616 usm->bootsset = 1; 617 usm->time = time; 618 usm->timeset = 1; 619 return 0; 620 } 621 622 static char * 623 usm_passwd2mkey(const EVP_MD *md, const char *passwd) 624 { 625 EVP_MD_CTX *ctx; 626 int i, count; 627 const u_char *pw; 628 u_char *c; 629 u_char keybuf[EVP_MAX_MD_SIZE]; 630 unsigned dlen; 631 char *key; 632 633 if ((ctx = EVP_MD_CTX_new()) == NULL) 634 return NULL; 635 if (!EVP_DigestInit_ex(ctx, md, NULL)) { 636 EVP_MD_CTX_free(ctx); 637 return NULL; 638 } 639 640 pw = (const u_char *)passwd; 641 for (count = 0; count < 1048576; count += 64) { 642 c = keybuf; 643 for (i = 0; i < 64; i++) { 644 if (*pw == '\0') 645 pw = (const u_char *)passwd; 646 *c++ = *pw++; 647 } 648 if (!EVP_DigestUpdate(ctx, keybuf, 64)) { 649 EVP_MD_CTX_free(ctx); 650 return NULL; 651 } 652 } 653 if (!EVP_DigestFinal_ex(ctx, keybuf, &dlen)) { 654 EVP_MD_CTX_free(ctx); 655 return NULL; 656 } 657 EVP_MD_CTX_free(ctx); 658 659 if ((key = malloc(dlen)) == NULL) 660 return NULL; 661 memcpy(key, keybuf, dlen); 662 return key; 663 } 664 665 static char * 666 usm_mkey2lkey(struct usm_sec *usm, const EVP_MD *md, const char *mkey) 667 { 668 EVP_MD_CTX *ctx; 669 u_char buf[EVP_MAX_MD_SIZE]; 670 u_char *lkey; 671 unsigned lklen; 672 673 if ((ctx = EVP_MD_CTX_new()) == NULL) 674 return NULL; 675 676 if (!EVP_DigestInit_ex(ctx, md, NULL) || 677 !EVP_DigestUpdate(ctx, mkey, EVP_MD_size(md)) || 678 !EVP_DigestUpdate(ctx, usm->engineid, usm->engineidlen) || 679 !EVP_DigestUpdate(ctx, mkey, EVP_MD_size(md)) || 680 !EVP_DigestFinal_ex(ctx, buf, &lklen)) { 681 EVP_MD_CTX_free(ctx); 682 return NULL; 683 } 684 685 EVP_MD_CTX_free(ctx); 686 687 if ((lkey = malloc(lklen)) == NULL) 688 return NULL; 689 memcpy(lkey, buf, lklen); 690 return lkey; 691 } 692 693 static size_t 694 usm_digestlen(const EVP_MD *md) 695 { 696 switch (EVP_MD_type(md)) { 697 case NID_md5: 698 case NID_sha1: 699 return 12; 700 case NID_sha224: 701 return 16; 702 case NID_sha256: 703 return 24; 704 case NID_sha384: 705 return 32; 706 case NID_sha512: 707 return 48; 708 default: 709 return 0; 710 } 711 } 712