Commit Diff


commit - dc306c6bd88271ab911e205539974da98be82d17
commit + 35d04818407d29f2e9de1c0630db561cf783d6fa
blob - /dev/null
blob + ddf0066b68229f5659b4bad24aee9708bdebed4f (mode 644)
--- /dev/null
+++ test/expect126.diff
@@ -0,0 +1,28 @@
+--- test126.left.txt
++++ test126.right.txt
+@@ -1,4 +1,4 @@
+-/* $OpenBSD: a_time_tm.c,v 1.15 2018/04/25 11:48:21 tb Exp $ */
++/* $OpenBSD: a_time_tm.c,v 1.16 2020/12/16 18:35:59 tb Exp $ */
+ /*
+  * Copyright (c) 2015 Bob Beck <beck@openbsd.org>
+  *
+@@ -108,10 +108,9 @@
+ 		return (-1);
+ 
+ 	lt = tm;
+-	if (lt == NULL) {
+-		memset(&ltm, 0, sizeof(ltm));
++	if (lt == NULL)
+ 		lt = &ltm;
+-	}
++	memset(lt, 0, sizeof(*lt));
+ 
+ 	/* Timezone is required and must be GMT (Zulu). */
+ 	if (bytes[len - 1] != 'Z')
+@@ -168,4 +167,4 @@
+ 	}
+ 
+ 	return (type);
+-}
++}
+\ No newline at end of file
blob - /dev/null
blob + a3628b8eba126a368931803b46672f9fb1ae8a10 (mode 644)
--- /dev/null
+++ test/test126.left.txt
@@ -0,0 +1,171 @@
+/* $OpenBSD: a_time_tm.c,v 1.15 2018/04/25 11:48:21 tb 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 "includes.h"
+
+#include <ctype.h>
+#include <limits.h>
+#include <stdio.h>
+#include <string.h>
+#include <time.h>
+
+#define GENTIME_LENGTH 15
+#define UTCTIME_LENGTH 13
+
+#define V_ASN1_UTCTIME		23
+#define V_ASN1_GENERALIZEDTIME	24
+
+int
+ASN1_time_tm_cmp(struct tm *tm1, struct tm *tm2)
+{
+	if (tm1->tm_year < tm2->tm_year)
+		return (-1);
+	if (tm1->tm_year > tm2->tm_year)
+		return (1);
+	if (tm1->tm_mon < tm2->tm_mon)
+		return (-1);
+	if (tm1->tm_mon > tm2->tm_mon)
+		return (1);
+	if (tm1->tm_mday < tm2->tm_mday)
+		return (-1);
+	if (tm1->tm_mday > tm2->tm_mday)
+		return (1);
+	if (tm1->tm_hour < tm2->tm_hour)
+		return (-1);
+	if (tm1->tm_hour > tm2->tm_hour)
+		return (1);
+	if (tm1->tm_min < tm2->tm_min)
+		return (-1);
+	if (tm1->tm_min > tm2->tm_min)
+		return (1);
+	if (tm1->tm_sec < tm2->tm_sec)
+		return (-1);
+	if (tm1->tm_sec > tm2->tm_sec)
+		return (1);
+	return 0;
+}
+
+int
+ASN1_time_tm_clamp_notafter(struct tm *tm)
+{
+#ifdef SMALL_TIME_T
+	struct tm broken_os_epoch_tm;
+	time_t broken_os_epoch_time = INT_MAX;
+
+	if (gmtime_r(&broken_os_epoch_time, &broken_os_epoch_tm) == NULL)
+		return 0;
+
+	if (ASN1_time_tm_cmp(tm, &broken_os_epoch_tm) == 1)
+		memcpy(tm, &broken_os_epoch_tm, sizeof(*tm));
+#endif
+	return 1;
+}
+
+/*
+ * Parse an RFC 5280 format ASN.1 time string.
+ *
+ * mode must be:
+ * 0 if we expect to parse a time as specified in RFC 5280 for an X509 object.
+ * V_ASN1_UTCTIME if we wish to parse an RFC5280 format UTC time.
+ * V_ASN1_GENERALIZEDTIME if we wish to parse an RFC5280 format Generalized 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	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)
+{
+	size_t i;
+	int type = 0;
+	struct tm ltm;
+	struct tm *lt;
+	const char *p;
+
+	if (bytes == NULL)
+		return (-1);
+
+	/* Constrain to valid lengths. */
+	if (len != UTCTIME_LENGTH && len != GENTIME_LENGTH)
+		return (-1);
+
+	lt = tm;
+	if (lt == NULL) {
+		memset(&ltm, 0, sizeof(ltm));
+		lt = &ltm;
+	}
+
+	/* Timezone is required and must be GMT (Zulu). */
+	if (bytes[len - 1] != 'Z')
+		return (-1);
+
+	/* Make sure everything else is digits. */
+	for (i = 0; i < len - 1; i++) {
+		if (isdigit((unsigned char)bytes[i]))
+			continue;
+		return (-1);
+	}
+
+	/*
+	 * Validate and convert the time
+	 */
+	p = bytes;
+	switch (len) {
+	case GENTIME_LENGTH:
+		if (mode == V_ASN1_UTCTIME)
+			return (-1);
+		lt->tm_year = (ATOI2(p) * 100) - 1900;	/* cc */
+		type = V_ASN1_GENERALIZEDTIME;
+		/* FALLTHROUGH */
+	case UTCTIME_LENGTH:
+		if (type == 0) {
+			if (mode == V_ASN1_GENERALIZEDTIME)
+				return (-1);
+			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) - 1;		/* mm */
+		if (lt->tm_mon < 0 || lt->tm_mon > 11)
+			return (-1);
+		lt->tm_mday = ATOI2(p);			/* dd */
+		if (lt->tm_mday < 1 || lt->tm_mday > 31)
+			return (-1);
+		lt->tm_hour = ATOI2(p);			/* HH */
+		if (lt->tm_hour < 0 || lt->tm_hour > 23)
+			return (-1);
+		lt->tm_min = ATOI2(p);			/* MM */
+		if (lt->tm_min < 0 || lt->tm_min > 59)
+			return (-1);
+		lt->tm_sec = ATOI2(p);			/* SS */
+		/* Leap second 60 is not accepted. Reconsider later? */
+		if (lt->tm_sec < 0 || lt->tm_sec > 59)
+			return (-1);
+		break;
+	default:
+		return (-1);
+	}
+
+	return (type);
+}
blob - /dev/null
blob + 8d81d0fe0db5b7de7ef4b20dce456f1c5fac0495 (mode 644)
--- /dev/null
+++ test/test126.right.txt
@@ -0,0 +1,170 @@
+/* $OpenBSD: a_time_tm.c,v 1.16 2020/12/16 18:35:59 tb 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 "includes.h"
+
+#include <ctype.h>
+#include <limits.h>
+#include <stdio.h>
+#include <string.h>
+#include <time.h>
+
+#define GENTIME_LENGTH 15
+#define UTCTIME_LENGTH 13
+
+#define V_ASN1_UTCTIME		23
+#define V_ASN1_GENERALIZEDTIME	24
+
+int
+ASN1_time_tm_cmp(struct tm *tm1, struct tm *tm2)
+{
+	if (tm1->tm_year < tm2->tm_year)
+		return (-1);
+	if (tm1->tm_year > tm2->tm_year)
+		return (1);
+	if (tm1->tm_mon < tm2->tm_mon)
+		return (-1);
+	if (tm1->tm_mon > tm2->tm_mon)
+		return (1);
+	if (tm1->tm_mday < tm2->tm_mday)
+		return (-1);
+	if (tm1->tm_mday > tm2->tm_mday)
+		return (1);
+	if (tm1->tm_hour < tm2->tm_hour)
+		return (-1);
+	if (tm1->tm_hour > tm2->tm_hour)
+		return (1);
+	if (tm1->tm_min < tm2->tm_min)
+		return (-1);
+	if (tm1->tm_min > tm2->tm_min)
+		return (1);
+	if (tm1->tm_sec < tm2->tm_sec)
+		return (-1);
+	if (tm1->tm_sec > tm2->tm_sec)
+		return (1);
+	return 0;
+}
+
+int
+ASN1_time_tm_clamp_notafter(struct tm *tm)
+{
+#ifdef SMALL_TIME_T
+	struct tm broken_os_epoch_tm;
+	time_t broken_os_epoch_time = INT_MAX;
+
+	if (gmtime_r(&broken_os_epoch_time, &broken_os_epoch_tm) == NULL)
+		return 0;
+
+	if (ASN1_time_tm_cmp(tm, &broken_os_epoch_tm) == 1)
+		memcpy(tm, &broken_os_epoch_tm, sizeof(*tm));
+#endif
+	return 1;
+}
+
+/*
+ * Parse an RFC 5280 format ASN.1 time string.
+ *
+ * mode must be:
+ * 0 if we expect to parse a time as specified in RFC 5280 for an X509 object.
+ * V_ASN1_UTCTIME if we wish to parse an RFC5280 format UTC time.
+ * V_ASN1_GENERALIZEDTIME if we wish to parse an RFC5280 format Generalized 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	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)
+{
+	size_t i;
+	int type = 0;
+	struct tm ltm;
+	struct tm *lt;
+	const char *p;
+
+	if (bytes == NULL)
+		return (-1);
+
+	/* Constrain to valid lengths. */
+	if (len != UTCTIME_LENGTH && len != GENTIME_LENGTH)
+		return (-1);
+
+	lt = tm;
+	if (lt == NULL)
+		lt = &ltm;
+	memset(lt, 0, sizeof(*lt));
+
+	/* Timezone is required and must be GMT (Zulu). */
+	if (bytes[len - 1] != 'Z')
+		return (-1);
+
+	/* Make sure everything else is digits. */
+	for (i = 0; i < len - 1; i++) {
+		if (isdigit((unsigned char)bytes[i]))
+			continue;
+		return (-1);
+	}
+
+	/*
+	 * Validate and convert the time
+	 */
+	p = bytes;
+	switch (len) {
+	case GENTIME_LENGTH:
+		if (mode == V_ASN1_UTCTIME)
+			return (-1);
+		lt->tm_year = (ATOI2(p) * 100) - 1900;	/* cc */
+		type = V_ASN1_GENERALIZEDTIME;
+		/* FALLTHROUGH */
+	case UTCTIME_LENGTH:
+		if (type == 0) {
+			if (mode == V_ASN1_GENERALIZEDTIME)
+				return (-1);
+			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) - 1;		/* mm */
+		if (lt->tm_mon < 0 || lt->tm_mon > 11)
+			return (-1);
+		lt->tm_mday = ATOI2(p);			/* dd */
+		if (lt->tm_mday < 1 || lt->tm_mday > 31)
+			return (-1);
+		lt->tm_hour = ATOI2(p);			/* HH */
+		if (lt->tm_hour < 0 || lt->tm_hour > 23)
+			return (-1);
+		lt->tm_min = ATOI2(p);			/* MM */
+		if (lt->tm_min < 0 || lt->tm_min > 59)
+			return (-1);
+		lt->tm_sec = ATOI2(p);			/* SS */
+		/* Leap second 60 is not accepted. Reconsider later? */
+		if (lt->tm_sec < 0 || lt->tm_sec > 59)
+			return (-1);
+		break;
+	default:
+		return (-1);
+	}
+
+	return (type);
+}
\ No newline at end of file