| /* | |
| * Copyright (c) 2005, 2024, Oracle and/or its affiliates. All rights reserved. | |
| * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. | |
| * | |
| * This code is free software; you can redistribute it and/or modify it | |
| * under the terms of the GNU General Public License version 2 only, as | |
| * published by the Free Software Foundation. Oracle designates this | |
| * particular file as subject to the "Classpath" exception as provided | |
| * by Oracle in the LICENSE file that accompanied this code. | |
| * | |
| * This code is distributed in the hope that it will be useful, but WITHOUT | |
| * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | |
| * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License | |
| * version 2 for more details (a copy is included in the LICENSE file that | |
| * accompanied this code). | |
| * | |
| * You should have received a copy of the GNU General Public License version | |
| * 2 along with this work; if not, write to the Free Software Foundation, | |
| * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. | |
| * | |
| * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA | |
| * or visit www.oracle.com if you need additional information or have any | |
| * questions. | |
| */ | |
| /* disable asserts in product mode */ | |
| #ifndef DEBUG | |
| #ifndef NDEBUG | |
| #define NDEBUG | |
| #endif | |
| #endif | |
| #include <stdio.h> | |
| #include <stdlib.h> | |
| #include <string.h> | |
| #include <assert.h> | |
| #include <winscard.h> | |
| // #define J2PCSC_DEBUG | |
| #ifdef J2PCSC_DEBUG | |
| #define debug_printf(format, ...) printf(format, ##__VA_ARGS__) | |
| #else | |
| #define debug_printf(format, ...) | |
| #endif | |
| #include "sun_security_smartcardio_PCSC.h" | |
| #include "pcsc_md.h" | |
| #include "jni_util.h" | |
| #define MAX_STACK_BUFFER_SIZE 8192 | |
| // make the buffers larger than what should be necessary, just in case | |
| #define ATR_BUFFER_SIZE 128 | |
| #define READERNAME_BUFFER_SIZE 128 | |
| #define RECEIVE_BUFFER_SIZE MAX_STACK_BUFFER_SIZE | |
| #define J2PCSC_EXCEPTION_NAME "sun/security/smartcardio/PCSCException" | |
| static void throwOutOfMemoryError(JNIEnv *env, const char *msg) { | |
| jclass cls = (*env)->FindClass(env, "java/lang/OutOfMemoryError"); | |
| if (cls != NULL) /* Otherwise an exception has already been thrown */ | |
| (*env)->ThrowNew(env, cls, msg); | |
| } | |
| void throwPCSCException(JNIEnv* env, LONG code) { | |
| jclass pcscClass; | |
| jmethodID constructor; | |
| jthrowable pcscException; | |
| pcscClass = (*env)->FindClass(env, J2PCSC_EXCEPTION_NAME); | |
| if (pcscClass == NULL) { | |
| return; | |
| } | |
| constructor = (*env)->GetMethodID(env, pcscClass, "<init>", "(I)V"); | |
| if (constructor == NULL) { | |
| return; | |
| } | |
| pcscException = (jthrowable) (*env)->NewObject(env, pcscClass, | |
| constructor, (jint)code); | |
| if (pcscException != NULL) { | |
| (*env)->Throw(env, pcscException); | |
| } | |
| } | |
| jboolean handleRV(JNIEnv* env, LONG code) { | |
| if (code == SCARD_S_SUCCESS) { | |
| return JNI_FALSE; | |
| } else { | |
| throwPCSCException(env, code); | |
| return JNI_TRUE; | |
| } | |
| } | |
| JNIEXPORT jint JNICALL DEF_JNI_OnLoad(JavaVM *vm, void *reserved) { | |
| return JNI_VERSION_1_4; | |
| } | |
| JNIEXPORT jlong JNICALL Java_sun_security_smartcardio_PCSC_SCardEstablishContext | |
| (JNIEnv *env, jclass thisClass, jint dwScope) | |
| { | |
| SCARDCONTEXT context = 0; | |
| LONG rv; | |
| debug_printf("-establishContext\n"); | |
| rv = CALL_SCardEstablishContext(dwScope, NULL, NULL, &context); | |
| if (handleRV(env, rv)) { | |
| return 0; | |
| } | |
| // note: SCARDCONTEXT is typedef'd as long, so this works | |
| return (jlong)context; | |
| } | |
| /** | |
| * Convert a multi string to a java string array, | |
| */ | |
| jobjectArray pcsc_multi2jstring(JNIEnv *env, char *spec) { | |
| jobjectArray result; | |
| jclass stringClass; | |
| char *cp, **tab = NULL; | |
| jstring js; | |
| int cnt = 0; | |
| cp = spec; | |
| while (*cp != 0) { | |
| cp += (strlen(cp) + 1); | |
| ++cnt; | |
| } | |
| tab = (char **)malloc(cnt * sizeof(char *)); | |
| if (tab == NULL) { | |
| throwOutOfMemoryError(env, NULL); | |
| return NULL; | |
| } | |
| cnt = 0; | |
| cp = spec; | |
| while (*cp != 0) { | |
| tab[cnt++] = cp; | |
| cp += (strlen(cp) + 1); | |
| } | |
| stringClass = (*env)->FindClass(env, "java/lang/String"); | |
| if (stringClass == NULL) { | |
| free(tab); | |
| return NULL; | |
| } | |
| result = (*env)->NewObjectArray(env, cnt, stringClass, NULL); | |
| if (result != NULL) { | |
| while (cnt-- > 0) { | |
| js = (*env)->NewStringUTF(env, tab[cnt]); | |
| if ((*env)->ExceptionCheck(env)) { | |
| free(tab); | |
| return NULL; | |
| } | |
| (*env)->SetObjectArrayElement(env, result, cnt, js); | |
| if ((*env)->ExceptionCheck(env)) { | |
| free(tab); | |
| return NULL; | |
| } | |
| (*env)->DeleteLocalRef(env, js); | |
| } | |
| } | |
| free(tab); | |
| return result; | |
| } | |
| JNIEXPORT jobjectArray JNICALL Java_sun_security_smartcardio_PCSC_SCardListReaders | |
| (JNIEnv *env, jclass thisClass, jlong jContext) | |
| { | |
| SCARDCONTEXT context = (SCARDCONTEXT)jContext; | |
| LONG rv; | |
| LPSTR mszReaders = NULL; | |
| DWORD size = 0; | |
| jobjectArray result; | |
| debug_printf("-context: %x\n", context); | |
| rv = CALL_SCardListReaders(context, NULL, NULL, &size); | |
| if (handleRV(env, rv)) { | |
| return NULL; | |
| } | |
| debug_printf("-size: %d\n", size); | |
| if (size != 0) { | |
| mszReaders = malloc(size); | |
| if (mszReaders == NULL) { | |
| throwOutOfMemoryError(env, NULL); | |
| return NULL; | |
| } | |
| rv = CALL_SCardListReaders(context, NULL, mszReaders, &size); | |
| if (handleRV(env, rv)) { | |
| free(mszReaders); | |
| return NULL; | |
| } | |
| debug_printf("-String: %s\n", mszReaders); | |
| } else { | |
| return NULL; | |
| } | |
| result = pcsc_multi2jstring(env, mszReaders); | |
| free(mszReaders); | |
| return result; | |
| } | |
| JNIEXPORT jlong JNICALL Java_sun_security_smartcardio_PCSC_SCardConnect | |
| (JNIEnv *env, jclass thisClass, jlong jContext, jstring jReaderName, | |
| jint jShareMode, jint jPreferredProtocols) | |
| { | |
| SCARDCONTEXT context = (SCARDCONTEXT)jContext; | |
| LONG rv; | |
| LPCSTR readerName; | |
| SCARDHANDLE card = 0; | |
| DWORD proto = 0; | |
| readerName = (*env)->GetStringUTFChars(env, jReaderName, NULL); | |
| if (readerName == NULL) { | |
| return 0; | |
| } | |
| rv = CALL_SCardConnect(context, readerName, jShareMode, jPreferredProtocols, &card, &proto); | |
| (*env)->ReleaseStringUTFChars(env, jReaderName, readerName); | |
| debug_printf("-cardhandle: %x\n", card); | |
| debug_printf("-protocol: %d\n", proto); | |
| if (handleRV(env, rv)) { | |
| return 0; | |
| } | |
| return (jlong)card; | |
| } | |
| JNIEXPORT jbyteArray JNICALL Java_sun_security_smartcardio_PCSC_SCardTransmit | |
| (JNIEnv *env, jclass thisClass, jlong jCard, jint protocol, | |
| jbyteArray jBuf, jint jOfs, jint jLen) | |
| { | |
| SCARDHANDLE card = (SCARDHANDLE)jCard; | |
| LONG rv; | |
| SCARD_IO_REQUEST sendPci; | |
| unsigned char *sbuf; | |
| unsigned char rbuf[RECEIVE_BUFFER_SIZE]; | |
| DWORD rlen = RECEIVE_BUFFER_SIZE; | |
| int ofs = (int)jOfs; | |
| int len = (int)jLen; | |
| jbyteArray jOut; | |
| sendPci.dwProtocol = protocol; | |
| sendPci.cbPciLength = sizeof(SCARD_IO_REQUEST); | |
| sbuf = (unsigned char *) ((*env)->GetByteArrayElements(env, jBuf, NULL)); | |
| if (sbuf == NULL) { | |
| return NULL; | |
| } | |
| rv = CALL_SCardTransmit(card, &sendPci, sbuf + ofs, len, NULL, rbuf, &rlen); | |
| (*env)->ReleaseByteArrayElements(env, jBuf, (jbyte *)sbuf, JNI_ABORT); | |
| if (handleRV(env, rv)) { | |
| return NULL; | |
| } | |
| jOut = (*env)->NewByteArray(env, rlen); | |
| if (jOut != NULL) { | |
| (*env)->SetByteArrayRegion(env, jOut, 0, rlen, (jbyte *)rbuf); | |
| if ((*env)->ExceptionCheck(env)) { | |
| return NULL; | |
| } | |
| } | |
| return jOut; | |
| } | |
| JNIEXPORT jbyteArray JNICALL Java_sun_security_smartcardio_PCSC_SCardStatus | |
| (JNIEnv *env, jclass thisClass, jlong jCard, jbyteArray jStatus) | |
| { | |
| SCARDHANDLE card = (SCARDHANDLE)jCard; | |
| LONG rv; | |
| char readerName[READERNAME_BUFFER_SIZE]; | |
| DWORD readerLen = READERNAME_BUFFER_SIZE; | |
| unsigned char atr[ATR_BUFFER_SIZE]; | |
| DWORD atrLen = ATR_BUFFER_SIZE; | |
| DWORD state = 0; | |
| DWORD protocol = 0; | |
| jbyteArray jArray; | |
| jbyte status[2]; | |
| rv = CALL_SCardStatus(card, readerName, &readerLen, &state, &protocol, atr, &atrLen); | |
| if (handleRV(env, rv)) { | |
| return NULL; | |
| } | |
| debug_printf("-reader: %s\n", readerName); | |
| debug_printf("-status: %d\n", state); | |
| debug_printf("-protocol: %d\n", protocol); | |
| jArray = (*env)->NewByteArray(env, atrLen); | |
| if (jArray == NULL) { | |
| return NULL; | |
| } | |
| (*env)->SetByteArrayRegion(env, jArray, 0, atrLen, (jbyte *)atr); | |
| if ((*env)->ExceptionCheck(env)) { | |
| return NULL; | |
| } | |
| status[0] = (jbyte) state; | |
| status[1] = (jbyte) protocol; | |
| (*env)->SetByteArrayRegion(env, jStatus, 0, 2, status); | |
| if ((*env)->ExceptionCheck(env)) { | |
| return NULL; | |
| } | |
| return jArray; | |
| } | |
| JNIEXPORT void JNICALL Java_sun_security_smartcardio_PCSC_SCardDisconnect | |
| (JNIEnv *env, jclass thisClass, jlong jCard, jint jDisposition) | |
| { | |
| SCARDHANDLE card = (SCARDHANDLE)jCard; | |
| LONG rv; | |
| rv = CALL_SCardDisconnect(card, jDisposition); | |
| debug_printf("-disconnect: 0x%X\n", rv); | |
| handleRV(env, rv); | |
| return; | |
| } | |
| JNIEXPORT jintArray JNICALL Java_sun_security_smartcardio_PCSC_SCardGetStatusChange | |
| (JNIEnv *env, jclass thisClass, jlong jContext, jlong jTimeout, | |
| jintArray jCurrentState, jobjectArray jReaderNames) | |
| { | |
| SCARDCONTEXT context = (SCARDCONTEXT)jContext; | |
| LONG rv; | |
| int readers = (*env)->GetArrayLength(env, jReaderNames); | |
| SCARD_READERSTATE *readerState; | |
| int i; | |
| jintArray jEventState = NULL; | |
| int *currentState = NULL; | |
| const char *readerName; | |
| readerState = calloc(readers, sizeof(SCARD_READERSTATE)); | |
| if (readerState == NULL && readers > 0) { | |
| throwOutOfMemoryError(env, NULL); | |
| return NULL; | |
| } | |
| currentState = (*env)->GetIntArrayElements(env, jCurrentState, NULL); | |
| if (currentState == NULL) { | |
| free(readerState); | |
| return NULL; | |
| } | |
| for (i = 0; i < readers; i++) { | |
| readerState[i].szReader = NULL; | |
| } | |
| for (i = 0; i < readers; i++) { | |
| jobject jReaderName = (*env)->GetObjectArrayElement(env, jReaderNames, i); | |
| if ((*env)->ExceptionCheck(env)) { | |
| goto cleanup; | |
| } | |
| readerName = (*env)->GetStringUTFChars(env, jReaderName, NULL); | |
| if (readerName == NULL) { | |
| goto cleanup; | |
| } | |
| readerState[i].szReader = strdup(readerName); | |
| (*env)->ReleaseStringUTFChars(env, jReaderName, readerName); | |
| if (readerState[i].szReader == NULL) { | |
| throwOutOfMemoryError(env, NULL); | |
| goto cleanup; | |
| } | |
| readerState[i].pvUserData = NULL; | |
| readerState[i].dwCurrentState = currentState[i]; | |
| readerState[i].dwEventState = SCARD_STATE_UNAWARE; | |
| readerState[i].cbAtr = 0; | |
| (*env)->DeleteLocalRef(env, jReaderName); | |
| } | |
| if (readers > 0) { | |
| rv = CALL_SCardGetStatusChange(context, (DWORD)jTimeout, readerState, readers); | |
| if (handleRV(env, rv)) { | |
| goto cleanup; | |
| } | |
| } | |
| jEventState = (*env)->NewIntArray(env, readers); | |
| if (jEventState == NULL) { | |
| goto cleanup; | |
| } | |
| for (i = 0; i < readers; i++) { | |
| jint eventStateTmp; | |
| debug_printf("-reader status %s: 0x%X, 0x%X\n", readerState[i].szReader, | |
| readerState[i].dwCurrentState, readerState[i].dwEventState); | |
| eventStateTmp = (jint)readerState[i].dwEventState; | |
| (*env)->SetIntArrayRegion(env, jEventState, i, 1, &eventStateTmp); | |
| if ((*env)->ExceptionCheck(env)) { | |
| jEventState = NULL; | |
| goto cleanup; | |
| } | |
| } | |
| cleanup: | |
| (*env)->ReleaseIntArrayElements(env, jCurrentState, currentState, JNI_ABORT); | |
| for (i = 0; i < readers; i++) { | |
| free((char *)readerState[i].szReader); | |
| } | |
| free(readerState); | |
| return jEventState; | |
| } | |
| JNIEXPORT void JNICALL Java_sun_security_smartcardio_PCSC_SCardBeginTransaction | |
| (JNIEnv *env, jclass thisClass, jlong jCard) | |
| { | |
| SCARDHANDLE card = (SCARDHANDLE)jCard; | |
| LONG rv; | |
| rv = CALL_SCardBeginTransaction(card); | |
| debug_printf("-beginTransaction: 0x%X\n", rv); | |
| handleRV(env, rv); | |
| return; | |
| } | |
| JNIEXPORT void JNICALL Java_sun_security_smartcardio_PCSC_SCardEndTransaction | |
| (JNIEnv *env, jclass thisClass, jlong jCard, jint jDisposition) | |
| { | |
| SCARDHANDLE card = (SCARDHANDLE)jCard; | |
| LONG rv; | |
| rv = CALL_SCardEndTransaction(card, jDisposition); | |
| debug_printf("-endTransaction: 0x%X\n", rv); | |
| handleRV(env, rv); | |
| return; | |
| } | |
| JNIEXPORT jbyteArray JNICALL Java_sun_security_smartcardio_PCSC_SCardControl | |
| (JNIEnv *env, jclass thisClass, jlong jCard, jint jControlCode, jbyteArray jSendBuffer) | |
| { | |
| SCARDHANDLE card = (SCARDHANDLE)jCard; | |
| LONG rv; | |
| jbyte* sendBuffer; | |
| jint sendBufferLength = (*env)->GetArrayLength(env, jSendBuffer); | |
| jbyte receiveBuffer[MAX_STACK_BUFFER_SIZE]; | |
| jint receiveBufferLength = MAX_STACK_BUFFER_SIZE; | |
| ULONG returnedLength = 0; | |
| jbyteArray jReceiveBuffer; | |
| sendBuffer = (*env)->GetByteArrayElements(env, jSendBuffer, NULL); | |
| if (sendBuffer == NULL) { | |
| return NULL; | |
| } | |
| #ifdef J2PCSC_DEBUG | |
| { | |
| int k; | |
| printf("-control: 0x%X\n", jControlCode); | |
| printf("-send: "); | |
| for (k = 0; k < sendBufferLength; k++) { | |
| printf("%02x ", sendBuffer[k]); | |
| } | |
| printf("\n"); | |
| } | |
| #endif | |
| rv = CALL_SCardControl(card, jControlCode, sendBuffer, sendBufferLength, | |
| receiveBuffer, receiveBufferLength, &returnedLength); | |
| (*env)->ReleaseByteArrayElements(env, jSendBuffer, sendBuffer, JNI_ABORT); | |
| if (handleRV(env, rv)) { | |
| return NULL; | |
| } | |
| #ifdef J2PCSC_DEBUG | |
| { | |
| int k; | |
| printf("-recv: "); | |
| for (k = 0; k < returnedLength; k++) { | |
| printf("%02x ", receiveBuffer[k]); | |
| } | |
| printf("\n"); | |
| } | |
| #endif | |
| jReceiveBuffer = (*env)->NewByteArray(env, returnedLength); | |
| if (jReceiveBuffer == NULL) { | |
| return NULL; | |
| } | |
| (*env)->SetByteArrayRegion(env, jReceiveBuffer, 0, returnedLength, receiveBuffer); | |
| if ((*env)->ExceptionCheck(env)) { | |
| return NULL; | |
| } | |
| return jReceiveBuffer; | |
| } |