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