]> git.saurik.com Git - apple/configd.git/blob - configd.tproj/session.c
bc25fe2afc53496486a7bde947ec0a8f8aa35612
[apple/configd.git] / configd.tproj / session.c
1 /*
2 * Copyright (c) 2000, 2001, 2003-2005, 2007-2009 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 /*
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
34 #include "configd.h"
35 #include "configd_server.h"
36 #include "session.h"
37
38 #include <unistd.h>
39 #include <bsm/libbsm.h>
40
41 /* information maintained for each active session */
42 static serverSessionRef *sessions = NULL;
43 static int nSessions = 0;
44
45
46 __private_extern__
47 serverSessionRef
48 getSession(mach_port_t server)
49 {
50 int i;
51
52 if (server == MACH_PORT_NULL) {
53 SCLog(TRUE, LOG_ERR, CFSTR("Excuse me, why is getSession() being called with an invalid port?"));
54 return NULL;
55 }
56
57 for (i = 0; i < nSessions; i++) {
58 serverSessionRef thisSession = sessions[i];
59
60 if (thisSession == NULL) {
61 /* found an empty slot, skip it */
62 continue;
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)) {
67 return thisSession;
68 }
69 }
70
71 /* no sessions available */
72 return NULL;
73 }
74
75
76 __private_extern__
77 serverSessionRef
78 addSession(mach_port_t server, CFStringRef (*copyDescription)(const void *info))
79 {
80 CFMachPortContext context = { 0, NULL, NULL, NULL, NULL };
81 mach_port_t mp = server;
82 int n = -1;
83
84 if (nSessions <= 0) {
85 /* new session (actually, the first) found */
86 sessions = malloc(sizeof(serverSessionRef));
87 n = 0;
88 nSessions = 1;
89 } else {
90 int i;
91
92 for (i = 0; i < nSessions; i++) {
93 if (sessions[i] == NULL) {
94 /* found an empty slot, use it */
95 n = i;
96 break;
97 }
98 }
99 /* new session identified */
100 if (n < 0) {
101 /* no empty slots, add one to the list */
102 n = nSessions++;
103 sessions = reallocf(sessions, ((nSessions) * sizeof(serverSessionRef)));
104 }
105 }
106
107 // allocate a new session for this server
108 sessions[n] = malloc(sizeof(serverSession));
109 bzero(sessions[n], sizeof(serverSession));
110
111 // create server port
112 context.info = sessions[n];
113 context.copyDescription = copyDescription;
114
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,
119 &mp);
120 }
121
122 //
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.
126 //
127 sessions[n]->serverPort = CFMachPortCreateWithPort(NULL,
128 mp,
129 configdCallback,
130 &context,
131 NULL);
132
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(),
136 mp,
137 mp,
138 MACH_MSG_TYPE_MAKE_SEND);
139 }
140
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;
147
148 return sessions[n];
149 }
150
151
152 __private_extern__
153 void
154 removeSession(mach_port_t server)
155 {
156 int i;
157 serverSessionRef thisSession;
158 CFStringRef sessionKey;
159
160 for (i = 0; i < nSessions; i++) {
161 thisSession = sessions[i];
162
163 if (thisSession == NULL) {
164 /* found an empty slot, skip it */
165 continue;
166 } else if (thisSession->key == server) {
167 /*
168 * We don't need any remaining information in the
169 * sessionData dictionary, remove it.
170 */
171 sessionKey = CFStringCreateWithFormat(NULL, NULL, CFSTR("%d"), server);
172 CFDictionaryRemoveValue(sessionData, sessionKey);
173 CFRelease(sessionKey);
174
175 /*
176 * Lastly, get rid of the per-session structure.
177 */
178 free(thisSession);
179 sessions[i] = NULL;
180
181 return;
182 }
183 }
184
185 return;
186 }
187
188
189 __private_extern__
190 void
191 cleanupSession(mach_port_t server)
192 {
193 int i;
194
195 for (i = 0; i < nSessions; i++) {
196 serverSessionRef thisSession = sessions[i];
197
198 if ((thisSession != NULL) && (thisSession->key == server)) {
199 /*
200 * session entry still exists.
201 */
202
203 if (_configd_trace) {
204 SCTrace(TRUE, _configd_trace, CFSTR("cleanup : %5d\n"), server);
205 }
206
207 /*
208 * Ensure that any changes made while we held the "lock"
209 * are discarded.
210 */
211 if ((storeLocked > 0) &&
212 ((SCDynamicStorePrivateRef)thisSession->store)->locked) {
213 /*
214 * swap store and associated data which, after
215 * being closed, will result in the restoration
216 * of the original pre-"locked" data.
217 */
218 _swapLockedStoreData();
219 }
220
221 /*
222 * Close any open connections including cancelling any outstanding
223 * notification requests and releasing any locks.
224 */
225 __MACH_PORT_DEBUG(TRUE, "*** cleanupSession", server);
226 (void) __SCDynamicStoreClose(&thisSession->store, TRUE);
227 __MACH_PORT_DEBUG(TRUE, "*** cleanupSession (after __SCDynamicStoreClose)", server);
228
229 /*
230 * Our send right has already been removed. Remove our receive right.
231 */
232 (void) mach_port_mod_refs(mach_task_self(), server, MACH_PORT_RIGHT_RECEIVE, -1);
233
234 /*
235 * Lastly, remove the session entry.
236 */
237 removeSession(server);
238
239 return;
240 }
241 }
242
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);
245 return;
246 }
247
248
249 __private_extern__
250 void
251 listSessions(FILE *f)
252 {
253 int i;
254
255 SCPrint(TRUE, f, CFSTR("Current sessions :\n"));
256 for (i = 0; i < nSessions; i++) {
257 serverSessionRef thisSession = sessions[i];
258
259 if (thisSession == NULL) {
260 continue;
261 }
262
263 SCPrint(TRUE, f, CFSTR("\t%d : port = 0x%x"), i, thisSession->key);
264
265 if (thisSession->store != NULL) {
266 SCDynamicStorePrivateRef storePrivate = (SCDynamicStorePrivateRef)thisSession->store;
267
268 if (storePrivate->notifySignalTask != TASK_NULL) {
269 SCPrint(TRUE, f, CFSTR(", task = %d"), storePrivate->notifySignalTask);
270 }
271 }
272
273 if (sessionData != NULL) {
274 CFDictionaryRef info;
275 CFStringRef key;
276
277 key = CFStringCreateWithFormat(NULL, NULL, CFSTR("%d"), thisSession->key);
278 info = CFDictionaryGetValue(sessionData, key);
279 CFRelease(key);
280 if (info != NULL) {
281 CFStringRef name;
282
283 name = CFDictionaryGetValue(info, kSCDName);
284 if (name != NULL) {
285 SCPrint(TRUE, f, CFSTR(", name = %@"), name);
286 }
287 }
288 }
289
290 if (thisSession->serverPort != NULL) {
291 SCPrint(TRUE, f, CFSTR("\n\t\t%@"), thisSession->serverPort);
292 }
293
294 if (thisSession->serverRunLoopSource != NULL) {
295 SCPrint(TRUE, f, CFSTR("\n\t\t%@"), thisSession->serverRunLoopSource);
296 }
297
298 SCPrint(TRUE, f, CFSTR("\n"));
299 }
300
301 SCPrint(TRUE, f, CFSTR("\n"));
302 return;
303 }
304
305
306 #if TARGET_OS_IPHONE
307
308 #include <Security/Security.h>
309 #include <Security/SecTask.h>
310
311 static CFStringRef
312 sessionName(serverSessionRef session)
313 {
314 CFDictionaryRef info;
315 CFStringRef name = NULL;
316 CFStringRef sessionKey;
317
318 sessionKey = CFStringCreateWithFormat(NULL, NULL, CFSTR("%d"), session->key);
319 info = CFDictionaryGetValue(sessionData, sessionKey);
320 CFRelease(sessionKey);
321
322 if (info != NULL) {
323 name = CFDictionaryGetValue(info, kSCDName);
324 }
325
326 return (name != NULL) ? name : CFSTR("???");
327 }
328
329
330 static Boolean
331 hasEntitlement(serverSessionRef session, CFStringRef entitlement)
332 {
333 Boolean hasEntitlement = FALSE;
334 SecTaskRef task;
335
336 /* Create the security task from the audit token. */
337 task = SecTaskCreateWithAuditToken(NULL, session->auditToken);
338 if (task != NULL) {
339 CFErrorRef error = NULL;
340 CFTypeRef value;
341
342 /* Get the value for the entitlement. */
343 value = SecTaskCopyValueForEntitlement(task, kSCWriteEntitlementName, &error);
344 if (value != NULL) {
345 if (isA_CFBoolean(value)) {
346 if (CFBooleanGetValue(value)) {
347 /* if client DOES have entitlement */
348 hasEntitlement = YES;
349 }
350 } else {
351 SCLog(TRUE, LOG_ERR,
352 CFSTR("hasEntitlement: entitlement not valid: %@"),
353 sessionName(session));
354 }
355
356 CFRelease(value);
357 } else if (error != NULL) {
358 SCLog(TRUE, LOG_ERR,
359 CFSTR("hasEntitlement SecTaskCopyValueForEntitlement() failed, error=%@: %@"),
360 error,
361 sessionName(session));
362 CFRelease(error);
363 }
364
365 CFRelease(task);
366 } else {
367 SCLog(TRUE, LOG_ERR,
368 CFSTR("hasEntitlement SecTaskCreateWithAuditToken() failed: %@"),
369 sessionName(session));
370 }
371
372 return hasEntitlement;
373 }
374
375 #endif // TARGET_OS_IPHONE
376
377
378 __private_extern__
379 Boolean
380 hasRootAccess(serverSessionRef session)
381 {
382 if (session->callerRootAccess == UNKNOWN) {
383 /*
384 * get the credentials associated with the caller.
385 */
386 audit_token_to_au32(session->auditToken,
387 NULL, // auidp
388 &session->callerEUID, // euid
389 NULL, // egid
390 NULL, // ruid
391 NULL, // rgid
392 NULL, // pid
393 NULL, // asid
394 NULL); // tid
395
396 session->callerRootAccess = (session->callerEUID == 0) ? YES : NO;
397 }
398
399 return (session->callerRootAccess == YES) ? TRUE : FALSE;
400 }
401
402
403 __private_extern__
404 Boolean
405 hasWriteAccess(serverSessionRef session)
406 {
407 if (session->callerWriteAccess == UNKNOWN) {
408 /* assume that the client DOES NOT have the entitlement */
409 session->callerWriteAccess = NO;
410
411 if (hasRootAccess(session)) {
412 // grant write access to eUID==0 processes
413 session->callerWriteAccess = YES;
414 }
415 #if TARGET_OS_IPHONE
416 else if (hasEntitlement(session, kSCWriteEntitlementName)) {
417 // grant write access to "entitled" processes
418 session->callerWriteAccess = YES;
419 }
420 #endif // TARGET_OS_IPHONE
421 }
422
423 return (session->callerWriteAccess == YES) ? TRUE : FALSE;
424 }