On Wed, Oct 16, 2019 at 10:47:25PM +0200, Claudio Jeker wrote:
> On Wed, Oct 16, 2019 at 07:26:25AM -0300, Alexandre Hamada wrote:
> > Hi Tech,
> > I would like to suggest to use UTC functions on all date/time convertions,
> > to avoid some clock drift errors.
> >
> > Kind regards,
> > Alexandre Hamada
> >
> > https://patch-diff.githubusercontent.com/raw/kristapsdz/rpki-client/pull/9.patch
> >
> > From a463f8cb23375f15b74eff49a06e8934423e3dbf Mon Sep 17 00:00:00 2001
> > From: dev-gto <[email protected]>
> > Date: Wed, 16 Oct 2019 07:22:46 -0300
> > Subject: [PATCH] Avoid local time conversion
> >
> > ---
> > mft.c | 4 ++--
> > 1 file changed, 2 insertions(+), 2 deletions(-)
> >
> > diff --git a/mft.c b/mft.c
> > index f9176b4..738f3ff 100644
> > --- a/mft.c
> > +++ b/mft.c
> > @@ -75,8 +75,8 @@ gentime2time(struct parse *p, const ASN1_GENERALIZEDTIME
> > *tp)
> > memset(&tm, 0, sizeof(struct tm));
> > if (strptime(buf, "%b %d %T %Y %Z", &tm) == NULL)
> > errx(EXIT_FAILURE, "%s: strptime", buf);
> > - if ((t = mktime(&tm)) == -1)
> > - errx(EXIT_FAILURE, "%s: mktime", buf);
> > + if ((t = timegm(&tm)) == -1)
> > + errx(EXIT_FAILURE, "%s: timegm", buf);
> > return t;
> > }
> >
>
> Hi Alexandre,
>
> How about this diff instead. This is inspired by OCSP_check_validity() and
> uses ASN1_GENERALIZEDTIME_check() and X509_cmp_time() to do the validity
> check. I think this has a way better chance to produce the expected
> results. My quick testing seems to indicate that it works but review and
> testing is very welcome.
I will commit this diff later today unless somebody speaks up.
> --
> :wq Claudio
>
> Index: mft.c
> ===================================================================
> RCS file: /cvs/src/usr.sbin/rpki-client/mft.c,v
> retrieving revision 1.7
> diff -u -p -r1.7 mft.c
> --- mft.c 13 Aug 2019 13:27:26 -0000 1.7
> +++ mft.c 16 Oct 2019 20:39:36 -0000
> @@ -35,49 +35,57 @@ struct parse {
> struct mft *res; /* result object */
> };
>
> -/*
> - * Convert from the ASN.1 generalised time to a time_t.
> - * Return the time.
> - * This is a stupid requirement due to using ASN1_GENERALIZEDTIME
> - * instead of the native ASN1_TIME functions for comparing time.
> - */
> -static time_t
> -gentime2time(struct parse *p, const ASN1_GENERALIZEDTIME *tp)
> +static const char *
> +gentime2str(const ASN1_GENERALIZEDTIME *time)
> {
> + static char buf[64];
> BIO *mem;
> - char *pp;
> - char buf[64];
> - long len;
> - struct tm tm;
> - time_t t;
>
> if ((mem = BIO_new(BIO_s_mem())) == NULL)
> cryptoerrx("BIO_new");
> - if (!ASN1_GENERALIZEDTIME_print(mem, tp))
> + if (!ASN1_GENERALIZEDTIME_print(mem, time))
> cryptoerrx("ASN1_GENERALIZEDTIME_print");
> + if (BIO_gets(mem, buf, sizeof(buf)) < 0)
> + cryptoerrx("BIO_gets");
>
> - /*
> - * The manpage says nothing about being NUL terminated and
> - * strptime(3) needs a string.
> - * So convert into a static buffer of decent size and NUL
> - * terminate in that way.
> - */
> -
> - len = BIO_get_mem_data(mem, &pp);
> - if (len < 0 || (size_t)len > sizeof(buf) - 1)
> - errx(EXIT_FAILURE, "BIO_get_mem_data");
> -
> - memcpy(buf, pp, len);
> - buf[len] = '\0';
> BIO_free(mem);
> + return buf;
> +}
> +
> +/*
> + * Validate and verify the time validity of the mft.
> + * Returns 1 if all is good, 0 if mft is stale, any other case -1.
> + */
> +static time_t
> +check_validity(const ASN1_GENERALIZEDTIME *from,
> + const ASN1_GENERALIZEDTIME *until, const char *fn, int force)
> +{
> + time_t now = time(NULL);
>
> - memset(&tm, 0, sizeof(struct tm));
> - if (strptime(buf, "%b %d %T %Y %Z", &tm) == NULL)
> - errx(EXIT_FAILURE, "%s: strptime", buf);
> - if ((t = mktime(&tm)) == -1)
> - errx(EXIT_FAILURE, "%s: mktime", buf);
> + if (!ASN1_GENERALIZEDTIME_check(from) ||
> + !ASN1_GENERALIZEDTIME_check(until)) {
> + warnx("%s: embedded time format invalid", fn);
> + return -1;
> + }
> + /* check that until is not before from */
> + if (ASN1_STRING_cmp(until, from) < 0) {
> + warnx("%s: bad update interval", fn);
> + return -1;
> + }
> + /* check that now is not before from */
> + if (X509_cmp_time(from, &now) > 0) {
> + warnx("%s: mft not yet valid %s", fn, gentime2str(from));
> + return -1;
> + }
> + /* check that now is not after until */
> + if (X509_cmp_time(until, &now) < 0) {
> + warnx("%s: mft expired on %s%s", fn, gentime2str(until),
> + force ? " (ignoring)" : "");
> + if (!force)
> + return 0;
> + }
>
> - return t;
> + return 1;
> }
>
> /*
> @@ -229,6 +237,7 @@ mft_parse_econtent(const unsigned char *
> {
> ASN1_SEQUENCE_ANY *seq;
> const ASN1_TYPE *t;
> + const ASN1_GENERALIZEDTIME *from, *until;
> int i, rc = -1;
> time_t this, next, now = time(NULL);
> char buf[64];
> @@ -289,7 +298,7 @@ mft_parse_econtent(const unsigned char *
> p->fn, ASN1_tag2str(t->type), t->type);
> goto out;
> }
> - this = gentime2time(p, t->value.generalizedtime);
> + from = t->value.generalizedtime;
>
> t = sk_ASN1_TYPE_value(seq, i++);
> if (t->type != V_ASN1_GENERALIZEDTIME) {
> @@ -298,26 +307,11 @@ mft_parse_econtent(const unsigned char *
> p->fn, ASN1_tag2str(t->type), t->type);
> goto out;
> }
> - next = gentime2time(p, t->value.generalizedtime);
> + until = t->value.generalizedtime;
>
> - if (this >= next) {
> - warnx("%s: bad update interval", p->fn);
> + rc = check_validity(from, until, p->fn, force);
> + if (rc != 1)
> goto out;
> - } else if (now < this) {
> - warnx("%s: before date interval (clock drift?)", p->fn);
> - goto out;
> - } else if (now >= next) {
> - ctime_r(&next, buf);
> - buf[strlen(buf) - 1] = '\0';
> - if (!force) {
> - if (verbose > 0)
> - warnx("%s: stale: expired %s", p->fn, buf);
> - rc = 0;
> - goto out;
> - }
> - if (verbose > 0)
> - warnx("%s: stale: expired %s (ignoring)", p->fn, buf);
> - }
>
> /* File list algorithm. */
>
>
--
:wq Claudio