3535#include "com_wolfssl_globals.h"
3636#include "com_wolfssl_WolfSSLContext.h"
3737
38- /* global object refs for verify, CRL callbacks */
39- static jobject g_verifyCbIfaceObj ;
40-
38+ /* The CTX-level missing-CRL callback below is process-global. wolfSSL
39+ * CbMissingCRL signature has no SSL/CTX pointer, so native wolfSSL cannot
40+ * route per instance. */
4141#ifdef HAVE_CRL
4242static jobject g_crlCtxCbIfaceObj ;
4343#endif
@@ -498,26 +498,36 @@ JNIEXPORT jint JNICALL Java_com_wolfssl_WolfSSLContext_useCertificateChainFile
498498#endif
499499}
500500
501- JNIEXPORT void JNICALL Java_com_wolfssl_WolfSSLContext_freeContext
502- (JNIEnv * jenv , jobject jcl , jlong ctxPtr )
501+ /* Release process-global CRL ctx callback ref. Called from JNI_OnUnload() */
502+ void NativeWolfSSLContextCleanup (JNIEnv * jenv )
503503{
504- WOLFSSL_CTX * ctx = (WOLFSSL_CTX * )(uintptr_t )ctxPtr ;
505- (void )jenv ;
506- (void )jcl ;
507-
508- /* release verify callback object if set */
509- if (g_verifyCbIfaceObj != NULL ) {
510- (* jenv )-> DeleteGlobalRef (jenv , g_verifyCbIfaceObj );
511- g_verifyCbIfaceObj = NULL ;
504+ if (jenv == NULL ) {
505+ return ;
512506 }
513-
514507#ifdef HAVE_CRL
515- /* release global CRL callback object if set */
516508 if (g_crlCtxCbIfaceObj != NULL ) {
517509 (* jenv )-> DeleteGlobalRef (jenv , g_crlCtxCbIfaceObj );
518510 g_crlCtxCbIfaceObj = NULL ;
519511 }
520- #endif /* HAVE_CRL */
512+ #endif
513+ }
514+
515+ JNIEXPORT void JNICALL Java_com_wolfssl_WolfSSLContext_freeContext
516+ (JNIEnv * jenv , jobject jcl , jlong ctxPtr )
517+ {
518+ WOLFSSL_CTX * ctx = (WOLFSSL_CTX * )(uintptr_t )ctxPtr ;
519+ jobject verifyCb = NULL ;
520+ (void )jcl ;
521+
522+ /* release per-context verify callback jobject if set */
523+ if (ctx != NULL && jenv != NULL && g_verifyCbCtxExDataIdx >= 0 ) {
524+ verifyCb = (jobject )wolfSSL_CTX_get_ex_data (ctx ,
525+ g_verifyCbCtxExDataIdx );
526+ if (verifyCb != NULL ) {
527+ (* jenv )-> DeleteGlobalRef (jenv , verifyCb );
528+ (void )wolfSSL_CTX_set_ex_data (ctx , g_verifyCbCtxExDataIdx , NULL );
529+ }
530+ }
521531
522532 /* wolfSSL checks for null pointer */
523533 wolfSSL_CTX_free (ctx );
@@ -527,31 +537,60 @@ JNIEXPORT void JNICALL Java_com_wolfssl_WolfSSLContext_setVerify(JNIEnv* jenv,
527537 jobject jcl , jlong ctxPtr , jint mode , jobject callbackIface )
528538{
529539 WOLFSSL_CTX * ctx = (WOLFSSL_CTX * )(uintptr_t )ctxPtr ;
540+ jobject prior = NULL ;
541+ jobject newRef = NULL ;
530542 (void )jcl ;
531543
532- if (jenv == NULL ) {
544+ if (jenv == NULL || ctx == NULL ) {
533545 return ;
534546 }
535547
536- /* release verify callback object if set before */
537- if (g_verifyCbIfaceObj != NULL ) {
538- (* jenv )-> DeleteGlobalRef (jenv , g_verifyCbIfaceObj );
539- g_verifyCbIfaceObj = NULL ;
548+ if (g_verifyCbCtxExDataIdx < 0 ) {
549+ /* ex_data slot was not allocated at init, cannot store per-CTX cb */
550+ throwWolfSSLJNIException (jenv ,
551+ "Verify callback ex_data slot not allocated" );
552+ return ;
540553 }
541554
555+ /* Capture the prior per-CTX verify callback jobject for release after
556+ * the new state is fully installed. The slot is only cleared/replaced
557+ * once the new state is committed, so a concurrent handshake firing
558+ * NativeVerifyCallback never sees a transient NULL. */
559+ prior = (jobject )wolfSSL_CTX_get_ex_data (ctx , g_verifyCbCtxExDataIdx );
560+
542561 if (!callbackIface ) {
562+ /* Unhook the native verify callback first so wolfSSL stops routing
563+ * handshakes through it, then clear the ex_data slot. */
543564 wolfSSL_CTX_set_verify (ctx , mode , NULL );
565+ (void )wolfSSL_CTX_set_ex_data (ctx , g_verifyCbCtxExDataIdx , NULL );
544566 }
545567 else {
546- /* store Java verify Interface object */
547- g_verifyCbIfaceObj = (* jenv )-> NewGlobalRef (jenv , callbackIface );
548- if (g_verifyCbIfaceObj == NULL ) {
549- printf ("error storing global callback interface\n" );
568+ /* Store Java verify Interface object as a per-CTX global ref. */
569+ newRef = (* jenv )-> NewGlobalRef (jenv , callbackIface );
570+ if (newRef == NULL ) {
571+ throwWolfSSLJNIException (jenv ,
572+ "Error storing verify callback global reference" );
573+ return ;
574+ }
575+ /* Atomically swap the ex_data slot from prior to newRef. */
576+ if (wolfSSL_CTX_set_ex_data (ctx , g_verifyCbCtxExDataIdx , newRef )
577+ != WOLFSSL_SUCCESS ) {
578+ (* jenv )-> DeleteGlobalRef (jenv , newRef );
579+ throwWolfSSLJNIException (jenv ,
580+ "Error storing verify callback in ctx ex_data" );
581+ return ;
550582 }
551583
552- /* set verify mode, register Java callback with wolfSSL */
584+ /* Ensure the native verify callback is registered. */
553585 wolfSSL_CTX_set_verify (ctx , mode , NativeVerifyCallback );
554586 }
587+
588+ /* Release the prior global ref last. A concurrent handshake that read
589+ * 'prior' from ex_data before the update above will still complete with
590+ * that ref valid for the duration of its callback. */
591+ if (prior != NULL ) {
592+ (* jenv )-> DeleteGlobalRef (jenv , prior );
593+ }
555594}
556595
557596int NativeVerifyCallback (int preverify_ok , WOLFSSL_X509_STORE_CTX * store )
@@ -564,6 +603,9 @@ int NativeVerifyCallback(int preverify_ok, WOLFSSL_X509_STORE_CTX* store)
564603 jclass verifyClass = NULL ;
565604 jmethodID verifyMethod = NULL ;
566605 jobjectRefType refcheck ;
606+ WOLFSSL * ssl = NULL ;
607+ WOLFSSL_CTX * ctx = NULL ;
608+ jobject verifyCbObj = NULL ;
567609
568610 if (!g_vm ) {
569611 /* we can't throw an exception yet, so just return 0 (failure) */
@@ -596,12 +638,38 @@ int NativeVerifyCallback(int preverify_ok, WOLFSSL_X509_STORE_CTX* store)
596638 return -103 ;
597639 }
598640
641+ /* Locate the per-context verify callback jobject via
642+ * store->ssl->ctx->ex_data. The jobject is owned by the WOLFSSL_CTX
643+ * that produced this handshake, so each context fires its own user
644+ * callback. Done after JNIEnv attach so we can throw an exception
645+ * if the slot is unexpectedly empty. */
646+ if (g_verifyCbCtxExDataIdx >= 0 && store != NULL ) {
647+ ssl = (WOLFSSL * )wolfSSL_X509_STORE_CTX_get_ex_data (store ,
648+ wolfSSL_get_ex_data_X509_STORE_CTX_idx ());
649+ if (ssl != NULL ) {
650+ ctx = wolfSSL_get_SSL_CTX (ssl );
651+ }
652+ if (ctx != NULL ) {
653+ verifyCbObj = (jobject )wolfSSL_CTX_get_ex_data (ctx ,
654+ g_verifyCbCtxExDataIdx );
655+ }
656+ }
657+ if (verifyCbObj == NULL ) {
658+ (* jenv )-> ThrowNew (jenv , excClass ,
659+ "No Java verify callback registered for WOLFSSL_CTX in "
660+ "NativeVerifyCallback" );
661+ if (needsDetach ) {
662+ (* g_vm )-> DetachCurrentThread (g_vm );
663+ }
664+ return -1 ;
665+ }
666+
599667 /* check if our stored object reference is valid */
600- refcheck = (* jenv )-> GetObjectRefType (jenv , g_verifyCbIfaceObj );
668+ refcheck = (* jenv )-> GetObjectRefType (jenv , verifyCbObj );
601669 if (refcheck == 2 ) {
602670
603671 /* lookup WolfSSLVerifyCallback class from global object ref */
604- verifyClass = (* jenv )-> GetObjectClass (jenv , g_verifyCbIfaceObj );
672+ verifyClass = (* jenv )-> GetObjectClass (jenv , verifyCbObj );
605673 if (!verifyClass ) {
606674 if ((* jenv )-> ExceptionOccurred (jenv )) {
607675 (* jenv )-> ExceptionDescribe (jenv );
@@ -630,7 +698,7 @@ int NativeVerifyCallback(int preverify_ok, WOLFSSL_X509_STORE_CTX* store)
630698 return -105 ;
631699 }
632700
633- retval = (* jenv )-> CallIntMethod (jenv , g_verifyCbIfaceObj ,
701+ retval = (* jenv )-> CallIntMethod (jenv , verifyCbObj ,
634702 verifyMethod , preverify_ok , (jlong )(uintptr_t )store );
635703
636704 if ((* jenv )-> ExceptionOccurred (jenv )) {
0 commit comments