2 * Copyright (c) 2000, 2001, 2003-2005, 2007-2009 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>
41 /* information maintained for each active session */
42 static serverSessionRef
*sessions
= NULL
;
43 static int nSessions
= 0;
48 getSession(mach_port_t server
)
52 if (server
== MACH_PORT_NULL
) {
53 SCLog(TRUE
, LOG_ERR
, CFSTR("Excuse me, why is getSession() being called with an invalid port?"));
57 for (i
= 0; i
< nSessions
; i
++) {
58 serverSessionRef thisSession
= sessions
[i
];
60 if (thisSession
== NULL
) {
61 /* found an empty slot, skip it */
63 } else if (thisSession
->key
== server
) {
64 return thisSession
; /* we've seen this server before */
65 } else if (thisSession
->store
&&
66 (((SCDynamicStorePrivateRef
)thisSession
->store
)->notifySignalTask
== server
)) {
71 /* no sessions available */
78 addSession(mach_port_t server
, CFStringRef (*copyDescription
)(const void *info
))
80 CFMachPortContext context
= { 0, NULL
, NULL
, NULL
, NULL
};
81 mach_port_t mp
= server
;
85 /* new session (actually, the first) found */
86 sessions
= malloc(sizeof(serverSessionRef
));
92 for (i
= 0; i
< nSessions
; i
++) {
93 if (sessions
[i
] == NULL
) {
94 /* found an empty slot, use it */
99 /* new session identified */
101 /* no empty slots, add one to the list */
103 sessions
= reallocf(sessions
, ((nSessions
) * sizeof(serverSessionRef
)));
107 // allocate a new session for this server
108 sessions
[n
] = malloc(sizeof(serverSession
));
109 bzero(sessions
[n
], sizeof(serverSession
));
111 // create server port
112 context
.info
= sessions
[n
];
113 context
.copyDescription
= copyDescription
;
115 if (server
== MACH_PORT_NULL
) {
116 // create mach port for SCDynamicStore client
117 (void) mach_port_allocate(mach_task_self(),
118 MACH_PORT_RIGHT_RECEIVE
,
123 // Note: we create the CFMachPort *before* we insert a send
124 // right present to ensure that CF does not establish
125 // its dead name notification.
127 sessions
[n
]->serverPort
= CFMachPortCreateWithPort(NULL
,
133 if (server
== MACH_PORT_NULL
) {
134 // insert send right that will be moved to the client
135 (void) mach_port_insert_right(mach_task_self(),
138 MACH_MSG_TYPE_MAKE_SEND
);
141 sessions
[n
]->key
= mp
;
142 // sessions[n]->serverRunLoopSource = NULL;
143 // sessions[n]->store = NULL;
144 sessions
[n
]->callerEUID
= 1; /* not "root" */
145 sessions
[n
]->callerRootAccess
= UNKNOWN
;
146 sessions
[n
]->callerWriteAccess
= UNKNOWN
;
154 removeSession(mach_port_t server
)
157 serverSessionRef thisSession
;
158 CFStringRef sessionKey
;
160 for (i
= 0; i
< nSessions
; i
++) {
161 thisSession
= sessions
[i
];
163 if (thisSession
== NULL
) {
164 /* found an empty slot, skip it */
166 } else if (thisSession
->key
== server
) {
168 * We don't need any remaining information in the
169 * sessionData dictionary, remove it.
171 sessionKey
= CFStringCreateWithFormat(NULL
, NULL
, CFSTR("%d"), server
);
172 CFDictionaryRemoveValue(sessionData
, sessionKey
);
173 CFRelease(sessionKey
);
176 * Lastly, get rid of the per-session structure.
191 cleanupSession(mach_port_t server
)
195 for (i
= 0; i
< nSessions
; i
++) {
196 serverSessionRef thisSession
= sessions
[i
];
198 if ((thisSession
!= NULL
) && (thisSession
->key
== server
)) {
200 * session entry still exists.
203 if (_configd_trace
) {
204 SCTrace(TRUE
, _configd_trace
, CFSTR("cleanup : %5d\n"), server
);
208 * Ensure that any changes made while we held the "lock"
211 if ((storeLocked
> 0) &&
212 ((SCDynamicStorePrivateRef
)thisSession
->store
)->locked
) {
214 * swap store and associated data which, after
215 * being closed, will result in the restoration
216 * of the original pre-"locked" data.
218 _swapLockedStoreData();
222 * Close any open connections including cancelling any outstanding
223 * notification requests and releasing any locks.
225 __MACH_PORT_DEBUG(TRUE
, "*** cleanupSession", server
);
226 (void) __SCDynamicStoreClose(&thisSession
->store
, TRUE
);
227 __MACH_PORT_DEBUG(TRUE
, "*** cleanupSession (after __SCDynamicStoreClose)", server
);
230 * Our send right has already been removed. Remove our receive right.
232 (void) mach_port_mod_refs(mach_task_self(), server
, MACH_PORT_RIGHT_RECEIVE
, -1);
235 * Lastly, remove the session entry.
237 removeSession(server
);
243 SCLog(TRUE
, LOG_ERR
, CFSTR("MACH_NOTIFY_NO_SENDERS w/no session, port = %d"), server
);
244 __MACH_PORT_DEBUG(TRUE
, "*** cleanupSession w/no session", server
);
251 listSessions(FILE *f
)
255 SCPrint(TRUE
, f
, CFSTR("Current sessions :\n"));
256 for (i
= 0; i
< nSessions
; i
++) {
257 serverSessionRef thisSession
= sessions
[i
];
259 if (thisSession
== NULL
) {
263 SCPrint(TRUE
, f
, CFSTR("\t%d : port = 0x%x"), i
, thisSession
->key
);
265 if (thisSession
->store
!= NULL
) {
266 SCDynamicStorePrivateRef storePrivate
= (SCDynamicStorePrivateRef
)thisSession
->store
;
268 if (storePrivate
->notifySignalTask
!= TASK_NULL
) {
269 SCPrint(TRUE
, f
, CFSTR(", task = %d"), storePrivate
->notifySignalTask
);
273 if (sessionData
!= NULL
) {
274 CFDictionaryRef info
;
277 key
= CFStringCreateWithFormat(NULL
, NULL
, CFSTR("%d"), thisSession
->key
);
278 info
= CFDictionaryGetValue(sessionData
, key
);
283 name
= CFDictionaryGetValue(info
, kSCDName
);
285 SCPrint(TRUE
, f
, CFSTR(", name = %@"), name
);
290 if (thisSession
->serverPort
!= NULL
) {
291 SCPrint(TRUE
, f
, CFSTR("\n\t\t%@"), thisSession
->serverPort
);
294 if (thisSession
->serverRunLoopSource
!= NULL
) {
295 SCPrint(TRUE
, f
, CFSTR("\n\t\t%@"), thisSession
->serverRunLoopSource
);
298 SCPrint(TRUE
, f
, CFSTR("\n"));
301 SCPrint(TRUE
, f
, CFSTR("\n"));
308 #include <Security/Security.h>
309 #include <Security/SecTask.h>
312 sessionName(serverSessionRef session
)
314 CFDictionaryRef info
;
315 CFStringRef name
= NULL
;
316 CFStringRef sessionKey
;
318 sessionKey
= CFStringCreateWithFormat(NULL
, NULL
, CFSTR("%d"), session
->key
);
319 info
= CFDictionaryGetValue(sessionData
, sessionKey
);
320 CFRelease(sessionKey
);
323 name
= CFDictionaryGetValue(info
, kSCDName
);
326 return (name
!= NULL
) ? name
: CFSTR("???");
331 hasEntitlement(serverSessionRef session
, CFStringRef entitlement
)
333 Boolean hasEntitlement
= FALSE
;
336 /* Create the security task from the audit token. */
337 task
= SecTaskCreateWithAuditToken(NULL
, session
->auditToken
);
339 CFErrorRef error
= NULL
;
342 /* Get the value for the entitlement. */
343 value
= SecTaskCopyValueForEntitlement(task
, kSCWriteEntitlementName
, &error
);
345 if (isA_CFBoolean(value
)) {
346 if (CFBooleanGetValue(value
)) {
347 /* if client DOES have entitlement */
348 hasEntitlement
= YES
;
352 CFSTR("hasEntitlement: entitlement not valid: %@"),
353 sessionName(session
));
357 } else if (error
!= NULL
) {
359 CFSTR("hasEntitlement SecTaskCopyValueForEntitlement() failed, error=%@: %@"),
361 sessionName(session
));
368 CFSTR("hasEntitlement SecTaskCreateWithAuditToken() failed: %@"),
369 sessionName(session
));
372 return hasEntitlement
;
375 #endif // TARGET_OS_IPHONE
380 hasRootAccess(serverSessionRef session
)
382 if (session
->callerRootAccess
== UNKNOWN
) {
384 * get the credentials associated with the caller.
386 audit_token_to_au32(session
->auditToken
,
388 &session
->callerEUID
, // euid
396 session
->callerRootAccess
= (session
->callerEUID
== 0) ? YES
: NO
;
399 return (session
->callerRootAccess
== YES
) ? TRUE
: FALSE
;
405 hasWriteAccess(serverSessionRef session
)
407 if (session
->callerWriteAccess
== UNKNOWN
) {
408 /* assume that the client DOES NOT have the entitlement */
409 session
->callerWriteAccess
= NO
;
411 if (hasRootAccess(session
)) {
412 // grant write access to eUID==0 processes
413 session
->callerWriteAccess
= YES
;
416 else if (hasEntitlement(session
, kSCWriteEntitlementName
)) {
417 // grant write access to "entitled" processes
418 session
->callerWriteAccess
= YES
;
420 #endif // TARGET_OS_IPHONE
423 return (session
->callerWriteAccess
== YES
) ? TRUE
: FALSE
;