2 * Copyright (c) 2000-2001 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
24 Copyright: 2000 by Apple Computer, Inc., all rights reserved.
29 #include "StorageManager.h"
30 #include "KCEventNotifier.h"
32 #include <Security/cssmapple.h>
33 #include <sys/types.h>
35 #include <CoreServices/../Frameworks/CarbonCore.framework/Headers/MacErrors.h>
38 #include <Security/Authorization.h>
39 #include <Security/AuthorizationTags.h>
40 #include <Security/AuthSession.h>
41 #include <Security/debugging.h>
45 #include "DefaultKeychain.h"
47 using namespace CssmClient
;
48 using namespace KeychainCore
;
50 StorageManager::StorageManager() :
53 mMultiDLDb(mSavedList
.list(), true) // Passinng true enables use of Secure Storage
57 // Create KC if it doesn't exist
59 StorageManager::keychain(const DLDbIdentifier
&dLDbIdentifier
)
61 //StLock<Mutex> _(mKeychainsLock);
62 KeychainMap::iterator it
= mKeychains
.find(dLDbIdentifier
);
63 if (it
!= mKeychains
.end())
66 // The keychain is not in our cache. Create it.
67 Keychain
keychain(mMultiDLDb
->database(dLDbIdentifier
));
69 // Add the keychain to the cache.
70 mKeychains
.insert(KeychainMap::value_type(dLDbIdentifier
, keychain
));
74 // Create KC if it doesn't exist
76 StorageManager::makeKeychain(const DLDbIdentifier
&dLDbIdentifier
)
78 Keychain
keychain(keychain(dLDbIdentifier
));
80 const vector
<DLDbIdentifier
> &list
= mMultiDLDb
->list();
81 if (find(list
.begin(), list
.end(), dLDbIdentifier
) != list
.end())
83 // The dLDbIdentifier for this keychain is already on our search list.
87 // If the keychain doesn't exist don't bother adding it to the search list yet.
88 if (!keychain
->exists())
91 // The keychain exists and is not in our search list add it to the search
92 // list and the cache. Then inform mMultiDLDb.
93 mSavedList
.revert(true);
94 mSavedList
.add(dLDbIdentifier
);
97 // @@@ Will happen again when kSecKeychainListChangedEvent notification is received.
98 mMultiDLDb
->list(mSavedList
.list());
100 KCEventNotifier::PostKeychainEvent(kSecKeychainListChangedEvent
);
106 StorageManager::created(const Keychain
&keychain
) // Be notified a Keychain just got created.
108 DLDbIdentifier dLDbIdentifier
= keychain
->dLDbIdentifier();
110 // If we don't have a default Keychain yet. Make the newly created keychain the default.
111 DefaultKeychain
&defaultKeychain
= globals().defaultKeychain
;
112 if (!defaultKeychain
.isSet())
113 defaultKeychain
.dLDbIdentifier(dLDbIdentifier
);
115 // Add the keychain to the search list and the cache. Then inform mMultiDLDb.
116 mSavedList
.revert(true);
117 mSavedList
.add(dLDbIdentifier
);
120 // @@@ Will happen again when kSecKeychainListChangedEvent notification is received.
121 mMultiDLDb
->list(mSavedList
.list());
123 KCEventNotifier::PostKeychainEvent(kSecKeychainListChangedEvent
);
128 StorageManager::createCursor(SecItemClass itemClass
, const SecKeychainAttributeList
*attrList
)
130 return KCCursor(DbCursor(mMultiDLDb
), itemClass
, attrList
);
134 StorageManager::createCursor(const SecKeychainAttributeList
*attrList
)
136 return KCCursor(DbCursor(mMultiDLDb
), attrList
);
140 StorageManager::lockAll()
142 for (KeychainMap::iterator ix
= mKeychains
.begin(); ix
!= mKeychains
.end(); ix
++)
144 Keychain
keychain(ix
->second
);
145 if (keychain
->isActive())
151 StorageManager::reload(bool force
)
153 // Reinitialize list from CFPrefs if changed. When force is true force a prefs revert now.
154 if (mSavedList
.revert(force
))
155 mMultiDLDb
->list(mSavedList
.list());
159 StorageManager::size()
162 return mMultiDLDb
->list().size();
166 StorageManager::at(unsigned int ix
)
169 if (ix
>= mMultiDLDb
->list().size())
170 MacOSError::throwMe(errSecInvalidKeychain
);
172 return keychain(mMultiDLDb
->list().at(ix
));
176 StorageManager::operator[](unsigned int ix
)
181 void StorageManager::remove(const list
<SecKeychainRef
>& kcsToRemove
)
183 //StLock<Mutex> _(mKeychainsLock);
184 mSavedList
.revert(true);
185 DLDbIdentifier defaultId
= globals().defaultKeychain
.dLDbIdentifier();
186 bool unsetDefault
=false;
187 for (list
<SecKeychainRef
>::const_iterator ix
= kcsToRemove
.begin();ix
!=kcsToRemove
.end();ix
++)
189 // Find the keychain object for the given ref
190 Keychain keychainToRemove
;
193 keychainToRemove
= KeychainRef::required(*ix
);
195 catch (const MacOSError
& err
)
197 if (err
.osStatus() == errSecInvalidKeychain
)
202 // Remove it from the saved list
203 mSavedList
.remove(keychainToRemove
->dLDbIdentifier());
204 if (keychainToRemove
->dLDbIdentifier() == defaultId
)
206 // Now remove it from the map
207 KeychainMap::iterator it
= mKeychains
.find(keychainToRemove
->dLDbIdentifier());
208 if (it
==mKeychains
.end())
210 mKeychains
.erase(it
);
213 mMultiDLDb
->list(mSavedList
.list());
214 KCEventNotifier::PostKeychainEvent(kSecKeychainListChangedEvent
);
216 globals().defaultKeychain
.unset();
219 void StorageManager::replace(const list
<SecKeychainRef
>& newKCList
)
221 // replace keychains list with new list
222 CssmClient::DLDbList dldbList
;
223 convert(newKCList
,dldbList
);
226 void StorageManager::convert(const list
<SecKeychainRef
>& SecKeychainRefList
,CssmClient::DLDbList
& dldbList
)
228 // Convert a list of SecKeychainRefs to a DLDbList
229 dldbList
.clear(); // If we don't clear list, we should use "add" instead of push_back
230 for (list
<SecKeychainRef
>::const_iterator ix
= SecKeychainRefList
.begin();ix
!=SecKeychainRefList
.end();ix
++)
232 // Find the keychain object for the given ref
236 keychain
= KeychainRef::required(*ix
);
238 catch (const MacOSError
& err
)
240 if (err
.osStatus() == errSecInvalidKeychain
)
245 // Add it to the list
246 dldbList
.push_back(keychain
->dLDbIdentifier());
251 #pragma mark ÑÑÑÑ Login Functions ÑÑÑÑ
253 void StorageManager::login(ConstStringPtr name
, ConstStringPtr password
)
255 if ( name
== NULL
|| password
== NULL
)
256 MacOSError::throwMe(paramErr
);
258 login(name
[0], name
+ 1, password
[0], password
+ 1);
261 void StorageManager::login(UInt32 nameLength
, const void *name
, UInt32 passwordLength
, const void *password
)
263 // @@@ set up the login session on behalf of loginwindow
264 // @@@ (this code should migrate into loginwindow)
265 debug("KClogin", "setting up login session");
266 if (OSStatus ssnErr
= SessionCreate(sessionKeepCurrentBootstrap
,
267 sessionHasGraphicAccess
| sessionHasTTY
))
268 debug("KClogin", "session setup failed status=%ld", ssnErr
);
270 if (name
== NULL
|| (passwordLength
!= 0 && password
== NULL
))
271 MacOSError::throwMe(paramErr
);
273 // Make sure name is zero terminated
274 string
theName(reinterpret_cast<const char *>(name
), nameLength
);
275 Keychain keychain
= make(theName
.c_str());
278 keychain
->unlock(CssmData(const_cast<void *>(password
), passwordLength
));
279 debug("KClogin", "keychain unlock successful");
281 catch(const CssmError
&e
)
283 if (e
.osStatus() != CSSMERR_DL_DATASTORE_DOESNOT_EXIST
)
285 debug("KClogin", "creating login keychain");
286 keychain
->create(passwordLength
, password
);
287 // Login Keychain does not lock on sleep nor lock after timeout by default.
288 keychain
->setSettings(INT_MAX
, false);
291 // @@@ Create a authorization credential for the current user.
292 debug("KClogin", "creating login authorization");
293 const AuthorizationItem envList
[] =
295 { kAuthorizationEnvironmentUsername
, nameLength
, const_cast<void *>(name
), 0 },
296 { kAuthorizationEnvironmentPassword
, passwordLength
, const_cast<void *>(password
), 0 },
297 { kAuthorizationEnvironmentShared
, 0, NULL
, 0 }
299 const AuthorizationEnvironment environment
=
301 sizeof(envList
) / sizeof(*envList
),
302 const_cast<AuthorizationItem
*>(envList
)
304 if (OSStatus authErr
= AuthorizationCreate(NULL
, &environment
,
305 kAuthorizationFlagExtendRights
| kAuthorizationFlagPreAuthorize
, NULL
))
306 debug("KClogin", "failed to create login auth, status=%ld", authErr
);
309 void StorageManager::logout()
311 // nothing left to do here
314 void StorageManager::changeLoginPassword(ConstStringPtr oldPassword
, ConstStringPtr newPassword
)
316 globals().defaultKeychain
.keychain()->changePassphrase(oldPassword
, newPassword
);
320 void StorageManager::changeLoginPassword(UInt32 oldPasswordLength
, const void *oldPassword
, UInt32 newPasswordLength
, const void *newPassword
)
322 globals().defaultKeychain
.keychain()->changePassphrase(oldPasswordLength
, oldPassword
, newPasswordLength
, newPassword
);
325 #pragma mark ÑÑÑÑ File Related ÑÑÑÑ
327 Keychain
StorageManager::make(const char *pathName
)
330 if ( pathName
[0] == '/' )
331 fullPathName
= pathName
;
334 // Get Home directory from environment.
335 const char *homeDir
= getenv("HOME");
338 // If $HOME is unset get the current users home directory from the passwd file.
339 struct passwd
*pw
= getpwuid(getuid());
341 MacOSError::throwMe(paramErr
);
343 homeDir
= pw
->pw_dir
;
346 fullPathName
= homeDir
;
347 fullPathName
+= "/Library/Keychains/";
348 fullPathName
+= pathName
;
351 const CSSM_NET_ADDRESS
*DbLocation
= NULL
; // NULL for keychains
352 const CSSM_VERSION
*version
= NULL
;
353 uint32 subserviceId
= 0;
354 CSSM_SERVICE_TYPE subserviceType
= CSSM_SERVICE_DL
| CSSM_SERVICE_CSP
;
355 const CssmSubserviceUid
ssuid( gGuidAppleCSPDL
, version
,
356 subserviceId
, subserviceType
);
357 DLDbIdentifier
dLDbIdentifier( ssuid
, fullPathName
.c_str(), DbLocation
);
358 return makeKeychain( dLDbIdentifier
);
362 StorageManager::keychainSchemaFor(const CssmClient::Db
&db
)
364 KeychainSchema
schema(db
);
365 pair
<KeychainSchemaSet::iterator
, bool> result
= mKeychainSchemaSet
.insert(db
);
368 return *result
.first
;