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