#define _REENTRANT /* for Sun */
#define __EXTENSIONS__ /* for Sun */
-#define _DEFAULT_SOURCE 1 /* timegm() */
#define _XOPEN_SOURCE 600 /* snprintf(), timezone */
#define _POSIX_C_SOURCE 200112L /* gmtime_r(), localtime_r(), tzset() */
#include <assert.h>
#include <errno.h>
-#if defined(sun) || defined(__sun) || defined(__solaris__)
-#define _EMULATE_TIMEGM
-#endif
-
-/*
- * Where to look for offset from GMT, Phase I.
- * Several platforms are known.
- */
-#if defined(__FreeBSD__) || defined(__OpenBSD__) \
- || (defined(__GNUC__) && defined(__APPLE_CC__)) \
- || (defined __GLIBC__ && __GLIBC__ >= 2)
-#undef HAVE_TM_GMTOFF
-#define HAVE_TM_GMTOFF
-#endif /* BSDs and newer glibc */
-
-/*
- * Where to look for offset from GMT, Phase II.
- */
-#ifdef HAVE_TM_GMTOFF
-#define GMTOFF(tm) ((tm).tm_gmtoff)
-#else /* HAVE_TM_GMTOFF */
-#define GMTOFF(tm) (-timezone)
-#endif /* HAVE_TM_GMTOFF */
-
-#if (defined(_EMULATE_TIMEGM) || !defined(HAVE_TM_GMTOFF))
-#warning "PLEASE STOP AND READ!"
-#warning " timegm() is implemented via getenv(\"TZ\")/setenv(\"TZ\"), which may be not thread-safe."
-#warning " "
-#warning " You must fix the code by inserting appropriate locking"
-#warning " if you want to use asn_GT2time() or asn_UT2time()."
-#warning "PLEASE STOP AND READ!"
-#endif /* _EMULATE_TIMEGM */
-
#define ATZVARS do { \
char tzoldbuf[64]; \
char *tzold
tzset(); \
} while(0); } while(0);
-#ifndef HAVE_TIMEGM
-#ifdef _EMULATE_TIMEGM
-static time_t timegm(struct tm *tm) {
- time_t tloc;
- ATZVARS;
- ATZSAVETZ;
- tloc = mktime(tm);
- ATZOLDTZ;
- return tloc;
-}
-#endif /* _EMULATE_TIMEGM */
-#endif
-
-
#ifndef ASN___INTERNAL_TEST_MODE
/*
asn_app_constraint_failed_f *ctfailcb,
void *app_key) {
const GeneralizedTime_t *st = (const GeneralizedTime_t *)sptr;
- time_t tloc;
- errno = EPERM; /* Just an unlikely error code */
- tloc = asn_GT2time(st, 0, 0);
- if(tloc == -1 && errno != EPERM) {
+ /* asn_GT2time() no longer supports NULL tm and no GMT */
+ fprintf(stderr, "GeneralizedTime_constraint() is not implemented for now.\n");
+ abort();
+
+ if(asn_GT2time(st, 0) != 0) {
ASN__CTFAIL(app_key, td, sptr,
"%s: Invalid time format: %s (%s:%d)",
td->name, strerror(errno), __FILE__, __LINE__);
asn_enc_rval_t erval;
int fv, fd; /* seconds fraction value and number of digits */
struct tm tm;
- time_t tloc;
/*
* Encode as a canonical DER.
*/
- errno = EPERM;
- tloc = asn_GT2time_frac((const GeneralizedTime_t *)sptr, &fv, &fd, &tm,
- 1); /* Recognize time */
- if(tloc == -1 && errno != EPERM) {
+ if(asn_GT2time_frac((const GeneralizedTime_t *)sptr, &fv, &fd, &tm) != 0) {
/* Failed to recognize time. Fail completely. */
ASN__ENCODE_FAILED;
}
- st = asn_time2GT_frac(0, &tm, fv, fd, 1); /* Save time canonically */
+ st = asn_time2GT_frac(0, &tm, fv, fd); /* Save time */
if(!st) ASN__ENCODE_FAILED; /* Memory allocation failure. */
erval = OCTET_STRING_encode_der(td, st, tag_mode, tag, cb, app_key);
int fv, fd; /* fractional parts */
struct tm tm;
- errno = EPERM;
if(asn_GT2time_frac((const GeneralizedTime_t *)sptr,
- &fv, &fd, &tm, 1) == -1
- && errno != EPERM)
+ &fv, &fd, &tm) != 0)
ASN__ENCODE_FAILED;
- gt = asn_time2GT_frac(0, &tm, fv, fd, 1);
+ gt = asn_time2GT_frac(0, &tm, fv, fd);
if(!gt) ASN__ENCODE_FAILED;
rv = OCTET_STRING_encode_xer_utf8(td, sptr, ilevel, flags,
struct tm tm;
int ret;
- errno = EPERM;
- if(asn_GT2time(st, &tm, 1) == -1 && errno != EPERM)
+ if(asn_GT2time(st, &tm) != 0)
return (cb("<bad-value>", 11, app_key) < 0) ? -1 : 0;
ret = snprintf(buf, sizeof(buf),
}
}
-time_t
-asn_GT2time(const GeneralizedTime_t *st, struct tm *ret_tm, int as_gmt) {
- return asn_GT2time_frac(st, 0, 0, ret_tm, as_gmt);
+int
+asn_GT2time(const GeneralizedTime_t *st, struct tm *ret_tm) {
+ return asn_GT2time_frac(st, 0, 0, ret_tm);
}
time_t
-asn_GT2time_prec(const GeneralizedTime_t *st, int *frac_value, int frac_digits, struct tm *ret_tm, int as_gmt) {
+asn_GT2time_prec(const GeneralizedTime_t *st, int *frac_value, int frac_digits, struct tm *ret_tm) {
time_t tloc;
int fv, fd = 0;
if(frac_value)
- tloc = asn_GT2time_frac(st, &fv, &fd, ret_tm, as_gmt);
+ tloc = asn_GT2time_frac(st, &fv, &fd, ret_tm);
else
- return asn_GT2time_frac(st, 0, 0, ret_tm, as_gmt);
+ return asn_GT2time_frac(st, 0, 0, ret_tm);
if(fd == 0 || frac_digits <= 0) {
*frac_value = 0;
} else {
return tloc;
}
-time_t
-asn_GT2time_frac(const GeneralizedTime_t *st, int *frac_value, int *frac_digits, struct tm *ret_tm, int as_gmt) {
+/*
+ * I had to tighten this up because timegm() is not standard.
+ * This outright means time_t is out of reach, which is a nightmare.
+ *
+ * rfc6486#section-4.2.1:
+ *
+ * The manifestNumber, thisUpdate, and nextUpdate fields are modeled
+ * after the corresponding fields in X.509 CRLs (see [RFC5280]).
+ *
+ * rfc5280#section-4.1.2.5.2:
+ *
+ * GeneralizedTime values MUST be
+ * expressed in Greenwich Mean Time (Zulu) and MUST include seconds
+ * (i.e., times are YYYYMMDDHHMMSSZ)
+ *
+ * This requirement makes the problem more pallatable, because it means we can
+ * convert the Generalized Time to a simple CST struct tm, and use that instead
+ * of time_t.
+ *
+ * I left fractional seconds in place for now.
+ *
+ * The resulting tm is always in CST.
+ */
+int
+asn_GT2time_frac(const GeneralizedTime_t *st, int *frac_value, int *frac_digits,
+ struct tm *ret_tm) {
struct tm tm_s;
uint8_t *buf;
uint8_t *end;
- int gmtoff_h = 0;
- int gmtoff_m = 0;
- int gmtoff = 0; /* h + m */
- int offset_specified = 0;
int fvalue = 0;
int fdigits = 0;
- time_t tloc;
- if(!st || !st->buf) {
- errno = EINVAL;
- return -1;
- } else {
- buf = st->buf;
- end = buf + st->size;
- }
+ if(!st || !st->buf)
+ goto garbage;
- if(st->size < 10) {
- errno = EINVAL;
- return -1;
- }
+ buf = st->buf;
+ end = buf + st->size;
+
+ if(st->size < 10)
+ goto garbage;
/*
* Decode first 10 bytes: "AAAAMMJJhh"
#define B2F(var) do { \
unsigned ch = *buf; \
if(ch < 0x30 || ch > 0x39) { \
- errno = EINVAL; \
- return -1; \
+ goto garbage; \
} else { \
var = var * 10 + (ch - 0x30); \
buf++; \
B2T(tm_hour); /* 9: h */
B2T(tm_hour); /* 0: h */
- if(buf == end) goto local_finish;
+ if(buf == end) goto garbage;
/*
* Parse [mm[ss[(.|,)ffff]]]
case 0x30: case 0x31: case 0x32: case 0x33: case 0x34:
case 0x35: case 0x36: case 0x37: case 0x38: case 0x39:
tm_s.tm_min = (*buf++) - 0x30;
- if(buf == end) { errno = EINVAL; return -1; }
+ if(buf == end) goto garbage;
B2T(tm_min);
break;
- case 0x2B: case 0x2D: /* +, - */
- goto offset;
- case 0x5A: /* Z */
- goto utc_finish;
- default:
- errno = EINVAL;
- return -1;
+ default: /* +, -, Z */
+ goto garbage;
}
- if(buf == end) goto local_finish;
+ if(buf == end) goto garbage;
/*
* Parse [mm[ss[(.|,)ffff]]]
case 0x30: case 0x31: case 0x32: case 0x33: case 0x34:
case 0x35: case 0x36: case 0x37: case 0x38: case 0x39:
tm_s.tm_sec = (*buf++) - 0x30;
- if(buf == end) { errno = EINVAL; return -1; }
+ if(buf == end) goto garbage;
B2T(tm_sec);
break;
- case 0x2B: case 0x2D: /* +, - */
- goto offset;
- case 0x5A: /* Z */
- goto utc_finish;
- default:
- errno = EINVAL;
- return -1;
+ default: /* +, -, Z */
+ goto garbage;
}
- if(buf == end) goto local_finish;
+ if(buf == end) goto garbage;
/*
* Parse [mm[ss[(.|,)ffff]]]
}
}
- if(buf == end) goto local_finish;
-
- switch(*buf) {
- case 0x2B: case 0x2D: /* +, - */
- goto offset;
- case 0x5A: /* Z */
- goto utc_finish;
- default:
- errno = EINVAL;
- return -1;
- }
-
+ if(buf == end) goto garbage;
-offset:
+ if ((*buf) != 0x5A) /* Zulu */
+ goto garbage;
- if(end - buf < 3) {
- errno = EINVAL;
- return -1;
- }
- buf++;
- B2F(gmtoff_h);
- B2F(gmtoff_h);
- if(buf[-3] == 0x2D) /* Negative */
- gmtoff = -1;
- else
- gmtoff = 1;
-
- if((end - buf) == 2) {
- B2F(gmtoff_m);
- B2F(gmtoff_m);
- } else if(end != buf) {
- errno = EINVAL;
- return -1;
- }
-
- gmtoff = gmtoff * (3600 * gmtoff_h + 60 * gmtoff_m);
-
- /* Fall through */
-utc_finish:
-
- offset_specified = 1;
-
- /* Fall through */
-local_finish:
-
- /*
- * Validation.
- */
+ /* Validation */
if((tm_s.tm_mon > 12 || tm_s.tm_mon < 1)
|| (tm_s.tm_mday > 31 || tm_s.tm_mday < 1)
|| (tm_s.tm_hour > 23)
|| (tm_s.tm_sec > 60)
- ) {
- errno = EINVAL;
- return -1;
- }
+ )
+ goto garbage;
/* Canonicalize */
tm_s.tm_mon -= 1; /* 0 - 11 */
tm_s.tm_year -= 1900;
- tm_s.tm_isdst = -1;
-
- tm_s.tm_sec -= gmtoff;
-
- /*** AT THIS POINT tm_s is either GMT or local (unknown) ****/
-
- if(offset_specified) {
- tloc = timegm(&tm_s);
- } else {
- /*
- * Without an offset (or "Z"),
- * we can only guess that it is a local zone.
- * Interpret it in this fashion.
- */
- tloc = mktime(&tm_s);
- }
- if(tloc == -1) {
- errno = EINVAL;
- return -1;
- }
+ tm_s.tm_isdst = 0;
- if(ret_tm) {
- if(as_gmt) {
- if(offset_specified) {
- *ret_tm = tm_s;
- } else {
- if(gmtime_r(&tloc, ret_tm) == 0) {
- errno = EINVAL;
- return -1;
- }
- }
- } else {
- if(localtime_r(&tloc, ret_tm) == 0) {
- errno = EINVAL;
- return -1;
- }
- }
- }
+ *ret_tm = tm_s;
/* Fractions of seconds */
if(frac_value) *frac_value = fvalue;
if(frac_digits) *frac_digits = fdigits;
- return tloc;
+ return 0;
+
+garbage:
+ errno = EINVAL;
+ return -1;
}
GeneralizedTime_t *
-asn_time2GT(GeneralizedTime_t *opt_gt, const struct tm *tm, int force_gmt) {
- return asn_time2GT_frac(opt_gt, tm, 0, 0, force_gmt);
+asn_time2GT(GeneralizedTime_t *opt_gt, const struct tm *tm) {
+ return asn_time2GT_frac(opt_gt, tm, 0, 0);
}
GeneralizedTime_t *
-asn_time2GT_frac(GeneralizedTime_t *opt_gt, const struct tm *tm, int frac_value, int frac_digits, int force_gmt) {
- struct tm tm_s;
- long gmtoff;
+asn_time2GT_frac(GeneralizedTime_t *opt_gt, const struct tm *tm, int frac_value, int frac_digits) {
const unsigned int buf_size =
4 + 2 + 2 /* yyyymmdd */
+ 2 + 2 + 2 /* hhmmss */
buf = (char *)MALLOC(buf_size);
if(!buf) return 0;
- gmtoff = GMTOFF(*tm);
-
- if(force_gmt && gmtoff) {
- tm_s = *tm;
- tm_s.tm_sec -= gmtoff;
- timegm(&tm_s); /* Fix the time */
- tm = &tm_s;
-#ifdef HAVE_TM_GMTOFF
- assert(!GMTOFF(tm_s)); /* Will fix itself */
-#else /* !HAVE_TM_GMTOFF */
- gmtoff = 0;
-#endif
- }
-
size = snprintf(buf, buf_size, "%04d%02d%02d%02d%02d%02d",
tm->tm_year + 1900,
tm->tm_mon + 1,
}
}
- if(force_gmt) {
- *p++ = 0x5a; /* "Z" */
- *p++ = 0;
- size++;
- } else {
- int ret;
- gmtoff %= 86400;
- ret = snprintf(p, buf_size - size, "%+03ld%02ld",
- gmtoff / 3600, labs(gmtoff % 3600) / 60);
- if(ret != 5) {
- FREEMEM(buf);
- errno = EINVAL;
- return 0;
- }
- size += ret;
- }
+ *p++ = 0x5a; /* "Z" */
+ *p++ = 0;
+ size++;
if(opt_gt) {
if(opt_gt->buf)
(void)td;
+ /* asn_GT2time_frac() no longer supports NULL tm and no GMT. */
+ fprintf(stderr, "GeneralizedTime_compare() is not implemented for now.\n");
+ abort();
+
if(a && b) {
int afrac_value, afrac_digits;
int bfrac_value, bfrac_digits;
time_t at, bt;
errno = EPERM;
- at = asn_GT2time_frac(a, &afrac_value, &afrac_digits, 0, 0);
+ at = asn_GT2time_frac(a, &afrac_value, &afrac_digits, 0);
aerr = errno;
errno = EPERM;
- bt = asn_GT2time_frac(b, &bfrac_value, &bfrac_digits, 0, 0);
+ bt = asn_GT2time_frac(b, &bfrac_value, &bfrac_digits, 0);
berr = errno;
if(at == -1 && aerr != EPERM) {
UTCTime_constraint(const asn_TYPE_descriptor_t *td, const void *sptr,
asn_app_constraint_failed_f *ctfailcb, void *app_key) {
const UTCTime_t *st = (const UTCTime_t *)sptr;
- time_t tloc;
- errno = EPERM; /* Just an unlikely error code */
- tloc = asn_UT2time(st, 0, 0);
- if(tloc == -1 && errno != EPERM) {
+ /* asn_UT2time() no longer supports NULL tm and no GMT. */
+ fprintf(stderr, "UTCTime_constraint() is not implemented for now.\n");
+ abort();
+
+ if(asn_UT2time(st, 0) != 0) {
ASN__CTFAIL(app_key, td, sptr, "%s: Invalid time format: %s (%s:%d)",
td->name, strerror(errno), __FILE__, __LINE__);
return -1;
UTCTime_t *ut;
struct tm tm;
- errno = EPERM;
- if(asn_UT2time((const UTCTime_t *)sptr, &tm, 1) == -1
- && errno != EPERM)
+ if(asn_UT2time((const UTCTime_t *)sptr, &tm) != 0)
ASN__ENCODE_FAILED;
/* Fractions are not allowed in UTCTime */
- ut = asn_time2UT(0, &tm, 1);
+ ut = asn_time2UT(0, &tm);
if(!ut) ASN__ENCODE_FAILED;
rv = OCTET_STRING_encode_xer_utf8(td, sptr, ilevel, flags,
struct tm tm;
int ret;
- errno = EPERM;
- if(asn_UT2time(st, &tm, 1) == -1 && errno != EPERM)
+ if(asn_UT2time(st, &tm) != 0)
return (cb("<bad-value>", 11, app_key) < 0) ? -1 : 0;
ret = snprintf(buf, sizeof(buf),
}
time_t
-asn_UT2time(const UTCTime_t *st, struct tm *_tm, int as_gmt) {
+asn_UT2time(const UTCTime_t *st, struct tm *_tm) {
char buf[24]; /* "AAMMJJhhmmss+hhmm" + cushion */
GeneralizedTime_t gt;
gt.buf[1] = 0x30;
}
- return asn_GT2time(>, _tm, as_gmt);
+ return asn_GT2time(>, _tm);
}
UTCTime_t *
-asn_time2UT(UTCTime_t *opt_ut, const struct tm *tm, int force_gmt) {
+asn_time2UT(UTCTime_t *opt_ut, const struct tm *tm) {
GeneralizedTime_t *gt = (GeneralizedTime_t *)opt_ut;
- gt = asn_time2GT(gt, tm, force_gmt);
+ gt = asn_time2GT(gt, tm);
if(gt == 0) return 0;
assert(gt->size >= 2);
(void)td;
+ /* asn_UT2time() no longer supports NULL tm and no GMT. */
+ fprintf(stderr, "UTCTime_compare() is not implemented for now.\n");
+ abort();
+
if(a && b) {
time_t at, bt;
int aerr, berr;
errno = EPERM;
- at = asn_UT2time(a, 0, 0);
+ at = asn_UT2time(a, 0);
aerr = errno;
errno = EPERM;
- bt = asn_UT2time(b, 0, 0);
+ bt = asn_UT2time(b, 0);
berr = errno;
if(at == -1 && aerr != EPERM) {
);
}
+/*
+ * Expects both arguments to be normalized and CST.
+ */
+static int
+tm_cmp(struct tm *tm1, struct tm *tm2)
+{
+#define TM_CMP(field) \
+ if (tm1->field < tm2->field) \
+ return -1; \
+ if (tm1->field > tm2->field) \
+ return 1; \
+
+ TM_CMP(tm_year);
+ TM_CMP(tm_mon);
+ TM_CMP(tm_mday);
+ TM_CMP(tm_hour);
+ TM_CMP(tm_min);
+ TM_CMP(tm_sec);
+ return 0;
+
+#undef TM_CMP
+}
+
static int
validate_dates(GeneralizedTime_t *this, GeneralizedTime_t *next)
{
tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday, \
tm.tm_hour, tm.tm_min, tm.tm_sec
- time_t thisUpdate;
- time_t nextUpdate;
- time_t now;
- struct tm thisUpdate_tm;
- struct tm nextUpdate_tm;
+ time_t now_tt;
+ struct tm now;
+ struct tm thisUpdate;
+ struct tm nextUpdate;
int error;
- /*
- * BTW: We only need the tm variables for error messages, which are
- * rarely needed.
- * So maybe we could get a small performance boost by postponing the
- * calls to localtime_r().
- */
- thisUpdate = asn_GT2time(this, &thisUpdate_tm, false);
- nextUpdate = asn_GT2time(next, &nextUpdate_tm, false);
+ error = asn_GT2time(this, &thisUpdate);
+ if (error)
+ return pr_val_err("Manifest's thisUpdate date is unparseable.");
+ error = asn_GT2time(next, &nextUpdate);
+ if (error)
+ return pr_val_err("Manifest's nextUpdate date is unparseable.");
- if (difftime(thisUpdate, nextUpdate) > 0) {
+ if (tm_cmp(&thisUpdate, &nextUpdate) > 0) {
return pr_val_err(
"Manifest's thisUpdate (" TM_FMT ") > nextUpdate ("
TM_FMT ").",
- TM_ARGS(thisUpdate_tm),
- TM_ARGS(nextUpdate_tm));
+ TM_ARGS(thisUpdate),
+ TM_ARGS(nextUpdate));
}
- now = 0;
- error = get_current_time(&now);
+ now_tt = 0;
+ error = get_current_time(&now_tt);
if (error)
return error;
+ if (gmtime_r(&now_tt, &now) == NULL) {
+ error = errno;
+ return pr_val_err("gmtime_r(now) error %d: %s", error,
+ strerror(error));
+ }
- if (difftime(now, thisUpdate) < 0) {
+ if (tm_cmp(&now, &thisUpdate) < 0) {
return pr_val_err(
"Manifest is not valid yet. (thisUpdate: " TM_FMT ")",
- TM_ARGS(thisUpdate_tm));
+ TM_ARGS(thisUpdate));
}
- if (difftime(now, nextUpdate) > 0) {
+ if (tm_cmp(&now, &nextUpdate) > 0) {
return incidence(INID_MFT_STALE,
"Manifest is stale. (nextUpdate: " TM_FMT ")",
- TM_ARGS(nextUpdate_tm));
+ TM_ARGS(nextUpdate));
}
return 0;