#include "KCEventNotifier.h"
#include "Keychains.h"
#include "Item.h"
#include "KCCursor.h"
#include "Globals.h"
#include <security_cdsa_utilities/Schema.h>
#include <security_cdsa_client/keychainacl.h>
#include <security_cdsa_utilities/cssmacl.h>
#include <CoreServices/../Frameworks/CarbonCore.framework/Headers/MacErrors.h>
#include <security_cdsa_utilities/cssmdb.h>
#include <security_utilities/trackingallocator.h>
#include <security_keychain/SecCFTypes.h>
#include "SecKeychainPriv.h"
#include <Security/SecKeychainItemPriv.h>
#include <CoreFoundation/CoreFoundation.h>
#include "DLDbListCFPref.h"
#include <fcntl.h>
#include <sys/param.h>
#include <syslog.h>
#include <sys/stat.h>
#include <sys/socket.h>
#include <sys/un.h>
#include <sys/types.h>
#include <sys/time.h>
static dispatch_once_t SecKeychainSystemKeychainChecked;
OSStatus SecKeychainSystemKeychainCheckWouldDeadlock()
{
dispatch_once(&SecKeychainSystemKeychainChecked, ^{});
return noErr;
}
using namespace KeychainCore;
using namespace CssmClient;
typedef struct EventItem
{
SecKeychainEvent kcEvent;
Item item;
} EventItem;
typedef std::list<EventItem> EventBufferSuper;
class EventBuffer : public EventBufferSuper
{
public:
EventBuffer () {}
virtual ~EventBuffer ();
};
EventBuffer::~EventBuffer ()
{
}
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;
DbCursor attributes(db);
attributes->recordType(CSSM_DL_DB_SCHEMA_ATTRIBUTES);
attributes->add(CSSM_DB_EQUAL, Schema::RelationID, relationID);
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);
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));
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];
}
}
}
KeychainSchemaImpl::~KeychainSchemaImpl()
{
try
{
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<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);
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<Mutex>_(mMutex);
if (CSSM_DB_RECORDTYPE_SCHEMA_START <= relationID
&& relationID < CSSM_DB_RECORDTYPE_SCHEMA_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];
}
}
struct Event
{
SecKeychainEvent eventCode;
PrimaryKey primaryKey;
};
typedef std::list<Event> 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()
{
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)) {
syslog(LOG_ERR, "Socket path too long, max length %d, your length %d", sizeof(keychain_check_server_address.sun_path), 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;
}
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(const Db &db)
: mInCache(false), mDb(db), mCustomUnlockCreds (this), mIsInBatchMode (false), mMutex(Mutex::recursive)
{
dispatch_once(&SecKeychainSystemKeychainChecked, ^{
check_system_keychain();
});
mDb->defaultCredentials(this); mEventBuffer = new EventBuffer;
}
KeychainImpl::~KeychainImpl()
{
try
{
globals().storageManager.removeKeychain(dlDbIdentifier(), this);
delete mEventBuffer;
}
catch(...)
{
}
}
Mutex*
KeychainImpl::getMutexForObject()
{
return globals().storageManager.getStorageManagerMutex();
}
Mutex*
KeychainImpl::getKeychainMutex()
{
return &mMutex;
}
void KeychainImpl::aboutToDestruct()
{
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<Mutex>_(mMutex);
StorageManager::KeychainList keychains;
keychains.push_back(Keychain(this));
return KCCursor(keychains, itemClass, attrList);
}
KCCursor
KeychainImpl::createCursor(const SecKeychainAttributeList *attrList)
{
StLock<Mutex>_(mMutex);
StorageManager::KeychainList keychains;
keychains.push_back(Keychain(this));
return KCCursor(keychains, attrList);
}
void
KeychainImpl::create(UInt32 passwordLength, const void *inPassword)
{
StLock<Mutex>_(mMutex);
if (!inPassword)
{
create();
return;
}
Allocator &alloc = Allocator::standard();
const CssmData password(const_cast<void *>(inPassword), passwordLength);
AclFactory::PasswordChangeCredentials pCreds (password, alloc);
AclFactory::AnyResourceContext rcc(pCreds);
create(&rcc);
}
void KeychainImpl::create(ConstStringPtr inPassword)
{
StLock<Mutex>_(mMutex);
if ( inPassword )
create(static_cast<UInt32>(inPassword[0]), &inPassword[1]);
else
create();
}
void
KeychainImpl::create()
{
StLock<Mutex>_(mMutex);
AclFactory aclFactory;
AclFactory::AnyResourceContext rcc(aclFactory.unlockCred());
create(&rcc);
}
void KeychainImpl::createWithBlob(CssmData &blob)
{
StLock<Mutex>_(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); globals().storageManager.created(Keychain(this));
KCEventNotifier::PostKeychainEvent (kSecKeychainListChangedEvent, this, NULL);
}
void
KeychainImpl::create(const ResourceControlContext *rcc)
{
StLock<Mutex>_(mMutex);
mDb->dbInfo(&Schema::DBInfo); mDb->resourceControlContext(rcc);
try
{
mDb->create();
}
catch (...)
{
mDb->resourceControlContext(NULL);
mDb->dbInfo(NULL); throw;
}
mDb->resourceControlContext(NULL);
mDb->dbInfo(NULL); globals().storageManager.created(Keychain(this));
}
void
KeychainImpl::open()
{
StLock<Mutex>_(mMutex);
mDb->open();
}
void
KeychainImpl::lock()
{
StLock<Mutex>_(mMutex);
mDb->lock();
}
void
KeychainImpl::unlock()
{
StLock<Mutex>_(mMutex);
mDb->unlock();
}
void
KeychainImpl::unlock(const CssmData &password)
{
StLock<Mutex>_(mMutex);
mDb->unlock(password);
}
void
KeychainImpl::unlock(ConstStringPtr password)
{
StLock<Mutex>_(mMutex);
if (password)
{
const CssmData data(const_cast<unsigned char *>(&password[1]), password[0]);
unlock(data);
}
else
unlock();
}
void
KeychainImpl::getSettings(uint32 &outIdleTimeOut, bool &outLockOnSleep)
{
StLock<Mutex>_(mMutex);
mDb->getSettings(outIdleTimeOut, outLockOnSleep);
}
void KeychainImpl::markBlobForDotMacSyncUpdate(CssmData& data)
{
CFArrayRef dictionaries = (CFArrayRef) CFPreferencesCopyValue(CFSTR("KeychainSyncList"),
CFSTR("com.apple.keychainsync"),
kCFPreferencesCurrentUser,
kCFPreferencesAnyHost);
if (dictionaries == NULL) {
return;
}
CFStringRef currentPath = CFStringCreateWithCString(NULL, name(), kCFStringEncodingUTF8);
CFIndex i;
CFIndex count = CFArrayGetCount(dictionaries);
CFMutableArrayRef mutableDictionaries = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
for (i = 0; i < count; ++i)
{
CFMutableDictionaryRef d = (CFMutableDictionaryRef) CFDictionaryCreateMutableCopy(NULL, 0, (CFDictionaryRef) CFArrayGetValueAtIndex(dictionaries, i));
CFArrayAppendValue(mutableDictionaries, d);
CFRelease(d);
}
CFRelease(dictionaries);
bool somethingChanged = false;
for (i = 0; i < count; ++i)
{
CFMutableDictionaryRef d = (CFMutableDictionaryRef) CFArrayGetValueAtIndex(mutableDictionaries, i);
CFStringRef path = (CFStringRef) CFDictionaryGetValue(d, CFSTR("DbName"));
CFIndex length = CFStringGetMaximumSizeForEncoding(CFStringGetLength(path), kCFStringEncodingUTF8);
char buffer[length + 1]; CFStringGetCString(path, buffer, sizeof(buffer), kCFStringEncodingUTF8);
string fullPath = buffer;
fullPath = DLDbListCFPref::ExpandTildesInPath(fullPath);
path = CFStringCreateWithCString(NULL, fullPath.c_str(), kCFStringEncodingUTF8);
if (CFStringCompare(path, currentPath, 0) == kCFCompareEqualTo)
{
CFDataRef oldBlob = (CFDataRef) CFDictionaryGetValue(d, CFSTR("DbOldBlob"));
if (oldBlob == NULL)
{
CFDataRef theBlob = (CFDataRef) CFDataCreate(NULL, (uint8*) data.data(), data.length());
CFDictionaryAddValue(d, CFSTR("DbOldBlob"), theBlob);
CFRelease(theBlob);
CFPreferencesSetValue(CFSTR("KeychainSyncList"),
mutableDictionaries,
CFSTR("com.apple.keychainsync"),
kCFPreferencesCurrentUser,
kCFPreferencesAnyHost);
somethingChanged = true;
}
}
}
CFRelease(currentPath);
CFRelease(mutableDictionaries);
if (somethingChanged)
{
CFPreferencesSynchronize(CFSTR("com.apple.keychainsync"), kCFPreferencesCurrentUser, kCFPreferencesAnyHost);
}
}
void
KeychainImpl::setSettings(uint32 inIdleTimeOut, bool inLockOnSleep)
{
StLock<Mutex>_(mMutex);
bool isSmartcard = (mDb->dl()->guid() == gGuidAppleSdCSPDL);
CssmAutoData oldBlob(mDb ->allocator());
if (!isSmartcard)
mDb->copyBlob(oldBlob.get());
mDb->setSettings(inIdleTimeOut, inLockOnSleep);
if (!isSmartcard)
markBlobForDotMacSyncUpdate(oldBlob.get());
}
void
KeychainImpl::changePassphrase(UInt32 oldPasswordLength, const void *oldPassword,
UInt32 newPasswordLength, const void *newPassword)
{
StLock<Mutex>_(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<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;
}
CssmAutoData oldBlob(mDb->allocator());
if (!isSmartcard)
mDb->copyBlob(oldBlob.get());
mDb->changePassphrase(&cred);
if (!isSmartcard)
markBlobForDotMacSyncUpdate(oldBlob.get());
}
void
KeychainImpl::changePassphrase(ConstStringPtr oldPassword, ConstStringPtr newPassword)
{
StLock<Mutex>_(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<Mutex>_(mMutex);
if (!exists())
MacOSError::throwMe(errSecNoSuchKeychain);
MacOSError::throwMe(unimpErr);
}
UInt32
KeychainImpl::status() const
{
return (mDb->isLocked() ? 0 : kSecUnlockStateStatus | kSecWritePermStatus)
| kSecReadPermStatus;
}
bool
KeychainImpl::exists()
{
StLock<Mutex>_(mMutex);
bool exists = true;
try
{
open();
}
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)
{
assert(!inItem->inCache());
pair<DbItemMap::iterator, bool> p =
mDbItemMap.insert(DbItemMap::value_type(primaryKey, inItem.get()));
if (!p.second)
{
ItemImpl *oldItem = p.first->second;
assert(oldItem->inCache());
secdebug("keychain", "add of new item %p somehow replaced %p",
inItem.get(), oldItem);
oldItem->inCache(false);
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 (oldPK != newPK)
{
assert(inItem->inCache());
if (inItem->inCache())
{
DbItemMap::iterator it = mDbItemMap.find(oldPK);
if (it != mDbItemMap.end() && (ItemImpl*) it->second == inItem.get())
mDbItemMap.erase(it);
pair<DbItemMap::iterator, bool> p =
mDbItemMap.insert(DbItemMap::value_type(newPK, inItem.get()));
if (!p.second)
{
ItemImpl *oldItem = p.first->second;
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)
{
{
if (!inoutItem->isPersistent())
MacOSError::throwMe(errSecInvalidItemRef);
DbUniqueRecord uniqueId = inoutItem->dbUniqueRecord();
PrimaryKey primaryKey = inoutItem->primaryKey();
uniqueId->deleteRecord();
}
postEvent(kSecDeleteEvent, inoutItem);
}
CssmClient::CSP
KeychainImpl::csp()
{
StLock<Mutex>_(mMutex);
if (!mDb->dl()->subserviceMask() & CSSM_SERVICE_CSP)
MacOSError::throwMe(errSecInvalidKeychain);
try
{
CssmClient::CSPDL cspdl(dynamic_cast<CssmClient::CSPDLImpl *>(&*mDb->dl()));
return CSP(cspdl);
}
catch (...)
{
SSDbImpl* impl = dynamic_cast<SSDbImpl *>(&(*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<Mutex>_(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<Mutex>_(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<Mutex>_(mMutex);
const CssmAutoDbRecordAttributeInfo &infos =
primaryKeyInfosFor(primaryKeyAttrs.recordType());
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)
{
mDbItemMap.erase(it);
}
else
{
return it->second;
}
}
return NULL;
}
Item
KeychainImpl::item(const PrimaryKey &primaryKey)
{
StLock<Mutex>_(mMutex);
ItemImpl *itemImpl = _lookupItem(primaryKey);
if (itemImpl)
return Item(itemImpl);
try
{
return Item(this, primaryKey);
}
catch (const MacOSError &e)
{
if (e.osStatus() == errSecDuplicateItem)
{
ItemImpl *itemImpl = _lookupItem(primaryKey);
if (itemImpl)
return Item(itemImpl);
}
throw;
}
}
Item
KeychainImpl::item(CSSM_DB_RECORDTYPE recordType, DbUniqueRecord &uniqueId)
{
StLock<Mutex>_(mMutex);
PrimaryKey primaryKey = makePrimaryKey(recordType, uniqueId);
{
ItemImpl *itemImpl = _lookupItem(primaryKey);
if (itemImpl)
{
return Item(itemImpl);
}
}
try
{
return Item(this, primaryKey, uniqueId);
}
catch (const MacOSError &e)
{
if (e.osStatus() == errSecDuplicateItem)
{
ItemImpl *itemImpl = _lookupItem(primaryKey);
if (itemImpl)
return Item(itemImpl);
}
throw;
}
}
KeychainSchema
KeychainImpl::keychainSchema()
{
StLock<Mutex>_(mMutex);
if (!mKeychainSchema)
mKeychainSchema = KeychainSchema(mDb);
return mKeychainSchema;
}
void KeychainImpl::resetSchema()
{
mKeychainSchema = NULL; }
void
KeychainImpl::addItem(const PrimaryKey &primaryKey, ItemImpl *dbItemImpl)
{
StLock<Mutex>_(mMutex);
assert(!dbItemImpl->inCache());
pair<DbItemMap::iterator, bool> p =
mDbItemMap.insert(DbItemMap::value_type(primaryKey, dbItemImpl));
if (!p.second)
{
MacOSError::throwMe(errSecDuplicateItem);
}
dbItemImpl->inCache(true);
}
void
KeychainImpl::didDeleteItem(ItemImpl *inItemImpl)
{
StLock<Mutex>_(mMutex);
secdebug("kcnotify", "%p notified that item %p was deleted", this, inItemImpl);
removeItem(inItemImpl->primaryKey(), inItemImpl);
}
void
KeychainImpl::removeItem(const PrimaryKey &primaryKey, ItemImpl *inItemImpl)
{
StLock<Mutex>_(mMutex);
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<Mutex>_(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<Mutex>_(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<Mutex>_(mMutex);
mDb->recode(data, extraData);
}
void
KeychainImpl::copyBlob(CssmData &data)
{
StLock<Mutex>_(mMutex);
mDb->copyBlob(data);
}
void
KeychainImpl::setBatchMode(Boolean mode, Boolean rollback)
{
StLock<Mutex>_(mMutex);
mDb->setBatchMode(mode, rollback);
mIsInBatchMode = mode;
if (!mode)
{
if (!rollback) {
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;
}
}
KCEventNotifier::PostKeychainEvent(kSecKeychainLeftBatchModeEvent);
mEventBuffer->clear();
}
else
{
KCEventNotifier::PostKeychainEvent(kSecKeychainEnteredBatchModeEvent);
}
}
void
KeychainImpl::postEvent(SecKeychainEvent kcEvent, ItemImpl* item)
{
PrimaryKey primaryKey;
{
StLock<Mutex>_(mMutex);
if (item != NULL)
{
primaryKey = item->primaryKey();
}
}
if (!mIsInBatchMode)
{
KCEventNotifier::PostKeychainEvent(kcEvent, mDb->dlDbIdentifier(), primaryKey);
}
else
{
StLock<Mutex>_(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::optional(SecKeychainRef handle)
{
if (handle)
return KeychainImpl::required(handle);
else
return globals().storageManager.defaultKeychain();
}
CFIndex GetKeychainRetainCount(Keychain& kc)
{
CFTypeRef ref = kc->handle(false);
return CFGetRetainCount(ref);
}
const AccessCredentials *
KeychainImpl::makeCredentials()
{
return defaultCredentials();
}
const AccessCredentials *
KeychainImpl::defaultCredentials()
{
StLock<Mutex>_(mMutex);
if (mDb->dl()->guid() == gGuidAppleCSPDL && mCustomUnlockCreds(mDb))
return &mCustomUnlockCreds;
else
if (mDb->dl()->guid() == gGuidAppleSdCSPDL)
return globals().smartcardCredentials();
else
return globals().keychainCredentials();
}