1/* 2 * Copyright (c) 2000-2007 Apple 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 * eventhandler.cpp 26 * SmartCardServices 27 */ 28 29/* 30 * MUSCLE SmartCard Development ( http://www.linuxnet.com ) 31 * 32 * Copyright (C) 2000 33 * David Corcoran <corcoran@linuxnet.com> 34 * Copyright (C) 2004 35 * Ludovic Rousseau <ludovic.rousseau@free.fr> 36 * 37 * $Id: eventhandler.cpp 123 2010-03-27 10:50:42Z ludovic.rousseau@gmail.com $ 38 */ 39 40/** 41 * @file 42 * @brief This keeps track of card insertion/removal events 43 * and updates ATR, protocol, and status information. 44 */ 45 46#include "config.h" 47#include <sys/types.h> 48#include <sys/stat.h> 49#include <sys/errno.h> 50#include <sys/mman.h> 51#include <fcntl.h> 52#include <string.h> 53#include <stdlib.h> 54 55#include "wintypes.h" 56#include "pcsclite.h" 57#include "ifdhandler.h" 58#include "debuglog.h" 59#include "thread_generic.h" 60#include "readerfactory.h" 61#include "eventhandler.h" 62#include "dyn_generic.h" 63#include "sys_generic.h" 64#include "ifdwrapper.h" 65#include "prothandler.h" 66#include "readerstate.h" 67 68#include <security_utilities/debugging.h> 69 70static PREADER_STATE readerStates[PCSCLITE_MAX_READERS_CONTEXTS]; 71 72void EHStatusHandlerThread(PREADER_CONTEXT); 73 74LONG EHInitializeEventStructures(void) 75{ 76 int fd, i, pageSize; 77 78 fd = 0; 79 i = 0; 80 pageSize = 0; 81 82 /* 83 Do not truncate to avoid possible SIGSEG on clients 84 Do not remove the file to allow long-term clients such as securityd to 85 stay connected to the same file 86 */ 87 fd = SYS_OpenFile(PCSCLITE_PUBSHM_FILE, O_RDWR | O_CREAT , 00644); 88 if (fd < 0) 89 { 90 Log3(PCSC_LOG_CRITICAL, "Cannot create public shared file %s: %s", 91 PCSCLITE_PUBSHM_FILE, strerror(errno)); 92 exit(1); 93 } 94 95 SYS_Chmod(PCSCLITE_PUBSHM_FILE, 96 S_IRGRP | S_IREAD | S_IWRITE | S_IROTH); 97 98 pageSize = SYS_GetPageSize(); 99 100 int rx = ftruncate(fd, pageSize * PCSCLITE_MAX_READERS_CONTEXTS); 101 if (rx) 102 Log3(PCSC_LOG_CRITICAL, "Cannot truncate public shared file %d: %s", 103 errno, strerror(errno)); 104 /* 105 * Jump to end of file space and allocate zero's 106 */ 107 SYS_SeekFile(fd, pageSize * PCSCLITE_MAX_READERS_CONTEXTS); 108 SYS_WriteFile(fd, "", 1); 109 110 /* 111 * Allocate each reader structure 112 */ 113 for (i = 0; i < PCSCLITE_MAX_READERS_CONTEXTS; i++) 114 { 115 readerStates[i] = (PREADER_STATE) 116 SYS_MemoryMap(sizeof(READER_STATE), fd, (i * pageSize)); 117 if (readerStates[i] == MAP_FAILED) 118 { 119 Log3(PCSC_LOG_CRITICAL, "Cannot memory map public shared file %s: %s", 120 PCSCLITE_PUBSHM_FILE, strerror(errno)); 121 exit(1); 122 } 123 124 /* 125 * Zero out each value in the struct 126 */ 127 memset((readerStates[i])->readerName, 0, MAX_READERNAME); 128 memset((readerStates[i])->cardAtr, 0, MAX_ATR_SIZE); 129 (readerStates[i])->readerID = 0; 130 (readerStates[i])->readerState = 0; 131 (readerStates[i])->lockState = 0; 132 (readerStates[i])->readerSharing = 0; 133 (readerStates[i])->cardAtrLength = 0; 134 (readerStates[i])->cardProtocol = SCARD_PROTOCOL_UNSET; // ok since this is 0 135 } 136 137 return SCARD_S_SUCCESS; 138} 139 140LONG EHDestroyEventHandler(PREADER_CONTEXT rContext) 141{ 142 if (NULL == rContext->readerState) 143 { 144 Log1(PCSC_LOG_ERROR, "Thread never started (reader init failed?)"); 145 return SCARD_S_SUCCESS; 146 } 147 148 PCSCD::SharedReaderState *rs = PCSCD::SharedReaderState::overlay(rContext->readerState); 149 if ((rContext->pthThread == 0) || !rs || (rs->readerNameLength() == 0)) 150 { 151 Log1(PCSC_LOG_INFO, "Thread already stomped."); 152 return SCARD_S_SUCCESS; 153 } 154 155 secdebug("pcscd", "EHDestroyEventHandler: pthThread: %p, reader name len: %ld", 156 rContext->pthThread, rs->readerNameLength()); 157 158 /* 159 * Zero out the public status struct to allow it to be recycled and 160 * used again 161 */ 162 163 rs->xreaderNameClear(); 164 rs->xcardAtrClear(); 165 rs->xreaderID(0); 166 rs->xreaderState(0); 167 rs->xlockState(0); 168 rs->sharing(0); 169 rs->xcardAtrLength(0); 170 rs->xcardProtocol(SCARD_PROTOCOL_UNSET); // we only set this one to write to memory cache 171 172 /* 173 * Set the thread to 0 to exit thread 174 */ 175 ReaderContextLock(rContext); 176 177 Log1(PCSC_LOG_INFO, "Stomping thread."); 178 179 int ix; 180 for (ix = 0; (ix < 100) && ReaderContextIsLocked(rContext); ++ix) 181 { 182 /* 183 * Wait 0.05 seconds for the child to respond 184 */ 185 SYS_USleep(50000); 186 } 187 188 secdebug("pcscd", "EHDestroyEventHandler: post-stop dwLockId: %d", rContext->dwLockId); 189 190 191 /* Zero the thread */ 192 rContext->pthThread = 0; 193 194 Log1(PCSC_LOG_INFO, "Thread stomped."); 195 196 return SCARD_S_SUCCESS; 197} 198 199LONG EHSpawnEventHandler(PREADER_CONTEXT rContext) 200{ 201 LONG rv; 202 DWORD dwStatus = 0; 203 int i; 204 UCHAR ucAtr[MAX_ATR_SIZE]; 205 DWORD dwAtrLen = 0; 206 207 secdebug("pcscd", "EHSpawnEventHandler: rContext: 0x%p", rContext); 208 rv = IFDStatusICC(rContext, &dwStatus, ucAtr, &dwAtrLen); 209 if (rv != SCARD_S_SUCCESS) 210 { 211 Log2(PCSC_LOG_ERROR, "Initial Check Failed on %s", rContext->lpcReader); 212 return SCARD_F_UNKNOWN_ERROR; 213 } 214 215 /* 216 * Find an empty reader slot and insert the new reader 217 */ 218 for (i = 0; i < PCSCLITE_MAX_READERS_CONTEXTS; i++) 219 { 220 PCSCD::SharedReaderState *rstmp = PCSCD::SharedReaderState::overlay(readerStates[i]); 221 if (rstmp->xreaderID() == 0) 222 break; 223 } 224 225 if (i == PCSCLITE_MAX_READERS_CONTEXTS) 226 return SCARD_F_INTERNAL_ERROR; 227 228 /* 229 * Set all the attributes to this reader 230 */ 231 PCSCD::SharedReaderState *rs = PCSCD::SharedReaderState::overlay(readerStates[i]); 232 rContext->readerState = readerStates[i]; 233 rs->xreaderName(rContext->lpcReader); 234 rs->xcardAtr(ucAtr, dwAtrLen); // also sets cardAtrLength 235 236 rs->xreaderID(i + 100); 237 rs->xreaderState(dwStatus); 238 rs->sharing(rContext->dwContexts); 239 rs->xcardProtocol(SCARD_PROTOCOL_UNSET); 240 241 rv = SYS_ThreadCreate(&rContext->pthThread, THREAD_ATTR_DETACHED, 242 (PCSCLITE_THREAD_FUNCTION( ))EHStatusHandlerThread, (LPVOID) rContext); 243 secdebug("pcscd", "EHSpawnEventHandler after thread create: %d [%04X]", rv, rv); 244 if (rv == 1) 245 return SCARD_S_SUCCESS; 246 else 247 return SCARD_E_NO_MEMORY; 248} 249 250void EHStatusHandlerThread(PREADER_CONTEXT rContext) 251{ 252 LONG rv; 253 LPCSTR lpcReader; 254 DWORD dwStatus, dwReaderSharing; 255 DWORD dwCurrentState; 256 int pageSize = SYS_GetPageSize(); 257 258 /* 259 * Zero out everything 260 */ 261 dwStatus = 0; 262 dwReaderSharing = 0; 263 dwCurrentState = 0; 264 265 secdebug("pcscd", "EHStatusHandlerThread: rContext: 0x%p", rContext); 266 lpcReader = rContext->lpcReader; 267 268 PCSCD::SharedReaderState *rs = PCSCD::SharedReaderState::overlay(rContext->readerState); 269 270 DWORD tmpCardAtrLength = MAX_ATR_SIZE; 271 rv = IFDStatusICC(rContext, &dwStatus, rs->xcardAtr(), &tmpCardAtrLength); 272 secdebug("pcscd", "EHStatusHandlerThread: initial call to IFDStatusICC: %d [%04X]", rv, rv); 273 274 if (dwStatus & SCARD_PRESENT) 275 { 276 tmpCardAtrLength = MAX_ATR_SIZE; 277 rv = IFDPowerICC(rContext, IFD_POWER_UP, rs->xcardAtr(), &tmpCardAtrLength); 278 279 /* the protocol is unset after a power on */ 280 rs->xcardProtocol(SCARD_PROTOCOL_UNSET); 281 282 secdebug("pcscd", "EHStatusHandlerThread: initial call to IFDPowerICC: %d [%04X]", rv, rv); 283 284 if (rv == IFD_SUCCESS) 285 { 286 rs->xcardAtrLength(tmpCardAtrLength); 287 288 dwStatus |= SCARD_PRESENT; 289 dwStatus &= ~SCARD_ABSENT; 290 dwStatus |= SCARD_POWERED; 291 dwStatus |= SCARD_NEGOTIABLE; 292 dwStatus &= ~SCARD_SPECIFIC; 293 dwStatus &= ~SCARD_SWALLOWED; 294 dwStatus &= ~SCARD_UNKNOWN; 295 296 if (rs->xcardAtrLength() > 0) 297 { 298 LogXxd(PCSC_LOG_INFO, "Card ATR: ", 299 rs->xcardAtr(), 300 rs->xcardAtrLength()); 301 } 302 else 303 Log1(PCSC_LOG_INFO, "Card ATR: (NULL)"); 304 } 305 else 306 { 307 dwStatus |= SCARD_PRESENT; 308 dwStatus &= ~SCARD_ABSENT; 309 dwStatus |= SCARD_SWALLOWED; 310 dwStatus &= ~SCARD_POWERED; 311 dwStatus &= ~SCARD_NEGOTIABLE; 312 dwStatus &= ~SCARD_SPECIFIC; 313 dwStatus &= ~SCARD_UNKNOWN; 314 Log3(PCSC_LOG_ERROR, "Error powering up card: %d 0x%04X", rv, rv); 315 } 316 317 dwCurrentState = SCARD_PRESENT; 318 } 319 else 320 { 321 dwStatus |= SCARD_ABSENT; 322 dwStatus &= ~SCARD_PRESENT; 323 dwStatus &= ~SCARD_POWERED; 324 dwStatus &= ~SCARD_NEGOTIABLE; 325 dwStatus &= ~SCARD_SPECIFIC; 326 dwStatus &= ~SCARD_SWALLOWED; 327 dwStatus &= ~SCARD_UNKNOWN; 328 rs->xcardAtrLength(0); 329 rs->xcardProtocol(SCARD_PROTOCOL_UNSET); 330 331 dwCurrentState = SCARD_ABSENT; 332 } 333 334 /* 335 * Set all the public attributes to this reader 336 */ 337 rs->xreaderState(dwStatus); 338 dwReaderSharing = rContext->dwContexts; 339 rs->sharing(dwReaderSharing); 340 341 SYS_MMapSynchronize((void *) rContext->readerState, pageSize); 342 343 while (1) 344 { 345 dwStatus = 0; 346 347 // Defensive measure 348 if (!rContext->vHandle) 349 { 350 // Exit and notify the caller 351 secdebug("pcscd", "EHStatusHandlerThread: lost dynamic callbacks ??"); 352 ReaderContextUnlock(rContext); 353 SYS_ThreadDetach(rContext->pthThread); 354 SYS_ThreadExit(0); 355 } 356 357 DWORD tmpCardAtrLength = MAX_ATR_SIZE; 358 rv = IFDStatusICC(rContext, &dwStatus, rs->xcardAtr(), &tmpCardAtrLength); 359 360 if (rv != SCARD_S_SUCCESS) 361 { 362 Log2(PCSC_LOG_ERROR, "Error communicating to: %s", lpcReader); 363 364 /* 365 * Set error status on this reader while errors occur 366 */ 367 368 DWORD readerStateTmp = rs->xreaderState(); 369 readerStateTmp &= ~SCARD_ABSENT; 370 readerStateTmp &= ~SCARD_PRESENT; 371 readerStateTmp &= ~SCARD_POWERED; 372 readerStateTmp &= ~SCARD_NEGOTIABLE; 373 readerStateTmp &= ~SCARD_SPECIFIC; 374 readerStateTmp &= ~SCARD_SWALLOWED; 375 readerStateTmp |= SCARD_UNKNOWN; 376 rs->xcardAtrLength(0); 377 rs->xcardProtocol(SCARD_PROTOCOL_UNSET); 378 rs->xreaderState(readerStateTmp); 379 380 dwCurrentState = SCARD_UNKNOWN; 381 382 SYS_MMapSynchronize((void *) rContext->readerState, pageSize); 383 384 /* 385 * This code causes race conditions on G4's with USB 386 * insertion 387 */ 388 /* 389 * dwErrorCount += 1; SYS_Sleep(1); 390 */ 391 /* 392 * After 10 seconds of errors, try to reinitialize the reader 393 * This sometimes helps bring readers out of *crazy* states. 394 */ 395 /* 396 * if ( dwErrorCount == 10 ) { RFUnInitializeReader( rContext 397 * ); RFInitializeReader( rContext ); dwErrorCount = 0; } 398 */ 399 400 /* 401 * End of race condition code block 402 */ 403 } 404 405 if (dwStatus & SCARD_ABSENT) 406 { 407 if (dwCurrentState == SCARD_PRESENT || 408 dwCurrentState == SCARD_UNKNOWN) 409 { 410 /* 411 * Change the status structure 412 */ 413 Log2(PCSC_LOG_INFO, "Card Removed From %s", lpcReader); 414 /* 415 * Notify the card has been removed 416 */ 417 RFSetReaderEventState(rContext, SCARD_REMOVED); 418 419 rs->xcardAtrLength(0); 420 rs->xcardProtocol(SCARD_PROTOCOL_UNSET); 421 DWORD readerStateTmp = rs->xreaderState(); 422 readerStateTmp |= SCARD_ABSENT; 423 readerStateTmp &= ~SCARD_UNKNOWN; 424 readerStateTmp &= ~SCARD_PRESENT; 425 readerStateTmp &= ~SCARD_POWERED; 426 readerStateTmp &= ~SCARD_NEGOTIABLE; 427 readerStateTmp &= ~SCARD_SWALLOWED; 428 readerStateTmp &= ~SCARD_SPECIFIC; 429 rs->xreaderState(readerStateTmp); 430 dwCurrentState = SCARD_ABSENT; 431 432 SYS_MMapSynchronize((void *) rContext->readerState, pageSize); 433 } 434 435 } 436 else if (dwStatus & SCARD_PRESENT) 437 { 438 if (dwCurrentState == SCARD_ABSENT || 439 dwCurrentState == SCARD_UNKNOWN) 440 { 441 /* 442 * Power and reset the card 443 */ 444 SYS_USleep(PCSCLITE_STATUS_WAIT); 445 DWORD tmpCardAtrLength = MAX_ATR_SIZE; 446 rv = IFDPowerICC(rContext, IFD_POWER_UP, rs->xcardAtr(), &tmpCardAtrLength); 447 448 /* the protocol is unset after a power on */ 449 rs->xcardProtocol(SCARD_PROTOCOL_UNSET); 450 451 secdebug("pcscd", "EHStatusHandlerThread: power-and-reset call to IFDPowerICC: %d [%04X]", rv, rv); 452 453 DWORD readerStateTmp = rs->xreaderState(); 454 if (rv == IFD_SUCCESS) 455 { 456 rs->xcardAtrLength(tmpCardAtrLength); 457 458 readerStateTmp |= SCARD_PRESENT; 459 readerStateTmp &= ~SCARD_ABSENT; 460 readerStateTmp |= SCARD_POWERED; 461 readerStateTmp |= SCARD_NEGOTIABLE; 462 readerStateTmp &= ~SCARD_SPECIFIC; 463 readerStateTmp &= ~SCARD_UNKNOWN; 464 readerStateTmp &= ~SCARD_SWALLOWED; 465 rs->xreaderState(readerStateTmp); 466 467 /* 468 * Notify the card has been reset 469 */ 470 RFSetReaderEventState(rContext, SCARD_RESET); 471 } 472 else 473 { 474 readerStateTmp |= SCARD_PRESENT; 475 readerStateTmp &= ~SCARD_ABSENT; 476 readerStateTmp |= SCARD_SWALLOWED; 477 readerStateTmp &= ~SCARD_POWERED; 478 readerStateTmp &= ~SCARD_NEGOTIABLE; 479 readerStateTmp &= ~SCARD_SPECIFIC; 480 readerStateTmp &= ~SCARD_UNKNOWN; 481 rs->xreaderState(readerStateTmp); 482 rs->xcardAtrLength(0); 483 } 484 485 dwCurrentState = SCARD_PRESENT; 486 487 SYS_MMapSynchronize((void *) rContext->readerState, pageSize); 488 489 Log2(PCSC_LOG_INFO, "Card inserted into %s", lpcReader); 490 491 if (rv == IFD_SUCCESS) 492 { 493 if (rs->xcardAtrLength() > 0) 494 LogXxd(PCSC_LOG_INFO, "Card ATR: ", rs->xcardAtr(), rs->xcardAtrLength()); 495 else 496 Log1(PCSC_LOG_INFO, "Card ATR: (NULL)"); 497 } 498 else 499 Log1(PCSC_LOG_ERROR,"Error powering up card."); 500 } 501 } 502 503 if (ReaderContextIsLocked(rContext)) 504 { 505 /* 506 * Exit and notify the caller 507 */ 508 secdebug("pcscd", "EHStatusHandlerThread: parent requested shutdown"); 509 ReaderContextUnlock(rContext); 510 SYS_ThreadDetach(rContext->pthThread); 511 SYS_ThreadExit(0); 512 } 513 514 /* 515 * Sharing may change w/o an event pass it on 516 */ 517 518 if (dwReaderSharing != (uint32_t)rContext->dwContexts) 519 { 520 dwReaderSharing = rContext->dwContexts; 521 rs->sharing(dwReaderSharing); 522 SYS_MMapSynchronize((void *) rContext->readerState, pageSize); 523 } 524 525 SYS_USleep(PCSCLITE_STATUS_POLL_RATE); 526 } 527} 528 529void EHSetSharingEvent(PREADER_CONTEXT rContext, DWORD dwValue) 530{ 531 PCSCD::SharedReaderState *rs = PCSCD::SharedReaderState::overlay(rContext->readerState); 532 rs->xlockState(dwValue); 533} 534