Security-55179.1.tar.gz
[apple/security.git] / libsecurity_keychain / lib / DLDBListCFPref.cpp
1 /*
2 * Copyright (c) 2000-2004 Apple Computer, Inc. All Rights Reserved.
3 *
4 * @APPLE_LICENSE_HEADER_START@
5 *
6 * This file contains Original Code and/or Modifications of Original Code
7 * as defined in and that are subject to the Apple Public Source License
8 * Version 2.0 (the 'License'). You may not use this file except in
9 * compliance with the License. Please obtain a copy of the License at
10 * http://www.opensource.apple.com/apsl/ and read it before using this
11 * file.
12 *
13 * The Original Code and all software distributed under the License are
14 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
15 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
16 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
18 * Please see the License for the specific language governing rights and
19 * limitations under the License.
20 *
21 * @APPLE_LICENSE_HEADER_END@
22 */
23
24
25 /*
26 DLDBListCFPref.cpp
27 */
28
29 #include "DLDBListCFPref.h"
30 #include <Security/cssmapple.h>
31 #include <security_utilities/debugging.h>
32 #include <security_utilities/utilities.h>
33 #include <memory>
34 #include <fcntl.h>
35 #include <sys/types.h>
36 #include <sys/stat.h>
37 #include <unistd.h>
38 #include <pwd.h>
39 #include <sys/param.h>
40 #include <copyfile.h>
41 #include <xpc/private.h>
42 #include <syslog.h>
43 #include <sandbox.h>
44
45 dispatch_once_t AppSandboxChecked;
46 xpc_object_t KeychainHomeFromXPC;
47
48 using namespace CssmClient;
49
50 static const double kDLDbListCFPrefRevertInterval = 30.0;
51
52 // normal debug calls, which get stubbed out for deployment builds
53
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"
71
72
73 // A utility class for managing password database lookups
74
75 const time_t kPasswordCacheExpire = 30; // number of seconds cached password db info is valid
76
77 PasswordDBLookup::PasswordDBLookup () : mValid (false), mCurrent (0), mTime (0)
78 {
79 }
80
81 void PasswordDBLookup::lookupInfoOnUID (uid_t uid)
82 {
83 time_t currentTime = time (NULL);
84
85 if (!mValid || uid != mCurrent || currentTime - mTime >= kPasswordCacheExpire)
86 {
87 struct passwd* pw = getpwuid(uid);
88 if (pw == NULL)
89 {
90 UnixError::throwMe (EPERM);
91 }
92
93 mDirectory = pw->pw_dir;
94 mName = pw->pw_name;
95 mValid = true;
96 mCurrent = uid;
97 mTime = currentTime;
98
99 secdebug("secpref", "uid=%d caching home=%s", uid, pw->pw_dir);
100
101 endpwent();
102 }
103 }
104
105 PasswordDBLookup *DLDbListCFPref::mPdbLookup = NULL;
106
107 //-------------------------------------------------------------------------------------
108 //
109 // Lists of DL/DBs, with CFPreferences backing store
110 //
111 //-------------------------------------------------------------------------------------
112
113 DLDbListCFPref::DLDbListCFPref(SecPreferencesDomain domain) : mDomain(domain), mPropertyList(NULL), mChanged(false),
114 mSearchListSet(false), mDefaultDLDbIdentifierSet(false), mLoginDLDbIdentifierSet(false)
115 {
116 secdebug("secpref", "New DLDbListCFPref %p for domain %d", this, domain);
117 loadPropertyList(true);
118 }
119
120 void DLDbListCFPref::set(SecPreferencesDomain domain)
121 {
122 save();
123
124 mDomain = domain;
125
126 secdebug("secpref", "DLDbListCFPref %p domain set to %d", this, domain);
127
128 if (loadPropertyList(true))
129 resetCachedValues();
130 }
131
132 DLDbListCFPref::~DLDbListCFPref()
133 {
134 save();
135
136 if (mPropertyList)
137 CFRelease(mPropertyList);
138 }
139
140 void
141 DLDbListCFPref::forceUserSearchListReread()
142 {
143 // set mPrefsTimeStamp so that it will "expire" the next time loadPropertyList is called
144 mPrefsTimeStamp = CFAbsoluteTimeGetCurrent() - kDLDbListCFPrefRevertInterval;
145 }
146
147 bool
148 DLDbListCFPref::loadPropertyList(bool force)
149 {
150 string prefsPath;
151
152 switch (mDomain)
153 {
154 case kSecPreferencesDomainUser:
155 prefsPath = ExpandTildesInPath(kUserDefaultPath);
156 break;
157 case kSecPreferencesDomainSystem:
158 prefsPath = kSystemDefaultPath;
159 break;
160 case kSecPreferencesDomainCommon:
161 prefsPath = kCommonDefaultPath;
162 break;
163 default:
164 MacOSError::throwMe(errSecInvalidPrefsDomain);
165 }
166
167 secdebug("secpref", "force=%s prefsPath=%s", force ? "true" : "false",
168 prefsPath.c_str());
169
170 CFAbsoluteTime now = CFAbsoluteTimeGetCurrent();
171
172 // If for some reason the prefs file path has changed, blow away the old plist and force an update
173 if (mPrefsPath != prefsPath)
174 {
175 mPrefsPath = prefsPath;
176 if (mPropertyList)
177 {
178 CFRelease(mPropertyList);
179 mPropertyList = NULL;
180 }
181
182 mPrefsTimeStamp = now;
183 }
184 else if (!force)
185 {
186 if (now - mPrefsTimeStamp < kDLDbListCFPrefRevertInterval)
187 return false;
188
189 mPrefsTimeStamp = now;
190 }
191
192 struct stat st;
193 if (stat(mPrefsPath.c_str(), &st))
194 {
195 if (errno == ENOENT)
196 {
197 if (mPropertyList)
198 {
199 if (CFDictionaryGetCount(mPropertyList) == 0)
200 return false;
201 CFRelease(mPropertyList);
202 }
203
204 mPropertyList = CFDictionaryCreateMutable(kCFAllocatorDefault, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
205 return true;
206 }
207 }
208 else
209 {
210 if (mPropertyList)
211 {
212 if (mTimespec.tv_sec == st.st_mtimespec.tv_sec
213 && mTimespec.tv_nsec == st.st_mtimespec.tv_nsec)
214 return false;
215 }
216
217 mTimespec = st.st_mtimespec;
218 }
219
220 CFMutableDictionaryRef thePropertyList = NULL;
221 CFMutableDataRef xmlData = NULL;
222 CFStringRef errorString = NULL;
223 int fd = -1;
224
225 do
226 {
227 fd = open(mPrefsPath.c_str(), O_RDONLY, 0);
228 if (fd < 0)
229 break;
230
231 off_t theSize = lseek(fd, 0, SEEK_END);
232 if (theSize <= 0)
233 break;
234
235 if (lseek(fd, 0, SEEK_SET))
236 break;
237
238 xmlData = CFDataCreateMutable(NULL, CFIndex(theSize));
239 if (!xmlData)
240 break;
241 CFDataSetLength(xmlData, CFIndex(theSize));
242 void *buffer = reinterpret_cast<void *>(CFDataGetMutableBytePtr(xmlData));
243 if (!buffer)
244 break;
245 ssize_t bytesRead = read(fd, buffer, theSize);
246 if (bytesRead != theSize)
247 break;
248
249 thePropertyList = CFMutableDictionaryRef(CFPropertyListCreateFromXMLData(NULL, xmlData, kCFPropertyListMutableContainers, &errorString));
250 if (!thePropertyList)
251 break;
252
253 if (CFGetTypeID(thePropertyList) != CFDictionaryGetTypeID())
254 {
255 CFRelease(thePropertyList);
256 thePropertyList = NULL;
257 break;
258 }
259 } while (0);
260
261 if (fd >= 0)
262 close(fd);
263 if (xmlData)
264 CFRelease(xmlData);
265 if (errorString)
266 CFRelease(errorString);
267
268 if (!thePropertyList)
269 {
270 thePropertyList = CFDictionaryCreateMutable(kCFAllocatorDefault, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
271 }
272
273 if (mPropertyList)
274 {
275 if (CFEqual(mPropertyList, thePropertyList))
276 {
277 // The new property list is the same as the old one, so nothing has changed.
278 CFRelease(thePropertyList);
279 return false;
280 }
281 CFRelease(mPropertyList);
282 }
283
284 mPropertyList = thePropertyList;
285 return true;
286 }
287
288 void
289 DLDbListCFPref::writePropertyList()
290 {
291 if (!mPropertyList || CFDictionaryGetCount(mPropertyList) == 0)
292 {
293 // There is nothing in the mPropertyList dictionary,
294 // so we don't need a prefs file.
295 unlink(mPrefsPath.c_str());
296 }
297 else
298 {
299 if(testAndFixPropertyList())
300 return;
301
302 CFDataRef xmlData = CFPropertyListCreateXMLData(NULL, mPropertyList);
303 if (!xmlData)
304 return; // Bad out of memory or something evil happened let's act like CF and do nothing.
305
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.
308
309 mode_t mode = 0666;
310 changeIdentity(UNPRIV);
311 int fd = open(mPrefsPath.c_str(), O_WRONLY|O_CREAT|O_TRUNC, mode);
312 changeIdentity(PRIV);
313 if (fd >= 0)
314 {
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.
319
320 fsync(fd);
321 struct stat st;
322 if (!fstat(fd, &st))
323 mTimespec = st.st_mtimespec;
324
325 close(fd);
326 }
327
328 CFRelease(xmlData);
329 }
330
331 mPrefsTimeStamp = CFAbsoluteTimeGetCurrent();
332 }
333
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.
338
339 int
340 DLDbListCFPref::testAndFixPropertyList()
341 {
342 char *prefsPath = (char *)mPrefsPath.c_str();
343
344 int fd1, fd2, retval;
345 struct stat stbuf;
346
347 if((fd1 = open(prefsPath, O_RDONLY)) < 0) {
348 if (errno == ENOENT) return 0; // Doesn't exist - the default case
349 else return -1;
350 }
351
352 if((retval = fstat(fd1, &stbuf)) == -1) return -1;
353
354 if(stbuf.st_uid != getuid()) {
355 char tempfile[MAXPATHLEN+1];
356
357 snprintf(tempfile, MAXPATHLEN, "%s.XXXXX", prefsPath);
358 mktemp(tempfile);
359 changeIdentity(UNPRIV);
360 if((fd2 = open(tempfile, O_RDWR | O_CREAT | O_EXCL, 0666)) < 0) {
361 retval = -1;
362 } else {
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);
368 }
369 changeIdentity(PRIV);
370 close(fd2);
371 }
372 close(fd1);
373 return retval;
374 }
375
376 // Encapsulated process uid/gid change routine.
377 void
378 DLDbListCFPref::changeIdentity(ID_Direction toPriv)
379 {
380 if(toPriv == UNPRIV) {
381 savedEUID = geteuid();
382 savedEGID = getegid();
383 if(savedEGID != getgid()) setegid(getgid());
384 if(savedEUID != getuid()) seteuid(getuid());
385 } else {
386 if(savedEUID != getuid()) seteuid(savedEUID);
387 if(savedEGID != getgid()) setegid(savedEGID);
388 }
389 }
390
391 void
392 DLDbListCFPref::resetCachedValues()
393 {
394 // Unset the login and default Keychain.
395 mLoginDLDbIdentifier = mDefaultDLDbIdentifier = DLDbIdentifier();
396
397 // Clear the searchList.
398 mSearchList.clear();
399
400 changed(false);
401
402 // Note that none of our cached values are valid
403 mSearchListSet = mDefaultDLDbIdentifierSet = mLoginDLDbIdentifierSet = false;
404
405 mPrefsTimeStamp = CFAbsoluteTimeGetCurrent();
406 }
407
408 void DLDbListCFPref::save()
409 {
410 if (!hasChanged())
411 return;
412
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);
417
418 // Do the searchList first since it might end up invoking defaultDLDbIdentifier() which can set
419 // mLoginDLDbIdentifierSet and mDefaultDLDbIdentifierSet to true.
420 if (mSearchListSet)
421 {
422 // Make a temporary CFArray with the contents of the vector
423 if (mSearchList.size() == 1 && mSearchList[0] == defaultDLDbIdentifier() && mSearchList[0] == LoginDLDbIdentifier())
424 {
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);
428 }
429 else
430 {
431 CFMutableArrayRef searchArray = CFArrayCreateMutable(kCFAllocatorDefault, mSearchList.size(), &kCFTypeArrayCallBacks);
432 for (DLDbList::const_iterator ix=mSearchList.begin();ix!=mSearchList.end();ix++)
433 {
434 CFDictionaryRef aDict = dlDbIdentifierToCFDictionaryRef(*ix);
435 CFArrayAppendValue(searchArray, aDict);
436 CFRelease(aDict);
437 }
438
439 CFDictionarySetValue(mPropertyList, kDefaultDLDbListKey, searchArray);
440 CFRelease(searchArray);
441 }
442 }
443
444 if (mLoginDLDbIdentifierSet)
445 {
446 // Make a temporary CFArray with the login keychain
447 CFArrayRef loginArray = NULL;
448 if (!mLoginDLDbIdentifier)
449 {
450 loginArray = CFArrayCreate(kCFAllocatorDefault, NULL, 0, &kCFTypeArrayCallBacks);
451 }
452 else if (!(mLoginDLDbIdentifier == LoginDLDbIdentifier()))
453 {
454 CFDictionaryRef aDict = dlDbIdentifierToCFDictionaryRef(mLoginDLDbIdentifier);
455 const void *value = reinterpret_cast<const void *>(aDict);
456 loginArray = CFArrayCreate(kCFAllocatorDefault, &value, 1, &kCFTypeArrayCallBacks);
457 CFRelease(aDict);
458 }
459
460 if (loginArray)
461 {
462 CFDictionarySetValue(mPropertyList, kLoginKeychainKey, loginArray);
463 CFRelease(loginArray);
464 }
465 else
466 CFDictionaryRemoveValue(mPropertyList, kLoginKeychainKey);
467 }
468
469 if (mDefaultDLDbIdentifierSet)
470 {
471 // Make a temporary CFArray with the default keychain
472 CFArrayRef defaultArray = NULL;
473 if (!mDefaultDLDbIdentifier)
474 {
475 defaultArray = CFArrayCreate(kCFAllocatorDefault, NULL, 0, &kCFTypeArrayCallBacks);
476 }
477 else if (!(mDefaultDLDbIdentifier == LoginDLDbIdentifier()))
478 {
479 CFDictionaryRef aDict = dlDbIdentifierToCFDictionaryRef(mDefaultDLDbIdentifier);
480 const void *value = reinterpret_cast<const void *>(aDict);
481 defaultArray = CFArrayCreate(kCFAllocatorDefault, &value, 1, &kCFTypeArrayCallBacks);
482 CFRelease(aDict);
483 }
484
485 if (defaultArray)
486 {
487 CFDictionarySetValue(mPropertyList, kDefaultKeychainKey, defaultArray);
488 CFRelease(defaultArray);
489 }
490 else
491 CFDictionaryRemoveValue(mPropertyList, kDefaultKeychainKey);
492 }
493
494 writePropertyList();
495 changed(false);
496 }
497
498
499 //----------------------------------------------------------------------
500 // Conversions
501 //----------------------------------------------------------------------
502
503 DLDbIdentifier DLDbListCFPref::LoginDLDbIdentifier()
504 {
505 CSSM_VERSION theVersion={};
506 CssmSubserviceUid ssuid(gGuidAppleCSPDL,&theVersion,0,CSSM_SERVICE_DL|CSSM_SERVICE_CSP);
507 CssmNetAddress *dbLocation=NULL;
508
509 switch (mDomain) {
510 case kSecPreferencesDomainUser:
511 return DLDbIdentifier(ssuid, ExpandTildesInPath(kUserLoginKeychainPath).c_str(), dbLocation);
512 default:
513 assert(false);
514 case kSecPreferencesDomainSystem:
515 case kSecPreferencesDomainCommon:
516 return DLDbIdentifier(ssuid, kSystemLoginKeychainPath, dbLocation);
517 }
518 }
519
520 DLDbIdentifier DLDbListCFPref::JaguarLoginDLDbIdentifier()
521 {
522 CSSM_VERSION theVersion={};
523 CssmSubserviceUid ssuid(gGuidAppleCSPDL,&theVersion,0,CSSM_SERVICE_DL|CSSM_SERVICE_CSP);
524 CssmNetAddress *dbLocation=NULL;
525
526 switch (mDomain) {
527 case kSecPreferencesDomainUser:
528 {
529 string basepath = ExpandTildesInPath(kLoginKeychainPathPrefix) + getPwInfo(kUsername);
530 return DLDbIdentifier(ssuid,basepath.c_str(),dbLocation);
531 }
532 case kSecPreferencesDomainSystem:
533 case kSecPreferencesDomainCommon:
534 return DLDbIdentifier(ssuid, kSystemLoginKeychainPath, dbLocation);
535 default:
536 assert(false);
537 return DLDbIdentifier();
538 }
539 }
540
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)
544 {
545 CssmSubserviceUid ssuid (guid, &version, subserviceId, subserviceType);
546 return DLDbIdentifier (ssuid, ExpandTildesInPath (dbName).c_str (), dbLocation);
547 }
548
549 DLDbIdentifier DLDbListCFPref::cfDictionaryRefToDLDbIdentifier(CFDictionaryRef theDict)
550 {
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");
554
555 // GUID
556 CCFValue vGuid(::CFDictionaryGetValue(theDict,kKeyGUID));
557 string guidStr=vGuid;
558 const Guid guid(guidStr.c_str());
559
560 //CSSM_VERSION
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;
566
567 //subserviceId
568 CCFValue vSsid(::CFDictionaryGetValue(theDict,kKeySubserviceId));
569 uint32 subserviceId=sint32(vSsid);
570
571 //CSSM_SERVICE_TYPE
572 CSSM_SERVICE_TYPE subserviceType=CSSM_SERVICE_DL;
573 CCFValue vSsType(::CFDictionaryGetValue(theDict,kKeySubserviceType));
574 subserviceType=vSsType;
575
576 // Get DbName from dictionary
577 CCFValue vDbName(::CFDictionaryGetValue(theDict,kKeyDbName));
578 string dbName=vDbName;
579
580 // jch Get DbLocation from dictionary
581 CssmNetAddress *dbLocation=NULL;
582
583 return makeDLDbIdentifier (guid, theVersion, subserviceId, subserviceType, dbName.c_str (), dbLocation);
584 }
585
586 void DLDbListCFPref::clearPWInfo ()
587 {
588 if (mPdbLookup != NULL)
589 {
590 delete mPdbLookup;
591 mPdbLookup = NULL;
592 }
593 }
594
595 string DLDbListCFPref::getPwInfo(PwInfoType type)
596 {
597 const char *value;
598 switch (type)
599 {
600 case kHomeDir:
601 if (KeychainHomeFromXPC) {
602 value = xpc_string_get_string_ptr(KeychainHomeFromXPC);
603 } else {
604 value = getenv("HOME");
605 }
606 if (value)
607 return value;
608 break;
609 case kUsername:
610 value = getenv("USER");
611 if (value)
612 return value;
613 break;
614 }
615
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();
620
621 // get the password entries
622 if (mPdbLookup == NULL)
623 {
624 mPdbLookup = new PasswordDBLookup ();
625 }
626
627 mPdbLookup->lookupInfoOnUID (uid);
628
629 string result;
630 switch (type)
631 {
632 case kHomeDir:
633 result = mPdbLookup->getDirectory ();
634 break;
635 case kUsername:
636 result = mPdbLookup->getName ();
637 break;
638 }
639
640 return result;
641 }
642
643 static void check_app_sandbox()
644 {
645 if (!_xpc_runtime_is_app_sandboxed()) {
646 // We are not in a sandbox, no work to do here
647 return;
648 }
649
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));
656 } else {
657 syslog(LOG_ERR, "Keychain sandbox unexpected connection event %p\n", event);
658 }
659 });
660 xpc_connection_resume(con);
661
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) {
666 #if 0
667 // This is useful for debugging.
668 char *debug = xpc_copy_description(reply);
669 syslog(LOG_ERR, "DEBUG (KCsandbox) %s\n", debug);
670 free(debug);
671 #endif
672
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];
676 char *path = pbuf;
677 int status = sandbox_consume_fs_extension(xpc_string_get_string_ptr(extension), &path);
678 if (status) {
679 syslog(LOG_ERR, "Keychain sandbox consume extension error: s=%d p=%s %m\n", status, path);
680 }
681 return (bool)true;
682 });
683
684 KeychainHomeFromXPC = xpc_dictionary_get_value(reply, "keychain-home");
685 xpc_retain(KeychainHomeFromXPC);
686 xpc_release(con);
687 } else if (XPC_TYPE_ERROR == xtype) {
688 syslog(LOG_ERR, "Keychain sandbox message error: %s\n", xpc_dictionary_get_string(reply, XPC_ERROR_KEY_DESCRIPTION));
689 } else {
690 syslog(LOG_ERR, "Keychain sandbox unexpected message reply type %p\n", xtype);
691 }
692 xpc_release(message);
693 xpc_release(reply);
694 }
695
696
697
698 string DLDbListCFPref::ExpandTildesInPath(const string &inPath)
699 {
700 dispatch_once(&AppSandboxChecked, ^{
701 check_app_sandbox();
702 });
703
704 if ((short)inPath.find("~/",0,2) == 0)
705 return getPwInfo(kHomeDir) + inPath.substr(1);
706 else
707 return inPath;
708 }
709
710 string DLDbListCFPref::StripPathStuff(const string &inPath)
711 {
712 if (inPath.find("/private/var/automount/Network/",0,31) == 0)
713 return inPath.substr(22);
714 if (inPath.find("/private/automount/Servers/",0,27) == 0)
715 return "/Network" + inPath.substr(18);
716 if (inPath.find("/automount/Servers/",0,19) == 0)
717 return "/Network" + inPath.substr(10);
718 if (inPath.find("/private/automount/Network/",0,27) == 0)
719 return inPath.substr(18);
720 if (inPath.find("/automount/Network/",0,19) == 0)
721 return inPath.substr(10);
722 if (inPath.find("/private/Network/",0,17) == 0)
723 return inPath.substr(8);
724 return inPath;
725 }
726
727 string DLDbListCFPref::AbbreviatedPath(const string &inPath)
728 {
729 string path = StripPathStuff(inPath);
730 string home = StripPathStuff(getPwInfo(kHomeDir) + "/");
731 size_t homeLen = home.length();
732
733 if (homeLen > 1 && path.find(home.c_str(), 0, homeLen) == 0)
734 return "~" + path.substr(homeLen - 1);
735 else
736 return path;
737 }
738
739 CFDictionaryRef DLDbListCFPref::dlDbIdentifierToCFDictionaryRef(const DLDbIdentifier& dldbIdentifier)
740 {
741 CFRef<CFMutableDictionaryRef> aDict(CFDictionaryCreateMutable(kCFAllocatorDefault,0,
742 &kCFTypeDictionaryKeyCallBacks,&kCFTypeDictionaryValueCallBacks));
743 if (!aDict)
744 throw ::std::bad_alloc();
745
746 // Put SUBSERVICE_UID in dictionary
747 char buffer[Guid::stringRepLength+1];
748 const CssmSubserviceUid& ssuid=dldbIdentifier.ssuid();
749 const Guid &theGuid = Guid::overlay(ssuid.Guid);
750 CFRef<CFStringRef> stringGuid(::CFStringCreateWithCString(kCFAllocatorDefault,
751 theGuid.toString(buffer),kCFStringEncodingMacRoman));
752 if (stringGuid)
753 ::CFDictionarySetValue(aDict,kKeyGUID,stringGuid);
754
755 if (ssuid.SubserviceId!=0)
756 {
757 CFRef<CFNumberRef> subserviceId(::CFNumberCreate(kCFAllocatorDefault,kCFNumberSInt32Type,&ssuid.SubserviceId));
758 if (subserviceId)
759 ::CFDictionarySetValue(aDict,kKeySubserviceId,subserviceId);
760 }
761 if (ssuid.SubserviceType!=0)
762 {
763 CFRef<CFNumberRef> subserviceType(CFNumberCreate(kCFAllocatorDefault,kCFNumberSInt32Type,&ssuid.SubserviceType));
764 if (subserviceType)
765 ::CFDictionarySetValue(aDict,kKeySubserviceType,subserviceType);
766 }
767 if (ssuid.Version.Major!=0 && ssuid.Version.Minor!=0)
768 {
769 CFRef<CFNumberRef> majorVersion(::CFNumberCreate(kCFAllocatorDefault,kCFNumberSInt32Type,&ssuid.Version.Major));
770 if (majorVersion)
771 ::CFDictionarySetValue(aDict,kKeyMajorVersion,majorVersion);
772 CFRef<CFNumberRef> minorVersion(::CFNumberCreate(kCFAllocatorDefault,kCFNumberSInt32Type,&ssuid.Version.Minor));
773 if (minorVersion)
774 ::CFDictionarySetValue(aDict,kKeyMinorVersion,minorVersion);
775 }
776
777 // Put DbName in dictionary
778 const char *dbName=dldbIdentifier.dbName();
779 if (dbName)
780 {
781 CFRef<CFStringRef> theDbName(::CFStringCreateWithCString(kCFAllocatorDefault,AbbreviatedPath(dbName).c_str(),kCFStringEncodingUTF8));
782 ::CFDictionarySetValue(aDict,kKeyDbName,theDbName);
783 }
784 // Put DbLocation in dictionary
785 const CSSM_NET_ADDRESS *dbLocation=dldbIdentifier.dbLocation();
786 if (dbLocation!=NULL && dbLocation->AddressType!=CSSM_ADDR_NONE)
787 {
788 CFRef<CFDataRef> theData(::CFDataCreate(kCFAllocatorDefault,dbLocation->Address.Data,dbLocation->Address.Length));
789 if (theData)
790 ::CFDictionarySetValue(aDict,kKeyDbLocation,theData);
791 }
792
793 ::CFRetain(aDict);
794 return aDict;
795 }
796
797 bool DLDbListCFPref::revert(bool force)
798 {
799 // If the prefs have not been refreshed in the last kDLDbListCFPrefRevertInterval
800 // seconds or we are asked to force a reload, then reload.
801 if (!loadPropertyList(force))
802 return false;
803
804 resetCachedValues();
805 return true;
806 }
807
808 void
809 DLDbListCFPref::add(const DLDbIdentifier &dldbIdentifier)
810 {
811 // convert the location specified in dldbIdentifier to a standard form
812 // make a canonical form of the database name
813 std::string canon = ExpandTildesInPath(AbbreviatedPath(dldbIdentifier.dbName()).c_str());
814
815 DLDbIdentifier localIdentifier (dldbIdentifier.ssuid(), canon.c_str(), dldbIdentifier.dbLocation ());
816
817 if (member(localIdentifier))
818 return;
819
820 mSearchList.push_back(localIdentifier);
821 changed(true);
822 }
823
824 void
825 DLDbListCFPref::remove(const DLDbIdentifier &dldbIdentifier)
826 {
827 // Make sure mSearchList is set
828 searchList();
829 for (vector<DLDbIdentifier>::iterator ix = mSearchList.begin(); ix != mSearchList.end(); ++ix)
830 {
831 if (*ix==dldbIdentifier) // found in list
832 {
833 mSearchList.erase(ix);
834 changed(true);
835 break;
836 }
837 }
838 }
839
840 void
841 DLDbListCFPref::rename(const DLDbIdentifier &oldId, const DLDbIdentifier &newId)
842 {
843 // Make sure mSearchList is set
844 searchList();
845 for (vector<DLDbIdentifier>::iterator ix = mSearchList.begin();
846 ix != mSearchList.end(); ++ix)
847 {
848 if (*ix==oldId)
849 {
850 // replace oldId with newId
851 *ix = newId;
852 changed(true);
853 }
854 else if (*ix==newId)
855 {
856 // remove newId except where we just inserted it
857 mSearchList.erase(ix);
858 changed(true);
859 }
860 }
861 }
862
863 bool
864 DLDbListCFPref::member(const DLDbIdentifier &dldbIdentifier)
865 {
866 for (vector<DLDbIdentifier>::const_iterator ix = searchList().begin(); ix != mSearchList.end(); ++ix)
867 {
868 if (ix->mImpl == NULL)
869 {
870 continue;
871 }
872
873 // compare the dldbIdentifiers based on the full, real path to the keychain
874 if (ix->ssuid() == dldbIdentifier.ssuid())
875 {
876 char localPath[PATH_MAX],
877 inPath[PATH_MAX];
878
879 // try to resolve these down to a canonical form
880 const char* localPathPtr = cached_realpath(ix->dbName(), localPath);
881 const char* inPathPtr = cached_realpath(dldbIdentifier.dbName(), inPath);
882
883 // if either of the paths didn't resolve for some reason, use the originals
884 if (localPathPtr == NULL)
885 {
886 localPathPtr = ix->dbName();
887 }
888
889 if (inPathPtr == NULL)
890 {
891 inPathPtr = dldbIdentifier.dbName();
892 }
893
894 if (strcmp(localPathPtr, inPathPtr) == 0)
895 {
896 return true;
897 }
898 }
899 }
900
901 return false;
902 }
903
904 const vector<DLDbIdentifier> &
905 DLDbListCFPref::searchList()
906 {
907 if (!mSearchListSet)
908 {
909 CFArrayRef searchList = reinterpret_cast<CFArrayRef>(CFDictionaryGetValue(mPropertyList, kDefaultDLDbListKey));
910 if (searchList && CFGetTypeID(searchList) != CFArrayGetTypeID())
911 searchList = NULL;
912
913 if (searchList)
914 {
915 CFIndex top = CFArrayGetCount(searchList);
916 // Each entry is a CFDictionary; peel it off & add it to the array
917 for (CFIndex idx = 0; idx < top; ++idx)
918 {
919 CFDictionaryRef theDict = reinterpret_cast<CFDictionaryRef>(CFArrayGetValueAtIndex(searchList, idx));
920 try
921 {
922 mSearchList.push_back(cfDictionaryRefToDLDbIdentifier(theDict));
923 }
924 catch (...)
925 {
926 // Drop stuff that doesn't parse on the floor.
927 }
928 }
929
930 // If there were entries specified, but they were invalid revert to using the
931 // default keychain in the searchlist.
932 if (top > 0 && mSearchList.size() == 0)
933 searchList = NULL;
934 }
935
936 // The default when no search list is specified is to only search the
937 // default keychain.
938 if (!searchList && static_cast<bool>(defaultDLDbIdentifier()))
939 mSearchList.push_back(mDefaultDLDbIdentifier);
940
941 mSearchListSet = true;
942 }
943
944 return mSearchList;
945 }
946
947 void
948 DLDbListCFPref::searchList(const vector<DLDbIdentifier> &searchList)
949 {
950 vector<DLDbIdentifier> newList(searchList);
951 mSearchList.swap(newList);
952 mSearchListSet = true;
953 changed(true);
954 }
955
956 void
957 DLDbListCFPref::defaultDLDbIdentifier(const DLDbIdentifier &dlDbIdentifier)
958 {
959 if (!(defaultDLDbIdentifier() == dlDbIdentifier))
960 {
961 mDefaultDLDbIdentifier = dlDbIdentifier;
962 changed(true);
963 }
964 }
965
966 const DLDbIdentifier &
967 DLDbListCFPref::defaultDLDbIdentifier()
968 {
969
970 if (!mDefaultDLDbIdentifierSet)
971 {
972 CFArrayRef defaultArray = reinterpret_cast<CFArrayRef>(CFDictionaryGetValue(mPropertyList, kDefaultKeychainKey));
973 if (defaultArray && CFGetTypeID(defaultArray) != CFArrayGetTypeID())
974 defaultArray = NULL;
975
976 if (defaultArray && CFArrayGetCount(defaultArray) > 0)
977 {
978 CFDictionaryRef defaultDict = reinterpret_cast<CFDictionaryRef>(CFArrayGetValueAtIndex(defaultArray, 0));
979 try
980 {
981 secdebug("secpref", "getting default DLDbIdentifier from defaultDict");
982 mDefaultDLDbIdentifier = cfDictionaryRefToDLDbIdentifier(defaultDict);
983 secdebug("secpref", "now we think the default keychain is %s", (mDefaultDLDbIdentifier) ? mDefaultDLDbIdentifier.dbName() : "<NULL>");
984 }
985 catch (...)
986 {
987 // If defaultArray doesn't parse fall back on the default way of getting the default keychain
988 defaultArray = NULL;
989 }
990 }
991
992 if (!defaultArray)
993 {
994
995 // If the Panther style login keychain actually exists we use that otherwise no
996 // default is set.
997 mDefaultDLDbIdentifier = loginDLDbIdentifier();
998 secdebug("secpref", "now we think the default keychain is: %s", (mDefaultDLDbIdentifier) ? mDefaultDLDbIdentifier.dbName() :
999 "Name doesn't exist");
1000
1001
1002 //"Name doesn't exist");
1003
1004 struct stat st;
1005 int st_result = stat(mDefaultDLDbIdentifier.dbName(), &st);
1006
1007
1008 if (st_result)
1009 {
1010 secdebug("secpref", "stat(%s) -> %d", mDefaultDLDbIdentifier.dbName(), st_result);
1011 mDefaultDLDbIdentifier = DLDbIdentifier(); // initialize a NULL keychain
1012 secdebug("secpref", "after DLDbIdentifier(), we think the default keychain is %s", static_cast<bool>(mDefaultDLDbIdentifier) ? mDefaultDLDbIdentifier.dbName() : "<NULL>");
1013 }
1014 }
1015
1016 mDefaultDLDbIdentifierSet = true;
1017 }
1018
1019
1020 return mDefaultDLDbIdentifier;
1021 }
1022
1023 void
1024 DLDbListCFPref::loginDLDbIdentifier(const DLDbIdentifier &dlDbIdentifier)
1025 {
1026 if (!(loginDLDbIdentifier() == dlDbIdentifier))
1027 {
1028 mLoginDLDbIdentifier = dlDbIdentifier;
1029 changed(true);
1030 }
1031 }
1032
1033 const DLDbIdentifier &
1034 DLDbListCFPref::loginDLDbIdentifier()
1035 {
1036 if (!mLoginDLDbIdentifierSet)
1037 {
1038 CFArrayRef loginArray = reinterpret_cast<CFArrayRef>(CFDictionaryGetValue(mPropertyList, kLoginKeychainKey));
1039 if (loginArray && CFGetTypeID(loginArray) != CFArrayGetTypeID())
1040 loginArray = NULL;
1041
1042 if (loginArray && CFArrayGetCount(loginArray) > 0)
1043 {
1044 CFDictionaryRef loginDict = reinterpret_cast<CFDictionaryRef>(CFArrayGetValueAtIndex(loginArray, 0));
1045 try
1046 {
1047 secdebug("secpref", "Getting login DLDbIdentifier from loginDict");
1048 mLoginDLDbIdentifier = cfDictionaryRefToDLDbIdentifier(loginDict);
1049 secdebug("secpref", "we think the login keychain is %s", static_cast<bool>(mLoginDLDbIdentifier) ? mLoginDLDbIdentifier.dbName() : "<NULL>");
1050 }
1051 catch (...)
1052 {
1053 // If loginArray doesn't parse fall back on the default way of getting the login keychain.
1054 loginArray = NULL;
1055 }
1056 }
1057
1058 if (!loginArray)
1059 {
1060 mLoginDLDbIdentifier = LoginDLDbIdentifier();
1061 secdebug("secpref", "after LoginDLDbIdentifier(), we think the login keychain is %s", static_cast<bool>(mLoginDLDbIdentifier) ? mLoginDLDbIdentifier.dbName() : "<NULL>");
1062 }
1063
1064 mLoginDLDbIdentifierSet = true;
1065 }
1066
1067 return mLoginDLDbIdentifier;
1068 }