]> git.saurik.com Git - apple/security.git/blob - Keychain/StorageManager.cpp
ed3a7875949ef323e684f611b4681c4e0a730c89
[apple/security.git] / Keychain / StorageManager.cpp
1 /*
2 * Copyright (c) 2000-2002 Apple Computer, Inc. All Rights Reserved.
3 *
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
8 * using this file.
9 *
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.
16 */
17
18
19 /*
20 File: StorageManager.cpp
21
22 Contains: Working with multiple keychains
23
24 */
25
26 #include "StorageManager.h"
27 #include "KCEventNotifier.h"
28
29 #include <Security/cssmapple.h>
30 #include <sys/types.h>
31 #include <pwd.h>
32 #include <CoreServices/../Frameworks/CarbonCore.framework/Headers/MacErrors.h>
33 #include <algorithm>
34 #include <string>
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>
40
41 #include "KCCursor.h"
42 #include "Globals.h"
43 #include "DefaultKeychain.h"
44
45 using namespace CssmClient;
46 using namespace KeychainCore;
47
48 StorageManager::StorageManager() :
49 mSavedList(),
50 mKeychains(),
51 mSearchList()
52 {
53 _doReload();
54 }
55
56 // Create KC if it doesn't exist
57 Keychain
58 StorageManager::keychain(const DLDbIdentifier &dLDbIdentifier)
59 {
60 StLock<Mutex> _(mLock);
61 return _keychain(dLDbIdentifier);
62 }
63
64 Keychain
65 StorageManager::_keychain(const DLDbIdentifier &dLDbIdentifier)
66 {
67 KeychainMap::iterator it = mKeychains.find(dLDbIdentifier);
68 if (it != mKeychains.end())
69 return it->second;
70
71 // The keychain is not in our cache. Create it.
72 Module module(dLDbIdentifier.ssuid().guid());
73 DL dl;
74 if (dLDbIdentifier.ssuid().subserviceType() & CSSM_SERVICE_CSP)
75 dl = SSCSPDL(module);
76 else
77 dl = DL(module);
78
79 dl->subserviceId(dLDbIdentifier.ssuid().subserviceId());
80 dl->version(dLDbIdentifier.ssuid().version());
81 Db db(dl, dLDbIdentifier.dbName());
82 Keychain keychain(db);
83
84 // Add the keychain to the cache.
85 mKeychains.insert(KeychainMap::value_type(dLDbIdentifier, keychain));
86 return keychain;
87 }
88
89 // Create KC if it doesn't exist, add it to the search list if it exists and is not already on it.
90 Keychain
91 StorageManager::makeKeychain(const DLDbIdentifier &dLDbIdentifier)
92 {
93 Keychain keychain(keychain(dLDbIdentifier));
94
95 {
96 StLock<Mutex> _(mLock);
97 if (find(mSearchList.begin(), mSearchList.end(), keychain) != mSearchList.end())
98 {
99 // This keychain is already on our search list.
100 return keychain;
101 }
102
103 // If the keychain doesn't exist don't bother adding it to the search list yet.
104 if (!keychain->exists())
105 return keychain;
106
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);
111 mSavedList.save();
112
113 // @@@ Will happen again when kSecKeychainListChangedEvent notification is received.
114 _doReload();
115 }
116
117 // Make sure we are not holding mLock when we post this event.
118 KCEventNotifier::PostKeychainEvent(kSecKeychainListChangedEvent);
119
120 return keychain;
121 }
122
123 void
124 StorageManager::created(const Keychain &keychain) // Be notified a Keychain just got created.
125 {
126 DLDbIdentifier dLDbIdentifier = keychain->dLDbIdentifier();
127
128 {
129 StLock<Mutex> _(mLock);
130
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);
135
136 // Add the keychain to the search list and the cache. Then inform mMultiDLDb.
137 mSavedList.revert(true);
138 mSavedList.add(dLDbIdentifier);
139 mSavedList.save();
140
141 // @@@ Will happen again when kSecKeychainListChangedEvent notification is received.
142 _doReload();
143 }
144
145 // Make sure we are not holding mLock when we post this event.
146 KCEventNotifier::PostKeychainEvent(kSecKeychainListChangedEvent);
147 }
148
149 KCCursor
150 StorageManager::createCursor(SecItemClass itemClass, const SecKeychainAttributeList *attrList)
151 {
152 StLock<Mutex> _(mLock);
153 return KCCursor(mSearchList, itemClass, attrList);
154 }
155
156 KCCursor
157 StorageManager::createCursor(const SecKeychainAttributeList *attrList)
158 {
159 StLock<Mutex> _(mLock);
160 return KCCursor(mSearchList, attrList);
161 }
162
163 void
164 StorageManager::lockAll()
165 {
166 // Make a snapshot of all known keychains while holding mLock.
167 KeychainList keychainList;
168 {
169 StLock<Mutex> _(mLock);
170 for (KeychainMap::iterator ix = mKeychains.begin(); ix != mKeychains.end(); ix++)
171 keychainList.push_back(ix->second);
172 }
173
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)
177 {
178 Keychain keychain = *ix;
179 if (keychain->isActive())
180 keychain->lock();
181 }
182 }
183
184 void
185 StorageManager::_doReload()
186 {
187 KeychainList newList;
188 newList.reserve(mSavedList.size());
189 for (CssmClient::DLDbList::iterator ix = mSavedList.begin(); ix != mSavedList.end(); ++ix)
190 {
191 Keychain keychain(_keychain(*ix));
192 newList.push_back(keychain);
193 }
194 mSearchList.swap(newList);
195 }
196
197 void
198 StorageManager::reload(bool force)
199 {
200 StLock<Mutex> _(mLock);
201 _reload(force);
202 }
203
204 void
205 StorageManager::_reload(bool force)
206 {
207 // Reinitialize list from CFPrefs if changed. When force is true force a prefs revert now.
208 if (mSavedList.revert(force))
209 _doReload();
210 }
211
212 size_t
213 StorageManager::size()
214 {
215 StLock<Mutex> _(mLock);
216 _reload();
217 return mSearchList.size();
218 }
219
220 Keychain
221 StorageManager::at(unsigned int ix)
222 {
223 StLock<Mutex> _(mLock);
224 _reload();
225 if (ix >= mSearchList.size())
226 MacOSError::throwMe(errSecInvalidKeychain);
227
228 return mSearchList.at(ix);
229 }
230
231 Keychain
232 StorageManager::operator[](unsigned int ix)
233 {
234 return at(ix);
235 }
236
237 void StorageManager::remove(const KeychainList &kcsToRemove, bool deleteDb)
238 {
239 bool unsetDefault = false;
240 {
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)
245 {
246 // Find the keychain object for the given ref
247 Keychain keychainToRemove = *ix;
248 DLDbIdentifier dLDbIdentifier = keychainToRemove->dLDbIdentifier();
249
250 // Remove it from the saved list
251 mSavedList.remove(dLDbIdentifier);
252 if (dLDbIdentifier == defaultId)
253 unsetDefault=true;
254
255 if (deleteDb)
256 {
257 keychainToRemove->database()->deleteDb();
258 // Now remove it from the map
259 KeychainMap::iterator it = mKeychains.find(dLDbIdentifier);
260 if (it == mKeychains.end())
261 continue;
262 mKeychains.erase(it);
263 }
264 }
265 mSavedList.save();
266 _doReload();
267 }
268
269 // Make sure we are not holding mLock when we post this event.
270 KCEventNotifier::PostKeychainEvent(kSecKeychainListChangedEvent);
271
272 if (unsetDefault)
273 {
274 // Make sure we are not holding mLock when we call this since it posts an event.
275 globals().defaultKeychain.unset();
276 }
277 }
278
279 void
280 StorageManager::getSearchList(KeychainList &keychainList)
281 {
282 // Make a copy of the searchList
283 StLock<Mutex> _(mLock);
284 StorageManager::KeychainList searchList(mSearchList);
285
286 // Return the copy of the list.
287 keychainList.swap(searchList);
288 }
289
290 void
291 StorageManager::setSearchList(const KeychainList &keychainList)
292 {
293 // Make a copy of the passed in searchList
294 StorageManager::KeychainList keychains(keychainList);
295
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);
300 }
301
302 void
303 StorageManager::optionalSearchList(CFTypeRef keychainOrArray, KeychainList &keychainList)
304 {
305 if (!keychainOrArray)
306 getSearchList(keychainList);
307 else
308 {
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)));
314 else
315 MacOSError::throwMe(paramErr);
316 }
317 }
318
319 // static methods.
320 void
321 StorageManager::convertToKeychainList(CFArrayRef keychainArray, KeychainList &keychainList)
322 {
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)
328 {
329 keychains[ix] = kcClass.required(SecKeychainRef(CFArrayGetValueAtIndex(keychainArray, ix)));
330 }
331
332 keychainList.swap(keychains);
333 }
334
335 CFArrayRef
336 StorageManager::convertFromKeychainList(const KeychainList &keychainList)
337 {
338 CFRef<CFMutableArrayRef> keychainArray(CFArrayCreateMutable(NULL, keychainList.size(), &kCFTypeArrayCallBacks));
339
340 CFClass<KeychainImpl, SecKeychainRef, errSecInvalidKeychain> &kcClass = gTypes().keychain;
341 for (KeychainList::const_iterator ix = keychainList.begin(); ix != keychainList.end(); ++ix)
342 {
343 SecKeychainRef keychainRef = kcClass.handle(**ix);
344 CFArrayAppendValue(keychainArray, keychainRef);
345 CFRelease(keychainRef);
346 }
347
348 // Counter the CFRelease that CFRef<> is about to do when keychainArray goes out of scope.
349 CFRetain(keychainArray);
350 return keychainArray;
351 }
352
353
354
355 #pragma mark ÑÑÑÑ Login Functions ÑÑÑÑ
356
357 void StorageManager::login(ConstStringPtr name, ConstStringPtr password)
358 {
359 if ( name == NULL || password == NULL )
360 MacOSError::throwMe(paramErr);
361
362 login(name[0], name + 1, password[0], password + 1);
363 }
364
365 void StorageManager::login(UInt32 nameLength, const void *name, UInt32 passwordLength, const void *password)
366 {
367 // @@@ set up the login session on behalf of loginwindow
368 // @@@ (this code should migrate into loginwindow)
369 #if 0
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);
374 #endif
375
376 if (name == NULL || (passwordLength != 0 && password == NULL))
377 MacOSError::throwMe(paramErr);
378
379 // Make sure name is zero terminated
380 string theName(reinterpret_cast<const char *>(name), nameLength);
381 Keychain keychain = make(theName.c_str());
382 try
383 {
384 keychain->unlock(CssmData(const_cast<void *>(password), passwordLength));
385 debug("KClogin", "keychain unlock successful");
386 }
387 catch(const CssmError &e)
388 {
389 if (e.osStatus() != CSSMERR_DL_DATASTORE_DOESNOT_EXIST)
390 throw;
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);
395 }
396 #if 0
397 // @@@ Create a authorization credential for the current user.
398 debug("KClogin", "creating login authorization");
399 const AuthorizationItem envList[] =
400 {
401 { kAuthorizationEnvironmentUsername, nameLength, const_cast<void *>(name), 0 },
402 { kAuthorizationEnvironmentPassword, passwordLength, const_cast<void *>(password), 0 },
403 { kAuthorizationEnvironmentShared, 0, NULL, 0 }
404 };
405 const AuthorizationEnvironment environment =
406 {
407 sizeof(envList) / sizeof(*envList),
408 const_cast<AuthorizationItem *>(envList)
409 };
410 if (OSStatus authErr = AuthorizationCreate(NULL, &environment,
411 kAuthorizationFlagExtendRights | kAuthorizationFlagPreAuthorize, NULL))
412 debug("KClogin", "failed to create login auth, status=%ld", authErr);
413 #endif
414 }
415
416 void StorageManager::logout()
417 {
418 // nothing left to do here
419 }
420
421 void StorageManager::changeLoginPassword(ConstStringPtr oldPassword, ConstStringPtr newPassword)
422 {
423 globals().defaultKeychain.keychain()->changePassphrase(oldPassword, newPassword);
424 }
425
426
427 void StorageManager::changeLoginPassword(UInt32 oldPasswordLength, const void *oldPassword, UInt32 newPasswordLength, const void *newPassword)
428 {
429 globals().defaultKeychain.keychain()->changePassphrase(oldPasswordLength, oldPassword, newPasswordLength, newPassword);
430 }
431
432 #pragma mark ÑÑÑÑ File Related ÑÑÑÑ
433
434 Keychain StorageManager::make(const char *pathName)
435 {
436 string fullPathName;
437 if ( pathName[0] == '/' )
438 fullPathName = pathName;
439 else
440 {
441 // Get Home directory from environment.
442 const char *homeDir = getenv("HOME");
443 if (homeDir == NULL)
444 {
445 // If $HOME is unset get the current users home directory from the passwd file.
446 struct passwd *pw = getpwuid(getuid());
447 if (!pw)
448 MacOSError::throwMe(paramErr);
449
450 homeDir = pw->pw_dir;
451 }
452
453 fullPathName = homeDir;
454 fullPathName += "/Library/Keychains/";
455 fullPathName += pathName;
456 }
457
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);
466 }
467
468 KeychainSchema
469 StorageManager::keychainSchemaFor(const CssmClient::Db &db)
470 {
471 // @@@ Locking
472 KeychainSchema schema(db);
473 pair<KeychainSchemaSet::iterator, bool> result = mKeychainSchemaSet.insert(db);
474 if (result.second)
475 return schema;
476 return *result.first;
477 }
478