pcsc-lite  1.9.8
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 
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 
74 extern char AutoExit;
75 static int contextMaxThreadCounter = PCSC_MAX_CONTEXT_THREADS;
76 static int contextMaxCardHandles = PCSC_MAX_CONTEXT_CARD_HANDLES;
77 
79 pthread_mutex_t contextsList_lock;
81 struct _psContext
82 {
83  int32_t hContext;
84  list_t cardsList;
85  pthread_mutex_t cardsList_lock;
86  uint32_t dwClientID;
87  pthread_t pthThread;
88 };
89 typedef struct _psContext SCONTEXT;
90 
91 static LONG MSGCheckHandleAssociation(SCARDHANDLE, SCONTEXT *);
92 static LONG MSGAddContext(SCARDCONTEXT, SCONTEXT *);
93 static LONG MSGRemoveContext(SCARDCONTEXT, SCONTEXT *);
94 static LONG MSGAddHandle(SCARDCONTEXT, SCARDHANDLE, SCONTEXT *);
95 static LONG MSGRemoveHandle(SCARDHANDLE, SCONTEXT *);
96 static void MSGCleanupClient(SCONTEXT *);
97 
98 static void * ContextThread(LPVOID pdwIndex);
99 
101 
102 static 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 
118 LONG 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 
148 void 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 
171 LONG 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 
256 out:
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
283 static 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 
328 static 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  LONG rv;
436 
437  /* remove the client fd from the list */
438  rv = EHUnregisterClientForEvent(filedes);
439 
440  /* send the response only if the client was still in the
441  * list */
442  if (rv != SCARD_F_INTERNAL_ERROR)
443  {
444  waStr.rv = rv;
445  WRITE_BODY(waStr);
446  }
447  }
448  break;
449 
451  {
452  struct establish_struct esStr;
453  SCARDCONTEXT hContext;
454 
455  READ_BODY(esStr);
456 
457  hContext = esStr.hContext;
458  esStr.rv = SCardEstablishContext(esStr.dwScope, 0, 0,
459  &hContext);
460  esStr.hContext = hContext;
461 
462  if (esStr.rv == SCARD_S_SUCCESS)
463  esStr.rv = MSGAddContext(esStr.hContext, threadContext);
464 
465  WRITE_BODY(esStr);
466  }
467  break;
468 
470  {
471  struct release_struct reStr;
472 
473  READ_BODY(reStr);
474 
475  reStr.rv = SCardReleaseContext(reStr.hContext);
476 
477  if (reStr.rv == SCARD_S_SUCCESS)
478  reStr.rv = MSGRemoveContext(reStr.hContext, threadContext);
479 
480  WRITE_BODY(reStr);
481  }
482  break;
483 
484  case SCARD_CONNECT:
485  {
486  struct connect_struct coStr;
487  SCARDHANDLE hCard;
488  DWORD dwActiveProtocol;
489 
490  READ_BODY(coStr);
491 
492  coStr.szReader[sizeof(coStr.szReader)-1] = 0;
493  hCard = coStr.hCard;
494  dwActiveProtocol = coStr.dwActiveProtocol;
495 
496  if (IsClientAuthorized(filedes, "access_card", coStr.szReader) == 0)
497  {
498  Log2(PCSC_LOG_CRITICAL, "Rejected unauthorized client for '%s'", coStr.szReader);
499  goto exit;
500  }
501  else
502  {
503  Log2(PCSC_LOG_DEBUG, "Authorized client for '%s'", coStr.szReader);
504  }
505 
506  coStr.rv = SCardConnect(coStr.hContext, coStr.szReader,
507  coStr.dwShareMode, coStr.dwPreferredProtocols,
508  &hCard, &dwActiveProtocol);
509 
510  coStr.hCard = hCard;
511  coStr.dwActiveProtocol = dwActiveProtocol;
512 
513  if (coStr.rv == SCARD_S_SUCCESS)
514  {
515  coStr.rv = MSGAddHandle(coStr.hContext, coStr.hCard,
516  threadContext);
517 
518  /* if storing the hCard fails we disconnect */
519  if (coStr.rv != SCARD_S_SUCCESS)
520  SCardDisconnect(coStr.hCard, SCARD_LEAVE_CARD);
521  }
522 
523  WRITE_BODY(coStr);
524  }
525  break;
526 
527  case SCARD_RECONNECT:
528  {
529  struct reconnect_struct rcStr;
530  DWORD dwActiveProtocol = SCARD_PROTOCOL_UNDEFINED;
531 
532  READ_BODY(rcStr);
533 
534  if (MSGCheckHandleAssociation(rcStr.hCard, threadContext))
535  goto exit;
536 
537  rcStr.rv = SCardReconnect(rcStr.hCard, rcStr.dwShareMode,
538  rcStr.dwPreferredProtocols, rcStr.dwInitialization,
539  &dwActiveProtocol);
540  rcStr.dwActiveProtocol = dwActiveProtocol;
541 
542  WRITE_BODY(rcStr);
543  }
544  break;
545 
546  case SCARD_DISCONNECT:
547  {
548  struct disconnect_struct diStr;
549 
550  READ_BODY(diStr);
551 
552  if (MSGCheckHandleAssociation(diStr.hCard, threadContext))
553  goto exit;
554 
555  diStr.rv = SCardDisconnect(diStr.hCard, diStr.dwDisposition);
556 
557  if (SCARD_S_SUCCESS == diStr.rv)
558  diStr.rv = MSGRemoveHandle(diStr.hCard, threadContext);
559 
560  WRITE_BODY(diStr);
561  }
562  break;
563 
565  {
566  struct begin_struct beStr;
567 
568  READ_BODY(beStr);
569 
570  if (MSGCheckHandleAssociation(beStr.hCard, threadContext))
571  goto exit;
572 
573  beStr.rv = SCardBeginTransaction(beStr.hCard);
574 
575  WRITE_BODY(beStr);
576  }
577  break;
578 
580  {
581  struct end_struct enStr;
582 
583  READ_BODY(enStr);
584 
585  if (MSGCheckHandleAssociation(enStr.hCard, threadContext))
586  goto exit;
587 
588  enStr.rv = SCardEndTransaction(enStr.hCard,
589  enStr.dwDisposition);
590 
591  WRITE_BODY(enStr);
592  }
593  break;
594 
595  case SCARD_CANCEL:
596  {
597  struct cancel_struct caStr;
598  SCONTEXT * psTargetContext = NULL;
599 
600  READ_BODY(caStr);
601 
602  /* find the client */
603  (void)pthread_mutex_lock(&contextsList_lock);
604  psTargetContext = (SCONTEXT *) list_seek(&contextsList,
605  &caStr.hContext);
606  (void)pthread_mutex_unlock(&contextsList_lock);
607 
608  /* default value = error */
609  caStr.rv = SCARD_E_INVALID_HANDLE;
610 
611  if (psTargetContext != NULL)
612  {
613  uint32_t fd = psTargetContext->dwClientID;
614  LONG rv;
615 
616  /* the client should not receive the event
617  * notification now the waiting has been cancelled */
619 
620  /* signal the client only if it was still waiting */
621  if (SCARD_S_SUCCESS == rv)
622  caStr.rv = MSGSignalClient(fd, SCARD_E_CANCELLED);
623  }
624 
625  WRITE_BODY(caStr);
626  }
627  break;
628 
629  case SCARD_STATUS:
630  {
631  struct status_struct stStr;
632 
633  READ_BODY(stStr);
634 
635  if (MSGCheckHandleAssociation(stStr.hCard, threadContext))
636  goto exit;
637 
638  /* only hCard and return value are used by the client */
639  stStr.rv = SCardStatus(stStr.hCard, NULL, NULL, NULL,
640  NULL, 0, NULL);
641 
642  WRITE_BODY(stStr);
643  }
644  break;
645 
646  case SCARD_TRANSMIT:
647  {
648  struct transmit_struct trStr;
649  unsigned char pbSendBuffer[MAX_BUFFER_SIZE_EXTENDED];
650  unsigned char pbRecvBuffer[MAX_BUFFER_SIZE_EXTENDED];
651  SCARD_IO_REQUEST ioSendPci;
652  SCARD_IO_REQUEST ioRecvPci;
653  DWORD cbRecvLength;
654 
655  READ_BODY(trStr);
656 
657  if (MSGCheckHandleAssociation(trStr.hCard, threadContext))
658  goto exit;
659 
660  /* avoids buffer overflow */
661  if (trStr.cbSendLength > sizeof(pbSendBuffer))
662  goto buffer_overflow;
663 
664  /* read sent buffer */
665  ret = MessageReceive(pbSendBuffer, trStr.cbSendLength, filedes);
666  if (ret != SCARD_S_SUCCESS)
667  {
668  Log2(PCSC_LOG_DEBUG, "Client die: %d", filedes);
669  goto exit;
670  }
671 
672  ioSendPci.dwProtocol = trStr.ioSendPciProtocol;
673  ioSendPci.cbPciLength = trStr.ioSendPciLength;
674  ioRecvPci.dwProtocol = trStr.ioRecvPciProtocol;
675  ioRecvPci.cbPciLength = trStr.ioRecvPciLength;
676  cbRecvLength = sizeof pbRecvBuffer;
677 
678  trStr.rv = SCardTransmit(trStr.hCard, &ioSendPci,
679  pbSendBuffer, trStr.cbSendLength, &ioRecvPci,
680  pbRecvBuffer, &cbRecvLength);
681 
682  if (cbRecvLength > trStr.pcbRecvLength)
683  /* The client buffer is not large enough.
684  * The pbRecvBuffer buffer will NOT be sent a few
685  * lines bellow. So no buffer overflow is expected. */
686  trStr.rv = SCARD_E_INSUFFICIENT_BUFFER;
687 
688  trStr.ioSendPciProtocol = ioSendPci.dwProtocol;
689  trStr.ioSendPciLength = ioSendPci.cbPciLength;
690  trStr.ioRecvPciProtocol = ioRecvPci.dwProtocol;
691  trStr.ioRecvPciLength = ioRecvPci.cbPciLength;
692  trStr.pcbRecvLength = cbRecvLength;
693 
694  WRITE_BODY(trStr);
695 
696  /* write received buffer */
697  if (SCARD_S_SUCCESS == trStr.rv)
698  ret = MessageSend(pbRecvBuffer, cbRecvLength, filedes);
699  }
700  break;
701 
702  case SCARD_CONTROL:
703  {
704  struct control_struct ctStr;
705  unsigned char pbSendBuffer[MAX_BUFFER_SIZE_EXTENDED];
706  unsigned char pbRecvBuffer[MAX_BUFFER_SIZE_EXTENDED];
707  DWORD dwBytesReturned;
708 
709  READ_BODY(ctStr);
710 
711  if (MSGCheckHandleAssociation(ctStr.hCard, threadContext))
712  goto exit;
713 
714  /* avoids buffer overflow */
715  if (ctStr.cbSendLength > sizeof(pbSendBuffer))
716  {
717  goto buffer_overflow;
718  }
719 
720  /* read sent buffer */
721  ret = MessageReceive(pbSendBuffer, ctStr.cbSendLength, filedes);
722  if (ret != SCARD_S_SUCCESS)
723  {
724  Log2(PCSC_LOG_DEBUG, "Client die: %d", filedes);
725  goto exit;
726  }
727 
728  dwBytesReturned = ctStr.dwBytesReturned;
729 
730  ctStr.rv = SCardControl(ctStr.hCard, ctStr.dwControlCode,
731  pbSendBuffer, ctStr.cbSendLength,
732  pbRecvBuffer, sizeof pbRecvBuffer,
733  &dwBytesReturned);
734 
735  if (dwBytesReturned > ctStr.cbRecvLength)
736  /* The client buffer is not large enough.
737  * The pbRecvBuffer buffer will NOT be sent a few
738  * lines bellow. So no buffer overflow is expected. */
739  ctStr.rv = SCARD_E_INSUFFICIENT_BUFFER;
740 
741  ctStr.dwBytesReturned = dwBytesReturned;
742 
743  WRITE_BODY(ctStr);
744 
745  /* write received buffer */
746  if (SCARD_S_SUCCESS == ctStr.rv)
747  ret = MessageSend(pbRecvBuffer, dwBytesReturned, filedes);
748  }
749  break;
750 
751  case SCARD_GET_ATTRIB:
752  {
753  struct getset_struct gsStr;
754  DWORD cbAttrLen;
755 
756  READ_BODY(gsStr);
757 
758  if (MSGCheckHandleAssociation(gsStr.hCard, threadContext))
759  goto exit;
760 
761  /* avoids buffer overflow */
762  if (gsStr.cbAttrLen > sizeof(gsStr.pbAttr))
763  goto buffer_overflow;
764 
765  cbAttrLen = gsStr.cbAttrLen;
766 
767  gsStr.rv = SCardGetAttrib(gsStr.hCard, gsStr.dwAttrId,
768  gsStr.pbAttr, &cbAttrLen);
769 
770  gsStr.cbAttrLen = cbAttrLen;
771 
772  WRITE_BODY(gsStr);
773  }
774  break;
775 
776  case SCARD_SET_ATTRIB:
777  {
778  struct getset_struct gsStr;
779 
780  READ_BODY(gsStr);
781 
782  if (MSGCheckHandleAssociation(gsStr.hCard, threadContext))
783  goto exit;
784 
785  /* avoids buffer overflow */
786  if (gsStr.cbAttrLen > sizeof(gsStr.pbAttr))
787  goto buffer_overflow;
788 
789  gsStr.rv = SCardSetAttrib(gsStr.hCard, gsStr.dwAttrId,
790  gsStr.pbAttr, gsStr.cbAttrLen);
791 
792  WRITE_BODY(gsStr);
793  }
794  break;
795 
796  default:
797  Log2(PCSC_LOG_CRITICAL, "Unknown command: %d", header.command);
798  goto exit;
799  }
800 
801  /* MessageSend() failed */
802  if (ret != SCARD_S_SUCCESS)
803  {
804  /* Clean up the dead client */
805  Log2(PCSC_LOG_DEBUG, "Client die: %d", filedes);
806  goto exit;
807  }
808  }
809 
810 buffer_overflow:
811  Log2(PCSC_LOG_DEBUG, "Buffer overflow detected: %d", filedes);
812  goto exit;
813 wrong_length:
814  Log2(PCSC_LOG_DEBUG, "Wrong length: %d", filedes);
815 exit:
816  (void)close(filedes);
817  MSGCleanupClient(threadContext);
818  (void)pthread_exit((LPVOID) NULL);
819 }
820 
821 LONG MSGSignalClient(uint32_t filedes, LONG rv)
822 {
823  uint32_t ret;
824  struct wait_reader_state_change waStr =
825  {
826  .timeOut = 0,
827  .rv = 0
828  };
829 
830  Log2(PCSC_LOG_DEBUG, "Signal client: %d", filedes);
831 
832  waStr.rv = rv;
833  WRITE_BODY_WITH_COMMAND("SIGNAL", waStr);
834 
835  return ret;
836 } /* MSGSignalClient */
837 
838 LONG MSGSendReaderStates(uint32_t filedes)
839 {
840  uint32_t ret;
841 
842  Log2(PCSC_LOG_DEBUG, "Send reader states: %d", filedes);
843 
844  /* dump the readers state */
845  ret = MessageSend(readerStates, sizeof(readerStates), filedes);
846 
847  return ret;
848 }
849 
850 static LONG MSGAddContext(SCARDCONTEXT hContext, SCONTEXT * threadContext)
851 {
852  threadContext->hContext = hContext;
853  return SCARD_S_SUCCESS;
854 }
855 
856 static LONG MSGRemoveContext(SCARDCONTEXT hContext, SCONTEXT * threadContext)
857 {
858  LONG rv;
859  int lrv;
860 
861  if (0 == threadContext->hContext)
862  {
863  Log1(PCSC_LOG_ERROR, "Invalidated handle");
864  return SCARD_E_INVALID_HANDLE;
865  }
866 
867  if (threadContext->hContext != hContext)
868  return SCARD_E_INVALID_VALUE;
869 
870  (void)pthread_mutex_lock(&threadContext->cardsList_lock);
871  while (list_size(&threadContext->cardsList) != 0)
872  {
873  READER_CONTEXT * rContext = NULL;
874  SCARDHANDLE hCard;
875  void *ptr;
876 
877  /*
878  * Disconnect each of these just in case
879  */
880  ptr = list_get_at(&threadContext->cardsList, 0);
881  if (NULL == ptr)
882  {
883  Log1(PCSC_LOG_CRITICAL, "list_get_at failed");
884  continue;
885  }
886  hCard = *(int32_t *)ptr;
887 
888  /*
889  * Unlock the sharing
890  */
891  rv = RFReaderInfoById(hCard, &rContext);
892  if (rv != SCARD_S_SUCCESS)
893  {
894  (void)pthread_mutex_unlock(&threadContext->cardsList_lock);
895  return rv;
896  }
897 
898  if (0 == rContext->hLockId)
899  {
900  /* no lock. Just leave the card */
901  (void)SCardDisconnect(hCard, SCARD_LEAVE_CARD);
902  }
903  else
904  {
905  if (hCard != rContext->hLockId)
906  {
907  /*
908  * if the card is locked by someone else we do not reset it
909  */
910 
911  /* decrement card use */
912  (void)SCardDisconnect(hCard, SCARD_LEAVE_CARD);
913  }
914  else
915  {
916  /* release the lock */
917  rContext->hLockId = 0;
918 
919  /*
920  * We will use SCardStatus to see if the card has been
921  * reset there is no need to reset each time
922  * Disconnect is called
923  */
924  rv = SCardStatus(hCard, NULL, NULL, NULL, NULL, NULL, NULL);
925 
926  if (rv == SCARD_W_RESET_CARD || rv == SCARD_W_REMOVED_CARD)
927  (void)SCardDisconnect(hCard, SCARD_LEAVE_CARD);
928  else
929  (void)SCardDisconnect(hCard, SCARD_RESET_CARD);
930  }
931  }
932 
933  /* Remove entry from the list */
934  lrv = list_delete_at(&threadContext->cardsList, 0);
935  if (lrv < 0)
936  Log2(PCSC_LOG_CRITICAL,
937  "list_delete_at failed with return value: %d", lrv);
938 
939  UNREF_READER(rContext)
940  }
941  (void)pthread_mutex_unlock(&threadContext->cardsList_lock);
942 
943  /* We only mark the context as no longer in use.
944  * The memory is freed in MSGCleanupCLient() */
945  threadContext->hContext = 0;
946 
947  return SCARD_S_SUCCESS;
948 }
949 
950 static LONG MSGAddHandle(SCARDCONTEXT hContext, SCARDHANDLE hCard,
951  SCONTEXT * threadContext)
952 {
953  LONG retval = SCARD_E_INVALID_VALUE;
954 
955  if (0 == threadContext->hContext)
956  {
957  Log1(PCSC_LOG_ERROR, "Invalidated handle");
958  return SCARD_E_INVALID_HANDLE;
959  }
960 
961  if (threadContext->hContext == hContext)
962  {
963  /*
964  * Find an empty spot to put the hCard value
965  */
966  int listLength;
967 
968  (void)pthread_mutex_lock(&threadContext->cardsList_lock);
969 
970  listLength = list_size(&threadContext->cardsList);
971  if (listLength >= contextMaxCardHandles)
972  {
973  Log4(PCSC_LOG_DEBUG,
974  "Too many card handles for thread context @%p: %d (max is %d). "
975  "Restart pcscd with --max-card-handle-per-thread value",
976  threadContext, listLength, contextMaxCardHandles);
977  retval = SCARD_E_NO_MEMORY;
978  }
979  else
980  {
981  int lrv;
982 
983  lrv = list_append(&threadContext->cardsList, &hCard);
984  if (lrv < 0)
985  {
986  Log2(PCSC_LOG_CRITICAL,
987  "list_append failed with return value: %d", lrv);
988  retval = SCARD_E_NO_MEMORY;
989  }
990  else
991  retval = SCARD_S_SUCCESS;
992  }
993 
994  (void)pthread_mutex_unlock(&threadContext->cardsList_lock);
995  }
996 
997  return retval;
998 }
999 
1000 /* Pre-condition: MSGCheckHandleAssociation must succeed. */
1001 static LONG MSGRemoveHandle(SCARDHANDLE hCard, SCONTEXT * threadContext)
1002 {
1003  int lrv;
1004 
1005  (void)pthread_mutex_lock(&threadContext->cardsList_lock);
1006  lrv = list_delete(&threadContext->cardsList, &hCard);
1007  (void)pthread_mutex_unlock(&threadContext->cardsList_lock);
1008  if (lrv < 0)
1009  {
1010  Log2(PCSC_LOG_CRITICAL, "list_delete failed with error %d", lrv);
1011  return SCARD_E_INVALID_VALUE;
1012  }
1013 
1014  return SCARD_S_SUCCESS;
1015 }
1016 
1017 
1018 static LONG MSGCheckHandleAssociation(SCARDHANDLE hCard,
1019  SCONTEXT * threadContext)
1020 {
1021  int list_index = 0;
1022 
1023  if (0 == threadContext->hContext)
1024  {
1025  /* the handle is no more valid. After SCardReleaseContext() for
1026  * example */
1027  Log1(PCSC_LOG_CRITICAL, "Invalidated handle");
1028  return -1;
1029  }
1030 
1031  (void)pthread_mutex_lock(&threadContext->cardsList_lock);
1032  list_index = list_locate(&threadContext->cardsList, &hCard);
1033  (void)pthread_mutex_unlock(&threadContext->cardsList_lock);
1034  if (list_index >= 0)
1035  return 0;
1036 
1037  /* Must be a rogue client, debug log and sleep a couple of seconds */
1038  Log1(PCSC_LOG_ERROR, "Client failed to authenticate");
1039  (void)SYS_Sleep(2);
1040 
1041  return -1;
1042 }
1043 
1044 
1045 /* Should be called just prior to exiting the thread as it de-allocates
1046  * the thread memory strucutres
1047  */
1048 static void MSGCleanupClient(SCONTEXT * threadContext)
1049 {
1050  int lrv;
1051  int listSize;
1052 
1053  if (threadContext->hContext != 0)
1054  {
1055  (void)SCardReleaseContext(threadContext->hContext);
1056  (void)MSGRemoveContext(threadContext->hContext, threadContext);
1057  }
1058 
1059  (void)pthread_mutex_lock(&threadContext->cardsList_lock);
1060  list_destroy(&threadContext->cardsList);
1061  (void)pthread_mutex_unlock(&threadContext->cardsList_lock);
1062 
1063  Log3(PCSC_LOG_DEBUG,
1064  "Thread is stopping: dwClientID=%d, threadContext @%p",
1065  threadContext->dwClientID, threadContext);
1066 
1067  /* Clear the struct to ensure that we detect
1068  * access to de-allocated memory
1069  * Hopefully the compiler won't optimise it out */
1070  memset((void*) threadContext, 0, sizeof(SCONTEXT));
1071  Log2(PCSC_LOG_DEBUG, "Freeing SCONTEXT @%p", threadContext);
1072 
1073  (void)pthread_mutex_lock(&contextsList_lock);
1074  lrv = list_delete(&contextsList, threadContext);
1075  listSize = list_size(&contextsList);
1076  (void)pthread_mutex_unlock(&contextsList_lock);
1077  if (lrv < 0)
1078  Log2(PCSC_LOG_CRITICAL, "list_delete failed with error %x", lrv);
1079 
1080  free(threadContext);
1081 
1082  /* start a suicide alarm */
1083  if (AutoExit && (listSize < 1))
1084  {
1085  Log2(PCSC_LOG_DEBUG, "Starting suicide alarm in %d seconds",
1086  TIME_BEFORE_SUICIDE);
1087  alarm(TIME_BEFORE_SUICIDE);
1088  }
1089 
1090  return;
1091 }
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_PROTOCOL_UNDEFINED
protocol not set
Definition: pcsclite.h:239
#define SCARD_LEAVE_CARD
Do nothing on close.
Definition: pcsclite.h:252
#define MAX_BUFFER_SIZE_EXTENDED
enhanced (64K + APDU + Lc + Le + SW) Tx/Rx Buffer
Definition: pcsclite.h:298
LONG SCARDHANDLE
hCard returned by SCardConnect()
Definition: pcsclite.h:55
#define PCSCLITE_MAX_READERS_CONTEXTS
Maximum readers context (a slot is count as a reader)
Definition: pcsclite.h:284
This keeps track of a list of currently available reader structures.
_Atomic SCARDHANDLE hLockId
Lock Id.
Protocol Control Information (PCI)
Definition: pcsclite.h:80
unsigned long dwProtocol
Protocol identifier.
Definition: pcsclite.h:81
unsigned long cbPciLength
Protocol Control Inf Length.
Definition: pcsclite.h:82
pthread_mutex_t cardsList_lock
lock for the above list
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:60
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.