]>
Commit | Line | Data |
---|---|---|
5958d7c0 | 1 | /* |
c956c85e | 2 | * Copyright (c) 2000, 2001, 2003-2005, 2007-2020 Apple Inc. All rights reserved. |
5958d7c0 A |
3 | * |
4 | * @APPLE_LICENSE_HEADER_START@ | |
9de8ab86 | 5 | * |
009ee3c6 A |
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. | |
9de8ab86 | 12 | * |
009ee3c6 A |
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 | |
5958d7c0 A |
15 | * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, |
16 | * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, | |
009ee3c6 A |
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. | |
9de8ab86 | 20 | * |
5958d7c0 A |
21 | * @APPLE_LICENSE_HEADER_END@ |
22 | */ | |
23 | ||
0fae82ee A |
24 | /* |
25 | * Modification History | |
26 | * | |
27 | * June 1, 2001 Allan Nathanson <ajn@apple.com> | |
28 | * - public API conversion | |
29 | * | |
30 | * March 24, 2000 Allan Nathanson <ajn@apple.com> | |
31 | * - initial revision | |
32 | */ | |
33 | ||
5e9ce69e | 34 | #include <SystemConfiguration/SystemConfiguration.h> |
5958d7c0 A |
35 | #include "configd.h" |
36 | #include "configd_server.h" | |
5e9ce69e | 37 | #include "pattern.h" |
5958d7c0 A |
38 | #include "session.h" |
39 | ||
a40a14f8 A |
40 | #include <unistd.h> |
41 | #include <bsm/libbsm.h> | |
afb19109 | 42 | #include <os/state_private.h> |
6bb65964 A |
43 | #include <sandbox.h> |
44 | ||
942cecd7 | 45 | #if !TARGET_OS_SIMULATOR || (defined(IPHONE_SIMULATOR_HOST_MIN_VERSION_REQUIRED) && (IPHONE_SIMULATOR_HOST_MIN_VERSION_REQUIRED >= 1090)) |
6f870c06 A |
46 | #define HAVE_MACHPORT_GUARDS |
47 | #endif | |
48 | ||
a40a14f8 | 49 | |
afb19109 A |
50 | /* information maintained for the main listener */ |
51 | static serverSessionRef server_session = NULL; | |
5958d7c0 | 52 | |
afb19109 A |
53 | /* |
54 | * information maintained for each active session | |
55 | * Note: sync w/sessionQueue() | |
56 | */ | |
57 | static CFMutableDictionaryRef client_sessions = NULL; | |
c956c85e | 58 | static CFIndex client_sessions_advise = 250; // when snapshot handler should detail sessions |
6bb65964 A |
59 | |
60 | ||
afb19109 A |
61 | static dispatch_queue_t |
62 | sessionQueue(void) | |
5958d7c0 | 63 | { |
afb19109 A |
64 | static dispatch_once_t once; |
65 | static dispatch_queue_t q; | |
5958d7c0 | 66 | |
afb19109 A |
67 | dispatch_once(&once, ^{ |
68 | // allocate mapping between [client] session mach port and session info | |
69 | client_sessions = CFDictionaryCreateMutable(NULL, | |
70 | 0, | |
71 | NULL, // use the actual mach_port_t as the key | |
72 | &kCFTypeDictionaryValueCallBacks); | |
73 | ||
74 | // and a queue to synchronize access to the mapping | |
75 | q = dispatch_queue_create("SCDynamicStore/sessions", NULL); | |
76 | }); | |
5958d7c0 | 77 | |
afb19109 A |
78 | return q; |
79 | } | |
009ee3c6 | 80 | |
17d3ee29 | 81 | |
afb19109 A |
82 | #pragma mark - |
83 | #pragma mark __serverSession object | |
17d3ee29 | 84 | |
afb19109 A |
85 | static CFStringRef __serverSessionCopyDescription (CFTypeRef cf); |
86 | static void __serverSessionDeallocate (CFTypeRef cf); | |
5958d7c0 | 87 | |
afb19109 A |
88 | static const CFRuntimeClass __serverSessionClass = { |
89 | 0, // version | |
90 | "serverSession", // className | |
91 | NULL, // init | |
92 | NULL, // copy | |
93 | __serverSessionDeallocate, // dealloc | |
94 | NULL, // equal | |
95 | NULL, // hash | |
96 | NULL, // copyFormattingDesc | |
97 | __serverSessionCopyDescription // copyDebugDesc | |
98 | }; | |
5958d7c0 | 99 | |
afb19109 | 100 | static CFTypeID __serverSessionTypeID = _kCFRuntimeNotATypeID; |
5958d7c0 | 101 | |
afb19109 A |
102 | |
103 | static CFStringRef | |
104 | __serverSessionCopyDescription(CFTypeRef cf) | |
17d3ee29 | 105 | { |
afb19109 A |
106 | CFAllocatorRef allocator = CFGetAllocator(cf); |
107 | CFMutableStringRef result; | |
108 | serverSessionRef session = (serverSessionRef)cf; | |
942cecd7 | 109 | |
afb19109 A |
110 | result = CFStringCreateMutable(allocator, 0); |
111 | CFStringAppendFormat(result, NULL, CFSTR("<serverSession %p [%p]> {"), cf, allocator); | |
17d3ee29 | 112 | |
afb19109 A |
113 | // add client port |
114 | CFStringAppendFormat(result, NULL, CFSTR("port = 0x%x (%d)"), session->key, session->key); | |
17d3ee29 | 115 | |
afb19109 A |
116 | // add session info |
117 | if (session->name != NULL) { | |
118 | CFStringAppendFormat(result, NULL, CFSTR(", name = %@"), session->name); | |
5e9ce69e | 119 | } |
17d3ee29 | 120 | |
afb19109 A |
121 | CFStringAppendFormat(result, NULL, CFSTR("}")); |
122 | return result; | |
17d3ee29 A |
123 | } |
124 | ||
125 | ||
afb19109 A |
126 | static void |
127 | __serverSessionDeallocate(CFTypeRef cf) | |
5958d7c0 | 128 | { |
afb19109 A |
129 | #pragma unused(cf) |
130 | serverSessionRef session = (serverSessionRef)cf; | |
5958d7c0 | 131 | |
afb19109 A |
132 | if (session->changedKeys != NULL) CFRelease(session->changedKeys); |
133 | if (session->name != NULL) CFRelease(session->name); | |
134 | if (session->sessionKeys != NULL) CFRelease(session->sessionKeys); | |
17d3ee29 | 135 | |
afb19109 A |
136 | return; |
137 | } | |
17d3ee29 | 138 | |
17d3ee29 | 139 | |
afb19109 A |
140 | static serverSessionRef |
141 | __serverSessionCreate(CFAllocatorRef allocator, mach_port_t server) | |
142 | { | |
143 | static dispatch_once_t once; | |
144 | serverSessionRef session; | |
145 | uint32_t size; | |
17d3ee29 | 146 | |
afb19109 A |
147 | // initialize runtime |
148 | dispatch_once(&once, ^{ | |
149 | __serverSessionTypeID = _CFRuntimeRegisterClass(&__serverSessionClass); | |
150 | }); | |
17d3ee29 | 151 | |
afb19109 A |
152 | // allocate session |
153 | size = sizeof(serverSession) - sizeof(CFRuntimeBase); | |
154 | session = (serverSessionRef)_CFRuntimeCreateInstance(allocator, | |
155 | __serverSessionTypeID, | |
156 | size, | |
157 | NULL); | |
158 | if (session == NULL) { | |
159 | return NULL; | |
160 | } | |
6f870c06 | 161 | |
afb19109 A |
162 | // if needed, allocate a mach port for SCDynamicStore client |
163 | if (server == MACH_PORT_NULL) { | |
164 | kern_return_t kr; | |
165 | mach_port_t mp = MACH_PORT_NULL; | |
166 | #ifdef HAVE_MACHPORT_GUARDS | |
167 | mach_port_options_t opts; | |
168 | #endif // HAVE_MACHPORT_GUARDS | |
5958d7c0 | 169 | |
6f870c06 A |
170 | retry_allocate : |
171 | ||
172 | #ifdef HAVE_MACHPORT_GUARDS | |
afb19109 | 173 | memset(&opts, 0, sizeof(opts)); |
6f870c06 A |
174 | opts.flags = MPO_CONTEXT_AS_GUARD; |
175 | ||
afb19109 | 176 | kr = mach_port_construct(mach_task_self(), &opts, (mach_port_context_t)session, &mp); |
6f870c06 A |
177 | #else // HAVE_MACHPORT_GUARDS |
178 | kr = mach_port_allocate(mach_task_self(), MACH_PORT_RIGHT_RECEIVE, &mp); | |
179 | #endif // HAVE_MACHPORT_GUARDS | |
180 | ||
181 | if (kr != KERN_SUCCESS) { | |
182 | char *err = NULL; | |
183 | ||
9de8ab86 | 184 | SC_log(LOG_NOTICE, "could not allocate mach port: %s", mach_error_string(kr)); |
6f870c06 A |
185 | if ((kr == KERN_NO_SPACE) || (kr == KERN_RESOURCE_SHORTAGE)) { |
186 | sleep(1); | |
187 | goto retry_allocate; | |
188 | } | |
189 | ||
9de8ab86 A |
190 | (void) asprintf(&err, "Could not allocate mach port: %s", mach_error_string(kr)); |
191 | _SC_crash(err != NULL ? err : "Could not allocate new session (mach) port", | |
6f870c06 A |
192 | NULL, |
193 | NULL); | |
194 | if (err != NULL) free(err); | |
afb19109 | 195 | CFRelease(session); |
6f870c06 A |
196 | return NULL; |
197 | } | |
be997540 | 198 | |
a40a14f8 | 199 | // insert send right that will be moved to the client |
6f870c06 A |
200 | kr = mach_port_insert_right(mach_task_self(), |
201 | mp, | |
202 | mp, | |
203 | MACH_MSG_TYPE_MAKE_SEND); | |
204 | if (kr != KERN_SUCCESS) { | |
205 | /* | |
206 | * We can't insert a send right into our own port! This should | |
207 | * only happen if someone stomped on OUR port (so let's leave | |
208 | * the port alone). | |
209 | */ | |
afb19109 A |
210 | SC_log(LOG_ERR, "mach_port_insert_right() failed: %s", mach_error_string(kr)); |
211 | CFRelease(session); | |
6f870c06 A |
212 | return NULL; |
213 | } | |
afb19109 A |
214 | |
215 | server = mp; | |
be997540 | 216 | } |
a40a14f8 | 217 | |
afb19109 A |
218 | session->callerEUID = 1; /* not "root" */ |
219 | session->callerRootAccess = UNKNOWN; | |
220 | session->callerWriteEntitlement = kCFNull; /* UNKNOWN */ | |
221 | session->key = server; | |
222 | // session->store = NULL; | |
942cecd7 | 223 | |
afb19109 A |
224 | return session; |
225 | } | |
5958d7c0 | 226 | |
afb19109 A |
227 | |
228 | #pragma mark - | |
229 | #pragma mark SCDynamicStore state handler | |
230 | ||
231 | ||
232 | static void | |
233 | addSessionReference(const void *key, const void *value, void *context) | |
234 | { | |
235 | #pragma unused(key) | |
236 | CFMutableDictionaryRef dict = (CFMutableDictionaryRef)context; | |
237 | serverSessionRef session = (serverSessionRef)value; | |
238 | ||
239 | if (session->name != NULL) { | |
240 | int cnt; | |
241 | CFNumberRef num; | |
242 | ||
243 | if (!CFDictionaryGetValueIfPresent(dict, | |
244 | session->name, | |
245 | (const void **)&num) || | |
246 | !CFNumberGetValue(num, kCFNumberIntType, &cnt)) { | |
247 | // if first session | |
248 | cnt = 0; | |
249 | } | |
250 | cnt++; | |
251 | num = CFNumberCreate(NULL, kCFNumberIntType, &cnt); | |
252 | CFDictionarySetValue(dict, session->name, num); | |
253 | CFRelease(num); | |
254 | } | |
255 | ||
256 | return; | |
5958d7c0 A |
257 | } |
258 | ||
259 | ||
afb19109 A |
260 | static void |
261 | add_state_handler() | |
5958d7c0 | 262 | { |
afb19109 A |
263 | os_state_block_t state_block; |
264 | ||
265 | state_block = ^os_state_data_t(os_state_hints_t hints) { | |
266 | #pragma unused(hints) | |
267 | CFDataRef data = NULL; | |
268 | CFIndex n; | |
269 | Boolean ok; | |
270 | os_state_data_t state_data; | |
271 | size_t state_data_size; | |
272 | CFIndex state_len; | |
273 | ||
274 | n = CFDictionaryGetCount(client_sessions); | |
c956c85e | 275 | if (n < client_sessions_advise) { |
afb19109 A |
276 | CFStringRef str; |
277 | ||
278 | str = CFStringCreateWithFormat(NULL, NULL, CFSTR("n = %ld"), n); | |
279 | ok = _SCSerialize(str, &data, NULL, NULL); | |
280 | CFRelease(str); | |
281 | } else { | |
282 | CFMutableDictionaryRef dict; | |
283 | ||
284 | dict = CFDictionaryCreateMutable(NULL, | |
285 | 0, | |
286 | &kCFTypeDictionaryKeyCallBacks, | |
287 | &kCFTypeDictionaryValueCallBacks); | |
288 | CFDictionaryApplyFunction(client_sessions, addSessionReference, dict); | |
289 | ok = _SCSerialize(dict, &data, NULL, NULL); | |
290 | CFRelease(dict); | |
291 | } | |
5958d7c0 | 292 | |
afb19109 A |
293 | state_len = (ok && (data != NULL)) ? CFDataGetLength(data) : 0; |
294 | state_data_size = OS_STATE_DATA_SIZE_NEEDED(state_len); | |
295 | if (state_data_size > MAX_STATEDUMP_SIZE) { | |
296 | SC_log(LOG_ERR, "SCDynamicStore/sessions : state data too large (%zd > %zd)", | |
297 | state_data_size, | |
298 | (size_t)MAX_STATEDUMP_SIZE); | |
299 | if (data != NULL) CFRelease(data); | |
300 | return NULL; | |
301 | } | |
5958d7c0 | 302 | |
afb19109 A |
303 | state_data = calloc(1, state_data_size); |
304 | if (state_data == NULL) { | |
305 | SC_log(LOG_ERR, "SCDynamicStore/sessions: could not allocate state data"); | |
306 | if (data != NULL) CFRelease(data); | |
307 | return NULL; | |
5958d7c0 | 308 | } |
5958d7c0 | 309 | |
afb19109 A |
310 | state_data->osd_type = OS_STATE_DATA_SERIALIZED_NSCF_OBJECT; |
311 | state_data->osd_data_size = (uint32_t)state_len; | |
312 | strlcpy(state_data->osd_title, "SCDynamicStore/sessions", sizeof(state_data->osd_title)); | |
313 | if (state_len > 0) { | |
314 | memcpy(state_data->osd_data, CFDataGetBytePtr(data), state_len); | |
315 | } | |
316 | if (data != NULL) CFRelease(data); | |
5958d7c0 | 317 | |
afb19109 A |
318 | return state_data; |
319 | }; | |
009ee3c6 | 320 | |
afb19109 A |
321 | (void) os_state_add_handler(sessionQueue(), state_block); |
322 | return; | |
323 | } | |
5958d7c0 | 324 | |
a40a14f8 | 325 | |
afb19109 A |
326 | #pragma mark - |
327 | #pragma mark SCDynamicStore session management | |
5e9ce69e | 328 | |
942cecd7 | 329 | |
afb19109 A |
330 | __private_extern__ |
331 | serverSessionRef | |
332 | getSession(mach_port_t server) | |
333 | { | |
334 | __block serverSessionRef session; | |
17d3ee29 | 335 | |
afb19109 A |
336 | assert(server != MACH_PORT_NULL); |
337 | dispatch_sync(sessionQueue(), ^{ | |
338 | session = (serverSessionRef)CFDictionaryGetValue(client_sessions, | |
339 | (const void *)(uintptr_t)server); | |
340 | }); | |
5958d7c0 | 341 | |
afb19109 A |
342 | return session; |
343 | } | |
344 | ||
345 | ||
346 | __private_extern__ | |
347 | serverSessionRef | |
348 | getSessionNum(CFNumberRef serverNum) | |
349 | { | |
350 | union { | |
351 | mach_port_t mp; | |
352 | uint64_t val; | |
353 | } server; | |
354 | serverSessionRef session; | |
355 | ||
356 | (void) CFNumberGetValue(serverNum, kCFNumberSInt64Type, &server.val); | |
357 | session = getSession(server.mp); | |
358 | ||
359 | return session; | |
360 | } | |
361 | ||
362 | ||
363 | __private_extern__ | |
364 | serverSessionRef | |
365 | getSessionStr(CFStringRef serverKey) | |
366 | { | |
367 | mach_port_t server; | |
368 | serverSessionRef session; | |
369 | char str[16]; | |
370 | ||
371 | (void) _SC_cfstring_to_cstring(serverKey, str, sizeof(str), kCFStringEncodingASCII); | |
372 | server = atoi(str); | |
373 | session = getSession(server); | |
374 | ||
375 | return session; | |
376 | } | |
377 | ||
378 | ||
c956c85e A |
379 | #if __has_feature(ptrauth_intrinsics) |
380 | extern const struct { char c; } objc_absolute_packed_isa_class_mask; | |
381 | #endif | |
382 | ||
383 | static void | |
384 | memcpy_objc_object(void* dst, const void* restrict src, size_t size) | |
385 | { | |
386 | // first, we copy the object | |
387 | memcpy(dst, src, size); | |
388 | ||
389 | // then, if needed, fix the isa pointer | |
390 | #if __has_feature(ptrauth_intrinsics) | |
391 | uintptr_t flags; | |
392 | uintptr_t isa_mask; | |
393 | void ** opaqueObject; | |
394 | uintptr_t raw_isa; | |
395 | uintptr_t real_isa; | |
396 | ||
397 | opaqueObject = (void**)src; | |
398 | isa_mask = (uintptr_t)&objc_absolute_packed_isa_class_mask; | |
399 | flags = (uintptr_t)(*opaqueObject) & ~isa_mask; | |
400 | real_isa = (uintptr_t)(*opaqueObject) & isa_mask; | |
401 | ||
402 | #if __has_feature(ptrauth_objc_isa) | |
403 | raw_isa = (uintptr_t)ptrauth_auth_data((void *)real_isa, | |
404 | ptrauth_key_process_independent_data, | |
405 | ptrauth_blend_discriminator(opaqueObject, 0x6AE1)); | |
406 | #else // __has_feature(ptrauth_objc_isa) | |
407 | raw_isa = (uintptr_t)ptrauth_strip((void*)real_isa, ptrauth_key_process_independent_data); | |
408 | #endif // __has_feature(ptrauth_objc_isa) | |
409 | ((CFRuntimeBase*)dst)->_cfisa = raw_isa; | |
410 | ((uint64_t*)dst)[0] |= flags; | |
411 | #endif // __has_feature(ptrauth_intrinsics) | |
412 | } | |
413 | ||
414 | ||
afb19109 A |
415 | __private_extern__ |
416 | serverSessionRef | |
417 | tempSession(mach_port_t server, CFStringRef name, audit_token_t auditToken) | |
418 | { | |
419 | static dispatch_once_t once; | |
420 | SCDynamicStorePrivateRef storePrivate; /* temp session */ | |
421 | static serverSession temp_session; | |
422 | ||
423 | dispatch_once(&once, ^{ | |
c956c85e A |
424 | memcpy_objc_object(&temp_session, /* use "server" session clone */ |
425 | server_session, | |
426 | sizeof(temp_session)); | |
afb19109 A |
427 | (void) __SCDynamicStoreOpen(&temp_session.store, NULL); |
428 | }); | |
429 | ||
430 | if (temp_session.key != server) { | |
431 | // if not SCDynamicStore "server" port | |
432 | return NULL; | |
5958d7c0 | 433 | } |
a40a14f8 | 434 | |
afb19109 A |
435 | /* save audit token, caller entitlements */ |
436 | temp_session.auditToken = auditToken; | |
437 | temp_session.callerEUID = 1; /* not "root" */ | |
438 | temp_session.callerRootAccess = UNKNOWN; | |
439 | if ((temp_session.callerWriteEntitlement != NULL) && | |
440 | (temp_session.callerWriteEntitlement != kCFNull)) { | |
441 | CFRelease(temp_session.callerWriteEntitlement); | |
442 | } | |
443 | temp_session.callerWriteEntitlement = kCFNull; /* UNKNOWN */ | |
444 | ||
445 | /* save name */ | |
446 | storePrivate = (SCDynamicStorePrivateRef)temp_session.store; | |
447 | if (storePrivate->name != NULL) CFRelease(storePrivate->name); | |
448 | storePrivate->name = CFRetain(name); | |
449 | ||
450 | return &temp_session; | |
5958d7c0 A |
451 | } |
452 | ||
453 | ||
009ee3c6 | 454 | __private_extern__ |
5958d7c0 | 455 | void |
afb19109 | 456 | addSession(serverSessionRef session, Boolean isMain) |
5958d7c0 | 457 | { |
afb19109 A |
458 | session->serverChannel = dispatch_mach_create_f("configd/SCDynamicStore", |
459 | server_queue(), | |
460 | (void *)session, | |
461 | server_mach_channel_handler); | |
462 | if (!isMain) { | |
463 | // if not main SCDynamicStore port, watch for exit | |
c956c85e | 464 | dispatch_mach_notify_no_senders(session->serverChannel, FALSE); |
afb19109 A |
465 | } |
466 | #if TARGET_OS_SIMULATOR | |
467 | // simulators don't support MiG QoS propagation yet | |
468 | dispatch_set_qos_class_fallback(session->serverChannel, QOS_CLASS_USER_INITIATED); | |
469 | #else | |
470 | dispatch_set_qos_class_fallback(session->serverChannel, QOS_CLASS_BACKGROUND); | |
471 | #endif | |
472 | dispatch_mach_connect(session->serverChannel, session->key, MACH_PORT_NULL, NULL); | |
473 | return; | |
474 | } | |
5958d7c0 | 475 | |
5958d7c0 | 476 | |
afb19109 A |
477 | __private_extern__ |
478 | serverSessionRef | |
479 | addClient(mach_port_t server, audit_token_t audit_token) | |
480 | { | |
5958d7c0 | 481 | |
afb19109 | 482 | __block serverSessionRef newSession = NULL; |
a40a14f8 | 483 | |
afb19109 A |
484 | dispatch_sync(sessionQueue(), ^{ |
485 | Boolean ok; | |
a40a14f8 | 486 | |
afb19109 A |
487 | // check to see if we already have an open session |
488 | ok = CFDictionaryContainsKey(client_sessions, | |
489 | (const void *)(uintptr_t)server); | |
490 | if (ok) { | |
491 | // if we've already added a session for this port | |
492 | return; | |
a40a14f8 A |
493 | } |
494 | ||
afb19109 A |
495 | // allocate a new session for "the" server |
496 | newSession = __serverSessionCreate(NULL, MACH_PORT_NULL); | |
497 | if (newSession != NULL) { | |
498 | // and add a port --> session mapping | |
499 | CFDictionarySetValue(client_sessions, | |
500 | (const void *)(uintptr_t)newSession->key, | |
501 | newSession); | |
a40a14f8 | 502 | |
afb19109 A |
503 | // save the audit_token in case we need to check the callers credentials |
504 | newSession->auditToken = audit_token; | |
a40a14f8 | 505 | |
afb19109 | 506 | CFRelease(newSession); // reference held by dictionary |
a40a14f8 | 507 | } |
afb19109 | 508 | }); |
a40a14f8 | 509 | |
afb19109 A |
510 | if (newSession != NULL) { |
511 | addSession(newSession, FALSE); | |
512 | } | |
a40a14f8 | 513 | |
afb19109 A |
514 | return newSession; |
515 | } | |
a40a14f8 | 516 | |
afb19109 A |
517 | |
518 | __private_extern__ | |
519 | serverSessionRef | |
520 | addServer(mach_port_t server) | |
521 | { | |
522 | // allocate a session for "the" server | |
523 | server_session = __serverSessionCreate(NULL, server); | |
524 | addSession(server_session, TRUE); | |
525 | ||
526 | // add a state dump handler | |
527 | add_state_handler(); | |
528 | ||
529 | return server_session; | |
530 | } | |
531 | ||
532 | ||
533 | __private_extern__ | |
534 | void | |
535 | cleanupSession(serverSessionRef session) | |
536 | { | |
537 | mach_port_t server = session->key; | |
538 | ||
539 | SC_trace("cleanup : %5d", server); | |
540 | ||
541 | /* | |
542 | * Close any open connections including cancelling any outstanding | |
543 | * notification requests and releasing any locks. | |
544 | */ | |
545 | __MACH_PORT_DEBUG(TRUE, "*** cleanupSession", server); | |
546 | (void) __SCDynamicStoreClose(&session->store); | |
547 | __MACH_PORT_DEBUG(TRUE, "*** cleanupSession (after __SCDynamicStoreClose)", server); | |
548 | ||
549 | /* | |
550 | * Our send right has already been removed. Remove our receive right. | |
551 | */ | |
552 | #ifdef HAVE_MACHPORT_GUARDS | |
553 | (void) mach_port_destruct(mach_task_self(), server, 0, (mach_port_context_t)session); | |
554 | #else // HAVE_MACHPORT_GUARDS | |
555 | (void) mach_port_mod_refs(mach_task_self(), server, MACH_PORT_RIGHT_RECEIVE, -1); | |
556 | #endif // HAVE_MACHPORT_GUARDS | |
557 | ||
558 | /* | |
559 | * release any entitlement info | |
560 | */ | |
561 | if ((session->callerWriteEntitlement != NULL) && | |
562 | (session->callerWriteEntitlement != kCFNull)) { | |
563 | CFRelease(session->callerWriteEntitlement); | |
a40a14f8 A |
564 | } |
565 | ||
afb19109 A |
566 | /* |
567 | * get rid of the per-session structure. | |
568 | */ | |
569 | dispatch_sync(sessionQueue(), ^{ | |
570 | CFDictionaryRemoveValue(client_sessions, | |
571 | (const void *)(uintptr_t)server); | |
572 | }); | |
573 | ||
a40a14f8 A |
574 | return; |
575 | } | |
576 | ||
577 | ||
afb19109 A |
578 | __private_extern__ |
579 | void | |
580 | closeSession(serverSessionRef session) | |
a40a14f8 | 581 | { |
afb19109 A |
582 | /* |
583 | * cancel and release the mach channel | |
584 | */ | |
585 | if (session->serverChannel != NULL) { | |
586 | dispatch_mach_cancel(session->serverChannel); | |
587 | dispatch_release(session->serverChannel); | |
588 | session->serverChannel = NULL; | |
589 | } | |
590 | ||
591 | return; | |
592 | } | |
a40a14f8 | 593 | |
a40a14f8 | 594 | |
afb19109 A |
595 | typedef struct ReportSessionInfo { |
596 | FILE *f; | |
597 | int n; | |
598 | } ReportSessionInfo, *ReportSessionInfoRef; | |
599 | ||
600 | static void | |
601 | printOne(const void *key, const void *value, void *context) | |
602 | { | |
603 | #pragma unused(key) | |
604 | ReportSessionInfoRef reportInfo = (ReportSessionInfoRef)context; | |
605 | serverSessionRef session = (serverSessionRef)value; | |
606 | ||
607 | SCPrint(TRUE, reportInfo->f, CFSTR(" %d : port = 0x%x"), ++reportInfo->n, session->key); | |
608 | SCPrint(TRUE, reportInfo->f, CFSTR(", name = %@"), session->name); | |
609 | if (session->changedKeys != NULL) { | |
610 | SCPrint(TRUE, reportInfo->f, CFSTR("\n changedKeys = %@"), session->changedKeys); | |
a40a14f8 | 611 | } |
afb19109 A |
612 | if (session->sessionKeys != NULL) { |
613 | SCPrint(TRUE, reportInfo->f, CFSTR("\n sessionKeys = %@"), session->sessionKeys); | |
614 | } | |
615 | SCPrint(TRUE, reportInfo->f, CFSTR("\n")); | |
616 | return; | |
617 | } | |
618 | ||
619 | ||
620 | __private_extern__ | |
621 | void | |
622 | listSessions(FILE *f) | |
623 | { | |
624 | dispatch_sync(sessionQueue(), ^{ | |
625 | ReportSessionInfo reportInfo = { .f = f, .n = 0 }; | |
5958d7c0 | 626 | |
afb19109 A |
627 | SCPrint(TRUE, f, CFSTR("Current sessions :\n")); |
628 | CFDictionaryApplyFunction(client_sessions, | |
629 | printOne, | |
630 | (void *)&reportInfo); | |
631 | SCPrint(TRUE, f, CFSTR("\n")); | |
632 | }); | |
633 | return; | |
a40a14f8 A |
634 | } |
635 | ||
afb19109 A |
636 | |
637 | #include <Security/Security.h> | |
638 | #include <Security/SecTask.h> | |
639 | ||
5e9ce69e A |
640 | static CFTypeRef |
641 | copyEntitlement(serverSessionRef session, CFStringRef entitlement) | |
a40a14f8 | 642 | { |
a40a14f8 | 643 | SecTaskRef task; |
5e9ce69e | 644 | CFTypeRef value = NULL; |
a40a14f8 | 645 | |
5e9ce69e | 646 | // Create the security task from the audit token |
a40a14f8 A |
647 | task = SecTaskCreateWithAuditToken(NULL, session->auditToken); |
648 | if (task != NULL) { | |
649 | CFErrorRef error = NULL; | |
a40a14f8 | 650 | |
5e9ce69e | 651 | // Get the value for the entitlement |
6bb65964 | 652 | value = SecTaskCopyValueForEntitlement(task, entitlement, &error); |
5e9ce69e A |
653 | if ((value == NULL) && (error != NULL)) { |
654 | CFIndex code = CFErrorGetCode(error); | |
655 | CFStringRef domain = CFErrorGetDomain(error); | |
656 | ||
657 | if (!CFEqual(domain, kCFErrorDomainMach) || | |
658 | ((code != kIOReturnInvalid) && (code != kIOReturnNotFound))) { | |
659 | // if unexpected error | |
9de8ab86 A |
660 | SC_log(LOG_NOTICE, "SecTaskCopyValueForEntitlement(,\"%@\",) failed, error = %@ : %@", |
661 | entitlement, | |
662 | error, | |
afb19109 | 663 | session->name); |
5958d7c0 | 664 | } |
a40a14f8 | 665 | CFRelease(error); |
5958d7c0 | 666 | } |
a40a14f8 A |
667 | |
668 | CFRelease(task); | |
669 | } else { | |
9de8ab86 | 670 | SC_log(LOG_NOTICE, "SecTaskCreateWithAuditToken() failed: %@", |
afb19109 | 671 | session->name); |
5958d7c0 | 672 | } |
a40a14f8 | 673 | |
5e9ce69e | 674 | return value; |
5958d7c0 A |
675 | } |
676 | ||
5e9ce69e A |
677 | |
678 | static pid_t | |
679 | sessionPid(serverSessionRef session) | |
680 | { | |
681 | pid_t caller_pid; | |
682 | ||
683 | #if (__MAC_OS_X_VERSION_MIN_REQUIRED >= 1080) && !TARGET_OS_IPHONE | |
684 | caller_pid = audit_token_to_pid(session->auditToken); | |
685 | #else // (__MAC_OS_X_VERSION_MIN_REQUIRED >= 1080) && !TARGET_OS_IPHONE | |
686 | audit_token_to_au32(session->auditToken, | |
687 | NULL, // auidp | |
688 | NULL, // euid | |
689 | NULL, // egid | |
690 | NULL, // ruid | |
691 | NULL, // rgid | |
692 | &caller_pid, // pid | |
693 | NULL, // asid | |
694 | NULL); // tid | |
695 | #endif // (__MAC_OS_X_VERSION_MIN_REQUIRED >= 1080) && !TARGET_OS_IPHONE | |
696 | ||
697 | return caller_pid; | |
698 | } | |
a40a14f8 A |
699 | |
700 | ||
701 | __private_extern__ | |
702 | Boolean | |
703 | hasRootAccess(serverSessionRef session) | |
704 | { | |
942cecd7 | 705 | #if !TARGET_OS_SIMULATOR |
5e9ce69e | 706 | |
a40a14f8 | 707 | if (session->callerRootAccess == UNKNOWN) { |
5e9ce69e A |
708 | #if (__MAC_OS_X_VERSION_MIN_REQUIRED >= 1080) && !TARGET_OS_IPHONE |
709 | session->callerEUID = audit_token_to_euid(session->auditToken); | |
710 | #else // (__MAC_OS_X_VERSION_MIN_REQUIRED >= 1080) && !TARGET_OS_IPHONE | |
a40a14f8 A |
711 | audit_token_to_au32(session->auditToken, |
712 | NULL, // auidp | |
713 | &session->callerEUID, // euid | |
714 | NULL, // egid | |
715 | NULL, // ruid | |
716 | NULL, // rgid | |
717 | NULL, // pid | |
718 | NULL, // asid | |
719 | NULL); // tid | |
5e9ce69e | 720 | #endif // (__MAC_OS_X_VERSION_MIN_REQUIRED >= 1080) && !TARGET_OS_IPHONE |
a40a14f8 A |
721 | session->callerRootAccess = (session->callerEUID == 0) ? YES : NO; |
722 | } | |
723 | ||
724 | return (session->callerRootAccess == YES) ? TRUE : FALSE; | |
5e9ce69e | 725 | |
942cecd7 | 726 | #else // !TARGET_OS_SIMULATOR |
1ef45fa4 | 727 | #pragma unused(session) |
5e9ce69e A |
728 | |
729 | /* | |
730 | * assume that all processes interacting with | |
731 | * the iOS Simulator "configd" are OK. | |
732 | */ | |
733 | return TRUE; | |
734 | ||
942cecd7 | 735 | #endif // !TARGET_OS_SIMULATOR |
a40a14f8 A |
736 | } |
737 | ||
738 | ||
739 | __private_extern__ | |
740 | Boolean | |
1ef45fa4 | 741 | hasWriteAccess(serverSessionRef session, const char *op, CFStringRef key) |
a40a14f8 | 742 | { |
5e9ce69e | 743 | Boolean isSetup; |
a40a14f8 | 744 | |
5e9ce69e A |
745 | // need to special case writing "Setup:" keys |
746 | isSetup = CFStringHasPrefix(key, kSCDynamicStoreDomainSetup); | |
747 | ||
748 | if (hasRootAccess(session)) { | |
749 | pid_t pid; | |
750 | ||
751 | // grant write access to eUID==0 processes | |
752 | ||
753 | pid = sessionPid(session); | |
754 | if (isSetup && (pid != getpid())) { | |
755 | /* | |
756 | * WAIT!!! | |
757 | * | |
758 | * This is NOT configd (or a plugin) trying to | |
759 | * write to an SCDynamicStore "Setup:" key. In | |
760 | * general, this is unwise and we should at the | |
761 | * very least complain. | |
762 | */ | |
1ef45fa4 | 763 | SC_log(LOG_NOTICE, "*** Non-configd process (pid=%d) attempting to %s \"%@\" ***", |
9de8ab86 | 764 | pid, |
1ef45fa4 | 765 | op, |
9de8ab86 | 766 | key); |
a40a14f8 | 767 | } |
5e9ce69e A |
768 | |
769 | return TRUE; | |
770 | } | |
771 | ||
772 | if (isSetup) { | |
773 | /* | |
774 | * STOP!!! | |
775 | * | |
776 | * This is a non-root process trying to write to | |
777 | * an SCDynamicStore "Setup:" key. This is not | |
778 | * something we should ever allow (regardless of | |
779 | * any entitlements). | |
780 | */ | |
9de8ab86 A |
781 | SC_log(LOG_NOTICE, "*** Non-root process (pid=%d) attempting to modify \"%@\" ***", |
782 | sessionPid(session), | |
783 | key); | |
5e9ce69e | 784 | |
afb19109 | 785 | return FALSE; |
5e9ce69e A |
786 | } |
787 | ||
5e9ce69e A |
788 | if (session->callerWriteEntitlement == kCFNull) { |
789 | session->callerWriteEntitlement = copyEntitlement(session, | |
790 | kSCWriteEntitlementName); | |
791 | } | |
792 | ||
793 | if (session->callerWriteEntitlement == NULL) { | |
794 | return FALSE; | |
795 | } | |
796 | ||
797 | if (isA_CFBoolean(session->callerWriteEntitlement) && | |
798 | CFBooleanGetValue(session->callerWriteEntitlement)) { | |
799 | // grant write access to "entitled" processes | |
800 | return TRUE; | |
801 | } | |
802 | ||
803 | if (isA_CFDictionary(session->callerWriteEntitlement)) { | |
804 | CFArrayRef keys; | |
805 | CFArrayRef patterns; | |
806 | ||
807 | keys = CFDictionaryGetValue(session->callerWriteEntitlement, CFSTR("keys")); | |
808 | if (isA_CFArray(keys)) { | |
809 | if (CFArrayContainsValue(keys, | |
810 | CFRangeMake(0, CFArrayGetCount(keys)), | |
811 | key)) { | |
812 | // if key matches one of the entitlement "keys", grant | |
813 | // write access | |
814 | return TRUE; | |
815 | } | |
816 | } | |
817 | ||
818 | patterns = CFDictionaryGetValue(session->callerWriteEntitlement, CFSTR("patterns")); | |
819 | if (isA_CFArray(patterns)) { | |
820 | CFIndex i; | |
821 | CFIndex n = CFArrayGetCount(patterns); | |
822 | ||
823 | for (i = 0; i < n; i++) { | |
824 | CFStringRef pattern; | |
825 | ||
826 | pattern = CFArrayGetValueAtIndex(patterns, i); | |
827 | if (isA_CFString(pattern)) { | |
828 | if (patternKeyMatches(pattern, key)) { | |
829 | // if key matches one of the entitlement | |
830 | // "patterns", grant write access | |
831 | return TRUE; | |
832 | } | |
833 | } | |
834 | } | |
a40a14f8 | 835 | } |
a40a14f8 A |
836 | } |
837 | ||
5e9ce69e | 838 | return FALSE; |
a40a14f8 | 839 | } |
6bb65964 A |
840 | |
841 | ||
842 | __private_extern__ | |
843 | Boolean | |
844 | hasPathAccess(serverSessionRef session, const char *path) | |
845 | { | |
846 | pid_t pid; | |
847 | char realPath[PATH_MAX]; | |
848 | ||
849 | if (realpath(path, realPath) == NULL) { | |
9de8ab86 | 850 | SC_log(LOG_INFO, "realpath() failed: %s", strerror(errno)); |
6bb65964 A |
851 | return FALSE; |
852 | } | |
853 | ||
5e9ce69e A |
854 | #if (__MAC_OS_X_VERSION_MIN_REQUIRED >= 1080) && !TARGET_OS_IPHONE |
855 | pid = audit_token_to_pid(session->auditToken); | |
856 | #else // (__MAC_OS_X_VERSION_MIN_REQUIRED >= 1080) && !TARGET_OS_IPHONE | |
6bb65964 A |
857 | audit_token_to_au32(session->auditToken, |
858 | NULL, // auidp | |
859 | NULL, // euid | |
860 | NULL, // egid | |
861 | NULL, // ruid | |
862 | NULL, // rgid | |
863 | &pid, // pid | |
864 | NULL, // asid | |
865 | NULL); // tid | |
5e9ce69e A |
866 | #endif // (__MAC_OS_X_VERSION_MIN_REQUIRED >= 1080) && !TARGET_OS_IPHONE |
867 | if (sandbox_check(pid, // pid | |
868 | "file-write-data", // operation | |
869 | SANDBOX_FILTER_PATH | SANDBOX_CHECK_NO_REPORT, // sandbox_filter_type | |
870 | realPath) > 0) { // ... | |
9de8ab86 | 871 | SC_log(LOG_INFO, "sandbox access denied: %s", strerror(errno)); |
6bb65964 A |
872 | return FALSE; |
873 | } | |
874 | ||
875 | return TRUE; | |
876 | } |