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>
45 dispatch_once_t AppSandboxChecked
;
46 xpc_object_t KeychainHomeFromXPC
;
48 using namespace CssmClient
;
50 static const double kDLDbListCFPrefRevertInterval
= 30.0;
52 // normal debug calls, which get stubbed out for deployment builds
54 #define kKeyGUID CFSTR("GUID")
55 #define kKeySubserviceId CFSTR("SubserviceId")
56 #define kKeySubserviceType CFSTR("SubserviceType")
57 #define kKeyDbName CFSTR("DbName")
58 #define kKeyDbLocation CFSTR("DbLocation")
59 #define kKeyActive CFSTR("Active")
60 #define kKeyMajorVersion CFSTR("MajorVersion")
61 #define kKeyMinorVersion CFSTR("MinorVersion")
62 #define kDefaultDLDbListKey CFSTR("DLDBSearchList")
63 #define kDefaultKeychainKey CFSTR("DefaultKeychain")
64 #define kLoginKeychainKey CFSTR("LoginKeychain")
65 #define kUserDefaultPath "~/Library/Preferences/com.apple.security.plist"
66 #define kSystemDefaultPath "/Library/Preferences/com.apple.security.plist"
67 #define kCommonDefaultPath "/Library/Preferences/com.apple.security-common.plist"
68 #define kLoginKeychainPathPrefix "~/Library/Keychains/"
69 #define kUserLoginKeychainPath "~/Library/Keychains/login.keychain"
70 #define kSystemLoginKeychainPath "/Library/Keychains/System.keychain"
73 // A utility class for managing password database lookups
75 const time_t kPasswordCacheExpire
= 30; // number of seconds cached password db info is valid
77 PasswordDBLookup::PasswordDBLookup () : mValid (false), mCurrent (0), mTime (0)
81 void PasswordDBLookup::lookupInfoOnUID (uid_t uid
)
83 time_t currentTime
= time (NULL
);
85 if (!mValid
|| uid
!= mCurrent
|| currentTime
- mTime
>= kPasswordCacheExpire
)
87 struct passwd
* pw
= getpwuid(uid
);
90 UnixError::throwMe (EPERM
);
93 mDirectory
= pw
->pw_dir
;
99 secdebug("secpref", "uid=%d caching home=%s", uid
, pw
->pw_dir
);
105 PasswordDBLookup
*DLDbListCFPref::mPdbLookup
= NULL
;
107 //-------------------------------------------------------------------------------------
109 // Lists of DL/DBs, with CFPreferences backing store
111 //-------------------------------------------------------------------------------------
113 DLDbListCFPref::DLDbListCFPref(SecPreferencesDomain domain
) : mDomain(domain
), mPropertyList(NULL
), mChanged(false),
114 mSearchListSet(false), mDefaultDLDbIdentifierSet(false), mLoginDLDbIdentifierSet(false)
116 secdebug("secpref", "New DLDbListCFPref %p for domain %d", this, domain
);
117 loadPropertyList(true);
120 void DLDbListCFPref::set(SecPreferencesDomain domain
)
126 secdebug("secpref", "DLDbListCFPref %p domain set to %d", this, domain
);
128 if (loadPropertyList(true))
132 DLDbListCFPref::~DLDbListCFPref()
137 CFRelease(mPropertyList
);
141 DLDbListCFPref::forceUserSearchListReread()
143 // set mPrefsTimeStamp so that it will "expire" the next time loadPropertyList is called
144 mPrefsTimeStamp
= CFAbsoluteTimeGetCurrent() - kDLDbListCFPrefRevertInterval
;
148 DLDbListCFPref::loadPropertyList(bool force
)
154 case kSecPreferencesDomainUser
:
155 prefsPath
= ExpandTildesInPath(kUserDefaultPath
);
157 case kSecPreferencesDomainSystem
:
158 prefsPath
= kSystemDefaultPath
;
160 case kSecPreferencesDomainCommon
:
161 prefsPath
= kCommonDefaultPath
;
164 MacOSError::throwMe(errSecInvalidPrefsDomain
);
167 secdebug("secpref", "force=%s prefsPath=%s", force
? "true" : "false",
170 CFAbsoluteTime now
= CFAbsoluteTimeGetCurrent();
172 // If for some reason the prefs file path has changed, blow away the old plist and force an update
173 if (mPrefsPath
!= prefsPath
)
175 mPrefsPath
= prefsPath
;
178 CFRelease(mPropertyList
);
179 mPropertyList
= NULL
;
182 mPrefsTimeStamp
= now
;
186 if (now
- mPrefsTimeStamp
< kDLDbListCFPrefRevertInterval
)
189 mPrefsTimeStamp
= now
;
193 if (stat(mPrefsPath
.c_str(), &st
))
199 if (CFDictionaryGetCount(mPropertyList
) == 0)
201 CFRelease(mPropertyList
);
204 mPropertyList
= CFDictionaryCreateMutable(kCFAllocatorDefault
, 0, &kCFTypeDictionaryKeyCallBacks
, &kCFTypeDictionaryValueCallBacks
);
212 if (mTimespec
.tv_sec
== st
.st_mtimespec
.tv_sec
213 && mTimespec
.tv_nsec
== st
.st_mtimespec
.tv_nsec
)
217 mTimespec
= st
.st_mtimespec
;
220 CFMutableDictionaryRef thePropertyList
= NULL
;
221 CFMutableDataRef xmlData
= NULL
;
222 CFStringRef errorString
= NULL
;
227 fd
= open(mPrefsPath
.c_str(), O_RDONLY
, 0);
231 off_t theSize
= lseek(fd
, 0, SEEK_END
);
235 if (lseek(fd
, 0, SEEK_SET
))
238 xmlData
= CFDataCreateMutable(NULL
, CFIndex(theSize
));
241 CFDataSetLength(xmlData
, CFIndex(theSize
));
242 void *buffer
= reinterpret_cast<void *>(CFDataGetMutableBytePtr(xmlData
));
245 ssize_t bytesRead
= read(fd
, buffer
, (size_t)theSize
);
246 if (bytesRead
!= theSize
)
249 thePropertyList
= CFMutableDictionaryRef(CFPropertyListCreateFromXMLData(NULL
, xmlData
, kCFPropertyListMutableContainers
, &errorString
));
250 if (!thePropertyList
)
253 if (CFGetTypeID(thePropertyList
) != CFDictionaryGetTypeID())
255 CFRelease(thePropertyList
);
256 thePropertyList
= NULL
;
266 CFRelease(errorString
);
268 if (!thePropertyList
)
270 thePropertyList
= CFDictionaryCreateMutable(kCFAllocatorDefault
, 0, &kCFTypeDictionaryKeyCallBacks
, &kCFTypeDictionaryValueCallBacks
);
275 if (CFEqual(mPropertyList
, thePropertyList
))
277 // The new property list is the same as the old one, so nothing has changed.
278 CFRelease(thePropertyList
);
281 CFRelease(mPropertyList
);
284 mPropertyList
= thePropertyList
;
289 DLDbListCFPref::writePropertyList()
291 if (!mPropertyList
|| CFDictionaryGetCount(mPropertyList
) == 0)
293 // There is nothing in the mPropertyList dictionary,
294 // so we don't need a prefs file.
295 unlink(mPrefsPath
.c_str());
299 if(testAndFixPropertyList())
302 CFDataRef xmlData
= CFPropertyListCreateXMLData(NULL
, mPropertyList
);
304 return; // Bad out of memory or something evil happened let's act like CF and do nothing.
306 // The prefs file should at least be made readable by user/group/other and writable by the owner.
307 // Change from euid to ruid if needed for the duration of the new prefs file creat.
310 changeIdentity(UNPRIV
);
311 int fd
= open(mPrefsPath
.c_str(), O_WRONLY
|O_CREAT
|O_TRUNC
, mode
);
312 changeIdentity(PRIV
);
315 const void *buffer
= CFDataGetBytePtr(xmlData
);
316 size_t toWrite
= CFDataGetLength(xmlData
);
317 /* ssize_t bytesWritten = */ write(fd
, buffer
, toWrite
);
318 // Emulate CFPreferences by not checking for any errors.
323 mTimespec
= st
.st_mtimespec
;
331 mPrefsTimeStamp
= CFAbsoluteTimeGetCurrent();
334 // This function can clean up some problems caused by setuid clients. We've had instances where the
335 // Keychain search list has become owned by root, but is still able to be re-written by the user because
336 // of the permissions on the directory above. We'll take advantage of that fact to recreate the file with
337 // the correct ownership by copying it.
340 DLDbListCFPref::testAndFixPropertyList()
342 char *prefsPath
= (char *)mPrefsPath
.c_str();
344 int fd1
, fd2
, retval
;
347 if((fd1
= open(prefsPath
, O_RDONLY
)) < 0) {
348 if (errno
== ENOENT
) return 0; // Doesn't exist - the default case
352 if((retval
= fstat(fd1
, &stbuf
)) == -1) return -1;
354 if(stbuf
.st_uid
!= getuid()) {
355 char tempfile
[MAXPATHLEN
+1];
357 snprintf(tempfile
, MAXPATHLEN
, "%s.XXXXX", prefsPath
);
359 changeIdentity(UNPRIV
);
360 if((fd2
= open(tempfile
, O_RDWR
| O_CREAT
| O_EXCL
, 0666)) < 0) {
363 copyfile_state_t s
= copyfile_state_alloc();
364 retval
= fcopyfile(fd1
, fd2
, s
, COPYFILE_DATA
);
365 copyfile_state_free(s
);
366 if(!retval
) retval
= ::unlink(prefsPath
);
367 if(!retval
) retval
= ::rename(tempfile
, prefsPath
);
369 changeIdentity(PRIV
);
376 // Encapsulated process uid/gid change routine.
378 DLDbListCFPref::changeIdentity(ID_Direction toPriv
)
380 if(toPriv
== UNPRIV
) {
381 savedEUID
= geteuid();
382 savedEGID
= getegid();
383 if(savedEGID
!= getgid()) setegid(getgid());
384 if(savedEUID
!= getuid()) seteuid(getuid());
386 if(savedEUID
!= getuid()) seteuid(savedEUID
);
387 if(savedEGID
!= getgid()) setegid(savedEGID
);
392 DLDbListCFPref::resetCachedValues()
394 // Unset the login and default Keychain.
395 mLoginDLDbIdentifier
= mDefaultDLDbIdentifier
= DLDbIdentifier();
397 // Clear the searchList.
402 // Note that none of our cached values are valid
403 mSearchListSet
= mDefaultDLDbIdentifierSet
= mLoginDLDbIdentifierSet
= false;
405 mPrefsTimeStamp
= CFAbsoluteTimeGetCurrent();
408 void DLDbListCFPref::save()
413 // Resync from disc to make sure we don't clobber anyone elses changes.
414 // @@@ This is probably already done by the next layer up so we don't
415 // really need to do it here again.
416 loadPropertyList(true);
418 // Do the searchList first since it might end up invoking defaultDLDbIdentifier() which can set
419 // mLoginDLDbIdentifierSet and mDefaultDLDbIdentifierSet to true.
422 // Make a temporary CFArray with the contents of the vector
423 if (mSearchList
.size() == 1 && mSearchList
[0] == defaultDLDbIdentifier() && mSearchList
[0] == LoginDLDbIdentifier())
425 // The only element in the search list is the default keychain, which is a
426 // post Jaguar style login keychain, so omit the entry from the prefs file.
427 CFDictionaryRemoveValue(mPropertyList
, kDefaultDLDbListKey
);
431 CFMutableArrayRef searchArray
= CFArrayCreateMutable(kCFAllocatorDefault
, mSearchList
.size(), &kCFTypeArrayCallBacks
);
432 for (DLDbList::const_iterator ix
=mSearchList
.begin();ix
!=mSearchList
.end();ix
++)
434 CFDictionaryRef aDict
= dlDbIdentifierToCFDictionaryRef(*ix
);
435 CFArrayAppendValue(searchArray
, aDict
);
439 CFDictionarySetValue(mPropertyList
, kDefaultDLDbListKey
, searchArray
);
440 CFRelease(searchArray
);
444 if (mLoginDLDbIdentifierSet
)
446 // Make a temporary CFArray with the login keychain
447 CFArrayRef loginArray
= NULL
;
448 if (!mLoginDLDbIdentifier
)
450 loginArray
= CFArrayCreate(kCFAllocatorDefault
, NULL
, 0, &kCFTypeArrayCallBacks
);
452 else if (!(mLoginDLDbIdentifier
== LoginDLDbIdentifier()))
454 CFDictionaryRef aDict
= dlDbIdentifierToCFDictionaryRef(mLoginDLDbIdentifier
);
455 const void *value
= reinterpret_cast<const void *>(aDict
);
456 loginArray
= CFArrayCreate(kCFAllocatorDefault
, &value
, 1, &kCFTypeArrayCallBacks
);
462 CFDictionarySetValue(mPropertyList
, kLoginKeychainKey
, loginArray
);
463 CFRelease(loginArray
);
466 CFDictionaryRemoveValue(mPropertyList
, kLoginKeychainKey
);
469 if (mDefaultDLDbIdentifierSet
)
471 // Make a temporary CFArray with the default keychain
472 CFArrayRef defaultArray
= NULL
;
473 if (!mDefaultDLDbIdentifier
)
475 defaultArray
= CFArrayCreate(kCFAllocatorDefault
, NULL
, 0, &kCFTypeArrayCallBacks
);
477 else if (!(mDefaultDLDbIdentifier
== LoginDLDbIdentifier()))
479 CFDictionaryRef aDict
= dlDbIdentifierToCFDictionaryRef(mDefaultDLDbIdentifier
);
480 const void *value
= reinterpret_cast<const void *>(aDict
);
481 defaultArray
= CFArrayCreate(kCFAllocatorDefault
, &value
, 1, &kCFTypeArrayCallBacks
);
487 CFDictionarySetValue(mPropertyList
, kDefaultKeychainKey
, defaultArray
);
488 CFRelease(defaultArray
);
491 CFDictionaryRemoveValue(mPropertyList
, kDefaultKeychainKey
);
499 //----------------------------------------------------------------------
501 //----------------------------------------------------------------------
503 DLDbIdentifier
DLDbListCFPref::LoginDLDbIdentifier()
505 CSSM_VERSION theVersion
={};
506 CssmSubserviceUid
ssuid(gGuidAppleCSPDL
,&theVersion
,0,CSSM_SERVICE_DL
|CSSM_SERVICE_CSP
);
507 CssmNetAddress
*dbLocation
=NULL
;
510 case kSecPreferencesDomainUser
:
511 return DLDbIdentifier(ssuid
, ExpandTildesInPath(kUserLoginKeychainPath
).c_str(), dbLocation
);
514 case kSecPreferencesDomainSystem
:
515 case kSecPreferencesDomainCommon
:
516 return DLDbIdentifier(ssuid
, kSystemLoginKeychainPath
, dbLocation
);
520 DLDbIdentifier
DLDbListCFPref::JaguarLoginDLDbIdentifier()
522 CSSM_VERSION theVersion
={};
523 CssmSubserviceUid
ssuid(gGuidAppleCSPDL
,&theVersion
,0,CSSM_SERVICE_DL
|CSSM_SERVICE_CSP
);
524 CssmNetAddress
*dbLocation
=NULL
;
527 case kSecPreferencesDomainUser
:
529 string basepath
= ExpandTildesInPath(kLoginKeychainPathPrefix
) + getPwInfo(kUsername
);
530 return DLDbIdentifier(ssuid
,basepath
.c_str(),dbLocation
);
532 case kSecPreferencesDomainSystem
:
533 case kSecPreferencesDomainCommon
:
534 return DLDbIdentifier(ssuid
, kSystemLoginKeychainPath
, dbLocation
);
537 return DLDbIdentifier();
541 DLDbIdentifier
DLDbListCFPref::makeDLDbIdentifier (const CSSM_GUID
&guid
, const CSSM_VERSION
&version
,
542 uint32 subserviceId
, CSSM_SERVICE_TYPE subserviceType
,
543 const char* dbName
, CSSM_NET_ADDRESS
*dbLocation
)
545 CssmSubserviceUid
ssuid (guid
, &version
, subserviceId
, subserviceType
);
546 return DLDbIdentifier (ssuid
, ExpandTildesInPath (dbName
).c_str (), dbLocation
);
549 DLDbIdentifier
DLDbListCFPref::cfDictionaryRefToDLDbIdentifier(CFDictionaryRef theDict
)
551 // We must get individual values from the dictionary and store in basic types
552 if (CFGetTypeID(theDict
) != CFDictionaryGetTypeID())
553 throw std::logic_error("wrong type in property list");
556 CCFValue
vGuid(::CFDictionaryGetValue(theDict
,kKeyGUID
));
557 string guidStr
=vGuid
;
558 const Guid
guid(guidStr
.c_str());
561 CSSM_VERSION theVersion
={0,};
562 CCFValue
vMajor(::CFDictionaryGetValue(theDict
,kKeyMajorVersion
));
563 theVersion
.Major
= vMajor
;
564 CCFValue
vMinor(::CFDictionaryGetValue(theDict
,kKeyMinorVersion
));
565 theVersion
.Minor
= vMinor
;
568 CCFValue
vSsid(::CFDictionaryGetValue(theDict
,kKeySubserviceId
));
569 uint32 subserviceId
=sint32(vSsid
);
572 CSSM_SERVICE_TYPE subserviceType
=CSSM_SERVICE_DL
;
573 CCFValue
vSsType(::CFDictionaryGetValue(theDict
,kKeySubserviceType
));
574 subserviceType
=vSsType
;
576 // Get DbName from dictionary
577 CCFValue
vDbName(::CFDictionaryGetValue(theDict
,kKeyDbName
));
578 string dbName
=vDbName
;
580 // jch Get DbLocation from dictionary
581 CssmNetAddress
*dbLocation
=NULL
;
583 return makeDLDbIdentifier (guid
, theVersion
, subserviceId
, subserviceType
, dbName
.c_str (), dbLocation
);
586 void DLDbListCFPref::clearPWInfo ()
588 if (mPdbLookup
!= NULL
)
595 string
DLDbListCFPref::getPwInfo(PwInfoType type
)
601 if (KeychainHomeFromXPC
) {
602 value
= xpc_string_get_string_ptr(KeychainHomeFromXPC
);
604 value
= getenv("HOME");
610 value
= getenv("USER");
616 // Get our effective uid
617 uid_t uid
= geteuid();
618 // If we are setuid root use the real uid instead
619 if (!uid
) uid
= getuid();
621 // get the password entries
622 if (mPdbLookup
== NULL
)
624 mPdbLookup
= new PasswordDBLookup ();
627 mPdbLookup
->lookupInfoOnUID (uid
);
633 result
= mPdbLookup
->getDirectory ();
636 result
= mPdbLookup
->getName ();
643 static void check_app_sandbox()
645 if (!_xpc_runtime_is_app_sandboxed()) {
646 // We are not in a sandbox, no work to do here
650 extern xpc_object_t
xpc_create_with_format(const char * format
, ...);
651 xpc_connection_t con
= xpc_connection_create("com.apple.security.XPCKeychainSandboxCheck", NULL
);
652 xpc_connection_set_event_handler(con
, ^(xpc_object_t event
) {
653 xpc_type_t xtype
= xpc_get_type(event
);
654 if (XPC_TYPE_ERROR
== xtype
) {
655 syslog(LOG_ERR
, "Keychain sandbox connection error: %s\n", xpc_dictionary_get_string(event
, XPC_ERROR_KEY_DESCRIPTION
));
657 syslog(LOG_ERR
, "Keychain sandbox unexpected connection event %p\n", event
);
660 xpc_connection_resume(con
);
662 xpc_object_t message
= xpc_create_with_format("{op: GrantKeychainPaths}");
663 xpc_object_t reply
= xpc_connection_send_message_with_reply_sync(con
, message
);
664 xpc_type_t xtype
= xpc_get_type(reply
);
665 if (XPC_TYPE_DICTIONARY
== xtype
) {
667 // This is useful for debugging.
668 char *debug
= xpc_copy_description(reply
);
669 syslog(LOG_ERR
, "DEBUG (KCsandbox) %s\n", debug
);
673 xpc_object_t extensions_array
= xpc_dictionary_get_value(reply
, "extensions");
674 xpc_array_apply(extensions_array
, ^(size_t index
, xpc_object_t extension
) {
675 char pbuf
[MAXPATHLEN
];
677 int status
= sandbox_consume_fs_extension(xpc_string_get_string_ptr(extension
), &path
);
679 syslog(LOG_ERR
, "Keychain sandbox consume extension error: s=%d p=%s %m\n", status
, path
);
681 status
= sandbox_release_fs_extension(xpc_string_get_string_ptr(extension
));
683 syslog(LOG_ERR
, "Keychain sandbox release extension error: s=%d p=%s %m\n", status
, path
);
689 KeychainHomeFromXPC
= xpc_dictionary_get_value(reply
, "keychain-home");
690 xpc_retain(KeychainHomeFromXPC
);
692 } else if (XPC_TYPE_ERROR
== xtype
) {
693 syslog(LOG_ERR
, "Keychain sandbox message error: %s\n", xpc_dictionary_get_string(reply
, XPC_ERROR_KEY_DESCRIPTION
));
695 syslog(LOG_ERR
, "Keychain sandbox unexpected message reply type %p\n", xtype
);
697 xpc_release(message
);
703 string
DLDbListCFPref::ExpandTildesInPath(const string
&inPath
)
705 dispatch_once(&AppSandboxChecked
, ^{
709 if ((short)inPath
.find("~/",0,2) == 0)
710 return getPwInfo(kHomeDir
) + inPath
.substr(1, inPath
.length() - 1);
715 string
DLDbListCFPref::StripPathStuff(const string
&inPath
)
717 if (inPath
.find("/private/var/automount/Network/",0,31) == 0)
718 return inPath
.substr(22);
719 if (inPath
.find("/private/automount/Servers/",0,27) == 0)
720 return "/Network" + inPath
.substr(18);
721 if (inPath
.find("/automount/Servers/",0,19) == 0)
722 return "/Network" + inPath
.substr(10);
723 if (inPath
.find("/private/automount/Network/",0,27) == 0)
724 return inPath
.substr(18);
725 if (inPath
.find("/automount/Network/",0,19) == 0)
726 return inPath
.substr(10);
727 if (inPath
.find("/private/Network/",0,17) == 0)
728 return inPath
.substr(8);
732 string
DLDbListCFPref::AbbreviatedPath(const string
&inPath
)
734 string path
= StripPathStuff(inPath
);
735 string home
= StripPathStuff(getPwInfo(kHomeDir
) + "/");
736 size_t homeLen
= home
.length();
738 if (homeLen
> 1 && path
.find(home
.c_str(), 0, homeLen
) == 0)
739 return "~" + path
.substr(homeLen
- 1);
744 CFDictionaryRef
DLDbListCFPref::dlDbIdentifierToCFDictionaryRef(const DLDbIdentifier
& dldbIdentifier
)
746 CFRef
<CFMutableDictionaryRef
> aDict(CFDictionaryCreateMutable(kCFAllocatorDefault
,0,
747 &kCFTypeDictionaryKeyCallBacks
,&kCFTypeDictionaryValueCallBacks
));
749 throw ::std::bad_alloc();
751 // Put SUBSERVICE_UID in dictionary
752 char buffer
[Guid::stringRepLength
+1];
753 const CssmSubserviceUid
& ssuid
=dldbIdentifier
.ssuid();
754 const Guid
&theGuid
= Guid::overlay(ssuid
.Guid
);
755 CFRef
<CFStringRef
> stringGuid(::CFStringCreateWithCString(kCFAllocatorDefault
,
756 theGuid
.toString(buffer
),kCFStringEncodingMacRoman
));
758 ::CFDictionarySetValue(aDict
,kKeyGUID
,stringGuid
);
760 if (ssuid
.SubserviceId
!=0)
762 CFRef
<CFNumberRef
> subserviceId(::CFNumberCreate(kCFAllocatorDefault
,kCFNumberSInt32Type
,&ssuid
.SubserviceId
));
764 ::CFDictionarySetValue(aDict
,kKeySubserviceId
,subserviceId
);
766 if (ssuid
.SubserviceType
!=0)
768 CFRef
<CFNumberRef
> subserviceType(CFNumberCreate(kCFAllocatorDefault
,kCFNumberSInt32Type
,&ssuid
.SubserviceType
));
770 ::CFDictionarySetValue(aDict
,kKeySubserviceType
,subserviceType
);
772 if (ssuid
.Version
.Major
!=0 && ssuid
.Version
.Minor
!=0)
774 CFRef
<CFNumberRef
> majorVersion(::CFNumberCreate(kCFAllocatorDefault
,kCFNumberSInt32Type
,&ssuid
.Version
.Major
));
776 ::CFDictionarySetValue(aDict
,kKeyMajorVersion
,majorVersion
);
777 CFRef
<CFNumberRef
> minorVersion(::CFNumberCreate(kCFAllocatorDefault
,kCFNumberSInt32Type
,&ssuid
.Version
.Minor
));
779 ::CFDictionarySetValue(aDict
,kKeyMinorVersion
,minorVersion
);
782 // Put DbName in dictionary
783 const char *dbName
=dldbIdentifier
.dbName();
786 CFRef
<CFStringRef
> theDbName(::CFStringCreateWithCString(kCFAllocatorDefault
,AbbreviatedPath(dbName
).c_str(),kCFStringEncodingUTF8
));
787 ::CFDictionarySetValue(aDict
,kKeyDbName
,theDbName
);
789 // Put DbLocation in dictionary
790 const CSSM_NET_ADDRESS
*dbLocation
=dldbIdentifier
.dbLocation();
791 if (dbLocation
!=NULL
&& dbLocation
->AddressType
!=CSSM_ADDR_NONE
)
793 CFRef
<CFDataRef
> theData(::CFDataCreate(kCFAllocatorDefault
,dbLocation
->Address
.Data
,dbLocation
->Address
.Length
));
795 ::CFDictionarySetValue(aDict
,kKeyDbLocation
,theData
);
802 bool DLDbListCFPref::revert(bool force
)
804 // If the prefs have not been refreshed in the last kDLDbListCFPrefRevertInterval
805 // seconds or we are asked to force a reload, then reload.
806 if (!loadPropertyList(force
))
814 DLDbListCFPref::add(const DLDbIdentifier
&dldbIdentifier
)
816 // convert the location specified in dldbIdentifier to a standard form
817 // make a canonical form of the database name
818 std::string canon
= ExpandTildesInPath(AbbreviatedPath(dldbIdentifier
.dbName()).c_str());
820 DLDbIdentifier
localIdentifier (dldbIdentifier
.ssuid(), canon
.c_str(), dldbIdentifier
.dbLocation ());
822 if (member(localIdentifier
))
825 mSearchList
.push_back(localIdentifier
);
830 DLDbListCFPref::remove(const DLDbIdentifier
&dldbIdentifier
)
832 // Make sure mSearchList is set
834 for (vector
<DLDbIdentifier
>::iterator ix
= mSearchList
.begin(); ix
!= mSearchList
.end(); ++ix
)
836 if (*ix
==dldbIdentifier
) // found in list
838 mSearchList
.erase(ix
);
846 DLDbListCFPref::rename(const DLDbIdentifier
&oldId
, const DLDbIdentifier
&newId
)
848 // Make sure mSearchList is set
850 for (vector
<DLDbIdentifier
>::iterator ix
= mSearchList
.begin();
851 ix
!= mSearchList
.end(); ++ix
)
855 // replace oldId with newId
861 // remove newId except where we just inserted it
862 mSearchList
.erase(ix
);
869 DLDbListCFPref::member(const DLDbIdentifier
&dldbIdentifier
)
871 if (dldbIdentifier
.IsImplEmpty())
876 for (vector
<DLDbIdentifier
>::const_iterator ix
= searchList().begin(); ix
!= mSearchList
.end(); ++ix
)
878 if (ix
->mImpl
== NULL
)
883 // compare the dldbIdentifiers based on the full, real path to the keychain
884 if (ix
->ssuid() == dldbIdentifier
.ssuid())
886 char localPath
[PATH_MAX
],
889 // try to resolve these down to a canonical form
890 const char* localPathPtr
= cached_realpath(ix
->dbName(), localPath
);
891 const char* inPathPtr
= cached_realpath(dldbIdentifier
.dbName(), inPath
);
893 // if either of the paths didn't resolve for some reason, use the originals
894 if (localPathPtr
== NULL
)
896 localPathPtr
= ix
->dbName();
899 if (inPathPtr
== NULL
)
901 inPathPtr
= dldbIdentifier
.dbName();
904 if (strcmp(localPathPtr
, inPathPtr
) == 0)
914 const vector
<DLDbIdentifier
> &
915 DLDbListCFPref::searchList()
919 CFArrayRef searchList
= reinterpret_cast<CFArrayRef
>(CFDictionaryGetValue(mPropertyList
, kDefaultDLDbListKey
));
920 if (searchList
&& CFGetTypeID(searchList
) != CFArrayGetTypeID())
925 CFIndex top
= CFArrayGetCount(searchList
);
926 // Each entry is a CFDictionary; peel it off & add it to the array
927 for (CFIndex idx
= 0; idx
< top
; ++idx
)
929 CFDictionaryRef theDict
= reinterpret_cast<CFDictionaryRef
>(CFArrayGetValueAtIndex(searchList
, idx
));
932 mSearchList
.push_back(cfDictionaryRefToDLDbIdentifier(theDict
));
936 // Drop stuff that doesn't parse on the floor.
940 // If there were entries specified, but they were invalid revert to using the
941 // default keychain in the searchlist.
942 if (top
> 0 && mSearchList
.size() == 0)
946 // The default when no search list is specified is to only search the
948 if (!searchList
&& static_cast<bool>(defaultDLDbIdentifier()))
949 mSearchList
.push_back(mDefaultDLDbIdentifier
);
951 mSearchListSet
= true;
958 DLDbListCFPref::searchList(const vector
<DLDbIdentifier
> &searchList
)
960 vector
<DLDbIdentifier
> newList(searchList
);
961 mSearchList
.swap(newList
);
962 mSearchListSet
= true;
967 DLDbListCFPref::defaultDLDbIdentifier(const DLDbIdentifier
&dlDbIdentifier
)
969 if (!(defaultDLDbIdentifier() == dlDbIdentifier
))
971 mDefaultDLDbIdentifier
= dlDbIdentifier
;
976 const DLDbIdentifier
&
977 DLDbListCFPref::defaultDLDbIdentifier()
980 if (!mDefaultDLDbIdentifierSet
)
982 CFArrayRef defaultArray
= reinterpret_cast<CFArrayRef
>(CFDictionaryGetValue(mPropertyList
, kDefaultKeychainKey
));
983 if (defaultArray
&& CFGetTypeID(defaultArray
) != CFArrayGetTypeID())
986 if (defaultArray
&& CFArrayGetCount(defaultArray
) > 0)
988 CFDictionaryRef defaultDict
= reinterpret_cast<CFDictionaryRef
>(CFArrayGetValueAtIndex(defaultArray
, 0));
991 secdebug("secpref", "getting default DLDbIdentifier from defaultDict");
992 mDefaultDLDbIdentifier
= cfDictionaryRefToDLDbIdentifier(defaultDict
);
993 secdebug("secpref", "now we think the default keychain is %s", (mDefaultDLDbIdentifier
) ? mDefaultDLDbIdentifier
.dbName() : "<NULL>");
997 // If defaultArray doesn't parse fall back on the default way of getting the default keychain
1005 // If the Panther style login keychain actually exists we use that otherwise no
1007 mDefaultDLDbIdentifier
= loginDLDbIdentifier();
1008 secdebug("secpref", "now we think the default keychain is: %s", (mDefaultDLDbIdentifier
) ? mDefaultDLDbIdentifier
.dbName() :
1009 "Name doesn't exist");
1014 if (mDefaultDLDbIdentifier
.mImpl
!= NULL
)
1016 st_result
= stat(mDefaultDLDbIdentifier
.dbName(), &st
);
1021 secdebug("secpref", "stat(%s) -> %d", mDefaultDLDbIdentifier
.dbName(), st_result
);
1022 mDefaultDLDbIdentifier
= DLDbIdentifier(); // initialize a NULL keychain
1023 secdebug("secpref", "after DLDbIdentifier(), we think the default keychain is %s", static_cast<bool>(mDefaultDLDbIdentifier
) ? mDefaultDLDbIdentifier
.dbName() : "<NULL>");
1027 mDefaultDLDbIdentifierSet
= true;
1031 return mDefaultDLDbIdentifier
;
1035 DLDbListCFPref::loginDLDbIdentifier(const DLDbIdentifier
&dlDbIdentifier
)
1037 if (!(loginDLDbIdentifier() == dlDbIdentifier
))
1039 mLoginDLDbIdentifier
= dlDbIdentifier
;
1044 const DLDbIdentifier
&
1045 DLDbListCFPref::loginDLDbIdentifier()
1047 if (!mLoginDLDbIdentifierSet
)
1049 CFArrayRef loginArray
= reinterpret_cast<CFArrayRef
>(CFDictionaryGetValue(mPropertyList
, kLoginKeychainKey
));
1050 if (loginArray
&& CFGetTypeID(loginArray
) != CFArrayGetTypeID())
1053 if (loginArray
&& CFArrayGetCount(loginArray
) > 0)
1055 CFDictionaryRef loginDict
= reinterpret_cast<CFDictionaryRef
>(CFArrayGetValueAtIndex(loginArray
, 0));
1058 secdebug("secpref", "Getting login DLDbIdentifier from loginDict");
1059 mLoginDLDbIdentifier
= cfDictionaryRefToDLDbIdentifier(loginDict
);
1060 secdebug("secpref", "we think the login keychain is %s", static_cast<bool>(mLoginDLDbIdentifier
) ? mLoginDLDbIdentifier
.dbName() : "<NULL>");
1064 // If loginArray doesn't parse fall back on the default way of getting the login keychain.
1071 mLoginDLDbIdentifier
= LoginDLDbIdentifier();
1072 secdebug("secpref", "after LoginDLDbIdentifier(), we think the login keychain is %s", static_cast<bool>(mLoginDLDbIdentifier
) ? mLoginDLDbIdentifier
.dbName() : "<NULL>");
1075 mLoginDLDbIdentifierSet
= true;
1078 return mLoginDLDbIdentifier
;