1 /*
2 * PC/SC Lite IFD front-end for libopenctapi
3 *
4 * Mapping of CT-API / CT-BCS interface to the IFD Handler 2.0.
5 * Getting/Setting IFD/Protocol/ICC parameters other than the ATR is not
6 * supported. IFDH_MAX_READERS simultaneous readers are supported.
7 *
8 * This file was taken and modified from the Unix driver for
9 * Towitoko smart card readers. Used and re-licensed as BSD with
10 * a permission from the author.
11 *
12 * Copyright (C) 1998-2001, Carlos Prados <cprados@yahoo.com>
13 * Copyright (C) 2003, Antti Tapaninen <aet@cc.hut.fi>
14 */
15
16 #ifdef HAVE_CONFIG_H
17 #include <config.h>
18 #endif
19 #include <stdio.h>
20 #include <stdlib.h>
21 #include <string.h>
22 #ifdef HAVE_PTHREAD
23 #include <pthread.h>
24 #endif
25 #ifdef DEBUG_IFDH
26 #include <syslog.h>
27 #endif
28 #include <limits.h>
29 #ifdef __APPLE__
30 #include <PCSC/wintypes.h>
31 #include <PCSC/pcsclite.h>
32 #else
33 #include <wintypes.h>
34 #include <pcsclite.h>
35 #endif
36 #include <openct/openct.h>
37 #include "ctapi.h" /* XXX: <openct/ctapi.h>? */
38 #define IFDHANDLERv2
39 #include "ifdhandler.h"
40
41 /* Maximum number of readers handled */
42 #define IFDH_MAX_READERS OPENCT_MAX_READERS
43
44 /* Maximum number of slots per reader handled */
45 #define IFDH_MAX_SLOTS 1 /* XXX: OPENCT_MAX_SLOTS? */
46
47 typedef struct {
48 DEVICE_CAPABILITIES device_capabilities;
49 ICC_STATE icc_state;
50 DWORD ATR_Length;
51 PROTOCOL_OPTIONS protocol_options;
52 } IFDH_Context;
53
54 /* Matrix that stores conext information of all slots and readers */
55 static IFDH_Context *ifdh_context[IFDH_MAX_READERS][IFDH_MAX_SLOTS] = {
56 {NULL},
57 {NULL},
58 {NULL},
59 {NULL},
60 };
61
62 /* Mutexes for all readers */
63 #ifdef HAVE_PTHREAD
64 static pthread_mutex_t ifdh_context_mutex[IFDH_MAX_READERS] = {
65 PTHREAD_MUTEX_INITIALIZER,
66 PTHREAD_MUTEX_INITIALIZER,
67 PTHREAD_MUTEX_INITIALIZER,
68 PTHREAD_MUTEX_INITIALIZER
69 };
70 #endif
71
72 /* PC/SC Lite hotplugging base channel */
73 #define HOTPLUG_BASE_PORT 0x200000
74
IFDHCreateChannel(DWORD Lun,DWORD Channel)75 RESPONSECODE IFDHCreateChannel(DWORD Lun, DWORD Channel)
76 {
77 char ret;
78 unsigned short ctn, pn, slot;
79 RESPONSECODE rv;
80
81 ctn = ((unsigned short)(Lun >> 16)) % IFDH_MAX_READERS;
82 slot = ((unsigned short)(Lun & 0x0000FFFF)) % IFDH_MAX_SLOTS;
83
84 #ifdef HAVE_PTHREAD
85 pthread_mutex_lock(&ifdh_context_mutex[ctn]);
86 #endif
87 if (ifdh_context[ctn][slot] == NULL) {
88 if (Channel >= HOTPLUG_BASE_PORT) {
89 Channel -= HOTPLUG_BASE_PORT;
90 }
91 /* We don't care that much about IFDH CHANNELID handling */
92 if (Channel > IFDH_MAX_READERS) {
93 pn = 0;
94 } else {
95 pn = ((Channel == 0) ? 0 : Channel - 1);
96 }
97 ret = CT_init(ctn, pn);
98
99 if (ret == OK) {
100 /* Initialize context of the all slots in this reader */
101 for (slot = 0; slot < IFDH_MAX_SLOTS; slot++) {
102 ifdh_context[ctn][slot] = (IFDH_Context *)
103 malloc(sizeof(IFDH_Context));
104
105 if (ifdh_context[ctn][slot] != NULL)
106 memset(ifdh_context[ctn][slot], 0,
107 sizeof(IFDH_Context));
108 }
109 rv = IFD_SUCCESS;
110 } else {
111 rv = IFD_COMMUNICATION_ERROR;
112 }
113 } else {
114 /* Assume that IFDHCreateChannel is being called for another
115 already initialized slot in this same reader, and return Success */
116 rv = IFD_SUCCESS;
117 }
118 #ifdef HAVE_PTHREAD
119 pthread_mutex_unlock(&ifdh_context_mutex[ctn]);
120 #endif
121 #ifdef DEBUG_IFDH
122 syslog(LOG_INFO, "IFDH: IFDHCreateChannel(Lun=0x%X, Channel=0x%X)=%d",
123 Lun, Channel, rv);
124 #endif
125 return rv;
126 }
127
IFDHCloseChannel(DWORD Lun)128 RESPONSECODE IFDHCloseChannel(DWORD Lun)
129 {
130 char ret;
131 unsigned short ctn, slot;
132 RESPONSECODE rv;
133
134 ctn = ((unsigned short)(Lun >> 16)) % IFDH_MAX_READERS;
135 slot = ((unsigned short)(Lun & 0x0000FFFF)) % IFDH_MAX_SLOTS;
136
137 ret = CT_close(ctn);
138
139 if (ret == OK) {
140 #ifdef HAVE_PTHREAD
141 pthread_mutex_lock(&ifdh_context_mutex[ctn]);
142 #endif
143 /* Free context of the all slots in this reader */
144 for (slot = 0; slot < IFDH_MAX_SLOTS; slot++) {
145 if (ifdh_context[ctn][slot] != NULL) {
146 free(ifdh_context[ctn][slot]);
147 ifdh_context[ctn][slot] = NULL;
148 }
149 }
150 #ifdef HAVE_PTHREAD
151 pthread_mutex_unlock(&ifdh_context_mutex[ctn]);
152 #endif
153 rv = IFD_SUCCESS;
154 } else {
155 rv = IFD_COMMUNICATION_ERROR;
156 }
157 #ifdef DEBUG_IFDH
158 syslog(LOG_INFO, "IFDH: IFDHCloseChannel(Lun=0x%X)=%d", Lun, rv);
159 #endif
160 return rv;
161 }
162
163 RESPONSECODE
IFDHGetCapabilities(DWORD Lun,DWORD Tag,PDWORD Length,PUCHAR Value)164 IFDHGetCapabilities(DWORD Lun, DWORD Tag, PDWORD Length, PUCHAR Value)
165 {
166 unsigned short ctn, slot;
167 RESPONSECODE rv;
168
169 ctn = ((unsigned short)(Lun >> 16)) % IFDH_MAX_READERS;
170 slot = ((unsigned short)(Lun & 0x0000FFFF)) % IFDH_MAX_SLOTS;
171
172 #ifdef HAVE_PTHREAD
173 pthread_mutex_lock(&ifdh_context_mutex[ctn]);
174 #endif
175 switch (Tag) {
176 case TAG_IFD_ATR:
177 (*Length) = ifdh_context[ctn][slot]->ATR_Length;
178 memcpy(Value, ifdh_context[ctn][slot]->icc_state.ATR,
179 (*Length));
180 rv = IFD_SUCCESS;
181 break;
182
183 case TAG_IFD_SLOTS_NUMBER:
184 (*Length) = 1;
185 (*Value) = IFDH_MAX_SLOTS;
186 rv = IFD_SUCCESS;
187 break;
188
189 case TAG_IFD_SIMULTANEOUS_ACCESS:
190 (*Length) = 1;
191 (*Value) = IFDH_MAX_READERS;
192 rv = IFD_SUCCESS;
193 break;
194
195 default:
196 (*Length) = 0;
197 rv = IFD_ERROR_TAG;
198 }
199 #ifdef HAVE_PTHREAD
200 pthread_mutex_unlock(&ifdh_context_mutex[ctn]);
201 #endif
202 #ifdef DEBUG_IFDH
203 syslog(LOG_INFO, "IFDH: IFDHGetCapabilities (Lun=0x%X, Tag=0x%X)=%d",
204 Lun, Tag, rv);
205 #endif
206 return rv;
207 }
208
209 RESPONSECODE
IFDHSetCapabilities(DWORD Lun,DWORD Tag,DWORD Length,PUCHAR Value)210 IFDHSetCapabilities(DWORD Lun, DWORD Tag, DWORD Length, PUCHAR Value)
211 {
212 #ifdef DEBUG_IFDH
213 #if 0
214 syslog(LOG_INFO, "IFDH: IFDHSetCapabilities (Lun=%X, Tag=%X)=%d", Lun,
215 Tag, IFD_NOT_SUPPORTED);
216 #endif
217 #endif
218 return IFD_NOT_SUPPORTED;
219 }
220
221 RESPONSECODE
IFDHSetProtocolParameters(DWORD Lun,DWORD Protocol,UCHAR Flags,UCHAR PTS1,UCHAR PTS2,UCHAR PTS3)222 IFDHSetProtocolParameters(DWORD Lun, DWORD Protocol,
223 UCHAR Flags, UCHAR PTS1, UCHAR PTS2, UCHAR PTS3)
224 {
225 char ret;
226 unsigned short ctn, slot, lc, lr;
227 UCHAR cmd[10], rsp[256], sad, dad;
228 RESPONSECODE rv;
229
230 ctn = ((unsigned short)(Lun >> 16)) % IFDH_MAX_READERS;
231 slot = ((unsigned short)(Lun & 0x0000FFFF)) % IFDH_MAX_SLOTS;
232
233 #ifdef HAVE_PTHREAD
234 pthread_mutex_lock(&ifdh_context_mutex[ctn]);
235 #endif
236 if (ifdh_context[ctn][slot] != NULL) {
237 cmd[0] = CTBCS_CLA_2;
238 cmd[1] = CTBCS_INS_SET_INTERFACE_PARAM;
239 cmd[2] = (UCHAR) (slot + 1);
240 cmd[3] = 0x00;
241 cmd[4] = 0x03;
242 cmd[5] = CTBCS_TAG_TPP;
243 cmd[6] = 0x01;
244 cmd[7] = Protocol & 0xFF;
245
246 lc = 8;
247
248 dad = 0x01;
249 sad = 0x02;
250 lr = 256;
251
252 ret = CT_data(ctn, &dad, &sad, lc, cmd, &lr, rsp);
253
254 if (ret == OK) {
255 rv = IFD_SUCCESS;
256 } else {
257 rv = IFD_ERROR_PTS_FAILURE;
258 }
259 } else {
260 rv = IFD_ICC_NOT_PRESENT;
261 }
262 #ifdef HAVE_PTHREAD
263 pthread_mutex_unlock(&ifdh_context_mutex[ctn]);
264 #endif
265 #ifdef DEBUG_IFDH
266 syslog(LOG_INFO,
267 "IFDH: IFDHSetProtocolParameters (Lun=0x%X, Protocol=%d, Flags=0x%02X, PTS1=0x%02X, PTS2=0x%02X, PTS3=0x%02X)=%d",
268 Lun, Protocol, Flags, PTS1, PTS2, PTS3, rv);
269 #endif
270 return rv;
271 }
272
IFDHPowerICC(DWORD Lun,DWORD Action,PUCHAR Atr,PDWORD AtrLength)273 RESPONSECODE IFDHPowerICC(DWORD Lun, DWORD Action, PUCHAR Atr, PDWORD AtrLength)
274 {
275 char ret;
276 unsigned short ctn, slot, lc, lr;
277 UCHAR cmd[5], rsp[256], sad, dad;
278 RESPONSECODE rv;
279
280 ctn = ((unsigned short)(Lun >> 16)) % IFDH_MAX_READERS;
281 slot = ((unsigned short)(Lun & 0x0000FFFF)) % IFDH_MAX_SLOTS;
282
283 #ifdef HAVE_PTHREAD
284 pthread_mutex_lock(&ifdh_context_mutex[ctn]);
285 #endif
286 if (ifdh_context[ctn][slot] != NULL) {
287 if (Action == IFD_POWER_UP) {
288 cmd[0] = CTBCS_CLA;
289 cmd[1] = CTBCS_INS_REQUEST_ICC;
290 cmd[2] = (UCHAR) (slot + 1);
291 cmd[3] = CTBCS_P2_REQUEST_GET_ATR;
292 cmd[4] = 0x00;
293
294 dad = 0x01;
295 sad = 0x02;
296 lr = 256;
297 lc = 5;
298
299 ret = CT_data(ctn, &dad, &sad, 5, cmd, &lr, rsp);
300
301 if ((ret == OK) && (lr >= 2)) {
302 ifdh_context[ctn][slot]->ATR_Length =
303 (DWORD) lr - 2;
304 memcpy(ifdh_context[ctn][slot]->icc_state.ATR,
305 rsp, lr - 2);
306
307 (*AtrLength) = (DWORD) lr - 2;
308 memcpy(Atr, rsp, lr - 2);
309
310 rv = IFD_SUCCESS;
311 } else {
312 rv = IFD_COMMUNICATION_ERROR;
313 }
314 } else if (Action == IFD_POWER_DOWN) {
315 cmd[0] = CTBCS_CLA;
316 cmd[1] = CTBCS_INS_EJECT_ICC;
317 cmd[2] = (UCHAR) (slot + 1);
318 cmd[3] = 0x00;
319 cmd[4] = 0x00;
320
321 dad = 0x01;
322 sad = 0x02;
323 lr = 256;
324 lc = 5;
325
326 ret = CT_data(ctn, &dad, &sad, 5, cmd, &lr, rsp);
327
328 if (ret == OK) {
329 ifdh_context[ctn][slot]->ATR_Length = 0;
330 memset(ifdh_context[ctn][slot]->icc_state.ATR,
331 0, MAX_ATR_SIZE);
332
333 (*AtrLength) = 0;
334 rv = IFD_SUCCESS;
335 } else {
336 rv = IFD_COMMUNICATION_ERROR;
337 }
338 } else if (Action == IFD_RESET) {
339 cmd[0] = CTBCS_CLA;
340 cmd[1] = CTBCS_INS_RESET;
341 cmd[2] = (UCHAR) (slot + 1);
342 cmd[3] = CTBCS_P2_RESET_GET_ATR;
343 cmd[4] = 0x00;
344
345 dad = 0x01;
346 sad = 0x02;
347 lr = 256;
348 lc = 5;
349
350 ret = CT_data(ctn, &dad, &sad, 5, cmd, &lr, rsp);
351
352 if ((ret == OK) && (lr >= 2)) {
353 ifdh_context[ctn][slot]->ATR_Length =
354 (DWORD) lr - 2;
355 memcpy(ifdh_context[ctn][slot]->icc_state.ATR,
356 rsp, lr - 2);
357
358 (*AtrLength) = (DWORD) lr - 2;
359 memcpy(Atr, rsp, lr - 2);
360
361 rv = IFD_SUCCESS;
362 } else {
363 rv = IFD_ERROR_POWER_ACTION;
364 }
365 } else {
366 rv = IFD_NOT_SUPPORTED;
367 }
368 } else {
369 rv = IFD_ICC_NOT_PRESENT;
370 }
371 #ifdef HAVE_PTHREAD
372 pthread_mutex_unlock(&ifdh_context_mutex[ctn]);
373 #endif
374 #ifdef DEBUG_IFDH
375 syslog(LOG_INFO, "IFDH: IFDHPowerICC (Lun=0x%X, Action=0x%X)=%d", Lun,
376 Action, rv);
377 #endif
378 return rv;
379 }
380
381 RESPONSECODE
IFDHTransmitToICC(DWORD Lun,SCARD_IO_HEADER SendPci,PUCHAR TxBuffer,DWORD TxLength,PUCHAR RxBuffer,PDWORD RxLength,PSCARD_IO_HEADER RecvPci)382 IFDHTransmitToICC(DWORD Lun, SCARD_IO_HEADER SendPci,
383 PUCHAR TxBuffer, DWORD TxLength,
384 PUCHAR RxBuffer, PDWORD RxLength, PSCARD_IO_HEADER RecvPci)
385 {
386 char ret;
387 unsigned short ctn, slot, lc, lr;
388 UCHAR sad, dad;
389 RESPONSECODE rv;
390
391 ctn = ((unsigned short)(Lun >> 16)) % IFDH_MAX_READERS;
392 slot = ((unsigned short)(Lun & 0x0000FFFF)) % IFDH_MAX_SLOTS;
393
394 if (TxLength > USHRT_MAX) {
395 (*RxLength) = 0;
396 return IFD_PROTOCOL_NOT_SUPPORTED;
397 }
398 #ifdef HAVE_PTHREAD
399 pthread_mutex_lock(&ifdh_context_mutex[ctn]);
400 #endif
401 if (ifdh_context[ctn][slot] != NULL) {
402 #ifdef HAVE_PTHREAD
403 pthread_mutex_unlock(&ifdh_context_mutex[ctn]);
404 #endif
405 dad = (UCHAR) ((slot == 0) ? 0x00 : slot + 1);
406 sad = 0x02;
407 lr = (*RxLength > USHRT_MAX) ? USHRT_MAX : (unsigned short)(*RxLength);
408 lc = (unsigned short)TxLength;
409
410 ret = CT_data(ctn, &dad, &sad, lc, TxBuffer, &lr, RxBuffer);
411
412 if (ret == OK) {
413 (*RxLength) = lr;
414 rv = IFD_SUCCESS;
415 } else {
416 (*RxLength) = 0;
417 rv = IFD_COMMUNICATION_ERROR;
418 }
419 } else {
420 #ifdef HAVE_PTHREAD
421 pthread_mutex_unlock(&ifdh_context_mutex[ctn]);
422 #endif
423 rv = IFD_ICC_NOT_PRESENT;
424 }
425 #ifdef DEBUG_IFDH
426 syslog(LOG_INFO, "IFDH: IFDHTransmitToICC (Lun=0x%X, Tx=%u, Rx=%u)=%d",
427 Lun, TxLength, (*RxLength), rv);
428 #endif
429 return rv;
430 }
431
432 #ifdef IFDHANDLERv2
433
434 RESPONSECODE
IFDHControl(DWORD Lun,PUCHAR TxBuffer,DWORD TxLength,PUCHAR RxBuffer,PDWORD RxLength)435 IFDHControl(DWORD Lun, PUCHAR TxBuffer,
436 DWORD TxLength, PUCHAR RxBuffer, PDWORD RxLength)
437 {
438 char ret;
439 unsigned short ctn, slot, lc, lr;
440 UCHAR sad, dad;
441 RESPONSECODE rv;
442
443 ctn = ((unsigned short)(Lun >> 16)) % IFDH_MAX_READERS;
444 slot = ((unsigned short)(Lun & 0x0000FFFF)) % IFDH_MAX_SLOTS;
445
446 if (TxLength > USHRT_MAX) {
447 (*RxLength) = 0;
448 return IFD_PROTOCOL_NOT_SUPPORTED;
449 }
450 #ifdef HAVE_PTHREAD
451 pthread_mutex_lock(&ifdh_context_mutex[ctn]);
452 #endif
453 if (ifdh_context[ctn][slot] != NULL) {
454 #ifdef HAVE_PTHREAD
455 pthread_mutex_unlock(&ifdh_context_mutex[ctn]);
456 #endif
457 dad = 0x01;
458 sad = 0x02;
459 lr = (*RxLength > USHRT_MAX) ? USHRT_MAX : (unsigned short)(*RxLength);
460 lc = (unsigned short)TxLength;
461
462 ret = CT_data(ctn, &dad, &sad, lc, TxBuffer, &lr, RxBuffer);
463
464 if (ret == OK) {
465 (*RxLength) = lr;
466 rv = IFD_SUCCESS;
467 } else {
468 (*RxLength) = 0;
469 rv = IFD_COMMUNICATION_ERROR;
470 }
471 } else {
472 #ifdef HAVE_PTHREAD
473 pthread_mutex_unlock(&ifdh_context_mutex[ctn]);
474 #endif
475 rv = IFD_ICC_NOT_PRESENT;
476 }
477 #ifdef DEBUG_IFDH
478 syslog(LOG_INFO, "IFDH: IFDHControl (Lun=0x%X, Tx=%u, Rx=%u)=%d", Lun,
479 TxLength, (*RxLength), rv);
480 #endif
481 return rv;
482 }
483
484 #else
485
IFDHControl(DWORD Lun,DWORD dwControlCode,PUCHAR TxBuffer,DWORD TxLength,PUCHAR RxBuffer,DWORD RxLength,PDWORD pdwBytesReturned)486 RESPONSECODE IFDHControl(DWORD Lun, DWORD dwControlCode,
487 PUCHAR TxBuffer, DWORD TxLength, PUCHAR RxBuffer,
488 DWORD RxLength, PDWORD pdwBytesReturned)
489 {
490 /* FIXME */
491 }
492
493 #endif
494
IFDHICCPresence(DWORD Lun)495 RESPONSECODE IFDHICCPresence(DWORD Lun)
496 {
497 char ret;
498 unsigned short ctn, slot, lc, lr;
499 UCHAR cmd[5], rsp[256], sad, dad;
500 RESPONSECODE rv;
501
502 ctn = ((unsigned short)(Lun >> 16)) % IFDH_MAX_READERS;
503 slot = ((unsigned short)(Lun & 0x0000FFFF)) % IFDH_MAX_SLOTS;
504
505 cmd[0] = CTBCS_CLA;
506 cmd[1] = CTBCS_INS_STATUS;
507 cmd[2] = CTBCS_UNIT_CT;
508 cmd[3] = CTBCS_P2_STATUS_ICC;
509 cmd[4] = 0x00;
510
511 dad = 0x01;
512 sad = 0x02;
513 lc = 5;
514 lr = 256;
515
516 ret = CT_data(ctn, &dad, &sad, lc, cmd, &lr, rsp);
517
518 if (ret == OK) {
519 if (slot < lr - 2) {
520 if (rsp[slot] == CTBCS_DATA_STATUS_NOCARD) {
521 rv = IFD_ICC_NOT_PRESENT;
522 } else {
523 rv = IFD_ICC_PRESENT;
524 }
525 } else {
526 rv = IFD_ICC_NOT_PRESENT;
527 }
528 } else {
529 rv = IFD_COMMUNICATION_ERROR;
530 }
531 #ifdef DEBUG_IFDH
532 syslog(LOG_INFO, "IFDH: IFDHICCPresence (Lun=0x%X)=%d", Lun, rv);
533 #endif
534 return rv;
535 }
536