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