]> git.saurik.com Git - apple/security.git/blobdiff - OSX/libsecurity_keychain/lib/KCCursor.cpp
Security-58286.260.20.tar.gz
[apple/security.git] / OSX / libsecurity_keychain / lib / KCCursor.cpp
index fc968cfc0d4e8594cfc465e65670f2a969a942cc..a5d25d961b372af17e217bb9f53dd1cb633c4938 100644 (file)
@@ -36,6 +36,8 @@
 #include "StorageManager.h"
 #include <Security/SecKeychainItemPriv.h>
 #include <SecBase.h>
+#include <Security/SecBasePriv.h>
+#include <utilities/array_size.h>
 
 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<Mutex> _(gActivationMutex()); // force serialization of cursor creation
-                Keychain &kc = *mCurrent;
-                Mutex* mutex = kc->getKeychainMutex();
-                StLock<Mutex> _(*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> _(*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<Mutex> _(gActivationMutex()); // force serialization of cursor creation
+                    Keychain &kc = *mCurrent;
+                    Mutex* mutex = kc->getKeychainMutex();
+                    StLock<Mutex> _(*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> _(*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<Mutex> _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;
+}