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