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