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 <Security/SecCFAllocator.h>
51 #include "TrustSettingsSchema.h"
52 #include <security_cdsa_client/wrapkey.h>
53 #include <securityd_client/ssblob.h>
54 #include <Security/SecBasePriv.h>
55 #include "TokenLogin.h"
57 //%%% add this to AuthorizationTagsPriv.h later
58 #ifndef AGENT_HINT_LOGIN_KC_SUPPRESS_RESET_PANEL
59 #define AGENT_HINT_LOGIN_KC_SUPPRESS_RESET_PANEL "loginKCCreate:suppressResetPanel"
66 using namespace CssmClient
;
67 using namespace KeychainCore
;
69 #define kLoginKeychainPathPrefix "~/Library/Keychains/"
70 #define kUserLoginKeychainPath "~/Library/Keychains/login.keychain"
71 #define kEmptyKeychainSizeInBytes 20460
73 //-----------------------------------------------------------------------------------
75 static SecPreferencesDomain
defaultPreferenceDomain()
77 SessionAttributeBits sessionAttrs
;
79 secnotice("servermode", "StorageManager initialized in server mode");
80 sessionAttrs
= sessionIsRoot
;
82 MacOSError::check(SessionGetInfo(callerSecuritySession
, NULL
, &sessionAttrs
));
85 // If this is the root session, use system preferences.
86 // (In SecurityServer debug mode, you'll get a (fake) root session
87 // that has graphics access. Ignore that to help testing.)
88 if ((sessionAttrs
& sessionIsRoot
)
89 IFDEBUG( && !(sessionAttrs
& sessionHasGraphicAccess
))) {
90 secnotice("storagemgr", "using system preferences");
91 return kSecPreferencesDomainSystem
;
94 // otherwise, use normal (user) preferences
95 return kSecPreferencesDomainUser
;
98 static bool isAppSandboxed()
101 SecTaskRef task
= SecTaskCreateFromSelf(NULL
);
103 CFTypeRef appSandboxValue
= SecTaskCopyValueForEntitlement(task
,
104 CFSTR("com.apple.security.app-sandbox"), NULL
);
105 if(appSandboxValue
!= NULL
) {
107 CFRelease(appSandboxValue
);
114 static bool shouldAddToSearchList(const DLDbIdentifier
&dLDbIdentifier
)
116 // Creation of a private keychain should not modify the search list: rdar://13529331
117 // However, we want to ensure the login and System keychains are in
118 // the search list if that is not the case when they are created.
119 // Note that App Sandbox apps may not modify the list in either case.
121 bool loginOrSystemKeychain
= false;
122 const char *dbname
= dLDbIdentifier
.dbName();
124 if ((!strcmp(dbname
, "/Library/Keychains/System.keychain")) ||
125 (strstr(dbname
, "/login.keychain")) ) {
126 loginOrSystemKeychain
= true;
129 return (loginOrSystemKeychain
&& !isAppSandboxed());
133 StorageManager::StorageManager() :
134 mSavedList(defaultPreferenceDomain()),
135 mCommonList(kSecPreferencesDomainCommon
),
136 mDomain(kSecPreferencesDomainUser
),
137 mMutex(Mutex::recursive
)
143 StorageManager::getStorageManagerMutex()
145 return &mKeychainMapMutex
;
150 StorageManager::keychain(const DLDbIdentifier
&dLDbIdentifier
)
152 StLock
<Mutex
>_(mKeychainMapMutex
);
157 KeychainMap::iterator it
= mKeychainMap
.end();
159 // If we have a keychain object for the munged keychain, return that.
160 // Don't hit the filesystem to check file status if we've already done that work...
161 DLDbIdentifier munge_dldbi
= forceMungeDLDbIDentifier(dLDbIdentifier
);
162 it
= mKeychainMap
.find(munge_dldbi
);
163 if (it
!= mKeychainMap
.end()) {
167 // If we have a keychain object for the un/demunged keychain, return that.
168 // We might be in the middle of an upgrade, where the -db file exists as a bit-perfect copy of the original file.
169 DLDbIdentifier demunge_dldbi
= demungeDLDbIdentifier(dLDbIdentifier
);
170 it
= mKeychainMap
.find(demunge_dldbi
);
171 if (it
!= mKeychainMap
.end()) {
175 // Okay, we haven't seen this keychain before. Do the full process...
176 DLDbIdentifier dldbi
= mungeDLDbIdentifier(dLDbIdentifier
, false);
177 it
= mKeychainMap
.find(dldbi
); // Almost certain not to find it here
178 if (it
!= mKeychainMap
.end())
184 secnotice("servermode", "keychain reference in server mode");
188 // The keychain is not in our cache. Create it.
189 Db
db(makeDb(dldbi
));
191 Keychain
keychain(db
);
192 // Add the keychain to the cache.
193 registerKeychain(keychain
);
198 // Note: this must be a munged DLDbidentifier.
200 StorageManager::makeDb(DLDbIdentifier dLDbIdentifier
) {
201 Module
module(dLDbIdentifier
.ssuid().guid());
204 if (dLDbIdentifier
.ssuid().subserviceType() & CSSM_SERVICE_CSP
)
205 dl
= SSCSPDL(module);
209 dl
->subserviceId(dLDbIdentifier
.ssuid().subserviceId());
210 dl
->version(dLDbIdentifier
.ssuid().version());
212 CssmClient::Db
db(dl
, dLDbIdentifier
.dbName());
217 // StorageManager is responsible for silently switching to newer-style keychains.
218 // If the keychain requested is in ~/Library/Keychains/, and there is a
219 // newer keychain available (with extension ".keychain-db"), open that one
220 // instead of the one requested.
222 // Because of backwards compatibility reasons, we can't update the plist
223 // files on disk to point to the upgraded keychains. We will be asked to
224 // load "/Users/account/Library/Keychains/login.keychain", hence this
225 // modification to 'login.keychain-db'.
227 StorageManager::mungeDLDbIdentifier(const DLDbIdentifier
& dLDbIdentifier
, bool isReset
) {
228 if(!dLDbIdentifier
.dbName()) {
229 // If this DLDbIdentifier doesn't have a filename, don't munge it
230 return dLDbIdentifier
;
233 string path
= dLDbIdentifier
.dbName();
235 bool shouldCreateProtected
= globals().integrityProtection();
237 // If we don't have a DLDbIdentifier, we can't return one
238 if(dLDbIdentifier
.mImpl
== NULL
) {
239 return DLDbIdentifier();
242 // Ensure we're in ~/Library/Keychains
243 if(pathInHomeLibraryKeychains(path
)) {
244 string pathdb
= makeKeychainDbFilename(path
);
248 int path_stat_err
= 0;
249 bool path_exists
= (::stat(path
.c_str(), &st
) == 0);
251 path_stat_err
= errno
;
254 int pathdb_stat_err
= 0;
255 bool pathdb_exists
= (::stat(pathdb
.c_str(), &st
) == 0);
257 pathdb_stat_err
= errno
;
260 // If protections are off, don't change the requested filename.
261 // If protictions are on and the -db file exists, always use it.
263 // If we're resetting, and we're creating a new-style keychain, use the -db path.
264 // If we're resetting, and we're creating an old-style keychain, use the original path.
266 // Protection pathdb_exists path_exists resetting Result
267 // DISABLED X X X original
270 // ENABLED 0 1 0 original
273 bool switchPaths
= shouldCreateProtected
&& (pathdb_exists
|| (!pathdb_exists
&& !path_exists
) || isReset
);
276 secinfo("integrity", "switching to keychain-db: %s from %s (%d %d %d_%d %d_%d)", pathdb
.c_str(), path
.c_str(), isReset
, shouldCreateProtected
, path_exists
, path_stat_err
, pathdb_exists
, pathdb_stat_err
);
279 secinfo("integrity", "not switching: %s from %s (%d %d %d_%d %d_%d)", pathdb
.c_str(), path
.c_str(), isReset
, shouldCreateProtected
, path_exists
, path_stat_err
, pathdb_exists
, pathdb_stat_err
);
283 DLDbIdentifier
id(dLDbIdentifier
.ssuid(), path
.c_str(), dLDbIdentifier
.dbLocation());
288 StorageManager::forceMungeDLDbIDentifier(const DLDbIdentifier
& dLDbIdentifier
) {
289 if(!dLDbIdentifier
.dbName() || dLDbIdentifier
.mImpl
== NULL
) {
290 return dLDbIdentifier
;
293 string path
= dLDbIdentifier
.dbName();
294 string pathdb
= makeKeychainDbFilename(path
);
296 DLDbIdentifier
id(dLDbIdentifier
.ssuid(), pathdb
.c_str(), dLDbIdentifier
.dbLocation());
301 StorageManager::demungeDLDbIdentifier(const DLDbIdentifier
& dLDbIdentifier
) {
302 if(dLDbIdentifier
.dbName() == NULL
) {
303 return dLDbIdentifier
;
306 string path
= dLDbIdentifier
.dbName();
307 string dbSuffix
= "-db";
308 bool endsWithKeychainDb
= (path
.size() > dbSuffix
.size() && (0 == path
.compare(path
.size() - dbSuffix
.size(), dbSuffix
.size(), dbSuffix
)));
310 // Ensure we're in ~/Library/Keychains, and that the path ends in "-db"
311 if(pathInHomeLibraryKeychains(path
) && endsWithKeychainDb
) {
312 // remove "-db" from the end.
313 path
.erase(path
.end() - 3, path
.end());
316 DLDbIdentifier
id(dLDbIdentifier
.ssuid(), path
.c_str(), dLDbIdentifier
.dbLocation());
321 StorageManager::makeKeychainDbFilename(const string
& filename
) {
322 string keychainDbSuffix
= "-db";
323 bool endsWithKeychainDb
= (filename
.size() > keychainDbSuffix
.size() && (0 == filename
.compare(filename
.size() - keychainDbSuffix
.size(), keychainDbSuffix
.size(), keychainDbSuffix
)));
325 if(endsWithKeychainDb
) {
328 return filename
+ keychainDbSuffix
;
333 StorageManager::pathInHomeLibraryKeychains(const string
& path
) {
334 return SecurityServer::CommonBlob::pathInHomeLibraryKeychains(path
);
338 StorageManager::reloadKeychain(Keychain keychain
) {
339 StLock
<Mutex
>_(mKeychainMapMutex
);
341 DLDbIdentifier dLDbIdentifier
= keychain
->database()->dlDbIdentifier();
343 keychain
->changeDatabase(makeDb(mungeDLDbIdentifier(dLDbIdentifier
, false)));
345 // This keychain might have a different dldbidentifier now, depending on what
346 // other processes have been doing to the keychain files. Let's re-register it, just
348 registerKeychain(keychain
);
352 StorageManager::removeKeychain(const DLDbIdentifier
&dLDbIdentifier
,
353 KeychainImpl
*keychainImpl
)
355 StLock
<Mutex
>_(mKeychainMapMutex
);
357 // Don't trust this dldbidentifier. Just look for the keychain and delete it.
358 forceRemoveFromCache(keychainImpl
);
362 StorageManager::didRemoveKeychain(const DLDbIdentifier
&dLDbIdentifier
)
364 // Lock the recursive mutex
366 StLock
<Mutex
>_(mKeychainMapMutex
);
368 KeychainMap::iterator it
= mKeychainMap
.find(dLDbIdentifier
);
369 if (it
!= mKeychainMap
.end())
371 it
->second
->inCache(false);
372 mKeychainMap
.erase(it
);
376 // If the client does not keep references to keychains, they are destroyed on
377 // every API exit, and recreated on every API entrance.
379 // To improve performance, we'll cache keychains for some short period of time.
380 // We'll do this by CFRetaining the keychain object, and setting a timer to
381 // CFRelease it when time's up. This way, the client can still recover all its
382 // memory if it doesn't want the keychains around, but repeated API calls will
383 // be significantly faster.
386 StorageManager::tickleKeychain(KeychainImpl
*keychainImpl
) {
387 static dispatch_once_t onceToken
= 0;
388 static dispatch_queue_t release_queue
= NULL
;
389 dispatch_once(&onceToken
, ^{
390 release_queue
= dispatch_queue_create("com.apple.security.keychain-cache-queue", DISPATCH_QUEUE_SERIAL
);
393 __block KeychainImpl
* kcImpl
= keychainImpl
;
399 // We really only want to cache CSPDL file-based keychains
400 if(kcImpl
->dlDbIdentifier().ssuid().guid() != gGuidAppleCSPDL
) {
404 // Make a one-shot timer to release the keychain
405 uint32_t seconds
= 1;
407 const string path
= kcImpl
->name();
408 bool isSystemKeychain
= (0 == path
.compare("/Library/Keychains/System.keychain"));
409 if(pathInHomeLibraryKeychains(path
) || isSystemKeychain
) {
410 // These keychains are important and likely aren't on removable media.
411 // Cache them longer.
415 __block CFTypeRef kcHandle
= kcImpl
->handle(); // calls retain; this keychain object will stay around until our dispatch block fires.
417 // You _must not_ call CFRelease while on this queue, due to the locking order mishmash. CFRelease takes a lock, so remember to do it later.
418 __block
bool releaseImmediately
= false;
420 dispatch_sync(release_queue
, ^() {
421 if(kcImpl
->mCacheTimer
) {
422 // Update the cache timer to be seconds from now
423 dispatch_source_set_timer(kcImpl
->mCacheTimer
, dispatch_time(DISPATCH_TIME_NOW
, seconds
* NSEC_PER_SEC
), DISPATCH_TIME_FOREVER
, NSEC_PER_SEC
/2);
424 secdebug("keychain", "updating cache on %p %s", kcImpl
, kcImpl
->name());
426 // We've added an extra retain to this keychain right before invoking this block. Remember to release it.
427 releaseImmediately
= true;
429 // No cache timer; make one.
430 kcImpl
->mCacheTimer
= dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER
, 0, 0, release_queue
);
431 dispatch_source_set_timer(kcImpl
->mCacheTimer
, dispatch_time(DISPATCH_TIME_NOW
, seconds
* NSEC_PER_SEC
), DISPATCH_TIME_FOREVER
, NSEC_PER_SEC
/2);
432 secdebug("keychain", "taking cache on %p %s", kcImpl
, kcImpl
->name());
434 dispatch_source_set_event_handler(kcImpl
->mCacheTimer
, ^{
435 secdebug("keychain", "releasing cache on %p %s", kcImpl
, kcImpl
->name());
436 dispatch_source_cancel(kcImpl
->mCacheTimer
);
437 dispatch_release(kcImpl
->mCacheTimer
);
438 kcImpl
->mCacheTimer
= NULL
;
440 // Since we're on the timer queue, we can't call CFRelease on the kcHandle (since that takes a lock). Dispatch_async it over to some other queue...
441 // This is better than using dispatch_async on the timer queue initially, since it's less dispatch_asyncs overall, even though it's more confusing.
442 dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND
,0), ^{
447 dispatch_resume(kcImpl
->mCacheTimer
);
451 if(releaseImmediately
) {
456 // Create keychain if it doesn't exist, and optionally add it to the search list.
458 StorageManager::makeKeychain(const DLDbIdentifier
&dLDbIdentifier
, bool add
, bool isReset
)
460 StLock
<Mutex
>_(mKeychainMapMutex
);
462 Keychain theKeychain
= keychain(mungeDLDbIdentifier(dLDbIdentifier
, isReset
));
464 bool updateList
= (add
&& shouldAddToSearchList(dLDbIdentifier
));
468 mSavedList
.revert(false);
469 DLDbList searchList
= mSavedList
.searchList();
470 if (find(searchList
.begin(), searchList
.end(), demungeDLDbIdentifier(dLDbIdentifier
)) != searchList
.end())
471 return theKeychain
; // theKeychain is already in the searchList.
473 mCommonList
.revert(false);
474 searchList
= mCommonList
.searchList();
475 if (find(searchList
.begin(), searchList
.end(), demungeDLDbIdentifier(dLDbIdentifier
)) != searchList
.end())
476 return theKeychain
; // theKeychain is already in the commonList don't add it to the searchList.
478 // If theKeychain doesn't exist don't bother adding it to the search list yet.
479 if (!theKeychain
->exists())
482 // theKeychain exists and is not in our search list, so add it to the
484 mSavedList
.revert(true);
485 mSavedList
.add(demungeDLDbIdentifier(dLDbIdentifier
));
492 // Make sure we are not holding mStorageManagerLock anymore when we
494 KCEventNotifier::PostKeychainEvent(kSecKeychainListChangedEvent
);
500 // Be notified a Keychain just got created.
502 StorageManager::created(const Keychain
&keychain
)
504 StLock
<Mutex
>_(mKeychainMapMutex
);
506 DLDbIdentifier dLDbIdentifier
= keychain
->dlDbIdentifier();
507 bool defaultChanged
= false;
508 bool updateList
= shouldAddToSearchList(dLDbIdentifier
);
512 mSavedList
.revert(true);
513 // If we don't have a default Keychain yet. Make the newly created
514 // keychain the default.
515 if (!mSavedList
.defaultDLDbIdentifier())
517 mSavedList
.defaultDLDbIdentifier(demungeDLDbIdentifier(dLDbIdentifier
));
518 defaultChanged
= true;
521 // Add the keychain to the search list prefs.
522 mSavedList
.add(demungeDLDbIdentifier(dLDbIdentifier
));
525 // Make sure we are not holding mLock when we post these events.
526 KCEventNotifier::PostKeychainEvent(kSecKeychainListChangedEvent
);
531 KCEventNotifier::PostKeychainEvent(kSecDefaultChangedEvent
, dLDbIdentifier
);
536 StorageManager::createCursor(SecItemClass itemClass
,
537 const SecKeychainAttributeList
*attrList
)
539 StLock
<Mutex
>_(mMutex
);
541 KeychainList searchList
;
542 getSearchList(searchList
);
543 return KCCursor(searchList
, itemClass
, attrList
);
547 StorageManager::createCursor(const SecKeychainAttributeList
*attrList
)
549 StLock
<Mutex
>_(mMutex
);
551 KeychainList searchList
;
552 getSearchList(searchList
);
553 return KCCursor(searchList
, attrList
);
557 StorageManager::lockAll()
559 StLock
<Mutex
>_(mMutex
);
561 SecurityServer::ClientSession
ss(Allocator::standard(), Allocator::standard());
566 StorageManager::defaultKeychain()
568 StLock
<Mutex
>_(mMutex
);
570 Keychain theKeychain
;
574 mSavedList
.revert(false);
575 DLDbIdentifier
defaultDLDbIdentifier(mSavedList
.defaultDLDbIdentifier());
576 if (defaultDLDbIdentifier
)
578 theKeychain
= keychain(defaultDLDbIdentifier
);
579 ref
= theKeychain
->handle(false);
583 if (theKeychain
/* && theKeychain->exists() */)
586 MacOSError::throwMe(errSecNoDefaultKeychain
);
590 StorageManager::defaultKeychain(const Keychain
&keychain
)
592 StLock
<Mutex
>_(mMutex
);
594 // Only set a keychain as the default if we own it and can read/write it,
595 // and our uid allows modifying the directory for that preference domain.
596 if (!keychainOwnerPermissionsValidForDomain(keychain
->name(), mDomain
))
597 MacOSError::throwMe(errSecWrPerm
);
599 DLDbIdentifier oldDefaultId
;
600 DLDbIdentifier
newDefaultId(keychain
->dlDbIdentifier());
602 oldDefaultId
= mSavedList
.defaultDLDbIdentifier();
603 mSavedList
.revert(true);
604 mSavedList
.defaultDLDbIdentifier(demungeDLDbIdentifier(newDefaultId
));
608 if (!(oldDefaultId
== newDefaultId
))
610 // Make sure we are not holding mLock when we post this event.
611 KCEventNotifier::PostKeychainEvent(kSecDefaultChangedEvent
, newDefaultId
);
616 StorageManager::defaultKeychain(SecPreferencesDomain domain
)
618 StLock
<Mutex
>_(mMutex
);
620 if (domain
== kSecPreferencesDomainDynamic
)
621 MacOSError::throwMe(errSecInvalidPrefsDomain
);
623 if (domain
== mDomain
)
624 return defaultKeychain();
627 DLDbIdentifier
defaultDLDbIdentifier(DLDbListCFPref(domain
).defaultDLDbIdentifier());
628 if (defaultDLDbIdentifier
)
629 return keychain(defaultDLDbIdentifier
);
631 MacOSError::throwMe(errSecNoDefaultKeychain
);
636 StorageManager::defaultKeychain(SecPreferencesDomain domain
, const Keychain
&keychain
)
638 StLock
<Mutex
>_(mMutex
);
640 if (domain
== kSecPreferencesDomainDynamic
)
641 MacOSError::throwMe(errSecInvalidPrefsDomain
);
643 if (domain
== mDomain
)
644 defaultKeychain(keychain
);
646 DLDbListCFPref(domain
).defaultDLDbIdentifier(keychain
->dlDbIdentifier());
650 StorageManager::loginKeychain()
652 StLock
<Mutex
>_(mMutex
);
654 Keychain theKeychain
;
656 mSavedList
.revert(false);
657 DLDbIdentifier
loginDLDbIdentifier(mSavedList
.loginDLDbIdentifier());
658 if (loginDLDbIdentifier
)
660 theKeychain
= keychain(loginDLDbIdentifier
);
664 if (theKeychain
&& theKeychain
->exists())
667 MacOSError::throwMe(errSecNoSuchKeychain
);
671 StorageManager::loginKeychainDLDbIdentifer()
673 StLock
<Mutex
>_(mMutex
);
674 DLDbIdentifier
loginDLDbIdentifier(mSavedList
.loginDLDbIdentifier());
675 return mungeDLDbIdentifier(loginDLDbIdentifier
, false);
679 StorageManager::loginKeychain(Keychain keychain
)
681 StLock
<Mutex
>_(mMutex
);
683 mSavedList
.revert(true);
684 mSavedList
.loginDLDbIdentifier(demungeDLDbIdentifier(keychain
->dlDbIdentifier()));
689 StorageManager::size()
691 StLock
<Mutex
>_(mMutex
);
693 mSavedList
.revert(false);
694 mCommonList
.revert(false);
695 return mSavedList
.searchList().size() + mCommonList
.searchList().size();
699 StorageManager::at(unsigned int ix
)
701 StLock
<Mutex
>_(mMutex
);
703 mSavedList
.revert(false);
704 DLDbList dLDbList
= mSavedList
.searchList();
705 if (ix
< dLDbList
.size())
707 return keychain(dLDbList
[ix
]);
711 ix
-= dLDbList
.size();
712 mCommonList
.revert(false);
713 DLDbList commonList
= mCommonList
.searchList();
714 if (ix
>= commonList
.size())
715 MacOSError::throwMe(errSecInvalidKeychain
);
717 return keychain(commonList
[ix
]);
722 StorageManager::operator[](unsigned int ix
)
724 StLock
<Mutex
>_(mMutex
);
729 void StorageManager::rename(Keychain keychain
, const char* newName
)
732 StLock
<Mutex
>_(mKeychainMapMutex
);
734 bool changedDefault
= false;
735 DLDbIdentifier newDLDbIdentifier
;
737 mSavedList
.revert(true);
738 DLDbIdentifier defaultId
= mSavedList
.defaultDLDbIdentifier();
740 // Find the keychain object for the given ref
741 DLDbIdentifier dLDbIdentifier
= keychain
->dlDbIdentifier();
743 if(!keychain
->database()->isLocked()) {
744 // Bring our unlock state with us
745 DLDbIdentifier
dldbi(dLDbIdentifier
.ssuid(), newName
, dLDbIdentifier
.dbLocation());
746 keychain
->database()->transferTo(dldbi
);
748 keychain
->database()->rename(newName
);
751 if (demungeDLDbIdentifier(dLDbIdentifier
) == defaultId
)
754 newDLDbIdentifier
= keychain
->dlDbIdentifier();
755 // Rename the keychain in the search list.
756 mSavedList
.rename(demungeDLDbIdentifier(dLDbIdentifier
), demungeDLDbIdentifier(newDLDbIdentifier
));
758 // If this was the default keychain change it accordingly
760 mSavedList
.defaultDLDbIdentifier(demungeDLDbIdentifier(newDLDbIdentifier
));
764 // If the keychain wasn't in the cache, don't touch the cache.
765 // Otherwise, update the cache to use its current identifier.
766 if(keychain
->inCache()) {
767 registerKeychain(keychain
);
771 KCEventNotifier::PostKeychainEvent(kSecKeychainListChangedEvent
);
774 KCEventNotifier::PostKeychainEvent(kSecDefaultChangedEvent
,
778 void StorageManager::registerKeychain(Keychain
& kc
) {
779 registerKeychainImpl(kc
.get());
782 void StorageManager::registerKeychainImpl(KeychainImpl
* kcimpl
) {
788 StLock
<Mutex
> _(mKeychainMapMutex
);
790 // First, iterate through the cache to see if this keychain is there. If so, remove it.
791 forceRemoveFromCache(kcimpl
);
793 // If we renamed this keychain on top of an existing one, let's drop the old one from the cache.
794 KeychainMap::iterator it
= mKeychainMap
.find(kcimpl
->dlDbIdentifier());
795 if (it
!= mKeychainMap
.end())
797 Keychain
oldKeychain(it
->second
);
798 oldKeychain
->inCache(false);
799 // @@@ Ideally we should invalidate or fault this keychain object.
802 mKeychainMap
.insert(KeychainMap::value_type(kcimpl
->dlDbIdentifier(), kcimpl
));
803 kcimpl
->inCache(true);
804 } // drop mKeychainMapMutex
807 void StorageManager::forceRemoveFromCache(KeychainImpl
* inKeychainImpl
) {
809 // Wrap all this in a try-block and ignore all errors - we're trying to clean up these maps
811 StLock
<Mutex
> _(mKeychainMapMutex
);
812 for(KeychainMap::iterator it
= mKeychainMap
.begin(); it
!= mKeychainMap
.end(); ) {
813 if(it
->second
== inKeychainImpl
) {
814 // Increment the iterator, but use its pre-increment value for the erase
815 it
->second
->inCache(false);
816 mKeychainMap
.erase(it
++);
821 } // drop mKeychainMapMutex
822 } catch(UnixError ue
) {
823 secnotice("storagemgr", "caught UnixError: %d %s", ue
.unixError(), ue
.what());
824 } catch (CssmError cssme
) {
825 const char* errStr
= cssmErrorString(cssme
.error
);
826 secnotice("storagemgr", "caught CssmError: %d %s", (int) cssme
.error
, errStr
);
827 } catch (MacOSError mose
) {
828 secnotice("storagemgr", "MacOSError: %d", (int)mose
.osStatus());
830 secnotice("storagemgr", "Unknown error");
834 // If you pass NULL as the keychain, you must pass an oldName.
835 void StorageManager::renameUnique(Keychain keychain
, CFStringRef oldName
, CFStringRef newName
, bool appendDbSuffix
)
837 StLock
<Mutex
>_(mMutex
);
839 bool doneCreating
= false;
843 char newNameCString
[MAXPATHLEN
];
844 if ( CFStringGetCString(newName
, newNameCString
, MAXPATHLEN
, kCFStringEncodingUTF8
) ) // make sure it fits in MAXPATHLEN, etc.
846 // Construct the new name...
848 CFMutableStringRef newNameCFStr
= NULL
;
849 newNameCFStr
= CFStringCreateMutable(NULL
, MAXPATHLEN
);
852 CFStringAppendFormat(newNameCFStr
, NULL
, CFSTR("%s%d"), newNameCString
, index
);
854 CFStringAppend(newNameCFStr
, CFSTR(kKeychainDbSuffix
));
856 CFStringAppend(newNameCFStr
, CFSTR(kKeychainSuffix
)); // add .keychain
858 char toUseBuff2
[MAXPATHLEN
];
859 if ( CFStringGetCString(newNameCFStr
, toUseBuff2
, MAXPATHLEN
, kCFStringEncodingUTF8
) ) // make sure it fits in MAXPATHLEN, etc.
862 if ( lstat(toUseBuff2
, &filebuf
) )
865 rename(keychain
, toUseBuff2
);
867 kcList
.push_back(keychain
);
868 remove(kcList
, false);
870 // We don't have a Keychain object, so force the rename here if possible
871 char oldNameCString
[MAXPATHLEN
];
872 if ( CFStringGetCString(oldName
, oldNameCString
, MAXPATHLEN
, kCFStringEncodingUTF8
) ) {
873 int result
= ::rename(oldNameCString
, toUseBuff2
);
874 secnotice("KClogin", "keychain force rename to %s: %d %d", newNameCString
, result
, (result
== 0) ? 0 : errno
);
876 UnixError::throwMe(errno
);
879 secnotice("KClogin", "path is wrong, quitting");
888 doneCreating
= true; // failure to get c string.
889 CFRelease(newNameCFStr
);
892 doneCreating
= false; // failure to create mutable string.
895 doneCreating
= false; // failure to get the string (i.e. > MAXPATHLEN?)
897 while (!doneCreating
&& index
!= INT_MAX
);
900 #define KEYCHAIN_SYNC_KEY CFSTR("KeychainSyncList")
901 #define KEYCHAIN_SYNC_DOMAIN CFSTR("com.apple.keychainsync")
903 static CFStringRef
MakeExpandedPath (const char* path
)
905 std::string name
= DLDbListCFPref::ExpandTildesInPath (std::string (path
));
906 CFStringRef expanded
= CFStringCreateWithCString (NULL
, name
.c_str (), 0);
910 void StorageManager::removeKeychainFromSyncList (const DLDbIdentifier
&id
)
912 StLock
<Mutex
>_(mMutex
);
914 // make a CFString of our identifier
915 const char* idname
= id
.dbName ();
921 CFRef
<CFStringRef
> idString
= MakeExpandedPath (idname
);
923 // check and see if this keychain is in the keychain syncing list
925 (CFArrayRef
) CFPreferencesCopyValue (KEYCHAIN_SYNC_KEY
,
926 KEYCHAIN_SYNC_DOMAIN
,
927 kCFPreferencesCurrentUser
,
928 kCFPreferencesAnyHost
);
934 // make a mutable copy of the dictionary
935 CFRef
<CFMutableArrayRef
> mtValue
= CFArrayCreateMutableCopy (NULL
, 0, value
);
938 // walk the array, looking for the value
940 CFIndex limit
= CFArrayGetCount (mtValue
.get());
943 for (i
= 0; i
< limit
; ++i
)
945 CFDictionaryRef idx
= (CFDictionaryRef
) CFArrayGetValueAtIndex (mtValue
.get(), i
);
946 CFStringRef v
= (CFStringRef
) CFDictionaryGetValue (idx
, CFSTR("DbName"));
949 return; // something is really wrong if this is taken
952 char* stringBuffer
= NULL
;
953 const char* pathString
= CFStringGetCStringPtr(v
, 0);
956 CFIndex maxLen
= CFStringGetMaximumSizeForEncoding(CFStringGetLength(v
), kCFStringEncodingUTF8
) + 1;
957 stringBuffer
= (char*) malloc(maxLen
);
958 CFStringGetCString(v
, stringBuffer
, maxLen
, kCFStringEncodingUTF8
);
959 pathString
= stringBuffer
;
962 CFStringRef vExpanded
= MakeExpandedPath(pathString
);
963 CFComparisonResult result
= CFStringCompare (vExpanded
, idString
.get(), 0);
964 if (stringBuffer
!= NULL
)
969 CFRelease (vExpanded
);
973 CFArrayRemoveValueAtIndex (mtValue
.get(), i
);
982 CFShow (mtValue
.get());
985 CFPreferencesSetValue (KEYCHAIN_SYNC_KEY
,
987 KEYCHAIN_SYNC_DOMAIN
,
988 kCFPreferencesCurrentUser
,
989 kCFPreferencesAnyHost
);
990 CFPreferencesSynchronize (KEYCHAIN_SYNC_DOMAIN
, kCFPreferencesCurrentUser
, kCFPreferencesAnyHost
);
994 void StorageManager::remove(const KeychainList
&kcsToRemove
, bool deleteDb
)
996 StLock
<Mutex
>_(mMutex
);
998 bool unsetDefault
= false;
999 bool updateList
= (!isAppSandboxed());
1003 mSavedList
.revert(true);
1004 DLDbIdentifier defaultId
= mSavedList
.defaultDLDbIdentifier();
1005 for (KeychainList::const_iterator ix
= kcsToRemove
.begin();
1006 ix
!= kcsToRemove
.end(); ++ix
)
1008 // Find the keychain object for the given ref
1009 Keychain theKeychain
= *ix
;
1010 DLDbIdentifier dLDbIdentifier
= theKeychain
->dlDbIdentifier();
1012 // Remove it from the saved list
1013 mSavedList
.remove(demungeDLDbIdentifier(dLDbIdentifier
));
1014 if (demungeDLDbIdentifier(dLDbIdentifier
) == defaultId
) {
1020 removeKeychainFromSyncList (dLDbIdentifier
);
1022 // Now remove it from the cache
1023 removeKeychain(dLDbIdentifier
, theKeychain
.get());
1028 mSavedList
.defaultDLDbIdentifier(DLDbIdentifier());
1035 // Delete the actual databases without holding any locks.
1036 for (KeychainList::const_iterator ix
= kcsToRemove
.begin();
1037 ix
!= kcsToRemove
.end(); ++ix
)
1039 (*ix
)->database()->deleteDb();
1044 // Make sure we are not holding mLock when we post these events.
1045 KCEventNotifier::PostKeychainEvent(kSecKeychainListChangedEvent
);
1049 KCEventNotifier::PostKeychainEvent(kSecDefaultChangedEvent
);
1053 StorageManager::getSearchList(KeychainList
&keychainList
)
1055 // hold the global lock since we make keychain objects in this function
1057 // to do: each of the items in this list must be retained, otherwise mayhem will occur
1058 StLock
<Mutex
>_(mMutex
);
1061 keychainList
.clear();
1065 mSavedList
.revert(false);
1066 mCommonList
.revert(false);
1068 // Merge mSavedList, mDynamicList and mCommonList
1069 DLDbList dLDbList
= mSavedList
.searchList();
1070 DLDbList dynamicList
= mDynamicList
.searchList();
1071 DLDbList commonList
= mCommonList
.searchList();
1072 KeychainList result
;
1073 result
.reserve(dLDbList
.size() + dynamicList
.size() + commonList
.size());
1076 for (DLDbList::const_iterator it
= dynamicList
.begin();
1077 it
!= dynamicList
.end(); ++it
)
1079 Keychain k
= keychain(*it
);
1080 result
.push_back(k
);
1083 for (DLDbList::const_iterator it
= dLDbList
.begin();
1084 it
!= dLDbList
.end(); ++it
)
1086 Keychain k
= keychain(*it
);
1087 result
.push_back(k
);
1090 for (DLDbList::const_iterator it
= commonList
.begin();
1091 it
!= commonList
.end(); ++it
)
1093 Keychain k
= keychain(*it
);
1094 result
.push_back(k
);
1098 keychainList
.swap(result
);
1102 StorageManager::setSearchList(const KeychainList
&keychainList
)
1104 StLock
<Mutex
>_(mMutex
);
1106 DLDbList searchList
, oldSearchList(mSavedList
.searchList());
1107 for (KeychainList::const_iterator it
= keychainList
.begin(); it
!= keychainList
.end(); ++it
)
1109 DLDbIdentifier dldbi
= demungeDLDbIdentifier((*it
)->dlDbIdentifier());
1111 // If this keychain is not in the common or dynamic lists, add it to the new search list
1112 DLDbList commonList
= mCommonList
.searchList();
1114 for(DLDbList::const_iterator jt
= commonList
.begin(); jt
!= commonList
.end(); ++jt
) {
1115 if((*jt
) == dldbi
) {
1120 DLDbList dynamicList
= mDynamicList
.searchList();
1121 for(DLDbList::const_iterator jt
= dynamicList
.begin(); jt
!= dynamicList
.end(); ++jt
) {
1122 if((*jt
) == dldbi
) {
1131 searchList
.push_back(dldbi
);
1135 // Set the current searchlist to be what was passed in, the old list will be freed
1136 // upon exit of this stackframe.
1137 mSavedList
.revert(true);
1138 mSavedList
.searchList(searchList
);
1142 if (!(oldSearchList
== searchList
))
1144 // Make sure we are not holding mLock when we post this event.
1145 KCEventNotifier::PostKeychainEvent(kSecKeychainListChangedEvent
);
1150 StorageManager::getSearchList(SecPreferencesDomain domain
, KeychainList
&keychainList
)
1152 StLock
<Mutex
>_(mMutex
);
1155 keychainList
.clear();
1159 if (domain
== kSecPreferencesDomainDynamic
)
1161 convertList(keychainList
, mDynamicList
.searchList());
1163 else if (domain
== mDomain
)
1165 mSavedList
.revert(false);
1166 convertList(keychainList
, mSavedList
.searchList());
1170 convertList(keychainList
, DLDbListCFPref(domain
).searchList());
1174 void StorageManager::forceUserSearchListReread()
1176 mSavedList
.forceUserSearchListReread();
1180 StorageManager::setSearchList(SecPreferencesDomain domain
, const KeychainList
&keychainList
)
1182 StLock
<Mutex
>_(mMutex
);
1184 if (domain
== kSecPreferencesDomainDynamic
)
1185 MacOSError::throwMe(errSecInvalidPrefsDomain
);
1187 DLDbList searchList
;
1188 convertList(searchList
, keychainList
);
1190 if (domain
== mDomain
)
1192 DLDbList
oldSearchList(mSavedList
.searchList());
1194 // Set the current searchlist to be what was passed in, the old list will be freed
1195 // upon exit of this stackframe.
1196 mSavedList
.revert(true);
1197 mSavedList
.searchList(searchList
);
1201 if (!(oldSearchList
== searchList
))
1203 KCEventNotifier::PostKeychainEvent(kSecKeychainListChangedEvent
);
1208 DLDbListCFPref(domain
).searchList(searchList
);
1213 StorageManager::domain(SecPreferencesDomain domain
)
1215 StLock
<Mutex
>_(mMutex
);
1217 if (domain
== kSecPreferencesDomainDynamic
)
1218 MacOSError::throwMe(errSecInvalidPrefsDomain
);
1220 if (domain
== mDomain
)
1221 return; // no change
1223 #if !defined(NDEBUG)
1226 case kSecPreferencesDomainSystem
:
1227 secnotice("storagemgr", "switching to system domain"); break;
1228 case kSecPreferencesDomainUser
:
1229 secnotice("storagemgr", "switching to user domain (uid %d)", getuid()); break;
1231 secnotice("storagemgr", "switching to weird prefs domain %d", domain
); break;
1236 mSavedList
.set(domain
);
1240 StorageManager::optionalSearchList(CFTypeRef keychainOrArray
, KeychainList
&keychainList
)
1242 StLock
<Mutex
>_(mMutex
);
1244 if (!keychainOrArray
)
1245 getSearchList(keychainList
);
1248 CFTypeID typeID
= CFGetTypeID(keychainOrArray
);
1249 if (typeID
== CFArrayGetTypeID())
1250 convertToKeychainList(CFArrayRef(keychainOrArray
), keychainList
);
1251 else if (typeID
== gTypes().KeychainImpl
.typeID
)
1252 keychainList
.push_back(KeychainImpl::required(SecKeychainRef(keychainOrArray
)));
1254 MacOSError::throwMe(errSecParam
);
1260 StorageManager::convertToKeychainList(CFArrayRef keychainArray
, KeychainList
&keychainList
)
1262 CFIndex count
= CFArrayGetCount(keychainArray
);
1266 KeychainList
keychains(count
);
1267 for (CFIndex ix
= 0; ix
< count
; ++ix
)
1269 keychains
[ix
] = KeychainImpl::required(SecKeychainRef(CFArrayGetValueAtIndex(keychainArray
, ix
)));
1272 keychainList
.swap(keychains
);
1276 StorageManager::convertFromKeychainList(const KeychainList
&keychainList
)
1278 CFRef
<CFMutableArrayRef
> keychainArray(CFArrayCreateMutable(NULL
, keychainList
.size(), &kCFTypeArrayCallBacks
));
1280 for (KeychainList::const_iterator ix
= keychainList
.begin(); ix
!= keychainList
.end(); ++ix
)
1282 SecKeychainRef keychainRef
= (*ix
)->handle();
1283 CFArrayAppendValue(keychainArray
, keychainRef
);
1284 CFRelease(keychainRef
);
1287 // Counter the CFRelease that CFRef<> is about to do when keychainArray goes out of scope.
1288 CFRetain(keychainArray
);
1289 return keychainArray
;
1292 void StorageManager::convertList(DLDbList
&ids
, const KeychainList
&kcs
)
1295 result
.reserve(kcs
.size());
1296 for (KeychainList::const_iterator ix
= kcs
.begin(); ix
!= kcs
.end(); ++ix
)
1298 result
.push_back(demungeDLDbIdentifier((*ix
)->dlDbIdentifier()));
1303 void StorageManager::convertList(KeychainList
&kcs
, const DLDbList
&ids
)
1305 StLock
<Mutex
>_(mMutex
);
1307 KeychainList result
;
1308 result
.reserve(ids
.size());
1310 for (DLDbList::const_iterator ix
= ids
.begin(); ix
!= ids
.end(); ++ix
)
1311 result
.push_back(keychain(*ix
));
1316 #pragma mark ____ Login Functions ____
1318 void StorageManager::login(AuthorizationRef authRef
, UInt32 nameLength
, const char* name
, bool isReset
)
1320 StLock
<Mutex
>_(mMutex
);
1322 AuthorizationItemSet
* info
= NULL
;
1323 OSStatus result
= AuthorizationCopyInfo(authRef
, NULL
, &info
); // get the results of the copy rights call.
1324 Boolean created
= false;
1325 if ( result
== errSecSuccess
&& info
->count
)
1327 // Grab the password from the auth context (info) and create the keychain...
1329 AuthorizationItem
* currItem
= info
->items
;
1330 for (UInt32 index
= 1; index
<= info
->count
; index
++) //@@@plugin bug won't return a specific context.
1332 if (strcmp(currItem
->name
, kAuthorizationEnvironmentPassword
) == 0)
1334 // creates the login keychain with the specified password
1337 login(nameLength
, name
, (UInt32
)currItem
->valueLength
, currItem
->value
, isReset
);
1349 AuthorizationFreeItemSet(info
);
1352 MacOSError::throwMe(errAuthorizationInternal
);
1355 void StorageManager::login(ConstStringPtr name
, ConstStringPtr password
)
1357 StLock
<Mutex
>_(mMutex
);
1359 if ( name
== NULL
|| password
== NULL
) {
1360 MacOSError::throwMe(errSecParam
);
1363 login(name
[0], name
+ 1, password
[0], password
+ 1, false);
1366 void StorageManager::login(UInt32 nameLength
, const void *name
,
1367 UInt32 passwordLength
, const void *password
, bool isReset
)
1369 if (passwordLength
!= 0 && password
== NULL
)
1371 secnotice("KCLogin", "StorageManager::login: invalid argument (NULL password)");
1372 MacOSError::throwMe(errSecParam
);
1375 DLDbIdentifier loginDLDbIdentifier
;
1377 mSavedList
.revert(true);
1378 loginDLDbIdentifier
= mSavedList
.loginDLDbIdentifier();
1381 secnotice("KCLogin", "StorageManager::login: loginDLDbIdentifier is %s", (loginDLDbIdentifier
) ? loginDLDbIdentifier
.dbName() : "<NULL>");
1382 if (!loginDLDbIdentifier
)
1383 MacOSError::throwMe(errSecNoSuchKeychain
);
1386 //***************************************************************
1387 // gather keychain information
1388 //***************************************************************
1391 int uid
= geteuid();
1392 struct passwd
*pw
= getpwuid(uid
);
1394 secnotice("KCLogin", "StorageManager::login: invalid argument (NULL uid)");
1395 MacOSError::throwMe(errSecParam
);
1397 std::string userName
= pw
->pw_name
;
1399 // make keychain path strings
1400 std::string keychainPath
= DLDbListCFPref::ExpandTildesInPath(kLoginKeychainPathPrefix
);
1401 std::string shortnameKeychain
= keychainPath
+ userName
;
1402 std::string shortnameDotKeychain
= shortnameKeychain
+ ".keychain";
1403 std::string loginDotKeychain
= keychainPath
+ "login.keychain";
1404 std::string loginRenamed1Keychain
= keychainPath
+ "login_renamed1.keychain";
1405 std::string loginKeychainDb
= keychainPath
+ "login.keychain-db";
1407 // check for existence of keychain files
1408 bool shortnameKeychainExists
= false;
1409 bool shortnameDotKeychainExists
= false;
1410 bool loginKeychainExists
= false;
1411 bool loginRenamed1KeychainExists
= false;
1412 bool loginKeychainDbExists
= false;
1416 stat_result
= ::stat(shortnameKeychain
.c_str(), &st
);
1417 shortnameKeychainExists
= (stat_result
== 0);
1418 stat_result
= ::stat(shortnameDotKeychain
.c_str(), &st
);
1419 shortnameDotKeychainExists
= (stat_result
== 0);
1420 stat_result
= ::stat(loginDotKeychain
.c_str(), &st
);
1421 loginKeychainExists
= (stat_result
== 0);
1422 stat_result
= ::stat(loginRenamed1Keychain
.c_str(), &st
);
1423 loginRenamed1KeychainExists
= (stat_result
== 0);
1424 stat_result
= ::stat(loginKeychainDb
.c_str(), &st
);
1425 loginKeychainDbExists
= (stat_result
== 0);
1428 // login.keychain-db is considered to be the same as login.keychain.
1429 // Our transparent keychain promotion on open will handle opening the right version of this file.
1430 loginKeychainExists
|= loginKeychainDbExists
;
1432 bool loginUnlocked
= false;
1434 // make the keychain identifiers
1435 CSSM_VERSION version
= {0, 0};
1436 DLDbIdentifier shortnameDLDbIdentifier
= DLDbListCFPref::makeDLDbIdentifier(gGuidAppleCSPDL
, version
, 0, CSSM_SERVICE_CSP
| CSSM_SERVICE_DL
, shortnameKeychain
.c_str(), NULL
);
1437 DLDbIdentifier shortnameDotDLDbIdentifier
= DLDbListCFPref::makeDLDbIdentifier(gGuidAppleCSPDL
, version
, 0, CSSM_SERVICE_CSP
| CSSM_SERVICE_DL
, shortnameDotKeychain
.c_str(), NULL
);
1438 DLDbIdentifier loginRenamed1DLDbIdentifier
= DLDbListCFPref::makeDLDbIdentifier(gGuidAppleCSPDL
, version
, 0, CSSM_SERVICE_CSP
| CSSM_SERVICE_DL
, loginRenamed1Keychain
.c_str(), NULL
);
1440 //***************************************************************
1441 // make file renaming changes first
1442 //***************************************************************
1444 // if "~/Library/Keychains/shortname" exists, we need to migrate it forward;
1445 // either to login.keychain if there isn't already one, otherwise to shortname.keychain
1446 if (shortnameKeychainExists
) {
1447 int rename_stat
= 0;
1448 if (loginKeychainExists
) {
1450 int tmp_result
= ::stat(loginDotKeychain
.c_str(), &st
);
1451 if (tmp_result
== 0) {
1452 if (st
.st_size
<= kEmptyKeychainSizeInBytes
) {
1453 tmp_result
= ::unlink(loginDotKeychain
.c_str());
1454 rename_stat
= ::rename(shortnameKeychain
.c_str(), loginDotKeychain
.c_str());
1455 shortnameKeychainExists
= (rename_stat
!= 0);
1459 if (shortnameKeychainExists
) {
1460 if (loginKeychainExists
&& !shortnameDotKeychainExists
) {
1461 rename_stat
= ::rename(shortnameKeychain
.c_str(), shortnameDotKeychain
.c_str());
1462 shortnameDotKeychainExists
= (rename_stat
== 0);
1463 } else if (!loginKeychainExists
) {
1464 rename_stat
= ::rename(shortnameKeychain
.c_str(), loginDotKeychain
.c_str());
1465 loginKeychainExists
= (rename_stat
== 0);
1467 // we have all 3 keychains: login.keychain, shortname, and shortname.keychain.
1468 // on Leopard we never want a shortname keychain, so we must move it aside.
1469 char pathbuf
[MAXPATHLEN
];
1470 std::string shortnameRenamedXXXKeychain
= keychainPath
;
1471 shortnameRenamedXXXKeychain
+= userName
;
1472 shortnameRenamedXXXKeychain
+= "_renamed_XXX.keychain";
1473 ::strlcpy(pathbuf
, shortnameRenamedXXXKeychain
.c_str(), sizeof(pathbuf
));
1474 ::mkstemps(pathbuf
, 9); // 9 == strlen(".keychain")
1475 rename_stat
= ::rename(shortnameKeychain
.c_str(), pathbuf
);
1476 shortnameKeychainExists
= (rename_stat
!= 0);
1479 if (rename_stat
!= 0) {
1480 MacOSError::throwMe(errno
);
1484 //***************************************************************
1485 // handle special case where user previously reset the keychain
1486 //***************************************************************
1487 // Since 9A581, we have changed the definition of kKeychainRenamedSuffix from "_renamed" to "_renamed_".
1488 // Therefore, if "login_renamed1.keychain" exists and there is no plist, the user may have run into a
1489 // prior upgrade issue and clicked Reset. If we can successfully unlock login_renamed1.keychain with the
1490 // supplied password, then we will attempt to rename it to login.keychain if that file is empty, or with
1491 // "shortname.keychain" if it is not.
1493 if (loginRenamed1KeychainExists
&& (!loginKeychainExists
||
1494 (mSavedList
.searchList().size() == 1 && mSavedList
.member(demungeDLDbIdentifier(loginDLDbIdentifier
))) )) {
1497 Keychain
loginRenamed1KC(keychain(loginRenamed1DLDbIdentifier
));
1498 secnotice("KCLogin", "Attempting to unlock renamed KC \"%s\"",
1499 (loginRenamed1KC
) ? loginRenamed1KC
->name() : "<NULL>");
1500 loginRenamed1KC
->unlock(CssmData(const_cast<void *>(password
), passwordLength
));
1501 // if we get here, we unlocked it
1502 if (loginKeychainExists
) {
1504 int tmp_result
= ::stat(loginDotKeychain
.c_str(), &st
);
1505 if (tmp_result
== 0) {
1506 if (st
.st_size
<= kEmptyKeychainSizeInBytes
) {
1507 tmp_result
= ::unlink(loginDotKeychain
.c_str());
1508 tmp_result
= ::rename(loginRenamed1Keychain
.c_str(), loginDotKeychain
.c_str());
1509 } else if (!shortnameDotKeychainExists
) {
1510 tmp_result
= ::rename(loginRenamed1Keychain
.c_str(), shortnameDotKeychain
.c_str());
1511 shortnameDotKeychainExists
= (tmp_result
== 0);
1513 throw 1; // can't do anything with it except move it out of the way
1517 int tmp_result
= ::rename(loginRenamed1Keychain
.c_str(), loginDotKeychain
.c_str());
1518 loginKeychainExists
= (tmp_result
== 0);
1523 // we failed to unlock the login_renamed1.keychain file with the login password.
1524 // move it aside so we don't try to deal with it again.
1525 char pathbuf
[MAXPATHLEN
];
1526 std::string loginRenamedXXXKeychain
= keychainPath
;
1527 loginRenamedXXXKeychain
+= "login_renamed_XXX.keychain";
1528 ::strlcpy(pathbuf
, loginRenamedXXXKeychain
.c_str(), sizeof(pathbuf
));
1529 ::mkstemps(pathbuf
, 9); // 9 == strlen(".keychain")
1530 ::rename(loginRenamed1Keychain
.c_str(), pathbuf
);
1534 // is it token login?
1535 CFRef
<CFDictionaryRef
> tokenLoginContext
;
1536 CFRef
<CFStringRef
> smartCardPassword
;
1537 OSStatus tokenContextStatus
= TokenLoginGetContext(password
, passwordLength
, tokenLoginContext
.take());
1538 // if login.keychain does not exist at this point, create it
1539 if (!loginKeychainExists
|| (isReset
&& !loginKeychainDbExists
)) {
1540 // when we creating new KC and user is logged using token (i.e. smart card), we have to get
1541 // the password for that account first
1542 if (tokenContextStatus
== errSecSuccess
) {
1543 secnotice("KCLogin", "Going to create login keychain for sc login");
1544 AuthorizationRef authRef
;
1545 OSStatus status
= AuthorizationCreate(NULL
, NULL
, 0, &authRef
);
1546 if (status
== errSecSuccess
) {
1547 AuthorizationItem right
= { "com.apple.builtin.sc-kc-new-passphrase", 0, NULL
, 0 };
1548 AuthorizationItemSet rightSet
= { 1, &right
};
1550 uint32_t reason
, tries
;
1553 AuthorizationItem envRights
[] = {
1554 { AGENT_HINT_RETRY_REASON
, sizeof(reason
), &reason
, 0 },
1555 { AGENT_HINT_TRIES
, sizeof(tries
), &tries
, 0 }};
1557 AuthorizationItemSet envSet
= { sizeof(envRights
) / sizeof(*envRights
), envRights
};
1558 status
= AuthorizationCopyRights(authRef
, &rightSet
, &envSet
, kAuthorizationFlagDefaults
|kAuthorizationFlagInteractionAllowed
|kAuthorizationFlagExtendRights
, NULL
);
1559 if (status
== errSecSuccess
) {
1560 AuthorizationItemSet
*returnedInfo
;
1561 status
= AuthorizationCopyInfo(authRef
, NULL
, &returnedInfo
);
1562 if (status
== errSecSuccess
) {
1563 if (returnedInfo
&& (returnedInfo
->count
> 0)) {
1564 for (uint32_t index
= 0; index
< returnedInfo
->count
; index
++) {
1565 AuthorizationItem
&item
= returnedInfo
->items
[index
];
1566 if (!strcmp(AGENT_PASSWORD
, item
.name
)) {
1567 CFIndex len
= item
.valueLength
;
1569 secnotice("KCLogin", "User entered pwd");
1570 smartCardPassword
= CFStringCreateWithBytes(SecCFAllocatorZeroize(), (UInt8
*)item
.value
, (CFIndex
)len
, kCFStringEncodingUTF8
, TRUE
);
1571 memset(item
.value
, 0, len
);
1578 AuthorizationFreeItemSet(returnedInfo
);
1581 AuthorizationFree(authRef
, 0);
1585 // but don't add it to the search list yet; we'll do that later
1586 Keychain theKeychain
= makeKeychain(loginDLDbIdentifier
, false, true);
1587 secnotice("KCLogin", "Creating login keychain %s", (loginDLDbIdentifier
) ? loginDLDbIdentifier
.dbName() : "<NULL>");
1588 if (tokenContextStatus
== errSecSuccess
) {
1589 if (smartCardPassword
.get()) {
1590 CFIndex length
= CFStringGetLength(smartCardPassword
);
1591 CFIndex maxSize
= CFStringGetMaximumSizeForEncoding(length
, kCFStringEncodingUTF8
) + 1;
1592 char *buffer
= (char *)malloc(maxSize
);
1593 if (CFStringGetCString(smartCardPassword
, buffer
, maxSize
, kCFStringEncodingUTF8
)) {
1594 secnotice("KCLogin", "Keychain is created using password provided by sc user");
1595 theKeychain
->create((UInt32
)strlen(buffer
), buffer
);
1596 memset(buffer
, 0, maxSize
);
1598 secnotice("KCLogin", "Conversion failed");
1599 MacOSError::throwMe(errSecNotAvailable
);
1602 secnotice("KCLogin", "User did not provide kc password");
1603 MacOSError::throwMe(errSecNotAvailable
);
1606 theKeychain
->create(passwordLength
, password
);
1608 secnotice("KCLogin", "Login keychain created successfully");
1609 loginKeychainExists
= true;
1610 // Set the prefs for this new login keychain.
1611 loginKeychain(theKeychain
);
1612 // Login Keychain does not lock on sleep nor lock after timeout by default.
1613 theKeychain
->setSettings(INT_MAX
, false);
1614 loginUnlocked
= true;
1615 mSavedList
.revert(true);
1618 //***************************************************************
1619 // make plist changes after files have been renamed or created
1620 //***************************************************************
1622 // if the shortname keychain exists in the search list, either rename or remove the entry
1623 if (mSavedList
.member(demungeDLDbIdentifier(shortnameDLDbIdentifier
))) {
1624 if (shortnameDotKeychainExists
&& !mSavedList
.member(demungeDLDbIdentifier(shortnameDotDLDbIdentifier
))) {
1625 // change shortname to shortname.keychain (login.keychain will be added later if not present)
1626 secnotice("KCLogin", "Renaming %s to %s in keychain search list",
1627 (shortnameDLDbIdentifier
) ? shortnameDLDbIdentifier
.dbName() : "<NULL>",
1628 (shortnameDotDLDbIdentifier
) ? shortnameDotDLDbIdentifier
.dbName() : "<NULL>");
1629 mSavedList
.rename(demungeDLDbIdentifier(shortnameDLDbIdentifier
),
1630 demungeDLDbIdentifier(shortnameDotDLDbIdentifier
));
1631 } else if (!mSavedList
.member(demungeDLDbIdentifier(loginDLDbIdentifier
))) {
1632 // change shortname to login.keychain
1633 secnotice("KCLogin", "Renaming %s to %s in keychain search list",
1634 (shortnameDLDbIdentifier
) ? shortnameDLDbIdentifier
.dbName() : "<NULL>",
1635 (loginDLDbIdentifier
) ? loginDLDbIdentifier
.dbName() : "<NULL>");
1636 mSavedList
.rename(demungeDLDbIdentifier(shortnameDLDbIdentifier
),
1637 demungeDLDbIdentifier(loginDLDbIdentifier
));
1639 // already have login.keychain in list, and renaming to shortname.keychain isn't an option,
1640 // so just remove the entry
1641 secnotice("KCLogin", "Removing %s from keychain search list", (shortnameDLDbIdentifier
) ? shortnameDLDbIdentifier
.dbName() : "<NULL>");
1642 mSavedList
.remove(demungeDLDbIdentifier(shortnameDLDbIdentifier
));
1645 // note: save() will cause the plist to be unlinked if the only remaining entry is for login.keychain
1647 mSavedList
.revert(true);
1650 // make sure that login.keychain is in the search list
1651 if (!mSavedList
.member(demungeDLDbIdentifier(loginDLDbIdentifier
))) {
1652 secnotice("KCLogin", "Adding %s to keychain search list", (loginDLDbIdentifier
) ? loginDLDbIdentifier
.dbName() : "<NULL>");
1653 mSavedList
.add(demungeDLDbIdentifier(loginDLDbIdentifier
));
1655 mSavedList
.revert(true);
1658 // if we have a shortname.keychain, always include it in the plist (after login.keychain)
1659 if (shortnameDotKeychainExists
&& !mSavedList
.member(demungeDLDbIdentifier(shortnameDotDLDbIdentifier
))) {
1660 mSavedList
.add(demungeDLDbIdentifier(shortnameDotDLDbIdentifier
));
1662 mSavedList
.revert(true);
1665 // make sure that the default keychain is in the search list; if not, reset the default to login.keychain
1666 if (!mSavedList
.member(mSavedList
.defaultDLDbIdentifier())) {
1667 secnotice("KCLogin", "Changing default keychain to %s", (loginDLDbIdentifier
) ? loginDLDbIdentifier
.dbName() : "<NULL>");
1668 mSavedList
.defaultDLDbIdentifier(demungeDLDbIdentifier(loginDLDbIdentifier
));
1670 mSavedList
.revert(true);
1673 //***************************************************************
1674 // auto-unlock the login keychain(s)
1675 //***************************************************************
1676 // all our preflight fixups are finally done, so we can now attempt to unlock the login keychain
1678 OSStatus loginResult
= errSecSuccess
;
1679 if (!loginUnlocked
) {
1682 Keychain
theKeychain(keychain(loginDLDbIdentifier
));
1683 secnotice("KCLogin", "Attempting to unlock login keychain \"%s\"",
1684 (theKeychain
) ? theKeychain
->name() : "<NULL>");
1685 theKeychain
->unlock(CssmData(const_cast<void *>(password
), passwordLength
));
1686 loginUnlocked
= true;
1688 catch(const CssmError
&e
)
1690 loginResult
= e
.osStatus(); // save this result
1694 if (!loginUnlocked
|| tokenContextStatus
== errSecSuccess
) {
1695 Keychain
theKeychain(keychain(loginDLDbIdentifier
));
1696 bool tokenLoginDataUpdated
= false;
1698 for (UInt32 i
= 0; i
< 2; i
++) {
1699 loginResult
= errSecSuccess
;
1701 CFRef
<CFDictionaryRef
> tokenLoginData
;
1702 if (tokenLoginContext
) {
1703 OSStatus status
= TokenLoginGetLoginData(tokenLoginContext
, tokenLoginData
.take());
1704 if (status
!= errSecSuccess
) {
1705 if (tokenLoginDataUpdated
) {
1706 loginResult
= status
;
1709 // updating unlock key fails if it is not token login
1710 secnotice("KCLogin", "Error %d, reconstructing unlock data", (int)status
);
1711 status
= TokenLoginUpdateUnlockData(tokenLoginContext
, smartCardPassword
);
1712 if (status
== errSecSuccess
) {
1713 loginResult
= TokenLoginGetLoginData(tokenLoginContext
, tokenLoginData
.take());
1714 if (loginResult
!= errSecSuccess
) {
1717 tokenLoginDataUpdated
= true;
1723 // first try to unlock login keychain because if this fails, token keychain unlock fails as well
1724 if (tokenLoginData
) {
1725 secnotice("KCLogin", "Going to unlock keybag using scBlob");
1726 OSStatus status
= TokenLoginUnlockKeybag(tokenLoginContext
, tokenLoginData
);
1727 secnotice("KCLogin", "Keybag unlock result %d", (int)status
);
1729 CssmError::throwMe(status
); // to trigger login data regeneration
1734 key
.header().BlobType
= CSSM_KEYBLOB_RAW
;
1735 key
.header().Format
= CSSM_KEYBLOB_RAW_FORMAT_OCTET_STRING
;
1736 key
.header().AlgorithmId
= CSSM_ALGID_3DES_3KEY
;
1737 key
.header().KeyClass
= CSSM_KEYCLASS_SESSION_KEY
;
1738 key
.header().KeyUsage
= CSSM_KEYUSE_ENCRYPT
| CSSM_KEYUSE_DECRYPT
| CSSM_KEYATTR_EXTRACTABLE
;
1739 key
.header().KeyAttr
= 0;
1740 CFRef
<CFDataRef
> tokenLoginUnlockKey
;
1741 if (tokenLoginData
) {
1742 OSStatus status
= TokenLoginGetUnlockKey(tokenLoginContext
, tokenLoginUnlockKey
.take());
1744 CssmError::throwMe(status
); // to trigger login data regeneration
1745 key
.KeyData
= CssmData(tokenLoginUnlockKey
.get());
1747 key
.KeyData
= CssmData(const_cast<void *>(password
), passwordLength
);
1749 // unwrap it into the CSP (but keep it raw)
1750 UnwrapKey
unwrap(theKeychain
->csp(), CSSM_ALGID_NONE
);
1752 CssmData descriptiveData
;
1754 KeySpec(CSSM_KEYUSE_ANY
, CSSM_KEYATTR_EXTRACTABLE
),
1755 masterKey
, &descriptiveData
, NULL
);
1757 CssmClient::Db db
= theKeychain
->database();
1759 // create the keychain, using appropriate credentials
1760 Allocator
&alloc
= db
->allocator();
1761 AutoCredentials
cred(alloc
); // will leak, but we're quitting soon :-)
1763 // use this passphrase
1764 cred
+= TypedList(alloc
, CSSM_SAMPLE_TYPE_KEYCHAIN_LOCK
,
1765 new(alloc
) ListElement(CSSM_SAMPLE_TYPE_SYMMETRIC_KEY
),
1766 new(alloc
) ListElement(CssmData::wrap(theKeychain
->csp()->handle())),
1767 new(alloc
) ListElement(CssmData::wrap(masterKey
)),
1768 new(alloc
) ListElement(CssmData()));
1769 db
->authenticate(CSSM_DB_ACCESS_READ
, &cred
);
1771 loginUnlocked
= true;
1772 } catch (const CssmError
&e
) {
1773 if (tokenLoginData
&& !tokenLoginDataUpdated
) {
1774 // token login unlock key was invalid
1775 loginResult
= TokenLoginUpdateUnlockData(tokenLoginContext
, smartCardPassword
);
1776 if (loginResult
== errSecSuccess
) {
1777 tokenLoginDataUpdated
= true;
1782 loginResult
= e
.osStatus();
1789 // if "shortname.keychain" exists and is in the search list, attempt to auto-unlock it with the same password
1790 if (shortnameDotKeychainExists
&& mSavedList
.member(demungeDLDbIdentifier(shortnameDotDLDbIdentifier
))) {
1793 Keychain
shortnameDotKC(keychain(shortnameDotDLDbIdentifier
));
1794 secnotice("KCLogin", "Attempting to unlock short name keychain \"%s\"",
1795 (shortnameDotKC
) ? shortnameDotKC
->name() : "<NULL>");
1796 shortnameDotKC
->unlock(CssmData(const_cast<void *>(password
), passwordLength
));
1798 catch(const CssmError
&e
)
1800 // ignore; failure to unlock this keychain is not considered an error
1804 if (loginResult
!= errSecSuccess
) {
1805 MacOSError::throwMe(loginResult
);
1809 void StorageManager::stashLogin()
1811 OSStatus loginResult
= errSecSuccess
;
1813 DLDbIdentifier loginDLDbIdentifier
;
1815 mSavedList
.revert(true);
1816 loginDLDbIdentifier
= mSavedList
.loginDLDbIdentifier();
1819 secnotice("KCLogin", "StorageManager::stash: loginDLDbIdentifier is %s", (loginDLDbIdentifier
) ? loginDLDbIdentifier
.dbName() : "<NULL>");
1820 if (!loginDLDbIdentifier
)
1821 MacOSError::throwMe(errSecNoSuchKeychain
);
1826 Keychain
theKeychain(keychain(loginDLDbIdentifier
));
1827 secnotice("KCLogin", "Attempting to use stash for login keychain \"%s\"",
1828 (theKeychain
) ? theKeychain
->name() : "<NULL>");
1829 theKeychain
->stashCheck();
1831 catch(const CssmError
&e
)
1833 loginResult
= e
.osStatus(); // save this result
1837 if (loginResult
!= errSecSuccess
) {
1838 MacOSError::throwMe(loginResult
);
1842 void StorageManager::stashKeychain()
1844 OSStatus loginResult
= errSecSuccess
;
1846 DLDbIdentifier loginDLDbIdentifier
;
1848 mSavedList
.revert(true);
1849 loginDLDbIdentifier
= mSavedList
.loginDLDbIdentifier();
1852 secnotice("KCLogin", "StorageManager::stash: loginDLDbIdentifier is %s", (loginDLDbIdentifier
) ? loginDLDbIdentifier
.dbName() : "<NULL>");
1853 if (!loginDLDbIdentifier
)
1854 MacOSError::throwMe(errSecNoSuchKeychain
);
1858 Keychain
theKeychain(keychain(loginDLDbIdentifier
));
1859 secnotice("KCLogin", "Attempting to stash login keychain \"%s\"",
1860 (theKeychain
) ? theKeychain
->name() : "<NULL>");
1861 theKeychain
->stash();
1863 catch(const CssmError
&e
)
1865 loginResult
= e
.osStatus(); // save this result
1869 if (loginResult
!= errSecSuccess
) {
1870 MacOSError::throwMe(loginResult
);
1874 void StorageManager::logout()
1876 // nothing left to do here
1879 void StorageManager::changeLoginPassword(ConstStringPtr oldPassword
, ConstStringPtr newPassword
)
1881 StLock
<Mutex
>_(mMutex
);
1883 loginKeychain()->changePassphrase(oldPassword
, newPassword
);
1884 secnotice("KClogin", "Changed login keychain password successfully");
1888 void StorageManager::changeLoginPassword(UInt32 oldPasswordLength
, const void *oldPassword
, UInt32 newPasswordLength
, const void *newPassword
)
1890 StLock
<Mutex
>_(mMutex
);
1892 loginKeychain()->changePassphrase(oldPasswordLength
, oldPassword
, newPasswordLength
, newPassword
);
1893 secnotice("KClogin", "Changed login keychain password successfully");
1896 // Clear out the keychain search list and rename the existing login.keychain.
1898 void StorageManager::resetKeychain(Boolean resetSearchList
)
1900 StLock
<Mutex
>_(mMutex
);
1902 // Clear the keychain search list.
1903 Keychain keychain
= NULL
;
1904 DLDbIdentifier dldbi
;
1907 if ( resetSearchList
)
1909 StorageManager::KeychainList keychainList
;
1910 setSearchList(keychainList
);
1912 // Get a reference to the existing login keychain...
1913 // If we don't have one, we throw (not requiring a rename).
1915 keychain
= loginKeychain();
1916 } catch(const CommonError
& e
) {
1917 secnotice("KClogin", "Failed to open login keychain due to an error: %s", e
.what());
1919 // Set up fallback rename.
1920 dldbi
= loginKeychainDLDbIdentifer();
1923 if(::stat(dldbi
.dbName(), &exists
) != 0) {
1924 // no file exists, everything is fine
1925 secnotice("KClogin", "no file exists; resetKeychain() is done");
1932 // Rename the existing login.keychain (i.e. put it aside).
1934 CFMutableStringRef newName
= NULL
;
1935 newName
= CFStringCreateMutable(NULL
, 0);
1936 CFStringRef currName
= NULL
;
1938 currName
= CFStringCreateWithCString(NULL
, keychain
->name(), kCFStringEncodingUTF8
);
1940 currName
= CFStringCreateWithCString(NULL
, dldbi
.dbName(), kCFStringEncodingUTF8
);
1942 if ( newName
&& currName
)
1944 CFStringAppend(newName
, currName
);
1945 CFStringRef kcSuffix
= CFSTR(kKeychainSuffix
);
1946 CFStringRef kcDbSuffix
= CFSTR(kKeychainDbSuffix
);
1947 bool hasDbSuffix
= false;
1948 if ( CFStringHasSuffix(newName
, kcSuffix
) ) // remove the .keychain extension
1950 CFRange suffixRange
= CFStringFind(newName
, kcSuffix
, 0);
1951 CFStringFindAndReplace(newName
, kcSuffix
, CFSTR(""), suffixRange
, 0);
1953 if (CFStringHasSuffix(newName
, kcDbSuffix
)) {
1955 CFRange suffixRange
= CFStringFind(newName
, kcDbSuffix
, 0);
1956 CFStringFindAndReplace(newName
, kcDbSuffix
, CFSTR(""), suffixRange
, 0);
1959 CFStringAppend(newName
, CFSTR(kKeychainRenamedSuffix
)); // add "_renamed_"
1962 secnotice("KClogin", "attempting keychain rename to %@", newName
);
1963 renameUnique(keychain
, currName
, newName
, hasDbSuffix
);
1965 catch(const CommonError
& e
)
1967 // we need to release 'newName' & 'currName'
1968 secnotice("KClogin", "Failed to renameUnique due to an error: %s", e
.what());
1972 secnotice("KClogin", "Failed to renameUnique due to an unknown error");
1974 } // else, let the login call report a duplicate
1976 secnotice("KClogin", "don't have paths, quitting");
1981 CFRelease(currName
);
1983 catch(const CommonError
& e
) {
1984 secnotice("KClogin", "Failed to reset login keychain due to an error: %s", e
.what());
1988 // We either don't have a login keychain, or there was a
1989 // failure to rename the existing one.
1990 secnotice("KClogin", "Failed to reset keychain due to an unknown error");
1994 #pragma mark ____ File Related ____
1996 Keychain
StorageManager::make(const char *pathName
)
1998 return make(pathName
, true);
2001 Keychain
StorageManager::make(const char *pathName
, bool add
)
2003 return make(pathName
, add
, false);
2006 Keychain
StorageManager::make(const char *pathName
, bool add
, bool isReset
) {
2007 return makeKeychain(makeDLDbIdentifier(pathName
), add
, isReset
);
2010 DLDbIdentifier
StorageManager::makeDLDbIdentifier(const char *pathName
) {
2011 StLock
<Mutex
>_(mMutex
);
2013 string fullPathName
;
2014 if ( pathName
[0] == '/' )
2015 fullPathName
= pathName
;
2018 // Get Home directory from environment.
2021 case kSecPreferencesDomainUser
:
2023 const char *homeDir
= getenv("HOME");
2024 if (homeDir
== NULL
)
2026 // If $HOME is unset get the current user's home directory
2027 // from the passwd file.
2028 uid_t uid
= geteuid();
2029 if (!uid
) uid
= getuid();
2030 struct passwd
*pw
= getpwuid(uid
);
2032 MacOSError::throwMe(errSecParam
);
2033 homeDir
= pw
->pw_dir
;
2035 fullPathName
= homeDir
;
2038 case kSecPreferencesDomainSystem
:
2042 assert(false); // invalid domain for this
2045 fullPathName
+= "/Library/Keychains/";
2046 fullPathName
+= pathName
;
2049 const CSSM_NET_ADDRESS
*DbLocation
= NULL
; // NULL for keychains
2050 const CSSM_VERSION
*version
= NULL
;
2051 uint32 subserviceId
= 0;
2052 CSSM_SERVICE_TYPE subserviceType
= CSSM_SERVICE_DL
| CSSM_SERVICE_CSP
;
2053 const CssmSubserviceUid
ssuid(gGuidAppleCSPDL
, version
,
2054 subserviceId
, subserviceType
);
2055 DLDbIdentifier
dlDbIdentifier(ssuid
, fullPathName
.c_str(), DbLocation
);
2056 return dlDbIdentifier
;
2059 Keychain
StorageManager::makeLoginAuthUI(const Item
*item
, bool isReset
)
2061 StLock
<Mutex
>_(mMutex
);
2063 // Create a login/default keychain for the user using UI.
2064 // The user can cancel out of the operation, or create a new login keychain.
2065 // If auto-login is turned off, the user will be asked for their login password.
2067 OSStatus result
= errSecSuccess
;
2068 Keychain keychain
; // We return this keychain.
2070 // Set up the Auth ref to bring up UI.
2072 AuthorizationItem
*currItem
, *authEnvirItemArrayPtr
= NULL
;
2073 AuthorizationRef authRef
= NULL
;
2076 result
= AuthorizationCreate(NULL
, NULL
, kAuthorizationFlagDefaults
, &authRef
);
2078 MacOSError::throwMe(result
);
2080 AuthorizationEnvironment envir
;
2081 envir
.count
= 6; // up to 6 hints can be used.
2082 authEnvirItemArrayPtr
= (AuthorizationItem
*)malloc(sizeof(AuthorizationItem
) * envir
.count
);
2083 if ( !authEnvirItemArrayPtr
)
2084 MacOSError::throwMe(errAuthorizationInternal
);
2086 currItem
= envir
.items
= authEnvirItemArrayPtr
;
2089 // 1st Hint (optional): The keychain item's account attribute string.
2090 // When item is specified, we assume an 'add' operation is being attempted.
2093 SecKeychainAttribute attr
= { kSecAccountItemAttr
, 255, &buff
};
2098 (*item
)->getAttribute(attr
, &actLen
);
2102 actLen
= 0; // This item didn't have the account attribute, so don't display one in the UI.
2105 currItem
->name
= AGENT_HINT_ATTR_NAME
; // name str that identifies this hint as attr name
2106 if ( actLen
) // Fill in the hint if we have an account attr
2108 if ( actLen
>= sizeof(buff
) )
2109 buff
[sizeof(buff
)-1] = 0;
2112 currItem
->valueLength
= strlen(buff
)+1;
2113 currItem
->value
= buff
;
2117 currItem
->valueLength
= 0;
2118 currItem
->value
= NULL
;
2120 currItem
->flags
= 0;
2123 // 2nd Hint (optional): The item's keychain full path.
2126 char* currDefaultName
= NULL
;
2129 currDefaultName
= (char*)defaultKeychain()->name(); // Use the name if we have it.
2130 currItem
->name
= AGENT_HINT_LOGIN_KC_NAME
; // Name str that identifies this hint as kc path
2131 currItem
->valueLength
= (currDefaultName
) ? strlen(currDefaultName
) : 0;
2132 currItem
->value
= (currDefaultName
) ? (void*)currDefaultName
: (void*)"";
2133 currItem
->flags
= 0;
2142 // 3rd Hint (required): check if curr default keychain is unavailable.
2143 // This is determined by the parent not existing.
2145 currItem
->name
= AGENT_HINT_LOGIN_KC_EXISTS_IN_KC_FOLDER
;
2146 Boolean loginUnavail
= false;
2149 Keychain defaultKC
= defaultKeychain();
2150 if ( !defaultKC
->exists() )
2151 loginUnavail
= true;
2153 catch(...) // login.keychain not present
2156 currItem
->valueLength
= sizeof(Boolean
);
2157 currItem
->value
= (void*)&loginUnavail
;
2158 currItem
->flags
= 0;
2161 // 4th Hint (required): userName
2164 currItem
->name
= AGENT_HINT_LOGIN_KC_USER_NAME
;
2165 char* uName
= getenv("USER");
2166 string userName
= uName
? uName
: "";
2167 if ( userName
.length() == 0 )
2169 uid_t uid
= geteuid();
2170 if (!uid
) uid
= getuid();
2171 struct passwd
*pw
= getpwuid(uid
); // fallback case...
2173 userName
= pw
->pw_name
;
2176 if ( userName
.length() == 0 ) // did we ultimately get one?
2177 MacOSError::throwMe(errAuthorizationInternal
);
2179 currItem
->value
= (void*)userName
.c_str();
2180 currItem
->valueLength
= userName
.length();
2181 currItem
->flags
= 0;
2184 // 5th Hint (required): flags if user has more than 1 keychain (used for a later warning when reset to default).
2187 currItem
->name
= AGENT_HINT_LOGIN_KC_USER_HAS_OTHER_KCS_STR
;
2188 Boolean moreThanOneKCExists
= false;
2190 // if item is NULL, then this is a user-initiated full reset
2191 if (item
&& mSavedList
.searchList().size() > 1)
2192 moreThanOneKCExists
= true;
2194 currItem
->value
= &moreThanOneKCExists
;
2195 currItem
->valueLength
= sizeof(Boolean
);
2196 currItem
->flags
= 0;
2199 // 6th Hint (required): If no item is involved, this is a user-initiated full reset.
2200 // We want to suppress the "do you want to reset to defaults?" panel in this case.
2203 currItem
->name
= AGENT_HINT_LOGIN_KC_SUPPRESS_RESET_PANEL
;
2204 Boolean suppressResetPanel
= (item
== NULL
) ? TRUE
: FALSE
;
2205 currItem
->valueLength
= sizeof(Boolean
);
2206 currItem
->value
= (void*)&suppressResetPanel
;
2207 currItem
->flags
= 0;
2210 // Set up the auth rights and make the auth call.
2212 AuthorizationItem authItem
= { LOGIN_KC_CREATION_RIGHT
, 0 , NULL
, 0 };
2213 AuthorizationRights rights
= { 1, &authItem
};
2214 AuthorizationFlags flags
= kAuthorizationFlagDefaults
| kAuthorizationFlagInteractionAllowed
| kAuthorizationFlagExtendRights
;
2215 result
= AuthorizationCopyRights(authRef
, &rights
, &envir
, flags
, NULL
);
2217 MacOSError::throwMe(result
);
2220 resetKeychain(true); // Clears the plist, moves aside existing login.keychain
2222 catch (...) // can throw if no existing login.keychain is found
2225 login(authRef
, (UInt32
)userName
.length(), userName
.c_str(), isReset
); // Create login.keychain
2226 keychain
= loginKeychain(); // Get newly-created login keychain
2227 defaultKeychain(keychain
); // Set it to be the default
2229 free(authEnvirItemArrayPtr
);
2230 AuthorizationFree(authRef
, kAuthorizationFlagDefaults
);
2235 // clean up allocations, then rethrow error
2236 if ( authEnvirItemArrayPtr
)
2237 free(authEnvirItemArrayPtr
);
2239 AuthorizationFree(authRef
, kAuthorizationFlagDefaults
);
2246 Keychain
StorageManager::defaultKeychainUI(Item
&item
)
2248 StLock
<Mutex
>_(mMutex
);
2250 Keychain returnedKeychain
;
2253 returnedKeychain
= defaultKeychain(); // If we have one, return it.
2254 if ( returnedKeychain
->exists() )
2255 return returnedKeychain
;
2257 catch(...) // We could have one, but it isn't available (i.e. on a un-mounted volume).
2260 if ( globals().getUserInteractionAllowed() )
2262 returnedKeychain
= makeLoginAuthUI(&item
, false); // If no Keychains is present, one will be created.
2263 if ( !returnedKeychain
)
2264 MacOSError::throwMe(errSecInvalidKeychain
); // Something went wrong...
2267 MacOSError::throwMe(errSecInteractionNotAllowed
); // If UI isn't allowed, return an error.
2269 return returnedKeychain
;
2273 StorageManager::addToDomainList(SecPreferencesDomain domain
,
2274 const char* dbName
, const CSSM_GUID
&guid
, uint32 subServiceType
)
2276 StLock
<Mutex
>_(mMutex
);
2278 if (domain
== kSecPreferencesDomainDynamic
)
2279 MacOSError::throwMe(errSecInvalidPrefsDomain
);
2281 // make the identifier
2282 CSSM_VERSION version
= {0, 0};
2283 DLDbIdentifier id
= DLDbListCFPref::makeDLDbIdentifier (guid
, version
, 0,
2284 subServiceType
, dbName
, NULL
);
2286 if (domain
== mDomain
)
2288 // manipulate the user's list
2290 mSavedList
.revert(true);
2291 mSavedList
.add(demungeDLDbIdentifier(id
));
2295 KCEventNotifier::PostKeychainEvent(kSecKeychainListChangedEvent
);
2299 // manipulate the other list
2300 DLDbListCFPref(domain
).add(id
);
2305 StorageManager::isInDomainList(SecPreferencesDomain domain
,
2306 const char* dbName
, const CSSM_GUID
&guid
, uint32 subServiceType
)
2308 StLock
<Mutex
>_(mMutex
);
2310 if (domain
== kSecPreferencesDomainDynamic
)
2311 MacOSError::throwMe(errSecInvalidPrefsDomain
);
2313 CSSM_VERSION version
= {0, 0};
2314 DLDbIdentifier id
= DLDbListCFPref::makeDLDbIdentifier (guid
, version
, 0,
2315 subServiceType
, dbName
, NULL
);
2317 // determine the list to search
2319 if (domain
== mDomain
)
2321 result
= mSavedList
.member(demungeDLDbIdentifier(id
));
2325 result
= DLDbListCFPref(domain
).member(demungeDLDbIdentifier(id
));
2331 MacOSError::throwMe(errSecNoSuchKeychain
);
2336 StorageManager::removeFromDomainList(SecPreferencesDomain domain
,
2337 const char* dbName
, const CSSM_GUID
&guid
, uint32 subServiceType
)
2339 StLock
<Mutex
>_(mMutex
);
2341 if (domain
== kSecPreferencesDomainDynamic
)
2342 MacOSError::throwMe(errSecInvalidPrefsDomain
);
2344 // make the identifier
2345 CSSM_VERSION version
= {0, 0};
2346 DLDbIdentifier id
= DLDbListCFPref::makeDLDbIdentifier (guid
, version
, 0,
2347 subServiceType
, dbName
, NULL
);
2349 if (domain
== mDomain
)
2351 // manipulate the user's list
2353 mSavedList
.revert(true);
2354 mSavedList
.remove(demungeDLDbIdentifier(id
));
2358 KCEventNotifier::PostKeychainEvent(kSecKeychainListChangedEvent
);
2362 // manipulate the other list
2363 DLDbListCFPref(domain
).remove(id
);
2368 StorageManager::keychainOwnerPermissionsValidForDomain(const char* path
, SecPreferencesDomain domain
)
2372 const char* sysPrefDir
= "/Library/Preferences";
2373 const char* errMsg
= "Will not set default";
2374 char* mustOwnDir
= NULL
;
2375 struct passwd
* pw
= NULL
;
2378 uid_t uid
= geteuid();
2379 if (!uid
) uid
= getuid();
2381 // our (e)uid must own the appropriate preferences or home directory
2382 // for the specified preference domain whose default we will be modifying
2384 case kSecPreferencesDomainUser
:
2385 mustOwnDir
= getenv("HOME");
2386 if (mustOwnDir
== NULL
) {
2388 if (!pw
) return false;
2389 mustOwnDir
= pw
->pw_dir
;
2392 case kSecPreferencesDomainSystem
:
2393 mustOwnDir
= (char*)sysPrefDir
;
2395 case kSecPreferencesDomainCommon
:
2396 mustOwnDir
= (char*)sysPrefDir
;
2402 if (mustOwnDir
!= NULL
) {
2404 if ( (stat(mustOwnDir
, &dsb
) != 0) || (dsb
.st_uid
!= uid
) ) {
2405 fprintf(stderr
, "%s: UID=%d does not own directory %s\n", errMsg
, (int)uid
, mustOwnDir
);
2406 mustOwnDir
= NULL
; // will return below after calling endpwent()
2413 if (mustOwnDir
== NULL
)
2416 // check that file actually exists
2417 if (stat(path
, &sb
) != 0) {
2418 fprintf(stderr
, "%s: file %s does not exist\n", errMsg
, path
);
2423 if (sb
.st_flags
& (SF_IMMUTABLE
| UF_IMMUTABLE
)) {
2424 fprintf(stderr
, "%s: file %s is immutable\n", errMsg
, path
);
2429 if (sb
.st_uid
!= uid
) {
2430 fprintf(stderr
, "%s: file %s is owned by UID=%d, but we have UID=%d\n",
2431 errMsg
, path
, (int)sb
.st_uid
, (int)uid
);
2437 perms
|= 0600; // must have owner read/write permission set
2438 if (sb
.st_mode
!= perms
) {
2439 fprintf(stderr
, "%s: file %s does not have the expected permissions\n", errMsg
, path
);
2443 // user owns file and can read/write it