1 /*
   2  * Copyright (c) 2005, 2014, 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 #define MAX_STACK_BUFFER_SIZE 8192
  59 
  60 // make the buffers larger than what should be necessary, just in case
  61 #define ATR_BUFFER_SIZE 128
  62 #define READERNAME_BUFFER_SIZE 128
  63 #define RECEIVE_BUFFER_SIZE MAX_STACK_BUFFER_SIZE
  64 
  65 #define J2PCSC_EXCEPTION_NAME "sun/security/smartcardio/PCSCException"
  66 
  67 void throwOutOfMemoryError(JNIEnv *env, const char *msg) {
  68     jclass cls = (*env)->FindClass(env, "java/lang/OutOfMemoryError");
  69 
  70     if (cls != NULL) /* Otherwise an exception has already been thrown */
  71         (*env)->ThrowNew(env, cls, msg);
  72 
  73 }
  74 
  75 void throwPCSCException(JNIEnv* env, LONG code) {
  76     jclass pcscClass;
  77     jmethodID constructor;
  78     jthrowable pcscException;
  79 
  80     pcscClass = (*env)->FindClass(env, J2PCSC_EXCEPTION_NAME);
  81     if (pcscClass == NULL) {
  82         return;
  83     }
  84     constructor = (*env)->GetMethodID(env, pcscClass, "<init>", "(I)V");
  85     if (constructor == NULL) {
  86         return;
  87     }
  88     pcscException = (jthrowable) (*env)->NewObject(env, pcscClass,
  89         constructor, (jint)code);
  90     if (pcscException != NULL) {
  91         (*env)->Throw(env, pcscException);
  92     }
  93 }
  94 
  95 jboolean handleRV(JNIEnv* env, LONG code) {
  96     if (code == SCARD_S_SUCCESS) {
  97         return JNI_FALSE;
  98     } else {
  99         throwPCSCException(env, code);
 100         return JNI_TRUE;
 101     }
 102 }
 103 
 104 JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM *vm, void *reserved) {
 105     return JNI_VERSION_1_4;
 106 }
 107 
 108 JNIEXPORT jlong JNICALL Java_sun_security_smartcardio_PCSC_SCardEstablishContext
 109     (JNIEnv *env, jclass thisClass, jint dwScope)
 110 {
 111     SCARDCONTEXT context;
 112     LONG rv;
 113     dprintf("-establishContext\n");
 114     rv = CALL_SCardEstablishContext(dwScope, NULL, NULL, &context);
 115     if (handleRV(env, rv)) {
 116         return 0;
 117     }
 118     // note: SCARDCONTEXT is typedef'd as long, so this works
 119     return (jlong)context;
 120 }
 121 
 122 /**
 123  * Convert a multi string to a java string array,
 124  */
 125 jobjectArray pcsc_multi2jstring(JNIEnv *env, char *spec) {
 126     jobjectArray result;
 127     jclass stringClass;
 128     char *cp, **tab;
 129     jstring js;
 130     int cnt = 0;
 131 
 132     cp = spec;
 133     while (*cp != 0) {
 134         cp += (strlen(cp) + 1);
 135         ++cnt;
 136     }
 137 
 138     tab = (char **)malloc(cnt * sizeof(char *));
 139     if (tab == NULL) {
 140         throwOutOfMemoryError(env, NULL);
 141         return NULL;
 142     }
 143 
 144     cnt = 0;
 145     cp = spec;
 146     while (*cp != 0) {
 147         tab[cnt++] = cp;
 148         cp += (strlen(cp) + 1);
 149     }
 150 
 151     stringClass = (*env)->FindClass(env, "java/lang/String");
 152     if (stringClass == NULL) {
 153         free(tab);
 154         return NULL;
 155     }
 156 
 157     result = (*env)->NewObjectArray(env, cnt, stringClass, NULL);
 158     if (result != NULL) {
 159         while (cnt-- > 0) {
 160             js = (*env)->NewStringUTF(env, tab[cnt]);
 161             if ((*env)->ExceptionCheck(env)) {
 162                 free(tab);
 163                 return NULL;
 164             }
 165             (*env)->SetObjectArrayElement(env, result, cnt, js);
 166             if ((*env)->ExceptionCheck(env)) {
 167                 free(tab);
 168                 return NULL;
 169             }
 170             (*env)->DeleteLocalRef(env, js);
 171         }
 172     }
 173     free(tab);
 174     return result;
 175 }
 176 
 177 JNIEXPORT jobjectArray JNICALL Java_sun_security_smartcardio_PCSC_SCardListReaders
 178     (JNIEnv *env, jclass thisClass, jlong jContext)
 179 {
 180     SCARDCONTEXT context = (SCARDCONTEXT)jContext;
 181     LONG rv;
 182     LPTSTR mszReaders;
 183     DWORD size;
 184     jobjectArray result;
 185 
 186     dprintf1("-context: %x\n", context);
 187     rv = CALL_SCardListReaders(context, NULL, NULL, &size);
 188     if (handleRV(env, rv)) {
 189         return NULL;
 190     }
 191     dprintf1("-size: %d\n", size);
 192 
 193     mszReaders = malloc(size);
 194     if (mszReaders == NULL) {
 195         throwOutOfMemoryError(env, NULL);
 196         return NULL;
 197     }
 198 
 199     rv = CALL_SCardListReaders(context, NULL, mszReaders, &size);
 200     if (handleRV(env, rv)) {
 201         free(mszReaders);
 202         return NULL;
 203     }
 204     dprintf1("-String: %s\n", mszReaders);
 205 
 206     result = pcsc_multi2jstring(env, mszReaders);
 207     free(mszReaders);
 208     return result;
 209 }
 210 
 211 JNIEXPORT jlong JNICALL Java_sun_security_smartcardio_PCSC_SCardConnect
 212     (JNIEnv *env, jclass thisClass, jlong jContext, jstring jReaderName,
 213     jint jShareMode, jint jPreferredProtocols)
 214 {
 215     SCARDCONTEXT context = (SCARDCONTEXT)jContext;
 216     LONG rv;
 217     LPCTSTR readerName;
 218     SCARDHANDLE card;
 219     DWORD proto;
 220 
 221     readerName = (*env)->GetStringUTFChars(env, jReaderName, NULL);
 222     if (readerName == NULL) {
 223         return 0;
 224     }
 225     rv = CALL_SCardConnect(context, readerName, jShareMode, jPreferredProtocols, &card, &proto);
 226     (*env)->ReleaseStringUTFChars(env, jReaderName, readerName);
 227     dprintf1("-cardhandle: %x\n", card);
 228     dprintf1("-protocol: %d\n", proto);
 229     if (handleRV(env, rv)) {
 230         return 0;
 231     }
 232 
 233     return (jlong)card;
 234 }
 235 
 236 JNIEXPORT jbyteArray JNICALL Java_sun_security_smartcardio_PCSC_SCardTransmit
 237     (JNIEnv *env, jclass thisClass, jlong jCard, jint protocol,
 238     jbyteArray jBuf, jint jOfs, jint jLen)
 239 {
 240     SCARDHANDLE card = (SCARDHANDLE)jCard;
 241     LONG rv;
 242     SCARD_IO_REQUEST sendPci;
 243     unsigned char *sbuf;
 244     unsigned char rbuf[RECEIVE_BUFFER_SIZE];
 245     DWORD rlen = RECEIVE_BUFFER_SIZE;
 246     int ofs = (int)jOfs;
 247     int len = (int)jLen;
 248     jbyteArray jOut;
 249 
 250     sendPci.dwProtocol = protocol;
 251     sendPci.cbPciLength = sizeof(SCARD_IO_REQUEST);
 252 
 253     sbuf = (unsigned char *) ((*env)->GetByteArrayElements(env, jBuf, NULL));
 254     if (sbuf == NULL) {
 255         return NULL;
 256     }
 257     rv = CALL_SCardTransmit(card, &sendPci, sbuf + ofs, len, NULL, rbuf, &rlen);
 258     (*env)->ReleaseByteArrayElements(env, jBuf, (jbyte *)sbuf, JNI_ABORT);
 259 
 260     if (handleRV(env, rv)) {
 261         return NULL;
 262     }
 263 
 264     jOut = (*env)->NewByteArray(env, rlen);
 265     if (jOut != NULL) {
 266         (*env)->SetByteArrayRegion(env, jOut, 0, rlen, (jbyte *)rbuf);
 267         if ((*env)->ExceptionCheck(env)) {
 268             return NULL;
 269         }
 270     }
 271     return jOut;
 272 }
 273 
 274 JNIEXPORT jbyteArray JNICALL Java_sun_security_smartcardio_PCSC_SCardStatus
 275     (JNIEnv *env, jclass thisClass, jlong jCard, jbyteArray jStatus)
 276 {
 277     SCARDHANDLE card = (SCARDHANDLE)jCard;
 278     LONG rv;
 279     char readerName[READERNAME_BUFFER_SIZE];
 280     DWORD readerLen = READERNAME_BUFFER_SIZE;
 281     unsigned char atr[ATR_BUFFER_SIZE];
 282     DWORD atrLen = ATR_BUFFER_SIZE;
 283     DWORD state;
 284     DWORD protocol;
 285     jbyteArray jArray;
 286     jbyte status[2];
 287 
 288     rv = CALL_SCardStatus(card, readerName, &readerLen, &state, &protocol, atr, &atrLen);
 289     if (handleRV(env, rv)) {
 290         return NULL;
 291     }
 292     dprintf1("-reader: %s\n", readerName);
 293     dprintf1("-status: %d\n", state);
 294     dprintf1("-protocol: %d\n", protocol);
 295 
 296     jArray = (*env)->NewByteArray(env, atrLen);
 297     if (jArray == NULL) {
 298         return NULL;
 299     }
 300     (*env)->SetByteArrayRegion(env, jArray, 0, atrLen, (jbyte *)atr);
 301     if ((*env)->ExceptionCheck(env)) {
 302         return NULL;
 303     }
 304     status[0] = (jbyte) state;
 305     status[1] = (jbyte) protocol;
 306     (*env)->SetByteArrayRegion(env, jStatus, 0, 2, status);
 307     if ((*env)->ExceptionCheck(env)) {
 308         return NULL;
 309     }
 310     return jArray;
 311 }
 312 
 313 JNIEXPORT void JNICALL Java_sun_security_smartcardio_PCSC_SCardDisconnect
 314     (JNIEnv *env, jclass thisClass, jlong jCard, jint jDisposition)
 315 {
 316     SCARDHANDLE card = (SCARDHANDLE)jCard;
 317     LONG rv;
 318 
 319     rv = CALL_SCardDisconnect(card, jDisposition);
 320     dprintf1("-disconnect: 0x%X\n", rv);
 321     handleRV(env, rv);
 322     return;
 323 }
 324 
 325 JNIEXPORT jintArray JNICALL Java_sun_security_smartcardio_PCSC_SCardGetStatusChange
 326     (JNIEnv *env, jclass thisClass, jlong jContext, jlong jTimeout,
 327     jintArray jCurrentState, jobjectArray jReaderNames)
 328 {
 329     SCARDCONTEXT context = (SCARDCONTEXT)jContext;
 330     LONG rv;
 331     int readers = (*env)->GetArrayLength(env, jReaderNames);
 332     SCARD_READERSTATE *readerState;
 333     int i;
 334     jintArray jEventState = NULL;
 335     int *currentState = NULL;
 336     const char *readerName;
 337 
 338     readerState = calloc(readers, sizeof(SCARD_READERSTATE));
 339     if (readerState == NULL) {
 340         throwOutOfMemoryError(env, NULL);
 341         return NULL;
 342     }
 343 
 344     currentState = (*env)->GetIntArrayElements(env, jCurrentState, NULL);
 345     if (currentState == NULL) {
 346         free(readerState);
 347         return NULL;
 348     }
 349 
 350     for (i = 0; i < readers; i++) {
 351         jobject jReaderName = (*env)->GetObjectArrayElement(env, jReaderNames, i);
 352         if ((*env)->ExceptionCheck(env)) {
 353             goto cleanup;
 354         }
 355         readerName = (*env)->GetStringUTFChars(env, jReaderName, NULL);
 356         if (readerName == NULL) {
 357             goto cleanup;
 358         }
 359         readerState[i].szReader = strdup(readerName);
 360         (*env)->ReleaseStringUTFChars(env, jReaderName, readerName);
 361         if (readerState[i].szReader == NULL) {
 362             throwOutOfMemoryError(env, NULL);
 363             goto cleanup;
 364         }
 365         readerState[i].pvUserData = NULL;
 366         readerState[i].dwCurrentState = currentState[i];
 367         readerState[i].dwEventState = SCARD_STATE_UNAWARE;
 368         readerState[i].cbAtr = 0;
 369         (*env)->DeleteLocalRef(env, jReaderName);
 370     }
 371 
 372     rv = CALL_SCardGetStatusChange(context, (DWORD)jTimeout, readerState, readers);
 373     if (handleRV(env, rv)) {
 374         goto cleanup;
 375     }
 376 
 377     jEventState = (*env)->NewIntArray(env, readers);
 378     if (jEventState == NULL) {
 379         goto cleanup;
 380     }
 381     for (i = 0; i < readers; i++) {
 382         jint eventStateTmp;
 383         dprintf3("-reader status %s: 0x%X, 0x%X\n", readerState[i].szReader,
 384             readerState[i].dwCurrentState, readerState[i].dwEventState);
 385         eventStateTmp = (jint)readerState[i].dwEventState;
 386         (*env)->SetIntArrayRegion(env, jEventState, i, 1, &eventStateTmp);
 387         if ((*env)->ExceptionCheck(env)) {
 388             jEventState = NULL;
 389             goto cleanup;
 390         }
 391     }
 392 cleanup:
 393     (*env)->ReleaseIntArrayElements(env, jCurrentState, currentState, JNI_ABORT);
 394     for (i = 0; i < readers; i++) {
 395         free((char *)readerState[i].szReader);
 396     }
 397     free(readerState);
 398     return jEventState;
 399 }
 400 
 401 JNIEXPORT void JNICALL Java_sun_security_smartcardio_PCSC_SCardBeginTransaction
 402     (JNIEnv *env, jclass thisClass, jlong jCard)
 403 {
 404     SCARDHANDLE card = (SCARDHANDLE)jCard;
 405     LONG rv;
 406 
 407     rv = CALL_SCardBeginTransaction(card);
 408     dprintf1("-beginTransaction: 0x%X\n", rv);
 409     handleRV(env, rv);
 410     return;
 411 }
 412 
 413 JNIEXPORT void JNICALL Java_sun_security_smartcardio_PCSC_SCardEndTransaction
 414     (JNIEnv *env, jclass thisClass, jlong jCard, jint jDisposition)
 415 {
 416     SCARDHANDLE card = (SCARDHANDLE)jCard;
 417     LONG rv;
 418 
 419     rv = CALL_SCardEndTransaction(card, jDisposition);
 420     dprintf1("-endTransaction: 0x%X\n", rv);
 421     handleRV(env, rv);
 422     return;
 423 }
 424 
 425 JNIEXPORT jbyteArray JNICALL Java_sun_security_smartcardio_PCSC_SCardControl
 426     (JNIEnv *env, jclass thisClass, jlong jCard, jint jControlCode, jbyteArray jSendBuffer)
 427 {
 428     SCARDHANDLE card = (SCARDHANDLE)jCard;
 429     LONG rv;
 430     jbyte* sendBuffer;
 431     jint sendBufferLength = (*env)->GetArrayLength(env, jSendBuffer);
 432     jbyte receiveBuffer[MAX_STACK_BUFFER_SIZE];
 433     jint receiveBufferLength = MAX_STACK_BUFFER_SIZE;
 434     ULONG returnedLength = 0;
 435     jbyteArray jReceiveBuffer;
 436 
 437     sendBuffer = (*env)->GetByteArrayElements(env, jSendBuffer, NULL);
 438     if (sendBuffer == NULL) {
 439         return NULL;
 440     }
 441 
 442 #ifdef J2PCSC_DEBUG
 443 {
 444     int k;
 445     printf("-control: 0x%X\n", jControlCode);
 446     printf("-send: ");
 447     for (k = 0; k < sendBufferLength; k++) {
 448         printf("%02x ", sendBuffer[k]);
 449     }
 450     printf("\n");
 451 }
 452 #endif
 453 
 454     rv = CALL_SCardControl(card, jControlCode, sendBuffer, sendBufferLength,
 455         receiveBuffer, receiveBufferLength, &returnedLength);
 456 
 457     (*env)->ReleaseByteArrayElements(env, jSendBuffer, sendBuffer, JNI_ABORT);
 458     if (handleRV(env, rv)) {
 459         return NULL;
 460     }
 461 
 462 #ifdef J2PCSC_DEBUG
 463 {
 464     int k;
 465     printf("-recv:  ");
 466     for (k = 0; k < returnedLength; k++) {
 467         printf("%02x ", receiveBuffer[k]);
 468     }
 469     printf("\n");
 470 }
 471 #endif
 472 
 473     jReceiveBuffer = (*env)->NewByteArray(env, returnedLength);
 474     if (jReceiveBuffer == NULL) {
 475         return NULL;
 476     }
 477     (*env)->SetByteArrayRegion(env, jReceiveBuffer, 0, returnedLength, receiveBuffer);
 478     if ((*env)->ExceptionCheck(env)) {
 479         return NULL;
 480     }
 481     return jReceiveBuffer;
 482 }