X-Git-Url: https://git.saurik.com/apple/security.git/blobdiff_plain/5dd5f9ec28f304ca377c42fd7f711d6cf12b90e1..5c19dc3ae3bd8e40a9c028b0deddd50ff337692c:/Security/libsecurity_keychain/lib/StorageManager.cpp?ds=inline diff --git a/Security/libsecurity_keychain/lib/StorageManager.cpp b/Security/libsecurity_keychain/lib/StorageManager.cpp deleted file mode 100644 index f5d16aed..00000000 --- a/Security/libsecurity_keychain/lib/StorageManager.cpp +++ /dev/null @@ -1,1975 +0,0 @@ -/* - * Copyright (c) 2000-2004,2011-2014 Apple Inc. All Rights Reserved. - * - * @APPLE_LICENSE_HEADER_START@ - * - * This file contains Original Code and/or Modifications of Original Code - * as defined in and that are subject to the Apple Public Source License - * Version 2.0 (the 'License'). You may not use this file except in - * compliance with the License. Please obtain a copy of the License at - * http://www.opensource.apple.com/apsl/ and read it before using this - * file. - * - * The Original Code and all software distributed under the License are - * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER - * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, - * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. - * Please see the License for the specific language governing rights and - * limitations under the License. - * - * @APPLE_LICENSE_HEADER_END@ - */ - - -/* - File: StorageManager.cpp - - Contains: Working with multiple keychains - -*/ - -#include "StorageManager.h" -#include "KCEventNotifier.h" - -#include -#include -#include -#include -#include -#include -#include -#include -//#include -//#include -#include -#include -//#include -#include -#include -#include -#include -#include -#include "TrustSettingsSchema.h" -#include - -//%%% add this to AuthorizationTagsPriv.h later -#ifndef AGENT_HINT_LOGIN_KC_SUPPRESS_RESET_PANEL -#define AGENT_HINT_LOGIN_KC_SUPPRESS_RESET_PANEL "loginKCCreate:suppressResetPanel" -#endif - -#include "KCCursor.h" -#include "Globals.h" - - -using namespace CssmClient; -using namespace KeychainCore; - -#define kLoginKeychainPathPrefix "~/Library/Keychains/" -#define kUserLoginKeychainPath "~/Library/Keychains/login.keychain" -#define kEmptyKeychainSizeInBytes 20460 - -//----------------------------------------------------------------------------------- - -static SecPreferencesDomain defaultPreferenceDomain() -{ - SessionAttributeBits sessionAttrs; - if (gServerMode) { - secdebug("servermode", "StorageManager initialized in server mode"); - sessionAttrs = sessionIsRoot; - } else { - MacOSError::check(SessionGetInfo(callerSecuritySession, NULL, &sessionAttrs)); - } - - // If this is the root session, use system preferences. - // (In SecurityServer debug mode, you'll get a (fake) root session - // that has graphics access. Ignore that to help testing.) - if ((sessionAttrs & sessionIsRoot) - IFDEBUG( && !(sessionAttrs & sessionHasGraphicAccess))) { - secdebug("storagemgr", "using system preferences"); - return kSecPreferencesDomainSystem; - } - - // otherwise, use normal (user) preferences - return kSecPreferencesDomainUser; -} - -static bool isAppSandboxed() -{ - bool result = false; - SecTaskRef task = SecTaskCreateFromSelf(NULL); - if(task != NULL) { - CFTypeRef appSandboxValue = SecTaskCopyValueForEntitlement(task, - CFSTR("com.apple.security.app-sandbox"), NULL); - if(appSandboxValue != NULL) { - result = true; - CFRelease(appSandboxValue); - } - CFRelease(task); - } - return result; -} - -static bool shouldAddToSearchList(const DLDbIdentifier &dLDbIdentifier) -{ - // Creation of a private keychain should not modify the search list: rdar://13529331 - // However, we want to ensure the login and System keychains are in - // the search list if that is not the case when they are created. - // Note that App Sandbox apps may not modify the list in either case. - - bool loginOrSystemKeychain = false; - const char *dbname = dLDbIdentifier.dbName(); - if (dbname) { - if ((!strcmp(dbname, "/Library/Keychains/System.keychain")) || - (strstr(dbname, "/login.keychain")) ) { - loginOrSystemKeychain = true; - } - } - return (loginOrSystemKeychain && !isAppSandboxed()); -} - - -StorageManager::StorageManager() : - mSavedList(defaultPreferenceDomain()), - mCommonList(kSecPreferencesDomainCommon), - mDomain(kSecPreferencesDomainUser), - mMutex(Mutex::recursive) -{ -} - - -Mutex* -StorageManager::getStorageManagerMutex() -{ - return &mKeychainMapMutex; -} - - -Keychain -StorageManager::keychain(const DLDbIdentifier &dLDbIdentifier) -{ - StLock_(mKeychainMapMutex); - - if (!dLDbIdentifier) - return Keychain(); - - KeychainMap::iterator it = mKeychains.find(dLDbIdentifier); - if (it != mKeychains.end()) - { - if (it->second == NULL) // cleared by weak reference? - { - mKeychains.erase(it); - } - else - { - return it->second; - } - } - - if (gServerMode) { - secdebug("servermode", "keychain reference in server mode"); - return Keychain(); - } - - // The keychain is not in our cache. Create it. - 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)); - keychain->inCache(true); - - return 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); - - keychainImpl->inCache(false); -} - -void -StorageManager::didRemoveKeychain(const DLDbIdentifier &dLDbIdentifier) -{ - // Lock the recursive mutex - - StLock_(mKeychainMapMutex); - - KeychainMap::iterator it = mKeychains.find(dLDbIdentifier); - if (it != mKeychains.end()) - { - if (it->second != NULL) // did we get zapped by weak reference destruction - { - KeychainImpl *keychainImpl = it->second; - keychainImpl->inCache(false); - } - - mKeychains.erase(it); - } -} - -// Create keychain if it doesn't exist, and optionally add it to the search list. -Keychain -StorageManager::makeKeychain(const DLDbIdentifier &dLDbIdentifier, bool add) -{ - StLock_(mKeychainMapMutex); - - Keychain theKeychain = keychain(dLDbIdentifier); - bool post = false; - bool updateList = (add && shouldAddToSearchList(dLDbIdentifier)); - - if (updateList) - { - mSavedList.revert(false); - DLDbList searchList = mSavedList.searchList(); - if (find(searchList.begin(), searchList.end(), 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()) - 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. - if (!theKeychain->exists()) - return theKeychain; - - // theKeychain exists and is not in our search list, so add it to the - // search list. - mSavedList.revert(true); - mSavedList.add(dLDbIdentifier); - mSavedList.save(); - post = true; - } - - if (post) - { - // Make sure we are not holding mStorageManagerLock anymore when we - // post this event. - KCEventNotifier::PostKeychainEvent(kSecKeychainListChangedEvent); - } - - return theKeychain; -} - -// Be notified a Keychain just got created. -void -StorageManager::created(const Keychain &keychain) -{ - StLock_(mKeychainMapMutex); - - DLDbIdentifier dLDbIdentifier = keychain->dlDbIdentifier(); - bool defaultChanged = false; - bool updateList = shouldAddToSearchList(dLDbIdentifier); - - if (updateList) - { - mSavedList.revert(true); - // If we don't have a default Keychain yet. Make the newly created - // keychain the default. - if (!mSavedList.defaultDLDbIdentifier()) - { - mSavedList.defaultDLDbIdentifier(dLDbIdentifier); - defaultChanged = true; - } - - // Add the keychain to the search list prefs. - mSavedList.add(dLDbIdentifier); - mSavedList.save(); - - // Make sure we are not holding mLock when we post these events. - KCEventNotifier::PostKeychainEvent(kSecKeychainListChangedEvent); - } - - if (defaultChanged) - { - KCEventNotifier::PostKeychainEvent(kSecDefaultChangedEvent, dLDbIdentifier); - } -} - -KCCursor -StorageManager::createCursor(SecItemClass itemClass, - const SecKeychainAttributeList *attrList) -{ - StLock_(mMutex); - - KeychainList searchList; - getSearchList(searchList); - return KCCursor(searchList, itemClass, attrList); -} - -KCCursor -StorageManager::createCursor(const SecKeychainAttributeList *attrList) -{ - StLock_(mMutex); - - KeychainList searchList; - getSearchList(searchList); - return KCCursor(searchList, attrList); -} - -void -StorageManager::lockAll() -{ - StLock_(mMutex); - - SecurityServer::ClientSession ss(Allocator::standard(), Allocator::standard()); - ss.lockAll (false); -} - -Keychain -StorageManager::defaultKeychain() -{ - StLock_(mMutex); - - Keychain theKeychain; - CFTypeRef ref; - - { - mSavedList.revert(false); - DLDbIdentifier defaultDLDbIdentifier(mSavedList.defaultDLDbIdentifier()); - if (defaultDLDbIdentifier) - { - theKeychain = keychain(defaultDLDbIdentifier); - ref = theKeychain->handle(false); - } - } - - if (theKeychain /* && theKeychain->exists() */) - return theKeychain; - - MacOSError::throwMe(errSecNoDefaultKeychain); -} - -void -StorageManager::defaultKeychain(const Keychain &keychain) -{ - StLock_(mMutex); - - // Only set a keychain as the default if we own it and can read/write it, - // and our uid allows modifying the directory for that preference domain. - if (!keychainOwnerPermissionsValidForDomain(keychain->name(), mDomain)) - MacOSError::throwMe(errSecWrPerm); - - DLDbIdentifier oldDefaultId; - DLDbIdentifier newDefaultId(keychain->dlDbIdentifier()); - { - oldDefaultId = mSavedList.defaultDLDbIdentifier(); - mSavedList.revert(true); - mSavedList.defaultDLDbIdentifier(newDefaultId); - mSavedList.save(); - } - - if (!(oldDefaultId == newDefaultId)) - { - // Make sure we are not holding mLock when we post this event. - KCEventNotifier::PostKeychainEvent(kSecDefaultChangedEvent, newDefaultId); - } -} - -Keychain -StorageManager::defaultKeychain(SecPreferencesDomain domain) -{ - StLock_(mMutex); - - if (domain == kSecPreferencesDomainDynamic) - MacOSError::throwMe(errSecInvalidPrefsDomain); - - if (domain == mDomain) - return defaultKeychain(); - else - { - DLDbIdentifier defaultDLDbIdentifier(DLDbListCFPref(domain).defaultDLDbIdentifier()); - if (defaultDLDbIdentifier) - return keychain(defaultDLDbIdentifier); - - MacOSError::throwMe(errSecNoDefaultKeychain); - } -} - -void -StorageManager::defaultKeychain(SecPreferencesDomain domain, const Keychain &keychain) -{ - StLock_(mMutex); - - if (domain == kSecPreferencesDomainDynamic) - MacOSError::throwMe(errSecInvalidPrefsDomain); - - if (domain == mDomain) - defaultKeychain(keychain); - else - DLDbListCFPref(domain).defaultDLDbIdentifier(keychain->dlDbIdentifier()); -} - -Keychain -StorageManager::loginKeychain() -{ - StLock_(mMutex); - - Keychain theKeychain; - { - mSavedList.revert(false); - DLDbIdentifier loginDLDbIdentifier(mSavedList.loginDLDbIdentifier()); - if (loginDLDbIdentifier) - { - theKeychain = keychain(loginDLDbIdentifier); - } - } - - if (theKeychain && theKeychain->exists()) - return theKeychain; - - MacOSError::throwMe(errSecNoSuchKeychain); -} - -void -StorageManager::loginKeychain(Keychain keychain) -{ - StLock_(mMutex); - - mSavedList.revert(true); - mSavedList.loginDLDbIdentifier(keychain->dlDbIdentifier()); - mSavedList.save(); -} - -size_t -StorageManager::size() -{ - StLock_(mMutex); - - mSavedList.revert(false); - mCommonList.revert(false); - return mSavedList.searchList().size() + mCommonList.searchList().size(); -} - -Keychain -StorageManager::at(unsigned int ix) -{ - StLock_(mMutex); - - mSavedList.revert(false); - DLDbList dLDbList = mSavedList.searchList(); - if (ix < dLDbList.size()) - { - return keychain(dLDbList[ix]); - } - else - { - ix -= dLDbList.size(); - mCommonList.revert(false); - DLDbList commonList = mCommonList.searchList(); - if (ix >= commonList.size()) - MacOSError::throwMe(errSecInvalidKeychain); - - return keychain(commonList[ix]); - } -} - -Keychain -StorageManager::operator[](unsigned int ix) -{ - StLock_(mMutex); - - return at(ix); -} - -void StorageManager::rename(Keychain keychain, const char* newName) -{ - - StLock_(mKeychainMapMutex); - - bool changedDefault = false; - DLDbIdentifier newDLDbIdentifier; - { - mSavedList.revert(true); - DLDbIdentifier defaultId = mSavedList.defaultDLDbIdentifier(); - - // Find the keychain object for the given ref - DLDbIdentifier dLDbIdentifier = keychain->dlDbIdentifier(); - - // Actually rename the database on disk. - keychain->database()->rename(newName); - - if (dLDbIdentifier == defaultId) - changedDefault=true; - - newDLDbIdentifier = keychain->dlDbIdentifier(); - // Rename the keychain in the search list. - mSavedList.rename(dLDbIdentifier, newDLDbIdentifier); - - // If this was the default keychain change it accordingly - if (changedDefault) - mSavedList.defaultDLDbIdentifier(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)); - } - } - - // Make sure we are not holding mLock when we post these events. - KCEventNotifier::PostKeychainEvent(kSecKeychainListChangedEvent); - - if (changedDefault) - KCEventNotifier::PostKeychainEvent(kSecDefaultChangedEvent, - newDLDbIdentifier); -} - -void StorageManager::renameUnique(Keychain keychain, CFStringRef newName) -{ - StLock_(mMutex); - - bool doneCreating = false; - int index = 1; - do - { - char newNameCString[MAXPATHLEN]; - if ( CFStringGetCString(newName, newNameCString, MAXPATHLEN, kCFStringEncodingUTF8) ) // make sure it fits in MAXPATHLEN, etc. - { - // Construct the new name... - // - CFMutableStringRef newNameCFStr = NULL; - newNameCFStr = CFStringCreateMutable(NULL, MAXPATHLEN); - if ( newNameCFStr ) - { - CFStringAppendFormat(newNameCFStr, NULL, CFSTR("%s%d"), newNameCString, index); - 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); - doneCreating = true; - } - else - index++; - } - else - doneCreating = true; // failure to get c string. - CFRelease(newNameCFStr); - } - else - doneCreating = false; // failure to create mutable string. - } - else - doneCreating = false; // failure to get the string (i.e. > MAXPATHLEN?) - } - while (!doneCreating && index != INT_MAX); -} - -#define KEYCHAIN_SYNC_KEY CFSTR("KeychainSyncList") -#define KEYCHAIN_SYNC_DOMAIN CFSTR("com.apple.keychainsync") - -static CFStringRef MakeExpandedPath (const char* path) -{ - std::string name = DLDbListCFPref::ExpandTildesInPath (std::string (path)); - CFStringRef expanded = CFStringCreateWithCString (NULL, name.c_str (), 0); - return expanded; -} - -void StorageManager::removeKeychainFromSyncList (const DLDbIdentifier &id) -{ - StLock_(mMutex); - - // make a CFString of our identifier - const char* idname = id.dbName (); - if (idname == NULL) - { - return; - } - - CFRef idString = MakeExpandedPath (idname); - - // check and see if this keychain is in the keychain syncing list - CFArrayRef value = - (CFArrayRef) CFPreferencesCopyValue (KEYCHAIN_SYNC_KEY, - KEYCHAIN_SYNC_DOMAIN, - kCFPreferencesCurrentUser, - kCFPreferencesAnyHost); - if (value == NULL) - { - return; - } - - // make a mutable copy of the dictionary - CFRef mtValue = CFArrayCreateMutableCopy (NULL, 0, value); - CFRelease (value); - - // walk the array, looking for the value - CFIndex i; - CFIndex limit = CFArrayGetCount (mtValue.get()); - bool found = false; - - for (i = 0; i < limit; ++i) - { - CFDictionaryRef idx = (CFDictionaryRef) CFArrayGetValueAtIndex (mtValue.get(), i); - CFStringRef v = (CFStringRef) CFDictionaryGetValue (idx, CFSTR("DbName")); - if (v == NULL) - { - return; // something is really wrong if this is taken - } - - char* stringBuffer = NULL; - const char* pathString = CFStringGetCStringPtr(v, 0); - if (pathString == 0) - { - CFIndex maxLen = CFStringGetMaximumSizeForEncoding(CFStringGetLength(v), kCFStringEncodingUTF8) + 1; - stringBuffer = (char*) malloc(maxLen); - CFStringGetCString(v, stringBuffer, maxLen, kCFStringEncodingUTF8); - pathString = stringBuffer; - } - - CFStringRef vExpanded = MakeExpandedPath(pathString); - CFComparisonResult result = CFStringCompare (vExpanded, idString.get(), 0); - if (stringBuffer != NULL) - { - free(stringBuffer); - } - - CFRelease (vExpanded); - - if (result == 0) - { - CFArrayRemoveValueAtIndex (mtValue.get(), i); - found = true; - break; - } - } - - if (found) - { -#ifndef NDEBUG - CFShow (mtValue.get()); -#endif - - CFPreferencesSetValue (KEYCHAIN_SYNC_KEY, - mtValue, - KEYCHAIN_SYNC_DOMAIN, - kCFPreferencesCurrentUser, - kCFPreferencesAnyHost); - CFPreferencesSynchronize (KEYCHAIN_SYNC_DOMAIN, kCFPreferencesCurrentUser, kCFPreferencesAnyHost); - } -} - -void StorageManager::remove(const KeychainList &kcsToRemove, bool deleteDb) -{ - StLock_(mMutex); - - bool unsetDefault = false; - bool updateList = (!isAppSandboxed()); - - if (updateList) - { - mSavedList.revert(true); - DLDbIdentifier defaultId = mSavedList.defaultDLDbIdentifier(); - for (KeychainList::const_iterator ix = kcsToRemove.begin(); - ix != kcsToRemove.end(); ++ix) - { - // Find the keychain object for the given ref - Keychain theKeychain = *ix; - DLDbIdentifier dLDbIdentifier = theKeychain->dlDbIdentifier(); - - // Remove it from the saved list - mSavedList.remove(dLDbIdentifier); - if (dLDbIdentifier == defaultId) - unsetDefault=true; - - if (deleteDb) - { - removeKeychainFromSyncList (dLDbIdentifier); - - // Now remove it from the cache - removeKeychain(dLDbIdentifier, theKeychain.get()); - } - } - - if (unsetDefault) - mSavedList.defaultDLDbIdentifier(DLDbIdentifier()); - - mSavedList.save(); - } - - if (deleteDb) - { - // Delete the actual databases without holding any locks. - for (KeychainList::const_iterator ix = kcsToRemove.begin(); - ix != kcsToRemove.end(); ++ix) - { - (*ix)->database()->deleteDb(); - } - } - - if (updateList) { - // Make sure we are not holding mLock when we post these events. - KCEventNotifier::PostKeychainEvent(kSecKeychainListChangedEvent); - } - - if (unsetDefault) - KCEventNotifier::PostKeychainEvent(kSecDefaultChangedEvent); -} - -void -StorageManager::getSearchList(KeychainList &keychainList) -{ - // hold the global lock since we make keychain objects in this function - - // to do: each of the items in this list must be retained, otherwise mayhem will occur - StLock_(mMutex); - - if (gServerMode) { - keychainList.clear(); - return; - } - - mSavedList.revert(false); - mCommonList.revert(false); - - // Merge mSavedList, mDynamicList and mCommonList - DLDbList dLDbList = mSavedList.searchList(); - DLDbList dynamicList = mDynamicList.searchList(); - DLDbList commonList = mCommonList.searchList(); - KeychainList result; - result.reserve(dLDbList.size() + dynamicList.size() + commonList.size()); - - { - for (DLDbList::const_iterator it = dynamicList.begin(); - it != dynamicList.end(); ++it) - { - Keychain k = keychain(*it); - result.push_back(k); - } - - for (DLDbList::const_iterator it = dLDbList.begin(); - it != dLDbList.end(); ++it) - { - Keychain k = keychain(*it); - result.push_back(k); - } - - for (DLDbList::const_iterator it = commonList.begin(); - it != commonList.end(); ++it) - { - Keychain k = keychain(*it); - result.push_back(k); - } - } - - keychainList.swap(result); -} - -void -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) - { - // Eliminate common entries from the end of the passed in keychainList. - if (it_end == keychainList.begin()) - break; - - --it_end; - if (!((*it_end)->dlDbIdentifier() == *it_common)) - { - ++it_end; - break; - } - } - - /* 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()); - } - - { - // Set the current searchlist to be what was passed in, the old list will be freed - // upon exit of this stackframe. - mSavedList.revert(true); - mSavedList.searchList(searchList); - mSavedList.save(); - } - - if (!(oldSearchList == searchList)) - { - // Make sure we are not holding mLock when we post this event. - KCEventNotifier::PostKeychainEvent(kSecKeychainListChangedEvent); - } -} - -void -StorageManager::getSearchList(SecPreferencesDomain domain, KeychainList &keychainList) -{ - StLock_(mMutex); - - if (gServerMode) { - keychainList.clear(); - return; - } - - if (domain == kSecPreferencesDomainDynamic) - { - convertList(keychainList, mDynamicList.searchList()); - } - else if (domain == mDomain) - { - mSavedList.revert(false); - convertList(keychainList, mSavedList.searchList()); - } - else - { - convertList(keychainList, DLDbListCFPref(domain).searchList()); - } -} - -void StorageManager::forceUserSearchListReread() -{ - mSavedList.forceUserSearchListReread(); -} - -void -StorageManager::setSearchList(SecPreferencesDomain domain, const KeychainList &keychainList) -{ - StLock_(mMutex); - - if (domain == kSecPreferencesDomainDynamic) - MacOSError::throwMe(errSecInvalidPrefsDomain); - - DLDbList searchList; - convertList(searchList, keychainList); - - if (domain == mDomain) - { - DLDbList oldSearchList(mSavedList.searchList()); - { - // Set the current searchlist to be what was passed in, the old list will be freed - // upon exit of this stackframe. - mSavedList.revert(true); - mSavedList.searchList(searchList); - mSavedList.save(); - } - - if (!(oldSearchList == searchList)) - { - KCEventNotifier::PostKeychainEvent(kSecKeychainListChangedEvent); - } - } - else - { - DLDbListCFPref(domain).searchList(searchList); - } -} - -void -StorageManager::domain(SecPreferencesDomain domain) -{ - StLock_(mMutex); - - if (domain == kSecPreferencesDomainDynamic) - MacOSError::throwMe(errSecInvalidPrefsDomain); - - if (domain == mDomain) - return; // no change - -#if !defined(NDEBUG) - switch (domain) - { - case kSecPreferencesDomainSystem: - secdebug("storagemgr", "switching to system domain"); break; - case kSecPreferencesDomainUser: - secdebug("storagemgr", "switching to user domain (uid %d)", getuid()); break; - default: - secdebug("storagemgr", "switching to weird prefs domain %d", domain); break; - } -#endif - - mDomain = domain; - mSavedList.set(domain); -} - -void -StorageManager::optionalSearchList(CFTypeRef keychainOrArray, KeychainList &keychainList) -{ - StLock_(mMutex); - - if (!keychainOrArray) - getSearchList(keychainList); - else - { - CFTypeID typeID = CFGetTypeID(keychainOrArray); - if (typeID == CFArrayGetTypeID()) - convertToKeychainList(CFArrayRef(keychainOrArray), keychainList); - else if (typeID == gTypes().KeychainImpl.typeID) - keychainList.push_back(KeychainImpl::required(SecKeychainRef(keychainOrArray))); - else - MacOSError::throwMe(errSecParam); - } -} - -// static methods. -void -StorageManager::convertToKeychainList(CFArrayRef keychainArray, KeychainList &keychainList) -{ - CFIndex count = CFArrayGetCount(keychainArray); - if (!(count > 0)) - return; - - KeychainList keychains(count); - for (CFIndex ix = 0; ix < count; ++ix) - { - keychains[ix] = KeychainImpl::required(SecKeychainRef(CFArrayGetValueAtIndex(keychainArray, ix))); - } - - keychainList.swap(keychains); -} - -CFArrayRef -StorageManager::convertFromKeychainList(const KeychainList &keychainList) -{ - CFRef keychainArray(CFArrayCreateMutable(NULL, keychainList.size(), &kCFTypeArrayCallBacks)); - - for (KeychainList::const_iterator ix = keychainList.begin(); ix != keychainList.end(); ++ix) - { - SecKeychainRef keychainRef = (*ix)->handle(); - CFArrayAppendValue(keychainArray, keychainRef); - CFRelease(keychainRef); - } - - // Counter the CFRelease that CFRef<> is about to do when keychainArray goes out of scope. - CFRetain(keychainArray); - return keychainArray; -} - -void StorageManager::convertList(DLDbList &ids, const KeychainList &kcs) -{ - DLDbList result; - result.reserve(kcs.size()); - for (KeychainList::const_iterator ix = kcs.begin(); ix != kcs.end(); ++ix) - { - result.push_back((*ix)->dlDbIdentifier()); - } - ids.swap(result); -} - -void StorageManager::convertList(KeychainList &kcs, const DLDbList &ids) -{ - StLock_(mMutex); - - KeychainList result; - result.reserve(ids.size()); - { - for (DLDbList::const_iterator ix = ids.begin(); ix != ids.end(); ++ix) - result.push_back(keychain(*ix)); - } - kcs.swap(result); -} - -#pragma mark ____ Login Functions ____ - -void StorageManager::login(AuthorizationRef authRef, UInt32 nameLength, const char* name) -{ - StLock_(mMutex); - - AuthorizationItemSet* info = NULL; - OSStatus result = AuthorizationCopyInfo(authRef, NULL, &info); // get the results of the copy rights call. - Boolean created = false; - if ( result == errSecSuccess && info->count ) - { - // Grab the password from the auth context (info) and create the keychain... - // - AuthorizationItem* currItem = info->items; - for (UInt32 index = 1; index <= info->count; index++) //@@@plugin bug won't return a specific context. - { - if (strcmp(currItem->name, kAuthorizationEnvironmentPassword) == 0) - { - // creates the login keychain with the specified password - try - { - login(nameLength, name, (UInt32)currItem->valueLength, currItem->value); - created = true; - } - catch(...) - { - } - break; - } - currItem++; - } - } - if ( info ) - AuthorizationFreeItemSet(info); - - if ( !created ) - MacOSError::throwMe(errAuthorizationInternal); -} - -void StorageManager::login(ConstStringPtr name, ConstStringPtr password) -{ - StLock_(mMutex); - - if ( name == NULL || password == NULL ) - MacOSError::throwMe(errSecParam); - - login(name[0], name + 1, password[0], password + 1); -} - -void StorageManager::login(UInt32 nameLength, const void *name, - UInt32 passwordLength, const void *password) -{ - if (passwordLength != 0 && password == NULL) - { - secdebug("KCLogin", "StorageManager::login: invalid argument (NULL password)"); - MacOSError::throwMe(errSecParam); - } - - DLDbIdentifier loginDLDbIdentifier; - { - mSavedList.revert(true); - loginDLDbIdentifier = mSavedList.loginDLDbIdentifier(); - } - - secdebug("KCLogin", "StorageManager::login: loginDLDbIdentifier is %s", (loginDLDbIdentifier) ? loginDLDbIdentifier.dbName() : ""); - if (!loginDLDbIdentifier) - MacOSError::throwMe(errSecNoSuchKeychain); - - - //*************************************************************** - // gather keychain information - //*************************************************************** - - // user name - int uid = geteuid(); - struct passwd *pw = getpwuid(uid); - if (pw == NULL) { - secdebug("KCLogin", "StorageManager::login: invalid argument (NULL uid)"); - MacOSError::throwMe(errSecParam); - } - char *userName = pw->pw_name; - - // make keychain path strings - std::string keychainPath = DLDbListCFPref::ExpandTildesInPath(kLoginKeychainPathPrefix); - std::string shortnameKeychain = keychainPath + userName; - std::string shortnameDotKeychain = shortnameKeychain + ".keychain"; - std::string loginDotKeychain = keychainPath + "login.keychain"; - std::string loginRenamed1Keychain = keychainPath + "login_renamed1.keychain"; - - // check for existence of keychain files - bool shortnameKeychainExists = false; - bool shortnameDotKeychainExists = false; - bool loginKeychainExists = false; - bool loginRenamed1KeychainExists = false; - { - struct stat st; - int stat_result; - stat_result = ::stat(shortnameKeychain.c_str(), &st); - shortnameKeychainExists = (stat_result == 0); - stat_result = ::stat(shortnameDotKeychain.c_str(), &st); - shortnameDotKeychainExists = (stat_result == 0); - stat_result = ::stat(loginDotKeychain.c_str(), &st); - loginKeychainExists = (stat_result == 0); - stat_result = ::stat(loginRenamed1Keychain.c_str(), &st); - loginRenamed1KeychainExists = (stat_result == 0); - } - - bool loginUnlocked = false; - - // make the keychain identifiers - CSSM_VERSION version = {0, 0}; - DLDbIdentifier shortnameDLDbIdentifier = DLDbListCFPref::makeDLDbIdentifier(gGuidAppleCSPDL, version, 0, CSSM_SERVICE_CSP | CSSM_SERVICE_DL, shortnameKeychain.c_str(), NULL); - DLDbIdentifier shortnameDotDLDbIdentifier = DLDbListCFPref::makeDLDbIdentifier(gGuidAppleCSPDL, version, 0, CSSM_SERVICE_CSP | CSSM_SERVICE_DL, shortnameDotKeychain.c_str(), NULL); - DLDbIdentifier loginRenamed1DLDbIdentifier = DLDbListCFPref::makeDLDbIdentifier(gGuidAppleCSPDL, version, 0, CSSM_SERVICE_CSP | CSSM_SERVICE_DL, loginRenamed1Keychain.c_str(), NULL); - - //*************************************************************** - // make file renaming changes first - //*************************************************************** - - // if "~/Library/Keychains/shortname" exists, we need to migrate it forward; - // either to login.keychain if there isn't already one, otherwise to shortname.keychain - if (shortnameKeychainExists) { - int rename_stat = 0; - if (loginKeychainExists) { - struct stat st; - int tmp_result = ::stat(loginDotKeychain.c_str(), &st); - if (tmp_result == 0) { - if (st.st_size <= kEmptyKeychainSizeInBytes) { - tmp_result = ::unlink(loginDotKeychain.c_str()); - rename_stat = ::rename(shortnameKeychain.c_str(), loginDotKeychain.c_str()); - shortnameKeychainExists = (rename_stat != 0); - } - } - } - if (shortnameKeychainExists) { - if (loginKeychainExists && !shortnameDotKeychainExists) { - rename_stat = ::rename(shortnameKeychain.c_str(), shortnameDotKeychain.c_str()); - shortnameDotKeychainExists = (rename_stat == 0); - } else if (!loginKeychainExists) { - rename_stat = ::rename(shortnameKeychain.c_str(), loginDotKeychain.c_str()); - loginKeychainExists = (rename_stat == 0); - } else { - // we have all 3 keychains: login.keychain, shortname, and shortname.keychain. - // on Leopard we never want a shortname keychain, so we must move it aside. - char pathbuf[MAXPATHLEN]; - std::string shortnameRenamedXXXKeychain = keychainPath; - shortnameRenamedXXXKeychain += userName; - shortnameRenamedXXXKeychain += "_renamed_XXX.keychain"; - ::strlcpy(pathbuf, shortnameRenamedXXXKeychain.c_str(), sizeof(pathbuf)); - ::mkstemps(pathbuf, 9); // 9 == strlen(".keychain") - rename_stat = ::rename(shortnameKeychain.c_str(), pathbuf); - shortnameKeychainExists = (rename_stat != 0); - } - } - if (rename_stat != 0) { - MacOSError::throwMe(errno); - } - } - - //*************************************************************** - // handle special case where user previously reset the keychain - //*************************************************************** - // Since 9A581, we have changed the definition of kKeychainRenamedSuffix from "_renamed" to "_renamed_". - // Therefore, if "login_renamed1.keychain" exists and there is no plist, the user may have run into a - // prior upgrade issue and clicked Reset. If we can successfully unlock login_renamed1.keychain with the - // supplied password, then we will attempt to rename it to login.keychain if that file is empty, or with - // "shortname.keychain" if it is not. - - if (loginRenamed1KeychainExists && (!loginKeychainExists || - (mSavedList.searchList().size() == 1 && mSavedList.member(loginDLDbIdentifier)) )) { - try - { - Keychain loginRenamed1KC(keychain(loginRenamed1DLDbIdentifier)); - secdebug("KCLogin", "Attempting to unlock %s with %d-character password", - (loginRenamed1KC) ? loginRenamed1KC->name() : "", (unsigned int)passwordLength); - loginRenamed1KC->unlock(CssmData(const_cast(password), passwordLength)); - // if we get here, we unlocked it - if (loginKeychainExists) { - struct stat st; - int tmp_result = ::stat(loginDotKeychain.c_str(), &st); - if (tmp_result == 0) { - if (st.st_size <= kEmptyKeychainSizeInBytes) { - tmp_result = ::unlink(loginDotKeychain.c_str()); - tmp_result = ::rename(loginRenamed1Keychain.c_str(), loginDotKeychain.c_str()); - } else if (!shortnameDotKeychainExists) { - tmp_result = ::rename(loginRenamed1Keychain.c_str(), shortnameDotKeychain.c_str()); - shortnameDotKeychainExists = (tmp_result == 0); - } else { - throw 1; // can't do anything with it except move it out of the way - } - } - } else { - int tmp_result = ::rename(loginRenamed1Keychain.c_str(), loginDotKeychain.c_str()); - loginKeychainExists = (tmp_result == 0); - } - } - catch(...) - { - // we failed to unlock the login_renamed1.keychain file with the login password. - // move it aside so we don't try to deal with it again. - char pathbuf[MAXPATHLEN]; - std::string loginRenamedXXXKeychain = keychainPath; - loginRenamedXXXKeychain += "login_renamed_XXX.keychain"; - ::strlcpy(pathbuf, loginRenamedXXXKeychain.c_str(), sizeof(pathbuf)); - ::mkstemps(pathbuf, 9); // 9 == strlen(".keychain") - ::rename(loginRenamed1Keychain.c_str(), pathbuf); - } - } - - // 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"); - loginKeychainExists = true; - // Set the prefs for this new login keychain. - loginKeychain(theKeychain); - // Login Keychain does not lock on sleep nor lock after timeout by default. - theKeychain->setSettings(INT_MAX, false); - loginUnlocked = true; - mSavedList.revert(true); - } - - //*************************************************************** - // make plist changes after files have been renamed or created - //*************************************************************** - - // if the shortname keychain exists in the search list, either rename or remove the entry - if (mSavedList.member(shortnameDLDbIdentifier)) { - if (shortnameDotKeychainExists && !mSavedList.member(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", - (shortnameDLDbIdentifier) ? shortnameDLDbIdentifier.dbName() : "", - (shortnameDotDLDbIdentifier) ? shortnameDotDLDbIdentifier.dbName() : ""); - mSavedList.rename(shortnameDLDbIdentifier, shortnameDotDLDbIdentifier); - } else if (!mSavedList.member(loginDLDbIdentifier)) { - // change shortname to login.keychain - secdebug("KCLogin", "Renaming %s to %s in keychain search list", - (shortnameDLDbIdentifier) ? shortnameDLDbIdentifier.dbName() : "", - (loginDLDbIdentifier) ? loginDLDbIdentifier.dbName() : ""); - mSavedList.rename(shortnameDLDbIdentifier, 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); - } - - // note: save() will cause the plist to be unlinked if the only remaining entry is for login.keychain - mSavedList.save(); - mSavedList.revert(true); - } - - // 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); - 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); - 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); - mSavedList.save(); - mSavedList.revert(true); - } - - //*************************************************************** - // auto-unlock the login keychain(s) - //*************************************************************** - // all our preflight fixups are finally done, so we can now attempt to unlock the login keychain - - OSStatus loginResult = errSecSuccess; - if (!loginUnlocked) { - try - { - Keychain theKeychain(keychain(loginDLDbIdentifier)); - secdebug("KCLogin", "Attempting to unlock login keychain \"%s\" with %d-character password", - (theKeychain) ? theKeychain->name() : "", (unsigned int)passwordLength); - theKeychain->unlock(CssmData(const_cast(password), passwordLength)); - loginUnlocked = true; - } - catch(const CssmError &e) - { - loginResult = e.osStatus(); // save this result - } - } - - if (!loginUnlocked) { - try { - loginResult = errSecSuccess; - Keychain theKeychain(keychain(loginDLDbIdentifier)); - - // 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(); - } - } - - // if "shortname.keychain" exists and is in the search list, attempt to auto-unlock it with the same password - if (shortnameDotKeychainExists && mSavedList.member(shortnameDotDLDbIdentifier)) { - try - { - Keychain shortnameDotKC(keychain(shortnameDotDLDbIdentifier)); - secdebug("KCLogin", "Attempting to unlock %s", - (shortnameDotKC) ? shortnameDotKC->name() : ""); - shortnameDotKC->unlock(CssmData(const_cast(password), passwordLength)); - } - catch(const CssmError &e) - { - // ignore; failure to unlock this keychain is not considered an error - } - } - - if (loginResult != errSecSuccess) { - MacOSError::throwMe(loginResult); - } -} - -void StorageManager::stashLogin() -{ - OSStatus loginResult = errSecSuccess; - - DLDbIdentifier loginDLDbIdentifier; - { - mSavedList.revert(true); - loginDLDbIdentifier = mSavedList.loginDLDbIdentifier(); - } - - secdebug("KCLogin", "StorageManager::stash: loginDLDbIdentifier is %s", (loginDLDbIdentifier) ? loginDLDbIdentifier.dbName() : ""); - if (!loginDLDbIdentifier) - MacOSError::throwMe(errSecNoSuchKeychain); - - try - { - CssmData empty; - Keychain theKeychain(keychain(loginDLDbIdentifier)); - secdebug("KCLogin", "Attempting to use stash for login keychain \"%s\"", - (theKeychain) ? theKeychain->name() : ""); - theKeychain->stashCheck(); - } - catch(const CssmError &e) - { - loginResult = e.osStatus(); // save this result - } - - - if (loginResult != errSecSuccess) { - MacOSError::throwMe(loginResult); - } -} - -void StorageManager::stashKeychain() -{ - OSStatus loginResult = errSecSuccess; - - DLDbIdentifier loginDLDbIdentifier; - { - mSavedList.revert(true); - loginDLDbIdentifier = mSavedList.loginDLDbIdentifier(); - } - - secdebug("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\"", - (theKeychain) ? theKeychain->name() : ""); - theKeychain->stash(); - } - catch(const CssmError &e) - { - loginResult = e.osStatus(); // save this result - } - - - if (loginResult != errSecSuccess) { - MacOSError::throwMe(loginResult); - } -} - -void StorageManager::logout() -{ - // nothing left to do here -} - -void StorageManager::changeLoginPassword(ConstStringPtr oldPassword, ConstStringPtr newPassword) -{ - StLock_(mMutex); - - loginKeychain()->changePassphrase(oldPassword, newPassword); - secdebug("KClogin", "Changed login keychain password successfully"); -} - - -void StorageManager::changeLoginPassword(UInt32 oldPasswordLength, const void *oldPassword, UInt32 newPasswordLength, const void *newPassword) -{ - StLock_(mMutex); - - loginKeychain()->changePassphrase(oldPasswordLength, oldPassword, newPasswordLength, newPassword); - secdebug("KClogin", "Changed login keychain password successfully"); -} - -// Clear out the keychain search list and rename the existing login.keychain. -// -void StorageManager::resetKeychain(Boolean resetSearchList) -{ - StLock_(mMutex); - - // Clear the keychain search list. - try - { - if ( resetSearchList ) - { - StorageManager::KeychainList keychainList; - setSearchList(keychainList); - } - // Get a reference to the existing login keychain... - // If we don't have one, we throw (not requiring a rename). - // - Keychain keychain = loginKeychain(); - // - // 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 ( newName && currName ) - { - CFStringAppend(newName, currName); - CFStringRef kcSuffix = CFSTR(kKeychainSuffix); - if ( CFStringHasSuffix(newName, kcSuffix) ) // remove the .keychain extension - { - CFRange suffixRange = CFStringFind(newName, kcSuffix, 0); - CFStringFindAndReplace(newName, kcSuffix, CFSTR(""), suffixRange, 0); - } - CFStringAppend(newName, CFSTR(kKeychainRenamedSuffix)); // add "_renamed_" - try - { - renameUnique(keychain, newName); - } - catch(...) - { - // we need to release 'newName' & 'currName' - } - } // else, let the login call report a duplicate - if ( newName ) - CFRelease(newName); - if ( currName ) - CFRelease(currName); - } - catch(...) - { - // We either don't have a login keychain, or there was a - // failure to rename the existing one. - } -} - -#pragma mark ____ File Related ____ - -Keychain StorageManager::make(const char *pathName) -{ - return make(pathName, true); -} - -Keychain StorageManager::make(const char *pathName, bool add) -{ - StLock_(mMutex); - - string fullPathName; - if ( pathName[0] == '/' ) - fullPathName = pathName; - else - { - // Get Home directory from environment. - switch (mDomain) - { - case kSecPreferencesDomainUser: - { - const char *homeDir = getenv("HOME"); - if (homeDir == NULL) - { - // If $HOME is unset get the current user's home directory - // from the passwd file. - uid_t uid = geteuid(); - if (!uid) uid = getuid(); - struct passwd *pw = getpwuid(uid); - if (!pw) - MacOSError::throwMe(errSecParam); - homeDir = pw->pw_dir; - } - fullPathName = homeDir; - } - break; - case kSecPreferencesDomainSystem: - fullPathName = ""; - break; - default: - assert(false); // invalid domain for this - } - - fullPathName += "/Library/Keychains/"; - fullPathName += pathName; - } - - const CSSM_NET_ADDRESS *DbLocation = NULL; // NULL for keychains - 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, add); -} - -Keychain StorageManager::makeLoginAuthUI(const Item *item) -{ - StLock_(mMutex); - - // Create a login/default keychain for the user using UI. - // The user can cancel out of the operation, or create a new login keychain. - // If auto-login is turned off, the user will be asked for their login password. - // - OSStatus result = errSecSuccess; - Keychain keychain; // We return this keychain. - // - // Set up the Auth ref to bring up UI. - // - AuthorizationItem *currItem, *authEnvirItemArrayPtr = NULL; - AuthorizationRef authRef = NULL; - try - { - result = AuthorizationCreate(NULL, NULL, kAuthorizationFlagDefaults, &authRef); - if ( result ) - MacOSError::throwMe(result); - - AuthorizationEnvironment envir; - envir.count = 6; // up to 6 hints can be used. - authEnvirItemArrayPtr = (AuthorizationItem*)malloc(sizeof(AuthorizationItem) * envir.count); - if ( !authEnvirItemArrayPtr ) - MacOSError::throwMe(errAuthorizationInternal); - - currItem = envir.items = authEnvirItemArrayPtr; - - // - // 1st Hint (optional): The keychain item's account attribute string. - // When item is specified, we assume an 'add' operation is being attempted. - char buff[256]; - UInt32 actLen = 0; - SecKeychainAttribute attr = { kSecAccountItemAttr, 255, &buff }; - if ( item ) - { - try - { - (*item)->getAttribute(attr, &actLen); - } - catch(...) - { - actLen = 0; // This item didn't have the account attribute, so don't display one in the UI. - } - } - currItem->name = AGENT_HINT_ATTR_NAME; // name str that identifies this hint as attr name - if ( actLen ) // Fill in the hint if we have an account attr - { - if ( actLen >= sizeof(buff) ) - buff[sizeof(buff)-1] = 0; - else - buff[actLen] = 0; - currItem->valueLength = strlen(buff)+1; - currItem->value = buff; - } - else - { - currItem->valueLength = 0; - currItem->value = NULL; - } - currItem->flags = 0; - - // - // 2nd Hint (optional): The item's keychain full path. - // - currItem++; - char* currDefaultName = NULL; - try - { - currDefaultName = (char*)defaultKeychain()->name(); // Use the name if we have it. - currItem->name = AGENT_HINT_LOGIN_KC_NAME; // Name str that identifies this hint as kc path - currItem->valueLength = (currDefaultName) ? strlen(currDefaultName) : 0; - currItem->value = (currDefaultName) ? (void*)currDefaultName : (void*)""; - currItem->flags = 0; - currItem++; - } - catch(...) - { - envir.count--; - } - - // - // 3rd Hint (required): check if curr default keychain is unavailable. - // This is determined by the parent not existing. - // - currItem->name = AGENT_HINT_LOGIN_KC_EXISTS_IN_KC_FOLDER; - Boolean loginUnavail = false; - try - { - Keychain defaultKC = defaultKeychain(); - if ( !defaultKC->exists() ) - loginUnavail = true; - } - catch(...) // login.keychain not present - { - } - currItem->valueLength = sizeof(Boolean); - currItem->value = (void*)&loginUnavail; - currItem->flags = 0; - - // - // 4th Hint (required): userName - // - currItem++; - currItem->name = AGENT_HINT_LOGIN_KC_USER_NAME; - char* uName = getenv("USER"); - string userName = uName ? uName : ""; - if ( userName.length() == 0 ) - { - uid_t uid = geteuid(); - if (!uid) uid = getuid(); - struct passwd *pw = getpwuid(uid); // fallback case... - if (pw) - userName = pw->pw_name; - endpwent(); - } - if ( userName.length() == 0 ) // did we ultimately get one? - MacOSError::throwMe(errAuthorizationInternal); - - currItem->value = (void*)userName.c_str(); - currItem->valueLength = userName.length(); - currItem->flags = 0; - - // - // 5th Hint (required): flags if user has more than 1 keychain (used for a later warning when reset to default). - // - currItem++; - currItem->name = AGENT_HINT_LOGIN_KC_USER_HAS_OTHER_KCS_STR; - Boolean moreThanOneKCExists = false; - { - // if item is NULL, then this is a user-initiated full reset - if (item && mSavedList.searchList().size() > 1) - moreThanOneKCExists = true; - } - currItem->value = &moreThanOneKCExists; - currItem->valueLength = sizeof(Boolean); - currItem->flags = 0; - - // - // 6th Hint (required): If no item is involved, this is a user-initiated full reset. - // We want to suppress the "do you want to reset to defaults?" panel in this case. - // - currItem++; - currItem->name = AGENT_HINT_LOGIN_KC_SUPPRESS_RESET_PANEL; - Boolean suppressResetPanel = (item == NULL) ? TRUE : FALSE; - currItem->valueLength = sizeof(Boolean); - currItem->value = (void*)&suppressResetPanel; - currItem->flags = 0; - - // - // Set up the auth rights and make the auth call. - // - AuthorizationItem authItem = { LOGIN_KC_CREATION_RIGHT, 0 , NULL, 0 }; - AuthorizationRights rights = { 1, &authItem }; - AuthorizationFlags flags = kAuthorizationFlagDefaults | kAuthorizationFlagInteractionAllowed | kAuthorizationFlagExtendRights; - result = AuthorizationCopyRights(authRef, &rights, &envir, flags, NULL); - if ( result ) - MacOSError::throwMe(result); - try - { - resetKeychain(true); // Clears the plist, moves aside existing login.keychain - } - catch (...) // can throw if no existing login.keychain is found - { - } - login(authRef, (UInt32)userName.length(), userName.c_str()); // Create login.keychain - keychain = loginKeychain(); // Get newly-created login keychain - defaultKeychain(keychain); // Set it to be the default - - free(authEnvirItemArrayPtr); - AuthorizationFree(authRef, kAuthorizationFlagDefaults); - } - - catch (...) - { - // clean up allocations, then rethrow error - if ( authEnvirItemArrayPtr ) - free(authEnvirItemArrayPtr); - if ( authRef ) - AuthorizationFree(authRef, kAuthorizationFlagDefaults); - throw; - } - - return keychain; -} - -Keychain StorageManager::defaultKeychainUI(Item &item) -{ - StLock_(mMutex); - - Keychain returnedKeychain; - try - { - returnedKeychain = defaultKeychain(); // If we have one, return it. - if ( returnedKeychain->exists() ) - return returnedKeychain; - } - catch(...) // We could have one, but it isn't available (i.e. on a un-mounted volume). - { - } - if ( globals().getUserInteractionAllowed() ) - { - returnedKeychain = makeLoginAuthUI(&item); // If no Keychains is present, one will be created. - if ( !returnedKeychain ) - MacOSError::throwMe(errSecInvalidKeychain); // Something went wrong... - } - else - MacOSError::throwMe(errSecInteractionNotAllowed); // If UI isn't allowed, return an error. - - return returnedKeychain; -} - -void -StorageManager::addToDomainList(SecPreferencesDomain domain, - const char* dbName, const CSSM_GUID &guid, uint32 subServiceType) -{ - StLock_(mMutex); - - if (domain == kSecPreferencesDomainDynamic) - MacOSError::throwMe(errSecInvalidPrefsDomain); - - // make the identifier - CSSM_VERSION version = {0, 0}; - DLDbIdentifier id = DLDbListCFPref::makeDLDbIdentifier (guid, version, 0, - subServiceType, dbName, NULL); - - if (domain == mDomain) - { - // manipulate the user's list - { - mSavedList.revert(true); - mSavedList.add(id); - mSavedList.save(); - } - - KCEventNotifier::PostKeychainEvent(kSecKeychainListChangedEvent); - } - else - { - // manipulate the other list - DLDbListCFPref(domain).add(id); - } -} - -void -StorageManager::isInDomainList(SecPreferencesDomain domain, - const char* dbName, const CSSM_GUID &guid, uint32 subServiceType) -{ - StLock_(mMutex); - - if (domain == kSecPreferencesDomainDynamic) - MacOSError::throwMe(errSecInvalidPrefsDomain); - - CSSM_VERSION version = {0, 0}; - DLDbIdentifier id = DLDbListCFPref::makeDLDbIdentifier (guid, version, 0, - subServiceType, dbName, NULL); - - // determine the list to search - bool result; - if (domain == mDomain) - { - result = mSavedList.member(id); - } - else - { - result = DLDbListCFPref(domain).member(id); - } - - // do the search - if (!result) - { - MacOSError::throwMe(errSecNoSuchKeychain); - } -} - -void -StorageManager::removeFromDomainList(SecPreferencesDomain domain, - const char* dbName, const CSSM_GUID &guid, uint32 subServiceType) -{ - StLock_(mMutex); - - if (domain == kSecPreferencesDomainDynamic) - MacOSError::throwMe(errSecInvalidPrefsDomain); - - // make the identifier - CSSM_VERSION version = {0, 0}; - DLDbIdentifier id = DLDbListCFPref::makeDLDbIdentifier (guid, version, 0, - subServiceType, dbName, NULL); - - if (domain == mDomain) - { - // manipulate the user's list - { - mSavedList.revert(true); - mSavedList.remove(id); - mSavedList.save(); - } - - KCEventNotifier::PostKeychainEvent(kSecKeychainListChangedEvent); - } - else - { - // manipulate the other list - DLDbListCFPref(domain).remove(id); - } -} - -bool -StorageManager::keychainOwnerPermissionsValidForDomain(const char* path, SecPreferencesDomain domain) -{ - struct stat sb; - mode_t perms; - const char* sysPrefDir = "/Library/Preferences"; - const char* errMsg = "Will not set default"; - char* mustOwnDir = NULL; - struct passwd* pw = NULL; - - // get my uid - uid_t uid = geteuid(); - if (!uid) uid = getuid(); - - // our (e)uid must own the appropriate preferences or home directory - // for the specified preference domain whose default we will be modifying - switch (domain) { - case kSecPreferencesDomainUser: - mustOwnDir = getenv("HOME"); - if (mustOwnDir == NULL) { - pw = getpwuid(uid); - if (!pw) return false; - mustOwnDir = pw->pw_dir; - } - break; - case kSecPreferencesDomainSystem: - mustOwnDir = (char*)sysPrefDir; - break; - case kSecPreferencesDomainCommon: - mustOwnDir = (char*)sysPrefDir; - break; - default: - return false; - } - - if (mustOwnDir != NULL) { - struct stat dsb; - if ( (stat(mustOwnDir, &dsb) != 0) || (dsb.st_uid != uid) ) { - fprintf(stderr, "%s: UID=%d does not own directory %s\n", errMsg, (int)uid, mustOwnDir); - mustOwnDir = NULL; // will return below after calling endpwent() - } - } - - if (pw != NULL) - endpwent(); - - if (mustOwnDir == NULL) - return false; - - // check that file actually exists - if (stat(path, &sb) != 0) { - fprintf(stderr, "%s: file %s does not exist\n", errMsg, path); - return false; - } - - // check flags - if (sb.st_flags & (SF_IMMUTABLE | UF_IMMUTABLE)) { - fprintf(stderr, "%s: file %s is immutable\n", errMsg, path); - return false; - } - - // check ownership - if (sb.st_uid != uid) { - fprintf(stderr, "%s: file %s is owned by UID=%d, but we have UID=%d\n", - errMsg, path, (int)sb.st_uid, (int)uid); - return false; - } - - // check mode - perms = sb.st_mode; - perms |= 0600; // must have owner read/write permission set - if (sb.st_mode != perms) { - fprintf(stderr, "%s: file %s does not have the expected permissions\n", errMsg, path); - return false; - } - - // user owns file and can read/write it - return true; -}