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,
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 = (
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,
normal,
debug,
);
- CURRENT_PROJECT_VERSION = 33082;
+ CURRENT_PROJECT_VERSION = 33639;
EXPORTED_SYMBOLS_FILE = "$(SRCROOT)/src/securityd.exp";
FRAMEWORK_SEARCH_PATHS = (
/usr/local/SecurityPieces/Frameworks,
static Port gMainServerPort;
+PCSCMonitor *gPCSC;
//
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);
#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,
: 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
// accounted for this reader
current.erase(reader);
} else {
- RefPointer<Reader> newReader = new Reader(getTokenCache (), state);
+ RefPointer<Reader> 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;
}
StLock<Mutex> _(*this);
assert(mServiceLevel == externalDaemon || Child::state() == alive);
if (message->event == kNotificationPCSCInitialized)
- {
- clearReaders();
-// mSession.close();
- }
+ clearReaders(Reader::pcsc);
pollReaders();
scheduleTimer(mReaders.empty() && !mGoingToSleep);
}
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:
IOKit::DeviceMatch customUsbSelector(::IOServiceMatching("IOUSBDevice"));
mIOKitNotifier.add(customUsbSelector, *this); // ditto for custom USB devices
}
+
+ // find and start software tokens
+ startSoftTokens();
+
break;
}
Server::active().longTermActivity();
StLock<Mutex> _(*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<Bundle> candidates("Security/tokend", ".tokend", "TOKENDAEMONPATH", false);
+ candidates.update();
+ for (CodeRepository<Bundle>::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> reader = new Reader(tokenCache(), bundleName);
+
+ // now launch the tokend
+ RefPointer<TokenDaemon> 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());
+ }
+}
#include <security_utilities/powerwatch.h>
#include <security_utilities/pcsc++.h>
#include <security_utilities/iodevices.h>
+#include <security_utilities/coderepository.h>
#include <set>
protected:
void pollReaders();
- void clearReaders();
+ void clearReaders(Reader::Type type);
Server &server;
- TokenCache *cache;
- std::string cachePath;
- TokenCache& getTokenCache ();
+ TokenCache& tokenCache();
protected:
// Listener
void scheduleTimer(bool enable);
void initialSetup();
void noDeviceTimeout();
+
+public: //@@@@
+ void startSoftTokens();
+ void loadSoftToken(Bundle *tokendBundle);
enum DeviceSupport {
impossible, // certain this is not a smartcard
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
// 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()
}
+//
+// Type qualification. None matches anything.
+//
+bool Reader::isType(Type reqType) const
+{
+ return reqType == this->type();
+}
+
+
//
// Killing a reader forcibly removes its Token, if any
//
//@@@ 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());
}
-void Reader::insertToken()
+void Reader::insertToken(TokenDaemon *tokend)
{
RefPointer<Token> token = new Token();
- token->insert(*this);
+ token->insert(*this, tokend);
mToken = token;
addReference(*token);
secdebug("reader", "%p (%s) inserted token %p",
//
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();
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)
#include "child.h"
#include <mach/mach_error.h>
#include <security_utilities/ccaudit.h>
+#include "pcscmonitor.h"
#include "agentquery.h"
NodeCore::dumpAll();
break;
#endif //DEBUGDUMP
+
+ case SIGUSR2:
+ {
+ extern PCSCMonitor *gPCSC;
+ gPCSC->startSoftTokens();
+ break;
+ }
+
default:
assert(false);
}
// 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<TokenDaemon> 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<Mutex> _(*this);
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<TokenDaemon> 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);
}
RefPointer<TokenDaemon> leader;
for (CodeRepository<Bundle>::const_iterator it = candidates.begin();
it != candidates.end(); it++) {
+ RefPointer<Bundle> candidate = *it;
try {
- // any pre-launch screening of candidate *it goes here
-
- RefPointer<TokenDaemon> 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<TokenDaemon> tokend = new TokenDaemon(candidate,
reader().name(), reader().pcscState(), reader().cache);
if (tokend->state() == ServerChild::dead) // ah well, this one's no good
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;
std::string printName() const { return mPrintName; }
TokenCache::Token &cache() const { return *mCache; }
- void insert(::Reader &slot);
+ void insert(::Reader &slot, RefPointer<TokenDaemon> tokend);
void remove();
void notify(NotificationEvent event);