]> git.saurik.com Git - apple/configd.git/blob - SCMonitor/monitor.c
configd-1109.101.1.tar.gz
[apple/configd.git] / SCMonitor / monitor.c
1 /*
2 * Copyright (c) 2007-2018, 2020 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 /*
25 * Modification History
26 *
27 * October 24, 2007 Allan Nathanson <ajn@apple.com>
28 * - initial revision
29 */
30
31 #include <stdio.h>
32 #include <stdlib.h>
33 #include <unistd.h>
34 #include <CoreFoundation/CoreFoundation.h>
35
36 #define SC_LOG_HANDLE __log_SCMonitor
37 #define SC_LOG_HANDLE_TYPE static
38 #include <SystemConfiguration/SystemConfiguration.h>
39 #include <SystemConfiguration/SCPrivate.h>
40
41 #include <IOKit/IOKitLib.h>
42 #include <IOKit/IOKitKeysPrivate.h>
43 #include <IOKit/IOMessage.h>
44 #include <CoreServices/CoreServices.h>
45 #include "UserEventAgentInterface.h"
46
47 #define MY_BUNDLE_ID "com.apple.SystemConfiguration.SCMonitor"
48 #define MY_ICON_PATH "/System/Library/PreferencePanes/Network.prefPane/Contents/Resources/Network.icns"
49
50 #define NETWORK_PREF_APP "/System/Library/PreferencePanes/Network.prefPane"
51 #define NETWORK_PREF_CMD "New Interface"
52
53 /*
54 * The following keys/values control the actions taken when a new interface
55 * has been detected and whether we interact with the user.
56 *
57 * The keys/values can be provided globally (in SCMonitor.plugin's Info.plist
58 * file) or per-inteface in the IORegistry.
59 *
60 * For the "New Interface Detected Action" key we define the following [CFString]
61 * values :
62 *
63 * "None" No action, ignore this interface.
64 * "Prompt" Post a "new interface detected" notification to the user.
65 * "Configure" Automatically configure this interface without any
66 * intervention.
67 *
68 * Note: automatic configuration may not be possible if the logged in user
69 * is NOT "root" (eUID==0) or if the authorization right that governs
70 * SCHelper write operations (kSCPreferencesAuthorizationRight_write)
71 * is not currently available.
72 *
73 * An [older] "User Intervention" key is also supported. That CFBoolean
74 * key, if present and TRUE, implies "Configure" configuration of the
75 * interface without intervention.
76 */
77
78 typedef struct {
79 UserEventAgentInterfaceStruct *_UserEventAgentInterface;
80 CFUUIDRef _factoryID;
81 UInt32 _refCount;
82
83 Boolean debug;
84
85 CFStringRef configuration_action;
86
87 CFRunLoopSourceRef monitorRls;
88
89 IONotificationPortRef notifyPort;
90 io_iterator_t notifyIterator;
91 CFMutableArrayRef notifyNodes;
92
93 // interfaces that we already know about
94 CFMutableSetRef interfaces_known;
95
96 // interfaces that should be auto-configured (no user notification)
97 CFMutableArrayRef interfaces_configure;
98
99 // interfaces that require user notification
100 CFMutableArrayRef interfaces_prompt;
101
102 CFUserNotificationRef userNotification;
103 CFRunLoopSourceRef userRls;
104
105 AuthorizationRef authorization;
106 } MyType;
107
108 static CFMutableDictionaryRef notify_to_instance = NULL;
109
110
111 #pragma mark -
112 #pragma mark Logging
113
114
115 /*
116 * Logging
117 */
118 static os_log_t
119 __log_SCMonitor(void)
120 {
121 static os_log_t log = NULL;
122
123 if (log == NULL) {
124 log = os_log_create("com.apple.SystemConfiguration", "SCMonitor");
125 }
126
127 return log;
128 }
129
130
131 #pragma mark -
132 #pragma mark Authorization
133
134
135 static AuthorizationRef
136 getAuthorization(MyType *myInstance)
137 {
138 if (myInstance->authorization == NULL) {
139 AuthorizationFlags flags = kAuthorizationFlagDefaults;
140 OSStatus status;
141
142 status = AuthorizationCreate(NULL,
143 kAuthorizationEmptyEnvironment,
144 flags,
145 &myInstance->authorization);
146 if (status != errAuthorizationSuccess) {
147 SC_log(LOG_ERR, "AuthorizationCreate() failed: status = %d", (int)status);
148 }
149 }
150
151 return myInstance->authorization;
152 }
153
154
155 static Boolean
156 hasAuthorization(MyType *myInstance)
157 {
158 AuthorizationRef authorization;
159 Boolean isAdmin = FALSE;
160
161 authorization = getAuthorization(myInstance);
162 if (authorization != NULL) {
163 AuthorizationFlags flags = kAuthorizationFlagDefaults;
164 AuthorizationItem items[1];
165 AuthorizationRights rights;
166 OSStatus status;
167
168 items[0].name = kSCPreferencesAuthorizationRight_write;
169 items[0].value = NULL;
170 items[0].valueLength = 0;
171 items[0].flags = 0;
172
173 rights.count = sizeof(items) / sizeof(items[0]);
174 rights.items = items;
175
176 status = AuthorizationCopyRights(authorization,
177 &rights,
178 kAuthorizationEmptyEnvironment,
179 flags,
180 NULL);
181 isAdmin = (status == errAuthorizationSuccess);
182 }
183
184 return isAdmin;
185 }
186
187
188 static void
189 freeAuthorization(MyType *myInstance)
190 {
191 if (myInstance->authorization != NULL) {
192 AuthorizationFree(myInstance->authorization, kAuthorizationFlagDefaults);
193 // AuthorizationFree(myInstance->authorization, kAuthorizationFlagDestroyRights);
194 myInstance->authorization = NULL;
195 }
196
197 return;
198 }
199
200
201 #pragma mark -
202 #pragma mark New interface notification / configuration
203
204
205 static void
206 open_NetworkPrefPane(MyType *myInstance)
207 {
208 #pragma unused(myInstance)
209 AEDesc aeDesc = { typeNull, NULL };
210 CFArrayRef prefArray;
211 CFURLRef prefURL;
212 LSLaunchURLSpec prefSpec;
213 OSStatus status;
214
215 prefURL = CFURLCreateWithFileSystemPath(kCFAllocatorDefault,
216 CFSTR(NETWORK_PREF_APP),
217 kCFURLPOSIXPathStyle,
218 FALSE);
219 prefArray = CFArrayCreate(NULL, (const void **)&prefURL, 1, &kCFTypeArrayCallBacks);
220 CFRelease(prefURL);
221
222 status = AECreateDesc('ptru',
223 (const void *)NETWORK_PREF_CMD,
224 strlen(NETWORK_PREF_CMD),
225 &aeDesc);
226 if (status != noErr) {
227 SC_log(LOG_ERR, "AECreateDesc() failed: %d", (int)status);
228 }
229
230 prefSpec.appURL = NULL;
231 prefSpec.itemURLs = prefArray;
232 prefSpec.passThruParams = &aeDesc;
233 prefSpec.launchFlags = kLSLaunchAsync | kLSLaunchDontAddToRecents;
234 prefSpec.asyncRefCon = NULL;
235
236 status = LSOpenFromURLSpec(&prefSpec, NULL);
237 if (status != noErr) {
238 SC_log(LOG_ERR, "LSOpenFromURLSpec() failed: %d", (int)status);
239 }
240
241 CFRelease(prefArray);
242 if (aeDesc.descriptorType != typeNull) AEDisposeDesc(&aeDesc);
243 return;
244 }
245
246
247 static void
248 notify_remove(MyType *myInstance, Boolean cancel)
249 {
250 if (myInstance->interfaces_configure != NULL) {
251 CFRelease(myInstance->interfaces_configure);
252 myInstance->interfaces_configure = NULL;
253 }
254
255 if (myInstance->interfaces_prompt != NULL) {
256 CFRelease(myInstance->interfaces_prompt);
257 myInstance->interfaces_prompt = NULL;
258 }
259
260 if (myInstance->userRls != NULL) {
261 CFRunLoopSourceInvalidate(myInstance->userRls);
262 CFRelease(myInstance->userRls);
263 myInstance->userRls = NULL;
264 }
265
266 if (myInstance->userNotification != NULL) {
267 if (cancel) {
268 SInt32 status;
269
270 status = CFUserNotificationCancel(myInstance->userNotification);
271 if (status != 0) {
272 SC_log(LOG_ERR,
273 "CFUserNotificationCancel() failed, status=%d",
274 (int)status);
275 }
276 }
277 CFRelease(myInstance->userNotification);
278 myInstance->userNotification = NULL;
279 }
280
281 return;
282 }
283
284
285 static void
286 notify_reply(CFUserNotificationRef userNotification, CFOptionFlags response_flags)
287 {
288 MyType *myInstance = NULL;
289
290 // get instance for notification
291 if (notify_to_instance != NULL) {
292 myInstance = (MyType *)CFDictionaryGetValue(notify_to_instance, userNotification);
293 if (myInstance != NULL) {
294 CFDictionaryRemoveValue(notify_to_instance, userNotification);
295 if (CFDictionaryGetCount(notify_to_instance) == 0) {
296 CFRelease(notify_to_instance);
297 notify_to_instance = NULL;
298 }
299 }
300 }
301 if (myInstance == NULL) {
302 SC_log(LOG_ERR, "can't find user notification");
303 return;
304 }
305
306 // process response
307 switch (response_flags & 0x3) {
308 case kCFUserNotificationDefaultResponse:
309 // user asked to configure interface
310 open_NetworkPrefPane(myInstance);
311 break;
312 default:
313 // user cancelled
314 break;
315 }
316
317 notify_remove(myInstance, FALSE);
318 return;
319 }
320
321
322 static void
323 notify_add(MyType *myInstance)
324 {
325 CFBundleRef bundle;
326 CFMutableDictionaryRef dict = NULL;
327 SInt32 error = 0;
328 CFIndex i;
329 CFIndex n = CFArrayGetCount(myInstance->interfaces_prompt);
330 CFURLRef url = NULL;
331
332 if (myInstance->userNotification != NULL) {
333 CFMutableArrayRef save = NULL;
334
335 if (n > 0) {
336 CFRetain(myInstance->interfaces_prompt);
337 save = myInstance->interfaces_prompt;
338 }
339 notify_remove(myInstance, TRUE);
340 myInstance->interfaces_prompt = save;
341 if (n == 0) {
342 return;
343 }
344 }
345
346 dict = CFDictionaryCreateMutable(NULL,
347 0,
348 &kCFTypeDictionaryKeyCallBacks,
349 &kCFTypeDictionaryValueCallBacks);
350
351 // set localization URL
352 bundle = CFBundleGetBundleWithIdentifier(CFSTR(MY_BUNDLE_ID));
353 if (bundle != NULL) {
354 url = CFBundleCopyBundleURL(bundle);
355 }
356 #ifdef MAIN
357 if (url == NULL) {
358 url = CFURLCreateFromFileSystemRepresentation(NULL,
359 (const UInt8 *)"/System/Library/UserEventPlugins/SCMonitor.plugin",
360 strlen("/System/Library/UserEventPlugins/SCMonitor.plugin"),
361 FALSE);
362 if (bundle == NULL) {
363 bundle = CFBundleCreate(NULL, url);
364 }
365 }
366 #endif // MAIN
367 if (url != NULL) {
368 // set URL
369 CFDictionarySetValue(dict, kCFUserNotificationLocalizationURLKey, url);
370 CFRelease(url);
371 } else {
372 SC_log(LOG_ERR, "can't find bundle");
373 goto done;
374 }
375
376 // set icon URL
377 url = CFURLCreateFromFileSystemRepresentation(NULL,
378 (const UInt8 *)MY_ICON_PATH,
379 strlen(MY_ICON_PATH),
380 FALSE);
381 if (url != NULL) {
382 CFDictionarySetValue(dict, kCFUserNotificationIconURLKey, url);
383 CFRelease(url);
384 }
385
386 // header
387 CFDictionarySetValue(dict,
388 kCFUserNotificationAlertHeaderKey,
389 (n == 1) ? CFSTR("HEADER_1") : CFSTR("HEADER_N"));
390
391 // message
392 if (n == 1) {
393 CFStringRef format;
394 SCNetworkInterfaceRef interface;
395 CFStringRef message;
396 CFStringRef name;
397
398 #define MESSAGE_1 "The “%@” network interface has not been set up. To set up this interface, use Network Preferences."
399
400 format = CFBundleCopyLocalizedString(bundle,
401 CFSTR("MESSAGE_1"),
402 CFSTR(MESSAGE_1),
403 NULL);
404 interface = CFArrayGetValueAtIndex(myInstance->interfaces_prompt, 0);
405 name = SCNetworkInterfaceGetLocalizedDisplayName(interface);
406 message = CFStringCreateWithFormat(NULL, NULL, format, name);
407 CFDictionarySetValue(dict, kCFUserNotificationAlertMessageKey, message);
408 CFRelease(message);
409 CFRelease(format);
410 } else {
411 CFMutableArrayRef message;
412
413 message = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
414 CFArrayAppendValue(message, CFSTR("MESSAGE_SN"));
415 for (i = 0; i < n; i++) {
416 SCNetworkInterfaceRef interface;
417 CFStringRef name;
418 CFStringRef str;
419
420 interface = CFArrayGetValueAtIndex(myInstance->interfaces_prompt, i);
421 name = SCNetworkInterfaceGetLocalizedDisplayName(interface);
422 str = CFStringCreateWithFormat(NULL, NULL, CFSTR("\n\t%@"), name);
423 CFArrayAppendValue(message, str);
424 CFRelease(str);
425 }
426 CFArrayAppendValue(message, CFSTR("MESSAGE_EN"));
427 CFDictionarySetValue(dict, kCFUserNotificationAlertMessageKey, message);
428 CFRelease(message);
429 }
430
431 // button titles
432 CFDictionaryAddValue(dict, kCFUserNotificationDefaultButtonTitleKey, CFSTR("OPEN_NP"));
433 CFDictionaryAddValue(dict, kCFUserNotificationAlternateButtonTitleKey, CFSTR("CANCEL"));
434
435 // create and post notification
436 myInstance->userNotification = CFUserNotificationCreate(NULL,
437 0,
438 kCFUserNotificationNoteAlertLevel,
439 &error,
440 dict);
441 if (myInstance->userNotification == NULL) {
442 SC_log(LOG_ERR, "CFUserNotificationCreate() failed: %d", (int)error);
443 goto done;
444 }
445
446 // establish callback
447 myInstance->userRls = CFUserNotificationCreateRunLoopSource(NULL,
448 myInstance->userNotification,
449 notify_reply,
450 0);
451 if (myInstance->userRls == NULL) {
452 SC_log(LOG_ERR, "CFUserNotificationCreateRunLoopSource() failed");
453 CFRelease(myInstance->userNotification);
454 myInstance->userNotification = NULL;
455 goto done;
456 }
457 CFRunLoopAddSource(CFRunLoopGetCurrent(), myInstance->userRls, kCFRunLoopDefaultMode);
458
459 // add instance for notification
460 if (notify_to_instance == NULL) {
461 notify_to_instance = CFDictionaryCreateMutable(NULL,
462 0,
463 &kCFTypeDictionaryKeyCallBacks,
464 NULL); // no retain/release/... for values
465 }
466 CFDictionarySetValue(notify_to_instance, myInstance->userNotification, myInstance);
467
468 done :
469
470 if (dict != NULL) CFRelease(dict);
471 return;
472 }
473
474
475 static void
476 notify_configure(MyType *myInstance)
477 {
478 CFIndex i;
479 Boolean locked;
480 CFIndex n = CFArrayGetCount(myInstance->interfaces_configure);
481 Boolean ok;
482 SCPreferencesRef prefs = NULL;
483 SCNetworkSetRef set = NULL;
484
485 if (geteuid() == 0) {
486 prefs = SCPreferencesCreate(NULL, CFSTR("SCMonitor"), NULL);
487 } else {
488 AuthorizationRef authorization;
489
490 authorization = getAuthorization(myInstance);
491 if (authorization == NULL) {
492 return;
493 }
494
495 prefs = SCPreferencesCreateWithAuthorization(NULL, CFSTR("SCMonitor"), NULL, authorization);
496 }
497
498 locked = SCPreferencesLock(prefs, TRUE);
499 if (!locked) {
500 SC_log(LOG_ERR,
501 "SCPreferencesLock() failed: %s",
502 SCErrorString(SCError()));
503 goto done;
504 }
505
506 set = SCNetworkSetCopyCurrent(prefs);
507 if (set == NULL) {
508 // if no "current" set, create new/default ("Automatic") set
509 set = _SCNetworkSetCreateDefault(prefs);
510 if (set == NULL) {
511 goto done;
512 }
513 SC_log(LOG_DEBUG, "added new \"default\" set");
514 }
515
516 for (i = 0; i < n; i++) {
517 SCNetworkInterfaceRef interface;
518
519 interface = CFArrayGetValueAtIndex(myInstance->interfaces_configure, i);
520 ok = SCNetworkSetEstablishDefaultInterfaceConfiguration(set, interface);
521 if (ok) {
522 CFStringRef name;
523
524 name = SCNetworkInterfaceGetLocalizedDisplayName(interface);
525 SC_log(LOG_NOTICE, "add/update service for %@", name);
526 }
527 }
528
529 ok = SCPreferencesCommitChanges(prefs);
530 if (!ok) {
531 SC_log(LOG_ERR,
532 "SCPreferencesCommitChanges() failed: %s",
533 SCErrorString(SCError()));
534 goto done;
535 }
536
537 ok = SCPreferencesApplyChanges(prefs);
538 if (!ok) {
539 SC_log(LOG_ERR,
540 "SCPreferencesApplyChanges() failed: %s",
541 SCErrorString(SCError()));
542 goto done;
543 }
544
545 done :
546
547 if (set != NULL) {
548 CFRelease(set);
549 set = NULL;
550 }
551
552 if (locked) {
553 SCPreferencesUnlock(prefs);
554 }
555
556 CFRelease(prefs);
557 prefs = NULL;
558
559 CFRelease(myInstance->interfaces_configure);
560 myInstance->interfaces_configure = NULL;
561
562 return;
563 }
564
565
566 #pragma mark -
567
568 static Boolean
569 onConsole()
570 {
571 CFArrayRef console_sessions;
572 Boolean on = FALSE;
573 io_registry_entry_t root;
574 uid_t uid = geteuid();
575
576 root = IORegistryGetRootEntry(kIOMasterPortDefault);
577 console_sessions = IORegistryEntryCreateCFProperty(root,
578 CFSTR(kIOConsoleUsersKey),
579 NULL,
580 0);
581 if (console_sessions != NULL) {
582 CFIndex n;
583
584 n = isA_CFArray(console_sessions) ? CFArrayGetCount(console_sessions) : 0;
585 for (CFIndex i = 0; i < n; i++) {
586 CFBooleanRef bVal;
587 CFDictionaryRef session;
588 uint64_t sessionUID;
589 CFNumberRef val;
590
591 session = CFArrayGetValueAtIndex(console_sessions, i);
592 if (!isA_CFDictionary(session)) {
593 // if not dictionary
594 continue;
595 }
596
597 if (!CFDictionaryGetValueIfPresent(session,
598 CFSTR(kIOConsoleSessionUIDKey),
599 (const void **)&val) ||
600 !isA_CFNumber(val) ||
601 !CFNumberGetValue(val, kCFNumberSInt64Type, (void *)&sessionUID) ||
602 (uid != sessionUID)) {
603 // if not my session
604 continue;
605 }
606
607 if (CFDictionaryGetValueIfPresent(session,
608 CFSTR(kIOConsoleSessionOnConsoleKey),
609 (const void **)&bVal) &&
610 isA_CFBoolean(bVal) &&
611 CFBooleanGetValue(bVal)) {
612 // if "on console" session
613 on = TRUE;
614 }
615
616 break;
617 }
618
619 CFRelease(console_sessions);
620 }
621 IOObjectRelease(root);
622
623 return on;
624 }
625
626
627 #pragma mark -
628
629
630 // configure ONLY IF authorized
631 #define kSCNetworkInterfaceConfigurationActionValueConfigureAuthorized CFSTR("Configure-Authorized")
632
633
634 static void
635 updateInterfaceList(MyType *myInstance)
636 {
637 Boolean changed = FALSE;
638 CFIndex i;
639 CFArrayRef interfaces;
640 CFMutableSetRef interfaces_old = NULL;
641 CFIndex n;
642 SCPreferencesRef prefs;
643 SCNetworkSetRef set = NULL;
644
645 if (!onConsole()) {
646 return;
647 }
648
649 prefs = SCPreferencesCreate(NULL, CFSTR("SCMonitor"), NULL);
650 if (prefs == NULL) {
651 return;
652 }
653
654 set = SCNetworkSetCopyCurrent(prefs);
655 if (set == NULL) {
656 // if no "current" set, create new/default ("Automatic") set
657 set = _SCNetworkSetCreateDefault(prefs);
658 if (set == NULL) {
659 goto done;
660 }
661 }
662
663 interfaces_old = CFSetCreateMutableCopy(NULL, 0, myInstance->interfaces_known);
664
665 interfaces = _SCNetworkInterfaceCopyAllWithPreferences(prefs);
666 if (interfaces != NULL) {
667 n = CFArrayGetCount(interfaces);
668 for (i = 0; i < n; i++) {
669 SCNetworkInterfaceRef interface;
670 Boolean ok;
671
672 interface = CFArrayGetValueAtIndex(interfaces, i);
673
674 // track new vs. old (removed) interfaces
675 CFSetRemoveValue(interfaces_old, interface);
676 if (CFSetContainsValue(myInstance->interfaces_known, interface)) {
677 // if we already know about this interface
678 continue;
679 }
680 CFSetAddValue(myInstance->interfaces_known, interface);
681 changed = TRUE;
682
683 ok = SCNetworkSetEstablishDefaultInterfaceConfiguration(set, interface);
684 if (ok) {
685 CFStringRef action;
686
687 // this is a *new* interface
688
689 action = _SCNetworkInterfaceGetConfigurationAction(interface);
690 if (action == NULL) {
691 // if no per-interface action, use [global] default
692 action = myInstance->configuration_action;
693 }
694 if ((action == NULL) ||
695 (!CFEqual(action, kSCNetworkInterfaceConfigurationActionValueNone) &&
696 !CFEqual(action, kSCNetworkInterfaceConfigurationActionValueConfigure))) {
697 if (_SCNetworkInterfaceIsBuiltin(interface)) {
698 // if built-in interface
699 action = kSCNetworkInterfaceConfigurationActionValueConfigureAuthorized;
700 } else {
701 action = kSCNetworkInterfaceConfigurationActionValuePrompt;
702 }
703 }
704
705 if (CFEqual(action, kSCNetworkInterfaceConfigurationActionValueNone)) {
706 continue;
707 } else if (CFEqual(action, kSCNetworkInterfaceConfigurationActionValueConfigure)) {
708 // configure automatically (without user intervention)
709 if (myInstance->interfaces_configure == NULL) {
710 myInstance->interfaces_configure = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
711 }
712 CFArrayAppendValue(myInstance->interfaces_configure, interface);
713 } else if (hasAuthorization(myInstance)) {
714 // if we already have the "admin" (kSCPreferencesAuthorizationRight_write)
715 // right, configure automatically (without user intervention)
716 if (myInstance->interfaces_configure == NULL) {
717 myInstance->interfaces_configure = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
718 }
719 CFArrayAppendValue(myInstance->interfaces_configure, interface);
720 } else if (!CFEqual(action, kSCNetworkInterfaceConfigurationActionValueConfigureAuthorized)) {
721 // notify user
722 if (myInstance->interfaces_prompt == NULL) {
723 myInstance->interfaces_prompt = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
724 }
725 CFArrayAppendValue(myInstance->interfaces_prompt, interface);
726 }
727 }
728 }
729
730 CFRelease(interfaces);
731 }
732
733 // remove any posted notifications for network interfaces that have been removed
734 n = CFSetGetCount(interfaces_old);
735 if (n > 0) {
736 const void * paths_q[32];
737 const void ** paths = paths_q;
738
739 if (n > (CFIndex)(sizeof(paths_q) / sizeof(CFTypeRef)))
740 paths = CFAllocatorAllocate(NULL, n * sizeof(CFTypeRef), 0);
741 CFSetGetValues(interfaces_old, paths);
742 for (i = 0; i < n; i++) {
743 if (myInstance->interfaces_prompt != NULL) {
744 CFIndex j;
745
746 j = CFArrayGetCount(myInstance->interfaces_prompt);
747 while (j > 0) {
748 SCNetworkInterfaceRef interface;
749
750 j--;
751 interface = CFArrayGetValueAtIndex(myInstance->interfaces_prompt, j);
752 if (CFEqual(interface, paths[i])) {
753 // if we have previously posted a notification
754 // for this no-longer-present interface
755 CFArrayRemoveValueAtIndex(myInstance->interfaces_prompt, j);
756 changed = TRUE;
757 }
758 }
759 }
760
761 CFSetRemoveValue(myInstance->interfaces_known, paths[i]);
762 }
763 if (paths != paths_q) CFAllocatorDeallocate(NULL, paths);
764 }
765
766 done :
767
768 if (changed) {
769 if (myInstance->interfaces_configure != NULL) {
770 // if we have network services to configure automatically
771 notify_configure(myInstance);
772 }
773
774 if (myInstance->interfaces_prompt != NULL) {
775 // if we have network services that require user intervention
776 // post notification for new interfaces
777 notify_add(myInstance);
778 }
779 }
780
781 if (interfaces_old != NULL) CFRelease(interfaces_old);
782 if (set != NULL) CFRelease(set);
783 CFRelease(prefs);
784 return;
785 }
786
787
788 #pragma mark -
789 #pragma mark Watch for new [network] interfaces
790
791
792 static void
793 update_lan(SCDynamicStoreRef store, CFArrayRef changes, void * arg)
794 {
795 #pragma unused(store)
796 #pragma unused(changes)
797 MyType *myInstance = (MyType *)arg;
798
799 updateInterfaceList(myInstance);
800 return;
801 }
802
803
804 static void
805 watcher_add_lan(MyType *myInstance)
806 {
807 SCDynamicStoreContext context = { 0, (void *)myInstance, NULL, NULL, NULL };
808 CFStringRef key;
809 CFArrayRef keys;
810 SCDynamicStoreRef store;
811
812 store = SCDynamicStoreCreate(NULL, CFSTR("SCMonitor"), update_lan, &context);
813 if (store == NULL) {
814 SC_log(LOG_ERR,
815 "SCDynamicStoreCreate() failed: %s",
816 SCErrorString(SCError()));
817 return;
818 }
819
820 key = SCDynamicStoreKeyCreateNetworkInterface(NULL, kSCDynamicStoreDomainState);
821
822 // watch for changes to the list of network interfaces
823 keys = CFArrayCreate(NULL, (const void **)&key, 1, &kCFTypeArrayCallBacks);
824 SCDynamicStoreSetNotificationKeys(store, keys, NULL);
825 CFRelease(keys);
826 myInstance->monitorRls = SCDynamicStoreCreateRunLoopSource(NULL, store, 0);
827 CFRunLoopAddSource(CFRunLoopGetCurrent(),
828 myInstance->monitorRls,
829 kCFRunLoopDefaultMode);
830
831 // check if we already have the "admin" (kSCPreferencesAuthorizationRight_write)
832 // right. If so, we can automatically configure (without user intervention) any
833 // "new" network interfaces that are present at login (e.g. a USB ethernet
834 // dongle that was plugged in before login).
835 if (!hasAuthorization(myInstance)) {
836 CFDictionaryRef dict;
837
838 // ... and if we don't have the right then we populate the list of
839 // known interfaces with those already named so that we avoid any
840 // login prompts (that the user might have no choice but to dismiss)
841 dict = SCDynamicStoreCopyValue(store, key);
842 if (dict != NULL) {
843 if (isA_CFDictionary(dict)) {
844 CFIndex i;
845 CFArrayRef interfaces;
846 CFIndex n;
847
848 interfaces = CFDictionaryGetValue(dict, kSCPropNetInterfaces);
849 n = isA_CFArray(interfaces) ? CFArrayGetCount(interfaces) : 0;
850 for (i = 0; i < n; i++) {
851 CFStringRef bsdName;
852
853 bsdName = CFArrayGetValueAtIndex(interfaces, i);
854 if (isA_CFString(bsdName)) {
855 SCNetworkInterfaceRef interface;
856
857 interface = _SCNetworkInterfaceCreateWithBSDName(NULL, bsdName, kIncludeNoVirtualInterfaces);
858 if (interface != NULL) {
859 CFSetAddValue(myInstance->interfaces_known, interface);
860 CFRelease(interface);
861 }
862 }
863 }
864 }
865
866 CFRelease(dict);
867 }
868 }
869
870 CFRelease(key);
871 CFRelease(store);
872 return;
873 }
874
875
876 static void
877 watcher_remove_lan(MyType *myInstance)
878 {
879 if (myInstance->monitorRls != NULL) {
880 CFRunLoopSourceInvalidate(myInstance->monitorRls);
881 CFRelease(myInstance->monitorRls);
882 myInstance->monitorRls = NULL;
883 }
884
885 return;
886 }
887
888
889 #pragma mark -
890
891
892 typedef struct {
893 io_registry_entry_t interface;
894 io_registry_entry_t interface_node;
895 MyType *myInstance;
896 io_object_t notification;
897 } MyNode;
898
899
900 static void
901 add_node_watcher(MyType *myInstance, io_registry_entry_t node, io_registry_entry_t interface);
902
903
904 static void
905 update_node(void *refCon, io_service_t service, natural_t messageType, void *messageArgument)
906 {
907 #pragma unused(messageArgument)
908 CFIndex i;
909 CFDataRef myData = (CFDataRef)refCon;
910 MyType *myInstance;
911 MyNode *myNode;
912
913 /* ALIGN: CF aligns to at least >8 bytes */
914 myNode = (MyNode *)(void *)CFDataGetBytePtr(myData);
915 myInstance = myNode->myInstance;
916
917 switch (messageType) {
918 case kIOMessageServicePropertyChange : {
919 Boolean initializing = FALSE;
920 SCNetworkInterfaceRef interface;
921 CFTypeRef val;
922
923 if (myNode->interface == MACH_PORT_NULL) {
924 // if we are not watching the "Initializing" property
925 return;
926 }
927
928 val = IORegistryEntryCreateCFProperty(service, kSCNetworkInterfaceInitializingKey, NULL, 0);
929 if (val != NULL) {
930 initializing = (isA_CFBoolean(val) && CFBooleanGetValue(val));
931 CFRelease(val);
932 if (initializing) {
933 // if initialization not complete, keep watching
934 return;
935 }
936 }
937
938 // node is ready
939 interface = _SCNetworkInterfaceCreateWithIONetworkInterfaceObject(myNode->interface);
940 if (interface != NULL) {
941 CFRelease(interface);
942
943 // watch interface (to see when/if it's removed)
944 add_node_watcher(myInstance, myNode->interface, MACH_PORT_NULL);
945 }
946 break;
947 }
948
949 case kIOMessageServiceIsTerminated :
950 break;
951
952 default :
953 return;
954 }
955
956 // remove no-longer-needed notification
957 if (myNode->interface != myNode->interface_node) {
958 IOObjectRelease(myNode->interface_node);
959 }
960 if (myNode->interface != MACH_PORT_NULL) {
961 IOObjectRelease(myNode->interface);
962 }
963 IOObjectRelease(myNode->notification);
964 i = CFArrayGetFirstIndexOfValue(myInstance->notifyNodes,
965 CFRangeMake(0, CFArrayGetCount(myInstance->notifyNodes)),
966 myData);
967 if (i != kCFNotFound) {
968 CFArrayRemoveValueAtIndex(myInstance->notifyNodes, i);
969 if (CFArrayGetCount(myInstance->notifyNodes) == 0) {
970 CFRelease(myInstance->notifyNodes);
971 myInstance->notifyNodes = NULL;
972 }
973 }
974
975 updateInterfaceList(myInstance);
976 return;
977 }
978
979
980 static void
981 add_node_watcher(MyType *myInstance, io_registry_entry_t node, io_registry_entry_t interface)
982 {
983 kern_return_t kr;
984 CFMutableDataRef myData;
985 MyNode *myNode;
986
987 // wait for initialization to complete
988 myData = CFDataCreateMutable(NULL, sizeof(MyNode));
989 CFDataSetLength(myData, sizeof(MyNode));
990
991 /* ALIGN: CF aligns to at least >8 bytes */
992 myNode = (MyNode *)(void *)CFDataGetBytePtr(myData);
993
994 memset(myNode, 0, sizeof(MyNode));
995 myNode->interface = interface;
996 if (myNode->interface != MACH_PORT_NULL) {
997 IOObjectRetain(myNode->interface);
998 }
999 myNode->interface_node = (interface == MACH_PORT_NULL) ? node : interface;
1000 if (myNode->interface != myNode->interface_node) {
1001 IOObjectRetain(myNode->interface_node);
1002 }
1003 myNode->myInstance = myInstance;
1004 myNode->notification = MACH_PORT_NULL;
1005
1006 kr = IOServiceAddInterestNotification(myInstance->notifyPort, // IONotificationPortRef
1007 node, // io_service_t
1008 kIOGeneralInterest, // interestType
1009 update_node, // IOServiceInterestCallback
1010 (void *)myData, // refCon
1011 &myNode->notification); // notification
1012 if (kr == KERN_SUCCESS) {
1013 if (myInstance->notifyNodes == NULL) {
1014 myInstance->notifyNodes = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
1015 }
1016 CFArrayAppendValue(myInstance->notifyNodes, myData);
1017 } else {
1018 SC_log(LOG_ERR,
1019 "add_init_watcher IOServiceAddInterestNotification() failed, kr = 0x%x",
1020 kr);
1021 }
1022 CFRelease(myData);
1023 }
1024
1025
1026 static void
1027 add_init_watcher(MyType *myInstance, io_registry_entry_t interface)
1028 {
1029 kern_return_t kr;
1030 io_registry_entry_t node = interface;
1031 CFTypeRef val = NULL;
1032
1033 while (node != MACH_PORT_NULL) {
1034 io_registry_entry_t parent;
1035
1036 val = IORegistryEntryCreateCFProperty(node, kSCNetworkInterfaceHiddenPortKey, NULL, 0);
1037 if (val != NULL) {
1038 CFRelease(val);
1039 val = NULL;
1040 break;
1041 }
1042
1043 val = IORegistryEntryCreateCFProperty(node, kSCNetworkInterfaceInitializingKey, NULL, 0);
1044 if (val != NULL) {
1045 break;
1046 }
1047
1048 parent = MACH_PORT_NULL;
1049 kr = IORegistryEntryGetParentEntry(node, kIOServicePlane, &parent);
1050 switch (kr) {
1051 case kIOReturnSuccess : // if we have a parent node
1052 case kIOReturnNoDevice : // if we have hit the root node
1053 break;
1054 default :
1055 SC_log(LOG_ERR, "add_init_watcher IORegistryEntryGetParentEntry() failed, kr = 0x%x", kr);
1056 break;
1057 }
1058 if (node != interface) {
1059 IOObjectRelease(node);
1060 }
1061 node = parent;
1062 }
1063
1064 if (val != NULL) {
1065 if (isA_CFBoolean(val) && CFBooleanGetValue(val)) {
1066 // watch the "Initializing" node
1067 add_node_watcher(myInstance, node, interface);
1068 }
1069
1070 CFRelease(val);
1071 }
1072
1073 if ((node != MACH_PORT_NULL) && (node != interface)) {
1074 IOObjectRelease(node);
1075 }
1076
1077 return;
1078 }
1079
1080
1081 static void
1082 update_serial(void *refcon, io_iterator_t iter)
1083 {
1084 MyType *myInstance = (MyType *)refcon;
1085 io_registry_entry_t obj;
1086
1087 while ((obj = IOIteratorNext(iter)) != MACH_PORT_NULL) {
1088 SCNetworkInterfaceRef interface;
1089
1090 interface = _SCNetworkInterfaceCreateWithIONetworkInterfaceObject(obj);
1091 if (interface != NULL) {
1092 CFRelease(interface);
1093
1094 // watch interface (to see when/if it's removed)
1095 add_node_watcher(myInstance, obj, MACH_PORT_NULL);
1096 } else {
1097 // check interface, watch if initializing
1098 add_init_watcher(myInstance, obj);
1099 }
1100
1101 IOObjectRelease(obj);
1102 }
1103
1104 return;
1105 }
1106
1107
1108 static void
1109 update_serial_nodes(void *refcon, io_iterator_t iter)
1110 {
1111 MyType *myInstance = (MyType *)refcon;
1112
1113 update_serial(refcon, iter);
1114 updateInterfaceList(myInstance);
1115 }
1116
1117
1118 static void
1119 watcher_add_serial(MyType *myInstance)
1120 {
1121 kern_return_t kr;
1122
1123 myInstance->notifyPort = IONotificationPortCreate(kIOMasterPortDefault);
1124 if (myInstance->notifyPort == NULL) {
1125 SC_log(LOG_ERR, "IONotificationPortCreate failed");
1126 return;
1127 }
1128
1129 // watch for the introduction of new network serial devices
1130 kr = IOServiceAddMatchingNotification(myInstance->notifyPort,
1131 kIOFirstMatchNotification,
1132 IOServiceMatching("IOSerialBSDClient"),
1133 &update_serial_nodes,
1134 (void *)myInstance, // refCon
1135 &myInstance->notifyIterator); // notification
1136 if (kr != KERN_SUCCESS) {
1137 SC_log(LOG_ERR, "SCMonitor : IOServiceAddMatchingNotification returned 0x%x", kr);
1138 return;
1139 }
1140
1141 myInstance->notifyNodes = NULL;
1142
1143 // Get the current list of matches and arm the notification for
1144 // future interface arrivals.
1145 update_serial((void *)myInstance, myInstance->notifyIterator);
1146
1147 if (myInstance->notifyNodes != NULL) {
1148 // if we have any serial nodes, check if we already have the
1149 // "admin" (kSCPreferencesAuthorizationRight_write) right. If
1150 // so, we can automatically configure (without user intervention)
1151 // any "new" network interfaces that are present at login (e.g. a
1152 // USB modem that was plugged in before login).
1153
1154 if (!hasAuthorization(myInstance)) {
1155 CFIndex i;
1156 CFIndex n = CFArrayGetCount(myInstance->notifyNodes);
1157
1158 // ... and if we don't have the right then we populate the list of
1159 // known interfaces with those already named so that we avoid any
1160 // login prompts (that the user might have no choice but to dismiss)
1161
1162 for (i = 0; i < n; i++) {
1163 SCNetworkInterfaceRef interface;
1164 CFDataRef myData;
1165 MyNode *myNode;
1166
1167 myData = CFArrayGetValueAtIndex(myInstance->notifyNodes, i);
1168
1169 /* ALIGN: CF aligns to at least >8 bytes */
1170 myNode = (MyNode *)(void *)CFDataGetBytePtr(myData);
1171
1172 interface = _SCNetworkInterfaceCreateWithIONetworkInterfaceObject(myNode->interface_node);
1173 if (interface != NULL) {
1174 CFSetAddValue(myInstance->interfaces_known, interface);
1175 CFRelease(interface);
1176 }
1177 }
1178 }
1179 }
1180
1181 // and keep watching
1182 CFRunLoopAddSource(CFRunLoopGetCurrent(),
1183 IONotificationPortGetRunLoopSource(myInstance->notifyPort),
1184 kCFRunLoopDefaultMode);
1185 return;
1186 }
1187
1188
1189 static void
1190 watcher_remove_serial(MyType *myInstance)
1191 {
1192 if (myInstance->notifyNodes != NULL) {
1193 CFIndex i;
1194 CFIndex n = CFArrayGetCount(myInstance->notifyNodes);
1195
1196 for (i = 0; i < n; i++) {
1197 CFDataRef myData;
1198 MyNode *myNode;
1199
1200 myData = CFArrayGetValueAtIndex(myInstance->notifyNodes, i);
1201
1202 /* ALIGN: CF aligns to at least >8 bytes */
1203 myNode = (MyNode *)(void *)CFDataGetBytePtr(myData);
1204
1205 if (myNode->interface != myNode->interface_node) {
1206 IOObjectRelease(myNode->interface_node);
1207 }
1208 if (myNode->interface != MACH_PORT_NULL) {
1209 IOObjectRelease(myNode->interface);
1210 }
1211 IOObjectRelease(myNode->notification);
1212 }
1213
1214 CFRelease(myInstance->notifyNodes);
1215 myInstance->notifyNodes = NULL;
1216 }
1217
1218 if (myInstance->notifyIterator != MACH_PORT_NULL) {
1219 IOObjectRelease(myInstance->notifyIterator);
1220 myInstance->notifyIterator = MACH_PORT_NULL;
1221 }
1222
1223 if (myInstance->notifyPort != MACH_PORT_NULL) {
1224 IONotificationPortDestroy(myInstance->notifyPort);
1225 myInstance->notifyPort = NULL;
1226 }
1227
1228 return;
1229 }
1230
1231
1232 #pragma mark -
1233
1234
1235 static void
1236 watcher_add(MyType *myInstance)
1237 {
1238 CFBundleRef bundle;
1239
1240 bundle = CFBundleGetBundleWithIdentifier(CFSTR(MY_BUNDLE_ID));
1241 if (bundle != NULL) {
1242 CFStringRef action;
1243 CFBooleanRef bVal;
1244 CFDictionaryRef info;
1245
1246 info = CFBundleGetInfoDictionary(bundle);
1247
1248 bVal = CFDictionaryGetValue(info, CFSTR("Debug"));
1249 bVal = isA_CFBoolean(bVal);
1250 if (bVal != NULL) {
1251 myInstance->debug = CFBooleanGetValue(bVal);
1252 }
1253
1254 action = CFDictionaryGetValue(info, kSCNetworkInterfaceConfigurationActionKey);
1255 action = isA_CFString(action);
1256 if (action != NULL) {
1257 myInstance->configuration_action = action;
1258 } else {
1259 CFBooleanRef user_intervention;
1260
1261 user_intervention = CFDictionaryGetValue(info, CFSTR("User Intervention"));
1262 if (isA_CFBoolean(user_intervention) && !CFBooleanGetValue(user_intervention)) {
1263 myInstance->configuration_action = kSCNetworkInterfaceConfigurationActionValueConfigure;
1264 }
1265 }
1266 }
1267
1268 // initialize the list of known interfaces
1269 myInstance->interfaces_known = CFSetCreateMutable(NULL, 0, &kCFTypeSetCallBacks);
1270
1271 // add LAN interfaces
1272 watcher_add_lan(myInstance);
1273
1274 // add SERIAL interfaces
1275 watcher_add_serial(myInstance);
1276
1277 // auto-configure (as needed)
1278 updateInterfaceList(myInstance);
1279
1280 return;
1281 }
1282
1283
1284 static void
1285 watcher_remove(MyType *myInstance)
1286 {
1287 watcher_remove_lan(myInstance);
1288 watcher_remove_serial(myInstance);
1289
1290 if (myInstance->interfaces_known != NULL) {
1291 CFRelease(myInstance->interfaces_known);
1292 myInstance->interfaces_known = NULL;
1293 }
1294
1295 return;
1296 }
1297
1298
1299 #pragma mark -
1300 #pragma mark UserEventAgent stubs
1301
1302
1303 static HRESULT
1304 myQueryInterface(void *myInstance, REFIID iid, LPVOID *ppv)
1305 {
1306 CFUUIDRef interfaceID = CFUUIDCreateFromUUIDBytes(NULL, iid);
1307
1308 // Test the requested ID against the valid interfaces.
1309 if (CFEqual(interfaceID, kUserEventAgentInterfaceID)) {
1310 ((MyType *) myInstance)->_UserEventAgentInterface->AddRef(myInstance);
1311 *ppv = myInstance;
1312 CFRelease(interfaceID);
1313 return S_OK;
1314 }
1315
1316 if (CFEqual(interfaceID, IUnknownUUID)) {
1317 ((MyType *) myInstance)->_UserEventAgentInterface->AddRef(myInstance);
1318 *ppv = myInstance;
1319 CFRelease(interfaceID);
1320 return S_OK;
1321 }
1322
1323 // Requested interface unknown, bail with error.
1324 *ppv = NULL;
1325 CFRelease(interfaceID);
1326 return E_NOINTERFACE;
1327 }
1328
1329
1330 static ULONG
1331 myAddRef(void *myInstance)
1332 {
1333 ((MyType *) myInstance)->_refCount++;
1334 return ((MyType *) myInstance)->_refCount;
1335 }
1336
1337
1338 static ULONG
1339 myRelease(void *myInstance)
1340 {
1341 ((MyType *) myInstance)->_refCount--;
1342 if (((MyType *) myInstance)->_refCount == 0) {
1343 CFUUIDRef factoryID = ((MyType *) myInstance)->_factoryID;
1344
1345 if (factoryID != NULL) {
1346 CFPlugInRemoveInstanceForFactory(factoryID);
1347 CFRelease(factoryID);
1348
1349 watcher_remove((MyType *)myInstance);
1350 notify_remove((MyType *)myInstance, TRUE);
1351 freeAuthorization((MyType *)myInstance);
1352 }
1353 free(myInstance);
1354 return 0;
1355 }
1356
1357 return ((MyType *) myInstance)->_refCount;
1358 }
1359
1360
1361 static void
1362 myInstall(void *myInstance)
1363 {
1364 watcher_add((MyType *)myInstance);
1365 return;
1366 }
1367
1368
1369 static UserEventAgentInterfaceStruct UserEventAgentInterfaceFtbl = {
1370 NULL, // Required padding for COM
1371 myQueryInterface, // These three are the required COM functions
1372 myAddRef,
1373 myRelease,
1374 myInstall // Interface implementation
1375 };
1376
1377
1378 void *
1379 UserEventAgentFactory(CFAllocatorRef allocator, CFUUIDRef typeID)
1380 {
1381 #pragma unused(allocator)
1382 MyType *newOne = NULL;
1383
1384 if (CFEqual(typeID, kUserEventAgentTypeID)) {
1385 newOne = (MyType *)malloc(sizeof(MyType));
1386 memset(newOne, 0, sizeof(*newOne));
1387 newOne->_UserEventAgentInterface = &UserEventAgentInterfaceFtbl;
1388 newOne->_factoryID = (CFUUIDRef)CFRetain(kUserEventAgentFactoryID);
1389 CFPlugInAddInstanceForFactory(kUserEventAgentFactoryID);
1390 newOne->_refCount = 1;
1391 }
1392
1393 return newOne;
1394 }
1395
1396
1397 #ifdef MAIN
1398 int
1399 main(int argc, char **argv)
1400 {
1401 MyType *newOne = (MyType *)malloc(sizeof(MyType));
1402
1403 _sc_log = kSCLogDestinationFile;
1404 _sc_verbose = (argc > 1) ? TRUE : FALSE;
1405
1406 memset(newOne, 0, sizeof(*newOne));
1407 myInstall(newOne);
1408 CFRunLoopRun();
1409 exit(0);
1410 return (0);
1411 }
1412 #endif // MAIN