Skip to content

[Bug]: Failed validation against the CAs loaded into WolfSSL doesn't trigger validation using Apple's API #9604

@arekmula

Description

@arekmula

Contact Details

[email protected]

Version

5.8.4

Description

Configure flags:

./configure flags: --disable-shared --enable-static --prefix=/ '--bindir=${prefix}/bin' '--sbindir=${prefix}/bin' '--libdir=${prefix}/lib' '--includedir=${prefix}/include' '--oldincludedir=${prefix}/include' --disable-examples --disable-crypttests --enable-harden --enable-debug=yes --enable-opensslall=yes --enable-opensslextra=yes --enable-sslv3=no --enable-alpn=no --enable-des3=no --enable-tls13=yes --enable-certgen=no --enable-dsa=no --enable-ripemd=no --enable-sessioncerts=no --enable-sni=no --enable-testcert=no --enable-shared=no --enable-static=yes --enable-asio --enable-altcertchains --enable-sys-ca-certs CC=/opt/homebrew/opt/llvm@20/bin/clang 'CFLAGS= -fPIC -g -mmacosx-version-min=15.0 -DWOLFSSL_APPLE_NATIVE_CERT_VALIDATION -framework CoreFoundation -framework Security' 'LDFLAGS= -mmacosx-version-min=15.0' CPPFLAGS=

Hey,

When the support for System Certificate Store is enabled on Apple platforms, and the validation of the peer cert chain against the CAs loaded into wolfSSL fails, wolfSSL wants to validate it against the system certificates using Apple's native trust APIs:

#if defined(__APPLE__) && defined(WOLFSSL_SYS_CA_CERTS)
    /* If we can't validate the peer cert chain against the CAs loaded
     * into wolfSSL, try to validate against the system certificates
     * using Apple's native trust APIs */
    if ((ret == WC_NO_ERR_TRACE(ASN_NO_SIGNER_E)) &&
        (ssl->ctx->doAppleNativeCertValidationFlag)) {
        if (DoAppleNativeCertValidation(ssl, args->certs,
                                             args->totalCerts)) {
            WOLFSSL_MSG("Apple native cert chain validation SUCCESS");
            ret = 0;
        }
        else {
            WOLFSSL_MSG("Apple native cert chain validation FAIL");
        }
    }
#endif /* defined(__APPLE__) && defined(WOLFSSL_SYS_CA_CERTS) */

    /* Do leaf verify callback when it wasn't called yet */
    if (ret == 0 || ret != args->leafVerifyErr)
        ret = DoVerifyCallback(SSL_CM(ssl), ssl, ret, args);

To make it happen, the ret must be equal to ASN_NO_SIGNER_E. However, the value will never be equal to ASN_NO_SIGNER_E if the verify callback is set and returns true because it will be overwritten here:

/* Do verify callback. */
  args->leafVerifyErr = ret =
          DoVerifyCallback(SSL_CM(ssl), ssl, ret, args);

  #if defined(__APPLE__) && defined(WOLFSSL_SYS_CA_CERTS)
  /* Disregard failure to verify peer cert, as we will verify
   * the whole chain with the native API later */
  if (ssl->ctx->doAppleNativeCertValidationFlag) {
      WOLFSSL_MSG("\tApple native CA validation override"
                  " available, will continue");
      /* check if fatal error */
      args->fatal = (args->verifyErr) ? 1 : 0;
      if (args->fatal)
          DoCertFatalAlert(ssl, ret);
  }

and the native cert validation will never be called.

The value of ret may be retained only if the verify callback fails, but then args->verifyErr is set and we end up here:

  args->fatal = (args->verifyErr) ? 1 : 0;
  if (args->fatal)
      DoCertFatalAlert(ssl, ret);

The ret can also be retained if the verify callback is not set at all.

Is it intended behaviour? It was introduced within this #9144.

I believe it can be fixed like this:

  /* Do verify callback. */
  args->leafVerifyErr = ret =
          DoVerifyCallback(SSL_CM(ssl), ssl, ret, args);

  #if defined(__APPLE__) && defined(WOLFSSL_SYS_CA_CERTS)
  /* Disregard failure to verify peer cert, as we will verify
   * the whole chain with the native API later */
  if (ssl->ctx->doAppleNativeCertValidationFlag) {
      WOLFSSL_MSG("\tApple native CA validation override"
                  " available, will continue");

      /* check if fatal error */
      if (ret != 0 && ret != WC_NO_ERR_TRACE(ASN_NO_SIGNER_E)) {
          args->fatal = (args->verifyErr) ? 1 : 0;
          if (args->fatal)
              DoCertFatalAlert(ssl, ret);
      }
  }

Reproduction steps

No response

Relevant log output

Metadata

Metadata

Assignees

Labels

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions