/ OSX / libsecurity_keychain / lib / StorageManager.cpp
StorageManager.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  	File:		StorageManager.cpp
  27  
  28  	Contains:	Working with multiple keychains
  29  
  30  */
  31  
  32  #include "StorageManager.h"
  33  #include "KCEventNotifier.h"
  34  
  35  #include <Security/cssmapple.h>
  36  #include <sys/types.h>
  37  #include <sys/param.h>
  38  #include <syslog.h>
  39  #include <pwd.h>
  40  #include <algorithm>
  41  #include <string>
  42  #include <stdio.h>
  43  #include <security_utilities/debugging.h>
  44  #include <security_keychain/SecCFTypes.h>
  45  #include <securityd_client/ssclient.h>
  46  #include <Security/AuthorizationTags.h>
  47  #include <Security/AuthorizationTagsPriv.h>
  48  #include <Security/SecTask.h>
  49  #include <security_keychain/SecCFTypes.h>
  50  #include <Security/SecCFAllocator.h>
  51  #include "TrustSettingsSchema.h"
  52  #include <security_cdsa_client/wrapkey.h>
  53  #include <securityd_client/ssblob.h>
  54  #include <Security/SecBasePriv.h>
  55  #include "TokenLogin.h"
  56  
  57  //%%% add this to AuthorizationTagsPriv.h later
  58  #ifndef AGENT_HINT_LOGIN_KC_SUPPRESS_RESET_PANEL
  59  #define AGENT_HINT_LOGIN_KC_SUPPRESS_RESET_PANEL "loginKCCreate:suppressResetPanel"
  60  #endif
  61  
  62  #include "KCCursor.h"
  63  #include "Globals.h"
  64  
  65  
  66  using namespace CssmClient;
  67  using namespace KeychainCore;
  68  
  69  #define kLoginKeychainPathPrefix "~/Library/Keychains/"
  70  #define kUserLoginKeychainPath "~/Library/Keychains/login.keychain"
  71  #define kEmptyKeychainSizeInBytes   20460
  72  
  73  //-----------------------------------------------------------------------------------
  74  
  75  static SecPreferencesDomain defaultPreferenceDomain()
  76  {
  77  	SessionAttributeBits sessionAttrs;
  78  	if (gServerMode) {
  79  		secnotice("servermode", "StorageManager initialized in server mode");
  80  		sessionAttrs = sessionIsRoot;
  81  	} else {
  82  		MacOSError::check(SessionGetInfo(callerSecuritySession, NULL, &sessionAttrs));
  83  	}
  84  
  85  	// If this is the root session, use system preferences.
  86  	// (In SecurityServer debug mode, you'll get a (fake) root session
  87  	// that has graphics access. Ignore that to help testing.)
  88  	if ((sessionAttrs & sessionIsRoot)
  89  			IFDEBUG( && !(sessionAttrs & sessionHasGraphicAccess))) {
  90  		secnotice("storagemgr", "using system preferences");
  91  		return kSecPreferencesDomainSystem;
  92  	}
  93  
  94  	// otherwise, use normal (user) preferences
  95  	return kSecPreferencesDomainUser;
  96  }
  97  
  98  static bool isAppSandboxed()
  99  {
 100  	bool result = false;
 101  	SecTaskRef task = SecTaskCreateFromSelf(NULL);
 102  	if(task != NULL) {
 103  		CFTypeRef appSandboxValue = SecTaskCopyValueForEntitlement(task,
 104  			CFSTR("com.apple.security.app-sandbox"), NULL);
 105  		if(appSandboxValue != NULL) {
 106  			result = true;
 107  			CFRelease(appSandboxValue);
 108  		}
 109  		CFRelease(task);
 110  	}
 111  	return result;
 112  }
 113  
 114  static bool shouldAddToSearchList(const DLDbIdentifier &dLDbIdentifier)
 115  {
 116  	// Creation of a private keychain should not modify the search list: rdar://13529331
 117  	// However, we want to ensure the login and System keychains are in
 118  	// the search list if that is not the case when they are created.
 119  	// Note that App Sandbox apps may not modify the list in either case.
 120  
 121  	bool loginOrSystemKeychain = false;
 122  	const char *dbname = dLDbIdentifier.dbName();
 123  	if (dbname) {
 124  		if ((!strcmp(dbname, "/Library/Keychains/System.keychain")) ||
 125  			(strstr(dbname, "/login.keychain")) ) {
 126  			loginOrSystemKeychain = true;
 127  		}
 128  	}
 129  	return (loginOrSystemKeychain && !isAppSandboxed());
 130  }
 131  
 132  
 133  StorageManager::StorageManager() :
 134  	mSavedList(defaultPreferenceDomain()),
 135  	mCommonList(kSecPreferencesDomainCommon),
 136  	mDomain(kSecPreferencesDomainUser),
 137  	mMutex(Mutex::recursive)
 138  {
 139  }
 140  
 141  
 142  Mutex*
 143  StorageManager::getStorageManagerMutex()
 144  {
 145  	return &mKeychainMapMutex;
 146  }
 147  
 148  
 149  Keychain
 150  StorageManager::keychain(const DLDbIdentifier &dLDbIdentifier)
 151  {
 152  	StLock<Mutex>_(mKeychainMapMutex);
 153  
 154  	if (!dLDbIdentifier)
 155  		return Keychain();
 156  
 157      KeychainMap::iterator it = mKeychainMap.end();
 158  
 159      // If we have a keychain object for the munged keychain, return that.
 160      // Don't hit the filesystem to check file status if we've already done that work...
 161      DLDbIdentifier munge_dldbi = forceMungeDLDbIDentifier(dLDbIdentifier);
 162      it = mKeychainMap.find(munge_dldbi);
 163      if (it != mKeychainMap.end()) {
 164          return it->second;
 165      }
 166  
 167      // If we have a keychain object for the un/demunged keychain, return that.
 168      // We might be in the middle of an upgrade, where the -db file exists as a bit-perfect copy of the original file.
 169      DLDbIdentifier demunge_dldbi = demungeDLDbIdentifier(dLDbIdentifier);
 170      it = mKeychainMap.find(demunge_dldbi);
 171      if (it != mKeychainMap.end()) {
 172          return it->second;
 173      }
 174  
 175      // Okay, we haven't seen this keychain before. Do the full process...
 176      DLDbIdentifier dldbi = mungeDLDbIdentifier(dLDbIdentifier, false);
 177      it = mKeychainMap.find(dldbi); // Almost certain not to find it here
 178      if (it != mKeychainMap.end())
 179  	{
 180          return it->second;
 181  	}
 182  
 183  	if (gServerMode) {
 184  		secnotice("servermode", "keychain reference in server mode");
 185  		return Keychain();
 186  	}
 187  
 188      // The keychain is not in our cache.  Create it.
 189      Db db(makeDb(dldbi));
 190  
 191  	Keychain keychain(db);
 192  	// Add the keychain to the cache.
 193      registerKeychain(keychain);
 194  
 195  	return keychain;
 196  }
 197  
 198  // Note: this must be a munged DLDbidentifier.
 199  CssmClient::Db
 200  StorageManager::makeDb(DLDbIdentifier dLDbIdentifier) {
 201      Module module(dLDbIdentifier.ssuid().guid());
 202  
 203      DL dl;
 204      if (dLDbIdentifier.ssuid().subserviceType() & CSSM_SERVICE_CSP)
 205          dl = SSCSPDL(module);
 206      else
 207          dl = DL(module);
 208  
 209      dl->subserviceId(dLDbIdentifier.ssuid().subserviceId());
 210      dl->version(dLDbIdentifier.ssuid().version());
 211  
 212      CssmClient::Db db(dl, dLDbIdentifier.dbName());
 213  
 214      return db;
 215  }
 216  
 217  // StorageManager is responsible for silently switching to newer-style keychains.
 218  // If the keychain requested is in ~/Library/Keychains/, and there is a
 219  // newer keychain available (with extension ".keychain-db"), open that one
 220  // instead of the one requested.
 221  //
 222  // Because of backwards compatibility reasons, we can't update the plist
 223  // files on disk to point to the upgraded keychains. We will be asked to
 224  // load "/Users/account/Library/Keychains/login.keychain", hence this
 225  // modification to 'login.keychain-db'.
 226  DLDbIdentifier
 227  StorageManager::mungeDLDbIdentifier(const DLDbIdentifier& dLDbIdentifier, bool isReset) {
 228      if(!dLDbIdentifier.dbName()) {
 229          // If this DLDbIdentifier doesn't have a filename, don't munge it
 230          return dLDbIdentifier;
 231      }
 232  
 233      string path = dLDbIdentifier.dbName();
 234  
 235      bool shouldCreateProtected = globals().integrityProtection();
 236  
 237      // If we don't have a DLDbIdentifier, we can't return one
 238      if(dLDbIdentifier.mImpl == NULL) {
 239          return DLDbIdentifier();
 240      }
 241  
 242      // Ensure we're in ~/Library/Keychains
 243      if(pathInHomeLibraryKeychains(path)) {
 244          string pathdb = makeKeychainDbFilename(path);
 245  
 246          struct stat st;
 247  
 248          int path_stat_err = 0;
 249          bool path_exists = (::stat(path.c_str(), &st) == 0);
 250          if(!path_exists) {
 251              path_stat_err = errno;
 252          }
 253  
 254          int pathdb_stat_err = 0;
 255          bool pathdb_exists = (::stat(pathdb.c_str(), &st) == 0);
 256          if(!pathdb_exists) {
 257              pathdb_stat_err = errno;
 258          }
 259  
 260          // If protections are off, don't change the requested filename.
 261          // If protictions are on and the -db file exists, always use it.
 262          //
 263          // If we're resetting, and we're creating a new-style keychain, use the -db path.
 264          // If we're resetting, and we're creating an old-style keychain, use the original path.
 265          //
 266          //  Protection  pathdb_exists path_exists resetting Result
 267          //  DISABLED       X           X             X       original
 268          //  ENABLED        1           X             X       -db
 269          //  ENABLED        0           0             X       -db
 270          //  ENABLED        0           1             0       original
 271          //  ENABLED        0           1             1       -db
 272          //
 273          bool switchPaths = shouldCreateProtected && (pathdb_exists || (!pathdb_exists && !path_exists) || isReset);
 274  
 275          if(switchPaths) {
 276              secinfo("integrity", "switching to keychain-db: %s from %s (%d %d %d_%d %d_%d)", pathdb.c_str(), path.c_str(), isReset, shouldCreateProtected, path_exists, path_stat_err, pathdb_exists, pathdb_stat_err);
 277              path = pathdb;
 278          } else {
 279              secinfo("integrity", "not switching: %s from %s (%d %d %d_%d %d_%d)", pathdb.c_str(), path.c_str(), isReset, shouldCreateProtected, path_exists, path_stat_err, pathdb_exists, pathdb_stat_err);
 280          }
 281      }
 282  
 283      DLDbIdentifier id(dLDbIdentifier.ssuid(), path.c_str(), dLDbIdentifier.dbLocation());
 284      return id;
 285  }
 286  
 287  DLDbIdentifier
 288  StorageManager::forceMungeDLDbIDentifier(const DLDbIdentifier& dLDbIdentifier) {
 289      if(!dLDbIdentifier.dbName() || dLDbIdentifier.mImpl == NULL) {
 290          return dLDbIdentifier;
 291      }
 292  
 293      string path = dLDbIdentifier.dbName();
 294      string pathdb = makeKeychainDbFilename(path);
 295  
 296      DLDbIdentifier id(dLDbIdentifier.ssuid(), pathdb.c_str(), dLDbIdentifier.dbLocation());
 297      return id;
 298  }
 299  
 300  DLDbIdentifier
 301  StorageManager::demungeDLDbIdentifier(const DLDbIdentifier& dLDbIdentifier) {
 302      if(dLDbIdentifier.dbName() == NULL) {
 303          return dLDbIdentifier;
 304      }
 305  
 306      string path = dLDbIdentifier.dbName();
 307      string dbSuffix = "-db";
 308      bool endsWithKeychainDb = (path.size() > dbSuffix.size() && (0 == path.compare(path.size() - dbSuffix.size(), dbSuffix.size(), dbSuffix)));
 309  
 310      // Ensure we're in ~/Library/Keychains, and that the path ends in "-db"
 311      if(pathInHomeLibraryKeychains(path) && endsWithKeychainDb) {
 312          // remove "-db" from the end.
 313          path.erase(path.end() - 3, path.end());
 314      }
 315  
 316      DLDbIdentifier id(dLDbIdentifier.ssuid(), path.c_str(), dLDbIdentifier.dbLocation());
 317      return id;
 318  }
 319  
 320  string
 321  StorageManager::makeKeychainDbFilename(const string& filename) {
 322      string keychainDbSuffix = "-db";
 323      bool endsWithKeychainDb = (filename.size() > keychainDbSuffix.size() && (0 == filename.compare(filename.size() - keychainDbSuffix.size(), keychainDbSuffix.size(), keychainDbSuffix)));
 324  
 325      if(endsWithKeychainDb) {
 326          return filename;
 327      } else {
 328          return filename + keychainDbSuffix;
 329      }
 330  }
 331  
 332  bool
 333  StorageManager::pathInHomeLibraryKeychains(const string& path) {
 334      return SecurityServer::CommonBlob::pathInHomeLibraryKeychains(path);
 335  }
 336  
 337  void
 338  StorageManager::reloadKeychain(Keychain keychain) {
 339      StLock<Mutex>_(mKeychainMapMutex);
 340  
 341      DLDbIdentifier dLDbIdentifier = keychain->database()->dlDbIdentifier();
 342  
 343      keychain->changeDatabase(makeDb(mungeDLDbIdentifier(dLDbIdentifier, false)));
 344  
 345      // This keychain might have a different dldbidentifier now, depending on what
 346      // other processes have been doing to the keychain files. Let's re-register it, just
 347      // to be sure.
 348      registerKeychain(keychain);
 349  }
 350  
 351  void
 352  StorageManager::removeKeychain(const DLDbIdentifier &dLDbIdentifier,
 353  	KeychainImpl *keychainImpl)
 354  {
 355      StLock<Mutex>_(mKeychainMapMutex);
 356  
 357      // Don't trust this dldbidentifier. Just look for the keychain and delete it.
 358      forceRemoveFromCache(keychainImpl);
 359  }
 360  
 361  void
 362  StorageManager::didRemoveKeychain(const DLDbIdentifier &dLDbIdentifier)
 363  {
 364  	// Lock the recursive mutex
 365  
 366  	StLock<Mutex>_(mKeychainMapMutex);
 367  
 368  	KeychainMap::iterator it = mKeychainMap.find(dLDbIdentifier);
 369  	if (it != mKeychainMap.end())
 370  	{
 371  		it->second->inCache(false);
 372  		mKeychainMap.erase(it);
 373  	}
 374  }
 375  
 376  // If the client does not keep references to keychains, they are destroyed on
 377  // every API exit, and recreated on every API entrance.
 378  //
 379  // To improve performance, we'll cache keychains for some short period of time.
 380  // We'll do this by CFRetaining the keychain object, and setting a timer to
 381  // CFRelease it when time's up. This way, the client can still recover all its
 382  // memory if it doesn't want the keychains around, but repeated API calls will
 383  // be significantly faster.
 384  //
 385  void
 386  StorageManager::tickleKeychain(KeychainImpl *keychainImpl) {
 387      static dispatch_once_t onceToken = 0;
 388      static dispatch_queue_t release_queue = NULL;
 389      dispatch_once(&onceToken, ^{
 390          release_queue = dispatch_queue_create("com.apple.security.keychain-cache-queue", DISPATCH_QUEUE_SERIAL);
 391      });
 392  
 393      __block KeychainImpl* kcImpl = keychainImpl;
 394  
 395      if(!kcImpl) {
 396          return;
 397      }
 398  
 399      // We really only want to cache CSPDL file-based keychains
 400      if(kcImpl->dlDbIdentifier().ssuid().guid() != gGuidAppleCSPDL) {
 401          return;
 402      }
 403  
 404      // Make a one-shot timer to release the keychain
 405      uint32_t seconds = 1;
 406  
 407      const string path = kcImpl->name();
 408      bool isSystemKeychain = (0 == path.compare("/Library/Keychains/System.keychain"));
 409      if(pathInHomeLibraryKeychains(path) || isSystemKeychain) {
 410          // These keychains are important and likely aren't on removable media.
 411          // Cache them longer.
 412          seconds = 5;
 413      }
 414  
 415      __block CFTypeRef kcHandle = kcImpl->handle(); // calls retain; this keychain object will stay around until our dispatch block fires.
 416  
 417      // You _must not_ call CFRelease while on this queue, due to the locking order mishmash. CFRelease takes a lock, so remember to do it later.
 418      __block bool releaseImmediately = false;
 419  
 420      dispatch_sync(release_queue, ^() {
 421          if(kcImpl->mCacheTimer) {
 422              // Update the cache timer to be seconds from now
 423              dispatch_source_set_timer(kcImpl->mCacheTimer, dispatch_time(DISPATCH_TIME_NOW, seconds * NSEC_PER_SEC), DISPATCH_TIME_FOREVER, NSEC_PER_SEC/2);
 424              secdebug("keychain", "updating cache on %p %s", kcImpl, kcImpl->name());
 425  
 426              // We've added an extra retain to this keychain right before invoking this block. Remember to release it.
 427              releaseImmediately = true;
 428          } else {
 429              // No cache timer; make one.
 430              kcImpl->mCacheTimer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, release_queue);
 431              dispatch_source_set_timer(kcImpl->mCacheTimer, dispatch_time(DISPATCH_TIME_NOW, seconds * NSEC_PER_SEC), DISPATCH_TIME_FOREVER, NSEC_PER_SEC/2);
 432              secdebug("keychain", "taking cache on %p %s", kcImpl, kcImpl->name());
 433  
 434              dispatch_source_set_event_handler(kcImpl->mCacheTimer, ^{
 435                  secdebug("keychain", "releasing cache on %p %s", kcImpl, kcImpl->name());
 436                  dispatch_source_cancel(kcImpl->mCacheTimer);
 437                  dispatch_release(kcImpl->mCacheTimer);
 438                  kcImpl->mCacheTimer = NULL;
 439  
 440                  // Since we're on the timer queue, we can't call CFRelease on the kcHandle (since that takes a lock). Dispatch_async it over to some other queue...
 441                  // This is better than using dispatch_async on the timer queue initially, since it's less dispatch_asyncs overall, even though it's more confusing.
 442                  dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND,0), ^{
 443                      CFRelease(kcHandle);
 444                  });
 445              });
 446  
 447              dispatch_resume(kcImpl->mCacheTimer);
 448          }
 449      });
 450  
 451      if(releaseImmediately) {
 452          CFRelease(kcHandle);
 453      }
 454  }
 455  
 456  // Create keychain if it doesn't exist, and optionally add it to the search list.
 457  Keychain
 458  StorageManager::makeKeychain(const DLDbIdentifier &dLDbIdentifier, bool add, bool isReset)
 459  {
 460  	StLock<Mutex>_(mKeychainMapMutex);
 461  
 462  	Keychain theKeychain = keychain(mungeDLDbIdentifier(dLDbIdentifier, isReset));
 463  	bool post = false;
 464  	bool updateList = (add && shouldAddToSearchList(dLDbIdentifier));
 465  
 466  	if (updateList)
 467  	{
 468  		mSavedList.revert(false);
 469  		DLDbList searchList = mSavedList.searchList();
 470  		if (find(searchList.begin(), searchList.end(), demungeDLDbIdentifier(dLDbIdentifier)) != searchList.end())
 471  			return theKeychain;  // theKeychain is already in the searchList.
 472  
 473  		mCommonList.revert(false);
 474  		searchList = mCommonList.searchList();
 475  		if (find(searchList.begin(), searchList.end(), demungeDLDbIdentifier(dLDbIdentifier)) != searchList.end())
 476  			return theKeychain;  // theKeychain is already in the commonList don't add it to the searchList.
 477  
 478  		// If theKeychain doesn't exist don't bother adding it to the search list yet.
 479  		if (!theKeychain->exists())
 480  			return theKeychain;
 481  
 482  		// theKeychain exists and is not in our search list, so add it to the
 483  		// search list.
 484  		mSavedList.revert(true);
 485  		mSavedList.add(demungeDLDbIdentifier(dLDbIdentifier));
 486  		mSavedList.save();
 487  		post = true;
 488  	}
 489  
 490  	if (post)
 491  	{
 492  		// Make sure we are not holding mStorageManagerLock anymore when we
 493  		// post this event.
 494  		KCEventNotifier::PostKeychainEvent(kSecKeychainListChangedEvent);
 495  	}
 496  
 497  	return theKeychain;
 498  }
 499  
 500  // Be notified a Keychain just got created.
 501  void
 502  StorageManager::created(const Keychain &keychain)
 503  {
 504  	StLock<Mutex>_(mKeychainMapMutex);
 505  
 506      DLDbIdentifier dLDbIdentifier = keychain->dlDbIdentifier();
 507  	bool defaultChanged = false;
 508  	bool updateList = shouldAddToSearchList(dLDbIdentifier);
 509  
 510  	if (updateList)
 511   	{
 512  		mSavedList.revert(true);
 513  		// If we don't have a default Keychain yet.  Make the newly created
 514  		// keychain the default.
 515  		if (!mSavedList.defaultDLDbIdentifier())
 516  		{
 517  			mSavedList.defaultDLDbIdentifier(demungeDLDbIdentifier(dLDbIdentifier));
 518  			defaultChanged = true;
 519  		}
 520  
 521  		// Add the keychain to the search list prefs.
 522  		mSavedList.add(demungeDLDbIdentifier(dLDbIdentifier));
 523  		mSavedList.save();
 524  
 525  		// Make sure we are not holding mLock when we post these events.
 526  		KCEventNotifier::PostKeychainEvent(kSecKeychainListChangedEvent);
 527  	}
 528  
 529  	if (defaultChanged)
 530  	{
 531  		KCEventNotifier::PostKeychainEvent(kSecDefaultChangedEvent, dLDbIdentifier);
 532  	}
 533  }
 534  
 535  KCCursor
 536  StorageManager::createCursor(SecItemClass itemClass,
 537  	const SecKeychainAttributeList *attrList)
 538  {
 539  	StLock<Mutex>_(mMutex);
 540  
 541  	KeychainList searchList;
 542  	getSearchList(searchList);
 543  	return KCCursor(searchList, itemClass, attrList);
 544  }
 545  
 546  KCCursor
 547  StorageManager::createCursor(const SecKeychainAttributeList *attrList)
 548  {
 549  	StLock<Mutex>_(mMutex);
 550  
 551  	KeychainList searchList;
 552  	getSearchList(searchList);
 553  	return KCCursor(searchList, attrList);
 554  }
 555  
 556  void
 557  StorageManager::lockAll()
 558  {
 559  	StLock<Mutex>_(mMutex);
 560  
 561      SecurityServer::ClientSession ss(Allocator::standard(), Allocator::standard());
 562      ss.lockAll (false);
 563  }
 564  
 565  Keychain
 566  StorageManager::defaultKeychain()
 567  {
 568  	StLock<Mutex>_(mMutex);
 569  
 570  	Keychain theKeychain;
 571      CFTypeRef ref;
 572  
 573  	{
 574  		mSavedList.revert(false);
 575  		DLDbIdentifier defaultDLDbIdentifier(mSavedList.defaultDLDbIdentifier());
 576  		if (defaultDLDbIdentifier)
 577  		{
 578  			theKeychain = keychain(defaultDLDbIdentifier);
 579              ref = theKeychain->handle(false);
 580  		}
 581  	}
 582  
 583  	if (theKeychain /* && theKeychain->exists() */)
 584  		return theKeychain;
 585  
 586  	MacOSError::throwMe(errSecNoDefaultKeychain);
 587  }
 588  
 589  void
 590  StorageManager::defaultKeychain(const Keychain &keychain)
 591  {
 592  	StLock<Mutex>_(mMutex);
 593  
 594  	// Only set a keychain as the default if we own it and can read/write it,
 595  	// and our uid allows modifying the directory for that preference domain.
 596  	if (!keychainOwnerPermissionsValidForDomain(keychain->name(), mDomain))
 597  		MacOSError::throwMe(errSecWrPerm);
 598  
 599  	DLDbIdentifier oldDefaultId;
 600  	DLDbIdentifier newDefaultId(keychain->dlDbIdentifier());
 601  	{
 602  		oldDefaultId = mSavedList.defaultDLDbIdentifier();
 603  		mSavedList.revert(true);
 604  		mSavedList.defaultDLDbIdentifier(demungeDLDbIdentifier(newDefaultId));
 605  		mSavedList.save();
 606  	}
 607  
 608  	if (!(oldDefaultId == newDefaultId))
 609  	{
 610  		// Make sure we are not holding mLock when we post this event.
 611  		KCEventNotifier::PostKeychainEvent(kSecDefaultChangedEvent, newDefaultId);
 612  	}
 613  }
 614  
 615  Keychain
 616  StorageManager::defaultKeychain(SecPreferencesDomain domain)
 617  {
 618  	StLock<Mutex>_(mMutex);
 619  
 620  	if (domain == kSecPreferencesDomainDynamic)
 621  		MacOSError::throwMe(errSecInvalidPrefsDomain);
 622  
 623  	if (domain == mDomain)
 624  		return defaultKeychain();
 625  	else
 626  	{
 627  		DLDbIdentifier defaultDLDbIdentifier(DLDbListCFPref(domain).defaultDLDbIdentifier());
 628  		if (defaultDLDbIdentifier)
 629  			return keychain(defaultDLDbIdentifier);
 630  
 631  		MacOSError::throwMe(errSecNoDefaultKeychain);
 632  	}
 633  }
 634  
 635  void
 636  StorageManager::defaultKeychain(SecPreferencesDomain domain, const Keychain &keychain)
 637  {
 638  	StLock<Mutex>_(mMutex);
 639  
 640  	if (domain == kSecPreferencesDomainDynamic)
 641  		MacOSError::throwMe(errSecInvalidPrefsDomain);
 642  
 643  	if (domain == mDomain)
 644  		defaultKeychain(keychain);
 645  	else
 646  		DLDbListCFPref(domain).defaultDLDbIdentifier(keychain->dlDbIdentifier());
 647  }
 648  
 649  Keychain
 650  StorageManager::loginKeychain()
 651  {
 652  	StLock<Mutex>_(mMutex);
 653  
 654  	Keychain theKeychain;
 655  	{
 656  		mSavedList.revert(false);
 657  		DLDbIdentifier loginDLDbIdentifier(mSavedList.loginDLDbIdentifier());
 658  		if (loginDLDbIdentifier)
 659  		{
 660  			theKeychain = keychain(loginDLDbIdentifier);
 661  		}
 662  	}
 663  
 664  	if (theKeychain && theKeychain->exists())
 665  		return theKeychain;
 666  
 667  	MacOSError::throwMe(errSecNoSuchKeychain);
 668  }
 669  
 670  DLDbIdentifier
 671  StorageManager::loginKeychainDLDbIdentifer()
 672  {
 673      StLock<Mutex>_(mMutex);
 674      DLDbIdentifier loginDLDbIdentifier(mSavedList.loginDLDbIdentifier());
 675      return mungeDLDbIdentifier(loginDLDbIdentifier, false);
 676  }
 677  
 678  void
 679  StorageManager::loginKeychain(Keychain keychain)
 680  {
 681  	StLock<Mutex>_(mMutex);
 682  
 683  	mSavedList.revert(true);
 684  	mSavedList.loginDLDbIdentifier(demungeDLDbIdentifier(keychain->dlDbIdentifier()));
 685  	mSavedList.save();
 686  }
 687  
 688  size_t
 689  StorageManager::size()
 690  {
 691  	StLock<Mutex>_(mMutex);
 692  
 693      mSavedList.revert(false);
 694  	mCommonList.revert(false);
 695  	return mSavedList.searchList().size() + mCommonList.searchList().size();
 696  }
 697  
 698  Keychain
 699  StorageManager::at(unsigned int ix)
 700  {
 701  	StLock<Mutex>_(mMutex);
 702  
 703  	mSavedList.revert(false);
 704  	DLDbList dLDbList = mSavedList.searchList();
 705  	if (ix < dLDbList.size())
 706  	{
 707  		return keychain(dLDbList[ix]);
 708  	}
 709  	else
 710  	{
 711  		ix -= dLDbList.size();
 712  		mCommonList.revert(false);
 713  		DLDbList commonList = mCommonList.searchList();
 714  		if (ix >= commonList.size())
 715  			MacOSError::throwMe(errSecInvalidKeychain);
 716  
 717  		return keychain(commonList[ix]);
 718  	}
 719  }
 720  
 721  Keychain
 722  StorageManager::operator[](unsigned int ix)
 723  {
 724  	StLock<Mutex>_(mMutex);
 725  
 726      return at(ix);
 727  }
 728  
 729  void StorageManager::rename(Keychain keychain, const char* newName)
 730  {
 731  
 732  	StLock<Mutex>_(mKeychainMapMutex);
 733  
 734      bool changedDefault = false;
 735  	DLDbIdentifier newDLDbIdentifier;
 736  	{
 737  		mSavedList.revert(true);
 738  		DLDbIdentifier defaultId = mSavedList.defaultDLDbIdentifier();
 739  
 740          // Find the keychain object for the given ref
 741          DLDbIdentifier dLDbIdentifier = keychain->dlDbIdentifier();
 742  
 743          if(!keychain->database()->isLocked()) {
 744              // Bring our unlock state with us
 745              DLDbIdentifier dldbi(dLDbIdentifier.ssuid(), newName, dLDbIdentifier.dbLocation());
 746              keychain->database()->transferTo(dldbi);
 747          } else {
 748              keychain->database()->rename(newName);
 749          }
 750  
 751          if (demungeDLDbIdentifier(dLDbIdentifier) == defaultId)
 752              changedDefault=true;
 753  
 754  		newDLDbIdentifier = keychain->dlDbIdentifier();
 755          // Rename the keychain in the search list.
 756          mSavedList.rename(demungeDLDbIdentifier(dLDbIdentifier), demungeDLDbIdentifier(newDLDbIdentifier));
 757  
 758  		// If this was the default keychain change it accordingly
 759  		if (changedDefault)
 760  			mSavedList.defaultDLDbIdentifier(demungeDLDbIdentifier(newDLDbIdentifier));
 761  
 762  		mSavedList.save();
 763  
 764          // If the keychain wasn't in the cache, don't touch the cache.
 765          // Otherwise, update the cache to use its current identifier.
 766          if(keychain->inCache()) {
 767              registerKeychain(keychain);
 768          }
 769      }
 770  
 771  	KCEventNotifier::PostKeychainEvent(kSecKeychainListChangedEvent);
 772  
 773  	if (changedDefault)
 774  		KCEventNotifier::PostKeychainEvent(kSecDefaultChangedEvent,
 775  			newDLDbIdentifier);
 776  }
 777  
 778  void StorageManager::registerKeychain(Keychain& kc) {
 779      registerKeychainImpl(kc.get());
 780  }
 781  
 782  void StorageManager::registerKeychainImpl(KeychainImpl* kcimpl) {
 783      if(!kcimpl) {
 784          return;
 785      }
 786  
 787      {
 788          StLock<Mutex> _(mKeychainMapMutex);
 789  
 790          // First, iterate through the cache to see if this keychain is there. If so, remove it.
 791          forceRemoveFromCache(kcimpl);
 792  
 793          // If we renamed this keychain on top of an existing one, let's drop the old one from the cache.
 794          KeychainMap::iterator it = mKeychainMap.find(kcimpl->dlDbIdentifier());
 795          if (it != mKeychainMap.end())
 796          {
 797              Keychain oldKeychain(it->second);
 798              oldKeychain->inCache(false);
 799              // @@@ Ideally we should invalidate or fault this keychain object.
 800          }
 801  
 802          mKeychainMap.insert(KeychainMap::value_type(kcimpl->dlDbIdentifier(), kcimpl));
 803          kcimpl->inCache(true);
 804      } // drop mKeychainMapMutex
 805  }
 806  
 807  void StorageManager::forceRemoveFromCache(KeychainImpl* inKeychainImpl) {
 808      try {
 809          // Wrap all this in a try-block and ignore all errors - we're trying to clean up these maps
 810          {
 811              StLock<Mutex> _(mKeychainMapMutex);
 812              for(KeychainMap::iterator it = mKeychainMap.begin(); it != mKeychainMap.end(); ) {
 813                  if(it->second == inKeychainImpl) {
 814                      // Increment the iterator, but use its pre-increment value for the erase
 815                      it->second->inCache(false);
 816                      mKeychainMap.erase(it++);
 817                  } else {
 818                      it++;
 819                  }
 820              }
 821          } // drop mKeychainMapMutex
 822      } catch(UnixError ue) {
 823          secnotice("storagemgr", "caught UnixError: %d %s", ue.unixError(), ue.what());
 824      } catch (CssmError cssme) {
 825          const char* errStr = cssmErrorString(cssme.error);
 826          secnotice("storagemgr", "caught CssmError: %d %s", (int) cssme.error, errStr);
 827      } catch (MacOSError mose) {
 828          secnotice("storagemgr", "MacOSError: %d", (int)mose.osStatus());
 829      } catch(...) {
 830          secnotice("storagemgr", "Unknown error");
 831      }
 832  }
 833  
 834  // If you pass NULL as the keychain, you must pass an oldName.
 835  void StorageManager::renameUnique(Keychain keychain, CFStringRef oldName, CFStringRef newName, bool appendDbSuffix)
 836  {
 837  	StLock<Mutex>_(mMutex);
 838  
 839      bool doneCreating = false;
 840      int index = 1;
 841      do
 842      {
 843          char newNameCString[MAXPATHLEN];
 844          if ( CFStringGetCString(newName, newNameCString, MAXPATHLEN, kCFStringEncodingUTF8) )	// make sure it fits in MAXPATHLEN, etc.
 845          {
 846              // Construct the new name...
 847              //
 848              CFMutableStringRef newNameCFStr = NULL;
 849              newNameCFStr = CFStringCreateMutable(NULL, MAXPATHLEN);
 850              if ( newNameCFStr )
 851              {
 852                  CFStringAppendFormat(newNameCFStr, NULL, CFSTR("%s%d"), newNameCString, index);
 853                  if(appendDbSuffix) {
 854                      CFStringAppend(newNameCFStr, CFSTR(kKeychainDbSuffix));
 855                  } else {
 856                      CFStringAppend(newNameCFStr, CFSTR(kKeychainSuffix));	// add .keychain
 857                  }
 858                  char toUseBuff2[MAXPATHLEN];
 859                  if ( CFStringGetCString(newNameCFStr, toUseBuff2, MAXPATHLEN, kCFStringEncodingUTF8) )	// make sure it fits in MAXPATHLEN, etc.
 860                  {
 861                      struct stat filebuf;
 862                      if ( lstat(toUseBuff2, &filebuf) )
 863                      {
 864                          if(keychain) {
 865                              rename(keychain, toUseBuff2);
 866                              KeychainList kcList;
 867                              kcList.push_back(keychain);
 868                              remove(kcList, false);
 869                          } else {
 870                              // We don't have a Keychain object, so force the rename here if possible
 871                              char oldNameCString[MAXPATHLEN];
 872                              if ( CFStringGetCString(oldName, oldNameCString, MAXPATHLEN, kCFStringEncodingUTF8) ) {
 873                                  int result = ::rename(oldNameCString, toUseBuff2);
 874                                  secnotice("KClogin", "keychain force rename to %s: %d %d", newNameCString, result, (result == 0) ? 0 : errno);
 875                                  if(result != 0) {
 876                                      UnixError::throwMe(errno);
 877                                  }
 878                              } else {
 879                                  secnotice("KClogin", "path is wrong, quitting");
 880                              }
 881                          }
 882                          doneCreating = true;
 883                      }
 884                      else
 885                          index++;
 886                  }
 887                  else
 888                      doneCreating = true;	// failure to get c string.
 889                  CFRelease(newNameCFStr);
 890              }
 891              else
 892                  doneCreating = false; // failure to create mutable string.
 893          }
 894          else
 895              doneCreating = false; // failure to get the string (i.e. > MAXPATHLEN?)
 896      }
 897      while (!doneCreating && index != INT_MAX);
 898  }
 899  
 900  #define KEYCHAIN_SYNC_KEY CFSTR("KeychainSyncList")
 901  #define KEYCHAIN_SYNC_DOMAIN CFSTR("com.apple.keychainsync")
 902  
 903  static CFStringRef MakeExpandedPath (const char* path)
 904  {
 905  	std::string name = DLDbListCFPref::ExpandTildesInPath (std::string (path));
 906  	CFStringRef expanded = CFStringCreateWithCString (NULL, name.c_str (), 0);
 907  	return expanded;
 908  }
 909  
 910  void StorageManager::removeKeychainFromSyncList (const DLDbIdentifier &id)
 911  {
 912  	StLock<Mutex>_(mMutex);
 913  
 914  	// make a CFString of our identifier
 915  	const char* idname = id.dbName ();
 916  	if (idname == NULL)
 917  	{
 918  		return;
 919  	}
 920  
 921  	CFRef<CFStringRef> idString = MakeExpandedPath (idname);
 922  
 923  	// check and see if this keychain is in the keychain syncing list
 924  	CFArrayRef value =
 925  		(CFArrayRef) CFPreferencesCopyValue (KEYCHAIN_SYNC_KEY,
 926  											 KEYCHAIN_SYNC_DOMAIN,
 927  											 kCFPreferencesCurrentUser,
 928  											 kCFPreferencesAnyHost);
 929  	if (value == NULL)
 930  	{
 931  		return;
 932  	}
 933  
 934  	// make a mutable copy of the dictionary
 935  	CFRef<CFMutableArrayRef> mtValue = CFArrayCreateMutableCopy (NULL, 0, value);
 936  	CFRelease (value);
 937  
 938  	// walk the array, looking for the value
 939  	CFIndex i;
 940  	CFIndex limit = CFArrayGetCount (mtValue.get());
 941  	bool found = false;
 942  
 943  	for (i = 0; i < limit; ++i)
 944  	{
 945  		CFDictionaryRef idx = (CFDictionaryRef) CFArrayGetValueAtIndex (mtValue.get(), i);
 946  		CFStringRef v = (CFStringRef) CFDictionaryGetValue (idx, CFSTR("DbName"));
 947  		if (v == NULL)
 948  		{
 949  			return; // something is really wrong if this is taken
 950  		}
 951  
 952          char* stringBuffer = NULL;
 953          const char* pathString = CFStringGetCStringPtr(v, 0);
 954          if (pathString == 0)
 955          {
 956              CFIndex maxLen = CFStringGetMaximumSizeForEncoding(CFStringGetLength(v), kCFStringEncodingUTF8) + 1;
 957              stringBuffer = (char*) malloc(maxLen);
 958              CFStringGetCString(v, stringBuffer, maxLen, kCFStringEncodingUTF8);
 959              pathString = stringBuffer;
 960          }
 961          
 962  		CFStringRef vExpanded = MakeExpandedPath(pathString);
 963  		CFComparisonResult result = CFStringCompare (vExpanded, idString.get(), 0);
 964          if (stringBuffer != NULL)
 965          {
 966              free(stringBuffer);
 967          }
 968          
 969  		CFRelease (vExpanded);
 970  
 971  		if (result == 0)
 972  		{
 973  			CFArrayRemoveValueAtIndex (mtValue.get(), i);
 974  			found = true;
 975  			break;
 976  		}
 977  	}
 978  
 979  	if (found)
 980  	{
 981  #ifndef NDEBUG
 982  		CFShow (mtValue.get());
 983  #endif
 984  
 985  		CFPreferencesSetValue (KEYCHAIN_SYNC_KEY,
 986  							   mtValue,
 987  							   KEYCHAIN_SYNC_DOMAIN,
 988  							   kCFPreferencesCurrentUser,
 989  							   kCFPreferencesAnyHost);
 990  		CFPreferencesSynchronize (KEYCHAIN_SYNC_DOMAIN, kCFPreferencesCurrentUser, kCFPreferencesAnyHost);
 991  	}
 992  }
 993  
 994  void StorageManager::remove(const KeychainList &kcsToRemove, bool deleteDb)
 995  {
 996  	StLock<Mutex>_(mMutex);
 997  
 998  	bool unsetDefault = false;
 999  	bool updateList = (!isAppSandboxed());
1000  
1001  	if (updateList)
1002  	{
1003  		mSavedList.revert(true);
1004  		DLDbIdentifier defaultId = mSavedList.defaultDLDbIdentifier();
1005  		for (KeychainList::const_iterator ix = kcsToRemove.begin();
1006  			ix != kcsToRemove.end(); ++ix)
1007  		{
1008  			// Find the keychain object for the given ref
1009  			Keychain theKeychain = *ix;
1010  			DLDbIdentifier dLDbIdentifier = theKeychain->dlDbIdentifier();
1011  
1012  			// Remove it from the saved list
1013  			mSavedList.remove(demungeDLDbIdentifier(dLDbIdentifier));
1014              if (demungeDLDbIdentifier(dLDbIdentifier) == defaultId) {
1015  				unsetDefault=true;
1016              }
1017  
1018  			if (deleteDb)
1019  			{
1020  				removeKeychainFromSyncList (dLDbIdentifier);
1021  
1022  				// Now remove it from the cache
1023  				removeKeychain(dLDbIdentifier, theKeychain.get());
1024  			}
1025  		}
1026  
1027  		if (unsetDefault)
1028              mSavedList.defaultDLDbIdentifier(DLDbIdentifier());
1029  
1030  		mSavedList.save();
1031  	}
1032  
1033  	if (deleteDb)
1034  	{
1035  		// Delete the actual databases without holding any locks.
1036  		for (KeychainList::const_iterator ix = kcsToRemove.begin();
1037  			ix != kcsToRemove.end(); ++ix)
1038  		{
1039  			(*ix)->database()->deleteDb();
1040  		}
1041  	}
1042  
1043  	if (updateList) {
1044  		// Make sure we are not holding mLock when we post these events.
1045  		KCEventNotifier::PostKeychainEvent(kSecKeychainListChangedEvent);
1046  	}
1047  
1048  	if (unsetDefault)
1049  		KCEventNotifier::PostKeychainEvent(kSecDefaultChangedEvent);
1050  }
1051  
1052  void
1053  StorageManager::getSearchList(KeychainList &keychainList)
1054  {
1055  	// hold the global lock since we make keychain objects in this function
1056  
1057  	// to do:  each of the items in this list must be retained, otherwise mayhem will occur
1058  	StLock<Mutex>_(mMutex);
1059  
1060  	if (gServerMode) {
1061  		keychainList.clear();
1062  		return;
1063  	}
1064  
1065      mSavedList.revert(false);
1066  	mCommonList.revert(false);
1067  
1068  	// Merge mSavedList, mDynamicList and mCommonList
1069  	DLDbList dLDbList = mSavedList.searchList();
1070  	DLDbList dynamicList = mDynamicList.searchList();
1071  	DLDbList commonList = mCommonList.searchList();
1072  	KeychainList result;
1073  	result.reserve(dLDbList.size() + dynamicList.size() + commonList.size());
1074  
1075  	{
1076  		for (DLDbList::const_iterator it = dynamicList.begin();
1077  			it != dynamicList.end(); ++it)
1078  		{
1079  			Keychain k = keychain(*it);
1080  			result.push_back(k);
1081  		}
1082  
1083  		for (DLDbList::const_iterator it = dLDbList.begin();
1084  			it != dLDbList.end(); ++it)
1085  		{
1086  			Keychain k = keychain(*it);
1087  			result.push_back(k);
1088  		}
1089  
1090  		for (DLDbList::const_iterator it = commonList.begin();
1091  			it != commonList.end(); ++it)
1092  		{
1093  			Keychain k = keychain(*it);
1094  			result.push_back(k);
1095  		}
1096  	}
1097  
1098  	keychainList.swap(result);
1099  }
1100  
1101  void
1102  StorageManager::setSearchList(const KeychainList &keychainList)
1103  {
1104  	StLock<Mutex>_(mMutex);
1105  
1106  	DLDbList searchList, oldSearchList(mSavedList.searchList());
1107  	for (KeychainList::const_iterator it = keychainList.begin(); it != keychainList.end(); ++it)
1108  	{
1109          DLDbIdentifier dldbi = demungeDLDbIdentifier((*it)->dlDbIdentifier());
1110  
1111          // If this keychain is not in the common or dynamic lists, add it to the new search list
1112          DLDbList commonList = mCommonList.searchList();
1113          bool found = false;
1114          for(DLDbList::const_iterator jt = commonList.begin(); jt != commonList.end(); ++jt) {
1115              if((*jt) == dldbi) {
1116                  found = true;
1117              }
1118          }
1119  
1120          DLDbList dynamicList = mDynamicList.searchList();
1121          for(DLDbList::const_iterator jt = dynamicList.begin(); jt != dynamicList.end(); ++jt) {
1122              if((*jt) == dldbi) {
1123                  found = true;
1124              }
1125          }
1126  
1127          if(found) {
1128              continue;
1129          }
1130  
1131  		searchList.push_back(dldbi);
1132  	}
1133  
1134  	{
1135  		// Set the current searchlist to be what was passed in, the old list will be freed
1136  		// upon exit of this stackframe.
1137  		mSavedList.revert(true);
1138  		mSavedList.searchList(searchList);
1139      	mSavedList.save();
1140  	}
1141  
1142  	if (!(oldSearchList == searchList))
1143  	{
1144  		// Make sure we are not holding mLock when we post this event.
1145  		KCEventNotifier::PostKeychainEvent(kSecKeychainListChangedEvent);
1146  	}
1147  }
1148  
1149  void
1150  StorageManager::getSearchList(SecPreferencesDomain domain, KeychainList &keychainList)
1151  {
1152  	StLock<Mutex>_(mMutex);
1153  
1154  	if (gServerMode) {
1155  		keychainList.clear();
1156  		return;
1157  	}
1158  
1159  	if (domain == kSecPreferencesDomainDynamic)
1160  	{
1161  		convertList(keychainList, mDynamicList.searchList());
1162  	}
1163  	else if (domain == mDomain)
1164  	{
1165  		mSavedList.revert(false);
1166  		convertList(keychainList, mSavedList.searchList());
1167  	}
1168  	else
1169  	{
1170  		convertList(keychainList, DLDbListCFPref(domain).searchList());
1171  	}
1172  }
1173  
1174  void StorageManager::forceUserSearchListReread()
1175  {
1176  	mSavedList.forceUserSearchListReread();
1177  }
1178  
1179  void
1180  StorageManager::setSearchList(SecPreferencesDomain domain, const KeychainList &keychainList)
1181  {
1182  	StLock<Mutex>_(mMutex);
1183  
1184  	if (domain == kSecPreferencesDomainDynamic)
1185  		MacOSError::throwMe(errSecInvalidPrefsDomain);
1186  
1187  	DLDbList searchList;
1188  	convertList(searchList, keychainList);
1189  
1190  	if (domain == mDomain)
1191  	{
1192  		DLDbList oldSearchList(mSavedList.searchList());
1193  		{
1194  			// Set the current searchlist to be what was passed in, the old list will be freed
1195  			// upon exit of this stackframe.
1196  			mSavedList.revert(true);
1197  			mSavedList.searchList(searchList);
1198  			mSavedList.save();
1199  		}
1200  
1201  		if (!(oldSearchList == searchList))
1202  		{
1203  			KCEventNotifier::PostKeychainEvent(kSecKeychainListChangedEvent);
1204  		}
1205  	}
1206  	else
1207  	{
1208  		DLDbListCFPref(domain).searchList(searchList);
1209  	}
1210  }
1211  
1212  void
1213  StorageManager::domain(SecPreferencesDomain domain)
1214  {
1215  	StLock<Mutex>_(mMutex);
1216  
1217  	if (domain == kSecPreferencesDomainDynamic)
1218  		MacOSError::throwMe(errSecInvalidPrefsDomain);
1219  
1220  	if (domain == mDomain)
1221  		return;	// no change
1222  
1223  #if !defined(NDEBUG)
1224  	switch (domain)
1225  	{
1226  	case kSecPreferencesDomainSystem:
1227  		secnotice("storagemgr", "switching to system domain"); break;
1228  	case kSecPreferencesDomainUser:
1229  		secnotice("storagemgr", "switching to user domain (uid %d)", getuid()); break;
1230  	default:
1231  		secnotice("storagemgr", "switching to weird prefs domain %d", domain); break;
1232  	}
1233  #endif
1234  
1235  	mDomain = domain;
1236  	mSavedList.set(domain);
1237  }
1238  
1239  void
1240  StorageManager::optionalSearchList(CFTypeRef keychainOrArray, KeychainList &keychainList)
1241  {
1242  	StLock<Mutex>_(mMutex);
1243  
1244  	if (!keychainOrArray)
1245  		getSearchList(keychainList);
1246  	else
1247  	{
1248  		CFTypeID typeID = CFGetTypeID(keychainOrArray);
1249  		if (typeID == CFArrayGetTypeID())
1250  			convertToKeychainList(CFArrayRef(keychainOrArray), keychainList);
1251  		else if (typeID == gTypes().KeychainImpl.typeID)
1252  			keychainList.push_back(KeychainImpl::required(SecKeychainRef(keychainOrArray)));
1253  		else
1254  			MacOSError::throwMe(errSecParam);
1255  	}
1256  }
1257  
1258  // static methods.
1259  void
1260  StorageManager::convertToKeychainList(CFArrayRef keychainArray, KeychainList &keychainList)
1261  {
1262  	CFIndex count = CFArrayGetCount(keychainArray);
1263  	if (!(count > 0))
1264  		return;
1265  
1266  	KeychainList keychains(count);
1267  	for (CFIndex ix = 0; ix < count; ++ix)
1268  	{
1269  		keychains[ix] = KeychainImpl::required(SecKeychainRef(CFArrayGetValueAtIndex(keychainArray, ix)));
1270  	}
1271  
1272  	keychainList.swap(keychains);
1273  }
1274  
1275  CFArrayRef
1276  StorageManager::convertFromKeychainList(const KeychainList &keychainList)
1277  {
1278  	CFRef<CFMutableArrayRef> keychainArray(CFArrayCreateMutable(NULL, keychainList.size(), &kCFTypeArrayCallBacks));
1279  
1280  	for (KeychainList::const_iterator ix = keychainList.begin(); ix != keychainList.end(); ++ix)
1281  	{
1282  		SecKeychainRef keychainRef = (*ix)->handle();
1283  		CFArrayAppendValue(keychainArray, keychainRef);
1284  		CFRelease(keychainRef);
1285  	}
1286  
1287  	// Counter the CFRelease that CFRef<> is about to do when keychainArray goes out of scope.
1288  	CFRetain(keychainArray);
1289  	return keychainArray;
1290  }
1291  
1292  void StorageManager::convertList(DLDbList &ids, const KeychainList &kcs)
1293  {
1294  	DLDbList result;
1295  	result.reserve(kcs.size());
1296  	for (KeychainList::const_iterator ix = kcs.begin(); ix != kcs.end(); ++ix)
1297  	{
1298  		result.push_back(demungeDLDbIdentifier((*ix)->dlDbIdentifier()));
1299  	}
1300  	ids.swap(result);
1301  }
1302  
1303  void StorageManager::convertList(KeychainList &kcs, const DLDbList &ids)
1304  {
1305  	StLock<Mutex>_(mMutex);
1306  
1307  	KeychainList result;
1308      result.reserve(ids.size());
1309  	{
1310  		for (DLDbList::const_iterator ix = ids.begin(); ix != ids.end(); ++ix)
1311  			result.push_back(keychain(*ix));
1312  	}
1313      kcs.swap(result);
1314  }
1315  
1316  #pragma mark ____ Login Functions ____
1317  
1318  void StorageManager::login(AuthorizationRef authRef, UInt32 nameLength, const char* name, bool isReset)
1319  {
1320  	StLock<Mutex>_(mMutex);
1321  
1322      AuthorizationItemSet* info = NULL;
1323      OSStatus result = AuthorizationCopyInfo(authRef, NULL, &info);	// get the results of the copy rights call.
1324      Boolean created = false;
1325      if ( result == errSecSuccess && info->count )
1326      {
1327          // Grab the password from the auth context (info) and create the keychain...
1328          //
1329          AuthorizationItem* currItem = info->items;
1330          for (UInt32 index = 1; index <= info->count; index++) //@@@plugin bug won't return a specific context.
1331          {
1332              if (strcmp(currItem->name, kAuthorizationEnvironmentPassword) == 0)
1333              {
1334                  // creates the login keychain with the specified password
1335                  try
1336                  {
1337                      login(nameLength, name, (UInt32)currItem->valueLength, currItem->value, isReset);
1338                      created = true;
1339                  }
1340                  catch(...)
1341                  {
1342                  }
1343                  break;
1344              }
1345              currItem++;
1346          }
1347      }
1348      if ( info )
1349          AuthorizationFreeItemSet(info);
1350  
1351      if ( !created )
1352          MacOSError::throwMe(errAuthorizationInternal);
1353  }
1354  
1355  void StorageManager::login(ConstStringPtr name, ConstStringPtr password)
1356  {
1357  	StLock<Mutex>_(mMutex);
1358  
1359      if ( name == NULL || password == NULL ) {
1360          MacOSError::throwMe(errSecParam);
1361      }
1362  
1363      login(name[0], name + 1, password[0], password + 1, false);
1364  }
1365  
1366  void StorageManager::login(UInt32 nameLength, const void *name,
1367  	UInt32 passwordLength, const void *password, bool isReset)
1368  {
1369  	if (passwordLength != 0 && password == NULL)
1370  	{
1371  		secnotice("KCLogin", "StorageManager::login: invalid argument (NULL password)");
1372  		MacOSError::throwMe(errSecParam);
1373  	}
1374  
1375  	DLDbIdentifier loginDLDbIdentifier;
1376  	{
1377  		mSavedList.revert(true);
1378  		loginDLDbIdentifier = mSavedList.loginDLDbIdentifier();
1379  	}
1380  
1381  	secnotice("KCLogin", "StorageManager::login: loginDLDbIdentifier is %s", (loginDLDbIdentifier) ? loginDLDbIdentifier.dbName() : "<NULL>");
1382  	if (!loginDLDbIdentifier)
1383  		MacOSError::throwMe(errSecNoSuchKeychain);
1384  
1385  
1386      //***************************************************************
1387      // gather keychain information
1388      //***************************************************************
1389  
1390      // user name
1391      int uid = geteuid();
1392      struct passwd *pw = getpwuid(uid);
1393      if (pw == NULL) {
1394          secnotice("KCLogin", "StorageManager::login: invalid argument (NULL uid)");
1395          MacOSError::throwMe(errSecParam);
1396      }
1397      std::string userName = pw->pw_name;
1398  
1399      // make keychain path strings
1400      std::string keychainPath = DLDbListCFPref::ExpandTildesInPath(kLoginKeychainPathPrefix);
1401      std::string shortnameKeychain = keychainPath + userName;
1402      std::string shortnameDotKeychain = shortnameKeychain + ".keychain";
1403      std::string loginDotKeychain = keychainPath + "login.keychain";
1404      std::string loginRenamed1Keychain = keychainPath + "login_renamed1.keychain";
1405      std::string loginKeychainDb =  keychainPath + "login.keychain-db";
1406  
1407      // check for existence of keychain files
1408      bool shortnameKeychainExists = false;
1409      bool shortnameDotKeychainExists = false;
1410      bool loginKeychainExists = false;
1411      bool loginRenamed1KeychainExists = false;
1412      bool loginKeychainDbExists = false;
1413      {
1414          struct stat st;
1415          int stat_result;
1416          stat_result = ::stat(shortnameKeychain.c_str(), &st);
1417          shortnameKeychainExists = (stat_result == 0);
1418          stat_result = ::stat(shortnameDotKeychain.c_str(), &st);
1419          shortnameDotKeychainExists = (stat_result == 0);
1420          stat_result = ::stat(loginDotKeychain.c_str(), &st);
1421          loginKeychainExists = (stat_result == 0);
1422          stat_result = ::stat(loginRenamed1Keychain.c_str(), &st);
1423          loginRenamed1KeychainExists = (stat_result == 0);
1424          stat_result = ::stat(loginKeychainDb.c_str(), &st);
1425          loginKeychainDbExists = (stat_result == 0);
1426      }
1427  
1428      // login.keychain-db is considered to be the same as login.keychain.
1429      // Our transparent keychain promotion on open will handle opening the right version of this file.
1430      loginKeychainExists |= loginKeychainDbExists;
1431  
1432      bool loginUnlocked = false;
1433  
1434      // make the keychain identifiers
1435      CSSM_VERSION version = {0, 0};
1436      DLDbIdentifier shortnameDLDbIdentifier = DLDbListCFPref::makeDLDbIdentifier(gGuidAppleCSPDL, version, 0, CSSM_SERVICE_CSP | CSSM_SERVICE_DL, shortnameKeychain.c_str(), NULL);
1437      DLDbIdentifier shortnameDotDLDbIdentifier = DLDbListCFPref::makeDLDbIdentifier(gGuidAppleCSPDL, version, 0, CSSM_SERVICE_CSP | CSSM_SERVICE_DL, shortnameDotKeychain.c_str(), NULL);
1438      DLDbIdentifier loginRenamed1DLDbIdentifier = DLDbListCFPref::makeDLDbIdentifier(gGuidAppleCSPDL, version, 0, CSSM_SERVICE_CSP | CSSM_SERVICE_DL, loginRenamed1Keychain.c_str(), NULL);
1439  
1440      //***************************************************************
1441      // make file renaming changes first
1442      //***************************************************************
1443  
1444      // if "~/Library/Keychains/shortname" exists, we need to migrate it forward;
1445      // either to login.keychain if there isn't already one, otherwise to shortname.keychain
1446      if (shortnameKeychainExists) {
1447          int rename_stat = 0;
1448          if (loginKeychainExists) {
1449              struct stat st;
1450              int tmp_result = ::stat(loginDotKeychain.c_str(), &st);
1451              if (tmp_result == 0) {
1452                  if (st.st_size <= kEmptyKeychainSizeInBytes) {
1453                      tmp_result = ::unlink(loginDotKeychain.c_str());
1454                      rename_stat = ::rename(shortnameKeychain.c_str(), loginDotKeychain.c_str());
1455                      shortnameKeychainExists = (rename_stat != 0);
1456                  }
1457              }
1458          }
1459          if (shortnameKeychainExists) {
1460              if (loginKeychainExists && !shortnameDotKeychainExists) {
1461                  rename_stat = ::rename(shortnameKeychain.c_str(), shortnameDotKeychain.c_str());
1462                  shortnameDotKeychainExists = (rename_stat == 0);
1463              } else if (!loginKeychainExists) {
1464                  rename_stat = ::rename(shortnameKeychain.c_str(), loginDotKeychain.c_str());
1465                  loginKeychainExists = (rename_stat == 0);
1466              } else {
1467                  // we have all 3 keychains: login.keychain, shortname, and shortname.keychain.
1468                  // on Leopard we never want a shortname keychain, so we must move it aside.
1469                  char pathbuf[MAXPATHLEN];
1470                  std::string shortnameRenamedXXXKeychain = keychainPath;
1471                  shortnameRenamedXXXKeychain += userName;
1472                  shortnameRenamedXXXKeychain += "_renamed_XXX.keychain";
1473                  ::strlcpy(pathbuf, shortnameRenamedXXXKeychain.c_str(), sizeof(pathbuf));
1474                  ::mkstemps(pathbuf, 9); // 9 == strlen(".keychain")
1475                  rename_stat = ::rename(shortnameKeychain.c_str(), pathbuf);
1476                  shortnameKeychainExists = (rename_stat != 0);
1477              }
1478          }
1479          if (rename_stat != 0) {
1480              MacOSError::throwMe(errno);
1481          }
1482      }
1483  
1484      //***************************************************************
1485      // handle special case where user previously reset the keychain
1486      //***************************************************************
1487      // Since 9A581, we have changed the definition of kKeychainRenamedSuffix from "_renamed" to "_renamed_".
1488      // Therefore, if "login_renamed1.keychain" exists and there is no plist, the user may have run into a
1489      // prior upgrade issue and clicked Reset. If we can successfully unlock login_renamed1.keychain with the
1490      // supplied password, then we will attempt to rename it to login.keychain if that file is empty, or with
1491      // "shortname.keychain" if it is not.
1492  
1493      if (loginRenamed1KeychainExists && (!loginKeychainExists ||
1494          (mSavedList.searchList().size() == 1 && mSavedList.member(demungeDLDbIdentifier(loginDLDbIdentifier))) )) {
1495          try
1496          {
1497              Keychain loginRenamed1KC(keychain(loginRenamed1DLDbIdentifier));
1498              secnotice("KCLogin", "Attempting to unlock renamed KC \"%s\"",
1499                        (loginRenamed1KC) ? loginRenamed1KC->name() : "<NULL>");
1500              loginRenamed1KC->unlock(CssmData(const_cast<void *>(password), passwordLength));
1501              // if we get here, we unlocked it
1502              if (loginKeychainExists) {
1503                  struct stat st;
1504                  int tmp_result = ::stat(loginDotKeychain.c_str(), &st);
1505                  if (tmp_result == 0) {
1506                      if (st.st_size <= kEmptyKeychainSizeInBytes) {
1507                          tmp_result = ::unlink(loginDotKeychain.c_str());
1508                          tmp_result = ::rename(loginRenamed1Keychain.c_str(), loginDotKeychain.c_str());
1509                      } else if (!shortnameDotKeychainExists) {
1510                          tmp_result = ::rename(loginRenamed1Keychain.c_str(), shortnameDotKeychain.c_str());
1511                          shortnameDotKeychainExists = (tmp_result == 0);
1512                      } else {
1513                          throw 1;   // can't do anything with it except move it out of the way
1514                      }
1515                  }
1516              } else {
1517                  int tmp_result = ::rename(loginRenamed1Keychain.c_str(), loginDotKeychain.c_str());
1518                  loginKeychainExists = (tmp_result == 0);
1519              }
1520          }
1521          catch(...)
1522          {
1523              // we failed to unlock the login_renamed1.keychain file with the login password.
1524              // move it aside so we don't try to deal with it again.
1525              char pathbuf[MAXPATHLEN];
1526              std::string loginRenamedXXXKeychain = keychainPath;
1527              loginRenamedXXXKeychain += "login_renamed_XXX.keychain";
1528              ::strlcpy(pathbuf, loginRenamedXXXKeychain.c_str(), sizeof(pathbuf));
1529              ::mkstemps(pathbuf, 9); // 9 == strlen(".keychain")
1530              ::rename(loginRenamed1Keychain.c_str(), pathbuf);
1531          }
1532      }
1533  
1534  	// is it token login?
1535  	CFRef<CFDictionaryRef> tokenLoginContext;
1536  	CFRef<CFStringRef> smartCardPassword;
1537  	OSStatus tokenContextStatus = TokenLoginGetContext(password, passwordLength, tokenLoginContext.take());
1538  	// if login.keychain does not exist at this point, create it
1539  	if (!loginKeychainExists || (isReset && !loginKeychainDbExists)) {
1540  		// when we creating new KC and user is logged using token (i.e. smart card), we have to get
1541  		// the password for that account first
1542  		if (tokenContextStatus == errSecSuccess) {
1543  			secnotice("KCLogin", "Going to create login keychain for sc login");
1544  			AuthorizationRef authRef;
1545  			OSStatus status = AuthorizationCreate(NULL, NULL, 0, &authRef);
1546  			if (status == errSecSuccess) {
1547  				AuthorizationItem right = { "com.apple.builtin.sc-kc-new-passphrase", 0, NULL, 0 };
1548  				AuthorizationItemSet rightSet = { 1, &right };
1549  
1550  				uint32_t reason, tries;
1551  				reason = 0;
1552  				tries = 0;
1553  				AuthorizationItem envRights[] = {
1554  					{ AGENT_HINT_RETRY_REASON, sizeof(reason), &reason, 0 },
1555  					{ AGENT_HINT_TRIES, sizeof(tries), &tries, 0 }};
1556  
1557  				AuthorizationItemSet envSet = { sizeof(envRights) / sizeof(*envRights), envRights };
1558  				status = AuthorizationCopyRights(authRef, &rightSet, &envSet, kAuthorizationFlagDefaults|kAuthorizationFlagInteractionAllowed|kAuthorizationFlagExtendRights, NULL);
1559  				if (status == errSecSuccess) {
1560  					AuthorizationItemSet *returnedInfo;
1561  					status = AuthorizationCopyInfo(authRef, NULL, &returnedInfo);
1562  					if (status == errSecSuccess) {
1563  						if (returnedInfo && (returnedInfo->count > 0)) {
1564  							for (uint32_t index = 0; index < returnedInfo->count; index++) {
1565  								AuthorizationItem &item = returnedInfo->items[index];
1566  								if (!strcmp(AGENT_PASSWORD, item.name)) {
1567  									CFIndex len = item.valueLength;
1568  									if (len) {
1569  										secnotice("KCLogin", "User entered pwd");
1570  										smartCardPassword = CFStringCreateWithBytes(SecCFAllocatorZeroize(), (UInt8 *)item.value, (CFIndex)len, kCFStringEncodingUTF8, TRUE);
1571  										memset(item.value, 0, len);
1572  									}
1573  								}
1574  							}
1575  						}
1576  					}
1577                      if(returnedInfo) {
1578                          AuthorizationFreeItemSet(returnedInfo);
1579                      }
1580  				}
1581  				AuthorizationFree(authRef, 0);
1582  			}
1583  		}
1584  
1585          // but don't add it to the search list yet; we'll do that later
1586          Keychain theKeychain = makeKeychain(loginDLDbIdentifier, false, true);
1587  		secnotice("KCLogin", "Creating login keychain %s", (loginDLDbIdentifier) ? loginDLDbIdentifier.dbName() : "<NULL>");
1588  		if (tokenContextStatus == errSecSuccess) {
1589  			if (smartCardPassword.get()) {
1590  				CFIndex length = CFStringGetLength(smartCardPassword);
1591  				CFIndex maxSize = CFStringGetMaximumSizeForEncoding(length, kCFStringEncodingUTF8) + 1;
1592  				char *buffer = (char *)malloc(maxSize);
1593  				if (CFStringGetCString(smartCardPassword, buffer, maxSize, kCFStringEncodingUTF8)) {
1594  					secnotice("KCLogin", "Keychain is created using password provided by sc user");
1595  					theKeychain->create((UInt32)strlen(buffer), buffer);
1596  					memset(buffer, 0, maxSize);
1597  				} else {
1598  					secnotice("KCLogin", "Conversion failed");
1599  					MacOSError::throwMe(errSecNotAvailable);
1600  				}
1601  			} else {
1602  				secnotice("KCLogin", "User did not provide kc password");
1603  				MacOSError::throwMe(errSecNotAvailable);
1604  			}
1605  		} else {
1606  			theKeychain->create(passwordLength, password);
1607  		}
1608          secnotice("KCLogin", "Login keychain created successfully");
1609          loginKeychainExists = true;
1610          // Set the prefs for this new login keychain.
1611          loginKeychain(theKeychain);
1612          // Login Keychain does not lock on sleep nor lock after timeout by default.
1613          theKeychain->setSettings(INT_MAX, false);
1614          loginUnlocked = true;
1615          mSavedList.revert(true);
1616      }
1617  
1618      //***************************************************************
1619      // make plist changes after files have been renamed or created
1620      //***************************************************************
1621  
1622      // if the shortname keychain exists in the search list, either rename or remove the entry
1623      if (mSavedList.member(demungeDLDbIdentifier(shortnameDLDbIdentifier))) {
1624          if (shortnameDotKeychainExists && !mSavedList.member(demungeDLDbIdentifier(shortnameDotDLDbIdentifier))) {
1625              // change shortname to shortname.keychain (login.keychain will be added later if not present)
1626              secnotice("KCLogin", "Renaming %s to %s in keychain search list",
1627                      (shortnameDLDbIdentifier) ? shortnameDLDbIdentifier.dbName() : "<NULL>",
1628                      (shortnameDotDLDbIdentifier) ? shortnameDotDLDbIdentifier.dbName() : "<NULL>");
1629              mSavedList.rename(demungeDLDbIdentifier(shortnameDLDbIdentifier),
1630                                demungeDLDbIdentifier(shortnameDotDLDbIdentifier));
1631          } else if (!mSavedList.member(demungeDLDbIdentifier(loginDLDbIdentifier))) {
1632              // change shortname to login.keychain
1633              secnotice("KCLogin", "Renaming %s to %s in keychain search list",
1634                      (shortnameDLDbIdentifier) ? shortnameDLDbIdentifier.dbName() : "<NULL>",
1635                      (loginDLDbIdentifier) ? loginDLDbIdentifier.dbName() : "<NULL>");
1636              mSavedList.rename(demungeDLDbIdentifier(shortnameDLDbIdentifier),
1637                                demungeDLDbIdentifier(loginDLDbIdentifier));
1638          } else {
1639              // already have login.keychain in list, and renaming to shortname.keychain isn't an option,
1640              // so just remove the entry
1641              secnotice("KCLogin", "Removing %s from keychain search list", (shortnameDLDbIdentifier) ? shortnameDLDbIdentifier.dbName() : "<NULL>");
1642              mSavedList.remove(demungeDLDbIdentifier(shortnameDLDbIdentifier));
1643          }
1644  
1645          // note: save() will cause the plist to be unlinked if the only remaining entry is for login.keychain
1646          mSavedList.save();
1647          mSavedList.revert(true);
1648      }
1649  
1650      // make sure that login.keychain is in the search list
1651      if (!mSavedList.member(demungeDLDbIdentifier(loginDLDbIdentifier))) {
1652      	secnotice("KCLogin", "Adding %s to keychain search list", (loginDLDbIdentifier) ? loginDLDbIdentifier.dbName() : "<NULL>");
1653          mSavedList.add(demungeDLDbIdentifier(loginDLDbIdentifier));
1654          mSavedList.save();
1655          mSavedList.revert(true);
1656      }
1657  
1658      // if we have a shortname.keychain, always include it in the plist (after login.keychain)
1659      if (shortnameDotKeychainExists && !mSavedList.member(demungeDLDbIdentifier(shortnameDotDLDbIdentifier))) {
1660          mSavedList.add(demungeDLDbIdentifier(shortnameDotDLDbIdentifier));
1661          mSavedList.save();
1662          mSavedList.revert(true);
1663      }
1664  
1665      // make sure that the default keychain is in the search list; if not, reset the default to login.keychain
1666  	if (!mSavedList.member(mSavedList.defaultDLDbIdentifier())) {
1667      	secnotice("KCLogin", "Changing default keychain to %s", (loginDLDbIdentifier) ? loginDLDbIdentifier.dbName() : "<NULL>");
1668          mSavedList.defaultDLDbIdentifier(demungeDLDbIdentifier(loginDLDbIdentifier));
1669          mSavedList.save();
1670          mSavedList.revert(true);
1671  	}
1672  
1673      //***************************************************************
1674      // auto-unlock the login keychain(s)
1675      //***************************************************************
1676      // all our preflight fixups are finally done, so we can now attempt to unlock the login keychain
1677  
1678      OSStatus loginResult = errSecSuccess;
1679  	if (!loginUnlocked) {
1680          try
1681          {
1682              Keychain theKeychain(keychain(loginDLDbIdentifier));
1683              secnotice("KCLogin", "Attempting to unlock login keychain \"%s\"",
1684                  (theKeychain) ? theKeychain->name() : "<NULL>");
1685              theKeychain->unlock(CssmData(const_cast<void *>(password), passwordLength));
1686              loginUnlocked = true;
1687          }
1688          catch(const CssmError &e)
1689          {
1690              loginResult = e.osStatus(); // save this result
1691          }
1692      }
1693  
1694  	if (!loginUnlocked || tokenContextStatus == errSecSuccess) {
1695  		Keychain theKeychain(keychain(loginDLDbIdentifier));
1696  		bool tokenLoginDataUpdated = false;
1697  
1698  		for (UInt32 i = 0; i < 2; i++) {
1699  			loginResult = errSecSuccess;
1700  
1701  			CFRef<CFDictionaryRef> tokenLoginData;
1702  			if (tokenLoginContext) {
1703  				OSStatus status = TokenLoginGetLoginData(tokenLoginContext, tokenLoginData.take());
1704  				if (status != errSecSuccess) {
1705  					if (tokenLoginDataUpdated) {
1706  						loginResult = status;
1707  						break;
1708  					}
1709  					// updating unlock key fails if it is not token login
1710  					secnotice("KCLogin", "Error %d, reconstructing unlock data", (int)status);
1711  					status = TokenLoginUpdateUnlockData(tokenLoginContext, smartCardPassword);
1712  					if (status == errSecSuccess) {
1713  						loginResult = TokenLoginGetLoginData(tokenLoginContext, tokenLoginData.take());
1714  						if (loginResult != errSecSuccess) {
1715  							break;
1716  						}
1717  						tokenLoginDataUpdated = true;
1718  					}
1719  				}
1720  			}
1721  
1722              try {
1723  				// first try to unlock login keychain because if this fails, token keychain unlock fails as well
1724  				if (tokenLoginData) {
1725  					secnotice("KCLogin", "Going to unlock keybag using scBlob");
1726  					OSStatus status = TokenLoginUnlockKeybag(tokenLoginContext, tokenLoginData);
1727  					secnotice("KCLogin", "Keybag unlock result %d", (int)status);
1728  					if (status)
1729  						CssmError::throwMe(status); // to trigger login data regeneration
1730  				}
1731  
1732                  // build a fake key
1733                  CssmKey key;
1734                  key.header().BlobType = CSSM_KEYBLOB_RAW;
1735                  key.header().Format = CSSM_KEYBLOB_RAW_FORMAT_OCTET_STRING;
1736                  key.header().AlgorithmId = CSSM_ALGID_3DES_3KEY;
1737                  key.header().KeyClass = CSSM_KEYCLASS_SESSION_KEY;
1738                  key.header().KeyUsage = CSSM_KEYUSE_ENCRYPT | CSSM_KEYUSE_DECRYPT | CSSM_KEYATTR_EXTRACTABLE;
1739                  key.header().KeyAttr = 0;
1740                  CFRef<CFDataRef> tokenLoginUnlockKey;
1741  				if (tokenLoginData) {
1742  					OSStatus status = TokenLoginGetUnlockKey(tokenLoginContext, tokenLoginUnlockKey.take());
1743  					if (status)
1744  						CssmError::throwMe(status); // to trigger login data regeneration
1745  					key.KeyData = CssmData(tokenLoginUnlockKey.get());
1746  				} else {
1747  					key.KeyData = CssmData(const_cast<void *>(password), passwordLength);
1748  				}
1749                  // unwrap it into the CSP (but keep it raw)
1750                  UnwrapKey unwrap(theKeychain->csp(), CSSM_ALGID_NONE);
1751                  CssmKey masterKey;
1752                  CssmData descriptiveData;
1753                  unwrap(key,
1754                         KeySpec(CSSM_KEYUSE_ANY, CSSM_KEYATTR_EXTRACTABLE),
1755                         masterKey, &descriptiveData, NULL);
1756                  
1757                  CssmClient::Db db = theKeychain->database();
1758                  
1759                  // create the keychain, using appropriate credentials
1760                  Allocator &alloc = db->allocator();
1761                  AutoCredentials cred(alloc);	// will leak, but we're quitting soon :-)
1762                  
1763                  // use this passphrase
1764                  cred += TypedList(alloc, CSSM_SAMPLE_TYPE_KEYCHAIN_LOCK,
1765                                    new(alloc) ListElement(CSSM_SAMPLE_TYPE_SYMMETRIC_KEY),
1766                                    new(alloc) ListElement(CssmData::wrap(theKeychain->csp()->handle())),
1767                                    new(alloc) ListElement(CssmData::wrap(masterKey)),
1768                                    new(alloc) ListElement(CssmData()));
1769                  db->authenticate(CSSM_DB_ACCESS_READ, &cred);
1770                  db->unlock();
1771                  loginUnlocked = true;
1772              } catch (const CssmError &e) {
1773                  if (tokenLoginData && !tokenLoginDataUpdated) {
1774                      // token login unlock key was invalid
1775  					loginResult = TokenLoginUpdateUnlockData(tokenLoginContext, smartCardPassword);
1776                      if (loginResult == errSecSuccess) {
1777                          tokenLoginDataUpdated = true;
1778                          continue;
1779                      }
1780                  }
1781                  else {
1782                      loginResult = e.osStatus();
1783                  }
1784              }
1785              break;
1786          }
1787      }
1788  
1789      // if "shortname.keychain" exists and is in the search list, attempt to auto-unlock it with the same password
1790      if (shortnameDotKeychainExists && mSavedList.member(demungeDLDbIdentifier(shortnameDotDLDbIdentifier))) {
1791          try
1792          {
1793              Keychain shortnameDotKC(keychain(shortnameDotDLDbIdentifier));
1794              secnotice("KCLogin", "Attempting to unlock short name keychain \"%s\"",
1795                  (shortnameDotKC) ? shortnameDotKC->name() : "<NULL>");
1796              shortnameDotKC->unlock(CssmData(const_cast<void *>(password), passwordLength));
1797          }
1798          catch(const CssmError &e)
1799          {
1800              // ignore; failure to unlock this keychain is not considered an error
1801          }
1802      }
1803  
1804      if (loginResult != errSecSuccess) {
1805          MacOSError::throwMe(loginResult);
1806      }
1807  }
1808  
1809  void StorageManager::stashLogin()
1810  {
1811      OSStatus loginResult = errSecSuccess;
1812      
1813      DLDbIdentifier loginDLDbIdentifier;
1814      {
1815          mSavedList.revert(true);
1816          loginDLDbIdentifier = mSavedList.loginDLDbIdentifier();
1817      }
1818      
1819  	secnotice("KCLogin", "StorageManager::stash: loginDLDbIdentifier is %s", (loginDLDbIdentifier) ? loginDLDbIdentifier.dbName() : "<NULL>");
1820  	if (!loginDLDbIdentifier)
1821  		MacOSError::throwMe(errSecNoSuchKeychain);
1822      
1823      try
1824      {
1825          CssmData empty;
1826          Keychain theKeychain(keychain(loginDLDbIdentifier));
1827          secnotice("KCLogin", "Attempting to use stash for login keychain \"%s\"",
1828                   (theKeychain) ? theKeychain->name() : "<NULL>");
1829          theKeychain->stashCheck();
1830      }
1831      catch(const CssmError &e)
1832      {
1833          loginResult = e.osStatus(); // save this result
1834      }
1835      
1836      
1837      if (loginResult != errSecSuccess) {
1838          MacOSError::throwMe(loginResult);
1839      }
1840  }
1841  
1842  void StorageManager::stashKeychain()
1843  {
1844      OSStatus loginResult = errSecSuccess;
1845      
1846      DLDbIdentifier loginDLDbIdentifier;
1847      {
1848          mSavedList.revert(true);
1849          loginDLDbIdentifier = mSavedList.loginDLDbIdentifier();
1850      }
1851      
1852  	secnotice("KCLogin", "StorageManager::stash: loginDLDbIdentifier is %s", (loginDLDbIdentifier) ? loginDLDbIdentifier.dbName() : "<NULL>");
1853  	if (!loginDLDbIdentifier)
1854  		MacOSError::throwMe(errSecNoSuchKeychain);
1855      
1856      try
1857      {
1858          Keychain theKeychain(keychain(loginDLDbIdentifier));
1859          secnotice("KCLogin", "Attempting to stash login keychain \"%s\"",
1860                   (theKeychain) ? theKeychain->name() : "<NULL>");
1861          theKeychain->stash();
1862      }
1863      catch(const CssmError &e)
1864      {
1865          loginResult = e.osStatus(); // save this result
1866      }
1867  
1868  
1869      if (loginResult != errSecSuccess) {
1870          MacOSError::throwMe(loginResult);
1871      }
1872  }
1873  
1874  void StorageManager::logout()
1875  {
1876      // nothing left to do here
1877  }
1878  
1879  void StorageManager::changeLoginPassword(ConstStringPtr oldPassword, ConstStringPtr newPassword)
1880  {
1881  	StLock<Mutex>_(mMutex);
1882  
1883  	loginKeychain()->changePassphrase(oldPassword, newPassword);
1884  	secnotice("KClogin", "Changed login keychain password successfully");
1885  }
1886  
1887  
1888  void StorageManager::changeLoginPassword(UInt32 oldPasswordLength, const void *oldPassword,  UInt32 newPasswordLength, const void *newPassword)
1889  {
1890  	StLock<Mutex>_(mMutex);
1891  
1892  	loginKeychain()->changePassphrase(oldPasswordLength, oldPassword,  newPasswordLength, newPassword);
1893  	secnotice("KClogin", "Changed login keychain password successfully");
1894  }
1895  
1896  // Clear out the keychain search list and rename the existing login.keychain.
1897  //
1898  void StorageManager::resetKeychain(Boolean resetSearchList)
1899  {
1900  	StLock<Mutex>_(mMutex);
1901  
1902      // Clear the keychain search list.
1903      Keychain keychain = NULL;
1904      DLDbIdentifier dldbi;
1905      try
1906      {
1907          if ( resetSearchList )
1908          {
1909              StorageManager::KeychainList keychainList;
1910              setSearchList(keychainList);
1911          }
1912          // Get a reference to the existing login keychain...
1913          // If we don't have one, we throw (not requiring a rename).
1914          //
1915          keychain = loginKeychain();
1916      } catch(const CommonError& e) {
1917          secnotice("KClogin", "Failed to open login keychain due to an error: %s", e.what());
1918  
1919          // Set up fallback rename.
1920          dldbi = loginKeychainDLDbIdentifer();
1921  
1922          struct stat exists;
1923          if(::stat(dldbi.dbName(), &exists) != 0) {
1924              // no file exists, everything is fine
1925              secnotice("KClogin", "no file exists; resetKeychain() is done");
1926              return;
1927          }
1928      }
1929  
1930      try{
1931          //
1932          // Rename the existing login.keychain (i.e. put it aside).
1933          //
1934          CFMutableStringRef newName = NULL;
1935          newName = CFStringCreateMutable(NULL, 0);
1936          CFStringRef currName = NULL;
1937          if(keychain) {
1938              currName = CFStringCreateWithCString(NULL, keychain->name(), kCFStringEncodingUTF8);
1939          } else {
1940              currName = CFStringCreateWithCString(NULL, dldbi.dbName(), kCFStringEncodingUTF8);
1941          }
1942          if ( newName && currName )
1943          {
1944              CFStringAppend(newName, currName);
1945              CFStringRef kcSuffix = CFSTR(kKeychainSuffix);
1946              CFStringRef kcDbSuffix = CFSTR(kKeychainDbSuffix);
1947              bool hasDbSuffix = false;
1948              if ( CFStringHasSuffix(newName, kcSuffix) )	// remove the .keychain extension
1949              {
1950                  CFRange suffixRange = CFStringFind(newName, kcSuffix, 0);
1951                  CFStringFindAndReplace(newName, kcSuffix, CFSTR(""), suffixRange, 0);
1952              }
1953              if (CFStringHasSuffix(newName, kcDbSuffix)) {
1954                  hasDbSuffix = true;
1955                  CFRange suffixRange = CFStringFind(newName, kcDbSuffix, 0);
1956                  CFStringFindAndReplace(newName, kcDbSuffix, CFSTR(""), suffixRange, 0);
1957              }
1958  
1959              CFStringAppend(newName, CFSTR(kKeychainRenamedSuffix));	// add "_renamed_"
1960              try
1961              {
1962                  secnotice("KClogin", "attempting keychain rename to %@", newName);
1963                  renameUnique(keychain, currName, newName, hasDbSuffix);
1964              }
1965              catch(const CommonError& e)
1966              {
1967                  // we need to release 'newName' & 'currName'
1968                  secnotice("KClogin", "Failed to renameUnique due to an error: %s", e.what());
1969              }
1970              catch(...)
1971              {
1972                  secnotice("KClogin", "Failed to renameUnique due to an unknown error");
1973              }
1974          }	 // else, let the login call report a duplicate
1975          else {
1976              secnotice("KClogin", "don't have paths, quitting");
1977          }
1978          if ( newName )
1979              CFRelease(newName);
1980          if ( currName )
1981              CFRelease(currName);
1982      }
1983      catch(const CommonError& e) {
1984          secnotice("KClogin", "Failed to reset login keychain due to an error: %s", e.what());
1985      }
1986      catch(...)
1987      {
1988          // We either don't have a login keychain, or there was a
1989          // failure to rename the existing one.
1990          secnotice("KClogin", "Failed to reset keychain due to an unknown error");
1991      }
1992  }
1993  
1994  #pragma mark ____ File Related ____
1995  
1996  Keychain StorageManager::make(const char *pathName)
1997  {
1998  	return make(pathName, true);
1999  }
2000  
2001  Keychain StorageManager::make(const char *pathName, bool add)
2002  {
2003      return make(pathName, add, false);
2004  }
2005  
2006  Keychain StorageManager::make(const char *pathName, bool add, bool isReset) {
2007      return makeKeychain(makeDLDbIdentifier(pathName), add, isReset);
2008  }
2009  
2010  DLDbIdentifier StorageManager::makeDLDbIdentifier(const char *pathName) {
2011  	StLock<Mutex>_(mMutex);
2012  
2013  	string fullPathName;
2014      if ( pathName[0] == '/' )
2015  		fullPathName = pathName;
2016  	else
2017      {
2018  		// Get Home directory from environment.
2019  		switch (mDomain)
2020  		{
2021  		case kSecPreferencesDomainUser:
2022  			{
2023  				const char *homeDir = getenv("HOME");
2024  				if (homeDir == NULL)
2025  				{
2026  					// If $HOME is unset get the current user's home directory
2027  					// from the passwd file.
2028  					uid_t uid = geteuid();
2029  					if (!uid) uid = getuid();
2030  					struct passwd *pw = getpwuid(uid);
2031  					if (!pw)
2032  						MacOSError::throwMe(errSecParam);
2033  					homeDir = pw->pw_dir;
2034  				}
2035  				fullPathName = homeDir;
2036  			}
2037  			break;
2038  		case kSecPreferencesDomainSystem:
2039  			fullPathName = "";
2040  			break;
2041  		default:
2042  			assert(false);	// invalid domain for this
2043  		}
2044  
2045  		fullPathName += "/Library/Keychains/";
2046  		fullPathName += pathName;
2047  	}
2048  
2049      const CSSM_NET_ADDRESS *DbLocation = NULL;	// NULL for keychains
2050      const CSSM_VERSION *version = NULL;
2051      uint32 subserviceId = 0;
2052      CSSM_SERVICE_TYPE subserviceType = CSSM_SERVICE_DL | CSSM_SERVICE_CSP;
2053      const CssmSubserviceUid ssuid(gGuidAppleCSPDL, version,
2054                                     subserviceId, subserviceType);
2055      DLDbIdentifier dlDbIdentifier(ssuid, fullPathName.c_str(), DbLocation);
2056      return dlDbIdentifier;
2057  }
2058  
2059  Keychain StorageManager::makeLoginAuthUI(const Item *item, bool isReset)
2060  {
2061  	StLock<Mutex>_(mMutex);
2062  
2063      // Create a login/default keychain for the user using UI.
2064      // The user can cancel out of the operation, or create a new login keychain.
2065      // If auto-login is turned off, the user will be asked for their login password.
2066      //
2067      OSStatus result = errSecSuccess;
2068      Keychain keychain;	// We return this keychain.
2069      //
2070      // Set up the Auth ref to bring up UI.
2071      //
2072  	AuthorizationItem *currItem, *authEnvirItemArrayPtr = NULL;
2073      AuthorizationRef authRef = NULL;
2074  	try
2075  	{
2076  		result = AuthorizationCreate(NULL, NULL, kAuthorizationFlagDefaults, &authRef);
2077  		if ( result )
2078  			MacOSError::throwMe(result);
2079  
2080  		AuthorizationEnvironment envir;
2081  		envir.count = 6;	// up to 6 hints can be used.
2082  		authEnvirItemArrayPtr = (AuthorizationItem*)malloc(sizeof(AuthorizationItem) * envir.count);
2083  		if ( !authEnvirItemArrayPtr )
2084  			MacOSError::throwMe(errAuthorizationInternal);
2085  
2086  		currItem = envir.items = authEnvirItemArrayPtr;
2087  
2088  		//
2089  		// 1st Hint (optional): The keychain item's account attribute string.
2090  		//						When item is specified, we assume an 'add' operation is being attempted.
2091  		char buff[256];
2092  		UInt32 actLen = 0;
2093  		SecKeychainAttribute attr = { kSecAccountItemAttr, 255, &buff };
2094  		if ( item )
2095  		{
2096  			try
2097  			{
2098  				(*item)->getAttribute(attr, &actLen);
2099  			}
2100  			catch(...)
2101  			{
2102  				actLen = 0;	// This item didn't have the account attribute, so don't display one in the UI.
2103  			}
2104  		}
2105  		currItem->name = AGENT_HINT_ATTR_NAME;	// name str that identifies this hint as attr name
2106  		if ( actLen )	// Fill in the hint if we have an account attr
2107  		{
2108  			if ( actLen >= sizeof(buff) )
2109  				buff[sizeof(buff)-1] = 0;
2110  			else
2111  				buff[actLen] = 0;
2112  			currItem->valueLength = strlen(buff)+1;
2113  			currItem->value = buff;
2114  		}
2115  		else
2116  		{
2117  			currItem->valueLength = 0;
2118  			currItem->value = NULL;
2119  		}
2120  		currItem->flags = 0;
2121  
2122  		//
2123  		// 2nd Hint (optional): The item's keychain full path.
2124  		//
2125  		currItem++;
2126  		char* currDefaultName = NULL;
2127  		try
2128  		{
2129  			currDefaultName = (char*)defaultKeychain()->name();	// Use the name if we have it.
2130  			currItem->name = AGENT_HINT_LOGIN_KC_NAME;	// Name str that identifies this hint as kc path
2131  			currItem->valueLength = (currDefaultName) ? strlen(currDefaultName) : 0;
2132  			currItem->value = (currDefaultName) ? (void*)currDefaultName : (void*)"";
2133  			currItem->flags = 0;
2134  			currItem++;
2135  		}
2136  		catch(...)
2137  		{
2138  			envir.count--;
2139  		}
2140  
2141  		//
2142  		// 3rd Hint (required): check if curr default keychain is unavailable.
2143  		// This is determined by the parent not existing.
2144  		//
2145  		currItem->name = AGENT_HINT_LOGIN_KC_EXISTS_IN_KC_FOLDER;
2146  		Boolean loginUnavail = false;
2147  		try
2148  		{
2149  			Keychain defaultKC = defaultKeychain();
2150  			if ( !defaultKC->exists() )
2151  				loginUnavail = true;
2152  		}
2153  		catch(...)	// login.keychain not present
2154  		{
2155  		}
2156  		currItem->valueLength = sizeof(Boolean);
2157  		currItem->value = (void*)&loginUnavail;
2158  		currItem->flags = 0;
2159  
2160  		//
2161  		// 4th Hint (required): userName
2162  		//
2163  		currItem++;
2164  		currItem->name = AGENT_HINT_LOGIN_KC_USER_NAME;
2165  		char* uName = getenv("USER");
2166  		string userName = uName ? uName : "";
2167  		if ( userName.length() == 0 )
2168  		{
2169  			uid_t uid = geteuid();
2170  			if (!uid) uid = getuid();
2171  			struct passwd *pw = getpwuid(uid);	// fallback case...
2172  			if (pw)
2173  				userName = pw->pw_name;
2174  			endpwent();
2175  		}
2176  		if ( userName.length() == 0 )	// did we ultimately get one?
2177  			MacOSError::throwMe(errAuthorizationInternal);
2178  
2179  		currItem->value = (void*)userName.c_str();
2180  		currItem->valueLength = userName.length();
2181  		currItem->flags = 0;
2182  
2183  		//
2184  		// 5th Hint (required): flags if user has more than 1 keychain (used for a later warning when reset to default).
2185  		//
2186  		currItem++;
2187  		currItem->name = AGENT_HINT_LOGIN_KC_USER_HAS_OTHER_KCS_STR;
2188  		Boolean moreThanOneKCExists = false;
2189  		{
2190  			// if item is NULL, then this is a user-initiated full reset
2191  			if (item && mSavedList.searchList().size() > 1)
2192  				moreThanOneKCExists = true;
2193  		}
2194  		currItem->value = &moreThanOneKCExists;
2195  		currItem->valueLength = sizeof(Boolean);
2196  		currItem->flags = 0;
2197  
2198  		//
2199  		// 6th Hint (required): If no item is involved, this is a user-initiated full reset.
2200  		// We want to suppress the "do you want to reset to defaults?" panel in this case.
2201  		//
2202  		currItem++;
2203  		currItem->name = AGENT_HINT_LOGIN_KC_SUPPRESS_RESET_PANEL;
2204  		Boolean suppressResetPanel = (item == NULL) ? TRUE : FALSE;
2205  		currItem->valueLength = sizeof(Boolean);
2206  		currItem->value = (void*)&suppressResetPanel;
2207  		currItem->flags = 0;
2208  
2209  		//
2210  		// Set up the auth rights and make the auth call.
2211  		//
2212  		AuthorizationItem authItem = { LOGIN_KC_CREATION_RIGHT, 0 , NULL, 0 };
2213  		AuthorizationRights rights = { 1, &authItem };
2214  		AuthorizationFlags flags = kAuthorizationFlagDefaults | kAuthorizationFlagInteractionAllowed | kAuthorizationFlagExtendRights;
2215  		result = AuthorizationCopyRights(authRef, &rights, &envir, flags, NULL);
2216  		if ( result )
2217  			MacOSError::throwMe(result);
2218  		try
2219  		{
2220  			resetKeychain(true); // Clears the plist, moves aside existing login.keychain
2221  		}
2222  		catch (...) // can throw if no existing login.keychain is found
2223  		{
2224  		}
2225  		login(authRef, (UInt32)userName.length(), userName.c_str(), isReset); // Create login.keychain
2226  		keychain = loginKeychain(); // Get newly-created login keychain
2227  		defaultKeychain(keychain);	// Set it to be the default
2228  
2229  		free(authEnvirItemArrayPtr);
2230  		AuthorizationFree(authRef, kAuthorizationFlagDefaults);
2231  	}
2232  
2233  	catch (...)
2234  	{
2235  		// clean up allocations, then rethrow error
2236  		if ( authEnvirItemArrayPtr )
2237  			free(authEnvirItemArrayPtr);
2238  		if ( authRef )
2239  			AuthorizationFree(authRef, kAuthorizationFlagDefaults);
2240  		throw;
2241  	}
2242  
2243      return keychain;
2244  }
2245  
2246  Keychain StorageManager::defaultKeychainUI(Item &item)
2247  {
2248  	StLock<Mutex>_(mMutex);
2249  
2250      Keychain returnedKeychain;
2251      try
2252      {
2253          returnedKeychain = defaultKeychain(); // If we have one, return it.
2254          if ( returnedKeychain->exists() )
2255              return returnedKeychain;
2256      }
2257      catch(...)	// We could have one, but it isn't available (i.e. on a un-mounted volume).
2258      {
2259      }
2260      if ( globals().getUserInteractionAllowed() )
2261      {
2262          returnedKeychain = makeLoginAuthUI(&item, false); // If no Keychains is present, one will be created.
2263          if ( !returnedKeychain )
2264              MacOSError::throwMe(errSecInvalidKeychain);	// Something went wrong...
2265      }
2266      else
2267          MacOSError::throwMe(errSecInteractionNotAllowed); // If UI isn't allowed, return an error.
2268  
2269      return returnedKeychain;
2270  }
2271  
2272  void
2273  StorageManager::addToDomainList(SecPreferencesDomain domain,
2274  	const char* dbName, const CSSM_GUID &guid, uint32 subServiceType)
2275  {
2276  	StLock<Mutex>_(mMutex);
2277  
2278  	if (domain == kSecPreferencesDomainDynamic)
2279  		MacOSError::throwMe(errSecInvalidPrefsDomain);
2280  
2281  	// make the identifier
2282  	CSSM_VERSION version = {0, 0};
2283  	DLDbIdentifier id = DLDbListCFPref::makeDLDbIdentifier (guid, version, 0,
2284  		subServiceType, dbName, NULL);
2285  
2286  	if (domain == mDomain)
2287  	{
2288  		// manipulate the user's list
2289  		{
2290  			mSavedList.revert(true);
2291  			mSavedList.add(demungeDLDbIdentifier(id));
2292  			mSavedList.save();
2293  		}
2294  
2295  		KCEventNotifier::PostKeychainEvent(kSecKeychainListChangedEvent);
2296  	}
2297  	else
2298  	{
2299  		// manipulate the other list
2300  		DLDbListCFPref(domain).add(id);
2301  	}
2302  }
2303  
2304  void
2305  StorageManager::isInDomainList(SecPreferencesDomain domain,
2306  	const char* dbName, const CSSM_GUID &guid, uint32 subServiceType)
2307  {
2308  	StLock<Mutex>_(mMutex);
2309  
2310  	if (domain == kSecPreferencesDomainDynamic)
2311  		MacOSError::throwMe(errSecInvalidPrefsDomain);
2312  
2313  	CSSM_VERSION version = {0, 0};
2314  	DLDbIdentifier id = DLDbListCFPref::makeDLDbIdentifier (guid, version, 0,
2315  		subServiceType, dbName, NULL);
2316  
2317  	// determine the list to search
2318  	bool result;
2319  	if (domain == mDomain)
2320  	{
2321  		result = mSavedList.member(demungeDLDbIdentifier(id));
2322  	}
2323  	else
2324  	{
2325  		result = DLDbListCFPref(domain).member(demungeDLDbIdentifier(id));
2326  	}
2327  
2328  	// do the search
2329  	if (!result)
2330  	{
2331  		MacOSError::throwMe(errSecNoSuchKeychain);
2332  	}
2333  }
2334  
2335  void
2336  StorageManager::removeFromDomainList(SecPreferencesDomain domain,
2337  	const char* dbName, const CSSM_GUID &guid, uint32 subServiceType)
2338  {
2339  	StLock<Mutex>_(mMutex);
2340  
2341  	if (domain == kSecPreferencesDomainDynamic)
2342  		MacOSError::throwMe(errSecInvalidPrefsDomain);
2343  
2344  	// make the identifier
2345  	CSSM_VERSION version = {0, 0};
2346  	DLDbIdentifier id = DLDbListCFPref::makeDLDbIdentifier (guid, version, 0,
2347  		subServiceType, dbName, NULL);
2348  
2349  	if (domain == mDomain)
2350  	{
2351  		// manipulate the user's list
2352  		{
2353  			mSavedList.revert(true);
2354  			mSavedList.remove(demungeDLDbIdentifier(id));
2355  			mSavedList.save();
2356  		}
2357  
2358  		KCEventNotifier::PostKeychainEvent(kSecKeychainListChangedEvent);
2359  	}
2360  	else
2361  	{
2362  		// manipulate the other list
2363  		DLDbListCFPref(domain).remove(id);
2364  	}
2365  }
2366  
2367  bool
2368  StorageManager::keychainOwnerPermissionsValidForDomain(const char* path, SecPreferencesDomain domain)
2369  {
2370  	struct stat sb;
2371  	mode_t perms;
2372  	const char* sysPrefDir = "/Library/Preferences";
2373  	const char* errMsg = "Will not set default";
2374  	char* mustOwnDir = NULL;
2375  	struct passwd* pw = NULL;
2376  
2377  	// get my uid
2378  	uid_t uid = geteuid();
2379  	if (!uid) uid = getuid();
2380  
2381  	// our (e)uid must own the appropriate preferences or home directory
2382  	// for the specified preference domain whose default we will be modifying
2383  	switch (domain) {
2384  		case kSecPreferencesDomainUser:
2385  			mustOwnDir = getenv("HOME");
2386  			if (mustOwnDir == NULL) {
2387  				pw = getpwuid(uid);
2388  				if (!pw) return false;
2389  				mustOwnDir = pw->pw_dir;
2390  			}
2391  			break;
2392  		case kSecPreferencesDomainSystem:
2393  			mustOwnDir = (char*)sysPrefDir;
2394  			break;
2395  		case kSecPreferencesDomainCommon:
2396  			mustOwnDir = (char*)sysPrefDir;
2397  			break;
2398  		default:
2399  			return false;
2400  	}
2401  
2402  	if (mustOwnDir != NULL) {
2403  		struct stat dsb;
2404  		if ( (stat(mustOwnDir, &dsb) != 0) || (dsb.st_uid != uid) ) {
2405  			fprintf(stderr, "%s: UID=%d does not own directory %s\n", errMsg, (int)uid, mustOwnDir);
2406  			mustOwnDir = NULL; // will return below after calling endpwent()
2407  		}
2408  	}
2409  
2410  	if (pw != NULL)
2411  		endpwent();
2412  
2413  	if (mustOwnDir == NULL)
2414  		return false;
2415  
2416  	// check that file actually exists
2417  	if (stat(path, &sb) != 0) {
2418  		fprintf(stderr, "%s: file %s does not exist\n", errMsg, path);
2419  		return false;
2420  	}
2421  
2422  	// check flags
2423  	if (sb.st_flags & (SF_IMMUTABLE | UF_IMMUTABLE)) {
2424  		fprintf(stderr, "%s: file %s is immutable\n", errMsg, path);
2425  		return false;
2426  	}
2427  
2428  	// check ownership
2429  	if (sb.st_uid != uid) {
2430  		fprintf(stderr, "%s: file %s is owned by UID=%d, but we have UID=%d\n",
2431  			errMsg, path, (int)sb.st_uid, (int)uid);
2432  		return false;
2433  	}
2434  
2435  	// check mode
2436  	perms = sb.st_mode;
2437  	perms |= 0600; // must have owner read/write permission set
2438  	if (sb.st_mode != perms) {
2439  		fprintf(stderr, "%s: file %s does not have the expected permissions\n", errMsg, path);
2440  		return false;
2441  	}
2442  
2443  	// user owns file and can read/write it
2444  	return true;
2445  }