1/*
2 * Copyright (c) 2003, 2013, Oracle and/or its affiliates. All rights reserved.
3 */
4
5/* Copyright  (c) 2002 Graz University of Technology. All rights reserved.
6 *
7 * Redistribution and use in  source and binary forms, with or without
8 * modification, are permitted  provided that the following conditions are met:
9 *
10 * 1. Redistributions of  source code must retain the above copyright notice,
11 *    this list of conditions and the following disclaimer.
12 *
13 * 2. Redistributions in  binary form must reproduce the above copyright notice,
14 *    this list of conditions and the following disclaimer in the documentation
15 *    and/or other materials provided with the distribution.
16 *
17 * 3. The end-user documentation included with the redistribution, if any, must
18 *    include the following acknowledgment:
19 *
20 *    "This product includes software developed by IAIK of Graz University of
21 *     Technology."
22 *
23 *    Alternately, this acknowledgment may appear in the software itself, if
24 *    and wherever such third-party acknowledgments normally appear.
25 *
26 * 4. The names "Graz University of Technology" and "IAIK of Graz University of
27 *    Technology" must not be used to endorse or promote products derived from
28 *    this software without prior written permission.
29 *
30 * 5. Products derived from this software may not be called
31 *    "IAIK PKCS Wrapper", nor may "IAIK" appear in their name, without prior
32 *    written permission of Graz University of Technology.
33 *
34 *  THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESSED OR IMPLIED
35 *  WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
36 *  WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
37 *  PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE LICENSOR BE
38 *  LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
39 *  OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
40 *  PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
41 *  OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
42 *  ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
43 *  OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
44 *  OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
45 *  POSSIBILITY  OF SUCH DAMAGE.
46 */
47
48#include "pkcs11wrapper.h"
49
50#include <stdio.h>
51#include <stdlib.h>
52#include <string.h>
53#include <assert.h>
54
55#include "sun_security_pkcs11_wrapper_PKCS11.h"
56
57/* The list of notify callback handles that are currently active and waiting
58 * for callbacks from their sessions.
59 */
60#ifndef NO_CALLBACKS
61NotifyListNode *notifyListHead = NULL;
62jobject notifyListLock = NULL;
63#endif /* NO_CALLBACKS */
64
65#ifdef P11_ENABLE_C_OPENSESSION
66/*
67 * Class:     sun_security_pkcs11_wrapper_PKCS11
68 * Method:    C_OpenSession
69 * Signature: (JJLjava/lang/Object;Lsun/security/pkcs11/wrapper/CK_NOTIFY;)J
70 * Parametermapping:                    *PKCS11*
71 * @param   jlong jSlotID               CK_SLOT_ID slotID
72 * @param   jlong jFlags                CK_FLAGS flags
73 * @param   jobject jApplication        CK_VOID_PTR pApplication
74 * @param   jobject jNotify             CK_NOTIFY Notify
75 * @return  jlong jSessionHandle        CK_SESSION_HANDLE_PTR phSession
76 */
77JNIEXPORT jlong JNICALL Java_sun_security_pkcs11_wrapper_PKCS11_C_1OpenSession
78    (JNIEnv *env, jobject obj, jlong jSlotID, jlong jFlags, jobject jApplication, jobject jNotify)
79{
80    CK_SESSION_HANDLE ckSessionHandle;
81    CK_SLOT_ID ckSlotID;
82    CK_FLAGS ckFlags;
83    CK_VOID_PTR ckpApplication;
84    CK_NOTIFY ckNotify;
85    jlong jSessionHandle;
86    CK_RV rv;
87#ifndef NO_CALLBACKS
88    NotifyEncapsulation *notifyEncapsulation = NULL;
89#endif /* NO_CALLBACKS */
90
91    CK_FUNCTION_LIST_PTR ckpFunctions = getFunctionList(env, obj);
92    if (ckpFunctions == NULL) { return 0L; }
93
94    ckSlotID = jLongToCKULong(jSlotID);
95    ckFlags = jLongToCKULong(jFlags);
96
97#ifndef NO_CALLBACKS
98    if (jNotify != NULL) {
99        notifyEncapsulation = (NotifyEncapsulation *) malloc(sizeof(NotifyEncapsulation));
100        if (notifyEncapsulation == NULL) {
101            throwOutOfMemoryError(env, 0);
102            return 0L;
103        }
104        notifyEncapsulation->jApplicationData = (jApplication != NULL)
105                ? (*env)->NewGlobalRef(env, jApplication)
106                : NULL;
107        notifyEncapsulation->jNotifyObject = (*env)->NewGlobalRef(env, jNotify);
108        ckpApplication = notifyEncapsulation;
109        ckNotify = (CK_NOTIFY) &notifyCallback;
110    } else {
111        ckpApplication = NULL_PTR;
112        ckNotify = NULL_PTR;
113    }
114#else
115        ckpApplication = NULL_PTR;
116        ckNotify = NULL_PTR;
117#endif /* NO_CALLBACKS */
118
119    TRACE0("DEBUG: C_OpenSession");
120    TRACE1(", slotID=%u", ckSlotID);
121    TRACE1(", flags=%x", ckFlags);
122    TRACE0(" ... ");
123
124    rv = (*ckpFunctions->C_OpenSession)(ckSlotID, ckFlags, ckpApplication, ckNotify, &ckSessionHandle);
125    if (ckAssertReturnValueOK(env, rv) != CK_ASSERT_OK) {
126#ifndef NO_CALLBACKS
127        if (notifyEncapsulation != NULL) {
128            if (notifyEncapsulation->jApplicationData != NULL) {
129                (*env)->DeleteGlobalRef(env, jApplication);
130            }
131            (*env)->DeleteGlobalRef(env, jNotify);
132            free(notifyEncapsulation);
133        }
134#endif /* NO_CALLBACKS */
135        return 0L;
136    }
137
138    TRACE0("got session");
139    TRACE1(", SessionHandle=%u", ckSessionHandle);
140    TRACE0(" ... ");
141
142    jSessionHandle = ckULongToJLong(ckSessionHandle);
143
144#ifndef NO_CALLBACKS
145    if (notifyEncapsulation != NULL) {
146        /* store the notifyEncapsulation to enable later cleanup */
147        putNotifyEntry(env, ckSessionHandle, notifyEncapsulation);
148    }
149#endif /* NO_CALLBACKS */
150
151    TRACE0("FINISHED\n");
152
153    return jSessionHandle ;
154}
155#endif
156
157#ifdef P11_ENABLE_C_CLOSESESSION
158/*
159 * Class:     sun_security_pkcs11_wrapper_PKCS11
160 * Method:    C_CloseSession
161 * Signature: (J)V
162 * Parametermapping:                    *PKCS11*
163 * @param   jlong jSessionHandle        CK_SESSION_HANDLE hSession
164 */
165JNIEXPORT void JNICALL Java_sun_security_pkcs11_wrapper_PKCS11_C_1CloseSession
166    (JNIEnv *env, jobject obj, jlong jSessionHandle)
167{
168    CK_SESSION_HANDLE ckSessionHandle;
169    CK_RV rv;
170#ifndef NO_CALLBACKS
171    NotifyEncapsulation *notifyEncapsulation;
172    jobject jApplicationData;
173#endif /* NO_CALLBACKS */
174
175    CK_FUNCTION_LIST_PTR ckpFunctions = getFunctionList(env, obj);
176    if (ckpFunctions == NULL) { return; }
177
178    ckSessionHandle = jLongToCKULong(jSessionHandle);
179
180    rv = (*ckpFunctions->C_CloseSession)(ckSessionHandle);
181    if (ckAssertReturnValueOK(env, rv) != CK_ASSERT_OK) { return; }
182
183#ifndef NO_CALLBACKS
184    notifyEncapsulation = removeNotifyEntry(env, ckSessionHandle);
185
186    if (notifyEncapsulation != NULL) {
187        /* there was a notify object used with this session, now dump the
188         * encapsulation object
189         */
190        (*env)->DeleteGlobalRef(env, notifyEncapsulation->jNotifyObject);
191        jApplicationData = notifyEncapsulation->jApplicationData;
192        if (jApplicationData != NULL) {
193            (*env)->DeleteGlobalRef(env, jApplicationData);
194        }
195        free(notifyEncapsulation);
196    }
197#endif /* NO_CALLBACKS */
198
199}
200#endif
201
202#ifdef P11_ENABLE_C_CLOSEALLSESSIONS
203/*
204 * Class:     sun_security_pkcs11_wrapper_PKCS11
205 * Method:    C_CloseAllSessions
206 * Signature: (J)V
207 * Parametermapping:                    *PKCS11*
208 * @param   jlong jSlotID               CK_SLOT_ID slotID
209 */
210JNIEXPORT void JNICALL Java_sun_security_pkcs11_wrapper_PKCS11_C_1CloseAllSessions
211    (JNIEnv *env, jobject obj, jlong jSlotID)
212{
213    CK_SLOT_ID ckSlotID;
214    CK_RV rv;
215#ifndef NO_CALLBACKS
216    NotifyEncapsulation *notifyEncapsulation;
217    jobject jApplicationData;
218#endif /* NO_CALLBACKS */
219
220    CK_FUNCTION_LIST_PTR ckpFunctions = getFunctionList(env, obj);
221    if (ckpFunctions == NULL) { return; }
222
223    ckSlotID = jLongToCKULong(jSlotID);
224
225    rv = (*ckpFunctions->C_CloseAllSessions)(ckSlotID);
226    if (ckAssertReturnValueOK(env, rv) != CK_ASSERT_OK) { return; }
227
228#ifndef NO_CALLBACKS
229    /* Remove all notify callback helper objects. */
230    while ((notifyEncapsulation = removeFirstNotifyEntry(env)) != NULL) {
231        /* there was a notify object used with this session, now dump the
232         * encapsulation object
233         */
234        (*env)->DeleteGlobalRef(env, notifyEncapsulation->jNotifyObject);
235        jApplicationData = notifyEncapsulation->jApplicationData;
236        if (jApplicationData != NULL) {
237            (*env)->DeleteGlobalRef(env, jApplicationData);
238        }
239        free(notifyEncapsulation);
240    }
241#endif /* NO_CALLBACKS */
242}
243#endif
244
245#ifdef P11_ENABLE_C_GETSESSIONINFO
246/*
247 * Class:     sun_security_pkcs11_wrapper_PKCS11
248 * Method:    C_GetSessionInfo
249 * Signature: (J)Lsun/security/pkcs11/wrapper/CK_SESSION_INFO;
250 * Parametermapping:                    *PKCS11*
251 * @param   jlong jSessionHandle        CK_SESSION_HANDLE hSession
252 * @return  jobject jSessionInfo        CK_SESSION_INFO_PTR pInfo
253 */
254JNIEXPORT jobject JNICALL Java_sun_security_pkcs11_wrapper_PKCS11_C_1GetSessionInfo
255    (JNIEnv *env, jobject obj, jlong jSessionHandle)
256{
257    CK_SESSION_HANDLE ckSessionHandle;
258    CK_SESSION_INFO ckSessionInfo;
259    jobject jSessionInfo=NULL;
260    CK_RV rv;
261
262    CK_FUNCTION_LIST_PTR ckpFunctions = getFunctionList(env, obj);
263    if (ckpFunctions == NULL) { return NULL; }
264
265    ckSessionHandle = jLongToCKULong(jSessionHandle);
266
267    rv = (*ckpFunctions->C_GetSessionInfo)(ckSessionHandle, &ckSessionInfo);
268    if (ckAssertReturnValueOK(env, rv) == CK_ASSERT_OK) {
269        jSessionInfo = ckSessionInfoPtrToJSessionInfo(env, &ckSessionInfo);
270    }
271    return jSessionInfo ;
272}
273#endif
274
275#ifdef P11_ENABLE_C_GETOPERATIONSTATE
276/*
277 * Class:     sun_security_pkcs11_wrapper_PKCS11
278 * Method:    C_GetOperationState
279 * Signature: (J)[B
280 * Parametermapping:                    *PKCS11*
281 * @param   jlong jSessionHandle        CK_SESSION_HANDLE hSession
282 * @return  jbyteArray jState           CK_BYTE_PTR pOperationState
283 *                                      CK_ULONG_PTR pulOperationStateLen
284 */
285JNIEXPORT jbyteArray JNICALL Java_sun_security_pkcs11_wrapper_PKCS11_C_1GetOperationState
286    (JNIEnv *env, jobject obj, jlong jSessionHandle)
287{
288    CK_SESSION_HANDLE ckSessionHandle;
289    CK_BYTE_PTR ckpState;
290    CK_ULONG ckStateLength;
291    jbyteArray jState = NULL;
292    CK_RV rv;
293
294    CK_FUNCTION_LIST_PTR ckpFunctions = getFunctionList(env, obj);
295    if (ckpFunctions == NULL) { return NULL; }
296
297    ckSessionHandle = jLongToCKULong(jSessionHandle);
298
299    rv = (*ckpFunctions->C_GetOperationState)(ckSessionHandle, NULL_PTR, &ckStateLength);
300    if (ckAssertReturnValueOK(env, rv) != CK_ASSERT_OK) { return NULL ; }
301
302    ckpState = (CK_BYTE_PTR) malloc(ckStateLength);
303    if (ckpState == NULL) {
304        throwOutOfMemoryError(env, 0);
305        return NULL;
306    }
307
308    rv = (*ckpFunctions->C_GetOperationState)(ckSessionHandle, ckpState, &ckStateLength);
309    if (ckAssertReturnValueOK(env, rv) == CK_ASSERT_OK) {
310        jState = ckByteArrayToJByteArray(env, ckpState, ckStateLength);
311    }
312    free(ckpState);
313
314    return jState ;
315}
316#endif
317
318#ifdef P11_ENABLE_C_SETOPERATIONSTATE
319/*
320 * Class:     sun_security_pkcs11_wrapper_PKCS11
321 * Method:    C_SetOperationState
322 * Signature: (J[BJJ)V
323 * Parametermapping:                        *PKCS11*
324 * @param   jlong jSessionHandle            CK_SESSION_HANDLE hSession
325 * @param   jbyteArray jOperationState      CK_BYTE_PTR pOperationState
326 *                                          CK_ULONG ulOperationStateLen
327 * @param   jlong jEncryptionKeyHandle      CK_OBJECT_HANDLE hEncryptionKey
328 * @param   jlong jAuthenticationKeyHandle  CK_OBJECT_HANDLE hAuthenticationKey
329 */
330JNIEXPORT void JNICALL Java_sun_security_pkcs11_wrapper_PKCS11_C_1SetOperationState
331    (JNIEnv *env, jobject obj, jlong jSessionHandle, jbyteArray jOperationState, jlong jEncryptionKeyHandle, jlong jAuthenticationKeyHandle)
332{
333    CK_SESSION_HANDLE ckSessionHandle;
334    CK_BYTE_PTR ckpState = NULL_PTR;
335    CK_ULONG ckStateLength;
336    CK_OBJECT_HANDLE ckEncryptionKeyHandle;
337    CK_OBJECT_HANDLE ckAuthenticationKeyHandle;
338    CK_RV rv;
339
340    CK_FUNCTION_LIST_PTR ckpFunctions = getFunctionList(env, obj);
341    if (ckpFunctions == NULL) { return; }
342
343    ckSessionHandle = jLongToCKULong(jSessionHandle);
344    jByteArrayToCKByteArray(env, jOperationState, &ckpState, &ckStateLength);
345    if ((*env)->ExceptionCheck(env)) { return; }
346
347    ckEncryptionKeyHandle = jLongToCKULong(jEncryptionKeyHandle);
348    ckAuthenticationKeyHandle = jLongToCKULong(jAuthenticationKeyHandle);
349
350    rv = (*ckpFunctions->C_SetOperationState)(ckSessionHandle, ckpState, ckStateLength, ckEncryptionKeyHandle, ckAuthenticationKeyHandle);
351
352    free(ckpState);
353
354    if (ckAssertReturnValueOK(env, rv) != CK_ASSERT_OK) { return; }
355}
356#endif
357
358#ifdef P11_ENABLE_C_LOGIN
359/*
360 * Class:     sun_security_pkcs11_wrapper_PKCS11
361 * Method:    C_Login
362 * Signature: (JJ[C)V
363 * Parametermapping:                    *PKCS11*
364 * @param   jlong jSessionHandle        CK_SESSION_HANDLE hSession
365 * @param   jlong jUserType             CK_USER_TYPE userType
366 * @param   jcharArray jPin             CK_CHAR_PTR pPin
367 *                                      CK_ULONG ulPinLen
368 */
369JNIEXPORT void JNICALL Java_sun_security_pkcs11_wrapper_PKCS11_C_1Login
370    (JNIEnv *env, jobject obj, jlong jSessionHandle, jlong jUserType, jcharArray jPin)
371{
372    CK_SESSION_HANDLE ckSessionHandle;
373    CK_USER_TYPE ckUserType;
374    CK_CHAR_PTR ckpPinArray = NULL_PTR;
375    CK_ULONG ckPinLength;
376    CK_RV rv;
377
378    CK_FUNCTION_LIST_PTR ckpFunctions = getFunctionList(env, obj);
379    if (ckpFunctions == NULL) { return; }
380
381    ckSessionHandle = jLongToCKULong(jSessionHandle);
382    ckUserType = jLongToCKULong(jUserType);
383    jCharArrayToCKCharArray(env, jPin, &ckpPinArray, &ckPinLength);
384    if ((*env)->ExceptionCheck(env)) { return; }
385
386    rv = (*ckpFunctions->C_Login)(ckSessionHandle, ckUserType, ckpPinArray, ckPinLength);
387
388    free(ckpPinArray);
389
390    if (ckAssertReturnValueOK(env, rv) != CK_ASSERT_OK) { return; }
391}
392#endif
393
394#ifdef P11_ENABLE_C_LOGOUT
395/*
396 * Class:     sun_security_pkcs11_wrapper_PKCS11
397 * Method:    C_Logout
398 * Signature: (J)V
399 * Parametermapping:                    *PKCS11*
400 * @param   jlong jSessionHandle        CK_SESSION_HANDLE hSession
401 */
402JNIEXPORT void JNICALL Java_sun_security_pkcs11_wrapper_PKCS11_C_1Logout
403    (JNIEnv *env, jobject obj, jlong jSessionHandle)
404{
405    CK_SESSION_HANDLE ckSessionHandle;
406    CK_RV rv;
407
408    CK_FUNCTION_LIST_PTR ckpFunctions = getFunctionList(env, obj);
409    if (ckpFunctions == NULL) { return; }
410
411    ckSessionHandle = jLongToCKULong(jSessionHandle);
412
413    rv = (*ckpFunctions->C_Logout)(ckSessionHandle);
414    if (ckAssertReturnValueOK(env, rv) != CK_ASSERT_OK) { return; }
415}
416#endif
417
418/* ************************************************************************** */
419/* Functions for keeping track of notify callbacks                            */
420/* ************************************************************************** */
421
422#ifndef NO_CALLBACKS
423
424/*
425 * Add the given notify encapsulation object to the list of active notify
426 * objects.
427 * If notifyEncapsulation is NULL, this function does nothing.
428 */
429void putNotifyEntry(JNIEnv *env, CK_SESSION_HANDLE hSession, NotifyEncapsulation *notifyEncapsulation) {
430    NotifyListNode *currentNode, *newNode;
431
432    if (notifyEncapsulation == NULL) {
433        return;
434    }
435
436    newNode = (NotifyListNode *) malloc(sizeof(NotifyListNode));
437    if (newNode == NULL) {
438        throwOutOfMemoryError(env, 0);
439        return;
440    }
441    newNode->hSession = hSession;
442    newNode->notifyEncapsulation = notifyEncapsulation;
443    newNode->next = NULL;
444
445    (*env)->MonitorEnter(env, notifyListLock); /* synchronize access to list */
446
447    if (notifyListHead == NULL) {
448        /* this is the first entry */
449        notifyListHead = newNode;
450    } else {
451        /* go to the last entry; i.e. the first node which's 'next' is NULL.
452         */
453        currentNode = notifyListHead;
454        while (currentNode->next != NULL) {
455            currentNode = currentNode->next;
456        }
457        currentNode->next = newNode;
458    }
459
460    (*env)->MonitorExit(env, notifyListLock); /* synchronize access to list */
461}
462
463/*
464 * Removes the active notifyEncapsulation object used with the given session and
465 * returns it. If there is no notifyEncapsulation active for this session, this
466 * function returns NULL.
467 */
468NotifyEncapsulation * removeNotifyEntry(JNIEnv *env, CK_SESSION_HANDLE hSession) {
469    NotifyEncapsulation *notifyEncapsulation;
470    NotifyListNode *currentNode, *previousNode;
471
472    (*env)->MonitorEnter(env, notifyListLock); /* synchronize access to list */
473
474    if (notifyListHead == NULL) {
475        /* this is the first entry */
476        notifyEncapsulation = NULL;
477    } else {
478        /* Find the node with the wanted session handle. Also stop, when we reach
479         * the last entry; i.e. the first node which's 'next' is NULL.
480         */
481        currentNode = notifyListHead;
482        previousNode = NULL;
483
484        while ((currentNode->hSession != hSession) && (currentNode->next != NULL)) {
485            previousNode = currentNode;
486            currentNode = currentNode->next;
487        }
488
489        if (currentNode->hSession == hSession) {
490            /* We found a entry for the wanted session, now remove it. */
491            if (previousNode == NULL) {
492                /* it's the first node */
493                notifyListHead = currentNode->next;
494            } else {
495                previousNode->next = currentNode->next;
496            }
497            notifyEncapsulation = currentNode->notifyEncapsulation;
498            free(currentNode);
499        } else {
500            /* We did not find a entry for this session */
501            notifyEncapsulation = NULL;
502        }
503    }
504
505    (*env)->MonitorExit(env, notifyListLock); /* synchronize access to list */
506
507    return notifyEncapsulation ;
508}
509
510/*
511
512 * Removes the first notifyEncapsulation object. If there is no notifyEncapsulation,
513 * this function returns NULL.
514 */
515NotifyEncapsulation * removeFirstNotifyEntry(JNIEnv *env) {
516    NotifyEncapsulation *notifyEncapsulation;
517    NotifyListNode *currentNode;
518
519    (*env)->MonitorEnter(env, notifyListLock); /* synchronize access to list */
520
521    if (notifyListHead == NULL) {
522        /* this is the first entry */
523        notifyEncapsulation = NULL;
524    } else {
525        /* Remove the first entry. */
526        currentNode = notifyListHead;
527        notifyListHead = notifyListHead->next;
528        notifyEncapsulation = currentNode->notifyEncapsulation;
529        free(currentNode);
530    }
531
532    (*env)->MonitorExit(env, notifyListLock); /* synchronize access to list */
533
534    return notifyEncapsulation ;
535}
536
537#endif /* NO_CALLBACKS */
538
539#ifndef NO_CALLBACKS
540
541/*
542 * The function handling notify callbacks. It casts the pApplication parameter
543 * back to a NotifyEncapsulation structure and retrieves the Notify object and
544 * the application data from it.
545 *
546 * @param hSession The session, this callback is comming from.
547 * @param event The type of event that occurred.
548 * @param pApplication The application data as passed in upon OpenSession. In
549                       this wrapper we always pass in a NotifyEncapsulation
550                       object, which holds necessary information for delegating
551                       the callback to the Java VM.
552 * @return
553 */
554CK_RV notifyCallback(
555    CK_SESSION_HANDLE hSession,     /* the session's handle */
556    CK_NOTIFICATION   event,
557    CK_VOID_PTR       pApplication  /* passed to C_OpenSession */
558)
559{
560    NotifyEncapsulation *notifyEncapsulation;
561    extern JavaVM *jvm;
562    JNIEnv *env;
563    jint returnValue;
564    jlong jSessionHandle;
565    jlong jEvent;
566    jclass ckNotifyClass;
567    jmethodID jmethod;
568    jthrowable pkcs11Exception;
569    jclass pkcs11ExceptionClass;
570    jlong errorCode;
571    CK_RV rv = CKR_OK;
572    int wasAttached = 1;
573
574    if (pApplication == NULL) { return rv ; } /* This should not occur in this wrapper. */
575
576    notifyEncapsulation = (NotifyEncapsulation *) pApplication;
577
578    /* Get the currently running Java VM */
579    if (jvm == NULL) { return rv ; } /* there is no VM running */
580
581    /* Determine, if current thread is already attached */
582    returnValue = (*jvm)->GetEnv(jvm, (void **) &env, JNI_VERSION_1_2);
583    if (returnValue == JNI_EDETACHED) {
584        /* thread detached, so attach it */
585        wasAttached = 0;
586        returnValue = (*jvm)->AttachCurrentThread(jvm, (void **) &env, NULL);
587    } else if (returnValue == JNI_EVERSION) {
588        /* this version of JNI is not supported, so just try to attach */
589        /* we assume it was attached to ensure that this thread is not detached
590         * afterwards even though it should not
591         */
592        wasAttached = 1;
593        returnValue = (*jvm)->AttachCurrentThread(jvm, (void **) &env, NULL);
594    } else {
595        /* attached */
596        wasAttached = 1;
597    }
598
599    jSessionHandle = ckULongToJLong(hSession);
600    jEvent = ckULongToJLong(event);
601
602    ckNotifyClass = (*env)->FindClass(env, CLASS_NOTIFY);
603    if (ckNotifyClass == NULL) { return rv; }
604    jmethod = (*env)->GetMethodID(env, ckNotifyClass, "CK_NOTIFY", "(JJLjava/lang/Object;)V");
605    if (jmethod == NULL) { return rv; }
606
607    (*env)->CallVoidMethod(env, notifyEncapsulation->jNotifyObject, jmethod,
608                         jSessionHandle, jEvent, notifyEncapsulation->jApplicationData);
609
610    /* check, if callback threw an exception */
611    pkcs11Exception = (*env)->ExceptionOccurred(env);
612
613    if (pkcs11Exception != NULL) {
614        /* TBD: clear the pending exception with ExceptionClear? */
615        /* The was an exception thrown, now we get the error-code from it */
616        pkcs11ExceptionClass = (*env)->FindClass(env, CLASS_PKCS11EXCEPTION);
617        if (pkcs11ExceptionClass == NULL) { return rv; }
618
619        jmethod = (*env)->GetMethodID(env, pkcs11ExceptionClass, "getErrorCode", "()J");
620        if (jmethod == NULL) { return rv; }
621
622        errorCode = (*env)->CallLongMethod(env, pkcs11Exception, jmethod);
623        rv = jLongToCKULong(errorCode);
624    }
625
626    /* if we attached this thread to the VM just for callback, we detach it now */
627    if (wasAttached) {
628        returnValue = (*jvm)->DetachCurrentThread(jvm);
629    }
630
631    return rv ;
632}
633
634#endif /* NO_CALLBACKS */
635