2 * Copyright (c) 2004 Apple Computer, Inc. All Rights Reserved.
4 * @APPLE_LICENSE_HEADER_START@
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
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.
21 * @APPLE_LICENSE_HEADER_END@
26 // pcscmonitor - use PCSC to monitor smartcard reader/card state for securityd
28 // PCSCMonitor 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.
32 // PCSCMonitor 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 // (*) A Child, to watch and manage the pcscd process
38 #include "pcscmonitor.h"
39 #include <security_utilities/logging.h>
40 #include <IOKit/usb/IOUSBLib.h>
44 // Fixed configuration parameters
46 static const char PCSCD_EXEC_PATH
[] = "/usr/sbin/pcscd"; // override with $PCSCDAEMON
47 static const char PCSCD_WORKING_DIR
[] = "/var/run/pcscd"; // pcscd's working directory
48 static const Time::Interval
PCSCD_IDLE_SHUTDOWN(120); // kill daemon if no devices present
52 // Construct a PCSCMonitor.
53 // We strongly assume there's only one of us around here.
55 // Note that this constructor may well run before the server loop has started.
56 // Don't call anything here that requires an active server loop (like Server::active()).
57 // In fact, you should push all the hard work into a timer, so as not to hold up the
58 // general startup process.
60 PCSCMonitor::PCSCMonitor(Server
&server
, const char* pathToCache
, ServiceLevel level
)
61 : Listener(kNotificationDomainPCSC
, SecurityServer::kNotificationAllEvents
),
62 MachServer::Timer(true), // "heavy" timer task
65 cachePath (pathToCache
),
67 mTimerAction(&PCSCMonitor::initialSetup
),
70 // do all the smartcard-related work once the event loop has started
71 server
.setTimer(this, Time::now()); // ASAP
76 // Poll PCSC for smartcard status.
77 // We are enumerating all readers on each call.
79 void PCSCMonitor::pollReaders()
81 // open PCSC session if it's not already open
85 vector
<string
> names
; // will hold reader name C strings throughout
86 mSession
.listReaders(names
);
87 size_t count
= names
.size();
88 secdebug("pcsc", "%ld reader(s) in system", count
);
90 // build the PCSC status inquiry array
91 vector
<PCSC::ReaderState
> states(count
); // reader status array (PCSC style)
92 for (unsigned int n
= 0; n
< count
; n
++) {
93 PCSC::ReaderState
&state
= states
[n
];
94 ReaderMap::iterator it
= mReaders
.find(names
[n
]);
95 if (it
== mReaders
.end()) { // new reader
97 state
.name(names
[n
].c_str());
98 // lastKnown(PCSC_STATE_UNKNOWN)
99 // userData<Reader>() = NULL
101 state
= it
->second
->pcscState();
102 state
.name(names
[n
].c_str()); // OUR pointer
103 state
.lastKnown(state
.state());
104 state
.userData
<Reader
>() = it
->second
;
108 // now ask PCSC for status changes
109 mSession
.statusChange(states
);
111 if (Debug::dumping("pcsc"))
112 for (unsigned int n
= 0; n
< count
; n
++)
116 // make a set of previously known reader objects (to catch those who disappeared)
118 copy_second(mReaders
.begin(), mReaders
.end(), inserter(current
, current
.end()));
120 // match state array against them
121 for (unsigned int n
= 0; n
< count
; n
++) {
122 PCSC::ReaderState
&state
= states
[n
];
123 if (Reader
*reader
= state
.userData
<Reader
>()) {
124 // if PCSC flags a change, notify the Reader
126 reader
->update(state
);
127 // accounted for this reader
128 current
.erase(reader
);
130 RefPointer
<Reader
> newReader
= new Reader(getTokenCache (), state
);
131 mReaders
.insert(make_pair(state
.name(), newReader
));
132 Syslog::notice("Token reader %s inserted into system", state
.name());
133 newReader
->update(state
); // initial state setup
137 // now deal with vanished readers
138 for (ReaderSet::iterator it
= current
.begin(); it
!= current
.end(); it
++) {
139 secdebug("pcsc", "removing reader %s", (*it
)->name().c_str());
140 Syslog::notice("Token reader %s removed from system", (*it
)->name().c_str());
141 (*it
)->kill(); // prepare to die
142 mReaders
.erase((*it
)->name()); // remove from reader map
147 TokenCache
& PCSCMonitor::getTokenCache ()
150 cache
= new TokenCache(cachePath
.c_str ());
158 void PCSCMonitor::launchPcscd()
161 secdebug("pcsc", "launching pcscd to handle smartcard device(s)");
162 assert(Child::state() != alive
);
166 // if pcscd doesn't report a reader found soon, we'll kill it off
172 // Code to launch pcscd (run in child as a result of Child::fork())
174 void PCSCMonitor::childAction()
176 // move aside any old play area
177 const char *aside
= tempnam("/tmp", "pcscd");
178 if (::rename(PCSCD_WORKING_DIR
, aside
))
180 case ENOENT
: // no /tmp/pcsc (fine)
183 secdebug("pcsc", "failed too move %s - errno=%d", PCSCD_WORKING_DIR
, errno
);
187 secdebug("pcsc", "old /tmp/pcsc moved to %s", aside
);
189 // lessen the pain for debugging
191 freopen("/tmp/pcsc.debuglog", "a", stdout
); // shut up pcsc dumps to stdout
194 // execute the daemon
195 const char *pcscdPath
= PCSCD_EXEC_PATH
;
196 if (const char *env
= getenv("PCSCDAEMON"))
198 secdebug("pcsc", "exec(%s,-f)", pcscdPath
);
199 execl(pcscdPath
, pcscdPath
, "-f", NULL
);
205 // These events are sent by pcscd for our (sole) benefit.
207 void PCSCMonitor::notifyMe(SecurityServer::NotificationDomain domain
,
208 SecurityServer::NotificationEvent event
, const CssmData
&data
)
210 Server::active().longTermActivity();
211 StLock
<Mutex
> _(*this);
212 assert(mServiceLevel
== externalDaemon
|| Child::state() == alive
);
214 scheduleTimer(mReaders
.empty() && !mGoingToSleep
);
219 // Power event notifications
221 void PCSCMonitor::systemWillSleep()
223 StLock
<Mutex
> _(*this);
224 secdebug("pcsc", "setting sleep marker (%ld readers as of now)", mReaders
.size());
225 mGoingToSleep
= true;
226 server
.clearTimer(this);
229 void PCSCMonitor::systemIsWaking()
231 StLock
<Mutex
> _(*this);
232 secdebug("pcsc", "clearing sleep marker (%ld readers as of now)", mReaders
.size());
233 mGoingToSleep
= false;
234 scheduleTimer(mReaders
.empty());
241 void PCSCMonitor::action()
243 StLock
<Mutex
> _(*this);
244 (this->*mTimerAction
)();
245 mTimerAction
= &PCSCMonitor::noDeviceTimeout
;
250 // Update the timeout timer as requested (and indicated by context)
252 void PCSCMonitor::scheduleTimer(bool enable
)
254 if (Child::state() == alive
) // we ran pcscd; let's manage it
256 secdebug("pcsc", "setting idle timer for %g seconds", PCSCD_IDLE_SHUTDOWN
.seconds());
257 server
.setTimer(this, PCSCD_IDLE_SHUTDOWN
);
258 } else if (Timer::scheduled()) {
259 secdebug("pcsc", "clearing idle timer");
260 server
.clearTimer(this);
266 // Perform the initial PCSC subsystem initialization.
267 // This runs (shortly) after securityd is fully functional and the
268 // server loop has started.
270 void PCSCMonitor::initialSetup()
272 switch (mServiceLevel
) {
274 secdebug("pcsc", "smartcard operation is FORCED OFF");
278 secdebug("pcsc", "pcscd launch is forced on");
283 secdebug("pcsc", "using external pcscd (if any); no launch operations");
287 secdebug("pcsc", "setting up automatic PCSC management in %s mode",
288 mServiceLevel
== conservative
? "conservative" : "aggressive");
290 // receive Mach-based IOKit notifications through mIOKitNotifier
291 server
.add(mIOKitNotifier
);
293 // receive power event notifications (through our IOPowerWatcher personality)
296 // ask for IOKit notifications for all new USB devices and process present ones
297 IOKit::DeviceMatch
usbSelector(kIOUSBInterfaceClassName
);
298 IOKit::DeviceMatch
pcCardSelector("IOPCCard16Device");
299 mIOKitNotifier
.add(usbSelector
, *this); // this will scan existing USB devices
300 mIOKitNotifier
.add(pcCardSelector
, *this); // ditto for PC Card devices
301 if (mServiceLevel
== aggressive
) {
302 // catch custom non-composite USB devices - they don't have IOServices attached
303 IOKit::DeviceMatch
customUsbSelector(::IOServiceMatching("IOUSBDevice"));
304 mIOKitNotifier
.add(customUsbSelector
, *this); // ditto for custom USB devices
309 // we are NOT scanning for PCSC devices here. Pcscd will send us a notification when it's up
314 // This function is called (as a timer function) when there haven't been any (recognized)
315 // smartcard devicees in the system for a while.
317 void PCSCMonitor::noDeviceTimeout()
319 secdebug("pcsc", "killing pcscd (no smartcard devices present for %g seconds)",
320 PCSCD_IDLE_SHUTDOWN
.seconds());
321 assert(mReaders
.empty());
322 Child::kill(SIGTERM
);
327 // IOKit device event notification.
328 // Here we listen for newly inserted devices and check whether to launch pcscd.
330 void PCSCMonitor::ioChange(IOKit::DeviceIterator
&iterator
)
332 assert(mServiceLevel
!= externalDaemon
&& mServiceLevel
!= forcedOff
);
333 if (Child::state() == alive
) {
334 secdebug("pcsc", "pcscd is alive; ignoring device insertion(s)");
337 secdebug("pcsc", "processing device insertion notices");
338 while (IOKit::Device dev
= iterator()) {
340 switch (deviceSupport(dev
)) {
345 launch
= (mServiceLevel
== aggressive
);
355 secdebug("pcsc", "no relevant devices found");
360 // Check an IOKit device that's just come online to see if it's
361 // a smartcard device of some sort.
363 PCSCMonitor::DeviceSupport
PCSCMonitor::deviceSupport(const IOKit::Device
&dev
)
366 secdebug("scsel", "%s", dev
.path().c_str());
367 if (CFRef
<CFNumberRef
> cfInterface
= dev
.property
<CFNumberRef
>("bInterfaceClass"))
368 switch (IFDEBUG(uint32 clas
=) cfNumber(cfInterface
)) {
369 case kUSBChipSmartCardInterfaceClass
: // CCID smartcard reader - go
370 secdebug("scsel", " CCID smartcard reader recognized");
372 case kUSBVendorSpecificInterfaceClass
:
373 secdebug("scsel", " Vendor-specific interface - possible match");
376 secdebug("scsel", " interface class %ld is not a smartcard device", clas
);
379 if (CFRef
<CFNumberRef
> cfDevice
= dev
.property
<CFNumberRef
>("bDeviceClass"))
380 if (cfNumber(cfDevice
) == kUSBVendorSpecificClass
) {
381 secdebug("scsel", " Vendor-specific device - possible match");
386 secdebug("scsel", " exception while examining device - ignoring it");
393 // This gets called (by the Unix/Child system) when pcscd has died for any reason
395 void PCSCMonitor::dying()
397 Server::active().longTermActivity();
398 StLock
<Mutex
> _(*this);
399 assert(Child::state() == dead
);
400 if (!mReaders
.empty()) {
401 // uh-oh. We had readers connected when pcscd suddenly left
402 secdebug("pcsc", "%ld readers were present when pcscd died", mReaders
.size());
403 for (ReaderMap::const_iterator it
= mReaders
.begin(); it
!= mReaders
.end(); it
++) {
404 Reader
*reader
= it
->second
;
405 secdebug("pcsc", "removing reader %s", reader
->name().c_str());
406 reader
->kill(); // prepare to die
408 mReaders
.erase(mReaders
.begin(), mReaders
.end());
409 secdebug("pcsc", "orphaned readers cleared");
411 //@@@ this is where we would attempt a restart, if we wanted to...