2 * Copyright (c) 2000-2004,2011-2014 Apple Inc. All Rights Reserved.
4 * @APPLE_LICENSE_HEADER_START@
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
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.
21 * @APPLE_LICENSE_HEADER_END@
29 #include "DLDBListCFPref.h"
30 #include <Security/cssmapple.h>
31 #include <security_utilities/debugging.h>
32 #include <security_utilities/utilities.h>
35 #include <sys/types.h>
39 #include <sys/param.h>
41 #include <xpc/private.h>
44 #include <security_keychain/StorageManager.h>
46 dispatch_once_t AppSandboxChecked
;
47 xpc_object_t KeychainHomeFromXPC
;
49 using namespace CssmClient
;
51 static const double kDLDbListCFPrefRevertInterval
= 30.0;
53 // normal debug calls, which get stubbed out for deployment builds
55 #define kKeyGUID CFSTR("GUID")
56 #define kKeySubserviceId CFSTR("SubserviceId")
57 #define kKeySubserviceType CFSTR("SubserviceType")
58 #define kKeyDbName CFSTR("DbName")
59 #define kKeyDbLocation CFSTR("DbLocation")
60 #define kKeyActive CFSTR("Active")
61 #define kKeyMajorVersion CFSTR("MajorVersion")
62 #define kKeyMinorVersion CFSTR("MinorVersion")
63 #define kDefaultDLDbListKey CFSTR("DLDBSearchList")
64 #define kDefaultKeychainKey CFSTR("DefaultKeychain")
65 #define kLoginKeychainKey CFSTR("LoginKeychain")
66 #define kUserDefaultPath "~/Library/Preferences/com.apple.security.plist"
67 #define kSystemDefaultPath "/Library/Preferences/com.apple.security.plist"
68 #define kCommonDefaultPath "/Library/Preferences/com.apple.security-common.plist"
69 #define kLoginKeychainPathPrefix "~/Library/Keychains/"
70 #define kUserLoginKeychainPath "~/Library/Keychains/login.keychain"
71 #define kSystemLoginKeychainPath "/Library/Keychains/System.keychain"
74 // A utility class for managing password database lookups
76 const time_t kPasswordCacheExpire
= 30; // number of seconds cached password db info is valid
78 PasswordDBLookup::PasswordDBLookup () : mValid (false), mCurrent (0), mTime (0)
82 void PasswordDBLookup::lookupInfoOnUID (uid_t uid
)
84 time_t currentTime
= time (NULL
);
86 if (!mValid
|| uid
!= mCurrent
|| currentTime
- mTime
>= kPasswordCacheExpire
)
88 struct passwd
* pw
= getpwuid(uid
);
91 UnixError::throwMe (EPERM
);
94 mDirectory
= pw
->pw_dir
;
100 secinfo("secpref", "uid=%d caching home=%s", uid
, pw
->pw_dir
);
106 PasswordDBLookup
*DLDbListCFPref::mPdbLookup
= NULL
;
108 //-------------------------------------------------------------------------------------
110 // Lists of DL/DBs, with CFPreferences backing store
112 //-------------------------------------------------------------------------------------
114 DLDbListCFPref::DLDbListCFPref(SecPreferencesDomain domain
) : mDomain(domain
), mPropertyList(NULL
), mChanged(false),
115 mSearchListSet(false), mDefaultDLDbIdentifierSet(false), mLoginDLDbIdentifierSet(false)
117 secinfo("secpref", "New DLDbListCFPref %p for domain %d", this, domain
);
118 loadPropertyList(true);
121 void DLDbListCFPref::set(SecPreferencesDomain domain
)
127 secinfo("secpref", "DLDbListCFPref %p domain set to %d", this, domain
);
129 if (loadPropertyList(true))
133 DLDbListCFPref::~DLDbListCFPref()
138 CFRelease(mPropertyList
);
142 DLDbListCFPref::forceUserSearchListReread()
144 // set mPrefsTimeStamp so that it will "expire" the next time loadPropertyList is called
145 mPrefsTimeStamp
= CFAbsoluteTimeGetCurrent() - kDLDbListCFPrefRevertInterval
;
149 DLDbListCFPref::loadPropertyList(bool force
)
155 case kSecPreferencesDomainUser
:
156 prefsPath
= ExpandTildesInPath(kUserDefaultPath
);
158 case kSecPreferencesDomainSystem
:
159 prefsPath
= kSystemDefaultPath
;
161 case kSecPreferencesDomainCommon
:
162 prefsPath
= kCommonDefaultPath
;
165 MacOSError::throwMe(errSecInvalidPrefsDomain
);
168 secinfo("secpref", "force=%s prefsPath=%s", force
? "true" : "false",
171 CFAbsoluteTime now
= CFAbsoluteTimeGetCurrent();
173 // If for some reason the prefs file path has changed, blow away the old plist and force an update
174 if (mPrefsPath
!= prefsPath
)
176 mPrefsPath
= prefsPath
;
179 CFRelease(mPropertyList
);
180 mPropertyList
= NULL
;
183 mPrefsTimeStamp
= now
;
187 if (now
- mPrefsTimeStamp
< kDLDbListCFPrefRevertInterval
)
190 mPrefsTimeStamp
= now
;
194 if (stat(mPrefsPath
.c_str(), &st
))
200 if (CFDictionaryGetCount(mPropertyList
) == 0)
202 CFRelease(mPropertyList
);
205 mPropertyList
= CFDictionaryCreateMutable(kCFAllocatorDefault
, 0, &kCFTypeDictionaryKeyCallBacks
, &kCFTypeDictionaryValueCallBacks
);
213 if (mTimespec
.tv_sec
== st
.st_mtimespec
.tv_sec
214 && mTimespec
.tv_nsec
== st
.st_mtimespec
.tv_nsec
)
218 mTimespec
= st
.st_mtimespec
;
221 CFMutableDictionaryRef thePropertyList
= NULL
;
222 CFMutableDataRef xmlData
= NULL
;
223 CFStringRef errorString
= NULL
;
228 fd
= open(mPrefsPath
.c_str(), O_RDONLY
, 0);
232 off_t theSize
= lseek(fd
, 0, SEEK_END
);
236 if (lseek(fd
, 0, SEEK_SET
))
239 xmlData
= CFDataCreateMutable(NULL
, CFIndex(theSize
));
242 CFDataSetLength(xmlData
, CFIndex(theSize
));
243 void *buffer
= reinterpret_cast<void *>(CFDataGetMutableBytePtr(xmlData
));
246 ssize_t bytesRead
= read(fd
, buffer
, (size_t)theSize
);
247 if (bytesRead
!= theSize
)
250 thePropertyList
= CFMutableDictionaryRef(CFPropertyListCreateFromXMLData(NULL
, xmlData
, kCFPropertyListMutableContainers
, &errorString
));
251 if (!thePropertyList
)
254 if (CFGetTypeID(thePropertyList
) != CFDictionaryGetTypeID())
256 CFRelease(thePropertyList
);
257 thePropertyList
= NULL
;
267 CFRelease(errorString
);
269 if (!thePropertyList
)
271 thePropertyList
= CFDictionaryCreateMutable(kCFAllocatorDefault
, 0, &kCFTypeDictionaryKeyCallBacks
, &kCFTypeDictionaryValueCallBacks
);
276 if (CFEqual(mPropertyList
, thePropertyList
))
278 // The new property list is the same as the old one, so nothing has changed.
279 CFRelease(thePropertyList
);
282 CFRelease(mPropertyList
);
285 mPropertyList
= thePropertyList
;
290 DLDbListCFPref::writePropertyList()
292 if (!mPropertyList
|| CFDictionaryGetCount(mPropertyList
) == 0)
294 // There is nothing in the mPropertyList dictionary,
295 // so we don't need a prefs file.
296 unlink(mPrefsPath
.c_str());
300 if(testAndFixPropertyList())
303 CFDataRef xmlData
= CFPropertyListCreateXMLData(NULL
, mPropertyList
);
305 return; // Bad out of memory or something evil happened let's act like CF and do nothing.
307 // The prefs file should at least be made readable by user/group/other and writable by the owner.
308 // Change from euid to ruid if needed for the duration of the new prefs file creat.
311 changeIdentity(UNPRIV
);
312 int fd
= open(mPrefsPath
.c_str(), O_WRONLY
|O_CREAT
|O_TRUNC
, mode
);
313 changeIdentity(PRIV
);
316 const void *buffer
= CFDataGetBytePtr(xmlData
);
317 size_t toWrite
= CFDataGetLength(xmlData
);
318 /* ssize_t bytesWritten = */ write(fd
, buffer
, toWrite
);
319 // Emulate CFPreferences by not checking for any errors.
324 mTimespec
= st
.st_mtimespec
;
332 mPrefsTimeStamp
= CFAbsoluteTimeGetCurrent();
335 // This function can clean up some problems caused by setuid clients. We've had instances where the
336 // Keychain search list has become owned by root, but is still able to be re-written by the user because
337 // of the permissions on the directory above. We'll take advantage of that fact to recreate the file with
338 // the correct ownership by copying it.
341 DLDbListCFPref::testAndFixPropertyList()
343 char *prefsPath
= (char *)mPrefsPath
.c_str();
348 if((fd1
= open(prefsPath
, O_RDONLY
)) < 0) {
349 if (errno
== ENOENT
) return 0; // Doesn't exist - the default case
353 if((retval
= fstat(fd1
, &stbuf
)) == -1) return -1;
355 if(stbuf
.st_uid
!= getuid()) {
356 char tempfile
[MAXPATHLEN
+1];
358 changeIdentity(UNPRIV
);
360 snprintf(tempfile
, MAXPATHLEN
, "%s.XXXXXX", prefsPath
);
361 int fd2
= mkstemp(tempfile
);
362 if (fd2
< 0 || ::fchmod(fd2
, 0644) != 0) {
366 copyfile_state_t s
= copyfile_state_alloc();
367 retval
= fcopyfile(fd1
, fd2
, s
, COPYFILE_DATA
);
368 copyfile_state_free(s
);
372 retval
= ::unlink(prefsPath
);
373 if(!retval
) retval
= ::rename(tempfile
, prefsPath
);
376 changeIdentity(PRIV
);
384 // Encapsulated process uid/gid change routine.
386 DLDbListCFPref::changeIdentity(ID_Direction toPriv
)
388 if(toPriv
== UNPRIV
) {
389 savedEUID
= geteuid();
390 savedEGID
= getegid();
391 if(savedEGID
!= getgid()) setegid(getgid());
392 if(savedEUID
!= getuid()) seteuid(getuid());
394 if(savedEUID
!= getuid()) seteuid(savedEUID
);
395 if(savedEGID
!= getgid()) setegid(savedEGID
);
400 DLDbListCFPref::resetCachedValues()
402 // Unset the login and default Keychain.
403 mLoginDLDbIdentifier
= mDefaultDLDbIdentifier
= DLDbIdentifier();
405 // Clear the searchList.
410 // Note that none of our cached values are valid
411 mSearchListSet
= mDefaultDLDbIdentifierSet
= mLoginDLDbIdentifierSet
= false;
413 mPrefsTimeStamp
= CFAbsoluteTimeGetCurrent();
416 void DLDbListCFPref::save()
422 // Resync from disc to make sure we don't clobber anyone elses changes.
423 // @@@ This is probably already done by the next layer up so we don't
424 // really need to do it here again.
425 loadPropertyList(true);
427 // Do the searchList first since it might end up invoking defaultDLDbIdentifier() which can set
428 // mLoginDLDbIdentifierSet and mDefaultDLDbIdentifierSet to true.
431 // Make a temporary CFArray with the contents of the vector
432 if (mSearchList
.size() == 1 && mSearchList
[0] == defaultDLDbIdentifier() && mSearchList
[0] == LoginDLDbIdentifier())
434 // The only element in the search list is the default keychain, which is a
435 // post Jaguar style login keychain, so omit the entry from the prefs file.
436 CFDictionaryRemoveValue(mPropertyList
, kDefaultDLDbListKey
);
440 CFMutableArrayRef searchArray
= CFArrayCreateMutable(kCFAllocatorDefault
, mSearchList
.size(), &kCFTypeArrayCallBacks
);
441 for (DLDbList::const_iterator ix
=mSearchList
.begin();ix
!=mSearchList
.end();ix
++)
443 CFDictionaryRef aDict
= dlDbIdentifierToCFDictionaryRef(*ix
);
444 CFArrayAppendValue(searchArray
, aDict
);
448 CFDictionarySetValue(mPropertyList
, kDefaultDLDbListKey
, searchArray
);
449 CFRelease(searchArray
);
453 if (mLoginDLDbIdentifierSet
)
455 // Make a temporary CFArray with the login keychain
456 CFArrayRef loginArray
= NULL
;
457 if (!mLoginDLDbIdentifier
)
459 loginArray
= CFArrayCreate(kCFAllocatorDefault
, NULL
, 0, &kCFTypeArrayCallBacks
);
461 else if (!(mLoginDLDbIdentifier
== LoginDLDbIdentifier()))
463 CFDictionaryRef aDict
= dlDbIdentifierToCFDictionaryRef(mLoginDLDbIdentifier
);
464 const void *value
= reinterpret_cast<const void *>(aDict
);
465 loginArray
= CFArrayCreate(kCFAllocatorDefault
, &value
, 1, &kCFTypeArrayCallBacks
);
471 CFDictionarySetValue(mPropertyList
, kLoginKeychainKey
, loginArray
);
472 CFRelease(loginArray
);
475 CFDictionaryRemoveValue(mPropertyList
, kLoginKeychainKey
);
478 if (mDefaultDLDbIdentifierSet
)
480 // Make a temporary CFArray with the default keychain
481 CFArrayRef defaultArray
= NULL
;
482 if (!mDefaultDLDbIdentifier
)
484 defaultArray
= CFArrayCreate(kCFAllocatorDefault
, NULL
, 0, &kCFTypeArrayCallBacks
);
486 else if (!(mDefaultDLDbIdentifier
== LoginDLDbIdentifier()))
488 CFDictionaryRef aDict
= dlDbIdentifierToCFDictionaryRef(mDefaultDLDbIdentifier
);
489 const void *value
= reinterpret_cast<const void *>(aDict
);
490 defaultArray
= CFArrayCreate(kCFAllocatorDefault
, &value
, 1, &kCFTypeArrayCallBacks
);
496 CFDictionarySetValue(mPropertyList
, kDefaultKeychainKey
, defaultArray
);
497 CFRelease(defaultArray
);
500 CFDictionaryRemoveValue(mPropertyList
, kDefaultKeychainKey
);
508 //----------------------------------------------------------------------
510 //----------------------------------------------------------------------
512 DLDbIdentifier
DLDbListCFPref::LoginDLDbIdentifier()
514 CSSM_VERSION theVersion
={};
515 CssmSubserviceUid
ssuid(gGuidAppleCSPDL
,&theVersion
,0,CSSM_SERVICE_DL
|CSSM_SERVICE_CSP
);
516 CssmNetAddress
*dbLocation
=NULL
;
519 case kSecPreferencesDomainUser
:
520 return DLDbIdentifier(ssuid
, ExpandTildesInPath(kUserLoginKeychainPath
).c_str(), dbLocation
);
523 case kSecPreferencesDomainSystem
:
524 case kSecPreferencesDomainCommon
:
525 return DLDbIdentifier(ssuid
, kSystemLoginKeychainPath
, dbLocation
);
529 DLDbIdentifier
DLDbListCFPref::JaguarLoginDLDbIdentifier()
531 CSSM_VERSION theVersion
={};
532 CssmSubserviceUid
ssuid(gGuidAppleCSPDL
,&theVersion
,0,CSSM_SERVICE_DL
|CSSM_SERVICE_CSP
);
533 CssmNetAddress
*dbLocation
=NULL
;
536 case kSecPreferencesDomainUser
:
538 string basepath
= ExpandTildesInPath(kLoginKeychainPathPrefix
) + getPwInfo(kUsername
);
539 return DLDbIdentifier(ssuid
,basepath
.c_str(),dbLocation
);
541 case kSecPreferencesDomainSystem
:
542 case kSecPreferencesDomainCommon
:
543 return DLDbIdentifier(ssuid
, kSystemLoginKeychainPath
, dbLocation
);
546 return DLDbIdentifier();
550 DLDbIdentifier
DLDbListCFPref::makeDLDbIdentifier (const CSSM_GUID
&guid
, const CSSM_VERSION
&version
,
551 uint32 subserviceId
, CSSM_SERVICE_TYPE subserviceType
,
552 const char* dbName
, CSSM_NET_ADDRESS
*dbLocation
)
554 CssmSubserviceUid
ssuid (guid
, &version
, subserviceId
, subserviceType
);
555 return DLDbIdentifier (ssuid
, ExpandTildesInPath (dbName
).c_str (), dbLocation
);
558 DLDbIdentifier
DLDbListCFPref::cfDictionaryRefToDLDbIdentifier(CFDictionaryRef theDict
)
560 // We must get individual values from the dictionary and store in basic types
561 if (CFGetTypeID(theDict
) != CFDictionaryGetTypeID())
562 throw std::logic_error("wrong type in property list");
565 CCFValue
vGuid(::CFDictionaryGetValue(theDict
,kKeyGUID
));
566 string guidStr
=vGuid
;
567 const Guid
guid(guidStr
.c_str());
570 CSSM_VERSION theVersion
={0,};
571 CCFValue
vMajor(::CFDictionaryGetValue(theDict
,kKeyMajorVersion
));
572 theVersion
.Major
= vMajor
;
573 CCFValue
vMinor(::CFDictionaryGetValue(theDict
,kKeyMinorVersion
));
574 theVersion
.Minor
= vMinor
;
577 CCFValue
vSsid(::CFDictionaryGetValue(theDict
,kKeySubserviceId
));
578 uint32 subserviceId
=sint32(vSsid
);
581 CSSM_SERVICE_TYPE subserviceType
=CSSM_SERVICE_DL
;
582 CCFValue
vSsType(::CFDictionaryGetValue(theDict
,kKeySubserviceType
));
583 subserviceType
=vSsType
;
585 // Get DbName from dictionary
586 CCFValue
vDbName(::CFDictionaryGetValue(theDict
,kKeyDbName
));
587 string dbName
=vDbName
;
589 // jch Get DbLocation from dictionary
590 CssmNetAddress
*dbLocation
=NULL
;
592 return makeDLDbIdentifier (guid
, theVersion
, subserviceId
, subserviceType
, dbName
.c_str (), dbLocation
);
595 void DLDbListCFPref::clearPWInfo ()
597 if (mPdbLookup
!= NULL
)
604 string
DLDbListCFPref::getPwInfo(PwInfoType type
)
610 if (KeychainHomeFromXPC
) {
611 value
= xpc_string_get_string_ptr(KeychainHomeFromXPC
);
613 value
= getenv("HOME");
619 value
= getenv("USER");
625 // Get our effective uid
626 uid_t uid
= geteuid();
627 // If we are setuid root use the real uid instead
628 if (!uid
) uid
= getuid();
630 // get the password entries
631 if (mPdbLookup
== NULL
)
633 mPdbLookup
= new PasswordDBLookup ();
636 mPdbLookup
->lookupInfoOnUID (uid
);
642 result
= mPdbLookup
->getDirectory ();
645 result
= mPdbLookup
->getName ();
652 static void check_app_sandbox()
654 if (!_xpc_runtime_is_app_sandboxed()) {
655 // We are not in a sandbox, no work to do here
659 extern xpc_object_t
xpc_create_with_format(const char * format
, ...);
660 xpc_connection_t con
= xpc_connection_create("com.apple.security.XPCKeychainSandboxCheck", NULL
);
661 xpc_connection_set_event_handler(con
, ^(xpc_object_t event
) {
662 xpc_type_t xtype
= xpc_get_type(event
);
663 if (XPC_TYPE_ERROR
== xtype
) {
664 syslog(LOG_ERR
, "Keychain sandbox connection error: %s\n", xpc_dictionary_get_string(event
, XPC_ERROR_KEY_DESCRIPTION
));
666 syslog(LOG_ERR
, "Keychain sandbox unexpected connection event %p\n", event
);
669 xpc_connection_resume(con
);
671 xpc_object_t message
= xpc_create_with_format("{op: GrantKeychainPaths}");
672 xpc_object_t reply
= xpc_connection_send_message_with_reply_sync(con
, message
);
673 xpc_type_t xtype
= xpc_get_type(reply
);
674 if (XPC_TYPE_DICTIONARY
== xtype
) {
676 // This is useful for debugging.
677 char *debug
= xpc_copy_description(reply
);
678 syslog(LOG_ERR
, "DEBUG (KCsandbox) %s\n", debug
);
682 xpc_object_t extensions_array
= xpc_dictionary_get_value(reply
, "extensions");
683 xpc_array_apply(extensions_array
, ^(size_t index
, xpc_object_t extension
) {
684 char pbuf
[MAXPATHLEN
];
686 int status
= sandbox_consume_fs_extension(xpc_string_get_string_ptr(extension
), &path
);
688 syslog(LOG_ERR
, "Keychain sandbox consume extension error: s=%d p=%s %m\n", status
, path
);
690 status
= sandbox_release_fs_extension(xpc_string_get_string_ptr(extension
));
692 syslog(LOG_ERR
, "Keychain sandbox release extension error: s=%d p=%s %m\n", status
, path
);
698 KeychainHomeFromXPC
= xpc_dictionary_get_value(reply
, "keychain-home");
699 xpc_retain(KeychainHomeFromXPC
);
701 } else if (XPC_TYPE_ERROR
== xtype
) {
702 syslog(LOG_ERR
, "Keychain sandbox message error: %s\n", xpc_dictionary_get_string(reply
, XPC_ERROR_KEY_DESCRIPTION
));
704 syslog(LOG_ERR
, "Keychain sandbox unexpected message reply type %p\n", xtype
);
706 xpc_release(message
);
712 string
DLDbListCFPref::ExpandTildesInPath(const string
&inPath
)
714 dispatch_once(&AppSandboxChecked
, ^{
718 if ((short)inPath
.find("~/",0,2) == 0)
719 return getPwInfo(kHomeDir
) + inPath
.substr(1, inPath
.length() - 1);
724 string
DLDbListCFPref::StripPathStuff(const string
&inPath
)
726 if (inPath
.find("/private/var/automount/Network/",0,31) == 0)
727 return inPath
.substr(22);
728 if (inPath
.find("/private/automount/Servers/",0,27) == 0)
729 return "/Network" + inPath
.substr(18);
730 if (inPath
.find("/automount/Servers/",0,19) == 0)
731 return "/Network" + inPath
.substr(10);
732 if (inPath
.find("/private/automount/Network/",0,27) == 0)
733 return inPath
.substr(18);
734 if (inPath
.find("/automount/Network/",0,19) == 0)
735 return inPath
.substr(10);
736 if (inPath
.find("/private/Network/",0,17) == 0)
737 return inPath
.substr(8);
741 string
DLDbListCFPref::AbbreviatedPath(const string
&inPath
)
743 string path
= StripPathStuff(inPath
);
744 string home
= StripPathStuff(getPwInfo(kHomeDir
) + "/");
745 size_t homeLen
= home
.length();
747 if (homeLen
> 1 && path
.find(home
.c_str(), 0, homeLen
) == 0)
748 return "~" + path
.substr(homeLen
- 1);
753 CFDictionaryRef
DLDbListCFPref::dlDbIdentifierToCFDictionaryRef(const DLDbIdentifier
& dldbIdentifier
)
755 CFRef
<CFMutableDictionaryRef
> aDict(CFDictionaryCreateMutable(kCFAllocatorDefault
,0,
756 &kCFTypeDictionaryKeyCallBacks
,&kCFTypeDictionaryValueCallBacks
));
758 throw ::std::bad_alloc();
760 // Put SUBSERVICE_UID in dictionary
761 char buffer
[Guid::stringRepLength
+1];
762 const CssmSubserviceUid
& ssuid
=dldbIdentifier
.ssuid();
763 const Guid
&theGuid
= Guid::overlay(ssuid
.Guid
);
764 CFRef
<CFStringRef
> stringGuid(::CFStringCreateWithCString(kCFAllocatorDefault
,
765 theGuid
.toString(buffer
),kCFStringEncodingMacRoman
));
767 ::CFDictionarySetValue(aDict
,kKeyGUID
,stringGuid
);
769 if (ssuid
.SubserviceId
!=0)
771 CFRef
<CFNumberRef
> subserviceId(::CFNumberCreate(kCFAllocatorDefault
,kCFNumberSInt32Type
,&ssuid
.SubserviceId
));
773 ::CFDictionarySetValue(aDict
,kKeySubserviceId
,subserviceId
);
775 if (ssuid
.SubserviceType
!=0)
777 CFRef
<CFNumberRef
> subserviceType(CFNumberCreate(kCFAllocatorDefault
,kCFNumberSInt32Type
,&ssuid
.SubserviceType
));
779 ::CFDictionarySetValue(aDict
,kKeySubserviceType
,subserviceType
);
781 if (ssuid
.Version
.Major
!=0 && ssuid
.Version
.Minor
!=0)
783 CFRef
<CFNumberRef
> majorVersion(::CFNumberCreate(kCFAllocatorDefault
,kCFNumberSInt32Type
,&ssuid
.Version
.Major
));
785 ::CFDictionarySetValue(aDict
,kKeyMajorVersion
,majorVersion
);
786 CFRef
<CFNumberRef
> minorVersion(::CFNumberCreate(kCFAllocatorDefault
,kCFNumberSInt32Type
,&ssuid
.Version
.Minor
));
788 ::CFDictionarySetValue(aDict
,kKeyMinorVersion
,minorVersion
);
791 // Put DbName in dictionary
792 const char *dbName
=dldbIdentifier
.dbName();
795 CFRef
<CFStringRef
> theDbName(::CFStringCreateWithCString(kCFAllocatorDefault
,AbbreviatedPath(dbName
).c_str(),kCFStringEncodingUTF8
));
796 ::CFDictionarySetValue(aDict
,kKeyDbName
,theDbName
);
798 // Put DbLocation in dictionary
799 const CSSM_NET_ADDRESS
*dbLocation
=dldbIdentifier
.dbLocation();
800 if (dbLocation
!=NULL
&& dbLocation
->AddressType
!=CSSM_ADDR_NONE
)
802 CFRef
<CFDataRef
> theData(::CFDataCreate(kCFAllocatorDefault
,dbLocation
->Address
.Data
,dbLocation
->Address
.Length
));
804 ::CFDictionarySetValue(aDict
,kKeyDbLocation
,theData
);
811 bool DLDbListCFPref::revert(bool force
)
813 // If the prefs have not been refreshed in the last kDLDbListCFPrefRevertInterval
814 // seconds or we are asked to force a reload, then reload.
815 if (!loadPropertyList(force
))
823 DLDbListCFPref::add(const DLDbIdentifier
&dldbIdentifier
)
825 // convert the location specified in dldbIdentifier to a standard form
826 // make a canonical form of the database name
827 std::string canon
= ExpandTildesInPath(AbbreviatedPath(dldbIdentifier
.dbName()).c_str());
829 DLDbIdentifier
localIdentifier (dldbIdentifier
.ssuid(), canon
.c_str(), dldbIdentifier
.dbLocation ());
831 if (member(localIdentifier
))
834 mSearchList
.push_back(localIdentifier
);
839 DLDbListCFPref::remove(const DLDbIdentifier
&dldbIdentifier
)
841 // Make sure mSearchList is set
843 for (vector
<DLDbIdentifier
>::iterator ix
= mSearchList
.begin(); ix
!= mSearchList
.end(); ++ix
)
845 if (*ix
==dldbIdentifier
) // found in list
847 mSearchList
.erase(ix
);
855 DLDbListCFPref::rename(const DLDbIdentifier
&oldId
, const DLDbIdentifier
&newId
)
857 // Make sure mSearchList is set
859 for (vector
<DLDbIdentifier
>::iterator ix
= mSearchList
.begin();
860 ix
!= mSearchList
.end(); ++ix
)
864 // replace oldId with newId
870 // remove newId except where we just inserted it
871 mSearchList
.erase(ix
);
878 DLDbListCFPref::member(const DLDbIdentifier
&dldbIdentifier
)
880 if (dldbIdentifier
.IsImplEmpty())
885 for (vector
<DLDbIdentifier
>::const_iterator ix
= searchList().begin(); ix
!= mSearchList
.end(); ++ix
)
887 if (ix
->mImpl
== NULL
)
892 // compare the dldbIdentifiers based on the full, real path to the keychain
893 if (ix
->ssuid() == dldbIdentifier
.ssuid())
895 char localPath
[PATH_MAX
],
898 // try to resolve these down to a canonical form
899 const char* localPathPtr
= cached_realpath(ix
->dbName(), localPath
);
900 const char* inPathPtr
= cached_realpath(dldbIdentifier
.dbName(), inPath
);
902 // if either of the paths didn't resolve for some reason, use the originals
903 if (localPathPtr
== NULL
)
905 localPathPtr
= ix
->dbName();
908 if (inPathPtr
== NULL
)
910 inPathPtr
= dldbIdentifier
.dbName();
913 if (strcmp(localPathPtr
, inPathPtr
) == 0)
923 const vector
<DLDbIdentifier
> &
924 DLDbListCFPref::searchList()
928 CFArrayRef searchList
= reinterpret_cast<CFArrayRef
>(CFDictionaryGetValue(mPropertyList
, kDefaultDLDbListKey
));
929 if (searchList
&& CFGetTypeID(searchList
) != CFArrayGetTypeID())
934 CFIndex top
= CFArrayGetCount(searchList
);
935 // Each entry is a CFDictionary; peel it off & add it to the array
936 for (CFIndex idx
= 0; idx
< top
; ++idx
)
938 CFDictionaryRef theDict
= reinterpret_cast<CFDictionaryRef
>(CFArrayGetValueAtIndex(searchList
, idx
));
941 mSearchList
.push_back(cfDictionaryRefToDLDbIdentifier(theDict
));
945 // Drop stuff that doesn't parse on the floor.
949 // If there were entries specified, but they were invalid revert to using the
950 // default keychain in the searchlist.
951 if (top
> 0 && mSearchList
.size() == 0)
955 // The default when no search list is specified is to only search the
957 if (!searchList
&& static_cast<bool>(defaultDLDbIdentifier()))
958 mSearchList
.push_back(mDefaultDLDbIdentifier
);
960 mSearchListSet
= true;
967 DLDbListCFPref::searchList(const vector
<DLDbIdentifier
> &searchList
)
969 if(searchList
.size() == 0) {
971 mSearchListSet
= false;
976 vector
<DLDbIdentifier
> newList(searchList
);
977 mSearchList
.swap(newList
);
978 mSearchListSet
= true;
983 DLDbListCFPref::defaultDLDbIdentifier(const DLDbIdentifier
&dlDbIdentifier
)
985 if (!(defaultDLDbIdentifier() == dlDbIdentifier
))
987 mDefaultDLDbIdentifier
= dlDbIdentifier
;
992 // Caution: if the backing file for the defaultDLDbIdentifier doesn't exist (or if the plist file is corrupt),
993 // this will return a DLDbIdentifier with a NULL impl
994 const DLDbIdentifier
&
995 DLDbListCFPref::defaultDLDbIdentifier()
998 if (!mDefaultDLDbIdentifierSet
)
1000 CFArrayRef defaultArray
= reinterpret_cast<CFArrayRef
>(CFDictionaryGetValue(mPropertyList
, kDefaultKeychainKey
));
1001 if (defaultArray
&& CFGetTypeID(defaultArray
) != CFArrayGetTypeID())
1002 defaultArray
= NULL
;
1004 if (defaultArray
&& CFArrayGetCount(defaultArray
) > 0)
1006 CFDictionaryRef defaultDict
= reinterpret_cast<CFDictionaryRef
>(CFArrayGetValueAtIndex(defaultArray
, 0));
1009 secinfo("secpref", "getting default DLDbIdentifier from defaultDict");
1010 mDefaultDLDbIdentifier
= cfDictionaryRefToDLDbIdentifier(defaultDict
);
1011 secinfo("secpref", "now we think the default keychain is %s", (mDefaultDLDbIdentifier
) ? mDefaultDLDbIdentifier
.dbName() : "<NULL>");
1015 // If defaultArray doesn't parse fall back on the default way of getting the default keychain
1016 defaultArray
= NULL
;
1022 // If the Panther style login keychain actually exists we use that otherwise no
1024 mDefaultDLDbIdentifier
= loginDLDbIdentifier();
1026 //Since we might be changing the keychain filename, we have to stat the right file. Delegate the knowledge of which files to StorageManager; DLDbListCFPref should contain "login.keychain".
1027 DLDbIdentifier actualIdentifier
= KeychainCore::StorageManager::mungeDLDbIdentifier(mDefaultDLDbIdentifier
, false);
1028 secinfo("secpref", "now we think the default keychain is: %s (actual: %s)",
1029 (mDefaultDLDbIdentifier
) ? mDefaultDLDbIdentifier
.dbName() : "Name doesn't exist",
1030 (actualIdentifier
) ? actualIdentifier
.dbName() : "Name doesn't exist");
1035 if (mDefaultDLDbIdentifier
.mImpl
!= NULL
&& actualIdentifier
.mImpl
!= NULL
)
1037 st_result
= stat(actualIdentifier
.dbName(), &st
);
1039 // Always claim that the system keychain exists for purposes of the search list
1040 if (st_result
&& 0 == strncmp(actualIdentifier
.dbName(), kSystemKeychainPath
, strlen(kSystemKeychainPath
))) {
1041 secnotice("secpref", "System keychain (%s) does not exist. Continuing as if it does...", actualIdentifier
.dbName());
1048 secinfo("secpref", "stat(%s) -> %d", actualIdentifier
.dbName(), st_result
);
1049 mDefaultDLDbIdentifier
= DLDbIdentifier(); // initialize a NULL keychain
1050 secinfo("secpref", "after DLDbIdentifier(), we think the default keychain is %s", static_cast<bool>(mDefaultDLDbIdentifier
) ? mDefaultDLDbIdentifier
.dbName() : "<NULL>");
1054 mDefaultDLDbIdentifierSet
= true;
1058 return mDefaultDLDbIdentifier
;
1062 DLDbListCFPref::loginDLDbIdentifier(const DLDbIdentifier
&dlDbIdentifier
)
1064 if (!(loginDLDbIdentifier() == dlDbIdentifier
))
1066 mLoginDLDbIdentifier
= dlDbIdentifier
;
1071 const DLDbIdentifier
&
1072 DLDbListCFPref::loginDLDbIdentifier()
1074 if (!mLoginDLDbIdentifierSet
)
1076 CFArrayRef loginArray
= reinterpret_cast<CFArrayRef
>(CFDictionaryGetValue(mPropertyList
, kLoginKeychainKey
));
1077 if (loginArray
&& CFGetTypeID(loginArray
) != CFArrayGetTypeID())
1080 if (loginArray
&& CFArrayGetCount(loginArray
) > 0)
1082 CFDictionaryRef loginDict
= reinterpret_cast<CFDictionaryRef
>(CFArrayGetValueAtIndex(loginArray
, 0));
1085 secinfo("secpref", "Getting login DLDbIdentifier from loginDict");
1086 mLoginDLDbIdentifier
= cfDictionaryRefToDLDbIdentifier(loginDict
);
1087 secinfo("secpref", "we think the login keychain is %s", static_cast<bool>(mLoginDLDbIdentifier
) ? mLoginDLDbIdentifier
.dbName() : "<NULL>");
1091 // If loginArray doesn't parse fall back on the default way of getting the login keychain.
1098 mLoginDLDbIdentifier
= LoginDLDbIdentifier();
1099 secinfo("secpref", "after LoginDLDbIdentifier(), we think the login keychain is %s", static_cast<bool>(mLoginDLDbIdentifier
) ? mLoginDLDbIdentifier
.dbName() : "<NULL>");
1102 mLoginDLDbIdentifierSet
= true;
1105 return mLoginDLDbIdentifier
;