]> git.saurik.com Git - apple/security.git/blob - OSX/libsecurity_keychain/lib/DLDBListCFPref.cpp
Security-57337.20.44.tar.gz
[apple/security.git] / OSX / libsecurity_keychain / lib / DLDBListCFPref.cpp
1 /*
2 * Copyright (c) 2000-2004,2011-2014 Apple 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, (size_t)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 status = sandbox_release_fs_extension(xpc_string_get_string_ptr(extension));
682 if (status) {
683 syslog(LOG_ERR, "Keychain sandbox release extension error: s=%d p=%s %m\n", status, path);
684 }
685
686 return (bool)true;
687 });
688
689 KeychainHomeFromXPC = xpc_dictionary_get_value(reply, "keychain-home");
690 xpc_retain(KeychainHomeFromXPC);
691 xpc_release(con);
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));
694 } else {
695 syslog(LOG_ERR, "Keychain sandbox unexpected message reply type %p\n", xtype);
696 }
697 xpc_release(message);
698 xpc_release(reply);
699 }
700
701
702
703 string DLDbListCFPref::ExpandTildesInPath(const string &inPath)
704 {
705 dispatch_once(&AppSandboxChecked, ^{
706 check_app_sandbox();
707 });
708
709 if ((short)inPath.find("~/",0,2) == 0)
710 return getPwInfo(kHomeDir) + inPath.substr(1, inPath.length() - 1);
711 else
712 return inPath;
713 }
714
715 string DLDbListCFPref::StripPathStuff(const string &inPath)
716 {
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);
729 return inPath;
730 }
731
732 string DLDbListCFPref::AbbreviatedPath(const string &inPath)
733 {
734 string path = StripPathStuff(inPath);
735 string home = StripPathStuff(getPwInfo(kHomeDir) + "/");
736 size_t homeLen = home.length();
737
738 if (homeLen > 1 && path.find(home.c_str(), 0, homeLen) == 0)
739 return "~" + path.substr(homeLen - 1);
740 else
741 return path;
742 }
743
744 CFDictionaryRef DLDbListCFPref::dlDbIdentifierToCFDictionaryRef(const DLDbIdentifier& dldbIdentifier)
745 {
746 CFRef<CFMutableDictionaryRef> aDict(CFDictionaryCreateMutable(kCFAllocatorDefault,0,
747 &kCFTypeDictionaryKeyCallBacks,&kCFTypeDictionaryValueCallBacks));
748 if (!aDict)
749 throw ::std::bad_alloc();
750
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));
757 if (stringGuid)
758 ::CFDictionarySetValue(aDict,kKeyGUID,stringGuid);
759
760 if (ssuid.SubserviceId!=0)
761 {
762 CFRef<CFNumberRef> subserviceId(::CFNumberCreate(kCFAllocatorDefault,kCFNumberSInt32Type,&ssuid.SubserviceId));
763 if (subserviceId)
764 ::CFDictionarySetValue(aDict,kKeySubserviceId,subserviceId);
765 }
766 if (ssuid.SubserviceType!=0)
767 {
768 CFRef<CFNumberRef> subserviceType(CFNumberCreate(kCFAllocatorDefault,kCFNumberSInt32Type,&ssuid.SubserviceType));
769 if (subserviceType)
770 ::CFDictionarySetValue(aDict,kKeySubserviceType,subserviceType);
771 }
772 if (ssuid.Version.Major!=0 && ssuid.Version.Minor!=0)
773 {
774 CFRef<CFNumberRef> majorVersion(::CFNumberCreate(kCFAllocatorDefault,kCFNumberSInt32Type,&ssuid.Version.Major));
775 if (majorVersion)
776 ::CFDictionarySetValue(aDict,kKeyMajorVersion,majorVersion);
777 CFRef<CFNumberRef> minorVersion(::CFNumberCreate(kCFAllocatorDefault,kCFNumberSInt32Type,&ssuid.Version.Minor));
778 if (minorVersion)
779 ::CFDictionarySetValue(aDict,kKeyMinorVersion,minorVersion);
780 }
781
782 // Put DbName in dictionary
783 const char *dbName=dldbIdentifier.dbName();
784 if (dbName)
785 {
786 CFRef<CFStringRef> theDbName(::CFStringCreateWithCString(kCFAllocatorDefault,AbbreviatedPath(dbName).c_str(),kCFStringEncodingUTF8));
787 ::CFDictionarySetValue(aDict,kKeyDbName,theDbName);
788 }
789 // Put DbLocation in dictionary
790 const CSSM_NET_ADDRESS *dbLocation=dldbIdentifier.dbLocation();
791 if (dbLocation!=NULL && dbLocation->AddressType!=CSSM_ADDR_NONE)
792 {
793 CFRef<CFDataRef> theData(::CFDataCreate(kCFAllocatorDefault,dbLocation->Address.Data,dbLocation->Address.Length));
794 if (theData)
795 ::CFDictionarySetValue(aDict,kKeyDbLocation,theData);
796 }
797
798 ::CFRetain(aDict);
799 return aDict;
800 }
801
802 bool DLDbListCFPref::revert(bool force)
803 {
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))
807 return false;
808
809 resetCachedValues();
810 return true;
811 }
812
813 void
814 DLDbListCFPref::add(const DLDbIdentifier &dldbIdentifier)
815 {
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());
819
820 DLDbIdentifier localIdentifier (dldbIdentifier.ssuid(), canon.c_str(), dldbIdentifier.dbLocation ());
821
822 if (member(localIdentifier))
823 return;
824
825 mSearchList.push_back(localIdentifier);
826 changed(true);
827 }
828
829 void
830 DLDbListCFPref::remove(const DLDbIdentifier &dldbIdentifier)
831 {
832 // Make sure mSearchList is set
833 searchList();
834 for (vector<DLDbIdentifier>::iterator ix = mSearchList.begin(); ix != mSearchList.end(); ++ix)
835 {
836 if (*ix==dldbIdentifier) // found in list
837 {
838 mSearchList.erase(ix);
839 changed(true);
840 break;
841 }
842 }
843 }
844
845 void
846 DLDbListCFPref::rename(const DLDbIdentifier &oldId, const DLDbIdentifier &newId)
847 {
848 // Make sure mSearchList is set
849 searchList();
850 for (vector<DLDbIdentifier>::iterator ix = mSearchList.begin();
851 ix != mSearchList.end(); ++ix)
852 {
853 if (*ix==oldId)
854 {
855 // replace oldId with newId
856 *ix = newId;
857 changed(true);
858 }
859 else if (*ix==newId)
860 {
861 // remove newId except where we just inserted it
862 mSearchList.erase(ix);
863 changed(true);
864 }
865 }
866 }
867
868 bool
869 DLDbListCFPref::member(const DLDbIdentifier &dldbIdentifier)
870 {
871 if (dldbIdentifier.IsImplEmpty())
872 {
873 return false;
874 }
875
876 for (vector<DLDbIdentifier>::const_iterator ix = searchList().begin(); ix != mSearchList.end(); ++ix)
877 {
878 if (ix->mImpl == NULL)
879 {
880 continue;
881 }
882
883 // compare the dldbIdentifiers based on the full, real path to the keychain
884 if (ix->ssuid() == dldbIdentifier.ssuid())
885 {
886 char localPath[PATH_MAX],
887 inPath[PATH_MAX];
888
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);
892
893 // if either of the paths didn't resolve for some reason, use the originals
894 if (localPathPtr == NULL)
895 {
896 localPathPtr = ix->dbName();
897 }
898
899 if (inPathPtr == NULL)
900 {
901 inPathPtr = dldbIdentifier.dbName();
902 }
903
904 if (strcmp(localPathPtr, inPathPtr) == 0)
905 {
906 return true;
907 }
908 }
909 }
910
911 return false;
912 }
913
914 const vector<DLDbIdentifier> &
915 DLDbListCFPref::searchList()
916 {
917 if (!mSearchListSet)
918 {
919 CFArrayRef searchList = reinterpret_cast<CFArrayRef>(CFDictionaryGetValue(mPropertyList, kDefaultDLDbListKey));
920 if (searchList && CFGetTypeID(searchList) != CFArrayGetTypeID())
921 searchList = NULL;
922
923 if (searchList)
924 {
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)
928 {
929 CFDictionaryRef theDict = reinterpret_cast<CFDictionaryRef>(CFArrayGetValueAtIndex(searchList, idx));
930 try
931 {
932 mSearchList.push_back(cfDictionaryRefToDLDbIdentifier(theDict));
933 }
934 catch (...)
935 {
936 // Drop stuff that doesn't parse on the floor.
937 }
938 }
939
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)
943 searchList = NULL;
944 }
945
946 // The default when no search list is specified is to only search the
947 // default keychain.
948 if (!searchList && static_cast<bool>(defaultDLDbIdentifier()))
949 mSearchList.push_back(mDefaultDLDbIdentifier);
950
951 mSearchListSet = true;
952 }
953
954 return mSearchList;
955 }
956
957 void
958 DLDbListCFPref::searchList(const vector<DLDbIdentifier> &searchList)
959 {
960 vector<DLDbIdentifier> newList(searchList);
961 mSearchList.swap(newList);
962 mSearchListSet = true;
963 changed(true);
964 }
965
966 void
967 DLDbListCFPref::defaultDLDbIdentifier(const DLDbIdentifier &dlDbIdentifier)
968 {
969 if (!(defaultDLDbIdentifier() == dlDbIdentifier))
970 {
971 mDefaultDLDbIdentifier = dlDbIdentifier;
972 changed(true);
973 }
974 }
975
976 const DLDbIdentifier &
977 DLDbListCFPref::defaultDLDbIdentifier()
978 {
979
980 if (!mDefaultDLDbIdentifierSet)
981 {
982 CFArrayRef defaultArray = reinterpret_cast<CFArrayRef>(CFDictionaryGetValue(mPropertyList, kDefaultKeychainKey));
983 if (defaultArray && CFGetTypeID(defaultArray) != CFArrayGetTypeID())
984 defaultArray = NULL;
985
986 if (defaultArray && CFArrayGetCount(defaultArray) > 0)
987 {
988 CFDictionaryRef defaultDict = reinterpret_cast<CFDictionaryRef>(CFArrayGetValueAtIndex(defaultArray, 0));
989 try
990 {
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>");
994 }
995 catch (...)
996 {
997 // If defaultArray doesn't parse fall back on the default way of getting the default keychain
998 defaultArray = NULL;
999 }
1000 }
1001
1002 if (!defaultArray)
1003 {
1004
1005 // If the Panther style login keychain actually exists we use that otherwise no
1006 // default is set.
1007 mDefaultDLDbIdentifier = loginDLDbIdentifier();
1008 secdebug("secpref", "now we think the default keychain is: %s", (mDefaultDLDbIdentifier) ? mDefaultDLDbIdentifier.dbName() :
1009 "Name doesn't exist");
1010
1011 struct stat st;
1012 int st_result = -1;
1013
1014 if (mDefaultDLDbIdentifier.mImpl != NULL)
1015 {
1016 st_result = stat(mDefaultDLDbIdentifier.dbName(), &st);
1017 }
1018
1019 if (st_result)
1020 {
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>");
1024 }
1025 }
1026
1027 mDefaultDLDbIdentifierSet = true;
1028 }
1029
1030
1031 return mDefaultDLDbIdentifier;
1032 }
1033
1034 void
1035 DLDbListCFPref::loginDLDbIdentifier(const DLDbIdentifier &dlDbIdentifier)
1036 {
1037 if (!(loginDLDbIdentifier() == dlDbIdentifier))
1038 {
1039 mLoginDLDbIdentifier = dlDbIdentifier;
1040 changed(true);
1041 }
1042 }
1043
1044 const DLDbIdentifier &
1045 DLDbListCFPref::loginDLDbIdentifier()
1046 {
1047 if (!mLoginDLDbIdentifierSet)
1048 {
1049 CFArrayRef loginArray = reinterpret_cast<CFArrayRef>(CFDictionaryGetValue(mPropertyList, kLoginKeychainKey));
1050 if (loginArray && CFGetTypeID(loginArray) != CFArrayGetTypeID())
1051 loginArray = NULL;
1052
1053 if (loginArray && CFArrayGetCount(loginArray) > 0)
1054 {
1055 CFDictionaryRef loginDict = reinterpret_cast<CFDictionaryRef>(CFArrayGetValueAtIndex(loginArray, 0));
1056 try
1057 {
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>");
1061 }
1062 catch (...)
1063 {
1064 // If loginArray doesn't parse fall back on the default way of getting the login keychain.
1065 loginArray = NULL;
1066 }
1067 }
1068
1069 if (!loginArray)
1070 {
1071 mLoginDLDbIdentifier = LoginDLDbIdentifier();
1072 secdebug("secpref", "after LoginDLDbIdentifier(), we think the login keychain is %s", static_cast<bool>(mLoginDLDbIdentifier) ? mLoginDLDbIdentifier.dbName() : "<NULL>");
1073 }
1074
1075 mLoginDLDbIdentifierSet = true;
1076 }
1077
1078 return mLoginDLDbIdentifier;
1079 }