• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /**
2  * @file
3  * Dummy SNMPv3 functions.
4  */
5 
6 /*
7  * Copyright (c) 2016 Elias Oenal.
8  * All rights reserved.
9  *
10  * Redistribution and use in source and binary forms, with or without modification,
11  * are permitted provided that the following conditions are met:
12  *
13  * 1. Redistributions of source code must retain the above copyright notice,
14  *    this list of conditions and the following disclaimer.
15  * 2. Redistributions in binary form must reproduce the above copyright notice,
16  *    this list of conditions and the following disclaimer in the documentation
17  *    and/or other materials provided with the distribution.
18  * 3. The name of the author may not be used to endorse or promote products
19  *    derived from this software without specific prior written permission.
20  *
21  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
22  * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
23  * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
24  * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
25  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
26  * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
27  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
28  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
29  * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
30  * OF SUCH DAMAGE.
31  *
32  * Author: Elias Oenal <lwip@eliasoenal.com>
33  *         Dirk Ziegelmeier <dirk@ziegelmeier.net>
34  */
35 
36 #include "lwip/apps/snmpv3.h"
37 #include "snmpv3_dummy.h"
38 #include <string.h>
39 #include "lwip/err.h"
40 #include "lwip/def.h"
41 #include "lwip/timeouts.h"
42 
43 #if LWIP_SNMP && LWIP_SNMP_V3
44 
45 struct user_table_entry {
46   char               username[32];
47   snmpv3_auth_algo_t auth_algo;
48   u8_t               auth_key[20];
49   snmpv3_priv_algo_t priv_algo;
50   u8_t               priv_key[20];
51 };
52 
53 static struct user_table_entry user_table[] = {
54   { "lwip", SNMP_V3_AUTH_ALGO_INVAL, "" , SNMP_V3_PRIV_ALGO_INVAL, "" },
55   { "piwl", SNMP_V3_AUTH_ALGO_INVAL, "" , SNMP_V3_PRIV_ALGO_INVAL, "" },
56   { "test", SNMP_V3_AUTH_ALGO_INVAL, "" , SNMP_V3_PRIV_ALGO_INVAL, "" }
57 };
58 
59 static char snmpv3_engineid[32];
60 static u8_t snmpv3_engineid_len;
61 
62 static u32_t enginetime = 0;
63 
64 /* In this implementation engineboots is volatile. In a real world application this value should be stored in non-volatile memory.*/
65 static u32_t engineboots = 0;
66 
67 /**
68  * @brief   Get the user table entry for the given username.
69  *
70  * @param[in] username  pointer to the username
71  *
72  * @return              pointer to the user table entry or NULL if not found.
73  */
74 static struct user_table_entry*
get_user(const char * username)75 get_user(const char *username)
76 {
77   size_t i;
78 
79   for (i = 0; i < LWIP_ARRAYSIZE(user_table); i++) {
80     if (strnlen(username, 32) != strnlen(user_table[i].username, 32)) {
81       continue;
82     }
83 
84     if (memcmp(username, user_table[i].username, strnlen(username, 32)) == 0) {
85       return &user_table[i];
86     }
87   }
88 
89   return NULL;
90 }
91 
92 u8_t
snmpv3_get_amount_of_users(void)93 snmpv3_get_amount_of_users(void)
94 {
95   return LWIP_ARRAYSIZE(user_table);
96 }
97 
98 /**
99  * @brief Get the username of a user number (index)
100  * @param username is a pointer to a string.
101  * @param index is the user index.
102  * @return ERR_OK if user is found, ERR_VAL is user is not found.
103  */
104 err_t
snmpv3_get_username(char * username,u8_t index)105 snmpv3_get_username(char *username, u8_t index)
106 {
107   if (index < LWIP_ARRAYSIZE(user_table)) {
108     MEMCPY(username, user_table[index].username, sizeof(user_table[0].username));
109     return ERR_OK;
110   }
111 
112   return ERR_VAL;
113 }
114 
115 /**
116  * Timer callback function that increments enginetime and reschedules itself.
117  *
118  * @param arg unused argument
119  */
120 static void
snmpv3_enginetime_timer(void * arg)121 snmpv3_enginetime_timer(void *arg)
122 {
123   LWIP_UNUSED_ARG(arg);
124 
125   enginetime++;
126 
127   /* This handles the engine time reset */
128   snmpv3_get_engine_time_internal();
129 
130   /* restart timer */
131   sys_timeout(1000, snmpv3_enginetime_timer, NULL);
132 }
133 
134 err_t
snmpv3_set_user_auth_algo(const char * username,snmpv3_auth_algo_t algo)135 snmpv3_set_user_auth_algo(const char *username, snmpv3_auth_algo_t algo)
136 {
137   struct user_table_entry *p = get_user(username);
138 
139   if (p) {
140     switch (algo) {
141     case SNMP_V3_AUTH_ALGO_INVAL:
142       if (p->priv_algo != SNMP_V3_PRIV_ALGO_INVAL) {
143         /* Privacy MUST be disabled before configuring authentication */
144         break;
145       } else {
146         p->auth_algo = algo;
147         return ERR_OK;
148       }
149 #if LWIP_SNMP_V3_CRYPTO
150     case SNMP_V3_AUTH_ALGO_MD5:
151     case SNMP_V3_AUTH_ALGO_SHA:
152 #endif
153       p->auth_algo = algo;
154       return ERR_OK;
155     default:
156       break;
157     }
158   }
159 
160   return ERR_VAL;
161 }
162 
163 err_t
snmpv3_set_user_priv_algo(const char * username,snmpv3_priv_algo_t algo)164 snmpv3_set_user_priv_algo(const char *username, snmpv3_priv_algo_t algo)
165 {
166   struct user_table_entry *p = get_user(username);
167 
168   if (p) {
169     switch (algo) {
170 #if LWIP_SNMP_V3_CRYPTO
171     case SNMP_V3_PRIV_ALGO_AES:
172     case SNMP_V3_PRIV_ALGO_DES:
173       if (p->auth_algo == SNMP_V3_AUTH_ALGO_INVAL) {
174         /* Authentication MUST be enabled before configuring privacy */
175         break;
176       } else {
177         p->priv_algo = algo;
178         return ERR_OK;
179       }
180 #endif
181     case SNMP_V3_PRIV_ALGO_INVAL:
182       p->priv_algo = algo;
183       return ERR_OK;
184     default:
185       break;
186     }
187   }
188 
189   return ERR_VAL;
190 }
191 
192 err_t
snmpv3_set_user_auth_key(const char * username,const char * password)193 snmpv3_set_user_auth_key(const char *username, const char *password)
194 {
195   struct user_table_entry *p = get_user(username);
196   const char *engineid;
197   u8_t engineid_len;
198 
199   if (p) {
200     /* password should be at least 8 characters long */
201     if (strlen(password) >= 8) {
202       memset(p->auth_key, 0, sizeof(p->auth_key));
203       snmpv3_get_engine_id(&engineid, &engineid_len);
204       switch (p->auth_algo) {
205       case SNMP_V3_AUTH_ALGO_INVAL:
206         return ERR_OK;
207 #if LWIP_SNMP_V3_CRYPTO
208       case SNMP_V3_AUTH_ALGO_MD5:
209         snmpv3_password_to_key_md5((const u8_t*)password, strlen(password), (const u8_t*)engineid, engineid_len, p->auth_key);
210         return ERR_OK;
211       case SNMP_V3_AUTH_ALGO_SHA:
212         snmpv3_password_to_key_sha((const u8_t*)password, strlen(password), (const u8_t*)engineid, engineid_len, p->auth_key);
213         return ERR_OK;
214 #endif
215       default:
216         return ERR_VAL;
217       }
218     }
219   }
220 
221   return ERR_VAL;
222 }
223 
224 err_t
snmpv3_set_user_priv_key(const char * username,const char * password)225 snmpv3_set_user_priv_key(const char *username, const char *password)
226 {
227   struct user_table_entry *p = get_user(username);
228   const char *engineid;
229   u8_t engineid_len;
230 
231   if (p) {
232     /* password should be at least 8 characters long */
233     if (strlen(password) >= 8) {
234       memset(p->priv_key, 0, sizeof(p->priv_key));
235       snmpv3_get_engine_id(&engineid, &engineid_len);
236       switch (p->auth_algo) {
237       case SNMP_V3_AUTH_ALGO_INVAL:
238         return ERR_OK;
239 #if LWIP_SNMP_V3_CRYPTO
240       case SNMP_V3_AUTH_ALGO_MD5:
241         snmpv3_password_to_key_md5((const u8_t*)password, strlen(password), (const u8_t*)engineid, engineid_len, p->priv_key);
242         return ERR_OK;
243       case SNMP_V3_AUTH_ALGO_SHA:
244         snmpv3_password_to_key_sha((const u8_t*)password, strlen(password), (const u8_t*)engineid, engineid_len, p->priv_key);
245         return ERR_OK;
246 #endif
247       default:
248         return ERR_VAL;
249       }
250     }
251   }
252 
253   return ERR_VAL;
254 }
255 
256 /**
257  * @brief   Get the storage type of the given username.
258  *
259  * @param[in] username  pointer to the username
260  * @param[out] type     the storage type
261  *
262  * @return              ERR_OK if the user was found, ERR_VAL if not.
263  */
264 err_t
snmpv3_get_user_storagetype(const char * username,snmpv3_user_storagetype_t * type)265 snmpv3_get_user_storagetype(const char *username, snmpv3_user_storagetype_t *type)
266 {
267   if (get_user(username) != NULL) {
268     /* Found user in user table
269      * In this dummy implementation, storage is permanent because no user can be deleted.
270      * All changes to users are lost after a reboot.*/
271     *type = SNMP_V3_USER_STORAGETYPE_PERMANENT;
272     return ERR_OK;
273   }
274 
275   return ERR_VAL;
276 }
277 
278 /**
279  *  @param username is a pointer to a string.
280  * @param auth_algo is a pointer to u8_t. The implementation has to set this if user was found.
281  * @param auth_key is a pointer to a pointer to a string. Implementation has to set this if user was found.
282  * @param priv_algo is a pointer to u8_t. The implementation has to set this if user was found.
283  * @param priv_key is a pointer to a pointer to a string. Implementation has to set this if user was found.
284  */
285 err_t
snmpv3_get_user(const char * username,snmpv3_auth_algo_t * auth_algo,u8_t * auth_key,snmpv3_priv_algo_t * priv_algo,u8_t * priv_key)286 snmpv3_get_user(const char* username, snmpv3_auth_algo_t *auth_algo, u8_t *auth_key, snmpv3_priv_algo_t *priv_algo, u8_t *priv_key)
287 {
288   const struct user_table_entry *p;
289 
290   /* The msgUserName specifies the user (principal) on whose behalf the
291      message is being exchanged. Note that a zero-length userName will
292      not match any user, but it can be used for snmpEngineID discovery. */
293   if(strlen(username) == 0) {
294     return ERR_OK;
295   }
296 
297   p = get_user(username);
298 
299   if (!p) {
300     return ERR_VAL;
301   }
302 
303   if (auth_algo != NULL) {
304     *auth_algo = p->auth_algo;
305   }
306   if(auth_key != NULL) {
307     MEMCPY(auth_key, p->auth_key, sizeof(p->auth_key));
308   }
309   if (priv_algo != NULL) {
310     *priv_algo = p->priv_algo;
311   }
312   if(priv_key != NULL) {
313     MEMCPY(priv_key, p->priv_key, sizeof(p->priv_key));
314   }
315   return ERR_OK;
316 }
317 
318 /**
319  * Get engine ID from persistence
320  */
321 void
snmpv3_get_engine_id(const char ** id,u8_t * len)322 snmpv3_get_engine_id(const char **id, u8_t *len)
323 {
324   *id = snmpv3_engineid;
325   *len = snmpv3_engineid_len;
326 }
327 
328 /**
329  * Store engine ID in persistence
330  */
331 err_t
snmpv3_set_engine_id(const char * id,u8_t len)332 snmpv3_set_engine_id(const char *id, u8_t len)
333 {
334   MEMCPY(snmpv3_engineid, id, len);
335   snmpv3_engineid_len = len;
336   return ERR_OK;
337 }
338 
339 /**
340  * Get engine boots from persistence. Must be increased on each boot.
341  */
342 u32_t
snmpv3_get_engine_boots(void)343 snmpv3_get_engine_boots(void)
344 {
345   return engineboots;
346 }
347 
348 /**
349  * Store engine boots in persistence
350  */
351 void
snmpv3_set_engine_boots(u32_t boots)352 snmpv3_set_engine_boots(u32_t boots)
353 {
354   engineboots = boots;
355 }
356 
357 /**
358  * RFC3414 2.2.2.
359  * Once the timer reaches 2147483647 it gets reset to zero and the
360  * engine boot ups get incremented.
361  */
362 u32_t
snmpv3_get_engine_time(void)363 snmpv3_get_engine_time(void)
364 {
365   return enginetime;
366 }
367 
368 /**
369  * Reset current engine time to 0
370  */
371 void
snmpv3_reset_engine_time(void)372 snmpv3_reset_engine_time(void)
373 {
374   enginetime = 0;
375 }
376 
377 /**
378  * Initialize dummy SNMPv3 implementation
379  */
380 void
snmpv3_dummy_init(void)381 snmpv3_dummy_init(void)
382 {
383   snmpv3_set_engine_id("FOO", 3);
384 
385   snmpv3_set_user_auth_algo("lwip", SNMP_V3_AUTH_ALGO_SHA);
386   snmpv3_set_user_auth_key("lwip", "maplesyrup");
387 
388   snmpv3_set_user_priv_algo("lwip", SNMP_V3_PRIV_ALGO_DES);
389   snmpv3_set_user_priv_key("lwip", "maplesyrup");
390 
391   /* Start the engine time timer */
392   snmpv3_enginetime_timer(NULL);
393 }
394 
395 #endif /* LWIP_SNMP && LWIP_SNMP_V3 */
396