#include <Security/cssmacl.h>
#include <CoreServices/../Frameworks/CarbonCore.framework/Headers/MacErrors.h>
#include <Security/cssmdb.h>
-
+#include <Security/trackingallocator.h>
+#include <Security/SecCFTypes.h>
using namespace KeychainCore;
using namespace CssmClient;
RelationInfoMap &rim = mDatabaseInfoMap[relationID];
while (attributes->next(&attributeRecord, NULL, uniqueId))
{
- if(CSSM_DB_ATTRIBUTE_FORMAT(attributeRecord.at(2))==CSSM_DB_ATTRIBUTE_NAME_AS_INTEGER)
+ // @@@ this if statement was blocking tags of different naming conventions
+ //if(CSSM_DB_ATTRIBUTE_FORMAT(attributeRecord.at(2))==CSSM_DB_ATTRIBUTE_NAME_AS_INTEGER)
rim[attributeRecord.at(1)] = attributeRecord.at(0);
}
for_each_map_delete(mPrimaryKeyInfoMap.begin(), mPrimaryKeyInfoMap.end());
}
-CSSM_DB_ATTRIBUTE_FORMAT
-KeychainSchemaImpl::attributeFormatFor(CSSM_DB_RECORDTYPE recordType, uint32 attributeId) const
+const KeychainSchemaImpl::RelationInfoMap &
+KeychainSchemaImpl::relationInfoMapFor(CSSM_DB_RECORDTYPE recordType) const
{
-
DatabaseInfoMap::const_iterator dit = mDatabaseInfoMap.find(recordType);
if (dit == mDatabaseInfoMap.end())
MacOSError::throwMe(errSecNoSuchClass);
- RelationInfoMap::const_iterator rit = dit->second.find(attributeId);
- if (dit == dit->second.end())
+ return dit->second;
+}
+
+bool
+KeychainSchemaImpl::hasAttribute(CSSM_DB_RECORDTYPE recordType, uint32 attributeId) const
+{
+ const RelationInfoMap &rmap = relationInfoMapFor(recordType);
+ RelationInfoMap::const_iterator rit = rmap.find(attributeId);
+ return rit != rmap.end();
+}
+
+CSSM_DB_ATTRIBUTE_FORMAT
+KeychainSchemaImpl::attributeFormatFor(CSSM_DB_RECORDTYPE recordType, uint32 attributeId) const
+{
+ const RelationInfoMap &rmap = relationInfoMapFor(recordType);
+ RelationInfoMap::const_iterator rit = rmap.find(attributeId);
+ if (rit == rmap.end())
MacOSError::throwMe(errSecNoSuchAttr);
return rit->second;
}
CssmDbAttributeInfo
-KeychainSchemaImpl::attributeInfoForTag(UInt32 tag)
+KeychainSchemaImpl::attributeInfoFor(CSSM_DB_RECORDTYPE recordType, uint32 attributeId) const
{
CSSM_DB_ATTRIBUTE_INFO info;
+ info.AttributeFormat = attributeFormatFor(recordType, attributeId);
+ info.AttributeNameFormat = CSSM_DB_ATTRIBUTE_NAME_AS_INTEGER;
+ info.Label.AttributeID = attributeId;
- for(DatabaseInfoMap::const_iterator dit = mDatabaseInfoMap.begin(); dit != mDatabaseInfoMap.end(); ++dit)
- {
- for(RelationInfoMap::const_iterator rit = dit->second.begin(); rit != dit->second.end(); ++rit)
- {
- if(rit->first==tag)
- {
- info.AttributeNameFormat = CSSM_DB_ATTRIBUTE_NAME_AS_INTEGER;
- info.Label.AttributeID = rit->first;
- info.AttributeFormat = rit->second;
- return info;
- }
- }
- }
return info;
}
void
-KeychainSchemaImpl::getAttributeInfoForRecordType(CSSM_DB_RECORDTYPE recordType, SecKeychainAttributeInfo **Info)
+KeychainSchemaImpl::getAttributeInfoForRecordType(CSSM_DB_RECORDTYPE recordType, SecKeychainAttributeInfo **Info) const
{
- DatabaseInfoMap::const_iterator dit = mDatabaseInfoMap.find(recordType);
- if (dit == mDatabaseInfoMap.end())
- MacOSError::throwMe(errSecNoSuchClass);
+ const RelationInfoMap &rmap = relationInfoMapFor(recordType);
SecKeychainAttributeInfo *theList=reinterpret_cast<SecKeychainAttributeInfo *>(malloc(sizeof(SecKeychainAttributeInfo)));
- UInt32 capacity=32;
+ UInt32 capacity=rmap.size();
UInt32 *tagBuf=reinterpret_cast<UInt32 *>(malloc(capacity*sizeof(UInt32)));
UInt32 *formatBuf=reinterpret_cast<UInt32 *>(malloc(capacity*sizeof(UInt32)));
UInt32 i=0;
- for(RelationInfoMap::const_iterator rit = dit->second.begin(); rit != dit->second.end(); ++rit)
+
+ for (RelationInfoMap::const_iterator rit = rmap.begin(); rit != rmap.end(); ++rit)
{
- if(i>=capacity)
+ if (i>=capacity)
{
- capacity*=2;
+ capacity *= 2;
+ if (capacity <= i) capacity = i + 1;
tagBuf=reinterpret_cast<UInt32 *>(realloc(tagBuf, (capacity*sizeof(UInt32))));
formatBuf=reinterpret_cast<UInt32 *>(realloc(tagBuf, (capacity*sizeof(UInt32))));
}
const CssmAutoDbRecordAttributeInfo &
-KeychainSchemaImpl::primaryKeyInfosFor(CSSM_DB_RECORDTYPE recordType)
+KeychainSchemaImpl::primaryKeyInfosFor(CSSM_DB_RECORDTYPE recordType) const
{
- PrimaryKeyInfoMap::iterator it;
+ PrimaryKeyInfoMap::const_iterator it;
it = mPrimaryKeyInfoMap.find(recordType);
- // if the primary key attributes have already been determined,
- // return the cached results
-
if (it == mPrimaryKeyInfoMap.end())
MacOSError::throwMe(errSecNoSuchClass); // @@@ Not really but whatever.
{
}
-KeychainImpl::~KeychainImpl()
+KeychainImpl::~KeychainImpl() throw()
{
+ globals().storageManager.removeKeychain(dLDbIdentifier(), this);
+}
+
+bool
+KeychainImpl::operator ==(const KeychainImpl &keychain) const
+{
+ return dLDbIdentifier() == keychain.dLDbIdentifier();
}
KCCursor
KeychainImpl::createCursor(SecItemClass itemClass, const SecKeychainAttributeList *attrList)
{
- return KCCursor(DbCursor(mDb), itemClass, attrList);
+ StorageManager::KeychainList keychains;
+ keychains.push_back(Keychain(this));
+ return KCCursor(keychains, itemClass, attrList);
}
KCCursor
KeychainImpl::createCursor(const SecKeychainAttributeList *attrList)
{
- return KCCursor(DbCursor(mDb), attrList);
+ StorageManager::KeychainList keychains;
+ keychains.push_back(Keychain(this));
+ return KCCursor(keychains, attrList);
}
void
}
CssmAllocator &alloc = CssmAllocator::standard();
+
// @@@ Share this instance
- KeychainAclFactory aclFactory(alloc);
- // @@@ This leaks the returned credentials
const CssmData password(const_cast<void *>(inPassword), passwordLength);
- const AccessCredentials *cred = aclFactory.passwordChangeCredentials(password);
-
- // @@@ Create a nice wrapper for building the default AclEntryPrototype.
- TypedList subject(alloc, CSSM_ACL_SUBJECT_TYPE_ANY);
- AclEntryPrototype protoType(subject);
- AuthorizationGroup &authGroup = protoType.authorization();
- CSSM_ACL_AUTHORIZATION_TAG tag = CSSM_ACL_AUTHORIZATION_ANY;
- authGroup.NumberOfAuthTags = 1;
- authGroup.AuthTags = &tag;
-
- const ResourceControlContext rcc(protoType, const_cast<AccessCredentials *>(cred));
+ AclFactory::PasswordChangeCredentials pCreds (password, alloc);
+ AclFactory::AnyResourceContext rcc(pCreds);
create(&rcc);
}
void
KeychainImpl::create()
{
- CssmAllocator &alloc = CssmAllocator::standard();
- // @@@ Share this instance
- KeychainAclFactory aclFactory(alloc);
-
- const AccessCredentials *cred = aclFactory.keychainPromptUnlockCredentials();
-
- // @@@ Create a nice wrapper for building the default AclEntryPrototype.
- TypedList subject(alloc, CSSM_ACL_SUBJECT_TYPE_ANY);
- AclEntryPrototype protoType(subject);
- AuthorizationGroup &authGroup = protoType.authorization();
- CSSM_ACL_AUTHORIZATION_TAG tag = CSSM_ACL_AUTHORIZATION_ANY;
- authGroup.NumberOfAuthTags = 1;
- authGroup.AuthTags = &tag;
-
- const ResourceControlContext rcc(protoType, const_cast<AccessCredentials *>(cred));
+ AclFactory aclFactory;
+ AclFactory::AnyResourceContext rcc(aclFactory.unlockCred());
create(&rcc);
}
mDb->resourceControlContext(NULL);
mDb->dbInfo(NULL); // Clear the schema (to not break an open call later)
globals().storageManager.created(Keychain(this));
+
+ KCEventNotifier::PostKeychainEvent (kSecKeychainListChangedEvent, this, NULL);
}
void
{
// @@@ We should figure out the read/write status though a DL passthrough or some other way.
// @@@ Also should locked be unlocked read only or just read-only?
- return (mDb->isLocked() ? 0 : kSecUnlockStateStatus | kSecWrPermStatus) | kSecRdPermStatus;
+ return (mDb->isLocked() ? 0 : kSecUnlockStateStatus | kSecWritePermStatus) | kSecReadPermStatus;
}
bool
void
KeychainImpl::add(Item &inItem)
{
- PrimaryKey primaryKey = inItem->add(this);
+ Keychain keychain(this);
+ PrimaryKey primaryKey = inItem->add(keychain);
{
StLock<Mutex> _(mDbItemMapLock);
- // Use &* to get the item's Impl.
- mDbItemMap[primaryKey] = &*inItem;
+ mDbItemMap[primaryKey] = inItem.get();
}
KCEventNotifier::PostKeychainEvent(kSecAddEvent, this, inItem);
KCEventNotifier::PostKeychainEvent(kSecDeleteEvent, dLDbIdentifier(), primaryKey);
}
+
+CssmClient::CSP
+KeychainImpl::csp()
+{
+ if (!mDb->dl()->subserviceMask() & CSSM_SERVICE_CSP)
+ MacOSError::throwMe(errSecInvalidKeychain);
+
+ SSDb ssDb(safe_cast<SSDbImpl *>(&(*mDb)));
+ return ssDb->csp();
+}
+
PrimaryKey
KeychainImpl::makePrimaryKey(CSSM_DB_RECORDTYPE recordType, DbUniqueRecord &uniqueId)
{
const CssmAutoDbRecordAttributeInfo &
KeychainImpl::primaryKeyInfosFor(CSSM_DB_RECORDTYPE recordType)
{
- return keychainSchema()->primaryKeyInfosFor(recordType);
+ try {
+ return keychainSchema()->primaryKeyInfosFor(recordType);
+ } catch (const CssmCommonError &error) {
+ switch (error.cssmError()) {
+ case errSecNoSuchClass:
+ case CSSMERR_DL_INVALID_RECORDTYPE:
+ resetSchema();
+ return keychainSchema()->primaryKeyInfosFor(recordType);
+ default:
+ throw;
+ }
+ }
}
void KeychainImpl::gatherPrimaryKeyAttributes(DbAttributes& primaryKeyAttrs)
Item
KeychainImpl::item(const PrimaryKey& primaryKey)
{
+ // @@@ This retry code isn't really the right way to do this,
+ // we need to redo the locking structure here in the future.
+ bool tried = false;
+ for (;;)
{
- StLock<Mutex> _(mDbItemMapLock);
- DbItemMap::iterator it = mDbItemMap.find(primaryKey);
- if (it != mDbItemMap.end())
{
- return Item(it->second);
+ StLock<Mutex> _(mDbItemMapLock);
+ DbItemMap::iterator it = mDbItemMap.find(primaryKey);
+ if (it != mDbItemMap.end())
+ {
+ return Item(it->second);
+ }
}
- }
- // Create an item with just a primary key
- return Item(this, primaryKey);
+ try
+ {
+ // Create an item with just a primary key
+ return Item(this, primaryKey);
+ }
+ catch (const MacOSError &e)
+ {
+ if (tried || e.osStatus() != errSecDuplicateItem)
+ throw;
+ tried = true;
+ }
+ }
}
Item
return mKeychainSchema;
}
+void KeychainImpl::resetSchema()
+{
+ mKeychainSchema = NULL; // re-fetch it from db next time
+}
+
+
// Called from DbItemImpl's constructor (so it is only paritally constructed), add it to the map.
void
KeychainImpl::addItem(const PrimaryKey &primaryKey, ItemImpl *dbItemImpl)
// @@@ There is a race condition here when being called in multiple threads
// We might have added an item using add and received a notification at the same time
//assert(true);
- throw errSecDuplicateItem;
+ MacOSError::throwMe(errSecDuplicateItem);
//mDbItemMap.erase(it);
// @@@ What to do here?
}
mDbItemMap.insert(DbItemMap::value_type(primaryKey, dbItemImpl));
}
+void
+KeychainImpl::didDeleteItem(const ItemImpl *inItemImpl)
+{
+ // Sent sent by CCallbackMgr.
+ secdebug("kcnotify", "%p notified that item %p was deleted", this, inItemImpl);
+ PrimaryKey primaryKey = inItemImpl->primaryKey();
+ StLock<Mutex> _(mDbItemMapLock);
+ DbItemMap::iterator it = mDbItemMap.find(primaryKey);
+ if (it != mDbItemMap.end())
+ mDbItemMap.erase(it);
+}
+
void
KeychainImpl::removeItem(const PrimaryKey &primaryKey, const ItemImpl *inItemImpl)
{
void
KeychainImpl::getAttributeInfoForItemID(CSSM_DB_RECORDTYPE itemID, SecKeychainAttributeInfo **Info)
{
- keychainSchema()->getAttributeInfoForRecordType(itemID, Info);
+ try {
+ keychainSchema()->getAttributeInfoForRecordType(itemID, Info);
+ } catch (const CssmCommonError &error) {
+ switch (error.cssmError()) {
+ case errSecNoSuchClass:
+ case CSSMERR_DL_INVALID_RECORDTYPE:
+ resetSchema();
+ keychainSchema()->getAttributeInfoForRecordType(itemID, Info);
+ default:
+ throw;
+ }
+ }
}
void
}
CssmDbAttributeInfo
-KeychainImpl::attributeInfoForTag(UInt32 tag)
-{
- return keychainSchema()->attributeInfoForTag(tag);
+KeychainImpl::attributeInfoFor(CSSM_DB_RECORDTYPE recordType, UInt32 tag)
+{
+ try {
+ return keychainSchema()->attributeInfoFor(recordType, tag);
+ } catch (const CssmCommonError &error) {
+ switch (error.cssmError()) {
+ case errSecNoSuchClass:
+ case CSSMERR_DL_INVALID_RECORDTYPE:
+ resetSchema();
+ return keychainSchema()->attributeInfoFor(recordType, tag);
+ default:
+ throw;
+ }
+ }
}
Keychain::optional(SecKeychainRef handle)
{
if (handle)
- return KeychainRef::required(handle);
+ return KeychainImpl::required(handle);
else
- return globals().defaultKeychain;
+ return globals().storageManager.defaultKeychain();
}