xref: /openbsd/usr.sbin/snmpd/usm.c (revision 09467b48)
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