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