xref: /openbsd-src/usr.sbin/snmpd/usm.c (revision f2da64fbbbf1b03f09f390ab01267c93dfd77c4c)
1 /*	$OpenBSD: usm.c,v 1.9 2015/01/16 00:05:13 deraadt 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 extern struct snmpd	*env;
46 
47 SLIST_HEAD(, usmuser)	usmuserlist;
48 
49 const EVP_MD		*usm_get_md(enum usmauth);
50 const EVP_CIPHER	*usm_get_cipher(enum usmpriv);
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_NONE:
101 	default:
102 		return NULL;
103 	}
104 }
105 
106 const EVP_CIPHER *
107 usm_get_cipher(enum usmpriv up)
108 {
109 	switch (up) {
110 	case PRIV_DES:
111 		return EVP_des_cbc();
112 	case PRIV_AES:
113 		return EVP_aes_128_cfb128();
114 	case PRIV_NONE:
115 	default:
116 		return NULL;
117 	}
118 }
119 
120 struct usmuser *
121 usm_newuser(char *name, const char **errp)
122 {
123 	struct usmuser *up = usm_finduser(name);
124 	if (up != NULL) {
125 		*errp = "user redefined";
126 		return NULL;
127 	}
128 	if ((up = calloc(1, sizeof(*up))) == NULL)
129 		fatal("usm");
130 	up->uu_name = name;
131 	SLIST_INSERT_HEAD(&usmuserlist, up, uu_next);
132 	return up;
133 }
134 
135 struct usmuser *
136 usm_finduser(char *name)
137 {
138 	struct usmuser *up;
139 
140 	SLIST_FOREACH(up, &usmuserlist, uu_next) {
141 		if (!strcmp(up->uu_name, name))
142 			return up;
143 	}
144 	return NULL;
145 }
146 
147 int
148 usm_checkuser(struct usmuser *up, const char **errp)
149 {
150 	char	*auth = NULL, *priv = NULL;
151 
152 	if (up->uu_auth != AUTH_NONE && up->uu_authkey == NULL) {
153 		*errp = "missing auth passphrase";
154 		goto fail;
155 	} else if (up->uu_auth == AUTH_NONE && up->uu_authkey != NULL)
156 		up->uu_auth = AUTH_DEFAULT;
157 
158 	if (up->uu_priv != PRIV_NONE && up->uu_privkey == NULL) {
159 		*errp = "missing priv passphrase";
160 		goto fail;
161 	} else if (up->uu_priv == PRIV_NONE && up->uu_privkey != NULL)
162 		up->uu_priv = PRIV_DEFAULT;
163 
164 	if (up->uu_auth == AUTH_NONE && up->uu_priv != PRIV_NONE) {
165 		/* Standard prohibits noAuthPriv */
166 		*errp = "auth is mandatory with enc";
167 		goto fail;
168 	}
169 
170 	switch (up->uu_auth) {
171 	case AUTH_NONE:
172 		auth = "none";
173 		break;
174 	case AUTH_MD5:
175 		up->uu_seclevel |= SNMP_MSGFLAG_AUTH;
176 		auth = "HMAC-MD5-96";
177 		break;
178 	case AUTH_SHA1:
179 		up->uu_seclevel |= SNMP_MSGFLAG_AUTH;
180 		auth = "HMAC-SHA1-96";
181 		break;
182 	}
183 
184 	switch (up->uu_priv) {
185 	case PRIV_NONE:
186 		priv = "none";
187 		break;
188 	case PRIV_DES:
189 		up->uu_seclevel |= SNMP_MSGFLAG_PRIV;
190 		priv = "CBC-DES";
191 		break;
192 	case PRIV_AES:
193 		up->uu_seclevel |= SNMP_MSGFLAG_PRIV;
194 		priv = "CFB128-AES-128";
195 		break;
196 	}
197 
198 	log_debug("user \"%s\" auth %s enc %s", up->uu_name, auth, priv);
199 	return 0;
200 
201 fail:
202 	free(up->uu_name);
203 	free(up->uu_authkey);
204 	free(up->uu_privkey);
205 	SLIST_REMOVE(&usmuserlist, up, usmuser, uu_next);
206 	free(up);
207 	return -1;
208 }
209 
210 struct ber_element *
211 usm_decode(struct snmp_message *msg, struct ber_element *elm, const char **errp)
212 {
213 	struct snmp_stats	*stats = &env->sc_stats;
214 	off_t			 offs, offs2;
215 	char			*usmparams;
216 	size_t			 len;
217 	size_t			 enginelen, userlen, digestlen, saltlen;
218 	struct ber		 ber;
219 	struct ber_element	*usm = NULL, *next = NULL, *decr;
220 	char			*engineid;
221 	char			*user;
222 	char			*digest, *salt;
223 	u_long			 now;
224 	long long		 engine_boots, engine_time;
225 
226 	bzero(&ber, sizeof(ber));
227 	offs = ber_getpos(elm);
228 
229 	if (ber_get_nstring(elm, (void *)&usmparams, &len) < 0) {
230 		*errp = "cannot decode security params";
231 		goto done;
232 	}
233 
234 	ber.fd = -1;
235 	ber_set_readbuf(&ber, usmparams, len);
236 	usm = ber_read_elements(&ber, NULL);
237 	if (usm == NULL) {
238 		*errp = "cannot decode security params";
239 		goto done;
240 	}
241 
242 #ifdef DEBUG
243 	fprintf(stderr, "decode USM parameters:\n");
244 	smi_debug_elements(usm);
245 #endif
246 
247 	if (ber_scanf_elements(usm, "{xiixpxx", &engineid, &enginelen,
248 	    &engine_boots, &engine_time, &user, &userlen, &offs2,
249 	    &digest, &digestlen, &salt, &saltlen) != 0) {
250 		*errp = "cannot decode USM params";
251 		goto done;
252 	}
253 
254 	log_debug("USM: engineid '%s', engine boots %lld, engine time %lld, "
255 	    "user '%s'", tohexstr(engineid, enginelen), engine_boots,
256 	    engine_time, user);
257 
258 	if (enginelen > SNMPD_MAXENGINEIDLEN ||
259 	    userlen > SNMPD_MAXUSERNAMELEN ||
260 	    (digestlen != (MSG_HAS_AUTH(msg) ? SNMP_USM_DIGESTLEN : 0)) ||
261 	    (saltlen != (MSG_HAS_PRIV(msg) ? SNMP_USM_SALTLEN : 0))) {
262 		*errp = "bad field length";
263 		goto done;
264 	}
265 
266 	if (enginelen != env->sc_engineid_len ||
267 	    memcmp(engineid, env->sc_engineid, enginelen) != 0) {
268 		*errp = "unknown engine id";
269 		msg->sm_usmerr = OIDVAL_usmErrEngineId;
270 		stats->snmp_usmnosuchengine++;
271 		goto done;
272 	}
273 
274 	if (engine_boots != 0LL && engine_time != 0LL) {
275 		now = snmpd_engine_time();
276 		if (engine_boots != env->sc_engine_boots ||
277 		    engine_time < (long long)(now - SNMP_MAX_TIMEWINDOW) ||
278 		    engine_time > (long long)(now + SNMP_MAX_TIMEWINDOW)) {
279 			*errp = "out of time window";
280 			msg->sm_usmerr = OIDVAL_usmErrTimeWindow;
281 			stats->snmp_usmtimewindow++;
282 			goto done;
283 		}
284 	}
285 
286 	msg->sm_engine_boots = (u_int32_t)engine_boots;
287 	msg->sm_engine_time = (u_int32_t)engine_time;
288 
289 	memcpy(msg->sm_username, user, userlen);
290 	msg->sm_username[userlen] = '\0';
291 	msg->sm_user = usm_finduser(msg->sm_username);
292 	if (msg->sm_user == NULL) {
293 		*errp = "no such user";
294 		msg->sm_usmerr = OIDVAL_usmErrUserName;
295 		stats->snmp_usmnosuchuser++;
296 		goto done;
297 	}
298 	if (MSG_SECLEVEL(msg) > msg->sm_user->uu_seclevel) {
299 		*errp = "unsupported security model";
300 		msg->sm_usmerr = OIDVAL_usmErrSecLevel;
301 		stats->snmp_usmbadseclevel++;
302 		goto done;
303 	}
304 
305 	/*
306 	 * offs is the offset of the USM string within the serialized msg
307 	 * and offs2 the offset of the digest within the USM string.
308 	 */
309 	if (!usm_valid_digest(msg, offs + offs2, digest, digestlen)) {
310 		*errp = "bad msg digest";
311 		msg->sm_usmerr = OIDVAL_usmErrDigest;
312 		stats->snmp_usmwrongdigest++;
313 		goto done;
314 	}
315 
316 	if (MSG_HAS_PRIV(msg)) {
317 		memcpy(msg->sm_salt, salt, saltlen);
318 		if ((decr = usm_decrypt(msg, elm->be_next)) == NULL) {
319 			*errp = "cannot decrypt msg";
320 			msg->sm_usmerr = OIDVAL_usmErrDecrypt;
321 			stats->snmp_usmdecrypterr++;
322 			goto done;
323 		}
324 		ber_replace_elements(elm, decr);
325 	}
326 	next = elm->be_next;
327 
328 done:
329 	ber_free(&ber);
330 	if (usm != NULL)
331 		ber_free_elements(usm);
332 	return next;
333 }
334 
335 struct ber_element *
336 usm_encode(struct snmp_message *msg, struct ber_element *e)
337 {
338 	struct ber		 ber;
339 	struct ber_element	*usm, *a, *res = NULL;
340 	void			*ptr;
341 	char			 digest[SNMP_USM_DIGESTLEN];
342 	size_t			 digestlen, saltlen, len;
343 
344 	msg->sm_digest_offs = 0;
345 	bzero(&ber, sizeof(ber));
346 	ber.fd = -1;
347 
348 	usm = ber_add_sequence(NULL);
349 
350 	if (MSG_HAS_AUTH(msg)) {
351 		/*
352 		 * Fill in enough zeroes and remember the position within the
353 		 * messages. The digest will be calculated once the message
354 		 * is complete.
355 		 */
356 #ifdef DEBUG
357 		assert(msg->sm_user != NULL);
358 #endif
359 		bzero(digest, sizeof(digest));
360 		digestlen = sizeof(digest);
361 	} else
362 		digestlen = 0;
363 
364 	if (MSG_HAS_PRIV(msg)) {
365 #ifdef DEBUG
366 		assert(msg->sm_user != NULL);
367 #endif
368 		++(msg->sm_user->uu_salt);
369 		memcpy(msg->sm_salt, &msg->sm_user->uu_salt,
370 		    sizeof(msg->sm_salt));
371 		saltlen = sizeof(msg->sm_salt);
372 	} else
373 		saltlen = 0;
374 
375 	msg->sm_engine_boots = (u_int32_t)env->sc_engine_boots;
376 	msg->sm_engine_time = (u_int32_t)snmpd_engine_time();
377 	if ((a = ber_printf_elements(usm, "xdds",
378 	    env->sc_engineid, env->sc_engineid_len, msg->sm_engine_boots,
379 	    msg->sm_engine_time, msg->sm_username)) == NULL)
380 		goto done;
381 
382 	if ((a = ber_add_nstring(a, digest, digestlen)) == NULL)
383 		goto done;
384 	if (digestlen > 0)
385 		ber_set_writecallback(a, usm_cb_digest, msg);
386 
387 	if ((a = ber_add_nstring(a, msg->sm_salt, saltlen)) == NULL)
388 		goto done;
389 
390 #ifdef DEBUG
391 	fprintf(stderr, "encode USM parameters:\n");
392 	smi_debug_elements(usm);
393 #endif
394 	len = ber_write_elements(&ber, usm);
395 	if (ber_get_writebuf(&ber, &ptr) > 0) {
396 		res = ber_add_nstring(e, (char *)ptr, len);
397 		if (digestlen > 0)
398 			ber_set_writecallback(res, usm_cb_digest, msg);
399 	}
400 
401 done:
402 	ber_free(&ber);
403 	ber_free_elements(usm);
404 	return res;
405 }
406 
407 void
408 usm_cb_digest(void *arg, size_t offs)
409 {
410 	struct snmp_message *msg = arg;
411 	msg->sm_digest_offs += offs;
412 }
413 
414 struct ber_element *
415 usm_encrypt(struct snmp_message *msg, struct ber_element *pdu)
416 {
417 	struct ber		 ber;
418 	struct ber_element	*encrpdu = NULL;
419 	void			*ptr;
420 	int			 len;
421 	ssize_t			 elen;
422 	u_char			 encbuf[READ_BUF_SIZE];
423 
424 	if (!MSG_HAS_PRIV(msg))
425 		return pdu;
426 
427 	bzero(&ber, sizeof(ber));
428 	ber.fd = -1;
429 
430 #ifdef DEBUG
431 	fprintf(stderr, "encrypted PDU:\n");
432 	smi_debug_elements(pdu);
433 #endif
434 
435 	len = ber_write_elements(&ber, pdu);
436 	if (ber_get_writebuf(&ber, &ptr) > 0) {
437 		elen = usm_crypt(msg, ptr, len, encbuf, 1);
438 		if (elen > 0)
439 			encrpdu = ber_add_nstring(NULL, (char *)encbuf, elen);
440 	}
441 
442 	ber_free(&ber);
443 	ber_free_elements(pdu);
444 	return encrpdu;
445 }
446 
447 /*
448  * Calculate message digest and replace within message
449  */
450 void
451 usm_finalize_digest(struct snmp_message *msg, char *buf, ssize_t len)
452 {
453 	const EVP_MD	*md;
454 	u_char		 digest[EVP_MAX_MD_SIZE];
455 	unsigned	 hlen;
456 
457 	if (msg->sm_resp == NULL ||
458 	    !MSG_HAS_AUTH(msg) ||
459 	    msg->sm_user == NULL ||
460 	    msg->sm_digest_offs == 0 ||
461 	    len <= 0)
462 		return;
463 	bzero(digest, SNMP_USM_DIGESTLEN);
464 #ifdef DEBUG
465 	assert(msg->sm_digest_offs + SNMP_USM_DIGESTLEN <= (size_t)len);
466 	assert(!memcmp(buf + msg->sm_digest_offs, digest, SNMP_USM_DIGESTLEN));
467 #endif
468 
469 	if ((md = usm_get_md(msg->sm_user->uu_auth)) == NULL)
470 		return;
471 
472 	HMAC(md, msg->sm_user->uu_authkey, (int)msg->sm_user->uu_authkeylen,
473 	    (u_char*)buf, (size_t)len, digest, &hlen);
474 
475 	memcpy(buf + msg->sm_digest_offs, digest, SNMP_USM_DIGESTLEN);
476 	return;
477 }
478 
479 void
480 usm_make_report(struct snmp_message *msg)
481 {
482 	struct ber_oid		 usmstat = OID(MIB_usmStats, 0, 0);
483 
484 	/* Always send report in clear-text */
485 	msg->sm_flags = 0;
486 	msg->sm_context = SNMP_C_REPORT;
487 	msg->sm_username[0] = '\0';
488 	usmstat.bo_id[OIDIDX_usmStats] = msg->sm_usmerr;
489 	usmstat.bo_n = OIDIDX_usmStats + 2;
490 	if (msg->sm_varbindresp != NULL)
491 		ber_free_elements(msg->sm_varbindresp);
492 	msg->sm_varbindresp = ber_add_sequence(NULL);
493 	mps_getreq(NULL, msg->sm_varbindresp, &usmstat, msg->sm_version);
494 	return;
495 }
496 
497 int
498 usm_valid_digest(struct snmp_message *msg, off_t offs,
499 	char *digest, size_t digestlen)
500 {
501 	const EVP_MD	*md;
502 	u_char		 exp_digest[EVP_MAX_MD_SIZE];
503 	unsigned	 hlen;
504 
505 	if (!MSG_HAS_AUTH(msg))
506 		return 1;
507 
508 	if (digestlen != SNMP_USM_DIGESTLEN)
509 		return 0;
510 
511 #ifdef DEBUG
512 	assert(offs + digestlen <= msg->sm_datalen);
513 	assert(bcmp(&msg->sm_data[offs], digest, digestlen) == 0);
514 #endif
515 
516 	if ((md = usm_get_md(msg->sm_user->uu_auth)) == NULL)
517 		return 0;
518 
519 	memset(&msg->sm_data[offs], 0, digestlen);
520 	HMAC(md, msg->sm_user->uu_authkey, (int)msg->sm_user->uu_authkeylen,
521 	    msg->sm_data, msg->sm_datalen, exp_digest, &hlen);
522 	/* we don't bother to restore the original message */
523 
524 	if (hlen < digestlen)
525 		return 0;
526 
527 	return memcmp(digest, exp_digest, digestlen) == 0;
528 }
529 
530 struct ber_element *
531 usm_decrypt(struct snmp_message *msg, struct ber_element *encr)
532 {
533 	u_char			*privstr;
534 	size_t			 privlen;
535 	u_char			 buf[READ_BUF_SIZE];
536 	struct ber		 ber;
537 	struct ber_element	*scoped_pdu = NULL;
538 	ssize_t			 scoped_pdu_len;
539 
540 	if (ber_get_nstring(encr, (void *)&privstr, &privlen) < 0)
541 		return NULL;
542 
543 	scoped_pdu_len = usm_crypt(msg, privstr, (int)privlen, buf, 0);
544 	if (scoped_pdu_len < 0)
545 		return NULL;
546 
547 	bzero(&ber, sizeof(ber));
548 	ber.fd = -1;
549 	ber_set_readbuf(&ber, buf, scoped_pdu_len);
550 	scoped_pdu = ber_read_elements(&ber, NULL);
551 
552 #ifdef DEBUG
553 	if (scoped_pdu != NULL) {
554 		fprintf(stderr, "decrypted scoped PDU:\n");
555 		smi_debug_elements(scoped_pdu);
556 	}
557 #endif
558 
559 	ber_free(&ber);
560 	return scoped_pdu;
561 }
562 
563 ssize_t
564 usm_crypt(struct snmp_message *msg, u_char *inbuf, int inlen, u_char *outbuf,
565 	int do_encrypt)
566 {
567 	const EVP_CIPHER	*cipher;
568 	EVP_CIPHER_CTX		 ctx;
569 	u_char			*privkey;
570 	int			 i;
571 	u_char			 iv[EVP_MAX_IV_LENGTH];
572 	int			 len, len2;
573 	int			 rv;
574 	u_int32_t		 ivv;
575 
576 	if ((cipher = usm_get_cipher(msg->sm_user->uu_priv)) == NULL)
577 		return -1;
578 
579 	privkey = (u_char *)msg->sm_user->uu_privkey;
580 #ifdef DEBUG
581 	assert(privkey != NULL);
582 #endif
583 	switch (msg->sm_user->uu_priv) {
584 	case PRIV_DES:
585 		/* RFC3414, chap 8.1.1.1. */
586 		for (i = 0; i < 8; i++)
587 			iv[i] = msg->sm_salt[i] ^ privkey[SNMP_USM_SALTLEN + i];
588 		break;
589 	case PRIV_AES:
590 		/* RFC3826, chap 3.1.2.1. */
591 		ivv = htobe32(msg->sm_engine_boots);
592 		memcpy(iv, &ivv, sizeof(ivv));
593 		ivv = htobe32(msg->sm_engine_time);
594 		memcpy(iv + sizeof(ivv), &ivv, sizeof(ivv));
595 		memcpy(iv + 2 * sizeof(ivv), msg->sm_salt, SNMP_USM_SALTLEN);
596 		break;
597 	default:
598 		return -1;
599 	}
600 
601 	if (!EVP_CipherInit(&ctx, cipher, privkey, iv, do_encrypt))
602 		return -1;
603 
604 	if (!do_encrypt)
605 		EVP_CIPHER_CTX_set_padding(&ctx, 0);
606 
607 	if (EVP_CipherUpdate(&ctx, outbuf, &len, inbuf, inlen) &&
608 	    EVP_CipherFinal(&ctx, outbuf + len, &len2))
609 		rv = len + len2;
610 	else
611 		rv = -1;
612 
613 	EVP_CIPHER_CTX_cleanup(&ctx);
614 	return rv;
615 }
616 
617 /*
618  * RFC3414, Password to Key Algorithm
619  */
620 char *
621 usm_passwd2key(const EVP_MD *md, char *passwd, int *maxlen)
622 {
623 	EVP_MD_CTX	 ctx;
624 	int		 i, count;
625 	u_char		*pw, *c;
626 	u_char		 pwbuf[2 * EVP_MAX_MD_SIZE + SNMPD_MAXENGINEIDLEN];
627 	u_char		 keybuf[EVP_MAX_MD_SIZE];
628 	unsigned	 dlen;
629 	char		*key;
630 
631 	EVP_DigestInit(&ctx, md);
632 	pw = (u_char *)passwd;
633 	for (count = 0; count < 1048576; count += 64) {
634 		c = pwbuf;
635 		for (i = 0; i < 64; i++) {
636 			if (*pw == '\0')
637 				pw = (u_char *)passwd;
638 			*c++ = *pw++;
639 		}
640 		EVP_DigestUpdate(&ctx, pwbuf, 64);
641 	}
642 	EVP_DigestFinal(&ctx, keybuf, &dlen);
643 	EVP_MD_CTX_cleanup(&ctx);
644 
645 	/* Localize the key */
646 #ifdef DEBUG
647 	assert(env->sc_engineid_len <= SNMPD_MAXENGINEIDLEN);
648 #endif
649 	memcpy(pwbuf, keybuf, dlen);
650 	memcpy(pwbuf + dlen, env->sc_engineid, env->sc_engineid_len);
651 	memcpy(pwbuf + dlen + env->sc_engineid_len, keybuf, dlen);
652 
653 	EVP_DigestInit(&ctx, md);
654 	EVP_DigestUpdate(&ctx, pwbuf, 2 * dlen + env->sc_engineid_len);
655 	EVP_DigestFinal(&ctx, keybuf, &dlen);
656 	EVP_MD_CTX_cleanup(&ctx);
657 
658 	if (*maxlen > 0 && dlen > (unsigned)*maxlen)
659 		dlen = (unsigned)*maxlen;
660 	if ((key = malloc(dlen)) == NULL)
661 		fatal("key");
662 	memcpy(key, keybuf, dlen);
663 	*maxlen = (int)dlen;
664 	return key;
665 }
666