GCC Code Coverage Report
Directory: ./ Exec Total Coverage
File: usr.sbin/snmpd/usm.c Lines: 26 309 8.4 %
Date: 2017-11-13 Branches: 18 161 11.2 %

Line Branch Exec Source
1
/*	$OpenBSD: usm.c,v 1.11 2016/10/28 08:01:53 rzalamena 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
const EVP_CIPHER	*usm_get_cipher(enum usmpriv);
49
void			 usm_cb_digest(void *, size_t);
50
int			 usm_valid_digest(struct snmp_message *, off_t, char *,
51
			    size_t);
52
struct ber_element	*usm_decrypt(struct snmp_message *,
53
			    struct ber_element *);
54
ssize_t			 usm_crypt(struct snmp_message *, u_char *, int,
55
			    u_char *, int);
56
char			*usm_passwd2key(const EVP_MD *, char *, int *);
57
58
void
59
usm_generate_keys(void)
60
{
61
	struct usmuser	*up;
62
	const EVP_MD	*md;
63
	char		*key;
64
	int		 len;
65
66
	SLIST_FOREACH(up, &usmuserlist, uu_next) {
67
		if ((md = usm_get_md(up->uu_auth)) == NULL)
68
			continue;
69
70
		/* convert auth password to key */
71
		len = 0;
72
		key = usm_passwd2key(md, up->uu_authkey, &len);
73
		free(up->uu_authkey);
74
		up->uu_authkey = key;
75
		up->uu_authkeylen = len;
76
77
		/* optionally convert privacy password to key */
78
		if (up->uu_priv != PRIV_NONE) {
79
			arc4random_buf(&up->uu_salt, sizeof(up->uu_salt));
80
81
			len = SNMP_CIPHER_KEYLEN;
82
			key = usm_passwd2key(md, up->uu_privkey, &len);
83
			free(up->uu_privkey);
84
			up->uu_privkey = key;
85
		}
86
	}
87
	return;
88
}
89
90
const EVP_MD *
91
usm_get_md(enum usmauth ua)
92
{
93
	switch (ua) {
94
	case AUTH_MD5:
95
		return EVP_md5();
96
	case AUTH_SHA1:
97
		return EVP_sha1();
98
	case AUTH_NONE:
99
	default:
100
		return NULL;
101
	}
102
}
103
104
const EVP_CIPHER *
105
usm_get_cipher(enum usmpriv up)
106
{
107
	switch (up) {
108
	case PRIV_DES:
109
		return EVP_des_cbc();
110
	case PRIV_AES:
111
		return EVP_aes_128_cfb128();
112
	case PRIV_NONE:
113
	default:
114
		return NULL;
115
	}
116
}
117
118
struct usmuser *
119
usm_newuser(char *name, const char **errp)
120
{
121
16
	struct usmuser *up = usm_finduser(name);
122
8
	if (up != NULL) {
123
		*errp = "user redefined";
124
		return NULL;
125
	}
126
8
	if ((up = calloc(1, sizeof(*up))) == NULL)
127
		fatal("usm");
128
8
	up->uu_name = name;
129
8
	SLIST_INSERT_HEAD(&usmuserlist, up, uu_next);
130
8
	return up;
131
8
}
132
133
struct usmuser *
134
usm_finduser(char *name)
135
{
136
	struct usmuser *up;
137
138
24
	SLIST_FOREACH(up, &usmuserlist, uu_next) {
139
		if (!strcmp(up->uu_name, name))
140
			return up;
141
	}
142
8
	return NULL;
143
8
}
144
145
int
146
usm_checkuser(struct usmuser *up, const char **errp)
147
{
148
	char	*auth = NULL, *priv = NULL;
149
150

16
	if (up->uu_auth != AUTH_NONE && up->uu_authkey == NULL) {
151
		*errp = "missing auth passphrase";
152
		goto fail;
153

16
	} else if (up->uu_auth == AUTH_NONE && up->uu_authkey != NULL)
154
8
		up->uu_auth = AUTH_DEFAULT;
155
156

12
	if (up->uu_priv != PRIV_NONE && up->uu_privkey == NULL) {
157
		*errp = "missing priv passphrase";
158
		goto fail;
159

12
	} else if (up->uu_priv == PRIV_NONE && up->uu_privkey != NULL)
160
		up->uu_priv = PRIV_DEFAULT;
161
162

8
	if (up->uu_auth == AUTH_NONE && up->uu_priv != PRIV_NONE) {
163
		/* Standard prohibits noAuthPriv */
164
		*errp = "auth is mandatory with enc";
165
		goto fail;
166
	}
167
168

16
	switch (up->uu_auth) {
169
	case AUTH_NONE:
170
		auth = "none";
171
		break;
172
	case AUTH_MD5:
173
		up->uu_seclevel |= SNMP_MSGFLAG_AUTH;
174
		auth = "HMAC-MD5-96";
175
		break;
176
	case AUTH_SHA1:
177
8
		up->uu_seclevel |= SNMP_MSGFLAG_AUTH;
178
		auth = "HMAC-SHA1-96";
179
8
		break;
180
	}
181
182

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