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