pcsc-lite 1.9.9
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-2011
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
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
75extern bool AutoExit;
76static int contextMaxThreadCounter = PCSC_MAX_CONTEXT_THREADS;
77static int contextMaxCardHandles = PCSC_MAX_CONTEXT_CARD_HANDLES;
78
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
102
103static int contextsListhContext_seeker(const void *el, const void *key)
104{
105 const SCONTEXT * currentContext = (SCONTEXT *)el;
106
107 if ((el == NULL) || (key == NULL))
108 {
109 Log3(PCSC_LOG_CRITICAL, "called with NULL pointer: el=%p, key=%p",
110 el, key);
111 return 0;
112 }
113
114 if (currentContext->hContext == *(int32_t *)key)
115 return 1;
116 return 0;
117}
118
119LONG ContextsInitialize(int customMaxThreadCounter,
121{
122 int lrv = 0;
123
124 if (customMaxThreadCounter != 0)
125 contextMaxThreadCounter = customMaxThreadCounter;
126
128 contextMaxCardHandles = customMaxThreadCardHandles;
129
130 lrv = list_init(&contextsList);
131 if (lrv < 0)
132 {
133 Log2(PCSC_LOG_CRITICAL, "list_init failed with return value: %d", lrv);
134 return -1;
135 }
136 lrv = list_attributes_seeker(& contextsList, contextsListhContext_seeker);
137 if (lrv < 0)
138 {
139 Log2(PCSC_LOG_CRITICAL,
140 "list_attributes_seeker failed with return value: %d", lrv);
141 return -1;
142 }
143
145
146 return 1;
147}
148
149void ContextsDeinitialize(void)
150{
151 int listSize;
152 listSize = list_size(&contextsList);
153#ifdef NO_LOG
154 (void)listSize;
155#endif
156 Log2(PCSC_LOG_DEBUG, "remaining threads: %d", listSize);
157 /* This is currently a no-op. It should terminate the threads properly. */
158
159 list_destroy(&contextsList);
160}
161
173{
174 int rv;
175 int lrv;
176 int listSize;
179
181
182 listSize = list_size(&contextsList);
183 if (listSize >= contextMaxThreadCounter)
184 {
185 Log2(PCSC_LOG_CRITICAL, "Too many context running: %d", listSize);
186 goto out;
187 }
188
189 /* Create the context for this thread. */
190 newContext = malloc(sizeof(*newContext));
191 if (NULL == newContext)
192 {
193 Log1(PCSC_LOG_CRITICAL, "Could not allocate new context");
194 goto out;
195 }
196 memset(newContext, 0, sizeof(*newContext));
197
198 newContext->dwClientID = *pdwClientID;
199
200 /* Initialise the list of card contexts */
201 lrv = list_init(&newContext->cardsList);
202 if (lrv < 0)
203 {
204 Log2(PCSC_LOG_CRITICAL, "list_init failed with return value: %d", lrv);
205 goto out;
206 }
207
208 /* request to store copies, and provide the metric function */
209 list_attributes_copy(&newContext->cardsList, list_meter_int32_t, 1);
210
211 /* Adding a comparator
212 * The stored type is SCARDHANDLE (long) but has only 32 bits
213 * useful even on a 64-bit CPU since the API between pcscd and
214 * libpcscliter uses "int32_t hCard;"
215 */
216 lrv = list_attributes_comparator(&newContext->cardsList,
217 list_comparator_int32_t);
218 if (lrv != 0)
219 {
220 Log2(PCSC_LOG_CRITICAL,
221 "list_attributes_comparator failed with return value: %d", lrv);
222 list_destroy(&newContext->cardsList);
223 goto out;
224 }
225
226 (void)pthread_mutex_init(&newContext->cardsList_lock, NULL);
227
228 lrv = list_append(&contextsList, newContext);
229 if (lrv < 0)
230 {
231 Log2(PCSC_LOG_CRITICAL, "list_append failed with return value: %d",
232 lrv);
233 list_destroy(&newContext->cardsList);
234 goto out;
235 }
236
237 rv = ThreadCreate(&newContext->pthThread, THREAD_ATTR_DETACHED,
238 (PCSCLITE_THREAD_FUNCTION( )) ContextThread, (LPVOID) newContext);
239 if (rv)
240 {
241 int lrv2;
242
243 Log2(PCSC_LOG_CRITICAL, "ThreadCreate failed: %s", strerror(rv));
244 lrv2 = list_delete(&contextsList, newContext);
245 if (lrv2 < 0)
246 Log2(PCSC_LOG_CRITICAL, "list_delete failed with error %d", lrv2);
247 list_destroy(&newContext->cardsList);
248 goto out;
249 }
250
251 /* disable any suicide alarm */
252 if (AutoExit)
253 alarm(0);
254
256
257out:
259
260 if (retval != SCARD_S_SUCCESS)
261 {
262 if (newContext)
265 }
266
267 return retval;
268}
269
270/*
271 * A list of local functions used to keep track of clients and their
272 * connections
273 */
274
283#ifndef NO_LOG
284static const char *CommandsText[] = {
285 "NULL",
286 "ESTABLISH_CONTEXT", /* 0x01 */
287 "RELEASE_CONTEXT",
288 "LIST_READERS",
289 "CONNECT",
290 "RECONNECT", /* 0x05 */
291 "DISCONNECT",
292 "BEGIN_TRANSACTION",
293 "END_TRANSACTION",
294 "TRANSMIT",
295 "CONTROL", /* 0x0A */
296 "STATUS",
297 "GET_STATUS_CHANGE",
298 "CANCEL",
299 "CANCEL_TRANSACTION",
300 "GET_ATTRIB", /* 0x0F */
301 "SET_ATTRIB",
302 "CMD_VERSION",
303 "CMD_GET_READERS_STATE",
304 "CMD_WAIT_READER_STATE_CHANGE",
305 "CMD_STOP_WAITING_READER_STATE_CHANGE", /* 0x14 */
306 "NULL"
307};
308#endif
309
310#define READ_BODY(v) \
311 do { \
312 if (header.size != sizeof(v)) \
313 goto wrong_length; \
314 ret = MessageReceive(&v, sizeof(v), filedes); \
315 if (ret != SCARD_S_SUCCESS) { \
316 Log2(PCSC_LOG_DEBUG, "Client die: %d", filedes); \
317 goto exit; \
318 } \
319 } while (0)
320
321#define WRITE_BODY(v) \
322 WRITE_BODY_WITH_COMMAND(CommandsText[header.command], v)
323#define WRITE_BODY_WITH_COMMAND(command, v) \
324 do { \
325 LogRv4(PCSC_LOG_DEBUG, v.rv, "%s for client %d", command, filedes); \
326 ret = MessageSend(&v, sizeof(v), filedes); \
327 } while (0)
328
329static void * ContextThread(LPVOID newContext)
330{
333
334 if (IsClientAuthorized(filedes, "access_pcsc", NULL) == 0)
335 {
336 Log1(PCSC_LOG_CRITICAL, "Rejected unauthorized PC/SC client");
337 goto exit;
338 }
339 else
340 {
341 Log1(PCSC_LOG_DEBUG, "Authorized PC/SC client");
342 }
343
344 Log3(PCSC_LOG_DEBUG, "Thread is started: dwClientID=%d, threadContext @%p",
345 threadContext->dwClientID, threadContext);
346
347 while (1)
348 {
349 struct rxHeader header;
351
352 if (ret != SCARD_S_SUCCESS)
353 {
354 /* Clean up the dead client */
355 Log2(PCSC_LOG_DEBUG, "Client die: %d", filedes);
357 goto exit;
358 }
359
360 if ((header.command > CMD_ENUM_FIRST)
361 && (header.command < CMD_ENUM_LAST))
362 Log3(PCSC_LOG_DEBUG, "Received command: %s from client %d",
363 CommandsText[header.command], filedes);
364
365 switch (header.command)
366 {
367 /* pcsc-lite client/server protocol version */
368 case CMD_VERSION:
369 {
370 struct version_struct veStr;
371
372 READ_BODY(veStr);
373
374 Log3(PCSC_LOG_DEBUG, "Client is protocol version %d:%d",
375 veStr.major, veStr.minor);
376
378
379 /* client and server use different protocol */
380 if ((veStr.major != PROTOCOL_VERSION_MAJOR)
381 || (veStr.minor != PROTOCOL_VERSION_MINOR))
382 {
383 Log1(PCSC_LOG_CRITICAL,
384 "Communication protocol mismatch!");
385 Log3(PCSC_LOG_ERROR, "Client protocol is %d:%d",
386 veStr.major, veStr.minor);
387 Log3(PCSC_LOG_ERROR, "Server protocol is %d:%d",
390 }
391
392 /* set the server protocol version */
395
396 /* send back the response */
397 WRITE_BODY(veStr);
398 }
399 break;
400
402 {
403 /* nothing to read */
404
405#ifdef USE_USB
406 /* wait until all readers are ready */
407 RFWaitForReaderInit();
408#endif
409
410 /* dump the readers state */
411 ret = MessageSend(readerStates, sizeof(readerStates), filedes);
412 }
413 break;
414
416 {
417 /* nothing to read */
418
419#ifdef USE_USB
420 /* wait until all readers are ready */
421 RFWaitForReaderInit();
422#endif
423
424 /* add the client fd to the list and dump the readers state */
425 EHRegisterClientForEvent(filedes);
426 }
427 break;
428
430 {
432 {
433 .timeOut = 0,
434 .rv = 0
435 };
436 LONG rv;
437
438 /* remove the client fd from the list */
440
441 /* send the response only if the client was still in the
442 * list */
443 if (rv != SCARD_F_INTERNAL_ERROR)
444 {
445 waStr.rv = rv;
446 WRITE_BODY(waStr);
447 }
448 }
449 break;
450
452 {
453 struct establish_struct esStr;
454 SCARDCONTEXT hContext;
455
456 READ_BODY(esStr);
457
458 hContext = esStr.hContext;
459 esStr.rv = SCardEstablishContext(esStr.dwScope, 0, 0,
460 &hContext);
461 esStr.hContext = hContext;
462
463 if (esStr.rv == SCARD_S_SUCCESS)
464 esStr.rv = MSGAddContext(esStr.hContext, threadContext);
465
466 WRITE_BODY(esStr);
467 }
468 break;
469
471 {
472 struct release_struct reStr;
473
474 READ_BODY(reStr);
475
476 reStr.rv = SCardReleaseContext(reStr.hContext);
477
478 if (reStr.rv == SCARD_S_SUCCESS)
479 reStr.rv = MSGRemoveContext(reStr.hContext, threadContext);
480
481 WRITE_BODY(reStr);
482 }
483 break;
484
485 case SCARD_CONNECT:
486 {
487 struct connect_struct coStr;
488 SCARDHANDLE hCard;
489 DWORD dwActiveProtocol;
490
491 READ_BODY(coStr);
492
493 coStr.szReader[sizeof(coStr.szReader)-1] = 0;
494 hCard = coStr.hCard;
495 dwActiveProtocol = coStr.dwActiveProtocol;
496
497 if (IsClientAuthorized(filedes, "access_card", coStr.szReader) == 0)
498 {
499 Log2(PCSC_LOG_CRITICAL, "Rejected unauthorized client for '%s'", coStr.szReader);
500 goto exit;
501 }
502 else
503 {
504 Log2(PCSC_LOG_DEBUG, "Authorized client for '%s'", coStr.szReader);
505 }
506
507 coStr.rv = SCardConnect(coStr.hContext, coStr.szReader,
508 coStr.dwShareMode, coStr.dwPreferredProtocols,
509 &hCard, &dwActiveProtocol);
510
511 coStr.hCard = hCard;
512 coStr.dwActiveProtocol = dwActiveProtocol;
513
514 if (coStr.rv == SCARD_S_SUCCESS)
515 {
516 coStr.rv = MSGAddHandle(coStr.hContext, coStr.hCard,
518
519 /* if storing the hCard fails we disconnect */
520 if (coStr.rv != SCARD_S_SUCCESS)
521 SCardDisconnect(coStr.hCard, SCARD_LEAVE_CARD);
522 }
523
524 WRITE_BODY(coStr);
525 }
526 break;
527
528 case SCARD_RECONNECT:
529 {
530 struct reconnect_struct rcStr;
531 DWORD dwActiveProtocol = SCARD_PROTOCOL_UNDEFINED;
532
533 READ_BODY(rcStr);
534
535 if (MSGCheckHandleAssociation(rcStr.hCard, threadContext))
536 goto exit;
537
538 rcStr.rv = SCardReconnect(rcStr.hCard, rcStr.dwShareMode,
539 rcStr.dwPreferredProtocols, rcStr.dwInitialization,
540 &dwActiveProtocol);
541 rcStr.dwActiveProtocol = dwActiveProtocol;
542
543 WRITE_BODY(rcStr);
544 }
545 break;
546
547 case SCARD_DISCONNECT:
548 {
550
551 READ_BODY(diStr);
552
553 if (MSGCheckHandleAssociation(diStr.hCard, threadContext))
554 goto exit;
555
556 diStr.rv = SCardDisconnect(diStr.hCard, diStr.dwDisposition);
557
558 if (SCARD_S_SUCCESS == diStr.rv)
559 diStr.rv = MSGRemoveHandle(diStr.hCard, threadContext);
560
561 WRITE_BODY(diStr);
562 }
563 break;
564
566 {
567 struct begin_struct beStr;
568
569 READ_BODY(beStr);
570
571 if (MSGCheckHandleAssociation(beStr.hCard, threadContext))
572 goto exit;
573
574 beStr.rv = SCardBeginTransaction(beStr.hCard);
575
576 WRITE_BODY(beStr);
577 }
578 break;
579
581 {
582 struct end_struct enStr;
583
584 READ_BODY(enStr);
585
586 if (MSGCheckHandleAssociation(enStr.hCard, threadContext))
587 goto exit;
588
589 enStr.rv = SCardEndTransaction(enStr.hCard,
590 enStr.dwDisposition);
591
592 WRITE_BODY(enStr);
593 }
594 break;
595
596 case SCARD_CANCEL:
597 {
598 struct cancel_struct caStr;
600
601 READ_BODY(caStr);
602
603 /* find the client */
605 psTargetContext = (SCONTEXT *) list_seek(&contextsList,
606 &caStr.hContext);
608
609 /* default value = error */
611
612 if (psTargetContext != NULL)
613 {
614 uint32_t fd = psTargetContext->dwClientID;
615 LONG rv;
616
617 /* the client should not receive the event
618 * notification now the waiting has been cancelled */
620
621 /* signal the client only if it was still waiting */
622 if (SCARD_S_SUCCESS == rv)
623 caStr.rv = MSGSignalClient(fd, SCARD_E_CANCELLED);
624 }
625
626 WRITE_BODY(caStr);
627 }
628 break;
629
630 case SCARD_STATUS:
631 {
632 struct status_struct stStr;
633
634 READ_BODY(stStr);
635
636 if (MSGCheckHandleAssociation(stStr.hCard, threadContext))
637 goto exit;
638
639 /* only hCard and return value are used by the client */
640 stStr.rv = SCardStatus(stStr.hCard, NULL, NULL, NULL,
641 NULL, 0, NULL);
642
643 WRITE_BODY(stStr);
644 }
645 break;
646
647 case SCARD_TRANSMIT:
648 {
649 struct transmit_struct trStr;
654 DWORD cbRecvLength;
655
656 READ_BODY(trStr);
657
658 if (MSGCheckHandleAssociation(trStr.hCard, threadContext))
659 goto exit;
660
661 /* avoids buffer overflow */
662 if (trStr.cbSendLength > sizeof(pbSendBuffer))
663 goto buffer_overflow;
664
665 /* read sent buffer */
666 ret = MessageReceive(pbSendBuffer, trStr.cbSendLength, filedes);
667 if (ret != SCARD_S_SUCCESS)
668 {
669 Log2(PCSC_LOG_DEBUG, "Client die: %d", filedes);
670 goto exit;
671 }
672
673 ioSendPci.dwProtocol = trStr.ioSendPciProtocol;
674 ioSendPci.cbPciLength = trStr.ioSendPciLength;
675 ioRecvPci.dwProtocol = trStr.ioRecvPciProtocol;
676 ioRecvPci.cbPciLength = trStr.ioRecvPciLength;
677 cbRecvLength = sizeof pbRecvBuffer;
678
679 trStr.rv = SCardTransmit(trStr.hCard, &ioSendPci,
680 pbSendBuffer, trStr.cbSendLength, &ioRecvPci,
681 pbRecvBuffer, &cbRecvLength);
682
683 if (cbRecvLength > trStr.pcbRecvLength)
684 /* The client buffer is not large enough.
685 * The pbRecvBuffer buffer will NOT be sent a few
686 * lines below. So no buffer overflow is expected. */
688
689 trStr.ioSendPciProtocol = ioSendPci.dwProtocol;
690 trStr.ioSendPciLength = ioSendPci.cbPciLength;
691 trStr.ioRecvPciProtocol = ioRecvPci.dwProtocol;
692 trStr.ioRecvPciLength = ioRecvPci.cbPciLength;
693 trStr.pcbRecvLength = cbRecvLength;
694
695 WRITE_BODY(trStr);
696
697 /* write received buffer */
698 if (SCARD_S_SUCCESS == trStr.rv)
699 ret = MessageSend(pbRecvBuffer, cbRecvLength, filedes);
700 }
701 break;
702
703 case SCARD_CONTROL:
704 {
705 struct control_struct ctStr;
708 DWORD dwBytesReturned;
709
710 READ_BODY(ctStr);
711
712 if (MSGCheckHandleAssociation(ctStr.hCard, threadContext))
713 goto exit;
714
715 /* avoids buffer overflow */
716 if (ctStr.cbSendLength > sizeof(pbSendBuffer))
717 {
718 goto buffer_overflow;
719 }
720
721 /* read sent buffer */
722 ret = MessageReceive(pbSendBuffer, ctStr.cbSendLength, filedes);
723 if (ret != SCARD_S_SUCCESS)
724 {
725 Log2(PCSC_LOG_DEBUG, "Client die: %d", filedes);
726 goto exit;
727 }
728
729 dwBytesReturned = ctStr.dwBytesReturned;
730
731 ctStr.rv = SCardControl(ctStr.hCard, ctStr.dwControlCode,
732 pbSendBuffer, ctStr.cbSendLength,
734 &dwBytesReturned);
735
736 if (dwBytesReturned > ctStr.cbRecvLength)
737 /* The client buffer is not large enough.
738 * The pbRecvBuffer buffer will NOT be sent a few
739 * lines below. So no buffer overflow is expected. */
741
742 ctStr.dwBytesReturned = dwBytesReturned;
743
744 WRITE_BODY(ctStr);
745
746 /* write received buffer */
747 if (SCARD_S_SUCCESS == ctStr.rv)
748 ret = MessageSend(pbRecvBuffer, dwBytesReturned, filedes);
749 }
750 break;
751
752 case SCARD_GET_ATTRIB:
753 {
754 struct getset_struct gsStr;
755 DWORD cbAttrLen;
756
757 READ_BODY(gsStr);
758
759 if (MSGCheckHandleAssociation(gsStr.hCard, threadContext))
760 goto exit;
761
762 /* avoids buffer overflow */
763 if (gsStr.cbAttrLen > sizeof(gsStr.pbAttr))
764 goto buffer_overflow;
765
766 cbAttrLen = gsStr.cbAttrLen;
767
768 gsStr.rv = SCardGetAttrib(gsStr.hCard, gsStr.dwAttrId,
769 gsStr.pbAttr, &cbAttrLen);
770
771 gsStr.cbAttrLen = cbAttrLen;
772
773 WRITE_BODY(gsStr);
774 }
775 break;
776
777 case SCARD_SET_ATTRIB:
778 {
779 struct getset_struct gsStr;
780
781 READ_BODY(gsStr);
782
783 if (MSGCheckHandleAssociation(gsStr.hCard, threadContext))
784 goto exit;
785
786 /* avoids buffer overflow */
787 if (gsStr.cbAttrLen > sizeof(gsStr.pbAttr))
788 goto buffer_overflow;
789
790 gsStr.rv = SCardSetAttrib(gsStr.hCard, gsStr.dwAttrId,
791 gsStr.pbAttr, gsStr.cbAttrLen);
792
793 WRITE_BODY(gsStr);
794 }
795 break;
796
797 default:
798 Log2(PCSC_LOG_CRITICAL, "Unknown command: %d", header.command);
799 goto exit;
800 }
801
802 /* MessageSend() failed */
803 if (ret != SCARD_S_SUCCESS)
804 {
805 /* Clean up the dead client */
806 Log2(PCSC_LOG_DEBUG, "Client die: %d", filedes);
807 goto exit;
808 }
809 }
810
812 Log2(PCSC_LOG_DEBUG, "Buffer overflow detected: %d", filedes);
813 goto exit;
815 Log2(PCSC_LOG_DEBUG, "Wrong length: %d", filedes);
816exit:
818 MSGCleanupClient(threadContext);
819 (void)pthread_exit((LPVOID) NULL);
820}
821
822LONG MSGSignalClient(uint32_t filedes, LONG rv)
823{
826 {
827 .timeOut = 0,
828 .rv = 0
829 };
830
831 Log2(PCSC_LOG_DEBUG, "Signal client: %d", filedes);
832
833 waStr.rv = rv;
834 WRITE_BODY_WITH_COMMAND("SIGNAL", waStr);
835
836 return ret;
837} /* MSGSignalClient */
838
839LONG MSGSendReaderStates(uint32_t filedes)
840{
842
843 Log2(PCSC_LOG_DEBUG, "Send reader states: %d", filedes);
844
845 /* dump the readers state */
846 ret = MessageSend(readerStates, sizeof(readerStates), filedes);
847
848 return ret;
849}
850
851static LONG MSGAddContext(SCARDCONTEXT hContext, SCONTEXT * threadContext)
852{
853 threadContext->hContext = hContext;
854 return SCARD_S_SUCCESS;
855}
856
857static LONG MSGRemoveContext(SCARDCONTEXT hContext, SCONTEXT * threadContext)
858{
859 LONG rv;
860 int lrv;
861
862 if (0 == threadContext->hContext)
863 {
864 Log1(PCSC_LOG_ERROR, "Invalidated handle");
866 }
867
868 if (threadContext->hContext != hContext)
870
871 (void)pthread_mutex_lock(&threadContext->cardsList_lock);
872 while (list_size(&threadContext->cardsList) != 0)
873 {
875 SCARDHANDLE hCard;
876 void *ptr;
877
878 /*
879 * Disconnect each of these just in case
880 */
881 ptr = list_get_at(&threadContext->cardsList, 0);
882 if (NULL == ptr)
883 {
884 Log1(PCSC_LOG_CRITICAL, "list_get_at failed");
885 continue;
886 }
887 hCard = *(int32_t *)ptr;
888
889 /*
890 * Unlock the sharing. If the reader or handle already
891 * disappeared, skip the disconnection part and just delete the
892 * orphan handle.
893 */
894 rv = RFReaderInfoById(hCard, &rContext);
895 if (rv != SCARD_S_SUCCESS && rv != SCARD_E_INVALID_VALUE
897 {
898 (void)pthread_mutex_unlock(&threadContext->cardsList_lock);
899 return rv;
900 }
901
902 if (rContext)
903 {
904 if (0 == rContext->hLockId)
905 {
906 /* no lock. Just leave the card */
907 (void)SCardDisconnect(hCard, SCARD_LEAVE_CARD);
908 }
909 else
910 {
911 if (hCard != rContext->hLockId)
912 {
913 /*
914 * if the card is locked by someone else we do not reset it
915 */
916
917 /* decrement card use */
918 (void)SCardDisconnect(hCard, SCARD_LEAVE_CARD);
919 }
920 else
921 {
922 /* release the lock */
923 rContext->hLockId = 0;
924
925 /*
926 * We will use SCardStatus to see if the card has been
927 * reset there is no need to reset each time
928 * Disconnect is called
929 */
930 rv = SCardStatus(hCard, NULL, NULL, NULL, NULL, NULL, NULL);
931
932 if (rv == SCARD_W_RESET_CARD || rv == SCARD_W_REMOVED_CARD)
933 (void)SCardDisconnect(hCard, SCARD_LEAVE_CARD);
934 else
935 (void)SCardDisconnect(hCard, SCARD_RESET_CARD);
936 }
937 }
938 }
939
940 /* Remove entry from the list */
941 lrv = list_delete_at(&threadContext->cardsList, 0);
942 if (lrv < 0)
943 Log2(PCSC_LOG_CRITICAL,
944 "list_delete_at failed with return value: %d", lrv);
945
946 if (rContext) {
947 UNREF_READER(rContext)
948 }
949 }
950 (void)pthread_mutex_unlock(&threadContext->cardsList_lock);
951
952 /* We only mark the context as no longer in use.
953 * The memory is freed in MSGCleanupCLient() */
954 threadContext->hContext = 0;
955
956 return SCARD_S_SUCCESS;
957}
958
959static LONG MSGAddHandle(SCARDCONTEXT hContext, SCARDHANDLE hCard,
961{
963
964 if (0 == threadContext->hContext)
965 {
966 Log1(PCSC_LOG_ERROR, "Invalidated handle");
968 }
969
970 if (threadContext->hContext == hContext)
971 {
972 /*
973 * Find an empty spot to put the hCard value
974 */
975 int listLength;
976
977 (void)pthread_mutex_lock(&threadContext->cardsList_lock);
978
979 listLength = list_size(&threadContext->cardsList);
980 if (listLength >= contextMaxCardHandles)
981 {
982 Log4(PCSC_LOG_DEBUG,
983 "Too many card handles for thread context @%p: %d (max is %d). "
984 "Restart pcscd with --max-card-handle-per-thread value",
985 threadContext, listLength, contextMaxCardHandles);
987 }
988 else
989 {
990 int lrv;
991
992 lrv = list_append(&threadContext->cardsList, &hCard);
993 if (lrv < 0)
994 {
995 Log2(PCSC_LOG_CRITICAL,
996 "list_append failed with return value: %d", lrv);
998 }
999 else
1001 }
1002
1003 (void)pthread_mutex_unlock(&threadContext->cardsList_lock);
1004 }
1005
1006 return retval;
1007}
1008
1009/* Pre-condition: MSGCheckHandleAssociation must succeed. */
1010static LONG MSGRemoveHandle(SCARDHANDLE hCard, SCONTEXT * threadContext)
1011{
1012 int lrv;
1013
1014 (void)pthread_mutex_lock(&threadContext->cardsList_lock);
1015 lrv = list_delete(&threadContext->cardsList, &hCard);
1016 (void)pthread_mutex_unlock(&threadContext->cardsList_lock);
1017 if (lrv < 0)
1018 {
1019 Log2(PCSC_LOG_CRITICAL, "list_delete failed with error %d", lrv);
1020 return SCARD_E_INVALID_VALUE;
1021 }
1022
1023 return SCARD_S_SUCCESS;
1024}
1025
1026
1027static LONG MSGCheckHandleAssociation(SCARDHANDLE hCard,
1029{
1030 int list_index = 0;
1031
1032 if (0 == threadContext->hContext)
1033 {
1034 /* the handle is no more valid. After SCardReleaseContext() for
1035 * example */
1036 Log1(PCSC_LOG_CRITICAL, "Invalidated handle");
1037 return -1;
1038 }
1039
1040 (void)pthread_mutex_lock(&threadContext->cardsList_lock);
1041 list_index = list_locate(&threadContext->cardsList, &hCard);
1042 (void)pthread_mutex_unlock(&threadContext->cardsList_lock);
1043 if (list_index >= 0)
1044 return 0;
1045
1046 /* Must be a rogue client, debug log and sleep a couple of seconds */
1047 Log1(PCSC_LOG_ERROR, "Client failed to authenticate");
1048 (void)SYS_Sleep(2);
1049
1050 return -1;
1051}
1052
1053
1054/* Should be called just prior to exiting the thread as it de-allocates
1055 * the thread memory structures
1056 */
1057static void MSGCleanupClient(SCONTEXT * threadContext)
1058{
1059 int lrv;
1060 int listSize;
1061
1062 if (threadContext->hContext != 0)
1063 {
1064 (void)SCardReleaseContext(threadContext->hContext);
1065 (void)MSGRemoveContext(threadContext->hContext, threadContext);
1066 }
1067
1068 (void)pthread_mutex_lock(&threadContext->cardsList_lock);
1069 list_destroy(&threadContext->cardsList);
1070 (void)pthread_mutex_unlock(&threadContext->cardsList_lock);
1071
1072 Log3(PCSC_LOG_DEBUG,
1073 "Thread is stopping: dwClientID=%d, threadContext @%p",
1074 threadContext->dwClientID, threadContext);
1075
1076 /* Clear the struct to ensure that we detect
1077 * access to de-allocated memory
1078 * Hopefully the compiler won't optimise it out */
1079 memset((void*) threadContext, 0, sizeof(SCONTEXT));
1080 Log2(PCSC_LOG_DEBUG, "Freeing SCONTEXT @%p", threadContext);
1081
1083 lrv = list_delete(&contextsList, threadContext);
1084 listSize = list_size(&contextsList);
1086 if (lrv < 0)
1087 Log2(PCSC_LOG_CRITICAL, "list_delete failed with error %x", lrv);
1088
1090
1091 /* start a suicide alarm */
1092 if (AutoExit && (listSize < 1))
1093 {
1094 Log2(PCSC_LOG_DEBUG, "Starting suicide alarm in %d seconds",
1095 TIME_BEFORE_SUICIDE);
1096 alarm(TIME_BEFORE_SUICIDE);
1097 }
1098
1099 return;
1100}
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_RESET_CARD
The smart card has been reset, so any shared state information is invalid.
Definition pcsclite.h:216
#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:218
#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
This keeps a list of defines for pcsc-lite.
@ POWER_STATE_POWERED
powered
Definition pcscd.h:64
#define SCARD_RESET_CARD
Reset on close.
Definition pcsclite.h:253
LONG SCARDCONTEXT
hContext returned by SCardEstablishContext()
Definition pcsclite.h:52
#define SCARD_PROTOCOL_UNDEFINED
protocol not set
Definition pcsclite.h:239
#define SCARD_LEAVE_CARD
Do nothing on close.
Definition pcsclite.h:252
#define MAX_BUFFER_SIZE_EXTENDED
enhanced (64K + APDU + Lc + Le + SW) Tx/Rx Buffer
Definition pcsclite.h:298
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:284
This keeps track of a list of currently available reader structures.
Protocol Control Information (PCI)
Definition pcsclite.h:80
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
Define an exported public reader state structure so each application gets instant notification of cha...
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.
uint32_t timeOut
timeout in ms
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_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_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()
@ 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()
bool AutoExit
Represents an Application Context on the Server side.
Definition pcscdaemon.c:76
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.