]> git.saurik.com Git - apple/configd.git/blame - SystemConfiguration.fproj/helper/SCHelper_server.c
configd-395.7.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
1254 if (!CFEqual(domain, kCFErrorDomainMach) || (code != kIOReturnNotFound)) {
1255 // if unexpected error
1256 SCLog(TRUE, LOG_ERR,
1257 CFSTR("SecTaskCopyValueForEntitlement(,\"%@\",) failed, error = %@ : %@"),
1258 entitlement,
1259 error,
1260 sessionName(session));
1261 }
1262 CFRelease(error);
1263 }
1264
1265 CFRelease(task);
1266 } else {
1267 SCLog(TRUE, LOG_ERR,
1268 CFSTR("SecTaskCreateWithAuditToken() failed: %@"),
1269 sessionName(session));
1270 }
1271
1272 return value;
1273}
1274
1275#endif // TARGET_OS_IPHONE
1276
1277
1278
1279
edebe297 1280static Boolean
a40a14f8 1281hasAuthorization(SCHelperSessionRef session)
edebe297 1282{
a40a14f8
A
1283 AuthorizationRef authorization = __SCHelperSessionGetAuthorization(session);
1284
6bb65964
A
1285 if (authorization == NULL) {
1286 return FALSE;
1287 }
1288
a40a14f8 1289#if !TARGET_OS_IPHONE
edebe297
A
1290 AuthorizationFlags flags;
1291 AuthorizationItem items[1];
1292 AuthorizationRights rights;
1293 OSStatus status;
1294
edebe297
A
1295 items[0].name = "system.preferences";
1296 items[0].value = NULL;
1297 items[0].valueLength = 0;
1298 items[0].flags = 0;
1299
1300 rights.count = sizeof(items) / sizeof(items[0]);
1301 rights.items = items;
1302
1303 flags = kAuthorizationFlagDefaults;
1304 flags |= kAuthorizationFlagExtendRights;
1305 flags |= kAuthorizationFlagInteractionAllowed;
1306// flags |= kAuthorizationFlagPartialRights;
1307// flags |= kAuthorizationFlagPreAuthorize;
1308
1309 status = AuthorizationCopyRights(authorization,
1310 &rights,
1311 kAuthorizationEmptyEnvironment,
1312 flags,
1313 NULL);
1314 if (status != errAuthorizationSuccess) {
1315 return FALSE;
1316 }
1317
6bb65964
A
1318 return TRUE;
1319#else // !TARGET_OS_IPHONE
1320 SCHelperSessionPrivateRef sessionPrivate = (SCHelperSessionPrivateRef)session;
a40a14f8 1321
6bb65964
A
1322 if (sessionPrivate->callerWriteAccess == UNKNOWN) {
1323 CFArrayRef entitlement;
1324 CFStringRef prefsID;
1325 SCPreferencesPrivateRef prefsPrivate;
1326
1327 // assume that the client DOES NOT have the entitlement
1328 sessionPrivate->callerWriteAccess = NO;
1329
1330 prefsPrivate = (SCPreferencesPrivateRef)sessionPrivate->prefs;
1331 prefsID = (prefsPrivate->prefsID != NULL) ? prefsPrivate->prefsID : PREFS_DEFAULT_CONFIG;
1332
1333 entitlement = copyEntitlement(session, kSCWriteEntitlementName);
1334 if (entitlement != NULL) {
1335 if (isA_CFArray(entitlement)) {
1336 if (CFArrayContainsValue(entitlement,
1337 CFRangeMake(0, CFArrayGetCount(entitlement)),
1338 prefsID)) {
1339 // if client DOES have entitlement
1340 sessionPrivate->callerWriteAccess = YES;
1341 }
1342 } else {
1343 SCLog(TRUE, LOG_ERR,
1344 CFSTR("hasAuthorization: entitlement not valid: %@"),
1345 sessionName(session));
1346 }
a40a14f8 1347
6bb65964
A
1348 CFRelease(entitlement);
1349 }
a40a14f8 1350
6bb65964
A
1351 // make an exception for VPN configuration management
1352 if (sessionPrivate->callerWriteAccess != YES) {
1353 entitlement = copyEntitlement(session, kSCVPNFilterEntitlementName);
1354 if (entitlement != NULL) {
1355 if (isA_CFArray(entitlement)) {
1356 if (CFEqual(prefsID, PREFS_DEFAULT_CONFIG)) {
1357 // save the VPN bundle identifiers
1358 __SCHelperSessionSetVPNFilter(session, entitlement);
1359
1360 // and grant a "filtered" exception
1361 sessionPrivate->callerWriteAccess = YES;
1362 } else if (CFStringHasPrefix(prefsID, CFSTR("VPN-")) &&
1363 CFStringHasSuffix(prefsID, CFSTR(".plist"))) {
1364 CFRange range;
1365 CFStringRef vpnID;
1366
1367 range.location = sizeof("VPN-") - 1;
1368 range.length = CFStringGetLength(prefsID)
1369 - (sizeof("VPN-") - 1) // trim VPN-
1370 - (sizeof(".plist") - 1); // trim .plist
1371 vpnID = CFStringCreateWithSubstring(NULL, prefsID, range);
1372 if (CFArrayContainsValue(entitlement,
1373 CFRangeMake(0, CFArrayGetCount(entitlement)),
1374 vpnID)) {
1375 // grant an exception
1376 sessionPrivate->callerWriteAccess = YES;
1377 }
1378 CFRelease(vpnID);
1379 }
1380 }
1381
1382 CFRelease(entitlement);
a40a14f8
A
1383 }
1384 }
1385
6bb65964
A
1386 if (sessionPrivate->callerWriteAccess != YES) {
1387 SCLog(TRUE, LOG_ERR,
1388 CFSTR("SCPreferences write access to \"%@\" denied, no entitlement for \"%@\""),
1389 prefsID,
1390 sessionName(session));
a40a14f8
A
1391 }
1392 }
a40a14f8 1393
6bb65964
A
1394 return (sessionPrivate->callerWriteAccess == YES) ? TRUE : FALSE;
1395#endif // TARGET_OS_IPHONE
edebe297
A
1396}
1397
1398
a40a14f8
A
1399typedef Boolean (*helperFunction) (SCHelperSessionRef session,
1400 void *info,
1401 CFDataRef data,
1402 uint32_t *status,
1403 CFDataRef *reply);
edebe297
A
1404
1405
1406static const struct helper {
1407 int command;
1408 const char *commandName;
1409 Boolean needsAuthorization;
1410 helperFunction func;
1411 void *info;
1412} helpers[] = {
1413 { SCHELPER_MSG_AUTH, "AUTH", FALSE, do_Auth , NULL },
1414
1415 { SCHELPER_MSG_PREFS_OPEN, "PREFS open", FALSE, do_prefs_Open , NULL },
1416 { SCHELPER_MSG_PREFS_ACCESS, "PREFS access", TRUE, do_prefs_Access , NULL },
1417 { SCHELPER_MSG_PREFS_LOCK, "PREFS lock", TRUE, do_prefs_Lock , (void *)FALSE },
1418 { SCHELPER_MSG_PREFS_LOCKWAIT, "PREFS lock/wait", TRUE, do_prefs_Lock , (void *)TRUE },
1419 { SCHELPER_MSG_PREFS_COMMIT, "PREFS commit", TRUE, do_prefs_Commit , NULL },
1420 { SCHELPER_MSG_PREFS_APPLY, "PREFS apply", TRUE, do_prefs_Apply , NULL },
1421 { SCHELPER_MSG_PREFS_UNLOCK, "PREFS unlock", FALSE, do_prefs_Unlock , NULL },
1422 { SCHELPER_MSG_PREFS_CLOSE, "PREFS close", FALSE, do_prefs_Close , NULL },
6d034b4e 1423 { SCHELPER_MSG_PREFS_SYNCHRONIZE, "PREFS synchronize", FALSE, do_prefs_Synchronize , NULL },
edebe297
A
1424
1425 { SCHELPER_MSG_INTERFACE_REFRESH, "INTERFACE refresh", TRUE, do_interface_refresh , NULL },
1426
a40a14f8 1427#if !TARGET_OS_IPHONE
edebe297
A
1428 { SCHELPER_MSG_KEYCHAIN_COPY, "KEYCHAIN copy", TRUE, do_keychain_copy , NULL },
1429 { SCHELPER_MSG_KEYCHAIN_EXISTS, "KEYCHAIN exists", TRUE, do_keychain_exists , NULL },
1430 { SCHELPER_MSG_KEYCHAIN_REMOVE, "KEYCHAIN remove", TRUE, do_keychain_remove , NULL },
1431 { SCHELPER_MSG_KEYCHAIN_SET, "KEYCHAIN set", TRUE, do_keychain_set , NULL },
a40a14f8 1432#endif // !TARGET_OS_IPHONE
edebe297
A
1433
1434 { SCHELPER_MSG_EXIT, "EXIT", FALSE, do_Exit , NULL }
1435};
1436#define nHELPERS (sizeof(helpers)/sizeof(struct helper))
1437
1438
1439static int
6bb65964 1440findCommand(uint32_t command)
edebe297
A
1441{
1442 int i;
1443
1444 for (i = 0; i < (int)nHELPERS; i++) {
1445 if (helpers[i].command == command) {
1446 return i;
1447 }
1448 }
1449
1450 return -1;
1451}
1452
1453
6bb65964
A
1454static void *
1455newHelper(void *arg)
edebe297 1456{
6bb65964
A
1457 CFRunLoopSourceRef rls = NULL;
1458 SCHelperSessionRef session = (SCHelperSessionRef)arg;
1459 SCHelperSessionPrivateRef sessionPrivate = (SCHelperSessionPrivateRef)session;
a40a14f8 1460
6bb65964 1461 __SCHelperSessionSetThreadName(session);
edebe297 1462
6bb65964
A
1463 rls = CFMachPortCreateRunLoopSource(NULL, sessionPrivate->mp, 0);
1464 CFRelease(sessionPrivate->mp);
edebe297 1465
6bb65964
A
1466 if (rls != NULL) {
1467 CFRunLoopAddSource(CFRunLoopGetCurrent(), rls, kCFRunLoopDefaultMode);
1468 CFRelease(rls);
a40a14f8 1469
6bb65964
A
1470 SCLog(debug, LOG_DEBUG, CFSTR("%p : start"), session);
1471 CFRunLoopRun();
1472 SCLog(debug, LOG_DEBUG, CFSTR("%p : stop"), session);
edebe297
A
1473 }
1474
6bb65964
A
1475 return NULL;
1476}
1477
1478
1479#pragma mark -
1480#pragma mark Main loop
1481
1482
1483// MiG generated externals and functions
1484extern struct mig_subsystem _helper_subsystem;
1485extern boolean_t helper_server(mach_msg_header_t *, mach_msg_header_t *);
1486
1487
1488static
1489boolean_t
1490notify_server(mach_msg_header_t *request, mach_msg_header_t *reply)
1491{
1492 mach_no_senders_notification_t *Request = (mach_no_senders_notification_t *)request;
1493 mig_reply_error_t *Reply = (mig_reply_error_t *)reply;
1494
1495 reply->msgh_bits = MACH_MSGH_BITS(MACH_MSGH_BITS_REMOTE(request->msgh_bits), 0);
1496 reply->msgh_remote_port = request->msgh_remote_port;
1497 reply->msgh_size = sizeof(mig_reply_error_t); /* Minimal size: update as needed */
1498 reply->msgh_local_port = MACH_PORT_NULL;
1499 reply->msgh_id = request->msgh_id + 100;
1500
1501 if ((Request->not_header.msgh_id > MACH_NOTIFY_LAST) ||
1502 (Request->not_header.msgh_id < MACH_NOTIFY_FIRST)) {
1503 Reply->NDR = NDR_record;
1504 Reply->RetCode = MIG_BAD_ID;
1505 return FALSE; /* if this is not a notification message */
edebe297
A
1506 }
1507
6bb65964
A
1508 switch (Request->not_header.msgh_id) {
1509 case MACH_NOTIFY_NO_SENDERS : {
1510 SCHelperSessionRef session;
edebe297 1511
6bb65964
A
1512 __MACH_PORT_DEBUG(TRUE, "*** notify_server MACH_NOTIFY_NO_SENDERS", Request->not_header.msgh_local_port);
1513
1514 // clean up session
1515 session = __SCHelperSessionFindWithPort(Request->not_header.msgh_local_port);
1516 if (session != NULL) {
1517 SCHelperSessionPrivateRef sessionPrivate = (SCHelperSessionPrivateRef)session;
1518
1519 // release CFMachPort *and* SCHelperSession
1520 CFMachPortInvalidate(sessionPrivate->mp);
1521 }
1522
1523 __MACH_PORT_DEBUG(TRUE, "*** notify_server after invalidate", Request->not_header.msgh_local_port);
1524
1525 // and, lastly, remove our receive right.
1526 (void) mach_port_mod_refs(mach_task_self(),
1527 Request->not_header.msgh_local_port,
1528 MACH_PORT_RIGHT_RECEIVE, -1);
1529
1530 Reply->Head.msgh_bits = 0;
1531 Reply->Head.msgh_remote_port = MACH_PORT_NULL;
1532 Reply->RetCode = KERN_SUCCESS;
1533 return TRUE;
edebe297 1534 }
6bb65964
A
1535
1536 default :
1537 break;
a40a14f8 1538 }
edebe297 1539
6bb65964
A
1540 SCLog(TRUE, LOG_ERR, CFSTR("HELP!, Received notification: port=%d, msgh_id=%d"),
1541 Request->not_header.msgh_local_port,
1542 Request->not_header.msgh_id);
edebe297 1543
6bb65964
A
1544 Reply->NDR = NDR_record;
1545 Reply->RetCode = MIG_BAD_ID;
1546 return FALSE; /* if this is not a notification we are handling */
1547}
1548
1549
1550__private_extern__
1551boolean_t
1552helper_demux(mach_msg_header_t *request, mach_msg_header_t *reply)
1553{
1554 Boolean processed = FALSE;
1555
1556 /*
1557 * (attempt to) process SCHelper requests.
1558 */
1559 processed = helper_server(request, reply);
1560 if (processed) {
1561 return TRUE;
a40a14f8 1562 }
edebe297 1563
6bb65964
A
1564 /*
1565 * (attempt to) process (NO MORE SENDERS) notification messages.
1566 */
1567 processed = notify_server(request, reply);
1568 if (processed) {
1569 return TRUE;
a40a14f8 1570 }
edebe297 1571
6bb65964
A
1572 /*
1573 * unknown message ID, log and return an error.
1574 */
1575 SCLog(TRUE, LOG_ERR, CFSTR("helper_demux(): unknown message ID (%d) received"), request->msgh_id);
1576 reply->msgh_bits = MACH_MSGH_BITS(MACH_MSGH_BITS_REMOTE(request->msgh_bits), 0);
1577 reply->msgh_remote_port = request->msgh_remote_port;
1578 reply->msgh_size = sizeof(mig_reply_error_t); /* Minimal size */
1579 reply->msgh_local_port = MACH_PORT_NULL;
1580 reply->msgh_id = request->msgh_id + 100;
1581 ((mig_reply_error_t *)reply)->NDR = NDR_record;
1582 ((mig_reply_error_t *)reply)->RetCode = MIG_BAD_ID;
1583
1584 return FALSE;
a40a14f8 1585}
edebe297 1586
edebe297 1587
6bb65964 1588#define MACH_MSG_BUFFER_SIZE 128
a40a14f8
A
1589
1590
1591static void
6bb65964 1592helperCallback(CFMachPortRef port, void *msg, CFIndex size, void *info)
a40a14f8 1593{
6bb65964
A
1594 mig_reply_error_t * bufRequest = msg;
1595 uint32_t bufReply_q[MACH_MSG_BUFFER_SIZE/sizeof(uint32_t)];
1596 mig_reply_error_t * bufReply = (mig_reply_error_t *)bufReply_q;
1597 static CFIndex bufSize = 0;
1598 mach_msg_return_t mr;
1599 int options;
1600
1601 if (bufSize == 0) {
1602 // get max size for MiG reply buffers
1603 bufSize = _helper_subsystem.maxsize;
1604
1605 // check if our on-the-stack reply buffer will be big enough
1606 if (bufSize > sizeof(bufReply_q)) {
1607 SCLog(TRUE, LOG_NOTICE,
1608 CFSTR("helperCallback(): buffer size should be increased > %d"),
1609 _helper_subsystem.maxsize);
1610 }
a40a14f8
A
1611 }
1612
6bb65964
A
1613 if (bufSize > sizeof(bufReply_q)) {
1614 bufReply = CFAllocatorAllocate(NULL, _helper_subsystem.maxsize, 0);
a40a14f8 1615 }
6bb65964 1616 bufReply->RetCode = 0;
a40a14f8 1617
6bb65964
A
1618 /* we have a request message */
1619 (void) helper_demux(&bufRequest->Head, &bufReply->Head);
a40a14f8 1620
6bb65964
A
1621 if (!(bufReply->Head.msgh_bits & MACH_MSGH_BITS_COMPLEX)) {
1622 if (bufReply->RetCode == MIG_NO_REPLY) {
1623 bufReply->Head.msgh_remote_port = MACH_PORT_NULL;
1624 } else if ((bufReply->RetCode != KERN_SUCCESS) &&
1625 (bufRequest->Head.msgh_bits & MACH_MSGH_BITS_COMPLEX)) {
1626 /*
1627 * destroy the request - but not the reply port
1628 */
1629 bufRequest->Head.msgh_remote_port = MACH_PORT_NULL;
1630 mach_msg_destroy(&bufRequest->Head);
1631 }
1632 }
a40a14f8 1633
6bb65964
A
1634 if (bufReply->Head.msgh_remote_port != MACH_PORT_NULL) {
1635 /*
1636 * send reply.
1637 *
1638 * We don't want to block indefinitely because the client
1639 * isn't receiving messages from the reply port.
1640 * If we have a send-once right for the reply port, then
1641 * this isn't a concern because the send won't block.
1642 * If we have a send right, we need to use MACH_SEND_TIMEOUT.
1643 * To avoid falling off the kernel's fast RPC path unnecessarily,
1644 * we only supply MACH_SEND_TIMEOUT when absolutely necessary.
1645 */
a40a14f8 1646
6bb65964
A
1647 options = MACH_SEND_MSG;
1648 if (MACH_MSGH_BITS_REMOTE(bufReply->Head.msgh_bits) != MACH_MSG_TYPE_MOVE_SEND_ONCE) {
1649 options |= MACH_SEND_TIMEOUT;
1650 }
1651 mr = mach_msg(&bufReply->Head, /* msg */
1652 options, /* option */
1653 bufReply->Head.msgh_size, /* send_size */
1654 0, /* rcv_size */
1655 MACH_PORT_NULL, /* rcv_name */
1656 MACH_MSG_TIMEOUT_NONE, /* timeout */
1657 MACH_PORT_NULL); /* notify */
1658
1659 /* Has a message error occurred? */
1660 switch (mr) {
1661 case MACH_SEND_INVALID_DEST:
1662 case MACH_SEND_TIMED_OUT:
1663 break;
1664 default :
1665 /* Includes success case. */
1666 goto done;
1667 }
1668 }
a40a14f8 1669
6bb65964
A
1670 if (bufReply->Head.msgh_bits & MACH_MSGH_BITS_COMPLEX) {
1671 mach_msg_destroy(&bufReply->Head);
a40a14f8 1672 }
a40a14f8 1673
6bb65964 1674 done :
a40a14f8 1675
6bb65964
A
1676 if (bufReply != (mig_reply_error_t *)bufReply_q)
1677 CFAllocatorDeallocate(NULL, bufReply);
1678 return;
1679}
a40a14f8 1680
a40a14f8 1681
6bb65964
A
1682static CFStringRef
1683initMPCopyDescription(const void *info)
1684{
1685 return CFStringCreateWithFormat(NULL, NULL, CFSTR("<SCHelper MP>"));
a40a14f8
A
1686}
1687
1688
6bb65964
A
1689__private_extern__
1690kern_return_t
1691_helperinit(mach_port_t server,
1692 mach_port_t *newSession,
1693 uint32_t *status,
1694 audit_token_t audit_token)
a40a14f8 1695{
6bb65964
A
1696 CFMachPortContext context = { 0
1697 , NULL
1698 , CFRetain
1699 , CFRelease
1700 , initMPCopyDescription
1701 };
1702 kern_return_t kr;
1703 mach_port_t oldNotify;
1704 SCHelperSessionRef session;
1705 SCHelperSessionPrivateRef sessionPrivate;
1706 pthread_attr_t tattr;
1707 pthread_t tid;
a40a14f8 1708
6bb65964
A
1709 session = __SCHelperSessionFindWithPort(server);
1710 if (session != NULL) {
1711#ifdef DEBUG
1712 SCLog(TRUE, LOG_DEBUG, CFSTR("_helperinit(): session is already open."));
1713#endif /* DEBUG */
1714 *status = kSCStatusFailed; /* you can't re-open an "open" session */
1715 return KERN_SUCCESS;
a40a14f8
A
1716 }
1717
6bb65964
A
1718 session = __SCHelperSessionCreate(NULL);
1719 sessionPrivate = (SCHelperSessionPrivateRef)session;
1720
1721 // create per-session port
1722 (void) mach_port_allocate(mach_task_self(),
1723 MACH_PORT_RIGHT_RECEIVE,
1724 &sessionPrivate->port);
1725 *newSession = sessionPrivate->port;
1726
1727 //
1728 // Note: we create the CFMachPort *before* we insert a send
1729 // right present to ensure that CF does not establish
1730 // its dead name notification.
1731 //
1732 context.info = (void *)session;
1733 sessionPrivate->mp = _SC_CFMachPortCreateWithPort("SCHelper/session",
1734 *newSession,
1735 helperCallback,
1736 &context);
1737
1738 /* Request a notification when/if the client dies */
1739 kr = mach_port_request_notification(mach_task_self(),
1740 *newSession,
1741 MACH_NOTIFY_NO_SENDERS,
1742 1,
1743 *newSession,
1744 MACH_MSG_TYPE_MAKE_SEND_ONCE,
1745 &oldNotify);
1746 if (kr != KERN_SUCCESS) {
1747 SCLog(TRUE, LOG_ERR, CFSTR("_helperinit() mach_port_request_notification() failed: %s"), mach_error_string(kr));
1748
1749 // clean up CFMachPort, mach port rights
1750 CFMachPortInvalidate(sessionPrivate->mp);
1751 CFRelease(sessionPrivate->mp);
1752 sessionPrivate->mp = NULL;
1753 (void) mach_port_mod_refs(mach_task_self(), *newSession, MACH_PORT_RIGHT_RECEIVE, -1);
1754 *newSession = MACH_PORT_NULL;
1755 *status = kSCStatusFailed;
1756 goto done;
a40a14f8
A
1757 }
1758
6bb65964
A
1759 if (oldNotify != MACH_PORT_NULL) {
1760 SCLog(TRUE, LOG_ERR, CFSTR("_helperinit(): oldNotify != MACH_PORT_NULL"));
a40a14f8
A
1761 }
1762
6bb65964
A
1763 // add send right (that will be passed back to the client)
1764 (void) mach_port_insert_right(mach_task_self(),
1765 *newSession,
1766 *newSession,
1767 MACH_MSG_TYPE_MAKE_SEND);
1768
1769 // save audit token
1770 sessionPrivate->auditToken = audit_token;
1771
1772 //
1773 // Note: at this time we should be holding ONE send right and
1774 // ONE receive right to the server. The send right is
1775 // moved to the caller.
1776 //
1777
a40a14f8
A
1778 // start per-session thread
1779 pthread_attr_init(&tattr);
1780 pthread_attr_setscope(&tattr, PTHREAD_SCOPE_SYSTEM);
1781 pthread_attr_setdetachstate(&tattr, PTHREAD_CREATE_DETACHED);
1782 pthread_attr_setstacksize(&tattr, 96 * 1024); // each thread gets a 96K stack
6bb65964 1783 pthread_create(&tid, &tattr, newHelper, (void *)session);
a40a14f8 1784 pthread_attr_destroy(&tattr);
edebe297 1785
6bb65964
A
1786 *status = kSCStatusOK;
1787
1788 done :
1789
1790 CFRelease(session);
1791 return KERN_SUCCESS;
1792}
1793
1794
1795__private_extern__
1796kern_return_t
1797_helperexec(mach_port_t server,
1798 uint32_t msgID,
1799 xmlData_t dataRef, /* raw XML bytes */
1800 mach_msg_type_number_t dataLen,
1801 uint32_t *status,
1802 xmlDataOut_t *replyRef, /* raw XML bytes */
1803 mach_msg_type_number_t *replyLen)
1804{
1805 CFDataRef data = NULL;
1806 int i;
1807 CFDataRef reply = NULL;
1808 SCHelperSessionRef session;
1809
1810 *status = kSCStatusOK;
1811 *replyRef = NULL;
1812 *replyLen = 0;
1813
1814 if ((dataRef != NULL) && (dataLen > 0)) {
1815 if (!_SCUnserializeData(&data, (void *)dataRef, dataLen)) {
1816 *status = SCError();
1817 return KERN_SUCCESS;
1818 }
1819 }
1820
1821 session = __SCHelperSessionFindWithPort(server);
1822 if (session == NULL) {
1823 *status = kSCStatusFailed; /* you must have an open session to play */
1824 goto done;
1825 }
1826
1827 i = findCommand(msgID);
1828 if (i == -1) {
1829 SCLog(TRUE, LOG_ERR, CFSTR("received unknown command : %u"), msgID);
1830 *status = kSCStatusInvalidArgument;
1831 goto done;
1832 }
1833
1834 SCLog(debug, LOG_DEBUG,
1835 CFSTR("%p : processing command \"%s\"%s"),
1836 session,
1837 helpers[i].commandName,
1838 (data != NULL) ? " w/data" : "");
1839
1840 if (helpers[i].needsAuthorization && !hasAuthorization(session)) {
1841 SCLog(debug, LOG_DEBUG,
1842 CFSTR("%p : command \"%s\" : not authorized"),
1843 session,
1844 helpers[i].commandName);
1845 *status = kSCStatusAccessError;
1846 }
1847
1848 if (*status == kSCStatusOK) {
1849 (*helpers[i].func)(session, helpers[i].info, data, status, &reply);
1850 }
1851
1852 if ((*status != -1) || (reply != NULL)) {
1853 Boolean ok;
1854
1855 SCLog(debug, LOG_DEBUG,
1856 CFSTR("%p : sending status %u%s"),
1857 session,
1858 *status,
1859 (reply != NULL) ? " w/reply" : "");
1860
1861 /* serialize the data */
1862 if (reply != NULL) {
1863 ok = _SCSerializeData(reply, (void **)replyRef, (CFIndex *)replyLen);
1864 CFRelease(reply);
1865 reply = NULL;
1866 if (!ok) {
1867 *status = SCError();
1868 goto done;
1869 }
1870 }
1871 }
1872
1873 done :
1874
1875 if (data != NULL) CFRelease(data);
1876 if (reply != NULL) CFRelease(reply);
1877 return KERN_SUCCESS;
1878}
1879
1880
1881static CFStringRef
1882helperMPCopyDescription(const void *info)
1883{
1884 return CFStringCreateWithFormat(NULL, NULL, CFSTR("<main SCHelper MP>"));
1885}
1886
1887
1888static void
1889init_MiG_1(const launch_data_t l_obj, const char *name, void *info)
1890{
1891 CFMachPortContext context = { 0
1892 , (void *)1
1893 , NULL
1894 , NULL
1895 , helperMPCopyDescription
1896 };
1897 launch_data_type_t l_type;
1898 CFMachPortRef mp;
1899 int *n_listeners = (int *)info;
1900 CFRunLoopSourceRef rls;
1901 mach_port_t service_port;
1902
1903 // get the mach port
1904 l_type = (l_obj != NULL) ? launch_data_get_type(l_obj) : 0;
1905 if (l_type != LAUNCH_DATA_MACHPORT) {
1906 SCLog(TRUE, LOG_ERR,
1907 CFSTR("SCHelper: error w/MachServices \"%s\" port (%p, %d)"),
1908 (name != NULL) ? name : "?",
1909 l_obj,
1910 l_type);
1911 return;
1912 }
1913 service_port = launch_data_get_machport(l_obj);
1914
1915 // add a run loop source to listen for new requests
1916 mp = _SC_CFMachPortCreateWithPort("SCHelper/server",
1917 service_port,
1918 helperCallback,
1919 &context);
1920 rls = CFMachPortCreateRunLoopSource(NULL, mp, 0);
1921 CFRelease(mp);
1922 CFRunLoopAddSource(CFRunLoopGetCurrent(), rls, kCFRunLoopDefaultMode);
1923 CFRelease(rls);
1924
1925 *n_listeners = *n_listeners + 1;
1926
a40a14f8
A
1927 return;
1928}
1929
1930
6bb65964
A
1931static int
1932init_MiG(launch_data_t l_reply, int *n_listeners)
1933{
1934 launch_data_t l_machservices;
1935 launch_data_type_t l_type;
1936
1937 l_machservices = launch_data_dict_lookup(l_reply, LAUNCH_JOBKEY_MACHSERVICES);
1938 l_type = (l_machservices != NULL) ? launch_data_get_type(l_machservices) : 0;
1939 if (l_type != LAUNCH_DATA_DICTIONARY) {
1940 SCLog(TRUE, LOG_ERR,
1941 CFSTR("SCHelper: error w/" LAUNCH_JOBKEY_MACHSERVICES " (%p, %d)"),
1942 l_machservices,
1943 l_type);
1944 return 1;
1945 }
1946
1947 launch_data_dict_iterate(l_machservices, init_MiG_1, (void *)n_listeners);
1948 return 0;
1949}
1950
1951
1952#pragma mark -
1953#pragma mark Main
a40a14f8
A
1954
1955
1956static const struct option longopts[] = {
1957 { "debug", no_argument, 0, 'd' },
1958 { 0, 0, 0, 0 }
1959};
1960
1961
1962int
1963main(int argc, char **argv)
1964{
6bb65964
A
1965 Boolean done = FALSE;
1966 int err = 0;
1967 int gen_reported = 0;
1968 int idle = 0;
a40a14f8
A
1969 launch_data_t l_msg;
1970 launch_data_t l_reply;
a40a14f8 1971 launch_data_type_t l_type;
6bb65964 1972 int n_listeners = 0;
a40a14f8
A
1973 extern int optind;
1974 int opt;
1975 int opti;
1976
1977 openlog("SCHelper", LOG_CONS|LOG_PID, LOG_DAEMON);
1978
1979 // process any arguments
1980 while ((opt = getopt_long(argc, argv, "d", longopts, &opti)) != -1) {
1981 switch(opt) {
1982 case 'd':
1983 debug = TRUE;
1984 break;
1985 case 0 :
1986// if (strcmp(longopts[opti].name, "debug") == 1) {
1987// }
1988 break;
1989 case '?':
1990 default :
1991 SCLog(TRUE, LOG_ERR,
1992 CFSTR("ignoring unknown or ambiguous command line option"));
1993 break;
edebe297 1994 }
a40a14f8
A
1995 }
1996// argc -= optind;
1997// argv += optind;
edebe297 1998
a40a14f8
A
1999 if (geteuid() != 0) {
2000 SCLog(TRUE, LOG_ERR, CFSTR("%s"), strerror(EACCES));
2001 exit(EACCES);
2002 }
2003
2004 main_runLoop = CFRunLoopGetCurrent();
2005
2006 l_msg = launch_data_new_string(LAUNCH_KEY_CHECKIN);
2007 l_reply = launch_msg(l_msg);
2008 launch_data_free(l_msg);
2009 l_type = (l_reply != NULL) ? launch_data_get_type(l_reply) : 0;
2010 if (l_type != LAUNCH_DATA_DICTIONARY) {
2011 SCLog(TRUE, LOG_ERR,
2012 CFSTR("SCHelper: error w/launchd " LAUNCH_KEY_CHECKIN " dictionary (%p, %d)"),
2013 l_reply,
2014 l_type);
2015 err = 1;
2016 goto done;
2017 }
2018
6bb65964
A
2019 err = init_MiG(l_reply, &n_listeners);
2020 if (err != 0) {
a40a14f8
A
2021 goto done;
2022 }
2023
a40a14f8
A
2024 done :
2025
2026 if (l_reply != NULL) launch_data_free(l_reply);
2027
6bb65964 2028 if ((err != 0) || (n_listeners == 0)) {
a40a14f8 2029 exit(err);
edebe297
A
2030 }
2031
6bb65964
A
2032 pthread_setname_np("SCHelper main thread");
2033
a40a14f8
A
2034 while (!done) {
2035 SInt32 rlStatus;
6bb65964 2036 int gen_current;
a40a14f8
A
2037
2038 rlStatus = CFRunLoopRunInMode(kCFRunLoopDefaultMode, 15.0, TRUE);
6bb65964
A
2039
2040 pthread_mutex_lock(&sessions_lock);
2041
2042 if (sessions != NULL) {
2043 if (rlStatus == kCFRunLoopRunTimedOut) {
2044 idle++;
2045
2046 if ((CFSetGetCount(sessions) == 0) && (sessions_closed == 0)) {
2047 // if we don't have any open sessions and no
2048 // sessions have recently been closed
2049 done = TRUE;
2050 }
2051 } else {
2052 idle = 0;
2053 }
a40a14f8 2054 }
6bb65964
A
2055 gen_current = sessions_generation;
2056 sessions_closed = 0;
2057
2058 if (!done && (idle >= (2 * 60 / 15))) {
2059 if (gen_reported != gen_current) {
2060 SCLog(TRUE, LOG_NOTICE, CFSTR("active (but IDLE) sessions"));
2061 CFSetApplyFunction(sessions, __SCHelperSessionLog, NULL);
2062 gen_reported = gen_current;
2063 }
2064 idle = 0;
2065 }
2066
2067 pthread_mutex_unlock(&sessions_lock);
edebe297
A
2068 }
2069
a40a14f8 2070 exit(EX_OK);
edebe297 2071}