1 /* $OpenBSD: usm.c,v 1.5 2019/10/24 12:39:26 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 bzero(&ctx, sizeof(ctx)); 283 if (!EVP_CipherInit(&ctx, cipher, key, iv, do_enc)) 284 return NULL; 285 286 EVP_CIPHER_CTX_set_padding(&ctx, do_enc); 287 288 bs = EVP_CIPHER_block_size(cipher); 289 /* Maximum output size */ 290 *outlen = pdulen + (bs - (pdulen % bs)); 291 292 if ((outtext = malloc(*outlen)) == NULL) 293 return NULL; 294 295 if (EVP_CipherUpdate(&ctx, outtext, &len, serialpdu, pdulen) && 296 EVP_CipherFinal_ex(&ctx, outtext + len, &len2)) 297 *outlen = len + len2; 298 else { 299 free(outtext); 300 outtext = NULL; 301 } 302 303 EVP_CIPHER_CTX_cleanup(&ctx); 304 305 return outtext; 306 } 307 308 static int 309 usm_finalparams(struct snmp_agent *agent, char *buf, size_t buflen, 310 size_t secparamsoffset, void *cookie) 311 { 312 struct usm_sec *usm = agent->v3->sec->data; 313 struct usm_cookie *usmcookie = cookie; 314 u_char digest[EVP_MAX_MD_SIZE]; 315 316 if ((agent->v3->level & SNMP_MSGFLAG_AUTH) == 0) 317 return 0; 318 319 if (usm->authlevel != USM_KEY_LOCALIZED) 320 return -1; 321 322 if (HMAC(usm->digest, usm->authkey, EVP_MD_size(usm->digest), buf, 323 buflen, digest, NULL) == NULL) 324 return -1; 325 326 memcpy(buf + secparamsoffset + usmcookie->digestoffset, digest, 327 usm_digestlen(usm->digest)); 328 return 0; 329 } 330 331 static int 332 usm_parseparams(struct snmp_agent *agent, char *packet, size_t packetlen, 333 off_t secparamsoffset, char *buf, size_t buflen, uint8_t level, 334 void **cookie) 335 { 336 struct usm_sec *usm = agent->v3->sec->data; 337 struct ber ber; 338 struct ber_element *secparams; 339 char *engineid, *user, *digest, *salt; 340 size_t engineidlen, userlen, digestlen, saltlen; 341 struct timespec now, timediff; 342 off_t digestoffset; 343 char exp_digest[EVP_MAX_MD_SIZE]; 344 struct usm_cookie *usmcookie; 345 346 bzero(&ber, sizeof(ber)); 347 bzero(exp_digest, sizeof(exp_digest)); 348 349 ober_set_application(&ber, smi_application); 350 ober_set_readbuf(&ber, buf, buflen); 351 if ((secparams = ober_read_elements(&ber, NULL)) == NULL) 352 return -1; 353 ober_free(&ber); 354 355 if ((usmcookie = malloc(sizeof(*usmcookie))) == NULL) 356 goto fail; 357 *cookie = usmcookie; 358 359 if (ober_scanf_elements(secparams, "{xddxpxx}", &engineid, &engineidlen, 360 &(usmcookie->boots), &(usmcookie->time), &user, &userlen, 361 &digestoffset, &digest, &digestlen, &salt, &saltlen) == -1) 362 goto fail; 363 if (saltlen != sizeof(usmcookie->salt) && saltlen != 0) 364 goto fail; 365 memcpy(&(usmcookie->salt), salt, saltlen); 366 367 if (!usm->engineidset) { 368 if (usm_setengineid(agent->v3->sec, engineid, 369 engineidlen) == -1) 370 goto fail; 371 } else { 372 if (usm->engineidlen != engineidlen) 373 goto fail; 374 if (memcmp(usm->engineid, engineid, engineidlen) != 0) 375 goto fail; 376 } 377 378 if (!usm->bootsset) { 379 usm->boots = usmcookie->boots; 380 usm->bootsset = 1; 381 } else { 382 if (usmcookie->boots < usm->boots) 383 goto fail; 384 if (usmcookie->boots > usm->boots) { 385 usm->bootsset = 0; 386 usm->timeset = 0; 387 usm_doinit(agent); 388 goto fail; 389 } 390 } 391 392 if (!usm->timeset) { 393 usm->time = usmcookie->time; 394 if (clock_gettime(CLOCK_MONOTONIC, &usm->timecheck) == -1) 395 goto fail; 396 usm->timeset = 1; 397 } else { 398 if (clock_gettime(CLOCK_MONOTONIC, &now) == -1) 399 goto fail; 400 timespecsub(&now, &(usm->timecheck), &timediff); 401 if (usmcookie->time < 402 usm->time + timediff.tv_sec - USM_MAX_TIMEWINDOW || 403 usmcookie->time > 404 usm->time + timediff.tv_sec + USM_MAX_TIMEWINDOW) { 405 usm->bootsset = 0; 406 usm->timeset = 0; 407 usm_doinit(agent); 408 goto fail; 409 } 410 } 411 /* 412 * Don't assume these are set if both are zero. 413 * Ugly hack for HP Laserjet 414 */ 415 if (usm->boots == 0 && usm->time == 0) { 416 usm->bootsset = 0; 417 usm->timeset = 0; 418 } 419 420 if (userlen != usm->userlen || 421 memcmp(user, usm->user, userlen) != 0) 422 goto fail; 423 424 if (level & SNMP_MSGFLAG_AUTH) { 425 if (digestlen != usm_digestlen(usm->digest)) 426 goto fail; 427 } 428 if ((agent->v3->level & SNMP_MSGFLAG_AUTH)) { 429 bzero(packet + secparamsoffset + digestoffset, digestlen); 430 if (HMAC(usm->digest, usm->authkey, EVP_MD_size(usm->digest), packet, 431 packetlen, exp_digest, NULL) == NULL) 432 goto fail; 433 434 if (memcmp(exp_digest, digest, digestlen) != 0) 435 goto fail; 436 } else 437 if (digestlen != 0) 438 goto fail; 439 440 ober_free_element(secparams); 441 return 0; 442 443 fail: 444 free(usmcookie); 445 ober_free_element(secparams); 446 return -1; 447 } 448 449 struct ber_element * 450 usm_decpdu(struct snmp_agent *agent, char *encpdu, size_t encpdulen, void *cookie) 451 { 452 struct usm_sec *usm = agent->v3->sec->data; 453 struct usm_cookie *usmcookie = cookie; 454 struct ber ber; 455 struct ber_element *scopedpdu; 456 char *rawpdu; 457 size_t rawpdulen; 458 459 if ((rawpdu = usm_crypt(usm->cipher, 0, usm->privkey, usmcookie, 460 encpdu, encpdulen, &rawpdulen)) == NULL) 461 return NULL; 462 463 bzero(&ber, sizeof(ber)); 464 ober_set_application(&ber, smi_application); 465 ober_set_readbuf(&ber, rawpdu, rawpdulen); 466 scopedpdu = ober_read_elements(&ber, NULL); 467 ober_free(&ber); 468 free(rawpdu); 469 470 return scopedpdu; 471 } 472 473 static void 474 usm_digest_pos(void *data, size_t offset) 475 { 476 struct usm_cookie *usmcookie = data; 477 478 usmcookie->digestoffset = offset; 479 } 480 481 static void 482 usm_free(void *data) 483 { 484 struct usm_sec *usm = data; 485 486 free(usm->user); 487 free(usm->authkey); 488 free(usm->privkey); 489 free(usm->engineid); 490 free(usm); 491 } 492 493 int 494 usm_setauth(struct snmp_sec *sec, const EVP_MD *digest, const char *key, 495 size_t keylen, enum usm_key_level level) 496 { 497 struct usm_sec *usm = sec->data; 498 char *lkey; 499 500 /* 501 * We could transform a master key to a local key here if we already 502 * have usm_setengineid called. Sine snmpc.c is the only caller at 503 * the moment there's no need, since it always calls this function 504 * first. 505 */ 506 if (level == USM_KEY_PASSWORD) { 507 if ((usm->authkey = usm_passwd2mkey(digest, key)) == NULL) 508 return -1; 509 level = USM_KEY_MASTER; 510 keylen = EVP_MD_size(digest); 511 } else { 512 if (keylen != (size_t)EVP_MD_size(digest)) { 513 errno = EINVAL; 514 return -1; 515 } 516 if ((lkey = malloc(keylen)) == NULL) 517 return -1; 518 memcpy(lkey, key, keylen); 519 usm->authkey = lkey; 520 } 521 usm->digest = digest; 522 usm->authlevel = level; 523 return 0; 524 } 525 526 int 527 usm_setpriv(struct snmp_sec *sec, const EVP_CIPHER *cipher, const char *key, 528 size_t keylen, enum usm_key_level level) 529 { 530 struct usm_sec *usm = sec->data; 531 char *lkey; 532 533 if (usm->digest == NULL) { 534 errno = EINVAL; 535 return -1; 536 } 537 538 /* 539 * We could transform a master key to a local key here if we already 540 * have usm_setengineid called. Sine snmpc.c is the only caller at 541 * the moment there's no need, since it always calls us first. 542 */ 543 if (level == USM_KEY_PASSWORD) { 544 if ((usm->privkey = usm_passwd2mkey(usm->digest, key)) == NULL) 545 return -1; 546 level = USM_KEY_MASTER; 547 keylen = EVP_MD_size(usm->digest); 548 } else { 549 if (keylen != (size_t)EVP_MD_size(usm->digest)) { 550 errno = EINVAL; 551 return -1; 552 } 553 if ((lkey = malloc(keylen)) == NULL) 554 return -1; 555 memcpy(lkey, key, keylen); 556 usm->privkey = lkey; 557 } 558 usm->cipher = cipher; 559 usm->privlevel = level; 560 return 0; 561 } 562 563 int 564 usm_setengineid(struct snmp_sec *sec, char *engineid, size_t engineidlen) 565 { 566 struct usm_sec *usm = sec->data; 567 char *mkey; 568 569 if (usm->engineid != NULL) 570 free(usm->engineid); 571 if ((usm->engineid = malloc(engineidlen)) == NULL) 572 return -1; 573 memcpy(usm->engineid, engineid, engineidlen); 574 usm->engineidlen = engineidlen; 575 usm->engineidset = 1; 576 577 if (usm->authlevel == USM_KEY_MASTER) { 578 mkey = usm->authkey; 579 if ((usm->authkey = usm_mkey2lkey(usm, usm->digest, 580 mkey)) == NULL) { 581 usm->authkey = mkey; 582 return -1; 583 } 584 free(mkey); 585 usm->authlevel = USM_KEY_LOCALIZED; 586 } 587 if (usm->privlevel == USM_KEY_MASTER) { 588 mkey = usm->privkey; 589 if ((usm->privkey = usm_mkey2lkey(usm, usm->digest, 590 mkey)) == NULL) { 591 usm->privkey = mkey; 592 return -1; 593 } 594 free(mkey); 595 usm->privlevel = USM_KEY_LOCALIZED; 596 } 597 598 return 0; 599 } 600 601 int 602 usm_setbootstime(struct snmp_sec *sec, uint32_t boots, uint32_t time) 603 { 604 struct usm_sec *usm = sec->data; 605 606 if (clock_gettime(CLOCK_MONOTONIC, &(usm->timecheck)) == -1) 607 return -1; 608 609 usm->boots = boots; 610 usm->bootsset = 1; 611 usm->time = time; 612 usm->timeset = 1; 613 return 0; 614 } 615 616 static char * 617 usm_passwd2mkey(const EVP_MD *md, const char *passwd) 618 { 619 EVP_MD_CTX ctx; 620 int i, count; 621 const u_char *pw; 622 u_char *c; 623 u_char keybuf[EVP_MAX_MD_SIZE]; 624 unsigned dlen; 625 char *key; 626 627 bzero(&ctx, sizeof(ctx)); 628 EVP_DigestInit_ex(&ctx, md, NULL); 629 pw = (const u_char *)passwd; 630 for (count = 0; count < 1048576; count += 64) { 631 c = keybuf; 632 for (i = 0; i < 64; i++) { 633 if (*pw == '\0') 634 pw = (const u_char *)passwd; 635 *c++ = *pw++; 636 } 637 EVP_DigestUpdate(&ctx, keybuf, 64); 638 } 639 EVP_DigestFinal_ex(&ctx, keybuf, &dlen); 640 EVP_MD_CTX_cleanup(&ctx); 641 642 if ((key = malloc(dlen)) == NULL) 643 return NULL; 644 memcpy(key, keybuf, dlen); 645 return key; 646 } 647 648 static char * 649 usm_mkey2lkey(struct usm_sec *usm, const EVP_MD *md, const char *mkey) 650 { 651 EVP_MD_CTX ctx; 652 u_char buf[EVP_MAX_MD_SIZE]; 653 u_char *lkey; 654 unsigned lklen; 655 656 bzero(&ctx, sizeof(ctx)); 657 EVP_DigestInit_ex(&ctx, md, NULL); 658 659 EVP_DigestUpdate(&ctx, mkey, EVP_MD_size(md)); 660 EVP_DigestUpdate(&ctx, usm->engineid, usm->engineidlen); 661 EVP_DigestUpdate(&ctx, mkey, EVP_MD_size(md)); 662 663 EVP_DigestFinal_ex(&ctx, buf, &lklen); 664 EVP_MD_CTX_cleanup(&ctx); 665 666 if ((lkey = malloc(lklen)) == NULL) 667 return NULL; 668 memcpy(lkey, buf, lklen); 669 return lkey; 670 } 671 672 static size_t 673 usm_digestlen(const EVP_MD *md) 674 { 675 switch (EVP_MD_type(md)) { 676 case NID_md5: 677 case NID_sha1: 678 return 12; 679 case NID_sha224: 680 return 16; 681 case NID_sha256: 682 return 24; 683 case NID_sha384: 684 return 32; 685 case NID_sha512: 686 return 48; 687 default: 688 return 0; 689 } 690 } 691