@@ -785,6 +785,127 @@ static int test_wolfSSL_X509_STORE_CTX_ex11(X509_STORE_test_data *testData)
785785 return EXPECT_RESULT ();
786786}
787787
788+ static int test_wolfSSL_X509_STORE_CTX_ex_partial_chain_neg (
789+ X509_STORE_test_data * testData )
790+ {
791+ EXPECT_DECLS ;
792+ X509_STORE * store = NULL ;
793+ X509_STORE_CTX * ctx = NULL ;
794+ STACK_OF (X509 )* untrusted = NULL ;
795+
796+ /* Negative partial-chain test: with X509_V_FLAG_PARTIAL_CHAIN set, the
797+ * intermediates are supplied ONLY as untrusted (passed through the
798+ * X509_STORE_CTX_init "chain" argument and never added to the store).
799+ * No certificate in the chain is in the store, so verification must
800+ * fail. Pre-fix, wolfSSL_X509_verify_cert would incorrectly accept
801+ * this chain because its partial-chain fallback only checked that some
802+ * intermediate had been temporarily loaded into the CertManager, not
803+ * that any chain certificate was actually trusted. */
804+ ExpectNotNull (store = X509_STORE_new ());
805+ /* Intentionally do NOT add x509CaInt, x509CaInt2, or x509Ca. */
806+ ExpectIntEQ (X509_STORE_set_flags (store , X509_V_FLAG_PARTIAL_CHAIN ), 1 );
807+
808+ ExpectNotNull (untrusted = sk_X509_new_null ());
809+ ExpectIntGT (sk_X509_push (untrusted , testData -> x509CaInt2 ), 0 );
810+ ExpectIntGT (sk_X509_push (untrusted , testData -> x509CaInt ), 0 );
811+
812+ ExpectNotNull (ctx = X509_STORE_CTX_new ());
813+ ExpectIntEQ (X509_STORE_CTX_init (ctx , store , testData -> x509Leaf , untrusted ),
814+ 1 );
815+ /* Must NOT verify: partial-chain does not relax the trust requirement. */
816+ ExpectIntNE (X509_verify_cert (ctx ), 1 );
817+ /* Verify the failure is specifically due to missing trust anchor, not
818+ * some unrelated error. */
819+ ExpectIntEQ (X509_STORE_CTX_get_error (ctx ),
820+ X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT_LOCALLY );
821+
822+ X509_STORE_CTX_free (ctx );
823+ X509_STORE_free (store );
824+ sk_X509_free (untrusted );
825+ return EXPECT_RESULT ();
826+ }
827+
828+ static int test_wolfSSL_X509_STORE_CTX_ex_partial_chain_mixed (
829+ X509_STORE_test_data * testData )
830+ {
831+ EXPECT_DECLS ;
832+ X509_STORE * store = NULL ;
833+ X509_STORE_CTX * ctx = NULL ;
834+ STACK_OF (X509 )* untrusted = NULL ;
835+
836+ /* Mixed trusted-store + untrusted-chain partial-chain test: the store
837+ * trusts an intermediate (x509CaInt2, the leaf's direct issuer), while
838+ * an additional intermediate (x509CaInt) is supplied only as untrusted
839+ * via the chain argument. With X509_V_FLAG_PARTIAL_CHAIN, verification
840+ * must succeed by terminating at the trusted intermediate. This test
841+ * exercises the snapshot-based trust check in X509StoreCertIsTrusted:
842+ * the untrusted intermediate injected during verification must not be
843+ * treated as a trust anchor, but the intermediate already in the store
844+ * must be. */
845+ ExpectNotNull (store = X509_STORE_new ());
846+ ExpectIntEQ (X509_STORE_add_cert (store , testData -> x509CaInt2 ), 1 );
847+ ExpectIntEQ (X509_STORE_set_flags (store , X509_V_FLAG_PARTIAL_CHAIN ), 1 );
848+
849+ ExpectNotNull (untrusted = sk_X509_new_null ());
850+ ExpectIntGT (sk_X509_push (untrusted , testData -> x509CaInt ), 0 );
851+
852+ ExpectNotNull (ctx = X509_STORE_CTX_new ());
853+ ExpectIntEQ (X509_STORE_CTX_init (ctx , store , testData -> x509Leaf , untrusted ),
854+ 1 );
855+ /* Must verify: chain terminates at trusted intermediate in the store. */
856+ ExpectIntEQ (X509_verify_cert (ctx ), 1 );
857+
858+ X509_STORE_CTX_free (ctx );
859+ X509_STORE_free (store );
860+ sk_X509_free (untrusted );
861+ return EXPECT_RESULT ();
862+ }
863+
864+ static int test_wolfSSL_X509_STORE_CTX_ex_partial_chain_untrusted_terminal (
865+ X509_STORE_test_data * testData )
866+ {
867+ EXPECT_DECLS ;
868+ X509_STORE * store = NULL ;
869+ X509_STORE_CTX * ctx = NULL ;
870+ STACK_OF (X509 )* untrusted = NULL ;
871+
872+ /* Partial-chain boundary test: the store trusts a CA (x509Ca) that is
873+ * NOT reachable from the leaf given the supplied untrusted intermediates,
874+ * and an untrusted intermediate (x509CaInt2) IS the terminal of the
875+ * (truncated) chain. With X509_V_FLAG_PARTIAL_CHAIN set, verification
876+ * must FAIL because the chain terminates at an untrusted certificate.
877+ *
878+ * This test specifically targets the snapshot-based trust check in
879+ * X509StoreCertIsTrusted. Before addAllButSelfSigned injects
880+ * x509CaInt2, origTrustedSk is snapshotted from the caller-trusted set
881+ * and contains only x509Ca. When the chain terminates at x509CaInt2,
882+ * the trust check consults origTrustedSk (not the mutated working
883+ * stack) and correctly finds no match. A regression that consulted
884+ * the post-injection working stack instead of the snapshot would
885+ * incorrectly mark x509CaInt2 as trusted and cause verification to
886+ * succeed. */
887+ ExpectNotNull (store = X509_STORE_new ());
888+ ExpectIntEQ (X509_STORE_add_cert (store , testData -> x509Ca ), 1 );
889+ ExpectIntEQ (X509_STORE_set_flags (store , X509_V_FLAG_PARTIAL_CHAIN ), 1 );
890+
891+ /* Only x509CaInt2 supplied as untrusted; x509CaInt is intentionally
892+ * withheld so the chain cannot actually reach the trusted x509Ca. */
893+ ExpectNotNull (untrusted = sk_X509_new_null ());
894+ ExpectIntGT (sk_X509_push (untrusted , testData -> x509CaInt2 ), 0 );
895+
896+ ExpectNotNull (ctx = X509_STORE_CTX_new ());
897+ ExpectIntEQ (X509_STORE_CTX_init (ctx , store , testData -> x509Leaf , untrusted ),
898+ 1 );
899+ /* Must NOT verify: the chain terminal (x509CaInt2) is not in the
900+ * original trust set, even though the store is non-empty. */
901+ ExpectIntNE (X509_verify_cert (ctx ), 1 );
902+
903+ X509_STORE_CTX_free (ctx );
904+ X509_STORE_free (store );
905+ sk_X509_free (untrusted );
906+ return EXPECT_RESULT ();
907+ }
908+
788909#ifdef HAVE_ECC
789910static int test_wolfSSL_X509_STORE_CTX_ex12 (void )
790911{
@@ -870,6 +991,12 @@ int test_wolfSSL_X509_STORE_CTX_ex(void)
870991 ExpectIntEQ (test_wolfSSL_X509_STORE_CTX_ex9 (& testData ), 1 );
871992 ExpectIntEQ (test_wolfSSL_X509_STORE_CTX_ex10 (& testData ), 1 );
872993 ExpectIntEQ (test_wolfSSL_X509_STORE_CTX_ex11 (& testData ), 1 );
994+ ExpectIntEQ (test_wolfSSL_X509_STORE_CTX_ex_partial_chain_neg (& testData ), 1 );
995+ ExpectIntEQ (test_wolfSSL_X509_STORE_CTX_ex_partial_chain_mixed (& testData ),
996+ 1 );
997+ ExpectIntEQ (
998+ test_wolfSSL_X509_STORE_CTX_ex_partial_chain_untrusted_terminal (
999+ & testData ), 1 );
8731000#ifdef HAVE_ECC
8741001 ExpectIntEQ (test_wolfSSL_X509_STORE_CTX_ex12 (), 1 );
8751002#endif
0 commit comments