diff -r 4ebc2e2fb97c -r 71c04702a3d5 src/jdk.crypto.cryptoki/share/native/libj2pkcs11/p11_sessmgmt.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/jdk.crypto.cryptoki/share/native/libj2pkcs11/p11_sessmgmt.c Tue Sep 12 19:03:39 2017 +0200 @@ -0,0 +1,634 @@ +/* + * Copyright (c) 2003, 2013, Oracle and/or its affiliates. All rights reserved. + */ + +/* Copyright (c) 2002 Graz University of Technology. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * 3. The end-user documentation included with the redistribution, if any, must + * include the following acknowledgment: + * + * "This product includes software developed by IAIK of Graz University of + * Technology." + * + * Alternately, this acknowledgment may appear in the software itself, if + * and wherever such third-party acknowledgments normally appear. + * + * 4. The names "Graz University of Technology" and "IAIK of Graz University of + * Technology" must not be used to endorse or promote products derived from + * this software without prior written permission. + * + * 5. Products derived from this software may not be called + * "IAIK PKCS Wrapper", nor may "IAIK" appear in their name, without prior + * written permission of Graz University of Technology. + * + * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESSED OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE LICENSOR BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, + * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, + * OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include "pkcs11wrapper.h" + +#include +#include +#include +#include + +#include "sun_security_pkcs11_wrapper_PKCS11.h" + +/* The list of notify callback handles that are currently active and waiting + * for callbacks from their sessions. + */ +#ifndef NO_CALLBACKS +NotifyListNode *notifyListHead = NULL; +jobject notifyListLock = NULL; +#endif /* NO_CALLBACKS */ + +#ifdef P11_ENABLE_C_OPENSESSION +/* + * Class: sun_security_pkcs11_wrapper_PKCS11 + * Method: C_OpenSession + * Signature: (JJLjava/lang/Object;Lsun/security/pkcs11/wrapper/CK_NOTIFY;)J + * Parametermapping: *PKCS11* + * @param jlong jSlotID CK_SLOT_ID slotID + * @param jlong jFlags CK_FLAGS flags + * @param jobject jApplication CK_VOID_PTR pApplication + * @param jobject jNotify CK_NOTIFY Notify + * @return jlong jSessionHandle CK_SESSION_HANDLE_PTR phSession + */ +JNIEXPORT jlong JNICALL Java_sun_security_pkcs11_wrapper_PKCS11_C_1OpenSession + (JNIEnv *env, jobject obj, jlong jSlotID, jlong jFlags, jobject jApplication, jobject jNotify) +{ + CK_SESSION_HANDLE ckSessionHandle; + CK_SLOT_ID ckSlotID; + CK_FLAGS ckFlags; + CK_VOID_PTR ckpApplication; + CK_NOTIFY ckNotify; + jlong jSessionHandle; + CK_RV rv; +#ifndef NO_CALLBACKS + NotifyEncapsulation *notifyEncapsulation = NULL; +#endif /* NO_CALLBACKS */ + + CK_FUNCTION_LIST_PTR ckpFunctions = getFunctionList(env, obj); + if (ckpFunctions == NULL) { return 0L; } + + ckSlotID = jLongToCKULong(jSlotID); + ckFlags = jLongToCKULong(jFlags); + +#ifndef NO_CALLBACKS + if (jNotify != NULL) { + notifyEncapsulation = (NotifyEncapsulation *) malloc(sizeof(NotifyEncapsulation)); + if (notifyEncapsulation == NULL) { + throwOutOfMemoryError(env, 0); + return 0L; + } + notifyEncapsulation->jApplicationData = (jApplication != NULL) + ? (*env)->NewGlobalRef(env, jApplication) + : NULL; + notifyEncapsulation->jNotifyObject = (*env)->NewGlobalRef(env, jNotify); + ckpApplication = notifyEncapsulation; + ckNotify = (CK_NOTIFY) ¬ifyCallback; + } else { + ckpApplication = NULL_PTR; + ckNotify = NULL_PTR; + } +#else + ckpApplication = NULL_PTR; + ckNotify = NULL_PTR; +#endif /* NO_CALLBACKS */ + + TRACE0("DEBUG: C_OpenSession"); + TRACE1(", slotID=%u", ckSlotID); + TRACE1(", flags=%x", ckFlags); + TRACE0(" ... "); + + rv = (*ckpFunctions->C_OpenSession)(ckSlotID, ckFlags, ckpApplication, ckNotify, &ckSessionHandle); + if (ckAssertReturnValueOK(env, rv) != CK_ASSERT_OK) { +#ifndef NO_CALLBACKS + if (notifyEncapsulation != NULL) { + if (notifyEncapsulation->jApplicationData != NULL) { + (*env)->DeleteGlobalRef(env, jApplication); + } + (*env)->DeleteGlobalRef(env, jNotify); + free(notifyEncapsulation); + } +#endif /* NO_CALLBACKS */ + return 0L; + } + + TRACE0("got session"); + TRACE1(", SessionHandle=%u", ckSessionHandle); + TRACE0(" ... "); + + jSessionHandle = ckULongToJLong(ckSessionHandle); + +#ifndef NO_CALLBACKS + if (notifyEncapsulation != NULL) { + /* store the notifyEncapsulation to enable later cleanup */ + putNotifyEntry(env, ckSessionHandle, notifyEncapsulation); + } +#endif /* NO_CALLBACKS */ + + TRACE0("FINISHED\n"); + + return jSessionHandle ; +} +#endif + +#ifdef P11_ENABLE_C_CLOSESESSION +/* + * Class: sun_security_pkcs11_wrapper_PKCS11 + * Method: C_CloseSession + * Signature: (J)V + * Parametermapping: *PKCS11* + * @param jlong jSessionHandle CK_SESSION_HANDLE hSession + */ +JNIEXPORT void JNICALL Java_sun_security_pkcs11_wrapper_PKCS11_C_1CloseSession + (JNIEnv *env, jobject obj, jlong jSessionHandle) +{ + CK_SESSION_HANDLE ckSessionHandle; + CK_RV rv; +#ifndef NO_CALLBACKS + NotifyEncapsulation *notifyEncapsulation; + jobject jApplicationData; +#endif /* NO_CALLBACKS */ + + CK_FUNCTION_LIST_PTR ckpFunctions = getFunctionList(env, obj); + if (ckpFunctions == NULL) { return; } + + ckSessionHandle = jLongToCKULong(jSessionHandle); + + rv = (*ckpFunctions->C_CloseSession)(ckSessionHandle); + if (ckAssertReturnValueOK(env, rv) != CK_ASSERT_OK) { return; } + +#ifndef NO_CALLBACKS + notifyEncapsulation = removeNotifyEntry(env, ckSessionHandle); + + if (notifyEncapsulation != NULL) { + /* there was a notify object used with this session, now dump the + * encapsulation object + */ + (*env)->DeleteGlobalRef(env, notifyEncapsulation->jNotifyObject); + jApplicationData = notifyEncapsulation->jApplicationData; + if (jApplicationData != NULL) { + (*env)->DeleteGlobalRef(env, jApplicationData); + } + free(notifyEncapsulation); + } +#endif /* NO_CALLBACKS */ + +} +#endif + +#ifdef P11_ENABLE_C_CLOSEALLSESSIONS +/* + * Class: sun_security_pkcs11_wrapper_PKCS11 + * Method: C_CloseAllSessions + * Signature: (J)V + * Parametermapping: *PKCS11* + * @param jlong jSlotID CK_SLOT_ID slotID + */ +JNIEXPORT void JNICALL Java_sun_security_pkcs11_wrapper_PKCS11_C_1CloseAllSessions + (JNIEnv *env, jobject obj, jlong jSlotID) +{ + CK_SLOT_ID ckSlotID; + CK_RV rv; +#ifndef NO_CALLBACKS + NotifyEncapsulation *notifyEncapsulation; + jobject jApplicationData; +#endif /* NO_CALLBACKS */ + + CK_FUNCTION_LIST_PTR ckpFunctions = getFunctionList(env, obj); + if (ckpFunctions == NULL) { return; } + + ckSlotID = jLongToCKULong(jSlotID); + + rv = (*ckpFunctions->C_CloseAllSessions)(ckSlotID); + if (ckAssertReturnValueOK(env, rv) != CK_ASSERT_OK) { return; } + +#ifndef NO_CALLBACKS + /* Remove all notify callback helper objects. */ + while ((notifyEncapsulation = removeFirstNotifyEntry(env)) != NULL) { + /* there was a notify object used with this session, now dump the + * encapsulation object + */ + (*env)->DeleteGlobalRef(env, notifyEncapsulation->jNotifyObject); + jApplicationData = notifyEncapsulation->jApplicationData; + if (jApplicationData != NULL) { + (*env)->DeleteGlobalRef(env, jApplicationData); + } + free(notifyEncapsulation); + } +#endif /* NO_CALLBACKS */ +} +#endif + +#ifdef P11_ENABLE_C_GETSESSIONINFO +/* + * Class: sun_security_pkcs11_wrapper_PKCS11 + * Method: C_GetSessionInfo + * Signature: (J)Lsun/security/pkcs11/wrapper/CK_SESSION_INFO; + * Parametermapping: *PKCS11* + * @param jlong jSessionHandle CK_SESSION_HANDLE hSession + * @return jobject jSessionInfo CK_SESSION_INFO_PTR pInfo + */ +JNIEXPORT jobject JNICALL Java_sun_security_pkcs11_wrapper_PKCS11_C_1GetSessionInfo + (JNIEnv *env, jobject obj, jlong jSessionHandle) +{ + CK_SESSION_HANDLE ckSessionHandle; + CK_SESSION_INFO ckSessionInfo; + jobject jSessionInfo=NULL; + CK_RV rv; + + CK_FUNCTION_LIST_PTR ckpFunctions = getFunctionList(env, obj); + if (ckpFunctions == NULL) { return NULL; } + + ckSessionHandle = jLongToCKULong(jSessionHandle); + + rv = (*ckpFunctions->C_GetSessionInfo)(ckSessionHandle, &ckSessionInfo); + if (ckAssertReturnValueOK(env, rv) == CK_ASSERT_OK) { + jSessionInfo = ckSessionInfoPtrToJSessionInfo(env, &ckSessionInfo); + } + return jSessionInfo ; +} +#endif + +#ifdef P11_ENABLE_C_GETOPERATIONSTATE +/* + * Class: sun_security_pkcs11_wrapper_PKCS11 + * Method: C_GetOperationState + * Signature: (J)[B + * Parametermapping: *PKCS11* + * @param jlong jSessionHandle CK_SESSION_HANDLE hSession + * @return jbyteArray jState CK_BYTE_PTR pOperationState + * CK_ULONG_PTR pulOperationStateLen + */ +JNIEXPORT jbyteArray JNICALL Java_sun_security_pkcs11_wrapper_PKCS11_C_1GetOperationState + (JNIEnv *env, jobject obj, jlong jSessionHandle) +{ + CK_SESSION_HANDLE ckSessionHandle; + CK_BYTE_PTR ckpState; + CK_ULONG ckStateLength; + jbyteArray jState = NULL; + CK_RV rv; + + CK_FUNCTION_LIST_PTR ckpFunctions = getFunctionList(env, obj); + if (ckpFunctions == NULL) { return NULL; } + + ckSessionHandle = jLongToCKULong(jSessionHandle); + + rv = (*ckpFunctions->C_GetOperationState)(ckSessionHandle, NULL_PTR, &ckStateLength); + if (ckAssertReturnValueOK(env, rv) != CK_ASSERT_OK) { return NULL ; } + + ckpState = (CK_BYTE_PTR) malloc(ckStateLength); + if (ckpState == NULL) { + throwOutOfMemoryError(env, 0); + return NULL; + } + + rv = (*ckpFunctions->C_GetOperationState)(ckSessionHandle, ckpState, &ckStateLength); + if (ckAssertReturnValueOK(env, rv) == CK_ASSERT_OK) { + jState = ckByteArrayToJByteArray(env, ckpState, ckStateLength); + } + free(ckpState); + + return jState ; +} +#endif + +#ifdef P11_ENABLE_C_SETOPERATIONSTATE +/* + * Class: sun_security_pkcs11_wrapper_PKCS11 + * Method: C_SetOperationState + * Signature: (J[BJJ)V + * Parametermapping: *PKCS11* + * @param jlong jSessionHandle CK_SESSION_HANDLE hSession + * @param jbyteArray jOperationState CK_BYTE_PTR pOperationState + * CK_ULONG ulOperationStateLen + * @param jlong jEncryptionKeyHandle CK_OBJECT_HANDLE hEncryptionKey + * @param jlong jAuthenticationKeyHandle CK_OBJECT_HANDLE hAuthenticationKey + */ +JNIEXPORT void JNICALL Java_sun_security_pkcs11_wrapper_PKCS11_C_1SetOperationState + (JNIEnv *env, jobject obj, jlong jSessionHandle, jbyteArray jOperationState, jlong jEncryptionKeyHandle, jlong jAuthenticationKeyHandle) +{ + CK_SESSION_HANDLE ckSessionHandle; + CK_BYTE_PTR ckpState = NULL_PTR; + CK_ULONG ckStateLength; + CK_OBJECT_HANDLE ckEncryptionKeyHandle; + CK_OBJECT_HANDLE ckAuthenticationKeyHandle; + CK_RV rv; + + CK_FUNCTION_LIST_PTR ckpFunctions = getFunctionList(env, obj); + if (ckpFunctions == NULL) { return; } + + ckSessionHandle = jLongToCKULong(jSessionHandle); + jByteArrayToCKByteArray(env, jOperationState, &ckpState, &ckStateLength); + if ((*env)->ExceptionCheck(env)) { return; } + + ckEncryptionKeyHandle = jLongToCKULong(jEncryptionKeyHandle); + ckAuthenticationKeyHandle = jLongToCKULong(jAuthenticationKeyHandle); + + rv = (*ckpFunctions->C_SetOperationState)(ckSessionHandle, ckpState, ckStateLength, ckEncryptionKeyHandle, ckAuthenticationKeyHandle); + + free(ckpState); + + if (ckAssertReturnValueOK(env, rv) != CK_ASSERT_OK) { return; } +} +#endif + +#ifdef P11_ENABLE_C_LOGIN +/* + * Class: sun_security_pkcs11_wrapper_PKCS11 + * Method: C_Login + * Signature: (JJ[C)V + * Parametermapping: *PKCS11* + * @param jlong jSessionHandle CK_SESSION_HANDLE hSession + * @param jlong jUserType CK_USER_TYPE userType + * @param jcharArray jPin CK_CHAR_PTR pPin + * CK_ULONG ulPinLen + */ +JNIEXPORT void JNICALL Java_sun_security_pkcs11_wrapper_PKCS11_C_1Login + (JNIEnv *env, jobject obj, jlong jSessionHandle, jlong jUserType, jcharArray jPin) +{ + CK_SESSION_HANDLE ckSessionHandle; + CK_USER_TYPE ckUserType; + CK_CHAR_PTR ckpPinArray = NULL_PTR; + CK_ULONG ckPinLength; + CK_RV rv; + + CK_FUNCTION_LIST_PTR ckpFunctions = getFunctionList(env, obj); + if (ckpFunctions == NULL) { return; } + + ckSessionHandle = jLongToCKULong(jSessionHandle); + ckUserType = jLongToCKULong(jUserType); + jCharArrayToCKCharArray(env, jPin, &ckpPinArray, &ckPinLength); + if ((*env)->ExceptionCheck(env)) { return; } + + rv = (*ckpFunctions->C_Login)(ckSessionHandle, ckUserType, ckpPinArray, ckPinLength); + + free(ckpPinArray); + + if (ckAssertReturnValueOK(env, rv) != CK_ASSERT_OK) { return; } +} +#endif + +#ifdef P11_ENABLE_C_LOGOUT +/* + * Class: sun_security_pkcs11_wrapper_PKCS11 + * Method: C_Logout + * Signature: (J)V + * Parametermapping: *PKCS11* + * @param jlong jSessionHandle CK_SESSION_HANDLE hSession + */ +JNIEXPORT void JNICALL Java_sun_security_pkcs11_wrapper_PKCS11_C_1Logout + (JNIEnv *env, jobject obj, jlong jSessionHandle) +{ + CK_SESSION_HANDLE ckSessionHandle; + CK_RV rv; + + CK_FUNCTION_LIST_PTR ckpFunctions = getFunctionList(env, obj); + if (ckpFunctions == NULL) { return; } + + ckSessionHandle = jLongToCKULong(jSessionHandle); + + rv = (*ckpFunctions->C_Logout)(ckSessionHandle); + if (ckAssertReturnValueOK(env, rv) != CK_ASSERT_OK) { return; } +} +#endif + +/* ************************************************************************** */ +/* Functions for keeping track of notify callbacks */ +/* ************************************************************************** */ + +#ifndef NO_CALLBACKS + +/* + * Add the given notify encapsulation object to the list of active notify + * objects. + * If notifyEncapsulation is NULL, this function does nothing. + */ +void putNotifyEntry(JNIEnv *env, CK_SESSION_HANDLE hSession, NotifyEncapsulation *notifyEncapsulation) { + NotifyListNode *currentNode, *newNode; + + if (notifyEncapsulation == NULL) { + return; + } + + newNode = (NotifyListNode *) malloc(sizeof(NotifyListNode)); + if (newNode == NULL) { + throwOutOfMemoryError(env, 0); + return; + } + newNode->hSession = hSession; + newNode->notifyEncapsulation = notifyEncapsulation; + newNode->next = NULL; + + (*env)->MonitorEnter(env, notifyListLock); /* synchronize access to list */ + + if (notifyListHead == NULL) { + /* this is the first entry */ + notifyListHead = newNode; + } else { + /* go to the last entry; i.e. the first node which's 'next' is NULL. + */ + currentNode = notifyListHead; + while (currentNode->next != NULL) { + currentNode = currentNode->next; + } + currentNode->next = newNode; + } + + (*env)->MonitorExit(env, notifyListLock); /* synchronize access to list */ +} + +/* + * Removes the active notifyEncapsulation object used with the given session and + * returns it. If there is no notifyEncapsulation active for this session, this + * function returns NULL. + */ +NotifyEncapsulation * removeNotifyEntry(JNIEnv *env, CK_SESSION_HANDLE hSession) { + NotifyEncapsulation *notifyEncapsulation; + NotifyListNode *currentNode, *previousNode; + + (*env)->MonitorEnter(env, notifyListLock); /* synchronize access to list */ + + if (notifyListHead == NULL) { + /* this is the first entry */ + notifyEncapsulation = NULL; + } else { + /* Find the node with the wanted session handle. Also stop, when we reach + * the last entry; i.e. the first node which's 'next' is NULL. + */ + currentNode = notifyListHead; + previousNode = NULL; + + while ((currentNode->hSession != hSession) && (currentNode->next != NULL)) { + previousNode = currentNode; + currentNode = currentNode->next; + } + + if (currentNode->hSession == hSession) { + /* We found a entry for the wanted session, now remove it. */ + if (previousNode == NULL) { + /* it's the first node */ + notifyListHead = currentNode->next; + } else { + previousNode->next = currentNode->next; + } + notifyEncapsulation = currentNode->notifyEncapsulation; + free(currentNode); + } else { + /* We did not find a entry for this session */ + notifyEncapsulation = NULL; + } + } + + (*env)->MonitorExit(env, notifyListLock); /* synchronize access to list */ + + return notifyEncapsulation ; +} + +/* + + * Removes the first notifyEncapsulation object. If there is no notifyEncapsulation, + * this function returns NULL. + */ +NotifyEncapsulation * removeFirstNotifyEntry(JNIEnv *env) { + NotifyEncapsulation *notifyEncapsulation; + NotifyListNode *currentNode; + + (*env)->MonitorEnter(env, notifyListLock); /* synchronize access to list */ + + if (notifyListHead == NULL) { + /* this is the first entry */ + notifyEncapsulation = NULL; + } else { + /* Remove the first entry. */ + currentNode = notifyListHead; + notifyListHead = notifyListHead->next; + notifyEncapsulation = currentNode->notifyEncapsulation; + free(currentNode); + } + + (*env)->MonitorExit(env, notifyListLock); /* synchronize access to list */ + + return notifyEncapsulation ; +} + +#endif /* NO_CALLBACKS */ + +#ifndef NO_CALLBACKS + +/* + * The function handling notify callbacks. It casts the pApplication parameter + * back to a NotifyEncapsulation structure and retrieves the Notify object and + * the application data from it. + * + * @param hSession The session, this callback is comming from. + * @param event The type of event that occurred. + * @param pApplication The application data as passed in upon OpenSession. In + this wrapper we always pass in a NotifyEncapsulation + object, which holds necessary information for delegating + the callback to the Java VM. + * @return + */ +CK_RV notifyCallback( + CK_SESSION_HANDLE hSession, /* the session's handle */ + CK_NOTIFICATION event, + CK_VOID_PTR pApplication /* passed to C_OpenSession */ +) +{ + NotifyEncapsulation *notifyEncapsulation; + extern JavaVM *jvm; + JNIEnv *env; + jint returnValue; + jlong jSessionHandle; + jlong jEvent; + jclass ckNotifyClass; + jmethodID jmethod; + jthrowable pkcs11Exception; + jclass pkcs11ExceptionClass; + jlong errorCode; + CK_RV rv = CKR_OK; + int wasAttached = 1; + + if (pApplication == NULL) { return rv ; } /* This should not occur in this wrapper. */ + + notifyEncapsulation = (NotifyEncapsulation *) pApplication; + + /* Get the currently running Java VM */ + if (jvm == NULL) { return rv ; } /* there is no VM running */ + + /* Determine, if current thread is already attached */ + returnValue = (*jvm)->GetEnv(jvm, (void **) &env, JNI_VERSION_1_2); + if (returnValue == JNI_EDETACHED) { + /* thread detached, so attach it */ + wasAttached = 0; + returnValue = (*jvm)->AttachCurrentThread(jvm, (void **) &env, NULL); + } else if (returnValue == JNI_EVERSION) { + /* this version of JNI is not supported, so just try to attach */ + /* we assume it was attached to ensure that this thread is not detached + * afterwards even though it should not + */ + wasAttached = 1; + returnValue = (*jvm)->AttachCurrentThread(jvm, (void **) &env, NULL); + } else { + /* attached */ + wasAttached = 1; + } + + jSessionHandle = ckULongToJLong(hSession); + jEvent = ckULongToJLong(event); + + ckNotifyClass = (*env)->FindClass(env, CLASS_NOTIFY); + if (ckNotifyClass == NULL) { return rv; } + jmethod = (*env)->GetMethodID(env, ckNotifyClass, "CK_NOTIFY", "(JJLjava/lang/Object;)V"); + if (jmethod == NULL) { return rv; } + + (*env)->CallVoidMethod(env, notifyEncapsulation->jNotifyObject, jmethod, + jSessionHandle, jEvent, notifyEncapsulation->jApplicationData); + + /* check, if callback threw an exception */ + pkcs11Exception = (*env)->ExceptionOccurred(env); + + if (pkcs11Exception != NULL) { + /* TBD: clear the pending exception with ExceptionClear? */ + /* The was an exception thrown, now we get the error-code from it */ + pkcs11ExceptionClass = (*env)->FindClass(env, CLASS_PKCS11EXCEPTION); + if (pkcs11ExceptionClass == NULL) { return rv; } + + jmethod = (*env)->GetMethodID(env, pkcs11ExceptionClass, "getErrorCode", "()J"); + if (jmethod == NULL) { return rv; } + + errorCode = (*env)->CallLongMethod(env, pkcs11Exception, jmethod); + rv = jLongToCKULong(errorCode); + } + + /* if we attached this thread to the VM just for callback, we detach it now */ + if (wasAttached) { + returnValue = (*jvm)->DetachCurrentThread(jvm); + } + + return rv ; +} + +#endif /* NO_CALLBACKS */