summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorStef Walter <stefw@gnome.org>2013-03-29 13:17:29 +0100
committerStef Walter <stefw@gnome.org>2013-04-03 15:45:38 +0200
commit8c69e467527c5ee484c9a921e9b5fd18c0c49b12 (patch)
treee4a847854881cd70a4234761ee96062456810e97
parent91aa0f9623e232fa253308c4f7464dab8902dfea (diff)
downloadp11-kit-8c69e467527c5ee484c9a921e9b5fd18c0c49b12.tar.gz
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
-rw-r--r--common/asn1.c332
-rw-r--r--common/asn1.h6
-rw-r--r--trust/builder.c100
-rw-r--r--trust/tests/test-builder.c14
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 e41d73f..15999bb 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 7cab1f6..a875b96 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);