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