Merge pull request #369 from cconlon/crlVerifyCbRefLifecycle
Fix global ref lifecycle for verify and missing-CRL callbackspull/371/head
commit
bf065a479f
|
|
@ -71,6 +71,10 @@ jmethodID g_bufferArrayOffsetMethodId = NULL;
|
|||
jmethodID g_bufferSetPositionMethodId = NULL;
|
||||
jmethodID g_verifyCallbackMethodId = NULL;
|
||||
|
||||
/* WOLFSSL_CTX ex_data slot for per-context verify callback jobject.
|
||||
* -1 means not allocated. Populated in Java_com_wolfssl_WolfSSL_init(). */
|
||||
int g_verifyCbCtxExDataIdx = -1;
|
||||
|
||||
#ifdef HAVE_FIPS
|
||||
/* global object ref for FIPS error callback */
|
||||
static jobject g_fipsCbIfaceObj;
|
||||
|
|
@ -215,6 +219,19 @@ JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM* vm, void* reserved)
|
|||
(*env)->DeleteLocalRef(env, byteBufferClass);
|
||||
(*env)->DeleteLocalRef(env, verifyClass);
|
||||
|
||||
/* Initialize the mutex serializing verify callback jobject reads/updates.
|
||||
* Done here to guarantee race-free init. */
|
||||
if (NativeVerifyCbMutexInit() != 0) {
|
||||
return JNI_ERR;
|
||||
}
|
||||
|
||||
/* Initialize the mutex serializing missing-CRL callback jobject
|
||||
* reads/updates. */
|
||||
if (NativeCrlCbMutexInit() != 0) {
|
||||
NativeVerifyCbMutexFree();
|
||||
return JNI_ERR;
|
||||
}
|
||||
|
||||
return JNI_VERSION_1_6;
|
||||
}
|
||||
|
||||
|
|
@ -230,6 +247,16 @@ JNIEXPORT void JNICALL JNI_OnUnload(JavaVM* vm, void* reserved)
|
|||
return;
|
||||
}
|
||||
|
||||
/* Release process global jobject refs at CTX and session levels. */
|
||||
NativeWolfSSLContextCleanup(env);
|
||||
NativeWolfSSLSessionCleanup(env);
|
||||
|
||||
/* Free the verify callback synchronization mutex. */
|
||||
NativeVerifyCbMutexFree();
|
||||
|
||||
/* Free the missing-CRL callback synchronization mutex. */
|
||||
NativeCrlCbMutexFree();
|
||||
|
||||
/* Clear cached method ID */
|
||||
g_sslIORecvMethodId = NULL;
|
||||
g_sslIORecvMethodId_BB = NULL;
|
||||
|
|
@ -431,7 +458,16 @@ JNIEXPORT jint JNICALL Java_com_wolfssl_WolfSSL_init
|
|||
#endif /* HAVE_FIPS && HAVE_FIPS_VERSION == 5 */
|
||||
|
||||
if (ret == 0) {
|
||||
return (jint)wolfSSL_Init();
|
||||
ret = (int)wolfSSL_Init();
|
||||
if (ret == WOLFSSL_SUCCESS) {
|
||||
/* Allocate the per-context verify callback ex_data slot if not
|
||||
* allocated yet. Guarded by g_verifyCbMutex (initialized in
|
||||
* JNI_OnLoad()). */
|
||||
if (NativeVerifyCbSlotEnsure() != 0) {
|
||||
ret = WOLFSSL_FAILURE;
|
||||
}
|
||||
}
|
||||
return (jint)ret;
|
||||
} else {
|
||||
return (jint)WOLFSSL_FAILURE;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -35,13 +35,167 @@
|
|||
#include "com_wolfssl_globals.h"
|
||||
#include "com_wolfssl_WolfSSLContext.h"
|
||||
|
||||
/* global object refs for verify, CRL callbacks */
|
||||
static jobject g_verifyCbIfaceObj;
|
||||
/* Process-global mutex serializing reads/updates of the per-WOLFSSL_CTX
|
||||
* verify callback jobject (stored in WOLFSSL_CTX ex_data at
|
||||
* g_verifyCbCtxExDataIdx).
|
||||
*
|
||||
* setVerify and freeContext take the mutex while updating ex_data, then
|
||||
* release it before calling DeleteGlobalRef on the prior ref. Any thread
|
||||
* already inside NativeVerifyCallback's local-ref window completes safely.
|
||||
*
|
||||
* Initialized in Java_com_wolfssl_WolfSSL_init, freed in JNI_OnUnload. */
|
||||
static wolfSSL_Mutex g_verifyCbMutex;
|
||||
static int g_verifyCbMutexInit = 0;
|
||||
|
||||
int NativeVerifyCbMutexInit(void)
|
||||
{
|
||||
if (g_verifyCbMutexInit) {
|
||||
return 0;
|
||||
}
|
||||
if (wc_InitMutex(&g_verifyCbMutex) != 0) {
|
||||
return -1;
|
||||
}
|
||||
g_verifyCbMutexInit = 1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void NativeVerifyCbMutexFree(void)
|
||||
{
|
||||
if (g_verifyCbMutexInit) {
|
||||
wc_FreeMutex(&g_verifyCbMutex);
|
||||
g_verifyCbMutexInit = 0;
|
||||
}
|
||||
}
|
||||
|
||||
int NativeVerifyCbLock(void)
|
||||
{
|
||||
int rc;
|
||||
|
||||
if (!g_verifyCbMutexInit) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
rc = wc_LockMutex(&g_verifyCbMutex);
|
||||
if (rc != 0) {
|
||||
WOLFSSL_MSG("Failed to lock verify callback mutex");
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
|
||||
int NativeVerifyCbUnlock(void)
|
||||
{
|
||||
int rc;
|
||||
|
||||
if (!g_verifyCbMutexInit) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
rc = wc_UnLockMutex(&g_verifyCbMutex);
|
||||
if (rc != 0) {
|
||||
WOLFSSL_MSG("Failed to unlock verify callback mutex");
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
|
||||
int NativeVerifyCbSlotEnsure(void)
|
||||
{
|
||||
int idx;
|
||||
int rc = 0;
|
||||
|
||||
/* Mutex should have been initialized in JNI_OnLoad() */
|
||||
if (!g_verifyCbMutexInit) {
|
||||
WOLFSSL_MSG("Verify callback mutex not initialized");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (NativeVerifyCbLock() != 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (g_verifyCbCtxExDataIdx < 0) {
|
||||
idx = wolfSSL_CTX_get_ex_new_index(0, NULL, NULL, NULL, NULL);
|
||||
if (idx < 0) {
|
||||
WOLFSSL_MSG("Failed to allocate verify callback ex_data index");
|
||||
rc = -1;
|
||||
}
|
||||
else {
|
||||
g_verifyCbCtxExDataIdx = idx;
|
||||
}
|
||||
}
|
||||
|
||||
(void)NativeVerifyCbUnlock();
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
/* The CTX-level missing-CRL callback below is process-global. wolfSSL
|
||||
* CbMissingCRL signature has no SSL/CTX pointer, so native wolfSSL cannot
|
||||
* route per instance. Access is serialized by g_crlCbMutex (shared with the
|
||||
* session-level g_crlCbIfaceObj in com_wolfssl_WolfSSLSession.c). */
|
||||
#ifdef HAVE_CRL
|
||||
static jobject g_crlCtxCbIfaceObj;
|
||||
#endif
|
||||
|
||||
/* Process-global mutex covering both CTX-level and session-level missing CRL
|
||||
* callback jobjects. Initialized in Java_com_wolfssl_WolfSSL_init, freed in
|
||||
* JNI_OnUnload. */
|
||||
static wolfSSL_Mutex g_crlCbMutex;
|
||||
static int g_crlCbMutexInit = 0;
|
||||
|
||||
int NativeCrlCbMutexInit(void)
|
||||
{
|
||||
if (g_crlCbMutexInit) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (wc_InitMutex(&g_crlCbMutex) != 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
g_crlCbMutexInit = 1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void NativeCrlCbMutexFree(void)
|
||||
{
|
||||
if (g_crlCbMutexInit) {
|
||||
wc_FreeMutex(&g_crlCbMutex);
|
||||
g_crlCbMutexInit = 0;
|
||||
}
|
||||
}
|
||||
|
||||
int NativeCrlCbLock(void)
|
||||
{
|
||||
int rc;
|
||||
|
||||
if (!g_crlCbMutexInit) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
rc = wc_LockMutex(&g_crlCbMutex);
|
||||
if (rc != 0) {
|
||||
WOLFSSL_MSG("Failed to lock missing-CRL callback mutex");
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
|
||||
int NativeCrlCbUnlock(void)
|
||||
{
|
||||
int rc;
|
||||
|
||||
if (!g_crlCbMutexInit) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
rc = wc_UnLockMutex(&g_crlCbMutex);
|
||||
if (rc != 0) {
|
||||
WOLFSSL_MSG("Failed to unlock missing-CRL callback mutex");
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
|
||||
/* custom I/O native fn prototypes */
|
||||
int NativeIORecvCb(WOLFSSL *ssl, char *buf, int sz, void *ctx);
|
||||
int NativeIOSendCb(WOLFSSL *ssl, char *buf, int sz, void *ctx);
|
||||
|
|
@ -498,27 +652,49 @@ JNIEXPORT jint JNICALL Java_com_wolfssl_WolfSSLContext_useCertificateChainFile
|
|||
#endif
|
||||
}
|
||||
|
||||
/* Release process-global CRL ctx callback ref. Called from JNI_OnUnload() */
|
||||
void NativeWolfSSLContextCleanup(JNIEnv* jenv)
|
||||
{
|
||||
#ifdef HAVE_CRL
|
||||
jobject prior = NULL;
|
||||
#endif
|
||||
|
||||
if (jenv == NULL) {
|
||||
return;
|
||||
}
|
||||
#ifdef HAVE_CRL
|
||||
(void)NativeCrlCbLock();
|
||||
prior = g_crlCtxCbIfaceObj;
|
||||
g_crlCtxCbIfaceObj = NULL;
|
||||
(void)NativeCrlCbUnlock();
|
||||
|
||||
if (prior != NULL) {
|
||||
(*jenv)->DeleteGlobalRef(jenv, prior);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
JNIEXPORT void JNICALL Java_com_wolfssl_WolfSSLContext_freeContext
|
||||
(JNIEnv* jenv, jobject jcl, jlong ctxPtr)
|
||||
{
|
||||
WOLFSSL_CTX* ctx = (WOLFSSL_CTX*)(uintptr_t)ctxPtr;
|
||||
(void)jenv;
|
||||
jobject verifyCb = NULL;
|
||||
(void)jcl;
|
||||
|
||||
/* release verify callback object if set */
|
||||
if (g_verifyCbIfaceObj != NULL) {
|
||||
(*jenv)->DeleteGlobalRef(jenv, g_verifyCbIfaceObj);
|
||||
g_verifyCbIfaceObj = NULL;
|
||||
/* Capture and clear the per-CTX verify callback jobject under lock. */
|
||||
if (ctx != NULL && jenv != NULL && g_verifyCbCtxExDataIdx >= 0) {
|
||||
(void)NativeVerifyCbLock();
|
||||
verifyCb = (jobject)wolfSSL_CTX_get_ex_data(ctx,
|
||||
g_verifyCbCtxExDataIdx);
|
||||
if (verifyCb != NULL) {
|
||||
(void)wolfSSL_CTX_set_ex_data(ctx, g_verifyCbCtxExDataIdx, NULL);
|
||||
}
|
||||
(void)NativeVerifyCbUnlock();
|
||||
if (verifyCb != NULL) {
|
||||
(*jenv)->DeleteGlobalRef(jenv, verifyCb);
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef HAVE_CRL
|
||||
/* release global CRL callback object if set */
|
||||
if (g_crlCtxCbIfaceObj != NULL) {
|
||||
(*jenv)->DeleteGlobalRef(jenv, g_crlCtxCbIfaceObj);
|
||||
g_crlCtxCbIfaceObj = NULL;
|
||||
}
|
||||
#endif /* HAVE_CRL */
|
||||
|
||||
/* wolfSSL checks for null pointer */
|
||||
wolfSSL_CTX_free(ctx);
|
||||
}
|
||||
|
|
@ -527,31 +703,63 @@ JNIEXPORT void JNICALL Java_com_wolfssl_WolfSSLContext_setVerify(JNIEnv* jenv,
|
|||
jobject jcl, jlong ctxPtr, jint mode, jobject callbackIface)
|
||||
{
|
||||
WOLFSSL_CTX* ctx = (WOLFSSL_CTX*)(uintptr_t)ctxPtr;
|
||||
jobject prior = NULL;
|
||||
jobject newRef = NULL;
|
||||
(void)jcl;
|
||||
|
||||
if (jenv == NULL) {
|
||||
if (jenv == NULL || ctx == NULL) {
|
||||
return;
|
||||
}
|
||||
|
||||
/* release verify callback object if set before */
|
||||
if (g_verifyCbIfaceObj != NULL) {
|
||||
(*jenv)->DeleteGlobalRef(jenv, g_verifyCbIfaceObj);
|
||||
g_verifyCbIfaceObj = NULL;
|
||||
if (g_verifyCbCtxExDataIdx < 0) {
|
||||
/* ex_data slot was not allocated at init, cannot store per-CTX cb */
|
||||
throwWolfSSLJNIException(jenv,
|
||||
"Verify callback ex_data slot not allocated");
|
||||
return;
|
||||
}
|
||||
|
||||
if (callbackIface != NULL) {
|
||||
newRef = (*jenv)->NewGlobalRef(jenv, callbackIface);
|
||||
if (newRef == NULL) {
|
||||
throwWolfSSLJNIException(jenv,
|
||||
"Error storing verify callback global reference");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
(void)NativeVerifyCbLock();
|
||||
|
||||
/* Capture the prior per-CTX verify callback object */
|
||||
prior = (jobject)wolfSSL_CTX_get_ex_data(ctx, g_verifyCbCtxExDataIdx);
|
||||
|
||||
if (!callbackIface) {
|
||||
/* Unregister the native verify callback first so wolfSSL stops
|
||||
* routing handshakes through it, then clear the slot. */
|
||||
wolfSSL_CTX_set_verify(ctx, mode, NULL);
|
||||
(void)wolfSSL_CTX_set_ex_data(ctx, g_verifyCbCtxExDataIdx, NULL);
|
||||
}
|
||||
else {
|
||||
/* store Java verify Interface object */
|
||||
g_verifyCbIfaceObj = (*jenv)->NewGlobalRef(jenv, callbackIface);
|
||||
if (g_verifyCbIfaceObj == NULL) {
|
||||
printf("error storing global callback interface\n");
|
||||
if (wolfSSL_CTX_set_ex_data(ctx, g_verifyCbCtxExDataIdx, newRef)
|
||||
!= WOLFSSL_SUCCESS) {
|
||||
(void)NativeVerifyCbUnlock();
|
||||
/* prior (if any) is still stored in ctx ex_data because the
|
||||
* set_ex_data call failed. Leave it there for the next setter
|
||||
* or freeContext() to release. */
|
||||
(*jenv)->DeleteGlobalRef(jenv, newRef);
|
||||
throwWolfSSLJNIException(jenv,
|
||||
"Error storing verify callback in ctx ex_data");
|
||||
return;
|
||||
}
|
||||
|
||||
/* set verify mode, register Java callback with wolfSSL */
|
||||
/* Ensure the native verify callback is registered. */
|
||||
wolfSSL_CTX_set_verify(ctx, mode, NativeVerifyCallback);
|
||||
}
|
||||
|
||||
(void)NativeVerifyCbUnlock();
|
||||
|
||||
/* Release the prior global ref outside the lock */
|
||||
if (prior != NULL) {
|
||||
(*jenv)->DeleteGlobalRef(jenv, prior);
|
||||
}
|
||||
}
|
||||
|
||||
int NativeVerifyCallback(int preverify_ok, WOLFSSL_X509_STORE_CTX* store)
|
||||
|
|
@ -564,6 +772,9 @@ int NativeVerifyCallback(int preverify_ok, WOLFSSL_X509_STORE_CTX* store)
|
|||
jclass verifyClass = NULL;
|
||||
jmethodID verifyMethod = NULL;
|
||||
jobjectRefType refcheck;
|
||||
WOLFSSL* ssl = NULL;
|
||||
WOLFSSL_CTX* ctx = NULL;
|
||||
jobject verifyCbObj = NULL;
|
||||
|
||||
if (!g_vm) {
|
||||
/* we can't throw an exception yet, so just return 0 (failure) */
|
||||
|
|
@ -596,12 +807,43 @@ int NativeVerifyCallback(int preverify_ok, WOLFSSL_X509_STORE_CTX* store)
|
|||
return -103;
|
||||
}
|
||||
|
||||
/* check if our stored object reference is valid */
|
||||
refcheck = (*jenv)->GetObjectRefType(jenv, g_verifyCbIfaceObj);
|
||||
if (refcheck == 2) {
|
||||
/* Locate the per-context verify callback jobject via
|
||||
* store->ssl->ctx->ex_data. The jobject is owned by the WOLFSSL_CTX
|
||||
* that produced this handshake, so each context fires its own user
|
||||
* callback. If the slot is empty or any lookup step fails, fall
|
||||
* through to verifyCbObj == NULL below and fail closed. */
|
||||
if (g_verifyCbCtxExDataIdx >= 0 && store != NULL) {
|
||||
ssl = (WOLFSSL*)wolfSSL_X509_STORE_CTX_get_ex_data(store,
|
||||
wolfSSL_get_ex_data_X509_STORE_CTX_idx());
|
||||
if (ssl != NULL) {
|
||||
ctx = wolfSSL_get_SSL_CTX(ssl);
|
||||
}
|
||||
}
|
||||
if (ctx != NULL) {
|
||||
(void)NativeVerifyCbLock();
|
||||
verifyCbObj = (jobject)wolfSSL_CTX_get_ex_data(ctx,
|
||||
g_verifyCbCtxExDataIdx);
|
||||
if (verifyCbObj != NULL) {
|
||||
verifyCbObj = (*jenv)->NewLocalRef(jenv, verifyCbObj);
|
||||
}
|
||||
(void)NativeVerifyCbUnlock();
|
||||
}
|
||||
if (verifyCbObj == NULL) {
|
||||
/* No Java callback registered. Fail closed without throwing. */
|
||||
if (needsDetach) {
|
||||
(*g_vm)->DetachCurrentThread(g_vm);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* check if our stored object reference is valid (non-zero ref type
|
||||
* covers local, global, or weak global; verifyCbObj is a local ref
|
||||
* promoted from the per-CTX global ref under g_verifyCbMutex) */
|
||||
refcheck = (*jenv)->GetObjectRefType(jenv, verifyCbObj);
|
||||
if (refcheck != 0) {
|
||||
|
||||
/* lookup WolfSSLVerifyCallback class from global object ref */
|
||||
verifyClass = (*jenv)->GetObjectClass(jenv, g_verifyCbIfaceObj);
|
||||
verifyClass = (*jenv)->GetObjectClass(jenv, verifyCbObj);
|
||||
if (!verifyClass) {
|
||||
if ((*jenv)->ExceptionOccurred(jenv)) {
|
||||
(*jenv)->ExceptionDescribe(jenv);
|
||||
|
|
@ -630,7 +872,7 @@ int NativeVerifyCallback(int preverify_ok, WOLFSSL_X509_STORE_CTX* store)
|
|||
return -105;
|
||||
}
|
||||
|
||||
retval = (*jenv)->CallIntMethod(jenv, g_verifyCbIfaceObj,
|
||||
retval = (*jenv)->CallIntMethod(jenv, verifyCbObj,
|
||||
verifyMethod, preverify_ok, (jlong)(uintptr_t)store);
|
||||
|
||||
if ((*jenv)->ExceptionOccurred(jenv)) {
|
||||
|
|
@ -1767,7 +2009,8 @@ JNIEXPORT jint JNICALL Java_com_wolfssl_WolfSSLContext_setCRLCb
|
|||
{
|
||||
#ifdef HAVE_CRL
|
||||
int ret = 0;
|
||||
jclass excClass;
|
||||
jobject newRef = NULL;
|
||||
jobject prior = NULL;
|
||||
WOLFSSL_CTX* ctx = (WOLFSSL_CTX*)(uintptr_t)ctxPtr;
|
||||
(void)jcl;
|
||||
|
||||
|
|
@ -1775,29 +2018,36 @@ JNIEXPORT jint JNICALL Java_com_wolfssl_WolfSSLContext_setCRLCb
|
|||
return (jint)BAD_FUNC_ARG;
|
||||
}
|
||||
|
||||
/* release global CRL callback object if set */
|
||||
if (g_crlCtxCbIfaceObj != NULL) {
|
||||
(*jenv)->DeleteGlobalRef(jenv, g_crlCtxCbIfaceObj);
|
||||
g_crlCtxCbIfaceObj = NULL;
|
||||
}
|
||||
|
||||
if (cb != NULL) {
|
||||
/* store Java CRL callback Interface object */
|
||||
g_crlCtxCbIfaceObj = (*jenv)->NewGlobalRef(jenv, cb);
|
||||
|
||||
if (!g_crlCtxCbIfaceObj) {
|
||||
excClass = (*jenv)->FindClass(jenv,
|
||||
"com/wolfssl/WolfSSLJNIException");
|
||||
newRef = (*jenv)->NewGlobalRef(jenv, cb);
|
||||
if (newRef == NULL) {
|
||||
if ((*jenv)->ExceptionOccurred(jenv)) {
|
||||
(*jenv)->ExceptionDescribe(jenv);
|
||||
(*jenv)->ExceptionClear(jenv);
|
||||
}
|
||||
(*jenv)->ThrowNew(jenv, excClass,
|
||||
"error storing global missing CTX CRL callback interface");
|
||||
throwWolfSSLJNIException(jenv,
|
||||
"error storing global missing CTX CRL callback interface");
|
||||
return (jint)SSL_FAILURE;
|
||||
}
|
||||
}
|
||||
|
||||
/* Swap the global pointer under g_crlCbMutex */
|
||||
(void)NativeCrlCbLock();
|
||||
prior = g_crlCtxCbIfaceObj;
|
||||
g_crlCtxCbIfaceObj = newRef;
|
||||
(void)NativeCrlCbUnlock();
|
||||
|
||||
if (prior != NULL) {
|
||||
(*jenv)->DeleteGlobalRef(jenv, prior);
|
||||
}
|
||||
|
||||
if (cb != NULL) {
|
||||
ret = wolfSSL_CTX_SetCRL_Cb(ctx, NativeCtxMissingCRLCallback);
|
||||
}
|
||||
else {
|
||||
/* Unregister native wolfSSL CRL callback */
|
||||
ret = wolfSSL_CTX_SetCRL_Cb(ctx, NULL);
|
||||
}
|
||||
|
||||
return (jint)ret;
|
||||
#else
|
||||
|
|
@ -1821,6 +2071,7 @@ void NativeCtxMissingCRLCallback(const char* url)
|
|||
jmethodID crlMethod;
|
||||
jstring missingUrl = NULL;
|
||||
jobjectRefType refcheck;
|
||||
jobject crlCbObj = NULL;
|
||||
|
||||
/* get JNIEnv from JavaVM */
|
||||
vmret = (int)((*g_vm)->GetEnv(g_vm, (void**) &jenv, JNI_VERSION_1_6));
|
||||
|
|
@ -1850,12 +2101,31 @@ void NativeCtxMissingCRLCallback(const char* url)
|
|||
return;
|
||||
}
|
||||
|
||||
/* check if our stored object reference is valid */
|
||||
refcheck = (*jenv)->GetObjectRefType(jenv, g_crlCtxCbIfaceObj);
|
||||
if (refcheck == 2) {
|
||||
/* Promote process-global jobject to a local ref under g_crlCbMutex. Local
|
||||
* ref keeps underlying object alive even if a concurrent setCRLCb()
|
||||
* clears g_crlCtxCbIfaceObj and deletes the prior global ref immediately
|
||||
* after we release the lock. */
|
||||
(void)NativeCrlCbLock();
|
||||
if (g_crlCtxCbIfaceObj != NULL) {
|
||||
crlCbObj = (*jenv)->NewLocalRef(jenv, g_crlCtxCbIfaceObj);
|
||||
}
|
||||
(void)NativeCrlCbUnlock();
|
||||
|
||||
/* lookup WolfSSLMissingCRLCallback class from global object ref */
|
||||
crlClass = (*jenv)->GetObjectClass(jenv, g_crlCtxCbIfaceObj);
|
||||
if (crlCbObj == NULL) {
|
||||
/* No Java callback registered, drop silently. */
|
||||
if (needsDetach) {
|
||||
(*g_vm)->DetachCurrentThread(g_vm);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
/* GetObjectRefType returns non-zero for local/global/weak refs;
|
||||
* crlCbObj is a local ref promoted from a global ref under lock. */
|
||||
refcheck = (*jenv)->GetObjectRefType(jenv, crlCbObj);
|
||||
if (refcheck != 0) {
|
||||
|
||||
/* lookup WolfSSLMissingCRLCallback class from local object ref */
|
||||
crlClass = (*jenv)->GetObjectClass(jenv, crlCbObj);
|
||||
if (!crlClass) {
|
||||
(*jenv)->ThrowNew(jenv, excClass,
|
||||
"Can't get native WolfSSLMissingCRLCallback class reference");
|
||||
|
|
@ -1883,7 +2153,7 @@ void NativeCtxMissingCRLCallback(const char* url)
|
|||
/* create jstring from char* */
|
||||
missingUrl = (*jenv)->NewStringUTF(jenv, url);
|
||||
|
||||
(*jenv)->CallVoidMethod(jenv, g_crlCtxCbIfaceObj, crlMethod,
|
||||
(*jenv)->CallVoidMethod(jenv, crlCbObj, crlMethod,
|
||||
missingUrl);
|
||||
|
||||
if ((*jenv)->ExceptionOccurred(jenv)) {
|
||||
|
|
|
|||
|
|
@ -73,7 +73,9 @@ int NativeSessionTicketCb(WOLFSSL *ssl, const unsigned char* ticket,
|
|||
#endif
|
||||
|
||||
#ifdef HAVE_CRL
|
||||
/* global object refs for CRL callback */
|
||||
/* Process-global jobject for the missing-CRL callback registered via
|
||||
* Java_com_wolfssl_WolfSSLSession_setCRLCb(). wolfSSL CbMissingCRL has no
|
||||
* SSL/CTX pointer, so native wolfSSL cannot route per-instance. */
|
||||
static jobject g_crlCbIfaceObj;
|
||||
#endif
|
||||
|
||||
|
|
@ -1756,6 +1758,30 @@ JNIEXPORT jint JNICALL Java_com_wolfssl_WolfSSLSession_accept
|
|||
return ret;
|
||||
}
|
||||
|
||||
/* Release process-global session-level missing-CRL callback ref. Called
|
||||
* from JNI_OnUnload() in com_wolfssl_WolfSSL.c so the global is torn
|
||||
* down at native library unload. */
|
||||
void NativeWolfSSLSessionCleanup(JNIEnv* jenv)
|
||||
{
|
||||
#ifdef HAVE_CRL
|
||||
jobject prior = NULL;
|
||||
#endif
|
||||
|
||||
if (jenv == NULL) {
|
||||
return;
|
||||
}
|
||||
#ifdef HAVE_CRL
|
||||
(void)NativeCrlCbLock();
|
||||
prior = g_crlCbIfaceObj;
|
||||
g_crlCbIfaceObj = NULL;
|
||||
(void)NativeCrlCbUnlock();
|
||||
|
||||
if (prior != NULL) {
|
||||
(*jenv)->DeleteGlobalRef(jenv, prior);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
JNIEXPORT void JNICALL Java_com_wolfssl_WolfSSLSession_freeSSL
|
||||
(JNIEnv* jenv, jobject jcl, jlong sslPtr)
|
||||
{
|
||||
|
|
@ -1857,14 +1883,6 @@ JNIEXPORT void JNICALL Java_com_wolfssl_WolfSSLSession_freeSSL
|
|||
return;
|
||||
}
|
||||
|
||||
#ifdef HAVE_CRL
|
||||
/* release global CRL callback ref if registered */
|
||||
if (g_crlCbIfaceObj != NULL) {
|
||||
(*jenv)->DeleteGlobalRef(jenv, g_crlCbIfaceObj);
|
||||
g_crlCbIfaceObj = NULL;
|
||||
}
|
||||
#endif
|
||||
|
||||
#if defined(HAVE_PK_CALLBACKS)
|
||||
#ifdef HAVE_ECC
|
||||
/* free ECC sign callback CTX global reference if set */
|
||||
|
|
@ -3919,6 +3937,8 @@ JNIEXPORT jint JNICALL Java_com_wolfssl_WolfSSLSession_setCRLCb
|
|||
{
|
||||
#ifdef HAVE_CRL
|
||||
int ret = 0;
|
||||
jobject newRef = NULL;
|
||||
jobject prior = NULL;
|
||||
WOLFSSL* ssl = (WOLFSSL*)(uintptr_t)sslPtr;
|
||||
(void)jcl;
|
||||
|
||||
|
|
@ -3932,23 +3952,36 @@ JNIEXPORT jint JNICALL Java_com_wolfssl_WolfSSLSession_setCRLCb
|
|||
return SSL_FAILURE;
|
||||
}
|
||||
|
||||
/* release global CRL callback ref if already registered */
|
||||
if (g_crlCbIfaceObj != NULL) {
|
||||
(*jenv)->DeleteGlobalRef(jenv, g_crlCbIfaceObj);
|
||||
g_crlCbIfaceObj = NULL;
|
||||
}
|
||||
|
||||
/* Allocate the new global ref outside the lock to avoid holding
|
||||
* g_crlCbMutex across the JNI allocation. */
|
||||
if (cb != NULL) {
|
||||
/* store Java CRL callback Interface object */
|
||||
g_crlCbIfaceObj = (*jenv)->NewGlobalRef(jenv, cb);
|
||||
if (g_crlCbIfaceObj == NULL) {
|
||||
newRef = (*jenv)->NewGlobalRef(jenv, cb);
|
||||
if (newRef == NULL) {
|
||||
throwWolfSSLException(jenv,
|
||||
"Error storing global missingCRLCallback interface");
|
||||
return SSL_FAILURE;
|
||||
}
|
||||
}
|
||||
|
||||
/* Swap global pointer under g_crlCbMutex. The prior ref is deleted
|
||||
* after the unlock so any in-flight NativeMissingCRLCallback that already
|
||||
* promoted the global to a local ref can complete safely. */
|
||||
(void)NativeCrlCbLock();
|
||||
prior = g_crlCbIfaceObj;
|
||||
g_crlCbIfaceObj = newRef;
|
||||
(void)NativeCrlCbUnlock();
|
||||
|
||||
if (prior != NULL) {
|
||||
(*jenv)->DeleteGlobalRef(jenv, prior);
|
||||
}
|
||||
|
||||
if (cb != NULL) {
|
||||
ret = wolfSSL_SetCRL_Cb(ssl, NativeMissingCRLCallback);
|
||||
}
|
||||
else {
|
||||
/* Unregister native wolfSSL CRL callback */
|
||||
ret = wolfSSL_SetCRL_Cb(ssl, NULL);
|
||||
}
|
||||
|
||||
return ret;
|
||||
#else
|
||||
|
|
@ -3971,6 +4004,7 @@ void NativeMissingCRLCallback(const char* url)
|
|||
jmethodID crlMethod = NULL;
|
||||
jobjectRefType refcheck;
|
||||
jstring missingUrl = NULL;
|
||||
jobject crlCbObj = NULL;
|
||||
|
||||
/* get JNIEnv from JavaVM */
|
||||
vmret = (int)((*g_vm)->GetEnv(g_vm, (void**) &jenv, JNI_VERSION_1_6));
|
||||
|
|
@ -3990,12 +4024,29 @@ void NativeMissingCRLCallback(const char* url)
|
|||
return;
|
||||
}
|
||||
|
||||
/* check if our stored object reference is valid */
|
||||
refcheck = (*jenv)->GetObjectRefType(jenv, g_crlCbIfaceObj);
|
||||
if (refcheck == 2) {
|
||||
/* Promote the global jobject to a local ref under g_crlCbMutex so we keep
|
||||
* the object alive even if a concurrent setCRLCb clears the global and
|
||||
* deletes the previous ref immediately after we unlock. */
|
||||
(void)NativeCrlCbLock();
|
||||
if (g_crlCbIfaceObj != NULL) {
|
||||
crlCbObj = (*jenv)->NewLocalRef(jenv, g_crlCbIfaceObj);
|
||||
}
|
||||
(void)NativeCrlCbUnlock();
|
||||
|
||||
/* lookup WolfSSLMissingCRLCallback class from global object ref */
|
||||
crlClass = (*jenv)->GetObjectClass(jenv, g_crlCbIfaceObj);
|
||||
if (crlCbObj == NULL) {
|
||||
/* No Java callback registered, drop silently. */
|
||||
if (needsDetach) {
|
||||
(*g_vm)->DetachCurrentThread(g_vm);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
/* GetObjectRefType returns non-zero for local/global/weak refs. */
|
||||
refcheck = (*jenv)->GetObjectRefType(jenv, crlCbObj);
|
||||
if (refcheck != 0) {
|
||||
|
||||
/* lookup WolfSSLMissingCRLCallback class from local object ref */
|
||||
crlClass = (*jenv)->GetObjectClass(jenv, crlCbObj);
|
||||
if (!crlClass) {
|
||||
throwWolfSSLException(jenv,
|
||||
"Can't get native WolfSSLMissingCRLCallback class reference");
|
||||
|
|
@ -4023,7 +4074,7 @@ void NativeMissingCRLCallback(const char* url)
|
|||
/* create jstring from char* */
|
||||
missingUrl = (*jenv)->NewStringUTF(jenv, url);
|
||||
|
||||
(*jenv)->CallVoidMethod(jenv, g_crlCbIfaceObj, crlMethod, missingUrl);
|
||||
(*jenv)->CallVoidMethod(jenv, crlCbObj, crlMethod, missingUrl);
|
||||
|
||||
if ((*jenv)->ExceptionOccurred(jenv)) {
|
||||
(*jenv)->ExceptionDescribe(jenv);
|
||||
|
|
|
|||
|
|
@ -46,6 +46,12 @@ extern jmethodID g_bufferArrayOffsetMethodId; /* ByteBuffer.arrayOffset() */
|
|||
extern jmethodID g_bufferSetPositionMethodId; /* ByteBuffer.position(int) */
|
||||
extern jmethodID g_verifyCallbackMethodId; /* WolfSSLVerifyCallback.verifyCallback */
|
||||
|
||||
/* WOLFSSL_CTX ex_data index used to store the per-WolfSSLContext jobject ref
|
||||
* to the user WolfSSLVerifyCallback. Allocated once in
|
||||
* Java_com_wolfssl_WolfSSL_init via wolfSSL_CTX_get_ex_new_index(). A
|
||||
* negative value means the slot has not yet been allocated. */
|
||||
extern int g_verifyCbCtxExDataIdx;
|
||||
|
||||
/* struct to hold I/O class, object refs */
|
||||
typedef struct {
|
||||
int active;
|
||||
|
|
@ -61,4 +67,27 @@ unsigned int NativePskServerCb(WOLFSSL* ssl, const char* identity,
|
|||
void throwWolfSSLJNIException(JNIEnv* jenv, const char* msg);
|
||||
void throwWolfSSLException(JNIEnv* jenv, const char* msg);
|
||||
|
||||
/* Release process-global jobject refs. Called from JNI_OnUnload() */
|
||||
void NativeWolfSSLContextCleanup(JNIEnv* jenv);
|
||||
void NativeWolfSSLSessionCleanup(JNIEnv* jenv);
|
||||
|
||||
/* Initialize/free the process-global mutex around verify callback */
|
||||
int NativeVerifyCbMutexInit(void);
|
||||
void NativeVerifyCbMutexFree(void);
|
||||
|
||||
/* Lock/unlock the verify callback mutex. Logs on failure. */
|
||||
int NativeVerifyCbLock(void);
|
||||
int NativeVerifyCbUnlock(void);
|
||||
|
||||
/* Ensure verify callback slot is allocated */
|
||||
int NativeVerifyCbSlotEnsure(void);
|
||||
|
||||
/* Initialize/free the process-global mutex around missing-CRL callback */
|
||||
int NativeCrlCbMutexInit(void);
|
||||
void NativeCrlCbMutexFree(void);
|
||||
|
||||
/* Lock/unlock the missing-CRL callback mutex. */
|
||||
int NativeCrlCbLock(void);
|
||||
int NativeCrlCbUnlock(void);
|
||||
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -31,6 +31,7 @@ import org.junit.rules.TestRule;
|
|||
import static org.junit.Assert.*;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.InetSocketAddress;
|
||||
import java.net.Socket;
|
||||
import java.net.ServerSocket;
|
||||
import java.nio.ByteBuffer;
|
||||
|
|
@ -46,6 +47,8 @@ import com.wolfssl.WolfSSL;
|
|||
import com.wolfssl.WolfSSLContext;
|
||||
import com.wolfssl.WolfSSLException;
|
||||
import com.wolfssl.WolfSSLJNIException;
|
||||
import com.wolfssl.WolfSSLVerifyCallback;
|
||||
import com.wolfssl.WolfSSLMissingCRLCallback;
|
||||
import com.wolfssl.WolfSSLPskClientCallback;
|
||||
import com.wolfssl.WolfSSLPskServerCallback;
|
||||
import com.wolfssl.WolfSSLSession;
|
||||
|
|
@ -74,6 +77,12 @@ public class WolfSSLContextTest {
|
|||
|
||||
WolfSSLContext ctx;
|
||||
|
||||
/* Hold a strong reference to a WolfSSL instance for the lifetime of
|
||||
* this test class so wolfSSL_Init() runs before any test executes.
|
||||
* Required when surefire/Maven runs this class outside of
|
||||
* WolfSSLTestSuite, whose @BeforeClass would otherwise create one. */
|
||||
private static WolfSSL sslLib = null;
|
||||
|
||||
@BeforeClass
|
||||
public static void loadLibrary() throws WolfSSLException {
|
||||
System.out.println("WolfSSLContext Class");
|
||||
|
|
@ -83,6 +92,7 @@ public class WolfSSLContextTest {
|
|||
} catch (UnsatisfiedLinkError ule) {
|
||||
fail("failed to load native JNI library");
|
||||
}
|
||||
sslLib = new WolfSSL();
|
||||
|
||||
cliCert = WolfSSLTestCommon.getPath(cliCert);
|
||||
cliKey = WolfSSLTestCommon.getPath(cliKey);
|
||||
|
|
@ -671,6 +681,350 @@ public class WolfSSLContextTest {
|
|||
}
|
||||
}
|
||||
|
||||
/* Custom verify callback class so each test instance is its own Java
|
||||
* object (and therefore its own JNI global ref). */
|
||||
private static class CountingVerifyCb implements WolfSSLVerifyCallback {
|
||||
public int calls = 0;
|
||||
public int verifyCallback(int preverify_ok, long x509StorePtr) {
|
||||
calls++;
|
||||
return preverify_ok;
|
||||
}
|
||||
}
|
||||
|
||||
/* Exercises setVerify ref lifecycle with multiple WolfSSLContext instances
|
||||
* each holding their own callback. Goes through the install, replace,
|
||||
* unregister, and re-install paths, frees one context while the other is
|
||||
* still live, and forces GC to surface any dangling-ref issues under
|
||||
* -Xcheck:jni or Android checked JNI.
|
||||
*
|
||||
* The Java callback objects are referenced at the end of the test
|
||||
* to keep them strongly reachable for the duration of the test. */
|
||||
@Test
|
||||
public void test_WolfSSLContext_setVerifyMultipleContexts()
|
||||
throws WolfSSLException {
|
||||
|
||||
/* Each context owns the WOLFSSL_METHOD it is constructed with and
|
||||
* frees it during free(), so allocate a method per context. */
|
||||
long methodA = WolfSSL.SSLv23_ServerMethod();
|
||||
long methodB = WolfSSL.SSLv23_ServerMethod();
|
||||
Assume.assumeTrue("SSLv23 server method not available",
|
||||
methodA != 0 && methodA != WolfSSL.NOT_COMPILED_IN &&
|
||||
methodB != 0 && methodB != WolfSSL.NOT_COMPILED_IN);
|
||||
|
||||
WolfSSLContext ctxA = new WolfSSLContext(methodA);
|
||||
WolfSSLContext ctxB = new WolfSSLContext(methodB);
|
||||
CountingVerifyCb cbA = new CountingVerifyCb();
|
||||
CountingVerifyCb cbB = new CountingVerifyCb();
|
||||
|
||||
try {
|
||||
/* Register distinct callbacks on each ctx. */
|
||||
ctxA.setVerify(WolfSSL.SSL_VERIFY_PEER, cbA);
|
||||
ctxB.setVerify(WolfSSL.SSL_VERIFY_PEER, cbB);
|
||||
|
||||
/* Replace ctxA's callback with a fresh one, exercises the
|
||||
* prior-ref-release path on the same ctx. */
|
||||
CountingVerifyCb cbA2 = new CountingVerifyCb();
|
||||
ctxA.setVerify(WolfSSL.SSL_VERIFY_PEER, cbA2);
|
||||
|
||||
/* Unregister ctxB's callback (null path). */
|
||||
ctxB.setVerify(WolfSSL.SSL_VERIFY_PEER, null);
|
||||
|
||||
/* Re-register on ctxB after a null pass. */
|
||||
ctxB.setVerify(WolfSSL.SSL_VERIFY_PEER, cbB);
|
||||
|
||||
/* Free ctxA first while ctxB's callback is still live. */
|
||||
ctxA.free();
|
||||
ctxA = null;
|
||||
|
||||
/* ctxB should still be functional (no exception thrown). */
|
||||
ctxB.setVerify(WolfSSL.SSL_VERIFY_PEER, null);
|
||||
|
||||
/* Touch the cb references at the end to defeat any compiler level
|
||||
* liveness shortening that would let GC collect them mid-test. */
|
||||
assertNotNull(cbA);
|
||||
assertNotNull(cbA2);
|
||||
assertNotNull(cbB);
|
||||
} finally {
|
||||
if (ctxA != null) {
|
||||
ctxA.free();
|
||||
}
|
||||
if (ctxB != null) {
|
||||
ctxB.free();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Custom missing CRL callback class so each test instance is its own
|
||||
* Java object (and own JNI global ref). */
|
||||
private static class CountingMissingCRLCb
|
||||
implements WolfSSLMissingCRLCallback {
|
||||
public int calls = 0;
|
||||
public void missingCRLCallback(String url) {
|
||||
calls++;
|
||||
}
|
||||
}
|
||||
|
||||
/* Exercises setCRLCb ref lifecycle for both WolfSSLContext.setCRLCb() and
|
||||
* WolfSSLSession.setCRLCb(). The two globals (g_crlCtxCbIfaceObj and
|
||||
* g_crlCbIfaceObj) are protected by a single process-global mutex
|
||||
* (g_crlCbMutex). This test does install / replace / unregister /
|
||||
* re-install / free paths and forces GC.
|
||||
*
|
||||
* Java callback objects are referenced at the end of the test to keep
|
||||
* them strongly reachable for the duration of the test. */
|
||||
@Test
|
||||
public void test_WolfSSLContext_setCRLCbMultipleContexts()
|
||||
throws Exception {
|
||||
|
||||
/* WolfSSLSession construction requires a CTX with cert and key loaded,
|
||||
* so this test is gated on RSA and filesystem support. */
|
||||
Assume.assumeTrue(WolfSSL.RsaEnabled() && WolfSSL.FileSystemEnabled());
|
||||
Assume.assumeTrue(WolfSSL.TLSv12Enabled());
|
||||
|
||||
WolfSSLContext ctxA = createCtx(cliCert, cliKey, caCert,
|
||||
WolfSSL.TLSv1_2_ClientMethod());
|
||||
WolfSSLContext ctxB = createCtx(cliCert, cliKey, caCert,
|
||||
WolfSSL.TLSv1_2_ClientMethod());
|
||||
WolfSSLSession sesA = null;
|
||||
WolfSSLSession sesB = null;
|
||||
CountingMissingCRLCb cbA = new CountingMissingCRLCb();
|
||||
CountingMissingCRLCb cbB = new CountingMissingCRLCb();
|
||||
CountingMissingCRLCb cbA2 = new CountingMissingCRLCb();
|
||||
CountingMissingCRLCb sesCbA = new CountingMissingCRLCb();
|
||||
CountingMissingCRLCb sesCbB = new CountingMissingCRLCb();
|
||||
|
||||
try {
|
||||
/* Check for HAVE_CRL by attempting a CTX-level register. Skip the
|
||||
* test if CRL is not compiled in. */
|
||||
int probe = ctxA.setCRLCb(cbA);
|
||||
Assume.assumeTrue("CRL not compiled in",
|
||||
probe != WolfSSL.NOT_COMPILED_IN);
|
||||
|
||||
ctxB.setCRLCb(cbB);
|
||||
|
||||
/* Replace ctxA callback with a fresh one, exercises the
|
||||
* prior-ref-release path on the same ctx. */
|
||||
ctxA.setCRLCb(cbA2);
|
||||
|
||||
/* Unregister ctxB callback (null path, should also unregister
|
||||
* the native wolfSSL CRL cb). */
|
||||
ctxB.setCRLCb(null);
|
||||
|
||||
/* Re-register on ctxB after a null pass. */
|
||||
ctxB.setCRLCb(cbB);
|
||||
|
||||
/* Session-level setCRLCb, same pattern on a session created
|
||||
* from each context. */
|
||||
sesA = new WolfSSLSession(ctxA);
|
||||
sesB = new WolfSSLSession(ctxB);
|
||||
sesA.setCRLCb(sesCbA);
|
||||
sesB.setCRLCb(sesCbB);
|
||||
sesA.setCRLCb(null);
|
||||
sesA.setCRLCb(sesCbA);
|
||||
|
||||
/* Force a GC pass to show any dangling refs in checked-JNI
|
||||
* environments before tearing down. */
|
||||
System.gc();
|
||||
try {
|
||||
Thread.sleep(10);
|
||||
} catch (InterruptedException ie) {
|
||||
Thread.currentThread().interrupt();
|
||||
}
|
||||
|
||||
/* Free sesA session, then ctxA, while ctxB and sesB still hold
|
||||
* live callbacks. */
|
||||
sesA.freeSSL();
|
||||
sesA = null;
|
||||
ctxA.free();
|
||||
ctxA = null;
|
||||
|
||||
/* ctxB should still be functional (no exception thrown). */
|
||||
ctxB.setCRLCb(null);
|
||||
sesB.setCRLCb(null);
|
||||
|
||||
/* Touch the cb references at the end to defeat any compiler
|
||||
* level liveness shortening that would let GC collect them
|
||||
* mid-test. */
|
||||
assertNotNull(cbA);
|
||||
assertNotNull(cbA2);
|
||||
assertNotNull(cbB);
|
||||
assertNotNull(sesCbA);
|
||||
assertNotNull(sesCbB);
|
||||
|
||||
/* No handshake is done in this test, so no missing-CRL callback
|
||||
* should have fired. A non-zero count would mean native callback
|
||||
* got wired to the wrong jobject or leaked across contexts. */
|
||||
assertEquals("cbA missingCRLCallback fired unexpectedly",
|
||||
0, cbA.calls);
|
||||
assertEquals("cbA2 missingCRLCallback fired unexpectedly",
|
||||
0, cbA2.calls);
|
||||
assertEquals("cbB missingCRLCallback fired unexpectedly",
|
||||
0, cbB.calls);
|
||||
assertEquals("sesCbA missingCRLCallback fired unexpectedly",
|
||||
0, sesCbA.calls);
|
||||
assertEquals("sesCbB missingCRLCallback fired unexpectedly",
|
||||
0, sesCbB.calls);
|
||||
|
||||
} finally {
|
||||
if (sesA != null) {
|
||||
sesA.freeSSL();
|
||||
}
|
||||
if (sesB != null) {
|
||||
sesB.freeSSL();
|
||||
}
|
||||
if (ctxA != null) {
|
||||
ctxA.free();
|
||||
}
|
||||
if (ctxB != null) {
|
||||
ctxB.free();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Do a real handshake on each of two client contexts and assert that each
|
||||
* context's verify callback fires only for its own handshake. */
|
||||
@Test
|
||||
public void test_WolfSSLContext_setVerifyHandshakeRouting()
|
||||
throws Exception {
|
||||
|
||||
Assume.assumeTrue(WolfSSL.RsaEnabled() && WolfSSL.FileSystemEnabled());
|
||||
Assume.assumeTrue(WolfSSL.TLSv12Enabled());
|
||||
|
||||
WolfSSLContext srvCtx = null;
|
||||
WolfSSLContext cliCtxA = null;
|
||||
WolfSSLContext cliCtxB = null;
|
||||
ServerSocket srvSocket = null;
|
||||
ExecutorService es = null;
|
||||
Future<Void> srvFuture = null;
|
||||
|
||||
try {
|
||||
srvCtx = createCtx(svrCert, svrKey, caCert,
|
||||
WolfSSL.TLSv1_2_ServerMethod());
|
||||
cliCtxA = createCtx(cliCert, cliKey, caCert,
|
||||
WolfSSL.TLSv1_2_ClientMethod());
|
||||
cliCtxB = createCtx(cliCert, cliKey, caCert,
|
||||
WolfSSL.TLSv1_2_ClientMethod());
|
||||
|
||||
final CountingVerifyCb cbA = new CountingVerifyCb();
|
||||
final CountingVerifyCb cbB = new CountingVerifyCb();
|
||||
cliCtxA.setVerify(WolfSSL.SSL_VERIFY_PEER, cbA);
|
||||
cliCtxB.setVerify(WolfSSL.SSL_VERIFY_PEER, cbB);
|
||||
|
||||
srvSocket = new ServerSocket(0);
|
||||
srvSocket.setSoTimeout(10000);
|
||||
final int port = srvSocket.getLocalPort();
|
||||
final ServerSocket fSrvSock = srvSocket;
|
||||
final WolfSSLContext fSrvCtx = srvCtx;
|
||||
|
||||
/* Server thread accepts two sequential connections. */
|
||||
es = Executors.newSingleThreadExecutor();
|
||||
srvFuture = es.submit(new Callable<Void>() {
|
||||
@Override
|
||||
public Void call() throws Exception {
|
||||
for (int i = 0; i < 2; i++) {
|
||||
Socket srv = fSrvSock.accept();
|
||||
WolfSSLSession ses = new WolfSSLSession(fSrvCtx);
|
||||
try {
|
||||
int r = ses.setFd(srv);
|
||||
if (r != WolfSSL.SSL_SUCCESS) {
|
||||
throw new Exception("srv setFd: " + r);
|
||||
}
|
||||
int err;
|
||||
do {
|
||||
r = ses.accept();
|
||||
err = ses.getError(r);
|
||||
} while (r != WolfSSL.SSL_SUCCESS &&
|
||||
(err == WolfSSL.SSL_ERROR_WANT_READ ||
|
||||
err == WolfSSL.SSL_ERROR_WANT_WRITE));
|
||||
if (r != WolfSSL.SSL_SUCCESS) {
|
||||
throw new Exception("srv accept: " + r);
|
||||
}
|
||||
ses.shutdownSSL();
|
||||
} finally {
|
||||
ses.freeSSL();
|
||||
srv.close();
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
});
|
||||
|
||||
/* Client A handshake: should fire cbA only. */
|
||||
doClientHandshake(cliCtxA, port);
|
||||
assertTrue("cbA not called for ctxA handshake", cbA.calls > 0);
|
||||
assertEquals("cbB unexpectedly called for ctxA handshake",
|
||||
0, cbB.calls);
|
||||
|
||||
int cbACallsAfterFirst = cbA.calls;
|
||||
|
||||
/* Client B handshake: should fire cbB only. */
|
||||
doClientHandshake(cliCtxB, port);
|
||||
assertTrue("cbB not called for ctxB handshake", cbB.calls > 0);
|
||||
assertEquals("cbA called for ctxB handshake",
|
||||
cbACallsAfterFirst, cbA.calls);
|
||||
|
||||
srvFuture.get(5, TimeUnit.SECONDS);
|
||||
} finally {
|
||||
/* Close the server socket first so any pending accept() unblocks.
|
||||
* Then cancel the future and force the executor to terminate. */
|
||||
if (srvSocket != null && !srvSocket.isClosed()) {
|
||||
srvSocket.close();
|
||||
}
|
||||
if (srvFuture != null) {
|
||||
srvFuture.cancel(true);
|
||||
}
|
||||
if (es != null) {
|
||||
es.shutdownNow();
|
||||
try {
|
||||
es.awaitTermination(5, TimeUnit.SECONDS);
|
||||
} catch (InterruptedException ie) {
|
||||
Thread.currentThread().interrupt();
|
||||
}
|
||||
}
|
||||
if (cliCtxA != null) {
|
||||
cliCtxA.free();
|
||||
}
|
||||
if (cliCtxB != null) {
|
||||
cliCtxB.free();
|
||||
}
|
||||
if (srvCtx != null) {
|
||||
srvCtx.free();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Helper method to open a TCP socket to localhost:port and complete a TLS
|
||||
* handshake using the given client context. */
|
||||
private void doClientHandshake(WolfSSLContext cliCtx, int port)
|
||||
throws Exception {
|
||||
|
||||
final int socketTimeoutMs = 5000;
|
||||
Socket sock = new Socket();
|
||||
sock.connect(new InetSocketAddress("localhost", port), socketTimeoutMs);
|
||||
sock.setSoTimeout(socketTimeoutMs);
|
||||
WolfSSLSession ses = new WolfSSLSession(cliCtx);
|
||||
try {
|
||||
int r = ses.setFd(sock);
|
||||
if (r != WolfSSL.SSL_SUCCESS) {
|
||||
throw new Exception("cli setFd: " + r);
|
||||
}
|
||||
int err;
|
||||
do {
|
||||
r = ses.connect();
|
||||
err = ses.getError(r);
|
||||
} while (r != WolfSSL.SSL_SUCCESS &&
|
||||
(err == WolfSSL.SSL_ERROR_WANT_READ ||
|
||||
err == WolfSSL.SSL_ERROR_WANT_WRITE));
|
||||
if (r != WolfSSL.SSL_SUCCESS) {
|
||||
throw new Exception("cli connect: " + r);
|
||||
}
|
||||
ses.shutdownSSL();
|
||||
} finally {
|
||||
ses.freeSSL();
|
||||
sock.close();
|
||||
}
|
||||
}
|
||||
|
||||
/* Context object shared between RSA sign/verify callbacks, tracks whether
|
||||
* callback was invoked during handshake */
|
||||
class TestRsaCbCtx
|
||||
|
|
|
|||
Loading…
Reference in New Issue