compute ASN.1 to UTC time without time functions
[strongswan.git] / src / libstrongswan / asn1 / asn1.c
index 6a5ba30..9657c88 100644 (file)
  * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
  * or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  * for more details.
- *
- * $Id$
  */
 
 #include <stdio.h>
 #include <string.h>
 #include <time.h>
+#include <pthread.h>
 
-#include <library.h>
+#include <utils.h>
 #include <debug.h>
 
 #include "oid.h"
@@ -209,9 +208,13 @@ int asn1_known_oid(chunk_t object)
                else
                {
                        if (oid_names[oid].next)
+                       {
                                oid = oid_names[oid].next;
+                       }
                        else
+                       {
                                return OID_UNKNOWN;
+                       }
                }
        }
        return -1;
@@ -220,6 +223,38 @@ int asn1_known_oid(chunk_t object)
 /*
  * Defined in header.
  */
+chunk_t asn1_build_known_oid(int n)
+{
+       chunk_t oid;
+       int i;
+       
+       if (n < 0 || n >= OID_MAX)
+       {
+               return chunk_empty;
+       }
+       
+       i = oid_names[n].level + 1;
+       oid = chunk_alloc(2 + i);
+       oid.ptr[0] = ASN1_OID;
+       oid.ptr[1] = i;
+       
+       do
+       {
+               if (oid_names[n].level >= i)
+               {
+                       n--;
+                       continue;
+               }
+               oid.ptr[--i + 2] = oid_names[n--].octet;
+       }
+       while (i > 0);
+       
+       return oid;
+}
+
+/*
+ * Defined in header.
+ */
 u_int asn1_length(chunk_t *blob)
 {
        u_char n;
@@ -264,13 +299,20 @@ u_int asn1_length(chunk_t *blob)
        return len;
 }
 
+#define TIME_MAX       0x7fffffff
+
+static const int days[] = { 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334 };
+static const int tm_leap_1970 = 477;
+
 /**
  * Converts ASN.1 UTCTIME or GENERALIZEDTIME into calender time
  */
 time_t asn1_to_time(const chunk_t *utctime, asn1_t type)
 {
-       struct tm t;
-       time_t tz_offset;
+       int tm_year, tm_mon, tm_day, tm_days, tm_hour, tm_min, tm_sec;
+       int tm_leap_4, tm_leap_100, tm_leap_400, tm_leap;
+       int tz_hour, tz_min, tz_offset;
+       time_t tm_secs;
        u_char *eot = NULL;
        
        if ((eot = memchr(utctime->ptr, 'Z', utctime->len)) != NULL)
@@ -279,15 +321,11 @@ time_t asn1_to_time(const chunk_t *utctime, asn1_t type)
        }
        else if ((eot = memchr(utctime->ptr, '+', utctime->len)) != NULL)
        {
-               int tz_hour, tz_min;
-       
                sscanf(eot+1, "%2d%2d", &tz_hour, &tz_min);
                tz_offset = 3600*tz_hour + 60*tz_min;  /* positive time zone offset */
        }
        else if ((eot = memchr(utctime->ptr, '-', utctime->len)) != NULL)
        {
-               int tz_hour, tz_min;
-       
                sscanf(eot+1, "%2d%2d", &tz_hour, &tz_min);
                tz_offset = -3600*tz_hour - 60*tz_min;  /* negative time zone offset */
        }
@@ -296,47 +334,63 @@ time_t asn1_to_time(const chunk_t *utctime, asn1_t type)
                return 0; /* error in time format */
        }
        
+       /* parse ASN.1 time string */
        {
-       const char* format = (type == ASN1_UTCTIME)? "%2d%2d%2d%2d%2d":
-                       "%4d%2d%2d%2d%2d";
+               const char* format = (type == ASN1_UTCTIME)? "%2d%2d%2d%2d%2d":
+                                                                                                        "%4d%2d%2d%2d%2d";
        
-       sscanf(utctime->ptr, format, &t.tm_year, &t.tm_mon, &t.tm_mday,
-                  &t.tm_hour, &t.tm_min);
+               sscanf(utctime->ptr, format, &tm_year, &tm_mon, &tm_day, &tm_hour, &tm_min);
        }
        
        /* is there a seconds field? */
        if ((eot - utctime->ptr) == ((type == ASN1_UTCTIME)?12:14))
        {
-               sscanf(eot-2, "%2d", &t.tm_sec);
+               sscanf(eot-2, "%2d", &tm_sec);
        }
        else
        {
-               t.tm_sec = 0;
+               tm_sec = 0;
        }
        
-       /* representation of year */
-       if (t.tm_year >= 1900)
+       /* representation of two-digit years */
+       if (type == ASN1_UTCTIME)
        {
-               t.tm_year -= 1900;
+               tm_year += (tm_year < 50) ? 2000 : 1900;
        }
-       else if (t.tm_year >= 100)
+       
+       /* prevent large 32 bit integer overflows */
+       if (sizeof(time_t) == 8 && tm_year > 1938)
        {
-               return 0;
+               return TIME_MAX;
        }
-       else if (t.tm_year < 50)
+
+       /* representation of months as 0..11*/
+       if (tm_mon > 12)
        {
-               t.tm_year += 100;
+               return 0; /* error in time format */
        }
+       tm_mon--;
        
-       /* representation of month 0..11*/
-       t.tm_mon--;
-       
-       /* set daylight saving time to off */
-       t.tm_isdst = 0;
-       
-       /* compensate timezone */
-       
-       return mktime(&t) - timezone - tz_offset;
+       /* representation of days as 0..30 */
+       tm_day--;
+
+       /* number of leap years between last year and 1970? */
+       tm_leap_4 = (tm_year - 1) / 4;
+       tm_leap_100 = tm_leap_4 / 25;
+       tm_leap_400 = tm_leap_100 / 4;
+       tm_leap = tm_leap_4 - tm_leap_100 + tm_leap_400 - tm_leap_1970;
+
+       /* if date later then February, is the current year a leap year? */
+       if ((tm_mon > 1 && 4*(tm_leap_4 + 1) == tm_year) &&
+               (100*(tm_leap_100 + 1) != tm_year || 400*(tm_leap_400 + 1) == tm_year))
+       {
+               tm_leap++;
+       }       
+       tm_days = 365 * (tm_year - 1970) + days[tm_mon] + tm_day + tm_leap;
+       tm_secs = 60 * (60 * (24 * tm_days + tm_hour) + tm_min) + tm_sec - tz_offset;
+
+       /* has a 32 bit overflow occurred? */
+       return (tm_secs < 0) ? TIME_MAX : tm_secs;
 }
 
 /**
@@ -348,8 +402,9 @@ chunk_t asn1_from_time(const time_t *time, asn1_t type)
        const char *format;
        char buf[BUF_LEN];
        chunk_t formatted_time;
-       struct tm *t = gmtime(time);
+       struct tm t;
        
+       gmtime_r(time, &t);
        if (type == ASN1_GENERALIZEDTIME)
        {
                format = "%04d%02d%02d%02d%02d%02dZ";
@@ -358,10 +413,10 @@ chunk_t asn1_from_time(const time_t *time, asn1_t type)
        else /* ASN1_UTCTIME */
        {
                format = "%02d%02d%02d%02d%02d%02dZ";
-               offset = (t->tm_year < 100)? 0 : -100;
+               offset = (t.tm_year < 100)? 0 : -100;
        }
-       snprintf(buf, BUF_LEN, format, t->tm_year + offset, 
-                        t->tm_mon + 1, t->tm_mday, t->tm_hour, t->tm_min, t->tm_sec);
+       snprintf(buf, BUF_LEN, format, t.tm_year + offset, 
+                        t.tm_mon + 1, t.tm_mday, t.tm_hour, t.tm_min, t.tm_sec);
        formatted_time.ptr = buf;
        formatted_time.len = strlen(buf);
        return asn1_simple_object(type, formatted_time);
@@ -396,7 +451,7 @@ void asn1_debug_simple_object(chunk_t object, asn1_t type, bool private)
                        {
                                time_t time = asn1_to_time(&object, type);
 
-                               DBG2("  '%T'", &time);
+                               DBG2("  '%T'", &time, TRUE);
                        }
                        return;
                default:
@@ -451,13 +506,6 @@ bool asn1_parse_simple_object(chunk_t *object, asn1_t type, u_int level, const c
  * ASN.1 definition of an algorithmIdentifier
  */
 static const asn1Object_t algorithmIdentifierObjects[] = {
-       { 0, "algorithmIdentifier",     ASN1_SEQUENCE,  ASN1_NONE }, /* 0 */
-       { 1,   "algorithm",                     ASN1_OID,               ASN1_BODY }, /* 1 */
-       { 1,   "parameters",            ASN1_EOC,               ASN1_RAW  }, /* 2 */
-       { 0, "exit",                            ASN1_EOC,               ASN1_EXIT }
-};
-/* parameters are optional in case of ecdsa-with-SHA1 as algorithm (RFC 3279) */
-static const asn1Object_t algorithmIdentifierObjectsOptional[] = {
        { 0, "algorithmIdentifier",     ASN1_SEQUENCE,  ASN1_NONE         }, /* 0 */
        { 1,   "algorithm",                     ASN1_OID,               ASN1_BODY         }, /* 1 */
        { 1,   "parameters",            ASN1_EOC,               ASN1_RAW|ASN1_OPT }, /* 2 */
@@ -476,14 +524,8 @@ int asn1_parse_algorithmIdentifier(chunk_t blob, int level0, chunk_t *parameters
        chunk_t object;
        int objectID;
        int alg = OID_UNKNOWN;
-       const asn1Object_t *objects = algorithmIdentifierObjectsOptional;
-       
-       if (parameters != NULL)
-       {
-               objects = algorithmIdentifierObjects;
-       }
        
-       parser = asn1_parser_create(objects, blob);
+       parser = asn1_parser_create(algorithmIdentifierObjects, blob);
        parser->set_top_level(parser, level0);
        
        while (parser->iterate(parser, &objectID, &object))