From 6558f972e00d6cde4dc708be8ddb05609a4b4d5c Mon Sep 17 00:00:00 2001 From: Stef Walter Date: Fri, 29 Mar 2013 13:17:29 +0100 Subject: [PATCH] Don't respect timezones for CKA_START_DATE or CKA_END_DATE The PKCS#11 specification does not note what timezone these dates are in. In addition the time values are not represented in PKCS#11. So don't reinterpret certificate dates, other than filling in the century for dates that have a two digit year. Lastly, these are low resolution optional fields so not being all strict about timezones here is appropriate. https://bugs.freedesktop.org/show_bug.cgi?id=62825 --- common/asn1.c | 332 --------------------------------------------- common/asn1.h | 6 - trust/builder.c | 100 +++++++++++--- trust/tests/test-builder.c | 14 +- 4 files changed, 81 insertions(+), 371 deletions(-) diff --git a/common/asn1.c b/common/asn1.c index c98d959..45d91ab 100644 --- a/common/asn1.c +++ b/common/asn1.c @@ -192,338 +192,6 @@ p11_asn1_encode (node_asn *asn, return der; } -static int -atoin (const char *p, - int digits) -{ - int ret = 0, base = 1; - while(--digits >= 0) { - if (p[digits] < '0' || p[digits] > '9') - return -1; - ret += (p[digits] - '0') * base; - base *= 10; - } - return ret; -} - -static int -two_to_four_digit_year (int year) -{ - time_t now; - struct tm tm; - int century, current; - - return_val_if_fail (year >= 0 && year <= 99, -1); - - /* Get the current year */ - now = time (NULL); - return_val_if_fail (now >= 0, -1); - if (!gmtime_r (&now, &tm)) - return_val_if_reached (-1); - - current = (tm.tm_year % 100); - century = (tm.tm_year + 1900) - current; - - /* - * Check if it's within 40 years before the - * current date. - */ - if (current < 40) { - if (year < current) - return century + year; - if (year > 100 - (40 - current)) - return (century - 100) + year; - } else { - if (year < current && year > (current - 40)) - return century + year; - } - - /* - * If it's after then adjust for overflows to - * the next century. - */ - if (year < current) - return century + 100 + year; - else - return century + year; -} - -static int -parse_utc_time (const char *time, - size_t n_time, - struct tm *when, - int *offset) -{ - const char *p, *e; - int year; - - assert (when != NULL); - assert (time != NULL); - assert (offset != NULL); - - /* YYMMDDhhmmss.ffff Z | +0000 */ - if (n_time < 6 || n_time >= 28) - return 0; - - /* Reset everything to default legal values */ - memset (when, 0, sizeof (*when)); - *offset = 0; - when->tm_mday = 1; - - /* Select the digits part of it */ - p = time; - for (e = p; *e >= '0' && *e <= '9'; ++e); - - if (p + 2 <= e) { - year = atoin (p, 2); - p += 2; - - /* - * 40 years in the past is our century. 60 years - * in the future is the next century. - */ - when->tm_year = two_to_four_digit_year (year) - 1900; - } - if (p + 2 <= e) { - when->tm_mon = atoin (p, 2) - 1; - p += 2; - } - if (p + 2 <= e) { - when->tm_mday = atoin (p, 2); - p += 2; - } - if (p + 2 <= e) { - when->tm_hour = atoin (p, 2); - p += 2; - } - if (p + 2 <= e) { - when->tm_min = atoin (p, 2); - p += 2; - } - if (p + 2 <= e) { - when->tm_sec = atoin (p, 2); - p += 2; - } - - if (when->tm_year < 0 || when->tm_year > 9999 || - when->tm_mon < 0 || when->tm_mon > 11 || - when->tm_mday < 1 || when->tm_mday > 31 || - when->tm_hour < 0 || when->tm_hour > 23 || - when->tm_min < 0 || when->tm_min > 59 || - when->tm_sec < 0 || when->tm_sec > 59) - return 0; - - /* Make sure all that got parsed */ - if (p != e) - return 0; - - /* Now the remaining optional stuff */ - e = time + n_time; - - /* See if there's a fraction, and discard it if so */ - if (p < e && *p == '.' && p + 5 <= e) - p += 5; - - /* See if it's UTC */ - if (p < e && *p == 'Z') { - p += 1; - - /* See if it has a timezone */ - } else if ((*p == '-' || *p == '+') && p + 3 <= e) { - int off, neg; - - neg = *p == '-'; - ++p; - - off = atoin (p, 2) * 3600; - if (off < 0 || off > 86400) - return 0; - p += 2; - - if (p + 2 <= e) { - off += atoin (p, 2) * 60; - p += 2; - } - - /* Use TZ offset */ - if (neg) - *offset = 0 - off; - else - *offset = off; - } - - /* Make sure everything got parsed */ - if (p != e) - return 0; - - return 1; -} - -static int -parse_general_time (const char *time, - size_t n_time, - struct tm *when, - int *offset) -{ - const char *p, *e; - - assert (time != NULL); - assert (when != NULL); - assert (offset != NULL); - - /* YYYYMMDDhhmmss.ffff Z | +0000 */ - if (n_time < 8 || n_time >= 30) - return 0; - - /* Reset everything to default legal values */ - memset (when, 0, sizeof (*when)); - *offset = 0; - when->tm_mday = 1; - - /* Select the digits part of it */ - p = time; - for (e = p; *e >= '0' && *e <= '9'; ++e); - - if (p + 4 <= e) { - when->tm_year = atoin (p, 4) - 1900; - p += 4; - } - if (p + 2 <= e) { - when->tm_mon = atoin (p, 2) - 1; - p += 2; - } - if (p + 2 <= e) { - when->tm_mday = atoin (p, 2); - p += 2; - } - if (p + 2 <= e) { - when->tm_hour = atoin (p, 2); - p += 2; - } - if (p + 2 <= e) { - when->tm_min = atoin (p, 2); - p += 2; - } - if (p + 2 <= e) { - when->tm_sec = atoin (p, 2); - p += 2; - } - - if (when->tm_year < 0 || when->tm_year > 9999 || - when->tm_mon < 0 || when->tm_mon > 11 || - when->tm_mday < 1 || when->tm_mday > 31 || - when->tm_hour < 0 || when->tm_hour > 23 || - when->tm_min < 0 || when->tm_min > 59 || - when->tm_sec < 0 || when->tm_sec > 59) - return 0; - - /* Make sure all that got parsed */ - if (p != e) - return 0; - - /* Now the remaining optional stuff */ - e = time + n_time; - - /* See if there's a fraction, and discard it if so */ - if (p < e && *p == '.' && p + 5 <= e) - p += 5; - - /* See if it's UTC */ - if (p < e && *p == 'Z') { - p += 1; - - /* See if it has a timezone */ - } else if ((*p == '-' || *p == '+') && p + 3 <= e) { - int off, neg; - - neg = *p == '-'; - ++p; - - off = atoin (p, 2) * 3600; - if (off < 0 || off > 86400) - return 0; - p += 2; - - if (p + 2 <= e) { - off += atoin (p, 2) * 60; - p += 2; - } - - /* Use TZ offset */ - if (neg) - *offset = 0 - off; - else - *offset = off; - } - - /* Make sure everything got parsed */ - if (p != e) - return 0; - - return 1; -} - -static time_t -when_and_offset_to_time_t (struct tm *when, - int tz_offset) -{ - time_t timet; - - /* A 32-bit time, cannot represent this time */ - if (sizeof (time_t) <= 4 && when->tm_year >= 138) { - return -1; - - /* Convert to seconds since epoch */ - } else { - timet = timegm (when); - return_val_if_fail (timet >= 0, -1); - timet += tz_offset; - } - - if (!gmtime_r (&timet, when)) - return_val_if_reached (-1); - - return timet; -} - -time_t -p11_asn1_parse_utc (const char *time_str, - struct tm *when) -{ - struct tm dummy; - int tz_offset; - int ret; - - if (!when) - when = &dummy; - - ret = parse_utc_time (time_str, strlen (time_str), - when, &tz_offset); - if (!ret) - return -1; - - return when_and_offset_to_time_t (when, tz_offset); -} - -time_t -p11_asn1_parse_general (const char *time_str, - struct tm *when) -{ - struct tm dummy; - int tz_offset; - int ret; - - if (!when) - when = &dummy; - - ret = parse_general_time (time_str, strlen (time_str), - when, &tz_offset); - if (!ret) - return -1; - - return when_and_offset_to_time_t (when, tz_offset); -} - ssize_t p11_asn1_tlv_length (const unsigned char *data, size_t length) diff --git a/common/asn1.h b/common/asn1.h index c79e8f6..1bd7dd1 100644 --- a/common/asn1.h +++ b/common/asn1.h @@ -55,12 +55,6 @@ node_asn * p11_asn1_create (p11_dict *asn1_defs, unsigned char * p11_asn1_encode (node_asn *asn, size_t *der_len); -time_t p11_asn1_parse_utc (const char *time_str, - struct tm *when); - -time_t p11_asn1_parse_general (const char *time_str, - struct tm *when); - ssize_t p11_asn1_tlv_length (const unsigned char *data, size_t length); diff --git a/trust/builder.c b/trust/builder.c index 32f2d1b..886b972 100644 --- a/trust/builder.c +++ b/trust/builder.c @@ -227,16 +227,72 @@ calc_check_value (const unsigned char *data, memcpy (check_value, checksum, 3); } +static int +atoin (const char *p, + int digits) +{ + int ret = 0, base = 1; + while(--digits >= 0) { + if (p[digits] < '0' || p[digits] > '9') + return -1; + ret += (p[digits] - '0') * base; + base *= 10; + } + return ret; +} + +static int +century_for_two_digit_year (int year) +{ + time_t now; + struct tm tm; + int century, current; + + return_val_if_fail (year >= 0 && year <= 99, -1); + + /* Get the current year */ + now = time (NULL); + return_val_if_fail (now >= 0, -1); + if (!gmtime_r (&now, &tm)) + return_val_if_reached (-1); + + current = (tm.tm_year % 100); + century = (tm.tm_year + 1900) - current; + + /* + * Check if it's within 40 years before the + * current date. + */ + if (current < 40) { + if (year < current) + return century; + if (year > 100 - (40 - current)) + return century - 100; + } else { + if (year < current && year > (current - 40)) + return century; + } + + /* + * If it's after then adjust for overflows to + * the next century. + */ + if (year < current) + return century + 100; + else + return century; +} + static bool calc_date (node_asn *node, const char *field, CK_DATE *date) { node_asn *choice; - struct tm when; char buf[64]; - time_t timet; + int century; char *sub; + int year; int len; int ret; @@ -252,39 +308,43 @@ calc_date (node_asn *node, sub = strconcat (field, ".", buf, NULL); + /* + * So here we take a shortcut and just copy the date from the + * certificate into the CK_DATE. This doesn't take into account + * time zones. However the PKCS#11 spec does not say what timezone + * the dates are in. In the PKCS#11 value have a day resolution, + * and time zones aren't that critical. + */ + if (strcmp (buf, "generalTime") == 0) { len = sizeof (buf) - 1; ret = asn1_read_value (node, sub, buf, &len); return_val_if_fail (ret == ASN1_SUCCESS, false); - timet = p11_asn1_parse_general (buf, &when); + return_val_if_fail (len >= 8, false); + + /* Same as first 8 characters of date */ + memcpy (date, buf, 8); } else if (strcmp (buf, "utcTime") == 0) { len = sizeof (buf) - 1; ret = asn1_read_value (node, sub, buf, &len); return_val_if_fail (ret == ASN1_SUCCESS, false); - timet = p11_asn1_parse_utc (buf, &when); + return_val_if_fail (len >= 6, false); + + year = atoin (buf, 2); + return_val_if_fail (year > 0, false); + + century = century_for_two_digit_year (year); + return_val_if_fail (century >= 0, false); + + snprintf ((char *)date->year, 3, "%02d", century); + memcpy (((char *)date) + 2, buf, 6); } else { return_val_if_reached (false); } free (sub); - - if (timet < 0) - return false; - - assert (sizeof (date->year) == 4); - snprintf ((char *)buf, 5, "%04d", 1900 + when.tm_year); - memcpy (date->year, buf, 4); - - assert (sizeof (date->month) == 2); - snprintf ((char *)buf, 3, "%02d", when.tm_mon + 1); - memcpy (date->month, buf, 2); - - assert (sizeof (date->day) == 2); - snprintf ((char *)buf, 3, "%02d", when.tm_mday); - memcpy (date->day, buf, 2); - return true; } diff --git a/trust/tests/test-builder.c b/trust/tests/test-builder.c index 5ce3b22..7ae3f14 100644 --- a/trust/tests/test-builder.c +++ b/trust/tests/test-builder.c @@ -552,7 +552,7 @@ test_build_distant_end_date (CuTest *cu) }; CK_ATTRIBUTE expected[] = { - { CKA_END_DATE, }, + { CKA_END_DATE, "20671229", 8 }, { CKA_START_DATE, "20130327", 8 }, { CKA_INVALID }, }; @@ -562,18 +562,6 @@ test_build_distant_end_date (CuTest *cu) setup (cu); - /* - * On a 32-bit system, the end date will be too big to compute with - * libc. So it'll be empty, since this is an optional field. - */ - if (sizeof (time_t) <= 4) { - expected[0].pValue = ""; - expected[0].ulValueLen = 0; - } else { - expected[0].pValue = "20671229"; - expected[0].ulValueLen = 8; - } - attrs = NULL; rv = p11_builder_build (test.builder, test.index, &attrs, p11_attrs_dup (input)); CuAssertIntEquals (cu, CKR_OK, rv); -- 1.8.2