2 * Copyright (c) 2000-2004,2011-2014 Apple Inc. All Rights Reserved.
4 * @APPLE_LICENSE_HEADER_START@
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
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.
21 * @APPLE_LICENSE_HEADER_END@
26 File: StorageManager.cpp
28 Contains: Working with multiple keychains
32 #include "StorageManager.h"
33 #include "KCEventNotifier.h"
35 #include <Security/cssmapple.h>
36 #include <sys/types.h>
37 #include <sys/param.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 #include <securityd_client/ssblob.h>
57 //%%% add this to AuthorizationTagsPriv.h later
58 #ifndef AGENT_HINT_LOGIN_KC_SUPPRESS_RESET_PANEL
59 #define AGENT_HINT_LOGIN_KC_SUPPRESS_RESET_PANEL "loginKCCreate:suppressResetPanel"
66 using namespace CssmClient
;
67 using namespace KeychainCore
;
69 #define kLoginKeychainPathPrefix "~/Library/Keychains/"
70 #define kUserLoginKeychainPath "~/Library/Keychains/login.keychain"
71 #define kEmptyKeychainSizeInBytes 20460
73 //-----------------------------------------------------------------------------------
75 static SecPreferencesDomain
defaultPreferenceDomain()
77 SessionAttributeBits sessionAttrs
;
79 secdebug("servermode", "StorageManager initialized in server mode");
80 sessionAttrs
= sessionIsRoot
;
82 MacOSError::check(SessionGetInfo(callerSecuritySession
, NULL
, &sessionAttrs
));
85 // If this is the root session, use system preferences.
86 // (In SecurityServer debug mode, you'll get a (fake) root session
87 // that has graphics access. Ignore that to help testing.)
88 if ((sessionAttrs
& sessionIsRoot
)
89 IFDEBUG( && !(sessionAttrs
& sessionHasGraphicAccess
))) {
90 secdebug("storagemgr", "using system preferences");
91 return kSecPreferencesDomainSystem
;
94 // otherwise, use normal (user) preferences
95 return kSecPreferencesDomainUser
;
98 static bool isAppSandboxed()
101 SecTaskRef task
= SecTaskCreateFromSelf(NULL
);
103 CFTypeRef appSandboxValue
= SecTaskCopyValueForEntitlement(task
,
104 CFSTR("com.apple.security.app-sandbox"), NULL
);
105 if(appSandboxValue
!= NULL
) {
107 CFRelease(appSandboxValue
);
114 static bool shouldAddToSearchList(const DLDbIdentifier
&dLDbIdentifier
)
116 // Creation of a private keychain should not modify the search list: rdar://13529331
117 // However, we want to ensure the login and System keychains are in
118 // the search list if that is not the case when they are created.
119 // Note that App Sandbox apps may not modify the list in either case.
121 bool loginOrSystemKeychain
= false;
122 const char *dbname
= dLDbIdentifier
.dbName();
124 if ((!strcmp(dbname
, "/Library/Keychains/System.keychain")) ||
125 (strstr(dbname
, "/login.keychain")) ) {
126 loginOrSystemKeychain
= true;
129 return (loginOrSystemKeychain
&& !isAppSandboxed());
133 StorageManager::StorageManager() :
134 mSavedList(defaultPreferenceDomain()),
135 mCommonList(kSecPreferencesDomainCommon
),
136 mDomain(kSecPreferencesDomainUser
),
137 mMutex(Mutex::recursive
)
143 StorageManager::getStorageManagerMutex()
145 return &mKeychainMapMutex
;
150 StorageManager::keychain(const DLDbIdentifier
&dLDbIdentifier
)
152 StLock
<Mutex
>_(mKeychainMapMutex
);
157 KeychainMap::iterator it
= mKeychains
.find(dLDbIdentifier
);
158 if (it
!= mKeychains
.end())
164 secdebug("servermode", "keychain reference in server mode");
168 // The keychain is not in our cache. Create it.
169 Db
db(makeDb(dLDbIdentifier
));
171 Keychain
keychain(db
);
172 // Add the keychain to the cache.
173 mKeychains
.insert(KeychainMap::value_type(dLDbIdentifier
, &*keychain
));
174 keychain
->inCache(true);
180 StorageManager::makeDb(DLDbIdentifier dLDbIdentifier
) {
181 Module
module(dLDbIdentifier
.ssuid().guid());
184 if (dLDbIdentifier
.ssuid().subserviceType() & CSSM_SERVICE_CSP
)
185 dl
= SSCSPDL(module);
189 dl
->subserviceId(dLDbIdentifier
.ssuid().subserviceId());
190 dl
->version(dLDbIdentifier
.ssuid().version());
192 CssmClient::Db
db(dl
, dLDbIdentifier
.dbName());
198 StorageManager::reloadKeychain(Keychain keychain
) {
199 StLock
<Mutex
>_(mKeychainMapMutex
);
201 DLDbIdentifier dLDbIdentifier
= keychain
->database()->dlDbIdentifier();
203 // Since we're going to reload this database and switch over the keychain's
204 // mDb, grab its mDb mutex
206 StLock
<Mutex
>__(keychain
->mDbMutex
);
208 CssmClient::Db
db(makeDb(dLDbIdentifier
));
212 // Since this new database is based on the exact same dLDbIdentifier, we
213 // don't need to update the mKeychains map.
217 StorageManager::removeKeychain(const DLDbIdentifier
&dLDbIdentifier
,
218 KeychainImpl
*keychainImpl
)
220 // Lock the recursive mutex
222 StLock
<Mutex
>_(mKeychainMapMutex
);
224 KeychainMap::iterator it
= mKeychains
.find(dLDbIdentifier
);
225 if (it
!= mKeychains
.end() && (KeychainImpl
*) it
->second
== keychainImpl
)
226 mKeychains
.erase(it
);
228 keychainImpl
->inCache(false);
232 StorageManager::didRemoveKeychain(const DLDbIdentifier
&dLDbIdentifier
)
234 // Lock the recursive mutex
236 StLock
<Mutex
>_(mKeychainMapMutex
);
238 KeychainMap::iterator it
= mKeychains
.find(dLDbIdentifier
);
239 if (it
!= mKeychains
.end())
241 mKeychains
.erase(it
);
245 // Create keychain if it doesn't exist, and optionally add it to the search list.
247 StorageManager::makeKeychain(const DLDbIdentifier
&dLDbIdentifier
, bool add
)
249 StLock
<Mutex
>_(mKeychainMapMutex
);
251 Keychain theKeychain
= keychain(dLDbIdentifier
);
253 bool updateList
= (add
&& shouldAddToSearchList(dLDbIdentifier
));
257 mSavedList
.revert(false);
258 DLDbList searchList
= mSavedList
.searchList();
259 if (find(searchList
.begin(), searchList
.end(), dLDbIdentifier
) != searchList
.end())
260 return theKeychain
; // theKeychain is already in the searchList.
262 mCommonList
.revert(false);
263 searchList
= mCommonList
.searchList();
264 if (find(searchList
.begin(), searchList
.end(), dLDbIdentifier
) != searchList
.end())
265 return theKeychain
; // theKeychain is already in the commonList don't add it to the searchList.
267 // If theKeychain doesn't exist don't bother adding it to the search list yet.
268 if (!theKeychain
->exists())
271 // theKeychain exists and is not in our search list, so add it to the
273 mSavedList
.revert(true);
274 mSavedList
.add(dLDbIdentifier
);
281 // Make sure we are not holding mStorageManagerLock anymore when we
283 KCEventNotifier::PostKeychainEvent(kSecKeychainListChangedEvent
);
289 // Be notified a Keychain just got created.
291 StorageManager::created(const Keychain
&keychain
)
293 StLock
<Mutex
>_(mKeychainMapMutex
);
295 DLDbIdentifier dLDbIdentifier
= keychain
->dlDbIdentifier();
296 bool defaultChanged
= false;
297 bool updateList
= shouldAddToSearchList(dLDbIdentifier
);
301 mSavedList
.revert(true);
302 // If we don't have a default Keychain yet. Make the newly created
303 // keychain the default.
304 if (!mSavedList
.defaultDLDbIdentifier())
306 mSavedList
.defaultDLDbIdentifier(dLDbIdentifier
);
307 defaultChanged
= true;
310 // Add the keychain to the search list prefs.
311 mSavedList
.add(dLDbIdentifier
);
314 // Make sure we are not holding mLock when we post these events.
315 KCEventNotifier::PostKeychainEvent(kSecKeychainListChangedEvent
);
320 KCEventNotifier::PostKeychainEvent(kSecDefaultChangedEvent
, dLDbIdentifier
);
325 StorageManager::createCursor(SecItemClass itemClass
,
326 const SecKeychainAttributeList
*attrList
)
328 StLock
<Mutex
>_(mMutex
);
330 KeychainList searchList
;
331 getSearchList(searchList
);
332 return KCCursor(searchList
, itemClass
, attrList
);
336 StorageManager::createCursor(const SecKeychainAttributeList
*attrList
)
338 StLock
<Mutex
>_(mMutex
);
340 KeychainList searchList
;
341 getSearchList(searchList
);
342 return KCCursor(searchList
, attrList
);
346 StorageManager::lockAll()
348 StLock
<Mutex
>_(mMutex
);
350 SecurityServer::ClientSession
ss(Allocator::standard(), Allocator::standard());
355 StorageManager::defaultKeychain()
357 StLock
<Mutex
>_(mMutex
);
359 Keychain theKeychain
;
363 mSavedList
.revert(false);
364 DLDbIdentifier
defaultDLDbIdentifier(mSavedList
.defaultDLDbIdentifier());
365 if (defaultDLDbIdentifier
)
367 theKeychain
= keychain(defaultDLDbIdentifier
);
368 ref
= theKeychain
->handle(false);
372 if (theKeychain
/* && theKeychain->exists() */)
375 MacOSError::throwMe(errSecNoDefaultKeychain
);
379 StorageManager::defaultKeychain(const Keychain
&keychain
)
381 StLock
<Mutex
>_(mMutex
);
383 // Only set a keychain as the default if we own it and can read/write it,
384 // and our uid allows modifying the directory for that preference domain.
385 if (!keychainOwnerPermissionsValidForDomain(keychain
->name(), mDomain
))
386 MacOSError::throwMe(errSecWrPerm
);
388 DLDbIdentifier oldDefaultId
;
389 DLDbIdentifier
newDefaultId(keychain
->dlDbIdentifier());
391 oldDefaultId
= mSavedList
.defaultDLDbIdentifier();
392 mSavedList
.revert(true);
393 mSavedList
.defaultDLDbIdentifier(newDefaultId
);
397 if (!(oldDefaultId
== newDefaultId
))
399 // Make sure we are not holding mLock when we post this event.
400 KCEventNotifier::PostKeychainEvent(kSecDefaultChangedEvent
, newDefaultId
);
405 StorageManager::defaultKeychain(SecPreferencesDomain domain
)
407 StLock
<Mutex
>_(mMutex
);
409 if (domain
== kSecPreferencesDomainDynamic
)
410 MacOSError::throwMe(errSecInvalidPrefsDomain
);
412 if (domain
== mDomain
)
413 return defaultKeychain();
416 DLDbIdentifier
defaultDLDbIdentifier(DLDbListCFPref(domain
).defaultDLDbIdentifier());
417 if (defaultDLDbIdentifier
)
418 return keychain(defaultDLDbIdentifier
);
420 MacOSError::throwMe(errSecNoDefaultKeychain
);
425 StorageManager::defaultKeychain(SecPreferencesDomain domain
, const Keychain
&keychain
)
427 StLock
<Mutex
>_(mMutex
);
429 if (domain
== kSecPreferencesDomainDynamic
)
430 MacOSError::throwMe(errSecInvalidPrefsDomain
);
432 if (domain
== mDomain
)
433 defaultKeychain(keychain
);
435 DLDbListCFPref(domain
).defaultDLDbIdentifier(keychain
->dlDbIdentifier());
439 StorageManager::loginKeychain()
441 StLock
<Mutex
>_(mMutex
);
443 Keychain theKeychain
;
445 mSavedList
.revert(false);
446 DLDbIdentifier
loginDLDbIdentifier(mSavedList
.loginDLDbIdentifier());
447 if (loginDLDbIdentifier
)
449 theKeychain
= keychain(loginDLDbIdentifier
);
453 if (theKeychain
&& theKeychain
->exists())
456 MacOSError::throwMe(errSecNoSuchKeychain
);
460 StorageManager::loginKeychain(Keychain keychain
)
462 StLock
<Mutex
>_(mMutex
);
464 mSavedList
.revert(true);
465 mSavedList
.loginDLDbIdentifier(keychain
->dlDbIdentifier());
470 StorageManager::size()
472 StLock
<Mutex
>_(mMutex
);
474 mSavedList
.revert(false);
475 mCommonList
.revert(false);
476 return mSavedList
.searchList().size() + mCommonList
.searchList().size();
480 StorageManager::at(unsigned int ix
)
482 StLock
<Mutex
>_(mMutex
);
484 mSavedList
.revert(false);
485 DLDbList dLDbList
= mSavedList
.searchList();
486 if (ix
< dLDbList
.size())
488 return keychain(dLDbList
[ix
]);
492 ix
-= dLDbList
.size();
493 mCommonList
.revert(false);
494 DLDbList commonList
= mCommonList
.searchList();
495 if (ix
>= commonList
.size())
496 MacOSError::throwMe(errSecInvalidKeychain
);
498 return keychain(commonList
[ix
]);
503 StorageManager::operator[](unsigned int ix
)
505 StLock
<Mutex
>_(mMutex
);
510 void StorageManager::rename(Keychain keychain
, const char* newName
)
513 StLock
<Mutex
>_(mKeychainMapMutex
);
515 bool changedDefault
= false;
516 DLDbIdentifier newDLDbIdentifier
;
518 mSavedList
.revert(true);
519 DLDbIdentifier defaultId
= mSavedList
.defaultDLDbIdentifier();
521 // Find the keychain object for the given ref
522 DLDbIdentifier dLDbIdentifier
= keychain
->dlDbIdentifier();
524 // Actually rename the database on disk.
525 keychain
->database()->rename(newName
);
527 if (dLDbIdentifier
== defaultId
)
530 newDLDbIdentifier
= keychain
->dlDbIdentifier();
531 // Rename the keychain in the search list.
532 mSavedList
.rename(dLDbIdentifier
, newDLDbIdentifier
);
534 // If this was the default keychain change it accordingly
536 mSavedList
.defaultDLDbIdentifier(newDLDbIdentifier
);
540 // we aren't worried about a weak reference here, because we have to
541 // hold a lock on an item in order to do the rename
543 // Now update the Keychain cache
544 if (keychain
->inCache())
546 KeychainMap::iterator it
= mKeychains
.find(dLDbIdentifier
);
547 if (it
!= mKeychains
.end() && (KeychainImpl
*) it
->second
== keychain
.get())
549 // Remove the keychain from the cache under its old
551 mKeychains
.erase(it
);
555 // If we renamed this keychain on top of an existing one we should
556 // drop the old one from the cache.
557 KeychainMap::iterator it
= mKeychains
.find(newDLDbIdentifier
);
558 if (it
!= mKeychains
.end())
560 Keychain
oldKeychain(it
->second
);
561 oldKeychain
->inCache(false);
562 // @@@ Ideally we should invalidate or fault this keychain object.
565 if (keychain
->inCache())
567 // If the keychain wasn't in the cache to being with let's not put
568 // it there now. There was probably a good reason it wasn't in it.
569 // If the keychain was in the cache, update it to use
570 // newDLDbIdentifier.
571 mKeychains
.insert(KeychainMap::value_type(newDLDbIdentifier
,
576 // Make sure we are not holding mLock when we post these events.
577 KCEventNotifier::PostKeychainEvent(kSecKeychainListChangedEvent
);
580 KCEventNotifier::PostKeychainEvent(kSecDefaultChangedEvent
,
584 void StorageManager::renameUnique(Keychain keychain
, CFStringRef newName
)
586 StLock
<Mutex
>_(mMutex
);
588 bool doneCreating
= false;
592 char newNameCString
[MAXPATHLEN
];
593 if ( CFStringGetCString(newName
, newNameCString
, MAXPATHLEN
, kCFStringEncodingUTF8
) ) // make sure it fits in MAXPATHLEN, etc.
595 // Construct the new name...
597 CFMutableStringRef newNameCFStr
= NULL
;
598 newNameCFStr
= CFStringCreateMutable(NULL
, MAXPATHLEN
);
601 CFStringAppendFormat(newNameCFStr
, NULL
, CFSTR("%s%d"), newNameCString
, index
);
602 CFStringAppend(newNameCFStr
, CFSTR(kKeychainSuffix
)); // add .keychain
603 char toUseBuff2
[MAXPATHLEN
];
604 if ( CFStringGetCString(newNameCFStr
, toUseBuff2
, MAXPATHLEN
, kCFStringEncodingUTF8
) ) // make sure it fits in MAXPATHLEN, etc.
607 if ( lstat(toUseBuff2
, &filebuf
) )
609 rename(keychain
, toUseBuff2
);
611 kcList
.push_back(keychain
);
612 remove(kcList
, false);
619 doneCreating
= true; // failure to get c string.
620 CFRelease(newNameCFStr
);
623 doneCreating
= false; // failure to create mutable string.
626 doneCreating
= false; // failure to get the string (i.e. > MAXPATHLEN?)
628 while (!doneCreating
&& index
!= INT_MAX
);
631 #define KEYCHAIN_SYNC_KEY CFSTR("KeychainSyncList")
632 #define KEYCHAIN_SYNC_DOMAIN CFSTR("com.apple.keychainsync")
634 static CFStringRef
MakeExpandedPath (const char* path
)
636 std::string name
= DLDbListCFPref::ExpandTildesInPath (std::string (path
));
637 CFStringRef expanded
= CFStringCreateWithCString (NULL
, name
.c_str (), 0);
641 void StorageManager::removeKeychainFromSyncList (const DLDbIdentifier
&id
)
643 StLock
<Mutex
>_(mMutex
);
645 // make a CFString of our identifier
646 const char* idname
= id
.dbName ();
652 CFRef
<CFStringRef
> idString
= MakeExpandedPath (idname
);
654 // check and see if this keychain is in the keychain syncing list
656 (CFArrayRef
) CFPreferencesCopyValue (KEYCHAIN_SYNC_KEY
,
657 KEYCHAIN_SYNC_DOMAIN
,
658 kCFPreferencesCurrentUser
,
659 kCFPreferencesAnyHost
);
665 // make a mutable copy of the dictionary
666 CFRef
<CFMutableArrayRef
> mtValue
= CFArrayCreateMutableCopy (NULL
, 0, value
);
669 // walk the array, looking for the value
671 CFIndex limit
= CFArrayGetCount (mtValue
.get());
674 for (i
= 0; i
< limit
; ++i
)
676 CFDictionaryRef idx
= (CFDictionaryRef
) CFArrayGetValueAtIndex (mtValue
.get(), i
);
677 CFStringRef v
= (CFStringRef
) CFDictionaryGetValue (idx
, CFSTR("DbName"));
680 return; // something is really wrong if this is taken
683 char* stringBuffer
= NULL
;
684 const char* pathString
= CFStringGetCStringPtr(v
, 0);
687 CFIndex maxLen
= CFStringGetMaximumSizeForEncoding(CFStringGetLength(v
), kCFStringEncodingUTF8
) + 1;
688 stringBuffer
= (char*) malloc(maxLen
);
689 CFStringGetCString(v
, stringBuffer
, maxLen
, kCFStringEncodingUTF8
);
690 pathString
= stringBuffer
;
693 CFStringRef vExpanded
= MakeExpandedPath(pathString
);
694 CFComparisonResult result
= CFStringCompare (vExpanded
, idString
.get(), 0);
695 if (stringBuffer
!= NULL
)
700 CFRelease (vExpanded
);
704 CFArrayRemoveValueAtIndex (mtValue
.get(), i
);
713 CFShow (mtValue
.get());
716 CFPreferencesSetValue (KEYCHAIN_SYNC_KEY
,
718 KEYCHAIN_SYNC_DOMAIN
,
719 kCFPreferencesCurrentUser
,
720 kCFPreferencesAnyHost
);
721 CFPreferencesSynchronize (KEYCHAIN_SYNC_DOMAIN
, kCFPreferencesCurrentUser
, kCFPreferencesAnyHost
);
725 void StorageManager::remove(const KeychainList
&kcsToRemove
, bool deleteDb
)
727 StLock
<Mutex
>_(mMutex
);
729 bool unsetDefault
= false;
730 bool updateList
= (!isAppSandboxed());
734 mSavedList
.revert(true);
735 DLDbIdentifier defaultId
= mSavedList
.defaultDLDbIdentifier();
736 for (KeychainList::const_iterator ix
= kcsToRemove
.begin();
737 ix
!= kcsToRemove
.end(); ++ix
)
739 // Find the keychain object for the given ref
740 Keychain theKeychain
= *ix
;
741 DLDbIdentifier dLDbIdentifier
= theKeychain
->dlDbIdentifier();
743 // Remove it from the saved list
744 mSavedList
.remove(dLDbIdentifier
);
745 if (dLDbIdentifier
== defaultId
)
750 removeKeychainFromSyncList (dLDbIdentifier
);
752 // Now remove it from the cache
753 removeKeychain(dLDbIdentifier
, theKeychain
.get());
758 mSavedList
.defaultDLDbIdentifier(DLDbIdentifier());
765 // Delete the actual databases without holding any locks.
766 for (KeychainList::const_iterator ix
= kcsToRemove
.begin();
767 ix
!= kcsToRemove
.end(); ++ix
)
769 (*ix
)->database()->deleteDb();
774 // Make sure we are not holding mLock when we post these events.
775 KCEventNotifier::PostKeychainEvent(kSecKeychainListChangedEvent
);
779 KCEventNotifier::PostKeychainEvent(kSecDefaultChangedEvent
);
783 StorageManager::getSearchList(KeychainList
&keychainList
)
785 // hold the global lock since we make keychain objects in this function
787 // to do: each of the items in this list must be retained, otherwise mayhem will occur
788 StLock
<Mutex
>_(mMutex
);
791 keychainList
.clear();
795 mSavedList
.revert(false);
796 mCommonList
.revert(false);
798 // Merge mSavedList, mDynamicList and mCommonList
799 DLDbList dLDbList
= mSavedList
.searchList();
800 DLDbList dynamicList
= mDynamicList
.searchList();
801 DLDbList commonList
= mCommonList
.searchList();
803 result
.reserve(dLDbList
.size() + dynamicList
.size() + commonList
.size());
806 for (DLDbList::const_iterator it
= dynamicList
.begin();
807 it
!= dynamicList
.end(); ++it
)
809 Keychain k
= keychain(*it
);
813 for (DLDbList::const_iterator it
= dLDbList
.begin();
814 it
!= dLDbList
.end(); ++it
)
816 Keychain k
= keychain(*it
);
820 for (DLDbList::const_iterator it
= commonList
.begin();
821 it
!= commonList
.end(); ++it
)
823 Keychain k
= keychain(*it
);
828 keychainList
.swap(result
);
832 StorageManager::setSearchList(const KeychainList
&keychainList
)
834 StLock
<Mutex
>_(mMutex
);
836 DLDbList commonList
= mCommonList
.searchList();
838 // Strip out the common list part from the end of the search list.
839 KeychainList::const_iterator it_end
= keychainList
.end();
840 DLDbList::const_reverse_iterator end_common
= commonList
.rend();
841 for (DLDbList::const_reverse_iterator it_common
= commonList
.rbegin(); it_common
!= end_common
; ++it_common
)
843 // Eliminate common entries from the end of the passed in keychainList.
844 if (it_end
== keychainList
.begin())
848 if (!((*it_end
)->dlDbIdentifier() == *it_common
))
855 /* it_end now points one past the last element in keychainList which is not in commonList. */
856 DLDbList searchList
, oldSearchList(mSavedList
.searchList());
857 for (KeychainList::const_iterator it
= keychainList
.begin(); it
!= it_end
; ++it
)
859 searchList
.push_back((*it
)->dlDbIdentifier());
863 // Set the current searchlist to be what was passed in, the old list will be freed
864 // upon exit of this stackframe.
865 mSavedList
.revert(true);
866 mSavedList
.searchList(searchList
);
870 if (!(oldSearchList
== searchList
))
872 // Make sure we are not holding mLock when we post this event.
873 KCEventNotifier::PostKeychainEvent(kSecKeychainListChangedEvent
);
878 StorageManager::getSearchList(SecPreferencesDomain domain
, KeychainList
&keychainList
)
880 StLock
<Mutex
>_(mMutex
);
883 keychainList
.clear();
887 if (domain
== kSecPreferencesDomainDynamic
)
889 convertList(keychainList
, mDynamicList
.searchList());
891 else if (domain
== mDomain
)
893 mSavedList
.revert(false);
894 convertList(keychainList
, mSavedList
.searchList());
898 convertList(keychainList
, DLDbListCFPref(domain
).searchList());
902 void StorageManager::forceUserSearchListReread()
904 mSavedList
.forceUserSearchListReread();
908 StorageManager::setSearchList(SecPreferencesDomain domain
, const KeychainList
&keychainList
)
910 StLock
<Mutex
>_(mMutex
);
912 if (domain
== kSecPreferencesDomainDynamic
)
913 MacOSError::throwMe(errSecInvalidPrefsDomain
);
916 convertList(searchList
, keychainList
);
918 if (domain
== mDomain
)
920 DLDbList
oldSearchList(mSavedList
.searchList());
922 // Set the current searchlist to be what was passed in, the old list will be freed
923 // upon exit of this stackframe.
924 mSavedList
.revert(true);
925 mSavedList
.searchList(searchList
);
929 if (!(oldSearchList
== searchList
))
931 KCEventNotifier::PostKeychainEvent(kSecKeychainListChangedEvent
);
936 DLDbListCFPref(domain
).searchList(searchList
);
941 StorageManager::domain(SecPreferencesDomain domain
)
943 StLock
<Mutex
>_(mMutex
);
945 if (domain
== kSecPreferencesDomainDynamic
)
946 MacOSError::throwMe(errSecInvalidPrefsDomain
);
948 if (domain
== mDomain
)
954 case kSecPreferencesDomainSystem
:
955 secdebug("storagemgr", "switching to system domain"); break;
956 case kSecPreferencesDomainUser
:
957 secdebug("storagemgr", "switching to user domain (uid %d)", getuid()); break;
959 secdebug("storagemgr", "switching to weird prefs domain %d", domain
); break;
964 mSavedList
.set(domain
);
968 StorageManager::optionalSearchList(CFTypeRef keychainOrArray
, KeychainList
&keychainList
)
970 StLock
<Mutex
>_(mMutex
);
972 if (!keychainOrArray
)
973 getSearchList(keychainList
);
976 CFTypeID typeID
= CFGetTypeID(keychainOrArray
);
977 if (typeID
== CFArrayGetTypeID())
978 convertToKeychainList(CFArrayRef(keychainOrArray
), keychainList
);
979 else if (typeID
== gTypes().KeychainImpl
.typeID
)
980 keychainList
.push_back(KeychainImpl::required(SecKeychainRef(keychainOrArray
)));
982 MacOSError::throwMe(errSecParam
);
988 StorageManager::convertToKeychainList(CFArrayRef keychainArray
, KeychainList
&keychainList
)
990 CFIndex count
= CFArrayGetCount(keychainArray
);
994 KeychainList
keychains(count
);
995 for (CFIndex ix
= 0; ix
< count
; ++ix
)
997 keychains
[ix
] = KeychainImpl::required(SecKeychainRef(CFArrayGetValueAtIndex(keychainArray
, ix
)));
1000 keychainList
.swap(keychains
);
1004 StorageManager::convertFromKeychainList(const KeychainList
&keychainList
)
1006 CFRef
<CFMutableArrayRef
> keychainArray(CFArrayCreateMutable(NULL
, keychainList
.size(), &kCFTypeArrayCallBacks
));
1008 for (KeychainList::const_iterator ix
= keychainList
.begin(); ix
!= keychainList
.end(); ++ix
)
1010 SecKeychainRef keychainRef
= (*ix
)->handle();
1011 CFArrayAppendValue(keychainArray
, keychainRef
);
1012 CFRelease(keychainRef
);
1015 // Counter the CFRelease that CFRef<> is about to do when keychainArray goes out of scope.
1016 CFRetain(keychainArray
);
1017 return keychainArray
;
1020 void StorageManager::convertList(DLDbList
&ids
, const KeychainList
&kcs
)
1023 result
.reserve(kcs
.size());
1024 for (KeychainList::const_iterator ix
= kcs
.begin(); ix
!= kcs
.end(); ++ix
)
1026 result
.push_back((*ix
)->dlDbIdentifier());
1031 void StorageManager::convertList(KeychainList
&kcs
, const DLDbList
&ids
)
1033 StLock
<Mutex
>_(mMutex
);
1035 KeychainList result
;
1036 result
.reserve(ids
.size());
1038 for (DLDbList::const_iterator ix
= ids
.begin(); ix
!= ids
.end(); ++ix
)
1039 result
.push_back(keychain(*ix
));
1044 #pragma mark ____ Login Functions ____
1046 void StorageManager::login(AuthorizationRef authRef
, UInt32 nameLength
, const char* name
)
1048 StLock
<Mutex
>_(mMutex
);
1050 AuthorizationItemSet
* info
= NULL
;
1051 OSStatus result
= AuthorizationCopyInfo(authRef
, NULL
, &info
); // get the results of the copy rights call.
1052 Boolean created
= false;
1053 if ( result
== errSecSuccess
&& info
->count
)
1055 // Grab the password from the auth context (info) and create the keychain...
1057 AuthorizationItem
* currItem
= info
->items
;
1058 for (UInt32 index
= 1; index
<= info
->count
; index
++) //@@@plugin bug won't return a specific context.
1060 if (strcmp(currItem
->name
, kAuthorizationEnvironmentPassword
) == 0)
1062 // creates the login keychain with the specified password
1065 login(nameLength
, name
, (UInt32
)currItem
->valueLength
, currItem
->value
);
1077 AuthorizationFreeItemSet(info
);
1080 MacOSError::throwMe(errAuthorizationInternal
);
1083 void StorageManager::login(ConstStringPtr name
, ConstStringPtr password
)
1085 StLock
<Mutex
>_(mMutex
);
1087 if ( name
== NULL
|| password
== NULL
)
1088 MacOSError::throwMe(errSecParam
);
1090 login(name
[0], name
+ 1, password
[0], password
+ 1);
1093 void StorageManager::login(UInt32 nameLength
, const void *name
,
1094 UInt32 passwordLength
, const void *password
)
1096 if (passwordLength
!= 0 && password
== NULL
)
1098 secdebug("KCLogin", "StorageManager::login: invalid argument (NULL password)");
1099 MacOSError::throwMe(errSecParam
);
1102 DLDbIdentifier loginDLDbIdentifier
;
1104 mSavedList
.revert(true);
1105 loginDLDbIdentifier
= mSavedList
.loginDLDbIdentifier();
1108 secdebug("KCLogin", "StorageManager::login: loginDLDbIdentifier is %s", (loginDLDbIdentifier
) ? loginDLDbIdentifier
.dbName() : "<NULL>");
1109 if (!loginDLDbIdentifier
)
1110 MacOSError::throwMe(errSecNoSuchKeychain
);
1113 //***************************************************************
1114 // gather keychain information
1115 //***************************************************************
1118 int uid
= geteuid();
1119 struct passwd
*pw
= getpwuid(uid
);
1121 secdebug("KCLogin", "StorageManager::login: invalid argument (NULL uid)");
1122 MacOSError::throwMe(errSecParam
);
1124 char *userName
= pw
->pw_name
;
1126 // make keychain path strings
1127 std::string keychainPath
= DLDbListCFPref::ExpandTildesInPath(kLoginKeychainPathPrefix
);
1128 std::string shortnameKeychain
= keychainPath
+ userName
;
1129 std::string shortnameDotKeychain
= shortnameKeychain
+ ".keychain";
1130 std::string loginDotKeychain
= keychainPath
+ "login.keychain";
1131 std::string loginRenamed1Keychain
= keychainPath
+ "login_renamed1.keychain";
1133 // check for existence of keychain files
1134 bool shortnameKeychainExists
= false;
1135 bool shortnameDotKeychainExists
= false;
1136 bool loginKeychainExists
= false;
1137 bool loginRenamed1KeychainExists
= false;
1141 stat_result
= ::stat(shortnameKeychain
.c_str(), &st
);
1142 shortnameKeychainExists
= (stat_result
== 0);
1143 stat_result
= ::stat(shortnameDotKeychain
.c_str(), &st
);
1144 shortnameDotKeychainExists
= (stat_result
== 0);
1145 stat_result
= ::stat(loginDotKeychain
.c_str(), &st
);
1146 loginKeychainExists
= (stat_result
== 0);
1147 stat_result
= ::stat(loginRenamed1Keychain
.c_str(), &st
);
1148 loginRenamed1KeychainExists
= (stat_result
== 0);
1151 bool loginUnlocked
= false;
1153 // make the keychain identifiers
1154 CSSM_VERSION version
= {0, 0};
1155 DLDbIdentifier shortnameDLDbIdentifier
= DLDbListCFPref::makeDLDbIdentifier(gGuidAppleCSPDL
, version
, 0, CSSM_SERVICE_CSP
| CSSM_SERVICE_DL
, shortnameKeychain
.c_str(), NULL
);
1156 DLDbIdentifier shortnameDotDLDbIdentifier
= DLDbListCFPref::makeDLDbIdentifier(gGuidAppleCSPDL
, version
, 0, CSSM_SERVICE_CSP
| CSSM_SERVICE_DL
, shortnameDotKeychain
.c_str(), NULL
);
1157 DLDbIdentifier loginRenamed1DLDbIdentifier
= DLDbListCFPref::makeDLDbIdentifier(gGuidAppleCSPDL
, version
, 0, CSSM_SERVICE_CSP
| CSSM_SERVICE_DL
, loginRenamed1Keychain
.c_str(), NULL
);
1159 //***************************************************************
1160 // make file renaming changes first
1161 //***************************************************************
1163 // if "~/Library/Keychains/shortname" exists, we need to migrate it forward;
1164 // either to login.keychain if there isn't already one, otherwise to shortname.keychain
1165 if (shortnameKeychainExists
) {
1166 int rename_stat
= 0;
1167 if (loginKeychainExists
) {
1169 int tmp_result
= ::stat(loginDotKeychain
.c_str(), &st
);
1170 if (tmp_result
== 0) {
1171 if (st
.st_size
<= kEmptyKeychainSizeInBytes
) {
1172 tmp_result
= ::unlink(loginDotKeychain
.c_str());
1173 rename_stat
= ::rename(shortnameKeychain
.c_str(), loginDotKeychain
.c_str());
1174 shortnameKeychainExists
= (rename_stat
!= 0);
1178 if (shortnameKeychainExists
) {
1179 if (loginKeychainExists
&& !shortnameDotKeychainExists
) {
1180 rename_stat
= ::rename(shortnameKeychain
.c_str(), shortnameDotKeychain
.c_str());
1181 shortnameDotKeychainExists
= (rename_stat
== 0);
1182 } else if (!loginKeychainExists
) {
1183 rename_stat
= ::rename(shortnameKeychain
.c_str(), loginDotKeychain
.c_str());
1184 loginKeychainExists
= (rename_stat
== 0);
1186 // we have all 3 keychains: login.keychain, shortname, and shortname.keychain.
1187 // on Leopard we never want a shortname keychain, so we must move it aside.
1188 char pathbuf
[MAXPATHLEN
];
1189 std::string shortnameRenamedXXXKeychain
= keychainPath
;
1190 shortnameRenamedXXXKeychain
+= userName
;
1191 shortnameRenamedXXXKeychain
+= "_renamed_XXX.keychain";
1192 ::strlcpy(pathbuf
, shortnameRenamedXXXKeychain
.c_str(), sizeof(pathbuf
));
1193 ::mkstemps(pathbuf
, 9); // 9 == strlen(".keychain")
1194 rename_stat
= ::rename(shortnameKeychain
.c_str(), pathbuf
);
1195 shortnameKeychainExists
= (rename_stat
!= 0);
1198 if (rename_stat
!= 0) {
1199 MacOSError::throwMe(errno
);
1203 //***************************************************************
1204 // handle special case where user previously reset the keychain
1205 //***************************************************************
1206 // Since 9A581, we have changed the definition of kKeychainRenamedSuffix from "_renamed" to "_renamed_".
1207 // Therefore, if "login_renamed1.keychain" exists and there is no plist, the user may have run into a
1208 // prior upgrade issue and clicked Reset. If we can successfully unlock login_renamed1.keychain with the
1209 // supplied password, then we will attempt to rename it to login.keychain if that file is empty, or with
1210 // "shortname.keychain" if it is not.
1212 if (loginRenamed1KeychainExists
&& (!loginKeychainExists
||
1213 (mSavedList
.searchList().size() == 1 && mSavedList
.member(loginDLDbIdentifier
)) )) {
1216 Keychain
loginRenamed1KC(keychain(loginRenamed1DLDbIdentifier
));
1217 secdebug("KCLogin", "Attempting to unlock %s with %d-character password",
1218 (loginRenamed1KC
) ? loginRenamed1KC
->name() : "<NULL>", (unsigned int)passwordLength
);
1219 loginRenamed1KC
->unlock(CssmData(const_cast<void *>(password
), passwordLength
));
1220 // if we get here, we unlocked it
1221 if (loginKeychainExists
) {
1223 int tmp_result
= ::stat(loginDotKeychain
.c_str(), &st
);
1224 if (tmp_result
== 0) {
1225 if (st
.st_size
<= kEmptyKeychainSizeInBytes
) {
1226 tmp_result
= ::unlink(loginDotKeychain
.c_str());
1227 tmp_result
= ::rename(loginRenamed1Keychain
.c_str(), loginDotKeychain
.c_str());
1228 } else if (!shortnameDotKeychainExists
) {
1229 tmp_result
= ::rename(loginRenamed1Keychain
.c_str(), shortnameDotKeychain
.c_str());
1230 shortnameDotKeychainExists
= (tmp_result
== 0);
1232 throw 1; // can't do anything with it except move it out of the way
1236 int tmp_result
= ::rename(loginRenamed1Keychain
.c_str(), loginDotKeychain
.c_str());
1237 loginKeychainExists
= (tmp_result
== 0);
1242 // we failed to unlock the login_renamed1.keychain file with the login password.
1243 // move it aside so we don't try to deal with it again.
1244 char pathbuf
[MAXPATHLEN
];
1245 std::string loginRenamedXXXKeychain
= keychainPath
;
1246 loginRenamedXXXKeychain
+= "login_renamed_XXX.keychain";
1247 ::strlcpy(pathbuf
, loginRenamedXXXKeychain
.c_str(), sizeof(pathbuf
));
1248 ::mkstemps(pathbuf
, 9); // 9 == strlen(".keychain")
1249 ::rename(loginRenamed1Keychain
.c_str(), pathbuf
);
1253 // if login.keychain does not exist at this point, create it
1254 if (!loginKeychainExists
) {
1255 Keychain
theKeychain(keychain(loginDLDbIdentifier
));
1256 secdebug("KCLogin", "Creating login keychain %s", (loginDLDbIdentifier
) ? loginDLDbIdentifier
.dbName() : "<NULL>");
1257 theKeychain
->create(passwordLength
, password
);
1258 secdebug("KCLogin", "Login keychain created successfully");
1259 loginKeychainExists
= true;
1260 // Set the prefs for this new login keychain.
1261 loginKeychain(theKeychain
);
1262 // Login Keychain does not lock on sleep nor lock after timeout by default.
1263 theKeychain
->setSettings(INT_MAX
, false);
1264 loginUnlocked
= true;
1265 mSavedList
.revert(true);
1268 //***************************************************************
1269 // make plist changes after files have been renamed or created
1270 //***************************************************************
1272 // if the shortname keychain exists in the search list, either rename or remove the entry
1273 if (mSavedList
.member(shortnameDLDbIdentifier
)) {
1274 if (shortnameDotKeychainExists
&& !mSavedList
.member(shortnameDotDLDbIdentifier
)) {
1275 // change shortname to shortname.keychain (login.keychain will be added later if not present)
1276 secdebug("KCLogin", "Renaming %s to %s in keychain search list",
1277 (shortnameDLDbIdentifier
) ? shortnameDLDbIdentifier
.dbName() : "<NULL>",
1278 (shortnameDotDLDbIdentifier
) ? shortnameDotDLDbIdentifier
.dbName() : "<NULL>");
1279 mSavedList
.rename(shortnameDLDbIdentifier
, shortnameDotDLDbIdentifier
);
1280 } else if (!mSavedList
.member(loginDLDbIdentifier
)) {
1281 // change shortname to login.keychain
1282 secdebug("KCLogin", "Renaming %s to %s in keychain search list",
1283 (shortnameDLDbIdentifier
) ? shortnameDLDbIdentifier
.dbName() : "<NULL>",
1284 (loginDLDbIdentifier
) ? loginDLDbIdentifier
.dbName() : "<NULL>");
1285 mSavedList
.rename(shortnameDLDbIdentifier
, loginDLDbIdentifier
);
1287 // already have login.keychain in list, and renaming to shortname.keychain isn't an option,
1288 // so just remove the entry
1289 secdebug("KCLogin", "Removing %s from keychain search list", (shortnameDLDbIdentifier
) ? shortnameDLDbIdentifier
.dbName() : "<NULL>");
1290 mSavedList
.remove(shortnameDLDbIdentifier
);
1293 // note: save() will cause the plist to be unlinked if the only remaining entry is for login.keychain
1295 mSavedList
.revert(true);
1298 // make sure that login.keychain is in the search list
1299 if (!mSavedList
.member(loginDLDbIdentifier
)) {
1300 secdebug("KCLogin", "Adding %s to keychain search list", (loginDLDbIdentifier
) ? loginDLDbIdentifier
.dbName() : "<NULL>");
1301 mSavedList
.add(loginDLDbIdentifier
);
1303 mSavedList
.revert(true);
1306 // if we have a shortname.keychain, always include it in the plist (after login.keychain)
1307 if (shortnameDotKeychainExists
&& !mSavedList
.member(shortnameDotDLDbIdentifier
)) {
1308 mSavedList
.add(shortnameDotDLDbIdentifier
);
1310 mSavedList
.revert(true);
1313 // make sure that the default keychain is in the search list; if not, reset the default to login.keychain
1314 if (!mSavedList
.member(mSavedList
.defaultDLDbIdentifier())) {
1315 secdebug("KCLogin", "Changing default keychain to %s", (loginDLDbIdentifier
) ? loginDLDbIdentifier
.dbName() : "<NULL>");
1316 mSavedList
.defaultDLDbIdentifier(loginDLDbIdentifier
);
1318 mSavedList
.revert(true);
1321 //***************************************************************
1322 // auto-unlock the login keychain(s)
1323 //***************************************************************
1324 // all our preflight fixups are finally done, so we can now attempt to unlock the login keychain
1326 OSStatus loginResult
= errSecSuccess
;
1327 if (!loginUnlocked
) {
1330 Keychain
theKeychain(keychain(loginDLDbIdentifier
));
1331 secdebug("KCLogin", "Attempting to unlock login keychain \"%s\" with %d-character password",
1332 (theKeychain
) ? theKeychain
->name() : "<NULL>", (unsigned int)passwordLength
);
1333 theKeychain
->unlock(CssmData(const_cast<void *>(password
), passwordLength
));
1334 loginUnlocked
= true;
1336 catch(const CssmError
&e
)
1338 loginResult
= e
.osStatus(); // save this result
1342 if (!loginUnlocked
) {
1344 loginResult
= errSecSuccess
;
1345 Keychain
theKeychain(keychain(loginDLDbIdentifier
));
1349 key
.header().BlobType
= CSSM_KEYBLOB_RAW
;
1350 key
.header().Format
= CSSM_KEYBLOB_RAW_FORMAT_OCTET_STRING
;
1351 key
.header().AlgorithmId
= CSSM_ALGID_3DES_3KEY
;
1352 key
.header().KeyClass
= CSSM_KEYCLASS_SESSION_KEY
;
1353 key
.header().KeyUsage
= CSSM_KEYUSE_ENCRYPT
| CSSM_KEYUSE_DECRYPT
| CSSM_KEYATTR_EXTRACTABLE
;
1354 key
.header().KeyAttr
= 0;
1355 key
.KeyData
= CssmData(const_cast<void *>(password
), passwordLength
);
1357 // unwrap it into the CSP (but keep it raw)
1358 UnwrapKey
unwrap(theKeychain
->csp(), CSSM_ALGID_NONE
);
1360 CssmData descriptiveData
;
1362 KeySpec(CSSM_KEYUSE_ANY
, CSSM_KEYATTR_EXTRACTABLE
),
1363 masterKey
, &descriptiveData
, NULL
);
1365 CssmClient::Db db
= theKeychain
->database();
1367 // create the keychain, using appropriate credentials
1368 Allocator
&alloc
= db
->allocator();
1369 AutoCredentials
cred(alloc
); // will leak, but we're quitting soon :-)
1371 // use this passphrase
1372 cred
+= TypedList(alloc
, CSSM_SAMPLE_TYPE_KEYCHAIN_LOCK
,
1373 new(alloc
) ListElement(CSSM_SAMPLE_TYPE_SYMMETRIC_KEY
),
1374 new(alloc
) ListElement(CssmData::wrap(theKeychain
->csp()->handle())),
1375 new(alloc
) ListElement(CssmData::wrap(masterKey
)),
1376 new(alloc
) ListElement(CssmData()));
1377 db
->authenticate(CSSM_DB_ACCESS_READ
, &cred
);
1379 loginUnlocked
= true;
1380 } catch (const CssmError
&e
) {
1381 loginResult
= e
.osStatus();
1385 // if "shortname.keychain" exists and is in the search list, attempt to auto-unlock it with the same password
1386 if (shortnameDotKeychainExists
&& mSavedList
.member(shortnameDotDLDbIdentifier
)) {
1389 Keychain
shortnameDotKC(keychain(shortnameDotDLDbIdentifier
));
1390 secdebug("KCLogin", "Attempting to unlock %s",
1391 (shortnameDotKC
) ? shortnameDotKC
->name() : "<NULL>");
1392 shortnameDotKC
->unlock(CssmData(const_cast<void *>(password
), passwordLength
));
1394 catch(const CssmError
&e
)
1396 // ignore; failure to unlock this keychain is not considered an error
1400 if (loginResult
!= errSecSuccess
) {
1401 MacOSError::throwMe(loginResult
);
1405 void StorageManager::stashLogin()
1407 OSStatus loginResult
= errSecSuccess
;
1409 DLDbIdentifier loginDLDbIdentifier
;
1411 mSavedList
.revert(true);
1412 loginDLDbIdentifier
= mSavedList
.loginDLDbIdentifier();
1415 secdebug("KCLogin", "StorageManager::stash: loginDLDbIdentifier is %s", (loginDLDbIdentifier
) ? loginDLDbIdentifier
.dbName() : "<NULL>");
1416 if (!loginDLDbIdentifier
)
1417 MacOSError::throwMe(errSecNoSuchKeychain
);
1422 Keychain
theKeychain(keychain(loginDLDbIdentifier
));
1423 secdebug("KCLogin", "Attempting to use stash for login keychain \"%s\"",
1424 (theKeychain
) ? theKeychain
->name() : "<NULL>");
1425 theKeychain
->stashCheck();
1427 catch(const CssmError
&e
)
1429 loginResult
= e
.osStatus(); // save this result
1433 if (loginResult
!= errSecSuccess
) {
1434 MacOSError::throwMe(loginResult
);
1438 void StorageManager::stashKeychain()
1440 OSStatus loginResult
= errSecSuccess
;
1442 DLDbIdentifier loginDLDbIdentifier
;
1444 mSavedList
.revert(true);
1445 loginDLDbIdentifier
= mSavedList
.loginDLDbIdentifier();
1448 secdebug("KCLogin", "StorageManager::stash: loginDLDbIdentifier is %s", (loginDLDbIdentifier
) ? loginDLDbIdentifier
.dbName() : "<NULL>");
1449 if (!loginDLDbIdentifier
)
1450 MacOSError::throwMe(errSecNoSuchKeychain
);
1454 Keychain
theKeychain(keychain(loginDLDbIdentifier
));
1455 secdebug("KCLogin", "Attempting to stash login keychain \"%s\"",
1456 (theKeychain
) ? theKeychain
->name() : "<NULL>");
1457 theKeychain
->stash();
1459 catch(const CssmError
&e
)
1461 loginResult
= e
.osStatus(); // save this result
1465 if (loginResult
!= errSecSuccess
) {
1466 MacOSError::throwMe(loginResult
);
1470 void StorageManager::logout()
1472 // nothing left to do here
1475 void StorageManager::changeLoginPassword(ConstStringPtr oldPassword
, ConstStringPtr newPassword
)
1477 StLock
<Mutex
>_(mMutex
);
1479 loginKeychain()->changePassphrase(oldPassword
, newPassword
);
1480 secdebug("KClogin", "Changed login keychain password successfully");
1484 void StorageManager::changeLoginPassword(UInt32 oldPasswordLength
, const void *oldPassword
, UInt32 newPasswordLength
, const void *newPassword
)
1486 StLock
<Mutex
>_(mMutex
);
1488 loginKeychain()->changePassphrase(oldPasswordLength
, oldPassword
, newPasswordLength
, newPassword
);
1489 secdebug("KClogin", "Changed login keychain password successfully");
1492 // Clear out the keychain search list and rename the existing login.keychain.
1494 void StorageManager::resetKeychain(Boolean resetSearchList
)
1496 StLock
<Mutex
>_(mMutex
);
1498 // Clear the keychain search list.
1501 if ( resetSearchList
)
1503 StorageManager::KeychainList keychainList
;
1504 setSearchList(keychainList
);
1506 // Get a reference to the existing login keychain...
1507 // If we don't have one, we throw (not requiring a rename).
1509 Keychain keychain
= loginKeychain();
1511 // Rename the existing login.keychain (i.e. put it aside).
1513 CFMutableStringRef newName
= NULL
;
1514 newName
= CFStringCreateMutable(NULL
, 0);
1515 CFStringRef currName
= NULL
;
1516 currName
= CFStringCreateWithCString(NULL
, keychain
->name(), kCFStringEncodingUTF8
);
1517 if ( newName
&& currName
)
1519 CFStringAppend(newName
, currName
);
1520 CFStringRef kcSuffix
= CFSTR(kKeychainSuffix
);
1521 if ( CFStringHasSuffix(newName
, kcSuffix
) ) // remove the .keychain extension
1523 CFRange suffixRange
= CFStringFind(newName
, kcSuffix
, 0);
1524 CFStringFindAndReplace(newName
, kcSuffix
, CFSTR(""), suffixRange
, 0);
1526 CFStringAppend(newName
, CFSTR(kKeychainRenamedSuffix
)); // add "_renamed_"
1529 renameUnique(keychain
, newName
);
1533 // we need to release 'newName' & 'currName'
1535 } // else, let the login call report a duplicate
1539 CFRelease(currName
);
1543 // We either don't have a login keychain, or there was a
1544 // failure to rename the existing one.
1548 #pragma mark ____ File Related ____
1550 Keychain
StorageManager::make(const char *pathName
)
1552 return make(pathName
, true);
1555 Keychain
StorageManager::make(const char *pathName
, bool add
)
1557 return makeKeychain(makeDLDbIdentifier(pathName
), add
);
1560 DLDbIdentifier
StorageManager::makeDLDbIdentifier(const char *pathName
) {
1561 StLock
<Mutex
>_(mMutex
);
1563 string fullPathName
;
1564 if ( pathName
[0] == '/' )
1565 fullPathName
= pathName
;
1568 // Get Home directory from environment.
1571 case kSecPreferencesDomainUser
:
1573 const char *homeDir
= getenv("HOME");
1574 if (homeDir
== NULL
)
1576 // If $HOME is unset get the current user's home directory
1577 // from the passwd file.
1578 uid_t uid
= geteuid();
1579 if (!uid
) uid
= getuid();
1580 struct passwd
*pw
= getpwuid(uid
);
1582 MacOSError::throwMe(errSecParam
);
1583 homeDir
= pw
->pw_dir
;
1585 fullPathName
= homeDir
;
1588 case kSecPreferencesDomainSystem
:
1592 assert(false); // invalid domain for this
1595 fullPathName
+= "/Library/Keychains/";
1596 fullPathName
+= pathName
;
1599 const CSSM_NET_ADDRESS
*DbLocation
= NULL
; // NULL for keychains
1600 const CSSM_VERSION
*version
= NULL
;
1601 uint32 subserviceId
= 0;
1602 CSSM_SERVICE_TYPE subserviceType
= CSSM_SERVICE_DL
| CSSM_SERVICE_CSP
;
1603 const CssmSubserviceUid
ssuid(gGuidAppleCSPDL
, version
,
1604 subserviceId
, subserviceType
);
1605 DLDbIdentifier
dlDbIdentifier(ssuid
, fullPathName
.c_str(), DbLocation
);
1606 return dlDbIdentifier
;
1609 Keychain
StorageManager::makeLoginAuthUI(const Item
*item
)
1611 StLock
<Mutex
>_(mMutex
);
1613 // Create a login/default keychain for the user using UI.
1614 // The user can cancel out of the operation, or create a new login keychain.
1615 // If auto-login is turned off, the user will be asked for their login password.
1617 OSStatus result
= errSecSuccess
;
1618 Keychain keychain
; // We return this keychain.
1620 // Set up the Auth ref to bring up UI.
1622 AuthorizationItem
*currItem
, *authEnvirItemArrayPtr
= NULL
;
1623 AuthorizationRef authRef
= NULL
;
1626 result
= AuthorizationCreate(NULL
, NULL
, kAuthorizationFlagDefaults
, &authRef
);
1628 MacOSError::throwMe(result
);
1630 AuthorizationEnvironment envir
;
1631 envir
.count
= 6; // up to 6 hints can be used.
1632 authEnvirItemArrayPtr
= (AuthorizationItem
*)malloc(sizeof(AuthorizationItem
) * envir
.count
);
1633 if ( !authEnvirItemArrayPtr
)
1634 MacOSError::throwMe(errAuthorizationInternal
);
1636 currItem
= envir
.items
= authEnvirItemArrayPtr
;
1639 // 1st Hint (optional): The keychain item's account attribute string.
1640 // When item is specified, we assume an 'add' operation is being attempted.
1643 SecKeychainAttribute attr
= { kSecAccountItemAttr
, 255, &buff
};
1648 (*item
)->getAttribute(attr
, &actLen
);
1652 actLen
= 0; // This item didn't have the account attribute, so don't display one in the UI.
1655 currItem
->name
= AGENT_HINT_ATTR_NAME
; // name str that identifies this hint as attr name
1656 if ( actLen
) // Fill in the hint if we have an account attr
1658 if ( actLen
>= sizeof(buff
) )
1659 buff
[sizeof(buff
)-1] = 0;
1662 currItem
->valueLength
= strlen(buff
)+1;
1663 currItem
->value
= buff
;
1667 currItem
->valueLength
= 0;
1668 currItem
->value
= NULL
;
1670 currItem
->flags
= 0;
1673 // 2nd Hint (optional): The item's keychain full path.
1676 char* currDefaultName
= NULL
;
1679 currDefaultName
= (char*)defaultKeychain()->name(); // Use the name if we have it.
1680 currItem
->name
= AGENT_HINT_LOGIN_KC_NAME
; // Name str that identifies this hint as kc path
1681 currItem
->valueLength
= (currDefaultName
) ? strlen(currDefaultName
) : 0;
1682 currItem
->value
= (currDefaultName
) ? (void*)currDefaultName
: (void*)"";
1683 currItem
->flags
= 0;
1692 // 3rd Hint (required): check if curr default keychain is unavailable.
1693 // This is determined by the parent not existing.
1695 currItem
->name
= AGENT_HINT_LOGIN_KC_EXISTS_IN_KC_FOLDER
;
1696 Boolean loginUnavail
= false;
1699 Keychain defaultKC
= defaultKeychain();
1700 if ( !defaultKC
->exists() )
1701 loginUnavail
= true;
1703 catch(...) // login.keychain not present
1706 currItem
->valueLength
= sizeof(Boolean
);
1707 currItem
->value
= (void*)&loginUnavail
;
1708 currItem
->flags
= 0;
1711 // 4th Hint (required): userName
1714 currItem
->name
= AGENT_HINT_LOGIN_KC_USER_NAME
;
1715 char* uName
= getenv("USER");
1716 string userName
= uName
? uName
: "";
1717 if ( userName
.length() == 0 )
1719 uid_t uid
= geteuid();
1720 if (!uid
) uid
= getuid();
1721 struct passwd
*pw
= getpwuid(uid
); // fallback case...
1723 userName
= pw
->pw_name
;
1726 if ( userName
.length() == 0 ) // did we ultimately get one?
1727 MacOSError::throwMe(errAuthorizationInternal
);
1729 currItem
->value
= (void*)userName
.c_str();
1730 currItem
->valueLength
= userName
.length();
1731 currItem
->flags
= 0;
1734 // 5th Hint (required): flags if user has more than 1 keychain (used for a later warning when reset to default).
1737 currItem
->name
= AGENT_HINT_LOGIN_KC_USER_HAS_OTHER_KCS_STR
;
1738 Boolean moreThanOneKCExists
= false;
1740 // if item is NULL, then this is a user-initiated full reset
1741 if (item
&& mSavedList
.searchList().size() > 1)
1742 moreThanOneKCExists
= true;
1744 currItem
->value
= &moreThanOneKCExists
;
1745 currItem
->valueLength
= sizeof(Boolean
);
1746 currItem
->flags
= 0;
1749 // 6th Hint (required): If no item is involved, this is a user-initiated full reset.
1750 // We want to suppress the "do you want to reset to defaults?" panel in this case.
1753 currItem
->name
= AGENT_HINT_LOGIN_KC_SUPPRESS_RESET_PANEL
;
1754 Boolean suppressResetPanel
= (item
== NULL
) ? TRUE
: FALSE
;
1755 currItem
->valueLength
= sizeof(Boolean
);
1756 currItem
->value
= (void*)&suppressResetPanel
;
1757 currItem
->flags
= 0;
1760 // Set up the auth rights and make the auth call.
1762 AuthorizationItem authItem
= { LOGIN_KC_CREATION_RIGHT
, 0 , NULL
, 0 };
1763 AuthorizationRights rights
= { 1, &authItem
};
1764 AuthorizationFlags flags
= kAuthorizationFlagDefaults
| kAuthorizationFlagInteractionAllowed
| kAuthorizationFlagExtendRights
;
1765 result
= AuthorizationCopyRights(authRef
, &rights
, &envir
, flags
, NULL
);
1767 MacOSError::throwMe(result
);
1770 resetKeychain(true); // Clears the plist, moves aside existing login.keychain
1772 catch (...) // can throw if no existing login.keychain is found
1775 login(authRef
, (UInt32
)userName
.length(), userName
.c_str()); // Create login.keychain
1776 keychain
= loginKeychain(); // Get newly-created login keychain
1777 defaultKeychain(keychain
); // Set it to be the default
1779 free(authEnvirItemArrayPtr
);
1780 AuthorizationFree(authRef
, kAuthorizationFlagDefaults
);
1785 // clean up allocations, then rethrow error
1786 if ( authEnvirItemArrayPtr
)
1787 free(authEnvirItemArrayPtr
);
1789 AuthorizationFree(authRef
, kAuthorizationFlagDefaults
);
1796 Keychain
StorageManager::defaultKeychainUI(Item
&item
)
1798 StLock
<Mutex
>_(mMutex
);
1800 Keychain returnedKeychain
;
1803 returnedKeychain
= defaultKeychain(); // If we have one, return it.
1804 if ( returnedKeychain
->exists() )
1805 return returnedKeychain
;
1807 catch(...) // We could have one, but it isn't available (i.e. on a un-mounted volume).
1810 if ( globals().getUserInteractionAllowed() )
1812 returnedKeychain
= makeLoginAuthUI(&item
); // If no Keychains is present, one will be created.
1813 if ( !returnedKeychain
)
1814 MacOSError::throwMe(errSecInvalidKeychain
); // Something went wrong...
1817 MacOSError::throwMe(errSecInteractionNotAllowed
); // If UI isn't allowed, return an error.
1819 return returnedKeychain
;
1823 StorageManager::addToDomainList(SecPreferencesDomain domain
,
1824 const char* dbName
, const CSSM_GUID
&guid
, uint32 subServiceType
)
1826 StLock
<Mutex
>_(mMutex
);
1828 if (domain
== kSecPreferencesDomainDynamic
)
1829 MacOSError::throwMe(errSecInvalidPrefsDomain
);
1831 // make the identifier
1832 CSSM_VERSION version
= {0, 0};
1833 DLDbIdentifier id
= DLDbListCFPref::makeDLDbIdentifier (guid
, version
, 0,
1834 subServiceType
, dbName
, NULL
);
1836 if (domain
== mDomain
)
1838 // manipulate the user's list
1840 mSavedList
.revert(true);
1845 KCEventNotifier::PostKeychainEvent(kSecKeychainListChangedEvent
);
1849 // manipulate the other list
1850 DLDbListCFPref(domain
).add(id
);
1855 StorageManager::isInDomainList(SecPreferencesDomain domain
,
1856 const char* dbName
, const CSSM_GUID
&guid
, uint32 subServiceType
)
1858 StLock
<Mutex
>_(mMutex
);
1860 if (domain
== kSecPreferencesDomainDynamic
)
1861 MacOSError::throwMe(errSecInvalidPrefsDomain
);
1863 CSSM_VERSION version
= {0, 0};
1864 DLDbIdentifier id
= DLDbListCFPref::makeDLDbIdentifier (guid
, version
, 0,
1865 subServiceType
, dbName
, NULL
);
1867 // determine the list to search
1869 if (domain
== mDomain
)
1871 result
= mSavedList
.member(id
);
1875 result
= DLDbListCFPref(domain
).member(id
);
1881 MacOSError::throwMe(errSecNoSuchKeychain
);
1886 StorageManager::removeFromDomainList(SecPreferencesDomain domain
,
1887 const char* dbName
, const CSSM_GUID
&guid
, uint32 subServiceType
)
1889 StLock
<Mutex
>_(mMutex
);
1891 if (domain
== kSecPreferencesDomainDynamic
)
1892 MacOSError::throwMe(errSecInvalidPrefsDomain
);
1894 // make the identifier
1895 CSSM_VERSION version
= {0, 0};
1896 DLDbIdentifier id
= DLDbListCFPref::makeDLDbIdentifier (guid
, version
, 0,
1897 subServiceType
, dbName
, NULL
);
1899 if (domain
== mDomain
)
1901 // manipulate the user's list
1903 mSavedList
.revert(true);
1904 mSavedList
.remove(id
);
1908 KCEventNotifier::PostKeychainEvent(kSecKeychainListChangedEvent
);
1912 // manipulate the other list
1913 DLDbListCFPref(domain
).remove(id
);
1918 StorageManager::keychainOwnerPermissionsValidForDomain(const char* path
, SecPreferencesDomain domain
)
1922 const char* sysPrefDir
= "/Library/Preferences";
1923 const char* errMsg
= "Will not set default";
1924 char* mustOwnDir
= NULL
;
1925 struct passwd
* pw
= NULL
;
1928 uid_t uid
= geteuid();
1929 if (!uid
) uid
= getuid();
1931 // our (e)uid must own the appropriate preferences or home directory
1932 // for the specified preference domain whose default we will be modifying
1934 case kSecPreferencesDomainUser
:
1935 mustOwnDir
= getenv("HOME");
1936 if (mustOwnDir
== NULL
) {
1938 if (!pw
) return false;
1939 mustOwnDir
= pw
->pw_dir
;
1942 case kSecPreferencesDomainSystem
:
1943 mustOwnDir
= (char*)sysPrefDir
;
1945 case kSecPreferencesDomainCommon
:
1946 mustOwnDir
= (char*)sysPrefDir
;
1952 if (mustOwnDir
!= NULL
) {
1954 if ( (stat(mustOwnDir
, &dsb
) != 0) || (dsb
.st_uid
!= uid
) ) {
1955 fprintf(stderr
, "%s: UID=%d does not own directory %s\n", errMsg
, (int)uid
, mustOwnDir
);
1956 mustOwnDir
= NULL
; // will return below after calling endpwent()
1963 if (mustOwnDir
== NULL
)
1966 // check that file actually exists
1967 if (stat(path
, &sb
) != 0) {
1968 fprintf(stderr
, "%s: file %s does not exist\n", errMsg
, path
);
1973 if (sb
.st_flags
& (SF_IMMUTABLE
| UF_IMMUTABLE
)) {
1974 fprintf(stderr
, "%s: file %s is immutable\n", errMsg
, path
);
1979 if (sb
.st_uid
!= uid
) {
1980 fprintf(stderr
, "%s: file %s is owned by UID=%d, but we have UID=%d\n",
1981 errMsg
, path
, (int)sb
.st_uid
, (int)uid
);
1987 perms
|= 0600; // must have owner read/write permission set
1988 if (sb
.st_mode
!= perms
) {
1989 fprintf(stderr
, "%s: file %s does not have the expected permissions\n", errMsg
, path
);
1993 // user owns file and can read/write it