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