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