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