1/* 2 * Copyright (c) 2010-2013 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 * eapol_monitor.c 26 * - EAPOLMonitor user event agent that initiates 802.1X authentication 27 * sessions 28 */ 29 30/* 31 * Modification History 32 * 33 * September 15, 2010 Dieter Siegmund 34 * - created 35 */ 36 37#include <stdlib.h> 38#include <unistd.h> 39#include <stdio.h> 40#include <fcntl.h> 41#include <sys/param.h> 42#include <paths.h> 43#include <pthread.h> 44#include <mach/mach.h> 45#include <sys/socket.h> 46#include <sys/ioctl.h> 47#include <net/if.h> 48#include <net/if_media.h> 49#include <sys/sockio.h> 50#include <SystemConfiguration/SystemConfiguration.h> 51#include <SystemConfiguration/SCPrivate.h> 52#include <SystemConfiguration/SCDPlugin.h> 53#include <CoreFoundation/CFUserNotification.h> 54#include <CoreFoundation/CFData.h> 55#include <CoreFoundation/CFDictionary.h> 56#include <CoreFoundation/CFRunLoop.h> 57#include <notify.h> 58#include "myCFUtil.h" 59#include "EAPOLClientConfiguration.h" 60#include "EAPOLClientConfigurationPrivate.h" 61#include "EAPOLControlTypes.h" 62#include "EAPOLControl.h" 63#include "EAPOLControlPrivate.h" 64#include "EAPOLControlTypesPrivate.h" 65#include "SupplicantTypes.h" 66#include "EAPClientTypes.h" 67#include "EAPLog.h" 68#include "EAPOLControlPrefs.h" 69 70#include "UserEventAgentInterface.h" 71#include <stdio.h> 72#include <stdlib.h> 73 74#define MY_BUNDLE_ID CFSTR("com.apple.UserEventAgent.EAPOLMonitor") 75 76typedef enum { 77 kMonitorStateIdle, 78 kMonitorStatePrompting, 79 kMonitorStateStarted, 80 kMonitorStateIgnorePermanently 81} MonitorState; 82 83struct MonitoredInterface_s; 84typedef struct MonitoredInterface_s * MonitoredInterfaceRef; 85 86#define LIST_HEAD_MonitoredInterfaceHead LIST_HEAD(MonitoredInterfaceHead, MonitoredInterface_s) 87static LIST_HEAD_MonitoredInterfaceHead S_MonitoredInterfaceHead; 88static struct MonitoredInterfaceHead * S_MonitoredInterfaceHead_p = &S_MonitoredInterfaceHead; 89 90#define LIST_ENTRY_MonitoredInterface LIST_ENTRY(MonitoredInterface_s) 91 92struct MonitoredInterface_s { 93 LIST_ENTRY_MonitoredInterface entries; 94 95 char if_name[IFNAMSIZ]; 96 CFStringRef if_name_cf; 97 MonitorState state; 98 CFAbsoluteTime start_time; 99 bool ignore_until_link_status_changes; 100 bool link_active_when_started; 101 bool require_fresh_detection; 102 CFUserNotificationRef cfun; 103 CFRunLoopSourceRef rls; 104 105 /* EAPOLClientConfigurationRef cfg; */ /* XXX might need in future */ 106 CFArrayRef profiles; 107}; 108 109typedef struct { 110 UserEventAgentInterfaceStruct * _UserEventAgentInterface; 111 CFUUIDRef _factoryID; 112 UInt32 _refCount; 113 SCDynamicStoreRef store; 114 CFRunLoopSourceRef rls; 115 116 /* notifications for user settings changing */ 117 struct { 118 CFMachPortRef mp; 119 int token; 120 } settings_change; 121} MyType; 122 123static bool S_auto_connect; 124 125/** 126 ** Utility routines 127 **/ 128 129static bool 130S_on_console(SCDynamicStoreRef store) 131{ 132 bool on_console; 133 uid_t uid; 134 CFStringRef user; 135 136 user = SCDynamicStoreCopyConsoleUser(store, &uid, NULL); 137 if (user == NULL || uid != getuid()) { 138 on_console = FALSE; 139 } 140 else { 141 on_console = TRUE; 142 } 143 my_CFRelease(&user); 144 return (on_console); 145} 146 147static CFBundleRef 148S_get_bundle() 149{ 150 CFBundleRef bundle; 151 152 bundle = CFBundleGetBundleWithIdentifier(MY_BUNDLE_ID); 153 if (bundle == NULL) { 154 bundle = CFBundleGetMainBundle(); 155 } 156 return (bundle); 157} 158 159/* 160 * Function: my_CFStringCopyComponent 161 * Purpose: 162 * Separates the given string using the given separator, and returns 163 * the component at the specified index. 164 * Returns: 165 * NULL if no such component exists, non-NULL component otherwise 166 */ 167static CFStringRef 168my_CFStringCopyComponent(CFStringRef path, CFStringRef separator, 169 CFIndex component_index) 170{ 171 CFArrayRef arr; 172 CFStringRef component = NULL; 173 174 arr = CFStringCreateArrayBySeparatingStrings(NULL, path, separator); 175 if (arr == NULL) { 176 goto done; 177 } 178 if (CFArrayGetCount(arr) <= component_index) { 179 goto done; 180 } 181 component = CFRetain(CFArrayGetValueAtIndex(arr, component_index)); 182 183 done: 184 my_CFRelease(&arr); 185 return (component); 186 187} 188 189static CFStringRef 190copy_localized_if_name(CFStringRef if_name_cf) 191{ 192 int count; 193 int i; 194 CFArrayRef if_list; 195 CFStringRef loc_name = NULL; 196 197 if_list = SCNetworkInterfaceCopyAll(); 198 if (if_list == NULL) { 199 goto done; 200 } 201 count = CFArrayGetCount(if_list); 202 for (i = 0; i < count; i++) { 203 CFStringRef this_name; 204 SCNetworkInterfaceRef sc_if; 205 206 sc_if = (SCNetworkInterfaceRef)CFArrayGetValueAtIndex(if_list, i); 207 this_name = SCNetworkInterfaceGetBSDName(sc_if); 208 if (this_name != NULL 209 && CFEqual(this_name, if_name_cf)) { 210 loc_name = SCNetworkInterfaceGetLocalizedDisplayName(sc_if); 211 if (loc_name != NULL) { 212 CFRetain(loc_name); 213 } 214 break; 215 } 216 } 217 218 done: 219 if (if_list != NULL) { 220 CFRelease(if_list); 221 } 222 return (loc_name); 223} 224 225static bool 226is_link_active(const char * name) 227{ 228 bool active = FALSE; 229 struct ifmediareq ifm; 230 int s; 231 232 s = socket(AF_INET, SOCK_DGRAM, 0); 233 if (s < 0) { 234 perror("socket"); 235 goto done; 236 } 237 bzero(&ifm, sizeof(ifm)); 238 strlcpy(ifm.ifm_name, name, sizeof(ifm.ifm_name)); 239 if (ioctl(s, SIOCGIFMEDIA, (caddr_t)&ifm) < 0) { 240 goto done; 241 } 242 if ((ifm.ifm_status & IFM_AVALID) == 0 243 || (ifm.ifm_status & IFM_ACTIVE) != 0) { 244 active = TRUE; 245 } 246 247 done: 248 if (s >= 0) { 249 close(s); 250 } 251 return (active); 252} 253 254static int 255get_ifm_type(const char * name) 256{ 257 struct ifmediareq ifm; 258 int s; 259 int ifm_type = 0; 260 261 s = socket(AF_INET, SOCK_DGRAM, 0); 262 if (s < 0) { 263 perror("socket"); 264 goto done; 265 } 266 bzero(&ifm, sizeof(ifm)); 267 strlcpy(ifm.ifm_name, name, sizeof(ifm.ifm_name)); 268 if (ioctl(s, SIOCGIFMEDIA, (caddr_t)&ifm) < 0) { 269 goto done; 270 } 271 ifm_type = IFM_TYPE(ifm.ifm_current); 272 done: 273 if (s >= 0) { 274 close(s); 275 } 276 return (ifm_type); 277} 278 279static uint32_t 280S_get_plist_uint32(CFDictionaryRef plist, CFStringRef key, uint32_t def) 281{ 282 CFNumberRef n; 283 uint32_t ret = def; 284 285 n = isA_CFNumber(CFDictionaryGetValue(plist, key)); 286 if (n) { 287 if (CFNumberGetValue(n, kCFNumberSInt32Type, &ret) == FALSE) { 288 ret = def; 289 } 290 } 291 return (ret); 292} 293 294static __inline__ CFOptionFlags 295S_CFUserNotificationResponse(CFOptionFlags flags) 296{ 297 return (flags & 0x3); 298} 299 300#define kNetworkPrefPath "/System/Library/PreferencePanes/Network.prefPane" 301 302static CFURLRef 303copy_icon_url(CFStringRef icon) 304{ 305 CFBundleRef np_bundle; 306 CFURLRef np_url; 307 CFURLRef url = NULL; 308 309 np_url = CFURLCreateWithFileSystemPath(NULL, 310 CFSTR(kNetworkPrefPath), 311 kCFURLPOSIXPathStyle, FALSE); 312 if (np_url != NULL) { 313 np_bundle = CFBundleCreate(NULL, np_url); 314 if (np_bundle != NULL) { 315 url = CFBundleCopyResourceURL(np_bundle, icon, 316 CFSTR("icns"), NULL); 317 if (url == NULL) { 318 url = CFBundleCopyResourceURL(np_bundle, icon, 319 CFSTR("tiff"), NULL); 320 } 321 CFRelease(np_bundle); 322 } 323 CFRelease(np_url); 324 } 325 return (url); 326} 327 328/** 329 ** PromptStartContext 330 **/ 331typedef struct { 332 EAPOLClientConfigurationRef cfg; 333 CFArrayRef profiles; 334 CFArrayRef profile_names; 335} PromptStartContext, * PromptStartContextRef; 336 337static void 338PromptStartContextRelease(PromptStartContextRef context) 339{ 340 my_CFRelease(&context->cfg); 341 my_CFRelease(&context->profiles); 342 my_CFRelease(&context->profile_names); 343 return; 344} 345 346static void 347PromptStartContextInit(PromptStartContextRef context) 348{ 349 context->cfg = EAPOLClientConfigurationCreate(NULL); 350 context->profiles = EAPOLClientConfigurationCopyProfiles(context->cfg); 351 context->profile_names = NULL; 352 if (context->profiles != NULL) { 353 CFBundleRef bundle; 354 int count; 355 int i; 356 CFRange r; 357 CFStringRef use_defaults; 358 CFMutableArrayRef profile_names; 359 360 count = CFArrayGetCount(context->profiles); 361 profile_names = CFArrayCreateMutable(NULL, count + 1, 362 &kCFTypeArrayCallBacks); 363 bundle = S_get_bundle(); 364 use_defaults 365 = CFBundleCopyLocalizedString(bundle, 366 CFSTR("DEFAULT_CONFIGURATION"), 367 NULL, NULL); 368 CFArrayAppendValue(profile_names, use_defaults); 369 CFRelease(use_defaults); 370 r.location = 0; 371 r.length = 1; 372 for (i = 0; i < count; i++) { 373 int instance; 374 CFStringRef name; 375 CFStringRef new_name; 376 EAPOLClientProfileRef profile; 377 378 profile = (EAPOLClientProfileRef) 379 CFArrayGetValueAtIndex(context->profiles, i); 380 name = EAPOLClientProfileGetUserDefinedName(profile); 381 if (name == NULL) { 382 name = EAPOLClientProfileGetID(profile); 383 } 384 for (instance = 2, new_name = CFRetain(name); 385 CFArrayContainsValue(profile_names, r, new_name); instance++) { 386 CFRelease(new_name); 387 new_name 388 = CFStringCreateWithFormat(NULL, NULL, 389 CFSTR("%@ (%d)"), name, 390 instance); 391 392 } 393 CFArrayAppendValue(profile_names, new_name); 394 CFRelease(new_name); 395 r.length++; 396 } 397 context->profile_names = profile_names; 398 } 399 return; 400 401} 402 403/** 404 ** MonitoredInterfaceRef 405 **/ 406 407static MonitoredInterfaceRef 408MonitoredInterfaceCreate(CFStringRef ifname) 409{ 410 MonitoredInterfaceRef mon; 411 412 mon = (MonitoredInterfaceRef)malloc(sizeof(*mon)); 413 bzero(mon, sizeof(*mon)); 414 mon->if_name_cf = CFRetain(ifname); 415 CFStringGetCString(ifname, mon->if_name, sizeof(mon->if_name), 416 kCFStringEncodingASCII); 417 LIST_INSERT_HEAD(S_MonitoredInterfaceHead_p, mon, entries); 418 return (mon); 419} 420 421static void 422MonitoredInterfaceReleasePrompt(MonitoredInterfaceRef mon) 423{ 424 if (mon->cfun != NULL) { 425 (void)CFUserNotificationCancel(mon->cfun); 426 my_CFRelease(&mon->cfun); 427 } 428 if (mon->rls != NULL) { 429 CFRunLoopRemoveSource(CFRunLoopGetCurrent(), mon->rls, 430 kCFRunLoopDefaultMode); 431 my_CFRelease(&mon->rls); 432 } 433 my_CFRelease(&mon->profiles); 434 return; 435} 436 437static void 438MonitoredInterfaceRelease(MonitoredInterfaceRef * mon_p) 439{ 440 MonitoredInterfaceRef mon; 441 442 mon = *mon_p; 443 if (mon == NULL) { 444 return; 445 } 446 *mon_p = NULL; 447 LIST_REMOVE(mon, entries); 448 my_CFRelease(&mon->if_name_cf); 449 MonitoredInterfaceReleasePrompt(mon); 450 free(mon); 451 return; 452} 453 454static MonitoredInterfaceRef 455MonitoredInterfaceLookupByName(CFStringRef if_name_cf) 456{ 457 MonitoredInterfaceRef scan; 458 459 LIST_FOREACH(scan, S_MonitoredInterfaceHead_p, entries) { 460 if (CFEqual(scan->if_name_cf, if_name_cf)) { 461 return (scan); 462 } 463 } 464 return (NULL); 465} 466 467static MonitoredInterfaceRef 468MonitoredInterfaceLookupByCFUN(CFUserNotificationRef cfun) 469{ 470 MonitoredInterfaceRef scan; 471 472 LIST_FOREACH(scan, S_MonitoredInterfaceHead_p, entries) { 473 if (scan->cfun == cfun) { 474 return (scan); 475 } 476 } 477 return (NULL); 478} 479 480static const CFStringRef S_manager_name = CFSTR("com.apple.UserEventAgent.EAPOLMonitor"); 481 482static void 483MonitoredInterfaceStartAuthentication(MonitoredInterfaceRef mon, 484 EAPOLClientItemIDRef itemID) 485{ 486 CFStringRef key; 487 CFDictionaryRef options; 488 EAPOLClientProfileRef profile = EAPOLClientItemIDGetProfile(itemID); 489 int status; 490 491 key = kEAPOLControlStartOptionManagerName; 492 options = CFDictionaryCreate(NULL, 493 (const void * *)&key, 494 (const void * *)&S_manager_name, 495 1, 496 &kCFTypeDictionaryKeyCallBacks, 497 &kCFTypeDictionaryValueCallBacks); 498 EAPLOG(LOG_NOTICE, 499 "EAPOLMonitor: %s starting %s", 500 mon->if_name, 501 (profile == NULL) ? "(Default Settings)" : "(Profile)"); 502 status = EAPOLControlStartWithOptions(mon->if_name, itemID, options); 503 CFRelease(options); 504 if (status == 0) { 505 mon->state = kMonitorStateStarted; 506 mon->start_time = CFAbsoluteTimeGetCurrent(); 507 mon->link_active_when_started = is_link_active(mon->if_name); 508 } 509 else { 510 /* XXX how to handle this ? */ 511 EAPLOG(LOG_NOTICE, 512 "EAPOLMonitor: %s start failed %d %s", 513 mon->if_name, 514 (profile == NULL) ? "(Default Settings)" : "(Profile)"); 515 } 516 return; 517} 518 519static void 520MonitoredInterfaceStartAuthenticationWithProfile(MonitoredInterfaceRef mon, 521 EAPOLClientProfileRef profile) 522{ 523 EAPOLClientItemIDRef itemID; 524 525 if (profile == NULL) { 526 itemID = EAPOLClientItemIDCreateDefault(); 527 } 528 else { 529 itemID = EAPOLClientItemIDCreateWithProfile(profile); 530 } 531 MonitoredInterfaceStartAuthentication(mon, itemID); 532 CFRelease(itemID); 533 return; 534} 535 536static void 537MonitoredInterfaceStopAuthentication(MonitoredInterfaceRef mon) 538{ 539 int status; 540 541 EAPLOG(LOG_NOTICE, "EAPOLMonitor: %s stopping", mon->if_name); 542 status = EAPOLControlStop(mon->if_name); 543 if (status != 0) { 544 EAPLOG(LOG_NOTICE, 545 "EAPOLMonitor: %s stop failed with %d", 546 mon->if_name, status); 547 } 548 return; 549} 550 551static void 552MonitoredInterfaceStopIfNecessary(MonitoredInterfaceRef mon) 553{ 554 EAPOLControlState control_state = kEAPOLControlStateIdle; 555 CFDictionaryRef dict; 556 int error; 557 558 error = EAPOLControlCopyStateAndStatus(mon->if_name, &control_state, &dict); 559 if (error != 0) { 560 EAPLOG(LOG_DEBUG, 561 "EAPOLMonitor: EAPOLControlCopyStateAndStatus(%s) returned %d", 562 mon->if_name, error); 563 return; 564 } 565 switch (control_state) { 566 case kEAPOLControlStateIdle: 567 case kEAPOLControlStateStopping: 568 break; 569 case kEAPOLControlStateStarting: 570 /* XXX we really need to wait for the state to be Running */ 571 case kEAPOLControlStateRunning: 572 if (dict != NULL) { 573 CFStringRef manager_name; 574 575 manager_name = CFDictionaryGetValue(dict, 576 kEAPOLControlManagerName); 577 if (manager_name != NULL 578 && CFEqual(manager_name, S_manager_name)) { 579 /* it's ours, so stop it */ 580 MonitoredInterfaceStopAuthentication(mon); 581 } 582 } 583 break; 584 } 585 my_CFRelease(&dict); 586 return; 587} 588 589static void 590MonitoredInterfacePromptComplete(CFUserNotificationRef cfun, 591 CFOptionFlags response_flags) 592{ 593 MonitoredInterfaceRef mon; 594 EAPOLClientProfileRef profile; 595 int which_profile = -1; 596 597 mon = MonitoredInterfaceLookupByCFUN(cfun); 598 if (mon == NULL) { 599 EAPLOG(LOG_ERR, "EAPOLMonitor: can't find user notification"); 600 return; 601 } 602 switch (S_CFUserNotificationResponse(response_flags)) { 603 case kCFUserNotificationDefaultResponse: 604 /* start the authentication */ 605 which_profile = (response_flags 606 & CFUserNotificationPopUpSelection(-1)) >> 24; 607 if (which_profile == 0) { 608 profile = NULL; 609 } 610 else { 611 profile = (EAPOLClientProfileRef) 612 CFArrayGetValueAtIndex(mon->profiles, which_profile - 1); 613 } 614 MonitoredInterfaceStartAuthenticationWithProfile(mon, profile); 615 break; 616 default: 617 mon->state = kMonitorStateIdle; 618 mon->ignore_until_link_status_changes = TRUE; 619 break; 620 } 621 MonitoredInterfaceReleasePrompt(mon); 622 return; 623} 624 625static void 626MonitoredInterfaceDisplayProfilePrompt(MonitoredInterfaceRef mon, 627 PromptStartContextRef context) 628{ 629 CFBundleRef bundle; 630 CFUserNotificationRef cfun = NULL; 631 CFMutableDictionaryRef dict = NULL; 632 SInt32 error = 0; 633 CFStringRef if_name_loc; 634 CFStringRef notif_header; 635 CFStringRef notif_header_format; 636 CFRunLoopSourceRef rls = NULL; 637 CFURLRef url = NULL; 638 639 MonitoredInterfaceReleasePrompt(mon); 640 bundle = S_get_bundle(); 641 if (bundle == NULL) { 642 EAPLOG(LOG_NOTICE, "EAPOLMonitor: can't find bundle"); 643 return; 644 } 645 url = CFBundleCopyBundleURL(bundle); 646 if (url == NULL) { 647 EAPLOG(LOG_ERR, "EAPOLMonitor: can't find bundle URL"); 648 goto done; 649 } 650 dict = CFDictionaryCreateMutable(NULL, 0, 651 &kCFTypeDictionaryKeyCallBacks, 652 &kCFTypeDictionaryValueCallBacks); 653 654 CFDictionaryAddValue(dict, kCFUserNotificationAlternateButtonTitleKey, 655 CFSTR("CANCEL")); 656 CFDictionaryAddValue(dict, kCFUserNotificationDefaultButtonTitleKey, 657 CFSTR("CONNECT")); 658 notif_header_format 659 = CFBundleCopyLocalizedString(bundle, 660 CFSTR("NOTIFICATION_HEADER"), 661 CFSTR("NOTIFICATION_HEADER %@"), 662 NULL); 663 if_name_loc = copy_localized_if_name(mon->if_name_cf); 664 notif_header = CFStringCreateWithFormat(NULL, NULL, 665 notif_header_format, 666 (if_name_loc != NULL) 667 ? if_name_loc 668 : mon->if_name_cf); 669 CFRelease(notif_header_format); 670 my_CFRelease(&if_name_loc); 671 CFDictionarySetValue(dict, kCFUserNotificationAlertHeaderKey, 672 notif_header); 673 CFRelease(notif_header); 674 CFDictionarySetValue(dict, kCFUserNotificationPopUpTitlesKey, 675 context->profile_names); 676 CFDictionarySetValue(dict, kCFUserNotificationLocalizationURLKey, 677 url); 678 CFRelease(url); 679 680 /* icon */ 681 url = copy_icon_url(CFSTR("Network")); 682 if (url != NULL) { 683 CFDictionarySetValue(dict, kCFUserNotificationIconURLKey, 684 url); 685 CFRelease(url); 686 } 687 cfun = CFUserNotificationCreate(NULL, 0, 0, &error, dict); 688 if (cfun == NULL) { 689 EAPLOG(LOG_ERR, "CFUserNotificationCreate() failed, %d", error); 690 goto done; 691 } 692 rls = CFUserNotificationCreateRunLoopSource(NULL, cfun, 693 MonitoredInterfacePromptComplete, 694 0); 695 if (rls == NULL) { 696 EAPLOG(LOG_ERR, "CFUserNotificationCreateRunLoopSource() failed"); 697 my_CFRelease(&cfun); 698 } 699 else { 700 CFRunLoopAddSource(CFRunLoopGetCurrent(), rls, kCFRunLoopDefaultMode); 701 mon->rls = rls; 702 mon->cfun = cfun; 703 mon->profiles = CFRetain(context->profiles); 704 mon->state = kMonitorStatePrompting; 705 EAPLOG(LOG_DEBUG, "EAPOLMonitor: %s prompting user", 706 mon->if_name); 707 } 708 709 done: 710 my_CFRelease(&dict); 711 return; 712} 713 714 715static void 716MonitoredInterfaceProcessStatus(MonitoredInterfaceRef mon, 717 CFDictionaryRef status_dict) 718{ 719 CFDataRef authenticator; 720 EAPClientStatus client_status; 721 CFStringRef manager_name; 722 bool our_manager; 723 CFStringRef profileID; 724 SupplicantState supp_state; 725 726 supp_state = S_get_plist_uint32(status_dict, kEAPOLControlSupplicantState, 727 -1); 728 client_status = S_get_plist_uint32(status_dict, kEAPOLControlClientStatus, 729 kEAPClientStatusOK); 730 /* ignore this if it's not ours */ 731 if (S_get_plist_uint32(status_dict, kEAPOLControlUID, -1) != getuid()) { 732 return; 733 } 734 EAPLOG(LOG_DEBUG, "EAPOLMonitor: %s is %s", mon->if_name, 735 SupplicantStateString(supp_state)); 736 manager_name = CFDictionaryGetValue(status_dict, 737 kEAPOLControlManagerName); 738 authenticator = CFDictionaryGetValue(status_dict, 739 kEAPOLControlAuthenticatorMACAddress); 740 profileID = CFDictionaryGetValue(status_dict, 741 kEAPOLControlUniqueIdentifier); 742 our_manager = (manager_name != NULL 743 && CFEqual(manager_name, S_manager_name)); 744 switch (supp_state) { 745 case kSupplicantStateDisconnected: 746 case kSupplicantStateConnecting: 747 case kSupplicantStateAcquired: 748 case kSupplicantStateAuthenticating: 749 case kSupplicantStateLogoff: 750 break; 751 case kSupplicantStateAuthenticated: { 752 EAPOLClientItemIDRef itemID = NULL; 753 754 if (profileID != NULL) { 755 EAPOLClientConfigurationRef cfg; 756 EAPOLClientProfileRef profile; 757 758 cfg = EAPOLClientConfigurationCreate(NULL); 759 profile = EAPOLClientConfigurationGetProfileWithID(cfg, profileID); 760 if (profile != NULL) { 761 itemID = EAPOLClientItemIDCreateWithProfile(profile); 762 } 763 } 764 else { 765 itemID = EAPOLClientItemIDCreateDefault(); 766 } 767 if (itemID != NULL && authenticator != NULL) { 768 EAPOLControlSetItemIDForAuthenticator(authenticator, itemID); 769 CFRelease(itemID); 770 } 771 break; 772 } 773 case kSupplicantStateHeld: 774 if (our_manager) { 775 switch (client_status) { 776 case kEAPClientStatusOK: 777 case kEAPClientStatusFailed: 778 break; 779 default: 780 /* clear the binding, since we ran into trouble */ 781 if (authenticator != NULL) { 782 EAPOLControlSetItemIDForAuthenticator(authenticator, NULL); 783 } 784 break; 785 } 786 } 787 break; 788 case kSupplicantStateInactive: 789 if (our_manager) { 790 bool stop_it = FALSE; 791 792 if (mon->state != kMonitorStateStarted) { 793 stop_it = TRUE; 794 } 795 else if (mon->link_active_when_started == FALSE) { 796 CFAbsoluteTime now; 797 798 now = CFAbsoluteTimeGetCurrent(); 799 if ((now - mon->start_time) > 0.9) { 800 stop_it = TRUE; 801 } 802 else { 803 /* if we *just* started the authentication, ignore this */ 804 EAPLOG(LOG_DEBUG, "EAPOLMonitor: %s delay was %g seconds", 805 mon->if_name, now - mon->start_time); 806 } 807 } 808 else { 809 stop_it = TRUE; 810 } 811 if (stop_it) { 812 MonitoredInterfaceStopAuthentication(mon); 813 } 814 } 815 break; 816 case kSupplicantStateNoAuthenticator: 817 break; 818 } 819 return; 820} 821 822static void 823MonitoredInterfaceCheckStatus(MonitoredInterfaceRef mon) 824{ 825 EAPOLControlState control_state = kEAPOLControlStateIdle; 826 CFDictionaryRef dict; 827 int error; 828 829 MonitoredInterfaceReleasePrompt(mon); 830 error = EAPOLControlCopyStateAndStatus(mon->if_name, &control_state, &dict); 831 if (error != 0) { 832 EAPLOG(LOG_DEBUG, 833 "EAPOLMonitor: EAPOLControlCopyStateAndStatus(%s) returned %d", 834 mon->if_name, error); 835 return; 836 } 837 switch (control_state) { 838 case kEAPOLControlStateIdle: 839 case kEAPOLControlStateStopping: 840 mon->state = kMonitorStateIdle; 841 if (EAPOLControlDidUserCancel(mon->if_name)) { 842 mon->ignore_until_link_status_changes = TRUE; 843 } 844 break; 845 case kEAPOLControlStateStarting: 846 /* not quite ready yet, we'll wait for Running */ 847 break; 848 case kEAPOLControlStateRunning: 849 if (mon->state != kMonitorStateStarted) { 850 /* user is controlling things */ 851 mon->state = kMonitorStateIdle; 852 mon->ignore_until_link_status_changes = TRUE; 853 } 854 if (dict != NULL) { 855 MonitoredInterfaceProcessStatus(mon, dict); 856 } 857 break; 858 } 859 my_CFRelease(&dict); 860 return; 861} 862 863static void 864MonitoredInterfaceLinkStatusChanged(MonitoredInterfaceRef mon) 865{ 866 bool link_active; 867 868 link_active = is_link_active(mon->if_name); 869 if (link_active == FALSE) { 870 if (mon->state == kMonitorStatePrompting) { 871 MonitoredInterfaceReleasePrompt(mon); 872 mon->state = kMonitorStateIdle; 873 } 874 } 875 mon->require_fresh_detection = TRUE; 876 mon->ignore_until_link_status_changes = FALSE; 877 return; 878} 879 880static void 881prompt_or_start(const void * key, const void * value, void * arg) 882{ 883 uint32_t age_seconds = 0; 884 CFDataRef authenticator = NULL; 885 CFDictionaryRef dict; 886 EAPOLClientItemIDRef itemID = NULL; 887 PromptStartContextRef context_p = (PromptStartContextRef)arg; 888 CFStringRef if_name_cf = (CFStringRef)key; 889 MonitoredInterfaceRef mon; 890 891 mon = MonitoredInterfaceLookupByName(if_name_cf); 892 if (mon == NULL) { 893 mon = MonitoredInterfaceCreate(if_name_cf); 894 } 895 EAPLOG(LOG_DEBUG, "EAPOLMonitor: %s auto-detected", mon->if_name); 896 if (mon->ignore_until_link_status_changes) { 897 EAPLOG(LOG_DEBUG, "EAPOLMonitor: %s ignoring until link status changes", 898 mon->if_name); 899 /* wait until the link status changes before dealing with this */ 900 return; 901 } 902 if (mon->state != kMonitorStateIdle) { 903 return; 904 } 905 906 /* check for an existing binding */ 907 dict = isA_CFDictionary(value); 908 if (dict != NULL) { 909 CFNumberRef age_seconds_cf; 910 911 age_seconds_cf 912 = CFDictionaryGetValue(dict, 913 kEAPOLAutoDetectSecondsSinceLastPacket); 914 if (isA_CFNumber(age_seconds_cf) != NULL) { 915 (void)CFNumberGetValue(age_seconds_cf, 916 kCFNumberSInt32Type, 917 &age_seconds); 918 if (age_seconds == 0) { 919 mon->require_fresh_detection = FALSE; 920 } 921 else { 922 if (is_link_active(mon->if_name) == FALSE) { 923 EAPLOG(LOG_DEBUG, 924 "EAPOLMonitor: %s link isn't active", 925 mon->if_name); 926 return; 927 } 928 if (mon->require_fresh_detection && age_seconds > 2) { 929 /* if the link status changed, wait for next packet */ 930 EAPLOG(LOG_DEBUG, 931 "EAPOLMonitor: %s wait for a fresh detect", 932 mon->if_name); 933 return; 934 } 935 if (age_seconds > 30) { 936 /* if it's been awhile, wait for the next packet */ 937 EAPLOG(LOG_DEBUG, 938 "EAPOLMonitor: %s ignoring (%d seconds)", 939 mon->if_name, age_seconds); 940 return; 941 } 942 } 943 } 944 authenticator 945 = CFDictionaryGetValue(dict, 946 kEAPOLAutoDetectAuthenticatorMACAddress); 947 authenticator = isA_CFData(authenticator); 948 } 949 if (authenticator != NULL) { 950 itemID = EAPOLControlCopyItemIDForAuthenticator(authenticator); 951 } 952 if (itemID != NULL) { 953 MonitoredInterfaceStartAuthentication(mon, itemID); 954 CFRelease(itemID); 955 } 956 else if (context_p->profiles != NULL) { 957 MonitoredInterfaceDisplayProfilePrompt(mon, context_p); 958 } 959 else { 960 MonitoredInterfaceStartAuthenticationWithProfile(mon, NULL); 961 } 962 return; 963} 964 965static void 966process_auto_detect_info(CFDictionaryRef auto_detect_info) 967{ 968 PromptStartContext context; 969 970 EAPLOG(LOG_DEBUG, "EAPOLMonitor: processing auto-detect information"); 971 PromptStartContextInit(&context); 972 CFDictionaryApplyFunction(auto_detect_info, prompt_or_start, &context); 973 PromptStartContextRelease(&context); 974 return; 975} 976 977static void 978process_interface_change_info(CFArrayRef if_status_changed) 979{ 980 int count; 981 int i; 982 983 count = CFArrayGetCount(if_status_changed); 984 for (i = 0; i < count; i++) { 985 CFStringRef if_name_cf; 986 MonitoredInterfaceRef mon; 987 988 if_name_cf = CFArrayGetValueAtIndex(if_status_changed, i); 989 mon = MonitoredInterfaceLookupByName(if_name_cf); 990 if (mon != NULL) { 991 if (mon->state == kMonitorStateIgnorePermanently) { 992 /* ignore non-ethernet interfaces */ 993 continue; 994 } 995 } 996 else { 997 mon = MonitoredInterfaceCreate(if_name_cf); 998 if (get_ifm_type(mon->if_name) != IFM_ETHER) { 999 mon->state = kMonitorStateIgnorePermanently; 1000 continue; 1001 } 1002 } 1003 MonitoredInterfaceCheckStatus(mon); 1004 } 1005 return; 1006} 1007 1008static void 1009handle_changes(SCDynamicStoreRef store, CFArrayRef changes, void * info) 1010{ 1011 bool check_auto_detect = FALSE; 1012 int count; 1013 int i; 1014 CFMutableArrayRef if_status_changed = NULL; 1015 1016 count = CFArrayGetCount(changes); 1017 for (i = 0; i < count; i++) { 1018 CFStringRef key = CFArrayGetValueAtIndex(changes, i); 1019 1020 if (CFEqual(key, kEAPOLControlAutoDetectInformationNotifyKey)) { 1021 if (S_auto_connect) { 1022 check_auto_detect = TRUE; 1023 } 1024 } 1025 else if (CFStringHasSuffix(key, kSCEntNetLink)) { 1026 CFStringRef if_name_cf; 1027 MonitoredInterfaceRef mon; 1028 1029 /* State:/Network/Interface/<ifname>/Link */ 1030 if_name_cf = my_CFStringCopyComponent(key, CFSTR("/"), 3); 1031 if (if_name_cf == NULL) { 1032 continue; 1033 } 1034 mon = MonitoredInterfaceLookupByName(if_name_cf); 1035 CFRelease(if_name_cf); 1036 if (mon != NULL) { 1037 MonitoredInterfaceLinkStatusChanged(mon); 1038 } 1039 } 1040 else { 1041 CFStringRef this_if; 1042 1043 this_if = EAPOLControlKeyCopyInterface(key); 1044 if (this_if != NULL) { 1045 if (if_status_changed == NULL) { 1046 if_status_changed 1047 = CFArrayCreateMutable(NULL, count, 1048 &kCFTypeArrayCallBacks); 1049 } 1050 CFArrayAppendValue(if_status_changed, this_if); 1051 CFRelease(this_if); 1052 } 1053 } 1054 } 1055 1056 /* process EAPOL state changes */ 1057 if (if_status_changed != NULL) { 1058 process_interface_change_info(if_status_changed); 1059 } 1060 1061 /* process interfaces that we've auto-detected 802.1X */ 1062 if (check_auto_detect && S_on_console(store)) { 1063 CFDictionaryRef auto_detect_info = NULL; 1064 1065 (void)EAPOLControlCopyAutoDetectInformation(&auto_detect_info); 1066 if (auto_detect_info != NULL) { 1067 process_auto_detect_info(auto_detect_info); 1068 } 1069 my_CFRelease(&auto_detect_info); 1070 } 1071 my_CFRelease(&if_status_changed); 1072 return; 1073} 1074 1075static void 1076do_cleanup(SCDynamicStoreRef store, void * info) 1077{ 1078 MonitoredInterfaceRef mon; 1079 1080 /* configd went away, throw away all of our state */ 1081 EAPLOG(LOG_DEBUG, "EAPOLMonitor: cleaning up state"); 1082 while ((mon = LIST_FIRST(S_MonitoredInterfaceHead_p)) != NULL) { 1083 MonitoredInterfaceRelease(&mon); 1084 } 1085 return; 1086} 1087 1088static void 1089check_settings(SCDynamicStoreRef store) 1090{ 1091 if (S_auto_connect) { 1092 /* start auto-connect */ 1093 CFDictionaryRef auto_detect_info; 1094 1095 if (S_on_console(store)) { 1096 EAPLOG(LOG_DEBUG, "EAPOLMonitor: auto-connect enabled"); 1097 (void)EAPOLControlCopyAutoDetectInformation(&auto_detect_info); 1098 if (auto_detect_info != NULL) { 1099 process_auto_detect_info(auto_detect_info); 1100 CFRelease(auto_detect_info); 1101 } 1102 } 1103 } 1104 else { 1105 MonitoredInterfaceRef scan; 1106 1107 /* stop auto-connect */ 1108 EAPLOG(LOG_NOTICE, "EAPOLMonitor: auto-connect disabled"); 1109 LIST_FOREACH(scan, S_MonitoredInterfaceHead_p, entries) { 1110 MonitoredInterfaceStopIfNecessary(scan); 1111 MonitoredInterfaceReleasePrompt(scan); 1112 scan->state = kMonitorStateIdle; 1113 scan->ignore_until_link_status_changes = FALSE; 1114 } 1115 1116 } 1117 return; 1118} 1119 1120static void 1121settings_changed(CFMachPortRef port, void * msg, CFIndex size, void * info) 1122{ 1123 bool auto_connect; 1124 MyType * me = (MyType *)info; 1125 1126 EAPLOG(LOG_DEBUG, "EAPOLMonitor: settings changed"); 1127 auto_connect = EAPOLControlIsUserAutoConnectEnabled(); 1128 if (auto_connect != S_auto_connect) { 1129 S_auto_connect = auto_connect; 1130 check_settings(me->store); 1131 } 1132 return; 1133} 1134 1135static void 1136check_prefs(SCPreferencesRef prefs) 1137{ 1138 uint32_t log_flags = EAPOLControlPrefsGetLogFlags(); 1139 1140 EAPLogSetVerbose(log_flags != 0); 1141 EAPOLControlPrefsSynchronize(); 1142 return; 1143} 1144 1145static void 1146add_settings_notification(MyType * me) 1147{ 1148 CFMachPortContext context = {0, NULL, NULL, NULL, NULL}; 1149 CFMachPortRef notify_port_cf; 1150 mach_port_t notify_port; 1151 int notify_token; 1152 CFRunLoopSourceRef rls; 1153 uint32_t status; 1154 1155 notify_port = MACH_PORT_NULL; 1156 status = notify_register_mach_port(kEAPOLControlUserSettingsNotifyKey, 1157 ¬ify_port, 0, ¬ify_token); 1158 if (status != NOTIFY_STATUS_OK) { 1159 EAPLOG(LOG_ERR, "EAPOLMonitor: notify_register_mach_port() failed"); 1160 return; 1161 } 1162 context.info = me; 1163 notify_port_cf = CFMachPortCreateWithPort(NULL, notify_port, 1164 settings_changed, 1165 &context, 1166 NULL); 1167 if (notify_port_cf == NULL) { 1168 EAPLOG(LOG_ERR, "EAPOLMonitor: CFMachPortCreateWithPort() failed"); 1169 (void)notify_cancel(notify_token); 1170 return; 1171 } 1172 rls = CFMachPortCreateRunLoopSource(NULL, notify_port_cf, 0); 1173 if (rls == NULL) { 1174 EAPLOG(LOG_ERR, "EAPOLMonitor: CFMachPortCreateRunLoopSource() failed"); 1175 CFRelease(notify_port_cf); 1176 (void)notify_cancel(notify_token); 1177 return; 1178 } 1179 CFRunLoopAddSource(CFRunLoopGetCurrent(), rls, kCFRunLoopDefaultMode); 1180 CFRelease(rls); 1181 me->settings_change.mp = notify_port_cf; 1182 me->settings_change.token = notify_token; 1183 check_prefs(EAPOLControlPrefsInit(CFRunLoopGetCurrent(), check_prefs)); 1184 return; 1185} 1186 1187/** 1188 ** Plug-in functions 1189 **/ 1190static void 1191myInstall(void * myInstance) 1192{ 1193 SCDynamicStoreContext context = {0, myInstance, NULL, NULL, NULL}; 1194 MyType * me = (MyType *)myInstance; 1195 CFStringRef key[2]; 1196 CFArrayRef keys; 1197 CFArrayRef patterns; 1198 CFRunLoopSourceRef rls; 1199 SCDynamicStoreRef store; 1200 1201 store = SCDynamicStoreCreate(NULL, CFSTR("EAPOLMonitor"), 1202 handle_changes, &context); 1203 if (store == NULL) { 1204 EAPLOG(LOG_ERR, 1205 "EAPOLMonitor: SCDynamicStoreCreate failed, %s", 1206 SCErrorString(SCError())); 1207 return; 1208 } 1209 1210 key[0] = kEAPOLControlAutoDetectInformationNotifyKey; 1211 keys = CFArrayCreate(NULL, (const void **)key, 1, 1212 &kCFTypeArrayCallBacks); 1213 1214 key[0] = EAPOLControlAnyInterfaceKeyCreate(); 1215 key[1] = SCDynamicStoreKeyCreateNetworkInterfaceEntity(NULL, 1216 kSCDynamicStoreDomainState, 1217 kSCCompAnyRegex, 1218 kSCEntNetLink); 1219 patterns = CFArrayCreate(NULL, (const void **)key, 2, 1220 &kCFTypeArrayCallBacks); 1221 CFRelease(key[0]); 1222 CFRelease(key[1]); 1223 1224 SCDynamicStoreSetNotificationKeys(store, keys, patterns); 1225 CFRelease(keys); 1226 CFRelease(patterns); 1227 1228 rls = SCDynamicStoreCreateRunLoopSource(NULL, store, 0); 1229 CFRunLoopAddSource(CFRunLoopGetCurrent(), rls, kCFRunLoopDefaultMode); 1230 me->store = store; 1231 me->rls = rls; 1232 1233 SCDynamicStoreSetDisconnectCallBack(store, do_cleanup); 1234 1235 add_settings_notification(me); 1236 S_auto_connect = EAPOLControlIsUserAutoConnectEnabled(); 1237 check_settings(store); 1238 return; 1239} 1240 1241static void _deallocMyType(MyType *myInstance); 1242 1243static HRESULT myQueryInterface(void *myInstance, REFIID iid, LPVOID *ppv) { 1244 CFUUIDRef interfaceID = CFUUIDCreateFromUUIDBytes(NULL, iid); 1245 1246 // Test the requested ID against the valid interfaces. 1247 if (CFEqual(interfaceID, kUserEventAgentInterfaceID)) { 1248 ((MyType *) myInstance)->_UserEventAgentInterface->AddRef(myInstance); 1249 *ppv = myInstance; 1250 CFRelease(interfaceID); 1251 return S_OK; 1252 } 1253 if (CFEqual(interfaceID, IUnknownUUID)) { 1254 ((MyType *) myInstance)->_UserEventAgentInterface->AddRef(myInstance); 1255 *ppv = myInstance; 1256 CFRelease(interfaceID); 1257 return S_OK; 1258 } 1259 // Requested interface unknown, bail with error. 1260 *ppv = NULL; 1261 CFRelease(interfaceID); 1262 return E_NOINTERFACE; 1263} 1264 1265static ULONG myAddRef(void *myInstance) { 1266 ((MyType *) myInstance)->_refCount++; 1267 return ((MyType *) myInstance)->_refCount; 1268} 1269 1270static ULONG myRelease(void *myInstance) { 1271 ((MyType *) myInstance)->_refCount--; 1272 if (((MyType *) myInstance)->_refCount == 0) { 1273 _deallocMyType((MyType *) myInstance); 1274 return 0; 1275 } else return ((MyType *) myInstance)->_refCount; 1276} 1277 1278static UserEventAgentInterfaceStruct UserEventAgentInterfaceFtbl = { 1279 NULL, // Required padding for COM 1280 myQueryInterface, // These three are the required COM functions 1281 myAddRef, 1282 myRelease, 1283 myInstall // Interface implementation 1284}; 1285 1286static MyType *_allocMyType(CFUUIDRef factoryID) { 1287 MyType *newOne = (MyType *)malloc(sizeof(MyType)); 1288 1289 bzero(newOne, sizeof(*newOne)); 1290 newOne->_UserEventAgentInterface = &UserEventAgentInterfaceFtbl; 1291 1292 if (factoryID) { 1293 newOne->_factoryID = (CFUUIDRef)CFRetain(factoryID); 1294 CFPlugInAddInstanceForFactory(factoryID); 1295 newOne->store = NULL; 1296 } 1297 1298 newOne->_refCount = 1; 1299 return newOne; 1300} 1301 1302static void 1303my_CFRunLoopSourceRelease(CFRunLoopSourceRef * rls_p) 1304{ 1305 CFRunLoopSourceRef rls = *rls_p; 1306 1307 if (rls != NULL) { 1308 CFRunLoopRemoveSource(CFRunLoopGetCurrent(), rls, 1309 kCFRunLoopDefaultMode); 1310 my_CFRelease(rls_p); 1311 } 1312 return; 1313} 1314 1315static void _deallocMyType(MyType * me) 1316{ 1317 CFUUIDRef factoryID = me->_factoryID; 1318 1319 if (factoryID != NULL) { 1320 MonitoredInterfaceRef mon; 1321 1322 CFPlugInRemoveInstanceForFactory(factoryID); 1323 my_CFRelease(&me->store); 1324 my_CFRunLoopSourceRelease(&me->rls); 1325 1326 if (me->settings_change.mp != NULL) { 1327 CFMachPortInvalidate(me->settings_change.mp); 1328 my_CFRelease(&me->settings_change.mp); 1329 (void)notify_cancel(me->settings_change.token); 1330 } 1331 1332 /* release the list of MonitoredInterfaceRef's */ 1333 while ((mon = LIST_FIRST(S_MonitoredInterfaceHead_p)) != NULL) { 1334 MonitoredInterfaceStopIfNecessary(mon); 1335 MonitoredInterfaceRelease(&mon); 1336 } 1337 CFRelease(factoryID); 1338 } 1339 free(me); 1340 return; 1341} 1342 1343void *UserEventAgentFactory(CFAllocatorRef allocator, CFUUIDRef typeID) 1344{ 1345 if (CFEqual(typeID, kUserEventAgentTypeID)) { 1346 MyType *result = _allocMyType(kUserEventAgentFactoryID); 1347 return result; 1348 } else return NULL; 1349} 1350 1351#ifdef TEST_PLUGIN 1352int 1353main() 1354{ 1355 MyType *newOne = (MyType *)malloc(sizeof(MyType)); 1356 1357 bzero(newOne, sizeof(*newOne)); 1358 myInstall(newOne); 1359 CFRunLoopRun(); 1360 exit(0); 1361 return (0); 1362} 1363#endif /* TEST_PLUGIN */ 1364