pcsc-lite  2.2.3
winscard_svc.c
Go to the documentation of this file.
1 /*
2  * MUSCLE SmartCard Development ( https://pcsclite.apdu.fr/ )
3  *
4  * Copyright (C) 2001-2004
5  * David Corcoran <corcoran@musclecard.com>
6  * Copyright (C) 2003-2004
7  * Damien Sauveron <damien.sauveron@labri.fr>
8  * Copyright (C) 2002-2023
9  * Ludovic Rousseau <ludovic.rousseau@free.fr>
10  * Copyright (C) 2009
11  * Jean-Luc Giraud <jlgiraud@googlemail.com>
12  *
13 Redistribution and use in source and binary forms, with or without
14 modification, are permitted provided that the following conditions
15 are met:
16 
17 1. Redistributions of source code must retain the above copyright
18  notice, this list of conditions and the following disclaimer.
19 2. 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.
22 3. 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 
25 THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
26 IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
27 OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
28 IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
29 INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
30 NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
31 DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
32 THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
33 (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
34 THIS 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 
75 extern bool AutoExit;
76 static int contextMaxThreadCounter = PCSC_MAX_CONTEXT_THREADS;
77 static int contextMaxCardHandles = PCSC_MAX_CONTEXT_CARD_HANDLES;
78 
80 pthread_mutex_t contextsList_lock;
82 struct _psContext
83 {
84  int32_t hContext;
85  list_t cardsList;
86  pthread_mutex_t cardsList_lock;
87  uint32_t dwClientID;
88  pthread_t pthThread;
89 };
90 typedef struct _psContext SCONTEXT;
91 
92 static LONG MSGCheckHandleAssociation(SCARDHANDLE, SCONTEXT *);
93 static LONG MSGAddContext(SCARDCONTEXT, SCONTEXT *);
94 static LONG MSGRemoveContext(SCARDCONTEXT, SCONTEXT *);
95 static LONG MSGAddHandle(SCARDCONTEXT, SCARDHANDLE, SCONTEXT *);
96 static LONG MSGRemoveHandle(SCARDHANDLE, SCONTEXT *);
97 static void MSGCleanupClient(SCONTEXT *);
98 
99 static void * ContextThread(LPVOID pdwIndex);
100 
102 
103 static 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 
119 LONG 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 
149 void 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 
191 LONG 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 
276 out:
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
303 static 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 
348 static 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 
520  coStr.rv = SCARD_W_SECURITY_VIOLATION;
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. */
711  trStr.rv = SCARD_E_INSUFFICIENT_BUFFER;
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. */
764  ctStr.rv = SCARD_E_INSUFFICIENT_BUFFER;
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 
835 buffer_overflow:
836  Log2(PCSC_LOG_DEBUG, "Buffer overflow detected: %d", filedes);
837  goto exit;
838 wrong_length:
839  Log2(PCSC_LOG_DEBUG, "Wrong length: %d", filedes);
840 exit:
841  (void)close(filedes);
842  MSGCleanupClient(threadContext);
843  (void)pthread_exit((LPVOID) NULL);
844 }
845 
846 LONG 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 
863 LONG 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 
875 static LONG MSGAddContext(SCARDCONTEXT hContext, SCONTEXT * threadContext)
876 {
877  threadContext->hContext = hContext;
878  return SCARD_S_SUCCESS;
879 }
880 
881 static 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");
889  return SCARD_E_INVALID_HANDLE;
890  }
891 
892  if (threadContext->hContext != hContext)
893  return SCARD_E_INVALID_VALUE;
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 
983 static 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");
991  return SCARD_E_INVALID_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. */
1034 static 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 
1051 static 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  */
1081 static 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 }
EHUnregisterClientForEvent
LONG EHUnregisterClientForEvent(int32_t filedes)
Unregister a client and log an error if the client is not found.
Definition: eventhandler.c:103
debuglog.h
This handles debugging.
SCARD_RESET_CARD
#define SCARD_RESET_CARD
Reset on close.
Definition: pcsclite.h:254
establish_struct
Information contained in SCARD_ESTABLISH_CONTEXT Messages.
Definition: winscard_msg.h:122
wait_reader_state_change
Information contained in CMD_WAIT_READER_STATE_CHANGE Messages.
Definition: winscard_msg.h:111
CreateContextThread
LONG CreateContextThread(uint32_t *pdwClientID)
Creates threads to handle messages received from Clients.
Definition: winscard_svc.c:191
SCARD_S_SUCCESS
#define SCARD_S_SUCCESS
No error was encountered.
Definition: pcsclite.h:107
SCARD_PROTOCOL_UNDEFINED
#define SCARD_PROTOCOL_UNDEFINED
protocol not set
Definition: pcsclite.h:240
pubReaderStatesList
Define an exported public reader state structure so each application gets instant notification of cha...
Definition: eventhandler.h:53
SCARD_W_REMOVED_CARD
#define SCARD_W_REMOVED_CARD
The smart card has been removed, so further communication is not possible.
Definition: pcsclite.h:219
PROTOCOL_VERSION_MAJOR
#define PROTOCOL_VERSION_MAJOR
Major version of the current message protocol.
Definition: winscard_msg.h:50
disconnect_struct
contained in SCARD_DISCONNECT Messages.
Definition: winscard_msg.h:176
SCARD_CONTROL
@ SCARD_CONTROL
used by SCardControl()
Definition: winscard_msg.h:88
SCARD_IO_REQUEST::cbPciLength
unsigned long cbPciLength
Protocol Control Inf Length.
Definition: pcsclite.h:82
CMD_STOP_WAITING_READER_STATE_CHANGE
@ CMD_STOP_WAITING_READER_STATE_CHANGE
stop waiting for a reader state change
Definition: winscard_msg.h:98
SCARD_CONNECT
@ SCARD_CONNECT
used by SCardConnect()
Definition: winscard_msg.h:82
sys_generic.h
This handles abstract system level calls.
readerfactory.h
This keeps track of a list of currently available reader structures.
rxHeader
header structure for client/server message data exchange.
Definition: winscard_msg.h:68
SYS_Sleep
int SYS_Sleep(int)
Makes the current process sleep for some seconds.
Definition: sys_unix.c:62
eventhandler.h
This handles card insertion/removal events, updates ATR, protocol, and status information.
winscard_svc.h
This demarshalls functions over the message queue and keeps track of clients and their handles.
cancel_struct
contained in SCARD_CANCEL Messages.
Definition: winscard_msg.h:211
SCARD_E_READER_UNAVAILABLE
#define SCARD_E_READER_UNAVAILABLE
The specified reader is not currently available for use.
Definition: pcsclite.h:153
wait_reader_state_change::timeOut
uint32_t timeOut
timeout in ms
Definition: winscard_msg.h:112
SCARD_CANCEL
@ SCARD_CANCEL
used by SCardCancel()
Definition: winscard_msg.h:91
SCARDHANDLE
LONG SCARDHANDLE
hCard returned by SCardConnect()
Definition: pcsclite.h:55
SCARD_W_SECURITY_VIOLATION
#define SCARD_W_SECURITY_VIOLATION
Access was denied because of a security violation.
Definition: pcsclite.h:222
contextsList
static list_t contextsList
Context tracking list.
Definition: winscard_svc.c:79
MAX_BUFFER_SIZE_EXTENDED
#define MAX_BUFFER_SIZE_EXTENDED
enhanced (64K + APDU + Lc + Le + SW) Tx/Rx Buffer
Definition: pcsclite.h:299
SCARD_E_CANCELLED
#define SCARD_E_CANCELLED
The action was cancelled by an SCardCancel request.
Definition: pcsclite.h:111
list_t
list object
Definition: simclist.h:181
control_struct
contained in SCARD_CONTROL Messages.
Definition: winscard_msg.h:250
connect_struct
contained in SCARD_CONNECT Messages.
Definition: winscard_msg.h:145
CMD_WAIT_READER_STATE_CHANGE
@ CMD_WAIT_READER_STATE_CHANGE
wait for a reader state change
Definition: winscard_msg.h:97
status_struct
contained in SCARD_STATUS Messages.
Definition: winscard_msg.h:222
getset_struct
contained in SCARD_GET_ATTRIB and Messages.
Definition: winscard_msg.h:265
SCARD_E_INSUFFICIENT_BUFFER
#define SCARD_E_INSUFFICIENT_BUFFER
The data buffer to receive returned data is too small for the returned data.
Definition: pcsclite.h:123
ReaderContext
Definition: readerfactory.h:107
SCARD_GET_ATTRIB
@ SCARD_GET_ATTRIB
used by SCardGetAttrib()
Definition: winscard_msg.h:93
AutoExit
bool AutoExit
Represents an Application Context on the Server side.
Definition: pcscdaemon.c:74
SCARD_END_TRANSACTION
@ SCARD_END_TRANSACTION
used by SCardEndTransaction()
Definition: winscard_msg.h:86
_psContext::cardsList_lock
pthread_mutex_t cardsList_lock
lock for the above list
Definition: winscard_svc.c:86
SCARD_W_RESET_CARD
#define SCARD_W_RESET_CARD
The smart card has been reset, so any shared state information is invalid.
Definition: pcsclite.h:217
SCARD_RECONNECT
@ SCARD_RECONNECT
used by SCardReconnect()
Definition: winscard_msg.h:83
begin_struct
contained in SCARD_BEGIN_TRANSACTION Messages.
Definition: winscard_msg.h:188
version_struct
Information transmitted in CMD_VERSION Messages.
Definition: winscard_msg.h:58
CMD_GET_READERS_STATE
@ CMD_GET_READERS_STATE
get the readers state
Definition: winscard_msg.h:96
end_struct
contained in SCARD_END_TRANSACTION Messages.
Definition: winscard_msg.h:199
winscard_msg.h
This defines some structures and #defines to be used over the transport layer.
SCARD_ESTABLISH_CONTEXT
@ SCARD_ESTABLISH_CONTEXT
used by SCardEstablishContext()
Definition: winscard_msg.h:79
SCARD_SET_ATTRIB
@ SCARD_SET_ATTRIB
used by SCardSetAttrib()
Definition: winscard_msg.h:94
SCARD_E_NO_MEMORY
#define SCARD_E_NO_MEMORY
Not enough memory available to complete this command.
Definition: pcsclite.h:119
PCSCLITE_MAX_READERS_CONTEXTS
#define PCSCLITE_MAX_READERS_CONTEXTS
Maximum readers context (a slot is count as a reader)
Definition: pcsclite.h:285
MessageReceive
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.
Definition: winscard_msg.c:455
_psContext::dwClientID
uint32_t dwClientID
Connection ID used to reference the Client.
Definition: winscard_svc.c:87
SCARD_IO_REQUEST::dwProtocol
unsigned long dwProtocol
Protocol identifier.
Definition: pcsclite.h:81
winscard.h
This handles smart card reader communications.
MessageSend
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:355
SCARDCONTEXT
LONG SCARDCONTEXT
hContext returned by SCardEstablishContext()
Definition: pcsclite.h:52
SCARD_LEAVE_CARD
#define SCARD_LEAVE_CARD
Do nothing on close.
Definition: pcsclite.h:253
SCARD_BEGIN_TRANSACTION
@ SCARD_BEGIN_TRANSACTION
used by SCardBeginTransaction()
Definition: winscard_msg.h:85
_psContext
Definition: winscard_svc.c:83
PROTOCOL_VERSION_MINOR
#define PROTOCOL_VERSION_MINOR
Minor version of the current message protocol.
Definition: winscard_msg.h:52
reconnect_struct
contained in SCARD_RECONNECT Messages.
Definition: winscard_msg.h:161
contextsList_lock
pthread_mutex_t contextsList_lock
lock for the above list
Definition: winscard_svc.c:80
SCARD_IO_REQUEST
Protocol Control Information (PCI)
Definition: pcsclite.h:80
SCARD_E_INVALID_VALUE
#define SCARD_E_INVALID_VALUE
One or more of the supplied parameters values could not be properly interpreted.
Definition: pcsclite.h:141
SCARD_RELEASE_CONTEXT
@ SCARD_RELEASE_CONTEXT
used by SCardReleaseContext()
Definition: winscard_msg.h:80
CommandsText
static const char * CommandsText[]
Handles messages received from Clients.
Definition: winscard_svc.c:303
SCARD_TRANSMIT
@ SCARD_TRANSMIT
used by SCardTransmit()
Definition: winscard_msg.h:87
SCARD_DISCONNECT
@ SCARD_DISCONNECT
used by SCardDisconnect()
Definition: winscard_msg.h:84
SCARD_STATUS
@ SCARD_STATUS
used by SCardStatus()
Definition: winscard_msg.h:89
SCARD_E_SERVICE_STOPPED
#define SCARD_E_SERVICE_STOPPED
The Smart card resource manager has shut down.
Definition: pcsclite.h:167
SCARD_F_INTERNAL_ERROR
#define SCARD_F_INTERNAL_ERROR
An internal consistency check failed.
Definition: pcsclite.h:109
SCARD_E_INVALID_HANDLE
#define SCARD_E_INVALID_HANDLE
The supplied handle was invalid.
Definition: pcsclite.h:113
transmit_struct
contained in SCARD_TRANSMIT Messages.
Definition: winscard_msg.h:233
_psContext::pthThread
pthread_t pthThread
Event polling thread's ID.
Definition: winscard_svc.c:88
CMD_VERSION
@ CMD_VERSION
get the client/server protocol version
Definition: winscard_msg.h:95
EHTryToUnregisterClientForEvent
LONG EHTryToUnregisterClientForEvent(int32_t filedes)
Try to unregister a client If no client is found then do not log an error.
Definition: eventhandler.c:83
ReaderContext::hLockId
_Atomic SCARDHANDLE hLockId
Lock Id.
Definition: readerfactory.h:126
readerStates
static READER_STATE readerStates[PCSCLITE_MAX_READERS_CONTEXTS]
Area used to read status information about the readers.
Definition: winscard_clnt.c:358
release_struct
Information contained in SCARD_RELEASE_CONTEXT Messages.
Definition: winscard_msg.h:134