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 <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 dispatch_async(release_queue
, ^() {
418 if(kcImpl
->mCacheTimer
) {
419 // Update the cache timer to be seconds from now
420 dispatch_source_set_timer(kcImpl
->mCacheTimer
, dispatch_time(DISPATCH_TIME_NOW
, seconds
* NSEC_PER_SEC
), DISPATCH_TIME_FOREVER
, NSEC_PER_SEC
/2);
421 secdebug("keychain", "updating cache on %p %s", kcImpl
, kcImpl
->name());
423 // We've added an extra retain to this keychain right before invoking this block. Release it.
427 // No cache timer; make one.
428 kcImpl
->mCacheTimer
= dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER
, 0, 0, release_queue
);
429 dispatch_source_set_timer(kcImpl
->mCacheTimer
, dispatch_time(DISPATCH_TIME_NOW
, seconds
* NSEC_PER_SEC
), DISPATCH_TIME_FOREVER
, NSEC_PER_SEC
/2);
430 secdebug("keychain", "taking cache on %p %s", kcImpl
, kcImpl
->name());
432 dispatch_source_set_event_handler(kcImpl
->mCacheTimer
, ^{
433 secdebug("keychain", "releasing cache on %p %s", kcImpl
, kcImpl
->name());
434 dispatch_source_cancel(kcImpl
->mCacheTimer
);
435 dispatch_release(kcImpl
->mCacheTimer
);
436 kcImpl
->mCacheTimer
= NULL
;
440 dispatch_resume(kcImpl
->mCacheTimer
);
445 // Create keychain if it doesn't exist, and optionally add it to the search list.
447 StorageManager::makeKeychain(const DLDbIdentifier
&dLDbIdentifier
, bool add
, bool isReset
)
449 StLock
<Mutex
>_(mKeychainMapMutex
);
451 Keychain theKeychain
= keychain(mungeDLDbIdentifier(dLDbIdentifier
, isReset
));
453 bool updateList
= (add
&& shouldAddToSearchList(dLDbIdentifier
));
457 mSavedList
.revert(false);
458 DLDbList searchList
= mSavedList
.searchList();
459 if (find(searchList
.begin(), searchList
.end(), demungeDLDbIdentifier(dLDbIdentifier
)) != searchList
.end())
460 return theKeychain
; // theKeychain is already in the searchList.
462 mCommonList
.revert(false);
463 searchList
= mCommonList
.searchList();
464 if (find(searchList
.begin(), searchList
.end(), demungeDLDbIdentifier(dLDbIdentifier
)) != searchList
.end())
465 return theKeychain
; // theKeychain is already in the commonList don't add it to the searchList.
467 // If theKeychain doesn't exist don't bother adding it to the search list yet.
468 if (!theKeychain
->exists())
471 // theKeychain exists and is not in our search list, so add it to the
473 mSavedList
.revert(true);
474 mSavedList
.add(demungeDLDbIdentifier(dLDbIdentifier
));
481 // Make sure we are not holding mStorageManagerLock anymore when we
483 KCEventNotifier::PostKeychainEvent(kSecKeychainListChangedEvent
);
489 // Be notified a Keychain just got created.
491 StorageManager::created(const Keychain
&keychain
)
493 StLock
<Mutex
>_(mKeychainMapMutex
);
495 DLDbIdentifier dLDbIdentifier
= keychain
->dlDbIdentifier();
496 bool defaultChanged
= false;
497 bool updateList
= shouldAddToSearchList(dLDbIdentifier
);
501 mSavedList
.revert(true);
502 // If we don't have a default Keychain yet. Make the newly created
503 // keychain the default.
504 if (!mSavedList
.defaultDLDbIdentifier())
506 mSavedList
.defaultDLDbIdentifier(demungeDLDbIdentifier(dLDbIdentifier
));
507 defaultChanged
= true;
510 // Add the keychain to the search list prefs.
511 mSavedList
.add(demungeDLDbIdentifier(dLDbIdentifier
));
514 // Make sure we are not holding mLock when we post these events.
515 KCEventNotifier::PostKeychainEvent(kSecKeychainListChangedEvent
);
520 KCEventNotifier::PostKeychainEvent(kSecDefaultChangedEvent
, dLDbIdentifier
);
525 StorageManager::createCursor(SecItemClass itemClass
,
526 const SecKeychainAttributeList
*attrList
)
528 StLock
<Mutex
>_(mMutex
);
530 KeychainList searchList
;
531 getSearchList(searchList
);
532 return KCCursor(searchList
, itemClass
, attrList
);
536 StorageManager::createCursor(const SecKeychainAttributeList
*attrList
)
538 StLock
<Mutex
>_(mMutex
);
540 KeychainList searchList
;
541 getSearchList(searchList
);
542 return KCCursor(searchList
, attrList
);
546 StorageManager::lockAll()
548 StLock
<Mutex
>_(mMutex
);
550 SecurityServer::ClientSession
ss(Allocator::standard(), Allocator::standard());
555 StorageManager::defaultKeychain()
557 StLock
<Mutex
>_(mMutex
);
559 Keychain theKeychain
;
563 mSavedList
.revert(false);
564 DLDbIdentifier
defaultDLDbIdentifier(mSavedList
.defaultDLDbIdentifier());
565 if (defaultDLDbIdentifier
)
567 theKeychain
= keychain(defaultDLDbIdentifier
);
568 ref
= theKeychain
->handle(false);
572 if (theKeychain
/* && theKeychain->exists() */)
575 MacOSError::throwMe(errSecNoDefaultKeychain
);
579 StorageManager::defaultKeychain(const Keychain
&keychain
)
581 StLock
<Mutex
>_(mMutex
);
583 // Only set a keychain as the default if we own it and can read/write it,
584 // and our uid allows modifying the directory for that preference domain.
585 if (!keychainOwnerPermissionsValidForDomain(keychain
->name(), mDomain
))
586 MacOSError::throwMe(errSecWrPerm
);
588 DLDbIdentifier oldDefaultId
;
589 DLDbIdentifier
newDefaultId(keychain
->dlDbIdentifier());
591 oldDefaultId
= mSavedList
.defaultDLDbIdentifier();
592 mSavedList
.revert(true);
593 mSavedList
.defaultDLDbIdentifier(demungeDLDbIdentifier(newDefaultId
));
597 if (!(oldDefaultId
== newDefaultId
))
599 // Make sure we are not holding mLock when we post this event.
600 KCEventNotifier::PostKeychainEvent(kSecDefaultChangedEvent
, newDefaultId
);
605 StorageManager::defaultKeychain(SecPreferencesDomain domain
)
607 StLock
<Mutex
>_(mMutex
);
609 if (domain
== kSecPreferencesDomainDynamic
)
610 MacOSError::throwMe(errSecInvalidPrefsDomain
);
612 if (domain
== mDomain
)
613 return defaultKeychain();
616 DLDbIdentifier
defaultDLDbIdentifier(DLDbListCFPref(domain
).defaultDLDbIdentifier());
617 if (defaultDLDbIdentifier
)
618 return keychain(defaultDLDbIdentifier
);
620 MacOSError::throwMe(errSecNoDefaultKeychain
);
625 StorageManager::defaultKeychain(SecPreferencesDomain domain
, const Keychain
&keychain
)
627 StLock
<Mutex
>_(mMutex
);
629 if (domain
== kSecPreferencesDomainDynamic
)
630 MacOSError::throwMe(errSecInvalidPrefsDomain
);
632 if (domain
== mDomain
)
633 defaultKeychain(keychain
);
635 DLDbListCFPref(domain
).defaultDLDbIdentifier(keychain
->dlDbIdentifier());
639 StorageManager::loginKeychain()
641 StLock
<Mutex
>_(mMutex
);
643 Keychain theKeychain
;
645 mSavedList
.revert(false);
646 DLDbIdentifier
loginDLDbIdentifier(mSavedList
.loginDLDbIdentifier());
647 if (loginDLDbIdentifier
)
649 theKeychain
= keychain(loginDLDbIdentifier
);
653 if (theKeychain
&& theKeychain
->exists())
656 MacOSError::throwMe(errSecNoSuchKeychain
);
660 StorageManager::loginKeychainDLDbIdentifer()
662 StLock
<Mutex
>_(mMutex
);
663 DLDbIdentifier
loginDLDbIdentifier(mSavedList
.loginDLDbIdentifier());
664 return mungeDLDbIdentifier(loginDLDbIdentifier
, false);
668 StorageManager::loginKeychain(Keychain keychain
)
670 StLock
<Mutex
>_(mMutex
);
672 mSavedList
.revert(true);
673 mSavedList
.loginDLDbIdentifier(demungeDLDbIdentifier(keychain
->dlDbIdentifier()));
678 StorageManager::size()
680 StLock
<Mutex
>_(mMutex
);
682 mSavedList
.revert(false);
683 mCommonList
.revert(false);
684 return mSavedList
.searchList().size() + mCommonList
.searchList().size();
688 StorageManager::at(unsigned int ix
)
690 StLock
<Mutex
>_(mMutex
);
692 mSavedList
.revert(false);
693 DLDbList dLDbList
= mSavedList
.searchList();
694 if (ix
< dLDbList
.size())
696 return keychain(dLDbList
[ix
]);
700 ix
-= dLDbList
.size();
701 mCommonList
.revert(false);
702 DLDbList commonList
= mCommonList
.searchList();
703 if (ix
>= commonList
.size())
704 MacOSError::throwMe(errSecInvalidKeychain
);
706 return keychain(commonList
[ix
]);
711 StorageManager::operator[](unsigned int ix
)
713 StLock
<Mutex
>_(mMutex
);
718 void StorageManager::rename(Keychain keychain
, const char* newName
)
721 StLock
<Mutex
>_(mKeychainMapMutex
);
723 bool changedDefault
= false;
724 DLDbIdentifier newDLDbIdentifier
;
726 mSavedList
.revert(true);
727 DLDbIdentifier defaultId
= mSavedList
.defaultDLDbIdentifier();
729 // Find the keychain object for the given ref
730 DLDbIdentifier dLDbIdentifier
= keychain
->dlDbIdentifier();
732 if(!keychain
->database()->isLocked()) {
733 // Bring our unlock state with us
734 DLDbIdentifier
dldbi(dLDbIdentifier
.ssuid(), newName
, dLDbIdentifier
.dbLocation());
735 keychain
->database()->transferTo(dldbi
);
737 keychain
->database()->rename(newName
);
740 if (demungeDLDbIdentifier(dLDbIdentifier
) == defaultId
)
743 newDLDbIdentifier
= keychain
->dlDbIdentifier();
744 // Rename the keychain in the search list.
745 mSavedList
.rename(demungeDLDbIdentifier(dLDbIdentifier
), demungeDLDbIdentifier(newDLDbIdentifier
));
747 // If this was the default keychain change it accordingly
749 mSavedList
.defaultDLDbIdentifier(demungeDLDbIdentifier(newDLDbIdentifier
));
753 // If the keychain wasn't in the cache, don't touch the cache.
754 // Otherwise, update the cache to use its current identifier.
755 if(keychain
->inCache()) {
756 registerKeychain(keychain
);
760 KCEventNotifier::PostKeychainEvent(kSecKeychainListChangedEvent
);
763 KCEventNotifier::PostKeychainEvent(kSecDefaultChangedEvent
,
767 void StorageManager::registerKeychain(Keychain
& kc
) {
768 registerKeychainImpl(kc
.get());
771 void StorageManager::registerKeychainImpl(KeychainImpl
* kcimpl
) {
777 StLock
<Mutex
> _(mKeychainMapMutex
);
779 // First, iterate through the cache to see if this keychain is there. If so, remove it.
780 forceRemoveFromCache(kcimpl
);
782 // If we renamed this keychain on top of an existing one, let's drop the old one from the cache.
783 KeychainMap::iterator it
= mKeychainMap
.find(kcimpl
->dlDbIdentifier());
784 if (it
!= mKeychainMap
.end())
786 Keychain
oldKeychain(it
->second
);
787 oldKeychain
->inCache(false);
788 // @@@ Ideally we should invalidate or fault this keychain object.
791 mKeychainMap
.insert(KeychainMap::value_type(kcimpl
->dlDbIdentifier(), kcimpl
));
792 kcimpl
->inCache(true);
793 } // drop mKeychainMapMutex
796 void StorageManager::forceRemoveFromCache(KeychainImpl
* inKeychainImpl
) {
798 // Wrap all this in a try-block and ignore all errors - we're trying to clean up these maps
800 StLock
<Mutex
> _(mKeychainMapMutex
);
801 for(KeychainMap::iterator it
= mKeychainMap
.begin(); it
!= mKeychainMap
.end(); ) {
802 if(it
->second
== inKeychainImpl
) {
803 // Increment the iterator, but use its pre-increment value for the erase
804 it
->second
->inCache(false);
805 mKeychainMap
.erase(it
++);
810 } // drop mKeychainMapMutex
811 } catch(UnixError ue
) {
812 secnotice("storagemgr", "caught UnixError: %d %s", ue
.unixError(), ue
.what());
813 } catch (CssmError cssme
) {
814 const char* errStr
= cssmErrorString(cssme
.error
);
815 secnotice("storagemgr", "caught CssmError: %d %s", (int) cssme
.error
, errStr
);
816 } catch (MacOSError mose
) {
817 secnotice("storagemgr", "MacOSError: %d", (int)mose
.osStatus());
819 secnotice("storagemgr", "Unknown error");
823 // If you pass NULL as the keychain, you must pass an oldName.
824 void StorageManager::renameUnique(Keychain keychain
, CFStringRef oldName
, CFStringRef newName
, bool appendDbSuffix
)
826 StLock
<Mutex
>_(mMutex
);
828 bool doneCreating
= false;
832 char newNameCString
[MAXPATHLEN
];
833 if ( CFStringGetCString(newName
, newNameCString
, MAXPATHLEN
, kCFStringEncodingUTF8
) ) // make sure it fits in MAXPATHLEN, etc.
835 // Construct the new name...
837 CFMutableStringRef newNameCFStr
= NULL
;
838 newNameCFStr
= CFStringCreateMutable(NULL
, MAXPATHLEN
);
841 CFStringAppendFormat(newNameCFStr
, NULL
, CFSTR("%s%d"), newNameCString
, index
);
843 CFStringAppend(newNameCFStr
, CFSTR(kKeychainDbSuffix
));
845 CFStringAppend(newNameCFStr
, CFSTR(kKeychainSuffix
)); // add .keychain
847 char toUseBuff2
[MAXPATHLEN
];
848 if ( CFStringGetCString(newNameCFStr
, toUseBuff2
, MAXPATHLEN
, kCFStringEncodingUTF8
) ) // make sure it fits in MAXPATHLEN, etc.
851 if ( lstat(toUseBuff2
, &filebuf
) )
854 rename(keychain
, toUseBuff2
);
856 kcList
.push_back(keychain
);
857 remove(kcList
, false);
859 // We don't have a Keychain object, so force the rename here if possible
860 char oldNameCString
[MAXPATHLEN
];
861 if ( CFStringGetCString(oldName
, oldNameCString
, MAXPATHLEN
, kCFStringEncodingUTF8
) ) {
862 int result
= ::rename(oldNameCString
, toUseBuff2
);
863 secnotice("KClogin", "keychain force rename to %s: %d %d", newNameCString
, result
, (result
== 0) ? 0 : errno
);
865 UnixError::throwMe(errno
);
868 secnotice("KClogin", "path is wrong, quitting");
877 doneCreating
= true; // failure to get c string.
878 CFRelease(newNameCFStr
);
881 doneCreating
= false; // failure to create mutable string.
884 doneCreating
= false; // failure to get the string (i.e. > MAXPATHLEN?)
886 while (!doneCreating
&& index
!= INT_MAX
);
889 #define KEYCHAIN_SYNC_KEY CFSTR("KeychainSyncList")
890 #define KEYCHAIN_SYNC_DOMAIN CFSTR("com.apple.keychainsync")
892 static CFStringRef
MakeExpandedPath (const char* path
)
894 std::string name
= DLDbListCFPref::ExpandTildesInPath (std::string (path
));
895 CFStringRef expanded
= CFStringCreateWithCString (NULL
, name
.c_str (), 0);
899 void StorageManager::removeKeychainFromSyncList (const DLDbIdentifier
&id
)
901 StLock
<Mutex
>_(mMutex
);
903 // make a CFString of our identifier
904 const char* idname
= id
.dbName ();
910 CFRef
<CFStringRef
> idString
= MakeExpandedPath (idname
);
912 // check and see if this keychain is in the keychain syncing list
914 (CFArrayRef
) CFPreferencesCopyValue (KEYCHAIN_SYNC_KEY
,
915 KEYCHAIN_SYNC_DOMAIN
,
916 kCFPreferencesCurrentUser
,
917 kCFPreferencesAnyHost
);
923 // make a mutable copy of the dictionary
924 CFRef
<CFMutableArrayRef
> mtValue
= CFArrayCreateMutableCopy (NULL
, 0, value
);
927 // walk the array, looking for the value
929 CFIndex limit
= CFArrayGetCount (mtValue
.get());
932 for (i
= 0; i
< limit
; ++i
)
934 CFDictionaryRef idx
= (CFDictionaryRef
) CFArrayGetValueAtIndex (mtValue
.get(), i
);
935 CFStringRef v
= (CFStringRef
) CFDictionaryGetValue (idx
, CFSTR("DbName"));
938 return; // something is really wrong if this is taken
941 char* stringBuffer
= NULL
;
942 const char* pathString
= CFStringGetCStringPtr(v
, 0);
945 CFIndex maxLen
= CFStringGetMaximumSizeForEncoding(CFStringGetLength(v
), kCFStringEncodingUTF8
) + 1;
946 stringBuffer
= (char*) malloc(maxLen
);
947 CFStringGetCString(v
, stringBuffer
, maxLen
, kCFStringEncodingUTF8
);
948 pathString
= stringBuffer
;
951 CFStringRef vExpanded
= MakeExpandedPath(pathString
);
952 CFComparisonResult result
= CFStringCompare (vExpanded
, idString
.get(), 0);
953 if (stringBuffer
!= NULL
)
958 CFRelease (vExpanded
);
962 CFArrayRemoveValueAtIndex (mtValue
.get(), i
);
971 CFShow (mtValue
.get());
974 CFPreferencesSetValue (KEYCHAIN_SYNC_KEY
,
976 KEYCHAIN_SYNC_DOMAIN
,
977 kCFPreferencesCurrentUser
,
978 kCFPreferencesAnyHost
);
979 CFPreferencesSynchronize (KEYCHAIN_SYNC_DOMAIN
, kCFPreferencesCurrentUser
, kCFPreferencesAnyHost
);
983 void StorageManager::remove(const KeychainList
&kcsToRemove
, bool deleteDb
)
985 StLock
<Mutex
>_(mMutex
);
987 bool unsetDefault
= false;
988 bool updateList
= (!isAppSandboxed());
992 mSavedList
.revert(true);
993 DLDbIdentifier defaultId
= mSavedList
.defaultDLDbIdentifier();
994 for (KeychainList::const_iterator ix
= kcsToRemove
.begin();
995 ix
!= kcsToRemove
.end(); ++ix
)
997 // Find the keychain object for the given ref
998 Keychain theKeychain
= *ix
;
999 DLDbIdentifier dLDbIdentifier
= theKeychain
->dlDbIdentifier();
1001 // Remove it from the saved list
1002 mSavedList
.remove(demungeDLDbIdentifier(dLDbIdentifier
));
1003 if (demungeDLDbIdentifier(dLDbIdentifier
) == defaultId
) {
1009 removeKeychainFromSyncList (dLDbIdentifier
);
1011 // Now remove it from the cache
1012 removeKeychain(dLDbIdentifier
, theKeychain
.get());
1017 mSavedList
.defaultDLDbIdentifier(DLDbIdentifier());
1024 // Delete the actual databases without holding any locks.
1025 for (KeychainList::const_iterator ix
= kcsToRemove
.begin();
1026 ix
!= kcsToRemove
.end(); ++ix
)
1028 (*ix
)->database()->deleteDb();
1033 // Make sure we are not holding mLock when we post these events.
1034 KCEventNotifier::PostKeychainEvent(kSecKeychainListChangedEvent
);
1038 KCEventNotifier::PostKeychainEvent(kSecDefaultChangedEvent
);
1042 StorageManager::getSearchList(KeychainList
&keychainList
)
1044 // hold the global lock since we make keychain objects in this function
1046 // to do: each of the items in this list must be retained, otherwise mayhem will occur
1047 StLock
<Mutex
>_(mMutex
);
1050 keychainList
.clear();
1054 mSavedList
.revert(false);
1055 mCommonList
.revert(false);
1057 // Merge mSavedList, mDynamicList and mCommonList
1058 DLDbList dLDbList
= mSavedList
.searchList();
1059 DLDbList dynamicList
= mDynamicList
.searchList();
1060 DLDbList commonList
= mCommonList
.searchList();
1061 KeychainList result
;
1062 result
.reserve(dLDbList
.size() + dynamicList
.size() + commonList
.size());
1065 for (DLDbList::const_iterator it
= dynamicList
.begin();
1066 it
!= dynamicList
.end(); ++it
)
1068 Keychain k
= keychain(*it
);
1069 result
.push_back(k
);
1072 for (DLDbList::const_iterator it
= dLDbList
.begin();
1073 it
!= dLDbList
.end(); ++it
)
1075 Keychain k
= keychain(*it
);
1076 result
.push_back(k
);
1079 for (DLDbList::const_iterator it
= commonList
.begin();
1080 it
!= commonList
.end(); ++it
)
1082 Keychain k
= keychain(*it
);
1083 result
.push_back(k
);
1087 keychainList
.swap(result
);
1091 StorageManager::setSearchList(const KeychainList
&keychainList
)
1093 StLock
<Mutex
>_(mMutex
);
1095 DLDbList searchList
, oldSearchList(mSavedList
.searchList());
1096 for (KeychainList::const_iterator it
= keychainList
.begin(); it
!= keychainList
.end(); ++it
)
1098 DLDbIdentifier dldbi
= demungeDLDbIdentifier((*it
)->dlDbIdentifier());
1100 // If this keychain is not in the common or dynamic lists, add it to the new search list
1101 DLDbList commonList
= mCommonList
.searchList();
1103 for(DLDbList::const_iterator jt
= commonList
.begin(); jt
!= commonList
.end(); ++jt
) {
1104 if((*jt
) == dldbi
) {
1109 DLDbList dynamicList
= mDynamicList
.searchList();
1110 for(DLDbList::const_iterator jt
= dynamicList
.begin(); jt
!= dynamicList
.end(); ++jt
) {
1111 if((*jt
) == dldbi
) {
1120 searchList
.push_back(dldbi
);
1124 // Set the current searchlist to be what was passed in, the old list will be freed
1125 // upon exit of this stackframe.
1126 mSavedList
.revert(true);
1127 mSavedList
.searchList(searchList
);
1131 if (!(oldSearchList
== searchList
))
1133 // Make sure we are not holding mLock when we post this event.
1134 KCEventNotifier::PostKeychainEvent(kSecKeychainListChangedEvent
);
1139 StorageManager::getSearchList(SecPreferencesDomain domain
, KeychainList
&keychainList
)
1141 StLock
<Mutex
>_(mMutex
);
1144 keychainList
.clear();
1148 if (domain
== kSecPreferencesDomainDynamic
)
1150 convertList(keychainList
, mDynamicList
.searchList());
1152 else if (domain
== mDomain
)
1154 mSavedList
.revert(false);
1155 convertList(keychainList
, mSavedList
.searchList());
1159 convertList(keychainList
, DLDbListCFPref(domain
).searchList());
1163 void StorageManager::forceUserSearchListReread()
1165 mSavedList
.forceUserSearchListReread();
1169 StorageManager::setSearchList(SecPreferencesDomain domain
, const KeychainList
&keychainList
)
1171 StLock
<Mutex
>_(mMutex
);
1173 if (domain
== kSecPreferencesDomainDynamic
)
1174 MacOSError::throwMe(errSecInvalidPrefsDomain
);
1176 DLDbList searchList
;
1177 convertList(searchList
, keychainList
);
1179 if (domain
== mDomain
)
1181 DLDbList
oldSearchList(mSavedList
.searchList());
1183 // Set the current searchlist to be what was passed in, the old list will be freed
1184 // upon exit of this stackframe.
1185 mSavedList
.revert(true);
1186 mSavedList
.searchList(searchList
);
1190 if (!(oldSearchList
== searchList
))
1192 KCEventNotifier::PostKeychainEvent(kSecKeychainListChangedEvent
);
1197 DLDbListCFPref(domain
).searchList(searchList
);
1202 StorageManager::domain(SecPreferencesDomain domain
)
1204 StLock
<Mutex
>_(mMutex
);
1206 if (domain
== kSecPreferencesDomainDynamic
)
1207 MacOSError::throwMe(errSecInvalidPrefsDomain
);
1209 if (domain
== mDomain
)
1210 return; // no change
1212 #if !defined(NDEBUG)
1215 case kSecPreferencesDomainSystem
:
1216 secnotice("storagemgr", "switching to system domain"); break;
1217 case kSecPreferencesDomainUser
:
1218 secnotice("storagemgr", "switching to user domain (uid %d)", getuid()); break;
1220 secnotice("storagemgr", "switching to weird prefs domain %d", domain
); break;
1225 mSavedList
.set(domain
);
1229 StorageManager::optionalSearchList(CFTypeRef keychainOrArray
, KeychainList
&keychainList
)
1231 StLock
<Mutex
>_(mMutex
);
1233 if (!keychainOrArray
)
1234 getSearchList(keychainList
);
1237 CFTypeID typeID
= CFGetTypeID(keychainOrArray
);
1238 if (typeID
== CFArrayGetTypeID())
1239 convertToKeychainList(CFArrayRef(keychainOrArray
), keychainList
);
1240 else if (typeID
== gTypes().KeychainImpl
.typeID
)
1241 keychainList
.push_back(KeychainImpl::required(SecKeychainRef(keychainOrArray
)));
1243 MacOSError::throwMe(errSecParam
);
1249 StorageManager::convertToKeychainList(CFArrayRef keychainArray
, KeychainList
&keychainList
)
1251 CFIndex count
= CFArrayGetCount(keychainArray
);
1255 KeychainList
keychains(count
);
1256 for (CFIndex ix
= 0; ix
< count
; ++ix
)
1258 keychains
[ix
] = KeychainImpl::required(SecKeychainRef(CFArrayGetValueAtIndex(keychainArray
, ix
)));
1261 keychainList
.swap(keychains
);
1265 StorageManager::convertFromKeychainList(const KeychainList
&keychainList
)
1267 CFRef
<CFMutableArrayRef
> keychainArray(CFArrayCreateMutable(NULL
, keychainList
.size(), &kCFTypeArrayCallBacks
));
1269 for (KeychainList::const_iterator ix
= keychainList
.begin(); ix
!= keychainList
.end(); ++ix
)
1271 SecKeychainRef keychainRef
= (*ix
)->handle();
1272 CFArrayAppendValue(keychainArray
, keychainRef
);
1273 CFRelease(keychainRef
);
1276 // Counter the CFRelease that CFRef<> is about to do when keychainArray goes out of scope.
1277 CFRetain(keychainArray
);
1278 return keychainArray
;
1281 void StorageManager::convertList(DLDbList
&ids
, const KeychainList
&kcs
)
1284 result
.reserve(kcs
.size());
1285 for (KeychainList::const_iterator ix
= kcs
.begin(); ix
!= kcs
.end(); ++ix
)
1287 result
.push_back(demungeDLDbIdentifier((*ix
)->dlDbIdentifier()));
1292 void StorageManager::convertList(KeychainList
&kcs
, const DLDbList
&ids
)
1294 StLock
<Mutex
>_(mMutex
);
1296 KeychainList result
;
1297 result
.reserve(ids
.size());
1299 for (DLDbList::const_iterator ix
= ids
.begin(); ix
!= ids
.end(); ++ix
)
1300 result
.push_back(keychain(*ix
));
1305 #pragma mark ____ Login Functions ____
1307 void StorageManager::login(AuthorizationRef authRef
, UInt32 nameLength
, const char* name
, bool isReset
)
1309 StLock
<Mutex
>_(mMutex
);
1311 AuthorizationItemSet
* info
= NULL
;
1312 OSStatus result
= AuthorizationCopyInfo(authRef
, NULL
, &info
); // get the results of the copy rights call.
1313 Boolean created
= false;
1314 if ( result
== errSecSuccess
&& info
->count
)
1316 // Grab the password from the auth context (info) and create the keychain...
1318 AuthorizationItem
* currItem
= info
->items
;
1319 for (UInt32 index
= 1; index
<= info
->count
; index
++) //@@@plugin bug won't return a specific context.
1321 if (strcmp(currItem
->name
, kAuthorizationEnvironmentPassword
) == 0)
1323 // creates the login keychain with the specified password
1326 login(nameLength
, name
, (UInt32
)currItem
->valueLength
, currItem
->value
, isReset
);
1338 AuthorizationFreeItemSet(info
);
1341 MacOSError::throwMe(errAuthorizationInternal
);
1344 void StorageManager::login(ConstStringPtr name
, ConstStringPtr password
)
1346 StLock
<Mutex
>_(mMutex
);
1348 if ( name
== NULL
|| password
== NULL
)
1349 MacOSError::throwMe(errSecParam
);
1351 login(name
[0], name
+ 1, password
[0], password
+ 1, false);
1354 void StorageManager::login(UInt32 nameLength
, const void *name
,
1355 UInt32 passwordLength
, const void *password
, bool isReset
)
1357 if (passwordLength
!= 0 && password
== NULL
)
1359 secnotice("KCLogin", "StorageManager::login: invalid argument (NULL password)");
1360 MacOSError::throwMe(errSecParam
);
1363 DLDbIdentifier loginDLDbIdentifier
;
1365 mSavedList
.revert(true);
1366 loginDLDbIdentifier
= mSavedList
.loginDLDbIdentifier();
1369 secnotice("KCLogin", "StorageManager::login: loginDLDbIdentifier is %s", (loginDLDbIdentifier
) ? loginDLDbIdentifier
.dbName() : "<NULL>");
1370 if (!loginDLDbIdentifier
)
1371 MacOSError::throwMe(errSecNoSuchKeychain
);
1374 //***************************************************************
1375 // gather keychain information
1376 //***************************************************************
1379 int uid
= geteuid();
1380 struct passwd
*pw
= getpwuid(uid
);
1382 secnotice("KCLogin", "StorageManager::login: invalid argument (NULL uid)");
1383 MacOSError::throwMe(errSecParam
);
1385 std::string userName
= pw
->pw_name
;
1387 // make keychain path strings
1388 std::string keychainPath
= DLDbListCFPref::ExpandTildesInPath(kLoginKeychainPathPrefix
);
1389 std::string shortnameKeychain
= keychainPath
+ userName
;
1390 std::string shortnameDotKeychain
= shortnameKeychain
+ ".keychain";
1391 std::string loginDotKeychain
= keychainPath
+ "login.keychain";
1392 std::string loginRenamed1Keychain
= keychainPath
+ "login_renamed1.keychain";
1393 std::string loginKeychainDb
= keychainPath
+ "login.keychain-db";
1395 // check for existence of keychain files
1396 bool shortnameKeychainExists
= false;
1397 bool shortnameDotKeychainExists
= false;
1398 bool loginKeychainExists
= false;
1399 bool loginRenamed1KeychainExists
= false;
1400 bool loginKeychainDbExists
= false;
1404 stat_result
= ::stat(shortnameKeychain
.c_str(), &st
);
1405 shortnameKeychainExists
= (stat_result
== 0);
1406 stat_result
= ::stat(shortnameDotKeychain
.c_str(), &st
);
1407 shortnameDotKeychainExists
= (stat_result
== 0);
1408 stat_result
= ::stat(loginDotKeychain
.c_str(), &st
);
1409 loginKeychainExists
= (stat_result
== 0);
1410 stat_result
= ::stat(loginRenamed1Keychain
.c_str(), &st
);
1411 loginRenamed1KeychainExists
= (stat_result
== 0);
1412 stat_result
= ::stat(loginKeychainDb
.c_str(), &st
);
1413 loginKeychainDbExists
= (stat_result
== 0);
1416 // login.keychain-db is considered to be the same as login.keychain.
1417 // Our transparent keychain promotion on open will handle opening the right version of this file.
1418 loginKeychainExists
|= loginKeychainDbExists
;
1420 bool loginUnlocked
= false;
1422 // make the keychain identifiers
1423 CSSM_VERSION version
= {0, 0};
1424 DLDbIdentifier shortnameDLDbIdentifier
= DLDbListCFPref::makeDLDbIdentifier(gGuidAppleCSPDL
, version
, 0, CSSM_SERVICE_CSP
| CSSM_SERVICE_DL
, shortnameKeychain
.c_str(), NULL
);
1425 DLDbIdentifier shortnameDotDLDbIdentifier
= DLDbListCFPref::makeDLDbIdentifier(gGuidAppleCSPDL
, version
, 0, CSSM_SERVICE_CSP
| CSSM_SERVICE_DL
, shortnameDotKeychain
.c_str(), NULL
);
1426 DLDbIdentifier loginRenamed1DLDbIdentifier
= DLDbListCFPref::makeDLDbIdentifier(gGuidAppleCSPDL
, version
, 0, CSSM_SERVICE_CSP
| CSSM_SERVICE_DL
, loginRenamed1Keychain
.c_str(), NULL
);
1428 //***************************************************************
1429 // make file renaming changes first
1430 //***************************************************************
1432 // if "~/Library/Keychains/shortname" exists, we need to migrate it forward;
1433 // either to login.keychain if there isn't already one, otherwise to shortname.keychain
1434 if (shortnameKeychainExists
) {
1435 int rename_stat
= 0;
1436 if (loginKeychainExists
) {
1438 int tmp_result
= ::stat(loginDotKeychain
.c_str(), &st
);
1439 if (tmp_result
== 0) {
1440 if (st
.st_size
<= kEmptyKeychainSizeInBytes
) {
1441 tmp_result
= ::unlink(loginDotKeychain
.c_str());
1442 rename_stat
= ::rename(shortnameKeychain
.c_str(), loginDotKeychain
.c_str());
1443 shortnameKeychainExists
= (rename_stat
!= 0);
1447 if (shortnameKeychainExists
) {
1448 if (loginKeychainExists
&& !shortnameDotKeychainExists
) {
1449 rename_stat
= ::rename(shortnameKeychain
.c_str(), shortnameDotKeychain
.c_str());
1450 shortnameDotKeychainExists
= (rename_stat
== 0);
1451 } else if (!loginKeychainExists
) {
1452 rename_stat
= ::rename(shortnameKeychain
.c_str(), loginDotKeychain
.c_str());
1453 loginKeychainExists
= (rename_stat
== 0);
1455 // we have all 3 keychains: login.keychain, shortname, and shortname.keychain.
1456 // on Leopard we never want a shortname keychain, so we must move it aside.
1457 char pathbuf
[MAXPATHLEN
];
1458 std::string shortnameRenamedXXXKeychain
= keychainPath
;
1459 shortnameRenamedXXXKeychain
+= userName
;
1460 shortnameRenamedXXXKeychain
+= "_renamed_XXX.keychain";
1461 ::strlcpy(pathbuf
, shortnameRenamedXXXKeychain
.c_str(), sizeof(pathbuf
));
1462 ::mkstemps(pathbuf
, 9); // 9 == strlen(".keychain")
1463 rename_stat
= ::rename(shortnameKeychain
.c_str(), pathbuf
);
1464 shortnameKeychainExists
= (rename_stat
!= 0);
1467 if (rename_stat
!= 0) {
1468 MacOSError::throwMe(errno
);
1472 //***************************************************************
1473 // handle special case where user previously reset the keychain
1474 //***************************************************************
1475 // Since 9A581, we have changed the definition of kKeychainRenamedSuffix from "_renamed" to "_renamed_".
1476 // Therefore, if "login_renamed1.keychain" exists and there is no plist, the user may have run into a
1477 // prior upgrade issue and clicked Reset. If we can successfully unlock login_renamed1.keychain with the
1478 // supplied password, then we will attempt to rename it to login.keychain if that file is empty, or with
1479 // "shortname.keychain" if it is not.
1481 if (loginRenamed1KeychainExists
&& (!loginKeychainExists
||
1482 (mSavedList
.searchList().size() == 1 && mSavedList
.member(demungeDLDbIdentifier(loginDLDbIdentifier
))) )) {
1485 Keychain
loginRenamed1KC(keychain(loginRenamed1DLDbIdentifier
));
1486 secnotice("KCLogin", "Attempting to unlock renamed KC \"%s\"",
1487 (loginRenamed1KC
) ? loginRenamed1KC
->name() : "<NULL>");
1488 loginRenamed1KC
->unlock(CssmData(const_cast<void *>(password
), passwordLength
));
1489 // if we get here, we unlocked it
1490 if (loginKeychainExists
) {
1492 int tmp_result
= ::stat(loginDotKeychain
.c_str(), &st
);
1493 if (tmp_result
== 0) {
1494 if (st
.st_size
<= kEmptyKeychainSizeInBytes
) {
1495 tmp_result
= ::unlink(loginDotKeychain
.c_str());
1496 tmp_result
= ::rename(loginRenamed1Keychain
.c_str(), loginDotKeychain
.c_str());
1497 } else if (!shortnameDotKeychainExists
) {
1498 tmp_result
= ::rename(loginRenamed1Keychain
.c_str(), shortnameDotKeychain
.c_str());
1499 shortnameDotKeychainExists
= (tmp_result
== 0);
1501 throw 1; // can't do anything with it except move it out of the way
1505 int tmp_result
= ::rename(loginRenamed1Keychain
.c_str(), loginDotKeychain
.c_str());
1506 loginKeychainExists
= (tmp_result
== 0);
1511 // we failed to unlock the login_renamed1.keychain file with the login password.
1512 // move it aside so we don't try to deal with it again.
1513 char pathbuf
[MAXPATHLEN
];
1514 std::string loginRenamedXXXKeychain
= keychainPath
;
1515 loginRenamedXXXKeychain
+= "login_renamed_XXX.keychain";
1516 ::strlcpy(pathbuf
, loginRenamedXXXKeychain
.c_str(), sizeof(pathbuf
));
1517 ::mkstemps(pathbuf
, 9); // 9 == strlen(".keychain")
1518 ::rename(loginRenamed1Keychain
.c_str(), pathbuf
);
1522 // is it token login?
1523 CFRef
<CFDictionaryRef
> tokenLoginContext
;
1524 CFRef
<CFStringRef
> smartCardPassword
;
1525 OSStatus tokenContextStatus
= TokenLoginGetContext(password
, passwordLength
, tokenLoginContext
.take());
1526 // if login.keychain does not exist at this point, create it
1527 if (!loginKeychainExists
|| (isReset
&& !loginKeychainDbExists
)) {
1528 // when we creating new KC and user is logged using token (i.e. smart card), we have to get
1529 // the password for that account first
1530 if (tokenContextStatus
== errSecSuccess
) {
1531 secnotice("KCLogin", "Going to create login keychain for sc login");
1532 AuthorizationRef authRef
;
1533 OSStatus status
= AuthorizationCreate(NULL
, NULL
, 0, &authRef
);
1534 if (status
== errSecSuccess
) {
1535 AuthorizationItem right
= { "com.apple.builtin.sc-kc-new-passphrase", 0, NULL
, 0 };
1536 AuthorizationItemSet rightSet
= { 1, &right
};
1538 uint32_t reason
, tries
;
1541 AuthorizationItem envRights
[] = {
1542 { AGENT_HINT_RETRY_REASON
, sizeof(reason
), &reason
, 0 },
1543 { AGENT_HINT_TRIES
, sizeof(tries
), &tries
, 0 }};
1545 AuthorizationItemSet envSet
= { sizeof(envRights
) / sizeof(*envRights
), envRights
};
1546 status
= AuthorizationCopyRights(authRef
, &rightSet
, &envSet
, kAuthorizationFlagDefaults
|kAuthorizationFlagInteractionAllowed
|kAuthorizationFlagExtendRights
, NULL
);
1547 if (status
== errSecSuccess
) {
1548 AuthorizationItemSet
*returnedInfo
;
1549 status
= AuthorizationCopyInfo(authRef
, NULL
, &returnedInfo
);
1550 if (status
== errSecSuccess
) {
1551 if (returnedInfo
&& (returnedInfo
->count
> 0)) {
1552 for (uint32_t index
= 0; index
< returnedInfo
->count
; index
++) {
1553 AuthorizationItem
&item
= returnedInfo
->items
[index
];
1554 if (!strcmp(AGENT_PASSWORD
, item
.name
)) {
1555 CFIndex len
= item
.valueLength
;
1557 secnotice("KCLogin", "User entered pwd");
1558 smartCardPassword
= CFStringCreateWithBytes(SecCFAllocatorZeroize(), (UInt8
*)item
.value
, (CFIndex
)len
, kCFStringEncodingUTF8
, TRUE
);
1559 memset(item
.value
, 0, len
);
1565 AuthorizationFreeItemSet(returnedInfo
);
1567 AuthorizationFree(authRef
, 0);
1571 // but don't add it to the search list yet; we'll do that later
1572 Keychain theKeychain
= makeKeychain(loginDLDbIdentifier
, false, true);
1573 secnotice("KCLogin", "Creating login keychain %s", (loginDLDbIdentifier
) ? loginDLDbIdentifier
.dbName() : "<NULL>");
1574 if (tokenContextStatus
== errSecSuccess
) {
1575 if (smartCardPassword
.get()) {
1576 CFIndex length
= CFStringGetLength(smartCardPassword
);
1577 CFIndex maxSize
= CFStringGetMaximumSizeForEncoding(length
, kCFStringEncodingUTF8
) + 1;
1578 char *buffer
= (char *)malloc(maxSize
);
1579 if (CFStringGetCString(smartCardPassword
, buffer
, maxSize
, kCFStringEncodingUTF8
)) {
1580 secnotice("KCLogin", "Keychain is created using password provided by sc user");
1581 theKeychain
->create((UInt32
)strlen(buffer
), buffer
);
1582 memset(buffer
, 0, maxSize
);
1584 secnotice("KCLogin", "Conversion failed");
1585 MacOSError::throwMe(errSecNotAvailable
);
1588 secnotice("KCLogin", "User did not provide kc password");
1589 MacOSError::throwMe(errSecNotAvailable
);
1592 theKeychain
->create(passwordLength
, password
);
1594 secnotice("KCLogin", "Login keychain created successfully");
1595 loginKeychainExists
= true;
1596 // Set the prefs for this new login keychain.
1597 loginKeychain(theKeychain
);
1598 // Login Keychain does not lock on sleep nor lock after timeout by default.
1599 theKeychain
->setSettings(INT_MAX
, false);
1600 loginUnlocked
= true;
1601 mSavedList
.revert(true);
1604 //***************************************************************
1605 // make plist changes after files have been renamed or created
1606 //***************************************************************
1608 // if the shortname keychain exists in the search list, either rename or remove the entry
1609 if (mSavedList
.member(demungeDLDbIdentifier(shortnameDLDbIdentifier
))) {
1610 if (shortnameDotKeychainExists
&& !mSavedList
.member(demungeDLDbIdentifier(shortnameDotDLDbIdentifier
))) {
1611 // change shortname to shortname.keychain (login.keychain will be added later if not present)
1612 secnotice("KCLogin", "Renaming %s to %s in keychain search list",
1613 (shortnameDLDbIdentifier
) ? shortnameDLDbIdentifier
.dbName() : "<NULL>",
1614 (shortnameDotDLDbIdentifier
) ? shortnameDotDLDbIdentifier
.dbName() : "<NULL>");
1615 mSavedList
.rename(demungeDLDbIdentifier(shortnameDLDbIdentifier
),
1616 demungeDLDbIdentifier(shortnameDotDLDbIdentifier
));
1617 } else if (!mSavedList
.member(demungeDLDbIdentifier(loginDLDbIdentifier
))) {
1618 // change shortname to login.keychain
1619 secnotice("KCLogin", "Renaming %s to %s in keychain search list",
1620 (shortnameDLDbIdentifier
) ? shortnameDLDbIdentifier
.dbName() : "<NULL>",
1621 (loginDLDbIdentifier
) ? loginDLDbIdentifier
.dbName() : "<NULL>");
1622 mSavedList
.rename(demungeDLDbIdentifier(shortnameDLDbIdentifier
),
1623 demungeDLDbIdentifier(loginDLDbIdentifier
));
1625 // already have login.keychain in list, and renaming to shortname.keychain isn't an option,
1626 // so just remove the entry
1627 secnotice("KCLogin", "Removing %s from keychain search list", (shortnameDLDbIdentifier
) ? shortnameDLDbIdentifier
.dbName() : "<NULL>");
1628 mSavedList
.remove(demungeDLDbIdentifier(shortnameDLDbIdentifier
));
1631 // note: save() will cause the plist to be unlinked if the only remaining entry is for login.keychain
1633 mSavedList
.revert(true);
1636 // make sure that login.keychain is in the search list
1637 if (!mSavedList
.member(demungeDLDbIdentifier(loginDLDbIdentifier
))) {
1638 secnotice("KCLogin", "Adding %s to keychain search list", (loginDLDbIdentifier
) ? loginDLDbIdentifier
.dbName() : "<NULL>");
1639 mSavedList
.add(demungeDLDbIdentifier(loginDLDbIdentifier
));
1641 mSavedList
.revert(true);
1644 // if we have a shortname.keychain, always include it in the plist (after login.keychain)
1645 if (shortnameDotKeychainExists
&& !mSavedList
.member(demungeDLDbIdentifier(shortnameDotDLDbIdentifier
))) {
1646 mSavedList
.add(demungeDLDbIdentifier(shortnameDotDLDbIdentifier
));
1648 mSavedList
.revert(true);
1651 // make sure that the default keychain is in the search list; if not, reset the default to login.keychain
1652 if (!mSavedList
.member(mSavedList
.defaultDLDbIdentifier())) {
1653 secnotice("KCLogin", "Changing default keychain to %s", (loginDLDbIdentifier
) ? loginDLDbIdentifier
.dbName() : "<NULL>");
1654 mSavedList
.defaultDLDbIdentifier(demungeDLDbIdentifier(loginDLDbIdentifier
));
1656 mSavedList
.revert(true);
1659 //***************************************************************
1660 // auto-unlock the login keychain(s)
1661 //***************************************************************
1662 // all our preflight fixups are finally done, so we can now attempt to unlock the login keychain
1664 OSStatus loginResult
= errSecSuccess
;
1665 if (!loginUnlocked
) {
1668 Keychain
theKeychain(keychain(loginDLDbIdentifier
));
1669 secnotice("KCLogin", "Attempting to unlock login keychain \"%s\"",
1670 (theKeychain
) ? theKeychain
->name() : "<NULL>");
1671 theKeychain
->unlock(CssmData(const_cast<void *>(password
), passwordLength
));
1672 loginUnlocked
= true;
1674 catch(const CssmError
&e
)
1676 loginResult
= e
.osStatus(); // save this result
1680 if (!loginUnlocked
|| tokenContextStatus
== errSecSuccess
) {
1681 Keychain
theKeychain(keychain(loginDLDbIdentifier
));
1682 bool tokenLoginDataUpdated
= false;
1684 for (UInt32 i
= 0; i
< 2; i
++) {
1685 loginResult
= errSecSuccess
;
1687 CFRef
<CFDictionaryRef
> tokenLoginData
;
1688 if (tokenLoginContext
) {
1689 OSStatus status
= TokenLoginGetLoginData(tokenLoginContext
, tokenLoginData
.take());
1690 if (status
!= errSecSuccess
) {
1691 if (tokenLoginDataUpdated
) {
1692 loginResult
= status
;
1695 // updating unlock key fails if it is not token login
1696 secnotice("KCLogin", "Error %d, reconstructing unlock data", (int)status
);
1697 status
= TokenLoginUpdateUnlockData(tokenLoginContext
, smartCardPassword
);
1698 if (status
== errSecSuccess
) {
1699 loginResult
= TokenLoginGetLoginData(tokenLoginContext
, tokenLoginData
.take());
1700 if (loginResult
!= errSecSuccess
) {
1703 tokenLoginDataUpdated
= true;
1709 // first try to unlock login keychain because if this fails, token keychain unlock fails as well
1710 if (tokenLoginData
) {
1711 secnotice("KCLogin", "Going to unlock keybag using scBlob");
1712 OSStatus status
= TokenLoginUnlockKeybag(tokenLoginContext
, tokenLoginData
);
1713 secnotice("KCLogin", "Keybag unlock result %d", (int)status
);
1715 CssmError::throwMe(status
); // to trigger login data regeneration
1720 key
.header().BlobType
= CSSM_KEYBLOB_RAW
;
1721 key
.header().Format
= CSSM_KEYBLOB_RAW_FORMAT_OCTET_STRING
;
1722 key
.header().AlgorithmId
= CSSM_ALGID_3DES_3KEY
;
1723 key
.header().KeyClass
= CSSM_KEYCLASS_SESSION_KEY
;
1724 key
.header().KeyUsage
= CSSM_KEYUSE_ENCRYPT
| CSSM_KEYUSE_DECRYPT
| CSSM_KEYATTR_EXTRACTABLE
;
1725 key
.header().KeyAttr
= 0;
1726 CFRef
<CFDataRef
> tokenLoginUnlockKey
;
1727 if (tokenLoginData
) {
1728 OSStatus status
= TokenLoginGetUnlockKey(tokenLoginContext
, tokenLoginUnlockKey
.take());
1730 CssmError::throwMe(status
); // to trigger login data regeneration
1731 key
.KeyData
= CssmData(tokenLoginUnlockKey
.get());
1733 key
.KeyData
= CssmData(const_cast<void *>(password
), passwordLength
);
1735 // unwrap it into the CSP (but keep it raw)
1736 UnwrapKey
unwrap(theKeychain
->csp(), CSSM_ALGID_NONE
);
1738 CssmData descriptiveData
;
1740 KeySpec(CSSM_KEYUSE_ANY
, CSSM_KEYATTR_EXTRACTABLE
),
1741 masterKey
, &descriptiveData
, NULL
);
1743 CssmClient::Db db
= theKeychain
->database();
1745 // create the keychain, using appropriate credentials
1746 Allocator
&alloc
= db
->allocator();
1747 AutoCredentials
cred(alloc
); // will leak, but we're quitting soon :-)
1749 // use this passphrase
1750 cred
+= TypedList(alloc
, CSSM_SAMPLE_TYPE_KEYCHAIN_LOCK
,
1751 new(alloc
) ListElement(CSSM_SAMPLE_TYPE_SYMMETRIC_KEY
),
1752 new(alloc
) ListElement(CssmData::wrap(theKeychain
->csp()->handle())),
1753 new(alloc
) ListElement(CssmData::wrap(masterKey
)),
1754 new(alloc
) ListElement(CssmData()));
1755 db
->authenticate(CSSM_DB_ACCESS_READ
, &cred
);
1757 loginUnlocked
= true;
1758 } catch (const CssmError
&e
) {
1759 if (tokenLoginData
&& !tokenLoginDataUpdated
) {
1760 // token login unlock key was invalid
1761 loginResult
= TokenLoginUpdateUnlockData(tokenLoginContext
, smartCardPassword
);
1762 if (loginResult
== errSecSuccess
) {
1763 tokenLoginDataUpdated
= true;
1768 loginResult
= e
.osStatus();
1775 // if "shortname.keychain" exists and is in the search list, attempt to auto-unlock it with the same password
1776 if (shortnameDotKeychainExists
&& mSavedList
.member(demungeDLDbIdentifier(shortnameDotDLDbIdentifier
))) {
1779 Keychain
shortnameDotKC(keychain(shortnameDotDLDbIdentifier
));
1780 secnotice("KCLogin", "Attempting to unlock short name keychain \"%s\"",
1781 (shortnameDotKC
) ? shortnameDotKC
->name() : "<NULL>");
1782 shortnameDotKC
->unlock(CssmData(const_cast<void *>(password
), passwordLength
));
1784 catch(const CssmError
&e
)
1786 // ignore; failure to unlock this keychain is not considered an error
1790 if (loginResult
!= errSecSuccess
) {
1791 MacOSError::throwMe(loginResult
);
1795 void StorageManager::stashLogin()
1797 OSStatus loginResult
= errSecSuccess
;
1799 DLDbIdentifier loginDLDbIdentifier
;
1801 mSavedList
.revert(true);
1802 loginDLDbIdentifier
= mSavedList
.loginDLDbIdentifier();
1805 secnotice("KCLogin", "StorageManager::stash: loginDLDbIdentifier is %s", (loginDLDbIdentifier
) ? loginDLDbIdentifier
.dbName() : "<NULL>");
1806 if (!loginDLDbIdentifier
)
1807 MacOSError::throwMe(errSecNoSuchKeychain
);
1812 Keychain
theKeychain(keychain(loginDLDbIdentifier
));
1813 secnotice("KCLogin", "Attempting to use stash for login keychain \"%s\"",
1814 (theKeychain
) ? theKeychain
->name() : "<NULL>");
1815 theKeychain
->stashCheck();
1817 catch(const CssmError
&e
)
1819 loginResult
= e
.osStatus(); // save this result
1823 if (loginResult
!= errSecSuccess
) {
1824 MacOSError::throwMe(loginResult
);
1828 void StorageManager::stashKeychain()
1830 OSStatus loginResult
= errSecSuccess
;
1832 DLDbIdentifier loginDLDbIdentifier
;
1834 mSavedList
.revert(true);
1835 loginDLDbIdentifier
= mSavedList
.loginDLDbIdentifier();
1838 secnotice("KCLogin", "StorageManager::stash: loginDLDbIdentifier is %s", (loginDLDbIdentifier
) ? loginDLDbIdentifier
.dbName() : "<NULL>");
1839 if (!loginDLDbIdentifier
)
1840 MacOSError::throwMe(errSecNoSuchKeychain
);
1844 Keychain
theKeychain(keychain(loginDLDbIdentifier
));
1845 secnotice("KCLogin", "Attempting to stash login keychain \"%s\"",
1846 (theKeychain
) ? theKeychain
->name() : "<NULL>");
1847 theKeychain
->stash();
1849 catch(const CssmError
&e
)
1851 loginResult
= e
.osStatus(); // save this result
1855 if (loginResult
!= errSecSuccess
) {
1856 MacOSError::throwMe(loginResult
);
1860 void StorageManager::logout()
1862 // nothing left to do here
1865 void StorageManager::changeLoginPassword(ConstStringPtr oldPassword
, ConstStringPtr newPassword
)
1867 StLock
<Mutex
>_(mMutex
);
1869 loginKeychain()->changePassphrase(oldPassword
, newPassword
);
1870 secnotice("KClogin", "Changed login keychain password successfully");
1874 void StorageManager::changeLoginPassword(UInt32 oldPasswordLength
, const void *oldPassword
, UInt32 newPasswordLength
, const void *newPassword
)
1876 StLock
<Mutex
>_(mMutex
);
1878 loginKeychain()->changePassphrase(oldPasswordLength
, oldPassword
, newPasswordLength
, newPassword
);
1879 secnotice("KClogin", "Changed login keychain password successfully");
1882 // Clear out the keychain search list and rename the existing login.keychain.
1884 void StorageManager::resetKeychain(Boolean resetSearchList
)
1886 StLock
<Mutex
>_(mMutex
);
1888 // Clear the keychain search list.
1889 Keychain keychain
= NULL
;
1890 DLDbIdentifier dldbi
;
1893 if ( resetSearchList
)
1895 StorageManager::KeychainList keychainList
;
1896 setSearchList(keychainList
);
1898 // Get a reference to the existing login keychain...
1899 // If we don't have one, we throw (not requiring a rename).
1901 keychain
= loginKeychain();
1902 } catch(const CommonError
& e
) {
1903 secnotice("KClogin", "Failed to open login keychain due to an error: %s", e
.what());
1905 // Set up fallback rename.
1906 dldbi
= loginKeychainDLDbIdentifer();
1909 if(::stat(dldbi
.dbName(), &exists
) != 0) {
1910 // no file exists, everything is fine
1911 secnotice("KClogin", "no file exists; resetKeychain() is done");
1918 // Rename the existing login.keychain (i.e. put it aside).
1920 CFMutableStringRef newName
= NULL
;
1921 newName
= CFStringCreateMutable(NULL
, 0);
1922 CFStringRef currName
= NULL
;
1924 currName
= CFStringCreateWithCString(NULL
, keychain
->name(), kCFStringEncodingUTF8
);
1926 currName
= CFStringCreateWithCString(NULL
, dldbi
.dbName(), kCFStringEncodingUTF8
);
1928 if ( newName
&& currName
)
1930 CFStringAppend(newName
, currName
);
1931 CFStringRef kcSuffix
= CFSTR(kKeychainSuffix
);
1932 CFStringRef kcDbSuffix
= CFSTR(kKeychainDbSuffix
);
1933 bool hasDbSuffix
= false;
1934 if ( CFStringHasSuffix(newName
, kcSuffix
) ) // remove the .keychain extension
1936 CFRange suffixRange
= CFStringFind(newName
, kcSuffix
, 0);
1937 CFStringFindAndReplace(newName
, kcSuffix
, CFSTR(""), suffixRange
, 0);
1939 if (CFStringHasSuffix(newName
, kcDbSuffix
)) {
1941 CFRange suffixRange
= CFStringFind(newName
, kcDbSuffix
, 0);
1942 CFStringFindAndReplace(newName
, kcDbSuffix
, CFSTR(""), suffixRange
, 0);
1945 CFStringAppend(newName
, CFSTR(kKeychainRenamedSuffix
)); // add "_renamed_"
1948 secnotice("KClogin", "attempting keychain rename to %@", newName
);
1949 renameUnique(keychain
, currName
, newName
, hasDbSuffix
);
1951 catch(const CommonError
& e
)
1953 // we need to release 'newName' & 'currName'
1954 secnotice("KClogin", "Failed to renameUnique due to an error: %s", e
.what());
1958 secnotice("KClogin", "Failed to renameUnique due to an unknown error");
1960 } // else, let the login call report a duplicate
1962 secnotice("KClogin", "don't have paths, quitting");
1967 CFRelease(currName
);
1969 catch(const CommonError
& e
) {
1970 secnotice("KClogin", "Failed to reset login keychain due to an error: %s", e
.what());
1974 // We either don't have a login keychain, or there was a
1975 // failure to rename the existing one.
1976 secnotice("KClogin", "Failed to reset keychain due to an unknown error");
1980 #pragma mark ____ File Related ____
1982 Keychain
StorageManager::make(const char *pathName
)
1984 return make(pathName
, true);
1987 Keychain
StorageManager::make(const char *pathName
, bool add
)
1989 return make(pathName
, add
, false);
1992 Keychain
StorageManager::make(const char *pathName
, bool add
, bool isReset
) {
1993 return makeKeychain(makeDLDbIdentifier(pathName
), add
, isReset
);
1996 DLDbIdentifier
StorageManager::makeDLDbIdentifier(const char *pathName
) {
1997 StLock
<Mutex
>_(mMutex
);
1999 string fullPathName
;
2000 if ( pathName
[0] == '/' )
2001 fullPathName
= pathName
;
2004 // Get Home directory from environment.
2007 case kSecPreferencesDomainUser
:
2009 const char *homeDir
= getenv("HOME");
2010 if (homeDir
== NULL
)
2012 // If $HOME is unset get the current user's home directory
2013 // from the passwd file.
2014 uid_t uid
= geteuid();
2015 if (!uid
) uid
= getuid();
2016 struct passwd
*pw
= getpwuid(uid
);
2018 MacOSError::throwMe(errSecParam
);
2019 homeDir
= pw
->pw_dir
;
2021 fullPathName
= homeDir
;
2024 case kSecPreferencesDomainSystem
:
2028 assert(false); // invalid domain for this
2031 fullPathName
+= "/Library/Keychains/";
2032 fullPathName
+= pathName
;
2035 const CSSM_NET_ADDRESS
*DbLocation
= NULL
; // NULL for keychains
2036 const CSSM_VERSION
*version
= NULL
;
2037 uint32 subserviceId
= 0;
2038 CSSM_SERVICE_TYPE subserviceType
= CSSM_SERVICE_DL
| CSSM_SERVICE_CSP
;
2039 const CssmSubserviceUid
ssuid(gGuidAppleCSPDL
, version
,
2040 subserviceId
, subserviceType
);
2041 DLDbIdentifier
dlDbIdentifier(ssuid
, fullPathName
.c_str(), DbLocation
);
2042 return dlDbIdentifier
;
2045 Keychain
StorageManager::makeLoginAuthUI(const Item
*item
, bool isReset
)
2047 StLock
<Mutex
>_(mMutex
);
2049 // Create a login/default keychain for the user using UI.
2050 // The user can cancel out of the operation, or create a new login keychain.
2051 // If auto-login is turned off, the user will be asked for their login password.
2053 OSStatus result
= errSecSuccess
;
2054 Keychain keychain
; // We return this keychain.
2056 // Set up the Auth ref to bring up UI.
2058 AuthorizationItem
*currItem
, *authEnvirItemArrayPtr
= NULL
;
2059 AuthorizationRef authRef
= NULL
;
2062 result
= AuthorizationCreate(NULL
, NULL
, kAuthorizationFlagDefaults
, &authRef
);
2064 MacOSError::throwMe(result
);
2066 AuthorizationEnvironment envir
;
2067 envir
.count
= 6; // up to 6 hints can be used.
2068 authEnvirItemArrayPtr
= (AuthorizationItem
*)malloc(sizeof(AuthorizationItem
) * envir
.count
);
2069 if ( !authEnvirItemArrayPtr
)
2070 MacOSError::throwMe(errAuthorizationInternal
);
2072 currItem
= envir
.items
= authEnvirItemArrayPtr
;
2075 // 1st Hint (optional): The keychain item's account attribute string.
2076 // When item is specified, we assume an 'add' operation is being attempted.
2079 SecKeychainAttribute attr
= { kSecAccountItemAttr
, 255, &buff
};
2084 (*item
)->getAttribute(attr
, &actLen
);
2088 actLen
= 0; // This item didn't have the account attribute, so don't display one in the UI.
2091 currItem
->name
= AGENT_HINT_ATTR_NAME
; // name str that identifies this hint as attr name
2092 if ( actLen
) // Fill in the hint if we have an account attr
2094 if ( actLen
>= sizeof(buff
) )
2095 buff
[sizeof(buff
)-1] = 0;
2098 currItem
->valueLength
= strlen(buff
)+1;
2099 currItem
->value
= buff
;
2103 currItem
->valueLength
= 0;
2104 currItem
->value
= NULL
;
2106 currItem
->flags
= 0;
2109 // 2nd Hint (optional): The item's keychain full path.
2112 char* currDefaultName
= NULL
;
2115 currDefaultName
= (char*)defaultKeychain()->name(); // Use the name if we have it.
2116 currItem
->name
= AGENT_HINT_LOGIN_KC_NAME
; // Name str that identifies this hint as kc path
2117 currItem
->valueLength
= (currDefaultName
) ? strlen(currDefaultName
) : 0;
2118 currItem
->value
= (currDefaultName
) ? (void*)currDefaultName
: (void*)"";
2119 currItem
->flags
= 0;
2128 // 3rd Hint (required): check if curr default keychain is unavailable.
2129 // This is determined by the parent not existing.
2131 currItem
->name
= AGENT_HINT_LOGIN_KC_EXISTS_IN_KC_FOLDER
;
2132 Boolean loginUnavail
= false;
2135 Keychain defaultKC
= defaultKeychain();
2136 if ( !defaultKC
->exists() )
2137 loginUnavail
= true;
2139 catch(...) // login.keychain not present
2142 currItem
->valueLength
= sizeof(Boolean
);
2143 currItem
->value
= (void*)&loginUnavail
;
2144 currItem
->flags
= 0;
2147 // 4th Hint (required): userName
2150 currItem
->name
= AGENT_HINT_LOGIN_KC_USER_NAME
;
2151 char* uName
= getenv("USER");
2152 string userName
= uName
? uName
: "";
2153 if ( userName
.length() == 0 )
2155 uid_t uid
= geteuid();
2156 if (!uid
) uid
= getuid();
2157 struct passwd
*pw
= getpwuid(uid
); // fallback case...
2159 userName
= pw
->pw_name
;
2162 if ( userName
.length() == 0 ) // did we ultimately get one?
2163 MacOSError::throwMe(errAuthorizationInternal
);
2165 currItem
->value
= (void*)userName
.c_str();
2166 currItem
->valueLength
= userName
.length();
2167 currItem
->flags
= 0;
2170 // 5th Hint (required): flags if user has more than 1 keychain (used for a later warning when reset to default).
2173 currItem
->name
= AGENT_HINT_LOGIN_KC_USER_HAS_OTHER_KCS_STR
;
2174 Boolean moreThanOneKCExists
= false;
2176 // if item is NULL, then this is a user-initiated full reset
2177 if (item
&& mSavedList
.searchList().size() > 1)
2178 moreThanOneKCExists
= true;
2180 currItem
->value
= &moreThanOneKCExists
;
2181 currItem
->valueLength
= sizeof(Boolean
);
2182 currItem
->flags
= 0;
2185 // 6th Hint (required): If no item is involved, this is a user-initiated full reset.
2186 // We want to suppress the "do you want to reset to defaults?" panel in this case.
2189 currItem
->name
= AGENT_HINT_LOGIN_KC_SUPPRESS_RESET_PANEL
;
2190 Boolean suppressResetPanel
= (item
== NULL
) ? TRUE
: FALSE
;
2191 currItem
->valueLength
= sizeof(Boolean
);
2192 currItem
->value
= (void*)&suppressResetPanel
;
2193 currItem
->flags
= 0;
2196 // Set up the auth rights and make the auth call.
2198 AuthorizationItem authItem
= { LOGIN_KC_CREATION_RIGHT
, 0 , NULL
, 0 };
2199 AuthorizationRights rights
= { 1, &authItem
};
2200 AuthorizationFlags flags
= kAuthorizationFlagDefaults
| kAuthorizationFlagInteractionAllowed
| kAuthorizationFlagExtendRights
;
2201 result
= AuthorizationCopyRights(authRef
, &rights
, &envir
, flags
, NULL
);
2203 MacOSError::throwMe(result
);
2206 resetKeychain(true); // Clears the plist, moves aside existing login.keychain
2208 catch (...) // can throw if no existing login.keychain is found
2211 login(authRef
, (UInt32
)userName
.length(), userName
.c_str(), isReset
); // Create login.keychain
2212 keychain
= loginKeychain(); // Get newly-created login keychain
2213 defaultKeychain(keychain
); // Set it to be the default
2215 free(authEnvirItemArrayPtr
);
2216 AuthorizationFree(authRef
, kAuthorizationFlagDefaults
);
2221 // clean up allocations, then rethrow error
2222 if ( authEnvirItemArrayPtr
)
2223 free(authEnvirItemArrayPtr
);
2225 AuthorizationFree(authRef
, kAuthorizationFlagDefaults
);
2232 Keychain
StorageManager::defaultKeychainUI(Item
&item
)
2234 StLock
<Mutex
>_(mMutex
);
2236 Keychain returnedKeychain
;
2239 returnedKeychain
= defaultKeychain(); // If we have one, return it.
2240 if ( returnedKeychain
->exists() )
2241 return returnedKeychain
;
2243 catch(...) // We could have one, but it isn't available (i.e. on a un-mounted volume).
2246 if ( globals().getUserInteractionAllowed() )
2248 returnedKeychain
= makeLoginAuthUI(&item
, false); // If no Keychains is present, one will be created.
2249 if ( !returnedKeychain
)
2250 MacOSError::throwMe(errSecInvalidKeychain
); // Something went wrong...
2253 MacOSError::throwMe(errSecInteractionNotAllowed
); // If UI isn't allowed, return an error.
2255 return returnedKeychain
;
2259 StorageManager::addToDomainList(SecPreferencesDomain domain
,
2260 const char* dbName
, const CSSM_GUID
&guid
, uint32 subServiceType
)
2262 StLock
<Mutex
>_(mMutex
);
2264 if (domain
== kSecPreferencesDomainDynamic
)
2265 MacOSError::throwMe(errSecInvalidPrefsDomain
);
2267 // make the identifier
2268 CSSM_VERSION version
= {0, 0};
2269 DLDbIdentifier id
= DLDbListCFPref::makeDLDbIdentifier (guid
, version
, 0,
2270 subServiceType
, dbName
, NULL
);
2272 if (domain
== mDomain
)
2274 // manipulate the user's list
2276 mSavedList
.revert(true);
2277 mSavedList
.add(demungeDLDbIdentifier(id
));
2281 KCEventNotifier::PostKeychainEvent(kSecKeychainListChangedEvent
);
2285 // manipulate the other list
2286 DLDbListCFPref(domain
).add(id
);
2291 StorageManager::isInDomainList(SecPreferencesDomain domain
,
2292 const char* dbName
, const CSSM_GUID
&guid
, uint32 subServiceType
)
2294 StLock
<Mutex
>_(mMutex
);
2296 if (domain
== kSecPreferencesDomainDynamic
)
2297 MacOSError::throwMe(errSecInvalidPrefsDomain
);
2299 CSSM_VERSION version
= {0, 0};
2300 DLDbIdentifier id
= DLDbListCFPref::makeDLDbIdentifier (guid
, version
, 0,
2301 subServiceType
, dbName
, NULL
);
2303 // determine the list to search
2305 if (domain
== mDomain
)
2307 result
= mSavedList
.member(demungeDLDbIdentifier(id
));
2311 result
= DLDbListCFPref(domain
).member(demungeDLDbIdentifier(id
));
2317 MacOSError::throwMe(errSecNoSuchKeychain
);
2322 StorageManager::removeFromDomainList(SecPreferencesDomain domain
,
2323 const char* dbName
, const CSSM_GUID
&guid
, uint32 subServiceType
)
2325 StLock
<Mutex
>_(mMutex
);
2327 if (domain
== kSecPreferencesDomainDynamic
)
2328 MacOSError::throwMe(errSecInvalidPrefsDomain
);
2330 // make the identifier
2331 CSSM_VERSION version
= {0, 0};
2332 DLDbIdentifier id
= DLDbListCFPref::makeDLDbIdentifier (guid
, version
, 0,
2333 subServiceType
, dbName
, NULL
);
2335 if (domain
== mDomain
)
2337 // manipulate the user's list
2339 mSavedList
.revert(true);
2340 mSavedList
.remove(demungeDLDbIdentifier(id
));
2344 KCEventNotifier::PostKeychainEvent(kSecKeychainListChangedEvent
);
2348 // manipulate the other list
2349 DLDbListCFPref(domain
).remove(id
);
2354 StorageManager::keychainOwnerPermissionsValidForDomain(const char* path
, SecPreferencesDomain domain
)
2358 const char* sysPrefDir
= "/Library/Preferences";
2359 const char* errMsg
= "Will not set default";
2360 char* mustOwnDir
= NULL
;
2361 struct passwd
* pw
= NULL
;
2364 uid_t uid
= geteuid();
2365 if (!uid
) uid
= getuid();
2367 // our (e)uid must own the appropriate preferences or home directory
2368 // for the specified preference domain whose default we will be modifying
2370 case kSecPreferencesDomainUser
:
2371 mustOwnDir
= getenv("HOME");
2372 if (mustOwnDir
== NULL
) {
2374 if (!pw
) return false;
2375 mustOwnDir
= pw
->pw_dir
;
2378 case kSecPreferencesDomainSystem
:
2379 mustOwnDir
= (char*)sysPrefDir
;
2381 case kSecPreferencesDomainCommon
:
2382 mustOwnDir
= (char*)sysPrefDir
;
2388 if (mustOwnDir
!= NULL
) {
2390 if ( (stat(mustOwnDir
, &dsb
) != 0) || (dsb
.st_uid
!= uid
) ) {
2391 fprintf(stderr
, "%s: UID=%d does not own directory %s\n", errMsg
, (int)uid
, mustOwnDir
);
2392 mustOwnDir
= NULL
; // will return below after calling endpwent()
2399 if (mustOwnDir
== NULL
)
2402 // check that file actually exists
2403 if (stat(path
, &sb
) != 0) {
2404 fprintf(stderr
, "%s: file %s does not exist\n", errMsg
, path
);
2409 if (sb
.st_flags
& (SF_IMMUTABLE
| UF_IMMUTABLE
)) {
2410 fprintf(stderr
, "%s: file %s is immutable\n", errMsg
, path
);
2415 if (sb
.st_uid
!= uid
) {
2416 fprintf(stderr
, "%s: file %s is owned by UID=%d, but we have UID=%d\n",
2417 errMsg
, path
, (int)sb
.st_uid
, (int)uid
);
2423 perms
|= 0600; // must have owner read/write permission set
2424 if (sb
.st_mode
!= perms
) {
2425 fprintf(stderr
, "%s: file %s does not have the expected permissions\n", errMsg
, path
);
2429 // user owns file and can read/write it