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/AuthorizationTags.h> 44//#include <Security/AuthSession.h> 45#include <security_utilities/debugging.h> 46#include <security_keychain/SecCFTypes.h> 47//#include <Security/SecurityAgentClient.h> 48#include <securityd_client/ssclient.h> 49#include <Security/AuthorizationTags.h> 50#include <Security/AuthorizationTagsPriv.h> 51#include <Security/SecTask.h> 52#include <security_keychain/SecCFTypes.h> 53#include "TrustSettingsSchema.h" 54#include <security_cdsa_client/wrapkey.h> 55 56//%%% add this to AuthorizationTagsPriv.h later 57#ifndef AGENT_HINT_LOGIN_KC_SUPPRESS_RESET_PANEL 58#define AGENT_HINT_LOGIN_KC_SUPPRESS_RESET_PANEL "loginKCCreate:suppressResetPanel" 59#endif 60 61#include "KCCursor.h" 62#include "Globals.h" 63 64 65using namespace CssmClient; 66using namespace KeychainCore; 67 68#define kLoginKeychainPathPrefix "~/Library/Keychains/" 69#define kUserLoginKeychainPath "~/Library/Keychains/login.keychain" 70#define kEmptyKeychainSizeInBytes 20460 71 72//----------------------------------------------------------------------------------- 73 74static SecPreferencesDomain defaultPreferenceDomain() 75{ 76 SessionAttributeBits sessionAttrs; 77 if (gServerMode) { 78 secdebug("servermode", "StorageManager initialized in server mode"); 79 sessionAttrs = sessionIsRoot; 80 } else { 81 MacOSError::check(SessionGetInfo(callerSecuritySession, NULL, &sessionAttrs)); 82 } 83 84 // If this is the root session, use system preferences. 85 // (In SecurityServer debug mode, you'll get a (fake) root session 86 // that has graphics access. Ignore that to help testing.) 87 if ((sessionAttrs & sessionIsRoot) 88 IFDEBUG( && !(sessionAttrs & sessionHasGraphicAccess))) { 89 secdebug("storagemgr", "using system preferences"); 90 return kSecPreferencesDomainSystem; 91 } 92 93 // otherwise, use normal (user) preferences 94 return kSecPreferencesDomainUser; 95} 96 97static bool isAppSandboxed() 98{ 99 bool result = false; 100 SecTaskRef task = SecTaskCreateFromSelf(NULL); 101 if(task != NULL) { 102 CFTypeRef appSandboxValue = SecTaskCopyValueForEntitlement(task, 103 CFSTR("com.apple.security.app-sandbox"), NULL); 104 if(appSandboxValue != NULL) { 105 result = true; 106 CFRelease(appSandboxValue); 107 } 108 CFRelease(task); 109 } 110 return result; 111} 112 113static bool shouldAddToSearchList(const DLDbIdentifier &dLDbIdentifier) 114{ 115 // Creation of a private keychain should not modify the search list: rdar://13529331 116 // However, we want to ensure the login and System keychains are in 117 // the search list if that is not the case when they are created. 118 // Note that App Sandbox apps may not modify the list in either case. 119 120 bool loginOrSystemKeychain = false; 121 const char *dbname = dLDbIdentifier.dbName(); 122 if (dbname) { 123 if ((!strcmp(dbname, "/Library/Keychains/System.keychain")) || 124 (strstr(dbname, "/login.keychain")) ) { 125 loginOrSystemKeychain = true; 126 } 127 } 128 return (loginOrSystemKeychain && !isAppSandboxed()); 129} 130 131 132StorageManager::StorageManager() : 133 mSavedList(defaultPreferenceDomain()), 134 mCommonList(kSecPreferencesDomainCommon), 135 mDomain(kSecPreferencesDomainUser), 136 mMutex(Mutex::recursive) 137{ 138} 139 140 141Mutex* 142StorageManager::getStorageManagerMutex() 143{ 144 return &mKeychainMapMutex; 145} 146 147 148Keychain 149StorageManager::keychain(const DLDbIdentifier &dLDbIdentifier) 150{ 151 StLock<Mutex>_(mKeychainMapMutex); 152 153 if (!dLDbIdentifier) 154 return Keychain(); 155 156 KeychainMap::iterator it = mKeychains.find(dLDbIdentifier); 157 if (it != mKeychains.end()) 158 { 159 if (it->second == NULL) // cleared by weak reference? 160 { 161 mKeychains.erase(it); 162 } 163 else 164 { 165 return it->second; 166 } 167 } 168 169 if (gServerMode) { 170 secdebug("servermode", "keychain reference in server mode"); 171 return Keychain(); 172 } 173 174 // The keychain is not in our cache. Create it. 175 Module module(dLDbIdentifier.ssuid().guid()); 176 DL dl; 177 if (dLDbIdentifier.ssuid().subserviceType() & CSSM_SERVICE_CSP) 178 dl = SSCSPDL(module); 179 else 180 dl = DL(module); 181 182 dl->subserviceId(dLDbIdentifier.ssuid().subserviceId()); 183 dl->version(dLDbIdentifier.ssuid().version()); 184 Db db(dl, dLDbIdentifier.dbName()); 185 186 Keychain keychain(db); 187 // Add the keychain to the cache. 188 mKeychains.insert(KeychainMap::value_type(dLDbIdentifier, &*keychain)); 189 keychain->inCache(true); 190 191 return keychain; 192} 193 194void 195StorageManager::removeKeychain(const DLDbIdentifier &dLDbIdentifier, 196 KeychainImpl *keychainImpl) 197{ 198 // Lock the recursive mutex 199 200 StLock<Mutex>_(mKeychainMapMutex); 201 202 KeychainMap::iterator it = mKeychains.find(dLDbIdentifier); 203 if (it != mKeychains.end() && (KeychainImpl*) it->second == keychainImpl) 204 mKeychains.erase(it); 205 206 keychainImpl->inCache(false); 207} 208 209void 210StorageManager::didRemoveKeychain(const DLDbIdentifier &dLDbIdentifier) 211{ 212 // Lock the recursive mutex 213 214 StLock<Mutex>_(mKeychainMapMutex); 215 216 KeychainMap::iterator it = mKeychains.find(dLDbIdentifier); 217 if (it != mKeychains.end()) 218 { 219 if (it->second != NULL) // did we get zapped by weak reference destruction 220 { 221 KeychainImpl *keychainImpl = it->second; 222 keychainImpl->inCache(false); 223 } 224 225 mKeychains.erase(it); 226 } 227} 228 229// Create keychain if it doesn't exist, and optionally add it to the search list. 230Keychain 231StorageManager::makeKeychain(const DLDbIdentifier &dLDbIdentifier, bool add) 232{ 233 StLock<Mutex>_(mKeychainMapMutex); 234 235 Keychain theKeychain = keychain(dLDbIdentifier); 236 bool post = false; 237 bool updateList = (add && shouldAddToSearchList(dLDbIdentifier)); 238 239 if (updateList) 240 { 241 mSavedList.revert(false); 242 DLDbList searchList = mSavedList.searchList(); 243 if (find(searchList.begin(), searchList.end(), dLDbIdentifier) != searchList.end()) 244 return theKeychain; // theKeychain is already in the searchList. 245 246 mCommonList.revert(false); 247 searchList = mCommonList.searchList(); 248 if (find(searchList.begin(), searchList.end(), dLDbIdentifier) != searchList.end()) 249 return theKeychain; // theKeychain is already in the commonList don't add it to the searchList. 250 251 // If theKeychain doesn't exist don't bother adding it to the search list yet. 252 if (!theKeychain->exists()) 253 return theKeychain; 254 255 // theKeychain exists and is not in our search list, so add it to the 256 // search list. 257 mSavedList.revert(true); 258 mSavedList.add(dLDbIdentifier); 259 mSavedList.save(); 260 post = true; 261 } 262 263 if (post) 264 { 265 // Make sure we are not holding mStorageManagerLock anymore when we 266 // post this event. 267 KCEventNotifier::PostKeychainEvent(kSecKeychainListChangedEvent); 268 } 269 270 return theKeychain; 271} 272 273// Be notified a Keychain just got created. 274void 275StorageManager::created(const Keychain &keychain) 276{ 277 StLock<Mutex>_(mKeychainMapMutex); 278 279 DLDbIdentifier dLDbIdentifier = keychain->dlDbIdentifier(); 280 bool defaultChanged = false; 281 bool updateList = shouldAddToSearchList(dLDbIdentifier); 282 283 if (updateList) 284 { 285 mSavedList.revert(true); 286 // If we don't have a default Keychain yet. Make the newly created 287 // keychain the default. 288 if (!mSavedList.defaultDLDbIdentifier()) 289 { 290 mSavedList.defaultDLDbIdentifier(dLDbIdentifier); 291 defaultChanged = true; 292 } 293 294 // Add the keychain to the search list prefs. 295 mSavedList.add(dLDbIdentifier); 296 mSavedList.save(); 297 298 // Make sure we are not holding mLock when we post these events. 299 KCEventNotifier::PostKeychainEvent(kSecKeychainListChangedEvent); 300 } 301 302 if (defaultChanged) 303 { 304 KCEventNotifier::PostKeychainEvent(kSecDefaultChangedEvent, dLDbIdentifier); 305 } 306} 307 308KCCursor 309StorageManager::createCursor(SecItemClass itemClass, 310 const SecKeychainAttributeList *attrList) 311{ 312 StLock<Mutex>_(mMutex); 313 314 KeychainList searchList; 315 getSearchList(searchList); 316 return KCCursor(searchList, itemClass, attrList); 317} 318 319KCCursor 320StorageManager::createCursor(const SecKeychainAttributeList *attrList) 321{ 322 StLock<Mutex>_(mMutex); 323 324 KeychainList searchList; 325 getSearchList(searchList); 326 return KCCursor(searchList, attrList); 327} 328 329void 330StorageManager::lockAll() 331{ 332 StLock<Mutex>_(mMutex); 333 334 SecurityServer::ClientSession ss(Allocator::standard(), Allocator::standard()); 335 ss.lockAll (false); 336} 337 338Keychain 339StorageManager::defaultKeychain() 340{ 341 StLock<Mutex>_(mMutex); 342 343 Keychain theKeychain; 344 CFTypeRef ref; 345 346 { 347 mSavedList.revert(false); 348 DLDbIdentifier defaultDLDbIdentifier(mSavedList.defaultDLDbIdentifier()); 349 if (defaultDLDbIdentifier) 350 { 351 theKeychain = keychain(defaultDLDbIdentifier); 352 ref = theKeychain->handle(false); 353 } 354 } 355 356 if (theKeychain /* && theKeychain->exists() */) 357 return theKeychain; 358 359 MacOSError::throwMe(errSecNoDefaultKeychain); 360} 361 362void 363StorageManager::defaultKeychain(const Keychain &keychain) 364{ 365 StLock<Mutex>_(mMutex); 366 367 // Only set a keychain as the default if we own it and can read/write it, 368 // and our uid allows modifying the directory for that preference domain. 369 if (!keychainOwnerPermissionsValidForDomain(keychain->name(), mDomain)) 370 MacOSError::throwMe(errSecWrPerm); 371 372 DLDbIdentifier oldDefaultId; 373 DLDbIdentifier newDefaultId(keychain->dlDbIdentifier()); 374 { 375 oldDefaultId = mSavedList.defaultDLDbIdentifier(); 376 mSavedList.revert(true); 377 mSavedList.defaultDLDbIdentifier(newDefaultId); 378 mSavedList.save(); 379 } 380 381 if (!(oldDefaultId == newDefaultId)) 382 { 383 // Make sure we are not holding mLock when we post this event. 384 KCEventNotifier::PostKeychainEvent(kSecDefaultChangedEvent, newDefaultId); 385 } 386} 387 388Keychain 389StorageManager::defaultKeychain(SecPreferencesDomain domain) 390{ 391 StLock<Mutex>_(mMutex); 392 393 if (domain == kSecPreferencesDomainDynamic) 394 MacOSError::throwMe(errSecInvalidPrefsDomain); 395 396 if (domain == mDomain) 397 return defaultKeychain(); 398 else 399 { 400 DLDbIdentifier defaultDLDbIdentifier(DLDbListCFPref(domain).defaultDLDbIdentifier()); 401 if (defaultDLDbIdentifier) 402 return keychain(defaultDLDbIdentifier); 403 404 MacOSError::throwMe(errSecNoDefaultKeychain); 405 } 406} 407 408void 409StorageManager::defaultKeychain(SecPreferencesDomain domain, const Keychain &keychain) 410{ 411 StLock<Mutex>_(mMutex); 412 413 if (domain == kSecPreferencesDomainDynamic) 414 MacOSError::throwMe(errSecInvalidPrefsDomain); 415 416 if (domain == mDomain) 417 defaultKeychain(keychain); 418 else 419 DLDbListCFPref(domain).defaultDLDbIdentifier(keychain->dlDbIdentifier()); 420} 421 422Keychain 423StorageManager::loginKeychain() 424{ 425 StLock<Mutex>_(mMutex); 426 427 Keychain theKeychain; 428 { 429 mSavedList.revert(false); 430 DLDbIdentifier loginDLDbIdentifier(mSavedList.loginDLDbIdentifier()); 431 if (loginDLDbIdentifier) 432 { 433 theKeychain = keychain(loginDLDbIdentifier); 434 } 435 } 436 437 if (theKeychain && theKeychain->exists()) 438 return theKeychain; 439 440 MacOSError::throwMe(errSecNoSuchKeychain); 441} 442 443void 444StorageManager::loginKeychain(Keychain keychain) 445{ 446 StLock<Mutex>_(mMutex); 447 448 mSavedList.revert(true); 449 mSavedList.loginDLDbIdentifier(keychain->dlDbIdentifier()); 450 mSavedList.save(); 451} 452 453size_t 454StorageManager::size() 455{ 456 StLock<Mutex>_(mMutex); 457 458 mSavedList.revert(false); 459 mCommonList.revert(false); 460 return mSavedList.searchList().size() + mCommonList.searchList().size(); 461} 462 463Keychain 464StorageManager::at(unsigned int ix) 465{ 466 StLock<Mutex>_(mMutex); 467 468 mSavedList.revert(false); 469 DLDbList dLDbList = mSavedList.searchList(); 470 if (ix < dLDbList.size()) 471 { 472 return keychain(dLDbList[ix]); 473 } 474 else 475 { 476 ix -= dLDbList.size(); 477 mCommonList.revert(false); 478 DLDbList commonList = mCommonList.searchList(); 479 if (ix >= commonList.size()) 480 MacOSError::throwMe(errSecInvalidKeychain); 481 482 return keychain(commonList[ix]); 483 } 484} 485 486Keychain 487StorageManager::operator[](unsigned int ix) 488{ 489 StLock<Mutex>_(mMutex); 490 491 return at(ix); 492} 493 494void StorageManager::rename(Keychain keychain, const char* newName) 495{ 496 497 StLock<Mutex>_(mKeychainMapMutex); 498 499 bool changedDefault = false; 500 DLDbIdentifier newDLDbIdentifier; 501 { 502 mSavedList.revert(true); 503 DLDbIdentifier defaultId = mSavedList.defaultDLDbIdentifier(); 504 505 // Find the keychain object for the given ref 506 DLDbIdentifier dLDbIdentifier = keychain->dlDbIdentifier(); 507 508 // Actually rename the database on disk. 509 keychain->database()->rename(newName); 510 511 if (dLDbIdentifier == defaultId) 512 changedDefault=true; 513 514 newDLDbIdentifier = keychain->dlDbIdentifier(); 515 // Rename the keychain in the search list. 516 mSavedList.rename(dLDbIdentifier, newDLDbIdentifier); 517 518 // If this was the default keychain change it accordingly 519 if (changedDefault) 520 mSavedList.defaultDLDbIdentifier(newDLDbIdentifier); 521 522 mSavedList.save(); 523 524 // we aren't worried about a weak reference here, because we have to 525 // hold a lock on an item in order to do the rename 526 527 // Now update the Keychain cache 528 if (keychain->inCache()) 529 { 530 KeychainMap::iterator it = mKeychains.find(dLDbIdentifier); 531 if (it != mKeychains.end() && (KeychainImpl*) it->second == keychain.get()) 532 { 533 // Remove the keychain from the cache under its old 534 // dLDbIdentifier 535 mKeychains.erase(it); 536 } 537 } 538 539 // If we renamed this keychain on top of an existing one we should 540 // drop the old one from the cache. 541 KeychainMap::iterator it = mKeychains.find(newDLDbIdentifier); 542 if (it != mKeychains.end()) 543 { 544 Keychain oldKeychain(it->second); 545 oldKeychain->inCache(false); 546 // @@@ Ideally we should invalidate or fault this keychain object. 547 } 548 549 if (keychain->inCache()) 550 { 551 // If the keychain wasn't in the cache to being with let's not put 552 // it there now. There was probably a good reason it wasn't in it. 553 // If the keychain was in the cache, update it to use 554 // newDLDbIdentifier. 555 mKeychains.insert(KeychainMap::value_type(newDLDbIdentifier, 556 keychain)); 557 } 558 } 559 560 // Make sure we are not holding mLock when we post these events. 561 KCEventNotifier::PostKeychainEvent(kSecKeychainListChangedEvent); 562 563 if (changedDefault) 564 KCEventNotifier::PostKeychainEvent(kSecDefaultChangedEvent, 565 newDLDbIdentifier); 566} 567 568void StorageManager::renameUnique(Keychain keychain, CFStringRef newName) 569{ 570 StLock<Mutex>_(mMutex); 571 572 bool doneCreating = false; 573 int index = 1; 574 do 575 { 576 char newNameCString[MAXPATHLEN]; 577 if ( CFStringGetCString(newName, newNameCString, MAXPATHLEN, kCFStringEncodingUTF8) ) // make sure it fits in MAXPATHLEN, etc. 578 { 579 // Construct the new name... 580 // 581 CFMutableStringRef newNameCFStr = NULL; 582 newNameCFStr = CFStringCreateMutable(NULL, MAXPATHLEN); 583 if ( newNameCFStr ) 584 { 585 CFStringAppendFormat(newNameCFStr, NULL, CFSTR("%s%d"), newNameCString, index); 586 CFStringAppend(newNameCFStr, CFSTR(kKeychainSuffix)); // add .keychain 587 char toUseBuff2[MAXPATHLEN]; 588 if ( CFStringGetCString(newNameCFStr, toUseBuff2, MAXPATHLEN, kCFStringEncodingUTF8) ) // make sure it fits in MAXPATHLEN, etc. 589 { 590 struct stat filebuf; 591 if ( lstat(toUseBuff2, &filebuf) ) 592 { 593 rename(keychain, toUseBuff2); 594 KeychainList kcList; 595 kcList.push_back(keychain); 596 remove(kcList, false); 597 doneCreating = true; 598 } 599 else 600 index++; 601 } 602 else 603 doneCreating = true; // failure to get c string. 604 CFRelease(newNameCFStr); 605 } 606 else 607 doneCreating = false; // failure to create mutable string. 608 } 609 else 610 doneCreating = false; // failure to get the string (i.e. > MAXPATHLEN?) 611 } 612 while (!doneCreating && index != INT_MAX); 613} 614 615#define KEYCHAIN_SYNC_KEY CFSTR("KeychainSyncList") 616#define KEYCHAIN_SYNC_DOMAIN CFSTR("com.apple.keychainsync") 617 618static CFStringRef MakeExpandedPath (const char* path) 619{ 620 std::string name = DLDbListCFPref::ExpandTildesInPath (std::string (path)); 621 CFStringRef expanded = CFStringCreateWithCString (NULL, name.c_str (), 0); 622 return expanded; 623} 624 625void StorageManager::removeKeychainFromSyncList (const DLDbIdentifier &id) 626{ 627 StLock<Mutex>_(mMutex); 628 629 // make a CFString of our identifier 630 const char* idname = id.dbName (); 631 if (idname == NULL) 632 { 633 return; 634 } 635 636 CFRef<CFStringRef> idString = MakeExpandedPath (idname); 637 638 // check and see if this keychain is in the keychain syncing list 639 CFArrayRef value = 640 (CFArrayRef) CFPreferencesCopyValue (KEYCHAIN_SYNC_KEY, 641 KEYCHAIN_SYNC_DOMAIN, 642 kCFPreferencesCurrentUser, 643 kCFPreferencesAnyHost); 644 if (value == NULL) 645 { 646 return; 647 } 648 649 // make a mutable copy of the dictionary 650 CFRef<CFMutableArrayRef> mtValue = CFArrayCreateMutableCopy (NULL, 0, value); 651 CFRelease (value); 652 653 // walk the array, looking for the value 654 CFIndex i; 655 CFIndex limit = CFArrayGetCount (mtValue.get()); 656 bool found = false; 657 658 for (i = 0; i < limit; ++i) 659 { 660 CFDictionaryRef idx = (CFDictionaryRef) CFArrayGetValueAtIndex (mtValue.get(), i); 661 CFStringRef v = (CFStringRef) CFDictionaryGetValue (idx, CFSTR("DbName")); 662 if (v == NULL) 663 { 664 return; // something is really wrong if this is taken 665 } 666 667 char* stringBuffer = NULL; 668 const char* pathString = CFStringGetCStringPtr(v, 0); 669 if (pathString == 0) 670 { 671 CFIndex maxLen = CFStringGetMaximumSizeForEncoding(CFStringGetLength(v), kCFStringEncodingUTF8) + 1; 672 stringBuffer = (char*) malloc(maxLen); 673 CFStringGetCString(v, stringBuffer, maxLen, kCFStringEncodingUTF8); 674 pathString = stringBuffer; 675 } 676 677 CFStringRef vExpanded = MakeExpandedPath(pathString); 678 CFComparisonResult result = CFStringCompare (vExpanded, idString.get(), 0); 679 if (stringBuffer != NULL) 680 { 681 free(stringBuffer); 682 } 683 684 CFRelease (vExpanded); 685 686 if (result == 0) 687 { 688 CFArrayRemoveValueAtIndex (mtValue.get(), i); 689 found = true; 690 break; 691 } 692 } 693 694 if (found) 695 { 696#ifndef NDEBUG 697 CFShow (mtValue.get()); 698#endif 699 700 CFPreferencesSetValue (KEYCHAIN_SYNC_KEY, 701 mtValue, 702 KEYCHAIN_SYNC_DOMAIN, 703 kCFPreferencesCurrentUser, 704 kCFPreferencesAnyHost); 705 CFPreferencesSynchronize (KEYCHAIN_SYNC_DOMAIN, kCFPreferencesCurrentUser, kCFPreferencesAnyHost); 706 } 707} 708 709void StorageManager::remove(const KeychainList &kcsToRemove, bool deleteDb) 710{ 711 StLock<Mutex>_(mMutex); 712 713 bool unsetDefault = false; 714 bool updateList = (!isAppSandboxed()); 715 716 if (updateList) 717 { 718 mSavedList.revert(true); 719 DLDbIdentifier defaultId = mSavedList.defaultDLDbIdentifier(); 720 for (KeychainList::const_iterator ix = kcsToRemove.begin(); 721 ix != kcsToRemove.end(); ++ix) 722 { 723 // Find the keychain object for the given ref 724 Keychain theKeychain = *ix; 725 DLDbIdentifier dLDbIdentifier = theKeychain->dlDbIdentifier(); 726 727 // Remove it from the saved list 728 mSavedList.remove(dLDbIdentifier); 729 if (dLDbIdentifier == defaultId) 730 unsetDefault=true; 731 732 if (deleteDb) 733 { 734 removeKeychainFromSyncList (dLDbIdentifier); 735 736 // Now remove it from the cache 737 removeKeychain(dLDbIdentifier, theKeychain.get()); 738 } 739 } 740 741 if (unsetDefault) 742 mSavedList.defaultDLDbIdentifier(DLDbIdentifier()); 743 744 mSavedList.save(); 745 } 746 747 if (deleteDb) 748 { 749 // Delete the actual databases without holding any locks. 750 for (KeychainList::const_iterator ix = kcsToRemove.begin(); 751 ix != kcsToRemove.end(); ++ix) 752 { 753 (*ix)->database()->deleteDb(); 754 } 755 } 756 757 if (updateList) { 758 // Make sure we are not holding mLock when we post these events. 759 KCEventNotifier::PostKeychainEvent(kSecKeychainListChangedEvent); 760 } 761 762 if (unsetDefault) 763 KCEventNotifier::PostKeychainEvent(kSecDefaultChangedEvent); 764} 765 766void 767StorageManager::getSearchList(KeychainList &keychainList) 768{ 769 // hold the global lock since we make keychain objects in this function 770 771 // to do: each of the items in this list must be retained, otherwise mayhem will occur 772 StLock<Mutex>_(mMutex); 773 774 if (gServerMode) { 775 keychainList.clear(); 776 return; 777 } 778 779 mSavedList.revert(false); 780 mCommonList.revert(false); 781 782 // Merge mSavedList, mDynamicList and mCommonList 783 DLDbList dLDbList = mSavedList.searchList(); 784 DLDbList dynamicList = mDynamicList.searchList(); 785 DLDbList commonList = mCommonList.searchList(); 786 KeychainList result; 787 result.reserve(dLDbList.size() + dynamicList.size() + commonList.size()); 788 789 { 790 for (DLDbList::const_iterator it = dynamicList.begin(); 791 it != dynamicList.end(); ++it) 792 { 793 Keychain k = keychain(*it); 794 result.push_back(k); 795 } 796 797 for (DLDbList::const_iterator it = dLDbList.begin(); 798 it != dLDbList.end(); ++it) 799 { 800 Keychain k = keychain(*it); 801 result.push_back(k); 802 } 803 804 for (DLDbList::const_iterator it = commonList.begin(); 805 it != commonList.end(); ++it) 806 { 807 Keychain k = keychain(*it); 808 result.push_back(k); 809 } 810 } 811 812 keychainList.swap(result); 813} 814 815void 816StorageManager::setSearchList(const KeychainList &keychainList) 817{ 818 StLock<Mutex>_(mMutex); 819 820 DLDbList commonList = mCommonList.searchList(); 821 822 // Strip out the common list part from the end of the search list. 823 KeychainList::const_iterator it_end = keychainList.end(); 824 DLDbList::const_reverse_iterator end_common = commonList.rend(); 825 for (DLDbList::const_reverse_iterator it_common = commonList.rbegin(); it_common != end_common; ++it_common) 826 { 827 // Eliminate common entries from the end of the passed in keychainList. 828 if (it_end == keychainList.begin()) 829 break; 830 831 --it_end; 832 if (!((*it_end)->dlDbIdentifier() == *it_common)) 833 { 834 ++it_end; 835 break; 836 } 837 } 838 839 /* it_end now points one past the last element in keychainList which is not in commonList. */ 840 DLDbList searchList, oldSearchList(mSavedList.searchList()); 841 for (KeychainList::const_iterator it = keychainList.begin(); it != it_end; ++it) 842 { 843 searchList.push_back((*it)->dlDbIdentifier()); 844 } 845 846 { 847 // Set the current searchlist to be what was passed in, the old list will be freed 848 // upon exit of this stackframe. 849 mSavedList.revert(true); 850 mSavedList.searchList(searchList); 851 mSavedList.save(); 852 } 853 854 if (!(oldSearchList == searchList)) 855 { 856 // Make sure we are not holding mLock when we post this event. 857 KCEventNotifier::PostKeychainEvent(kSecKeychainListChangedEvent); 858 } 859} 860 861void 862StorageManager::getSearchList(SecPreferencesDomain domain, KeychainList &keychainList) 863{ 864 StLock<Mutex>_(mMutex); 865 866 if (gServerMode) { 867 keychainList.clear(); 868 return; 869 } 870 871 if (domain == kSecPreferencesDomainDynamic) 872 { 873 convertList(keychainList, mDynamicList.searchList()); 874 } 875 else if (domain == mDomain) 876 { 877 mSavedList.revert(false); 878 convertList(keychainList, mSavedList.searchList()); 879 } 880 else 881 { 882 convertList(keychainList, DLDbListCFPref(domain).searchList()); 883 } 884} 885 886void StorageManager::forceUserSearchListReread() 887{ 888 mSavedList.forceUserSearchListReread(); 889} 890 891void 892StorageManager::setSearchList(SecPreferencesDomain domain, const KeychainList &keychainList) 893{ 894 StLock<Mutex>_(mMutex); 895 896 if (domain == kSecPreferencesDomainDynamic) 897 MacOSError::throwMe(errSecInvalidPrefsDomain); 898 899 DLDbList searchList; 900 convertList(searchList, keychainList); 901 902 if (domain == mDomain) 903 { 904 DLDbList oldSearchList(mSavedList.searchList()); 905 { 906 // Set the current searchlist to be what was passed in, the old list will be freed 907 // upon exit of this stackframe. 908 mSavedList.revert(true); 909 mSavedList.searchList(searchList); 910 mSavedList.save(); 911 } 912 913 if (!(oldSearchList == searchList)) 914 { 915 KCEventNotifier::PostKeychainEvent(kSecKeychainListChangedEvent); 916 } 917 } 918 else 919 { 920 DLDbListCFPref(domain).searchList(searchList); 921 } 922} 923 924void 925StorageManager::domain(SecPreferencesDomain domain) 926{ 927 StLock<Mutex>_(mMutex); 928 929 if (domain == kSecPreferencesDomainDynamic) 930 MacOSError::throwMe(errSecInvalidPrefsDomain); 931 932 if (domain == mDomain) 933 return; // no change 934 935#if !defined(NDEBUG) 936 switch (domain) 937 { 938 case kSecPreferencesDomainSystem: 939 secdebug("storagemgr", "switching to system domain"); break; 940 case kSecPreferencesDomainUser: 941 secdebug("storagemgr", "switching to user domain (uid %d)", getuid()); break; 942 default: 943 secdebug("storagemgr", "switching to weird prefs domain %d", domain); break; 944 } 945#endif 946 947 mDomain = domain; 948 mSavedList.set(domain); 949} 950 951void 952StorageManager::optionalSearchList(CFTypeRef keychainOrArray, KeychainList &keychainList) 953{ 954 StLock<Mutex>_(mMutex); 955 956 if (!keychainOrArray) 957 getSearchList(keychainList); 958 else 959 { 960 CFTypeID typeID = CFGetTypeID(keychainOrArray); 961 if (typeID == CFArrayGetTypeID()) 962 convertToKeychainList(CFArrayRef(keychainOrArray), keychainList); 963 else if (typeID == gTypes().KeychainImpl.typeID) 964 keychainList.push_back(KeychainImpl::required(SecKeychainRef(keychainOrArray))); 965 else 966 MacOSError::throwMe(errSecParam); 967 } 968} 969 970// static methods. 971void 972StorageManager::convertToKeychainList(CFArrayRef keychainArray, KeychainList &keychainList) 973{ 974 CFIndex count = CFArrayGetCount(keychainArray); 975 if (!(count > 0)) 976 return; 977 978 KeychainList keychains(count); 979 for (CFIndex ix = 0; ix < count; ++ix) 980 { 981 keychains[ix] = KeychainImpl::required(SecKeychainRef(CFArrayGetValueAtIndex(keychainArray, ix))); 982 } 983 984 keychainList.swap(keychains); 985} 986 987CFArrayRef 988StorageManager::convertFromKeychainList(const KeychainList &keychainList) 989{ 990 CFRef<CFMutableArrayRef> keychainArray(CFArrayCreateMutable(NULL, keychainList.size(), &kCFTypeArrayCallBacks)); 991 992 for (KeychainList::const_iterator ix = keychainList.begin(); ix != keychainList.end(); ++ix) 993 { 994 SecKeychainRef keychainRef = (*ix)->handle(); 995 CFArrayAppendValue(keychainArray, keychainRef); 996 CFRelease(keychainRef); 997 } 998 999 // Counter the CFRelease that CFRef<> is about to do when keychainArray goes out of scope. 1000 CFRetain(keychainArray); 1001 return keychainArray; 1002} 1003 1004void StorageManager::convertList(DLDbList &ids, const KeychainList &kcs) 1005{ 1006 DLDbList result; 1007 result.reserve(kcs.size()); 1008 for (KeychainList::const_iterator ix = kcs.begin(); ix != kcs.end(); ++ix) 1009 { 1010 result.push_back((*ix)->dlDbIdentifier()); 1011 } 1012 ids.swap(result); 1013} 1014 1015void StorageManager::convertList(KeychainList &kcs, const DLDbList &ids) 1016{ 1017 StLock<Mutex>_(mMutex); 1018 1019 KeychainList result; 1020 result.reserve(ids.size()); 1021 { 1022 for (DLDbList::const_iterator ix = ids.begin(); ix != ids.end(); ++ix) 1023 result.push_back(keychain(*ix)); 1024 } 1025 kcs.swap(result); 1026} 1027 1028#pragma mark ____ Login Functions ____ 1029 1030void StorageManager::login(AuthorizationRef authRef, UInt32 nameLength, const char* name) 1031{ 1032 StLock<Mutex>_(mMutex); 1033 1034 AuthorizationItemSet* info = NULL; 1035 OSStatus result = AuthorizationCopyInfo(authRef, NULL, &info); // get the results of the copy rights call. 1036 Boolean created = false; 1037 if ( result == errSecSuccess && info->count ) 1038 { 1039 // Grab the password from the auth context (info) and create the keychain... 1040 // 1041 AuthorizationItem* currItem = info->items; 1042 for (UInt32 index = 1; index <= info->count; index++) //@@@plugin bug won't return a specific context. 1043 { 1044 if (strcmp(currItem->name, kAuthorizationEnvironmentPassword) == 0) 1045 { 1046 // creates the login keychain with the specified password 1047 try 1048 { 1049 login(nameLength, name, (UInt32)currItem->valueLength, currItem->value); 1050 created = true; 1051 } 1052 catch(...) 1053 { 1054 } 1055 break; 1056 } 1057 currItem++; 1058 } 1059 } 1060 if ( info ) 1061 AuthorizationFreeItemSet(info); 1062 1063 if ( !created ) 1064 MacOSError::throwMe(errAuthorizationInternal); 1065} 1066 1067void StorageManager::login(ConstStringPtr name, ConstStringPtr password) 1068{ 1069 StLock<Mutex>_(mMutex); 1070 1071 if ( name == NULL || password == NULL ) 1072 MacOSError::throwMe(errSecParam); 1073 1074 login(name[0], name + 1, password[0], password + 1); 1075} 1076 1077void StorageManager::login(UInt32 nameLength, const void *name, 1078 UInt32 passwordLength, const void *password) 1079{ 1080 if (passwordLength != 0 && password == NULL) 1081 { 1082 secdebug("KCLogin", "StorageManager::login: invalid argument (NULL password)"); 1083 MacOSError::throwMe(errSecParam); 1084 } 1085 1086 DLDbIdentifier loginDLDbIdentifier; 1087 { 1088 mSavedList.revert(true); 1089 loginDLDbIdentifier = mSavedList.loginDLDbIdentifier(); 1090 } 1091 1092 secdebug("KCLogin", "StorageManager::login: loginDLDbIdentifier is %s", (loginDLDbIdentifier) ? loginDLDbIdentifier.dbName() : "<NULL>"); 1093 if (!loginDLDbIdentifier) 1094 MacOSError::throwMe(errSecNoSuchKeychain); 1095 1096 1097 //*************************************************************** 1098 // gather keychain information 1099 //*************************************************************** 1100 1101 // user name 1102 int uid = geteuid(); 1103 struct passwd *pw = getpwuid(uid); 1104 if (pw == NULL) { 1105 secdebug("KCLogin", "StorageManager::login: invalid argument (NULL uid)"); 1106 MacOSError::throwMe(errSecParam); 1107 } 1108 char *userName = pw->pw_name; 1109 1110 // make keychain path strings 1111 std::string keychainPath = DLDbListCFPref::ExpandTildesInPath(kLoginKeychainPathPrefix); 1112 std::string shortnameKeychain = keychainPath + userName; 1113 std::string shortnameDotKeychain = shortnameKeychain + ".keychain"; 1114 std::string loginDotKeychain = keychainPath + "login.keychain"; 1115 std::string loginRenamed1Keychain = keychainPath + "login_renamed1.keychain"; 1116 1117 // check for existence of keychain files 1118 bool shortnameKeychainExists = false; 1119 bool shortnameDotKeychainExists = false; 1120 bool loginKeychainExists = false; 1121 bool loginRenamed1KeychainExists = false; 1122 { 1123 struct stat st; 1124 int stat_result; 1125 stat_result = ::stat(shortnameKeychain.c_str(), &st); 1126 shortnameKeychainExists = (stat_result == 0); 1127 stat_result = ::stat(shortnameDotKeychain.c_str(), &st); 1128 shortnameDotKeychainExists = (stat_result == 0); 1129 stat_result = ::stat(loginDotKeychain.c_str(), &st); 1130 loginKeychainExists = (stat_result == 0); 1131 stat_result = ::stat(loginRenamed1Keychain.c_str(), &st); 1132 loginRenamed1KeychainExists = (stat_result == 0); 1133 } 1134 1135 bool loginUnlocked = false; 1136 1137 // make the keychain identifiers 1138 CSSM_VERSION version = {0, 0}; 1139 DLDbIdentifier shortnameDLDbIdentifier = DLDbListCFPref::makeDLDbIdentifier(gGuidAppleCSPDL, version, 0, CSSM_SERVICE_CSP | CSSM_SERVICE_DL, shortnameKeychain.c_str(), NULL); 1140 DLDbIdentifier shortnameDotDLDbIdentifier = DLDbListCFPref::makeDLDbIdentifier(gGuidAppleCSPDL, version, 0, CSSM_SERVICE_CSP | CSSM_SERVICE_DL, shortnameDotKeychain.c_str(), NULL); 1141 DLDbIdentifier loginRenamed1DLDbIdentifier = DLDbListCFPref::makeDLDbIdentifier(gGuidAppleCSPDL, version, 0, CSSM_SERVICE_CSP | CSSM_SERVICE_DL, loginRenamed1Keychain.c_str(), NULL); 1142 1143 //*************************************************************** 1144 // make file renaming changes first 1145 //*************************************************************** 1146 1147 // if "~/Library/Keychains/shortname" exists, we need to migrate it forward; 1148 // either to login.keychain if there isn't already one, otherwise to shortname.keychain 1149 if (shortnameKeychainExists) { 1150 int rename_stat = 0; 1151 if (loginKeychainExists) { 1152 struct stat st; 1153 int tmp_result = ::stat(loginDotKeychain.c_str(), &st); 1154 if (tmp_result == 0) { 1155 if (st.st_size <= kEmptyKeychainSizeInBytes) { 1156 tmp_result = ::unlink(loginDotKeychain.c_str()); 1157 rename_stat = ::rename(shortnameKeychain.c_str(), loginDotKeychain.c_str()); 1158 shortnameKeychainExists = (rename_stat != 0); 1159 } 1160 } 1161 } 1162 if (shortnameKeychainExists) { 1163 if (loginKeychainExists && !shortnameDotKeychainExists) { 1164 rename_stat = ::rename(shortnameKeychain.c_str(), shortnameDotKeychain.c_str()); 1165 shortnameDotKeychainExists = (rename_stat == 0); 1166 } else if (!loginKeychainExists) { 1167 rename_stat = ::rename(shortnameKeychain.c_str(), loginDotKeychain.c_str()); 1168 loginKeychainExists = (rename_stat == 0); 1169 } else { 1170 // we have all 3 keychains: login.keychain, shortname, and shortname.keychain. 1171 // on Leopard we never want a shortname keychain, so we must move it aside. 1172 char pathbuf[MAXPATHLEN]; 1173 std::string shortnameRenamedXXXKeychain = keychainPath; 1174 shortnameRenamedXXXKeychain += userName; 1175 shortnameRenamedXXXKeychain += "_renamed_XXX.keychain"; 1176 ::strlcpy(pathbuf, shortnameRenamedXXXKeychain.c_str(), sizeof(pathbuf)); 1177 ::mkstemps(pathbuf, 9); // 9 == strlen(".keychain") 1178 rename_stat = ::rename(shortnameKeychain.c_str(), pathbuf); 1179 shortnameKeychainExists = (rename_stat != 0); 1180 } 1181 } 1182 if (rename_stat != 0) { 1183 MacOSError::throwMe(errno); 1184 } 1185 } 1186 1187 //*************************************************************** 1188 // handle special case where user previously reset the keychain 1189 //*************************************************************** 1190 // Since 9A581, we have changed the definition of kKeychainRenamedSuffix from "_renamed" to "_renamed_". 1191 // Therefore, if "login_renamed1.keychain" exists and there is no plist, the user may have run into a 1192 // prior upgrade issue and clicked Reset. If we can successfully unlock login_renamed1.keychain with the 1193 // supplied password, then we will attempt to rename it to login.keychain if that file is empty, or with 1194 // "shortname.keychain" if it is not. 1195 1196 if (loginRenamed1KeychainExists && (!loginKeychainExists || 1197 (mSavedList.searchList().size() == 1 && mSavedList.member(loginDLDbIdentifier)) )) { 1198 try 1199 { 1200 Keychain loginRenamed1KC(keychain(loginRenamed1DLDbIdentifier)); 1201 secdebug("KCLogin", "Attempting to unlock %s with %d-character password", 1202 (loginRenamed1KC) ? loginRenamed1KC->name() : "<NULL>", (unsigned int)passwordLength); 1203 loginRenamed1KC->unlock(CssmData(const_cast<void *>(password), passwordLength)); 1204 // if we get here, we unlocked it 1205 if (loginKeychainExists) { 1206 struct stat st; 1207 int tmp_result = ::stat(loginDotKeychain.c_str(), &st); 1208 if (tmp_result == 0) { 1209 if (st.st_size <= kEmptyKeychainSizeInBytes) { 1210 tmp_result = ::unlink(loginDotKeychain.c_str()); 1211 tmp_result = ::rename(loginRenamed1Keychain.c_str(), loginDotKeychain.c_str()); 1212 } else if (!shortnameDotKeychainExists) { 1213 tmp_result = ::rename(loginRenamed1Keychain.c_str(), shortnameDotKeychain.c_str()); 1214 shortnameDotKeychainExists = (tmp_result == 0); 1215 } else { 1216 throw 1; // can't do anything with it except move it out of the way 1217 } 1218 } 1219 } else { 1220 int tmp_result = ::rename(loginRenamed1Keychain.c_str(), loginDotKeychain.c_str()); 1221 loginKeychainExists = (tmp_result == 0); 1222 } 1223 } 1224 catch(...) 1225 { 1226 // we failed to unlock the login_renamed1.keychain file with the login password. 1227 // move it aside so we don't try to deal with it again. 1228 char pathbuf[MAXPATHLEN]; 1229 std::string loginRenamedXXXKeychain = keychainPath; 1230 loginRenamedXXXKeychain += "login_renamed_XXX.keychain"; 1231 ::strlcpy(pathbuf, loginRenamedXXXKeychain.c_str(), sizeof(pathbuf)); 1232 ::mkstemps(pathbuf, 9); // 9 == strlen(".keychain") 1233 ::rename(loginRenamed1Keychain.c_str(), pathbuf); 1234 } 1235 } 1236 1237 // if login.keychain does not exist at this point, create it 1238 if (!loginKeychainExists) { 1239 Keychain theKeychain(keychain(loginDLDbIdentifier)); 1240 secdebug("KCLogin", "Creating login keychain %s", (loginDLDbIdentifier) ? loginDLDbIdentifier.dbName() : "<NULL>"); 1241 theKeychain->create(passwordLength, password); 1242 secdebug("KCLogin", "Login keychain created successfully"); 1243 loginKeychainExists = true; 1244 // Set the prefs for this new login keychain. 1245 loginKeychain(theKeychain); 1246 // Login Keychain does not lock on sleep nor lock after timeout by default. 1247 theKeychain->setSettings(INT_MAX, false); 1248 loginUnlocked = true; 1249 mSavedList.revert(true); 1250 } 1251 1252 //*************************************************************** 1253 // make plist changes after files have been renamed or created 1254 //*************************************************************** 1255 1256 // if the shortname keychain exists in the search list, either rename or remove the entry 1257 if (mSavedList.member(shortnameDLDbIdentifier)) { 1258 if (shortnameDotKeychainExists && !mSavedList.member(shortnameDotDLDbIdentifier)) { 1259 // change shortname to shortname.keychain (login.keychain will be added later if not present) 1260 secdebug("KCLogin", "Renaming %s to %s in keychain search list", 1261 (shortnameDLDbIdentifier) ? shortnameDLDbIdentifier.dbName() : "<NULL>", 1262 (shortnameDotDLDbIdentifier) ? shortnameDotDLDbIdentifier.dbName() : "<NULL>"); 1263 mSavedList.rename(shortnameDLDbIdentifier, shortnameDotDLDbIdentifier); 1264 } else if (!mSavedList.member(loginDLDbIdentifier)) { 1265 // change shortname to login.keychain 1266 secdebug("KCLogin", "Renaming %s to %s in keychain search list", 1267 (shortnameDLDbIdentifier) ? shortnameDLDbIdentifier.dbName() : "<NULL>", 1268 (loginDLDbIdentifier) ? loginDLDbIdentifier.dbName() : "<NULL>"); 1269 mSavedList.rename(shortnameDLDbIdentifier, loginDLDbIdentifier); 1270 } else { 1271 // already have login.keychain in list, and renaming to shortname.keychain isn't an option, 1272 // so just remove the entry 1273 secdebug("KCLogin", "Removing %s from keychain search list", (shortnameDLDbIdentifier) ? shortnameDLDbIdentifier.dbName() : "<NULL>"); 1274 mSavedList.remove(shortnameDLDbIdentifier); 1275 } 1276 1277 // note: save() will cause the plist to be unlinked if the only remaining entry is for login.keychain 1278 mSavedList.save(); 1279 mSavedList.revert(true); 1280 } 1281 1282 // make sure that login.keychain is in the search list 1283 if (!mSavedList.member(loginDLDbIdentifier)) { 1284 secdebug("KCLogin", "Adding %s to keychain search list", (loginDLDbIdentifier) ? loginDLDbIdentifier.dbName() : "<NULL>"); 1285 mSavedList.add(loginDLDbIdentifier); 1286 mSavedList.save(); 1287 mSavedList.revert(true); 1288 } 1289 1290 // if we have a shortname.keychain, always include it in the plist (after login.keychain) 1291 if (shortnameDotKeychainExists && !mSavedList.member(shortnameDotDLDbIdentifier)) { 1292 mSavedList.add(shortnameDotDLDbIdentifier); 1293 mSavedList.save(); 1294 mSavedList.revert(true); 1295 } 1296 1297 // make sure that the default keychain is in the search list; if not, reset the default to login.keychain 1298 if (!mSavedList.member(mSavedList.defaultDLDbIdentifier())) { 1299 secdebug("KCLogin", "Changing default keychain to %s", (loginDLDbIdentifier) ? loginDLDbIdentifier.dbName() : "<NULL>"); 1300 mSavedList.defaultDLDbIdentifier(loginDLDbIdentifier); 1301 mSavedList.save(); 1302 mSavedList.revert(true); 1303 } 1304 1305 //*************************************************************** 1306 // auto-unlock the login keychain(s) 1307 //*************************************************************** 1308 // all our preflight fixups are finally done, so we can now attempt to unlock the login keychain 1309 1310 OSStatus loginResult = errSecSuccess; 1311 if (!loginUnlocked) { 1312 try 1313 { 1314 Keychain theKeychain(keychain(loginDLDbIdentifier)); 1315 secdebug("KCLogin", "Attempting to unlock login keychain \"%s\" with %d-character password", 1316 (theKeychain) ? theKeychain->name() : "<NULL>", (unsigned int)passwordLength); 1317 theKeychain->unlock(CssmData(const_cast<void *>(password), passwordLength)); 1318 loginUnlocked = true; 1319 } 1320 catch(const CssmError &e) 1321 { 1322 loginResult = e.osStatus(); // save this result 1323 } 1324 } 1325 1326 if (!loginUnlocked) { 1327 try { 1328 loginResult = errSecSuccess; 1329 Keychain theKeychain(keychain(loginDLDbIdentifier)); 1330 1331 // build a fake key 1332 CssmKey key; 1333 key.header().BlobType = CSSM_KEYBLOB_RAW; 1334 key.header().Format = CSSM_KEYBLOB_RAW_FORMAT_OCTET_STRING; 1335 key.header().AlgorithmId = CSSM_ALGID_3DES_3KEY; 1336 key.header().KeyClass = CSSM_KEYCLASS_SESSION_KEY; 1337 key.header().KeyUsage = CSSM_KEYUSE_ENCRYPT | CSSM_KEYUSE_DECRYPT | CSSM_KEYATTR_EXTRACTABLE; 1338 key.header().KeyAttr = 0; 1339 key.KeyData = CssmData(const_cast<void *>(password), passwordLength); 1340 1341 // unwrap it into the CSP (but keep it raw) 1342 UnwrapKey unwrap(theKeychain->csp(), CSSM_ALGID_NONE); 1343 CssmKey masterKey; 1344 CssmData descriptiveData; 1345 unwrap(key, 1346 KeySpec(CSSM_KEYUSE_ANY, CSSM_KEYATTR_EXTRACTABLE), 1347 masterKey, &descriptiveData, NULL); 1348 1349 CssmClient::Db db = theKeychain->database(); 1350 1351 // create the keychain, using appropriate credentials 1352 Allocator &alloc = db->allocator(); 1353 AutoCredentials cred(alloc); // will leak, but we're quitting soon :-) 1354 1355 // use this passphrase 1356 cred += TypedList(alloc, CSSM_SAMPLE_TYPE_KEYCHAIN_LOCK, 1357 new(alloc) ListElement(CSSM_SAMPLE_TYPE_SYMMETRIC_KEY), 1358 new(alloc) ListElement(CssmData::wrap(theKeychain->csp()->handle())), 1359 new(alloc) ListElement(CssmData::wrap(masterKey)), 1360 new(alloc) ListElement(CssmData())); 1361 db->authenticate(CSSM_DB_ACCESS_READ, &cred); 1362 db->unlock(); 1363 loginUnlocked = true; 1364 } catch (const CssmError &e) { 1365 loginResult = e.osStatus(); 1366 } 1367 } 1368 1369 // if "shortname.keychain" exists and is in the search list, attempt to auto-unlock it with the same password 1370 if (shortnameDotKeychainExists && mSavedList.member(shortnameDotDLDbIdentifier)) { 1371 try 1372 { 1373 Keychain shortnameDotKC(keychain(shortnameDotDLDbIdentifier)); 1374 secdebug("KCLogin", "Attempting to unlock %s", 1375 (shortnameDotKC) ? shortnameDotKC->name() : "<NULL>"); 1376 shortnameDotKC->unlock(CssmData(const_cast<void *>(password), passwordLength)); 1377 } 1378 catch(const CssmError &e) 1379 { 1380 // ignore; failure to unlock this keychain is not considered an error 1381 } 1382 } 1383 1384 if (loginResult != errSecSuccess) { 1385 MacOSError::throwMe(loginResult); 1386 } 1387} 1388 1389void StorageManager::stashLogin() 1390{ 1391 OSStatus loginResult = errSecSuccess; 1392 1393 DLDbIdentifier loginDLDbIdentifier; 1394 { 1395 mSavedList.revert(true); 1396 loginDLDbIdentifier = mSavedList.loginDLDbIdentifier(); 1397 } 1398 1399 secdebug("KCLogin", "StorageManager::stash: loginDLDbIdentifier is %s", (loginDLDbIdentifier) ? loginDLDbIdentifier.dbName() : "<NULL>"); 1400 if (!loginDLDbIdentifier) 1401 MacOSError::throwMe(errSecNoSuchKeychain); 1402 1403 try 1404 { 1405 CssmData empty; 1406 Keychain theKeychain(keychain(loginDLDbIdentifier)); 1407 secdebug("KCLogin", "Attempting to use stash for login keychain \"%s\"", 1408 (theKeychain) ? theKeychain->name() : "<NULL>"); 1409 theKeychain->stashCheck(); 1410 } 1411 catch(const CssmError &e) 1412 { 1413 loginResult = e.osStatus(); // save this result 1414 } 1415 1416 1417 if (loginResult != errSecSuccess) { 1418 MacOSError::throwMe(loginResult); 1419 } 1420} 1421 1422void StorageManager::stashKeychain() 1423{ 1424 OSStatus loginResult = errSecSuccess; 1425 1426 DLDbIdentifier loginDLDbIdentifier; 1427 { 1428 mSavedList.revert(true); 1429 loginDLDbIdentifier = mSavedList.loginDLDbIdentifier(); 1430 } 1431 1432 secdebug("KCLogin", "StorageManager::stash: loginDLDbIdentifier is %s", (loginDLDbIdentifier) ? loginDLDbIdentifier.dbName() : "<NULL>"); 1433 if (!loginDLDbIdentifier) 1434 MacOSError::throwMe(errSecNoSuchKeychain); 1435 1436 try 1437 { 1438 Keychain theKeychain(keychain(loginDLDbIdentifier)); 1439 secdebug("KCLogin", "Attempting to stash login keychain \"%s\"", 1440 (theKeychain) ? theKeychain->name() : "<NULL>"); 1441 theKeychain->stash(); 1442 } 1443 catch(const CssmError &e) 1444 { 1445 loginResult = e.osStatus(); // save this result 1446 } 1447 1448 1449 if (loginResult != errSecSuccess) { 1450 MacOSError::throwMe(loginResult); 1451 } 1452} 1453 1454void StorageManager::logout() 1455{ 1456 // nothing left to do here 1457} 1458 1459void StorageManager::changeLoginPassword(ConstStringPtr oldPassword, ConstStringPtr newPassword) 1460{ 1461 StLock<Mutex>_(mMutex); 1462 1463 loginKeychain()->changePassphrase(oldPassword, newPassword); 1464 secdebug("KClogin", "Changed login keychain password successfully"); 1465} 1466 1467 1468void StorageManager::changeLoginPassword(UInt32 oldPasswordLength, const void *oldPassword, UInt32 newPasswordLength, const void *newPassword) 1469{ 1470 StLock<Mutex>_(mMutex); 1471 1472 loginKeychain()->changePassphrase(oldPasswordLength, oldPassword, newPasswordLength, newPassword); 1473 secdebug("KClogin", "Changed login keychain password successfully"); 1474} 1475 1476// Clear out the keychain search list and rename the existing login.keychain. 1477// 1478void StorageManager::resetKeychain(Boolean resetSearchList) 1479{ 1480 StLock<Mutex>_(mMutex); 1481 1482 // Clear the keychain search list. 1483 try 1484 { 1485 if ( resetSearchList ) 1486 { 1487 StorageManager::KeychainList keychainList; 1488 setSearchList(keychainList); 1489 } 1490 // Get a reference to the existing login keychain... 1491 // If we don't have one, we throw (not requiring a rename). 1492 // 1493 Keychain keychain = loginKeychain(); 1494 // 1495 // Rename the existing login.keychain (i.e. put it aside). 1496 // 1497 CFMutableStringRef newName = NULL; 1498 newName = CFStringCreateMutable(NULL, 0); 1499 CFStringRef currName = NULL; 1500 currName = CFStringCreateWithCString(NULL, keychain->name(), kCFStringEncodingUTF8); 1501 if ( newName && currName ) 1502 { 1503 CFStringAppend(newName, currName); 1504 CFStringRef kcSuffix = CFSTR(kKeychainSuffix); 1505 if ( CFStringHasSuffix(newName, kcSuffix) ) // remove the .keychain extension 1506 { 1507 CFRange suffixRange = CFStringFind(newName, kcSuffix, 0); 1508 CFStringFindAndReplace(newName, kcSuffix, CFSTR(""), suffixRange, 0); 1509 } 1510 CFStringAppend(newName, CFSTR(kKeychainRenamedSuffix)); // add "_renamed_" 1511 try 1512 { 1513 renameUnique(keychain, newName); 1514 } 1515 catch(...) 1516 { 1517 // we need to release 'newName' & 'currName' 1518 } 1519 } // else, let the login call report a duplicate 1520 if ( newName ) 1521 CFRelease(newName); 1522 if ( currName ) 1523 CFRelease(currName); 1524 } 1525 catch(...) 1526 { 1527 // We either don't have a login keychain, or there was a 1528 // failure to rename the existing one. 1529 } 1530} 1531 1532#pragma mark ____ File Related ____ 1533 1534Keychain StorageManager::make(const char *pathName) 1535{ 1536 return make(pathName, true); 1537} 1538 1539Keychain StorageManager::make(const char *pathName, bool add) 1540{ 1541 StLock<Mutex>_(mMutex); 1542 1543 string fullPathName; 1544 if ( pathName[0] == '/' ) 1545 fullPathName = pathName; 1546 else 1547 { 1548 // Get Home directory from environment. 1549 switch (mDomain) 1550 { 1551 case kSecPreferencesDomainUser: 1552 { 1553 const char *homeDir = getenv("HOME"); 1554 if (homeDir == NULL) 1555 { 1556 // If $HOME is unset get the current user's home directory 1557 // from the passwd file. 1558 uid_t uid = geteuid(); 1559 if (!uid) uid = getuid(); 1560 struct passwd *pw = getpwuid(uid); 1561 if (!pw) 1562 MacOSError::throwMe(errSecParam); 1563 homeDir = pw->pw_dir; 1564 } 1565 fullPathName = homeDir; 1566 } 1567 break; 1568 case kSecPreferencesDomainSystem: 1569 fullPathName = ""; 1570 break; 1571 default: 1572 assert(false); // invalid domain for this 1573 } 1574 1575 fullPathName += "/Library/Keychains/"; 1576 fullPathName += pathName; 1577 } 1578 1579 const CSSM_NET_ADDRESS *DbLocation = NULL; // NULL for keychains 1580 const CSSM_VERSION *version = NULL; 1581 uint32 subserviceId = 0; 1582 CSSM_SERVICE_TYPE subserviceType = CSSM_SERVICE_DL | CSSM_SERVICE_CSP; 1583 const CssmSubserviceUid ssuid(gGuidAppleCSPDL, version, 1584 subserviceId, subserviceType); 1585 DLDbIdentifier dLDbIdentifier(ssuid, fullPathName.c_str(), DbLocation); 1586 return makeKeychain(dLDbIdentifier, add); 1587} 1588 1589Keychain StorageManager::makeLoginAuthUI(const Item *item) 1590{ 1591 StLock<Mutex>_(mMutex); 1592 1593 // Create a login/default keychain for the user using UI. 1594 // The user can cancel out of the operation, or create a new login keychain. 1595 // If auto-login is turned off, the user will be asked for their login password. 1596 // 1597 OSStatus result = errSecSuccess; 1598 Keychain keychain; // We return this keychain. 1599 // 1600 // Set up the Auth ref to bring up UI. 1601 // 1602 AuthorizationItem *currItem, *authEnvirItemArrayPtr = NULL; 1603 AuthorizationRef authRef = NULL; 1604 try 1605 { 1606 result = AuthorizationCreate(NULL, NULL, kAuthorizationFlagDefaults, &authRef); 1607 if ( result ) 1608 MacOSError::throwMe(result); 1609 1610 AuthorizationEnvironment envir; 1611 envir.count = 6; // up to 6 hints can be used. 1612 authEnvirItemArrayPtr = (AuthorizationItem*)malloc(sizeof(AuthorizationItem) * envir.count); 1613 if ( !authEnvirItemArrayPtr ) 1614 MacOSError::throwMe(errAuthorizationInternal); 1615 1616 currItem = envir.items = authEnvirItemArrayPtr; 1617 1618 // 1619 // 1st Hint (optional): The keychain item's account attribute string. 1620 // When item is specified, we assume an 'add' operation is being attempted. 1621 char buff[256]; 1622 UInt32 actLen = 0; 1623 SecKeychainAttribute attr = { kSecAccountItemAttr, 255, &buff }; 1624 if ( item ) 1625 { 1626 try 1627 { 1628 (*item)->getAttribute(attr, &actLen); 1629 } 1630 catch(...) 1631 { 1632 actLen = 0; // This item didn't have the account attribute, so don't display one in the UI. 1633 } 1634 } 1635 currItem->name = AGENT_HINT_ATTR_NAME; // name str that identifies this hint as attr name 1636 if ( actLen ) // Fill in the hint if we have an account attr 1637 { 1638 if ( actLen >= sizeof(buff) ) 1639 buff[sizeof(buff)-1] = 0; 1640 else 1641 buff[actLen] = 0; 1642 currItem->valueLength = strlen(buff)+1; 1643 currItem->value = buff; 1644 } 1645 else 1646 { 1647 currItem->valueLength = 0; 1648 currItem->value = NULL; 1649 } 1650 currItem->flags = 0; 1651 1652 // 1653 // 2nd Hint (optional): The item's keychain full path. 1654 // 1655 currItem++; 1656 char* currDefaultName = NULL; 1657 try 1658 { 1659 currDefaultName = (char*)defaultKeychain()->name(); // Use the name if we have it. 1660 currItem->name = AGENT_HINT_LOGIN_KC_NAME; // Name str that identifies this hint as kc path 1661 currItem->valueLength = (currDefaultName) ? strlen(currDefaultName) : 0; 1662 currItem->value = (currDefaultName) ? (void*)currDefaultName : (void*)""; 1663 currItem->flags = 0; 1664 currItem++; 1665 } 1666 catch(...) 1667 { 1668 envir.count--; 1669 } 1670 1671 // 1672 // 3rd Hint (required): check if curr default keychain is unavailable. 1673 // This is determined by the parent not existing. 1674 // 1675 currItem->name = AGENT_HINT_LOGIN_KC_EXISTS_IN_KC_FOLDER; 1676 Boolean loginUnavail = false; 1677 try 1678 { 1679 Keychain defaultKC = defaultKeychain(); 1680 if ( !defaultKC->exists() ) 1681 loginUnavail = true; 1682 } 1683 catch(...) // login.keychain not present 1684 { 1685 } 1686 currItem->valueLength = sizeof(Boolean); 1687 currItem->value = (void*)&loginUnavail; 1688 currItem->flags = 0; 1689 1690 // 1691 // 4th Hint (required): userName 1692 // 1693 currItem++; 1694 currItem->name = AGENT_HINT_LOGIN_KC_USER_NAME; 1695 char* uName = getenv("USER"); 1696 string userName = uName ? uName : ""; 1697 if ( userName.length() == 0 ) 1698 { 1699 uid_t uid = geteuid(); 1700 if (!uid) uid = getuid(); 1701 struct passwd *pw = getpwuid(uid); // fallback case... 1702 if (pw) 1703 userName = pw->pw_name; 1704 endpwent(); 1705 } 1706 if ( userName.length() == 0 ) // did we ultimately get one? 1707 MacOSError::throwMe(errAuthorizationInternal); 1708 1709 currItem->value = (void*)userName.c_str(); 1710 currItem->valueLength = userName.length(); 1711 currItem->flags = 0; 1712 1713 // 1714 // 5th Hint (required): flags if user has more than 1 keychain (used for a later warning when reset to default). 1715 // 1716 currItem++; 1717 currItem->name = AGENT_HINT_LOGIN_KC_USER_HAS_OTHER_KCS_STR; 1718 Boolean moreThanOneKCExists = false; 1719 { 1720 // if item is NULL, then this is a user-initiated full reset 1721 if (item && mSavedList.searchList().size() > 1) 1722 moreThanOneKCExists = true; 1723 } 1724 currItem->value = &moreThanOneKCExists; 1725 currItem->valueLength = sizeof(Boolean); 1726 currItem->flags = 0; 1727 1728 // 1729 // 6th Hint (required): If no item is involved, this is a user-initiated full reset. 1730 // We want to suppress the "do you want to reset to defaults?" panel in this case. 1731 // 1732 currItem++; 1733 currItem->name = AGENT_HINT_LOGIN_KC_SUPPRESS_RESET_PANEL; 1734 Boolean suppressResetPanel = (item == NULL) ? TRUE : FALSE; 1735 currItem->valueLength = sizeof(Boolean); 1736 currItem->value = (void*)&suppressResetPanel; 1737 currItem->flags = 0; 1738 1739 // 1740 // Set up the auth rights and make the auth call. 1741 // 1742 AuthorizationItem authItem = { LOGIN_KC_CREATION_RIGHT, 0 , NULL, 0 }; 1743 AuthorizationRights rights = { 1, &authItem }; 1744 AuthorizationFlags flags = kAuthorizationFlagDefaults | kAuthorizationFlagInteractionAllowed | kAuthorizationFlagExtendRights; 1745 result = AuthorizationCopyRights(authRef, &rights, &envir, flags, NULL); 1746 if ( result ) 1747 MacOSError::throwMe(result); 1748 try 1749 { 1750 resetKeychain(true); // Clears the plist, moves aside existing login.keychain 1751 } 1752 catch (...) // can throw if no existing login.keychain is found 1753 { 1754 } 1755 login(authRef, (UInt32)userName.length(), userName.c_str()); // Create login.keychain 1756 keychain = loginKeychain(); // Get newly-created login keychain 1757 defaultKeychain(keychain); // Set it to be the default 1758 1759 free(authEnvirItemArrayPtr); 1760 AuthorizationFree(authRef, kAuthorizationFlagDefaults); 1761 } 1762 1763 catch (...) 1764 { 1765 // clean up allocations, then rethrow error 1766 if ( authEnvirItemArrayPtr ) 1767 free(authEnvirItemArrayPtr); 1768 if ( authRef ) 1769 AuthorizationFree(authRef, kAuthorizationFlagDefaults); 1770 throw; 1771 } 1772 1773 return keychain; 1774} 1775 1776Keychain StorageManager::defaultKeychainUI(Item &item) 1777{ 1778 StLock<Mutex>_(mMutex); 1779 1780 Keychain returnedKeychain; 1781 try 1782 { 1783 returnedKeychain = defaultKeychain(); // If we have one, return it. 1784 if ( returnedKeychain->exists() ) 1785 return returnedKeychain; 1786 } 1787 catch(...) // We could have one, but it isn't available (i.e. on a un-mounted volume). 1788 { 1789 } 1790 if ( globals().getUserInteractionAllowed() ) 1791 { 1792 returnedKeychain = makeLoginAuthUI(&item); // If no Keychains is present, one will be created. 1793 if ( !returnedKeychain ) 1794 MacOSError::throwMe(errSecInvalidKeychain); // Something went wrong... 1795 } 1796 else 1797 MacOSError::throwMe(errSecInteractionNotAllowed); // If UI isn't allowed, return an error. 1798 1799 return returnedKeychain; 1800} 1801 1802void 1803StorageManager::addToDomainList(SecPreferencesDomain domain, 1804 const char* dbName, const CSSM_GUID &guid, uint32 subServiceType) 1805{ 1806 StLock<Mutex>_(mMutex); 1807 1808 if (domain == kSecPreferencesDomainDynamic) 1809 MacOSError::throwMe(errSecInvalidPrefsDomain); 1810 1811 // make the identifier 1812 CSSM_VERSION version = {0, 0}; 1813 DLDbIdentifier id = DLDbListCFPref::makeDLDbIdentifier (guid, version, 0, 1814 subServiceType, dbName, NULL); 1815 1816 if (domain == mDomain) 1817 { 1818 // manipulate the user's list 1819 { 1820 mSavedList.revert(true); 1821 mSavedList.add(id); 1822 mSavedList.save(); 1823 } 1824 1825 KCEventNotifier::PostKeychainEvent(kSecKeychainListChangedEvent); 1826 } 1827 else 1828 { 1829 // manipulate the other list 1830 DLDbListCFPref(domain).add(id); 1831 } 1832} 1833 1834void 1835StorageManager::isInDomainList(SecPreferencesDomain domain, 1836 const char* dbName, const CSSM_GUID &guid, uint32 subServiceType) 1837{ 1838 StLock<Mutex>_(mMutex); 1839 1840 if (domain == kSecPreferencesDomainDynamic) 1841 MacOSError::throwMe(errSecInvalidPrefsDomain); 1842 1843 CSSM_VERSION version = {0, 0}; 1844 DLDbIdentifier id = DLDbListCFPref::makeDLDbIdentifier (guid, version, 0, 1845 subServiceType, dbName, NULL); 1846 1847 // determine the list to search 1848 bool result; 1849 if (domain == mDomain) 1850 { 1851 result = mSavedList.member(id); 1852 } 1853 else 1854 { 1855 result = DLDbListCFPref(domain).member(id); 1856 } 1857 1858 // do the search 1859 if (!result) 1860 { 1861 MacOSError::throwMe(errSecNoSuchKeychain); 1862 } 1863} 1864 1865void 1866StorageManager::removeFromDomainList(SecPreferencesDomain domain, 1867 const char* dbName, const CSSM_GUID &guid, uint32 subServiceType) 1868{ 1869 StLock<Mutex>_(mMutex); 1870 1871 if (domain == kSecPreferencesDomainDynamic) 1872 MacOSError::throwMe(errSecInvalidPrefsDomain); 1873 1874 // make the identifier 1875 CSSM_VERSION version = {0, 0}; 1876 DLDbIdentifier id = DLDbListCFPref::makeDLDbIdentifier (guid, version, 0, 1877 subServiceType, dbName, NULL); 1878 1879 if (domain == mDomain) 1880 { 1881 // manipulate the user's list 1882 { 1883 mSavedList.revert(true); 1884 mSavedList.remove(id); 1885 mSavedList.save(); 1886 } 1887 1888 KCEventNotifier::PostKeychainEvent(kSecKeychainListChangedEvent); 1889 } 1890 else 1891 { 1892 // manipulate the other list 1893 DLDbListCFPref(domain).remove(id); 1894 } 1895} 1896 1897bool 1898StorageManager::keychainOwnerPermissionsValidForDomain(const char* path, SecPreferencesDomain domain) 1899{ 1900 struct stat sb; 1901 mode_t perms; 1902 const char* sysPrefDir = "/Library/Preferences"; 1903 const char* errMsg = "Will not set default"; 1904 char* mustOwnDir = NULL; 1905 struct passwd* pw = NULL; 1906 1907 // get my uid 1908 uid_t uid = geteuid(); 1909 if (!uid) uid = getuid(); 1910 1911 // our (e)uid must own the appropriate preferences or home directory 1912 // for the specified preference domain whose default we will be modifying 1913 switch (domain) { 1914 case kSecPreferencesDomainUser: 1915 mustOwnDir = getenv("HOME"); 1916 if (mustOwnDir == NULL) { 1917 pw = getpwuid(uid); 1918 if (!pw) return false; 1919 mustOwnDir = pw->pw_dir; 1920 } 1921 break; 1922 case kSecPreferencesDomainSystem: 1923 mustOwnDir = (char*)sysPrefDir; 1924 break; 1925 case kSecPreferencesDomainCommon: 1926 mustOwnDir = (char*)sysPrefDir; 1927 break; 1928 default: 1929 return false; 1930 } 1931 1932 if (mustOwnDir != NULL) { 1933 struct stat dsb; 1934 if ( (stat(mustOwnDir, &dsb) != 0) || (dsb.st_uid != uid) ) { 1935 fprintf(stderr, "%s: UID=%d does not own directory %s\n", errMsg, (int)uid, mustOwnDir); 1936 mustOwnDir = NULL; // will return below after calling endpwent() 1937 } 1938 } 1939 1940 if (pw != NULL) 1941 endpwent(); 1942 1943 if (mustOwnDir == NULL) 1944 return false; 1945 1946 // check that file actually exists 1947 if (stat(path, &sb) != 0) { 1948 fprintf(stderr, "%s: file %s does not exist\n", errMsg, path); 1949 return false; 1950 } 1951 1952 // check flags 1953 if (sb.st_flags & (SF_IMMUTABLE | UF_IMMUTABLE)) { 1954 fprintf(stderr, "%s: file %s is immutable\n", errMsg, path); 1955 return false; 1956 } 1957 1958 // check ownership 1959 if (sb.st_uid != uid) { 1960 fprintf(stderr, "%s: file %s is owned by UID=%d, but we have UID=%d\n", 1961 errMsg, path, (int)sb.st_uid, (int)uid); 1962 return false; 1963 } 1964 1965 // check mode 1966 perms = sb.st_mode; 1967 perms |= 0600; // must have owner read/write permission set 1968 if (sb.st_mode != perms) { 1969 fprintf(stderr, "%s: file %s does not have the expected permissions\n", errMsg, path); 1970 return false; 1971 } 1972 1973 // user owns file and can read/write it 1974 return true; 1975} 1976