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