+++ /dev/null
-/*
- * 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.
- */
-
-
-//
-// Keychains.cpp
-//
-
-#include "Keychains.h"
-#include "KCEventNotifier.h"
-
-#include "Item.h"
-#include "KCCursor.h"
-#include "Globals.h"
-#include "Schema.h"
-#include <Security/keychainacl.h>
-#include <Security/cssmacl.h>
-#include <CoreServices/../Frameworks/CarbonCore.framework/Headers/MacErrors.h>
-#include <Security/cssmdb.h>
-#include <Security/trackingallocator.h>
-#include <Security/SecCFTypes.h>
-
-using namespace KeychainCore;
-using namespace CssmClient;
-
-
-//
-// KeychainSchemaImpl
-//
-KeychainSchemaImpl::KeychainSchemaImpl(const Db &db)
-{
- 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);
- attributeRecord.add(Schema::AttributeNameFormat);
-
-
- RelationInfoMap &rim = mDatabaseInfoMap[relationID];
- while (attributes->next(&attributeRecord, NULL, uniqueId))
- {
- // @@@ this if statement was blocking tags of different naming conventions
- //if(CSSM_DB_ATTRIBUTE_FORMAT(attributeRecord.at(2))==CSSM_DB_ATTRIBUTE_NAME_AS_INTEGER)
- 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);
- info.AttributeFormat = rim[info.Label.AttributeID]; // @@@ Might insert bogus value if DB is corrupt
- }
- }
-}
-
-KeychainSchemaImpl::~KeychainSchemaImpl()
-{
- for_each_map_delete(mPrimaryKeyInfoMap.begin(), mPrimaryKeyInfoMap.end());
-}
-
-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::hasAttribute(CSSM_DB_RECORDTYPE recordType, uint32 attributeId) const
-{
- const RelationInfoMap &rmap = relationInfoMapFor(recordType);
- RelationInfoMap::const_iterator rit = rmap.find(attributeId);
- return rit != rmap.end();
-}
-
-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<SecKeychainAttributeInfo *>(malloc(sizeof(SecKeychainAttributeInfo)));
-
- UInt32 capacity=rmap.size();
- UInt32 *tagBuf=reinterpret_cast<UInt32 *>(malloc(capacity*sizeof(UInt32)));
- UInt32 *formatBuf=reinterpret_cast<UInt32 *>(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<UInt32 *>(realloc(tagBuf, (capacity*sizeof(UInt32))));
- formatBuf=reinterpret_cast<UInt32 *>(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;
-}
-
-
-//
-// KeychainImpl
-//
-KeychainImpl::KeychainImpl(const Db &db)
-: mDb(db)
-{
-}
-
-KeychainImpl::~KeychainImpl() throw()
-{
- globals().storageManager.removeKeychain(dLDbIdentifier(), this);
-}
-
-bool
-KeychainImpl::operator ==(const KeychainImpl &keychain) const
-{
- return dLDbIdentifier() == keychain.dLDbIdentifier();
-}
-
-KCCursor
-KeychainImpl::createCursor(SecItemClass itemClass, const SecKeychainAttributeList *attrList)
-{
- StorageManager::KeychainList keychains;
- keychains.push_back(Keychain(this));
- return KCCursor(keychains, itemClass, attrList);
-}
-
-KCCursor
-KeychainImpl::createCursor(const SecKeychainAttributeList *attrList)
-{
- StorageManager::KeychainList keychains;
- keychains.push_back(Keychain(this));
- return KCCursor(keychains, attrList);
-}
-
-void
-KeychainImpl::create(UInt32 passwordLength, const void *inPassword)
-{
- if (!inPassword)
- {
- create();
- return;
- }
-
- CssmAllocator &alloc = CssmAllocator::standard();
-
- // @@@ Share this instance
-
- const CssmData password(const_cast<void *>(inPassword), passwordLength);
- AclFactory::PasswordChangeCredentials pCreds (password, alloc);
- AclFactory::AnyResourceContext rcc(pCreds);
- create(&rcc);
-}
-
-void KeychainImpl::create(ConstStringPtr inPassword)
-{
- if ( inPassword )
- create(static_cast<UInt32>(inPassword[0]), &inPassword[1]);
- else
- create();
-}
-
-void
-KeychainImpl::create()
-{
- AclFactory aclFactory;
- AclFactory::AnyResourceContext rcc(aclFactory.unlockCred());
- create(&rcc);
-}
-
-void
-KeychainImpl::create(const ResourceControlContext *rcc)
-{
- 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));
-
- KCEventNotifier::PostKeychainEvent (kSecKeychainListChangedEvent, this, NULL);
-}
-
-void
-KeychainImpl::open()
-{
- mDb->open();
-}
-
-void
-KeychainImpl::lock()
-{
- mDb->lock();
-}
-
-void
-KeychainImpl::unlock()
-{
- mDb->unlock();
-}
-
-void
-KeychainImpl::unlock(const CssmData &password)
-{
- mDb->unlock(password);
-}
-
-void
-KeychainImpl::unlock(ConstStringPtr password)
-{
- if (password)
- {
- const CssmData data(const_cast<unsigned char *>(&password[1]), password[0]);
- unlock(data);
- }
- else
- unlock();
-}
-
-void
-KeychainImpl::getSettings(uint32 &outIdleTimeOut, bool &outLockOnSleep)
-{
- mDb->getSettings(outIdleTimeOut, outLockOnSleep);
-}
-
-void
-KeychainImpl::setSettings(uint32 inIdleTimeOut, bool inLockOnSleep)
-{
- mDb->setSettings(inIdleTimeOut, inLockOnSleep);
-}
-void
-KeychainImpl::changePassphrase(UInt32 oldPasswordLength, const void *oldPassword,
- UInt32 newPasswordLength, const void *newPassword)
-{
- // @@@ When AutoCredentials is actually finished we should no logner use a tracking allocator.
- TrackingAllocator allocator(CssmAllocator::standard());
- AutoCredentials cred = AutoCredentials(allocator);
- if (oldPassword)
- {
- const CssmData &oldPass = *new(allocator) CssmData(const_cast<void *>(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<void *>(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;
- }
-
- mDb->changePassphrase(&cred);
-}
-
-void
-KeychainImpl::changePassphrase(ConstStringPtr oldPassword, ConstStringPtr newPassword)
-{
- 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)
-{
- // @@@ This should do an authenticate which is not the same as unlock.
- if (!exists())
- MacOSError::throwMe(errSecNoSuchKeychain);
-
- MacOSError::throwMe(unimpErr);
-}
-
-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()
-{
- 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.cssmError() != CSSMERR_DL_DATASTORE_DOESNOT_EXIST)
- throw;
- exists = false;
- }
-
- return exists;
-}
-
-bool
-KeychainImpl::isActive() const
-{
- return mDb->isActive();
-}
-
-void
-KeychainImpl::add(Item &inItem)
-{
- Keychain keychain(this);
- PrimaryKey primaryKey = inItem->add(keychain);
- {
- StLock<Mutex> _(mDbItemMapLock);
- mDbItemMap[primaryKey] = inItem.get();
- }
-
- KCEventNotifier::PostKeychainEvent(kSecAddEvent, this, inItem);
-}
-
-void
-KeychainImpl::didUpdate(ItemImpl *inItemImpl, PrimaryKey &oldPK,
- PrimaryKey &newPK)
-{
- // Make sure we only hold mDbItemMapLock as long as we need to.
- {
- StLock<Mutex> _(mDbItemMapLock);
- DbItemMap::iterator it = mDbItemMap.find(oldPK);
- if (it != mDbItemMap.end() && it->second == inItemImpl)
- mDbItemMap.erase(it);
- mDbItemMap[newPK] = inItemImpl;
- }
-
- KCEventNotifier::PostKeychainEvent( kSecUpdateEvent, this, inItemImpl );
-}
-
-void
-KeychainImpl::deleteItem(Item &inoutItem)
-{
- // item must be persistant.
- if (!inoutItem->isPersistant())
- MacOSError::throwMe(errSecInvalidItemRef);
-
- DbUniqueRecord uniqueId = inoutItem->dbUniqueRecord();
- PrimaryKey primaryKey = inoutItem->primaryKey();
- uniqueId->deleteRecord();
-
- // Don't kill the ref or clear the Item() since this potentially
- // messes up things for the receiver of the kSecDeleteEvent notification.
- //inoutItem->killRef();
- //inoutItem = Item();
-
- // Post the notification for the item deletion with
- // the primaryKey obtained when the item still existed
- KCEventNotifier::PostKeychainEvent(kSecDeleteEvent, dLDbIdentifier(), primaryKey);
-}
-
-
-CssmClient::CSP
-KeychainImpl::csp()
-{
- if (!mDb->dl()->subserviceMask() & CSSM_SERVICE_CSP)
- MacOSError::throwMe(errSecInvalidKeychain);
-
- SSDb ssDb(safe_cast<SSDbImpl *>(&(*mDb)));
- return ssDb->csp();
-}
-
-PrimaryKey
-KeychainImpl::makePrimaryKey(CSSM_DB_RECORDTYPE recordType, DbUniqueRecord &uniqueId)
-{
- DbAttributes primaryKeyAttrs(uniqueId->database());
- primaryKeyAttrs.recordType(recordType);
- gatherPrimaryKeyAttributes(primaryKeyAttrs);
- uniqueId->get(&primaryKeyAttrs, NULL);
- return PrimaryKey(primaryKeyAttrs);
-}
-
-const CssmAutoDbRecordAttributeInfo &
-KeychainImpl::primaryKeyInfosFor(CSSM_DB_RECORDTYPE recordType)
-{
- try {
- return keychainSchema()->primaryKeyInfosFor(recordType);
- } catch (const CssmCommonError &error) {
- switch (error.cssmError()) {
- case errSecNoSuchClass:
- case CSSMERR_DL_INVALID_RECORDTYPE:
- resetSchema();
- return keychainSchema()->primaryKeyInfosFor(recordType);
- default:
- throw;
- }
- }
-}
-
-void KeychainImpl::gatherPrimaryKeyAttributes(DbAttributes& primaryKeyAttrs)
-{
- 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));
-}
-
-Item
-KeychainImpl::item(const PrimaryKey& primaryKey)
-{
- // @@@ This retry code isn't really the right way to do this,
- // we need to redo the locking structure here in the future.
- bool tried = false;
- for (;;)
- {
- {
- StLock<Mutex> _(mDbItemMapLock);
- DbItemMap::iterator it = mDbItemMap.find(primaryKey);
- if (it != mDbItemMap.end())
- {
- return Item(it->second);
- }
- }
-
- try
- {
- // Create an item with just a primary key
- return Item(this, primaryKey);
- }
- catch (const MacOSError &e)
- {
- if (tried || e.osStatus() != errSecDuplicateItem)
- throw;
- tried = true;
- }
- }
-}
-
-Item
-KeychainImpl::item(CSSM_DB_RECORDTYPE recordType, DbUniqueRecord &uniqueId)
-{
- PrimaryKey primaryKey = makePrimaryKey(recordType, uniqueId);
- {
- StLock<Mutex> _(mDbItemMapLock);
- DbItemMap::iterator it = mDbItemMap.find(primaryKey);
- if (it != mDbItemMap.end())
- {
- return Item(it->second);
- }
- }
-
- // Create a new item
- return Item(this, primaryKey, uniqueId);
-}
-
-KeychainSchema
-KeychainImpl::keychainSchema()
-{
- if (!mKeychainSchema)
- {
- // @@@ Use cache in storageManager
- 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 paritally constructed), add it to the map.
-void
-KeychainImpl::addItem(const PrimaryKey &primaryKey, ItemImpl *dbItemImpl)
-{
- StLock<Mutex> _(mDbItemMapLock);
- DbItemMap::iterator it = mDbItemMap.find(primaryKey);
- if (it != mDbItemMap.end())
- {
- // @@@ 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
- //assert(true);
- MacOSError::throwMe(errSecDuplicateItem);
- //mDbItemMap.erase(it);
- // @@@ What to do here?
- }
-
- mDbItemMap.insert(DbItemMap::value_type(primaryKey, dbItemImpl));
-}
-
-void
-KeychainImpl::didDeleteItem(const ItemImpl *inItemImpl)
-{
- // Sent sent by CCallbackMgr.
- secdebug("kcnotify", "%p notified that item %p was deleted", this, inItemImpl);
- PrimaryKey primaryKey = inItemImpl->primaryKey();
- StLock<Mutex> _(mDbItemMapLock);
- DbItemMap::iterator it = mDbItemMap.find(primaryKey);
- if (it != mDbItemMap.end())
- mDbItemMap.erase(it);
-}
-
-void
-KeychainImpl::removeItem(const PrimaryKey &primaryKey, const ItemImpl *inItemImpl)
-{
- // Sent from DbItemImpl's destructor, remove it from the map.
- StLock<Mutex> _(mDbItemMapLock);
- DbItemMap::iterator it = mDbItemMap.find(primaryKey);
- if (it != mDbItemMap.end() && it->second == inItemImpl)
- mDbItemMap.erase(it);
-}
-
-void
-KeychainImpl::getAttributeInfoForItemID(CSSM_DB_RECORDTYPE itemID, SecKeychainAttributeInfo **Info)
-{
- try {
- keychainSchema()->getAttributeInfoForRecordType(itemID, Info);
- } catch (const CssmCommonError &error) {
- switch (error.cssmError()) {
- 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)
-{
- try {
- return keychainSchema()->attributeInfoFor(recordType, tag);
- } catch (const CssmCommonError &error) {
- switch (error.cssmError()) {
- case errSecNoSuchClass:
- case CSSMERR_DL_INVALID_RECORDTYPE:
- resetSchema();
- return keychainSchema()->attributeInfoFor(recordType, tag);
- default:
- throw;
- }
- }
-
-}
-
-Keychain
-Keychain::optional(SecKeychainRef handle)
-{
- if (handle)
- return KeychainImpl::required(handle);
- else
- return globals().storageManager.defaultKeychain();
-}
-