Undo commit d420ac2
[openssl.git] / crypto / asn1 / a_gentm.c
index db4510fbe63a143297094f6734d8d52f5ee1b72d..2c5fb1c3d2f972b80dcf8aa748f588bd7e40ff33 100644 (file)
-/* crypto/asn1/a_gentm.c */
-/* Copyright (C) 1995-1998 Eric Young (eay@cryptsoft.com)
- * All rights reserved.
- *
- * This package is an SSL implementation written
- * by Eric Young (eay@cryptsoft.com).
- * The implementation was written so as to conform with Netscapes SSL.
- *
- * This library is free for commercial and non-commercial use as long as
- * the following conditions are aheared to.  The following conditions
- * apply to all code found in this distribution, be it the RC4, RSA,
- * lhash, DES, etc., code; not just the SSL code.  The SSL documentation
- * included with this distribution is covered by the same copyright terms
- * except that the holder is Tim Hudson (tjh@cryptsoft.com).
- *
- * Copyright remains Eric Young's, and as such any Copyright notices in
- * the code are not to be removed.
- * If this package is used in a product, Eric Young should be given attribution
- * as the author of the parts of the library used.
- * This can be in the form of a textual message at program startup or
- * in documentation (online or textual) provided with the package.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the copyright
- *    notice, this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright
- *    notice, this list of conditions and the following disclaimer in the
- *    documentation and/or other materials provided with the distribution.
- * 3. All advertising materials mentioning features or use of this software
- *    must display the following acknowledgement:
- *    "This product includes cryptographic software written by
- *     Eric Young (eay@cryptsoft.com)"
- *    The word 'cryptographic' can be left out if the rouines from the library
- *    being used are not cryptographic related :-).
- * 4. If you include any Windows specific code (or a derivative thereof) from
- *    the apps directory (application code) you must include an acknowledgement:
- *    "This product includes software written by Tim Hudson (tjh@cryptsoft.com)"
- *
- * THIS SOFTWARE IS PROVIDED BY ERIC YOUNG ``AS IS'' AND
- * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
- * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
- * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
- * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
- * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
- * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
- * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
- * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
- * SUCH DAMAGE.
+/*
+ * Copyright 1995-2016 The OpenSSL Project Authors. All Rights Reserved.
  *
- * The licence and distribution terms for any publically available version or
- * derivative of this code cannot be changed.  i.e. this code cannot simply be
- * copied and put under another distribution licence
- * [including the GNU Public Licence.]
+ * Licensed under the OpenSSL license (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
  */
 
 /*
- * GENERALIZEDTIME implementation, written by Steve Henson. Based on UTCTIME
+ * GENERALIZEDTIME implementation. Based on UTCTIME
  */
 
 #include <stdio.h>
 #include <time.h>
-#include "cryptlib.h"
+#include "internal/cryptlib.h"
 #include <openssl/asn1.h>
 #include "asn1_locl.h"
 
-#if 0
-
-int i2d_ASN1_GENERALIZEDTIME(ASN1_GENERALIZEDTIME *a, unsigned char **pp)
-{
-# ifdef CHARSET_EBCDIC
-    /* KLUDGE! We convert to ascii before writing DER */
-    int len;
-    char tmp[24];
-    ASN1_STRING tmpstr = *(ASN1_STRING *)a;
-
-    len = tmpstr.length;
-    ebcdic2ascii(tmp, tmpstr.data, (len >= sizeof tmp) ? sizeof tmp : len);
-    tmpstr.data = tmp;
-
-    a = (ASN1_GENERALIZEDTIME *)&tmpstr;
-# endif
-    return (i2d_ASN1_bytes((ASN1_STRING *)a, pp,
-                           V_ASN1_GENERALIZEDTIME, V_ASN1_UNIVERSAL));
-}
-
-ASN1_GENERALIZEDTIME *d2i_ASN1_GENERALIZEDTIME(ASN1_GENERALIZEDTIME **a,
-                                               unsigned char **pp,
-                                               long length)
-{
-    ASN1_GENERALIZEDTIME *ret = NULL;
-
-    ret =
-        (ASN1_GENERALIZEDTIME *)d2i_ASN1_bytes((ASN1_STRING **)a, pp, length,
-                                               V_ASN1_GENERALIZEDTIME,
-                                               V_ASN1_UNIVERSAL);
-    if (ret == NULL) {
-        ASN1err(ASN1_F_D2I_ASN1_GENERALIZEDTIME, ERR_R_NESTED_ASN1_ERROR);
-        return (NULL);
-    }
-# ifdef CHARSET_EBCDIC
-    ascii2ebcdic(ret->data, ret->data, ret->length);
-# endif
-    if (!ASN1_GENERALIZEDTIME_check(ret)) {
-        ASN1err(ASN1_F_D2I_ASN1_GENERALIZEDTIME, ASN1_R_INVALID_TIME_FORMAT);
-        goto err;
-    }
-
-    return (ret);
- err:
-    if ((ret != NULL) && ((a == NULL) || (*a != ret)))
-        M_ASN1_GENERALIZEDTIME_free(ret);
-    return (NULL);
-}
-
-#endif
-
 int asn1_generalizedtime_to_tm(struct tm *tm, const ASN1_GENERALIZEDTIME *d)
 {
     static const int min[9] = { 0, 0, 1, 1, 0, 0, 0, 0, 0 };
     static const int max[9] = { 99, 99, 12, 31, 23, 59, 59, 12, 59 };
     char *a;
-    int n, i, l, o;
+    int n, i, l, o, min_l = 13, strict = 0;
 
     if (d->type != V_ASN1_GENERALIZEDTIME)
         return (0);
@@ -134,10 +34,26 @@ int asn1_generalizedtime_to_tm(struct tm *tm, const ASN1_GENERALIZEDTIME *d)
      * as YYYY. This stuff treats everything as a two digit field so make
      * first two fields 00 to 99
      */
-    if (l < 13)
+
+    /*
+     * ASN1_STRING_FLAG_X509_TIME is used to enforce RFC 5280
+     * time string format, in which:
+     *
+     * 1. "seconds" is a 'MUST'
+     * 2. "Zulu" timezone is a 'MUST'
+     * 3. "+|-" is not allowed to indicate a time zone
+     * 4. fractional seconds are not allowed in GeneralizedTime
+     */
+
+    if (d->flags & ASN1_STRING_FLAG_X509_TIME) {
+        min_l = 15;
+        strict = 1;
+    }
+
+    if (l < min_l)
         goto err;
     for (i = 0; i < 7; i++) {
-        if ((i == 6) && ((a[o] == 'Z') || (a[o] == '+') || (a[o] == '-'))) {
+        if (!strict && (i == 6) && ((a[o] == 'Z') || (a[o] == '+') || (a[o] == '-'))) {
             i++;
             if (tm)
                 tm->tm_sec = 0;
@@ -146,13 +62,15 @@ int asn1_generalizedtime_to_tm(struct tm *tm, const ASN1_GENERALIZEDTIME *d)
         if ((a[o] < '0') || (a[o] > '9'))
             goto err;
         n = a[o] - '0';
-        if (++o > l)
+        /* incomplete 2-digital number */
+        if (++o == l)
             goto err;
 
         if ((a[o] < '0') || (a[o] > '9'))
             goto err;
         n = (n * 10) + a[o] - '0';
-        if (++o > l)
+        /* no more bytes to read, but we haven't seen time-zone yet */
+        if (++o == l)
             goto err;
 
         if ((n < min[i]) || (n > max[i]))
@@ -188,22 +106,39 @@ int asn1_generalizedtime_to_tm(struct tm *tm, const ASN1_GENERALIZEDTIME *d)
      * digits.
      */
     if (a[o] == '.') {
-        if (++o > l)
+        if (strict)
+            /* RFC 5280 forbids fractional seconds */
+            goto err;
+        if (++o == l)
             goto err;
         i = o;
-        while ((a[o] >= '0') && (a[o] <= '9') && (o <= l))
+        while ((o < l) && (a[o] >= '0') && (a[o] <= '9'))
             o++;
         /* Must have at least one digit after decimal point */
         if (i == o)
             goto err;
+        /* no more bytes to read, but we haven't seen time-zone yet */
+        if (o == l)
+            goto err;
     }
 
-    if (a[o] == 'Z')
+    /*
+     * 'o' will never point to '\0' at this point, the only chance
+     * 'o' can point th '\0' is either the subsequent if or the first
+     * else if is true.
+     */
+    if (a[o] == 'Z') {
         o++;
-    else if ((a[o] == '+') || (a[o] == '-')) {
-        int offsign = a[o] == '-' ? -1 : 1, offset = 0;
+    } else if (!strict && ((a[o] == '+') || (a[o] == '-'))) {
+        int offsign = a[o] == '-' ? 1 : -1, offset = 0;
         o++;
-        if (o + 4 > l)
+        /*
+         * if not equal, no need to do subsequent checks
+         * since the following for-loop will add 'o' by 4
+         * and the final return statement will check if 'l'
+         * and 'o' are equal.
+         */
+        if (o + 4 != l)
             goto err;
         for (i = 7; i < 9; i++) {
             if ((a[o] < '0') || (a[o] > '9'))
@@ -246,10 +181,11 @@ int ASN1_GENERALIZEDTIME_set_string(ASN1_GENERALIZEDTIME *s, const char *str)
     t.type = V_ASN1_GENERALIZEDTIME;
     t.length = strlen(str);
     t.data = (unsigned char *)str;
+    t.flags = 0;
+
     if (ASN1_GENERALIZEDTIME_check(&t)) {
         if (s != NULL) {
-            if (!ASN1_STRING_set((ASN1_STRING *)s,
-                                 (unsigned char *)str, t.length))
+            if (!ASN1_STRING_set((ASN1_STRING *)s, str, t.length))
                 return 0;
             s->type = V_ASN1_GENERALIZEDTIME;
         }
@@ -272,40 +208,103 @@ ASN1_GENERALIZEDTIME *ASN1_GENERALIZEDTIME_adj(ASN1_GENERALIZEDTIME *s,
     struct tm *ts;
     struct tm data;
     size_t len = 20;
+    ASN1_GENERALIZEDTIME *tmps = NULL;
 
     if (s == NULL)
-        s = M_ASN1_GENERALIZEDTIME_new();
-    if (s == NULL)
-        return (NULL);
+        tmps = ASN1_GENERALIZEDTIME_new();
+    else
+        tmps = s;
+    if (tmps == NULL)
+        return NULL;
 
     ts = OPENSSL_gmtime(&t, &data);
     if (ts == NULL)
-        return (NULL);
+        goto err;
 
     if (offset_day || offset_sec) {
         if (!OPENSSL_gmtime_adj(ts, offset_day, offset_sec))
-            return NULL;
+            goto err;
     }
 
-    p = (char *)s->data;
-    if ((p == NULL) || ((size_t)s->length < len)) {
+    p = (char *)tmps->data;
+    if ((p == NULL) || ((size_t)tmps->length < len)) {
         p = OPENSSL_malloc(len);
         if (p == NULL) {
             ASN1err(ASN1_F_ASN1_GENERALIZEDTIME_ADJ, ERR_R_MALLOC_FAILURE);
-            return (NULL);
+            goto err;
         }
-        if (s->data != NULL)
-            OPENSSL_free(s->data);
-        s->data = (unsigned char *)p;
+        OPENSSL_free(tmps->data);
+        tmps->data = (unsigned char *)p;
     }
 
-    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);
-    s->length = strlen(p);
-    s->type = V_ASN1_GENERALIZEDTIME;
+    sprintf(p, "%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);
+    tmps->length = strlen(p);
+    tmps->type = V_ASN1_GENERALIZEDTIME;
 #ifdef CHARSET_EBCDIC_not
-    ebcdic2ascii(s->data, s->data, s->length);
+    ebcdic2ascii(tmps->data, tmps->data, tmps->length);
 #endif
-    return (s);
+    return tmps;
+ err:
+    if (s == NULL)
+        ASN1_GENERALIZEDTIME_free(tmps);
+    return NULL;
+}
+
+const char *_asn1_mon[12] = {
+    "Jan", "Feb", "Mar", "Apr", "May", "Jun",
+    "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
+};
+
+int ASN1_GENERALIZEDTIME_print(BIO *bp, const ASN1_GENERALIZEDTIME *tm)
+{
+    char *v;
+    int gmt = 0;
+    int i;
+    int y = 0, M = 0, d = 0, h = 0, m = 0, s = 0;
+    char *f = NULL;
+    int f_len = 0;
+
+    i = tm->length;
+    v = (char *)tm->data;
+
+    if (i < 12)
+        goto err;
+    if (v[i - 1] == 'Z')
+        gmt = 1;
+    for (i = 0; i < 12; i++)
+        if ((v[i] > '9') || (v[i] < '0'))
+            goto err;
+    y = (v[0] - '0') * 1000 + (v[1] - '0') * 100
+        + (v[2] - '0') * 10 + (v[3] - '0');
+    M = (v[4] - '0') * 10 + (v[5] - '0');
+    if ((M > 12) || (M < 1))
+        goto err;
+    d = (v[6] - '0') * 10 + (v[7] - '0');
+    h = (v[8] - '0') * 10 + (v[9] - '0');
+    m = (v[10] - '0') * 10 + (v[11] - '0');
+    if (tm->length >= 14 &&
+        (v[12] >= '0') && (v[12] <= '9') &&
+        (v[13] >= '0') && (v[13] <= '9')) {
+        s = (v[12] - '0') * 10 + (v[13] - '0');
+        /* Check for fractions of seconds. */
+        if (tm->length >= 15 && v[14] == '.') {
+            int l = tm->length;
+            f = &v[14];         /* The decimal point. */
+            f_len = 1;
+            while (14 + f_len < l && f[f_len] >= '0' && f[f_len] <= '9')
+                ++f_len;
+        }
+    }
+
+    if (BIO_printf(bp, "%s %2d %02d:%02d:%02d%.*s %d%s",
+                   _asn1_mon[M - 1], d, h, m, s, f_len, f, y,
+                   (gmt) ? " GMT" : "") <= 0)
+        return (0);
+    else
+        return (1);
+ err:
+    BIO_write(bp, "Bad time value", 14);
+    return (0);
 }