#include <Security/cssmapple.h>
#include <sys/types.h>
+#include <sys/param.h>
+#include <syslog.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 <Security/AuthSession.h>
+#include <Security/SecurityAgentClient.h>
+#include <Security/ssclient.h>
#include "KCCursor.h"
#include "Globals.h"
-#include "DefaultKeychain.h"
using namespace CssmClient;
using namespace KeychainCore;
+// normal debug calls, which get stubbed out for deployment builds
+#define x_debug(str) secdebug("KClogin",(str))
+#define x_debug1(fmt,arg1) secdebug("KClogin",(fmt),(arg1))
+#define x_debug2(fmt,arg1,arg2) secdebug("KClogin",(fmt),(arg1),(arg2))
+
+//-----------------------------------------------------------------------------------
+
StorageManager::StorageManager() :
- mSavedList(),
- mKeychains(),
- mSearchList()
+ mSavedList(kSecPreferencesDomainUser),
+ mCommonList(kSecPreferencesDomainCommon),
+ mDomain(kSecPreferencesDomainUser),
+ mKeychains()
{
- _doReload();
+ // get session attributes
+ SessionAttributeBits sessionAttrs;
+ if (OSStatus err = SessionGetInfo(callerSecuritySession,
+ NULL, &sessionAttrs))
+ CssmError::throwMe(err);
+
+ // If this is the root session, switch to 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", "switching to system preferences");
+ mDomain = kSecPreferencesDomainSystem;
+ mSavedList.set(kSecPreferencesDomainSystem);
+ }
}
// Create KC if it doesn't exist
Keychain
StorageManager::_keychain(const DLDbIdentifier &dLDbIdentifier)
{
+ if (!dLDbIdentifier)
+ return Keychain();
+
KeychainMap::iterator it = mKeychains.find(dLDbIdentifier);
if (it != mKeychains.end())
return it->second;
dl->subserviceId(dLDbIdentifier.ssuid().subserviceId());
dl->version(dLDbIdentifier.ssuid().version());
Db db(dl, dLDbIdentifier.dbName());
- Keychain keychain(db);
+ Keychain keychain(db);
// Add the keychain to the cache.
- mKeychains.insert(KeychainMap::value_type(dLDbIdentifier, keychain));
+ mKeychains.insert(KeychainMap::value_type(dLDbIdentifier, &*keychain));
+
return keychain;
}
+// Called from KeychainImpl's destructor remove it from the map.
+void
+StorageManager::removeKeychain(const DLDbIdentifier &dLDbIdentifier, KeychainImpl *keychainImpl)
+{
+ // @@@ Work out locking StLock<Mutex> _(mLock);
+ KeychainMap::iterator it = mKeychains.find(dLDbIdentifier);
+ if (it != mKeychains.end() && it->second == keychainImpl)
+ mKeychains.erase(it);
+}
+
+// if a database is key-unlockable, authenticate it with any matching unlock keys found in the KC list
+void StorageManager::setDefaultCredentials(const Db &db)
+{
+ try {
+ CssmAutoData index(db->allocator());
+ if (!db->getUnlockKeyIndex(index.get()))
+ return; // no suggested index (probably not a CSPDL)
+
+ TrackingAllocator alloc(CssmAllocator::standard());
+
+ KCCursor search(createCursor(CSSM_DL_DB_RECORD_SYMMETRIC_KEY, NULL));
+ CssmAutoData keyLabel(CssmAllocator::standard());
+ keyLabel = StringData("SYSKC**");
+ keyLabel.append(index);
+ static const CSSM_DB_ATTRIBUTE_INFO infoLabel = {
+ CSSM_DB_ATTRIBUTE_NAME_AS_STRING,
+ {"Label"},
+ CSSM_DB_ATTRIBUTE_FORMAT_BLOB
+ };
+ search->add(CSSM_DB_EQUAL, infoLabel, keyLabel.get());
+
+ // could run a loop below to catch *all* eligible keys,
+ // but that's stretching it; and beware CSP scope if you add this...
+ AutoCredentials cred(alloc);
+ Item keyItem;
+ if (search->next(keyItem)) {
+ CssmClient::Key key = dynamic_cast<KeyItem &>(*keyItem).key();
+
+ // create AccessCredentials from that key. Still allow interactive unlock
+ const CssmKey &masterKey = key;
+ CSSM_CSP_HANDLE cspHandle = key->csp()->handle();
+ cred += TypedList(alloc, CSSM_SAMPLE_TYPE_KEYCHAIN_LOCK,
+ new(alloc) ListElement(CSSM_WORDID_SYMMETRIC_KEY),
+ new(alloc) ListElement(CssmData::wrap(cspHandle)),
+ new(alloc) ListElement(CssmData::wrap(masterKey)));
+ cred += TypedList(alloc, CSSM_SAMPLE_TYPE_KEYCHAIN_LOCK,
+ new(alloc) ListElement(CSSM_SAMPLE_TYPE_KEYCHAIN_PROMPT));
+
+ secdebug("storagemgr", "authenticating %s for default key credentials", db->name());
+ db->authenticate(db->accessRequest(), &cred);
+ }
+ } catch (...) {
+ secdebug("storagemgr", "setDefaultCredentials for %s abandoned due to exception", db->name());
+ }
+}
+
// Create KC if it doesn't exist, add it to the search list if it exists and is not already on it.
Keychain
-StorageManager::makeKeychain(const DLDbIdentifier &dLDbIdentifier)
+StorageManager::makeKeychain(const DLDbIdentifier &dLDbIdentifier, bool add)
{
- Keychain keychain(keychain(dLDbIdentifier));
+ Keychain keychain;
+ bool post = false;
{
StLock<Mutex> _(mLock);
- if (find(mSearchList.begin(), mSearchList.end(), keychain) != mSearchList.end())
+ keychain = _keychain(dLDbIdentifier);
+
+ if (add)
{
- // This keychain is already on our search list.
- return keychain;
+ mSavedList.revert(false);
+ DLDbList searchList = mSavedList.searchList();
+ if (find(searchList.begin(), searchList.end(), dLDbIdentifier) != searchList.end())
+ return keychain; // Keychain is already in the searchList.
+
+ mCommonList.revert(false);
+ searchList = mCommonList.searchList();
+ if (find(searchList.begin(), searchList.end(), dLDbIdentifier) != searchList.end())
+ return keychain; // Keychain is already in the commonList don't add it to the searchList.
+
+ // 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.
+ mSavedList.revert(true);
+ mSavedList.add(dLDbIdentifier);
+ mSavedList.save();
+ post = true;
}
-
- // 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.
- _doReload();
}
- // Make sure we are not holding mLock when we post this event.
- KCEventNotifier::PostKeychainEvent(kSecKeychainListChangedEvent);
+ if (post)
+ {
+ // Make sure we are not holding mLock when we post this event.
+ KCEventNotifier::PostKeychainEvent(kSecKeychainListChangedEvent);
+ }
return keychain;
}
StorageManager::created(const Keychain &keychain) // Be notified a Keychain just got created.
{
DLDbIdentifier dLDbIdentifier = keychain->dLDbIdentifier();
+ bool defaultChanged = false;
{
StLock<Mutex> _(mLock);
- // 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);
+ // 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();
-
- // @@@ Will happen again when kSecKeychainListChangedEvent notification is received.
- _doReload();
}
- // Make sure we are not holding mLock when we post this event.
+ // 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> _(mLock);
- return KCCursor(mSearchList, itemClass, attrList);
+ KeychainList searchList;
+ getSearchList(searchList);
+ return KCCursor(searchList, itemClass, attrList);
}
KCCursor
StorageManager::createCursor(const SecKeychainAttributeList *attrList)
{
- StLock<Mutex> _(mLock);
- return KCCursor(mSearchList, attrList);
+ KeychainList searchList;
+ getSearchList(searchList);
+ return KCCursor(searchList, attrList);
}
void
StorageManager::lockAll()
{
- // Make a snapshot of all known keychains while holding mLock.
- KeychainList keychainList;
+ SecurityServer::ClientSession ss(CssmAllocator::standard(), CssmAllocator::standard());
+ ss.lockAll (false);
+}
+
+Keychain
+StorageManager::defaultKeychain()
+{
+ Keychain theKeychain;
+ {
+ StLock<Mutex> _(mLock);
+ mSavedList.revert(false);
+ DLDbIdentifier defaultDLDbIdentifier(mSavedList.defaultDLDbIdentifier());
+ if (defaultDLDbIdentifier)
+ {
+ theKeychain = _keychain(defaultDLDbIdentifier);
+ }
+ }
+
+ if (theKeychain /* && theKeychain->exists() */)
+ return theKeychain;
+
+ MacOSError::throwMe(errSecNoDefaultKeychain);
+}
+
+void
+StorageManager::defaultKeychain(const Keychain &keychain)
+{
+ DLDbIdentifier oldDefaultId;
+ DLDbIdentifier newDefaultId(keychain->dLDbIdentifier());
{
StLock<Mutex> _(mLock);
- for (KeychainMap::iterator ix = mKeychains.begin(); ix != mKeychains.end(); ix++)
- keychainList.push_back(ix->second);
+ oldDefaultId = mSavedList.defaultDLDbIdentifier();
+ mSavedList.revert(true);
+ mSavedList.defaultDLDbIdentifier(newDefaultId);
+ mSavedList.save();
}
- // Lock each active keychain after having released mLock since locking keychains
- // will send notifications.
- for (KeychainList::iterator ix = keychainList.begin(); ix != keychainList.end(); ++ix)
+ if (!(oldDefaultId == newDefaultId))
{
- Keychain keychain = *ix;
- if (keychain->isActive())
- keychain->lock();
+ // Make sure we are not holding mLock when we post this event.
+ KCEventNotifier::PostKeychainEvent(kSecDefaultChangedEvent, newDefaultId);
}
}
-void
-StorageManager::_doReload()
+Keychain
+StorageManager::defaultKeychain(SecPreferencesDomain domain)
{
- KeychainList newList;
- newList.reserve(mSavedList.size());
- for (CssmClient::DLDbList::iterator ix = mSavedList.begin(); ix != mSavedList.end(); ++ix)
+ if (domain == mDomain)
+ return defaultKeychain();
+ else
{
- Keychain keychain(_keychain(*ix));
- newList.push_back(keychain);
+ DLDbIdentifier defaultDLDbIdentifier(DLDbListCFPref(domain).defaultDLDbIdentifier());
+ if (defaultDLDbIdentifier)
+ return keychain(defaultDLDbIdentifier);
+
+ MacOSError::throwMe(errSecNoDefaultKeychain);
}
- mSearchList.swap(newList);
}
void
-StorageManager::reload(bool force)
+StorageManager::defaultKeychain(SecPreferencesDomain domain, const Keychain &keychain)
{
- StLock<Mutex> _(mLock);
- _reload(force);
+ if (domain == mDomain)
+ defaultKeychain(keychain);
+ else
+ DLDbListCFPref(domain).defaultDLDbIdentifier(keychain->dLDbIdentifier());
+}
+
+Keychain
+StorageManager::loginKeychain()
+{
+ Keychain theKeychain;
+ {
+ StLock<Mutex> _(mLock);
+ mSavedList.revert(false);
+ DLDbIdentifier loginDLDbIdentifier(mSavedList.loginDLDbIdentifier());
+ if (loginDLDbIdentifier)
+ {
+ theKeychain = _keychain(loginDLDbIdentifier);
+ }
+ }
+
+ if (theKeychain && theKeychain->exists())
+ return theKeychain;
+
+ MacOSError::throwMe(errSecNoSuchKeychain);
}
void
-StorageManager::_reload(bool force)
+StorageManager::loginKeychain(Keychain keychain)
{
- // Reinitialize list from CFPrefs if changed. When force is true force a prefs revert now.
- if (mSavedList.revert(force))
- _doReload();
+ StLock<Mutex> _(mLock);
+ mSavedList.revert(true);
+ mSavedList.loginDLDbIdentifier(keychain->dLDbIdentifier());
+ mSavedList.save();
}
size_t
StorageManager::size()
{
StLock<Mutex> _(mLock);
- _reload();
- return mSearchList.size();
+ mSavedList.revert(false);
+ mCommonList.revert(false);
+ return mSavedList.searchList().size() + mCommonList.searchList().size();
}
Keychain
StorageManager::at(unsigned int ix)
{
StLock<Mutex> _(mLock);
- _reload();
- if (ix >= mSearchList.size())
- MacOSError::throwMe(errSecInvalidKeychain);
+ 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 mSearchList.at(ix);
+ return _keychain(commonList[ix]);
+ }
}
Keychain
return at(ix);
}
+void StorageManager::rename(Keychain keychain, const char* newName)
+{
+ // This is not a generic purpose rename method for keychains.
+ // The keychain doesn't remain in the cache.
+ //
+ bool changedDefault = false;
+ DLDbIdentifier newDLDbIdentifier;
+ {
+ StLock<Mutex> _(mLock);
+ mSavedList.revert(true);
+ DLDbIdentifier defaultId = mSavedList.defaultDLDbIdentifier();
+
+ // Find the keychain object for the given ref
+ DLDbIdentifier dLDbIdentifier = keychain->dLDbIdentifier();
+
+ // Remove it from the saved list
+ mSavedList.remove(dLDbIdentifier);
+ if (dLDbIdentifier == defaultId)
+ changedDefault=true;
+
+ // Actually rename the database on disk.
+ keychain->database()->rename(newName);
+
+ newDLDbIdentifier = keychain->dLDbIdentifier();
+
+ // Now update the keychain map to use the newDLDbIdentifier
+ KeychainMap::iterator it = mKeychains.find(dLDbIdentifier);
+ if (it != mKeychains.end())
+ {
+ mKeychains.erase(it);
+ mKeychains.insert(KeychainMap::value_type(newDLDbIdentifier, keychain));
+ }
+
+ // If this was the default keychain change it accordingly
+ if (changedDefault)
+ mSavedList.defaultDLDbIdentifier(newDLDbIdentifier);
+
+ mSavedList.save();
+ }
+
+ // @@@ We need a kSecKeychainRenamedEvent so other clients can close this keychain and move on with life.
+ //KCEventNotifier::PostKeychainEvent(kSecKeychainRenamedEvent);
+
+ // 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)
+{
+ 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);
+ 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);
+}
+
void StorageManager::remove(const KeychainList &kcsToRemove, bool deleteDb)
{
bool unsetDefault = false;
{
StLock<Mutex> _(mLock);
mSavedList.revert(true);
- DLDbIdentifier defaultId = globals().defaultKeychain.dLDbIdentifier();
+ DLDbIdentifier defaultId = mSavedList.defaultDLDbIdentifier();
for (KeychainList::const_iterator ix = kcsToRemove.begin(); ix != kcsToRemove.end(); ++ix)
{
// Find the keychain object for the given ref
mKeychains.erase(it);
}
}
+
+ if (unsetDefault)
+ mSavedList.defaultDLDbIdentifier(DLDbIdentifier());
+
mSavedList.save();
- _doReload();
}
- // Make sure we are not holding mLock when we post this event.
+ // Make sure we are not holding mLock when we post these events.
KCEventNotifier::PostKeychainEvent(kSecKeychainListChangedEvent);
if (unsetDefault)
- {
- // Make sure we are not holding mLock when we call this since it posts an event.
- globals().defaultKeychain.unset();
- }
+ KCEventNotifier::PostKeychainEvent(kSecDefaultChangedEvent);
}
void
StorageManager::getSearchList(KeychainList &keychainList)
{
- // Make a copy of the searchList
StLock<Mutex> _(mLock);
- StorageManager::KeychainList searchList(mSearchList);
+ mSavedList.revert(false);
+ mCommonList.revert(false);
+
+ // Merge mSavedList and common list
+ DLDbList dLDbList = mSavedList.searchList();
+ DLDbList commonList = mCommonList.searchList();
+ KeychainList result;
+ result.reserve(dLDbList.size() + commonList.size());
+
+ for (DLDbList::const_iterator it = dLDbList.begin(); it != dLDbList.end(); ++it)
+ {
+ Keychain keychain(_keychain(*it));
+ result.push_back(keychain);
+ }
+
+ for (DLDbList::const_iterator it = commonList.begin(); it != commonList.end(); ++it)
+ {
+ Keychain keychain(_keychain(*it));
+ result.push_back(keychain);
+ }
- // Return the copy of the list.
- keychainList.swap(searchList);
+ keychainList.swap(result);
}
void
StorageManager::setSearchList(const KeychainList &keychainList)
{
- // Make a copy of the passed in searchList
- StorageManager::KeychainList keychains(keychainList);
+ 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.
+ StLock<Mutex> _(mLock);
+ mSavedList.revert(true);
+ mSavedList.searchList(searchList);
+ mSavedList.save();
+ }
- // Set the current searchlist to be what was passed in, the old list will be freed
- // upon exit of this stackframe.
+ 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)
+{
+ if (domain == mDomain)
+ {
+ StLock<Mutex> _(mLock);
+ mSavedList.revert(false);
+ convertList(keychainList, mSavedList.searchList());
+ }
+ else
+ {
+ convertList(keychainList, DLDbListCFPref(domain).searchList());
+ }
+}
+
+void
+StorageManager::setSearchList(SecPreferencesDomain domain, const KeychainList &keychainList)
+{
+ 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.
+ StLock<Mutex> _(mLock);
+ 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);
+ }
+ }
+ else
+ {
+ DLDbListCFPref(domain).searchList(searchList);
+ }
+}
+
+void
+StorageManager::domain(SecPreferencesDomain domain)
+{
StLock<Mutex> _(mLock);
- mSearchList.swap(keychains);
+ 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
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 if (typeID == gTypes().KeychainImpl.typeID)
+ keychainList.push_back(KeychainImpl::required(SecKeychainRef(keychainOrArray)));
else
MacOSError::throwMe(paramErr);
}
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)));
+ keychains[ix] = KeychainImpl::required(SecKeychainRef(CFArrayGetValueAtIndex(keychainArray, ix)));
}
keychainList.swap(keychains);
{
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);
+ SecKeychainRef keychainRef = (*ix)->handle();
CFArrayAppendValue(keychainArray, keychainRef);
CFRelease(keychainRef);
}
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)
+{
+ KeychainList result;
+ result.reserve(ids.size());
+ for (DLDbList::const_iterator ix = ids.begin(); ix != ids.end(); ++ix)
+ {
+ Keychain keychain(_keychain(*ix));
+ result.push_back(keychain);
+ }
+ kcs.swap(result);
+}
#pragma mark ÑÑÑÑ Login Functions ÑÑÑÑ
+void StorageManager::login(AuthorizationRef authRef, UInt32 nameLength, const char* name)
+{
+ AuthorizationItemSet* info = NULL;
+ OSStatus result = AuthorizationCopyInfo(authRef, NULL, &info); // get the results of the copy rights call.
+ Boolean created = false;
+ if ( result == noErr && 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, 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)
{
if ( name == NULL || password == NULL )
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)
-#if 0
- debug("KClogin", "setting up login session");
- if (OSStatus ssnErr = SessionCreate(sessionKeepCurrentBootstrap,
- sessionHasGraphicAccess | sessionHasTTY))
- debug("KClogin", "session setup failed status=%ld", ssnErr);
-#endif
+ x_debug("StorageManager::login: entered");
+ mSavedList.revert(true);
+ if (passwordLength != 0 && password == NULL)
+ {
+ x_debug("StorageManager::login: invalid argument (NULL password)");
+ MacOSError::throwMe(paramErr);
+ }
- if (name == NULL || (passwordLength != 0 && password == NULL))
- MacOSError::throwMe(paramErr);
+ DLDbIdentifier loginDLDbIdentifier(mSavedList.loginDLDbIdentifier());
+ x_debug1("StorageManager::login: loginDLDbIdentifier is %s", (loginDLDbIdentifier) ? loginDLDbIdentifier.dbName() : "<NULL>");
+ if (!loginDLDbIdentifier)
+ MacOSError::throwMe(errSecNoSuchKeychain);
- // Make sure name is zero terminated
- string theName(reinterpret_cast<const char *>(name), nameLength);
- Keychain keychain = make(theName.c_str());
+ Keychain theKeychain(keychain(loginDLDbIdentifier));
try
{
- keychain->unlock(CssmData(const_cast<void *>(password), passwordLength));
- debug("KClogin", "keychain unlock successful");
+ x_debug2("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));
+ x_debug("Login keychain unlocked successfully");
}
catch(const CssmError &e)
{
if (e.osStatus() != CSSMERR_DL_DATASTORE_DOESNOT_EXIST)
throw;
- debug("KClogin", "creating login keychain");
- keychain->create(passwordLength, password);
+ x_debug1("Creating login keychain %s", (loginDLDbIdentifier) ? loginDLDbIdentifier.dbName() : "<NULL>");
+ theKeychain->create(passwordLength, password);
+ x_debug("Login keychain created successfully");
+ // Set the prefs for this new login keychain.
+ loginKeychain(theKeychain);
// Login Keychain does not lock on sleep nor lock after timeout by default.
- keychain->setSettings(INT_MAX, false);
+ theKeychain->setSettings(INT_MAX, false);
}
-#if 0
- // @@@ Create a authorization credential for the current user.
- 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);
+ loginKeychain()->changePassphrase(oldPassword, newPassword);
+ secdebug("KClogin", "Changed login keychain password successfully");
}
void StorageManager::changeLoginPassword(UInt32 oldPasswordLength, const void *oldPassword, UInt32 newPasswordLength, const void *newPassword)
{
- globals().defaultKeychain.keychain()->changePassphrase(oldPasswordLength, oldPassword, newPasswordLength, newPassword);
+ 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)
+{
+ // Clear the keychain search list.
+ //
+ CFArrayRef emptySearchList = nil;
+ try
+ {
+ if ( resetSearchList )
+ {
+ emptySearchList = CFArrayCreate(NULL, NULL, 0, NULL);
+ StorageManager::KeychainList keychainList;
+ convertToKeychainList(emptySearchList, 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.
+ }
+ if ( emptySearchList )
+ CFRelease(emptySearchList);
}
#pragma mark ÑÑÑÑ File Related ÑÑÑÑ
Keychain StorageManager::make(const char *pathName)
+{
+ return make(pathName, true);
+}
+
+Keychain StorageManager::make(const char *pathName, bool add)
{
string fullPathName;
if ( pathName[0] == '/' )
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;
+ 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(paramErr);
+ homeDir = pw->pw_dir;
+ }
+ fullPathName = homeDir;
+ }
+ break;
+ case kSecPreferencesDomainSystem:
+ fullPathName = "";
+ break;
+ default:
+ assert(false); // invalid domain for this
}
- fullPathName = homeDir;
fullPathName += "/Library/Keychains/";
fullPathName += pathName;
}
const CSSM_VERSION *version = NULL;
uint32 subserviceId = 0;
CSSM_SERVICE_TYPE subserviceType = CSSM_SERVICE_DL | CSSM_SERVICE_CSP;
- const CssmSubserviceUid ssuid(gGuidAppleCSPDL, version,
+ const CssmSubserviceUid ssuid(gGuidAppleCSPDL, version,
subserviceId, subserviceType);
DLDbIdentifier dLDbIdentifier(ssuid, fullPathName.c_str(), DbLocation);
- return makeKeychain(dLDbIdentifier);
+ return makeKeychain(dLDbIdentifier, add);
}
-KeychainSchema
-StorageManager::keychainSchemaFor(const CssmClient::Db &db)
+Keychain StorageManager::makeLoginAuthUI(Item &item)
{
- // @@@ Locking
- KeychainSchema schema(db);
- pair<KeychainSchemaSet::iterator, bool> result = mKeychainSchemaSet.insert(db);
- if (result.second)
- return schema;
- return *result.first;
+ // 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 = noErr;
+ Keychain keychain = NULL; // We return this keychain.
+ //
+ // Set up the Auth ref to bring up UI.
+ //
+ AuthorizationRef authRef = NULL;
+ result = AuthorizationCreate(NULL, NULL, kAuthorizationFlagDefaults, &authRef);
+ if ( result != noErr )
+ MacOSError::throwMe(errAuthorizationInternal);
+ AuthorizationEnvironment envir;
+ envir.count = 5; // 5 hints are used.
+ AuthorizationItem* authEnvirItemArrayPtr = (AuthorizationItem*)malloc(sizeof(AuthorizationItem) * envir.count);
+ if ( !authEnvirItemArrayPtr )
+ {
+ if ( authRef )
+ AuthorizationFree(authRef, kAuthorizationFlagDefaults);
+ MacOSError::throwMe(errAuthorizationInternal);
+ }
+ envir.items = authEnvirItemArrayPtr;
+ AuthorizationItem* currItem = 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[255];
+ UInt32 actLen;
+ SecKeychainAttribute attr = { kSecAccountItemAttr, 255, &buff };
+ 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 a 'srvr' attr
+ {
+ if ( actLen > 255 )
+ buff[255] = 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*)globals().storageManager.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 = strlen(currDefaultName);
+ currItem->value = (void*)currDefaultName;
+ currItem->flags = 0;
+ currItem++;
+ }
+ catch(...)
+ {
+ envir.count--;
+ }
+
+ //
+ // 3rd Hint (optional): 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?
+ {
+ currItem->value = (void*)userName.c_str();
+ currItem->valueLength = userName.length();
+ }
+ else // trouble getting user name; can't continue...
+ {
+ if ( authRef )
+ AuthorizationFree(authRef, kAuthorizationFlagDefaults);
+ free(authEnvirItemArrayPtr);
+ MacOSError::throwMe(errAuthorizationInternal);
+ }
+ currItem->flags = 0;
+ //
+ // 5th Hint (optional) flags if user has more than 1 keychain (used for a later warning when reset to default).
+ //
+ currItem++; // last hint...
+ currItem->name = AGENT_HINT_LOGIN_KC_USER_HAS_OTHER_KCS_STR;
+ Boolean moreThanOneKCExists = false;
+ {
+ StLock<Mutex> _(mLock);
+ if (mSavedList.searchList().size() > 1)
+ moreThanOneKCExists = true;
+ }
+ currItem->value = &moreThanOneKCExists;
+ currItem->valueLength = sizeof(Boolean);
+ 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 };
+ result = AuthorizationCopyRights(authRef, &rights, &envir, kAuthorizationFlagDefaults | kAuthorizationFlagInteractionAllowed | kAuthorizationFlagExtendRights, NULL);
+ free(authEnvirItemArrayPtr); // done with the auth items.
+ if ( result == errAuthorizationSuccess ) // On success, revert to defaults.
+ {
+ try
+ {
+ resetKeychain(true); // Clears the plist, moves aside existing login.keychain
+ login(authRef, userName.length(), userName.c_str()); // Creates a login.keychain
+ keychain = loginKeychain(); // Return it.
+ defaultKeychain(keychain); // Set it to the default.
+ }
+ catch(...)
+ {
+ // Reset failed, login.keychain creation failed, or setting it to default.
+ // We need to release 'authRef'...
+ }
+ }
+ if ( authRef )
+ AuthorizationFree(authRef, kAuthorizationFlagDefaults);
+ if ( result )
+ MacOSError::throwMe(result); // Any other error means we don't return a keychain.
+ return keychain;
}
+Keychain StorageManager::defaultKeychainUI(Item &item)
+{
+ Keychain returnedKeychain = NULL;
+ try
+ {
+ returnedKeychain = globals().storageManager.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;
+}