2 * Copyright (c) 2000-2004,2011-2014 Apple Inc. All Rights Reserved.
4 * @APPLE_LICENSE_HEADER_START@
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
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.
21 * @APPLE_LICENSE_HEADER_END@
29 #include "KCEventNotifier.h"
30 #include "Keychains.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 #include <securityd_client/ssblob.h>
42 #include <Security/TrustSettingsSchema.h>
44 #include "SecKeychainPriv.h"
46 #include <Security/SecKeychainItemPriv.h>
47 #include <CoreFoundation/CoreFoundation.h>
48 #include "DLDbListCFPref.h"
50 #include <sys/param.h>
53 #include <sys/socket.h>
55 #include <sys/types.h>
58 static dispatch_once_t SecKeychainSystemKeychainChecked
;
60 OSStatus
SecKeychainSystemKeychainCheckWouldDeadlock()
62 dispatch_once(&SecKeychainSystemKeychainChecked
, ^{});
66 using namespace KeychainCore
;
67 using namespace CssmClient
;
70 typedef struct EventItem
72 SecKeychainEvent kcEvent
;
76 typedef std::list
<EventItem
> EventBufferSuper
;
77 class EventBuffer
: public EventBufferSuper
81 virtual ~EventBuffer ();
85 EventBuffer::~EventBuffer ()
94 KeychainSchemaImpl::KeychainSchemaImpl(const Db
&db
) : mMutex(Mutex::recursive
)
96 DbCursor
relations(db
);
97 relations
->recordType(CSSM_DL_DB_SCHEMA_INFO
);
98 DbAttributes
relationRecord(db
, 1);
99 relationRecord
.add(Schema::RelationID
);
100 DbUniqueRecord
outerUniqueId(db
);
102 while (relations
->next(&relationRecord
, NULL
, outerUniqueId
))
104 DbUniqueRecord
uniqueId(db
);
106 uint32 relationID
= relationRecord
.at(0);
107 if (CSSM_DB_RECORDTYPE_SCHEMA_START
<= relationID
108 && relationID
< CSSM_DB_RECORDTYPE_SCHEMA_END
)
111 // Create a cursor on the SCHEMA_ATTRIBUTES table for records with
112 // RelationID == relationID
113 DbCursor
attributes(db
);
114 attributes
->recordType(CSSM_DL_DB_SCHEMA_ATTRIBUTES
);
115 attributes
->add(CSSM_DB_EQUAL
, Schema::RelationID
, relationID
);
117 // Set up a record for retriving the SCHEMA_ATTRIBUTES
118 DbAttributes
attributeRecord(db
, 2);
119 attributeRecord
.add(Schema::AttributeFormat
);
120 attributeRecord
.add(Schema::AttributeID
);
122 RelationInfoMap
&rim
= mDatabaseInfoMap
[relationID
];
123 while (attributes
->next(&attributeRecord
, NULL
, uniqueId
))
124 rim
[attributeRecord
.at(1)] = attributeRecord
.at(0);
126 // Create a cursor on the CSSM_DL_DB_SCHEMA_INDEXES table for records
127 // with RelationID == relationID
128 DbCursor
indexes(db
);
129 indexes
->recordType(CSSM_DL_DB_SCHEMA_INDEXES
);
130 indexes
->conjunctive(CSSM_DB_AND
);
131 indexes
->add(CSSM_DB_EQUAL
, Schema::RelationID
, relationID
);
132 indexes
->add(CSSM_DB_EQUAL
, Schema::IndexType
,
133 uint32(CSSM_DB_INDEX_UNIQUE
));
135 // Set up a record for retriving the SCHEMA_INDEXES
136 DbAttributes
indexRecord(db
, 1);
137 indexRecord
.add(Schema::AttributeID
);
139 CssmAutoDbRecordAttributeInfo
&infos
=
140 *new CssmAutoDbRecordAttributeInfo();
142 insert(PrimaryKeyInfoMap::value_type(relationID
, &infos
));
143 infos
.DataRecordType
= relationID
;
144 while (indexes
->next(&indexRecord
, NULL
, uniqueId
))
146 CssmDbAttributeInfo
&info
= infos
.add();
147 info
.AttributeNameFormat
= CSSM_DB_ATTRIBUTE_NAME_AS_INTEGER
;
148 info
.Label
.AttributeID
= indexRecord
.at(0);
149 // @@@ Might insert bogus value if DB is corrupt
150 info
.AttributeFormat
= rim
[info
.Label
.AttributeID
];
155 KeychainSchemaImpl::~KeychainSchemaImpl()
159 map
<CSSM_DB_RECORDTYPE
, CssmAutoDbRecordAttributeInfo
*>::iterator it
= mPrimaryKeyInfoMap
.begin();
160 while (it
!= mPrimaryKeyInfoMap
.end())
165 // for_each_map_delete(mPrimaryKeyInfoMap.begin(), mPrimaryKeyInfoMap.end());
172 const KeychainSchemaImpl::RelationInfoMap
&
173 KeychainSchemaImpl::relationInfoMapFor(CSSM_DB_RECORDTYPE recordType
) const
175 DatabaseInfoMap::const_iterator dit
= mDatabaseInfoMap
.find(recordType
);
176 if (dit
== mDatabaseInfoMap
.end())
177 MacOSError::throwMe(errSecNoSuchClass
);
181 bool KeychainSchemaImpl::hasRecordType (CSSM_DB_RECORDTYPE recordType
) const
183 DatabaseInfoMap::const_iterator it
= mDatabaseInfoMap
.find(recordType
);
184 return it
!= mDatabaseInfoMap
.end();
188 KeychainSchemaImpl::hasAttribute(CSSM_DB_RECORDTYPE recordType
, uint32 attributeId
) const
192 const RelationInfoMap
&rmap
= relationInfoMapFor(recordType
);
193 RelationInfoMap::const_iterator rit
= rmap
.find(attributeId
);
194 return rit
!= rmap
.end();
196 catch (MacOSError result
)
198 if (result
.osStatus () == errSecNoSuchClass
)
209 CSSM_DB_ATTRIBUTE_FORMAT
210 KeychainSchemaImpl::attributeFormatFor(CSSM_DB_RECORDTYPE recordType
, uint32 attributeId
) const
212 const RelationInfoMap
&rmap
= relationInfoMapFor(recordType
);
213 RelationInfoMap::const_iterator rit
= rmap
.find(attributeId
);
214 if (rit
== rmap
.end())
215 MacOSError::throwMe(errSecNoSuchAttr
);
221 KeychainSchemaImpl::attributeInfoFor(CSSM_DB_RECORDTYPE recordType
, uint32 attributeId
) const
223 CSSM_DB_ATTRIBUTE_INFO info
;
224 info
.AttributeFormat
= attributeFormatFor(recordType
, attributeId
);
225 info
.AttributeNameFormat
= CSSM_DB_ATTRIBUTE_NAME_AS_INTEGER
;
226 info
.Label
.AttributeID
= attributeId
;
232 KeychainSchemaImpl::getAttributeInfoForRecordType(CSSM_DB_RECORDTYPE recordType
, SecKeychainAttributeInfo
**Info
) const
234 const RelationInfoMap
&rmap
= relationInfoMapFor(recordType
);
236 SecKeychainAttributeInfo
*theList
=reinterpret_cast<SecKeychainAttributeInfo
*>(malloc(sizeof(SecKeychainAttributeInfo
)));
238 size_t capacity
=rmap
.size();
239 UInt32
*tagBuf
=reinterpret_cast<UInt32
*>(malloc(capacity
*sizeof(UInt32
)));
240 UInt32
*formatBuf
=reinterpret_cast<UInt32
*>(malloc(capacity
*sizeof(UInt32
)));
244 for (RelationInfoMap::const_iterator rit
= rmap
.begin(); rit
!= rmap
.end(); ++rit
)
249 if (capacity
<= i
) capacity
= i
+ 1;
250 tagBuf
=reinterpret_cast<UInt32
*>(realloc(tagBuf
, (capacity
*sizeof(UInt32
))));
251 formatBuf
=reinterpret_cast<UInt32
*>(realloc(tagBuf
, (capacity
*sizeof(UInt32
))));
253 tagBuf
[i
]=rit
->first
;
254 formatBuf
[i
++]=rit
->second
;
259 theList
->format
=formatBuf
;
264 const CssmAutoDbRecordAttributeInfo
&
265 KeychainSchemaImpl::primaryKeyInfosFor(CSSM_DB_RECORDTYPE recordType
) const
267 PrimaryKeyInfoMap::const_iterator it
;
268 it
= mPrimaryKeyInfoMap
.find(recordType
);
270 if (it
== mPrimaryKeyInfoMap
.end())
271 MacOSError::throwMe(errSecNoSuchClass
); // @@@ Not really but whatever.
277 KeychainSchemaImpl::operator <(const KeychainSchemaImpl
&other
) const
279 return mDatabaseInfoMap
< other
.mDatabaseInfoMap
;
283 KeychainSchemaImpl::operator ==(const KeychainSchemaImpl
&other
) const
285 return mDatabaseInfoMap
== other
.mDatabaseInfoMap
;
289 KeychainSchemaImpl::didCreateRelation(CSSM_DB_RECORDTYPE relationID
,
290 const char *inRelationName
,
291 uint32 inNumberOfAttributes
,
292 const CSSM_DB_SCHEMA_ATTRIBUTE_INFO
*pAttributeInfo
,
293 uint32 inNumberOfIndexes
,
294 const CSSM_DB_SCHEMA_INDEX_INFO
*pIndexInfo
)
296 StLock
<Mutex
>_(mMutex
);
298 if (CSSM_DB_RECORDTYPE_SCHEMA_START
<= relationID
299 && relationID
< CSSM_DB_RECORDTYPE_SCHEMA_END
)
302 // if our schema is already in the map, return
303 if (mPrimaryKeyInfoMap
.find(relationID
) != mPrimaryKeyInfoMap
.end())
308 RelationInfoMap
&rim
= mDatabaseInfoMap
[relationID
];
309 for (uint32 ix
= 0; ix
< inNumberOfAttributes
; ++ix
)
310 rim
[pAttributeInfo
[ix
].AttributeId
] = pAttributeInfo
[ix
].DataType
;
312 CssmAutoDbRecordAttributeInfo
*infos
= new CssmAutoDbRecordAttributeInfo();
315 insert(PrimaryKeyInfoMap::value_type(relationID
, infos
));
316 infos
->DataRecordType
= relationID
;
317 for (uint32 ix
= 0; ix
< inNumberOfIndexes
; ++ix
)
318 if (pIndexInfo
[ix
].IndexType
== CSSM_DB_INDEX_UNIQUE
)
320 CssmDbAttributeInfo
&info
= infos
->add();
321 info
.AttributeNameFormat
= CSSM_DB_ATTRIBUTE_NAME_AS_INTEGER
;
322 info
.Label
.AttributeID
= pIndexInfo
[ix
].AttributeId
;
323 info
.AttributeFormat
= rim
[info
.Label
.AttributeID
];
329 KeychainSchema::~KeychainSchema()
338 SecKeychainEvent eventCode
;
339 PrimaryKey primaryKey
;
341 typedef std::list
<Event
> EventList
;
343 #define SYSTEM_KEYCHAIN_CHECK_UNIX_BASE_NAME "/var/run/systemkeychaincheck"
344 #define SYSTEM_KEYCHAIN_CHECK_UNIX_DOMAIN_SOCKET_NAME (SYSTEM_KEYCHAIN_CHECK_UNIX_BASE_NAME ".socket")
345 #define SYSTEM_KEYCHAIN_CHECK_COMPLETE_FILE_NAME (SYSTEM_KEYCHAIN_CHECK_UNIX_BASE_NAME ".done")
347 static void check_system_keychain()
349 // sadly we can't use XPC here, XPC_DOMAIN_TYPE_SYSTEM doesn't exist yet. Also xpc-helper uses the
350 // keychain API (I assume for checking codesign things). So we use Unix Domain Sockets.
352 // NOTE: if we hit a system error we attempt to log it, and then just don't check the system keychain.
353 // In theory a system might be able to recover from this state if we let it try to muddle along, and
354 // past behaviour didn't even try this hard to do the keychain check. In particular we could be in a
355 // sandbox'ed process. So we just do our best and let another process try again.
357 struct stat keycheck_file_info
;
358 if (stat(SYSTEM_KEYCHAIN_CHECK_COMPLETE_FILE_NAME
, &keycheck_file_info
) < 0) {
359 int server_fd
= socket(PF_UNIX
, SOCK_STREAM
, 0);
361 syslog(LOG_ERR
, "Can't get socket (%m) system keychain may be unchecked");
365 struct sockaddr_un keychain_check_server_address
;
366 keychain_check_server_address
.sun_family
= AF_UNIX
;
367 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
)) {
368 // It would be nice if we could compile time assert this
369 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
));
373 keychain_check_server_address
.sun_len
= SUN_LEN(&keychain_check_server_address
);
375 int rc
= connect(server_fd
, (struct sockaddr
*)&keychain_check_server_address
, keychain_check_server_address
.sun_len
);
377 syslog(LOG_ERR
, "Can not connect to %s: %m", SYSTEM_KEYCHAIN_CHECK_UNIX_DOMAIN_SOCKET_NAME
);
382 // 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)
384 ssize_t read_size
= read(server_fd
, &byte
, 1);
386 syslog(LOG_ERR
, "Error reading from system keychain checker: %m");
397 KeychainImpl::KeychainImpl(const Db
&db
)
398 : mAttemptedUpgrade(false), mDbItemMapMutex(Mutex::recursive
), mDbDeletedItemMapMutex(Mutex::recursive
),
399 mInCache(false), mDb(db
), mCustomUnlockCreds (this), mIsInBatchMode (false), mMutex(Mutex::recursive
)
401 dispatch_once(&SecKeychainSystemKeychainChecked
, ^{
402 check_system_keychain();
404 mDb
->defaultCredentials(this); // install activation hook
405 mEventBuffer
= new EventBuffer
;
408 KeychainImpl::~KeychainImpl()
412 // Remove ourselves from the cache if we are in it.
413 // fprintf(stderr, "Removing %p from storage manager cache.\n", handle(false));
414 globals().storageManager
.removeKeychain(dlDbIdentifier(), this);
423 KeychainImpl::getMutexForObject()
425 return globals().storageManager
.getStorageManagerMutex();
429 KeychainImpl::getKeychainMutex()
435 KeychainImpl::getKeychainReadWriteLock()
440 void KeychainImpl::aboutToDestruct()
442 // remove me from the global cache, we are done
443 // fprintf(stderr, "Destructing keychain object\n");
444 DLDbIdentifier identifier
= dlDbIdentifier();
445 globals().storageManager
.removeKeychain(identifier
, this);
449 KeychainImpl::operator ==(const KeychainImpl
&keychain
) const
451 return dlDbIdentifier() == keychain
.dlDbIdentifier();
455 KeychainImpl::createCursor(SecItemClass itemClass
, const SecKeychainAttributeList
*attrList
)
457 StLock
<Mutex
>_(mMutex
);
459 StorageManager::KeychainList keychains
;
460 keychains
.push_back(Keychain(this));
461 return KCCursor(keychains
, itemClass
, attrList
);
465 KeychainImpl::createCursor(const SecKeychainAttributeList
*attrList
)
467 StLock
<Mutex
>_(mMutex
);
469 StorageManager::KeychainList keychains
;
470 keychains
.push_back(Keychain(this));
471 return KCCursor(keychains
, attrList
);
475 KeychainImpl::create(UInt32 passwordLength
, const void *inPassword
)
477 StLock
<Mutex
>_(mMutex
);
485 Allocator
&alloc
= Allocator::standard();
487 // @@@ Share this instance
489 const CssmData
password(const_cast<void *>(inPassword
), passwordLength
);
490 AclFactory::PasswordChangeCredentials
pCreds (password
, alloc
);
491 AclFactory::AnyResourceContext
rcc(pCreds
);
495 void KeychainImpl::create(ConstStringPtr inPassword
)
497 StLock
<Mutex
>_(mMutex
);
500 create(static_cast<UInt32
>(inPassword
[0]), &inPassword
[1]);
506 KeychainImpl::create()
508 StLock
<Mutex
>_(mMutex
);
510 AclFactory aclFactory
;
511 AclFactory::AnyResourceContext
rcc(aclFactory
.unlockCred());
515 void KeychainImpl::createWithBlob(CssmData
&blob
)
517 StLock
<Mutex
>_(mMutex
);
519 mDb
->dbInfo(&Schema::DBInfo
);
520 AclFactory aclFactory
;
521 AclFactory::AnyResourceContext
rcc(aclFactory
.unlockCred());
522 mDb
->resourceControlContext (&rcc
);
525 mDb
->createWithBlob(blob
);
529 mDb
->resourceControlContext(NULL
);
533 mDb
->resourceControlContext(NULL
);
534 mDb
->dbInfo(NULL
); // Clear the schema (to not break an open call later)
535 globals().storageManager
.created(Keychain(this));
537 KCEventNotifier::PostKeychainEvent (kSecKeychainListChangedEvent
, this, NULL
);
541 KeychainImpl::create(const ResourceControlContext
*rcc
)
543 StLock
<Mutex
>_(mMutex
);
545 mDb
->dbInfo(&Schema::DBInfo
); // Set the schema (to force a create)
546 mDb
->resourceControlContext(rcc
);
553 mDb
->resourceControlContext(NULL
);
554 mDb
->dbInfo(NULL
); // Clear the schema (to not break an open call later)
557 mDb
->resourceControlContext(NULL
);
558 mDb
->dbInfo(NULL
); // Clear the schema (to not break an open call later)
559 globals().storageManager
.created(Keychain(this));
565 StLock
<Mutex
>_(mMutex
);
573 StLock
<Mutex
>_(mMutex
);
579 KeychainImpl::unlock()
581 StLock
<Mutex
>_(mMutex
);
587 KeychainImpl::unlock(const CssmData
&password
)
589 StLock
<Mutex
>_(mMutex
);
591 mDb
->unlock(password
);
595 KeychainImpl::unlock(ConstStringPtr password
)
597 StLock
<Mutex
>_(mMutex
);
601 const CssmData
data(const_cast<unsigned char *>(&password
[1]), password
[0]);
609 KeychainImpl::stash()
611 StLock
<Mutex
>_(mMutex
);
617 KeychainImpl::stashCheck()
619 StLock
<Mutex
>_(mMutex
);
625 KeychainImpl::getSettings(uint32
&outIdleTimeOut
, bool &outLockOnSleep
)
627 StLock
<Mutex
>_(mMutex
);
629 mDb
->getSettings(outIdleTimeOut
, outLockOnSleep
);
633 KeychainImpl::setSettings(uint32 inIdleTimeOut
, bool inLockOnSleep
)
635 StLock
<Mutex
>_(mMutex
);
637 // The .Mac syncing code only makes sense for the AppleFile CSP/DL,
638 // but other DLs such as the OCSP and LDAP DLs do not expose a way to
639 // change settings or the password. To make a minimal change that only affects
640 // the smartcard case, we only look for that CSP/DL
642 bool isSmartcard
= (mDb
->dl()->guid() == gGuidAppleSdCSPDL
);
644 // get the old keychain blob so that we can tell .Mac to resync it
645 CssmAutoData
oldBlob(mDb
->allocator());
647 mDb
->copyBlob(oldBlob
.get());
649 mDb
->setSettings(inIdleTimeOut
, inLockOnSleep
);
653 KeychainImpl::changePassphrase(UInt32 oldPasswordLength
, const void *oldPassword
,
654 UInt32 newPasswordLength
, const void *newPassword
)
656 StLock
<Mutex
>_(mMutex
);
658 bool isSmartcard
= (mDb
->dl()->guid() == gGuidAppleSdCSPDL
);
660 TrackingAllocator
allocator(Allocator::standard());
661 AutoCredentials cred
= AutoCredentials(allocator
);
664 const CssmData
&oldPass
= *new(allocator
) CssmData(const_cast<void *>(oldPassword
), oldPasswordLength
);
665 TypedList
&oldList
= *new(allocator
) TypedList(allocator
, CSSM_SAMPLE_TYPE_KEYCHAIN_LOCK
);
666 oldList
.append(new(allocator
) ListElement(CSSM_SAMPLE_TYPE_PASSWORD
));
667 oldList
.append(new(allocator
) ListElement(oldPass
));
673 const CssmData
&newPass
= *new(allocator
) CssmData(const_cast<void *>(newPassword
), newPasswordLength
);
674 TypedList
&newList
= *new(allocator
) TypedList(allocator
, CSSM_SAMPLE_TYPE_KEYCHAIN_CHANGE_LOCK
);
675 newList
.append(new(allocator
) ListElement(CSSM_SAMPLE_TYPE_PASSWORD
));
676 newList
.append(new(allocator
) ListElement(newPass
));
680 // get the old keychain blob so that we can tell .Mac to resync it
681 CssmAutoData
oldBlob(mDb
->allocator());
683 mDb
->copyBlob(oldBlob
.get());
685 mDb
->changePassphrase(&cred
);
689 KeychainImpl::changePassphrase(ConstStringPtr oldPassword
, ConstStringPtr newPassword
)
691 StLock
<Mutex
>_(mMutex
);
693 const void *oldPtr
, *newPtr
;
694 UInt32 oldLen
, newLen
;
697 oldLen
= oldPassword
[0];
698 oldPtr
= oldPassword
+ 1;
708 newLen
= newPassword
[0];
709 newPtr
= newPassword
+ 1;
717 changePassphrase(oldLen
, oldPtr
, newLen
, newPtr
);
721 KeychainImpl::authenticate(const CSSM_ACCESS_CREDENTIALS
*cred
)
723 StLock
<Mutex
>_(mMutex
);
726 MacOSError::throwMe(errSecNoSuchKeychain
);
728 MacOSError::throwMe(errSecUnimplemented
);
732 KeychainImpl::status() const
734 // @@@ We should figure out the read/write status though a DL passthrough
735 // or some other way. Also should locked be unlocked read only or just
737 return (mDb
->isLocked() ? 0 : kSecUnlockStateStatus
| kSecWritePermStatus
)
738 | kSecReadPermStatus
;
742 KeychainImpl::exists()
744 StLock
<Mutex
>_(mMutex
);
750 // Ok to leave the mDb open since it will get closed when it goes away.
752 catch (const CssmError
&e
)
754 if (e
.osStatus() != CSSMERR_DL_DATASTORE_DOESNOT_EXIST
)
763 KeychainImpl::isActive() const
765 return mDb
->isActive();
768 void KeychainImpl::completeAdd(Item
&inItem
, PrimaryKey
&primaryKey
)
772 // The inItem shouldn't be in the cache yet
773 assert(!inItem
->inCache());
775 // Insert inItem into mDbItemMap with key primaryKey. p.second will be
776 // true if it got inserted. If not p.second will be false and p.first
777 // will point to the current entry with key primaryKey.
778 StLock
<Mutex
> _(mDbItemMapMutex
);
779 pair
<DbItemMap::iterator
, bool> p
=
780 mDbItemMap
.insert(DbItemMap::value_type(primaryKey
, inItem
.get()));
783 // There was already an ItemImpl * in mDbItemMap with key
784 // primaryKey. Remove it, and try the add again.
785 ItemImpl
*oldItem
= p
.first
->second
;
787 // @@@ If this happens we are breaking our API contract of
788 // uniquifying items. We really need to insert the item into the
789 // map before we start the add. And have the item be in an
790 // "is being added" state.
791 secdebug("keychain", "add of new item %p somehow replaced %p",
792 inItem
.get(), oldItem
);
794 mDbItemMap
.erase(p
.first
);
795 oldItem
->inCache(false);
796 forceRemoveFromCache(oldItem
);
797 mDbItemMap
.insert(DbItemMap::value_type(primaryKey
, inItem
.get()));
800 inItem
->inCache(true);
804 KeychainImpl::addCopy(Item
&inItem
)
806 Keychain
keychain(this);
807 PrimaryKey primaryKey
= inItem
->addWithCopyInfo(keychain
, true);
808 completeAdd(inItem
, primaryKey
);
809 postEvent(kSecAddEvent
, inItem
);
813 KeychainImpl::add(Item
&inItem
)
815 Keychain
keychain(this);
816 PrimaryKey primaryKey
= inItem
->add(keychain
);
817 completeAdd(inItem
, primaryKey
);
818 postEvent(kSecAddEvent
, inItem
);
822 KeychainImpl::didUpdate(const Item
&inItem
, PrimaryKey
&oldPK
,
825 // If the primary key hasn't changed we don't need to update mDbItemMap.
828 // If inItem isn't in the cache we don't need to update mDbItemMap.
829 assert(inItem
->inCache());
830 if (inItem
->inCache())
832 StLock
<Mutex
> _(mDbItemMapMutex
);
833 // First remove the entry for inItem in mDbItemMap with key oldPK.
834 DbItemMap::iterator it
= mDbItemMap
.find(oldPK
);
835 if (it
!= mDbItemMap
.end() && (ItemImpl
*) it
->second
== inItem
.get())
836 mDbItemMap
.erase(it
);
838 // Insert inItem into mDbItemMap with key newPK. p.second will be
839 // true if it got inserted. If not p.second will be false and
840 // p.first will point to the current entry with key newPK.
841 pair
<DbItemMap::iterator
, bool> p
=
842 mDbItemMap
.insert(DbItemMap::value_type(newPK
, inItem
.get()));
845 // There was already an ItemImpl * in mDbItemMap with key
846 // primaryKey. Remove it, and try the add again.
847 ItemImpl
*oldItem
= p
.first
->second
;
849 // @@@ If this happens we are breaking our API contract of
850 // uniquifying items. We really need to insert the item into
851 // the map with the new primary key before we start the update.
852 // And have the item be in an "is being updated" state.
853 secdebug("keychain", "update of item %p somehow replaced %p",
854 inItem
.get(), oldItem
);
856 mDbItemMap
.erase(p
.first
);
857 oldItem
->inCache(false);
858 forceRemoveFromCache(oldItem
);
859 mDbItemMap
.insert(DbItemMap::value_type(newPK
, inItem
.get()));
864 // Item updates now are technically a delete and re-add, so post these events instead of kSecUpdateEvent
865 postEvent(kSecDeleteEvent
, inItem
);
866 postEvent(kSecAddEvent
, inItem
);
870 KeychainImpl::deleteItem(Item
&inoutItem
)
873 // We don't need to hold the DO mutex through event posting, and, in fact, doing so causes deadlock.
874 // Hold it only as long as needed, instead.
877 // item must be persistent
878 if (!inoutItem
->isPersistent())
879 MacOSError::throwMe(errSecInvalidItemRef
);
881 secdebug("kcnotify", "starting deletion of item %p", inoutItem
.get());
883 DbUniqueRecord uniqueId
= inoutItem
->dbUniqueRecord();
884 PrimaryKey primaryKey
= inoutItem
->primaryKey();
885 uniqueId
->deleteRecord();
887 // Move the item from mDbItemMap to mDbDeletedItemMap. We need the item
888 // to give to the client process when we receive the kSecDeleteEvent
889 // notification, but if that notification never arrives, we don't want
890 // the item hanging around. When didDeleteItem is called by CCallbackMgr,
891 // we'll remove all traces of the item.
893 if (inoutItem
->inCache()) {
894 StLock
<Mutex
> _(mDbItemMapMutex
);
895 StLock
<Mutex
> __(mDbDeletedItemMapMutex
);
896 // Only look for it if it's in the cache
897 DbItemMap::iterator it
= mDbItemMap
.find(primaryKey
);
899 if (it
!= mDbItemMap
.end() && (ItemImpl
*) it
->second
== inoutItem
.get()) {
900 mDbDeletedItemMap
.insert(DbItemMap::value_type(primaryKey
, it
->second
));
901 mDbItemMap
.erase(it
);
905 // Post the notification for the item deletion with
906 // the primaryKey obtained when the item still existed
909 postEvent(kSecDeleteEvent
, inoutItem
);
916 StLock
<Mutex
>_(mMutex
);
918 if (!mDb
->dl()->subserviceMask() & CSSM_SERVICE_CSP
)
919 MacOSError::throwMe(errSecInvalidKeychain
);
921 // Try to cast first to a CSPDL to handle case where we don't have an SSDb
924 CssmClient::CSPDL
cspdl(dynamic_cast<CssmClient::CSPDLImpl
*>(&*mDb
->dl()));
929 SSDbImpl
* impl
= dynamic_cast<SSDbImpl
*>(&(*mDb
));
932 CssmError::throwMe(CSSMERR_CSSM_INVALID_POINTER
);
941 KeychainImpl::makePrimaryKey(CSSM_DB_RECORDTYPE recordType
, DbUniqueRecord
&uniqueId
)
943 StLock
<Mutex
>_(mMutex
);
945 DbAttributes
primaryKeyAttrs(uniqueId
->database());
946 primaryKeyAttrs
.recordType(recordType
);
947 gatherPrimaryKeyAttributes(primaryKeyAttrs
);
948 uniqueId
->get(&primaryKeyAttrs
, NULL
);
949 return PrimaryKey(primaryKeyAttrs
);
953 KeychainImpl::makePrimaryKey(CSSM_DB_RECORDTYPE recordType
, DbAttributes
* currentAttributes
)
955 StLock
<Mutex
>_(mMutex
);
957 DbAttributes primaryKeyAttrs
;
958 primaryKeyAttrs
.recordType(recordType
);
959 gatherPrimaryKeyAttributes(primaryKeyAttrs
);
961 for(int i
= 0; i
< primaryKeyAttrs
.size(); i
++) {
962 CssmDbAttributeData
& attr
= primaryKeyAttrs
[i
];
964 CssmDbAttributeData
* actual
= currentAttributes
->find(attr
.info());
966 attr
.set(*actual
, Allocator::standard());
969 return PrimaryKey(primaryKeyAttrs
);
972 const CssmAutoDbRecordAttributeInfo
&
973 KeychainImpl::primaryKeyInfosFor(CSSM_DB_RECORDTYPE recordType
)
975 StLock
<Mutex
>_(mMutex
);
979 return keychainSchema()->primaryKeyInfosFor(recordType
);
981 catch (const CommonError
&error
)
983 switch (error
.osStatus())
985 case errSecNoSuchClass
:
986 case CSSMERR_DL_INVALID_RECORDTYPE
:
988 return keychainSchema()->primaryKeyInfosFor(recordType
);
995 void KeychainImpl::gatherPrimaryKeyAttributes(DbAttributes
& primaryKeyAttrs
)
997 StLock
<Mutex
> _(mMutex
);
999 const CssmAutoDbRecordAttributeInfo
&infos
=
1000 primaryKeyInfosFor(primaryKeyAttrs
.recordType());
1002 // @@@ fix this to not copy info.
1003 for (uint32 i
= 0; i
< infos
.size(); i
++)
1004 primaryKeyAttrs
.add(infos
.at(i
));
1008 KeychainImpl::_lookupItem(const PrimaryKey
&primaryKey
)
1010 StLock
<Mutex
> _(mDbItemMapMutex
);
1011 DbItemMap::iterator it
= mDbItemMap
.find(primaryKey
);
1012 if (it
!= mDbItemMap
.end())
1021 KeychainImpl::_lookupDeletedItemOnly(const PrimaryKey
&primaryKey
)
1023 DbItemMap::iterator it
= mDbDeletedItemMap
.find(primaryKey
);
1024 if (it
!= mDbDeletedItemMap
.end())
1033 KeychainImpl::item(const PrimaryKey
&primaryKey
)
1035 StLock
<Mutex
>_(mMutex
);
1037 // Lookup the item in the map while holding the apiLock.
1038 ItemImpl
*itemImpl
= _lookupItem(primaryKey
);
1040 return Item(itemImpl
);
1045 // We didn't find it so create a new item with just a keychain and
1046 // a primary key. However since we aren't holding
1047 // globals().apiLock anymore some other thread might have beaten
1048 // us to creating this item and adding it to the cache. If that
1049 // happens we retry the lookup.
1050 return Item(this, primaryKey
);
1052 catch (const MacOSError
&e
)
1054 // If the item creation failed because some other thread already
1055 // inserted this item into the cache we retry the lookup.
1056 if (e
.osStatus() == errSecDuplicateItem
)
1058 // Lookup the item in the map while holding the apiLock.
1059 ItemImpl
*itemImpl
= _lookupItem(primaryKey
);
1061 return Item(itemImpl
);
1066 // Check for an item that may have been deleted.
1068 KeychainImpl::itemdeleted(const PrimaryKey
& primaryKey
) {
1069 StLock
<Mutex
>_(mMutex
);
1071 Item i
= _lookupDeletedItemOnly(primaryKey
);
1075 return item(primaryKey
);
1081 KeychainImpl::item(CSSM_DB_RECORDTYPE recordType
, DbUniqueRecord
&uniqueId
)
1083 StLock
<Mutex
>_(mMutex
);
1085 PrimaryKey primaryKey
= makePrimaryKey(recordType
, uniqueId
);
1087 // Lookup the item in the map while holding the apiLock.
1088 ItemImpl
*itemImpl
= _lookupItem(primaryKey
);
1092 return Item(itemImpl
);
1098 // We didn't find it so create a new item with a keychain, a primary key
1099 // and a DbUniqueRecord. However since we aren't holding
1100 // globals().apiLock anymore some other thread might have beaten
1101 // us to creating this item and adding it to the cache. If that
1102 // happens we retry the lookup.
1103 return Item(this, primaryKey
, uniqueId
);
1105 catch (const MacOSError
&e
)
1107 // If the item creation failed because some other thread already
1108 // inserted this item into the cache we retry the lookup.
1109 if (e
.osStatus() == errSecDuplicateItem
)
1111 // Lookup the item in the map while holding the apiLock.
1112 ItemImpl
*itemImpl
= _lookupItem(primaryKey
);
1114 return Item(itemImpl
);
1121 KeychainImpl::keychainSchema()
1123 StLock
<Mutex
>_(mMutex
);
1124 if (!mKeychainSchema
)
1125 mKeychainSchema
= KeychainSchema(mDb
);
1127 return mKeychainSchema
;
1130 void KeychainImpl::resetSchema()
1132 mKeychainSchema
= NULL
; // re-fetch it from db next time
1136 // Called from DbItemImpl's constructor (so it is only partially constructed),
1137 // add it to the map.
1139 KeychainImpl::addItem(const PrimaryKey
&primaryKey
, ItemImpl
*dbItemImpl
)
1141 StLock
<Mutex
>_(mMutex
);
1143 // The dbItemImpl shouldn't be in the cache yet
1144 assert(!dbItemImpl
->inCache());
1146 // Insert dbItemImpl into mDbItemMap with key primaryKey. p.second will
1147 // be true if it got inserted. If not p.second will be false and p.first
1148 // will point to the current entry with key primaryKey.
1149 StLock
<Mutex
> __(mDbItemMapMutex
);
1150 pair
<DbItemMap::iterator
, bool> p
=
1151 mDbItemMap
.insert(DbItemMap::value_type(primaryKey
, dbItemImpl
));
1155 // There was already an ItemImpl * in mDbItemMap with key primaryKey.
1156 // There is a race condition here when being called in multiple threads
1157 // We might have added an item using add and received a notification at
1159 MacOSError::throwMe(errSecDuplicateItem
);
1162 dbItemImpl
->inCache(true);
1166 KeychainImpl::didDeleteItem(ItemImpl
*inItemImpl
)
1168 StLock
<Mutex
>_(mMutex
);
1170 // Called by CCallbackMgr
1171 secdebug("kcnotify", "%p notified that item %p was deleted", this, inItemImpl
);
1172 removeItem(inItemImpl
->primaryKey(), inItemImpl
);
1176 KeychainImpl::removeItem(const PrimaryKey
&primaryKey
, ItemImpl
*inItemImpl
)
1178 StLock
<Mutex
>_(mMutex
);
1180 // If inItemImpl isn't in the cache to begin with we are done.
1181 if (!inItemImpl
->inCache())
1185 StLock
<Mutex
> _(mDbItemMapMutex
);
1186 DbItemMap::iterator it
= mDbItemMap
.find(primaryKey
);
1187 if (it
!= mDbItemMap
.end() && (ItemImpl
*) it
->second
== inItemImpl
) {
1188 mDbItemMap
.erase(it
);
1190 } // drop mDbItemMapMutex
1193 StLock
<Mutex
> _(mDbDeletedItemMapMutex
);
1194 DbItemMap::iterator it
= mDbDeletedItemMap
.find(primaryKey
);
1195 if (it
!= mDbDeletedItemMap
.end() && (ItemImpl
*) it
->second
== inItemImpl
) {
1196 mDbDeletedItemMap
.erase(it
);
1198 } // drop mDbDeletedItemMapMutex
1200 inItemImpl
->inCache(false);
1204 KeychainImpl::forceRemoveFromCache(ItemImpl
* inItemImpl
) {
1206 // Wrap all this in a try-block and ignore all errors - we're trying to clean up these maps
1208 StLock
<Mutex
> _(mDbItemMapMutex
);
1209 for(DbItemMap::iterator it
= mDbItemMap
.begin(); it
!= mDbItemMap
.end(); ) {
1210 if(it
->second
== inItemImpl
) {
1211 // Increment the iterator, but use its pre-increment value for the erase
1212 it
->second
->inCache(false);
1213 mDbItemMap
.erase(it
++);
1218 } // drop mDbItemMapMutex
1221 StLock
<Mutex
> _(mDbDeletedItemMapMutex
);
1222 for(DbItemMap::iterator it
= mDbDeletedItemMap
.begin(); it
!= mDbDeletedItemMap
.end(); ) {
1223 if(it
->second
== inItemImpl
) {
1224 // Increment the iterator, but use its pre-increment value for the erase
1225 it
->second
->inCache(false);
1226 mDbDeletedItemMap
.erase(it
++);
1231 } // drop mDbDeletedItemMapMutex
1232 } catch(UnixError ue
) {
1233 secdebugfunc("keychain", "caught UnixError: %d %s", ue
.unixError(), ue
.what());
1234 } catch (CssmError cssme
) {
1235 const char* errStr
= cssmErrorString(cssme
.error
);
1236 secdebugfunc("keychain", "caught CssmError: %d %s", (int) cssme
.error
, errStr
);
1237 } catch (MacOSError mose
) {
1238 secdebugfunc("keychain", "MacOSError: %d", (int)mose
.osStatus());
1240 secdebugfunc("keychain", "Unknown error");
1245 KeychainImpl::getAttributeInfoForItemID(CSSM_DB_RECORDTYPE itemID
,
1246 SecKeychainAttributeInfo
**Info
)
1248 StLock
<Mutex
>_(mMutex
);
1252 keychainSchema()->getAttributeInfoForRecordType(itemID
, Info
);
1254 catch (const CommonError
&error
)
1256 switch (error
.osStatus())
1258 case errSecNoSuchClass
:
1259 case CSSMERR_DL_INVALID_RECORDTYPE
:
1261 keychainSchema()->getAttributeInfoForRecordType(itemID
, Info
);
1269 KeychainImpl::freeAttributeInfo(SecKeychainAttributeInfo
*Info
)
1277 KeychainImpl::attributeInfoFor(CSSM_DB_RECORDTYPE recordType
, UInt32 tag
)
1279 StLock
<Mutex
>_(mMutex
);
1283 return keychainSchema()->attributeInfoFor(recordType
, tag
);
1285 catch (const CommonError
&error
)
1287 switch (error
.osStatus())
1289 case errSecNoSuchClass
:
1290 case CSSMERR_DL_INVALID_RECORDTYPE
:
1292 return keychainSchema()->attributeInfoFor(recordType
, tag
);
1300 KeychainImpl::recode(const CssmData
&data
, const CssmData
&extraData
)
1302 StLock
<Mutex
>_(mMutex
);
1304 mDb
->recode(data
, extraData
);
1308 KeychainImpl::copyBlob(CssmData
&data
)
1310 StLock
<Mutex
>_(mMutex
);
1312 mDb
->copyBlob(data
);
1316 KeychainImpl::setBatchMode(Boolean mode
, Boolean rollback
)
1318 StLock
<Mutex
>_(mMutex
);
1320 mDb
->setBatchMode(mode
, rollback
);
1321 mIsInBatchMode
= mode
;
1324 if (!rollback
) // was batch mode being turned off without an abort?
1327 EventBuffer::iterator it
= mEventBuffer
->begin();
1328 while (it
!= mEventBuffer
->end())
1330 PrimaryKey primaryKey
;
1333 primaryKey
= it
->item
->primaryKey();
1336 KCEventNotifier::PostKeychainEvent(it
->kcEvent
, mDb
->dlDbIdentifier(), primaryKey
);
1343 // notify that a keychain has changed in too many ways to count
1344 KCEventNotifier::PostKeychainEvent(kSecKeychainLeftBatchModeEvent
);
1345 mEventBuffer
->clear();
1349 KCEventNotifier::PostKeychainEvent(kSecKeychainEnteredBatchModeEvent
);
1354 KeychainImpl::postEvent(SecKeychainEvent kcEvent
, ItemImpl
* item
)
1356 PrimaryKey primaryKey
;
1359 StLock
<Mutex
>_(mMutex
);
1363 primaryKey
= item
->primaryKey();
1367 if (!mIsInBatchMode
)
1369 KCEventNotifier::PostKeychainEvent(kcEvent
, mDb
->dlDbIdentifier(), primaryKey
);
1373 StLock
<Mutex
>_(mMutex
);
1376 it
.kcEvent
= kcEvent
;
1382 mEventBuffer
->push_back (it
);
1387 bool KeychainImpl::performKeychainUpgradeIfNeeded() {
1388 // Grab this keychain's mutex. This might not be sufficient, since the
1389 // keychain might have outstanding cursors. We'll grab the RWLock later if needed.
1390 StLock
<Mutex
>_(mMutex
);
1392 if(!globals().integrityProtection()) {
1393 secdebugfunc("integrity", "skipping upgrade for %s due to global integrity protection being diabled", mDb
->name());
1397 // We need a CSP database for 'upgrade' to be meaningful
1398 if((mDb
->dl()->subserviceMask() & CSSM_SERVICE_CSP
) == 0) {
1402 // We only want to upgrade file-based Apple keychains. Check the GUID.
1403 if(mDb
->dl()->guid() != gGuidAppleCSPDL
) {
1404 secdebugfunc("integrity", "skipping upgrade for %s due to guid mismatch\n", mDb
->name());
1408 // If we've already attempted an upgrade on this keychain, don't bother again
1409 if(mAttemptedUpgrade
) {
1413 // Don't upgrade the System root certificate keychain (to make old tp code happy)
1414 if(strncmp(mDb
->name(), SYSTEM_ROOT_STORE_PATH
, strlen(SYSTEM_ROOT_STORE_PATH
)) == 0) {
1415 secdebugfunc("integrity", "skipping upgrade for %s\n", mDb
->name());
1419 uint32 dbBlobVersion
= SecurityServer::DbBlob::version_MacOS_10_0
;
1422 dbBlobVersion
= mDb
->dbBlobVersion();
1423 } catch (CssmError cssme
) {
1424 if(cssme
.error
== CSSMERR_DL_DATASTORE_DOESNOT_EXIST
) {
1425 // oh well! We tried to get the blob version of a database
1426 // that doesn't exist. It doesn't need migration, so do nothing.
1427 secdebugfunc("integrity", "dbBlobVersion() failed for a non-existent database");
1430 // Some other error occurred. We can't upgrade this keychain, so fail.
1431 const char* errStr
= cssmErrorString(cssme
.error
);
1432 secdebugfunc("integrity", "dbBlobVersion() failed for a CssmError: %d %s", (int) cssme
.error
, errStr
);
1436 secdebugfunc("integrity", "dbBlobVersion() failed for an unknown reason");
1440 if(dbBlobVersion
!= SecurityServer::DbBlob::currentVersion
) {
1441 secdebugfunc("integrity", "going to upgrade %s from version %d to %d!", mDb
->name(), dbBlobVersion
, SecurityServer::DbBlob::currentVersion
);
1443 // We need to opportunistically perform the upgrade/reload dance.
1445 // If the keychain is unlocked, try to upgrade it.
1446 // In either case, reload the database from disk.
1447 // We need this keychain's read/write lock.
1449 // Try to grab the keychain write lock.
1450 StReadWriteLock
lock(mRWLock
, StReadWriteLock::TryWrite
);
1452 // If we didn't manage to grab the lock, there's readers out there
1453 // currently reading this keychain. Abort the upgrade.
1454 if(!lock
.isLocked()) {
1459 // We can only attempt an upgrade if the keychain is currently unlocked
1460 // There's a TOCTTOU issue here, but it's going to be rare in practice, and the upgrade will simply fail.
1461 if(!mDb
->isLocked()) {
1462 secdebugfunc("integrity", "attempting migration on database %s", mDb
->name());
1463 // Database blob is out of date. Attempt a migration.
1464 uint32 convertedVersion
= attemptKeychainMigration(dbBlobVersion
, SecurityServer::DbBlob::currentVersion
);
1465 if(convertedVersion
== SecurityServer::DbBlob::currentVersion
) {
1466 secdebugfunc("integrity", "conversion succeeded");
1468 secdebugfunc("integrity", "conversion failed, keychain is still %d", convertedVersion
);
1471 } catch (CssmError cssme
) {
1472 const char* errStr
= cssmErrorString(cssme
.error
);
1473 secdebugfunc("integrity", "caught CssmError: %d %s", (int) cssme
.error
, errStr
);
1475 // Something went wrong, but don't worry about it.
1478 // No matter if the migrator succeeded, we need to reload this keychain from disk.
1479 // Maybe someone else beat us to upgrading the keychain, but it's been locked since then.
1480 secdebugfunc("integrity", "reloading keychain");
1481 globals().storageManager
.reloadKeychain(this);
1482 secdebugfunc("integrity", "database %s is version %d", mDb
->name(), mDb
->dbBlobVersion());
1490 // Make sure you have this keychain's mutex and write lock when you call this function!
1491 uint32
KeychainImpl::attemptKeychainMigration(uint32 oldBlobVersion
, uint32 newBlobVersion
) {
1492 Db db
= mDb
; // let's not muck up our db for now
1495 // Let's reload this keychain to see if someone changed it on disk
1496 globals().storageManager
.reloadKeychain(this);
1498 if(mDb
->dbBlobVersion() == newBlobVersion
) {
1499 // Someone else upgraded this, hurray!
1500 secdebugfunc("integrity", "reloaded keychain version %d, quitting", mDb
->dbBlobVersion());
1501 db
->releaseFileLock(false);
1502 return newBlobVersion
;
1505 mAttemptedUpgrade
= true;
1506 uint32 newDbVersion
= oldBlobVersion
;
1509 secdebugfunc("integrity", "attempting migration from version %d to %d", oldBlobVersion
, newBlobVersion
);
1511 // First, make a backup of this database (so we never lose data if something goes wrong)
1514 if(oldBlobVersion
== SecurityServer::DbBlob::version_MacOS_10_0
&& newBlobVersion
== SecurityServer::DbBlob::version_partition
) {
1515 // Let the upgrade begin.
1516 newDbVersion
= db
->recodeDbToVersion(newBlobVersion
);
1517 if(newDbVersion
!= newBlobVersion
) {
1518 // Recoding failed. Don't proceed.
1519 secdebugfunc("integrity", "recodeDbToVersion failed, version is still %d", newDbVersion
);
1520 db
->releaseFileLock(false);
1521 return newDbVersion
;
1524 secdebugfunc("integrity", "recoded db successfully, adding extra integrity");
1526 Keychain
keychain(db
);
1528 // Breaking abstraction, but what're you going to do?
1529 // Don't upgrade this keychain, since we just upgraded the DB
1530 // But the DB won't return any new data until the txion commits
1531 keychain
->mAttemptedUpgrade
= true;
1533 SecItemClass classes
[] = {kSecGenericPasswordItemClass
,
1534 kSecInternetPasswordItemClass
,
1535 kSecPublicKeyItemClass
,
1536 kSecPrivateKeyItemClass
,
1537 kSecSymmetricKeyItemClass
};
1539 for(int i
= 0; i
< sizeof(classes
) / sizeof(classes
[0]); i
++) {
1541 KCCursor kcc
= keychain
->createCursor(classes
[i
], NULL
);
1543 // During recoding, we might have deleted some corrupt keys.
1544 // Because of this, we might have zombie SSGroup records left in
1545 // the database that have no matching key. Tell the KCCursor to
1546 // delete these if found.
1547 // This will also try to suppress any other invalid items.
1548 kcc
->setDeleteInvalidRecords(true);
1550 while(kcc
->next(item
)) {
1552 // Force the item to set integrity. The keychain is confused about its version because it hasn't written to disk yet,
1553 // but if we've reached this point, the keychain supports integrity.
1554 item
->setIntegrity(true);
1555 } catch(CssmError cssme
) {
1556 // During recoding, we might have deleted some corrupt keys. Because of this, we might have zombie SSGroup records left in
1557 // the database that have no matching key. If we get a DL_RECORD_NOT_FOUND error, delete the matching item record.
1558 if (cssme
.osStatus() == CSSMERR_DL_RECORD_NOT_FOUND
) {
1559 secdebugfunc("integrity", "deleting corrupt (Not Found) record");
1560 keychain
->deleteItem(item
);
1568 // If we reach here, tell releaseFileLock() to commit the transaction and return the new blob version
1569 secdebugfunc("integrity", "releasing file lock");
1570 db
->releaseFileLock(true);
1572 secdebugfunc("integrity", "success, returning version %d", newDbVersion
);
1573 return newDbVersion
;
1575 } catch (CssmError cssme
) {
1576 const char* errStr
= cssmErrorString(cssme
.error
);
1577 secdebugfunc("integrity", "caught CssmError: %d %s", (int) cssme
.error
, errStr
);
1578 db
->releaseFileLock(false);
1579 } catch (MacOSError mose
) {
1580 secdebugfunc("integrity", "MacOSError: %d", (int)mose
.osStatus());
1581 db
->releaseFileLock(false);
1583 // We failed to migrate. We won't commit the transaction, so the blob on-disk stays the same.
1584 secdebugfunc("integrity", "unknown error");
1585 db
->releaseFileLock(false);
1588 // If we reached here, we failed the migration. Return the old version.
1589 return oldBlobVersion
;
1593 Keychain::Keychain()
1595 dispatch_once(&SecKeychainSystemKeychainChecked
, ^{
1596 check_system_keychain();
1600 Keychain::~Keychain()
1607 Keychain::optional(SecKeychainRef handle
)
1610 return KeychainImpl::required(handle
);
1612 return globals().storageManager
.defaultKeychain();
1616 CFIndex
KeychainCore::GetKeychainRetainCount(Keychain
& kc
)
1618 CFTypeRef ref
= kc
->handle(false);
1619 return CFGetRetainCount(ref
);
1624 // Create default credentials for this keychain.
1625 // This is triggered upon default open (i.e. a Db::activate() with no set credentials).
1627 // This function embodies the "default credentials" logic for Keychain-layer databases.
1629 const AccessCredentials
*
1630 KeychainImpl::makeCredentials()
1632 return defaultCredentials();
1636 const AccessCredentials
*
1637 KeychainImpl::defaultCredentials()
1639 StLock
<Mutex
>_(mMutex
);
1641 // Use custom unlock credentials for file keychains which have a referral
1642 // record and the standard credentials for all others.
1644 if (mDb
->dl()->guid() == gGuidAppleCSPDL
&& mCustomUnlockCreds(mDb
))
1645 return &mCustomUnlockCreds
;
1647 if (mDb
->dl()->guid() == gGuidAppleSdCSPDL
)
1648 return globals().smartcardCredentials();
1650 return globals().keychainCredentials();
1655 bool KeychainImpl::mayDelete()
1660 bool KeychainImpl::hasIntegrityProtection() {
1661 // This keychain only supports integrity if there's a database attached, that database is an Apple CSPDL, and the blob version is high enough
1662 if(mDb
&& (mDb
->dl()->guid() == gGuidAppleCSPDL
)) {
1663 if(mDb
->dbBlobVersion() >= SecurityServer::DbBlob::version_partition
) {
1666 secdebugfunc("integrity", "keychain blob version does not support integrity");
1670 secdebugfunc("integrity", "keychain guid does not support integrity");