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