Partly apply Elias Oenal's SNMP V3 patch, part 2: Add SNMPv3 support routines. Crypto does not work yet.

This commit is contained in:
Dirk Ziegelmeier 2016-03-29 10:24:20 +02:00
parent d0c3baf340
commit 52fdc6bb3e
3 changed files with 451 additions and 0 deletions

View File

@ -135,6 +135,7 @@ SNMPFILES=$(LWIPDIR)/apps/snmp/snmp_asn1.c \
$(LWIPDIR)/apps/snmp/snmp_mib2_tcp.c \
$(LWIPDIR)/apps/snmp/snmp_mib2_udp.c \
$(LWIPDIR)/apps/snmp/snmp_msg.c \
$(LWIPDIR)/apps/snmp/snmpv3.c \
$(LWIPDIR)/apps/snmp/snmp_netconn.c \
$(LWIPDIR)/apps/snmp/snmp_pbuf_stream.c \
$(LWIPDIR)/apps/snmp/snmp_raw.c \

326
src/apps/snmp/snmpv3.c Normal file
View File

@ -0,0 +1,326 @@
/**
* @file
* Additional SNMPv3 functionality RFC3414 and RFC3826.
*/
/*
* Copyright (c) 2016 Elias Oenal.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification,
* are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
* 3. The name of the author may not be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
* SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
* OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
* IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
* OF SUCH DAMAGE.
*
* Author: Elias Oenal <lwip@eliasoenal.com>
*/
#include "snmpv3.h"
#include "arch/cc.h"
#include "snmp_msg.h"
#include "lwip/sys.h"
#include <string.h>
#if LWIP_SNMP && LWIP_SNMP_V3
#ifdef LWIP_SNMPV3_INCLUDE_ENGINE
#include LWIP_SNMPV3_INCLUDE_ENGINE
#endif
#ifdef LWIP_SNMP_V3_CRYPTO
#ifdef LWIP_INCLUDE_CRYPTO_LIB
#include LWIP_INCLUDE_CRYPTO_LIB
#endif
#ifdef LWIP_INCLUDE_CRYPTO_MD5
#include LWIP_INCLUDE_CRYPTO_MD5
#endif
#ifdef LWIP_INCLUDE_CRYPTO_SHA
#include LWIP_INCLUDE_CRYPTO_SHA
#endif
#ifdef LWIP_INCLUDE_CRYPTO_DES
#include LWIP_INCLUDE_CRYPTO_DES
#endif
#ifdef LWIP_INCLUDE_CRYPTO_AES
#include LWIP_INCLUDE_CRYPTO_AES
#endif
#endif
#ifdef LWIP_SNMP_V3_CRYPTO
#if !defined(LWIP_MD5_HMAC_HANDLE) || !defined(LWIP_MD5_HMAC_INIT) || \
!defined(LWIP_MD5_HMAC_UPDATE) || !defined(LWIP_MD5_HMAC_FINAL)
#error LWIP_SNMP_V3_CRYPTO requires MD5 HMAC
#endif
#if !defined(LWIP_SHA_HMAC_HANDLE) || !defined(LWIP_SHA_HMAC_INIT) || \
!defined(LWIP_SHA_HMAC_UPDATE) || !defined(LWIP_SHA_HMAC_FINAL)
#error LWIP_SNMP_V3_CRYPTO requires SHA HMAC
#endif
#if !defined(LWIP_DES_CBC_ENCRYPT_HANDLE) || !defined(LWIP_DES_CBC_ENCRYPT_INIT) || \
!defined(LWIP_DES_CBC_ENCRYPT_UPDATE) || !defined(LWIP_DES_CBC_ENCRYPT_FINAL) || \
!defined(LWIP_DES_CBC_DECRYPT_HANDLE) || !defined(LWIP_DES_CBC_DECRYPT_INIT) || \
!defined(LWIP_DES_CBC_DECRYPT_UPDATE) || !defined(LWIP_DES_CBC_DECRYPT_FINAL)
#error LWIP_SNMP_V3_CRYPTO requires DES CBC
#endif
#if !defined(LWIP_AES_CFB_ENCRYPT_HANDLE) || !defined(LWIP_AES_CFB_ENCRYPT_INIT) || \
!defined(LWIP_AES_CFB_ENCRYPT_UPDATE) || !defined(LWIP_AES_CFB_ENCRYPT_FINAL) || \
!defined(LWIP_AES_CFB_DECRYPT_HANDLE) || !defined(LWIP_AES_CFB_DECRYPT_INIT) || \
!defined(LWIP_AES_CFB_DECRYPT_UPDATE) || !defined(LWIP_AES_CFB_DECRYPT_FINAL)
#error LWIP_SNMP_V3_CRYPTO requires AES CFB
#endif
#endif
#define SNMP_MAX_TIME_BOOT 2147483647UL
/* Engine ID, as specified in RFC3411 */
const char*
snmpv3_get_engine_id(void)
{
return LWIP_SNMPV3_GET_ENGINE_ID();
}
/* Has to reset boots, see below */
void
snmpv3_engine_id_changed(void)
{
LWIP_SNMPV3_SET_ENGINE_BOOTS(0);
}
/* According to RFC3414 2.2.2.
*
* The number of times that the SNMP engine has
* (re-)initialized itself since snmpEngineID
* was last configured.
*/
u32_t
snmpv3_get_engine_boots(void)
{
if (LWIP_SNMPV3_GET_ENGINE_BOOTS() == 0 ||
LWIP_SNMPV3_GET_ENGINE_BOOTS() < SNMP_MAX_TIME_BOOT) {
return LWIP_SNMPV3_GET_ENGINE_BOOTS();
}
LWIP_SNMPV3_SET_ENGINE_BOOTS(SNMP_MAX_TIME_BOOT);
return LWIP_SNMPV3_GET_ENGINE_BOOTS();
}
/* RFC3414 2.2.2.
*
* Once the timer reaches 2147483647 it gets reset to zero and the
* engine boot ups get incremented.
*/
u32_t
snmpv3_get_engine_time(void)
{
if (LWIP_SNMPV3_GET_ENGINE_TIME() >= SNMP_MAX_TIME_BOOT) {
LWIP_SNMPV3_RESET_ENGINE_TIME();
if (LWIP_SNMPV3_GET_ENGINE_BOOTS() < SNMP_MAX_TIME_BOOT - 1) {
LWIP_SNMPV3_SET_ENGINE_BOOTS(LWIP_SNMPV3_GET_ENGINE_BOOTS() + 1);
} else {
LWIP_SNMPV3_SET_ENGINE_BOOTS(SNMP_MAX_TIME_BOOT);
}
}
return LWIP_SNMPV3_GET_ENGINE_TIME();
}
#ifdef LWIP_SNMP_V3_CRYPTO
err_t
snmpv3_auth(struct snmp_pbuf_stream* stream, u16_t length,
const u8_t* key, u8_t algo, u8_t* hmac_out)
{
u32_t i;
u8_t byte;
struct snmp_pbuf_stream read_stream;
snmp_pbuf_stream_init(&read_stream, stream->pbuf, stream->offset,
stream->length);
if (algo == SNMP_V3_AUTH_ALGO_MD5) {
LWIP_MD5_HMAC_HANDLE mh;
if (LWIP_MD5_HMAC_INIT(&mh, key, SNMP_V3_MD5_LEN))
return ERR_ARG;
for (i = 0; i < length; i++) {
if (snmp_pbuf_stream_read(&read_stream, &byte))
return ERR_ARG;
if (LWIP_MD5_HMAC_UPDATE(&mh, &byte, 1))
return ERR_ARG;
}
if (LWIP_MD5_HMAC_FINAL(&mh, hmac_out))
return ERR_ARG;
} else if (algo == SNMP_V3_AUTH_ALGO_SHA) {
LWIP_SHA_HMAC_HANDLE sh;
if (LWIP_SHA_HMAC_INIT(&sh, key, SNMP_V3_SHA_LEN))
return ERR_ARG;
for (i = 0; i < length; i++) {
if (snmp_pbuf_stream_read(&read_stream, &byte))
return ERR_ARG;
if (LWIP_SHA_HMAC_UPDATE(&sh, &byte, 1))
return ERR_ARG;
}
if (LWIP_SHA_HMAC_FINAL(&sh, hmac_out))
return ERR_ARG;
} else
return ERR_ARG;
return ERR_OK;
}
err_t
snmpv3_crypt(struct snmp_pbuf_stream* stream, u16_t length,
const u8_t* key, const u8_t* priv_param, const u32_t engine_boots,
const u32_t engine_time, u8_t algo, u8_t mode)
{
u8_t in_bytes[8];
u8_t out_bytes[8];
u8_t iv_local[16];
u32_t i, j;
/* RFC 3414 mandates padding for DES */
if (algo == SNMP_V3_PRIV_ALGO_DES) {
if (length % 8)
return ERR_ARG;
for (i = 0; i < 8; i++)
iv_local[i] = priv_param[i] ^ key[i + 8];
} else if (algo == SNMP_V3_PRIV_ALGO_AES) {
/*
* IV is the big endian concatenation of boots,
* uptime and priv param - see RFC3826.
*/
iv_local[0 + 0] = (engine_boots >> 24) & 0xFF;
iv_local[0 + 1] = (engine_boots >> 16) & 0xFF;
iv_local[0 + 2] = (engine_boots >> 8) & 0xFF;
iv_local[0 + 3] = (engine_boots >> 0) & 0xFF;
iv_local[4 + 0] = (engine_time >> 24) & 0xFF;
iv_local[4 + 1] = (engine_time >> 16) & 0xFF;
iv_local[4 + 2] = (engine_time >> 8) & 0xFF;
iv_local[4 + 3] = (engine_time >> 0) & 0xFF;
memcpy(iv_local + 8, priv_param, 8);
}
struct snmp_pbuf_stream read_stream;
struct snmp_pbuf_stream write_stream;
snmp_pbuf_stream_init(&read_stream, stream->pbuf, stream->offset,
stream->length);
snmp_pbuf_stream_init(&write_stream, stream->pbuf, stream->offset,
stream->length);
if (algo == SNMP_V3_PRIV_ALGO_DES && mode == SNMP_V3_PRIV_MODE_ENCRYPT) {
LWIP_DES_CBC_ENCRYPT_HANDLE handle;
LWIP_DES_CBC_ENCRYPT_INIT(&handle, key);
for (i = 0; i < length; i += 8) {
for (j = 0; j < 8; j++)
snmp_pbuf_stream_read(&read_stream, &in_bytes[j]);
LWIP_DES_CBC_ENCRYPT_UPDATE(&handle, 8, iv_local, in_bytes, out_bytes);
for (j = 0; j < 8; j++)
snmp_pbuf_stream_write(&write_stream, out_bytes[j]);
}
LWIP_DES_CBC_ENCRYPT_FINAL(&handle);
} else if (algo == SNMP_V3_PRIV_ALGO_DES && mode == SNMP_V3_PRIV_MODE_DECRYPT) {
LWIP_DES_CBC_DECRYPT_HANDLE handle;
LWIP_DES_CBC_DECRYPT_INIT(&handle, key);
for (i = 0; i < length; i += 8) {
for (j = 0; j < 8; j++)
snmp_pbuf_stream_read(&read_stream, &in_bytes[j]);
LWIP_DES_CBC_DECRYPT_UPDATE(&handle, 8, iv_local, in_bytes, out_bytes);
for (j = 0; j < 8; j++)
snmp_pbuf_stream_write(&write_stream, out_bytes[j]);
}
LWIP_DES_CBC_DECRYPT_FINAL(&handle);
} else if (algo == SNMP_V3_PRIV_ALGO_AES && mode == SNMP_V3_PRIV_MODE_ENCRYPT) {
size_t iv_offset = 0;
LWIP_AES_CFB_ENCRYPT_HANDLE handle;
LWIP_AES_CFB_ENCRYPT_INIT(&handle, key);
for (i = 0; i < length; i++) {
snmp_pbuf_stream_read(&read_stream, &in_bytes[0]);
LWIP_AES_CFB_ENCRYPT_UPDATE(&handle, 1, &iv_offset, iv_local, in_bytes,
out_bytes);
snmp_pbuf_stream_write(&write_stream, out_bytes[0]);
}
LWIP_AES_CFB_ENCRYPT_FINAL(&handle);
} else if (algo == SNMP_V3_PRIV_ALGO_AES && mode == SNMP_V3_PRIV_MODE_DECRYPT) {
size_t iv_off = 0;
LWIP_AES_CFB_DECRYPT_HANDLE handle;
LWIP_AES_CFB_DECRYPT_INIT(&handle, key);
for (i = 0; i < length; i++) {
snmp_pbuf_stream_read(&read_stream, &in_bytes[0]);
LWIP_AES_CFB_DECRYPT_UPDATE(&handle, 1, &iv_off, iv_local, in_bytes,
out_bytes);
snmp_pbuf_stream_write(&write_stream, out_bytes[0]);
}
LWIP_AES_CFB_DECRYPT_FINAL(&handle);
} else
return ERR_ARG;
return ERR_OK;
}
/* This function ignores the byte order suggestion in RFC3414
* since it simply doesn't influence the effectiveness of an IV.
*
* Implementing RFC3826 priv param algorithm if LWIP_RAND is available.
*
* TODO: This is a potential thread safety issue.
*/
err_t
snmpv3_build_priv_param(u8_t* priv_param)
{
#ifdef LWIP_RAND /* Based on RFC3826 */
static u8_t init;
static u32_t priv1, priv2;
/* Lazy initialisation */
if (init == 0) {
init = 1;
priv1 = LWIP_RAND();
priv2 = LWIP_RAND();
}
memcpy(&priv_param[0], &priv1, sizeof(priv1));
memcpy(&priv_param[4], &priv2, sizeof(priv2));
/* Emulate 64bit increment */
priv1++;
if (!priv1) /* Overflow */
priv2++;
#else /* Based on RFC3414 */
static u32_t ctr;
u32_t boots = LWIP_SNMPV3_GET_ENGINE_BOOTS();
memcpy(&priv_param[0], &boots, 4);
memcpy(&priv_param[4], &ctr, 4);
ctr++;
#endif
return ERR_OK;
}
#endif /* LWIP_SNMP_V3_CRYPTO */
#endif

124
src/apps/snmp/snmpv3.h Normal file
View File

@ -0,0 +1,124 @@
/**
* @file
* Additional SNMPv3 functionality RFC3414 and RFC3826.
*/
/*
* Copyright (c) 2016 Elias Oenal.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification,
* are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
* 3. The name of the author may not be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
* SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
* OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
* IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
* OF SUCH DAMAGE.
*
* Author: Elias Oenal <lwip@eliasoenal.com>
*/
#ifndef LWIP_HDR_APPS_SNMP_V3_H
#define LWIP_HDR_APPS_SNMP_V3_H
#include "lwip/apps/snmp_opts.h"
#if LWIP_SNMP && LWIP_SNMP_V3
#include "snmp_pbuf_stream.h"
#ifndef LWIP_SNMPV3_GET_ENGINE_BOOTS
/* #warning RFC3414 complicance requires a persistent boot count */
#define LWIP_SNMPV3_GET_ENGINE_BOOTS() 0
#endif
#ifndef LWIP_SNMPV3_SET_ENGINE_BOOTS
/* #warning RFC3414 complicance requires a method to set boot count */
#define LWIP_SNMPV3_SET_ENGINE_BOOTS(val)
#endif
#ifndef LWIP_SNMPV3_GET_ENGINE_TIME
/* #warning RFC3414 complicance requires the uptime to count until 2147483647 */
#define LWIP_SNMPV3_GET_ENGINE_TIME() (sys_now() / 10)
#endif
#ifndef LWIP_SNMPV3_RESET_ENGINE_TIME
/* #warning RFC3414 complicance requires a method to reset uptime */
#define LWIP_SNMPV3_RESET_ENGINE_TIME()
#endif
#ifndef LWIP_SNMPV3_GET_ENGINE_ID
/* #warning RFC3414 complicance requires an engine ID */
/* Using the one from the test vectors from RFC3414 */
#define LWIP_SNMPV3_GET_ENGINE_ID() "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02"
#endif
#ifndef LWIP_SNMPV3_GET_ENGINE_ID_LEN
/* #warning RFC3414 complicance requires an engine ID */
#define LWIP_SNMPV3_GET_ENGINE_ID_LEN() 12
#endif
#ifndef LWIP_SNMPV3_GET_USER
/* #warning Implement user handling */
/* @param username is a pointer to a string.
* @param auth_algo is a pointer to u8_t. The implementation has to set this if user was found.
* @param auth_key is a pointer to a pointer to a string. Implementation has to set this if user was found.
* @param priv_algo is a pointer to u8_t. The implementation has to set this if user was found.
* @param priv_key is a pointer to a pointer to a string. Implementation has to set this if user was found.
*/
/* Dummy implementation, pretend the user was found if cryptography isn't used */
#define LWIP_SNMPV3_GET_USER(username, auth_algo, auth_key, priv_algo, priv_key) ((auth_algo || auth_key \
|| priv_algo || priv_key)?1:0)
#endif
/* According to RFC 3411 */
#define SNMP_V3_MAX_ENGINE_ID_LENGTH 32
#define SNMP_V3_MAX_USER_LENGTH 32
#define SNMP_V3_MAX_AUTH_PARAM_LENGTH 12
#define SNMP_V3_MAX_PRIV_PARAM_LENGTH 8
#define SNMP_V3_AUTH_FLAG 0x01
#define SNMP_V3_PRIV_FLAG 0x02
#define SNMP_V3_MD5_LEN 16
#define SNMP_V3_SHA_LEN 20
#define SNMP_V3_AUTH_ALGO_INVAL 0
#define SNMP_V3_AUTH_ALGO_MD5 1
#define SNMP_V3_AUTH_ALGO_SHA 2
#define SNMP_V3_PRIV_ALGO_INVAL 0
#define SNMP_V3_PRIV_ALGO_DES 1
#define SNMP_V3_PRIV_ALGO_AES 2
#define SNMP_V3_PRIV_MODE_DECRYPT 0
#define SNMP_V3_PRIV_MODE_ENCRYPT 1
const char* snmpv3_get_engine_id(void);
void snmpv3_set_engine_id(const char* id);
u32_t snmpv3_get_engine_boots(void);
u32_t snmpv3_get_engine_time(void);
void snmpv3_engine_id_changed(void);
err_t snmpv3_auth(struct snmp_pbuf_stream* stream, u16_t length, const u8_t* key, u8_t algo, u8_t* hmac_out);
err_t snmpv3_crypt(struct snmp_pbuf_stream* stream, u16_t length, const u8_t* key,
const u8_t* priv_param, const u32_t engine_boots, const u32_t engine_time, u8_t algo, u8_t mode);
err_t snmpv3_build_priv_param(u8_t* priv_param);
#endif
#endif /* LWIP_HDR_APPS_SNMP_V3_H */