]> git.saurik.com Git - apple/security.git/blobdiff - Security/libsecurity_keychain/lib/DLDBListCFPref.cpp
Security-57031.1.35.tar.gz
[apple/security.git] / Security / libsecurity_keychain / lib / DLDBListCFPref.cpp
diff --git a/Security/libsecurity_keychain/lib/DLDBListCFPref.cpp b/Security/libsecurity_keychain/lib/DLDBListCFPref.cpp
new file mode 100644 (file)
index 0000000..4836f4d
--- /dev/null
@@ -0,0 +1,1079 @@
+/*
+ * Copyright (c) 2000-2004,2011-2014 Apple Inc. All Rights Reserved.
+ * 
+ * @APPLE_LICENSE_HEADER_START@
+ * 
+ * This file contains Original Code and/or Modifications of Original Code
+ * as defined in and that are subject to the Apple Public Source License
+ * Version 2.0 (the 'License'). You may not use this file except in
+ * compliance with the License. Please obtain a copy of the License at
+ * http://www.opensource.apple.com/apsl/ and read it before using this
+ * file.
+ * 
+ * The Original Code and all software distributed under the License are
+ * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
+ * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
+ * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
+ * Please see the License for the specific language governing rights and
+ * limitations under the License.
+ * 
+ * @APPLE_LICENSE_HEADER_END@
+ */
+
+
+/*
+    DLDBListCFPref.cpp
+*/
+
+#include "DLDBListCFPref.h"
+#include <Security/cssmapple.h>
+#include <security_utilities/debugging.h>
+#include <security_utilities/utilities.h>
+#include <memory>
+#include <fcntl.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <pwd.h>
+#include <sys/param.h>
+#include <copyfile.h>
+#include <xpc/private.h>
+#include <syslog.h>
+#include <sandbox.h>
+
+dispatch_once_t AppSandboxChecked;
+xpc_object_t KeychainHomeFromXPC;
+
+using namespace CssmClient;
+
+static const double kDLDbListCFPrefRevertInterval = 30.0;
+
+// normal debug calls, which get stubbed out for deployment builds
+
+#define kKeyGUID CFSTR("GUID")
+#define kKeySubserviceId CFSTR("SubserviceId")
+#define kKeySubserviceType CFSTR("SubserviceType")
+#define kKeyDbName CFSTR("DbName")
+#define kKeyDbLocation CFSTR("DbLocation")
+#define kKeyActive CFSTR("Active")
+#define kKeyMajorVersion CFSTR("MajorVersion")
+#define kKeyMinorVersion CFSTR("MinorVersion")
+#define kDefaultDLDbListKey CFSTR("DLDBSearchList")
+#define kDefaultKeychainKey CFSTR("DefaultKeychain")
+#define kLoginKeychainKey CFSTR("LoginKeychain")
+#define kUserDefaultPath "~/Library/Preferences/com.apple.security.plist"
+#define kSystemDefaultPath "/Library/Preferences/com.apple.security.plist"
+#define kCommonDefaultPath "/Library/Preferences/com.apple.security-common.plist"
+#define kLoginKeychainPathPrefix "~/Library/Keychains/"
+#define kUserLoginKeychainPath "~/Library/Keychains/login.keychain"
+#define kSystemLoginKeychainPath "/Library/Keychains/System.keychain"
+
+
+// A utility class for managing password database lookups
+
+const time_t kPasswordCacheExpire = 30; // number of seconds cached password db info is valid
+
+PasswordDBLookup::PasswordDBLookup () : mValid (false), mCurrent (0), mTime (0)
+{
+}
+
+void PasswordDBLookup::lookupInfoOnUID (uid_t uid)
+{
+    time_t currentTime = time (NULL);
+    
+    if (!mValid || uid != mCurrent || currentTime - mTime >= kPasswordCacheExpire)
+    {
+        struct passwd* pw = getpwuid(uid);
+               if (pw == NULL)
+               {
+                       UnixError::throwMe (EPERM);
+               }
+               
+        mDirectory = pw->pw_dir;
+        mName = pw->pw_name;
+        mValid = true;
+        mCurrent = uid;
+        mTime = currentTime;
+
+        secdebug("secpref", "uid=%d caching home=%s", uid, pw->pw_dir);
+
+        endpwent();
+    }
+}
+
+PasswordDBLookup *DLDbListCFPref::mPdbLookup = NULL;
+
+//-------------------------------------------------------------------------------------
+//
+//                     Lists of DL/DBs, with CFPreferences backing store
+//
+//-------------------------------------------------------------------------------------
+
+DLDbListCFPref::DLDbListCFPref(SecPreferencesDomain domain) : mDomain(domain), mPropertyList(NULL), mChanged(false),
+    mSearchListSet(false), mDefaultDLDbIdentifierSet(false), mLoginDLDbIdentifierSet(false)
+{
+    secdebug("secpref", "New DLDbListCFPref %p for domain %d", this, domain);
+       loadPropertyList(true);
+}
+
+void DLDbListCFPref::set(SecPreferencesDomain domain)
+{
+       save();
+
+       mDomain = domain;
+
+    secdebug("secpref", "DLDbListCFPref %p domain set to %d", this, domain);
+
+       if (loadPropertyList(true))
+        resetCachedValues();
+}
+
+DLDbListCFPref::~DLDbListCFPref()
+{
+    save();
+
+       if (mPropertyList)
+               CFRelease(mPropertyList);
+}
+
+void
+DLDbListCFPref::forceUserSearchListReread()
+{
+       // set mPrefsTimeStamp so that it will "expire" the next time loadPropertyList is called
+       mPrefsTimeStamp = CFAbsoluteTimeGetCurrent() - kDLDbListCFPrefRevertInterval;
+}
+
+bool
+DLDbListCFPref::loadPropertyList(bool force)
+{
+    string prefsPath;
+       
+       switch (mDomain)
+    {
+       case kSecPreferencesDomainUser:
+               prefsPath = ExpandTildesInPath(kUserDefaultPath);
+               break;
+       case kSecPreferencesDomainSystem:
+               prefsPath = kSystemDefaultPath;
+               break;
+       case kSecPreferencesDomainCommon:
+               prefsPath = kCommonDefaultPath;
+               break;
+       default:
+               MacOSError::throwMe(errSecInvalidPrefsDomain);
+       }
+
+       secdebug("secpref", "force=%s prefsPath=%s", force ? "true" : "false",
+               prefsPath.c_str());
+
+       CFAbsoluteTime now = CFAbsoluteTimeGetCurrent();
+
+    // If for some reason the prefs file path has changed, blow away the old plist and force an update
+    if (mPrefsPath != prefsPath)
+    {
+        mPrefsPath = prefsPath;
+        if (mPropertyList)
+        {
+            CFRelease(mPropertyList);
+            mPropertyList = NULL;
+        }
+
+               mPrefsTimeStamp = now;
+    }
+       else if (!force)
+       {
+               if (now - mPrefsTimeStamp < kDLDbListCFPrefRevertInterval)
+                       return false;
+
+               mPrefsTimeStamp = now;
+       }
+
+       struct stat st;
+       if (stat(mPrefsPath.c_str(), &st))
+       {
+               if (errno == ENOENT)
+               {
+                       if (mPropertyList)
+                       {
+                               if (CFDictionaryGetCount(mPropertyList) == 0)
+                                       return false;
+                               CFRelease(mPropertyList);
+                       }
+
+                       mPropertyList = CFDictionaryCreateMutable(kCFAllocatorDefault, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
+                       return true;
+               }
+       }
+       else
+       {
+               if (mPropertyList)
+               {
+                       if (mTimespec.tv_sec == st.st_mtimespec.tv_sec
+                               && mTimespec.tv_nsec == st.st_mtimespec.tv_nsec)
+                               return false;
+               }
+
+               mTimespec = st.st_mtimespec;
+       }
+
+       CFMutableDictionaryRef thePropertyList = NULL;
+       CFMutableDataRef xmlData = NULL;
+       CFStringRef errorString = NULL;
+       int fd = -1;
+
+       do
+       {
+               fd = open(mPrefsPath.c_str(), O_RDONLY, 0);
+               if (fd < 0)
+                       break;
+
+               off_t theSize = lseek(fd, 0, SEEK_END);
+               if (theSize <= 0)
+                       break;
+
+               if (lseek(fd, 0, SEEK_SET))
+                       break;
+
+               xmlData = CFDataCreateMutable(NULL, CFIndex(theSize));
+               if (!xmlData)
+                       break;
+               CFDataSetLength(xmlData, CFIndex(theSize));
+               void *buffer = reinterpret_cast<void *>(CFDataGetMutableBytePtr(xmlData));
+               if (!buffer)
+                       break;
+               ssize_t bytesRead = read(fd, buffer, (size_t)theSize);
+               if (bytesRead != theSize)
+                       break;
+
+               thePropertyList = CFMutableDictionaryRef(CFPropertyListCreateFromXMLData(NULL, xmlData, kCFPropertyListMutableContainers, &errorString));
+               if (!thePropertyList)
+                       break;
+
+               if (CFGetTypeID(thePropertyList) != CFDictionaryGetTypeID())
+               {
+                       CFRelease(thePropertyList);
+                       thePropertyList = NULL;
+                       break;
+               }
+       } while (0);
+
+       if (fd >= 0)
+               close(fd);
+       if (xmlData)
+               CFRelease(xmlData);
+       if (errorString)
+               CFRelease(errorString);
+
+       if (!thePropertyList)
+       {
+               thePropertyList = CFDictionaryCreateMutable(kCFAllocatorDefault, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
+       }
+
+       if (mPropertyList)
+       {
+               if (CFEqual(mPropertyList, thePropertyList))
+               {
+            // The new property list is the same as the old one, so nothing has changed.
+                       CFRelease(thePropertyList);
+                       return false;
+               }
+               CFRelease(mPropertyList);
+       }
+
+       mPropertyList = thePropertyList;
+       return true;
+}
+
+void
+DLDbListCFPref::writePropertyList()
+{
+       if (!mPropertyList || CFDictionaryGetCount(mPropertyList) == 0)
+       {
+               // There is nothing in the mPropertyList dictionary,
+               // so we don't need a prefs file.
+               unlink(mPrefsPath.c_str());
+       }
+       else
+       {
+               if(testAndFixPropertyList()) 
+                       return;
+               
+               CFDataRef xmlData = CFPropertyListCreateXMLData(NULL, mPropertyList);
+               if (!xmlData)
+                       return; // Bad out of memory or something evil happened let's act like CF and do nothing.
+                       
+               // The prefs file should at least be made readable by user/group/other and writable by the owner.
+               // Change from euid to ruid if needed for the duration of the new prefs file creat.
+               
+               mode_t mode = 0666;
+               changeIdentity(UNPRIV);
+               int fd = open(mPrefsPath.c_str(), O_WRONLY|O_CREAT|O_TRUNC, mode);
+               changeIdentity(PRIV);
+               if (fd >= 0)
+               {
+                       const void *buffer = CFDataGetBytePtr(xmlData);
+                       size_t toWrite = CFDataGetLength(xmlData);
+                       /* ssize_t bytesWritten = */ write(fd, buffer, toWrite);
+                       // Emulate CFPreferences by not checking for any errors.
+       
+                       fsync(fd);
+                       struct stat st;
+                       if (!fstat(fd, &st))
+                               mTimespec = st.st_mtimespec;
+
+                       close(fd);
+               }
+
+               CFRelease(xmlData);
+       }
+
+       mPrefsTimeStamp = CFAbsoluteTimeGetCurrent();
+}
+
+// This function can clean up some problems caused by setuid clients.  We've had instances where the
+// Keychain search list has become owned by root, but is still able to be re-written by the user because
+// of the permissions on the directory above.  We'll take advantage of that fact to recreate the file with
+// the correct ownership by copying it.
+
+int
+DLDbListCFPref::testAndFixPropertyList()
+{
+       char *prefsPath = (char *)mPrefsPath.c_str();
+       
+       int fd1, fd2, retval;
+       struct stat stbuf;
+
+       if((fd1 = open(prefsPath, O_RDONLY)) < 0) {
+               if (errno == ENOENT) return 0; // Doesn't exist - the default case
+               else return -1;
+       }
+       
+       if((retval = fstat(fd1, &stbuf)) == -1) return -1;
+               
+       if(stbuf.st_uid != getuid()) {
+               char tempfile[MAXPATHLEN+1];
+
+               snprintf(tempfile, MAXPATHLEN, "%s.XXXXX", prefsPath);
+               mktemp(tempfile);
+               changeIdentity(UNPRIV);
+               if((fd2 = open(tempfile, O_RDWR | O_CREAT | O_EXCL, 0666)) < 0) {
+                       retval = -1;
+               } else {
+                       copyfile_state_t s = copyfile_state_alloc();
+                       retval = fcopyfile(fd1, fd2, s, COPYFILE_DATA);
+                       copyfile_state_free(s);
+                       if(!retval) retval = ::unlink(prefsPath);
+                       if(!retval) retval = ::rename(tempfile, prefsPath);
+               }
+               changeIdentity(PRIV);
+               close(fd2);
+       }
+       close(fd1);
+       return retval;
+}
+
+// Encapsulated process uid/gid change routine.
+void 
+DLDbListCFPref::changeIdentity(ID_Direction toPriv)
+{
+       if(toPriv == UNPRIV) {
+               savedEUID = geteuid();
+               savedEGID = getegid();
+               if(savedEGID != getgid()) setegid(getgid());
+               if(savedEUID != getuid()) seteuid(getuid());
+       } else {
+               if(savedEUID != getuid()) seteuid(savedEUID);
+               if(savedEGID != getgid()) setegid(savedEGID);
+       }
+}
+
+void
+DLDbListCFPref::resetCachedValues()
+{
+       // Unset the login and default Keychain.
+       mLoginDLDbIdentifier = mDefaultDLDbIdentifier = DLDbIdentifier();
+
+       // Clear the searchList.
+       mSearchList.clear();
+
+       changed(false);
+
+    // Note that none of our cached values are valid
+    mSearchListSet = mDefaultDLDbIdentifierSet = mLoginDLDbIdentifierSet = false;
+
+       mPrefsTimeStamp = CFAbsoluteTimeGetCurrent();
+}
+
+void DLDbListCFPref::save()
+{
+    if (!hasChanged())
+        return;
+
+       // Resync from disc to make sure we don't clobber anyone elses changes.
+       // @@@ This is probably already done by the next layer up so we don't
+       // really need to do it here again.
+       loadPropertyList(true);
+
+    // Do the searchList first since it might end up invoking defaultDLDbIdentifier() which can set
+    // mLoginDLDbIdentifierSet and mDefaultDLDbIdentifierSet to true.
+    if (mSearchListSet)
+    {
+        // Make a temporary CFArray with the contents of the vector
+        if (mSearchList.size() == 1 && mSearchList[0] == defaultDLDbIdentifier() && mSearchList[0] == LoginDLDbIdentifier())
+        {
+            // The only element in the search list is the default keychain, which is a
+            // post Jaguar style login keychain, so omit the entry from the prefs file.
+            CFDictionaryRemoveValue(mPropertyList, kDefaultDLDbListKey);
+        }
+        else
+        {
+            CFMutableArrayRef searchArray = CFArrayCreateMutable(kCFAllocatorDefault, mSearchList.size(), &kCFTypeArrayCallBacks);
+            for (DLDbList::const_iterator ix=mSearchList.begin();ix!=mSearchList.end();ix++)
+            {
+                CFDictionaryRef aDict = dlDbIdentifierToCFDictionaryRef(*ix);
+                CFArrayAppendValue(searchArray, aDict);
+                CFRelease(aDict);
+            }
+    
+            CFDictionarySetValue(mPropertyList, kDefaultDLDbListKey, searchArray);
+            CFRelease(searchArray);
+        }
+    }
+
+    if (mLoginDLDbIdentifierSet)
+    {
+        // Make a temporary CFArray with the login keychain
+        CFArrayRef loginArray = NULL;
+        if (!mLoginDLDbIdentifier)
+        {
+            loginArray = CFArrayCreate(kCFAllocatorDefault, NULL, 0, &kCFTypeArrayCallBacks);
+        }
+        else if (!(mLoginDLDbIdentifier == LoginDLDbIdentifier()))
+        {
+            CFDictionaryRef aDict = dlDbIdentifierToCFDictionaryRef(mLoginDLDbIdentifier);
+            const void *value = reinterpret_cast<const void *>(aDict);
+            loginArray = CFArrayCreate(kCFAllocatorDefault, &value, 1, &kCFTypeArrayCallBacks);
+            CFRelease(aDict);
+        }
+    
+        if (loginArray)
+        {
+            CFDictionarySetValue(mPropertyList, kLoginKeychainKey, loginArray);
+            CFRelease(loginArray);
+        }
+        else
+            CFDictionaryRemoveValue(mPropertyList, kLoginKeychainKey);
+    }
+
+    if (mDefaultDLDbIdentifierSet)
+    {
+        // Make a temporary CFArray with the default keychain
+        CFArrayRef defaultArray = NULL;
+        if (!mDefaultDLDbIdentifier)
+        {
+            defaultArray = CFArrayCreate(kCFAllocatorDefault, NULL, 0, &kCFTypeArrayCallBacks);
+        }
+        else if (!(mDefaultDLDbIdentifier == LoginDLDbIdentifier()))
+        {
+            CFDictionaryRef aDict = dlDbIdentifierToCFDictionaryRef(mDefaultDLDbIdentifier);
+            const void *value = reinterpret_cast<const void *>(aDict);
+            defaultArray = CFArrayCreate(kCFAllocatorDefault, &value, 1, &kCFTypeArrayCallBacks);
+            CFRelease(aDict);
+        }
+    
+        if (defaultArray)
+        {
+            CFDictionarySetValue(mPropertyList, kDefaultKeychainKey, defaultArray);
+            CFRelease(defaultArray);
+        }
+        else
+            CFDictionaryRemoveValue(mPropertyList, kDefaultKeychainKey);
+    }
+
+       writePropertyList();
+    changed(false);
+}
+
+
+//----------------------------------------------------------------------
+//                     Conversions
+//----------------------------------------------------------------------
+
+DLDbIdentifier DLDbListCFPref::LoginDLDbIdentifier()
+{
+       CSSM_VERSION theVersion={};
+    CssmSubserviceUid ssuid(gGuidAppleCSPDL,&theVersion,0,CSSM_SERVICE_DL|CSSM_SERVICE_CSP);
+       CssmNetAddress *dbLocation=NULL;
+
+       switch (mDomain) {
+       case kSecPreferencesDomainUser:
+               return DLDbIdentifier(ssuid, ExpandTildesInPath(kUserLoginKeychainPath).c_str(), dbLocation);
+       default:
+               assert(false);
+       case kSecPreferencesDomainSystem:
+       case kSecPreferencesDomainCommon:
+               return DLDbIdentifier(ssuid, kSystemLoginKeychainPath, dbLocation);
+       }
+}
+
+DLDbIdentifier DLDbListCFPref::JaguarLoginDLDbIdentifier()
+{
+       CSSM_VERSION theVersion={};
+    CssmSubserviceUid ssuid(gGuidAppleCSPDL,&theVersion,0,CSSM_SERVICE_DL|CSSM_SERVICE_CSP);
+       CssmNetAddress *dbLocation=NULL;
+
+       switch (mDomain) {
+       case kSecPreferencesDomainUser:
+    {
+        string basepath = ExpandTildesInPath(kLoginKeychainPathPrefix) + getPwInfo(kUsername);
+        return DLDbIdentifier(ssuid,basepath.c_str(),dbLocation);
+    }
+       case kSecPreferencesDomainSystem:
+       case kSecPreferencesDomainCommon:
+               return DLDbIdentifier(ssuid, kSystemLoginKeychainPath, dbLocation);
+       default:
+               assert(false);
+               return DLDbIdentifier();
+       }
+}
+
+DLDbIdentifier DLDbListCFPref::makeDLDbIdentifier (const CSSM_GUID &guid, const CSSM_VERSION &version,
+                                                                                                  uint32 subserviceId, CSSM_SERVICE_TYPE subserviceType,
+                                                                                                  const char* dbName, CSSM_NET_ADDRESS *dbLocation)
+{
+       CssmSubserviceUid ssuid (guid, &version, subserviceId, subserviceType);
+       return DLDbIdentifier (ssuid, ExpandTildesInPath (dbName).c_str (), dbLocation);
+}
+
+DLDbIdentifier DLDbListCFPref::cfDictionaryRefToDLDbIdentifier(CFDictionaryRef theDict)
+{
+    // We must get individual values from the dictionary and store in basic types
+       if (CFGetTypeID(theDict) != CFDictionaryGetTypeID())
+               throw std::logic_error("wrong type in property list");
+
+    // GUID
+    CCFValue vGuid(::CFDictionaryGetValue(theDict,kKeyGUID));
+    string guidStr=vGuid;
+    const Guid guid(guidStr.c_str());
+
+    //CSSM_VERSION
+       CSSM_VERSION theVersion={0,};
+    CCFValue vMajor(::CFDictionaryGetValue(theDict,kKeyMajorVersion));
+       theVersion.Major = vMajor;
+    CCFValue vMinor(::CFDictionaryGetValue(theDict,kKeyMinorVersion));
+       theVersion.Minor = vMinor;
+
+    //subserviceId
+    CCFValue vSsid(::CFDictionaryGetValue(theDict,kKeySubserviceId));
+    uint32 subserviceId=sint32(vSsid);
+
+    //CSSM_SERVICE_TYPE
+    CSSM_SERVICE_TYPE subserviceType=CSSM_SERVICE_DL;
+    CCFValue vSsType(::CFDictionaryGetValue(theDict,kKeySubserviceType));
+    subserviceType=vSsType;
+    
+    // Get DbName from dictionary
+    CCFValue vDbName(::CFDictionaryGetValue(theDict,kKeyDbName));
+    string dbName=vDbName;
+    
+    // jch Get DbLocation from dictionary
+       CssmNetAddress *dbLocation=NULL;
+    
+       return makeDLDbIdentifier (guid, theVersion, subserviceId, subserviceType, dbName.c_str (), dbLocation);
+}
+
+void DLDbListCFPref::clearPWInfo ()
+{
+    if (mPdbLookup != NULL)
+    {
+        delete mPdbLookup;
+        mPdbLookup = NULL;
+    }
+}
+
+string DLDbListCFPref::getPwInfo(PwInfoType type)
+{
+    const char *value;
+    switch (type)
+    {
+    case kHomeDir:
+               if (KeychainHomeFromXPC) {
+                       value = xpc_string_get_string_ptr(KeychainHomeFromXPC);
+               } else {
+                       value = getenv("HOME");
+               }
+        if (value)
+            return value;
+        break;
+    case kUsername:
+        value = getenv("USER");
+        if (value)
+            return value;
+        break;
+    }
+
+       // Get our effective uid
+       uid_t uid = geteuid();
+       // If we are setuid root use the real uid instead
+       if (!uid) uid = getuid();
+
+    // get the password entries
+    if (mPdbLookup == NULL)
+    {
+        mPdbLookup = new PasswordDBLookup ();
+    }
+    
+    mPdbLookup->lookupInfoOnUID (uid);
+    
+    string result;
+    switch (type)
+    {
+    case kHomeDir:
+        result = mPdbLookup->getDirectory ();
+        break;
+    case kUsername:
+        result = mPdbLookup->getName ();
+        break;
+    }
+
+       return result;
+}
+
+static void check_app_sandbox()
+{
+       if (!_xpc_runtime_is_app_sandboxed()) {
+               // We are not in a sandbox, no work to do here
+               return;
+       }
+       
+       extern xpc_object_t xpc_create_with_format(const char * format, ...);
+       xpc_connection_t con = xpc_connection_create("com.apple.security.XPCKeychainSandboxCheck", NULL);
+    xpc_connection_set_event_handler(con, ^(xpc_object_t event) {
+        xpc_type_t xtype = xpc_get_type(event);
+        if (XPC_TYPE_ERROR == xtype) {
+            syslog(LOG_ERR, "Keychain sandbox connection error: %s\n", xpc_dictionary_get_string(event, XPC_ERROR_KEY_DESCRIPTION));
+        } else {
+            syslog(LOG_ERR, "Keychain sandbox unexpected connection event %p\n", event);
+        }
+    });
+    xpc_connection_resume(con);
+    
+    xpc_object_t message = xpc_create_with_format("{op: GrantKeychainPaths}");
+       xpc_object_t reply = xpc_connection_send_message_with_reply_sync(con, message);
+       xpc_type_t xtype = xpc_get_type(reply);
+       if (XPC_TYPE_DICTIONARY == xtype) {
+#if 0
+               // This is useful for debugging.
+               char *debug = xpc_copy_description(reply);
+               syslog(LOG_ERR, "DEBUG (KCsandbox) %s\n", debug);
+               free(debug);
+#endif
+               
+               xpc_object_t extensions_array = xpc_dictionary_get_value(reply, "extensions");
+               xpc_array_apply(extensions_array, ^(size_t index, xpc_object_t extension) {
+                       char pbuf[MAXPATHLEN];
+                       char *path = pbuf;
+                       int status = sandbox_consume_fs_extension(xpc_string_get_string_ptr(extension), &path);
+                       if (status) {
+                               syslog(LOG_ERR, "Keychain sandbox consume extension error: s=%d p=%s %m\n", status, path);
+                       }
+            status = sandbox_release_fs_extension(xpc_string_get_string_ptr(extension));
+            if (status) {
+                               syslog(LOG_ERR, "Keychain sandbox release extension error: s=%d p=%s %m\n", status, path);
+                       }
+
+                       return (bool)true;
+               });
+               
+               KeychainHomeFromXPC = xpc_dictionary_get_value(reply, "keychain-home");
+               xpc_retain(KeychainHomeFromXPC);
+               xpc_release(con);
+       } else if (XPC_TYPE_ERROR == xtype) {
+               syslog(LOG_ERR, "Keychain sandbox message error: %s\n", xpc_dictionary_get_string(reply, XPC_ERROR_KEY_DESCRIPTION));
+       } else {
+               syslog(LOG_ERR, "Keychain sandbox unexpected message reply type %p\n", xtype);
+       }
+    xpc_release(message);
+       xpc_release(reply);
+}
+
+
+
+string DLDbListCFPref::ExpandTildesInPath(const string &inPath)
+{
+       dispatch_once(&AppSandboxChecked, ^{
+               check_app_sandbox();
+       });
+       
+       if ((short)inPath.find("~/",0,2) == 0)
+        return getPwInfo(kHomeDir) + inPath.substr(1, inPath.length() - 1);
+    else
+        return inPath;
+}
+
+string DLDbListCFPref::StripPathStuff(const string &inPath)
+{
+    if (inPath.find("/private/var/automount/Network/",0,31) == 0)
+        return inPath.substr(22);
+    if (inPath.find("/private/automount/Servers/",0,27) == 0)
+        return "/Network" + inPath.substr(18);
+    if (inPath.find("/automount/Servers/",0,19) == 0)
+        return "/Network" + inPath.substr(10);
+    if (inPath.find("/private/automount/Network/",0,27) == 0)
+        return inPath.substr(18);
+    if (inPath.find("/automount/Network/",0,19) == 0)
+        return inPath.substr(10);
+    if (inPath.find("/private/Network/",0,17) == 0)
+        return inPath.substr(8);
+    return inPath;
+}
+
+string DLDbListCFPref::AbbreviatedPath(const string &inPath)
+{
+    string path = StripPathStuff(inPath);
+    string home = StripPathStuff(getPwInfo(kHomeDir) + "/");
+    size_t homeLen = home.length();
+
+    if (homeLen > 1 && path.find(home.c_str(), 0, homeLen) == 0)
+        return "~" + path.substr(homeLen - 1);
+    else
+        return path;
+}
+
+CFDictionaryRef DLDbListCFPref::dlDbIdentifierToCFDictionaryRef(const DLDbIdentifier& dldbIdentifier)
+{
+       CFRef<CFMutableDictionaryRef> aDict(CFDictionaryCreateMutable(kCFAllocatorDefault,0,
+               &kCFTypeDictionaryKeyCallBacks,&kCFTypeDictionaryValueCallBacks));
+    if (!aDict)
+        throw ::std::bad_alloc();
+
+    // Put SUBSERVICE_UID in dictionary
+    char buffer[Guid::stringRepLength+1];
+    const CssmSubserviceUid& ssuid=dldbIdentifier.ssuid();
+    const Guid &theGuid = Guid::overlay(ssuid.Guid);
+    CFRef<CFStringRef> stringGuid(::CFStringCreateWithCString(kCFAllocatorDefault,
+            theGuid.toString(buffer),kCFStringEncodingMacRoman));
+    if (stringGuid)
+        ::CFDictionarySetValue(aDict,kKeyGUID,stringGuid);
+        
+    if (ssuid.SubserviceId!=0)
+    {
+        CFRef<CFNumberRef> subserviceId(::CFNumberCreate(kCFAllocatorDefault,kCFNumberSInt32Type,&ssuid.SubserviceId));
+        if (subserviceId)
+            ::CFDictionarySetValue(aDict,kKeySubserviceId,subserviceId);
+    }
+    if (ssuid.SubserviceType!=0)
+    {
+        CFRef<CFNumberRef> subserviceType(CFNumberCreate(kCFAllocatorDefault,kCFNumberSInt32Type,&ssuid.SubserviceType));
+        if (subserviceType)
+            ::CFDictionarySetValue(aDict,kKeySubserviceType,subserviceType);
+    }
+    if (ssuid.Version.Major!=0 && ssuid.Version.Minor!=0)
+    {
+        CFRef<CFNumberRef> majorVersion(::CFNumberCreate(kCFAllocatorDefault,kCFNumberSInt32Type,&ssuid.Version.Major));
+        if (majorVersion)
+            ::CFDictionarySetValue(aDict,kKeyMajorVersion,majorVersion);
+        CFRef<CFNumberRef> minorVersion(::CFNumberCreate(kCFAllocatorDefault,kCFNumberSInt32Type,&ssuid.Version.Minor));
+        if (minorVersion)
+            ::CFDictionarySetValue(aDict,kKeyMinorVersion,minorVersion);
+    }
+    
+    // Put DbName in dictionary
+       const char *dbName=dldbIdentifier.dbName();
+    if (dbName)
+    {
+        CFRef<CFStringRef> theDbName(::CFStringCreateWithCString(kCFAllocatorDefault,AbbreviatedPath(dbName).c_str(),kCFStringEncodingUTF8));
+        ::CFDictionarySetValue(aDict,kKeyDbName,theDbName);
+    }
+    // Put DbLocation in dictionary
+       const CSSM_NET_ADDRESS *dbLocation=dldbIdentifier.dbLocation();
+    if (dbLocation!=NULL && dbLocation->AddressType!=CSSM_ADDR_NONE)
+    {
+        CFRef<CFDataRef> theData(::CFDataCreate(kCFAllocatorDefault,dbLocation->Address.Data,dbLocation->Address.Length));
+        if (theData)
+            ::CFDictionarySetValue(aDict,kKeyDbLocation,theData);
+    }
+
+    ::CFRetain(aDict);
+       return aDict;
+}
+
+bool DLDbListCFPref::revert(bool force)
+{ 
+       // If the prefs have not been refreshed in the last kDLDbListCFPrefRevertInterval
+       // seconds or we are asked to force a reload, then reload.
+       if (!loadPropertyList(force))
+               return false;
+
+       resetCachedValues();
+       return true;
+}
+
+void
+DLDbListCFPref::add(const DLDbIdentifier &dldbIdentifier)
+{
+       // convert the location specified in dldbIdentifier to a standard form
+       // make a canonical form of the database name
+       std::string canon = ExpandTildesInPath(AbbreviatedPath(dldbIdentifier.dbName()).c_str());
+
+       DLDbIdentifier localIdentifier  (dldbIdentifier.ssuid(), canon.c_str(), dldbIdentifier.dbLocation ());
+       
+       if (member(localIdentifier))
+               return;
+
+    mSearchList.push_back(localIdentifier);
+    changed(true);
+}
+
+void
+DLDbListCFPref::remove(const DLDbIdentifier &dldbIdentifier)
+{
+    // Make sure mSearchList is set
+    searchList();
+    for (vector<DLDbIdentifier>::iterator ix = mSearchList.begin(); ix != mSearchList.end(); ++ix)
+       {
+               if (*ix==dldbIdentifier)                // found in list
+               {
+                       mSearchList.erase(ix);
+                       changed(true);
+                       break;
+               }
+       }
+}
+
+void
+DLDbListCFPref::rename(const DLDbIdentifier &oldId, const DLDbIdentifier &newId)
+{
+    // Make sure mSearchList is set
+    searchList();
+    for (vector<DLDbIdentifier>::iterator ix = mSearchList.begin();
+               ix != mSearchList.end(); ++ix)
+       {
+               if (*ix==oldId)
+               {
+                       // replace oldId with newId
+                       *ix = newId;
+                       changed(true);
+               }
+               else if (*ix==newId)
+               {
+                       // remove newId except where we just inserted it
+                       mSearchList.erase(ix);
+                       changed(true);
+               }
+       }
+}
+
+bool
+DLDbListCFPref::member(const DLDbIdentifier &dldbIdentifier)
+{
+    if (dldbIdentifier.IsImplEmpty())
+    {
+        return false;
+    }
+    
+    for (vector<DLDbIdentifier>::const_iterator ix = searchList().begin(); ix != mSearchList.end(); ++ix)
+       {
+        if (ix->mImpl == NULL)
+        {
+            continue;
+        }
+        
+               // compare the dldbIdentifiers based on the full, real path to the keychain
+               if (ix->ssuid() == dldbIdentifier.ssuid())
+               {
+                       char localPath[PATH_MAX],
+                                inPath[PATH_MAX];
+                       
+                       // try to resolve these down to a canonical form
+                       const char* localPathPtr = cached_realpath(ix->dbName(), localPath);
+                       const char* inPathPtr = cached_realpath(dldbIdentifier.dbName(), inPath);
+
+                       // if either of the paths didn't resolve for some reason, use the originals
+                       if (localPathPtr == NULL)
+                       {
+                               localPathPtr = ix->dbName();
+                       }
+                       
+                       if (inPathPtr == NULL)
+                       {
+                               inPathPtr = dldbIdentifier.dbName();
+                       }
+                       
+                       if (strcmp(localPathPtr, inPathPtr) == 0)
+                       {
+                               return true;
+                       }
+               }
+       }
+
+       return false;
+}
+
+const vector<DLDbIdentifier> &
+DLDbListCFPref::searchList()
+{
+    if (!mSearchListSet)
+    {
+        CFArrayRef searchList = reinterpret_cast<CFArrayRef>(CFDictionaryGetValue(mPropertyList, kDefaultDLDbListKey));
+        if (searchList && CFGetTypeID(searchList) != CFArrayGetTypeID())
+            searchList = NULL;
+
+        if (searchList)
+        {
+            CFIndex top = CFArrayGetCount(searchList);
+            // Each entry is a CFDictionary; peel it off & add it to the array
+            for (CFIndex idx = 0; idx < top; ++idx)
+            {
+                CFDictionaryRef theDict = reinterpret_cast<CFDictionaryRef>(CFArrayGetValueAtIndex(searchList, idx));
+                try
+                {
+                    mSearchList.push_back(cfDictionaryRefToDLDbIdentifier(theDict));
+                }
+                catch (...)
+                {
+                    // Drop stuff that doesn't parse on the floor.
+                }
+            }
+    
+            // If there were entries specified, but they were invalid revert to using the
+            // default keychain in the searchlist.
+            if (top > 0 && mSearchList.size() == 0)
+                searchList = NULL;
+        }
+
+        // The default when no search list is specified is to only search the
+        // default keychain.
+        if (!searchList && static_cast<bool>(defaultDLDbIdentifier()))
+            mSearchList.push_back(mDefaultDLDbIdentifier);
+
+        mSearchListSet = true;
+    }
+
+       return mSearchList;
+}
+
+void
+DLDbListCFPref::searchList(const vector<DLDbIdentifier> &searchList)
+{
+       vector<DLDbIdentifier> newList(searchList);
+       mSearchList.swap(newList);
+    mSearchListSet = true;
+    changed(true);
+}
+
+void
+DLDbListCFPref::defaultDLDbIdentifier(const DLDbIdentifier &dlDbIdentifier)
+{
+       if (!(defaultDLDbIdentifier() == dlDbIdentifier))
+       {
+               mDefaultDLDbIdentifier = dlDbIdentifier;
+               changed(true);
+       }
+}
+
+const DLDbIdentifier &
+DLDbListCFPref::defaultDLDbIdentifier()
+{
+       
+    if (!mDefaultDLDbIdentifierSet)
+    {
+        CFArrayRef defaultArray = reinterpret_cast<CFArrayRef>(CFDictionaryGetValue(mPropertyList, kDefaultKeychainKey));
+        if (defaultArray && CFGetTypeID(defaultArray) != CFArrayGetTypeID())
+            defaultArray = NULL;
+
+        if (defaultArray && CFArrayGetCount(defaultArray) > 0)
+        {
+            CFDictionaryRef defaultDict = reinterpret_cast<CFDictionaryRef>(CFArrayGetValueAtIndex(defaultArray, 0));
+            try
+            {
+                secdebug("secpref", "getting default DLDbIdentifier from defaultDict");
+                mDefaultDLDbIdentifier = cfDictionaryRefToDLDbIdentifier(defaultDict);
+                secdebug("secpref", "now we think the default keychain is %s", (mDefaultDLDbIdentifier) ? mDefaultDLDbIdentifier.dbName() : "<NULL>");
+            }
+            catch (...)
+            {
+                // If defaultArray doesn't parse fall back on the default way of getting the default keychain
+                defaultArray = NULL;
+            }
+        }
+    
+        if (!defaultArray)
+        {
+                       
+            // If the Panther style login keychain actually exists we use that otherwise no
+            // default is set.
+            mDefaultDLDbIdentifier = loginDLDbIdentifier();
+            secdebug("secpref", "now we think the default keychain is: %s", (mDefaultDLDbIdentifier) ? mDefaultDLDbIdentifier.dbName() : 
+                       "Name doesn't exist");
+                       
+            struct stat st;
+            int st_result = -1;
+                       
+                       if (mDefaultDLDbIdentifier.mImpl != NULL)
+                       {
+                               st_result = stat(mDefaultDLDbIdentifier.dbName(), &st);
+                       }
+                       
+            if (st_result)
+            {
+                               secdebug("secpref", "stat(%s) -> %d", mDefaultDLDbIdentifier.dbName(), st_result);
+                mDefaultDLDbIdentifier  = DLDbIdentifier(); // initialize a NULL keychain
+                secdebug("secpref", "after DLDbIdentifier(), we think the default keychain is %s", static_cast<bool>(mDefaultDLDbIdentifier) ? mDefaultDLDbIdentifier.dbName() : "<NULL>");
+            }
+        }
+               
+        mDefaultDLDbIdentifierSet = true;
+    }
+       
+       
+       return mDefaultDLDbIdentifier;
+}
+
+void
+DLDbListCFPref::loginDLDbIdentifier(const DLDbIdentifier &dlDbIdentifier)
+{
+       if (!(loginDLDbIdentifier() == dlDbIdentifier))
+       {
+               mLoginDLDbIdentifier = dlDbIdentifier;
+               changed(true);
+       }
+}
+
+const DLDbIdentifier &
+DLDbListCFPref::loginDLDbIdentifier()
+{
+    if (!mLoginDLDbIdentifierSet)
+    {
+        CFArrayRef loginArray = reinterpret_cast<CFArrayRef>(CFDictionaryGetValue(mPropertyList, kLoginKeychainKey));
+        if (loginArray && CFGetTypeID(loginArray) != CFArrayGetTypeID())
+            loginArray = NULL;
+
+        if (loginArray && CFArrayGetCount(loginArray) > 0)
+        {
+            CFDictionaryRef loginDict = reinterpret_cast<CFDictionaryRef>(CFArrayGetValueAtIndex(loginArray, 0));
+            try
+            {
+                secdebug("secpref", "Getting login DLDbIdentifier from loginDict");
+                mLoginDLDbIdentifier = cfDictionaryRefToDLDbIdentifier(loginDict);
+                secdebug("secpref", "we think the login keychain is %s", static_cast<bool>(mLoginDLDbIdentifier) ? mLoginDLDbIdentifier.dbName() : "<NULL>");
+            }
+            catch (...)
+            {
+                // If loginArray doesn't parse fall back on the default way of getting the login keychain.
+                loginArray = NULL;
+            }
+        }
+    
+        if (!loginArray)
+        {
+                       mLoginDLDbIdentifier = LoginDLDbIdentifier();
+                       secdebug("secpref", "after LoginDLDbIdentifier(), we think the login keychain is %s", static_cast<bool>(mLoginDLDbIdentifier) ? mLoginDLDbIdentifier.dbName() : "<NULL>");
+        }
+
+        mLoginDLDbIdentifierSet = true;
+    }
+
+       return mLoginDLDbIdentifier;
+}