+++ /dev/null
-/*
- * 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 <Security/cssmapple.h>
-#include <sys/types.h>
-#include <sys/param.h>
-#include <syslog.h>
-#include <pwd.h>
-#include <algorithm>
-#include <string>
-#include <stdio.h>
-//#include <Security/AuthorizationTags.h>
-//#include <Security/AuthSession.h>
-#include <security_utilities/debugging.h>
-#include <security_keychain/SecCFTypes.h>
-//#include <Security/SecurityAgentClient.h>
-#include <securityd_client/ssclient.h>
-#include <Security/AuthorizationTags.h>
-#include <Security/AuthorizationTagsPriv.h>
-#include <Security/SecTask.h>
-#include <security_keychain/SecCFTypes.h>
-#include "TrustSettingsSchema.h"
-#include <security_cdsa_client/wrapkey.h>
-
-//%%% 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<Mutex>_(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<Mutex>_(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<Mutex>_(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<Mutex>_(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<Mutex>_(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<Mutex>_(mMutex);
-
- KeychainList searchList;
- getSearchList(searchList);
- return KCCursor(searchList, itemClass, attrList);
-}
-
-KCCursor
-StorageManager::createCursor(const SecKeychainAttributeList *attrList)
-{
- StLock<Mutex>_(mMutex);
-
- KeychainList searchList;
- getSearchList(searchList);
- return KCCursor(searchList, attrList);
-}
-
-void
-StorageManager::lockAll()
-{
- StLock<Mutex>_(mMutex);
-
- SecurityServer::ClientSession ss(Allocator::standard(), Allocator::standard());
- ss.lockAll (false);
-}
-
-Keychain
-StorageManager::defaultKeychain()
-{
- StLock<Mutex>_(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<Mutex>_(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<Mutex>_(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<Mutex>_(mMutex);
-
- if (domain == kSecPreferencesDomainDynamic)
- MacOSError::throwMe(errSecInvalidPrefsDomain);
-
- if (domain == mDomain)
- defaultKeychain(keychain);
- else
- DLDbListCFPref(domain).defaultDLDbIdentifier(keychain->dlDbIdentifier());
-}
-
-Keychain
-StorageManager::loginKeychain()
-{
- StLock<Mutex>_(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<Mutex>_(mMutex);
-
- mSavedList.revert(true);
- mSavedList.loginDLDbIdentifier(keychain->dlDbIdentifier());
- mSavedList.save();
-}
-
-size_t
-StorageManager::size()
-{
- StLock<Mutex>_(mMutex);
-
- mSavedList.revert(false);
- mCommonList.revert(false);
- return mSavedList.searchList().size() + mCommonList.searchList().size();
-}
-
-Keychain
-StorageManager::at(unsigned int ix)
-{
- StLock<Mutex>_(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<Mutex>_(mMutex);
-
- return at(ix);
-}
-
-void StorageManager::rename(Keychain keychain, const char* newName)
-{
-
- StLock<Mutex>_(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<Mutex>_(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<Mutex>_(mMutex);
-
- // make a CFString of our identifier
- const char* idname = id.dbName ();
- if (idname == NULL)
- {
- return;
- }
-
- CFRef<CFStringRef> 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<CFMutableArrayRef> 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<Mutex>_(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<Mutex>_(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<Mutex>_(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<Mutex>_(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<Mutex>_(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<Mutex>_(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<Mutex>_(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<CFMutableArrayRef> 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<Mutex>_(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<Mutex>_(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<Mutex>_(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() : "<NULL>");
- 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() : "<NULL>", (unsigned int)passwordLength);
- loginRenamed1KC->unlock(CssmData(const_cast<void *>(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() : "<NULL>");
- 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() : "<NULL>",
- (shortnameDotDLDbIdentifier) ? shortnameDotDLDbIdentifier.dbName() : "<NULL>");
- 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() : "<NULL>",
- (loginDLDbIdentifier) ? loginDLDbIdentifier.dbName() : "<NULL>");
- 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() : "<NULL>");
- 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() : "<NULL>");
- 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() : "<NULL>");
- 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() : "<NULL>", (unsigned int)passwordLength);
- theKeychain->unlock(CssmData(const_cast<void *>(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<void *>(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() : "<NULL>");
- shortnameDotKC->unlock(CssmData(const_cast<void *>(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() : "<NULL>");
- 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() : "<NULL>");
- 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() : "<NULL>");
- if (!loginDLDbIdentifier)
- MacOSError::throwMe(errSecNoSuchKeychain);
-
- try
- {
- Keychain theKeychain(keychain(loginDLDbIdentifier));
- secdebug("KCLogin", "Attempting to stash login keychain \"%s\"",
- (theKeychain) ? theKeychain->name() : "<NULL>");
- 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<Mutex>_(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<Mutex>_(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<Mutex>_(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<Mutex>_(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<Mutex>_(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<Mutex>_(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<Mutex>_(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<Mutex>_(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<Mutex>_(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;
-}