Skip to content

Commit 8e1a905

Browse files
committed
JNI/JSSE: fix global ref lifecycle for verify and missing-CRL callbacks
1 parent 1bea4be commit 8e1a905

5 files changed

Lines changed: 229 additions & 39 deletions

File tree

native/com_wolfssl_WolfSSL.c

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,10 @@ jmethodID g_bufferArrayOffsetMethodId = NULL;
7171
jmethodID g_bufferSetPositionMethodId = NULL;
7272
jmethodID g_verifyCallbackMethodId = NULL;
7373

74+
/* WOLFSSL_CTX ex_data slot for per-context verify callback jobject.
75+
* -1 means not allocated. Populated in Java_com_wolfssl_WolfSSL_init(). */
76+
int g_verifyCbCtxExDataIdx = -1;
77+
7478
#ifdef HAVE_FIPS
7579
/* global object ref for FIPS error callback */
7680
static jobject g_fipsCbIfaceObj;
@@ -230,6 +234,10 @@ JNIEXPORT void JNICALL JNI_OnUnload(JavaVM* vm, void* reserved)
230234
return;
231235
}
232236

237+
/* Release process global jobject refs at CTX and session levels */
238+
NativeWolfSSLContextCleanup(env);
239+
NativeWolfSSLSessionCleanup(env);
240+
233241
/* Clear cached method ID */
234242
g_sslIORecvMethodId = NULL;
235243
g_sslIORecvMethodId_BB = NULL;
@@ -431,7 +439,18 @@ JNIEXPORT jint JNICALL Java_com_wolfssl_WolfSSL_init
431439
#endif /* HAVE_FIPS && HAVE_FIPS_VERSION == 5 */
432440

433441
if (ret == 0) {
434-
return (jint)wolfSSL_Init();
442+
ret = (int)wolfSSL_Init();
443+
if (ret == WOLFSSL_SUCCESS && g_verifyCbCtxExDataIdx < 0) {
444+
/* Allocate WOLFSSL_CTX ex_data slot for per-context verify
445+
* callback jobject. Done once at library init so index is shared
446+
* by all WolfSSLContext instances. If allocation fails,
447+
* setVerify() will return an error. */
448+
int idx = wolfSSL_CTX_get_ex_new_index(0, NULL, NULL, NULL, NULL);
449+
if (idx >= 0) {
450+
g_verifyCbCtxExDataIdx = idx;
451+
}
452+
}
453+
return (jint)ret;
435454
} else {
436455
return (jint)WOLFSSL_FAILURE;
437456
}

native/com_wolfssl_WolfSSLContext.c

Lines changed: 97 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -35,9 +35,9 @@
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
4242
static 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

557596
int 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)) {

native/com_wolfssl_WolfSSLSession.c

Lines changed: 19 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -73,7 +73,9 @@ int NativeSessionTicketCb(WOLFSSL *ssl, const unsigned char* ticket,
7373
#endif
7474

7575
#ifdef HAVE_CRL
76-
/* global object refs for CRL callback */
76+
/* Process-global jobject for the missing-CRL callback registered via
77+
* Java_com_wolfssl_WolfSSLSession_setCRLCb(). wolfSSL CbMissingCRL has no
78+
* SSL/CTX pointer, so native wolfSSL cannot route per-instance. */
7779
static jobject g_crlCbIfaceObj;
7880
#endif
7981

@@ -1756,6 +1758,22 @@ JNIEXPORT jint JNICALL Java_com_wolfssl_WolfSSLSession_accept
17561758
return ret;
17571759
}
17581760

1761+
/* Release process-global session-level missing-CRL callback ref. Called
1762+
* from JNI_OnUnload() in com_wolfssl_WolfSSL.c so the global is torn
1763+
* down at native library unload. */
1764+
void NativeWolfSSLSessionCleanup(JNIEnv* jenv)
1765+
{
1766+
if (jenv == NULL) {
1767+
return;
1768+
}
1769+
#ifdef HAVE_CRL
1770+
if (g_crlCbIfaceObj != NULL) {
1771+
(*jenv)->DeleteGlobalRef(jenv, g_crlCbIfaceObj);
1772+
g_crlCbIfaceObj = NULL;
1773+
}
1774+
#endif
1775+
}
1776+
17591777
JNIEXPORT void JNICALL Java_com_wolfssl_WolfSSLSession_freeSSL
17601778
(JNIEnv* jenv, jobject jcl, jlong sslPtr)
17611779
{
@@ -1857,14 +1875,6 @@ JNIEXPORT void JNICALL Java_com_wolfssl_WolfSSLSession_freeSSL
18571875
return;
18581876
}
18591877

1860-
#ifdef HAVE_CRL
1861-
/* release global CRL callback ref if registered */
1862-
if (g_crlCbIfaceObj != NULL) {
1863-
(*jenv)->DeleteGlobalRef(jenv, g_crlCbIfaceObj);
1864-
g_crlCbIfaceObj = NULL;
1865-
}
1866-
#endif
1867-
18681878
#if defined(HAVE_PK_CALLBACKS)
18691879
#ifdef HAVE_ECC
18701880
/* free ECC sign callback CTX global reference if set */

native/com_wolfssl_globals.h

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,12 @@ extern jmethodID g_bufferArrayOffsetMethodId; /* ByteBuffer.arrayOffset() */
4646
extern jmethodID g_bufferSetPositionMethodId; /* ByteBuffer.position(int) */
4747
extern jmethodID g_verifyCallbackMethodId; /* WolfSSLVerifyCallback.verifyCallback */
4848

49+
/* WOLFSSL_CTX ex_data index used to store the per-WolfSSLContext jobject ref
50+
* to the user WolfSSLVerifyCallback. Allocated once in
51+
* Java_com_wolfssl_WolfSSL_init via wolfSSL_CTX_get_ex_new_index(). A
52+
* negative value means the slot has not yet been allocated. */
53+
extern int g_verifyCbCtxExDataIdx;
54+
4955
/* struct to hold I/O class, object refs */
5056
typedef struct {
5157
int active;
@@ -61,4 +67,8 @@ unsigned int NativePskServerCb(WOLFSSL* ssl, const char* identity,
6167
void throwWolfSSLJNIException(JNIEnv* jenv, const char* msg);
6268
void throwWolfSSLException(JNIEnv* jenv, const char* msg);
6369

70+
/* Release process-global jobject refs. Called from JNI_OnUnload() */
71+
void NativeWolfSSLContextCleanup(JNIEnv* jenv);
72+
void NativeWolfSSLSessionCleanup(JNIEnv* jenv);
73+
6474
#endif

0 commit comments

Comments
 (0)