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