1/*
2 * Copyright (c) 2000-2004,2011-2014 Apple 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
786		// make sure that we both mark the item and remove the item from the cache
787		removeItem(oldItem->primaryKey(), oldItem);
788		oldItem = inItem.get();
789	}
790
791	inItem->inCache(true);
792}
793
794void
795KeychainImpl::addCopy(Item &inItem)
796{
797	Keychain keychain(this);
798	PrimaryKey primaryKey = inItem->addWithCopyInfo(keychain, true);
799	completeAdd(inItem, primaryKey);
800	postEvent(kSecAddEvent, inItem);
801}
802
803void
804KeychainImpl::add(Item &inItem)
805{
806	Keychain keychain(this);
807	PrimaryKey primaryKey = inItem->add(keychain);
808	completeAdd(inItem, primaryKey);
809	postEvent(kSecAddEvent, inItem);
810}
811
812void
813KeychainImpl::didUpdate(const Item &inItem, PrimaryKey &oldPK,
814						PrimaryKey &newPK)
815{
816	// If the primary key hasn't changed we don't need to update mDbItemMap.
817	if (oldPK != newPK)
818	{
819		// If inItem isn't in the cache we don't need to update mDbItemMap.
820		assert(inItem->inCache());
821		if (inItem->inCache())
822		{
823			// First remove the entry for inItem in mDbItemMap with key oldPK.
824			DbItemMap::iterator it = mDbItemMap.find(oldPK);
825			if (it != mDbItemMap.end() && (ItemImpl*) it->second == inItem.get())
826				mDbItemMap.erase(it);
827
828			// Insert inItem into mDbItemMap with key newPK.  p.second will be
829			// true if it got inserted. If not p.second will be false and
830			// p.first will point to the current entry with key newPK.
831			pair<DbItemMap::iterator, bool> p =
832				mDbItemMap.insert(DbItemMap::value_type(newPK, inItem.get()));
833			if (!p.second)
834			{
835				// There was already an ItemImpl * in mDbItemMap with key
836				// primaryKey.  Get a ref to the pointer to it so we can assign
837				// a new value to it below.
838				ItemImpl *oldItem = p.first->second;
839
840				// @@@ If this happens we are breaking our API contract of
841				// uniquifying items.  We really need to insert the item into
842				// the map with the new primary key before we start the update.
843				// And have the item be in an "is being updated" state.
844				assert(oldItem->inCache());
845				secdebug("keychain", "update of item %p somehow replaced %p",
846					inItem.get(), oldItem);
847				oldItem->inCache(false);
848				oldItem = inItem.get();
849			}
850		}
851	}
852
853	postEvent(kSecUpdateEvent, inItem);
854}
855
856void
857KeychainImpl::deleteItem(Item &inoutItem)
858{
859	{
860		// We don't need to hold the DO mutex through event posting, and, in fact, doing so causes deadlock.
861		// Hold it only as long as needed, instead.
862
863
864		// item must be persistent
865		if (!inoutItem->isPersistent())
866			MacOSError::throwMe(errSecInvalidItemRef);
867
868		DbUniqueRecord uniqueId = inoutItem->dbUniqueRecord();
869		PrimaryKey primaryKey = inoutItem->primaryKey();
870		uniqueId->deleteRecord();
871
872		// Don't remove the item from the mDbItemMap here since this would cause
873		// us to report a new item to our caller when we receive the
874		// kSecDeleteEvent notification.
875		// It will be removed before we post the notification, because
876		// CCallbackMgr will call didDeleteItem()
877
878		// Post the notification for the item deletion with
879		// the primaryKey obtained when the item still existed
880	}
881
882	postEvent(kSecDeleteEvent, inoutItem);
883}
884
885
886CssmClient::CSP
887KeychainImpl::csp()
888{
889	StLock<Mutex>_(mMutex);
890
891	if (!mDb->dl()->subserviceMask() & CSSM_SERVICE_CSP)
892		MacOSError::throwMe(errSecInvalidKeychain);
893
894	// Try to cast first to a CSPDL to handle case where we don't have an SSDb
895	try
896	{
897		CssmClient::CSPDL cspdl(dynamic_cast<CssmClient::CSPDLImpl *>(&*mDb->dl()));
898		return CSP(cspdl);
899	}
900	catch (...)
901	{
902		SSDbImpl* impl = dynamic_cast<SSDbImpl *>(&(*mDb));
903		if (impl == NULL)
904		{
905			CssmError::throwMe(CSSMERR_CSSM_INVALID_POINTER);
906		}
907
908		SSDb ssDb(impl);
909		return ssDb->csp();
910	}
911}
912
913PrimaryKey
914KeychainImpl::makePrimaryKey(CSSM_DB_RECORDTYPE recordType, DbUniqueRecord &uniqueId)
915{
916	StLock<Mutex>_(mMutex);
917
918	DbAttributes primaryKeyAttrs(uniqueId->database());
919	primaryKeyAttrs.recordType(recordType);
920	gatherPrimaryKeyAttributes(primaryKeyAttrs);
921	uniqueId->get(&primaryKeyAttrs, NULL);
922	return PrimaryKey(primaryKeyAttrs);
923}
924
925const CssmAutoDbRecordAttributeInfo &
926KeychainImpl::primaryKeyInfosFor(CSSM_DB_RECORDTYPE recordType)
927{
928	StLock<Mutex>_(mMutex);
929
930	try
931	{
932		return keychainSchema()->primaryKeyInfosFor(recordType);
933	}
934	catch (const CommonError &error)
935	{
936		switch (error.osStatus())
937		{
938		case errSecNoSuchClass:
939		case CSSMERR_DL_INVALID_RECORDTYPE:
940			resetSchema();
941			return keychainSchema()->primaryKeyInfosFor(recordType);
942		default:
943			throw;
944		}
945	}
946}
947
948void KeychainImpl::gatherPrimaryKeyAttributes(DbAttributes& primaryKeyAttrs)
949{
950	StLock<Mutex>_(mMutex);
951
952	const CssmAutoDbRecordAttributeInfo &infos =
953		primaryKeyInfosFor(primaryKeyAttrs.recordType());
954
955	// @@@ fix this to not copy info.
956	for (uint32 i = 0; i < infos.size(); i++)
957		primaryKeyAttrs.add(infos.at(i));
958}
959
960ItemImpl *
961KeychainImpl::_lookupItem(const PrimaryKey &primaryKey)
962{
963	DbItemMap::iterator it = mDbItemMap.find(primaryKey);
964	if (it != mDbItemMap.end())
965	{
966		if (it->second == NULL)
967		{
968			// we've been weak released...
969			mDbItemMap.erase(it);
970		}
971		else
972		{
973			return it->second;
974		}
975	}
976
977	return NULL;
978}
979
980Item
981KeychainImpl::item(const PrimaryKey &primaryKey)
982{
983	StLock<Mutex>_(mMutex);
984
985	// Lookup the item in the map while holding the apiLock.
986	ItemImpl *itemImpl = _lookupItem(primaryKey);
987	if (itemImpl)
988		return Item(itemImpl);
989
990	try
991	{
992		// We didn't find it so create a new item with just a keychain and
993		// a primary key.  However since we aren't holding
994		// globals().apiLock anymore some other thread might have beaten
995		// us to creating this item and adding it to the cache.  If that
996		// happens we retry the lookup.
997		return Item(this, primaryKey);
998	}
999	catch (const MacOSError &e)
1000	{
1001		// If the item creation failed because some other thread already
1002		// inserted this item into the cache we retry the lookup.
1003		if (e.osStatus() == errSecDuplicateItem)
1004		{
1005			// Lookup the item in the map while holding the apiLock.
1006			ItemImpl *itemImpl = _lookupItem(primaryKey);
1007			if (itemImpl)
1008				return Item(itemImpl);
1009		}
1010		throw;
1011	}
1012}
1013
1014
1015Item
1016KeychainImpl::item(CSSM_DB_RECORDTYPE recordType, DbUniqueRecord &uniqueId)
1017{
1018	StLock<Mutex>_(mMutex);
1019
1020	PrimaryKey primaryKey = makePrimaryKey(recordType, uniqueId);
1021	{
1022		// Lookup the item in the map while holding the apiLock.
1023		ItemImpl *itemImpl = _lookupItem(primaryKey);
1024
1025		if (itemImpl)
1026		{
1027			return Item(itemImpl);
1028		}
1029	}
1030
1031	try
1032	{
1033		// We didn't find it so create a new item with a keychain, a primary key
1034		// and a DbUniqueRecord. However since we aren't holding
1035		// globals().apiLock anymore some other thread might have beaten
1036		// us to creating this item and adding it to the cache.  If that
1037		// happens we retry the lookup.
1038		return Item(this, primaryKey, uniqueId);
1039	}
1040	catch (const MacOSError &e)
1041	{
1042		// If the item creation failed because some other thread already
1043		// inserted this item into the cache we retry the lookup.
1044		if (e.osStatus() == errSecDuplicateItem)
1045		{
1046			// Lookup the item in the map while holding the apiLock.
1047			ItemImpl *itemImpl = _lookupItem(primaryKey);
1048			if (itemImpl)
1049				return Item(itemImpl);
1050		}
1051		throw;
1052	}
1053}
1054
1055KeychainSchema
1056KeychainImpl::keychainSchema()
1057{
1058	StLock<Mutex>_(mMutex);
1059	if (!mKeychainSchema)
1060		mKeychainSchema = KeychainSchema(mDb);
1061
1062	return mKeychainSchema;
1063}
1064
1065void KeychainImpl::resetSchema()
1066{
1067	mKeychainSchema = NULL;	// re-fetch it from db next time
1068}
1069
1070
1071// Called from DbItemImpl's constructor (so it is only partially constructed),
1072// add it to the map.
1073void
1074KeychainImpl::addItem(const PrimaryKey &primaryKey, ItemImpl *dbItemImpl)
1075{
1076	StLock<Mutex>_(mMutex);
1077
1078	// The dbItemImpl shouldn't be in the cache yet
1079	assert(!dbItemImpl->inCache());
1080
1081	// Insert dbItemImpl into mDbItemMap with key primaryKey.  p.second will
1082	// be true if it got inserted. If not p.second will be false and p.first
1083	// will point to the current entry with key primaryKey.
1084	pair<DbItemMap::iterator, bool> p =
1085		mDbItemMap.insert(DbItemMap::value_type(primaryKey, dbItemImpl));
1086
1087	if (!p.second)
1088	{
1089		// There was already an ItemImpl * in mDbItemMap with key primaryKey.
1090		// There is a race condition here when being called in multiple threads
1091		// We might have added an item using add and received a notification at
1092		// the same time.
1093		MacOSError::throwMe(errSecDuplicateItem);
1094	}
1095
1096	dbItemImpl->inCache(true);
1097}
1098
1099void
1100KeychainImpl::didDeleteItem(ItemImpl *inItemImpl)
1101{
1102	StLock<Mutex>_(mMutex);
1103
1104	// Called by CCallbackMgr
1105    secdebug("kcnotify", "%p notified that item %p was deleted", this, inItemImpl);
1106	removeItem(inItemImpl->primaryKey(), inItemImpl);
1107}
1108
1109void
1110KeychainImpl::removeItem(const PrimaryKey &primaryKey, ItemImpl *inItemImpl)
1111{
1112	StLock<Mutex>_(mMutex);
1113
1114	// If inItemImpl isn't in the cache to begin with we are done.
1115	if (!inItemImpl->inCache())
1116		return;
1117
1118	DbItemMap::iterator it = mDbItemMap.find(primaryKey);
1119	if (it != mDbItemMap.end() && (ItemImpl*) it->second == inItemImpl)
1120		mDbItemMap.erase(it);
1121
1122	inItemImpl->inCache(false);
1123}
1124
1125void
1126KeychainImpl::getAttributeInfoForItemID(CSSM_DB_RECORDTYPE itemID,
1127	SecKeychainAttributeInfo **Info)
1128{
1129	StLock<Mutex>_(mMutex);
1130
1131	try
1132	{
1133		keychainSchema()->getAttributeInfoForRecordType(itemID, Info);
1134	}
1135	catch (const CommonError &error)
1136	{
1137		switch (error.osStatus())
1138		{
1139		case errSecNoSuchClass:
1140		case CSSMERR_DL_INVALID_RECORDTYPE:
1141			resetSchema();
1142			keychainSchema()->getAttributeInfoForRecordType(itemID, Info);
1143		default:
1144			throw;
1145		}
1146	}
1147}
1148
1149void
1150KeychainImpl::freeAttributeInfo(SecKeychainAttributeInfo *Info)
1151{
1152	free(Info->tag);
1153	free(Info->format);
1154	free(Info);
1155}
1156
1157CssmDbAttributeInfo
1158KeychainImpl::attributeInfoFor(CSSM_DB_RECORDTYPE recordType, UInt32 tag)
1159{
1160	StLock<Mutex>_(mMutex);
1161
1162	try
1163	{
1164		return keychainSchema()->attributeInfoFor(recordType, tag);
1165	}
1166	catch (const CommonError &error)
1167	{
1168		switch (error.osStatus())
1169		{
1170		case errSecNoSuchClass:
1171		case CSSMERR_DL_INVALID_RECORDTYPE:
1172			resetSchema();
1173			return keychainSchema()->attributeInfoFor(recordType, tag);
1174		default:
1175			throw;
1176		}
1177	}
1178}
1179
1180void
1181KeychainImpl::recode(const CssmData &data, const CssmData &extraData)
1182{
1183	StLock<Mutex>_(mMutex);
1184
1185	mDb->recode(data, extraData);
1186}
1187
1188void
1189KeychainImpl::copyBlob(CssmData &data)
1190{
1191	StLock<Mutex>_(mMutex);
1192
1193	mDb->copyBlob(data);
1194}
1195
1196void
1197KeychainImpl::setBatchMode(Boolean mode, Boolean rollback)
1198{
1199	StLock<Mutex>_(mMutex);
1200
1201	mDb->setBatchMode(mode, rollback);
1202	mIsInBatchMode = mode;
1203	if (!mode)
1204	{
1205		if (!rollback) // was batch mode being turned off without an abort?
1206		{
1207			// dump the buffer
1208			EventBuffer::iterator it = mEventBuffer->begin();
1209			while (it != mEventBuffer->end())
1210			{
1211				PrimaryKey primaryKey;
1212				if (it->item)
1213				{
1214					primaryKey = it->item->primaryKey();
1215				}
1216
1217				KCEventNotifier::PostKeychainEvent(it->kcEvent, mDb->dlDbIdentifier(), primaryKey);
1218
1219				++it;
1220			}
1221
1222		}
1223
1224		// notify that a keychain has changed in too many ways to count
1225		KCEventNotifier::PostKeychainEvent(kSecKeychainLeftBatchModeEvent);
1226		mEventBuffer->clear();
1227	}
1228	else
1229	{
1230		KCEventNotifier::PostKeychainEvent(kSecKeychainEnteredBatchModeEvent);
1231	}
1232}
1233
1234void
1235KeychainImpl::postEvent(SecKeychainEvent kcEvent, ItemImpl* item)
1236{
1237	PrimaryKey primaryKey;
1238
1239	{
1240		StLock<Mutex>_(mMutex);
1241
1242		if (item != NULL)
1243		{
1244			primaryKey = item->primaryKey();
1245		}
1246	}
1247
1248	if (!mIsInBatchMode)
1249	{
1250		KCEventNotifier::PostKeychainEvent(kcEvent, mDb->dlDbIdentifier(), primaryKey);
1251	}
1252	else
1253	{
1254		StLock<Mutex>_(mMutex);
1255
1256		EventItem it;
1257		it.kcEvent = kcEvent;
1258		if (item != NULL)
1259		{
1260			it.item = item;
1261		}
1262
1263		mEventBuffer->push_back (it);
1264	}
1265}
1266
1267Keychain::Keychain()
1268{
1269	dispatch_once(&SecKeychainSystemKeychainChecked, ^{
1270		check_system_keychain();
1271	});
1272}
1273
1274Keychain::~Keychain()
1275{
1276}
1277
1278
1279
1280Keychain
1281Keychain::optional(SecKeychainRef handle)
1282{
1283	if (handle)
1284		return KeychainImpl::required(handle);
1285	else
1286		return globals().storageManager.defaultKeychain();
1287}
1288
1289
1290CFIndex KeychainCore::GetKeychainRetainCount(Keychain& kc)
1291{
1292	CFTypeRef ref = kc->handle(false);
1293	return CFGetRetainCount(ref);
1294}
1295
1296
1297//
1298// Create default credentials for this keychain.
1299// This is triggered upon default open (i.e. a Db::activate() with no set credentials).
1300//
1301// This function embodies the "default credentials" logic for Keychain-layer databases.
1302//
1303const AccessCredentials *
1304KeychainImpl::makeCredentials()
1305{
1306	return defaultCredentials();
1307}
1308
1309
1310const AccessCredentials *
1311KeychainImpl::defaultCredentials()
1312{
1313	StLock<Mutex>_(mMutex);
1314
1315	// Use custom unlock credentials for file keychains which have a referral
1316	// record and the standard credentials for all others.
1317
1318	if (mDb->dl()->guid() == gGuidAppleCSPDL && mCustomUnlockCreds(mDb))
1319		return &mCustomUnlockCreds;
1320	else
1321	if (mDb->dl()->guid() == gGuidAppleSdCSPDL)
1322		return globals().smartcardCredentials();
1323	else
1324		return globals().keychainCredentials();
1325}
1326
1327
1328
1329bool KeychainImpl::mayDelete()
1330{
1331    return true;
1332}
1333