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