/* * MUSCLE SmartCard Development ( https://pcsclite.apdu.fr/ ) * * Copyright (C) 2001-2004 * David Corcoran * Copyright (C) 2003-2011 * Ludovic Rousseau * Copyright (C) 2003 * Toni Andjelkovic * Copyright (C) 2003-2004 * Damien Sauveron * 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. 3. The name of the author may not be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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. */ /** * @file * @brief This provides a search API for hot pluggble devices. */ #include "config.h" #ifdef HAVE_LIBUSB #include #include #include #include #include #include #include #include #include #include #include #include #include "misc.h" #include "wintypes.h" #include "pcscd.h" #include "debuglog.h" #include "parser.h" #include "readerfactory.h" #include "winscard_msg.h" #include "sys_generic.h" #include "hotplug.h" #include "utils.h" #undef DEBUG_HOTPLUG /* format is "%d:%d:%d", bus_number, device_address, interface */ #define BUS_DEVICE_STRSIZE 10+1+10+1+10+1 #define READER_ABSENT 0 #define READER_PRESENT 1 #define READER_FAILED 2 #define FALSE 0 #define TRUE 1 extern char Add_Interface_In_Name; extern char Add_Serial_In_Name; /* we use the default libusb context */ #define ctx NULL pthread_mutex_t usbNotifierMutex; static pthread_t usbNotifyThread; static int driverSize = -1; static char AraKiriHotPlug = FALSE; static int rescan_pipe[] = { -1, -1 }; extern int HPForceReaderPolling; /* values of ifdCapabilities bits */ #define IFD_GENERATE_HOTPLUG 1 /** * keep track of drivers in a dynamically allocated array */ static struct _driverTracker { unsigned int manuID; unsigned int productID; char *bundleName; char *libraryPath; char *readerName; int ifdCapabilities; char *CFBundleName; } *driverTracker = NULL; #define DRIVER_TRACKER_SIZE_STEP 8 /** * keep track of PCSCLITE_MAX_READERS_CONTEXTS simultaneous readers */ static struct _readerTracker { char status; char bus_device[BUS_DEVICE_STRSIZE]; /**< device name */ char *fullName; /**< full reader name (including serial number) */ } readerTracker[PCSCLITE_MAX_READERS_CONTEXTS]; static LONG HPAddHotPluggable(struct libusb_device *dev, struct libusb_device_descriptor desc, const char bus_device[], const struct libusb_interface *idesc, struct _driverTracker *driver, struct _driverTracker *classdriver); static LONG HPRemoveHotPluggable(int reader_index); static LONG HPReadBundleValues(void) { LONG rv; DIR *hpDir; struct dirent *currFP = NULL; char fullPath[FILENAME_MAX]; char fullLibPath[FILENAME_MAX]; int listCount = 0; hpDir = opendir(PCSCLITE_HP_DROPDIR); if (hpDir == NULL) { Log1(PCSC_LOG_ERROR, "Cannot open PC/SC drivers directory: " PCSCLITE_HP_DROPDIR); Log1(PCSC_LOG_ERROR, "Disabling USB support for pcscd."); return -1; } /* allocate a first array */ driverTracker = calloc(DRIVER_TRACKER_SIZE_STEP, sizeof(*driverTracker)); if (NULL == driverTracker) { Log1(PCSC_LOG_CRITICAL, "Not enough memory"); return -1; } driverSize = DRIVER_TRACKER_SIZE_STEP; #define GET_KEY(key, values) \ rv = LTPBundleFindValueWithKey(&plist, key, values); \ if (rv) \ { \ Log2(PCSC_LOG_ERROR, "Value/Key not defined for " key " in %s", \ fullPath); \ continue; \ } while ((currFP = readdir(hpDir)) != 0) { if (strstr(currFP->d_name, ".bundle") != 0) { unsigned int alias; list_t plist, *values; list_t *manuIDs, *productIDs, *readerNames; char *libraryPath; int ifdCapabilities; char *CFBundleName; /* * The bundle exists - let's form a full path name and get the * vendor and product ID's for this particular bundle */ snprintf(fullPath, sizeof(fullPath), "%s/%s/Contents/Info.plist", PCSCLITE_HP_DROPDIR, currFP->d_name); fullPath[sizeof(fullPath) - 1] = '\0'; rv = bundleParse(fullPath, &plist); if (rv) continue; /* get CFBundleExecutable */ GET_KEY(PCSCLITE_HP_LIBRKEY_NAME, &values) libraryPath = list_get_at(values, 0); (void)snprintf(fullLibPath, sizeof(fullLibPath), "%s/%s/Contents/%s/%s", PCSCLITE_HP_DROPDIR, currFP->d_name, PCSC_ARCH, libraryPath); fullLibPath[sizeof(fullLibPath) - 1] = '\0'; /* Get ifdCapabilities */ GET_KEY(PCSCLITE_HP_CPCTKEY_NAME, &values) ifdCapabilities = strtol(list_get_at(values, 0), NULL, 16); GET_KEY(PCSCLITE_HP_MANUKEY_NAME, &manuIDs) GET_KEY(PCSCLITE_HP_PRODKEY_NAME, &productIDs) GET_KEY(PCSCLITE_HP_NAMEKEY_NAME, &readerNames) /* Get CFBundleName */ rv = LTPBundleFindValueWithKey(&plist, PCSCLITE_HP_CFBUNDLE_NAME, &values); if (rv) CFBundleName = NULL; else CFBundleName = strdup(list_get_at(values, 0)); /* while we find a nth ifdVendorID in Info.plist */ for (alias=0; aliasd_name); driverTracker[listCount].libraryPath = strdup(fullLibPath); driverTracker[listCount].ifdCapabilities = ifdCapabilities; driverTracker[listCount].CFBundleName = CFBundleName; #ifdef DEBUG_HOTPLUG Log2(PCSC_LOG_INFO, "Found driver for: %s", driverTracker[listCount].readerName); #endif listCount++; if (listCount >= driverSize) { int i; /* increase the array size */ driverSize += DRIVER_TRACKER_SIZE_STEP; #ifdef DEBUG_HOTPLUG Log2(PCSC_LOG_INFO, "Increase driverTracker to %d entries", driverSize); #endif void* tmp = realloc(driverTracker, driverSize * sizeof(*driverTracker)); if (NULL == tmp) { free(driverTracker); Log1(PCSC_LOG_CRITICAL, "Not enough memory"); driverSize = -1; closedir(hpDir); return -1; } driverTracker = tmp; /* clean the newly allocated entries */ for (i=driverSize-DRIVER_TRACKER_SIZE_STEP; ibNumInterfaces; interface++) { int newreader; /* A known device has been found */ snprintf(bus_device, BUS_DEVICE_STRSIZE, "%d:%d:%d", bus_number, device_address, interface); bus_device[BUS_DEVICE_STRSIZE - 1] = '\0'; newreader = TRUE; /* Check if the reader is a new one */ for (j=0; jinterface[interface], driver, classdriver); } libusb_free_config_descriptor(config_desc); } /* * check if all the previously found readers are still present */ for (i=0; i 0) { Log1(PCSC_LOG_INFO, "Reload serial configuration"); HPRescanUsbBus(); #ifdef USE_SERIAL RFReCheckReaderConf(); #endif Log1(PCSC_LOG_INFO, "End reload serial configuration"); } close(rescan_pipe[0]); rescan_pipe[0] = -1; } return NULL; } LONG HPSearchHotPluggables(void) { int i; for (i=0; i 0) { int pipefd[2]; char c; if (pipe(pipefd) == -1) { Log2(PCSC_LOG_ERROR, "pipe: %s", strerror(errno)); return -1; } ThreadCreate(&usbNotifyThread, THREAD_ATTR_DETACHED, (PCSCLITE_THREAD_FUNCTION( )) HPEstablishUSBNotifications, pipefd); /* Wait for initial readers to setup */ if (read(pipefd[0], &c, 1) == -1) { Log2(PCSC_LOG_ERROR, "read: %s", strerror(errno)); return -1; }; /* cleanup pipe fd */ close(pipefd[0]); close(pipefd[1]); } return 0; } LONG HPStopHotPluggables(void) { AraKiriHotPlug = TRUE; if (rescan_pipe[1] >= 0) { close(rescan_pipe[1]); rescan_pipe[1] = -1; } return 0; } static LONG HPAddHotPluggable(struct libusb_device *dev, struct libusb_device_descriptor desc, const char bus_device[], const struct libusb_interface *idesc, struct _driverTracker *driver, struct _driverTracker *classdriver) { int i; uint8_t iInterface = 0; uint8_t iSerialNumber = 0; char deviceName[MAX_DEVICENAME]; Log2(PCSC_LOG_INFO, "Adding USB device: %s", bus_device); snprintf(deviceName, sizeof(deviceName), "usb:%04x/%04x:libusb-1.0:%s", desc.idVendor, desc.idProduct, bus_device); deviceName[sizeof(deviceName) -1] = '\0'; pthread_mutex_lock(&usbNotifierMutex); /* find a free entry */ for (i=0; inum_altsetting > 0) iInterface = idesc->altsetting[0].iInterface; if (Add_Serial_In_Name) iSerialNumber = desc.iSerialNumber; if (iSerialNumber != 0 || iInterface != 0) { libusb_device_handle *device; int ret; ret = libusb_open(dev, &device); if (ret < 0) { Log2(PCSC_LOG_ERROR, "libusb_open failed: %s", libusb_error_name(ret)); } else { unsigned char interfaceName[MAX_READERNAME]; unsigned char serialNumber[MAX_READERNAME]; char fullname[MAX_READERNAME * 3]; fullname[0] = '\0'; int ret_interface = 0; int ret_serial = 0; if (iInterface) { ret_interface = libusb_get_string_descriptor_ascii(device, iInterface, interfaceName, sizeof interfaceName); if (ret_interface < 0) { Log2(PCSC_LOG_ERROR, "libusb_get_string_descriptor_ascii failed: %s", libusb_error_name(ret_interface)); } } if (iSerialNumber) { ret_serial = libusb_get_string_descriptor_ascii(device, iSerialNumber, serialNumber, sizeof serialNumber); if (ret_serial < 0) { Log2(PCSC_LOG_ERROR, "libusb_get_string_descriptor_ascii failed: %s", libusb_error_name(ret_serial)); } } libusb_close(device); if (ret_interface > 0 && ret_serial > 0) { snprintf(fullname, sizeof(fullname), "%s [%s] (%s)", driver->readerName, interfaceName, serialNumber); } else { if (ret_interface > 0) { snprintf(fullname, sizeof(fullname), "%s [%s]", driver->readerName, interfaceName); } else { if (ret_serial > 0) { snprintf(fullname, sizeof(fullname), "%s (%s)", driver->readerName, serialNumber); } } } if (fullname[0] != '\0') readerTracker[i].fullName = strdup(fullname); } } if (readerTracker[i].fullName == NULL) readerTracker[i].fullName = strdup(driver->readerName); LONG ret; ret = RFAddReader(readerTracker[i].fullName, PCSCLITE_HP_BASE_PORT + i, driver->libraryPath, deviceName); /* success by default */ readerTracker[i].status = READER_PRESENT; if ((SCARD_S_SUCCESS != ret) && (SCARD_E_UNKNOWN_READER != ret)) { Log2(PCSC_LOG_ERROR, "Failed adding USB device: %s", driver->readerName); if (classdriver && driver != classdriver) { /* the reader can also be used by the a class driver */ ret = RFAddReader(readerTracker[i].fullName, PCSCLITE_HP_BASE_PORT + i, classdriver->libraryPath, deviceName); if ((SCARD_S_SUCCESS != ret) && (SCARD_E_UNKNOWN_READER != ret)) { Log2(PCSC_LOG_ERROR, "Failed adding USB device: %s", driver->readerName); readerTracker[i].status = READER_FAILED; } } else readerTracker[i].status = READER_FAILED; } if (READER_FAILED == readerTracker[i].status) (void)CheckForOpenCT(); pthread_mutex_unlock(&usbNotifierMutex); return 1; } /* End of function */ static LONG HPRemoveHotPluggable(int reader_index) { pthread_mutex_lock(&usbNotifierMutex); Log3(PCSC_LOG_INFO, "Removing USB device[%d]: %s", reader_index, readerTracker[reader_index].bus_device); RFRemoveReader(readerTracker[reader_index].fullName, PCSCLITE_HP_BASE_PORT + reader_index); free(readerTracker[reader_index].fullName); readerTracker[reader_index].status = READER_ABSENT; readerTracker[reader_index].bus_device[0] = '\0'; readerTracker[reader_index].fullName = NULL; pthread_mutex_unlock(&usbNotifierMutex); return 1; } /* End of function */ /** * Sets up callbacks for device hotplug events. */ ULONG HPRegisterForHotplugEvents(void) { (void)pthread_mutex_init(&usbNotifierMutex, NULL); return 0; } void HPReCheckSerialReaders(void) { Log0(PCSC_LOG_INFO); if (rescan_pipe[1] >= 0) { char dummy = 0; if (write(rescan_pipe[1], &dummy, sizeof(dummy)) == -1) Log2(PCSC_LOG_ERROR, "write: %s", strerror(errno)); } } #endif