PCSCDriverBundle.cpp [plain text]
#include "PCSCDriverBundle.h"
#include <CoreFoundation/CoreFoundation.h>
#include <security_utilities/cfutilities.h>
#include <security_utilities/debugging.h>
#include <security_utilities/errors.h>
#include <IOKit/usb/USBSpec.h>
#include <IOKit/usb/USB.h>
#define DEBUG_BUNDLE_MATCHES 1
namespace PCSCD {
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)
{
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<CFArrayRef> products(reinterpret_cast<CFArrayRef>(xprod));
CFRef<CFArrayRef> names (reinterpret_cast<CFArrayRef>(xname));
const int productCount = CFArrayGetCount(reinterpret_cast<CFArrayRef>(vend));
if ((productCount != CFArrayGetCount(products)) ||
(productCount != CFArrayGetCount(names)))
CFError::throwMe();
for (int ix=0;ix<productCount;++ix)
{
std::string vstr(getStringAttr(reinterpret_cast<CFArrayRef>(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<CFStringRef>(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)
{
CFTypeRef attr = CFDictionaryGetValue(dict, key);
if (!attr)
return std::string();
if (CFGetTypeID(attr) != CFStringGetTypeID())
CFError::throwMe();
return std::string(cfString(static_cast<CFStringRef>(attr)));
}
std::string DriverBundle::getStringAttr(CFArrayRef arr, CFIndex idx)
{
CFTypeRef attr = CFArrayGetValueAtIndex(arr, idx);
if (!attr)
return std::string();
if (CFGetTypeID(attr) != CFStringGetTypeID())
CFError::throwMe();
return std::string(cfString(static_cast<CFStringRef>(attr)));
}
DriverBundle::~DriverBundle() throw()
{
}
uint32_t DriverBundle::matches(const PCSCD::Device &device, std::string &name) const
{
#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
for (ConstDeviceDescriptionIterator it=mDeviceDescriptions.begin();it!=mDeviceDescriptions.end();++it)
{
const DeviceDescription *desc = static_cast<DeviceDescription *>(*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<DeviceDescription *>(*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
}
}