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