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