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