1/* 2 * Copyright(c) 2000-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 * Modification History 26 * 27 * February 16, 2004 Allan Nathanson <ajn@apple.com> 28 * - add preference notification APIs 29 * 30 * June 1, 2001 Allan Nathanson <ajn@apple.com> 31 * - public API conversion 32 * 33 * November 9, 2000 Allan Nathanson <ajn@apple.com> 34 * - initial revision 35 */ 36 37#include <Availability.h> 38#include <TargetConditionals.h> 39#include <sys/cdefs.h> 40#include <dispatch/dispatch.h> 41#include <SystemConfiguration/SystemConfiguration.h> 42#include <SystemConfiguration/SCValidation.h> 43#include <SystemConfiguration/SCPrivate.h> 44#include "SCPreferencesInternal.h" 45#include "SCHelper_client.h" 46 47#include "dy_framework.h" 48 49#include <fcntl.h> 50#include <pthread.h> 51#include <sandbox.h> 52#include <unistd.h> 53#include <sys/errno.h> 54 55 56const AuthorizationRef kSCPreferencesUseEntitlementAuthorization = (AuthorizationRef)CFSTR("UseEntitlement"); 57 58 59static __inline__ CFTypeRef 60isA_SCPreferences(CFTypeRef obj) 61{ 62 return (isA_CFType(obj, SCPreferencesGetTypeID())); 63} 64 65 66static CFStringRef 67__SCPreferencesCopyDescription(CFTypeRef cf) { 68 CFAllocatorRef allocator = CFGetAllocator(cf); 69 SCPreferencesPrivateRef prefsPrivate = (SCPreferencesPrivateRef)cf; 70 CFMutableStringRef result; 71 72 result = CFStringCreateMutable(allocator, 0); 73 CFStringAppendFormat(result, NULL, CFSTR("<SCPreferences %p [%p]> {"), cf, allocator); 74 CFStringAppendFormat(result, NULL, CFSTR("name = %@"), prefsPrivate->name); 75 CFStringAppendFormat(result, NULL, CFSTR(", id = %@"), prefsPrivate->prefsID); 76 CFStringAppendFormat(result, NULL, CFSTR(", path = %s"), 77 prefsPrivate->newPath ? prefsPrivate->newPath : prefsPrivate->path); 78 if (prefsPrivate->accessed) { 79 CFStringAppendFormat(result, NULL, CFSTR(", accessed")); 80 } 81 if (prefsPrivate->changed) { 82 CFStringAppendFormat(result, NULL, CFSTR(", changed")); 83 } 84 if (prefsPrivate->locked) { 85 CFStringAppendFormat(result, NULL, CFSTR(", locked")); 86 } 87 if (prefsPrivate->helper_port != MACH_PORT_NULL) { 88 CFStringAppendFormat(result, NULL, CFSTR(", helper port = 0x%x"), prefsPrivate->helper_port); 89 } 90 CFStringAppendFormat(result, NULL, CFSTR("}")); 91 92 return result; 93} 94 95 96static void 97__SCPreferencesDeallocate(CFTypeRef cf) 98{ 99 SCPreferencesPrivateRef prefsPrivate = (SCPreferencesPrivateRef)cf; 100 101 /* release resources */ 102 103 pthread_mutex_destroy(&prefsPrivate->lock); 104 105 if (prefsPrivate->name) CFRelease(prefsPrivate->name); 106 if (prefsPrivate->prefsID) CFRelease(prefsPrivate->prefsID); 107 if (prefsPrivate->options) CFRelease(prefsPrivate->options); 108 if (prefsPrivate->path) CFAllocatorDeallocate(NULL, prefsPrivate->path); 109 if (prefsPrivate->newPath) CFAllocatorDeallocate(NULL, prefsPrivate->newPath); 110 if (prefsPrivate->lockFD != -1) { 111 if (prefsPrivate->lockPath != NULL) { 112 unlink(prefsPrivate->lockPath); 113 } 114 close(prefsPrivate->lockFD); 115 } 116 if (prefsPrivate->lockPath) CFAllocatorDeallocate(NULL, prefsPrivate->lockPath); 117 if (prefsPrivate->signature) CFRelease(prefsPrivate->signature); 118 if (prefsPrivate->session) CFRelease(prefsPrivate->session); 119 if (prefsPrivate->sessionKeyLock) CFRelease(prefsPrivate->sessionKeyLock); 120 if (prefsPrivate->sessionKeyCommit) CFRelease(prefsPrivate->sessionKeyCommit); 121 if (prefsPrivate->sessionKeyApply) CFRelease(prefsPrivate->sessionKeyApply); 122 if (prefsPrivate->rlsContext.release != NULL) { 123 (*prefsPrivate->rlsContext.release)(prefsPrivate->rlsContext.info); 124 } 125 if (prefsPrivate->prefs) CFRelease(prefsPrivate->prefs); 126 if (prefsPrivate->authorizationData != NULL) CFRelease(prefsPrivate->authorizationData); 127 if (prefsPrivate->helper_port != MACH_PORT_NULL) { 128 (void) _SCHelperExec(prefsPrivate->helper_port, 129 SCHELPER_MSG_PREFS_CLOSE, 130 NULL, 131 NULL, 132 NULL); 133 _SCHelperClose(&prefsPrivate->helper_port); 134 } 135 136 return; 137} 138 139 140static CFTypeID __kSCPreferencesTypeID = _kCFRuntimeNotATypeID; 141 142 143static const CFRuntimeClass __SCPreferencesClass = { 144 0, // version 145 "SCPreferences", // className 146 NULL, // init 147 NULL, // copy 148 __SCPreferencesDeallocate, // dealloc 149 NULL, // equal 150 NULL, // hash 151 NULL, // copyFormattingDesc 152 __SCPreferencesCopyDescription // copyDebugDesc 153}; 154 155 156static pthread_once_t initialized = PTHREAD_ONCE_INIT; 157 158static void 159__SCPreferencesInitialize(void) { 160 __kSCPreferencesTypeID = _CFRuntimeRegisterClass(&__SCPreferencesClass); 161 return; 162} 163 164 165static SCPreferencesPrivateRef 166__SCPreferencesCreatePrivate(CFAllocatorRef allocator) 167{ 168 SCPreferencesPrivateRef prefsPrivate; 169 uint32_t size; 170 171 /* initialize runtime */ 172 pthread_once(&initialized, __SCPreferencesInitialize); 173 174 /* allocate prefs session */ 175 size = sizeof(SCPreferencesPrivate) - sizeof(CFRuntimeBase); 176 prefsPrivate = (SCPreferencesPrivateRef)_CFRuntimeCreateInstance(allocator, 177 __kSCPreferencesTypeID, 178 size, 179 NULL); 180 if (prefsPrivate == NULL) { 181 return NULL; 182 } 183 184 pthread_mutex_init(&prefsPrivate->lock, NULL); 185 186 prefsPrivate->name = NULL; 187 prefsPrivate->prefsID = NULL; 188 prefsPrivate->options = NULL; 189 prefsPrivate->path = NULL; 190 prefsPrivate->newPath = NULL; // new prefs path 191 prefsPrivate->locked = FALSE; 192 prefsPrivate->lockFD = -1; 193 prefsPrivate->lockPath = NULL; 194 prefsPrivate->signature = NULL; 195 prefsPrivate->session = NULL; 196 prefsPrivate->sessionKeyLock = NULL; 197 prefsPrivate->sessionKeyCommit = NULL; 198 prefsPrivate->sessionKeyApply = NULL; 199 prefsPrivate->scheduled = FALSE; 200 prefsPrivate->rls = NULL; 201 prefsPrivate->rlsFunction = NULL; 202 prefsPrivate->rlsContext.info = NULL; 203 prefsPrivate->rlsContext.retain = NULL; 204 prefsPrivate->rlsContext.release = NULL; 205 prefsPrivate->rlsContext.copyDescription = NULL; 206 prefsPrivate->rlList = NULL; 207 prefsPrivate->dispatchQueue = NULL; 208 prefsPrivate->prefs = NULL; 209 prefsPrivate->accessed = FALSE; 210 prefsPrivate->changed = FALSE; 211 prefsPrivate->isRoot = (geteuid() == 0); 212 prefsPrivate->limit_SCNetworkConfiguration = FALSE; 213 prefsPrivate->authorizationData = NULL; 214 prefsPrivate->authorizationRequired = FALSE; 215 prefsPrivate->helper_port = MACH_PORT_NULL; 216 217 return prefsPrivate; 218} 219 220 221__private_extern__ Boolean 222__SCPreferencesCreate_helper(SCPreferencesRef prefs) 223{ 224 CFDataRef data = NULL; 225 CFMutableDictionaryRef info; 226 CFNumberRef num; 227 Boolean ok; 228 SCPreferencesPrivateRef prefsPrivate = (SCPreferencesPrivateRef)prefs; 229 uint32_t status = kSCStatusOK; 230 CFStringRef str; 231 uint32_t pid = getpid(); 232 233 // start helper 234 ok = _SCHelperOpen(prefsPrivate->authorizationData, 235 &prefsPrivate->helper_port); 236 if (!ok) { 237 goto fail; 238 } 239 240 // create a dictionary of information to pass to the helper 241 info = CFDictionaryCreateMutable(NULL, 242 0, 243 &kCFTypeDictionaryKeyCallBacks, 244 &kCFTypeDictionaryValueCallBacks); 245 246 // save prefsID 247 if (prefsPrivate->prefsID != NULL) { 248 CFDictionarySetValue(info, CFSTR("prefsID"), prefsPrivate->prefsID); 249 } 250 251 // save options 252 if (prefsPrivate->options != NULL) { 253 CFDictionarySetValue(info, CFSTR("options"), prefsPrivate->options); 254 } 255 256 // save preferences session "name" 257 CFDictionarySetValue(info, CFSTR("name"), prefsPrivate->name); 258 259 // save PID 260 num = CFNumberCreate(NULL, kCFNumberSInt32Type, &pid); 261 CFDictionarySetValue(info, CFSTR("PID"), num); 262 CFRelease(num); 263 264 // save process name 265 str = CFStringCreateWithCString(NULL, getprogname(), kCFStringEncodingUTF8); 266 CFDictionarySetValue(info, CFSTR("PROC_NAME"), str); 267 CFRelease(str); 268 269 // serialize the info 270 ok = _SCSerialize(info, &data, NULL, NULL); 271 CFRelease(info); 272 if (data == NULL || !ok) { 273 goto fail; 274 } 275 276 // have the helper "open" the prefs 277 ok = _SCHelperExec(prefsPrivate->helper_port, 278 SCHELPER_MSG_PREFS_OPEN, 279 data, 280 &status, 281 NULL); 282 if (data != NULL) CFRelease(data); 283 if (!ok) { 284 goto fail; 285 } 286 287 if (status != kSCStatusOK) { 288 goto error; 289 } 290 291 return TRUE; 292 293 fail : 294 295 // close helper 296 if (prefsPrivate->helper_port != MACH_PORT_NULL) { 297 _SCHelperClose(&prefsPrivate->helper_port); 298 } 299 300 status = kSCStatusAccessError; 301 302 error : 303 304 // return error 305 _SCErrorSet(status); 306 return FALSE; 307} 308 309 310static Boolean 311__SCPreferencesAccess_helper(SCPreferencesRef prefs) 312{ 313 Boolean ok; 314 SCPreferencesPrivateRef prefsPrivate = (SCPreferencesPrivateRef)prefs; 315 CFDictionaryRef serverDict = NULL; 316 CFDictionaryRef serverPrefs = NULL; 317 CFDictionaryRef serverSignature = NULL; 318 uint32_t status = kSCStatusOK; 319 CFDataRef reply = NULL; 320 321 if (prefsPrivate->helper_port == MACH_PORT_NULL) { 322 ok = __SCPreferencesCreate_helper(prefs); 323 if (!ok) { 324 return FALSE; 325 } 326 } 327 328 // have the helper "access" the prefs 329 ok = _SCHelperExec(prefsPrivate->helper_port, 330 SCHELPER_MSG_PREFS_ACCESS, 331 NULL, 332 &status, 333 &reply); 334 if (!ok) { 335 goto fail; 336 } 337 338 if (status != kSCStatusOK) { 339 goto error; 340 } 341 342 if (reply == NULL) { 343 goto fail; 344 } 345 346 ok = _SCUnserialize((CFPropertyListRef *)&serverDict, reply, NULL, 0); 347 CFRelease(reply); 348 if (!ok) { 349 goto fail; 350 } 351 352 if (isA_CFDictionary(serverDict)) { 353 serverPrefs = CFDictionaryGetValue(serverDict, CFSTR("preferences")); 354 serverPrefs = isA_CFDictionary(serverPrefs); 355 356 serverSignature = CFDictionaryGetValue(serverDict, CFSTR("signature")); 357 serverSignature = isA_CFData(serverSignature); 358 } 359 360 if ((serverPrefs == NULL) || (serverSignature == NULL)) { 361 if (serverDict != NULL) CFRelease(serverDict); 362 goto fail; 363 } 364 365 prefsPrivate->prefs = CFDictionaryCreateMutableCopy(NULL, 0, serverPrefs); 366 prefsPrivate->signature = CFRetain(serverSignature); 367 prefsPrivate->accessed = TRUE; 368 CFRelease(serverDict); 369 370 return TRUE; 371 372 fail : 373 374 // close helper 375 if (prefsPrivate->helper_port != MACH_PORT_NULL) { 376 _SCHelperClose(&prefsPrivate->helper_port); 377 } 378 379 status = kSCStatusAccessError; 380 381 error : 382 383 // return error 384 _SCErrorSet(status); 385 return FALSE; 386} 387 388 389static SCPreferencesPrivateRef 390__SCPreferencesCreate(CFAllocatorRef allocator, 391 CFStringRef name, 392 CFStringRef prefsID, 393 CFDataRef authorizationData, 394 CFDictionaryRef options) 395{ 396 SCPreferencesPrivateRef prefsPrivate; 397 int sc_status = kSCStatusOK; 398 399 /* 400 * allocate and initialize a new prefs session 401 */ 402 prefsPrivate = __SCPreferencesCreatePrivate(allocator); 403 if (prefsPrivate == NULL) { 404 return NULL; 405 } 406 407 prefsPrivate->name = CFStringCreateCopy(allocator, name); 408 if (prefsID != NULL) { 409 prefsPrivate->prefsID = CFStringCreateCopy(allocator, prefsID); 410 } 411 if (authorizationData != NULL) { 412 prefsPrivate->authorizationData = CFRetain(authorizationData); 413 } 414 if (options != NULL) { 415 prefsPrivate->options = CFDictionaryCreateCopy(allocator, options); 416 } 417 418 retry : 419 420 /* 421 * convert prefsID to path 422 */ 423 prefsPrivate->path = __SCPreferencesPath(allocator, 424 prefsID, 425 (prefsPrivate->newPath == NULL)); 426 if (prefsPrivate->path == NULL) { 427 sc_status = kSCStatusFailed; 428 goto error; 429 } 430 431 if (access(prefsPrivate->path, R_OK) == 0) { 432 goto done; 433 } 434 435 switch (errno) { 436 case ENOENT : 437 /* no prefs file */ 438 if ((prefsID == NULL) || !CFStringHasPrefix(prefsID, CFSTR("/"))) { 439 /* if default preference ID or relative path */ 440 if (prefsPrivate->newPath == NULL) { 441 /* 442 * we've looked in the "new" prefs directory 443 * without success. Save the "new" path and 444 * look in the "old" prefs directory. 445 */ 446 prefsPrivate->newPath = prefsPrivate->path; 447 goto retry; 448 } else { 449 /* 450 * we've looked in both the "new" and "old" 451 * prefs directories without success. Use 452 * the "new" path. 453 */ 454 CFAllocatorDeallocate(NULL, prefsPrivate->path); 455 prefsPrivate->path = prefsPrivate->newPath; 456 prefsPrivate->newPath = NULL; 457 } 458 } 459 460 /* no preference data, start fresh */ 461 sc_status = kSCStatusNoConfigFile; 462 goto done; 463 case EPERM : 464 case EACCES : 465 if (prefsPrivate->authorizationData != NULL) { 466 /* no problem, we'll be using the helper */ 467 goto done; 468 } 469 470 SCLog(_sc_verbose, LOG_DEBUG, CFSTR("__SCPreferencesCreate open() failed: %s"), strerror(errno)); 471 sc_status = kSCStatusAccessError; 472 break; 473 default : 474 SCLog(TRUE, LOG_ERR, CFSTR("__SCPreferencesCreate open() failed: %s"), strerror(errno)); 475 sc_status = kSCStatusFailed; 476 break; 477 } 478 479 error: 480 481 CFRelease(prefsPrivate); 482 _SCErrorSet(sc_status); 483 return NULL; 484 485 done : 486 487 /* all OK */ 488 _SCErrorSet(sc_status); 489 return prefsPrivate; 490} 491 492 493__private_extern__ void 494__SCPreferencesAccess(SCPreferencesRef prefs) 495{ 496 CFAllocatorRef allocator = CFGetAllocator(prefs); 497 int fd = -1; 498 SCPreferencesPrivateRef prefsPrivate = (SCPreferencesPrivateRef)prefs; 499 struct stat statBuf; 500 501 if (prefsPrivate->accessed) { 502 // if preference data has already been accessed 503 return; 504 } 505 506 if (!prefsPrivate->authorizationRequired) { 507 if (access(prefsPrivate->path, R_OK) == 0) { 508 fd = open(prefsPrivate->path, O_RDONLY, 0644); 509 } else { 510 fd = -1; 511 } 512 } else { 513 errno = EACCES; 514 } 515 if (fd != -1) { 516 // create signature 517 if (fstat(fd, &statBuf) == -1) { 518 SCLog(TRUE, LOG_ERR, CFSTR("__SCPreferencesAccess fstat() failed: %s"), strerror(errno)); 519 bzero(&statBuf, sizeof(statBuf)); 520 } 521 } else { 522 switch (errno) { 523 case ENOENT : 524 /* no preference data, start fresh */ 525 break; 526 case EPERM : 527 case EACCES : 528 if (prefsPrivate->authorizationData != NULL) { 529 if (__SCPreferencesAccess_helper(prefs)) { 530 goto done; 531 } else { 532 SCLog(TRUE, LOG_ERR, 533 CFSTR("__SCPreferencesAccess_helper() failed: %s"), 534 SCErrorString(SCError())); 535 } 536 break; 537 } 538 // fall through 539 default : 540 SCLog(TRUE, LOG_ERR, CFSTR("__SCPreferencesAccess open() failed: %s"), strerror(errno)); 541 break; 542 } 543 bzero(&statBuf, sizeof(statBuf)); 544 } 545 546 if (prefsPrivate->signature != NULL) CFRelease(prefsPrivate->signature); 547 prefsPrivate->signature = __SCPSignatureFromStatbuf(&statBuf); 548 549 if (statBuf.st_size > 0) { 550 CFDictionaryRef dict; 551 CFErrorRef error = NULL; 552 CFMutableDataRef xmlData; 553 554 /* 555 * extract property list 556 */ 557 xmlData = CFDataCreateMutable(allocator, (CFIndex)statBuf.st_size); 558 CFDataSetLength(xmlData, (CFIndex)statBuf.st_size); 559 if (read(fd, (void *)CFDataGetBytePtr(xmlData), (CFIndex)statBuf.st_size) != (CFIndex)statBuf.st_size) { 560 /* corrupt prefs file, start fresh */ 561 SCLog(_sc_verbose, LOG_DEBUG, CFSTR("__SCPreferencesAccess read(): could not load preference data.")); 562 CFRelease(xmlData); 563 xmlData = NULL; 564 goto done; 565 } 566 567 /* 568 * load preferences 569 */ 570 dict = CFPropertyListCreateWithData(allocator, xmlData, kCFPropertyListImmutable, NULL, &error); 571 CFRelease(xmlData); 572 if (dict == NULL) { 573 /* corrupt prefs file, start fresh */ 574 if (error != NULL) { 575 SCLog(TRUE, LOG_ERR, 576 CFSTR("__SCPreferencesAccess CFPropertyListCreateWithData(): %@"), 577 error); 578 CFRelease(error); 579 } 580 goto done; 581 } 582 583 /* 584 * make sure that we've got a dictionary 585 */ 586 if (!isA_CFDictionary(dict)) { 587 /* corrupt prefs file, start fresh */ 588 SCLog(_sc_verbose, LOG_DEBUG, CFSTR("__SCPreferencesAccess CFGetTypeID(): not a dictionary.")); 589 CFRelease(dict); 590 goto done; 591 } 592 593 prefsPrivate->prefs = CFDictionaryCreateMutableCopy(allocator, 0, dict); 594 CFRelease(dict); 595 } 596 597 done : 598 599 if (fd != -1) { 600 (void) close(fd); 601 } 602 603 if (prefsPrivate->prefs == NULL) { 604 /* 605 * new file, create empty preferences 606 */ 607// SCLog(_sc_verbose, LOG_DEBUG, CFSTR("__SCPreferencesAccess(): creating new preferences file.")); 608 prefsPrivate->prefs = CFDictionaryCreateMutable(allocator, 609 0, 610 &kCFTypeDictionaryKeyCallBacks, 611 &kCFTypeDictionaryValueCallBacks); 612 prefsPrivate->changed = TRUE; 613 } 614 615 prefsPrivate->accessed = TRUE; 616 return; 617} 618 619 620SCPreferencesRef 621SCPreferencesCreate(CFAllocatorRef allocator, 622 CFStringRef name, 623 CFStringRef prefsID) 624{ 625 SCPreferencesPrivateRef prefsPrivate; 626 627 prefsPrivate = __SCPreferencesCreate(allocator, name, prefsID, NULL, NULL); 628 return (SCPreferencesRef)prefsPrivate; 629} 630 631 632SCPreferencesRef 633SCPreferencesCreateWithAuthorization(CFAllocatorRef allocator, 634 CFStringRef name, 635 CFStringRef prefsID, 636 AuthorizationRef authorization) 637{ 638 SCPreferencesRef prefs; 639 640#if !TARGET_OS_IPHONE 641 if (authorization == NULL) { 642 authorization = kSCPreferencesUseEntitlementAuthorization; 643 } 644#else // !TARGET_OS_IPHONE 645 authorization = kSCPreferencesUseEntitlementAuthorization; 646#endif // !TARGET_OS_IPHONE 647 648 prefs = SCPreferencesCreateWithOptions(allocator, name, prefsID, authorization, NULL); 649 return prefs; 650} 651 652 653SCPreferencesRef 654SCPreferencesCreateWithOptions(CFAllocatorRef allocator, 655 CFStringRef name, 656 CFStringRef prefsID, 657 AuthorizationRef authorization, 658 CFDictionaryRef options) 659{ 660 CFDataRef authorizationData = NULL; 661 SCPreferencesPrivateRef prefsPrivate; 662 663 if (options != NULL) { 664 if (!isA_CFDictionary(options)) { 665 _SCErrorSet(kSCStatusInvalidArgument); 666 return NULL; 667 } 668 } 669 670 if (authorization != NULL) { 671 CFMutableDictionaryRef authorizationDict; 672 CFBundleRef bundle; 673 CFStringRef bundleID = NULL; 674 675 authorizationDict = CFDictionaryCreateMutable(NULL, 676 0, 677 &kCFTypeDictionaryKeyCallBacks, 678 &kCFTypeDictionaryValueCallBacks); 679#if !TARGET_OS_IPHONE 680 if (authorization != kSCPreferencesUseEntitlementAuthorization) { 681 CFDataRef data; 682 AuthorizationExternalForm extForm; 683 OSStatus os_status; 684 685 os_status = AuthorizationMakeExternalForm(authorization, &extForm); 686 if (os_status != errAuthorizationSuccess) { 687 SCLog(TRUE, LOG_INFO, CFSTR("_SCHelperOpen AuthorizationMakeExternalForm() failed")); 688 _SCErrorSet(kSCStatusInvalidArgument); 689 CFRelease(authorizationDict); 690 return NULL; 691 } 692 693 data = CFDataCreate(NULL, (const UInt8 *)extForm.bytes, sizeof(extForm.bytes)); 694 CFDictionaryAddValue(authorizationDict, 695 kSCHelperAuthAuthorization, 696 data); 697 CFRelease(data); 698 } 699#endif 700 701 /* get the application/executable/bundle name */ 702 bundle = CFBundleGetMainBundle(); 703 if (bundle != NULL) { 704 bundleID = CFBundleGetIdentifier(bundle); 705 if (bundleID != NULL) { 706 CFRetain(bundleID); 707 } else { 708 CFURLRef url; 709 710 url = CFBundleCopyExecutableURL(bundle); 711 if (url != NULL) { 712 bundleID = CFURLCopyPath(url); 713 CFRelease(url); 714 } 715 } 716 717 if (bundleID != NULL) { 718 if (CFEqual(bundleID, CFSTR("/"))) { 719 CFRelease(bundleID); 720 bundleID = NULL; 721 } 722 } 723 } 724 if (bundleID == NULL) { 725 bundleID = CFStringCreateWithFormat(NULL, NULL, CFSTR("Unknown(%d)"), getpid()); 726 } 727 CFDictionaryAddValue(authorizationDict, 728 kSCHelperAuthCallerInfo, 729 bundleID); 730 CFRelease(bundleID); 731 732 if (authorizationDict != NULL) { 733 _SCSerialize((CFPropertyListRef)authorizationDict, 734 &authorizationData, 735 NULL, 736 NULL); 737 CFRelease(authorizationDict); 738 } 739 } 740 741 prefsPrivate = __SCPreferencesCreate(allocator, name, prefsID, authorizationData, options); 742 if (authorizationData != NULL) CFRelease(authorizationData); 743 744 return (SCPreferencesRef)prefsPrivate; 745} 746 747 748CFTypeID 749SCPreferencesGetTypeID(void) { 750 pthread_once(&initialized, __SCPreferencesInitialize); /* initialize runtime */ 751 return __kSCPreferencesTypeID; 752} 753 754 755static void 756prefsNotify(SCDynamicStoreRef store, CFArrayRef changedKeys, void *info) 757{ 758 void *context_info; 759 void (*context_release)(const void *); 760 CFIndex i; 761 CFIndex n; 762 SCPreferencesNotification notify = 0; 763 SCPreferencesRef prefs = (SCPreferencesRef)info; 764 SCPreferencesPrivateRef prefsPrivate = (SCPreferencesPrivateRef)prefs; 765 SCPreferencesCallBack rlsFunction; 766 767 n = (changedKeys != NULL) ? CFArrayGetCount(changedKeys) : 0; 768 for (i = 0; i < n; i++) { 769 CFStringRef key; 770 771 key = CFArrayGetValueAtIndex(changedKeys, i); 772 if (CFEqual(key, prefsPrivate->sessionKeyCommit)) { 773 // if preferences have been saved 774 notify |= kSCPreferencesNotificationCommit; 775 } 776 if (CFEqual(key, prefsPrivate->sessionKeyApply)) { 777 // if stored preferences should be applied to current configuration 778 notify |= kSCPreferencesNotificationApply; 779 } 780 } 781 782 if (notify == 0) { 783 // if no changes 784 return; 785 } 786 787 pthread_mutex_lock(&prefsPrivate->lock); 788 789 /* callout */ 790 rlsFunction = prefsPrivate->rlsFunction; 791 if (prefsPrivate->rlsContext.retain != NULL) { 792 context_info = (void *)prefsPrivate->rlsContext.retain(prefsPrivate->rlsContext.info); 793 context_release = prefsPrivate->rlsContext.release; 794 } else { 795 context_info = prefsPrivate->rlsContext.info; 796 context_release = NULL; 797 } 798 799 pthread_mutex_unlock(&prefsPrivate->lock); 800 801 if (rlsFunction != NULL) { 802 (*rlsFunction)(prefs, notify, context_info); 803 } 804 805 if (context_release != NULL) { 806 (*context_release)(context_info); 807 } 808 809 return; 810} 811 812 813__private_extern__ Boolean 814__SCPreferencesAddSession(SCPreferencesRef prefs) 815{ 816 CFAllocatorRef allocator = CFGetAllocator(prefs); 817 SCDynamicStoreContext context = { 0 818 , (void *)prefs 819 , NULL 820 , NULL 821 , NULL 822 }; 823 SCPreferencesPrivateRef prefsPrivate = (SCPreferencesPrivateRef)prefs; 824 825 /* establish a dynamic store session */ 826 prefsPrivate->session = SCDynamicStoreCreate(allocator, 827 prefsPrivate->name, 828 prefsNotify, 829 &context); 830 if (prefsPrivate->session == NULL) { 831 SCLog(_sc_verbose, LOG_INFO, CFSTR("__SCPreferencesAddSession SCDynamicStoreCreate() failed")); 832 return FALSE; 833 } 834 835 /* create the session "commit" key */ 836 prefsPrivate->sessionKeyCommit = _SCPNotificationKey(NULL, 837 prefsPrivate->prefsID, 838 kSCPreferencesKeyCommit); 839 840 /* create the session "apply" key */ 841 prefsPrivate->sessionKeyApply = _SCPNotificationKey(NULL, 842 prefsPrivate->prefsID, 843 kSCPreferencesKeyApply); 844 845 return TRUE; 846} 847 848 849Boolean 850SCPreferencesSetCallback(SCPreferencesRef prefs, 851 SCPreferencesCallBack callout, 852 SCPreferencesContext *context) 853{ 854 SCPreferencesPrivateRef prefsPrivate = (SCPreferencesPrivateRef)prefs; 855 856 if (!isA_SCPreferences(prefs)) { 857 /* sorry, you must provide a session */ 858 _SCErrorSet(kSCStatusNoPrefsSession); 859 return FALSE; 860 } 861 862 pthread_mutex_lock(&prefsPrivate->lock); 863 864 if (prefsPrivate->rlsContext.release != NULL) { 865 /* let go of the current context */ 866 (*prefsPrivate->rlsContext.release)(prefsPrivate->rlsContext.info); 867 } 868 869 prefsPrivate->rlsFunction = callout; 870 prefsPrivate->rlsContext.info = NULL; 871 prefsPrivate->rlsContext.retain = NULL; 872 prefsPrivate->rlsContext.release = NULL; 873 prefsPrivate->rlsContext.copyDescription = NULL; 874 if (context != NULL) { 875 bcopy(context, &prefsPrivate->rlsContext, sizeof(SCPreferencesContext)); 876 if (context->retain != NULL) { 877 prefsPrivate->rlsContext.info = (void *)(*context->retain)(context->info); 878 } 879 } 880 881 pthread_mutex_unlock(&prefsPrivate->lock); 882 883 return TRUE; 884} 885 886 887static Boolean 888__SCPreferencesScheduleWithRunLoop(SCPreferencesRef prefs, 889 CFRunLoopRef runLoop, 890 CFStringRef runLoopMode, 891 dispatch_queue_t queue) 892{ 893 Boolean ok = FALSE; 894 SCPreferencesPrivateRef prefsPrivate = (SCPreferencesPrivateRef)prefs; 895 896 pthread_mutex_lock(&prefsPrivate->lock); 897 898 if ((prefsPrivate->dispatchQueue != NULL) || // if we are already scheduled on a dispatch queue 899 ((queue != NULL) && prefsPrivate->scheduled)) { // if we are already scheduled on a CFRunLoop 900 _SCErrorSet(kSCStatusInvalidArgument); 901 goto done; 902 } 903 904 if (!prefsPrivate->scheduled) { 905 CFMutableArrayRef keys; 906 907 if (prefsPrivate->session == NULL) { 908 ok = __SCPreferencesAddSession(prefs); 909 if (!ok) { 910 goto done; 911 } 912 } 913 914 CFRetain(prefs); // hold a reference to the prefs 915 916 keys = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks); 917 CFArrayAppendValue(keys, prefsPrivate->sessionKeyCommit); 918 CFArrayAppendValue(keys, prefsPrivate->sessionKeyApply); 919 (void) SCDynamicStoreSetNotificationKeys(prefsPrivate->session, keys, NULL); 920 CFRelease(keys); 921 922 if (runLoop != NULL) { 923 prefsPrivate->rls = SCDynamicStoreCreateRunLoopSource(NULL, prefsPrivate->session, 0); 924 prefsPrivate->rlList = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks); 925 } 926 927 prefsPrivate->scheduled = TRUE; 928 } 929 930 if (queue != NULL) { 931 ok = SCDynamicStoreSetDispatchQueue(prefsPrivate->session, queue); 932 if (!ok) { 933 prefsPrivate->scheduled = FALSE; 934 (void) SCDynamicStoreSetNotificationKeys(prefsPrivate->session, NULL, NULL); 935 CFRelease(prefs); 936 goto done; 937 } 938 939 prefsPrivate->dispatchQueue = queue; 940 dispatch_retain(prefsPrivate->dispatchQueue); 941 } else { 942 if (!_SC_isScheduled(NULL, runLoop, runLoopMode, prefsPrivate->rlList)) { 943 /* 944 * if we do not already have notifications scheduled with 945 * this runLoop / runLoopMode 946 */ 947 CFRunLoopAddSource(runLoop, prefsPrivate->rls, runLoopMode); 948 } 949 950 _SC_schedule(prefs, runLoop, runLoopMode, prefsPrivate->rlList); 951 } 952 953 ok = TRUE; 954 955 done : 956 957 pthread_mutex_unlock(&prefsPrivate->lock); 958 return ok; 959} 960 961 962static Boolean 963__SCPreferencesUnscheduleFromRunLoop(SCPreferencesRef prefs, 964 CFRunLoopRef runLoop, 965 CFStringRef runLoopMode) 966{ 967 SCPreferencesPrivateRef prefsPrivate = (SCPreferencesPrivateRef)prefs; 968 CFIndex n = 0; 969 Boolean ok = FALSE; 970 971 pthread_mutex_lock(&prefsPrivate->lock); 972 973 if ((runLoop != NULL) && !prefsPrivate->scheduled) { // if we should be scheduled (but are not) 974 _SCErrorSet(kSCStatusInvalidArgument); 975 goto done; 976 } 977 978 if (((runLoop == NULL) && (prefsPrivate->dispatchQueue == NULL)) || // if we should be scheduled on a dispatch queue (but are not) 979 ((runLoop != NULL) && (prefsPrivate->dispatchQueue != NULL))) { // if we should be scheduled on a CFRunLoop (but are scheduled on a dispatch queue) 980 _SCErrorSet(kSCStatusInvalidArgument); 981 goto done; 982 } 983 984 if (runLoop == NULL) { 985 SCDynamicStoreSetDispatchQueue(prefsPrivate->session, NULL); 986 dispatch_release(prefsPrivate->dispatchQueue); 987 prefsPrivate->dispatchQueue = NULL; 988 } else { 989 if (!_SC_unschedule(prefs, runLoop, runLoopMode, prefsPrivate->rlList, FALSE)) { 990 // if not currently scheduled on this runLoop / runLoopMode 991 _SCErrorSet(kSCStatusInvalidArgument); 992 goto done; 993 } 994 995 n = CFArrayGetCount(prefsPrivate->rlList); 996 if (n == 0 || !_SC_isScheduled(NULL, runLoop, runLoopMode, prefsPrivate->rlList)) { 997 /* 998 * if we are no longer scheduled to receive notifications for 999 * this runLoop / runLoopMode 1000 */ 1001 CFRunLoopRemoveSource(runLoop, prefsPrivate->rls, runLoopMode); 1002 1003 if (n == 0) { 1004 // if *all* notifications have been unscheduled 1005 CFRelease(prefsPrivate->rlList); 1006 prefsPrivate->rlList = NULL; 1007 CFRunLoopSourceInvalidate(prefsPrivate->rls); 1008 CFRelease(prefsPrivate->rls); 1009 prefsPrivate->rls = NULL; 1010 } 1011 } 1012 } 1013 1014 if (n == 0) { 1015 CFArrayRef changedKeys; 1016 1017 // if *all* notifications have been unscheduled 1018 prefsPrivate->scheduled = FALSE; 1019 1020 // no need to track changes 1021 (void) SCDynamicStoreSetNotificationKeys(prefsPrivate->session, NULL, NULL); 1022 1023 // clear out any pending notifications 1024 changedKeys = SCDynamicStoreCopyNotifiedKeys(prefsPrivate->session); 1025 if (changedKeys != NULL) { 1026 CFRelease(changedKeys); 1027 } 1028 1029 // release our reference to the prefs 1030 CFRelease(prefs); 1031 } 1032 1033 ok = TRUE; 1034 1035 done : 1036 1037 pthread_mutex_unlock(&prefsPrivate->lock); 1038 return ok; 1039} 1040 1041 1042Boolean 1043SCPreferencesScheduleWithRunLoop(SCPreferencesRef prefs, 1044 CFRunLoopRef runLoop, 1045 CFStringRef runLoopMode) 1046{ 1047 if (!isA_SCPreferences(prefs) || (runLoop == NULL) || (runLoopMode == NULL)) { 1048 _SCErrorSet(kSCStatusInvalidArgument); 1049 return FALSE; 1050 } 1051 1052 return __SCPreferencesScheduleWithRunLoop(prefs, runLoop, runLoopMode, NULL); 1053} 1054 1055 1056Boolean 1057SCPreferencesUnscheduleFromRunLoop(SCPreferencesRef prefs, 1058 CFRunLoopRef runLoop, 1059 CFStringRef runLoopMode) 1060{ 1061 if (!isA_SCPreferences(prefs) || (runLoop == NULL) || (runLoopMode == NULL)) { 1062 _SCErrorSet(kSCStatusInvalidArgument); 1063 return FALSE; 1064 } 1065 1066 return __SCPreferencesUnscheduleFromRunLoop(prefs, runLoop, runLoopMode); 1067} 1068 1069 1070Boolean 1071SCPreferencesSetDispatchQueue(SCPreferencesRef prefs, 1072 dispatch_queue_t queue) 1073{ 1074 Boolean ok = FALSE; 1075 1076 if (!isA_SCPreferences(prefs)) { 1077 /* sorry, you must provide a session */ 1078 _SCErrorSet(kSCStatusNoPrefsSession); 1079 return FALSE; 1080 } 1081 1082 if (queue != NULL) { 1083 ok = __SCPreferencesScheduleWithRunLoop(prefs, NULL, NULL, queue); 1084 } else { 1085 ok = __SCPreferencesUnscheduleFromRunLoop(prefs, NULL, NULL); 1086 } 1087 1088 return ok; 1089} 1090 1091 1092static void 1093__SCPreferencesSynchronize_helper(SCPreferencesRef prefs) 1094{ 1095 Boolean ok; 1096 SCPreferencesPrivateRef prefsPrivate = (SCPreferencesPrivateRef)prefs; 1097 uint32_t status = kSCStatusOK; 1098 1099 if (prefsPrivate->helper_port == MACH_PORT_NULL) { 1100 // if no helper 1101 return; 1102 } 1103 1104 // have the helper "synchronize" the prefs 1105 ok = _SCHelperExec(prefsPrivate->helper_port, 1106 SCHELPER_MSG_PREFS_SYNCHRONIZE, 1107 NULL, 1108 &status, 1109 NULL); 1110 if (!ok) { 1111 // close helper 1112 if (prefsPrivate->helper_port != MACH_PORT_NULL) { 1113 _SCHelperClose(&prefsPrivate->helper_port); 1114 } 1115 } 1116 1117 return; 1118} 1119 1120 1121void 1122SCPreferencesSynchronize(SCPreferencesRef prefs) 1123{ 1124 SCPreferencesPrivateRef prefsPrivate = (SCPreferencesPrivateRef)prefs; 1125 1126 if (!isA_SCPreferences(prefs)) { 1127 /* sorry, you must provide a session */ 1128 _SCErrorSet(kSCStatusNoPrefsSession); 1129 return; 1130 } 1131 1132 if (prefsPrivate->authorizationData != NULL) { 1133 __SCPreferencesSynchronize_helper(prefs); 1134 } 1135 if (prefsPrivate->prefs != NULL) { 1136 CFRelease(prefsPrivate->prefs); 1137 prefsPrivate->prefs = NULL; 1138 } 1139 if (prefsPrivate->signature != NULL) { 1140 CFRelease(prefsPrivate->signature); 1141 prefsPrivate->signature = NULL; 1142 } 1143 prefsPrivate->accessed = FALSE; 1144 prefsPrivate->changed = FALSE; 1145 1146 return; 1147} 1148