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