Coverage Report

Created: 2026-10-17 06:54

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/net-snmp/snmplib/lcd_time.c
Line
Count
Source
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, *prev;
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
    prev = &etimelist[rval];
270
0
    while ((e = *prev) != NULL) {
271
0
  if (e->engineID_len == engineID_len &&
272
0
      memcmp(e->engineID, engineID, engineID_len) == 0) {
273
0
      *prev = e->next;
274
0
      SNMP_FREE(e->engineID);
275
0
      SNMP_FREE(e);
276
0
  } else {
277
0
      prev = &e->next;
278
0
  }
279
0
    }
280
281
0
}
282
283
/*******************************************************************-o-****
284
**
285
 * free_etimelist
286
 *
287
 * Parameters:
288
 *   None
289
 *      
290
 * Returns:
291
 *   void
292
 *
293
 *
294
 * Free all of the memory used by entries in the etimelist.
295
 *
296
 */
297
void free_etimelist(void)
298
0
{
299
0
     int index = 0;
300
0
     Enginetime e = NULL;
301
0
     Enginetime nextE = NULL;
302
303
0
     for( ; index < ETIMELIST_SIZE; ++index)
304
0
     {
305
0
           e = etimelist[index];
306
307
0
           while(e != NULL)
308
0
           {
309
0
                 nextE = e->next;
310
0
                 SNMP_FREE(e->engineID);
311
0
                 SNMP_FREE(e);
312
0
                 e = nextE;
313
0
           }
314
315
0
           etimelist[index] = NULL;
316
0
     }
317
0
     return;
318
0
}
319
320
/*******************************************************************-o-******
321
 * set_enginetime
322
 *
323
 * Parameters:
324
 *  *engineID
325
 *   engineID_len
326
 *   engineboot
327
 *   engine_time
328
 *      
329
 * Returns:
330
 *  SNMPERR_SUCCESS   Success.
331
 *  SNMPERR_GENERR    Otherwise.
332
 *
333
 *
334
 * Lookup engineID and store the given <engine_time, engineboot> tuple
335
 * and then stamp the record with a consistent source of local time.
336
 * If the engineID record does not exist, create one.
337
 *
338
 * Special case: engineID is NULL or engineID_len is 0 defines an engineID
339
 * that is "always set."
340
 *
341
 * XXX  "Current time within the local engine" == time(NULL)...
342
 */
343
int
344
set_enginetime(const u_char * engineID,
345
               u_int engineID_len,
346
               u_int engineboot, u_int engine_time, u_int authenticated)
347
2
{
348
2
    int             rval = SNMPERR_SUCCESS, iindex;
349
2
    Enginetime      e = NULL;
350
351
352
353
    /*
354
     * Sanity check.
355
     */
356
2
    if (!engineID || (engineID_len <= 0)) {
357
0
        return rval;
358
0
    }
359
360
361
    /*
362
     * Store the given <engine_time, engineboot> tuple in the record
363
     * for engineID.  Create a new record if necessary.
364
     */
365
2
    if (!(e = search_enginetime_list(engineID, engineID_len))) {
366
2
        if ((iindex = hash_engineID(engineID, engineID_len)) < 0) {
367
0
            QUITFUN(SNMPERR_GENERR, set_enginetime_quit);
368
0
        }
369
370
2
        e = calloc(1, sizeof(*e));
371
2
        if (!e) {
372
0
            QUITFUN(SNMPERR_GENERR, set_enginetime_quit);
373
0
        }
374
375
2
        e->engineID = calloc(1, engineID_len);
376
2
        if (!e->engineID) {
377
0
            QUITFUN(SNMPERR_GENERR, set_enginetime_quit);
378
0
        }
379
2
        memcpy(e->engineID, engineID, engineID_len);
380
381
2
        e->engineID_len = engineID_len;
382
2
        e->next = etimelist[iindex];
383
2
        etimelist[iindex] = e;
384
2
    }
385
2
#ifdef LCD_TIME_SYNC_OPT
386
2
    if (authenticated || !e->authenticatedFlag) {
387
2
        e->authenticatedFlag = authenticated;
388
#else
389
    if (authenticated) {
390
#endif
391
2
        e->engineTime = engine_time;
392
2
        e->engineBoot = engineboot;
393
2
        e->lastReceivedEngineTime = snmpv3_local_snmpEngineTime();
394
2
    }
395
396
2
    e = NULL;                   /* Indicates a successful update. */
397
398
2
    DEBUGMSGTL(("lcd_set_enginetime", "engineID "));
399
2
    DEBUGMSGHEX(("lcd_set_enginetime", engineID, engineID_len));
400
2
    DEBUGMSG(("lcd_set_enginetime", ": boots=%d, time=%d\n", engineboot,
401
2
              engine_time));
402
403
2
  set_enginetime_quit:
404
2
    SNMP_FREE(e);
405
406
2
    return rval;
407
408
2
}                               /* end set_enginetime() */
409
410
411
412
413
/*******************************************************************-o-******
414
 * search_enginetime_list
415
 *
416
 * Parameters:
417
 *  *engineID
418
 *   engineID_len
419
 *      
420
 * Returns:
421
 *  Pointer to a etimelist record with engineID <engineID>  -OR-
422
 *  NULL if no record exists.
423
 *
424
 *
425
 * Search etimelist for an entry with engineID.
426
 *
427
 * ASSUMES that no engineID will have more than one record in the list.
428
 */
429
Enginetime
430
search_enginetime_list(const u_char * engineID, u_int engineID_len)
431
2
{
432
2
    int             rval = SNMPERR_SUCCESS;
433
2
    Enginetime      e = NULL;
434
435
436
    /*
437
     * Sanity check.
438
     */
439
2
    if (!engineID || (engineID_len <= 0)) {
440
0
        QUITFUN(SNMPERR_GENERR, search_enginetime_list_quit);
441
0
    }
442
443
444
    /*
445
     * Find the entry for engineID if there be one.
446
     */
447
2
    rval = hash_engineID(engineID, engineID_len);
448
2
    if (rval < 0) {
449
0
        QUITFUN(SNMPERR_GENERR, search_enginetime_list_quit);
450
0
    }
451
2
    e = etimelist[rval];
452
453
2
    for ( /*EMPTY*/; e; e = e->next) {
454
0
        if ((engineID_len == e->engineID_len)
455
0
            && !memcmp(e->engineID, engineID, engineID_len)) {
456
0
            break;
457
0
        }
458
0
    }
459
460
461
2
  search_enginetime_list_quit:
462
2
    return e;
463
464
2
}                               /* end search_enginetime_list() */
465
466
467
468
469
470
/*******************************************************************-o-******
471
 * hash_engineID
472
 *
473
 * Parameters:
474
 *  *engineID
475
 *   engineID_len
476
 *      
477
 * Returns:
478
 *  >0      etimelist index for this engineID.
479
 *  SNMPERR_GENERR    Error.
480
 *  
481
 * 
482
 * Use a cheap hash to build an index into the etimelist.  Method is 
483
 * to hash the engineID, then split the hash into u_int's and add them up
484
 * and modulo the size of the list.
485
 *
486
 */
487
int
488
hash_engineID(const u_char * engineID, u_int engineID_len)
489
4
{
490
4
    int             rval = SNMPERR_GENERR;
491
4
    size_t          buf_len = SNMP_MAXBUF;
492
4
    u_int           additive = 0;
493
4
    u_char         *bufp, buf[SNMP_MAXBUF];
494
4
    void           *context = NULL;
495
496
497
498
    /*
499
     * Sanity check.
500
     */
501
4
    if (!engineID || (engineID_len <= 0)) {
502
0
        QUITFUN(SNMPERR_GENERR, hash_engineID_quit);
503
0
    }
504
505
506
    /*
507
     * Hash engineID into a list index.
508
     */
509
4
#ifndef NETSNMP_DISABLE_MD5
510
4
    rval = sc_hash(usmHMACMD5AuthProtocol,
511
4
                   OID_LENGTH(usmHMACMD5AuthProtocol),
512
4
                   engineID, engineID_len, buf, &buf_len);
513
4
    if (rval == SNMPERR_SC_NOT_CONFIGURED) {
514
        /* fall back to sha1 */
515
0
        rval = sc_hash(usmHMACSHA1AuthProtocol,
516
0
                   OID_LENGTH(usmHMACSHA1AuthProtocol),
517
0
                   engineID, engineID_len, buf, &buf_len);
518
0
    }
519
#else
520
    rval = sc_hash(usmHMACSHA1AuthProtocol,
521
                   OID_LENGTH(usmHMACSHA1AuthProtocol),
522
                   engineID, engineID_len, buf, &buf_len);
523
#endif
524
4
    QUITFUN(rval, hash_engineID_quit);
525
526
20
    for (bufp = buf; (bufp - buf) < (int) buf_len; bufp += 4) {
527
16
        additive += (u_int) * bufp;
528
16
    }
529
530
4
  hash_engineID_quit:
531
4
    SNMP_FREE(context);
532
4
    memset(buf, 0, SNMP_MAXBUF);
533
534
4
    return (rval < 0) ? rval : (int)(additive % ETIMELIST_SIZE);
535
536
4
}                               /* end hash_engineID() */
537
538
539
540
541
#ifdef NETSNMP_ENABLE_TESTING_CODE
542
/*******************************************************************-o-******
543
 * dump_etimelist_entry
544
 *
545
 * Parameters:
546
 *  e
547
 *  count
548
 */
549
void
550
dump_etimelist_entry(Enginetime e, int count)
551
{
552
    size_t          buflen;
553
    char            tabs[SNMP_MAXBUF], *s;
554
555
    snprintf(tabs, sizeof(tabs), "%*s", 2 * (count + 1), "");
556
557
    buflen = e->engineID_len;
558
    if (!(s = dump_snmpEngineID(e->engineID, &buflen))) {
559
        binary_to_hex(e->engineID, e->engineID_len, &s);
560
    }
561
562
    DEBUGMSGTL(("dump_etimelist", "%s\n", tabs));
563
    DEBUGMSGTL(("dump_etimelist", "%s%s (len=%d) <%d,%d>\n", tabs,
564
                s, e->engineID_len, e->engineTime, e->engineBoot));
565
    DEBUGMSGTL(("dump_etimelist", "%s%ld (%ld)", tabs,
566
                e->lastReceivedEngineTime,
567
                snmpv3_local_snmpEngineTime() - e->lastReceivedEngineTime));
568
569
    SNMP_FREE(s);
570
571
}                               /* end dump_etimelist_entry() */
572
573
574
575
576
/*******************************************************************-o-******
577
 * dump_etimelist
578
 */
579
void
580
dump_etimelist(void)
581
{
582
    int             iindex = -1, count = 0;
583
    Enginetime      e;
584
585
586
587
    DEBUGMSGTL(("dump_etimelist", "\n"));
588
589
    while (++iindex < ETIMELIST_SIZE) {
590
        DEBUGMSG(("dump_etimelist", "[%d]", iindex));
591
592
        count = 0;
593
        e = etimelist[iindex];
594
595
        while (e) {
596
            dump_etimelist_entry(e, count++);
597
            e = e->next;
598
        }
599
600
        if (count > 0) {
601
            DEBUGMSG(("dump_etimelist", "\n"));
602
        }
603
    }                           /* endwhile */
604
605
    DEBUGMSG(("dump_etimelist", "\n"));
606
607
}                               /* end dump_etimelist() */
608
#endif                          /* NETSNMP_ENABLE_TESTING_CODE */
609
#endif /* NETSNMP_FEATURE_REMOVE_USM_LCD_TIME */