diff --git a/org.debian.pcsc-lite.policy b/org.debian.pcsc-lite.policy new file mode 100644 index 0000000..e089707 --- /dev/null +++ b/org.debian.pcsc-lite.policy @@ -0,0 +1,30 @@ + + + + The PCSC-lite Project + http://pcsclite.alioth.debian.org/ + + + + Access to the PC/SC daemon + Authentication is required to access the PC/SC daemon + + yes + yes + yes + + + + + Access to the smart card + Authentication is required to access the smart card + + yes + yes + yes + + + + diff --git a/pcsc-lite.spec b/pcsc-lite.spec index e480b8f..d249b69 100644 --- a/pcsc-lite.spec +++ b/pcsc-lite.spec @@ -2,18 +2,26 @@ Name: pcsc-lite Version: 1.8.10 -Release: 1%{?dist} +Release: 2%{?dist} Summary: PC/SC Lite smart card framework and applications Group: System Environment/Daemons License: BSD URL: http://pcsclite.alioth.debian.org/ Source0: https://alioth.debian.org/frs/download.php/file/%{upstream_build}/%{name}-%{version}.tar.bz2 +Source1: org.debian.pcsc-lite.policy +Patch0: pcscd-1.8.10-polkit.patch +Patch1: pcscd-1.8.10-null-force.patch BuildRequires: doxygen BuildRequires: graphviz BuildRequires: systemd-devel BuildRequires: /usr/bin/pod2man +BuildRequires: polkit-devel +BuildRequires: autoconf +BuildRequires: automake +BuildRequires: gettext-devel +BuildRequires: libtool Requires(post): systemd Requires(preun): systemd @@ -58,6 +66,9 @@ Requires: %{name}-libs = %{version}-%{release} %prep %setup -q +%patch0 -p1 -b .polkit +%patch1 -p1 -b .null + # Convert to utf-8 for file in ChangeLog; do iconv -f ISO-8859-1 -t UTF-8 -o $file.new $file && \ @@ -67,8 +78,10 @@ done %build +autoreconf -if %configure \ --disable-static \ + --enable-polkit \ --enable-usbdropdir=%{_libdir}/pcsc/drivers make %{?_smp_mflags} doxygen doc/doxygen.conf ; rm -f doc/api/*.{map,md5} @@ -76,6 +89,10 @@ doxygen doc/doxygen.conf ; rm -f doc/api/*.{map,md5} %install make install DESTDIR=$RPM_BUILD_ROOT +rm -f $RPM_BUILD_ROOT%{_datadir}/polkit-1/actions/org.debian.pcsc-lite.policy + +mkdir -p $RPM_BUILD_ROOT%{_datadir}/polkit-1/actions/ +install -p -m 644 %{SOURCE1} $RPM_BUILD_ROOT%{_datadir}/polkit-1/actions/ # Create empty directories mkdir -p $RPM_BUILD_ROOT%{_sysconfdir}/reader.conf.d @@ -112,6 +129,7 @@ rm $RPM_BUILD_ROOT%{_docdir}/pcsc-lite/README.DAEMON %dir %{_libdir}/pcsc/drivers/ %{_mandir}/man5/reader.conf.5* %{_mandir}/man8/pcscd.8* +%{_datadir}/polkit-1/actions/org.debian.pcsc-lite.policy %ghost %dir %{_localstatedir}/run/pcscd/ %files libs @@ -131,6 +149,10 @@ rm $RPM_BUILD_ROOT%{_docdir}/pcsc-lite/README.DAEMON %changelog +* Tue Feb 11 2014 Nikos Mavrogiannopoulos - 1.8.10-2 +- Added upstream patch to support polkit +- Force sanity of parameters received by the client + * Sun Oct 20 2013 Kalev Lember - 1.8.10-1 - Update to 1.8.10 - Update source URL diff --git a/pcscd-1.8.10-null-force.patch b/pcscd-1.8.10-null-force.patch new file mode 100644 index 0000000..98ffb4a --- /dev/null +++ b/pcscd-1.8.10-null-force.patch @@ -0,0 +1,12 @@ +diff -ur 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:03:03.145500010 +0100 +@@ -436,6 +436,7 @@ + + READ_BODY(coStr) + ++ coStr.szReader[sizeof(coStr.szReader)-1] = 0; + hCard = coStr.hCard; + dwActiveProtocol = coStr.dwActiveProtocol; + +Only in pcsc-lite-1.8.10/src: winscard_svc.c.orig diff --git a/pcscd-1.8.10-polkit.patch b/pcscd-1.8.10-polkit.patch new file mode 100644 index 0000000..64e80b2 --- /dev/null +++ b/pcscd-1.8.10-polkit.patch @@ -0,0 +1,1382 @@ +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 @@ ++ ++ ++ ++ The PCSC-lite Project ++ http://pcsclite.alioth.debian.org/ ++ ++ ++ ++ Access to the PC/SC daemon ++ Authentication is required to access the PC/SC daemon ++ ++ auth_admin ++ auth_admin ++ yes ++ ++ ++ ++ ++ Access to the smart card ++ Authentication is required to access the smart card ++ ++ auth_admin ++ auth_admin ++ yes ++ ++ ++ ++ +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 ++ */ ++ ++/** ++ * @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 ++#include ++#include ++#include ++#include ++#include "debuglog.h" ++ ++#include ++ ++#if defined(HAVE_POLKIT) && defined(SO_PEERCRED) ++ ++#include ++ ++/* 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 ++ * ++ */ ++ ++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 ++ * Copyright (C) 2003-2004 ++ * Damien Sauveron ++ * Copyright (C) 2002-2011 ++ * Ludovic Rousseau ++ * Copyright (C) 2009 ++ * Jean-Luc Giraud ++ * ++ * $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 ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#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; ++}