]> git.saurik.com Git - apple/security.git/blob - Keychain/StorageManager.cpp
Security-163.tar.gz
[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 <sys/param.h>
32 #include <syslog.h>
33 #include <pwd.h>
34 #include <CoreServices/../Frameworks/CarbonCore.framework/Headers/MacErrors.h>
35 #include <algorithm>
36 #include <string>
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>
44
45 #include "KCCursor.h"
46 #include "Globals.h"
47
48 using namespace CssmClient;
49 using namespace KeychainCore;
50
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))
55
56 //-----------------------------------------------------------------------------------
57
58 StorageManager::StorageManager() :
59 mSavedList(kSecPreferencesDomainUser),
60 mCommonList(kSecPreferencesDomainCommon),
61 mDomain(kSecPreferencesDomainUser),
62 mKeychains()
63 {
64 // get session attributes
65 SessionAttributeBits sessionAttrs;
66 if (OSStatus err = SessionGetInfo(callerSecuritySession,
67 NULL, &sessionAttrs))
68 CssmError::throwMe(err);
69
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);
78 }
79 }
80
81 // Create KC if it doesn't exist
82 Keychain
83 StorageManager::keychain(const DLDbIdentifier &dLDbIdentifier)
84 {
85 StLock<Mutex> _(mLock);
86 return _keychain(dLDbIdentifier);
87 }
88
89 Keychain
90 StorageManager::_keychain(const DLDbIdentifier &dLDbIdentifier)
91 {
92 if (!dLDbIdentifier)
93 return Keychain();
94
95 KeychainMap::iterator it = mKeychains.find(dLDbIdentifier);
96 if (it != mKeychains.end())
97 return it->second;
98
99 // The keychain is not in our cache. Create it.
100 Module module(dLDbIdentifier.ssuid().guid());
101 DL dl;
102 if (dLDbIdentifier.ssuid().subserviceType() & CSSM_SERVICE_CSP)
103 dl = SSCSPDL(module);
104 else
105 dl = DL(module);
106
107 dl->subserviceId(dLDbIdentifier.ssuid().subserviceId());
108 dl->version(dLDbIdentifier.ssuid().version());
109 Db db(dl, dLDbIdentifier.dbName());
110
111 Keychain keychain(db);
112 // Add the keychain to the cache.
113 mKeychains.insert(KeychainMap::value_type(dLDbIdentifier, &*keychain));
114
115 return keychain;
116 }
117
118 // Called from KeychainImpl's destructor remove it from the map.
119 void
120 StorageManager::removeKeychain(const DLDbIdentifier &dLDbIdentifier, KeychainImpl *keychainImpl)
121 {
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);
126 }
127
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)
130 {
131 try {
132 CssmAutoData index(db->allocator());
133 if (!db->getUnlockKeyIndex(index.get()))
134 return; // no suggested index (probably not a CSPDL)
135
136 TrackingAllocator alloc(CssmAllocator::standard());
137
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,
144 {"Label"},
145 CSSM_DB_ATTRIBUTE_FORMAT_BLOB
146 };
147 search->add(CSSM_DB_EQUAL, infoLabel, keyLabel.get());
148
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);
152 Item keyItem;
153 if (search->next(keyItem)) {
154 CssmClient::Key key = dynamic_cast<KeyItem &>(*keyItem).key();
155
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));
165
166 secdebug("storagemgr", "authenticating %s for default key credentials", db->name());
167 db->authenticate(db->accessRequest(), &cred);
168 }
169 } catch (...) {
170 secdebug("storagemgr", "setDefaultCredentials for %s abandoned due to exception", db->name());
171 }
172 }
173
174 // Create KC if it doesn't exist, add it to the search list if it exists and is not already on it.
175 Keychain
176 StorageManager::makeKeychain(const DLDbIdentifier &dLDbIdentifier, bool add)
177 {
178 Keychain keychain;
179 bool post = false;
180
181 {
182 StLock<Mutex> _(mLock);
183 keychain = _keychain(dLDbIdentifier);
184
185 if (add)
186 {
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.
191
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.
196
197 // If the keychain doesn't exist don't bother adding it to the search list yet.
198 if (!keychain->exists())
199 return keychain;
200
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);
205 mSavedList.save();
206 post = true;
207 }
208 }
209
210 if (post)
211 {
212 // Make sure we are not holding mLock when we post this event.
213 KCEventNotifier::PostKeychainEvent(kSecKeychainListChangedEvent);
214 }
215
216 return keychain;
217 }
218
219 void
220 StorageManager::created(const Keychain &keychain) // Be notified a Keychain just got created.
221 {
222 DLDbIdentifier dLDbIdentifier = keychain->dLDbIdentifier();
223 bool defaultChanged = false;
224
225 {
226 StLock<Mutex> _(mLock);
227
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())
231 {
232 mSavedList.defaultDLDbIdentifier(dLDbIdentifier);
233 defaultChanged = true;
234 }
235
236 // Add the keychain to the search list prefs.
237 mSavedList.add(dLDbIdentifier);
238 mSavedList.save();
239 }
240
241 // Make sure we are not holding mLock when we post these events.
242 KCEventNotifier::PostKeychainEvent(kSecKeychainListChangedEvent);
243
244 if (defaultChanged)
245 {
246 KCEventNotifier::PostKeychainEvent(kSecDefaultChangedEvent, dLDbIdentifier);
247 }
248 }
249
250 KCCursor
251 StorageManager::createCursor(SecItemClass itemClass, const SecKeychainAttributeList *attrList)
252 {
253 KeychainList searchList;
254 getSearchList(searchList);
255 return KCCursor(searchList, itemClass, attrList);
256 }
257
258 KCCursor
259 StorageManager::createCursor(const SecKeychainAttributeList *attrList)
260 {
261 KeychainList searchList;
262 getSearchList(searchList);
263 return KCCursor(searchList, attrList);
264 }
265
266 void
267 StorageManager::lockAll()
268 {
269 SecurityServer::ClientSession ss(CssmAllocator::standard(), CssmAllocator::standard());
270 ss.lockAll (false);
271 }
272
273 Keychain
274 StorageManager::defaultKeychain()
275 {
276 Keychain theKeychain;
277 {
278 StLock<Mutex> _(mLock);
279 mSavedList.revert(false);
280 DLDbIdentifier defaultDLDbIdentifier(mSavedList.defaultDLDbIdentifier());
281 if (defaultDLDbIdentifier)
282 {
283 theKeychain = _keychain(defaultDLDbIdentifier);
284 }
285 }
286
287 if (theKeychain /* && theKeychain->exists() */)
288 return theKeychain;
289
290 MacOSError::throwMe(errSecNoDefaultKeychain);
291 }
292
293 void
294 StorageManager::defaultKeychain(const Keychain &keychain)
295 {
296 DLDbIdentifier oldDefaultId;
297 DLDbIdentifier newDefaultId(keychain->dLDbIdentifier());
298 {
299 StLock<Mutex> _(mLock);
300 oldDefaultId = mSavedList.defaultDLDbIdentifier();
301 mSavedList.revert(true);
302 mSavedList.defaultDLDbIdentifier(newDefaultId);
303 mSavedList.save();
304 }
305
306 if (!(oldDefaultId == newDefaultId))
307 {
308 // Make sure we are not holding mLock when we post this event.
309 KCEventNotifier::PostKeychainEvent(kSecDefaultChangedEvent, newDefaultId);
310 }
311 }
312
313 Keychain
314 StorageManager::defaultKeychain(SecPreferencesDomain domain)
315 {
316 if (domain == mDomain)
317 return defaultKeychain();
318 else
319 {
320 DLDbIdentifier defaultDLDbIdentifier(DLDbListCFPref(domain).defaultDLDbIdentifier());
321 if (defaultDLDbIdentifier)
322 return keychain(defaultDLDbIdentifier);
323
324 MacOSError::throwMe(errSecNoDefaultKeychain);
325 }
326 }
327
328 void
329 StorageManager::defaultKeychain(SecPreferencesDomain domain, const Keychain &keychain)
330 {
331 if (domain == mDomain)
332 defaultKeychain(keychain);
333 else
334 DLDbListCFPref(domain).defaultDLDbIdentifier(keychain->dLDbIdentifier());
335 }
336
337 Keychain
338 StorageManager::loginKeychain()
339 {
340 Keychain theKeychain;
341 {
342 StLock<Mutex> _(mLock);
343 mSavedList.revert(false);
344 DLDbIdentifier loginDLDbIdentifier(mSavedList.loginDLDbIdentifier());
345 if (loginDLDbIdentifier)
346 {
347 theKeychain = _keychain(loginDLDbIdentifier);
348 }
349 }
350
351 if (theKeychain && theKeychain->exists())
352 return theKeychain;
353
354 MacOSError::throwMe(errSecNoSuchKeychain);
355 }
356
357 void
358 StorageManager::loginKeychain(Keychain keychain)
359 {
360 StLock<Mutex> _(mLock);
361 mSavedList.revert(true);
362 mSavedList.loginDLDbIdentifier(keychain->dLDbIdentifier());
363 mSavedList.save();
364 }
365
366 size_t
367 StorageManager::size()
368 {
369 StLock<Mutex> _(mLock);
370 mSavedList.revert(false);
371 mCommonList.revert(false);
372 return mSavedList.searchList().size() + mCommonList.searchList().size();
373 }
374
375 Keychain
376 StorageManager::at(unsigned int ix)
377 {
378 StLock<Mutex> _(mLock);
379 mSavedList.revert(false);
380 DLDbList dLDbList = mSavedList.searchList();
381 if (ix < dLDbList.size())
382 {
383 return _keychain(dLDbList[ix]);
384 }
385 else
386 {
387 ix -= dLDbList.size();
388 mCommonList.revert(false);
389 DLDbList commonList = mCommonList.searchList();
390 if (ix >= commonList.size())
391 MacOSError::throwMe(errSecInvalidKeychain);
392
393 return _keychain(commonList[ix]);
394 }
395 }
396
397 Keychain
398 StorageManager::operator[](unsigned int ix)
399 {
400 return at(ix);
401 }
402
403 void StorageManager::rename(Keychain keychain, const char* newName)
404 {
405 // This is not a generic purpose rename method for keychains.
406 // The keychain doesn't remain in the cache.
407 //
408 bool changedDefault = false;
409 DLDbIdentifier newDLDbIdentifier;
410 {
411 StLock<Mutex> _(mLock);
412 mSavedList.revert(true);
413 DLDbIdentifier defaultId = mSavedList.defaultDLDbIdentifier();
414
415 // Find the keychain object for the given ref
416 DLDbIdentifier dLDbIdentifier = keychain->dLDbIdentifier();
417
418 // Remove it from the saved list
419 mSavedList.remove(dLDbIdentifier);
420 if (dLDbIdentifier == defaultId)
421 changedDefault=true;
422
423 // Actually rename the database on disk.
424 keychain->database()->rename(newName);
425
426 newDLDbIdentifier = keychain->dLDbIdentifier();
427
428 // Now update the keychain map to use the newDLDbIdentifier
429 KeychainMap::iterator it = mKeychains.find(dLDbIdentifier);
430 if (it != mKeychains.end())
431 {
432 mKeychains.erase(it);
433 mKeychains.insert(KeychainMap::value_type(newDLDbIdentifier, keychain));
434 }
435
436 // If this was the default keychain change it accordingly
437 if (changedDefault)
438 mSavedList.defaultDLDbIdentifier(newDLDbIdentifier);
439
440 mSavedList.save();
441 }
442
443 // @@@ We need a kSecKeychainRenamedEvent so other clients can close this keychain and move on with life.
444 //KCEventNotifier::PostKeychainEvent(kSecKeychainRenamedEvent);
445
446 // Make sure we are not holding mLock when we post these events.
447 KCEventNotifier::PostKeychainEvent(kSecKeychainListChangedEvent);
448
449 if (changedDefault)
450 KCEventNotifier::PostKeychainEvent(kSecDefaultChangedEvent, newDLDbIdentifier);
451 }
452
453 void StorageManager::renameUnique(Keychain keychain, CFStringRef newName)
454 {
455 bool doneCreating = false;
456 int index = 1;
457 do
458 {
459 char newNameCString[MAXPATHLEN];
460 if ( CFStringGetCString(newName, newNameCString, MAXPATHLEN, kCFStringEncodingUTF8) ) // make sure it fits in MAXPATHLEN, etc.
461 {
462 // Construct the new name...
463 //
464 CFMutableStringRef newNameCFStr = NULL;
465 newNameCFStr = CFStringCreateMutable(NULL, MAXPATHLEN);
466 if ( newNameCFStr )
467 {
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.
472 {
473 struct stat filebuf;
474 if ( lstat(toUseBuff2, &filebuf) )
475 {
476 rename(keychain, toUseBuff2);
477 doneCreating = true;
478 }
479 else
480 index++;
481 }
482 else
483 doneCreating = true; // failure to get c string.
484 CFRelease(newNameCFStr);
485 }
486 else
487 doneCreating = false; // failure to create mutable string.
488 }
489 else
490 doneCreating = false; // failure to get the string (i.e. > MAXPATHLEN?)
491 }
492 while (!doneCreating && index != INT_MAX);
493 }
494
495 void StorageManager::remove(const KeychainList &kcsToRemove, bool deleteDb)
496 {
497 bool unsetDefault = false;
498 {
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)
503 {
504 // Find the keychain object for the given ref
505 Keychain keychainToRemove = *ix;
506 DLDbIdentifier dLDbIdentifier = keychainToRemove->dLDbIdentifier();
507
508 // Remove it from the saved list
509 mSavedList.remove(dLDbIdentifier);
510 if (dLDbIdentifier == defaultId)
511 unsetDefault=true;
512
513 if (deleteDb)
514 {
515 keychainToRemove->database()->deleteDb();
516 // Now remove it from the map
517 KeychainMap::iterator it = mKeychains.find(dLDbIdentifier);
518 if (it == mKeychains.end())
519 continue;
520 mKeychains.erase(it);
521 }
522 }
523
524 if (unsetDefault)
525 mSavedList.defaultDLDbIdentifier(DLDbIdentifier());
526
527 mSavedList.save();
528 }
529
530 // Make sure we are not holding mLock when we post these events.
531 KCEventNotifier::PostKeychainEvent(kSecKeychainListChangedEvent);
532
533 if (unsetDefault)
534 KCEventNotifier::PostKeychainEvent(kSecDefaultChangedEvent);
535 }
536
537 void
538 StorageManager::getSearchList(KeychainList &keychainList)
539 {
540 StLock<Mutex> _(mLock);
541 mSavedList.revert(false);
542 mCommonList.revert(false);
543
544 // Merge mSavedList and common list
545 DLDbList dLDbList = mSavedList.searchList();
546 DLDbList commonList = mCommonList.searchList();
547 KeychainList result;
548 result.reserve(dLDbList.size() + commonList.size());
549
550 for (DLDbList::const_iterator it = dLDbList.begin(); it != dLDbList.end(); ++it)
551 {
552 Keychain keychain(_keychain(*it));
553 result.push_back(keychain);
554 }
555
556 for (DLDbList::const_iterator it = commonList.begin(); it != commonList.end(); ++it)
557 {
558 Keychain keychain(_keychain(*it));
559 result.push_back(keychain);
560 }
561
562 keychainList.swap(result);
563 }
564
565 void
566 StorageManager::setSearchList(const KeychainList &keychainList)
567 {
568 DLDbList commonList = mCommonList.searchList();
569
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)
574 {
575 // Eliminate common entries from the end of the passed in keychainList.
576 if (it_end == keychainList.begin())
577 break;
578
579 --it_end;
580 if (!((*it_end)->dLDbIdentifier() == *it_common))
581 {
582 ++it_end;
583 break;
584 }
585 }
586
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)
590 {
591 searchList.push_back((*it)->dLDbIdentifier());
592 }
593
594 {
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);
600 mSavedList.save();
601 }
602
603 if (!(oldSearchList == searchList))
604 {
605 // Make sure we are not holding mLock when we post this event.
606 KCEventNotifier::PostKeychainEvent(kSecKeychainListChangedEvent);
607 }
608 }
609
610 void
611 StorageManager::getSearchList(SecPreferencesDomain domain, KeychainList &keychainList)
612 {
613 if (domain == mDomain)
614 {
615 StLock<Mutex> _(mLock);
616 mSavedList.revert(false);
617 convertList(keychainList, mSavedList.searchList());
618 }
619 else
620 {
621 convertList(keychainList, DLDbListCFPref(domain).searchList());
622 }
623 }
624
625 void
626 StorageManager::setSearchList(SecPreferencesDomain domain, const KeychainList &keychainList)
627 {
628 DLDbList searchList;
629 convertList(searchList, keychainList);
630
631 if (domain == mDomain)
632 {
633 DLDbList oldSearchList(mSavedList.searchList());
634 {
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);
640 mSavedList.save();
641 }
642
643 if (!(oldSearchList == searchList))
644 {
645 // Make sure we are not holding mLock when we post this event.
646 KCEventNotifier::PostKeychainEvent(kSecKeychainListChangedEvent);
647 }
648 }
649 else
650 {
651 DLDbListCFPref(domain).searchList(searchList);
652 }
653 }
654
655 void
656 StorageManager::domain(SecPreferencesDomain domain)
657 {
658 StLock<Mutex> _(mLock);
659 if (domain == mDomain)
660 return; // no change
661
662 #if !defined(NDEBUG)
663 switch (domain)
664 {
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;
669 default:
670 secdebug("storagemgr", "switching to weird prefs domain %d", domain); break;
671 }
672 #endif
673
674 mDomain = domain;
675 mSavedList.set(domain);
676 }
677
678 void
679 StorageManager::optionalSearchList(CFTypeRef keychainOrArray, KeychainList &keychainList)
680 {
681 if (!keychainOrArray)
682 getSearchList(keychainList);
683 else
684 {
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)));
690 else
691 MacOSError::throwMe(paramErr);
692 }
693 }
694
695 // static methods.
696 void
697 StorageManager::convertToKeychainList(CFArrayRef keychainArray, KeychainList &keychainList)
698 {
699 assert(keychainArray);
700 CFIndex count = CFArrayGetCount(keychainArray);
701 KeychainList keychains(count);
702 for (CFIndex ix = 0; ix < count; ++ix)
703 {
704 keychains[ix] = KeychainImpl::required(SecKeychainRef(CFArrayGetValueAtIndex(keychainArray, ix)));
705 }
706
707 keychainList.swap(keychains);
708 }
709
710 CFArrayRef
711 StorageManager::convertFromKeychainList(const KeychainList &keychainList)
712 {
713 CFRef<CFMutableArrayRef> keychainArray(CFArrayCreateMutable(NULL, keychainList.size(), &kCFTypeArrayCallBacks));
714
715 for (KeychainList::const_iterator ix = keychainList.begin(); ix != keychainList.end(); ++ix)
716 {
717 SecKeychainRef keychainRef = (*ix)->handle();
718 CFArrayAppendValue(keychainArray, keychainRef);
719 CFRelease(keychainRef);
720 }
721
722 // Counter the CFRelease that CFRef<> is about to do when keychainArray goes out of scope.
723 CFRetain(keychainArray);
724 return keychainArray;
725 }
726
727 void StorageManager::convertList(DLDbList &ids, const KeychainList &kcs)
728 {
729 DLDbList result;
730 result.reserve(kcs.size());
731 for (KeychainList::const_iterator ix = kcs.begin(); ix != kcs.end(); ++ix)
732 {
733 result.push_back((*ix)->dLDbIdentifier());
734 }
735 ids.swap(result);
736 }
737
738 void StorageManager::convertList(KeychainList &kcs, const DLDbList &ids)
739 {
740 KeychainList result;
741 result.reserve(ids.size());
742 for (DLDbList::const_iterator ix = ids.begin(); ix != ids.end(); ++ix)
743 {
744 Keychain keychain(_keychain(*ix));
745 result.push_back(keychain);
746 }
747 kcs.swap(result);
748 }
749
750 #pragma mark ÑÑÑÑ Login Functions ÑÑÑÑ
751
752 void StorageManager::login(AuthorizationRef authRef, UInt32 nameLength, const char* name)
753 {
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 )
758 {
759 // Grab the password from the auth context (info) and create the keychain...
760 //
761 AuthorizationItem* currItem = info->items;
762 for (UInt32 index = 1; index <= info->count; index++) //@@@plugin bug won't return a specific context.
763 {
764 if (strcmp(currItem->name, kAuthorizationEnvironmentPassword) == 0)
765 {
766 // creates the login keychain with the specified password
767 try
768 {
769 login(nameLength, name, currItem->valueLength, currItem->value);
770 created = true;
771 }
772 catch(...)
773 {
774 }
775 break;
776 }
777 currItem++;
778 }
779 }
780 if ( info )
781 AuthorizationFreeItemSet(info);
782
783 if ( !created )
784 MacOSError::throwMe(errAuthorizationInternal);
785 }
786
787 void StorageManager::login(ConstStringPtr name, ConstStringPtr password)
788 {
789 if ( name == NULL || password == NULL )
790 MacOSError::throwMe(paramErr);
791
792 login(name[0], name + 1, password[0], password + 1);
793 }
794
795 void StorageManager::login(UInt32 nameLength, const void *name, UInt32 passwordLength, const void *password)
796 {
797 x_debug("StorageManager::login: entered");
798 mSavedList.revert(true);
799 if (passwordLength != 0 && password == NULL)
800 {
801 x_debug("StorageManager::login: invalid argument (NULL password)");
802 MacOSError::throwMe(paramErr);
803 }
804
805 DLDbIdentifier loginDLDbIdentifier(mSavedList.loginDLDbIdentifier());
806 x_debug1("StorageManager::login: loginDLDbIdentifier is %s", (loginDLDbIdentifier) ? loginDLDbIdentifier.dbName() : "<NULL>");
807 if (!loginDLDbIdentifier)
808 MacOSError::throwMe(errSecNoSuchKeychain);
809
810 Keychain theKeychain(keychain(loginDLDbIdentifier));
811 try
812 {
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");
816 }
817 catch(const CssmError &e)
818 {
819 if (e.osStatus() != CSSMERR_DL_DATASTORE_DOESNOT_EXIST)
820 throw;
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);
828 }
829 }
830
831 void StorageManager::logout()
832 {
833 // nothing left to do here
834 }
835
836 void StorageManager::changeLoginPassword(ConstStringPtr oldPassword, ConstStringPtr newPassword)
837 {
838 loginKeychain()->changePassphrase(oldPassword, newPassword);
839 secdebug("KClogin", "Changed login keychain password successfully");
840 }
841
842
843 void StorageManager::changeLoginPassword(UInt32 oldPasswordLength, const void *oldPassword, UInt32 newPasswordLength, const void *newPassword)
844 {
845 loginKeychain()->changePassphrase(oldPasswordLength, oldPassword, newPasswordLength, newPassword);
846 secdebug("KClogin", "Changed login keychain password successfully");
847 }
848
849 // Clear out the keychain search list and rename the existing login.keychain.
850 //
851 void StorageManager::resetKeychain(Boolean resetSearchList)
852 {
853 // Clear the keychain search list.
854 //
855 CFArrayRef emptySearchList = nil;
856 try
857 {
858 if ( resetSearchList )
859 {
860 emptySearchList = CFArrayCreate(NULL, NULL, 0, NULL);
861 StorageManager::KeychainList keychainList;
862 convertToKeychainList(emptySearchList, keychainList);
863 setSearchList(keychainList);
864 }
865 // Get a reference to the existing login keychain...
866 // If we don't have one, we throw (not requiring a rename).
867 //
868 Keychain keychain = loginKeychain();
869 //
870 // Rename the existing login.keychain (i.e. put it aside).
871 //
872 CFMutableStringRef newName = NULL;
873 newName = CFStringCreateMutable(NULL, 0);
874 CFStringRef currName = NULL;
875 currName = CFStringCreateWithCString(NULL, keychain->name(), kCFStringEncodingUTF8);
876 if ( newName && currName )
877 {
878 CFStringAppend(newName, currName);
879 CFStringRef kcSuffix = CFSTR(kKeychainSuffix);
880 if ( CFStringHasSuffix(newName, kcSuffix) ) // remove the .keychain extension
881 {
882 CFRange suffixRange = CFStringFind(newName, kcSuffix, 0);
883 CFStringFindAndReplace(newName, kcSuffix, CFSTR(""), suffixRange, 0);
884 }
885 CFStringAppend(newName, CFSTR(kKeychainRenamedSuffix)); // add "_renamed"
886 try
887 {
888 renameUnique(keychain, newName);
889 }
890 catch(...)
891 {
892 // we need to release 'newName' & 'currName'
893 }
894 } // else, let the login call report a duplicate
895 if ( newName )
896 CFRelease(newName);
897 if ( currName )
898 CFRelease(currName);
899 }
900 catch(...)
901 {
902 // We either don't have a login keychain, or there was a
903 // failure to rename the existing one.
904 }
905 if ( emptySearchList )
906 CFRelease(emptySearchList);
907 }
908
909 #pragma mark ÑÑÑÑ File Related ÑÑÑÑ
910
911 Keychain StorageManager::make(const char *pathName)
912 {
913 return make(pathName, true);
914 }
915
916 Keychain StorageManager::make(const char *pathName, bool add)
917 {
918 string fullPathName;
919 if ( pathName[0] == '/' )
920 fullPathName = pathName;
921 else
922 {
923 // Get Home directory from environment.
924 switch (mDomain) {
925 case kSecPreferencesDomainUser:
926 {
927 const char *homeDir = getenv("HOME");
928 if (homeDir == NULL)
929 {
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);
934 if (!pw)
935 MacOSError::throwMe(paramErr);
936 homeDir = pw->pw_dir;
937 }
938 fullPathName = homeDir;
939 }
940 break;
941 case kSecPreferencesDomainSystem:
942 fullPathName = "";
943 break;
944 default:
945 assert(false); // invalid domain for this
946 }
947
948 fullPathName += "/Library/Keychains/";
949 fullPathName += pathName;
950 }
951
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);
960 }
961
962 Keychain StorageManager::makeLoginAuthUI(Item &item)
963 {
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.
967 //
968 OSStatus result = noErr;
969 Keychain keychain = NULL; // We return this keychain.
970 //
971 // Set up the Auth ref to bring up UI.
972 //
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 )
981 {
982 if ( authRef )
983 AuthorizationFree(authRef, kAuthorizationFlagDefaults);
984 MacOSError::throwMe(errAuthorizationInternal);
985 }
986 envir.items = authEnvirItemArrayPtr;
987 AuthorizationItem* currItem = authEnvirItemArrayPtr;
988 //
989 // 1st Hint (optional): The keychain item's account attribute string.
990 // When item is specified, we assume an 'add' operation is being attempted.
991 char buff[255];
992 UInt32 actLen;
993 SecKeychainAttribute attr = { kSecAccountItemAttr, 255, &buff };
994 try
995 {
996 item->getAttribute(attr, &actLen);
997 }
998 catch(...)
999 {
1000 actLen = 0; // This item didn't have the account attribute, so don't display one in the UI.
1001 }
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
1004 {
1005 if ( actLen > 255 )
1006 buff[255] = 0;
1007 else
1008 buff[actLen] = 0;
1009 currItem->valueLength = strlen(buff)+1;
1010 currItem->value = buff;
1011 }
1012 else
1013 {
1014 currItem->valueLength = 0;
1015 currItem->value = NULL;
1016 }
1017 currItem->flags = 0;
1018 //
1019 // 2nd Hint (optional): The item's keychain full path.
1020 //
1021 currItem++;
1022 char* currDefaultName = NULL;
1023 try
1024 {
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;
1030 currItem++;
1031 }
1032 catch(...)
1033 {
1034 envir.count--;
1035 }
1036
1037 //
1038 // 3rd Hint (optional): If curr default keychain is unavailable.
1039 // This is determined by the parent not existing.
1040 //
1041 currItem->name = AGENT_HINT_LOGIN_KC_EXISTS_IN_KC_FOLDER;
1042 Boolean loginUnavail = false;
1043 try
1044 {
1045 Keychain defaultKC = defaultKeychain();
1046 if ( !defaultKC->exists() )
1047 loginUnavail = true;
1048 }
1049 catch(...) // login.keychain not present
1050 {
1051 }
1052 currItem->valueLength = sizeof(Boolean);
1053 currItem->value = (void*)&loginUnavail;
1054 currItem->flags = 0;
1055 //
1056 // 4th Hint (required) userName
1057 //
1058 currItem++;
1059 currItem->name = AGENT_HINT_LOGIN_KC_USER_NAME;
1060 char* uName = getenv("USER");
1061 string userName = uName ? uName : "";
1062 if ( userName.length() == 0 )
1063 {
1064 uid_t uid = geteuid();
1065 if (!uid) uid = getuid();
1066 struct passwd *pw = getpwuid(uid); // fallback case...
1067 if (pw)
1068 userName = pw->pw_name;
1069 endpwent();
1070 }
1071 if ( userName.length() != 0 ) // did we ultimately get one?
1072 {
1073 currItem->value = (void*)userName.c_str();
1074 currItem->valueLength = userName.length();
1075 }
1076 else // trouble getting user name; can't continue...
1077 {
1078 if ( authRef )
1079 AuthorizationFree(authRef, kAuthorizationFlagDefaults);
1080 free(authEnvirItemArrayPtr);
1081 MacOSError::throwMe(errAuthorizationInternal);
1082 }
1083 currItem->flags = 0;
1084 //
1085 // 5th Hint (optional) flags if user has more than 1 keychain (used for a later warning when reset to default).
1086 //
1087 currItem++; // last hint...
1088 currItem->name = AGENT_HINT_LOGIN_KC_USER_HAS_OTHER_KCS_STR;
1089 Boolean moreThanOneKCExists = false;
1090 {
1091 StLock<Mutex> _(mLock);
1092 if (mSavedList.searchList().size() > 1)
1093 moreThanOneKCExists = true;
1094 }
1095 currItem->value = &moreThanOneKCExists;
1096 currItem->valueLength = sizeof(Boolean);
1097 currItem->flags = 0;
1098 //
1099 // Set up the auth rights and make the auth call.
1100 //
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.
1106 {
1107 try
1108 {
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.
1113 }
1114 catch(...)
1115 {
1116 // Reset failed, login.keychain creation failed, or setting it to default.
1117 // We need to release 'authRef'...
1118 }
1119 }
1120 if ( authRef )
1121 AuthorizationFree(authRef, kAuthorizationFlagDefaults);
1122 if ( result )
1123 MacOSError::throwMe(result); // Any other error means we don't return a keychain.
1124 return keychain;
1125 }
1126
1127 Keychain StorageManager::defaultKeychainUI(Item &item)
1128 {
1129 Keychain returnedKeychain = NULL;
1130 try
1131 {
1132 returnedKeychain = globals().storageManager.defaultKeychain(); // If we have one, return it.
1133 if ( returnedKeychain->exists() )
1134 return returnedKeychain;
1135 }
1136 catch(...) // We could have one, but it isn't available (i.e. on a un-mounted volume).
1137 {
1138 }
1139 if ( globals().getUserInteractionAllowed() )
1140 {
1141 returnedKeychain = makeLoginAuthUI(item); // If no Keychains Ä is present, one will be created.
1142 if ( !returnedKeychain )
1143 MacOSError::throwMe(errSecInvalidKeychain); // Something went wrong...
1144 }
1145 else
1146 MacOSError::throwMe(errSecInteractionNotAllowed); // If UI isn't allowed, return an error.
1147
1148 return returnedKeychain;
1149 }