1/* 2 * Copyright (c) 2002-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#include <stdio.h> 25#include <unistd.h> 26#include <stdlib.h> 27#include <sys/time.h> 28#include <mach/mach.h> 29#include <mach/message.h> 30#include <mach/boolean.h> 31#include <mach/mach_error.h> 32#include <servers/bootstrap.h> 33#include <syslog.h> 34#include <CoreFoundation/CFDictionary.h> 35#include <CoreFoundation/CFString.h> 36#include <SystemConfiguration/SCPrivate.h> 37#include <SystemConfiguration/SCValidation.h> 38#include <SystemConfiguration/SystemConfiguration.h> 39#include <SystemConfiguration/SCDynamicStorePrivate.h> 40#include "EAPOLControl.h" 41#include "EAPOLControlPrivate.h" 42#include "EAPOLControlPrefs.h" 43#include "myCFUtil.h" 44#include <TargetConditionals.h> 45 46typedef int func_t(int argc, char * argv[]); 47typedef func_t * funcptr_t; 48 49static char * progname = NULL; 50static int S_command_index; 51 52static void command_usage(); 53static void usage(); 54 55void 56timestamp_fprintf(FILE * f, const char * message, ...) 57{ 58 struct timeval tv; 59 struct tm tm; 60 time_t t; 61 va_list ap; 62 63 (void)gettimeofday(&tv, NULL); 64 t = tv.tv_sec; 65 (void)localtime_r(&t, &tm); 66 67 va_start(ap, message); 68 fprintf(f, "%04d/%02d/%02d %2d:%02d:%02d.%06d ", 69 tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday, 70 tm.tm_hour, tm.tm_min, tm.tm_sec, 71 tv.tv_usec); 72 vfprintf(f, message, ap); 73 va_end(ap); 74} 75 76static const char * 77S_state_names(EAPOLControlState state) 78{ 79 static const char * names[] = { 80 "Idle", "Starting", "Running", "Stopping" 81 }; 82 if (state <= kEAPOLControlStateStopping) { 83 return (names[state]); 84 } 85 return ("<unknown>"); 86} 87 88static void 89dump_plist(FILE * f, CFTypeRef p) 90{ 91 CFDataRef data; 92 93 data = CFPropertyListCreateData(NULL, p, 94 kCFPropertyListXMLFormat_v1_0, 95 0, NULL); 96 if (data == NULL) { 97 return; 98 } 99 fwrite(CFDataGetBytePtr(data), CFDataGetLength(data), 1, f); 100 CFRelease(data); 101 return; 102} 103 104static int 105get_eapol_interface_status(const char * ifname) 106{ 107 CFDictionaryRef dict = NULL; 108 int result; 109 EAPOLControlState state; 110 111 result = EAPOLControlCopyStateAndStatus(ifname, &state, &dict); 112 if (result == 0) { 113 fprintf(stdout, "EAPOLControlCopyStateAndStatus(%s) = %s\n", ifname, 114 S_state_names(state)); 115 if (dict != NULL) { 116 fprintf(stdout, "Status dict:\n"); 117 dump_plist(stdout, dict); 118 CFRelease(dict); 119 } 120 } 121 else { 122 fprintf(stderr, "EAPOLControlCopyStateAndStatus(%s) returned %d\n", ifname, result); 123 } 124 return (result); 125 126} 127 128static SCDynamicStoreRef 129config_session_start(SCDynamicStoreCallBack func, void * arg, const char * ifname) 130{ 131 SCDynamicStoreContext context; 132 CFStringRef key; 133 SCDynamicStoreRef store; 134 135 bzero(&context, sizeof(context)); 136 context.info = arg; 137 store = SCDynamicStoreCreate(NULL, CFSTR("/usr/local/bin/eapoltest"), 138 func, &context); 139 if (store == NULL) { 140 fprintf(stderr, "SCDynamicStoreCreate() failed, %s", 141 SCErrorString(SCError())); 142 return (NULL); 143 } 144 /* EAPClient status notifications */ 145 if (ifname == NULL) { 146 /* watch all interfaces */ 147 CFArrayRef patterns; 148 149 key = EAPOLControlAnyInterfaceKeyCreate(); 150 patterns = CFArrayCreate(NULL, (const void **)&key, 1, &kCFTypeArrayCallBacks); 151 CFRelease(key); 152 SCDynamicStoreSetNotificationKeys(store, NULL, patterns); 153 CFRelease(patterns); 154 } 155 else { 156 /* watch just one interface */ 157 CFArrayRef keys = NULL; 158 159 key = EAPOLControlKeyCreate(ifname); 160 keys = CFArrayCreate(NULL, (const void **)&key, 1, &kCFTypeArrayCallBacks); 161 CFRelease(key); 162 SCDynamicStoreSetNotificationKeys(store, keys, NULL); 163 CFRelease(keys); 164 } 165 return (store); 166} 167 168static int 169cfstring_to_cstring(CFStringRef cfstr, char * str, int len) 170{ 171 CFIndex l; 172 CFIndex n; 173 CFRange range; 174 175 range = CFRangeMake(0, CFStringGetLength(cfstr)); 176 (void)CFStringGetBytes(cfstr, range, kCFStringEncodingMacRoman, 177 0, FALSE, (uint8_t *)str, len, &l); 178 str[l] = '\0'; 179 return ((int)l); 180} 181 182static void 183monitor_eapol_change(SCDynamicStoreRef store, CFArrayRef changes, void * arg) 184{ 185 CFIndex count; 186 int i; 187 188 count = CFArrayGetCount(changes); 189 for (i = 0; i < count; i++) { 190 CFStringRef key = CFArrayGetValueAtIndex(changes, i); 191 CFStringRef interface = NULL; 192 char ifname[16]; 193 194 interface = EAPOLControlKeyCopyInterface(key); 195 if (interface == NULL) { 196 continue; 197 } 198 cfstring_to_cstring(interface, ifname, sizeof(ifname)); 199 CFRelease(interface); 200 timestamp_fprintf(stdout, "%s changed\n", ifname); 201 get_eapol_interface_status(ifname); 202 printf("\n"); 203 } 204 return; 205} 206 207static int 208S_monitor(int argc, char * argv[]) 209{ 210 const char * ifname = NULL; 211 SCDynamicStoreRef store = NULL; 212 CFRunLoopSourceRef rls = NULL; 213 214 if (argc > 0) { 215 ifname = argv[0]; 216 get_eapol_interface_status(ifname); 217 } 218 219 store = config_session_start(monitor_eapol_change, NULL, ifname); 220 rls = SCDynamicStoreCreateRunLoopSource(NULL, store, 0); 221 CFRunLoopAddSource(CFRunLoopGetCurrent(), rls, kCFRunLoopDefaultMode); 222 CFRelease(rls); 223 CFRunLoopRun(); 224 225 /* not reached */ 226 return (0); 227} 228 229static int 230S_state(int argc, char * argv[]) 231{ 232 return (get_eapol_interface_status(argv[0])); 233} 234 235static int 236S_start(int argc, char * argv[]) 237{ 238 CFDictionaryRef dict = NULL; 239 int result; 240 241 if (access(argv[1], R_OK) != 0) { 242 fprintf(stderr, "%s: %s\n", argv[1], strerror(errno)); 243 return (errno); 244 } 245 dict = (CFDictionaryRef)my_CFPropertyListCreateFromFile(argv[1]); 246 if (isA_CFDictionary(dict) == NULL) { 247 fprintf(stderr, "contents of file %s invalid\n", argv[1]); 248 my_CFRelease(&dict); 249 return (EINVAL); 250 } 251 result = EAPOLControlStart(argv[0], dict); 252 fprintf(stderr, "EAPOLControlStart returned %d\n", result); 253 my_CFRelease(&dict); 254 return (result); 255} 256 257#if ! TARGET_OS_EMBEDDED 258static int 259S_start_system(int argc, char * argv[]) 260{ 261 CFDictionaryRef dict = NULL; 262 int result; 263 264 if (argc > 1) { 265 if (access(argv[1], R_OK) != 0) { 266 fprintf(stderr, "%s: %s\n", argv[1], strerror(errno)); 267 return (errno); 268 } 269 dict = (CFDictionaryRef)my_CFPropertyListCreateFromFile(argv[1]); 270 if (isA_CFDictionary(dict) == NULL) { 271 fprintf(stderr, "contents of file %s invalid\n", argv[1]); 272 my_CFRelease(&dict); 273 return (EINVAL); 274 } 275 } 276 result = EAPOLControlStartSystem(argv[0], dict); 277 fprintf(stderr, "EAPOLControlStartSystem returned %d\n", result); 278 my_CFRelease(&dict); 279 return (result); 280} 281#endif /* ! TARGET_OS_EMBEDDED */ 282 283static int 284S_stop(int argc, char * argv[]) 285{ 286 int result; 287 288 result = EAPOLControlStop(argv[0]); 289 fprintf(stderr, "EAPOLControlStop returned %d\n", result); 290 return (result); 291} 292 293static int 294S_retry(int argc, char * argv[]) 295{ 296 int result; 297 298 result = EAPOLControlRetry(argv[0]); 299 fprintf(stderr, "EAPOLControlRetry returned %d\n", result); 300 return (result); 301} 302 303static int 304S_update(int argc, char * argv[]) 305{ 306 CFDictionaryRef dict = NULL; 307 int result; 308 309 if (access(argv[1], R_OK) != 0) { 310 fprintf(stderr, "%s: %s\n", argv[1], strerror(errno)); 311 return (errno); 312 } 313 dict = (CFDictionaryRef)my_CFPropertyListCreateFromFile(argv[1]); 314 if (isA_CFDictionary(dict) == NULL) { 315 fprintf(stderr, "contents of file %s invalid\n", argv[1]); 316 my_CFRelease(&dict); 317 return (EINVAL); 318 } 319 result = EAPOLControlUpdate(argv[0], dict); 320 fprintf(stderr, "EAPOLControlUpdate returned %d\n", result); 321 my_CFRelease(&dict); 322 return (result); 323} 324 325static int 326S_input(int argc, char * argv[]) 327{ 328 CFDictionaryRef dict = NULL; 329 int result; 330 331 if (argc > 1) { 332 if (access(argv[1], R_OK) != 0) { 333 fprintf(stderr, "%s: %s\n", argv[1], strerror(errno)); 334 return (errno); 335 } 336 dict = (CFDictionaryRef)my_CFPropertyListCreateFromFile(argv[1]); 337 if (isA_CFDictionary(dict) == NULL) { 338 fprintf(stderr, "contents of file %s invalid\n", argv[1]); 339 my_CFRelease(&dict); 340 return (EINVAL); 341 } 342 } 343 result = EAPOLControlProvideUserInput(argv[0], dict); 344 fprintf(stderr, "EAPOLControlProvideUserInput returned %d\n", result); 345 my_CFRelease(&dict); 346 return (result); 347} 348 349static int 350S_set_verbose(int argc, char * argv[]) 351{ 352 uint32_t log_flags = 0; 353 int result; 354 355 if (strcasecmp(argv[0], "on") == 0) { 356 log_flags = -1; 357 } 358 else if (strcasecmp(argv[0], "off") == 0) { 359 log_flags = 0; 360 } 361 else { 362 command_usage(); 363 } 364 if (EAPOLControlPrefsSetLogFlags(log_flags) == FALSE) { 365 fprintf(stderr, "Failed to set verbose %s\n", 366 log_flags != 0 ? "on" : "off"); 367 result = 1; 368 } 369 else { 370 result = 0; 371 } 372 return (result); 373} 374 375#if ! TARGET_OS_EMBEDDED 376static int 377S_loginwindow_config(int argc, char * argv[]) 378{ 379 CFDictionaryRef dict = NULL; 380 char * ifname = argv[0]; 381 int result; 382 383 result = EAPOLControlCopyLoginWindowConfiguration(ifname, &dict); 384 if (result == 0) { 385 fprintf(stdout, 386 "EAPOLControlCopyLoginWindowConfiguration(%s):\n", ifname); 387 if (dict != NULL) { 388 dump_plist(stdout, dict); 389 CFRelease(dict); 390 } 391 } 392 else { 393 fprintf(stderr, 394 "EAPOLControlCopyLoginWindowConfiguration(%s) returned %d\n", 395 ifname, result); 396 } 397 return (result); 398} 399 400static int 401S_set_user_autoconnect(int argc, char * argv[]) 402{ 403 Boolean enable = FALSE; 404 const char * enable_str = argv[0]; 405 int result; 406 407 if (strcasecmp(enable_str, "on") == 0) { 408 enable = TRUE; 409 } 410 else if (strcasecmp(enable_str, "off") == 0) { 411 enable = FALSE; 412 } 413 else { 414 command_usage(); 415 } 416 EAPOLControlSetUserAutoConnectEnabled(enable); 417 return (0); 418} 419 420static int 421S_get_user_autoconnect(int argc, char * argv[]) 422{ 423 Boolean enable; 424 425 enable = EAPOLControlIsUserAutoConnectEnabled(); 426 printf("%s\n", enable ? "on" : "off"); 427 return (0); 428} 429 430static int 431S_did_user_cancel(int argc, char * argv[]) 432{ 433 boolean_t did_cancel; 434 435 did_cancel = EAPOLControlDidUserCancel(argv[0]); 436 printf("%s\n", did_cancel ? "true" : "false"); 437 return (0); 438} 439 440static int 441S_show_autodetect_info(int argc, char * argv[]) 442{ 443 CFDictionaryRef info; 444 int result; 445 446 result = EAPOLControlCopyAutoDetectInformation(&info); 447 if (info != NULL) { 448 CFShow(info); 449 CFRelease(info); 450 } 451 return (result); 452} 453 454#endif /* ! TARGET_OS_EMBEDDED */ 455 456static int 457S_wait_for_state(const char * ifname, 458 SCDynamicStoreRef store, EAPOLControlState desired_state, 459 EAPOLControlState ok_state) 460{ 461 int result; 462 463 while (1) { 464 EAPOLControlState state; 465 CFDictionaryRef status_dict = NULL; 466 467 result = EAPOLControlCopyStateAndStatus(ifname, &state, &status_dict); 468 if (result != 0) { 469 fprintf(stderr, 470 "EAPOLControlCopyStateAndStatus(%s) returned %d (%s)\n", 471 ifname, result, strerror(result)); 472 break; 473 } 474 my_CFRelease(&status_dict); 475 if (state == desired_state) { 476 break; 477 } 478 if (state != ok_state) { 479 fprintf(stderr, 480 "EAPOLControlState on %s is %d (!= %d)\n", 481 ifname, state, ok_state); 482 result = EINVAL; 483 break; 484 } 485 if (SCDynamicStoreNotifyWait(store) == FALSE) { 486 fprintf(stderr, "SCDynamicStoreNotifyWait failed\n"); 487 result = EINVAL; 488 break; 489 } 490 } 491 return (result); 492} 493 494static int 495S_stress_start(int argc, char * argv[]) 496{ 497 CFDictionaryRef dict = NULL; 498 int i; 499 char * ifname = argv[0]; 500 int result; 501 SCDynamicStoreRef store; 502 503 if (access(argv[1], R_OK) != 0) { 504 fprintf(stderr, "%s: %s\n", argv[1], strerror(errno)); 505 return (errno); 506 } 507 dict = (CFDictionaryRef)my_CFPropertyListCreateFromFile(argv[1]); 508 if (isA_CFDictionary(dict) == NULL) { 509 fprintf(stderr, "contents of file %s invalid\n", argv[1]); 510 my_CFRelease(&dict); 511 return (EINVAL); 512 } 513 store = config_session_start(NULL, NULL, ifname); 514 if (store == NULL) { 515 CFRelease(dict); 516 return (EINVAL); 517 } 518 519 for (i = 0; TRUE; i++) { 520 result = EAPOLControlStart(ifname, dict); 521 if (result != 0) { 522 fprintf(stderr, "EAPOLControlStart(%s) returned %d (%s)\n", 523 ifname, result, strerror(result)); 524 break; 525 } 526 result = S_wait_for_state(ifname, store, 527 kEAPOLControlStateRunning, 528 kEAPOLControlStateStarting); 529 if (result != 0) { 530 fprintf(stderr, "Waiting for Running failed\n"); 531 break; 532 } 533 result = EAPOLControlStop(ifname); 534 if (result != 0) { 535 fprintf(stderr, "EAPOLControlStop(%s) returned %d (%s)\n", 536 ifname, result, strerror(result)); 537 break; 538 } 539 (void)S_wait_for_state(ifname, store, 540 kEAPOLControlStateIdle, 541 kEAPOLControlStateStopping); 542 } 543 fprintf(stderr, "Failed at iteration %d\n", i + 1); 544 my_CFRelease(&dict); 545 return (result); 546} 547 548#include <EAP8021X/EAPCertificateUtil.h> 549 550static CFStringRef 551identity_copy_username(SecIdentityRef identity) 552{ 553 SecCertificateRef cert; 554 OSStatus status; 555 CFStringRef username; 556 557 status = SecIdentityCopyCertificate(identity, &cert); 558 if (status != noErr) { 559 fprintf(stderr, "SecIdentityCopyCertificate failed %ld\n", 560 (long)status); 561 return (NULL); 562 } 563 username = EAPSecCertificateCopyUserNameString(cert); 564 CFRelease(cert); 565 return (username); 566} 567 568static int 569S_show_identities(int argc, char * argv[]) 570{ 571 CFIndex count; 572 int i; 573 CFArrayRef list; 574 OSStatus status; 575 576 status = EAPSecIdentityListCreate(&list); 577 if (status != noErr) { 578 fprintf(stderr, "EAPSecIdentityListCreate returned %ld\n", 579 (long)status); 580 return (-1); 581 } 582 count = CFArrayGetCount(list); 583 printf("Number of identities: %d\n", (int)count); 584 for (i = 0; i < count; i++) { 585 EAPSecIdentityHandleRef handle; 586 SecIdentityRef identity; 587 CFStringRef username; 588 589 identity = (SecIdentityRef)CFArrayGetValueAtIndex(list, i); 590 username = identity_copy_username(identity); 591 SCPrint(TRUE, stdout, CFSTR("\n%d. '%@'\n"), i + 1, username); 592 CFRelease(username); 593 handle = EAPSecIdentityHandleCreate(identity); 594 dump_plist(stdout, handle); 595 CFRelease(handle); 596 597 } 598 CFRelease(list); 599 return (0); 600} 601 602typedef struct { 603 char * command; 604 funcptr_t func; 605 int argc; 606 char * usage; 607} commandInfo, *commandInfoRef; 608 609static commandInfo commands[] = { 610 { "state", S_state, 1, "<interface_name>" }, 611 { "start", S_start, 2, "<interface_name> <config_file>" }, 612 { "stop", S_stop, 1, "<interface_name>" }, 613 { "retry", S_retry, 1, "<interface_name>" }, 614 { "update", S_update, 2, "<interface_name> <config_file>" }, 615 { "input", S_input, 1, "<interface_name> [ <config_file> ]" }, 616 { "monitor", S_monitor, 0, "[ <interface_name> ]" }, 617 { "set_verbose", S_set_verbose, 1, "( on | off )" }, 618 { "stress_start", S_stress_start, 2, "<interface_name> <config_file>" }, 619 { "show_identities", S_show_identities, 0 }, 620#if ! TARGET_OS_EMBEDDED 621 { "start_system", S_start_system, 1, "<interface_name> [ <config_file> ]"}, 622 { "loginwindow_config", S_loginwindow_config, 1, "<interface_name>" }, 623 { "auto_detect_info", S_show_autodetect_info, 0, NULL }, 624 { "set_user_autoconnect", S_set_user_autoconnect, 1, "( on | off )" }, 625 { "get_user_autoconnect", S_get_user_autoconnect, 0, "" }, 626 { "did_user_cancel", S_did_user_cancel, 1, "<interface_name>" }, 627#endif /* ! TARGET_OS_EMBEDDED */ 628 { NULL, NULL, 0, NULL }, 629}; 630 631static void 632usage(void) 633{ 634 commandInfoRef cmd; 635 int i; 636 637 fprintf(stderr, "usage: %s <command> <args>\n", progname); 638 fprintf(stderr, "where <command> is one of "); 639 for (i = 0, cmd = commands; cmd->command; i++, cmd++) { 640 fprintf(stderr, "%s%s", i == 0 ? "" : ", ", cmd->command); 641 } 642 fprintf(stderr, "\n"); 643 exit(1); 644} 645 646static void 647command_usage(void) 648{ 649 commandInfoRef cmd = commands + S_command_index; 650 651 fprintf(stderr, "usage: %s %s\n", 652 cmd->command, cmd->usage ? cmd->usage : ""); 653 exit(1); 654} 655 656static funcptr_t 657S_lookup_func(char * cmd, int argc) 658{ 659 int i; 660 661 for (i = 0; commands[i].command; i++) { 662 if (strcmp(cmd, commands[i].command) == 0) { 663 S_command_index = i; 664 if (argc < commands[i].argc) { 665 command_usage(); 666 exit(1); 667 } 668 return commands[i].func; 669 } 670 } 671 return (NULL); 672} 673 674int 675main(int argc, char * argv[]) 676{ 677 funcptr_t func; 678 679 progname = argv[0]; 680 if (argc < 2) 681 usage(); 682 683 argv++; argc--; 684 685 func = S_lookup_func(argv[0], argc - 1); 686 if (func == NULL) { 687 usage(); 688 } 689 else { 690 argv++; argc--; 691 exit((*func)(argc, argv)); 692 } 693 exit(0); 694} 695