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