]> git.saurik.com Git - apple/configd.git/blame - SystemConfiguration.fproj/helper/SCHelper_server.c
configd-395.11.tar.gz
[apple/configd.git] / SystemConfiguration.fproj / helper / SCHelper_server.c
CommitLineData
edebe297 1/*
6bb65964 2 * Copyright (c) 2005-2011 Apple Inc. All rights reserved.
edebe297
A
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
a40a14f8 24#include <getopt.h>
6bb65964
A
25#include <grp.h>
26#include <launch.h>
27#include <pthread.h>
28#include <stdlib.h>
edebe297 29#include <unistd.h>
6bb65964 30#include <bsm/libbsm.h>
edebe297 31#include <sys/types.h>
6bb65964
A
32#include <sysexits.h>
33
34//#define DEBUG_MACH_PORT_ALLOCATIONS
edebe297
A
35
36#include <CoreFoundation/CoreFoundation.h>
a40a14f8 37#include <CoreFoundation/CFRuntime.h>
edebe297
A
38#include <SystemConfiguration/SystemConfiguration.h>
39#include <SystemConfiguration/SCPrivate.h>
a40a14f8 40#include <SystemConfiguration/SCValidation.h>
edebe297
A
41
42#include "SCPreferencesInternal.h"
43#include "SCHelper_client.h"
6bb65964
A
44#include "helper_types.h"
45
46
47#pragma mark -
48#pragma mark SCHelper session managment
edebe297
A
49
50
a40a14f8 51#if TARGET_OS_IPHONE
a40a14f8 52
6bb65964
A
53//
54// entitlement used to control write access to a given "prefsID"
55//
56#define kSCWriteEntitlementName CFSTR("com.apple.SystemConfiguration.SCPreferences-write-access")
a40a14f8 57
6bb65964
A
58//
59// entitlement used to allow limited [VPN configuration] write access to the "preferences.plist"
60//
61#define kSCVPNFilterEntitlementName CFSTR("com.apple.networking.vpn.configuration")
a40a14f8 62
6bb65964 63#endif // TARGET_OS_IPHONE
a40a14f8
A
64
65
6bb65964
A
66typedef enum { NO = 0, YES, UNKNOWN } lazyBoolean;
67
a40a14f8
A
68typedef const struct __SCHelperSession * SCHelperSessionRef;
69
70typedef struct {
71
72 // base CFType information
73 CFRuntimeBase cfBase;
74
6bb65964
A
75 // per session lock
76 pthread_mutex_t lock;
77
a40a14f8
A
78 // authorization
79 AuthorizationRef authorization;
6bb65964
A
80
81 // session mach port
82 mach_port_t port;
83 CFMachPortRef mp;
84
85 // Mach security audit trailer for evaluating credentials
86 audit_token_t auditToken;
87
88 // write access entitlement associated with this session
89 lazyBoolean callerWriteAccess;
90
91 // VPN configuration filtering
92 CFArrayRef vpnFilter;
a40a14f8
A
93
94 // preferences
95 SCPreferencesRef prefs;
96
97} SCHelperSessionPrivate, *SCHelperSessionPrivateRef;
98
99
6bb65964
A
100static CFStringRef __SCHelperSessionCopyDescription (CFTypeRef cf);
101static void __SCHelperSessionDeallocate (CFTypeRef cf);
102
103
104static CFTypeID __kSCHelperSessionTypeID = _kCFRuntimeNotATypeID;
105static Boolean debug = FALSE;
106static pthread_once_t initialized = PTHREAD_ONCE_INIT;
107static CFRunLoopRef main_runLoop = NULL;
108static CFMutableSetRef sessions = NULL;
109static int sessions_closed = 0; // count of sessions recently closed
110static int sessions_generation = 0;
111static pthread_mutex_t sessions_lock = PTHREAD_MUTEX_INITIALIZER;
112
113
114#pragma mark -
115
116
a40a14f8
A
117static AuthorizationRef
118__SCHelperSessionGetAuthorization(SCHelperSessionRef session)
119{
120 SCHelperSessionPrivateRef sessionPrivate = (SCHelperSessionPrivateRef)session;
121
122 return sessionPrivate->authorization;
123}
124
125
126static Boolean
127__SCHelperSessionSetAuthorization(SCHelperSessionRef session, CFTypeRef authorizationData)
128{
129 Boolean ok = TRUE;
130 SCHelperSessionPrivateRef sessionPrivate = (SCHelperSessionPrivateRef)session;
131
6bb65964
A
132 pthread_mutex_lock(&sessionPrivate->lock);
133
a40a14f8
A
134#if !TARGET_OS_IPHONE
135 if (sessionPrivate->authorization != NULL) {
136 AuthorizationFree(sessionPrivate->authorization, kAuthorizationFlagDefaults);
137// AuthorizationFree(sessionPrivate->authorization, kAuthorizationFlagDestroyRights);
138 sessionPrivate->authorization = NULL;
139 }
140
141 if (isA_CFData(authorizationData)) {
142 AuthorizationExternalForm extForm;
143
144 if (CFDataGetLength(authorizationData) == sizeof(extForm.bytes)) {
145 OSStatus err;
146
147 bcopy(CFDataGetBytePtr(authorizationData), extForm.bytes, sizeof(extForm.bytes));
148 err = AuthorizationCreateFromExternalForm(&extForm,
149 &sessionPrivate->authorization);
150 if (err != errAuthorizationSuccess) {
151 SCLog(TRUE, LOG_ERR,
152 CFSTR("AuthorizationCreateFromExternalForm() failed: status = %d"),
153 (int)err);
154 sessionPrivate->authorization = NULL;
155 ok = FALSE;
156 }
157 }
158 }
159#else // !TARGET_OS_IPHONE
160 if (sessionPrivate->authorization != NULL) {
161 CFRelease(sessionPrivate->authorization);
162 sessionPrivate->authorization = NULL;
163 }
164
165 if (isA_CFString(authorizationData)) {
166 sessionPrivate->authorization = (void *)CFRetain(authorizationData);
167 }
168#endif // !TARGET_OS_IPHONE
169
6bb65964
A
170 pthread_mutex_unlock(&sessionPrivate->lock);
171
a40a14f8
A
172 return ok;
173}
174
175
6bb65964
A
176static SCPreferencesRef
177__SCHelperSessionGetPreferences(SCHelperSessionRef session)
178{
179 SCHelperSessionPrivateRef sessionPrivate = (SCHelperSessionPrivateRef)session;
180
181 return sessionPrivate->prefs;
182}
183
184
a40a14f8 185static void
6bb65964 186__SCHelperSessionSetThreadName(SCHelperSessionRef session)
a40a14f8 187{
6bb65964
A
188 char *caller = NULL;
189 char name[64];
190 char *path = NULL;
191 char *path_s = NULL;
a40a14f8
A
192 SCHelperSessionPrivateRef sessionPrivate = (SCHelperSessionPrivateRef)session;
193
6bb65964
A
194
195 if (sessionPrivate->mp == NULL) {
196 return;
197 }
198
199 if (sessionPrivate->prefs != NULL) {
200 SCPreferencesPrivateRef prefsPrivate = (SCPreferencesPrivateRef)sessionPrivate->prefs;
201
202 if (prefsPrivate->name != NULL) {
203 caller = _SC_cfstring_to_cstring(prefsPrivate->name,
204 NULL,
205 0,
206 kCFStringEncodingUTF8);
207 }
208
209 path = (prefsPrivate->newPath != NULL) ? prefsPrivate->newPath : prefsPrivate->path;
210 if (path != NULL) {
211 path_s = strrchr(path, '/');
212 if (path_s != NULL) {
213 path = path_s;
214 }
215 }
216 }
217
218 if (caller != NULL) {
219 snprintf(name, sizeof(name), "SESSION|%p|%s|%s%s",
220 (void *)(uintptr_t)CFMachPortGetPort(sessionPrivate->mp),
221 (caller != NULL) ? caller : "?",
222 (path_s != NULL) ? "*/" : "",
223 (path != NULL) ? path : "?");
224 CFAllocatorDeallocate(NULL, caller);
225 } else {
226 snprintf(name, sizeof(name), "SESSION|%p",
227 (void *)(uintptr_t)CFMachPortGetPort(sessionPrivate->mp));
228 }
229
230 pthread_setname_np(name);
231
a40a14f8
A
232 return;
233}
234
235
236static Boolean
6bb65964 237__SCHelperSessionSetPreferences(SCHelperSessionRef session, SCPreferencesRef prefs)
a40a14f8
A
238{
239 SCHelperSessionPrivateRef sessionPrivate = (SCHelperSessionPrivateRef)session;
240
6bb65964
A
241 pthread_mutex_lock(&sessionPrivate->lock);
242
243 if (prefs != NULL) {
244 CFRetain(prefs);
245 }
246 if (sessionPrivate->prefs != NULL) {
247 SCLog(debug, LOG_DEBUG,
248 CFSTR("%p : close"),
249 session);
250 CFRelease(sessionPrivate->prefs);
251 }
252 if (prefs != NULL) {
253 SCLog(debug, LOG_DEBUG,
254 CFSTR("%p : open, prefs = %@"),
255 session,
256 prefs);
257 }
258 sessionPrivate->prefs = prefs;
259
260 __SCHelperSessionSetThreadName(session);
261
262 pthread_mutex_unlock(&sessionPrivate->lock);
263
a40a14f8
A
264 return TRUE;
265}
a40a14f8 266
6bb65964
A
267
268static CFArrayRef
269__SCHelperSessionGetVPNFilter(SCHelperSessionRef session)
a40a14f8
A
270{
271 SCHelperSessionPrivateRef sessionPrivate = (SCHelperSessionPrivateRef)session;
272
6bb65964 273 return sessionPrivate->vpnFilter;
a40a14f8
A
274}
275
276
277static Boolean
6bb65964 278__SCHelperSessionSetVPNFilter(SCHelperSessionRef session, CFArrayRef vpnFilter)
a40a14f8
A
279{
280 SCHelperSessionPrivateRef sessionPrivate = (SCHelperSessionPrivateRef)session;
281
6bb65964
A
282 pthread_mutex_lock(&sessionPrivate->lock);
283
284 if (vpnFilter != NULL) {
285 CFRetain(vpnFilter);
a40a14f8 286 }
6bb65964
A
287 if (sessionPrivate->vpnFilter != NULL) {
288 CFRelease(sessionPrivate->vpnFilter);
a40a14f8 289 }
6bb65964
A
290 sessionPrivate->vpnFilter = vpnFilter;
291
292 pthread_mutex_unlock(&sessionPrivate->lock);
a40a14f8
A
293
294 return TRUE;
295}
296
297
6bb65964
A
298static void
299__SCHelperSessionLog(const void *value, void *context)
300{
301 SCHelperSessionRef session = (SCHelperSessionRef)value;
302 SCHelperSessionPrivateRef sessionPrivate = (SCHelperSessionPrivateRef)session;
a40a14f8 303
6bb65964 304 pthread_mutex_lock(&sessionPrivate->lock);
a40a14f8 305
6bb65964
A
306 if ((sessionPrivate->mp != NULL) && (sessionPrivate->prefs != NULL)) {
307 SCPreferencesPrivateRef prefsPrivate = (SCPreferencesPrivateRef)sessionPrivate->prefs;
308
309 SCLog(TRUE, LOG_NOTICE,
310 CFSTR(" %p {port = %p, caller = %@, path = %s%s}"),
311 session,
312 CFMachPortGetPort(sessionPrivate->mp),
313 prefsPrivate->name,
314 prefsPrivate->newPath ? prefsPrivate->newPath : prefsPrivate->path,
315 prefsPrivate->locked ? ", locked" : "");
316 }
317
318 pthread_mutex_unlock(&sessionPrivate->lock);
319
320 return;
321}
322
323
324#pragma mark -
a40a14f8
A
325
326
327static const CFRuntimeClass __SCHelperSessionClass = {
328 0, // version
329 "SCHelperSession", // className
330 NULL, // init
331 NULL, // copy
332 __SCHelperSessionDeallocate, // dealloc
333 NULL, // equal
334 NULL, // hash
335 NULL, // copyFormattingDesc
336 __SCHelperSessionCopyDescription // copyDebugDesc
337};
338
339
340static CFStringRef
341__SCHelperSessionCopyDescription(CFTypeRef cf)
342{
343 CFAllocatorRef allocator = CFGetAllocator(cf);
344 CFMutableStringRef result;
345 SCHelperSessionPrivateRef sessionPrivate = (SCHelperSessionPrivateRef)cf;
346
6bb65964
A
347 pthread_mutex_lock(&sessionPrivate->lock);
348
a40a14f8
A
349 result = CFStringCreateMutable(allocator, 0);
350 CFStringAppendFormat(result, NULL, CFSTR("<SCHelperSession %p [%p]> {"), cf, allocator);
351 CFStringAppendFormat(result, NULL, CFSTR("authorization = %p"), sessionPrivate->authorization);
6bb65964
A
352 if (sessionPrivate->mp != NULL) {
353 CFStringAppendFormat(result, NULL,
354 CFSTR(", mp = %p (port = %p)"),
355 sessionPrivate->mp,
356 CFMachPortGetPort(sessionPrivate->mp));
357 }
358 if (sessionPrivate->prefs != NULL) {
359 CFStringAppendFormat(result, NULL, CFSTR(", prefs = %@"), sessionPrivate->prefs);
360 }
a40a14f8
A
361 CFStringAppendFormat(result, NULL, CFSTR("}"));
362
6bb65964
A
363 pthread_mutex_unlock(&sessionPrivate->lock);
364
a40a14f8
A
365 return result;
366}
367
368
369static void
370__SCHelperSessionDeallocate(CFTypeRef cf)
371{
6bb65964
A
372 SCHelperSessionRef session = (SCHelperSessionRef)cf;
373 SCHelperSessionPrivateRef sessionPrivate = (SCHelperSessionPrivateRef)session;
374
375 // we're releasing "a" session so take the global lock...
376 pthread_mutex_lock(&sessions_lock);
a40a14f8
A
377
378 // release resources
6bb65964
A
379 __SCHelperSessionSetAuthorization(session, NULL);
380 __SCHelperSessionSetPreferences (session, NULL);
381 __SCHelperSessionSetVPNFilter (session, NULL);
382 pthread_mutex_destroy(&sessionPrivate->lock);
a40a14f8
A
383
384 // we no longer need/want to track this session
a40a14f8 385 CFSetRemoveValue(sessions, sessionPrivate);
6bb65964 386 sessions_generation++;
a40a14f8 387 sessions_closed++;
6bb65964
A
388
389 // release the global lock, wake up the main runloop, all done
a40a14f8
A
390 pthread_mutex_unlock(&sessions_lock);
391 CFRunLoopWakeUp(main_runLoop);
392
393 return;
394}
395
396
397static void
398__SCHelperSessionInitialize(void)
399{
400 __kSCHelperSessionTypeID = _CFRuntimeRegisterClass(&__SCHelperSessionClass);
401 return;
402}
403
404
405static SCHelperSessionRef
406__SCHelperSessionCreate(CFAllocatorRef allocator)
407{
408 SCHelperSessionPrivateRef sessionPrivate;
409 uint32_t size;
410
6bb65964 411 // initialize runtime
a40a14f8
A
412 pthread_once(&initialized, __SCHelperSessionInitialize);
413
6bb65964 414 // allocate session
a40a14f8
A
415 size = sizeof(SCHelperSessionPrivate) - sizeof(CFRuntimeBase);
416 sessionPrivate = (SCHelperSessionPrivateRef)_CFRuntimeCreateInstance(allocator,
417 __kSCHelperSessionTypeID,
418 size,
419 NULL);
420 if (sessionPrivate == NULL) {
421 return NULL;
422 }
423
6bb65964
A
424 if (pthread_mutex_init(&sessionPrivate->lock, NULL) != 0) {
425 SCLog(TRUE, LOG_ERR, CFSTR("pthread_mutex_init(): failure to initialize per session lock"));
426 CFRelease(sessionPrivate);
427 return NULL;
428 }
429 sessionPrivate->authorization = NULL;
430 sessionPrivate->port = MACH_PORT_NULL;
431 sessionPrivate->mp = NULL;
432 sessionPrivate->callerWriteAccess = UNKNOWN;
433 sessionPrivate->vpnFilter = NULL;
434 sessionPrivate->prefs = NULL;
a40a14f8
A
435
436 // keep track this session
437 pthread_mutex_lock(&sessions_lock);
438 if (sessions == NULL) {
6bb65964
A
439 const CFSetCallBacks mySetCallBacks = { 0, NULL, NULL, CFCopyDescription, CFEqual, CFHash };
440
441 // create a non-retaining set
442 sessions = CFSetCreateMutable(NULL, 0, &mySetCallBacks);
a40a14f8
A
443 }
444 CFSetAddValue(sessions, sessionPrivate);
6bb65964 445 sessions_generation++;
a40a14f8
A
446 pthread_mutex_unlock(&sessions_lock);
447
448 return (SCHelperSessionRef)sessionPrivate;
449}
450
451
6bb65964
A
452#pragma mark -
453
454
455static SCHelperSessionRef
456__SCHelperSessionFindWithPort(mach_port_t port)
457{
458 SCHelperSessionRef session = NULL;
459
460 // keep track this session
461 pthread_mutex_lock(&sessions_lock);
462 if (sessions != NULL) {
463 CFIndex i;
464 CFIndex n = CFSetGetCount(sessions);
465 const void * vals_q[16];
466 const void ** vals = vals_q;
467
468 if (n > (CFIndex)(sizeof(vals_q) / sizeof(SCHelperSessionRef)))
469 vals = CFAllocatorAllocate(NULL, n * sizeof(CFStringRef), 0);
470 CFSetGetValues(sessions, vals);
471 for (i = 0; i < n; i++) {
472 SCHelperSessionPrivateRef sessionPrivate;
473
474 sessionPrivate = (SCHelperSessionPrivateRef)vals[i];
475 if (sessionPrivate->port == port) {
476 session = (SCHelperSessionRef)sessionPrivate;
477 break;
478 }
479 }
480 if (vals != vals_q)
481 CFAllocatorDeallocate(NULL, vals);
482 }
483 pthread_mutex_unlock(&sessions_lock);
484
485 return session;
486}
487
488
a40a14f8
A
489#pragma mark -
490#pragma mark Helpers
edebe297
A
491
492
493/*
494 * EXIT
495 * (in) data = N/A
496 * (out) status = SCError()
497 * (out) reply = N/A
498 */
499static Boolean
a40a14f8 500do_Exit(SCHelperSessionRef session, void *info, CFDataRef data, uint32_t *status, CFDataRef *reply)
edebe297 501{
a40a14f8 502 *status = -1;
edebe297
A
503 return FALSE;
504}
505
506
507/*
508 * AUTHORIZE
509 * (in) data = AuthorizationExternalForm
510 * (out) status = OSStatus
511 * (out) reply = N/A
512 */
513static Boolean
a40a14f8 514do_Auth(SCHelperSessionRef session, void *info, CFDataRef data, uint32_t *status, CFDataRef *reply)
edebe297 515{
a40a14f8 516 Boolean ok;
edebe297 517
a40a14f8 518#if !TARGET_OS_IPHONE
edebe297 519
a40a14f8 520 ok = __SCHelperSessionSetAuthorization(session, data);
edebe297 521
a40a14f8 522#else //!TARGET_OS_IPHONE
edebe297 523
a40a14f8
A
524 CFStringRef authorizationInfo = NULL;
525
526 if ((data != NULL) && !_SCUnserializeString(&authorizationInfo, data, NULL, 0)) {
527 return FALSE;
edebe297
A
528 }
529
a40a14f8
A
530 if (!isA_CFString(authorizationInfo)) {
531 if (authorizationInfo != NULL) CFRelease(authorizationInfo);
532 return FALSE;
533 }
534
535 ok = __SCHelperSessionSetAuthorization(session, authorizationInfo);
536 if (authorizationInfo != NULL) CFRelease(authorizationInfo);
537
538#endif // !TARGET_OS_IPHONE
edebe297 539
a40a14f8 540 *status = ok ? 0 : 1;
edebe297
A
541 return TRUE;
542}
543
544
a40a14f8
A
545#if !TARGET_OS_IPHONE
546
547
edebe297
A
548/*
549 * SCHELPER_MSG_KEYCHAIN_COPY
550 * (in) data = unique_id
551 * (out) status = SCError()
552 * (out) reply = password
553 */
554static Boolean
a40a14f8 555do_keychain_copy(SCHelperSessionRef session, void *info, CFDataRef data, uint32_t *status, CFDataRef *reply)
edebe297 556{
6bb65964 557 Boolean ok = FALSE;
a40a14f8
A
558 SCPreferencesRef prefs;
559 CFStringRef unique_id = NULL;
edebe297
A
560
561 if ((data != NULL) && !_SCUnserializeString(&unique_id, data, NULL, 0)) {
562 return FALSE;
563 }
564
6bb65964
A
565 if (unique_id != NULL) {
566 if (isA_CFString(unique_id)) {
567 prefs = __SCHelperSessionGetPreferences(session);
568 *reply = _SCPreferencesSystemKeychainPasswordItemCopy(prefs, unique_id);
569 if (*reply == NULL) {
570 *status = SCError();
571 }
572 ok = TRUE;
573 }
edebe297 574
6bb65964 575 CFRelease(unique_id);
edebe297
A
576 }
577
6bb65964 578 return ok;
edebe297
A
579}
580
581
582/*
583 * SCHELPER_MSG_KEYCHAIN_EXISTS
584 * (in) data = unique_id
585 * (out) status = SCError()
586 * (out) reply = N/A
587 */
588static Boolean
a40a14f8 589do_keychain_exists(SCHelperSessionRef session, void *info, CFDataRef data, uint32_t *status, CFDataRef *reply)
edebe297 590{
6bb65964 591 Boolean ok = FALSE;
a40a14f8
A
592 SCPreferencesRef prefs;
593 CFStringRef unique_id = NULL;
edebe297
A
594
595 if ((data != NULL) && !_SCUnserializeString(&unique_id, data, NULL, 0)) {
596 return FALSE;
597 }
598
6bb65964
A
599 if (unique_id != NULL) {
600 if (isA_CFString(unique_id)) {
601 prefs = __SCHelperSessionGetPreferences(session);
602 ok = _SCPreferencesSystemKeychainPasswordItemExists(prefs, unique_id);
603 if (!ok) {
604 *status = SCError();
605 }
606 }
edebe297 607
6bb65964 608 CFRelease(unique_id);
edebe297
A
609 }
610
6bb65964 611 return ok;
edebe297
A
612}
613
614
615/*
616 * SCHELPER_MSG_KEYCHAIN_REMOVE
617 * (in) data = unique_id
618 * (out) status = SCError()
619 * (out) reply = N/A
620 */
621static Boolean
a40a14f8 622do_keychain_remove(SCHelperSessionRef session, void *info, CFDataRef data, uint32_t *status, CFDataRef *reply)
edebe297 623{
6bb65964 624 Boolean ok = FALSE;
a40a14f8
A
625 SCPreferencesRef prefs;
626 CFStringRef unique_id = NULL;
edebe297
A
627
628 if ((data != NULL) && !_SCUnserializeString(&unique_id, data, NULL, 0)) {
629 return FALSE;
630 }
631
6bb65964
A
632 if (unique_id != NULL) {
633 if (isA_CFString(unique_id)) {
634 prefs = __SCHelperSessionGetPreferences(session);
635 ok = _SCPreferencesSystemKeychainPasswordItemRemove(prefs, unique_id);
636 if (!ok) {
637 *status = SCError();
638 }
639 }
edebe297 640
6bb65964 641 CFRelease(unique_id);
edebe297
A
642 }
643
6bb65964 644 return ok;
edebe297
A
645}
646
647
648/*
649 * SCHELPER_MSG_KEYCHAIN_SET
650 * (in) data = options dictionary
651 * (out) status = SCError()
652 * (out) reply = N/A
653 */
654static Boolean
a40a14f8 655do_keychain_set(SCHelperSessionRef session, void *info, CFDataRef data, uint32_t *status, CFDataRef *reply)
edebe297 656{
a40a14f8
A
657 CFStringRef account;
658 CFStringRef description;
659 CFArrayRef executablePaths = NULL;
660 CFStringRef label;
661 Boolean ok;
662 CFDictionaryRef options = NULL;
663 CFDataRef password;
664 SCPreferencesRef prefs;
665 CFStringRef unique_id;
edebe297
A
666
667 if ((data != NULL) && !_SCUnserialize((CFPropertyListRef *)&options, data, NULL, 0)) {
668 return FALSE;
669 }
670
6bb65964
A
671 if (options != NULL) {
672 if (!isA_CFDictionary(options)) {
673 CFRelease(options);
674 return FALSE;
675 }
676 } else {
edebe297
A
677 return FALSE;
678 }
679
680 if (CFDictionaryGetValueIfPresent(options,
681 kSCKeychainOptionsAllowedExecutables,
682 (const void **)&executablePaths)) {
683 CFMutableArrayRef executableURLs;
684 CFIndex i;
685 CFIndex n;
686 CFMutableDictionaryRef newOptions;
687
688 executableURLs = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
689 n = CFArrayGetCount(executablePaths);
690 for (i = 0; i < n; i++) {
691 CFDataRef path;
692 CFURLRef url;
693
694 path = CFArrayGetValueAtIndex(executablePaths, i);
695 url = CFURLCreateFromFileSystemRepresentation(NULL,
696 CFDataGetBytePtr(path),
697 CFDataGetLength(path),
698 FALSE);
699 if (url != NULL) {
700 CFArrayAppendValue(executableURLs, url);
701 CFRelease(url);
702 }
703 }
704
705 newOptions = CFDictionaryCreateMutableCopy(NULL, 0, options);
706 CFDictionarySetValue(newOptions, kSCKeychainOptionsAllowedExecutables, executableURLs);
707 CFRelease(executableURLs);
708
709 CFRelease(options);
710 options = newOptions;
711 }
712
713 unique_id = CFDictionaryGetValue(options, kSCKeychainOptionsUniqueID);
714 label = CFDictionaryGetValue(options, kSCKeychainOptionsLabel);
715 description = CFDictionaryGetValue(options, kSCKeychainOptionsDescription);
716 account = CFDictionaryGetValue(options, kSCKeychainOptionsAccount);
717 password = CFDictionaryGetValue(options, kSCKeychainOptionsPassword);
718
a40a14f8 719 prefs = __SCHelperSessionGetPreferences(session);
edebe297
A
720 ok = _SCPreferencesSystemKeychainPasswordItemSet(prefs,
721 unique_id,
722 label,
723 description,
724 account,
725 password,
726 options);
727 CFRelease(options);
edebe297
A
728 if (!ok) {
729 *status = SCError();
730 }
731
732 return TRUE;
733}
734
735
a40a14f8
A
736#endif // !TARGET_OS_IPHONE
737
738
edebe297
A
739/*
740 * SCHELPER_MSG_INTERFACE_REFRESH
741 * (in) data = ifName
742 * (out) status = SCError()
743 * (out) reply = N/A
744 */
745static Boolean
a40a14f8 746do_interface_refresh(SCHelperSessionRef session, void *info, CFDataRef data, uint32_t *status, CFDataRef *reply)
edebe297
A
747{
748 CFStringRef ifName = NULL;
6bb65964 749 Boolean ok = FALSE;
edebe297
A
750
751 if ((data != NULL) && !_SCUnserializeString(&ifName, data, NULL, 0)) {
752 SCLog(TRUE, LOG_ERR, CFSTR("interface name not valid"));
753 return FALSE;
754 }
755
6bb65964
A
756 if (ifName != NULL) {
757 if (isA_CFString(ifName)) {
758 ok = _SCNetworkInterfaceForceConfigurationRefresh(ifName);
759 if (!ok) {
760 *status = SCError();
761 }
762 }
763
764 CFRelease(ifName);
edebe297
A
765 }
766
edebe297 767 if (!ok) {
6bb65964 768 SCLog(TRUE, LOG_ERR, CFSTR("interface name not valid"));
edebe297
A
769 }
770
6bb65964 771 return ok;
edebe297
A
772}
773
774
775/*
776 * OPEN
777 * (in) data = prefsID
778 * (out) status = SCError()
779 * (out) reply = N/A
780 */
781static Boolean
a40a14f8 782do_prefs_Open(SCHelperSessionRef session, void *info, CFDataRef data, uint32_t *status, CFDataRef *reply)
edebe297 783{
a40a14f8 784 CFStringRef name;
6bb65964 785 CFDictionaryRef options;
a40a14f8
A
786 CFNumberRef pid;
787 SCPreferencesRef prefs = __SCHelperSessionGetPreferences(session);
788 CFDictionaryRef prefsInfo = NULL;
789 CFStringRef prefsID;
790 CFStringRef prefsName;
6bb65964 791 CFStringRef proc_name;
edebe297
A
792
793 if (prefs != NULL) {
794 return FALSE;
795 }
796
a40a14f8
A
797 if ((data != NULL) && !_SCUnserialize((CFPropertyListRef *)&prefsInfo, data, NULL, 0)) {
798 SCLog(TRUE, LOG_ERR, CFSTR("data not valid, %@"), data);
edebe297
A
799 return FALSE;
800 }
801
a40a14f8
A
802 if ((prefsInfo == NULL) || !isA_CFDictionary(prefsInfo)) {
803 SCLog(TRUE, LOG_ERR, CFSTR("info not valid"));
804 if (prefsInfo != NULL) CFRelease(prefsInfo);
805 return FALSE;
806 }
edebe297 807
a40a14f8
A
808 // get [optional] prefsID
809 prefsID = CFDictionaryGetValue(prefsInfo, CFSTR("prefsID"));
810 prefsID = isA_CFString(prefsID);
811 if (prefsID != NULL) {
812 if (CFStringHasPrefix(prefsID, CFSTR("/")) ||
813 CFStringHasPrefix(prefsID, CFSTR("../")) ||
814 CFStringHasSuffix(prefsID, CFSTR("/..")) ||
815 (CFStringFind(prefsID, CFSTR("/../"), 0).location != kCFNotFound)) {
816 // if we're trying to escape from the preferences directory
817 SCLog(TRUE, LOG_ERR, CFSTR("prefsID (%@) not valid"), prefsID);
818 CFRelease(prefsInfo);
819 *status = kSCStatusInvalidArgument;
820 return TRUE;
821 }
822 }
823
6bb65964
A
824 // get [optional] options
825 options = CFDictionaryGetValue(prefsInfo, CFSTR("options"));
826 options = isA_CFDictionary(options);
827
a40a14f8
A
828 // get preferences session "name"
829 name = CFDictionaryGetValue(prefsInfo, CFSTR("name"));
830 if (!isA_CFString(name)) {
831 SCLog(TRUE, LOG_ERR, CFSTR("session \"name\" not valid"));
832 CFRelease(prefsInfo);
833 return FALSE;
834 }
835
836 // get PID of caller
837 pid = CFDictionaryGetValue(prefsInfo, CFSTR("PID"));
838 if (!isA_CFNumber(pid)) {
839 SCLog(TRUE, LOG_ERR, CFSTR("PID not valid"));
840 CFRelease(prefsInfo);
841 return FALSE;
842 }
843
6bb65964
A
844 // get process name of caller
845 proc_name = CFDictionaryGetValue(prefsInfo, CFSTR("PROC_NAME"));
846 if (!isA_CFString(proc_name)) {
847 SCLog(TRUE, LOG_ERR, CFSTR("process name not valid"));
848 CFRelease(prefsInfo);
849 return FALSE;
850 }
851
a40a14f8
A
852 // build [helper] preferences "name" (used for debugging) and estabish
853 // a preferences session.
6bb65964
A
854 prefsName = CFStringCreateWithFormat(NULL, NULL,
855 CFSTR("%@(%@):%@"),
856 proc_name,
857 pid,
858 name);
859
860 prefs = SCPreferencesCreateWithOptions(NULL, prefsName, prefsID, NULL, options);
a40a14f8
A
861 CFRelease(prefsName);
862 CFRelease(prefsInfo);
863
864 __SCHelperSessionSetPreferences(session, prefs);
6bb65964 865
a40a14f8 866 if (prefs != NULL) {
6bb65964
A
867#ifdef NOTYET
868 Boolean ok;
869 CFRunLoopRef rl = CFRunLoopGetCurrent();
870
871 // [temporarily] schedule notifications to ensure that we can use
872 // the SCDynamicStore to track helper sessions
873 ok = SCPreferencesScheduleWithRunLoop(prefs, rl, kCFRunLoopDefaultMode);
874 if (ok) {
875 (void)SCPreferencesUnscheduleFromRunLoop(prefs, rl, kCFRunLoopDefaultMode);
876 }
877#endif // NOTYET
878
879 // the session now holds a references to the SCPreferencesRef
a40a14f8
A
880 CFRelease(prefs);
881 } else {
edebe297
A
882 *status = SCError();
883 }
884
885 return TRUE;
886}
887
888
889/*
890 * ACCESS
891 * (in) data = N/A
892 * (out) status = SCError()
893 * (out) reply = current signature + current preferences
894 */
895static Boolean
a40a14f8 896do_prefs_Access(SCHelperSessionRef session, void *info, CFDataRef data, uint32_t *status, CFDataRef *reply)
edebe297
A
897{
898 Boolean ok;
a40a14f8 899 SCPreferencesRef prefs = __SCHelperSessionGetPreferences(session);
edebe297
A
900 CFDataRef signature;
901
902 if (prefs == NULL) {
903 return FALSE;
904 }
905
906 signature = SCPreferencesGetSignature(prefs);
907 if (signature != NULL) {
a40a14f8
A
908 const void * dictKeys[2];
909 const void * dictVals[2];
910 SCPreferencesPrivateRef prefsPrivate = (SCPreferencesPrivateRef)prefs;
911 CFDictionaryRef replyDict;
edebe297
A
912
913 dictKeys[0] = CFSTR("signature");
914 dictVals[0] = signature;
915
916 dictKeys[1] = CFSTR("preferences");
917 dictVals[1] = prefsPrivate->prefs;
918
919 replyDict = CFDictionaryCreate(NULL,
920 (const void **)&dictKeys,
921 (const void **)&dictVals,
922 sizeof(dictKeys)/sizeof(dictKeys[0]),
923 &kCFTypeDictionaryKeyCallBacks,
924 &kCFTypeDictionaryValueCallBacks);
925
926 ok = _SCSerialize(replyDict, reply, NULL, NULL);
927 CFRelease(replyDict);
928 if (!ok) {
929 return FALSE;
930 }
931 } else {
932 *status = SCError();
933 }
934
edebe297
A
935 return TRUE;
936}
937
938
939/*
940 * LOCK
941 * (in) data = client prefs signature (NULL if check not needed)
942 * (out) status = SCError()
943 * (out) reply = N/A
944 */
945static Boolean
a40a14f8 946do_prefs_Lock(SCHelperSessionRef session, void *info, CFDataRef data, uint32_t *status, CFDataRef *reply)
edebe297 947{
a40a14f8
A
948 CFDataRef clientSignature = (CFDataRef)data;
949 Boolean ok;
950 SCPreferencesRef prefs = __SCHelperSessionGetPreferences(session);
951 Boolean wait = (info == (void *)FALSE) ? FALSE : TRUE;
edebe297
A
952
953 if (prefs == NULL) {
954 return FALSE;
955 }
956
957 ok = SCPreferencesLock(prefs, wait);
958 if (!ok) {
959 *status = SCError();
960 return TRUE;
961 }
962
963 if (clientSignature != NULL) {
964 CFDataRef serverSignature;
965
966 serverSignature = SCPreferencesGetSignature(prefs);
967 if (!CFEqual(clientSignature, serverSignature)) {
968 (void)SCPreferencesUnlock(prefs);
969 *status = kSCStatusStale;
970 }
971 }
972
973 return TRUE;
974}
975
976
977/*
978 * COMMIT
979 * (in) data = new preferences (NULL if commit w/no changes)
980 * (out) status = SCError()
981 * (out) reply = new signature
982 */
983static Boolean
a40a14f8 984do_prefs_Commit(SCHelperSessionRef session, void *info, CFDataRef data, uint32_t *status, CFDataRef *reply)
edebe297
A
985{
986 Boolean ok;
6bb65964
A
987 SCPreferencesRef prefs = __SCHelperSessionGetPreferences(session);
988 CFPropertyListRef prefsData = NULL;
989 SCPreferencesPrivateRef prefsPrivate = (SCPreferencesPrivateRef)prefs;
990 CFArrayRef vpnFilter;
edebe297
A
991
992 if (prefs == NULL) {
993 return FALSE;
994 }
995
996 if (data != NULL) {
6bb65964
A
997 ok = _SCUnserialize(&prefsData, data, NULL, 0);
998 if (!ok) {
999 return FALSE;
1000 }
a40a14f8 1001
6bb65964
A
1002 if (!isA_CFDictionary(prefsData)) {
1003 *status = kSCStatusFailed;
1004 ok = FALSE;
1005 goto done;
1006 }
1007 }
1008
1009 vpnFilter = __SCHelperSessionGetVPNFilter(session);
1010 if (vpnFilter != NULL) {
1011 ok = FALSE;
1012
1013 if ((prefsPrivate->prefs != NULL) && (prefsData != NULL)) {
1014 CFIndex c;
1015 CFMutableDictionaryRef prefsNew = NULL;
1016 CFMutableDictionaryRef prefsOld = NULL;
1017 CFMutableDictionaryRef prefsSave = prefsPrivate->prefs;
1018 CFRange range = CFRangeMake(0, CFArrayGetCount(vpnFilter));
1019
1020 for (c = 0; c < 2; c++) {
1021 CFArrayRef services;
1022
1023 switch (c) {
1024 case 0 :
1025 prefsPrivate->prefs = CFDictionaryCreateMutableCopy(NULL, 0, prefsSave);
1026 break;
1027 case 1 :
1028 prefsPrivate->prefs = CFDictionaryCreateMutableCopy(NULL, 0, prefsData);
1029 break;
1030 }
1031
1032 // filter out VPN services of the specified type
1033 services = SCNetworkServiceCopyAll(prefs);
1034 if (services != NULL) {
1035 CFIndex i;
1036 CFIndex n = CFArrayGetCount(services);
1037
1038 for (i = 0; i < n; i++) {
1039 SCNetworkServiceRef service;
1040
1041 service = CFArrayGetValueAtIndex(services, i);
1042 if (_SCNetworkServiceIsVPN(service)) {
1043 SCNetworkInterfaceRef child;
1044 CFStringRef childType = NULL;
1045 SCNetworkInterfaceRef interface;
1046 CFStringRef interfaceType;
1047
1048 interface = SCNetworkServiceGetInterface(service);
1049 interfaceType = SCNetworkInterfaceGetInterfaceType(interface);
1050 child = SCNetworkInterfaceGetInterface(interface);
1051 if (child != NULL) {
1052 childType = SCNetworkInterfaceGetInterfaceType(child);
1053 }
1054 if (CFEqual(interfaceType, kSCNetworkInterfaceTypeVPN) &&
1055 (childType != NULL) &&
1056 CFArrayContainsValue(vpnFilter, range, childType)) {
1057 // filter out VPN service
1058 (void) SCNetworkServiceRemove(service);
1059 } else {
1060 // mark all other VPN services "enabled"
1061 (void) SCNetworkServiceSetEnabled(service, TRUE);
1062 }
1063 }
1064 }
1065
1066 CFRelease(services);
1067 }
1068
1069 switch (c) {
1070 case 0 :
1071 prefsOld = prefsPrivate->prefs;
1072 break;
1073 case 1 :
1074 prefsNew = prefsPrivate->prefs;
1075 break;
1076 }
1077 }
1078
1079 // compare the filtered configurations
1080 ok = _SC_CFEqual(prefsOld, prefsNew);
1081
1082 // clean up
1083 if (prefsOld != NULL) CFRelease(prefsOld);
1084 if (prefsNew != NULL) CFRelease(prefsNew);
1085 prefsPrivate->prefs = prefsSave;
edebe297
A
1086 }
1087
edebe297 1088 if (!ok) {
6bb65964
A
1089 *status = kSCStatusAccessError;
1090 goto done;
edebe297 1091 }
6bb65964 1092 }
edebe297 1093
6bb65964
A
1094 if (prefsData != NULL) {
1095 if (prefsPrivate->prefs != NULL) {
1096 CFRelease(prefsPrivate->prefs);
1097 }
1098 prefsPrivate->prefs = CFDictionaryCreateMutableCopy(NULL, 0, prefsData);
edebe297
A
1099 prefsPrivate->accessed = TRUE;
1100 prefsPrivate->changed = TRUE;
1101 }
1102
1103 ok = SCPreferencesCommitChanges(prefs);
1104 if (ok) {
1105 *reply = SCPreferencesGetSignature(prefs);
1106 CFRetain(*reply);
1107 } else {
1108 *status = SCError();
1109 }
1110
6bb65964
A
1111 done :
1112
1113 if (prefsData != NULL) CFRelease(prefsData);
1114 return ok;
edebe297
A
1115}
1116
1117
1118/*
1119 * APPLY
1120 * (in) data = N/A
1121 * (out) status = SCError()
1122 * (out) reply = N/A
1123 */
1124static Boolean
a40a14f8 1125do_prefs_Apply(SCHelperSessionRef session, void *info, CFDataRef data, uint32_t *status, CFDataRef *reply)
edebe297 1126{
a40a14f8
A
1127 Boolean ok;
1128 SCPreferencesRef prefs = __SCHelperSessionGetPreferences(session);
edebe297
A
1129
1130 if (prefs == NULL) {
1131 return FALSE;
1132 }
1133
1134 ok = SCPreferencesApplyChanges(prefs);
1135 if (!ok) {
1136 *status = SCError();
1137 }
1138
1139 return TRUE;
1140}
1141
1142
1143/*
1144 * UNLOCK
1145 * (in) data = N/A
1146 * (out) status = SCError()
1147 * (out) reply = N/A
1148 */
1149static Boolean
a40a14f8 1150do_prefs_Unlock(SCHelperSessionRef session, void *info, CFDataRef data, uint32_t *status, CFDataRef *reply)
edebe297 1151{
a40a14f8
A
1152 Boolean ok;
1153 SCPreferencesRef prefs = __SCHelperSessionGetPreferences(session);
edebe297
A
1154
1155 if (prefs == NULL) {
1156 return FALSE;
1157 }
1158
1159 ok = SCPreferencesUnlock(prefs);
1160 if (!ok) {
1161 *status = SCError();
1162 }
1163
1164 return TRUE;
1165}
1166
1167
1168/*
1169 * CLOSE
1170 * (in) data = N/A
1171 * (out) status = SCError()
1172 * (out) reply = N/A
1173 */
1174static Boolean
a40a14f8 1175do_prefs_Close(SCHelperSessionRef session, void *info, CFDataRef data, uint32_t *status, CFDataRef *reply)
edebe297 1176{
a40a14f8
A
1177 SCPreferencesRef prefs = __SCHelperSessionGetPreferences(session);
1178
edebe297
A
1179 if (prefs == NULL) {
1180 return FALSE;
1181 }
1182
a40a14f8
A
1183 __SCHelperSessionSetPreferences(session, NULL);
1184 *status = -1;
edebe297
A
1185 return TRUE;
1186}
1187
1188
6d034b4e
A
1189/*
1190 * SYNCHRONIZE
1191 * (in) data = N/A
1192 * (out) status = kSCStatusOK
1193 * (out) reply = N/A
1194 */
1195static Boolean
a40a14f8 1196do_prefs_Synchronize(SCHelperSessionRef session, void *info, CFDataRef data, uint32_t *status, CFDataRef *reply)
6d034b4e 1197{
a40a14f8
A
1198 SCPreferencesRef prefs = __SCHelperSessionGetPreferences(session);
1199
6d034b4e
A
1200 if (prefs == NULL) {
1201 return FALSE;
1202 }
a40a14f8 1203
6d034b4e
A
1204 SCPreferencesSynchronize(prefs);
1205 *status = kSCStatusOK;
1206 return TRUE;
1207}
1208
1209
a40a14f8
A
1210#pragma mark -
1211#pragma mark Process commands
1212
1213
6bb65964
A
1214#if TARGET_OS_IPHONE
1215
1216#include <Security/Security.h>
1217#include <Security/SecTask.h>
1218
1219static CFStringRef
1220sessionName(SCHelperSessionRef session)
1221{
1222 CFStringRef name = NULL;
1223 SCPreferencesRef prefs;
1224
1225 prefs = __SCHelperSessionGetPreferences(session);
1226 if (prefs != NULL) {
1227 SCPreferencesPrivateRef prefsPrivate = (SCPreferencesPrivateRef)prefs;
1228
1229 name = prefsPrivate->name;
1230 }
1231
1232 return (name != NULL) ? name : CFSTR("???");
1233}
1234
1235
1236static CFTypeRef
1237copyEntitlement(SCHelperSessionRef session, CFStringRef entitlement)
1238{
1239 SCHelperSessionPrivateRef sessionPrivate = (SCHelperSessionPrivateRef)session;
1240 SecTaskRef task;
1241 CFTypeRef value = NULL;
1242
1243 // Create the security task from the audit token
1244 task = SecTaskCreateWithAuditToken(NULL, sessionPrivate->auditToken);
1245 if (task != NULL) {
1246 CFErrorRef error = NULL;
1247
1248 // Get the value for the entitlement
1249 value = SecTaskCopyValueForEntitlement(task, entitlement, &error);
1250 if ((value == NULL) && (error != NULL)) {
1251 CFIndex code = CFErrorGetCode(error);
1252 CFStringRef domain = CFErrorGetDomain(error);
1253
009a3e7e
A
1254 if (!CFEqual(domain, kCFErrorDomainMach) ||
1255 ((code != kIOReturnInvalid) && (code != kIOReturnNotFound))) {
6bb65964
A
1256 // if unexpected error
1257 SCLog(TRUE, LOG_ERR,
1258 CFSTR("SecTaskCopyValueForEntitlement(,\"%@\",) failed, error = %@ : %@"),
1259 entitlement,
1260 error,
1261 sessionName(session));
1262 }
1263 CFRelease(error);
1264 }
1265
1266 CFRelease(task);
1267 } else {
1268 SCLog(TRUE, LOG_ERR,
1269 CFSTR("SecTaskCreateWithAuditToken() failed: %@"),
1270 sessionName(session));
1271 }
1272
1273 return value;
1274}
1275
1276#endif // TARGET_OS_IPHONE
1277
1278
1279
1280
edebe297 1281static Boolean
a40a14f8 1282hasAuthorization(SCHelperSessionRef session)
edebe297 1283{
a40a14f8
A
1284 AuthorizationRef authorization = __SCHelperSessionGetAuthorization(session);
1285
6bb65964
A
1286 if (authorization == NULL) {
1287 return FALSE;
1288 }
1289
a40a14f8 1290#if !TARGET_OS_IPHONE
edebe297
A
1291 AuthorizationFlags flags;
1292 AuthorizationItem items[1];
1293 AuthorizationRights rights;
1294 OSStatus status;
1295
edebe297
A
1296 items[0].name = "system.preferences";
1297 items[0].value = NULL;
1298 items[0].valueLength = 0;
1299 items[0].flags = 0;
1300
1301 rights.count = sizeof(items) / sizeof(items[0]);
1302 rights.items = items;
1303
1304 flags = kAuthorizationFlagDefaults;
1305 flags |= kAuthorizationFlagExtendRights;
1306 flags |= kAuthorizationFlagInteractionAllowed;
1307// flags |= kAuthorizationFlagPartialRights;
1308// flags |= kAuthorizationFlagPreAuthorize;
1309
1310 status = AuthorizationCopyRights(authorization,
1311 &rights,
1312 kAuthorizationEmptyEnvironment,
1313 flags,
1314 NULL);
1315 if (status != errAuthorizationSuccess) {
1316 return FALSE;
1317 }
1318
6bb65964
A
1319 return TRUE;
1320#else // !TARGET_OS_IPHONE
1321 SCHelperSessionPrivateRef sessionPrivate = (SCHelperSessionPrivateRef)session;
a40a14f8 1322
6bb65964
A
1323 if (sessionPrivate->callerWriteAccess == UNKNOWN) {
1324 CFArrayRef entitlement;
1325 CFStringRef prefsID;
1326 SCPreferencesPrivateRef prefsPrivate;
1327
1328 // assume that the client DOES NOT have the entitlement
1329 sessionPrivate->callerWriteAccess = NO;
1330
1331 prefsPrivate = (SCPreferencesPrivateRef)sessionPrivate->prefs;
1332 prefsID = (prefsPrivate->prefsID != NULL) ? prefsPrivate->prefsID : PREFS_DEFAULT_CONFIG;
1333
1334 entitlement = copyEntitlement(session, kSCWriteEntitlementName);
1335 if (entitlement != NULL) {
1336 if (isA_CFArray(entitlement)) {
1337 if (CFArrayContainsValue(entitlement,
1338 CFRangeMake(0, CFArrayGetCount(entitlement)),
1339 prefsID)) {
1340 // if client DOES have entitlement
1341 sessionPrivate->callerWriteAccess = YES;
1342 }
1343 } else {
1344 SCLog(TRUE, LOG_ERR,
1345 CFSTR("hasAuthorization: entitlement not valid: %@"),
1346 sessionName(session));
1347 }
a40a14f8 1348
6bb65964
A
1349 CFRelease(entitlement);
1350 }
a40a14f8 1351
6bb65964
A
1352 // make an exception for VPN configuration management
1353 if (sessionPrivate->callerWriteAccess != YES) {
1354 entitlement = copyEntitlement(session, kSCVPNFilterEntitlementName);
1355 if (entitlement != NULL) {
1356 if (isA_CFArray(entitlement)) {
1357 if (CFEqual(prefsID, PREFS_DEFAULT_CONFIG)) {
1358 // save the VPN bundle identifiers
1359 __SCHelperSessionSetVPNFilter(session, entitlement);
1360
1361 // and grant a "filtered" exception
1362 sessionPrivate->callerWriteAccess = YES;
1363 } else if (CFStringHasPrefix(prefsID, CFSTR("VPN-")) &&
1364 CFStringHasSuffix(prefsID, CFSTR(".plist"))) {
1365 CFRange range;
1366 CFStringRef vpnID;
1367
1368 range.location = sizeof("VPN-") - 1;
1369 range.length = CFStringGetLength(prefsID)
1370 - (sizeof("VPN-") - 1) // trim VPN-
1371 - (sizeof(".plist") - 1); // trim .plist
1372 vpnID = CFStringCreateWithSubstring(NULL, prefsID, range);
1373 if (CFArrayContainsValue(entitlement,
1374 CFRangeMake(0, CFArrayGetCount(entitlement)),
1375 vpnID)) {
1376 // grant an exception
1377 sessionPrivate->callerWriteAccess = YES;
1378 }
1379 CFRelease(vpnID);
1380 }
1381 }
1382
1383 CFRelease(entitlement);
a40a14f8
A
1384 }
1385 }
1386
6bb65964
A
1387 if (sessionPrivate->callerWriteAccess != YES) {
1388 SCLog(TRUE, LOG_ERR,
1389 CFSTR("SCPreferences write access to \"%@\" denied, no entitlement for \"%@\""),
1390 prefsID,
1391 sessionName(session));
a40a14f8
A
1392 }
1393 }
a40a14f8 1394
6bb65964
A
1395 return (sessionPrivate->callerWriteAccess == YES) ? TRUE : FALSE;
1396#endif // TARGET_OS_IPHONE
edebe297
A
1397}
1398
1399
a40a14f8
A
1400typedef Boolean (*helperFunction) (SCHelperSessionRef session,
1401 void *info,
1402 CFDataRef data,
1403 uint32_t *status,
1404 CFDataRef *reply);
edebe297
A
1405
1406
1407static const struct helper {
1408 int command;
1409 const char *commandName;
1410 Boolean needsAuthorization;
1411 helperFunction func;
1412 void *info;
1413} helpers[] = {
1414 { SCHELPER_MSG_AUTH, "AUTH", FALSE, do_Auth , NULL },
1415
1416 { SCHELPER_MSG_PREFS_OPEN, "PREFS open", FALSE, do_prefs_Open , NULL },
1417 { SCHELPER_MSG_PREFS_ACCESS, "PREFS access", TRUE, do_prefs_Access , NULL },
1418 { SCHELPER_MSG_PREFS_LOCK, "PREFS lock", TRUE, do_prefs_Lock , (void *)FALSE },
1419 { SCHELPER_MSG_PREFS_LOCKWAIT, "PREFS lock/wait", TRUE, do_prefs_Lock , (void *)TRUE },
1420 { SCHELPER_MSG_PREFS_COMMIT, "PREFS commit", TRUE, do_prefs_Commit , NULL },
1421 { SCHELPER_MSG_PREFS_APPLY, "PREFS apply", TRUE, do_prefs_Apply , NULL },
1422 { SCHELPER_MSG_PREFS_UNLOCK, "PREFS unlock", FALSE, do_prefs_Unlock , NULL },
1423 { SCHELPER_MSG_PREFS_CLOSE, "PREFS close", FALSE, do_prefs_Close , NULL },
6d034b4e 1424 { SCHELPER_MSG_PREFS_SYNCHRONIZE, "PREFS synchronize", FALSE, do_prefs_Synchronize , NULL },
edebe297
A
1425
1426 { SCHELPER_MSG_INTERFACE_REFRESH, "INTERFACE refresh", TRUE, do_interface_refresh , NULL },
1427
a40a14f8 1428#if !TARGET_OS_IPHONE
edebe297
A
1429 { SCHELPER_MSG_KEYCHAIN_COPY, "KEYCHAIN copy", TRUE, do_keychain_copy , NULL },
1430 { SCHELPER_MSG_KEYCHAIN_EXISTS, "KEYCHAIN exists", TRUE, do_keychain_exists , NULL },
1431 { SCHELPER_MSG_KEYCHAIN_REMOVE, "KEYCHAIN remove", TRUE, do_keychain_remove , NULL },
1432 { SCHELPER_MSG_KEYCHAIN_SET, "KEYCHAIN set", TRUE, do_keychain_set , NULL },
a40a14f8 1433#endif // !TARGET_OS_IPHONE
edebe297
A
1434
1435 { SCHELPER_MSG_EXIT, "EXIT", FALSE, do_Exit , NULL }
1436};
1437#define nHELPERS (sizeof(helpers)/sizeof(struct helper))
1438
1439
1440static int
6bb65964 1441findCommand(uint32_t command)
edebe297
A
1442{
1443 int i;
1444
1445 for (i = 0; i < (int)nHELPERS; i++) {
1446 if (helpers[i].command == command) {
1447 return i;
1448 }
1449 }
1450
1451 return -1;
1452}
1453
1454
6bb65964
A
1455static void *
1456newHelper(void *arg)
edebe297 1457{
6bb65964
A
1458 CFRunLoopSourceRef rls = NULL;
1459 SCHelperSessionRef session = (SCHelperSessionRef)arg;
1460 SCHelperSessionPrivateRef sessionPrivate = (SCHelperSessionPrivateRef)session;
a40a14f8 1461
6bb65964 1462 __SCHelperSessionSetThreadName(session);
edebe297 1463
6bb65964
A
1464 rls = CFMachPortCreateRunLoopSource(NULL, sessionPrivate->mp, 0);
1465 CFRelease(sessionPrivate->mp);
edebe297 1466
6bb65964
A
1467 if (rls != NULL) {
1468 CFRunLoopAddSource(CFRunLoopGetCurrent(), rls, kCFRunLoopDefaultMode);
1469 CFRelease(rls);
a40a14f8 1470
6bb65964
A
1471 SCLog(debug, LOG_DEBUG, CFSTR("%p : start"), session);
1472 CFRunLoopRun();
1473 SCLog(debug, LOG_DEBUG, CFSTR("%p : stop"), session);
edebe297
A
1474 }
1475
6bb65964
A
1476 return NULL;
1477}
1478
1479
1480#pragma mark -
1481#pragma mark Main loop
1482
1483
1484// MiG generated externals and functions
1485extern struct mig_subsystem _helper_subsystem;
1486extern boolean_t helper_server(mach_msg_header_t *, mach_msg_header_t *);
1487
1488
1489static
1490boolean_t
1491notify_server(mach_msg_header_t *request, mach_msg_header_t *reply)
1492{
1493 mach_no_senders_notification_t *Request = (mach_no_senders_notification_t *)request;
1494 mig_reply_error_t *Reply = (mig_reply_error_t *)reply;
1495
1496 reply->msgh_bits = MACH_MSGH_BITS(MACH_MSGH_BITS_REMOTE(request->msgh_bits), 0);
1497 reply->msgh_remote_port = request->msgh_remote_port;
1498 reply->msgh_size = sizeof(mig_reply_error_t); /* Minimal size: update as needed */
1499 reply->msgh_local_port = MACH_PORT_NULL;
1500 reply->msgh_id = request->msgh_id + 100;
1501
1502 if ((Request->not_header.msgh_id > MACH_NOTIFY_LAST) ||
1503 (Request->not_header.msgh_id < MACH_NOTIFY_FIRST)) {
1504 Reply->NDR = NDR_record;
1505 Reply->RetCode = MIG_BAD_ID;
1506 return FALSE; /* if this is not a notification message */
edebe297
A
1507 }
1508
6bb65964
A
1509 switch (Request->not_header.msgh_id) {
1510 case MACH_NOTIFY_NO_SENDERS : {
1511 SCHelperSessionRef session;
edebe297 1512
6bb65964
A
1513 __MACH_PORT_DEBUG(TRUE, "*** notify_server MACH_NOTIFY_NO_SENDERS", Request->not_header.msgh_local_port);
1514
1515 // clean up session
1516 session = __SCHelperSessionFindWithPort(Request->not_header.msgh_local_port);
1517 if (session != NULL) {
1518 SCHelperSessionPrivateRef sessionPrivate = (SCHelperSessionPrivateRef)session;
1519
1520 // release CFMachPort *and* SCHelperSession
1521 CFMachPortInvalidate(sessionPrivate->mp);
1522 }
1523
1524 __MACH_PORT_DEBUG(TRUE, "*** notify_server after invalidate", Request->not_header.msgh_local_port);
1525
1526 // and, lastly, remove our receive right.
1527 (void) mach_port_mod_refs(mach_task_self(),
1528 Request->not_header.msgh_local_port,
1529 MACH_PORT_RIGHT_RECEIVE, -1);
1530
1531 Reply->Head.msgh_bits = 0;
1532 Reply->Head.msgh_remote_port = MACH_PORT_NULL;
1533 Reply->RetCode = KERN_SUCCESS;
1534 return TRUE;
edebe297 1535 }
6bb65964
A
1536
1537 default :
1538 break;
a40a14f8 1539 }
edebe297 1540
6bb65964
A
1541 SCLog(TRUE, LOG_ERR, CFSTR("HELP!, Received notification: port=%d, msgh_id=%d"),
1542 Request->not_header.msgh_local_port,
1543 Request->not_header.msgh_id);
edebe297 1544
6bb65964
A
1545 Reply->NDR = NDR_record;
1546 Reply->RetCode = MIG_BAD_ID;
1547 return FALSE; /* if this is not a notification we are handling */
1548}
1549
1550
1551__private_extern__
1552boolean_t
1553helper_demux(mach_msg_header_t *request, mach_msg_header_t *reply)
1554{
1555 Boolean processed = FALSE;
1556
1557 /*
1558 * (attempt to) process SCHelper requests.
1559 */
1560 processed = helper_server(request, reply);
1561 if (processed) {
1562 return TRUE;
a40a14f8 1563 }
edebe297 1564
6bb65964
A
1565 /*
1566 * (attempt to) process (NO MORE SENDERS) notification messages.
1567 */
1568 processed = notify_server(request, reply);
1569 if (processed) {
1570 return TRUE;
a40a14f8 1571 }
edebe297 1572
6bb65964
A
1573 /*
1574 * unknown message ID, log and return an error.
1575 */
1576 SCLog(TRUE, LOG_ERR, CFSTR("helper_demux(): unknown message ID (%d) received"), request->msgh_id);
1577 reply->msgh_bits = MACH_MSGH_BITS(MACH_MSGH_BITS_REMOTE(request->msgh_bits), 0);
1578 reply->msgh_remote_port = request->msgh_remote_port;
1579 reply->msgh_size = sizeof(mig_reply_error_t); /* Minimal size */
1580 reply->msgh_local_port = MACH_PORT_NULL;
1581 reply->msgh_id = request->msgh_id + 100;
1582 ((mig_reply_error_t *)reply)->NDR = NDR_record;
1583 ((mig_reply_error_t *)reply)->RetCode = MIG_BAD_ID;
1584
1585 return FALSE;
a40a14f8 1586}
edebe297 1587
edebe297 1588
6bb65964 1589#define MACH_MSG_BUFFER_SIZE 128
a40a14f8
A
1590
1591
1592static void
6bb65964 1593helperCallback(CFMachPortRef port, void *msg, CFIndex size, void *info)
a40a14f8 1594{
6bb65964
A
1595 mig_reply_error_t * bufRequest = msg;
1596 uint32_t bufReply_q[MACH_MSG_BUFFER_SIZE/sizeof(uint32_t)];
1597 mig_reply_error_t * bufReply = (mig_reply_error_t *)bufReply_q;
1598 static CFIndex bufSize = 0;
1599 mach_msg_return_t mr;
1600 int options;
1601
1602 if (bufSize == 0) {
1603 // get max size for MiG reply buffers
1604 bufSize = _helper_subsystem.maxsize;
1605
1606 // check if our on-the-stack reply buffer will be big enough
1607 if (bufSize > sizeof(bufReply_q)) {
1608 SCLog(TRUE, LOG_NOTICE,
1609 CFSTR("helperCallback(): buffer size should be increased > %d"),
1610 _helper_subsystem.maxsize);
1611 }
a40a14f8
A
1612 }
1613
6bb65964
A
1614 if (bufSize > sizeof(bufReply_q)) {
1615 bufReply = CFAllocatorAllocate(NULL, _helper_subsystem.maxsize, 0);
a40a14f8 1616 }
6bb65964 1617 bufReply->RetCode = 0;
a40a14f8 1618
6bb65964
A
1619 /* we have a request message */
1620 (void) helper_demux(&bufRequest->Head, &bufReply->Head);
a40a14f8 1621
6bb65964
A
1622 if (!(bufReply->Head.msgh_bits & MACH_MSGH_BITS_COMPLEX)) {
1623 if (bufReply->RetCode == MIG_NO_REPLY) {
1624 bufReply->Head.msgh_remote_port = MACH_PORT_NULL;
1625 } else if ((bufReply->RetCode != KERN_SUCCESS) &&
1626 (bufRequest->Head.msgh_bits & MACH_MSGH_BITS_COMPLEX)) {
1627 /*
1628 * destroy the request - but not the reply port
1629 */
1630 bufRequest->Head.msgh_remote_port = MACH_PORT_NULL;
1631 mach_msg_destroy(&bufRequest->Head);
1632 }
1633 }
a40a14f8 1634
6bb65964
A
1635 if (bufReply->Head.msgh_remote_port != MACH_PORT_NULL) {
1636 /*
1637 * send reply.
1638 *
1639 * We don't want to block indefinitely because the client
1640 * isn't receiving messages from the reply port.
1641 * If we have a send-once right for the reply port, then
1642 * this isn't a concern because the send won't block.
1643 * If we have a send right, we need to use MACH_SEND_TIMEOUT.
1644 * To avoid falling off the kernel's fast RPC path unnecessarily,
1645 * we only supply MACH_SEND_TIMEOUT when absolutely necessary.
1646 */
a40a14f8 1647
6bb65964
A
1648 options = MACH_SEND_MSG;
1649 if (MACH_MSGH_BITS_REMOTE(bufReply->Head.msgh_bits) != MACH_MSG_TYPE_MOVE_SEND_ONCE) {
1650 options |= MACH_SEND_TIMEOUT;
1651 }
1652 mr = mach_msg(&bufReply->Head, /* msg */
1653 options, /* option */
1654 bufReply->Head.msgh_size, /* send_size */
1655 0, /* rcv_size */
1656 MACH_PORT_NULL, /* rcv_name */
1657 MACH_MSG_TIMEOUT_NONE, /* timeout */
1658 MACH_PORT_NULL); /* notify */
1659
1660 /* Has a message error occurred? */
1661 switch (mr) {
1662 case MACH_SEND_INVALID_DEST:
1663 case MACH_SEND_TIMED_OUT:
1664 break;
1665 default :
1666 /* Includes success case. */
1667 goto done;
1668 }
1669 }
a40a14f8 1670
6bb65964
A
1671 if (bufReply->Head.msgh_bits & MACH_MSGH_BITS_COMPLEX) {
1672 mach_msg_destroy(&bufReply->Head);
a40a14f8 1673 }
a40a14f8 1674
6bb65964 1675 done :
a40a14f8 1676
6bb65964
A
1677 if (bufReply != (mig_reply_error_t *)bufReply_q)
1678 CFAllocatorDeallocate(NULL, bufReply);
1679 return;
1680}
a40a14f8 1681
a40a14f8 1682
6bb65964
A
1683static CFStringRef
1684initMPCopyDescription(const void *info)
1685{
1686 return CFStringCreateWithFormat(NULL, NULL, CFSTR("<SCHelper MP>"));
a40a14f8
A
1687}
1688
1689
6bb65964
A
1690__private_extern__
1691kern_return_t
1692_helperinit(mach_port_t server,
1693 mach_port_t *newSession,
1694 uint32_t *status,
1695 audit_token_t audit_token)
a40a14f8 1696{
6bb65964
A
1697 CFMachPortContext context = { 0
1698 , NULL
1699 , CFRetain
1700 , CFRelease
1701 , initMPCopyDescription
1702 };
1703 kern_return_t kr;
1704 mach_port_t oldNotify;
1705 SCHelperSessionRef session;
1706 SCHelperSessionPrivateRef sessionPrivate;
1707 pthread_attr_t tattr;
1708 pthread_t tid;
a40a14f8 1709
6bb65964
A
1710 session = __SCHelperSessionFindWithPort(server);
1711 if (session != NULL) {
1712#ifdef DEBUG
1713 SCLog(TRUE, LOG_DEBUG, CFSTR("_helperinit(): session is already open."));
1714#endif /* DEBUG */
1715 *status = kSCStatusFailed; /* you can't re-open an "open" session */
1716 return KERN_SUCCESS;
a40a14f8
A
1717 }
1718
6bb65964
A
1719 session = __SCHelperSessionCreate(NULL);
1720 sessionPrivate = (SCHelperSessionPrivateRef)session;
1721
1722 // create per-session port
1723 (void) mach_port_allocate(mach_task_self(),
1724 MACH_PORT_RIGHT_RECEIVE,
1725 &sessionPrivate->port);
1726 *newSession = sessionPrivate->port;
1727
1728 //
1729 // Note: we create the CFMachPort *before* we insert a send
1730 // right present to ensure that CF does not establish
1731 // its dead name notification.
1732 //
1733 context.info = (void *)session;
1734 sessionPrivate->mp = _SC_CFMachPortCreateWithPort("SCHelper/session",
1735 *newSession,
1736 helperCallback,
1737 &context);
1738
1739 /* Request a notification when/if the client dies */
1740 kr = mach_port_request_notification(mach_task_self(),
1741 *newSession,
1742 MACH_NOTIFY_NO_SENDERS,
1743 1,
1744 *newSession,
1745 MACH_MSG_TYPE_MAKE_SEND_ONCE,
1746 &oldNotify);
1747 if (kr != KERN_SUCCESS) {
1748 SCLog(TRUE, LOG_ERR, CFSTR("_helperinit() mach_port_request_notification() failed: %s"), mach_error_string(kr));
1749
1750 // clean up CFMachPort, mach port rights
1751 CFMachPortInvalidate(sessionPrivate->mp);
1752 CFRelease(sessionPrivate->mp);
1753 sessionPrivate->mp = NULL;
1754 (void) mach_port_mod_refs(mach_task_self(), *newSession, MACH_PORT_RIGHT_RECEIVE, -1);
1755 *newSession = MACH_PORT_NULL;
1756 *status = kSCStatusFailed;
1757 goto done;
a40a14f8
A
1758 }
1759
6bb65964
A
1760 if (oldNotify != MACH_PORT_NULL) {
1761 SCLog(TRUE, LOG_ERR, CFSTR("_helperinit(): oldNotify != MACH_PORT_NULL"));
a40a14f8
A
1762 }
1763
6bb65964
A
1764 // add send right (that will be passed back to the client)
1765 (void) mach_port_insert_right(mach_task_self(),
1766 *newSession,
1767 *newSession,
1768 MACH_MSG_TYPE_MAKE_SEND);
1769
1770 // save audit token
1771 sessionPrivate->auditToken = audit_token;
1772
1773 //
1774 // Note: at this time we should be holding ONE send right and
1775 // ONE receive right to the server. The send right is
1776 // moved to the caller.
1777 //
1778
a40a14f8
A
1779 // start per-session thread
1780 pthread_attr_init(&tattr);
1781 pthread_attr_setscope(&tattr, PTHREAD_SCOPE_SYSTEM);
1782 pthread_attr_setdetachstate(&tattr, PTHREAD_CREATE_DETACHED);
1783 pthread_attr_setstacksize(&tattr, 96 * 1024); // each thread gets a 96K stack
6bb65964 1784 pthread_create(&tid, &tattr, newHelper, (void *)session);
a40a14f8 1785 pthread_attr_destroy(&tattr);
edebe297 1786
6bb65964
A
1787 *status = kSCStatusOK;
1788
1789 done :
1790
1791 CFRelease(session);
1792 return KERN_SUCCESS;
1793}
1794
1795
1796__private_extern__
1797kern_return_t
1798_helperexec(mach_port_t server,
1799 uint32_t msgID,
1800 xmlData_t dataRef, /* raw XML bytes */
1801 mach_msg_type_number_t dataLen,
1802 uint32_t *status,
1803 xmlDataOut_t *replyRef, /* raw XML bytes */
1804 mach_msg_type_number_t *replyLen)
1805{
1806 CFDataRef data = NULL;
1807 int i;
1808 CFDataRef reply = NULL;
1809 SCHelperSessionRef session;
1810
1811 *status = kSCStatusOK;
1812 *replyRef = NULL;
1813 *replyLen = 0;
1814
1815 if ((dataRef != NULL) && (dataLen > 0)) {
1816 if (!_SCUnserializeData(&data, (void *)dataRef, dataLen)) {
1817 *status = SCError();
1818 return KERN_SUCCESS;
1819 }
1820 }
1821
1822 session = __SCHelperSessionFindWithPort(server);
1823 if (session == NULL) {
1824 *status = kSCStatusFailed; /* you must have an open session to play */
1825 goto done;
1826 }
1827
1828 i = findCommand(msgID);
1829 if (i == -1) {
1830 SCLog(TRUE, LOG_ERR, CFSTR("received unknown command : %u"), msgID);
1831 *status = kSCStatusInvalidArgument;
1832 goto done;
1833 }
1834
1835 SCLog(debug, LOG_DEBUG,
1836 CFSTR("%p : processing command \"%s\"%s"),
1837 session,
1838 helpers[i].commandName,
1839 (data != NULL) ? " w/data" : "");
1840
1841 if (helpers[i].needsAuthorization && !hasAuthorization(session)) {
1842 SCLog(debug, LOG_DEBUG,
1843 CFSTR("%p : command \"%s\" : not authorized"),
1844 session,
1845 helpers[i].commandName);
1846 *status = kSCStatusAccessError;
1847 }
1848
1849 if (*status == kSCStatusOK) {
1850 (*helpers[i].func)(session, helpers[i].info, data, status, &reply);
1851 }
1852
1853 if ((*status != -1) || (reply != NULL)) {
1854 Boolean ok;
1855
1856 SCLog(debug, LOG_DEBUG,
1857 CFSTR("%p : sending status %u%s"),
1858 session,
1859 *status,
1860 (reply != NULL) ? " w/reply" : "");
1861
1862 /* serialize the data */
1863 if (reply != NULL) {
1864 ok = _SCSerializeData(reply, (void **)replyRef, (CFIndex *)replyLen);
1865 CFRelease(reply);
1866 reply = NULL;
1867 if (!ok) {
1868 *status = SCError();
1869 goto done;
1870 }
1871 }
1872 }
1873
1874 done :
1875
1876 if (data != NULL) CFRelease(data);
1877 if (reply != NULL) CFRelease(reply);
1878 return KERN_SUCCESS;
1879}
1880
1881
1882static CFStringRef
1883helperMPCopyDescription(const void *info)
1884{
1885 return CFStringCreateWithFormat(NULL, NULL, CFSTR("<main SCHelper MP>"));
1886}
1887
1888
1889static void
1890init_MiG_1(const launch_data_t l_obj, const char *name, void *info)
1891{
1892 CFMachPortContext context = { 0
1893 , (void *)1
1894 , NULL
1895 , NULL
1896 , helperMPCopyDescription
1897 };
1898 launch_data_type_t l_type;
1899 CFMachPortRef mp;
1900 int *n_listeners = (int *)info;
1901 CFRunLoopSourceRef rls;
1902 mach_port_t service_port;
1903
1904 // get the mach port
1905 l_type = (l_obj != NULL) ? launch_data_get_type(l_obj) : 0;
1906 if (l_type != LAUNCH_DATA_MACHPORT) {
1907 SCLog(TRUE, LOG_ERR,
1908 CFSTR("SCHelper: error w/MachServices \"%s\" port (%p, %d)"),
1909 (name != NULL) ? name : "?",
1910 l_obj,
1911 l_type);
1912 return;
1913 }
1914 service_port = launch_data_get_machport(l_obj);
1915
1916 // add a run loop source to listen for new requests
1917 mp = _SC_CFMachPortCreateWithPort("SCHelper/server",
1918 service_port,
1919 helperCallback,
1920 &context);
1921 rls = CFMachPortCreateRunLoopSource(NULL, mp, 0);
1922 CFRelease(mp);
1923 CFRunLoopAddSource(CFRunLoopGetCurrent(), rls, kCFRunLoopDefaultMode);
1924 CFRelease(rls);
1925
1926 *n_listeners = *n_listeners + 1;
1927
a40a14f8
A
1928 return;
1929}
1930
1931
6bb65964
A
1932static int
1933init_MiG(launch_data_t l_reply, int *n_listeners)
1934{
1935 launch_data_t l_machservices;
1936 launch_data_type_t l_type;
1937
1938 l_machservices = launch_data_dict_lookup(l_reply, LAUNCH_JOBKEY_MACHSERVICES);
1939 l_type = (l_machservices != NULL) ? launch_data_get_type(l_machservices) : 0;
1940 if (l_type != LAUNCH_DATA_DICTIONARY) {
1941 SCLog(TRUE, LOG_ERR,
1942 CFSTR("SCHelper: error w/" LAUNCH_JOBKEY_MACHSERVICES " (%p, %d)"),
1943 l_machservices,
1944 l_type);
1945 return 1;
1946 }
1947
1948 launch_data_dict_iterate(l_machservices, init_MiG_1, (void *)n_listeners);
1949 return 0;
1950}
1951
1952
1953#pragma mark -
1954#pragma mark Main
a40a14f8
A
1955
1956
1957static const struct option longopts[] = {
1958 { "debug", no_argument, 0, 'd' },
1959 { 0, 0, 0, 0 }
1960};
1961
1962
1963int
1964main(int argc, char **argv)
1965{
6bb65964
A
1966 Boolean done = FALSE;
1967 int err = 0;
1968 int gen_reported = 0;
1969 int idle = 0;
a40a14f8
A
1970 launch_data_t l_msg;
1971 launch_data_t l_reply;
a40a14f8 1972 launch_data_type_t l_type;
6bb65964 1973 int n_listeners = 0;
a40a14f8
A
1974 extern int optind;
1975 int opt;
1976 int opti;
1977
1978 openlog("SCHelper", LOG_CONS|LOG_PID, LOG_DAEMON);
1979
1980 // process any arguments
1981 while ((opt = getopt_long(argc, argv, "d", longopts, &opti)) != -1) {
1982 switch(opt) {
1983 case 'd':
1984 debug = TRUE;
1985 break;
1986 case 0 :
1987// if (strcmp(longopts[opti].name, "debug") == 1) {
1988// }
1989 break;
1990 case '?':
1991 default :
1992 SCLog(TRUE, LOG_ERR,
1993 CFSTR("ignoring unknown or ambiguous command line option"));
1994 break;
edebe297 1995 }
a40a14f8
A
1996 }
1997// argc -= optind;
1998// argv += optind;
edebe297 1999
a40a14f8
A
2000 if (geteuid() != 0) {
2001 SCLog(TRUE, LOG_ERR, CFSTR("%s"), strerror(EACCES));
2002 exit(EACCES);
2003 }
2004
2005 main_runLoop = CFRunLoopGetCurrent();
2006
2007 l_msg = launch_data_new_string(LAUNCH_KEY_CHECKIN);
2008 l_reply = launch_msg(l_msg);
2009 launch_data_free(l_msg);
2010 l_type = (l_reply != NULL) ? launch_data_get_type(l_reply) : 0;
2011 if (l_type != LAUNCH_DATA_DICTIONARY) {
2012 SCLog(TRUE, LOG_ERR,
2013 CFSTR("SCHelper: error w/launchd " LAUNCH_KEY_CHECKIN " dictionary (%p, %d)"),
2014 l_reply,
2015 l_type);
2016 err = 1;
2017 goto done;
2018 }
2019
6bb65964
A
2020 err = init_MiG(l_reply, &n_listeners);
2021 if (err != 0) {
a40a14f8
A
2022 goto done;
2023 }
2024
a40a14f8
A
2025 done :
2026
2027 if (l_reply != NULL) launch_data_free(l_reply);
2028
6bb65964 2029 if ((err != 0) || (n_listeners == 0)) {
a40a14f8 2030 exit(err);
edebe297
A
2031 }
2032
6bb65964
A
2033 pthread_setname_np("SCHelper main thread");
2034
a40a14f8
A
2035 while (!done) {
2036 SInt32 rlStatus;
6bb65964 2037 int gen_current;
a40a14f8
A
2038
2039 rlStatus = CFRunLoopRunInMode(kCFRunLoopDefaultMode, 15.0, TRUE);
6bb65964
A
2040
2041 pthread_mutex_lock(&sessions_lock);
2042
2043 if (sessions != NULL) {
2044 if (rlStatus == kCFRunLoopRunTimedOut) {
2045 idle++;
2046
2047 if ((CFSetGetCount(sessions) == 0) && (sessions_closed == 0)) {
2048 // if we don't have any open sessions and no
2049 // sessions have recently been closed
2050 done = TRUE;
2051 }
2052 } else {
2053 idle = 0;
2054 }
a40a14f8 2055 }
6bb65964
A
2056 gen_current = sessions_generation;
2057 sessions_closed = 0;
2058
2059 if (!done && (idle >= (2 * 60 / 15))) {
2060 if (gen_reported != gen_current) {
2061 SCLog(TRUE, LOG_NOTICE, CFSTR("active (but IDLE) sessions"));
2062 CFSetApplyFunction(sessions, __SCHelperSessionLog, NULL);
2063 gen_reported = gen_current;
2064 }
2065 idle = 0;
2066 }
2067
2068 pthread_mutex_unlock(&sessions_lock);
edebe297
A
2069 }
2070
a40a14f8 2071 exit(EX_OK);
edebe297 2072}