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