Skip to content

Explicitly trusting a CA cached as WOLFSSL_CHAIN_CA leaves it removable #12019

Closed as not planned

Description

@ejohnstown

An application that explicitly trusts a CA after wolfSSL has already cached that same CA from a peer chain cannot make it a trusted root. The signer keeps its WOLFSSL_CHAIN_CA type, so a later wolfSSL_CertManagerUnloadIntermediateCerts() removes a CA the application loaded as trusted. Both loads return WOLFSSL_SUCCESS, so the application has no way to tell.

This applies to wolfSSL_CertManagerLoadCABuffer() (and the CTX load-verify paths) and to wolfSSL_CertManagerLoadCABufferType(..., WOLFSSL_USER_CA).

Cause

  • AddCA() finds a signer with the same subject key ID ("Already have this CA, not adding again") and leaves its type unchanged.
  • wolfSSL_CertManagerLoadCABufferType() calls SetCAType() only when type != WOLFSSL_USER_CA, so an explicit WOLFSSL_USER_CA load is a no-op on an existing signer. SetCAType() is WOLFSSL_LOCAL, so applications cannot retag the signer themselves.

Reproduction

wolfSSL 5.9.4-stable (--enable-all). Master as of 44fa143 has the same type != WOLFSSL_USER_CA check. Uses the test certs in certs/; ca-int2-cert.pem is signed by ca-int-cert.pem.

WOLFSSL_CERT_MANAGER* cm = wolfSSL_CertManagerNew();

/* trusted root */
wolfSSL_CertManagerLoadCABuffer(cm, root, rootSz, WOLFSSL_FILETYPE_PEM);
/* ca-int cached from a peer chain, as TLS does */
wolfSSL_CertManagerLoadCABufferType(cm, inter, interSz, WOLFSSL_FILETYPE_PEM,
        0, WOLFSSL_LOAD_VERIFY_DEFAULT_FLAGS, WOLFSSL_CHAIN_CA);
/* application later trusts ca-int explicitly; returns WOLFSSL_SUCCESS */
wolfSSL_CertManagerLoadCABufferType(cm, inter, interSz, WOLFSSL_FILETYPE_PEM,
        0, WOLFSSL_LOAD_VERIFY_DEFAULT_FLAGS, WOLFSSL_USER_CA);
wolfSSL_CertManagerUnloadIntermediateCerts(cm);
/* ca-int2, signed by ca-int */
ret = wolfSSL_CertManagerVerifyBuffer(cm, leaf, leafSz, WOLFSSL_FILETYPE_PEM);

Output:

explicit trust via wolfSSL_CertManagerLoadCABuffer:
  verify cert signed by int: -188 (certificate verify failed)
explicit trust via wolfSSL_CertManagerLoadCABufferType(USER_CA):
  verify cert signed by int: -188 (certificate verify failed)
control, no CHAIN_CA load first:
  verify cert signed by int: 1 (ok)

Every load and the unload return WOLFSSL_SUCCESS (1).

Suggested fix

When an explicit trust load finds an existing signer with the same key ID, retag it WOLFSSL_USER_CA. For example, wolfSSL_CertManagerLoadCABufferType() could call SetCAType() for WOLFSSL_USER_CA as well, and the untyped load paths could do the same. Promoting a chain CA to a user CA is safe, because the application asked to trust it. The reverse direction, retagging an existing user CA as a chain CA, is what the current short-circuit protects against and should stay as it is.

Found while adding chain-CA support to wolfSSH's certificate manager (wolfSSL/wolfssh#1338). wolfSSH documents load ordering as a workaround for now.

Activity

  1. ejohnstown commented on Nov 5, 2026

    @ejohnstown
    ContributorAuthor

    I did not intend this to become an issue.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions