118334Speter/*-
290075Sobrien * Copyright (c) 2010 The FreeBSD Foundation
3132718Skan *
418334Speter * This software was developed by Shteryana Sotirova Shopova under
590075Sobrien * sponsorship from the FreeBSD Foundation.
618334Speter *
790075Sobrien * Redistribution and use in source and binary forms, with or without
890075Sobrien * modification, are permitted provided that the following conditions
990075Sobrien * are met:
1090075Sobrien * 1. Redistributions of source code must retain the above copyright
1118334Speter *    notice, this list of conditions and the following disclaimer.
1290075Sobrien * 2. Redistributions in binary form must reproduce the above copyright
1390075Sobrien *    notice, this list of conditions and the following disclaimer in the
1490075Sobrien *    documentation and/or other materials provided with the distribution.
1590075Sobrien *
1618334Speter * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
1718334Speter * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
1890075Sobrien * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
1990075Sobrien * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
2090075Sobrien * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
2118334Speter * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2218334Speter * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2318334Speter * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
2418334Speter * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
2518334Speter * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
2618334Speter * SUCH DAMAGE.
2718334Speter *
2818334Speter * $FreeBSD$
2918334Speter */
3050397Sobrien#include <sys/types.h>
3150397Sobrien#include <sys/socket.h>
32132718Skan#include <stdio.h>
33132718Skan#include <stdlib.h>
3418334Speter#include <stddef.h>
3518334Speter#include <stdarg.h>
3618334Speter#ifdef HAVE_STDINT_H
3718334Speter#include <stdint.h>
3818334Speter#elif defined(HAVE_INTTYPES_H)
3918334Speter#include <inttypes.h>
4018334Speter#endif
41117395Skan#include <string.h>
4290075Sobrien#include <ctype.h>
4350397Sobrien#include <errno.h>
4490075Sobrien#include <netinet/in.h>
4518334Speter
4690075Sobrien#ifdef HAVE_LIBCRYPTO
4790075Sobrien#include <openssl/evp.h>
4890075Sobrien#endif
4990075Sobrien
5090075Sobrien#include "asn1.h"
51132718Skan#include "snmp.h"
5218334Speter#include "snmppriv.h"
5318334Speter
5490075Sobrien#define	SNMP_PRIV_AES_IV_SIZ		16
5590075Sobrien#define	SNMP_EXTENDED_KEY_SIZ		64
5618334Speter#define	SNMP_AUTH_KEY_LOOPCNT		1048576
5718334Speter#define	SNMP_AUTH_BUF_SIZE		72
5850397Sobrien
5950397Sobrien#ifdef HAVE_LIBCRYPTO
6050397Sobrien
6118334Speterstatic const uint8_t ipad = 0x36;
6218334Speterstatic const uint8_t opad = 0x5c;
6390075Sobrien
6418334Speterstatic int32_t
6518334Spetersnmp_digest_init(const struct snmp_user *user, EVP_MD_CTX *ctx,
6618334Speter    const EVP_MD **dtype, uint32_t *keylen)
6790075Sobrien{
6890075Sobrien	if (user->auth_proto == SNMP_AUTH_HMAC_MD5) {
6918334Speter		*dtype = EVP_md5();
7090075Sobrien		*keylen = SNMP_AUTH_HMACMD5_KEY_SIZ;
71117395Skan	} else if (user->auth_proto == SNMP_AUTH_HMAC_SHA) {
7290075Sobrien		*dtype = EVP_sha1();
7390075Sobrien		*keylen = SNMP_AUTH_HMACSHA_KEY_SIZ;
7490075Sobrien	} else if (user->auth_proto == SNMP_AUTH_NOAUTH)
7590075Sobrien		return (0);
7690075Sobrien	else {
77117395Skan		snmp_error("unknown authentication option - %d",
7890075Sobrien		    user->auth_proto);
7990075Sobrien		return (-1);
8090075Sobrien	}
8190075Sobrien
8290075Sobrien	if (EVP_DigestInit(ctx, *dtype) != 1)
8390075Sobrien		return (-1);
8490075Sobrien
8590075Sobrien	return (1);
8690075Sobrien}
87117395Skan
88117395Skanenum snmp_code
89117395Skansnmp_pdu_calc_digest(const struct snmp_pdu *pdu, uint8_t *digest)
90117395Skan{
9190075Sobrien	uint8_t md[EVP_MAX_MD_SIZE], extkey[SNMP_EXTENDED_KEY_SIZ];
9290075Sobrien	uint8_t key1[SNMP_EXTENDED_KEY_SIZ], key2[SNMP_EXTENDED_KEY_SIZ];
93117395Skan	uint32_t i, keylen, olen;
94117395Skan	int32_t err;
9590075Sobrien	const EVP_MD *dtype;
9690075Sobrien	EVP_MD_CTX *ctx;
9790075Sobrien
9890075Sobrien	ctx = EVP_MD_CTX_new();
99132718Skan	if (ctx == NULL)
100132718Skan		return (SNMP_CODE_FAILED);
101132718Skan	err = snmp_digest_init(&pdu->user, ctx, &dtype, &keylen);
102132718Skan	if (err <= 0)
10390075Sobrien		EVP_MD_CTX_free(ctx);
10490075Sobrien	if (err < 0)
10590075Sobrien		return (SNMP_CODE_BADDIGEST);
10690075Sobrien	else if (err == 0)
10790075Sobrien		return (SNMP_CODE_OK);
10890075Sobrien
10990075Sobrien	memset(pdu->digest_ptr, 0, sizeof(pdu->msg_digest));
110132718Skan	memcpy(extkey, pdu->user.auth_key, keylen);
11190075Sobrien	memset(extkey + keylen, 0, sizeof(extkey) - keylen);
11218334Speter
11318334Speter	for (i = 0; i < SNMP_EXTENDED_KEY_SIZ; i++) {
11418334Speter		key1[i] = extkey[i] ^ ipad;
115132718Skan		key2[i] = extkey[i] ^ opad;
11618334Speter	}
11718334Speter
11818334Speter	if (EVP_DigestUpdate(ctx, key1, SNMP_EXTENDED_KEY_SIZ) != 1 ||
11918334Speter	    EVP_DigestUpdate(ctx, pdu->outer_ptr, pdu->outer_len) != 1 ||
12018334Speter	    EVP_DigestFinal(ctx, md, &olen) != 1)
12118334Speter		goto failed;
12218334Speter
12318334Speter	if (EVP_DigestInit(ctx, dtype) != 1 ||
12418334Speter	    EVP_DigestUpdate(ctx, key2, SNMP_EXTENDED_KEY_SIZ) != 1 ||
12518334Speter	    EVP_DigestUpdate(ctx, md, olen) != 1 ||
12618334Speter	    EVP_DigestFinal(ctx, md, &olen) != 1)
12718334Speter		goto failed;
12818334Speter
12990075Sobrien	if (olen < SNMP_USM_AUTH_SIZE) {
13090075Sobrien		snmp_error("bad digest size - %d", olen);
13190075Sobrien		EVP_MD_CTX_free(ctx);
13290075Sobrien		return (SNMP_CODE_BADDIGEST);
13390075Sobrien	}
13490075Sobrien
13518334Speter	memcpy(digest, md, SNMP_USM_AUTH_SIZE);
13690075Sobrien	EVP_MD_CTX_free(ctx);
13718334Speter	return (SNMP_CODE_OK);
138132718Skan
139132718Skanfailed:
140132718Skan	EVP_MD_CTX_free(ctx);
141132718Skan	return (SNMP_CODE_BADDIGEST);
142132718Skan}
143132718Skan
144132718Skanstatic int32_t
145132718Skansnmp_pdu_cipher_init(const struct snmp_pdu *pdu, int32_t len,
146132718Skan    const EVP_CIPHER **ctype, uint8_t *piv)
147132718Skan{
148132718Skan	int i;
149132718Skan	uint32_t netint;
150132718Skan
151132718Skan	if (pdu->user.priv_proto == SNMP_PRIV_DES) {
152132718Skan		if (len  % 8 != 0)
153132718Skan			return (-1);
154117395Skan		*ctype = EVP_des_cbc();
155132718Skan		memcpy(piv, pdu->msg_salt, sizeof(pdu->msg_salt));
156132718Skan		for (i = 0; i < 8; i++)
157132718Skan			piv[i] = piv[i] ^ pdu->user.priv_key[8 + i];
158132718Skan	} else if (pdu->user.priv_proto == SNMP_PRIV_AES) {
159132718Skan		*ctype = EVP_aes_128_cfb128();
160132718Skan		netint = htonl(pdu->engine.engine_boots);
161132718Skan		memcpy(piv, &netint, sizeof(netint));
162132718Skan		piv += sizeof(netint);
163132718Skan		netint = htonl(pdu->engine.engine_time);
164132718Skan		memcpy(piv, &netint, sizeof(netint));
165132718Skan		piv += sizeof(netint);
166132718Skan		memcpy(piv, pdu->msg_salt, sizeof(pdu->msg_salt));
167132718Skan	} else if (pdu->user.priv_proto == SNMP_PRIV_NOPRIV)
16850397Sobrien		return (0);
169132718Skan	else {
170132718Skan		snmp_error("unknown privacy option - %d", pdu->user.priv_proto);
17150397Sobrien		return (-1);
17250397Sobrien	}
17350397Sobrien
174132718Skan	return (1);
175132718Skan}
176132718Skan
17750397Sobrienenum snmp_code
17850397Sobriensnmp_pdu_encrypt(const struct snmp_pdu *pdu)
179132718Skan{
180132718Skan	int32_t err, olen;
181132718Skan	uint8_t iv[SNMP_PRIV_AES_IV_SIZ];
182132718Skan	const EVP_CIPHER *ctype;
18318334Speter	EVP_CIPHER_CTX *ctx;
184132718Skan
18550397Sobrien	err = snmp_pdu_cipher_init(pdu, pdu->scoped_len, &ctype, iv);
18650397Sobrien	if (err < 0)
18750397Sobrien		return (SNMP_CODE_EDECRYPT);
18890075Sobrien	else if (err == 0)
18990075Sobrien		return (SNMP_CODE_OK);
19050397Sobrien
19190075Sobrien	ctx = EVP_CIPHER_CTX_new();
19290075Sobrien	if (ctx == NULL)
19390075Sobrien		return (SNMP_CODE_FAILED);
194117395Skan	if (EVP_EncryptInit(ctx, ctype, pdu->user.priv_key, iv) != 1)
195117395Skan		goto failed;
196117395Skan
19718334Speter	if (EVP_EncryptUpdate(ctx, pdu->scoped_ptr, &olen, pdu->scoped_ptr,
19850397Sobrien	    pdu->scoped_len) != 1 ||
19918334Speter	    EVP_EncryptFinal(ctx, pdu->scoped_ptr + olen, &olen) != 1)
200132718Skan		goto failed;
201132718Skan
20218334Speter	EVP_CIPHER_CTX_free(ctx);
203117395Skan	return (SNMP_CODE_OK);
20490075Sobrien
20518334Speterfailed:
20618334Speter	EVP_CIPHER_CTX_free(ctx);
20718334Speter	return (SNMP_CODE_FAILED);
20890075Sobrien}
20918334Speter
21018334Speterenum snmp_code
211132718Skansnmp_pdu_decrypt(const struct snmp_pdu *pdu)
21218334Speter{
21390075Sobrien	int32_t err, olen;
21490075Sobrien	uint8_t iv[SNMP_PRIV_AES_IV_SIZ];
215132718Skan	const EVP_CIPHER *ctype;
21690075Sobrien	EVP_CIPHER_CTX *ctx;
21790075Sobrien
21890075Sobrien	err = snmp_pdu_cipher_init(pdu, pdu->scoped_len, &ctype, iv);
21990075Sobrien	if (err < 0)
22090075Sobrien		return (SNMP_CODE_EDECRYPT);
22190075Sobrien	else if (err == 0)
222132718Skan		return (SNMP_CODE_OK);
22390075Sobrien
22418334Speter	ctx = EVP_CIPHER_CTX_new();
22518334Speter	if (ctx == NULL)
22618334Speter		return (SNMP_CODE_FAILED);
22718334Speter	if (EVP_DecryptInit(ctx, ctype, pdu->user.priv_key, iv) != 1 ||
22818334Speter	    EVP_CIPHER_CTX_set_padding(ctx, 0) != 1)
22918334Speter		goto failed;
23018334Speter
23118334Speter	if (EVP_DecryptUpdate(ctx, pdu->scoped_ptr, &olen, pdu->scoped_ptr,
232132718Skan	    pdu->scoped_len) != 1 ||
23318334Speter	    EVP_DecryptFinal(ctx, pdu->scoped_ptr + olen, &olen) != 1)
23418334Speter		goto failed;
23518334Speter
236117395Skan	EVP_CIPHER_CTX_free(ctx);
23750397Sobrien	return (SNMP_CODE_OK);
23818334Speter
23918334Speterfailed:
24018334Speter	EVP_CIPHER_CTX_free(ctx);
24118334Speter	return (SNMP_CODE_EDECRYPT);
24218334Speter}
24318334Speter
244132718Skan/* [RFC 3414] - A.2. Password to Key Algorithm */
24518334Speterenum snmp_code
24618334Spetersnmp_passwd_to_keys(struct snmp_user *user, char *passwd)
24718334Speter{
248117395Skan	int err, loop, i, pwdlen;
24950397Sobrien	uint32_t  keylen, olen;
25018334Speter	const EVP_MD *dtype;
25118334Speter	EVP_MD_CTX *ctx;
25250397Sobrien	uint8_t authbuf[SNMP_AUTH_BUF_SIZE];
25318334Speter
25450397Sobrien	if (passwd == NULL || user == NULL)
25518334Speter		return (SNMP_CODE_FAILED);
25618334Speter
25750397Sobrien	ctx = EVP_MD_CTX_new();
25850397Sobrien	if (ctx == NULL)
25918334Speter		return (SNMP_CODE_FAILED);
26018334Speter
261117395Skan	err = snmp_digest_init(user, ctx, &dtype, &keylen);
26218334Speter	if (err <= 0)
26318334Speter		EVP_MD_CTX_free(ctx);
26418334Speter	if (err < 0)
26518334Speter		return (SNMP_CODE_BADDIGEST);
266132718Skan	else if (err == 0)
26718334Speter		return (SNMP_CODE_OK);
26818334Speter
26918334Speter	memset(user->auth_key, 0, sizeof(user->auth_key));
27018334Speter	pwdlen = strlen(passwd);
271117395Skan
272117395Skan	for (loop = 0; loop < SNMP_AUTH_KEY_LOOPCNT; loop += i) {
273117395Skan		for (i = 0; i < SNMP_EXTENDED_KEY_SIZ; i++)
274117395Skan			authbuf[i] = passwd[(loop + i) % pwdlen];
275117395Skan		if (EVP_DigestUpdate(ctx, authbuf, SNMP_EXTENDED_KEY_SIZ) != 1)
276117395Skan			goto failed;
277117395Skan	}
278117395Skan
27918334Speter	if (EVP_DigestFinal(ctx, user->auth_key, &olen) != 1)
28018334Speter		goto failed;
281117395Skan
28218334Speter	EVP_MD_CTX_free(ctx);
28318334Speter	return (SNMP_CODE_OK);
28450397Sobrien
28518334Speterfailed:
28618334Speter	EVP_MD_CTX_free(ctx);
287132718Skan	return (SNMP_CODE_BADDIGEST);
28818334Speter}
28918334Speter
29018334Speter/* [RFC 3414] - 2.6. Key Localization Algorithm */
29118334Speterenum snmp_code
29250397Sobriensnmp_get_local_keys(struct snmp_user *user, uint8_t *eid, uint32_t elen)
29350397Sobrien{
29450397Sobrien	int err;
295132718Skan	uint32_t  keylen, olen;
29650397Sobrien	const EVP_MD *dtype;
29750397Sobrien	EVP_MD_CTX *ctx;
29850397Sobrien	uint8_t authbuf[SNMP_AUTH_BUF_SIZE];
29950397Sobrien
30090075Sobrien	if (user == NULL || eid == NULL || elen > SNMP_ENGINE_ID_SIZ)
30190075Sobrien		return (SNMP_CODE_FAILED);
30290075Sobrien
303132718Skan	ctx = EVP_MD_CTX_new();
30490075Sobrien	if (ctx == NULL)
30590075Sobrien		return (SNMP_CODE_FAILED);
30690075Sobrien
30790075Sobrien	memset(user->priv_key, 0, sizeof(user->priv_key));
30890075Sobrien	memset(authbuf, 0, sizeof(authbuf));
30990075Sobrien
31090075Sobrien	err = snmp_digest_init(user, ctx, &dtype, &keylen);
31190075Sobrien	if (err <= 0)
312132718Skan		EVP_MD_CTX_free(ctx);
31390075Sobrien	if (err < 0)
31490075Sobrien		return (SNMP_CODE_BADDIGEST);
31590075Sobrien	else if (err == 0)
31690075Sobrien		return (SNMP_CODE_OK);
31790075Sobrien
31890075Sobrien	memcpy(authbuf, user->auth_key, keylen);
31990075Sobrien	memcpy(authbuf + keylen, eid, elen);
32090075Sobrien	memcpy(authbuf + keylen + elen, user->auth_key, keylen);
32190075Sobrien
32290075Sobrien	if (EVP_DigestUpdate(ctx, authbuf, 2 * keylen + elen) != 1 ||
32390075Sobrien	    EVP_DigestFinal(ctx, user->auth_key, &olen) != 1) {
324132718Skan		EVP_MD_CTX_free(ctx);
32590075Sobrien		return (SNMP_CODE_BADDIGEST);
32690075Sobrien	}
32790075Sobrien	EVP_MD_CTX_free(ctx);
328117395Skan
32990075Sobrien	if (user->priv_proto != SNMP_PRIV_NOPRIV)
33090075Sobrien		memcpy(user->priv_key, user->auth_key, sizeof(user->priv_key));
33190075Sobrien
33290075Sobrien	return (SNMP_CODE_OK);
33390075Sobrien}
33490075Sobrien
33590075Sobrienenum snmp_code
336117395Skansnmp_calc_keychange(struct snmp_user *user, uint8_t *keychange)
33790075Sobrien{
33890075Sobrien	int32_t err, rvalue[SNMP_AUTH_HMACSHA_KEY_SIZ / 4];
33990075Sobrien	uint32_t i, keylen, olen;
340132718Skan	const EVP_MD *dtype;
34190075Sobrien	EVP_MD_CTX *ctx;
34290075Sobrien
34390075Sobrien	ctx = EVP_MD_CTX_new();
344117395Skan	if (ctx == NULL)
345117395Skan		return (SNMP_CODE_FAILED);
34690075Sobrien
34790075Sobrien	err = snmp_digest_init(user, ctx, &dtype, &keylen);
34890075Sobrien	if (err <= 0)
34990075Sobrien		EVP_MD_CTX_free(ctx);
35090075Sobrien	if (err < 0)
35190075Sobrien		return (SNMP_CODE_BADDIGEST);
352117395Skan	else if (err == 0)
35390075Sobrien		return (SNMP_CODE_OK);
35490075Sobrien
35590075Sobrien	for (i = 0; i < keylen / 4; i++)
35690075Sobrien		rvalue[i] = random();
35790075Sobrien
35890075Sobrien	memcpy(keychange, user->auth_key, keylen);
35990075Sobrien	memcpy(keychange + keylen, rvalue, keylen);
36090075Sobrien
36190075Sobrien	if (EVP_DigestUpdate(ctx, keychange, 2 * keylen) != 1 ||
36290075Sobrien	    EVP_DigestFinal(ctx, keychange, &olen) != 1) {
363132718Skan		EVP_MD_CTX_free(ctx);
36490075Sobrien		return (SNMP_CODE_BADDIGEST);
36590075Sobrien	}
36690075Sobrien
367117395Skan	EVP_MD_CTX_free(ctx);
36890075Sobrien	return (SNMP_CODE_OK);
36990075Sobrien}
37090075Sobrien
37190075Sobrien#else /* !HAVE_LIBCRYPTO */
37290075Sobrien
37390075Sobrienenum snmp_code
374132718Skansnmp_pdu_calc_digest(const struct snmp_pdu *pdu, uint8_t *digest __unused)
37590075Sobrien{
37690075Sobrien	if  (pdu->user.auth_proto != SNMP_AUTH_NOAUTH)
37790075Sobrien		return (SNMP_CODE_BADSECLEVEL);
37890075Sobrien
37990075Sobrien
38090075Sobrien	return (SNMP_CODE_OK);
38190075Sobrien}
38290075Sobrien
38390075Sobrienenum snmp_code
38490075Sobriensnmp_pdu_encrypt(const struct snmp_pdu *pdu)
38590075Sobrien{
38690075Sobrien	if (pdu->user.priv_proto != SNMP_PRIV_NOPRIV)
38790075Sobrien		return (SNMP_CODE_BADSECLEVEL);
38890075Sobrien
389132718Skan	return (SNMP_CODE_OK);
39090075Sobrien}
39190075Sobrien
39290075Sobrienenum snmp_code
39390075Sobriensnmp_pdu_decrypt(const struct snmp_pdu *pdu)
39490075Sobrien{
39590075Sobrien	if (pdu->user.priv_proto != SNMP_PRIV_NOPRIV)
396117395Skan		return (SNMP_CODE_BADSECLEVEL);
39790075Sobrien
39890075Sobrien	return (SNMP_CODE_OK);
39990075Sobrien}
40090075Sobrien
40190075Sobrienenum snmp_code
40290075Sobriensnmp_passwd_to_keys(struct snmp_user *user, char *passwd __unused)
40390075Sobrien{
40490075Sobrien	if (user->auth_proto == SNMP_AUTH_NOAUTH &&
40590075Sobrien	    user->priv_proto == SNMP_PRIV_NOPRIV)
40690075Sobrien		return (SNMP_CODE_OK);
40790075Sobrien
40818334Speter	errno = EPROTONOSUPPORT;
40918334Speter
41050397Sobrien	return (SNMP_CODE_FAILED);
41150397Sobrien}
41218334Speter
41318334Speterenum snmp_code
414132718Skansnmp_get_local_keys(struct snmp_user *user, uint8_t *eid __unused,
41518334Speter    uint32_t elen __unused)
41690075Sobrien{
41790075Sobrien	if (user->auth_proto == SNMP_AUTH_NOAUTH &&
41890075Sobrien	    user->priv_proto == SNMP_PRIV_NOPRIV)
41918334Speter		return (SNMP_CODE_OK);
42018334Speter
42118334Speter	errno = EPROTONOSUPPORT;
42218334Speter
42390075Sobrien	return (SNMP_CODE_FAILED);
42490075Sobrien}
42590075Sobrien
42696263Sobrienenum snmp_code
42796263Sobriensnmp_calc_keychange(struct snmp_user *user __unused,
42896263Sobrien    uint8_t *keychange __unused)
42996263Sobrien{
43090075Sobrien	errno = EPROTONOSUPPORT;
43118334Speter	return (SNMP_CODE_FAILED);
43290075Sobrien}
43396263Sobrien
434132718Skan#endif /* HAVE_LIBCRYPTO */
43590075Sobrien