// // Construct a Reader // This does not commence state tracking; call update to start up the reader. // Reader::Reader(TokenCache &tc, const PCSC::ReaderState &state) : cache(tc), mType(pcsc), mToken(NULL) { mName = state.name(); // remember separate copy of name mPrintName = mName; //@@@ how to make this readable? Use IOKit information? secdebug("reader", "%p (%s) new PCSC reader", this, name().c_str()); }
void Connection::open(const PCSC::ReaderState &reader, unsigned share) { // fill in the minimum needed to identify the card MSCTokenInfo info; // set slot name in info strncpy(info.slotName, reader.name(), MAX_READERNAME); // set ATR in info assert(reader.length() <= MAX_ATR_SIZE); memcpy(info.tokenId, reader.data(), reader.length()); info.tokenIdLength = (MSCULong32)reader.length(); // establish Muscle-level connection to card Error::check(::MSCEstablishConnection(&info, share, NULL, 0, this)); mIsOpen = true; secdebug("muscle", "%p opened %s", this, info.slotName); // pull initial status updateStatus(); }
// // State transition matrix for a reader, based on PCSC state changes // void Reader::update(const PCSC::ReaderState &state) { // set new state unsigned long oldState = mState.state(); mState = state; mState.name(mName.c_str()); // (fix name pointer, unchanged) try { if (state.state(SCARD_STATE_UNAVAILABLE)) { // reader is unusable (probably being removed) secdebug("reader", "%p (%s) unavailable (0x%lx)", this, name().c_str(), state.state()); if (mToken) removeToken(); } else if (state.state(SCARD_STATE_EMPTY)) { // reader is empty (no token present) secdebug("reader", "%p (%s) empty (0x%lx)", this, name().c_str(), state.state()); if (mToken) removeToken(); } else if (state.state(SCARD_STATE_PRESENT)) { // reader has a token inserted secdebug("reader", "%p (%s) card present (0x%lx)", this, name().c_str(), state.state()); //@@@ is this hack worth it (with notifications in)?? if (mToken && CssmData(state) != CssmData(pcscState())) removeToken(); // incomplete but better than nothing //@@@ or should we call some verify-still-the-same function of tokend? //@@@ (I think pcsc will return an error if the card changed?) if (!mToken) insertToken(NULL); } else { secdebug("reader", "%p (%s) unexpected state change (0x%lx to 0x%lx)", this, name().c_str(), oldState, state.state()); } } catch (...) { secdebug("reader", "state update exception (ignored)"); } }
// // Poll PCSC for smartcard status. // We are enumerating all readers on each call. // void PCSCMonitor::Watcher::action() { // Associate this watching thread with the server, so that it is possible to call // Server::active() from inside code called from this thread. mServer.associateThread(); try { // open PCSC session mSession.open(); // Array of states, userData() points to associated Reader instance, // name points to string held by Reader::name attribute. vector<PCSC::ReaderState> states; for (;;) { // enumerate all current readers. vector<string> names; mSession.listReaders(names); secinfo("pcsc", "%ld reader(s) in system", names.size()); // Update PCSC states array with new/removed readers. for (vector<PCSC::ReaderState>::iterator stateIt = states.begin(); stateIt != states.end(); ) { Reader *reader = stateIt->userData<Reader>(); vector<string>::iterator nameIt = find(names.begin(), names.end(), reader->name()); if (nameIt == names.end()) { // Reader was removed from the system. if (Reader *reader = stateIt->userData<Reader>()) { secinfo("pcsc", "removing reader %s", stateIt->name()); Syslog::notice("Token reader %s removed from system", stateIt->name()); reader->kill(); // prepare to die mReaders.erase(reader->name()); // remove from reader map stateIt = states.erase(stateIt); } } else { // This reader is already tracked, copy its signalled state into the last known state. stateIt->lastKnown(stateIt->state()); names.erase(nameIt); stateIt++; } } // Add states for remaining (newly appeared) reader names. for (vector<string>::iterator it = names.begin(); it != names.end(); ++it) { PCSC::ReaderState state; state.clearPod(); state.set(it->c_str()); states.push_back(state); } // Now ask PCSC for status changes, and wait for them. mSession.statusChange(states, INFINITE); // Go through the states and notify changed readers. for (vector<PCSC::ReaderState>::iterator stateIt = states.begin(); stateIt != states.end(); stateIt++) { Reader *reader = stateIt->userData<Reader>(); if (!reader) { reader = new Reader(mTokenCache, *stateIt); stateIt->userData<Reader>() = reader; stateIt->name(reader->name().c_str()); mReaders.insert(make_pair(reader->name(), reader)); Syslog::notice("Token reader %s inserted into system", stateIt->name()); } // if PCSC flags a change, notify the Reader if (stateIt->changed()) { Syslog::notice("reader %s: state changed %lu -> %lu", stateIt->name(), stateIt->lastKnown(), stateIt->state()); try { reader->update(*stateIt); } catch (const exception &e) { Syslog::notice("Token in reader %s: %s", stateIt->name(), e.what()); } } } //wakeup mach server to process notifications ClientSession session(Allocator::standard(), Allocator::standard()); session.postNotification(kNotificationDomainPCSC, kNotificationPCSCStateChange, CssmData()); } } catch (const exception &e) { Syslog::error("An error '%s' occured while tracking token readers", e.what()); } }