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