]> git.saurik.com Git - apple/security.git/blame - OSX/libsecurity_keychain/lib/Keychains.cpp
Security-58286.70.7.tar.gz
[apple/security.git] / OSX / libsecurity_keychain / lib / Keychains.cpp
CommitLineData
b1ab9ed8 1/*
d8f41ccd 2 * Copyright (c) 2000-2004,2011-2014 Apple Inc. All Rights Reserved.
b1ab9ed8
A
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>
b1ab9ed8
A
38#include <security_cdsa_utilities/cssmdb.h>
39#include <security_utilities/trackingallocator.h>
fa7225c8 40#include <security_utilities/FileLockTransaction.h>
b1ab9ed8 41#include <security_keychain/SecCFTypes.h>
e3d460c9
A
42#include <securityd_client/ssblob.h>
43#include <Security/TrustSettingsSchema.h>
b1ab9ed8
A
44
45#include "SecKeychainPriv.h"
46
47#include <Security/SecKeychainItemPriv.h>
48#include <CoreFoundation/CoreFoundation.h>
866f8763 49#include "DLDBListCFPref.h"
b1ab9ed8 50#include <fcntl.h>
fa7225c8 51#include <glob.h>
b1ab9ed8
A
52#include <sys/param.h>
53#include <syslog.h>
54#include <sys/stat.h>
55#include <sys/socket.h>
56#include <sys/un.h>
57#include <sys/types.h>
58#include <sys/time.h>
59
60static dispatch_once_t SecKeychainSystemKeychainChecked;
61
62OSStatus SecKeychainSystemKeychainCheckWouldDeadlock()
63{
64 dispatch_once(&SecKeychainSystemKeychainChecked, ^{});
427c49bc 65 return errSecSuccess;
b1ab9ed8
A
66}
67
68using namespace KeychainCore;
69using namespace CssmClient;
70
71
72typedef struct EventItem
73{
74 SecKeychainEvent kcEvent;
75 Item item;
76} EventItem;
77
78typedef std::list<EventItem> EventBufferSuper;
79class EventBuffer : public EventBufferSuper
80{
81public:
82 EventBuffer () {}
83 virtual ~EventBuffer ();
84};
85
86
87EventBuffer::~EventBuffer ()
88{
89}
90
91
92
93//
94// KeychainSchemaImpl
95//
96KeychainSchemaImpl::KeychainSchemaImpl(const Db &db) : mMutex(Mutex::recursive)
97{
98 DbCursor relations(db);
99 relations->recordType(CSSM_DL_DB_SCHEMA_INFO);
100 DbAttributes relationRecord(db, 1);
101 relationRecord.add(Schema::RelationID);
102 DbUniqueRecord outerUniqueId(db);
103
104 while (relations->next(&relationRecord, NULL, outerUniqueId))
105 {
106 DbUniqueRecord uniqueId(db);
107
108 uint32 relationID = relationRecord.at(0);
109 if (CSSM_DB_RECORDTYPE_SCHEMA_START <= relationID
110 && relationID < CSSM_DB_RECORDTYPE_SCHEMA_END)
111 continue;
112
113 // Create a cursor on the SCHEMA_ATTRIBUTES table for records with
114 // RelationID == relationID
115 DbCursor attributes(db);
116 attributes->recordType(CSSM_DL_DB_SCHEMA_ATTRIBUTES);
117 attributes->add(CSSM_DB_EQUAL, Schema::RelationID, relationID);
118
119 // Set up a record for retriving the SCHEMA_ATTRIBUTES
120 DbAttributes attributeRecord(db, 2);
121 attributeRecord.add(Schema::AttributeFormat);
122 attributeRecord.add(Schema::AttributeID);
123
124 RelationInfoMap &rim = mDatabaseInfoMap[relationID];
125 while (attributes->next(&attributeRecord, NULL, uniqueId))
126 rim[attributeRecord.at(1)] = attributeRecord.at(0);
127
128 // Create a cursor on the CSSM_DL_DB_SCHEMA_INDEXES table for records
129 // with RelationID == relationID
130 DbCursor indexes(db);
131 indexes->recordType(CSSM_DL_DB_SCHEMA_INDEXES);
132 indexes->conjunctive(CSSM_DB_AND);
133 indexes->add(CSSM_DB_EQUAL, Schema::RelationID, relationID);
134 indexes->add(CSSM_DB_EQUAL, Schema::IndexType,
135 uint32(CSSM_DB_INDEX_UNIQUE));
136
137 // Set up a record for retriving the SCHEMA_INDEXES
138 DbAttributes indexRecord(db, 1);
139 indexRecord.add(Schema::AttributeID);
140
141 CssmAutoDbRecordAttributeInfo &infos =
142 *new CssmAutoDbRecordAttributeInfo();
143 mPrimaryKeyInfoMap.
144 insert(PrimaryKeyInfoMap::value_type(relationID, &infos));
145 infos.DataRecordType = relationID;
146 while (indexes->next(&indexRecord, NULL, uniqueId))
147 {
148 CssmDbAttributeInfo &info = infos.add();
149 info.AttributeNameFormat = CSSM_DB_ATTRIBUTE_NAME_AS_INTEGER;
150 info.Label.AttributeID = indexRecord.at(0);
151 // @@@ Might insert bogus value if DB is corrupt
152 info.AttributeFormat = rim[info.Label.AttributeID];
153 }
154 }
155}
156
157KeychainSchemaImpl::~KeychainSchemaImpl()
158{
159 try
160 {
427c49bc
A
161 map<CSSM_DB_RECORDTYPE, CssmAutoDbRecordAttributeInfo *>::iterator it = mPrimaryKeyInfoMap.begin();
162 while (it != mPrimaryKeyInfoMap.end())
163 {
164 delete it->second;
165 it++;
166 }
167 // for_each_map_delete(mPrimaryKeyInfoMap.begin(), mPrimaryKeyInfoMap.end());
b1ab9ed8
A
168 }
169 catch(...)
170 {
171 }
172}
173
174const KeychainSchemaImpl::RelationInfoMap &
175KeychainSchemaImpl::relationInfoMapFor(CSSM_DB_RECORDTYPE recordType) const
176{
177 DatabaseInfoMap::const_iterator dit = mDatabaseInfoMap.find(recordType);
178 if (dit == mDatabaseInfoMap.end())
179 MacOSError::throwMe(errSecNoSuchClass);
180 return dit->second;
181}
182
183bool KeychainSchemaImpl::hasRecordType (CSSM_DB_RECORDTYPE recordType) const
184{
185 DatabaseInfoMap::const_iterator it = mDatabaseInfoMap.find(recordType);
186 return it != mDatabaseInfoMap.end();
187}
188
189bool
190KeychainSchemaImpl::hasAttribute(CSSM_DB_RECORDTYPE recordType, uint32 attributeId) const
191{
192 try
193 {
194 const RelationInfoMap &rmap = relationInfoMapFor(recordType);
195 RelationInfoMap::const_iterator rit = rmap.find(attributeId);
196 return rit != rmap.end();
197 }
198 catch (MacOSError result)
199 {
200 if (result.osStatus () == errSecNoSuchClass)
201 {
202 return false;
203 }
204 else
205 {
206 throw;
207 }
208 }
209}
210
211CSSM_DB_ATTRIBUTE_FORMAT
212KeychainSchemaImpl::attributeFormatFor(CSSM_DB_RECORDTYPE recordType, uint32 attributeId) const
213{
214 const RelationInfoMap &rmap = relationInfoMapFor(recordType);
215 RelationInfoMap::const_iterator rit = rmap.find(attributeId);
216 if (rit == rmap.end())
217 MacOSError::throwMe(errSecNoSuchAttr);
218
219 return rit->second;
220}
221
222CssmDbAttributeInfo
223KeychainSchemaImpl::attributeInfoFor(CSSM_DB_RECORDTYPE recordType, uint32 attributeId) const
224{
225 CSSM_DB_ATTRIBUTE_INFO info;
226 info.AttributeFormat = attributeFormatFor(recordType, attributeId);
227 info.AttributeNameFormat = CSSM_DB_ATTRIBUTE_NAME_AS_INTEGER;
228 info.Label.AttributeID = attributeId;
229
230 return info;
231}
232
233void
234KeychainSchemaImpl::getAttributeInfoForRecordType(CSSM_DB_RECORDTYPE recordType, SecKeychainAttributeInfo **Info) const
235{
236 const RelationInfoMap &rmap = relationInfoMapFor(recordType);
237
238 SecKeychainAttributeInfo *theList=reinterpret_cast<SecKeychainAttributeInfo *>(malloc(sizeof(SecKeychainAttributeInfo)));
239
427c49bc 240 size_t capacity=rmap.size();
b1ab9ed8
A
241 UInt32 *tagBuf=reinterpret_cast<UInt32 *>(malloc(capacity*sizeof(UInt32)));
242 UInt32 *formatBuf=reinterpret_cast<UInt32 *>(malloc(capacity*sizeof(UInt32)));
243 UInt32 i=0;
244
245
246 for (RelationInfoMap::const_iterator rit = rmap.begin(); rit != rmap.end(); ++rit)
247 {
248 if (i>=capacity)
249 {
250 capacity *= 2;
251 if (capacity <= i) capacity = i + 1;
252 tagBuf=reinterpret_cast<UInt32 *>(realloc(tagBuf, (capacity*sizeof(UInt32))));
6b200bc3 253 formatBuf=reinterpret_cast<UInt32 *>(realloc(formatBuf, (capacity*sizeof(UInt32))));
b1ab9ed8
A
254 }
255 tagBuf[i]=rit->first;
256 formatBuf[i++]=rit->second;
257 }
258
259 theList->count=i;
260 theList->tag=tagBuf;
261 theList->format=formatBuf;
262 *Info=theList;
263}
264
265
266const CssmAutoDbRecordAttributeInfo &
267KeychainSchemaImpl::primaryKeyInfosFor(CSSM_DB_RECORDTYPE recordType) const
268{
269 PrimaryKeyInfoMap::const_iterator it;
270 it = mPrimaryKeyInfoMap.find(recordType);
271
272 if (it == mPrimaryKeyInfoMap.end())
273 MacOSError::throwMe(errSecNoSuchClass); // @@@ Not really but whatever.
274
275 return *it->second;
276}
277
278bool
279KeychainSchemaImpl::operator <(const KeychainSchemaImpl &other) const
280{
281 return mDatabaseInfoMap < other.mDatabaseInfoMap;
282}
283
284bool
285KeychainSchemaImpl::operator ==(const KeychainSchemaImpl &other) const
286{
287 return mDatabaseInfoMap == other.mDatabaseInfoMap;
288}
289
290void
291KeychainSchemaImpl::didCreateRelation(CSSM_DB_RECORDTYPE relationID,
292 const char *inRelationName,
293 uint32 inNumberOfAttributes,
294 const CSSM_DB_SCHEMA_ATTRIBUTE_INFO *pAttributeInfo,
295 uint32 inNumberOfIndexes,
296 const CSSM_DB_SCHEMA_INDEX_INFO *pIndexInfo)
297{
298 StLock<Mutex>_(mMutex);
299
300 if (CSSM_DB_RECORDTYPE_SCHEMA_START <= relationID
301 && relationID < CSSM_DB_RECORDTYPE_SCHEMA_END)
302 return;
303
427c49bc
A
304 // if our schema is already in the map, return
305 if (mPrimaryKeyInfoMap.find(relationID) != mPrimaryKeyInfoMap.end())
306 {
307 return;
308 }
309
b1ab9ed8
A
310 RelationInfoMap &rim = mDatabaseInfoMap[relationID];
311 for (uint32 ix = 0; ix < inNumberOfAttributes; ++ix)
312 rim[pAttributeInfo[ix].AttributeId] = pAttributeInfo[ix].DataType;
313
427c49bc
A
314 CssmAutoDbRecordAttributeInfo *infos = new CssmAutoDbRecordAttributeInfo();
315
b1ab9ed8 316 mPrimaryKeyInfoMap.
427c49bc
A
317 insert(PrimaryKeyInfoMap::value_type(relationID, infos));
318 infos->DataRecordType = relationID;
b1ab9ed8
A
319 for (uint32 ix = 0; ix < inNumberOfIndexes; ++ix)
320 if (pIndexInfo[ix].IndexType == CSSM_DB_INDEX_UNIQUE)
321 {
427c49bc 322 CssmDbAttributeInfo &info = infos->add();
b1ab9ed8
A
323 info.AttributeNameFormat = CSSM_DB_ATTRIBUTE_NAME_AS_INTEGER;
324 info.Label.AttributeID = pIndexInfo[ix].AttributeId;
325 info.AttributeFormat = rim[info.Label.AttributeID];
326 }
327}
328
329
427c49bc
A
330
331KeychainSchema::~KeychainSchema()
332
333{
334}
335
336
337
b1ab9ed8
A
338struct Event
339{
340 SecKeychainEvent eventCode;
341 PrimaryKey primaryKey;
342};
343typedef std::list<Event> EventList;
344
345#define SYSTEM_KEYCHAIN_CHECK_UNIX_BASE_NAME "/var/run/systemkeychaincheck"
346#define SYSTEM_KEYCHAIN_CHECK_UNIX_DOMAIN_SOCKET_NAME (SYSTEM_KEYCHAIN_CHECK_UNIX_BASE_NAME ".socket")
347#define SYSTEM_KEYCHAIN_CHECK_COMPLETE_FILE_NAME (SYSTEM_KEYCHAIN_CHECK_UNIX_BASE_NAME ".done")
348
349static void check_system_keychain()
350{
351 // sadly we can't use XPC here, XPC_DOMAIN_TYPE_SYSTEM doesn't exist yet. Also xpc-helper uses the
352 // keychain API (I assume for checking codesign things). So we use Unix Domain Sockets.
353
354 // NOTE: if we hit a system error we attempt to log it, and then just don't check the system keychain.
355 // In theory a system might be able to recover from this state if we let it try to muddle along, and
356 // past behaviour didn't even try this hard to do the keychain check. In particular we could be in a
357 // sandbox'ed process. So we just do our best and let another process try again.
358
359 struct stat keycheck_file_info;
360 if (stat(SYSTEM_KEYCHAIN_CHECK_COMPLETE_FILE_NAME, &keycheck_file_info) < 0) {
361 int server_fd = socket(PF_UNIX, SOCK_STREAM, 0);
362 if (server_fd < 0) {
363 syslog(LOG_ERR, "Can't get socket (%m) system keychain may be unchecked");
364 return;
365 }
366
367 struct sockaddr_un keychain_check_server_address;
368 keychain_check_server_address.sun_family = AF_UNIX;
369 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)) {
370 // It would be nice if we could compile time assert this
427c49bc 371 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));
b1ab9ed8
A
372 close(server_fd);
373 return;
374 }
375 keychain_check_server_address.sun_len = SUN_LEN(&keychain_check_server_address);
376
377 int rc = connect(server_fd, (struct sockaddr *)&keychain_check_server_address, keychain_check_server_address.sun_len);
378 if (rc < 0) {
379 syslog(LOG_ERR, "Can not connect to %s: %m", SYSTEM_KEYCHAIN_CHECK_UNIX_DOMAIN_SOCKET_NAME);
380 close(server_fd);
381 return;
382 }
383
384 // 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)
385 char byte;
386 ssize_t read_size = read(server_fd, &byte, 1);
387 if (read_size < 0) {
388 syslog(LOG_ERR, "Error reading from system keychain checker: %m");
389 }
390
391 close(server_fd);
392 return;
393 }
394}
395
396//
397// KeychainImpl
398//
399KeychainImpl::KeychainImpl(const Db &db)
fa7225c8 400: mCacheTimer(NULL), mSuppressTickle(false), mAttemptedUpgrade(false), mDbItemMapMutex(Mutex::recursive), mDbDeletedItemMapMutex(Mutex::recursive),
e3d460c9 401 mInCache(false), mDb(db), mCustomUnlockCreds (this), mIsInBatchMode (false), mMutex(Mutex::recursive)
b1ab9ed8
A
402{
403 dispatch_once(&SecKeychainSystemKeychainChecked, ^{
404 check_system_keychain();
405 });
406 mDb->defaultCredentials(this); // install activation hook
407 mEventBuffer = new EventBuffer;
408}
409
410KeychainImpl::~KeychainImpl()
411{
412 try
413 {
414 // Remove ourselves from the cache if we are in it.
415 // fprintf(stderr, "Removing %p from storage manager cache.\n", handle(false));
416 globals().storageManager.removeKeychain(dlDbIdentifier(), this);
417 delete mEventBuffer;
418 }
419 catch(...)
420 {
421 }
422}
423
424Mutex*
fa7225c8 425KeychainImpl::getMutexForObject() const
b1ab9ed8
A
426{
427 return globals().storageManager.getStorageManagerMutex();
428}
429
430Mutex*
431KeychainImpl::getKeychainMutex()
432{
433 return &mMutex;
434}
435
436void KeychainImpl::aboutToDestruct()
437{
438 // remove me from the global cache, we are done
439 // fprintf(stderr, "Destructing keychain object\n");
440 DLDbIdentifier identifier = dlDbIdentifier();
441 globals().storageManager.removeKeychain(identifier, this);
442}
443
444bool
445KeychainImpl::operator ==(const KeychainImpl &keychain) const
446{
447 return dlDbIdentifier() == keychain.dlDbIdentifier();
448}
449
450KCCursor
451KeychainImpl::createCursor(SecItemClass itemClass, const SecKeychainAttributeList *attrList)
452{
453 StLock<Mutex>_(mMutex);
454
455 StorageManager::KeychainList keychains;
456 keychains.push_back(Keychain(this));
457 return KCCursor(keychains, itemClass, attrList);
458}
459
460KCCursor
461KeychainImpl::createCursor(const SecKeychainAttributeList *attrList)
462{
463 StLock<Mutex>_(mMutex);
464
465 StorageManager::KeychainList keychains;
466 keychains.push_back(Keychain(this));
467 return KCCursor(keychains, attrList);
468}
469
470void
471KeychainImpl::create(UInt32 passwordLength, const void *inPassword)
472{
473 StLock<Mutex>_(mMutex);
474
475 if (!inPassword)
476 {
477 create();
478 return;
479 }
480
481 Allocator &alloc = Allocator::standard();
482
483 // @@@ Share this instance
484
485 const CssmData password(const_cast<void *>(inPassword), passwordLength);
486 AclFactory::PasswordChangeCredentials pCreds (password, alloc);
487 AclFactory::AnyResourceContext rcc(pCreds);
488 create(&rcc);
fa7225c8
A
489
490 // Now that we've created, trigger setting the defaultCredentials
491 mDb->open();
b1ab9ed8
A
492}
493
494void KeychainImpl::create(ConstStringPtr inPassword)
495{
496 StLock<Mutex>_(mMutex);
497
498 if ( inPassword )
499 create(static_cast<UInt32>(inPassword[0]), &inPassword[1]);
500 else
501 create();
502}
503
504void
505KeychainImpl::create()
506{
507 StLock<Mutex>_(mMutex);
508
509 AclFactory aclFactory;
510 AclFactory::AnyResourceContext rcc(aclFactory.unlockCred());
511 create(&rcc);
fa7225c8
A
512
513 // Now that we've created, trigger setting the defaultCredentials
514 mDb->open();
b1ab9ed8
A
515}
516
517void KeychainImpl::createWithBlob(CssmData &blob)
518{
519 StLock<Mutex>_(mMutex);
520
521 mDb->dbInfo(&Schema::DBInfo);
522 AclFactory aclFactory;
523 AclFactory::AnyResourceContext rcc(aclFactory.unlockCred());
524 mDb->resourceControlContext (&rcc);
525 try
526 {
527 mDb->createWithBlob(blob);
528 }
529 catch (...)
530 {
531 mDb->resourceControlContext(NULL);
532 mDb->dbInfo(NULL);
533 throw;
534 }
535 mDb->resourceControlContext(NULL);
536 mDb->dbInfo(NULL); // Clear the schema (to not break an open call later)
537 globals().storageManager.created(Keychain(this));
538
539 KCEventNotifier::PostKeychainEvent (kSecKeychainListChangedEvent, this, NULL);
540}
541
542void
543KeychainImpl::create(const ResourceControlContext *rcc)
544{
545 StLock<Mutex>_(mMutex);
546
547 mDb->dbInfo(&Schema::DBInfo); // Set the schema (to force a create)
548 mDb->resourceControlContext(rcc);
549 try
550 {
551 mDb->create();
552 }
553 catch (...)
554 {
555 mDb->resourceControlContext(NULL);
556 mDb->dbInfo(NULL); // Clear the schema (to not break an open call later)
557 throw;
558 }
559 mDb->resourceControlContext(NULL);
560 mDb->dbInfo(NULL); // Clear the schema (to not break an open call later)
561 globals().storageManager.created(Keychain(this));
562}
563
564void
565KeychainImpl::open()
566{
567 StLock<Mutex>_(mMutex);
568
569 mDb->open();
570}
571
572void
573KeychainImpl::lock()
574{
575 StLock<Mutex>_(mMutex);
576
577 mDb->lock();
578}
579
580void
581KeychainImpl::unlock()
582{
583 StLock<Mutex>_(mMutex);
584
585 mDb->unlock();
586}
587
588void
589KeychainImpl::unlock(const CssmData &password)
590{
591 StLock<Mutex>_(mMutex);
592
593 mDb->unlock(password);
594}
595
596void
597KeychainImpl::unlock(ConstStringPtr password)
598{
599 StLock<Mutex>_(mMutex);
600
601 if (password)
602 {
603 const CssmData data(const_cast<unsigned char *>(&password[1]), password[0]);
604 unlock(data);
605 }
606 else
607 unlock();
608}
609
610void
427c49bc 611KeychainImpl::stash()
b1ab9ed8 612{
427c49bc 613 StLock<Mutex>_(mMutex);
b1ab9ed8 614
427c49bc 615 mDb->stash();
b1ab9ed8
A
616}
617
427c49bc
A
618void
619KeychainImpl::stashCheck()
b1ab9ed8 620{
427c49bc 621 StLock<Mutex>_(mMutex);
b1ab9ed8 622
427c49bc
A
623 mDb->stashCheck();
624}
b1ab9ed8 625
427c49bc
A
626void
627KeychainImpl::getSettings(uint32 &outIdleTimeOut, bool &outLockOnSleep)
628{
629 StLock<Mutex>_(mMutex);
b1ab9ed8 630
427c49bc 631 mDb->getSettings(outIdleTimeOut, outLockOnSleep);
b1ab9ed8
A
632}
633
b1ab9ed8
A
634void
635KeychainImpl::setSettings(uint32 inIdleTimeOut, bool inLockOnSleep)
636{
637 StLock<Mutex>_(mMutex);
638
639 // The .Mac syncing code only makes sense for the AppleFile CSP/DL,
640 // but other DLs such as the OCSP and LDAP DLs do not expose a way to
641 // change settings or the password. To make a minimal change that only affects
642 // the smartcard case, we only look for that CSP/DL
643
644 bool isSmartcard = (mDb->dl()->guid() == gGuidAppleSdCSPDL);
645
646 // get the old keychain blob so that we can tell .Mac to resync it
647 CssmAutoData oldBlob(mDb ->allocator());
648 if (!isSmartcard)
649 mDb->copyBlob(oldBlob.get());
650
651 mDb->setSettings(inIdleTimeOut, inLockOnSleep);
b1ab9ed8
A
652}
653
654void
655KeychainImpl::changePassphrase(UInt32 oldPasswordLength, const void *oldPassword,
656 UInt32 newPasswordLength, const void *newPassword)
657{
658 StLock<Mutex>_(mMutex);
659
660 bool isSmartcard = (mDb->dl()->guid() == gGuidAppleSdCSPDL);
661
662 TrackingAllocator allocator(Allocator::standard());
663 AutoCredentials cred = AutoCredentials(allocator);
664 if (oldPassword)
665 {
666 const CssmData &oldPass = *new(allocator) CssmData(const_cast<void *>(oldPassword), oldPasswordLength);
667 TypedList &oldList = *new(allocator) TypedList(allocator, CSSM_SAMPLE_TYPE_KEYCHAIN_LOCK);
668 oldList.append(new(allocator) ListElement(CSSM_SAMPLE_TYPE_PASSWORD));
669 oldList.append(new(allocator) ListElement(oldPass));
670 cred += oldList;
671 }
672
673 if (newPassword)
674 {
675 const CssmData &newPass = *new(allocator) CssmData(const_cast<void *>(newPassword), newPasswordLength);
676 TypedList &newList = *new(allocator) TypedList(allocator, CSSM_SAMPLE_TYPE_KEYCHAIN_CHANGE_LOCK);
677 newList.append(new(allocator) ListElement(CSSM_SAMPLE_TYPE_PASSWORD));
678 newList.append(new(allocator) ListElement(newPass));
679 cred += newList;
680 }
681
682 // get the old keychain blob so that we can tell .Mac to resync it
683 CssmAutoData oldBlob(mDb->allocator());
684 if (!isSmartcard)
685 mDb->copyBlob(oldBlob.get());
686
687 mDb->changePassphrase(&cred);
b1ab9ed8
A
688}
689
690void
691KeychainImpl::changePassphrase(ConstStringPtr oldPassword, ConstStringPtr newPassword)
692{
693 StLock<Mutex>_(mMutex);
694
695 const void *oldPtr, *newPtr;
696 UInt32 oldLen, newLen;
697 if (oldPassword)
698 {
699 oldLen = oldPassword[0];
700 oldPtr = oldPassword + 1;
701 }
702 else
703 {
704 oldLen = 0;
705 oldPtr = NULL;
706 }
707
708 if (newPassword)
709 {
710 newLen = newPassword[0];
711 newPtr = newPassword + 1;
712 }
713 else
714 {
715 newLen = 0;
716 newPtr = NULL;
717 }
718
719 changePassphrase(oldLen, oldPtr, newLen, newPtr);
720}
721
722void
723KeychainImpl::authenticate(const CSSM_ACCESS_CREDENTIALS *cred)
724{
725 StLock<Mutex>_(mMutex);
726
727 if (!exists())
728 MacOSError::throwMe(errSecNoSuchKeychain);
729
427c49bc 730 MacOSError::throwMe(errSecUnimplemented);
b1ab9ed8
A
731}
732
733UInt32
734KeychainImpl::status() const
735{
b04fe171
A
736 StLock<Mutex>_(mMutex);
737
b1ab9ed8
A
738 // @@@ We should figure out the read/write status though a DL passthrough
739 // or some other way. Also should locked be unlocked read only or just
740 // read-only?
741 return (mDb->isLocked() ? 0 : kSecUnlockStateStatus | kSecWritePermStatus)
742 | kSecReadPermStatus;
743}
744
745bool
746KeychainImpl::exists()
747{
748 StLock<Mutex>_(mMutex);
749
750 bool exists = true;
751 try
752 {
753 open();
754 // Ok to leave the mDb open since it will get closed when it goes away.
755 }
756 catch (const CssmError &e)
757 {
758 if (e.osStatus() != CSSMERR_DL_DATASTORE_DOESNOT_EXIST)
759 throw;
760 exists = false;
761 }
762
763 return exists;
764}
765
766bool
767KeychainImpl::isActive() const
768{
769 return mDb->isActive();
770}
771
772void KeychainImpl::completeAdd(Item &inItem, PrimaryKey &primaryKey)
773{
b1ab9ed8
A
774 // The inItem shouldn't be in the cache yet
775 assert(!inItem->inCache());
776
777 // Insert inItem into mDbItemMap with key primaryKey. p.second will be
778 // true if it got inserted. If not p.second will be false and p.first
779 // will point to the current entry with key primaryKey.
e3d460c9 780 StLock<Mutex> _(mDbItemMapMutex);
b1ab9ed8
A
781 pair<DbItemMap::iterator, bool> p =
782 mDbItemMap.insert(DbItemMap::value_type(primaryKey, inItem.get()));
783 if (!p.second)
784 {
785 // There was already an ItemImpl * in mDbItemMap with key
e3d460c9 786 // primaryKey. Remove it, and try the add again.
b1ab9ed8
A
787 ItemImpl *oldItem = p.first->second;
788
e3d460c9 789 // @@@ If this happens we are breaking our API contract of
b1ab9ed8
A
790 // uniquifying items. We really need to insert the item into the
791 // map before we start the add. And have the item be in an
792 // "is being added" state.
fa7225c8 793 secnotice("keychain", "add of new item %p somehow replaced %p",
b1ab9ed8 794 inItem.get(), oldItem);
e3d460c9
A
795
796 mDbItemMap.erase(p.first);
797 oldItem->inCache(false);
798 forceRemoveFromCache(oldItem);
799 mDbItemMap.insert(DbItemMap::value_type(primaryKey, inItem.get()));
b1ab9ed8
A
800 }
801
802 inItem->inCache(true);
803}
804
805void
806KeychainImpl::addCopy(Item &inItem)
807{
866f8763 808 StLock<Mutex>_(mMutex);
b04fe171 809
b1ab9ed8
A
810 Keychain keychain(this);
811 PrimaryKey primaryKey = inItem->addWithCopyInfo(keychain, true);
812 completeAdd(inItem, primaryKey);
813 postEvent(kSecAddEvent, inItem);
814}
815
816void
817KeychainImpl::add(Item &inItem)
818{
866f8763 819 StLock<Mutex>_(mMutex);
b04fe171 820
b1ab9ed8
A
821 Keychain keychain(this);
822 PrimaryKey primaryKey = inItem->add(keychain);
823 completeAdd(inItem, primaryKey);
824 postEvent(kSecAddEvent, inItem);
825}
826
827void
828KeychainImpl::didUpdate(const Item &inItem, PrimaryKey &oldPK,
829 PrimaryKey &newPK)
830{
831 // If the primary key hasn't changed we don't need to update mDbItemMap.
832 if (oldPK != newPK)
833 {
834 // If inItem isn't in the cache we don't need to update mDbItemMap.
835 assert(inItem->inCache());
836 if (inItem->inCache())
837 {
e3d460c9 838 StLock<Mutex> _(mDbItemMapMutex);
b1ab9ed8
A
839 // First remove the entry for inItem in mDbItemMap with key oldPK.
840 DbItemMap::iterator it = mDbItemMap.find(oldPK);
841 if (it != mDbItemMap.end() && (ItemImpl*) it->second == inItem.get())
842 mDbItemMap.erase(it);
843
844 // Insert inItem into mDbItemMap with key newPK. p.second will be
845 // true if it got inserted. If not p.second will be false and
846 // p.first will point to the current entry with key newPK.
847 pair<DbItemMap::iterator, bool> p =
848 mDbItemMap.insert(DbItemMap::value_type(newPK, inItem.get()));
849 if (!p.second)
850 {
851 // There was already an ItemImpl * in mDbItemMap with key
e3d460c9 852 // primaryKey. Remove it, and try the add again.
b1ab9ed8
A
853 ItemImpl *oldItem = p.first->second;
854
e3d460c9 855 // @@@ If this happens we are breaking our API contract of
b1ab9ed8
A
856 // uniquifying items. We really need to insert the item into
857 // the map with the new primary key before we start the update.
858 // And have the item be in an "is being updated" state.
fa7225c8 859 secnotice("keychain", "update of item %p somehow replaced %p",
b1ab9ed8 860 inItem.get(), oldItem);
e3d460c9
A
861
862 mDbItemMap.erase(p.first);
863 oldItem->inCache(false);
864 forceRemoveFromCache(oldItem);
865 mDbItemMap.insert(DbItemMap::value_type(newPK, inItem.get()));
b1ab9ed8
A
866 }
867 }
868 }
e3d460c9 869
fa7225c8
A
870 // Item updates now are technically a delete and re-add, so post these events instead of kSecUpdateEvent
871 postEvent(kSecDeleteEvent, inItem, oldPK);
872 postEvent(kSecAddEvent, inItem);
b1ab9ed8
A
873}
874
875void
876KeychainImpl::deleteItem(Item &inoutItem)
877{
866f8763 878 StLock<Mutex>_(mMutex);
b04fe171 879
b1ab9ed8 880 {
b1ab9ed8
A
881 // item must be persistent
882 if (!inoutItem->isPersistent())
883 MacOSError::throwMe(errSecInvalidItemRef);
884
fa7225c8 885 secinfo("kcnotify", "starting deletion of item %p", inoutItem.get());
e3d460c9 886
b1ab9ed8
A
887 DbUniqueRecord uniqueId = inoutItem->dbUniqueRecord();
888 PrimaryKey primaryKey = inoutItem->primaryKey();
889 uniqueId->deleteRecord();
890
e3d460c9
A
891 // Move the item from mDbItemMap to mDbDeletedItemMap. We need the item
892 // to give to the client process when we receive the kSecDeleteEvent
893 // notification, but if that notification never arrives, we don't want
894 // the item hanging around. When didDeleteItem is called by CCallbackMgr,
895 // we'll remove all traces of the item.
896
897 if (inoutItem->inCache()) {
898 StLock<Mutex> _(mDbItemMapMutex);
899 StLock<Mutex> __(mDbDeletedItemMapMutex);
900 // Only look for it if it's in the cache
901 DbItemMap::iterator it = mDbItemMap.find(primaryKey);
902
903 if (it != mDbItemMap.end() && (ItemImpl*) it->second == inoutItem.get()) {
904 mDbDeletedItemMap.insert(DbItemMap::value_type(primaryKey, it->second));
905 mDbItemMap.erase(it);
906 }
907 }
b1ab9ed8
A
908
909 // Post the notification for the item deletion with
910 // the primaryKey obtained when the item still existed
911 }
912
913 postEvent(kSecDeleteEvent, inoutItem);
914}
915
fa7225c8
A
916void KeychainImpl::changeDatabase(CssmClient::Db db)
917{
918 StLock<Mutex>_(mDbMutex);
919 mDb = db;
920 mDb->defaultCredentials(this);
921}
922
b1ab9ed8
A
923
924CssmClient::CSP
925KeychainImpl::csp()
926{
927 StLock<Mutex>_(mMutex);
928
866f8763 929 if (!(mDb->dl()->subserviceMask() & CSSM_SERVICE_CSP))
b1ab9ed8
A
930 MacOSError::throwMe(errSecInvalidKeychain);
931
932 // Try to cast first to a CSPDL to handle case where we don't have an SSDb
933 try
934 {
935 CssmClient::CSPDL cspdl(dynamic_cast<CssmClient::CSPDLImpl *>(&*mDb->dl()));
936 return CSP(cspdl);
937 }
938 catch (...)
939 {
940 SSDbImpl* impl = dynamic_cast<SSDbImpl *>(&(*mDb));
941 if (impl == NULL)
942 {
943 CssmError::throwMe(CSSMERR_CSSM_INVALID_POINTER);
944 }
945
946 SSDb ssDb(impl);
947 return ssDb->csp();
948 }
949}
950
951PrimaryKey
952KeychainImpl::makePrimaryKey(CSSM_DB_RECORDTYPE recordType, DbUniqueRecord &uniqueId)
953{
954 StLock<Mutex>_(mMutex);
955
956 DbAttributes primaryKeyAttrs(uniqueId->database());
957 primaryKeyAttrs.recordType(recordType);
958 gatherPrimaryKeyAttributes(primaryKeyAttrs);
959 uniqueId->get(&primaryKeyAttrs, NULL);
960 return PrimaryKey(primaryKeyAttrs);
961}
962
e3d460c9
A
963PrimaryKey
964KeychainImpl::makePrimaryKey(CSSM_DB_RECORDTYPE recordType, DbAttributes* currentAttributes)
965{
966 StLock<Mutex>_(mMutex);
967
968 DbAttributes primaryKeyAttrs;
969 primaryKeyAttrs.recordType(recordType);
970 gatherPrimaryKeyAttributes(primaryKeyAttrs);
971
972 for(int i = 0; i < primaryKeyAttrs.size(); i++) {
973 CssmDbAttributeData& attr = primaryKeyAttrs[i];
974
975 CssmDbAttributeData * actual = currentAttributes->find(attr.info());
976 if(actual) {
977 attr.set(*actual, Allocator::standard());
978 }
979 }
980 return PrimaryKey(primaryKeyAttrs);
981}
982
b1ab9ed8
A
983const CssmAutoDbRecordAttributeInfo &
984KeychainImpl::primaryKeyInfosFor(CSSM_DB_RECORDTYPE recordType)
985{
986 StLock<Mutex>_(mMutex);
987
988 try
989 {
990 return keychainSchema()->primaryKeyInfosFor(recordType);
991 }
992 catch (const CommonError &error)
993 {
994 switch (error.osStatus())
995 {
996 case errSecNoSuchClass:
997 case CSSMERR_DL_INVALID_RECORDTYPE:
998 resetSchema();
999 return keychainSchema()->primaryKeyInfosFor(recordType);
1000 default:
1001 throw;
1002 }
1003 }
1004}
1005
1006void KeychainImpl::gatherPrimaryKeyAttributes(DbAttributes& primaryKeyAttrs)
1007{
e3d460c9 1008 StLock<Mutex> _(mMutex);
b1ab9ed8
A
1009
1010 const CssmAutoDbRecordAttributeInfo &infos =
1011 primaryKeyInfosFor(primaryKeyAttrs.recordType());
1012
1013 // @@@ fix this to not copy info.
1014 for (uint32 i = 0; i < infos.size(); i++)
1015 primaryKeyAttrs.add(infos.at(i));
1016}
1017
1018ItemImpl *
1019KeychainImpl::_lookupItem(const PrimaryKey &primaryKey)
1020{
e3d460c9 1021 StLock<Mutex> _(mDbItemMapMutex);
b1ab9ed8
A
1022 DbItemMap::iterator it = mDbItemMap.find(primaryKey);
1023 if (it != mDbItemMap.end())
1024 {
e3d460c9 1025 return it->second;
b1ab9ed8
A
1026 }
1027
1028 return NULL;
1029}
1030
e3d460c9
A
1031ItemImpl *
1032KeychainImpl::_lookupDeletedItemOnly(const PrimaryKey &primaryKey)
1033{
fa7225c8 1034 StLock<Mutex> _(mDbDeletedItemMapMutex);
e3d460c9
A
1035 DbItemMap::iterator it = mDbDeletedItemMap.find(primaryKey);
1036 if (it != mDbDeletedItemMap.end())
1037 {
1038 return it->second;
1039 }
1040
1041 return NULL;
1042}
1043
b1ab9ed8
A
1044Item
1045KeychainImpl::item(const PrimaryKey &primaryKey)
1046{
1047 StLock<Mutex>_(mMutex);
1048
1049 // Lookup the item in the map while holding the apiLock.
1050 ItemImpl *itemImpl = _lookupItem(primaryKey);
e3d460c9 1051 if (itemImpl) {
b1ab9ed8 1052 return Item(itemImpl);
e3d460c9 1053 }
b1ab9ed8
A
1054
1055 try
1056 {
1057 // We didn't find it so create a new item with just a keychain and
fa7225c8 1058 // a primary key. Some other thread might have beaten
b1ab9ed8
A
1059 // us to creating this item and adding it to the cache. If that
1060 // happens we retry the lookup.
1061 return Item(this, primaryKey);
1062 }
1063 catch (const MacOSError &e)
1064 {
1065 // If the item creation failed because some other thread already
1066 // inserted this item into the cache we retry the lookup.
1067 if (e.osStatus() == errSecDuplicateItem)
1068 {
1069 // Lookup the item in the map while holding the apiLock.
1070 ItemImpl *itemImpl = _lookupItem(primaryKey);
1071 if (itemImpl)
1072 return Item(itemImpl);
1073 }
1074 throw;
1075 }
1076}
e3d460c9
A
1077// Check for an item that may have been deleted.
1078Item
1079KeychainImpl::itemdeleted(const PrimaryKey& primaryKey) {
1080 StLock<Mutex>_(mMutex);
1081
1082 Item i = _lookupDeletedItemOnly(primaryKey);
1083 if(i.get()) {
1084 return i;
1085 } else {
1086 return item(primaryKey);
1087 }
1088}
b1ab9ed8
A
1089
1090
1091Item
1092KeychainImpl::item(CSSM_DB_RECORDTYPE recordType, DbUniqueRecord &uniqueId)
1093{
1094 StLock<Mutex>_(mMutex);
1095
1096 PrimaryKey primaryKey = makePrimaryKey(recordType, uniqueId);
1097 {
1098 // Lookup the item in the map while holding the apiLock.
1099 ItemImpl *itemImpl = _lookupItem(primaryKey);
1100
1101 if (itemImpl)
1102 {
1103 return Item(itemImpl);
1104 }
1105 }
1106
1107 try
1108 {
1109 // We didn't find it so create a new item with a keychain, a primary key
1110 // and a DbUniqueRecord. However since we aren't holding
1111 // globals().apiLock anymore some other thread might have beaten
1112 // us to creating this item and adding it to the cache. If that
1113 // happens we retry the lookup.
1114 return Item(this, primaryKey, uniqueId);
1115 }
1116 catch (const MacOSError &e)
1117 {
1118 // If the item creation failed because some other thread already
1119 // inserted this item into the cache we retry the lookup.
1120 if (e.osStatus() == errSecDuplicateItem)
1121 {
1122 // Lookup the item in the map while holding the apiLock.
1123 ItemImpl *itemImpl = _lookupItem(primaryKey);
1124 if (itemImpl)
1125 return Item(itemImpl);
1126 }
1127 throw;
1128 }
1129}
1130
1131KeychainSchema
1132KeychainImpl::keychainSchema()
1133{
1134 StLock<Mutex>_(mMutex);
1135 if (!mKeychainSchema)
1136 mKeychainSchema = KeychainSchema(mDb);
1137
1138 return mKeychainSchema;
1139}
1140
1141void KeychainImpl::resetSchema()
1142{
1143 mKeychainSchema = NULL; // re-fetch it from db next time
1144}
1145
1146
1147// Called from DbItemImpl's constructor (so it is only partially constructed),
1148// add it to the map.
1149void
1150KeychainImpl::addItem(const PrimaryKey &primaryKey, ItemImpl *dbItemImpl)
1151{
1152 StLock<Mutex>_(mMutex);
1153
1154 // The dbItemImpl shouldn't be in the cache yet
1155 assert(!dbItemImpl->inCache());
1156
1157 // Insert dbItemImpl into mDbItemMap with key primaryKey. p.second will
1158 // be true if it got inserted. If not p.second will be false and p.first
1159 // will point to the current entry with key primaryKey.
e3d460c9 1160 StLock<Mutex> __(mDbItemMapMutex);
b1ab9ed8
A
1161 pair<DbItemMap::iterator, bool> p =
1162 mDbItemMap.insert(DbItemMap::value_type(primaryKey, dbItemImpl));
1163
1164 if (!p.second)
1165 {
1166 // There was already an ItemImpl * in mDbItemMap with key primaryKey.
1167 // There is a race condition here when being called in multiple threads
1168 // We might have added an item using add and received a notification at
1169 // the same time.
1170 MacOSError::throwMe(errSecDuplicateItem);
1171 }
1172
1173 dbItemImpl->inCache(true);
1174}
1175
1176void
1177KeychainImpl::didDeleteItem(ItemImpl *inItemImpl)
1178{
1179 StLock<Mutex>_(mMutex);
1180
1181 // Called by CCallbackMgr
fa7225c8 1182 secinfo("kcnotify", "%p notified that item %p was deleted", this, inItemImpl);
b1ab9ed8
A
1183 removeItem(inItemImpl->primaryKey(), inItemImpl);
1184}
1185
1186void
1187KeychainImpl::removeItem(const PrimaryKey &primaryKey, ItemImpl *inItemImpl)
1188{
1189 StLock<Mutex>_(mMutex);
1190
1191 // If inItemImpl isn't in the cache to begin with we are done.
1192 if (!inItemImpl->inCache())
1193 return;
1194
e3d460c9
A
1195 {
1196 StLock<Mutex> _(mDbItemMapMutex);
1197 DbItemMap::iterator it = mDbItemMap.find(primaryKey);
1198 if (it != mDbItemMap.end() && (ItemImpl*) it->second == inItemImpl) {
1199 mDbItemMap.erase(it);
1200 }
1201 } // drop mDbItemMapMutex
1202
1203 {
1204 StLock<Mutex> _(mDbDeletedItemMapMutex);
1205 DbItemMap::iterator it = mDbDeletedItemMap.find(primaryKey);
1206 if (it != mDbDeletedItemMap.end() && (ItemImpl*) it->second == inItemImpl) {
1207 mDbDeletedItemMap.erase(it);
1208 }
1209 } // drop mDbDeletedItemMapMutex
b1ab9ed8
A
1210
1211 inItemImpl->inCache(false);
1212}
1213
e3d460c9
A
1214void
1215KeychainImpl::forceRemoveFromCache(ItemImpl* inItemImpl) {
1216 try {
1217 // Wrap all this in a try-block and ignore all errors - we're trying to clean up these maps
1218 {
1219 StLock<Mutex> _(mDbItemMapMutex);
1220 for(DbItemMap::iterator it = mDbItemMap.begin(); it != mDbItemMap.end(); ) {
1221 if(it->second == inItemImpl) {
1222 // Increment the iterator, but use its pre-increment value for the erase
1223 it->second->inCache(false);
1224 mDbItemMap.erase(it++);
1225 } else {
1226 it++;
1227 }
1228 }
1229 } // drop mDbItemMapMutex
1230
1231 {
1232 StLock<Mutex> _(mDbDeletedItemMapMutex);
1233 for(DbItemMap::iterator it = mDbDeletedItemMap.begin(); it != mDbDeletedItemMap.end(); ) {
1234 if(it->second == inItemImpl) {
1235 // Increment the iterator, but use its pre-increment value for the erase
1236 it->second->inCache(false);
1237 mDbDeletedItemMap.erase(it++);
1238 } else {
1239 it++;
1240 }
1241 }
1242 } // drop mDbDeletedItemMapMutex
1243 } catch(UnixError ue) {
fa7225c8 1244 secnotice("keychain", "caught UnixError: %d %s", ue.unixError(), ue.what());
e3d460c9
A
1245 } catch (CssmError cssme) {
1246 const char* errStr = cssmErrorString(cssme.error);
fa7225c8 1247 secnotice("keychain", "caught CssmError: %d %s", (int) cssme.error, errStr);
e3d460c9 1248 } catch (MacOSError mose) {
fa7225c8 1249 secnotice("keychain", "MacOSError: %d", (int)mose.osStatus());
e3d460c9 1250 } catch(...) {
fa7225c8 1251 secnotice("keychain", "Unknown error");
e3d460c9
A
1252 }
1253}
1254
b1ab9ed8
A
1255void
1256KeychainImpl::getAttributeInfoForItemID(CSSM_DB_RECORDTYPE itemID,
1257 SecKeychainAttributeInfo **Info)
1258{
1259 StLock<Mutex>_(mMutex);
1260
1261 try
1262 {
1263 keychainSchema()->getAttributeInfoForRecordType(itemID, Info);
1264 }
1265 catch (const CommonError &error)
1266 {
1267 switch (error.osStatus())
1268 {
1269 case errSecNoSuchClass:
1270 case CSSMERR_DL_INVALID_RECORDTYPE:
1271 resetSchema();
1272 keychainSchema()->getAttributeInfoForRecordType(itemID, Info);
1273 default:
1274 throw;
1275 }
1276 }
1277}
1278
1279void
1280KeychainImpl::freeAttributeInfo(SecKeychainAttributeInfo *Info)
1281{
1282 free(Info->tag);
1283 free(Info->format);
1284 free(Info);
1285}
1286
1287CssmDbAttributeInfo
1288KeychainImpl::attributeInfoFor(CSSM_DB_RECORDTYPE recordType, UInt32 tag)
1289{
1290 StLock<Mutex>_(mMutex);
1291
1292 try
1293 {
1294 return keychainSchema()->attributeInfoFor(recordType, tag);
1295 }
1296 catch (const CommonError &error)
1297 {
1298 switch (error.osStatus())
1299 {
1300 case errSecNoSuchClass:
1301 case CSSMERR_DL_INVALID_RECORDTYPE:
1302 resetSchema();
1303 return keychainSchema()->attributeInfoFor(recordType, tag);
1304 default:
1305 throw;
1306 }
1307 }
1308}
1309
1310void
1311KeychainImpl::recode(const CssmData &data, const CssmData &extraData)
1312{
1313 StLock<Mutex>_(mMutex);
1314
1315 mDb->recode(data, extraData);
1316}
1317
1318void
1319KeychainImpl::copyBlob(CssmData &data)
1320{
1321 StLock<Mutex>_(mMutex);
1322
1323 mDb->copyBlob(data);
1324}
1325
1326void
1327KeychainImpl::setBatchMode(Boolean mode, Boolean rollback)
1328{
1329 StLock<Mutex>_(mMutex);
1330
1331 mDb->setBatchMode(mode, rollback);
1332 mIsInBatchMode = mode;
1333 if (!mode)
1334 {
1335 if (!rollback) // was batch mode being turned off without an abort?
1336 {
1337 // dump the buffer
1338 EventBuffer::iterator it = mEventBuffer->begin();
1339 while (it != mEventBuffer->end())
1340 {
1341 PrimaryKey primaryKey;
1342 if (it->item)
1343 {
1344 primaryKey = it->item->primaryKey();
1345 }
1346
1347 KCEventNotifier::PostKeychainEvent(it->kcEvent, mDb->dlDbIdentifier(), primaryKey);
1348
1349 ++it;
1350 }
1351
1352 }
1353
1354 // notify that a keychain has changed in too many ways to count
6b200bc3 1355 KCEventNotifier::PostKeychainEvent((SecKeychainEvent) kSecKeychainLeftBatchModeEvent);
b1ab9ed8
A
1356 mEventBuffer->clear();
1357 }
1358 else
1359 {
6b200bc3 1360 KCEventNotifier::PostKeychainEvent((SecKeychainEvent) kSecKeychainEnteredBatchModeEvent);
b1ab9ed8
A
1361 }
1362}
b1ab9ed8
A
1363void
1364KeychainImpl::postEvent(SecKeychainEvent kcEvent, ItemImpl* item)
fa7225c8
A
1365{
1366 postEvent(kcEvent, item, NULL);
1367}
1368
1369void
1370KeychainImpl::postEvent(SecKeychainEvent kcEvent, ItemImpl* item, PrimaryKey pk)
b1ab9ed8
A
1371{
1372 PrimaryKey primaryKey;
1373
fa7225c8
A
1374 if(pk.get()) {
1375 primaryKey = pk;
1376 } else {
b1ab9ed8
A
1377 StLock<Mutex>_(mMutex);
1378
1379 if (item != NULL)
1380 {
1381 primaryKey = item->primaryKey();
1382 }
1383 }
1384
1385 if (!mIsInBatchMode)
1386 {
1387 KCEventNotifier::PostKeychainEvent(kcEvent, mDb->dlDbIdentifier(), primaryKey);
1388 }
1389 else
1390 {
1391 StLock<Mutex>_(mMutex);
1392
1393 EventItem it;
1394 it.kcEvent = kcEvent;
1395 if (item != NULL)
1396 {
1397 it.item = item;
1398 }
1399
1400 mEventBuffer->push_back (it);
1401 }
1402}
1403
fa7225c8
A
1404void KeychainImpl::tickle() {
1405 if(!mSuppressTickle) {
1406 globals().storageManager.tickleKeychain(this);
1407 }
1408}
1409
e3d460c9
A
1410
1411bool KeychainImpl::performKeychainUpgradeIfNeeded() {
866f8763 1412 // Grab this keychain's mutex.
e3d460c9
A
1413 StLock<Mutex>_(mMutex);
1414
1415 if(!globals().integrityProtection()) {
fa7225c8 1416 secnotice("integrity", "skipping upgrade for %s due to global integrity protection being disabled", mDb->name());
e3d460c9
A
1417 return false;
1418 }
1419
1420 // We need a CSP database for 'upgrade' to be meaningful
1421 if((mDb->dl()->subserviceMask() & CSSM_SERVICE_CSP) == 0) {
1422 return false;
1423 }
1424
1425 // We only want to upgrade file-based Apple keychains. Check the GUID.
1426 if(mDb->dl()->guid() != gGuidAppleCSPDL) {
b04fe171 1427 secinfo("integrity", "skipping upgrade for %s due to guid mismatch\n", mDb->name());
e3d460c9
A
1428 return false;
1429 }
1430
1431 // If we've already attempted an upgrade on this keychain, don't bother again
1432 if(mAttemptedUpgrade) {
1433 return false;
1434 }
1435
1436 // Don't upgrade the System root certificate keychain (to make old tp code happy)
1437 if(strncmp(mDb->name(), SYSTEM_ROOT_STORE_PATH, strlen(SYSTEM_ROOT_STORE_PATH)) == 0) {
b04fe171 1438 secinfo("integrity", "skipping upgrade for %s\n", mDb->name());
e3d460c9
A
1439 return false;
1440 }
1441
1442 uint32 dbBlobVersion = SecurityServer::DbBlob::version_MacOS_10_0;
1443
1444 try {
1445 dbBlobVersion = mDb->dbBlobVersion();
1446 } catch (CssmError cssme) {
1447 if(cssme.error == CSSMERR_DL_DATASTORE_DOESNOT_EXIST) {
1448 // oh well! We tried to get the blob version of a database
1449 // that doesn't exist. It doesn't need migration, so do nothing.
fa7225c8 1450 secnotice("integrity", "dbBlobVersion() failed for a non-existent database");
e3d460c9
A
1451 return false;
1452 } else {
1453 // Some other error occurred. We can't upgrade this keychain, so fail.
1454 const char* errStr = cssmErrorString(cssme.error);
fa7225c8 1455 secnotice("integrity", "dbBlobVersion() failed for a CssmError: %d %s", (int) cssme.error, errStr);
e3d460c9
A
1456 return false;
1457 }
1458 } catch (...) {
fa7225c8 1459 secnotice("integrity", "dbBlobVersion() failed for an unknown reason");
e3d460c9
A
1460 return false;
1461 }
1462
e3d460c9 1463
e3d460c9 1464
fa7225c8
A
1465 // Check the location of this keychain
1466 string path = mDb->name();
1467 string keychainDbPath = StorageManager::makeKeychainDbFilename(path);
e3d460c9 1468
fa7225c8
A
1469 bool inHomeLibraryKeychains = StorageManager::pathInHomeLibraryKeychains(path);
1470
1471 string keychainDbSuffix = "-db";
1472 bool endsWithKeychainDb = (path.size() > keychainDbSuffix.size() && (0 == path.compare(path.size() - keychainDbSuffix.size(), keychainDbSuffix.size(), keychainDbSuffix)));
1473
1474 bool isSystemKeychain = (0 == path.compare("/Library/Keychains/System.keychain"));
1475
1476 bool result = false;
1477
1478 if(inHomeLibraryKeychains && endsWithKeychainDb && dbBlobVersion == SecurityServer::DbBlob::version_MacOS_10_0) {
1479 // something has gone horribly wrong: an old-versioned keychain has a .keychain-db name. Rename it.
1480 string basePath = path;
1481 basePath.erase(basePath.end()-3, basePath.end());
1482
1483 attemptKeychainRename(path, basePath, dbBlobVersion);
1484
1485 // If we moved to a good path, we might still want to perform the upgrade. Update our variables.
1486 path = mDb->name();
e3d460c9
A
1487
1488 try {
fa7225c8 1489 dbBlobVersion = mDb->dbBlobVersion();
e3d460c9
A
1490 } catch (CssmError cssme) {
1491 const char* errStr = cssmErrorString(cssme.error);
fa7225c8
A
1492 secnotice("integrity", "dbBlobVersion() after a rename failed for a CssmError: %d %s", (int) cssme.error, errStr);
1493 return false;
e3d460c9 1494 } catch (...) {
fa7225c8
A
1495 secnotice("integrity", "dbBlobVersion() failed for an unknown reason after a rename");
1496 return false;
1497 }
1498
1499 endsWithKeychainDb = (path.size() > keychainDbSuffix.size() && (0 == path.compare(path.size() - keychainDbSuffix.size(), keychainDbSuffix.size(), keychainDbSuffix)));
1500 keychainDbPath = StorageManager::makeKeychainDbFilename(path);
1501 secnotice("integrity", "after rename, our database thinks that it is %s", path.c_str());
1502 }
1503
1504 // Migrate an old keychain in ~/Library/Keychains
1505 if(inHomeLibraryKeychains && dbBlobVersion != SecurityServer::DbBlob::version_partition && !endsWithKeychainDb) {
1506 // We can only attempt to migrate an unlocked keychain.
1507 if(mDb->isLocked()) {
1508 // However, it's possible that while we weren't doing any keychain operations, someone upgraded the keychain,
1509 // and then locked it. No way around hitting the filesystem here: check for the existence of a new file and,
1510 // if no new file exists, quit.
1511 DLDbIdentifier mungedDLDbIdentifier = StorageManager::mungeDLDbIdentifier(mDb->dlDbIdentifier(), false);
1512 string mungedPath(mungedDLDbIdentifier.dbName());
1513
1514 // If this matches the file we already have, skip the upgrade. Otherwise, continue.
1515 if(mungedPath == path) {
1516 secnotice("integrity", "skipping upgrade for locked keychain %s\n", mDb->name());
1517 return false;
1518 }
1519 }
1520
1521 result = keychainMigration(path, dbBlobVersion, keychainDbPath, SecurityServer::DbBlob::version_partition);
1522 } else if(inHomeLibraryKeychains && dbBlobVersion == SecurityServer::DbBlob::version_partition && !endsWithKeychainDb) {
1523 // This is a new-style keychain with the wrong name, try to rename it
1524 attemptKeychainRename(path, keychainDbPath, dbBlobVersion);
1525 result = true;
1526 } else if(isSystemKeychain && dbBlobVersion == SecurityServer::DbBlob::version_partition) {
1527 // Try to "unupgrade" the system keychain, to clean up our old issues
1528 secnotice("integrity", "attempting downgrade for %s version %d (%d %d %d)", path.c_str(), dbBlobVersion, inHomeLibraryKeychains, endsWithKeychainDb, isSystemKeychain);
1529
1530 // First step: acquire the credentials to allow for ACL modification
1531 SecurityServer::SystemKeychainKey skk(kSystemUnlockFile);
1532 if(skk.valid()) {
1533 // We've managed to read the key; now, create credentials using it
1534 CssmClient::Key systemKeychainMasterKey(csp(), skk.key(), true);
1535 CssmClient::AclFactory::MasterKeyUnlockCredentials creds(systemKeychainMasterKey, Allocator::standard(Allocator::sensitive));
1536
1537 // Attempt the downgrade, using our master key as the ACL override
1538 result = keychainMigration(path, dbBlobVersion, path, SecurityServer::DbBlob::version_MacOS_10_0, creds.getAccessCredentials());
1539 } else {
1540 secnotice("integrity", "Couldn't read System.keychain key, skipping update");
e3d460c9 1541 }
fa7225c8 1542 } else {
b04fe171 1543 secinfo("integrity", "not attempting migration for %s version %d (%d %d %d)", path.c_str(), dbBlobVersion, inHomeLibraryKeychains, endsWithKeychainDb, isSystemKeychain);
fa7225c8
A
1544
1545 // Since we don't believe any migration needs to be done here, mark the
1546 // migration as "attempted" to short-circuit future checks.
1547 mAttemptedUpgrade = true;
1548 }
e3d460c9 1549
fa7225c8
A
1550 // We might have changed our location on disk. Let StorageManager know.
1551 globals().storageManager.registerKeychainImpl(this);
1552
1553 // if we attempted a migration, try to clean up leftover files from <rdar://problem/23950408> XARA backup have provided me with 12GB of login keychain copies
1554 if(result) {
1555 string pattern = path + "_*_backup";
1556 glob_t pglob = {};
1557 secnotice("integrity", "globbing for %s", pattern.c_str());
1558 int globresult = glob(pattern.c_str(), GLOB_MARK, NULL, &pglob);
1559 if(globresult == 0) {
1560 secnotice("integrity", "glob: %lu results", pglob.gl_pathc);
1561 if(pglob.gl_pathc > 10) {
1562 // There are more than 10 backup files, indicating a problem.
1563 // Delete all but one of them. Under rdar://23950408, they should all be identical.
1564 secnotice("integrity", "saving backup file: %s", pglob.gl_pathv[0]);
1565 for(int i = 1; i < pglob.gl_pathc; i++) {
1566 secnotice("integrity", "cleaning up backup file: %s", pglob.gl_pathv[i]);
1567 // ignore return code; this is a best-effort cleanup
1568 unlink(pglob.gl_pathv[i]);
1569 }
1570 }
1571
1572 struct stat st;
1573 bool pathExists = (::stat(path.c_str(), &st) == 0);
1574 bool keychainDbPathExists = (::stat(keychainDbPath.c_str(), &st) == 0);
1575
1576 if(!pathExists && keychainDbPathExists && pglob.gl_pathc >= 1) {
1577 // We have a file at keychainDbPath, no file at path, and at least one backup keychain file.
1578 //
1579 // Move the backup file to path, to simulate the current "split-world" view,
1580 // which copies from path to keychainDbPath, then modifies keychainDbPath.
1581 secnotice("integrity", "moving backup file %s to %s", pglob.gl_pathv[0], path.c_str());
1582 ::rename(pglob.gl_pathv[0], path.c_str());
1583 }
1584 }
e3d460c9 1585
fa7225c8 1586 globfree(&pglob);
e3d460c9
A
1587 }
1588
fa7225c8 1589 return result;
e3d460c9
A
1590}
1591
fa7225c8
A
1592bool KeychainImpl::keychainMigration(const string oldPath, const uint32 dbBlobVersion, const string newPath, const uint32 newBlobVersion, const AccessCredentials *cred) {
1593 secnotice("integrity", "going to migrate %s at version %d to", oldPath.c_str(), dbBlobVersion);
1594 secnotice("integrity", " %s at version %d", newPath.c_str(), newBlobVersion);
1595
1596 // We need to opportunistically perform the upgrade/reload dance.
1597 //
1598 // If the keychain is unlocked, try to upgrade it.
1599 // In either case, reload the database from disk.
fa7225c8 1600
866f8763
A
1601 // Try to grab the keychain mutex (although we should already have it)
1602 StLock<Mutex>_(mMutex);
fa7225c8
A
1603
1604 // Take the file lock on the existing database. We don't need to commit this txion, because we're not planning to
1605 // change the original keychain.
1606 FileLockTransaction fileLockmDb(mDb);
e3d460c9
A
1607
1608 // Let's reload this keychain to see if someone changed it on disk
1609 globals().storageManager.reloadKeychain(this);
1610
fa7225c8
A
1611 bool result = false;
1612
1613 try {
1614 // We can only attempt an upgrade if the keychain is currently unlocked
1615 // There's a TOCTTOU issue here, but it's going to be rare in practice, and the upgrade will simply fail.
1616 if(!mDb->isLocked()) {
1617 secnotice("integrity", "have a plan to migrate database %s", mDb->name());
1618 // Database blob is out of date. Attempt a migration.
1619 uint32 convertedVersion = attemptKeychainMigration(oldPath, dbBlobVersion, newPath, newBlobVersion, cred);
1620 if(convertedVersion == newBlobVersion) {
1621 secnotice("integrity", "conversion succeeded");
1622 result = true;
1623 } else {
1624 secnotice("integrity", "conversion failed, keychain is still %d", convertedVersion);
1625 }
1626 } else {
1627 secnotice("integrity", "keychain is locked, can't upgrade");
1628 }
1629 } catch (CssmError cssme) {
1630 const char* errStr = cssmErrorString(cssme.error);
1631 secnotice("integrity", "caught CssmError: %d %s", (int) cssme.error, errStr);
1632 } catch (...) {
1633 // Something went wrong, but don't worry about it.
1634 secnotice("integrity", "caught unknown error");
1635 }
1636
1637 // No matter if the migrator succeeded, we need to reload this keychain from disk.
1638 secnotice("integrity", "reloading keychain after migration");
1639 globals().storageManager.reloadKeychain(this);
1640 secnotice("integrity", "database %s is now version %d", mDb->name(), mDb->dbBlobVersion());
1641
1642 return result;
1643}
1644
1645// Make sure you have this keychain's mutex and write lock when you call this function!
1646uint32 KeychainImpl::attemptKeychainMigration(const string oldPath, const uint32 oldBlobVersion, const string newPath, const uint32 newBlobVersion, const AccessCredentials* cred) {
e3d460c9
A
1647 if(mDb->dbBlobVersion() == newBlobVersion) {
1648 // Someone else upgraded this, hurray!
fa7225c8 1649 secnotice("integrity", "reloaded keychain version %d, quitting", mDb->dbBlobVersion());
e3d460c9
A
1650 return newBlobVersion;
1651 }
1652
1653 mAttemptedUpgrade = true;
1654 uint32 newDbVersion = oldBlobVersion;
1655
fa7225c8
A
1656 if( (oldBlobVersion == SecurityServer::DbBlob::version_MacOS_10_0 && newBlobVersion == SecurityServer::DbBlob::version_partition) ||
1657 (oldBlobVersion == SecurityServer::DbBlob::version_partition && newBlobVersion == SecurityServer::DbBlob::version_MacOS_10_0 && cred != NULL)) {
1658 // Here's the upgrade outline:
1659 //
1660 // 1. Make a copy of the keychain with the new file path
1661 // 2. Open that keychain database.
1662 // 3. Recode it to use the new version.
1663 // 4. Notify the StorageManager that the DLDB identifier for this keychain has changed.
1664 //
1665 // If we're creating a new keychain file, on failure, try to delete the new file. Otherwise,
1666 // everyone will try to use it.
1667
1668 secnotice("integrity", "attempting migration from version %d to %d", oldBlobVersion, newBlobVersion);
e3d460c9 1669
fa7225c8
A
1670 Db db;
1671 bool newFile = (oldPath != newPath);
1672
1673 try {
1674 DLDbIdentifier dldbi(dlDbIdentifier().ssuid(), newPath.c_str(), dlDbIdentifier().dbLocation());
1675 if(newFile) {
1676 secnotice("integrity", "creating a new keychain at %s", newPath.c_str());
1677 db = mDb->cloneTo(dldbi);
1678 } else {
1679 secnotice("integrity", "using old keychain at %s", newPath.c_str());
1680 db = mDb;
1681 }
1682 FileLockTransaction fileLockDb(db);
1683
1684 if(newFile) {
1685 // since we're creating a completely new file, if this migration fails, delete the new file
1686 fileLockDb.setDeleteOnFailure();
1687 }
e3d460c9 1688
e3d460c9
A
1689 // Let the upgrade begin.
1690 newDbVersion = db->recodeDbToVersion(newBlobVersion);
1691 if(newDbVersion != newBlobVersion) {
1692 // Recoding failed. Don't proceed.
fa7225c8 1693 secnotice("integrity", "recodeDbToVersion failed, version is still %d", newDbVersion);
e3d460c9
A
1694 return newDbVersion;
1695 }
1696
fa7225c8 1697 secnotice("integrity", "recoded db successfully, adding extra integrity");
e3d460c9
A
1698
1699 Keychain keychain(db);
1700
1701 // Breaking abstraction, but what're you going to do?
1702 // Don't upgrade this keychain, since we just upgraded the DB
1703 // But the DB won't return any new data until the txion commits
1704 keychain->mAttemptedUpgrade = true;
fa7225c8 1705 keychain->mSuppressTickle = true;
e3d460c9
A
1706
1707 SecItemClass classes[] = {kSecGenericPasswordItemClass,
1708 kSecInternetPasswordItemClass,
1709 kSecPublicKeyItemClass,
1710 kSecPrivateKeyItemClass,
1711 kSecSymmetricKeyItemClass};
1712
1713 for(int i = 0; i < sizeof(classes) / sizeof(classes[0]); i++) {
1714 Item item;
1715 KCCursor kcc = keychain->createCursor(classes[i], NULL);
1716
1717 // During recoding, we might have deleted some corrupt keys.
1718 // Because of this, we might have zombie SSGroup records left in
1719 // the database that have no matching key. Tell the KCCursor to
1720 // delete these if found.
1721 // This will also try to suppress any other invalid items.
1722 kcc->setDeleteInvalidRecords(true);
1723
1724 while(kcc->next(item)) {
1725 try {
fa7225c8
A
1726 if(newBlobVersion == SecurityServer::DbBlob::version_partition) {
1727 // Force the item to set integrity. The keychain is confused about its version because it hasn't written to disk yet,
1728 // but if we've reached this point, the keychain supports integrity.
1729 item->setIntegrity(true);
1730 } else if(newBlobVersion == SecurityServer::DbBlob::version_MacOS_10_0) {
1731 // We're downgrading this keychain. Pass in whatever credentials our caller thinks will allow this ACL modification.
1732 item->removeIntegrity(cred);
1733 }
e3d460c9
A
1734 } catch(CssmError cssme) {
1735 // During recoding, we might have deleted some corrupt keys. Because of this, we might have zombie SSGroup records left in
1736 // the database that have no matching key. If we get a DL_RECORD_NOT_FOUND error, delete the matching item record.
1737 if (cssme.osStatus() == CSSMERR_DL_RECORD_NOT_FOUND) {
fa7225c8 1738 secnotice("integrity", "deleting corrupt (Not Found) record");
e3d460c9 1739 keychain->deleteItem(item);
b04fe171
A
1740 } else if(cssme.osStatus() == CSSMERR_CSP_INVALID_KEY) {
1741 secnotice("integrity", "deleting corrupt key record");
1742 keychain->deleteItem(item);
e3d460c9
A
1743 } else {
1744 throw;
1745 }
1746 }
1747 }
1748 }
1749
fa7225c8
A
1750 // Tell securityd we're done with the upgrade, to re-enable all protections
1751 db->recodeFinished();
1752
1753 // If we reach here, tell the file locks to commit the transaction and return the new blob version
1754 fileLockDb.success();
e3d460c9 1755
fa7225c8 1756 secnotice("integrity", "success, returning version %d", newDbVersion);
e3d460c9 1757 return newDbVersion;
fa7225c8
A
1758 } catch(UnixError ue) {
1759 secnotice("integrity", "caught UnixError: %d %s", ue.unixError(), ue.what());
1760 } catch (CssmError cssme) {
1761 const char* errStr = cssmErrorString(cssme.error);
1762 secnotice("integrity", "caught CssmError: %d %s", (int) cssme.error, errStr);
1763 } catch (MacOSError mose) {
1764 secnotice("integrity", "MacOSError: %d", (int)mose.osStatus());
1765 } catch (const std::bad_cast & e) {
1766 secnotice("integrity", "***** bad cast: %s", e.what());
1767 } catch (...) {
1768 // We failed to migrate. We won't commit the transaction, so the blob on-disk stays the same.
1769 secnotice("integrity", "***** unknown error");
e3d460c9 1770 }
fa7225c8
A
1771 } else {
1772 secnotice("integrity", "no migration path for %s at version %d to", oldPath.c_str(), oldBlobVersion);
1773 secnotice("integrity", " %s at version %d", newPath.c_str(), newBlobVersion);
1774 return oldBlobVersion;
e3d460c9
A
1775 }
1776
fa7225c8 1777 // If we reached here, the migration failed. Return the old version.
e3d460c9
A
1778 return oldBlobVersion;
1779}
1780
fa7225c8
A
1781void KeychainImpl::attemptKeychainRename(const string oldPath, const string newPath, uint32 blobVersion) {
1782 secnotice("integrity", "attempting to rename keychain (%d) from %s to %s", blobVersion, oldPath.c_str(), newPath.c_str());
1783
1784 // Take the file lock on this database, so other people won't try to move it before we do
1785 // NOTE: during a migration from a v256 to a v512 keychain, the db is first copied from the .keychain to the
1786 // .keychain-db path. Other non-migrating processes, if they open the keychain, enter this function to
1787 // try to move it back. These will attempt to take the .keychain-db file lock, but they will not succeed
1788 // until the migration is finished. Once they acquire that, they might try to take the .keychain file lock.
1789 // This is technically lock inversion, but deadlocks will not happen since the migrating process creates the
1790 // .keychain-db file lock before creating the .keychain-db file, so other processes will not try to grab the
1791 // .keychain-db lock in this function before the migrating process already has it.
1792 FileLockTransaction fileLockmDb(mDb);
1793
1794 // first, check if someone renamed this keychain while we were grabbing the file lock
1795 globals().storageManager.reloadKeychain(this);
1796
1797 uint32 dbBlobVersion = SecurityServer::DbBlob::version_MacOS_10_0;
1798
1799 try {
1800 dbBlobVersion = mDb->dbBlobVersion();
1801 } catch (...) {
1802 secnotice("integrity", "dbBlobVersion() failed for an unknown reason while renaming, aborting rename");
1803 return;
1804 }
1805
1806 if(dbBlobVersion != blobVersion) {
1807 secnotice("integrity", "database version changed while we were grabbing the file lock; aborting rename");
1808 return;
1809 }
1810
1811 if(oldPath != mDb->name()) {
1812 secnotice("integrity", "database location changed while we were grabbing the file lock; aborting rename");
1813 return;
1814 }
1815
1816 // we're still at the original location and version; go ahead and do the move
1817 globals().storageManager.rename(this, newPath.c_str());
1818}
e3d460c9 1819
b1ab9ed8
A
1820Keychain::Keychain()
1821{
1822 dispatch_once(&SecKeychainSystemKeychainChecked, ^{
1823 check_system_keychain();
1824 });
1825}
1826
427c49bc
A
1827Keychain::~Keychain()
1828{
1829}
1830
1831
1832
b1ab9ed8
A
1833Keychain
1834Keychain::optional(SecKeychainRef handle)
1835{
1836 if (handle)
1837 return KeychainImpl::required(handle);
1838 else
1839 return globals().storageManager.defaultKeychain();
1840}
1841
1842
427c49bc 1843CFIndex KeychainCore::GetKeychainRetainCount(Keychain& kc)
b1ab9ed8
A
1844{
1845 CFTypeRef ref = kc->handle(false);
1846 return CFGetRetainCount(ref);
1847}
1848
1849
1850//
1851// Create default credentials for this keychain.
1852// This is triggered upon default open (i.e. a Db::activate() with no set credentials).
1853//
1854// This function embodies the "default credentials" logic for Keychain-layer databases.
1855//
1856const AccessCredentials *
1857KeychainImpl::makeCredentials()
1858{
1859 return defaultCredentials();
1860}
1861
1862
1863const AccessCredentials *
1864KeychainImpl::defaultCredentials()
1865{
1866 StLock<Mutex>_(mMutex);
1867
1868 // Use custom unlock credentials for file keychains which have a referral
1869 // record and the standard credentials for all others.
1870
1871 if (mDb->dl()->guid() == gGuidAppleCSPDL && mCustomUnlockCreds(mDb))
1872 return &mCustomUnlockCreds;
1873 else
1874 if (mDb->dl()->guid() == gGuidAppleSdCSPDL)
1875 return globals().smartcardCredentials();
1876 else
1877 return globals().keychainCredentials();
1878}
1879
1880
1881
427c49bc
A
1882bool KeychainImpl::mayDelete()
1883{
1884 return true;
1885}
e3d460c9
A
1886
1887bool KeychainImpl::hasIntegrityProtection() {
b04fe171
A
1888 StLock<Mutex>_(mMutex);
1889
e3d460c9
A
1890 // This keychain only supports integrity if there's a database attached, that database is an Apple CSPDL, and the blob version is high enough
1891 if(mDb && (mDb->dl()->guid() == gGuidAppleCSPDL)) {
1892 if(mDb->dbBlobVersion() >= SecurityServer::DbBlob::version_partition) {
1893 return true;
1894 } else {
fa7225c8 1895 secnotice("integrity", "keychain blob version does not support integrity");
e3d460c9
A
1896 return false;
1897 }
1898 } else {
fa7225c8 1899 secnotice("integrity", "keychain guid does not support integrity");
e3d460c9
A
1900 return false;
1901 }
e3d460c9
A
1902}
1903