]> git.saurik.com Git - apple/configd.git/blob - SystemConfiguration.fproj/helper/SCHelper_server.c
cdf3fd3dae78c501e7c8d5d3f4de5abcf71d9368
[apple/configd.git] / SystemConfiguration.fproj / helper / SCHelper_server.c
1 /*
2 * Copyright (c) 2005-2009 Apple Inc. All rights reserved.
3 *
4 * @APPLE_LICENSE_HEADER_START@
5 *
6 * This file contains Original Code and/or Modifications of Original Code
7 * as defined in and that are subject to the Apple Public Source License
8 * Version 2.0 (the 'License'). You may not use this file except in
9 * compliance with the License. Please obtain a copy of the License at
10 * http://www.opensource.apple.com/apsl/ and read it before using this
11 * file.
12 *
13 * The Original Code and all software distributed under the License are
14 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
15 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
16 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
18 * Please see the License for the specific language governing rights and
19 * limitations under the License.
20 *
21 * @APPLE_LICENSE_HEADER_END@
22 */
23
24 #include <stdlib.h>
25 #include <getopt.h>
26 #include <unistd.h>
27 #include <sys/types.h>
28
29 #include <CoreFoundation/CoreFoundation.h>
30 #include <CoreFoundation/CFRuntime.h>
31 #include <SystemConfiguration/SystemConfiguration.h>
32 #include <SystemConfiguration/SCPrivate.h>
33 #include <SystemConfiguration/SCValidation.h>
34 #include <pthread.h>
35 #include <sysexits.h>
36
37 #include "SCPreferencesInternal.h"
38 #include "SCHelper_client.h"
39 #include "helper_comm.h"
40
41
42 #if TARGET_OS_IPHONE
43 #include <grp.h>
44
45 __private_extern__ int
46 getgrnam_r(const char *name, __unused struct group *grp, __unused char *buf, __unused size_t bufsize, struct group **grpP)
47 {
48 *grpP = getgrnam(name);
49 return (*grpP == NULL) ? -1 : 0;
50 }
51 #endif // TARGET_OS_IPHONE
52
53
54 #pragma mark -
55 #pragma mark Session managment
56
57
58 typedef const struct __SCHelperSession * SCHelperSessionRef;
59
60 typedef struct {
61
62 // base CFType information
63 CFRuntimeBase cfBase;
64
65 // authorization
66 AuthorizationRef authorization;
67 #if TARGET_OS_IPHONE
68 uid_t peer_euid;
69 gid_t peer_egid;
70 #endif // TARGET_OS_IPHONE
71
72 // preferences
73 SCPreferencesRef prefs;
74
75 } SCHelperSessionPrivate, *SCHelperSessionPrivateRef;
76
77
78 static AuthorizationRef
79 __SCHelperSessionGetAuthorization(SCHelperSessionRef session)
80 {
81 SCHelperSessionPrivateRef sessionPrivate = (SCHelperSessionPrivateRef)session;
82
83 return sessionPrivate->authorization;
84 }
85
86
87 static Boolean
88 __SCHelperSessionSetAuthorization(SCHelperSessionRef session, CFTypeRef authorizationData)
89 {
90 Boolean ok = TRUE;
91 SCHelperSessionPrivateRef sessionPrivate = (SCHelperSessionPrivateRef)session;
92
93 #if !TARGET_OS_IPHONE
94 if (sessionPrivate->authorization != NULL) {
95 AuthorizationFree(sessionPrivate->authorization, kAuthorizationFlagDefaults);
96 // AuthorizationFree(sessionPrivate->authorization, kAuthorizationFlagDestroyRights);
97 sessionPrivate->authorization = NULL;
98 }
99
100 if (isA_CFData(authorizationData)) {
101 AuthorizationExternalForm extForm;
102
103 if (CFDataGetLength(authorizationData) == sizeof(extForm.bytes)) {
104 OSStatus err;
105
106 bcopy(CFDataGetBytePtr(authorizationData), extForm.bytes, sizeof(extForm.bytes));
107 err = AuthorizationCreateFromExternalForm(&extForm,
108 &sessionPrivate->authorization);
109 if (err != errAuthorizationSuccess) {
110 SCLog(TRUE, LOG_ERR,
111 CFSTR("AuthorizationCreateFromExternalForm() failed: status = %d"),
112 (int)err);
113 sessionPrivate->authorization = NULL;
114 ok = FALSE;
115 }
116 }
117 }
118 #else // !TARGET_OS_IPHONE
119 if (sessionPrivate->authorization != NULL) {
120 CFRelease(sessionPrivate->authorization);
121 sessionPrivate->authorization = NULL;
122 }
123
124 if (isA_CFString(authorizationData)) {
125 sessionPrivate->authorization = (void *)CFRetain(authorizationData);
126 }
127 #endif // !TARGET_OS_IPHONE
128
129 return ok;
130 }
131
132
133 #if TARGET_OS_IPHONE
134 static void
135 __SCHelperSessionGetCredentials(SCHelperSessionRef session, uid_t *euid, gid_t *egid)
136 {
137 SCHelperSessionPrivateRef sessionPrivate = (SCHelperSessionPrivateRef)session;
138
139 if (euid != NULL) *euid = sessionPrivate->peer_euid;
140 if (egid != NULL) *egid = sessionPrivate->peer_egid;
141 return;
142 }
143
144
145 static Boolean
146 __SCHelperSessionSetCredentials(SCHelperSessionRef session, uid_t euid, gid_t egid)
147 {
148 SCHelperSessionPrivateRef sessionPrivate = (SCHelperSessionPrivateRef)session;
149
150 sessionPrivate->peer_euid = euid;
151 sessionPrivate->peer_egid = egid;
152 return TRUE;
153 }
154 #endif // TARGET_OS_IPHONE
155
156 static SCPreferencesRef
157 __SCHelperSessionGetPreferences(SCHelperSessionRef session)
158 {
159 SCHelperSessionPrivateRef sessionPrivate = (SCHelperSessionPrivateRef)session;
160
161 return sessionPrivate->prefs;
162 }
163
164
165 static Boolean
166 __SCHelperSessionSetPreferences(SCHelperSessionRef session, SCPreferencesRef prefs)
167 {
168 SCHelperSessionPrivateRef sessionPrivate = (SCHelperSessionPrivateRef)session;
169
170 if (prefs != NULL) {
171 CFRetain(prefs);
172 }
173 if (sessionPrivate->prefs != NULL) {
174 CFRelease(sessionPrivate->prefs);
175 }
176 sessionPrivate->prefs = prefs;
177
178 return TRUE;
179 }
180
181
182 static CFStringRef __SCHelperSessionCopyDescription (CFTypeRef cf);
183 static void __SCHelperSessionDeallocate (CFTypeRef cf);
184
185
186 static CFTypeID __kSCHelperSessionTypeID = _kCFRuntimeNotATypeID;
187 static Boolean debug = FALSE;
188 static pthread_once_t initialized = PTHREAD_ONCE_INIT;
189 static CFRunLoopRef main_runLoop = NULL;
190 static CFMutableSetRef sessions = NULL;
191 static int sessions_closed = 0; // count of sessions recently closed
192 static pthread_mutex_t sessions_lock = PTHREAD_MUTEX_INITIALIZER;
193
194
195 static const CFRuntimeClass __SCHelperSessionClass = {
196 0, // version
197 "SCHelperSession", // className
198 NULL, // init
199 NULL, // copy
200 __SCHelperSessionDeallocate, // dealloc
201 NULL, // equal
202 NULL, // hash
203 NULL, // copyFormattingDesc
204 __SCHelperSessionCopyDescription // copyDebugDesc
205 };
206
207
208 static CFStringRef
209 __SCHelperSessionCopyDescription(CFTypeRef cf)
210 {
211 CFAllocatorRef allocator = CFGetAllocator(cf);
212 CFMutableStringRef result;
213 SCHelperSessionPrivateRef sessionPrivate = (SCHelperSessionPrivateRef)cf;
214
215 result = CFStringCreateMutable(allocator, 0);
216 CFStringAppendFormat(result, NULL, CFSTR("<SCHelperSession %p [%p]> {"), cf, allocator);
217 CFStringAppendFormat(result, NULL, CFSTR("authorization = %p"), sessionPrivate->authorization);
218 CFStringAppendFormat(result, NULL, CFSTR(", prefs = %p"), sessionPrivate->prefs);
219 CFStringAppendFormat(result, NULL, CFSTR("}"));
220
221 return result;
222 }
223
224
225 static void
226 __SCHelperSessionDeallocate(CFTypeRef cf)
227 {
228 SCHelperSessionPrivateRef sessionPrivate = (SCHelperSessionPrivateRef)cf;
229
230 // release resources
231 __SCHelperSessionSetAuthorization((SCHelperSessionRef)sessionPrivate, NULL);
232 __SCHelperSessionSetPreferences ((SCHelperSessionRef)sessionPrivate, NULL);
233
234 // we no longer need/want to track this session
235 pthread_mutex_lock(&sessions_lock);
236 CFSetRemoveValue(sessions, sessionPrivate);
237 sessions_closed++;
238 pthread_mutex_unlock(&sessions_lock);
239 CFRunLoopWakeUp(main_runLoop);
240
241 return;
242 }
243
244
245 static void
246 __SCHelperSessionInitialize(void)
247 {
248 __kSCHelperSessionTypeID = _CFRuntimeRegisterClass(&__SCHelperSessionClass);
249 return;
250 }
251
252
253 static SCHelperSessionRef
254 __SCHelperSessionCreate(CFAllocatorRef allocator)
255 {
256 SCHelperSessionPrivateRef sessionPrivate;
257 uint32_t size;
258
259 /* initialize runtime */
260 pthread_once(&initialized, __SCHelperSessionInitialize);
261
262 /* allocate session */
263 size = sizeof(SCHelperSessionPrivate) - sizeof(CFRuntimeBase);
264 sessionPrivate = (SCHelperSessionPrivateRef)_CFRuntimeCreateInstance(allocator,
265 __kSCHelperSessionTypeID,
266 size,
267 NULL);
268 if (sessionPrivate == NULL) {
269 return NULL;
270 }
271
272 sessionPrivate->authorization = NULL;
273 #if TARGET_OS_IPHONE
274 sessionPrivate->peer_euid = 0;
275 sessionPrivate->peer_egid = 0;
276 #endif // TARGET_OS_IPHONE
277 sessionPrivate->prefs = NULL;
278
279 // keep track this session
280 pthread_mutex_lock(&sessions_lock);
281 if (sessions == NULL) {
282 sessions = CFSetCreateMutable(NULL, 0, NULL); // create a non-retaining set
283 }
284 CFSetAddValue(sessions, sessionPrivate);
285 pthread_mutex_unlock(&sessions_lock);
286
287 return (SCHelperSessionRef)sessionPrivate;
288 }
289
290
291 #pragma mark -
292 #pragma mark Helpers
293
294
295 /*
296 * EXIT
297 * (in) data = N/A
298 * (out) status = SCError()
299 * (out) reply = N/A
300 */
301 static Boolean
302 do_Exit(SCHelperSessionRef session, void *info, CFDataRef data, uint32_t *status, CFDataRef *reply)
303 {
304 *status = -1;
305 return FALSE;
306 }
307
308
309 /*
310 * AUTHORIZE
311 * (in) data = AuthorizationExternalForm
312 * (out) status = OSStatus
313 * (out) reply = N/A
314 */
315 static Boolean
316 do_Auth(SCHelperSessionRef session, void *info, CFDataRef data, uint32_t *status, CFDataRef *reply)
317 {
318 Boolean ok;
319
320 #if !TARGET_OS_IPHONE
321
322 ok = __SCHelperSessionSetAuthorization(session, data);
323
324 #else //!TARGET_OS_IPHONE
325
326 CFStringRef authorizationInfo = NULL;
327
328 if ((data != NULL) && !_SCUnserializeString(&authorizationInfo, data, NULL, 0)) {
329 return FALSE;
330 }
331
332 if (!isA_CFString(authorizationInfo)) {
333 if (authorizationInfo != NULL) CFRelease(authorizationInfo);
334 return FALSE;
335 }
336
337 ok = __SCHelperSessionSetAuthorization(session, authorizationInfo);
338 if (authorizationInfo != NULL) CFRelease(authorizationInfo);
339
340 #endif // !TARGET_OS_IPHONE
341
342 *status = ok ? 0 : 1;
343 return TRUE;
344 }
345
346
347 #if !TARGET_OS_IPHONE
348
349
350 /*
351 * SCHELPER_MSG_KEYCHAIN_COPY
352 * (in) data = unique_id
353 * (out) status = SCError()
354 * (out) reply = password
355 */
356 static Boolean
357 do_keychain_copy(SCHelperSessionRef session, void *info, CFDataRef data, uint32_t *status, CFDataRef *reply)
358 {
359 SCPreferencesRef prefs;
360 CFStringRef unique_id = NULL;
361
362 if ((data != NULL) && !_SCUnserializeString(&unique_id, data, NULL, 0)) {
363 return FALSE;
364 }
365
366 if (!isA_CFString(unique_id)) {
367 return FALSE;
368 }
369
370 prefs = __SCHelperSessionGetPreferences(session);
371 *reply = _SCPreferencesSystemKeychainPasswordItemCopy(prefs, unique_id);
372 CFRelease(unique_id);
373 if (*reply == NULL) {
374 *status = SCError();
375 }
376
377 return TRUE;
378 }
379
380
381 /*
382 * SCHELPER_MSG_KEYCHAIN_EXISTS
383 * (in) data = unique_id
384 * (out) status = SCError()
385 * (out) reply = N/A
386 */
387 static Boolean
388 do_keychain_exists(SCHelperSessionRef session, void *info, CFDataRef data, uint32_t *status, CFDataRef *reply)
389 {
390 Boolean ok;
391 SCPreferencesRef prefs;
392 CFStringRef unique_id = NULL;
393
394 if ((data != NULL) && !_SCUnserializeString(&unique_id, data, NULL, 0)) {
395 return FALSE;
396 }
397
398 if (!isA_CFString(unique_id)) {
399 if (unique_id != NULL) CFRelease(unique_id);
400 return FALSE;
401 }
402
403 prefs = __SCHelperSessionGetPreferences(session);
404 ok = _SCPreferencesSystemKeychainPasswordItemExists(prefs, unique_id);
405 CFRelease(unique_id);
406 if (!ok) {
407 *status = SCError();
408 }
409
410 return TRUE;
411 }
412
413
414 /*
415 * SCHELPER_MSG_KEYCHAIN_REMOVE
416 * (in) data = unique_id
417 * (out) status = SCError()
418 * (out) reply = N/A
419 */
420 static Boolean
421 do_keychain_remove(SCHelperSessionRef session, void *info, CFDataRef data, uint32_t *status, CFDataRef *reply)
422 {
423 Boolean ok;
424 SCPreferencesRef prefs;
425 CFStringRef unique_id = NULL;
426
427 if ((data != NULL) && !_SCUnserializeString(&unique_id, data, NULL, 0)) {
428 return FALSE;
429 }
430
431 if (!isA_CFString(unique_id)) {
432 if (unique_id != NULL) CFRelease(unique_id);
433 return FALSE;
434 }
435
436 prefs = __SCHelperSessionGetPreferences(session);
437 ok = _SCPreferencesSystemKeychainPasswordItemRemove(prefs, unique_id);
438 CFRelease(unique_id);
439 if (!ok) {
440 *status = SCError();
441 }
442
443 return TRUE;
444 }
445
446
447 /*
448 * SCHELPER_MSG_KEYCHAIN_SET
449 * (in) data = options dictionary
450 * (out) status = SCError()
451 * (out) reply = N/A
452 */
453 static Boolean
454 do_keychain_set(SCHelperSessionRef session, void *info, CFDataRef data, uint32_t *status, CFDataRef *reply)
455 {
456 CFStringRef account;
457 CFStringRef description;
458 CFArrayRef executablePaths = NULL;
459 CFStringRef label;
460 Boolean ok;
461 CFDictionaryRef options = NULL;
462 CFDataRef password;
463 SCPreferencesRef prefs;
464 CFStringRef unique_id;
465
466 if ((data != NULL) && !_SCUnserialize((CFPropertyListRef *)&options, data, NULL, 0)) {
467 return FALSE;
468 }
469
470 if (!isA_CFDictionary(options)) {
471 if (options != NULL) CFRelease(options);
472 return FALSE;
473 }
474
475 if (CFDictionaryGetValueIfPresent(options,
476 kSCKeychainOptionsAllowedExecutables,
477 (const void **)&executablePaths)) {
478 CFMutableArrayRef executableURLs;
479 CFIndex i;
480 CFIndex n;
481 CFMutableDictionaryRef newOptions;
482
483 executableURLs = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
484 n = CFArrayGetCount(executablePaths);
485 for (i = 0; i < n; i++) {
486 CFDataRef path;
487 CFURLRef url;
488
489 path = CFArrayGetValueAtIndex(executablePaths, i);
490 url = CFURLCreateFromFileSystemRepresentation(NULL,
491 CFDataGetBytePtr(path),
492 CFDataGetLength(path),
493 FALSE);
494 if (url != NULL) {
495 CFArrayAppendValue(executableURLs, url);
496 CFRelease(url);
497 }
498 }
499
500 newOptions = CFDictionaryCreateMutableCopy(NULL, 0, options);
501 CFDictionarySetValue(newOptions, kSCKeychainOptionsAllowedExecutables, executableURLs);
502 CFRelease(executableURLs);
503
504 CFRelease(options);
505 options = newOptions;
506 }
507
508 unique_id = CFDictionaryGetValue(options, kSCKeychainOptionsUniqueID);
509 label = CFDictionaryGetValue(options, kSCKeychainOptionsLabel);
510 description = CFDictionaryGetValue(options, kSCKeychainOptionsDescription);
511 account = CFDictionaryGetValue(options, kSCKeychainOptionsAccount);
512 password = CFDictionaryGetValue(options, kSCKeychainOptionsPassword);
513
514 prefs = __SCHelperSessionGetPreferences(session);
515 ok = _SCPreferencesSystemKeychainPasswordItemSet(prefs,
516 unique_id,
517 label,
518 description,
519 account,
520 password,
521 options);
522 CFRelease(options);
523 if (!ok) {
524 *status = SCError();
525 }
526
527 return TRUE;
528 }
529
530
531 #endif // !TARGET_OS_IPHONE
532
533
534 /*
535 * SCHELPER_MSG_INTERFACE_REFRESH
536 * (in) data = ifName
537 * (out) status = SCError()
538 * (out) reply = N/A
539 */
540 static Boolean
541 do_interface_refresh(SCHelperSessionRef session, void *info, CFDataRef data, uint32_t *status, CFDataRef *reply)
542 {
543 CFStringRef ifName = NULL;
544 Boolean ok;
545
546 if ((data != NULL) && !_SCUnserializeString(&ifName, data, NULL, 0)) {
547 SCLog(TRUE, LOG_ERR, CFSTR("interface name not valid"));
548 return FALSE;
549 }
550
551 if (!isA_CFString(ifName)) {
552 SCLog(TRUE, LOG_ERR, CFSTR("interface name not valid"));
553 if (ifName != NULL) CFRelease(ifName);
554 return FALSE;
555 }
556
557 ok = _SCNetworkInterfaceForceConfigurationRefresh(ifName);
558 CFRelease(ifName);
559 if (!ok) {
560 *status = SCError();
561 }
562
563 return TRUE;
564 }
565
566
567 /*
568 * OPEN
569 * (in) data = prefsID
570 * (out) status = SCError()
571 * (out) reply = N/A
572 */
573 static Boolean
574 do_prefs_Open(SCHelperSessionRef session, void *info, CFDataRef data, uint32_t *status, CFDataRef *reply)
575 {
576 CFStringRef name;
577 CFNumberRef pid;
578 SCPreferencesRef prefs = __SCHelperSessionGetPreferences(session);
579 CFDictionaryRef prefsInfo = NULL;
580 CFStringRef prefsID;
581 CFStringRef prefsName;
582
583 if (prefs != NULL) {
584 return FALSE;
585 }
586
587 if ((data != NULL) && !_SCUnserialize((CFPropertyListRef *)&prefsInfo, data, NULL, 0)) {
588 SCLog(TRUE, LOG_ERR, CFSTR("data not valid, %@"), data);
589 return FALSE;
590 }
591
592 if ((prefsInfo == NULL) || !isA_CFDictionary(prefsInfo)) {
593 SCLog(TRUE, LOG_ERR, CFSTR("info not valid"));
594 if (prefsInfo != NULL) CFRelease(prefsInfo);
595 return FALSE;
596 }
597
598 // get [optional] prefsID
599 prefsID = CFDictionaryGetValue(prefsInfo, CFSTR("prefsID"));
600 prefsID = isA_CFString(prefsID);
601 if (prefsID != NULL) {
602 if (CFStringHasPrefix(prefsID, CFSTR("/")) ||
603 CFStringHasPrefix(prefsID, CFSTR("../")) ||
604 CFStringHasSuffix(prefsID, CFSTR("/..")) ||
605 (CFStringFind(prefsID, CFSTR("/../"), 0).location != kCFNotFound)) {
606 // if we're trying to escape from the preferences directory
607 SCLog(TRUE, LOG_ERR, CFSTR("prefsID (%@) not valid"), prefsID);
608 CFRelease(prefsInfo);
609 *status = kSCStatusInvalidArgument;
610 return TRUE;
611 }
612 }
613
614 // get preferences session "name"
615 name = CFDictionaryGetValue(prefsInfo, CFSTR("name"));
616 if (!isA_CFString(name)) {
617 SCLog(TRUE, LOG_ERR, CFSTR("session \"name\" not valid"));
618 CFRelease(prefsInfo);
619 return FALSE;
620 }
621
622 // get PID of caller
623 pid = CFDictionaryGetValue(prefsInfo, CFSTR("PID"));
624 if (!isA_CFNumber(pid)) {
625 SCLog(TRUE, LOG_ERR, CFSTR("PID not valid"));
626 CFRelease(prefsInfo);
627 return FALSE;
628 }
629
630 // build [helper] preferences "name" (used for debugging) and estabish
631 // a preferences session.
632 prefsName = CFStringCreateWithFormat(NULL, NULL, CFSTR("%@:%@"), pid, name);
633 prefs = SCPreferencesCreate(NULL, prefsName, prefsID);
634 CFRelease(prefsName);
635 CFRelease(prefsInfo);
636
637 __SCHelperSessionSetPreferences(session, prefs);
638 if (prefs != NULL) {
639 CFRelease(prefs);
640 } else {
641 *status = SCError();
642 }
643
644 return TRUE;
645 }
646
647
648 /*
649 * ACCESS
650 * (in) data = N/A
651 * (out) status = SCError()
652 * (out) reply = current signature + current preferences
653 */
654 static Boolean
655 do_prefs_Access(SCHelperSessionRef session, void *info, CFDataRef data, uint32_t *status, CFDataRef *reply)
656 {
657 Boolean ok;
658 SCPreferencesRef prefs = __SCHelperSessionGetPreferences(session);
659 CFDataRef signature;
660
661 if (prefs == NULL) {
662 return FALSE;
663 }
664
665 signature = SCPreferencesGetSignature(prefs);
666 if (signature != NULL) {
667 const void * dictKeys[2];
668 const void * dictVals[2];
669 SCPreferencesPrivateRef prefsPrivate = (SCPreferencesPrivateRef)prefs;
670 CFDictionaryRef replyDict;
671
672 dictKeys[0] = CFSTR("signature");
673 dictVals[0] = signature;
674
675 dictKeys[1] = CFSTR("preferences");
676 dictVals[1] = prefsPrivate->prefs;
677
678 replyDict = CFDictionaryCreate(NULL,
679 (const void **)&dictKeys,
680 (const void **)&dictVals,
681 sizeof(dictKeys)/sizeof(dictKeys[0]),
682 &kCFTypeDictionaryKeyCallBacks,
683 &kCFTypeDictionaryValueCallBacks);
684
685 ok = _SCSerialize(replyDict, reply, NULL, NULL);
686 CFRelease(replyDict);
687 if (!ok) {
688 return FALSE;
689 }
690 } else {
691 *status = SCError();
692 }
693
694 return TRUE;
695 }
696
697
698 /*
699 * LOCK
700 * (in) data = client prefs signature (NULL if check not needed)
701 * (out) status = SCError()
702 * (out) reply = N/A
703 */
704 static Boolean
705 do_prefs_Lock(SCHelperSessionRef session, void *info, CFDataRef data, uint32_t *status, CFDataRef *reply)
706 {
707 CFDataRef clientSignature = (CFDataRef)data;
708 Boolean ok;
709 SCPreferencesRef prefs = __SCHelperSessionGetPreferences(session);
710 Boolean wait = (info == (void *)FALSE) ? FALSE : TRUE;
711
712 if (prefs == NULL) {
713 return FALSE;
714 }
715
716 ok = SCPreferencesLock(prefs, wait);
717 if (!ok) {
718 *status = SCError();
719 return TRUE;
720 }
721
722 if (clientSignature != NULL) {
723 CFDataRef serverSignature;
724
725 serverSignature = SCPreferencesGetSignature(prefs);
726 if (!CFEqual(clientSignature, serverSignature)) {
727 (void)SCPreferencesUnlock(prefs);
728 *status = kSCStatusStale;
729 }
730 }
731
732 return TRUE;
733 }
734
735
736 /*
737 * COMMIT
738 * (in) data = new preferences (NULL if commit w/no changes)
739 * (out) status = SCError()
740 * (out) reply = new signature
741 */
742 static Boolean
743 do_prefs_Commit(SCHelperSessionRef session, void *info, CFDataRef data, uint32_t *status, CFDataRef *reply)
744 {
745 Boolean ok;
746 SCPreferencesRef prefs = __SCHelperSessionGetPreferences(session);
747
748 if (prefs == NULL) {
749 return FALSE;
750 }
751
752 if (data != NULL) {
753 SCPreferencesPrivateRef prefsPrivate = (SCPreferencesPrivateRef)prefs;
754
755 if (prefsPrivate->prefs != NULL) {
756 CFRelease(prefsPrivate->prefs);
757 }
758
759 ok = _SCUnserialize((CFPropertyListRef *)&prefsPrivate->prefs, data, NULL, 0);
760 if (!ok) {
761 return FALSE;
762 }
763
764 prefsPrivate->accessed = TRUE;
765 prefsPrivate->changed = TRUE;
766 }
767
768 ok = SCPreferencesCommitChanges(prefs);
769 if (ok) {
770 *reply = SCPreferencesGetSignature(prefs);
771 CFRetain(*reply);
772 } else {
773 *status = SCError();
774 }
775
776 return TRUE;
777 }
778
779
780 /*
781 * APPLY
782 * (in) data = N/A
783 * (out) status = SCError()
784 * (out) reply = N/A
785 */
786 static Boolean
787 do_prefs_Apply(SCHelperSessionRef session, void *info, CFDataRef data, uint32_t *status, CFDataRef *reply)
788 {
789 Boolean ok;
790 SCPreferencesRef prefs = __SCHelperSessionGetPreferences(session);
791
792 if (prefs == NULL) {
793 return FALSE;
794 }
795
796 ok = SCPreferencesApplyChanges(prefs);
797 if (!ok) {
798 *status = SCError();
799 }
800
801 return TRUE;
802 }
803
804
805 /*
806 * UNLOCK
807 * (in) data = N/A
808 * (out) status = SCError()
809 * (out) reply = N/A
810 */
811 static Boolean
812 do_prefs_Unlock(SCHelperSessionRef session, void *info, CFDataRef data, uint32_t *status, CFDataRef *reply)
813 {
814 Boolean ok;
815 SCPreferencesRef prefs = __SCHelperSessionGetPreferences(session);
816
817 if (prefs == NULL) {
818 return FALSE;
819 }
820
821 ok = SCPreferencesUnlock(prefs);
822 if (!ok) {
823 *status = SCError();
824 }
825
826 return TRUE;
827 }
828
829
830 /*
831 * CLOSE
832 * (in) data = N/A
833 * (out) status = SCError()
834 * (out) reply = N/A
835 */
836 static Boolean
837 do_prefs_Close(SCHelperSessionRef session, void *info, CFDataRef data, uint32_t *status, CFDataRef *reply)
838 {
839 SCPreferencesRef prefs = __SCHelperSessionGetPreferences(session);
840
841 if (prefs == NULL) {
842 return FALSE;
843 }
844
845 __SCHelperSessionSetPreferences(session, NULL);
846 *status = -1;
847 return TRUE;
848 }
849
850
851 /*
852 * SYNCHRONIZE
853 * (in) data = N/A
854 * (out) status = kSCStatusOK
855 * (out) reply = N/A
856 */
857 static Boolean
858 do_prefs_Synchronize(SCHelperSessionRef session, void *info, CFDataRef data, uint32_t *status, CFDataRef *reply)
859 {
860 SCPreferencesRef prefs = __SCHelperSessionGetPreferences(session);
861
862 if (prefs == NULL) {
863 return FALSE;
864 }
865
866 SCPreferencesSynchronize(prefs);
867 *status = kSCStatusOK;
868 return TRUE;
869 }
870
871
872 #pragma mark -
873 #pragma mark Process commands
874
875
876 static Boolean
877 hasAuthorization(SCHelperSessionRef session)
878 {
879 AuthorizationRef authorization = __SCHelperSessionGetAuthorization(session);
880
881 #if !TARGET_OS_IPHONE
882 AuthorizationFlags flags;
883 AuthorizationItem items[1];
884 AuthorizationRights rights;
885 OSStatus status;
886
887 if (authorization == NULL) {
888 return FALSE;
889 }
890
891 items[0].name = "system.preferences";
892 items[0].value = NULL;
893 items[0].valueLength = 0;
894 items[0].flags = 0;
895
896 rights.count = sizeof(items) / sizeof(items[0]);
897 rights.items = items;
898
899 flags = kAuthorizationFlagDefaults;
900 flags |= kAuthorizationFlagExtendRights;
901 flags |= kAuthorizationFlagInteractionAllowed;
902 // flags |= kAuthorizationFlagPartialRights;
903 // flags |= kAuthorizationFlagPreAuthorize;
904
905 status = AuthorizationCopyRights(authorization,
906 &rights,
907 kAuthorizationEmptyEnvironment,
908 flags,
909 NULL);
910 if (status != errAuthorizationSuccess) {
911 return FALSE;
912 }
913 #else // !TARGET_OS_IPHONE
914 uid_t peer_euid;
915 gid_t peer_egid;
916
917 if (authorization == NULL) {
918 return FALSE;
919 }
920
921 __SCHelperSessionGetCredentials(session, &peer_euid, &peer_egid);
922 if ((peer_euid != 0) && (peer_egid != 0)) {
923 static gid_t mobile_gid = -1;
924
925 /*
926 * if peer is not user "root" nor group "wheel" then
927 * we check to see if we are one of the authorized
928 * callers.
929 */
930 if (mobile_gid == -1) {
931 char buffer[1024];
932 struct group grp;
933 struct group *grpP;
934
935 if (getgrnam_r("mobile", &grp, buffer, sizeof(buffer), &grpP) == 0) {
936 mobile_gid = grpP->gr_gid;
937 }
938 }
939
940 if (peer_egid != mobile_gid) {
941 return FALSE;
942 }
943 }
944 #endif // !TARGET_OS_IPHONE
945
946 // if (items[0].flags != 0) SCLog(TRUE, LOG_DEBUG, CFSTR("***** success w/flags (%u) != 0"), items[0].flags);
947 return TRUE;
948 }
949
950
951 typedef Boolean (*helperFunction) (SCHelperSessionRef session,
952 void *info,
953 CFDataRef data,
954 uint32_t *status,
955 CFDataRef *reply);
956
957
958 static const struct helper {
959 int command;
960 const char *commandName;
961 Boolean needsAuthorization;
962 helperFunction func;
963 void *info;
964 } helpers[] = {
965 { SCHELPER_MSG_AUTH, "AUTH", FALSE, do_Auth , NULL },
966
967 { SCHELPER_MSG_PREFS_OPEN, "PREFS open", FALSE, do_prefs_Open , NULL },
968 { SCHELPER_MSG_PREFS_ACCESS, "PREFS access", TRUE, do_prefs_Access , NULL },
969 { SCHELPER_MSG_PREFS_LOCK, "PREFS lock", TRUE, do_prefs_Lock , (void *)FALSE },
970 { SCHELPER_MSG_PREFS_LOCKWAIT, "PREFS lock/wait", TRUE, do_prefs_Lock , (void *)TRUE },
971 { SCHELPER_MSG_PREFS_COMMIT, "PREFS commit", TRUE, do_prefs_Commit , NULL },
972 { SCHELPER_MSG_PREFS_APPLY, "PREFS apply", TRUE, do_prefs_Apply , NULL },
973 { SCHELPER_MSG_PREFS_UNLOCK, "PREFS unlock", FALSE, do_prefs_Unlock , NULL },
974 { SCHELPER_MSG_PREFS_CLOSE, "PREFS close", FALSE, do_prefs_Close , NULL },
975 { SCHELPER_MSG_PREFS_SYNCHRONIZE, "PREFS synchronize", FALSE, do_prefs_Synchronize , NULL },
976
977 { SCHELPER_MSG_INTERFACE_REFRESH, "INTERFACE refresh", TRUE, do_interface_refresh , NULL },
978
979 #if !TARGET_OS_IPHONE
980 { SCHELPER_MSG_KEYCHAIN_COPY, "KEYCHAIN copy", TRUE, do_keychain_copy , NULL },
981 { SCHELPER_MSG_KEYCHAIN_EXISTS, "KEYCHAIN exists", TRUE, do_keychain_exists , NULL },
982 { SCHELPER_MSG_KEYCHAIN_REMOVE, "KEYCHAIN remove", TRUE, do_keychain_remove , NULL },
983 { SCHELPER_MSG_KEYCHAIN_SET, "KEYCHAIN set", TRUE, do_keychain_set , NULL },
984 #endif // !TARGET_OS_IPHONE
985
986 { SCHELPER_MSG_EXIT, "EXIT", FALSE, do_Exit , NULL }
987 };
988 #define nHELPERS (sizeof(helpers)/sizeof(struct helper))
989
990
991 static int
992 findHelper(uint32_t command)
993 {
994 int i;
995
996 for (i = 0; i < (int)nHELPERS; i++) {
997 if (helpers[i].command == command) {
998 return i;
999 }
1000 }
1001
1002 return -1;
1003 }
1004
1005
1006 static Boolean
1007 process_command(SCHelperSessionRef session, int fd, int *err)
1008 {
1009 uint32_t command = 0;
1010 CFDataRef data = NULL;
1011 int i;
1012 Boolean ok = FALSE;
1013 CFDataRef reply = NULL;
1014 uint32_t status = kSCStatusOK;
1015
1016 if (!__SCHelper_rxMessage(fd, &command, &data)) {
1017 SCLog(TRUE, LOG_ERR, CFSTR("no command"));
1018 *err = EIO;
1019 goto done;
1020 }
1021
1022 i = findHelper(command);
1023 if (i == -1) {
1024 SCLog(TRUE, LOG_ERR, CFSTR("received unknown command : %u"), command);
1025 *err = EINVAL;
1026 goto done;
1027 }
1028
1029 SCLog(debug, LOG_DEBUG,
1030 CFSTR("processing command \"%s\"%s"),
1031 helpers[i].commandName,
1032 (data != NULL) ? " w/data" : "");
1033
1034 if (helpers[i].needsAuthorization && !hasAuthorization(session)) {
1035 SCLog(debug, LOG_DEBUG,
1036 CFSTR("command \"%s\" : not authorized"),
1037 helpers[i].commandName);
1038 status = kSCStatusAccessError;
1039 }
1040
1041 if (status == kSCStatusOK) {
1042 ok = (*helpers[i].func)(session, helpers[i].info, data, &status, &reply);
1043 }
1044
1045 if ((status != -1) || (reply != NULL)) {
1046 SCLog(debug, LOG_DEBUG,
1047 CFSTR("sending status %u%s"),
1048 status,
1049 (reply != NULL) ? " w/reply" : "");
1050
1051 if (!__SCHelper_txMessage(fd, status, reply)) {
1052 *err = EIO;
1053 ok = FALSE;
1054 goto done;
1055 }
1056 }
1057
1058 done :
1059
1060 if (data != NULL) {
1061 CFRelease(data);
1062 }
1063
1064 if (reply != NULL) {
1065 CFRelease(reply);
1066 }
1067
1068 return ok;
1069 }
1070
1071
1072 #pragma mark -
1073 #pragma mark Main loop
1074
1075
1076 static void
1077 readCallback(CFSocketRef s,
1078 CFSocketCallBackType callbackType,
1079 CFDataRef address,
1080 const void *data,
1081 void *info)
1082 {
1083 CFSocketNativeHandle fd;
1084 int err = 0;
1085 Boolean ok;
1086 SCHelperSessionRef session = (SCHelperSessionRef)info;
1087
1088 if (callbackType != kCFSocketReadCallBack) {
1089 SCLog(TRUE, LOG_ERR, CFSTR("readCallback w/callbackType = %d"), callbackType);
1090 return;
1091 }
1092
1093 fd = CFSocketGetNative(s);
1094 ok = process_command(session, fd, &err);
1095 if (!ok) {
1096 SCLog(debug, LOG_DEBUG, CFSTR("per-session socket : invalidate fd %d"), fd);
1097 CFSocketInvalidate(s);
1098 }
1099
1100 return;
1101 }
1102
1103
1104 static void *
1105 newHelper(void *arg)
1106 {
1107 CFSocketContext context = { 0, NULL, CFRetain, CFRelease, CFCopyDescription };
1108 CFSocketNativeHandle fd = (CFSocketNativeHandle)(intptr_t)arg;
1109 CFRunLoopSourceRef rls;
1110 SCHelperSessionRef session;
1111 CFSocketRef sock;
1112
1113 #if TARGET_OS_IPHONE
1114 uid_t peer_euid;
1115 gid_t peer_egid;
1116 #endif // TARGET_OS_IPHONE
1117
1118 session = __SCHelperSessionCreate(NULL);
1119 #if TARGET_OS_IPHONE
1120 if (getpeereid(fd, &peer_euid, &peer_egid) == 0) {
1121 __SCHelperSessionSetCredentials(session, peer_euid, peer_egid);
1122 } else {
1123 SCLog(TRUE, LOG_ERR, CFSTR("getpeereid() failed: %s"), strerror(errno));
1124 }
1125 #endif // TARGET_OS_IPHONE
1126
1127 context.info = (void *)session;
1128 sock = CFSocketCreateWithNative(NULL,
1129 fd,
1130 kCFSocketReadCallBack,
1131 readCallback,
1132 &context);
1133 CFRelease(session);
1134
1135 rls = CFSocketCreateRunLoopSource(NULL, sock, 0);
1136 CFRelease(sock);
1137
1138 CFRunLoopAddSource(CFRunLoopGetCurrent(), rls, kCFRunLoopDefaultMode);
1139 CFRelease(rls);
1140
1141 CFRunLoopRun();
1142 return NULL;
1143 }
1144
1145
1146 static void
1147 acceptCallback(CFSocketRef s,
1148 CFSocketCallBackType callbackType,
1149 CFDataRef address,
1150 const void *data,
1151 void *info)
1152 {
1153 CFSocketNativeHandle fd;
1154 pthread_attr_t tattr;
1155 pthread_t tid;
1156 static int yes = 1;
1157
1158 if (callbackType != kCFSocketAcceptCallBack) {
1159 SCLog(TRUE, LOG_ERR, CFSTR("acceptCallback w/callbackType = %d"), callbackType);
1160 return;
1161 }
1162
1163 if ((data == NULL) ||
1164 ((fd = *((CFSocketNativeHandle *)data)) == -1)) {
1165 SCLog(TRUE, LOG_ERR, CFSTR("accept w/no FD"));
1166 return;
1167 }
1168
1169 if (setsockopt(fd, SOL_SOCKET, SO_NOSIGPIPE, (const void *)&yes, sizeof(yes)) == -1) {
1170 SCLog(TRUE, LOG_ERR, CFSTR("setsockopt(SO_NOSIGPIPE) failed: %s"), strerror(errno));
1171 return;
1172 }
1173
1174 // start per-session thread
1175 pthread_attr_init(&tattr);
1176 pthread_attr_setscope(&tattr, PTHREAD_SCOPE_SYSTEM);
1177 pthread_attr_setdetachstate(&tattr, PTHREAD_CREATE_DETACHED);
1178 pthread_attr_setstacksize(&tattr, 96 * 1024); // each thread gets a 96K stack
1179 pthread_create(&tid, &tattr, newHelper, (void *)(intptr_t)fd);
1180 pthread_attr_destroy(&tattr);
1181
1182 return;
1183 }
1184
1185
1186 #include <launch.h>
1187
1188
1189 static const struct option longopts[] = {
1190 { "debug", no_argument, 0, 'd' },
1191 { 0, 0, 0, 0 }
1192 };
1193
1194
1195 int
1196 main(int argc, char **argv)
1197 {
1198 Boolean done = FALSE;
1199 int err = 0;
1200 int i;
1201 launch_data_t l_listeners;
1202 launch_data_t l_msg;
1203 launch_data_t l_reply;
1204 launch_data_t l_sockets;
1205 launch_data_type_t l_type;
1206 int n = 0;
1207 extern int optind;
1208 int opt;
1209 int opti;
1210
1211 openlog("SCHelper", LOG_CONS|LOG_PID, LOG_DAEMON);
1212
1213 // process any arguments
1214 while ((opt = getopt_long(argc, argv, "d", longopts, &opti)) != -1) {
1215 switch(opt) {
1216 case 'd':
1217 debug = TRUE;
1218 break;
1219 case 0 :
1220 // if (strcmp(longopts[opti].name, "debug") == 1) {
1221 // }
1222 break;
1223 case '?':
1224 default :
1225 SCLog(TRUE, LOG_ERR,
1226 CFSTR("ignoring unknown or ambiguous command line option"));
1227 break;
1228 }
1229 }
1230 // argc -= optind;
1231 // argv += optind;
1232
1233 if (geteuid() != 0) {
1234 SCLog(TRUE, LOG_ERR, CFSTR("%s"), strerror(EACCES));
1235 exit(EACCES);
1236 }
1237
1238 main_runLoop = CFRunLoopGetCurrent();
1239
1240 l_msg = launch_data_new_string(LAUNCH_KEY_CHECKIN);
1241 l_reply = launch_msg(l_msg);
1242 launch_data_free(l_msg);
1243 l_type = (l_reply != NULL) ? launch_data_get_type(l_reply) : 0;
1244 if (l_type != LAUNCH_DATA_DICTIONARY) {
1245 SCLog(TRUE, LOG_ERR,
1246 CFSTR("SCHelper: error w/launchd " LAUNCH_KEY_CHECKIN " dictionary (%p, %d)"),
1247 l_reply,
1248 l_type);
1249 err = 1;
1250 goto done;
1251 }
1252
1253 l_sockets = launch_data_dict_lookup(l_reply, LAUNCH_JOBKEY_SOCKETS);
1254 l_type = (l_sockets != NULL) ? launch_data_get_type(l_sockets) : 0;
1255 if (l_type != LAUNCH_DATA_DICTIONARY) {
1256 SCLog(TRUE, LOG_ERR,
1257 CFSTR("SCHelper: error w/" LAUNCH_JOBKEY_SOCKETS " (%p, %d)"),
1258 l_sockets,
1259 l_type);
1260 err = 1;
1261 goto done;
1262 }
1263
1264 l_listeners = launch_data_dict_lookup(l_sockets, "Listeners");
1265 l_type = (l_listeners != NULL) ? launch_data_get_type(l_listeners) : 0;
1266 if (l_type != LAUNCH_DATA_ARRAY) {
1267 SCLog(TRUE, LOG_ERR, CFSTR("SCHelper: error w/Listeners (%p, %d)"),
1268 l_listeners,
1269 l_type);
1270 goto done;
1271 }
1272
1273 n = launch_data_array_get_count(l_listeners);
1274 for (i = 0; i < n; i++) {
1275 CFSocketNativeHandle fd;
1276 launch_data_t l_fd;
1277 CFRunLoopSourceRef rls;
1278 CFSocketRef sock;
1279
1280 l_fd = launch_data_array_get_index(l_listeners, i);
1281 l_type = (l_fd != NULL) ? launch_data_get_type(l_fd) : 0;
1282 if (l_type != LAUNCH_DATA_FD) {
1283 SCLog(TRUE, LOG_ERR, CFSTR("SCHelper: error w/Listeners[%d] (%p, %d)"),
1284 i,
1285 l_fd,
1286 l_type);
1287 err = 1;
1288 goto done;
1289 }
1290
1291 fd = launch_data_get_fd(l_fd);
1292 sock = CFSocketCreateWithNative(NULL,
1293 fd,
1294 kCFSocketAcceptCallBack,
1295 acceptCallback,
1296 NULL);
1297 rls = CFSocketCreateRunLoopSource(NULL, sock, 0);
1298 CFRunLoopAddSource(main_runLoop, rls, kCFRunLoopDefaultMode);
1299 CFRelease(rls);
1300 CFRelease(sock);
1301 }
1302
1303 done :
1304
1305 if (l_reply != NULL) launch_data_free(l_reply);
1306
1307 if ((err != 0) || (n == 0)) {
1308 exit(err);
1309 }
1310
1311 while (!done) {
1312 SInt32 rlStatus;
1313
1314 rlStatus = CFRunLoopRunInMode(kCFRunLoopDefaultMode, 15.0, TRUE);
1315 if (rlStatus == kCFRunLoopRunTimedOut) {
1316 pthread_mutex_lock(&sessions_lock);
1317 done = ((sessions != NULL) &&
1318 (CFSetGetCount(sessions) == 0) &&
1319 (sessions_closed == 0));
1320 sessions_closed = 0;
1321 pthread_mutex_unlock(&sessions_lock);
1322 }
1323 }
1324
1325 exit(EX_OK);
1326 }