]> git.ipfire.org Git - thirdparty/openssl.git/blobdiff - crypto/asn1/a_time.c
X509: Enable printing cert even with invalid validity times, saying 'Bad time value'
[thirdparty/openssl.git] / crypto / asn1 / a_time.c
index e5b5f9a052a8a099cca3bce227bc77548d00c41d..aebbf53fd075d014d91ee21999101d7e23c25971 100644 (file)
@@ -1,7 +1,7 @@
 /*
- * Copyright 1999-2017 The OpenSSL Project Authors. All Rights Reserved.
+ * Copyright 1999-2020 The OpenSSL Project Authors. All Rights Reserved.
  *
- * Licensed under the OpenSSL license (the "License").  You may not use
+ * Licensed under the Apache License 2.0 (the "License").  You may not use
  * this file except in compliance with the License.  You can obtain a copy
  * in the file LICENSE in the source distribution or at
  * https://www.openssl.org/source/license.html
 
 #include <stdio.h>
 #include <time.h>
+#include "crypto/asn1.h"
+#include "crypto/ctype.h"
 #include "internal/cryptlib.h"
 #include <openssl/asn1t.h>
-#include "asn1_locl.h"
+#include "asn1_local.h"
 
 IMPLEMENT_ASN1_MSTRING(ASN1_TIME, B_ASN1_TIME)
 
 IMPLEMENT_ASN1_FUNCTIONS(ASN1_TIME)
+IMPLEMENT_ASN1_DUP_FUNCTION(ASN1_TIME)
+
+static int is_utc(const int year)
+{
+    if (50 <= year && year <= 149)
+        return 1;
+    return 0;
+}
 
 static int leap_year(const int year)
 {
@@ -59,7 +69,7 @@ static void determine_days(struct tm *tm)
     }
     c = y / 100;
     y %= 100;
-    /* Zeller's congruance */
+    /* Zeller's congruence */
     tm->tm_wday = (d + (13 * m) / 5 + y + y / 4 + c / 4 + 5 * c + 6) % 7;
 }
 
@@ -71,7 +81,11 @@ int asn1_time_to_tm(struct tm *tm, const ASN1_TIME *d)
     char *a;
     int n, i, i2, l, o, min_l = 11, strict = 0, end = 6, btz = 5, md;
     struct tm tmp;
-
+#if defined(CHARSET_EBCDIC)
+    const char upper_z = 0x5A, num_zero = 0x30, period = 0x2E, minus = 0x2D, plus = 0x2B;
+#else
+    const char upper_z = 'Z', num_zero = '0', period = '.', minus = '-', plus = '+';
+#endif
     /*
      * ASN1_STRING_FLAG_X509_TIME is used to enforce RFC 5280
      * time string format, in which:
@@ -112,20 +126,20 @@ int asn1_time_to_tm(struct tm *tm, const ASN1_TIME *d)
     if (l < min_l)
         goto err;
     for (i = 0; i < end; i++) {
-        if (!strict && (i == btz) && ((a[o] == 'Z') || (a[o] == '+') || (a[o] == '-'))) {
+        if (!strict && (i == btz) && ((a[o] == upper_z) || (a[o] == plus) || (a[o] == minus))) {
             i++;
             break;
         }
-        if ((a[o] < '0') || (a[o] > '9'))
+        if (!ascii_isdigit(a[o]))
             goto err;
-        n = a[o] - '0';
+        n = a[o] - num_zero;
         /* incomplete 2-digital number */
         if (++o == l)
             goto err;
 
-        if ((a[o] < '0') || (a[o] > '9'))
+        if (!ascii_isdigit(a[o]))
             goto err;
-        n = (n * 10) + a[o] - '0';
+        n = (n * 10) + a[o] - num_zero;
         /* no more bytes to read, but we haven't seen time-zone yet */
         if (++o == l)
             goto err;
@@ -177,14 +191,14 @@ int asn1_time_to_tm(struct tm *tm, const ASN1_TIME *d)
      * Optional fractional seconds: decimal point followed by one or more
      * digits.
      */
-    if (d->type == V_ASN1_GENERALIZEDTIME && a[o] == '.') {
+    if (d->type == V_ASN1_GENERALIZEDTIME && a[o] == period) {
         if (strict)
             /* RFC 5280 forbids fractional seconds */
             goto err;
         if (++o == l)
             goto err;
         i = o;
-        while ((o < l) && (a[o] >= '0') && (a[o] <= '9'))
+        while ((o < l) && ascii_isdigit(a[o]))
             o++;
         /* Must have at least one digit after decimal point */
         if (i == o)
@@ -199,10 +213,10 @@ int asn1_time_to_tm(struct tm *tm, const ASN1_TIME *d)
      * 'o' can point to '\0' is either the subsequent if or the first
      * else if is true.
      */
-    if (a[o] == 'Z') {
+    if (a[o] == upper_z) {
         o++;
-    } else if (!strict && ((a[o] == '+') || (a[o] == '-'))) {
-        int offsign = a[o] == '-' ? 1 : -1;
+    } else if (!strict && ((a[o] == plus) || (a[o] == minus))) {
+        int offsign = a[o] == minus ? 1 : -1;
         int offset = 0;
 
         o++;
@@ -215,13 +229,13 @@ int asn1_time_to_tm(struct tm *tm, const ASN1_TIME *d)
         if (o + 4 != l)
             goto err;
         for (i = end; i < end + 2; i++) {
-            if ((a[o] < '0') || (a[o] > '9'))
+            if (!ascii_isdigit(a[o]))
                 goto err;
-            n = a[o] - '0';
+            n = a[o] - num_zero;
             o++;
-            if ((a[o] < '0') || (a[o] > '9'))
+            if (!ascii_isdigit(a[o]))
                 goto err;
-            n = (n * 10) + a[o] - '0';
+            n = (n * 10) + a[o] - num_zero;
             i2 = (d->type == V_ASN1_UTCTIME) ? i + 1 : i;
             if ((n < min[i2]) || (n > max[i2]))
                 goto err;
@@ -250,6 +264,58 @@ int asn1_time_to_tm(struct tm *tm, const ASN1_TIME *d)
     return 0;
 }
 
+ASN1_TIME *asn1_time_from_tm(ASN1_TIME *s, struct tm *ts, int type)
+{
+    char* p;
+    ASN1_TIME *tmps = NULL;
+    const size_t len = 20;
+
+    if (type == V_ASN1_UNDEF) {
+        if (is_utc(ts->tm_year))
+            type = V_ASN1_UTCTIME;
+        else
+            type = V_ASN1_GENERALIZEDTIME;
+    } else if (type == V_ASN1_UTCTIME) {
+        if (!is_utc(ts->tm_year))
+            goto err;
+    } else if (type != V_ASN1_GENERALIZEDTIME) {
+        goto err;
+    }
+
+    if (s == NULL)
+        tmps = ASN1_STRING_new();
+    else
+        tmps = s;
+    if (tmps == NULL)
+        return NULL;
+
+    if (!ASN1_STRING_set(tmps, NULL, len))
+        goto err;
+
+    tmps->type = type;
+    p = (char*)tmps->data;
+
+    if (type == V_ASN1_GENERALIZEDTIME)
+        tmps->length = BIO_snprintf(p, len, "%04d%02d%02d%02d%02d%02dZ",
+                                    ts->tm_year + 1900, ts->tm_mon + 1,
+                                    ts->tm_mday, ts->tm_hour, ts->tm_min,
+                                    ts->tm_sec);
+    else
+        tmps->length = BIO_snprintf(p, len, "%02d%02d%02d%02d%02d%02dZ",
+                                    ts->tm_year % 100, ts->tm_mon + 1,
+                                    ts->tm_mday, ts->tm_hour, ts->tm_min,
+                                    ts->tm_sec);
+
+#ifdef CHARSET_EBCDIC
+    ebcdic2ascii(tmps->data, tmps->data, tmps->length);
+#endif
+    return tmps;
+ err:
+    if (tmps != s)
+        ASN1_STRING_free(tmps);
+    return NULL;
+}
+
 ASN1_TIME *ASN1_TIME_set(ASN1_TIME *s, time_t t)
 {
     return ASN1_TIME_adj(s, t, 0, 0);
@@ -263,16 +329,14 @@ ASN1_TIME *ASN1_TIME_adj(ASN1_TIME *s, time_t t,
 
     ts = OPENSSL_gmtime(&t, &data);
     if (ts == NULL) {
-        ASN1err(ASN1_F_ASN1_TIME_ADJ, ASN1_R_ERROR_GETTING_TIME);
+        ERR_raise(ERR_LIB_ASN1, ASN1_R_ERROR_GETTING_TIME);
         return NULL;
     }
     if (offset_day || offset_sec) {
         if (!OPENSSL_gmtime_adj(ts, offset_day, offset_sec))
             return NULL;
     }
-    if ((ts->tm_year >= 50) && (ts->tm_year < 150))
-        return ASN1_UTCTIME_adj(s, t, offset_day, offset_sec);
-    return ASN1_GENERALIZEDTIME_adj(s, t, offset_day, offset_sec);
+    return asn1_time_from_tm(s, ts, V_ASN1_UNDEF);
 }
 
 int ASN1_TIME_check(const ASN1_TIME *t)
@@ -289,70 +353,28 @@ ASN1_GENERALIZEDTIME *ASN1_TIME_to_generalizedtime(const ASN1_TIME *t,
                                                    ASN1_GENERALIZEDTIME **out)
 {
     ASN1_GENERALIZEDTIME *ret = NULL;
-    char *str;
+    struct tm tm;
 
-    if (!ASN1_TIME_check(t))
+    if (!ASN1_TIME_to_tm(t, &tm))
         return NULL;
 
-    if (out == NULL || *out == NULL) {
-        if ((ret = ASN1_GENERALIZEDTIME_new()) == NULL)
-            goto err;
-    } else
+    if (out != NULL)
         ret = *out;
 
-    /* If already GeneralizedTime just copy across */
-    if (t->type == V_ASN1_GENERALIZEDTIME) {
-        if (!ASN1_STRING_set(ret, t->data, t->length))
-            goto err;
-        goto done;
-    }
-
-    /*
-     * Grow the string by two bytes.
-     * The actual allocation is t->length + 3 to include a terminator byte.
-     */
-    if (!ASN1_STRING_set(ret, NULL, t->length + 2))
-        goto err;
-    str = (char *)ret->data;
-    /* Work out the century and prepend */
-    memcpy(str, t->data[0] >= '5' ? "19" : "20", 2);
-    /*
-     * t->length + 1 is the size of the data and the allocated buffer has
-     * this much space after the first two characters.
-     */
-    OPENSSL_strlcpy(str + 2, (const char *)t->data, t->length + 1);
+    ret = asn1_time_from_tm(ret, &tm, V_ASN1_GENERALIZEDTIME);
 
- done:
-   if (out != NULL && *out == NULL)
-       *out = ret;
-   return ret;
+    if (out != NULL && ret != NULL)
+        *out = ret;
 
- err:
-    if (out == NULL || *out != ret)
-        ASN1_GENERALIZEDTIME_free(ret);
-    return NULL;
+    return ret;
 }
 
 int ASN1_TIME_set_string(ASN1_TIME *s, const char *str)
 {
-    ASN1_TIME t;
-
-    t.length = strlen(str);
-    t.data = (unsigned char *)str;
-    t.flags = 0;
-
-    t.type = V_ASN1_UTCTIME;
-
-    if (!ASN1_TIME_check(&t)) {
-        t.type = V_ASN1_GENERALIZEDTIME;
-        if (!ASN1_TIME_check(&t))
-            return 0;
-    }
-
-    if (s && !ASN1_STRING_copy((ASN1_STRING *)s, (ASN1_STRING *)&t))
-        return 0;
-
-    return 1;
+    /* Try UTC, if that fails, try GENERALIZED */
+    if (ASN1_UTCTIME_set_string(s, str))
+        return 1;
+    return ASN1_GENERALIZEDTIME_set_string(s, str);
 }
 
 int ASN1_TIME_set_string_X509(ASN1_TIME *s, const char *str)
@@ -390,7 +412,7 @@ int ASN1_TIME_set_string_X509(ASN1_TIME *s, const char *str)
     if (s != NULL && t.type == V_ASN1_GENERALIZEDTIME) {
         if (!asn1_time_to_tm(&tm, &t))
             goto out;
-        if (tm.tm_year >= 50 && tm.tm_year < 150) {
+        if (is_utc(tm.tm_year)) {
             t.length -= 2;
             /*
              * it's OK to let original t.data go since that's assigned
@@ -421,7 +443,7 @@ int ASN1_TIME_to_tm(const ASN1_TIME *s, struct tm *tm)
 
         time(&now_t);
         memset(tm, 0, sizeof(*tm));
-        if (OPENSSL_gmtime(&now_t, tm))
+        if (OPENSSL_gmtime(&now_t, tm) != NULL)
             return 1;
         return 0;
     }
@@ -441,12 +463,101 @@ int ASN1_TIME_diff(int *pday, int *psec,
     return OPENSSL_gmtime_diff(pday, psec, &tm_from, &tm_to);
 }
 
+static const char _asn1_mon[12][4] = {
+    "Jan", "Feb", "Mar", "Apr", "May", "Jun",
+    "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
+};
+
+/* returns 1 on success, 0 on BIO write error or parse failure */
 int ASN1_TIME_print(BIO *bp, const ASN1_TIME *tm)
 {
-    if (tm->type == V_ASN1_UTCTIME)
-        return ASN1_UTCTIME_print(bp, tm);
-    if (tm->type == V_ASN1_GENERALIZEDTIME)
-        return ASN1_GENERALIZEDTIME_print(bp, tm);
-    BIO_write(bp, "Bad time value", 14);
-    return (0);
+    return asn1_time_print_ex(bp, tm) > 0;
+}
+
+/* returns 0 on BIO write error, else -1 in case of parse failure, else 1 */
+int asn1_time_print_ex(BIO *bp, const ASN1_TIME *tm)
+{
+    char *v;
+    int gmt = 0, l;
+    struct tm stm;
+    const char upper_z = 0x5A, period = 0x2E;
+
+    /* asn1_time_to_tm will check the time type */
+    if (!asn1_time_to_tm(&stm, tm))
+        return BIO_write(bp, "Bad time value", 14) ? -1 : 0;
+
+    l = tm->length;
+    v = (char *)tm->data;
+    if (v[l - 1] == upper_z)
+        gmt = 1;
+
+    if (tm->type == V_ASN1_GENERALIZEDTIME) {
+        char *f = NULL;
+        int f_len = 0;
+
+        /*
+         * Try to parse fractional seconds. '14' is the place of
+         * 'fraction point' in a GeneralizedTime string.
+         */
+        if (tm->length > 15 && v[14] == period) {
+            f = &v[14];
+            f_len = 1;
+            while (14 + f_len < l && ascii_isdigit(f[f_len]))
+                ++f_len;
+        }
+
+        return BIO_printf(bp, "%s %2d %02d:%02d:%02d%.*s %d%s",
+                          _asn1_mon[stm.tm_mon], stm.tm_mday, stm.tm_hour,
+                          stm.tm_min, stm.tm_sec, f_len, f, stm.tm_year + 1900,
+                          (gmt ? " GMT" : "")) > 0;
+    } else {
+        return BIO_printf(bp, "%s %2d %02d:%02d:%02d %d%s",
+                          _asn1_mon[stm.tm_mon], stm.tm_mday, stm.tm_hour,
+                          stm.tm_min, stm.tm_sec, stm.tm_year + 1900,
+                          (gmt ? " GMT" : "")) > 0;
+    }
+}
+
+int ASN1_TIME_cmp_time_t(const ASN1_TIME *s, time_t t)
+{
+    struct tm stm, ttm;
+    int day, sec;
+
+    if (!ASN1_TIME_to_tm(s, &stm))
+        return -2;
+
+    if (!OPENSSL_gmtime(&t, &ttm))
+        return -2;
+
+    if (!OPENSSL_gmtime_diff(&day, &sec, &ttm, &stm))
+        return -2;
+
+    if (day > 0 || sec > 0)
+        return 1;
+    if (day < 0 || sec < 0)
+        return -1;
+    return 0;
+}
+
+int ASN1_TIME_normalize(ASN1_TIME *t)
+{
+    struct tm tm;
+
+    if (!ASN1_TIME_to_tm(t, &tm))
+        return 0;
+
+    return asn1_time_from_tm(t, &tm, V_ASN1_UNDEF) != NULL;
+}
+
+int ASN1_TIME_compare(const ASN1_TIME *a, const ASN1_TIME *b)
+{
+    int day, sec;
+
+    if (!ASN1_TIME_diff(&day, &sec, b, a))
+        return -2;
+    if (day > 0 || sec > 0)
+        return 1;
+    if (day < 0 || sec < 0)
+        return -1;
+    return 0;
 }