/src/net-snmp/snmplib/lcd_time.c
Line | Count | Source (jump to first uncovered line) |
1 | | /* |
2 | | * lcd_time.c |
3 | | * |
4 | | * XXX Should etimelist entries with <0,0> time tuples be timed out? |
5 | | * XXX Need a routine to free the memory? (Perhaps at shutdown?) |
6 | | */ |
7 | | |
8 | | #include <net-snmp/net-snmp-config.h> |
9 | | #include <net-snmp/net-snmp-features.h> |
10 | | |
11 | | #include <sys/types.h> |
12 | | #include <stdio.h> |
13 | | #ifdef HAVE_STDLIB_H |
14 | | #include <stdlib.h> |
15 | | #endif |
16 | | #ifdef HAVE_STRING_H |
17 | | #include <string.h> |
18 | | #else |
19 | | #include <strings.h> |
20 | | #endif |
21 | | #ifdef TIME_WITH_SYS_TIME |
22 | | # include <sys/time.h> |
23 | | # include <time.h> |
24 | | #else |
25 | | # ifdef HAVE_SYS_TIME_H |
26 | | # include <sys/time.h> |
27 | | # else |
28 | | # include <time.h> |
29 | | # endif |
30 | | #endif |
31 | | #ifdef HAVE_NETINET_IN_H |
32 | | #include <netinet/in.h> |
33 | | #endif |
34 | | |
35 | | #ifdef HAVE_UNISTD_H |
36 | | #include <unistd.h> |
37 | | #endif |
38 | | |
39 | | #include <net-snmp/types.h> |
40 | | #include <net-snmp/output_api.h> |
41 | | #include <net-snmp/utilities.h> |
42 | | |
43 | | #include <net-snmp/library/snmp_api.h> |
44 | | #include <net-snmp/library/callback.h> |
45 | | #include <net-snmp/library/snmp_secmod.h> |
46 | | #include <net-snmp/library/snmpusm.h> |
47 | | #include <net-snmp/library/lcd_time.h> |
48 | | #include <net-snmp/library/scapi.h> |
49 | | #include <net-snmp/library/snmpv3.h> |
50 | | |
51 | | #include <net-snmp/library/transform_oids.h> |
52 | | |
53 | | netsnmp_feature_child_of(usm_support, libnetsnmp); |
54 | | netsnmp_feature_child_of(usm_lcd_time, usm_support); |
55 | | |
56 | | #ifndef NETSNMP_FEATURE_REMOVE_USM_LCD_TIME |
57 | | |
58 | | /* |
59 | | * Global static hashlist to contain Enginetime entries. |
60 | | * |
61 | | * New records are prepended to the appropriate list at the hash index. |
62 | | */ |
63 | | static Enginetime etimelist[ETIMELIST_SIZE]; |
64 | | |
65 | | |
66 | | |
67 | | |
68 | | /*******************************************************************-o-****** |
69 | | * get_enginetime |
70 | | * |
71 | | * Parameters: |
72 | | * *engineID |
73 | | * engineID_len |
74 | | * *engineboot |
75 | | * *engine_time |
76 | | * |
77 | | * Returns: |
78 | | * SNMPERR_SUCCESS Success -- when a record for engineID is found. |
79 | | * SNMPERR_GENERR Otherwise. |
80 | | * |
81 | | * |
82 | | * Lookup engineID and return the recorded values for the |
83 | | * <engine_time, engineboot> tuple adjusted to reflect the estimated time |
84 | | * at the engine in question. |
85 | | * |
86 | | * Special case: if engineID is NULL or if engineID_len is 0 then |
87 | | * the time tuple is returned immediately as zero. |
88 | | * |
89 | | * XXX What if timediff wraps? >shrug< |
90 | | * XXX Then: you need to increment the boots value. Now. Detecting |
91 | | * this is another matter. |
92 | | */ |
93 | | int |
94 | | get_enginetime(const u_char * engineID, |
95 | | u_int engineID_len, |
96 | | u_int * engineboot, |
97 | | u_int * engine_time, u_int authenticated) |
98 | 0 | { |
99 | 0 | int rval = SNMPERR_SUCCESS; |
100 | 0 | int timediff = 0; |
101 | 0 | Enginetime e = NULL; |
102 | | |
103 | | |
104 | | |
105 | | /* |
106 | | * Sanity check. |
107 | | */ |
108 | 0 | if (!engine_time || !engineboot) { |
109 | 0 | QUITFUN(SNMPERR_GENERR, get_enginetime_quit); |
110 | 0 | } |
111 | | |
112 | | |
113 | | /* |
114 | | * Compute estimated current engine_time tuple at engineID if |
115 | | * a record is cached for it. |
116 | | */ |
117 | 0 | *engine_time = *engineboot = 0; |
118 | |
|
119 | 0 | if (!engineID || (engineID_len <= 0)) { |
120 | 0 | QUITFUN(SNMPERR_GENERR, get_enginetime_quit); |
121 | 0 | } |
122 | | |
123 | 0 | if (!(e = search_enginetime_list(engineID, engineID_len))) { |
124 | 0 | QUITFUN(SNMPERR_GENERR, get_enginetime_quit); |
125 | 0 | } |
126 | 0 | #ifdef LCD_TIME_SYNC_OPT |
127 | 0 | if (!authenticated || e->authenticatedFlag) { |
128 | 0 | #endif |
129 | 0 | *engine_time = e->engineTime; |
130 | 0 | *engineboot = e->engineBoot; |
131 | |
|
132 | 0 | timediff = (int) (snmpv3_local_snmpEngineTime() - e->lastReceivedEngineTime); |
133 | |
|
134 | 0 | #ifdef LCD_TIME_SYNC_OPT |
135 | 0 | } |
136 | 0 | #endif |
137 | |
|
138 | 0 | if (timediff > (int) (ENGINETIME_MAX - *engine_time)) { |
139 | 0 | *engine_time = (timediff - (ENGINETIME_MAX - *engine_time)); |
140 | | |
141 | | /* |
142 | | * FIX -- move this check up... should not change anything |
143 | | * * if engineboot is already locked. ??? |
144 | | */ |
145 | 0 | if (*engineboot < ENGINEBOOT_MAX) { |
146 | 0 | *engineboot += 1; |
147 | 0 | } |
148 | |
|
149 | 0 | } else { |
150 | 0 | *engine_time += timediff; |
151 | 0 | } |
152 | |
|
153 | 0 | DEBUGMSGTL(("lcd_get_enginetime", "engineID ")); |
154 | 0 | DEBUGMSGHEX(("lcd_get_enginetime", engineID, engineID_len)); |
155 | 0 | DEBUGMSG(("lcd_get_enginetime", ": boots=%d, time=%d\n", *engineboot, |
156 | 0 | *engine_time)); |
157 | |
|
158 | 0 | get_enginetime_quit: |
159 | 0 | return rval; |
160 | |
|
161 | 0 | } /* end get_enginetime() */ |
162 | | |
163 | | /*******************************************************************-o-****** |
164 | | * get_enginetime |
165 | | * |
166 | | * Parameters: |
167 | | * *engineID |
168 | | * engineID_len |
169 | | * *engineboot |
170 | | * *engine_time |
171 | | * |
172 | | * Returns: |
173 | | * SNMPERR_SUCCESS Success -- when a record for engineID is found. |
174 | | * SNMPERR_GENERR Otherwise. |
175 | | * |
176 | | * |
177 | | * Lookup engineID and return the recorded values for the |
178 | | * <engine_time, engineboot> tuple adjusted to reflect the estimated time |
179 | | * at the engine in question. |
180 | | * |
181 | | * Special case: if engineID is NULL or if engineID_len is 0 then |
182 | | * the time tuple is returned immediately as zero. |
183 | | * |
184 | | * XXX What if timediff wraps? >shrug< |
185 | | * XXX Then: you need to increment the boots value. Now. Detecting |
186 | | * this is another matter. |
187 | | */ |
188 | | int |
189 | | get_enginetime_ex(u_char * engineID, |
190 | | u_int engineID_len, |
191 | | u_int * engineboot, |
192 | | u_int * engine_time, |
193 | | u_int * last_engine_time, u_int authenticated) |
194 | 0 | { |
195 | 0 | int rval = SNMPERR_SUCCESS; |
196 | 0 | int timediff = 0; |
197 | 0 | Enginetime e = NULL; |
198 | | |
199 | | |
200 | | |
201 | | /* |
202 | | * Sanity check. |
203 | | */ |
204 | 0 | if (!engine_time || !engineboot || !last_engine_time) { |
205 | 0 | QUITFUN(SNMPERR_GENERR, get_enginetime_ex_quit); |
206 | 0 | } |
207 | | |
208 | | |
209 | | /* |
210 | | * Compute estimated current engine_time tuple at engineID if |
211 | | * a record is cached for it. |
212 | | */ |
213 | 0 | *last_engine_time = *engine_time = *engineboot = 0; |
214 | |
|
215 | 0 | if (!engineID || (engineID_len <= 0)) { |
216 | 0 | QUITFUN(SNMPERR_GENERR, get_enginetime_ex_quit); |
217 | 0 | } |
218 | | |
219 | 0 | if (!(e = search_enginetime_list(engineID, engineID_len))) { |
220 | 0 | QUITFUN(SNMPERR_GENERR, get_enginetime_ex_quit); |
221 | 0 | } |
222 | 0 | #ifdef LCD_TIME_SYNC_OPT |
223 | 0 | if (!authenticated || e->authenticatedFlag) { |
224 | 0 | #endif |
225 | 0 | *last_engine_time = *engine_time = e->engineTime; |
226 | 0 | *engineboot = e->engineBoot; |
227 | |
|
228 | 0 | timediff = (int) (snmpv3_local_snmpEngineTime() - e->lastReceivedEngineTime); |
229 | |
|
230 | 0 | #ifdef LCD_TIME_SYNC_OPT |
231 | 0 | } |
232 | 0 | #endif |
233 | |
|
234 | 0 | if (timediff > (int) (ENGINETIME_MAX - *engine_time)) { |
235 | 0 | *engine_time = (timediff - (ENGINETIME_MAX - *engine_time)); |
236 | | |
237 | | /* |
238 | | * FIX -- move this check up... should not change anything |
239 | | * * if engineboot is already locked. ??? |
240 | | */ |
241 | 0 | if (*engineboot < ENGINEBOOT_MAX) { |
242 | 0 | *engineboot += 1; |
243 | 0 | } |
244 | |
|
245 | 0 | } else { |
246 | 0 | *engine_time += timediff; |
247 | 0 | } |
248 | |
|
249 | 0 | DEBUGMSGTL(("lcd_get_enginetime_ex", "engineID ")); |
250 | 0 | DEBUGMSGHEX(("lcd_get_enginetime_ex", engineID, engineID_len)); |
251 | 0 | DEBUGMSG(("lcd_get_enginetime_ex", ": boots=%d, time=%d\n", |
252 | 0 | *engineboot, *engine_time)); |
253 | |
|
254 | 0 | get_enginetime_ex_quit: |
255 | 0 | return rval; |
256 | |
|
257 | 0 | } /* end get_enginetime_ex() */ |
258 | | |
259 | | |
260 | | void free_enginetime(unsigned char *engineID, size_t engineID_len) |
261 | 0 | { |
262 | 0 | Enginetime e = NULL; |
263 | 0 | int rval = 0; |
264 | |
|
265 | 0 | rval = hash_engineID(engineID, engineID_len); |
266 | 0 | if (rval < 0) |
267 | 0 | return; |
268 | | |
269 | 0 | e = etimelist[rval]; |
270 | |
|
271 | 0 | while (e != NULL) { |
272 | 0 | etimelist[rval] = e->next; |
273 | 0 | SNMP_FREE(e->engineID); |
274 | 0 | SNMP_FREE(e); |
275 | 0 | e = etimelist[rval]; |
276 | 0 | } |
277 | |
|
278 | 0 | } |
279 | | |
280 | | /*******************************************************************-o-**** |
281 | | ** |
282 | | * free_etimelist |
283 | | * |
284 | | * Parameters: |
285 | | * None |
286 | | * |
287 | | * Returns: |
288 | | * void |
289 | | * |
290 | | * |
291 | | * Free all of the memory used by entries in the etimelist. |
292 | | * |
293 | | */ |
294 | | void free_etimelist(void) |
295 | 5.59k | { |
296 | 5.59k | int index = 0; |
297 | 5.59k | Enginetime e = NULL; |
298 | 5.59k | Enginetime nextE = NULL; |
299 | | |
300 | 134k | for( ; index < ETIMELIST_SIZE; ++index) |
301 | 128k | { |
302 | 128k | e = etimelist[index]; |
303 | | |
304 | 131k | while(e != NULL) |
305 | 2.79k | { |
306 | 2.79k | nextE = e->next; |
307 | 2.79k | SNMP_FREE(e->engineID); |
308 | 2.79k | SNMP_FREE(e); |
309 | 2.79k | e = nextE; |
310 | 2.79k | } |
311 | | |
312 | 128k | etimelist[index] = NULL; |
313 | 128k | } |
314 | 5.59k | return; |
315 | 5.59k | } |
316 | | |
317 | | /*******************************************************************-o-****** |
318 | | * set_enginetime |
319 | | * |
320 | | * Parameters: |
321 | | * *engineID |
322 | | * engineID_len |
323 | | * engineboot |
324 | | * engine_time |
325 | | * |
326 | | * Returns: |
327 | | * SNMPERR_SUCCESS Success. |
328 | | * SNMPERR_GENERR Otherwise. |
329 | | * |
330 | | * |
331 | | * Lookup engineID and store the given <engine_time, engineboot> tuple |
332 | | * and then stamp the record with a consistent source of local time. |
333 | | * If the engineID record does not exist, create one. |
334 | | * |
335 | | * Special case: engineID is NULL or engineID_len is 0 defines an engineID |
336 | | * that is "always set." |
337 | | * |
338 | | * XXX "Current time within the local engine" == time(NULL)... |
339 | | */ |
340 | | int |
341 | | set_enginetime(const u_char * engineID, |
342 | | u_int engineID_len, |
343 | | u_int engineboot, u_int engine_time, u_int authenticated) |
344 | 2.80k | { |
345 | 2.80k | int rval = SNMPERR_SUCCESS, iindex; |
346 | 2.80k | Enginetime e = NULL; |
347 | | |
348 | | |
349 | | |
350 | | /* |
351 | | * Sanity check. |
352 | | */ |
353 | 2.80k | if (!engineID || (engineID_len <= 0)) { |
354 | 0 | return rval; |
355 | 0 | } |
356 | | |
357 | | |
358 | | /* |
359 | | * Store the given <engine_time, engineboot> tuple in the record |
360 | | * for engineID. Create a new record if necessary. |
361 | | */ |
362 | 2.80k | if (!(e = search_enginetime_list(engineID, engineID_len))) { |
363 | 2.80k | if ((iindex = hash_engineID(engineID, engineID_len)) < 0) { |
364 | 0 | QUITFUN(SNMPERR_GENERR, set_enginetime_quit); |
365 | 0 | } |
366 | | |
367 | 2.80k | e = calloc(1, sizeof(*e)); |
368 | | |
369 | 2.80k | e->next = etimelist[iindex]; |
370 | 2.80k | etimelist[iindex] = e; |
371 | | |
372 | 2.80k | e->engineID = calloc(1, engineID_len); |
373 | 2.80k | memcpy(e->engineID, engineID, engineID_len); |
374 | | |
375 | 2.80k | e->engineID_len = engineID_len; |
376 | 2.80k | } |
377 | 2.80k | #ifdef LCD_TIME_SYNC_OPT |
378 | 2.80k | if (authenticated || !e->authenticatedFlag) { |
379 | 2.80k | e->authenticatedFlag = authenticated; |
380 | | #else |
381 | | if (authenticated) { |
382 | | #endif |
383 | 2.80k | e->engineTime = engine_time; |
384 | 2.80k | e->engineBoot = engineboot; |
385 | 2.80k | e->lastReceivedEngineTime = snmpv3_local_snmpEngineTime(); |
386 | 2.80k | } |
387 | | |
388 | 2.80k | e = NULL; /* Indicates a successful update. */ |
389 | | |
390 | 2.80k | DEBUGMSGTL(("lcd_set_enginetime", "engineID ")); |
391 | 2.80k | DEBUGMSGHEX(("lcd_set_enginetime", engineID, engineID_len)); |
392 | 2.80k | DEBUGMSG(("lcd_set_enginetime", ": boots=%d, time=%d\n", engineboot, |
393 | 2.80k | engine_time)); |
394 | | |
395 | 2.80k | set_enginetime_quit: |
396 | 2.80k | SNMP_FREE(e); |
397 | | |
398 | 2.80k | return rval; |
399 | | |
400 | 2.80k | } /* end set_enginetime() */ |
401 | | |
402 | | |
403 | | |
404 | | |
405 | | /*******************************************************************-o-****** |
406 | | * search_enginetime_list |
407 | | * |
408 | | * Parameters: |
409 | | * *engineID |
410 | | * engineID_len |
411 | | * |
412 | | * Returns: |
413 | | * Pointer to a etimelist record with engineID <engineID> -OR- |
414 | | * NULL if no record exists. |
415 | | * |
416 | | * |
417 | | * Search etimelist for an entry with engineID. |
418 | | * |
419 | | * ASSUMES that no engineID will have more than one record in the list. |
420 | | */ |
421 | | Enginetime |
422 | | search_enginetime_list(const u_char * engineID, u_int engineID_len) |
423 | 2.80k | { |
424 | 2.80k | int rval = SNMPERR_SUCCESS; |
425 | 2.80k | Enginetime e = NULL; |
426 | | |
427 | | |
428 | | /* |
429 | | * Sanity check. |
430 | | */ |
431 | 2.80k | if (!engineID || (engineID_len <= 0)) { |
432 | 0 | QUITFUN(SNMPERR_GENERR, search_enginetime_list_quit); |
433 | 0 | } |
434 | | |
435 | | |
436 | | /* |
437 | | * Find the entry for engineID if there be one. |
438 | | */ |
439 | 2.80k | rval = hash_engineID(engineID, engineID_len); |
440 | 2.80k | if (rval < 0) { |
441 | 0 | QUITFUN(SNMPERR_GENERR, search_enginetime_list_quit); |
442 | 0 | } |
443 | 2.80k | e = etimelist[rval]; |
444 | | |
445 | 2.80k | for ( /*EMPTY*/; e; e = e->next) { |
446 | 0 | if ((engineID_len == e->engineID_len) |
447 | 0 | && !memcmp(e->engineID, engineID, engineID_len)) { |
448 | 0 | break; |
449 | 0 | } |
450 | 0 | } |
451 | | |
452 | | |
453 | 2.80k | search_enginetime_list_quit: |
454 | 2.80k | return e; |
455 | | |
456 | 2.80k | } /* end search_enginetime_list() */ |
457 | | |
458 | | |
459 | | |
460 | | |
461 | | |
462 | | /*******************************************************************-o-****** |
463 | | * hash_engineID |
464 | | * |
465 | | * Parameters: |
466 | | * *engineID |
467 | | * engineID_len |
468 | | * |
469 | | * Returns: |
470 | | * >0 etimelist index for this engineID. |
471 | | * SNMPERR_GENERR Error. |
472 | | * |
473 | | * |
474 | | * Use a cheap hash to build an index into the etimelist. Method is |
475 | | * to hash the engineID, then split the hash into u_int's and add them up |
476 | | * and modulo the size of the list. |
477 | | * |
478 | | */ |
479 | | int |
480 | | hash_engineID(const u_char * engineID, u_int engineID_len) |
481 | 5.60k | { |
482 | 5.60k | int rval = SNMPERR_GENERR; |
483 | 5.60k | size_t buf_len = SNMP_MAXBUF; |
484 | 5.60k | u_int additive = 0; |
485 | 5.60k | u_char *bufp, buf[SNMP_MAXBUF]; |
486 | 5.60k | void *context = NULL; |
487 | | |
488 | | |
489 | | |
490 | | /* |
491 | | * Sanity check. |
492 | | */ |
493 | 5.60k | if (!engineID || (engineID_len <= 0)) { |
494 | 0 | QUITFUN(SNMPERR_GENERR, hash_engineID_quit); |
495 | 0 | } |
496 | | |
497 | | |
498 | | /* |
499 | | * Hash engineID into a list index. |
500 | | */ |
501 | 5.60k | #ifndef NETSNMP_DISABLE_MD5 |
502 | 5.60k | rval = sc_hash(usmHMACMD5AuthProtocol, |
503 | 5.60k | OID_LENGTH(usmHMACMD5AuthProtocol), |
504 | 5.60k | engineID, engineID_len, buf, &buf_len); |
505 | 5.60k | if (rval == SNMPERR_SC_NOT_CONFIGURED) { |
506 | | /* fall back to sha1 */ |
507 | 0 | rval = sc_hash(usmHMACSHA1AuthProtocol, |
508 | 0 | OID_LENGTH(usmHMACSHA1AuthProtocol), |
509 | 0 | engineID, engineID_len, buf, &buf_len); |
510 | 0 | } |
511 | | #else |
512 | | rval = sc_hash(usmHMACSHA1AuthProtocol, |
513 | | OID_LENGTH(usmHMACSHA1AuthProtocol), |
514 | | engineID, engineID_len, buf, &buf_len); |
515 | | #endif |
516 | 5.60k | QUITFUN(rval, hash_engineID_quit); |
517 | | |
518 | 28.0k | for (bufp = buf; (bufp - buf) < (int) buf_len; bufp += 4) { |
519 | 22.4k | additive += (u_int) * bufp; |
520 | 22.4k | } |
521 | | |
522 | 5.60k | hash_engineID_quit: |
523 | 5.60k | SNMP_FREE(context); |
524 | 5.60k | memset(buf, 0, SNMP_MAXBUF); |
525 | | |
526 | 5.60k | return (rval < 0) ? rval : (int)(additive % ETIMELIST_SIZE); |
527 | | |
528 | 5.60k | } /* end hash_engineID() */ |
529 | | |
530 | | |
531 | | |
532 | | |
533 | | #ifdef NETSNMP_ENABLE_TESTING_CODE |
534 | | /*******************************************************************-o-****** |
535 | | * dump_etimelist_entry |
536 | | * |
537 | | * Parameters: |
538 | | * e |
539 | | * count |
540 | | */ |
541 | | void |
542 | | dump_etimelist_entry(Enginetime e, int count) |
543 | | { |
544 | | size_t buflen; |
545 | | char tabs[SNMP_MAXBUF], *t = tabs, *s; |
546 | | |
547 | | |
548 | | |
549 | | count += 1; |
550 | | while (count--) { |
551 | | t += sprintf(t, " "); |
552 | | } |
553 | | |
554 | | |
555 | | buflen = e->engineID_len; |
556 | | if (!(s = dump_snmpEngineID(e->engineID, &buflen))) { |
557 | | binary_to_hex(e->engineID, e->engineID_len, &s); |
558 | | } |
559 | | |
560 | | DEBUGMSGTL(("dump_etimelist", "%s\n", tabs)); |
561 | | DEBUGMSGTL(("dump_etimelist", "%s%s (len=%d) <%d,%d>\n", tabs, |
562 | | s, e->engineID_len, e->engineTime, e->engineBoot)); |
563 | | DEBUGMSGTL(("dump_etimelist", "%s%ld (%ld)", tabs, |
564 | | e->lastReceivedEngineTime, |
565 | | snmpv3_local_snmpEngineTime() - e->lastReceivedEngineTime)); |
566 | | |
567 | | SNMP_FREE(s); |
568 | | |
569 | | } /* end dump_etimelist_entry() */ |
570 | | |
571 | | |
572 | | |
573 | | |
574 | | /*******************************************************************-o-****** |
575 | | * dump_etimelist |
576 | | */ |
577 | | void |
578 | | dump_etimelist(void) |
579 | | { |
580 | | int iindex = -1, count = 0; |
581 | | Enginetime e; |
582 | | |
583 | | |
584 | | |
585 | | DEBUGMSGTL(("dump_etimelist", "\n")); |
586 | | |
587 | | while (++iindex < ETIMELIST_SIZE) { |
588 | | DEBUGMSG(("dump_etimelist", "[%d]", iindex)); |
589 | | |
590 | | count = 0; |
591 | | e = etimelist[iindex]; |
592 | | |
593 | | while (e) { |
594 | | dump_etimelist_entry(e, count++); |
595 | | e = e->next; |
596 | | } |
597 | | |
598 | | if (count > 0) { |
599 | | DEBUGMSG(("dump_etimelist", "\n")); |
600 | | } |
601 | | } /* endwhile */ |
602 | | |
603 | | DEBUGMSG(("dump_etimelist", "\n")); |
604 | | |
605 | | } /* end dump_etimelist() */ |
606 | | #endif /* NETSNMP_ENABLE_TESTING_CODE */ |
607 | | #endif /* NETSNMP_FEATURE_REMOVE_USM_LCD_TIME */ |