/* * 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 */