StorageManager.cpp [plain text]
#include "StorageManager.h"
#include "KCEventNotifier.h"
#include <Security/cssmapple.h>
#include <sys/types.h>
#include <pwd.h>
#include <CoreServices/../Frameworks/CarbonCore.framework/Headers/MacErrors.h>
#include <algorithm>
#include <string>
#include <Security/Authorization.h>
#include <Security/AuthorizationTags.h>
#include <Security/AuthSession.h>
#include <Security/debugging.h>
#include <Security/SecCFTypes.h>
#include "KCCursor.h"
#include "Globals.h"
#include "DefaultKeychain.h"
using namespace CssmClient;
using namespace KeychainCore;
StorageManager::StorageManager() :
mSavedList(),
mKeychains(),
mSearchList()
{
_doReload();
}
Keychain
StorageManager::keychain(const DLDbIdentifier &dLDbIdentifier)
{
StLock<Mutex> _(mLock);
return _keychain(dLDbIdentifier);
}
Keychain
StorageManager::_keychain(const DLDbIdentifier &dLDbIdentifier)
{
KeychainMap::iterator it = mKeychains.find(dLDbIdentifier);
if (it != mKeychains.end())
return it->second;
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);
mKeychains.insert(KeychainMap::value_type(dLDbIdentifier, keychain));
return keychain;
}
Keychain
StorageManager::makeKeychain(const DLDbIdentifier &dLDbIdentifier)
{
Keychain keychain(keychain(dLDbIdentifier));
{
StLock<Mutex> _(mLock);
if (find(mSearchList.begin(), mSearchList.end(), keychain) != mSearchList.end())
{
return keychain;
}
if (!keychain->exists())
return keychain;
mSavedList.revert(true);
mSavedList.add(dLDbIdentifier);
mSavedList.save();
_doReload();
}
KCEventNotifier::PostKeychainEvent(kSecKeychainListChangedEvent);
return keychain;
}
void
StorageManager::created(const Keychain &keychain) {
DLDbIdentifier dLDbIdentifier = keychain->dLDbIdentifier();
{
StLock<Mutex> _(mLock);
DefaultKeychain &defaultKeychain = globals().defaultKeychain;
if (!defaultKeychain.isSet())
defaultKeychain.dLDbIdentifier(dLDbIdentifier);
mSavedList.revert(true);
mSavedList.add(dLDbIdentifier);
mSavedList.save();
_doReload();
}
KCEventNotifier::PostKeychainEvent(kSecKeychainListChangedEvent);
}
KCCursor
StorageManager::createCursor(SecItemClass itemClass, const SecKeychainAttributeList *attrList)
{
StLock<Mutex> _(mLock);
return KCCursor(mSearchList, itemClass, attrList);
}
KCCursor
StorageManager::createCursor(const SecKeychainAttributeList *attrList)
{
StLock<Mutex> _(mLock);
return KCCursor(mSearchList, attrList);
}
void
StorageManager::lockAll()
{
KeychainList keychainList;
{
StLock<Mutex> _(mLock);
for (KeychainMap::iterator ix = mKeychains.begin(); ix != mKeychains.end(); ix++)
keychainList.push_back(ix->second);
}
for (KeychainList::iterator ix = keychainList.begin(); ix != keychainList.end(); ++ix)
{
Keychain keychain = *ix;
if (keychain->isActive())
keychain->lock();
}
}
void
StorageManager::_doReload()
{
KeychainList newList;
newList.reserve(mSavedList.size());
for (CssmClient::DLDbList::iterator ix = mSavedList.begin(); ix != mSavedList.end(); ++ix)
{
Keychain keychain(_keychain(*ix));
newList.push_back(keychain);
}
mSearchList.swap(newList);
}
void
StorageManager::reload(bool force)
{
StLock<Mutex> _(mLock);
_reload(force);
}
void
StorageManager::_reload(bool force)
{
if (mSavedList.revert(force))
_doReload();
}
size_t
StorageManager::size()
{
StLock<Mutex> _(mLock);
_reload();
return mSearchList.size();
}
Keychain
StorageManager::at(unsigned int ix)
{
StLock<Mutex> _(mLock);
_reload();
if (ix >= mSearchList.size())
MacOSError::throwMe(errSecInvalidKeychain);
return mSearchList.at(ix);
}
Keychain
StorageManager::operator[](unsigned int ix)
{
return at(ix);
}
void StorageManager::remove(const KeychainList &kcsToRemove, bool deleteDb)
{
bool unsetDefault = false;
{
StLock<Mutex> _(mLock);
mSavedList.revert(true);
DLDbIdentifier defaultId = globals().defaultKeychain.dLDbIdentifier();
for (KeychainList::const_iterator ix = kcsToRemove.begin(); ix != kcsToRemove.end(); ++ix)
{
Keychain keychainToRemove = *ix;
DLDbIdentifier dLDbIdentifier = keychainToRemove->dLDbIdentifier();
mSavedList.remove(dLDbIdentifier);
if (dLDbIdentifier == defaultId)
unsetDefault=true;
if (deleteDb)
{
keychainToRemove->database()->deleteDb();
KeychainMap::iterator it = mKeychains.find(dLDbIdentifier);
if (it == mKeychains.end())
continue;
mKeychains.erase(it);
}
}
mSavedList.save();
_doReload();
}
KCEventNotifier::PostKeychainEvent(kSecKeychainListChangedEvent);
if (unsetDefault)
{
globals().defaultKeychain.unset();
}
}
void
StorageManager::getSearchList(KeychainList &keychainList)
{
StLock<Mutex> _(mLock);
StorageManager::KeychainList searchList(mSearchList);
keychainList.swap(searchList);
}
void
StorageManager::setSearchList(const KeychainList &keychainList)
{
StorageManager::KeychainList keychains(keychainList);
StLock<Mutex> _(mLock);
mSearchList.swap(keychains);
}
void
StorageManager::optionalSearchList(CFTypeRef keychainOrArray, KeychainList &keychainList)
{
if (!keychainOrArray)
getSearchList(keychainList);
else
{
CFTypeID typeID = CFGetTypeID(keychainOrArray);
if (typeID == CFArrayGetTypeID())
convertToKeychainList(CFArrayRef(keychainOrArray), keychainList);
else if (typeID == gTypes().keychain.typeId)
keychainList.push_back(gTypes().keychain.required(SecKeychainRef(keychainOrArray)));
else
MacOSError::throwMe(paramErr);
}
}
void
StorageManager::convertToKeychainList(CFArrayRef keychainArray, KeychainList &keychainList)
{
assert(keychainArray);
CFIndex count = CFArrayGetCount(keychainArray);
KeychainList keychains(count);
CFClass<KeychainImpl, SecKeychainRef, errSecInvalidKeychain> &kcClass = gTypes().keychain;
for (CFIndex ix = 0; ix < count; ++ix)
{
keychains[ix] = kcClass.required(SecKeychainRef(CFArrayGetValueAtIndex(keychainArray, ix)));
}
keychainList.swap(keychains);
}
CFArrayRef
StorageManager::convertFromKeychainList(const KeychainList &keychainList)
{
CFRef<CFMutableArrayRef> keychainArray(CFArrayCreateMutable(NULL, keychainList.size(), &kCFTypeArrayCallBacks));
CFClass<KeychainImpl, SecKeychainRef, errSecInvalidKeychain> &kcClass = gTypes().keychain;
for (KeychainList::const_iterator ix = keychainList.begin(); ix != keychainList.end(); ++ix)
{
SecKeychainRef keychainRef = kcClass.handle(**ix);
CFArrayAppendValue(keychainArray, keychainRef);
CFRelease(keychainRef);
}
CFRetain(keychainArray);
return keychainArray;
}
#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)
{
#if 0
debug("KClogin", "setting up login session");
if (OSStatus ssnErr = SessionCreate(sessionKeepCurrentBootstrap,
sessionHasGraphicAccess | sessionHasTTY))
debug("KClogin", "session setup failed status=%ld", ssnErr);
#endif
if (name == NULL || (passwordLength != 0 && password == NULL))
MacOSError::throwMe(paramErr);
string theName(reinterpret_cast<const char *>(name), nameLength);
Keychain keychain = make(theName.c_str());
try
{
keychain->unlock(CssmData(const_cast<void *>(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);
keychain->setSettings(INT_MAX, false);
}
#if 0
debug("KClogin", "creating login authorization");
const AuthorizationItem envList[] =
{
{ kAuthorizationEnvironmentUsername, nameLength, const_cast<void *>(name), 0 },
{ kAuthorizationEnvironmentPassword, passwordLength, const_cast<void *>(password), 0 },
{ kAuthorizationEnvironmentShared, 0, NULL, 0 }
};
const AuthorizationEnvironment environment =
{
sizeof(envList) / sizeof(*envList),
const_cast<AuthorizationItem *>(envList)
};
if (OSStatus authErr = AuthorizationCreate(NULL, &environment,
kAuthorizationFlagExtendRights | kAuthorizationFlagPreAuthorize, NULL))
debug("KClogin", "failed to create login auth, status=%ld", authErr);
#endif
}
void StorageManager::logout()
{
}
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
{
const char *homeDir = getenv("HOME");
if (homeDir == NULL)
{
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; 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<KeychainSchemaSet::iterator, bool> result = mKeychainSchemaSet.insert(db);
if (result.second)
return schema;
return *result.first;
}