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