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