Repository navigation
Description
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()callsSetCAType()only whentype != WOLFSSL_USER_CA, so an explicitWOLFSSL_USER_CAload is a no-op on an existing signer.SetCAType()isWOLFSSL_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
ejohnstown commentedon Nov 5, 2026
I did not intend this to become an issue.