]>
Commit | Line | Data |
---|---|---|
5958d7c0 | 1 | /* |
78403150 | 2 | * Copyright (c) 2000, 2001, 2003-2005, 2007-2014 Apple Inc. All rights reserved. |
5958d7c0 A |
3 | * |
4 | * @APPLE_LICENSE_HEADER_START@ | |
009ee3c6 | 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. | |
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 | |
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. | |
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> | |
6bb65964 A |
42 | #include <sandbox.h> |
43 | ||
6f870c06 A |
44 | #if !TARGET_IPHONE_SIMULATOR || (defined(IPHONE_SIMULATOR_HOST_MIN_VERSION_REQUIRED) && (IPHONE_SIMULATOR_HOST_MIN_VERSION_REQUIRED >= 1090)) |
45 | #define HAVE_MACHPORT_GUARDS | |
46 | #endif | |
47 | ||
a40a14f8 | 48 | |
5958d7c0 | 49 | /* information maintained for each active session */ |
17d3ee29 A |
50 | static serverSessionRef *sessions = NULL; |
51 | static int nSessions = 0; /* # of allocated sessions */ | |
52 | static int lastSession = -1; /* # of last used session */ | |
5958d7c0 | 53 | |
6bb65964 A |
54 | /* CFMachPortInvalidation runloop */ |
55 | static CFRunLoopRef sessionRunLoop = NULL; | |
56 | ||
17d3ee29 A |
57 | /* temp session */ |
58 | static serverSessionRef temp_session = NULL; | |
59 | ||
6bb65964 | 60 | |
009ee3c6 | 61 | __private_extern__ |
5958d7c0 A |
62 | serverSessionRef |
63 | getSession(mach_port_t server) | |
64 | { | |
65 | int i; | |
66 | ||
67 | if (server == MACH_PORT_NULL) { | |
a40a14f8 | 68 | SCLog(TRUE, LOG_ERR, CFSTR("Excuse me, why is getSession() being called with an invalid port?")); |
5958d7c0 A |
69 | return NULL; |
70 | } | |
71 | ||
17d3ee29 A |
72 | /* look for matching session (note: slot 0 is the "server" port) */ |
73 | for (i = 1; i <= lastSession; i++) { | |
009ee3c6 A |
74 | serverSessionRef thisSession = sessions[i]; |
75 | ||
76 | if (thisSession == NULL) { | |
77 | /* found an empty slot, skip it */ | |
78 | continue; | |
17d3ee29 A |
79 | } |
80 | ||
81 | if (thisSession->key == server) { | |
82 | /* we've seen this server before */ | |
83 | return thisSession; | |
84 | } | |
85 | ||
86 | if ((thisSession->store != NULL) && | |
87 | (((SCDynamicStorePrivateRef)thisSession->store)->notifySignalTask == server)) { | |
88 | /* we've seen this task port before */ | |
009ee3c6 | 89 | return thisSession; |
5958d7c0 A |
90 | } |
91 | } | |
92 | ||
93 | /* no sessions available */ | |
94 | return NULL; | |
95 | } | |
96 | ||
97 | ||
17d3ee29 A |
98 | __private_extern__ |
99 | serverSessionRef | |
100 | tempSession(mach_port_t server, CFStringRef name, audit_token_t auditToken) | |
101 | { | |
102 | static dispatch_once_t once; | |
103 | SCDynamicStorePrivateRef storePrivate; | |
104 | ||
105 | if (sessions[0]->key != server) { | |
106 | // if not SCDynamicStore "server" port | |
107 | return NULL; | |
108 | } | |
109 | ||
110 | dispatch_once(&once, ^{ | |
111 | temp_session = sessions[0]; /* use "server" session */ | |
112 | (void) __SCDynamicStoreOpen(&temp_session->store, NULL); | |
113 | }); | |
114 | ||
5e9ce69e A |
115 | /* save audit token, caller entitlements */ |
116 | temp_session->auditToken = auditToken; | |
117 | temp_session->callerEUID = 1; /* not "root" */ | |
118 | temp_session->callerRootAccess = UNKNOWN; | |
5e9ce69e A |
119 | if ((temp_session->callerWriteEntitlement != NULL) && |
120 | (temp_session->callerWriteEntitlement != kCFNull)) { | |
121 | CFRelease(temp_session->callerWriteEntitlement); | |
122 | } | |
123 | temp_session->callerWriteEntitlement = kCFNull; /* UNKNOWN */ | |
17d3ee29 A |
124 | |
125 | /* save name */ | |
126 | storePrivate = (SCDynamicStorePrivateRef)temp_session->store; | |
127 | if (storePrivate->name != NULL) CFRelease(storePrivate->name); | |
128 | storePrivate->name = CFRetain(name); | |
129 | ||
130 | return temp_session; | |
131 | } | |
132 | ||
133 | ||
009ee3c6 | 134 | __private_extern__ |
5958d7c0 | 135 | serverSessionRef |
be997540 | 136 | addSession(mach_port_t server, CFStringRef (*copyDescription)(const void *info)) |
5958d7c0 | 137 | { |
6f870c06 A |
138 | CFMachPortContext context = { 0, NULL, NULL, NULL, NULL }; |
139 | kern_return_t kr; | |
140 | mach_port_t mp = server; | |
141 | int n = -1; | |
142 | serverSessionRef newSession = NULL; | |
6bb65964 A |
143 | |
144 | /* save current (SCDynamicStore) runloop */ | |
145 | if (sessionRunLoop == NULL) { | |
146 | sessionRunLoop = CFRunLoopGetCurrent(); | |
147 | } | |
5958d7c0 A |
148 | |
149 | if (nSessions <= 0) { | |
17d3ee29 A |
150 | /* if first session (the "server" port) */ |
151 | n = 0; /* use slot "0" */ | |
152 | lastSession = 0; /* last used slot */ | |
153 | ||
154 | nSessions = 64; | |
155 | sessions = malloc(nSessions * sizeof(serverSessionRef)); | |
6f870c06 A |
156 | |
157 | // allocate a new session for "the" server | |
158 | newSession = calloc(1, sizeof(serverSession)); | |
5958d7c0 | 159 | } else { |
6f870c06 A |
160 | int i; |
161 | #ifdef HAVE_MACHPORT_GUARDS | |
162 | mach_port_options_t opts; | |
163 | #endif // HAVE_MACHPORT_GUARDS | |
be997540 | 164 | |
17d3ee29 A |
165 | /* check to see if we already have an open session (note: slot 0 is the "server" port) */ |
166 | for (i = 1; i <= lastSession; i++) { | |
6f870c06 | 167 | serverSessionRef thisSession = sessions[i]; |
17d3ee29 A |
168 | |
169 | if (thisSession == NULL) { | |
170 | /* found an empty slot */ | |
171 | if (n < 0) { | |
172 | /* keep track of the first [empty] slot */ | |
173 | n = i; | |
174 | } | |
175 | ||
176 | /* and keep looking for a matching session */ | |
177 | continue; | |
178 | } | |
179 | ||
180 | if (thisSession->key == server) { | |
181 | /* we've seen this server before */ | |
182 | return NULL; | |
183 | } | |
184 | ||
185 | if ((thisSession->store != NULL) && | |
186 | (((SCDynamicStorePrivateRef)thisSession->store)->notifySignalTask == server)) { | |
187 | /* we've seen this task port before */ | |
188 | return NULL; | |
5958d7c0 A |
189 | } |
190 | } | |
17d3ee29 A |
191 | |
192 | /* add a new session */ | |
5958d7c0 | 193 | if (n < 0) { |
17d3ee29 A |
194 | /* if no empty slots */ |
195 | n = ++lastSession; | |
196 | if (lastSession >= nSessions) { | |
197 | /* expand the session list */ | |
198 | nSessions *= 2; | |
199 | sessions = reallocf(sessions, (nSessions * sizeof(serverSessionRef))); | |
200 | } | |
5958d7c0 | 201 | } |
17d3ee29 | 202 | |
6f870c06 A |
203 | // allocate a session for this client |
204 | newSession = calloc(1, sizeof(serverSession)); | |
205 | ||
17d3ee29 A |
206 | // create mach port for SCDynamicStore client |
207 | mp = MACH_PORT_NULL; | |
5958d7c0 | 208 | |
6f870c06 A |
209 | retry_allocate : |
210 | ||
211 | #ifdef HAVE_MACHPORT_GUARDS | |
212 | bzero(&opts, sizeof(opts)); | |
213 | opts.flags = MPO_CONTEXT_AS_GUARD; | |
214 | ||
215 | kr = mach_port_construct(mach_task_self(), &opts, newSession, &mp); | |
216 | #else // HAVE_MACHPORT_GUARDS | |
217 | kr = mach_port_allocate(mach_task_self(), MACH_PORT_RIGHT_RECEIVE, &mp); | |
218 | #endif // HAVE_MACHPORT_GUARDS | |
219 | ||
220 | if (kr != KERN_SUCCESS) { | |
221 | char *err = NULL; | |
222 | ||
223 | SCLog(TRUE, LOG_ERR, CFSTR("addSession: could not allocate mach port: %s"), mach_error_string(kr)); | |
224 | if ((kr == KERN_NO_SPACE) || (kr == KERN_RESOURCE_SHORTAGE)) { | |
225 | sleep(1); | |
226 | goto retry_allocate; | |
227 | } | |
228 | ||
229 | (void) asprintf(&err, "addSession: could not allocate mach port: %s", mach_error_string(kr)); | |
230 | _SC_crash(err != NULL ? err : "addSession: could not allocate mach port", | |
231 | NULL, | |
232 | NULL); | |
233 | if (err != NULL) free(err); | |
234 | ||
235 | free(newSession); | |
236 | return NULL; | |
237 | } | |
238 | } | |
be997540 A |
239 | |
240 | // create server port | |
6f870c06 | 241 | context.info = newSession; |
17d3ee29 | 242 | context.copyDescription = copyDescription; |
a40a14f8 A |
243 | |
244 | // | |
245 | // Note: we create the CFMachPort *before* we insert a send | |
246 | // right present to ensure that CF does not establish | |
247 | // its dead name notification. | |
248 | // | |
6f870c06 A |
249 | newSession->serverPort = _SC_CFMachPortCreateWithPort("SCDynamicStore/session", |
250 | mp, | |
251 | configdCallback, | |
252 | &context); | |
6bb65964 | 253 | |
17d3ee29 | 254 | if (n > 0) { |
a40a14f8 | 255 | // insert send right that will be moved to the client |
6f870c06 A |
256 | kr = mach_port_insert_right(mach_task_self(), |
257 | mp, | |
258 | mp, | |
259 | MACH_MSG_TYPE_MAKE_SEND); | |
260 | if (kr != KERN_SUCCESS) { | |
261 | /* | |
262 | * We can't insert a send right into our own port! This should | |
263 | * only happen if someone stomped on OUR port (so let's leave | |
264 | * the port alone). | |
265 | */ | |
266 | SCLog(TRUE, LOG_ERR, CFSTR("addSession mach_port_insert_right(): %s"), mach_error_string(kr)); | |
267 | ||
268 | free(newSession); | |
269 | return NULL; | |
270 | } | |
be997540 | 271 | } |
a40a14f8 | 272 | |
6f870c06 | 273 | sessions[n] = newSession; |
5e9ce69e A |
274 | sessions[n]->key = mp; |
275 | // sessions[n]->serverRunLoopSource = NULL; | |
276 | // sessions[n]->store = NULL; | |
277 | sessions[n]->callerEUID = 1; /* not "root" */ | |
278 | sessions[n]->callerRootAccess = UNKNOWN; | |
5e9ce69e | 279 | sessions[n]->callerWriteEntitlement = kCFNull; /* UNKNOWN */ |
5958d7c0 | 280 | |
6f870c06 | 281 | return newSession; |
5958d7c0 A |
282 | } |
283 | ||
284 | ||
009ee3c6 | 285 | __private_extern__ |
5958d7c0 | 286 | void |
17d3ee29 | 287 | cleanupSession(mach_port_t server) |
5958d7c0 | 288 | { |
17d3ee29 | 289 | int i; |
5958d7c0 | 290 | |
17d3ee29 A |
291 | for (i = 1; i <= lastSession; i++) { |
292 | CFStringRef sessionKey; | |
293 | serverSessionRef thisSession = sessions[i]; | |
5958d7c0 A |
294 | |
295 | if (thisSession == NULL) { | |
296 | /* found an empty slot, skip it */ | |
297 | continue; | |
5958d7c0 | 298 | } |
5958d7c0 | 299 | |
17d3ee29 | 300 | if (thisSession->key == server) { |
5958d7c0 A |
301 | /* |
302 | * session entry still exists. | |
303 | */ | |
304 | ||
009ee3c6 A |
305 | if (_configd_trace) { |
306 | SCTrace(TRUE, _configd_trace, CFSTR("cleanup : %5d\n"), server); | |
307 | } | |
308 | ||
5958d7c0 A |
309 | /* |
310 | * Close any open connections including cancelling any outstanding | |
311 | * notification requests and releasing any locks. | |
312 | */ | |
a40a14f8 | 313 | __MACH_PORT_DEBUG(TRUE, "*** cleanupSession", server); |
17d3ee29 | 314 | (void) __SCDynamicStoreClose(&thisSession->store); |
a40a14f8 | 315 | __MACH_PORT_DEBUG(TRUE, "*** cleanupSession (after __SCDynamicStoreClose)", server); |
5958d7c0 | 316 | |
be997540 | 317 | /* |
a40a14f8 | 318 | * Our send right has already been removed. Remove our receive right. |
be997540 | 319 | */ |
6f870c06 A |
320 | #ifdef HAVE_MACHPORT_GUARDS |
321 | (void) mach_port_destruct(mach_task_self(), server, 0, thisSession); | |
322 | #else // HAVE_MACHPORT_GUARDS | |
a40a14f8 | 323 | (void) mach_port_mod_refs(mach_task_self(), server, MACH_PORT_RIGHT_RECEIVE, -1); |
6f870c06 | 324 | #endif // HAVE_MACHPORT_GUARDS |
a40a14f8 | 325 | |
5e9ce69e A |
326 | /* |
327 | * release any entitlement info | |
328 | */ | |
329 | if ((thisSession->callerWriteEntitlement != NULL) && | |
330 | (thisSession->callerWriteEntitlement != kCFNull)) { | |
331 | CFRelease(thisSession->callerWriteEntitlement); | |
332 | } | |
5e9ce69e | 333 | |
5958d7c0 | 334 | /* |
17d3ee29 A |
335 | * We don't need any remaining information in the |
336 | * sessionData dictionary, remove it. | |
337 | */ | |
338 | sessionKey = CFStringCreateWithFormat(NULL, NULL, CFSTR("%d"), server); | |
339 | CFDictionaryRemoveValue(sessionData, sessionKey); | |
340 | CFRelease(sessionKey); | |
341 | ||
342 | /* | |
343 | * get rid of the per-session structure. | |
5958d7c0 | 344 | */ |
17d3ee29 A |
345 | free(thisSession); |
346 | sessions[i] = NULL; | |
347 | ||
348 | if (i == lastSession) { | |
349 | /* we are removing the last session, update last used slot */ | |
350 | while (--lastSession > 0) { | |
351 | if (sessions[lastSession] != NULL) { | |
352 | break; | |
353 | } | |
354 | } | |
355 | } | |
5958d7c0 A |
356 | |
357 | return; | |
358 | } | |
359 | } | |
a40a14f8 A |
360 | |
361 | SCLog(TRUE, LOG_ERR, CFSTR("MACH_NOTIFY_NO_SENDERS w/no session, port = %d"), server); | |
362 | __MACH_PORT_DEBUG(TRUE, "*** cleanupSession w/no session", server); | |
5958d7c0 A |
363 | return; |
364 | } | |
365 | ||
366 | ||
009ee3c6 | 367 | __private_extern__ |
5958d7c0 | 368 | void |
a40a14f8 | 369 | listSessions(FILE *f) |
5958d7c0 A |
370 | { |
371 | int i; | |
372 | ||
a40a14f8 | 373 | SCPrint(TRUE, f, CFSTR("Current sessions :\n")); |
17d3ee29 | 374 | for (i = 0; i <= lastSession; i++) { |
5958d7c0 A |
375 | serverSessionRef thisSession = sessions[i]; |
376 | ||
377 | if (thisSession == NULL) { | |
378 | continue; | |
379 | } | |
380 | ||
a40a14f8 A |
381 | SCPrint(TRUE, f, CFSTR("\t%d : port = 0x%x"), i, thisSession->key); |
382 | ||
383 | if (thisSession->store != NULL) { | |
384 | SCDynamicStorePrivateRef storePrivate = (SCDynamicStorePrivateRef)thisSession->store; | |
385 | ||
386 | if (storePrivate->notifySignalTask != TASK_NULL) { | |
387 | SCPrint(TRUE, f, CFSTR(", task = %d"), storePrivate->notifySignalTask); | |
388 | } | |
389 | } | |
390 | ||
391 | if (sessionData != NULL) { | |
392 | CFDictionaryRef info; | |
393 | CFStringRef key; | |
394 | ||
395 | key = CFStringCreateWithFormat(NULL, NULL, CFSTR("%d"), thisSession->key); | |
396 | info = CFDictionaryGetValue(sessionData, key); | |
397 | CFRelease(key); | |
398 | if (info != NULL) { | |
399 | CFStringRef name; | |
400 | ||
401 | name = CFDictionaryGetValue(info, kSCDName); | |
402 | if (name != NULL) { | |
403 | SCPrint(TRUE, f, CFSTR(", name = %@"), name); | |
404 | } | |
405 | } | |
406 | } | |
407 | ||
408 | if (thisSession->serverPort != NULL) { | |
409 | SCPrint(TRUE, f, CFSTR("\n\t\t%@"), thisSession->serverPort); | |
410 | } | |
411 | ||
412 | if (thisSession->serverRunLoopSource != NULL) { | |
413 | SCPrint(TRUE, f, CFSTR("\n\t\t%@"), thisSession->serverRunLoopSource); | |
414 | } | |
415 | ||
416 | SCPrint(TRUE, f, CFSTR("\n")); | |
417 | } | |
418 | ||
419 | SCPrint(TRUE, f, CFSTR("\n")); | |
420 | return; | |
421 | } | |
422 | ||
423 | ||
a40a14f8 A |
424 | #include <Security/Security.h> |
425 | #include <Security/SecTask.h> | |
5958d7c0 | 426 | |
a40a14f8 A |
427 | static CFStringRef |
428 | sessionName(serverSessionRef session) | |
429 | { | |
430 | CFDictionaryRef info; | |
431 | CFStringRef name = NULL; | |
432 | CFStringRef sessionKey; | |
433 | ||
434 | sessionKey = CFStringCreateWithFormat(NULL, NULL, CFSTR("%d"), session->key); | |
435 | info = CFDictionaryGetValue(sessionData, sessionKey); | |
436 | CFRelease(sessionKey); | |
437 | ||
438 | if (info != NULL) { | |
439 | name = CFDictionaryGetValue(info, kSCDName); | |
440 | } | |
5958d7c0 | 441 | |
a40a14f8 A |
442 | return (name != NULL) ? name : CFSTR("???"); |
443 | } | |
444 | ||
5e9ce69e A |
445 | static CFTypeRef |
446 | copyEntitlement(serverSessionRef session, CFStringRef entitlement) | |
a40a14f8 | 447 | { |
a40a14f8 | 448 | SecTaskRef task; |
5e9ce69e | 449 | CFTypeRef value = NULL; |
a40a14f8 | 450 | |
5e9ce69e | 451 | // Create the security task from the audit token |
a40a14f8 A |
452 | task = SecTaskCreateWithAuditToken(NULL, session->auditToken); |
453 | if (task != NULL) { | |
454 | CFErrorRef error = NULL; | |
a40a14f8 | 455 | |
5e9ce69e | 456 | // Get the value for the entitlement |
6bb65964 | 457 | value = SecTaskCopyValueForEntitlement(task, entitlement, &error); |
5e9ce69e A |
458 | if ((value == NULL) && (error != NULL)) { |
459 | CFIndex code = CFErrorGetCode(error); | |
460 | CFStringRef domain = CFErrorGetDomain(error); | |
461 | ||
462 | if (!CFEqual(domain, kCFErrorDomainMach) || | |
463 | ((code != kIOReturnInvalid) && (code != kIOReturnNotFound))) { | |
464 | // if unexpected error | |
a40a14f8 | 465 | SCLog(TRUE, LOG_ERR, |
5e9ce69e A |
466 | CFSTR("SecTaskCopyValueForEntitlement(,\"%@\",) failed, error = %@ : %@"), |
467 | entitlement, | |
468 | error, | |
a40a14f8 | 469 | sessionName(session)); |
5958d7c0 | 470 | } |
a40a14f8 | 471 | CFRelease(error); |
5958d7c0 | 472 | } |
a40a14f8 A |
473 | |
474 | CFRelease(task); | |
475 | } else { | |
476 | SCLog(TRUE, LOG_ERR, | |
5e9ce69e | 477 | CFSTR("SecTaskCreateWithAuditToken() failed: %@"), |
a40a14f8 | 478 | sessionName(session)); |
5958d7c0 | 479 | } |
a40a14f8 | 480 | |
5e9ce69e | 481 | return value; |
5958d7c0 A |
482 | } |
483 | ||
5e9ce69e A |
484 | |
485 | static pid_t | |
486 | sessionPid(serverSessionRef session) | |
487 | { | |
488 | pid_t caller_pid; | |
489 | ||
490 | #if (__MAC_OS_X_VERSION_MIN_REQUIRED >= 1080) && !TARGET_OS_IPHONE | |
491 | caller_pid = audit_token_to_pid(session->auditToken); | |
492 | #else // (__MAC_OS_X_VERSION_MIN_REQUIRED >= 1080) && !TARGET_OS_IPHONE | |
493 | audit_token_to_au32(session->auditToken, | |
494 | NULL, // auidp | |
495 | NULL, // euid | |
496 | NULL, // egid | |
497 | NULL, // ruid | |
498 | NULL, // rgid | |
499 | &caller_pid, // pid | |
500 | NULL, // asid | |
501 | NULL); // tid | |
502 | #endif // (__MAC_OS_X_VERSION_MIN_REQUIRED >= 1080) && !TARGET_OS_IPHONE | |
503 | ||
504 | return caller_pid; | |
505 | } | |
a40a14f8 A |
506 | |
507 | ||
508 | __private_extern__ | |
509 | Boolean | |
510 | hasRootAccess(serverSessionRef session) | |
511 | { | |
5e9ce69e A |
512 | #if !TARGET_IPHONE_SIMULATOR |
513 | ||
a40a14f8 | 514 | if (session->callerRootAccess == UNKNOWN) { |
5e9ce69e A |
515 | #if (__MAC_OS_X_VERSION_MIN_REQUIRED >= 1080) && !TARGET_OS_IPHONE |
516 | session->callerEUID = audit_token_to_euid(session->auditToken); | |
517 | #else // (__MAC_OS_X_VERSION_MIN_REQUIRED >= 1080) && !TARGET_OS_IPHONE | |
a40a14f8 A |
518 | audit_token_to_au32(session->auditToken, |
519 | NULL, // auidp | |
520 | &session->callerEUID, // euid | |
521 | NULL, // egid | |
522 | NULL, // ruid | |
523 | NULL, // rgid | |
524 | NULL, // pid | |
525 | NULL, // asid | |
526 | NULL); // tid | |
5e9ce69e | 527 | #endif // (__MAC_OS_X_VERSION_MIN_REQUIRED >= 1080) && !TARGET_OS_IPHONE |
a40a14f8 A |
528 | session->callerRootAccess = (session->callerEUID == 0) ? YES : NO; |
529 | } | |
530 | ||
531 | return (session->callerRootAccess == YES) ? TRUE : FALSE; | |
5e9ce69e A |
532 | |
533 | #else // !TARGET_IPHONE_SIMULATOR | |
534 | ||
535 | /* | |
536 | * assume that all processes interacting with | |
537 | * the iOS Simulator "configd" are OK. | |
538 | */ | |
539 | return TRUE; | |
540 | ||
541 | #endif // !TARGET_IPHONE_SIMULATOR | |
a40a14f8 A |
542 | } |
543 | ||
544 | ||
545 | __private_extern__ | |
546 | Boolean | |
5e9ce69e | 547 | hasWriteAccess(serverSessionRef session, CFStringRef key) |
a40a14f8 | 548 | { |
5e9ce69e | 549 | Boolean isSetup; |
a40a14f8 | 550 | |
5e9ce69e A |
551 | // need to special case writing "Setup:" keys |
552 | isSetup = CFStringHasPrefix(key, kSCDynamicStoreDomainSetup); | |
553 | ||
554 | if (hasRootAccess(session)) { | |
555 | pid_t pid; | |
556 | ||
557 | // grant write access to eUID==0 processes | |
558 | ||
559 | pid = sessionPid(session); | |
560 | if (isSetup && (pid != getpid())) { | |
561 | /* | |
562 | * WAIT!!! | |
563 | * | |
564 | * This is NOT configd (or a plugin) trying to | |
565 | * write to an SCDynamicStore "Setup:" key. In | |
566 | * general, this is unwise and we should at the | |
567 | * very least complain. | |
568 | */ | |
569 | SCLog(TRUE, LOG_ERR, | |
570 | CFSTR("*** Non-configd process (pid=%d) attempting to modify \"%@\" ***"), | |
571 | pid, | |
572 | key); | |
a40a14f8 | 573 | } |
5e9ce69e A |
574 | |
575 | return TRUE; | |
576 | } | |
577 | ||
578 | if (isSetup) { | |
579 | /* | |
580 | * STOP!!! | |
581 | * | |
582 | * This is a non-root process trying to write to | |
583 | * an SCDynamicStore "Setup:" key. This is not | |
584 | * something we should ever allow (regardless of | |
585 | * any entitlements). | |
586 | */ | |
587 | SCLog(TRUE, LOG_ERR, | |
588 | CFSTR("*** Non-root process (pid=%d) attempting to modify \"%@\" ***"), | |
589 | sessionPid(session), | |
590 | key); | |
591 | ||
592 | //return FALSE; // return FALSE when rdar://9811832 has beed fixed | |
593 | } | |
594 | ||
5e9ce69e A |
595 | if (session->callerWriteEntitlement == kCFNull) { |
596 | session->callerWriteEntitlement = copyEntitlement(session, | |
597 | kSCWriteEntitlementName); | |
598 | } | |
599 | ||
600 | if (session->callerWriteEntitlement == NULL) { | |
601 | return FALSE; | |
602 | } | |
603 | ||
604 | if (isA_CFBoolean(session->callerWriteEntitlement) && | |
605 | CFBooleanGetValue(session->callerWriteEntitlement)) { | |
606 | // grant write access to "entitled" processes | |
607 | return TRUE; | |
608 | } | |
609 | ||
610 | if (isA_CFDictionary(session->callerWriteEntitlement)) { | |
611 | CFArrayRef keys; | |
612 | CFArrayRef patterns; | |
613 | ||
614 | keys = CFDictionaryGetValue(session->callerWriteEntitlement, CFSTR("keys")); | |
615 | if (isA_CFArray(keys)) { | |
616 | if (CFArrayContainsValue(keys, | |
617 | CFRangeMake(0, CFArrayGetCount(keys)), | |
618 | key)) { | |
619 | // if key matches one of the entitlement "keys", grant | |
620 | // write access | |
621 | return TRUE; | |
622 | } | |
623 | } | |
624 | ||
625 | patterns = CFDictionaryGetValue(session->callerWriteEntitlement, CFSTR("patterns")); | |
626 | if (isA_CFArray(patterns)) { | |
627 | CFIndex i; | |
628 | CFIndex n = CFArrayGetCount(patterns); | |
629 | ||
630 | for (i = 0; i < n; i++) { | |
631 | CFStringRef pattern; | |
632 | ||
633 | pattern = CFArrayGetValueAtIndex(patterns, i); | |
634 | if (isA_CFString(pattern)) { | |
635 | if (patternKeyMatches(pattern, key)) { | |
636 | // if key matches one of the entitlement | |
637 | // "patterns", grant write access | |
638 | return TRUE; | |
639 | } | |
640 | } | |
641 | } | |
a40a14f8 | 642 | } |
a40a14f8 A |
643 | } |
644 | ||
5e9ce69e | 645 | return FALSE; |
a40a14f8 | 646 | } |
6bb65964 A |
647 | |
648 | ||
649 | __private_extern__ | |
650 | Boolean | |
651 | hasPathAccess(serverSessionRef session, const char *path) | |
652 | { | |
653 | pid_t pid; | |
654 | char realPath[PATH_MAX]; | |
655 | ||
656 | if (realpath(path, realPath) == NULL) { | |
657 | SCLog(TRUE, LOG_DEBUG, CFSTR("hasPathAccess realpath() failed: %s"), strerror(errno)); | |
658 | return FALSE; | |
659 | } | |
660 | ||
5e9ce69e A |
661 | #if (__MAC_OS_X_VERSION_MIN_REQUIRED >= 1080) && !TARGET_OS_IPHONE |
662 | pid = audit_token_to_pid(session->auditToken); | |
663 | #else // (__MAC_OS_X_VERSION_MIN_REQUIRED >= 1080) && !TARGET_OS_IPHONE | |
6bb65964 A |
664 | audit_token_to_au32(session->auditToken, |
665 | NULL, // auidp | |
666 | NULL, // euid | |
667 | NULL, // egid | |
668 | NULL, // ruid | |
669 | NULL, // rgid | |
670 | &pid, // pid | |
671 | NULL, // asid | |
672 | NULL); // tid | |
5e9ce69e A |
673 | #endif // (__MAC_OS_X_VERSION_MIN_REQUIRED >= 1080) && !TARGET_OS_IPHONE |
674 | if (sandbox_check(pid, // pid | |
675 | "file-write-data", // operation | |
676 | SANDBOX_FILTER_PATH | SANDBOX_CHECK_NO_REPORT, // sandbox_filter_type | |
677 | realPath) > 0) { // ... | |
6bb65964 A |
678 | SCLog(TRUE, LOG_DEBUG, CFSTR("hasPathAccess sandbox access denied: %s"), strerror(errno)); |
679 | return FALSE; | |
680 | } | |
681 | ||
682 | return TRUE; | |
683 | } |