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