X-Git-Url: https://git.saurik.com/apple/security.git/blobdiff_plain/5c19dc3ae3bd8e40a9c028b0deddd50ff337692c..7e6b461318c8a779d91381531435a68ee4e8b6ed:/OSX/libsecurity_keychain/lib/KCCursor.cpp?ds=inline diff --git a/OSX/libsecurity_keychain/lib/KCCursor.cpp b/OSX/libsecurity_keychain/lib/KCCursor.cpp index fc968cfc..ad7039cf 100644 --- a/OSX/libsecurity_keychain/lib/KCCursor.cpp +++ b/OSX/libsecurity_keychain/lib/KCCursor.cpp @@ -35,7 +35,9 @@ #include "Globals.h" #include "StorageManager.h" #include -#include +#include +#include +#include using namespace KeychainCore; using namespace CssmClient; @@ -58,6 +60,8 @@ KCCursorImpl::KCCursorImpl(const StorageManager::KeychainList &searchList, SecIt mSearchList(searchList), mCurrent(mSearchList.begin()), mAllFailed(true), + mDeleteInvalidRecords(false), + mIsNewKeychain(true), mMutex(Mutex::recursive) { recordType(Schema::recordTypeFor(itemClass)); @@ -71,8 +75,9 @@ KCCursorImpl::KCCursorImpl(const StorageManager::KeychainList &searchList, SecIt for (const SecKeychainAttribute *attr=attrList->attr; attr != end; ++attr) { const CSSM_DB_ATTRIBUTE_INFO *temp; - - if (attr->tag <' ') // ok, is this a key schema? Handle differently, just because we can... + + // ok, is this a key schema? Handle differently, just because we can... + if (attr->tag <' ' && attr->tag < array_size(gKeyAttributeLookupTable)) { temp = gKeyAttributeLookupTable[attr->tag]; } @@ -113,6 +118,8 @@ KCCursorImpl::KCCursorImpl(const StorageManager::KeychainList &searchList, const mSearchList(searchList), mCurrent(mSearchList.begin()), mAllFailed(true), + mDeleteInvalidRecords(false), + mIsNewKeychain(true), mMutex(Mutex::recursive) { if (!attrList) // No additional selectionPredicates: we are done @@ -181,118 +188,161 @@ KCCursorImpl::next(Item &item) for (;;) { - while (!mDbCursor) - { - if (mCurrent == mSearchList.end()) - { - // If we got always failed when calling mDbCursor->next return the error from - // the last call to mDbCursor->next now - if (mAllFailed && status) - CssmError::throwMe(status); - - // No more keychains to search so we are done. - return false; - } - - try - { - // StLock _(gActivationMutex()); // force serialization of cursor creation - Keychain &kc = *mCurrent; - Mutex* mutex = kc->getKeychainMutex(); - StLock _(*mutex); - (*mCurrent)->database()->activate(); - mDbCursor = DbCursor((*mCurrent)->database(), *this); - } - catch(const CommonError &err) - { - ++mCurrent; - } - } + Item tempItem = NULL; + { + while (!mDbCursor) + { + // Do the newKeychain dance before we check our done status + newKeychain(mCurrent); - Keychain &kc = *mCurrent; - Mutex* mutex = kc->getKeychainMutex(); - StLock _(*mutex); - - bool gotRecord; - try - { - // Clear out existing attributes first! - // (the previous iteration may have left attributes from a different schema) - dbAttributes.clear(); + if (mCurrent == mSearchList.end()) + { + // If we got always failed when calling mDbCursor->next return the error from + // the last call to mDbCursor->next now + if (mAllFailed && status) + CssmError::throwMe(status); - gotRecord = mDbCursor->next(&dbAttributes, NULL, uniqueId); - mAllFailed = false; - } - catch(const CommonError &err) - { - // Catch the last error we get and move on to the next keychain - // This error will be returned when we reach the end of our keychain list - // iff all calls to KCCursorImpl::next failed - status = err.osStatus(); - gotRecord = false; - dbAttributes.invalidate(); - } - catch(...) - { - // Catch all other errors - status = errSecItemNotFound; - gotRecord = false; - } + // No more keychains to search so we are done. + return false; + } - // If we did not get a record from the current keychain or the current - // keychain did not exist skip to the next keychain in the list. - if (!gotRecord) - { - ++mCurrent; - mDbCursor = DbCursor(); - continue; - } + try + { + // StLock _(gActivationMutex()); // force serialization of cursor creation + Keychain &kc = *mCurrent; + Mutex* mutex = kc->getKeychainMutex(); + StLock _(*mutex); + (*mCurrent)->database()->activate(); + mDbCursor = DbCursor((*mCurrent)->database(), *this); + } + catch(const CommonError &err) + { + ++mCurrent; + mIsNewKeychain = true; + } + } - // If doing a search for all records, skip the db blob added by the CSPDL - if (dbAttributes.recordType() == CSSM_DL_DB_RECORD_METADATA && - mDbCursor->recordType() == CSSM_DL_DB_RECORD_ANY) + Keychain &kc = *mCurrent; + + Mutex* mutex = kc->getKeychainMutex(); + StLock _(*mutex); + + bool gotRecord; + try + { + // Clear out existing attributes first! + // (the previous iteration may have left attributes from a different schema) + dbAttributes.clear(); + + gotRecord = mDbCursor->next(&dbAttributes, NULL, uniqueId); + mAllFailed = false; + } + catch(const CommonError &err) + { + // Catch the last error we get and move on to the next keychain + // This error will be returned when we reach the end of our keychain list + // iff all calls to KCCursorImpl::next failed + status = err.osStatus(); + gotRecord = false; + dbAttributes.invalidate(); + } + catch(...) + { + // Catch all other errors + status = errSecItemNotFound; + gotRecord = false; + } + + // If we did not get a record from the current keychain or the current + // keychain did not exist skip to the next keychain in the list. + if (!gotRecord) + { + ++mCurrent; + mDbCursor = DbCursor(); + // we'd like to call newKeychain(mCurrent) here, but to avoid deadlock + // we need to drop the current keychain's mutex first. Use this silly + // hack to void the stack Mutex object. + mIsNewKeychain = true; continue; - - // Filter out group keys at this layer - if (dbAttributes.recordType() == CSSM_DL_DB_RECORD_SYMMETRIC_KEY) - { - bool groupKey = false; - try - { - // fetch the key label attribute, if it exists - dbAttributes.add(KeySchema::Label); - Db db((*mCurrent)->database()); - CSSM_RETURN getattr_result = CSSM_DL_DataGetFromUniqueRecordId(db->handle(), uniqueId, &dbAttributes, NULL); - if (getattr_result == CSSM_OK) - { - CssmDbAttributeData *label = dbAttributes.find(KeySchema::Label); - CssmData attrData; - if (label) - attrData = *label; - if (attrData.length() > 4 && !memcmp(attrData.data(), "ssgp", 4)) - groupKey = true; - } - else + } + + // If doing a search for all records, skip the db blob added by the CSPDL + if (dbAttributes.recordType() == CSSM_DL_DB_RECORD_METADATA && + mDbCursor->recordType() == CSSM_DL_DB_RECORD_ANY) + continue; + + // Filter out group keys at this layer + if (dbAttributes.recordType() == CSSM_DL_DB_RECORD_SYMMETRIC_KEY) + { + bool groupKey = false; + try { - dbAttributes.invalidate(); + // fetch the key label attribute, if it exists + dbAttributes.add(KeySchema::Label); + Db db((*mCurrent)->database()); + CSSM_RETURN getattr_result = CSSM_DL_DataGetFromUniqueRecordId(db->handle(), uniqueId, &dbAttributes, NULL); + if (getattr_result == CSSM_OK) + { + CssmDbAttributeData *label = dbAttributes.find(KeySchema::Label); + CssmData attrData; + if (label) + attrData = *label; + if (attrData.length() > 4 && !memcmp(attrData.data(), "ssgp", 4)) + groupKey = true; + } + else + { + dbAttributes.invalidate(); + } } - } - catch (...) {} + catch (...) {} - if (groupKey) - continue; + if (groupKey) + continue; + } + + // Create the Item + // Go though Keychain since item might already exist. + // This might throw a CSSMERR_DL_RECORD_NOT_FOUND or be otherwise invalid. If we're supposed to delete these items, delete them... + try { + tempItem = (*mCurrent)->item(dbAttributes.recordType(), uniqueId); + } catch(CssmError cssme) { + if (mDeleteInvalidRecords) { + // This is an invalid record for some reason; delete it and restart the loop + const char* errStr = cssmErrorString(cssme.error); + secnotice("integrity", "deleting corrupt record because: %d %s", (int) cssme.error, errStr); + + deleteInvalidRecord(uniqueId); + // if deleteInvalidRecord doesn't throw, we want to restart the loop + continue; + } else { + throw; + } + } } + item = tempItem; + break; } - // Go though Keychain since item might already exist. - Keychain &kc = *mCurrent; - StLock _mutexLocker(*kc->getKeychainMutex()); - item = (*mCurrent)->item(dbAttributes.recordType(), uniqueId); + // If we reach here, we've found an item return true; } +void KCCursorImpl::deleteInvalidRecord(DbUniqueRecord& uniqueId) { + // This might throw a RECORD_NOT_FOUND. Handle that, because we don't care if we're trying to delete the item. + try { + uniqueId->deleteRecord(); + } catch(CssmError delcssme) { + if (delcssme.osStatus() == CSSMERR_DL_RECORD_NOT_FOUND) { + secnotice("integrity", "couldn't delete nonexistent record (this is okay)"); + } else { + throw; + } + } +} + bool KCCursorImpl::mayDelete() @@ -306,3 +356,22 @@ bool KCCursorImpl::mayDelete() return true; } } + +void KCCursorImpl::setDeleteInvalidRecords(bool deleteRecord) { + mDeleteInvalidRecords = deleteRecord; +} + +void KCCursorImpl::newKeychain(StorageManager::KeychainList::iterator kcIter) { + if(!mIsNewKeychain) { + // We've already been called on this keychain, don't bother. + return; + } + + if(kcIter != mSearchList.end()) { + (*kcIter)->performKeychainUpgradeIfNeeded(); + (*kcIter)->tickle(); + } + + // Mark down that this function has been called + mIsNewKeychain = false; +}