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
69void 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
77void 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
97jboolean 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
106JNIEXPORT jint JNICALL DEF_JNI_OnLoad(JavaVM *vm, void *reserved) {
107    return JNI_VERSION_1_4;
108}
109
110JNIEXPORT 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 */
127jobjectArray 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
179JNIEXPORT 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
215JNIEXPORT 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
240JNIEXPORT 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
278JNIEXPORT 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
317JNIEXPORT 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
329JNIEXPORT 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    }
402cleanup:
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
411JNIEXPORT 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
423JNIEXPORT 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
435JNIEXPORT 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}
493