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