pcsc-lite 2.4.1
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
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
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
412 veStr.rv = SCARD_E_SERVICE_STOPPED;
413 else
414 Log1(PCSC_LOG_INFO, "Enable backward compatibility");
415 }
416
417 /* set the server protocol version */
418 veStr.major = PROTOCOL_VERSION_MAJOR;
419 veStr.minor = PROTOCOL_VERSION_MINOR;
420
421 /* send back the response */
422 WRITE_BODY(veStr);
423 }
424 break;
425
427 {
428 /* nothing to read */
429
430#ifdef USE_USB
431 /* wait until all readers are ready */
432 RFWaitForReaderInit();
433#endif
434
435 /* dump the readers state */
436 ret = MessageSend(readerStates, sizeof(readerStates), filedes);
437 }
438 break;
439
441 {
442 /* nothing to read */
443
444#ifdef USE_USB
445 /* wait until all readers are ready */
446 RFWaitForReaderInit();
447#endif
448
449 /* add the client fd to the list and dump the readers state */
450 EHRegisterClientForEvent(filedes);
451 }
452 break;
453
455 {
456 struct wait_reader_state_change waStr =
457 {
458 .timeOut = 0,
459 .rv = SCARD_S_SUCCESS
460 };
461 LONG rv;
462
463 /* remove the client fd from the list */
464 rv = EHUnregisterClientForEvent(filedes);
465
466 /* send the response only if the client was still in the
467 * list */
468 if (rv != SCARD_F_INTERNAL_ERROR)
469 {
470 waStr.rv = rv;
471 WRITE_BODY(waStr);
472 }
473 }
474 break;
475
477 {
478 /* nothing to read */
479
480 struct get_reader_events readerEvents =
481 {
482 .readerEvents = ReaderEvents,
483 .rv = SCARD_S_SUCCESS
484 };
485
486 WRITE_BODY(readerEvents);
487 }
488 break;
489
491 {
492 struct establish_struct esStr;
493 SCARDCONTEXT hContext;
494
495 READ_BODY(esStr);
496
497 hContext = esStr.hContext;
498 esStr.rv = SCardEstablishContext(esStr.dwScope, 0, 0,
499 &hContext);
500 esStr.hContext = hContext;
501
502 if (esStr.rv == SCARD_S_SUCCESS)
503 esStr.rv = MSGAddContext(esStr.hContext, threadContext);
504
505 WRITE_BODY(esStr);
506 }
507 break;
508
510 {
511 struct release_struct reStr;
512
513 READ_BODY(reStr);
514
515 reStr.rv = SCardReleaseContext(reStr.hContext);
516
517 if (reStr.rv == SCARD_S_SUCCESS)
518 reStr.rv = MSGRemoveContext(reStr.hContext, threadContext);
519
520 WRITE_BODY(reStr);
521 }
522 break;
523
524 case SCARD_CONNECT:
525 {
526 struct connect_struct coStr;
527 SCARDHANDLE hCard;
528 DWORD dwActiveProtocol;
529
530 READ_BODY(coStr);
531
532 coStr.szReader[sizeof(coStr.szReader)-1] = 0;
533 hCard = coStr.hCard;
534 dwActiveProtocol = coStr.dwActiveProtocol;
535
536 if (IsClientAuthorized(filedes, "access_card", coStr.szReader) == 0)
537 {
538 Log2(PCSC_LOG_CRITICAL, "Rejected unauthorized client for '%s'", coStr.szReader);
539
541 hCard = -1;
542 dwActiveProtocol = -1;
543 }
544 else
545 {
546 Log2(PCSC_LOG_DEBUG, "Authorized client for '%s'", coStr.szReader);
547
548 coStr.rv = SCardConnect(coStr.hContext, coStr.szReader,
549 coStr.dwShareMode, coStr.dwPreferredProtocols,
550 &hCard, &dwActiveProtocol);
551 }
552
553 coStr.hCard = hCard;
554 coStr.dwActiveProtocol = dwActiveProtocol;
555
556 if (coStr.rv == SCARD_S_SUCCESS)
557 {
558 coStr.rv = MSGAddHandle(coStr.hContext, coStr.hCard,
559 threadContext);
560
561 /* if storing the hCard fails we disconnect */
562 if (coStr.rv != SCARD_S_SUCCESS)
563 SCardDisconnect(coStr.hCard, SCARD_LEAVE_CARD);
564 }
565
566 WRITE_BODY(coStr);
567 }
568 break;
569
570 case SCARD_RECONNECT:
571 {
572 struct reconnect_struct rcStr;
573 DWORD dwActiveProtocol = SCARD_PROTOCOL_UNDEFINED;
574
575 READ_BODY(rcStr);
576
577 if (MSGCheckHandleAssociation(rcStr.hCard, threadContext))
578 goto exit;
579
580 rcStr.rv = SCardReconnect(rcStr.hCard, rcStr.dwShareMode,
581 rcStr.dwPreferredProtocols, rcStr.dwInitialization,
582 &dwActiveProtocol);
583 rcStr.dwActiveProtocol = dwActiveProtocol;
584
585 WRITE_BODY(rcStr);
586 }
587 break;
588
589 case SCARD_DISCONNECT:
590 {
591 struct disconnect_struct diStr;
592
593 READ_BODY(diStr);
594
595 if (MSGCheckHandleAssociation(diStr.hCard, threadContext))
596 goto exit;
597
598 diStr.rv = SCardDisconnect(diStr.hCard, diStr.dwDisposition);
599
600 if (SCARD_S_SUCCESS == diStr.rv)
601 diStr.rv = MSGRemoveHandle(diStr.hCard, threadContext);
602
603 WRITE_BODY(diStr);
604 }
605 break;
606
608 {
609 struct begin_struct beStr;
610
611 READ_BODY(beStr);
612
613 if (MSGCheckHandleAssociation(beStr.hCard, threadContext))
614 goto exit;
615
616 beStr.rv = SCardBeginTransaction(beStr.hCard);
617
618 WRITE_BODY(beStr);
619 }
620 break;
621
623 {
624 struct end_struct enStr;
625
626 READ_BODY(enStr);
627
628 if (MSGCheckHandleAssociation(enStr.hCard, threadContext))
629 goto exit;
630
631 enStr.rv = SCardEndTransaction(enStr.hCard,
632 enStr.dwDisposition);
633
634 WRITE_BODY(enStr);
635 }
636 break;
637
638 case SCARD_CANCEL:
639 {
640 struct cancel_struct caStr;
641 SCONTEXT * psTargetContext = NULL;
642
643 READ_BODY(caStr);
644
645 /* find the client */
646 (void)pthread_mutex_lock(&contextsList_lock);
647 psTargetContext = (SCONTEXT *) list_seek(&contextsList,
648 &caStr.hContext);
649 (void)pthread_mutex_unlock(&contextsList_lock);
650
651 /* default value = error */
652 caStr.rv = SCARD_E_INVALID_HANDLE;
653
654 if (psTargetContext != NULL)
655 {
656 uint32_t fd = psTargetContext->dwClientID;
657 LONG rv;
658
659 /* the client should not receive the event
660 * notification now the waiting has been cancelled */
662
663 /* signal the client only if it was still waiting */
664 if (SCARD_S_SUCCESS == rv)
665 caStr.rv = MSGSignalClient(fd, SCARD_E_CANCELLED);
666 else
667 caStr.rv = SCARD_S_SUCCESS;
668 }
669
670 WRITE_BODY(caStr);
671 }
672 break;
673
674 case SCARD_STATUS:
675 {
676 struct status_struct stStr;
677
678 READ_BODY(stStr);
679
680 if (MSGCheckHandleAssociation(stStr.hCard, threadContext))
681 goto exit;
682
683 /* only hCard and return value are used by the client */
684 stStr.rv = SCardStatus(stStr.hCard, NULL, NULL, NULL,
685 NULL, 0, NULL);
686
687 WRITE_BODY(stStr);
688 }
689 break;
690
691 case SCARD_TRANSMIT:
692 {
693 struct transmit_struct trStr;
694 unsigned char pbSendBuffer[MAX_BUFFER_SIZE_EXTENDED];
695 unsigned char pbRecvBuffer[MAX_BUFFER_SIZE_EXTENDED];
696 SCARD_IO_REQUEST ioSendPci;
697 SCARD_IO_REQUEST ioRecvPci;
698 DWORD cbRecvLength;
699
700 READ_BODY(trStr);
701
702 if (MSGCheckHandleAssociation(trStr.hCard, threadContext))
703 goto exit;
704
705 /* avoids buffer overflow */
706 if (trStr.cbSendLength > sizeof(pbSendBuffer))
707 goto buffer_overflow;
708
709 /* read sent buffer */
710 ret = MessageReceive(pbSendBuffer, trStr.cbSendLength, filedes);
711 if (ret != SCARD_S_SUCCESS)
712 {
713 Log2(PCSC_LOG_DEBUG, "Client die: %d", filedes);
714 goto exit;
715 }
716
717 ioSendPci.dwProtocol = trStr.ioSendPciProtocol;
718 ioSendPci.cbPciLength = trStr.ioSendPciLength;
719 ioRecvPci.dwProtocol = trStr.ioRecvPciProtocol;
720 ioRecvPci.cbPciLength = trStr.ioRecvPciLength;
721 cbRecvLength = sizeof pbRecvBuffer;
722
723 trStr.rv = SCardTransmit(trStr.hCard, &ioSendPci,
724 pbSendBuffer, trStr.cbSendLength, &ioRecvPci,
725 pbRecvBuffer, &cbRecvLength);
726
727 if (cbRecvLength > trStr.pcbRecvLength)
728 /* The client buffer is not large enough.
729 * The pbRecvBuffer buffer will NOT be sent a few
730 * lines below. So no buffer overflow is expected. */
732
733 trStr.ioSendPciProtocol = ioSendPci.dwProtocol;
734 trStr.ioSendPciLength = ioSendPci.cbPciLength;
735 trStr.ioRecvPciProtocol = ioRecvPci.dwProtocol;
736 trStr.ioRecvPciLength = ioRecvPci.cbPciLength;
737 trStr.pcbRecvLength = cbRecvLength;
738
739 WRITE_BODY(trStr);
740
741 /* write received buffer */
742 if (SCARD_S_SUCCESS == trStr.rv)
743 ret = MessageSend(pbRecvBuffer, cbRecvLength, filedes);
744 }
745 break;
746
747 case SCARD_CONTROL:
748 {
749 struct control_struct ctStr;
750 unsigned char pbSendBuffer[MAX_BUFFER_SIZE_EXTENDED];
751 unsigned char pbRecvBuffer[MAX_BUFFER_SIZE_EXTENDED];
752 DWORD dwBytesReturned;
753
754 READ_BODY(ctStr);
755
756 if (MSGCheckHandleAssociation(ctStr.hCard, threadContext))
757 goto exit;
758
759 /* avoids buffer overflow */
760 if (ctStr.cbSendLength > sizeof(pbSendBuffer))
761 {
762 goto buffer_overflow;
763 }
764
765 /* read sent buffer */
766 ret = MessageReceive(pbSendBuffer, ctStr.cbSendLength, filedes);
767 if (ret != SCARD_S_SUCCESS)
768 {
769 Log2(PCSC_LOG_DEBUG, "Client die: %d", filedes);
770 goto exit;
771 }
772
773 dwBytesReturned = ctStr.dwBytesReturned;
774
775 ctStr.rv = SCardControl(ctStr.hCard, ctStr.dwControlCode,
776 pbSendBuffer, ctStr.cbSendLength,
777 pbRecvBuffer, sizeof pbRecvBuffer,
778 &dwBytesReturned);
779
780 if (dwBytesReturned > ctStr.cbRecvLength)
781 /* The client buffer is not large enough.
782 * The pbRecvBuffer buffer will NOT be sent a few
783 * lines below. So no buffer overflow is expected. */
785
786 ctStr.dwBytesReturned = dwBytesReturned;
787
788 WRITE_BODY(ctStr);
789
790 /* write received buffer */
791 if (SCARD_S_SUCCESS == ctStr.rv)
792 ret = MessageSend(pbRecvBuffer, dwBytesReturned, filedes);
793 }
794 break;
795
796 case SCARD_GET_ATTRIB:
797 {
798 struct getset_struct gsStr;
799 DWORD cbAttrLen;
800
801 READ_BODY(gsStr);
802
803 if (MSGCheckHandleAssociation(gsStr.hCard, threadContext))
804 goto exit;
805
806 /* avoids buffer overflow */
807 if (gsStr.cbAttrLen > sizeof(gsStr.pbAttr))
808 goto buffer_overflow;
809
810 cbAttrLen = gsStr.cbAttrLen;
811
812 gsStr.rv = SCardGetAttrib(gsStr.hCard, gsStr.dwAttrId,
813 gsStr.pbAttr, &cbAttrLen);
814
815 gsStr.cbAttrLen = cbAttrLen;
816
817 WRITE_BODY(gsStr);
818 }
819 break;
820
821 case SCARD_SET_ATTRIB:
822 {
823 struct getset_struct gsStr;
824
825 READ_BODY(gsStr);
826
827 if (MSGCheckHandleAssociation(gsStr.hCard, threadContext))
828 goto exit;
829
830 /* avoids buffer overflow */
831 if (gsStr.cbAttrLen > sizeof(gsStr.pbAttr))
832 goto buffer_overflow;
833
834 gsStr.rv = SCardSetAttrib(gsStr.hCard, gsStr.dwAttrId,
835 gsStr.pbAttr, gsStr.cbAttrLen);
836
837 WRITE_BODY(gsStr);
838 }
839 break;
840
841 default:
842 Log2(PCSC_LOG_CRITICAL, "Unknown command: %d", header.command);
843 goto exit;
844 }
845
846 /* MessageSend() failed */
847 if (ret != SCARD_S_SUCCESS)
848 {
849 /* Clean up the dead client */
850 Log2(PCSC_LOG_DEBUG, "Client die: %d", filedes);
851 goto exit;
852 }
853 }
854
855buffer_overflow:
856 Log2(PCSC_LOG_DEBUG, "Buffer overflow detected: %d", filedes);
857 goto exit;
858wrong_length:
859 Log2(PCSC_LOG_DEBUG, "Wrong length: %d", filedes);
860exit:
861 (void)close(filedes);
862 MSGCleanupClient(threadContext);
863 (void)pthread_exit((LPVOID) NULL);
864}
865
866LONG MSGSignalClient(uint32_t filedes, LONG rv)
867{
868 uint32_t ret;
869 struct wait_reader_state_change waStr =
870 {
871 .timeOut = 0,
872 .rv = SCARD_S_SUCCESS
873 };
874
875 Log2(PCSC_LOG_DEBUG, "Signal client: %d", filedes);
876
877 waStr.rv = rv;
878 WRITE_BODY_WITH_COMMAND("SIGNAL", waStr);
879
880 return ret;
881} /* MSGSignalClient */
882
883LONG MSGSendReaderStates(uint32_t filedes)
884{
885 uint32_t ret;
886
887 Log2(PCSC_LOG_DEBUG, "Send reader states: %d", filedes);
888
889 /* dump the readers state */
890 ret = MessageSend(readerStates, sizeof(readerStates), filedes);
891
892 return ret;
893}
894
895static LONG MSGAddContext(SCARDCONTEXT hContext, SCONTEXT * threadContext)
896{
897 threadContext->hContext = hContext;
898 return SCARD_S_SUCCESS;
899}
900
901static LONG MSGRemoveContext(SCARDCONTEXT hContext, SCONTEXT * threadContext)
902{
903 LONG rv;
904 int lrv;
905
906 if (0 == threadContext->hContext)
907 {
908 Log1(PCSC_LOG_ERROR, "Invalidated handle");
910 }
911
912 if (threadContext->hContext != hContext)
914
915 (void)pthread_mutex_lock(&threadContext->cardsList_lock);
916 while (list_size(&threadContext->cardsList) != 0)
917 {
918 READER_CONTEXT * rContext = NULL;
919 SCARDHANDLE hCard;
920 void *ptr;
921
922 /*
923 * Disconnect each of these just in case
924 */
925 ptr = list_get_at(&threadContext->cardsList, 0);
926 if (NULL == ptr)
927 {
928 Log1(PCSC_LOG_CRITICAL, "list_get_at failed");
929 continue;
930 }
931 hCard = *(int32_t *)ptr;
932
933 /*
934 * Unlock the sharing. If the reader or handle already
935 * disappeared, skip the disconnection part and just delete the
936 * orphan handle.
937 */
938 rv = RFReaderInfoById(hCard, &rContext);
939 if (rv != SCARD_S_SUCCESS && rv != SCARD_E_INVALID_VALUE
941 {
942 (void)pthread_mutex_unlock(&threadContext->cardsList_lock);
943 return rv;
944 }
945
946 if (rContext)
947 {
948 if (0 == rContext->hLockId)
949 {
950 /* no lock. Just leave the card */
951 (void)SCardDisconnect(hCard, SCARD_LEAVE_CARD);
952 }
953 else
954 {
955 if (hCard != rContext->hLockId)
956 {
957 /*
958 * if the card is locked by someone else we do not reset it
959 */
960
961 /* decrement card use */
962 (void)SCardDisconnect(hCard, SCARD_LEAVE_CARD);
963 }
964 else
965 {
966 /* release the lock */
967 rContext->hLockId = 0;
968
969 /*
970 * We will use SCardStatus to see if the card has been
971 * reset there is no need to reset each time
972 * Disconnect is called
973 */
974 rv = SCardStatus(hCard, NULL, NULL, NULL, NULL, NULL, NULL);
975
976 if (rv == SCARD_W_RESET_CARD || rv == SCARD_W_REMOVED_CARD)
977 (void)SCardDisconnect(hCard, SCARD_LEAVE_CARD);
978 else
979 (void)SCardDisconnect(hCard, SCARD_RESET_CARD);
980 }
981 }
982 }
983
984 /* Remove entry from the list */
985 lrv = list_delete_at(&threadContext->cardsList, 0);
986 if (lrv < 0)
987 Log2(PCSC_LOG_CRITICAL,
988 "list_delete_at failed with return value: %d", lrv);
989
990 if (rContext) {
991 UNREF_READER(rContext)
992 }
993 }
994 (void)pthread_mutex_unlock(&threadContext->cardsList_lock);
995
996 /* We only mark the context as no longer in use.
997 * The memory is freed in MSGCleanupCLient() */
998 threadContext->hContext = 0;
999
1000 return SCARD_S_SUCCESS;
1001}
1002
1003static LONG MSGAddHandle(SCARDCONTEXT hContext, SCARDHANDLE hCard,
1004 SCONTEXT * threadContext)
1005{
1006 LONG retval = SCARD_E_INVALID_VALUE;
1007
1008 if (0 == threadContext->hContext)
1009 {
1010 Log1(PCSC_LOG_ERROR, "Invalidated handle");
1012 }
1013
1014 if (threadContext->hContext == hContext)
1015 {
1016 /*
1017 * Find an empty spot to put the hCard value
1018 */
1019 int listLength;
1020
1021 (void)pthread_mutex_lock(&threadContext->cardsList_lock);
1022
1023 listLength = list_size(&threadContext->cardsList);
1024 if (listLength >= contextMaxCardHandles)
1025 {
1026 Log4(PCSC_LOG_DEBUG,
1027 "Too many card handles for thread context @%p: %d (max is %d). "
1028 "Restart pcscd with --max-card-handle-per-thread value",
1029 threadContext, listLength, contextMaxCardHandles);
1030 retval = SCARD_E_NO_MEMORY;
1031 }
1032 else
1033 {
1034 int lrv;
1035
1036 lrv = list_append(&threadContext->cardsList, &hCard);
1037 if (lrv < 0)
1038 {
1039 Log2(PCSC_LOG_CRITICAL,
1040 "list_append failed with return value: %d", lrv);
1041 retval = SCARD_E_NO_MEMORY;
1042 }
1043 else
1044 retval = SCARD_S_SUCCESS;
1045 }
1046
1047 (void)pthread_mutex_unlock(&threadContext->cardsList_lock);
1048 }
1049
1050 return retval;
1051}
1052
1053/* Pre-condition: MSGCheckHandleAssociation must succeed. */
1054static LONG MSGRemoveHandle(SCARDHANDLE hCard, SCONTEXT * threadContext)
1055{
1056 int lrv;
1057
1058 (void)pthread_mutex_lock(&threadContext->cardsList_lock);
1059 lrv = list_delete(&threadContext->cardsList, &hCard);
1060 (void)pthread_mutex_unlock(&threadContext->cardsList_lock);
1061 if (lrv < 0)
1062 {
1063 Log2(PCSC_LOG_CRITICAL, "list_delete failed with error %d", lrv);
1064 return SCARD_E_INVALID_VALUE;
1065 }
1066
1067 return SCARD_S_SUCCESS;
1068}
1069
1070
1071static LONG MSGCheckHandleAssociation(SCARDHANDLE hCard,
1072 SCONTEXT * threadContext)
1073{
1074 int list_index = 0;
1075
1076 if (0 == threadContext->hContext)
1077 {
1078 /* the handle is no more valid. After SCardReleaseContext() for
1079 * example */
1080 Log1(PCSC_LOG_CRITICAL, "Invalidated handle");
1081 return -1;
1082 }
1083
1084 (void)pthread_mutex_lock(&threadContext->cardsList_lock);
1085 list_index = list_locate(&threadContext->cardsList, &hCard);
1086 (void)pthread_mutex_unlock(&threadContext->cardsList_lock);
1087 if (list_index >= 0)
1088 return 0;
1089
1090 /* Must be a rogue client, debug log and sleep a couple of seconds */
1091 Log1(PCSC_LOG_ERROR, "Client failed to authenticate");
1092 (void)SYS_Sleep(2);
1093
1094 return -1;
1095}
1096
1097
1098/* Should be called just prior to exiting the thread as it de-allocates
1099 * the thread memory structures
1100 */
1101static void MSGCleanupClient(SCONTEXT * threadContext)
1102{
1103 int lrv;
1104 int listSize;
1105
1106 if (threadContext->hContext != 0)
1107 {
1108 (void)SCardReleaseContext(threadContext->hContext);
1109 (void)MSGRemoveContext(threadContext->hContext, threadContext);
1110 }
1111
1112 (void)pthread_mutex_lock(&threadContext->cardsList_lock);
1113 list_destroy(&threadContext->cardsList);
1114 (void)pthread_mutex_unlock(&threadContext->cardsList_lock);
1115
1116 Log3(PCSC_LOG_DEBUG,
1117 "Thread is stopping: dwClientID=%d, threadContext @%p",
1118 threadContext->dwClientID, threadContext);
1119
1120 /* Clear the struct to ensure that we detect
1121 * access to de-allocated memory
1122 * Hopefully the compiler won't optimise it out */
1123 memset((void*) threadContext, 0, sizeof(SCONTEXT));
1124 Log2(PCSC_LOG_DEBUG, "Freeing SCONTEXT @%p", threadContext);
1125
1126 (void)pthread_mutex_lock(&contextsList_lock);
1127 lrv = list_delete(&contextsList, threadContext);
1128 listSize = list_size(&contextsList);
1129 (void)pthread_mutex_unlock(&contextsList_lock);
1130 if (lrv < 0)
1131 Log2(PCSC_LOG_CRITICAL, "list_delete failed with error %x", lrv);
1132
1133 free(threadContext);
1134
1135 /* start a suicide alarm */
1136 if (AutoExit && (listSize < 1))
1137 {
1138 Log2(PCSC_LOG_DEBUG, "Starting suicide alarm in %d seconds",
1139 TIME_BEFORE_SUICIDE);
1140 alarm(TIME_BEFORE_SUICIDE);
1141 }
1142
1143 return;
1144}
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.
struct pubReaderStatesList READER_STATE
Define an exported public reader state structure so each application gets instant notification of cha...
#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.
_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
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_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.