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