1/*
2 * Copyright (c) 2012, 2013 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#include <Availability.h>
25#include <TargetConditionals.h>
26#include <asl.h>
27#include <dispatch/dispatch.h>
28#include <vproc.h>
29#include <vproc_priv.h>
30#include <xpc/xpc.h>
31#include <xpc/private.h>
32
33#include <CoreFoundation/CoreFoundation.h>
34#include <SystemConfiguration/SCPrivate.h>
35
36#include "libSystemConfiguration_server.h"
37
38#define kTrailingEdgeAgentEntitlement "com.apple.SystemConfiguration.trailing-edge-agent"
39
40#pragma mark -
41#pragma mark Support functions
42
43
44//__private_extern__ void
45//log_xpc_object(const char *msg, xpc_object_t obj)
46//{
47//	char	*desc;
48//
49//	desc = xpc_copy_description(obj);
50//	asl_log(NULL, NULL, ASL_LEVEL_ERR, "%s = %s", msg, desc);
51//	free(desc);
52//}
53
54
55#pragma mark -
56#pragma mark client connection trackng
57
58
59typedef struct {
60	xpc_connection_t	connection;
61} client_key_t;
62
63typedef struct {
64	pid_t			pid;
65	uint64_t		generation_pushed;
66	uint64_t		generation_acknowledged;
67} client_val_t;
68
69
70static __inline__ CF_RETURNS_RETAINED CFDataRef
71_client_key(xpc_connection_t c)
72{
73	client_key_t	key;
74	CFDataRef	client_key;
75
76	key.connection = c;
77	client_key = CFDataCreate(NULL, (UInt8 *)&key, sizeof(key));
78	return client_key;
79}
80
81
82#if	((__MAC_OS_X_VERSION_MIN_REQUIRED >= 1090) || (__IPHONE_OS_VERSION_MIN_REQUIRED >= 60000))
83static void
84_handle_entitlement_check_failure(pid_t pid)
85{
86	static Boolean			cleanupScheduled	= FALSE;
87	static dispatch_once_t		initializer		= 0;
88	static CFMutableArrayRef	pids			= NULL;
89	static dispatch_queue_t		queue			= NULL;
90
91	dispatch_once(&initializer, ^{
92		pids = CFArrayCreateMutable(kCFAllocatorDefault, 0, &kCFTypeArrayCallBacks);
93		queue = dispatch_queue_create("handle unentitled ack", NULL);
94	});
95
96	dispatch_sync(queue, ^{
97		CFNumberRef	pidNumber	= CFNumberCreate(kCFAllocatorDefault, kCFNumberIntType, &pid);
98
99		if (!CFArrayContainsValue(pids, CFRangeMake(0, CFArrayGetCount(pids)), pidNumber)) {
100			CFArrayAppendValue(pids, pidNumber);
101
102			SCLog(TRUE, LOG_ERR, CFSTR("DNS/nwi dropping ack w/no entitlement, pid = %d"), pid);
103
104			if (!cleanupScheduled) {
105				cleanupScheduled = TRUE;
106				dispatch_after(dispatch_time(DISPATCH_TIME_NOW, 180LL * NSEC_PER_SEC), queue, ^{
107					CFArrayRemoveAllValues(pids);
108					cleanupScheduled = FALSE;
109				});
110			}
111		}
112
113		CFRelease(pidNumber);
114	});
115}
116#endif	// ((__MAC_OS_X_VERSION_MIN_REQUIRED >= 1090) || (__IPHONE_OS_VERSION_MIN_REQUIRED >= 60000))
117
118
119/*
120 * libSystemConfiguraiton_client
121 *
122 * - all APIs must be called from the same [serial] dispatch queue
123 */
124
125
126__private_extern__
127void
128_libSC_info_server_init(libSC_info_server_t *server_info) {
129	bzero(server_info, sizeof(*server_info));
130	server_info->info = CFDictionaryCreateMutable(NULL,
131						      0,
132						      &kCFTypeDictionaryKeyCallBacks,
133						      &kCFTypeDictionaryValueCallBacks);
134	return;
135}
136
137
138__private_extern__
139void
140_libSC_info_server_set_data(libSC_info_server_t	*server_info,
141			    CFDataRef		data,
142			    uint64_t		generation)
143{
144	// update stored configuration
145	if (server_info->data != NULL) {
146		CFRelease(server_info->data);
147		server_info->data = NULL;
148	}
149	if (data != NULL) {
150		CFRetain(data);
151		server_info->data = data;
152	}
153
154	// update generation
155	if (generation == 0) {
156		// generation must be non-zero
157		generation = 1;
158	}
159	server_info->generation = generation;
160
161	// new configuration, all ack'ing clients need to
162	// check-in again
163	server_info->inSync_NO += server_info->inSync_YES;
164	server_info->inSync_YES = 0;
165
166	return;
167}
168
169
170/*
171 * _libSC_info_server_in_sync
172 *
173 * Called to check if all of the "active" configuration [XPC] connection
174 * are in sync with the requested generation.
175 */
176__private_extern__
177Boolean
178_libSC_info_server_in_sync(libSC_info_server_t *server_info)
179{
180	return (server_info->inSync_NO == 0) ? TRUE : FALSE;
181}
182
183
184/*
185 * _libSC_info_server_open
186 *
187 * Called when a new configuration [XPC] connection
188 * is established.
189 *
190 * - tracks the last generation pushed to the caller and
191 *   the last generation ack'd by the caller
192 */
193__private_extern__
194void
195_libSC_info_server_open(libSC_info_server_t	*server_info,
196			xpc_connection_t	c)
197{
198	CFDataRef	client_key;
199	CFDataRef	client_val;
200	client_val_t	val;
201
202	client_key = _client_key(c);
203
204	val.pid                     = xpc_connection_get_pid(c);
205	val.generation_pushed       = 0;
206	val.generation_acknowledged = 0;
207	client_val = CFDataCreate(NULL, (UInt8 *)&val, sizeof(val));
208
209	CFDictionarySetValue(server_info->info, client_key, client_val);
210	CFRelease(client_key);
211	CFRelease(client_val);
212
213	return;
214}
215
216
217/*
218 * _libSC_info_server_get_data
219 *
220 * Called when a [XPC] connection wants the current configuration.
221 *
222 * - updates the last generation pushed to the caller
223 */
224__private_extern__
225CFDataRef
226_libSC_info_server_get_data(libSC_info_server_t	*server_info,
227			    xpc_connection_t	c,
228			    uint64_t		*generation)
229{
230	CFDataRef	client_key;
231	CFDataRef	client_val;
232	client_val_t	*val;
233
234	// update last generation pushed to client
235	client_key = _client_key(c);
236	client_val = CFDictionaryGetValue(server_info->info, client_key);
237	CFRelease(client_key);
238
239	val = (client_val_t *)(void *)CFDataGetBytePtr(client_val);
240	val->generation_pushed = server_info->generation;
241
242	// return generation
243	*generation = server_info->generation;
244	if (*generation == 1) {
245		*generation = 0;
246	}
247
248	// return data
249	return server_info->data;
250}
251
252
253/*
254 * _libSC_info_server_acknowledged
255 *
256 * Called when a [XPC] connection wants to acknowledge a
257 * processed configuration.
258 *
259 * - updates the last generation ack'd by the caller
260 * - updates the count of [XPC] connections that are / not in sync
261 */
262__private_extern__
263Boolean
264_libSC_info_server_acknowledged(libSC_info_server_t	*server_info,
265				xpc_connection_t	c,
266				uint64_t		generation)
267{
268	CFDataRef	client_key;
269	CFDataRef	client_val;
270#if	((__MAC_OS_X_VERSION_MIN_REQUIRED >= 1090) || (__IPHONE_OS_VERSION_MIN_REQUIRED >= 60000))
271	xpc_object_t	ent_value;
272	Boolean		entitled	= FALSE;
273#endif	// ((__MAC_OS_X_VERSION_MIN_REQUIRED >= 1090) || (__IPHONE_OS_VERSION_MIN_REQUIRED >= 60000))
274	Boolean		sync_updated	= FALSE;
275	client_val_t	*val;
276
277#if	((__MAC_OS_X_VERSION_MIN_REQUIRED >= 1090) || (__IPHONE_OS_VERSION_MIN_REQUIRED >= 60000))
278	ent_value = xpc_connection_copy_entitlement_value(c, kTrailingEdgeAgentEntitlement);
279	if (ent_value != NULL) {
280		if (xpc_get_type(ent_value) == XPC_TYPE_BOOL) {
281			entitled = xpc_bool_get_value(ent_value);
282		}
283		xpc_release(ent_value);
284	}
285
286	if (!entitled) {
287		_handle_entitlement_check_failure(xpc_connection_get_pid(c));
288		return FALSE;
289	}
290#endif	// ((__MAC_OS_X_VERSION_MIN_REQUIRED >= 1090) || (__IPHONE_OS_VERSION_MIN_REQUIRED >= 60000))
291
292	client_key = _client_key(c);
293	client_val = CFDictionaryGetValue(server_info->info, client_key);
294	CFRelease(client_key);
295
296	val = (client_val_t *)(void *)CFDataGetBytePtr(client_val);
297
298	if (val->generation_acknowledged == 0) {
299		// if first ack
300		if (generation == server_info->generation) {
301			server_info->inSync_YES++;
302		} else {
303			server_info->inSync_NO++;
304			sync_updated = TRUE;
305		}
306	} else if ((generation != val->generation_acknowledged) &&
307		    (generation == server_info->generation)) {
308		// if we've previously ack'd a configuration
309		// ... and if we are ack'ing a configuration
310		//     that we have not previously ack'd
311		// ... and if we're ack'ing the current stored
312		//     configuration
313		server_info->inSync_NO--;
314		server_info->inSync_YES++;
315		sync_updated = TRUE;
316	}
317
318	val->generation_acknowledged = generation;
319
320	return sync_updated;
321}
322
323
324/*
325 * _libSC_info_server_close
326 *
327 * Called when a configuration [XPC] connection is closed.
328 */
329__private_extern__
330Boolean
331_libSC_info_server_close(libSC_info_server_t	*server_info,
332			 xpc_connection_t	c)
333{
334	CFDataRef	client_key;
335	CFDataRef	client_val;
336	Boolean		sync_updated	= FALSE;
337
338	client_key = _client_key(c);
339
340	// get client info, remove ack'd info
341	client_val = CFDictionaryGetValue(server_info->info, client_key);
342	if (client_val != NULL) {
343		client_val_t	*val;
344
345		val = (client_val_t *)(void *)CFDataGetBytePtr(client_val);
346		if (val->generation_acknowledged > 0) {
347			// if we've previously ack'd a configuration
348			if (val->generation_acknowledged == server_info->generation) {
349				// if currently in sync
350				server_info->inSync_YES--;
351			} else {
352				// if currently NOT in sync
353				server_info->inSync_NO--;
354				sync_updated = TRUE;
355			}
356		}
357	}
358
359	CFDictionaryRemoveValue(server_info->info, client_key);
360	CFRelease(client_key);
361
362	return sync_updated;
363}
364
365