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