/* * Copyright (c) 2006 Apple Computer, Inc. All Rights Reserved. * * @APPLE_LICENSE_HEADER_START@ * * This file contains Original Code and/or Modifications of Original Code * as defined in and that are subject to the Apple Public Source License * Version 2.0 (the 'License'). You may not use this file except in * compliance with the License. Please obtain a copy of the License at * http://www.opensource.apple.com/apsl/ and read it before using this * file. * * The Original Code and all software distributed under the License are * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. * Please see the License for the specific language governing rights and * limitations under the License. * * @APPLE_LICENSE_HEADER_END@ */ /* * PCSCDriverBundle.cpp * SmartCardServices */ /* A driver bundle is a standard Mac OS X bundle that usually lives in the directory: /usr/libexec/SmartCardServices/drivers/ The two major components of this bundle are the executable and the Info.plist. A single driver bundle may provide support for multiple readers. See pcscd crash for multiple VID/PIDs and The key that determines if a driver supports only one reader or multiple readers is "ifdVendorID", sometimes referred to as the manufacturer name. If this is a CFStringRef, then only one reader is supported; if it is a CFArrayRef, then multiple readers are supports. There are three fields for each reader: VendorID uint32_t ProductID uint32_t Friendly name string See e.g. http://pcsclite.alioth.debian.org/ccid.html for a working driver with multiple IDs. */ #include "PCSCDriverBundle.h" #include #include #include #include #include #include #define DEBUG_BUNDLE_MATCHES 1 namespace PCSCD { // Keys in CFDictionary for bundle's Info.plist static const CFStringRef kManufacturerName = CFSTR("ifdVendorID"); static const CFStringRef kProductName = CFSTR("ifdProductID"); static const CFStringRef kFriendlyName = CFSTR("ifdFriendlyName"); static const CFStringRef kInterfaceClass = CFSTR("ifdInterfaceClass"); static const CFStringRef kInterfaceSubClass = CFSTR("ifdInterfaceSubClass"); static const CFStringRef kInterfaceProtocol = CFSTR("ifdInterfaceProtocol"); DriverBundle::DriverBundle(CFBundleRef bundle) : LoadableBundle(bundle) { initialize(CFBundleGetInfoDictionary(bundle)); } void DriverBundle::initialize(CFDictionaryRef dict) { const int radix = 16; try { CFTypeRef vend = CFDictionaryGetValue(dict, kManufacturerName); if (!vend) { // Must be a class driver secdebug("pcscd", "Class Driver: %s", path().c_str()); std::string istr(getStringAttr(dict,kInterfaceClass)); uint8_t dclass = strtoul(istr.c_str(), NULL, radix); std::string sstr(getStringAttr(dict,kInterfaceSubClass)); uint8_t dsubclass = strtoul(sstr.c_str(), NULL, radix); std::string pstr(getStringAttr(dict,kInterfaceProtocol)); uint8_t dprotocol = strtoul(pstr.c_str(), NULL, radix); std::string name(getStringAttr(dict,kFriendlyName)); DeviceDescription *dev = new DeviceDescription(dclass, dsubclass, dprotocol, name); addProduct(dev); } else if (CFGetTypeID(vend) == CFArrayGetTypeID()) { secdebug("pcscd", "Driver with aliases: %s", path().c_str()); CFTypeRef xprod = CFDictionaryGetValue(dict, kProductName); CFTypeRef xname = CFDictionaryGetValue(dict, kFriendlyName); if (!xprod || !xname || (CFGetTypeID(xprod) != CFArrayGetTypeID()) || (CFGetTypeID(xname) != CFArrayGetTypeID())) CFError::throwMe(); CFRef products(reinterpret_cast(xprod)); CFRef names (reinterpret_cast(xname)); const int productCount = CFArrayGetCount(reinterpret_cast(vend)); // Make sure parallel arrays vendor, product, name are same size if ((productCount != CFArrayGetCount(products)) || (productCount != CFArrayGetCount(names))) CFError::throwMe(); for (int ix=0;ix(vend), ix)); uint16_t vendor = strtoul(vstr.c_str(), NULL, radix); std::string pstr(getStringAttr(products, ix)); uint16_t product = strtoul(pstr.c_str(), NULL, radix); std::string name(getStringAttr(names, ix)); DeviceDescription *dev = new DeviceDescription(vendor, product, name); addProduct(dev); } } else if (CFGetTypeID(vend) == CFStringGetTypeID()) { secdebug("pcscd", "Driver for single product: %s", path().c_str()); std::string vstr(cfString(reinterpret_cast(vend))); uint16_t vendor = strtoul(vstr.c_str(), NULL, radix); std::string pstr(getStringAttr(dict,kProductName)); uint16_t product = strtoul(pstr.c_str(), NULL, radix); std::string name(getStringAttr(dict,kFriendlyName)); DeviceDescription *dev = new DeviceDescription(vendor, product, name); addProduct(dev); } else CFError::throwMe(); } catch (...) { secdebug("pcscd", "Malformed Info.plist for: %s", path().c_str()); secdebug("pcscd", "error getting plugin directory bundles"); return; } dump(); } std::string DriverBundle::getStringAttr(CFDictionaryRef dict, CFStringRef key) { // Do some sanity checking on potential string values in the plist CFTypeRef attr = CFDictionaryGetValue(dict, key); if (!attr) return std::string(); if (CFGetTypeID(attr) != CFStringGetTypeID()) CFError::throwMe(); return std::string(cfString(static_cast(attr))); } std::string DriverBundle::getStringAttr(CFArrayRef arr, CFIndex idx) { // Do some sanity checking on potential string values in the plist CFTypeRef attr = CFArrayGetValueAtIndex(arr, idx); if (!attr) return std::string(); if (CFGetTypeID(attr) != CFStringGetTypeID()) CFError::throwMe(); return std::string(cfString(static_cast(attr))); } DriverBundle::~DriverBundle() throw() { // delete supported devices objects } uint32_t DriverBundle::matches(const PCSCD::Device &device, std::string &name) const { // Searches for a driver bundle that matches device. If found, // it sets the libpath for the device and returns true. #ifdef DEBUG_BUNDLE_MATCHES secdebug("device", " DEVICE: vendor/product: 0x%04X/0x%04X, interfaceClass: 0x%04X, vendor/product: %s/%s", device.vendorid(), device.productid(), device.interfaceClass(), device.vendorName().c_str(), device.productName().c_str()); #endif // Look for a manufacturer-specific driver first for (ConstDeviceDescriptionIterator it=mDeviceDescriptions.begin();it!=mDeviceDescriptions.end();++it) { const DeviceDescription *desc = static_cast(*it); #ifdef DEBUG_BUNDLE_MATCHES secdebug("device", " DESC: vendor/product: 0x%04X/0x%04X, interfaceClass: 0x%04X, path: %s", desc->vendorid(), desc->productid(), desc->interfaceClass(), path().c_str()); #endif if (desc->vendorid() && (desc->vendorid()==device.vendorid()) && desc->productid() && (desc->productid()==device.productid())) { name = desc->name(); return eMatchVendorSpecific; } } if (device.interfaceClass()) for (ConstDeviceDescriptionIterator it=mDeviceDescriptions.begin();it!=mDeviceDescriptions.end();++it) { const DeviceDescription *desc = static_cast(*it); if (desc->interfaceClass() && (desc->interfaceClass()==device.interfaceClass())) { name = desc->name(); return eMatchInterfaceClass; } } return eMatchNone; } #pragma mark -------------------- Operators -------------------- bool DriverBundle::operator < (const DriverBundle &other) const throw() { return this->path() < other.path(); } bool DeviceDescription::operator < (const DeviceDescription &other) const throw() { if (this->mVendor >= other.mVendor) return false; return (this->mProduct < other.mProduct); } #pragma mark -------------------- Debugging Routines -------------------- void DriverBundle::dump() { #ifndef NDEBUG secdebug("pcscd", "Driver at path: %s", path().c_str()); for (DeviceDescriptionIterator it = mDeviceDescriptions.begin(); it != mDeviceDescriptions.end();++it) (*it)->dump(); #endif } void DeviceDescription::dump() { #ifndef NDEBUG secdebug("pcscd", " Friendly name: %s", mFriendlyName.c_str()); if (interfaceClass()) secdebug("pcscd", " Class: 0x%02X SubClass: 0x%02X Protocol: 0x%02X", mDeviceClass,mDeviceSubClass,mDeviceProtocol); else secdebug("pcscd", " VendorID: 0x%04X ProductID: 0x%04X", mVendor, mProduct); #endif } } // end namespace PCSCD