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