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 }