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_utilities/FileLockTransaction.h> 
  41 #include <security_keychain/SecCFTypes.h> 
  42 #include <securityd_client/ssblob.h> 
  43 #include <Security/TrustSettingsSchema.h> 
  45 #include "SecKeychainPriv.h" 
  47 #include <Security/SecKeychainItemPriv.h> 
  48 #include <CoreFoundation/CoreFoundation.h> 
  49 #include "DLDBListCFPref.h" 
  52 #include <sys/param.h> 
  55 #include <sys/socket.h> 
  57 #include <sys/types.h> 
  60 static dispatch_once_t SecKeychainSystemKeychainChecked
; 
  62 OSStatus 
SecKeychainSystemKeychainCheckWouldDeadlock() 
  64     dispatch_once(&SecKeychainSystemKeychainChecked
, ^{}); 
  68 using namespace KeychainCore
; 
  69 using namespace CssmClient
; 
  72 typedef struct EventItem
 
  74         SecKeychainEvent kcEvent
; 
  78 typedef std::list
<EventItem
> EventBufferSuper
; 
  79 class EventBuffer 
: public EventBufferSuper
 
  83         virtual ~EventBuffer (); 
  87 EventBuffer::~EventBuffer () 
  96 KeychainSchemaImpl::KeychainSchemaImpl(const Db 
&db
) : mMutex(Mutex::recursive
) 
  98         DbCursor 
relations(db
); 
  99         relations
->recordType(CSSM_DL_DB_SCHEMA_INFO
); 
 100         DbAttributes 
relationRecord(db
, 1); 
 101         relationRecord
.add(Schema::RelationID
); 
 102         DbUniqueRecord 
outerUniqueId(db
); 
 104         while (relations
->next(&relationRecord
, NULL
, outerUniqueId
)) 
 106                 DbUniqueRecord 
uniqueId(db
); 
 108                 uint32 relationID 
= relationRecord
.at(0); 
 109                 if (CSSM_DB_RECORDTYPE_SCHEMA_START 
<= relationID
 
 110                         && relationID 
< CSSM_DB_RECORDTYPE_SCHEMA_END
) 
 113                 // Create a cursor on the SCHEMA_ATTRIBUTES table for records with 
 114                 // RelationID == relationID 
 115                 DbCursor 
attributes(db
); 
 116                 attributes
->recordType(CSSM_DL_DB_SCHEMA_ATTRIBUTES
); 
 117                 attributes
->add(CSSM_DB_EQUAL
, Schema::RelationID
, relationID
); 
 119                 // Set up a record for retriving the SCHEMA_ATTRIBUTES 
 120                 DbAttributes 
attributeRecord(db
, 2); 
 121                 attributeRecord
.add(Schema::AttributeFormat
); 
 122                 attributeRecord
.add(Schema::AttributeID
);        
 124                 RelationInfoMap 
&rim 
= mDatabaseInfoMap
[relationID
]; 
 125                 while (attributes
->next(&attributeRecord
, NULL
, uniqueId
)) 
 126                         rim
[attributeRecord
.at(1)] = attributeRecord
.at(0); 
 128                 // Create a cursor on the CSSM_DL_DB_SCHEMA_INDEXES table for records 
 129                 // with RelationID == relationID 
 130                 DbCursor 
indexes(db
); 
 131                 indexes
->recordType(CSSM_DL_DB_SCHEMA_INDEXES
); 
 132                 indexes
->conjunctive(CSSM_DB_AND
); 
 133                 indexes
->add(CSSM_DB_EQUAL
, Schema::RelationID
, relationID
); 
 134                 indexes
->add(CSSM_DB_EQUAL
, Schema::IndexType
, 
 135                         uint32(CSSM_DB_INDEX_UNIQUE
)); 
 137                 // Set up a record for retriving the SCHEMA_INDEXES 
 138                 DbAttributes 
indexRecord(db
, 1); 
 139                 indexRecord
.add(Schema::AttributeID
); 
 141                 CssmAutoDbRecordAttributeInfo 
&infos 
= 
 142                         *new CssmAutoDbRecordAttributeInfo(); 
 144                         insert(PrimaryKeyInfoMap::value_type(relationID
, &infos
)); 
 145                 infos
.DataRecordType 
= relationID
; 
 146                 while (indexes
->next(&indexRecord
, NULL
, uniqueId
)) 
 148                         CssmDbAttributeInfo 
&info 
= infos
.add(); 
 149                         info
.AttributeNameFormat 
= CSSM_DB_ATTRIBUTE_NAME_AS_INTEGER
; 
 150                         info
.Label
.AttributeID 
= indexRecord
.at(0); 
 151                         // @@@ Might insert bogus value if DB is corrupt 
 152                         info
.AttributeFormat 
= rim
[info
.Label
.AttributeID
]; 
 157 KeychainSchemaImpl::~KeychainSchemaImpl() 
 161         map
<CSSM_DB_RECORDTYPE
, CssmAutoDbRecordAttributeInfo 
*>::iterator it 
= mPrimaryKeyInfoMap
.begin(); 
 162         while (it 
!= mPrimaryKeyInfoMap
.end()) 
 167                 // for_each_map_delete(mPrimaryKeyInfoMap.begin(), mPrimaryKeyInfoMap.end()); 
 174 const KeychainSchemaImpl::RelationInfoMap 
& 
 175 KeychainSchemaImpl::relationInfoMapFor(CSSM_DB_RECORDTYPE recordType
) const 
 177         DatabaseInfoMap::const_iterator dit 
= mDatabaseInfoMap
.find(recordType
); 
 178         if (dit 
== mDatabaseInfoMap
.end()) 
 179                 MacOSError::throwMe(errSecNoSuchClass
); 
 183 bool KeychainSchemaImpl::hasRecordType (CSSM_DB_RECORDTYPE recordType
) const 
 185         DatabaseInfoMap::const_iterator it 
= mDatabaseInfoMap
.find(recordType
); 
 186         return it 
!= mDatabaseInfoMap
.end(); 
 190 KeychainSchemaImpl::hasAttribute(CSSM_DB_RECORDTYPE recordType
, uint32 attributeId
) const 
 194                 const RelationInfoMap 
&rmap 
= relationInfoMapFor(recordType
); 
 195                 RelationInfoMap::const_iterator rit 
= rmap
.find(attributeId
); 
 196                 return rit 
!= rmap
.end(); 
 198         catch (MacOSError result
) 
 200                 if (result
.osStatus () == errSecNoSuchClass
) 
 211 CSSM_DB_ATTRIBUTE_FORMAT 
 
 212 KeychainSchemaImpl::attributeFormatFor(CSSM_DB_RECORDTYPE recordType
, uint32 attributeId
) const 
 214         const RelationInfoMap 
&rmap 
= relationInfoMapFor(recordType
); 
 215         RelationInfoMap::const_iterator rit 
= rmap
.find(attributeId
); 
 216         if (rit 
== rmap
.end()) 
 217                 MacOSError::throwMe(errSecNoSuchAttr
); 
 223 KeychainSchemaImpl::attributeInfoFor(CSSM_DB_RECORDTYPE recordType
, uint32 attributeId
) const 
 225         CSSM_DB_ATTRIBUTE_INFO info
; 
 226         info
.AttributeFormat 
= attributeFormatFor(recordType
, attributeId
); 
 227         info
.AttributeNameFormat 
= CSSM_DB_ATTRIBUTE_NAME_AS_INTEGER
; 
 228         info
.Label
.AttributeID 
= attributeId
; 
 234 KeychainSchemaImpl::getAttributeInfoForRecordType(CSSM_DB_RECORDTYPE recordType
, SecKeychainAttributeInfo 
**Info
) const 
 236         const RelationInfoMap 
&rmap 
= relationInfoMapFor(recordType
); 
 238         SecKeychainAttributeInfo 
*theList
=reinterpret_cast<SecKeychainAttributeInfo 
*>(malloc(sizeof(SecKeychainAttributeInfo
))); 
 240         size_t capacity
=rmap
.size(); 
 241         UInt32 
*tagBuf
=reinterpret_cast<UInt32 
*>(malloc(capacity
*sizeof(UInt32
))); 
 242         UInt32 
*formatBuf
=reinterpret_cast<UInt32 
*>(malloc(capacity
*sizeof(UInt32
))); 
 246         for (RelationInfoMap::const_iterator rit 
= rmap
.begin(); rit 
!= rmap
.end(); ++rit
) 
 251                         if (capacity 
<= i
) capacity 
= i 
+ 1; 
 252                         tagBuf
=reinterpret_cast<UInt32 
*>(realloc(tagBuf
, (capacity
*sizeof(UInt32
)))); 
 253                         formatBuf
=reinterpret_cast<UInt32 
*>(realloc(formatBuf
, (capacity
*sizeof(UInt32
)))); 
 255                 tagBuf
[i
]=rit
->first
; 
 256                 formatBuf
[i
++]=rit
->second
; 
 261         theList
->format
=formatBuf
; 
 266 const CssmAutoDbRecordAttributeInfo 
& 
 267 KeychainSchemaImpl::primaryKeyInfosFor(CSSM_DB_RECORDTYPE recordType
) const 
 269         PrimaryKeyInfoMap::const_iterator it
; 
 270         it 
= mPrimaryKeyInfoMap
.find(recordType
); 
 272         if (it 
== mPrimaryKeyInfoMap
.end()) 
 273                 MacOSError::throwMe(errSecNoSuchClass
); // @@@ Not really but whatever. 
 279 KeychainSchemaImpl::operator <(const KeychainSchemaImpl 
&other
) const 
 281         return mDatabaseInfoMap 
< other
.mDatabaseInfoMap
; 
 285 KeychainSchemaImpl::operator ==(const KeychainSchemaImpl 
&other
) const 
 287         return mDatabaseInfoMap 
== other
.mDatabaseInfoMap
; 
 291 KeychainSchemaImpl::didCreateRelation(CSSM_DB_RECORDTYPE relationID
, 
 292         const char *inRelationName
, 
 293         uint32 inNumberOfAttributes
, 
 294         const CSSM_DB_SCHEMA_ATTRIBUTE_INFO 
*pAttributeInfo
, 
 295         uint32 inNumberOfIndexes
, 
 296         const CSSM_DB_SCHEMA_INDEX_INFO 
*pIndexInfo
) 
 298         StLock
<Mutex
>_(mMutex
); 
 300         if (CSSM_DB_RECORDTYPE_SCHEMA_START 
<= relationID
 
 301                 && relationID 
< CSSM_DB_RECORDTYPE_SCHEMA_END
) 
 304     // if our schema is already in the map, return 
 305     if (mPrimaryKeyInfoMap
.find(relationID
) != mPrimaryKeyInfoMap
.end()) 
 310         RelationInfoMap 
&rim 
= mDatabaseInfoMap
[relationID
]; 
 311         for (uint32 ix 
= 0; ix 
< inNumberOfAttributes
; ++ix
) 
 312                 rim
[pAttributeInfo
[ix
].AttributeId
] = pAttributeInfo
[ix
].DataType
; 
 314         CssmAutoDbRecordAttributeInfo 
*infos 
= new CssmAutoDbRecordAttributeInfo(); 
 317                 insert(PrimaryKeyInfoMap::value_type(relationID
, infos
)); 
 318         infos
->DataRecordType 
= relationID
; 
 319         for (uint32 ix 
= 0; ix 
< inNumberOfIndexes
; ++ix
) 
 320                 if (pIndexInfo
[ix
].IndexType 
== CSSM_DB_INDEX_UNIQUE
) 
 322                         CssmDbAttributeInfo 
&info 
= infos
->add(); 
 323                         info
.AttributeNameFormat 
= CSSM_DB_ATTRIBUTE_NAME_AS_INTEGER
; 
 324                         info
.Label
.AttributeID 
= pIndexInfo
[ix
].AttributeId
; 
 325                         info
.AttributeFormat 
= rim
[info
.Label
.AttributeID
]; 
 331 KeychainSchema::~KeychainSchema() 
 340         SecKeychainEvent eventCode
; 
 341         PrimaryKey primaryKey
; 
 343 typedef std::list
<Event
> EventList
; 
 345 #define SYSTEM_KEYCHAIN_CHECK_UNIX_BASE_NAME "/var/run/systemkeychaincheck" 
 346 #define SYSTEM_KEYCHAIN_CHECK_UNIX_DOMAIN_SOCKET_NAME (SYSTEM_KEYCHAIN_CHECK_UNIX_BASE_NAME ".socket") 
 347 #define SYSTEM_KEYCHAIN_CHECK_COMPLETE_FILE_NAME (SYSTEM_KEYCHAIN_CHECK_UNIX_BASE_NAME ".done") 
 349 static void check_system_keychain() 
 351         // sadly we can't use XPC here, XPC_DOMAIN_TYPE_SYSTEM doesn't exist yet.  Also xpc-helper uses the 
 352         // keychain API (I assume for checking codesign things).   So we use Unix Domain Sockets. 
 354         // NOTE: if we hit a system error we attempt to log it, and then just don't check the system keychain. 
 355         // In theory a system might be able to recover from this state if we let it try to muddle along, and 
 356         // past behaviour didn't even try this hard to do the keychain check.  In particular we could be in a 
 357         // sandbox'ed process.   So we just do our best and let another process try again. 
 359         struct stat keycheck_file_info
; 
 360         if (stat(SYSTEM_KEYCHAIN_CHECK_COMPLETE_FILE_NAME
, &keycheck_file_info
) < 0) { 
 361                 int server_fd 
= socket(PF_UNIX
, SOCK_STREAM
, 0); 
 363                         syslog(LOG_ERR
, "Can't get socket (%m) system keychain may be unchecked"); 
 367                 struct sockaddr_un keychain_check_server_address
; 
 368                 keychain_check_server_address
.sun_family 
= AF_UNIX
; 
 369                 if (strlcpy(keychain_check_server_address
.sun_path
, SYSTEM_KEYCHAIN_CHECK_UNIX_DOMAIN_SOCKET_NAME
, sizeof(keychain_check_server_address
.sun_path
)) > sizeof(keychain_check_server_address
.sun_path
)) { 
 370                         // It would be nice if we could compile time assert this 
 371                         syslog(LOG_ERR
, "Socket path too long, max length %lu, your length %lu", (unsigned long)sizeof(keychain_check_server_address
.sun_path
), (unsigned long)strlen(SYSTEM_KEYCHAIN_CHECK_UNIX_DOMAIN_SOCKET_NAME
)); 
 375                 keychain_check_server_address
.sun_len 
= SUN_LEN(&keychain_check_server_address
); 
 377                 int rc 
= connect(server_fd
, (struct sockaddr 
*)&keychain_check_server_address
, keychain_check_server_address
.sun_len
); 
 379                         syslog(LOG_ERR
, "Can not connect to %s: %m", SYSTEM_KEYCHAIN_CHECK_UNIX_DOMAIN_SOCKET_NAME
); 
 384                 // this read lets us block until the EOF comes, we don't ever get a byte (and if we do, we don't care about it) 
 386                 ssize_t read_size 
= read(server_fd
, &byte
, 1); 
 388                         syslog(LOG_ERR
, "Error reading from system keychain checker: %m"); 
 399 KeychainImpl::KeychainImpl(const Db 
&db
) 
 400 :  mCacheTimer(NULL
), mSuppressTickle(false), mAttemptedUpgrade(false), mDbItemMapMutex(Mutex::recursive
), mDbDeletedItemMapMutex(Mutex::recursive
), 
 401       mInCache(false), mDb(db
), mCustomUnlockCreds (this), mIsInBatchMode (false), mMutex(Mutex::recursive
) 
 403         dispatch_once(&SecKeychainSystemKeychainChecked
, ^{ 
 404                 check_system_keychain(); 
 406         mDb
->defaultCredentials(this);  // install activation hook 
 407         mEventBuffer 
= new EventBuffer
; 
 410 KeychainImpl::~KeychainImpl()  
 414                 // Remove ourselves from the cache if we are in it. 
 415         // fprintf(stderr, "Removing %p from storage manager cache.\n", handle(false)); 
 416                 globals().storageManager
.removeKeychain(dlDbIdentifier(), this); 
 425 KeychainImpl::getMutexForObject() const 
 427         return globals().storageManager
.getStorageManagerMutex(); 
 431 KeychainImpl::getKeychainMutex() 
 436 void KeychainImpl::aboutToDestruct() 
 438     // remove me from the global cache, we are done 
 439     // fprintf(stderr, "Destructing keychain object\n"); 
 440     DLDbIdentifier identifier 
= dlDbIdentifier(); 
 441     globals().storageManager
.removeKeychain(identifier
, this); 
 445 KeychainImpl::operator ==(const KeychainImpl 
&keychain
) const 
 447         return dlDbIdentifier() == keychain
.dlDbIdentifier(); 
 451 KeychainImpl::createCursor(SecItemClass itemClass
, const SecKeychainAttributeList 
*attrList
) 
 453         StLock
<Mutex
>_(mMutex
); 
 455         StorageManager::KeychainList keychains
; 
 456         keychains
.push_back(Keychain(this)); 
 457         return KCCursor(keychains
, itemClass
, attrList
); 
 461 KeychainImpl::createCursor(const SecKeychainAttributeList 
*attrList
) 
 463         StLock
<Mutex
>_(mMutex
); 
 465         StorageManager::KeychainList keychains
; 
 466         keychains
.push_back(Keychain(this)); 
 467         return KCCursor(keychains
, attrList
); 
 471 KeychainImpl::create(UInt32 passwordLength
, const void *inPassword
) 
 473         StLock
<Mutex
>_(mMutex
); 
 481         Allocator 
&alloc 
= Allocator::standard(); 
 483         // @@@ Share this instance 
 485         const CssmData 
password(const_cast<void *>(inPassword
), passwordLength
); 
 486         AclFactory::PasswordChangeCredentials 
pCreds (password
, alloc
); 
 487         AclFactory::AnyResourceContext 
rcc(pCreds
); 
 490     // Now that we've created, trigger setting the defaultCredentials 
 494 void KeychainImpl::create(ConstStringPtr inPassword
) 
 496         StLock
<Mutex
>_(mMutex
); 
 499         create(static_cast<UInt32
>(inPassword
[0]), &inPassword
[1]); 
 505 KeychainImpl::create() 
 507         StLock
<Mutex
>_(mMutex
); 
 509         AclFactory aclFactory
; 
 510         AclFactory::AnyResourceContext 
rcc(aclFactory
.unlockCred()); 
 513     // Now that we've created, trigger setting the defaultCredentials 
 517 void KeychainImpl::createWithBlob(CssmData 
&blob
) 
 519         StLock
<Mutex
>_(mMutex
); 
 521         mDb
->dbInfo(&Schema::DBInfo
); 
 522         AclFactory aclFactory
; 
 523         AclFactory::AnyResourceContext 
rcc(aclFactory
.unlockCred()); 
 524         mDb
->resourceControlContext (&rcc
); 
 527                 mDb
->createWithBlob(blob
); 
 531                 mDb
->resourceControlContext(NULL
); 
 535         mDb
->resourceControlContext(NULL
); 
 536         mDb
->dbInfo(NULL
); // Clear the schema (to not break an open call later) 
 537         globals().storageManager
.created(Keychain(this)); 
 539     KCEventNotifier::PostKeychainEvent (kSecKeychainListChangedEvent
, this, NULL
); 
 543 KeychainImpl::create(const ResourceControlContext 
*rcc
) 
 545         StLock
<Mutex
>_(mMutex
); 
 547         mDb
->dbInfo(&Schema::DBInfo
); // Set the schema (to force a create) 
 548         mDb
->resourceControlContext(rcc
); 
 555                 mDb
->resourceControlContext(NULL
); 
 556         mDb
->dbInfo(NULL
); // Clear the schema (to not break an open call later) 
 559         mDb
->resourceControlContext(NULL
); 
 560         mDb
->dbInfo(NULL
); // Clear the schema (to not break an open call later) 
 561         globals().storageManager
.created(Keychain(this)); 
 567         StLock
<Mutex
>_(mMutex
); 
 575         StLock
<Mutex
>_(mMutex
); 
 581 KeychainImpl::unlock() 
 583         StLock
<Mutex
>_(mMutex
); 
 589 KeychainImpl::unlock(const CssmData 
&password
) 
 591         StLock
<Mutex
>_(mMutex
); 
 593         mDb
->unlock(password
); 
 597 KeychainImpl::unlock(ConstStringPtr password
) 
 599         StLock
<Mutex
>_(mMutex
); 
 603                 const CssmData 
data(const_cast<unsigned char *>(&password
[1]), password
[0]); 
 611 KeychainImpl::stash() 
 613         StLock
<Mutex
>_(mMutex
); 
 619 KeychainImpl::stashCheck() 
 621         StLock
<Mutex
>_(mMutex
); 
 627 KeychainImpl::getSettings(uint32 
&outIdleTimeOut
, bool &outLockOnSleep
) 
 629         StLock
<Mutex
>_(mMutex
); 
 631         mDb
->getSettings(outIdleTimeOut
, outLockOnSleep
); 
 635 KeychainImpl::setSettings(uint32 inIdleTimeOut
, bool inLockOnSleep
) 
 637         StLock
<Mutex
>_(mMutex
); 
 639         // The .Mac syncing code only makes sense for the AppleFile CSP/DL, 
 640         // but other DLs such as the OCSP and LDAP DLs do not expose a way to 
 641         // change settings or the password. To make a minimal change that only affects 
 642         // the smartcard case, we only look for that CSP/DL 
 644         bool isSmartcard 
=      (mDb
->dl()->guid() == gGuidAppleSdCSPDL
); 
 646         // get the old keychain blob so that we can tell .Mac to resync it 
 647         CssmAutoData 
oldBlob(mDb 
->allocator()); 
 649                 mDb
->copyBlob(oldBlob
.get()); 
 651         mDb
->setSettings(inIdleTimeOut
, inLockOnSleep
); 
 655 KeychainImpl::changePassphrase(UInt32 oldPasswordLength
, const void *oldPassword
, 
 656         UInt32 newPasswordLength
, const void *newPassword
) 
 658         StLock
<Mutex
>_(mMutex
); 
 660         bool isSmartcard 
=      (mDb
->dl()->guid() == gGuidAppleSdCSPDL
); 
 662         TrackingAllocator 
allocator(Allocator::standard()); 
 663         AutoCredentials cred 
= AutoCredentials(allocator
); 
 666                 const CssmData 
&oldPass 
= *new(allocator
) CssmData(const_cast<void *>(oldPassword
), oldPasswordLength
); 
 667                 TypedList 
&oldList 
= *new(allocator
) TypedList(allocator
, CSSM_SAMPLE_TYPE_KEYCHAIN_LOCK
); 
 668                 oldList
.append(new(allocator
) ListElement(CSSM_SAMPLE_TYPE_PASSWORD
)); 
 669                 oldList
.append(new(allocator
) ListElement(oldPass
)); 
 675                 const CssmData 
&newPass 
= *new(allocator
) CssmData(const_cast<void *>(newPassword
), newPasswordLength
); 
 676                 TypedList 
&newList 
= *new(allocator
) TypedList(allocator
, CSSM_SAMPLE_TYPE_KEYCHAIN_CHANGE_LOCK
); 
 677                 newList
.append(new(allocator
) ListElement(CSSM_SAMPLE_TYPE_PASSWORD
)); 
 678                 newList
.append(new(allocator
) ListElement(newPass
)); 
 682         // get the old keychain blob so that we can tell .Mac to resync it 
 683         CssmAutoData 
oldBlob(mDb
->allocator()); 
 685                 mDb
->copyBlob(oldBlob
.get()); 
 687         mDb
->changePassphrase(&cred
); 
 691 KeychainImpl::changePassphrase(ConstStringPtr oldPassword
, ConstStringPtr newPassword
) 
 693         StLock
<Mutex
>_(mMutex
); 
 695         const void *oldPtr
, *newPtr
; 
 696         UInt32 oldLen
, newLen
; 
 699                 oldLen 
= oldPassword
[0]; 
 700                 oldPtr 
= oldPassword 
+ 1; 
 710                 newLen 
= newPassword
[0]; 
 711                 newPtr 
= newPassword 
+ 1; 
 719         changePassphrase(oldLen
, oldPtr
, newLen
, newPtr
); 
 723 KeychainImpl::authenticate(const CSSM_ACCESS_CREDENTIALS 
*cred
) 
 725         StLock
<Mutex
>_(mMutex
); 
 728                 MacOSError::throwMe(errSecNoSuchKeychain
); 
 730         MacOSError::throwMe(errSecUnimplemented
); 
 734 KeychainImpl::status() const 
 736     StLock
<Mutex
>_(mMutex
); 
 738         // @@@ We should figure out the read/write status though a DL passthrough 
 739         // or some other way. Also should locked be unlocked read only or just 
 741         return (mDb
->isLocked() ? 0 : kSecUnlockStateStatus 
| kSecWritePermStatus
) 
 742                 | kSecReadPermStatus
; 
 746 KeychainImpl::exists() 
 748         StLock
<Mutex
>_(mMutex
); 
 754                 // Ok to leave the mDb open since it will get closed when it goes away. 
 756         catch (const CssmError 
&e
) 
 758                 if (e
.osStatus() != CSSMERR_DL_DATASTORE_DOESNOT_EXIST
) 
 767 KeychainImpl::isActive() const 
 769         return mDb
->isActive(); 
 772 void KeychainImpl::completeAdd(Item 
&inItem
, PrimaryKey 
&primaryKey
) 
 774         // The inItem shouldn't be in the cache yet 
 775         assert(!inItem
->inCache()); 
 777         // Insert inItem into mDbItemMap with key primaryKey.  p.second will be 
 778         // true if it got inserted. If not p.second will be false and p.first 
 779         // will point to the current entry with key primaryKey. 
 780     StLock
<Mutex
> _(mDbItemMapMutex
); 
 781         pair
<DbItemMap::iterator
, bool> p 
= 
 782                 mDbItemMap
.insert(DbItemMap::value_type(primaryKey
, inItem
.get())); 
 785                 // There was already an ItemImpl * in mDbItemMap with key 
 786                 // primaryKey. Remove it, and try the add again. 
 787                 ItemImpl 
*oldItem 
= p
.first
->second
; 
 789                 // @@@ If this happens we are breaking our API contract of 
 790                 // uniquifying items.  We really need to insert the item into the 
 791                 // map before we start the add.  And have the item be in an 
 792                 // "is being added" state. 
 793                 secnotice("keychain", "add of new item %p somehow replaced %p", 
 794                         inItem
.get(), oldItem
); 
 796         mDbItemMap
.erase(p
.first
); 
 797         oldItem
->inCache(false); 
 798         forceRemoveFromCache(oldItem
); 
 799         mDbItemMap
.insert(DbItemMap::value_type(primaryKey
, inItem
.get())); 
 802         inItem
->inCache(true); 
 806 KeychainImpl::addCopy(Item 
&inItem
) 
 808     StLock
<Mutex
>_(mMutex
); 
 810         Keychain 
keychain(this); 
 811         PrimaryKey primaryKey 
= inItem
->addWithCopyInfo(keychain
, true); 
 812         completeAdd(inItem
, primaryKey
); 
 813         postEvent(kSecAddEvent
, inItem
); 
 817 KeychainImpl::add(Item 
&inItem
) 
 819     StLock
<Mutex
>_(mMutex
); 
 821         Keychain 
keychain(this); 
 822         PrimaryKey primaryKey 
= inItem
->add(keychain
); 
 823         completeAdd(inItem
, primaryKey
); 
 824         postEvent(kSecAddEvent
, inItem
); 
 828 KeychainImpl::didUpdate(const Item 
&inItem
, PrimaryKey 
&oldPK
, 
 831         // If the primary key hasn't changed we don't need to update mDbItemMap. 
 834                 // If inItem isn't in the cache we don't need to update mDbItemMap. 
 835                 assert(inItem
->inCache()); 
 836                 if (inItem
->inCache()) 
 838             StLock
<Mutex
> _(mDbItemMapMutex
); 
 839                         // First remove the entry for inItem in mDbItemMap with key oldPK. 
 840                         DbItemMap::iterator it 
= mDbItemMap
.find(oldPK
); 
 841                         if (it 
!= mDbItemMap
.end() && (ItemImpl
*) it
->second 
== inItem
.get()) 
 842                                 mDbItemMap
.erase(it
); 
 844                         // Insert inItem into mDbItemMap with key newPK.  p.second will be 
 845                         // true if it got inserted. If not p.second will be false and 
 846                         // p.first will point to the current entry with key newPK. 
 847                         pair
<DbItemMap::iterator
, bool> p 
= 
 848                                 mDbItemMap
.insert(DbItemMap::value_type(newPK
, inItem
.get())); 
 851                                 // There was already an ItemImpl * in mDbItemMap with key 
 852                                 // primaryKey. Remove it, and try the add again. 
 853                                 ItemImpl 
*oldItem 
= p
.first
->second
; 
 855                                 // @@@ If this happens we are breaking our API contract of 
 856                                 // uniquifying items.  We really need to insert the item into 
 857                                 // the map with the new primary key before we start the update. 
 858                                 // And have the item be in an "is being updated" state. 
 859                                 secnotice("keychain", "update of item %p somehow replaced %p", 
 860                                         inItem
.get(), oldItem
); 
 862                 mDbItemMap
.erase(p
.first
); 
 863                 oldItem
->inCache(false); 
 864                 forceRemoveFromCache(oldItem
); 
 865                 mDbItemMap
.insert(DbItemMap::value_type(newPK
, inItem
.get())); 
 870     // Item updates now are technically a delete and re-add, so post these events instead of kSecUpdateEvent 
 871     postEvent(kSecDeleteEvent
, inItem
, oldPK
); 
 872     postEvent(kSecAddEvent
, inItem
); 
 876 KeychainImpl::deleteItem(Item 
&inoutItem
) 
 878     StLock
<Mutex
>_(mMutex
); 
 881                 // item must be persistent 
 882                 if (!inoutItem
->isPersistent()) 
 883                         MacOSError::throwMe(errSecInvalidItemRef
); 
 885         secinfo("kcnotify", "starting deletion of item %p", inoutItem
.get()); 
 887                 DbUniqueRecord uniqueId 
= inoutItem
->dbUniqueRecord(); 
 888                 PrimaryKey primaryKey 
= inoutItem
->primaryKey(); 
 889                 uniqueId
->deleteRecord(); 
 891         // Move the item from mDbItemMap to mDbDeletedItemMap. We need the item 
 892         // to give to the client process when we receive the kSecDeleteEvent 
 893         // notification, but if that notification never arrives, we don't want 
 894         // the item hanging around. When didDeleteItem is called by CCallbackMgr, 
 895         // we'll remove all traces of the item. 
 897         if (inoutItem
->inCache()) { 
 898             StLock
<Mutex
> _(mDbItemMapMutex
); 
 899             StLock
<Mutex
> __(mDbDeletedItemMapMutex
); 
 900             // Only look for it if it's in the cache 
 901             DbItemMap::iterator it 
= mDbItemMap
.find(primaryKey
); 
 903             if (it 
!= mDbItemMap
.end() && (ItemImpl
*) it
->second 
== inoutItem
.get()) { 
 904                 mDbDeletedItemMap
.insert(DbItemMap::value_type(primaryKey
, it
->second
)); 
 905                 mDbItemMap
.erase(it
); 
 909                 // Post the notification for the item deletion with 
 910                 // the primaryKey obtained when the item still existed 
 913         postEvent(kSecDeleteEvent
, inoutItem
); 
 916 void KeychainImpl::changeDatabase(CssmClient::Db db
) 
 918     StLock
<Mutex
>_(mDbMutex
); 
 920     mDb
->defaultCredentials(this); 
 927         StLock
<Mutex
>_(mMutex
); 
 929         if (!(mDb
->dl()->subserviceMask() & CSSM_SERVICE_CSP
)) 
 930                 MacOSError::throwMe(errSecInvalidKeychain
); 
 932         // Try to cast first to a CSPDL to handle case where we don't have an SSDb 
 935                 CssmClient::CSPDL 
cspdl(dynamic_cast<CssmClient::CSPDLImpl 
*>(&*mDb
->dl())); 
 940                 SSDbImpl
* impl 
= dynamic_cast<SSDbImpl 
*>(&(*mDb
)); 
 943                         CssmError::throwMe(CSSMERR_CSSM_INVALID_POINTER
); 
 952 KeychainImpl::makePrimaryKey(CSSM_DB_RECORDTYPE recordType
, DbUniqueRecord 
&uniqueId
) 
 954         StLock
<Mutex
>_(mMutex
); 
 956         DbAttributes 
primaryKeyAttrs(uniqueId
->database()); 
 957         primaryKeyAttrs
.recordType(recordType
); 
 958         gatherPrimaryKeyAttributes(primaryKeyAttrs
); 
 959         uniqueId
->get(&primaryKeyAttrs
, NULL
); 
 960         return PrimaryKey(primaryKeyAttrs
); 
 964 KeychainImpl::makePrimaryKey(CSSM_DB_RECORDTYPE recordType
, DbAttributes
* currentAttributes
) 
 966     StLock
<Mutex
>_(mMutex
); 
 968     DbAttributes primaryKeyAttrs
; 
 969     primaryKeyAttrs
.recordType(recordType
); 
 970     gatherPrimaryKeyAttributes(primaryKeyAttrs
); 
 972     for(int i 
= 0; i 
< primaryKeyAttrs
.size(); i
++) { 
 973         CssmDbAttributeData
& attr 
= primaryKeyAttrs
[i
]; 
 975         CssmDbAttributeData 
* actual 
= currentAttributes
->find(attr
.info()); 
 977             attr
.set(*actual
, Allocator::standard()); 
 980     return PrimaryKey(primaryKeyAttrs
); 
 983 const CssmAutoDbRecordAttributeInfo 
& 
 984 KeychainImpl::primaryKeyInfosFor(CSSM_DB_RECORDTYPE recordType
) 
 986         StLock
<Mutex
>_(mMutex
); 
 990                 return keychainSchema()->primaryKeyInfosFor(recordType
); 
 992         catch (const CommonError 
&error
) 
 994                 switch (error
.osStatus()) 
 996                 case errSecNoSuchClass
: 
 997                 case CSSMERR_DL_INVALID_RECORDTYPE
: 
 999                         return keychainSchema()->primaryKeyInfosFor(recordType
); 
1006 void KeychainImpl::gatherPrimaryKeyAttributes(DbAttributes
& primaryKeyAttrs
) 
1008         StLock
<Mutex
> _(mMutex
); 
1010         const CssmAutoDbRecordAttributeInfo 
&infos 
= 
1011                 primaryKeyInfosFor(primaryKeyAttrs
.recordType()); 
1013         // @@@ fix this to not copy info.                
1014         for (uint32 i 
= 0; i 
< infos
.size(); i
++) 
1015                 primaryKeyAttrs
.add(infos
.at(i
)); 
1019 KeychainImpl::_lookupItem(const PrimaryKey 
&primaryKey
) 
1021     StLock
<Mutex
> _(mDbItemMapMutex
); 
1022         DbItemMap::iterator it 
= mDbItemMap
.find(primaryKey
); 
1023         if (it 
!= mDbItemMap
.end()) 
1032 KeychainImpl::_lookupDeletedItemOnly(const PrimaryKey 
&primaryKey
) 
1034     StLock
<Mutex
> _(mDbDeletedItemMapMutex
); 
1035     DbItemMap::iterator it 
= mDbDeletedItemMap
.find(primaryKey
); 
1036     if (it 
!= mDbDeletedItemMap
.end()) 
1045 KeychainImpl::item(const PrimaryKey 
&primaryKey
) 
1047         StLock
<Mutex
>_(mMutex
); 
1049         // Lookup the item in the map while holding the apiLock. 
1050         ItemImpl 
*itemImpl 
= _lookupItem(primaryKey
); 
1052                 return Item(itemImpl
); 
1057                 // We didn't find it so create a new item with just a keychain and 
1058                 // a primary key.  Some other thread might have beaten 
1059                 // us to creating this item and adding it to the cache.  If that 
1060                 // happens we retry the lookup. 
1061                 return Item(this, primaryKey
); 
1063         catch (const MacOSError 
&e
) 
1065                 // If the item creation failed because some other thread already 
1066                 // inserted this item into the cache we retry the lookup. 
1067                 if (e
.osStatus() == errSecDuplicateItem
) 
1069                         // Lookup the item in the map while holding the apiLock. 
1070                         ItemImpl 
*itemImpl 
= _lookupItem(primaryKey
); 
1072                                 return Item(itemImpl
); 
1077 // Check for an item that may have been deleted. 
1079 KeychainImpl::itemdeleted(const PrimaryKey
& primaryKey
) { 
1080     StLock
<Mutex
>_(mMutex
); 
1082     Item i 
= _lookupDeletedItemOnly(primaryKey
); 
1086         return item(primaryKey
); 
1092 KeychainImpl::item(CSSM_DB_RECORDTYPE recordType
, DbUniqueRecord 
&uniqueId
) 
1094         StLock
<Mutex
>_(mMutex
); 
1096         PrimaryKey primaryKey 
= makePrimaryKey(recordType
, uniqueId
); 
1098                 // Lookup the item in the map while holding the apiLock. 
1099                 ItemImpl 
*itemImpl 
= _lookupItem(primaryKey
); 
1103                         return Item(itemImpl
); 
1109                 // We didn't find it so create a new item with a keychain, a primary key 
1110                 // and a DbUniqueRecord. However since we aren't holding 
1111                 // globals().apiLock anymore some other thread might have beaten 
1112                 // us to creating this item and adding it to the cache.  If that 
1113                 // happens we retry the lookup. 
1114                 return Item(this, primaryKey
, uniqueId
); 
1116         catch (const MacOSError 
&e
) 
1118                 // If the item creation failed because some other thread already 
1119                 // inserted this item into the cache we retry the lookup. 
1120                 if (e
.osStatus() == errSecDuplicateItem
) 
1122                         // Lookup the item in the map while holding the apiLock. 
1123                         ItemImpl 
*itemImpl 
= _lookupItem(primaryKey
); 
1125                                 return Item(itemImpl
); 
1132 KeychainImpl::keychainSchema() 
1134         StLock
<Mutex
>_(mMutex
); 
1135         if (!mKeychainSchema
) 
1136                 mKeychainSchema 
= KeychainSchema(mDb
); 
1138         return mKeychainSchema
; 
1141 void KeychainImpl::resetSchema() 
1143         mKeychainSchema 
= NULL
; // re-fetch it from db next time 
1147 // Called from DbItemImpl's constructor (so it is only partially constructed), 
1148 // add it to the map.  
1150 KeychainImpl::addItem(const PrimaryKey 
&primaryKey
, ItemImpl 
*dbItemImpl
) 
1152         StLock
<Mutex
>_(mMutex
); 
1154         // The dbItemImpl shouldn't be in the cache yet 
1155         assert(!dbItemImpl
->inCache()); 
1157         // Insert dbItemImpl into mDbItemMap with key primaryKey.  p.second will 
1158         // be true if it got inserted. If not p.second will be false and p.first 
1159         // will point to the current entry with key primaryKey. 
1160     StLock
<Mutex
> __(mDbItemMapMutex
); 
1161         pair
<DbItemMap::iterator
, bool> p 
= 
1162                 mDbItemMap
.insert(DbItemMap::value_type(primaryKey
, dbItemImpl
)); 
1166                 // There was already an ItemImpl * in mDbItemMap with key primaryKey. 
1167                 // There is a race condition here when being called in multiple threads 
1168                 // We might have added an item using add and received a notification at 
1170                 MacOSError::throwMe(errSecDuplicateItem
); 
1173         dbItemImpl
->inCache(true); 
1177 KeychainImpl::didDeleteItem(ItemImpl 
*inItemImpl
) 
1179         StLock
<Mutex
>_(mMutex
); 
1181         // Called by CCallbackMgr 
1182     secinfo("kcnotify", "%p notified that item %p was deleted", this, inItemImpl
); 
1183         removeItem(inItemImpl
->primaryKey(), inItemImpl
); 
1187 KeychainImpl::removeItem(const PrimaryKey 
&primaryKey
, ItemImpl 
*inItemImpl
) 
1189         StLock
<Mutex
>_(mMutex
); 
1191         // If inItemImpl isn't in the cache to begin with we are done. 
1192         if (!inItemImpl
->inCache()) 
1196         StLock
<Mutex
> _(mDbItemMapMutex
); 
1197         DbItemMap::iterator it 
= mDbItemMap
.find(primaryKey
); 
1198         if (it 
!= mDbItemMap
.end() && (ItemImpl
*) it
->second 
== inItemImpl
) { 
1199             mDbItemMap
.erase(it
); 
1201     } // drop mDbItemMapMutex 
1204         StLock
<Mutex
> _(mDbDeletedItemMapMutex
); 
1205         DbItemMap::iterator it 
= mDbDeletedItemMap
.find(primaryKey
); 
1206         if (it 
!= mDbDeletedItemMap
.end() && (ItemImpl
*) it
->second 
== inItemImpl
) { 
1207             mDbDeletedItemMap
.erase(it
); 
1209     } // drop mDbDeletedItemMapMutex 
1211         inItemImpl
->inCache(false); 
1215 KeychainImpl::forceRemoveFromCache(ItemImpl
* inItemImpl
) { 
1217         // Wrap all this in a try-block and ignore all errors - we're trying to clean up these maps 
1219             StLock
<Mutex
> _(mDbItemMapMutex
); 
1220             for(DbItemMap::iterator it 
= mDbItemMap
.begin(); it 
!= mDbItemMap
.end(); ) { 
1221                 if(it
->second 
== inItemImpl
) { 
1222                     // Increment the iterator, but use its pre-increment value for the erase 
1223                     it
->second
->inCache(false); 
1224                     mDbItemMap
.erase(it
++); 
1229         } // drop mDbItemMapMutex 
1232             StLock
<Mutex
> _(mDbDeletedItemMapMutex
); 
1233             for(DbItemMap::iterator it 
= mDbDeletedItemMap
.begin(); it 
!= mDbDeletedItemMap
.end(); ) { 
1234                 if(it
->second 
== inItemImpl
) { 
1235                     // Increment the iterator, but use its pre-increment value for the erase 
1236                     it
->second
->inCache(false); 
1237                     mDbDeletedItemMap
.erase(it
++); 
1242         } // drop mDbDeletedItemMapMutex 
1243     } catch(UnixError ue
) { 
1244         secnotice("keychain", "caught UnixError: %d %s", ue
.unixError(), ue
.what()); 
1245     } catch (CssmError cssme
) { 
1246         const char* errStr 
= cssmErrorString(cssme
.error
); 
1247         secnotice("keychain", "caught CssmError: %d %s", (int) cssme
.error
, errStr
); 
1248     } catch (MacOSError mose
) { 
1249         secnotice("keychain", "MacOSError: %d", (int)mose
.osStatus()); 
1251         secnotice("keychain", "Unknown error"); 
1256 KeychainImpl::getAttributeInfoForItemID(CSSM_DB_RECORDTYPE itemID
, 
1257         SecKeychainAttributeInfo 
**Info
) 
1259         StLock
<Mutex
>_(mMutex
); 
1263                 keychainSchema()->getAttributeInfoForRecordType(itemID
, Info
); 
1265         catch (const CommonError 
&error
) 
1267                 switch (error
.osStatus()) 
1269                 case errSecNoSuchClass
: 
1270                 case CSSMERR_DL_INVALID_RECORDTYPE
: 
1272                         keychainSchema()->getAttributeInfoForRecordType(itemID
, Info
); 
1280 KeychainImpl::freeAttributeInfo(SecKeychainAttributeInfo 
*Info
) 
1288 KeychainImpl::attributeInfoFor(CSSM_DB_RECORDTYPE recordType
, UInt32 tag
) 
1290         StLock
<Mutex
>_(mMutex
); 
1294                 return keychainSchema()->attributeInfoFor(recordType
, tag
); 
1296         catch (const CommonError 
&error
) 
1298                 switch (error
.osStatus()) 
1300                 case errSecNoSuchClass
: 
1301                 case CSSMERR_DL_INVALID_RECORDTYPE
: 
1303                         return keychainSchema()->attributeInfoFor(recordType
, tag
); 
1311 KeychainImpl::recode(const CssmData 
&data
, const CssmData 
&extraData
) 
1313         StLock
<Mutex
>_(mMutex
); 
1315         mDb
->recode(data
, extraData
); 
1319 KeychainImpl::copyBlob(CssmData 
&data
) 
1321         StLock
<Mutex
>_(mMutex
); 
1323         mDb
->copyBlob(data
); 
1327 KeychainImpl::setBatchMode(Boolean mode
, Boolean rollback
) 
1329         StLock
<Mutex
>_(mMutex
); 
1331         mDb
->setBatchMode(mode
, rollback
); 
1332         mIsInBatchMode 
= mode
; 
1335                 if (!rollback
) // was batch mode being turned off without an abort? 
1338                         EventBuffer::iterator it 
= mEventBuffer
->begin(); 
1339                         while (it 
!= mEventBuffer
->end()) 
1341                                 PrimaryKey primaryKey
; 
1344                                         primaryKey 
= it
->item
->primaryKey(); 
1347                                 KCEventNotifier::PostKeychainEvent(it
->kcEvent
, mDb
->dlDbIdentifier(), primaryKey
); 
1354                 // notify that a keychain has changed in too many ways to count 
1355                 KCEventNotifier::PostKeychainEvent((SecKeychainEvent
) kSecKeychainLeftBatchModeEvent
); 
1356                 mEventBuffer
->clear(); 
1360                 KCEventNotifier::PostKeychainEvent((SecKeychainEvent
) kSecKeychainEnteredBatchModeEvent
); 
1364 KeychainImpl::postEvent(SecKeychainEvent kcEvent
, ItemImpl
* item
) 
1366     postEvent(kcEvent
, item
, NULL
); 
1370 KeychainImpl::postEvent(SecKeychainEvent kcEvent
, ItemImpl
* item
, PrimaryKey pk
) 
1372         PrimaryKey primaryKey
; 
1377                 StLock
<Mutex
>_(mMutex
); 
1381                         primaryKey 
= item
->primaryKey(); 
1385         if (!mIsInBatchMode
) 
1387                 KCEventNotifier::PostKeychainEvent(kcEvent
, mDb
->dlDbIdentifier(), primaryKey
); 
1391                 StLock
<Mutex
>_(mMutex
); 
1394                 it
.kcEvent 
= kcEvent
; 
1400                 mEventBuffer
->push_back (it
); 
1404 void KeychainImpl::tickle() { 
1405     if(!mSuppressTickle
) { 
1406         globals().storageManager
.tickleKeychain(this); 
1411 bool KeychainImpl::performKeychainUpgradeIfNeeded() { 
1412     // Grab this keychain's mutex. 
1413     StLock
<Mutex
>_(mMutex
); 
1415     if(!globals().integrityProtection()) { 
1416         secnotice("integrity", "skipping upgrade for %s due to global integrity protection being disabled", mDb
->name()); 
1420     // We need a CSP database for 'upgrade' to be meaningful 
1421     if((mDb
->dl()->subserviceMask() & CSSM_SERVICE_CSP
) == 0) { 
1425     // We only want to upgrade file-based Apple keychains. Check the GUID. 
1426     if(mDb
->dl()->guid() != gGuidAppleCSPDL
) { 
1427         secinfo("integrity", "skipping upgrade for %s due to guid mismatch\n", mDb
->name()); 
1431     // If we've already attempted an upgrade on this keychain, don't bother again 
1432     if(mAttemptedUpgrade
) { 
1436     // Don't upgrade the System root certificate keychain (to make old tp code happy) 
1437     if(strncmp(mDb
->name(), SYSTEM_ROOT_STORE_PATH
, strlen(SYSTEM_ROOT_STORE_PATH
)) == 0) { 
1438         secinfo("integrity", "skipping upgrade for %s\n", mDb
->name()); 
1442     uint32 dbBlobVersion 
= SecurityServer::DbBlob::version_MacOS_10_0
; 
1445         dbBlobVersion 
= mDb
->dbBlobVersion(); 
1446     } catch (CssmError cssme
) { 
1447         if(cssme
.error 
== CSSMERR_DL_DATASTORE_DOESNOT_EXIST
) { 
1448             // oh well! We tried to get the blob version of a database 
1449             // that doesn't exist. It doesn't need migration, so do nothing. 
1450             secnotice("integrity", "dbBlobVersion() failed for a non-existent database"); 
1453             // Some other error occurred. We can't upgrade this keychain, so fail. 
1454             const char* errStr 
= cssmErrorString(cssme
.error
); 
1455             secnotice("integrity", "dbBlobVersion() failed for a CssmError: %d %s", (int) cssme
.error
, errStr
); 
1459         secnotice("integrity", "dbBlobVersion() failed for an unknown reason"); 
1465     // Check the location of this keychain 
1466     string path 
= mDb
->name(); 
1467     string keychainDbPath 
= StorageManager::makeKeychainDbFilename(path
); 
1469     bool inHomeLibraryKeychains 
= StorageManager::pathInHomeLibraryKeychains(path
); 
1471     string keychainDbSuffix 
= "-db"; 
1472     bool endsWithKeychainDb 
= (path
.size() > keychainDbSuffix
.size() && (0 == path
.compare(path
.size() - keychainDbSuffix
.size(), keychainDbSuffix
.size(), keychainDbSuffix
))); 
1474     bool isSystemKeychain 
= (0 == path
.compare("/Library/Keychains/System.keychain")); 
1476     bool result 
= false; 
1478     if(inHomeLibraryKeychains 
&& endsWithKeychainDb 
&& dbBlobVersion 
== SecurityServer::DbBlob::version_MacOS_10_0
) { 
1479         // something has gone horribly wrong: an old-versioned keychain has a .keychain-db name. Rename it. 
1480         string basePath 
= path
; 
1481         basePath
.erase(basePath
.end()-3, basePath
.end()); 
1483         attemptKeychainRename(path
, basePath
, dbBlobVersion
); 
1485         // If we moved to a good path, we might still want to perform the upgrade. Update our variables. 
1489             dbBlobVersion 
= mDb
->dbBlobVersion(); 
1490         } catch (CssmError cssme
) { 
1491             const char* errStr 
= cssmErrorString(cssme
.error
); 
1492             secnotice("integrity", "dbBlobVersion() after a rename failed for a CssmError: %d %s", (int) cssme
.error
, errStr
); 
1495             secnotice("integrity", "dbBlobVersion() failed for an unknown reason after a rename"); 
1499         endsWithKeychainDb 
= (path
.size() > keychainDbSuffix
.size() && (0 == path
.compare(path
.size() - keychainDbSuffix
.size(), keychainDbSuffix
.size(), keychainDbSuffix
))); 
1500         keychainDbPath 
= StorageManager::makeKeychainDbFilename(path
); 
1501         secnotice("integrity", "after rename, our database thinks that it is %s", path
.c_str()); 
1504     // Migrate an old keychain in ~/Library/Keychains 
1505     if(inHomeLibraryKeychains 
&& dbBlobVersion 
!= SecurityServer::DbBlob::version_partition 
&& !endsWithKeychainDb
) { 
1506         // We can only attempt to migrate an unlocked keychain. 
1507         if(mDb
->isLocked()) { 
1508             // However, it's possible that while we weren't doing any keychain operations, someone upgraded the keychain, 
1509             // and then locked it. No way around hitting the filesystem here: check for the existence of a new file and, 
1510             // if no new file exists, quit. 
1511             DLDbIdentifier mungedDLDbIdentifier 
= StorageManager::mungeDLDbIdentifier(mDb
->dlDbIdentifier(), false); 
1512             string 
mungedPath(mungedDLDbIdentifier
.dbName()); 
1514             // If this matches the file we already have, skip the upgrade. Otherwise, continue. 
1515             if(mungedPath 
== path
) { 
1516                 secnotice("integrity", "skipping upgrade for locked keychain %s\n", mDb
->name()); 
1521         result 
= keychainMigration(path
, dbBlobVersion
, keychainDbPath
, SecurityServer::DbBlob::version_partition
); 
1522     } else if(inHomeLibraryKeychains 
&& dbBlobVersion 
== SecurityServer::DbBlob::version_partition 
&& !endsWithKeychainDb
) { 
1523         // This is a new-style keychain with the wrong name, try to rename it 
1524         attemptKeychainRename(path
, keychainDbPath
, dbBlobVersion
); 
1526     } else if(isSystemKeychain 
&& dbBlobVersion 
== SecurityServer::DbBlob::version_partition
) { 
1527         // Try to "unupgrade" the system keychain, to clean up our old issues 
1528         secnotice("integrity", "attempting downgrade for %s version %d (%d %d %d)", path
.c_str(), dbBlobVersion
, inHomeLibraryKeychains
, endsWithKeychainDb
, isSystemKeychain
); 
1530         // First step: acquire the credentials to allow for ACL modification 
1531         SecurityServer::SystemKeychainKey 
skk(kSystemUnlockFile
); 
1533             // We've managed to read the key; now, create credentials using it 
1534             CssmClient::Key 
systemKeychainMasterKey(csp(), skk
.key(), true); 
1535             CssmClient::AclFactory::MasterKeyUnlockCredentials 
creds(systemKeychainMasterKey
, Allocator::standard(Allocator::sensitive
)); 
1537             // Attempt the downgrade, using our master key as the ACL override 
1538             result 
= keychainMigration(path
, dbBlobVersion
, path
, SecurityServer::DbBlob::version_MacOS_10_0
, creds
.getAccessCredentials()); 
1540             secnotice("integrity", "Couldn't read System.keychain key, skipping update"); 
1543         secinfo("integrity", "not attempting migration for %s version %d (%d %d %d)", path
.c_str(), dbBlobVersion
, inHomeLibraryKeychains
, endsWithKeychainDb
, isSystemKeychain
); 
1545         // Since we don't believe any migration needs to be done here, mark the 
1546         // migration as "attempted" to short-circuit future checks. 
1547         mAttemptedUpgrade 
= true; 
1550     // We might have changed our location on disk. Let StorageManager know. 
1551     globals().storageManager
.registerKeychainImpl(this); 
1553     // if we attempted a migration, try to clean up leftover files from <rdar://problem/23950408> XARA backup have provided me with 12GB of login keychain copies 
1555         string pattern 
= path 
+ "_*_backup"; 
1557         secnotice("integrity", "globbing for %s", pattern
.c_str()); 
1558         int globresult 
= glob(pattern
.c_str(), GLOB_MARK
, NULL
, &pglob
); 
1559         if(globresult 
== 0) { 
1560             secnotice("integrity", "glob: %lu results", pglob
.gl_pathc
); 
1561             if(pglob
.gl_pathc 
> 10) { 
1562                 // There are more than 10 backup files, indicating a problem. 
1563                 // Delete all but one of them. Under rdar://23950408, they should all be identical. 
1564                 secnotice("integrity", "saving backup file: %s", pglob
.gl_pathv
[0]); 
1565                 for(int i 
= 1; i 
< pglob
.gl_pathc
; i
++) { 
1566                     secnotice("integrity", "cleaning up backup file: %s", pglob
.gl_pathv
[i
]); 
1567                     // ignore return code; this is a best-effort cleanup 
1568                     unlink(pglob
.gl_pathv
[i
]); 
1573             bool pathExists 
= (::stat(path
.c_str(), &st
) == 0); 
1574             bool keychainDbPathExists 
= (::stat(keychainDbPath
.c_str(), &st
) == 0); 
1576             if(!pathExists 
&& keychainDbPathExists 
&& pglob
.gl_pathc 
>= 1) { 
1577                 // We have a file at keychainDbPath, no file at path, and at least one backup keychain file. 
1579                 // Move the backup file to path, to simulate the current  "split-world" view, 
1580                 // which copies from path to keychainDbPath, then modifies keychainDbPath. 
1581                 secnotice("integrity", "moving backup file %s to %s", pglob
.gl_pathv
[0], path
.c_str()); 
1582                 ::rename(pglob
.gl_pathv
[0], path
.c_str()); 
1592 bool KeychainImpl::keychainMigration(const string oldPath
, const uint32 dbBlobVersion
, const string newPath
, const uint32 newBlobVersion
, const AccessCredentials 
*cred
) { 
1593     secnotice("integrity", "going to migrate %s at version %d to", oldPath
.c_str(), dbBlobVersion
); 
1594     secnotice("integrity", "                 %s at version %d", newPath
.c_str(), newBlobVersion
); 
1596     // We need to opportunistically perform the upgrade/reload dance. 
1598     // If the keychain is unlocked, try to upgrade it. 
1599     // In either case, reload the database from disk. 
1601     // Try to grab the keychain mutex (although we should already have it) 
1602     StLock
<Mutex
>_(mMutex
); 
1604     // Take the file lock on the existing database. We don't need to commit this txion, because we're not planning to 
1605     // change the original keychain. 
1606     FileLockTransaction 
fileLockmDb(mDb
); 
1608     // Let's reload this keychain to see if someone changed it on disk 
1609     globals().storageManager
.reloadKeychain(this); 
1611     bool result 
= false; 
1614         // We can only attempt an upgrade if the keychain is currently unlocked 
1615         // There's a TOCTTOU issue here, but it's going to be rare in practice, and the upgrade will simply fail. 
1616         if(!mDb
->isLocked()) { 
1617             secnotice("integrity", "have a plan to migrate database %s", mDb
->name()); 
1618             // Database blob is out of date. Attempt a migration. 
1619             uint32 convertedVersion 
= attemptKeychainMigration(oldPath
, dbBlobVersion
, newPath
, newBlobVersion
, cred
); 
1620             if(convertedVersion 
== newBlobVersion
) { 
1621                 secnotice("integrity", "conversion succeeded"); 
1624                 secnotice("integrity", "conversion failed, keychain is still %d", convertedVersion
); 
1627             secnotice("integrity", "keychain is locked, can't upgrade"); 
1629     } catch (CssmError cssme
) { 
1630         const char* errStr 
= cssmErrorString(cssme
.error
); 
1631         secnotice("integrity", "caught CssmError: %d %s", (int) cssme
.error
, errStr
); 
1633         // Something went wrong, but don't worry about it. 
1634         secnotice("integrity", "caught unknown error"); 
1637     // No matter if the migrator succeeded, we need to reload this keychain from disk. 
1638     secnotice("integrity", "reloading keychain after migration"); 
1639     globals().storageManager
.reloadKeychain(this); 
1640     secnotice("integrity", "database %s is now version %d", mDb
->name(), mDb
->dbBlobVersion()); 
1645 // Make sure you have this keychain's mutex and write lock when you call this function! 
1646 uint32 
KeychainImpl::attemptKeychainMigration(const string oldPath
, const uint32 oldBlobVersion
, const string newPath
, const uint32 newBlobVersion
, const AccessCredentials
* cred
) { 
1647     if(mDb
->dbBlobVersion() == newBlobVersion
) { 
1648         // Someone else upgraded this, hurray! 
1649         secnotice("integrity", "reloaded keychain version %d, quitting", mDb
->dbBlobVersion()); 
1650         return newBlobVersion
; 
1653     mAttemptedUpgrade 
= true; 
1654     uint32 newDbVersion 
= oldBlobVersion
; 
1656     if( (oldBlobVersion 
== SecurityServer::DbBlob::version_MacOS_10_0 
&& newBlobVersion 
== SecurityServer::DbBlob::version_partition
) || 
1657         (oldBlobVersion 
== SecurityServer::DbBlob::version_partition 
&& newBlobVersion 
== SecurityServer::DbBlob::version_MacOS_10_0 
&& cred 
!= NULL
)) { 
1658         // Here's the upgrade outline: 
1660         //   1. Make a copy of the keychain with the new file path 
1661         //   2. Open that keychain database. 
1662         //   3. Recode it to use the new version. 
1663         //   4. Notify the StorageManager that the DLDB identifier for this keychain has changed. 
1665         // If we're creating a new keychain file, on failure, try to delete the new file. Otherwise, 
1666         // everyone will try to use it. 
1668         secnotice("integrity", "attempting migration from version %d to %d", oldBlobVersion
, newBlobVersion
); 
1671         bool newFile 
= (oldPath 
!= newPath
); 
1674             DLDbIdentifier 
dldbi(dlDbIdentifier().ssuid(), newPath
.c_str(), dlDbIdentifier().dbLocation()); 
1676                 secnotice("integrity", "creating a new keychain at %s", newPath
.c_str()); 
1677                 db 
= mDb
->cloneTo(dldbi
); 
1679                 secnotice("integrity", "using old keychain at %s", newPath
.c_str()); 
1682             FileLockTransaction 
fileLockDb(db
); 
1685                 // since we're creating a completely new file, if this migration fails, delete the new file 
1686                 fileLockDb
.setDeleteOnFailure(); 
1689             // Let the upgrade begin. 
1690             newDbVersion 
= db
->recodeDbToVersion(newBlobVersion
); 
1691             if(newDbVersion 
!= newBlobVersion
) { 
1692                 // Recoding failed. Don't proceed. 
1693                 secnotice("integrity", "recodeDbToVersion failed, version is still %d", newDbVersion
); 
1694                 return newDbVersion
; 
1697             secnotice("integrity", "recoded db successfully, adding extra integrity"); 
1699             Keychain 
keychain(db
); 
1701             // Breaking abstraction, but what're you going to do? 
1702             // Don't upgrade this keychain, since we just upgraded the DB 
1703             // But the DB won't return any new data until the txion commits 
1704             keychain
->mAttemptedUpgrade 
= true; 
1705             keychain
->mSuppressTickle 
= true; 
1707             SecItemClass classes
[] = {kSecGenericPasswordItemClass
, 
1708                                       kSecInternetPasswordItemClass
, 
1709                                       kSecPublicKeyItemClass
, 
1710                                       kSecPrivateKeyItemClass
, 
1711                                       kSecSymmetricKeyItemClass
}; 
1713             for(int i 
= 0; i 
< sizeof(classes
) / sizeof(classes
[0]); i
++) { 
1715                 KCCursor kcc 
= keychain
->createCursor(classes
[i
], NULL
); 
1717                 // During recoding, we might have deleted some corrupt keys. 
1718                 // Because of this, we might have zombie SSGroup records left in 
1719                 // the database that have no matching key. Tell the KCCursor to 
1720                 // delete these if found. 
1721                 // This will also try to suppress any other invalid items. 
1722                 kcc
->setDeleteInvalidRecords(true); 
1724                 while(kcc
->next(item
)) { 
1726                         if(newBlobVersion 
== SecurityServer::DbBlob::version_partition
) { 
1727                             // Force the item to set integrity. The keychain is confused about its version because it hasn't written to disk yet, 
1728                             // but if we've reached this point, the keychain supports integrity. 
1729                             item
->setIntegrity(true); 
1730                         } else if(newBlobVersion 
== SecurityServer::DbBlob::version_MacOS_10_0
) { 
1731                             // We're downgrading this keychain. Pass in whatever credentials our caller thinks will allow this ACL modification. 
1732                             item
->removeIntegrity(cred
); 
1734                     } catch(CssmError cssme
) { 
1735                         // During recoding, we might have deleted some corrupt keys. Because of this, we might have zombie SSGroup records left in 
1736                         // the database that have no matching key. If we get a DL_RECORD_NOT_FOUND error, delete the matching item record. 
1737                         if (cssme
.osStatus() == CSSMERR_DL_RECORD_NOT_FOUND
) { 
1738                             secnotice("integrity", "deleting corrupt (Not Found) record"); 
1739                             keychain
->deleteItem(item
); 
1740                         } else if(cssme
.osStatus() == CSSMERR_CSP_INVALID_KEY
) { 
1741                             secnotice("integrity", "deleting corrupt key record"); 
1742                             keychain
->deleteItem(item
); 
1750             // Tell securityd we're done with the upgrade, to re-enable all protections 
1751             db
->recodeFinished(); 
1753             // If we reach here, tell the file locks to commit the transaction and return the new blob version 
1754             fileLockDb
.success(); 
1756             secnotice("integrity", "success, returning version %d", newDbVersion
); 
1757             return newDbVersion
; 
1758         } catch(UnixError ue
) { 
1759             secnotice("integrity", "caught UnixError: %d %s", ue
.unixError(), ue
.what()); 
1760         } catch (CssmError cssme
) { 
1761             const char* errStr 
= cssmErrorString(cssme
.error
); 
1762             secnotice("integrity", "caught CssmError: %d %s", (int) cssme
.error
, errStr
); 
1763         } catch (MacOSError mose
) { 
1764             secnotice("integrity", "MacOSError: %d", (int)mose
.osStatus()); 
1765         } catch (const std::bad_cast 
& e
) { 
1766             secnotice("integrity", "***** bad cast: %s", e
.what()); 
1768             // We failed to migrate. We won't commit the transaction, so the blob on-disk stays the same. 
1769             secnotice("integrity", "***** unknown error"); 
1772         secnotice("integrity", "no migration path for %s at version %d to", oldPath
.c_str(), oldBlobVersion
); 
1773         secnotice("integrity", "                      %s at version %d", newPath
.c_str(), newBlobVersion
); 
1774         return oldBlobVersion
; 
1777     // If we reached here, the migration failed. Return the old version. 
1778     return oldBlobVersion
; 
1781 void KeychainImpl::attemptKeychainRename(const string oldPath
, const string newPath
, uint32 blobVersion
) { 
1782     secnotice("integrity", "attempting to rename keychain (%d) from %s to %s", blobVersion
, oldPath
.c_str(), newPath
.c_str()); 
1784     // Take the file lock on this database, so other people won't try to move it before we do 
1785     // NOTE: during a migration from a v256 to a v512 keychain, the db is first copied from the .keychain to the 
1786     //       .keychain-db path. Other non-migrating processes, if they open the keychain, enter this function to 
1787     //       try to move it back. These will attempt to take the .keychain-db file lock, but they will not succeed 
1788     //       until the migration is finished. Once they acquire that, they might try to take the .keychain file lock. 
1789     //       This is technically lock inversion, but deadlocks will not happen since the migrating process creates the 
1790     //       .keychain-db file lock before creating the .keychain-db file, so other processes will not try to grab the 
1791     //       .keychain-db lock in this function before the migrating process already has it. 
1792     FileLockTransaction 
fileLockmDb(mDb
); 
1794     // first, check if someone renamed this keychain while we were grabbing the file lock 
1795     globals().storageManager
.reloadKeychain(this); 
1797     uint32 dbBlobVersion 
= SecurityServer::DbBlob::version_MacOS_10_0
; 
1800         dbBlobVersion 
= mDb
->dbBlobVersion(); 
1802         secnotice("integrity", "dbBlobVersion() failed for an unknown reason while renaming, aborting rename"); 
1806     if(dbBlobVersion 
!= blobVersion
) { 
1807         secnotice("integrity", "database version changed while we were grabbing the file lock; aborting rename"); 
1811     if(oldPath 
!= mDb
->name()) { 
1812         secnotice("integrity", "database location changed while we were grabbing the file lock; aborting rename"); 
1816     // we're still at the original location and version; go ahead and do the move 
1817     globals().storageManager
.rename(this, newPath
.c_str()); 
1820 Keychain::Keychain() 
1822         dispatch_once(&SecKeychainSystemKeychainChecked
, ^{ 
1823                 check_system_keychain(); 
1827 Keychain::~Keychain() 
1834 Keychain::optional(SecKeychainRef handle
) 
1837                 return KeychainImpl::required(handle
); 
1839                 return globals().storageManager
.defaultKeychain(); 
1843 CFIndex 
KeychainCore::GetKeychainRetainCount(Keychain
& kc
) 
1845         CFTypeRef ref 
= kc
->handle(false); 
1846         return CFGetRetainCount(ref
); 
1851 // Create default credentials for this keychain. 
1852 // This is triggered upon default open (i.e. a Db::activate() with no set credentials). 
1854 // This function embodies the "default credentials" logic for Keychain-layer databases. 
1856 const AccessCredentials 
* 
1857 KeychainImpl::makeCredentials() 
1859         return defaultCredentials(); 
1863 const AccessCredentials 
* 
1864 KeychainImpl::defaultCredentials() 
1866         StLock
<Mutex
>_(mMutex
); 
1868         // Use custom unlock credentials for file keychains which have a referral 
1869         // record and the standard credentials for all others. 
1871         if (mDb
->dl()->guid() == gGuidAppleCSPDL 
&& mCustomUnlockCreds(mDb
)) 
1872                 return &mCustomUnlockCreds
; 
1874         if (mDb
->dl()->guid() == gGuidAppleSdCSPDL
) 
1875                 return globals().smartcardCredentials(); 
1877                 return globals().keychainCredentials(); 
1882 bool KeychainImpl::mayDelete() 
1887 bool KeychainImpl::hasIntegrityProtection() { 
1888     StLock
<Mutex
>_(mMutex
); 
1890     // This keychain only supports integrity if there's a database attached, that database is an Apple CSPDL, and the blob version is high enough 
1891     if(mDb 
&& (mDb
->dl()->guid() == gGuidAppleCSPDL
)) { 
1892         if(mDb
->dbBlobVersion() >= SecurityServer::DbBlob::version_partition
) { 
1895             secnotice("integrity", "keychain blob version does not support integrity"); 
1899         secnotice("integrity", "keychain guid does not support integrity");