2 * Copyright (c) 2000-2002 Apple Computer, Inc. All Rights Reserved.
4 * The contents of this file constitute Original Code as defined in and are
5 * subject to the Apple Public Source License Version 1.2 (the 'License').
6 * You may not use this file except in compliance with the License. Please obtain
7 * a copy of the License at http://www.apple.com/publicsource and read it before
10 * This Original Code and all software distributed under the License are
11 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESS
12 * OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, INCLUDING WITHOUT
13 * LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
14 * PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. Please see the License for the
15 * specific language governing rights and limitations under the License.
20 File: StorageManager.cpp
22 Contains: Working with multiple keychains
26 #include "StorageManager.h"
27 #include "KCEventNotifier.h"
29 #include <Security/cssmapple.h>
30 #include <sys/types.h>
31 #include <sys/param.h>
34 #include <CoreServices/../Frameworks/CarbonCore.framework/Headers/MacErrors.h>
37 #include <Security/AuthorizationTags.h>
38 #include <Security/AuthSession.h>
39 #include <Security/debugging.h>
40 #include <Security/SecCFTypes.h>
41 #include <Security/AuthSession.h>
42 #include <Security/SecurityAgentClient.h>
43 #include <Security/ssclient.h>
48 using namespace CssmClient
;
49 using namespace KeychainCore
;
51 // normal debug calls, which get stubbed out for deployment builds
52 #define x_debug(str) secdebug("KClogin",(str))
53 #define x_debug1(fmt,arg1) secdebug("KClogin",(fmt),(arg1))
54 #define x_debug2(fmt,arg1,arg2) secdebug("KClogin",(fmt),(arg1),(arg2))
56 //-----------------------------------------------------------------------------------
58 StorageManager::StorageManager() :
59 mSavedList(kSecPreferencesDomainUser
),
60 mCommonList(kSecPreferencesDomainCommon
),
61 mDomain(kSecPreferencesDomainUser
),
64 // get session attributes
65 SessionAttributeBits sessionAttrs
;
66 if (OSStatus err
= SessionGetInfo(callerSecuritySession
,
68 CssmError::throwMe(err
);
70 // If this is the root session, switch to system preferences.
71 // (In SecurityServer debug mode, you'll get a (fake) root session
72 // that has graphics access. Ignore that to help testing.)
73 if ((sessionAttrs
& sessionIsRoot
)
74 IFDEBUG( && !(sessionAttrs
& sessionHasGraphicAccess
))) {
75 secdebug("storagemgr", "switching to system preferences");
76 mDomain
= kSecPreferencesDomainSystem
;
77 mSavedList
.set(kSecPreferencesDomainSystem
);
81 // Create KC if it doesn't exist
83 StorageManager::keychain(const DLDbIdentifier
&dLDbIdentifier
)
85 StLock
<Mutex
> _(mLock
);
86 return _keychain(dLDbIdentifier
);
90 StorageManager::_keychain(const DLDbIdentifier
&dLDbIdentifier
)
95 KeychainMap::iterator it
= mKeychains
.find(dLDbIdentifier
);
96 if (it
!= mKeychains
.end())
99 // The keychain is not in our cache. Create it.
100 Module
module(dLDbIdentifier
.ssuid().guid());
102 if (dLDbIdentifier
.ssuid().subserviceType() & CSSM_SERVICE_CSP
)
103 dl
= SSCSPDL(module);
107 dl
->subserviceId(dLDbIdentifier
.ssuid().subserviceId());
108 dl
->version(dLDbIdentifier
.ssuid().version());
109 Db
db(dl
, dLDbIdentifier
.dbName());
111 Keychain
keychain(db
);
112 // Add the keychain to the cache.
113 mKeychains
.insert(KeychainMap::value_type(dLDbIdentifier
, &*keychain
));
118 // Called from KeychainImpl's destructor remove it from the map.
120 StorageManager::removeKeychain(const DLDbIdentifier
&dLDbIdentifier
, KeychainImpl
*keychainImpl
)
122 // @@@ Work out locking StLock<Mutex> _(mLock);
123 KeychainMap::iterator it
= mKeychains
.find(dLDbIdentifier
);
124 if (it
!= mKeychains
.end() && it
->second
== keychainImpl
)
125 mKeychains
.erase(it
);
128 // if a database is key-unlockable, authenticate it with any matching unlock keys found in the KC list
129 void StorageManager::setDefaultCredentials(const Db
&db
)
132 CssmAutoData
index(db
->allocator());
133 if (!db
->getUnlockKeyIndex(index
.get()))
134 return; // no suggested index (probably not a CSPDL)
136 TrackingAllocator
alloc(CssmAllocator::standard());
138 KCCursor
search(createCursor(CSSM_DL_DB_RECORD_SYMMETRIC_KEY
, NULL
));
139 CssmAutoData
keyLabel(CssmAllocator::standard());
140 keyLabel
= StringData("SYSKC**");
141 keyLabel
.append(index
);
142 static const CSSM_DB_ATTRIBUTE_INFO infoLabel
= {
143 CSSM_DB_ATTRIBUTE_NAME_AS_STRING
,
145 CSSM_DB_ATTRIBUTE_FORMAT_BLOB
147 search
->add(CSSM_DB_EQUAL
, infoLabel
, keyLabel
.get());
149 // could run a loop below to catch *all* eligible keys,
150 // but that's stretching it; and beware CSP scope if you add this...
151 AutoCredentials
cred(alloc
);
153 if (search
->next(keyItem
)) {
154 CssmClient::Key key
= dynamic_cast<KeyItem
&>(*keyItem
).key();
156 // create AccessCredentials from that key. Still allow interactive unlock
157 const CssmKey
&masterKey
= key
;
158 CSSM_CSP_HANDLE cspHandle
= key
->csp()->handle();
159 cred
+= TypedList(alloc
, CSSM_SAMPLE_TYPE_KEYCHAIN_LOCK
,
160 new(alloc
) ListElement(CSSM_WORDID_SYMMETRIC_KEY
),
161 new(alloc
) ListElement(CssmData::wrap(cspHandle
)),
162 new(alloc
) ListElement(CssmData::wrap(masterKey
)));
163 cred
+= TypedList(alloc
, CSSM_SAMPLE_TYPE_KEYCHAIN_LOCK
,
164 new(alloc
) ListElement(CSSM_SAMPLE_TYPE_KEYCHAIN_PROMPT
));
166 secdebug("storagemgr", "authenticating %s for default key credentials", db
->name());
167 db
->authenticate(db
->accessRequest(), &cred
);
170 secdebug("storagemgr", "setDefaultCredentials for %s abandoned due to exception", db
->name());
174 // Create KC if it doesn't exist, add it to the search list if it exists and is not already on it.
176 StorageManager::makeKeychain(const DLDbIdentifier
&dLDbIdentifier
, bool add
)
182 StLock
<Mutex
> _(mLock
);
183 keychain
= _keychain(dLDbIdentifier
);
187 mSavedList
.revert(false);
188 DLDbList searchList
= mSavedList
.searchList();
189 if (find(searchList
.begin(), searchList
.end(), dLDbIdentifier
) != searchList
.end())
190 return keychain
; // Keychain is already in the searchList.
192 mCommonList
.revert(false);
193 searchList
= mCommonList
.searchList();
194 if (find(searchList
.begin(), searchList
.end(), dLDbIdentifier
) != searchList
.end())
195 return keychain
; // Keychain is already in the commonList don't add it to the searchList.
197 // If the keychain doesn't exist don't bother adding it to the search list yet.
198 if (!keychain
->exists())
201 // The keychain exists and is not in our search list add it to the search
202 // list and the cache.
203 mSavedList
.revert(true);
204 mSavedList
.add(dLDbIdentifier
);
212 // Make sure we are not holding mLock when we post this event.
213 KCEventNotifier::PostKeychainEvent(kSecKeychainListChangedEvent
);
220 StorageManager::created(const Keychain
&keychain
) // Be notified a Keychain just got created.
222 DLDbIdentifier dLDbIdentifier
= keychain
->dLDbIdentifier();
223 bool defaultChanged
= false;
226 StLock
<Mutex
> _(mLock
);
228 mSavedList
.revert(true);
229 // If we don't have a default Keychain yet. Make the newly created keychain the default.
230 if (!mSavedList
.defaultDLDbIdentifier())
232 mSavedList
.defaultDLDbIdentifier(dLDbIdentifier
);
233 defaultChanged
= true;
236 // Add the keychain to the search list prefs.
237 mSavedList
.add(dLDbIdentifier
);
241 // Make sure we are not holding mLock when we post these events.
242 KCEventNotifier::PostKeychainEvent(kSecKeychainListChangedEvent
);
246 KCEventNotifier::PostKeychainEvent(kSecDefaultChangedEvent
, dLDbIdentifier
);
251 StorageManager::createCursor(SecItemClass itemClass
, const SecKeychainAttributeList
*attrList
)
253 KeychainList searchList
;
254 getSearchList(searchList
);
255 return KCCursor(searchList
, itemClass
, attrList
);
259 StorageManager::createCursor(const SecKeychainAttributeList
*attrList
)
261 KeychainList searchList
;
262 getSearchList(searchList
);
263 return KCCursor(searchList
, attrList
);
267 StorageManager::lockAll()
269 SecurityServer::ClientSession
ss(CssmAllocator::standard(), CssmAllocator::standard());
274 StorageManager::defaultKeychain()
276 Keychain theKeychain
;
278 StLock
<Mutex
> _(mLock
);
279 mSavedList
.revert(false);
280 DLDbIdentifier
defaultDLDbIdentifier(mSavedList
.defaultDLDbIdentifier());
281 if (defaultDLDbIdentifier
)
283 theKeychain
= _keychain(defaultDLDbIdentifier
);
287 if (theKeychain
/* && theKeychain->exists() */)
290 MacOSError::throwMe(errSecNoDefaultKeychain
);
294 StorageManager::defaultKeychain(const Keychain
&keychain
)
296 DLDbIdentifier oldDefaultId
;
297 DLDbIdentifier
newDefaultId(keychain
->dLDbIdentifier());
299 StLock
<Mutex
> _(mLock
);
300 oldDefaultId
= mSavedList
.defaultDLDbIdentifier();
301 mSavedList
.revert(true);
302 mSavedList
.defaultDLDbIdentifier(newDefaultId
);
306 if (!(oldDefaultId
== newDefaultId
))
308 // Make sure we are not holding mLock when we post this event.
309 KCEventNotifier::PostKeychainEvent(kSecDefaultChangedEvent
, newDefaultId
);
314 StorageManager::defaultKeychain(SecPreferencesDomain domain
)
316 if (domain
== mDomain
)
317 return defaultKeychain();
320 DLDbIdentifier
defaultDLDbIdentifier(DLDbListCFPref(domain
).defaultDLDbIdentifier());
321 if (defaultDLDbIdentifier
)
322 return keychain(defaultDLDbIdentifier
);
324 MacOSError::throwMe(errSecNoDefaultKeychain
);
329 StorageManager::defaultKeychain(SecPreferencesDomain domain
, const Keychain
&keychain
)
331 if (domain
== mDomain
)
332 defaultKeychain(keychain
);
334 DLDbListCFPref(domain
).defaultDLDbIdentifier(keychain
->dLDbIdentifier());
338 StorageManager::loginKeychain()
340 Keychain theKeychain
;
342 StLock
<Mutex
> _(mLock
);
343 mSavedList
.revert(false);
344 DLDbIdentifier
loginDLDbIdentifier(mSavedList
.loginDLDbIdentifier());
345 if (loginDLDbIdentifier
)
347 theKeychain
= _keychain(loginDLDbIdentifier
);
351 if (theKeychain
&& theKeychain
->exists())
354 MacOSError::throwMe(errSecNoSuchKeychain
);
358 StorageManager::loginKeychain(Keychain keychain
)
360 StLock
<Mutex
> _(mLock
);
361 mSavedList
.revert(true);
362 mSavedList
.loginDLDbIdentifier(keychain
->dLDbIdentifier());
367 StorageManager::size()
369 StLock
<Mutex
> _(mLock
);
370 mSavedList
.revert(false);
371 mCommonList
.revert(false);
372 return mSavedList
.searchList().size() + mCommonList
.searchList().size();
376 StorageManager::at(unsigned int ix
)
378 StLock
<Mutex
> _(mLock
);
379 mSavedList
.revert(false);
380 DLDbList dLDbList
= mSavedList
.searchList();
381 if (ix
< dLDbList
.size())
383 return _keychain(dLDbList
[ix
]);
387 ix
-= dLDbList
.size();
388 mCommonList
.revert(false);
389 DLDbList commonList
= mCommonList
.searchList();
390 if (ix
>= commonList
.size())
391 MacOSError::throwMe(errSecInvalidKeychain
);
393 return _keychain(commonList
[ix
]);
398 StorageManager::operator[](unsigned int ix
)
403 void StorageManager::rename(Keychain keychain
, const char* newName
)
405 // This is not a generic purpose rename method for keychains.
406 // The keychain doesn't remain in the cache.
408 bool changedDefault
= false;
409 DLDbIdentifier newDLDbIdentifier
;
411 StLock
<Mutex
> _(mLock
);
412 mSavedList
.revert(true);
413 DLDbIdentifier defaultId
= mSavedList
.defaultDLDbIdentifier();
415 // Find the keychain object for the given ref
416 DLDbIdentifier dLDbIdentifier
= keychain
->dLDbIdentifier();
418 // Remove it from the saved list
419 mSavedList
.remove(dLDbIdentifier
);
420 if (dLDbIdentifier
== defaultId
)
423 // Actually rename the database on disk.
424 keychain
->database()->rename(newName
);
426 newDLDbIdentifier
= keychain
->dLDbIdentifier();
428 // Now update the keychain map to use the newDLDbIdentifier
429 KeychainMap::iterator it
= mKeychains
.find(dLDbIdentifier
);
430 if (it
!= mKeychains
.end())
432 mKeychains
.erase(it
);
433 mKeychains
.insert(KeychainMap::value_type(newDLDbIdentifier
, keychain
));
436 // If this was the default keychain change it accordingly
438 mSavedList
.defaultDLDbIdentifier(newDLDbIdentifier
);
443 // @@@ We need a kSecKeychainRenamedEvent so other clients can close this keychain and move on with life.
444 //KCEventNotifier::PostKeychainEvent(kSecKeychainRenamedEvent);
446 // Make sure we are not holding mLock when we post these events.
447 KCEventNotifier::PostKeychainEvent(kSecKeychainListChangedEvent
);
450 KCEventNotifier::PostKeychainEvent(kSecDefaultChangedEvent
, newDLDbIdentifier
);
453 void StorageManager::renameUnique(Keychain keychain
, CFStringRef newName
)
455 bool doneCreating
= false;
459 char newNameCString
[MAXPATHLEN
];
460 if ( CFStringGetCString(newName
, newNameCString
, MAXPATHLEN
, kCFStringEncodingUTF8
) ) // make sure it fits in MAXPATHLEN, etc.
462 // Construct the new name...
464 CFMutableStringRef newNameCFStr
= NULL
;
465 newNameCFStr
= CFStringCreateMutable(NULL
, MAXPATHLEN
);
468 CFStringAppendFormat(newNameCFStr
, NULL
, CFSTR("%s%d"), &newNameCString
, index
);
469 CFStringAppend(newNameCFStr
, CFSTR(kKeychainSuffix
)); // add .keychain
470 char toUseBuff2
[MAXPATHLEN
];
471 if ( CFStringGetCString(newNameCFStr
, toUseBuff2
, MAXPATHLEN
, kCFStringEncodingUTF8
) ) // make sure it fits in MAXPATHLEN, etc.
474 if ( lstat(toUseBuff2
, &filebuf
) )
476 rename(keychain
, toUseBuff2
);
483 doneCreating
= true; // failure to get c string.
484 CFRelease(newNameCFStr
);
487 doneCreating
= false; // failure to create mutable string.
490 doneCreating
= false; // failure to get the string (i.e. > MAXPATHLEN?)
492 while (!doneCreating
&& index
!= INT_MAX
);
495 void StorageManager::remove(const KeychainList
&kcsToRemove
, bool deleteDb
)
497 bool unsetDefault
= false;
499 StLock
<Mutex
> _(mLock
);
500 mSavedList
.revert(true);
501 DLDbIdentifier defaultId
= mSavedList
.defaultDLDbIdentifier();
502 for (KeychainList::const_iterator ix
= kcsToRemove
.begin(); ix
!= kcsToRemove
.end(); ++ix
)
504 // Find the keychain object for the given ref
505 Keychain keychainToRemove
= *ix
;
506 DLDbIdentifier dLDbIdentifier
= keychainToRemove
->dLDbIdentifier();
508 // Remove it from the saved list
509 mSavedList
.remove(dLDbIdentifier
);
510 if (dLDbIdentifier
== defaultId
)
515 keychainToRemove
->database()->deleteDb();
516 // Now remove it from the map
517 KeychainMap::iterator it
= mKeychains
.find(dLDbIdentifier
);
518 if (it
== mKeychains
.end())
520 mKeychains
.erase(it
);
525 mSavedList
.defaultDLDbIdentifier(DLDbIdentifier());
530 // Make sure we are not holding mLock when we post these events.
531 KCEventNotifier::PostKeychainEvent(kSecKeychainListChangedEvent
);
534 KCEventNotifier::PostKeychainEvent(kSecDefaultChangedEvent
);
538 StorageManager::getSearchList(KeychainList
&keychainList
)
540 StLock
<Mutex
> _(mLock
);
541 mSavedList
.revert(false);
542 mCommonList
.revert(false);
544 // Merge mSavedList and common list
545 DLDbList dLDbList
= mSavedList
.searchList();
546 DLDbList commonList
= mCommonList
.searchList();
548 result
.reserve(dLDbList
.size() + commonList
.size());
550 for (DLDbList::const_iterator it
= dLDbList
.begin(); it
!= dLDbList
.end(); ++it
)
552 Keychain
keychain(_keychain(*it
));
553 result
.push_back(keychain
);
556 for (DLDbList::const_iterator it
= commonList
.begin(); it
!= commonList
.end(); ++it
)
558 Keychain
keychain(_keychain(*it
));
559 result
.push_back(keychain
);
562 keychainList
.swap(result
);
566 StorageManager::setSearchList(const KeychainList
&keychainList
)
568 DLDbList commonList
= mCommonList
.searchList();
570 // Strip out the common list part from the end of the search list.
571 KeychainList::const_iterator it_end
= keychainList
.end();
572 DLDbList::const_reverse_iterator end_common
= commonList
.rend();
573 for (DLDbList::const_reverse_iterator it_common
= commonList
.rbegin(); it_common
!= end_common
; ++it_common
)
575 // Eliminate common entries from the end of the passed in keychainList.
576 if (it_end
== keychainList
.begin())
580 if (!((*it_end
)->dLDbIdentifier() == *it_common
))
587 /* it_end now points one past the last element in keychainList which is not in commonList. */
588 DLDbList searchList
, oldSearchList(mSavedList
.searchList());
589 for (KeychainList::const_iterator it
= keychainList
.begin(); it
!= it_end
; ++it
)
591 searchList
.push_back((*it
)->dLDbIdentifier());
595 // Set the current searchlist to be what was passed in, the old list will be freed
596 // upon exit of this stackframe.
597 StLock
<Mutex
> _(mLock
);
598 mSavedList
.revert(true);
599 mSavedList
.searchList(searchList
);
603 if (!(oldSearchList
== searchList
))
605 // Make sure we are not holding mLock when we post this event.
606 KCEventNotifier::PostKeychainEvent(kSecKeychainListChangedEvent
);
611 StorageManager::getSearchList(SecPreferencesDomain domain
, KeychainList
&keychainList
)
613 if (domain
== mDomain
)
615 StLock
<Mutex
> _(mLock
);
616 mSavedList
.revert(false);
617 convertList(keychainList
, mSavedList
.searchList());
621 convertList(keychainList
, DLDbListCFPref(domain
).searchList());
626 StorageManager::setSearchList(SecPreferencesDomain domain
, const KeychainList
&keychainList
)
629 convertList(searchList
, keychainList
);
631 if (domain
== mDomain
)
633 DLDbList
oldSearchList(mSavedList
.searchList());
635 // Set the current searchlist to be what was passed in, the old list will be freed
636 // upon exit of this stackframe.
637 StLock
<Mutex
> _(mLock
);
638 mSavedList
.revert(true);
639 mSavedList
.searchList(searchList
);
643 if (!(oldSearchList
== searchList
))
645 // Make sure we are not holding mLock when we post this event.
646 KCEventNotifier::PostKeychainEvent(kSecKeychainListChangedEvent
);
651 DLDbListCFPref(domain
).searchList(searchList
);
656 StorageManager::domain(SecPreferencesDomain domain
)
658 StLock
<Mutex
> _(mLock
);
659 if (domain
== mDomain
)
665 case kSecPreferencesDomainSystem
:
666 secdebug("storagemgr", "switching to system domain"); break;
667 case kSecPreferencesDomainUser
:
668 secdebug("storagemgr", "switching to user domain (uid %d)", getuid()); break;
670 secdebug("storagemgr", "switching to weird prefs domain %d", domain
); break;
675 mSavedList
.set(domain
);
679 StorageManager::optionalSearchList(CFTypeRef keychainOrArray
, KeychainList
&keychainList
)
681 if (!keychainOrArray
)
682 getSearchList(keychainList
);
685 CFTypeID typeID
= CFGetTypeID(keychainOrArray
);
686 if (typeID
== CFArrayGetTypeID())
687 convertToKeychainList(CFArrayRef(keychainOrArray
), keychainList
);
688 else if (typeID
== gTypes().KeychainImpl
.typeID
)
689 keychainList
.push_back(KeychainImpl::required(SecKeychainRef(keychainOrArray
)));
691 MacOSError::throwMe(paramErr
);
697 StorageManager::convertToKeychainList(CFArrayRef keychainArray
, KeychainList
&keychainList
)
699 assert(keychainArray
);
700 CFIndex count
= CFArrayGetCount(keychainArray
);
701 KeychainList
keychains(count
);
702 for (CFIndex ix
= 0; ix
< count
; ++ix
)
704 keychains
[ix
] = KeychainImpl::required(SecKeychainRef(CFArrayGetValueAtIndex(keychainArray
, ix
)));
707 keychainList
.swap(keychains
);
711 StorageManager::convertFromKeychainList(const KeychainList
&keychainList
)
713 CFRef
<CFMutableArrayRef
> keychainArray(CFArrayCreateMutable(NULL
, keychainList
.size(), &kCFTypeArrayCallBacks
));
715 for (KeychainList::const_iterator ix
= keychainList
.begin(); ix
!= keychainList
.end(); ++ix
)
717 SecKeychainRef keychainRef
= (*ix
)->handle();
718 CFArrayAppendValue(keychainArray
, keychainRef
);
719 CFRelease(keychainRef
);
722 // Counter the CFRelease that CFRef<> is about to do when keychainArray goes out of scope.
723 CFRetain(keychainArray
);
724 return keychainArray
;
727 void StorageManager::convertList(DLDbList
&ids
, const KeychainList
&kcs
)
730 result
.reserve(kcs
.size());
731 for (KeychainList::const_iterator ix
= kcs
.begin(); ix
!= kcs
.end(); ++ix
)
733 result
.push_back((*ix
)->dLDbIdentifier());
738 void StorageManager::convertList(KeychainList
&kcs
, const DLDbList
&ids
)
741 result
.reserve(ids
.size());
742 for (DLDbList::const_iterator ix
= ids
.begin(); ix
!= ids
.end(); ++ix
)
744 Keychain
keychain(_keychain(*ix
));
745 result
.push_back(keychain
);
750 #pragma mark ÑÑÑÑ Login Functions ÑÑÑÑ
752 void StorageManager::login(AuthorizationRef authRef
, UInt32 nameLength
, const char* name
)
754 AuthorizationItemSet
* info
= NULL
;
755 OSStatus result
= AuthorizationCopyInfo(authRef
, NULL
, &info
); // get the results of the copy rights call.
756 Boolean created
= false;
757 if ( result
== noErr
&& info
->count
)
759 // Grab the password from the auth context (info) and create the keychain...
761 AuthorizationItem
* currItem
= info
->items
;
762 for (UInt32 index
= 1; index
<= info
->count
; index
++) //@@@plugin bug won't return a specific context.
764 if (strcmp(currItem
->name
, kAuthorizationEnvironmentPassword
) == 0)
766 // creates the login keychain with the specified password
769 login(nameLength
, name
, currItem
->valueLength
, currItem
->value
);
781 AuthorizationFreeItemSet(info
);
784 MacOSError::throwMe(errAuthorizationInternal
);
787 void StorageManager::login(ConstStringPtr name
, ConstStringPtr password
)
789 if ( name
== NULL
|| password
== NULL
)
790 MacOSError::throwMe(paramErr
);
792 login(name
[0], name
+ 1, password
[0], password
+ 1);
795 void StorageManager::login(UInt32 nameLength
, const void *name
, UInt32 passwordLength
, const void *password
)
797 x_debug("StorageManager::login: entered");
798 mSavedList
.revert(true);
799 if (passwordLength
!= 0 && password
== NULL
)
801 x_debug("StorageManager::login: invalid argument (NULL password)");
802 MacOSError::throwMe(paramErr
);
805 DLDbIdentifier
loginDLDbIdentifier(mSavedList
.loginDLDbIdentifier());
806 x_debug1("StorageManager::login: loginDLDbIdentifier is %s", (loginDLDbIdentifier
) ? loginDLDbIdentifier
.dbName() : "<NULL>");
807 if (!loginDLDbIdentifier
)
808 MacOSError::throwMe(errSecNoSuchKeychain
);
810 Keychain
theKeychain(keychain(loginDLDbIdentifier
));
813 x_debug2("Attempting to unlock login keychain %s with %d-character password", (theKeychain
) ? theKeychain
->name() : "<NULL>", (unsigned int)passwordLength
);
814 theKeychain
->unlock(CssmData(const_cast<void *>(password
), passwordLength
));
815 x_debug("Login keychain unlocked successfully");
817 catch(const CssmError
&e
)
819 if (e
.osStatus() != CSSMERR_DL_DATASTORE_DOESNOT_EXIST
)
821 x_debug1("Creating login keychain %s", (loginDLDbIdentifier
) ? loginDLDbIdentifier
.dbName() : "<NULL>");
822 theKeychain
->create(passwordLength
, password
);
823 x_debug("Login keychain created successfully");
824 // Set the prefs for this new login keychain.
825 loginKeychain(theKeychain
);
826 // Login Keychain does not lock on sleep nor lock after timeout by default.
827 theKeychain
->setSettings(INT_MAX
, false);
831 void StorageManager::logout()
833 // nothing left to do here
836 void StorageManager::changeLoginPassword(ConstStringPtr oldPassword
, ConstStringPtr newPassword
)
838 loginKeychain()->changePassphrase(oldPassword
, newPassword
);
839 secdebug("KClogin", "Changed login keychain password successfully");
843 void StorageManager::changeLoginPassword(UInt32 oldPasswordLength
, const void *oldPassword
, UInt32 newPasswordLength
, const void *newPassword
)
845 loginKeychain()->changePassphrase(oldPasswordLength
, oldPassword
, newPasswordLength
, newPassword
);
846 secdebug("KClogin", "Changed login keychain password successfully");
849 // Clear out the keychain search list and rename the existing login.keychain.
851 void StorageManager::resetKeychain(Boolean resetSearchList
)
853 // Clear the keychain search list.
855 CFArrayRef emptySearchList
= nil
;
858 if ( resetSearchList
)
860 emptySearchList
= CFArrayCreate(NULL
, NULL
, 0, NULL
);
861 StorageManager::KeychainList keychainList
;
862 convertToKeychainList(emptySearchList
, keychainList
);
863 setSearchList(keychainList
);
865 // Get a reference to the existing login keychain...
866 // If we don't have one, we throw (not requiring a rename).
868 Keychain keychain
= loginKeychain();
870 // Rename the existing login.keychain (i.e. put it aside).
872 CFMutableStringRef newName
= NULL
;
873 newName
= CFStringCreateMutable(NULL
, 0);
874 CFStringRef currName
= NULL
;
875 currName
= CFStringCreateWithCString(NULL
, keychain
->name(), kCFStringEncodingUTF8
);
876 if ( newName
&& currName
)
878 CFStringAppend(newName
, currName
);
879 CFStringRef kcSuffix
= CFSTR(kKeychainSuffix
);
880 if ( CFStringHasSuffix(newName
, kcSuffix
) ) // remove the .keychain extension
882 CFRange suffixRange
= CFStringFind(newName
, kcSuffix
, 0);
883 CFStringFindAndReplace(newName
, kcSuffix
, CFSTR(""), suffixRange
, 0);
885 CFStringAppend(newName
, CFSTR(kKeychainRenamedSuffix
)); // add "_renamed"
888 renameUnique(keychain
, newName
);
892 // we need to release 'newName' & 'currName'
894 } // else, let the login call report a duplicate
902 // We either don't have a login keychain, or there was a
903 // failure to rename the existing one.
905 if ( emptySearchList
)
906 CFRelease(emptySearchList
);
909 #pragma mark ÑÑÑÑ File Related ÑÑÑÑ
911 Keychain
StorageManager::make(const char *pathName
)
913 return make(pathName
, true);
916 Keychain
StorageManager::make(const char *pathName
, bool add
)
919 if ( pathName
[0] == '/' )
920 fullPathName
= pathName
;
923 // Get Home directory from environment.
925 case kSecPreferencesDomainUser
:
927 const char *homeDir
= getenv("HOME");
930 // If $HOME is unset get the current user's home directory from the passwd file.
931 uid_t uid
= geteuid();
932 if (!uid
) uid
= getuid();
933 struct passwd
*pw
= getpwuid(uid
);
935 MacOSError::throwMe(paramErr
);
936 homeDir
= pw
->pw_dir
;
938 fullPathName
= homeDir
;
941 case kSecPreferencesDomainSystem
:
945 assert(false); // invalid domain for this
948 fullPathName
+= "/Library/Keychains/";
949 fullPathName
+= pathName
;
952 const CSSM_NET_ADDRESS
*DbLocation
= NULL
; // NULL for keychains
953 const CSSM_VERSION
*version
= NULL
;
954 uint32 subserviceId
= 0;
955 CSSM_SERVICE_TYPE subserviceType
= CSSM_SERVICE_DL
| CSSM_SERVICE_CSP
;
956 const CssmSubserviceUid
ssuid(gGuidAppleCSPDL
, version
,
957 subserviceId
, subserviceType
);
958 DLDbIdentifier
dLDbIdentifier(ssuid
, fullPathName
.c_str(), DbLocation
);
959 return makeKeychain(dLDbIdentifier
, add
);
962 Keychain
StorageManager::makeLoginAuthUI(Item
&item
)
964 // Create a login/default keychain for the user using UI.
965 // The user can cancel out of the operation, or create a new login keychain.
966 // If auto-login is turned off, the user will be asked for their login password.
968 OSStatus result
= noErr
;
969 Keychain keychain
= NULL
; // We return this keychain.
971 // Set up the Auth ref to bring up UI.
973 AuthorizationRef authRef
= NULL
;
974 result
= AuthorizationCreate(NULL
, NULL
, kAuthorizationFlagDefaults
, &authRef
);
975 if ( result
!= noErr
)
976 MacOSError::throwMe(errAuthorizationInternal
);
977 AuthorizationEnvironment envir
;
978 envir
.count
= 5; // 5 hints are used.
979 AuthorizationItem
* authEnvirItemArrayPtr
= (AuthorizationItem
*)malloc(sizeof(AuthorizationItem
) * envir
.count
);
980 if ( !authEnvirItemArrayPtr
)
983 AuthorizationFree(authRef
, kAuthorizationFlagDefaults
);
984 MacOSError::throwMe(errAuthorizationInternal
);
986 envir
.items
= authEnvirItemArrayPtr
;
987 AuthorizationItem
* currItem
= authEnvirItemArrayPtr
;
989 // 1st Hint (optional): The keychain item's account attribute string.
990 // When item is specified, we assume an 'add' operation is being attempted.
993 SecKeychainAttribute attr
= { kSecAccountItemAttr
, 255, &buff
};
996 item
->getAttribute(attr
, &actLen
);
1000 actLen
= 0; // This item didn't have the account attribute, so don't display one in the UI.
1002 currItem
->name
= AGENT_HINT_ATTR_NAME
; // name str that identifies this hint as attr name
1003 if ( actLen
) // Fill in the hint if we have a 'srvr' attr
1009 currItem
->valueLength
= strlen(buff
)+1;
1010 currItem
->value
= buff
;
1014 currItem
->valueLength
= 0;
1015 currItem
->value
= NULL
;
1017 currItem
->flags
= 0;
1019 // 2nd Hint (optional): The item's keychain full path.
1022 char* currDefaultName
= NULL
;
1025 currDefaultName
= (char*)globals().storageManager
.defaultKeychain()->name(); // Use the name if we have it.
1026 currItem
->name
= AGENT_HINT_LOGIN_KC_NAME
; // Name str that identifies this hint as kc path
1027 currItem
->valueLength
= strlen(currDefaultName
);
1028 currItem
->value
= (void*)currDefaultName
;
1029 currItem
->flags
= 0;
1038 // 3rd Hint (optional): If curr default keychain is unavailable.
1039 // This is determined by the parent not existing.
1041 currItem
->name
= AGENT_HINT_LOGIN_KC_EXISTS_IN_KC_FOLDER
;
1042 Boolean loginUnavail
= false;
1045 Keychain defaultKC
= defaultKeychain();
1046 if ( !defaultKC
->exists() )
1047 loginUnavail
= true;
1049 catch(...) // login.keychain not present
1052 currItem
->valueLength
= sizeof(Boolean
);
1053 currItem
->value
= (void*)&loginUnavail
;
1054 currItem
->flags
= 0;
1056 // 4th Hint (required) userName
1059 currItem
->name
= AGENT_HINT_LOGIN_KC_USER_NAME
;
1060 char* uName
= getenv("USER");
1061 string userName
= uName
? uName
: "";
1062 if ( userName
.length() == 0 )
1064 uid_t uid
= geteuid();
1065 if (!uid
) uid
= getuid();
1066 struct passwd
*pw
= getpwuid(uid
); // fallback case...
1068 userName
= pw
->pw_name
;
1071 if ( userName
.length() != 0 ) // did we ultimately get one?
1073 currItem
->value
= (void*)userName
.c_str();
1074 currItem
->valueLength
= userName
.length();
1076 else // trouble getting user name; can't continue...
1079 AuthorizationFree(authRef
, kAuthorizationFlagDefaults
);
1080 free(authEnvirItemArrayPtr
);
1081 MacOSError::throwMe(errAuthorizationInternal
);
1083 currItem
->flags
= 0;
1085 // 5th Hint (optional) flags if user has more than 1 keychain (used for a later warning when reset to default).
1087 currItem
++; // last hint...
1088 currItem
->name
= AGENT_HINT_LOGIN_KC_USER_HAS_OTHER_KCS_STR
;
1089 Boolean moreThanOneKCExists
= false;
1091 StLock
<Mutex
> _(mLock
);
1092 if (mSavedList
.searchList().size() > 1)
1093 moreThanOneKCExists
= true;
1095 currItem
->value
= &moreThanOneKCExists
;
1096 currItem
->valueLength
= sizeof(Boolean
);
1097 currItem
->flags
= 0;
1099 // Set up the auth rights and make the auth call.
1101 AuthorizationItem authItem
= { LOGIN_KC_CREATION_RIGHT
, 0 , NULL
, 0};
1102 AuthorizationRights rights
= { 1, &authItem
};
1103 result
= AuthorizationCopyRights(authRef
, &rights
, &envir
, kAuthorizationFlagDefaults
| kAuthorizationFlagInteractionAllowed
| kAuthorizationFlagExtendRights
, NULL
);
1104 free(authEnvirItemArrayPtr
); // done with the auth items.
1105 if ( result
== errAuthorizationSuccess
) // On success, revert to defaults.
1109 resetKeychain(true); // Clears the plist, moves aside existing login.keychain
1110 login(authRef
, userName
.length(), userName
.c_str()); // Creates a login.keychain
1111 keychain
= loginKeychain(); // Return it.
1112 defaultKeychain(keychain
); // Set it to the default.
1116 // Reset failed, login.keychain creation failed, or setting it to default.
1117 // We need to release 'authRef'...
1121 AuthorizationFree(authRef
, kAuthorizationFlagDefaults
);
1123 MacOSError::throwMe(result
); // Any other error means we don't return a keychain.
1127 Keychain
StorageManager::defaultKeychainUI(Item
&item
)
1129 Keychain returnedKeychain
= NULL
;
1132 returnedKeychain
= globals().storageManager
.defaultKeychain(); // If we have one, return it.
1133 if ( returnedKeychain
->exists() )
1134 return returnedKeychain
;
1136 catch(...) // We could have one, but it isn't available (i.e. on a un-mounted volume).
1139 if ( globals().getUserInteractionAllowed() )
1141 returnedKeychain
= makeLoginAuthUI(item
); // If no Keychains Ä is present, one will be created.
1142 if ( !returnedKeychain
)
1143 MacOSError::throwMe(errSecInvalidKeychain
); // Something went wrong...
1146 MacOSError::throwMe(errSecInteractionNotAllowed
); // If UI isn't allowed, return an error.
1148 return returnedKeychain
;