2 * Copyright (c) 2000-2004,2012 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>
40 #include <CoreServices/../Frameworks/CarbonCore.framework/Headers/MacErrors.h>
44 //#include <Security/AuthorizationTags.h>
45 //#include <Security/AuthSession.h>
46 #include <security_utilities/debugging.h>
47 #include <security_keychain/SecCFTypes.h>
48 //#include <Security/SecurityAgentClient.h>
49 #include <securityd_client/ssclient.h>
50 #include <Security/AuthorizationTags.h>
51 #include <Security/AuthorizationTagsPriv.h>
52 #include <Security/SecTask.h>
53 #include <security_keychain/SecCFTypes.h>
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"
64 using namespace CssmClient
;
65 using namespace KeychainCore
;
67 #define kLoginKeychainPathPrefix "~/Library/Keychains/"
68 #define kUserLoginKeychainPath "~/Library/Keychains/login.keychain"
69 #define kEmptyKeychainSizeInBytes 20460
71 //-----------------------------------------------------------------------------------
73 static SecPreferencesDomain
defaultPreferenceDomain()
75 SessionAttributeBits sessionAttrs
;
77 secdebug("servermode", "StorageManager initialized in server mode");
78 sessionAttrs
= sessionIsRoot
;
80 MacOSError::check(SessionGetInfo(callerSecuritySession
, NULL
, &sessionAttrs
));
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
;
92 // otherwise, use normal (user) preferences
93 return kSecPreferencesDomainUser
;
96 static bool isAppSandboxed()
99 SecTaskRef task
= SecTaskCreateFromSelf(NULL
);
101 CFTypeRef appSandboxValue
= SecTaskCopyValueForEntitlement(task
,
102 CFSTR("com.apple.security.app-sandbox"), NULL
);
103 if(appSandboxValue
!= NULL
) {
105 CFRelease(appSandboxValue
);
113 StorageManager::StorageManager() :
114 mSavedList(defaultPreferenceDomain()),
115 mCommonList(kSecPreferencesDomainCommon
),
116 mDomain(kSecPreferencesDomainUser
),
117 mMutex(Mutex::recursive
)
123 StorageManager::getStorageManagerMutex()
125 return &mKeychainMapMutex
;
130 StorageManager::keychain(const DLDbIdentifier
&dLDbIdentifier
)
132 StLock
<Mutex
>_(mKeychainMapMutex
);
138 secdebug("servermode", "keychain reference in server mode");
142 KeychainMap::iterator it
= mKeychains
.find(dLDbIdentifier
);
143 if (it
!= mKeychains
.end())
145 if (it
->second
== NULL
) // cleared by weak reference?
147 mKeychains
.erase(it
);
155 // The keychain is not in our cache. Create it.
156 Module
module(dLDbIdentifier
.ssuid().guid());
158 if (dLDbIdentifier
.ssuid().subserviceType() & CSSM_SERVICE_CSP
)
159 dl
= SSCSPDL(module);
163 dl
->subserviceId(dLDbIdentifier
.ssuid().subserviceId());
164 dl
->version(dLDbIdentifier
.ssuid().version());
165 Db
db(dl
, dLDbIdentifier
.dbName());
167 Keychain
keychain(db
);
168 // Add the keychain to the cache.
169 mKeychains
.insert(KeychainMap::value_type(dLDbIdentifier
, &*keychain
));
170 keychain
->inCache(true);
176 StorageManager::removeKeychain(const DLDbIdentifier
&dLDbIdentifier
,
177 KeychainImpl
*keychainImpl
)
179 // Lock the recursive mutex
181 StLock
<Mutex
>_(mKeychainMapMutex
);
183 KeychainMap::iterator it
= mKeychains
.find(dLDbIdentifier
);
184 if (it
!= mKeychains
.end() && (KeychainImpl
*) it
->second
== keychainImpl
)
185 mKeychains
.erase(it
);
187 keychainImpl
->inCache(false);
191 StorageManager::didRemoveKeychain(const DLDbIdentifier
&dLDbIdentifier
)
193 // Lock the recursive mutex
195 StLock
<Mutex
>_(mKeychainMapMutex
);
197 KeychainMap::iterator it
= mKeychains
.find(dLDbIdentifier
);
198 if (it
!= mKeychains
.end())
200 if (it
->second
!= NULL
) // did we get zapped by weak reference destruction
202 KeychainImpl
*keychainImpl
= it
->second
;
203 keychainImpl
->inCache(false);
206 mKeychains
.erase(it
);
210 // Create keychain if it doesn't exist, and optionally add it to the search list.
212 StorageManager::makeKeychain(const DLDbIdentifier
&dLDbIdentifier
, bool add
)
214 StLock
<Mutex
>_(mKeychainMapMutex
);
216 Keychain theKeychain
= keychain(dLDbIdentifier
);
218 bool updateList
= (add
&& !isAppSandboxed());
222 mSavedList
.revert(false);
223 DLDbList searchList
= mSavedList
.searchList();
224 if (find(searchList
.begin(), searchList
.end(), dLDbIdentifier
) != searchList
.end())
225 return theKeychain
; // theKeychain is already in the searchList.
227 mCommonList
.revert(false);
228 searchList
= mCommonList
.searchList();
229 if (find(searchList
.begin(), searchList
.end(), dLDbIdentifier
) != searchList
.end())
230 return theKeychain
; // theKeychain is already in the commonList don't add it to the searchList.
232 // If theKeychain doesn't exist don't bother adding it to the search list yet.
233 if (!theKeychain
->exists())
236 // theKeychain exists and is not in our search list, so add it to the
238 mSavedList
.revert(true);
239 mSavedList
.add(dLDbIdentifier
);
246 // Make sure we are not holding mStorageManagerLock anymore when we
248 KCEventNotifier::PostKeychainEvent(kSecKeychainListChangedEvent
);
254 // Be notified a Keychain just got created.
256 StorageManager::created(const Keychain
&keychain
)
258 StLock
<Mutex
>_(mKeychainMapMutex
);
260 DLDbIdentifier dLDbIdentifier
= keychain
->dlDbIdentifier();
261 bool defaultChanged
= false;
262 bool updateList
= (!isAppSandboxed());
266 mSavedList
.revert(true);
267 // If we don't have a default Keychain yet. Make the newly created
268 // keychain the default.
269 if (!mSavedList
.defaultDLDbIdentifier())
271 mSavedList
.defaultDLDbIdentifier(dLDbIdentifier
);
272 defaultChanged
= true;
275 // Add the keychain to the search list prefs.
276 mSavedList
.add(dLDbIdentifier
);
279 // Make sure we are not holding mLock when we post these events.
280 KCEventNotifier::PostKeychainEvent(kSecKeychainListChangedEvent
);
285 KCEventNotifier::PostKeychainEvent(kSecDefaultChangedEvent
, dLDbIdentifier
);
290 StorageManager::createCursor(SecItemClass itemClass
,
291 const SecKeychainAttributeList
*attrList
)
293 StLock
<Mutex
>_(mMutex
);
295 KeychainList searchList
;
296 getSearchList(searchList
);
297 return KCCursor(searchList
, itemClass
, attrList
);
301 StorageManager::createCursor(const SecKeychainAttributeList
*attrList
)
303 StLock
<Mutex
>_(mMutex
);
305 KeychainList searchList
;
306 getSearchList(searchList
);
307 return KCCursor(searchList
, attrList
);
311 StorageManager::lockAll()
313 StLock
<Mutex
>_(mMutex
);
315 SecurityServer::ClientSession
ss(Allocator::standard(), Allocator::standard());
320 StorageManager::defaultKeychain()
322 StLock
<Mutex
>_(mMutex
);
324 Keychain theKeychain
;
328 mSavedList
.revert(false);
329 DLDbIdentifier
defaultDLDbIdentifier(mSavedList
.defaultDLDbIdentifier());
330 if (defaultDLDbIdentifier
)
332 theKeychain
= keychain(defaultDLDbIdentifier
);
333 ref
= theKeychain
->handle(false);
337 if (theKeychain
/* && theKeychain->exists() */)
340 MacOSError::throwMe(errSecNoDefaultKeychain
);
344 StorageManager::defaultKeychain(const Keychain
&keychain
)
346 StLock
<Mutex
>_(mMutex
);
348 // Only set a keychain as the default if we own it and can read/write it,
349 // and our uid allows modifying the directory for that preference domain.
350 if (!keychainOwnerPermissionsValidForDomain(keychain
->name(), mDomain
))
351 MacOSError::throwMe(wrPermErr
);
353 DLDbIdentifier oldDefaultId
;
354 DLDbIdentifier
newDefaultId(keychain
->dlDbIdentifier());
356 oldDefaultId
= mSavedList
.defaultDLDbIdentifier();
357 mSavedList
.revert(true);
358 mSavedList
.defaultDLDbIdentifier(newDefaultId
);
362 if (!(oldDefaultId
== newDefaultId
))
364 // Make sure we are not holding mLock when we post this event.
365 KCEventNotifier::PostKeychainEvent(kSecDefaultChangedEvent
, newDefaultId
);
370 StorageManager::defaultKeychain(SecPreferencesDomain domain
)
372 StLock
<Mutex
>_(mMutex
);
374 if (domain
== kSecPreferencesDomainDynamic
)
375 MacOSError::throwMe(errSecInvalidPrefsDomain
);
377 if (domain
== mDomain
)
378 return defaultKeychain();
381 DLDbIdentifier
defaultDLDbIdentifier(DLDbListCFPref(domain
).defaultDLDbIdentifier());
382 if (defaultDLDbIdentifier
)
383 return keychain(defaultDLDbIdentifier
);
385 MacOSError::throwMe(errSecNoDefaultKeychain
);
390 StorageManager::defaultKeychain(SecPreferencesDomain domain
, const Keychain
&keychain
)
392 StLock
<Mutex
>_(mMutex
);
394 if (domain
== kSecPreferencesDomainDynamic
)
395 MacOSError::throwMe(errSecInvalidPrefsDomain
);
397 if (domain
== mDomain
)
398 defaultKeychain(keychain
);
400 DLDbListCFPref(domain
).defaultDLDbIdentifier(keychain
->dlDbIdentifier());
404 StorageManager::loginKeychain()
406 StLock
<Mutex
>_(mMutex
);
408 Keychain theKeychain
;
410 mSavedList
.revert(false);
411 DLDbIdentifier
loginDLDbIdentifier(mSavedList
.loginDLDbIdentifier());
412 if (loginDLDbIdentifier
)
414 theKeychain
= keychain(loginDLDbIdentifier
);
418 if (theKeychain
&& theKeychain
->exists())
421 MacOSError::throwMe(errSecNoSuchKeychain
);
425 StorageManager::loginKeychain(Keychain keychain
)
427 StLock
<Mutex
>_(mMutex
);
429 mSavedList
.revert(true);
430 mSavedList
.loginDLDbIdentifier(keychain
->dlDbIdentifier());
435 StorageManager::size()
437 StLock
<Mutex
>_(mMutex
);
439 mSavedList
.revert(false);
440 mCommonList
.revert(false);
441 return mSavedList
.searchList().size() + mCommonList
.searchList().size();
445 StorageManager::at(unsigned int ix
)
447 StLock
<Mutex
>_(mMutex
);
449 mSavedList
.revert(false);
450 DLDbList dLDbList
= mSavedList
.searchList();
451 if (ix
< dLDbList
.size())
453 return keychain(dLDbList
[ix
]);
457 ix
-= dLDbList
.size();
458 mCommonList
.revert(false);
459 DLDbList commonList
= mCommonList
.searchList();
460 if (ix
>= commonList
.size())
461 MacOSError::throwMe(errSecInvalidKeychain
);
463 return keychain(commonList
[ix
]);
468 StorageManager::operator[](unsigned int ix
)
470 StLock
<Mutex
>_(mMutex
);
475 void StorageManager::rename(Keychain keychain
, const char* newName
)
478 StLock
<Mutex
>_(mKeychainMapMutex
);
480 bool changedDefault
= false;
481 DLDbIdentifier newDLDbIdentifier
;
483 mSavedList
.revert(true);
484 DLDbIdentifier defaultId
= mSavedList
.defaultDLDbIdentifier();
486 // Find the keychain object for the given ref
487 DLDbIdentifier dLDbIdentifier
= keychain
->dlDbIdentifier();
489 // Actually rename the database on disk.
490 keychain
->database()->rename(newName
);
492 if (dLDbIdentifier
== defaultId
)
495 newDLDbIdentifier
= keychain
->dlDbIdentifier();
496 // Rename the keychain in the search list.
497 mSavedList
.rename(dLDbIdentifier
, newDLDbIdentifier
);
499 // If this was the default keychain change it accordingly
501 mSavedList
.defaultDLDbIdentifier(newDLDbIdentifier
);
505 // we aren't worried about a weak reference here, because we have to
506 // hold a lock on an item in order to do the rename
508 // Now update the Keychain cache
509 if (keychain
->inCache())
511 KeychainMap::iterator it
= mKeychains
.find(dLDbIdentifier
);
512 if (it
!= mKeychains
.end() && (KeychainImpl
*) it
->second
== keychain
.get())
514 // Remove the keychain from the cache under its old
516 mKeychains
.erase(it
);
520 // If we renamed this keychain on top of an existing one we should
521 // drop the old one from the cache.
522 KeychainMap::iterator it
= mKeychains
.find(newDLDbIdentifier
);
523 if (it
!= mKeychains
.end())
525 Keychain
oldKeychain(it
->second
);
526 oldKeychain
->inCache(false);
527 // @@@ Ideally we should invalidate or fault this keychain object.
530 if (keychain
->inCache())
532 // If the keychain wasn't in the cache to being with let's not put
533 // it there now. There was probably a good reason it wasn't in it.
534 // If the keychain was in the cache, update it to use
535 // newDLDbIdentifier.
536 mKeychains
.insert(KeychainMap::value_type(newDLDbIdentifier
,
541 // Make sure we are not holding mLock when we post these events.
542 KCEventNotifier::PostKeychainEvent(kSecKeychainListChangedEvent
);
545 KCEventNotifier::PostKeychainEvent(kSecDefaultChangedEvent
,
549 void StorageManager::renameUnique(Keychain keychain
, CFStringRef newName
)
551 StLock
<Mutex
>_(mMutex
);
553 bool doneCreating
= false;
557 char newNameCString
[MAXPATHLEN
];
558 if ( CFStringGetCString(newName
, newNameCString
, MAXPATHLEN
, kCFStringEncodingUTF8
) ) // make sure it fits in MAXPATHLEN, etc.
560 // Construct the new name...
562 CFMutableStringRef newNameCFStr
= NULL
;
563 newNameCFStr
= CFStringCreateMutable(NULL
, MAXPATHLEN
);
566 CFStringAppendFormat(newNameCFStr
, NULL
, CFSTR("%s%d"), &newNameCString
, index
);
567 CFStringAppend(newNameCFStr
, CFSTR(kKeychainSuffix
)); // add .keychain
568 char toUseBuff2
[MAXPATHLEN
];
569 if ( CFStringGetCString(newNameCFStr
, toUseBuff2
, MAXPATHLEN
, kCFStringEncodingUTF8
) ) // make sure it fits in MAXPATHLEN, etc.
572 if ( lstat(toUseBuff2
, &filebuf
) )
574 rename(keychain
, toUseBuff2
);
576 kcList
.push_back(keychain
);
577 remove(kcList
, false);
584 doneCreating
= true; // failure to get c string.
585 CFRelease(newNameCFStr
);
588 doneCreating
= false; // failure to create mutable string.
591 doneCreating
= false; // failure to get the string (i.e. > MAXPATHLEN?)
593 while (!doneCreating
&& index
!= INT_MAX
);
596 #define KEYCHAIN_SYNC_KEY CFSTR("KeychainSyncList")
597 #define KEYCHAIN_SYNC_DOMAIN CFSTR("com.apple.keychainsync")
599 static CFStringRef
MakeExpandedPath (const char* path
)
601 std::string name
= DLDbListCFPref::ExpandTildesInPath (std::string (path
));
602 CFStringRef expanded
= CFStringCreateWithCString (NULL
, name
.c_str (), 0);
606 void StorageManager::removeKeychainFromSyncList (const DLDbIdentifier
&id
)
608 StLock
<Mutex
>_(mMutex
);
610 // make a CFString of our identifier
611 const char* idname
= id
.dbName ();
617 CFRef
<CFStringRef
> idString
= MakeExpandedPath (idname
);
619 // check and see if this keychain is in the keychain syncing list
621 (CFArrayRef
) CFPreferencesCopyValue (KEYCHAIN_SYNC_KEY
,
622 KEYCHAIN_SYNC_DOMAIN
,
623 kCFPreferencesCurrentUser
,
624 kCFPreferencesAnyHost
);
630 // make a mutable copy of the dictionary
631 CFRef
<CFMutableArrayRef
> mtValue
= CFArrayCreateMutableCopy (NULL
, 0, value
);
634 // walk the array, looking for the value
636 CFIndex limit
= CFArrayGetCount (mtValue
.get());
639 for (i
= 0; i
< limit
; ++i
)
641 CFDictionaryRef idx
= (CFDictionaryRef
) CFArrayGetValueAtIndex (mtValue
.get(), i
);
642 CFStringRef v
= (CFStringRef
) CFDictionaryGetValue (idx
, CFSTR("DbName"));
645 return; // something is really wrong if this is taken
648 CFStringRef vExpanded
= MakeExpandedPath (CFStringGetCStringPtr (v
, 0));
649 CFComparisonResult result
= CFStringCompare (vExpanded
, idString
.get(), 0);
650 CFRelease (vExpanded
);
654 CFArrayRemoveValueAtIndex (mtValue
.get(), i
);
663 CFShow (mtValue
.get());
666 CFPreferencesSetValue (KEYCHAIN_SYNC_KEY
,
668 KEYCHAIN_SYNC_DOMAIN
,
669 kCFPreferencesCurrentUser
,
670 kCFPreferencesAnyHost
);
671 CFPreferencesSynchronize (KEYCHAIN_SYNC_DOMAIN
, kCFPreferencesCurrentUser
, kCFPreferencesAnyHost
);
675 void StorageManager::remove(const KeychainList
&kcsToRemove
, bool deleteDb
)
677 StLock
<Mutex
>_(mMutex
);
679 bool unsetDefault
= false;
680 bool updateList
= (!isAppSandboxed());
684 mSavedList
.revert(true);
685 DLDbIdentifier defaultId
= mSavedList
.defaultDLDbIdentifier();
686 for (KeychainList::const_iterator ix
= kcsToRemove
.begin();
687 ix
!= kcsToRemove
.end(); ++ix
)
689 // Find the keychain object for the given ref
690 Keychain theKeychain
= *ix
;
691 DLDbIdentifier dLDbIdentifier
= theKeychain
->dlDbIdentifier();
693 // Remove it from the saved list
694 mSavedList
.remove(dLDbIdentifier
);
695 if (dLDbIdentifier
== defaultId
)
700 removeKeychainFromSyncList (dLDbIdentifier
);
702 // Now remove it from the cache
703 removeKeychain(dLDbIdentifier
, theKeychain
.get());
708 mSavedList
.defaultDLDbIdentifier(DLDbIdentifier());
715 // Delete the actual databases without holding any locks.
716 for (KeychainList::const_iterator ix
= kcsToRemove
.begin();
717 ix
!= kcsToRemove
.end(); ++ix
)
719 (*ix
)->database()->deleteDb();
724 // Make sure we are not holding mLock when we post these events.
725 KCEventNotifier::PostKeychainEvent(kSecKeychainListChangedEvent
);
729 KCEventNotifier::PostKeychainEvent(kSecDefaultChangedEvent
);
733 StorageManager::getSearchList(KeychainList
&keychainList
)
735 // hold the global lock since we make keychain objects in this function
737 // to do: each of the items in this list must be retained, otherwise mayhem will occur
738 StLock
<Mutex
>_(mMutex
);
741 keychainList
.clear();
745 mSavedList
.revert(false);
746 mCommonList
.revert(false);
748 // Merge mSavedList, mDynamicList and mCommonList
749 DLDbList dLDbList
= mSavedList
.searchList();
750 DLDbList dynamicList
= mDynamicList
.searchList();
751 DLDbList commonList
= mCommonList
.searchList();
753 result
.reserve(dLDbList
.size() + dynamicList
.size() + commonList
.size());
756 for (DLDbList::const_iterator it
= dynamicList
.begin();
757 it
!= dynamicList
.end(); ++it
)
759 Keychain k
= keychain(*it
);
763 for (DLDbList::const_iterator it
= dLDbList
.begin();
764 it
!= dLDbList
.end(); ++it
)
766 Keychain k
= keychain(*it
);
770 for (DLDbList::const_iterator it
= commonList
.begin();
771 it
!= commonList
.end(); ++it
)
773 Keychain k
= keychain(*it
);
778 keychainList
.swap(result
);
782 StorageManager::setSearchList(const KeychainList
&keychainList
)
784 StLock
<Mutex
>_(mMutex
);
786 DLDbList commonList
= mCommonList
.searchList();
788 // Strip out the common list part from the end of the search list.
789 KeychainList::const_iterator it_end
= keychainList
.end();
790 DLDbList::const_reverse_iterator end_common
= commonList
.rend();
791 for (DLDbList::const_reverse_iterator it_common
= commonList
.rbegin(); it_common
!= end_common
; ++it_common
)
793 // Eliminate common entries from the end of the passed in keychainList.
794 if (it_end
== keychainList
.begin())
798 if (!((*it_end
)->dlDbIdentifier() == *it_common
))
805 /* it_end now points one past the last element in keychainList which is not in commonList. */
806 DLDbList searchList
, oldSearchList(mSavedList
.searchList());
807 for (KeychainList::const_iterator it
= keychainList
.begin(); it
!= it_end
; ++it
)
809 searchList
.push_back((*it
)->dlDbIdentifier());
813 // Set the current searchlist to be what was passed in, the old list will be freed
814 // upon exit of this stackframe.
815 mSavedList
.revert(true);
816 mSavedList
.searchList(searchList
);
820 if (!(oldSearchList
== searchList
))
822 // Make sure we are not holding mLock when we post this event.
823 KCEventNotifier::PostKeychainEvent(kSecKeychainListChangedEvent
);
828 StorageManager::getSearchList(SecPreferencesDomain domain
, KeychainList
&keychainList
)
830 StLock
<Mutex
>_(mMutex
);
833 keychainList
.clear();
837 if (domain
== kSecPreferencesDomainDynamic
)
839 convertList(keychainList
, mDynamicList
.searchList());
841 else if (domain
== mDomain
)
843 mSavedList
.revert(false);
844 convertList(keychainList
, mSavedList
.searchList());
848 convertList(keychainList
, DLDbListCFPref(domain
).searchList());
852 void StorageManager::forceUserSearchListReread()
854 mSavedList
.forceUserSearchListReread();
858 StorageManager::setSearchList(SecPreferencesDomain domain
, const KeychainList
&keychainList
)
860 StLock
<Mutex
>_(mMutex
);
862 if (domain
== kSecPreferencesDomainDynamic
)
863 MacOSError::throwMe(errSecInvalidPrefsDomain
);
866 convertList(searchList
, keychainList
);
868 if (domain
== mDomain
)
870 DLDbList
oldSearchList(mSavedList
.searchList());
872 // Set the current searchlist to be what was passed in, the old list will be freed
873 // upon exit of this stackframe.
874 mSavedList
.revert(true);
875 mSavedList
.searchList(searchList
);
879 if (!(oldSearchList
== searchList
))
881 KCEventNotifier::PostKeychainEvent(kSecKeychainListChangedEvent
);
886 DLDbListCFPref(domain
).searchList(searchList
);
891 StorageManager::domain(SecPreferencesDomain domain
)
893 StLock
<Mutex
>_(mMutex
);
895 if (domain
== kSecPreferencesDomainDynamic
)
896 MacOSError::throwMe(errSecInvalidPrefsDomain
);
898 if (domain
== mDomain
)
904 case kSecPreferencesDomainSystem
:
905 secdebug("storagemgr", "switching to system domain"); break;
906 case kSecPreferencesDomainUser
:
907 secdebug("storagemgr", "switching to user domain (uid %d)", getuid()); break;
909 secdebug("storagemgr", "switching to weird prefs domain %d", domain
); break;
914 mSavedList
.set(domain
);
918 StorageManager::optionalSearchList(CFTypeRef keychainOrArray
, KeychainList
&keychainList
)
920 StLock
<Mutex
>_(mMutex
);
922 if (!keychainOrArray
)
923 getSearchList(keychainList
);
926 CFTypeID typeID
= CFGetTypeID(keychainOrArray
);
927 if (typeID
== CFArrayGetTypeID())
928 convertToKeychainList(CFArrayRef(keychainOrArray
), keychainList
);
929 else if (typeID
== gTypes().KeychainImpl
.typeID
)
930 keychainList
.push_back(KeychainImpl::required(SecKeychainRef(keychainOrArray
)));
932 MacOSError::throwMe(paramErr
);
938 StorageManager::convertToKeychainList(CFArrayRef keychainArray
, KeychainList
&keychainList
)
940 CFIndex count
= CFArrayGetCount(keychainArray
);
944 KeychainList
keychains(count
);
945 for (CFIndex ix
= 0; ix
< count
; ++ix
)
947 keychains
[ix
] = KeychainImpl::required(SecKeychainRef(CFArrayGetValueAtIndex(keychainArray
, ix
)));
950 keychainList
.swap(keychains
);
954 StorageManager::convertFromKeychainList(const KeychainList
&keychainList
)
956 CFRef
<CFMutableArrayRef
> keychainArray(CFArrayCreateMutable(NULL
, keychainList
.size(), &kCFTypeArrayCallBacks
));
958 for (KeychainList::const_iterator ix
= keychainList
.begin(); ix
!= keychainList
.end(); ++ix
)
960 SecKeychainRef keychainRef
= (*ix
)->handle();
961 CFArrayAppendValue(keychainArray
, keychainRef
);
962 CFRelease(keychainRef
);
965 // Counter the CFRelease that CFRef<> is about to do when keychainArray goes out of scope.
966 CFRetain(keychainArray
);
967 return keychainArray
;
970 void StorageManager::convertList(DLDbList
&ids
, const KeychainList
&kcs
)
973 result
.reserve(kcs
.size());
974 for (KeychainList::const_iterator ix
= kcs
.begin(); ix
!= kcs
.end(); ++ix
)
976 result
.push_back((*ix
)->dlDbIdentifier());
981 void StorageManager::convertList(KeychainList
&kcs
, const DLDbList
&ids
)
983 StLock
<Mutex
>_(mMutex
);
986 result
.reserve(ids
.size());
988 for (DLDbList::const_iterator ix
= ids
.begin(); ix
!= ids
.end(); ++ix
)
989 result
.push_back(keychain(*ix
));
994 #pragma mark ____ Login Functions ____
996 void StorageManager::login(AuthorizationRef authRef
, UInt32 nameLength
, const char* name
)
998 StLock
<Mutex
>_(mMutex
);
1000 AuthorizationItemSet
* info
= NULL
;
1001 OSStatus result
= AuthorizationCopyInfo(authRef
, NULL
, &info
); // get the results of the copy rights call.
1002 Boolean created
= false;
1003 if ( result
== noErr
&& info
->count
)
1005 // Grab the password from the auth context (info) and create the keychain...
1007 AuthorizationItem
* currItem
= info
->items
;
1008 for (UInt32 index
= 1; index
<= info
->count
; index
++) //@@@plugin bug won't return a specific context.
1010 if (strcmp(currItem
->name
, kAuthorizationEnvironmentPassword
) == 0)
1012 // creates the login keychain with the specified password
1015 login(nameLength
, name
, currItem
->valueLength
, currItem
->value
);
1027 AuthorizationFreeItemSet(info
);
1030 MacOSError::throwMe(errAuthorizationInternal
);
1033 void StorageManager::login(ConstStringPtr name
, ConstStringPtr password
)
1035 StLock
<Mutex
>_(mMutex
);
1037 if ( name
== NULL
|| password
== NULL
)
1038 MacOSError::throwMe(paramErr
);
1040 login(name
[0], name
+ 1, password
[0], password
+ 1);
1043 void StorageManager::login(UInt32 nameLength
, const void *name
,
1044 UInt32 passwordLength
, const void *password
)
1046 if (passwordLength
!= 0 && password
== NULL
)
1048 secdebug("KCLogin", "StorageManager::login: invalid argument (NULL password)");
1049 MacOSError::throwMe(paramErr
);
1052 DLDbIdentifier loginDLDbIdentifier
;
1054 mSavedList
.revert(true);
1055 loginDLDbIdentifier
= mSavedList
.loginDLDbIdentifier();
1058 secdebug("KCLogin", "StorageManager::login: loginDLDbIdentifier is %s", (loginDLDbIdentifier
) ? loginDLDbIdentifier
.dbName() : "<NULL>");
1059 if (!loginDLDbIdentifier
)
1060 MacOSError::throwMe(errSecNoSuchKeychain
);
1063 //***************************************************************
1064 // gather keychain information
1065 //***************************************************************
1068 int uid
= geteuid();
1069 struct passwd
*pw
= getpwuid(uid
);
1071 secdebug("KCLogin", "StorageManager::login: invalid argument (NULL uid)");
1072 MacOSError::throwMe(paramErr
);
1074 char *userName
= pw
->pw_name
;
1076 // make keychain path strings
1077 std::string keychainPath
= DLDbListCFPref::ExpandTildesInPath(kLoginKeychainPathPrefix
);
1078 std::string shortnameKeychain
= keychainPath
+ userName
;
1079 std::string shortnameDotKeychain
= shortnameKeychain
+ ".keychain";
1080 std::string loginDotKeychain
= keychainPath
+ "login.keychain";
1081 std::string loginRenamed1Keychain
= keychainPath
+ "login_renamed1.keychain";
1083 // check for existence of keychain files
1084 bool shortnameKeychainExists
= false;
1085 bool shortnameDotKeychainExists
= false;
1086 bool loginKeychainExists
= false;
1087 bool loginRenamed1KeychainExists
= false;
1091 stat_result
= ::stat(shortnameKeychain
.c_str(), &st
);
1092 shortnameKeychainExists
= (stat_result
== 0);
1093 stat_result
= ::stat(shortnameDotKeychain
.c_str(), &st
);
1094 shortnameDotKeychainExists
= (stat_result
== 0);
1095 stat_result
= ::stat(loginDotKeychain
.c_str(), &st
);
1096 loginKeychainExists
= (stat_result
== 0);
1097 stat_result
= ::stat(loginRenamed1Keychain
.c_str(), &st
);
1098 loginRenamed1KeychainExists
= (stat_result
== 0);
1101 bool loginUnlocked
= false;
1103 // make the keychain identifiers
1104 CSSM_VERSION version
= {0, 0};
1105 DLDbIdentifier shortnameDLDbIdentifier
= DLDbListCFPref::makeDLDbIdentifier(gGuidAppleCSPDL
, version
, 0, CSSM_SERVICE_CSP
| CSSM_SERVICE_DL
, shortnameKeychain
.c_str(), NULL
);
1106 DLDbIdentifier shortnameDotDLDbIdentifier
= DLDbListCFPref::makeDLDbIdentifier(gGuidAppleCSPDL
, version
, 0, CSSM_SERVICE_CSP
| CSSM_SERVICE_DL
, shortnameDotKeychain
.c_str(), NULL
);
1107 DLDbIdentifier loginRenamed1DLDbIdentifier
= DLDbListCFPref::makeDLDbIdentifier(gGuidAppleCSPDL
, version
, 0, CSSM_SERVICE_CSP
| CSSM_SERVICE_DL
, loginRenamed1Keychain
.c_str(), NULL
);
1109 //***************************************************************
1110 // make file renaming changes first
1111 //***************************************************************
1113 // if "~/Library/Keychains/shortname" exists, we need to migrate it forward;
1114 // either to login.keychain if there isn't already one, otherwise to shortname.keychain
1115 if (shortnameKeychainExists
) {
1116 int rename_stat
= 0;
1117 if (loginKeychainExists
) {
1119 int tmp_result
= ::stat(loginDotKeychain
.c_str(), &st
);
1120 if (tmp_result
== 0) {
1121 if (st
.st_size
<= kEmptyKeychainSizeInBytes
) {
1122 tmp_result
= ::unlink(loginDotKeychain
.c_str());
1123 rename_stat
= ::rename(shortnameKeychain
.c_str(), loginDotKeychain
.c_str());
1124 shortnameKeychainExists
= (rename_stat
!= 0);
1128 if (shortnameKeychainExists
) {
1129 if (loginKeychainExists
&& !shortnameDotKeychainExists
) {
1130 rename_stat
= ::rename(shortnameKeychain
.c_str(), shortnameDotKeychain
.c_str());
1131 shortnameDotKeychainExists
= (rename_stat
== 0);
1132 } else if (!loginKeychainExists
) {
1133 rename_stat
= ::rename(shortnameKeychain
.c_str(), loginDotKeychain
.c_str());
1134 loginKeychainExists
= (rename_stat
== 0);
1136 // we have all 3 keychains: login.keychain, shortname, and shortname.keychain.
1137 // on Leopard we never want a shortname keychain, so we must move it aside.
1138 char pathbuf
[MAXPATHLEN
];
1139 std::string shortnameRenamedXXXKeychain
= keychainPath
;
1140 shortnameRenamedXXXKeychain
+= userName
;
1141 shortnameRenamedXXXKeychain
+= "_renamed_XXX.keychain";
1142 ::strlcpy(pathbuf
, shortnameRenamedXXXKeychain
.c_str(), sizeof(pathbuf
));
1143 ::mkstemps(pathbuf
, 9); // 9 == strlen(".keychain")
1144 rename_stat
= ::rename(shortnameKeychain
.c_str(), pathbuf
);
1145 shortnameKeychainExists
= (rename_stat
!= 0);
1148 if (rename_stat
!= 0) {
1149 MacOSError::throwMe(errno
);
1153 //***************************************************************
1154 // handle special case where user previously reset the keychain
1155 //***************************************************************
1156 // Since 9A581, we have changed the definition of kKeychainRenamedSuffix from "_renamed" to "_renamed_".
1157 // Therefore, if "login_renamed1.keychain" exists and there is no plist, the user may have run into a
1158 // prior upgrade issue and clicked Reset. If we can successfully unlock login_renamed1.keychain with the
1159 // supplied password, then we will attempt to rename it to login.keychain if that file is empty, or with
1160 // "shortname.keychain" if it is not.
1162 if (loginRenamed1KeychainExists
&& (!loginKeychainExists
||
1163 (mSavedList
.searchList().size() == 1 && mSavedList
.member(loginDLDbIdentifier
)) )) {
1166 Keychain
loginRenamed1KC(keychain(loginRenamed1DLDbIdentifier
));
1167 secdebug("KCLogin", "Attempting to unlock %s with %d-character password",
1168 (loginRenamed1KC
) ? loginRenamed1KC
->name() : "<NULL>", (unsigned int)passwordLength
);
1169 loginRenamed1KC
->unlock(CssmData(const_cast<void *>(password
), passwordLength
));
1170 // if we get here, we unlocked it
1171 if (loginKeychainExists
) {
1173 int tmp_result
= ::stat(loginDotKeychain
.c_str(), &st
);
1174 if (tmp_result
== 0) {
1175 if (st
.st_size
<= kEmptyKeychainSizeInBytes
) {
1176 tmp_result
= ::unlink(loginDotKeychain
.c_str());
1177 tmp_result
= ::rename(loginRenamed1Keychain
.c_str(), loginDotKeychain
.c_str());
1178 } else if (!shortnameDotKeychainExists
) {
1179 tmp_result
= ::rename(loginRenamed1Keychain
.c_str(), shortnameDotKeychain
.c_str());
1180 shortnameDotKeychainExists
= (tmp_result
== 0);
1182 throw 1; // can't do anything with it except move it out of the way
1186 int tmp_result
= ::rename(loginRenamed1Keychain
.c_str(), loginDotKeychain
.c_str());
1187 loginKeychainExists
= (tmp_result
== 0);
1192 // we failed to unlock the login_renamed1.keychain file with the login password.
1193 // move it aside so we don't try to deal with it again.
1194 char pathbuf
[MAXPATHLEN
];
1195 std::string loginRenamedXXXKeychain
= keychainPath
;
1196 loginRenamedXXXKeychain
+= "login_renamed_XXX.keychain";
1197 ::strlcpy(pathbuf
, loginRenamedXXXKeychain
.c_str(), sizeof(pathbuf
));
1198 ::mkstemps(pathbuf
, 9); // 9 == strlen(".keychain")
1199 ::rename(loginRenamed1Keychain
.c_str(), pathbuf
);
1203 // if login.keychain does not exist at this point, create it
1204 if (!loginKeychainExists
) {
1205 Keychain
theKeychain(keychain(loginDLDbIdentifier
));
1206 secdebug("KCLogin", "Creating login keychain %s", (loginDLDbIdentifier
) ? loginDLDbIdentifier
.dbName() : "<NULL>");
1207 theKeychain
->create(passwordLength
, password
);
1208 secdebug("KCLogin", "Login keychain created successfully");
1209 loginKeychainExists
= true;
1210 // Set the prefs for this new login keychain.
1211 loginKeychain(theKeychain
);
1212 // Login Keychain does not lock on sleep nor lock after timeout by default.
1213 theKeychain
->setSettings(INT_MAX
, false);
1214 loginUnlocked
= true;
1215 mSavedList
.revert(true);
1218 //***************************************************************
1219 // make plist changes after files have been renamed or created
1220 //***************************************************************
1222 // if the shortname keychain exists in the search list, either rename or remove the entry
1223 if (mSavedList
.member(shortnameDLDbIdentifier
)) {
1224 if (shortnameDotKeychainExists
&& !mSavedList
.member(shortnameDotDLDbIdentifier
)) {
1225 // change shortname to shortname.keychain (login.keychain will be added later if not present)
1226 secdebug("KCLogin", "Renaming %s to %s in keychain search list",
1227 (shortnameDLDbIdentifier
) ? shortnameDLDbIdentifier
.dbName() : "<NULL>",
1228 (shortnameDotDLDbIdentifier
) ? shortnameDotDLDbIdentifier
.dbName() : "<NULL>");
1229 mSavedList
.rename(shortnameDLDbIdentifier
, shortnameDotDLDbIdentifier
);
1230 } else if (!mSavedList
.member(loginDLDbIdentifier
)) {
1231 // change shortname to login.keychain
1232 secdebug("KCLogin", "Renaming %s to %s in keychain search list",
1233 (shortnameDLDbIdentifier
) ? shortnameDLDbIdentifier
.dbName() : "<NULL>",
1234 (loginDLDbIdentifier
) ? loginDLDbIdentifier
.dbName() : "<NULL>");
1235 mSavedList
.rename(shortnameDLDbIdentifier
, loginDLDbIdentifier
);
1237 // already have login.keychain in list, and renaming to shortname.keychain isn't an option,
1238 // so just remove the entry
1239 secdebug("KCLogin", "Removing %s from keychain search list", (shortnameDLDbIdentifier
) ? shortnameDLDbIdentifier
.dbName() : "<NULL>");
1240 mSavedList
.remove(shortnameDLDbIdentifier
);
1243 // note: save() will cause the plist to be unlinked if the only remaining entry is for login.keychain
1245 mSavedList
.revert(true);
1248 // make sure that login.keychain is in the search list
1249 if (!mSavedList
.member(loginDLDbIdentifier
)) {
1250 secdebug("KCLogin", "Adding %s to keychain search list", (loginDLDbIdentifier
) ? loginDLDbIdentifier
.dbName() : "<NULL>");
1251 mSavedList
.add(loginDLDbIdentifier
);
1253 mSavedList
.revert(true);
1256 // if we have a shortname.keychain, always include it in the plist (after login.keychain)
1257 if (shortnameDotKeychainExists
&& !mSavedList
.member(shortnameDotDLDbIdentifier
)) {
1258 mSavedList
.add(shortnameDotDLDbIdentifier
);
1260 mSavedList
.revert(true);
1263 // make sure that the default keychain is in the search list; if not, reset the default to login.keychain
1264 if (!mSavedList
.member(mSavedList
.defaultDLDbIdentifier())) {
1265 secdebug("KCLogin", "Changing default keychain to %s", (loginDLDbIdentifier
) ? loginDLDbIdentifier
.dbName() : "<NULL>");
1266 mSavedList
.defaultDLDbIdentifier(loginDLDbIdentifier
);
1268 mSavedList
.revert(true);
1271 //***************************************************************
1272 // auto-unlock the login keychain(s)
1273 //***************************************************************
1274 // all our preflight fixups are finally done, so we can now attempt to unlock the login keychain
1276 OSStatus loginResult
= noErr
;
1277 if (!loginUnlocked
) {
1280 Keychain
theKeychain(keychain(loginDLDbIdentifier
));
1281 secdebug("KCLogin", "Attempting to unlock login keychain \"%s\" with %d-character password",
1282 (theKeychain
) ? theKeychain
->name() : "<NULL>", (unsigned int)passwordLength
);
1283 theKeychain
->unlock(CssmData(const_cast<void *>(password
), passwordLength
));
1284 loginUnlocked
= true;
1286 catch(const CssmError
&e
)
1288 loginResult
= e
.osStatus(); // save this result
1292 // if "shortname.keychain" exists and is in the search list, attempt to auto-unlock it with the same password
1293 if (shortnameDotKeychainExists
&& mSavedList
.member(shortnameDotDLDbIdentifier
)) {
1296 Keychain
shortnameDotKC(keychain(shortnameDotDLDbIdentifier
));
1297 secdebug("KCLogin", "Attempting to unlock %s",
1298 (shortnameDotKC
) ? shortnameDotKC
->name() : "<NULL>");
1299 shortnameDotKC
->unlock(CssmData(const_cast<void *>(password
), passwordLength
));
1301 catch(const CssmError
&e
)
1303 // ignore; failure to unlock this keychain is not considered an error
1307 if (loginResult
!= noErr
) {
1308 MacOSError::throwMe(loginResult
);
1312 void StorageManager::logout()
1314 // nothing left to do here
1317 void StorageManager::changeLoginPassword(ConstStringPtr oldPassword
, ConstStringPtr newPassword
)
1319 StLock
<Mutex
>_(mMutex
);
1321 loginKeychain()->changePassphrase(oldPassword
, newPassword
);
1322 secdebug("KClogin", "Changed login keychain password successfully");
1326 void StorageManager::changeLoginPassword(UInt32 oldPasswordLength
, const void *oldPassword
, UInt32 newPasswordLength
, const void *newPassword
)
1328 StLock
<Mutex
>_(mMutex
);
1330 loginKeychain()->changePassphrase(oldPasswordLength
, oldPassword
, newPasswordLength
, newPassword
);
1331 secdebug("KClogin", "Changed login keychain password successfully");
1334 // Clear out the keychain search list and rename the existing login.keychain.
1336 void StorageManager::resetKeychain(Boolean resetSearchList
)
1338 StLock
<Mutex
>_(mMutex
);
1340 // Clear the keychain search list.
1343 if ( resetSearchList
)
1345 StorageManager::KeychainList keychainList
;
1346 setSearchList(keychainList
);
1348 // Get a reference to the existing login keychain...
1349 // If we don't have one, we throw (not requiring a rename).
1351 Keychain keychain
= loginKeychain();
1353 // Rename the existing login.keychain (i.e. put it aside).
1355 CFMutableStringRef newName
= NULL
;
1356 newName
= CFStringCreateMutable(NULL
, 0);
1357 CFStringRef currName
= NULL
;
1358 currName
= CFStringCreateWithCString(NULL
, keychain
->name(), kCFStringEncodingUTF8
);
1359 if ( newName
&& currName
)
1361 CFStringAppend(newName
, currName
);
1362 CFStringRef kcSuffix
= CFSTR(kKeychainSuffix
);
1363 if ( CFStringHasSuffix(newName
, kcSuffix
) ) // remove the .keychain extension
1365 CFRange suffixRange
= CFStringFind(newName
, kcSuffix
, 0);
1366 CFStringFindAndReplace(newName
, kcSuffix
, CFSTR(""), suffixRange
, 0);
1368 CFStringAppend(newName
, CFSTR(kKeychainRenamedSuffix
)); // add "_renamed_"
1371 renameUnique(keychain
, newName
);
1375 // we need to release 'newName' & 'currName'
1377 } // else, let the login call report a duplicate
1381 CFRelease(currName
);
1385 // We either don't have a login keychain, or there was a
1386 // failure to rename the existing one.
1390 #pragma mark ____ File Related ____
1392 Keychain
StorageManager::make(const char *pathName
)
1394 return make(pathName
, true);
1397 Keychain
StorageManager::make(const char *pathName
, bool add
)
1399 StLock
<Mutex
>_(mMutex
);
1401 string fullPathName
;
1402 if ( pathName
[0] == '/' )
1403 fullPathName
= pathName
;
1406 // Get Home directory from environment.
1409 case kSecPreferencesDomainUser
:
1411 const char *homeDir
= getenv("HOME");
1412 if (homeDir
== NULL
)
1414 // If $HOME is unset get the current user's home directory
1415 // from the passwd file.
1416 uid_t uid
= geteuid();
1417 if (!uid
) uid
= getuid();
1418 struct passwd
*pw
= getpwuid(uid
);
1420 MacOSError::throwMe(paramErr
);
1421 homeDir
= pw
->pw_dir
;
1423 fullPathName
= homeDir
;
1426 case kSecPreferencesDomainSystem
:
1430 assert(false); // invalid domain for this
1433 fullPathName
+= "/Library/Keychains/";
1434 fullPathName
+= pathName
;
1437 const CSSM_NET_ADDRESS
*DbLocation
= NULL
; // NULL for keychains
1438 const CSSM_VERSION
*version
= NULL
;
1439 uint32 subserviceId
= 0;
1440 CSSM_SERVICE_TYPE subserviceType
= CSSM_SERVICE_DL
| CSSM_SERVICE_CSP
;
1441 const CssmSubserviceUid
ssuid(gGuidAppleCSPDL
, version
,
1442 subserviceId
, subserviceType
);
1443 DLDbIdentifier
dLDbIdentifier(ssuid
, fullPathName
.c_str(), DbLocation
);
1444 return makeKeychain(dLDbIdentifier
, add
);
1447 Keychain
StorageManager::makeLoginAuthUI(const Item
*item
)
1449 StLock
<Mutex
>_(mMutex
);
1451 // Create a login/default keychain for the user using UI.
1452 // The user can cancel out of the operation, or create a new login keychain.
1453 // If auto-login is turned off, the user will be asked for their login password.
1455 OSStatus result
= noErr
;
1456 Keychain keychain
; // We return this keychain.
1458 // Set up the Auth ref to bring up UI.
1460 AuthorizationItem
*currItem
, *authEnvirItemArrayPtr
= NULL
;
1461 AuthorizationRef authRef
= NULL
;
1464 result
= AuthorizationCreate(NULL
, NULL
, kAuthorizationFlagDefaults
, &authRef
);
1466 MacOSError::throwMe(result
);
1468 AuthorizationEnvironment envir
;
1469 envir
.count
= 6; // up to 6 hints can be used.
1470 authEnvirItemArrayPtr
= (AuthorizationItem
*)malloc(sizeof(AuthorizationItem
) * envir
.count
);
1471 if ( !authEnvirItemArrayPtr
)
1472 MacOSError::throwMe(errAuthorizationInternal
);
1474 currItem
= envir
.items
= authEnvirItemArrayPtr
;
1477 // 1st Hint (optional): The keychain item's account attribute string.
1478 // When item is specified, we assume an 'add' operation is being attempted.
1481 SecKeychainAttribute attr
= { kSecAccountItemAttr
, 255, &buff
};
1486 (*item
)->getAttribute(attr
, &actLen
);
1490 actLen
= 0; // This item didn't have the account attribute, so don't display one in the UI.
1493 currItem
->name
= AGENT_HINT_ATTR_NAME
; // name str that identifies this hint as attr name
1494 if ( actLen
) // Fill in the hint if we have an account attr
1500 currItem
->valueLength
= strlen(buff
)+1;
1501 currItem
->value
= buff
;
1505 currItem
->valueLength
= 0;
1506 currItem
->value
= NULL
;
1508 currItem
->flags
= 0;
1511 // 2nd Hint (optional): The item's keychain full path.
1514 char* currDefaultName
= NULL
;
1517 currDefaultName
= (char*)defaultKeychain()->name(); // Use the name if we have it.
1518 currItem
->name
= AGENT_HINT_LOGIN_KC_NAME
; // Name str that identifies this hint as kc path
1519 currItem
->valueLength
= (currDefaultName
) ? strlen(currDefaultName
) : 0;
1520 currItem
->value
= (currDefaultName
) ? (void*)currDefaultName
: (void*)"";
1521 currItem
->flags
= 0;
1530 // 3rd Hint (required): check if curr default keychain is unavailable.
1531 // This is determined by the parent not existing.
1533 currItem
->name
= AGENT_HINT_LOGIN_KC_EXISTS_IN_KC_FOLDER
;
1534 Boolean loginUnavail
= false;
1537 Keychain defaultKC
= defaultKeychain();
1538 if ( !defaultKC
->exists() )
1539 loginUnavail
= true;
1541 catch(...) // login.keychain not present
1544 currItem
->valueLength
= sizeof(Boolean
);
1545 currItem
->value
= (void*)&loginUnavail
;
1546 currItem
->flags
= 0;
1549 // 4th Hint (required): userName
1552 currItem
->name
= AGENT_HINT_LOGIN_KC_USER_NAME
;
1553 char* uName
= getenv("USER");
1554 string userName
= uName
? uName
: "";
1555 if ( userName
.length() == 0 )
1557 uid_t uid
= geteuid();
1558 if (!uid
) uid
= getuid();
1559 struct passwd
*pw
= getpwuid(uid
); // fallback case...
1561 userName
= pw
->pw_name
;
1564 if ( userName
.length() == 0 ) // did we ultimately get one?
1565 MacOSError::throwMe(errAuthorizationInternal
);
1567 currItem
->value
= (void*)userName
.c_str();
1568 currItem
->valueLength
= userName
.length();
1569 currItem
->flags
= 0;
1572 // 5th Hint (required): flags if user has more than 1 keychain (used for a later warning when reset to default).
1575 currItem
->name
= AGENT_HINT_LOGIN_KC_USER_HAS_OTHER_KCS_STR
;
1576 Boolean moreThanOneKCExists
= false;
1578 // if item is NULL, then this is a user-initiated full reset
1579 if (item
&& mSavedList
.searchList().size() > 1)
1580 moreThanOneKCExists
= true;
1582 currItem
->value
= &moreThanOneKCExists
;
1583 currItem
->valueLength
= sizeof(Boolean
);
1584 currItem
->flags
= 0;
1587 // 6th Hint (required): If no item is involved, this is a user-initiated full reset.
1588 // We want to suppress the "do you want to reset to defaults?" panel in this case.
1591 currItem
->name
= AGENT_HINT_LOGIN_KC_SUPPRESS_RESET_PANEL
;
1592 Boolean suppressResetPanel
= (item
== NULL
) ? TRUE
: FALSE
;
1593 currItem
->valueLength
= sizeof(Boolean
);
1594 currItem
->value
= (void*)&suppressResetPanel
;
1595 currItem
->flags
= 0;
1598 // Set up the auth rights and make the auth call.
1600 AuthorizationItem authItem
= { LOGIN_KC_CREATION_RIGHT
, 0 , NULL
, 0 };
1601 AuthorizationRights rights
= { 1, &authItem
};
1602 AuthorizationFlags flags
= kAuthorizationFlagDefaults
| kAuthorizationFlagInteractionAllowed
| kAuthorizationFlagExtendRights
;
1603 result
= AuthorizationCopyRights(authRef
, &rights
, &envir
, flags
, NULL
);
1605 MacOSError::throwMe(result
);
1608 resetKeychain(true); // Clears the plist, moves aside existing login.keychain
1610 catch (...) // can throw if no existing login.keychain is found
1613 login(authRef
, userName
.length(), userName
.c_str()); // Create login.keychain
1614 keychain
= loginKeychain(); // Get newly-created login keychain
1615 defaultKeychain(keychain
); // Set it to be the default
1617 free(authEnvirItemArrayPtr
);
1618 AuthorizationFree(authRef
, kAuthorizationFlagDefaults
);
1623 // clean up allocations, then rethrow error
1624 if ( authEnvirItemArrayPtr
)
1625 free(authEnvirItemArrayPtr
);
1627 AuthorizationFree(authRef
, kAuthorizationFlagDefaults
);
1634 Keychain
StorageManager::defaultKeychainUI(Item
&item
)
1636 StLock
<Mutex
>_(mMutex
);
1638 Keychain returnedKeychain
;
1641 returnedKeychain
= defaultKeychain(); // If we have one, return it.
1642 if ( returnedKeychain
->exists() )
1643 return returnedKeychain
;
1645 catch(...) // We could have one, but it isn't available (i.e. on a un-mounted volume).
1648 if ( globals().getUserInteractionAllowed() )
1650 returnedKeychain
= makeLoginAuthUI(&item
); // If no Keychains is present, one will be created.
1651 if ( !returnedKeychain
)
1652 MacOSError::throwMe(errSecInvalidKeychain
); // Something went wrong...
1655 MacOSError::throwMe(errSecInteractionNotAllowed
); // If UI isn't allowed, return an error.
1657 return returnedKeychain
;
1661 StorageManager::addToDomainList(SecPreferencesDomain domain
,
1662 const char* dbName
, const CSSM_GUID
&guid
, uint32 subServiceType
)
1664 StLock
<Mutex
>_(mMutex
);
1666 if (domain
== kSecPreferencesDomainDynamic
)
1667 MacOSError::throwMe(errSecInvalidPrefsDomain
);
1669 // make the identifier
1670 CSSM_VERSION version
= {0, 0};
1671 DLDbIdentifier id
= DLDbListCFPref::makeDLDbIdentifier (guid
, version
, 0,
1672 subServiceType
, dbName
, NULL
);
1674 if (domain
== mDomain
)
1676 // manipulate the user's list
1678 mSavedList
.revert(true);
1683 KCEventNotifier::PostKeychainEvent(kSecKeychainListChangedEvent
);
1687 // manipulate the other list
1688 DLDbListCFPref(domain
).add(id
);
1693 StorageManager::isInDomainList(SecPreferencesDomain domain
,
1694 const char* dbName
, const CSSM_GUID
&guid
, uint32 subServiceType
)
1696 StLock
<Mutex
>_(mMutex
);
1698 if (domain
== kSecPreferencesDomainDynamic
)
1699 MacOSError::throwMe(errSecInvalidPrefsDomain
);
1701 CSSM_VERSION version
= {0, 0};
1702 DLDbIdentifier id
= DLDbListCFPref::makeDLDbIdentifier (guid
, version
, 0,
1703 subServiceType
, dbName
, NULL
);
1705 // determine the list to search
1707 if (domain
== mDomain
)
1709 result
= mSavedList
.member(id
);
1713 result
= DLDbListCFPref(domain
).member(id
);
1719 MacOSError::throwMe(errSecNoSuchKeychain
);
1724 StorageManager::removeFromDomainList(SecPreferencesDomain domain
,
1725 const char* dbName
, const CSSM_GUID
&guid
, uint32 subServiceType
)
1727 StLock
<Mutex
>_(mMutex
);
1729 if (domain
== kSecPreferencesDomainDynamic
)
1730 MacOSError::throwMe(errSecInvalidPrefsDomain
);
1732 // make the identifier
1733 CSSM_VERSION version
= {0, 0};
1734 DLDbIdentifier id
= DLDbListCFPref::makeDLDbIdentifier (guid
, version
, 0,
1735 subServiceType
, dbName
, NULL
);
1737 if (domain
== mDomain
)
1739 // manipulate the user's list
1741 mSavedList
.revert(true);
1742 mSavedList
.remove(id
);
1746 KCEventNotifier::PostKeychainEvent(kSecKeychainListChangedEvent
);
1750 // manipulate the other list
1751 DLDbListCFPref(domain
).remove(id
);
1756 StorageManager::keychainOwnerPermissionsValidForDomain(const char* path
, SecPreferencesDomain domain
)
1760 const char* sysPrefDir
= "/Library/Preferences";
1761 const char* errMsg
= "Will not set default";
1762 char* mustOwnDir
= NULL
;
1763 struct passwd
* pw
= NULL
;
1766 uid_t uid
= geteuid();
1767 if (!uid
) uid
= getuid();
1769 // our (e)uid must own the appropriate preferences or home directory
1770 // for the specified preference domain whose default we will be modifying
1772 case kSecPreferencesDomainUser
:
1773 mustOwnDir
= getenv("HOME");
1774 if (mustOwnDir
== NULL
) {
1776 if (!pw
) return false;
1777 mustOwnDir
= pw
->pw_dir
;
1780 case kSecPreferencesDomainSystem
:
1781 mustOwnDir
= (char*)sysPrefDir
;
1783 case kSecPreferencesDomainCommon
:
1784 mustOwnDir
= (char*)sysPrefDir
;
1790 if (mustOwnDir
!= NULL
) {
1792 if ( (stat(mustOwnDir
, &dsb
) != 0) || (dsb
.st_uid
!= uid
) ) {
1793 fprintf(stderr
, "%s: UID=%d does not own directory %s\n", errMsg
, (int)uid
, mustOwnDir
);
1794 mustOwnDir
= NULL
; // will return below after calling endpwent()
1801 if (mustOwnDir
== NULL
)
1804 // check that file actually exists
1805 if (stat(path
, &sb
) != 0) {
1806 fprintf(stderr
, "%s: file %s does not exist\n", errMsg
, path
);
1811 if (sb
.st_flags
& (SF_IMMUTABLE
| UF_IMMUTABLE
)) {
1812 fprintf(stderr
, "%s: file %s is immutable\n", errMsg
, path
);
1817 if (sb
.st_uid
!= uid
) {
1818 fprintf(stderr
, "%s: file %s is owned by UID=%d, but we have UID=%d\n",
1819 errMsg
, path
, (int)sb
.st_uid
, (int)uid
);
1825 perms
|= 0600; // must have owner read/write permission set
1826 if (sb
.st_mode
!= perms
) {
1827 fprintf(stderr
, "%s: file %s does not have the expected permissions\n", errMsg
, path
);
1831 // user owns file and can read/write it