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