1383 lines
38 KiB
Diff
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;
|
|
+}
|