]> git.saurik.com Git - apple/security.git/blob - libsecurity_keychain/lib/StorageManager.cpp
Security-55163.44.tar.gz
[apple/security.git] / libsecurity_keychain / lib / StorageManager.cpp
1 /*
2 * Copyright (c) 2000-2004,2012 Apple Inc. All Rights Reserved.
3 *
4 * @APPLE_LICENSE_HEADER_START@
5 *
6 * This file contains Original Code and/or Modifications of Original Code
7 * as defined in and that are subject to the Apple Public Source License
8 * Version 2.0 (the 'License'). You may not use this file except in
9 * compliance with the License. Please obtain a copy of the License at
10 * http://www.opensource.apple.com/apsl/ and read it before using this
11 * file.
12 *
13 * The Original Code and all software distributed under the License are
14 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
15 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
16 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
18 * Please see the License for the specific language governing rights and
19 * limitations under the License.
20 *
21 * @APPLE_LICENSE_HEADER_END@
22 */
23
24
25 /*
26 File: StorageManager.cpp
27
28 Contains: Working with multiple keychains
29
30 */
31
32 #include "StorageManager.h"
33 #include "KCEventNotifier.h"
34
35 #include <Security/cssmapple.h>
36 #include <sys/types.h>
37 #include <sys/param.h>
38 #include <syslog.h>
39 #include <pwd.h>
40 #include <CoreServices/../Frameworks/CarbonCore.framework/Headers/MacErrors.h>
41 #include <algorithm>
42 #include <string>
43 #include <stdio.h>
44 //#include <Security/AuthorizationTags.h>
45 //#include <Security/AuthSession.h>
46 #include <security_utilities/debugging.h>
47 #include <security_keychain/SecCFTypes.h>
48 //#include <Security/SecurityAgentClient.h>
49 #include <securityd_client/ssclient.h>
50 #include <Security/AuthorizationTags.h>
51 #include <Security/AuthorizationTagsPriv.h>
52 #include <Security/SecTask.h>
53 #include <security_keychain/SecCFTypes.h>
54
55 //%%% add this to AuthorizationTagsPriv.h later
56 #ifndef AGENT_HINT_LOGIN_KC_SUPPRESS_RESET_PANEL
57 #define AGENT_HINT_LOGIN_KC_SUPPRESS_RESET_PANEL "loginKCCreate:suppressResetPanel"
58 #endif
59
60 #include "KCCursor.h"
61 #include "Globals.h"
62
63
64 using namespace CssmClient;
65 using namespace KeychainCore;
66
67 #define kLoginKeychainPathPrefix "~/Library/Keychains/"
68 #define kUserLoginKeychainPath "~/Library/Keychains/login.keychain"
69 #define kEmptyKeychainSizeInBytes 20460
70
71 //-----------------------------------------------------------------------------------
72
73 static SecPreferencesDomain defaultPreferenceDomain()
74 {
75 SessionAttributeBits sessionAttrs;
76 if (gServerMode) {
77 secdebug("servermode", "StorageManager initialized in server mode");
78 sessionAttrs = sessionIsRoot;
79 } else {
80 MacOSError::check(SessionGetInfo(callerSecuritySession, NULL, &sessionAttrs));
81 }
82
83 // If this is the root session, use system preferences.
84 // (In SecurityServer debug mode, you'll get a (fake) root session
85 // that has graphics access. Ignore that to help testing.)
86 if ((sessionAttrs & sessionIsRoot)
87 IFDEBUG( && !(sessionAttrs & sessionHasGraphicAccess))) {
88 secdebug("storagemgr", "using system preferences");
89 return kSecPreferencesDomainSystem;
90 }
91
92 // otherwise, use normal (user) preferences
93 return kSecPreferencesDomainUser;
94 }
95
96 static bool isAppSandboxed()
97 {
98 bool result = false;
99 SecTaskRef task = SecTaskCreateFromSelf(NULL);
100 if(task != NULL) {
101 CFTypeRef appSandboxValue = SecTaskCopyValueForEntitlement(task,
102 CFSTR("com.apple.security.app-sandbox"), NULL);
103 if(appSandboxValue != NULL) {
104 result = true;
105 CFRelease(appSandboxValue);
106 }
107 CFRelease(task);
108 }
109 return result;
110 }
111
112
113 StorageManager::StorageManager() :
114 mSavedList(defaultPreferenceDomain()),
115 mCommonList(kSecPreferencesDomainCommon),
116 mDomain(kSecPreferencesDomainUser),
117 mMutex(Mutex::recursive)
118 {
119 }
120
121
122 Mutex*
123 StorageManager::getStorageManagerMutex()
124 {
125 return &mKeychainMapMutex;
126 }
127
128
129 Keychain
130 StorageManager::keychain(const DLDbIdentifier &dLDbIdentifier)
131 {
132 StLock<Mutex>_(mKeychainMapMutex);
133
134 if (!dLDbIdentifier)
135 return Keychain();
136
137 if (gServerMode) {
138 secdebug("servermode", "keychain reference in server mode");
139 return Keychain();
140 }
141
142 KeychainMap::iterator it = mKeychains.find(dLDbIdentifier);
143 if (it != mKeychains.end())
144 {
145 if (it->second == NULL) // cleared by weak reference?
146 {
147 mKeychains.erase(it);
148 }
149 else
150 {
151 return it->second;
152 }
153 }
154
155 // The keychain is not in our cache. Create it.
156 Module module(dLDbIdentifier.ssuid().guid());
157 DL dl;
158 if (dLDbIdentifier.ssuid().subserviceType() & CSSM_SERVICE_CSP)
159 dl = SSCSPDL(module);
160 else
161 dl = DL(module);
162
163 dl->subserviceId(dLDbIdentifier.ssuid().subserviceId());
164 dl->version(dLDbIdentifier.ssuid().version());
165 Db db(dl, dLDbIdentifier.dbName());
166
167 Keychain keychain(db);
168 // Add the keychain to the cache.
169 mKeychains.insert(KeychainMap::value_type(dLDbIdentifier, &*keychain));
170 keychain->inCache(true);
171
172 return keychain;
173 }
174
175 void
176 StorageManager::removeKeychain(const DLDbIdentifier &dLDbIdentifier,
177 KeychainImpl *keychainImpl)
178 {
179 // Lock the recursive mutex
180
181 StLock<Mutex>_(mKeychainMapMutex);
182
183 KeychainMap::iterator it = mKeychains.find(dLDbIdentifier);
184 if (it != mKeychains.end() && (KeychainImpl*) it->second == keychainImpl)
185 mKeychains.erase(it);
186
187 keychainImpl->inCache(false);
188 }
189
190 void
191 StorageManager::didRemoveKeychain(const DLDbIdentifier &dLDbIdentifier)
192 {
193 // Lock the recursive mutex
194
195 StLock<Mutex>_(mKeychainMapMutex);
196
197 KeychainMap::iterator it = mKeychains.find(dLDbIdentifier);
198 if (it != mKeychains.end())
199 {
200 if (it->second != NULL) // did we get zapped by weak reference destruction
201 {
202 KeychainImpl *keychainImpl = it->second;
203 keychainImpl->inCache(false);
204 }
205
206 mKeychains.erase(it);
207 }
208 }
209
210 // Create keychain if it doesn't exist, and optionally add it to the search list.
211 Keychain
212 StorageManager::makeKeychain(const DLDbIdentifier &dLDbIdentifier, bool add)
213 {
214 StLock<Mutex>_(mKeychainMapMutex);
215
216 Keychain theKeychain = keychain(dLDbIdentifier);
217 bool post = false;
218 bool updateList = (add && !isAppSandboxed());
219
220 if (updateList)
221 {
222 mSavedList.revert(false);
223 DLDbList searchList = mSavedList.searchList();
224 if (find(searchList.begin(), searchList.end(), dLDbIdentifier) != searchList.end())
225 return theKeychain; // theKeychain is already in the searchList.
226
227 mCommonList.revert(false);
228 searchList = mCommonList.searchList();
229 if (find(searchList.begin(), searchList.end(), dLDbIdentifier) != searchList.end())
230 return theKeychain; // theKeychain is already in the commonList don't add it to the searchList.
231
232 // If theKeychain doesn't exist don't bother adding it to the search list yet.
233 if (!theKeychain->exists())
234 return theKeychain;
235
236 // theKeychain exists and is not in our search list, so add it to the
237 // search list.
238 mSavedList.revert(true);
239 mSavedList.add(dLDbIdentifier);
240 mSavedList.save();
241 post = true;
242 }
243
244 if (post)
245 {
246 // Make sure we are not holding mStorageManagerLock anymore when we
247 // post this event.
248 KCEventNotifier::PostKeychainEvent(kSecKeychainListChangedEvent);
249 }
250
251 return theKeychain;
252 }
253
254 // Be notified a Keychain just got created.
255 void
256 StorageManager::created(const Keychain &keychain)
257 {
258 StLock<Mutex>_(mKeychainMapMutex);
259
260 DLDbIdentifier dLDbIdentifier = keychain->dlDbIdentifier();
261 bool defaultChanged = false;
262 bool updateList = (!isAppSandboxed());
263
264 if (updateList)
265 {
266 mSavedList.revert(true);
267 // If we don't have a default Keychain yet. Make the newly created
268 // keychain the default.
269 if (!mSavedList.defaultDLDbIdentifier())
270 {
271 mSavedList.defaultDLDbIdentifier(dLDbIdentifier);
272 defaultChanged = true;
273 }
274
275 // Add the keychain to the search list prefs.
276 mSavedList.add(dLDbIdentifier);
277 mSavedList.save();
278
279 // Make sure we are not holding mLock when we post these events.
280 KCEventNotifier::PostKeychainEvent(kSecKeychainListChangedEvent);
281 }
282
283 if (defaultChanged)
284 {
285 KCEventNotifier::PostKeychainEvent(kSecDefaultChangedEvent, dLDbIdentifier);
286 }
287 }
288
289 KCCursor
290 StorageManager::createCursor(SecItemClass itemClass,
291 const SecKeychainAttributeList *attrList)
292 {
293 StLock<Mutex>_(mMutex);
294
295 KeychainList searchList;
296 getSearchList(searchList);
297 return KCCursor(searchList, itemClass, attrList);
298 }
299
300 KCCursor
301 StorageManager::createCursor(const SecKeychainAttributeList *attrList)
302 {
303 StLock<Mutex>_(mMutex);
304
305 KeychainList searchList;
306 getSearchList(searchList);
307 return KCCursor(searchList, attrList);
308 }
309
310 void
311 StorageManager::lockAll()
312 {
313 StLock<Mutex>_(mMutex);
314
315 SecurityServer::ClientSession ss(Allocator::standard(), Allocator::standard());
316 ss.lockAll (false);
317 }
318
319 Keychain
320 StorageManager::defaultKeychain()
321 {
322 StLock<Mutex>_(mMutex);
323
324 Keychain theKeychain;
325 CFTypeRef ref;
326
327 {
328 mSavedList.revert(false);
329 DLDbIdentifier defaultDLDbIdentifier(mSavedList.defaultDLDbIdentifier());
330 if (defaultDLDbIdentifier)
331 {
332 theKeychain = keychain(defaultDLDbIdentifier);
333 ref = theKeychain->handle(false);
334 }
335 }
336
337 if (theKeychain /* && theKeychain->exists() */)
338 return theKeychain;
339
340 MacOSError::throwMe(errSecNoDefaultKeychain);
341 }
342
343 void
344 StorageManager::defaultKeychain(const Keychain &keychain)
345 {
346 StLock<Mutex>_(mMutex);
347
348 // Only set a keychain as the default if we own it and can read/write it,
349 // and our uid allows modifying the directory for that preference domain.
350 if (!keychainOwnerPermissionsValidForDomain(keychain->name(), mDomain))
351 MacOSError::throwMe(wrPermErr);
352
353 DLDbIdentifier oldDefaultId;
354 DLDbIdentifier newDefaultId(keychain->dlDbIdentifier());
355 {
356 oldDefaultId = mSavedList.defaultDLDbIdentifier();
357 mSavedList.revert(true);
358 mSavedList.defaultDLDbIdentifier(newDefaultId);
359 mSavedList.save();
360 }
361
362 if (!(oldDefaultId == newDefaultId))
363 {
364 // Make sure we are not holding mLock when we post this event.
365 KCEventNotifier::PostKeychainEvent(kSecDefaultChangedEvent, newDefaultId);
366 }
367 }
368
369 Keychain
370 StorageManager::defaultKeychain(SecPreferencesDomain domain)
371 {
372 StLock<Mutex>_(mMutex);
373
374 if (domain == kSecPreferencesDomainDynamic)
375 MacOSError::throwMe(errSecInvalidPrefsDomain);
376
377 if (domain == mDomain)
378 return defaultKeychain();
379 else
380 {
381 DLDbIdentifier defaultDLDbIdentifier(DLDbListCFPref(domain).defaultDLDbIdentifier());
382 if (defaultDLDbIdentifier)
383 return keychain(defaultDLDbIdentifier);
384
385 MacOSError::throwMe(errSecNoDefaultKeychain);
386 }
387 }
388
389 void
390 StorageManager::defaultKeychain(SecPreferencesDomain domain, const Keychain &keychain)
391 {
392 StLock<Mutex>_(mMutex);
393
394 if (domain == kSecPreferencesDomainDynamic)
395 MacOSError::throwMe(errSecInvalidPrefsDomain);
396
397 if (domain == mDomain)
398 defaultKeychain(keychain);
399 else
400 DLDbListCFPref(domain).defaultDLDbIdentifier(keychain->dlDbIdentifier());
401 }
402
403 Keychain
404 StorageManager::loginKeychain()
405 {
406 StLock<Mutex>_(mMutex);
407
408 Keychain theKeychain;
409 {
410 mSavedList.revert(false);
411 DLDbIdentifier loginDLDbIdentifier(mSavedList.loginDLDbIdentifier());
412 if (loginDLDbIdentifier)
413 {
414 theKeychain = keychain(loginDLDbIdentifier);
415 }
416 }
417
418 if (theKeychain && theKeychain->exists())
419 return theKeychain;
420
421 MacOSError::throwMe(errSecNoSuchKeychain);
422 }
423
424 void
425 StorageManager::loginKeychain(Keychain keychain)
426 {
427 StLock<Mutex>_(mMutex);
428
429 mSavedList.revert(true);
430 mSavedList.loginDLDbIdentifier(keychain->dlDbIdentifier());
431 mSavedList.save();
432 }
433
434 size_t
435 StorageManager::size()
436 {
437 StLock<Mutex>_(mMutex);
438
439 mSavedList.revert(false);
440 mCommonList.revert(false);
441 return mSavedList.searchList().size() + mCommonList.searchList().size();
442 }
443
444 Keychain
445 StorageManager::at(unsigned int ix)
446 {
447 StLock<Mutex>_(mMutex);
448
449 mSavedList.revert(false);
450 DLDbList dLDbList = mSavedList.searchList();
451 if (ix < dLDbList.size())
452 {
453 return keychain(dLDbList[ix]);
454 }
455 else
456 {
457 ix -= dLDbList.size();
458 mCommonList.revert(false);
459 DLDbList commonList = mCommonList.searchList();
460 if (ix >= commonList.size())
461 MacOSError::throwMe(errSecInvalidKeychain);
462
463 return keychain(commonList[ix]);
464 }
465 }
466
467 Keychain
468 StorageManager::operator[](unsigned int ix)
469 {
470 StLock<Mutex>_(mMutex);
471
472 return at(ix);
473 }
474
475 void StorageManager::rename(Keychain keychain, const char* newName)
476 {
477
478 StLock<Mutex>_(mKeychainMapMutex);
479
480 bool changedDefault = false;
481 DLDbIdentifier newDLDbIdentifier;
482 {
483 mSavedList.revert(true);
484 DLDbIdentifier defaultId = mSavedList.defaultDLDbIdentifier();
485
486 // Find the keychain object for the given ref
487 DLDbIdentifier dLDbIdentifier = keychain->dlDbIdentifier();
488
489 // Actually rename the database on disk.
490 keychain->database()->rename(newName);
491
492 if (dLDbIdentifier == defaultId)
493 changedDefault=true;
494
495 newDLDbIdentifier = keychain->dlDbIdentifier();
496 // Rename the keychain in the search list.
497 mSavedList.rename(dLDbIdentifier, newDLDbIdentifier);
498
499 // If this was the default keychain change it accordingly
500 if (changedDefault)
501 mSavedList.defaultDLDbIdentifier(newDLDbIdentifier);
502
503 mSavedList.save();
504
505 // we aren't worried about a weak reference here, because we have to
506 // hold a lock on an item in order to do the rename
507
508 // Now update the Keychain cache
509 if (keychain->inCache())
510 {
511 KeychainMap::iterator it = mKeychains.find(dLDbIdentifier);
512 if (it != mKeychains.end() && (KeychainImpl*) it->second == keychain.get())
513 {
514 // Remove the keychain from the cache under its old
515 // dLDbIdentifier
516 mKeychains.erase(it);
517 }
518 }
519
520 // If we renamed this keychain on top of an existing one we should
521 // drop the old one from the cache.
522 KeychainMap::iterator it = mKeychains.find(newDLDbIdentifier);
523 if (it != mKeychains.end())
524 {
525 Keychain oldKeychain(it->second);
526 oldKeychain->inCache(false);
527 // @@@ Ideally we should invalidate or fault this keychain object.
528 }
529
530 if (keychain->inCache())
531 {
532 // If the keychain wasn't in the cache to being with let's not put
533 // it there now. There was probably a good reason it wasn't in it.
534 // If the keychain was in the cache, update it to use
535 // newDLDbIdentifier.
536 mKeychains.insert(KeychainMap::value_type(newDLDbIdentifier,
537 keychain));
538 }
539 }
540
541 // Make sure we are not holding mLock when we post these events.
542 KCEventNotifier::PostKeychainEvent(kSecKeychainListChangedEvent);
543
544 if (changedDefault)
545 KCEventNotifier::PostKeychainEvent(kSecDefaultChangedEvent,
546 newDLDbIdentifier);
547 }
548
549 void StorageManager::renameUnique(Keychain keychain, CFStringRef newName)
550 {
551 StLock<Mutex>_(mMutex);
552
553 bool doneCreating = false;
554 int index = 1;
555 do
556 {
557 char newNameCString[MAXPATHLEN];
558 if ( CFStringGetCString(newName, newNameCString, MAXPATHLEN, kCFStringEncodingUTF8) ) // make sure it fits in MAXPATHLEN, etc.
559 {
560 // Construct the new name...
561 //
562 CFMutableStringRef newNameCFStr = NULL;
563 newNameCFStr = CFStringCreateMutable(NULL, MAXPATHLEN);
564 if ( newNameCFStr )
565 {
566 CFStringAppendFormat(newNameCFStr, NULL, CFSTR("%s%d"), &newNameCString, index);
567 CFStringAppend(newNameCFStr, CFSTR(kKeychainSuffix)); // add .keychain
568 char toUseBuff2[MAXPATHLEN];
569 if ( CFStringGetCString(newNameCFStr, toUseBuff2, MAXPATHLEN, kCFStringEncodingUTF8) ) // make sure it fits in MAXPATHLEN, etc.
570 {
571 struct stat filebuf;
572 if ( lstat(toUseBuff2, &filebuf) )
573 {
574 rename(keychain, toUseBuff2);
575 KeychainList kcList;
576 kcList.push_back(keychain);
577 remove(kcList, false);
578 doneCreating = true;
579 }
580 else
581 index++;
582 }
583 else
584 doneCreating = true; // failure to get c string.
585 CFRelease(newNameCFStr);
586 }
587 else
588 doneCreating = false; // failure to create mutable string.
589 }
590 else
591 doneCreating = false; // failure to get the string (i.e. > MAXPATHLEN?)
592 }
593 while (!doneCreating && index != INT_MAX);
594 }
595
596 #define KEYCHAIN_SYNC_KEY CFSTR("KeychainSyncList")
597 #define KEYCHAIN_SYNC_DOMAIN CFSTR("com.apple.keychainsync")
598
599 static CFStringRef MakeExpandedPath (const char* path)
600 {
601 std::string name = DLDbListCFPref::ExpandTildesInPath (std::string (path));
602 CFStringRef expanded = CFStringCreateWithCString (NULL, name.c_str (), 0);
603 return expanded;
604 }
605
606 void StorageManager::removeKeychainFromSyncList (const DLDbIdentifier &id)
607 {
608 StLock<Mutex>_(mMutex);
609
610 // make a CFString of our identifier
611 const char* idname = id.dbName ();
612 if (idname == NULL)
613 {
614 return;
615 }
616
617 CFRef<CFStringRef> idString = MakeExpandedPath (idname);
618
619 // check and see if this keychain is in the keychain syncing list
620 CFArrayRef value =
621 (CFArrayRef) CFPreferencesCopyValue (KEYCHAIN_SYNC_KEY,
622 KEYCHAIN_SYNC_DOMAIN,
623 kCFPreferencesCurrentUser,
624 kCFPreferencesAnyHost);
625 if (value == NULL)
626 {
627 return;
628 }
629
630 // make a mutable copy of the dictionary
631 CFRef<CFMutableArrayRef> mtValue = CFArrayCreateMutableCopy (NULL, 0, value);
632 CFRelease (value);
633
634 // walk the array, looking for the value
635 CFIndex i;
636 CFIndex limit = CFArrayGetCount (mtValue.get());
637 bool found = false;
638
639 for (i = 0; i < limit; ++i)
640 {
641 CFDictionaryRef idx = (CFDictionaryRef) CFArrayGetValueAtIndex (mtValue.get(), i);
642 CFStringRef v = (CFStringRef) CFDictionaryGetValue (idx, CFSTR("DbName"));
643 if (v == NULL)
644 {
645 return; // something is really wrong if this is taken
646 }
647
648 CFStringRef vExpanded = MakeExpandedPath (CFStringGetCStringPtr (v, 0));
649 CFComparisonResult result = CFStringCompare (vExpanded, idString.get(), 0);
650 CFRelease (vExpanded);
651
652 if (result == 0)
653 {
654 CFArrayRemoveValueAtIndex (mtValue.get(), i);
655 found = true;
656 break;
657 }
658 }
659
660 if (found)
661 {
662 #ifndef NDEBUG
663 CFShow (mtValue.get());
664 #endif
665
666 CFPreferencesSetValue (KEYCHAIN_SYNC_KEY,
667 mtValue,
668 KEYCHAIN_SYNC_DOMAIN,
669 kCFPreferencesCurrentUser,
670 kCFPreferencesAnyHost);
671 CFPreferencesSynchronize (KEYCHAIN_SYNC_DOMAIN, kCFPreferencesCurrentUser, kCFPreferencesAnyHost);
672 }
673 }
674
675 void StorageManager::remove(const KeychainList &kcsToRemove, bool deleteDb)
676 {
677 StLock<Mutex>_(mMutex);
678
679 bool unsetDefault = false;
680 bool updateList = (!isAppSandboxed());
681
682 if (updateList)
683 {
684 mSavedList.revert(true);
685 DLDbIdentifier defaultId = mSavedList.defaultDLDbIdentifier();
686 for (KeychainList::const_iterator ix = kcsToRemove.begin();
687 ix != kcsToRemove.end(); ++ix)
688 {
689 // Find the keychain object for the given ref
690 Keychain theKeychain = *ix;
691 DLDbIdentifier dLDbIdentifier = theKeychain->dlDbIdentifier();
692
693 // Remove it from the saved list
694 mSavedList.remove(dLDbIdentifier);
695 if (dLDbIdentifier == defaultId)
696 unsetDefault=true;
697
698 if (deleteDb)
699 {
700 removeKeychainFromSyncList (dLDbIdentifier);
701
702 // Now remove it from the cache
703 removeKeychain(dLDbIdentifier, theKeychain.get());
704 }
705 }
706
707 if (unsetDefault)
708 mSavedList.defaultDLDbIdentifier(DLDbIdentifier());
709
710 mSavedList.save();
711 }
712
713 if (deleteDb)
714 {
715 // Delete the actual databases without holding any locks.
716 for (KeychainList::const_iterator ix = kcsToRemove.begin();
717 ix != kcsToRemove.end(); ++ix)
718 {
719 (*ix)->database()->deleteDb();
720 }
721 }
722
723 if (updateList) {
724 // Make sure we are not holding mLock when we post these events.
725 KCEventNotifier::PostKeychainEvent(kSecKeychainListChangedEvent);
726 }
727
728 if (unsetDefault)
729 KCEventNotifier::PostKeychainEvent(kSecDefaultChangedEvent);
730 }
731
732 void
733 StorageManager::getSearchList(KeychainList &keychainList)
734 {
735 // hold the global lock since we make keychain objects in this function
736
737 // to do: each of the items in this list must be retained, otherwise mayhem will occur
738 StLock<Mutex>_(mMutex);
739
740 if (gServerMode) {
741 keychainList.clear();
742 return;
743 }
744
745 mSavedList.revert(false);
746 mCommonList.revert(false);
747
748 // Merge mSavedList, mDynamicList and mCommonList
749 DLDbList dLDbList = mSavedList.searchList();
750 DLDbList dynamicList = mDynamicList.searchList();
751 DLDbList commonList = mCommonList.searchList();
752 KeychainList result;
753 result.reserve(dLDbList.size() + dynamicList.size() + commonList.size());
754
755 {
756 for (DLDbList::const_iterator it = dynamicList.begin();
757 it != dynamicList.end(); ++it)
758 {
759 Keychain k = keychain(*it);
760 result.push_back(k);
761 }
762
763 for (DLDbList::const_iterator it = dLDbList.begin();
764 it != dLDbList.end(); ++it)
765 {
766 Keychain k = keychain(*it);
767 result.push_back(k);
768 }
769
770 for (DLDbList::const_iterator it = commonList.begin();
771 it != commonList.end(); ++it)
772 {
773 Keychain k = keychain(*it);
774 result.push_back(k);
775 }
776 }
777
778 keychainList.swap(result);
779 }
780
781 void
782 StorageManager::setSearchList(const KeychainList &keychainList)
783 {
784 StLock<Mutex>_(mMutex);
785
786 DLDbList commonList = mCommonList.searchList();
787
788 // Strip out the common list part from the end of the search list.
789 KeychainList::const_iterator it_end = keychainList.end();
790 DLDbList::const_reverse_iterator end_common = commonList.rend();
791 for (DLDbList::const_reverse_iterator it_common = commonList.rbegin(); it_common != end_common; ++it_common)
792 {
793 // Eliminate common entries from the end of the passed in keychainList.
794 if (it_end == keychainList.begin())
795 break;
796
797 --it_end;
798 if (!((*it_end)->dlDbIdentifier() == *it_common))
799 {
800 ++it_end;
801 break;
802 }
803 }
804
805 /* it_end now points one past the last element in keychainList which is not in commonList. */
806 DLDbList searchList, oldSearchList(mSavedList.searchList());
807 for (KeychainList::const_iterator it = keychainList.begin(); it != it_end; ++it)
808 {
809 searchList.push_back((*it)->dlDbIdentifier());
810 }
811
812 {
813 // Set the current searchlist to be what was passed in, the old list will be freed
814 // upon exit of this stackframe.
815 mSavedList.revert(true);
816 mSavedList.searchList(searchList);
817 mSavedList.save();
818 }
819
820 if (!(oldSearchList == searchList))
821 {
822 // Make sure we are not holding mLock when we post this event.
823 KCEventNotifier::PostKeychainEvent(kSecKeychainListChangedEvent);
824 }
825 }
826
827 void
828 StorageManager::getSearchList(SecPreferencesDomain domain, KeychainList &keychainList)
829 {
830 StLock<Mutex>_(mMutex);
831
832 if (gServerMode) {
833 keychainList.clear();
834 return;
835 }
836
837 if (domain == kSecPreferencesDomainDynamic)
838 {
839 convertList(keychainList, mDynamicList.searchList());
840 }
841 else if (domain == mDomain)
842 {
843 mSavedList.revert(false);
844 convertList(keychainList, mSavedList.searchList());
845 }
846 else
847 {
848 convertList(keychainList, DLDbListCFPref(domain).searchList());
849 }
850 }
851
852 void StorageManager::forceUserSearchListReread()
853 {
854 mSavedList.forceUserSearchListReread();
855 }
856
857 void
858 StorageManager::setSearchList(SecPreferencesDomain domain, const KeychainList &keychainList)
859 {
860 StLock<Mutex>_(mMutex);
861
862 if (domain == kSecPreferencesDomainDynamic)
863 MacOSError::throwMe(errSecInvalidPrefsDomain);
864
865 DLDbList searchList;
866 convertList(searchList, keychainList);
867
868 if (domain == mDomain)
869 {
870 DLDbList oldSearchList(mSavedList.searchList());
871 {
872 // Set the current searchlist to be what was passed in, the old list will be freed
873 // upon exit of this stackframe.
874 mSavedList.revert(true);
875 mSavedList.searchList(searchList);
876 mSavedList.save();
877 }
878
879 if (!(oldSearchList == searchList))
880 {
881 KCEventNotifier::PostKeychainEvent(kSecKeychainListChangedEvent);
882 }
883 }
884 else
885 {
886 DLDbListCFPref(domain).searchList(searchList);
887 }
888 }
889
890 void
891 StorageManager::domain(SecPreferencesDomain domain)
892 {
893 StLock<Mutex>_(mMutex);
894
895 if (domain == kSecPreferencesDomainDynamic)
896 MacOSError::throwMe(errSecInvalidPrefsDomain);
897
898 if (domain == mDomain)
899 return; // no change
900
901 #if !defined(NDEBUG)
902 switch (domain)
903 {
904 case kSecPreferencesDomainSystem:
905 secdebug("storagemgr", "switching to system domain"); break;
906 case kSecPreferencesDomainUser:
907 secdebug("storagemgr", "switching to user domain (uid %d)", getuid()); break;
908 default:
909 secdebug("storagemgr", "switching to weird prefs domain %d", domain); break;
910 }
911 #endif
912
913 mDomain = domain;
914 mSavedList.set(domain);
915 }
916
917 void
918 StorageManager::optionalSearchList(CFTypeRef keychainOrArray, KeychainList &keychainList)
919 {
920 StLock<Mutex>_(mMutex);
921
922 if (!keychainOrArray)
923 getSearchList(keychainList);
924 else
925 {
926 CFTypeID typeID = CFGetTypeID(keychainOrArray);
927 if (typeID == CFArrayGetTypeID())
928 convertToKeychainList(CFArrayRef(keychainOrArray), keychainList);
929 else if (typeID == gTypes().KeychainImpl.typeID)
930 keychainList.push_back(KeychainImpl::required(SecKeychainRef(keychainOrArray)));
931 else
932 MacOSError::throwMe(paramErr);
933 }
934 }
935
936 // static methods.
937 void
938 StorageManager::convertToKeychainList(CFArrayRef keychainArray, KeychainList &keychainList)
939 {
940 CFIndex count = CFArrayGetCount(keychainArray);
941 if (!(count > 0))
942 return;
943
944 KeychainList keychains(count);
945 for (CFIndex ix = 0; ix < count; ++ix)
946 {
947 keychains[ix] = KeychainImpl::required(SecKeychainRef(CFArrayGetValueAtIndex(keychainArray, ix)));
948 }
949
950 keychainList.swap(keychains);
951 }
952
953 CFArrayRef
954 StorageManager::convertFromKeychainList(const KeychainList &keychainList)
955 {
956 CFRef<CFMutableArrayRef> keychainArray(CFArrayCreateMutable(NULL, keychainList.size(), &kCFTypeArrayCallBacks));
957
958 for (KeychainList::const_iterator ix = keychainList.begin(); ix != keychainList.end(); ++ix)
959 {
960 SecKeychainRef keychainRef = (*ix)->handle();
961 CFArrayAppendValue(keychainArray, keychainRef);
962 CFRelease(keychainRef);
963 }
964
965 // Counter the CFRelease that CFRef<> is about to do when keychainArray goes out of scope.
966 CFRetain(keychainArray);
967 return keychainArray;
968 }
969
970 void StorageManager::convertList(DLDbList &ids, const KeychainList &kcs)
971 {
972 DLDbList result;
973 result.reserve(kcs.size());
974 for (KeychainList::const_iterator ix = kcs.begin(); ix != kcs.end(); ++ix)
975 {
976 result.push_back((*ix)->dlDbIdentifier());
977 }
978 ids.swap(result);
979 }
980
981 void StorageManager::convertList(KeychainList &kcs, const DLDbList &ids)
982 {
983 StLock<Mutex>_(mMutex);
984
985 KeychainList result;
986 result.reserve(ids.size());
987 {
988 for (DLDbList::const_iterator ix = ids.begin(); ix != ids.end(); ++ix)
989 result.push_back(keychain(*ix));
990 }
991 kcs.swap(result);
992 }
993
994 #pragma mark ____ Login Functions ____
995
996 void StorageManager::login(AuthorizationRef authRef, UInt32 nameLength, const char* name)
997 {
998 StLock<Mutex>_(mMutex);
999
1000 AuthorizationItemSet* info = NULL;
1001 OSStatus result = AuthorizationCopyInfo(authRef, NULL, &info); // get the results of the copy rights call.
1002 Boolean created = false;
1003 if ( result == noErr && info->count )
1004 {
1005 // Grab the password from the auth context (info) and create the keychain...
1006 //
1007 AuthorizationItem* currItem = info->items;
1008 for (UInt32 index = 1; index <= info->count; index++) //@@@plugin bug won't return a specific context.
1009 {
1010 if (strcmp(currItem->name, kAuthorizationEnvironmentPassword) == 0)
1011 {
1012 // creates the login keychain with the specified password
1013 try
1014 {
1015 login(nameLength, name, currItem->valueLength, currItem->value);
1016 created = true;
1017 }
1018 catch(...)
1019 {
1020 }
1021 break;
1022 }
1023 currItem++;
1024 }
1025 }
1026 if ( info )
1027 AuthorizationFreeItemSet(info);
1028
1029 if ( !created )
1030 MacOSError::throwMe(errAuthorizationInternal);
1031 }
1032
1033 void StorageManager::login(ConstStringPtr name, ConstStringPtr password)
1034 {
1035 StLock<Mutex>_(mMutex);
1036
1037 if ( name == NULL || password == NULL )
1038 MacOSError::throwMe(paramErr);
1039
1040 login(name[0], name + 1, password[0], password + 1);
1041 }
1042
1043 void StorageManager::login(UInt32 nameLength, const void *name,
1044 UInt32 passwordLength, const void *password)
1045 {
1046 if (passwordLength != 0 && password == NULL)
1047 {
1048 secdebug("KCLogin", "StorageManager::login: invalid argument (NULL password)");
1049 MacOSError::throwMe(paramErr);
1050 }
1051
1052 DLDbIdentifier loginDLDbIdentifier;
1053 {
1054 mSavedList.revert(true);
1055 loginDLDbIdentifier = mSavedList.loginDLDbIdentifier();
1056 }
1057
1058 secdebug("KCLogin", "StorageManager::login: loginDLDbIdentifier is %s", (loginDLDbIdentifier) ? loginDLDbIdentifier.dbName() : "<NULL>");
1059 if (!loginDLDbIdentifier)
1060 MacOSError::throwMe(errSecNoSuchKeychain);
1061
1062
1063 //***************************************************************
1064 // gather keychain information
1065 //***************************************************************
1066
1067 // user name
1068 int uid = geteuid();
1069 struct passwd *pw = getpwuid(uid);
1070 if (pw == NULL) {
1071 secdebug("KCLogin", "StorageManager::login: invalid argument (NULL uid)");
1072 MacOSError::throwMe(paramErr);
1073 }
1074 char *userName = pw->pw_name;
1075
1076 // make keychain path strings
1077 std::string keychainPath = DLDbListCFPref::ExpandTildesInPath(kLoginKeychainPathPrefix);
1078 std::string shortnameKeychain = keychainPath + userName;
1079 std::string shortnameDotKeychain = shortnameKeychain + ".keychain";
1080 std::string loginDotKeychain = keychainPath + "login.keychain";
1081 std::string loginRenamed1Keychain = keychainPath + "login_renamed1.keychain";
1082
1083 // check for existence of keychain files
1084 bool shortnameKeychainExists = false;
1085 bool shortnameDotKeychainExists = false;
1086 bool loginKeychainExists = false;
1087 bool loginRenamed1KeychainExists = false;
1088 {
1089 struct stat st;
1090 int stat_result;
1091 stat_result = ::stat(shortnameKeychain.c_str(), &st);
1092 shortnameKeychainExists = (stat_result == 0);
1093 stat_result = ::stat(shortnameDotKeychain.c_str(), &st);
1094 shortnameDotKeychainExists = (stat_result == 0);
1095 stat_result = ::stat(loginDotKeychain.c_str(), &st);
1096 loginKeychainExists = (stat_result == 0);
1097 stat_result = ::stat(loginRenamed1Keychain.c_str(), &st);
1098 loginRenamed1KeychainExists = (stat_result == 0);
1099 }
1100
1101 bool loginUnlocked = false;
1102
1103 // make the keychain identifiers
1104 CSSM_VERSION version = {0, 0};
1105 DLDbIdentifier shortnameDLDbIdentifier = DLDbListCFPref::makeDLDbIdentifier(gGuidAppleCSPDL, version, 0, CSSM_SERVICE_CSP | CSSM_SERVICE_DL, shortnameKeychain.c_str(), NULL);
1106 DLDbIdentifier shortnameDotDLDbIdentifier = DLDbListCFPref::makeDLDbIdentifier(gGuidAppleCSPDL, version, 0, CSSM_SERVICE_CSP | CSSM_SERVICE_DL, shortnameDotKeychain.c_str(), NULL);
1107 DLDbIdentifier loginRenamed1DLDbIdentifier = DLDbListCFPref::makeDLDbIdentifier(gGuidAppleCSPDL, version, 0, CSSM_SERVICE_CSP | CSSM_SERVICE_DL, loginRenamed1Keychain.c_str(), NULL);
1108
1109 //***************************************************************
1110 // make file renaming changes first
1111 //***************************************************************
1112
1113 // if "~/Library/Keychains/shortname" exists, we need to migrate it forward;
1114 // either to login.keychain if there isn't already one, otherwise to shortname.keychain
1115 if (shortnameKeychainExists) {
1116 int rename_stat = 0;
1117 if (loginKeychainExists) {
1118 struct stat st;
1119 int tmp_result = ::stat(loginDotKeychain.c_str(), &st);
1120 if (tmp_result == 0) {
1121 if (st.st_size <= kEmptyKeychainSizeInBytes) {
1122 tmp_result = ::unlink(loginDotKeychain.c_str());
1123 rename_stat = ::rename(shortnameKeychain.c_str(), loginDotKeychain.c_str());
1124 shortnameKeychainExists = (rename_stat != 0);
1125 }
1126 }
1127 }
1128 if (shortnameKeychainExists) {
1129 if (loginKeychainExists && !shortnameDotKeychainExists) {
1130 rename_stat = ::rename(shortnameKeychain.c_str(), shortnameDotKeychain.c_str());
1131 shortnameDotKeychainExists = (rename_stat == 0);
1132 } else if (!loginKeychainExists) {
1133 rename_stat = ::rename(shortnameKeychain.c_str(), loginDotKeychain.c_str());
1134 loginKeychainExists = (rename_stat == 0);
1135 } else {
1136 // we have all 3 keychains: login.keychain, shortname, and shortname.keychain.
1137 // on Leopard we never want a shortname keychain, so we must move it aside.
1138 char pathbuf[MAXPATHLEN];
1139 std::string shortnameRenamedXXXKeychain = keychainPath;
1140 shortnameRenamedXXXKeychain += userName;
1141 shortnameRenamedXXXKeychain += "_renamed_XXX.keychain";
1142 ::strlcpy(pathbuf, shortnameRenamedXXXKeychain.c_str(), sizeof(pathbuf));
1143 ::mkstemps(pathbuf, 9); // 9 == strlen(".keychain")
1144 rename_stat = ::rename(shortnameKeychain.c_str(), pathbuf);
1145 shortnameKeychainExists = (rename_stat != 0);
1146 }
1147 }
1148 if (rename_stat != 0) {
1149 MacOSError::throwMe(errno);
1150 }
1151 }
1152
1153 //***************************************************************
1154 // handle special case where user previously reset the keychain
1155 //***************************************************************
1156 // Since 9A581, we have changed the definition of kKeychainRenamedSuffix from "_renamed" to "_renamed_".
1157 // Therefore, if "login_renamed1.keychain" exists and there is no plist, the user may have run into a
1158 // prior upgrade issue and clicked Reset. If we can successfully unlock login_renamed1.keychain with the
1159 // supplied password, then we will attempt to rename it to login.keychain if that file is empty, or with
1160 // "shortname.keychain" if it is not.
1161
1162 if (loginRenamed1KeychainExists && (!loginKeychainExists ||
1163 (mSavedList.searchList().size() == 1 && mSavedList.member(loginDLDbIdentifier)) )) {
1164 try
1165 {
1166 Keychain loginRenamed1KC(keychain(loginRenamed1DLDbIdentifier));
1167 secdebug("KCLogin", "Attempting to unlock %s with %d-character password",
1168 (loginRenamed1KC) ? loginRenamed1KC->name() : "<NULL>", (unsigned int)passwordLength);
1169 loginRenamed1KC->unlock(CssmData(const_cast<void *>(password), passwordLength));
1170 // if we get here, we unlocked it
1171 if (loginKeychainExists) {
1172 struct stat st;
1173 int tmp_result = ::stat(loginDotKeychain.c_str(), &st);
1174 if (tmp_result == 0) {
1175 if (st.st_size <= kEmptyKeychainSizeInBytes) {
1176 tmp_result = ::unlink(loginDotKeychain.c_str());
1177 tmp_result = ::rename(loginRenamed1Keychain.c_str(), loginDotKeychain.c_str());
1178 } else if (!shortnameDotKeychainExists) {
1179 tmp_result = ::rename(loginRenamed1Keychain.c_str(), shortnameDotKeychain.c_str());
1180 shortnameDotKeychainExists = (tmp_result == 0);
1181 } else {
1182 throw 1; // can't do anything with it except move it out of the way
1183 }
1184 }
1185 } else {
1186 int tmp_result = ::rename(loginRenamed1Keychain.c_str(), loginDotKeychain.c_str());
1187 loginKeychainExists = (tmp_result == 0);
1188 }
1189 }
1190 catch(...)
1191 {
1192 // we failed to unlock the login_renamed1.keychain file with the login password.
1193 // move it aside so we don't try to deal with it again.
1194 char pathbuf[MAXPATHLEN];
1195 std::string loginRenamedXXXKeychain = keychainPath;
1196 loginRenamedXXXKeychain += "login_renamed_XXX.keychain";
1197 ::strlcpy(pathbuf, loginRenamedXXXKeychain.c_str(), sizeof(pathbuf));
1198 ::mkstemps(pathbuf, 9); // 9 == strlen(".keychain")
1199 ::rename(loginRenamed1Keychain.c_str(), pathbuf);
1200 }
1201 }
1202
1203 // if login.keychain does not exist at this point, create it
1204 if (!loginKeychainExists) {
1205 Keychain theKeychain(keychain(loginDLDbIdentifier));
1206 secdebug("KCLogin", "Creating login keychain %s", (loginDLDbIdentifier) ? loginDLDbIdentifier.dbName() : "<NULL>");
1207 theKeychain->create(passwordLength, password);
1208 secdebug("KCLogin", "Login keychain created successfully");
1209 loginKeychainExists = true;
1210 // Set the prefs for this new login keychain.
1211 loginKeychain(theKeychain);
1212 // Login Keychain does not lock on sleep nor lock after timeout by default.
1213 theKeychain->setSettings(INT_MAX, false);
1214 loginUnlocked = true;
1215 mSavedList.revert(true);
1216 }
1217
1218 //***************************************************************
1219 // make plist changes after files have been renamed or created
1220 //***************************************************************
1221
1222 // if the shortname keychain exists in the search list, either rename or remove the entry
1223 if (mSavedList.member(shortnameDLDbIdentifier)) {
1224 if (shortnameDotKeychainExists && !mSavedList.member(shortnameDotDLDbIdentifier)) {
1225 // change shortname to shortname.keychain (login.keychain will be added later if not present)
1226 secdebug("KCLogin", "Renaming %s to %s in keychain search list",
1227 (shortnameDLDbIdentifier) ? shortnameDLDbIdentifier.dbName() : "<NULL>",
1228 (shortnameDotDLDbIdentifier) ? shortnameDotDLDbIdentifier.dbName() : "<NULL>");
1229 mSavedList.rename(shortnameDLDbIdentifier, shortnameDotDLDbIdentifier);
1230 } else if (!mSavedList.member(loginDLDbIdentifier)) {
1231 // change shortname to login.keychain
1232 secdebug("KCLogin", "Renaming %s to %s in keychain search list",
1233 (shortnameDLDbIdentifier) ? shortnameDLDbIdentifier.dbName() : "<NULL>",
1234 (loginDLDbIdentifier) ? loginDLDbIdentifier.dbName() : "<NULL>");
1235 mSavedList.rename(shortnameDLDbIdentifier, loginDLDbIdentifier);
1236 } else {
1237 // already have login.keychain in list, and renaming to shortname.keychain isn't an option,
1238 // so just remove the entry
1239 secdebug("KCLogin", "Removing %s from keychain search list", (shortnameDLDbIdentifier) ? shortnameDLDbIdentifier.dbName() : "<NULL>");
1240 mSavedList.remove(shortnameDLDbIdentifier);
1241 }
1242
1243 // note: save() will cause the plist to be unlinked if the only remaining entry is for login.keychain
1244 mSavedList.save();
1245 mSavedList.revert(true);
1246 }
1247
1248 // make sure that login.keychain is in the search list
1249 if (!mSavedList.member(loginDLDbIdentifier)) {
1250 secdebug("KCLogin", "Adding %s to keychain search list", (loginDLDbIdentifier) ? loginDLDbIdentifier.dbName() : "<NULL>");
1251 mSavedList.add(loginDLDbIdentifier);
1252 mSavedList.save();
1253 mSavedList.revert(true);
1254 }
1255
1256 // if we have a shortname.keychain, always include it in the plist (after login.keychain)
1257 if (shortnameDotKeychainExists && !mSavedList.member(shortnameDotDLDbIdentifier)) {
1258 mSavedList.add(shortnameDotDLDbIdentifier);
1259 mSavedList.save();
1260 mSavedList.revert(true);
1261 }
1262
1263 // make sure that the default keychain is in the search list; if not, reset the default to login.keychain
1264 if (!mSavedList.member(mSavedList.defaultDLDbIdentifier())) {
1265 secdebug("KCLogin", "Changing default keychain to %s", (loginDLDbIdentifier) ? loginDLDbIdentifier.dbName() : "<NULL>");
1266 mSavedList.defaultDLDbIdentifier(loginDLDbIdentifier);
1267 mSavedList.save();
1268 mSavedList.revert(true);
1269 }
1270
1271 //***************************************************************
1272 // auto-unlock the login keychain(s)
1273 //***************************************************************
1274 // all our preflight fixups are finally done, so we can now attempt to unlock the login keychain
1275
1276 OSStatus loginResult = noErr;
1277 if (!loginUnlocked) {
1278 try
1279 {
1280 Keychain theKeychain(keychain(loginDLDbIdentifier));
1281 secdebug("KCLogin", "Attempting to unlock login keychain \"%s\" with %d-character password",
1282 (theKeychain) ? theKeychain->name() : "<NULL>", (unsigned int)passwordLength);
1283 theKeychain->unlock(CssmData(const_cast<void *>(password), passwordLength));
1284 loginUnlocked = true;
1285 }
1286 catch(const CssmError &e)
1287 {
1288 loginResult = e.osStatus(); // save this result
1289 }
1290 }
1291
1292 // if "shortname.keychain" exists and is in the search list, attempt to auto-unlock it with the same password
1293 if (shortnameDotKeychainExists && mSavedList.member(shortnameDotDLDbIdentifier)) {
1294 try
1295 {
1296 Keychain shortnameDotKC(keychain(shortnameDotDLDbIdentifier));
1297 secdebug("KCLogin", "Attempting to unlock %s",
1298 (shortnameDotKC) ? shortnameDotKC->name() : "<NULL>");
1299 shortnameDotKC->unlock(CssmData(const_cast<void *>(password), passwordLength));
1300 }
1301 catch(const CssmError &e)
1302 {
1303 // ignore; failure to unlock this keychain is not considered an error
1304 }
1305 }
1306
1307 if (loginResult != noErr) {
1308 MacOSError::throwMe(loginResult);
1309 }
1310 }
1311
1312 void StorageManager::logout()
1313 {
1314 // nothing left to do here
1315 }
1316
1317 void StorageManager::changeLoginPassword(ConstStringPtr oldPassword, ConstStringPtr newPassword)
1318 {
1319 StLock<Mutex>_(mMutex);
1320
1321 loginKeychain()->changePassphrase(oldPassword, newPassword);
1322 secdebug("KClogin", "Changed login keychain password successfully");
1323 }
1324
1325
1326 void StorageManager::changeLoginPassword(UInt32 oldPasswordLength, const void *oldPassword, UInt32 newPasswordLength, const void *newPassword)
1327 {
1328 StLock<Mutex>_(mMutex);
1329
1330 loginKeychain()->changePassphrase(oldPasswordLength, oldPassword, newPasswordLength, newPassword);
1331 secdebug("KClogin", "Changed login keychain password successfully");
1332 }
1333
1334 // Clear out the keychain search list and rename the existing login.keychain.
1335 //
1336 void StorageManager::resetKeychain(Boolean resetSearchList)
1337 {
1338 StLock<Mutex>_(mMutex);
1339
1340 // Clear the keychain search list.
1341 try
1342 {
1343 if ( resetSearchList )
1344 {
1345 StorageManager::KeychainList keychainList;
1346 setSearchList(keychainList);
1347 }
1348 // Get a reference to the existing login keychain...
1349 // If we don't have one, we throw (not requiring a rename).
1350 //
1351 Keychain keychain = loginKeychain();
1352 //
1353 // Rename the existing login.keychain (i.e. put it aside).
1354 //
1355 CFMutableStringRef newName = NULL;
1356 newName = CFStringCreateMutable(NULL, 0);
1357 CFStringRef currName = NULL;
1358 currName = CFStringCreateWithCString(NULL, keychain->name(), kCFStringEncodingUTF8);
1359 if ( newName && currName )
1360 {
1361 CFStringAppend(newName, currName);
1362 CFStringRef kcSuffix = CFSTR(kKeychainSuffix);
1363 if ( CFStringHasSuffix(newName, kcSuffix) ) // remove the .keychain extension
1364 {
1365 CFRange suffixRange = CFStringFind(newName, kcSuffix, 0);
1366 CFStringFindAndReplace(newName, kcSuffix, CFSTR(""), suffixRange, 0);
1367 }
1368 CFStringAppend(newName, CFSTR(kKeychainRenamedSuffix)); // add "_renamed_"
1369 try
1370 {
1371 renameUnique(keychain, newName);
1372 }
1373 catch(...)
1374 {
1375 // we need to release 'newName' & 'currName'
1376 }
1377 } // else, let the login call report a duplicate
1378 if ( newName )
1379 CFRelease(newName);
1380 if ( currName )
1381 CFRelease(currName);
1382 }
1383 catch(...)
1384 {
1385 // We either don't have a login keychain, or there was a
1386 // failure to rename the existing one.
1387 }
1388 }
1389
1390 #pragma mark ____ File Related ____
1391
1392 Keychain StorageManager::make(const char *pathName)
1393 {
1394 return make(pathName, true);
1395 }
1396
1397 Keychain StorageManager::make(const char *pathName, bool add)
1398 {
1399 StLock<Mutex>_(mMutex);
1400
1401 string fullPathName;
1402 if ( pathName[0] == '/' )
1403 fullPathName = pathName;
1404 else
1405 {
1406 // Get Home directory from environment.
1407 switch (mDomain)
1408 {
1409 case kSecPreferencesDomainUser:
1410 {
1411 const char *homeDir = getenv("HOME");
1412 if (homeDir == NULL)
1413 {
1414 // If $HOME is unset get the current user's home directory
1415 // from the passwd file.
1416 uid_t uid = geteuid();
1417 if (!uid) uid = getuid();
1418 struct passwd *pw = getpwuid(uid);
1419 if (!pw)
1420 MacOSError::throwMe(paramErr);
1421 homeDir = pw->pw_dir;
1422 }
1423 fullPathName = homeDir;
1424 }
1425 break;
1426 case kSecPreferencesDomainSystem:
1427 fullPathName = "";
1428 break;
1429 default:
1430 assert(false); // invalid domain for this
1431 }
1432
1433 fullPathName += "/Library/Keychains/";
1434 fullPathName += pathName;
1435 }
1436
1437 const CSSM_NET_ADDRESS *DbLocation = NULL; // NULL for keychains
1438 const CSSM_VERSION *version = NULL;
1439 uint32 subserviceId = 0;
1440 CSSM_SERVICE_TYPE subserviceType = CSSM_SERVICE_DL | CSSM_SERVICE_CSP;
1441 const CssmSubserviceUid ssuid(gGuidAppleCSPDL, version,
1442 subserviceId, subserviceType);
1443 DLDbIdentifier dLDbIdentifier(ssuid, fullPathName.c_str(), DbLocation);
1444 return makeKeychain(dLDbIdentifier, add);
1445 }
1446
1447 Keychain StorageManager::makeLoginAuthUI(const Item *item)
1448 {
1449 StLock<Mutex>_(mMutex);
1450
1451 // Create a login/default keychain for the user using UI.
1452 // The user can cancel out of the operation, or create a new login keychain.
1453 // If auto-login is turned off, the user will be asked for their login password.
1454 //
1455 OSStatus result = noErr;
1456 Keychain keychain; // We return this keychain.
1457 //
1458 // Set up the Auth ref to bring up UI.
1459 //
1460 AuthorizationItem *currItem, *authEnvirItemArrayPtr = NULL;
1461 AuthorizationRef authRef = NULL;
1462 try
1463 {
1464 result = AuthorizationCreate(NULL, NULL, kAuthorizationFlagDefaults, &authRef);
1465 if ( result )
1466 MacOSError::throwMe(result);
1467
1468 AuthorizationEnvironment envir;
1469 envir.count = 6; // up to 6 hints can be used.
1470 authEnvirItemArrayPtr = (AuthorizationItem*)malloc(sizeof(AuthorizationItem) * envir.count);
1471 if ( !authEnvirItemArrayPtr )
1472 MacOSError::throwMe(errAuthorizationInternal);
1473
1474 currItem = envir.items = authEnvirItemArrayPtr;
1475
1476 //
1477 // 1st Hint (optional): The keychain item's account attribute string.
1478 // When item is specified, we assume an 'add' operation is being attempted.
1479 char buff[255];
1480 UInt32 actLen = 0;
1481 SecKeychainAttribute attr = { kSecAccountItemAttr, 255, &buff };
1482 if ( item )
1483 {
1484 try
1485 {
1486 (*item)->getAttribute(attr, &actLen);
1487 }
1488 catch(...)
1489 {
1490 actLen = 0; // This item didn't have the account attribute, so don't display one in the UI.
1491 }
1492 }
1493 currItem->name = AGENT_HINT_ATTR_NAME; // name str that identifies this hint as attr name
1494 if ( actLen ) // Fill in the hint if we have an account attr
1495 {
1496 if ( actLen > 255 )
1497 buff[255] = 0;
1498 else
1499 buff[actLen] = 0;
1500 currItem->valueLength = strlen(buff)+1;
1501 currItem->value = buff;
1502 }
1503 else
1504 {
1505 currItem->valueLength = 0;
1506 currItem->value = NULL;
1507 }
1508 currItem->flags = 0;
1509
1510 //
1511 // 2nd Hint (optional): The item's keychain full path.
1512 //
1513 currItem++;
1514 char* currDefaultName = NULL;
1515 try
1516 {
1517 currDefaultName = (char*)defaultKeychain()->name(); // Use the name if we have it.
1518 currItem->name = AGENT_HINT_LOGIN_KC_NAME; // Name str that identifies this hint as kc path
1519 currItem->valueLength = (currDefaultName) ? strlen(currDefaultName) : 0;
1520 currItem->value = (currDefaultName) ? (void*)currDefaultName : (void*)"";
1521 currItem->flags = 0;
1522 currItem++;
1523 }
1524 catch(...)
1525 {
1526 envir.count--;
1527 }
1528
1529 //
1530 // 3rd Hint (required): check if curr default keychain is unavailable.
1531 // This is determined by the parent not existing.
1532 //
1533 currItem->name = AGENT_HINT_LOGIN_KC_EXISTS_IN_KC_FOLDER;
1534 Boolean loginUnavail = false;
1535 try
1536 {
1537 Keychain defaultKC = defaultKeychain();
1538 if ( !defaultKC->exists() )
1539 loginUnavail = true;
1540 }
1541 catch(...) // login.keychain not present
1542 {
1543 }
1544 currItem->valueLength = sizeof(Boolean);
1545 currItem->value = (void*)&loginUnavail;
1546 currItem->flags = 0;
1547
1548 //
1549 // 4th Hint (required): userName
1550 //
1551 currItem++;
1552 currItem->name = AGENT_HINT_LOGIN_KC_USER_NAME;
1553 char* uName = getenv("USER");
1554 string userName = uName ? uName : "";
1555 if ( userName.length() == 0 )
1556 {
1557 uid_t uid = geteuid();
1558 if (!uid) uid = getuid();
1559 struct passwd *pw = getpwuid(uid); // fallback case...
1560 if (pw)
1561 userName = pw->pw_name;
1562 endpwent();
1563 }
1564 if ( userName.length() == 0 ) // did we ultimately get one?
1565 MacOSError::throwMe(errAuthorizationInternal);
1566
1567 currItem->value = (void*)userName.c_str();
1568 currItem->valueLength = userName.length();
1569 currItem->flags = 0;
1570
1571 //
1572 // 5th Hint (required): flags if user has more than 1 keychain (used for a later warning when reset to default).
1573 //
1574 currItem++;
1575 currItem->name = AGENT_HINT_LOGIN_KC_USER_HAS_OTHER_KCS_STR;
1576 Boolean moreThanOneKCExists = false;
1577 {
1578 // if item is NULL, then this is a user-initiated full reset
1579 if (item && mSavedList.searchList().size() > 1)
1580 moreThanOneKCExists = true;
1581 }
1582 currItem->value = &moreThanOneKCExists;
1583 currItem->valueLength = sizeof(Boolean);
1584 currItem->flags = 0;
1585
1586 //
1587 // 6th Hint (required): If no item is involved, this is a user-initiated full reset.
1588 // We want to suppress the "do you want to reset to defaults?" panel in this case.
1589 //
1590 currItem++;
1591 currItem->name = AGENT_HINT_LOGIN_KC_SUPPRESS_RESET_PANEL;
1592 Boolean suppressResetPanel = (item == NULL) ? TRUE : FALSE;
1593 currItem->valueLength = sizeof(Boolean);
1594 currItem->value = (void*)&suppressResetPanel;
1595 currItem->flags = 0;
1596
1597 //
1598 // Set up the auth rights and make the auth call.
1599 //
1600 AuthorizationItem authItem = { LOGIN_KC_CREATION_RIGHT, 0 , NULL, 0 };
1601 AuthorizationRights rights = { 1, &authItem };
1602 AuthorizationFlags flags = kAuthorizationFlagDefaults | kAuthorizationFlagInteractionAllowed | kAuthorizationFlagExtendRights;
1603 result = AuthorizationCopyRights(authRef, &rights, &envir, flags, NULL);
1604 if ( result )
1605 MacOSError::throwMe(result);
1606 try
1607 {
1608 resetKeychain(true); // Clears the plist, moves aside existing login.keychain
1609 }
1610 catch (...) // can throw if no existing login.keychain is found
1611 {
1612 }
1613 login(authRef, userName.length(), userName.c_str()); // Create login.keychain
1614 keychain = loginKeychain(); // Get newly-created login keychain
1615 defaultKeychain(keychain); // Set it to be the default
1616
1617 free(authEnvirItemArrayPtr);
1618 AuthorizationFree(authRef, kAuthorizationFlagDefaults);
1619 }
1620
1621 catch (...)
1622 {
1623 // clean up allocations, then rethrow error
1624 if ( authEnvirItemArrayPtr )
1625 free(authEnvirItemArrayPtr);
1626 if ( authRef )
1627 AuthorizationFree(authRef, kAuthorizationFlagDefaults);
1628 throw;
1629 }
1630
1631 return keychain;
1632 }
1633
1634 Keychain StorageManager::defaultKeychainUI(Item &item)
1635 {
1636 StLock<Mutex>_(mMutex);
1637
1638 Keychain returnedKeychain;
1639 try
1640 {
1641 returnedKeychain = defaultKeychain(); // If we have one, return it.
1642 if ( returnedKeychain->exists() )
1643 return returnedKeychain;
1644 }
1645 catch(...) // We could have one, but it isn't available (i.e. on a un-mounted volume).
1646 {
1647 }
1648 if ( globals().getUserInteractionAllowed() )
1649 {
1650 returnedKeychain = makeLoginAuthUI(&item); // If no Keychains is present, one will be created.
1651 if ( !returnedKeychain )
1652 MacOSError::throwMe(errSecInvalidKeychain); // Something went wrong...
1653 }
1654 else
1655 MacOSError::throwMe(errSecInteractionNotAllowed); // If UI isn't allowed, return an error.
1656
1657 return returnedKeychain;
1658 }
1659
1660 void
1661 StorageManager::addToDomainList(SecPreferencesDomain domain,
1662 const char* dbName, const CSSM_GUID &guid, uint32 subServiceType)
1663 {
1664 StLock<Mutex>_(mMutex);
1665
1666 if (domain == kSecPreferencesDomainDynamic)
1667 MacOSError::throwMe(errSecInvalidPrefsDomain);
1668
1669 // make the identifier
1670 CSSM_VERSION version = {0, 0};
1671 DLDbIdentifier id = DLDbListCFPref::makeDLDbIdentifier (guid, version, 0,
1672 subServiceType, dbName, NULL);
1673
1674 if (domain == mDomain)
1675 {
1676 // manipulate the user's list
1677 {
1678 mSavedList.revert(true);
1679 mSavedList.add(id);
1680 mSavedList.save();
1681 }
1682
1683 KCEventNotifier::PostKeychainEvent(kSecKeychainListChangedEvent);
1684 }
1685 else
1686 {
1687 // manipulate the other list
1688 DLDbListCFPref(domain).add(id);
1689 }
1690 }
1691
1692 void
1693 StorageManager::isInDomainList(SecPreferencesDomain domain,
1694 const char* dbName, const CSSM_GUID &guid, uint32 subServiceType)
1695 {
1696 StLock<Mutex>_(mMutex);
1697
1698 if (domain == kSecPreferencesDomainDynamic)
1699 MacOSError::throwMe(errSecInvalidPrefsDomain);
1700
1701 CSSM_VERSION version = {0, 0};
1702 DLDbIdentifier id = DLDbListCFPref::makeDLDbIdentifier (guid, version, 0,
1703 subServiceType, dbName, NULL);
1704
1705 // determine the list to search
1706 bool result;
1707 if (domain == mDomain)
1708 {
1709 result = mSavedList.member(id);
1710 }
1711 else
1712 {
1713 result = DLDbListCFPref(domain).member(id);
1714 }
1715
1716 // do the search
1717 if (!result)
1718 {
1719 MacOSError::throwMe(errSecNoSuchKeychain);
1720 }
1721 }
1722
1723 void
1724 StorageManager::removeFromDomainList(SecPreferencesDomain domain,
1725 const char* dbName, const CSSM_GUID &guid, uint32 subServiceType)
1726 {
1727 StLock<Mutex>_(mMutex);
1728
1729 if (domain == kSecPreferencesDomainDynamic)
1730 MacOSError::throwMe(errSecInvalidPrefsDomain);
1731
1732 // make the identifier
1733 CSSM_VERSION version = {0, 0};
1734 DLDbIdentifier id = DLDbListCFPref::makeDLDbIdentifier (guid, version, 0,
1735 subServiceType, dbName, NULL);
1736
1737 if (domain == mDomain)
1738 {
1739 // manipulate the user's list
1740 {
1741 mSavedList.revert(true);
1742 mSavedList.remove(id);
1743 mSavedList.save();
1744 }
1745
1746 KCEventNotifier::PostKeychainEvent(kSecKeychainListChangedEvent);
1747 }
1748 else
1749 {
1750 // manipulate the other list
1751 DLDbListCFPref(domain).remove(id);
1752 }
1753 }
1754
1755 bool
1756 StorageManager::keychainOwnerPermissionsValidForDomain(const char* path, SecPreferencesDomain domain)
1757 {
1758 struct stat sb;
1759 mode_t perms;
1760 const char* sysPrefDir = "/Library/Preferences";
1761 const char* errMsg = "Will not set default";
1762 char* mustOwnDir = NULL;
1763 struct passwd* pw = NULL;
1764
1765 // get my uid
1766 uid_t uid = geteuid();
1767 if (!uid) uid = getuid();
1768
1769 // our (e)uid must own the appropriate preferences or home directory
1770 // for the specified preference domain whose default we will be modifying
1771 switch (domain) {
1772 case kSecPreferencesDomainUser:
1773 mustOwnDir = getenv("HOME");
1774 if (mustOwnDir == NULL) {
1775 pw = getpwuid(uid);
1776 if (!pw) return false;
1777 mustOwnDir = pw->pw_dir;
1778 }
1779 break;
1780 case kSecPreferencesDomainSystem:
1781 mustOwnDir = (char*)sysPrefDir;
1782 break;
1783 case kSecPreferencesDomainCommon:
1784 mustOwnDir = (char*)sysPrefDir;
1785 break;
1786 default:
1787 return false;
1788 }
1789
1790 if (mustOwnDir != NULL) {
1791 struct stat dsb;
1792 if ( (stat(mustOwnDir, &dsb) != 0) || (dsb.st_uid != uid) ) {
1793 fprintf(stderr, "%s: UID=%d does not own directory %s\n", errMsg, (int)uid, mustOwnDir);
1794 mustOwnDir = NULL; // will return below after calling endpwent()
1795 }
1796 }
1797
1798 if (pw != NULL)
1799 endpwent();
1800
1801 if (mustOwnDir == NULL)
1802 return false;
1803
1804 // check that file actually exists
1805 if (stat(path, &sb) != 0) {
1806 fprintf(stderr, "%s: file %s does not exist\n", errMsg, path);
1807 return false;
1808 }
1809
1810 // check flags
1811 if (sb.st_flags & (SF_IMMUTABLE | UF_IMMUTABLE)) {
1812 fprintf(stderr, "%s: file %s is immutable\n", errMsg, path);
1813 return false;
1814 }
1815
1816 // check ownership
1817 if (sb.st_uid != uid) {
1818 fprintf(stderr, "%s: file %s is owned by UID=%d, but we have UID=%d\n",
1819 errMsg, path, (int)sb.st_uid, (int)uid);
1820 return false;
1821 }
1822
1823 // check mode
1824 perms = sb.st_mode;
1825 perms |= 0600; // must have owner read/write permission set
1826 if (sb.st_mode != perms) {
1827 fprintf(stderr, "%s: file %s does not have the expected permissions\n", errMsg, path);
1828 return false;
1829 }
1830
1831 // user owns file and can read/write it
1832 return true;
1833 }