On Wed, Feb 24, 2021 at 09:21:56PM +0100, Theo Buehler wrote:
> On Wed, Feb 24, 2021 at 09:00:05PM +0100, Theo Buehler wrote:
> > On Wed, Feb 24, 2021 at 06:47:00AM +0100, Jan Klemkow wrote:
> > > another co-worker of mine has found an other regress in the LibreSSL
> > > legacy verifier. I took his diff and made a test for our regression
> > > framework.
> > >
> > > The legacy verifier seems not to check the certificate if no root CA was
> > > given. The following test creates an expired certificate and tries to
> > > verify it. In one case it found the expected error in another, it does
> > > not.
> >
> > Thanks for the report and the test case, that's very helpful. The diff
> > at the end addresses this.
> >
> > The verifier does not find the expected error because it now bails out
> > earlier. This is a consequence of a refactoring of X509_verify_cert()
> > (x509_vfy.c r1.75) that was done to integrate the new verifier.
> >
> > https://cvsweb.openbsd.org/cgi-bin/cvsweb/src/lib/libcrypto/x509/x509_vfy.c.diff?r1=1.74&r2=1.75
> >
> > What happens is that x509_legacy_verify_build_chain() returns ok == 0 in
> > your test case. The safety net at the end of x509_verify_cert_legacy()
> > sets ctx->error to X509_V_ERR_UNSPECIFIED (so the unchecked call to
> > X509_verify_cert() in your regress test actually indicates verification
> > failure).
> >
> >
> > The diff below restores the previous behavior and fixes a bug.
> >
> > Prior to the the refactoring, each 'goto end' in the code that is now in
> > x509_legacy_verify_build_chain() would stop validation, while in other
> > cases validation would have carried on. So indicate this via the return
> > value and return ok via a pointer.
> >
> > The bug is that the return value check of x509_legacy_verify_build_chain()
> > should have been if (ok <= 0), not if (!ok).
> >
> > Regarding your regress diff: I don't think we want to land it as it is.
Ok.
> > The verifier lives in libcrypto/x509, so the regress test belongs in
> > there.
You are right, its the better place. At least I want to send you a bug
report with concrete code to test.
> > We really need to come up with an extensible design that can check a
> > number of such cases (and ideally includes the bulk of your openssl/x509
> > regress tests). I don't want to add a directory for every bug in the
> > verifier or legacy verifier. As jsing already mentioned, I expect that
> > we want to commit the test certs so we don't need perl modules from
> > ports to run the regress. Then we want to add generating scripts and a
> > README that gives instructions on how to regenerate the certs if needed.
> >
> > I would like to have one C program that runs all tests in a loop (or
> > perhaps one C program per family of regressions). It would be nice if
> > this C program could also be compiled against OpenSSL 1.1.1 so we can
> > easily check for differences of behavior (see x509/bettertls/Makefile
> > for an example that does this). For your test program this just means:
> > don't access csc->blah, but use X509_STORE_CTX_get_blah(csc) instead.
> >
> > Why does the test set TRUSTED_FIRST?
I just forget to remove the this line, from the original version.
> > The code also needs a bit of cleaning. There are a number of unchecked
> > return values, for example strdup and sk_*_push, and csc is leaked
> > after X509_verify_cert().
> >
> > It would also be nice to run this test against the new verifier.
The test passes with the new verifier in current, but not in 6.8.
> Missed an obvious simplification.
The diff looks fine to me and it fixes our regressions.
I would give you an OK jan, fwiw.
Thanks,
Jan
> Index: x509/x509_vfy.c
> ===================================================================
> RCS file: /cvs/src/lib/libcrypto/x509/x509_vfy.c,v
> retrieving revision 1.85
> diff -u -p -r1.85 x509_vfy.c
> --- x509/x509_vfy.c 11 Feb 2021 04:56:43 -0000 1.85
> +++ x509/x509_vfy.c 24 Feb 2021 20:19:34 -0000
> @@ -240,12 +240,13 @@ x509_vfy_check_id(X509_STORE_CTX *ctx) {
> * Oooooooh..
> */
> static int
> -X509_verify_cert_legacy_build_chain(X509_STORE_CTX *ctx, int *bad)
> +X509_verify_cert_legacy_build_chain(X509_STORE_CTX *ctx, int *bad, int
> *out_ok)
> {
> X509 *x, *xtmp, *xtmp2, *chain_ss = NULL;
> int bad_chain = 0;
> X509_VERIFY_PARAM *param = ctx->param;
> - int depth, i, ok = 0;
> + int ok = 0, ret = 0;
> + int depth, i;
> int num, j, retry, trust;
> int (*cb) (int xok, X509_STORE_CTX *xctx);
> STACK_OF(X509) *sktmp = NULL;
> @@ -517,11 +518,15 @@ X509_verify_cert_legacy_build_chain(X509
> if (!ok)
> goto end;
> }
> +
> + ret = 1;
> end:
> sk_X509_free(sktmp);
> X509_free(chain_ss);
> *bad = bad_chain;
> - return ok;
> + *out_ok = ok;
> +
> + return ret;
> }
>
> static int
> @@ -531,8 +536,7 @@ X509_verify_cert_legacy(X509_STORE_CTX *
>
> ctx->error = X509_V_OK; /* Initialize to OK */
>
> - ok = X509_verify_cert_legacy_build_chain(ctx, &bad_chain);
> - if (!ok)
> + if (!X509_verify_cert_legacy_build_chain(ctx, &bad_chain, &ok))
> goto end;
>
> /* We have the chain complete: now we need to check its purpose */
>