1/*
2 * Copyright (c) 2005, 2019, 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 LPSTR 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
217JNIEXPORT 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 LPCSTR 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
242JNIEXPORT 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
280JNIEXPORT 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
319JNIEXPORT 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
331JNIEXPORT 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 }
404cleanup:
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
413JNIEXPORT 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
425JNIEXPORT 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
437JNIEXPORT 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}
495