From 617c89972602e5d19253662326b2d0860811ebf8 Mon Sep 17 00:00:00 2001
From: Marco <marco.veeneman@schleifenbauer.eu>
Date: Wed, 1 Mar 2017 16:54:18 +0100
Subject: [PATCH] Implemented usm mib.

---
 src/apps/snmp/snmp_snmpv2_usm.c         | 417 ++++++++++++++++++++++++++++++++
 src/include/lwip/apps/snmp_snmpv2_usm.h |  24 ++
 src/include/lwip/apps/snmpv3.h          |   9 +
 3 files changed, 450 insertions(+)
 create mode 100644 src/apps/snmp/snmp_snmpv2_usm.c
 create mode 100644 src/include/lwip/apps/snmp_snmpv2_usm.h

diff --git a/src/apps/snmp/snmp_snmpv2_usm.c b/src/apps/snmp/snmp_snmpv2_usm.c
new file mode 100644
index 0000000..965ea84
--- /dev/null
+++ b/src/apps/snmp/snmp_snmpv2_usm.c
@@ -0,0 +1,417 @@
+/*
+Generated by LwipMibCompiler
+*/
+
+#include "lwip/apps/snmp_opts.h"
+#if LWIP_SNMP
+
+#include "lwip/apps/snmp_snmpv2_usm.h"
+#include "lwip/apps/snmp.h"
+#include "lwip/apps/snmp_core.h"
+#include "lwip/apps/snmp_scalar.h"
+#include "lwip/apps/snmp_table.h"
+#include "lwip/apps/snmpv3.h"
+#include "snmpv3_priv.h"
+
+#include "lwip/apps/snmp_snmpv2_framework.h"
+
+#include <string.h>
+
+/* --- usmUser 1.3.6.1.6.3.15.1.2 ----------------------------------------------------- */
+
+static const struct snmp_oid_range usmUserTable_oid_ranges[] = {
+  { 0, 0xff }, { 0, 0xff }, { 0, 0xff }, { 0, 0xff },
+  { 0, 0xff }, { 0, 0xff }, { 0, 0xff }, { 0, 0xff },
+  { 0, 0xff }, { 0, 0xff }, { 0, 0xff }, { 0, 0xff },
+  { 0, 0xff }, { 0, 0xff }, { 0, 0xff }, { 0, 0xff },
+  { 0, 0xff }, { 0, 0xff }, { 0, 0xff }, { 0, 0xff },
+  { 0, 0xff }, { 0, 0xff }, { 0, 0xff }, { 0, 0xff },
+  { 0, 0xff }, { 0, 0xff }, { 0, 0xff }, { 0, 0xff },
+  { 0, 0xff }, { 0, 0xff }, { 0, 0xff }, { 0, 0xff }
+};
+
+static u8_t snmp_engineid_to_oid(const char *engineid, u32_t *oid, u32_t len)
+{
+  u8_t i;
+
+  for (i = 0; i < len; i++) {
+    oid[i] = engineid[i];
+  }
+
+  return len;
+}
+
+static u8_t snmp_oid_to_name(char *name, const u32_t *oid, u8_t len)
+{
+  u8_t i;
+
+  for (i = 0; i < len; i++) {
+    name[i] = oid[i];
+  }
+
+  return len;
+}
+
+static u8_t snmp_name_to_oid(const char *name, u32_t *oid, u8_t len)
+{
+  u8_t i;
+
+  for (i = 0; i < len; i++) {
+	oid[i] = name[i];
+  }
+
+  return len;
+}
+
+static const struct snmp_obj_id *snmp_auth_algo_to_oid(u8_t algo)
+{
+  if (algo == SNMP_V3_AUTH_ALGO_MD5) {
+    return &usmHMACMD5AuthProtocol;
+  }
+  else if (algo ==  SNMP_V3_AUTH_ALGO_SHA) {
+    return &usmHMACMD5AuthProtocol;
+  }
+
+  return &usmNoAuthProtocol;
+}
+
+static const struct snmp_obj_id *snmp_priv_algo_to_oid(u8_t algo)
+{
+  if (algo == SNMP_V3_PRIV_ALGO_DES) {
+    return &usmDESPrivProtocol;
+  }
+  else if (algo == SNMP_V3_PRIV_ALGO_AES) {
+    return &usmAESPrivProtocol;
+  }
+
+  return &usmNoPrivProtocol;
+}
+
+char username[32];
+
+static snmp_err_t usmusertable_get_instance(const u32_t *column, const u32_t *row_oid, u8_t row_oid_len, struct snmp_node_instance *cell_instance)
+{
+  const char *engineid;
+  u8_t eid_len;
+
+  u32_t engineid_oid[SNMP_V3_MAX_ENGINE_ID_LENGTH];
+
+  u8_t name_len;
+  u8_t engineid_len;
+
+  u8_t name_start;
+  u8_t engineid_start;
+
+  snmpv3_get_engine_id(&engineid, &eid_len);
+
+  engineid_len = row_oid[0];
+  engineid_start = 1;
+
+  if (engineid_len != eid_len) {
+    /* EngineID length does not match! */
+    return SNMP_ERR_NOSUCHINSTANCE;
+  }
+
+  if (engineid_len > row_oid_len) {
+    /* row OID doesn't contain enough data according to engineid_len.*/
+    return SNMP_ERR_NOSUCHINSTANCE;
+  }
+
+  /* check if incoming OID length and if values are in plausible range */
+  if (!snmp_oid_in_range(&row_oid[engineid_start], engineid_len, usmUserTable_oid_ranges, engineid_len)) {
+    return SNMP_ERR_NOSUCHINSTANCE;
+  }
+
+  snmp_engineid_to_oid(engineid, engineid_oid, engineid_len);
+
+  /* Verify EngineID */
+  if (snmp_oid_equal(&row_oid[engineid_start], engineid_len, engineid_oid, engineid_len)) {
+    return SNMP_ERR_NOSUCHINSTANCE;
+  }
+
+  name_len = row_oid[engineid_start + engineid_len];
+  name_start = engineid_start + engineid_len + 1;
+
+  if (name_len > SNMP_V3_MAX_USER_LENGTH) {
+    /* specified name is too long */
+    return SNMP_ERR_NOSUCHINSTANCE;
+  }
+
+  if (1 + engineid_len + 1 + name_len != row_oid_len) {
+    /* Length of EngineID and name does not match row oid length. (+2 for length fields)*/
+    return SNMP_ERR_NOSUCHINSTANCE;
+  }
+
+  /* check if incoming OID length and if values are in plausible range */
+  if (!snmp_oid_in_range(&row_oid[name_start], name_len, usmUserTable_oid_ranges, name_len)) {
+    return SNMP_ERR_NOSUCHINSTANCE;
+  }
+
+  /* Verify if user exists */
+  memset(username, 0, sizeof(username));
+  snmp_oid_to_name(username, &row_oid[name_start], name_len);
+  if (snmpv3_get_user(username, NULL, NULL, NULL, NULL) != ERR_OK) {
+    return SNMP_ERR_NOSUCHINSTANCE;
+  }
+
+  /* Save name in reference pointer to make it easier to handle later on */
+  cell_instance->reference.ptr = username;
+  cell_instance->reference_len = name_len;
+
+  /* user was found */
+  return SNMP_ERR_NOERROR;
+}
+
+/*
+ * valid oid options
+ * <oid>
+ * <oid>.<EngineID length>
+ * <oid>.<EngineID length>.<partial EngineID>
+ * <oid>.<EngineID length>.<EngineID>
+ * <oid>.<EngineID length>.<EngineID>.<UserName length>
+ * <oid>.<EngineID length>.<EngineID>.<UserName length>.<partial UserName>
+ * <oid>.<EngineID length>.<EngineID>.<UserName length>.<UserName>
+ *
+ */
+static snmp_err_t usmusertable_get_next_instance(const u32_t *column, struct snmp_obj_id *row_oid, struct snmp_node_instance *cell_instance)
+{
+  const char *engineid;
+  u8_t eid_len;
+
+  u32_t engineid_oid[SNMP_V3_MAX_ENGINE_ID_LENGTH];
+
+  u8_t name_len;
+  u8_t engineid_len;
+
+  u8_t name_start;
+  u8_t engineid_start = 1;
+  u8_t i;
+
+  struct snmp_next_oid_state state;
+
+  u32_t result_temp[LWIP_ARRAYSIZE(usmUserTable_oid_ranges)];
+
+  snmpv3_get_engine_id(&engineid, &eid_len);
+
+  /* If EngineID might be given */
+  if (row_oid->len > 0) {
+    engineid_len = row_oid->id[0];
+    engineid_start = 1;
+
+    if (engineid_len != eid_len) {
+	  /* EngineID length does not match! */
+	  return SNMP_ERR_NOSUCHINSTANCE;
+    }
+
+    if (engineid_len > row_oid->len) {
+      /* Verify partial EngineID */
+      snmp_engineid_to_oid(engineid, engineid_oid, row_oid->len - 1);
+      if (!snmp_oid_equal(&row_oid->id[engineid_start], row_oid->len - 1, engineid_oid, row_oid->len - 1)) {
+	    return SNMP_ERR_NOSUCHINSTANCE;
+      }
+    }
+    else {
+      /* Verify complete EngineID */
+      snmp_engineid_to_oid(engineid, engineid_oid, engineid_len);
+      if (!snmp_oid_equal(&row_oid->id[engineid_start], engineid_len, engineid_oid, engineid_len)) {
+	    return SNMP_ERR_NOSUCHINSTANCE;
+      }
+    }
+
+    /* At this point, the given EngineID (partially) matches the local EngineID.*/
+
+    /* If name might also be given */
+    if (row_oid->len > engineid_start + engineid_len) {
+      name_len = row_oid->id[engineid_start + engineid_len];
+      name_start = engineid_start + engineid_len + 1;
+
+      if (name_len > SNMP_V3_MAX_USER_LENGTH) {
+	    /* specified name is too long, max length is 32 according to mib file.*/
+	    return SNMP_ERR_NOSUCHINSTANCE;
+      }
+
+      if (row_oid->len < engineid_len + name_len + 2) {
+        /* Partial name given according to oid.*/
+        u8_t tmplen = row_oid->len - engineid_len - 2;
+    	if (!snmp_oid_in_range(&row_oid->id[name_start], tmplen, usmUserTable_oid_ranges, tmplen)) {
+          return SNMP_ERR_NOSUCHINSTANCE;
+        }
+      }
+      else {
+        /* Full name given according to oid. Also test for too much data.*/
+        u8_t tmplen = row_oid->len - engineid_len - 2;
+        if (!snmp_oid_in_range(&row_oid->id[name_start], name_len, usmUserTable_oid_ranges, tmplen)) {
+	      return SNMP_ERR_NOSUCHINSTANCE;
+        }
+      }
+
+      /* At this point the EngineID and (partial) UserName match the local EngineID and UserName.*/
+    }
+  }
+
+  /* init struct to search next oid */
+  snmp_next_oid_init(&state, row_oid->id, row_oid->len, result_temp, LWIP_ARRAYSIZE(usmUserTable_oid_ranges));
+
+  for (i = 0; i < snmpv3_get_amount_of_users(); i++) {
+    u32_t test_oid[LWIP_ARRAYSIZE(usmUserTable_oid_ranges)];
+
+    test_oid[0] = eid_len;
+    snmp_engineid_to_oid(engineid, &test_oid[1], eid_len);
+
+    snmpv3_get_username(username, i);
+
+    test_oid[1 + eid_len] = strlen(username);
+    snmp_name_to_oid(username, &test_oid[2 + eid_len], strlen(username));
+
+    /* check generated OID: is it a candidate for the next one? */
+    snmp_next_oid_check(&state, test_oid, 1 + eid_len + 1 + strlen(username), (void *)(uintptr_t)i);
+  }
+
+  /* did we find a next one? */
+  if (state.status == SNMP_NEXT_OID_STATUS_SUCCESS) {
+    snmp_oid_assign(row_oid, state.next_oid, state.next_oid_len);
+    /* store username for subsequent operations (get/test/set) */
+    memset(username, 0, sizeof(username));
+    snmpv3_get_username(username, (uintptr_t)state.reference);
+    cell_instance->reference.ptr = username;
+    cell_instance->reference_len = strlen(username);
+    return SNMP_ERR_NOERROR;
+  }
+
+  /* not found */
+  return SNMP_ERR_NOSUCHINSTANCE;
+}
+
+static s16_t usmusertable_get_value(struct snmp_node_instance *cell_instance, void *value)
+{
+  u8_t u8;
+
+  switch (SNMP_TABLE_GET_COLUMN_FROM_OID(cell_instance->instance_oid.id)) {
+  case 3: /* usmUserSecurityName */
+    MEMCPY(value, cell_instance->reference.ptr, cell_instance->reference_len);
+    return cell_instance->reference_len;
+    break;
+  case 4: /* usmUserCloneFrom */
+    MEMCPY(value, snmp_zero_dot_zero.id, snmp_zero_dot_zero.len * sizeof(u32_t));
+    return snmp_zero_dot_zero.len * sizeof(u32_t);
+  case 5: /* usmUserAuthProtocol */
+    {
+      const struct snmp_obj_id *auth_algo;
+      snmpv3_get_user(cell_instance->reference.ptr, &u8, NULL, NULL, NULL);
+      auth_algo = snmp_auth_algo_to_oid(u8);
+      MEMCPY(value, auth_algo->id, auth_algo->len * sizeof(u32_t));
+      return auth_algo->len * sizeof(u32_t);
+    }
+  case 6: /* usmUserAuthKeyChange */
+    return 0;
+  case 7: /* usmUserOwnAuthKeyChange */
+    return 0;
+  case 8: /* usmUserPrivProtocol */
+    {
+      const struct snmp_obj_id *priv_algo;
+      snmpv3_get_user(cell_instance->reference.ptr, NULL, NULL, &u8, NULL);
+      priv_algo = snmp_priv_algo_to_oid(u8);
+      MEMCPY(value, priv_algo->id, priv_algo->len * sizeof(u32_t));
+      return priv_algo->len * sizeof(u32_t);
+    }
+  case 9: /* usmUserPrivKeyChange */
+    return 0;
+  case 10: /* usmUserOwnPrivKeyChange */
+    return 0;
+  case 11: /* usmUserPublic */
+    /* TODO: Implement usmUserPublic */
+    return 0;
+  case 12: /* usmUserStorageType */
+    snmpv3_get_user_storagetype(cell_instance->reference.ptr, &u8);
+    *(s32_t*)value = u8;
+    return sizeof(s32_t);
+  case 13: /* usmUserStatus */
+    *(s32_t*)value = 1; /* active */
+    return sizeof(s32_t);
+  default:
+    LWIP_DEBUGF(SNMP_MIB_DEBUG,("usmusertable_get_value(): unknown id: %"S32_F"\n", SNMP_TABLE_GET_COLUMN_FROM_OID(cell_instance->instance_oid.id)));
+    return 0;
+  }
+}
+
+/* --- usmMIBObjects 1.3.6.1.6.3.15.1 ----------------------------------------------------- */
+static s16_t usmstats_scalars_get_value(const struct snmp_scalar_array_node_def *node, void *value)
+{
+  u32_t *uint_ptr = (u32_t*)value;
+  switch (node->oid) {
+  case 1: /* usmStatsUnsupportedSecLevels */
+    *uint_ptr = snmp_stats.unsupportedseclevels;
+    break;
+  case 2: /* usmStatsNotInTimeWindows */
+    *uint_ptr = snmp_stats.notintimewindows;
+    break;
+  case 3: /* usmStatsUnknownUserNames */
+    *uint_ptr = snmp_stats.unknownusernames;
+    break;
+  case 4: /* usmStatsUnknownEngineIDs */
+    *uint_ptr = snmp_stats.unknownengineids;
+    break;
+  case 5: /* usmStatsWrongDigests */
+    *uint_ptr = snmp_stats.wrongdigests;
+    break;
+  case 6: /* usmStatsDecryptionErrors */
+    *uint_ptr = snmp_stats.decryptionerrors;
+    break;
+  default:
+    LWIP_DEBUGF(SNMP_MIB_DEBUG,("usmstats_scalars_get_value(): unknown id: %"S32_F"\n", node->oid));
+    return 0;
+  }
+
+  return sizeof(*uint_ptr);
+}
+
+/* --- snmpUsmMIB  ----------------------------------------------------- */
+
+/* --- usmUser 1.3.6.1.6.3.15.1.2 ----------------------------------------------------- */
+
+static const struct snmp_table_col_def usmusertable_columns[] = {
+  {3,  SNMP_ASN1_TYPE_OCTET_STRING, SNMP_NODE_INSTANCE_READ_ONLY}, /* usmUserSecurityName */
+  {4,  SNMP_ASN1_TYPE_OBJECT_ID,    SNMP_NODE_INSTANCE_READ_ONLY}, /* usmUserCloneFrom */
+  {5,  SNMP_ASN1_TYPE_OBJECT_ID,    SNMP_NODE_INSTANCE_READ_ONLY}, /* usmUserAuthProtocol */
+  {6,  SNMP_ASN1_TYPE_OCTET_STRING, SNMP_NODE_INSTANCE_READ_ONLY}, /* usmUserAuthKeyChange */
+  {7,  SNMP_ASN1_TYPE_OCTET_STRING, SNMP_NODE_INSTANCE_READ_ONLY}, /* usmUserOwnAuthKeyChange */
+  {8,  SNMP_ASN1_TYPE_OBJECT_ID,    SNMP_NODE_INSTANCE_READ_ONLY}, /* usmUserPrivProtocol */
+  {9,  SNMP_ASN1_TYPE_OCTET_STRING, SNMP_NODE_INSTANCE_READ_ONLY}, /* usmUserPrivKeyChange */
+  {10, SNMP_ASN1_TYPE_OCTET_STRING, SNMP_NODE_INSTANCE_READ_ONLY}, /* usmUserOwnPrivKeyChange */
+  {11, SNMP_ASN1_TYPE_OCTET_STRING, SNMP_NODE_INSTANCE_READ_ONLY}, /* usmUserPublic */
+  {12, SNMP_ASN1_TYPE_INTEGER,      SNMP_NODE_INSTANCE_READ_ONLY}, /* usmUserStorageType */
+  {13, SNMP_ASN1_TYPE_INTEGER,      SNMP_NODE_INSTANCE_READ_ONLY}, /* usmUserStatus */
+};
+static const struct snmp_table_node usmusertable = SNMP_TABLE_CREATE(2, usmusertable_columns, usmusertable_get_instance, usmusertable_get_next_instance, usmusertable_get_value, NULL, NULL);
+
+static const struct snmp_node *const usmuser_subnodes[] = {
+  &usmusertable.node.node
+};
+static const struct snmp_tree_node usmuser_treenode = SNMP_CREATE_TREE_NODE(2, usmuser_subnodes);
+
+/* --- usmMIBObjects 1.3.6.1.6.3.15.1 ----------------------------------------------------- */
+static const struct snmp_scalar_array_node_def usmstats_scalars_nodes[] = {
+  {1, SNMP_ASN1_TYPE_COUNTER, SNMP_NODE_INSTANCE_READ_ONLY}, /* usmStatsUnsupportedSecLevels */
+  {2, SNMP_ASN1_TYPE_COUNTER, SNMP_NODE_INSTANCE_READ_ONLY}, /* usmStatsNotInTimeWindows */
+  {3, SNMP_ASN1_TYPE_COUNTER, SNMP_NODE_INSTANCE_READ_ONLY}, /* usmStatsUnknownUserNames */
+  {4, SNMP_ASN1_TYPE_COUNTER, SNMP_NODE_INSTANCE_READ_ONLY}, /* usmStatsUnknownEngineIDs */
+  {5, SNMP_ASN1_TYPE_COUNTER, SNMP_NODE_INSTANCE_READ_ONLY}, /* usmStatsWrongDigests */
+  {6, SNMP_ASN1_TYPE_COUNTER, SNMP_NODE_INSTANCE_READ_ONLY}, /* usmStatsDecryptionErrors */
+};
+static const struct snmp_scalar_array_node usmstats_scalars = SNMP_SCALAR_CREATE_ARRAY_NODE(1, usmstats_scalars_nodes, usmstats_scalars_get_value, NULL, NULL);
+
+static const struct snmp_node *const usmmibobjects_subnodes[] = {
+  &usmstats_scalars.node.node,
+  &usmuser_treenode.node
+};
+static const struct snmp_tree_node usmmibobjects_treenode = SNMP_CREATE_TREE_NODE(1, usmmibobjects_subnodes);
+
+/* --- snmpUsmMIB  ----------------------------------------------------- */
+static const struct snmp_node *const snmpusmmib_subnodes[] = {
+  &usmmibobjects_treenode.node
+};
+static const struct snmp_tree_node snmpusmmib_root = SNMP_CREATE_TREE_NODE(15, snmpusmmib_subnodes);
+static const u32_t snmpusmmib_base_oid[] = {1,3,6,1,6,3,15};
+const struct snmp_mib snmpusmmib = {snmpusmmib_base_oid, LWIP_ARRAYSIZE(snmpusmmib_base_oid), &snmpusmmib_root.node};
+
+#endif /* LWIP_SNMP */
diff --git a/src/include/lwip/apps/snmp_snmpv2_usm.h b/src/include/lwip/apps/snmp_snmpv2_usm.h
new file mode 100644
index 0000000..e5d77c7
--- /dev/null
+++ b/src/include/lwip/apps/snmp_snmpv2_usm.h
@@ -0,0 +1,24 @@
+/*
+Generated by LwipMibCompiler
+*/
+
+#ifndef SNMP_USER_BASED_SM_MIB_H
+#define SNMP_USER_BASED_SM_MIB_H SNMP_USER_BASED_SM_MIB_H
+
+#include "lwip/apps/snmp_opts.h"
+#if LWIP_SNMP
+
+#ifdef __cplusplus
+extern "C" {
+#endif /* __cplusplus */
+
+#include "lwip/apps/snmp_core.h"
+
+extern const struct snmp_mib snmpusmmib;
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+
+#endif /* LWIP_SNMP */
+#endif /* SNMP_USER_BASED_SM_MIB_H */
diff --git a/src/include/lwip/apps/snmpv3.h b/src/include/lwip/apps/snmpv3.h
index c99fed4..0e04f78 100644
--- a/src/include/lwip/apps/snmpv3.h
+++ b/src/include/lwip/apps/snmpv3.h
@@ -51,6 +51,12 @@
 #define SNMP_V3_PRIV_MODE_DECRYPT  0
 #define SNMP_V3_PRIV_MODE_ENCRYPT  1
 
+#define SNMP_V3_USER_STORAGETYPE_OTHER        1
+#define SNMP_V3_USER_STORAGETYPE_VOLATILE     2
+#define SNMP_V3_USER_STORAGETYPE_NONVOLATILE  3
+#define SNMP_V3_USER_STORAGETYPE_PERMANENT    4
+#define SNMP_V3_USER_STORAGETYPE_READONLY     5
+
 /*
  * The following callback functions must be implemented by the application.
  * There is a dummy implementation in snmpv3_dummy.c.
@@ -66,6 +72,9 @@ u32_t snmpv3_get_engine_time(void);
 void snmpv3_reset_engine_time(void);
 
 err_t snmpv3_get_user(const char* username, u8_t *auth_algo, u8_t *auth_key, u8_t *priv_algo, u8_t *priv_key);
+u8_t snmpv3_get_amount_of_users(void);
+err_t snmpv3_get_user_storagetype(const char *username, u8_t *storagetype);
+err_t snmpv3_get_username(char *username, u8_t index);
 
 /* The following functions are provided by the SNMPv3 agent */
 
-- 
2.8.1.windows.1

