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