ntp_leapsec.c revision 358659
1/*
2 * ntp_leapsec.c - leap second processing for NTPD
3 *
4 * Written by Juergen Perlinger (perlinger@ntp.org) for the NTP project.
5 * The contents of 'html/copyright.html' apply.
6 * ----------------------------------------------------------------------
7 * This is an attempt to get the leap second handling into a dedicated
8 * module to make the somewhat convoluted logic testable.
9 */
10
11#include <config.h>
12#include <sys/types.h>
13#include <sys/stat.h>
14#include <ctype.h>
15
16#include "ntp_types.h"
17#include "ntp_fp.h"
18#include "ntp_stdlib.h"
19#include "ntp_calendar.h"
20#include "ntp_leapsec.h"
21#include "ntp.h"
22#include "vint64ops.h"
23#include "lib_strbuf.h"
24
25#include "isc/sha1.h"
26
27static 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 */
35static inline void*
36noconst(
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
53struct 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};
59typedef struct leap_info leap_info_t;
60
61struct 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};
74typedef struct leap_head leap_head_t;
75
76struct 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 */
83static leap_table_t _ltab[2], *_lptr;
84static int/*BOOL*/  _electric;
85
86/* Forward decls of local helpers */
87static int    add_range(leap_table_t*, const leap_info_t*);
88static char * get_line(leapsec_reader, void*, char*, size_t);
89static char * skipws(const char*);
90static int    parsefail(const char * cp, const char * ep);
91static void   reload_limits(leap_table_t*, const vint64*);
92static void   fetch_leap_era(leap_era_t*, const leap_table_t*,
93			     const vint64*);
94static int    betweenu32(uint32_t, uint32_t, uint32_t);
95static void   reset_times(leap_table_t*);
96static int    leapsec_add(leap_table_t*, const vint64*, int);
97static int    leapsec_raw(leap_table_t*, const vint64 *, int, int);
98static const char * lstostr(const vint64 * ts);
99
100/* =====================================================================
101 * Get & Set the current leap table
102 */
103
104/* ------------------------------------------------------------------ */
105leap_table_t *
106leapsec_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/* ------------------------------------------------------------------ */
132int/*BOOL*/
133leapsec_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/* ------------------------------------------------------------------ */
142int/*BOOL*/
143leapsec_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 */
167void
168leapsec_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 */
179int/*BOOL*/
180leapsec_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
240fail_read:
241	errno = EILSEQ;
242fail_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 */
251void
252leapsec_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
284int/*BOOL*/
285leapsec_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/* ------------------------------------------------------------------ */
374int/*BOOL*/
375leapsec_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/* ------------------------------------------------------------------ */
390int/*BOOL*/
391leapsec_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 */
410void
411leapsec_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 */
421int/*BOOL*/
422leapsec_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/* ------------------------------------------------------------------ */
498int/*BOOL*/
499leapsec_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	 * [perlinger@ntp.org]
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/* ------------------------------------------------------------------ */
563void
564leapsec_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/* ------------------------------------------------------------------ */
574int/*BOOL*/
575leapsec_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/* ------------------------------------------------------------------ */
588int32_t
589leapsec_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 */
604int/*BOOL*/
605leapsec_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/* ------------------------------------------------------------------ */
639int/*BOOL*/
640leapsec_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/* ------------------------------------------------------------------ */
655int/*BOOL*/
656leapsec_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 */
730static void
731reset_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 */
743static int/*BOOL*/
744add_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 */
796static char *
797get_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. */
824static char *
825skipws(
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 */
836static int/*BOOL*/
837parsefail(
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 */
855static void
856reload_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 */
911static void
912fetch_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 */
947static int/*BOOL*/
948leapsec_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 */
998int/*BOOL*/
999leapsec_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 */
1046static int/*BOOL*/
1047betweenu32(
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
1065typedef 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 */
1080static int/*BOOL*/
1081do_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 */
1108static void
1109do_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 */
1133int
1134leapsec_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 */
1169static const char *
1170lstostr(
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 */
1189void
1190leapsec_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