]> git.saurik.com Git - apple/security.git/blobdiff - Keychain/StorageManager.cpp
Security-54.1.tar.gz
[apple/security.git] / Keychain / StorageManager.cpp
index 78194a43d07d0fb319d84ee5b430d6246ce7c2f7..ed3a7875949ef323e684f611b4681c4e0a730c89 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2000-2001 Apple Computer, Inc. All Rights Reserved.
+ * Copyright (c) 2000-2002 Apple Computer, Inc. All Rights Reserved.
  * 
  * The contents of this file constitute Original Code as defined in and are
  * subject to the Apple Public Source License Version 1.2 (the 'License').
@@ -21,9 +21,6 @@
 
        Contains:       Working with multiple keychains
 
-       Copyright:      2000 by Apple Computer, Inc., all rights reserved.
-
-       To Do:
 */
 
 #include "StorageManager.h"
@@ -39,6 +36,7 @@
 #include <Security/AuthorizationTags.h>
 #include <Security/AuthSession.h>
 #include <Security/debugging.h>
+#include <Security/SecCFTypes.h>
 
 #include "KCCursor.h"
 #include "Globals.h"
@@ -50,53 +48,73 @@ using namespace KeychainCore;
 StorageManager::StorageManager() :
     mSavedList(),
     mKeychains(),
-    mMultiDLDb(mSavedList.list(), true) // Passinng true enables use of Secure Storage
+    mSearchList()
 {
+       _doReload();
 }
 
 // Create KC if it doesn't exist       
 Keychain
 StorageManager::keychain(const DLDbIdentifier &dLDbIdentifier)
 {
-       //StLock<Mutex> _(mKeychainsLock);
+       StLock<Mutex> _(mLock);
+       return _keychain(dLDbIdentifier);
+}
+
+Keychain
+StorageManager::_keychain(const DLDbIdentifier &dLDbIdentifier)
+{
     KeychainMap::iterator it = mKeychains.find(dLDbIdentifier);
     if (it != mKeychains.end())
                return it->second;
 
        // The keychain is not in our cache.  Create it.
-       Keychain keychain(mMultiDLDb->database(dLDbIdentifier));
+       Module module(dLDbIdentifier.ssuid().guid());
+       DL dl;
+       if (dLDbIdentifier.ssuid().subserviceType() & CSSM_SERVICE_CSP)
+               dl = SSCSPDL(module);
+       else
+               dl = DL(module);
+
+       dl->subserviceId(dLDbIdentifier.ssuid().subserviceId());
+       dl->version(dLDbIdentifier.ssuid().version());
+       Db db(dl, dLDbIdentifier.dbName());
+       Keychain keychain(db);
 
        // Add the keychain to the cache.
        mKeychains.insert(KeychainMap::value_type(dLDbIdentifier, keychain));
        return keychain;
 }
 
-// Create KC if it doesn't exist       
+// Create KC if it doesn't exist, add it to the search list if it exists and is not already on it.
 Keychain
 StorageManager::makeKeychain(const DLDbIdentifier &dLDbIdentifier)
 {
        Keychain keychain(keychain(dLDbIdentifier));
 
-       const vector<DLDbIdentifier> &list = mMultiDLDb->list();
-       if (find(list.begin(), list.end(), dLDbIdentifier) != list.end())
        {
-               // The dLDbIdentifier for this keychain is already on our search list.
-               return keychain;
+               StLock<Mutex> _(mLock);
+               if (find(mSearchList.begin(), mSearchList.end(), keychain) != mSearchList.end())
+               {
+                       // This keychain is already on our search list.
+                       return keychain;
+               }
+       
+               // If the keychain doesn't exist don't bother adding it to the search list yet.
+               if (!keychain->exists())
+                       return keychain;
+       
+               // The keychain exists and is not in our search list add it to the search
+               // list and the cache.  Then inform mMultiDLDb.
+               mSavedList.revert(true);
+               mSavedList.add(dLDbIdentifier);
+               mSavedList.save();
+       
+               // @@@ Will happen again when kSecKeychainListChangedEvent notification is received.
+               _doReload();
        }
 
-       // If the keychain doesn't exist don't bother adding it to the search list yet.
-       if (!keychain->exists())
-               return keychain;
-
-       // The keychain exists and is not in our search list add it to the search
-       // list and the cache.  Then inform mMultiDLDb.
-       mSavedList.revert(true);
-       mSavedList.add(dLDbIdentifier);
-       mSavedList.save();
-
-       // @@@ Will happen again when kSecKeychainListChangedEvent notification is received.
-       mMultiDLDb->list(mSavedList.list());
-
+       // Make sure we are not holding mLock when we post this event.
        KCEventNotifier::PostKeychainEvent(kSecKeychainListChangedEvent);
 
        return keychain;
@@ -106,70 +124,108 @@ void
 StorageManager::created(const Keychain &keychain) // Be notified a Keychain just got created.
 {
     DLDbIdentifier dLDbIdentifier = keychain->dLDbIdentifier();
-       
-    // If we don't have a default Keychain yet.  Make the newly created keychain the default.
-    DefaultKeychain &defaultKeychain = globals().defaultKeychain;
-    if (!defaultKeychain.isSet())
-        defaultKeychain.dLDbIdentifier(dLDbIdentifier);
 
-       // Add the keychain to the search list and the cache.  Then inform mMultiDLDb.
-       mSavedList.revert(true);
-       mSavedList.add(dLDbIdentifier);
-       mSavedList.save();
+       {
+               StLock<Mutex> _(mLock);
 
-       // @@@ Will happen again when kSecKeychainListChangedEvent notification is received.
-       mMultiDLDb->list(mSavedList.list());
+               // If we don't have a default Keychain yet.  Make the newly created keychain the default.
+               DefaultKeychain &defaultKeychain = globals().defaultKeychain;
+               if (!defaultKeychain.isSet())
+                       defaultKeychain.dLDbIdentifier(dLDbIdentifier);
+       
+               // Add the keychain to the search list and the cache.  Then inform mMultiDLDb.
+               mSavedList.revert(true);
+               mSavedList.add(dLDbIdentifier);
+               mSavedList.save();
+       
+               // @@@ Will happen again when kSecKeychainListChangedEvent notification is received.
+               _doReload();
+       }
 
+       // Make sure we are not holding mLock when we post this event.
        KCEventNotifier::PostKeychainEvent(kSecKeychainListChangedEvent);
 }
 
-
 KCCursor
 StorageManager::createCursor(SecItemClass itemClass, const SecKeychainAttributeList *attrList)
 {
-       return KCCursor(DbCursor(mMultiDLDb), itemClass, attrList);
+       StLock<Mutex> _(mLock);
+       return KCCursor(mSearchList, itemClass, attrList);
 }
 
 KCCursor
 StorageManager::createCursor(const SecKeychainAttributeList *attrList)
 {
-       return KCCursor(DbCursor(mMultiDLDb), attrList);
+       StLock<Mutex> _(mLock);
+       return KCCursor(mSearchList, attrList);
 }
 
 void
 StorageManager::lockAll()
 {
-    for (KeychainMap::iterator ix = mKeychains.begin(); ix != mKeychains.end(); ix++)
+       // Make a snapshot of all known keychains while holding mLock.
+       KeychainList keychainList;
        {
-               Keychain keychain(ix->second);
+               StLock<Mutex> _(mLock);
+               for (KeychainMap::iterator ix = mKeychains.begin(); ix != mKeychains.end(); ix++)
+                       keychainList.push_back(ix->second);
+       }
+
+       // Lock each active keychain after having released mLock since locking keychains
+       // will send notifications.
+       for (KeychainList::iterator ix = keychainList.begin(); ix != keychainList.end(); ++ix)
+       {
+               Keychain keychain = *ix;
                if (keychain->isActive())
                        keychain->lock();
        }
 }
 
+void
+StorageManager::_doReload()
+{
+       KeychainList newList;
+       newList.reserve(mSavedList.size());
+       for (CssmClient::DLDbList::iterator ix = mSavedList.begin(); ix != mSavedList.end(); ++ix)
+       {
+               Keychain keychain(_keychain(*ix));
+               newList.push_back(keychain);
+       }
+       mSearchList.swap(newList);
+}
+
 void
 StorageManager::reload(bool force)
+{
+       StLock<Mutex> _(mLock);
+    _reload(force);
+}
+
+void
+StorageManager::_reload(bool force)
 {
     // Reinitialize list from CFPrefs if changed.  When force is true force a prefs revert now.
     if (mSavedList.revert(force))
-        mMultiDLDb->list(mSavedList.list());
+        _doReload();
 }
 
 size_t
 StorageManager::size()
 {
-    reload();
-    return mMultiDLDb->list().size();
+       StLock<Mutex> _(mLock);
+    _reload();
+    return mSearchList.size();
 }
 
 Keychain
 StorageManager::at(unsigned int ix)
 {
-    reload();
-    if (ix >= mMultiDLDb->list().size())
+       StLock<Mutex> _(mLock);
+    _reload();
+    if (ix >= mSearchList.size())
         MacOSError::throwMe(errSecInvalidKeychain);
 
-    return keychain(mMultiDLDb->list().at(ix));
+    return mSearchList.at(ix);
 }
 
 Keychain
@@ -178,76 +234,124 @@ StorageManager::operator[](unsigned int ix)
     return at(ix);
 }      
 
-void StorageManager::remove(const list<SecKeychainRef>& kcsToRemove)
+void StorageManager::remove(const KeychainList &kcsToRemove, bool deleteDb)
 {
-       //StLock<Mutex> _(mKeychainsLock);
-       mSavedList.revert(true);
-       DLDbIdentifier defaultId = globals().defaultKeychain.dLDbIdentifier();
-       bool unsetDefault=false;
-    for (list<SecKeychainRef>::const_iterator ix = kcsToRemove.begin();ix!=kcsToRemove.end();ix++)
+       bool unsetDefault = false;
        {
-               // Find the keychain object for the given ref
-               Keychain keychainToRemove;
-               try
-               {
-                       keychainToRemove = KeychainRef::required(*ix);
-               }
-               catch (const MacOSError& err)
+               StLock<Mutex> _(mLock);
+               mSavedList.revert(true);
+               DLDbIdentifier defaultId = globals().defaultKeychain.dLDbIdentifier();
+               for (KeychainList::const_iterator ix = kcsToRemove.begin(); ix != kcsToRemove.end(); ++ix)
                {
-                       if (err.osStatus() == errSecInvalidKeychain)
-                               continue;
-                       throw;
+                       // Find the keychain object for the given ref
+                       Keychain keychainToRemove = *ix;
+                       DLDbIdentifier dLDbIdentifier = keychainToRemove->dLDbIdentifier();
+       
+                       // Remove it from the saved list
+                       mSavedList.remove(dLDbIdentifier);
+                       if (dLDbIdentifier == defaultId)
+                               unsetDefault=true;
+
+                       if (deleteDb)
+                       {
+                               keychainToRemove->database()->deleteDb();
+                               // Now remove it from the map
+                               KeychainMap::iterator it = mKeychains.find(dLDbIdentifier);
+                               if (it == mKeychains.end())
+                                       continue;
+                               mKeychains.erase(it);
+                       }
                }
-               
-               // Remove it from the saved list
-               mSavedList.remove(keychainToRemove->dLDbIdentifier());
-               if (keychainToRemove->dLDbIdentifier() == defaultId)
-                       unsetDefault=true;
-               // Now remove it from the map
-               KeychainMap::iterator it = mKeychains.find(keychainToRemove->dLDbIdentifier());
-               if (it==mKeychains.end())
-                       continue;
-               mKeychains.erase(it);
+               mSavedList.save();
+               _doReload();
        }
-       mSavedList.save();
-       mMultiDLDb->list(mSavedList.list());
+
+       // Make sure we are not holding mLock when we post this event.
        KCEventNotifier::PostKeychainEvent(kSecKeychainListChangedEvent);
+
        if (unsetDefault)
+       {
+               // Make sure we are not holding mLock when we call this since it posts an event.
                globals().defaultKeychain.unset();
+       }
+}
+
+void
+StorageManager::getSearchList(KeychainList &keychainList)
+{
+       // Make a copy of the searchList
+       StLock<Mutex> _(mLock);
+       StorageManager::KeychainList searchList(mSearchList);
+
+       // Return the copy of the list.
+       keychainList.swap(searchList);
 }
 
-void StorageManager::replace(const list<SecKeychainRef>& newKCList)
+void
+StorageManager::setSearchList(const KeychainList &keychainList)
 {
-       // replace keychains list with new list
-       CssmClient::DLDbList dldbList;
-       convert(newKCList,dldbList);
+       // Make a copy of the passed in searchList
+       StorageManager::KeychainList keychains(keychainList);
+
+       // Set the current searchlist to be what was passed in, the old list will be freed
+       // upon exit of this stackframe.
+       StLock<Mutex> _(mLock);
+       mSearchList.swap(keychains);
 }
 
-void StorageManager::convert(const list<SecKeychainRef>& SecKeychainRefList,CssmClient::DLDbList& dldbList)
+void
+StorageManager::optionalSearchList(CFTypeRef keychainOrArray, KeychainList &keychainList)
 {
-    // Convert a list of SecKeychainRefs to a DLDbList
-       dldbList.clear();               // If we don't clear list, we should use "add" instead of push_back
-       for (list<SecKeychainRef>::const_iterator ix = SecKeychainRefList.begin();ix!=SecKeychainRefList.end();ix++)
+       if (!keychainOrArray)
+               getSearchList(keychainList);
+       else
        {
-               // Find the keychain object for the given ref
-               Keychain keychain;
-               try
-               {
-                       keychain = KeychainRef::required(*ix);
-               }
-               catch (const MacOSError& err)
-               {
-                       if (err.osStatus() == errSecInvalidKeychain)
-                               continue;
-                       throw;
-               }
-               
-               // Add it to the list
-               dldbList.push_back(keychain->dLDbIdentifier());
+               CFTypeID typeID = CFGetTypeID(keychainOrArray);
+               if (typeID == CFArrayGetTypeID())
+                       convertToKeychainList(CFArrayRef(keychainOrArray), keychainList);
+               else if (typeID == gTypes().keychain.typeId)
+                       keychainList.push_back(gTypes().keychain.required(SecKeychainRef(keychainOrArray)));
+               else
+                       MacOSError::throwMe(paramErr);
+       }
+}
+
+// static methods.
+void
+StorageManager::convertToKeychainList(CFArrayRef keychainArray, KeychainList &keychainList)
+{
+       assert(keychainArray);
+       CFIndex count = CFArrayGetCount(keychainArray);
+       KeychainList keychains(count);
+       CFClass<KeychainImpl, SecKeychainRef, errSecInvalidKeychain> &kcClass = gTypes().keychain;
+       for (CFIndex ix = 0; ix < count; ++ix)
+       {
+               keychains[ix] = kcClass.required(SecKeychainRef(CFArrayGetValueAtIndex(keychainArray, ix)));
+       }
+
+       keychainList.swap(keychains);
+}
+
+CFArrayRef
+StorageManager::convertFromKeychainList(const KeychainList &keychainList)
+{
+       CFRef<CFMutableArrayRef> keychainArray(CFArrayCreateMutable(NULL, keychainList.size(), &kCFTypeArrayCallBacks));
+
+       CFClass<KeychainImpl, SecKeychainRef, errSecInvalidKeychain> &kcClass = gTypes().keychain;
+       for (KeychainList::const_iterator ix = keychainList.begin(); ix != keychainList.end(); ++ix)
+       {
+               SecKeychainRef keychainRef = kcClass.handle(**ix);
+               CFArrayAppendValue(keychainArray, keychainRef);
+               CFRelease(keychainRef);
        }
+
+       // Counter the CFRelease that CFRef<> is about to do when keychainArray goes out of scope.
+       CFRetain(keychainArray);
+       return keychainArray;
 }
 
 
+
 #pragma mark ÑÑÑÑ Login Functions ÑÑÑÑ
 
 void StorageManager::login(ConstStringPtr name, ConstStringPtr password)
@@ -262,10 +366,12 @@ void StorageManager::login(UInt32 nameLength, const void *name, UInt32 passwordL
 {
     // @@@ set up the login session on behalf of loginwindow
     // @@@ (this code should migrate into loginwindow)
+#if 0
     debug("KClogin", "setting up login session");
     if (OSStatus ssnErr = SessionCreate(sessionKeepCurrentBootstrap,
             sessionHasGraphicAccess | sessionHasTTY))
-        debug("KClogin", "session setup failed status=%ld", ssnErr);
+       debug("KClogin", "session setup failed status=%ld", ssnErr);
+#endif
 
     if (name == NULL || (passwordLength != 0 && password == NULL))
         MacOSError::throwMe(paramErr);
@@ -287,7 +393,7 @@ void StorageManager::login(UInt32 nameLength, const void *name, UInt32 passwordL
                // Login Keychain does not lock on sleep nor lock after timeout by default.
                keychain->setSettings(INT_MAX, false);
        }
-
+#if 0
        // @@@ Create a authorization credential for the current user.
     debug("KClogin", "creating login authorization");
        const AuthorizationItem envList[] =
@@ -304,6 +410,7 @@ void StorageManager::login(UInt32 nameLength, const void *name, UInt32 passwordL
        if (OSStatus authErr = AuthorizationCreate(NULL, &environment,
             kAuthorizationFlagExtendRights | kAuthorizationFlagPreAuthorize, NULL))
         debug("KClogin", "failed to create login auth, status=%ld", authErr);
+#endif
 }
 
 void StorageManager::logout()
@@ -352,15 +459,16 @@ Keychain StorageManager::make(const char *pathName)
     const CSSM_VERSION *version = NULL;
     uint32 subserviceId = 0;
     CSSM_SERVICE_TYPE subserviceType = CSSM_SERVICE_DL | CSSM_SERVICE_CSP;
-    const CssmSubserviceUid ssuid( gGuidAppleCSPDL, version, 
-                                   subserviceId, subserviceType );
-       DLDbIdentifier dLDbIdentifier( ssuid, fullPathName.c_str(), DbLocation );
-       return makeKeychain( dLDbIdentifier );
+    const CssmSubserviceUid ssuid(gGuidAppleCSPDL, version, 
+                                   subserviceId, subserviceType);
+       DLDbIdentifier dLDbIdentifier(ssuid, fullPathName.c_str(), DbLocation);
+       return makeKeychain(dLDbIdentifier);
 }
 
 KeychainSchema
 StorageManager::keychainSchemaFor(const CssmClient::Db &db)
 {
+       // @@@ Locking
        KeychainSchema schema(db);
        pair<KeychainSchemaSet::iterator, bool> result = mKeychainSchemaSet.insert(db);
        if (result.second)