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:
> > Hi,
> > 
> > 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.
> 
> The verifier lives in libcrypto/x509, so the regress test belongs in
> there.
> 
> 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?
> 
> 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.

Missed an obvious simplification.

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 */

Reply via email to