1*eabc0478Schristos /* $NetBSD: ntp_leapsec.c,v 1.8 2024/08/18 20:47:17 christos Exp $ */ 28585484eSchristos 38585484eSchristos /* 48585484eSchristos * ntp_leapsec.c - leap second processing for NTPD 58585484eSchristos * 68585484eSchristos * Written by Juergen Perlinger (perlinger@ntp.org) for the NTP project. 78585484eSchristos * The contents of 'html/copyright.html' apply. 88585484eSchristos * ---------------------------------------------------------------------- 98585484eSchristos * This is an attempt to get the leap second handling into a dedicated 108585484eSchristos * module to make the somewhat convoluted logic testable. 118585484eSchristos */ 128585484eSchristos 138585484eSchristos #include <config.h> 148585484eSchristos #include <sys/types.h> 15b8ecfcfeSchristos #include <sys/stat.h> 168585484eSchristos #include <ctype.h> 178585484eSchristos 18*eabc0478Schristos #include "ntp.h" 198585484eSchristos #include "ntp_stdlib.h" 208585484eSchristos #include "ntp_calendar.h" 218585484eSchristos #include "ntp_leapsec.h" 22b8ecfcfeSchristos #include "vint64ops.h" 23b8ecfcfeSchristos 24b8ecfcfeSchristos #include "isc/sha1.h" 25b8ecfcfeSchristos 26b8ecfcfeSchristos static const char * const logPrefix = "leapsecond file"; 278585484eSchristos 288585484eSchristos /* --------------------------------------------------------------------- 298585484eSchristos * Our internal data structure 308585484eSchristos */ 318585484eSchristos #define MAX_HIST 10 /* history of leap seconds */ 328585484eSchristos 338585484eSchristos struct leap_info { 348585484eSchristos vint64 ttime; /* transition time (after the step, ntp scale) */ 358585484eSchristos uint32_t stime; /* schedule limit (a month before transition) */ 368585484eSchristos int16_t taiof; /* TAI offset on and after the transition */ 378585484eSchristos uint8_t dynls; /* dynamic: inserted on peer/clock request */ 388585484eSchristos }; 398585484eSchristos typedef struct leap_info leap_info_t; 408585484eSchristos 418585484eSchristos struct leap_head { 428585484eSchristos vint64 update; /* time of information update */ 438585484eSchristos vint64 expire; /* table expiration time */ 448585484eSchristos uint16_t size; /* number of infos in table */ 458585484eSchristos int16_t base_tai; /* total leaps before first entry */ 468585484eSchristos int16_t this_tai; /* current TAI offset */ 478585484eSchristos int16_t next_tai; /* TAI offset after 'when' */ 488585484eSchristos vint64 dtime; /* due time (current era end) */ 498585484eSchristos vint64 ttime; /* nominal transition time (next era start) */ 508585484eSchristos vint64 stime; /* schedule time (when we take notice) */ 518585484eSchristos vint64 ebase; /* base time of this leap era */ 528585484eSchristos uint8_t dynls; /* next leap is dynamic (by peer request) */ 538585484eSchristos }; 548585484eSchristos typedef struct leap_head leap_head_t; 558585484eSchristos 568585484eSchristos struct leap_table { 578585484eSchristos leap_signature_t lsig; 588585484eSchristos leap_head_t head; 598585484eSchristos leap_info_t info[MAX_HIST]; 608585484eSchristos }; 618585484eSchristos 628585484eSchristos /* Where we store our tables */ 638585484eSchristos static leap_table_t _ltab[2], *_lptr; 648585484eSchristos static int/*BOOL*/ _electric; 658585484eSchristos 668585484eSchristos /* Forward decls of local helpers */ 678585484eSchristos static int add_range (leap_table_t *, const leap_info_t *); 68*eabc0478Schristos static char * get_line (leapsec_reader, void *, char *, 69*eabc0478Schristos size_t); 70*eabc0478Schristos static inline char * skipws (char *ptr); 718585484eSchristos static int parsefail (const char *cp, const char *ep); 728585484eSchristos static void reload_limits (leap_table_t *, const vint64 *); 735d681e99Schristos static void fetch_leap_era (leap_era_t *, const leap_table_t *, 745d681e99Schristos const vint64 *); 75*eabc0478Schristos static int betweenu32 (u_int32, u_int32, u_int32); 768585484eSchristos static void reset_times (leap_table_t *); 778585484eSchristos static int leapsec_add (leap_table_t *, const vint64 *, int); 78*eabc0478Schristos static int leapsec_raw (leap_table_t *, const vint64 *, int, 79*eabc0478Schristos int); 805d681e99Schristos static const char * lstostr (const vint64 *ts); 818585484eSchristos 828585484eSchristos /* ===================================================================== 838585484eSchristos * Get & Set the current leap table 848585484eSchristos */ 858585484eSchristos 868585484eSchristos /* ------------------------------------------------------------------ */ 878585484eSchristos leap_table_t * 888585484eSchristos leapsec_get_table( 898585484eSchristos int alternate) 908585484eSchristos { 918585484eSchristos leap_table_t *p1, *p2; 928585484eSchristos 938585484eSchristos p1 = _lptr; 945d681e99Schristos if (p1 == &_ltab[0]) { 955d681e99Schristos p2 = &_ltab[1]; 965d681e99Schristos } else if (p1 == &_ltab[1]) { 975d681e99Schristos p2 = &_ltab[0]; 985d681e99Schristos } else { 995d681e99Schristos p1 = &_ltab[0]; 1005d681e99Schristos p2 = &_ltab[1]; 1015d681e99Schristos reset_times(p1); 1025d681e99Schristos reset_times(p2); 1035d681e99Schristos _lptr = p1; 1045d681e99Schristos } 1058585484eSchristos if (alternate) { 1068585484eSchristos memcpy(p2, p1, sizeof(leap_table_t)); 1078585484eSchristos p1 = p2; 1088585484eSchristos } 1098585484eSchristos 1108585484eSchristos return p1; 1118585484eSchristos } 1128585484eSchristos 1138585484eSchristos /* ------------------------------------------------------------------ */ 1148585484eSchristos int/*BOOL*/ 1158585484eSchristos leapsec_set_table( 1168585484eSchristos leap_table_t * pt) 1178585484eSchristos { 1188585484eSchristos if (pt == &_ltab[0] || pt == &_ltab[1]) 1198585484eSchristos _lptr = pt; 1208585484eSchristos return _lptr == pt; 1218585484eSchristos } 1228585484eSchristos 1238585484eSchristos /* ------------------------------------------------------------------ */ 1248585484eSchristos int/*BOOL*/ 1258585484eSchristos leapsec_electric( 1268585484eSchristos int/*BOOL*/ on) 1278585484eSchristos { 1288585484eSchristos int res = _electric; 1298585484eSchristos if (on < 0) 1308585484eSchristos return res; 1318585484eSchristos 1328585484eSchristos _electric = (on != 0); 1338585484eSchristos if (_electric == res) 1348585484eSchristos return res; 1358585484eSchristos 1368585484eSchristos if (_lptr == &_ltab[0] || _lptr == &_ltab[1]) 1378585484eSchristos reset_times(_lptr); 1388585484eSchristos 1398585484eSchristos return res; 1408585484eSchristos } 1418585484eSchristos 1428585484eSchristos /* ===================================================================== 1438585484eSchristos * API functions that operate on tables 1448585484eSchristos */ 1458585484eSchristos 1468585484eSchristos /* --------------------------------------------------------------------- 1478585484eSchristos * Clear all leap second data. Use it for init & cleanup 1488585484eSchristos */ 1498585484eSchristos void 1508585484eSchristos leapsec_clear( 1518585484eSchristos leap_table_t * pt) 1528585484eSchristos { 1538585484eSchristos memset(&pt->lsig, 0, sizeof(pt->lsig)); 1548585484eSchristos memset(&pt->head, 0, sizeof(pt->head)); 1558585484eSchristos reset_times(pt); 1568585484eSchristos } 1578585484eSchristos 1588585484eSchristos /* --------------------------------------------------------------------- 1598585484eSchristos * Load a leap second file and check expiration on the go 1608585484eSchristos */ 1618585484eSchristos int/*BOOL*/ 1628585484eSchristos leapsec_load( 1638585484eSchristos leap_table_t * pt, 1648585484eSchristos leapsec_reader func, 1658585484eSchristos void * farg, 166*eabc0478Schristos int use_build_limit 167*eabc0478Schristos ) 1688585484eSchristos { 169*eabc0478Schristos char *cp, *ep, *endp, linebuf[50]; 1708585484eSchristos vint64 ttime, limit; 1718585484eSchristos long taiof; 1728585484eSchristos struct calendar build; 1738585484eSchristos 1748585484eSchristos leapsec_clear(pt); 1755d681e99Schristos if (use_build_limit && ntpcal_get_build_date(&build)) { 1765d681e99Schristos /* don't prune everything -- permit the last 10yrs 1775d681e99Schristos * before build. 1785d681e99Schristos */ 1795d681e99Schristos build.year -= 10; 1808585484eSchristos limit = ntpcal_date_to_ntp64(&build); 1815d681e99Schristos } else { 1828585484eSchristos memset(&limit, 0, sizeof(limit)); 1835d681e99Schristos } 1848585484eSchristos 1858585484eSchristos while (get_line(func, farg, linebuf, sizeof(linebuf))) { 1868585484eSchristos cp = linebuf; 1878585484eSchristos if (*cp == '#') { 1888585484eSchristos cp++; 1898585484eSchristos if (*cp == '@') { 1908585484eSchristos cp = skipws(cp+1); 1918585484eSchristos pt->head.expire = strtouv64(cp, &ep, 10); 1928585484eSchristos if (parsefail(cp, ep)) 1938585484eSchristos goto fail_read; 1948585484eSchristos pt->lsig.etime = pt->head.expire.D_s.lo; 1958585484eSchristos } else if (*cp == '$') { 1968585484eSchristos cp = skipws(cp+1); 1978585484eSchristos pt->head.update = strtouv64(cp, &ep, 10); 1988585484eSchristos if (parsefail(cp, ep)) 1998585484eSchristos goto fail_read; 2008585484eSchristos } 2018585484eSchristos } else if (isdigit((u_char)*cp)) { 2028585484eSchristos ttime = strtouv64(cp, &ep, 10); 2038585484eSchristos if (parsefail(cp, ep)) 2048585484eSchristos goto fail_read; 2058585484eSchristos cp = skipws(ep); 206*eabc0478Schristos taiof = strtol(cp, &endp, 10); 207*eabc0478Schristos if ( parsefail(cp, endp) 208*eabc0478Schristos || taiof > INT16_MAX || taiof < INT16_MIN) 2098585484eSchristos goto fail_read; 2108585484eSchristos if (ucmpv64(&ttime, &limit) >= 0) { 2118585484eSchristos if (!leapsec_raw(pt, &ttime, 2128585484eSchristos taiof, FALSE)) 2138585484eSchristos goto fail_insn; 2148585484eSchristos } else { 2158585484eSchristos pt->head.base_tai = (int16_t)taiof; 2168585484eSchristos } 2178585484eSchristos pt->lsig.ttime = ttime.D_s.lo; 2188585484eSchristos pt->lsig.taiof = (int16_t)taiof; 2198585484eSchristos } 2208585484eSchristos } 2218585484eSchristos return TRUE; 2228585484eSchristos 2238585484eSchristos fail_read: 2248585484eSchristos errno = EILSEQ; 2258585484eSchristos fail_insn: 2268585484eSchristos leapsec_clear(pt); 2278585484eSchristos return FALSE; 2288585484eSchristos } 2298585484eSchristos 2308585484eSchristos /* --------------------------------------------------------------------- 2318585484eSchristos * Dump a table in human-readable format. Use 'fprintf' and a FILE 2328585484eSchristos * pointer if you want to get it printed into a stream. 2338585484eSchristos */ 2348585484eSchristos void 2358585484eSchristos leapsec_dump( 2368585484eSchristos const leap_table_t * pt , 2378585484eSchristos leapsec_dumper func, 2388585484eSchristos void * farg) 2398585484eSchristos { 2408585484eSchristos int idx; 2418585484eSchristos vint64 ts; 2428585484eSchristos struct calendar atb, ttb; 2438585484eSchristos 2448585484eSchristos ntpcal_ntp64_to_date(&ttb, &pt->head.expire); 2458585484eSchristos (*func)(farg, "leap table (%u entries) expires at %04u-%02u-%02u:\n", 2468585484eSchristos pt->head.size, 2478585484eSchristos ttb.year, ttb.month, ttb.monthday); 2488585484eSchristos idx = pt->head.size; 2498585484eSchristos while (idx-- != 0) { 2508585484eSchristos ts = pt->info[idx].ttime; 2518585484eSchristos ntpcal_ntp64_to_date(&ttb, &ts); 2528585484eSchristos ts = subv64u32(&ts, pt->info[idx].stime); 2538585484eSchristos ntpcal_ntp64_to_date(&atb, &ts); 2548585484eSchristos 2558585484eSchristos (*func)(farg, "%04u-%02u-%02u [%c] (%04u-%02u-%02u) - %d\n", 2568585484eSchristos ttb.year, ttb.month, ttb.monthday, 2578585484eSchristos "-*"[pt->info[idx].dynls != 0], 2588585484eSchristos atb.year, atb.month, atb.monthday, 2598585484eSchristos pt->info[idx].taiof); 2608585484eSchristos } 2618585484eSchristos } 2628585484eSchristos 2638585484eSchristos /* ===================================================================== 2648585484eSchristos * usecase driven API functions 2658585484eSchristos */ 2668585484eSchristos 2678585484eSchristos int/*BOOL*/ 2688585484eSchristos leapsec_query( 2698585484eSchristos leap_result_t * qr , 2708585484eSchristos uint32_t ts32 , 2718585484eSchristos const time_t * pivot) 2728585484eSchristos { 2738585484eSchristos leap_table_t * pt; 2748585484eSchristos vint64 ts64, last, next; 2758585484eSchristos uint32_t due32; 2768585484eSchristos int fired; 2778585484eSchristos 2788585484eSchristos /* preset things we use later on... */ 2798585484eSchristos fired = FALSE; 2808585484eSchristos ts64 = ntpcal_ntp_to_ntp(ts32, pivot); 2818585484eSchristos pt = leapsec_get_table(FALSE); 2828585484eSchristos memset(qr, 0, sizeof(leap_result_t)); 2838585484eSchristos 2848585484eSchristos if (ucmpv64(&ts64, &pt->head.ebase) < 0) { 2858585484eSchristos /* Most likely after leap frame reset. Could also be a 2868585484eSchristos * backstep of the system clock. Anyway, get the new 2878585484eSchristos * leap era frame. 2888585484eSchristos */ 2898585484eSchristos reload_limits(pt, &ts64); 2908585484eSchristos } else if (ucmpv64(&ts64, &pt->head.dtime) >= 0) { 2918585484eSchristos /* Boundary crossed in forward direction. This might 2928585484eSchristos * indicate a leap transition, so we prepare for that 2938585484eSchristos * case. 2948585484eSchristos * 2958585484eSchristos * Some operations below are actually NOPs in electric 2968585484eSchristos * mode, but having only one code path that works for 2978585484eSchristos * both modes is easier to maintain. 2985d681e99Schristos * 2995d681e99Schristos * There's another quirk we must keep looking out for: 3005d681e99Schristos * If we just stepped the clock, the step might have 3015d681e99Schristos * crossed a leap boundary. As with backward steps, we 3025d681e99Schristos * do not want to raise the 'fired' event in that case. 3035d681e99Schristos * So we raise the 'fired' event only if we're close to 3045d681e99Schristos * the transition and just reload the limits otherwise. 3058585484eSchristos */ 3065d681e99Schristos last = addv64i32(&pt->head.dtime, 3); /* get boundary */ 3075d681e99Schristos if (ucmpv64(&ts64, &last) >= 0) { 3085d681e99Schristos /* that was likely a query after a step */ 3095d681e99Schristos reload_limits(pt, &ts64); 3105d681e99Schristos } else { 3115d681e99Schristos /* close enough for deeper examination */ 3128585484eSchristos last = pt->head.ttime; 3138585484eSchristos qr->warped = (int16_t)(last.D_s.lo - 3148585484eSchristos pt->head.dtime.D_s.lo); 3158585484eSchristos next = addv64i32(&ts64, qr->warped); 3168585484eSchristos reload_limits(pt, &next); 3178585484eSchristos fired = ucmpv64(&pt->head.ebase, &last) == 0; 3188585484eSchristos if (fired) { 3198585484eSchristos ts64 = next; 3208585484eSchristos ts32 = next.D_s.lo; 3218585484eSchristos } else { 3228585484eSchristos qr->warped = 0; 3238585484eSchristos } 3248585484eSchristos } 3255d681e99Schristos } 3268585484eSchristos 3278585484eSchristos qr->tai_offs = pt->head.this_tai; 3285d681e99Schristos qr->ebase = pt->head.ebase; 3295d681e99Schristos qr->ttime = pt->head.ttime; 3308585484eSchristos 3318585484eSchristos /* If before the next scheduling alert, we're done. */ 3328585484eSchristos if (ucmpv64(&ts64, &pt->head.stime) < 0) 3338585484eSchristos return fired; 3348585484eSchristos 3355d681e99Schristos /* now start to collect the remaining data */ 3368585484eSchristos due32 = pt->head.dtime.D_s.lo; 3378585484eSchristos 3388585484eSchristos qr->tai_diff = pt->head.next_tai - pt->head.this_tai; 3398585484eSchristos qr->ddist = due32 - ts32; 3408585484eSchristos qr->dynamic = pt->head.dynls; 3418585484eSchristos qr->proximity = LSPROX_SCHEDULE; 3428585484eSchristos 3438585484eSchristos /* if not in the last day before transition, we're done. */ 3448585484eSchristos if (!betweenu32(due32 - SECSPERDAY, ts32, due32)) 3458585484eSchristos return fired; 3468585484eSchristos 3478585484eSchristos qr->proximity = LSPROX_ANNOUNCE; 3488585484eSchristos if (!betweenu32(due32 - 10, ts32, due32)) 3498585484eSchristos return fired; 3508585484eSchristos 3518585484eSchristos /* The last 10s before the transition. Prepare for action! */ 3528585484eSchristos qr->proximity = LSPROX_ALERT; 3538585484eSchristos return fired; 3548585484eSchristos } 3558585484eSchristos 3568585484eSchristos /* ------------------------------------------------------------------ */ 3578585484eSchristos int/*BOOL*/ 3585d681e99Schristos leapsec_query_era( 3595d681e99Schristos leap_era_t * qr , 3605d681e99Schristos uint32_t ntpts, 3615d681e99Schristos const time_t * pivot) 3625d681e99Schristos { 3635d681e99Schristos const leap_table_t * pt; 3645d681e99Schristos vint64 ts64; 3655d681e99Schristos 3665d681e99Schristos pt = leapsec_get_table(FALSE); 3675d681e99Schristos ts64 = ntpcal_ntp_to_ntp(ntpts, pivot); 3685d681e99Schristos fetch_leap_era(qr, pt, &ts64); 3695d681e99Schristos return TRUE; 3705d681e99Schristos } 3715d681e99Schristos 3725d681e99Schristos /* ------------------------------------------------------------------ */ 3735d681e99Schristos int/*BOOL*/ 3748585484eSchristos leapsec_frame( 3758585484eSchristos leap_result_t *qr) 3768585484eSchristos { 3778585484eSchristos const leap_table_t * pt; 3788585484eSchristos 3798585484eSchristos memset(qr, 0, sizeof(leap_result_t)); 3808585484eSchristos pt = leapsec_get_table(FALSE); 3818585484eSchristos 3828585484eSchristos qr->tai_offs = pt->head.this_tai; 3838585484eSchristos qr->tai_diff = pt->head.next_tai - pt->head.this_tai; 3845d681e99Schristos qr->ebase = pt->head.ebase; 3858585484eSchristos qr->ttime = pt->head.ttime; 3868585484eSchristos qr->dynamic = pt->head.dynls; 3878585484eSchristos 3885d681e99Schristos return ucmpv64(&pt->head.ttime, &pt->head.stime) >= 0; 3898585484eSchristos } 3908585484eSchristos 3918585484eSchristos /* ------------------------------------------------------------------ */ 3928585484eSchristos /* Reset the current leap frame */ 3938585484eSchristos void 3948585484eSchristos leapsec_reset_frame(void) 3958585484eSchristos { 3968585484eSchristos reset_times(leapsec_get_table(FALSE)); 3978585484eSchristos } 3988585484eSchristos 3998585484eSchristos /* ------------------------------------------------------------------ */ 400*eabc0478Schristos /* load a file from a FILE pointer. Note: If vhash is true, load 401b8ecfcfeSchristos * only after successful signature check. The stream must be seekable 402b8ecfcfeSchristos * or this will fail. 403b8ecfcfeSchristos */ 4048585484eSchristos int/*BOOL*/ 405b8ecfcfeSchristos leapsec_load_stream( 4068585484eSchristos FILE * ifp , 407b8ecfcfeSchristos const char * fname, 408cdfa2a7eSchristos int/*BOOL*/ logall, 409cdfa2a7eSchristos int/*BOOL*/ vhash) 4108585484eSchristos { 4118585484eSchristos leap_table_t *pt; 412b8ecfcfeSchristos int rcheck; 4138585484eSchristos 414b8ecfcfeSchristos if (NULL == fname) 415b8ecfcfeSchristos fname = "<unknown>"; 416b8ecfcfeSchristos 417cdfa2a7eSchristos if (vhash) { 418*eabc0478Schristos rcheck = leapsec_validate((leapsec_reader)&getc, ifp); 419b8ecfcfeSchristos if (logall) 420b8ecfcfeSchristos switch (rcheck) 421b8ecfcfeSchristos { 422b8ecfcfeSchristos case LSVALID_GOODHASH: 423b8ecfcfeSchristos msyslog(LOG_NOTICE, "%s ('%s'): good hash signature", 424b8ecfcfeSchristos logPrefix, fname); 425b8ecfcfeSchristos break; 426b8ecfcfeSchristos 427b8ecfcfeSchristos case LSVALID_NOHASH: 428b8ecfcfeSchristos msyslog(LOG_ERR, "%s ('%s'): no hash signature", 429b8ecfcfeSchristos logPrefix, fname); 430b8ecfcfeSchristos break; 431b8ecfcfeSchristos case LSVALID_BADHASH: 432b8ecfcfeSchristos msyslog(LOG_ERR, "%s ('%s'): signature mismatch", 433b8ecfcfeSchristos logPrefix, fname); 434b8ecfcfeSchristos break; 435b8ecfcfeSchristos case LSVALID_BADFORMAT: 436b8ecfcfeSchristos msyslog(LOG_ERR, "%s ('%s'): malformed hash signature", 437b8ecfcfeSchristos logPrefix, fname); 438b8ecfcfeSchristos break; 439b8ecfcfeSchristos default: 440b8ecfcfeSchristos msyslog(LOG_ERR, "%s ('%s'): unknown error code %d", 441b8ecfcfeSchristos logPrefix, fname, rcheck); 442b8ecfcfeSchristos break; 443b8ecfcfeSchristos } 444b8ecfcfeSchristos if (rcheck < 0) 445b8ecfcfeSchristos return FALSE; 446b8ecfcfeSchristos rewind(ifp); 447cdfa2a7eSchristos } 4488585484eSchristos pt = leapsec_get_table(TRUE); 449b8ecfcfeSchristos if (!leapsec_load(pt, (leapsec_reader)getc, ifp, TRUE)) { 450b8ecfcfeSchristos switch (errno) { 451b8ecfcfeSchristos case EINVAL: 452b8ecfcfeSchristos msyslog(LOG_ERR, "%s ('%s'): bad transition time", 453b8ecfcfeSchristos logPrefix, fname); 454b8ecfcfeSchristos break; 455b8ecfcfeSchristos case ERANGE: 456b8ecfcfeSchristos msyslog(LOG_ERR, "%s ('%s'): times not ascending", 457b8ecfcfeSchristos logPrefix, fname); 458b8ecfcfeSchristos break; 459b8ecfcfeSchristos default: 460b8ecfcfeSchristos msyslog(LOG_ERR, "%s ('%s'): parsing error", 461b8ecfcfeSchristos logPrefix, fname); 462b8ecfcfeSchristos break; 463b8ecfcfeSchristos } 464b8ecfcfeSchristos return FALSE; 465b8ecfcfeSchristos } 466b8ecfcfeSchristos 467b8ecfcfeSchristos if (pt->head.size) 468b8ecfcfeSchristos msyslog(LOG_NOTICE, "%s ('%s'): loaded, expire=%s last=%s ofs=%d", 469b8ecfcfeSchristos logPrefix, fname, lstostr(&pt->head.expire), 470b8ecfcfeSchristos lstostr(&pt->info[0].ttime), pt->info[0].taiof); 471b8ecfcfeSchristos else 472b8ecfcfeSchristos msyslog(LOG_NOTICE, 473b8ecfcfeSchristos "%s ('%s'): loaded, expire=%s ofs=%d (no entries after build date)", 474b8ecfcfeSchristos logPrefix, fname, lstostr(&pt->head.expire), 475b8ecfcfeSchristos pt->head.base_tai); 476b8ecfcfeSchristos 477b8ecfcfeSchristos return leapsec_set_table(pt); 478b8ecfcfeSchristos } 479b8ecfcfeSchristos 480b8ecfcfeSchristos /* ------------------------------------------------------------------ */ 481b8ecfcfeSchristos int/*BOOL*/ 482b8ecfcfeSchristos leapsec_load_file( 483b8ecfcfeSchristos const char * fname, 484b8ecfcfeSchristos struct stat * sb_old, 485b8ecfcfeSchristos int/*BOOL*/ force, 486cdfa2a7eSchristos int/*BOOL*/ logall, 487cdfa2a7eSchristos int/*BOOL*/ vhash) 488b8ecfcfeSchristos { 489b8ecfcfeSchristos FILE * fp; 490b8ecfcfeSchristos struct stat sb_new; 491b8ecfcfeSchristos int rc; 492b8ecfcfeSchristos 493b8ecfcfeSchristos /* just do nothing if there is no leap file */ 494b8ecfcfeSchristos if ( !(fname && *fname) ) 495b8ecfcfeSchristos return FALSE; 496b8ecfcfeSchristos 497b8ecfcfeSchristos /* try to stat the leapfile */ 498b8ecfcfeSchristos if (0 != stat(fname, &sb_new)) { 499b8ecfcfeSchristos if (logall) 500b8ecfcfeSchristos msyslog(LOG_ERR, "%s ('%s'): stat failed: %m", 501b8ecfcfeSchristos logPrefix, fname); 502b8ecfcfeSchristos return FALSE; 503b8ecfcfeSchristos } 504b8ecfcfeSchristos 505b8ecfcfeSchristos /* silently skip to postcheck if no new file found */ 506b8ecfcfeSchristos if (NULL != sb_old) { 507b8ecfcfeSchristos if (!force 508b8ecfcfeSchristos && sb_old->st_mtime == sb_new.st_mtime 509b8ecfcfeSchristos && sb_old->st_ctime == sb_new.st_ctime 510b8ecfcfeSchristos ) 511b8ecfcfeSchristos return FALSE; 512b8ecfcfeSchristos *sb_old = sb_new; 513b8ecfcfeSchristos } 514b8ecfcfeSchristos 515b8ecfcfeSchristos /* try to open the leap file, complain if that fails 516b8ecfcfeSchristos * 517b8ecfcfeSchristos * [perlinger@ntp.org] 518b8ecfcfeSchristos * coverity raises a TOCTOU (time-of-check/time-of-use) issue 519b8ecfcfeSchristos * here, which is not entirely helpful: While there is indeed a 520b8ecfcfeSchristos * possible race condition between the 'stat()' call above and 521b8ecfcfeSchristos * the 'fopen)' call below, I intentionally want to omit the 522b8ecfcfeSchristos * overhead of opening the file and calling 'fstat()', because 523b8ecfcfeSchristos * in most cases the file would have be to closed anyway without 524b8ecfcfeSchristos * reading the contents. I chose to disable the coverity 525b8ecfcfeSchristos * warning instead. 526b8ecfcfeSchristos * 527b8ecfcfeSchristos * So unless someone comes up with a reasonable argument why 528b8ecfcfeSchristos * this could be a real issue, I'll just try to silence coverity 529b8ecfcfeSchristos * on that topic. 530b8ecfcfeSchristos */ 531b8ecfcfeSchristos /* coverity[toctou] */ 532b8ecfcfeSchristos if ((fp = fopen(fname, "r")) == NULL) { 533b8ecfcfeSchristos if (logall) 534b8ecfcfeSchristos msyslog(LOG_ERR, 535b8ecfcfeSchristos "%s ('%s'): open failed: %m", 536b8ecfcfeSchristos logPrefix, fname); 537b8ecfcfeSchristos return FALSE; 538b8ecfcfeSchristos } 539b8ecfcfeSchristos 540cdfa2a7eSchristos rc = leapsec_load_stream(fp, fname, logall, vhash); 541b8ecfcfeSchristos fclose(fp); 542b8ecfcfeSchristos return rc; 5438585484eSchristos } 5448585484eSchristos 5458585484eSchristos /* ------------------------------------------------------------------ */ 5468585484eSchristos void 5478585484eSchristos leapsec_getsig( 5488585484eSchristos leap_signature_t * psig) 5498585484eSchristos { 5508585484eSchristos const leap_table_t * pt; 5518585484eSchristos 5528585484eSchristos pt = leapsec_get_table(FALSE); 5538585484eSchristos memcpy(psig, &pt->lsig, sizeof(leap_signature_t)); 5548585484eSchristos } 5558585484eSchristos 5568585484eSchristos /* ------------------------------------------------------------------ */ 5578585484eSchristos int/*BOOL*/ 5588585484eSchristos leapsec_expired( 5598585484eSchristos uint32_t when, 5608585484eSchristos const time_t * tpiv) 5618585484eSchristos { 5628585484eSchristos const leap_table_t * pt; 5638585484eSchristos vint64 limit; 5648585484eSchristos 5658585484eSchristos pt = leapsec_get_table(FALSE); 5668585484eSchristos limit = ntpcal_ntp_to_ntp(when, tpiv); 5678585484eSchristos return ucmpv64(&limit, &pt->head.expire) >= 0; 5688585484eSchristos } 5698585484eSchristos 5708585484eSchristos /* ------------------------------------------------------------------ */ 5718585484eSchristos int32_t 5728585484eSchristos leapsec_daystolive( 5738585484eSchristos uint32_t when, 5748585484eSchristos const time_t * tpiv) 5758585484eSchristos { 5768585484eSchristos const leap_table_t * pt; 5778585484eSchristos vint64 limit; 5788585484eSchristos 5798585484eSchristos pt = leapsec_get_table(FALSE); 5808585484eSchristos limit = ntpcal_ntp_to_ntp(when, tpiv); 5818585484eSchristos limit = subv64(&pt->head.expire, &limit); 5828585484eSchristos return ntpcal_daysplit(&limit).hi; 5838585484eSchristos } 5848585484eSchristos 5858585484eSchristos /* ------------------------------------------------------------------ */ 5865d681e99Schristos #if 0 /* currently unused -- possibly revived later */ 5878585484eSchristos int/*BOOL*/ 5888585484eSchristos leapsec_add_fix( 5898585484eSchristos int total, 5908585484eSchristos uint32_t ttime, 5918585484eSchristos uint32_t etime, 5928585484eSchristos const time_t * pivot) 5938585484eSchristos { 5948585484eSchristos time_t tpiv; 5958585484eSchristos leap_table_t * pt; 5968585484eSchristos vint64 tt64, et64; 5978585484eSchristos 5988585484eSchristos if (pivot == NULL) { 5998585484eSchristos time(&tpiv); 6008585484eSchristos pivot = &tpiv; 6018585484eSchristos } 6028585484eSchristos 6038585484eSchristos et64 = ntpcal_ntp_to_ntp(etime, pivot); 6048585484eSchristos tt64 = ntpcal_ntp_to_ntp(ttime, pivot); 6058585484eSchristos pt = leapsec_get_table(TRUE); 6068585484eSchristos 6078585484eSchristos if ( ucmpv64(&et64, &pt->head.expire) <= 0 6088585484eSchristos || !leapsec_raw(pt, &tt64, total, FALSE) ) 6098585484eSchristos return FALSE; 6108585484eSchristos 6118585484eSchristos pt->lsig.etime = etime; 6128585484eSchristos pt->lsig.ttime = ttime; 6138585484eSchristos pt->lsig.taiof = (int16_t)total; 6148585484eSchristos 6158585484eSchristos pt->head.expire = et64; 6168585484eSchristos 6178585484eSchristos return leapsec_set_table(pt); 6188585484eSchristos } 6195d681e99Schristos #endif 6208585484eSchristos 6218585484eSchristos /* ------------------------------------------------------------------ */ 6228585484eSchristos int/*BOOL*/ 6238585484eSchristos leapsec_add_dyn( 6248585484eSchristos int insert, 6258585484eSchristos uint32_t ntpnow, 6268585484eSchristos const time_t * pivot ) 6278585484eSchristos { 6288585484eSchristos leap_table_t * pt; 6298585484eSchristos vint64 now64; 6308585484eSchristos 6318585484eSchristos pt = leapsec_get_table(TRUE); 6328585484eSchristos now64 = ntpcal_ntp_to_ntp(ntpnow, pivot); 633b8ecfcfeSchristos return ( leapsec_add(pt, &now64, (insert != 0)) 634b8ecfcfeSchristos && leapsec_set_table(pt)); 6358585484eSchristos } 6368585484eSchristos 6375d681e99Schristos /* ------------------------------------------------------------------ */ 6385d681e99Schristos int/*BOOL*/ 6395d681e99Schristos leapsec_autokey_tai( 6405d681e99Schristos int tai_offset, 6415d681e99Schristos uint32_t ntpnow , 6425d681e99Schristos const time_t * pivot ) 6435d681e99Schristos { 6445d681e99Schristos leap_table_t * pt; 6455d681e99Schristos leap_era_t era; 6465d681e99Schristos vint64 now64; 6475d681e99Schristos int idx; 6485d681e99Schristos 6495d681e99Schristos (void)tai_offset; 6505d681e99Schristos pt = leapsec_get_table(FALSE); 6515d681e99Schristos 6525d681e99Schristos /* Bail out if the basic offset is not zero and the putative 6535d681e99Schristos * offset is bigger than 10s. That was in 1972 -- we don't want 6545d681e99Schristos * to go back that far! 6555d681e99Schristos */ 6565d681e99Schristos if (pt->head.base_tai != 0 || tai_offset < 10) 6575d681e99Schristos return FALSE; 6585d681e99Schristos 6595d681e99Schristos /* If there's already data in the table, check if an update is 6605d681e99Schristos * possible. Update is impossible if there are static entries 6615d681e99Schristos * (since this indicates a valid leapsecond file) or if we're 6625d681e99Schristos * too close to a leapsecond transition: We do not know on what 6635d681e99Schristos * side the transition the sender might have been, so we use a 6645d681e99Schristos * dead zone around the transition. 6655d681e99Schristos */ 6665d681e99Schristos 6675d681e99Schristos /* Check for static entries */ 6685d681e99Schristos for (idx = 0; idx != pt->head.size; idx++) 6695d681e99Schristos if ( ! pt->info[idx].dynls) 6705d681e99Schristos return FALSE; 6715d681e99Schristos 6725d681e99Schristos /* get the fulll time stamp and leap era for it */ 6735d681e99Schristos now64 = ntpcal_ntp_to_ntp(ntpnow, pivot); 6745d681e99Schristos fetch_leap_era(&era, pt, &now64); 6755d681e99Schristos 6765d681e99Schristos /* check the limits with 20s dead band */ 6775d681e99Schristos era.ebase = addv64i32(&era.ebase, 20); 6785d681e99Schristos if (ucmpv64(&now64, &era.ebase) < 0) 6795d681e99Schristos return FALSE; 6805d681e99Schristos 6815d681e99Schristos era.ttime = addv64i32(&era.ttime, -20); 6825d681e99Schristos if (ucmpv64(&now64, &era.ttime) > 0) 6835d681e99Schristos return FALSE; 6845d681e99Schristos 6855d681e99Schristos /* Here we can proceed. Calculate the delta update. */ 6865d681e99Schristos tai_offset -= era.taiof; 6875d681e99Schristos 6885d681e99Schristos /* Shift the header info offsets. */ 6895d681e99Schristos pt->head.base_tai += tai_offset; 6905d681e99Schristos pt->head.this_tai += tai_offset; 6915d681e99Schristos pt->head.next_tai += tai_offset; 6925d681e99Schristos 6935d681e99Schristos /* Shift table entry offsets (if any) */ 6945d681e99Schristos for (idx = 0; idx != pt->head.size; idx++) 6955d681e99Schristos pt->info[idx].taiof += tai_offset; 6965d681e99Schristos 6975d681e99Schristos /* claim success... */ 6985d681e99Schristos return TRUE; 6995d681e99Schristos } 7005d681e99Schristos 7015d681e99Schristos 7028585484eSchristos /* ===================================================================== 7038585484eSchristos * internal helpers 7048585484eSchristos */ 7058585484eSchristos 7068585484eSchristos /* [internal] Reset / init the time window in the leap processor to 7078585484eSchristos * force reload on next query. Since a leap transition cannot take place 7088585484eSchristos * at an odd second, the value chosen avoids spurious leap transition 7098585484eSchristos * triggers. Making all three times equal forces a reload. Using the 7108585484eSchristos * maximum value for unsigned 64 bits makes finding the next leap frame 7118585484eSchristos * a bit easier. 7128585484eSchristos */ 7138585484eSchristos static void 7148585484eSchristos reset_times( 7158585484eSchristos leap_table_t * pt) 7168585484eSchristos { 7178585484eSchristos memset(&pt->head.ebase, 0xFF, sizeof(vint64)); 7188585484eSchristos pt->head.stime = pt->head.ebase; 7198585484eSchristos pt->head.ttime = pt->head.ebase; 7208585484eSchristos pt->head.dtime = pt->head.ebase; 7218585484eSchristos } 7228585484eSchristos 7238585484eSchristos /* [internal] Add raw data to the table, removing old entries on the 7248585484eSchristos * fly. This cannot fail currently. 7258585484eSchristos */ 7268585484eSchristos static int/*BOOL*/ 7278585484eSchristos add_range( 7288585484eSchristos leap_table_t * pt, 7298585484eSchristos const leap_info_t * pi) 7308585484eSchristos { 7318585484eSchristos /* If the table is full, make room by throwing out the oldest 7324eea345dSchristos * entry. But remember the accumulated leap seconds! 7334eea345dSchristos * 7344eea345dSchristos * Setting the first entry is a bit tricky, too: Simply assuming 7354eea345dSchristos * it is an insertion is wrong if the first entry is a dynamic 7364eea345dSchristos * leap second removal. So we decide on the sign -- if the first 7374eea345dSchristos * entry has a negative offset, we assume that it is a leap 7384eea345dSchristos * second removal. In both cases the table base offset is set 7394eea345dSchristos * accordingly to reflect the decision. 7404eea345dSchristos * 7414eea345dSchristos * In practice starting with a removal can only happen if the 7424eea345dSchristos * first entry is a dynamic request without having a leap file 7434eea345dSchristos * for the history proper. 7448585484eSchristos */ 7455d681e99Schristos if (pt->head.size == 0) { 7464eea345dSchristos if (pi->taiof >= 0) 7475d681e99Schristos pt->head.base_tai = pi->taiof - 1; 7484eea345dSchristos else 7494eea345dSchristos pt->head.base_tai = pi->taiof + 1; 7505d681e99Schristos } else if (pt->head.size >= MAX_HIST) { 7518585484eSchristos pt->head.size = MAX_HIST - 1; 7528585484eSchristos pt->head.base_tai = pt->info[pt->head.size].taiof; 7538585484eSchristos } 7548585484eSchristos 7558585484eSchristos /* make room in lower end and insert item */ 7568585484eSchristos memmove(pt->info+1, pt->info, pt->head.size*sizeof(*pt->info)); 7578585484eSchristos pt->info[0] = *pi; 7588585484eSchristos pt->head.size++; 7598585484eSchristos 7608585484eSchristos /* invalidate the cached limit data -- we might have news ;-) 7618585484eSchristos * 7628585484eSchristos * This blocks a spurious transition detection. OTOH, if you add 7638585484eSchristos * a value after the last query before a leap transition was 7648585484eSchristos * expected to occur, this transition trigger is lost. But we 7658585484eSchristos * can probably live with that. 7668585484eSchristos */ 7678585484eSchristos reset_times(pt); 7688585484eSchristos return TRUE; 7698585484eSchristos } 7708585484eSchristos 7718585484eSchristos /* [internal] given a reader function, read characters into a buffer 7728585484eSchristos * until either EOL or EOF is reached. Makes sure that the buffer is 7738585484eSchristos * always NUL terminated, but silently truncates excessive data. The 7748585484eSchristos * EOL-marker ('\n') is *not* stored in the buffer. 7758585484eSchristos * 7768585484eSchristos * Returns the pointer to the buffer, unless EOF was reached when trying 7778585484eSchristos * to read the first character of a line. 7788585484eSchristos */ 7798585484eSchristos static char * 7808585484eSchristos get_line( 7818585484eSchristos leapsec_reader func, 7828585484eSchristos void * farg, 7838585484eSchristos char * buff, 7848585484eSchristos size_t size) 7858585484eSchristos { 7868585484eSchristos int ch; 7878585484eSchristos char *ptr; 7888585484eSchristos 7898585484eSchristos /* if we cannot even store the delimiter, declare failure */ 7908585484eSchristos if (buff == NULL || size == 0) 7918585484eSchristos return NULL; 7928585484eSchristos 7938585484eSchristos ptr = buff; 7948585484eSchristos while (EOF != (ch = (*func)(farg)) && '\n' != ch) 7958585484eSchristos if (size > 1) { 7968585484eSchristos size--; 7978585484eSchristos *ptr++ = (char)ch; 7988585484eSchristos } 7998585484eSchristos /* discard trailing whitespace */ 8008585484eSchristos while (ptr != buff && isspace((u_char)ptr[-1])) 8018585484eSchristos ptr--; 8028585484eSchristos *ptr = '\0'; 8038585484eSchristos return (ptr == buff && ch == EOF) ? NULL : buff; 8048585484eSchristos } 8058585484eSchristos 8068585484eSchristos /* [internal] skips whitespace characters from a character buffer. */ 807*eabc0478Schristos static inline char * 8088585484eSchristos skipws( 809*eabc0478Schristos char * ptr 810*eabc0478Schristos ) 8118585484eSchristos { 812*eabc0478Schristos while (isspace((u_char)*ptr)) { 8138585484eSchristos ptr++; 814*eabc0478Schristos } 815*eabc0478Schristos return ptr; 8168585484eSchristos } 8178585484eSchristos 8185d681e99Schristos /* [internal] check if a strtoXYZ ended at EOL or whitespace and 8198585484eSchristos * converted something at all. Return TRUE if something went wrong. 8208585484eSchristos */ 8218585484eSchristos static int/*BOOL*/ 8228585484eSchristos parsefail( 8238585484eSchristos const char * cp, 8248585484eSchristos const char * ep) 8258585484eSchristos { 8268585484eSchristos return (cp == ep) 8278585484eSchristos || (*ep && *ep != '#' && !isspace((u_char)*ep)); 8288585484eSchristos } 8298585484eSchristos 8308585484eSchristos /* [internal] reload the table limits around the given time stamp. This 8318585484eSchristos * is where the real work is done when it comes to table lookup and 8328585484eSchristos * evaluation. Some care has been taken to have correct code for dealing 8338585484eSchristos * with boundary conditions and empty tables. 8348585484eSchristos * 8358585484eSchristos * In electric mode, transition and trip time are the same. In dumb 8368585484eSchristos * mode, the difference of the TAI offsets must be taken into account 8378585484eSchristos * and trip time and transition time become different. The difference 8388585484eSchristos * becomes the warping distance when the trip time is reached. 8398585484eSchristos */ 8408585484eSchristos static void 8418585484eSchristos reload_limits( 8428585484eSchristos leap_table_t * pt, 8438585484eSchristos const vint64 * ts) 8448585484eSchristos { 8458585484eSchristos int idx; 8468585484eSchristos 8478585484eSchristos /* Get full time and search the true lower bound. Use a 8488585484eSchristos * simple loop here, since the number of entries does 8498585484eSchristos * not warrant a binary search. This also works for an empty 8508585484eSchristos * table, so there is no shortcut for that case. 8518585484eSchristos */ 8528585484eSchristos for (idx = 0; idx != pt->head.size; idx++) 8538585484eSchristos if (ucmpv64(ts, &pt->info[idx].ttime) >= 0) 8548585484eSchristos break; 8558585484eSchristos 8568585484eSchristos /* get time limits with proper bound conditions. Note that the 8578585484eSchristos * bounds of the table will be observed even if the table is 8588585484eSchristos * empty -- no undefined condition must arise from this code. 8598585484eSchristos */ 8608585484eSchristos if (idx >= pt->head.size) { 8618585484eSchristos memset(&pt->head.ebase, 0x00, sizeof(vint64)); 8628585484eSchristos pt->head.this_tai = pt->head.base_tai; 8638585484eSchristos } else { 8648585484eSchristos pt->head.ebase = pt->info[idx].ttime; 8658585484eSchristos pt->head.this_tai = pt->info[idx].taiof; 8668585484eSchristos } 8678585484eSchristos if (--idx >= 0) { 8688585484eSchristos pt->head.next_tai = pt->info[idx].taiof; 8698585484eSchristos pt->head.dynls = pt->info[idx].dynls; 8708585484eSchristos pt->head.ttime = pt->info[idx].ttime; 8718585484eSchristos 8728585484eSchristos if (_electric) 8738585484eSchristos pt->head.dtime = pt->head.ttime; 8748585484eSchristos else 8758585484eSchristos pt->head.dtime = addv64i32( 8768585484eSchristos &pt->head.ttime, 8778585484eSchristos pt->head.next_tai - pt->head.this_tai); 8788585484eSchristos 8798585484eSchristos pt->head.stime = subv64u32( 8808585484eSchristos &pt->head.ttime, pt->info[idx].stime); 8818585484eSchristos 8828585484eSchristos } else { 8838585484eSchristos memset(&pt->head.ttime, 0xFF, sizeof(vint64)); 8848585484eSchristos pt->head.stime = pt->head.ttime; 8858585484eSchristos pt->head.dtime = pt->head.ttime; 8868585484eSchristos pt->head.next_tai = pt->head.this_tai; 8878585484eSchristos pt->head.dynls = 0; 8888585484eSchristos } 8898585484eSchristos } 8908585484eSchristos 8915d681e99Schristos /* [internal] fetch the leap era for a given time stamp. 8925d681e99Schristos * This is a cut-down version the algorithm used to reload the table 8935d681e99Schristos * limits, but it does not update any global state and provides just the 8945d681e99Schristos * era information for a given time stamp. 8955d681e99Schristos */ 8965d681e99Schristos static void 8975d681e99Schristos fetch_leap_era( 8985d681e99Schristos leap_era_t * into, 8995d681e99Schristos const leap_table_t * pt , 9005d681e99Schristos const vint64 * ts ) 9015d681e99Schristos { 9025d681e99Schristos int idx; 9035d681e99Schristos 9045d681e99Schristos /* Simple search loop, also works with empty table. */ 9055d681e99Schristos for (idx = 0; idx != pt->head.size; idx++) 9065d681e99Schristos if (ucmpv64(ts, &pt->info[idx].ttime) >= 0) 9075d681e99Schristos break; 9085d681e99Schristos /* fetch era data, keeping an eye on boundary conditions */ 9095d681e99Schristos if (idx >= pt->head.size) { 9105d681e99Schristos memset(&into->ebase, 0x00, sizeof(vint64)); 9115d681e99Schristos into->taiof = pt->head.base_tai; 9125d681e99Schristos } else { 9135d681e99Schristos into->ebase = pt->info[idx].ttime; 9145d681e99Schristos into->taiof = pt->info[idx].taiof; 9155d681e99Schristos } 9165d681e99Schristos if (--idx >= 0) 9175d681e99Schristos into->ttime = pt->info[idx].ttime; 9185d681e99Schristos else 9195d681e99Schristos memset(&into->ttime, 0xFF, sizeof(vint64)); 9205d681e99Schristos } 9215d681e99Schristos 9228585484eSchristos /* [internal] Take a time stamp and create a leap second frame for 9238585484eSchristos * it. This will schedule a leap second for the beginning of the next 9248585484eSchristos * month, midnight UTC. The 'insert' argument tells if a leap second is 9258585484eSchristos * added (!=0) or removed (==0). We do not handle multiple inserts 9268585484eSchristos * (yet?) 9278585484eSchristos * 9288585484eSchristos * Returns 1 if the insert worked, 0 otherwise. (It's not possible to 9298585484eSchristos * insert a leap second into the current history -- only appending 9308585484eSchristos * towards the future is allowed!) 9318585484eSchristos */ 9328585484eSchristos static int/*BOOL*/ 9338585484eSchristos leapsec_add( 9348585484eSchristos leap_table_t* pt , 9358585484eSchristos const vint64 * now64 , 9368585484eSchristos int insert) 9378585484eSchristos { 9387476e6e4Schristos vint64 ttime, starttime; 9398585484eSchristos struct calendar fts; 9408585484eSchristos leap_info_t li; 9418585484eSchristos 9425d681e99Schristos /* Check against the table expiration and the latest available 9438585484eSchristos * leap entry. Do not permit inserts, only appends, and only if 9448585484eSchristos * the extend the table beyond the expiration! 9458585484eSchristos */ 9468585484eSchristos if ( ucmpv64(now64, &pt->head.expire) < 0 9478585484eSchristos || (pt->head.size && ucmpv64(now64, &pt->info[0].ttime) <= 0)) { 9488585484eSchristos errno = ERANGE; 9498585484eSchristos return FALSE; 9508585484eSchristos } 9518585484eSchristos 9528585484eSchristos ntpcal_ntp64_to_date(&fts, now64); 9538585484eSchristos /* To guard against dangling leap flags: do not accept leap 9548585484eSchristos * second request on the 1st hour of the 1st day of the month. 9558585484eSchristos */ 9568585484eSchristos if (fts.monthday == 1 && fts.hour == 0) { 9578585484eSchristos errno = EINVAL; 9588585484eSchristos return FALSE; 9598585484eSchristos } 9608585484eSchristos 9618585484eSchristos /* Ok, do the remaining calculations */ 9628585484eSchristos fts.monthday = 1; 9638585484eSchristos fts.hour = 0; 9648585484eSchristos fts.minute = 0; 9658585484eSchristos fts.second = 0; 9667476e6e4Schristos starttime = ntpcal_date_to_ntp64(&fts); 9678585484eSchristos fts.month++; 9688585484eSchristos ttime = ntpcal_date_to_ntp64(&fts); 9698585484eSchristos 9708585484eSchristos li.ttime = ttime; 9717476e6e4Schristos li.stime = ttime.D_s.lo - starttime.D_s.lo; 9728585484eSchristos li.taiof = (pt->head.size ? pt->info[0].taiof : pt->head.base_tai) 9738585484eSchristos + (insert ? 1 : -1); 9748585484eSchristos li.dynls = 1; 9758585484eSchristos return add_range(pt, &li); 9768585484eSchristos } 9778585484eSchristos 9788585484eSchristos /* [internal] Given a time stamp for a leap insertion (the exact begin 9798585484eSchristos * of the new leap era), create new leap frame and put it into the 9808585484eSchristos * table. This is the work horse for reading a leap file and getting a 9818585484eSchristos * leap second update via authenticated network packet. 9828585484eSchristos */ 9838585484eSchristos int/*BOOL*/ 9848585484eSchristos leapsec_raw( 9858585484eSchristos leap_table_t * pt, 9868585484eSchristos const vint64 * ttime, 9878585484eSchristos int taiof, 9888585484eSchristos int dynls) 9898585484eSchristos { 9907476e6e4Schristos vint64 starttime; 9918585484eSchristos struct calendar fts; 9928585484eSchristos leap_info_t li; 9938585484eSchristos 9945d681e99Schristos /* Check that we either extend the table or get a duplicate of 9955d681e99Schristos * the latest entry. The latter is a benevolent overwrite with 9965d681e99Schristos * identical data and could happen if we get an autokey message 9975d681e99Schristos * that extends the lifetime of the current leapsecond table. 9985d681e99Schristos * Otherwise paranoia rulez! 9995d681e99Schristos */ 10005d681e99Schristos if (pt->head.size) { 10015d681e99Schristos int cmp = ucmpv64(ttime, &pt->info[0].ttime); 10025d681e99Schristos if (cmp == 0) 10035d681e99Schristos cmp -= (taiof != pt->info[0].taiof); 10045d681e99Schristos if (cmp < 0) { 10058585484eSchristos errno = ERANGE; 10068585484eSchristos return FALSE; 10078585484eSchristos } 10085d681e99Schristos if (cmp == 0) 10095d681e99Schristos return TRUE; 10105d681e99Schristos } 10118585484eSchristos 10128585484eSchristos ntpcal_ntp64_to_date(&fts, ttime); 10138585484eSchristos /* If this does not match the exact month start, bail out. */ 10148585484eSchristos if (fts.monthday != 1 || fts.hour || fts.minute || fts.second) { 10158585484eSchristos errno = EINVAL; 10168585484eSchristos return FALSE; 10178585484eSchristos } 10188585484eSchristos fts.month--; /* was in range 1..12, no overflow here! */ 10197476e6e4Schristos starttime = ntpcal_date_to_ntp64(&fts); 10208585484eSchristos li.ttime = *ttime; 10217476e6e4Schristos li.stime = ttime->D_s.lo - starttime.D_s.lo; 10228585484eSchristos li.taiof = (int16_t)taiof; 10238585484eSchristos li.dynls = (dynls != 0); 10248585484eSchristos return add_range(pt, &li); 10258585484eSchristos } 10268585484eSchristos 10278585484eSchristos /* [internal] Do a wrap-around save range inclusion check. 10288585484eSchristos * Returns TRUE if x in [lo,hi[ (intervall open on right side) with full 10298585484eSchristos * handling of an overflow / wrap-around. 10308585484eSchristos */ 10318585484eSchristos static int/*BOOL*/ 10328585484eSchristos betweenu32( 10338585484eSchristos uint32_t lo, 10348585484eSchristos uint32_t x, 10358585484eSchristos uint32_t hi) 10368585484eSchristos { 10378585484eSchristos int rc; 1038b8ecfcfeSchristos 10398585484eSchristos if (lo <= hi) 10408585484eSchristos rc = (lo <= x) && (x < hi); 10418585484eSchristos else 10428585484eSchristos rc = (lo <= x) || (x < hi); 10438585484eSchristos return rc; 10448585484eSchristos } 10458585484eSchristos 1046b8ecfcfeSchristos /* ===================================================================== 1047b8ecfcfeSchristos * validation stuff 1048b8ecfcfeSchristos */ 1049b8ecfcfeSchristos 1050b8ecfcfeSchristos typedef struct { 1051b8ecfcfeSchristos unsigned char hv[ISC_SHA1_DIGESTLENGTH]; 1052b8ecfcfeSchristos } sha1_digest; 1053b8ecfcfeSchristos 1054b8ecfcfeSchristos /* [internal] parse a digest line to get the hash signature 1055b8ecfcfeSchristos * The NIST code creating the hash writes them out as 5 hex integers 1056b8ecfcfeSchristos * without leading zeros. This makes reading them back as hex-encoded 1057b8ecfcfeSchristos * BLOB impossible, because there might be less than 40 hex digits. 1058b8ecfcfeSchristos * 1059b8ecfcfeSchristos * The solution is to read the values back as integers, and then do the 1060b8ecfcfeSchristos * byte twiddle necessary to get it into an array of 20 chars. The 1061b8ecfcfeSchristos * drawback is that it permits any acceptable number syntax provided by 1062b8ecfcfeSchristos * 'scanf()' and 'strtoul()', including optional signs and '0x' 1063b8ecfcfeSchristos * prefixes. 1064b8ecfcfeSchristos */ 1065b8ecfcfeSchristos static int/*BOOL*/ 1066b8ecfcfeSchristos do_leap_hash( 1067b8ecfcfeSchristos sha1_digest * mac, 1068b8ecfcfeSchristos char const * cp ) 1069b8ecfcfeSchristos { 1070b8ecfcfeSchristos int wi, di, num, len; 1071b8ecfcfeSchristos unsigned long tmp[5]; 1072b8ecfcfeSchristos 1073b8ecfcfeSchristos memset(mac, 0, sizeof(*mac)); 1074b8ecfcfeSchristos num = sscanf(cp, " %lx %lx %lx %lx %lx%n", 1075b8ecfcfeSchristos &tmp[0], &tmp[1], &tmp[2], &tmp[3], &tmp[4], 1076b8ecfcfeSchristos &len); 1077b8ecfcfeSchristos if (num != 5 || cp[len] > ' ') 1078b8ecfcfeSchristos return FALSE; 1079b8ecfcfeSchristos 1080b8ecfcfeSchristos /* now do the byte twiddle */ 1081b8ecfcfeSchristos for (wi=0; wi < 5; ++wi) 1082b8ecfcfeSchristos for (di=3; di >= 0; --di) { 10837476e6e4Schristos mac->hv[wi*4 + di] = 10847476e6e4Schristos (unsigned char)(tmp[wi] & 0x0FF); 1085b8ecfcfeSchristos tmp[wi] >>= 8; 1086b8ecfcfeSchristos } 1087b8ecfcfeSchristos return TRUE; 1088b8ecfcfeSchristos } 1089b8ecfcfeSchristos 1090b8ecfcfeSchristos /* [internal] add the digits of a data line to the hash, stopping at the 1091b8ecfcfeSchristos * next hash ('#') character. 1092b8ecfcfeSchristos */ 1093b8ecfcfeSchristos static void 1094b8ecfcfeSchristos do_hash_data( 1095b8ecfcfeSchristos isc_sha1_t * mdctx, 1096b8ecfcfeSchristos char const * cp ) 1097b8ecfcfeSchristos { 1098b8ecfcfeSchristos unsigned char text[32]; // must be power of two! 1099b8ecfcfeSchristos unsigned int tlen = 0; 1100b8ecfcfeSchristos unsigned char ch; 1101b8ecfcfeSchristos 1102b8ecfcfeSchristos while ('\0' != (ch = *cp++) && '#' != ch) 1103b8ecfcfeSchristos if (isdigit(ch)) { 1104b8ecfcfeSchristos text[tlen++] = ch; 1105b8ecfcfeSchristos tlen &= (sizeof(text)-1); 1106b8ecfcfeSchristos if (0 == tlen) 1107b8ecfcfeSchristos isc_sha1_update( 1108b8ecfcfeSchristos mdctx, text, sizeof(text)); 1109b8ecfcfeSchristos } 1110b8ecfcfeSchristos 1111b8ecfcfeSchristos if (0 < tlen) 1112b8ecfcfeSchristos isc_sha1_update(mdctx, text, tlen); 1113b8ecfcfeSchristos } 1114b8ecfcfeSchristos 1115b8ecfcfeSchristos /* given a reader and a reader arg, calculate and validate the the hash 1116b8ecfcfeSchristos * signature of a NIST leap second file. 1117b8ecfcfeSchristos */ 1118b8ecfcfeSchristos int 1119b8ecfcfeSchristos leapsec_validate( 1120b8ecfcfeSchristos leapsec_reader func, 1121b8ecfcfeSchristos void * farg) 1122b8ecfcfeSchristos { 1123b8ecfcfeSchristos isc_sha1_t mdctx; 1124b8ecfcfeSchristos sha1_digest rdig, ldig; /* remote / local digests */ 1125b8ecfcfeSchristos char line[50]; 1126b8ecfcfeSchristos int hlseen = -1; 1127b8ecfcfeSchristos 1128b8ecfcfeSchristos isc_sha1_init(&mdctx); 1129b8ecfcfeSchristos while (get_line(func, farg, line, sizeof(line))) { 1130b8ecfcfeSchristos if (!strncmp(line, "#h", 2)) 1131b8ecfcfeSchristos hlseen = do_leap_hash(&rdig, line+2); 1132b8ecfcfeSchristos else if (!strncmp(line, "#@", 2)) 1133b8ecfcfeSchristos do_hash_data(&mdctx, line+2); 1134b8ecfcfeSchristos else if (!strncmp(line, "#$", 2)) 1135b8ecfcfeSchristos do_hash_data(&mdctx, line+2); 1136ea66d795Schristos else if (isdigit((unsigned char)line[0])) 1137b8ecfcfeSchristos do_hash_data(&mdctx, line); 1138b8ecfcfeSchristos } 1139b8ecfcfeSchristos isc_sha1_final(&mdctx, ldig.hv); 1140b8ecfcfeSchristos isc_sha1_invalidate(&mdctx); 1141b8ecfcfeSchristos 1142b8ecfcfeSchristos if (0 > hlseen) 1143b8ecfcfeSchristos return LSVALID_NOHASH; 1144b8ecfcfeSchristos if (0 == hlseen) 1145b8ecfcfeSchristos return LSVALID_BADFORMAT; 1146b8ecfcfeSchristos if (0 != memcmp(&rdig, &ldig, sizeof(sha1_digest))) 1147b8ecfcfeSchristos return LSVALID_BADHASH; 1148b8ecfcfeSchristos return LSVALID_GOODHASH; 1149b8ecfcfeSchristos } 1150b8ecfcfeSchristos 1151b8ecfcfeSchristos /* 1152b8ecfcfeSchristos * lstostr - prettyprint NTP seconds 1153b8ecfcfeSchristos */ 11545d681e99Schristos static const char * 11555d681e99Schristos lstostr( 1156b8ecfcfeSchristos const vint64 * ts) 1157b8ecfcfeSchristos { 1158b8ecfcfeSchristos char * buf; 1159b8ecfcfeSchristos struct calendar tm; 1160b8ecfcfeSchristos 1161b8ecfcfeSchristos LIB_GETBUF(buf); 11625d681e99Schristos 11635d681e99Schristos if ( ! (ts->d_s.hi >= 0 && ntpcal_ntp64_to_date(&tm, ts) >= 0)) 11645d681e99Schristos snprintf(buf, LIB_BUFLENGTH, "%s", "9999-12-31T23:59:59Z"); 11655d681e99Schristos else 11665d681e99Schristos snprintf(buf, LIB_BUFLENGTH, "%04d-%02d-%02dT%02d:%02d:%02dZ", 1167b8ecfcfeSchristos tm.year, tm.month, tm.monthday, 11685d681e99Schristos tm.hour, tm.minute, tm.second); 11695d681e99Schristos 1170b8ecfcfeSchristos return buf; 1171b8ecfcfeSchristos } 1172b8ecfcfeSchristos 11735d681e99Schristos /* reset the global state for unit tests */ 11745d681e99Schristos void 11755d681e99Schristos leapsec_ut_pristine(void) 11765d681e99Schristos { 11775d681e99Schristos memset(_ltab, 0, sizeof(_ltab)); 11785d681e99Schristos _lptr = NULL; 11795d681e99Schristos _electric = 0; 11805d681e99Schristos } 11815d681e99Schristos 1182b8ecfcfeSchristos 1183b8ecfcfeSchristos 11848585484eSchristos /* -*- that's all folks! -*- */ 1185