summaryrefslogtreecommitdiff
path: root/lib/libssl
diff options
context:
space:
mode:
authorBob Beck <beck@cvs.openbsd.org>2015-10-02 15:04:46 +0000
committerBob Beck <beck@cvs.openbsd.org>2015-10-02 15:04:46 +0000
commited5fb6ced62c46121d14815fd741e4d529fd789e (patch)
tree5f2612e6cbe2f32d9a691195745bcfd6567fd6bf /lib/libssl
parent2f8502400f87712285194eb99f5f99f3b6a02a29 (diff)
Flense the greasy black guts of unreadble string parsing code out of three areas
in asn1 and x509 code, all dealing with an ASN1_TIME. This brings the parsing together in one function that converts into a struct tm. While we are at it this also brings us into conformance with RFC 5280 for times allowed in an X509 cert, as OpenSSL is very liberal with what it allows. input and fixes from deraadt@ jsing@ guethther@ and others. ok krw@, guenther@, jsing@
Diffstat (limited to 'lib/libssl')
-rw-r--r--lib/libssl/src/crypto/asn1/a_gentm.c106
-rw-r--r--lib/libssl/src/crypto/asn1/a_time.c25
-rw-r--r--lib/libssl/src/crypto/asn1/a_time_tm.c257
-rw-r--r--lib/libssl/src/crypto/asn1/a_utctm.c80
-rw-r--r--lib/libssl/src/crypto/asn1/asn1_locl.h6
-rw-r--r--lib/libssl/src/crypto/x509/x509_lcl.h1
-rw-r--r--lib/libssl/src/crypto/x509/x509_vfy.c128
7 files changed, 332 insertions, 271 deletions
diff --git a/lib/libssl/src/crypto/asn1/a_gentm.c b/lib/libssl/src/crypto/asn1/a_gentm.c
index 4cee40437cc..594eb630580 100644
--- a/lib/libssl/src/crypto/asn1/a_gentm.c
+++ b/lib/libssl/src/crypto/asn1/a_gentm.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: a_gentm.c,v 1.24 2015/09/30 18:04:02 jsing Exp $ */
+/* $OpenBSD: a_gentm.c,v 1.25 2015/10/02 15:04:45 beck Exp $ */
/* Copyright (C) 1995-1998 Eric Young (eay@cryptsoft.com)
* All rights reserved.
*
@@ -66,86 +66,14 @@
#include <openssl/err.h>
#include "o_time.h"
+#include "asn1_locl.h"
int
ASN1_GENERALIZEDTIME_check(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;
-
if (d->type != V_ASN1_GENERALIZEDTIME)
return (0);
- l = d->length;
- a = (char *)d->data;
- o = 0;
- /* GENERALIZEDTIME is similar to UTCTIME except the year is
- * represented as YYYY. This stuff treats everything as a two digit
- * field so make first two fields 00 to 99
- */
- if (l < 13)
- goto err;
- for (i = 0; i < 7; i++) {
- if ((i == 6) && ((a[o] == 'Z') ||
- (a[o] == '+') || (a[o] == '-'))) {
- i++;
- break;
- }
- if ((a[o] < '0') || (a[o] > '9'))
- goto err;
- n= a[o]-'0';
- if (++o > l)
- goto err;
-
- if ((a[o] < '0') || (a[o] > '9'))
- goto err;
- n = (n * 10)+ a[o] - '0';
- if (++o > l)
- goto err;
-
- if ((n < min[i]) || (n > max[i]))
- goto err;
- }
- /* Optional fractional seconds: decimal point followed by one
- * or more digits.
- */
- if (a[o] == '.') {
- if (++o > l)
- goto err;
- i = o;
- while ((a[o] >= '0') && (a[o] <= '9') && (o <= l))
- o++;
- /* Must have at least one digit after decimal point */
- if (i == o)
- goto err;
- }
-
- if (a[o] == 'Z')
- o++;
- else if ((a[o] == '+') || (a[o] == '-')) {
- o++;
- if (o + 4 > l)
- goto err;
- for (i = 7; i < 9; i++) {
- if ((a[o] < '0') || (a[o] > '9'))
- goto err;
- n = a[o] - '0';
- o++;
- if ((a[o] < '0') || (a[o] > '9'))
- goto err;
- n = (n * 10) + a[o] - '0';
- if ((n < min[i]) || (n > max[i]))
- goto err;
- o++;
- }
- } else {
- /* Missing time zone information. */
- goto err;
- }
- return (o == l);
-err:
- return (0);
+ return (d->type == asn1_time_parse(d->data, d->length, NULL, d->type));
}
int
@@ -179,34 +107,26 @@ ASN1_GENERALIZEDTIME_adj_internal(ASN1_GENERALIZEDTIME *s, time_t t,
int offset_day, long offset_sec)
{
char *p;
- struct tm *ts;
+ struct tm *tm;
struct tm data;
- size_t len = 20;
- ts = gmtime_r(&t, &data);
- if (ts == NULL)
+ tm = gmtime_r(&t, &data);
+ if (tm == NULL)
return (NULL);
if (offset_day || offset_sec) {
- if (!OPENSSL_gmtime_adj(ts, offset_day, offset_sec))
+ if (!OPENSSL_gmtime_adj(tm, offset_day, offset_sec))
return NULL;
}
- p = (char *)s->data;
- if ((p == NULL) || ((size_t)s->length < len)) {
- p = malloc(len);
- if (p == NULL) {
- ASN1err(ASN1_F_ASN1_GENERALIZEDTIME_ADJ,
- ERR_R_MALLOC_FAILURE);
- return (NULL);
- }
- free(s->data);
- s->data = (unsigned char *)p;
+ if ((p = gentime_string_from_tm(tm)) == NULL) {
+ ASN1err(ASN1_F_ASN1_GENERALIZEDTIME_ADJ, ERR_R_MALLOC_FAILURE);
+ return (NULL);
}
-
- 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);
+ free(s->data);
+ s->data = p;
s->length = strlen(p);
+
s->type = V_ASN1_GENERALIZEDTIME;
return (s);
}
diff --git a/lib/libssl/src/crypto/asn1/a_time.c b/lib/libssl/src/crypto/asn1/a_time.c
index 25a1805640e..a6c7c8e736a 100644
--- a/lib/libssl/src/crypto/asn1/a_time.c
+++ b/lib/libssl/src/crypto/asn1/a_time.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: a_time.c,v 1.25 2015/09/30 18:04:02 jsing Exp $ */
+/* $OpenBSD: a_time.c,v 1.26 2015/10/02 15:04:45 beck Exp $ */
/* ====================================================================
* Copyright (c) 1999 The OpenSSL Project. All rights reserved.
*
@@ -68,7 +68,7 @@
#include <openssl/err.h>
#include "o_time.h"
-
+#include "asn1_locl.h"
const ASN1_ITEM ASN1_TIME_it = {
.itype = ASN1_ITYPE_MSTRING,
@@ -135,11 +135,9 @@ ASN1_TIME_adj(ASN1_TIME *s, time_t t, int offset_day, long offset_sec)
int
ASN1_TIME_check(ASN1_TIME *t)
{
- if (t->type == V_ASN1_GENERALIZEDTIME)
- return ASN1_GENERALIZEDTIME_check(t);
- else if (t->type == V_ASN1_UTCTIME)
- return ASN1_UTCTIME_check(t);
- return 0;
+ if (t->type != V_ASN1_GENERALIZEDTIME && t->type != V_ASN1_UTCTIME)
+ return 0;
+ return (t->type == asn1_time_parse(t->data, t->length, NULL, t->type));
}
/* Convert an ASN1_TIME structure to GeneralizedTime */
@@ -210,13 +208,12 @@ ASN1_TIME_set_string(ASN1_TIME *s, const char *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;
- }
+ t.type = asn1_time_parse(t.data, t.length, NULL, V_ASN1_UTCTIME);
+ if (t.type == -1)
+ t.type = asn1_time_parse(t.data, t.length, NULL,
+ V_ASN1_GENERALIZEDTIME);
+ if (t.type == -1)
+ return 0;
if (s && !ASN1_STRING_copy((ASN1_STRING *)s, (ASN1_STRING *)&t))
return 0;
diff --git a/lib/libssl/src/crypto/asn1/a_time_tm.c b/lib/libssl/src/crypto/asn1/a_time_tm.c
new file mode 100644
index 00000000000..65f75c68cc6
--- /dev/null
+++ b/lib/libssl/src/crypto/asn1/a_time_tm.c
@@ -0,0 +1,257 @@
+/* $OpenBSD: a_time_tm.c,v 1.1 2015/10/02 15:04:45 beck Exp $ */
+/*
+ * Copyright (c) 2015 Bob Beck <beck@openbsd.org>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <stdio.h>
+#include <string.h>
+#include <time.h>
+#include <ctype.h>
+#include <sys/limits.h>
+
+#include <openssl/asn1t.h>
+#include <openssl/err.h>
+
+#include "o_time.h"
+#include "asn1_locl.h"
+
+char *
+gentime_string_from_tm(struct tm *tm)
+{
+ char *ret = NULL;
+ int year;
+ year = tm->tm_year + 1900;
+ if (year < 0 || year > 9999)
+ return (NULL);
+ if (asprintf(&ret, "%04u%02u%02u%02u%02u%02uZ", year,
+ tm->tm_mon + 1, tm->tm_mday, tm->tm_hour, tm->tm_min,
+ tm->tm_sec) == -1)
+ ret = NULL;
+ return (ret);
+}
+
+char *
+utctime_string_from_tm(struct tm *tm)
+{
+ char *ret = NULL;
+ if (tm->tm_year >= 150 || tm->tm_year < 50)
+ return (NULL);
+ if (asprintf(&ret, "%02u%02u%02u%02u%02u%02uZ",
+ tm->tm_year % 100, tm->tm_mon + 1, tm->tm_mday,
+ tm->tm_hour, tm->tm_min, tm->tm_sec) == -1)
+ ret = NULL;
+ return (ret);
+}
+
+/*
+ * Parse an ASN.1 time string.
+ *
+ * mode must be:
+ * 0 if we expect to parse a time as specified in RFC 5280 from an
+ * X509 certificate.
+ * V_ASN1_UTCTIME if we wish to parse a legacy ASN1 UTC time.
+ * V_ASN1_GENERALIZEDTIME if we wish to parse a legacy ASN1
+ * Generalizd time.
+ *
+ * Returns:
+ * -1 if the string was invalid.
+ * V_ASN1_UTCTIME if the string validated as a UTC time string.
+ * V_ASN1_GENERALIZEDTIME if the string validated as a Generalized time string.
+ *
+ * Fills in *tm with the corresponding time if tm is non NULL.
+ */
+#define RFC5280 0
+#define ATOI2(ar) ((ar) += 2, ((ar)[-2] - '0') * 10 + ((ar)[-1] - '0'))
+int asn1_time_parse(const char * bytes, size_t len, struct tm *tm, int mode)
+{
+ char *p, *buf = NULL, *dot = NULL, *tz = NULL;
+ int i, offset, noseconds = 0, type = 0;
+ struct tm ltm;
+ struct tm *lt;
+ size_t tlen;
+ char tzc;
+
+ if (bytes == NULL)
+ goto err;
+
+ if (len > INT_MAX)
+ goto err;
+
+ /* Constrain the RFC5280 case within max/min valid lengths. */
+ if (mode == RFC5280 && (len > 15 || len < 13))
+ goto err;
+
+ if ((buf = strndup(bytes, len)) == NULL)
+ goto err;
+ lt = tm;
+ if (lt == NULL) {
+ time_t t = time(NULL);
+ lt = gmtime_r(&t, &ltm);
+ if (lt == NULL)
+ goto err;
+ }
+
+ /*
+ * Find position of the optional fractional seconds, and the
+ * start of the timezone, while ensuring everything else is
+ * digits.
+ */
+ for (i = 0; i < len; i++) {
+ char *t = buf + i;
+ if (isdigit((unsigned char)*t))
+ continue;
+ if (*t == '.' && dot == NULL) {
+ dot = t;
+ continue;
+ }
+ if ((*t == 'Z' || *t == '+' || *t == '-') && tz == NULL) {
+ tz = t;
+ continue;
+ }
+ goto err;
+ }
+
+ /*
+ * Timezone is required. For the non-RFC case it may be
+ * either Z or +- HHMM, but for RFC5280 it may be only Z.
+ */
+ if (tz == NULL)
+ goto err;
+ tzc = *tz;
+ *tz++ = '\0';
+ if (tzc == 'Z') {
+ if (*tz != '\0')
+ goto err;
+ offset = 0;
+ } else if (mode != RFC5280 && (tzc == '+' || tzc == '-') &&
+ (strlen(tz) == 4)) {
+ int hours, mins;
+ hours = ATOI2(tz);
+ mins = ATOI2(tz);
+ if (hours > 12 || mins > 59)
+ goto err;
+ offset = hours * 3600 + mins * 60;
+ if (tzc == '-')
+ offset = -offset;
+ } else
+ goto err;
+
+ if (mode != RFC5280) {
+ /* XXX - yuck - OPENSSL_gmtime_adj should go away */
+ if (!OPENSSL_gmtime_adj(lt, 0, offset))
+ goto err;
+ }
+
+ /*
+ * We only allow fractional seconds to be present if we are in
+ * the non-RFC case of a Generalized time. RFC 5280 forbids
+ * fractional seconds.
+ */
+ if (dot != NULL) {
+ if (mode != V_ASN1_GENERALIZEDTIME)
+ goto err;
+ *dot++ = '\0';
+ if (!isdigit((unsigned char)*dot))
+ goto err;
+ }
+
+ /*
+ * Validate and convert the time
+ */
+ p = buf;
+ tlen = strlen(buf);
+ switch (tlen) {
+ case 14:
+ lt->tm_year = (ATOI2(p) * 100) - 1900; /* cc */
+ if (mode == RFC5280 || mode == V_ASN1_GENERALIZEDTIME)
+ type = V_ASN1_GENERALIZEDTIME;
+ else
+ goto err;
+ /* FALLTHROUGH */
+ case 12:
+ if (type == 0 && mode == V_ASN1_GENERALIZEDTIME) {
+ /*
+ * In the non-RFC case of a Generalized time
+ * seconds may not have been provided. RFC
+ * 5280 mandates that seconds must be present.
+ */
+ noseconds = 1;
+ lt->tm_year = (ATOI2(p) * 100) - 1900; /* cc */
+ type = V_ASN1_GENERALIZEDTIME;
+ }
+ /* FALLTHROUGH */
+ case 10:
+ if (type == 0) {
+ /*
+ * At this point we must have a UTC time.
+ * In the RFC 5280 case it must have the
+ * seconds present. In the non-RFC case
+ * may have no seconds.
+ */
+ if (mode == V_ASN1_GENERALIZEDTIME)
+ goto err;
+ if (tlen == 10) {
+ if (mode == V_ASN1_UTCTIME)
+ noseconds = 1;
+ else
+ goto err;
+ }
+ type = V_ASN1_UTCTIME;
+ }
+ lt->tm_year += ATOI2(p); /* yy */
+ if (type == V_ASN1_UTCTIME) {
+ if (lt->tm_year < 50)
+ lt->tm_year += 100;
+ }
+ lt->tm_mon = ATOI2(p); /* mm */
+ if ((lt->tm_mon > 12) || !lt->tm_mon)
+ goto err;
+ --lt->tm_mon; /* struct tm is 0 - 11 */
+ lt->tm_mday = ATOI2(p); /* dd */
+ if ((lt->tm_mday > 31) || !lt->tm_mday)
+ goto err;
+ lt->tm_hour = ATOI2(p); /* HH */
+ if (lt->tm_hour > 23)
+ goto err;
+ lt->tm_min = ATOI2(p); /* MM */
+ if (lt->tm_min > 59)
+ goto err;
+ lt->tm_sec = 0; /* SS */
+ if (noseconds)
+ break;
+ lt->tm_sec = ATOI2(p);
+ /* Leap second 60 is not accepted. Reconsider later? */
+ if (lt->tm_sec > 59)
+ goto err;
+ break;
+ default:
+ goto err;
+ }
+
+ /* RFC 5280 section 4.1.2.5 */
+ if (mode == RFC5280 && lt->tm_year < 150 &&
+ type != V_ASN1_UTCTIME)
+ goto err;
+ if (mode == RFC5280 && lt->tm_year >= 150 &&
+ type != V_ASN1_GENERALIZEDTIME)
+ goto err;
+
+ free(buf);
+ return type;
+
+err:
+ free(buf);
+ return -1;
+}
diff --git a/lib/libssl/src/crypto/asn1/a_utctm.c b/lib/libssl/src/crypto/asn1/a_utctm.c
index ca19a8c7a00..c208d494c3a 100644
--- a/lib/libssl/src/crypto/asn1/a_utctm.c
+++ b/lib/libssl/src/crypto/asn1/a_utctm.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: a_utctm.c,v 1.28 2015/09/30 18:26:07 jsing Exp $ */
+/* $OpenBSD: a_utctm.c,v 1.29 2015/10/02 15:04:45 beck Exp $ */
/* Copyright (C) 1995-1998 Eric Young (eay@cryptsoft.com)
* All rights reserved.
*
@@ -64,66 +64,14 @@
#include <openssl/err.h>
#include "o_time.h"
+#include "asn1_locl.h"
int
ASN1_UTCTIME_check(ASN1_UTCTIME *d)
{
- static const int min[8] = {0, 1, 1, 0, 0, 0, 0, 0};
- static const int max[8] = {99, 12, 31, 23, 59, 59, 12, 59};
- char *a;
- int n, i, l, o;
-
if (d->type != V_ASN1_UTCTIME)
return (0);
- l = d->length;
- a = (char *)d->data;
- o = 0;
-
- if (l < 11)
-
- goto err;
- for (i = 0; i < 6; i++) {
- if ((i == 5) && ((a[o] == 'Z') ||
- (a[o] == '+') || (a[o] == '-'))) {
- i++;
- break;
- }
- if ((a[o] < '0') || (a[o] > '9'))
- goto err;
- n = a[o]-'0';
- if (++o > l)
- goto err;
- if ((a[o] < '0') || (a[o] > '9'))
- goto err;
- n = (n * 10) + a[o] - '0';
- if (++o > l)
- goto err;
- if ((n < min[i]) || (n > max[i]))
- goto err;
- }
- if (a[o] == 'Z')
- o++;
- else if ((a[o] == '+') || (a[o] == '-')) {
- o++;
- if (o + 4 > l)
- goto err;
- for (i = 6; i < 8; i++) {
- if ((a[o] < '0') || (a[o] > '9'))
- goto err;
- n = a[o] -'0';
- o++;
- if ((a[o] < '0') || (a[o] > '9'))
- goto err;
- n = (n * 10) + a[o] - '0';
- if ((n < min[i]) || (n > max[i]))
- goto err;
- o++;
- }
- }
- return (o == l);
-
-err:
- return (0);
+ return(d->type == asn1_time_parse(d->data, d->length, NULL, d->type));
}
int
@@ -159,7 +107,6 @@ ASN1_UTCTIME_adj_internal(ASN1_UTCTIME *s, time_t t, int offset_day,
char *p;
struct tm *ts;
struct tm data;
- size_t len = 20;
ts = gmtime_r(&t, &data);
if (ts == NULL)
@@ -170,23 +117,14 @@ ASN1_UTCTIME_adj_internal(ASN1_UTCTIME *s, time_t t, int offset_day,
return NULL;
}
- if ((ts->tm_year < 50) || (ts->tm_year >= 150))
- return NULL;
-
- p = (char *)s->data;
- if ((p == NULL) || ((size_t)s->length < len)) {
- p = malloc(len);
- if (p == NULL) {
- ASN1err(ASN1_F_ASN1_UTCTIME_ADJ, ERR_R_MALLOC_FAILURE);
- return (NULL);
- }
- free(s->data);
- s->data = (unsigned char *)p;
+ if ((p = utctime_string_from_tm(ts)) == NULL) {
+ ASN1err(ASN1_F_ASN1_UTCTIME_ADJ, ERR_R_MALLOC_FAILURE);
+ return (NULL);
}
-
- 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);
+ free(s->data);
+ s->data = p;
s->length = strlen(p);
+
s->type = V_ASN1_UTCTIME;
return (s);
}
diff --git a/lib/libssl/src/crypto/asn1/asn1_locl.h b/lib/libssl/src/crypto/asn1/asn1_locl.h
index c6c80aa6aa2..d4994c7cee5 100644
--- a/lib/libssl/src/crypto/asn1/asn1_locl.h
+++ b/lib/libssl/src/crypto/asn1/asn1_locl.h
@@ -1,4 +1,4 @@
-/* $OpenBSD: asn1_locl.h,v 1.5 2014/06/12 15:49:27 deraadt Exp $ */
+/* $OpenBSD: asn1_locl.h,v 1.6 2015/10/02 15:04:45 beck Exp $ */
/* Written by Dr Stephen N Henson (steve@openssl.org) for the OpenSSL
* project 2006.
*/
@@ -58,6 +58,10 @@
/* Internal ASN1 structures and functions: not for application use */
+char * gentime_string_from_tm(struct tm *tm);
+char * utctime_string_from_tm(struct tm *tm);
+int asn1_time_parse(const char *, size_t, struct tm *, int);
+
/* ASN1 print context structure */
struct asn1_pctx_st {
diff --git a/lib/libssl/src/crypto/x509/x509_lcl.h b/lib/libssl/src/crypto/x509/x509_lcl.h
index b16df78ad7c..0c1c130d5c8 100644
--- a/lib/libssl/src/crypto/x509/x509_lcl.h
+++ b/lib/libssl/src/crypto/x509/x509_lcl.h
@@ -57,3 +57,4 @@
*/
int x509_check_cert_time(X509_STORE_CTX *ctx, X509 *x, int quiet);
+int asn1_time_parse(const char *, size_t, struct tm *, int);
diff --git a/lib/libssl/src/crypto/x509/x509_vfy.c b/lib/libssl/src/crypto/x509/x509_vfy.c
index 8d4d15668ec..c48143f3517 100644
--- a/lib/libssl/src/crypto/x509/x509_vfy.c
+++ b/lib/libssl/src/crypto/x509/x509_vfy.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: x509_vfy.c,v 1.45 2015/09/14 16:13:39 jsing Exp $ */
+/* $OpenBSD: x509_vfy.c,v 1.46 2015/10/02 15:04:45 beck Exp $ */
/* Copyright (C) 1995-1998 Eric Young (eay@cryptsoft.com)
* All rights reserved.
*
@@ -1631,106 +1631,50 @@ X509_cmp_current_time(const ASN1_TIME *ctm)
return X509_cmp_time(ctm, NULL);
}
+/*
+ * Compare a possibly unvalidated ASN1_TIME string against a time_t
+ * using RFC 5280 rules for the time string. If *cmp_time is NULL
+ * the current system time is used.
+ *
+ * XXX NOTE that unlike what you expect a "cmp" function to do in C,
+ * XXX this one is "special", and returns 0 for error.
+ *
+ * Returns:
+ * -1 if the ASN1_time is earlier than OR the same as *cmp_time.
+ * 1 if the ASN1_time is later than *cmp_time.
+ * 0 on error.
+ */
int
X509_cmp_time(const ASN1_TIME *ctm, time_t *cmp_time)
{
- char *str;
- ASN1_TIME atm;
- long offset;
- char buff1[24], buff2[24], *p;
- int i, j;
+ time_t time1, time2;
+ struct tm tm1;
+ int ret = 0;
- p = buff1;
- i = ctm->length;
- str = (char *)ctm->data;
- if (ctm->type == V_ASN1_UTCTIME) {
- if ((i < 11) || (i > 17))
- return 0;
- memcpy(p, str, 10);
- p += 10;
- str += 10;
- i -= 10;
- } else {
- if (i < 13)
- return 0;
- memcpy(p, str, 12);
- p += 12;
- str += 12;
- i -= 12;
- }
+ if (cmp_time == NULL)
+ time2 = time(NULL);
+ else
+ time2 = *cmp_time;
- if (i < 1)
- return 0;
- if ((*str == 'Z') || (*str == '-') || (*str == '+')) {
- *(p++) = '0';
- *(p++) = '0';
- } else {
- if (i < 2)
- return 0;
- *(p++) = *(str++);
- *(p++) = *(str++);
- i -= 2;
- if (i < 1)
- return 0;
- /* Skip any fractional seconds... */
- if (*str == '.') {
- str++;
- i--;
- while (i > 1 && (*str >= '0') && (*str <= '9')) {
- str++;
- i--;
- }
- }
- }
- *(p++) = 'Z';
- *(p++) = '\0';
+ memset(&tm1, 0, sizeof(tm1));
- if (i < 1)
- return 0;
- if (*str == 'Z') {
- if (i != 1)
- return 0;
- offset = 0;
- } else {
- if (i != 5)
- return 0;
- if ((*str != '+') && (*str != '-'))
- return 0;
- if (str[1] < '0' || str[1] > '9' ||
- str[2] < '0' || str[2] > '9' ||
- str[3] < '0' || str[3] > '9' ||
- str[4] < '0' || str[4] > '9')
- return 0;
- offset = ((str[1] - '0') * 10 + (str[2] - '0')) * 60;
- offset += (str[3] - '0') * 10 + (str[4] - '0');
- if (*str == '-')
- offset = -offset;
- }
- atm.type = ctm->type;
- atm.flags = 0;
- atm.length = sizeof(buff2);
- atm.data = (unsigned char *)buff2;
+ if (asn1_time_parse(ctm->data, ctm->length, &tm1, 0) == -1)
+ goto out; /* invalid time */
- if (X509_time_adj(&atm, offset * 60, cmp_time) == NULL)
- return 0;
+ /*
+ * Defensively fail if the time string is not representable as
+ * a time_t. A time_t must be sane if you care about times after
+ * Jan 19 2038.
+ */
+ if ((time1 = timegm(&tm1)) == -1)
+ goto out;
- if (ctm->type == V_ASN1_UTCTIME) {
- i = (buff1[0] - '0') * 10 + (buff1[1] - '0');
- if (i < 50)
- i += 100; /* cf. RFC 2459 */
- j = (buff2[0] - '0') * 10 + (buff2[1] - '0');
- if (j < 50)
- j += 100;
- if (i < j)
- return -1;
- if (i > j)
- return 1;
- }
- i = strcmp(buff1, buff2);
- if (i == 0) /* wait a second then return younger :-) */
- return -1;
+ if (time1 <= time2)
+ ret = -1;
else
- return i;
+ ret = 1;
+ out:
+ return (ret);
}
ASN1_TIME *