pcsc-lite  2.0.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-2011
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 }
LONG EHUnregisterClientForEvent(int32_t filedes)
Unregister a client and log an error if the client is not found.
Definition: eventhandler.c:103
#define SCARD_E_INVALID_VALUE
One or more of the supplied parameters values could not be properly interpreted.
Definition: pcsclite.h:141
used by SCardBeginTransaction()
Definition: winscard_msg.h:85
contained in SCARD_CONNECT Messages.
Definition: winscard_msg.h:144
list object
Definition: simclist.h:181
wait for a reader state change
Definition: winscard_msg.h:97
contained in SCARD_CANCEL Messages.
Definition: winscard_msg.h:210
contained in SCARD_TRANSMIT Messages.
Definition: winscard_msg.h:232
#define SCARD_S_SUCCESS
No error was encountered.
Definition: pcsclite.h:107
contained in SCARD_END_TRANSACTION Messages.
Definition: winscard_msg.h:198
get the client/server protocol version
Definition: winscard_msg.h:95
LONG CreateContextThread(uint32_t *pdwClientID)
Creates threads to handle messages received from Clients.
Definition: winscard_svc.c:191
pthread_t pthThread
Event polling thread's ID.
Definition: winscard_svc.c:88
used by SCardEstablishContext()
Definition: winscard_msg.h:79
used by SCardEndTransaction()
Definition: winscard_msg.h:86
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:190
unsigned long cbPciLength
Protocol Control Inf Length.
Definition: pcsclite.h:82
#define SCARD_LEAVE_CARD
Do nothing on close.
Definition: pcsclite.h:253
#define SCARD_W_SECURITY_VIOLATION
Access was denied because of a security violation.
Definition: pcsclite.h:222
This handles abstract system level calls.
int SYS_Sleep(int)
Makes the current process sleep for some seconds.
Definition: sys_unix.c:62
used by SCardConnect()
Definition: winscard_msg.h:82
uint32_t dwClientID
Connection ID used to reference the Client.
Definition: winscard_svc.c:87
This demarshalls functions over the message queue and keeps track of clients and their handles...
#define PROTOCOL_VERSION_MAJOR
Major version of the current message protocol.
Definition: winscard_msg.h:50
contained in SCARD_DISCONNECT Messages.
Definition: winscard_msg.h:175
static list_t contextsList
Context tracking list.
Definition: winscard_svc.c:79
bool AutoExit
Represents an Application Context on the Server side.
Definition: pcscdaemon.c:76
PCSC_API LONG SCardGetAttrib(SCARDHANDLE hCard, DWORD dwAttrId, LPBYTE pbAttr, LPDWORD pcbAttrLen)
Get an attribute from the IFD Handler (reader driver).
Definition: winscard.c:1378
Information contained in SCARD_RELEASE_CONTEXT Messages.
Definition: winscard_msg.h:133
PCSC_API LONG SCardDisconnect(SCARDHANDLE hCard, DWORD dwDisposition)
Terminates a connection made through SCardConnect().
Definition: winscard.c:840
contained in SCARD_BEGIN_TRANSACTION Messages.
Definition: winscard_msg.h:187
Information contained in SCARD_ESTABLISH_CONTEXT Messages.
Definition: winscard_msg.h:121
_Atomic SCARDHANDLE hLockId
Lock Id.
get the readers state
Definition: winscard_msg.h:96
Information transmitted in CMD_VERSION Messages.
Definition: winscard_msg.h:57
header structure for client/server message data exchange.
Definition: winscard_msg.h:67
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:458
used by SCardReleaseContext()
Definition: winscard_msg.h:80
LONG SCARDCONTEXT
hContext returned by SCardEstablishContext()
Definition: pcsclite.h:52
#define SCARD_E_NO_MEMORY
Not enough memory available to complete this command.
Definition: pcsclite.h:119
contained in SCARD_STATUS Messages.
Definition: winscard_msg.h:221
#define SCARD_E_READER_UNAVAILABLE
The specified reader is not currently available for use.
Definition: pcsclite.h:153
contained in SCARD_RECONNECT Messages.
Definition: winscard_msg.h:160
unsigned long dwProtocol
Protocol identifier.
Definition: pcsclite.h:81
uint32_t timeOut
timeout in ms
Definition: winscard_msg.h:112
#define PCSCLITE_MAX_READERS_CONTEXTS
Maximum readers context (a slot is count as a reader)
Definition: pcsclite.h:285
contained in SCARD_GET_ATTRIB and Messages.
Definition: winscard_msg.h:264
This defines some structures and #defines to be used over the transport layer.
Information contained in CMD_WAIT_READER_STATE_CHANGE Messages.
Definition: winscard_msg.h:110
#define SCARD_W_RESET_CARD
The smart card has been reset, so any shared state information is invalid.
Definition: pcsclite.h:217
used by SCardReconnect()
Definition: winscard_msg.h:83
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:529
#define MAX_BUFFER_SIZE_EXTENDED
enhanced (64K + APDU + Lc + Le + SW) Tx/Rx Buffer
Definition: pcsclite.h:299
static READER_STATE readerStates[PCSCLITE_MAX_READERS_CONTEXTS]
Area used to read status information about the readers.
used by SCardTransmit()
Definition: winscard_msg.h:87
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
This handles card insertion/removal events, updates ATR, protocol, and status information.
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:229
stop waiting for a reader state change
Definition: winscard_msg.h:98
PCSC_API LONG SCardSetAttrib(SCARDHANDLE hCard, DWORD dwAttrId, LPCBYTE pbAttr, DWORD cbAttrLen)
Set an attribute of the IFD Handler.
Definition: winscard.c:1453
#define SCARD_W_REMOVED_CARD
The smart card has been removed, so further communication is not possible.
Definition: pcsclite.h:219
#define SCARD_PROTOCOL_UNDEFINED
protocol not set
Definition: pcsclite.h:240
#define SCARD_RESET_CARD
Reset on close.
Definition: pcsclite.h:254
LONG SCARDHANDLE
hCard returned by SCardConnect()
Definition: pcsclite.h:55
#define SCARD_E_INSUFFICIENT_BUFFER
The data buffer to receive returned data is too small for the returned data.
Definition: pcsclite.h:123
static const char * CommandsText[]
Handles messages received from Clients.
Definition: winscard_svc.c:303
#define SCARD_E_CANCELLED
The action was cancelled by an SCardCancel request.
Definition: pcsclite.h:111
#define PROTOCOL_VERSION_MINOR
Minor version of the current message protocol.
Definition: winscard_msg.h:52
PCSC_API LONG SCardBeginTransaction(SCARDHANDLE hCard)
Establishes a temporary exclusive access mode for doing a series of commands in a transaction...
Definition: winscard.c:1062
used by SCardControl()
Definition: winscard_msg.h:88
This keeps a list of defines for pcsc-lite.
Protocol Control Information (PCI)
Definition: pcsclite.h:79
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:1319
Define an exported public reader state structure so each application gets instant notification of cha...
Definition: eventhandler.h:52
used by SCardSetAttrib()
Definition: winscard_msg.h:94
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:358
used by SCardDisconnect()
Definition: winscard_msg.h:84
contained in SCARD_CONTROL Messages.
Definition: winscard_msg.h:249
This keeps track of a list of currently available reader structures.
used by SCardGetAttrib()
Definition: winscard_msg.h:93
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:1256
used by SCardCancel()
Definition: winscard_msg.h:91
#define SCARD_F_INTERNAL_ERROR
An internal consistency check failed.
Definition: pcsclite.h:109
PCSC_API LONG SCardEndTransaction(SCARDHANDLE hCard, DWORD dwDisposition)
Ends a previously begun transaction.
Definition: winscard.c:1104
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:1503
pthread_mutex_t cardsList_lock
lock for the above list
Definition: winscard_svc.c:86
#define SCARD_E_SERVICE_STOPPED
The Smart card resource manager has shut down.
Definition: pcsclite.h:167
pthread_mutex_t contextsList_lock
lock for the above list
Definition: winscard_svc.c:80
#define SCARD_E_INVALID_HANDLE
The supplied handle was invalid.
Definition: pcsclite.h:113
used by SCardStatus()
Definition: winscard_msg.h:89
This handles smart card reader communications.
PCSC_API LONG SCardReleaseContext(SCARDCONTEXT hContext)
Destroys a communication context to the PC/SC Resource Manager.
Definition: winscard.c:215
This handles debugging.