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//
26// pcscmonitor - use PCSC to monitor smartcard reader/card state for securityd
27//
28// PCSCDMonitor is the "glue" between PCSC and the securityd objects representing
29// smartcard-related things. Its job is to manage the daemon and translate real-world
30// events (such as card and device insertions) into the securityd object web.
31//
32// PCSCDMonitor uses multiple inheritance to the hilt. It is (among others)
33//	(*) A notification listener, to listen to pcscd state notifications
34//  (*) A MachServer::Timer, to handle timed actions
35//  (*) A NotificationPort::Receiver, to get IOKit notifications of device insertions
36//
37
38#include "pcscdmonitor.h"
39#include <security_utilities/logging.h>
40#include <security_utilities/refcount.h>
41#include <IOKit/usb/IOUSBLib.h>
42#include <IOKit/IOMessage.h>
43#include <asl.h>
44//#include <Kernel/IOKit/pccard/IOPCCardBridge.h>
45//#include <Kernel/IOKit/pccard/cs.h>
46
47#ifndef _IOKIT_IOPCCARDBRIDGE_H
48// Avoid kernel header include
49#define kIOPCCardVersionOneMatchKey			"VersionOneInfo"
50#define kIOPCCardFunctionNameMatchKey		"FunctionName"
51#define kIOPCCardFunctionIDMatchKey			"FunctionID"
52#define kIOPCCardVendorIDMatchKey			"VendorID"
53#define kIOPCCardDeviceIDMatchKey			"DeviceID"
54#define kIOPCCardFunctionExtensionMatchKey	"FunctionExtension"
55#define kIOPCCardMemoryDeviceNameMatchKey	"MemoryDeviceName"
56
57// this should be unique across the entire system
58#define sub_iokit_pccard        err_sub(21)
59#define kIOPCCardCSEventMessage iokit_family_msg(sub_iokit_pccard, 1)
60#endif /*  _IOKIT_IOPCCARDBRIDGE_H */
61
62// _LINUX_CS_H
63#define CS_EVENT_CARD_INSERTION		0x000004
64#define CS_EVENT_CARD_REMOVAL		0x000008
65#define CS_EVENT_EJECTION_REQUEST	0x010000
66
67// Locally defined string constants for IOKit values
68
69#define kzIOUSBSerialNumberKey				"Serial Number"
70#define kzIOUSBVendorNameKey				"USB Vendor Name"
71#define kzIOUSBProductNameKey				"USB Product Name"
72#define kzIOUSBLocationIDKey				"locationID"
73#define kzIOUSBbInterfaceClassKey			"bInterfaceClass"
74#define kzIOUSBbDeviceClassKey				"bDeviceClass"
75
76#define kzIOPCCardIONameKey					"IOName"
77#define kzIOPCCardIODeviceMemoryKey			"IODeviceMemory"
78#define kzIOPCCardParentKey					"parent"
79#define kzIOPCCardAddressKey				"address"
80
81#define kzIOPCCard16DeviceClassName			"IOPCCard16Device"
82
83#define PTRPARAMCAST(X)				(static_cast<unsigned int>(reinterpret_cast<uintptr_t>(X)))
84
85//
86// Fixed configuration parameters
87//
88static const Time::Interval PCSCD_IDLE_SHUTDOWN(120);		// kill daemon if no devices present
89
90// Apple built-in iSight Device VendorID/ProductID: 0x05AC/0x8501
91
92static const uint32_t kVendorProductMask = 0x0000FFFF;
93static const uint32_t kVendorIDApple = 0x05AC;
94static const uint16_t kProductIDBuiltInISight = 0x8501;
95
96/*
97	Copied from USBVideoClass-230.2.3/Digitizers/USBVDC/Camera/USBClient/APW_VDO_USBVDC_USBClient.h
98*/
99
100enum {
101	kBuiltIniSightProductID = 0x8501,
102	kBuiltIniSightWave2ProductID = 0x8502,
103	kBuiltIniSightWave3ProductID = 0x8505,
104	kUSBWave4ProductID        = 0x8507,
105	kUSBWave2InK29ProductID        = 0x8508,
106	kUSBWaveReserved1ProductID        = 0x8509,
107	kUSBWaveReserved2ProductID        = 0x850a,
108	kExternaliSightProductID = 0x1111,
109	kLogitechVendorID = 0x046d
110};
111
112//static void dumpdictentry(const void *key, const void *value, void *context);
113
114#pragma mark -------------------- Class Methods --------------------
115
116//
117// Construct a PCSCDMonitor.
118// We strongly assume there's only one of us around here.
119//
120// Note that this constructor may well run before the server loop has started.
121// Don't call anything here that requires an active server loop (like Server::active()).
122// In fact, you should push all the hard work into a timer, so as not to hold up the
123// general startup process.
124//
125
126PCSCDMonitor::PCSCDMonitor(PCSCD::Server &server, PCSCD::DriverBundles &drivers) :
127	MachPlusPlus::MachServer::Timer(true),			// "heavy" timer task
128	server(server),
129	drivers(drivers),
130	mAddDeviceCallback(NULL), mRemoveDeviceCallback(NULL),
131	mWillSleepCallback(NULL), mIsWakingCallback(NULL),
132	mTimerAction(&PCSCDMonitor::initialSetup),
133	mGoingToSleep(false),
134	mTerminationNoticeReceiver(*this),
135	mSleepWakePeriod(false),
136	mWakeConditionVariable(mWakeConditionLock)
137{
138	// do all the smartcard-related work once the event loop has started
139	secdebug("pcsc", "PCSCDMonitor server is %p", &server);
140	server.setTimer(this, Time::now());				// ASAP
141	// timer only used now to call initialSetup
142	mDevices.erase(mDevices.begin(),mDevices.end());
143}
144
145//
146// Power event notifications
147//
148void PCSCDMonitor::systemWillSleep()
149{
150	StLock<Mutex> _(mLock);
151	secdebug("pcsc", "setting sleep marker (%ld readers as of now)", mDevices.size());
152	mGoingToSleep = true;
153	server.clearTimer(this);
154	if (mWillSleepCallback)
155	{
156		uint32_t rx = (*mWillSleepCallback)();
157		secdebug("pcsc", "  WillSleepCallback returned %d", rx);
158	}
159	setSystemIsAwakeCondition(false);
160}
161
162void PCSCDMonitor::systemIsWaking()
163{
164	StLock<Mutex> _(mLock);
165	secdebug("pcsc", "------------------ Waking from sleep ... ------------------ ");
166	secdebug("pcsc", "clearing sleep marker (%ld readers as of now)", mDevices.size());
167	mGoingToSleep = false;
168	// rescan here
169	if (mIsWakingCallback)
170	{
171		uint32_t rx = (*mIsWakingCallback)();
172		secdebug("pcsc", "  IsWakingCallback returned %d", rx);
173	}
174	setSystemIsAwakeCondition(true);
175}
176
177void PCSCDMonitor::setSystemIsAwakeCondition(bool isAwake)
178{
179	secdebug("pcsc", "  setSystemIsAwakeCondition %d", isAwake);
180	if (isAwake)
181	{
182		sleepWakePeriod(false);
183		mWakeConditionVariable.broadcast();
184	}
185	else
186		sleepWakePeriod(true);
187}
188
189bool PCSCDMonitor::isSleepWakePeriod() const
190{
191	StLock<Mutex> _(mSleepWakePeriodLock);
192	return mSleepWakePeriod;
193}
194
195void PCSCDMonitor::sleepWakePeriod(bool isASleepWakePeriod)
196{
197	StLock<Mutex> _(mSleepWakePeriodLock);
198	mSleepWakePeriod = isASleepWakePeriod;
199}
200
201void PCSCDMonitor::systemAwakeAndReadyCheck()
202{
203//	const long sleepTimeMSec = 100;	// 0.1s
204
205	StLock<Mutex> _(mWakeConditionLock);
206	while (isSleepWakePeriod())
207    {
208		secdebug("pcsc", "...### thread paused before waking ###...");
209		mWakeConditionVariable.wait();
210		secdebug("pcsc", "...### thread resume after waking ###...");
211	}
212}
213
214void PCSCDMonitor::action()
215{
216	// Timer action
217	StLock<Mutex> _(mLock);
218	secdebug("pcsc", "Calling PCSCDMonitor::action()");
219	(this->*mTimerAction)();
220	mTimerAction = &PCSCDMonitor::noDeviceTimeout;
221}
222
223void PCSCDMonitor::scheduleTimer(bool enable)
224{
225	// Update the timeout timer as requested (and indicated by context)
226}
227
228//
229// Perform the initial PCSC subsystem initialization.
230// This runs (shortly) after securityd is fully functional and the
231// server loop has started.
232//
233void PCSCDMonitor::initialSetup()
234{
235	secdebug("pcsc", "Calling PCSCDMonitor::initialSetup()");
236	// receive Mach-based IOKit notifications through mIOKitNotifier
237	server.add(mIOKitNotifier);
238
239	// receive power event notifications (through our IOPowerWatcher personality)
240	server.add(this);
241
242	AddIOKitNotifications();
243
244	PCSCDMonitor::postNotification(SecurityServer::kNotificationPCSCInitialized);
245}
246
247void PCSCDMonitor::AddIOKitNotifications()
248{
249	try
250	{
251		// ask for IOKit notifications for all new USB devices and process present ones
252		IOKit::DeviceMatch usbSelector(kIOUSBInterfaceClassName);
253		IOKit::DeviceMatch pcCardSelector(kzIOPCCard16DeviceClassName);
254		mIOKitNotifier.add(usbSelector, *this, kIOMatchedNotification);	// this will scan existing USB devices
255	//	mIOKitNotifier.add(usbSelector, mTerminationNoticeReceiver, kIOTerminatedNotification);	// ditto for PC Card devices
256		mIOKitNotifier.add(pcCardSelector, *this, kIOMatchedNotification);	// ditto for PC Card devices
257		mIOKitNotifier.add(pcCardSelector, mTerminationNoticeReceiver, kIOTerminatedNotification);	// ditto for PC Card devices
258
259		// catch custom non-composite USB devices - they don't have IOServices attached
260		IOKit::DeviceMatch customUsbSelector(::IOServiceMatching(kIOUSBDeviceClassName));
261		mIOKitNotifier.add(customUsbSelector, *this, kIOMatchedNotification);	// ditto for custom USB devices
262	//	mIOKitNotifier.add(customUsbSelector, mTerminationNoticeReceiver, kIOTerminatedNotification);
263	}
264	catch (...)
265	{
266		secdebug("pcscd", "trouble adding IOKit notifications (ignored)");
267	}
268}
269
270void PCSCDMonitor::RemoveIOKitNotifications()
271{
272}
273
274
275void PCSCDMonitor::rescanExistingDevices()
276{
277    kern_return_t kr;
278	mach_port_t masterPort = ((IOKit::NotificationPort)mIOKitNotifier).port();
279//	mach_port_t masterPort = port();
280	io_iterator_t iterator;
281
282	// Process existing USB devices
283	IOKit::DeviceMatch usbSelector(kIOUSBInterfaceClassName);
284	kr = IOServiceGetMatchingServices(masterPort, usbSelector, &iterator);
285	IOKit::DeviceIterator usbdev(iterator);
286	ioChange(usbdev);
287
288	// Process existing PC Card devices
289	IOKit::DeviceMatch pcCardSelector(kzIOPCCard16DeviceClassName);
290	kr = IOServiceGetMatchingServices(masterPort, pcCardSelector, &iterator);
291	IOKit::DeviceIterator pcdev(iterator);
292	ioChange(pcdev);
293
294	// catch custom non-composite USB devices - they don't have IOServices attached
295	IOKit::DeviceMatch customUsbSelector(::IOServiceMatching(kIOUSBDeviceClassName));
296	kr = IOServiceGetMatchingServices(masterPort, customUsbSelector, &iterator);
297	IOKit::DeviceIterator customusbdev(iterator);
298	ioChange(customusbdev);
299}
300
301void PCSCDMonitor::postNotification(const SecurityServer::NotificationEvent event)
302{
303	// send a change notification to securityd
304	// Either kNotificationPCSCStateChange or kNotificationPCSCInitialized
305	using namespace SecurityServer;
306	ClientSession session(Allocator::standard(), Allocator::standard());
307	try {
308		session.postNotification(kNotificationDomainPCSC, event, CssmData());
309		secdebug("pcscd", "notification sent");
310	} catch (const MachPlusPlus::Error &err) {
311		switch (err.error) {
312		case BOOTSTRAP_UNKNOWN_SERVICE: // securityd not yet available; this is not an error
313			secdebug("pcscd", "securityd not up; no notification sent");
314			break;
315#if !defined(NDEBUG)
316		// for debugging only, support a securityd restart. This is NOT thread-safe
317		case MACH_SEND_INVALID_DEST:
318			secdebug("pcscd", "resetting securityd connection for debugging");
319			session.reset();
320			try {
321				session.postNotification(kNotificationDomainPCSC,
322					kNotificationPCSCStateChange, CssmData());
323			} catch (...) {
324				secdebug("pcscd", "re-send attempt failed, punting");
325			}
326			break;
327#endif //NDEBUG
328		default:
329			secdebug("pcscd", "exception trying to send notification (ignored)");
330		}
331	} catch (...) {
332		secdebug("pcscd", "trouble sending security notification (ignored)");
333	}
334}
335
336//
337// This function is called (as a timer function) when there haven't been any (recognized)
338// smartcard devicees in the system for a while.
339//
340void PCSCDMonitor::noDeviceTimeout()
341{
342	secdebug("pcsc", "killing pcscd (no smartcard devices present for %g seconds)",
343		PCSCD_IDLE_SHUTDOWN.seconds());
344}
345
346void PCSCDMonitor::addInterestNotification()
347{
348	secdebug("pcsc", "Adding interest notification for service 0x%04X (this=%p)", mServiceOfInterest,this);
349	mIOKitNotifier.addInterestNotification(*this, mServiceOfInterest);
350}
351
352void PCSCDMonitor::scheduleAddInterestNotification(io_service_t serviceOfInterest)
353{
354	StLock<Mutex> _(mLock);
355	secdebug("pcsc", "Scheduling interest notification for service 0x%04X (this=%p)", serviceOfInterest, this);
356	mServiceOfInterest = serviceOfInterest;
357	mTimerAction = &PCSCDMonitor::addInterestNotification;
358	server.setTimer(this, Time::now());				// ASAP
359}
360
361//
362// IOKit device event notification.
363// Here we listen for newly inserted devices
364//
365void PCSCDMonitor::ioChange(IOKit::DeviceIterator &iterator)
366{
367	secdebug("pcsc", "Processing device event notification");
368	int def=0, pos=0, total=0;
369	// Always drain this iterator
370	while (IOKit::Device dev = iterator())
371	{
372		++total;
373		displayPropertiesOfDevice(dev);
374		switch (deviceSupport(dev))
375		{
376		case definite:
377			++def;
378			addDevice(dev);
379			break;
380		case possible:
381			++pos;
382			addDevice(dev);
383			break;
384		case impossible:
385			break;
386		}
387	}
388
389	dumpDevices();
390	secdebug("pcsc", "Relevant devices: %d definite, %d possible, %d total", def, pos, total);
391}
392
393// IOKit device event notification.
394// Here we listen for newly removed devices
395//
396void PCSCDMonitor::ioServiceChange(void *refCon, io_service_t service,
397	natural_t messageType, void *messageArgument)
398{
399	uintptr_t messageArg = uintptr_t(messageArgument);
400	secdebug("pcsc", "Processing ioServiceChange notice: 0x%08X [refCon=0x%08lX, service=0x%08X, arg=0x%08lX]",
401		messageType, (uintptr_t)refCon, service, messageArg);
402
403	if (mGoingToSleep && isSleepWakePeriod())	// waking up but still drowsy
404	{
405		secdebug("pcsc", "  ignoring ioServiceChange notice during wake up phase");
406		return;
407	}
408
409	PCSCDMonitor::displayPropertiesOfDevice(service);
410	// This is called since we asked for kIOGeneralInterest notices
411	// Usually it is the "device removed" notification
412	switch (messageType)
413	{
414	case kIOMessageServiceIsTerminated:		// We get these when device is removed
415		{
416			uint32_t address;
417			if (deviceAddress(service, address))
418			{
419				secdebug("pcsc", "  device removed notice: 0x%04X address: 0x%08X", service, address);
420				this->removeDevice(service, address);
421			}
422			else
423				secdebug("pcsc", "  device removed notice, but failed to find address for service: 0x%04X", service);
424		}
425		break;
426	case kIOMessageServiceWasClosed:		// We get these when the system sleeps
427		{
428#ifndef NDEBUG
429			uint32_t address;
430			deviceAddress(service, address);
431			secdebug("pcsc", "  service was closed notice: 0x%04X address: 0x%08X", service, address);
432#endif
433		}
434		break;
435	case kIOPCCardCSEventMessage:	// 0xE0054001 - not handled by mach_error_string
436		secdebug("pcsc", "  pccard event message: service: 0x%04X, type: 0x%08X",
437			service, (unsigned int)messageArg);
438		// Card Services Events are defined in IOKit/pccard/cs.h
439		switch (messageArg)
440		{
441			case CS_EVENT_EJECTION_REQUEST:
442				secdebug("pcsc", "  pccard event message: ejection request");
443				break;
444
445			case CS_EVENT_CARD_REMOVAL:
446			{
447				uint32_t address;
448				if (deviceMemoryAddress(service, address))
449				{
450					secdebug("pcsc", "  device removed notice: 0x%04X address: 0x%08X", service, address);
451					this->removeDevice(service, address);
452				}
453				else
454					secdebug("pcsc", "  device removed notice, but failed to find address for service: 0x%04X", service);
455				break;
456			}
457		}
458		break;
459	default:
460		secdebug("pcsc", "  processing device general notice: 0x%08X", messageType);
461		break;
462	}
463}
464
465void PCSCDMonitor::addDevice(const IOKit::Device &dev)
466{
467	DeviceMap::iterator it;
468	if (!findDevice(dev,it))		// new device
469	{
470		io_service_t service = dev.ioObject();
471
472		RefPointer<PCSCD::Device> newDevice = new PCSCD::Device(service);
473		uint32_t address = 0;
474
475		if (deviceAddress(dev, address))
476		{
477			newDevice->setAddress(address);
478			secdebug("scsel", "  Device address:  0x%08X [service: 0x%04X]", address, service);
479			setDeviceProperties(dev, *newDevice);
480			if (drivers.find(*newDevice))
481			{
482				secdebug("driver", "  found matching driver for %s: %s", newDevice->name().c_str(), newDevice->path().c_str());
483				setDebugPropertiesForDevice(dev, newDevice);
484				insert(make_pair(address, newDevice));
485				if (mAddDeviceCallback)
486				{
487					// kPCSCLITE_HP_BASE_PORT
488					uint32_t rx = (*mAddDeviceCallback)(newDevice->name().c_str(), address, newDevice->path().c_str(), newDevice->name().c_str());
489					secdebug("pcsc", "  AddDeviceCallback returned %d", rx);
490					if (rx != SCARD_S_SUCCESS && rx != SCARD_E_DUPLICATE_READER)
491					{
492						DeviceMap::iterator it = mDevices.find(address);
493						if (it != mDevices.end())		// found it
494							remove(it);					// remove from reader map
495						return;
496					}
497				}
498				PCSCDMonitor::postNotification(SecurityServer::kNotificationPCSCStateChange);
499				secdebug("pcsc", "     added to device map, address:  0x%08X, service: 0x%04X, [class @:%p]", address, service, newDevice.get());
500			}
501			else
502			{
503				secdebug("driver", "  no matching driver found for %s: %s", newDevice->name().c_str(), newDevice->path().c_str());
504				// Add MessageTracer logging as per <rdar://problem/6432650>. If we get here, pcscd was launched
505				// for a device insertion, but the device is not a smartcard reader (or doesn't have a
506				// matching driver.
507				char buf[256];
508				aslmsg msg = asl_new(ASL_TYPE_MSG);
509				asl_set(msg, "com.apple.message.domain", "com.apple.security.smartcardservices.unknowndevice" );
510				asl_set(msg, "com.apple.message.signature", "Non-smartcard device launched pcscd");
511				snprintf(buf, sizeof(buf), "%u", newDevice->vendorid());
512				asl_set(msg, "com.apple.message.signature2", buf);	// vendor ID
513				snprintf(buf, sizeof(buf), "%u", newDevice->productid());
514				asl_set(msg, "com.apple.message.signature3", buf);	// product ID
515				snprintf(buf, sizeof(buf), "Non-smartcard device launched pcscd [Vendor: %#X, Product: %#X]",
516					newDevice->vendorid(), newDevice->productid());
517				asl_log(NULL, msg, ASL_LEVEL_NOTICE, buf);
518				asl_free(msg);
519			}
520		}
521		else
522			secdebug("pcsc", "  device added notice, but failed to find address for service: 0x%04X", service);
523	}
524	else
525	{
526		PCSCD::Device *theDevice = static_cast<PCSCD::Device *>(it->second);
527		secdebug("scsel", "  Already in map: Device address:  0x%08X [service: 0x%04X]",
528			theDevice->address(), dev.ioObject());
529		setDeviceProperties(dev, *theDevice);
530		setDebugPropertiesForDevice(dev, theDevice);
531	}
532
533	// We always try to add the interest notification. It may be that
534	// we added the device during a callback for a particular plane,
535	// but we didn't have the right information then to add the notification
536	io_service_t servicex = dev.ioObject();
537	mIOKitNotifier.addInterestNotification(*this, servicex);
538	dumpDevices();
539}
540
541bool PCSCDMonitor::findDevice(const IOKit::Device &dev, DeviceMap::iterator &it)
542{
543	uint32_t address = 0;
544	deviceAddress(dev, address);
545	it = mDevices.find(address);
546	return (it != mDevices.end());
547}
548
549bool PCSCDMonitor::findDeviceByName(const IOKit::Device &dev, DeviceMap::iterator &outit)
550{
551	CFRef<CFStringRef> ioName = dev.property<CFStringRef>(kzIOPCCardIONameKey);
552	if (!ioName)
553		return false;
554
555	std::string devname = cfString(ioName);
556	for (DeviceMap::iterator it = mDevices.begin(); it != mDevices.end(); ++it)
557	{
558		PCSCD::Device *theDevice = static_cast<PCSCD::Device *>(it->second);
559		if (theDevice->name() == devname)
560		{
561			outit = it;
562			return true;
563		}
564	}
565
566	return false;
567}
568
569void PCSCDMonitor::updateDevice(const IOKit::Device &dev)
570{
571	DeviceMap::iterator it;
572	if (findDevice(dev,it))
573	{
574		PCSCD::Device *theDevice = static_cast<PCSCD::Device *>(it->second);
575		setDeviceProperties(dev, *theDevice);
576		if (drivers.find(*theDevice))
577			secdebug("driver", "  found matching driver for %s: %s", theDevice->name().c_str(), theDevice->path().c_str());
578		setDebugPropertiesForDevice(dev, theDevice);
579	}
580}
581
582bool PCSCDMonitor::hasLegacyDriver(const IOKit::Device &dev)
583{
584	PCSCD::Device tmpDevice(0);	//dev.ioObject() - fake it
585	uint32_t address = 0;
586	if (deviceAddress(dev, address))
587		tmpDevice.setAddress(address);
588	setDeviceProperties(dev, tmpDevice);
589	if (drivers.find(tmpDevice))
590	{
591		secdebug("driver", "  found matching driver for legacy device: %s", tmpDevice.path().c_str());
592		return true;
593	}
594
595	return false;
596}
597
598bool PCSCDMonitor::deviceIsPCCard(const IOKit::Device &dev)
599{
600	if (CFRef<CFStringRef> ioName = dev.property<CFStringRef>(kzIOPCCardIONameKey))
601		if (cfString(ioName).find("pccard", 0, 1) == 0)
602			return true;
603
604	return false;
605}
606
607bool PCSCDMonitor::deviceIsPCCard(io_service_t service)
608{
609	if (CFRef<CFStringRef> ioName = static_cast<CFStringRef>(::IORegistryEntryCreateCFProperty(
610		service, CFSTR(kzIOPCCardIONameKey), kCFAllocatorDefault, 0)))
611		if (cfString(ioName).find("pccard", 0, 1) == 0)
612			return true;
613
614	return false;
615}
616
617void PCSCDMonitor::getVendorAndProductID(const IOKit::Device &dev, uint32_t &vendorID, uint32_t &productID, bool &isPCCard)
618{
619	vendorID = productID = 0;
620	isPCCard = deviceIsPCCard(dev);
621
622	if (!isPCCard)
623	{
624		if (CFRef<CFNumberRef> cfVendorID = dev.property<CFNumberRef>(kUSBVendorID))
625			vendorID = cfNumber(cfVendorID);
626
627		if (CFRef<CFNumberRef> cfProductID = dev.property<CFNumberRef>(kUSBProductID))
628			productID = cfNumber(cfProductID);
629	}
630	else
631	{
632		if (CFRef<CFNumberRef> cfVendorID = dev.property<CFNumberRef>(kIOPCCardVendorIDMatchKey))
633			vendorID = cfNumber(cfVendorID);
634
635		if (CFRef<CFNumberRef> cfProductID = dev.property<CFNumberRef>(kIOPCCardDeviceIDMatchKey))
636			productID = cfNumber(cfProductID);
637
638		// One special case for legacy OmniKey CardMan 4040 support
639		CFRef<CFStringRef> ioName = dev.property<CFStringRef>(kzIOPCCardIONameKey);
640		if (ioName && CFEqual(ioName, CFSTR("pccard-no-cis")))
641		{
642			vendorID = 0x0223;
643			productID = 0x0200;
644		}
645	}
646}
647
648void PCSCDMonitor::setDeviceProperties(const IOKit::Device &dev, PCSCD::Device &device)
649{
650	uint32_t vendorID, productID;
651	bool isPCCard;
652
653	getVendorAndProductID(dev, vendorID, productID, isPCCard);
654
655	device.setIsPCCard(isPCCard);
656
657	if (CFRef<CFNumberRef> cfInterface = dev.property<CFNumberRef>(kzIOUSBbInterfaceClassKey))
658		device.setInterfaceClass(cfNumber(cfInterface));
659
660	if (CFRef<CFNumberRef> cfDevice = dev.property<CFNumberRef>(kzIOUSBbDeviceClassKey))
661		device.setDeviceClass(cfNumber(cfDevice));
662
663	device.setVendorid(vendorID);
664	device.setProductid(productID);
665
666	if (CFRef<CFStringRef> ioName = dev.property<CFStringRef>(kzIOPCCardIONameKey))
667		device.setName(cfString(ioName));
668}
669
670bool PCSCDMonitor::isExcludedDevice(const IOKit::Device &dev)
671{
672	uint32_t vendorID, productID;
673	bool isPCCard;
674
675	getVendorAndProductID(dev, vendorID, productID, isPCCard);
676
677	if ((vendorID & kVendorProductMask) != kVendorIDApple)
678		return false;	// i.e. it is not an excluded device
679
680	// Since Apple does not manufacture smartcard readers, just exclude
681	// If we even start making them, we should make it a CCID reader anyway
682
683	return true;
684}
685
686void PCSCDMonitor::setDebugPropertiesForDevice(const IOKit::Device &dev, PCSCD::Device * newDevice)
687{
688	/*
689		Many of these properties are only defined on the "IOUSBDevice" plane, so
690		will be non-empty on the third iteration.
691	*/
692	std::string vendorName, productName, serialNumber;
693
694	if (CFRef<CFStringRef> cfVendorString = dev.property<CFStringRef>(kzIOUSBVendorNameKey))
695		vendorName = cfString(cfVendorString);
696
697	if (CFRef<CFStringRef> cfProductString = dev.property<CFStringRef>(kzIOUSBProductNameKey))
698		productName = cfString(cfProductString);
699
700	if (CFRef<CFStringRef> cfSerialString = dev.property<CFStringRef>(kzIOUSBSerialNumberKey))
701		serialNumber = cfString(cfSerialString);
702
703	if (deviceIsPCCard(dev))
704	{
705		if (CFRef<CFArrayRef> cfVersionOne = dev.property<CFArrayRef>(kIOPCCardVersionOneMatchKey))
706		if (CFArrayGetCount(cfVersionOne) > 1)
707		{
708			CFStringRef cfVendorString = (CFStringRef)CFArrayGetValueAtIndex(cfVersionOne, 0);
709			if (cfVendorString)
710				vendorName = cfString(cfVendorString);
711
712			CFStringRef cfProductString = (CFStringRef)CFArrayGetValueAtIndex(cfVersionOne, 1);
713			if (cfProductString)
714				productName = cfString(cfProductString);
715		}
716	}
717
718	newDevice->setDebugParams(vendorName, productName, serialNumber);
719
720//	secdebug("scsel", "  deviceSupport: vendor/product: 0x%04X/0x%04X, vendor:  %s, product: %s, serial: %s", vendorid, productid,
721//		vendorName.c_str(), productName.c_str(), serialNumber.c_str());
722}
723
724void PCSCDMonitor::removeDevice(io_service_t service, uint32_t address)
725{
726	secdebug("pcsc", " Size of mDevices: %ld, service: 0x%04X", mDevices.size(), service);
727	if (!mDevices.empty())
728	{
729		secdebug("pcsc", "  device removed notice: 0x%04X address: 0x%08X", service, address);
730		DeviceMap::iterator it = mDevices.find(address);
731		if (it != mDevices.end())		// found it
732		{
733			if (mRemoveDeviceCallback)
734			{
735				uint32_t rx = (*mRemoveDeviceCallback)((it->second)->name().c_str(), address);
736				secdebug("pcsc", "  RemoveDeviceCallback returned %d", rx);
737			}
738			remove(it);					// remove from reader map
739		}
740		else
741			secdebug("pcsc", " service: 0x%04X at address 0x%04X not found ??", service, address);
742	}
743	dumpDevices();
744	::IOObjectRelease(service);		// we don't want notifications here until re-added
745}
746
747void PCSCDMonitor::removeDeviceByName(const IOKit::Device &dev)
748{
749	io_service_t service = dev.ioObject();
750	secdebug("pcsc", " Size of mDevices: %ld, service: 0x%04X", mDevices.size(), service);
751	if (!mDevices.empty())
752	{
753		uint32_t address = 0;
754		deviceAddress(dev, address);
755		DeviceMap::iterator it;
756		if (findDeviceByName(dev, it))		// found it
757		{
758			if (mRemoveDeviceCallback)
759			{
760				uint32_t rx = (*mRemoveDeviceCallback)((it->second)->name().c_str(), address);
761				secdebug("pcsc", "  RemoveDeviceCallback returned %d", rx);
762			}
763			remove(it);					// remove from reader map
764		}
765		else
766			secdebug("pcsc", " service: 0x%04X at address 0x%04X not found ??", service, address);
767	}
768	dumpDevices();
769	::IOObjectRelease(service);		// we don't want notifications here until re-added
770}
771
772void PCSCDMonitor::removeAllDevices()
773{
774	secdebug("pcsc", ">>>>>> removeAllDevices: Size of mDevices: %ld", mDevices.size());
775	for (DeviceMap::iterator it = mDevices.begin(); it != mDevices.end(); ++it)
776	{
777		PCSCD::Device *dev = static_cast<PCSCD::Device *>(it->second);
778		uint32_t address = 0;
779	//	PCSCDMonitor::deviceAddress(*dev, &address);
780	address = dev->address();
781		io_service_t service = dev->ioObject();
782		if (mRemoveDeviceCallback)
783		{
784			uint32_t rx = (*mRemoveDeviceCallback)(dev->name().c_str(), address);
785			secdebug("pcsc", "  RemoveDeviceCallback returned %d", rx);
786		}
787		::IOObjectRelease(service);		// we don't want notifications here until re-added
788		remove(it);						// remove from reader map
789	}
790	secdebug("pcsc", ">>>>>> removeAllDevices [end]: Size of mDevices: %ld", mDevices.size());
791}
792
793
794//
795// Check an IOKit device that's just come online to see if it's
796// a smartcard device of some sort.
797//
798PCSCDMonitor::DeviceSupport PCSCDMonitor::deviceSupport(const IOKit::Device &dev)
799{
800#ifndef NDEBUG
801	try
802	{
803		secdebug("scsel", "path: %s", dev.path().c_str());	// this can fail sometimes
804	}
805	catch (...)
806	{
807		secdebug("scsel", "  exception while displaying device path - ignoring error");
808	}
809#endif
810
811	try
812	{
813		// composite USB device with interface class
814		if (CFRef<CFNumberRef> cfInterface = dev.property<CFNumberRef>(kzIOUSBbInterfaceClassKey))
815			switch (uint32_t clas = cfNumber(cfInterface))
816			{
817			case kUSBChipSmartCardInterfaceClass:		// CCID smartcard reader - go
818				secdebug("scsel", "  CCID smartcard reader recognized");
819				return definite;
820			case kUSBVendorSpecificInterfaceClass:
821				if (isExcludedDevice(dev))
822				{
823					secdebug("scsel", "  interface class %d is not a smartcard device (excluded)", clas);
824					return impossible;
825				}
826				secdebug("scsel", "  Vendor-specific interface - possible match");
827				return possible;
828			default:
829				if ((clas == 0) && hasLegacyDriver(dev))
830				{
831					secdebug("scsel", "  Vendor-specific legacy driver - possible match");
832					return possible;
833				}
834				secdebug("scsel", "  interface class %d is not a smartcard device", clas);
835				return impossible;
836			}
837
838		// noncomposite USB device
839		if (CFRef<CFNumberRef> cfDevice = dev.property<CFNumberRef>(kzIOUSBbDeviceClassKey))
840			if (cfNumber(cfDevice) == kUSBVendorSpecificClass)
841			{
842				if (isExcludedDevice(dev))
843				{
844					secdebug("scsel", "  device class %d is not a smartcard device (excluded)", cfNumber(cfDevice));
845					return impossible;
846				}
847				secdebug("scsel", "  Vendor-specific device - possible match");
848				return possible;
849			}
850
851		// PCCard (aka PCMCIA aka ...) interface (don't know how to recognize a reader here)
852		if (deviceIsPCCard(dev))
853		{
854			secdebug("scsel", "  PCCard - possible match");
855			return possible;
856		}
857
858		return impossible;
859	}
860	catch (...)
861	{
862		secdebug("scsel", "  exception while examining device - ignoring it");
863		return impossible;
864	}
865}
866
867#pragma mark -------------------- Static Methods --------------------
868
869bool PCSCDMonitor::deviceAddress(io_service_t service, uint32_t &address)
870{
871	if (CFRef<CFNumberRef> cfLocationID = static_cast<CFNumberRef>(::IORegistryEntryCreateCFProperty(
872		service, CFSTR(kzIOUSBLocationIDKey), kCFAllocatorDefault, 0)))
873	{
874		address = cfNumber(cfLocationID);
875		return true;
876	}
877
878	// don't bother to test if it is a pc card, just try looking
879	return deviceMemoryAddress(service, address);
880}
881
882bool PCSCDMonitor::deviceAddress(const IOKit::Device &dev, uint32_t &address)
883{
884	if (CFRef<CFNumberRef> cfLocationID = dev.property<CFNumberRef>(kzIOUSBLocationIDKey))
885	{
886		address = cfNumber(cfLocationID);
887		return true;
888	}
889
890	// don't bother to test if it is a pc card, just try looking
891	return deviceMemoryAddress(dev, address);
892}
893
894bool PCSCDMonitor::deviceMemoryAddress(const IOKit::Device &dev, uint32_t &address)
895{
896//	CFRef<CFStringRef> ioName = dev.property<CFStringRef>(kzIOPCCardIONameKey);
897	CFRef<CFArrayRef> cfDeviceMemory = dev.property<CFArrayRef>(kzIOPCCardIODeviceMemoryKey);
898	return deviceMemoryAddressCore(cfDeviceMemory, dev.path(), address);
899}
900
901bool PCSCDMonitor::deviceMemoryAddress(io_service_t service, uint32_t &address)
902{
903//	CFRef<CFStringRef> ioName = static_cast<CFStringRef>(::IORegistryEntryCreateCFProperty(
904//		service, CFSTR(kzIOPCCardIONameKey), kCFAllocatorDefault, 0));
905	CFRef<CFArrayRef> cfDeviceMemory = static_cast<CFArrayRef>(::IORegistryEntryCreateCFProperty(
906			service, CFSTR(kzIOPCCardIODeviceMemoryKey), kCFAllocatorDefault, 0));
907	return deviceMemoryAddressCore(cfDeviceMemory, "", address);
908}
909
910bool PCSCDMonitor::deviceMemoryAddressCore(CFArrayRef cfDeviceMemory, std::string path, uint32_t &address)
911{
912	address = 0;
913	try
914	{
915		if (cfDeviceMemory)
916		{
917			if (CFRef<CFDictionaryRef> cfTempMem = (CFDictionaryRef)CFRetain(CFArrayGetValueAtIndex(cfDeviceMemory, 0)))
918			{
919			//	CFDictionaryApplyFunction(cfTempMem, dumpdictentry, NULL);
920				if (CFRef<CFArrayRef> cfParent = (CFArrayRef)CFRetain(CFDictionaryGetValue(cfTempMem, CFSTR(kzIOPCCardParentKey))))
921					if (CFRef<CFDictionaryRef> cfTempMem2 = (CFDictionaryRef)CFRetain(CFArrayGetValueAtIndex(cfParent, 0)))
922						if (CFRef<CFNumberRef> cfAddress = (CFNumberRef)CFRetain(CFDictionaryGetValue((CFDictionaryRef)cfTempMem2, CFSTR(kzIOPCCardAddressKey))))
923						{
924							address = cfNumber(cfAddress);
925							secdebug("scsel", "  address from device memory address property: 0x%08X", address);
926							return true;
927						}
928			}
929		}
930		else
931		if (!path.empty())
932		{
933		//	std::string name = cfString(ioName);
934		//	address = CFHash (ioName);
935		//	address = 0xF2000000;
936			addressFromPath(path, address);
937			secdebug("scsel", "  extracted address: 0x%08X for device [%s]", address, path.c_str());
938			return true;
939		}
940	}
941	catch (...)
942	{
943		secdebug("scsel", "  exception while examining deviceMemoryAddress property");
944	}
945	return false;
946}
947
948bool PCSCDMonitor::addressFromPath(std::string path, uint32_t &address)
949{
950	/*
951		Try to extract the address from the path if the other keys are not present.
952		An example path is:
953
954			IOService:/MacRISC2PE/pci@f2000000/AppleMacRiscPCI/cardbus@13/IOPCCardBridge/pccard2bd,1003@0,0
955
956		where e.g. the address is f2000000, the vendor is 0x2bd, and the product id is 0x1003
957	*/
958	address = 0;
959	#define HEX_TO_INT(x) ((x) >= '0' &&(x) <= '9' ? (x) - '0' : (x) - ('a' - 10))
960
961	try
962	{
963		secdebug("scsel", "path: %s", path.c_str());			// this can fail sometimes
964
965		std::string lhs("/pci@");
966		std::string rhs("/");
967
968		std::string::size_type start = path.find(lhs)+lhs.length();
969		std::string::size_type end = path.find(rhs, start);
970
971		std::string addressString(path, start, end-start);
972
973		// now addressString should contain something like f2000000
974		uint32_t tmp = 0;
975		const char *px = addressString.c_str();
976		size_t len = strlen(px);
977		for (unsigned int ix=0;ix<len;ix++,px++)
978		{
979			tmp<<=4;
980			tmp += HEX_TO_INT(*px);
981		}
982
983		address = tmp;
984
985		secdebug("scsel", "  address 0x%08X extracted from path", address);
986	}
987	catch (...)
988	{
989		secdebug("scsel", "  exception while displaying device path - ignoring error");
990		return false;
991	}
992
993	return true;
994}
995
996#pragma mark -------------------- Termination Notice Receiver --------------------
997
998TerminationNoticeReceiver::~TerminationNoticeReceiver()
999{
1000}
1001
1002void TerminationNoticeReceiver::ioChange(IOKit::DeviceIterator &iterator)
1003{
1004	secdebug("pcsc", "[TerminationNoticeReceiver] Processing ioChange notification");
1005	// Always drain this iterator
1006	while (IOKit::Device dev = iterator())
1007	{
1008		PCSCDMonitor::displayPropertiesOfDevice(dev);
1009		parent().removeDeviceByName(dev);
1010	}
1011}
1012
1013void TerminationNoticeReceiver::ioServiceChange(void *refCon, io_service_t service,
1014	natural_t messageType, void *messageArgument)
1015{
1016	uintptr_t messageArg = uintptr_t(messageArgument);
1017	secdebug("pcsc", "  [TerminationNoticeReceiver] processing ioServiceChange notice: 0x%08X [refCon=0x%08lX, service=0x%08X, arg=0x%08lX]",
1018		messageType, (uintptr_t)refCon, service, messageArg);
1019	parent().ioServiceChange(refCon, service, messageType, messageArgument);
1020}
1021
1022#pragma mark -------------------- Debug Routines --------------------
1023
1024void PCSCDMonitor::displayPropertiesOfDevice(const IOKit::Device &dev)
1025{
1026	/*
1027		Many of these properties are only defined on the "IOUSBDevice" plane, so
1028		will be non-empty on the third iteration.
1029	*/
1030	try
1031	{
1032		std::string vendorName, productName, serialNumber, name;
1033
1034		uint32_t vendorID, productID;
1035		bool isPCCard;
1036
1037		CFRef<CFStringRef> ioName = dev.property<CFStringRef>(kzIOPCCardIONameKey);
1038		if (ioName)
1039			name = cfString(ioName);
1040
1041		getVendorAndProductID(dev, vendorID, productID, isPCCard);
1042
1043		if (CFRef<CFStringRef> cfSerialString = dev.property<CFStringRef>(kzIOUSBSerialNumberKey))
1044			serialNumber = cfString(cfSerialString);
1045
1046		if (isPCCard)
1047		{
1048			if (CFRef<CFArrayRef> cfVersionOne = dev.property<CFArrayRef>(kIOPCCardVersionOneMatchKey))
1049			if (CFArrayGetCount(cfVersionOne) > 1)
1050			{
1051				CFStringRef cfVendorString = (CFStringRef)CFArrayGetValueAtIndex(cfVersionOne, 0);
1052				if (cfVendorString)
1053					vendorName = cfString(cfVendorString);
1054
1055				CFStringRef cfProductString = (CFStringRef)CFArrayGetValueAtIndex(cfVersionOne, 1);
1056				if (cfProductString)
1057					productName = cfString(cfProductString);
1058			}
1059
1060			uint32_t address;
1061			deviceMemoryAddress(dev, address);
1062		}
1063		else
1064		{
1065			if (CFRef<CFStringRef> cfVendorString = dev.property<CFStringRef>(kzIOUSBVendorNameKey))
1066				vendorName = cfString(cfVendorString);
1067
1068			if (CFRef<CFStringRef> cfProductString = dev.property<CFStringRef>(kzIOUSBProductNameKey))
1069				productName = cfString(cfProductString);
1070		}
1071
1072		secdebug("scsel", "--- properties: service: 0x%04X, name: %s, vendor/product: 0x%04X/0x%04X, vendor: %s, product: %s, serial: %s",
1073			dev.ioObject(), name.c_str(), vendorID, productID,
1074			vendorName.c_str(), productName.c_str(), serialNumber.c_str());
1075	}
1076	catch (...)
1077	{
1078		secdebug("scsel", "  exception in displayPropertiesOfDevice - ignoring error");
1079	}
1080}
1081
1082void PCSCDMonitor::displayPropertiesOfDevice(io_service_t service)
1083{
1084    kern_return_t	kr;
1085    CFMutableDictionaryRef properties = NULL;
1086
1087	// get a copy of the in kernel registry object
1088	kr = IORegistryEntryCreateCFProperties(service, &properties, kCFAllocatorDefault, 0);
1089	if (kr != KERN_SUCCESS)
1090	{
1091	    printf("IORegistryEntryCreateCFProperties failed with %x\n", kr);
1092	}
1093	else
1094	if (properties)
1095	{
1096//		CFShow(properties);
1097		CFRelease(properties);
1098	}
1099
1100	try
1101	{
1102		std::string vendorName, productName, serialNumber, name;
1103
1104		uint32_t vendorID = 0, productID = 0;
1105		bool isPCCard = false;
1106
1107		CFRef<CFStringRef> ioName = static_cast<CFStringRef>(::IORegistryEntryCreateCFProperty(
1108			service, CFSTR(kzIOPCCardIONameKey), kCFAllocatorDefault, 0));
1109		if (ioName)
1110			name = cfString(ioName);
1111
1112//		getVendorAndProductID(dev, vendorID, productID, isPCCard);
1113
1114		CFRef<CFStringRef> cfSerialString = static_cast<CFStringRef>(::IORegistryEntryCreateCFProperty(
1115			service, CFSTR(kzIOUSBSerialNumberKey), kCFAllocatorDefault, 0));
1116		if (cfSerialString)
1117			serialNumber = cfString(cfSerialString);
1118
1119		if (isPCCard)
1120		{
1121			CFRef<CFArrayRef> cfVersionOne = static_cast<CFArrayRef>(::IORegistryEntryCreateCFProperty(
1122				service, CFSTR(kIOPCCardVersionOneMatchKey), kCFAllocatorDefault, 0));
1123			if (cfVersionOne && (CFArrayGetCount(cfVersionOne) > 1))
1124			{
1125				CFStringRef cfVendorString = (CFStringRef)CFArrayGetValueAtIndex(cfVersionOne, 0);
1126				if (cfVendorString)
1127					vendorName = cfString(cfVendorString);
1128
1129				CFStringRef cfProductString = (CFStringRef)CFArrayGetValueAtIndex(cfVersionOne, 1);
1130				if (cfProductString)
1131					productName = cfString(cfProductString);
1132			}
1133
1134			uint32_t address;
1135			deviceMemoryAddress(service, address);
1136		}
1137		else
1138		{
1139			CFRef<CFStringRef> cfVendorString = static_cast<CFStringRef>(::IORegistryEntryCreateCFProperty(
1140				service, CFSTR(kzIOUSBVendorNameKey), kCFAllocatorDefault, 0));
1141			if (cfVendorString)
1142				vendorName = cfString(cfVendorString);
1143
1144			CFRef<CFStringRef> cfProductString = static_cast<CFStringRef>(::IORegistryEntryCreateCFProperty(
1145				service, CFSTR(kzIOUSBProductNameKey), kCFAllocatorDefault, 0));
1146			if (cfProductString)
1147				productName = cfString(cfProductString);
1148		}
1149
1150		secdebug("scsel", "--- properties: service: 0x%04X, name: %s, vendor/product: 0x%04X/0x%04X, vendor: %s, product: %s, serial: %s",
1151			service, name.c_str(), vendorID, productID,
1152			vendorName.c_str(), productName.c_str(), serialNumber.c_str());
1153	}
1154	catch (...)
1155	{
1156		secdebug("scsel", "  exception in displayPropertiesOfDevice - ignoring error");
1157	}
1158}
1159
1160void PCSCDMonitor::dumpDevices()
1161{
1162	secdebug("pcsc", "------------------ Device Map ------------------");
1163	for (DeviceMap::iterator it = mDevices.begin();it!=mDevices.end();++it)
1164	{
1165		PCSCD::Device *dev = static_cast<PCSCD::Device *>(it->second);
1166		dev->dump();
1167	}
1168	secdebug("pcsc", "------------------------------------------------");
1169}
1170
1171#if 0
1172static void dumpdictentry(const void *key, const void *value, void *context)
1173{
1174	secdebug("dumpd", "  dictionary key: %s, val: %p, CFGetTypeID: %d", cfString((CFStringRef)key).c_str(), value, (int)CFGetTypeID(value));
1175}
1176#endif
1177
1178