1/* 2 * Copyright (c) 2002-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#include <stdio.h> 25#include <unistd.h> 26#include <stdlib.h> 27#include <bsm/audit.h> 28#include <mach/mach.h> 29#include <mach/message.h> 30#include <stdbool.h> 31#include <mach/mach_error.h> 32#include <servers/bootstrap.h> 33#include <CoreFoundation/CFMachPort.h> 34#include <SystemConfiguration/SCPrivate.h> 35#include <SystemConfiguration/SCValidation.h> 36#include <SystemConfiguration/SystemConfiguration.h> 37#include "eapolcontroller.h" 38#include "eapolcontroller_types.h" 39#include "eapolcontroller_ext.h" 40#include "EAPLog.h" 41#include "myCFUtil.h" 42#include "EAPOLControl.h" 43#include "EAPOLControlTypes.h" 44#include "EAPOLControlTypesPrivate.h" 45#if ! TARGET_OS_EMBEDDED 46#include <CoreFoundation/CFPreferences.h> 47#include <notify.h> 48#include <net/ethernet.h> 49#include "EAPOLClientConfigurationPrivate.h" 50const CFStringRef kEAPOLControlAutoDetectInformationNotifyKey = CFSTR("com.apple.network.eapolcontrol.autodetect"); 51const CFStringRef kEAPOLAutoDetectSecondsSinceLastPacket = CFSTR("SecondsSinceLastPacket"); 52const CFStringRef kEAPOLAutoDetectAuthenticatorMACAddress = CFSTR("AuthenticatorMACAddress"); 53#endif /* ! TARGET_OS_EMBEDDED */ 54#include "EAPClientProperties.h" 55 56#ifndef kSCEntNetEAPOL 57#define kSCEntNetEAPOL CFSTR("EAPOL") 58#endif /* kSCEntNetEAPOL */ 59 60static Boolean 61get_server_port(mach_port_t * server, kern_return_t * status) 62{ 63 *status = eapolcontroller_server_port(server); 64 if (*status != BOOTSTRAP_SUCCESS) { 65 EAPLOG_FL(LOG_NOTICE, "eapolcontroller_server_port failed, %s", 66 mach_error_string(*status)); 67 return (FALSE); 68 } 69 return (TRUE); 70} 71 72int 73EAPOLControlStart(const char * interface_name, CFDictionaryRef config_dict) 74{ 75 mach_port_t au_session = MACH_PORT_NULL; 76 CFDataRef data = NULL; 77 if_name_t if_name; 78 boolean_t need_deallocate; 79 mach_port_t server; 80 int result = 0; 81 kern_return_t status; 82 83 status = mach_port_mod_refs(mach_task_self(), bootstrap_port, 84 MACH_PORT_RIGHT_SEND, 1); 85 if (status != KERN_SUCCESS) { 86 need_deallocate = FALSE; 87 EAPLOG_FL(LOG_NOTICE, "mach_port_mod_refs failed, %s (%d)", 88 mach_error_string(status), status); 89 result = ENXIO; 90 goto done; 91 } 92 need_deallocate = TRUE; 93 au_session = audit_session_self(); 94 if (au_session == MACH_PORT_NULL) { 95 result = ENXIO; 96 EAPLOG_FL(LOG_NOTICE, "audit_session_self failed"); 97 goto done; 98 } 99 if (get_server_port(&server, &status) == FALSE) { 100 result = ENXIO; 101 goto done; 102 } 103 if (isA_CFDictionary(config_dict) == NULL) { 104 result = EINVAL; 105 goto done; 106 } 107 data = CFPropertyListCreateXMLData(NULL, config_dict); 108 if (data == NULL) { 109 result = ENOMEM; 110 goto done; 111 } 112 strncpy(if_name, interface_name, sizeof(if_name)); 113 status = eapolcontroller_start(server, 114 if_name, 115 (xmlDataOut_t)CFDataGetBytePtr(data), 116 CFDataGetLength(data), 117 bootstrap_port, 118 au_session, 119 &result); 120 if (status != KERN_SUCCESS) { 121 EAPLOG_FL(LOG_NOTICE, "eapolcontroller_start failed, %s (%d)", 122 mach_error_string(status), status); 123 result = ENXIO; 124 goto done; 125 } 126 if (result != 0) { 127 EAPLOG_FL(LOG_NOTICE, "eapolcontroller_start: result is %d", result); 128 } 129 130 done: 131 if (need_deallocate) { 132 (void)mach_port_mod_refs(mach_task_self(), bootstrap_port, 133 MACH_PORT_RIGHT_SEND, -1); 134 } 135 if (au_session != MACH_PORT_NULL) { 136 (void)mach_port_deallocate(mach_task_self(), au_session); 137 } 138 my_CFRelease(&data); 139 return (result); 140} 141 142 143#if ! TARGET_OS_EMBEDDED 144static Boolean 145EAPOLControlAuthInfoIsValid(CFDictionaryRef * dict_p) 146{ 147 int count; 148 CFDictionaryRef dict; 149 const void * keys[4]; 150 int keys_count = sizeof(keys) / sizeof(keys[0]); 151 152 dict = *dict_p; 153 if (dict == NULL) { 154 return (TRUE); 155 } 156 if (isA_CFDictionary(dict) == NULL) { 157 return (FALSE); 158 } 159 count = CFDictionaryGetCount(dict); 160 if (count == 0) { 161 /* ignore it */ 162 *dict_p = NULL; 163 } 164 else if (count > keys_count) { 165 return (FALSE); 166 } 167 else { 168 int i; 169 170 CFDictionaryGetKeysAndValues(dict, keys, NULL); 171 for (i = 0; i < count; i++) { 172 if (CFEqual(keys[i], 173 kEAPClientPropSaveCredentialsOnSuccessfulAuthentication)) { 174 if (count == 1) { 175 /* no credentials specified, ignore it */ 176 EAPLOG_FL(LOG_NOTICE, 177 "Ignoring %@ since no credentials were specified", 178 keys[i]); 179 *dict_p = NULL; 180 } 181 } 182 else if (!CFEqual(keys[i], kEAPClientPropUserName) 183 && !CFEqual(keys[i], kEAPClientPropUserPassword) 184 && !CFEqual(keys[i], kEAPClientPropTLSIdentityHandle)) { 185 return (FALSE); 186 } 187 } 188 } 189 return (TRUE); 190} 191 192const CFStringRef kEAPOLControlStartOptionClientItemID = CFSTR("ClientItemID"); 193const CFStringRef kEAPOLControlStartOptionManagerName = CFSTR("ManagerName"); 194const CFStringRef kEAPOLControlStartOptionAuthenticationInfo = CFSTR("AuthenticationInfo"); 195 196 197int 198EAPOLControlStartWithOptions(const char * if_name, 199 EAPOLClientItemIDRef itemID, 200 CFDictionaryRef options) 201{ 202 CFDictionaryRef auth_info; 203 CFDictionaryRef config_dict; 204 int count; 205 CFDictionaryRef item_dict; 206 const void * keys[3]; 207 CFStringRef manager_name; 208 int ret; 209 const void * values[3]; 210 211 auth_info 212 = CFDictionaryGetValue(options, 213 kEAPOLControlStartOptionAuthenticationInfo); 214 if (EAPOLControlAuthInfoIsValid(&auth_info) == FALSE) { 215 return (EINVAL); 216 } 217 manager_name = CFDictionaryGetValue(options, 218 kEAPOLControlStartOptionManagerName); 219 if (manager_name != NULL && isA_CFString(manager_name) == NULL) { 220 return (EINVAL); 221 } 222 item_dict = EAPOLClientItemIDCopyDictionary(itemID); 223 if (item_dict == NULL) { 224 return (EINVAL); 225 } 226 count = 0; 227 keys[count] = (const void *)kEAPOLControlClientItemID; 228 values[count] = (const void *)item_dict; 229 count++; 230 if (auth_info != NULL) { 231 keys[count] = (const void *)kEAPOLControlEAPClientConfiguration; 232 values[count] = (const void *)auth_info; 233 count++; 234 } 235 if (manager_name != NULL) { 236 keys[count] = (const void *)kEAPOLControlManagerName; 237 values[count] = (const void *)manager_name; 238 count++; 239 } 240 config_dict = CFDictionaryCreate(NULL, keys, values, count, 241 &kCFTypeDictionaryKeyCallBacks, 242 &kCFTypeDictionaryValueCallBacks); 243 CFRelease(item_dict); 244 ret = EAPOLControlStart(if_name, config_dict); 245 if (config_dict != NULL) { 246 CFRelease(config_dict); 247 } 248 return (ret); 249} 250 251int 252EAPOLControlStartWithClientItemID(const char * if_name, 253 EAPOLClientItemIDRef itemID, 254 CFDictionaryRef auth_info) 255{ 256 CFDictionaryRef config_dict; 257 int count; 258 CFDictionaryRef item_dict; 259 const void * keys[2]; 260 int ret; 261 const void * values[2]; 262 263 if (EAPOLControlAuthInfoIsValid(&auth_info) == FALSE) { 264 return (EINVAL); 265 } 266 item_dict = EAPOLClientItemIDCopyDictionary(itemID); 267 if (item_dict == NULL) { 268 return (EINVAL); 269 } 270 271 keys[0] = (const void *)kEAPOLControlClientItemID; 272 values[0] = (const void *)item_dict; 273 count = 1; 274 if (auth_info != NULL) { 275 keys[1] = (const void *)kEAPOLControlEAPClientConfiguration; 276 values[1] = (const void *)auth_info; 277 count = 2; 278 } 279 config_dict = CFDictionaryCreate(NULL, keys, values, count, 280 &kCFTypeDictionaryKeyCallBacks, 281 &kCFTypeDictionaryValueCallBacks); 282 CFRelease(item_dict); 283 ret = EAPOLControlStart(if_name, config_dict); 284 if (config_dict != NULL) { 285 CFRelease(config_dict); 286 } 287 return (ret); 288} 289 290#endif /* ! TARGET_OS_EMBEDDED */ 291 292int 293EAPOLControlStop(const char * interface_name) 294{ 295 if_name_t if_name; 296 mach_port_t server; 297 int result = 0; 298 kern_return_t status; 299 300 if (get_server_port(&server, &status) == FALSE) { 301 result = ENXIO; 302 goto done; 303 } 304 strncpy(if_name, interface_name, sizeof(if_name)); 305 status = eapolcontroller_stop(server, 306 if_name, &result); 307 if (status != KERN_SUCCESS) { 308 mach_error("eapolcontroller_start failed", status); 309 result = ENXIO; 310 goto done; 311 } 312 done: 313 return (result); 314} 315 316int 317EAPOLControlUpdate(const char * interface_name, CFDictionaryRef config_dict) 318{ 319 CFDataRef data = NULL; 320 if_name_t if_name; 321 mach_port_t server; 322 int result = 0; 323 kern_return_t status; 324 325 if (get_server_port(&server, &status) == FALSE) { 326 result = ENXIO; 327 goto done; 328 } 329 if (isA_CFDictionary(config_dict) == NULL) { 330 result = EINVAL; 331 goto done; 332 } 333 data = CFPropertyListCreateXMLData(NULL, config_dict); 334 if (data == NULL) { 335 result = ENOMEM; 336 goto done; 337 } 338 strncpy(if_name, interface_name, sizeof(if_name)); 339 status = eapolcontroller_update(server, if_name, 340 (xmlDataOut_t)CFDataGetBytePtr(data), 341 CFDataGetLength(data), 342 &result); 343 if (status != KERN_SUCCESS) { 344 mach_error("eapolcontroller_update failed", status); 345 result = ENXIO; 346 goto done; 347 } 348 done: 349 my_CFRelease(&data); 350 return (result); 351} 352 353int 354EAPOLControlRetry(const char * interface_name) 355{ 356 if_name_t if_name; 357 int result = 0; 358 mach_port_t server; 359 kern_return_t status; 360 361 if (get_server_port(&server, &status) == FALSE) { 362 result = ENXIO; 363 goto done; 364 } 365 strncpy(if_name, interface_name, sizeof(if_name)); 366 status = eapolcontroller_retry(server, if_name, &result); 367 if (status != KERN_SUCCESS) { 368 mach_error("eapolcontroller_retry failed", status); 369 result = ENXIO; 370 goto done; 371 } 372 done: 373 return (result); 374} 375 376int 377EAPOLControlProvideUserInput(const char * interface_name, 378 CFDictionaryRef user_input) 379{ 380 CFDataRef data = NULL; 381 if_name_t if_name; 382 mach_port_t server; 383 int result = 0; 384 kern_return_t status; 385 xmlDataOut_t xml_data = NULL; 386 CFIndex xml_data_len = 0; 387 388 if (get_server_port(&server, &status) == FALSE) { 389 result = ENXIO; 390 goto done; 391 } 392 if (user_input != NULL) { 393 if (isA_CFDictionary(user_input) == NULL) { 394 result = EINVAL; 395 goto done; 396 } 397 data = CFPropertyListCreateXMLData(NULL, user_input); 398 if (data == NULL) { 399 result = ENOMEM; 400 goto done; 401 } 402 xml_data = (xmlDataOut_t)CFDataGetBytePtr(data); 403 xml_data_len = CFDataGetLength(data); 404 } 405 strncpy(if_name, interface_name, sizeof(if_name)); 406 status = eapolcontroller_provide_user_input(server, 407 if_name, xml_data, 408 xml_data_len, &result); 409 if (status != KERN_SUCCESS) { 410 mach_error("eapolcontroller_provide_user_input failed", status); 411 result = ENXIO; 412 goto done; 413 } 414 done: 415 my_CFRelease(&data); 416 return (result); 417} 418 419int 420EAPOLControlCopyStateAndStatus(const char * interface_name, 421 EAPOLControlState * state, 422 CFDictionaryRef * status_dict_p) 423{ 424 if_name_t if_name; 425 int result = 0; 426 mach_port_t server; 427 kern_return_t status; 428 xmlDataOut_t status_data = NULL; 429 unsigned int status_data_len = 0; 430 431 *status_dict_p = NULL; 432 if (get_server_port(&server, &status) == FALSE) { 433 result = ENXIO; 434 goto done; 435 } 436 strncpy(if_name, interface_name, sizeof(if_name)); 437 status = eapolcontroller_copy_status(server, 438 if_name, 439 &status_data, &status_data_len, 440 (int *)state, &result); 441 if (status != KERN_SUCCESS) { 442 mach_error("eapolcontroller_copy_status failed", status); 443 result = ENXIO; 444 goto done; 445 } 446 if (status_data != NULL) { 447 *status_dict_p = 448 my_CFPropertyListCreateWithBytePtrAndLength(status_data, 449 status_data_len); 450 (void)vm_deallocate(mach_task_self(), (vm_address_t)status_data, 451 status_data_len); 452 if (*status_dict_p == NULL) { 453 result = ENOMEM; 454 goto done; 455 } 456 } 457 458 done: 459 return (result); 460} 461 462int 463EAPOLControlSetLogLevel(const char * interface_name, int32_t level) 464{ 465 return (EINVAL); 466} 467 468CFStringRef 469EAPOLControlKeyCreate(const char * interface_name) 470{ 471 CFStringRef if_name_cf; 472 CFStringRef str; 473 474 if_name_cf = CFStringCreateWithCString(NULL, interface_name, 475 kCFStringEncodingASCII); 476 str = SCDynamicStoreKeyCreateNetworkInterfaceEntity(NULL, 477 kSCDynamicStoreDomainState, 478 if_name_cf, 479 kSCEntNetEAPOL); 480 my_CFRelease(&if_name_cf); 481 return (str); 482} 483 484#if ! TARGET_OS_EMBEDDED 485int 486EAPOLControlStartSystem(const char * interface_name, CFDictionaryRef options) 487{ 488 CFDataRef data = NULL; 489 if_name_t if_name; 490 mach_port_t server; 491 int result = 0; 492 kern_return_t status; 493 xmlDataOut_t xml_data = NULL; 494 CFIndex xml_data_len = 0; 495 496 if (get_server_port(&server, &status) == FALSE) { 497 result = ENXIO; 498 goto done; 499 } 500 if (options != NULL) { 501 if (isA_CFDictionary(options) == NULL) { 502 result = EINVAL; 503 goto done; 504 } 505 data = CFPropertyListCreateXMLData(NULL, options); 506 if (data == NULL) { 507 result = ENOMEM; 508 goto done; 509 } 510 xml_data = (xmlDataOut_t)CFDataGetBytePtr(data); 511 xml_data_len = CFDataGetLength(data); 512 } 513 strncpy(if_name, interface_name, sizeof(if_name)); 514 status = eapolcontroller_start_system(server, if_name, 515 xml_data, xml_data_len, 516 &result); 517 if (status != KERN_SUCCESS) { 518 EAPLOG_FL(LOG_NOTICE, "eapolcontroller_start_system failed, %s (%d)", 519 mach_error_string(status), status); 520 result = ENXIO; 521 goto done; 522 } 523 if (result != 0) { 524 EAPLOG_FL(LOG_NOTICE, "eapolcontroller_start_system: result is %d", 525 result); 526 } 527 done: 528 my_CFRelease(&data); 529 return (result); 530} 531 532int 533EAPOLControlStartSystemWithClientItemID(const char * interface_name, 534 EAPOLClientItemIDRef itemID) 535{ 536 CFDictionaryRef config_dict; 537 CFDictionaryRef item_dict; 538 CFStringRef key; 539 int ret; 540 541 item_dict = EAPOLClientItemIDCopyDictionary(itemID); 542 if (item_dict == NULL) { 543 return (EINVAL); 544 } 545 key = kEAPOLControlClientItemID; 546 config_dict = CFDictionaryCreate(NULL, 547 (const void * *)&key, 548 (const void * *)&item_dict, 549 1, 550 &kCFTypeDictionaryKeyCallBacks, 551 &kCFTypeDictionaryValueCallBacks); 552 CFRelease(item_dict); 553 ret = EAPOLControlStartSystem(interface_name, config_dict); 554 if (config_dict != NULL) { 555 CFRelease(config_dict); 556 } 557 return (ret); 558} 559 560static int 561S_copy_loginwindow_config(const char * interface_name, 562 CFDictionaryRef * ret_config_p) 563{ 564 CFDictionaryRef dict = NULL; 565 if_name_t if_name; 566 int result = 0; 567 mach_port_t server; 568 kern_return_t status; 569 xmlDataOut_t config = NULL; 570 unsigned int config_len = 0; 571 572 if (get_server_port(&server, &status) == FALSE) { 573 result = ENXIO; 574 goto done; 575 } 576 strncpy(if_name, interface_name, sizeof(if_name)); 577 status = eapolcontroller_copy_loginwindow_config(server, 578 if_name, 579 &config, 580 &config_len, 581 &result); 582 if (status != KERN_SUCCESS) { 583 EAPLOG_FL(LOG_NOTICE, 584 "eapolcontroller_copy_loginwindow_config failed, %s (%d)", 585 mach_error_string(status), status); 586 result = ENXIO; 587 goto done; 588 } 589 if (result != 0 || config == NULL) { 590 if (result == 0) { 591 /* should not happen */ 592 result = ENOENT; 593 } 594 goto done; 595 } 596 dict = my_CFPropertyListCreateWithBytePtrAndLength(config, 597 config_len); 598 (void)vm_deallocate(mach_task_self(), (vm_address_t)config, 599 config_len); 600 if (dict == NULL) { 601 result = ENOMEM; 602 goto done; 603 } 604 605 done: 606 *ret_config_p = dict; 607 return (result); 608} 609 610/* 611 * Function: EAPOLControlCopyLoginWindowConfiguration 612 * Purpose: 613 * If LoginWindow mode is activated during this login session, returns the 614 * configuration that was used. This value is cleared when the user logs out. 615 * 616 * Returns: 617 * 0 and non-NULL CFDictionaryRef value in *config_p on success, 618 * non-zero on failure 619 */ 620int 621EAPOLControlCopyLoginWindowConfiguration(const char * interface_name, 622 CFDictionaryRef * ret_config_p) 623{ 624 CFDictionaryRef dict = NULL; 625 int result = 0; 626 627 result = S_copy_loginwindow_config(interface_name, &dict); 628 if (result != 0) { 629 goto done; 630 } 631 632 /* if this contains an EAPOLClientItemID dictionary, return NULL */ 633 if (CFDictionaryContainsKey(dict, kEAPOLControlClientItemID)) { 634 my_CFRelease(&dict); 635 result = ENOENT; 636 } 637 638 done: 639 *ret_config_p = dict; 640 return (result); 641} 642 643/* 644 * Function: EAPOLControlCopyLoginWindowClientItemID 645 * 646 * Purpose: 647 * If LoginWindow mode is activated during this login session, returns the 648 * EAPOLClientItemIDRef corresponding to the profile that was used. The 649 * information is cleared when the user logs out. 650 * 651 * Returns: 652 * 0 and non-NULL EAPOLClientItemIDRef on success, 653 * non-zero errno on failure. 654 */ 655int 656EAPOLControlCopyLoginWindowClientItemID(const char * interface_name, 657 EAPOLClientItemIDRef * itemID_p) 658{ 659 EAPOLClientConfigurationRef cfg; 660 CFDictionaryRef dict = NULL; 661 EAPOLClientItemIDRef itemID = NULL; 662 CFDictionaryRef itemID_dict; 663 int result = 0; 664 665 result = S_copy_loginwindow_config(interface_name, &dict); 666 if (result != 0) { 667 goto done; 668 } 669 itemID_dict = CFDictionaryGetValue(dict, kEAPOLControlClientItemID); 670 if (itemID_dict == NULL) { 671 result = ENOENT; 672 goto done; 673 } 674 cfg = EAPOLClientConfigurationCreate(NULL); 675 if (cfg != NULL) { 676 itemID = EAPOLClientItemIDCreateWithDictionary(cfg, itemID_dict); 677 CFRelease(cfg); 678 } 679 if (itemID == NULL) { 680 result = ENOENT; 681 } 682 683 done: 684 my_CFRelease(&dict); 685 *itemID_p = itemID; 686 return (result); 687} 688 689int 690EAPOLControlCopyAutoDetectInformation(CFDictionaryRef * info_p) 691{ 692 int result = 0; 693 mach_port_t server; 694 kern_return_t status; 695 xmlDataOut_t info_data = NULL; 696 unsigned int info_data_len = 0; 697 698 *info_p = NULL; 699 if (get_server_port(&server, &status) == FALSE) { 700 result = ENXIO; 701 goto done; 702 } 703 status = eapolcontroller_copy_autodetect_info(server, 704 &info_data, &info_data_len, 705 &result); 706 if (status != KERN_SUCCESS) { 707 EAPLOG_FL(LOG_NOTICE, 708 "eapolcontroller_copy_autodetect_info failed, %s (%d)", 709 mach_error_string(status), status); 710 result = ENXIO; 711 goto done; 712 } 713 if (info_data != NULL) { 714 *info_p = 715 my_CFPropertyListCreateWithBytePtrAndLength(info_data, 716 info_data_len); 717 (void)vm_deallocate(mach_task_self(), (vm_address_t)info_data, 718 info_data_len); 719 if (*info_p == NULL) { 720 result = ENOMEM; 721 goto done; 722 } 723 } 724 725 done: 726 return (result); 727} 728 729boolean_t 730EAPOLControlDidUserCancel(const char * interface_name) 731{ 732 boolean_t cancelled = FALSE; 733 if_name_t if_name; 734 mach_port_t server; 735 kern_return_t status; 736 737 if (get_server_port(&server, &status) == FALSE) { 738 goto done; 739 } 740 strncpy(if_name, interface_name, sizeof(if_name)); 741 status = eapolcontroller_did_user_cancel(server, 742 if_name, 743 &cancelled); 744 if (status != KERN_SUCCESS) { 745 mach_error("eapolcontroller_copy_loginwindow_config failed", status); 746 } 747 done: 748 return (cancelled); 749} 750 751#define kEthernetAutoConnect CFSTR("EthernetAutoConnect") 752#define kEthernetAuthenticatorList CFSTR("EthernetAuthenticatorList") 753 754#define kProfileID CFSTR("ProfileID") 755 756#define kEAPOLControlUserSettingsApplicationID CFSTR("com.apple.network.eapolcontrol") 757 758const char * kEAPOLControlUserSettingsNotifyKey = "com.apple.network.eapolcontrol.user"; 759 760static int token; 761static bool token_valid; 762 763static void 764settings_change_check(void) 765{ 766 int check = 0; 767 uint32_t status; 768 769 if (!token_valid) { 770 status = notify_register_check(kEAPOLControlUserSettingsNotifyKey, 771 &token); 772 if (status != NOTIFY_STATUS_OK) { 773 EAPLOG_FL(LOG_NOTICE, "notify_register_check returned %d", status); 774 return; 775 } 776 token_valid = TRUE; 777 } 778 status = notify_check(token, &check); 779 if (status != NOTIFY_STATUS_OK) { 780 EAPLOG_FL(LOG_NOTICE, "notify_check returned %d", status); 781 return; 782 } 783 if (check != 0) { 784 CFPreferencesSynchronize(kEAPOLControlUserSettingsApplicationID, 785 kCFPreferencesCurrentUser, 786 kCFPreferencesCurrentHost); 787 } 788 return; 789} 790 791static void 792settings_change_notify(void) 793{ 794 uint32_t status; 795 796 status = notify_post(kEAPOLControlUserSettingsNotifyKey); 797 if (status != NOTIFY_STATUS_OK) { 798 EAPLOG_FL(LOG_NOTICE, "notify_post returned %d", status); 799 } 800 return; 801} 802 803static CFPropertyListRef 804EAPOLControlUserCopyValue(CFStringRef key) 805{ 806 settings_change_check(); 807 return (CFPreferencesCopyValue(key, 808 kEAPOLControlUserSettingsApplicationID, 809 kCFPreferencesCurrentUser, 810 kCFPreferencesCurrentHost)); 811} 812 813static Boolean 814EAPOLControlUserGetBooleanValue(CFStringRef key, Boolean def_value) 815{ 816 Boolean result; 817 CFBooleanRef val; 818 819 val = EAPOLControlUserCopyValue(key); 820 if (isA_CFBoolean(val) == NULL) { 821 result = def_value; 822 } 823 else { 824 result = CFBooleanGetValue(val); 825 } 826 my_CFRelease(&val); 827 return (result); 828} 829 830void 831EAPOLControlUserSetBooleanValue(CFStringRef key, Boolean enable) 832{ 833 CFPreferencesSetValue(key, 834 enable ? kCFBooleanTrue : kCFBooleanFalse, 835 kEAPOLControlUserSettingsApplicationID, 836 kCFPreferencesCurrentUser, 837 kCFPreferencesCurrentHost); 838 CFPreferencesSynchronize(kEAPOLControlUserSettingsApplicationID, 839 kCFPreferencesCurrentUser, 840 kCFPreferencesCurrentHost); 841 842 settings_change_notify(); 843 return; 844} 845 846void 847EAPOLControlSetUserAutoConnectEnabled(Boolean enable) 848{ 849 EAPOLControlUserSetBooleanValue(kEthernetAutoConnect, enable); 850 return; 851} 852 853Boolean 854EAPOLControlIsUserAutoConnectEnabled(void) 855{ 856 return (EAPOLControlUserGetBooleanValue(kEthernetAutoConnect, TRUE)); 857} 858 859 860#define EA_FORMAT "%02x:%02x:%02x:%02x:%02x:%02x" 861#define EA_CH(e, i) ((u_char)((u_char *)(e))[(i)]) 862#define EA_LIST(ea) EA_CH(ea,0),EA_CH(ea,1),EA_CH(ea,2),EA_CH(ea,3),EA_CH(ea,4),EA_CH(ea,5) 863 864static CFStringRef 865S_authenticator_copy_string(CFDataRef authenticator) 866{ 867 const UInt8 * bytes; 868 869 if (CFDataGetLength(authenticator) != sizeof(struct ether_addr)) { 870 return (NULL); 871 } 872 bytes = CFDataGetBytePtr(authenticator); 873 return (CFStringCreateWithFormat(NULL, NULL, CFSTR(EA_FORMAT), 874 EA_LIST(bytes))); 875} 876 877EAPOLClientItemIDRef 878EAPOLControlCopyItemIDForAuthenticator(CFDataRef authenticator) 879{ 880 CFDictionaryRef binding; 881 CFDictionaryRef list; 882 EAPOLClientItemIDRef itemID = NULL; 883 CFStringRef str; 884 885 list = EAPOLControlUserCopyValue(kEthernetAuthenticatorList); 886 if (isA_CFDictionary(list) == NULL) { 887 goto done; 888 } 889 str = S_authenticator_copy_string(authenticator); 890 binding = CFDictionaryGetValue(list, str); 891 if (isA_CFDictionary(binding) != NULL) { 892 CFStringRef profileID; 893 894 profileID = CFDictionaryGetValue(binding, kProfileID); 895 if (isA_CFString(profileID) != NULL) { 896 EAPOLClientConfigurationRef cfg; 897 EAPOLClientProfileRef profile; 898 899 cfg = EAPOLClientConfigurationCreate(NULL); 900 profile = EAPOLClientConfigurationGetProfileWithID(cfg, profileID); 901 if (profile != NULL) { 902 itemID = EAPOLClientItemIDCreateWithProfile(profile); 903 } 904 else { 905 /* profile is no longer present */ 906 } 907 CFRelease(cfg); 908 } 909 else { 910 itemID = EAPOLClientItemIDCreateDefault(); 911 } 912 } 913 if (str != NULL) { 914 CFRelease(str); 915 } 916 917 done: 918 my_CFRelease(&list); 919 return (itemID); 920} 921 922void 923EAPOLControlSetItemIDForAuthenticator(CFDataRef authenticator, 924 EAPOLClientItemIDRef itemID) 925{ 926 CFDictionaryRef binding = NULL; 927 Boolean changed = FALSE; 928 CFDictionaryRef list; 929 CFStringRef profileID = NULL; 930 CFStringRef str; 931 932 list = EAPOLControlUserCopyValue(kEthernetAuthenticatorList); 933 str = S_authenticator_copy_string(authenticator); 934 if (itemID == NULL) { 935 /* if there was a previous binding, we need to remove it */ 936 if (list != NULL && CFDictionaryGetValue(list, str) != NULL) { 937 changed = TRUE; 938 } 939 } 940 else { 941 EAPOLClientProfileRef profile = EAPOLClientItemIDGetProfile(itemID); 942 CFStringRef prev_profileID = NULL; 943 944 if (profile != NULL) { 945 profileID = EAPOLClientProfileGetID(profile); 946 } 947 if (list != NULL) { 948 binding = isA_CFDictionary(CFDictionaryGetValue(list, str)); 949 if (binding != NULL) { 950 prev_profileID 951 = isA_CFString(CFDictionaryGetValue(binding, kProfileID)); 952 } 953 } 954 if (my_CFEqual(prev_profileID, profileID) == FALSE) { 955 changed = TRUE; 956 } 957 else if (binding == NULL && profile == NULL) { 958 /* no existing binding, and the user chose Default */ 959 changed = TRUE; 960 } 961 } 962 if (changed) { 963 CFMutableDictionaryRef new_list; 964 965 if (isA_CFDictionary(list) != NULL) { 966 new_list = CFDictionaryCreateMutableCopy(NULL, 0, list); 967 } 968 else { 969 new_list 970 = CFDictionaryCreateMutable(NULL, 0, 971 &kCFTypeDictionaryKeyCallBacks, 972 &kCFTypeDictionaryValueCallBacks); 973 } 974 if (itemID == NULL) { 975 /* clear the binding */ 976 CFDictionaryRemoveValue(new_list, str); 977 } 978 else { 979 CFMutableDictionaryRef new_binding = NULL; 980 981 if (binding != NULL) { 982 new_binding = CFDictionaryCreateMutableCopy(NULL, 0, binding); 983 } 984 else { 985 new_binding 986 = CFDictionaryCreateMutable(NULL, 0, 987 &kCFTypeDictionaryKeyCallBacks, 988 &kCFTypeDictionaryValueCallBacks); 989 } 990 if (profileID != NULL) { 991 CFDictionarySetValue(new_binding, kProfileID, profileID); 992 } 993 else { 994 CFDictionaryRemoveValue(new_binding, kProfileID); 995 } 996 CFDictionarySetValue(new_list, str, new_binding); 997 CFRelease(new_binding); 998 999 } 1000 CFPreferencesSetValue(kEthernetAuthenticatorList, 1001 new_list, 1002 kEAPOLControlUserSettingsApplicationID, 1003 kCFPreferencesCurrentUser, 1004 kCFPreferencesCurrentHost); 1005 CFPreferencesSynchronize(kEAPOLControlUserSettingsApplicationID, 1006 kCFPreferencesCurrentUser, 1007 kCFPreferencesCurrentHost); 1008 settings_change_notify(); 1009 CFRelease(new_list); 1010 } 1011 my_CFRelease(&list); 1012 my_CFRelease(&str); 1013 return; 1014} 1015 1016#endif /* ! TARGET_OS_EMBEDDED */ 1017 1018CFStringRef 1019EAPOLControlAnyInterfaceKeyCreate(void) 1020{ 1021 CFStringRef str; 1022 str = SCDynamicStoreKeyCreateNetworkInterfaceEntity(NULL, 1023 kSCDynamicStoreDomainState, 1024 kSCCompAnyRegex, 1025 kSCEntNetEAPOL); 1026 return (str); 1027} 1028 1029/* 1030 * Function: copy_component 1031 * Purpose: 1032 * Given a string 'key' and a string prefix 'prefix', 1033 * return the next component in the slash '/' separated 1034 * key. If no slash follows the prefix, return NULL. 1035 * 1036 * Examples: 1037 * 1. key = "a/b/c" prefix = "a/" 1038 * returns "b" 1039 * 2. key = "a/b/c" prefix = "a/b/" 1040 * returns NULL 1041 */ 1042static CFStringRef 1043copy_component(CFStringRef key, CFStringRef prefix) 1044{ 1045 CFMutableStringRef comp; 1046 CFRange range; 1047 1048 if (CFStringHasPrefix(key, prefix) == FALSE) { 1049 return (NULL); 1050 } 1051 comp = CFStringCreateMutableCopy(NULL, 0, key); 1052 if (comp == NULL) { 1053 return (NULL); 1054 } 1055 CFStringDelete(comp, CFRangeMake(0, CFStringGetLength(prefix))); 1056 range = CFStringFind(comp, CFSTR("/"), 0); 1057 if (range.location == kCFNotFound) { 1058 CFRelease(comp); 1059 return (NULL); 1060 } 1061 range.length = CFStringGetLength(comp) - range.location; 1062 CFStringDelete(comp, range); 1063 return (comp); 1064} 1065 1066CFStringRef 1067EAPOLControlKeyCopyInterface(CFStringRef key) 1068{ 1069 static CFStringRef prefix = NULL; 1070 1071 if (CFStringHasSuffix(key, kSCEntNetEAPOL) == FALSE) { 1072 return (NULL); 1073 } 1074 if (prefix == NULL) { 1075 prefix = SCDynamicStoreKeyCreate(NULL, 1076 CFSTR("%@/%@/%@/"), 1077 kSCDynamicStoreDomainState, 1078 kSCCompNetwork, 1079 kSCCompInterface); 1080 } 1081 return (copy_component(key, prefix)); 1082} 1083