#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.
}
/* ------------------------------------------------------------------ */
+/* 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);
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! -*- */
"3550089600 33 # 1 Jul 2012\n"
"#\n";
+// short table with good hash
+static const char leap_ghash [] =
+ "#\n"
+ "#@ 3610569600\n"
+ "#$ 3610566000\n"
+ "#\n"
+ "2272060800 10 # 1 Jan 1972\n"
+ "2287785600 11 # 1 Jul 1972\n"
+ "2303683200 12 # 1 Jan 1973\n"
+ "2335219200 13 # 1 Jan 1974\n"
+ "2366755200 14 # 1 Jan 1975\n"
+ "2398291200 15 # 1 Jan 1976\n"
+ "2429913600 16 # 1 Jan 1977\n"
+ "2461449600 17 # 1 Jan 1978\n"
+ "2492985600 18 # 1 Jan 1979\n"
+ "2524521600 19 # 1 Jan 1980\n"
+ "#\n"
+ "#h 4b304e10 95642b3f c10b91f9 90791725 25f280d0\n"
+ "#\n";
+
+// short table with bad hash
+static const char leap_bhash [] =
+ "#\n"
+ "#@ 3610569600\n"
+ "#$ 3610566000\n"
+ "#\n"
+ "2272060800 10 # 1 Jan 1972\n"
+ "2287785600 11 # 1 Jul 1972\n"
+ "2303683200 12 # 1 Jan 1973\n"
+ "2335219200 13 # 1 Jan 1974\n"
+ "2366755200 14 # 1 Jan 1975\n"
+ "2398291200 15 # 1 Jan 1976\n"
+ "2429913600 16 # 1 Jan 1977\n"
+ "2461449600 17 # 1 Jan 1978\n"
+ "2492985600 18 # 1 Jan 1979\n"
+ "2524521600 19 # 1 Jan 1980\n"
+ "#\n"
+ "#h dc2e6b0b 5aade95d a0587abd 4e0dacb4 e4d5049e\n"
+ "#\n";
+
+// short table with malformed hash
+static const char leap_mhash [] =
+ "#\n"
+ "#@ 3610569600\n"
+ "#$ 3610566000\n"
+ "#\n"
+ "2272060800 10 # 1 Jan 1972\n"
+ "2287785600 11 # 1 Jul 1972\n"
+ "2303683200 12 # 1 Jan 1973\n"
+ "2335219200 13 # 1 Jan 1974\n"
+ "2366755200 14 # 1 Jan 1975\n"
+ "2398291200 15 # 1 Jan 1976\n"
+ "2429913600 16 # 1 Jan 1977\n"
+ "2461449600 17 # 1 Jan 1978\n"
+ "2492985600 18 # 1 Jan 1979\n"
+ "2524521600 19 # 1 Jan 1980\n"
+ "#\n"
+ "#h f2349a02 788b9534 a8f2e141 f2029Q6d 4064a7ee\n"
+ "#\n";
static uint32_t lsec2009 = 3439756800u; // 1 Jan 2009, 00:00:00 utc
static uint32_t lsec2012 = 3550089600u; // 1 Jul 2012, 00:00:00 utc
ntpcal_set_timefunc(NULL);
}
+// =====================================================================
+// VALIDATION TESTS
+// =====================================================================
+
+// ----------------------------------------------------------------------
+TEST_F(leapsecTest, ValidateGood) {
+ const char *cp = leap_ghash;
+ int rc = leapsec_validate(stringreader, &cp);
+ EXPECT_EQ(LSVALID_GOODHASH, rc);
+}
+
+// ----------------------------------------------------------------------
+TEST_F(leapsecTest, ValidateNoHash) {
+ const char *cp = leap2;
+ int rc = leapsec_validate(stringreader, &cp);
+ EXPECT_EQ(LSVALID_NOHASH, rc);
+}
+
+// ----------------------------------------------------------------------
+TEST_F(leapsecTest, ValidateBad) {
+ const char *cp = leap_bhash;
+ int rc = leapsec_validate(stringreader, &cp);
+ EXPECT_EQ(LSVALID_BADHASH, rc);
+}
+
+// ----------------------------------------------------------------------
+TEST_F(leapsecTest, ValidateMalformed) {
+ const char *cp = leap_mhash;
+ int rc = leapsec_validate(stringreader, &cp);
+ EXPECT_EQ(LSVALID_BADFORMAT, rc);
+}
+
+// =====================================================================
+// BASIC FUNCTIONS
+// =====================================================================
+
+// ----------------------------------------------------------------------
// test number parser
TEST_F(leapsecTest, ParseVUI64) {
vint64 act, exp;
EXPECT_EQ(*ep, '\0');
}
+// ----------------------------------------------------------------------
// test table selection
TEST_F(leapsecTest, tableSelect) {
leap_table_t *pt1, *pt2, *pt3, *pt4;
EXPECT_NE(pt2, pt3);
}
+// ----------------------------------------------------------------------
// load file & check expiration
TEST_F(leapsecTest, loadFileExpire) {
const char *cp = leap1;
EXPECT_EQ(1, rc);
}
+// ----------------------------------------------------------------------
// load file & check time-to-live
TEST_F(leapsecTest, loadFileTTL) {
const char *cp = leap1;
EXPECT_EQ(-1, rc);
}
+// ----------------------------------------------------------------------
// ad-hoc jump: leap second at 2009.01.01 -60days
TEST_F(leapsecTest, ls2009faraway) {
int rc;
EXPECT_EQ(LSPROX_NOWARN, qr.proximity);
}
+// ----------------------------------------------------------------------
// ad-hoc jump: leap second at 2009.01.01 -1week
TEST_F(leapsecTest, ls2009weekaway) {
int rc;
EXPECT_EQ(LSPROX_SCHEDULE, qr.proximity);
}
+// ----------------------------------------------------------------------
// ad-hoc jump: leap second at 2009.01.01 -1hr
TEST_F(leapsecTest, ls2009houraway) {
int rc;
EXPECT_EQ(LSPROX_ANNOUNCE, qr.proximity);
}
+// ----------------------------------------------------------------------
// ad-hoc jump: leap second at 2009.01.01 -1sec
TEST_F(leapsecTest, ls2009secaway) {
int rc;
EXPECT_EQ(LSPROX_ALERT, qr.proximity);
}
+// ----------------------------------------------------------------------
// ad-hoc jump to leap second at 2009.01.01
TEST_F(leapsecTest, ls2009onspot) {
int rc;
EXPECT_EQ(LSPROX_NOWARN, qr.proximity);
}
+// ----------------------------------------------------------------------
// test handling of the leap second at 2009.01.01 without table
TEST_F(leapsecTest, ls2009nodata) {
int rc;
EXPECT_EQ(LSPROX_NOWARN, qr.proximity);
}
+// ----------------------------------------------------------------------
// test handling of the leap second at 2009.01.01 with culled data
TEST_F(leapsecTest, ls2009limdata) {
int rc;
EXPECT_EQ(LSPROX_NOWARN, qr.proximity);
}
+// ----------------------------------------------------------------------
// add dynamic leap second (like from peer/clock)
TEST_F(leapsecTest, addDynamic) {
int rc;
//leapsec_dump(pt, (leapsec_dumper)fprintf, stdout);
}
+// ----------------------------------------------------------------------
// add fixed leap seconds (like from network packet)
TEST_F(leapsecTest, addFixed) {
int rc;
// SEQUENCE TESTS
// =====================================================================
+// ----------------------------------------------------------------------
// leap second insert at 2009.01.01, electric mode
TEST_F(leapsecTest, ls2009seqInsElectric) {
int rc;
EXPECT_EQ(LSPROX_NOWARN, qr.proximity);
}
+// ----------------------------------------------------------------------
// leap second insert at 2009.01.01, dumb mode
TEST_F(leapsecTest, ls2009seqInsDumb) {
int rc;
}
+// ----------------------------------------------------------------------
// fake leap second remove at 2009.01.01, electric mode
TEST_F(leapsecTest, ls2009seqDelElectric) {
int rc;
EXPECT_EQ(LSPROX_NOWARN, qr.proximity);
}
+// ----------------------------------------------------------------------
// fake leap second remove at 2009.01.01. dumb mode
TEST_F(leapsecTest, ls2009seqDelDumb) {
int rc;
EXPECT_EQ(LSPROX_NOWARN, qr.proximity);
}
+// ----------------------------------------------------------------------
// leap second insert at 2012.07.01, electric mode
TEST_F(leapsecTest, ls2012seqInsElectric) {
int rc;
EXPECT_EQ(LSPROX_NOWARN, qr.proximity);
}
+// ----------------------------------------------------------------------
// leap second insert at 2012.07.01, dumb mode
TEST_F(leapsecTest, ls2012seqInsDumb) {
int rc;
EXPECT_EQ(LSPROX_NOWARN, qr.proximity);
}
-