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>
32 #include <CoreServices/../Frameworks/CarbonCore.framework/Headers/MacErrors.h>
35 #include <Security/Authorization.h>
36 #include <Security/AuthorizationTags.h>
37 #include <Security/AuthSession.h>
38 #include <Security/debugging.h>
39 #include <Security/SecCFTypes.h>
43 #include "DefaultKeychain.h"
45 using namespace CssmClient
;
46 using namespace KeychainCore
;
48 StorageManager::StorageManager() :
56 // Create KC if it doesn't exist
58 StorageManager::keychain(const DLDbIdentifier
&dLDbIdentifier
)
60 StLock
<Mutex
> _(mLock
);
61 return _keychain(dLDbIdentifier
);
65 StorageManager::_keychain(const DLDbIdentifier
&dLDbIdentifier
)
67 KeychainMap::iterator it
= mKeychains
.find(dLDbIdentifier
);
68 if (it
!= mKeychains
.end())
71 // The keychain is not in our cache. Create it.
72 Module
module(dLDbIdentifier
.ssuid().guid());
74 if (dLDbIdentifier
.ssuid().subserviceType() & CSSM_SERVICE_CSP
)
79 dl
->subserviceId(dLDbIdentifier
.ssuid().subserviceId());
80 dl
->version(dLDbIdentifier
.ssuid().version());
81 Db
db(dl
, dLDbIdentifier
.dbName());
82 Keychain
keychain(db
);
84 // Add the keychain to the cache.
85 mKeychains
.insert(KeychainMap::value_type(dLDbIdentifier
, keychain
));
89 // Create KC if it doesn't exist, add it to the search list if it exists and is not already on it.
91 StorageManager::makeKeychain(const DLDbIdentifier
&dLDbIdentifier
)
93 Keychain
keychain(keychain(dLDbIdentifier
));
96 StLock
<Mutex
> _(mLock
);
97 if (find(mSearchList
.begin(), mSearchList
.end(), keychain
) != mSearchList
.end())
99 // This keychain is already on our search list.
103 // If the keychain doesn't exist don't bother adding it to the search list yet.
104 if (!keychain
->exists())
107 // The keychain exists and is not in our search list add it to the search
108 // list and the cache. Then inform mMultiDLDb.
109 mSavedList
.revert(true);
110 mSavedList
.add(dLDbIdentifier
);
113 // @@@ Will happen again when kSecKeychainListChangedEvent notification is received.
117 // Make sure we are not holding mLock when we post this event.
118 KCEventNotifier::PostKeychainEvent(kSecKeychainListChangedEvent
);
124 StorageManager::created(const Keychain
&keychain
) // Be notified a Keychain just got created.
126 DLDbIdentifier dLDbIdentifier
= keychain
->dLDbIdentifier();
129 StLock
<Mutex
> _(mLock
);
131 // If we don't have a default Keychain yet. Make the newly created keychain the default.
132 DefaultKeychain
&defaultKeychain
= globals().defaultKeychain
;
133 if (!defaultKeychain
.isSet())
134 defaultKeychain
.dLDbIdentifier(dLDbIdentifier
);
136 // Add the keychain to the search list and the cache. Then inform mMultiDLDb.
137 mSavedList
.revert(true);
138 mSavedList
.add(dLDbIdentifier
);
141 // @@@ Will happen again when kSecKeychainListChangedEvent notification is received.
145 // Make sure we are not holding mLock when we post this event.
146 KCEventNotifier::PostKeychainEvent(kSecKeychainListChangedEvent
);
150 StorageManager::createCursor(SecItemClass itemClass
, const SecKeychainAttributeList
*attrList
)
152 StLock
<Mutex
> _(mLock
);
153 return KCCursor(mSearchList
, itemClass
, attrList
);
157 StorageManager::createCursor(const SecKeychainAttributeList
*attrList
)
159 StLock
<Mutex
> _(mLock
);
160 return KCCursor(mSearchList
, attrList
);
164 StorageManager::lockAll()
166 // Make a snapshot of all known keychains while holding mLock.
167 KeychainList keychainList
;
169 StLock
<Mutex
> _(mLock
);
170 for (KeychainMap::iterator ix
= mKeychains
.begin(); ix
!= mKeychains
.end(); ix
++)
171 keychainList
.push_back(ix
->second
);
174 // Lock each active keychain after having released mLock since locking keychains
175 // will send notifications.
176 for (KeychainList::iterator ix
= keychainList
.begin(); ix
!= keychainList
.end(); ++ix
)
178 Keychain keychain
= *ix
;
179 if (keychain
->isActive())
185 StorageManager::_doReload()
187 KeychainList newList
;
188 newList
.reserve(mSavedList
.size());
189 for (CssmClient::DLDbList::iterator ix
= mSavedList
.begin(); ix
!= mSavedList
.end(); ++ix
)
191 Keychain
keychain(_keychain(*ix
));
192 newList
.push_back(keychain
);
194 mSearchList
.swap(newList
);
198 StorageManager::reload(bool force
)
200 StLock
<Mutex
> _(mLock
);
205 StorageManager::_reload(bool force
)
207 // Reinitialize list from CFPrefs if changed. When force is true force a prefs revert now.
208 if (mSavedList
.revert(force
))
213 StorageManager::size()
215 StLock
<Mutex
> _(mLock
);
217 return mSearchList
.size();
221 StorageManager::at(unsigned int ix
)
223 StLock
<Mutex
> _(mLock
);
225 if (ix
>= mSearchList
.size())
226 MacOSError::throwMe(errSecInvalidKeychain
);
228 return mSearchList
.at(ix
);
232 StorageManager::operator[](unsigned int ix
)
237 void StorageManager::remove(const KeychainList
&kcsToRemove
, bool deleteDb
)
239 bool unsetDefault
= false;
241 StLock
<Mutex
> _(mLock
);
242 mSavedList
.revert(true);
243 DLDbIdentifier defaultId
= globals().defaultKeychain
.dLDbIdentifier();
244 for (KeychainList::const_iterator ix
= kcsToRemove
.begin(); ix
!= kcsToRemove
.end(); ++ix
)
246 // Find the keychain object for the given ref
247 Keychain keychainToRemove
= *ix
;
248 DLDbIdentifier dLDbIdentifier
= keychainToRemove
->dLDbIdentifier();
250 // Remove it from the saved list
251 mSavedList
.remove(dLDbIdentifier
);
252 if (dLDbIdentifier
== defaultId
)
257 keychainToRemove
->database()->deleteDb();
258 // Now remove it from the map
259 KeychainMap::iterator it
= mKeychains
.find(dLDbIdentifier
);
260 if (it
== mKeychains
.end())
262 mKeychains
.erase(it
);
269 // Make sure we are not holding mLock when we post this event.
270 KCEventNotifier::PostKeychainEvent(kSecKeychainListChangedEvent
);
274 // Make sure we are not holding mLock when we call this since it posts an event.
275 globals().defaultKeychain
.unset();
280 StorageManager::getSearchList(KeychainList
&keychainList
)
282 // Make a copy of the searchList
283 StLock
<Mutex
> _(mLock
);
284 StorageManager::KeychainList
searchList(mSearchList
);
286 // Return the copy of the list.
287 keychainList
.swap(searchList
);
291 StorageManager::setSearchList(const KeychainList
&keychainList
)
293 // Make a copy of the passed in searchList
294 StorageManager::KeychainList
keychains(keychainList
);
296 // Set the current searchlist to be what was passed in, the old list will be freed
297 // upon exit of this stackframe.
298 StLock
<Mutex
> _(mLock
);
299 mSearchList
.swap(keychains
);
303 StorageManager::optionalSearchList(CFTypeRef keychainOrArray
, KeychainList
&keychainList
)
305 if (!keychainOrArray
)
306 getSearchList(keychainList
);
309 CFTypeID typeID
= CFGetTypeID(keychainOrArray
);
310 if (typeID
== CFArrayGetTypeID())
311 convertToKeychainList(CFArrayRef(keychainOrArray
), keychainList
);
312 else if (typeID
== gTypes().keychain
.typeId
)
313 keychainList
.push_back(gTypes().keychain
.required(SecKeychainRef(keychainOrArray
)));
315 MacOSError::throwMe(paramErr
);
321 StorageManager::convertToKeychainList(CFArrayRef keychainArray
, KeychainList
&keychainList
)
323 assert(keychainArray
);
324 CFIndex count
= CFArrayGetCount(keychainArray
);
325 KeychainList
keychains(count
);
326 CFClass
<KeychainImpl
, SecKeychainRef
, errSecInvalidKeychain
> &kcClass
= gTypes().keychain
;
327 for (CFIndex ix
= 0; ix
< count
; ++ix
)
329 keychains
[ix
] = kcClass
.required(SecKeychainRef(CFArrayGetValueAtIndex(keychainArray
, ix
)));
332 keychainList
.swap(keychains
);
336 StorageManager::convertFromKeychainList(const KeychainList
&keychainList
)
338 CFRef
<CFMutableArrayRef
> keychainArray(CFArrayCreateMutable(NULL
, keychainList
.size(), &kCFTypeArrayCallBacks
));
340 CFClass
<KeychainImpl
, SecKeychainRef
, errSecInvalidKeychain
> &kcClass
= gTypes().keychain
;
341 for (KeychainList::const_iterator ix
= keychainList
.begin(); ix
!= keychainList
.end(); ++ix
)
343 SecKeychainRef keychainRef
= kcClass
.handle(**ix
);
344 CFArrayAppendValue(keychainArray
, keychainRef
);
345 CFRelease(keychainRef
);
348 // Counter the CFRelease that CFRef<> is about to do when keychainArray goes out of scope.
349 CFRetain(keychainArray
);
350 return keychainArray
;
355 #pragma mark ÑÑÑÑ Login Functions ÑÑÑÑ
357 void StorageManager::login(ConstStringPtr name
, ConstStringPtr password
)
359 if ( name
== NULL
|| password
== NULL
)
360 MacOSError::throwMe(paramErr
);
362 login(name
[0], name
+ 1, password
[0], password
+ 1);
365 void StorageManager::login(UInt32 nameLength
, const void *name
, UInt32 passwordLength
, const void *password
)
367 // @@@ set up the login session on behalf of loginwindow
368 // @@@ (this code should migrate into loginwindow)
370 debug("KClogin", "setting up login session");
371 if (OSStatus ssnErr
= SessionCreate(sessionKeepCurrentBootstrap
,
372 sessionHasGraphicAccess
| sessionHasTTY
))
373 debug("KClogin", "session setup failed status=%ld", ssnErr
);
376 if (name
== NULL
|| (passwordLength
!= 0 && password
== NULL
))
377 MacOSError::throwMe(paramErr
);
379 // Make sure name is zero terminated
380 string
theName(reinterpret_cast<const char *>(name
), nameLength
);
381 Keychain keychain
= make(theName
.c_str());
384 keychain
->unlock(CssmData(const_cast<void *>(password
), passwordLength
));
385 debug("KClogin", "keychain unlock successful");
387 catch(const CssmError
&e
)
389 if (e
.osStatus() != CSSMERR_DL_DATASTORE_DOESNOT_EXIST
)
391 debug("KClogin", "creating login keychain");
392 keychain
->create(passwordLength
, password
);
393 // Login Keychain does not lock on sleep nor lock after timeout by default.
394 keychain
->setSettings(INT_MAX
, false);
397 // @@@ Create a authorization credential for the current user.
398 debug("KClogin", "creating login authorization");
399 const AuthorizationItem envList
[] =
401 { kAuthorizationEnvironmentUsername
, nameLength
, const_cast<void *>(name
), 0 },
402 { kAuthorizationEnvironmentPassword
, passwordLength
, const_cast<void *>(password
), 0 },
403 { kAuthorizationEnvironmentShared
, 0, NULL
, 0 }
405 const AuthorizationEnvironment environment
=
407 sizeof(envList
) / sizeof(*envList
),
408 const_cast<AuthorizationItem
*>(envList
)
410 if (OSStatus authErr
= AuthorizationCreate(NULL
, &environment
,
411 kAuthorizationFlagExtendRights
| kAuthorizationFlagPreAuthorize
, NULL
))
412 debug("KClogin", "failed to create login auth, status=%ld", authErr
);
416 void StorageManager::logout()
418 // nothing left to do here
421 void StorageManager::changeLoginPassword(ConstStringPtr oldPassword
, ConstStringPtr newPassword
)
423 globals().defaultKeychain
.keychain()->changePassphrase(oldPassword
, newPassword
);
427 void StorageManager::changeLoginPassword(UInt32 oldPasswordLength
, const void *oldPassword
, UInt32 newPasswordLength
, const void *newPassword
)
429 globals().defaultKeychain
.keychain()->changePassphrase(oldPasswordLength
, oldPassword
, newPasswordLength
, newPassword
);
432 #pragma mark ÑÑÑÑ File Related ÑÑÑÑ
434 Keychain
StorageManager::make(const char *pathName
)
437 if ( pathName
[0] == '/' )
438 fullPathName
= pathName
;
441 // Get Home directory from environment.
442 const char *homeDir
= getenv("HOME");
445 // If $HOME is unset get the current users home directory from the passwd file.
446 struct passwd
*pw
= getpwuid(getuid());
448 MacOSError::throwMe(paramErr
);
450 homeDir
= pw
->pw_dir
;
453 fullPathName
= homeDir
;
454 fullPathName
+= "/Library/Keychains/";
455 fullPathName
+= pathName
;
458 const CSSM_NET_ADDRESS
*DbLocation
= NULL
; // NULL for keychains
459 const CSSM_VERSION
*version
= NULL
;
460 uint32 subserviceId
= 0;
461 CSSM_SERVICE_TYPE subserviceType
= CSSM_SERVICE_DL
| CSSM_SERVICE_CSP
;
462 const CssmSubserviceUid
ssuid(gGuidAppleCSPDL
, version
,
463 subserviceId
, subserviceType
);
464 DLDbIdentifier
dLDbIdentifier(ssuid
, fullPathName
.c_str(), DbLocation
);
465 return makeKeychain(dLDbIdentifier
);
469 StorageManager::keychainSchemaFor(const CssmClient::Db
&db
)
472 KeychainSchema
schema(db
);
473 pair
<KeychainSchemaSet::iterator
, bool> result
= mKeychainSchemaSet
.insert(db
);
476 return *result
.first
;