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