]> git.saurik.com Git - apple/security.git/blob - Keychain/StorageManager.cpp
78194a43d07d0fb319d84ee5b430d6246ce7c2f7
[apple/security.git] / Keychain / StorageManager.cpp
1 /*
2 * Copyright (c) 2000-2001 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 Copyright: 2000 by Apple Computer, Inc., all rights reserved.
25
26 To Do:
27 */
28
29 #include "StorageManager.h"
30 #include "KCEventNotifier.h"
31
32 #include <Security/cssmapple.h>
33 #include <sys/types.h>
34 #include <pwd.h>
35 #include <CoreServices/../Frameworks/CarbonCore.framework/Headers/MacErrors.h>
36 #include <algorithm>
37 #include <string>
38 #include <Security/Authorization.h>
39 #include <Security/AuthorizationTags.h>
40 #include <Security/AuthSession.h>
41 #include <Security/debugging.h>
42
43 #include "KCCursor.h"
44 #include "Globals.h"
45 #include "DefaultKeychain.h"
46
47 using namespace CssmClient;
48 using namespace KeychainCore;
49
50 StorageManager::StorageManager() :
51 mSavedList(),
52 mKeychains(),
53 mMultiDLDb(mSavedList.list(), true) // Passinng true enables use of Secure Storage
54 {
55 }
56
57 // Create KC if it doesn't exist
58 Keychain
59 StorageManager::keychain(const DLDbIdentifier &dLDbIdentifier)
60 {
61 //StLock<Mutex> _(mKeychainsLock);
62 KeychainMap::iterator it = mKeychains.find(dLDbIdentifier);
63 if (it != mKeychains.end())
64 return it->second;
65
66 // The keychain is not in our cache. Create it.
67 Keychain keychain(mMultiDLDb->database(dLDbIdentifier));
68
69 // Add the keychain to the cache.
70 mKeychains.insert(KeychainMap::value_type(dLDbIdentifier, keychain));
71 return keychain;
72 }
73
74 // Create KC if it doesn't exist
75 Keychain
76 StorageManager::makeKeychain(const DLDbIdentifier &dLDbIdentifier)
77 {
78 Keychain keychain(keychain(dLDbIdentifier));
79
80 const vector<DLDbIdentifier> &list = mMultiDLDb->list();
81 if (find(list.begin(), list.end(), dLDbIdentifier) != list.end())
82 {
83 // The dLDbIdentifier for this keychain is already on our search list.
84 return keychain;
85 }
86
87 // If the keychain doesn't exist don't bother adding it to the search list yet.
88 if (!keychain->exists())
89 return keychain;
90
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);
95 mSavedList.save();
96
97 // @@@ Will happen again when kSecKeychainListChangedEvent notification is received.
98 mMultiDLDb->list(mSavedList.list());
99
100 KCEventNotifier::PostKeychainEvent(kSecKeychainListChangedEvent);
101
102 return keychain;
103 }
104
105 void
106 StorageManager::created(const Keychain &keychain) // Be notified a Keychain just got created.
107 {
108 DLDbIdentifier dLDbIdentifier = keychain->dLDbIdentifier();
109
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);
114
115 // Add the keychain to the search list and the cache. Then inform mMultiDLDb.
116 mSavedList.revert(true);
117 mSavedList.add(dLDbIdentifier);
118 mSavedList.save();
119
120 // @@@ Will happen again when kSecKeychainListChangedEvent notification is received.
121 mMultiDLDb->list(mSavedList.list());
122
123 KCEventNotifier::PostKeychainEvent(kSecKeychainListChangedEvent);
124 }
125
126
127 KCCursor
128 StorageManager::createCursor(SecItemClass itemClass, const SecKeychainAttributeList *attrList)
129 {
130 return KCCursor(DbCursor(mMultiDLDb), itemClass, attrList);
131 }
132
133 KCCursor
134 StorageManager::createCursor(const SecKeychainAttributeList *attrList)
135 {
136 return KCCursor(DbCursor(mMultiDLDb), attrList);
137 }
138
139 void
140 StorageManager::lockAll()
141 {
142 for (KeychainMap::iterator ix = mKeychains.begin(); ix != mKeychains.end(); ix++)
143 {
144 Keychain keychain(ix->second);
145 if (keychain->isActive())
146 keychain->lock();
147 }
148 }
149
150 void
151 StorageManager::reload(bool force)
152 {
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());
156 }
157
158 size_t
159 StorageManager::size()
160 {
161 reload();
162 return mMultiDLDb->list().size();
163 }
164
165 Keychain
166 StorageManager::at(unsigned int ix)
167 {
168 reload();
169 if (ix >= mMultiDLDb->list().size())
170 MacOSError::throwMe(errSecInvalidKeychain);
171
172 return keychain(mMultiDLDb->list().at(ix));
173 }
174
175 Keychain
176 StorageManager::operator[](unsigned int ix)
177 {
178 return at(ix);
179 }
180
181 void StorageManager::remove(const list<SecKeychainRef>& kcsToRemove)
182 {
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++)
188 {
189 // Find the keychain object for the given ref
190 Keychain keychainToRemove;
191 try
192 {
193 keychainToRemove = KeychainRef::required(*ix);
194 }
195 catch (const MacOSError& err)
196 {
197 if (err.osStatus() == errSecInvalidKeychain)
198 continue;
199 throw;
200 }
201
202 // Remove it from the saved list
203 mSavedList.remove(keychainToRemove->dLDbIdentifier());
204 if (keychainToRemove->dLDbIdentifier() == defaultId)
205 unsetDefault=true;
206 // Now remove it from the map
207 KeychainMap::iterator it = mKeychains.find(keychainToRemove->dLDbIdentifier());
208 if (it==mKeychains.end())
209 continue;
210 mKeychains.erase(it);
211 }
212 mSavedList.save();
213 mMultiDLDb->list(mSavedList.list());
214 KCEventNotifier::PostKeychainEvent(kSecKeychainListChangedEvent);
215 if (unsetDefault)
216 globals().defaultKeychain.unset();
217 }
218
219 void StorageManager::replace(const list<SecKeychainRef>& newKCList)
220 {
221 // replace keychains list with new list
222 CssmClient::DLDbList dldbList;
223 convert(newKCList,dldbList);
224 }
225
226 void StorageManager::convert(const list<SecKeychainRef>& SecKeychainRefList,CssmClient::DLDbList& dldbList)
227 {
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++)
231 {
232 // Find the keychain object for the given ref
233 Keychain keychain;
234 try
235 {
236 keychain = KeychainRef::required(*ix);
237 }
238 catch (const MacOSError& err)
239 {
240 if (err.osStatus() == errSecInvalidKeychain)
241 continue;
242 throw;
243 }
244
245 // Add it to the list
246 dldbList.push_back(keychain->dLDbIdentifier());
247 }
248 }
249
250
251 #pragma mark ÑÑÑÑ Login Functions ÑÑÑÑ
252
253 void StorageManager::login(ConstStringPtr name, ConstStringPtr password)
254 {
255 if ( name == NULL || password == NULL )
256 MacOSError::throwMe(paramErr);
257
258 login(name[0], name + 1, password[0], password + 1);
259 }
260
261 void StorageManager::login(UInt32 nameLength, const void *name, UInt32 passwordLength, const void *password)
262 {
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);
269
270 if (name == NULL || (passwordLength != 0 && password == NULL))
271 MacOSError::throwMe(paramErr);
272
273 // Make sure name is zero terminated
274 string theName(reinterpret_cast<const char *>(name), nameLength);
275 Keychain keychain = make(theName.c_str());
276 try
277 {
278 keychain->unlock(CssmData(const_cast<void *>(password), passwordLength));
279 debug("KClogin", "keychain unlock successful");
280 }
281 catch(const CssmError &e)
282 {
283 if (e.osStatus() != CSSMERR_DL_DATASTORE_DOESNOT_EXIST)
284 throw;
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);
289 }
290
291 // @@@ Create a authorization credential for the current user.
292 debug("KClogin", "creating login authorization");
293 const AuthorizationItem envList[] =
294 {
295 { kAuthorizationEnvironmentUsername, nameLength, const_cast<void *>(name), 0 },
296 { kAuthorizationEnvironmentPassword, passwordLength, const_cast<void *>(password), 0 },
297 { kAuthorizationEnvironmentShared, 0, NULL, 0 }
298 };
299 const AuthorizationEnvironment environment =
300 {
301 sizeof(envList) / sizeof(*envList),
302 const_cast<AuthorizationItem *>(envList)
303 };
304 if (OSStatus authErr = AuthorizationCreate(NULL, &environment,
305 kAuthorizationFlagExtendRights | kAuthorizationFlagPreAuthorize, NULL))
306 debug("KClogin", "failed to create login auth, status=%ld", authErr);
307 }
308
309 void StorageManager::logout()
310 {
311 // nothing left to do here
312 }
313
314 void StorageManager::changeLoginPassword(ConstStringPtr oldPassword, ConstStringPtr newPassword)
315 {
316 globals().defaultKeychain.keychain()->changePassphrase(oldPassword, newPassword);
317 }
318
319
320 void StorageManager::changeLoginPassword(UInt32 oldPasswordLength, const void *oldPassword, UInt32 newPasswordLength, const void *newPassword)
321 {
322 globals().defaultKeychain.keychain()->changePassphrase(oldPasswordLength, oldPassword, newPasswordLength, newPassword);
323 }
324
325 #pragma mark ÑÑÑÑ File Related ÑÑÑÑ
326
327 Keychain StorageManager::make(const char *pathName)
328 {
329 string fullPathName;
330 if ( pathName[0] == '/' )
331 fullPathName = pathName;
332 else
333 {
334 // Get Home directory from environment.
335 const char *homeDir = getenv("HOME");
336 if (homeDir == NULL)
337 {
338 // If $HOME is unset get the current users home directory from the passwd file.
339 struct passwd *pw = getpwuid(getuid());
340 if (!pw)
341 MacOSError::throwMe(paramErr);
342
343 homeDir = pw->pw_dir;
344 }
345
346 fullPathName = homeDir;
347 fullPathName += "/Library/Keychains/";
348 fullPathName += pathName;
349 }
350
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 );
359 }
360
361 KeychainSchema
362 StorageManager::keychainSchemaFor(const CssmClient::Db &db)
363 {
364 KeychainSchema schema(db);
365 pair<KeychainSchemaSet::iterator, bool> result = mKeychainSchemaSet.insert(db);
366 if (result.second)
367 return schema;
368 return *result.first;
369 }
370