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