From: Juergen Perlinger Date: Sat, 1 Mar 2014 11:52:12 +0000 (+0000) Subject: [Bug 2570] add signature check when reading leapseconds file X-Git-Tag: NTP_4_2_7P428~2^2 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=3342a07b78e4ad6b780f2dcc12f000203eda68cc;p=thirdparty%2Fntp.git [Bug 2570] add signature check when reading leapseconds file bk: 5311c9ecpgDfZ41n78yDOR2kLliigg --- diff --git a/ChangeLog b/ChangeLog index 66ba2efde..1bc233c67 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,4 @@ +* [Bug 2570] refuse to load leapsec file with bad/missing SHA1 hash * [Bug 2562] first release of the GPSD client clock (type 46) (4.2.7p426) 2014/02/28 Released by Harlan Stenn * [Bug 2113] Warn about ignored extra args in ntpq. diff --git a/libntp/Makefile.am b/libntp/Makefile.am index d0aa08d7c..069f85722 100644 --- a/libntp/Makefile.am +++ b/libntp/Makefile.am @@ -34,6 +34,7 @@ libisc_SRCS = \ $(srcdir)/../lib/isc/task.c \ $(srcdir)/../lib/isc/$(LIBISC_PTHREADS_NOTHREADS)/thread.c \ $(srcdir)/../lib/isc/unix/time.c \ + $(srcdir)/../lib/isc/sha1.c \ $(srcdir)/../lib/isc/sockaddr.c \ $(NULL) diff --git a/ntpd/ntp_leapsec.c b/ntpd/ntp_leapsec.c index 22e2bd9c7..2e52d0a83 100644 --- a/ntpd/ntp_leapsec.c +++ b/ntpd/ntp_leapsec.c @@ -19,6 +19,8 @@ #include "ntp_leapsec.h" #include "ntp.h" +#include "isc/sha1.h" + /* --------------------------------------------------------------------- * GCC is rather sticky with its 'const' attribute. We have to do it more * explicit than with a cast if we want to get rid of a CONST qualifier. @@ -614,13 +616,49 @@ leapsec_reset_frame(void) } /* ------------------------------------------------------------------ */ +/* load a file from a FILE pointer. Note: If hcheck is true, load + * only after successful signature check. The stream must be seekable + * or this will fail. + */ int/*BOOL*/ leapsec_load_file( FILE * ifp , - int blimit) + int blimit, + int hcheck) { leap_table_t * pt; + int rcheck; + + hcheck = (hcheck != 0); + rcheck = leapsec_validate((leapsec_reader)getc, ifp); + switch (rcheck) + { + case LSVALID_GOODHASH: + msyslog(LOG_NOTICE, "%s", + "leapsecond file: good hash signature"); + break; + + case LSVALID_NOHASH: + msyslog(LOG_INFO, "%s", + "leapsecond file: no hash signature"); + break; + case LSVALID_BADHASH: + msyslog(LOG_ERR, "%s", + "leapsecond file: signature mismatch"); + break; + case LSVALID_BADFORMAT: + msyslog(LOG_ERR, "%s", + "leapsecond file: malformed hash signature"); + break; + default: + msyslog(LOG_ERR, "leapsecond file: unknown error code %d", + rcheck); + break; + } + if (rcheck < hcheck) + return 0; + rewind(ifp); pt = leapsec_get_table(TRUE); return leapsec_load(pt, (leapsec_reader)getc, ifp, blimit) && leapsec_set_table(pt); @@ -998,4 +1036,106 @@ betweenu32( return rc; } +/* ===================================================================== + * validation stuff + */ + +typedef struct { + unsigned char hv[ISC_SHA1_DIGESTLENGTH]; +} sha1_digest; + +/* [internal] parse a digest line to get the hash signature + * I would have preferred the CTYPE 'isblank()' function, but alas, + * it's not in MSVC bevore Studio2013; it also seems to be added + * with the C99 standard (not being sure about that) and not all + * target compilers implement this. Using a direct character + * compare is the way out; the NIST leapsec file is 7bit ASCII + * and the locale should not matter much here. + */ +static int/*BOOL*/ +do_leap_hash( + sha1_digest * mac, + char const * cp ) +{ + int idx, dv; + + memset(mac, 0, sizeof(*mac)); + for (idx = 0; *cp && idx < 2*sizeof(*mac); ++cp) { + if (isdigit(*cp)) + dv = *cp - '0'; + else if (isxdigit(*cp)) + dv = (toupper(*cp) - 'A' + 10); + else if ('\t' == *cp || ' ' == *cp) /*isblank(*cp))*/ + continue; + else + break; + + mac->hv[idx/2] = (unsigned char)( + (mac->hv[idx/2] * 16) + dv); + ++idx; + } + return (idx == 2*sizeof(*mac)); +} + +/* [internal] add the digits of a data line to the hash, stopping at the + * next hash ('#') character. + */ +static void +do_hash_data( + isc_sha1_t * mdctx, + char const * cp ) +{ + unsigned char text[32]; // must be power of two! + unsigned int tlen = 0; + unsigned char ch; + + while ('\0' != (ch = *cp++) && '#' != ch) + if (isdigit(ch)) { + text[tlen++] = ch; + tlen &= (sizeof(text)-1); + if (0 == tlen) + isc_sha1_update( + mdctx, text, sizeof(text)); + } + + if (0 < tlen) + isc_sha1_update(mdctx, text, tlen); +} + +/* given a reader and a reader arg, calculate and validate the the hash + * signature of a NIST leap second file. + */ +int +leapsec_validate( + leapsec_reader func, + void * farg) +{ + isc_sha1_t mdctx; + sha1_digest rdig, ldig; /* remote / local digests */ + char line[50]; + int hlseen = -1; + + isc_sha1_init(&mdctx); + while (get_line(func, farg, line, sizeof(line))) { + if (!strncmp(line, "#h", 2)) + hlseen = do_leap_hash(&rdig, line+2); + else if (!strncmp(line, "#@", 2)) + do_hash_data(&mdctx, line+2); + else if (!strncmp(line, "#$", 2)) + do_hash_data(&mdctx, line+2); + else if (isdigit(line[0])) + do_hash_data(&mdctx, line); + } + isc_sha1_final(&mdctx, ldig.hv); + isc_sha1_invalidate(&mdctx); + + if (0 > hlseen) + return LSVALID_NOHASH; + if (0 == hlseen) + return LSVALID_BADFORMAT; + if (0 != memcmp(&rdig, &ldig, sizeof(sha1_digest))) + return LSVALID_BADHASH; + return LSVALID_GOODHASH; +} + /* -*- that's all folks! -*- */ diff --git a/ntpd/ntp_leapsec.h b/ntpd/ntp_leapsec.h index f2442c224..014028a7b 100644 --- a/ntpd/ntp_leapsec.h +++ b/ntpd/ntp_leapsec.h @@ -26,6 +26,19 @@ typedef int (*leapsec_reader)(void*); struct leap_table; typedef struct leap_table leap_table_t; +/* Validate a stream containing a leap second file in the NIST / NTPD + * format that can also be loaded via 'leapsec_load()'. This uses + * the SHA1 hash and preprocessing as described in the NIST leapsecond + * file. + */ +#define LSVALID_GOODHASH 1 /* valid signature */ +#define LSVALID_NOHASH 0 /* no signature in file */ +#define LSVALID_BADHASH -1 /* signature mismatch */ +#define LSVALID_BADFORMAT -2 /* signature not parseable */ + +extern int leapsec_validate(leapsec_reader, void*); + + /* Set/get electric mode * Electric mode is defined as the operation mode where the system clock * automagically manages the leap second, so we don't have to care about @@ -123,7 +136,7 @@ extern void leapsec_dump(const leap_table_t*, leapsec_dumper func, void *farg); /* Read a leap second file. This is a convenience wrapper around the * generic load function, 'leapsec_load()'. */ -extern int/*BOOL*/ leapsec_load_file(FILE * fp, int blimit); +extern int/*BOOL*/ leapsec_load_file(FILE * fp, int blimit, int checked); /* Get the current leap data signature. This consists of the last * ransition, the table expiration, and the total TAI difference at the diff --git a/ntpd/ntp_util.c b/ntpd/ntp_util.c index 185f4b0e7..5613f1b96 100644 --- a/ntpd/ntp_util.c +++ b/ntpd/ntp_util.c @@ -471,6 +471,10 @@ stats_config( /* * Read leapseconds file. + * + * Note: Currently a leap file without SHA1 signature is + * accepted, but if there is a signature line, the signature + * must be valid or the file is rejected. */ case STATS_LEAP_FILE: if (!value || (len = strlen(value)) == 0) @@ -489,7 +493,7 @@ stats_config( msyslog(LOG_ERR, "leapseconds: stat(%s) failed: %m", leapseconds_file); - } else if (!leapsec_load_file(fp, TRUE)) { + } else if (!leapsec_load_file(fp, TRUE, FALSE)) { msyslog(LOG_ERR, "format error leapseconds file %s", leapseconds_file); @@ -866,6 +870,10 @@ record_timing_stats( * -1 if there was a problem, * 0 if the leapfile has expired or less than 24hrs remaining TTL * >0 # of full days until the leapfile expires + * + * Note: This loads a new leapfile on the fly. Currently a leap file + * without SHA1 signature is accepted, but if there is a signature line, + * the signature must be valid or the file is rejected. */ int check_leap_file( @@ -894,7 +902,7 @@ check_leap_file( } else if ( (sp1->st_mtime != sp2->st_mtime) || (sp1->st_ctime != sp2->st_ctime)) { leapseconds_file_sb1 = leapseconds_file_sb2; - if (!leapsec_load_file(fp, TRUE)) { + if (!leapsec_load_file(fp, TRUE, FALSE)) { msyslog(LOG_ERR, "format error leapseconds file %s", leapseconds_file); diff --git a/ports/winnt/vs2008/libntp/libntp.vcproj b/ports/winnt/vs2008/libntp/libntp.vcproj index f9f57507d..536c16e02 100644 --- a/ports/winnt/vs2008/libntp/libntp.vcproj +++ b/ports/winnt/vs2008/libntp/libntp.vcproj @@ -1,7 +1,7 @@ + + @@ -862,7 +866,7 @@ >