pcsc-lite/pcscd-1.8.10-polkit.patch

1383 lines
38 KiB
Diff

diff -urN pcsc-lite-1.8.10.orig/configure.ac pcsc-lite-1.8.10/configure.ac
--- pcsc-lite-1.8.10.orig/configure.ac 2013-10-19 18:33:04.000000000 +0200
+++ pcsc-lite-1.8.10/configure.ac 2014-02-11 13:04:10.505009385 +0100
@@ -280,6 +280,29 @@
PCSCLITE_FEATURES="${PCSCLITE_FEATURES} libusb"
fi
+# --enable-polkit
+POLKIT_MINIMUM=0.111
+AC_ARG_ENABLE(polkit,
+ AS_HELP_STRING([--enable-polkit],
+ [Build with polkit support]),
+ use_polkit=$withval, use_polkit=no)
+if test "$use_polkit" != "no"; then
+ PKG_CHECK_MODULES(POLKIT, [polkit-gobject-1 >= $POLKIT_MINIMUM], [use_polkit=yes], [use_polkit=no])
+ if test "$use_polkit" != "no";then
+ AC_DEFINE([HAVE_POLKIT], 1, [Build polkit access control support])
+ polkit_policy_dir=$($PKG_CONFIG polkit-gobject-1 --variable=policydir)
+ AC_SUBST(POLICY_DIR, [$polkit_policy_dir])
+ else
+ use_polkit=no
+ AC_MSG_ERROR([[
+***
+*** polkit >= $POLKIT_MINIMUM was not found. Access control will be disabled.
+*** You may get it from http://www.freedesktop.org/software/polkit/
+*** ]])
+ fi
+fi
+AM_CONDITIONAL(ENABLE_POLKIT, test "$use_polkit" != "no")
+
# --with-systemdsystemunitdir=DIR
AC_ARG_WITH([systemdsystemunitdir],
AS_HELP_STRING([--with-systemdsystemunitdir=DIR], [Directory for systemd service files]),
@@ -389,6 +412,8 @@
PCSC_ARCH: ${PCSC_ARCH}
pcscd binary ${PCSCD_BINARY}
+polkit support: ${use_polkit}
+polkit policy dir: ${polkit_policy_dir}
libudev support: ${use_libudev}
libusb support: ${use_libusb}
USB drop directory: ${usbdropdir}
diff -urN pcsc-lite-1.8.10.orig/doc/Makefile.am pcsc-lite-1.8.10/doc/Makefile.am
--- pcsc-lite-1.8.10.orig/doc/Makefile.am 2010-10-27 09:40:50.000000000 +0200
+++ pcsc-lite-1.8.10/doc/Makefile.am 2014-02-11 13:04:10.506009400 +0100
@@ -3,13 +3,23 @@
SUBDIRS = . example
doc_DATA = \
- README.DAEMON
+ README.DAEMON README.polkit
man_MANS = pcscd.8 reader.conf.5
man_in = pcscd.8.in reader.conf.5.in
-EXTRA_DIST = $(doc_DATA) $(man_in) doxygen.conf.in formaticc.1
+EXTRA_DIST = $(doc_DATA) $(man_in) doxygen.conf.in formaticc.1 \
+ org.debian.pcsc-lite.policy
doxygen:
rm -fr api ; cd .. ; doxygen doc/doxygen.conf
# cp doxygen.css api/
+
+if ENABLE_POLKIT
+install-data-hook:
+ $(MKDIR_P) $(DESTDIR)/$(POLICY_DIR)
+ $(INSTALL_DATA) $(srcdir)/org.debian.pcsc-lite.policy $(DESTDIR)/$(POLICY_DIR)
+
+uninstall-hook:
+ rm $(DESTDIR)/$(POLICY_DIR)/org.debian.pcsc-lite.policy
+endif
diff -urN pcsc-lite-1.8.10.orig/doc/org.debian.pcsc-lite.policy pcsc-lite-1.8.10/doc/org.debian.pcsc-lite.policy
--- pcsc-lite-1.8.10.orig/doc/org.debian.pcsc-lite.policy 1970-01-01 01:00:00.000000000 +0100
+++ pcsc-lite-1.8.10/doc/org.debian.pcsc-lite.policy 2014-02-11 13:04:10.505009385 +0100
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE policyconfig PUBLIC
+ "-//freedesktop//DTD PolicyKit Policy Configuration 1.0//EN"
+ "http://www.freedesktop.org/standards/PolicyKit/1.0/policyconfig.dtd">
+<policyconfig>
+ <vendor>The PCSC-lite Project</vendor>
+ <vendor_url>http://pcsclite.alioth.debian.org/</vendor_url>
+<!-- <icon_name>smart-card</icon_name> -->
+
+ <action id="org.debian.pcsc-lite.access_pcsc">
+ <description>Access to the PC/SC daemon</description>
+ <message>Authentication is required to access the PC/SC daemon</message>
+ <defaults>
+ <allow_any>auth_admin</allow_any>
+ <allow_inactive>auth_admin</allow_inactive>
+ <allow_active>yes</allow_active>
+ </defaults>
+ </action>
+
+ <action id="org.debian.pcsc-lite.access_card">
+ <description>Access to the smart card</description>
+ <message>Authentication is required to access the smart card</message>
+ <defaults>
+ <allow_any>auth_admin</allow_any>
+ <allow_inactive>auth_admin</allow_inactive>
+ <allow_active>yes</allow_active>
+ </defaults>
+ </action>
+
+</policyconfig>
diff -urN pcsc-lite-1.8.10.orig/doc/README.polkit pcsc-lite-1.8.10/doc/README.polkit
--- pcsc-lite-1.8.10.orig/doc/README.polkit 1970-01-01 01:00:00.000000000 +0100
+++ pcsc-lite-1.8.10/doc/README.polkit 2014-02-11 13:04:10.506009400 +0100
@@ -0,0 +1,39 @@
+When pcsc-lite is compiled using the --enable-polkit option then
+polkit will be used to control access to the pcsc-lite daemon.
+
+That allows more fine grained access control to smart cards that
+is tied to the system processes rather than solely depending on
+the smart card controls (e.g., only console users can access the
+card and so on).
+
+Polkit is documented at:
+http://www.freedesktop.org/software/polkit/docs/latest/polkit.8.html
+
+A default polkit policy is shipped with pcsc-lite in
+org.debian.pcsc-lite.policy. The policy file allows restricting access
+to the daemon as well as access to smart cards.
+
+Polkit allows for additional rules, e.g., restricting access to
+particular smart cards. The rules are javascript files placed
+in /usr/share/polkit-1/rules.d/. To make specific smart card
+reader accessible by the web server (run as www-data user) you
+may use the following rules:
+
+polkit.addRule(function(action, subject) {
+ if (action.id == "org.debian.pcsc-lite.access_card" &&
+ action.lookup("reader") == 'name of reader' &&
+ subject.user == "www-data") {
+ return polkit.Result.YES;
+ }
+});
+
+polkit.addRule(function(action, subject) {
+ if (action.id == "org.debian.pcsc-lite.access_pcsc" &&
+ subject.user == "www-data") {
+ return polkit.Result.YES;
+ }
+});
+
+
+Note that the name of the reader can be obtained using "opensc-tool -l"
+or "pcsc_scan".
diff -urN pcsc-lite-1.8.10.orig/src/auth.c pcsc-lite-1.8.10/src/auth.c
--- pcsc-lite-1.8.10.orig/src/auth.c 1970-01-01 01:00:00.000000000 +0100
+++ pcsc-lite-1.8.10/src/auth.c 2014-02-11 13:04:10.506009400 +0100
@@ -0,0 +1,155 @@
+/*
+ * MUSCLE SmartCard Development ( http://www.linuxnet.com )
+ *
+ * Copyright (C) 2013 Red Hat
+ *
+ * All rights reserved.
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+ * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
+ * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ *
+ * Author: Nikos Mavrogiannopoulos <nmav@redhat.com>
+ */
+
+/**
+ * @file
+ * @brief polkit authorization of clients
+ *
+ * IsClientAuthorized() checks whether the connecting client is authorized
+ * to access the resources using polkit.
+ */
+
+#include "config.h"
+#define _GNU_SOURCE
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/ioctl.h>
+#include <sys/un.h>
+#include <stdio.h>
+#include "debuglog.h"
+
+#include <errno.h>
+
+#if defined(HAVE_POLKIT) && defined(SO_PEERCRED)
+
+#include <polkit/polkit.h>
+
+/* Returns non zero when the client is authorized */
+unsigned IsClientAuthorized(int socket, const char* action, const char* reader)
+{
+ struct ucred cr;
+ socklen_t cr_len;
+ int ret;
+ PolkitSubject *subject;
+ PolkitAuthority *authority;
+ PolkitAuthorizationResult *result;
+ PolkitDetails *details;
+ GError *error = NULL;
+ char action_name[128];
+
+ snprintf(action_name, sizeof(action_name), "org.debian.pcsc-lite.%s", action);
+
+ cr_len = sizeof(cr);
+ ret = getsockopt(socket, SOL_SOCKET, SO_PEERCRED, &cr, &cr_len);
+ if (ret == -1)
+ {
+ int e = errno;
+ Log2(PCSC_LOG_CRITICAL,
+ "Error obtaining client process credentials: %s", strerror(e));
+ return 0;
+ }
+
+ authority = polkit_authority_get_sync(NULL, NULL);
+ if (authority == NULL)
+ {
+ Log1(PCSC_LOG_CRITICAL, "polkit_authority_get_sync failed");
+ return 0;
+ }
+
+ subject = polkit_unix_process_new_for_owner(cr.pid, 0, cr.uid);
+ if (subject == NULL)
+ {
+ Log1(PCSC_LOG_CRITICAL, "polkit_unix_process_new_for_owner failed");
+ ret = 0;
+ goto cleanup1;
+ }
+
+ details = polkit_details_new();
+ if (details == NULL)
+ {
+ Log1(PCSC_LOG_CRITICAL, "polkit_details_new failed");
+ ret = 0;
+ goto cleanup0;
+ }
+
+ if (reader != NULL)
+ polkit_details_insert(details, "reader", reader);
+
+ result = polkit_authority_check_authorization_sync(authority, subject,
+ action_name, details,
+ POLKIT_CHECK_AUTHORIZATION_FLAGS_NONE,
+ NULL,
+ &error);
+
+ if (result == NULL)
+ {
+ Log2(PCSC_LOG_CRITICAL, "Error in authorization: %s", error->message);
+ g_error_free(error);
+ ret = 0;
+ }
+ else
+ {
+ if (polkit_authorization_result_get_is_authorized(result))
+ {
+ ret = 1;
+ }
+ else
+ {
+ ret = 0;
+ }
+ }
+
+ if (ret == 0)
+ {
+ Log4(PCSC_LOG_CRITICAL,
+ "Process %u (user: %u) is NOT authorized for action: %s",
+ (unsigned)cr.pid, (unsigned)cr.uid, action);
+ }
+
+ g_object_unref(subject);
+cleanup0:
+ g_object_unref(details);
+cleanup1:
+ g_object_unref(authority);
+
+ return ret;
+}
+
+#else
+
+int IsClientAuthorized(int socket, const char* action, const char* reader)
+{
+ return 1;
+}
+
+#endif
diff -urN pcsc-lite-1.8.10.orig/src/auth.h pcsc-lite-1.8.10/src/auth.h
--- pcsc-lite-1.8.10.orig/src/auth.h 1970-01-01 01:00:00.000000000 +0100
+++ pcsc-lite-1.8.10/src/auth.h 2014-02-11 13:04:10.506009400 +0100
@@ -0,0 +1,35 @@
+/*
+ * MUSCLE SmartCard Development ( http://www.linuxnet.com )
+ *
+ * Copyright (C) 2013 Red Hat
+ *
+ * All rights reserved.
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+ * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
+ * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ *
+ * Author: Nikos Mavrogiannopoulos <nmav@redhat.com>
+ *
+ */
+
+unsigned IsClientAuthorized(int socket, const char* action, const char* reader);
diff -urN pcsc-lite-1.8.10.orig/src/Makefile.am pcsc-lite-1.8.10/src/Makefile.am
--- pcsc-lite-1.8.10.orig/src/Makefile.am 2013-03-30 13:15:33.000000000 +0100
+++ pcsc-lite-1.8.10/src/Makefile.am 2014-02-11 13:04:10.507009422 +0100
@@ -35,6 +35,8 @@
libpcsclite_la_LIBADD = $(PTHREAD_CFLAGS) $(PTHREAD_LIBS)
pcscd_SOURCES = \
+ auth.c \
+ auth.h \
atrhandler.c \
atrhandler.h \
configfile.h \
@@ -84,12 +86,14 @@
winscard_svc.c \
winscard_svc.h
pcscd_CFLAGS = $(CFLAGS) $(PTHREAD_CFLAGS) $(LIBUSB_CFLAGS) $(LIBUDEV_CFLAGS) \
+ $(POLKIT_CFLAGS) \
-DPCSCD -DSIMCLIST_NO_DUMPRESTORE
pcscd_LDFLAGS = $(LDFLAGS) -export-dynamic
pcscd_LDADD = \
$(PTHREAD_LIBS) $(COREFOUNDATION) \
$(LIBUSB_LIBS) $(IOKIT) $(LIBUDEV_LIBS) \
- $(PTHREAD_LIBS) $(PTHREAD_CFLAGS)
+ $(PTHREAD_LIBS) $(PTHREAD_CFLAGS) \
+ $(POLKIT_LIBS)
fix-rights: install-sbinPROGRAMS
chgrp pcscd $(DESTDIR)$(sbindir)/pcscd
diff -urN pcsc-lite-1.8.10.orig/src/winscard_svc.c pcsc-lite-1.8.10/src/winscard_svc.c
--- pcsc-lite-1.8.10.orig/src/winscard_svc.c 2013-08-05 21:18:44.000000000 +0200
+++ pcsc-lite-1.8.10/src/winscard_svc.c 2014-02-11 13:04:10.507009422 +0100
@@ -42,6 +42,7 @@
#include "readerfactory.h"
#include "eventhandler.h"
#include "simclist.h"
+#include "auth.h"
/**
* @brief Represents an Application Context on the Server side.
@@ -297,6 +298,16 @@
SCONTEXT * threadContext = (SCONTEXT *) newContext;
int32_t filedes = threadContext->dwClientID;
+ if (IsClientAuthorized(filedes, "access_pcsc", NULL) == 0)
+ {
+ Log1(PCSC_LOG_CRITICAL, "Rejected unauthorized PC/SC client");
+ goto exit;
+ }
+ else
+ {
+ Log1(PCSC_LOG_DEBUG, "Authorized PC/SC client");
+ }
+
Log3(PCSC_LOG_DEBUG, "Thread is started: dwClientID=%d, threadContext @%p",
threadContext->dwClientID, threadContext);
@@ -439,6 +450,16 @@
hCard = coStr.hCard;
dwActiveProtocol = coStr.dwActiveProtocol;
+ if (IsClientAuthorized(filedes, "access_card", coStr.szReader) == 0)
+ {
+ Log2(PCSC_LOG_CRITICAL, "Rejected unauthorized client for '%s'", coStr.szReader);
+ goto exit;
+ }
+ else
+ {
+ Log2(PCSC_LOG_DEBUG, "Authorized client for '%s'", coStr.szReader);
+ }
+
coStr.rv = SCardConnect(coStr.hContext, coStr.szReader,
coStr.dwShareMode, coStr.dwPreferredProtocols,
&hCard, &dwActiveProtocol);
diff -urN pcsc-lite-1.8.10.orig/src/winscard_svc.c.orig pcsc-lite-1.8.10/src/winscard_svc.c.orig
--- pcsc-lite-1.8.10.orig/src/winscard_svc.c.orig 1970-01-01 01:00:00.000000000 +0100
+++ pcsc-lite-1.8.10/src/winscard_svc.c.orig 2013-08-05 21:18:44.000000000 +0200
@@ -0,0 +1,959 @@
+/*
+ * MUSCLE SmartCard Development ( http://www.linuxnet.com )
+ *
+ * Copyright (C) 2001-2004
+ * David Corcoran <corcoran@linuxnet.com>
+ * Copyright (C) 2003-2004
+ * Damien Sauveron <damien.sauveron@labri.fr>
+ * Copyright (C) 2002-2011
+ * Ludovic Rousseau <ludovic.rousseau@free.fr>
+ * Copyright (C) 2009
+ * Jean-Luc Giraud <jlgiraud@googlemail.com>
+ *
+ * $Id: winscard_svc.c 6709 2013-08-05 18:49:31Z rousseau $
+ */
+
+/**
+ * @file
+ * @brief This demarshalls functions over the message queue and keeps
+ * track of clients and their handles.
+ *
+ * Each Client message is deald by creating a thread (\c CreateContextThread).
+ * The thread establishes reands and demarshalls the message and calls the
+ * appropriate function to threat it.
+ */
+
+#include "config.h"
+#include <time.h>
+#include <stdio.h>
+#include <string.h>
+#include <stddef.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <pthread.h>
+
+#include "pcscd.h"
+#include "winscard.h"
+#include "debuglog.h"
+#include "winscard_msg.h"
+#include "winscard_svc.h"
+#include "sys_generic.h"
+#include "utils.h"
+#include "readerfactory.h"
+#include "eventhandler.h"
+#include "simclist.h"
+
+/**
+ * @brief Represents an Application Context on the Server side.
+ *
+ * An Application Context contains Channels (\c hCard).
+ */
+
+extern char AutoExit;
+static int contextMaxThreadCounter = PCSC_MAX_CONTEXT_THREADS;
+static int contextMaxCardHandles = PCSC_MAX_CONTEXT_CARD_HANDLES;
+
+static list_t contextsList; /**< Context tracking list */
+pthread_mutex_t contextsList_lock; /**< lock for the above list */
+
+struct _psContext
+{
+ int32_t hContext;
+ list_t cardsList;
+ pthread_mutex_t cardsList_lock; /**< lock for the above list */
+ uint32_t dwClientID; /**< Connection ID used to reference the Client. */
+ pthread_t pthThread; /**< Event polling thread's ID */
+};
+typedef struct _psContext SCONTEXT;
+
+static LONG MSGCheckHandleAssociation(SCARDHANDLE, SCONTEXT *);
+static LONG MSGAddContext(SCARDCONTEXT, SCONTEXT *);
+static LONG MSGRemoveContext(SCARDCONTEXT, SCONTEXT *);
+static LONG MSGAddHandle(SCARDCONTEXT, SCARDHANDLE, SCONTEXT *);
+static LONG MSGRemoveHandle(SCARDHANDLE, SCONTEXT *);
+static LONG MSGCleanupClient(SCONTEXT *);
+
+static void ContextThread(LPVOID pdwIndex);
+
+extern READER_STATE readerStates[PCSCLITE_MAX_READERS_CONTEXTS];
+
+static int contextsListhContext_seeker(const void *el, const void *key)
+{
+ const SCONTEXT * currentContext = (SCONTEXT *)el;
+
+ if ((el == NULL) || (key == NULL))
+ {
+ Log3(PCSC_LOG_CRITICAL, "called with NULL pointer: el=%p, key=%p",
+ el, key);
+ return 0;
+ }
+
+ if (currentContext->hContext == *(int32_t *)key)
+ return 1;
+ return 0;
+}
+
+LONG ContextsInitialize(int customMaxThreadCounter,
+ int customMaxThreadCardHandles)
+{
+ int lrv = 0;
+
+ if (customMaxThreadCounter != 0)
+ contextMaxThreadCounter = customMaxThreadCounter;
+
+ if (customMaxThreadCardHandles != 0)
+ contextMaxCardHandles = customMaxThreadCardHandles;
+
+ lrv = list_init(&contextsList);
+ if (lrv < 0)
+ {
+ Log2(PCSC_LOG_CRITICAL, "list_init failed with return value: %d", lrv);
+ return -1;
+ }
+ lrv = list_attributes_seeker(& contextsList, contextsListhContext_seeker);
+ if (lrv < 0)
+ {
+ Log2(PCSC_LOG_CRITICAL,
+ "list_attributes_seeker failed with return value: %d", lrv);
+ return -1;
+ }
+
+ (void)pthread_mutex_init(&contextsList_lock, NULL);
+
+ return 1;
+}
+
+void ContextsDeinitialize(void)
+{
+ int listSize;
+ listSize = list_size(&contextsList);
+ Log2(PCSC_LOG_DEBUG, "remaining threads: %d", listSize);
+ /* This is currently a no-op. It should terminate the threads properly. */
+
+ list_destroy(&contextsList);
+}
+
+/**
+ * @brief Creates threads to handle messages received from Clients.
+ *
+ * @param[in] pdwClientID Connection ID used to reference the Client.
+ *
+ * @return Error code.
+ * @retval SCARD_S_SUCCESS Success.
+ * @retval SCARD_F_INTERNAL_ERROR Exceded the maximum number of simultaneous Application Contexts.
+ * @retval SCARD_E_NO_MEMORY Error creating the Context Thread.
+ */
+LONG CreateContextThread(uint32_t *pdwClientID)
+{
+ int rv;
+ int lrv;
+ int listSize;
+ SCONTEXT * newContext = NULL;
+ LONG retval = SCARD_E_NO_MEMORY;
+
+ (void)pthread_mutex_lock(&contextsList_lock);
+
+ listSize = list_size(&contextsList);
+ if (listSize >= contextMaxThreadCounter)
+ {
+ Log2(PCSC_LOG_CRITICAL, "Too many context running: %d", listSize);
+ goto out;
+ }
+
+ /* Create the context for this thread. */
+ newContext = malloc(sizeof(*newContext));
+ if (NULL == newContext)
+ {
+ Log1(PCSC_LOG_CRITICAL, "Could not allocate new context");
+ goto out;
+ }
+ memset(newContext, 0, sizeof(*newContext));
+
+ newContext->dwClientID = *pdwClientID;
+
+ /* Initialise the list of card contexts */
+ lrv = list_init(&newContext->cardsList);
+ if (lrv < 0)
+ {
+ Log2(PCSC_LOG_CRITICAL, "list_init failed with return value: %d", lrv);
+ goto out;
+ }
+
+ /* request to store copies, and provide the metric function */
+ list_attributes_copy(&newContext->cardsList, list_meter_int32_t, 1);
+
+ /* Adding a comparator
+ * The stored type is SCARDHANDLE (long) but has only 32 bits
+ * usefull even on a 64-bit CPU since the API between pcscd and
+ * libpcscliter uses "int32_t hCard;"
+ */
+ lrv = list_attributes_comparator(&newContext->cardsList,
+ list_comparator_int32_t);
+ if (lrv != 0)
+ {
+ Log2(PCSC_LOG_CRITICAL,
+ "list_attributes_comparator failed with return value: %d", lrv);
+ list_destroy(&newContext->cardsList);
+ goto out;
+ }
+
+ (void)pthread_mutex_init(&newContext->cardsList_lock, NULL);
+
+ lrv = list_append(&contextsList, newContext);
+ if (lrv < 0)
+ {
+ Log2(PCSC_LOG_CRITICAL, "list_append failed with return value: %d",
+ lrv);
+ list_destroy(&newContext->cardsList);
+ goto out;
+ }
+
+ rv = ThreadCreate(&newContext->pthThread, THREAD_ATTR_DETACHED,
+ (PCSCLITE_THREAD_FUNCTION( )) ContextThread, (LPVOID) newContext);
+ if (rv)
+ {
+ int lrv2;
+
+ Log2(PCSC_LOG_CRITICAL, "ThreadCreate failed: %s", strerror(rv));
+ lrv2 = list_delete(&contextsList, newContext);
+ if (lrv2 < 0)
+ Log2(PCSC_LOG_CRITICAL, "list_delete failed with error %d", lrv2);
+ list_destroy(&newContext->cardsList);
+ goto out;
+ }
+
+ /* disable any suicide alarm */
+ if (AutoExit)
+ alarm(0);
+
+ retval = SCARD_S_SUCCESS;
+
+out:
+ (void)pthread_mutex_unlock(&contextsList_lock);
+
+ if (retval != SCARD_S_SUCCESS)
+ {
+ if (newContext)
+ free(newContext);
+ (void)close(*pdwClientID);
+ }
+
+ return retval;
+}
+
+/*
+ * A list of local functions used to keep track of clients and their
+ * connections
+ */
+
+/**
+ * @brief Handles messages received from Clients.
+ *
+ * For each Client message a new instance of this thread is created.
+ *
+ * @param[in] dwIndex Index of an avaiable Application Context slot in
+ * \c SCONTEXT *.
+ */
+#ifndef NO_LOG
+static const char *CommandsText[] = {
+ "NULL",
+ "ESTABLISH_CONTEXT", /* 0x01 */
+ "RELEASE_CONTEXT",
+ "LIST_READERS",
+ "CONNECT",
+ "RECONNECT", /* 0x05 */
+ "DISCONNECT",
+ "BEGIN_TRANSACTION",
+ "END_TRANSACTION",
+ "TRANSMIT",
+ "CONTROL", /* 0x0A */
+ "STATUS",
+ "GET_STATUS_CHANGE",
+ "CANCEL",
+ "CANCEL_TRANSACTION",
+ "GET_ATTRIB", /* 0x0F */
+ "SET_ATTRIB",
+ "CMD_VERSION",
+ "CMD_GET_READERS_STATE",
+ "CMD_WAIT_READER_STATE_CHANGE",
+ "CMD_STOP_WAITING_READER_STATE_CHANGE", /* 0x14 */
+ "NULL"
+};
+#endif
+
+#define READ_BODY(v) \
+ if (header.size != sizeof(v)) { goto wrong_length; } \
+ ret = MessageReceive(&v, sizeof(v), filedes); \
+ if (ret != SCARD_S_SUCCESS) { Log2(PCSC_LOG_DEBUG, "Client die: %d", filedes); goto exit; }
+
+#define WRITE_BODY(v) \
+ WRITE_BODY_WITH_COMMAND(CommandsText[header.command], v)
+#define WRITE_BODY_WITH_COMMAND(command, v) \
+ Log4(PCSC_LOG_DEBUG, "%s rv=0x%X for client %d", command, v.rv, filedes); \
+ ret = MessageSend(&v, sizeof(v), filedes);
+
+static void ContextThread(LPVOID newContext)
+{
+ SCONTEXT * threadContext = (SCONTEXT *) newContext;
+ int32_t filedes = threadContext->dwClientID;
+
+ Log3(PCSC_LOG_DEBUG, "Thread is started: dwClientID=%d, threadContext @%p",
+ threadContext->dwClientID, threadContext);
+
+ while (1)
+ {
+ struct rxHeader header;
+ int32_t ret = MessageReceive(&header, sizeof(header), filedes);
+
+ if (ret != SCARD_S_SUCCESS)
+ {
+ /* Clean up the dead client */
+ Log2(PCSC_LOG_DEBUG, "Client die: %d", filedes);
+ EHTryToUnregisterClientForEvent(filedes);
+ goto exit;
+ }
+
+ if ((header.command > CMD_ENUM_FIRST)
+ && (header.command < CMD_ENUM_LAST))
+ Log3(PCSC_LOG_DEBUG, "Received command: %s from client %d",
+ CommandsText[header.command], filedes);
+
+ switch (header.command)
+ {
+ /* pcsc-lite client/server protocol version */
+ case CMD_VERSION:
+ {
+ struct version_struct veStr;
+
+ READ_BODY(veStr)
+
+ Log3(PCSC_LOG_DEBUG, "Client is protocol version %d:%d",
+ veStr.major, veStr.minor);
+
+ veStr.rv = SCARD_S_SUCCESS;
+
+ /* client and server use different protocol */
+ if ((veStr.major != PROTOCOL_VERSION_MAJOR)
+ || (veStr.minor != PROTOCOL_VERSION_MINOR))
+ {
+ Log3(PCSC_LOG_CRITICAL, "Client protocol is %d:%d",
+ veStr.major, veStr.minor);
+ Log3(PCSC_LOG_CRITICAL, "Server protocol is %d:%d",
+ PROTOCOL_VERSION_MAJOR, PROTOCOL_VERSION_MINOR);
+ veStr.rv = SCARD_E_NO_SERVICE;
+ }
+
+ /* set the server protocol version */
+ veStr.major = PROTOCOL_VERSION_MAJOR;
+ veStr.minor = PROTOCOL_VERSION_MINOR;
+
+ /* send back the response */
+ WRITE_BODY(veStr)
+ }
+ break;
+
+ case CMD_GET_READERS_STATE:
+ {
+ /* nothing to read */
+
+#ifdef USE_USB
+ /* wait until all readers are ready */
+ RFWaitForReaderInit();
+#endif
+
+ /* dump the readers state */
+ ret = MessageSend(readerStates, sizeof(readerStates), filedes);
+ }
+ break;
+
+ case CMD_WAIT_READER_STATE_CHANGE:
+ {
+ struct wait_reader_state_change waStr;
+
+ READ_BODY(waStr)
+
+ /* add the client fd to the list */
+ EHRegisterClientForEvent(filedes);
+
+ /* We do not send anything here.
+ * Either the client will timeout or the server will
+ * answer if an event occurs */
+ }
+ break;
+
+ case CMD_STOP_WAITING_READER_STATE_CHANGE:
+ {
+ struct wait_reader_state_change waStr;
+
+ READ_BODY(waStr)
+
+ /* add the client fd to the list */
+ waStr.rv = EHUnregisterClientForEvent(filedes);
+
+ WRITE_BODY(waStr)
+ }
+ break;
+
+ case SCARD_ESTABLISH_CONTEXT:
+ {
+ struct establish_struct esStr;
+ SCARDCONTEXT hContext;
+
+ READ_BODY(esStr)
+
+ hContext = esStr.hContext;
+ esStr.rv = SCardEstablishContext(esStr.dwScope, 0, 0,
+ &hContext);
+ esStr.hContext = hContext;
+
+ if (esStr.rv == SCARD_S_SUCCESS)
+ esStr.rv = MSGAddContext(esStr.hContext, threadContext);
+
+ WRITE_BODY(esStr)
+ }
+ break;
+
+ case SCARD_RELEASE_CONTEXT:
+ {
+ struct release_struct reStr;
+
+ READ_BODY(reStr)
+
+ reStr.rv = SCardReleaseContext(reStr.hContext);
+
+ if (reStr.rv == SCARD_S_SUCCESS)
+ reStr.rv = MSGRemoveContext(reStr.hContext, threadContext);
+
+ WRITE_BODY(reStr)
+ }
+ break;
+
+ case SCARD_CONNECT:
+ {
+ struct connect_struct coStr;
+ SCARDHANDLE hCard;
+ DWORD dwActiveProtocol;
+
+ READ_BODY(coStr)
+
+ hCard = coStr.hCard;
+ dwActiveProtocol = coStr.dwActiveProtocol;
+
+ coStr.rv = SCardConnect(coStr.hContext, coStr.szReader,
+ coStr.dwShareMode, coStr.dwPreferredProtocols,
+ &hCard, &dwActiveProtocol);
+
+ coStr.hCard = hCard;
+ coStr.dwActiveProtocol = dwActiveProtocol;
+
+ if (coStr.rv == SCARD_S_SUCCESS)
+ coStr.rv = MSGAddHandle(coStr.hContext, coStr.hCard,
+ threadContext);
+
+ WRITE_BODY(coStr)
+ }
+ break;
+
+ case SCARD_RECONNECT:
+ {
+ struct reconnect_struct rcStr;
+ DWORD dwActiveProtocol;
+
+ READ_BODY(rcStr)
+
+ if (MSGCheckHandleAssociation(rcStr.hCard, threadContext))
+ goto exit;
+
+ rcStr.rv = SCardReconnect(rcStr.hCard, rcStr.dwShareMode,
+ rcStr.dwPreferredProtocols, rcStr.dwInitialization,
+ &dwActiveProtocol);
+ rcStr.dwActiveProtocol = dwActiveProtocol;
+
+ WRITE_BODY(rcStr)
+ }
+ break;
+
+ case SCARD_DISCONNECT:
+ {
+ struct disconnect_struct diStr;
+
+ READ_BODY(diStr)
+
+ if (MSGCheckHandleAssociation(diStr.hCard, threadContext))
+ goto exit;
+
+ diStr.rv = SCardDisconnect(diStr.hCard, diStr.dwDisposition);
+
+ if (SCARD_S_SUCCESS == diStr.rv)
+ diStr.rv = MSGRemoveHandle(diStr.hCard, threadContext);
+
+ WRITE_BODY(diStr)
+ }
+ break;
+
+ case SCARD_BEGIN_TRANSACTION:
+ {
+ struct begin_struct beStr;
+
+ READ_BODY(beStr)
+
+ if (MSGCheckHandleAssociation(beStr.hCard, threadContext))
+ goto exit;
+
+ beStr.rv = SCardBeginTransaction(beStr.hCard);
+
+ WRITE_BODY(beStr)
+ }
+ break;
+
+ case SCARD_END_TRANSACTION:
+ {
+ struct end_struct enStr;
+
+ READ_BODY(enStr)
+
+ if (MSGCheckHandleAssociation(enStr.hCard, threadContext))
+ goto exit;
+
+ enStr.rv = SCardEndTransaction(enStr.hCard,
+ enStr.dwDisposition);
+
+ WRITE_BODY(enStr)
+ }
+ break;
+
+ case SCARD_CANCEL:
+ {
+ struct cancel_struct caStr;
+ SCONTEXT * psTargetContext = NULL;
+ READ_BODY(caStr)
+
+ /* find the client */
+ (void)pthread_mutex_lock(&contextsList_lock);
+ psTargetContext = (SCONTEXT *) list_seek(&contextsList,
+ &caStr.hContext);
+ (void)pthread_mutex_unlock(&contextsList_lock);
+ if (psTargetContext != NULL)
+ {
+ uint32_t fd = psTargetContext->dwClientID;
+ caStr.rv = MSGSignalClient(fd, SCARD_E_CANCELLED);
+ }
+ else
+ caStr.rv = SCARD_E_INVALID_HANDLE;
+
+ WRITE_BODY(caStr)
+ }
+ break;
+
+ case SCARD_STATUS:
+ {
+ struct status_struct stStr;
+
+ READ_BODY(stStr)
+
+ if (MSGCheckHandleAssociation(stStr.hCard, threadContext))
+ goto exit;
+
+ /* only hCard and return value are used by the client */
+ stStr.rv = SCardStatus(stStr.hCard, NULL, NULL, NULL,
+ NULL, 0, NULL);
+
+ WRITE_BODY(stStr)
+ }
+ break;
+
+ case SCARD_TRANSMIT:
+ {
+ struct transmit_struct trStr;
+ unsigned char pbSendBuffer[MAX_BUFFER_SIZE_EXTENDED];
+ unsigned char pbRecvBuffer[MAX_BUFFER_SIZE_EXTENDED];
+ SCARD_IO_REQUEST ioSendPci;
+ SCARD_IO_REQUEST ioRecvPci;
+ DWORD cbRecvLength;
+
+ READ_BODY(trStr)
+
+ if (MSGCheckHandleAssociation(trStr.hCard, threadContext))
+ goto exit;
+
+ /* avoids buffer overflow */
+ if ((trStr.pcbRecvLength > sizeof(pbRecvBuffer))
+ || (trStr.cbSendLength > sizeof(pbSendBuffer)))
+ goto buffer_overflow;
+
+ /* read sent buffer */
+ ret = MessageReceive(pbSendBuffer, trStr.cbSendLength, filedes);
+ if (ret != SCARD_S_SUCCESS)
+ {
+ Log2(PCSC_LOG_DEBUG, "Client die: %d", filedes);
+ goto exit;
+ }
+
+ ioSendPci.dwProtocol = trStr.ioSendPciProtocol;
+ ioSendPci.cbPciLength = trStr.ioSendPciLength;
+ ioRecvPci.dwProtocol = trStr.ioRecvPciProtocol;
+ ioRecvPci.cbPciLength = trStr.ioRecvPciLength;
+ cbRecvLength = trStr.pcbRecvLength;
+
+ trStr.rv = SCardTransmit(trStr.hCard, &ioSendPci,
+ pbSendBuffer, trStr.cbSendLength, &ioRecvPci,
+ pbRecvBuffer, &cbRecvLength);
+
+ trStr.ioSendPciProtocol = ioSendPci.dwProtocol;
+ trStr.ioSendPciLength = ioSendPci.cbPciLength;
+ trStr.ioRecvPciProtocol = ioRecvPci.dwProtocol;
+ trStr.ioRecvPciLength = ioRecvPci.cbPciLength;
+ trStr.pcbRecvLength = cbRecvLength;
+
+ WRITE_BODY(trStr)
+
+ /* write received buffer */
+ if (SCARD_S_SUCCESS == trStr.rv)
+ ret = MessageSend(pbRecvBuffer, cbRecvLength, filedes);
+ }
+ break;
+
+ case SCARD_CONTROL:
+ {
+ struct control_struct ctStr;
+ unsigned char pbSendBuffer[MAX_BUFFER_SIZE_EXTENDED];
+ unsigned char pbRecvBuffer[MAX_BUFFER_SIZE_EXTENDED];
+ DWORD dwBytesReturned;
+
+ READ_BODY(ctStr)
+
+ if (MSGCheckHandleAssociation(ctStr.hCard, threadContext))
+ goto exit;
+
+ /* avoids buffer overflow */
+ if ((ctStr.cbRecvLength > sizeof(pbRecvBuffer))
+ || (ctStr.cbSendLength > sizeof(pbSendBuffer)))
+ {
+ goto buffer_overflow;
+ }
+
+ /* read sent buffer */
+ ret = MessageReceive(pbSendBuffer, ctStr.cbSendLength, filedes);
+ if (ret != SCARD_S_SUCCESS)
+ {
+ Log2(PCSC_LOG_DEBUG, "Client die: %d", filedes);
+ goto exit;
+ }
+
+ dwBytesReturned = ctStr.dwBytesReturned;
+
+ ctStr.rv = SCardControl(ctStr.hCard, ctStr.dwControlCode,
+ pbSendBuffer, ctStr.cbSendLength,
+ pbRecvBuffer, ctStr.cbRecvLength,
+ &dwBytesReturned);
+
+ ctStr.dwBytesReturned = dwBytesReturned;
+
+ WRITE_BODY(ctStr)
+
+ /* write received buffer */
+ if (SCARD_S_SUCCESS == ctStr.rv)
+ ret = MessageSend(pbRecvBuffer, dwBytesReturned, filedes);
+ }
+ break;
+
+ case SCARD_GET_ATTRIB:
+ {
+ struct getset_struct gsStr;
+ DWORD cbAttrLen;
+
+ READ_BODY(gsStr)
+
+ if (MSGCheckHandleAssociation(gsStr.hCard, threadContext))
+ goto exit;
+
+ /* avoids buffer overflow */
+ if (gsStr.cbAttrLen > sizeof(gsStr.pbAttr))
+ goto buffer_overflow;
+
+ cbAttrLen = gsStr.cbAttrLen;
+
+ gsStr.rv = SCardGetAttrib(gsStr.hCard, gsStr.dwAttrId,
+ gsStr.pbAttr, &cbAttrLen);
+
+ gsStr.cbAttrLen = cbAttrLen;
+
+ WRITE_BODY(gsStr)
+ }
+ break;
+
+ case SCARD_SET_ATTRIB:
+ {
+ struct getset_struct gsStr;
+
+ READ_BODY(gsStr)
+
+ if (MSGCheckHandleAssociation(gsStr.hCard, threadContext))
+ goto exit;
+
+ /* avoids buffer overflow */
+ if (gsStr.cbAttrLen > sizeof(gsStr.pbAttr))
+ goto buffer_overflow;
+
+ gsStr.rv = SCardSetAttrib(gsStr.hCard, gsStr.dwAttrId,
+ gsStr.pbAttr, gsStr.cbAttrLen);
+
+ WRITE_BODY(gsStr)
+ }
+ break;
+
+ default:
+ Log2(PCSC_LOG_CRITICAL, "Unknown command: %d", header.command);
+ goto exit;
+ }
+
+ /* MessageSend() failed */
+ if (ret != SCARD_S_SUCCESS)
+ {
+ /* Clean up the dead client */
+ Log2(PCSC_LOG_DEBUG, "Client die: %d", filedes);
+ goto exit;
+ }
+ }
+
+buffer_overflow:
+ Log2(PCSC_LOG_DEBUG, "Buffer overflow detected: %d", filedes);
+ goto exit;
+wrong_length:
+ Log2(PCSC_LOG_DEBUG, "Wrong length: %d", filedes);
+exit:
+ (void)close(filedes);
+ (void)MSGCleanupClient(threadContext);
+ (void)pthread_exit((LPVOID) NULL);
+}
+
+LONG MSGSignalClient(uint32_t filedes, LONG rv)
+{
+ uint32_t ret;
+ struct wait_reader_state_change waStr;
+
+ Log2(PCSC_LOG_DEBUG, "Signal client: %d", filedes);
+
+ waStr.rv = rv;
+ WRITE_BODY_WITH_COMMAND("SIGNAL", waStr)
+
+ return ret;
+} /* MSGSignalClient */
+
+static LONG MSGAddContext(SCARDCONTEXT hContext, SCONTEXT * threadContext)
+{
+ threadContext->hContext = hContext;
+ return SCARD_S_SUCCESS;
+}
+
+static LONG MSGRemoveContext(SCARDCONTEXT hContext, SCONTEXT * threadContext)
+{
+ LONG rv;
+ int lrv;
+
+ if (threadContext->hContext != hContext)
+ return SCARD_E_INVALID_VALUE;
+
+ (void)pthread_mutex_lock(&threadContext->cardsList_lock);
+ while (list_size(&threadContext->cardsList) != 0)
+ {
+ READER_CONTEXT * rContext = NULL;
+ SCARDHANDLE hCard, hLockId;
+ void *ptr;
+
+ /*
+ * Disconnect each of these just in case
+ */
+ ptr = list_get_at(&threadContext->cardsList, 0);
+ if (NULL == ptr)
+ {
+ Log1(PCSC_LOG_CRITICAL, "list_get_at failed");
+ continue;
+ }
+ hCard = *(int32_t *)ptr;
+
+ /*
+ * Unlock the sharing
+ */
+ rv = RFReaderInfoById(hCard, &rContext);
+ if (rv != SCARD_S_SUCCESS)
+ {
+ (void)pthread_mutex_unlock(&threadContext->cardsList_lock);
+ return rv;
+ }
+
+ hLockId = rContext->hLockId;
+ rContext->hLockId = 0;
+
+ if (hCard != hLockId)
+ {
+ /*
+ * if the card is locked by someone else we do not reset it
+ * and simulate a card removal
+ */
+ rv = SCARD_W_REMOVED_CARD;
+ }
+ else
+ {
+ /*
+ * We will use SCardStatus to see if the card has been
+ * reset there is no need to reset each time
+ * Disconnect is called
+ */
+ rv = SCardStatus(hCard, NULL, NULL, NULL, NULL, NULL, NULL);
+ }
+
+ if (rv == SCARD_W_RESET_CARD || rv == SCARD_W_REMOVED_CARD)
+ (void)SCardDisconnect(hCard, SCARD_LEAVE_CARD);
+ else
+ (void)SCardDisconnect(hCard, SCARD_RESET_CARD);
+
+ /* Remove entry from the list */
+ lrv = list_delete_at(&threadContext->cardsList, 0);
+ if (lrv < 0)
+ Log2(PCSC_LOG_CRITICAL,
+ "list_delete_at failed with return value: %d", lrv);
+
+ UNREF_READER(rContext)
+ }
+ (void)pthread_mutex_unlock(&threadContext->cardsList_lock);
+ list_destroy(&threadContext->cardsList);
+
+ /* We only mark the context as no longer in use.
+ * The memory is freed in MSGCleanupCLient() */
+ threadContext->hContext = 0;
+
+ return SCARD_S_SUCCESS;
+}
+
+static LONG MSGAddHandle(SCARDCONTEXT hContext, SCARDHANDLE hCard,
+ SCONTEXT * threadContext)
+{
+ LONG retval = SCARD_E_INVALID_VALUE;
+
+ if (threadContext->hContext == hContext)
+ {
+ /*
+ * Find an empty spot to put the hCard value
+ */
+ int listLength;
+
+ (void)pthread_mutex_lock(&threadContext->cardsList_lock);
+
+ listLength = list_size(&threadContext->cardsList);
+ if (listLength >= contextMaxCardHandles)
+ {
+ Log4(PCSC_LOG_DEBUG,
+ "Too many card handles for thread context @%p: %d (max is %d)"
+ "Restart pcscd with --max-card-handle-per-thread value",
+ threadContext, listLength, contextMaxCardHandles);
+ retval = SCARD_E_NO_MEMORY;
+ }
+ else
+ {
+ int lrv;
+
+ lrv = list_append(&threadContext->cardsList, &hCard);
+ if (lrv < 0)
+ {
+ Log2(PCSC_LOG_CRITICAL,
+ "list_append failed with return value: %d", lrv);
+ retval = SCARD_E_NO_MEMORY;
+ }
+ else
+ retval = SCARD_S_SUCCESS;
+ }
+
+ (void)pthread_mutex_unlock(&threadContext->cardsList_lock);
+ }
+
+ return retval;
+}
+
+static LONG MSGRemoveHandle(SCARDHANDLE hCard, SCONTEXT * threadContext)
+{
+ int lrv;
+
+ (void)pthread_mutex_lock(&threadContext->cardsList_lock);
+ lrv = list_delete(&threadContext->cardsList, &hCard);
+ (void)pthread_mutex_unlock(&threadContext->cardsList_lock);
+ if (lrv < 0)
+ {
+ Log2(PCSC_LOG_CRITICAL, "list_delete failed with error %d", lrv);
+ return SCARD_E_INVALID_VALUE;
+ }
+
+ return SCARD_S_SUCCESS;
+}
+
+
+static LONG MSGCheckHandleAssociation(SCARDHANDLE hCard,
+ SCONTEXT * threadContext)
+{
+ int list_index = 0;
+
+ if (0 == threadContext->hContext)
+ {
+ /* the handle is no more valid. After SCardReleaseContext() for
+ * example */
+ Log1(PCSC_LOG_CRITICAL, "Invalidated handle");
+ return -1;
+ }
+
+ (void)pthread_mutex_lock(&threadContext->cardsList_lock);
+ list_index = list_locate(&threadContext->cardsList, &hCard);
+ (void)pthread_mutex_unlock(&threadContext->cardsList_lock);
+ if (list_index >= 0)
+ return 0;
+
+ /* Must be a rogue client, debug log and sleep a couple of seconds */
+ Log1(PCSC_LOG_ERROR, "Client failed to authenticate");
+ (void)SYS_Sleep(2);
+
+ return -1;
+}
+
+
+/* Should be called just prior to exiting the thread as it de-allocates
+ * the thread memory strucutres
+ */
+static LONG MSGCleanupClient(SCONTEXT * threadContext)
+{
+ int lrv;
+ int listSize;
+
+ if (threadContext->hContext != 0)
+ {
+ (void)SCardReleaseContext(threadContext->hContext);
+ (void)MSGRemoveContext(threadContext->hContext, threadContext);
+ }
+
+ Log3(PCSC_LOG_DEBUG,
+ "Thread is stopping: dwClientID=%d, threadContext @%p",
+ threadContext->dwClientID, threadContext);
+
+ /* Clear the struct to ensure that we detect
+ * access to de-allocated memory
+ * Hopefully the compiler won't optimise it out */
+ memset((void*) threadContext, 0, sizeof(SCONTEXT));
+ Log2(PCSC_LOG_DEBUG, "Freeing SCONTEXT @%p", threadContext);
+
+ (void)pthread_mutex_lock(&contextsList_lock);
+ lrv = list_delete(&contextsList, threadContext);
+ listSize = list_size(&contextsList);
+ (void)pthread_mutex_unlock(&contextsList_lock);
+ if (lrv < 0)
+ Log2(PCSC_LOG_CRITICAL, "list_delete failed with error %x", lrv);
+
+ free(threadContext);
+
+ /* start a suicide alarm */
+ if (AutoExit && (listSize < 1))
+ {
+ Log2(PCSC_LOG_DEBUG, "Starting suicide alarm in %d seconds",
+ TIME_BEFORE_SUICIDE);
+ alarm(TIME_BEFORE_SUICIDE);
+ }
+
+ return 0;
+}