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