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