1/*
2 * Copyright (c) 2000-2004 Apple Computer, Inc. All Rights Reserved.
3 *
4 * @APPLE_LICENSE_HEADER_START@
5 *
6 * This file contains Original Code and/or Modifications of Original Code
7 * as defined in and that are subject to the Apple Public Source License
8 * Version 2.0 (the 'License'). You may not use this file except in
9 * compliance with the License. Please obtain a copy of the License at
10 * http://www.opensource.apple.com/apsl/ and read it before using this
11 * file.
12 *
13 * The Original Code and all software distributed under the License are
14 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
15 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
16 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
18 * Please see the License for the specific language governing rights and
19 * limitations under the License.
20 *
21 * @APPLE_LICENSE_HEADER_END@
22 */
23
24
25//
26// Keychains.cpp
27//
28
29#include "KCEventNotifier.h"
30#include "Keychains.h"
31
32#include "Item.h"
33#include "KCCursor.h"
34#include "Globals.h"
35#include <security_cdsa_utilities/Schema.h>
36#include <security_cdsa_client/keychainacl.h>
37#include <security_cdsa_utilities/cssmacl.h>
38#include <security_cdsa_utilities/cssmdb.h>
39#include <security_utilities/trackingallocator.h>
40#include <security_keychain/SecCFTypes.h>
41
42#include "SecKeychainPriv.h"
43
44#include <Security/SecKeychainItemPriv.h>
45#include <CoreFoundation/CoreFoundation.h>
46#include "DLDbListCFPref.h"
47#include <fcntl.h>
48#include <sys/param.h>
49#include <syslog.h>
50#include <sys/stat.h>
51#include <sys/socket.h>
52#include <sys/un.h>
53#include <sys/types.h>
54#include <sys/time.h>
55
56static dispatch_once_t SecKeychainSystemKeychainChecked;
57
58OSStatus SecKeychainSystemKeychainCheckWouldDeadlock()
59{
60    dispatch_once(&SecKeychainSystemKeychainChecked, ^{});
61    return errSecSuccess;
62}
63
64using namespace KeychainCore;
65using namespace CssmClient;
66
67
68typedef struct EventItem
69{
70	SecKeychainEvent kcEvent;
71	Item item;
72} EventItem;
73
74typedef std::list<EventItem> EventBufferSuper;
75class EventBuffer : public EventBufferSuper
76{
77public:
78	EventBuffer () {}
79	virtual ~EventBuffer ();
80};
81
82
83EventBuffer::~EventBuffer ()
84{
85}
86
87
88
89//
90// KeychainSchemaImpl
91//
92KeychainSchemaImpl::KeychainSchemaImpl(const Db &db) : mMutex(Mutex::recursive)
93{
94	DbCursor relations(db);
95	relations->recordType(CSSM_DL_DB_SCHEMA_INFO);
96	DbAttributes relationRecord(db, 1);
97	relationRecord.add(Schema::RelationID);
98	DbUniqueRecord outerUniqueId(db);
99
100	while (relations->next(&relationRecord, NULL, outerUniqueId))
101	{
102		DbUniqueRecord uniqueId(db);
103
104		uint32 relationID = relationRecord.at(0);
105		if (CSSM_DB_RECORDTYPE_SCHEMA_START <= relationID
106			&& relationID < CSSM_DB_RECORDTYPE_SCHEMA_END)
107			continue;
108
109		// Create a cursor on the SCHEMA_ATTRIBUTES table for records with
110		// RelationID == relationID
111		DbCursor attributes(db);
112		attributes->recordType(CSSM_DL_DB_SCHEMA_ATTRIBUTES);
113		attributes->add(CSSM_DB_EQUAL, Schema::RelationID, relationID);
114
115		// Set up a record for retriving the SCHEMA_ATTRIBUTES
116		DbAttributes attributeRecord(db, 2);
117		attributeRecord.add(Schema::AttributeFormat);
118		attributeRecord.add(Schema::AttributeID);
119
120		RelationInfoMap &rim = mDatabaseInfoMap[relationID];
121		while (attributes->next(&attributeRecord, NULL, uniqueId))
122			rim[attributeRecord.at(1)] = attributeRecord.at(0);
123
124		// Create a cursor on the CSSM_DL_DB_SCHEMA_INDEXES table for records
125		// with RelationID == relationID
126		DbCursor indexes(db);
127		indexes->recordType(CSSM_DL_DB_SCHEMA_INDEXES);
128		indexes->conjunctive(CSSM_DB_AND);
129		indexes->add(CSSM_DB_EQUAL, Schema::RelationID, relationID);
130		indexes->add(CSSM_DB_EQUAL, Schema::IndexType,
131			uint32(CSSM_DB_INDEX_UNIQUE));
132
133		// Set up a record for retriving the SCHEMA_INDEXES
134		DbAttributes indexRecord(db, 1);
135		indexRecord.add(Schema::AttributeID);
136
137		CssmAutoDbRecordAttributeInfo &infos =
138			*new CssmAutoDbRecordAttributeInfo();
139		mPrimaryKeyInfoMap.
140			insert(PrimaryKeyInfoMap::value_type(relationID, &infos));
141		infos.DataRecordType = relationID;
142		while (indexes->next(&indexRecord, NULL, uniqueId))
143		{
144			CssmDbAttributeInfo &info = infos.add();
145			info.AttributeNameFormat = CSSM_DB_ATTRIBUTE_NAME_AS_INTEGER;
146			info.Label.AttributeID = indexRecord.at(0);
147			// @@@ Might insert bogus value if DB is corrupt
148			info.AttributeFormat = rim[info.Label.AttributeID];
149		}
150	}
151}
152
153KeychainSchemaImpl::~KeychainSchemaImpl()
154{
155	try
156	{
157        map<CSSM_DB_RECORDTYPE, CssmAutoDbRecordAttributeInfo *>::iterator it = mPrimaryKeyInfoMap.begin();
158        while (it != mPrimaryKeyInfoMap.end())
159        {
160            delete it->second;
161            it++;
162        }
163		// for_each_map_delete(mPrimaryKeyInfoMap.begin(), mPrimaryKeyInfoMap.end());
164	}
165	catch(...)
166	{
167	}
168}
169
170const KeychainSchemaImpl::RelationInfoMap &
171KeychainSchemaImpl::relationInfoMapFor(CSSM_DB_RECORDTYPE recordType) const
172{
173	DatabaseInfoMap::const_iterator dit = mDatabaseInfoMap.find(recordType);
174	if (dit == mDatabaseInfoMap.end())
175		MacOSError::throwMe(errSecNoSuchClass);
176	return dit->second;
177}
178
179bool KeychainSchemaImpl::hasRecordType (CSSM_DB_RECORDTYPE recordType) const
180{
181	DatabaseInfoMap::const_iterator it = mDatabaseInfoMap.find(recordType);
182	return it != mDatabaseInfoMap.end();
183}
184
185bool
186KeychainSchemaImpl::hasAttribute(CSSM_DB_RECORDTYPE recordType, uint32 attributeId) const
187{
188	try
189	{
190		const RelationInfoMap &rmap = relationInfoMapFor(recordType);
191		RelationInfoMap::const_iterator rit = rmap.find(attributeId);
192		return rit != rmap.end();
193	}
194	catch (MacOSError result)
195	{
196		if (result.osStatus () == errSecNoSuchClass)
197		{
198			return false;
199		}
200		else
201		{
202			throw;
203		}
204	}
205}
206
207CSSM_DB_ATTRIBUTE_FORMAT
208KeychainSchemaImpl::attributeFormatFor(CSSM_DB_RECORDTYPE recordType, uint32 attributeId) const
209{
210	const RelationInfoMap &rmap = relationInfoMapFor(recordType);
211	RelationInfoMap::const_iterator rit = rmap.find(attributeId);
212	if (rit == rmap.end())
213		MacOSError::throwMe(errSecNoSuchAttr);
214
215	return rit->second;
216}
217
218CssmDbAttributeInfo
219KeychainSchemaImpl::attributeInfoFor(CSSM_DB_RECORDTYPE recordType, uint32 attributeId) const
220{
221	CSSM_DB_ATTRIBUTE_INFO info;
222	info.AttributeFormat = attributeFormatFor(recordType, attributeId);
223	info.AttributeNameFormat = CSSM_DB_ATTRIBUTE_NAME_AS_INTEGER;
224	info.Label.AttributeID = attributeId;
225
226	return info;
227}
228
229void
230KeychainSchemaImpl::getAttributeInfoForRecordType(CSSM_DB_RECORDTYPE recordType, SecKeychainAttributeInfo **Info) const
231{
232	const RelationInfoMap &rmap = relationInfoMapFor(recordType);
233
234	SecKeychainAttributeInfo *theList=reinterpret_cast<SecKeychainAttributeInfo *>(malloc(sizeof(SecKeychainAttributeInfo)));
235
236	size_t capacity=rmap.size();
237	UInt32 *tagBuf=reinterpret_cast<UInt32 *>(malloc(capacity*sizeof(UInt32)));
238	UInt32 *formatBuf=reinterpret_cast<UInt32 *>(malloc(capacity*sizeof(UInt32)));
239	UInt32 i=0;
240
241
242	for (RelationInfoMap::const_iterator rit = rmap.begin(); rit != rmap.end(); ++rit)
243	{
244		if (i>=capacity)
245		{
246			capacity *= 2;
247			if (capacity <= i) capacity = i + 1;
248			tagBuf=reinterpret_cast<UInt32 *>(realloc(tagBuf, (capacity*sizeof(UInt32))));
249			formatBuf=reinterpret_cast<UInt32 *>(realloc(tagBuf, (capacity*sizeof(UInt32))));
250		}
251		tagBuf[i]=rit->first;
252		formatBuf[i++]=rit->second;
253	}
254
255	theList->count=i;
256	theList->tag=tagBuf;
257	theList->format=formatBuf;
258	*Info=theList;
259}
260
261
262const CssmAutoDbRecordAttributeInfo &
263KeychainSchemaImpl::primaryKeyInfosFor(CSSM_DB_RECORDTYPE recordType) const
264{
265	PrimaryKeyInfoMap::const_iterator it;
266	it = mPrimaryKeyInfoMap.find(recordType);
267
268	if (it == mPrimaryKeyInfoMap.end())
269		MacOSError::throwMe(errSecNoSuchClass); // @@@ Not really but whatever.
270
271	return *it->second;
272}
273
274bool
275KeychainSchemaImpl::operator <(const KeychainSchemaImpl &other) const
276{
277	return mDatabaseInfoMap < other.mDatabaseInfoMap;
278}
279
280bool
281KeychainSchemaImpl::operator ==(const KeychainSchemaImpl &other) const
282{
283	return mDatabaseInfoMap == other.mDatabaseInfoMap;
284}
285
286void
287KeychainSchemaImpl::didCreateRelation(CSSM_DB_RECORDTYPE relationID,
288	const char *inRelationName,
289	uint32 inNumberOfAttributes,
290	const CSSM_DB_SCHEMA_ATTRIBUTE_INFO *pAttributeInfo,
291	uint32 inNumberOfIndexes,
292	const CSSM_DB_SCHEMA_INDEX_INFO *pIndexInfo)
293{
294	StLock<Mutex>_(mMutex);
295
296	if (CSSM_DB_RECORDTYPE_SCHEMA_START <= relationID
297		&& relationID < CSSM_DB_RECORDTYPE_SCHEMA_END)
298		return;
299
300    // if our schema is already in the map, return
301    if (mPrimaryKeyInfoMap.find(relationID) != mPrimaryKeyInfoMap.end())
302    {
303        return;
304    }
305
306	RelationInfoMap &rim = mDatabaseInfoMap[relationID];
307	for (uint32 ix = 0; ix < inNumberOfAttributes; ++ix)
308		rim[pAttributeInfo[ix].AttributeId] = pAttributeInfo[ix].DataType;
309
310	CssmAutoDbRecordAttributeInfo *infos = new CssmAutoDbRecordAttributeInfo();
311
312	mPrimaryKeyInfoMap.
313		insert(PrimaryKeyInfoMap::value_type(relationID, infos));
314	infos->DataRecordType = relationID;
315	for (uint32 ix = 0; ix < inNumberOfIndexes; ++ix)
316		if (pIndexInfo[ix].IndexType == CSSM_DB_INDEX_UNIQUE)
317		{
318			CssmDbAttributeInfo &info = infos->add();
319			info.AttributeNameFormat = CSSM_DB_ATTRIBUTE_NAME_AS_INTEGER;
320			info.Label.AttributeID = pIndexInfo[ix].AttributeId;
321			info.AttributeFormat = rim[info.Label.AttributeID];
322		}
323}
324
325
326
327KeychainSchema::~KeychainSchema()
328
329{
330}
331
332
333
334struct Event
335{
336	SecKeychainEvent eventCode;
337	PrimaryKey primaryKey;
338};
339typedef std::list<Event> EventList;
340
341#define SYSTEM_KEYCHAIN_CHECK_UNIX_BASE_NAME "/var/run/systemkeychaincheck"
342#define SYSTEM_KEYCHAIN_CHECK_UNIX_DOMAIN_SOCKET_NAME (SYSTEM_KEYCHAIN_CHECK_UNIX_BASE_NAME ".socket")
343#define SYSTEM_KEYCHAIN_CHECK_COMPLETE_FILE_NAME (SYSTEM_KEYCHAIN_CHECK_UNIX_BASE_NAME ".done")
344
345static void check_system_keychain()
346{
347	// sadly we can't use XPC here, XPC_DOMAIN_TYPE_SYSTEM doesn't exist yet.  Also xpc-helper uses the
348	// keychain API (I assume for checking codesign things).   So we use Unix Domain Sockets.
349
350	// NOTE: if we hit a system error we attempt to log it, and then just don't check the system keychain.
351	// In theory a system might be able to recover from this state if we let it try to muddle along, and
352	// past behaviour didn't even try this hard to do the keychain check.  In particular we could be in a
353	// sandbox'ed process.   So we just do our best and let another process try again.
354
355	struct stat keycheck_file_info;
356	if (stat(SYSTEM_KEYCHAIN_CHECK_COMPLETE_FILE_NAME, &keycheck_file_info) < 0) {
357		int server_fd = socket(PF_UNIX, SOCK_STREAM, 0);
358		if (server_fd < 0) {
359			syslog(LOG_ERR, "Can't get socket (%m) system keychain may be unchecked");
360			return;
361		}
362
363		struct sockaddr_un keychain_check_server_address;
364		keychain_check_server_address.sun_family = AF_UNIX;
365		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)) {
366			// It would be nice if we could compile time assert this
367			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));
368			close(server_fd);
369			return;
370		}
371		keychain_check_server_address.sun_len = SUN_LEN(&keychain_check_server_address);
372
373		int rc = connect(server_fd, (struct sockaddr *)&keychain_check_server_address, keychain_check_server_address.sun_len);
374		if (rc < 0) {
375			syslog(LOG_ERR, "Can not connect to %s: %m", SYSTEM_KEYCHAIN_CHECK_UNIX_DOMAIN_SOCKET_NAME);
376			close(server_fd);
377			return;
378		}
379
380		// 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)
381		char byte;
382		ssize_t read_size = read(server_fd, &byte, 1);
383		if (read_size < 0) {
384			syslog(LOG_ERR, "Error reading from system keychain checker: %m");
385		}
386
387		close(server_fd);
388		return;
389	}
390}
391
392//
393// KeychainImpl
394//
395KeychainImpl::KeychainImpl(const Db &db)
396	: mInCache(false), mDb(db), mCustomUnlockCreds (this), mIsInBatchMode (false), mMutex(Mutex::recursive)
397{
398	dispatch_once(&SecKeychainSystemKeychainChecked, ^{
399		check_system_keychain();
400	});
401	mDb->defaultCredentials(this);	// install activation hook
402	mEventBuffer = new EventBuffer;
403}
404
405KeychainImpl::~KeychainImpl()
406{
407	try
408	{
409		// Remove ourselves from the cache if we are in it.
410        // fprintf(stderr, "Removing %p from storage manager cache.\n", handle(false));
411		globals().storageManager.removeKeychain(dlDbIdentifier(), this);
412		delete mEventBuffer;
413	}
414	catch(...)
415	{
416	}
417}
418
419Mutex*
420KeychainImpl::getMutexForObject()
421{
422	return globals().storageManager.getStorageManagerMutex();
423}
424
425Mutex*
426KeychainImpl::getKeychainMutex()
427{
428	return &mMutex;
429}
430
431void KeychainImpl::aboutToDestruct()
432{
433    // remove me from the global cache, we are done
434    // fprintf(stderr, "Destructing keychain object\n");
435    DLDbIdentifier identifier = dlDbIdentifier();
436    globals().storageManager.removeKeychain(identifier, this);
437}
438
439bool
440KeychainImpl::operator ==(const KeychainImpl &keychain) const
441{
442	return dlDbIdentifier() == keychain.dlDbIdentifier();
443}
444
445KCCursor
446KeychainImpl::createCursor(SecItemClass itemClass, const SecKeychainAttributeList *attrList)
447{
448	StLock<Mutex>_(mMutex);
449
450	StorageManager::KeychainList keychains;
451	keychains.push_back(Keychain(this));
452	return KCCursor(keychains, itemClass, attrList);
453}
454
455KCCursor
456KeychainImpl::createCursor(const SecKeychainAttributeList *attrList)
457{
458	StLock<Mutex>_(mMutex);
459
460	StorageManager::KeychainList keychains;
461	keychains.push_back(Keychain(this));
462	return KCCursor(keychains, attrList);
463}
464
465void
466KeychainImpl::create(UInt32 passwordLength, const void *inPassword)
467{
468	StLock<Mutex>_(mMutex);
469
470	if (!inPassword)
471	{
472		create();
473		return;
474	}
475
476	Allocator &alloc = Allocator::standard();
477
478	// @@@ Share this instance
479
480	const CssmData password(const_cast<void *>(inPassword), passwordLength);
481	AclFactory::PasswordChangeCredentials pCreds (password, alloc);
482	AclFactory::AnyResourceContext rcc(pCreds);
483	create(&rcc);
484}
485
486void KeychainImpl::create(ConstStringPtr inPassword)
487{
488	StLock<Mutex>_(mMutex);
489
490    if ( inPassword )
491        create(static_cast<UInt32>(inPassword[0]), &inPassword[1]);
492    else
493        create();
494}
495
496void
497KeychainImpl::create()
498{
499	StLock<Mutex>_(mMutex);
500
501	AclFactory aclFactory;
502	AclFactory::AnyResourceContext rcc(aclFactory.unlockCred());
503	create(&rcc);
504}
505
506void KeychainImpl::createWithBlob(CssmData &blob)
507{
508	StLock<Mutex>_(mMutex);
509
510	mDb->dbInfo(&Schema::DBInfo);
511	AclFactory aclFactory;
512	AclFactory::AnyResourceContext rcc(aclFactory.unlockCred());
513	mDb->resourceControlContext (&rcc);
514	try
515	{
516		mDb->createWithBlob(blob);
517	}
518	catch (...)
519	{
520		mDb->resourceControlContext(NULL);
521		mDb->dbInfo(NULL);
522		throw;
523	}
524	mDb->resourceControlContext(NULL);
525	mDb->dbInfo(NULL); // Clear the schema (to not break an open call later)
526	globals().storageManager.created(Keychain(this));
527
528    KCEventNotifier::PostKeychainEvent (kSecKeychainListChangedEvent, this, NULL);
529}
530
531void
532KeychainImpl::create(const ResourceControlContext *rcc)
533{
534	StLock<Mutex>_(mMutex);
535
536	mDb->dbInfo(&Schema::DBInfo); // Set the schema (to force a create)
537	mDb->resourceControlContext(rcc);
538    try
539    {
540        mDb->create();
541    }
542    catch (...)
543    {
544		mDb->resourceControlContext(NULL);
545        mDb->dbInfo(NULL); // Clear the schema (to not break an open call later)
546        throw;
547    }
548	mDb->resourceControlContext(NULL);
549	mDb->dbInfo(NULL); // Clear the schema (to not break an open call later)
550	globals().storageManager.created(Keychain(this));
551}
552
553void
554KeychainImpl::open()
555{
556	StLock<Mutex>_(mMutex);
557
558	mDb->open();
559}
560
561void
562KeychainImpl::lock()
563{
564	StLock<Mutex>_(mMutex);
565
566	mDb->lock();
567}
568
569void
570KeychainImpl::unlock()
571{
572	StLock<Mutex>_(mMutex);
573
574	mDb->unlock();
575}
576
577void
578KeychainImpl::unlock(const CssmData &password)
579{
580	StLock<Mutex>_(mMutex);
581
582	mDb->unlock(password);
583}
584
585void
586KeychainImpl::unlock(ConstStringPtr password)
587{
588	StLock<Mutex>_(mMutex);
589
590	if (password)
591	{
592		const CssmData data(const_cast<unsigned char *>(&password[1]), password[0]);
593		unlock(data);
594	}
595	else
596		unlock();
597}
598
599void
600KeychainImpl::stash()
601{
602  	StLock<Mutex>_(mMutex);
603
604	mDb->stash();
605}
606
607void
608KeychainImpl::stashCheck()
609{
610  	StLock<Mutex>_(mMutex);
611
612	mDb->stashCheck();
613}
614
615void
616KeychainImpl::getSettings(uint32 &outIdleTimeOut, bool &outLockOnSleep)
617{
618	StLock<Mutex>_(mMutex);
619
620	mDb->getSettings(outIdleTimeOut, outLockOnSleep);
621}
622
623void
624KeychainImpl::setSettings(uint32 inIdleTimeOut, bool inLockOnSleep)
625{
626	StLock<Mutex>_(mMutex);
627
628	// The .Mac syncing code only makes sense for the AppleFile CSP/DL,
629	// but other DLs such as the OCSP and LDAP DLs do not expose a way to
630	// change settings or the password. To make a minimal change that only affects
631	// the smartcard case, we only look for that CSP/DL
632
633	bool isSmartcard = 	(mDb->dl()->guid() == gGuidAppleSdCSPDL);
634
635	// get the old keychain blob so that we can tell .Mac to resync it
636	CssmAutoData oldBlob(mDb ->allocator());
637	if (!isSmartcard)
638		mDb->copyBlob(oldBlob.get());
639
640	mDb->setSettings(inIdleTimeOut, inLockOnSleep);
641}
642
643void
644KeychainImpl::changePassphrase(UInt32 oldPasswordLength, const void *oldPassword,
645	UInt32 newPasswordLength, const void *newPassword)
646{
647	StLock<Mutex>_(mMutex);
648
649	bool isSmartcard = 	(mDb->dl()->guid() == gGuidAppleSdCSPDL);
650
651	TrackingAllocator allocator(Allocator::standard());
652	AutoCredentials cred = AutoCredentials(allocator);
653	if (oldPassword)
654	{
655		const CssmData &oldPass = *new(allocator) CssmData(const_cast<void *>(oldPassword), oldPasswordLength);
656		TypedList &oldList = *new(allocator) TypedList(allocator, CSSM_SAMPLE_TYPE_KEYCHAIN_LOCK);
657		oldList.append(new(allocator) ListElement(CSSM_SAMPLE_TYPE_PASSWORD));
658		oldList.append(new(allocator) ListElement(oldPass));
659		cred += oldList;
660	}
661
662	if (newPassword)
663	{
664		const CssmData &newPass = *new(allocator) CssmData(const_cast<void *>(newPassword), newPasswordLength);
665		TypedList &newList = *new(allocator) TypedList(allocator, CSSM_SAMPLE_TYPE_KEYCHAIN_CHANGE_LOCK);
666		newList.append(new(allocator) ListElement(CSSM_SAMPLE_TYPE_PASSWORD));
667		newList.append(new(allocator) ListElement(newPass));
668		cred += newList;
669	}
670
671	// get the old keychain blob so that we can tell .Mac to resync it
672	CssmAutoData oldBlob(mDb->allocator());
673	if (!isSmartcard)
674		mDb->copyBlob(oldBlob.get());
675
676	mDb->changePassphrase(&cred);
677}
678
679void
680KeychainImpl::changePassphrase(ConstStringPtr oldPassword, ConstStringPtr newPassword)
681{
682	StLock<Mutex>_(mMutex);
683
684	const void *oldPtr, *newPtr;
685	UInt32 oldLen, newLen;
686	if (oldPassword)
687	{
688		oldLen = oldPassword[0];
689		oldPtr = oldPassword + 1;
690	}
691	else
692	{
693		oldLen = 0;
694		oldPtr = NULL;
695	}
696
697	if (newPassword)
698	{
699		newLen = newPassword[0];
700		newPtr = newPassword + 1;
701	}
702	else
703	{
704		newLen = 0;
705		newPtr = NULL;
706	}
707
708	changePassphrase(oldLen, oldPtr, newLen, newPtr);
709}
710
711void
712KeychainImpl::authenticate(const CSSM_ACCESS_CREDENTIALS *cred)
713{
714	StLock<Mutex>_(mMutex);
715
716	if (!exists())
717		MacOSError::throwMe(errSecNoSuchKeychain);
718
719	MacOSError::throwMe(errSecUnimplemented);
720}
721
722UInt32
723KeychainImpl::status() const
724{
725	// @@@ We should figure out the read/write status though a DL passthrough
726	// or some other way. Also should locked be unlocked read only or just
727	// read-only?
728	return (mDb->isLocked() ? 0 : kSecUnlockStateStatus | kSecWritePermStatus)
729		| kSecReadPermStatus;
730}
731
732bool
733KeychainImpl::exists()
734{
735	StLock<Mutex>_(mMutex);
736
737	bool exists = true;
738	try
739	{
740		open();
741		// Ok to leave the mDb open since it will get closed when it goes away.
742	}
743	catch (const CssmError &e)
744	{
745		if (e.osStatus() != CSSMERR_DL_DATASTORE_DOESNOT_EXIST)
746			throw;
747		exists = false;
748	}
749
750	return exists;
751}
752
753bool
754KeychainImpl::isActive() const
755{
756	return mDb->isActive();
757}
758
759void KeychainImpl::completeAdd(Item &inItem, PrimaryKey &primaryKey)
760{
761
762
763	// The inItem shouldn't be in the cache yet
764	assert(!inItem->inCache());
765
766	// Insert inItem into mDbItemMap with key primaryKey.  p.second will be
767	// true if it got inserted. If not p.second will be false and p.first
768	// will point to the current entry with key primaryKey.
769	pair<DbItemMap::iterator, bool> p =
770		mDbItemMap.insert(DbItemMap::value_type(primaryKey, inItem.get()));
771	if (!p.second)
772	{
773		// There was already an ItemImpl * in mDbItemMap with key
774		// primaryKey.  Get a ref to the pointer to it so we can assign a
775		// new value to it below.
776		ItemImpl *oldItem = p.first->second;
777
778		// @@@ If this happens we are breaking our API contract of
779		// uniquifying items.  We really need to insert the item into the
780		// map before we start the add.  And have the item be in an
781		// "is being added" state.
782		assert(oldItem->inCache());
783		secdebug("keychain", "add of new item %p somehow replaced %p",
784			inItem.get(), oldItem);
785		oldItem->inCache(false);
786		oldItem = inItem.get();
787	}
788
789	inItem->inCache(true);
790}
791
792void
793KeychainImpl::addCopy(Item &inItem)
794{
795	Keychain keychain(this);
796	PrimaryKey primaryKey = inItem->addWithCopyInfo(keychain, true);
797	completeAdd(inItem, primaryKey);
798	postEvent(kSecAddEvent, inItem);
799}
800
801void
802KeychainImpl::add(Item &inItem)
803{
804	Keychain keychain(this);
805	PrimaryKey primaryKey = inItem->add(keychain);
806	completeAdd(inItem, primaryKey);
807	postEvent(kSecAddEvent, inItem);
808}
809
810void
811KeychainImpl::didUpdate(const Item &inItem, PrimaryKey &oldPK,
812						PrimaryKey &newPK)
813{
814	// If the primary key hasn't changed we don't need to update mDbItemMap.
815	if (oldPK != newPK)
816	{
817		// If inItem isn't in the cache we don't need to update mDbItemMap.
818		assert(inItem->inCache());
819		if (inItem->inCache())
820		{
821			// First remove the entry for inItem in mDbItemMap with key oldPK.
822			DbItemMap::iterator it = mDbItemMap.find(oldPK);
823			if (it != mDbItemMap.end() && (ItemImpl*) it->second == inItem.get())
824				mDbItemMap.erase(it);
825
826			// Insert inItem into mDbItemMap with key newPK.  p.second will be
827			// true if it got inserted. If not p.second will be false and
828			// p.first will point to the current entry with key newPK.
829			pair<DbItemMap::iterator, bool> p =
830				mDbItemMap.insert(DbItemMap::value_type(newPK, inItem.get()));
831			if (!p.second)
832			{
833				// There was already an ItemImpl * in mDbItemMap with key
834				// primaryKey.  Get a ref to the pointer to it so we can assign
835				// a new value to it below.
836				ItemImpl *oldItem = p.first->second;
837
838				// @@@ If this happens we are breaking our API contract of
839				// uniquifying items.  We really need to insert the item into
840				// the map with the new primary key before we start the update.
841				// And have the item be in an "is being updated" state.
842				assert(oldItem->inCache());
843				secdebug("keychain", "update of item %p somehow replaced %p",
844					inItem.get(), oldItem);
845				oldItem->inCache(false);
846				oldItem = inItem.get();
847			}
848		}
849	}
850
851	postEvent(kSecUpdateEvent, inItem);
852}
853
854void
855KeychainImpl::deleteItem(Item &inoutItem)
856{
857	{
858		// We don't need to hold the DO mutex through event posting, and, in fact, doing so causes deadlock.
859		// Hold it only as long as needed, instead.
860
861
862		// item must be persistent
863		if (!inoutItem->isPersistent())
864			MacOSError::throwMe(errSecInvalidItemRef);
865
866		DbUniqueRecord uniqueId = inoutItem->dbUniqueRecord();
867		PrimaryKey primaryKey = inoutItem->primaryKey();
868		uniqueId->deleteRecord();
869
870		// Don't remove the item from the mDbItemMap here since this would cause
871		// us to report a new item to our caller when we receive the
872		// kSecDeleteEvent notification.
873		// It will be removed before we post the notification, because
874		// CCallbackMgr will call didDeleteItem()
875
876		// Post the notification for the item deletion with
877		// the primaryKey obtained when the item still existed
878	}
879
880	postEvent(kSecDeleteEvent, inoutItem);
881}
882
883
884CssmClient::CSP
885KeychainImpl::csp()
886{
887	StLock<Mutex>_(mMutex);
888
889	if (!mDb->dl()->subserviceMask() & CSSM_SERVICE_CSP)
890		MacOSError::throwMe(errSecInvalidKeychain);
891
892	// Try to cast first to a CSPDL to handle case where we don't have an SSDb
893	try
894	{
895		CssmClient::CSPDL cspdl(dynamic_cast<CssmClient::CSPDLImpl *>(&*mDb->dl()));
896		return CSP(cspdl);
897	}
898	catch (...)
899	{
900		SSDbImpl* impl = dynamic_cast<SSDbImpl *>(&(*mDb));
901		if (impl == NULL)
902		{
903			CssmError::throwMe(CSSMERR_CSSM_INVALID_POINTER);
904		}
905
906		SSDb ssDb(impl);
907		return ssDb->csp();
908	}
909}
910
911PrimaryKey
912KeychainImpl::makePrimaryKey(CSSM_DB_RECORDTYPE recordType, DbUniqueRecord &uniqueId)
913{
914	StLock<Mutex>_(mMutex);
915
916	DbAttributes primaryKeyAttrs(uniqueId->database());
917	primaryKeyAttrs.recordType(recordType);
918	gatherPrimaryKeyAttributes(primaryKeyAttrs);
919	uniqueId->get(&primaryKeyAttrs, NULL);
920	return PrimaryKey(primaryKeyAttrs);
921}
922
923const CssmAutoDbRecordAttributeInfo &
924KeychainImpl::primaryKeyInfosFor(CSSM_DB_RECORDTYPE recordType)
925{
926	StLock<Mutex>_(mMutex);
927
928	try
929	{
930		return keychainSchema()->primaryKeyInfosFor(recordType);
931	}
932	catch (const CommonError &error)
933	{
934		switch (error.osStatus())
935		{
936		case errSecNoSuchClass:
937		case CSSMERR_DL_INVALID_RECORDTYPE:
938			resetSchema();
939			return keychainSchema()->primaryKeyInfosFor(recordType);
940		default:
941			throw;
942		}
943	}
944}
945
946void KeychainImpl::gatherPrimaryKeyAttributes(DbAttributes& primaryKeyAttrs)
947{
948	StLock<Mutex>_(mMutex);
949
950	const CssmAutoDbRecordAttributeInfo &infos =
951		primaryKeyInfosFor(primaryKeyAttrs.recordType());
952
953	// @@@ fix this to not copy info.
954	for (uint32 i = 0; i < infos.size(); i++)
955		primaryKeyAttrs.add(infos.at(i));
956}
957
958ItemImpl *
959KeychainImpl::_lookupItem(const PrimaryKey &primaryKey)
960{
961	DbItemMap::iterator it = mDbItemMap.find(primaryKey);
962	if (it != mDbItemMap.end())
963	{
964		if (it->second == NULL)
965		{
966			// we've been weak released...
967			mDbItemMap.erase(it);
968		}
969		else
970		{
971			return it->second;
972		}
973	}
974
975	return NULL;
976}
977
978Item
979KeychainImpl::item(const PrimaryKey &primaryKey)
980{
981	StLock<Mutex>_(mMutex);
982
983	// Lookup the item in the map while holding the apiLock.
984	ItemImpl *itemImpl = _lookupItem(primaryKey);
985	if (itemImpl)
986		return Item(itemImpl);
987
988	try
989	{
990		// We didn't find it so create a new item with just a keychain and
991		// a primary key.  However since we aren't holding
992		// globals().apiLock anymore some other thread might have beaten
993		// us to creating this item and adding it to the cache.  If that
994		// happens we retry the lookup.
995		return Item(this, primaryKey);
996	}
997	catch (const MacOSError &e)
998	{
999		// If the item creation failed because some other thread already
1000		// inserted this item into the cache we retry the lookup.
1001		if (e.osStatus() == errSecDuplicateItem)
1002		{
1003			// Lookup the item in the map while holding the apiLock.
1004			ItemImpl *itemImpl = _lookupItem(primaryKey);
1005			if (itemImpl)
1006				return Item(itemImpl);
1007		}
1008		throw;
1009	}
1010}
1011
1012
1013Item
1014KeychainImpl::item(CSSM_DB_RECORDTYPE recordType, DbUniqueRecord &uniqueId)
1015{
1016	StLock<Mutex>_(mMutex);
1017
1018	PrimaryKey primaryKey = makePrimaryKey(recordType, uniqueId);
1019	{
1020		// Lookup the item in the map while holding the apiLock.
1021		ItemImpl *itemImpl = _lookupItem(primaryKey);
1022
1023		if (itemImpl)
1024		{
1025			return Item(itemImpl);
1026		}
1027	}
1028
1029	try
1030	{
1031		// We didn't find it so create a new item with a keychain, a primary key
1032		// and a DbUniqueRecord. However since we aren't holding
1033		// globals().apiLock anymore some other thread might have beaten
1034		// us to creating this item and adding it to the cache.  If that
1035		// happens we retry the lookup.
1036		return Item(this, primaryKey, uniqueId);
1037	}
1038	catch (const MacOSError &e)
1039	{
1040		// If the item creation failed because some other thread already
1041		// inserted this item into the cache we retry the lookup.
1042		if (e.osStatus() == errSecDuplicateItem)
1043		{
1044			// Lookup the item in the map while holding the apiLock.
1045			ItemImpl *itemImpl = _lookupItem(primaryKey);
1046			if (itemImpl)
1047				return Item(itemImpl);
1048		}
1049		throw;
1050	}
1051}
1052
1053KeychainSchema
1054KeychainImpl::keychainSchema()
1055{
1056	StLock<Mutex>_(mMutex);
1057	if (!mKeychainSchema)
1058		mKeychainSchema = KeychainSchema(mDb);
1059
1060	return mKeychainSchema;
1061}
1062
1063void KeychainImpl::resetSchema()
1064{
1065	mKeychainSchema = NULL;	// re-fetch it from db next time
1066}
1067
1068
1069// Called from DbItemImpl's constructor (so it is only partially constructed),
1070// add it to the map.
1071void
1072KeychainImpl::addItem(const PrimaryKey &primaryKey, ItemImpl *dbItemImpl)
1073{
1074	StLock<Mutex>_(mMutex);
1075
1076	// The dbItemImpl shouldn't be in the cache yet
1077	assert(!dbItemImpl->inCache());
1078
1079	// Insert dbItemImpl into mDbItemMap with key primaryKey.  p.second will
1080	// be true if it got inserted. If not p.second will be false and p.first
1081	// will point to the current entry with key primaryKey.
1082	pair<DbItemMap::iterator, bool> p =
1083		mDbItemMap.insert(DbItemMap::value_type(primaryKey, dbItemImpl));
1084
1085	if (!p.second)
1086	{
1087		// There was already an ItemImpl * in mDbItemMap with key primaryKey.
1088		// There is a race condition here when being called in multiple threads
1089		// We might have added an item using add and received a notification at
1090		// the same time.
1091		MacOSError::throwMe(errSecDuplicateItem);
1092	}
1093
1094	dbItemImpl->inCache(true);
1095}
1096
1097void
1098KeychainImpl::didDeleteItem(ItemImpl *inItemImpl)
1099{
1100	StLock<Mutex>_(mMutex);
1101
1102	// Called by CCallbackMgr
1103    secdebug("kcnotify", "%p notified that item %p was deleted", this, inItemImpl);
1104	removeItem(inItemImpl->primaryKey(), inItemImpl);
1105}
1106
1107void
1108KeychainImpl::removeItem(const PrimaryKey &primaryKey, ItemImpl *inItemImpl)
1109{
1110	StLock<Mutex>_(mMutex);
1111
1112	// If inItemImpl isn't in the cache to begin with we are done.
1113	if (!inItemImpl->inCache())
1114		return;
1115
1116	DbItemMap::iterator it = mDbItemMap.find(primaryKey);
1117	if (it != mDbItemMap.end() && (ItemImpl*) it->second == inItemImpl)
1118		mDbItemMap.erase(it);
1119
1120	inItemImpl->inCache(false);
1121}
1122
1123void
1124KeychainImpl::getAttributeInfoForItemID(CSSM_DB_RECORDTYPE itemID,
1125	SecKeychainAttributeInfo **Info)
1126{
1127	StLock<Mutex>_(mMutex);
1128
1129	try
1130	{
1131		keychainSchema()->getAttributeInfoForRecordType(itemID, Info);
1132	}
1133	catch (const CommonError &error)
1134	{
1135		switch (error.osStatus())
1136		{
1137		case errSecNoSuchClass:
1138		case CSSMERR_DL_INVALID_RECORDTYPE:
1139			resetSchema();
1140			keychainSchema()->getAttributeInfoForRecordType(itemID, Info);
1141		default:
1142			throw;
1143		}
1144	}
1145}
1146
1147void
1148KeychainImpl::freeAttributeInfo(SecKeychainAttributeInfo *Info)
1149{
1150	free(Info->tag);
1151	free(Info->format);
1152	free(Info);
1153}
1154
1155CssmDbAttributeInfo
1156KeychainImpl::attributeInfoFor(CSSM_DB_RECORDTYPE recordType, UInt32 tag)
1157{
1158	StLock<Mutex>_(mMutex);
1159
1160	try
1161	{
1162		return keychainSchema()->attributeInfoFor(recordType, tag);
1163	}
1164	catch (const CommonError &error)
1165	{
1166		switch (error.osStatus())
1167		{
1168		case errSecNoSuchClass:
1169		case CSSMERR_DL_INVALID_RECORDTYPE:
1170			resetSchema();
1171			return keychainSchema()->attributeInfoFor(recordType, tag);
1172		default:
1173			throw;
1174		}
1175	}
1176}
1177
1178void
1179KeychainImpl::recode(const CssmData &data, const CssmData &extraData)
1180{
1181	StLock<Mutex>_(mMutex);
1182
1183	mDb->recode(data, extraData);
1184}
1185
1186void
1187KeychainImpl::copyBlob(CssmData &data)
1188{
1189	StLock<Mutex>_(mMutex);
1190
1191	mDb->copyBlob(data);
1192}
1193
1194void
1195KeychainImpl::setBatchMode(Boolean mode, Boolean rollback)
1196{
1197	StLock<Mutex>_(mMutex);
1198
1199	mDb->setBatchMode(mode, rollback);
1200	mIsInBatchMode = mode;
1201	if (!mode)
1202	{
1203		if (!rollback) // was batch mode being turned off without an abort?
1204		{
1205			// dump the buffer
1206			EventBuffer::iterator it = mEventBuffer->begin();
1207			while (it != mEventBuffer->end())
1208			{
1209				PrimaryKey primaryKey;
1210				if (it->item)
1211				{
1212					primaryKey = it->item->primaryKey();
1213				}
1214
1215				KCEventNotifier::PostKeychainEvent(it->kcEvent, mDb->dlDbIdentifier(), primaryKey);
1216
1217				++it;
1218			}
1219
1220		}
1221
1222		// notify that a keychain has changed in too many ways to count
1223		KCEventNotifier::PostKeychainEvent(kSecKeychainLeftBatchModeEvent);
1224		mEventBuffer->clear();
1225	}
1226	else
1227	{
1228		KCEventNotifier::PostKeychainEvent(kSecKeychainEnteredBatchModeEvent);
1229	}
1230}
1231
1232void
1233KeychainImpl::postEvent(SecKeychainEvent kcEvent, ItemImpl* item)
1234{
1235	PrimaryKey primaryKey;
1236
1237	{
1238		StLock<Mutex>_(mMutex);
1239
1240		if (item != NULL)
1241		{
1242			primaryKey = item->primaryKey();
1243		}
1244	}
1245
1246	if (!mIsInBatchMode)
1247	{
1248		KCEventNotifier::PostKeychainEvent(kcEvent, mDb->dlDbIdentifier(), primaryKey);
1249	}
1250	else
1251	{
1252		StLock<Mutex>_(mMutex);
1253
1254		EventItem it;
1255		it.kcEvent = kcEvent;
1256		if (item != NULL)
1257		{
1258			it.item = item;
1259		}
1260
1261		mEventBuffer->push_back (it);
1262	}
1263}
1264
1265Keychain::Keychain()
1266{
1267	dispatch_once(&SecKeychainSystemKeychainChecked, ^{
1268		check_system_keychain();
1269	});
1270}
1271
1272Keychain::~Keychain()
1273{
1274}
1275
1276
1277
1278Keychain
1279Keychain::optional(SecKeychainRef handle)
1280{
1281	if (handle)
1282		return KeychainImpl::required(handle);
1283	else
1284		return globals().storageManager.defaultKeychain();
1285}
1286
1287
1288CFIndex KeychainCore::GetKeychainRetainCount(Keychain& kc)
1289{
1290	CFTypeRef ref = kc->handle(false);
1291	return CFGetRetainCount(ref);
1292}
1293
1294
1295//
1296// Create default credentials for this keychain.
1297// This is triggered upon default open (i.e. a Db::activate() with no set credentials).
1298//
1299// This function embodies the "default credentials" logic for Keychain-layer databases.
1300//
1301const AccessCredentials *
1302KeychainImpl::makeCredentials()
1303{
1304	return defaultCredentials();
1305}
1306
1307
1308const AccessCredentials *
1309KeychainImpl::defaultCredentials()
1310{
1311	StLock<Mutex>_(mMutex);
1312
1313	// Use custom unlock credentials for file keychains which have a referral
1314	// record and the standard credentials for all others.
1315
1316	if (mDb->dl()->guid() == gGuidAppleCSPDL && mCustomUnlockCreds(mDb))
1317		return &mCustomUnlockCreds;
1318	else
1319	if (mDb->dl()->guid() == gGuidAppleSdCSPDL)
1320		return globals().smartcardCredentials();
1321	else
1322		return globals().keychainCredentials();
1323}
1324
1325
1326
1327bool KeychainImpl::mayDelete()
1328{
1329    return true;
1330}
1331