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