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