2 * Copyright (c) 2000-2004 Apple Computer, 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 <CoreServices/../Frameworks/CarbonCore.framework/Headers/MacErrors.h>
39 #include <security_cdsa_utilities/cssmdb.h>
40 #include <security_utilities/trackingallocator.h>
41 #include <security_keychain/SecCFTypes.h>
43 #include "SecKeychainPriv.h"
45 #include <Security/SecKeychainItemPriv.h>
46 #include <CoreFoundation/CoreFoundation.h>
47 #include "DLDbListCFPref.h"
49 #include <sys/param.h>
52 #include <sys/socket.h>
54 #include <sys/types.h>
57 static dispatch_once_t SecKeychainSystemKeychainChecked
;
59 OSStatus
SecKeychainSystemKeychainCheckWouldDeadlock()
61 dispatch_once(&SecKeychainSystemKeychainChecked
, ^{});
65 using namespace KeychainCore
;
66 using namespace CssmClient
;
69 typedef struct EventItem
71 SecKeychainEvent kcEvent
;
75 typedef std::list
<EventItem
> EventBufferSuper
;
76 class EventBuffer
: public EventBufferSuper
80 virtual ~EventBuffer ();
84 EventBuffer::~EventBuffer ()
93 KeychainSchemaImpl::KeychainSchemaImpl(const Db
&db
) : mMutex(Mutex::recursive
)
95 DbCursor
relations(db
);
96 relations
->recordType(CSSM_DL_DB_SCHEMA_INFO
);
97 DbAttributes
relationRecord(db
, 1);
98 relationRecord
.add(Schema::RelationID
);
99 DbUniqueRecord
outerUniqueId(db
);
101 while (relations
->next(&relationRecord
, NULL
, outerUniqueId
))
103 DbUniqueRecord
uniqueId(db
);
105 uint32 relationID
= relationRecord
.at(0);
106 if (CSSM_DB_RECORDTYPE_SCHEMA_START
<= relationID
107 && relationID
< CSSM_DB_RECORDTYPE_SCHEMA_END
)
110 // Create a cursor on the SCHEMA_ATTRIBUTES table for records with
111 // RelationID == relationID
112 DbCursor
attributes(db
);
113 attributes
->recordType(CSSM_DL_DB_SCHEMA_ATTRIBUTES
);
114 attributes
->add(CSSM_DB_EQUAL
, Schema::RelationID
, relationID
);
116 // Set up a record for retriving the SCHEMA_ATTRIBUTES
117 DbAttributes
attributeRecord(db
, 2);
118 attributeRecord
.add(Schema::AttributeFormat
);
119 attributeRecord
.add(Schema::AttributeID
);
121 RelationInfoMap
&rim
= mDatabaseInfoMap
[relationID
];
122 while (attributes
->next(&attributeRecord
, NULL
, uniqueId
))
123 rim
[attributeRecord
.at(1)] = attributeRecord
.at(0);
125 // Create a cursor on the CSSM_DL_DB_SCHEMA_INDEXES table for records
126 // with RelationID == relationID
127 DbCursor
indexes(db
);
128 indexes
->recordType(CSSM_DL_DB_SCHEMA_INDEXES
);
129 indexes
->conjunctive(CSSM_DB_AND
);
130 indexes
->add(CSSM_DB_EQUAL
, Schema::RelationID
, relationID
);
131 indexes
->add(CSSM_DB_EQUAL
, Schema::IndexType
,
132 uint32(CSSM_DB_INDEX_UNIQUE
));
134 // Set up a record for retriving the SCHEMA_INDEXES
135 DbAttributes
indexRecord(db
, 1);
136 indexRecord
.add(Schema::AttributeID
);
138 CssmAutoDbRecordAttributeInfo
&infos
=
139 *new CssmAutoDbRecordAttributeInfo();
141 insert(PrimaryKeyInfoMap::value_type(relationID
, &infos
));
142 infos
.DataRecordType
= relationID
;
143 while (indexes
->next(&indexRecord
, NULL
, uniqueId
))
145 CssmDbAttributeInfo
&info
= infos
.add();
146 info
.AttributeNameFormat
= CSSM_DB_ATTRIBUTE_NAME_AS_INTEGER
;
147 info
.Label
.AttributeID
= indexRecord
.at(0);
148 // @@@ Might insert bogus value if DB is corrupt
149 info
.AttributeFormat
= rim
[info
.Label
.AttributeID
];
154 KeychainSchemaImpl::~KeychainSchemaImpl()
158 for_each_map_delete(mPrimaryKeyInfoMap
.begin(), mPrimaryKeyInfoMap
.end());
165 const KeychainSchemaImpl::RelationInfoMap
&
166 KeychainSchemaImpl::relationInfoMapFor(CSSM_DB_RECORDTYPE recordType
) const
168 DatabaseInfoMap::const_iterator dit
= mDatabaseInfoMap
.find(recordType
);
169 if (dit
== mDatabaseInfoMap
.end())
170 MacOSError::throwMe(errSecNoSuchClass
);
174 bool KeychainSchemaImpl::hasRecordType (CSSM_DB_RECORDTYPE recordType
) const
176 DatabaseInfoMap::const_iterator it
= mDatabaseInfoMap
.find(recordType
);
177 return it
!= mDatabaseInfoMap
.end();
181 KeychainSchemaImpl::hasAttribute(CSSM_DB_RECORDTYPE recordType
, uint32 attributeId
) const
185 const RelationInfoMap
&rmap
= relationInfoMapFor(recordType
);
186 RelationInfoMap::const_iterator rit
= rmap
.find(attributeId
);
187 return rit
!= rmap
.end();
189 catch (MacOSError result
)
191 if (result
.osStatus () == errSecNoSuchClass
)
202 CSSM_DB_ATTRIBUTE_FORMAT
203 KeychainSchemaImpl::attributeFormatFor(CSSM_DB_RECORDTYPE recordType
, uint32 attributeId
) const
205 const RelationInfoMap
&rmap
= relationInfoMapFor(recordType
);
206 RelationInfoMap::const_iterator rit
= rmap
.find(attributeId
);
207 if (rit
== rmap
.end())
208 MacOSError::throwMe(errSecNoSuchAttr
);
214 KeychainSchemaImpl::attributeInfoFor(CSSM_DB_RECORDTYPE recordType
, uint32 attributeId
) const
216 CSSM_DB_ATTRIBUTE_INFO info
;
217 info
.AttributeFormat
= attributeFormatFor(recordType
, attributeId
);
218 info
.AttributeNameFormat
= CSSM_DB_ATTRIBUTE_NAME_AS_INTEGER
;
219 info
.Label
.AttributeID
= attributeId
;
225 KeychainSchemaImpl::getAttributeInfoForRecordType(CSSM_DB_RECORDTYPE recordType
, SecKeychainAttributeInfo
**Info
) const
227 const RelationInfoMap
&rmap
= relationInfoMapFor(recordType
);
229 SecKeychainAttributeInfo
*theList
=reinterpret_cast<SecKeychainAttributeInfo
*>(malloc(sizeof(SecKeychainAttributeInfo
)));
231 UInt32 capacity
=rmap
.size();
232 UInt32
*tagBuf
=reinterpret_cast<UInt32
*>(malloc(capacity
*sizeof(UInt32
)));
233 UInt32
*formatBuf
=reinterpret_cast<UInt32
*>(malloc(capacity
*sizeof(UInt32
)));
237 for (RelationInfoMap::const_iterator rit
= rmap
.begin(); rit
!= rmap
.end(); ++rit
)
242 if (capacity
<= i
) capacity
= i
+ 1;
243 tagBuf
=reinterpret_cast<UInt32
*>(realloc(tagBuf
, (capacity
*sizeof(UInt32
))));
244 formatBuf
=reinterpret_cast<UInt32
*>(realloc(tagBuf
, (capacity
*sizeof(UInt32
))));
246 tagBuf
[i
]=rit
->first
;
247 formatBuf
[i
++]=rit
->second
;
252 theList
->format
=formatBuf
;
257 const CssmAutoDbRecordAttributeInfo
&
258 KeychainSchemaImpl::primaryKeyInfosFor(CSSM_DB_RECORDTYPE recordType
) const
260 PrimaryKeyInfoMap::const_iterator it
;
261 it
= mPrimaryKeyInfoMap
.find(recordType
);
263 if (it
== mPrimaryKeyInfoMap
.end())
264 MacOSError::throwMe(errSecNoSuchClass
); // @@@ Not really but whatever.
270 KeychainSchemaImpl::operator <(const KeychainSchemaImpl
&other
) const
272 return mDatabaseInfoMap
< other
.mDatabaseInfoMap
;
276 KeychainSchemaImpl::operator ==(const KeychainSchemaImpl
&other
) const
278 return mDatabaseInfoMap
== other
.mDatabaseInfoMap
;
282 KeychainSchemaImpl::didCreateRelation(CSSM_DB_RECORDTYPE relationID
,
283 const char *inRelationName
,
284 uint32 inNumberOfAttributes
,
285 const CSSM_DB_SCHEMA_ATTRIBUTE_INFO
*pAttributeInfo
,
286 uint32 inNumberOfIndexes
,
287 const CSSM_DB_SCHEMA_INDEX_INFO
*pIndexInfo
)
289 StLock
<Mutex
>_(mMutex
);
291 if (CSSM_DB_RECORDTYPE_SCHEMA_START
<= relationID
292 && relationID
< CSSM_DB_RECORDTYPE_SCHEMA_END
)
295 RelationInfoMap
&rim
= mDatabaseInfoMap
[relationID
];
296 for (uint32 ix
= 0; ix
< inNumberOfAttributes
; ++ix
)
297 rim
[pAttributeInfo
[ix
].AttributeId
] = pAttributeInfo
[ix
].DataType
;
299 CssmAutoDbRecordAttributeInfo
&infos
=
300 *new CssmAutoDbRecordAttributeInfo();
302 insert(PrimaryKeyInfoMap::value_type(relationID
, &infos
));
303 infos
.DataRecordType
= relationID
;
304 for (uint32 ix
= 0; ix
< inNumberOfIndexes
; ++ix
)
305 if (pIndexInfo
[ix
].IndexType
== CSSM_DB_INDEX_UNIQUE
)
307 CssmDbAttributeInfo
&info
= infos
.add();
308 info
.AttributeNameFormat
= CSSM_DB_ATTRIBUTE_NAME_AS_INTEGER
;
309 info
.Label
.AttributeID
= pIndexInfo
[ix
].AttributeId
;
310 info
.AttributeFormat
= rim
[info
.Label
.AttributeID
];
317 SecKeychainEvent eventCode
;
318 PrimaryKey primaryKey
;
320 typedef std::list
<Event
> EventList
;
322 #define SYSTEM_KEYCHAIN_CHECK_UNIX_BASE_NAME "/var/run/systemkeychaincheck"
323 #define SYSTEM_KEYCHAIN_CHECK_UNIX_DOMAIN_SOCKET_NAME (SYSTEM_KEYCHAIN_CHECK_UNIX_BASE_NAME ".socket")
324 #define SYSTEM_KEYCHAIN_CHECK_COMPLETE_FILE_NAME (SYSTEM_KEYCHAIN_CHECK_UNIX_BASE_NAME ".done")
326 static void check_system_keychain()
328 // sadly we can't use XPC here, XPC_DOMAIN_TYPE_SYSTEM doesn't exist yet. Also xpc-helper uses the
329 // keychain API (I assume for checking codesign things). So we use Unix Domain Sockets.
331 // NOTE: if we hit a system error we attempt to log it, and then just don't check the system keychain.
332 // In theory a system might be able to recover from this state if we let it try to muddle along, and
333 // past behaviour didn't even try this hard to do the keychain check. In particular we could be in a
334 // sandbox'ed process. So we just do our best and let another process try again.
336 struct stat keycheck_file_info
;
337 if (stat(SYSTEM_KEYCHAIN_CHECK_COMPLETE_FILE_NAME
, &keycheck_file_info
) < 0) {
338 int server_fd
= socket(PF_UNIX
, SOCK_STREAM
, 0);
340 syslog(LOG_ERR
, "Can't get socket (%m) system keychain may be unchecked");
344 struct sockaddr_un keychain_check_server_address
;
345 keychain_check_server_address
.sun_family
= AF_UNIX
;
346 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
)) {
347 // It would be nice if we could compile time assert this
348 syslog(LOG_ERR
, "Socket path too long, max length %d, your length %d", sizeof(keychain_check_server_address
.sun_path
), strlen(SYSTEM_KEYCHAIN_CHECK_UNIX_DOMAIN_SOCKET_NAME
));
352 keychain_check_server_address
.sun_len
= SUN_LEN(&keychain_check_server_address
);
354 int rc
= connect(server_fd
, (struct sockaddr
*)&keychain_check_server_address
, keychain_check_server_address
.sun_len
);
356 syslog(LOG_ERR
, "Can not connect to %s: %m", SYSTEM_KEYCHAIN_CHECK_UNIX_DOMAIN_SOCKET_NAME
);
361 // 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)
363 ssize_t read_size
= read(server_fd
, &byte
, 1);
365 syslog(LOG_ERR
, "Error reading from system keychain checker: %m");
376 KeychainImpl::KeychainImpl(const Db
&db
)
377 : mInCache(false), mDb(db
), mCustomUnlockCreds (this), mIsInBatchMode (false), mMutex(Mutex::recursive
)
379 dispatch_once(&SecKeychainSystemKeychainChecked
, ^{
380 check_system_keychain();
382 mDb
->defaultCredentials(this); // install activation hook
383 mEventBuffer
= new EventBuffer
;
386 KeychainImpl::~KeychainImpl()
390 // Remove ourselves from the cache if we are in it.
391 // fprintf(stderr, "Removing %p from storage manager cache.\n", handle(false));
392 globals().storageManager
.removeKeychain(dlDbIdentifier(), this);
401 KeychainImpl::getMutexForObject()
403 return globals().storageManager
.getStorageManagerMutex();
407 KeychainImpl::getKeychainMutex()
412 void KeychainImpl::aboutToDestruct()
414 // remove me from the global cache, we are done
415 // fprintf(stderr, "Destructing keychain object\n");
416 DLDbIdentifier identifier
= dlDbIdentifier();
417 globals().storageManager
.removeKeychain(identifier
, this);
421 KeychainImpl::operator ==(const KeychainImpl
&keychain
) const
423 return dlDbIdentifier() == keychain
.dlDbIdentifier();
427 KeychainImpl::createCursor(SecItemClass itemClass
, const SecKeychainAttributeList
*attrList
)
429 StLock
<Mutex
>_(mMutex
);
431 StorageManager::KeychainList keychains
;
432 keychains
.push_back(Keychain(this));
433 return KCCursor(keychains
, itemClass
, attrList
);
437 KeychainImpl::createCursor(const SecKeychainAttributeList
*attrList
)
439 StLock
<Mutex
>_(mMutex
);
441 StorageManager::KeychainList keychains
;
442 keychains
.push_back(Keychain(this));
443 return KCCursor(keychains
, attrList
);
447 KeychainImpl::create(UInt32 passwordLength
, const void *inPassword
)
449 StLock
<Mutex
>_(mMutex
);
457 Allocator
&alloc
= Allocator::standard();
459 // @@@ Share this instance
461 const CssmData
password(const_cast<void *>(inPassword
), passwordLength
);
462 AclFactory::PasswordChangeCredentials
pCreds (password
, alloc
);
463 AclFactory::AnyResourceContext
rcc(pCreds
);
467 void KeychainImpl::create(ConstStringPtr inPassword
)
469 StLock
<Mutex
>_(mMutex
);
472 create(static_cast<UInt32
>(inPassword
[0]), &inPassword
[1]);
478 KeychainImpl::create()
480 StLock
<Mutex
>_(mMutex
);
482 AclFactory aclFactory
;
483 AclFactory::AnyResourceContext
rcc(aclFactory
.unlockCred());
487 void KeychainImpl::createWithBlob(CssmData
&blob
)
489 StLock
<Mutex
>_(mMutex
);
491 mDb
->dbInfo(&Schema::DBInfo
);
492 AclFactory aclFactory
;
493 AclFactory::AnyResourceContext
rcc(aclFactory
.unlockCred());
494 mDb
->resourceControlContext (&rcc
);
497 mDb
->createWithBlob(blob
);
501 mDb
->resourceControlContext(NULL
);
505 mDb
->resourceControlContext(NULL
);
506 mDb
->dbInfo(NULL
); // Clear the schema (to not break an open call later)
507 globals().storageManager
.created(Keychain(this));
509 KCEventNotifier::PostKeychainEvent (kSecKeychainListChangedEvent
, this, NULL
);
513 KeychainImpl::create(const ResourceControlContext
*rcc
)
515 StLock
<Mutex
>_(mMutex
);
517 mDb
->dbInfo(&Schema::DBInfo
); // Set the schema (to force a create)
518 mDb
->resourceControlContext(rcc
);
525 mDb
->resourceControlContext(NULL
);
526 mDb
->dbInfo(NULL
); // Clear the schema (to not break an open call later)
529 mDb
->resourceControlContext(NULL
);
530 mDb
->dbInfo(NULL
); // Clear the schema (to not break an open call later)
531 globals().storageManager
.created(Keychain(this));
537 StLock
<Mutex
>_(mMutex
);
545 StLock
<Mutex
>_(mMutex
);
551 KeychainImpl::unlock()
553 StLock
<Mutex
>_(mMutex
);
559 KeychainImpl::unlock(const CssmData
&password
)
561 StLock
<Mutex
>_(mMutex
);
563 mDb
->unlock(password
);
567 KeychainImpl::unlock(ConstStringPtr password
)
569 StLock
<Mutex
>_(mMutex
);
573 const CssmData
data(const_cast<unsigned char *>(&password
[1]), password
[0]);
581 KeychainImpl::getSettings(uint32
&outIdleTimeOut
, bool &outLockOnSleep
)
583 StLock
<Mutex
>_(mMutex
);
585 mDb
->getSettings(outIdleTimeOut
, outLockOnSleep
);
588 void KeychainImpl::markBlobForDotMacSyncUpdate(CssmData
& data
)
590 // find the plist for dot mac
591 CFArrayRef dictionaries
= (CFArrayRef
) CFPreferencesCopyValue(CFSTR("KeychainSyncList"),
592 CFSTR("com.apple.keychainsync"),
593 kCFPreferencesCurrentUser
,
594 kCFPreferencesAnyHost
);
596 if (dictionaries
== NULL
) // no such preference? The user doesn't .Mac
601 CFStringRef currentPath
= CFStringCreateWithCString(NULL
, name(), kCFStringEncodingUTF8
);
603 // in each dictionary look for the path to the current keychain
605 CFIndex count
= CFArrayGetCount(dictionaries
);
607 // make a mutable copy of the array
608 CFMutableArrayRef mutableDictionaries
= CFArrayCreateMutable(NULL
, 0, &kCFTypeArrayCallBacks
);
609 for (i
= 0; i
< count
; ++i
)
611 CFMutableDictionaryRef d
= (CFMutableDictionaryRef
) CFDictionaryCreateMutableCopy(NULL
, 0, (CFDictionaryRef
) CFArrayGetValueAtIndex(dictionaries
, i
));
612 CFArrayAppendValue(mutableDictionaries
, d
);
616 // clean up what we don't need anymore
617 CFRelease(dictionaries
);
619 bool somethingChanged
= false;
621 for (i
= 0; i
< count
; ++i
)
623 CFMutableDictionaryRef d
= (CFMutableDictionaryRef
) CFArrayGetValueAtIndex(mutableDictionaries
, i
);
625 // get the path and expand any tildes
626 CFStringRef path
= (CFStringRef
) CFDictionaryGetValue(d
, CFSTR("DbName"));
627 CFIndex length
= CFStringGetMaximumSizeForEncoding(CFStringGetLength(path
), kCFStringEncodingUTF8
);
628 char buffer
[length
+ 1]; // adjust for NULL termination
629 CFStringGetCString(path
, buffer
, sizeof(buffer
), kCFStringEncodingUTF8
);
630 string fullPath
= buffer
;
631 fullPath
= DLDbListCFPref::ExpandTildesInPath(fullPath
);
632 path
= CFStringCreateWithCString(NULL
, fullPath
.c_str(), kCFStringEncodingUTF8
);
634 if (CFStringCompare(path
, currentPath
, 0) == kCFCompareEqualTo
)
636 // if the value already exists, don't worry about it
637 CFDataRef oldBlob
= (CFDataRef
) CFDictionaryGetValue(d
, CFSTR("DbOldBlob"));
641 CFDataRef theBlob
= (CFDataRef
) CFDataCreate(NULL
, (uint8
*) data
.data(), data
.length());
642 CFDictionaryAddValue(d
, CFSTR("DbOldBlob"), theBlob
);
645 CFPreferencesSetValue(CFSTR("KeychainSyncList"),
647 CFSTR("com.apple.keychainsync"),
648 kCFPreferencesCurrentUser
,
649 kCFPreferencesAnyHost
);
650 somethingChanged
= true;
655 CFRelease(currentPath
);
657 CFRelease(mutableDictionaries
);
659 if (somethingChanged
)
661 CFPreferencesSynchronize(CFSTR("com.apple.keychainsync"), kCFPreferencesCurrentUser
, kCFPreferencesAnyHost
);
668 KeychainImpl::setSettings(uint32 inIdleTimeOut
, bool inLockOnSleep
)
670 StLock
<Mutex
>_(mMutex
);
672 // The .Mac syncing code only makes sense for the AppleFile CSP/DL,
673 // but other DLs such as the OCSP and LDAP DLs do not expose a way to
674 // change settings or the password. To make a minimal change that only affects
675 // the smartcard case, we only look for that CSP/DL
677 bool isSmartcard
= (mDb
->dl()->guid() == gGuidAppleSdCSPDL
);
679 // get the old keychain blob so that we can tell .Mac to resync it
680 CssmAutoData
oldBlob(mDb
->allocator());
682 mDb
->copyBlob(oldBlob
.get());
684 mDb
->setSettings(inIdleTimeOut
, inLockOnSleep
);
686 // if we got here, nothing threw underneath. Send the results off to .Mac heaven
688 markBlobForDotMacSyncUpdate(oldBlob
.get());
692 KeychainImpl::changePassphrase(UInt32 oldPasswordLength
, const void *oldPassword
,
693 UInt32 newPasswordLength
, const void *newPassword
)
695 StLock
<Mutex
>_(mMutex
);
697 bool isSmartcard
= (mDb
->dl()->guid() == gGuidAppleSdCSPDL
);
699 TrackingAllocator
allocator(Allocator::standard());
700 AutoCredentials cred
= AutoCredentials(allocator
);
703 const CssmData
&oldPass
= *new(allocator
) CssmData(const_cast<void *>(oldPassword
), oldPasswordLength
);
704 TypedList
&oldList
= *new(allocator
) TypedList(allocator
, CSSM_SAMPLE_TYPE_KEYCHAIN_LOCK
);
705 oldList
.append(new(allocator
) ListElement(CSSM_SAMPLE_TYPE_PASSWORD
));
706 oldList
.append(new(allocator
) ListElement(oldPass
));
712 const CssmData
&newPass
= *new(allocator
) CssmData(const_cast<void *>(newPassword
), newPasswordLength
);
713 TypedList
&newList
= *new(allocator
) TypedList(allocator
, CSSM_SAMPLE_TYPE_KEYCHAIN_CHANGE_LOCK
);
714 newList
.append(new(allocator
) ListElement(CSSM_SAMPLE_TYPE_PASSWORD
));
715 newList
.append(new(allocator
) ListElement(newPass
));
719 // get the old keychain blob so that we can tell .Mac to resync it
720 CssmAutoData
oldBlob(mDb
->allocator());
722 mDb
->copyBlob(oldBlob
.get());
724 mDb
->changePassphrase(&cred
);
726 // if we got here, nothing threw underneath. Send the results off to .Mac heaven
728 markBlobForDotMacSyncUpdate(oldBlob
.get());
732 KeychainImpl::changePassphrase(ConstStringPtr oldPassword
, ConstStringPtr newPassword
)
734 StLock
<Mutex
>_(mMutex
);
736 const void *oldPtr
, *newPtr
;
737 UInt32 oldLen
, newLen
;
740 oldLen
= oldPassword
[0];
741 oldPtr
= oldPassword
+ 1;
751 newLen
= newPassword
[0];
752 newPtr
= newPassword
+ 1;
760 changePassphrase(oldLen
, oldPtr
, newLen
, newPtr
);
764 KeychainImpl::authenticate(const CSSM_ACCESS_CREDENTIALS
*cred
)
766 StLock
<Mutex
>_(mMutex
);
769 MacOSError::throwMe(errSecNoSuchKeychain
);
771 MacOSError::throwMe(unimpErr
);
775 KeychainImpl::status() const
777 // @@@ We should figure out the read/write status though a DL passthrough
778 // or some other way. Also should locked be unlocked read only or just
780 return (mDb
->isLocked() ? 0 : kSecUnlockStateStatus
| kSecWritePermStatus
)
781 | kSecReadPermStatus
;
785 KeychainImpl::exists()
787 StLock
<Mutex
>_(mMutex
);
793 // Ok to leave the mDb open since it will get closed when it goes away.
795 catch (const CssmError
&e
)
797 if (e
.osStatus() != CSSMERR_DL_DATASTORE_DOESNOT_EXIST
)
806 KeychainImpl::isActive() const
808 return mDb
->isActive();
811 void KeychainImpl::completeAdd(Item
&inItem
, PrimaryKey
&primaryKey
)
815 // The inItem shouldn't be in the cache yet
816 assert(!inItem
->inCache());
818 // Insert inItem into mDbItemMap with key primaryKey. p.second will be
819 // true if it got inserted. If not p.second will be false and p.first
820 // will point to the current entry with key primaryKey.
821 pair
<DbItemMap::iterator
, bool> p
=
822 mDbItemMap
.insert(DbItemMap::value_type(primaryKey
, inItem
.get()));
825 // There was already an ItemImpl * in mDbItemMap with key
826 // primaryKey. Get a ref to the pointer to it so we can assign a
827 // new value to it below.
828 ItemImpl
*oldItem
= p
.first
->second
;
830 // @@@ If this happens we are breaking our API contract of
831 // uniquifying items. We really need to insert the item into the
832 // map before we start the add. And have the item be in an
833 // "is being added" state.
834 assert(oldItem
->inCache());
835 secdebug("keychain", "add of new item %p somehow replaced %p",
836 inItem
.get(), oldItem
);
837 oldItem
->inCache(false);
838 oldItem
= inItem
.get();
841 inItem
->inCache(true);
845 KeychainImpl::addCopy(Item
&inItem
)
847 Keychain
keychain(this);
848 PrimaryKey primaryKey
= inItem
->addWithCopyInfo(keychain
, true);
849 completeAdd(inItem
, primaryKey
);
850 postEvent(kSecAddEvent
, inItem
);
854 KeychainImpl::add(Item
&inItem
)
856 Keychain
keychain(this);
857 PrimaryKey primaryKey
= inItem
->add(keychain
);
858 completeAdd(inItem
, primaryKey
);
859 postEvent(kSecAddEvent
, inItem
);
863 KeychainImpl::didUpdate(const Item
&inItem
, PrimaryKey
&oldPK
,
866 // If the primary key hasn't changed we don't need to update mDbItemMap.
869 // If inItem isn't in the cache we don't need to update mDbItemMap.
870 assert(inItem
->inCache());
871 if (inItem
->inCache())
873 // First remove the entry for inItem in mDbItemMap with key oldPK.
874 DbItemMap::iterator it
= mDbItemMap
.find(oldPK
);
875 if (it
!= mDbItemMap
.end() && (ItemImpl
*) it
->second
== inItem
.get())
876 mDbItemMap
.erase(it
);
878 // Insert inItem into mDbItemMap with key newPK. p.second will be
879 // true if it got inserted. If not p.second will be false and
880 // p.first will point to the current entry with key newPK.
881 pair
<DbItemMap::iterator
, bool> p
=
882 mDbItemMap
.insert(DbItemMap::value_type(newPK
, inItem
.get()));
885 // There was already an ItemImpl * in mDbItemMap with key
886 // primaryKey. Get a ref to the pointer to it so we can assign
887 // a new value to it below.
888 ItemImpl
*oldItem
= p
.first
->second
;
890 // @@@ If this happens we are breaking our API contract of
891 // uniquifying items. We really need to insert the item into
892 // the map with the new primary key before we start the update.
893 // And have the item be in an "is being updated" state.
894 assert(oldItem
->inCache());
895 secdebug("keychain", "update of item %p somehow replaced %p",
896 inItem
.get(), oldItem
);
897 oldItem
->inCache(false);
898 oldItem
= inItem
.get();
903 postEvent(kSecUpdateEvent
, inItem
);
907 KeychainImpl::deleteItem(Item
&inoutItem
)
910 // We don't need to hold the DO mutex through event posting, and, in fact, doing so causes deadlock.
911 // Hold it only as long as needed, instead.
914 // item must be persistent
915 if (!inoutItem
->isPersistent())
916 MacOSError::throwMe(errSecInvalidItemRef
);
918 DbUniqueRecord uniqueId
= inoutItem
->dbUniqueRecord();
919 PrimaryKey primaryKey
= inoutItem
->primaryKey();
920 uniqueId
->deleteRecord();
922 // Don't remove the item from the mDbItemMap here since this would cause
923 // us to report a new item to our caller when we receive the
924 // kSecDeleteEvent notification.
925 // It will be removed before we post the notification, because
926 // CCallbackMgr will call didDeleteItem()
928 // Post the notification for the item deletion with
929 // the primaryKey obtained when the item still existed
932 postEvent(kSecDeleteEvent
, inoutItem
);
939 StLock
<Mutex
>_(mMutex
);
941 if (!mDb
->dl()->subserviceMask() & CSSM_SERVICE_CSP
)
942 MacOSError::throwMe(errSecInvalidKeychain
);
944 // Try to cast first to a CSPDL to handle case where we don't have an SSDb
947 CssmClient::CSPDL
cspdl(dynamic_cast<CssmClient::CSPDLImpl
*>(&*mDb
->dl()));
952 SSDbImpl
* impl
= dynamic_cast<SSDbImpl
*>(&(*mDb
));
955 CssmError::throwMe(CSSMERR_CSSM_INVALID_POINTER
);
964 KeychainImpl::makePrimaryKey(CSSM_DB_RECORDTYPE recordType
, DbUniqueRecord
&uniqueId
)
966 StLock
<Mutex
>_(mMutex
);
968 DbAttributes
primaryKeyAttrs(uniqueId
->database());
969 primaryKeyAttrs
.recordType(recordType
);
970 gatherPrimaryKeyAttributes(primaryKeyAttrs
);
971 uniqueId
->get(&primaryKeyAttrs
, NULL
);
972 return PrimaryKey(primaryKeyAttrs
);
975 const CssmAutoDbRecordAttributeInfo
&
976 KeychainImpl::primaryKeyInfosFor(CSSM_DB_RECORDTYPE recordType
)
978 StLock
<Mutex
>_(mMutex
);
982 return keychainSchema()->primaryKeyInfosFor(recordType
);
984 catch (const CommonError
&error
)
986 switch (error
.osStatus())
988 case errSecNoSuchClass
:
989 case CSSMERR_DL_INVALID_RECORDTYPE
:
991 return keychainSchema()->primaryKeyInfosFor(recordType
);
998 void KeychainImpl::gatherPrimaryKeyAttributes(DbAttributes
& primaryKeyAttrs
)
1000 StLock
<Mutex
>_(mMutex
);
1002 const CssmAutoDbRecordAttributeInfo
&infos
=
1003 primaryKeyInfosFor(primaryKeyAttrs
.recordType());
1005 // @@@ fix this to not copy info.
1006 for (uint32 i
= 0; i
< infos
.size(); i
++)
1007 primaryKeyAttrs
.add(infos
.at(i
));
1011 KeychainImpl::_lookupItem(const PrimaryKey
&primaryKey
)
1013 DbItemMap::iterator it
= mDbItemMap
.find(primaryKey
);
1014 if (it
!= mDbItemMap
.end())
1016 if (it
->second
== NULL
)
1018 // we've been weak released...
1019 mDbItemMap
.erase(it
);
1031 KeychainImpl::item(const PrimaryKey
&primaryKey
)
1033 StLock
<Mutex
>_(mMutex
);
1035 // Lookup the item in the map while holding the apiLock.
1036 ItemImpl
*itemImpl
= _lookupItem(primaryKey
);
1038 return Item(itemImpl
);
1042 // We didn't find it so create a new item with just a keychain and
1043 // a primary key. However since we aren't holding
1044 // globals().apiLock anymore some other thread might have beaten
1045 // us to creating this item and adding it to the cache. If that
1046 // happens we retry the lookup.
1047 return Item(this, primaryKey
);
1049 catch (const MacOSError
&e
)
1051 // If the item creation failed because some other thread already
1052 // inserted this item into the cache we retry the lookup.
1053 if (e
.osStatus() == errSecDuplicateItem
)
1055 // Lookup the item in the map while holding the apiLock.
1056 ItemImpl
*itemImpl
= _lookupItem(primaryKey
);
1058 return Item(itemImpl
);
1066 KeychainImpl::item(CSSM_DB_RECORDTYPE recordType
, DbUniqueRecord
&uniqueId
)
1068 StLock
<Mutex
>_(mMutex
);
1070 PrimaryKey primaryKey
= makePrimaryKey(recordType
, uniqueId
);
1072 // Lookup the item in the map while holding the apiLock.
1073 ItemImpl
*itemImpl
= _lookupItem(primaryKey
);
1077 return Item(itemImpl
);
1083 // We didn't find it so create a new item with a keychain, a primary key
1084 // and a DbUniqueRecord. However since we aren't holding
1085 // globals().apiLock anymore some other thread might have beaten
1086 // us to creating this item and adding it to the cache. If that
1087 // happens we retry the lookup.
1088 return Item(this, primaryKey
, uniqueId
);
1090 catch (const MacOSError
&e
)
1092 // If the item creation failed because some other thread already
1093 // inserted this item into the cache we retry the lookup.
1094 if (e
.osStatus() == errSecDuplicateItem
)
1096 // Lookup the item in the map while holding the apiLock.
1097 ItemImpl
*itemImpl
= _lookupItem(primaryKey
);
1099 return Item(itemImpl
);
1106 KeychainImpl::keychainSchema()
1108 StLock
<Mutex
>_(mMutex
);
1109 if (!mKeychainSchema
)
1110 mKeychainSchema
= KeychainSchema(mDb
);
1112 return mKeychainSchema
;
1115 void KeychainImpl::resetSchema()
1117 mKeychainSchema
= NULL
; // re-fetch it from db next time
1121 // Called from DbItemImpl's constructor (so it is only partially constructed),
1122 // add it to the map.
1124 KeychainImpl::addItem(const PrimaryKey
&primaryKey
, ItemImpl
*dbItemImpl
)
1126 StLock
<Mutex
>_(mMutex
);
1128 // The dbItemImpl shouldn't be in the cache yet
1129 assert(!dbItemImpl
->inCache());
1131 // Insert dbItemImpl into mDbItemMap with key primaryKey. p.second will
1132 // be true if it got inserted. If not p.second will be false and p.first
1133 // will point to the current entry with key primaryKey.
1134 pair
<DbItemMap::iterator
, bool> p
=
1135 mDbItemMap
.insert(DbItemMap::value_type(primaryKey
, dbItemImpl
));
1139 // There was already an ItemImpl * in mDbItemMap with key primaryKey.
1140 // There is a race condition here when being called in multiple threads
1141 // We might have added an item using add and received a notification at
1143 MacOSError::throwMe(errSecDuplicateItem
);
1146 dbItemImpl
->inCache(true);
1150 KeychainImpl::didDeleteItem(ItemImpl
*inItemImpl
)
1152 StLock
<Mutex
>_(mMutex
);
1154 // Called by CCallbackMgr
1155 secdebug("kcnotify", "%p notified that item %p was deleted", this, inItemImpl
);
1156 removeItem(inItemImpl
->primaryKey(), inItemImpl
);
1160 KeychainImpl::removeItem(const PrimaryKey
&primaryKey
, ItemImpl
*inItemImpl
)
1162 StLock
<Mutex
>_(mMutex
);
1164 // If inItemImpl isn't in the cache to begin with we are done.
1165 if (!inItemImpl
->inCache())
1168 DbItemMap::iterator it
= mDbItemMap
.find(primaryKey
);
1169 if (it
!= mDbItemMap
.end() && (ItemImpl
*) it
->second
== inItemImpl
)
1170 mDbItemMap
.erase(it
);
1172 inItemImpl
->inCache(false);
1176 KeychainImpl::getAttributeInfoForItemID(CSSM_DB_RECORDTYPE itemID
,
1177 SecKeychainAttributeInfo
**Info
)
1179 StLock
<Mutex
>_(mMutex
);
1183 keychainSchema()->getAttributeInfoForRecordType(itemID
, Info
);
1185 catch (const CommonError
&error
)
1187 switch (error
.osStatus())
1189 case errSecNoSuchClass
:
1190 case CSSMERR_DL_INVALID_RECORDTYPE
:
1192 keychainSchema()->getAttributeInfoForRecordType(itemID
, Info
);
1200 KeychainImpl::freeAttributeInfo(SecKeychainAttributeInfo
*Info
)
1208 KeychainImpl::attributeInfoFor(CSSM_DB_RECORDTYPE recordType
, UInt32 tag
)
1210 StLock
<Mutex
>_(mMutex
);
1214 return keychainSchema()->attributeInfoFor(recordType
, tag
);
1216 catch (const CommonError
&error
)
1218 switch (error
.osStatus())
1220 case errSecNoSuchClass
:
1221 case CSSMERR_DL_INVALID_RECORDTYPE
:
1223 return keychainSchema()->attributeInfoFor(recordType
, tag
);
1231 KeychainImpl::recode(const CssmData
&data
, const CssmData
&extraData
)
1233 StLock
<Mutex
>_(mMutex
);
1235 mDb
->recode(data
, extraData
);
1239 KeychainImpl::copyBlob(CssmData
&data
)
1241 StLock
<Mutex
>_(mMutex
);
1243 mDb
->copyBlob(data
);
1247 KeychainImpl::setBatchMode(Boolean mode
, Boolean rollback
)
1249 StLock
<Mutex
>_(mMutex
);
1251 mDb
->setBatchMode(mode
, rollback
);
1252 mIsInBatchMode
= mode
;
1255 if (!rollback
) // was batch mode being turned off without an abort?
1258 EventBuffer::iterator it
= mEventBuffer
->begin();
1259 while (it
!= mEventBuffer
->end())
1261 PrimaryKey primaryKey
;
1264 primaryKey
= it
->item
->primaryKey();
1267 KCEventNotifier::PostKeychainEvent(it
->kcEvent
, mDb
->dlDbIdentifier(), primaryKey
);
1274 // notify that a keychain has changed in too many ways to count
1275 KCEventNotifier::PostKeychainEvent(kSecKeychainLeftBatchModeEvent
);
1276 mEventBuffer
->clear();
1280 KCEventNotifier::PostKeychainEvent(kSecKeychainEnteredBatchModeEvent
);
1285 KeychainImpl::postEvent(SecKeychainEvent kcEvent
, ItemImpl
* item
)
1287 PrimaryKey primaryKey
;
1290 StLock
<Mutex
>_(mMutex
);
1294 primaryKey
= item
->primaryKey();
1298 if (!mIsInBatchMode
)
1300 KCEventNotifier::PostKeychainEvent(kcEvent
, mDb
->dlDbIdentifier(), primaryKey
);
1304 StLock
<Mutex
>_(mMutex
);
1307 it
.kcEvent
= kcEvent
;
1313 mEventBuffer
->push_back (it
);
1317 Keychain::Keychain()
1319 dispatch_once(&SecKeychainSystemKeychainChecked
, ^{
1320 check_system_keychain();
1325 Keychain::optional(SecKeychainRef handle
)
1328 return KeychainImpl::required(handle
);
1330 return globals().storageManager
.defaultKeychain();
1334 CFIndex
GetKeychainRetainCount(Keychain
& kc
)
1336 CFTypeRef ref
= kc
->handle(false);
1337 return CFGetRetainCount(ref
);
1342 // Create default credentials for this keychain.
1343 // This is triggered upon default open (i.e. a Db::activate() with no set credentials).
1345 // This function embodies the "default credentials" logic for Keychain-layer databases.
1347 const AccessCredentials
*
1348 KeychainImpl::makeCredentials()
1350 return defaultCredentials();
1354 const AccessCredentials
*
1355 KeychainImpl::defaultCredentials()
1357 StLock
<Mutex
>_(mMutex
);
1359 // Use custom unlock credentials for file keychains which have a referral
1360 // record and the standard credentials for all others.
1362 if (mDb
->dl()->guid() == gGuidAppleCSPDL
&& mCustomUnlockCreds(mDb
))
1363 return &mCustomUnlockCreds
;
1365 if (mDb
->dl()->guid() == gGuidAppleSdCSPDL
)
1366 return globals().smartcardCredentials();
1368 return globals().keychainCredentials();