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>
42 #include "SecKeychainPriv.h"
44 #include <Security/SecKeychainItemPriv.h>
45 #include <CoreFoundation/CoreFoundation.h>
46 #include "DLDbListCFPref.h"
48 #include <sys/param.h>
51 #include <sys/socket.h>
53 #include <sys/types.h>
56 static dispatch_once_t SecKeychainSystemKeychainChecked
;
58 OSStatus
SecKeychainSystemKeychainCheckWouldDeadlock()
60 dispatch_once(&SecKeychainSystemKeychainChecked
, ^{});
64 using namespace KeychainCore
;
65 using namespace CssmClient
;
68 typedef struct EventItem
70 SecKeychainEvent kcEvent
;
74 typedef std::list
<EventItem
> EventBufferSuper
;
75 class EventBuffer
: public EventBufferSuper
79 virtual ~EventBuffer ();
83 EventBuffer::~EventBuffer ()
92 KeychainSchemaImpl::KeychainSchemaImpl(const Db
&db
) : mMutex(Mutex::recursive
)
94 DbCursor
relations(db
);
95 relations
->recordType(CSSM_DL_DB_SCHEMA_INFO
);
96 DbAttributes
relationRecord(db
, 1);
97 relationRecord
.add(Schema::RelationID
);
98 DbUniqueRecord
outerUniqueId(db
);
100 while (relations
->next(&relationRecord
, NULL
, outerUniqueId
))
102 DbUniqueRecord
uniqueId(db
);
104 uint32 relationID
= relationRecord
.at(0);
105 if (CSSM_DB_RECORDTYPE_SCHEMA_START
<= relationID
106 && relationID
< CSSM_DB_RECORDTYPE_SCHEMA_END
)
109 // Create a cursor on the SCHEMA_ATTRIBUTES table for records with
110 // RelationID == relationID
111 DbCursor
attributes(db
);
112 attributes
->recordType(CSSM_DL_DB_SCHEMA_ATTRIBUTES
);
113 attributes
->add(CSSM_DB_EQUAL
, Schema::RelationID
, relationID
);
115 // Set up a record for retriving the SCHEMA_ATTRIBUTES
116 DbAttributes
attributeRecord(db
, 2);
117 attributeRecord
.add(Schema::AttributeFormat
);
118 attributeRecord
.add(Schema::AttributeID
);
120 RelationInfoMap
&rim
= mDatabaseInfoMap
[relationID
];
121 while (attributes
->next(&attributeRecord
, NULL
, uniqueId
))
122 rim
[attributeRecord
.at(1)] = attributeRecord
.at(0);
124 // Create a cursor on the CSSM_DL_DB_SCHEMA_INDEXES table for records
125 // with RelationID == relationID
126 DbCursor
indexes(db
);
127 indexes
->recordType(CSSM_DL_DB_SCHEMA_INDEXES
);
128 indexes
->conjunctive(CSSM_DB_AND
);
129 indexes
->add(CSSM_DB_EQUAL
, Schema::RelationID
, relationID
);
130 indexes
->add(CSSM_DB_EQUAL
, Schema::IndexType
,
131 uint32(CSSM_DB_INDEX_UNIQUE
));
133 // Set up a record for retriving the SCHEMA_INDEXES
134 DbAttributes
indexRecord(db
, 1);
135 indexRecord
.add(Schema::AttributeID
);
137 CssmAutoDbRecordAttributeInfo
&infos
=
138 *new CssmAutoDbRecordAttributeInfo();
140 insert(PrimaryKeyInfoMap::value_type(relationID
, &infos
));
141 infos
.DataRecordType
= relationID
;
142 while (indexes
->next(&indexRecord
, NULL
, uniqueId
))
144 CssmDbAttributeInfo
&info
= infos
.add();
145 info
.AttributeNameFormat
= CSSM_DB_ATTRIBUTE_NAME_AS_INTEGER
;
146 info
.Label
.AttributeID
= indexRecord
.at(0);
147 // @@@ Might insert bogus value if DB is corrupt
148 info
.AttributeFormat
= rim
[info
.Label
.AttributeID
];
153 KeychainSchemaImpl::~KeychainSchemaImpl()
157 map
<CSSM_DB_RECORDTYPE
, CssmAutoDbRecordAttributeInfo
*>::iterator it
= mPrimaryKeyInfoMap
.begin();
158 while (it
!= mPrimaryKeyInfoMap
.end())
163 // for_each_map_delete(mPrimaryKeyInfoMap.begin(), mPrimaryKeyInfoMap.end());
170 const KeychainSchemaImpl::RelationInfoMap
&
171 KeychainSchemaImpl::relationInfoMapFor(CSSM_DB_RECORDTYPE recordType
) const
173 DatabaseInfoMap::const_iterator dit
= mDatabaseInfoMap
.find(recordType
);
174 if (dit
== mDatabaseInfoMap
.end())
175 MacOSError::throwMe(errSecNoSuchClass
);
179 bool KeychainSchemaImpl::hasRecordType (CSSM_DB_RECORDTYPE recordType
) const
181 DatabaseInfoMap::const_iterator it
= mDatabaseInfoMap
.find(recordType
);
182 return it
!= mDatabaseInfoMap
.end();
186 KeychainSchemaImpl::hasAttribute(CSSM_DB_RECORDTYPE recordType
, uint32 attributeId
) const
190 const RelationInfoMap
&rmap
= relationInfoMapFor(recordType
);
191 RelationInfoMap::const_iterator rit
= rmap
.find(attributeId
);
192 return rit
!= rmap
.end();
194 catch (MacOSError result
)
196 if (result
.osStatus () == errSecNoSuchClass
)
207 CSSM_DB_ATTRIBUTE_FORMAT
208 KeychainSchemaImpl::attributeFormatFor(CSSM_DB_RECORDTYPE recordType
, uint32 attributeId
) const
210 const RelationInfoMap
&rmap
= relationInfoMapFor(recordType
);
211 RelationInfoMap::const_iterator rit
= rmap
.find(attributeId
);
212 if (rit
== rmap
.end())
213 MacOSError::throwMe(errSecNoSuchAttr
);
219 KeychainSchemaImpl::attributeInfoFor(CSSM_DB_RECORDTYPE recordType
, uint32 attributeId
) const
221 CSSM_DB_ATTRIBUTE_INFO info
;
222 info
.AttributeFormat
= attributeFormatFor(recordType
, attributeId
);
223 info
.AttributeNameFormat
= CSSM_DB_ATTRIBUTE_NAME_AS_INTEGER
;
224 info
.Label
.AttributeID
= attributeId
;
230 KeychainSchemaImpl::getAttributeInfoForRecordType(CSSM_DB_RECORDTYPE recordType
, SecKeychainAttributeInfo
**Info
) const
232 const RelationInfoMap
&rmap
= relationInfoMapFor(recordType
);
234 SecKeychainAttributeInfo
*theList
=reinterpret_cast<SecKeychainAttributeInfo
*>(malloc(sizeof(SecKeychainAttributeInfo
)));
236 size_t capacity
=rmap
.size();
237 UInt32
*tagBuf
=reinterpret_cast<UInt32
*>(malloc(capacity
*sizeof(UInt32
)));
238 UInt32
*formatBuf
=reinterpret_cast<UInt32
*>(malloc(capacity
*sizeof(UInt32
)));
242 for (RelationInfoMap::const_iterator rit
= rmap
.begin(); rit
!= rmap
.end(); ++rit
)
247 if (capacity
<= i
) capacity
= i
+ 1;
248 tagBuf
=reinterpret_cast<UInt32
*>(realloc(tagBuf
, (capacity
*sizeof(UInt32
))));
249 formatBuf
=reinterpret_cast<UInt32
*>(realloc(tagBuf
, (capacity
*sizeof(UInt32
))));
251 tagBuf
[i
]=rit
->first
;
252 formatBuf
[i
++]=rit
->second
;
257 theList
->format
=formatBuf
;
262 const CssmAutoDbRecordAttributeInfo
&
263 KeychainSchemaImpl::primaryKeyInfosFor(CSSM_DB_RECORDTYPE recordType
) const
265 PrimaryKeyInfoMap::const_iterator it
;
266 it
= mPrimaryKeyInfoMap
.find(recordType
);
268 if (it
== mPrimaryKeyInfoMap
.end())
269 MacOSError::throwMe(errSecNoSuchClass
); // @@@ Not really but whatever.
275 KeychainSchemaImpl::operator <(const KeychainSchemaImpl
&other
) const
277 return mDatabaseInfoMap
< other
.mDatabaseInfoMap
;
281 KeychainSchemaImpl::operator ==(const KeychainSchemaImpl
&other
) const
283 return mDatabaseInfoMap
== other
.mDatabaseInfoMap
;
287 KeychainSchemaImpl::didCreateRelation(CSSM_DB_RECORDTYPE relationID
,
288 const char *inRelationName
,
289 uint32 inNumberOfAttributes
,
290 const CSSM_DB_SCHEMA_ATTRIBUTE_INFO
*pAttributeInfo
,
291 uint32 inNumberOfIndexes
,
292 const CSSM_DB_SCHEMA_INDEX_INFO
*pIndexInfo
)
294 StLock
<Mutex
>_(mMutex
);
296 if (CSSM_DB_RECORDTYPE_SCHEMA_START
<= relationID
297 && relationID
< CSSM_DB_RECORDTYPE_SCHEMA_END
)
300 // if our schema is already in the map, return
301 if (mPrimaryKeyInfoMap
.find(relationID
) != mPrimaryKeyInfoMap
.end())
306 RelationInfoMap
&rim
= mDatabaseInfoMap
[relationID
];
307 for (uint32 ix
= 0; ix
< inNumberOfAttributes
; ++ix
)
308 rim
[pAttributeInfo
[ix
].AttributeId
] = pAttributeInfo
[ix
].DataType
;
310 CssmAutoDbRecordAttributeInfo
*infos
= new CssmAutoDbRecordAttributeInfo();
313 insert(PrimaryKeyInfoMap::value_type(relationID
, infos
));
314 infos
->DataRecordType
= relationID
;
315 for (uint32 ix
= 0; ix
< inNumberOfIndexes
; ++ix
)
316 if (pIndexInfo
[ix
].IndexType
== CSSM_DB_INDEX_UNIQUE
)
318 CssmDbAttributeInfo
&info
= infos
->add();
319 info
.AttributeNameFormat
= CSSM_DB_ATTRIBUTE_NAME_AS_INTEGER
;
320 info
.Label
.AttributeID
= pIndexInfo
[ix
].AttributeId
;
321 info
.AttributeFormat
= rim
[info
.Label
.AttributeID
];
327 KeychainSchema::~KeychainSchema()
336 SecKeychainEvent eventCode
;
337 PrimaryKey primaryKey
;
339 typedef std::list
<Event
> EventList
;
341 #define SYSTEM_KEYCHAIN_CHECK_UNIX_BASE_NAME "/var/run/systemkeychaincheck"
342 #define SYSTEM_KEYCHAIN_CHECK_UNIX_DOMAIN_SOCKET_NAME (SYSTEM_KEYCHAIN_CHECK_UNIX_BASE_NAME ".socket")
343 #define SYSTEM_KEYCHAIN_CHECK_COMPLETE_FILE_NAME (SYSTEM_KEYCHAIN_CHECK_UNIX_BASE_NAME ".done")
345 static void check_system_keychain()
347 // sadly we can't use XPC here, XPC_DOMAIN_TYPE_SYSTEM doesn't exist yet. Also xpc-helper uses the
348 // keychain API (I assume for checking codesign things). So we use Unix Domain Sockets.
350 // NOTE: if we hit a system error we attempt to log it, and then just don't check the system keychain.
351 // In theory a system might be able to recover from this state if we let it try to muddle along, and
352 // past behaviour didn't even try this hard to do the keychain check. In particular we could be in a
353 // sandbox'ed process. So we just do our best and let another process try again.
355 struct stat keycheck_file_info
;
356 if (stat(SYSTEM_KEYCHAIN_CHECK_COMPLETE_FILE_NAME
, &keycheck_file_info
) < 0) {
357 int server_fd
= socket(PF_UNIX
, SOCK_STREAM
, 0);
359 syslog(LOG_ERR
, "Can't get socket (%m) system keychain may be unchecked");
363 struct sockaddr_un keychain_check_server_address
;
364 keychain_check_server_address
.sun_family
= AF_UNIX
;
365 if (strlcpy(keychain_check_server_address
.sun_path
, SYSTEM_KEYCHAIN_CHECK_UNIX_DOMAIN_SOCKET_NAME
, sizeof(keychain_check_server_address
.sun_path
)) > sizeof(keychain_check_server_address
.sun_path
)) {
366 // It would be nice if we could compile time assert this
367 syslog(LOG_ERR
, "Socket path too long, max length %lu, your length %lu", (unsigned long)sizeof(keychain_check_server_address
.sun_path
), (unsigned long)strlen(SYSTEM_KEYCHAIN_CHECK_UNIX_DOMAIN_SOCKET_NAME
));
371 keychain_check_server_address
.sun_len
= SUN_LEN(&keychain_check_server_address
);
373 int rc
= connect(server_fd
, (struct sockaddr
*)&keychain_check_server_address
, keychain_check_server_address
.sun_len
);
375 syslog(LOG_ERR
, "Can not connect to %s: %m", SYSTEM_KEYCHAIN_CHECK_UNIX_DOMAIN_SOCKET_NAME
);
380 // this read lets us block until the EOF comes, we don't ever get a byte (and if we do, we don't care about it)
382 ssize_t read_size
= read(server_fd
, &byte
, 1);
384 syslog(LOG_ERR
, "Error reading from system keychain checker: %m");
395 KeychainImpl::KeychainImpl(const Db
&db
)
396 : mInCache(false), mDb(db
), mCustomUnlockCreds (this), mIsInBatchMode (false), mMutex(Mutex::recursive
)
398 dispatch_once(&SecKeychainSystemKeychainChecked
, ^{
399 check_system_keychain();
401 mDb
->defaultCredentials(this); // install activation hook
402 mEventBuffer
= new EventBuffer
;
405 KeychainImpl::~KeychainImpl()
409 // Remove ourselves from the cache if we are in it.
410 // fprintf(stderr, "Removing %p from storage manager cache.\n", handle(false));
411 globals().storageManager
.removeKeychain(dlDbIdentifier(), this);
420 KeychainImpl::getMutexForObject()
422 return globals().storageManager
.getStorageManagerMutex();
426 KeychainImpl::getKeychainMutex()
431 void KeychainImpl::aboutToDestruct()
433 // remove me from the global cache, we are done
434 // fprintf(stderr, "Destructing keychain object\n");
435 DLDbIdentifier identifier
= dlDbIdentifier();
436 globals().storageManager
.removeKeychain(identifier
, this);
440 KeychainImpl::operator ==(const KeychainImpl
&keychain
) const
442 return dlDbIdentifier() == keychain
.dlDbIdentifier();
446 KeychainImpl::createCursor(SecItemClass itemClass
, const SecKeychainAttributeList
*attrList
)
448 StLock
<Mutex
>_(mMutex
);
450 StorageManager::KeychainList keychains
;
451 keychains
.push_back(Keychain(this));
452 return KCCursor(keychains
, itemClass
, attrList
);
456 KeychainImpl::createCursor(const SecKeychainAttributeList
*attrList
)
458 StLock
<Mutex
>_(mMutex
);
460 StorageManager::KeychainList keychains
;
461 keychains
.push_back(Keychain(this));
462 return KCCursor(keychains
, attrList
);
466 KeychainImpl::create(UInt32 passwordLength
, const void *inPassword
)
468 StLock
<Mutex
>_(mMutex
);
476 Allocator
&alloc
= Allocator::standard();
478 // @@@ Share this instance
480 const CssmData
password(const_cast<void *>(inPassword
), passwordLength
);
481 AclFactory::PasswordChangeCredentials
pCreds (password
, alloc
);
482 AclFactory::AnyResourceContext
rcc(pCreds
);
486 void KeychainImpl::create(ConstStringPtr inPassword
)
488 StLock
<Mutex
>_(mMutex
);
491 create(static_cast<UInt32
>(inPassword
[0]), &inPassword
[1]);
497 KeychainImpl::create()
499 StLock
<Mutex
>_(mMutex
);
501 AclFactory aclFactory
;
502 AclFactory::AnyResourceContext
rcc(aclFactory
.unlockCred());
506 void KeychainImpl::createWithBlob(CssmData
&blob
)
508 StLock
<Mutex
>_(mMutex
);
510 mDb
->dbInfo(&Schema::DBInfo
);
511 AclFactory aclFactory
;
512 AclFactory::AnyResourceContext
rcc(aclFactory
.unlockCred());
513 mDb
->resourceControlContext (&rcc
);
516 mDb
->createWithBlob(blob
);
520 mDb
->resourceControlContext(NULL
);
524 mDb
->resourceControlContext(NULL
);
525 mDb
->dbInfo(NULL
); // Clear the schema (to not break an open call later)
526 globals().storageManager
.created(Keychain(this));
528 KCEventNotifier::PostKeychainEvent (kSecKeychainListChangedEvent
, this, NULL
);
532 KeychainImpl::create(const ResourceControlContext
*rcc
)
534 StLock
<Mutex
>_(mMutex
);
536 mDb
->dbInfo(&Schema::DBInfo
); // Set the schema (to force a create)
537 mDb
->resourceControlContext(rcc
);
544 mDb
->resourceControlContext(NULL
);
545 mDb
->dbInfo(NULL
); // Clear the schema (to not break an open call later)
548 mDb
->resourceControlContext(NULL
);
549 mDb
->dbInfo(NULL
); // Clear the schema (to not break an open call later)
550 globals().storageManager
.created(Keychain(this));
556 StLock
<Mutex
>_(mMutex
);
564 StLock
<Mutex
>_(mMutex
);
570 KeychainImpl::unlock()
572 StLock
<Mutex
>_(mMutex
);
578 KeychainImpl::unlock(const CssmData
&password
)
580 StLock
<Mutex
>_(mMutex
);
582 mDb
->unlock(password
);
586 KeychainImpl::unlock(ConstStringPtr password
)
588 StLock
<Mutex
>_(mMutex
);
592 const CssmData
data(const_cast<unsigned char *>(&password
[1]), password
[0]);
600 KeychainImpl::stash()
602 StLock
<Mutex
>_(mMutex
);
608 KeychainImpl::stashCheck()
610 StLock
<Mutex
>_(mMutex
);
616 KeychainImpl::getSettings(uint32
&outIdleTimeOut
, bool &outLockOnSleep
)
618 StLock
<Mutex
>_(mMutex
);
620 mDb
->getSettings(outIdleTimeOut
, outLockOnSleep
);
624 KeychainImpl::setSettings(uint32 inIdleTimeOut
, bool inLockOnSleep
)
626 StLock
<Mutex
>_(mMutex
);
628 // The .Mac syncing code only makes sense for the AppleFile CSP/DL,
629 // but other DLs such as the OCSP and LDAP DLs do not expose a way to
630 // change settings or the password. To make a minimal change that only affects
631 // the smartcard case, we only look for that CSP/DL
633 bool isSmartcard
= (mDb
->dl()->guid() == gGuidAppleSdCSPDL
);
635 // get the old keychain blob so that we can tell .Mac to resync it
636 CssmAutoData
oldBlob(mDb
->allocator());
638 mDb
->copyBlob(oldBlob
.get());
640 mDb
->setSettings(inIdleTimeOut
, inLockOnSleep
);
644 KeychainImpl::changePassphrase(UInt32 oldPasswordLength
, const void *oldPassword
,
645 UInt32 newPasswordLength
, const void *newPassword
)
647 StLock
<Mutex
>_(mMutex
);
649 bool isSmartcard
= (mDb
->dl()->guid() == gGuidAppleSdCSPDL
);
651 TrackingAllocator
allocator(Allocator::standard());
652 AutoCredentials cred
= AutoCredentials(allocator
);
655 const CssmData
&oldPass
= *new(allocator
) CssmData(const_cast<void *>(oldPassword
), oldPasswordLength
);
656 TypedList
&oldList
= *new(allocator
) TypedList(allocator
, CSSM_SAMPLE_TYPE_KEYCHAIN_LOCK
);
657 oldList
.append(new(allocator
) ListElement(CSSM_SAMPLE_TYPE_PASSWORD
));
658 oldList
.append(new(allocator
) ListElement(oldPass
));
664 const CssmData
&newPass
= *new(allocator
) CssmData(const_cast<void *>(newPassword
), newPasswordLength
);
665 TypedList
&newList
= *new(allocator
) TypedList(allocator
, CSSM_SAMPLE_TYPE_KEYCHAIN_CHANGE_LOCK
);
666 newList
.append(new(allocator
) ListElement(CSSM_SAMPLE_TYPE_PASSWORD
));
667 newList
.append(new(allocator
) ListElement(newPass
));
671 // get the old keychain blob so that we can tell .Mac to resync it
672 CssmAutoData
oldBlob(mDb
->allocator());
674 mDb
->copyBlob(oldBlob
.get());
676 mDb
->changePassphrase(&cred
);
680 KeychainImpl::changePassphrase(ConstStringPtr oldPassword
, ConstStringPtr newPassword
)
682 StLock
<Mutex
>_(mMutex
);
684 const void *oldPtr
, *newPtr
;
685 UInt32 oldLen
, newLen
;
688 oldLen
= oldPassword
[0];
689 oldPtr
= oldPassword
+ 1;
699 newLen
= newPassword
[0];
700 newPtr
= newPassword
+ 1;
708 changePassphrase(oldLen
, oldPtr
, newLen
, newPtr
);
712 KeychainImpl::authenticate(const CSSM_ACCESS_CREDENTIALS
*cred
)
714 StLock
<Mutex
>_(mMutex
);
717 MacOSError::throwMe(errSecNoSuchKeychain
);
719 MacOSError::throwMe(errSecUnimplemented
);
723 KeychainImpl::status() const
725 // @@@ We should figure out the read/write status though a DL passthrough
726 // or some other way. Also should locked be unlocked read only or just
728 return (mDb
->isLocked() ? 0 : kSecUnlockStateStatus
| kSecWritePermStatus
)
729 | kSecReadPermStatus
;
733 KeychainImpl::exists()
735 StLock
<Mutex
>_(mMutex
);
741 // Ok to leave the mDb open since it will get closed when it goes away.
743 catch (const CssmError
&e
)
745 if (e
.osStatus() != CSSMERR_DL_DATASTORE_DOESNOT_EXIST
)
754 KeychainImpl::isActive() const
756 return mDb
->isActive();
759 void KeychainImpl::completeAdd(Item
&inItem
, PrimaryKey
&primaryKey
)
763 // The inItem shouldn't be in the cache yet
764 assert(!inItem
->inCache());
766 // Insert inItem into mDbItemMap with key primaryKey. p.second will be
767 // true if it got inserted. If not p.second will be false and p.first
768 // will point to the current entry with key primaryKey.
769 pair
<DbItemMap::iterator
, bool> p
=
770 mDbItemMap
.insert(DbItemMap::value_type(primaryKey
, inItem
.get()));
773 // There was already an ItemImpl * in mDbItemMap with key
774 // primaryKey. Get a ref to the pointer to it so we can assign a
775 // new value to it below.
776 ItemImpl
*oldItem
= p
.first
->second
;
778 // @@@ If this happens we are breaking our API contract of
779 // uniquifying items. We really need to insert the item into the
780 // map before we start the add. And have the item be in an
781 // "is being added" state.
782 assert(oldItem
->inCache());
783 secdebug("keychain", "add of new item %p somehow replaced %p",
784 inItem
.get(), oldItem
);
786 // make sure that we both mark the item and remove the item from the cache
787 removeItem(oldItem
->primaryKey(), oldItem
);
788 oldItem
= inItem
.get();
791 inItem
->inCache(true);
795 KeychainImpl::addCopy(Item
&inItem
)
797 Keychain
keychain(this);
798 PrimaryKey primaryKey
= inItem
->addWithCopyInfo(keychain
, true);
799 completeAdd(inItem
, primaryKey
);
800 postEvent(kSecAddEvent
, inItem
);
804 KeychainImpl::add(Item
&inItem
)
806 Keychain
keychain(this);
807 PrimaryKey primaryKey
= inItem
->add(keychain
);
808 completeAdd(inItem
, primaryKey
);
809 postEvent(kSecAddEvent
, inItem
);
813 KeychainImpl::didUpdate(const Item
&inItem
, PrimaryKey
&oldPK
,
816 // If the primary key hasn't changed we don't need to update mDbItemMap.
819 // If inItem isn't in the cache we don't need to update mDbItemMap.
820 assert(inItem
->inCache());
821 if (inItem
->inCache())
823 // First remove the entry for inItem in mDbItemMap with key oldPK.
824 DbItemMap::iterator it
= mDbItemMap
.find(oldPK
);
825 if (it
!= mDbItemMap
.end() && (ItemImpl
*) it
->second
== inItem
.get())
826 mDbItemMap
.erase(it
);
828 // Insert inItem into mDbItemMap with key newPK. p.second will be
829 // true if it got inserted. If not p.second will be false and
830 // p.first will point to the current entry with key newPK.
831 pair
<DbItemMap::iterator
, bool> p
=
832 mDbItemMap
.insert(DbItemMap::value_type(newPK
, inItem
.get()));
835 // There was already an ItemImpl * in mDbItemMap with key
836 // primaryKey. Get a ref to the pointer to it so we can assign
837 // a new value to it below.
838 ItemImpl
*oldItem
= p
.first
->second
;
840 // @@@ If this happens we are breaking our API contract of
841 // uniquifying items. We really need to insert the item into
842 // the map with the new primary key before we start the update.
843 // And have the item be in an "is being updated" state.
844 assert(oldItem
->inCache());
845 secdebug("keychain", "update of item %p somehow replaced %p",
846 inItem
.get(), oldItem
);
847 oldItem
->inCache(false);
848 oldItem
= inItem
.get();
853 postEvent(kSecUpdateEvent
, inItem
);
857 KeychainImpl::deleteItem(Item
&inoutItem
)
860 // We don't need to hold the DO mutex through event posting, and, in fact, doing so causes deadlock.
861 // Hold it only as long as needed, instead.
864 // item must be persistent
865 if (!inoutItem
->isPersistent())
866 MacOSError::throwMe(errSecInvalidItemRef
);
868 DbUniqueRecord uniqueId
= inoutItem
->dbUniqueRecord();
869 PrimaryKey primaryKey
= inoutItem
->primaryKey();
870 uniqueId
->deleteRecord();
872 // Don't remove the item from the mDbItemMap here since this would cause
873 // us to report a new item to our caller when we receive the
874 // kSecDeleteEvent notification.
875 // It will be removed before we post the notification, because
876 // CCallbackMgr will call didDeleteItem()
878 // Post the notification for the item deletion with
879 // the primaryKey obtained when the item still existed
882 postEvent(kSecDeleteEvent
, inoutItem
);
889 StLock
<Mutex
>_(mMutex
);
891 if (!mDb
->dl()->subserviceMask() & CSSM_SERVICE_CSP
)
892 MacOSError::throwMe(errSecInvalidKeychain
);
894 // Try to cast first to a CSPDL to handle case where we don't have an SSDb
897 CssmClient::CSPDL
cspdl(dynamic_cast<CssmClient::CSPDLImpl
*>(&*mDb
->dl()));
902 SSDbImpl
* impl
= dynamic_cast<SSDbImpl
*>(&(*mDb
));
905 CssmError::throwMe(CSSMERR_CSSM_INVALID_POINTER
);
914 KeychainImpl::makePrimaryKey(CSSM_DB_RECORDTYPE recordType
, DbUniqueRecord
&uniqueId
)
916 StLock
<Mutex
>_(mMutex
);
918 DbAttributes
primaryKeyAttrs(uniqueId
->database());
919 primaryKeyAttrs
.recordType(recordType
);
920 gatherPrimaryKeyAttributes(primaryKeyAttrs
);
921 uniqueId
->get(&primaryKeyAttrs
, NULL
);
922 return PrimaryKey(primaryKeyAttrs
);
925 const CssmAutoDbRecordAttributeInfo
&
926 KeychainImpl::primaryKeyInfosFor(CSSM_DB_RECORDTYPE recordType
)
928 StLock
<Mutex
>_(mMutex
);
932 return keychainSchema()->primaryKeyInfosFor(recordType
);
934 catch (const CommonError
&error
)
936 switch (error
.osStatus())
938 case errSecNoSuchClass
:
939 case CSSMERR_DL_INVALID_RECORDTYPE
:
941 return keychainSchema()->primaryKeyInfosFor(recordType
);
948 void KeychainImpl::gatherPrimaryKeyAttributes(DbAttributes
& primaryKeyAttrs
)
950 StLock
<Mutex
>_(mMutex
);
952 const CssmAutoDbRecordAttributeInfo
&infos
=
953 primaryKeyInfosFor(primaryKeyAttrs
.recordType());
955 // @@@ fix this to not copy info.
956 for (uint32 i
= 0; i
< infos
.size(); i
++)
957 primaryKeyAttrs
.add(infos
.at(i
));
961 KeychainImpl::_lookupItem(const PrimaryKey
&primaryKey
)
963 DbItemMap::iterator it
= mDbItemMap
.find(primaryKey
);
964 if (it
!= mDbItemMap
.end())
966 if (it
->second
== NULL
)
968 // we've been weak released...
969 mDbItemMap
.erase(it
);
981 KeychainImpl::item(const PrimaryKey
&primaryKey
)
983 StLock
<Mutex
>_(mMutex
);
985 // Lookup the item in the map while holding the apiLock.
986 ItemImpl
*itemImpl
= _lookupItem(primaryKey
);
988 return Item(itemImpl
);
992 // We didn't find it so create a new item with just a keychain and
993 // a primary key. However since we aren't holding
994 // globals().apiLock anymore some other thread might have beaten
995 // us to creating this item and adding it to the cache. If that
996 // happens we retry the lookup.
997 return Item(this, primaryKey
);
999 catch (const MacOSError
&e
)
1001 // If the item creation failed because some other thread already
1002 // inserted this item into the cache we retry the lookup.
1003 if (e
.osStatus() == errSecDuplicateItem
)
1005 // Lookup the item in the map while holding the apiLock.
1006 ItemImpl
*itemImpl
= _lookupItem(primaryKey
);
1008 return Item(itemImpl
);
1016 KeychainImpl::item(CSSM_DB_RECORDTYPE recordType
, DbUniqueRecord
&uniqueId
)
1018 StLock
<Mutex
>_(mMutex
);
1020 PrimaryKey primaryKey
= makePrimaryKey(recordType
, uniqueId
);
1022 // Lookup the item in the map while holding the apiLock.
1023 ItemImpl
*itemImpl
= _lookupItem(primaryKey
);
1027 return Item(itemImpl
);
1033 // We didn't find it so create a new item with a keychain, a primary key
1034 // and a DbUniqueRecord. However since we aren't holding
1035 // globals().apiLock anymore some other thread might have beaten
1036 // us to creating this item and adding it to the cache. If that
1037 // happens we retry the lookup.
1038 return Item(this, primaryKey
, uniqueId
);
1040 catch (const MacOSError
&e
)
1042 // If the item creation failed because some other thread already
1043 // inserted this item into the cache we retry the lookup.
1044 if (e
.osStatus() == errSecDuplicateItem
)
1046 // Lookup the item in the map while holding the apiLock.
1047 ItemImpl
*itemImpl
= _lookupItem(primaryKey
);
1049 return Item(itemImpl
);
1056 KeychainImpl::keychainSchema()
1058 StLock
<Mutex
>_(mMutex
);
1059 if (!mKeychainSchema
)
1060 mKeychainSchema
= KeychainSchema(mDb
);
1062 return mKeychainSchema
;
1065 void KeychainImpl::resetSchema()
1067 mKeychainSchema
= NULL
; // re-fetch it from db next time
1071 // Called from DbItemImpl's constructor (so it is only partially constructed),
1072 // add it to the map.
1074 KeychainImpl::addItem(const PrimaryKey
&primaryKey
, ItemImpl
*dbItemImpl
)
1076 StLock
<Mutex
>_(mMutex
);
1078 // The dbItemImpl shouldn't be in the cache yet
1079 assert(!dbItemImpl
->inCache());
1081 // Insert dbItemImpl into mDbItemMap with key primaryKey. p.second will
1082 // be true if it got inserted. If not p.second will be false and p.first
1083 // will point to the current entry with key primaryKey.
1084 pair
<DbItemMap::iterator
, bool> p
=
1085 mDbItemMap
.insert(DbItemMap::value_type(primaryKey
, dbItemImpl
));
1089 // There was already an ItemImpl * in mDbItemMap with key primaryKey.
1090 // There is a race condition here when being called in multiple threads
1091 // We might have added an item using add and received a notification at
1093 MacOSError::throwMe(errSecDuplicateItem
);
1096 dbItemImpl
->inCache(true);
1100 KeychainImpl::didDeleteItem(ItemImpl
*inItemImpl
)
1102 StLock
<Mutex
>_(mMutex
);
1104 // Called by CCallbackMgr
1105 secdebug("kcnotify", "%p notified that item %p was deleted", this, inItemImpl
);
1106 removeItem(inItemImpl
->primaryKey(), inItemImpl
);
1110 KeychainImpl::removeItem(const PrimaryKey
&primaryKey
, ItemImpl
*inItemImpl
)
1112 StLock
<Mutex
>_(mMutex
);
1114 // If inItemImpl isn't in the cache to begin with we are done.
1115 if (!inItemImpl
->inCache())
1118 DbItemMap::iterator it
= mDbItemMap
.find(primaryKey
);
1119 if (it
!= mDbItemMap
.end() && (ItemImpl
*) it
->second
== inItemImpl
)
1120 mDbItemMap
.erase(it
);
1122 inItemImpl
->inCache(false);
1126 KeychainImpl::getAttributeInfoForItemID(CSSM_DB_RECORDTYPE itemID
,
1127 SecKeychainAttributeInfo
**Info
)
1129 StLock
<Mutex
>_(mMutex
);
1133 keychainSchema()->getAttributeInfoForRecordType(itemID
, Info
);
1135 catch (const CommonError
&error
)
1137 switch (error
.osStatus())
1139 case errSecNoSuchClass
:
1140 case CSSMERR_DL_INVALID_RECORDTYPE
:
1142 keychainSchema()->getAttributeInfoForRecordType(itemID
, Info
);
1150 KeychainImpl::freeAttributeInfo(SecKeychainAttributeInfo
*Info
)
1158 KeychainImpl::attributeInfoFor(CSSM_DB_RECORDTYPE recordType
, UInt32 tag
)
1160 StLock
<Mutex
>_(mMutex
);
1164 return keychainSchema()->attributeInfoFor(recordType
, tag
);
1166 catch (const CommonError
&error
)
1168 switch (error
.osStatus())
1170 case errSecNoSuchClass
:
1171 case CSSMERR_DL_INVALID_RECORDTYPE
:
1173 return keychainSchema()->attributeInfoFor(recordType
, tag
);
1181 KeychainImpl::recode(const CssmData
&data
, const CssmData
&extraData
)
1183 StLock
<Mutex
>_(mMutex
);
1185 mDb
->recode(data
, extraData
);
1189 KeychainImpl::copyBlob(CssmData
&data
)
1191 StLock
<Mutex
>_(mMutex
);
1193 mDb
->copyBlob(data
);
1197 KeychainImpl::setBatchMode(Boolean mode
, Boolean rollback
)
1199 StLock
<Mutex
>_(mMutex
);
1201 mDb
->setBatchMode(mode
, rollback
);
1202 mIsInBatchMode
= mode
;
1205 if (!rollback
) // was batch mode being turned off without an abort?
1208 EventBuffer::iterator it
= mEventBuffer
->begin();
1209 while (it
!= mEventBuffer
->end())
1211 PrimaryKey primaryKey
;
1214 primaryKey
= it
->item
->primaryKey();
1217 KCEventNotifier::PostKeychainEvent(it
->kcEvent
, mDb
->dlDbIdentifier(), primaryKey
);
1224 // notify that a keychain has changed in too many ways to count
1225 KCEventNotifier::PostKeychainEvent(kSecKeychainLeftBatchModeEvent
);
1226 mEventBuffer
->clear();
1230 KCEventNotifier::PostKeychainEvent(kSecKeychainEnteredBatchModeEvent
);
1235 KeychainImpl::postEvent(SecKeychainEvent kcEvent
, ItemImpl
* item
)
1237 PrimaryKey primaryKey
;
1240 StLock
<Mutex
>_(mMutex
);
1244 primaryKey
= item
->primaryKey();
1248 if (!mIsInBatchMode
)
1250 KCEventNotifier::PostKeychainEvent(kcEvent
, mDb
->dlDbIdentifier(), primaryKey
);
1254 StLock
<Mutex
>_(mMutex
);
1257 it
.kcEvent
= kcEvent
;
1263 mEventBuffer
->push_back (it
);
1267 Keychain::Keychain()
1269 dispatch_once(&SecKeychainSystemKeychainChecked
, ^{
1270 check_system_keychain();
1274 Keychain::~Keychain()
1281 Keychain::optional(SecKeychainRef handle
)
1284 return KeychainImpl::required(handle
);
1286 return globals().storageManager
.defaultKeychain();
1290 CFIndex
KeychainCore::GetKeychainRetainCount(Keychain
& kc
)
1292 CFTypeRef ref
= kc
->handle(false);
1293 return CFGetRetainCount(ref
);
1298 // Create default credentials for this keychain.
1299 // This is triggered upon default open (i.e. a Db::activate() with no set credentials).
1301 // This function embodies the "default credentials" logic for Keychain-layer databases.
1303 const AccessCredentials
*
1304 KeychainImpl::makeCredentials()
1306 return defaultCredentials();
1310 const AccessCredentials
*
1311 KeychainImpl::defaultCredentials()
1313 StLock
<Mutex
>_(mMutex
);
1315 // Use custom unlock credentials for file keychains which have a referral
1316 // record and the standard credentials for all others.
1318 if (mDb
->dl()->guid() == gGuidAppleCSPDL
&& mCustomUnlockCreds(mDb
))
1319 return &mCustomUnlockCreds
;
1321 if (mDb
->dl()->guid() == gGuidAppleSdCSPDL
)
1322 return globals().smartcardCredentials();
1324 return globals().keychainCredentials();
1329 bool KeychainImpl::mayDelete()