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