1 /* $OpenBSD: usm.c,v 1.17 2019/10/24 12:39:27 tb Exp $ */ 2 3 /* 4 * Copyright (c) 2012 GeNUA mbH 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/queue.h> 20 #include <sys/types.h> 21 #include <sys/stat.h> 22 #include <sys/socket.h> 23 #include <sys/un.h> 24 #include <sys/tree.h> 25 26 #include <net/if.h> 27 28 #include <errno.h> 29 #include <event.h> 30 #include <fcntl.h> 31 #include <stdlib.h> 32 #include <string.h> 33 #include <unistd.h> 34 #include <signal.h> 35 #ifdef DEBUG 36 #include <assert.h> 37 #endif 38 39 #include <openssl/evp.h> 40 #include <openssl/hmac.h> 41 42 #include "snmpd.h" 43 #include "mib.h" 44 45 SLIST_HEAD(, usmuser) usmuserlist; 46 47 const EVP_MD *usm_get_md(enum usmauth); 48 size_t usm_get_digestlen(enum usmauth); 49 const EVP_CIPHER *usm_get_cipher(enum usmpriv); 50 int usm_valid_digestlen(size_t digestlen); 51 void usm_cb_digest(void *, size_t); 52 int usm_valid_digest(struct snmp_message *, off_t, char *, 53 size_t); 54 struct ber_element *usm_decrypt(struct snmp_message *, 55 struct ber_element *); 56 ssize_t usm_crypt(struct snmp_message *, u_char *, int, 57 u_char *, int); 58 char *usm_passwd2key(const EVP_MD *, char *, int *); 59 60 void 61 usm_generate_keys(void) 62 { 63 struct usmuser *up; 64 const EVP_MD *md; 65 char *key; 66 int len; 67 68 SLIST_FOREACH(up, &usmuserlist, uu_next) { 69 if ((md = usm_get_md(up->uu_auth)) == NULL) 70 continue; 71 72 /* convert auth password to key */ 73 len = 0; 74 key = usm_passwd2key(md, up->uu_authkey, &len); 75 free(up->uu_authkey); 76 up->uu_authkey = key; 77 up->uu_authkeylen = len; 78 79 /* optionally convert privacy password to key */ 80 if (up->uu_priv != PRIV_NONE) { 81 arc4random_buf(&up->uu_salt, sizeof(up->uu_salt)); 82 83 len = SNMP_CIPHER_KEYLEN; 84 key = usm_passwd2key(md, up->uu_privkey, &len); 85 free(up->uu_privkey); 86 up->uu_privkey = key; 87 } 88 } 89 return; 90 } 91 92 const EVP_MD * 93 usm_get_md(enum usmauth ua) 94 { 95 switch (ua) { 96 case AUTH_MD5: 97 return EVP_md5(); 98 case AUTH_SHA1: 99 return EVP_sha1(); 100 case AUTH_SHA224: 101 return EVP_sha224(); 102 case AUTH_SHA256: 103 return EVP_sha256(); 104 case AUTH_SHA384: 105 return EVP_sha384(); 106 case AUTH_SHA512: 107 return EVP_sha512(); 108 case AUTH_NONE: 109 default: 110 return NULL; 111 } 112 } 113 114 size_t 115 usm_get_digestlen(enum usmauth ua) 116 { 117 switch (ua) { 118 case AUTH_MD5: 119 case AUTH_SHA1: 120 return 12; 121 case AUTH_SHA224: 122 return 16; 123 case AUTH_SHA256: 124 return 24; 125 case AUTH_SHA384: 126 return 32; 127 case AUTH_SHA512: 128 return 48; 129 case AUTH_NONE: 130 default: 131 return 0; 132 } 133 } 134 135 const EVP_CIPHER * 136 usm_get_cipher(enum usmpriv up) 137 { 138 switch (up) { 139 case PRIV_DES: 140 return EVP_des_cbc(); 141 case PRIV_AES: 142 return EVP_aes_128_cfb128(); 143 case PRIV_NONE: 144 default: 145 return NULL; 146 } 147 } 148 149 int 150 usm_valid_digestlen(size_t digestlen) 151 { 152 switch (digestlen) { 153 case 0: 154 case 12: 155 case 16: 156 case 24: 157 case 32: 158 case 48: 159 return 1; 160 default: 161 return 0; 162 } 163 } 164 165 struct usmuser * 166 usm_newuser(char *name, const char **errp) 167 { 168 struct usmuser *up = usm_finduser(name); 169 if (up != NULL) { 170 *errp = "user redefined"; 171 return NULL; 172 } 173 if ((up = calloc(1, sizeof(*up))) == NULL) 174 fatal("usm"); 175 up->uu_name = name; 176 SLIST_INSERT_HEAD(&usmuserlist, up, uu_next); 177 return up; 178 } 179 180 struct usmuser * 181 usm_finduser(char *name) 182 { 183 struct usmuser *up; 184 185 SLIST_FOREACH(up, &usmuserlist, uu_next) { 186 if (!strcmp(up->uu_name, name)) 187 return up; 188 } 189 return NULL; 190 } 191 192 int 193 usm_checkuser(struct usmuser *up, const char **errp) 194 { 195 char *auth = NULL, *priv = NULL; 196 197 if (up->uu_auth != AUTH_NONE && up->uu_authkey == NULL) { 198 *errp = "missing auth passphrase"; 199 goto fail; 200 } else if (up->uu_auth == AUTH_NONE && up->uu_authkey != NULL) 201 up->uu_auth = AUTH_DEFAULT; 202 203 if (up->uu_priv != PRIV_NONE && up->uu_privkey == NULL) { 204 *errp = "missing priv passphrase"; 205 goto fail; 206 } else if (up->uu_priv == PRIV_NONE && up->uu_privkey != NULL) 207 up->uu_priv = PRIV_DEFAULT; 208 209 if (up->uu_auth == AUTH_NONE && up->uu_priv != PRIV_NONE) { 210 /* Standard prohibits noAuthPriv */ 211 *errp = "auth is mandatory with enc"; 212 goto fail; 213 } 214 215 switch (up->uu_auth) { 216 case AUTH_NONE: 217 auth = "none"; 218 break; 219 case AUTH_MD5: 220 up->uu_seclevel |= SNMP_MSGFLAG_AUTH; 221 auth = "HMAC-MD5-96"; 222 break; 223 case AUTH_SHA1: 224 up->uu_seclevel |= SNMP_MSGFLAG_AUTH; 225 auth = "HMAC-SHA1-96"; 226 break; 227 case AUTH_SHA224: 228 up->uu_seclevel |= SNMP_MSGFLAG_AUTH; 229 auth = "usmHMAC128SHA224AuthProtocol"; 230 case AUTH_SHA256: 231 up->uu_seclevel |= SNMP_MSGFLAG_AUTH; 232 auth = "usmHMAC192SHA256AuthProtocol"; 233 case AUTH_SHA384: 234 up->uu_seclevel |= SNMP_MSGFLAG_AUTH; 235 auth = "usmHMAC256SHA384AuthProtocol"; 236 case AUTH_SHA512: 237 up->uu_seclevel |= SNMP_MSGFLAG_AUTH; 238 auth = "usmHMAC384SHA512AuthProtocol"; 239 } 240 241 switch (up->uu_priv) { 242 case PRIV_NONE: 243 priv = "none"; 244 break; 245 case PRIV_DES: 246 up->uu_seclevel |= SNMP_MSGFLAG_PRIV; 247 priv = "CBC-DES"; 248 break; 249 case PRIV_AES: 250 up->uu_seclevel |= SNMP_MSGFLAG_PRIV; 251 priv = "CFB128-AES-128"; 252 break; 253 } 254 255 log_debug("user \"%s\" auth %s enc %s", up->uu_name, auth, priv); 256 return 0; 257 258 fail: 259 free(up->uu_name); 260 free(up->uu_authkey); 261 free(up->uu_privkey); 262 SLIST_REMOVE(&usmuserlist, up, usmuser, uu_next); 263 free(up); 264 return -1; 265 } 266 267 struct ber_element * 268 usm_decode(struct snmp_message *msg, struct ber_element *elm, const char **errp) 269 { 270 struct snmp_stats *stats = &snmpd_env->sc_stats; 271 off_t offs, offs2; 272 char *usmparams; 273 size_t len; 274 size_t enginelen, userlen, digestlen, saltlen; 275 struct ber ber; 276 struct ber_element *usm = NULL, *next = NULL, *decr; 277 char *engineid; 278 char *user; 279 char *digest, *salt; 280 u_long now; 281 long long engine_boots, engine_time; 282 283 bzero(&ber, sizeof(ber)); 284 offs = ober_getpos(elm); 285 286 if (ober_get_nstring(elm, (void *)&usmparams, &len) < 0) { 287 *errp = "cannot decode security params"; 288 msg->sm_flags &= SNMP_MSGFLAG_REPORT; 289 goto done; 290 } 291 292 ober_set_readbuf(&ber, usmparams, len); 293 usm = ober_read_elements(&ber, NULL); 294 if (usm == NULL) { 295 *errp = "cannot decode security params"; 296 msg->sm_flags &= SNMP_MSGFLAG_REPORT; 297 goto done; 298 } 299 300 #ifdef DEBUG 301 fprintf(stderr, "decode USM parameters:\n"); 302 smi_debug_elements(usm); 303 #endif 304 305 if (ober_scanf_elements(usm, "{xiixpxx", &engineid, &enginelen, 306 &engine_boots, &engine_time, &user, &userlen, &offs2, 307 &digest, &digestlen, &salt, &saltlen) != 0) { 308 *errp = "cannot decode USM params"; 309 msg->sm_flags &= SNMP_MSGFLAG_REPORT; 310 goto done; 311 } 312 313 log_debug("USM: engineid '%s', engine boots %lld, engine time %lld, " 314 "user '%s'", tohexstr(engineid, enginelen), engine_boots, 315 engine_time, user); 316 317 if (enginelen > SNMPD_MAXENGINEIDLEN || 318 userlen > SNMPD_MAXUSERNAMELEN || 319 !usm_valid_digestlen(digestlen) || 320 (saltlen != (MSG_HAS_PRIV(msg) ? SNMP_USM_SALTLEN : 0))) { 321 *errp = "bad field length"; 322 msg->sm_flags &= SNMP_MSGFLAG_REPORT; 323 goto done; 324 } 325 326 if (enginelen != snmpd_env->sc_engineid_len || 327 memcmp(engineid, snmpd_env->sc_engineid, enginelen) != 0) { 328 *errp = "unknown engine id"; 329 msg->sm_usmerr = OIDVAL_usmErrEngineId; 330 stats->snmp_usmnosuchengine++; 331 msg->sm_flags &= SNMP_MSGFLAG_REPORT; 332 goto done; 333 } 334 335 msg->sm_engine_boots = (u_int32_t)engine_boots; 336 msg->sm_engine_time = (u_int32_t)engine_time; 337 338 memcpy(msg->sm_username, user, userlen); 339 msg->sm_username[userlen] = '\0'; 340 msg->sm_user = usm_finduser(msg->sm_username); 341 if (msg->sm_user == NULL) { 342 *errp = "no such user"; 343 msg->sm_usmerr = OIDVAL_usmErrUserName; 344 stats->snmp_usmnosuchuser++; 345 msg->sm_flags &= SNMP_MSGFLAG_REPORT; 346 goto done; 347 } 348 if (MSG_SECLEVEL(msg) > msg->sm_user->uu_seclevel) { 349 *errp = "unsupported security model"; 350 msg->sm_usmerr = OIDVAL_usmErrSecLevel; 351 stats->snmp_usmbadseclevel++; 352 msg->sm_flags &= SNMP_MSGFLAG_REPORT; 353 goto done; 354 } 355 356 /* 357 * offs is the offset of the USM string within the serialized msg 358 * and offs2 the offset of the digest within the USM string. 359 */ 360 if (!usm_valid_digest(msg, offs + offs2, digest, digestlen)) { 361 *errp = "bad msg digest"; 362 msg->sm_usmerr = OIDVAL_usmErrDigest; 363 stats->snmp_usmwrongdigest++; 364 msg->sm_flags &= SNMP_MSGFLAG_REPORT; 365 goto done; 366 } 367 368 if (MSG_HAS_PRIV(msg)) { 369 memcpy(msg->sm_salt, salt, saltlen); 370 if ((decr = usm_decrypt(msg, elm->be_next)) == NULL) { 371 *errp = "cannot decrypt msg"; 372 msg->sm_usmerr = OIDVAL_usmErrDecrypt; 373 stats->snmp_usmdecrypterr++; 374 msg->sm_flags &= SNMP_MSGFLAG_REPORT; 375 goto done; 376 } 377 ober_replace_elements(elm, decr); 378 } 379 380 now = snmpd_engine_time(); 381 if (engine_boots != snmpd_env->sc_engine_boots || 382 engine_time < (long long)(now - SNMP_MAX_TIMEWINDOW) || 383 engine_time > (long long)(now + SNMP_MAX_TIMEWINDOW)) { 384 *errp = "out of time window"; 385 msg->sm_usmerr = OIDVAL_usmErrTimeWindow; 386 stats->snmp_usmtimewindow++; 387 goto done; 388 } 389 390 next = elm->be_next; 391 392 done: 393 ober_free(&ber); 394 if (usm != NULL) 395 ober_free_elements(usm); 396 return next; 397 } 398 399 struct ber_element * 400 usm_encode(struct snmp_message *msg, struct ber_element *e) 401 { 402 struct ber ber; 403 struct ber_element *usm, *a, *res = NULL; 404 void *ptr; 405 char digest[SNMP_USM_MAXDIGESTLEN]; 406 size_t digestlen, saltlen; 407 ssize_t len; 408 409 msg->sm_digest_offs = 0; 410 bzero(&ber, sizeof(ber)); 411 412 usm = ober_add_sequence(NULL); 413 414 if (MSG_HAS_AUTH(msg)) { 415 /* 416 * Fill in enough zeroes and remember the position within the 417 * messages. The digest will be calculated once the message 418 * is complete. 419 */ 420 #ifdef DEBUG 421 assert(msg->sm_user != NULL); 422 #endif 423 bzero(digest, sizeof(digest)); 424 digestlen = usm_get_digestlen(msg->sm_user->uu_auth); 425 } else 426 digestlen = 0; 427 428 if (MSG_HAS_PRIV(msg)) { 429 #ifdef DEBUG 430 assert(msg->sm_user != NULL); 431 #endif 432 ++(msg->sm_user->uu_salt); 433 memcpy(msg->sm_salt, &msg->sm_user->uu_salt, 434 sizeof(msg->sm_salt)); 435 saltlen = sizeof(msg->sm_salt); 436 } else 437 saltlen = 0; 438 439 msg->sm_engine_boots = (u_int32_t)snmpd_env->sc_engine_boots; 440 msg->sm_engine_time = (u_int32_t)snmpd_engine_time(); 441 if ((a = ober_printf_elements(usm, "xdds", 442 snmpd_env->sc_engineid, snmpd_env->sc_engineid_len, 443 msg->sm_engine_boots, msg->sm_engine_time, 444 msg->sm_username)) == NULL) 445 goto done; 446 447 if ((a = ober_add_nstring(a, digest, digestlen)) == NULL) 448 goto done; 449 if (digestlen > 0) 450 ober_set_writecallback(a, usm_cb_digest, msg); 451 452 if ((a = ober_add_nstring(a, msg->sm_salt, saltlen)) == NULL) 453 goto done; 454 455 #ifdef DEBUG 456 fprintf(stderr, "encode USM parameters:\n"); 457 smi_debug_elements(usm); 458 #endif 459 len = ober_write_elements(&ber, usm); 460 if (ober_get_writebuf(&ber, &ptr) > 0) { 461 res = ober_add_nstring(e, (char *)ptr, len); 462 if (digestlen > 0) 463 ober_set_writecallback(res, usm_cb_digest, msg); 464 } 465 466 done: 467 ober_free(&ber); 468 ober_free_elements(usm); 469 return res; 470 } 471 472 void 473 usm_cb_digest(void *arg, size_t offs) 474 { 475 struct snmp_message *msg = arg; 476 msg->sm_digest_offs += offs; 477 } 478 479 struct ber_element * 480 usm_encrypt(struct snmp_message *msg, struct ber_element *pdu) 481 { 482 struct ber ber; 483 struct ber_element *encrpdu = NULL; 484 void *ptr; 485 ssize_t elen, len; 486 u_char encbuf[READ_BUF_SIZE]; 487 488 if (!MSG_HAS_PRIV(msg)) 489 return pdu; 490 491 bzero(&ber, sizeof(ber)); 492 493 #ifdef DEBUG 494 fprintf(stderr, "encrypted PDU:\n"); 495 smi_debug_elements(pdu); 496 #endif 497 498 len = ober_write_elements(&ber, pdu); 499 if (ober_get_writebuf(&ber, &ptr) > 0) { 500 elen = usm_crypt(msg, ptr, len, encbuf, 1); 501 if (elen > 0) 502 encrpdu = ober_add_nstring(NULL, (char *)encbuf, elen); 503 } 504 505 ober_free(&ber); 506 ober_free_elements(pdu); 507 return encrpdu; 508 } 509 510 /* 511 * Calculate message digest and replace within message 512 */ 513 void 514 usm_finalize_digest(struct snmp_message *msg, char *buf, ssize_t len) 515 { 516 const EVP_MD *md; 517 u_char digest[EVP_MAX_MD_SIZE]; 518 size_t digestlen; 519 unsigned hlen; 520 521 if (msg->sm_resp == NULL || 522 !MSG_HAS_AUTH(msg) || 523 msg->sm_user == NULL || 524 msg->sm_digest_offs == 0 || 525 len <= 0) 526 return; 527 528 if ((digestlen = usm_get_digestlen(msg->sm_user->uu_auth)) == 0) 529 return; 530 bzero(digest, digestlen); 531 #ifdef DEBUG 532 assert(msg->sm_digest_offs + digestlen <= (size_t)len); 533 assert(!memcmp(buf + msg->sm_digest_offs, digest, digestlen)); 534 #endif 535 536 if ((md = usm_get_md(msg->sm_user->uu_auth)) == NULL) 537 return; 538 539 HMAC(md, msg->sm_user->uu_authkey, (int)msg->sm_user->uu_authkeylen, 540 (u_char*)buf, (size_t)len, digest, &hlen); 541 542 memcpy(buf + msg->sm_digest_offs, digest, digestlen); 543 return; 544 } 545 546 void 547 usm_make_report(struct snmp_message *msg) 548 { 549 struct ber_oid usmstat = OID(MIB_usmStats, 0, 0); 550 551 msg->sm_context = SNMP_C_REPORT; 552 usmstat.bo_id[OIDIDX_usmStats] = msg->sm_usmerr; 553 usmstat.bo_n = OIDIDX_usmStats + 2; 554 if (msg->sm_varbindresp != NULL) 555 ober_free_elements(msg->sm_varbindresp); 556 msg->sm_varbindresp = ober_add_sequence(NULL); 557 mps_getreq(NULL, msg->sm_varbindresp, &usmstat, msg->sm_version); 558 return; 559 } 560 561 int 562 usm_valid_digest(struct snmp_message *msg, off_t offs, 563 char *digest, size_t digestlen) 564 { 565 const EVP_MD *md; 566 u_char exp_digest[EVP_MAX_MD_SIZE]; 567 unsigned hlen; 568 569 if (!MSG_HAS_AUTH(msg)) 570 return 1; 571 572 if (digestlen != usm_get_digestlen(msg->sm_user->uu_auth)) 573 return 0; 574 575 #ifdef DEBUG 576 assert(offs + digestlen <= msg->sm_datalen); 577 assert(bcmp(&msg->sm_data[offs], digest, digestlen) == 0); 578 #endif 579 580 if ((md = usm_get_md(msg->sm_user->uu_auth)) == NULL) 581 return 0; 582 583 memset(&msg->sm_data[offs], 0, digestlen); 584 HMAC(md, msg->sm_user->uu_authkey, (int)msg->sm_user->uu_authkeylen, 585 msg->sm_data, msg->sm_datalen, exp_digest, &hlen); 586 /* we don't bother to restore the original message */ 587 588 if (hlen < digestlen) 589 return 0; 590 591 return memcmp(digest, exp_digest, digestlen) == 0; 592 } 593 594 struct ber_element * 595 usm_decrypt(struct snmp_message *msg, struct ber_element *encr) 596 { 597 u_char *privstr; 598 size_t privlen; 599 u_char buf[READ_BUF_SIZE]; 600 struct ber ber; 601 struct ber_element *scoped_pdu = NULL; 602 ssize_t scoped_pdu_len; 603 604 if (ober_get_nstring(encr, (void *)&privstr, &privlen) < 0) 605 return NULL; 606 607 scoped_pdu_len = usm_crypt(msg, privstr, (int)privlen, buf, 0); 608 if (scoped_pdu_len < 0) 609 return NULL; 610 611 bzero(&ber, sizeof(ber)); 612 ober_set_readbuf(&ber, buf, scoped_pdu_len); 613 scoped_pdu = ober_read_elements(&ber, NULL); 614 615 #ifdef DEBUG 616 if (scoped_pdu != NULL) { 617 fprintf(stderr, "decrypted scoped PDU:\n"); 618 smi_debug_elements(scoped_pdu); 619 } 620 #endif 621 622 ober_free(&ber); 623 return scoped_pdu; 624 } 625 626 ssize_t 627 usm_crypt(struct snmp_message *msg, u_char *inbuf, int inlen, u_char *outbuf, 628 int do_encrypt) 629 { 630 const EVP_CIPHER *cipher; 631 EVP_CIPHER_CTX ctx; 632 u_char *privkey; 633 int i; 634 u_char iv[EVP_MAX_IV_LENGTH]; 635 int len, len2; 636 int rv; 637 u_int32_t ivv; 638 639 if ((cipher = usm_get_cipher(msg->sm_user->uu_priv)) == NULL) 640 return -1; 641 642 privkey = (u_char *)msg->sm_user->uu_privkey; 643 #ifdef DEBUG 644 assert(privkey != NULL); 645 #endif 646 switch (msg->sm_user->uu_priv) { 647 case PRIV_DES: 648 /* RFC3414, chap 8.1.1.1. */ 649 for (i = 0; i < 8; i++) 650 iv[i] = msg->sm_salt[i] ^ privkey[SNMP_USM_SALTLEN + i]; 651 break; 652 case PRIV_AES: 653 /* RFC3826, chap 3.1.2.1. */ 654 ivv = htobe32(msg->sm_engine_boots); 655 memcpy(iv, &ivv, sizeof(ivv)); 656 ivv = htobe32(msg->sm_engine_time); 657 memcpy(iv + sizeof(ivv), &ivv, sizeof(ivv)); 658 memcpy(iv + 2 * sizeof(ivv), msg->sm_salt, SNMP_USM_SALTLEN); 659 break; 660 default: 661 return -1; 662 } 663 664 if (!EVP_CipherInit(&ctx, cipher, privkey, iv, do_encrypt)) 665 return -1; 666 667 if (!do_encrypt) 668 EVP_CIPHER_CTX_set_padding(&ctx, 0); 669 670 if (EVP_CipherUpdate(&ctx, outbuf, &len, inbuf, inlen) && 671 EVP_CipherFinal_ex(&ctx, outbuf + len, &len2)) 672 rv = len + len2; 673 else 674 rv = -1; 675 676 EVP_CIPHER_CTX_cleanup(&ctx); 677 return rv; 678 } 679 680 /* 681 * RFC3414, Password to Key Algorithm 682 */ 683 char * 684 usm_passwd2key(const EVP_MD *md, char *passwd, int *maxlen) 685 { 686 EVP_MD_CTX ctx; 687 int i, count; 688 u_char *pw, *c; 689 u_char pwbuf[2 * EVP_MAX_MD_SIZE + SNMPD_MAXENGINEIDLEN]; 690 u_char keybuf[EVP_MAX_MD_SIZE]; 691 unsigned dlen; 692 char *key; 693 694 EVP_DigestInit(&ctx, md); 695 pw = (u_char *)passwd; 696 for (count = 0; count < 1048576; count += 64) { 697 c = pwbuf; 698 for (i = 0; i < 64; i++) { 699 if (*pw == '\0') 700 pw = (u_char *)passwd; 701 *c++ = *pw++; 702 } 703 EVP_DigestUpdate(&ctx, pwbuf, 64); 704 } 705 EVP_DigestFinal(&ctx, keybuf, &dlen); 706 EVP_MD_CTX_cleanup(&ctx); 707 708 /* Localize the key */ 709 #ifdef DEBUG 710 assert(snmpd_env->sc_engineid_len <= SNMPD_MAXENGINEIDLEN); 711 #endif 712 memcpy(pwbuf, keybuf, dlen); 713 memcpy(pwbuf + dlen, snmpd_env->sc_engineid, 714 snmpd_env->sc_engineid_len); 715 memcpy(pwbuf + dlen + snmpd_env->sc_engineid_len, keybuf, dlen); 716 717 EVP_DigestInit(&ctx, md); 718 EVP_DigestUpdate(&ctx, pwbuf, 2 * dlen + snmpd_env->sc_engineid_len); 719 EVP_DigestFinal(&ctx, keybuf, &dlen); 720 EVP_MD_CTX_cleanup(&ctx); 721 722 if (*maxlen > 0 && dlen > (unsigned)*maxlen) 723 dlen = (unsigned)*maxlen; 724 if ((key = malloc(dlen)) == NULL) 725 fatal("key"); 726 memcpy(key, keybuf, dlen); 727 *maxlen = (int)dlen; 728 return key; 729 } 730