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