1/* 2 * Copyright (c) 2006 Apple Computer, Inc. All Rights Reserved. 3 * 4 * @APPLE_LICENSE_HEADER_START@ 5 * 6 * This file contains Original Code and/or Modifications of Original Code 7 * as defined in and that are subject to the Apple Public Source License 8 * Version 2.0 (the 'License'). You may not use this file except in 9 * compliance with the License. Please obtain a copy of the License at 10 * http://www.opensource.apple.com/apsl/ and read it before using this 11 * file. 12 * 13 * The Original Code and all software distributed under the License are 14 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER 15 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, 16 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, 17 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. 18 * Please see the License for the specific language governing rights and 19 * limitations under the License. 20 * 21 * @APPLE_LICENSE_HEADER_END@ 22 */ 23 24/* 25 * PCSCDriverBundle.cpp 26 * SmartCardServices 27 */ 28 29/* 30 A driver bundle is a standard Mac OS X bundle that usually lives in the directory: 31 32 /usr/libexec/SmartCardServices/drivers/ 33 34 The two major components of this bundle are the executable and the Info.plist. A single 35 driver bundle may provide support for multiple readers. See 36 37 <rdar://problem/4432039> pcscd crash for multiple VID/PIDs 38 and 39 <http://pcsclite.alioth.debian.org/ifdhandler-3/node7.html> 40 41 The key that determines if a driver supports only one reader or multiple readers is 42 "ifdVendorID", sometimes referred to as the manufacturer name. If this is a 43 CFStringRef, then only one reader is supported; if it is a CFArrayRef, then 44 multiple readers are supports. There are three fields for each reader: 45 46 VendorID uint32_t 47 ProductID uint32_t 48 Friendly name string 49 50 See e.g. http://pcsclite.alioth.debian.org/ccid.html for a working driver with multiple IDs. 51 52*/ 53 54#include "PCSCDriverBundle.h" 55#include <CoreFoundation/CoreFoundation.h> 56#include <security_utilities/cfutilities.h> 57#include <security_utilities/debugging.h> 58#include <security_utilities/errors.h> 59#include <IOKit/usb/USBSpec.h> 60#include <IOKit/usb/USB.h> 61 62#define DEBUG_BUNDLE_MATCHES 1 63 64namespace PCSCD { 65 66// Keys in CFDictionary for bundle's Info.plist 67static const CFStringRef kManufacturerName = CFSTR("ifdVendorID"); 68static const CFStringRef kProductName = CFSTR("ifdProductID"); 69static const CFStringRef kFriendlyName = CFSTR("ifdFriendlyName"); 70static const CFStringRef kInterfaceClass = CFSTR("ifdInterfaceClass"); 71static const CFStringRef kInterfaceSubClass = CFSTR("ifdInterfaceSubClass"); 72static const CFStringRef kInterfaceProtocol = CFSTR("ifdInterfaceProtocol"); 73 74DriverBundle::DriverBundle(CFBundleRef bundle) : LoadableBundle(bundle) 75{ 76 initialize(CFBundleGetInfoDictionary(bundle)); 77} 78 79void DriverBundle::initialize(CFDictionaryRef dict) 80{ 81 const int radix = 16; 82 83 try 84 { 85 CFTypeRef vend = CFDictionaryGetValue(dict, kManufacturerName); 86 if (!vend) 87 { 88 // Must be a class driver 89 secdebug("pcscd", "Class Driver: %s", path().c_str()); 90 std::string istr(getStringAttr(dict,kInterfaceClass)); 91 uint8_t dclass = strtoul(istr.c_str(), NULL, radix); 92 std::string sstr(getStringAttr(dict,kInterfaceSubClass)); 93 uint8_t dsubclass = strtoul(sstr.c_str(), NULL, radix); 94 std::string pstr(getStringAttr(dict,kInterfaceProtocol)); 95 uint8_t dprotocol = strtoul(pstr.c_str(), NULL, radix); 96 std::string name(getStringAttr(dict,kFriendlyName)); 97 DeviceDescription *dev = new DeviceDescription(dclass, dsubclass, dprotocol, name); 98 addProduct(dev); 99 } 100 else 101 if (CFGetTypeID(vend) == CFArrayGetTypeID()) 102 { 103 secdebug("pcscd", "Driver with aliases: %s", path().c_str()); 104 CFTypeRef xprod = CFDictionaryGetValue(dict, kProductName); 105 CFTypeRef xname = CFDictionaryGetValue(dict, kFriendlyName); 106 if (!xprod || !xname || 107 (CFGetTypeID(xprod) != CFArrayGetTypeID()) || (CFGetTypeID(xname) != CFArrayGetTypeID())) 108 CFError::throwMe(); 109 CFRef<CFArrayRef> products(reinterpret_cast<CFArrayRef>(xprod)); 110 CFRef<CFArrayRef> names (reinterpret_cast<CFArrayRef>(xname)); 111 const int productCount = CFArrayGetCount(reinterpret_cast<CFArrayRef>(vend)); 112 // Make sure parallel arrays vendor, product, name are same size 113 if ((productCount != CFArrayGetCount(products)) || 114 (productCount != CFArrayGetCount(names))) 115 CFError::throwMe(); 116 117 for (int ix=0;ix<productCount;++ix) 118 { 119 std::string vstr(getStringAttr(reinterpret_cast<CFArrayRef>(vend), ix)); 120 uint16_t vendor = strtoul(vstr.c_str(), NULL, radix); 121 std::string pstr(getStringAttr(products, ix)); 122 uint16_t product = strtoul(pstr.c_str(), NULL, radix); 123 std::string name(getStringAttr(names, ix)); 124 DeviceDescription *dev = new DeviceDescription(vendor, product, name); 125 addProduct(dev); 126 } 127 } 128 else 129 if (CFGetTypeID(vend) == CFStringGetTypeID()) 130 { 131 secdebug("pcscd", "Driver for single product: %s", path().c_str()); 132 std::string vstr(cfString(reinterpret_cast<CFStringRef>(vend))); 133 uint16_t vendor = strtoul(vstr.c_str(), NULL, radix); 134 std::string pstr(getStringAttr(dict,kProductName)); 135 uint16_t product = strtoul(pstr.c_str(), NULL, radix); 136 std::string name(getStringAttr(dict,kFriendlyName)); 137 DeviceDescription *dev = new DeviceDescription(vendor, product, name); 138 addProduct(dev); 139 } 140 else 141 CFError::throwMe(); 142 } 143 catch (...) 144 { 145 secdebug("pcscd", "Malformed Info.plist for: %s", path().c_str()); 146 secdebug("pcscd", "error getting plugin directory bundles"); 147 return; 148 } 149 150 dump(); 151} 152 153std::string DriverBundle::getStringAttr(CFDictionaryRef dict, CFStringRef key) 154{ 155 // Do some sanity checking on potential string values in the plist 156 CFTypeRef attr = CFDictionaryGetValue(dict, key); 157 if (!attr) 158 return std::string(); 159 if (CFGetTypeID(attr) != CFStringGetTypeID()) 160 CFError::throwMe(); 161 162 return std::string(cfString(static_cast<CFStringRef>(attr))); 163} 164 165std::string DriverBundle::getStringAttr(CFArrayRef arr, CFIndex idx) 166{ 167 // Do some sanity checking on potential string values in the plist 168 CFTypeRef attr = CFArrayGetValueAtIndex(arr, idx); 169 if (!attr) 170 return std::string(); 171 if (CFGetTypeID(attr) != CFStringGetTypeID()) 172 CFError::throwMe(); 173 174 return std::string(cfString(static_cast<CFStringRef>(attr))); 175} 176 177DriverBundle::~DriverBundle() throw() 178{ 179 // delete supported devices objects 180} 181 182uint32_t DriverBundle::matches(const PCSCD::Device &device, std::string &name) const 183{ 184 // Searches for a driver bundle that matches device. If found, 185 // it sets the libpath for the device and returns true. 186 187#ifdef DEBUG_BUNDLE_MATCHES 188 secdebug("device", " DEVICE: vendor/product: 0x%04X/0x%04X, interfaceClass: 0x%04X, vendor/product: %s/%s", 189 device.vendorid(), device.productid(), device.interfaceClass(), 190 device.vendorName().c_str(), device.productName().c_str()); 191#endif 192 193 // Look for a manufacturer-specific driver first 194 for (ConstDeviceDescriptionIterator it=mDeviceDescriptions.begin();it!=mDeviceDescriptions.end();++it) 195 { 196 const DeviceDescription *desc = static_cast<DeviceDescription *>(*it); 197#ifdef DEBUG_BUNDLE_MATCHES 198 secdebug("device", " DESC: vendor/product: 0x%04X/0x%04X, interfaceClass: 0x%04X, path: %s", 199 desc->vendorid(), desc->productid(), desc->interfaceClass(), path().c_str()); 200#endif 201 if (desc->vendorid() && (desc->vendorid()==device.vendorid()) && 202 desc->productid() && (desc->productid()==device.productid())) 203 { 204 name = desc->name(); 205 return eMatchVendorSpecific; 206 } 207 } 208 209 if (device.interfaceClass()) 210 for (ConstDeviceDescriptionIterator it=mDeviceDescriptions.begin();it!=mDeviceDescriptions.end();++it) 211 { 212 const DeviceDescription *desc = static_cast<DeviceDescription *>(*it); 213 if (desc->interfaceClass() && (desc->interfaceClass()==device.interfaceClass())) 214 { 215 name = desc->name(); 216 return eMatchInterfaceClass; 217 } 218 } 219 220 return eMatchNone; 221} 222 223#pragma mark -------------------- Operators -------------------- 224 225bool DriverBundle::operator < (const DriverBundle &other) const throw() 226{ 227 return this->path() < other.path(); 228} 229 230bool DeviceDescription::operator < (const DeviceDescription &other) const throw() 231{ 232 if (this->mVendor >= other.mVendor) 233 return false; 234 235 return (this->mProduct < other.mProduct); 236} 237 238#pragma mark -------------------- Debugging Routines -------------------- 239 240void DriverBundle::dump() 241{ 242#ifndef NDEBUG 243 secdebug("pcscd", "Driver at path: %s", path().c_str()); 244 for (DeviceDescriptionIterator it = mDeviceDescriptions.begin(); it != mDeviceDescriptions.end();++it) 245 (*it)->dump(); 246#endif 247} 248 249void DeviceDescription::dump() 250{ 251#ifndef NDEBUG 252 secdebug("pcscd", " Friendly name: %s", mFriendlyName.c_str()); 253 if (interfaceClass()) 254 secdebug("pcscd", " Class: 0x%02X SubClass: 0x%02X Protocol: 0x%02X", 255 mDeviceClass,mDeviceSubClass,mDeviceProtocol); 256 else 257 secdebug("pcscd", " VendorID: 0x%04X ProductID: 0x%04X", mVendor, mProduct); 258#endif 259} 260 261} // end namespace PCSCD 262