Coverage Report

Created: 2023-05-19 06:16

/src/ntp-dev/ntpd/ntp_leapsec.c
Line
Count
Source (jump to first uncovered line)
1
/*
2
 * ntp_leapsec.c - leap second processing for NTPD
3
 *
4
 * Written by Juergen Perlinger (perlinger@ntp.org) for the NTP project.
5
 * The contents of 'html/copyright.html' apply.
6
 * ----------------------------------------------------------------------
7
 * This is an attempt to get the leap second handling into a dedicated
8
 * module to make the somewhat convoluted logic testable.
9
 */
10
11
#include <config.h>
12
#include <sys/types.h>
13
#include <sys/stat.h>
14
#include <ctype.h>
15
16
#include "ntp_types.h"
17
#include "ntp_fp.h"
18
#include "ntp_stdlib.h"
19
#include "ntp_calendar.h"
20
#include "ntp_leapsec.h"
21
#include "ntp.h"
22
#include "vint64ops.h"
23
#include "lib_strbuf.h"
24
25
#include "isc/sha1.h"
26
27
static const char * const logPrefix = "leapsecond file";
28
29
/* ---------------------------------------------------------------------
30
 * GCC is rather sticky with its 'const' attribute. We have to do it more
31
 * explicit than with a cast if we want to get rid of a CONST qualifier.
32
 * Greetings from the PASCAL world, where casting was only possible via
33
 * untagged unions...
34
 */
35
static inline void*
36
noconst(
37
  const void* ptr
38
  )
39
0
{
40
0
  union {
41
0
    const void * cp;
42
0
    void *       vp;
43
0
  } tmp;
44
0
  tmp.cp = ptr;
45
0
  return tmp.vp;
46
0
}
47
48
/* ---------------------------------------------------------------------
49
 * Our internal data structure
50
 */
51
0
#define MAX_HIST 10  /* history of leap seconds */
52
53
struct leap_info {
54
  vint64   ttime; /* transition time (after the step, ntp scale) */
55
  uint32_t stime; /* schedule limit (a month before transition)  */
56
  int16_t  taiof; /* TAI offset on and after the transition      */
57
  uint8_t  dynls; /* dynamic: inserted on peer/clock request     */
58
};
59
typedef struct leap_info leap_info_t;
60
61
struct leap_head {
62
  vint64   update; /* time of information update                 */
63
  vint64   expire; /* table expiration time                      */
64
  uint16_t size;   /* number of infos in table                 */
65
  int16_t  base_tai;  /* total leaps before first entry      */
66
  int16_t  this_tai;  /* current TAI offset                */
67
  int16_t  next_tai;  /* TAI offset after 'when'             */
68
  vint64   dtime;  /* due time (current era end)                 */
69
  vint64   ttime;  /* nominal transition time (next era start)   */
70
  vint64   stime;  /* schedule time (when we take notice)        */
71
  vint64   ebase;  /* base time of this leap era                 */
72
  uint8_t  dynls;  /* next leap is dynamic (by peer request)     */
73
};
74
typedef struct leap_head leap_head_t;
75
76
struct leap_table {
77
  leap_signature_t lsig;
78
  leap_head_t  head;
79
  leap_info_t    info[MAX_HIST];
80
};
81
82
/* Where we store our tables */
83
static leap_table_t _ltab[2], *_lptr;
84
static int/*BOOL*/  _electric;
85
86
/* Forward decls of local helpers */
87
static int    add_range(leap_table_t*, const leap_info_t*);
88
static char * get_line(leapsec_reader, void*, char*, size_t);
89
static char * skipws(const char*);
90
static int    parsefail(const char * cp, const char * ep);
91
static void   reload_limits(leap_table_t*, const vint64*);
92
static void   fetch_leap_era(leap_era_t*, const leap_table_t*,
93
           const vint64*);
94
static int    betweenu32(uint32_t, uint32_t, uint32_t);
95
static void   reset_times(leap_table_t*);
96
static int    leapsec_add(leap_table_t*, const vint64*, int);
97
static int    leapsec_raw(leap_table_t*, const vint64 *, int, int);
98
static const char * lstostr(const vint64 * ts);
99
100
/* =====================================================================
101
 * Get & Set the current leap table
102
 */
103
104
/* ------------------------------------------------------------------ */
105
leap_table_t *
106
leapsec_get_table(
107
  int alternate)
108
70
{
109
70
  leap_table_t *p1, *p2;
110
111
70
  p1 = _lptr;
112
70
  if (p1 == &_ltab[0]) {
113
69
    p2 = &_ltab[1];
114
69
  } else if (p1 == &_ltab[1]) {
115
0
    p2 = &_ltab[0];
116
1
  } else {
117
1
    p1 = &_ltab[0];
118
1
    p2 = &_ltab[1];
119
1
    reset_times(p1);
120
1
    reset_times(p2);
121
1
    _lptr = p1;
122
1
  }
123
70
  if (alternate) {
124
0
    memcpy(p2, p1, sizeof(leap_table_t));
125
0
    p1 = p2;
126
0
  }
127
128
70
  return p1;
129
70
}
130
131
/* ------------------------------------------------------------------ */
132
int/*BOOL*/
133
leapsec_set_table(
134
  leap_table_t * pt)
135
0
{
136
0
  if (pt == &_ltab[0] || pt == &_ltab[1])
137
0
    _lptr = pt;
138
0
  return _lptr == pt;
139
0
}
140
141
/* ------------------------------------------------------------------ */
142
int/*BOOL*/
143
leapsec_electric(
144
  int/*BOOL*/ on)
145
0
{
146
0
  int res = _electric;
147
0
  if (on < 0)
148
0
    return res;
149
150
0
  _electric = (on != 0);
151
0
  if (_electric == res)
152
0
    return res;
153
154
0
  if (_lptr == &_ltab[0] || _lptr == &_ltab[1])
155
0
    reset_times(_lptr);
156
157
0
  return res;
158
0
}
159
160
/* =====================================================================
161
 * API functions that operate on tables
162
 */
163
164
/* ---------------------------------------------------------------------
165
 * Clear all leap second data. Use it for init & cleanup
166
 */
167
void
168
leapsec_clear(
169
  leap_table_t * pt)
170
0
{
171
0
  memset(&pt->lsig, 0, sizeof(pt->lsig));
172
0
  memset(&pt->head, 0, sizeof(pt->head));
173
0
  reset_times(pt);
174
0
}
175
176
/* ---------------------------------------------------------------------
177
 * Load a leap second file and check expiration on the go
178
 */
179
int/*BOOL*/
180
leapsec_load(
181
  leap_table_t * pt  ,
182
  leapsec_reader func,
183
  void *         farg,
184
  int            use_build_limit)
185
0
{
186
0
  char   *cp, *ep, linebuf[50];
187
0
  vint64 ttime, limit;
188
0
  long   taiof;
189
0
  struct calendar build;
190
191
0
  leapsec_clear(pt);
192
0
  if (use_build_limit && ntpcal_get_build_date(&build)) {
193
    /* don't prune everything -- permit the last 10yrs
194
     * before build.
195
     */
196
0
    build.year -= 10;
197
0
    limit = ntpcal_date_to_ntp64(&build);
198
0
  } else {
199
0
    memset(&limit, 0, sizeof(limit));
200
0
  }
201
202
0
  while (get_line(func, farg, linebuf, sizeof(linebuf))) {
203
0
    cp = linebuf;
204
0
    if (*cp == '#') {
205
0
      cp++;
206
0
      if (*cp == '@') {
207
0
        cp = skipws(cp+1);
208
0
        pt->head.expire = strtouv64(cp, &ep, 10);
209
0
        if (parsefail(cp, ep))
210
0
          goto fail_read;
211
0
        pt->lsig.etime = pt->head.expire.D_s.lo;
212
0
      } else if (*cp == '$') {
213
0
        cp = skipws(cp+1);
214
0
        pt->head.update = strtouv64(cp, &ep, 10);
215
0
        if (parsefail(cp, ep))
216
0
          goto fail_read;
217
0
      }
218
0
    } else if (isdigit((u_char)*cp)) {
219
0
      ttime = strtouv64(cp, &ep, 10);
220
0
      if (parsefail(cp, ep))
221
0
        goto fail_read;
222
0
      cp = skipws(ep);
223
0
      taiof = strtol(cp, &ep, 10);
224
0
      if (   parsefail(cp, ep)
225
0
          || taiof > SHRT_MAX || taiof < SHRT_MIN)
226
0
        goto fail_read;
227
0
      if (ucmpv64(&ttime, &limit) >= 0) {
228
0
        if (!leapsec_raw(pt, &ttime,
229
0
             taiof, FALSE))
230
0
          goto fail_insn;
231
0
      } else {
232
0
        pt->head.base_tai = (int16_t)taiof;
233
0
      }
234
0
      pt->lsig.ttime = ttime.D_s.lo;
235
0
      pt->lsig.taiof = (int16_t)taiof;
236
0
    }
237
0
  }
238
0
  return TRUE;
239
240
0
fail_read:
241
0
  errno = EILSEQ;
242
0
fail_insn:
243
0
  leapsec_clear(pt);
244
0
  return FALSE;
245
0
}
246
247
/* ---------------------------------------------------------------------
248
 * Dump a table in human-readable format. Use 'fprintf' and a FILE
249
 * pointer if you want to get it printed into a stream.
250
 */
251
void
252
leapsec_dump(
253
  const leap_table_t * pt  ,
254
  leapsec_dumper       func,
255
  void *               farg)
256
0
{
257
0
  int             idx;
258
0
  vint64          ts;
259
0
  struct calendar atb, ttb;
260
261
0
  ntpcal_ntp64_to_date(&ttb, &pt->head.expire);
262
0
  (*func)(farg, "leap table (%u entries) expires at %04u-%02u-%02u:\n",
263
0
    pt->head.size,
264
0
    ttb.year, ttb.month, ttb.monthday);
265
0
  idx = pt->head.size;
266
0
  while (idx-- != 0) {
267
0
    ts = pt->info[idx].ttime;
268
0
    ntpcal_ntp64_to_date(&ttb, &ts);
269
0
    ts = subv64u32(&ts, pt->info[idx].stime);
270
0
    ntpcal_ntp64_to_date(&atb, &ts);
271
272
0
    (*func)(farg, "%04u-%02u-%02u [%c] (%04u-%02u-%02u) - %d\n",
273
0
      ttb.year, ttb.month, ttb.monthday,
274
0
      "-*"[pt->info[idx].dynls != 0],
275
0
      atb.year, atb.month, atb.monthday,
276
0
      pt->info[idx].taiof);
277
0
  }
278
0
}
279
280
/* =====================================================================
281
 * usecase driven API functions
282
 */
283
284
int/*BOOL*/
285
leapsec_query(
286
  leap_result_t * qr   ,
287
  uint32_t        ts32 ,
288
  const time_t *  pivot)
289
0
{
290
0
  leap_table_t *   pt;
291
0
  vint64           ts64, last, next;
292
0
  uint32_t         due32;
293
0
  int              fired;
294
295
  /* preset things we use later on... */
296
0
  fired = FALSE;
297
0
  ts64  = ntpcal_ntp_to_ntp(ts32, pivot);
298
0
  pt    = leapsec_get_table(FALSE);
299
0
  memset(qr, 0, sizeof(leap_result_t));
300
301
0
  if (ucmpv64(&ts64, &pt->head.ebase) < 0) {
302
    /* Most likely after leap frame reset. Could also be a
303
     * backstep of the system clock. Anyway, get the new
304
     * leap era frame.
305
     */
306
0
    reload_limits(pt, &ts64);
307
0
  } else if (ucmpv64(&ts64, &pt->head.dtime) >= 0) {
308
    /* Boundary crossed in forward direction. This might
309
     * indicate a leap transition, so we prepare for that
310
     * case.
311
     *
312
     * Some operations below are actually NOPs in electric
313
     * mode, but having only one code path that works for
314
     * both modes is easier to maintain.
315
     *
316
     * There's another quirk we must keep looking out for:
317
     * If we just stepped the clock, the step might have
318
     * crossed a leap boundary. As with backward steps, we
319
     * do not want to raise the 'fired' event in that case.
320
     * So we raise the 'fired' event only if we're close to
321
     * the transition and just reload the limits otherwise.
322
     */
323
0
    last = addv64i32(&pt->head.dtime, 3); /* get boundary */
324
0
    if (ucmpv64(&ts64, &last) >= 0) {
325
      /* that was likely a query after a step */
326
0
      reload_limits(pt, &ts64);
327
0
    } else {
328
      /* close enough for deeper examination */
329
0
      last = pt->head.ttime;
330
0
      qr->warped = (int16_t)(last.D_s.lo -
331
0
                 pt->head.dtime.D_s.lo);
332
0
      next = addv64i32(&ts64, qr->warped);
333
0
      reload_limits(pt, &next);
334
0
      fired = ucmpv64(&pt->head.ebase, &last) == 0;
335
0
      if (fired) {
336
0
        ts64 = next;
337
0
        ts32 = next.D_s.lo;
338
0
      } else {
339
0
        qr->warped = 0;
340
0
      }
341
0
    }
342
0
  }
343
344
0
  qr->tai_offs = pt->head.this_tai;
345
0
  qr->ebase    = pt->head.ebase;
346
0
  qr->ttime    = pt->head.ttime;
347
348
  /* If before the next scheduling alert, we're done. */
349
0
  if (ucmpv64(&ts64, &pt->head.stime) < 0)
350
0
    return fired;
351
352
  /* now start to collect the remaining data */
353
0
  due32 = pt->head.dtime.D_s.lo;
354
355
0
  qr->tai_diff  = pt->head.next_tai - pt->head.this_tai;
356
0
  qr->ddist     = due32 - ts32;
357
0
  qr->dynamic   = pt->head.dynls;
358
0
  qr->proximity = LSPROX_SCHEDULE;
359
360
  /* if not in the last day before transition, we're done. */
361
0
  if (!betweenu32(due32 - SECSPERDAY, ts32, due32))
362
0
    return fired;
363
364
0
  qr->proximity = LSPROX_ANNOUNCE;
365
0
  if (!betweenu32(due32 - 10, ts32, due32))
366
0
    return fired;
367
368
  /* The last 10s before the transition. Prepare for action! */
369
0
  qr->proximity = LSPROX_ALERT;
370
0
  return fired;
371
0
}
372
373
/* ------------------------------------------------------------------ */
374
int/*BOOL*/
375
leapsec_query_era(
376
  leap_era_t *   qr   ,
377
  uint32_t       ntpts,
378
  const time_t * pivot)
379
0
{
380
0
  const leap_table_t * pt;
381
0
  vint64               ts64;
382
383
0
  pt   = leapsec_get_table(FALSE);
384
0
  ts64 = ntpcal_ntp_to_ntp(ntpts, pivot);
385
0
  fetch_leap_era(qr, pt, &ts64);
386
0
  return TRUE;
387
0
}
388
389
/* ------------------------------------------------------------------ */
390
int/*BOOL*/
391
leapsec_frame(
392
        leap_result_t *qr)
393
0
{
394
0
  const leap_table_t * pt;
395
396
0
        memset(qr, 0, sizeof(leap_result_t));
397
0
  pt = leapsec_get_table(FALSE);
398
399
0
  qr->tai_offs = pt->head.this_tai;
400
0
  qr->tai_diff = pt->head.next_tai - pt->head.this_tai;
401
0
  qr->ebase    = pt->head.ebase;
402
0
  qr->ttime    = pt->head.ttime;
403
0
  qr->dynamic  = pt->head.dynls;
404
405
0
  return ucmpv64(&pt->head.ttime, &pt->head.stime) >= 0;
406
0
}
407
408
/* ------------------------------------------------------------------ */
409
/* Reset the current leap frame */
410
void
411
leapsec_reset_frame(void)
412
0
{
413
0
  reset_times(leapsec_get_table(FALSE));
414
0
}
415
416
/* ------------------------------------------------------------------ */
417
/* load a file from a FILE pointer. Note: If hcheck is true, load
418
 * only after successful signature check. The stream must be seekable
419
 * or this will fail.
420
 */
421
int/*BOOL*/
422
leapsec_load_stream(
423
  FILE       * ifp  ,
424
  const char * fname,
425
  int/*BOOL*/  logall)
426
0
{
427
0
  leap_table_t *pt;
428
0
  int           rcheck;
429
430
0
  if (NULL == fname)
431
0
    fname = "<unknown>";
432
433
0
  rcheck = leapsec_validate((leapsec_reader)getc, ifp);
434
0
  if (logall)
435
0
    switch (rcheck)
436
0
    {
437
0
    case LSVALID_GOODHASH:
438
0
      msyslog(LOG_NOTICE, "%s ('%s'): good hash signature",
439
0
        logPrefix, fname);
440
0
      break;
441
442
0
    case LSVALID_NOHASH:
443
0
      msyslog(LOG_ERR, "%s ('%s'): no hash signature",
444
0
        logPrefix, fname);
445
0
      break;
446
0
    case LSVALID_BADHASH:
447
0
      msyslog(LOG_ERR, "%s ('%s'): signature mismatch",
448
0
        logPrefix, fname);
449
0
      break;
450
0
    case LSVALID_BADFORMAT:
451
0
      msyslog(LOG_ERR, "%s ('%s'): malformed hash signature",
452
0
        logPrefix, fname);
453
0
      break;
454
0
    default:
455
0
      msyslog(LOG_ERR, "%s ('%s'): unknown error code %d",
456
0
        logPrefix, fname, rcheck);
457
0
      break;
458
0
    }
459
0
  if (rcheck < 0)
460
0
    return FALSE;
461
462
0
  rewind(ifp);
463
0
  pt = leapsec_get_table(TRUE);
464
0
  if (!leapsec_load(pt, (leapsec_reader)getc, ifp, TRUE)) {
465
0
    switch (errno) {
466
0
    case EINVAL:
467
0
      msyslog(LOG_ERR, "%s ('%s'): bad transition time",
468
0
        logPrefix, fname);
469
0
      break;
470
0
    case ERANGE:
471
0
      msyslog(LOG_ERR, "%s ('%s'): times not ascending",
472
0
        logPrefix, fname);
473
0
      break;
474
0
    default:
475
0
      msyslog(LOG_ERR, "%s ('%s'): parsing error",
476
0
        logPrefix, fname);
477
0
      break;
478
0
    }
479
0
    return FALSE;
480
0
  }
481
482
0
  if (pt->head.size)
483
0
    msyslog(LOG_NOTICE, "%s ('%s'): loaded, expire=%s last=%s ofs=%d",
484
0
      logPrefix, fname, lstostr(&pt->head.expire),
485
0
      lstostr(&pt->info[0].ttime), pt->info[0].taiof);
486
0
  else
487
0
    msyslog(LOG_NOTICE,
488
0
      "%s ('%s'): loaded, expire=%s ofs=%d (no entries after build date)",
489
0
      logPrefix, fname, lstostr(&pt->head.expire),
490
0
      pt->head.base_tai);
491
492
0
  return leapsec_set_table(pt);
493
0
}
494
495
/* ------------------------------------------------------------------ */
496
int/*BOOL*/
497
leapsec_load_file(
498
  const char  * fname,
499
  struct stat * sb_old,
500
  int/*BOOL*/   force,
501
  int/*BOOL*/   logall)
502
0
{
503
0
  FILE       * fp;
504
0
  struct stat  sb_new;
505
0
  int          rc;
506
507
  /* just do nothing if there is no leap file */
508
0
  if ( !(fname && *fname) )
509
0
    return FALSE;
510
511
  /* try to stat the leapfile */
512
0
  if (0 != stat(fname, &sb_new)) {
513
0
    if (logall)
514
0
      msyslog(LOG_ERR, "%s ('%s'): stat failed: %m",
515
0
        logPrefix, fname);
516
0
    return FALSE;
517
0
  }
518
519
  /* silently skip to postcheck if no new file found */
520
0
  if (NULL != sb_old) {
521
0
    if (!force
522
0
     && sb_old->st_mtime == sb_new.st_mtime
523
0
     && sb_old->st_ctime == sb_new.st_ctime
524
0
       )
525
0
      return FALSE;
526
0
    *sb_old = sb_new;
527
0
  }
528
529
  /* try to open the leap file, complain if that fails
530
   *
531
   * [perlinger@ntp.org]
532
   * coverity raises a TOCTOU (time-of-check/time-of-use) issue
533
   * here, which is not entirely helpful: While there is indeed a
534
   * possible race condition between the 'stat()' call above and
535
   * the 'fopen)' call below, I intentionally want to omit the
536
   * overhead of opening the file and calling 'fstat()', because
537
   * in most cases the file would have be to closed anyway without
538
   * reading the contents.  I chose to disable the coverity
539
   * warning instead.
540
   *
541
   * So unless someone comes up with a reasonable argument why
542
   * this could be a real issue, I'll just try to silence coverity
543
   * on that topic.
544
   */
545
  /* coverity[toctou] */
546
0
  if ((fp = fopen(fname, "r")) == NULL) {
547
0
    if (logall)
548
0
      msyslog(LOG_ERR,
549
0
        "%s ('%s'): open failed: %m",
550
0
        logPrefix, fname);
551
0
    return FALSE;
552
0
  }
553
554
0
  rc = leapsec_load_stream(fp, fname, logall);
555
0
  fclose(fp);
556
0
  return rc;
557
0
}
558
559
/* ------------------------------------------------------------------ */
560
void
561
leapsec_getsig(
562
  leap_signature_t * psig)
563
70
{
564
70
  const leap_table_t * pt;
565
566
70
  pt = leapsec_get_table(FALSE);
567
70
  memcpy(psig, &pt->lsig, sizeof(leap_signature_t));
568
70
}
569
570
/* ------------------------------------------------------------------ */
571
int/*BOOL*/
572
leapsec_expired(
573
  uint32_t       when,
574
  const time_t * tpiv)
575
0
{
576
0
  const leap_table_t * pt;
577
0
  vint64 limit;
578
579
0
  pt = leapsec_get_table(FALSE);
580
0
  limit = ntpcal_ntp_to_ntp(when, tpiv);
581
0
  return ucmpv64(&limit, &pt->head.expire) >= 0;
582
0
}
583
584
/* ------------------------------------------------------------------ */
585
int32_t
586
leapsec_daystolive(
587
  uint32_t       when,
588
  const time_t * tpiv)
589
0
{
590
0
  const leap_table_t * pt;
591
0
  vint64 limit;
592
593
0
  pt = leapsec_get_table(FALSE);
594
0
  limit = ntpcal_ntp_to_ntp(when, tpiv);
595
0
  limit = subv64(&pt->head.expire, &limit);
596
0
  return ntpcal_daysplit(&limit).hi;
597
0
}
598
599
/* ------------------------------------------------------------------ */
600
#if 0 /* currently unused -- possibly revived later */
601
int/*BOOL*/
602
leapsec_add_fix(
603
  int            total,
604
  uint32_t       ttime,
605
  uint32_t       etime,
606
  const time_t * pivot)
607
{
608
  time_t         tpiv;
609
  leap_table_t * pt;
610
  vint64         tt64, et64;
611
612
  if (pivot == NULL) {
613
    time(&tpiv);
614
    pivot = &tpiv;
615
  }
616
617
  et64 = ntpcal_ntp_to_ntp(etime, pivot);
618
  tt64 = ntpcal_ntp_to_ntp(ttime, pivot);
619
  pt   = leapsec_get_table(TRUE);
620
621
  if (   ucmpv64(&et64, &pt->head.expire) <= 0
622
     || !leapsec_raw(pt, &tt64, total, FALSE) )
623
    return FALSE;
624
625
  pt->lsig.etime = etime;
626
  pt->lsig.ttime = ttime;
627
  pt->lsig.taiof = (int16_t)total;
628
629
  pt->head.expire = et64;
630
631
  return leapsec_set_table(pt);
632
}
633
#endif
634
635
/* ------------------------------------------------------------------ */
636
int/*BOOL*/
637
leapsec_add_dyn(
638
  int            insert,
639
  uint32_t       ntpnow,
640
  const time_t * pivot )
641
0
{
642
0
  leap_table_t * pt;
643
0
  vint64         now64;
644
645
0
  pt = leapsec_get_table(TRUE);
646
0
  now64 = ntpcal_ntp_to_ntp(ntpnow, pivot);
647
0
  return (   leapsec_add(pt, &now64, (insert != 0))
648
0
    && leapsec_set_table(pt));
649
0
}
650
651
/* ------------------------------------------------------------------ */
652
int/*BOOL*/
653
leapsec_autokey_tai(
654
  int            tai_offset,
655
  uint32_t       ntpnow    ,
656
  const time_t * pivot     )
657
0
{
658
0
  leap_table_t * pt;
659
0
  leap_era_t     era;
660
0
  vint64         now64;
661
0
  int            idx;
662
663
0
  (void)tai_offset;
664
0
  pt = leapsec_get_table(FALSE);
665
666
  /* Bail out if the basic offset is not zero and the putative
667
   * offset is bigger than 10s. That was in 1972 -- we don't want
668
   * to go back that far!
669
   */
670
0
  if (pt->head.base_tai != 0 || tai_offset < 10)
671
0
    return FALSE;
672
673
  /* If there's already data in the table, check if an update is
674
   * possible. Update is impossible if there are static entries
675
   * (since this indicates a valid leapsecond file) or if we're
676
   * too close to a leapsecond transition: We do not know on what
677
   * side the transition the sender might have been, so we use a
678
   * dead zone around the transition.
679
   */
680
681
  /* Check for static entries */
682
0
  for (idx = 0; idx != pt->head.size; idx++)
683
0
    if ( ! pt->info[idx].dynls)
684
0
      return FALSE;
685
686
  /* get the fulll time stamp and leap era for it */
687
0
  now64 = ntpcal_ntp_to_ntp(ntpnow, pivot);
688
0
  fetch_leap_era(&era, pt, &now64);
689
690
  /* check the limits with 20s dead band */
691
0
  era.ebase = addv64i32(&era.ebase,  20);
692
0
  if (ucmpv64(&now64, &era.ebase) < 0)
693
0
    return FALSE;
694
695
0
  era.ttime = addv64i32(&era.ttime, -20);
696
0
  if (ucmpv64(&now64, &era.ttime) > 0)
697
0
    return FALSE;
698
699
  /* Here we can proceed. Calculate the delta update. */
700
0
  tai_offset -= era.taiof;
701
702
  /* Shift the header info offsets. */
703
0
  pt->head.base_tai += tai_offset;
704
0
  pt->head.this_tai += tai_offset;
705
0
  pt->head.next_tai += tai_offset;
706
707
  /* Shift table entry offsets (if any) */
708
0
  for (idx = 0; idx != pt->head.size; idx++)
709
0
    pt->info[idx].taiof += tai_offset;
710
711
  /* claim success... */
712
0
  return TRUE;
713
0
}
714
715
716
/* =====================================================================
717
 * internal helpers
718
 */
719
720
/* [internal] Reset / init the time window in the leap processor to
721
 * force reload on next query. Since a leap transition cannot take place
722
 * at an odd second, the value chosen avoids spurious leap transition
723
 * triggers. Making all three times equal forces a reload. Using the
724
 * maximum value for unsigned 64 bits makes finding the next leap frame
725
 * a bit easier.
726
 */
727
static void
728
reset_times(
729
  leap_table_t * pt)
730
2
{
731
2
  memset(&pt->head.ebase, 0xFF, sizeof(vint64));
732
2
  pt->head.stime = pt->head.ebase;
733
2
  pt->head.ttime = pt->head.ebase;
734
2
  pt->head.dtime = pt->head.ebase;
735
2
}
736
737
/* [internal] Add raw data to the table, removing old entries on the
738
 * fly. This cannot fail currently.
739
 */
740
static int/*BOOL*/
741
add_range(
742
  leap_table_t *      pt,
743
  const leap_info_t * pi)
744
0
{
745
  /* If the table is full, make room by throwing out the oldest
746
   * entry. But remember the accumulated leap seconds!
747
   *
748
   * Setting the first entry is a bit tricky, too: Simply assuming
749
   * it is an insertion is wrong if the first entry is a dynamic
750
   * leap second removal. So we decide on the sign -- if the first
751
   * entry has a negative offset, we assume that it is a leap
752
   * second removal. In both cases the table base offset is set
753
   * accordingly to reflect the decision.
754
   *
755
   * In practice starting with a removal can only happen if the
756
   * first entry is a dynamic request without having a leap file
757
   * for the history proper.
758
   */
759
0
  if (pt->head.size == 0) {
760
0
    if (pi->taiof >= 0)
761
0
      pt->head.base_tai = pi->taiof - 1;
762
0
    else
763
0
      pt->head.base_tai = pi->taiof + 1;
764
0
  } else if (pt->head.size >= MAX_HIST) {
765
0
    pt->head.size     = MAX_HIST - 1;
766
0
    pt->head.base_tai = pt->info[pt->head.size].taiof;
767
0
  }
768
769
  /* make room in lower end and insert item */
770
0
  memmove(pt->info+1, pt->info, pt->head.size*sizeof(*pt->info));
771
0
  pt->info[0] = *pi;
772
0
  pt->head.size++;
773
774
  /* invalidate the cached limit data -- we might have news ;-)
775
   *
776
   * This blocks a spurious transition detection. OTOH, if you add
777
   * a value after the last query before a leap transition was
778
   * expected to occur, this transition trigger is lost. But we
779
   * can probably live with that.
780
   */
781
0
  reset_times(pt);
782
0
  return TRUE;
783
0
}
784
785
/* [internal] given a reader function, read characters into a buffer
786
 * until either EOL or EOF is reached. Makes sure that the buffer is
787
 * always NUL terminated, but silently truncates excessive data. The
788
 * EOL-marker ('\n') is *not* stored in the buffer.
789
 *
790
 * Returns the pointer to the buffer, unless EOF was reached when trying
791
 * to read the first character of a line.
792
 */
793
static char *
794
get_line(
795
  leapsec_reader func,
796
  void *         farg,
797
  char *         buff,
798
  size_t         size)
799
0
{
800
0
  int   ch;
801
0
  char *ptr;
802
803
  /* if we cannot even store the delimiter, declare failure */
804
0
  if (buff == NULL || size == 0)
805
0
    return NULL;
806
807
0
  ptr = buff;
808
0
  while (EOF != (ch = (*func)(farg)) && '\n' != ch)
809
0
    if (size > 1) {
810
0
      size--;
811
0
      *ptr++ = (char)ch;
812
0
    }
813
  /* discard trailing whitespace */
814
0
  while (ptr != buff && isspace((u_char)ptr[-1]))
815
0
    ptr--;
816
0
  *ptr = '\0';
817
0
  return (ptr == buff && ch == EOF) ? NULL : buff;
818
0
}
819
820
/* [internal] skips whitespace characters from a character buffer. */
821
static char *
822
skipws(
823
  const char *ptr)
824
0
{
825
0
  while (isspace((u_char)*ptr))
826
0
    ptr++;
827
0
  return (char*)noconst(ptr);
828
0
}
829
830
/* [internal] check if a strtoXYZ ended at EOL or whitespace and
831
 * converted something at all. Return TRUE if something went wrong.
832
 */
833
static int/*BOOL*/
834
parsefail(
835
  const char * cp,
836
  const char * ep)
837
0
{
838
0
  return (cp == ep)
839
0
      || (*ep && *ep != '#' && !isspace((u_char)*ep));
840
0
}
841
842
/* [internal] reload the table limits around the given time stamp. This
843
 * is where the real work is done when it comes to table lookup and
844
 * evaluation. Some care has been taken to have correct code for dealing
845
 * with boundary conditions and empty tables.
846
 *
847
 * In electric mode, transition and trip time are the same. In dumb
848
 * mode, the difference of the TAI offsets must be taken into account
849
 * and trip time and transition time become different. The difference
850
 * becomes the warping distance when the trip time is reached.
851
 */
852
static void
853
reload_limits(
854
  leap_table_t * pt,
855
  const vint64 * ts)
856
0
{
857
0
  int idx;
858
859
  /* Get full time and search the true lower bound. Use a
860
   * simple loop here, since the number of entries does
861
   * not warrant a binary search. This also works for an empty
862
   * table, so there is no shortcut for that case.
863
   */
864
0
  for (idx = 0; idx != pt->head.size; idx++)
865
0
    if (ucmpv64(ts, &pt->info[idx].ttime) >= 0)
866
0
      break;
867
868
  /* get time limits with proper bound conditions. Note that the
869
   * bounds of the table will be observed even if the table is
870
   * empty -- no undefined condition must arise from this code.
871
   */
872
0
  if (idx >= pt->head.size) {
873
0
    memset(&pt->head.ebase, 0x00, sizeof(vint64));
874
0
    pt->head.this_tai = pt->head.base_tai;
875
0
  } else {
876
0
    pt->head.ebase    = pt->info[idx].ttime;
877
0
    pt->head.this_tai = pt->info[idx].taiof;
878
0
  }
879
0
  if (--idx >= 0) {
880
0
    pt->head.next_tai = pt->info[idx].taiof;
881
0
    pt->head.dynls    = pt->info[idx].dynls;
882
0
    pt->head.ttime    = pt->info[idx].ttime;
883
884
0
    if (_electric)
885
0
      pt->head.dtime = pt->head.ttime;
886
0
                else
887
0
      pt->head.dtime = addv64i32(
888
0
        &pt->head.ttime,
889
0
        pt->head.next_tai - pt->head.this_tai);
890
891
0
    pt->head.stime = subv64u32(
892
0
      &pt->head.ttime, pt->info[idx].stime);
893
894
0
  } else {
895
0
    memset(&pt->head.ttime, 0xFF, sizeof(vint64));
896
0
    pt->head.stime    = pt->head.ttime;
897
0
    pt->head.dtime    = pt->head.ttime;
898
0
    pt->head.next_tai = pt->head.this_tai;
899
0
    pt->head.dynls    = 0;
900
0
  }
901
0
}
902
903
/* [internal] fetch the leap era for a given time stamp.
904
 * This is a cut-down version the algorithm used to reload the table
905
 * limits, but it does not update any global state and provides just the
906
 * era information for a given time stamp.
907
 */
908
static void
909
fetch_leap_era(
910
  leap_era_t         * into,
911
  const leap_table_t * pt  ,
912
  const vint64       * ts  )
913
0
{
914
0
  int idx;
915
916
  /* Simple search loop, also works with empty table. */
917
0
  for (idx = 0; idx != pt->head.size; idx++)
918
0
    if (ucmpv64(ts, &pt->info[idx].ttime) >= 0)
919
0
      break;
920
  /* fetch era data, keeping an eye on boundary conditions */
921
0
  if (idx >= pt->head.size) {
922
0
    memset(&into->ebase, 0x00, sizeof(vint64));
923
0
    into->taiof = pt->head.base_tai;
924
0
  } else {
925
0
    into->ebase = pt->info[idx].ttime;
926
0
    into->taiof = pt->info[idx].taiof;
927
0
  }
928
0
  if (--idx >= 0)
929
0
    into->ttime = pt->info[idx].ttime;
930
0
  else
931
0
    memset(&into->ttime, 0xFF, sizeof(vint64));
932
0
}
933
934
/* [internal] Take a time stamp and create a leap second frame for
935
 * it. This will schedule a leap second for the beginning of the next
936
 * month, midnight UTC. The 'insert' argument tells if a leap second is
937
 * added (!=0) or removed (==0). We do not handle multiple inserts
938
 * (yet?)
939
 *
940
 * Returns 1 if the insert worked, 0 otherwise. (It's not possible to
941
 * insert a leap second into the current history -- only appending
942
 * towards the future is allowed!)
943
 */
944
static int/*BOOL*/
945
leapsec_add(
946
  leap_table_t*  pt    ,
947
  const vint64 * now64 ,
948
  int            insert)
949
0
{
950
0
  vint64    ttime, starttime;
951
0
  struct calendar fts;
952
0
  leap_info_t li;
953
954
  /* Check against the table expiration and the latest available
955
   * leap entry. Do not permit inserts, only appends, and only if
956
   * the extend the table beyond the expiration!
957
   */
958
0
  if (   ucmpv64(now64, &pt->head.expire) < 0
959
0
      || (pt->head.size && ucmpv64(now64, &pt->info[0].ttime) <= 0)) {
960
0
    errno = ERANGE;
961
0
    return FALSE;
962
0
  }
963
964
0
  ntpcal_ntp64_to_date(&fts, now64);
965
  /* To guard against dangling leap flags: do not accept leap
966
   * second request on the 1st hour of the 1st day of the month.
967
   */
968
0
  if (fts.monthday == 1 && fts.hour == 0) {
969
0
    errno = EINVAL;
970
0
    return FALSE;
971
0
  }
972
973
  /* Ok, do the remaining calculations */
974
0
  fts.monthday = 1;
975
0
  fts.hour     = 0;
976
0
  fts.minute   = 0;
977
0
  fts.second   = 0;
978
0
  starttime = ntpcal_date_to_ntp64(&fts);
979
0
  fts.month++;
980
0
  ttime = ntpcal_date_to_ntp64(&fts);
981
982
0
  li.ttime = ttime;
983
0
  li.stime = ttime.D_s.lo - starttime.D_s.lo;
984
0
  li.taiof = (pt->head.size ? pt->info[0].taiof : pt->head.base_tai)
985
0
           + (insert ? 1 : -1);
986
0
  li.dynls = 1;
987
0
  return add_range(pt, &li);
988
0
}
989
990
/* [internal] Given a time stamp for a leap insertion (the exact begin
991
 * of the new leap era), create new leap frame and put it into the
992
 * table. This is the work horse for reading a leap file and getting a
993
 * leap second update via authenticated network packet.
994
 */
995
int/*BOOL*/
996
leapsec_raw(
997
  leap_table_t * pt,
998
  const vint64 * ttime,
999
  int            taiof,
1000
  int            dynls)
1001
0
{
1002
0
  vint64    starttime;
1003
0
  struct calendar fts;
1004
0
  leap_info_t li;
1005
1006
  /* Check that we either extend the table or get a duplicate of
1007
   * the latest entry. The latter is a benevolent overwrite with
1008
   * identical data and could happen if we get an autokey message
1009
   * that extends the lifetime of the current leapsecond table.
1010
   * Otherwise paranoia rulez!
1011
   */
1012
0
  if (pt->head.size) {
1013
0
    int cmp = ucmpv64(ttime, &pt->info[0].ttime);
1014
0
    if (cmp == 0)
1015
0
      cmp -= (taiof != pt->info[0].taiof);
1016
0
    if (cmp < 0) {
1017
0
      errno = ERANGE;
1018
0
      return FALSE;
1019
0
    }
1020
0
    if (cmp == 0)
1021
0
      return TRUE;
1022
0
  }
1023
1024
0
  ntpcal_ntp64_to_date(&fts, ttime);
1025
  /* If this does not match the exact month start, bail out. */
1026
0
  if (fts.monthday != 1 || fts.hour || fts.minute || fts.second) {
1027
0
    errno = EINVAL;
1028
0
    return FALSE;
1029
0
  }
1030
0
  fts.month--; /* was in range 1..12, no overflow here! */
1031
0
  starttime = ntpcal_date_to_ntp64(&fts);
1032
0
  li.ttime = *ttime;
1033
0
  li.stime = ttime->D_s.lo - starttime.D_s.lo;
1034
0
  li.taiof = (int16_t)taiof;
1035
0
  li.dynls = (dynls != 0);
1036
0
  return add_range(pt, &li);
1037
0
}
1038
1039
/* [internal] Do a wrap-around save range inclusion check.
1040
 * Returns TRUE if x in [lo,hi[ (intervall open on right side) with full
1041
 * handling of an overflow / wrap-around.
1042
 */
1043
static int/*BOOL*/
1044
betweenu32(
1045
  uint32_t lo,
1046
  uint32_t x,
1047
  uint32_t hi)
1048
0
{
1049
0
  int rc;
1050
1051
0
  if (lo <= hi)
1052
0
    rc = (lo <= x) && (x < hi);
1053
0
  else
1054
0
    rc = (lo <= x) || (x < hi);
1055
0
  return rc;
1056
0
}
1057
1058
/* =====================================================================
1059
 * validation stuff
1060
 */
1061
1062
typedef struct {
1063
  unsigned char hv[ISC_SHA1_DIGESTLENGTH];
1064
} sha1_digest;
1065
1066
/* [internal] parse a digest line to get the hash signature
1067
 * The NIST code creating the hash writes them out as 5 hex integers
1068
 * without leading zeros. This makes reading them back as hex-encoded
1069
 * BLOB impossible, because there might be less than 40 hex digits.
1070
 *
1071
 * The solution is to read the values back as integers, and then do the
1072
 * byte twiddle necessary to get it into an array of 20 chars. The
1073
 * drawback is that it permits any acceptable number syntax provided by
1074
 * 'scanf()' and 'strtoul()', including optional signs and '0x'
1075
 * prefixes.
1076
 */
1077
static int/*BOOL*/
1078
do_leap_hash(
1079
  sha1_digest * mac,
1080
  char const  * cp )
1081
0
{
1082
0
  int wi, di, num, len;
1083
0
  unsigned long tmp[5];
1084
1085
0
  memset(mac, 0, sizeof(*mac));
1086
0
  num = sscanf(cp, " %lx %lx %lx %lx %lx%n",
1087
0
         &tmp[0], &tmp[1], &tmp[2], &tmp[3], &tmp[4],
1088
0
         &len);
1089
0
  if (num != 5 || cp[len] > ' ')
1090
0
    return FALSE;
1091
1092
  /* now do the byte twiddle */
1093
0
  for (wi=0; wi < 5; ++wi)
1094
0
    for (di=3; di >= 0; --di) {
1095
0
      mac->hv[wi*4 + di] =
1096
0
        (unsigned char)(tmp[wi] & 0x0FF);
1097
0
      tmp[wi] >>= 8;
1098
0
    }
1099
0
  return TRUE;
1100
0
}
1101
1102
/* [internal] add the digits of a data line to the hash, stopping at the
1103
 * next hash ('#') character.
1104
 */
1105
static void
1106
do_hash_data(
1107
  isc_sha1_t * mdctx,
1108
  char const * cp   )
1109
0
{
1110
0
  unsigned char  text[32]; // must be power of two!
1111
0
  unsigned int   tlen =  0;
1112
0
  unsigned char  ch;
1113
1114
0
  while ('\0' != (ch = *cp++) && '#' != ch)
1115
0
    if (isdigit(ch)) {
1116
0
      text[tlen++] = ch;
1117
0
      tlen &= (sizeof(text)-1);
1118
0
      if (0 == tlen)
1119
0
        isc_sha1_update(
1120
0
          mdctx, text, sizeof(text));
1121
0
    }
1122
1123
0
  if (0 < tlen)
1124
0
    isc_sha1_update(mdctx, text, tlen);
1125
0
}
1126
1127
/* given a reader and a reader arg, calculate and validate the the hash
1128
 * signature of a NIST leap second file.
1129
 */
1130
int
1131
leapsec_validate(
1132
  leapsec_reader func,
1133
  void *         farg)
1134
0
{
1135
0
  isc_sha1_t     mdctx;
1136
0
  sha1_digest    rdig, ldig; /* remote / local digests */
1137
0
  char           line[50];
1138
0
  int            hlseen = -1;
1139
1140
0
  isc_sha1_init(&mdctx);
1141
0
  while (get_line(func, farg, line, sizeof(line))) {
1142
0
    if (!strncmp(line, "#h", 2))
1143
0
      hlseen = do_leap_hash(&rdig, line+2);
1144
0
    else if (!strncmp(line, "#@", 2))
1145
0
      do_hash_data(&mdctx, line+2);
1146
0
    else if (!strncmp(line, "#$", 2))
1147
0
      do_hash_data(&mdctx, line+2);
1148
0
    else if (isdigit((unsigned char)line[0]))
1149
0
      do_hash_data(&mdctx, line);
1150
0
  }
1151
0
  isc_sha1_final(&mdctx, ldig.hv);
1152
0
  isc_sha1_invalidate(&mdctx);
1153
1154
0
  if (0 > hlseen)
1155
0
    return LSVALID_NOHASH;
1156
0
  if (0 == hlseen)
1157
0
    return LSVALID_BADFORMAT;
1158
0
  if (0 != memcmp(&rdig, &ldig, sizeof(sha1_digest)))
1159
0
    return LSVALID_BADHASH;
1160
0
  return LSVALID_GOODHASH;
1161
0
}
1162
1163
/*
1164
 * lstostr - prettyprint NTP seconds
1165
 */
1166
static const char *
1167
lstostr(
1168
  const vint64 * ts)
1169
0
{
1170
0
  char *    buf;
1171
0
  struct calendar tm;
1172
1173
0
  LIB_GETBUF(buf);
1174
1175
0
  if ( ! (ts->d_s.hi >= 0 && ntpcal_ntp64_to_date(&tm, ts) >= 0))
1176
0
    snprintf(buf, LIB_BUFLENGTH, "%s", "9999-12-31T23:59:59Z");
1177
0
  else
1178
0
    snprintf(buf, LIB_BUFLENGTH, "%04d-%02d-%02dT%02d:%02d:%02dZ",
1179
0
      tm.year, tm.month, tm.monthday,
1180
0
      tm.hour, tm.minute, tm.second);
1181
1182
0
  return buf;
1183
0
}
1184
1185
/* reset the global state for unit tests */
1186
void
1187
leapsec_ut_pristine(void)
1188
0
{
1189
0
  memset(_ltab, 0, sizeof(_ltab));
1190
0
  _lptr     = NULL;
1191
0
  _electric = 0;
1192
0
}
1193
1194
1195
1196
/* -*- that's all folks! -*- */