X-Git-Url: https://git.saurik.com/apple/security.git/blobdiff_plain/5dd5f9ec28f304ca377c42fd7f711d6cf12b90e1..5c19dc3ae3bd8e40a9c028b0deddd50ff337692c:/Security/libsecurity_keychain/lib/Keychains.cpp?ds=inline diff --git a/Security/libsecurity_keychain/lib/Keychains.cpp b/Security/libsecurity_keychain/lib/Keychains.cpp deleted file mode 100644 index 1d94dee2..00000000 --- a/Security/libsecurity_keychain/lib/Keychains.cpp +++ /dev/null @@ -1,1332 +0,0 @@ -/* - * Copyright (c) 2000-2004,2011-2014 Apple Inc. All Rights Reserved. - * - * @APPLE_LICENSE_HEADER_START@ - * - * This file contains Original Code and/or Modifications of Original Code - * as defined in and that are subject to the Apple Public Source License - * Version 2.0 (the 'License'). You may not use this file except in - * compliance with the License. Please obtain a copy of the License at - * http://www.opensource.apple.com/apsl/ and read it before using this - * file. - * - * The Original Code and all software distributed under the License are - * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER - * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, - * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. - * Please see the License for the specific language governing rights and - * limitations under the License. - * - * @APPLE_LICENSE_HEADER_END@ - */ - - -// -// Keychains.cpp -// - -#include "KCEventNotifier.h" -#include "Keychains.h" - -#include "Item.h" -#include "KCCursor.h" -#include "Globals.h" -#include -#include -#include -#include -#include -#include - -#include "SecKeychainPriv.h" - -#include -#include -#include "DLDbListCFPref.h" -#include -#include -#include -#include -#include -#include -#include -#include - -static dispatch_once_t SecKeychainSystemKeychainChecked; - -OSStatus SecKeychainSystemKeychainCheckWouldDeadlock() -{ - dispatch_once(&SecKeychainSystemKeychainChecked, ^{}); - return errSecSuccess; -} - -using namespace KeychainCore; -using namespace CssmClient; - - -typedef struct EventItem -{ - SecKeychainEvent kcEvent; - Item item; -} EventItem; - -typedef std::list EventBufferSuper; -class EventBuffer : public EventBufferSuper -{ -public: - EventBuffer () {} - virtual ~EventBuffer (); -}; - - -EventBuffer::~EventBuffer () -{ -} - - - -// -// KeychainSchemaImpl -// -KeychainSchemaImpl::KeychainSchemaImpl(const Db &db) : mMutex(Mutex::recursive) -{ - DbCursor relations(db); - relations->recordType(CSSM_DL_DB_SCHEMA_INFO); - DbAttributes relationRecord(db, 1); - relationRecord.add(Schema::RelationID); - DbUniqueRecord outerUniqueId(db); - - while (relations->next(&relationRecord, NULL, outerUniqueId)) - { - DbUniqueRecord uniqueId(db); - - uint32 relationID = relationRecord.at(0); - if (CSSM_DB_RECORDTYPE_SCHEMA_START <= relationID - && relationID < CSSM_DB_RECORDTYPE_SCHEMA_END) - continue; - - // Create a cursor on the SCHEMA_ATTRIBUTES table for records with - // RelationID == relationID - DbCursor attributes(db); - attributes->recordType(CSSM_DL_DB_SCHEMA_ATTRIBUTES); - attributes->add(CSSM_DB_EQUAL, Schema::RelationID, relationID); - - // Set up a record for retriving the SCHEMA_ATTRIBUTES - DbAttributes attributeRecord(db, 2); - attributeRecord.add(Schema::AttributeFormat); - attributeRecord.add(Schema::AttributeID); - - RelationInfoMap &rim = mDatabaseInfoMap[relationID]; - while (attributes->next(&attributeRecord, NULL, uniqueId)) - rim[attributeRecord.at(1)] = attributeRecord.at(0); - - // Create a cursor on the CSSM_DL_DB_SCHEMA_INDEXES table for records - // with RelationID == relationID - DbCursor indexes(db); - indexes->recordType(CSSM_DL_DB_SCHEMA_INDEXES); - indexes->conjunctive(CSSM_DB_AND); - indexes->add(CSSM_DB_EQUAL, Schema::RelationID, relationID); - indexes->add(CSSM_DB_EQUAL, Schema::IndexType, - uint32(CSSM_DB_INDEX_UNIQUE)); - - // Set up a record for retriving the SCHEMA_INDEXES - DbAttributes indexRecord(db, 1); - indexRecord.add(Schema::AttributeID); - - CssmAutoDbRecordAttributeInfo &infos = - *new CssmAutoDbRecordAttributeInfo(); - mPrimaryKeyInfoMap. - insert(PrimaryKeyInfoMap::value_type(relationID, &infos)); - infos.DataRecordType = relationID; - while (indexes->next(&indexRecord, NULL, uniqueId)) - { - CssmDbAttributeInfo &info = infos.add(); - info.AttributeNameFormat = CSSM_DB_ATTRIBUTE_NAME_AS_INTEGER; - info.Label.AttributeID = indexRecord.at(0); - // @@@ Might insert bogus value if DB is corrupt - info.AttributeFormat = rim[info.Label.AttributeID]; - } - } -} - -KeychainSchemaImpl::~KeychainSchemaImpl() -{ - try - { - map::iterator it = mPrimaryKeyInfoMap.begin(); - while (it != mPrimaryKeyInfoMap.end()) - { - delete it->second; - it++; - } - // for_each_map_delete(mPrimaryKeyInfoMap.begin(), mPrimaryKeyInfoMap.end()); - } - catch(...) - { - } -} - -const KeychainSchemaImpl::RelationInfoMap & -KeychainSchemaImpl::relationInfoMapFor(CSSM_DB_RECORDTYPE recordType) const -{ - DatabaseInfoMap::const_iterator dit = mDatabaseInfoMap.find(recordType); - if (dit == mDatabaseInfoMap.end()) - MacOSError::throwMe(errSecNoSuchClass); - return dit->second; -} - -bool KeychainSchemaImpl::hasRecordType (CSSM_DB_RECORDTYPE recordType) const -{ - DatabaseInfoMap::const_iterator it = mDatabaseInfoMap.find(recordType); - return it != mDatabaseInfoMap.end(); -} - -bool -KeychainSchemaImpl::hasAttribute(CSSM_DB_RECORDTYPE recordType, uint32 attributeId) const -{ - try - { - const RelationInfoMap &rmap = relationInfoMapFor(recordType); - RelationInfoMap::const_iterator rit = rmap.find(attributeId); - return rit != rmap.end(); - } - catch (MacOSError result) - { - if (result.osStatus () == errSecNoSuchClass) - { - return false; - } - else - { - throw; - } - } -} - -CSSM_DB_ATTRIBUTE_FORMAT -KeychainSchemaImpl::attributeFormatFor(CSSM_DB_RECORDTYPE recordType, uint32 attributeId) const -{ - const RelationInfoMap &rmap = relationInfoMapFor(recordType); - RelationInfoMap::const_iterator rit = rmap.find(attributeId); - if (rit == rmap.end()) - MacOSError::throwMe(errSecNoSuchAttr); - - return rit->second; -} - -CssmDbAttributeInfo -KeychainSchemaImpl::attributeInfoFor(CSSM_DB_RECORDTYPE recordType, uint32 attributeId) const -{ - CSSM_DB_ATTRIBUTE_INFO info; - info.AttributeFormat = attributeFormatFor(recordType, attributeId); - info.AttributeNameFormat = CSSM_DB_ATTRIBUTE_NAME_AS_INTEGER; - info.Label.AttributeID = attributeId; - - return info; -} - -void -KeychainSchemaImpl::getAttributeInfoForRecordType(CSSM_DB_RECORDTYPE recordType, SecKeychainAttributeInfo **Info) const -{ - const RelationInfoMap &rmap = relationInfoMapFor(recordType); - - SecKeychainAttributeInfo *theList=reinterpret_cast(malloc(sizeof(SecKeychainAttributeInfo))); - - size_t capacity=rmap.size(); - UInt32 *tagBuf=reinterpret_cast(malloc(capacity*sizeof(UInt32))); - UInt32 *formatBuf=reinterpret_cast(malloc(capacity*sizeof(UInt32))); - UInt32 i=0; - - - for (RelationInfoMap::const_iterator rit = rmap.begin(); rit != rmap.end(); ++rit) - { - if (i>=capacity) - { - capacity *= 2; - if (capacity <= i) capacity = i + 1; - tagBuf=reinterpret_cast(realloc(tagBuf, (capacity*sizeof(UInt32)))); - formatBuf=reinterpret_cast(realloc(tagBuf, (capacity*sizeof(UInt32)))); - } - tagBuf[i]=rit->first; - formatBuf[i++]=rit->second; - } - - theList->count=i; - theList->tag=tagBuf; - theList->format=formatBuf; - *Info=theList; -} - - -const CssmAutoDbRecordAttributeInfo & -KeychainSchemaImpl::primaryKeyInfosFor(CSSM_DB_RECORDTYPE recordType) const -{ - PrimaryKeyInfoMap::const_iterator it; - it = mPrimaryKeyInfoMap.find(recordType); - - if (it == mPrimaryKeyInfoMap.end()) - MacOSError::throwMe(errSecNoSuchClass); // @@@ Not really but whatever. - - return *it->second; -} - -bool -KeychainSchemaImpl::operator <(const KeychainSchemaImpl &other) const -{ - return mDatabaseInfoMap < other.mDatabaseInfoMap; -} - -bool -KeychainSchemaImpl::operator ==(const KeychainSchemaImpl &other) const -{ - return mDatabaseInfoMap == other.mDatabaseInfoMap; -} - -void -KeychainSchemaImpl::didCreateRelation(CSSM_DB_RECORDTYPE relationID, - const char *inRelationName, - uint32 inNumberOfAttributes, - const CSSM_DB_SCHEMA_ATTRIBUTE_INFO *pAttributeInfo, - uint32 inNumberOfIndexes, - const CSSM_DB_SCHEMA_INDEX_INFO *pIndexInfo) -{ - StLock_(mMutex); - - if (CSSM_DB_RECORDTYPE_SCHEMA_START <= relationID - && relationID < CSSM_DB_RECORDTYPE_SCHEMA_END) - return; - - // if our schema is already in the map, return - if (mPrimaryKeyInfoMap.find(relationID) != mPrimaryKeyInfoMap.end()) - { - return; - } - - RelationInfoMap &rim = mDatabaseInfoMap[relationID]; - for (uint32 ix = 0; ix < inNumberOfAttributes; ++ix) - rim[pAttributeInfo[ix].AttributeId] = pAttributeInfo[ix].DataType; - - CssmAutoDbRecordAttributeInfo *infos = new CssmAutoDbRecordAttributeInfo(); - - mPrimaryKeyInfoMap. - insert(PrimaryKeyInfoMap::value_type(relationID, infos)); - infos->DataRecordType = relationID; - for (uint32 ix = 0; ix < inNumberOfIndexes; ++ix) - if (pIndexInfo[ix].IndexType == CSSM_DB_INDEX_UNIQUE) - { - CssmDbAttributeInfo &info = infos->add(); - info.AttributeNameFormat = CSSM_DB_ATTRIBUTE_NAME_AS_INTEGER; - info.Label.AttributeID = pIndexInfo[ix].AttributeId; - info.AttributeFormat = rim[info.Label.AttributeID]; - } -} - - - -KeychainSchema::~KeychainSchema() - -{ -} - - - -struct Event -{ - SecKeychainEvent eventCode; - PrimaryKey primaryKey; -}; -typedef std::list EventList; - -#define SYSTEM_KEYCHAIN_CHECK_UNIX_BASE_NAME "/var/run/systemkeychaincheck" -#define SYSTEM_KEYCHAIN_CHECK_UNIX_DOMAIN_SOCKET_NAME (SYSTEM_KEYCHAIN_CHECK_UNIX_BASE_NAME ".socket") -#define SYSTEM_KEYCHAIN_CHECK_COMPLETE_FILE_NAME (SYSTEM_KEYCHAIN_CHECK_UNIX_BASE_NAME ".done") - -static void check_system_keychain() -{ - // sadly we can't use XPC here, XPC_DOMAIN_TYPE_SYSTEM doesn't exist yet. Also xpc-helper uses the - // keychain API (I assume for checking codesign things). So we use Unix Domain Sockets. - - // NOTE: if we hit a system error we attempt to log it, and then just don't check the system keychain. - // In theory a system might be able to recover from this state if we let it try to muddle along, and - // past behaviour didn't even try this hard to do the keychain check. In particular we could be in a - // sandbox'ed process. So we just do our best and let another process try again. - - struct stat keycheck_file_info; - if (stat(SYSTEM_KEYCHAIN_CHECK_COMPLETE_FILE_NAME, &keycheck_file_info) < 0) { - int server_fd = socket(PF_UNIX, SOCK_STREAM, 0); - if (server_fd < 0) { - syslog(LOG_ERR, "Can't get socket (%m) system keychain may be unchecked"); - return; - } - - struct sockaddr_un keychain_check_server_address; - keychain_check_server_address.sun_family = AF_UNIX; - if (strlcpy(keychain_check_server_address.sun_path, SYSTEM_KEYCHAIN_CHECK_UNIX_DOMAIN_SOCKET_NAME, sizeof(keychain_check_server_address.sun_path)) > sizeof(keychain_check_server_address.sun_path)) { - // It would be nice if we could compile time assert this - syslog(LOG_ERR, "Socket path too long, max length %lu, your length %lu", (unsigned long)sizeof(keychain_check_server_address.sun_path), (unsigned long)strlen(SYSTEM_KEYCHAIN_CHECK_UNIX_DOMAIN_SOCKET_NAME)); - close(server_fd); - return; - } - keychain_check_server_address.sun_len = SUN_LEN(&keychain_check_server_address); - - int rc = connect(server_fd, (struct sockaddr *)&keychain_check_server_address, keychain_check_server_address.sun_len); - if (rc < 0) { - syslog(LOG_ERR, "Can not connect to %s: %m", SYSTEM_KEYCHAIN_CHECK_UNIX_DOMAIN_SOCKET_NAME); - close(server_fd); - return; - } - - // this read lets us block until the EOF comes, we don't ever get a byte (and if we do, we don't care about it) - char byte; - ssize_t read_size = read(server_fd, &byte, 1); - if (read_size < 0) { - syslog(LOG_ERR, "Error reading from system keychain checker: %m"); - } - - close(server_fd); - return; - } -} - -// -// KeychainImpl -// -KeychainImpl::KeychainImpl(const Db &db) - : mInCache(false), mDb(db), mCustomUnlockCreds (this), mIsInBatchMode (false), mMutex(Mutex::recursive) -{ - dispatch_once(&SecKeychainSystemKeychainChecked, ^{ - check_system_keychain(); - }); - mDb->defaultCredentials(this); // install activation hook - mEventBuffer = new EventBuffer; -} - -KeychainImpl::~KeychainImpl() -{ - try - { - // Remove ourselves from the cache if we are in it. - // fprintf(stderr, "Removing %p from storage manager cache.\n", handle(false)); - globals().storageManager.removeKeychain(dlDbIdentifier(), this); - delete mEventBuffer; - } - catch(...) - { - } -} - -Mutex* -KeychainImpl::getMutexForObject() -{ - return globals().storageManager.getStorageManagerMutex(); -} - -Mutex* -KeychainImpl::getKeychainMutex() -{ - return &mMutex; -} - -void KeychainImpl::aboutToDestruct() -{ - // remove me from the global cache, we are done - // fprintf(stderr, "Destructing keychain object\n"); - DLDbIdentifier identifier = dlDbIdentifier(); - globals().storageManager.removeKeychain(identifier, this); -} - -bool -KeychainImpl::operator ==(const KeychainImpl &keychain) const -{ - return dlDbIdentifier() == keychain.dlDbIdentifier(); -} - -KCCursor -KeychainImpl::createCursor(SecItemClass itemClass, const SecKeychainAttributeList *attrList) -{ - StLock_(mMutex); - - StorageManager::KeychainList keychains; - keychains.push_back(Keychain(this)); - return KCCursor(keychains, itemClass, attrList); -} - -KCCursor -KeychainImpl::createCursor(const SecKeychainAttributeList *attrList) -{ - StLock_(mMutex); - - StorageManager::KeychainList keychains; - keychains.push_back(Keychain(this)); - return KCCursor(keychains, attrList); -} - -void -KeychainImpl::create(UInt32 passwordLength, const void *inPassword) -{ - StLock_(mMutex); - - if (!inPassword) - { - create(); - return; - } - - Allocator &alloc = Allocator::standard(); - - // @@@ Share this instance - - const CssmData password(const_cast(inPassword), passwordLength); - AclFactory::PasswordChangeCredentials pCreds (password, alloc); - AclFactory::AnyResourceContext rcc(pCreds); - create(&rcc); -} - -void KeychainImpl::create(ConstStringPtr inPassword) -{ - StLock_(mMutex); - - if ( inPassword ) - create(static_cast(inPassword[0]), &inPassword[1]); - else - create(); -} - -void -KeychainImpl::create() -{ - StLock_(mMutex); - - AclFactory aclFactory; - AclFactory::AnyResourceContext rcc(aclFactory.unlockCred()); - create(&rcc); -} - -void KeychainImpl::createWithBlob(CssmData &blob) -{ - StLock_(mMutex); - - mDb->dbInfo(&Schema::DBInfo); - AclFactory aclFactory; - AclFactory::AnyResourceContext rcc(aclFactory.unlockCred()); - mDb->resourceControlContext (&rcc); - try - { - mDb->createWithBlob(blob); - } - catch (...) - { - mDb->resourceControlContext(NULL); - mDb->dbInfo(NULL); - throw; - } - mDb->resourceControlContext(NULL); - mDb->dbInfo(NULL); // Clear the schema (to not break an open call later) - globals().storageManager.created(Keychain(this)); - - KCEventNotifier::PostKeychainEvent (kSecKeychainListChangedEvent, this, NULL); -} - -void -KeychainImpl::create(const ResourceControlContext *rcc) -{ - StLock_(mMutex); - - mDb->dbInfo(&Schema::DBInfo); // Set the schema (to force a create) - mDb->resourceControlContext(rcc); - try - { - mDb->create(); - } - catch (...) - { - mDb->resourceControlContext(NULL); - mDb->dbInfo(NULL); // Clear the schema (to not break an open call later) - throw; - } - mDb->resourceControlContext(NULL); - mDb->dbInfo(NULL); // Clear the schema (to not break an open call later) - globals().storageManager.created(Keychain(this)); -} - -void -KeychainImpl::open() -{ - StLock_(mMutex); - - mDb->open(); -} - -void -KeychainImpl::lock() -{ - StLock_(mMutex); - - mDb->lock(); -} - -void -KeychainImpl::unlock() -{ - StLock_(mMutex); - - mDb->unlock(); -} - -void -KeychainImpl::unlock(const CssmData &password) -{ - StLock_(mMutex); - - mDb->unlock(password); -} - -void -KeychainImpl::unlock(ConstStringPtr password) -{ - StLock_(mMutex); - - if (password) - { - const CssmData data(const_cast(&password[1]), password[0]); - unlock(data); - } - else - unlock(); -} - -void -KeychainImpl::stash() -{ - StLock_(mMutex); - - mDb->stash(); -} - -void -KeychainImpl::stashCheck() -{ - StLock_(mMutex); - - mDb->stashCheck(); -} - -void -KeychainImpl::getSettings(uint32 &outIdleTimeOut, bool &outLockOnSleep) -{ - StLock_(mMutex); - - mDb->getSettings(outIdleTimeOut, outLockOnSleep); -} - -void -KeychainImpl::setSettings(uint32 inIdleTimeOut, bool inLockOnSleep) -{ - StLock_(mMutex); - - // The .Mac syncing code only makes sense for the AppleFile CSP/DL, - // but other DLs such as the OCSP and LDAP DLs do not expose a way to - // change settings or the password. To make a minimal change that only affects - // the smartcard case, we only look for that CSP/DL - - bool isSmartcard = (mDb->dl()->guid() == gGuidAppleSdCSPDL); - - // get the old keychain blob so that we can tell .Mac to resync it - CssmAutoData oldBlob(mDb ->allocator()); - if (!isSmartcard) - mDb->copyBlob(oldBlob.get()); - - mDb->setSettings(inIdleTimeOut, inLockOnSleep); -} - -void -KeychainImpl::changePassphrase(UInt32 oldPasswordLength, const void *oldPassword, - UInt32 newPasswordLength, const void *newPassword) -{ - StLock_(mMutex); - - bool isSmartcard = (mDb->dl()->guid() == gGuidAppleSdCSPDL); - - TrackingAllocator allocator(Allocator::standard()); - AutoCredentials cred = AutoCredentials(allocator); - if (oldPassword) - { - const CssmData &oldPass = *new(allocator) CssmData(const_cast(oldPassword), oldPasswordLength); - TypedList &oldList = *new(allocator) TypedList(allocator, CSSM_SAMPLE_TYPE_KEYCHAIN_LOCK); - oldList.append(new(allocator) ListElement(CSSM_SAMPLE_TYPE_PASSWORD)); - oldList.append(new(allocator) ListElement(oldPass)); - cred += oldList; - } - - if (newPassword) - { - const CssmData &newPass = *new(allocator) CssmData(const_cast(newPassword), newPasswordLength); - TypedList &newList = *new(allocator) TypedList(allocator, CSSM_SAMPLE_TYPE_KEYCHAIN_CHANGE_LOCK); - newList.append(new(allocator) ListElement(CSSM_SAMPLE_TYPE_PASSWORD)); - newList.append(new(allocator) ListElement(newPass)); - cred += newList; - } - - // get the old keychain blob so that we can tell .Mac to resync it - CssmAutoData oldBlob(mDb->allocator()); - if (!isSmartcard) - mDb->copyBlob(oldBlob.get()); - - mDb->changePassphrase(&cred); -} - -void -KeychainImpl::changePassphrase(ConstStringPtr oldPassword, ConstStringPtr newPassword) -{ - StLock_(mMutex); - - const void *oldPtr, *newPtr; - UInt32 oldLen, newLen; - if (oldPassword) - { - oldLen = oldPassword[0]; - oldPtr = oldPassword + 1; - } - else - { - oldLen = 0; - oldPtr = NULL; - } - - if (newPassword) - { - newLen = newPassword[0]; - newPtr = newPassword + 1; - } - else - { - newLen = 0; - newPtr = NULL; - } - - changePassphrase(oldLen, oldPtr, newLen, newPtr); -} - -void -KeychainImpl::authenticate(const CSSM_ACCESS_CREDENTIALS *cred) -{ - StLock_(mMutex); - - if (!exists()) - MacOSError::throwMe(errSecNoSuchKeychain); - - MacOSError::throwMe(errSecUnimplemented); -} - -UInt32 -KeychainImpl::status() const -{ - // @@@ We should figure out the read/write status though a DL passthrough - // or some other way. Also should locked be unlocked read only or just - // read-only? - return (mDb->isLocked() ? 0 : kSecUnlockStateStatus | kSecWritePermStatus) - | kSecReadPermStatus; -} - -bool -KeychainImpl::exists() -{ - StLock_(mMutex); - - bool exists = true; - try - { - open(); - // Ok to leave the mDb open since it will get closed when it goes away. - } - catch (const CssmError &e) - { - if (e.osStatus() != CSSMERR_DL_DATASTORE_DOESNOT_EXIST) - throw; - exists = false; - } - - return exists; -} - -bool -KeychainImpl::isActive() const -{ - return mDb->isActive(); -} - -void KeychainImpl::completeAdd(Item &inItem, PrimaryKey &primaryKey) -{ - - - // The inItem shouldn't be in the cache yet - assert(!inItem->inCache()); - - // Insert inItem into mDbItemMap with key primaryKey. p.second will be - // true if it got inserted. If not p.second will be false and p.first - // will point to the current entry with key primaryKey. - pair p = - mDbItemMap.insert(DbItemMap::value_type(primaryKey, inItem.get())); - if (!p.second) - { - // There was already an ItemImpl * in mDbItemMap with key - // primaryKey. Get a ref to the pointer to it so we can assign a - // new value to it below. - ItemImpl *oldItem = p.first->second; - - // @@@ If this happens we are breaking our API contract of - // uniquifying items. We really need to insert the item into the - // map before we start the add. And have the item be in an - // "is being added" state. - assert(oldItem->inCache()); - secdebug("keychain", "add of new item %p somehow replaced %p", - inItem.get(), oldItem); - - // make sure that we both mark the item and remove the item from the cache - removeItem(oldItem->primaryKey(), oldItem); - oldItem = inItem.get(); - } - - inItem->inCache(true); -} - -void -KeychainImpl::addCopy(Item &inItem) -{ - Keychain keychain(this); - PrimaryKey primaryKey = inItem->addWithCopyInfo(keychain, true); - completeAdd(inItem, primaryKey); - postEvent(kSecAddEvent, inItem); -} - -void -KeychainImpl::add(Item &inItem) -{ - Keychain keychain(this); - PrimaryKey primaryKey = inItem->add(keychain); - completeAdd(inItem, primaryKey); - postEvent(kSecAddEvent, inItem); -} - -void -KeychainImpl::didUpdate(const Item &inItem, PrimaryKey &oldPK, - PrimaryKey &newPK) -{ - // If the primary key hasn't changed we don't need to update mDbItemMap. - if (oldPK != newPK) - { - // If inItem isn't in the cache we don't need to update mDbItemMap. - assert(inItem->inCache()); - if (inItem->inCache()) - { - // First remove the entry for inItem in mDbItemMap with key oldPK. - DbItemMap::iterator it = mDbItemMap.find(oldPK); - if (it != mDbItemMap.end() && (ItemImpl*) it->second == inItem.get()) - mDbItemMap.erase(it); - - // Insert inItem into mDbItemMap with key newPK. p.second will be - // true if it got inserted. If not p.second will be false and - // p.first will point to the current entry with key newPK. - pair p = - mDbItemMap.insert(DbItemMap::value_type(newPK, inItem.get())); - if (!p.second) - { - // There was already an ItemImpl * in mDbItemMap with key - // primaryKey. Get a ref to the pointer to it so we can assign - // a new value to it below. - ItemImpl *oldItem = p.first->second; - - // @@@ If this happens we are breaking our API contract of - // uniquifying items. We really need to insert the item into - // the map with the new primary key before we start the update. - // And have the item be in an "is being updated" state. - assert(oldItem->inCache()); - secdebug("keychain", "update of item %p somehow replaced %p", - inItem.get(), oldItem); - oldItem->inCache(false); - oldItem = inItem.get(); - } - } - } - - postEvent(kSecUpdateEvent, inItem); -} - -void -KeychainImpl::deleteItem(Item &inoutItem) -{ - { - // We don't need to hold the DO mutex through event posting, and, in fact, doing so causes deadlock. - // Hold it only as long as needed, instead. - - - // item must be persistent - if (!inoutItem->isPersistent()) - MacOSError::throwMe(errSecInvalidItemRef); - - DbUniqueRecord uniqueId = inoutItem->dbUniqueRecord(); - PrimaryKey primaryKey = inoutItem->primaryKey(); - uniqueId->deleteRecord(); - - // Don't remove the item from the mDbItemMap here since this would cause - // us to report a new item to our caller when we receive the - // kSecDeleteEvent notification. - // It will be removed before we post the notification, because - // CCallbackMgr will call didDeleteItem() - - // Post the notification for the item deletion with - // the primaryKey obtained when the item still existed - } - - postEvent(kSecDeleteEvent, inoutItem); -} - - -CssmClient::CSP -KeychainImpl::csp() -{ - StLock_(mMutex); - - if (!mDb->dl()->subserviceMask() & CSSM_SERVICE_CSP) - MacOSError::throwMe(errSecInvalidKeychain); - - // Try to cast first to a CSPDL to handle case where we don't have an SSDb - try - { - CssmClient::CSPDL cspdl(dynamic_cast(&*mDb->dl())); - return CSP(cspdl); - } - catch (...) - { - SSDbImpl* impl = dynamic_cast(&(*mDb)); - if (impl == NULL) - { - CssmError::throwMe(CSSMERR_CSSM_INVALID_POINTER); - } - - SSDb ssDb(impl); - return ssDb->csp(); - } -} - -PrimaryKey -KeychainImpl::makePrimaryKey(CSSM_DB_RECORDTYPE recordType, DbUniqueRecord &uniqueId) -{ - StLock_(mMutex); - - DbAttributes primaryKeyAttrs(uniqueId->database()); - primaryKeyAttrs.recordType(recordType); - gatherPrimaryKeyAttributes(primaryKeyAttrs); - uniqueId->get(&primaryKeyAttrs, NULL); - return PrimaryKey(primaryKeyAttrs); -} - -const CssmAutoDbRecordAttributeInfo & -KeychainImpl::primaryKeyInfosFor(CSSM_DB_RECORDTYPE recordType) -{ - StLock_(mMutex); - - try - { - return keychainSchema()->primaryKeyInfosFor(recordType); - } - catch (const CommonError &error) - { - switch (error.osStatus()) - { - case errSecNoSuchClass: - case CSSMERR_DL_INVALID_RECORDTYPE: - resetSchema(); - return keychainSchema()->primaryKeyInfosFor(recordType); - default: - throw; - } - } -} - -void KeychainImpl::gatherPrimaryKeyAttributes(DbAttributes& primaryKeyAttrs) -{ - StLock_(mMutex); - - const CssmAutoDbRecordAttributeInfo &infos = - primaryKeyInfosFor(primaryKeyAttrs.recordType()); - - // @@@ fix this to not copy info. - for (uint32 i = 0; i < infos.size(); i++) - primaryKeyAttrs.add(infos.at(i)); -} - -ItemImpl * -KeychainImpl::_lookupItem(const PrimaryKey &primaryKey) -{ - DbItemMap::iterator it = mDbItemMap.find(primaryKey); - if (it != mDbItemMap.end()) - { - if (it->second == NULL) - { - // we've been weak released... - mDbItemMap.erase(it); - } - else - { - return it->second; - } - } - - return NULL; -} - -Item -KeychainImpl::item(const PrimaryKey &primaryKey) -{ - StLock_(mMutex); - - // Lookup the item in the map while holding the apiLock. - ItemImpl *itemImpl = _lookupItem(primaryKey); - if (itemImpl) - return Item(itemImpl); - - try - { - // We didn't find it so create a new item with just a keychain and - // a primary key. However since we aren't holding - // globals().apiLock anymore some other thread might have beaten - // us to creating this item and adding it to the cache. If that - // happens we retry the lookup. - return Item(this, primaryKey); - } - catch (const MacOSError &e) - { - // If the item creation failed because some other thread already - // inserted this item into the cache we retry the lookup. - if (e.osStatus() == errSecDuplicateItem) - { - // Lookup the item in the map while holding the apiLock. - ItemImpl *itemImpl = _lookupItem(primaryKey); - if (itemImpl) - return Item(itemImpl); - } - throw; - } -} - - -Item -KeychainImpl::item(CSSM_DB_RECORDTYPE recordType, DbUniqueRecord &uniqueId) -{ - StLock_(mMutex); - - PrimaryKey primaryKey = makePrimaryKey(recordType, uniqueId); - { - // Lookup the item in the map while holding the apiLock. - ItemImpl *itemImpl = _lookupItem(primaryKey); - - if (itemImpl) - { - return Item(itemImpl); - } - } - - try - { - // We didn't find it so create a new item with a keychain, a primary key - // and a DbUniqueRecord. However since we aren't holding - // globals().apiLock anymore some other thread might have beaten - // us to creating this item and adding it to the cache. If that - // happens we retry the lookup. - return Item(this, primaryKey, uniqueId); - } - catch (const MacOSError &e) - { - // If the item creation failed because some other thread already - // inserted this item into the cache we retry the lookup. - if (e.osStatus() == errSecDuplicateItem) - { - // Lookup the item in the map while holding the apiLock. - ItemImpl *itemImpl = _lookupItem(primaryKey); - if (itemImpl) - return Item(itemImpl); - } - throw; - } -} - -KeychainSchema -KeychainImpl::keychainSchema() -{ - StLock_(mMutex); - if (!mKeychainSchema) - mKeychainSchema = KeychainSchema(mDb); - - return mKeychainSchema; -} - -void KeychainImpl::resetSchema() -{ - mKeychainSchema = NULL; // re-fetch it from db next time -} - - -// Called from DbItemImpl's constructor (so it is only partially constructed), -// add it to the map. -void -KeychainImpl::addItem(const PrimaryKey &primaryKey, ItemImpl *dbItemImpl) -{ - StLock_(mMutex); - - // The dbItemImpl shouldn't be in the cache yet - assert(!dbItemImpl->inCache()); - - // Insert dbItemImpl into mDbItemMap with key primaryKey. p.second will - // be true if it got inserted. If not p.second will be false and p.first - // will point to the current entry with key primaryKey. - pair p = - mDbItemMap.insert(DbItemMap::value_type(primaryKey, dbItemImpl)); - - if (!p.second) - { - // There was already an ItemImpl * in mDbItemMap with key primaryKey. - // There is a race condition here when being called in multiple threads - // We might have added an item using add and received a notification at - // the same time. - MacOSError::throwMe(errSecDuplicateItem); - } - - dbItemImpl->inCache(true); -} - -void -KeychainImpl::didDeleteItem(ItemImpl *inItemImpl) -{ - StLock_(mMutex); - - // Called by CCallbackMgr - secdebug("kcnotify", "%p notified that item %p was deleted", this, inItemImpl); - removeItem(inItemImpl->primaryKey(), inItemImpl); -} - -void -KeychainImpl::removeItem(const PrimaryKey &primaryKey, ItemImpl *inItemImpl) -{ - StLock_(mMutex); - - // If inItemImpl isn't in the cache to begin with we are done. - if (!inItemImpl->inCache()) - return; - - DbItemMap::iterator it = mDbItemMap.find(primaryKey); - if (it != mDbItemMap.end() && (ItemImpl*) it->second == inItemImpl) - mDbItemMap.erase(it); - - inItemImpl->inCache(false); -} - -void -KeychainImpl::getAttributeInfoForItemID(CSSM_DB_RECORDTYPE itemID, - SecKeychainAttributeInfo **Info) -{ - StLock_(mMutex); - - try - { - keychainSchema()->getAttributeInfoForRecordType(itemID, Info); - } - catch (const CommonError &error) - { - switch (error.osStatus()) - { - case errSecNoSuchClass: - case CSSMERR_DL_INVALID_RECORDTYPE: - resetSchema(); - keychainSchema()->getAttributeInfoForRecordType(itemID, Info); - default: - throw; - } - } -} - -void -KeychainImpl::freeAttributeInfo(SecKeychainAttributeInfo *Info) -{ - free(Info->tag); - free(Info->format); - free(Info); -} - -CssmDbAttributeInfo -KeychainImpl::attributeInfoFor(CSSM_DB_RECORDTYPE recordType, UInt32 tag) -{ - StLock_(mMutex); - - try - { - return keychainSchema()->attributeInfoFor(recordType, tag); - } - catch (const CommonError &error) - { - switch (error.osStatus()) - { - case errSecNoSuchClass: - case CSSMERR_DL_INVALID_RECORDTYPE: - resetSchema(); - return keychainSchema()->attributeInfoFor(recordType, tag); - default: - throw; - } - } -} - -void -KeychainImpl::recode(const CssmData &data, const CssmData &extraData) -{ - StLock_(mMutex); - - mDb->recode(data, extraData); -} - -void -KeychainImpl::copyBlob(CssmData &data) -{ - StLock_(mMutex); - - mDb->copyBlob(data); -} - -void -KeychainImpl::setBatchMode(Boolean mode, Boolean rollback) -{ - StLock_(mMutex); - - mDb->setBatchMode(mode, rollback); - mIsInBatchMode = mode; - if (!mode) - { - if (!rollback) // was batch mode being turned off without an abort? - { - // dump the buffer - EventBuffer::iterator it = mEventBuffer->begin(); - while (it != mEventBuffer->end()) - { - PrimaryKey primaryKey; - if (it->item) - { - primaryKey = it->item->primaryKey(); - } - - KCEventNotifier::PostKeychainEvent(it->kcEvent, mDb->dlDbIdentifier(), primaryKey); - - ++it; - } - - } - - // notify that a keychain has changed in too many ways to count - KCEventNotifier::PostKeychainEvent(kSecKeychainLeftBatchModeEvent); - mEventBuffer->clear(); - } - else - { - KCEventNotifier::PostKeychainEvent(kSecKeychainEnteredBatchModeEvent); - } -} - -void -KeychainImpl::postEvent(SecKeychainEvent kcEvent, ItemImpl* item) -{ - PrimaryKey primaryKey; - - { - StLock_(mMutex); - - if (item != NULL) - { - primaryKey = item->primaryKey(); - } - } - - if (!mIsInBatchMode) - { - KCEventNotifier::PostKeychainEvent(kcEvent, mDb->dlDbIdentifier(), primaryKey); - } - else - { - StLock_(mMutex); - - EventItem it; - it.kcEvent = kcEvent; - if (item != NULL) - { - it.item = item; - } - - mEventBuffer->push_back (it); - } -} - -Keychain::Keychain() -{ - dispatch_once(&SecKeychainSystemKeychainChecked, ^{ - check_system_keychain(); - }); -} - -Keychain::~Keychain() -{ -} - - - -Keychain -Keychain::optional(SecKeychainRef handle) -{ - if (handle) - return KeychainImpl::required(handle); - else - return globals().storageManager.defaultKeychain(); -} - - -CFIndex KeychainCore::GetKeychainRetainCount(Keychain& kc) -{ - CFTypeRef ref = kc->handle(false); - return CFGetRetainCount(ref); -} - - -// -// Create default credentials for this keychain. -// This is triggered upon default open (i.e. a Db::activate() with no set credentials). -// -// This function embodies the "default credentials" logic for Keychain-layer databases. -// -const AccessCredentials * -KeychainImpl::makeCredentials() -{ - return defaultCredentials(); -} - - -const AccessCredentials * -KeychainImpl::defaultCredentials() -{ - StLock_(mMutex); - - // Use custom unlock credentials for file keychains which have a referral - // record and the standard credentials for all others. - - if (mDb->dl()->guid() == gGuidAppleCSPDL && mCustomUnlockCreds(mDb)) - return &mCustomUnlockCreds; - else - if (mDb->dl()->guid() == gGuidAppleSdCSPDL) - return globals().smartcardCredentials(); - else - return globals().keychainCredentials(); -} - - - -bool KeychainImpl::mayDelete() -{ - return true; -}