/*
- * Copyright (c) 2004 Apple Computer, Inc. All Rights Reserved.
+ * Copyright (c) 2004,2007 Apple Inc. All Rights Reserved.
*
* @APPLE_LICENSE_HEADER_START@
*
Token::~Token()
{
- secdebug("token", "%p (%s:%ld) destroyed",
+ secdebug("token", "%p (%s:%d) destroyed",
this, mGuid.toString().c_str(), mSubservice);
}
}
+//
+// We override getAcl to provide PIN state feedback
+//
+void Token::getAcl(const char *tag, uint32 &count, AclEntryInfo *&acls)
+{
+ if (pinFromAclTag(tag, "?")) { // read from tokend - do not cache
+ AclEntryInfo *racls;
+ token().tokend().getAcl(aclKind(), tokenHandle(), tag, count, racls);
+ // make a chunk-copy because that's the contract we have with the caller
+ acls = Allocator::standard().alloc<AclEntryInfo>(count * sizeof(AclEntryInfo));
+ memcpy(acls, racls, count * sizeof(AclEntryInfo));
+ ChunkCopyWalker copy;
+ for (uint32 n = 0; n < count; n++)
+ walk(copy, acls[n]);
+ return;
+ }
+
+ TokenAcl::cssmGetAcl(tag, count, acls);
+}
+
+
//
// Reset management.
// A Token has a "reset level", a number that is incremented whenever a token
void Token::resetAcls()
{
- StLock<Mutex> _(*this);
- mResetLevel++;
- secdebug("token", "%p reset (level=%d, propagating to %ld common(s)",
- this, mResetLevel, mCommons.size());
- for (CommonSet::const_iterator it = mCommons.begin(); it != mCommons.end(); it++)
- RefPointer<TokenDbCommon>(*it)->resetAcls();
+ CommonSet tmpCommons;
+ {
+ StLock<Mutex> _(*this);
+ mResetLevel++;
+ secdebug("token", "%p reset (level=%d, propagating to %ld common(s)",
+ this, mResetLevel, mCommons.size());
+ // Make a copy to avoid deadlock with TokenDbCommon lock
+ tmpCommons = mCommons;
+ }
+ for (CommonSet::const_iterator it = tmpCommons.begin(); it != tmpCommons.end();)
+ RefPointer<TokenDbCommon>(*it++)->resetAcls();
}
void Token::addCommon(TokenDbCommon &dbc)
{
+ secdebug("token", "%p addCommon TokenDbCommon %p", this, &dbc);
mCommons.insert(&dbc);
}
void Token::removeCommon(TokenDbCommon &dbc)
{
- assert(mCommons.find(&dbc) != mCommons.end());
- mCommons.erase(&dbc);
+ secdebug("token", "%p removeCommon TokenDbCommon %p", this, &dbc);
+ if (mCommons.find(&dbc) != mCommons.end())
+ mCommons.erase(&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<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=%ld, 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=%ld, temporary)",
+ secdebug("token", "%p using %s (score=%d, temporary)",
this, tokend->bundlePath().c_str(), tokend->score());
mCache = new TokenCache::Token(reader().cache);
}
mPrintName = printName;
if (mPrintName.empty()) {
// last resort - new card and tokend didn't give us one
- snprintf(printName, sizeof(printName), "smart card #%ld", mSubservice);
+ snprintf(printName, sizeof(printName), "smart card #%d", mSubservice);
mPrintName = printName;
}
if (mCache->type() != TokenCache::Token::existing)
secdebug("token", "%p installing MDS from %s(%s)", this,
tokend->bundlePath().c_str(),
mdsDirectory[0] ? mdsDirectory : "ALL");
- string holdGuid = mGuid.toString(); // extend lifetime of .toString()
+ string holdGuid = mGuid.toString(); // extend lifetime of std::string
+ string holdTokenUid;
+ if (tokend->hasTokenUid())
+ holdTokenUid = tokend->tokenUid();
+ string holdPrintName = this->printName();
MDS_InstallDefaults mdsDefaults = {
holdGuid.c_str(),
mSubservice,
- tokend->hasTokenUid() ? tokend->tokenUid().c_str() : "",
- this->printName().c_str()
+ holdTokenUid.c_str(),
+ holdPrintName.c_str()
};
mds().install(&mdsDefaults,
tokend->bundlePath().c_str(),
slot.name().c_str(), mPrintName.c_str(),
mTokend->hasTokenUid() ? mTokend->tokenUid().c_str() : "NO UID",
mSubservice, mTokend->bundleIdentifier().c_str());
- secdebug("token", "%p inserted as %s:%ld", this, mGuid.toString().c_str(), mSubservice);
+ secdebug("token", "%p inserted as %s:%d", this, mGuid.toString().c_str(), mSubservice);
} catch (const CommonError &err) {
Syslog::notice("token in reader %s cannot be used (error %ld)", slot.name().c_str(), err.osStatus());
secdebug("token", "exception during insertion processing");
this, &reader(), reader().name().c_str());
if (mTokend)
mTokend->faultRelay(NULL); // unregister (no more faults, please)
- notify(kNotificationCDSARemoval);
mds().uninstall(mGuid.toString().c_str(), mSubservice);
+ secdebug("token", "%p mds uninstall complete", this);
this->kill();
+ secdebug("token", "%p kill complete", this);
+ notify(kNotificationCDSARemoval);
secdebug("token", "%p removal complete", this);
}
notify(kNotificationCDSAFailure);
// cast off our TokenDaemon for good
- mTokend = NULL;
+//>>> mTokend = NULL;
}
// if this is a synchronous fault, abort this operation now
//
void Token::kill()
{
- StLock<Mutex> _(*this);
- if (mTokend)
+ // Avoid holding the lock across call to resetAcls
+ // This can cause deadlock on card removal
{
- mTokend = NULL; // cast loose our tokend (if any)
- // Take us out of the map
- StLock<Mutex> _(mSSIDLock);
- SSIDMap::iterator it = mSubservices.find(mSubservice);
- assert(it != mSubservices.end() && it->second == this);
- if (it != mSubservices.end() && it->second == this)
- mSubservices.erase(it);
+ StLock<Mutex> _(*this);
+ if (mTokend)
+ {
+ mTokend = NULL; // cast loose our tokend (if any)
+ // Take us out of the map
+ StLock<Mutex> _(mSSIDLock);
+ SSIDMap::iterator it = mSubservices.find(mSubservice);
+ assert(it != mSubservices.end() && it->second == this);
+ if (it != mSubservices.end() && it->second == this)
+ mSubservices.erase(it);
+ }
}
-
+
resetAcls(); // release our TokenDbCommons
PerGlobal::kill(); // generic action
void Token::notify(NotificationEvent event)
{
NameValueDictionary nvd;
- CssmSubserviceUid ssuid(mGuid, NULL, mSubservice,
- CSSM_SERVICE_DL | CSSM_SERVICE_CSP);
+ CssmSubserviceUid ssuid(mGuid, NULL, h2n (mSubservice),
+ h2n(CSSM_SERVICE_DL | CSSM_SERVICE_CSP));
nvd.Insert(new NameValuePair(SSUID_KEY, CssmData::wrap(ssuid)));
CssmData data;
nvd.Export(data);
RefPointer<TokenDaemon> Token::chooseTokend()
{
//@@@ CodeRepository should learn to update from disk changes to be re-usable
- CodeRepository<GenericBundle> candidates("Security/tokend", ".tokend", "TOKENDAEMONPATH", false);
+ CodeRepository<Bundle> candidates("Security/tokend", ".tokend", "TOKENDAEMONPATH", false);
candidates.update();
//@@@ we could sort by reverse "maxScore" and avoid launching those who won't cut it anyway...
RefPointer<TokenDaemon> leader;
- for (CodeRepository<GenericBundle>::const_iterator it = candidates.begin();
+ 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;
void Token::dumpNode()
{
PerGlobal::dumpNode();
- Debug::dump(" %s[%ld] tokend=%p",
+ Debug::dump(" %s[%d] tokend=%p",
mGuid.toString().c_str(), mSubservice, mTokend.get());
}