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