1275970Scy/*
2275970Scy * ntp_leapsec.c - leap second processing for NTPD
3275970Scy *
4275970Scy * Written by Juergen Perlinger (perlinger@ntp.org) for the NTP project.
5275970Scy * The contents of 'html/copyright.html' apply.
6275970Scy * ----------------------------------------------------------------------
7275970Scy * This is an attempt to get the leap second handling into a dedicated
8275970Scy * module to make the somewhat convoluted logic testable.
9275970Scy */
10275970Scy
11275970Scy#include <config.h>
12275970Scy#include <sys/types.h>
13275970Scy#include <sys/stat.h>
14275970Scy#include <ctype.h>
15275970Scy
16275970Scy#include "ntp_types.h"
17275970Scy#include "ntp_fp.h"
18275970Scy#include "ntp_stdlib.h"
19275970Scy#include "ntp_calendar.h"
20275970Scy#include "ntp_leapsec.h"
21275970Scy#include "ntp.h"
22275970Scy#include "vint64ops.h"
23275970Scy#include "lib_strbuf.h"
24275970Scy
25275970Scy#include "isc/sha1.h"
26275970Scy
27275970Scystatic const char * const logPrefix = "leapsecond file";
28275970Scy
29275970Scy/* ---------------------------------------------------------------------
30275970Scy * GCC is rather sticky with its 'const' attribute. We have to do it more
31275970Scy * explicit than with a cast if we want to get rid of a CONST qualifier.
32275970Scy * Greetings from the PASCAL world, where casting was only possible via
33275970Scy * untagged unions...
34275970Scy */
35275970Scystatic inline void*
36275970Scynoconst(
37275970Scy	const void* ptr
38275970Scy	)
39275970Scy{
40275970Scy	union {
41275970Scy		const void * cp;
42275970Scy		void *       vp;
43275970Scy	} tmp;
44275970Scy	tmp.cp = ptr;
45275970Scy	return tmp.vp;
46275970Scy}
47275970Scy
48275970Scy/* ---------------------------------------------------------------------
49275970Scy * Our internal data structure
50275970Scy */
51275970Scy#define MAX_HIST 10	/* history of leap seconds */
52275970Scy
53275970Scystruct leap_info {
54275970Scy	vint64   ttime;	/* transition time (after the step, ntp scale) */
55275970Scy	uint32_t stime;	/* schedule limit (a month before transition)  */
56275970Scy	int16_t  taiof;	/* TAI offset on and after the transition      */
57275970Scy	uint8_t  dynls; /* dynamic: inserted on peer/clock request     */
58275970Scy};
59275970Scytypedef struct leap_info leap_info_t;
60275970Scy
61275970Scystruct leap_head {
62275970Scy	vint64   update; /* time of information update                 */
63275970Scy	vint64   expire; /* table expiration time                      */
64275970Scy	uint16_t size;	 /* number of infos in table	               */
65275970Scy	int16_t  base_tai;	/* total leaps before first entry      */
66275970Scy	int16_t  this_tai;	/* current TAI offset	               */
67275970Scy	int16_t  next_tai;	/* TAI offset after 'when'             */
68275970Scy	vint64   dtime;	 /* due time (current era end)                 */
69275970Scy	vint64   ttime;	 /* nominal transition time (next era start)   */
70275970Scy	vint64   stime;	 /* schedule time (when we take notice)        */
71275970Scy	vint64   ebase;	 /* base time of this leap era                 */
72275970Scy	uint8_t  dynls;	 /* next leap is dynamic (by peer request)     */
73275970Scy};
74275970Scytypedef struct leap_head leap_head_t;
75275970Scy
76275970Scystruct leap_table {
77275970Scy	leap_signature_t lsig;
78275970Scy	leap_head_t	 head;
79275970Scy	leap_info_t  	 info[MAX_HIST];
80275970Scy};
81275970Scy
82275970Scy/* Where we store our tables */
83275970Scystatic leap_table_t _ltab[2], *_lptr;
84275970Scystatic int/*BOOL*/  _electric;
85275970Scy
86275970Scy/* Forward decls of local helpers */
87275970Scystatic int    add_range(leap_table_t*, const leap_info_t*);
88275970Scystatic char * get_line(leapsec_reader, void*, char*, size_t);
89275970Scystatic char * skipws(const char*);
90275970Scystatic int    parsefail(const char * cp, const char * ep);
91275970Scystatic void   reload_limits(leap_table_t*, const vint64*);
92285612Sdelphijstatic void   fetch_leap_era(leap_era_t*, const leap_table_t*,
93285612Sdelphij			     const vint64*);
94275970Scystatic int    betweenu32(uint32_t, uint32_t, uint32_t);
95275970Scystatic void   reset_times(leap_table_t*);
96275970Scystatic int    leapsec_add(leap_table_t*, const vint64*, int);
97275970Scystatic int    leapsec_raw(leap_table_t*, const vint64 *, int, int);
98285612Sdelphijstatic const char * lstostr(const vint64 * ts);
99275970Scy
100275970Scy/* =====================================================================
101275970Scy * Get & Set the current leap table
102275970Scy */
103275970Scy
104275970Scy/* ------------------------------------------------------------------ */
105275970Scyleap_table_t *
106275970Scyleapsec_get_table(
107275970Scy	int alternate)
108275970Scy{
109275970Scy	leap_table_t *p1, *p2;
110275970Scy
111275970Scy	p1 = _lptr;
112285612Sdelphij	if (p1 == &_ltab[0]) {
113285612Sdelphij		p2 = &_ltab[1];
114285612Sdelphij	} else if (p1 == &_ltab[1]) {
115285612Sdelphij		p2 = &_ltab[0];
116285612Sdelphij	} else {
117285612Sdelphij		p1 = &_ltab[0];
118285612Sdelphij		p2 = &_ltab[1];
119285612Sdelphij		reset_times(p1);
120285612Sdelphij		reset_times(p2);
121285612Sdelphij		_lptr = p1;
122285612Sdelphij	}
123275970Scy	if (alternate) {
124275970Scy		memcpy(p2, p1, sizeof(leap_table_t));
125275970Scy		p1 = p2;
126275970Scy	}
127275970Scy
128275970Scy	return p1;
129275970Scy}
130275970Scy
131275970Scy/* ------------------------------------------------------------------ */
132275970Scyint/*BOOL*/
133275970Scyleapsec_set_table(
134275970Scy	leap_table_t * pt)
135275970Scy{
136275970Scy	if (pt == &_ltab[0] || pt == &_ltab[1])
137275970Scy		_lptr = pt;
138275970Scy	return _lptr == pt;
139275970Scy}
140275970Scy
141275970Scy/* ------------------------------------------------------------------ */
142275970Scyint/*BOOL*/
143275970Scyleapsec_electric(
144275970Scy	int/*BOOL*/ on)
145275970Scy{
146275970Scy	int res = _electric;
147275970Scy	if (on < 0)
148275970Scy		return res;
149275970Scy
150275970Scy	_electric = (on != 0);
151275970Scy	if (_electric == res)
152275970Scy		return res;
153275970Scy
154275970Scy	if (_lptr == &_ltab[0] || _lptr == &_ltab[1])
155275970Scy		reset_times(_lptr);
156275970Scy
157275970Scy	return res;
158275970Scy}
159275970Scy
160275970Scy/* =====================================================================
161275970Scy * API functions that operate on tables
162275970Scy */
163275970Scy
164275970Scy/* ---------------------------------------------------------------------
165275970Scy * Clear all leap second data. Use it for init & cleanup
166275970Scy */
167275970Scyvoid
168275970Scyleapsec_clear(
169275970Scy	leap_table_t * pt)
170275970Scy{
171275970Scy	memset(&pt->lsig, 0, sizeof(pt->lsig));
172275970Scy	memset(&pt->head, 0, sizeof(pt->head));
173275970Scy	reset_times(pt);
174275970Scy}
175275970Scy
176275970Scy/* ---------------------------------------------------------------------
177275970Scy * Load a leap second file and check expiration on the go
178275970Scy */
179275970Scyint/*BOOL*/
180275970Scyleapsec_load(
181275970Scy	leap_table_t * pt  ,
182275970Scy	leapsec_reader func,
183275970Scy	void *         farg,
184275970Scy	int            use_build_limit)
185275970Scy{
186275970Scy	char   *cp, *ep, linebuf[50];
187275970Scy	vint64 ttime, limit;
188275970Scy	long   taiof;
189275970Scy	struct calendar build;
190275970Scy
191275970Scy	leapsec_clear(pt);
192285612Sdelphij	if (use_build_limit && ntpcal_get_build_date(&build)) {
193285612Sdelphij		/* don't prune everything -- permit the last 10yrs
194285612Sdelphij		 * before build.
195285612Sdelphij		 */
196285612Sdelphij		build.year -= 10;
197275970Scy		limit = ntpcal_date_to_ntp64(&build);
198285612Sdelphij	} else {
199275970Scy		memset(&limit, 0, sizeof(limit));
200285612Sdelphij	}
201275970Scy
202275970Scy	while (get_line(func, farg, linebuf, sizeof(linebuf))) {
203275970Scy		cp = linebuf;
204275970Scy		if (*cp == '#') {
205275970Scy			cp++;
206275970Scy			if (*cp == '@') {
207275970Scy				cp = skipws(cp+1);
208275970Scy				pt->head.expire = strtouv64(cp, &ep, 10);
209275970Scy				if (parsefail(cp, ep))
210275970Scy					goto fail_read;
211275970Scy				pt->lsig.etime = pt->head.expire.D_s.lo;
212275970Scy			} else if (*cp == '$') {
213275970Scy				cp = skipws(cp+1);
214275970Scy				pt->head.update = strtouv64(cp, &ep, 10);
215275970Scy				if (parsefail(cp, ep))
216275970Scy					goto fail_read;
217285612Sdelphij			}
218275970Scy		} else if (isdigit((u_char)*cp)) {
219275970Scy			ttime = strtouv64(cp, &ep, 10);
220275970Scy			if (parsefail(cp, ep))
221275970Scy				goto fail_read;
222275970Scy			cp = skipws(ep);
223275970Scy			taiof = strtol(cp, &ep, 10);
224275970Scy			if (   parsefail(cp, ep)
225275970Scy			    || taiof > SHRT_MAX || taiof < SHRT_MIN)
226275970Scy				goto fail_read;
227275970Scy			if (ucmpv64(&ttime, &limit) >= 0) {
228275970Scy				if (!leapsec_raw(pt, &ttime,
229275970Scy						 taiof, FALSE))
230275970Scy					goto fail_insn;
231275970Scy			} else {
232275970Scy				pt->head.base_tai = (int16_t)taiof;
233275970Scy			}
234275970Scy			pt->lsig.ttime = ttime.D_s.lo;
235275970Scy			pt->lsig.taiof = (int16_t)taiof;
236275970Scy		}
237275970Scy	}
238275970Scy	return TRUE;
239275970Scy
240275970Scyfail_read:
241275970Scy	errno = EILSEQ;
242275970Scyfail_insn:
243275970Scy	leapsec_clear(pt);
244275970Scy	return FALSE;
245275970Scy}
246275970Scy
247275970Scy/* ---------------------------------------------------------------------
248275970Scy * Dump a table in human-readable format. Use 'fprintf' and a FILE
249275970Scy * pointer if you want to get it printed into a stream.
250275970Scy */
251275970Scyvoid
252275970Scyleapsec_dump(
253275970Scy	const leap_table_t * pt  ,
254275970Scy	leapsec_dumper       func,
255275970Scy	void *               farg)
256275970Scy{
257275970Scy	int             idx;
258275970Scy	vint64          ts;
259275970Scy	struct calendar atb, ttb;
260275970Scy
261275970Scy	ntpcal_ntp64_to_date(&ttb, &pt->head.expire);
262275970Scy	(*func)(farg, "leap table (%u entries) expires at %04u-%02u-%02u:\n",
263275970Scy		pt->head.size,
264275970Scy		ttb.year, ttb.month, ttb.monthday);
265275970Scy	idx = pt->head.size;
266275970Scy	while (idx-- != 0) {
267275970Scy		ts = pt->info[idx].ttime;
268275970Scy		ntpcal_ntp64_to_date(&ttb, &ts);
269275970Scy		ts = subv64u32(&ts, pt->info[idx].stime);
270275970Scy		ntpcal_ntp64_to_date(&atb, &ts);
271275970Scy
272275970Scy		(*func)(farg, "%04u-%02u-%02u [%c] (%04u-%02u-%02u) - %d\n",
273275970Scy			ttb.year, ttb.month, ttb.monthday,
274275970Scy			"-*"[pt->info[idx].dynls != 0],
275275970Scy			atb.year, atb.month, atb.monthday,
276275970Scy			pt->info[idx].taiof);
277275970Scy	}
278275970Scy}
279275970Scy
280275970Scy/* =====================================================================
281275970Scy * usecase driven API functions
282275970Scy */
283275970Scy
284275970Scyint/*BOOL*/
285275970Scyleapsec_query(
286275970Scy	leap_result_t * qr   ,
287275970Scy	uint32_t        ts32 ,
288275970Scy	const time_t *  pivot)
289275970Scy{
290275970Scy	leap_table_t *   pt;
291275970Scy	vint64           ts64, last, next;
292275970Scy	uint32_t         due32;
293275970Scy	int              fired;
294275970Scy
295275970Scy	/* preset things we use later on... */
296275970Scy	fired = FALSE;
297275970Scy	ts64  = ntpcal_ntp_to_ntp(ts32, pivot);
298275970Scy	pt    = leapsec_get_table(FALSE);
299275970Scy	memset(qr, 0, sizeof(leap_result_t));
300275970Scy
301275970Scy	if (ucmpv64(&ts64, &pt->head.ebase) < 0) {
302275970Scy		/* Most likely after leap frame reset. Could also be a
303275970Scy		 * backstep of the system clock. Anyway, get the new
304275970Scy		 * leap era frame.
305275970Scy		 */
306275970Scy		reload_limits(pt, &ts64);
307285612Sdelphij	} else if (ucmpv64(&ts64, &pt->head.dtime) >= 0) {
308275970Scy		/* Boundary crossed in forward direction. This might
309275970Scy		 * indicate a leap transition, so we prepare for that
310275970Scy		 * case.
311275970Scy		 *
312275970Scy		 * Some operations below are actually NOPs in electric
313275970Scy		 * mode, but having only one code path that works for
314275970Scy		 * both modes is easier to maintain.
315285612Sdelphij		 *
316285612Sdelphij		 * There's another quirk we must keep looking out for:
317285612Sdelphij		 * If we just stepped the clock, the step might have
318285612Sdelphij		 * crossed a leap boundary. As with backward steps, we
319285612Sdelphij		 * do not want to raise the 'fired' event in that case.
320285612Sdelphij		 * So we raise the 'fired' event only if we're close to
321285612Sdelphij		 * the transition and just reload the limits otherwise.
322275970Scy		 */
323285612Sdelphij		last = addv64i32(&pt->head.dtime, 3); /* get boundary */
324285612Sdelphij		if (ucmpv64(&ts64, &last) >= 0) {
325285612Sdelphij			/* that was likely a query after a step */
326285612Sdelphij			reload_limits(pt, &ts64);
327275970Scy		} else {
328285612Sdelphij			/* close enough for deeper examination */
329285612Sdelphij			last = pt->head.ttime;
330285612Sdelphij			qr->warped = (int16_t)(last.D_s.lo -
331285612Sdelphij					       pt->head.dtime.D_s.lo);
332285612Sdelphij			next = addv64i32(&ts64, qr->warped);
333285612Sdelphij			reload_limits(pt, &next);
334285612Sdelphij			fired = ucmpv64(&pt->head.ebase, &last) == 0;
335285612Sdelphij			if (fired) {
336285612Sdelphij				ts64 = next;
337285612Sdelphij				ts32 = next.D_s.lo;
338285612Sdelphij			} else {
339285612Sdelphij				qr->warped = 0;
340285612Sdelphij			}
341275970Scy		}
342275970Scy	}
343275970Scy
344275970Scy	qr->tai_offs = pt->head.this_tai;
345285612Sdelphij	qr->ebase    = pt->head.ebase;
346285612Sdelphij	qr->ttime    = pt->head.ttime;
347275970Scy
348275970Scy	/* If before the next scheduling alert, we're done. */
349275970Scy	if (ucmpv64(&ts64, &pt->head.stime) < 0)
350275970Scy		return fired;
351275970Scy
352285612Sdelphij	/* now start to collect the remaining data */
353275970Scy	due32 = pt->head.dtime.D_s.lo;
354275970Scy
355275970Scy	qr->tai_diff  = pt->head.next_tai - pt->head.this_tai;
356275970Scy	qr->ddist     = due32 - ts32;
357275970Scy	qr->dynamic   = pt->head.dynls;
358275970Scy	qr->proximity = LSPROX_SCHEDULE;
359275970Scy
360275970Scy	/* if not in the last day before transition, we're done. */
361275970Scy	if (!betweenu32(due32 - SECSPERDAY, ts32, due32))
362275970Scy		return fired;
363285612Sdelphij
364275970Scy	qr->proximity = LSPROX_ANNOUNCE;
365275970Scy	if (!betweenu32(due32 - 10, ts32, due32))
366275970Scy		return fired;
367275970Scy
368275970Scy	/* The last 10s before the transition. Prepare for action! */
369275970Scy	qr->proximity = LSPROX_ALERT;
370275970Scy	return fired;
371275970Scy}
372275970Scy
373275970Scy/* ------------------------------------------------------------------ */
374275970Scyint/*BOOL*/
375285612Sdelphijleapsec_query_era(
376285612Sdelphij	leap_era_t *   qr   ,
377285612Sdelphij	uint32_t       ntpts,
378285612Sdelphij	const time_t * pivot)
379285612Sdelphij{
380285612Sdelphij	const leap_table_t * pt;
381285612Sdelphij	vint64               ts64;
382285612Sdelphij
383285612Sdelphij	pt   = leapsec_get_table(FALSE);
384285612Sdelphij	ts64 = ntpcal_ntp_to_ntp(ntpts, pivot);
385285612Sdelphij	fetch_leap_era(qr, pt, &ts64);
386285612Sdelphij	return TRUE;
387285612Sdelphij}
388285612Sdelphij
389285612Sdelphij/* ------------------------------------------------------------------ */
390285612Sdelphijint/*BOOL*/
391275970Scyleapsec_frame(
392275970Scy        leap_result_t *qr)
393275970Scy{
394275970Scy	const leap_table_t * pt;
395275970Scy
396275970Scy        memset(qr, 0, sizeof(leap_result_t));
397275970Scy	pt = leapsec_get_table(FALSE);
398275970Scy
399275970Scy	qr->tai_offs = pt->head.this_tai;
400275970Scy	qr->tai_diff = pt->head.next_tai - pt->head.this_tai;
401285612Sdelphij	qr->ebase    = pt->head.ebase;
402275970Scy	qr->ttime    = pt->head.ttime;
403275970Scy	qr->dynamic  = pt->head.dynls;
404275970Scy
405285612Sdelphij	return ucmpv64(&pt->head.ttime, &pt->head.stime) >= 0;
406275970Scy}
407275970Scy
408275970Scy/* ------------------------------------------------------------------ */
409275970Scy/* Reset the current leap frame */
410275970Scyvoid
411275970Scyleapsec_reset_frame(void)
412275970Scy{
413275970Scy	reset_times(leapsec_get_table(FALSE));
414275970Scy}
415275970Scy
416275970Scy/* ------------------------------------------------------------------ */
417275970Scy/* load a file from a FILE pointer. Note: If hcheck is true, load
418275970Scy * only after successful signature check. The stream must be seekable
419275970Scy * or this will fail.
420275970Scy */
421275970Scyint/*BOOL*/
422275970Scyleapsec_load_stream(
423275970Scy	FILE       * ifp  ,
424275970Scy	const char * fname,
425275970Scy	int/*BOOL*/  logall)
426275970Scy{
427275970Scy	leap_table_t *pt;
428275970Scy	int           rcheck;
429275970Scy
430275970Scy	if (NULL == fname)
431275970Scy		fname = "<unknown>";
432275970Scy
433275970Scy	rcheck = leapsec_validate((leapsec_reader)getc, ifp);
434275970Scy	if (logall)
435275970Scy		switch (rcheck)
436275970Scy		{
437275970Scy		case LSVALID_GOODHASH:
438275970Scy			msyslog(LOG_NOTICE, "%s ('%s'): good hash signature",
439275970Scy				logPrefix, fname);
440275970Scy			break;
441285612Sdelphij
442275970Scy		case LSVALID_NOHASH:
443275970Scy			msyslog(LOG_ERR, "%s ('%s'): no hash signature",
444275970Scy				logPrefix, fname);
445275970Scy			break;
446275970Scy		case LSVALID_BADHASH:
447275970Scy			msyslog(LOG_ERR, "%s ('%s'): signature mismatch",
448275970Scy				logPrefix, fname);
449275970Scy			break;
450275970Scy		case LSVALID_BADFORMAT:
451275970Scy			msyslog(LOG_ERR, "%s ('%s'): malformed hash signature",
452275970Scy				logPrefix, fname);
453275970Scy			break;
454275970Scy		default:
455275970Scy			msyslog(LOG_ERR, "%s ('%s'): unknown error code %d",
456275970Scy				logPrefix, fname, rcheck);
457275970Scy			break;
458275970Scy		}
459275970Scy	if (rcheck < 0)
460275970Scy		return FALSE;
461275970Scy
462275970Scy	rewind(ifp);
463275970Scy	pt = leapsec_get_table(TRUE);
464275970Scy	if (!leapsec_load(pt, (leapsec_reader)getc, ifp, TRUE)) {
465275970Scy		switch (errno) {
466275970Scy		case EINVAL:
467275970Scy			msyslog(LOG_ERR, "%s ('%s'): bad transition time",
468275970Scy				logPrefix, fname);
469275970Scy			break;
470275970Scy		case ERANGE:
471275970Scy			msyslog(LOG_ERR, "%s ('%s'): times not ascending",
472275970Scy				logPrefix, fname);
473275970Scy			break;
474275970Scy		default:
475275970Scy			msyslog(LOG_ERR, "%s ('%s'): parsing error",
476275970Scy				logPrefix, fname);
477275970Scy			break;
478275970Scy		}
479275970Scy		return FALSE;
480275970Scy	}
481275970Scy
482275970Scy	if (pt->head.size)
483275970Scy		msyslog(LOG_NOTICE, "%s ('%s'): loaded, expire=%s last=%s ofs=%d",
484275970Scy			logPrefix, fname, lstostr(&pt->head.expire),
485275970Scy			lstostr(&pt->info[0].ttime), pt->info[0].taiof);
486275970Scy	else
487275970Scy		msyslog(LOG_NOTICE,
488275970Scy			"%s ('%s'): loaded, expire=%s ofs=%d (no entries after build date)",
489275970Scy			logPrefix, fname, lstostr(&pt->head.expire),
490275970Scy			pt->head.base_tai);
491285612Sdelphij
492275970Scy	return leapsec_set_table(pt);
493275970Scy}
494275970Scy
495275970Scy/* ------------------------------------------------------------------ */
496275970Scyint/*BOOL*/
497275970Scyleapsec_load_file(
498275970Scy	const char  * fname,
499275970Scy	struct stat * sb_old,
500275970Scy	int/*BOOL*/   force,
501275970Scy	int/*BOOL*/   logall)
502275970Scy{
503275970Scy	FILE       * fp;
504275970Scy	struct stat  sb_new;
505275970Scy	int          rc;
506275970Scy
507275970Scy	/* just do nothing if there is no leap file */
508275970Scy	if ( !(fname && *fname) )
509275970Scy		return FALSE;
510285612Sdelphij
511275970Scy	/* try to stat the leapfile */
512275970Scy	if (0 != stat(fname, &sb_new)) {
513275970Scy		if (logall)
514275970Scy			msyslog(LOG_ERR, "%s ('%s'): stat failed: %m",
515275970Scy				logPrefix, fname);
516275970Scy		return FALSE;
517275970Scy	}
518275970Scy
519275970Scy	/* silently skip to postcheck if no new file found */
520275970Scy	if (NULL != sb_old) {
521275970Scy		if (!force
522275970Scy		 && sb_old->st_mtime == sb_new.st_mtime
523275970Scy		 && sb_old->st_ctime == sb_new.st_ctime
524275970Scy		   )
525275970Scy			return FALSE;
526275970Scy		*sb_old = sb_new;
527275970Scy	}
528275970Scy
529275970Scy	/* try to open the leap file, complain if that fails
530275970Scy	 *
531275970Scy	 * [perlinger@ntp.org]
532275970Scy	 * coverity raises a TOCTOU (time-of-check/time-of-use) issue
533275970Scy	 * here, which is not entirely helpful: While there is indeed a
534275970Scy	 * possible race condition between the 'stat()' call above and
535275970Scy	 * the 'fopen)' call below, I intentionally want to omit the
536275970Scy	 * overhead of opening the file and calling 'fstat()', because
537275970Scy	 * in most cases the file would have be to closed anyway without
538275970Scy	 * reading the contents.  I chose to disable the coverity
539275970Scy	 * warning instead.
540275970Scy	 *
541275970Scy	 * So unless someone comes up with a reasonable argument why
542275970Scy	 * this could be a real issue, I'll just try to silence coverity
543275970Scy	 * on that topic.
544275970Scy	 */
545275970Scy	/* coverity[toctou] */
546275970Scy	if ((fp = fopen(fname, "r")) == NULL) {
547275970Scy		if (logall)
548275970Scy			msyslog(LOG_ERR,
549275970Scy				"%s ('%s'): open failed: %m",
550275970Scy				logPrefix, fname);
551275970Scy		return FALSE;
552275970Scy	}
553275970Scy
554275970Scy	rc = leapsec_load_stream(fp, fname, logall);
555275970Scy	fclose(fp);
556275970Scy	return rc;
557275970Scy}
558275970Scy
559275970Scy/* ------------------------------------------------------------------ */
560275970Scyvoid
561275970Scyleapsec_getsig(
562275970Scy	leap_signature_t * psig)
563275970Scy{
564275970Scy	const leap_table_t * pt;
565275970Scy
566275970Scy	pt = leapsec_get_table(FALSE);
567275970Scy	memcpy(psig, &pt->lsig, sizeof(leap_signature_t));
568275970Scy}
569275970Scy
570275970Scy/* ------------------------------------------------------------------ */
571275970Scyint/*BOOL*/
572275970Scyleapsec_expired(
573275970Scy	uint32_t       when,
574275970Scy	const time_t * tpiv)
575275970Scy{
576275970Scy	const leap_table_t * pt;
577275970Scy	vint64 limit;
578275970Scy
579275970Scy	pt = leapsec_get_table(FALSE);
580275970Scy	limit = ntpcal_ntp_to_ntp(when, tpiv);
581275970Scy	return ucmpv64(&limit, &pt->head.expire) >= 0;
582275970Scy}
583275970Scy
584275970Scy/* ------------------------------------------------------------------ */
585275970Scyint32_t
586275970Scyleapsec_daystolive(
587275970Scy	uint32_t       when,
588275970Scy	const time_t * tpiv)
589275970Scy{
590275970Scy	const leap_table_t * pt;
591275970Scy	vint64 limit;
592275970Scy
593275970Scy	pt = leapsec_get_table(FALSE);
594275970Scy	limit = ntpcal_ntp_to_ntp(when, tpiv);
595275970Scy	limit = subv64(&pt->head.expire, &limit);
596275970Scy	return ntpcal_daysplit(&limit).hi;
597275970Scy}
598275970Scy
599275970Scy/* ------------------------------------------------------------------ */
600285612Sdelphij#if 0 /* currently unused -- possibly revived later */
601275970Scyint/*BOOL*/
602275970Scyleapsec_add_fix(
603275970Scy	int            total,
604275970Scy	uint32_t       ttime,
605275970Scy	uint32_t       etime,
606275970Scy	const time_t * pivot)
607275970Scy{
608275970Scy	time_t         tpiv;
609275970Scy	leap_table_t * pt;
610275970Scy	vint64         tt64, et64;
611275970Scy
612275970Scy	if (pivot == NULL) {
613275970Scy		time(&tpiv);
614275970Scy		pivot = &tpiv;
615275970Scy	}
616285612Sdelphij
617275970Scy	et64 = ntpcal_ntp_to_ntp(etime, pivot);
618275970Scy	tt64 = ntpcal_ntp_to_ntp(ttime, pivot);
619275970Scy	pt   = leapsec_get_table(TRUE);
620275970Scy
621275970Scy	if (   ucmpv64(&et64, &pt->head.expire) <= 0
622275970Scy	   || !leapsec_raw(pt, &tt64, total, FALSE) )
623275970Scy		return FALSE;
624275970Scy
625275970Scy	pt->lsig.etime = etime;
626275970Scy	pt->lsig.ttime = ttime;
627275970Scy	pt->lsig.taiof = (int16_t)total;
628275970Scy
629275970Scy	pt->head.expire = et64;
630275970Scy
631275970Scy	return leapsec_set_table(pt);
632275970Scy}
633285612Sdelphij#endif
634275970Scy
635275970Scy/* ------------------------------------------------------------------ */
636275970Scyint/*BOOL*/
637275970Scyleapsec_add_dyn(
638275970Scy	int            insert,
639275970Scy	uint32_t       ntpnow,
640275970Scy	const time_t * pivot )
641275970Scy{
642275970Scy	leap_table_t * pt;
643275970Scy	vint64         now64;
644275970Scy
645275970Scy	pt = leapsec_get_table(TRUE);
646275970Scy	now64 = ntpcal_ntp_to_ntp(ntpnow, pivot);
647275970Scy	return (   leapsec_add(pt, &now64, (insert != 0))
648275970Scy		&& leapsec_set_table(pt));
649275970Scy}
650275970Scy
651285612Sdelphij/* ------------------------------------------------------------------ */
652285612Sdelphijint/*BOOL*/
653285612Sdelphijleapsec_autokey_tai(
654285612Sdelphij	int            tai_offset,
655285612Sdelphij	uint32_t       ntpnow    ,
656285612Sdelphij	const time_t * pivot     )
657285612Sdelphij{
658285612Sdelphij	leap_table_t * pt;
659285612Sdelphij	leap_era_t     era;
660285612Sdelphij	vint64         now64;
661285612Sdelphij	int            idx;
662285612Sdelphij
663285612Sdelphij	(void)tai_offset;
664285612Sdelphij	pt = leapsec_get_table(FALSE);
665285612Sdelphij
666285612Sdelphij	/* Bail out if the basic offset is not zero and the putative
667285612Sdelphij	 * offset is bigger than 10s. That was in 1972 -- we don't want
668285612Sdelphij	 * to go back that far!
669285612Sdelphij	 */
670285612Sdelphij	if (pt->head.base_tai != 0 || tai_offset < 10)
671285612Sdelphij		return FALSE;
672285612Sdelphij
673285612Sdelphij	/* If there's already data in the table, check if an update is
674285612Sdelphij	 * possible. Update is impossible if there are static entries
675285612Sdelphij	 * (since this indicates a valid leapsecond file) or if we're
676285612Sdelphij	 * too close to a leapsecond transition: We do not know on what
677285612Sdelphij	 * side the transition the sender might have been, so we use a
678285612Sdelphij	 * dead zone around the transition.
679285612Sdelphij	 */
680285612Sdelphij
681285612Sdelphij	/* Check for static entries */
682285612Sdelphij	for (idx = 0; idx != pt->head.size; idx++)
683285612Sdelphij		if ( ! pt->info[idx].dynls)
684285612Sdelphij			return FALSE;
685285612Sdelphij
686285612Sdelphij	/* get the fulll time stamp and leap era for it */
687285612Sdelphij	now64 = ntpcal_ntp_to_ntp(ntpnow, pivot);
688285612Sdelphij	fetch_leap_era(&era, pt, &now64);
689285612Sdelphij
690285612Sdelphij	/* check the limits with 20s dead band */
691285612Sdelphij	era.ebase = addv64i32(&era.ebase,  20);
692285612Sdelphij	if (ucmpv64(&now64, &era.ebase) < 0)
693285612Sdelphij		return FALSE;
694285612Sdelphij
695285612Sdelphij	era.ttime = addv64i32(&era.ttime, -20);
696285612Sdelphij	if (ucmpv64(&now64, &era.ttime) > 0)
697285612Sdelphij		return FALSE;
698285612Sdelphij
699285612Sdelphij	/* Here we can proceed. Calculate the delta update. */
700285612Sdelphij	tai_offset -= era.taiof;
701285612Sdelphij
702285612Sdelphij	/* Shift the header info offsets. */
703285612Sdelphij	pt->head.base_tai += tai_offset;
704285612Sdelphij	pt->head.this_tai += tai_offset;
705285612Sdelphij	pt->head.next_tai += tai_offset;
706285612Sdelphij
707285612Sdelphij	/* Shift table entry offsets (if any) */
708285612Sdelphij	for (idx = 0; idx != pt->head.size; idx++)
709285612Sdelphij		pt->info[idx].taiof += tai_offset;
710285612Sdelphij
711285612Sdelphij	/* claim success... */
712285612Sdelphij	return TRUE;
713285612Sdelphij}
714285612Sdelphij
715285612Sdelphij
716275970Scy/* =====================================================================
717275970Scy * internal helpers
718275970Scy */
719275970Scy
720275970Scy/* [internal] Reset / init the time window in the leap processor to
721275970Scy * force reload on next query. Since a leap transition cannot take place
722275970Scy * at an odd second, the value chosen avoids spurious leap transition
723275970Scy * triggers. Making all three times equal forces a reload. Using the
724275970Scy * maximum value for unsigned 64 bits makes finding the next leap frame
725275970Scy * a bit easier.
726275970Scy */
727275970Scystatic void
728275970Scyreset_times(
729275970Scy	leap_table_t * pt)
730275970Scy{
731275970Scy	memset(&pt->head.ebase, 0xFF, sizeof(vint64));
732275970Scy	pt->head.stime = pt->head.ebase;
733275970Scy	pt->head.ttime = pt->head.ebase;
734275970Scy	pt->head.dtime = pt->head.ebase;
735275970Scy}
736275970Scy
737275970Scy/* [internal] Add raw data to the table, removing old entries on the
738275970Scy * fly. This cannot fail currently.
739275970Scy */
740275970Scystatic int/*BOOL*/
741275970Scyadd_range(
742275970Scy	leap_table_t *      pt,
743275970Scy	const leap_info_t * pi)
744275970Scy{
745275970Scy	/* If the table is full, make room by throwing out the oldest
746330141Sdelphij	 * entry. But remember the accumulated leap seconds!
747330141Sdelphij	 *
748330141Sdelphij	 * Setting the first entry is a bit tricky, too: Simply assuming
749330141Sdelphij	 * it is an insertion is wrong if the first entry is a dynamic
750330141Sdelphij	 * leap second removal. So we decide on the sign -- if the first
751330141Sdelphij	 * entry has a negative offset, we assume that it is a leap
752330141Sdelphij	 * second removal. In both cases the table base offset is set
753330141Sdelphij	 * accordingly to reflect the decision.
754330141Sdelphij	 *
755330141Sdelphij	 * In practice starting with a removal can only happen if the
756330141Sdelphij	 * first entry is a dynamic request without having a leap file
757330141Sdelphij	 * for the history proper.
758275970Scy	 */
759285612Sdelphij	if (pt->head.size == 0) {
760330141Sdelphij		if (pi->taiof >= 0)
761330141Sdelphij			pt->head.base_tai = pi->taiof - 1;
762330141Sdelphij		else
763330141Sdelphij			pt->head.base_tai = pi->taiof + 1;
764285612Sdelphij	} else if (pt->head.size >= MAX_HIST) {
765275970Scy		pt->head.size     = MAX_HIST - 1;
766275970Scy		pt->head.base_tai = pt->info[pt->head.size].taiof;
767275970Scy	}
768275970Scy
769275970Scy	/* make room in lower end and insert item */
770285612Sdelphij	memmove(pt->info+1, pt->info, pt->head.size*sizeof(*pt->info));
771275970Scy	pt->info[0] = *pi;
772275970Scy	pt->head.size++;
773275970Scy
774275970Scy	/* invalidate the cached limit data -- we might have news ;-)
775275970Scy	 *
776275970Scy	 * This blocks a spurious transition detection. OTOH, if you add
777275970Scy	 * a value after the last query before a leap transition was
778275970Scy	 * expected to occur, this transition trigger is lost. But we
779275970Scy	 * can probably live with that.
780275970Scy	 */
781275970Scy	reset_times(pt);
782275970Scy	return TRUE;
783275970Scy}
784275970Scy
785275970Scy/* [internal] given a reader function, read characters into a buffer
786275970Scy * until either EOL or EOF is reached. Makes sure that the buffer is
787275970Scy * always NUL terminated, but silently truncates excessive data. The
788275970Scy * EOL-marker ('\n') is *not* stored in the buffer.
789275970Scy *
790275970Scy * Returns the pointer to the buffer, unless EOF was reached when trying
791275970Scy * to read the first character of a line.
792275970Scy */
793275970Scystatic char *
794275970Scyget_line(
795275970Scy	leapsec_reader func,
796275970Scy	void *         farg,
797275970Scy	char *         buff,
798275970Scy	size_t         size)
799275970Scy{
800275970Scy	int   ch;
801275970Scy	char *ptr;
802285612Sdelphij
803275970Scy	/* if we cannot even store the delimiter, declare failure */
804275970Scy	if (buff == NULL || size == 0)
805275970Scy		return NULL;
806275970Scy
807275970Scy	ptr = buff;
808275970Scy	while (EOF != (ch = (*func)(farg)) && '\n' != ch)
809275970Scy		if (size > 1) {
810275970Scy			size--;
811275970Scy			*ptr++ = (char)ch;
812275970Scy		}
813275970Scy	/* discard trailing whitespace */
814275970Scy	while (ptr != buff && isspace((u_char)ptr[-1]))
815275970Scy		ptr--;
816275970Scy	*ptr = '\0';
817275970Scy	return (ptr == buff && ch == EOF) ? NULL : buff;
818275970Scy}
819275970Scy
820275970Scy/* [internal] skips whitespace characters from a character buffer. */
821275970Scystatic char *
822275970Scyskipws(
823275970Scy	const char *ptr)
824275970Scy{
825275970Scy	while (isspace((u_char)*ptr))
826275970Scy		ptr++;
827275970Scy	return (char*)noconst(ptr);
828275970Scy}
829275970Scy
830285612Sdelphij/* [internal] check if a strtoXYZ ended at EOL or whitespace and
831275970Scy * converted something at all. Return TRUE if something went wrong.
832275970Scy */
833275970Scystatic int/*BOOL*/
834275970Scyparsefail(
835275970Scy	const char * cp,
836275970Scy	const char * ep)
837275970Scy{
838275970Scy	return (cp == ep)
839275970Scy	    || (*ep && *ep != '#' && !isspace((u_char)*ep));
840275970Scy}
841275970Scy
842275970Scy/* [internal] reload the table limits around the given time stamp. This
843275970Scy * is where the real work is done when it comes to table lookup and
844275970Scy * evaluation. Some care has been taken to have correct code for dealing
845275970Scy * with boundary conditions and empty tables.
846275970Scy *
847275970Scy * In electric mode, transition and trip time are the same. In dumb
848275970Scy * mode, the difference of the TAI offsets must be taken into account
849275970Scy * and trip time and transition time become different. The difference
850275970Scy * becomes the warping distance when the trip time is reached.
851275970Scy */
852275970Scystatic void
853275970Scyreload_limits(
854275970Scy	leap_table_t * pt,
855275970Scy	const vint64 * ts)
856275970Scy{
857275970Scy	int idx;
858275970Scy
859275970Scy	/* Get full time and search the true lower bound. Use a
860275970Scy	 * simple loop here, since the number of entries does
861275970Scy	 * not warrant a binary search. This also works for an empty
862275970Scy	 * table, so there is no shortcut for that case.
863275970Scy	 */
864275970Scy	for (idx = 0; idx != pt->head.size; idx++)
865275970Scy		if (ucmpv64(ts, &pt->info[idx].ttime) >= 0)
866275970Scy			break;
867275970Scy
868275970Scy	/* get time limits with proper bound conditions. Note that the
869275970Scy	 * bounds of the table will be observed even if the table is
870275970Scy	 * empty -- no undefined condition must arise from this code.
871275970Scy	 */
872275970Scy	if (idx >= pt->head.size) {
873275970Scy		memset(&pt->head.ebase, 0x00, sizeof(vint64));
874275970Scy		pt->head.this_tai = pt->head.base_tai;
875275970Scy	} else {
876275970Scy		pt->head.ebase    = pt->info[idx].ttime;
877275970Scy		pt->head.this_tai = pt->info[idx].taiof;
878275970Scy	}
879275970Scy	if (--idx >= 0) {
880275970Scy		pt->head.next_tai = pt->info[idx].taiof;
881275970Scy		pt->head.dynls    = pt->info[idx].dynls;
882275970Scy		pt->head.ttime    = pt->info[idx].ttime;
883275970Scy
884275970Scy		if (_electric)
885275970Scy			pt->head.dtime = pt->head.ttime;
886275970Scy                else
887275970Scy			pt->head.dtime = addv64i32(
888275970Scy				&pt->head.ttime,
889275970Scy				pt->head.next_tai - pt->head.this_tai);
890285612Sdelphij
891275970Scy		pt->head.stime = subv64u32(
892275970Scy			&pt->head.ttime, pt->info[idx].stime);
893275970Scy
894275970Scy	} else {
895275970Scy		memset(&pt->head.ttime, 0xFF, sizeof(vint64));
896275970Scy		pt->head.stime    = pt->head.ttime;
897275970Scy		pt->head.dtime    = pt->head.ttime;
898275970Scy		pt->head.next_tai = pt->head.this_tai;
899275970Scy		pt->head.dynls    = 0;
900275970Scy	}
901275970Scy}
902275970Scy
903285612Sdelphij/* [internal] fetch the leap era for a given time stamp.
904285612Sdelphij * This is a cut-down version the algorithm used to reload the table
905285612Sdelphij * limits, but it does not update any global state and provides just the
906285612Sdelphij * era information for a given time stamp.
907285612Sdelphij */
908285612Sdelphijstatic void
909285612Sdelphijfetch_leap_era(
910285612Sdelphij	leap_era_t         * into,
911285612Sdelphij	const leap_table_t * pt  ,
912285612Sdelphij	const vint64       * ts  )
913285612Sdelphij{
914285612Sdelphij	int idx;
915285612Sdelphij
916285612Sdelphij	/* Simple search loop, also works with empty table. */
917285612Sdelphij	for (idx = 0; idx != pt->head.size; idx++)
918285612Sdelphij		if (ucmpv64(ts, &pt->info[idx].ttime) >= 0)
919285612Sdelphij			break;
920285612Sdelphij	/* fetch era data, keeping an eye on boundary conditions */
921285612Sdelphij	if (idx >= pt->head.size) {
922285612Sdelphij		memset(&into->ebase, 0x00, sizeof(vint64));
923285612Sdelphij		into->taiof = pt->head.base_tai;
924285612Sdelphij	} else {
925285612Sdelphij		into->ebase = pt->info[idx].ttime;
926285612Sdelphij		into->taiof = pt->info[idx].taiof;
927285612Sdelphij	}
928285612Sdelphij	if (--idx >= 0)
929285612Sdelphij		into->ttime = pt->info[idx].ttime;
930285612Sdelphij	else
931285612Sdelphij		memset(&into->ttime, 0xFF, sizeof(vint64));
932285612Sdelphij}
933285612Sdelphij
934275970Scy/* [internal] Take a time stamp and create a leap second frame for
935275970Scy * it. This will schedule a leap second for the beginning of the next
936275970Scy * month, midnight UTC. The 'insert' argument tells if a leap second is
937275970Scy * added (!=0) or removed (==0). We do not handle multiple inserts
938275970Scy * (yet?)
939275970Scy *
940275970Scy * Returns 1 if the insert worked, 0 otherwise. (It's not possible to
941275970Scy * insert a leap second into the current history -- only appending
942275970Scy * towards the future is allowed!)
943275970Scy */
944275970Scystatic int/*BOOL*/
945275970Scyleapsec_add(
946275970Scy	leap_table_t*  pt    ,
947275970Scy	const vint64 * now64 ,
948275970Scy	int            insert)
949275970Scy{
950285612Sdelphij	vint64		ttime, starttime;
951275970Scy	struct calendar	fts;
952275970Scy	leap_info_t	li;
953275970Scy
954285612Sdelphij	/* Check against the table expiration and the latest available
955275970Scy	 * leap entry. Do not permit inserts, only appends, and only if
956275970Scy	 * the extend the table beyond the expiration!
957275970Scy	 */
958275970Scy	if (   ucmpv64(now64, &pt->head.expire) < 0
959275970Scy	    || (pt->head.size && ucmpv64(now64, &pt->info[0].ttime) <= 0)) {
960275970Scy		errno = ERANGE;
961275970Scy		return FALSE;
962275970Scy	}
963275970Scy
964275970Scy	ntpcal_ntp64_to_date(&fts, now64);
965275970Scy	/* To guard against dangling leap flags: do not accept leap
966275970Scy	 * second request on the 1st hour of the 1st day of the month.
967275970Scy	 */
968275970Scy	if (fts.monthday == 1 && fts.hour == 0) {
969275970Scy		errno = EINVAL;
970275970Scy		return FALSE;
971275970Scy	}
972275970Scy
973275970Scy	/* Ok, do the remaining calculations */
974275970Scy	fts.monthday = 1;
975275970Scy	fts.hour     = 0;
976275970Scy	fts.minute   = 0;
977275970Scy	fts.second   = 0;
978285612Sdelphij	starttime = ntpcal_date_to_ntp64(&fts);
979275970Scy	fts.month++;
980275970Scy	ttime = ntpcal_date_to_ntp64(&fts);
981275970Scy
982275970Scy	li.ttime = ttime;
983285612Sdelphij	li.stime = ttime.D_s.lo - starttime.D_s.lo;
984275970Scy	li.taiof = (pt->head.size ? pt->info[0].taiof : pt->head.base_tai)
985275970Scy	         + (insert ? 1 : -1);
986275970Scy	li.dynls = 1;
987275970Scy	return add_range(pt, &li);
988275970Scy}
989275970Scy
990275970Scy/* [internal] Given a time stamp for a leap insertion (the exact begin
991275970Scy * of the new leap era), create new leap frame and put it into the
992275970Scy * table. This is the work horse for reading a leap file and getting a
993275970Scy * leap second update via authenticated network packet.
994275970Scy */
995275970Scyint/*BOOL*/
996275970Scyleapsec_raw(
997275970Scy	leap_table_t * pt,
998275970Scy	const vint64 * ttime,
999275970Scy	int            taiof,
1000275970Scy	int            dynls)
1001275970Scy{
1002285612Sdelphij	vint64		starttime;
1003275970Scy	struct calendar	fts;
1004275970Scy	leap_info_t	li;
1005275970Scy
1006285612Sdelphij	/* Check that we either extend the table or get a duplicate of
1007285612Sdelphij	 * the latest entry. The latter is a benevolent overwrite with
1008285612Sdelphij	 * identical data and could happen if we get an autokey message
1009285612Sdelphij	 * that extends the lifetime of the current leapsecond table.
1010285612Sdelphij	 * Otherwise paranoia rulez!
1011285612Sdelphij	 */
1012285612Sdelphij	if (pt->head.size) {
1013285612Sdelphij		int cmp = ucmpv64(ttime, &pt->info[0].ttime);
1014285612Sdelphij		if (cmp == 0)
1015285612Sdelphij			cmp -= (taiof != pt->info[0].taiof);
1016285612Sdelphij		if (cmp < 0) {
1017285612Sdelphij			errno = ERANGE;
1018285612Sdelphij			return FALSE;
1019285612Sdelphij		}
1020285612Sdelphij		if (cmp == 0)
1021285612Sdelphij			return TRUE;
1022275970Scy	}
1023275970Scy
1024275970Scy	ntpcal_ntp64_to_date(&fts, ttime);
1025275970Scy	/* If this does not match the exact month start, bail out. */
1026275970Scy	if (fts.monthday != 1 || fts.hour || fts.minute || fts.second) {
1027275970Scy		errno = EINVAL;
1028275970Scy		return FALSE;
1029275970Scy	}
1030275970Scy	fts.month--; /* was in range 1..12, no overflow here! */
1031285612Sdelphij	starttime = ntpcal_date_to_ntp64(&fts);
1032275970Scy	li.ttime = *ttime;
1033285612Sdelphij	li.stime = ttime->D_s.lo - starttime.D_s.lo;
1034275970Scy	li.taiof = (int16_t)taiof;
1035275970Scy	li.dynls = (dynls != 0);
1036275970Scy	return add_range(pt, &li);
1037275970Scy}
1038275970Scy
1039275970Scy/* [internal] Do a wrap-around save range inclusion check.
1040275970Scy * Returns TRUE if x in [lo,hi[ (intervall open on right side) with full
1041275970Scy * handling of an overflow / wrap-around.
1042275970Scy */
1043275970Scystatic int/*BOOL*/
1044275970Scybetweenu32(
1045275970Scy	uint32_t lo,
1046275970Scy	uint32_t x,
1047275970Scy	uint32_t hi)
1048275970Scy{
1049275970Scy	int rc;
1050275970Scy
1051275970Scy	if (lo <= hi)
1052275970Scy		rc = (lo <= x) && (x < hi);
1053275970Scy	else
1054275970Scy		rc = (lo <= x) || (x < hi);
1055275970Scy	return rc;
1056275970Scy}
1057275970Scy
1058275970Scy/* =====================================================================
1059275970Scy * validation stuff
1060275970Scy */
1061275970Scy
1062275970Scytypedef struct {
1063275970Scy	unsigned char hv[ISC_SHA1_DIGESTLENGTH];
1064275970Scy} sha1_digest;
1065275970Scy
1066275970Scy/* [internal] parse a digest line to get the hash signature
1067275970Scy * The NIST code creating the hash writes them out as 5 hex integers
1068275970Scy * without leading zeros. This makes reading them back as hex-encoded
1069275970Scy * BLOB impossible, because there might be less than 40 hex digits.
1070275970Scy *
1071275970Scy * The solution is to read the values back as integers, and then do the
1072275970Scy * byte twiddle necessary to get it into an array of 20 chars. The
1073275970Scy * drawback is that it permits any acceptable number syntax provided by
1074275970Scy * 'scanf()' and 'strtoul()', including optional signs and '0x'
1075275970Scy * prefixes.
1076275970Scy */
1077275970Scystatic int/*BOOL*/
1078275970Scydo_leap_hash(
1079275970Scy	sha1_digest * mac,
1080275970Scy	char const  * cp )
1081275970Scy{
1082275970Scy	int wi, di, num, len;
1083275970Scy	unsigned long tmp[5];
1084275970Scy
1085275970Scy	memset(mac, 0, sizeof(*mac));
1086275970Scy	num = sscanf(cp, " %lx %lx %lx %lx %lx%n",
1087275970Scy		     &tmp[0], &tmp[1], &tmp[2], &tmp[3], &tmp[4],
1088275970Scy		     &len);
1089275970Scy	if (num != 5 || cp[len] > ' ')
1090275970Scy		return FALSE;
1091275970Scy
1092275970Scy	/* now do the byte twiddle */
1093275970Scy	for (wi=0; wi < 5; ++wi)
1094275970Scy		for (di=3; di >= 0; --di) {
1095280849Scy			mac->hv[wi*4 + di] =
1096280849Scy				(unsigned char)(tmp[wi] & 0x0FF);
1097275970Scy			tmp[wi] >>= 8;
1098275970Scy		}
1099275970Scy	return TRUE;
1100275970Scy}
1101275970Scy
1102275970Scy/* [internal] add the digits of a data line to the hash, stopping at the
1103275970Scy * next hash ('#') character.
1104275970Scy */
1105275970Scystatic void
1106275970Scydo_hash_data(
1107275970Scy	isc_sha1_t * mdctx,
1108275970Scy	char const * cp   )
1109275970Scy{
1110275970Scy	unsigned char  text[32]; // must be power of two!
1111275970Scy	unsigned int   tlen =  0;
1112275970Scy	unsigned char  ch;
1113275970Scy
1114275970Scy	while ('\0' != (ch = *cp++) && '#' != ch)
1115275970Scy		if (isdigit(ch)) {
1116275970Scy			text[tlen++] = ch;
1117275970Scy			tlen &= (sizeof(text)-1);
1118275970Scy			if (0 == tlen)
1119275970Scy				isc_sha1_update(
1120275970Scy					mdctx, text, sizeof(text));
1121275970Scy		}
1122285612Sdelphij
1123275970Scy	if (0 < tlen)
1124275970Scy		isc_sha1_update(mdctx, text, tlen);
1125275970Scy}
1126275970Scy
1127275970Scy/* given a reader and a reader arg, calculate and validate the the hash
1128275970Scy * signature of a NIST leap second file.
1129275970Scy */
1130275970Scyint
1131275970Scyleapsec_validate(
1132275970Scy	leapsec_reader func,
1133275970Scy	void *         farg)
1134275970Scy{
1135275970Scy	isc_sha1_t     mdctx;
1136275970Scy	sha1_digest    rdig, ldig; /* remote / local digests */
1137275970Scy	char           line[50];
1138275970Scy	int            hlseen = -1;
1139275970Scy
1140275970Scy	isc_sha1_init(&mdctx);
1141275970Scy	while (get_line(func, farg, line, sizeof(line))) {
1142275970Scy		if (!strncmp(line, "#h", 2))
1143275970Scy			hlseen = do_leap_hash(&rdig, line+2);
1144275970Scy		else if (!strncmp(line, "#@", 2))
1145275970Scy			do_hash_data(&mdctx, line+2);
1146275970Scy		else if (!strncmp(line, "#$", 2))
1147275970Scy			do_hash_data(&mdctx, line+2);
1148280849Scy		else if (isdigit((unsigned char)line[0]))
1149275970Scy			do_hash_data(&mdctx, line);
1150275970Scy	}
1151275970Scy	isc_sha1_final(&mdctx, ldig.hv);
1152275970Scy	isc_sha1_invalidate(&mdctx);
1153275970Scy
1154275970Scy	if (0 > hlseen)
1155275970Scy		return LSVALID_NOHASH;
1156275970Scy	if (0 == hlseen)
1157275970Scy		return LSVALID_BADFORMAT;
1158275970Scy	if (0 != memcmp(&rdig, &ldig, sizeof(sha1_digest)))
1159275970Scy		return LSVALID_BADHASH;
1160275970Scy	return LSVALID_GOODHASH;
1161275970Scy}
1162275970Scy
1163275970Scy/*
1164275970Scy * lstostr - prettyprint NTP seconds
1165275970Scy */
1166285612Sdelphijstatic const char *
1167285612Sdelphijlstostr(
1168275970Scy	const vint64 * ts)
1169275970Scy{
1170275970Scy	char *		buf;
1171275970Scy	struct calendar tm;
1172275970Scy
1173275970Scy	LIB_GETBUF(buf);
1174285612Sdelphij
1175285612Sdelphij	if ( ! (ts->d_s.hi >= 0 && ntpcal_ntp64_to_date(&tm, ts) >= 0))
1176285612Sdelphij		snprintf(buf, LIB_BUFLENGTH, "%s", "9999-12-31T23:59:59Z");
1177285612Sdelphij	else
1178285612Sdelphij		snprintf(buf, LIB_BUFLENGTH, "%04d-%02d-%02dT%02d:%02d:%02dZ",
1179285612Sdelphij			tm.year, tm.month, tm.monthday,
1180285612Sdelphij			tm.hour, tm.minute, tm.second);
1181285612Sdelphij
1182275970Scy	return buf;
1183275970Scy}
1184275970Scy
1185285612Sdelphij/* reset the global state for unit tests */
1186285612Sdelphijvoid
1187285612Sdelphijleapsec_ut_pristine(void)
1188285612Sdelphij{
1189285612Sdelphij	memset(_ltab, 0, sizeof(_ltab));
1190285612Sdelphij	_lptr     = NULL;
1191285612Sdelphij	_electric = 0;
1192285612Sdelphij}
1193275970Scy
1194275970Scy
1195285612Sdelphij
1196275970Scy/* -*- that's all folks! -*- */
1197