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