2 * Copyright (c) 2000, 2001, 2003-2005, 2007-2011 Apple Inc. All rights reserved.
4 * @APPLE_LICENSE_HEADER_START@
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
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.
21 * @APPLE_LICENSE_HEADER_END@
25 * Modification History
27 * June 1, 2001 Allan Nathanson <ajn@apple.com>
28 * - public API conversion
30 * March 24, 2000 Allan Nathanson <ajn@apple.com>
35 #include "configd_server.h"
39 #include <bsm/libbsm.h>
43 /* information maintained for each active session */
44 static serverSessionRef
*sessions
= NULL
;
45 static int nSessions
= 0;
47 /* CFMachPortInvalidation runloop */
48 static CFRunLoopRef sessionRunLoop
= NULL
;
52 CFMachPortInvalidateSessionCallback(CFMachPortRef port
, void *info
)
54 CFRunLoopRef currentRunLoop
= CFRunLoopGetCurrent();
57 if (!_SC_CFEqual(currentRunLoop
, sessionRunLoop
)) {
58 _SC_crash("SCDynamicStore CFMachPort invalidation error",
59 CFSTR("CFMachPort invalidated"),
60 CFSTR("An SCDynamicStore CFMachPort has incorrectly been invalidated."));
67 getSession(mach_port_t server
)
71 if (server
== MACH_PORT_NULL
) {
72 SCLog(TRUE
, LOG_ERR
, CFSTR("Excuse me, why is getSession() being called with an invalid port?"));
76 for (i
= 0; i
< nSessions
; i
++) {
77 serverSessionRef thisSession
= sessions
[i
];
79 if (thisSession
== NULL
) {
80 /* found an empty slot, skip it */
82 } else if (thisSession
->key
== server
) {
83 return thisSession
; /* we've seen this server before */
84 } else if (thisSession
->store
&&
85 (((SCDynamicStorePrivateRef
)thisSession
->store
)->notifySignalTask
== server
)) {
90 /* no sessions available */
97 addSession(mach_port_t server
, CFStringRef (*copyDescription
)(const void *info
))
99 CFMachPortContext context
= { 0, NULL
, NULL
, NULL
, NULL
};
100 mach_port_t mp
= server
;
103 /* save current (SCDynamicStore) runloop */
104 if (sessionRunLoop
== NULL
) {
105 sessionRunLoop
= CFRunLoopGetCurrent();
108 if (nSessions
<= 0) {
109 /* new session (actually, the first) found */
110 sessions
= malloc(sizeof(serverSessionRef
));
116 for (i
= 0; i
< nSessions
; i
++) {
117 if (sessions
[i
] == NULL
) {
118 /* found an empty slot, use it */
123 /* new session identified */
125 /* no empty slots, add one to the list */
127 sessions
= reallocf(sessions
, ((nSessions
) * sizeof(serverSessionRef
)));
131 // allocate a new session for this server
132 sessions
[n
] = malloc(sizeof(serverSession
));
133 bzero(sessions
[n
], sizeof(serverSession
));
135 // create server port
136 context
.info
= sessions
[n
];
137 context
.copyDescription
= copyDescription
;
139 if (server
== MACH_PORT_NULL
) {
140 // create mach port for SCDynamicStore client
141 (void) mach_port_allocate(mach_task_self(),
142 MACH_PORT_RIGHT_RECEIVE
,
147 // Note: we create the CFMachPort *before* we insert a send
148 // right present to ensure that CF does not establish
149 // its dead name notification.
151 sessions
[n
]->serverPort
= _SC_CFMachPortCreateWithPort("SCDynamicStore/session",
157 // Set bear trap (an invalidation callback) to catch other
158 // threads stomping on us
160 CFMachPortSetInvalidationCallBack(sessions
[n
]->serverPort
,
161 CFMachPortInvalidateSessionCallback
);
163 if (server
== MACH_PORT_NULL
) {
164 // insert send right that will be moved to the client
165 (void) mach_port_insert_right(mach_task_self(),
168 MACH_MSG_TYPE_MAKE_SEND
);
171 sessions
[n
]->key
= mp
;
172 // sessions[n]->serverRunLoopSource = NULL;
173 // sessions[n]->store = NULL;
174 sessions
[n
]->callerEUID
= 1; /* not "root" */
175 sessions
[n
]->callerRootAccess
= UNKNOWN
;
176 sessions
[n
]->callerWriteAccess
= UNKNOWN
;
184 removeSession(mach_port_t server
)
187 serverSessionRef thisSession
;
188 CFStringRef sessionKey
;
190 for (i
= 0; i
< nSessions
; i
++) {
191 thisSession
= sessions
[i
];
193 if (thisSession
== NULL
) {
194 /* found an empty slot, skip it */
196 } else if (thisSession
->key
== server
) {
198 * We don't need any remaining information in the
199 * sessionData dictionary, remove it.
201 sessionKey
= CFStringCreateWithFormat(NULL
, NULL
, CFSTR("%d"), server
);
202 CFDictionaryRemoveValue(sessionData
, sessionKey
);
203 CFRelease(sessionKey
);
206 * Lastly, get rid of the per-session structure.
221 cleanupSession(mach_port_t server
)
225 for (i
= 0; i
< nSessions
; i
++) {
226 serverSessionRef thisSession
= sessions
[i
];
228 if ((thisSession
!= NULL
) && (thisSession
->key
== server
)) {
230 * session entry still exists.
233 if (_configd_trace
) {
234 SCTrace(TRUE
, _configd_trace
, CFSTR("cleanup : %5d\n"), server
);
238 * Ensure that any changes made while we held the "lock"
241 if ((storeLocked
> 0) &&
242 ((SCDynamicStorePrivateRef
)thisSession
->store
)->locked
) {
244 * swap store and associated data which, after
245 * being closed, will result in the restoration
246 * of the original pre-"locked" data.
248 _swapLockedStoreData();
252 * Close any open connections including cancelling any outstanding
253 * notification requests and releasing any locks.
255 __MACH_PORT_DEBUG(TRUE
, "*** cleanupSession", server
);
256 (void) __SCDynamicStoreClose(&thisSession
->store
, TRUE
);
257 __MACH_PORT_DEBUG(TRUE
, "*** cleanupSession (after __SCDynamicStoreClose)", server
);
260 * Our send right has already been removed. Remove our receive right.
262 (void) mach_port_mod_refs(mach_task_self(), server
, MACH_PORT_RIGHT_RECEIVE
, -1);
265 * Lastly, remove the session entry.
267 removeSession(server
);
273 SCLog(TRUE
, LOG_ERR
, CFSTR("MACH_NOTIFY_NO_SENDERS w/no session, port = %d"), server
);
274 __MACH_PORT_DEBUG(TRUE
, "*** cleanupSession w/no session", server
);
281 listSessions(FILE *f
)
285 SCPrint(TRUE
, f
, CFSTR("Current sessions :\n"));
286 for (i
= 0; i
< nSessions
; i
++) {
287 serverSessionRef thisSession
= sessions
[i
];
289 if (thisSession
== NULL
) {
293 SCPrint(TRUE
, f
, CFSTR("\t%d : port = 0x%x"), i
, thisSession
->key
);
295 if (thisSession
->store
!= NULL
) {
296 SCDynamicStorePrivateRef storePrivate
= (SCDynamicStorePrivateRef
)thisSession
->store
;
298 if (storePrivate
->notifySignalTask
!= TASK_NULL
) {
299 SCPrint(TRUE
, f
, CFSTR(", task = %d"), storePrivate
->notifySignalTask
);
303 if (sessionData
!= NULL
) {
304 CFDictionaryRef info
;
307 key
= CFStringCreateWithFormat(NULL
, NULL
, CFSTR("%d"), thisSession
->key
);
308 info
= CFDictionaryGetValue(sessionData
, key
);
313 name
= CFDictionaryGetValue(info
, kSCDName
);
315 SCPrint(TRUE
, f
, CFSTR(", name = %@"), name
);
320 if (thisSession
->serverPort
!= NULL
) {
321 SCPrint(TRUE
, f
, CFSTR("\n\t\t%@"), thisSession
->serverPort
);
324 if (thisSession
->serverRunLoopSource
!= NULL
) {
325 SCPrint(TRUE
, f
, CFSTR("\n\t\t%@"), thisSession
->serverRunLoopSource
);
328 SCPrint(TRUE
, f
, CFSTR("\n"));
331 SCPrint(TRUE
, f
, CFSTR("\n"));
336 #if TARGET_OS_IPHONE || (__MAC_OS_X_VERSION_MIN_REQUIRED >= 1070)
338 #include <Security/Security.h>
339 #include <Security/SecTask.h>
342 sessionName(serverSessionRef session
)
344 CFDictionaryRef info
;
345 CFStringRef name
= NULL
;
346 CFStringRef sessionKey
;
348 sessionKey
= CFStringCreateWithFormat(NULL
, NULL
, CFSTR("%d"), session
->key
);
349 info
= CFDictionaryGetValue(sessionData
, sessionKey
);
350 CFRelease(sessionKey
);
353 name
= CFDictionaryGetValue(info
, kSCDName
);
356 return (name
!= NULL
) ? name
: CFSTR("???");
361 hasEntitlement(serverSessionRef session
, CFStringRef entitlement
)
363 Boolean hasEntitlement
= FALSE
;
366 /* Create the security task from the audit token. */
367 task
= SecTaskCreateWithAuditToken(NULL
, session
->auditToken
);
369 CFErrorRef error
= NULL
;
372 /* Get the value for the entitlement. */
373 value
= SecTaskCopyValueForEntitlement(task
, entitlement
, &error
);
375 if (isA_CFBoolean(value
)) {
376 if (CFBooleanGetValue(value
)) {
377 /* if client DOES have entitlement */
378 hasEntitlement
= TRUE
;
382 CFSTR("hasEntitlement: entitlement not valid: %@"),
383 sessionName(session
));
390 (value
== NULL
) ? LOG_ERR
: LOG_DEBUG
,
391 CFSTR("hasEntitlement SecTaskCopyValueForEntitlement() %s, error domain=%@, error code=%lx"),
392 (value
== NULL
) ? "failed" : "warned",
393 CFErrorGetDomain(error
),
394 CFErrorGetCode(error
));
401 CFSTR("hasEntitlement SecTaskCreateWithAuditToken() failed: %@"),
402 sessionName(session
));
405 return hasEntitlement
;
408 #endif // TARGET_OS_IPHONE || (__MAC_OS_X_VERSION_MIN_REQUIRED >= 1070)
413 hasRootAccess(serverSessionRef session
)
415 if (session
->callerRootAccess
== UNKNOWN
) {
417 * get the credentials associated with the caller.
419 audit_token_to_au32(session
->auditToken
,
421 &session
->callerEUID
, // euid
429 session
->callerRootAccess
= (session
->callerEUID
== 0) ? YES
: NO
;
432 return (session
->callerRootAccess
== YES
) ? TRUE
: FALSE
;
438 hasWriteAccess(serverSessionRef session
)
440 if (session
->callerWriteAccess
== UNKNOWN
) {
441 /* assume that the client DOES NOT have the entitlement */
442 session
->callerWriteAccess
= NO
;
444 if (hasRootAccess(session
)) {
445 // grant write access to eUID==0 processes
446 session
->callerWriteAccess
= YES
;
448 #if TARGET_OS_IPHONE || (__MAC_OS_X_VERSION_MIN_REQUIRED >= 1070)
449 else if (hasEntitlement(session
, kSCWriteEntitlementName
)) {
450 // grant write access to "entitled" processes
451 session
->callerWriteAccess
= YES
;
453 #endif // TARGET_OS_IPHONE || (__MAC_OS_X_VERSION_MIN_REQUIRED >= 1070)
456 return (session
->callerWriteAccess
== YES
) ? TRUE
: FALSE
;
462 hasPathAccess(serverSessionRef session
, const char *path
)
465 char realPath
[PATH_MAX
];
467 if (realpath(path
, realPath
) == NULL
) {
468 SCLog(TRUE
, LOG_DEBUG
, CFSTR("hasPathAccess realpath() failed: %s"), strerror(errno
));
472 audit_token_to_au32(session
->auditToken
,
482 if (sandbox_check(pid
, "file-write-data", SANDBOX_FILTER_PATH
, realPath
) > 0) {
483 SCLog(TRUE
, LOG_DEBUG
, CFSTR("hasPathAccess sandbox access denied: %s"), strerror(errno
));