From 2f9dc5dc91d2adf097a030bc067f00235982c474 Mon Sep 17 00:00:00 2001 From: Apple Date: Fri, 4 Apr 2008 22:36:39 +0000 Subject: [PATCH] securityd-33639.tar.gz --- securityd.xcodeproj/project.pbxproj | 8 +- src/main.cpp | 7 +- src/pcscmonitor.cpp | 131 +++++++++++++++++++++------- src/pcscmonitor.h | 14 ++- src/reader.cpp | 27 ++++-- src/reader.h | 17 +++- src/server.cpp | 9 ++ src/token.cpp | 37 ++++---- src/token.h | 2 +- 9 files changed, 186 insertions(+), 66 deletions(-) diff --git a/securityd.xcodeproj/project.pbxproj b/securityd.xcodeproj/project.pbxproj index 1466aa3..c7a7e71 100644 --- a/securityd.xcodeproj/project.pbxproj +++ b/securityd.xcodeproj/project.pbxproj @@ -897,7 +897,7 @@ BUILD_VARIANTS = debug; COPY_PHASE_STRIP = NO; CSSM_HEADERS = "$(BUILT_PRODUCTS_DIR)/Security.framework/Headers:$(SYSTEM_LIBRARY_DIR)/Frameworks/Security.framework/Headers"; - CURRENT_PROJECT_VERSION = 33082; + CURRENT_PROJECT_VERSION = 33639; FRAMEWORK_SEARCH_PATHS = ( /usr/local/SecurityPieces/Frameworks, /usr/local/SecurityPieces/Components/securityd, @@ -959,7 +959,7 @@ debug, ); CSSM_HEADERS = "$(BUILT_PRODUCTS_DIR)/Security.framework/Headers:$(SYSTEM_LIBRARY_DIR)/Frameworks/Security.framework/Headers"; - CURRENT_PROJECT_VERSION = 33082; + CURRENT_PROJECT_VERSION = 33639; DEAD_CODE_STRIPPING = YES; EXPORTED_SYMBOLS_FILE = "$(SRCROOT)/src/securityd.exp"; FRAMEWORK_SEARCH_PATHS = ( @@ -1018,7 +1018,7 @@ buildSettings = { BUILD_VARIANTS = normal; COPY_PHASE_STRIP = NO; - CURRENT_PROJECT_VERSION = 33082; + CURRENT_PROJECT_VERSION = 33639; EXPORTED_SYMBOLS_FILE = "$(SRCROOT)/src/securityd.exp"; FRAMEWORK_SEARCH_PATHS = ( /usr/local/SecurityPieces/Frameworks, @@ -1075,7 +1075,7 @@ normal, debug, ); - CURRENT_PROJECT_VERSION = 33082; + CURRENT_PROJECT_VERSION = 33639; EXPORTED_SYMBOLS_FILE = "$(SRCROOT)/src/securityd.exp"; FRAMEWORK_SEARCH_PATHS = ( /usr/local/SecurityPieces/Frameworks, diff --git a/src/main.cpp b/src/main.cpp index 683a907..159e4ab 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -76,6 +76,7 @@ static PCSCMonitor::ServiceLevel scOptions(const char *optionString); static Port gMainServerPort; +PCSCMonitor *gPCSC; // @@ -237,8 +238,10 @@ int main(int argc, char *argv[]) secdebug("SS", "Cannot handle SIGPIPE: errno=%d", errno); #if !defined(NDEBUG) if (signal(SIGUSR1, handleSignals) == SIG_ERR) - secdebug("SS", "Cannot handle SIGHUP: errno=%d", errno); + secdebug("SS", "Cannot handle SIGUSR1: errno=%d", errno); #endif //NDEBUG + if (signal(SIGUSR2, handleSignals) == SIG_ERR) + secdebug("SS", "Cannot handle SIGUSR2: errno=%d", errno); // create an Authorization engine Authority authority(authorizationConfig); @@ -287,7 +290,7 @@ int main(int argc, char *argv[]) #endif //NDEBUG // create a smartcard monitor to manage external token devices - new PCSCMonitor(server, tokenCacheDir, scOptions(smartCardOptions)); + gPCSC = new PCSCMonitor(server, tokenCacheDir, scOptions(smartCardOptions)); // create the RootSession object (if -d, give it graphics and tty attributes) RootSession rootSession(server, diff --git a/src/pcscmonitor.cpp b/src/pcscmonitor.cpp index 2385507..d38efc9 100644 --- a/src/pcscmonitor.cpp +++ b/src/pcscmonitor.cpp @@ -66,11 +66,11 @@ PCSCMonitor::PCSCMonitor(Server &server, const char* pathToCache, ServiceLevel l : Listener(kNotificationDomainPCSC, SecurityServer::kNotificationAllEvents), MachServer::Timer(true), // "heavy" timer task server(server), - cache (NULL), - cachePath (pathToCache), mServiceLevel(level), mTimerAction(&PCSCMonitor::initialSetup), - mGoingToSleep(false) + mGoingToSleep(false), + mCachePath(pathToCache), + mTokenCache(NULL) { // do all the smartcard-related work once the event loop has started server.setTimer(this, Time::now()); // ASAP @@ -132,48 +132,60 @@ void PCSCMonitor::pollReaders() // accounted for this reader current.erase(reader); } else { - RefPointer newReader = new Reader(getTokenCache (), state); + RefPointer newReader = new Reader(tokenCache(), state); mReaders.insert(make_pair(state.name(), newReader)); Syslog::notice("Token reader %s inserted into system", state.name()); newReader->update(state); // initial state setup } } - // now deal with vanished readers + // now deal with known readers that PCSC did not report for (ReaderSet::iterator it = current.begin(); it != current.end(); it++) { - secdebug("pcsc", "removing reader %s", (*it)->name().c_str()); - Syslog::notice("Token reader %s removed from system", (*it)->name().c_str()); - (*it)->kill(); // prepare to die - mReaders.erase((*it)->name()); // remove from reader map + switch ((*it)->type()) { + case Reader::pcsc: + // previous PCSC reader - was removed from system + secdebug("pcsc", "removing reader %s", (*it)->name().c_str()); + Syslog::notice("Token reader %s removed from system", (*it)->name().c_str()); + (*it)->kill(); // prepare to die + mReaders.erase((*it)->name()); // remove from reader map + break; + case Reader::software: + // previous software reader - keep + break; + } } } + // -// Poll PCSC for smartcard status. -// We are enumerating all readers on each call. +// Remove some types of readers // -void PCSCMonitor::clearReaders() +void PCSCMonitor::clearReaders(Reader::Type type) { if (!mReaders.empty()) { - // uh-oh. We had readers connected when pcscd suddenly left - secdebug("pcsc", "%ld readers were present when pcscd died", mReaders.size()); - for (ReaderMap::const_iterator it = mReaders.begin(); it != mReaders.end(); it++) { - Reader *reader = it->second; - secdebug("pcsc", "removing reader %s", reader->name().c_str()); - reader->kill(); // prepare to die + secdebug("pcsc", "%ld readers present - clearing type %d", mReaders.size(), type); + for (ReaderMap::iterator it = mReaders.begin(); it != mReaders.end(); ) { + ReaderMap::iterator cur = it++; + Reader *reader = cur->second; + if (reader->isType(type)) { + secdebug("pcsc", "removing reader %s", reader->name().c_str()); + reader->kill(); // prepare to die + mReaders.erase(cur); + } } - mReaders.erase(mReaders.begin(), mReaders.end()); - secdebug("pcsc", "orphaned readers cleared"); } } -TokenCache& PCSCMonitor::getTokenCache () + +// +// Poll PCSC for smartcard status. +// We are enumerating all readers on each call. +// +TokenCache& PCSCMonitor::tokenCache() { - if (cache == NULL) { - cache = new TokenCache(cachePath.c_str ()); - } - - return *cache; + if (mTokenCache == NULL) + mTokenCache = new TokenCache(mCachePath.c_str()); + return *mTokenCache; } @@ -233,10 +245,7 @@ void PCSCMonitor::notifyMe(Notification *message) StLock _(*this); assert(mServiceLevel == externalDaemon || Child::state() == alive); if (message->event == kNotificationPCSCInitialized) - { - clearReaders(); -// mSession.close(); - } + clearReaders(Reader::pcsc); pollReaders(); scheduleTimer(mReaders.empty() && !mGoingToSleep); } @@ -304,10 +313,12 @@ void PCSCMonitor::initialSetup() case forcedOn: secdebug("pcsc", "pcscd launch is forced on"); launchPcscd(); + startSoftTokens(); break; case externalDaemon: secdebug("pcsc", "using external pcscd (if any); no launch operations"); + startSoftTokens(); break; default: @@ -330,6 +341,10 @@ void PCSCMonitor::initialSetup() IOKit::DeviceMatch customUsbSelector(::IOServiceMatching("IOUSBDevice")); mIOKitNotifier.add(customUsbSelector, *this); // ditto for custom USB devices } + + // find and start software tokens + startSoftTokens(); + break; } @@ -459,6 +474,60 @@ void PCSCMonitor::dying() Server::active().longTermActivity(); StLock _(*this); assert(Child::state() == dead); - clearReaders(); + clearReaders(Reader::pcsc); //@@@ this is where we would attempt a restart, if we wanted to... } + + +// +// Software token support +// +void PCSCMonitor::startSoftTokens() +{ + // clear all software readers. This will kill the respective TokenDaemons + clearReaders(Reader::software); + + // scan for new ones + CodeRepository candidates("Security/tokend", ".tokend", "TOKENDAEMONPATH", false); + candidates.update(); + for (CodeRepository::iterator it = candidates.begin(); it != candidates.end(); ++it) { + if (CFTypeRef type = (*it)->infoPlistItem("TokendType")) + if (CFEqual(type, CFSTR("software"))) + loadSoftToken(*it); + } +} + +void PCSCMonitor::loadSoftToken(Bundle *tokendBundle) +{ + try { + string bundleName = tokendBundle->identifier(); + + // prepare a virtual reader, removing any existing one (this would kill a previous tokend) + assert(mReaders.find(bundleName) == mReaders.end()); // not already present + RefPointer reader = new Reader(tokenCache(), bundleName); + + // now launch the tokend + RefPointer tokend = new TokenDaemon(tokendBundle, + reader->name(), reader->pcscState(), reader->cache); + + if (tokend->state() == ServerChild::dead) { // ah well, this one's no good + secdebug("pcsc", "softtoken %s tokend launch failed", bundleName.c_str()); + Syslog::notice("Software token %s failed to run", tokendBundle->canonicalPath().c_str()); + return; + } + + // probe the (single) tokend + if (!tokend->probe()) { // non comprende... + secdebug("pcsc", "softtoken %s probe failed", bundleName.c_str()); + Syslog::notice("Software token %s refused operation", tokendBundle->canonicalPath().c_str()); + return; + } + + // okay, this seems to work. Set it up + mReaders.insert(make_pair(reader->name(), reader)); + reader->insertToken(tokend); + Syslog::notice("Software token %s activated", bundleName.c_str()); + } catch (...) { + secdebug("pcsc", "exception loading softtoken %s - continuing", tokendBundle->identifier().c_str()); + } +} diff --git a/src/pcscmonitor.h b/src/pcscmonitor.h index 6b023e1..36bf3ed 100644 --- a/src/pcscmonitor.h +++ b/src/pcscmonitor.h @@ -37,6 +37,7 @@ #include #include #include +#include #include @@ -65,12 +66,10 @@ public: protected: void pollReaders(); - void clearReaders(); + void clearReaders(Reader::Type type); Server &server; - TokenCache *cache; - std::string cachePath; - TokenCache& getTokenCache (); + TokenCache& tokenCache(); protected: // Listener @@ -95,6 +94,10 @@ protected: void scheduleTimer(bool enable); void initialSetup(); void noDeviceTimeout(); + +public: //@@@@ + void startSoftTokens(); + void loadSoftToken(Bundle *tokendBundle); enum DeviceSupport { impossible, // certain this is not a smartcard @@ -108,6 +111,9 @@ private: ServiceLevel mServiceLevel; // level of service requested/determined void (PCSCMonitor::*mTimerAction)(); // what to do when our timer fires bool mGoingToSleep; // between sleep and wakeup; special timer handling + + std::string mCachePath; // path to cache directory + TokenCache *mTokenCache; // cache object (lazy) PCSC::Session mSession; // PCSC client session IOKit::MachPortNotificationPort mIOKitNotifier; // IOKit connection diff --git a/src/reader.cpp b/src/reader.cpp index ac476b3..32b45ab 100644 --- a/src/reader.cpp +++ b/src/reader.cpp @@ -33,11 +33,19 @@ // This does not commence state tracking; call update to start up the reader. // Reader::Reader(TokenCache &tc, const PCSC::ReaderState &state) - : cache(tc), mToken(NULL) + : 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 reader", this, name().c_str()); + secdebug("reader", "%p (%s) new PCSC reader", this, name().c_str()); +} + +Reader::Reader(TokenCache &tc, const string &identifier) + : cache(tc), mType(software), mToken(NULL) +{ + mName = identifier; + mPrintName = mName; + secdebug("reader", "%p (%s) new software reader", this, name().c_str()); } Reader::~Reader() @@ -46,6 +54,15 @@ Reader::~Reader() } +// +// Type qualification. None matches anything. +// +bool Reader::isType(Type reqType) const +{ + return reqType == this->type(); +} + + // // Killing a reader forcibly removes its Token, if any // @@ -90,7 +107,7 @@ void Reader::update(const PCSC::ReaderState &state) //@@@ 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(); + insertToken(NULL); } else { secdebug("reader", "%p (%s) unexpected state change (0x%lx to 0x%lx)", this, name().c_str(), oldState, state.state()); @@ -101,10 +118,10 @@ void Reader::update(const PCSC::ReaderState &state) } -void Reader::insertToken() +void Reader::insertToken(TokenDaemon *tokend) { RefPointer token = new Token(); - token->insert(*this); + token->insert(*this, tokend); mToken = token; addReference(*token); secdebug("reader", "%p (%s) inserted token %p", diff --git a/src/reader.h b/src/reader.h index 84cb931..8e3d0d9 100644 --- a/src/reader.h +++ b/src/reader.h @@ -40,9 +40,17 @@ // class Reader : public PerGlobal { public: - Reader(TokenCache &cache, const PCSC::ReaderState &state); + Reader(TokenCache &cache, const PCSC::ReaderState &state); // PCSC managed + Reader(TokenCache &cache, const std::string &name); // software ~Reader(); + enum Type { + pcsc, // represents PCSC-managed reader + software // software (virtual) reader, + }; + Type type() const { return mType; } + bool isType(Type type) const; + TokenCache &cache; void kill(); @@ -50,16 +58,17 @@ public: string name() const { return mName; } string printName() const { return mPrintName; } const PCSC::ReaderState &pcscState() const { return mState; } - + + void insertToken(TokenDaemon *tokend); void update(const PCSC::ReaderState &state); + void removeToken(); IFDUMP(void dumpNode()); protected: - void insertToken(); - void removeToken(); private: + Type mType; string mName; // PCSC reader name string mPrintName; // human readable name of reader PCSC::ReaderState mState; // name field not valid (use mName) diff --git a/src/server.cpp b/src/server.cpp index f07241d..9f4fb6b 100644 --- a/src/server.cpp +++ b/src/server.cpp @@ -36,6 +36,7 @@ #include "child.h" #include #include +#include "pcscmonitor.h" #include "agentquery.h" @@ -374,6 +375,14 @@ kern_return_t self_server_handleSignal(mach_port_t sport, NodeCore::dumpAll(); break; #endif //DEBUGDUMP + + case SIGUSR2: + { + extern PCSCMonitor *gPCSC; + gPCSC->startSoftTokens(); + break; + } + default: assert(false); } diff --git a/src/token.cpp b/src/token.cpp index 9b3de21..1f8f7c0 100644 --- a/src/token.cpp +++ b/src/token.cpp @@ -200,11 +200,21 @@ void Token::removeCommon(TokenDbCommon &dbc) // we're analyzing the token, determine its characteristics, and get ready to // use it. // -void Token::insert(::Reader &slot) +void Token::insert(::Reader &slot, RefPointer tokend) { try { // this might take a while... Server::active().longTermActivity(); + referent(slot); + mState = slot.pcscState(); + + if (tokend == NULL) { + // no pre-determined Tokend - search for one + if (!(tokend = chooseTokend())) { + secdebug("token", "%p no token daemons available - faulting this card", this); + fault(false); // throws + } + } // take Token lock and hold throughout insertion StLock _(*this); @@ -212,26 +222,18 @@ void Token::insert(::Reader &slot) Syslog::debug("token inserted into reader %s", slot.name().c_str()); secdebug("token", "%p begin insertion into slot %p (reader %s)", this, &slot, slot.name().c_str()); - referent(slot); - mState = slot.pcscState(); - - RefPointer tokend = chooseTokend(); - if (!tokend) { - secdebug("token", "%p no token daemons available - faulting this card", this); - fault(false); - } // tell the tokend object to relay faults to us tokend->faultRelay(this); // locate or establish cache directories if (tokend->hasTokenUid()) { - secdebug("token", "%p CHOOSING %s (score=%d, uid=\"%s\")", + secdebug("token", "%p using %s (score=%d, uid=\"%s\")", this, tokend->bundlePath().c_str(), tokend->score(), tokend->tokenUid().c_str()); mCache = new TokenCache::Token(reader().cache, tokend->bundleIdentifier() + ":" + tokend->tokenUid()); } else { - secdebug("token", "%p CHOOSING %s (score=%d, temporary)", + secdebug("token", "%p using %s (score=%d, temporary)", this, tokend->bundlePath().c_str(), tokend->score()); mCache = new TokenCache::Token(reader().cache); } @@ -448,10 +450,15 @@ RefPointer Token::chooseTokend() RefPointer leader; for (CodeRepository::const_iterator it = candidates.begin(); it != candidates.end(); it++) { + RefPointer candidate = *it; try { - // any pre-launch screening of candidate *it goes here - - RefPointer tokend = new TokenDaemon(*it, + // skip software token daemons - ineligible for automatic choosing + if (CFTypeRef type = (*it)->infoPlistItem("TokendType")) + if (CFEqual(type, CFSTR("software"))) + continue; + + // okay, launch it and let it try + RefPointer tokend = new TokenDaemon(candidate, reader().name(), reader().pcscState(), reader().cache); if (tokend->state() == ServerChild::dead) // ah well, this one's no good @@ -465,7 +472,7 @@ RefPointer Token::chooseTokend() if (!leader || tokend->score() > leader->score()) leader = tokend; // a new front runner, he is... } catch (...) { - secdebug("token", "exception setting up %s (moving on)", (*it)->canonicalPath().c_str()); + secdebug("token", "exception setting up %s (moving on)", candidate->canonicalPath().c_str()); } } return leader; diff --git a/src/token.h b/src/token.h index 3acd721..cbf3aca 100644 --- a/src/token.h +++ b/src/token.h @@ -59,7 +59,7 @@ public: std::string printName() const { return mPrintName; } TokenCache::Token &cache() const { return *mCache; } - void insert(::Reader &slot); + void insert(::Reader &slot, RefPointer tokend); void remove(); void notify(NotificationEvent event); -- 2.45.2