X-Git-Url: https://git.saurik.com/apple/security.git/blobdiff_plain/e3d460c9de4426da6c630c3ae3f46173a99f82d8..ee5f17c73ddf6cea151be3383378b7972c71f538:/OSX/libsecurity_keychain/lib/StorageManager.cpp diff --git a/OSX/libsecurity_keychain/lib/StorageManager.cpp b/OSX/libsecurity_keychain/lib/StorageManager.cpp index 55176915..efab9b8b 100644 --- a/OSX/libsecurity_keychain/lib/StorageManager.cpp +++ b/OSX/libsecurity_keychain/lib/StorageManager.cpp @@ -40,19 +40,19 @@ #include #include #include -//#include -//#include #include #include -//#include #include #include #include #include #include +#include #include "TrustSettingsSchema.h" #include #include +#include +#include "TokenLogin.h" //%%% add this to AuthorizationTagsPriv.h later #ifndef AGENT_HINT_LOGIN_KC_SUPPRESS_RESET_PANEL @@ -76,7 +76,7 @@ static SecPreferencesDomain defaultPreferenceDomain() { SessionAttributeBits sessionAttrs; if (gServerMode) { - secdebug("servermode", "StorageManager initialized in server mode"); + secnotice("servermode", "StorageManager initialized in server mode"); sessionAttrs = sessionIsRoot; } else { MacOSError::check(SessionGetInfo(callerSecuritySession, NULL, &sessionAttrs)); @@ -87,7 +87,7 @@ static SecPreferencesDomain defaultPreferenceDomain() // that has graphics access. Ignore that to help testing.) if ((sessionAttrs & sessionIsRoot) IFDEBUG( && !(sessionAttrs & sessionHasGraphicAccess))) { - secdebug("storagemgr", "using system preferences"); + secnotice("storagemgr", "using system preferences"); return kSecPreferencesDomainSystem; } @@ -154,28 +154,48 @@ StorageManager::keychain(const DLDbIdentifier &dLDbIdentifier) if (!dLDbIdentifier) return Keychain(); - KeychainMap::iterator it = mKeychains.find(dLDbIdentifier); - if (it != mKeychains.end()) + KeychainMap::iterator it = mKeychainMap.end(); + + // If we have a keychain object for the munged keychain, return that. + // Don't hit the filesystem to check file status if we've already done that work... + DLDbIdentifier munge_dldbi = forceMungeDLDbIDentifier(dLDbIdentifier); + it = mKeychainMap.find(munge_dldbi); + if (it != mKeychainMap.end()) { + return it->second; + } + + // If we have a keychain object for the un/demunged keychain, return that. + // We might be in the middle of an upgrade, where the -db file exists as a bit-perfect copy of the original file. + DLDbIdentifier demunge_dldbi = demungeDLDbIdentifier(dLDbIdentifier); + it = mKeychainMap.find(demunge_dldbi); + if (it != mKeychainMap.end()) { + return it->second; + } + + // Okay, we haven't seen this keychain before. Do the full process... + DLDbIdentifier dldbi = mungeDLDbIdentifier(dLDbIdentifier, false); + it = mKeychainMap.find(dldbi); // Almost certain not to find it here + if (it != mKeychainMap.end()) { return it->second; } if (gServerMode) { - secdebug("servermode", "keychain reference in server mode"); + secnotice("servermode", "keychain reference in server mode"); return Keychain(); } // The keychain is not in our cache. Create it. - Db db(makeDb(dLDbIdentifier)); + Db db(makeDb(dldbi)); Keychain keychain(db); // Add the keychain to the cache. - mKeychains.insert(KeychainMap::value_type(dLDbIdentifier, &*keychain)); - keychain->inCache(true); + registerKeychain(keychain); return keychain; } +// Note: this must be a munged DLDbidentifier. CssmClient::Db StorageManager::makeDb(DLDbIdentifier dLDbIdentifier) { Module module(dLDbIdentifier.ssuid().guid()); @@ -194,38 +214,148 @@ StorageManager::makeDb(DLDbIdentifier dLDbIdentifier) { return db; } +// StorageManager is responsible for silently switching to newer-style keychains. +// If the keychain requested is in ~/Library/Keychains/, and there is a +// newer keychain available (with extension ".keychain-db"), open that one +// instead of the one requested. +// +// Because of backwards compatibility reasons, we can't update the plist +// files on disk to point to the upgraded keychains. We will be asked to +// load "/Users/account/Library/Keychains/login.keychain", hence this +// modification to 'login.keychain-db'. +DLDbIdentifier +StorageManager::mungeDLDbIdentifier(const DLDbIdentifier& dLDbIdentifier, bool isReset) { + if(!dLDbIdentifier.dbName()) { + // If this DLDbIdentifier doesn't have a filename, don't munge it + return dLDbIdentifier; + } + + string path = dLDbIdentifier.dbName(); + + bool shouldCreateProtected = globals().integrityProtection(); + + // If we don't have a DLDbIdentifier, we can't return one + if(dLDbIdentifier.mImpl == NULL) { + return DLDbIdentifier(); + } + + // Ensure we're in ~/Library/Keychains + if(pathInHomeLibraryKeychains(path)) { + string pathdb = makeKeychainDbFilename(path); + + struct stat st; + + int path_stat_err = 0; + bool path_exists = (::stat(path.c_str(), &st) == 0); + if(!path_exists) { + path_stat_err = errno; + } + + int pathdb_stat_err = 0; + bool pathdb_exists = (::stat(pathdb.c_str(), &st) == 0); + if(!pathdb_exists) { + pathdb_stat_err = errno; + } + + // If protections are off, don't change the requested filename. + // If protictions are on and the -db file exists, always use it. + // + // If we're resetting, and we're creating a new-style keychain, use the -db path. + // If we're resetting, and we're creating an old-style keychain, use the original path. + // + // Protection pathdb_exists path_exists resetting Result + // DISABLED X X X original + // ENABLED 1 X X -db + // ENABLED 0 0 X -db + // ENABLED 0 1 0 original + // ENABLED 0 1 1 -db + // + bool switchPaths = shouldCreateProtected && (pathdb_exists || (!pathdb_exists && !path_exists) || isReset); + + if(switchPaths) { + secinfo("integrity", "switching to keychain-db: %s from %s (%d %d %d_%d %d_%d)", pathdb.c_str(), path.c_str(), isReset, shouldCreateProtected, path_exists, path_stat_err, pathdb_exists, pathdb_stat_err); + path = pathdb; + } else { + secinfo("integrity", "not switching: %s from %s (%d %d %d_%d %d_%d)", pathdb.c_str(), path.c_str(), isReset, shouldCreateProtected, path_exists, path_stat_err, pathdb_exists, pathdb_stat_err); + } + } + + DLDbIdentifier id(dLDbIdentifier.ssuid(), path.c_str(), dLDbIdentifier.dbLocation()); + return id; +} + +DLDbIdentifier +StorageManager::forceMungeDLDbIDentifier(const DLDbIdentifier& dLDbIdentifier) { + if(!dLDbIdentifier.dbName() || dLDbIdentifier.mImpl == NULL) { + return dLDbIdentifier; + } + + string path = dLDbIdentifier.dbName(); + string pathdb = makeKeychainDbFilename(path); + + DLDbIdentifier id(dLDbIdentifier.ssuid(), pathdb.c_str(), dLDbIdentifier.dbLocation()); + return id; +} + +DLDbIdentifier +StorageManager::demungeDLDbIdentifier(const DLDbIdentifier& dLDbIdentifier) { + if(dLDbIdentifier.dbName() == NULL) { + return dLDbIdentifier; + } + + string path = dLDbIdentifier.dbName(); + string dbSuffix = "-db"; + bool endsWithKeychainDb = (path.size() > dbSuffix.size() && (0 == path.compare(path.size() - dbSuffix.size(), dbSuffix.size(), dbSuffix))); + + // Ensure we're in ~/Library/Keychains, and that the path ends in "-db" + if(pathInHomeLibraryKeychains(path) && endsWithKeychainDb) { + // remove "-db" from the end. + path.erase(path.end() - 3, path.end()); + } + + DLDbIdentifier id(dLDbIdentifier.ssuid(), path.c_str(), dLDbIdentifier.dbLocation()); + return id; +} + +string +StorageManager::makeKeychainDbFilename(const string& filename) { + string keychainDbSuffix = "-db"; + bool endsWithKeychainDb = (filename.size() > keychainDbSuffix.size() && (0 == filename.compare(filename.size() - keychainDbSuffix.size(), keychainDbSuffix.size(), keychainDbSuffix))); + + if(endsWithKeychainDb) { + return filename; + } else { + return filename + keychainDbSuffix; + } +} + +bool +StorageManager::pathInHomeLibraryKeychains(const string& path) { + return SecurityServer::CommonBlob::pathInHomeLibraryKeychains(path); +} + void StorageManager::reloadKeychain(Keychain keychain) { StLock_(mKeychainMapMutex); DLDbIdentifier dLDbIdentifier = keychain->database()->dlDbIdentifier(); - // Since we're going to reload this database and switch over the keychain's - // mDb, grab its mDb mutex - { - StLock__(keychain->mDbMutex); + keychain->changeDatabase(makeDb(mungeDLDbIdentifier(dLDbIdentifier, false))); - CssmClient::Db db(makeDb(dLDbIdentifier)); - keychain->mDb = db; - } - - // Since this new database is based on the exact same dLDbIdentifier, we - // don't need to update the mKeychains map. + // This keychain might have a different dldbidentifier now, depending on what + // other processes have been doing to the keychain files. Let's re-register it, just + // to be sure. + registerKeychain(keychain); } void StorageManager::removeKeychain(const DLDbIdentifier &dLDbIdentifier, KeychainImpl *keychainImpl) { - // Lock the recursive mutex - - StLock_(mKeychainMapMutex); - - KeychainMap::iterator it = mKeychains.find(dLDbIdentifier); - if (it != mKeychains.end() && (KeychainImpl*) it->second == keychainImpl) - mKeychains.erase(it); + StLock_(mKeychainMapMutex); - keychainImpl->inCache(false); + // Don't trust this dldbidentifier. Just look for the keychain and delete it. + forceRemoveFromCache(keychainImpl); } void @@ -235,20 +365,101 @@ StorageManager::didRemoveKeychain(const DLDbIdentifier &dLDbIdentifier) StLock_(mKeychainMapMutex); - KeychainMap::iterator it = mKeychains.find(dLDbIdentifier); - if (it != mKeychains.end()) + KeychainMap::iterator it = mKeychainMap.find(dLDbIdentifier); + if (it != mKeychainMap.end()) { - mKeychains.erase(it); + it->second->inCache(false); + mKeychainMap.erase(it); } } +// If the client does not keep references to keychains, they are destroyed on +// every API exit, and recreated on every API entrance. +// +// To improve performance, we'll cache keychains for some short period of time. +// We'll do this by CFRetaining the keychain object, and setting a timer to +// CFRelease it when time's up. This way, the client can still recover all its +// memory if it doesn't want the keychains around, but repeated API calls will +// be significantly faster. +// +void +StorageManager::tickleKeychain(KeychainImpl *keychainImpl) { + static dispatch_once_t onceToken = 0; + static dispatch_queue_t release_queue = NULL; + dispatch_once(&onceToken, ^{ + release_queue = dispatch_queue_create("com.apple.security.keychain-cache-queue", DISPATCH_QUEUE_SERIAL); + }); + + __block KeychainImpl* kcImpl = keychainImpl; + + if(!kcImpl) { + return; + } + + // We really only want to cache CSPDL file-based keychains + if(kcImpl->dlDbIdentifier().ssuid().guid() != gGuidAppleCSPDL) { + return; + } + + // Make a one-shot timer to release the keychain + uint32_t seconds = 1; + + const string path = kcImpl->name(); + bool isSystemKeychain = (0 == path.compare("/Library/Keychains/System.keychain")); + if(pathInHomeLibraryKeychains(path) || isSystemKeychain) { + // These keychains are important and likely aren't on removable media. + // Cache them longer. + seconds = 5; + } + + __block CFTypeRef kcHandle = kcImpl->handle(); // calls retain; this keychain object will stay around until our dispatch block fires. + + // You _must not_ call CFRelease while on this queue, due to the locking order mishmash. CFRelease takes a lock, so remember to do it later. + __block bool releaseImmediately = false; + + dispatch_sync(release_queue, ^() { + if(kcImpl->mCacheTimer) { + // Update the cache timer to be seconds from now + dispatch_source_set_timer(kcImpl->mCacheTimer, dispatch_time(DISPATCH_TIME_NOW, seconds * NSEC_PER_SEC), DISPATCH_TIME_FOREVER, NSEC_PER_SEC/2); + secdebug("keychain", "updating cache on %p %s", kcImpl, kcImpl->name()); + + // We've added an extra retain to this keychain right before invoking this block. Remember to release it. + releaseImmediately = true; + } else { + // No cache timer; make one. + kcImpl->mCacheTimer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, release_queue); + dispatch_source_set_timer(kcImpl->mCacheTimer, dispatch_time(DISPATCH_TIME_NOW, seconds * NSEC_PER_SEC), DISPATCH_TIME_FOREVER, NSEC_PER_SEC/2); + secdebug("keychain", "taking cache on %p %s", kcImpl, kcImpl->name()); + + dispatch_source_set_event_handler(kcImpl->mCacheTimer, ^{ + secdebug("keychain", "releasing cache on %p %s", kcImpl, kcImpl->name()); + dispatch_source_cancel(kcImpl->mCacheTimer); + dispatch_release(kcImpl->mCacheTimer); + kcImpl->mCacheTimer = NULL; + + // Since we're on the timer queue, we can't call CFRelease on the kcHandle (since that takes a lock). Dispatch_async it over to some other queue... + // This is better than using dispatch_async on the timer queue initially, since it's less dispatch_asyncs overall, even though it's more confusing. + dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND,0), ^{ + CFRelease(kcHandle); + }); + }); + + dispatch_resume(kcImpl->mCacheTimer); + } + }); + + if(releaseImmediately) { + CFRelease(kcHandle); + } +} + // Create keychain if it doesn't exist, and optionally add it to the search list. Keychain -StorageManager::makeKeychain(const DLDbIdentifier &dLDbIdentifier, bool add) +StorageManager::makeKeychain(const DLDbIdentifier &dLDbIdentifier, bool add, bool isReset) { StLock_(mKeychainMapMutex); - Keychain theKeychain = keychain(dLDbIdentifier); + Keychain theKeychain = keychain(mungeDLDbIdentifier(dLDbIdentifier, isReset)); bool post = false; bool updateList = (add && shouldAddToSearchList(dLDbIdentifier)); @@ -256,12 +467,12 @@ StorageManager::makeKeychain(const DLDbIdentifier &dLDbIdentifier, bool add) { mSavedList.revert(false); DLDbList searchList = mSavedList.searchList(); - if (find(searchList.begin(), searchList.end(), dLDbIdentifier) != searchList.end()) + if (find(searchList.begin(), searchList.end(), demungeDLDbIdentifier(dLDbIdentifier)) != searchList.end()) return theKeychain; // theKeychain is already in the searchList. mCommonList.revert(false); searchList = mCommonList.searchList(); - if (find(searchList.begin(), searchList.end(), dLDbIdentifier) != searchList.end()) + if (find(searchList.begin(), searchList.end(), demungeDLDbIdentifier(dLDbIdentifier)) != searchList.end()) return theKeychain; // theKeychain is already in the commonList don't add it to the searchList. // If theKeychain doesn't exist don't bother adding it to the search list yet. @@ -271,7 +482,7 @@ StorageManager::makeKeychain(const DLDbIdentifier &dLDbIdentifier, bool add) // theKeychain exists and is not in our search list, so add it to the // search list. mSavedList.revert(true); - mSavedList.add(dLDbIdentifier); + mSavedList.add(demungeDLDbIdentifier(dLDbIdentifier)); mSavedList.save(); post = true; } @@ -303,12 +514,12 @@ StorageManager::created(const Keychain &keychain) // keychain the default. if (!mSavedList.defaultDLDbIdentifier()) { - mSavedList.defaultDLDbIdentifier(dLDbIdentifier); + mSavedList.defaultDLDbIdentifier(demungeDLDbIdentifier(dLDbIdentifier)); defaultChanged = true; } // Add the keychain to the search list prefs. - mSavedList.add(dLDbIdentifier); + mSavedList.add(demungeDLDbIdentifier(dLDbIdentifier)); mSavedList.save(); // Make sure we are not holding mLock when we post these events. @@ -390,7 +601,7 @@ StorageManager::defaultKeychain(const Keychain &keychain) { oldDefaultId = mSavedList.defaultDLDbIdentifier(); mSavedList.revert(true); - mSavedList.defaultDLDbIdentifier(newDefaultId); + mSavedList.defaultDLDbIdentifier(demungeDLDbIdentifier(newDefaultId)); mSavedList.save(); } @@ -456,13 +667,21 @@ StorageManager::loginKeychain() MacOSError::throwMe(errSecNoSuchKeychain); } +DLDbIdentifier +StorageManager::loginKeychainDLDbIdentifer() +{ + StLock_(mMutex); + DLDbIdentifier loginDLDbIdentifier(mSavedList.loginDLDbIdentifier()); + return mungeDLDbIdentifier(loginDLDbIdentifier, false); +} + void StorageManager::loginKeychain(Keychain keychain) { StLock_(mMutex); mSavedList.revert(true); - mSavedList.loginDLDbIdentifier(keychain->dlDbIdentifier()); + mSavedList.loginDLDbIdentifier(demungeDLDbIdentifier(keychain->dlDbIdentifier())); mSavedList.save(); } @@ -521,59 +740,34 @@ void StorageManager::rename(Keychain keychain, const char* newName) // Find the keychain object for the given ref DLDbIdentifier dLDbIdentifier = keychain->dlDbIdentifier(); - // Actually rename the database on disk. - keychain->database()->rename(newName); + if(!keychain->database()->isLocked()) { + // Bring our unlock state with us + DLDbIdentifier dldbi(dLDbIdentifier.ssuid(), newName, dLDbIdentifier.dbLocation()); + keychain->database()->transferTo(dldbi); + } else { + keychain->database()->rename(newName); + } - if (dLDbIdentifier == defaultId) + if (demungeDLDbIdentifier(dLDbIdentifier) == defaultId) changedDefault=true; newDLDbIdentifier = keychain->dlDbIdentifier(); // Rename the keychain in the search list. - mSavedList.rename(dLDbIdentifier, newDLDbIdentifier); + mSavedList.rename(demungeDLDbIdentifier(dLDbIdentifier), demungeDLDbIdentifier(newDLDbIdentifier)); // If this was the default keychain change it accordingly if (changedDefault) - mSavedList.defaultDLDbIdentifier(newDLDbIdentifier); + mSavedList.defaultDLDbIdentifier(demungeDLDbIdentifier(newDLDbIdentifier)); mSavedList.save(); - // we aren't worried about a weak reference here, because we have to - // hold a lock on an item in order to do the rename - - // Now update the Keychain cache - if (keychain->inCache()) - { - KeychainMap::iterator it = mKeychains.find(dLDbIdentifier); - if (it != mKeychains.end() && (KeychainImpl*) it->second == keychain.get()) - { - // Remove the keychain from the cache under its old - // dLDbIdentifier - mKeychains.erase(it); - } - } - - // If we renamed this keychain on top of an existing one we should - // drop the old one from the cache. - KeychainMap::iterator it = mKeychains.find(newDLDbIdentifier); - if (it != mKeychains.end()) - { - Keychain oldKeychain(it->second); - oldKeychain->inCache(false); - // @@@ Ideally we should invalidate or fault this keychain object. - } - - if (keychain->inCache()) - { - // If the keychain wasn't in the cache to being with let's not put - // it there now. There was probably a good reason it wasn't in it. - // If the keychain was in the cache, update it to use - // newDLDbIdentifier. - mKeychains.insert(KeychainMap::value_type(newDLDbIdentifier, - keychain)); - } - } + // If the keychain wasn't in the cache, don't touch the cache. + // Otherwise, update the cache to use its current identifier. + if(keychain->inCache()) { + registerKeychain(keychain); + } + } - // Make sure we are not holding mLock when we post these events. KCEventNotifier::PostKeychainEvent(kSecKeychainListChangedEvent); if (changedDefault) @@ -581,7 +775,64 @@ void StorageManager::rename(Keychain keychain, const char* newName) newDLDbIdentifier); } -void StorageManager::renameUnique(Keychain keychain, CFStringRef newName) +void StorageManager::registerKeychain(Keychain& kc) { + registerKeychainImpl(kc.get()); +} + +void StorageManager::registerKeychainImpl(KeychainImpl* kcimpl) { + if(!kcimpl) { + return; + } + + { + StLock _(mKeychainMapMutex); + + // First, iterate through the cache to see if this keychain is there. If so, remove it. + forceRemoveFromCache(kcimpl); + + // If we renamed this keychain on top of an existing one, let's drop the old one from the cache. + KeychainMap::iterator it = mKeychainMap.find(kcimpl->dlDbIdentifier()); + if (it != mKeychainMap.end()) + { + Keychain oldKeychain(it->second); + oldKeychain->inCache(false); + // @@@ Ideally we should invalidate or fault this keychain object. + } + + mKeychainMap.insert(KeychainMap::value_type(kcimpl->dlDbIdentifier(), kcimpl)); + kcimpl->inCache(true); + } // drop mKeychainMapMutex +} + +void StorageManager::forceRemoveFromCache(KeychainImpl* inKeychainImpl) { + try { + // Wrap all this in a try-block and ignore all errors - we're trying to clean up these maps + { + StLock _(mKeychainMapMutex); + for(KeychainMap::iterator it = mKeychainMap.begin(); it != mKeychainMap.end(); ) { + if(it->second == inKeychainImpl) { + // Increment the iterator, but use its pre-increment value for the erase + it->second->inCache(false); + mKeychainMap.erase(it++); + } else { + it++; + } + } + } // drop mKeychainMapMutex + } catch(UnixError ue) { + secnotice("storagemgr", "caught UnixError: %d %s", ue.unixError(), ue.what()); + } catch (CssmError cssme) { + const char* errStr = cssmErrorString(cssme.error); + secnotice("storagemgr", "caught CssmError: %d %s", (int) cssme.error, errStr); + } catch (MacOSError mose) { + secnotice("storagemgr", "MacOSError: %d", (int)mose.osStatus()); + } catch(...) { + secnotice("storagemgr", "Unknown error"); + } +} + +// If you pass NULL as the keychain, you must pass an oldName. +void StorageManager::renameUnique(Keychain keychain, CFStringRef oldName, CFStringRef newName, bool appendDbSuffix) { StLock_(mMutex); @@ -599,17 +850,35 @@ void StorageManager::renameUnique(Keychain keychain, CFStringRef newName) if ( newNameCFStr ) { CFStringAppendFormat(newNameCFStr, NULL, CFSTR("%s%d"), newNameCString, index); - CFStringAppend(newNameCFStr, CFSTR(kKeychainSuffix)); // add .keychain + if(appendDbSuffix) { + CFStringAppend(newNameCFStr, CFSTR(kKeychainDbSuffix)); + } else { + CFStringAppend(newNameCFStr, CFSTR(kKeychainSuffix)); // add .keychain + } char toUseBuff2[MAXPATHLEN]; if ( CFStringGetCString(newNameCFStr, toUseBuff2, MAXPATHLEN, kCFStringEncodingUTF8) ) // make sure it fits in MAXPATHLEN, etc. { struct stat filebuf; if ( lstat(toUseBuff2, &filebuf) ) { - rename(keychain, toUseBuff2); - KeychainList kcList; - kcList.push_back(keychain); - remove(kcList, false); + if(keychain) { + rename(keychain, toUseBuff2); + KeychainList kcList; + kcList.push_back(keychain); + remove(kcList, false); + } else { + // We don't have a Keychain object, so force the rename here if possible + char oldNameCString[MAXPATHLEN]; + if ( CFStringGetCString(oldName, oldNameCString, MAXPATHLEN, kCFStringEncodingUTF8) ) { + int result = ::rename(oldNameCString, toUseBuff2); + secnotice("KClogin", "keychain force rename to %s: %d %d", newNameCString, result, (result == 0) ? 0 : errno); + if(result != 0) { + UnixError::throwMe(errno); + } + } else { + secnotice("KClogin", "path is wrong, quitting"); + } + } doneCreating = true; } else @@ -741,9 +1010,10 @@ void StorageManager::remove(const KeychainList &kcsToRemove, bool deleteDb) DLDbIdentifier dLDbIdentifier = theKeychain->dlDbIdentifier(); // Remove it from the saved list - mSavedList.remove(dLDbIdentifier); - if (dLDbIdentifier == defaultId) + mSavedList.remove(demungeDLDbIdentifier(dLDbIdentifier)); + if (demungeDLDbIdentifier(dLDbIdentifier) == defaultId) { unsetDefault=true; + } if (deleteDb) { @@ -755,7 +1025,7 @@ void StorageManager::remove(const KeychainList &kcsToRemove, bool deleteDb) } if (unsetDefault) - mSavedList.defaultDLDbIdentifier(DLDbIdentifier()); + mSavedList.defaultDLDbIdentifier(DLDbIdentifier()); mSavedList.save(); } @@ -833,30 +1103,32 @@ StorageManager::setSearchList(const KeychainList &keychainList) { StLock_(mMutex); - DLDbList commonList = mCommonList.searchList(); - - // Strip out the common list part from the end of the search list. - KeychainList::const_iterator it_end = keychainList.end(); - DLDbList::const_reverse_iterator end_common = commonList.rend(); - for (DLDbList::const_reverse_iterator it_common = commonList.rbegin(); it_common != end_common; ++it_common) + DLDbList searchList, oldSearchList(mSavedList.searchList()); + for (KeychainList::const_iterator it = keychainList.begin(); it != keychainList.end(); ++it) { - // Eliminate common entries from the end of the passed in keychainList. - if (it_end == keychainList.begin()) - break; + DLDbIdentifier dldbi = demungeDLDbIdentifier((*it)->dlDbIdentifier()); + + // If this keychain is not in the common or dynamic lists, add it to the new search list + DLDbList commonList = mCommonList.searchList(); + bool found = false; + for(DLDbList::const_iterator jt = commonList.begin(); jt != commonList.end(); ++jt) { + if((*jt) == dldbi) { + found = true; + } + } - --it_end; - if (!((*it_end)->dlDbIdentifier() == *it_common)) - { - ++it_end; - break; - } - } + DLDbList dynamicList = mDynamicList.searchList(); + for(DLDbList::const_iterator jt = dynamicList.begin(); jt != dynamicList.end(); ++jt) { + if((*jt) == dldbi) { + found = true; + } + } - /* it_end now points one past the last element in keychainList which is not in commonList. */ - DLDbList searchList, oldSearchList(mSavedList.searchList()); - for (KeychainList::const_iterator it = keychainList.begin(); it != it_end; ++it) - { - searchList.push_back((*it)->dlDbIdentifier()); + if(found) { + continue; + } + + searchList.push_back(dldbi); } { @@ -952,11 +1224,11 @@ StorageManager::domain(SecPreferencesDomain domain) switch (domain) { case kSecPreferencesDomainSystem: - secdebug("storagemgr", "switching to system domain"); break; + secnotice("storagemgr", "switching to system domain"); break; case kSecPreferencesDomainUser: - secdebug("storagemgr", "switching to user domain (uid %d)", getuid()); break; + secnotice("storagemgr", "switching to user domain (uid %d)", getuid()); break; default: - secdebug("storagemgr", "switching to weird prefs domain %d", domain); break; + secnotice("storagemgr", "switching to weird prefs domain %d", domain); break; } #endif @@ -1023,7 +1295,7 @@ void StorageManager::convertList(DLDbList &ids, const KeychainList &kcs) result.reserve(kcs.size()); for (KeychainList::const_iterator ix = kcs.begin(); ix != kcs.end(); ++ix) { - result.push_back((*ix)->dlDbIdentifier()); + result.push_back(demungeDLDbIdentifier((*ix)->dlDbIdentifier())); } ids.swap(result); } @@ -1043,7 +1315,7 @@ void StorageManager::convertList(KeychainList &kcs, const DLDbList &ids) #pragma mark ____ Login Functions ____ -void StorageManager::login(AuthorizationRef authRef, UInt32 nameLength, const char* name) +void StorageManager::login(AuthorizationRef authRef, UInt32 nameLength, const char* name, bool isReset) { StLock_(mMutex); @@ -1062,7 +1334,7 @@ void StorageManager::login(AuthorizationRef authRef, UInt32 nameLength, const ch // creates the login keychain with the specified password try { - login(nameLength, name, (UInt32)currItem->valueLength, currItem->value); + login(nameLength, name, (UInt32)currItem->valueLength, currItem->value, isReset); created = true; } catch(...) @@ -1087,15 +1359,15 @@ void StorageManager::login(ConstStringPtr name, ConstStringPtr password) if ( name == NULL || password == NULL ) MacOSError::throwMe(errSecParam); - login(name[0], name + 1, password[0], password + 1); + login(name[0], name + 1, password[0], password + 1, false); } void StorageManager::login(UInt32 nameLength, const void *name, - UInt32 passwordLength, const void *password) + UInt32 passwordLength, const void *password, bool isReset) { if (passwordLength != 0 && password == NULL) { - secdebug("KCLogin", "StorageManager::login: invalid argument (NULL password)"); + secnotice("KCLogin", "StorageManager::login: invalid argument (NULL password)"); MacOSError::throwMe(errSecParam); } @@ -1105,7 +1377,7 @@ void StorageManager::login(UInt32 nameLength, const void *name, loginDLDbIdentifier = mSavedList.loginDLDbIdentifier(); } - secdebug("KCLogin", "StorageManager::login: loginDLDbIdentifier is %s", (loginDLDbIdentifier) ? loginDLDbIdentifier.dbName() : ""); + secnotice("KCLogin", "StorageManager::login: loginDLDbIdentifier is %s", (loginDLDbIdentifier) ? loginDLDbIdentifier.dbName() : ""); if (!loginDLDbIdentifier) MacOSError::throwMe(errSecNoSuchKeychain); @@ -1118,10 +1390,10 @@ void StorageManager::login(UInt32 nameLength, const void *name, int uid = geteuid(); struct passwd *pw = getpwuid(uid); if (pw == NULL) { - secdebug("KCLogin", "StorageManager::login: invalid argument (NULL uid)"); + secnotice("KCLogin", "StorageManager::login: invalid argument (NULL uid)"); MacOSError::throwMe(errSecParam); } - char *userName = pw->pw_name; + std::string userName = pw->pw_name; // make keychain path strings std::string keychainPath = DLDbListCFPref::ExpandTildesInPath(kLoginKeychainPathPrefix); @@ -1129,12 +1401,14 @@ void StorageManager::login(UInt32 nameLength, const void *name, std::string shortnameDotKeychain = shortnameKeychain + ".keychain"; std::string loginDotKeychain = keychainPath + "login.keychain"; std::string loginRenamed1Keychain = keychainPath + "login_renamed1.keychain"; + std::string loginKeychainDb = keychainPath + "login.keychain-db"; // check for existence of keychain files bool shortnameKeychainExists = false; bool shortnameDotKeychainExists = false; bool loginKeychainExists = false; bool loginRenamed1KeychainExists = false; + bool loginKeychainDbExists = false; { struct stat st; int stat_result; @@ -1146,8 +1420,14 @@ void StorageManager::login(UInt32 nameLength, const void *name, loginKeychainExists = (stat_result == 0); stat_result = ::stat(loginRenamed1Keychain.c_str(), &st); loginRenamed1KeychainExists = (stat_result == 0); + stat_result = ::stat(loginKeychainDb.c_str(), &st); + loginKeychainDbExists = (stat_result == 0); } + // login.keychain-db is considered to be the same as login.keychain. + // Our transparent keychain promotion on open will handle opening the right version of this file. + loginKeychainExists |= loginKeychainDbExists; + bool loginUnlocked = false; // make the keychain identifiers @@ -1210,12 +1490,12 @@ void StorageManager::login(UInt32 nameLength, const void *name, // "shortname.keychain" if it is not. if (loginRenamed1KeychainExists && (!loginKeychainExists || - (mSavedList.searchList().size() == 1 && mSavedList.member(loginDLDbIdentifier)) )) { + (mSavedList.searchList().size() == 1 && mSavedList.member(demungeDLDbIdentifier(loginDLDbIdentifier))) )) { try { Keychain loginRenamed1KC(keychain(loginRenamed1DLDbIdentifier)); - secdebug("KCLogin", "Attempting to unlock %s with %d-character password", - (loginRenamed1KC) ? loginRenamed1KC->name() : "", (unsigned int)passwordLength); + secnotice("KCLogin", "Attempting to unlock renamed KC \"%s\"", + (loginRenamed1KC) ? loginRenamed1KC->name() : ""); loginRenamed1KC->unlock(CssmData(const_cast(password), passwordLength)); // if we get here, we unlocked it if (loginKeychainExists) { @@ -1250,12 +1530,81 @@ void StorageManager::login(UInt32 nameLength, const void *name, } } - // if login.keychain does not exist at this point, create it - if (!loginKeychainExists) { - Keychain theKeychain(keychain(loginDLDbIdentifier)); - secdebug("KCLogin", "Creating login keychain %s", (loginDLDbIdentifier) ? loginDLDbIdentifier.dbName() : ""); - theKeychain->create(passwordLength, password); - secdebug("KCLogin", "Login keychain created successfully"); + // is it token login? + CFRef tokenLoginContext; + CFRef smartCardPassword; + OSStatus tokenContextStatus = TokenLoginGetContext(password, passwordLength, tokenLoginContext.take()); + // if login.keychain does not exist at this point, create it + if (!loginKeychainExists || (isReset && !loginKeychainDbExists)) { + // when we creating new KC and user is logged using token (i.e. smart card), we have to get + // the password for that account first + if (tokenContextStatus == errSecSuccess) { + secnotice("KCLogin", "Going to create login keychain for sc login"); + AuthorizationRef authRef; + OSStatus status = AuthorizationCreate(NULL, NULL, 0, &authRef); + if (status == errSecSuccess) { + AuthorizationItem right = { "com.apple.builtin.sc-kc-new-passphrase", 0, NULL, 0 }; + AuthorizationItemSet rightSet = { 1, &right }; + + uint32_t reason, tries; + reason = 0; + tries = 0; + AuthorizationItem envRights[] = { + { AGENT_HINT_RETRY_REASON, sizeof(reason), &reason, 0 }, + { AGENT_HINT_TRIES, sizeof(tries), &tries, 0 }}; + + AuthorizationItemSet envSet = { sizeof(envRights) / sizeof(*envRights), envRights }; + status = AuthorizationCopyRights(authRef, &rightSet, &envSet, kAuthorizationFlagDefaults|kAuthorizationFlagInteractionAllowed|kAuthorizationFlagExtendRights, NULL); + if (status == errSecSuccess) { + AuthorizationItemSet *returnedInfo; + status = AuthorizationCopyInfo(authRef, NULL, &returnedInfo); + if (status == errSecSuccess) { + if (returnedInfo && (returnedInfo->count > 0)) { + for (uint32_t index = 0; index < returnedInfo->count; index++) { + AuthorizationItem &item = returnedInfo->items[index]; + if (!strcmp(AGENT_PASSWORD, item.name)) { + CFIndex len = item.valueLength; + if (len) { + secnotice("KCLogin", "User entered pwd"); + smartCardPassword = CFStringCreateWithBytes(SecCFAllocatorZeroize(), (UInt8 *)item.value, (CFIndex)len, kCFStringEncodingUTF8, TRUE); + memset(item.value, 0, len); + } + } + } + } + } + if(returnedInfo) { + AuthorizationFreeItemSet(returnedInfo); + } + } + AuthorizationFree(authRef, 0); + } + } + + // but don't add it to the search list yet; we'll do that later + Keychain theKeychain = makeKeychain(loginDLDbIdentifier, false, true); + secnotice("KCLogin", "Creating login keychain %s", (loginDLDbIdentifier) ? loginDLDbIdentifier.dbName() : ""); + if (tokenContextStatus == errSecSuccess) { + if (smartCardPassword.get()) { + CFIndex length = CFStringGetLength(smartCardPassword); + CFIndex maxSize = CFStringGetMaximumSizeForEncoding(length, kCFStringEncodingUTF8) + 1; + char *buffer = (char *)malloc(maxSize); + if (CFStringGetCString(smartCardPassword, buffer, maxSize, kCFStringEncodingUTF8)) { + secnotice("KCLogin", "Keychain is created using password provided by sc user"); + theKeychain->create((UInt32)strlen(buffer), buffer); + memset(buffer, 0, maxSize); + } else { + secnotice("KCLogin", "Conversion failed"); + MacOSError::throwMe(errSecNotAvailable); + } + } else { + secnotice("KCLogin", "User did not provide kc password"); + MacOSError::throwMe(errSecNotAvailable); + } + } else { + theKeychain->create(passwordLength, password); + } + secnotice("KCLogin", "Login keychain created successfully"); loginKeychainExists = true; // Set the prefs for this new login keychain. loginKeychain(theKeychain); @@ -1270,24 +1619,26 @@ void StorageManager::login(UInt32 nameLength, const void *name, //*************************************************************** // if the shortname keychain exists in the search list, either rename or remove the entry - if (mSavedList.member(shortnameDLDbIdentifier)) { - if (shortnameDotKeychainExists && !mSavedList.member(shortnameDotDLDbIdentifier)) { + if (mSavedList.member(demungeDLDbIdentifier(shortnameDLDbIdentifier))) { + if (shortnameDotKeychainExists && !mSavedList.member(demungeDLDbIdentifier(shortnameDotDLDbIdentifier))) { // change shortname to shortname.keychain (login.keychain will be added later if not present) - secdebug("KCLogin", "Renaming %s to %s in keychain search list", + secnotice("KCLogin", "Renaming %s to %s in keychain search list", (shortnameDLDbIdentifier) ? shortnameDLDbIdentifier.dbName() : "", (shortnameDotDLDbIdentifier) ? shortnameDotDLDbIdentifier.dbName() : ""); - mSavedList.rename(shortnameDLDbIdentifier, shortnameDotDLDbIdentifier); - } else if (!mSavedList.member(loginDLDbIdentifier)) { + mSavedList.rename(demungeDLDbIdentifier(shortnameDLDbIdentifier), + demungeDLDbIdentifier(shortnameDotDLDbIdentifier)); + } else if (!mSavedList.member(demungeDLDbIdentifier(loginDLDbIdentifier))) { // change shortname to login.keychain - secdebug("KCLogin", "Renaming %s to %s in keychain search list", + secnotice("KCLogin", "Renaming %s to %s in keychain search list", (shortnameDLDbIdentifier) ? shortnameDLDbIdentifier.dbName() : "", (loginDLDbIdentifier) ? loginDLDbIdentifier.dbName() : ""); - mSavedList.rename(shortnameDLDbIdentifier, loginDLDbIdentifier); + mSavedList.rename(demungeDLDbIdentifier(shortnameDLDbIdentifier), + demungeDLDbIdentifier(loginDLDbIdentifier)); } else { // already have login.keychain in list, and renaming to shortname.keychain isn't an option, // so just remove the entry - secdebug("KCLogin", "Removing %s from keychain search list", (shortnameDLDbIdentifier) ? shortnameDLDbIdentifier.dbName() : ""); - mSavedList.remove(shortnameDLDbIdentifier); + secnotice("KCLogin", "Removing %s from keychain search list", (shortnameDLDbIdentifier) ? shortnameDLDbIdentifier.dbName() : ""); + mSavedList.remove(demungeDLDbIdentifier(shortnameDLDbIdentifier)); } // note: save() will cause the plist to be unlinked if the only remaining entry is for login.keychain @@ -1296,24 +1647,24 @@ void StorageManager::login(UInt32 nameLength, const void *name, } // make sure that login.keychain is in the search list - if (!mSavedList.member(loginDLDbIdentifier)) { - secdebug("KCLogin", "Adding %s to keychain search list", (loginDLDbIdentifier) ? loginDLDbIdentifier.dbName() : ""); - mSavedList.add(loginDLDbIdentifier); + if (!mSavedList.member(demungeDLDbIdentifier(loginDLDbIdentifier))) { + secnotice("KCLogin", "Adding %s to keychain search list", (loginDLDbIdentifier) ? loginDLDbIdentifier.dbName() : ""); + mSavedList.add(demungeDLDbIdentifier(loginDLDbIdentifier)); mSavedList.save(); mSavedList.revert(true); } // if we have a shortname.keychain, always include it in the plist (after login.keychain) - if (shortnameDotKeychainExists && !mSavedList.member(shortnameDotDLDbIdentifier)) { - mSavedList.add(shortnameDotDLDbIdentifier); + if (shortnameDotKeychainExists && !mSavedList.member(demungeDLDbIdentifier(shortnameDotDLDbIdentifier))) { + mSavedList.add(demungeDLDbIdentifier(shortnameDotDLDbIdentifier)); mSavedList.save(); mSavedList.revert(true); } // make sure that the default keychain is in the search list; if not, reset the default to login.keychain if (!mSavedList.member(mSavedList.defaultDLDbIdentifier())) { - secdebug("KCLogin", "Changing default keychain to %s", (loginDLDbIdentifier) ? loginDLDbIdentifier.dbName() : ""); - mSavedList.defaultDLDbIdentifier(loginDLDbIdentifier); + secnotice("KCLogin", "Changing default keychain to %s", (loginDLDbIdentifier) ? loginDLDbIdentifier.dbName() : ""); + mSavedList.defaultDLDbIdentifier(demungeDLDbIdentifier(loginDLDbIdentifier)); mSavedList.save(); mSavedList.revert(true); } @@ -1328,8 +1679,8 @@ void StorageManager::login(UInt32 nameLength, const void *name, try { Keychain theKeychain(keychain(loginDLDbIdentifier)); - secdebug("KCLogin", "Attempting to unlock login keychain \"%s\" with %d-character password", - (theKeychain) ? theKeychain->name() : "", (unsigned int)passwordLength); + secnotice("KCLogin", "Attempting to unlock login keychain \"%s\"", + (theKeychain) ? theKeychain->name() : ""); theKeychain->unlock(CssmData(const_cast(password), passwordLength)); loginUnlocked = true; } @@ -1339,55 +1690,107 @@ void StorageManager::login(UInt32 nameLength, const void *name, } } - if (!loginUnlocked) { - try { - loginResult = errSecSuccess; - Keychain theKeychain(keychain(loginDLDbIdentifier)); + if (!loginUnlocked || tokenContextStatus == errSecSuccess) { + Keychain theKeychain(keychain(loginDLDbIdentifier)); + bool tokenLoginDataUpdated = false; + + for (UInt32 i = 0; i < 2; i++) { + loginResult = errSecSuccess; + + CFRef tokenLoginData; + if (tokenLoginContext) { + OSStatus status = TokenLoginGetLoginData(tokenLoginContext, tokenLoginData.take()); + if (status != errSecSuccess) { + if (tokenLoginDataUpdated) { + loginResult = status; + break; + } + // updating unlock key fails if it is not token login + secnotice("KCLogin", "Error %d, reconstructing unlock data", (int)status); + status = TokenLoginUpdateUnlockData(tokenLoginContext, smartCardPassword); + if (status == errSecSuccess) { + loginResult = TokenLoginGetLoginData(tokenLoginContext, tokenLoginData.take()); + if (loginResult != errSecSuccess) { + break; + } + tokenLoginDataUpdated = true; + } + } + } - // build a fake key - CssmKey key; - key.header().BlobType = CSSM_KEYBLOB_RAW; - key.header().Format = CSSM_KEYBLOB_RAW_FORMAT_OCTET_STRING; - key.header().AlgorithmId = CSSM_ALGID_3DES_3KEY; - key.header().KeyClass = CSSM_KEYCLASS_SESSION_KEY; - key.header().KeyUsage = CSSM_KEYUSE_ENCRYPT | CSSM_KEYUSE_DECRYPT | CSSM_KEYATTR_EXTRACTABLE; - key.header().KeyAttr = 0; - key.KeyData = CssmData(const_cast(password), passwordLength); - - // unwrap it into the CSP (but keep it raw) - UnwrapKey unwrap(theKeychain->csp(), CSSM_ALGID_NONE); - CssmKey masterKey; - CssmData descriptiveData; - unwrap(key, - KeySpec(CSSM_KEYUSE_ANY, CSSM_KEYATTR_EXTRACTABLE), - masterKey, &descriptiveData, NULL); - - CssmClient::Db db = theKeychain->database(); - - // create the keychain, using appropriate credentials - Allocator &alloc = db->allocator(); - AutoCredentials cred(alloc); // will leak, but we're quitting soon :-) - - // use this passphrase - cred += TypedList(alloc, CSSM_SAMPLE_TYPE_KEYCHAIN_LOCK, - new(alloc) ListElement(CSSM_SAMPLE_TYPE_SYMMETRIC_KEY), - new(alloc) ListElement(CssmData::wrap(theKeychain->csp()->handle())), - new(alloc) ListElement(CssmData::wrap(masterKey)), - new(alloc) ListElement(CssmData())); - db->authenticate(CSSM_DB_ACCESS_READ, &cred); - db->unlock(); - loginUnlocked = true; - } catch (const CssmError &e) { - loginResult = e.osStatus(); + try { + // first try to unlock login keychain because if this fails, token keychain unlock fails as well + if (tokenLoginData) { + secnotice("KCLogin", "Going to unlock keybag using scBlob"); + OSStatus status = TokenLoginUnlockKeybag(tokenLoginContext, tokenLoginData); + secnotice("KCLogin", "Keybag unlock result %d", (int)status); + if (status) + CssmError::throwMe(status); // to trigger login data regeneration + } + + // build a fake key + CssmKey key; + key.header().BlobType = CSSM_KEYBLOB_RAW; + key.header().Format = CSSM_KEYBLOB_RAW_FORMAT_OCTET_STRING; + key.header().AlgorithmId = CSSM_ALGID_3DES_3KEY; + key.header().KeyClass = CSSM_KEYCLASS_SESSION_KEY; + key.header().KeyUsage = CSSM_KEYUSE_ENCRYPT | CSSM_KEYUSE_DECRYPT | CSSM_KEYATTR_EXTRACTABLE; + key.header().KeyAttr = 0; + CFRef tokenLoginUnlockKey; + if (tokenLoginData) { + OSStatus status = TokenLoginGetUnlockKey(tokenLoginContext, tokenLoginUnlockKey.take()); + if (status) + CssmError::throwMe(status); // to trigger login data regeneration + key.KeyData = CssmData(tokenLoginUnlockKey.get()); + } else { + key.KeyData = CssmData(const_cast(password), passwordLength); + } + // unwrap it into the CSP (but keep it raw) + UnwrapKey unwrap(theKeychain->csp(), CSSM_ALGID_NONE); + CssmKey masterKey; + CssmData descriptiveData; + unwrap(key, + KeySpec(CSSM_KEYUSE_ANY, CSSM_KEYATTR_EXTRACTABLE), + masterKey, &descriptiveData, NULL); + + CssmClient::Db db = theKeychain->database(); + + // create the keychain, using appropriate credentials + Allocator &alloc = db->allocator(); + AutoCredentials cred(alloc); // will leak, but we're quitting soon :-) + + // use this passphrase + cred += TypedList(alloc, CSSM_SAMPLE_TYPE_KEYCHAIN_LOCK, + new(alloc) ListElement(CSSM_SAMPLE_TYPE_SYMMETRIC_KEY), + new(alloc) ListElement(CssmData::wrap(theKeychain->csp()->handle())), + new(alloc) ListElement(CssmData::wrap(masterKey)), + new(alloc) ListElement(CssmData())); + db->authenticate(CSSM_DB_ACCESS_READ, &cred); + db->unlock(); + loginUnlocked = true; + } catch (const CssmError &e) { + if (tokenLoginData && !tokenLoginDataUpdated) { + // token login unlock key was invalid + loginResult = TokenLoginUpdateUnlockData(tokenLoginContext, smartCardPassword); + if (loginResult == errSecSuccess) { + tokenLoginDataUpdated = true; + continue; + } + } + else { + loginResult = e.osStatus(); + } + } + break; } } // if "shortname.keychain" exists and is in the search list, attempt to auto-unlock it with the same password - if (shortnameDotKeychainExists && mSavedList.member(shortnameDotDLDbIdentifier)) { + if (shortnameDotKeychainExists && mSavedList.member(demungeDLDbIdentifier(shortnameDotDLDbIdentifier))) { try { Keychain shortnameDotKC(keychain(shortnameDotDLDbIdentifier)); - secdebug("KCLogin", "Attempting to unlock %s", + secnotice("KCLogin", "Attempting to unlock short name keychain \"%s\"", (shortnameDotKC) ? shortnameDotKC->name() : ""); shortnameDotKC->unlock(CssmData(const_cast(password), passwordLength)); } @@ -1412,7 +1815,7 @@ void StorageManager::stashLogin() loginDLDbIdentifier = mSavedList.loginDLDbIdentifier(); } - secdebug("KCLogin", "StorageManager::stash: loginDLDbIdentifier is %s", (loginDLDbIdentifier) ? loginDLDbIdentifier.dbName() : ""); + secnotice("KCLogin", "StorageManager::stash: loginDLDbIdentifier is %s", (loginDLDbIdentifier) ? loginDLDbIdentifier.dbName() : ""); if (!loginDLDbIdentifier) MacOSError::throwMe(errSecNoSuchKeychain); @@ -1420,7 +1823,7 @@ void StorageManager::stashLogin() { CssmData empty; Keychain theKeychain(keychain(loginDLDbIdentifier)); - secdebug("KCLogin", "Attempting to use stash for login keychain \"%s\"", + secnotice("KCLogin", "Attempting to use stash for login keychain \"%s\"", (theKeychain) ? theKeychain->name() : ""); theKeychain->stashCheck(); } @@ -1445,14 +1848,14 @@ void StorageManager::stashKeychain() loginDLDbIdentifier = mSavedList.loginDLDbIdentifier(); } - secdebug("KCLogin", "StorageManager::stash: loginDLDbIdentifier is %s", (loginDLDbIdentifier) ? loginDLDbIdentifier.dbName() : ""); + secnotice("KCLogin", "StorageManager::stash: loginDLDbIdentifier is %s", (loginDLDbIdentifier) ? loginDLDbIdentifier.dbName() : ""); if (!loginDLDbIdentifier) MacOSError::throwMe(errSecNoSuchKeychain); try { Keychain theKeychain(keychain(loginDLDbIdentifier)); - secdebug("KCLogin", "Attempting to stash login keychain \"%s\"", + secnotice("KCLogin", "Attempting to stash login keychain \"%s\"", (theKeychain) ? theKeychain->name() : ""); theKeychain->stash(); } @@ -1477,7 +1880,7 @@ void StorageManager::changeLoginPassword(ConstStringPtr oldPassword, ConstString StLock_(mMutex); loginKeychain()->changePassphrase(oldPassword, newPassword); - secdebug("KClogin", "Changed login keychain password successfully"); + secnotice("KClogin", "Changed login keychain password successfully"); } @@ -1486,7 +1889,7 @@ void StorageManager::changeLoginPassword(UInt32 oldPasswordLength, const void *o StLock_(mMutex); loginKeychain()->changePassphrase(oldPasswordLength, oldPassword, newPasswordLength, newPassword); - secdebug("KClogin", "Changed login keychain password successfully"); + secnotice("KClogin", "Changed login keychain password successfully"); } // Clear out the keychain search list and rename the existing login.keychain. @@ -1496,6 +1899,8 @@ void StorageManager::resetKeychain(Boolean resetSearchList) StLock_(mMutex); // Clear the keychain search list. + Keychain keychain = NULL; + DLDbIdentifier dldbi; try { if ( resetSearchList ) @@ -1506,42 +1911,82 @@ void StorageManager::resetKeychain(Boolean resetSearchList) // Get a reference to the existing login keychain... // If we don't have one, we throw (not requiring a rename). // - Keychain keychain = loginKeychain(); + keychain = loginKeychain(); + } catch(const CommonError& e) { + secnotice("KClogin", "Failed to open login keychain due to an error: %s", e.what()); + + // Set up fallback rename. + dldbi = loginKeychainDLDbIdentifer(); + + struct stat exists; + if(::stat(dldbi.dbName(), &exists) != 0) { + // no file exists, everything is fine + secnotice("KClogin", "no file exists; resetKeychain() is done"); + return; + } + } + + try{ // // Rename the existing login.keychain (i.e. put it aside). // CFMutableStringRef newName = NULL; newName = CFStringCreateMutable(NULL, 0); CFStringRef currName = NULL; - currName = CFStringCreateWithCString(NULL, keychain->name(), kCFStringEncodingUTF8); + if(keychain) { + currName = CFStringCreateWithCString(NULL, keychain->name(), kCFStringEncodingUTF8); + } else { + currName = CFStringCreateWithCString(NULL, dldbi.dbName(), kCFStringEncodingUTF8); + } if ( newName && currName ) { CFStringAppend(newName, currName); CFStringRef kcSuffix = CFSTR(kKeychainSuffix); + CFStringRef kcDbSuffix = CFSTR(kKeychainDbSuffix); + bool hasDbSuffix = false; if ( CFStringHasSuffix(newName, kcSuffix) ) // remove the .keychain extension { CFRange suffixRange = CFStringFind(newName, kcSuffix, 0); CFStringFindAndReplace(newName, kcSuffix, CFSTR(""), suffixRange, 0); } + if (CFStringHasSuffix(newName, kcDbSuffix)) { + hasDbSuffix = true; + CFRange suffixRange = CFStringFind(newName, kcDbSuffix, 0); + CFStringFindAndReplace(newName, kcDbSuffix, CFSTR(""), suffixRange, 0); + } + CFStringAppend(newName, CFSTR(kKeychainRenamedSuffix)); // add "_renamed_" try { - renameUnique(keychain, newName); + secnotice("KClogin", "attempting keychain rename to %@", newName); + renameUnique(keychain, currName, newName, hasDbSuffix); } - catch(...) + catch(const CommonError& e) { // we need to release 'newName' & 'currName' + secnotice("KClogin", "Failed to renameUnique due to an error: %s", e.what()); + } + catch(...) + { + secnotice("KClogin", "Failed to renameUnique due to an unknown error"); } } // else, let the login call report a duplicate + else { + secnotice("KClogin", "don't have paths, quitting"); + } if ( newName ) CFRelease(newName); if ( currName ) CFRelease(currName); } + catch(const CommonError& e) { + secnotice("KClogin", "Failed to reset login keychain due to an error: %s", e.what()); + } catch(...) { // We either don't have a login keychain, or there was a // failure to rename the existing one. + secnotice("KClogin", "Failed to reset keychain due to an unknown error"); } } @@ -1554,7 +1999,11 @@ Keychain StorageManager::make(const char *pathName) Keychain StorageManager::make(const char *pathName, bool add) { - return makeKeychain(makeDLDbIdentifier(pathName), add); + return make(pathName, add, false); +} + +Keychain StorageManager::make(const char *pathName, bool add, bool isReset) { + return makeKeychain(makeDLDbIdentifier(pathName), add, isReset); } DLDbIdentifier StorageManager::makeDLDbIdentifier(const char *pathName) { @@ -1606,7 +2055,7 @@ DLDbIdentifier StorageManager::makeDLDbIdentifier(const char *pathName) { return dlDbIdentifier; } -Keychain StorageManager::makeLoginAuthUI(const Item *item) +Keychain StorageManager::makeLoginAuthUI(const Item *item, bool isReset) { StLock_(mMutex); @@ -1772,7 +2221,7 @@ Keychain StorageManager::makeLoginAuthUI(const Item *item) catch (...) // can throw if no existing login.keychain is found { } - login(authRef, (UInt32)userName.length(), userName.c_str()); // Create login.keychain + login(authRef, (UInt32)userName.length(), userName.c_str(), isReset); // Create login.keychain keychain = loginKeychain(); // Get newly-created login keychain defaultKeychain(keychain); // Set it to be the default @@ -1809,7 +2258,7 @@ Keychain StorageManager::defaultKeychainUI(Item &item) } if ( globals().getUserInteractionAllowed() ) { - returnedKeychain = makeLoginAuthUI(&item); // If no Keychains is present, one will be created. + returnedKeychain = makeLoginAuthUI(&item, false); // If no Keychains is present, one will be created. if ( !returnedKeychain ) MacOSError::throwMe(errSecInvalidKeychain); // Something went wrong... } @@ -1838,7 +2287,7 @@ StorageManager::addToDomainList(SecPreferencesDomain domain, // manipulate the user's list { mSavedList.revert(true); - mSavedList.add(id); + mSavedList.add(demungeDLDbIdentifier(id)); mSavedList.save(); } @@ -1868,11 +2317,11 @@ StorageManager::isInDomainList(SecPreferencesDomain domain, bool result; if (domain == mDomain) { - result = mSavedList.member(id); + result = mSavedList.member(demungeDLDbIdentifier(id)); } else { - result = DLDbListCFPref(domain).member(id); + result = DLDbListCFPref(domain).member(demungeDLDbIdentifier(id)); } // do the search @@ -1901,7 +2350,7 @@ StorageManager::removeFromDomainList(SecPreferencesDomain domain, // manipulate the user's list { mSavedList.revert(true); - mSavedList.remove(id); + mSavedList.remove(demungeDLDbIdentifier(id)); mSavedList.save(); }