From bc21eb8d8aba5bb9a8bc82b15cbe4deb6c62ec29 Mon Sep 17 00:00:00 2001
From: Jan Breuer <jan.breuer@jaybee.cz>
Date: Fri, 12 May 2023 12:22:35 +0200
Subject: [PATCH] snmp_v3: add basic write access support

---
 contrib/examples/snmp/snmp_v3/snmpv3_dummy.c | 43 ++++++++++++++++++--
 contrib/examples/snmp/snmp_v3/snmpv3_dummy.h |  1 +
 src/apps/snmp/snmp_msg.c                     | 19 ++++++---
 src/include/lwip/apps/snmpv3.h               |  7 ++++
 4 files changed, 61 insertions(+), 9 deletions(-)

diff --git a/contrib/examples/snmp/snmp_v3/snmpv3_dummy.c b/contrib/examples/snmp/snmp_v3/snmpv3_dummy.c
index a7c53ed7..6c8691af 100644
--- a/contrib/examples/snmp/snmp_v3/snmpv3_dummy.c
+++ b/contrib/examples/snmp/snmp_v3/snmpv3_dummy.c
@@ -48,12 +48,13 @@ struct user_table_entry {
   u8_t               auth_key[20];
   snmpv3_priv_algo_t priv_algo;
   u8_t               priv_key[20];
+  snmpv3_user_access_t access;
 };
 
 static struct user_table_entry user_table[] = {
-  { "lwip", SNMP_V3_AUTH_ALGO_INVAL, "" , SNMP_V3_PRIV_ALGO_INVAL, "" },
-  { "piwl", SNMP_V3_AUTH_ALGO_INVAL, "" , SNMP_V3_PRIV_ALGO_INVAL, "" },
-  { "test", SNMP_V3_AUTH_ALGO_INVAL, "" , SNMP_V3_PRIV_ALGO_INVAL, "" }
+  { "lwip", SNMP_V3_AUTH_ALGO_INVAL, "" , SNMP_V3_PRIV_ALGO_INVAL, "", SNMP_V3_USER_ACCESS_READ_ONLY },
+  { "piwl", SNMP_V3_AUTH_ALGO_INVAL, "" , SNMP_V3_PRIV_ALGO_INVAL, "", SNMP_V3_USER_ACCESS_READ_ONLY },
+  { "test", SNMP_V3_AUTH_ALGO_INVAL, "" , SNMP_V3_PRIV_ALGO_INVAL, "", SNMP_V3_USER_ACCESS_READ_ONLY }
 };
 
 static char snmpv3_engineid[32];
@@ -253,6 +254,40 @@ snmpv3_set_user_priv_key(const char *username, const char *password)
   return ERR_VAL;
 }
 
+err_t
+snmpv3_set_user_access(const char *username, snmpv3_user_access_t access)
+{
+  struct user_table_entry *p = get_user(username);
+
+  if (p) {
+    p->access = access;
+    return ERR_OK;
+  }
+
+  return ERR_VAL;
+}
+
+/**
+ * @brief   Get access rights of the given username.
+ * @param[in] username  pointer to the username
+ * @param[out] access   access rights
+ *
+ * @return              ERR_OK if the user was found, ERR_VAL if not.
+ */
+err_t
+snmpv3_get_user_access(const char *username, snmpv3_user_access_t *access)
+{
+  struct user_table_entry *p = get_user(username);
+
+  if (p) {
+    *access = p->access;
+    return ERR_OK;
+  }
+
+  return ERR_VAL;
+}
+
+
 /**
  * @brief   Get the storage type of the given username.
  *
@@ -388,6 +423,8 @@ snmpv3_dummy_init(void)
   snmpv3_set_user_priv_algo("lwip", SNMP_V3_PRIV_ALGO_DES);
   snmpv3_set_user_priv_key("lwip", "maplesyrup");
 
+  snmpv3_set_user_access("lwip", SNMP_V3_USER_ACCESS_READ_WRITE);
+
   /* Start the engine time timer */
   snmpv3_enginetime_timer(NULL);
 }
diff --git a/contrib/examples/snmp/snmp_v3/snmpv3_dummy.h b/contrib/examples/snmp/snmp_v3/snmpv3_dummy.h
index ba25b6d7..1966aefb 100644
--- a/contrib/examples/snmp/snmp_v3/snmpv3_dummy.h
+++ b/contrib/examples/snmp/snmp_v3/snmpv3_dummy.h
@@ -45,6 +45,7 @@ err_t snmpv3_set_user_auth_algo(const char *username, snmpv3_auth_algo_t algo);
 err_t snmpv3_set_user_priv_algo(const char *username, snmpv3_priv_algo_t algo);
 err_t snmpv3_set_user_auth_key(const char *username, const char *password);
 err_t snmpv3_set_user_priv_key(const char *username, const char *password);
+err_t snmpv3_set_user_access(const char *username, snmpv3_user_access_t access);
 
 void snmpv3_dummy_init(void);
 
diff --git a/src/apps/snmp/snmp_msg.c b/src/apps/snmp/snmp_msg.c
index 9cd4b071..23d8ed08 100644
--- a/src/apps/snmp/snmp_msg.c
+++ b/src/apps/snmp/snmp_msg.c
@@ -837,12 +837,6 @@ snmp_parse_inbound_frame(struct snmp_request *request)
     u16_t u16_value;
     u16_t inbound_msgAuthenticationParameters_offset;
 
-    /* SNMPv3 doesn't use communities */
-    /* @todo: Differentiate read/write access */
-    strncpy((char *)request->community, snmp_community, SNMP_MAX_COMMUNITY_STR_LEN);
-    request->community[SNMP_MAX_COMMUNITY_STR_LEN] = 0; /* ensure NULL termination (strncpy does NOT guarantee it!) */
-    request->community_strlen = (u16_t)strlen((char *)request->community);
-
     /* RFC3414 globalData */
     IF_PARSE_EXEC(snmp_asn1_dec_tlv(&pbuf_stream, &tlv));
     IF_PARSE_ASSERT(tlv.type == SNMP_ASN1_TYPE_SEQUENCE);
@@ -1212,6 +1206,19 @@ snmp_parse_inbound_frame(struct snmp_request *request)
   request->request_type = tlv.type & SNMP_ASN1_DATATYPE_MASK;
   request->request_out_type = (SNMP_ASN1_CLASS_CONTEXT | SNMP_ASN1_CONTENTTYPE_CONSTRUCTED | SNMP_ASN1_CONTEXT_PDU_GET_RESP);
 
+#if LWIP_SNMP_V3
+    if (request->version == SNMP_VERSION_3) {
+      if (request->request_type == SNMP_ASN1_CONTEXT_PDU_SET_REQ) {
+        snmpv3_user_access_t user_access;
+        if ((snmpv3_get_user_access((char *)request->msg_user_name, &user_access) != ERR_OK) ||
+            (user_access != SNMP_V3_USER_ACCESS_READ_WRITE)) {
+          /* no write access */
+          request->error_status = SNMP_ERR_NOTWRITABLE;
+          request->error_index  = 1;
+        }
+      }
+    } else
+#endif
   /* validate community (do this after decoding PDU type because we don't want to increase 'inbadcommunitynames' for wrong frame types */
   if (request->community_strlen == 0) {
     /* community string was too long or really empty*/
diff --git a/src/include/lwip/apps/snmpv3.h b/src/include/lwip/apps/snmpv3.h
index e0dda649..397a3367 100644
--- a/src/include/lwip/apps/snmpv3.h
+++ b/src/include/lwip/apps/snmpv3.h
@@ -67,6 +67,12 @@ typedef enum
   SNMP_V3_USER_STORAGETYPE_READONLY    = 5
 } snmpv3_user_storagetype_t;
 
+typedef enum
+{
+  SNMP_V3_USER_ACCESS_READ_ONLY  = 1,
+  SNMP_V3_USER_ACCESS_READ_WRITE = 2
+} snmpv3_user_access_t;
+
 /*
  * The following callback functions must be implemented by the application.
  * There is a dummy implementation in snmpv3_dummy.c.
@@ -85,6 +91,7 @@ err_t snmpv3_get_user(const char* username, snmpv3_auth_algo_t *auth_algo, u8_t
 u8_t snmpv3_get_amount_of_users(void);
 err_t snmpv3_get_user_storagetype(const char *username, snmpv3_user_storagetype_t *storagetype);
 err_t snmpv3_get_username(char *username, u8_t index);
+err_t snmpv3_get_user_access(const char *username, snmpv3_user_access_t *access);
 
 /* The following functions are provided by the SNMPv3 agent */
 
-- 
2.39.3

