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