/* * Copyright (c) 2000-2001 Apple Computer, Inc. All Rights Reserved. * * The contents of this file constitute Original Code as defined in and are * subject to the Apple Public Source License Version 1.2 (the 'License'). * You may not use this file except in compliance with the License. Please obtain * a copy of the License at http://www.apple.com/publicsource and read it before * using this file. * * This 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. */ /* File: StorageManager.cpp Contains: Working with multiple keychains Copyright: 2000 by Apple Computer, Inc., all rights reserved. To Do: */ #include "StorageManager.h" #include "KCEventNotifier.h" #include #include #include #include #include #include #include #include #include #include #include "KCCursor.h" #include "Globals.h" #include "DefaultKeychain.h" using namespace CssmClient; using namespace KeychainCore; StorageManager::StorageManager() : mSavedList(), mKeychains(), mMultiDLDb(mSavedList.list(), true) // Passinng true enables use of Secure Storage { } // Create KC if it doesn't exist Keychain StorageManager::keychain(const DLDbIdentifier &dLDbIdentifier) { //StLock _(mKeychainsLock); KeychainMap::iterator it = mKeychains.find(dLDbIdentifier); if (it != mKeychains.end()) return it->second; // The keychain is not in our cache. Create it. Keychain keychain(mMultiDLDb->database(dLDbIdentifier)); // Add the keychain to the cache. mKeychains.insert(KeychainMap::value_type(dLDbIdentifier, keychain)); return keychain; } // Create KC if it doesn't exist Keychain StorageManager::makeKeychain(const DLDbIdentifier &dLDbIdentifier) { Keychain keychain(keychain(dLDbIdentifier)); const vector &list = mMultiDLDb->list(); if (find(list.begin(), list.end(), dLDbIdentifier) != list.end()) { // The dLDbIdentifier for this keychain is already on our search list. return keychain; } // If the keychain doesn't exist don't bother adding it to the search list yet. if (!keychain->exists()) return keychain; // The keychain exists and is not in our search list add it to the search // list and the cache. Then inform mMultiDLDb. mSavedList.revert(true); mSavedList.add(dLDbIdentifier); mSavedList.save(); // @@@ Will happen again when kSecKeychainListChangedEvent notification is received. mMultiDLDb->list(mSavedList.list()); KCEventNotifier::PostKeychainEvent(kSecKeychainListChangedEvent); return keychain; } void StorageManager::created(const Keychain &keychain) // Be notified a Keychain just got created. { DLDbIdentifier dLDbIdentifier = keychain->dLDbIdentifier(); // If we don't have a default Keychain yet. Make the newly created keychain the default. DefaultKeychain &defaultKeychain = globals().defaultKeychain; if (!defaultKeychain.isSet()) defaultKeychain.dLDbIdentifier(dLDbIdentifier); // Add the keychain to the search list and the cache. Then inform mMultiDLDb. mSavedList.revert(true); mSavedList.add(dLDbIdentifier); mSavedList.save(); // @@@ Will happen again when kSecKeychainListChangedEvent notification is received. mMultiDLDb->list(mSavedList.list()); KCEventNotifier::PostKeychainEvent(kSecKeychainListChangedEvent); } KCCursor StorageManager::createCursor(SecItemClass itemClass, const SecKeychainAttributeList *attrList) { return KCCursor(DbCursor(mMultiDLDb), itemClass, attrList); } KCCursor StorageManager::createCursor(const SecKeychainAttributeList *attrList) { return KCCursor(DbCursor(mMultiDLDb), attrList); } void StorageManager::lockAll() { for (KeychainMap::iterator ix = mKeychains.begin(); ix != mKeychains.end(); ix++) { Keychain keychain(ix->second); if (keychain->isActive()) keychain->lock(); } } void StorageManager::reload(bool force) { // Reinitialize list from CFPrefs if changed. When force is true force a prefs revert now. if (mSavedList.revert(force)) mMultiDLDb->list(mSavedList.list()); } size_t StorageManager::size() { reload(); return mMultiDLDb->list().size(); } Keychain StorageManager::at(unsigned int ix) { reload(); if (ix >= mMultiDLDb->list().size()) MacOSError::throwMe(errSecInvalidKeychain); return keychain(mMultiDLDb->list().at(ix)); } Keychain StorageManager::operator[](unsigned int ix) { return at(ix); } void StorageManager::remove(const list& kcsToRemove) { //StLock _(mKeychainsLock); mSavedList.revert(true); DLDbIdentifier defaultId = globals().defaultKeychain.dLDbIdentifier(); bool unsetDefault=false; for (list::const_iterator ix = kcsToRemove.begin();ix!=kcsToRemove.end();ix++) { // Find the keychain object for the given ref Keychain keychainToRemove; try { keychainToRemove = KeychainRef::required(*ix); } catch (const MacOSError& err) { if (err.osStatus() == errSecInvalidKeychain) continue; throw; } // Remove it from the saved list mSavedList.remove(keychainToRemove->dLDbIdentifier()); if (keychainToRemove->dLDbIdentifier() == defaultId) unsetDefault=true; // Now remove it from the map KeychainMap::iterator it = mKeychains.find(keychainToRemove->dLDbIdentifier()); if (it==mKeychains.end()) continue; mKeychains.erase(it); } mSavedList.save(); mMultiDLDb->list(mSavedList.list()); KCEventNotifier::PostKeychainEvent(kSecKeychainListChangedEvent); if (unsetDefault) globals().defaultKeychain.unset(); } void StorageManager::replace(const list& newKCList) { // replace keychains list with new list CssmClient::DLDbList dldbList; convert(newKCList,dldbList); } void StorageManager::convert(const list& SecKeychainRefList,CssmClient::DLDbList& dldbList) { // Convert a list of SecKeychainRefs to a DLDbList dldbList.clear(); // If we don't clear list, we should use "add" instead of push_back for (list::const_iterator ix = SecKeychainRefList.begin();ix!=SecKeychainRefList.end();ix++) { // Find the keychain object for the given ref Keychain keychain; try { keychain = KeychainRef::required(*ix); } catch (const MacOSError& err) { if (err.osStatus() == errSecInvalidKeychain) continue; throw; } // Add it to the list dldbList.push_back(keychain->dLDbIdentifier()); } } #pragma mark ΡΡΡΡ Login Functions ΡΡΡΡ void StorageManager::login(ConstStringPtr name, ConstStringPtr password) { if ( name == NULL || password == NULL ) MacOSError::throwMe(paramErr); login(name[0], name + 1, password[0], password + 1); } void StorageManager::login(UInt32 nameLength, const void *name, UInt32 passwordLength, const void *password) { // @@@ set up the login session on behalf of loginwindow // @@@ (this code should migrate into loginwindow) debug("KClogin", "setting up login session"); if (OSStatus ssnErr = SessionCreate(sessionKeepCurrentBootstrap, sessionHasGraphicAccess | sessionHasTTY)) debug("KClogin", "session setup failed status=%ld", ssnErr); if (name == NULL || (passwordLength != 0 && password == NULL)) MacOSError::throwMe(paramErr); // Make sure name is zero terminated string theName(reinterpret_cast(name), nameLength); Keychain keychain = make(theName.c_str()); try { keychain->unlock(CssmData(const_cast(password), passwordLength)); debug("KClogin", "keychain unlock successful"); } catch(const CssmError &e) { if (e.osStatus() != CSSMERR_DL_DATASTORE_DOESNOT_EXIST) throw; debug("KClogin", "creating login keychain"); keychain->create(passwordLength, password); // Login Keychain does not lock on sleep nor lock after timeout by default. keychain->setSettings(INT_MAX, false); } // @@@ Create a authorization credential for the current user. debug("KClogin", "creating login authorization"); const AuthorizationItem envList[] = { { kAuthorizationEnvironmentUsername, nameLength, const_cast(name), 0 }, { kAuthorizationEnvironmentPassword, passwordLength, const_cast(password), 0 }, { kAuthorizationEnvironmentShared, 0, NULL, 0 } }; const AuthorizationEnvironment environment = { sizeof(envList) / sizeof(*envList), const_cast(envList) }; if (OSStatus authErr = AuthorizationCreate(NULL, &environment, kAuthorizationFlagExtendRights | kAuthorizationFlagPreAuthorize, NULL)) debug("KClogin", "failed to create login auth, status=%ld", authErr); } void StorageManager::logout() { // nothing left to do here } void StorageManager::changeLoginPassword(ConstStringPtr oldPassword, ConstStringPtr newPassword) { globals().defaultKeychain.keychain()->changePassphrase(oldPassword, newPassword); } void StorageManager::changeLoginPassword(UInt32 oldPasswordLength, const void *oldPassword, UInt32 newPasswordLength, const void *newPassword) { globals().defaultKeychain.keychain()->changePassphrase(oldPasswordLength, oldPassword, newPasswordLength, newPassword); } #pragma mark ΡΡΡΡ File Related ΡΡΡΡ Keychain StorageManager::make(const char *pathName) { string fullPathName; if ( pathName[0] == '/' ) fullPathName = pathName; else { // Get Home directory from environment. const char *homeDir = getenv("HOME"); if (homeDir == NULL) { // If $HOME is unset get the current users home directory from the passwd file. struct passwd *pw = getpwuid(getuid()); if (!pw) MacOSError::throwMe(paramErr); homeDir = pw->pw_dir; } fullPathName = homeDir; 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 ); } KeychainSchema StorageManager::keychainSchemaFor(const CssmClient::Db &db) { KeychainSchema schema(db); pair result = mKeychainSchemaSet.insert(db); if (result.second) return schema; return *result.first; }