1 /*
   2  * Copyright (c) 2005, 2015, 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) {
 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     }
 209 
 210     result = pcsc_multi2jstring(env, mszReaders);
 211     free(mszReaders);
 212     return result;
 213 }
 214 
 215 JNIEXPORT jlong JNICALL Java_sun_security_smartcardio_PCSC_SCardConnect
 216     (JNIEnv *env, jclass thisClass, jlong jContext, jstring jReaderName,
 217     jint jShareMode, jint jPreferredProtocols)
 218 {
 219     SCARDCONTEXT context = (SCARDCONTEXT)jContext;
 220     LONG rv;
 221     LPCTSTR readerName;
 222     SCARDHANDLE card = 0;
 223     DWORD proto = 0;
 224 
 225     readerName = (*env)->GetStringUTFChars(env, jReaderName, NULL);
 226     if (readerName == NULL) {
 227         return 0;
 228     }
 229     rv = CALL_SCardConnect(context, readerName, jShareMode, jPreferredProtocols, &card, &proto);
 230     (*env)->ReleaseStringUTFChars(env, jReaderName, readerName);
 231     dprintf1("-cardhandle: %x\n", card);
 232     dprintf1("-protocol: %d\n", proto);
 233     if (handleRV(env, rv)) {
 234         return 0;
 235     }
 236 
 237     return (jlong)card;
 238 }
 239 
 240 JNIEXPORT jbyteArray JNICALL Java_sun_security_smartcardio_PCSC_SCardTransmit
 241     (JNIEnv *env, jclass thisClass, jlong jCard, jint protocol,
 242     jbyteArray jBuf, jint jOfs, jint jLen)
 243 {
 244     SCARDHANDLE card = (SCARDHANDLE)jCard;
 245     LONG rv;
 246     SCARD_IO_REQUEST sendPci;
 247     unsigned char *sbuf;
 248     unsigned char rbuf[RECEIVE_BUFFER_SIZE];
 249     DWORD rlen = RECEIVE_BUFFER_SIZE;
 250     int ofs = (int)jOfs;
 251     int len = (int)jLen;
 252     jbyteArray jOut;
 253 
 254     sendPci.dwProtocol = protocol;
 255     sendPci.cbPciLength = sizeof(SCARD_IO_REQUEST);
 256 
 257     sbuf = (unsigned char *) ((*env)->GetByteArrayElements(env, jBuf, NULL));
 258     if (sbuf == NULL) {
 259         return NULL;
 260     }
 261     rv = CALL_SCardTransmit(card, &sendPci, sbuf + ofs, len, NULL, rbuf, &rlen);
 262     (*env)->ReleaseByteArrayElements(env, jBuf, (jbyte *)sbuf, JNI_ABORT);
 263 
 264     if (handleRV(env, rv)) {
 265         return NULL;
 266     }
 267 
 268     jOut = (*env)->NewByteArray(env, rlen);
 269     if (jOut != NULL) {
 270         (*env)->SetByteArrayRegion(env, jOut, 0, rlen, (jbyte *)rbuf);
 271         if ((*env)->ExceptionCheck(env)) {
 272             return NULL;
 273         }
 274     }
 275     return jOut;
 276 }
 277 
 278 JNIEXPORT jbyteArray JNICALL Java_sun_security_smartcardio_PCSC_SCardStatus
 279     (JNIEnv *env, jclass thisClass, jlong jCard, jbyteArray jStatus)
 280 {
 281     SCARDHANDLE card = (SCARDHANDLE)jCard;
 282     LONG rv;
 283     char readerName[READERNAME_BUFFER_SIZE];
 284     DWORD readerLen = READERNAME_BUFFER_SIZE;
 285     unsigned char atr[ATR_BUFFER_SIZE];
 286     DWORD atrLen = ATR_BUFFER_SIZE;
 287     DWORD state = 0;
 288     DWORD protocol = 0;
 289     jbyteArray jArray;
 290     jbyte status[2];
 291 
 292     rv = CALL_SCardStatus(card, readerName, &readerLen, &state, &protocol, atr, &atrLen);
 293     if (handleRV(env, rv)) {
 294         return NULL;
 295     }
 296     dprintf1("-reader: %s\n", readerName);
 297     dprintf1("-status: %d\n", state);
 298     dprintf1("-protocol: %d\n", protocol);
 299 
 300     jArray = (*env)->NewByteArray(env, atrLen);
 301     if (jArray == NULL) {
 302         return NULL;
 303     }
 304     (*env)->SetByteArrayRegion(env, jArray, 0, atrLen, (jbyte *)atr);
 305     if ((*env)->ExceptionCheck(env)) {
 306         return NULL;
 307     }
 308     status[0] = (jbyte) state;
 309     status[1] = (jbyte) protocol;
 310     (*env)->SetByteArrayRegion(env, jStatus, 0, 2, status);
 311     if ((*env)->ExceptionCheck(env)) {
 312         return NULL;
 313     }
 314     return jArray;
 315 }
 316 
 317 JNIEXPORT void JNICALL Java_sun_security_smartcardio_PCSC_SCardDisconnect
 318     (JNIEnv *env, jclass thisClass, jlong jCard, jint jDisposition)
 319 {
 320     SCARDHANDLE card = (SCARDHANDLE)jCard;
 321     LONG rv;
 322 
 323     rv = CALL_SCardDisconnect(card, jDisposition);
 324     dprintf1("-disconnect: 0x%X\n", rv);
 325     handleRV(env, rv);
 326     return;
 327 }
 328 
 329 JNIEXPORT jintArray JNICALL Java_sun_security_smartcardio_PCSC_SCardGetStatusChange
 330     (JNIEnv *env, jclass thisClass, jlong jContext, jlong jTimeout,
 331     jintArray jCurrentState, jobjectArray jReaderNames)
 332 {
 333     SCARDCONTEXT context = (SCARDCONTEXT)jContext;
 334     LONG rv;
 335     int readers = (*env)->GetArrayLength(env, jReaderNames);
 336     SCARD_READERSTATE *readerState;
 337     int i;
 338     jintArray jEventState = NULL;
 339     int *currentState = NULL;
 340     const char *readerName;
 341 
 342     readerState = calloc(readers, sizeof(SCARD_READERSTATE));
 343     if (readerState == NULL && readers > 0) {
 344         throwOutOfMemoryError(env, NULL);
 345         return NULL;
 346     }
 347 
 348     currentState = (*env)->GetIntArrayElements(env, jCurrentState, NULL);
 349     if (currentState == NULL) {
 350         free(readerState);
 351         return NULL;
 352     }
 353 
 354     for (i = 0; i < readers; i++) {
 355         readerState[i].szReader = NULL;
 356     }
 357 
 358     for (i = 0; i < readers; i++) {
 359         jobject jReaderName = (*env)->GetObjectArrayElement(env, jReaderNames, i);
 360         if ((*env)->ExceptionCheck(env)) {
 361             goto cleanup;
 362         }
 363         readerName = (*env)->GetStringUTFChars(env, jReaderName, NULL);
 364         if (readerName == NULL) {
 365             goto cleanup;
 366         }
 367         readerState[i].szReader = strdup(readerName);
 368         (*env)->ReleaseStringUTFChars(env, jReaderName, readerName);
 369         if (readerState[i].szReader == NULL) {
 370             throwOutOfMemoryError(env, NULL);
 371             goto cleanup;
 372         }
 373         readerState[i].pvUserData = NULL;
 374         readerState[i].dwCurrentState = currentState[i];
 375         readerState[i].dwEventState = SCARD_STATE_UNAWARE;
 376         readerState[i].cbAtr = 0;
 377         (*env)->DeleteLocalRef(env, jReaderName);
 378     }
 379 
 380     if (readers > 0) {
 381         rv = CALL_SCardGetStatusChange(context, (DWORD)jTimeout, readerState, readers);
 382         if (handleRV(env, rv)) {
 383             goto cleanup;
 384         }
 385     }
 386 
 387     jEventState = (*env)->NewIntArray(env, readers);
 388     if (jEventState == NULL) {
 389         goto cleanup;
 390     }
 391     for (i = 0; i < readers; i++) {
 392         jint eventStateTmp;
 393         dprintf3("-reader status %s: 0x%X, 0x%X\n", readerState[i].szReader,
 394             readerState[i].dwCurrentState, readerState[i].dwEventState);
 395         eventStateTmp = (jint)readerState[i].dwEventState;
 396         (*env)->SetIntArrayRegion(env, jEventState, i, 1, &eventStateTmp);
 397         if ((*env)->ExceptionCheck(env)) {
 398             jEventState = NULL;
 399             goto cleanup;
 400         }
 401     }
 402 cleanup:
 403     (*env)->ReleaseIntArrayElements(env, jCurrentState, currentState, JNI_ABORT);
 404     for (i = 0; i < readers; i++) {
 405         free((char *)readerState[i].szReader);
 406     }
 407     free(readerState);
 408     return jEventState;
 409 }
 410 
 411 JNIEXPORT void JNICALL Java_sun_security_smartcardio_PCSC_SCardBeginTransaction
 412     (JNIEnv *env, jclass thisClass, jlong jCard)
 413 {
 414     SCARDHANDLE card = (SCARDHANDLE)jCard;
 415     LONG rv;
 416 
 417     rv = CALL_SCardBeginTransaction(card);
 418     dprintf1("-beginTransaction: 0x%X\n", rv);
 419     handleRV(env, rv);
 420     return;
 421 }
 422 
 423 JNIEXPORT void JNICALL Java_sun_security_smartcardio_PCSC_SCardEndTransaction
 424     (JNIEnv *env, jclass thisClass, jlong jCard, jint jDisposition)
 425 {
 426     SCARDHANDLE card = (SCARDHANDLE)jCard;
 427     LONG rv;
 428 
 429     rv = CALL_SCardEndTransaction(card, jDisposition);
 430     dprintf1("-endTransaction: 0x%X\n", rv);
 431     handleRV(env, rv);
 432     return;
 433 }
 434 
 435 JNIEXPORT jbyteArray JNICALL Java_sun_security_smartcardio_PCSC_SCardControl
 436     (JNIEnv *env, jclass thisClass, jlong jCard, jint jControlCode, jbyteArray jSendBuffer)
 437 {
 438     SCARDHANDLE card = (SCARDHANDLE)jCard;
 439     LONG rv;
 440     jbyte* sendBuffer;
 441     jint sendBufferLength = (*env)->GetArrayLength(env, jSendBuffer);
 442     jbyte receiveBuffer[MAX_STACK_BUFFER_SIZE];
 443     jint receiveBufferLength = MAX_STACK_BUFFER_SIZE;
 444     ULONG returnedLength = 0;
 445     jbyteArray jReceiveBuffer;
 446 
 447     sendBuffer = (*env)->GetByteArrayElements(env, jSendBuffer, NULL);
 448     if (sendBuffer == NULL) {
 449         return NULL;
 450     }
 451 
 452 #ifdef J2PCSC_DEBUG
 453 {
 454     int k;
 455     printf("-control: 0x%X\n", jControlCode);
 456     printf("-send: ");
 457     for (k = 0; k < sendBufferLength; k++) {
 458         printf("%02x ", sendBuffer[k]);
 459     }
 460     printf("\n");
 461 }
 462 #endif
 463 
 464     rv = CALL_SCardControl(card, jControlCode, sendBuffer, sendBufferLength,
 465         receiveBuffer, receiveBufferLength, &returnedLength);
 466 
 467     (*env)->ReleaseByteArrayElements(env, jSendBuffer, sendBuffer, JNI_ABORT);
 468     if (handleRV(env, rv)) {
 469         return NULL;
 470     }
 471 
 472 #ifdef J2PCSC_DEBUG
 473 {
 474     int k;
 475     printf("-recv:  ");
 476     for (k = 0; k < returnedLength; k++) {
 477         printf("%02x ", receiveBuffer[k]);
 478     }
 479     printf("\n");
 480 }
 481 #endif
 482 
 483     jReceiveBuffer = (*env)->NewByteArray(env, returnedLength);
 484     if (jReceiveBuffer == NULL) {
 485         return NULL;
 486     }
 487     (*env)->SetByteArrayRegion(env, jReceiveBuffer, 0, returnedLength, receiveBuffer);
 488     if ((*env)->ExceptionCheck(env)) {
 489         return NULL;
 490     }
 491     return jReceiveBuffer;
 492 }