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()
421 // Resync from disc to make sure we don't clobber anyone elses changes.
422 // @@@ This is probably already done by the next layer up so we don't
423 // really need to do it here again.
424 loadPropertyList(true);
426 // Do the searchList first since it might end up invoking defaultDLDbIdentifier() which can set
427 // mLoginDLDbIdentifierSet and mDefaultDLDbIdentifierSet to true.
430 // Make a temporary CFArray with the contents of the vector
431 if (mSearchList
.size() == 1 && mSearchList
[0] == defaultDLDbIdentifier() && mSearchList
[0] == LoginDLDbIdentifier())
433 // The only element in the search list is the default keychain, which is a
434 // post Jaguar style login keychain, so omit the entry from the prefs file.
435 CFDictionaryRemoveValue(mPropertyList
, kDefaultDLDbListKey
);
439 CFMutableArrayRef searchArray
= CFArrayCreateMutable(kCFAllocatorDefault
, mSearchList
.size(), &kCFTypeArrayCallBacks
);
440 for (DLDbList::const_iterator ix
=mSearchList
.begin();ix
!=mSearchList
.end();ix
++)
442 CFDictionaryRef aDict
= dlDbIdentifierToCFDictionaryRef(*ix
);
443 CFArrayAppendValue(searchArray
, aDict
);
447 CFDictionarySetValue(mPropertyList
, kDefaultDLDbListKey
, searchArray
);
448 CFRelease(searchArray
);
452 if (mLoginDLDbIdentifierSet
)
454 // Make a temporary CFArray with the login keychain
455 CFArrayRef loginArray
= NULL
;
456 if (!mLoginDLDbIdentifier
)
458 loginArray
= CFArrayCreate(kCFAllocatorDefault
, NULL
, 0, &kCFTypeArrayCallBacks
);
460 else if (!(mLoginDLDbIdentifier
== LoginDLDbIdentifier()))
462 CFDictionaryRef aDict
= dlDbIdentifierToCFDictionaryRef(mLoginDLDbIdentifier
);
463 const void *value
= reinterpret_cast<const void *>(aDict
);
464 loginArray
= CFArrayCreate(kCFAllocatorDefault
, &value
, 1, &kCFTypeArrayCallBacks
);
470 CFDictionarySetValue(mPropertyList
, kLoginKeychainKey
, loginArray
);
471 CFRelease(loginArray
);
474 CFDictionaryRemoveValue(mPropertyList
, kLoginKeychainKey
);
477 if (mDefaultDLDbIdentifierSet
)
479 // Make a temporary CFArray with the default keychain
480 CFArrayRef defaultArray
= NULL
;
481 if (!mDefaultDLDbIdentifier
)
483 defaultArray
= CFArrayCreate(kCFAllocatorDefault
, NULL
, 0, &kCFTypeArrayCallBacks
);
485 else if (!(mDefaultDLDbIdentifier
== LoginDLDbIdentifier()))
487 CFDictionaryRef aDict
= dlDbIdentifierToCFDictionaryRef(mDefaultDLDbIdentifier
);
488 const void *value
= reinterpret_cast<const void *>(aDict
);
489 defaultArray
= CFArrayCreate(kCFAllocatorDefault
, &value
, 1, &kCFTypeArrayCallBacks
);
495 CFDictionarySetValue(mPropertyList
, kDefaultKeychainKey
, defaultArray
);
496 CFRelease(defaultArray
);
499 CFDictionaryRemoveValue(mPropertyList
, kDefaultKeychainKey
);
507 //----------------------------------------------------------------------
509 //----------------------------------------------------------------------
511 DLDbIdentifier
DLDbListCFPref::LoginDLDbIdentifier()
513 CSSM_VERSION theVersion
={};
514 CssmSubserviceUid
ssuid(gGuidAppleCSPDL
,&theVersion
,0,CSSM_SERVICE_DL
|CSSM_SERVICE_CSP
);
515 CssmNetAddress
*dbLocation
=NULL
;
518 case kSecPreferencesDomainUser
:
519 return DLDbIdentifier(ssuid
, ExpandTildesInPath(kUserLoginKeychainPath
).c_str(), dbLocation
);
522 case kSecPreferencesDomainSystem
:
523 case kSecPreferencesDomainCommon
:
524 return DLDbIdentifier(ssuid
, kSystemLoginKeychainPath
, dbLocation
);
528 DLDbIdentifier
DLDbListCFPref::JaguarLoginDLDbIdentifier()
530 CSSM_VERSION theVersion
={};
531 CssmSubserviceUid
ssuid(gGuidAppleCSPDL
,&theVersion
,0,CSSM_SERVICE_DL
|CSSM_SERVICE_CSP
);
532 CssmNetAddress
*dbLocation
=NULL
;
535 case kSecPreferencesDomainUser
:
537 string basepath
= ExpandTildesInPath(kLoginKeychainPathPrefix
) + getPwInfo(kUsername
);
538 return DLDbIdentifier(ssuid
,basepath
.c_str(),dbLocation
);
540 case kSecPreferencesDomainSystem
:
541 case kSecPreferencesDomainCommon
:
542 return DLDbIdentifier(ssuid
, kSystemLoginKeychainPath
, dbLocation
);
545 return DLDbIdentifier();
549 DLDbIdentifier
DLDbListCFPref::makeDLDbIdentifier (const CSSM_GUID
&guid
, const CSSM_VERSION
&version
,
550 uint32 subserviceId
, CSSM_SERVICE_TYPE subserviceType
,
551 const char* dbName
, CSSM_NET_ADDRESS
*dbLocation
)
553 CssmSubserviceUid
ssuid (guid
, &version
, subserviceId
, subserviceType
);
554 return DLDbIdentifier (ssuid
, ExpandTildesInPath (dbName
).c_str (), dbLocation
);
557 DLDbIdentifier
DLDbListCFPref::cfDictionaryRefToDLDbIdentifier(CFDictionaryRef theDict
)
559 // We must get individual values from the dictionary and store in basic types
560 if (CFGetTypeID(theDict
) != CFDictionaryGetTypeID())
561 throw std::logic_error("wrong type in property list");
564 CCFValue
vGuid(::CFDictionaryGetValue(theDict
,kKeyGUID
));
565 string guidStr
=vGuid
;
566 const Guid
guid(guidStr
.c_str());
569 CSSM_VERSION theVersion
={0,};
570 CCFValue
vMajor(::CFDictionaryGetValue(theDict
,kKeyMajorVersion
));
571 theVersion
.Major
= vMajor
;
572 CCFValue
vMinor(::CFDictionaryGetValue(theDict
,kKeyMinorVersion
));
573 theVersion
.Minor
= vMinor
;
576 CCFValue
vSsid(::CFDictionaryGetValue(theDict
,kKeySubserviceId
));
577 uint32 subserviceId
=sint32(vSsid
);
580 CSSM_SERVICE_TYPE subserviceType
=CSSM_SERVICE_DL
;
581 CCFValue
vSsType(::CFDictionaryGetValue(theDict
,kKeySubserviceType
));
582 subserviceType
=vSsType
;
584 // Get DbName from dictionary
585 CCFValue
vDbName(::CFDictionaryGetValue(theDict
,kKeyDbName
));
586 string dbName
=vDbName
;
588 // jch Get DbLocation from dictionary
589 CssmNetAddress
*dbLocation
=NULL
;
591 return makeDLDbIdentifier (guid
, theVersion
, subserviceId
, subserviceType
, dbName
.c_str (), dbLocation
);
594 void DLDbListCFPref::clearPWInfo ()
596 if (mPdbLookup
!= NULL
)
603 string
DLDbListCFPref::getPwInfo(PwInfoType type
)
609 if (KeychainHomeFromXPC
) {
610 value
= xpc_string_get_string_ptr(KeychainHomeFromXPC
);
612 value
= getenv("HOME");
618 value
= getenv("USER");
624 // Get our effective uid
625 uid_t uid
= geteuid();
626 // If we are setuid root use the real uid instead
627 if (!uid
) uid
= getuid();
629 // get the password entries
630 if (mPdbLookup
== NULL
)
632 mPdbLookup
= new PasswordDBLookup ();
635 mPdbLookup
->lookupInfoOnUID (uid
);
641 result
= mPdbLookup
->getDirectory ();
644 result
= mPdbLookup
->getName ();
651 static void check_app_sandbox()
653 if (!_xpc_runtime_is_app_sandboxed()) {
654 // We are not in a sandbox, no work to do here
658 extern xpc_object_t
xpc_create_with_format(const char * format
, ...);
659 xpc_connection_t con
= xpc_connection_create("com.apple.security.XPCKeychainSandboxCheck", NULL
);
660 xpc_connection_set_event_handler(con
, ^(xpc_object_t event
) {
661 xpc_type_t xtype
= xpc_get_type(event
);
662 if (XPC_TYPE_ERROR
== xtype
) {
663 syslog(LOG_ERR
, "Keychain sandbox connection error: %s\n", xpc_dictionary_get_string(event
, XPC_ERROR_KEY_DESCRIPTION
));
665 syslog(LOG_ERR
, "Keychain sandbox unexpected connection event %p\n", event
);
668 xpc_connection_resume(con
);
670 xpc_object_t message
= xpc_create_with_format("{op: GrantKeychainPaths}");
671 xpc_object_t reply
= xpc_connection_send_message_with_reply_sync(con
, message
);
672 xpc_type_t xtype
= xpc_get_type(reply
);
673 if (XPC_TYPE_DICTIONARY
== xtype
) {
675 // This is useful for debugging.
676 char *debug
= xpc_copy_description(reply
);
677 syslog(LOG_ERR
, "DEBUG (KCsandbox) %s\n", debug
);
681 xpc_object_t extensions_array
= xpc_dictionary_get_value(reply
, "extensions");
682 xpc_array_apply(extensions_array
, ^(size_t index
, xpc_object_t extension
) {
683 char pbuf
[MAXPATHLEN
];
685 int status
= sandbox_consume_fs_extension(xpc_string_get_string_ptr(extension
), &path
);
687 syslog(LOG_ERR
, "Keychain sandbox consume extension error: s=%d p=%s %m\n", status
, path
);
689 status
= sandbox_release_fs_extension(xpc_string_get_string_ptr(extension
));
691 syslog(LOG_ERR
, "Keychain sandbox release extension error: s=%d p=%s %m\n", status
, path
);
697 KeychainHomeFromXPC
= xpc_dictionary_get_value(reply
, "keychain-home");
698 xpc_retain(KeychainHomeFromXPC
);
700 } else if (XPC_TYPE_ERROR
== xtype
) {
701 syslog(LOG_ERR
, "Keychain sandbox message error: %s\n", xpc_dictionary_get_string(reply
, XPC_ERROR_KEY_DESCRIPTION
));
703 syslog(LOG_ERR
, "Keychain sandbox unexpected message reply type %p\n", xtype
);
705 xpc_release(message
);
711 string
DLDbListCFPref::ExpandTildesInPath(const string
&inPath
)
713 dispatch_once(&AppSandboxChecked
, ^{
717 if ((short)inPath
.find("~/",0,2) == 0)
718 return getPwInfo(kHomeDir
) + inPath
.substr(1, inPath
.length() - 1);
723 string
DLDbListCFPref::StripPathStuff(const string
&inPath
)
725 if (inPath
.find("/private/var/automount/Network/",0,31) == 0)
726 return inPath
.substr(22);
727 if (inPath
.find("/private/automount/Servers/",0,27) == 0)
728 return "/Network" + inPath
.substr(18);
729 if (inPath
.find("/automount/Servers/",0,19) == 0)
730 return "/Network" + inPath
.substr(10);
731 if (inPath
.find("/private/automount/Network/",0,27) == 0)
732 return inPath
.substr(18);
733 if (inPath
.find("/automount/Network/",0,19) == 0)
734 return inPath
.substr(10);
735 if (inPath
.find("/private/Network/",0,17) == 0)
736 return inPath
.substr(8);
740 string
DLDbListCFPref::AbbreviatedPath(const string
&inPath
)
742 string path
= StripPathStuff(inPath
);
743 string home
= StripPathStuff(getPwInfo(kHomeDir
) + "/");
744 size_t homeLen
= home
.length();
746 if (homeLen
> 1 && path
.find(home
.c_str(), 0, homeLen
) == 0)
747 return "~" + path
.substr(homeLen
- 1);
752 CFDictionaryRef
DLDbListCFPref::dlDbIdentifierToCFDictionaryRef(const DLDbIdentifier
& dldbIdentifier
)
754 CFRef
<CFMutableDictionaryRef
> aDict(CFDictionaryCreateMutable(kCFAllocatorDefault
,0,
755 &kCFTypeDictionaryKeyCallBacks
,&kCFTypeDictionaryValueCallBacks
));
757 throw ::std::bad_alloc();
759 // Put SUBSERVICE_UID in dictionary
760 char buffer
[Guid::stringRepLength
+1];
761 const CssmSubserviceUid
& ssuid
=dldbIdentifier
.ssuid();
762 const Guid
&theGuid
= Guid::overlay(ssuid
.Guid
);
763 CFRef
<CFStringRef
> stringGuid(::CFStringCreateWithCString(kCFAllocatorDefault
,
764 theGuid
.toString(buffer
),kCFStringEncodingMacRoman
));
766 ::CFDictionarySetValue(aDict
,kKeyGUID
,stringGuid
);
768 if (ssuid
.SubserviceId
!=0)
770 CFRef
<CFNumberRef
> subserviceId(::CFNumberCreate(kCFAllocatorDefault
,kCFNumberSInt32Type
,&ssuid
.SubserviceId
));
772 ::CFDictionarySetValue(aDict
,kKeySubserviceId
,subserviceId
);
774 if (ssuid
.SubserviceType
!=0)
776 CFRef
<CFNumberRef
> subserviceType(CFNumberCreate(kCFAllocatorDefault
,kCFNumberSInt32Type
,&ssuid
.SubserviceType
));
778 ::CFDictionarySetValue(aDict
,kKeySubserviceType
,subserviceType
);
780 if (ssuid
.Version
.Major
!=0 && ssuid
.Version
.Minor
!=0)
782 CFRef
<CFNumberRef
> majorVersion(::CFNumberCreate(kCFAllocatorDefault
,kCFNumberSInt32Type
,&ssuid
.Version
.Major
));
784 ::CFDictionarySetValue(aDict
,kKeyMajorVersion
,majorVersion
);
785 CFRef
<CFNumberRef
> minorVersion(::CFNumberCreate(kCFAllocatorDefault
,kCFNumberSInt32Type
,&ssuid
.Version
.Minor
));
787 ::CFDictionarySetValue(aDict
,kKeyMinorVersion
,minorVersion
);
790 // Put DbName in dictionary
791 const char *dbName
=dldbIdentifier
.dbName();
794 CFRef
<CFStringRef
> theDbName(::CFStringCreateWithCString(kCFAllocatorDefault
,AbbreviatedPath(dbName
).c_str(),kCFStringEncodingUTF8
));
795 ::CFDictionarySetValue(aDict
,kKeyDbName
,theDbName
);
797 // Put DbLocation in dictionary
798 const CSSM_NET_ADDRESS
*dbLocation
=dldbIdentifier
.dbLocation();
799 if (dbLocation
!=NULL
&& dbLocation
->AddressType
!=CSSM_ADDR_NONE
)
801 CFRef
<CFDataRef
> theData(::CFDataCreate(kCFAllocatorDefault
,dbLocation
->Address
.Data
,dbLocation
->Address
.Length
));
803 ::CFDictionarySetValue(aDict
,kKeyDbLocation
,theData
);
810 bool DLDbListCFPref::revert(bool force
)
812 // If the prefs have not been refreshed in the last kDLDbListCFPrefRevertInterval
813 // seconds or we are asked to force a reload, then reload.
814 if (!loadPropertyList(force
))
822 DLDbListCFPref::add(const DLDbIdentifier
&dldbIdentifier
)
824 // convert the location specified in dldbIdentifier to a standard form
825 // make a canonical form of the database name
826 std::string canon
= ExpandTildesInPath(AbbreviatedPath(dldbIdentifier
.dbName()).c_str());
828 DLDbIdentifier
localIdentifier (dldbIdentifier
.ssuid(), canon
.c_str(), dldbIdentifier
.dbLocation ());
830 if (member(localIdentifier
))
833 mSearchList
.push_back(localIdentifier
);
838 DLDbListCFPref::remove(const DLDbIdentifier
&dldbIdentifier
)
840 // Make sure mSearchList is set
842 for (vector
<DLDbIdentifier
>::iterator ix
= mSearchList
.begin(); ix
!= mSearchList
.end(); ++ix
)
844 if (*ix
==dldbIdentifier
) // found in list
846 mSearchList
.erase(ix
);
854 DLDbListCFPref::rename(const DLDbIdentifier
&oldId
, const DLDbIdentifier
&newId
)
856 // Make sure mSearchList is set
858 for (vector
<DLDbIdentifier
>::iterator ix
= mSearchList
.begin();
859 ix
!= mSearchList
.end(); ++ix
)
863 // replace oldId with newId
869 // remove newId except where we just inserted it
870 mSearchList
.erase(ix
);
877 DLDbListCFPref::member(const DLDbIdentifier
&dldbIdentifier
)
879 if (dldbIdentifier
.IsImplEmpty())
884 for (vector
<DLDbIdentifier
>::const_iterator ix
= searchList().begin(); ix
!= mSearchList
.end(); ++ix
)
886 if (ix
->mImpl
== NULL
)
891 // compare the dldbIdentifiers based on the full, real path to the keychain
892 if (ix
->ssuid() == dldbIdentifier
.ssuid())
894 char localPath
[PATH_MAX
],
897 // try to resolve these down to a canonical form
898 const char* localPathPtr
= cached_realpath(ix
->dbName(), localPath
);
899 const char* inPathPtr
= cached_realpath(dldbIdentifier
.dbName(), inPath
);
901 // if either of the paths didn't resolve for some reason, use the originals
902 if (localPathPtr
== NULL
)
904 localPathPtr
= ix
->dbName();
907 if (inPathPtr
== NULL
)
909 inPathPtr
= dldbIdentifier
.dbName();
912 if (strcmp(localPathPtr
, inPathPtr
) == 0)
922 const vector
<DLDbIdentifier
> &
923 DLDbListCFPref::searchList()
927 CFArrayRef searchList
= reinterpret_cast<CFArrayRef
>(CFDictionaryGetValue(mPropertyList
, kDefaultDLDbListKey
));
928 if (searchList
&& CFGetTypeID(searchList
) != CFArrayGetTypeID())
933 CFIndex top
= CFArrayGetCount(searchList
);
934 // Each entry is a CFDictionary; peel it off & add it to the array
935 for (CFIndex idx
= 0; idx
< top
; ++idx
)
937 CFDictionaryRef theDict
= reinterpret_cast<CFDictionaryRef
>(CFArrayGetValueAtIndex(searchList
, idx
));
940 mSearchList
.push_back(cfDictionaryRefToDLDbIdentifier(theDict
));
944 // Drop stuff that doesn't parse on the floor.
948 // If there were entries specified, but they were invalid revert to using the
949 // default keychain in the searchlist.
950 if (top
> 0 && mSearchList
.size() == 0)
954 // The default when no search list is specified is to only search the
956 if (!searchList
&& static_cast<bool>(defaultDLDbIdentifier()))
957 mSearchList
.push_back(mDefaultDLDbIdentifier
);
959 mSearchListSet
= true;
966 DLDbListCFPref::searchList(const vector
<DLDbIdentifier
> &searchList
)
968 if(searchList
.size() == 0) {
970 mSearchListSet
= false;
975 vector
<DLDbIdentifier
> newList(searchList
);
976 mSearchList
.swap(newList
);
977 mSearchListSet
= true;
982 DLDbListCFPref::defaultDLDbIdentifier(const DLDbIdentifier
&dlDbIdentifier
)
984 if (!(defaultDLDbIdentifier() == dlDbIdentifier
))
986 mDefaultDLDbIdentifier
= dlDbIdentifier
;
991 // Caution: if the backing file for the defaultDLDbIdentifier doesn't exist (or if the plist file is corrupt),
992 // this will return a DLDbIdentifier with a NULL impl
993 const DLDbIdentifier
&
994 DLDbListCFPref::defaultDLDbIdentifier()
997 if (!mDefaultDLDbIdentifierSet
)
999 CFArrayRef defaultArray
= reinterpret_cast<CFArrayRef
>(CFDictionaryGetValue(mPropertyList
, kDefaultKeychainKey
));
1000 if (defaultArray
&& CFGetTypeID(defaultArray
) != CFArrayGetTypeID())
1001 defaultArray
= NULL
;
1003 if (defaultArray
&& CFArrayGetCount(defaultArray
) > 0)
1005 CFDictionaryRef defaultDict
= reinterpret_cast<CFDictionaryRef
>(CFArrayGetValueAtIndex(defaultArray
, 0));
1008 secinfo("secpref", "getting default DLDbIdentifier from defaultDict");
1009 mDefaultDLDbIdentifier
= cfDictionaryRefToDLDbIdentifier(defaultDict
);
1010 secinfo("secpref", "now we think the default keychain is %s", (mDefaultDLDbIdentifier
) ? mDefaultDLDbIdentifier
.dbName() : "<NULL>");
1014 // If defaultArray doesn't parse fall back on the default way of getting the default keychain
1015 defaultArray
= NULL
;
1021 // If the Panther style login keychain actually exists we use that otherwise no
1023 mDefaultDLDbIdentifier
= loginDLDbIdentifier();
1025 //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".
1026 DLDbIdentifier actualIdentifier
= KeychainCore::StorageManager::mungeDLDbIdentifier(mDefaultDLDbIdentifier
, false);
1027 secinfo("secpref", "now we think the default keychain is: %s (actual: %s)",
1028 (mDefaultDLDbIdentifier
) ? mDefaultDLDbIdentifier
.dbName() : "Name doesn't exist",
1029 (actualIdentifier
) ? actualIdentifier
.dbName() : "Name doesn't exist");
1034 if (mDefaultDLDbIdentifier
.mImpl
!= NULL
&& actualIdentifier
.mImpl
!= NULL
)
1036 st_result
= stat(actualIdentifier
.dbName(), &st
);
1038 // Always claim that the system keychain exists for purposes of the search list
1039 if (st_result
&& 0 == strncmp(actualIdentifier
.dbName(), kSystemKeychainPath
, strlen(kSystemKeychainPath
))) {
1040 secnotice("secpref", "System keychain (%s) does not exist. Continuing as if it does...", actualIdentifier
.dbName());
1047 secinfo("secpref", "stat(%s) -> %d", actualIdentifier
.dbName(), st_result
);
1048 mDefaultDLDbIdentifier
= DLDbIdentifier(); // initialize a NULL keychain
1049 secinfo("secpref", "after DLDbIdentifier(), we think the default keychain is %s", static_cast<bool>(mDefaultDLDbIdentifier
) ? mDefaultDLDbIdentifier
.dbName() : "<NULL>");
1053 mDefaultDLDbIdentifierSet
= true;
1057 return mDefaultDLDbIdentifier
;
1061 DLDbListCFPref::loginDLDbIdentifier(const DLDbIdentifier
&dlDbIdentifier
)
1063 if (!(loginDLDbIdentifier() == dlDbIdentifier
))
1065 mLoginDLDbIdentifier
= dlDbIdentifier
;
1070 const DLDbIdentifier
&
1071 DLDbListCFPref::loginDLDbIdentifier()
1073 if (!mLoginDLDbIdentifierSet
)
1075 CFArrayRef loginArray
= reinterpret_cast<CFArrayRef
>(CFDictionaryGetValue(mPropertyList
, kLoginKeychainKey
));
1076 if (loginArray
&& CFGetTypeID(loginArray
) != CFArrayGetTypeID())
1079 if (loginArray
&& CFArrayGetCount(loginArray
) > 0)
1081 CFDictionaryRef loginDict
= reinterpret_cast<CFDictionaryRef
>(CFArrayGetValueAtIndex(loginArray
, 0));
1084 secinfo("secpref", "Getting login DLDbIdentifier from loginDict");
1085 mLoginDLDbIdentifier
= cfDictionaryRefToDLDbIdentifier(loginDict
);
1086 secinfo("secpref", "we think the login keychain is %s", static_cast<bool>(mLoginDLDbIdentifier
) ? mLoginDLDbIdentifier
.dbName() : "<NULL>");
1090 // If loginArray doesn't parse fall back on the default way of getting the login keychain.
1097 mLoginDLDbIdentifier
= LoginDLDbIdentifier();
1098 secinfo("secpref", "after LoginDLDbIdentifier(), we think the login keychain is %s", static_cast<bool>(mLoginDLDbIdentifier
) ? mLoginDLDbIdentifier
.dbName() : "<NULL>");
1101 mLoginDLDbIdentifierSet
= true;
1104 return mLoginDLDbIdentifier
;