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_utilities/debugging.h>
44 #include <security_keychain/SecCFTypes.h>
45 #include <securityd_client/ssclient.h>
46 #include <Security/AuthorizationTags.h>
47 #include <Security/AuthorizationTagsPriv.h>
48 #include <Security/SecTask.h>
49 #include <security_keychain/SecCFTypes.h>
50 #include "TrustSettingsSchema.h"
51 #include <security_cdsa_client/wrapkey.h>
52 #include <securityd_client/ssblob.h>
53 #include <SecBasePriv.h>
54 #include "TokenLogin.h"
56 //%%% add this to AuthorizationTagsPriv.h later
57 #ifndef AGENT_HINT_LOGIN_KC_SUPPRESS_RESET_PANEL
58 #define AGENT_HINT_LOGIN_KC_SUPPRESS_RESET_PANEL "loginKCCreate:suppressResetPanel"
65 using namespace CssmClient
;
66 using namespace KeychainCore
;
68 #define kLoginKeychainPathPrefix "~/Library/Keychains/"
69 #define kUserLoginKeychainPath "~/Library/Keychains/login.keychain"
70 #define kEmptyKeychainSizeInBytes 20460
72 //-----------------------------------------------------------------------------------
74 static SecPreferencesDomain
defaultPreferenceDomain()
76 SessionAttributeBits sessionAttrs
;
78 secnotice("servermode", "StorageManager initialized in server mode");
79 sessionAttrs
= sessionIsRoot
;
81 MacOSError::check(SessionGetInfo(callerSecuritySession
, NULL
, &sessionAttrs
));
84 // If this is the root session, use system preferences.
85 // (In SecurityServer debug mode, you'll get a (fake) root session
86 // that has graphics access. Ignore that to help testing.)
87 if ((sessionAttrs
& sessionIsRoot
)
88 IFDEBUG( && !(sessionAttrs
& sessionHasGraphicAccess
))) {
89 secnotice("storagemgr", "using system preferences");
90 return kSecPreferencesDomainSystem
;
93 // otherwise, use normal (user) preferences
94 return kSecPreferencesDomainUser
;
97 static bool isAppSandboxed()
100 SecTaskRef task
= SecTaskCreateFromSelf(NULL
);
102 CFTypeRef appSandboxValue
= SecTaskCopyValueForEntitlement(task
,
103 CFSTR("com.apple.security.app-sandbox"), NULL
);
104 if(appSandboxValue
!= NULL
) {
106 CFRelease(appSandboxValue
);
113 static bool shouldAddToSearchList(const DLDbIdentifier
&dLDbIdentifier
)
115 // Creation of a private keychain should not modify the search list: rdar://13529331
116 // However, we want to ensure the login and System keychains are in
117 // the search list if that is not the case when they are created.
118 // Note that App Sandbox apps may not modify the list in either case.
120 bool loginOrSystemKeychain
= false;
121 const char *dbname
= dLDbIdentifier
.dbName();
123 if ((!strcmp(dbname
, "/Library/Keychains/System.keychain")) ||
124 (strstr(dbname
, "/login.keychain")) ) {
125 loginOrSystemKeychain
= true;
128 return (loginOrSystemKeychain
&& !isAppSandboxed());
132 StorageManager::StorageManager() :
133 mSavedList(defaultPreferenceDomain()),
134 mCommonList(kSecPreferencesDomainCommon
),
135 mDomain(kSecPreferencesDomainUser
),
136 mMutex(Mutex::recursive
)
142 StorageManager::getStorageManagerMutex()
144 return &mKeychainMapMutex
;
149 StorageManager::keychain(const DLDbIdentifier
&dLDbIdentifier
)
151 StLock
<Mutex
>_(mKeychainMapMutex
);
156 DLDbIdentifier dldbi
= mungeDLDbIdentifier(dLDbIdentifier
, false);
158 KeychainMap::iterator it
= mKeychainMap
.find(dldbi
);
159 if (it
!= mKeychainMap
.end())
164 // If we have a keychain object for the un/demunged keychain, return that.
165 // We might be in the middle of an upgrade...
166 DLDbIdentifier demunge_dldbi
= demungeDLDbIdentifier(dLDbIdentifier
);
167 it
= mKeychainMap
.find(demunge_dldbi
);
168 if (it
!= mKeychainMap
.end()) {
169 secnotice("integrity", "returning unmunged keychain ref");
174 secnotice("servermode", "keychain reference in server mode");
178 // The keychain is not in our cache. Create it.
179 Db
db(makeDb(dldbi
));
181 Keychain
keychain(db
);
182 // Add the keychain to the cache.
183 registerKeychain(keychain
);
188 // Note: this must be a munged DLDbidentifier.
190 StorageManager::makeDb(DLDbIdentifier dLDbIdentifier
) {
191 Module
module(dLDbIdentifier
.ssuid().guid());
194 if (dLDbIdentifier
.ssuid().subserviceType() & CSSM_SERVICE_CSP
)
195 dl
= SSCSPDL(module);
199 dl
->subserviceId(dLDbIdentifier
.ssuid().subserviceId());
200 dl
->version(dLDbIdentifier
.ssuid().version());
202 CssmClient::Db
db(dl
, dLDbIdentifier
.dbName());
207 // StorageManager is responsible for silently switching to newer-style keychains.
208 // If the keychain requested is in ~/Library/Keychains/, and there is a
209 // newer keychain available (with extension ".keychain-db"), open that one
210 // instead of the one requested.
212 // Because of backwards compatibility reasons, we can't update the plist
213 // files on disk to point to the upgraded keychains. We will be asked to
214 // load "/Users/account/Library/Keychains/login.keychain", hence this
215 // modification to 'login.keychain-db'.
217 StorageManager::mungeDLDbIdentifier(const DLDbIdentifier
& dLDbIdentifier
, bool isReset
) {
218 if(!dLDbIdentifier
.dbName()) {
219 // If this DLDbIdentifier doesn't have a filename, don't munge it
220 return dLDbIdentifier
;
223 string path
= dLDbIdentifier
.dbName();
225 bool shouldCreateProtected
= globals().integrityProtection();
227 // If we don't have a DLDbIdentifier, we can't return one
228 if(dLDbIdentifier
.mImpl
== NULL
) {
229 return DLDbIdentifier();
232 // Ensure we're in ~/Library/Keychains
233 if(pathInHomeLibraryKeychains(path
)) {
234 string pathdb
= makeKeychainDbFilename(path
);
238 stat_result
= ::stat(path
.c_str(), &st
);
239 bool path_exists
= (stat_result
== 0);
241 stat_result
= ::stat(pathdb
.c_str(), &st
);
242 bool pathdb_exists
= (stat_result
== 0);
244 // If protections are off, don't change the requested filename.
245 // If protictions are on and the -db file exists, always use it.
247 // If we're resetting, and we're creating a new-style keychain, use the -db path.
248 // If we're resetting, and we're creating an old-style keychain, use the original path.
250 // Protection pathdb_exists path_exists resetting Result
251 // DISABLED X X X original
254 // ENABLED 0 1 0 original
257 bool switchPaths
= shouldCreateProtected
&& (pathdb_exists
|| (!pathdb_exists
&& !path_exists
) || isReset
);
260 secnotice("integrity", "switching to keychain-db: %s from %s (%d %d %d %d)", pathdb
.c_str(), path
.c_str(), isReset
, shouldCreateProtected
, path_exists
, pathdb_exists
);
263 secnotice("integrity", "not switching: %s from %s (%d %d %d %d)", pathdb
.c_str(), path
.c_str(), isReset
, shouldCreateProtected
, path_exists
, pathdb_exists
);
266 secnotice("integrity", "not switching as we're not in ~/Library/Keychains/: %s (%d)", path
.c_str(), isReset
);
269 DLDbIdentifier
id(dLDbIdentifier
.ssuid(), path
.c_str(), dLDbIdentifier
.dbLocation());
274 StorageManager::demungeDLDbIdentifier(const DLDbIdentifier
& dLDbIdentifier
) {
275 if(dLDbIdentifier
.dbName() == NULL
) {
276 return dLDbIdentifier
;
279 string path
= dLDbIdentifier
.dbName();
280 string dbSuffix
= "-db";
281 bool endsWithKeychainDb
= (path
.size() > dbSuffix
.size() && (0 == path
.compare(path
.size() - dbSuffix
.size(), dbSuffix
.size(), dbSuffix
)));
283 // Ensure we're in ~/Library/Keychains, and that the path ends in "-db"
284 if(pathInHomeLibraryKeychains(path
) && endsWithKeychainDb
) {
285 // remove "-db" from the end.
286 path
.erase(path
.end() - 3, path
.end());
289 DLDbIdentifier
id(dLDbIdentifier
.ssuid(), path
.c_str(), dLDbIdentifier
.dbLocation());
294 StorageManager::makeKeychainDbFilename(const string
& filename
) {
295 string keychainDbSuffix
= "-db";
296 bool endsWithKeychainDb
= (filename
.size() > keychainDbSuffix
.size() && (0 == filename
.compare(filename
.size() - keychainDbSuffix
.size(), keychainDbSuffix
.size(), keychainDbSuffix
)));
298 if(endsWithKeychainDb
) {
301 return filename
+ keychainDbSuffix
;
306 StorageManager::pathInHomeLibraryKeychains(const string
& path
) {
307 return SecurityServer::CommonBlob::pathInHomeLibraryKeychains(path
);
311 StorageManager::reloadKeychain(Keychain keychain
) {
312 StLock
<Mutex
>_(mKeychainMapMutex
);
314 DLDbIdentifier dLDbIdentifier
= keychain
->database()->dlDbIdentifier();
316 keychain
->changeDatabase(makeDb(mungeDLDbIdentifier(dLDbIdentifier
, false)));
318 // This keychain might have a different dldbidentifier now, depending on what
319 // other processes have been doing to the keychain files. Let's re-register it, just
321 registerKeychain(keychain
);
325 StorageManager::removeKeychain(const DLDbIdentifier
&dLDbIdentifier
,
326 KeychainImpl
*keychainImpl
)
328 StLock
<Mutex
>_(mKeychainMapMutex
);
330 // Don't trust this dldbidentifier. Just look for the keychain and delete it.
331 forceRemoveFromCache(keychainImpl
);
335 StorageManager::didRemoveKeychain(const DLDbIdentifier
&dLDbIdentifier
)
337 // Lock the recursive mutex
339 StLock
<Mutex
>_(mKeychainMapMutex
);
341 KeychainMap::iterator it
= mKeychainMap
.find(dLDbIdentifier
);
342 if (it
!= mKeychainMap
.end())
344 it
->second
->inCache(false);
345 mKeychainMap
.erase(it
);
349 // If the client does not keep references to keychains, they are destroyed on
350 // every API exit, and recreated on every API entrance.
352 // To improve performance, we'll cache keychains for some short period of time.
353 // We'll do this by CFRetaining the keychain object, and setting a timer to
354 // CFRelease it when time's up. This way, the client can still recover all its
355 // memory if it doesn't want the keychains around, but repeated API calls will
356 // be significantly faster.
359 StorageManager::tickleKeychain(KeychainImpl
*keychainImpl
) {
360 static dispatch_once_t onceToken
= 0;
361 static dispatch_queue_t release_queue
= NULL
;
362 dispatch_once(&onceToken
, ^{
363 release_queue
= dispatch_queue_create("com.apple.security.keychain-cache-queue", DISPATCH_QUEUE_SERIAL
);
366 __block KeychainImpl
* kcImpl
= keychainImpl
;
372 // We really only want to cache CSPDL file-based keychains
373 if(kcImpl
->dlDbIdentifier().ssuid().guid() != gGuidAppleCSPDL
) {
377 // Make a one-shot timer to release the keychain
378 uint32_t seconds
= 1;
380 const string path
= kcImpl
->name();
381 bool isSystemKeychain
= (0 == path
.compare("/Library/Keychains/System.keychain"));
382 if(pathInHomeLibraryKeychains(path
) || isSystemKeychain
) {
383 // These keychains are important and likely aren't on removable media.
384 // Cache them longer.
388 __block CFTypeRef kcHandle
= kcImpl
->handle(); // calls retain; this keychain object will stay around until our dispatch block fires.
390 dispatch_async(release_queue
, ^() {
391 if(kcImpl
->mCacheTimer
) {
392 // Update the cache timer to be seconds from now
393 dispatch_source_set_timer(kcImpl
->mCacheTimer
, dispatch_time(DISPATCH_TIME_NOW
, seconds
* NSEC_PER_SEC
), DISPATCH_TIME_FOREVER
, NSEC_PER_SEC
/2);
395 // We've added an extra retain to this keychain right before invoking this block. Release it.
399 // No cache timer; make one.
400 kcImpl
->mCacheTimer
= dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER
, 0, 0, release_queue
);
401 dispatch_source_set_timer(kcImpl
->mCacheTimer
, dispatch_time(DISPATCH_TIME_NOW
, seconds
* NSEC_PER_SEC
), DISPATCH_TIME_FOREVER
, NSEC_PER_SEC
/2);
403 dispatch_source_set_event_handler(kcImpl
->mCacheTimer
, ^{
404 dispatch_source_cancel(kcImpl
->mCacheTimer
);
405 dispatch_release(kcImpl
->mCacheTimer
);
406 kcImpl
->mCacheTimer
= NULL
;
410 dispatch_resume(kcImpl
->mCacheTimer
);
415 // Create keychain if it doesn't exist, and optionally add it to the search list.
417 StorageManager::makeKeychain(const DLDbIdentifier
&dLDbIdentifier
, bool add
, bool isReset
)
419 StLock
<Mutex
>_(mKeychainMapMutex
);
421 Keychain theKeychain
= keychain(mungeDLDbIdentifier(dLDbIdentifier
, isReset
));
423 bool updateList
= (add
&& shouldAddToSearchList(dLDbIdentifier
));
427 mSavedList
.revert(false);
428 DLDbList searchList
= mSavedList
.searchList();
429 if (find(searchList
.begin(), searchList
.end(), demungeDLDbIdentifier(dLDbIdentifier
)) != searchList
.end())
430 return theKeychain
; // theKeychain is already in the searchList.
432 mCommonList
.revert(false);
433 searchList
= mCommonList
.searchList();
434 if (find(searchList
.begin(), searchList
.end(), demungeDLDbIdentifier(dLDbIdentifier
)) != searchList
.end())
435 return theKeychain
; // theKeychain is already in the commonList don't add it to the searchList.
437 // If theKeychain doesn't exist don't bother adding it to the search list yet.
438 if (!theKeychain
->exists())
441 // theKeychain exists and is not in our search list, so add it to the
443 mSavedList
.revert(true);
444 mSavedList
.add(demungeDLDbIdentifier(dLDbIdentifier
));
451 // Make sure we are not holding mStorageManagerLock anymore when we
453 KCEventNotifier::PostKeychainEvent(kSecKeychainListChangedEvent
);
459 // Be notified a Keychain just got created.
461 StorageManager::created(const Keychain
&keychain
)
463 StLock
<Mutex
>_(mKeychainMapMutex
);
465 DLDbIdentifier dLDbIdentifier
= keychain
->dlDbIdentifier();
466 bool defaultChanged
= false;
467 bool updateList
= shouldAddToSearchList(dLDbIdentifier
);
471 mSavedList
.revert(true);
472 // If we don't have a default Keychain yet. Make the newly created
473 // keychain the default.
474 if (!mSavedList
.defaultDLDbIdentifier())
476 mSavedList
.defaultDLDbIdentifier(demungeDLDbIdentifier(dLDbIdentifier
));
477 defaultChanged
= true;
480 // Add the keychain to the search list prefs.
481 mSavedList
.add(demungeDLDbIdentifier(dLDbIdentifier
));
484 // Make sure we are not holding mLock when we post these events.
485 KCEventNotifier::PostKeychainEvent(kSecKeychainListChangedEvent
);
490 KCEventNotifier::PostKeychainEvent(kSecDefaultChangedEvent
, dLDbIdentifier
);
495 StorageManager::createCursor(SecItemClass itemClass
,
496 const SecKeychainAttributeList
*attrList
)
498 StLock
<Mutex
>_(mMutex
);
500 KeychainList searchList
;
501 getSearchList(searchList
);
502 return KCCursor(searchList
, itemClass
, attrList
);
506 StorageManager::createCursor(const SecKeychainAttributeList
*attrList
)
508 StLock
<Mutex
>_(mMutex
);
510 KeychainList searchList
;
511 getSearchList(searchList
);
512 return KCCursor(searchList
, attrList
);
516 StorageManager::lockAll()
518 StLock
<Mutex
>_(mMutex
);
520 SecurityServer::ClientSession
ss(Allocator::standard(), Allocator::standard());
525 StorageManager::defaultKeychain()
527 StLock
<Mutex
>_(mMutex
);
529 Keychain theKeychain
;
533 mSavedList
.revert(false);
534 DLDbIdentifier
defaultDLDbIdentifier(mSavedList
.defaultDLDbIdentifier());
535 if (defaultDLDbIdentifier
)
537 theKeychain
= keychain(defaultDLDbIdentifier
);
538 ref
= theKeychain
->handle(false);
542 if (theKeychain
/* && theKeychain->exists() */)
545 MacOSError::throwMe(errSecNoDefaultKeychain
);
549 StorageManager::defaultKeychain(const Keychain
&keychain
)
551 StLock
<Mutex
>_(mMutex
);
553 // Only set a keychain as the default if we own it and can read/write it,
554 // and our uid allows modifying the directory for that preference domain.
555 if (!keychainOwnerPermissionsValidForDomain(keychain
->name(), mDomain
))
556 MacOSError::throwMe(errSecWrPerm
);
558 DLDbIdentifier oldDefaultId
;
559 DLDbIdentifier
newDefaultId(keychain
->dlDbIdentifier());
561 oldDefaultId
= mSavedList
.defaultDLDbIdentifier();
562 mSavedList
.revert(true);
563 mSavedList
.defaultDLDbIdentifier(demungeDLDbIdentifier(newDefaultId
));
567 if (!(oldDefaultId
== newDefaultId
))
569 // Make sure we are not holding mLock when we post this event.
570 KCEventNotifier::PostKeychainEvent(kSecDefaultChangedEvent
, newDefaultId
);
575 StorageManager::defaultKeychain(SecPreferencesDomain domain
)
577 StLock
<Mutex
>_(mMutex
);
579 if (domain
== kSecPreferencesDomainDynamic
)
580 MacOSError::throwMe(errSecInvalidPrefsDomain
);
582 if (domain
== mDomain
)
583 return defaultKeychain();
586 DLDbIdentifier
defaultDLDbIdentifier(DLDbListCFPref(domain
).defaultDLDbIdentifier());
587 if (defaultDLDbIdentifier
)
588 return keychain(defaultDLDbIdentifier
);
590 MacOSError::throwMe(errSecNoDefaultKeychain
);
595 StorageManager::defaultKeychain(SecPreferencesDomain domain
, const Keychain
&keychain
)
597 StLock
<Mutex
>_(mMutex
);
599 if (domain
== kSecPreferencesDomainDynamic
)
600 MacOSError::throwMe(errSecInvalidPrefsDomain
);
602 if (domain
== mDomain
)
603 defaultKeychain(keychain
);
605 DLDbListCFPref(domain
).defaultDLDbIdentifier(keychain
->dlDbIdentifier());
609 StorageManager::loginKeychain()
611 StLock
<Mutex
>_(mMutex
);
613 Keychain theKeychain
;
615 mSavedList
.revert(false);
616 DLDbIdentifier
loginDLDbIdentifier(mSavedList
.loginDLDbIdentifier());
617 if (loginDLDbIdentifier
)
619 theKeychain
= keychain(loginDLDbIdentifier
);
623 if (theKeychain
&& theKeychain
->exists())
626 MacOSError::throwMe(errSecNoSuchKeychain
);
630 StorageManager::loginKeychain(Keychain keychain
)
632 StLock
<Mutex
>_(mMutex
);
634 mSavedList
.revert(true);
635 mSavedList
.loginDLDbIdentifier(demungeDLDbIdentifier(keychain
->dlDbIdentifier()));
640 StorageManager::size()
642 StLock
<Mutex
>_(mMutex
);
644 mSavedList
.revert(false);
645 mCommonList
.revert(false);
646 return mSavedList
.searchList().size() + mCommonList
.searchList().size();
650 StorageManager::at(unsigned int ix
)
652 StLock
<Mutex
>_(mMutex
);
654 mSavedList
.revert(false);
655 DLDbList dLDbList
= mSavedList
.searchList();
656 if (ix
< dLDbList
.size())
658 return keychain(dLDbList
[ix
]);
662 ix
-= dLDbList
.size();
663 mCommonList
.revert(false);
664 DLDbList commonList
= mCommonList
.searchList();
665 if (ix
>= commonList
.size())
666 MacOSError::throwMe(errSecInvalidKeychain
);
668 return keychain(commonList
[ix
]);
673 StorageManager::operator[](unsigned int ix
)
675 StLock
<Mutex
>_(mMutex
);
680 void StorageManager::rename(Keychain keychain
, const char* newName
)
683 StLock
<Mutex
>_(mKeychainMapMutex
);
685 bool changedDefault
= false;
686 DLDbIdentifier newDLDbIdentifier
;
688 mSavedList
.revert(true);
689 DLDbIdentifier defaultId
= mSavedList
.defaultDLDbIdentifier();
691 // Find the keychain object for the given ref
692 DLDbIdentifier dLDbIdentifier
= keychain
->dlDbIdentifier();
694 if(!keychain
->database()->isLocked()) {
695 // Bring our unlock state with us
696 DLDbIdentifier
dldbi(dLDbIdentifier
.ssuid(), newName
, dLDbIdentifier
.dbLocation());
697 keychain
->database()->transferTo(dldbi
);
699 keychain
->database()->rename(newName
);
702 if (demungeDLDbIdentifier(dLDbIdentifier
) == defaultId
)
705 newDLDbIdentifier
= keychain
->dlDbIdentifier();
706 // Rename the keychain in the search list.
707 mSavedList
.rename(demungeDLDbIdentifier(dLDbIdentifier
), demungeDLDbIdentifier(newDLDbIdentifier
));
709 // If this was the default keychain change it accordingly
711 mSavedList
.defaultDLDbIdentifier(demungeDLDbIdentifier(newDLDbIdentifier
));
715 // If the keychain wasn't in the cache, don't touch the cache.
716 // Otherwise, update the cache to use its current identifier.
717 if(keychain
->inCache()) {
718 registerKeychain(keychain
);
722 KCEventNotifier::PostKeychainEvent(kSecKeychainListChangedEvent
);
725 KCEventNotifier::PostKeychainEvent(kSecDefaultChangedEvent
,
729 void StorageManager::registerKeychain(Keychain
& kc
) {
730 registerKeychainImpl(kc
.get());
733 void StorageManager::registerKeychainImpl(KeychainImpl
* kcimpl
) {
739 StLock
<Mutex
> _(mKeychainMapMutex
);
741 // First, iterate through the cache to see if this keychain is there. If so, remove it.
742 forceRemoveFromCache(kcimpl
);
744 // If we renamed this keychain on top of an existing one, let's drop the old one from the cache.
745 KeychainMap::iterator it
= mKeychainMap
.find(kcimpl
->dlDbIdentifier());
746 if (it
!= mKeychainMap
.end())
748 Keychain
oldKeychain(it
->second
);
749 oldKeychain
->inCache(false);
750 // @@@ Ideally we should invalidate or fault this keychain object.
753 mKeychainMap
.insert(KeychainMap::value_type(kcimpl
->dlDbIdentifier(), kcimpl
));
754 kcimpl
->inCache(true);
755 } // drop mKeychainMapMutex
758 void StorageManager::forceRemoveFromCache(KeychainImpl
* inKeychainImpl
) {
760 // Wrap all this in a try-block and ignore all errors - we're trying to clean up these maps
762 StLock
<Mutex
> _(mKeychainMapMutex
);
763 for(KeychainMap::iterator it
= mKeychainMap
.begin(); it
!= mKeychainMap
.end(); ) {
764 if(it
->second
== inKeychainImpl
) {
765 // Increment the iterator, but use its pre-increment value for the erase
766 it
->second
->inCache(false);
767 mKeychainMap
.erase(it
++);
772 } // drop mKeychainMapMutex
773 } catch(UnixError ue
) {
774 secnotice("storagemgr", "caught UnixError: %d %s", ue
.unixError(), ue
.what());
775 } catch (CssmError cssme
) {
776 const char* errStr
= cssmErrorString(cssme
.error
);
777 secnotice("storagemgr", "caught CssmError: %d %s", (int) cssme
.error
, errStr
);
778 } catch (MacOSError mose
) {
779 secnotice("storagemgr", "MacOSError: %d", (int)mose
.osStatus());
781 secnotice("storagemgr", "Unknown error");
785 void StorageManager::renameUnique(Keychain keychain
, CFStringRef newName
, bool appendDbSuffix
)
787 StLock
<Mutex
>_(mMutex
);
789 bool doneCreating
= false;
793 char newNameCString
[MAXPATHLEN
];
794 if ( CFStringGetCString(newName
, newNameCString
, MAXPATHLEN
, kCFStringEncodingUTF8
) ) // make sure it fits in MAXPATHLEN, etc.
796 // Construct the new name...
798 CFMutableStringRef newNameCFStr
= NULL
;
799 newNameCFStr
= CFStringCreateMutable(NULL
, MAXPATHLEN
);
802 CFStringAppendFormat(newNameCFStr
, NULL
, CFSTR("%s%d"), newNameCString
, index
);
804 CFStringAppend(newNameCFStr
, CFSTR(kKeychainDbSuffix
));
806 CFStringAppend(newNameCFStr
, CFSTR(kKeychainSuffix
)); // add .keychain
808 char toUseBuff2
[MAXPATHLEN
];
809 if ( CFStringGetCString(newNameCFStr
, toUseBuff2
, MAXPATHLEN
, kCFStringEncodingUTF8
) ) // make sure it fits in MAXPATHLEN, etc.
812 if ( lstat(toUseBuff2
, &filebuf
) )
814 rename(keychain
, toUseBuff2
);
816 kcList
.push_back(keychain
);
817 remove(kcList
, false);
824 doneCreating
= true; // failure to get c string.
825 CFRelease(newNameCFStr
);
828 doneCreating
= false; // failure to create mutable string.
831 doneCreating
= false; // failure to get the string (i.e. > MAXPATHLEN?)
833 while (!doneCreating
&& index
!= INT_MAX
);
836 #define KEYCHAIN_SYNC_KEY CFSTR("KeychainSyncList")
837 #define KEYCHAIN_SYNC_DOMAIN CFSTR("com.apple.keychainsync")
839 static CFStringRef
MakeExpandedPath (const char* path
)
841 std::string name
= DLDbListCFPref::ExpandTildesInPath (std::string (path
));
842 CFStringRef expanded
= CFStringCreateWithCString (NULL
, name
.c_str (), 0);
846 void StorageManager::removeKeychainFromSyncList (const DLDbIdentifier
&id
)
848 StLock
<Mutex
>_(mMutex
);
850 // make a CFString of our identifier
851 const char* idname
= id
.dbName ();
857 CFRef
<CFStringRef
> idString
= MakeExpandedPath (idname
);
859 // check and see if this keychain is in the keychain syncing list
861 (CFArrayRef
) CFPreferencesCopyValue (KEYCHAIN_SYNC_KEY
,
862 KEYCHAIN_SYNC_DOMAIN
,
863 kCFPreferencesCurrentUser
,
864 kCFPreferencesAnyHost
);
870 // make a mutable copy of the dictionary
871 CFRef
<CFMutableArrayRef
> mtValue
= CFArrayCreateMutableCopy (NULL
, 0, value
);
874 // walk the array, looking for the value
876 CFIndex limit
= CFArrayGetCount (mtValue
.get());
879 for (i
= 0; i
< limit
; ++i
)
881 CFDictionaryRef idx
= (CFDictionaryRef
) CFArrayGetValueAtIndex (mtValue
.get(), i
);
882 CFStringRef v
= (CFStringRef
) CFDictionaryGetValue (idx
, CFSTR("DbName"));
885 return; // something is really wrong if this is taken
888 char* stringBuffer
= NULL
;
889 const char* pathString
= CFStringGetCStringPtr(v
, 0);
892 CFIndex maxLen
= CFStringGetMaximumSizeForEncoding(CFStringGetLength(v
), kCFStringEncodingUTF8
) + 1;
893 stringBuffer
= (char*) malloc(maxLen
);
894 CFStringGetCString(v
, stringBuffer
, maxLen
, kCFStringEncodingUTF8
);
895 pathString
= stringBuffer
;
898 CFStringRef vExpanded
= MakeExpandedPath(pathString
);
899 CFComparisonResult result
= CFStringCompare (vExpanded
, idString
.get(), 0);
900 if (stringBuffer
!= NULL
)
905 CFRelease (vExpanded
);
909 CFArrayRemoveValueAtIndex (mtValue
.get(), i
);
918 CFShow (mtValue
.get());
921 CFPreferencesSetValue (KEYCHAIN_SYNC_KEY
,
923 KEYCHAIN_SYNC_DOMAIN
,
924 kCFPreferencesCurrentUser
,
925 kCFPreferencesAnyHost
);
926 CFPreferencesSynchronize (KEYCHAIN_SYNC_DOMAIN
, kCFPreferencesCurrentUser
, kCFPreferencesAnyHost
);
930 void StorageManager::remove(const KeychainList
&kcsToRemove
, bool deleteDb
)
932 StLock
<Mutex
>_(mMutex
);
934 bool unsetDefault
= false;
935 bool updateList
= (!isAppSandboxed());
939 mSavedList
.revert(true);
940 DLDbIdentifier defaultId
= mSavedList
.defaultDLDbIdentifier();
941 for (KeychainList::const_iterator ix
= kcsToRemove
.begin();
942 ix
!= kcsToRemove
.end(); ++ix
)
944 // Find the keychain object for the given ref
945 Keychain theKeychain
= *ix
;
946 DLDbIdentifier dLDbIdentifier
= theKeychain
->dlDbIdentifier();
948 // Remove it from the saved list
949 mSavedList
.remove(demungeDLDbIdentifier(dLDbIdentifier
));
950 if (demungeDLDbIdentifier(dLDbIdentifier
) == defaultId
) {
956 removeKeychainFromSyncList (dLDbIdentifier
);
958 // Now remove it from the cache
959 removeKeychain(dLDbIdentifier
, theKeychain
.get());
964 mSavedList
.defaultDLDbIdentifier(DLDbIdentifier());
971 // Delete the actual databases without holding any locks.
972 for (KeychainList::const_iterator ix
= kcsToRemove
.begin();
973 ix
!= kcsToRemove
.end(); ++ix
)
975 (*ix
)->database()->deleteDb();
980 // Make sure we are not holding mLock when we post these events.
981 KCEventNotifier::PostKeychainEvent(kSecKeychainListChangedEvent
);
985 KCEventNotifier::PostKeychainEvent(kSecDefaultChangedEvent
);
989 StorageManager::getSearchList(KeychainList
&keychainList
)
991 // hold the global lock since we make keychain objects in this function
993 // to do: each of the items in this list must be retained, otherwise mayhem will occur
994 StLock
<Mutex
>_(mMutex
);
997 keychainList
.clear();
1001 mSavedList
.revert(false);
1002 mCommonList
.revert(false);
1004 // Merge mSavedList, mDynamicList and mCommonList
1005 DLDbList dLDbList
= mSavedList
.searchList();
1006 DLDbList dynamicList
= mDynamicList
.searchList();
1007 DLDbList commonList
= mCommonList
.searchList();
1008 KeychainList result
;
1009 result
.reserve(dLDbList
.size() + dynamicList
.size() + commonList
.size());
1012 for (DLDbList::const_iterator it
= dynamicList
.begin();
1013 it
!= dynamicList
.end(); ++it
)
1015 Keychain k
= keychain(*it
);
1016 result
.push_back(k
);
1019 for (DLDbList::const_iterator it
= dLDbList
.begin();
1020 it
!= dLDbList
.end(); ++it
)
1022 Keychain k
= keychain(*it
);
1023 result
.push_back(k
);
1026 for (DLDbList::const_iterator it
= commonList
.begin();
1027 it
!= commonList
.end(); ++it
)
1029 Keychain k
= keychain(*it
);
1030 result
.push_back(k
);
1034 keychainList
.swap(result
);
1038 StorageManager::setSearchList(const KeychainList
&keychainList
)
1040 StLock
<Mutex
>_(mMutex
);
1042 DLDbList searchList
, oldSearchList(mSavedList
.searchList());
1043 for (KeychainList::const_iterator it
= keychainList
.begin(); it
!= keychainList
.end(); ++it
)
1045 DLDbIdentifier dldbi
= demungeDLDbIdentifier((*it
)->dlDbIdentifier());
1047 // If this keychain is not in the common or dynamic lists, add it to the new search list
1048 DLDbList commonList
= mCommonList
.searchList();
1050 for(DLDbList::const_iterator jt
= commonList
.begin(); jt
!= commonList
.end(); ++jt
) {
1051 if((*jt
) == dldbi
) {
1056 DLDbList dynamicList
= mDynamicList
.searchList();
1057 for(DLDbList::const_iterator jt
= dynamicList
.begin(); jt
!= dynamicList
.end(); ++jt
) {
1058 if((*jt
) == dldbi
) {
1067 searchList
.push_back(dldbi
);
1071 // Set the current searchlist to be what was passed in, the old list will be freed
1072 // upon exit of this stackframe.
1073 mSavedList
.revert(true);
1074 mSavedList
.searchList(searchList
);
1078 if (!(oldSearchList
== searchList
))
1080 // Make sure we are not holding mLock when we post this event.
1081 KCEventNotifier::PostKeychainEvent(kSecKeychainListChangedEvent
);
1086 StorageManager::getSearchList(SecPreferencesDomain domain
, KeychainList
&keychainList
)
1088 StLock
<Mutex
>_(mMutex
);
1091 keychainList
.clear();
1095 if (domain
== kSecPreferencesDomainDynamic
)
1097 convertList(keychainList
, mDynamicList
.searchList());
1099 else if (domain
== mDomain
)
1101 mSavedList
.revert(false);
1102 convertList(keychainList
, mSavedList
.searchList());
1106 convertList(keychainList
, DLDbListCFPref(domain
).searchList());
1110 void StorageManager::forceUserSearchListReread()
1112 mSavedList
.forceUserSearchListReread();
1116 StorageManager::setSearchList(SecPreferencesDomain domain
, const KeychainList
&keychainList
)
1118 StLock
<Mutex
>_(mMutex
);
1120 if (domain
== kSecPreferencesDomainDynamic
)
1121 MacOSError::throwMe(errSecInvalidPrefsDomain
);
1123 DLDbList searchList
;
1124 convertList(searchList
, keychainList
);
1126 if (domain
== mDomain
)
1128 DLDbList
oldSearchList(mSavedList
.searchList());
1130 // Set the current searchlist to be what was passed in, the old list will be freed
1131 // upon exit of this stackframe.
1132 mSavedList
.revert(true);
1133 mSavedList
.searchList(searchList
);
1137 if (!(oldSearchList
== searchList
))
1139 KCEventNotifier::PostKeychainEvent(kSecKeychainListChangedEvent
);
1144 DLDbListCFPref(domain
).searchList(searchList
);
1149 StorageManager::domain(SecPreferencesDomain domain
)
1151 StLock
<Mutex
>_(mMutex
);
1153 if (domain
== kSecPreferencesDomainDynamic
)
1154 MacOSError::throwMe(errSecInvalidPrefsDomain
);
1156 if (domain
== mDomain
)
1157 return; // no change
1159 #if !defined(NDEBUG)
1162 case kSecPreferencesDomainSystem
:
1163 secnotice("storagemgr", "switching to system domain"); break;
1164 case kSecPreferencesDomainUser
:
1165 secnotice("storagemgr", "switching to user domain (uid %d)", getuid()); break;
1167 secnotice("storagemgr", "switching to weird prefs domain %d", domain
); break;
1172 mSavedList
.set(domain
);
1176 StorageManager::optionalSearchList(CFTypeRef keychainOrArray
, KeychainList
&keychainList
)
1178 StLock
<Mutex
>_(mMutex
);
1180 if (!keychainOrArray
)
1181 getSearchList(keychainList
);
1184 CFTypeID typeID
= CFGetTypeID(keychainOrArray
);
1185 if (typeID
== CFArrayGetTypeID())
1186 convertToKeychainList(CFArrayRef(keychainOrArray
), keychainList
);
1187 else if (typeID
== gTypes().KeychainImpl
.typeID
)
1188 keychainList
.push_back(KeychainImpl::required(SecKeychainRef(keychainOrArray
)));
1190 MacOSError::throwMe(errSecParam
);
1196 StorageManager::convertToKeychainList(CFArrayRef keychainArray
, KeychainList
&keychainList
)
1198 CFIndex count
= CFArrayGetCount(keychainArray
);
1202 KeychainList
keychains(count
);
1203 for (CFIndex ix
= 0; ix
< count
; ++ix
)
1205 keychains
[ix
] = KeychainImpl::required(SecKeychainRef(CFArrayGetValueAtIndex(keychainArray
, ix
)));
1208 keychainList
.swap(keychains
);
1212 StorageManager::convertFromKeychainList(const KeychainList
&keychainList
)
1214 CFRef
<CFMutableArrayRef
> keychainArray(CFArrayCreateMutable(NULL
, keychainList
.size(), &kCFTypeArrayCallBacks
));
1216 for (KeychainList::const_iterator ix
= keychainList
.begin(); ix
!= keychainList
.end(); ++ix
)
1218 SecKeychainRef keychainRef
= (*ix
)->handle();
1219 CFArrayAppendValue(keychainArray
, keychainRef
);
1220 CFRelease(keychainRef
);
1223 // Counter the CFRelease that CFRef<> is about to do when keychainArray goes out of scope.
1224 CFRetain(keychainArray
);
1225 return keychainArray
;
1228 void StorageManager::convertList(DLDbList
&ids
, const KeychainList
&kcs
)
1231 result
.reserve(kcs
.size());
1232 for (KeychainList::const_iterator ix
= kcs
.begin(); ix
!= kcs
.end(); ++ix
)
1234 result
.push_back(demungeDLDbIdentifier((*ix
)->dlDbIdentifier()));
1239 void StorageManager::convertList(KeychainList
&kcs
, const DLDbList
&ids
)
1241 StLock
<Mutex
>_(mMutex
);
1243 KeychainList result
;
1244 result
.reserve(ids
.size());
1246 for (DLDbList::const_iterator ix
= ids
.begin(); ix
!= ids
.end(); ++ix
)
1247 result
.push_back(keychain(*ix
));
1252 #pragma mark ____ Login Functions ____
1254 void StorageManager::login(AuthorizationRef authRef
, UInt32 nameLength
, const char* name
, bool isReset
)
1256 StLock
<Mutex
>_(mMutex
);
1258 AuthorizationItemSet
* info
= NULL
;
1259 OSStatus result
= AuthorizationCopyInfo(authRef
, NULL
, &info
); // get the results of the copy rights call.
1260 Boolean created
= false;
1261 if ( result
== errSecSuccess
&& info
->count
)
1263 // Grab the password from the auth context (info) and create the keychain...
1265 AuthorizationItem
* currItem
= info
->items
;
1266 for (UInt32 index
= 1; index
<= info
->count
; index
++) //@@@plugin bug won't return a specific context.
1268 if (strcmp(currItem
->name
, kAuthorizationEnvironmentPassword
) == 0)
1270 // creates the login keychain with the specified password
1273 login(nameLength
, name
, (UInt32
)currItem
->valueLength
, currItem
->value
, isReset
);
1285 AuthorizationFreeItemSet(info
);
1288 MacOSError::throwMe(errAuthorizationInternal
);
1291 void StorageManager::login(ConstStringPtr name
, ConstStringPtr password
)
1293 StLock
<Mutex
>_(mMutex
);
1295 if ( name
== NULL
|| password
== NULL
)
1296 MacOSError::throwMe(errSecParam
);
1298 login(name
[0], name
+ 1, password
[0], password
+ 1, false);
1301 void StorageManager::login(UInt32 nameLength
, const void *name
,
1302 UInt32 passwordLength
, const void *password
, bool isReset
)
1304 if (passwordLength
!= 0 && password
== NULL
)
1306 secnotice("KCLogin", "StorageManager::login: invalid argument (NULL password)");
1307 MacOSError::throwMe(errSecParam
);
1310 DLDbIdentifier loginDLDbIdentifier
;
1312 mSavedList
.revert(true);
1313 loginDLDbIdentifier
= mSavedList
.loginDLDbIdentifier();
1316 secnotice("KCLogin", "StorageManager::login: loginDLDbIdentifier is %s", (loginDLDbIdentifier
) ? loginDLDbIdentifier
.dbName() : "<NULL>");
1317 if (!loginDLDbIdentifier
)
1318 MacOSError::throwMe(errSecNoSuchKeychain
);
1321 //***************************************************************
1322 // gather keychain information
1323 //***************************************************************
1326 int uid
= geteuid();
1327 struct passwd
*pw
= getpwuid(uid
);
1329 secnotice("KCLogin", "StorageManager::login: invalid argument (NULL uid)");
1330 MacOSError::throwMe(errSecParam
);
1332 char *userName
= pw
->pw_name
;
1334 // make keychain path strings
1335 std::string keychainPath
= DLDbListCFPref::ExpandTildesInPath(kLoginKeychainPathPrefix
);
1336 std::string shortnameKeychain
= keychainPath
+ userName
;
1337 std::string shortnameDotKeychain
= shortnameKeychain
+ ".keychain";
1338 std::string loginDotKeychain
= keychainPath
+ "login.keychain";
1339 std::string loginRenamed1Keychain
= keychainPath
+ "login_renamed1.keychain";
1340 std::string loginKeychainDb
= keychainPath
+ "login.keychain-db";
1342 // check for existence of keychain files
1343 bool shortnameKeychainExists
= false;
1344 bool shortnameDotKeychainExists
= false;
1345 bool loginKeychainExists
= false;
1346 bool loginRenamed1KeychainExists
= false;
1347 bool loginKeychainDbExists
= false;
1351 stat_result
= ::stat(shortnameKeychain
.c_str(), &st
);
1352 shortnameKeychainExists
= (stat_result
== 0);
1353 stat_result
= ::stat(shortnameDotKeychain
.c_str(), &st
);
1354 shortnameDotKeychainExists
= (stat_result
== 0);
1355 stat_result
= ::stat(loginDotKeychain
.c_str(), &st
);
1356 loginKeychainExists
= (stat_result
== 0);
1357 stat_result
= ::stat(loginRenamed1Keychain
.c_str(), &st
);
1358 loginRenamed1KeychainExists
= (stat_result
== 0);
1359 stat_result
= ::stat(loginKeychainDb
.c_str(), &st
);
1360 loginKeychainDbExists
= (stat_result
== 0);
1363 // login.keychain-db is considered to be the same as login.keychain.
1364 // Our transparent keychain promotion on open will handle opening the right version of this file.
1365 loginKeychainExists
|= loginKeychainDbExists
;
1367 bool loginUnlocked
= false;
1369 // make the keychain identifiers
1370 CSSM_VERSION version
= {0, 0};
1371 DLDbIdentifier shortnameDLDbIdentifier
= DLDbListCFPref::makeDLDbIdentifier(gGuidAppleCSPDL
, version
, 0, CSSM_SERVICE_CSP
| CSSM_SERVICE_DL
, shortnameKeychain
.c_str(), NULL
);
1372 DLDbIdentifier shortnameDotDLDbIdentifier
= DLDbListCFPref::makeDLDbIdentifier(gGuidAppleCSPDL
, version
, 0, CSSM_SERVICE_CSP
| CSSM_SERVICE_DL
, shortnameDotKeychain
.c_str(), NULL
);
1373 DLDbIdentifier loginRenamed1DLDbIdentifier
= DLDbListCFPref::makeDLDbIdentifier(gGuidAppleCSPDL
, version
, 0, CSSM_SERVICE_CSP
| CSSM_SERVICE_DL
, loginRenamed1Keychain
.c_str(), NULL
);
1375 //***************************************************************
1376 // make file renaming changes first
1377 //***************************************************************
1379 // if "~/Library/Keychains/shortname" exists, we need to migrate it forward;
1380 // either to login.keychain if there isn't already one, otherwise to shortname.keychain
1381 if (shortnameKeychainExists
) {
1382 int rename_stat
= 0;
1383 if (loginKeychainExists
) {
1385 int tmp_result
= ::stat(loginDotKeychain
.c_str(), &st
);
1386 if (tmp_result
== 0) {
1387 if (st
.st_size
<= kEmptyKeychainSizeInBytes
) {
1388 tmp_result
= ::unlink(loginDotKeychain
.c_str());
1389 rename_stat
= ::rename(shortnameKeychain
.c_str(), loginDotKeychain
.c_str());
1390 shortnameKeychainExists
= (rename_stat
!= 0);
1394 if (shortnameKeychainExists
) {
1395 if (loginKeychainExists
&& !shortnameDotKeychainExists
) {
1396 rename_stat
= ::rename(shortnameKeychain
.c_str(), shortnameDotKeychain
.c_str());
1397 shortnameDotKeychainExists
= (rename_stat
== 0);
1398 } else if (!loginKeychainExists
) {
1399 rename_stat
= ::rename(shortnameKeychain
.c_str(), loginDotKeychain
.c_str());
1400 loginKeychainExists
= (rename_stat
== 0);
1402 // we have all 3 keychains: login.keychain, shortname, and shortname.keychain.
1403 // on Leopard we never want a shortname keychain, so we must move it aside.
1404 char pathbuf
[MAXPATHLEN
];
1405 std::string shortnameRenamedXXXKeychain
= keychainPath
;
1406 shortnameRenamedXXXKeychain
+= userName
;
1407 shortnameRenamedXXXKeychain
+= "_renamed_XXX.keychain";
1408 ::strlcpy(pathbuf
, shortnameRenamedXXXKeychain
.c_str(), sizeof(pathbuf
));
1409 ::mkstemps(pathbuf
, 9); // 9 == strlen(".keychain")
1410 rename_stat
= ::rename(shortnameKeychain
.c_str(), pathbuf
);
1411 shortnameKeychainExists
= (rename_stat
!= 0);
1414 if (rename_stat
!= 0) {
1415 MacOSError::throwMe(errno
);
1419 //***************************************************************
1420 // handle special case where user previously reset the keychain
1421 //***************************************************************
1422 // Since 9A581, we have changed the definition of kKeychainRenamedSuffix from "_renamed" to "_renamed_".
1423 // Therefore, if "login_renamed1.keychain" exists and there is no plist, the user may have run into a
1424 // prior upgrade issue and clicked Reset. If we can successfully unlock login_renamed1.keychain with the
1425 // supplied password, then we will attempt to rename it to login.keychain if that file is empty, or with
1426 // "shortname.keychain" if it is not.
1428 if (loginRenamed1KeychainExists
&& (!loginKeychainExists
||
1429 (mSavedList
.searchList().size() == 1 && mSavedList
.member(demungeDLDbIdentifier(loginDLDbIdentifier
))) )) {
1432 Keychain
loginRenamed1KC(keychain(loginRenamed1DLDbIdentifier
));
1433 secnotice("KCLogin", "Attempting to unlock renamed KC \"%s\"",
1434 (loginRenamed1KC
) ? loginRenamed1KC
->name() : "<NULL>");
1435 loginRenamed1KC
->unlock(CssmData(const_cast<void *>(password
), passwordLength
));
1436 // if we get here, we unlocked it
1437 if (loginKeychainExists
) {
1439 int tmp_result
= ::stat(loginDotKeychain
.c_str(), &st
);
1440 if (tmp_result
== 0) {
1441 if (st
.st_size
<= kEmptyKeychainSizeInBytes
) {
1442 tmp_result
= ::unlink(loginDotKeychain
.c_str());
1443 tmp_result
= ::rename(loginRenamed1Keychain
.c_str(), loginDotKeychain
.c_str());
1444 } else if (!shortnameDotKeychainExists
) {
1445 tmp_result
= ::rename(loginRenamed1Keychain
.c_str(), shortnameDotKeychain
.c_str());
1446 shortnameDotKeychainExists
= (tmp_result
== 0);
1448 throw 1; // can't do anything with it except move it out of the way
1452 int tmp_result
= ::rename(loginRenamed1Keychain
.c_str(), loginDotKeychain
.c_str());
1453 loginKeychainExists
= (tmp_result
== 0);
1458 // we failed to unlock the login_renamed1.keychain file with the login password.
1459 // move it aside so we don't try to deal with it again.
1460 char pathbuf
[MAXPATHLEN
];
1461 std::string loginRenamedXXXKeychain
= keychainPath
;
1462 loginRenamedXXXKeychain
+= "login_renamed_XXX.keychain";
1463 ::strlcpy(pathbuf
, loginRenamedXXXKeychain
.c_str(), sizeof(pathbuf
));
1464 ::mkstemps(pathbuf
, 9); // 9 == strlen(".keychain")
1465 ::rename(loginRenamed1Keychain
.c_str(), pathbuf
);
1469 // if login.keychain does not exist at this point, create it
1470 if (!loginKeychainExists
|| (isReset
&& !loginKeychainDbExists
)) {
1471 // but don't add it to the search list yet; we'll do that later
1472 Keychain theKeychain
= makeKeychain(loginDLDbIdentifier
, false, true);
1473 secnotice("KCLogin", "Creating login keychain %s", (loginDLDbIdentifier
) ? loginDLDbIdentifier
.dbName() : "<NULL>");
1474 theKeychain
->create(passwordLength
, password
);
1475 secnotice("KCLogin", "Login keychain created successfully");
1476 loginKeychainExists
= true;
1477 // Set the prefs for this new login keychain.
1478 loginKeychain(theKeychain
);
1479 // Login Keychain does not lock on sleep nor lock after timeout by default.
1480 theKeychain
->setSettings(INT_MAX
, false);
1481 loginUnlocked
= true;
1482 mSavedList
.revert(true);
1485 //***************************************************************
1486 // make plist changes after files have been renamed or created
1487 //***************************************************************
1489 // if the shortname keychain exists in the search list, either rename or remove the entry
1490 if (mSavedList
.member(demungeDLDbIdentifier(shortnameDLDbIdentifier
))) {
1491 if (shortnameDotKeychainExists
&& !mSavedList
.member(demungeDLDbIdentifier(shortnameDotDLDbIdentifier
))) {
1492 // change shortname to shortname.keychain (login.keychain will be added later if not present)
1493 secnotice("KCLogin", "Renaming %s to %s in keychain search list",
1494 (shortnameDLDbIdentifier
) ? shortnameDLDbIdentifier
.dbName() : "<NULL>",
1495 (shortnameDotDLDbIdentifier
) ? shortnameDotDLDbIdentifier
.dbName() : "<NULL>");
1496 mSavedList
.rename(demungeDLDbIdentifier(shortnameDLDbIdentifier
),
1497 demungeDLDbIdentifier(shortnameDotDLDbIdentifier
));
1498 } else if (!mSavedList
.member(demungeDLDbIdentifier(loginDLDbIdentifier
))) {
1499 // change shortname to login.keychain
1500 secnotice("KCLogin", "Renaming %s to %s in keychain search list",
1501 (shortnameDLDbIdentifier
) ? shortnameDLDbIdentifier
.dbName() : "<NULL>",
1502 (loginDLDbIdentifier
) ? loginDLDbIdentifier
.dbName() : "<NULL>");
1503 mSavedList
.rename(demungeDLDbIdentifier(shortnameDLDbIdentifier
),
1504 demungeDLDbIdentifier(loginDLDbIdentifier
));
1506 // already have login.keychain in list, and renaming to shortname.keychain isn't an option,
1507 // so just remove the entry
1508 secnotice("KCLogin", "Removing %s from keychain search list", (shortnameDLDbIdentifier
) ? shortnameDLDbIdentifier
.dbName() : "<NULL>");
1509 mSavedList
.remove(demungeDLDbIdentifier(shortnameDLDbIdentifier
));
1512 // note: save() will cause the plist to be unlinked if the only remaining entry is for login.keychain
1514 mSavedList
.revert(true);
1517 // make sure that login.keychain is in the search list
1518 if (!mSavedList
.member(demungeDLDbIdentifier(loginDLDbIdentifier
))) {
1519 secnotice("KCLogin", "Adding %s to keychain search list", (loginDLDbIdentifier
) ? loginDLDbIdentifier
.dbName() : "<NULL>");
1520 mSavedList
.add(demungeDLDbIdentifier(loginDLDbIdentifier
));
1522 mSavedList
.revert(true);
1525 // if we have a shortname.keychain, always include it in the plist (after login.keychain)
1526 if (shortnameDotKeychainExists
&& !mSavedList
.member(demungeDLDbIdentifier(shortnameDotDLDbIdentifier
))) {
1527 mSavedList
.add(demungeDLDbIdentifier(shortnameDotDLDbIdentifier
));
1529 mSavedList
.revert(true);
1532 // make sure that the default keychain is in the search list; if not, reset the default to login.keychain
1533 if (!mSavedList
.member(mSavedList
.defaultDLDbIdentifier())) {
1534 secnotice("KCLogin", "Changing default keychain to %s", (loginDLDbIdentifier
) ? loginDLDbIdentifier
.dbName() : "<NULL>");
1535 mSavedList
.defaultDLDbIdentifier(demungeDLDbIdentifier(loginDLDbIdentifier
));
1537 mSavedList
.revert(true);
1540 //***************************************************************
1541 // auto-unlock the login keychain(s)
1542 //***************************************************************
1543 // all our preflight fixups are finally done, so we can now attempt to unlock the login keychain
1545 OSStatus loginResult
= errSecSuccess
;
1546 if (!loginUnlocked
) {
1549 Keychain
theKeychain(keychain(loginDLDbIdentifier
));
1550 secnotice("KCLogin", "Attempting to unlock login keychain \"%s\"",
1551 (theKeychain
) ? theKeychain
->name() : "<NULL>");
1552 theKeychain
->unlock(CssmData(const_cast<void *>(password
), passwordLength
));
1553 loginUnlocked
= true;
1555 catch(const CssmError
&e
)
1557 loginResult
= e
.osStatus(); // save this result
1561 // is it token login?
1562 CFRef
<CFDictionaryRef
> tokenLoginContext
;
1563 OSStatus status
= TokenLoginGetContext(password
, passwordLength
, tokenLoginContext
.take());
1564 if (!loginUnlocked
|| status
== errSecSuccess
) {
1565 Keychain
theKeychain(keychain(loginDLDbIdentifier
));
1566 bool tokenLoginDataUpdated
= false;
1568 for (UInt32 i
= 0; i
< 2; i
++) {
1569 loginResult
= errSecSuccess
;
1571 CFRef
<CFDictionaryRef
> tokenLoginData
;
1572 if (tokenLoginContext
) {
1573 status
= TokenLoginGetLoginData(tokenLoginContext
, tokenLoginData
.take());
1574 if (status
!= errSecSuccess
) {
1575 if (tokenLoginDataUpdated
) {
1576 loginResult
= status
;
1579 // updating unlock key fails if it is not token login
1580 secnotice("KCLogin", "Error %d, reconstructing unlock data", (int)status
);
1581 status
= TokenLoginUpdateUnlockData(tokenLoginContext
);
1582 if (status
== errSecSuccess
) {
1583 loginResult
= TokenLoginGetLoginData(tokenLoginContext
, tokenLoginData
.take());
1584 if (loginResult
!= errSecSuccess
) {
1587 tokenLoginDataUpdated
= true;
1593 // first try to unlock login keychain because if this fails, token keychain unlock fails as well
1594 if (tokenLoginData
) {
1595 secnotice("KCLogin", "Going to unlock keybag using scBlob");
1596 status
= TokenLoginUnlockKeybag(tokenLoginContext
, tokenLoginData
);
1597 secnotice("KCLogin", "Keybag unlock result %d", (int)status
);
1599 CssmError::throwMe(status
); // to trigger login data regeneration
1604 key
.header().BlobType
= CSSM_KEYBLOB_RAW
;
1605 key
.header().Format
= CSSM_KEYBLOB_RAW_FORMAT_OCTET_STRING
;
1606 key
.header().AlgorithmId
= CSSM_ALGID_3DES_3KEY
;
1607 key
.header().KeyClass
= CSSM_KEYCLASS_SESSION_KEY
;
1608 key
.header().KeyUsage
= CSSM_KEYUSE_ENCRYPT
| CSSM_KEYUSE_DECRYPT
| CSSM_KEYATTR_EXTRACTABLE
;
1609 key
.header().KeyAttr
= 0;
1610 CFRef
<CFDataRef
> tokenLoginUnlockKey
;
1611 if (tokenLoginData
) {
1612 status
= TokenLoginGetUnlockKey(tokenLoginContext
, tokenLoginUnlockKey
.take());
1614 CssmError::throwMe(status
); // to trigger login data regeneration
1615 key
.KeyData
= CssmData(tokenLoginUnlockKey
.get());
1617 key
.KeyData
= CssmData(const_cast<void *>(password
), passwordLength
);
1619 // unwrap it into the CSP (but keep it raw)
1620 UnwrapKey
unwrap(theKeychain
->csp(), CSSM_ALGID_NONE
);
1622 CssmData descriptiveData
;
1624 KeySpec(CSSM_KEYUSE_ANY
, CSSM_KEYATTR_EXTRACTABLE
),
1625 masterKey
, &descriptiveData
, NULL
);
1627 CssmClient::Db db
= theKeychain
->database();
1629 // create the keychain, using appropriate credentials
1630 Allocator
&alloc
= db
->allocator();
1631 AutoCredentials
cred(alloc
); // will leak, but we're quitting soon :-)
1633 // use this passphrase
1634 cred
+= TypedList(alloc
, CSSM_SAMPLE_TYPE_KEYCHAIN_LOCK
,
1635 new(alloc
) ListElement(CSSM_SAMPLE_TYPE_SYMMETRIC_KEY
),
1636 new(alloc
) ListElement(CssmData::wrap(theKeychain
->csp()->handle())),
1637 new(alloc
) ListElement(CssmData::wrap(masterKey
)),
1638 new(alloc
) ListElement(CssmData()));
1639 db
->authenticate(CSSM_DB_ACCESS_READ
, &cred
);
1641 loginUnlocked
= true;
1642 } catch (const CssmError
&e
) {
1643 if (tokenLoginData
&& !tokenLoginDataUpdated
) {
1644 // token login unlock key was invalid
1645 loginResult
= TokenLoginUpdateUnlockData(tokenLoginContext
);
1646 if (loginResult
== errSecSuccess
) {
1647 tokenLoginDataUpdated
= true;
1652 loginResult
= e
.osStatus();
1659 // if "shortname.keychain" exists and is in the search list, attempt to auto-unlock it with the same password
1660 if (shortnameDotKeychainExists
&& mSavedList
.member(demungeDLDbIdentifier(shortnameDotDLDbIdentifier
))) {
1663 Keychain
shortnameDotKC(keychain(shortnameDotDLDbIdentifier
));
1664 secnotice("KCLogin", "Attempting to unlock short name keychain \"%s\"",
1665 (shortnameDotKC
) ? shortnameDotKC
->name() : "<NULL>");
1666 shortnameDotKC
->unlock(CssmData(const_cast<void *>(password
), passwordLength
));
1668 catch(const CssmError
&e
)
1670 // ignore; failure to unlock this keychain is not considered an error
1674 if (loginResult
!= errSecSuccess
) {
1675 MacOSError::throwMe(loginResult
);
1679 void StorageManager::stashLogin()
1681 OSStatus loginResult
= errSecSuccess
;
1683 DLDbIdentifier loginDLDbIdentifier
;
1685 mSavedList
.revert(true);
1686 loginDLDbIdentifier
= mSavedList
.loginDLDbIdentifier();
1689 secnotice("KCLogin", "StorageManager::stash: loginDLDbIdentifier is %s", (loginDLDbIdentifier
) ? loginDLDbIdentifier
.dbName() : "<NULL>");
1690 if (!loginDLDbIdentifier
)
1691 MacOSError::throwMe(errSecNoSuchKeychain
);
1696 Keychain
theKeychain(keychain(loginDLDbIdentifier
));
1697 secnotice("KCLogin", "Attempting to use stash for login keychain \"%s\"",
1698 (theKeychain
) ? theKeychain
->name() : "<NULL>");
1699 theKeychain
->stashCheck();
1701 catch(const CssmError
&e
)
1703 loginResult
= e
.osStatus(); // save this result
1707 if (loginResult
!= errSecSuccess
) {
1708 MacOSError::throwMe(loginResult
);
1712 void StorageManager::stashKeychain()
1714 OSStatus loginResult
= errSecSuccess
;
1716 DLDbIdentifier loginDLDbIdentifier
;
1718 mSavedList
.revert(true);
1719 loginDLDbIdentifier
= mSavedList
.loginDLDbIdentifier();
1722 secnotice("KCLogin", "StorageManager::stash: loginDLDbIdentifier is %s", (loginDLDbIdentifier
) ? loginDLDbIdentifier
.dbName() : "<NULL>");
1723 if (!loginDLDbIdentifier
)
1724 MacOSError::throwMe(errSecNoSuchKeychain
);
1728 Keychain
theKeychain(keychain(loginDLDbIdentifier
));
1729 secnotice("KCLogin", "Attempting to stash login keychain \"%s\"",
1730 (theKeychain
) ? theKeychain
->name() : "<NULL>");
1731 theKeychain
->stash();
1733 catch(const CssmError
&e
)
1735 loginResult
= e
.osStatus(); // save this result
1739 if (loginResult
!= errSecSuccess
) {
1740 MacOSError::throwMe(loginResult
);
1744 void StorageManager::logout()
1746 // nothing left to do here
1749 void StorageManager::changeLoginPassword(ConstStringPtr oldPassword
, ConstStringPtr newPassword
)
1751 StLock
<Mutex
>_(mMutex
);
1753 loginKeychain()->changePassphrase(oldPassword
, newPassword
);
1754 secnotice("KClogin", "Changed login keychain password successfully");
1758 void StorageManager::changeLoginPassword(UInt32 oldPasswordLength
, const void *oldPassword
, UInt32 newPasswordLength
, const void *newPassword
)
1760 StLock
<Mutex
>_(mMutex
);
1762 loginKeychain()->changePassphrase(oldPasswordLength
, oldPassword
, newPasswordLength
, newPassword
);
1763 secnotice("KClogin", "Changed login keychain password successfully");
1766 // Clear out the keychain search list and rename the existing login.keychain.
1768 void StorageManager::resetKeychain(Boolean resetSearchList
)
1770 StLock
<Mutex
>_(mMutex
);
1772 // Clear the keychain search list.
1775 if ( resetSearchList
)
1777 StorageManager::KeychainList keychainList
;
1778 setSearchList(keychainList
);
1780 // Get a reference to the existing login keychain...
1781 // If we don't have one, we throw (not requiring a rename).
1783 Keychain keychain
= loginKeychain();
1785 // Rename the existing login.keychain (i.e. put it aside).
1787 CFMutableStringRef newName
= NULL
;
1788 newName
= CFStringCreateMutable(NULL
, 0);
1789 CFStringRef currName
= NULL
;
1790 currName
= CFStringCreateWithCString(NULL
, keychain
->name(), kCFStringEncodingUTF8
);
1791 if ( newName
&& currName
)
1793 CFStringAppend(newName
, currName
);
1794 CFStringRef kcSuffix
= CFSTR(kKeychainSuffix
);
1795 CFStringRef kcDbSuffix
= CFSTR(kKeychainDbSuffix
);
1796 bool hasDbSuffix
= false;
1797 if ( CFStringHasSuffix(newName
, kcSuffix
) ) // remove the .keychain extension
1799 CFRange suffixRange
= CFStringFind(newName
, kcSuffix
, 0);
1800 CFStringFindAndReplace(newName
, kcSuffix
, CFSTR(""), suffixRange
, 0);
1802 if (CFStringHasSuffix(newName
, kcDbSuffix
)) {
1804 CFRange suffixRange
= CFStringFind(newName
, kcDbSuffix
, 0);
1805 CFStringFindAndReplace(newName
, kcDbSuffix
, CFSTR(""), suffixRange
, 0);
1808 CFStringAppend(newName
, CFSTR(kKeychainRenamedSuffix
)); // add "_renamed_"
1811 renameUnique(keychain
, newName
, hasDbSuffix
);
1815 // we need to release 'newName' & 'currName'
1817 } // else, let the login call report a duplicate
1821 CFRelease(currName
);
1825 // We either don't have a login keychain, or there was a
1826 // failure to rename the existing one.
1830 #pragma mark ____ File Related ____
1832 Keychain
StorageManager::make(const char *pathName
)
1834 return make(pathName
, true);
1837 Keychain
StorageManager::make(const char *pathName
, bool add
)
1839 return make(pathName
, add
, false);
1842 Keychain
StorageManager::make(const char *pathName
, bool add
, bool isReset
) {
1843 return makeKeychain(makeDLDbIdentifier(pathName
), add
, isReset
);
1846 DLDbIdentifier
StorageManager::makeDLDbIdentifier(const char *pathName
) {
1847 StLock
<Mutex
>_(mMutex
);
1849 string fullPathName
;
1850 if ( pathName
[0] == '/' )
1851 fullPathName
= pathName
;
1854 // Get Home directory from environment.
1857 case kSecPreferencesDomainUser
:
1859 const char *homeDir
= getenv("HOME");
1860 if (homeDir
== NULL
)
1862 // If $HOME is unset get the current user's home directory
1863 // from the passwd file.
1864 uid_t uid
= geteuid();
1865 if (!uid
) uid
= getuid();
1866 struct passwd
*pw
= getpwuid(uid
);
1868 MacOSError::throwMe(errSecParam
);
1869 homeDir
= pw
->pw_dir
;
1871 fullPathName
= homeDir
;
1874 case kSecPreferencesDomainSystem
:
1878 assert(false); // invalid domain for this
1881 fullPathName
+= "/Library/Keychains/";
1882 fullPathName
+= pathName
;
1885 const CSSM_NET_ADDRESS
*DbLocation
= NULL
; // NULL for keychains
1886 const CSSM_VERSION
*version
= NULL
;
1887 uint32 subserviceId
= 0;
1888 CSSM_SERVICE_TYPE subserviceType
= CSSM_SERVICE_DL
| CSSM_SERVICE_CSP
;
1889 const CssmSubserviceUid
ssuid(gGuidAppleCSPDL
, version
,
1890 subserviceId
, subserviceType
);
1891 DLDbIdentifier
dlDbIdentifier(ssuid
, fullPathName
.c_str(), DbLocation
);
1892 return dlDbIdentifier
;
1895 Keychain
StorageManager::makeLoginAuthUI(const Item
*item
, bool isReset
)
1897 StLock
<Mutex
>_(mMutex
);
1899 // Create a login/default keychain for the user using UI.
1900 // The user can cancel out of the operation, or create a new login keychain.
1901 // If auto-login is turned off, the user will be asked for their login password.
1903 OSStatus result
= errSecSuccess
;
1904 Keychain keychain
; // We return this keychain.
1906 // Set up the Auth ref to bring up UI.
1908 AuthorizationItem
*currItem
, *authEnvirItemArrayPtr
= NULL
;
1909 AuthorizationRef authRef
= NULL
;
1912 result
= AuthorizationCreate(NULL
, NULL
, kAuthorizationFlagDefaults
, &authRef
);
1914 MacOSError::throwMe(result
);
1916 AuthorizationEnvironment envir
;
1917 envir
.count
= 6; // up to 6 hints can be used.
1918 authEnvirItemArrayPtr
= (AuthorizationItem
*)malloc(sizeof(AuthorizationItem
) * envir
.count
);
1919 if ( !authEnvirItemArrayPtr
)
1920 MacOSError::throwMe(errAuthorizationInternal
);
1922 currItem
= envir
.items
= authEnvirItemArrayPtr
;
1925 // 1st Hint (optional): The keychain item's account attribute string.
1926 // When item is specified, we assume an 'add' operation is being attempted.
1929 SecKeychainAttribute attr
= { kSecAccountItemAttr
, 255, &buff
};
1934 (*item
)->getAttribute(attr
, &actLen
);
1938 actLen
= 0; // This item didn't have the account attribute, so don't display one in the UI.
1941 currItem
->name
= AGENT_HINT_ATTR_NAME
; // name str that identifies this hint as attr name
1942 if ( actLen
) // Fill in the hint if we have an account attr
1944 if ( actLen
>= sizeof(buff
) )
1945 buff
[sizeof(buff
)-1] = 0;
1948 currItem
->valueLength
= strlen(buff
)+1;
1949 currItem
->value
= buff
;
1953 currItem
->valueLength
= 0;
1954 currItem
->value
= NULL
;
1956 currItem
->flags
= 0;
1959 // 2nd Hint (optional): The item's keychain full path.
1962 char* currDefaultName
= NULL
;
1965 currDefaultName
= (char*)defaultKeychain()->name(); // Use the name if we have it.
1966 currItem
->name
= AGENT_HINT_LOGIN_KC_NAME
; // Name str that identifies this hint as kc path
1967 currItem
->valueLength
= (currDefaultName
) ? strlen(currDefaultName
) : 0;
1968 currItem
->value
= (currDefaultName
) ? (void*)currDefaultName
: (void*)"";
1969 currItem
->flags
= 0;
1978 // 3rd Hint (required): check if curr default keychain is unavailable.
1979 // This is determined by the parent not existing.
1981 currItem
->name
= AGENT_HINT_LOGIN_KC_EXISTS_IN_KC_FOLDER
;
1982 Boolean loginUnavail
= false;
1985 Keychain defaultKC
= defaultKeychain();
1986 if ( !defaultKC
->exists() )
1987 loginUnavail
= true;
1989 catch(...) // login.keychain not present
1992 currItem
->valueLength
= sizeof(Boolean
);
1993 currItem
->value
= (void*)&loginUnavail
;
1994 currItem
->flags
= 0;
1997 // 4th Hint (required): userName
2000 currItem
->name
= AGENT_HINT_LOGIN_KC_USER_NAME
;
2001 char* uName
= getenv("USER");
2002 string userName
= uName
? uName
: "";
2003 if ( userName
.length() == 0 )
2005 uid_t uid
= geteuid();
2006 if (!uid
) uid
= getuid();
2007 struct passwd
*pw
= getpwuid(uid
); // fallback case...
2009 userName
= pw
->pw_name
;
2012 if ( userName
.length() == 0 ) // did we ultimately get one?
2013 MacOSError::throwMe(errAuthorizationInternal
);
2015 currItem
->value
= (void*)userName
.c_str();
2016 currItem
->valueLength
= userName
.length();
2017 currItem
->flags
= 0;
2020 // 5th Hint (required): flags if user has more than 1 keychain (used for a later warning when reset to default).
2023 currItem
->name
= AGENT_HINT_LOGIN_KC_USER_HAS_OTHER_KCS_STR
;
2024 Boolean moreThanOneKCExists
= false;
2026 // if item is NULL, then this is a user-initiated full reset
2027 if (item
&& mSavedList
.searchList().size() > 1)
2028 moreThanOneKCExists
= true;
2030 currItem
->value
= &moreThanOneKCExists
;
2031 currItem
->valueLength
= sizeof(Boolean
);
2032 currItem
->flags
= 0;
2035 // 6th Hint (required): If no item is involved, this is a user-initiated full reset.
2036 // We want to suppress the "do you want to reset to defaults?" panel in this case.
2039 currItem
->name
= AGENT_HINT_LOGIN_KC_SUPPRESS_RESET_PANEL
;
2040 Boolean suppressResetPanel
= (item
== NULL
) ? TRUE
: FALSE
;
2041 currItem
->valueLength
= sizeof(Boolean
);
2042 currItem
->value
= (void*)&suppressResetPanel
;
2043 currItem
->flags
= 0;
2046 // Set up the auth rights and make the auth call.
2048 AuthorizationItem authItem
= { LOGIN_KC_CREATION_RIGHT
, 0 , NULL
, 0 };
2049 AuthorizationRights rights
= { 1, &authItem
};
2050 AuthorizationFlags flags
= kAuthorizationFlagDefaults
| kAuthorizationFlagInteractionAllowed
| kAuthorizationFlagExtendRights
;
2051 result
= AuthorizationCopyRights(authRef
, &rights
, &envir
, flags
, NULL
);
2053 MacOSError::throwMe(result
);
2056 resetKeychain(true); // Clears the plist, moves aside existing login.keychain
2058 catch (...) // can throw if no existing login.keychain is found
2061 login(authRef
, (UInt32
)userName
.length(), userName
.c_str(), isReset
); // Create login.keychain
2062 keychain
= loginKeychain(); // Get newly-created login keychain
2063 defaultKeychain(keychain
); // Set it to be the default
2065 free(authEnvirItemArrayPtr
);
2066 AuthorizationFree(authRef
, kAuthorizationFlagDefaults
);
2071 // clean up allocations, then rethrow error
2072 if ( authEnvirItemArrayPtr
)
2073 free(authEnvirItemArrayPtr
);
2075 AuthorizationFree(authRef
, kAuthorizationFlagDefaults
);
2082 Keychain
StorageManager::defaultKeychainUI(Item
&item
)
2084 StLock
<Mutex
>_(mMutex
);
2086 Keychain returnedKeychain
;
2089 returnedKeychain
= defaultKeychain(); // If we have one, return it.
2090 if ( returnedKeychain
->exists() )
2091 return returnedKeychain
;
2093 catch(...) // We could have one, but it isn't available (i.e. on a un-mounted volume).
2096 if ( globals().getUserInteractionAllowed() )
2098 returnedKeychain
= makeLoginAuthUI(&item
, false); // If no Keychains is present, one will be created.
2099 if ( !returnedKeychain
)
2100 MacOSError::throwMe(errSecInvalidKeychain
); // Something went wrong...
2103 MacOSError::throwMe(errSecInteractionNotAllowed
); // If UI isn't allowed, return an error.
2105 return returnedKeychain
;
2109 StorageManager::addToDomainList(SecPreferencesDomain domain
,
2110 const char* dbName
, const CSSM_GUID
&guid
, uint32 subServiceType
)
2112 StLock
<Mutex
>_(mMutex
);
2114 if (domain
== kSecPreferencesDomainDynamic
)
2115 MacOSError::throwMe(errSecInvalidPrefsDomain
);
2117 // make the identifier
2118 CSSM_VERSION version
= {0, 0};
2119 DLDbIdentifier id
= DLDbListCFPref::makeDLDbIdentifier (guid
, version
, 0,
2120 subServiceType
, dbName
, NULL
);
2122 if (domain
== mDomain
)
2124 // manipulate the user's list
2126 mSavedList
.revert(true);
2127 mSavedList
.add(demungeDLDbIdentifier(id
));
2131 KCEventNotifier::PostKeychainEvent(kSecKeychainListChangedEvent
);
2135 // manipulate the other list
2136 DLDbListCFPref(domain
).add(id
);
2141 StorageManager::isInDomainList(SecPreferencesDomain domain
,
2142 const char* dbName
, const CSSM_GUID
&guid
, uint32 subServiceType
)
2144 StLock
<Mutex
>_(mMutex
);
2146 if (domain
== kSecPreferencesDomainDynamic
)
2147 MacOSError::throwMe(errSecInvalidPrefsDomain
);
2149 CSSM_VERSION version
= {0, 0};
2150 DLDbIdentifier id
= DLDbListCFPref::makeDLDbIdentifier (guid
, version
, 0,
2151 subServiceType
, dbName
, NULL
);
2153 // determine the list to search
2155 if (domain
== mDomain
)
2157 result
= mSavedList
.member(demungeDLDbIdentifier(id
));
2161 result
= DLDbListCFPref(domain
).member(demungeDLDbIdentifier(id
));
2167 MacOSError::throwMe(errSecNoSuchKeychain
);
2172 StorageManager::removeFromDomainList(SecPreferencesDomain domain
,
2173 const char* dbName
, const CSSM_GUID
&guid
, uint32 subServiceType
)
2175 StLock
<Mutex
>_(mMutex
);
2177 if (domain
== kSecPreferencesDomainDynamic
)
2178 MacOSError::throwMe(errSecInvalidPrefsDomain
);
2180 // make the identifier
2181 CSSM_VERSION version
= {0, 0};
2182 DLDbIdentifier id
= DLDbListCFPref::makeDLDbIdentifier (guid
, version
, 0,
2183 subServiceType
, dbName
, NULL
);
2185 if (domain
== mDomain
)
2187 // manipulate the user's list
2189 mSavedList
.revert(true);
2190 mSavedList
.remove(demungeDLDbIdentifier(id
));
2194 KCEventNotifier::PostKeychainEvent(kSecKeychainListChangedEvent
);
2198 // manipulate the other list
2199 DLDbListCFPref(domain
).remove(id
);
2204 StorageManager::keychainOwnerPermissionsValidForDomain(const char* path
, SecPreferencesDomain domain
)
2208 const char* sysPrefDir
= "/Library/Preferences";
2209 const char* errMsg
= "Will not set default";
2210 char* mustOwnDir
= NULL
;
2211 struct passwd
* pw
= NULL
;
2214 uid_t uid
= geteuid();
2215 if (!uid
) uid
= getuid();
2217 // our (e)uid must own the appropriate preferences or home directory
2218 // for the specified preference domain whose default we will be modifying
2220 case kSecPreferencesDomainUser
:
2221 mustOwnDir
= getenv("HOME");
2222 if (mustOwnDir
== NULL
) {
2224 if (!pw
) return false;
2225 mustOwnDir
= pw
->pw_dir
;
2228 case kSecPreferencesDomainSystem
:
2229 mustOwnDir
= (char*)sysPrefDir
;
2231 case kSecPreferencesDomainCommon
:
2232 mustOwnDir
= (char*)sysPrefDir
;
2238 if (mustOwnDir
!= NULL
) {
2240 if ( (stat(mustOwnDir
, &dsb
) != 0) || (dsb
.st_uid
!= uid
) ) {
2241 fprintf(stderr
, "%s: UID=%d does not own directory %s\n", errMsg
, (int)uid
, mustOwnDir
);
2242 mustOwnDir
= NULL
; // will return below after calling endpwent()
2249 if (mustOwnDir
== NULL
)
2252 // check that file actually exists
2253 if (stat(path
, &sb
) != 0) {
2254 fprintf(stderr
, "%s: file %s does not exist\n", errMsg
, path
);
2259 if (sb
.st_flags
& (SF_IMMUTABLE
| UF_IMMUTABLE
)) {
2260 fprintf(stderr
, "%s: file %s is immutable\n", errMsg
, path
);
2265 if (sb
.st_uid
!= uid
) {
2266 fprintf(stderr
, "%s: file %s is owned by UID=%d, but we have UID=%d\n",
2267 errMsg
, path
, (int)sb
.st_uid
, (int)uid
);
2273 perms
|= 0600; // must have owner read/write permission set
2274 if (sb
.st_mode
!= perms
) {
2275 fprintf(stderr
, "%s: file %s does not have the expected permissions\n", errMsg
, path
);
2279 // user owns file and can read/write it