pcsc-lite 2.5.0
winscard_svc.c
Go to the documentation of this file.
1/*
2 * MUSCLE SmartCard Development ( https://pcsclite.apdu.fr/ )
3 *
4 * Copyright (C) 2001-2004
5 * David Corcoran <corcoran@musclecard.com>
6 * Copyright (C) 2003-2004
7 * Damien Sauveron <damien.sauveron@labri.fr>
8 * Copyright (C) 2002-2025
9 * Ludovic Rousseau <ludovic.rousseau@free.fr>
10 * Copyright (C) 2009
11 * Jean-Luc Giraud <jlgiraud@googlemail.com>
12 *
13Redistribution and use in source and binary forms, with or without
14modification, are permitted provided that the following conditions
15are met:
16
171. Redistributions of source code must retain the above copyright
18 notice, this list of conditions and the following disclaimer.
192. Redistributions in binary form must reproduce the above copyright
20 notice, this list of conditions and the following disclaimer in the
21 documentation and/or other materials provided with the distribution.
223. The name of the author may not be used to endorse or promote products
23 derived from this software without specific prior written permission.
24
25THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
26IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
27OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
28IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
29INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
30NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
31DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
32THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
33(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
34THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
35 */
36
46
47#include "config.h"
48#include <time.h>
49#include <stdio.h>
50#include <string.h>
51#include <stddef.h>
52#include <stdlib.h>
53#include <unistd.h>
54#include <pthread.h>
55#include <stdbool.h>
56
57#include "pcscd.h"
58#include "winscard.h"
59#include "debuglog.h"
60#include "winscard_msg.h"
61#include "winscard_svc.h"
62#include "sys_generic.h"
63#include "utils.h"
64#include "readerfactory.h"
65#include "eventhandler.h"
66#include "simclist.h"
67#include "auth.h"
68
74
75extern bool AutoExit;
76static int contextMaxThreadCounter = PCSC_MAX_CONTEXT_THREADS;
77static int contextMaxCardHandles = PCSC_MAX_CONTEXT_CARD_HANDLES;
78
80pthread_mutex_t contextsList_lock;
81
83{
84 int32_t hContext;
85 list_t cardsList;
86 pthread_mutex_t cardsList_lock;
87 uint32_t dwClientID;
88 pthread_t pthThread;
89};
90typedef struct _psContext SCONTEXT;
91
92static LONG MSGCheckHandleAssociation(SCARDHANDLE, SCONTEXT *);
93static LONG MSGAddContext(SCARDCONTEXT, SCONTEXT *);
94static LONG MSGRemoveContext(SCARDCONTEXT, SCONTEXT *);
95static LONG MSGAddHandle(SCARDCONTEXT, SCARDHANDLE, SCONTEXT *);
96static LONG MSGRemoveHandle(SCARDHANDLE, SCONTEXT *);
97static void MSGCleanupClient(SCONTEXT *);
98
99static void * ContextThread(LPVOID pdwIndex);
100
101extern int pcsclite_max_reader_context;
102extern READER_CONTEXT ** sReadersContexts;
103extern int16_t ReaderEvents;
104
105static int contextsListhContext_seeker(const void *el, const void *key)
106{
107 const SCONTEXT * currentContext = (SCONTEXT *)el;
108
109 if ((el == NULL) || (key == NULL))
110 {
111 Log3(PCSC_LOG_CRITICAL, "called with NULL pointer: el=%p, key=%p",
112 el, key);
113 return 0;
114 }
115
116 if (currentContext->hContext == *(int32_t *)key)
117 return 1;
118 return 0;
119}
120
121LONG ContextsInitialize(int customMaxThreadCounter,
122 int customMaxThreadCardHandles)
123{
124 int lrv = 0;
125
126 if (customMaxThreadCounter != 0)
127 contextMaxThreadCounter = customMaxThreadCounter;
128
129 if (customMaxThreadCardHandles != 0)
130 contextMaxCardHandles = customMaxThreadCardHandles;
131
132 lrv = list_init(&contextsList);
133 if (lrv < 0)
134 {
135 Log2(PCSC_LOG_CRITICAL, "list_init failed with return value: %d", lrv);
136 return -1;
137 }
138 lrv = list_attributes_seeker(& contextsList, contextsListhContext_seeker);
139 if (lrv < 0)
140 {
141 Log2(PCSC_LOG_CRITICAL,
142 "list_attributes_seeker failed with return value: %d", lrv);
143 return -1;
144 }
145
146 (void)pthread_mutex_init(&contextsList_lock, NULL);
147
148 return 1;
149}
150
151void ContextsDeinitialize(void)
152{
153 int listSize;
154 (void)pthread_mutex_lock(&contextsList_lock);
155 listSize = list_size(&contextsList);
156#ifdef NO_LOG
157 (void)listSize;
158#endif
159 Log2(PCSC_LOG_DEBUG, "remaining threads: %d", listSize);
160
161 /* terminate all the client threads */
162 int rv = list_iterator_start(&contextsList);
163 if (0 == rv)
164 Log1(PCSC_LOG_ERROR, "list_iterator_start failed");
165 else
166 {
167 while (list_iterator_hasnext(&contextsList))
168 {
169 SCONTEXT * elt = list_iterator_next(&contextsList);
170 Log3(PCSC_LOG_DEBUG, "Cancel dwClientID=%d hContext: %p",
171 elt->dwClientID, elt);
173 close(elt->dwClientID);
174 Log2(PCSC_LOG_DEBUG, "Waiting client: %d", elt->dwClientID);
175 pthread_join(elt->pthThread, NULL);
176 Log2(PCSC_LOG_INFO, "Client %d terminated", elt->dwClientID);
177 }
178 }
179 list_destroy(&contextsList);
180 (void)pthread_mutex_unlock(&contextsList_lock);
181}
182
193LONG CreateContextThread(uint32_t *pdwClientID)
194{
195 int rv;
196 int lrv;
197 int listSize;
198 SCONTEXT * newContext = NULL;
199 LONG retval = SCARD_E_NO_MEMORY;
200
201 (void)pthread_mutex_lock(&contextsList_lock);
202
203 listSize = list_size(&contextsList);
204 if (listSize >= contextMaxThreadCounter)
205 {
206 Log2(PCSC_LOG_CRITICAL, "Too many context running: %d", listSize);
207 goto out;
208 }
209
210 /* Create the context for this thread. */
211 newContext = malloc(sizeof(*newContext));
212 if (NULL == newContext)
213 {
214 Log1(PCSC_LOG_CRITICAL, "Could not allocate new context");
215 goto out;
216 }
217 memset(newContext, 0, sizeof(*newContext));
218
219 newContext->dwClientID = *pdwClientID;
220
221 /* Initialise the list of card contexts */
222 lrv = list_init(&newContext->cardsList);
223 if (lrv < 0)
224 {
225 Log2(PCSC_LOG_CRITICAL, "list_init failed with return value: %d", lrv);
226 goto out;
227 }
228
229 /* request to store copies, and provide the metric function */
230 list_attributes_copy(&newContext->cardsList, list_meter_int32_t, 1);
231
232 /* Adding a comparator
233 * The stored type is SCARDHANDLE (long) but has only 32 bits
234 * useful even on a 64-bit CPU since the API between pcscd and
235 * libpcscliter uses "int32_t hCard;"
236 */
237 lrv = list_attributes_comparator(&newContext->cardsList,
238 list_comparator_int32_t);
239 if (lrv != 0)
240 {
241 Log2(PCSC_LOG_CRITICAL,
242 "list_attributes_comparator failed with return value: %d", lrv);
243 list_destroy(&newContext->cardsList);
244 goto out;
245 }
246
247 (void)pthread_mutex_init(&newContext->cardsList_lock, NULL);
248
249 lrv = list_append(&contextsList, newContext);
250 if (lrv < 0)
251 {
252 Log2(PCSC_LOG_CRITICAL, "list_append failed with return value: %d",
253 lrv);
254 list_destroy(&newContext->cardsList);
255 goto out;
256 }
257
258 rv = ThreadCreate(&newContext->pthThread, THREAD_ATTR_DETACHED,
259 (PCSCLITE_THREAD_FUNCTION( )) ContextThread, (LPVOID) newContext);
260 if (rv)
261 {
262 int lrv2;
263
264 Log2(PCSC_LOG_CRITICAL, "ThreadCreate failed: %s", strerror(rv));
265 lrv2 = list_delete(&contextsList, newContext);
266 if (lrv2 < 0)
267 Log2(PCSC_LOG_CRITICAL, "list_delete failed with error %d", lrv2);
268 list_destroy(&newContext->cardsList);
269 goto out;
270 }
271
272 /* disable any exit alarm */
273 if (AutoExit)
274 alarm(0);
275
276 retval = SCARD_S_SUCCESS;
277
278out:
279 (void)pthread_mutex_unlock(&contextsList_lock);
280
281 if (retval != SCARD_S_SUCCESS)
282 {
283 if (newContext)
284 free(newContext);
285 (void)close(*pdwClientID);
286 }
287
288 return retval;
289}
290
291/*
292 * A list of local functions used to keep track of clients and their
293 * connections
294 */
295
304#ifndef NO_LOG
305static const char *CommandsText[] = {
306 "NULL",
307 "ESTABLISH_CONTEXT", /* 0x01 */
308 "RELEASE_CONTEXT",
309 "LIST_READERS",
310 "CONNECT",
311 "RECONNECT", /* 0x05 */
312 "DISCONNECT",
313 "BEGIN_TRANSACTION",
314 "END_TRANSACTION",
315 "TRANSMIT",
316 "CONTROL", /* 0x0A */
317 "STATUS",
318 "GET_STATUS_CHANGE",
319 "CANCEL",
320 "CANCEL_TRANSACTION",
321 "GET_ATTRIB", /* 0x0F */
322 "SET_ATTRIB",
323 "CMD_VERSION",
324 "CMD_GET_READERS_STATE",
325 "CMD_WAIT_READER_STATE_CHANGE",
326 "CMD_STOP_WAITING_READER_STATE_CHANGE", /* 0x14 */
327 "CMD_GET_READER_EVENTS",
328 "CMD_GET_READERS_STATE_SIZE",
329 "CMD_GET_READERS_STATE_ARRAY",
330 "NULL"
331};
332#endif
333
334#define READ_BODY(v) \
335 do { \
336 if (header.size != sizeof(v)) \
337 goto wrong_length; \
338 ret = MessageReceive(&v, sizeof(v), filedes); \
339 if (ret != SCARD_S_SUCCESS) { \
340 Log2(PCSC_LOG_DEBUG, "Client die: %d", filedes); \
341 goto exit; \
342 } \
343 } while (0)
344
345#define WRITE_BODY(v) \
346 WRITE_BODY_WITH_COMMAND(CommandsText[header.command], v)
347#define WRITE_BODY_WITH_COMMAND(command, v) \
348 do { \
349 LogRv4(PCSC_LOG_DEBUG, v.rv, "%s for client %d", command, filedes); \
350 ret = MessageSend(&v, sizeof(v), filedes); \
351 } while (0)
352
353static void * ContextThread(LPVOID newContext)
354{
355 SCONTEXT * threadContext = (SCONTEXT *) newContext;
356 int32_t filedes = threadContext->dwClientID;
357 bool old_reader_state_api = false;
358
359 if (IsClientAuthorized(filedes, "access_pcsc", NULL) == 0)
360 {
361 Log1(PCSC_LOG_CRITICAL, "Rejected unauthorized PC/SC client");
362 goto exit;
363 }
364 else
365 {
366 Log1(PCSC_LOG_DEBUG, "Authorized PC/SC client");
367 }
368
369 Log3(PCSC_LOG_DEBUG, "Thread is started: dwClientID=%d, threadContext @%p",
370 threadContext->dwClientID, threadContext);
371
372 while (1)
373 {
374 struct rxHeader header;
375 int32_t ret = MessageReceive(&header, sizeof(header), filedes);
376
377 if (ret != SCARD_S_SUCCESS)
378 {
379 /* Clean up the dead client */
380 Log2(PCSC_LOG_DEBUG, "Client die: %d", filedes);
382 goto exit;
383 }
384
385 if ((header.command > CMD_ENUM_FIRST)
386 && (header.command < CMD_ENUM_LAST))
387 Log3(PCSC_LOG_DEBUG, "Received command: %s from client %d",
388 CommandsText[header.command], filedes);
389
390 switch (header.command)
391 {
392 /* pcsc-lite client/server protocol version */
393 case CMD_VERSION:
394 {
395 struct version_struct veStr;
396
397 READ_BODY(veStr);
398
399 Log3(PCSC_LOG_DEBUG, "Client is protocol version %d:%d",
400 veStr.major, veStr.minor);
401
402 veStr.rv = SCARD_S_SUCCESS;
403
404 /* client and server use different protocol */
405 if ((veStr.major != PROTOCOL_VERSION_MAJOR)
406 || (veStr.minor != PROTOCOL_VERSION_MINOR))
407 {
408 Log1(PCSC_LOG_CRITICAL,
409 "Communication protocol mismatch!");
410 Log3(PCSC_LOG_ERROR, "Client protocol is %d:%d",
411 veStr.major, veStr.minor);
412 Log3(PCSC_LOG_ERROR, "Server protocol is %d:%d",
414
415 /* the protocol major corresponds and the minior
416 * indicates the protocol is just old enough */
417 if (PROTOCOL_VERSION_MAJOR == veStr.major
419 && veStr.minor < PROTOCOL_VERSION_MINOR)
420 {
421 Log1(PCSC_LOG_INFO, "Enable backward compatibility");
422 old_reader_state_api = true;
423 }
424 else
425 /* any other case is unsupported */
426 veStr.rv = SCARD_E_SERVICE_STOPPED;
427 }
428
429 /* set the server protocol version */
430 veStr.major = PROTOCOL_VERSION_MAJOR;
431 veStr.minor = PROTOCOL_VERSION_MINOR;
432
433 /* send back the response */
434 WRITE_BODY(veStr);
435 }
436 break;
437
439 {
440 /* nothing to read */
441
442#ifdef USE_USB
443 /* wait until all readers are ready */
444 RFWaitForReaderInit();
445#endif
446
447 MSGSendReaderStates(filedes, true);
448 }
449 break;
450
452 {
453 /* nothing to read */
454
455#ifdef USE_USB
456 /* wait until all readers are ready */
457 RFWaitForReaderInit();
458#endif
459
460 /* add the client fd to the list and dump the readers state */
461 EHRegisterClientForEvent(filedes);
462 if (old_reader_state_api)
463 MSGSendReaderStates(filedes, true);
464 }
465 break;
466
468 {
469 struct wait_reader_state_change waStr =
470 {
471 .timeOut = 0,
472 .rv = SCARD_S_SUCCESS
473 };
474 LONG rv;
475
476 /* remove the client fd from the list */
477 rv = EHUnregisterClientForEvent(filedes);
478
479 /* send the response only if the client was still in the
480 * list */
481 if (rv != SCARD_F_INTERNAL_ERROR)
482 {
483 waStr.rv = rv;
484 WRITE_BODY(waStr);
485 }
486 }
487 break;
488
490 {
491 /* nothing to read */
492
493 struct get_reader_events readerEvents =
494 {
495 .readerEvents = ReaderEvents,
496 .rv = SCARD_S_SUCCESS
497 };
498
499 WRITE_BODY(readerEvents);
500 }
501 break;
502
503 case CMD_GET_READERS_STATE_SIZE:
504 {
505 /* nothing to read */
506 int32_t array_size = pcsclite_max_reader_context;
507
508 MessageSend(&array_size, sizeof(array_size), filedes);
509 }
510 break;
511
513 {
514 /* nothing to read */
515
516#ifdef USE_USB
517 /* wait until all readers are ready */
518 RFWaitForReaderInit();
519#endif
520
521 MSGSendReaderStates(filedes, false);
522 }
523 break;
524
526 {
527 struct establish_struct esStr;
528 SCARDCONTEXT hContext;
529
530 READ_BODY(esStr);
531
532 hContext = esStr.hContext;
533 esStr.rv = SCardEstablishContext(esStr.dwScope, 0, 0,
534 &hContext);
535 esStr.hContext = hContext;
536
537 if (esStr.rv == SCARD_S_SUCCESS)
538 esStr.rv = MSGAddContext(esStr.hContext, threadContext);
539
540 WRITE_BODY(esStr);
541 }
542 break;
543
545 {
546 struct release_struct reStr;
547
548 READ_BODY(reStr);
549
550 reStr.rv = SCardReleaseContext(reStr.hContext);
551
552 if (reStr.rv == SCARD_S_SUCCESS)
553 reStr.rv = MSGRemoveContext(reStr.hContext, threadContext);
554
555 WRITE_BODY(reStr);
556 }
557 break;
558
559 case SCARD_CONNECT:
560 {
561 struct connect_struct coStr;
562 SCARDHANDLE hCard;
563 DWORD dwActiveProtocol;
564
565 READ_BODY(coStr);
566
567 coStr.szReader[sizeof(coStr.szReader)-1] = 0;
568 hCard = coStr.hCard;
569 dwActiveProtocol = coStr.dwActiveProtocol;
570
571 if (IsClientAuthorized(filedes, "access_card", coStr.szReader) == 0)
572 {
573 Log2(PCSC_LOG_CRITICAL, "Rejected unauthorized client for '%s'", coStr.szReader);
574
576 hCard = -1;
577 dwActiveProtocol = -1;
578 }
579 else
580 {
581 Log2(PCSC_LOG_DEBUG, "Authorized client for '%s'", coStr.szReader);
582
583 coStr.rv = SCardConnect(coStr.hContext, coStr.szReader,
584 coStr.dwShareMode, coStr.dwPreferredProtocols,
585 &hCard, &dwActiveProtocol);
586 }
587
588 coStr.hCard = hCard;
589 coStr.dwActiveProtocol = dwActiveProtocol;
590
591 if (coStr.rv == SCARD_S_SUCCESS)
592 {
593 coStr.rv = MSGAddHandle(coStr.hContext, coStr.hCard,
594 threadContext);
595
596 /* if storing the hCard fails we disconnect */
597 if (coStr.rv != SCARD_S_SUCCESS)
598 SCardDisconnect(coStr.hCard, SCARD_LEAVE_CARD);
599 }
600
601 WRITE_BODY(coStr);
602 }
603 break;
604
605 case SCARD_RECONNECT:
606 {
607 struct reconnect_struct rcStr;
608 DWORD dwActiveProtocol = SCARD_PROTOCOL_UNDEFINED;
609
610 READ_BODY(rcStr);
611
612 if (MSGCheckHandleAssociation(rcStr.hCard, threadContext))
613 goto exit;
614
615 rcStr.rv = SCardReconnect(rcStr.hCard, rcStr.dwShareMode,
616 rcStr.dwPreferredProtocols, rcStr.dwInitialization,
617 &dwActiveProtocol);
618 rcStr.dwActiveProtocol = dwActiveProtocol;
619
620 WRITE_BODY(rcStr);
621 }
622 break;
623
624 case SCARD_DISCONNECT:
625 {
626 struct disconnect_struct diStr;
627
628 READ_BODY(diStr);
629
630 if (MSGCheckHandleAssociation(diStr.hCard, threadContext))
631 goto exit;
632
633 diStr.rv = SCardDisconnect(diStr.hCard, diStr.dwDisposition);
634
635 if (SCARD_S_SUCCESS == diStr.rv)
636 diStr.rv = MSGRemoveHandle(diStr.hCard, threadContext);
637
638 WRITE_BODY(diStr);
639 }
640 break;
641
643 {
644 struct begin_struct beStr;
645
646 READ_BODY(beStr);
647
648 if (MSGCheckHandleAssociation(beStr.hCard, threadContext))
649 goto exit;
650
651 beStr.rv = SCardBeginTransaction(beStr.hCard);
652
653 WRITE_BODY(beStr);
654 }
655 break;
656
658 {
659 struct end_struct enStr;
660
661 READ_BODY(enStr);
662
663 if (MSGCheckHandleAssociation(enStr.hCard, threadContext))
664 goto exit;
665
666 enStr.rv = SCardEndTransaction(enStr.hCard,
667 enStr.dwDisposition);
668
669 WRITE_BODY(enStr);
670 }
671 break;
672
673 case SCARD_CANCEL:
674 {
675 struct cancel_struct caStr;
676 SCONTEXT * psTargetContext = NULL;
677
678 READ_BODY(caStr);
679
680 /* find the client */
681 (void)pthread_mutex_lock(&contextsList_lock);
682 psTargetContext = (SCONTEXT *) list_seek(&contextsList,
683 &caStr.hContext);
684 (void)pthread_mutex_unlock(&contextsList_lock);
685
686 /* default value = error */
687 caStr.rv = SCARD_E_INVALID_HANDLE;
688
689 if (psTargetContext != NULL)
690 {
691 uint32_t fd = psTargetContext->dwClientID;
692 LONG rv;
693
694 /* the client should not receive the event
695 * notification now the waiting has been cancelled */
697
698 /* signal the client only if it was still waiting */
699 if (SCARD_S_SUCCESS == rv)
700 caStr.rv = MSGSignalClient(fd, SCARD_E_CANCELLED);
701 else
702 caStr.rv = SCARD_S_SUCCESS;
703 }
704
705 WRITE_BODY(caStr);
706 }
707 break;
708
709 case SCARD_STATUS:
710 {
711 struct status_struct stStr;
712
713 READ_BODY(stStr);
714
715 if (MSGCheckHandleAssociation(stStr.hCard, threadContext))
716 goto exit;
717
718 /* only hCard and return value are used by the client */
719 stStr.rv = SCardStatus(stStr.hCard, NULL, NULL, NULL,
720 NULL, 0, NULL);
721
722 WRITE_BODY(stStr);
723 }
724 break;
725
726 case SCARD_TRANSMIT:
727 {
728 struct transmit_struct trStr;
729 unsigned char pbSendBuffer[MAX_BUFFER_SIZE_EXTENDED];
730 unsigned char pbRecvBuffer[MAX_BUFFER_SIZE_EXTENDED];
731 SCARD_IO_REQUEST ioSendPci;
732 SCARD_IO_REQUEST ioRecvPci;
733 DWORD cbRecvLength;
734
735 READ_BODY(trStr);
736
737 if (MSGCheckHandleAssociation(trStr.hCard, threadContext))
738 goto exit;
739
740 /* avoids buffer overflow */
741 if (trStr.cbSendLength > sizeof(pbSendBuffer))
742 goto buffer_overflow;
743
744 /* read sent buffer */
745 ret = MessageReceive(pbSendBuffer, trStr.cbSendLength, filedes);
746 if (ret != SCARD_S_SUCCESS)
747 {
748 Log2(PCSC_LOG_DEBUG, "Client die: %d", filedes);
749 goto exit;
750 }
751
752 ioSendPci.dwProtocol = trStr.ioSendPciProtocol;
753 ioSendPci.cbPciLength = trStr.ioSendPciLength;
754 ioRecvPci.dwProtocol = trStr.ioRecvPciProtocol;
755 ioRecvPci.cbPciLength = trStr.ioRecvPciLength;
756 cbRecvLength = sizeof pbRecvBuffer;
757
758 trStr.rv = SCardTransmit(trStr.hCard, &ioSendPci,
759 pbSendBuffer, trStr.cbSendLength, &ioRecvPci,
760 pbRecvBuffer, &cbRecvLength);
761
762 if (cbRecvLength > trStr.pcbRecvLength)
763 /* The client buffer is not large enough.
764 * The pbRecvBuffer buffer will NOT be sent a few
765 * lines below. So no buffer overflow is expected. */
767
768 trStr.ioSendPciProtocol = ioSendPci.dwProtocol;
769 trStr.ioSendPciLength = ioSendPci.cbPciLength;
770 trStr.ioRecvPciProtocol = ioRecvPci.dwProtocol;
771 trStr.ioRecvPciLength = ioRecvPci.cbPciLength;
772 trStr.pcbRecvLength = cbRecvLength;
773
774 WRITE_BODY(trStr);
775
776 /* write received buffer */
777 if (SCARD_S_SUCCESS == trStr.rv)
778 ret = MessageSend(pbRecvBuffer, cbRecvLength, filedes);
779 }
780 break;
781
782 case SCARD_CONTROL:
783 {
784 struct control_struct ctStr;
785 unsigned char pbSendBuffer[MAX_BUFFER_SIZE_EXTENDED];
786 unsigned char pbRecvBuffer[MAX_BUFFER_SIZE_EXTENDED];
787 DWORD dwBytesReturned;
788
789 READ_BODY(ctStr);
790
791 if (MSGCheckHandleAssociation(ctStr.hCard, threadContext))
792 goto exit;
793
794 /* avoids buffer overflow */
795 if (ctStr.cbSendLength > sizeof(pbSendBuffer))
796 {
797 goto buffer_overflow;
798 }
799
800 /* read sent buffer */
801 ret = MessageReceive(pbSendBuffer, ctStr.cbSendLength, filedes);
802 if (ret != SCARD_S_SUCCESS)
803 {
804 Log2(PCSC_LOG_DEBUG, "Client die: %d", filedes);
805 goto exit;
806 }
807
808 dwBytesReturned = ctStr.dwBytesReturned;
809
810 ctStr.rv = SCardControl(ctStr.hCard, ctStr.dwControlCode,
811 pbSendBuffer, ctStr.cbSendLength,
812 pbRecvBuffer, sizeof pbRecvBuffer,
813 &dwBytesReturned);
814
815 if (dwBytesReturned > ctStr.cbRecvLength)
816 /* The client buffer is not large enough.
817 * The pbRecvBuffer buffer will NOT be sent a few
818 * lines below. So no buffer overflow is expected. */
820
821 ctStr.dwBytesReturned = dwBytesReturned;
822
823 WRITE_BODY(ctStr);
824
825 /* write received buffer */
826 if (SCARD_S_SUCCESS == ctStr.rv)
827 ret = MessageSend(pbRecvBuffer, dwBytesReturned, filedes);
828 }
829 break;
830
831 case SCARD_GET_ATTRIB:
832 {
833 struct getset_struct gsStr;
834 DWORD cbAttrLen;
835
836 READ_BODY(gsStr);
837
838 if (MSGCheckHandleAssociation(gsStr.hCard, threadContext))
839 goto exit;
840
841 /* avoids buffer overflow */
842 if (gsStr.cbAttrLen > sizeof(gsStr.pbAttr))
843 goto buffer_overflow;
844
845 cbAttrLen = gsStr.cbAttrLen;
846
847 gsStr.rv = SCardGetAttrib(gsStr.hCard, gsStr.dwAttrId,
848 gsStr.pbAttr, &cbAttrLen);
849
850 gsStr.cbAttrLen = cbAttrLen;
851
852 WRITE_BODY(gsStr);
853 }
854 break;
855
856 case SCARD_SET_ATTRIB:
857 {
858 struct getset_struct gsStr;
859
860 READ_BODY(gsStr);
861
862 if (MSGCheckHandleAssociation(gsStr.hCard, threadContext))
863 goto exit;
864
865 /* avoids buffer overflow */
866 if (gsStr.cbAttrLen > sizeof(gsStr.pbAttr))
867 goto buffer_overflow;
868
869 gsStr.rv = SCardSetAttrib(gsStr.hCard, gsStr.dwAttrId,
870 gsStr.pbAttr, gsStr.cbAttrLen);
871
872 WRITE_BODY(gsStr);
873 }
874 break;
875
876 default:
877 Log2(PCSC_LOG_CRITICAL, "Unknown command: %d", header.command);
878 goto exit;
879 }
880
881 /* MessageSend() failed */
882 if (ret != SCARD_S_SUCCESS)
883 {
884 /* Clean up the dead client */
885 Log2(PCSC_LOG_DEBUG, "Client die: %d", filedes);
886 goto exit;
887 }
888 }
889
890buffer_overflow:
891 Log2(PCSC_LOG_DEBUG, "Buffer overflow detected: %d", filedes);
892 goto exit;
893wrong_length:
894 Log2(PCSC_LOG_DEBUG, "Wrong length: %d", filedes);
895exit:
896 (void)close(filedes);
897 MSGCleanupClient(threadContext);
898 (void)pthread_exit((LPVOID) NULL);
899}
900
901LONG MSGSignalClient(uint32_t filedes, LONG rv)
902{
903 uint32_t ret;
904 struct wait_reader_state_change waStr =
905 {
906 .timeOut = 0,
907 .rv = SCARD_S_SUCCESS
908 };
909
910 Log2(PCSC_LOG_DEBUG, "Signal client: %d", filedes);
911
912 waStr.rv = rv;
913 WRITE_BODY_WITH_COMMAND("SIGNAL", waStr);
914
915 return ret;
916} /* MSGSignalClient */
917
918LONG MSGSendReaderStates(uint32_t filedes, bool old_api)
919{
920 uint32_t ret = SCARD_S_SUCCESS;
921
922 Log2(PCSC_LOG_DEBUG, "Send reader states: %d", filedes);
923
924 int length = pcsclite_max_reader_context;
925 if (old_api)
926 {
929 }
930
931 /* dump the readers state */
932 for (int i=0; i<length; i++)
933 {
934 ret = MessageSend(&sReadersContexts[i]->readerState,
935 sizeof(READER_STATE), filedes);
936 }
937
938 if (old_api)
939 {
940 /* complete the list */
942 {
943 READER_STATE readerState = { 0 };
944 for (int i=length; i<PCSCLITE_MAX_READERS_CONTEXTS; i++)
945 ret = MessageSend(&readerState,
946 sizeof(READER_STATE), filedes);
947 }
948 }
949
950 return ret;
951}
952
953static LONG MSGAddContext(SCARDCONTEXT hContext, SCONTEXT * threadContext)
954{
955 threadContext->hContext = hContext;
956 return SCARD_S_SUCCESS;
957}
958
959static LONG MSGRemoveContext(SCARDCONTEXT hContext, SCONTEXT * threadContext)
960{
961 LONG rv;
962 int lrv;
963
964 if (0 == threadContext->hContext)
965 {
966 Log1(PCSC_LOG_ERROR, "Invalidated handle");
968 }
969
970 if (threadContext->hContext != hContext)
972
973 (void)pthread_mutex_lock(&threadContext->cardsList_lock);
974 while (list_size(&threadContext->cardsList) != 0)
975 {
976 READER_CONTEXT * rContext = NULL;
977 SCARDHANDLE hCard;
978 void *ptr;
979
980 /*
981 * Disconnect each of these just in case
982 */
983 ptr = list_get_at(&threadContext->cardsList, 0);
984 if (NULL == ptr)
985 {
986 Log1(PCSC_LOG_CRITICAL, "list_get_at failed");
987 continue;
988 }
989 hCard = *(int32_t *)ptr;
990
991 /*
992 * Unlock the sharing. If the reader or handle already
993 * disappeared, skip the disconnection part and just delete the
994 * orphan handle.
995 */
996 rv = RFReaderInfoById(hCard, &rContext);
997 if (rv != SCARD_S_SUCCESS && rv != SCARD_E_INVALID_VALUE
999 {
1000 (void)pthread_mutex_unlock(&threadContext->cardsList_lock);
1001 return rv;
1002 }
1003
1004 if (rContext)
1005 {
1006 if (0 == rContext->hLockId)
1007 {
1008 /* no lock. Just leave the card */
1009 (void)SCardDisconnect(hCard, SCARD_LEAVE_CARD);
1010 }
1011 else
1012 {
1013 if (hCard != rContext->hLockId)
1014 {
1015 /*
1016 * if the card is locked by someone else we do not reset it
1017 */
1018
1019 /* decrement card use */
1020 (void)SCardDisconnect(hCard, SCARD_LEAVE_CARD);
1021 }
1022 else
1023 {
1024 /* release the lock */
1025 rContext->hLockId = 0;
1026
1027 /*
1028 * We will use SCardStatus to see if the card has been
1029 * reset there is no need to reset each time
1030 * Disconnect is called
1031 */
1032 rv = SCardStatus(hCard, NULL, NULL, NULL, NULL, NULL, NULL);
1033
1034 if (rv == SCARD_W_RESET_CARD || rv == SCARD_W_REMOVED_CARD)
1035 (void)SCardDisconnect(hCard, SCARD_LEAVE_CARD);
1036 else
1037 (void)SCardDisconnect(hCard, SCARD_RESET_CARD);
1038 }
1039 }
1040 }
1041
1042 /* Remove entry from the list */
1043 lrv = list_delete_at(&threadContext->cardsList, 0);
1044 if (lrv < 0)
1045 Log2(PCSC_LOG_CRITICAL,
1046 "list_delete_at failed with return value: %d", lrv);
1047
1048 if (rContext) {
1049 UNREF_READER(rContext)
1050 }
1051 }
1052 (void)pthread_mutex_unlock(&threadContext->cardsList_lock);
1053
1054 /* We only mark the context as no longer in use.
1055 * The memory is freed in MSGCleanupCLient() */
1056 threadContext->hContext = 0;
1057
1058 return SCARD_S_SUCCESS;
1059}
1060
1061static LONG MSGAddHandle(SCARDCONTEXT hContext, SCARDHANDLE hCard,
1062 SCONTEXT * threadContext)
1063{
1064 LONG retval = SCARD_E_INVALID_VALUE;
1065
1066 if (0 == threadContext->hContext)
1067 {
1068 Log1(PCSC_LOG_ERROR, "Invalidated handle");
1070 }
1071
1072 if (threadContext->hContext == hContext)
1073 {
1074 /*
1075 * Find an empty spot to put the hCard value
1076 */
1077 int listLength;
1078
1079 (void)pthread_mutex_lock(&threadContext->cardsList_lock);
1080
1081 listLength = list_size(&threadContext->cardsList);
1082 if (listLength >= contextMaxCardHandles)
1083 {
1084 Log4(PCSC_LOG_DEBUG,
1085 "Too many card handles for thread context @%p: %d (max is %d). "
1086 "Restart pcscd with --max-card-handle-per-thread value",
1087 threadContext, listLength, contextMaxCardHandles);
1088 retval = SCARD_E_NO_MEMORY;
1089 }
1090 else
1091 {
1092 int lrv;
1093
1094 lrv = list_append(&threadContext->cardsList, &hCard);
1095 if (lrv < 0)
1096 {
1097 Log2(PCSC_LOG_CRITICAL,
1098 "list_append failed with return value: %d", lrv);
1099 retval = SCARD_E_NO_MEMORY;
1100 }
1101 else
1102 retval = SCARD_S_SUCCESS;
1103 }
1104
1105 (void)pthread_mutex_unlock(&threadContext->cardsList_lock);
1106 }
1107
1108 return retval;
1109}
1110
1111/* Pre-condition: MSGCheckHandleAssociation must succeed. */
1112static LONG MSGRemoveHandle(SCARDHANDLE hCard, SCONTEXT * threadContext)
1113{
1114 int lrv;
1115
1116 (void)pthread_mutex_lock(&threadContext->cardsList_lock);
1117 lrv = list_delete(&threadContext->cardsList, &hCard);
1118 (void)pthread_mutex_unlock(&threadContext->cardsList_lock);
1119 if (lrv < 0)
1120 {
1121 Log2(PCSC_LOG_CRITICAL, "list_delete failed with error %d", lrv);
1122 return SCARD_E_INVALID_VALUE;
1123 }
1124
1125 return SCARD_S_SUCCESS;
1126}
1127
1128
1129static LONG MSGCheckHandleAssociation(SCARDHANDLE hCard,
1130 SCONTEXT * threadContext)
1131{
1132 int list_index = 0;
1133
1134 if (0 == threadContext->hContext)
1135 {
1136 /* the handle is no more valid. After SCardReleaseContext() for
1137 * example */
1138 Log1(PCSC_LOG_CRITICAL, "Invalidated handle");
1139 return -1;
1140 }
1141
1142 (void)pthread_mutex_lock(&threadContext->cardsList_lock);
1143 list_index = list_locate(&threadContext->cardsList, &hCard);
1144 (void)pthread_mutex_unlock(&threadContext->cardsList_lock);
1145 if (list_index >= 0)
1146 return 0;
1147
1148 /* Must be a rogue client, debug log and sleep a couple of seconds */
1149 Log1(PCSC_LOG_ERROR, "Client failed to authenticate");
1150 (void)SYS_Sleep(2);
1151
1152 return -1;
1153}
1154
1155
1156/* Should be called just prior to exiting the thread as it de-allocates
1157 * the thread memory structures
1158 */
1159static void MSGCleanupClient(SCONTEXT * threadContext)
1160{
1161 int lrv;
1162 int listSize;
1163
1164 if (threadContext->hContext != 0)
1165 {
1166 (void)SCardReleaseContext(threadContext->hContext);
1167 (void)MSGRemoveContext(threadContext->hContext, threadContext);
1168 }
1169
1170 (void)pthread_mutex_lock(&threadContext->cardsList_lock);
1171 list_destroy(&threadContext->cardsList);
1172 (void)pthread_mutex_unlock(&threadContext->cardsList_lock);
1173
1174 Log3(PCSC_LOG_DEBUG,
1175 "Thread is stopping: dwClientID=%d, threadContext @%p",
1176 threadContext->dwClientID, threadContext);
1177
1178 /* Clear the struct to ensure that we detect
1179 * access to de-allocated memory
1180 * Hopefully the compiler won't optimise it out */
1181 memset((void*) threadContext, 0, sizeof(SCONTEXT));
1182 Log2(PCSC_LOG_DEBUG, "Freeing SCONTEXT @%p", threadContext);
1183
1184 (void)pthread_mutex_lock(&contextsList_lock);
1185 lrv = list_delete(&contextsList, threadContext);
1186 listSize = list_size(&contextsList);
1187 (void)pthread_mutex_unlock(&contextsList_lock);
1188 if (lrv < 0)
1189 Log2(PCSC_LOG_CRITICAL, "list_delete failed with error %x", lrv);
1190
1191 free(threadContext);
1192
1193 /* start an exit alarm */
1194 if (AutoExit && (listSize < 1))
1195 {
1196 Log2(PCSC_LOG_DEBUG, "Starting exit alarm in %d seconds",
1197 TIME_BEFORE_SUICIDE);
1198 alarm(TIME_BEFORE_SUICIDE);
1199 }
1200
1201 return;
1202}
This handles debugging.
LONG EHTryToUnregisterClientForEvent(int32_t filedes)
Try to unregister a client If no client is found then do not log an error.
LONG EHUnregisterClientForEvent(int32_t filedes)
Unregister a client and log an error if the client is not found.
This handles card insertion/removal events, updates ATR, protocol, and status information.
#define SCARD_E_INVALID_HANDLE
The supplied handle was invalid.
Definition pcsclite.h:113
#define SCARD_F_INTERNAL_ERROR
An internal consistency check failed.
Definition pcsclite.h:109
#define SCARD_W_SECURITY_VIOLATION
Access was denied because of a security violation.
Definition pcsclite.h:222
#define SCARD_W_RESET_CARD
The smart card has been reset, so any shared state information is invalid.
Definition pcsclite.h:217
#define SCARD_E_SERVICE_STOPPED
The Smart card resource manager has shut down.
Definition pcsclite.h:167
#define SCARD_E_CANCELLED
The action was cancelled by an SCardCancel request.
Definition pcsclite.h:111
#define SCARD_S_SUCCESS
No error was encountered.
Definition pcsclite.h:107
#define SCARD_E_NO_MEMORY
Not enough memory available to complete this command.
Definition pcsclite.h:119
#define SCARD_E_INVALID_VALUE
One or more of the supplied parameters values could not be properly interpreted.
Definition pcsclite.h:141
#define SCARD_W_REMOVED_CARD
The smart card has been removed, so further communication is not possible.
Definition pcsclite.h:219
#define SCARD_E_INSUFFICIENT_BUFFER
The data buffer to receive returned data is too small for the returned data.
Definition pcsclite.h:123
#define SCARD_E_READER_UNAVAILABLE
The specified reader is not currently available for use.
Definition pcsclite.h:153
bool AutoExit
Represents an Application Context on the Server side.
Definition pcscdaemon.c:74
#define SCARD_RESET_CARD
Reset on close.
Definition pcsclite.h:254
LONG SCARDCONTEXT
hContext returned by SCardEstablishContext()
Definition pcsclite.h:52
#define SCARD_PROTOCOL_UNDEFINED
protocol not set
Definition pcsclite.h:240
#define SCARD_LEAVE_CARD
Do nothing on close.
Definition pcsclite.h:253
#define MAX_BUFFER_SIZE_EXTENDED
enhanced (64K + APDU + Lc + Le + SW) Tx/Rx Buffer
Definition pcsclite.h:299
LONG SCARDHANDLE
hCard returned by SCardConnect()
Definition pcsclite.h:55
#define PCSCLITE_MAX_READERS_CONTEXTS
Maximum readers context (a slot is count as a reader).
Definition pcsclite.h:285
This keeps track of a list of currently available reader structures.
struct pubReaderState READER_STATE
Define an exported public reader state structure so each application gets instant notification of cha...
_Atomic SCARDHANDLE hLockId
Lock Id.
Definition readers.h:124
Protocol Control Information (PCI).
Definition pcsclite.h:80
unsigned long dwProtocol
Protocol identifier.
Definition pcsclite.h:81
unsigned long cbPciLength
Protocol Control Inf Length.
Definition pcsclite.h:82
pthread_mutex_t cardsList_lock
lock for the above list
pthread_t pthThread
Event polling thread's ID.
uint32_t dwClientID
Connection ID used to reference the Client.
contained in SCARD_BEGIN_TRANSACTION Messages.
contained in SCARD_CANCEL Messages.
contained in SCARD_CONNECT Messages.
contained in SCARD_CONTROL Messages.
contained in SCARD_DISCONNECT Messages.
contained in SCARD_END_TRANSACTION Messages.
Information contained in SCARD_ESTABLISH_CONTEXT Messages.
contained in SCARD_GET_ATTRIB and Messages.
list object
Definition simclist.h:181
contained in SCARD_RECONNECT Messages.
Information contained in SCARD_RELEASE_CONTEXT Messages.
header structure for client/server message data exchange.
contained in SCARD_STATUS Messages.
contained in SCARD_TRANSMIT Messages.
Information transmitted in CMD_VERSION Messages.
Information contained in CMD_WAIT_READER_STATE_CHANGE Messages.
This handles abstract system level calls.
int SYS_Sleep(int)
Makes the current process sleep for some seconds.
Definition sys_unix.c:62
This handles smart card reader communications.
INTERNAL LONG MessageSend(void *buffer_void, uint64_t buffer_size, int32_t filedes)
Sends a menssage from client to server or vice-versa.
INTERNAL LONG MessageReceive(void *buffer_void, uint64_t buffer_size, int32_t filedes)
Called by the Client to get the response from the server or vice-versa.
This defines some structures and #defines to be used over the transport layer.
#define PROTOCOL_VERSION_MINOR_SERVER_BACKWARD
Minor version the server also supports.
#define PROTOCOL_VERSION_MAJOR
Major version of the current message protocol.
#define PROTOCOL_VERSION_MINOR
Minor version of the current message protocol.
@ SCARD_DISCONNECT
used by SCardDisconnect()
@ SCARD_SET_ATTRIB
used by SCardSetAttrib()
@ SCARD_RELEASE_CONTEXT
used by SCardReleaseContext()
@ CMD_STOP_WAITING_READER_STATE_CHANGE
stop waiting for a reader state change
@ CMD_GET_READERS_STATE
get the readers state
@ SCARD_CONTROL
used by SCardControl()
@ CMD_GET_READERS_STATE_ARRAY
get the readers state array
@ CMD_VERSION
get the client/server protocol version
@ CMD_WAIT_READER_STATE_CHANGE
wait for a reader state change
@ SCARD_RECONNECT
used by SCardReconnect()
@ SCARD_STATUS
used by SCardStatus()
@ SCARD_GET_ATTRIB
used by SCardGetAttrib()
@ CMD_GET_READER_EVENTS
get the number of reader events
@ SCARD_BEGIN_TRANSACTION
used by SCardBeginTransaction()
@ SCARD_TRANSMIT
used by SCardTransmit()
@ SCARD_END_TRANSACTION
used by SCardEndTransaction()
@ SCARD_CANCEL
used by SCardCancel()
@ SCARD_CONNECT
used by SCardConnect()
@ SCARD_ESTABLISH_CONTEXT
used by SCardEstablishContext()
LONG CreateContextThread(uint32_t *pdwClientID)
Creates threads to handle messages received from Clients.
static const char * CommandsText[]
Handles messages received from Clients.
static list_t contextsList
Context tracking list.
pthread_mutex_t contextsList_lock
lock for the above list
This demarshalls functions over the message queue and keeps track of clients and their handles.