1 /*
   2  * Copyright (c) 2005, 2018, Oracle and/or its affiliates. All rights reserved.
   3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   4  *
   5  * This code is free software; you can redistribute it and/or modify it
   6  * under the terms of the GNU General Public License version 2 only, as
   7  * published by the Free Software Foundation.  Oracle designates this
   8  * particular file as subject to the "Classpath" exception as provided
   9  * by Oracle in the LICENSE file that accompanied this code.
  10  *
  11  * This code is distributed in the hope that it will be useful, but WITHOUT
  12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  14  * version 2 for more details (a copy is included in the LICENSE file that
  15  * accompanied this code).
  16  *
  17  * You should have received a copy of the GNU General Public License version
  18  * 2 along with this work; if not, write to the Free Software Foundation,
  19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  20  *
  21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  22  * or visit www.oracle.com if you need additional information or have any
  23  * questions.
  24  */
  25 
  26 /* disable asserts in product mode */
  27 #ifndef DEBUG
  28   #ifndef NDEBUG
  29     #define NDEBUG
  30   #endif
  31 #endif
  32 
  33 #include <stdio.h>
  34 #include <stdlib.h>
  35 #include <string.h>
  36 #include <assert.h>
  37 
  38 #include <winscard.h>
  39 
  40 // #define J2PCSC_DEBUG
  41 
  42 #ifdef J2PCSC_DEBUG
  43 #define dprintf(s) printf(s)
  44 #define dprintf1(s, p1) printf(s, p1)
  45 #define dprintf2(s, p1, p2) printf(s, p1, p2)
  46 #define dprintf3(s, p1, p2, p3) printf(s, p1, p2, p3)
  47 #else
  48 #define dprintf(s)
  49 #define dprintf1(s, p1)
  50 #define dprintf2(s, p1, p2)
  51 #define dprintf3(s, p1, p2, p3)
  52 #endif
  53 
  54 #include "sun_security_smartcardio_PCSC.h"
  55 
  56 #include "pcsc_md.h"
  57 
  58 #include "jni_util.h"
  59 
  60 #define MAX_STACK_BUFFER_SIZE 8192
  61 
  62 // make the buffers larger than what should be necessary, just in case
  63 #define ATR_BUFFER_SIZE 128
  64 #define READERNAME_BUFFER_SIZE 128
  65 #define RECEIVE_BUFFER_SIZE MAX_STACK_BUFFER_SIZE
  66 
  67 #define J2PCSC_EXCEPTION_NAME "sun/security/smartcardio/PCSCException"
  68 
  69 void throwOutOfMemoryError(JNIEnv *env, const char *msg) {
  70     jclass cls = (*env)->FindClass(env, "java/lang/OutOfMemoryError");
  71 
  72     if (cls != NULL) /* Otherwise an exception has already been thrown */
  73         (*env)->ThrowNew(env, cls, msg);
  74 
  75 }
  76 
  77 void throwPCSCException(JNIEnv* env, LONG code) {
  78     jclass pcscClass;
  79     jmethodID constructor;
  80     jthrowable pcscException;
  81 
  82     pcscClass = (*env)->FindClass(env, J2PCSC_EXCEPTION_NAME);
  83     if (pcscClass == NULL) {
  84         return;
  85     }
  86     constructor = (*env)->GetMethodID(env, pcscClass, "<init>", "(I)V");
  87     if (constructor == NULL) {
  88         return;
  89     }
  90     pcscException = (jthrowable) (*env)->NewObject(env, pcscClass,
  91         constructor, (jint)code);
  92     if (pcscException != NULL) {
  93         (*env)->Throw(env, pcscException);
  94     }
  95 }
  96 
  97 jboolean handleRV(JNIEnv* env, LONG code) {
  98     if (code == SCARD_S_SUCCESS) {
  99         return JNI_FALSE;
 100     } else {
 101         throwPCSCException(env, code);
 102         return JNI_TRUE;
 103     }
 104 }
 105 
 106 JNIEXPORT jint JNICALL DEF_JNI_OnLoad(JavaVM *vm, void *reserved) {
 107     return JNI_VERSION_1_4;
 108 }
 109 
 110 JNIEXPORT jlong JNICALL Java_sun_security_smartcardio_PCSC_SCardEstablishContext
 111     (JNIEnv *env, jclass thisClass, jint dwScope)
 112 {
 113     SCARDCONTEXT context = 0;
 114     LONG rv;
 115     dprintf("-establishContext\n");
 116     rv = CALL_SCardEstablishContext(dwScope, NULL, NULL, &context);
 117     if (handleRV(env, rv)) {
 118         return 0;
 119     }
 120     // note: SCARDCONTEXT is typedef'd as long, so this works
 121     return (jlong)context;
 122 }
 123 
 124 /**
 125  * Convert a multi string to a java string array,
 126  */
 127 jobjectArray pcsc_multi2jstring(JNIEnv *env, char *spec) {
 128     jobjectArray result;
 129     jclass stringClass;
 130     char *cp, **tab = NULL;
 131     jstring js;
 132     int cnt = 0;
 133 
 134     cp = spec;
 135     while (*cp != 0) {
 136         cp += (strlen(cp) + 1);
 137         ++cnt;
 138     }
 139 
 140     tab = (char **)malloc(cnt * sizeof(char *));
 141     if (tab == NULL) {
 142         throwOutOfMemoryError(env, NULL);
 143         return NULL;
 144     }
 145 
 146     cnt = 0;
 147     cp = spec;
 148     while (*cp != 0) {
 149         tab[cnt++] = cp;
 150         cp += (strlen(cp) + 1);
 151     }
 152 
 153     stringClass = (*env)->FindClass(env, "java/lang/String");
 154     if (stringClass == NULL) {
 155         free(tab);
 156         return NULL;
 157     }
 158 
 159     result = (*env)->NewObjectArray(env, cnt, stringClass, NULL);
 160     if (result != NULL) {
 161         while (cnt-- > 0) {
 162             js = (*env)->NewStringUTF(env, tab[cnt]);
 163             if ((*env)->ExceptionCheck(env)) {
 164                 free(tab);
 165                 return NULL;
 166             }
 167             (*env)->SetObjectArrayElement(env, result, cnt, js);
 168             if ((*env)->ExceptionCheck(env)) {
 169                 free(tab);
 170                 return NULL;
 171             }
 172             (*env)->DeleteLocalRef(env, js);
 173         }
 174     }
 175     free(tab);
 176     return result;
 177 }
 178 
 179 JNIEXPORT jobjectArray JNICALL Java_sun_security_smartcardio_PCSC_SCardListReaders
 180     (JNIEnv *env, jclass thisClass, jlong jContext)
 181 {
 182     SCARDCONTEXT context = (SCARDCONTEXT)jContext;
 183     LONG rv;
 184     LPTSTR mszReaders = NULL;
 185     DWORD size = 0;
 186     jobjectArray result;
 187 
 188     dprintf1("-context: %x\n", context);
 189     rv = CALL_SCardListReaders(context, NULL, NULL, &size);
 190     if (handleRV(env, rv)) {
 191         return NULL;
 192     }
 193     dprintf1("-size: %d\n", size);
 194 
 195     if (size != 0) {
 196         mszReaders = malloc(size);
 197         if (mszReaders == NULL) {
 198             throwOutOfMemoryError(env, NULL);
 199             return NULL;
 200         }
 201 
 202         rv = CALL_SCardListReaders(context, NULL, mszReaders, &size);
 203         if (handleRV(env, rv)) {
 204             free(mszReaders);
 205             return NULL;
 206         }
 207         dprintf1("-String: %s\n", mszReaders);
 208     } else {
 209       return NULL;
 210     }
 211 
 212     result = pcsc_multi2jstring(env, mszReaders);
 213     free(mszReaders);
 214     return result;
 215 }
 216 
 217 JNIEXPORT jlong JNICALL Java_sun_security_smartcardio_PCSC_SCardConnect
 218     (JNIEnv *env, jclass thisClass, jlong jContext, jstring jReaderName,
 219     jint jShareMode, jint jPreferredProtocols)
 220 {
 221     SCARDCONTEXT context = (SCARDCONTEXT)jContext;
 222     LONG rv;
 223     LPCTSTR readerName;
 224     SCARDHANDLE card = 0;
 225     DWORD proto = 0;
 226 
 227     readerName = (*env)->GetStringUTFChars(env, jReaderName, NULL);
 228     if (readerName == NULL) {
 229         return 0;
 230     }
 231     rv = CALL_SCardConnect(context, readerName, jShareMode, jPreferredProtocols, &card, &proto);
 232     (*env)->ReleaseStringUTFChars(env, jReaderName, readerName);
 233     dprintf1("-cardhandle: %x\n", card);
 234     dprintf1("-protocol: %d\n", proto);
 235     if (handleRV(env, rv)) {
 236         return 0;
 237     }
 238 
 239     return (jlong)card;
 240 }
 241 
 242 JNIEXPORT jbyteArray JNICALL Java_sun_security_smartcardio_PCSC_SCardTransmit
 243     (JNIEnv *env, jclass thisClass, jlong jCard, jint protocol,
 244     jbyteArray jBuf, jint jOfs, jint jLen)
 245 {
 246     SCARDHANDLE card = (SCARDHANDLE)jCard;
 247     LONG rv;
 248     SCARD_IO_REQUEST sendPci;
 249     unsigned char *sbuf;
 250     unsigned char rbuf[RECEIVE_BUFFER_SIZE];
 251     DWORD rlen = RECEIVE_BUFFER_SIZE;
 252     int ofs = (int)jOfs;
 253     int len = (int)jLen;
 254     jbyteArray jOut;
 255 
 256     sendPci.dwProtocol = protocol;
 257     sendPci.cbPciLength = sizeof(SCARD_IO_REQUEST);
 258 
 259     sbuf = (unsigned char *) ((*env)->GetByteArrayElements(env, jBuf, NULL));
 260     if (sbuf == NULL) {
 261         return NULL;
 262     }
 263     rv = CALL_SCardTransmit(card, &sendPci, sbuf + ofs, len, NULL, rbuf, &rlen);
 264     (*env)->ReleaseByteArrayElements(env, jBuf, (jbyte *)sbuf, JNI_ABORT);
 265 
 266     if (handleRV(env, rv)) {
 267         return NULL;
 268     }
 269 
 270     jOut = (*env)->NewByteArray(env, rlen);
 271     if (jOut != NULL) {
 272         (*env)->SetByteArrayRegion(env, jOut, 0, rlen, (jbyte *)rbuf);
 273         if ((*env)->ExceptionCheck(env)) {
 274             return NULL;
 275         }
 276     }
 277     return jOut;
 278 }
 279 
 280 JNIEXPORT jbyteArray JNICALL Java_sun_security_smartcardio_PCSC_SCardStatus
 281     (JNIEnv *env, jclass thisClass, jlong jCard, jbyteArray jStatus)
 282 {
 283     SCARDHANDLE card = (SCARDHANDLE)jCard;
 284     LONG rv;
 285     char readerName[READERNAME_BUFFER_SIZE];
 286     DWORD readerLen = READERNAME_BUFFER_SIZE;
 287     unsigned char atr[ATR_BUFFER_SIZE];
 288     DWORD atrLen = ATR_BUFFER_SIZE;
 289     DWORD state = 0;
 290     DWORD protocol = 0;
 291     jbyteArray jArray;
 292     jbyte status[2];
 293 
 294     rv = CALL_SCardStatus(card, readerName, &readerLen, &state, &protocol, atr, &atrLen);
 295     if (handleRV(env, rv)) {
 296         return NULL;
 297     }
 298     dprintf1("-reader: %s\n", readerName);
 299     dprintf1("-status: %d\n", state);
 300     dprintf1("-protocol: %d\n", protocol);
 301 
 302     jArray = (*env)->NewByteArray(env, atrLen);
 303     if (jArray == NULL) {
 304         return NULL;
 305     }
 306     (*env)->SetByteArrayRegion(env, jArray, 0, atrLen, (jbyte *)atr);
 307     if ((*env)->ExceptionCheck(env)) {
 308         return NULL;
 309     }
 310     status[0] = (jbyte) state;
 311     status[1] = (jbyte) protocol;
 312     (*env)->SetByteArrayRegion(env, jStatus, 0, 2, status);
 313     if ((*env)->ExceptionCheck(env)) {
 314         return NULL;
 315     }
 316     return jArray;
 317 }
 318 
 319 JNIEXPORT void JNICALL Java_sun_security_smartcardio_PCSC_SCardDisconnect
 320     (JNIEnv *env, jclass thisClass, jlong jCard, jint jDisposition)
 321 {
 322     SCARDHANDLE card = (SCARDHANDLE)jCard;
 323     LONG rv;
 324 
 325     rv = CALL_SCardDisconnect(card, jDisposition);
 326     dprintf1("-disconnect: 0x%X\n", rv);
 327     handleRV(env, rv);
 328     return;
 329 }
 330 
 331 JNIEXPORT jintArray JNICALL Java_sun_security_smartcardio_PCSC_SCardGetStatusChange
 332     (JNIEnv *env, jclass thisClass, jlong jContext, jlong jTimeout,
 333     jintArray jCurrentState, jobjectArray jReaderNames)
 334 {
 335     SCARDCONTEXT context = (SCARDCONTEXT)jContext;
 336     LONG rv;
 337     int readers = (*env)->GetArrayLength(env, jReaderNames);
 338     SCARD_READERSTATE *readerState;
 339     int i;
 340     jintArray jEventState = NULL;
 341     int *currentState = NULL;
 342     const char *readerName;
 343 
 344     readerState = calloc(readers, sizeof(SCARD_READERSTATE));
 345     if (readerState == NULL && readers > 0) {
 346         throwOutOfMemoryError(env, NULL);
 347         return NULL;
 348     }
 349 
 350     currentState = (*env)->GetIntArrayElements(env, jCurrentState, NULL);
 351     if (currentState == NULL) {
 352         free(readerState);
 353         return NULL;
 354     }
 355 
 356     for (i = 0; i < readers; i++) {
 357         readerState[i].szReader = NULL;
 358     }
 359 
 360     for (i = 0; i < readers; i++) {
 361         jobject jReaderName = (*env)->GetObjectArrayElement(env, jReaderNames, i);
 362         if ((*env)->ExceptionCheck(env)) {
 363             goto cleanup;
 364         }
 365         readerName = (*env)->GetStringUTFChars(env, jReaderName, NULL);
 366         if (readerName == NULL) {
 367             goto cleanup;
 368         }
 369         readerState[i].szReader = strdup(readerName);
 370         (*env)->ReleaseStringUTFChars(env, jReaderName, readerName);
 371         if (readerState[i].szReader == NULL) {
 372             throwOutOfMemoryError(env, NULL);
 373             goto cleanup;
 374         }
 375         readerState[i].pvUserData = NULL;
 376         readerState[i].dwCurrentState = currentState[i];
 377         readerState[i].dwEventState = SCARD_STATE_UNAWARE;
 378         readerState[i].cbAtr = 0;
 379         (*env)->DeleteLocalRef(env, jReaderName);
 380     }
 381 
 382     if (readers > 0) {
 383         rv = CALL_SCardGetStatusChange(context, (DWORD)jTimeout, readerState, readers);
 384         if (handleRV(env, rv)) {
 385             goto cleanup;
 386         }
 387     }
 388 
 389     jEventState = (*env)->NewIntArray(env, readers);
 390     if (jEventState == NULL) {
 391         goto cleanup;
 392     }
 393     for (i = 0; i < readers; i++) {
 394         jint eventStateTmp;
 395         dprintf3("-reader status %s: 0x%X, 0x%X\n", readerState[i].szReader,
 396             readerState[i].dwCurrentState, readerState[i].dwEventState);
 397         eventStateTmp = (jint)readerState[i].dwEventState;
 398         (*env)->SetIntArrayRegion(env, jEventState, i, 1, &eventStateTmp);
 399         if ((*env)->ExceptionCheck(env)) {
 400             jEventState = NULL;
 401             goto cleanup;
 402         }
 403     }
 404 cleanup:
 405     (*env)->ReleaseIntArrayElements(env, jCurrentState, currentState, JNI_ABORT);
 406     for (i = 0; i < readers; i++) {
 407         free((char *)readerState[i].szReader);
 408     }
 409     free(readerState);
 410     return jEventState;
 411 }
 412 
 413 JNIEXPORT void JNICALL Java_sun_security_smartcardio_PCSC_SCardBeginTransaction
 414     (JNIEnv *env, jclass thisClass, jlong jCard)
 415 {
 416     SCARDHANDLE card = (SCARDHANDLE)jCard;
 417     LONG rv;
 418 
 419     rv = CALL_SCardBeginTransaction(card);
 420     dprintf1("-beginTransaction: 0x%X\n", rv);
 421     handleRV(env, rv);
 422     return;
 423 }
 424 
 425 JNIEXPORT void JNICALL Java_sun_security_smartcardio_PCSC_SCardEndTransaction
 426     (JNIEnv *env, jclass thisClass, jlong jCard, jint jDisposition)
 427 {
 428     SCARDHANDLE card = (SCARDHANDLE)jCard;
 429     LONG rv;
 430 
 431     rv = CALL_SCardEndTransaction(card, jDisposition);
 432     dprintf1("-endTransaction: 0x%X\n", rv);
 433     handleRV(env, rv);
 434     return;
 435 }
 436 
 437 JNIEXPORT jbyteArray JNICALL Java_sun_security_smartcardio_PCSC_SCardControl
 438     (JNIEnv *env, jclass thisClass, jlong jCard, jint jControlCode, jbyteArray jSendBuffer)
 439 {
 440     SCARDHANDLE card = (SCARDHANDLE)jCard;
 441     LONG rv;
 442     jbyte* sendBuffer;
 443     jint sendBufferLength = (*env)->GetArrayLength(env, jSendBuffer);
 444     jbyte receiveBuffer[MAX_STACK_BUFFER_SIZE];
 445     jint receiveBufferLength = MAX_STACK_BUFFER_SIZE;
 446     ULONG returnedLength = 0;
 447     jbyteArray jReceiveBuffer;
 448 
 449     sendBuffer = (*env)->GetByteArrayElements(env, jSendBuffer, NULL);
 450     if (sendBuffer == NULL) {
 451         return NULL;
 452     }
 453 
 454 #ifdef J2PCSC_DEBUG
 455 {
 456     int k;
 457     printf("-control: 0x%X\n", jControlCode);
 458     printf("-send: ");
 459     for (k = 0; k < sendBufferLength; k++) {
 460         printf("%02x ", sendBuffer[k]);
 461     }
 462     printf("\n");
 463 }
 464 #endif
 465 
 466     rv = CALL_SCardControl(card, jControlCode, sendBuffer, sendBufferLength,
 467         receiveBuffer, receiveBufferLength, &returnedLength);
 468 
 469     (*env)->ReleaseByteArrayElements(env, jSendBuffer, sendBuffer, JNI_ABORT);
 470     if (handleRV(env, rv)) {
 471         return NULL;
 472     }
 473 
 474 #ifdef J2PCSC_DEBUG
 475 {
 476     int k;
 477     printf("-recv:  ");
 478     for (k = 0; k < returnedLength; k++) {
 479         printf("%02x ", receiveBuffer[k]);
 480     }
 481     printf("\n");
 482 }
 483 #endif
 484 
 485     jReceiveBuffer = (*env)->NewByteArray(env, returnedLength);
 486     if (jReceiveBuffer == NULL) {
 487         return NULL;
 488     }
 489     (*env)->SetByteArrayRegion(env, jReceiveBuffer, 0, returnedLength, receiveBuffer);
 490     if ((*env)->ExceptionCheck(env)) {
 491         return NULL;
 492     }
 493     return jReceiveBuffer;
 494 }