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