mirror of
https://github.com/wolfSSL/wolfssl.git
synced 2026-10-24 04:09:07 +03:00
Adds an opt-in callback that takes over the peer certificate trust decision. wolfSSL builds no chain, verifies no signature and checks no date, revocation status, key usage or host name; the verify callback is not called either. The callback gets only the DER certificates from the Certificate message as WOLFSSL_BUFFER_INFO entries, certs[0] first. It is consulted even under WOLFSSL_VERIFY_NONE, and its rejection fails the handshake either way. wolfSSL still decodes every certificate with ParseCert(NO_VERIFY) before the callback runs, so malformed DER fails the handshake and the callback is never handed it. Content wolfSSL does not understand - an unknown critical extension, an unsupported key or signature algorithm, a signature algorithm mismatch - is not a decoding failure and is left for the callback to judge. The check runs once, not again on re-entry after a deferred verdict. The callback may return CHAIN_VERIFY_WANT_E to suspend the handshake and be asked again with the same certificates when the application re-enters wolfSSL_connect(), wolfSSL_accept(), wolfSSL_read() or wolfSSL_write(). A certificate received after the handshake (TLS 1.3 post-handshake authentication) arrives inside wolfSSL_read() and is resumed by reading again: ReceiveData() re-enters on any handshake-suspend error, and the ticket-sent accept state leaves a suspended message alone. Rejection fails with CHAIN_VERIFY_CB_E and one bad_certificate alert, and sets peerVerifyRet so wolfSSL_get_verify_result() does not report X509_V_OK. Still applied: an empty Certificate message keeps the existing mutual-auth policy (the callback is not called for it), and the minimum peer key sizes still gate the peer's own key, which the handshake uses directly. DTLS, raw public keys and OCSP stapling are not supported with the callback. wolfSSL_CTX_SetChainVerifyCb() and wolfSSL_SetChainVerifyCb() refuse with CHAIN_VERIFY_UNSUPPORTED_E when the context or object is already configured for one of them, and a connection that uses one of them anyway fails with the same error and an internal_error alert before the callback is called. The DTLS paths are otherwise untouched. The callback and its user context can be set on the context or on the SSL object, the object's taking precedence. The feature is part of --enable-all, has a CMake option and Doxygen entries. The suspend machinery is shared with WOLFSSL_NONBLOCK_OCSP and WOLFSSL_ASYNC_CRYPT. The open-coded pairs of comparisons against WC_PENDING_E and OCSP_WANT_READ are replaced by IsHsSuspendErr(), and the matching build guards by WOLFSSL_HAVE_HS_SUSPEND. That also closes three holes which affected WOLFSSL_NONBLOCK_OCSP builds: - DoHandShakeMsg() freed ssl->pendingMsg on a non-WC_PENDING_E suspend while ProcPeerCertArgs still pointed into it. - wolfSSL_connect()/wolfSSL_accept() and their TLS 1.3 counterparts called FreeAsyncCtx() when buffered output flushed, discarding the saved certificate state. - ProcessPeerCerts() XFREE'd args in WOLFSSL_SMALL_STACK builds without WOLFSSL_ASYNC_CRYPT, where args points inside ssl->async.