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