/*
- * 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').
Contains: Working with multiple keychains
- Copyright: 2000 by Apple Computer, Inc., all rights reserved.
-
- To Do:
*/
#include "StorageManager.h"
#include <Security/AuthorizationTags.h>
#include <Security/AuthSession.h>
#include <Security/debugging.h>
+#include <Security/SecCFTypes.h>
#include "KCCursor.h"
#include "Globals.h"
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;
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
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)
{
// @@@ 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);
// 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[] =
if (OSStatus authErr = AuthorizationCreate(NULL, &environment,
kAuthorizationFlagExtendRights | kAuthorizationFlagPreAuthorize, NULL))
debug("KClogin", "failed to create login auth, status=%ld", authErr);
+#endif
}
void StorageManager::logout()
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)