1/*
2 * Copyright (c) 2009-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/*
25 * EAPOLClientConfiguration.c
26 * - implementation of the EAPOLClientConfiguration CF object
27 */
28
29/*
30 * Modification History
31 *
32 * December 8, 2009	Dieter Siegmund (dieter@apple.com)
33 * - created
34 */
35
36#include <stdio.h>
37#include <stdlib.h>
38#include <unistd.h>
39#include <sys/types.h>
40#include <string.h>
41#include <TargetConditionals.h>
42#include <CoreFoundation/CFString.h>
43#include <SystemConfiguration/SCNetworkConfiguration.h>
44#include <SystemConfiguration/SCPrivate.h>
45#include <SystemConfiguration/SCValidation.h>
46#include <notify.h>
47#include <pthread.h>
48#include "EAP.h"
49#include "EAPClientProperties.h"
50#include "EAPOLControlTypes.h"
51#include "EAPOLClientConfigurationInternal.h"
52#include "symbol_scope.h"
53#include "myCFUtil.h"
54#include "EAPLog.h"
55
56#define kPrefsName CFSTR("EAPOLClientConfiguration")
57
58/* used with notify(3) to detect configuration changes */
59const char * 	kEAPOLClientConfigurationChangedNotifyKey = "com.apple.network.eapolclientconfiguration";
60
61/**
62 ** 802.1X Profiles Schema
63 **/
64#define kConfigurationKeyProfiles 	CFSTR("Profiles")
65#define kConfigurationKeyDefaultAuthenticationProperties	CFSTR("DefaultAuthenticationProperties")
66
67/**
68 ** 802.1X Network Prefs Schema
69 **/
70#define kEAPOL				CFSTR("EAPOL")
71#define kSystemProfileID		CFSTR("SystemProfileID")
72#define kLoginWindowProfileIDs		CFSTR("LoginWindowProfileIDs")
73
74/**
75 ** Constant CFStrings
76 **/
77const CFStringRef	kEAPOLClientProfileWLANSecurityTypeWEP = CFSTR("WEP");
78const CFStringRef	kEAPOLClientProfileWLANSecurityTypeWPA = CFSTR("WPA");
79const CFStringRef	kEAPOLClientProfileWLANSecurityTypeWPA2 = CFSTR("WPA2");
80const CFStringRef	kEAPOLClientProfileWLANSecurityTypeAny = CFSTR("Any");
81
82/**
83 ** Utility Functions
84 **/
85
86STATIC SCPreferencesRef
87get_sc_prefs(EAPOLClientConfigurationRef cfg)
88{
89    if (cfg->sc_prefs == NULL) {
90	cfg->sc_prefs = SCPreferencesCreate(NULL, kPrefsName, NULL);
91	if (cfg->sc_prefs == NULL) {
92	    EAPLOG(LOG_NOTICE,
93		   "EAPOLClientConfiguration: SCPreferencesCreate failed, %s",
94		   SCErrorString(SCError()));
95	}
96    }
97    return (cfg->sc_prefs);
98}
99
100/*
101 * Function: copy_configured_interface_names
102 *
103 * Purpose:
104 *   Return the BSD interface name of all configured interfaces.  If
105 *   'entity' is non-NULL, also check that the interface has the specified
106 *   extended configuration.
107 */
108STATIC CFArrayRef /* of CFStringRef */
109copy_configured_interface_names(SCPreferencesRef prefs, CFStringRef entity_name)
110{
111    SCNetworkSetRef		current_set = NULL;
112    int				count;
113    int				i;
114    CFMutableArrayRef		ret_names = NULL;
115    CFRange			ret_names_range = { 0 , 0 };
116    CFArrayRef			services = NULL;
117
118    if (prefs == NULL) {
119	goto done;
120    }
121    current_set = SCNetworkSetCopyCurrent(prefs);
122    if (current_set == NULL) {
123	goto done;
124    }
125    services = SCNetworkSetCopyServices(current_set);
126    if (services == NULL) {
127	goto done;
128    }
129
130    count = CFArrayGetCount(services);
131    for (i = 0; i < count; i++) {
132	CFStringRef		this_if_name;
133	SCNetworkInterfaceRef	this_if;
134	SCNetworkServiceRef	s;
135
136	s = (SCNetworkServiceRef)CFArrayGetValueAtIndex(services, i);
137	this_if = SCNetworkServiceGetInterface(s);
138	if (this_if == NULL) {
139	    continue;
140	}
141	if (entity_name != NULL
142	    && (SCNetworkInterfaceGetExtendedConfiguration(this_if, entity_name)
143		== NULL)) {
144	    /* interface doesn't have specified entity */
145	    continue;
146	}
147	this_if_name = SCNetworkInterfaceGetBSDName(this_if);
148	if (this_if_name == NULL) {
149	    continue;
150	}
151	if (ret_names == NULL
152	    || CFArrayContainsValue(ret_names, ret_names_range,
153				    this_if_name) == FALSE) {
154	    if (ret_names == NULL) {
155		ret_names = CFArrayCreateMutable(NULL, count,
156						 &kCFTypeArrayCallBacks);
157	    }
158	    CFArrayAppendValue(ret_names, this_if_name);
159	    ret_names_range.length++;
160	}
161    }
162
163 done:
164    my_CFRelease(&current_set);
165    my_CFRelease(&services);
166    return (ret_names);
167}
168
169STATIC CFDictionaryRef
170copy_profiles_for_mode(EAPOLClientConfigurationRef cfg,
171		       EAPOLControlMode mode)
172{
173    CFArrayRef			all_names;
174    int				count;
175    int				i;
176    CFMutableDictionaryRef	ret_profiles = NULL;
177
178    switch (mode) {
179    case kEAPOLControlModeLoginWindow:
180    case kEAPOLControlModeSystem:
181	break;
182    default:
183	return (NULL);
184    }
185    all_names = copy_configured_interface_names(get_sc_prefs(cfg), kEAPOL);
186    if (all_names == NULL) {
187	return (NULL);
188    }
189    count = CFArrayGetCount(all_names);
190    for (i = 0; i < count; i++) {
191	CFStringRef		if_name = CFArrayGetValueAtIndex(all_names, i);
192	CFTypeRef		val = NULL;
193
194	if (mode == kEAPOLControlModeSystem) {
195	    EAPOLClientProfileRef	profile;
196
197	    profile = EAPOLClientConfigurationGetSystemProfile(cfg, if_name);
198	    if (profile != NULL) {
199		val = profile;
200		CFRetain(val);
201	    }
202	}
203	else {
204	    CFArrayRef			profiles;
205
206	    profiles
207		= EAPOLClientConfigurationCopyLoginWindowProfiles(cfg,
208								  if_name);
209	    if (profiles != NULL) {
210		val = profiles;
211	    }
212
213	}
214	if (val != NULL) {
215	    if (ret_profiles == NULL) {
216		ret_profiles
217		    = CFDictionaryCreateMutable(NULL, 0,
218						&kCFTypeDictionaryKeyCallBacks,
219						&kCFTypeDictionaryValueCallBacks);
220	    }
221	    CFDictionarySetValue(ret_profiles, if_name, val);
222	    CFRelease(val);
223	}
224    }
225    CFRelease(all_names);
226    return (ret_profiles);
227}
228
229STATIC SCNetworkInterfaceRef
230copy_configured_interface(SCPreferencesRef prefs, CFStringRef if_name)
231{
232    SCNetworkSetRef		current_set = NULL;
233    int				count;
234    int				i;
235    SCNetworkInterfaceRef	ret_if = NULL;
236    CFArrayRef			services = NULL;
237
238    current_set = SCNetworkSetCopyCurrent(prefs);
239    if (current_set == NULL) {
240	goto done;
241    }
242    services = SCNetworkSetCopyServices(current_set);
243    if (services == NULL) {
244	goto done;
245    }
246    count = CFArrayGetCount(services);
247    for (i = 0; i < count; i++) {
248	CFStringRef		this_if_name;
249	SCNetworkInterfaceRef	this_if;
250	SCNetworkServiceRef	s;
251
252	s = (SCNetworkServiceRef)CFArrayGetValueAtIndex(services, i);
253	this_if = SCNetworkServiceGetInterface(s);
254	if (this_if == NULL) {
255	    continue;
256	}
257	this_if_name = SCNetworkInterfaceGetBSDName(this_if);
258	if (this_if_name == NULL) {
259	    continue;
260	}
261	if (CFEqual(this_if_name, if_name)) {
262	    ret_if = this_if;
263	    CFRetain(ret_if);
264	    break;
265	}
266    }
267
268 done:
269    my_CFRelease(&current_set);
270    my_CFRelease(&services);
271    return (ret_if);
272}
273
274/*
275 * Function: copy_present_interface
276 * Purpose:
277 *   Check the list of interfaces on the system, and find the one corresponding
278 *   to the specified BSD name.
279 */
280STATIC SCNetworkInterfaceRef
281copy_present_interface(CFStringRef if_name)
282{
283    int				count = 0;
284    int				i;
285    CFArrayRef			list;
286    SCNetworkInterfaceRef	ret = NULL;
287
288    list = SCNetworkInterfaceCopyAll();
289    if (list != NULL) {
290	count = CFArrayGetCount(list);
291    }
292    if (count == 0) {
293	goto done;
294    }
295    for (i = 0; i < count; i++) {
296	SCNetworkInterfaceRef	this_if;
297	CFStringRef		this_if_name;
298
299	this_if = (SCNetworkInterfaceRef)CFArrayGetValueAtIndex(list, i);
300	this_if_name = SCNetworkInterfaceGetBSDName(this_if);
301	if (this_if_name == NULL) {
302	    continue;
303	}
304	if (CFEqual(if_name, this_if_name)) {
305	    ret = this_if;
306	    CFRetain(ret);
307	    break;
308	}
309    }
310
311 done:
312    my_CFRelease(&list);
313    return (ret);
314}
315
316STATIC void
317import_profiles(EAPOLClientConfigurationRef cfg)
318{
319    int					count = 0;
320    CFMutableDictionaryRef		domains_dict;
321    int					i;
322    const void * *			keys;
323    CFDictionaryRef			prefs_dict;
324    CFMutableDictionaryRef		profiles_dict;
325    CFMutableDictionaryRef		ssids_dict;
326    const void * *			values;
327
328    profiles_dict = CFDictionaryCreateMutable(NULL, 0,
329					      &kCFTypeDictionaryKeyCallBacks,
330					      &kCFTypeDictionaryValueCallBacks);
331    ssids_dict = CFDictionaryCreateMutable(NULL, 0,
332					   &kCFTypeDictionaryKeyCallBacks,
333					   &kCFTypeDictionaryValueCallBacks);
334    domains_dict = CFDictionaryCreateMutable(NULL, 0,
335					     &kCFTypeDictionaryKeyCallBacks,
336					     &kCFTypeDictionaryValueCallBacks);
337    prefs_dict = SCPreferencesGetValue(cfg->eap_prefs,
338				       kConfigurationKeyProfiles);
339    if (isA_CFDictionary(prefs_dict) != NULL) {
340	count = CFDictionaryGetCount(prefs_dict);
341    }
342    if (count == 0) {
343	goto done;
344    }
345
346    /* allocate a single array, half for keys, half for values */
347    keys = (const void * *)malloc(sizeof(*keys) * count * 2);
348    values = keys + count;
349    CFDictionaryGetKeysAndValues(prefs_dict, keys, values);
350    for (i = 0; i < count; i++) {
351	EAPOLClientProfileRef	profile;
352	CFDictionaryRef		profile_dict = values[i];
353	CFStringRef		profileID = keys[i];
354	CFDataRef		ssid;
355
356	if (isA_CFDictionary(profile_dict) == NULL) {
357	    SCLog(TRUE, LOG_NOTICE,
358		  CFSTR("EAPOLClientConfiguration: invalid profile with id %@"),
359		  profileID);
360	    continue;
361	}
362	profile = EAPOLClientProfileCreateWithDictAndProfileID(profile_dict,
363							       profileID);
364	if (profile == NULL) {
365	    continue;
366	}
367	ssid = EAPOLClientProfileGetWLANSSIDAndSecurityType(profile, NULL);
368	if (ssid != NULL) {
369	    CFStringRef		conflicting_profileID;
370
371	    conflicting_profileID = CFDictionaryGetValue(ssids_dict, ssid);
372	    if (conflicting_profileID != NULL) {
373		CFStringRef	ssid_str = my_CFStringCreateWithData(ssid);
374
375		SCLog(TRUE, LOG_NOTICE,
376		      CFSTR("EAPOLClientConfiguration: ignoring profile %@:"
377			    " SSID '%@' already used by %@"),
378		      profileID, ssid_str, conflicting_profileID);
379		CFRelease(ssid_str);
380		CFRelease(profile);
381		continue;
382	    }
383	    CFDictionarySetValue(ssids_dict, ssid, profileID);
384	}
385	else {
386	    CFStringRef		domain;
387
388	    domain = EAPOLClientProfileGetWLANDomain(profile);
389	    if (domain != NULL) {
390		CFStringRef		conflicting_profileID;
391
392		conflicting_profileID
393		    = CFDictionaryGetValue(profiles_dict, domain);
394		if (conflicting_profileID != NULL) {
395		    SCLog(TRUE, LOG_NOTICE,
396			  CFSTR("EAPOLClientConfiguration: ignoring profile %@:"
397				" WLAN domain '%@' already used by %@"),
398			  profileID, domain, conflicting_profileID);
399		    CFRelease(profile);
400		    continue;
401		}
402		CFDictionarySetValue(domains_dict, domain, profileID);
403	    }
404	}
405	CFDictionarySetValue(profiles_dict, profileID, profile);
406	EAPOLClientProfileSetConfiguration(profile, cfg);
407	CFRelease(profile);
408    }
409    free(keys);
410
411 done:
412    cfg->ssids = ssids_dict;
413    cfg->profiles = profiles_dict;
414    cfg->domains = domains_dict;
415    return;
416}
417
418STATIC CFDictionaryRef
419export_profiles(EAPOLClientConfigurationRef cfg)
420{
421    int				count;
422    CFMutableDictionaryRef	dict;
423    int				i;
424    const void * *		values;
425
426    count = CFDictionaryGetCount(cfg->profiles);
427    dict = CFDictionaryCreateMutable(NULL, count,
428				     &kCFTypeDictionaryKeyCallBacks,
429				     &kCFTypeDictionaryValueCallBacks);
430    if (count == 0) {
431	/* return empty dict */
432	goto done;
433    }
434
435    values = (const void * *)malloc(sizeof(*values) * count);
436    CFDictionaryGetKeysAndValues(cfg->profiles, NULL, values);
437    for (i = 0; i < count; i++) {
438	EAPOLClientProfileRef	profile = (EAPOLClientProfileRef)values[i];
439	CFDictionaryRef		profile_dict;
440	CFStringRef		profileID;
441
442	profile_dict
443	    = EAPOLClientProfileCreateDictAndProfileID(profile, &profileID);
444	if (profile_dict == NULL) {
445	    /* error, return NULL */
446	    my_CFRelease(&dict);
447	    break;
448	}
449	CFDictionarySetValue(dict, profileID, profile_dict);
450	CFRelease(profile_dict);
451	CFRelease(profileID);
452    }
453    free(values);
454
455 done:
456    return (dict);
457}
458
459STATIC CFArrayRef
460myCFArrayCreateWithIntegerList(const int * list, int list_count)
461{
462    CFMutableArrayRef		array;
463    int				i;
464
465    array = CFArrayCreateMutable(NULL, list_count, &kCFTypeArrayCallBacks);
466    for (i = 0; i < list_count; i++) {
467	CFNumberRef	num;
468
469	num = CFNumberCreate(NULL, kCFNumberIntType, list + i);
470	CFArrayAppendValue(array, num);
471	CFRelease(num);
472    }
473    return (array);
474}
475
476STATIC CFDictionaryRef
477copy_def_auth_props(SCPreferencesRef eap_prefs)
478{
479    CFArrayRef			accept_types = NULL;
480    CFMutableDictionaryRef	auth_props;
481    int				list[] = {
482	kEAPTypePEAP,
483	kEAPTypeTTLS,
484	kEAPTypeEAPFAST,
485	kEAPTypeTLS
486    };
487    int				list_count = sizeof(list) / sizeof(list[0]);
488    CFDictionaryRef		pref_auth_props;
489
490    pref_auth_props
491	= SCPreferencesGetValue(eap_prefs,
492				kConfigurationKeyDefaultAuthenticationProperties);
493    if (pref_auth_props != NULL) {
494	accept_types = CFDictionaryGetValue(pref_auth_props,
495					    kEAPClientPropAcceptEAPTypes);
496	if (accept_types_valid(accept_types)) {
497	    CFRetain(pref_auth_props);
498	    return (pref_auth_props);
499	}
500	if (accept_types != NULL) {
501	    SCLog(TRUE, LOG_NOTICE,
502		  CFSTR("EAPOLClientConfiguration: default Authentication "
503			"Properties invalid, %@ - ignoring"), pref_auth_props);
504	}
505    }
506    accept_types = myCFArrayCreateWithIntegerList(list, list_count);
507    auth_props = CFDictionaryCreateMutable(NULL, 0,
508					   &kCFTypeDictionaryKeyCallBacks,
509					   &kCFTypeDictionaryValueCallBacks);
510    CFDictionarySetValue(auth_props,
511			 kEAPClientPropAcceptEAPTypes,
512			 accept_types);
513    CFDictionarySetValue(auth_props, kEAPClientPropEAPFASTUsePAC,
514			 kCFBooleanTrue);
515    CFDictionarySetValue(auth_props, kEAPClientPropEAPFASTProvisionPAC,
516			 kCFBooleanTrue);
517    CFRelease(accept_types);
518    return (auth_props);
519
520}
521
522/*
523 * Function: copy_service
524 * Purpose:
525 *   Check the global list of SCNetworkServiceRefs for one that is configured
526 *   over the specifed SCNetworkInterfaceRef.   The assumption here is that
527 *   a previous call has already checked for such a service in the current
528 *   set.
529 */
530STATIC SCNetworkServiceRef
531copy_service(SCPreferencesRef prefs, SCNetworkInterfaceRef net_if)
532{
533    int			count = 0;
534    int			i;
535    SCNetworkServiceRef	service = NULL;
536    CFArrayRef		list;
537
538    list = SCNetworkServiceCopyAll(prefs);
539    if (list != NULL) {
540	count = CFArrayGetCount(list);
541    }
542    if (count == 0) {
543	goto done;
544    }
545    for (i = 0; i < count; i++) {
546	SCNetworkInterfaceRef	this_if;
547	SCNetworkServiceRef	this_service;
548
549	this_service = (SCNetworkServiceRef)CFArrayGetValueAtIndex(list, i);
550	this_if = SCNetworkServiceGetInterface(this_service);
551	if (this_if == NULL) {
552	    continue;
553	}
554	if (CFEqual(this_if, net_if)) {
555	    service = this_service;
556	    CFRetain(service);
557	    break;
558	}
559    }
560 done:
561    my_CFRelease(&list);
562    return (service);
563}
564
565/*
566 * Function: copy_interface
567 * Purpose:
568 *   Get a reference to an SCNetworkInterfaceRef for the specified
569 *   interface name.   First try to get an interface configured in the
570 *   current set.  If that fails, copy a service configured over the
571 *   specified interface, and add it to the current set.
572 *
573 *   Return the interface, service, and set for the caller to release.
574 */
575STATIC SCNetworkInterfaceRef
576copy_interface(SCPreferencesRef prefs, CFStringRef if_name,
577	       SCNetworkSetRef * ret_set_p,
578	       SCNetworkServiceRef * ret_service_p)
579{
580    SCNetworkSetRef		current_set = NULL;
581    SCNetworkServiceRef		service = NULL;
582    SCNetworkInterfaceRef	net_if;
583    SCNetworkInterfaceRef	ret = NULL;
584
585    /* if the interface is part of a service/set, we're done */
586    net_if = copy_configured_interface(prefs, if_name);
587    if (net_if != NULL) {
588	ret = net_if;
589	CFRetain(ret);
590	goto done;
591    }
592
593    /* interface isn't part of a service/set, make it so */
594    net_if = copy_present_interface(if_name);
595    if (net_if == NULL) {
596	goto done;
597    }
598
599    /* find the service in any set */
600    service = copy_service(prefs, net_if);
601    if (service == NULL) {
602	EAPLOG(LOG_ERR,
603	       "EAPOLClientConfiguration: can't get service");
604	goto done;
605    }
606    /* add the service to the current set */
607    current_set = SCNetworkSetCopyCurrent(prefs);
608    if (current_set == NULL) {
609	EAPLOG(LOG_ERR,
610	       "EAPOLClientConfiguration: can't get current set");
611	goto done;
612    }
613    if (SCNetworkSetAddService(current_set, service) == FALSE) {
614	EAPLOG(LOG_ERR,
615	       "EAPOLClientConfiguration: failed to add dummy service");
616	goto done;
617    }
618    /* return this SCNetworkInterfaceRef since it's bound to the prefs */
619    ret = SCNetworkServiceGetInterface(service);
620    CFRetain(ret);
621
622 done:
623    my_CFRelease(&net_if);
624    if (ret == NULL) {
625	my_CFRelease(&service);
626	my_CFRelease(&current_set);
627    }
628    *ret_service_p = service;
629    *ret_set_p = current_set;
630    return (ret);
631}
632
633/*
634 * Function: set_eapol_configuration
635 * Purpose:
636 *   Set the EAPOL configuration in the prefs.
637 */
638STATIC Boolean
639set_eapol_configuration(SCPreferencesRef prefs, CFStringRef if_name,
640			CFDictionaryRef dict)
641{
642    SCNetworkSetRef		current_set = NULL;
643    SCNetworkInterfaceRef	net_if_save;
644    Boolean			ret = FALSE;
645    SCNetworkServiceRef		service = NULL;
646
647    net_if_save = copy_interface(prefs, if_name, &current_set, &service);
648    if (net_if_save != NULL) {
649	ret = SCNetworkInterfaceSetExtendedConfiguration(net_if_save, kEAPOL,
650							 dict);
651	if (ret == FALSE) {
652	    EAPLOG(LOG_ERR,
653		   "EAPOLClientConfiguration: SetExtendedConfiguration failed");
654	}
655    }
656    if (current_set != NULL) {
657	SCNetworkSetRemoveService(current_set, service);
658	CFRelease(current_set);
659    }
660    my_CFRelease(&service);
661    my_CFRelease(&net_if_save);
662    return (ret);
663}
664
665
666/*
667 * Function: get_eapol_configuration
668 * Purpose:
669 *   Get the EAPOL configuration for the interface.
670 * Note:
671 *   Side-effect on prefs is that a service gets added to the current set.
672 *   Since this prefs reference will not be saved, we leave the service in
673 *   the current set so that we can find it as a configured interface later
674 *   when it's time to save the writable prefs.
675 */
676STATIC CFDictionaryRef
677get_eapol_configuration(SCPreferencesRef prefs, CFStringRef if_name,
678			SCNetworkInterfaceRef * ret_net_if)
679{
680    SCNetworkSetRef		current_set = NULL;
681    CFDictionaryRef		dict = NULL;
682    SCNetworkServiceRef		service = NULL;
683    SCNetworkInterfaceRef	net_if = NULL;
684
685    net_if = copy_interface(prefs, if_name, &current_set, &service);
686    if (net_if != NULL) {
687	dict = SCNetworkInterfaceGetExtendedConfiguration(net_if, kEAPOL);
688    }
689    my_CFRelease(&current_set);
690    my_CFRelease(&service);
691    if (ret_net_if != NULL) {
692	*ret_net_if = net_if;
693    }
694    else {
695	my_CFRelease(&net_if);
696    }
697    return (dict);
698}
699
700/*
701 * Function: setInterfaceEAPOLConfiguration
702 * Purpose:
703 *   Set the EAPOL configuration for the particular interface in the
704 *   cfg->sc_prefs and add the SCNetworkInterfaceRef to cfg->sc_changed_if.
705 *   That allows saveInterfaceEAPOLConfiguration() to know which interfaces
706 *   were changed when it commits the changes to the writable prefs.
707 */
708STATIC Boolean
709setInterfaceEAPOLConfiguration(EAPOLClientConfigurationRef cfg,
710			       SCNetworkInterfaceRef net_if,
711			       CFDictionaryRef dict)
712{
713    CFRange	r;
714    Boolean	ret;
715
716    ret = SCNetworkInterfaceSetExtendedConfiguration(net_if, kEAPOL, dict);
717    if (ret == FALSE) {
718	return (ret);
719    }
720    /* keep track of which SCNetworkInterfaceRef's were changed */
721    if (cfg->sc_changed_if == NULL) {
722	cfg->sc_changed_if = CFArrayCreateMutable(NULL, 0,
723						  &kCFTypeArrayCallBacks);
724    }
725    r.location = 0;
726    r.length = CFArrayGetCount(cfg->sc_changed_if);
727    if (CFArrayContainsValue(cfg->sc_changed_if, r, net_if) == FALSE) {
728	CFArrayAppendValue(cfg->sc_changed_if, net_if);
729    }
730    return (TRUE);
731}
732
733/*
734 * Function: saveInterfaceEAPOLConfiguration
735 * Purpose:
736 *   Save the SCNetworkInterface EAPOL information for System and LoginWindow
737 *   modes.
738 *
739 *   Iterate over the changed SCNetworkInterfaceRef list cfg->sc_changed_if,
740 *   and for each interface, grab the EAPOL extended information from
741 *   cfg->sc_prefs.    Then set the corresponding value in the new
742 *   freshly created SCPreferencesRef.
743 *
744 *   All this done to avoid a writer getting Stale Object errors
745 *   when it has its own SCPreferencesRef object that it manipulates while
746 *   having an EAPOLClientConfigurationRef open.
747 */
748STATIC Boolean
749saveInterfaceEAPOLConfiguration(EAPOLClientConfigurationRef cfg,
750				Boolean * changed_p)
751{
752    AuthorizationExternalForm *	auth_ext_p;
753    int				count;
754    int				i;
755    SCPreferencesRef		prefs = NULL;
756    Boolean			ret = FALSE;
757
758    *changed_p = FALSE;
759    if (cfg->sc_changed_if == NULL) {
760	return (TRUE);
761    }
762    auth_ext_p = EAPOLClientConfigurationGetAuthorizationExternalForm(cfg);
763    if (auth_ext_p != NULL) {
764	AuthorizationRef	auth;
765	OSStatus		status;
766
767	status = AuthorizationCreateFromExternalForm(auth_ext_p, &auth);
768	if (status != errAuthorizationSuccess) {
769	    EAPLOG(LOG_ERR,
770		   "EAPOLClientConfiguration: can't allocate Authorization, %d",
771		   (int)status);
772	    goto done;
773	}
774	prefs = SCPreferencesCreateWithAuthorization(NULL,
775						     kPrefsName, NULL,
776						     auth);
777	AuthorizationFree(auth, kAuthorizationFlagDefaults);
778    }
779    else {
780	prefs = SCPreferencesCreate(NULL, kPrefsName, NULL);
781    }
782    count = CFArrayGetCount(cfg->sc_changed_if);
783    for (i = 0; i < count; i++) {
784	CFDictionaryRef		dict;
785	CFStringRef		if_name;
786	SCNetworkInterfaceRef	net_if;
787
788	net_if = (SCNetworkInterfaceRef)
789	    CFArrayGetValueAtIndex(cfg->sc_changed_if, i);
790	if_name = SCNetworkInterfaceGetBSDName(net_if);
791	if (if_name == NULL) {
792	    /* should not happen */
793	    EAPLOG(LOG_ERR, "EAPOLClientConfiguration: missing BSD name");
794	    continue;
795	}
796	dict = SCNetworkInterfaceGetExtendedConfiguration(net_if, kEAPOL);
797
798	/* find the same interface in the saving prefs */
799	if (set_eapol_configuration(prefs, if_name, dict) == FALSE) {
800	    continue;
801	}
802    }
803
804    ret = SCPreferencesCommitChanges(prefs);
805    if (ret == FALSE) {
806	EAPLOG(LOG_NOTICE,
807	       "EAPOLClientConfigurationSave SCPreferencesCommitChanges"
808	       " failed %s", SCErrorString(SCError()));
809	goto done;
810    }
811    SCPreferencesApplyChanges(prefs);
812    *changed_p = TRUE;
813
814 done:
815    my_CFRelease(&cfg->sc_changed_if);
816    my_CFRelease(&prefs);
817    return (ret);
818}
819
820/**
821 ** CF object glue code
822 **/
823STATIC CFStringRef	__EAPOLClientConfigurationCopyDebugDesc(CFTypeRef cf);
824STATIC void		__EAPOLClientConfigurationDeallocate(CFTypeRef cf);
825
826STATIC CFTypeID __kEAPOLClientConfigurationTypeID = _kCFRuntimeNotATypeID;
827
828STATIC const CFRuntimeClass __EAPOLClientConfigurationClass = {
829    0,						/* version */
830    "EAPOLClientConfiguration",			/* className */
831    NULL,					/* init */
832    NULL,					/* copy */
833    __EAPOLClientConfigurationDeallocate,	/* deallocate */
834    NULL,					/* equal */
835    NULL,					/* hash */
836    NULL,					/* copyFormattingDesc */
837    __EAPOLClientConfigurationCopyDebugDesc	/* copyDebugDesc */
838};
839
840STATIC CFStringRef
841__EAPOLClientConfigurationCopyDebugDesc(CFTypeRef cf)
842{
843    CFAllocatorRef		allocator = CFGetAllocator(cf);
844    EAPOLClientConfigurationRef	cfg = (EAPOLClientConfigurationRef)cf;
845    CFMutableStringRef		result;
846
847    result = CFStringCreateMutable(allocator, 0);
848    if (cfg->auth_ext_p == NULL) {
849	CFStringAppendFormat(result, NULL,
850			     CFSTR("<EAPOLClientConfiguration %p [%p]> {"),
851			     cf, allocator);
852    }
853    else {
854	CFStringAppendFormat(result, NULL,
855			     CFSTR("<EAPOLClientConfiguration %p [%p] auth> {"),
856			     cf, allocator);
857    }
858    CFStringAppendFormat(result, NULL, CFSTR("profiles = %@"), cfg->profiles);
859    CFStringAppendFormat(result, NULL, CFSTR("ssids = %@"), cfg->ssids);
860    CFStringAppend(result, CFSTR("}"));
861    return (result);
862}
863
864STATIC void
865__EAPOLClientConfigurationDeallocate(CFTypeRef cf)
866{
867    EAPOLClientConfigurationRef cfg = (EAPOLClientConfigurationRef)cf;
868
869    if (cfg->auth_ext_p != NULL) {
870	free(cfg->auth_ext_p);
871	cfg->auth_ext_p = NULL;
872    }
873    my_CFRelease(&cfg->eap_prefs);
874    my_CFRelease(&cfg->sc_prefs);
875    my_CFRelease(&cfg->sc_changed_if);
876    my_CFRelease(&cfg->profiles);
877    my_CFRelease(&cfg->ssids);
878    my_CFRelease(&cfg->domains);
879    my_CFRelease(&cfg->def_auth_props);
880    return;
881}
882
883STATIC void
884__EAPOLClientConfigurationInitialize(void)
885{
886    /* initialize runtime */
887    __kEAPOLClientConfigurationTypeID
888	= _CFRuntimeRegisterClass(&__EAPOLClientConfigurationClass);
889    return;
890}
891
892STATIC void
893__EAPOLClientConfigurationRegisterClass(void)
894{
895    STATIC pthread_once_t	initialized = PTHREAD_ONCE_INIT;
896
897    pthread_once(&initialized, __EAPOLClientConfigurationInitialize);
898    return;
899}
900
901STATIC EAPOLClientConfigurationRef
902__EAPOLClientConfigurationAllocate(CFAllocatorRef allocator)
903{
904    EAPOLClientConfigurationRef	cfg;
905    int				size;
906
907    __EAPOLClientConfigurationRegisterClass();
908
909    size = sizeof(*cfg) - sizeof(CFRuntimeBase);
910    cfg = (EAPOLClientConfigurationRef)
911	_CFRuntimeCreateInstance(allocator,
912				 __kEAPOLClientConfigurationTypeID, size, NULL);
913    bzero(((void *)cfg) + sizeof(CFRuntimeBase), size);
914    return (cfg);
915}
916
917/**
918 ** EAPOLClientConfiguration APIs
919 **/
920
921CFTypeID
922EAPOLClientConfigurationGetTypeID(void)
923{
924    __EAPOLClientConfigurationRegisterClass();
925    return (__kEAPOLClientConfigurationTypeID);
926}
927
928EAPOLClientConfigurationRef
929EAPOLClientConfigurationCreateInternal(CFAllocatorRef allocator,
930				       AuthorizationRef auth)
931{
932    EAPOLClientConfigurationRef		cfg;
933
934    /* allocate/return an EAPOLClientConfigurationRef */
935    cfg = __EAPOLClientConfigurationAllocate(allocator);
936    if (cfg == NULL) {
937	return (NULL);
938    }
939    if (auth != NULL) {
940	cfg->eap_prefs
941	    = SCPreferencesCreateWithAuthorization(allocator,
942						   kPrefsName,
943						   kEAPOLClientConfigurationPrefsID,
944						   auth);
945    }
946    else {
947	cfg->eap_prefs = SCPreferencesCreate(allocator, kPrefsName,
948					     kEAPOLClientConfigurationPrefsID);
949    }
950    if (cfg->eap_prefs == NULL) {
951	goto failed;
952    }
953    if (auth != NULL) {
954	AuthorizationExternalForm *	auth_ext_p;
955	OSStatus			status;
956
957	auth_ext_p = malloc(sizeof(*auth_ext_p));
958	status = AuthorizationMakeExternalForm(auth, auth_ext_p);
959	if (status != errAuthorizationSuccess) {
960	    free(auth_ext_p);
961	    goto failed;
962	}
963	cfg->auth_ext_p = auth_ext_p;
964    }
965
966    import_profiles(cfg);
967    cfg->def_auth_props = copy_def_auth_props(cfg->eap_prefs);
968    return (cfg);
969
970 failed:
971    my_CFRelease(&cfg);
972    return (NULL);
973
974}
975
976/*
977 * Function: EAPOLClientConfigurationCreate
978 *
979 * Purpose:
980 *   Get reference to EAPOL Client Configuration object.
981 *   Used by pure readers of the configuration, and by root processes
982 *   that modify the configuration.
983 *
984 * Returns:
985 *   NULL if reference could not be allocated, non-NULL otherwise.
986 *
987 * Note:
988 *   Attempts to invoke EAPOLClientConfigurationSave() as non-root using an
989 *   EAPOLClientConfigurationRef acquired by calling this function will fail.
990 *   Use EAPOLClientConfigurationCreateWithAuthorization() if you want to
991 *   make changes as non-root.
992 */
993EAPOLClientConfigurationRef
994EAPOLClientConfigurationCreate(CFAllocatorRef allocator)
995{
996    return (EAPOLClientConfigurationCreateInternal(allocator, NULL));
997}
998
999/*
1000 * Function: EAPOLClientConfigurationCreateWithAuthorization
1001 *
1002 * Purpose:
1003 *   Get reference to EAPOL Client Configuration object with the specified
1004 *   AuthorizationRef.
1005 *
1006 *   Used by non-root processes that need to modify the configuration.
1007 *
1008 * Returns:
1009 *   NULL if reference could not be allocated, non-NULL otherwise.
1010 */
1011EAPOLClientConfigurationRef
1012EAPOLClientConfigurationCreateWithAuthorization(CFAllocatorRef allocator,
1013						AuthorizationRef auth)
1014{
1015    if (auth == NULL) {
1016	return (NULL);
1017    }
1018    return (EAPOLClientConfigurationCreateInternal(allocator, auth));
1019}
1020
1021/*
1022 * Function: EAPOLClientConfigurationSave
1023 *
1024 * Purpose:
1025 *   Write the configuration to persistent storage.
1026 *
1027 * Returns:
1028 *   TRUE if successfully written, FALSE otherwise.
1029 */
1030Boolean
1031EAPOLClientConfigurationSave(EAPOLClientConfigurationRef cfg)
1032{
1033    Boolean		changed = FALSE;
1034    CFDictionaryRef	existing_prefs_dict;
1035    CFDictionaryRef	prefs_dict;
1036    Boolean		ret = FALSE;
1037
1038    /* save the 802.1X prefs */
1039    prefs_dict = export_profiles(cfg);
1040    if (prefs_dict == NULL) {
1041	EAPLOG(LOG_NOTICE,
1042	       "EAPOLClientConfigurationSave export_profiles() failed");
1043	goto done;
1044    }
1045    existing_prefs_dict = SCPreferencesGetValue(cfg->eap_prefs,
1046						kConfigurationKeyProfiles);
1047    if (cfg->def_auth_props_changed == FALSE
1048	&& my_CFEqual(existing_prefs_dict, prefs_dict)) {
1049	/* configuration is the same, no need to save */
1050    }
1051    else {
1052	if (cfg->def_auth_props_changed) {
1053	    ret = SCPreferencesSetValue(cfg->eap_prefs,
1054					kConfigurationKeyDefaultAuthenticationProperties,
1055					cfg->def_auth_props);
1056	    if (ret == FALSE) {
1057		EAPLOG(LOG_NOTICE,
1058		       "EAPOLClientConfigurationSave SCPreferencesSetValue"
1059		       " failed %s",
1060		       SCErrorString(SCError()));
1061		goto done;
1062	    }
1063	}
1064	ret = SCPreferencesSetValue(cfg->eap_prefs, kConfigurationKeyProfiles,
1065				    prefs_dict);
1066	if (ret == FALSE) {
1067	    EAPLOG(LOG_NOTICE,
1068		   "EAPOLClientConfigurationSave SCPreferencesSetValue"
1069		   " failed %s",
1070		   SCErrorString(SCError()));
1071	    goto done;
1072	}
1073	ret = SCPreferencesCommitChanges(cfg->eap_prefs);
1074	if (ret == FALSE) {
1075	    EAPLOG(LOG_NOTICE,
1076		   "EAPOLClientConfigurationSave SCPreferencesCommitChanges"
1077		   " failed %s", SCErrorString(SCError()));
1078	    return (FALSE);
1079	}
1080	cfg->def_auth_props_changed = FALSE;
1081	SCPreferencesApplyChanges(cfg->eap_prefs);
1082	changed = TRUE;
1083    }
1084
1085    /* save the network prefs */
1086    {
1087	Boolean		this_changed = FALSE;
1088
1089	ret = saveInterfaceEAPOLConfiguration(cfg, &this_changed);
1090	if (ret == FALSE) {
1091	    goto done;
1092	}
1093	if (this_changed) {
1094	    changed = TRUE;
1095	}
1096    }
1097    my_CFRelease(&cfg->sc_prefs); /* force a refresh */
1098
1099 done:
1100    my_CFRelease(&prefs_dict);
1101    if (changed) {
1102	notify_post(kEAPOLClientConfigurationChangedNotifyKey);
1103    }
1104    return (ret);
1105}
1106
1107/*
1108 * Function: EAPOLClientConfigurationCopyProfiles
1109 *
1110 * Purpose:
1111 *   Get the list of defined profiles.   If there are no profiles defined,
1112 *   returns NULL.
1113 *
1114 * Returns:
1115 *   NULL if no profiles are defined, non-NULL non-empty array of profiles
1116 *   otherwise.
1117 */
1118CFArrayRef /* of EAPOLClientProfileRef */
1119EAPOLClientConfigurationCopyProfiles(EAPOLClientConfigurationRef cfg)
1120{
1121    CFAllocatorRef		allocator = CFGetAllocator(cfg);
1122    int				count;
1123    CFArrayRef			profiles;
1124    const void * *		values;
1125
1126    count = CFDictionaryGetCount(cfg->profiles);
1127    if (count == 0) {
1128	return (NULL);
1129    }
1130    values = (const void * *)malloc(sizeof(*values) * count);
1131    CFDictionaryGetKeysAndValues(cfg->profiles, NULL, values);
1132    profiles = CFArrayCreate(allocator, values, count, &kCFTypeArrayCallBacks);
1133    free(values);
1134    return (profiles);
1135}
1136
1137/*
1138 * Function: EAPOLClientConfigurationGetProfileWithID
1139 *
1140 * Purpose:
1141 *   Return the profile associated with the specified profileID.
1142 *
1143 * Returns:
1144 *   NULL if no such profile exists, non-NULL profile otherwise.
1145 */
1146EAPOLClientProfileRef
1147EAPOLClientConfigurationGetProfileWithID(EAPOLClientConfigurationRef cfg,
1148					 CFStringRef profileID)
1149{
1150    return ((EAPOLClientProfileRef)
1151	    CFDictionaryGetValue(cfg->profiles, profileID));
1152}
1153
1154/*
1155 * Function: EAPOLClientConfigurationGetProfileWithWLANSSID
1156 *
1157 * Purpose:
1158 *   Return the profile associated with the specified WLAN SSID.
1159 *
1160 * Returns:
1161 *   NULL if no such profile exists, non-NULL profile otherwise.
1162 */
1163EAPOLClientProfileRef
1164EAPOLClientConfigurationGetProfileWithWLANSSID(EAPOLClientConfigurationRef cfg,
1165					       CFDataRef ssid)
1166{
1167    CFStringRef		profileID;
1168
1169    profileID = CFDictionaryGetValue(cfg->ssids, ssid);
1170    if (profileID == NULL) {
1171	return (NULL);
1172    }
1173    return ((EAPOLClientProfileRef)
1174	    CFDictionaryGetValue(cfg->profiles, profileID));
1175}
1176
1177/*
1178 * Function: EAPOLClientConfigurationGetProfileWithWLANDomain
1179 *
1180 * Purpose:
1181 *   Return the profile associated with the specified WLAN
1182 *   Hotspot 2.0 domain name.
1183 *
1184 * Returns:
1185 *   NULL if no such profile exists, non-NULL profile otherwise.
1186 */
1187EAPOLClientProfileRef
1188EAPOLClientConfigurationGetProfileWithWLANDomain(EAPOLClientConfigurationRef cfg,
1189						 CFStringRef domain)
1190{
1191    CFStringRef		profileID;
1192
1193    profileID = CFDictionaryGetValue(cfg->domains, domain);
1194    if (profileID == NULL) {
1195	return (NULL);
1196    }
1197    return ((EAPOLClientProfileRef)
1198	    CFDictionaryGetValue(cfg->profiles, profileID));
1199}
1200
1201/*
1202 * Function: EAPOLClientConfigurationRemoveProfile
1203 *
1204 * Purpose:
1205 *   Remove the specified profile from the configuration.
1206 *
1207 * Returns:
1208 *   FALSE if the profile is invalid or not in the configuration,
1209 *   TRUE otherwise.
1210 */
1211Boolean
1212EAPOLClientConfigurationRemoveProfile(EAPOLClientConfigurationRef cfg,
1213				      EAPOLClientProfileRef profile)
1214{
1215    CFStringRef			profileID = EAPOLClientProfileGetID(profile);
1216    CFDataRef			ssid;
1217
1218    if (EAPOLClientConfigurationGetProfileWithID(cfg, profileID) != profile) {
1219	/* trying to remove profile that isn't part of the configuration */
1220	return (FALSE);
1221    }
1222    ssid = EAPOLClientProfileGetWLANSSIDAndSecurityType(profile, NULL);
1223    if (ssid != NULL) {
1224	CFDictionaryRemoveValue(cfg->ssids, ssid);
1225    }
1226    CFDictionaryRemoveValue(cfg->profiles, profileID);
1227    return (TRUE);
1228}
1229
1230/*
1231 * Function: EAPOLClientConfigurationAddProfile
1232 *
1233 * Purpose:
1234 *   Add the specified profile to the configuration.
1235 *
1236 * Returns:
1237 *   FALSE if the profile could not be added, either because:
1238 *   - the profile is already in the configuration, or
1239 *   - the profile conflicts with an existing profile (profileID or WLAN SSID)
1240 *   TRUE if the profile was added successfully.
1241 */
1242Boolean
1243EAPOLClientConfigurationAddProfile(EAPOLClientConfigurationRef cfg,
1244				   EAPOLClientProfileRef profile)
1245{
1246    CFStringRef		domain = NULL;
1247    CFStringRef		profileID = EAPOLClientProfileGetID(profile);
1248    CFDataRef		ssid;
1249
1250    if (profile->cfg != NULL) {
1251	/* profile is already part of the configuration */
1252	return (FALSE);
1253    }
1254    if (EAPOLClientConfigurationGetProfileWithID(cfg, profileID) != NULL) {
1255	/* profile already present with that profileID */
1256	return (FALSE);
1257    }
1258    ssid = EAPOLClientProfileGetWLANSSIDAndSecurityType(profile, NULL);
1259    if (ssid != NULL) {
1260	if (EAPOLClientConfigurationGetProfileWithWLANSSID(cfg, ssid) != NULL) {
1261	    /* profile already present with that SSID */
1262	    return (FALSE);
1263	}
1264    }
1265    else {
1266	domain = EAPOLClientProfileGetWLANDomain(profile);
1267	if (domain != NULL) {
1268	    if (EAPOLClientConfigurationGetProfileWithWLANDomain(cfg, domain)
1269		!= NULL) {
1270		/* profile already exists with that domain */
1271		return (FALSE);
1272	    }
1273	}
1274    }
1275    CFDictionarySetValue(cfg->profiles, profileID, profile);
1276    if (ssid != NULL) {
1277	CFDictionarySetValue(cfg->ssids, ssid, profileID);
1278    }
1279    else if (domain != NULL) {
1280	CFDictionarySetValue(cfg->domains, domain, profileID);
1281    }
1282    EAPOLClientProfileSetConfiguration(profile, cfg);
1283    return (TRUE);
1284}
1285
1286/*
1287 * Function: EAPOLClientConfigurationCopyMatchingProfiles
1288 *
1289 * Purpose:
1290 *   Find the profile(s) matching the specified profile.
1291 *   A profile is matched based on the profileID, the WLAN SSID, and
1292 *   the WLAN domain, all of which must be unique in the configuration.
1293 *
1294 *   Usually invoked after calling
1295 *   EAPOLClientProfileCreateWithPropertyList() to instantiate a profile
1296 *   from an external format.
1297 *
1298 * Returns:
1299 *   NULL if no matching profile is part of the configuration,
1300 *   non-NULL CFArrayRef of EAPOLClientProfileRef if found.
1301 */
1302CFArrayRef /* of EAPOLClientProfileRef */
1303EAPOLClientConfigurationCopyMatchingProfiles(EAPOLClientConfigurationRef cfg,
1304					     EAPOLClientProfileRef profile)
1305{
1306    int				count;
1307    EAPOLClientProfileRef	matching_profile;
1308    CFStringRef			profileID = EAPOLClientProfileGetID(profile);
1309    CFDataRef			ssid;
1310    const void *		values[2] = { NULL, NULL };
1311
1312    count = 0;
1313    matching_profile = EAPOLClientConfigurationGetProfileWithID(cfg, profileID);
1314    if (matching_profile != NULL) {
1315	values[count] = matching_profile;
1316	count++;
1317    }
1318    matching_profile = NULL;
1319    ssid = EAPOLClientProfileGetWLANSSIDAndSecurityType(profile, NULL);
1320    if (ssid != NULL) {
1321	matching_profile
1322	    = EAPOLClientConfigurationGetProfileWithWLANSSID(cfg, ssid);
1323    }
1324    else {
1325	CFStringRef		domain;
1326
1327	domain = EAPOLClientProfileGetWLANDomain(profile);
1328	if (domain != NULL) {
1329	    matching_profile
1330		= EAPOLClientConfigurationGetProfileWithWLANDomain(cfg, domain);
1331	}
1332    }
1333    if (matching_profile != NULL && values[0] != matching_profile) {
1334	values[count] = matching_profile;
1335	count++;
1336    }
1337    if (count == 0) {
1338	return (NULL);
1339    }
1340    return (CFArrayCreate(CFGetAllocator(cfg), values, count,
1341			  &kCFTypeArrayCallBacks));
1342}
1343
1344/*
1345 * Function: EAPOLClientConfigurationCopyLoginWindowProfiles
1346 *
1347 * Purpose:
1348 *   Return the list of profiles configured for LoginWindow mode on the
1349 *   specified BSD network interface (e.g. "en0", "en1").
1350 *
1351 * Returns:
1352 *   NULL if no profiles are defined, non-NULL non-empty array of profiles
1353 *   otherwise.
1354 */
1355CFArrayRef /* of EAPOLClientProfileRef */
1356EAPOLClientConfigurationCopyLoginWindowProfiles(EAPOLClientConfigurationRef cfg,
1357						CFStringRef if_name)
1358{
1359    int				count;
1360    int				i;
1361    CFDictionaryRef		dict;
1362    CFArrayRef			profile_ids;
1363    CFMutableArrayRef		ret_profiles = NULL;
1364
1365    dict = get_eapol_configuration(get_sc_prefs(cfg), if_name, NULL);
1366    if (dict == NULL) {
1367	goto done;
1368    }
1369    profile_ids = CFDictionaryGetValue(dict, kLoginWindowProfileIDs);
1370    if (isA_CFArray(profile_ids) == NULL) {
1371	goto done;
1372    }
1373    count = CFArrayGetCount(profile_ids);
1374    if (count == 0) {
1375	goto done;
1376    }
1377    ret_profiles = CFArrayCreateMutable(NULL, count, &kCFTypeArrayCallBacks);
1378    for (i = 0; i < count; i++) {
1379	CFStringRef		profileID;
1380	EAPOLClientProfileRef	profile;
1381
1382	profileID = (CFStringRef)CFArrayGetValueAtIndex(profile_ids, i);
1383	if (isA_CFString(profileID) == NULL) {
1384	    continue;
1385	}
1386	profile = EAPOLClientConfigurationGetProfileWithID(cfg, profileID);
1387	if (profile != NULL) {
1388	    CFArrayAppendValue(ret_profiles, profile);
1389	}
1390    }
1391    if (CFArrayGetCount(ret_profiles) == 0) {
1392	my_CFRelease(&ret_profiles);
1393    }
1394
1395 done:
1396    return (ret_profiles);
1397}
1398
1399/*
1400 * Function: EAPOLClientConfigurationSetLoginWindowProfiles
1401 *
1402 * Purpose:
1403 *   Set the list of profiles configured for LoginWindow mode on the
1404 *   specified BSD network interface (e.g. "en0", "en1").
1405 *
1406 *   If you pass NULL for the "profiles" argument, the LoginWindow profile
1407 *   list is cleared.
1408 */
1409Boolean
1410EAPOLClientConfigurationSetLoginWindowProfiles(EAPOLClientConfigurationRef cfg,
1411					       CFStringRef if_name,
1412					       CFArrayRef profiles)
1413{
1414    CFDictionaryRef		dict;
1415    CFArrayRef			existing_profile_ids = NULL;
1416    SCNetworkInterfaceRef	net_if = NULL;
1417    CFMutableDictionaryRef	new_dict = NULL;
1418    CFMutableArrayRef		profile_ids = NULL;
1419    Boolean			ret = FALSE;
1420
1421    dict = get_eapol_configuration(get_sc_prefs(cfg), if_name, &net_if);
1422    if (net_if == NULL) {
1423	goto done;
1424    }
1425    if (dict != NULL) {
1426	existing_profile_ids
1427	    = CFDictionaryGetValue(dict, kLoginWindowProfileIDs);
1428	existing_profile_ids = isA_CFArray(existing_profile_ids);
1429    }
1430    if (profiles == NULL || CFArrayGetCount(profiles) == 0) {
1431	profile_ids = NULL;
1432    }
1433    else {
1434	int				count;
1435	int				i;
1436	CFRange				r = { 0, 0 };
1437
1438	count = CFArrayGetCount(profiles);
1439	profile_ids = CFArrayCreateMutable(NULL, count, &kCFTypeArrayCallBacks);
1440	for (i = 0; i < count; i++) {
1441	    EAPOLClientProfileRef	profile;
1442	    CFStringRef			profileID;
1443
1444	    profile = (EAPOLClientProfileRef)
1445		CFArrayGetValueAtIndex(profiles, i);
1446	    profileID = EAPOLClientProfileGetID(profile);
1447	    if (CFArrayContainsValue(profile_ids, r, profileID) == FALSE) {
1448		CFArrayAppendValue(profile_ids, profileID);
1449		r.length++;
1450	    }
1451	}
1452    }
1453    if (my_CFEqual(existing_profile_ids, profile_ids)) {
1454	ret = TRUE;
1455	goto done;
1456    }
1457    if (dict != NULL) {
1458	/*
1459	 * remove the AcceptEAPTypes array to give EAPOLController a way to
1460	 * know whether we're using new configuration or the old
1461	 */
1462	new_dict = CFDictionaryCreateMutableCopy(NULL, 0, dict);
1463	CFDictionaryRemoveValue(new_dict, kEAPClientPropAcceptEAPTypes);
1464	CFDictionaryRemoveValue(new_dict, kSCResvInactive);
1465    }
1466    else {
1467	new_dict = CFDictionaryCreateMutable(NULL, 0,
1468					     &kCFTypeDictionaryKeyCallBacks,
1469					     &kCFTypeDictionaryValueCallBacks);
1470    }
1471    if (profile_ids == NULL) {
1472	CFDictionaryRemoveValue(new_dict, kLoginWindowProfileIDs);
1473	if (CFDictionaryGetCount(new_dict) == 0) {
1474	    my_CFRelease(&new_dict);
1475	}
1476    }
1477    else {
1478	CFDictionarySetValue(new_dict, kLoginWindowProfileIDs, profile_ids);
1479    }
1480    if (setInterfaceEAPOLConfiguration(cfg, net_if, new_dict)
1481	== FALSE) {
1482	goto done;
1483    }
1484    ret = TRUE;
1485
1486 done:
1487    my_CFRelease(&new_dict);
1488    my_CFRelease(&profile_ids);
1489    my_CFRelease(&net_if);
1490    return (ret);
1491}
1492
1493/*
1494 * Function: EAPOLClientConfigurationGetSystemProfile
1495 *
1496 * Purpose:
1497 *   Return the profile configured for System mode on the
1498 *   specified BSD network interface (e.g. "en0", "en1").
1499 *
1500 * Returns:
1501 *   NULL if no such profile is defined, non-NULL profile
1502 *   otherwise.
1503 */
1504EAPOLClientProfileRef
1505EAPOLClientConfigurationGetSystemProfile(EAPOLClientConfigurationRef cfg,
1506					 CFStringRef if_name)
1507{
1508    CFDictionaryRef		dict;
1509    SCNetworkInterfaceRef 	net_if = NULL;
1510    EAPOLClientProfileRef	profile = NULL;
1511    CFStringRef			profileID;
1512
1513    dict = get_eapol_configuration(get_sc_prefs(cfg), if_name, &net_if);
1514    if (dict != NULL
1515	&& !CFEqual(SCNetworkInterfaceGetInterfaceType(net_if),
1516		    kSCNetworkInterfaceTypeIEEE80211)) {
1517	profileID = CFDictionaryGetValue(dict, kSystemProfileID);
1518	if (isA_CFString(profileID) != NULL) {
1519	    profile = EAPOLClientConfigurationGetProfileWithID(cfg, profileID);
1520	}
1521    }
1522    my_CFRelease(&net_if);
1523    return (profile);
1524}
1525
1526/*
1527 * Function: EAPOLClientConfigurationSetSystemProfile
1528 *
1529 * Purpose:
1530 *   Set the profile configured for System mode on the specified
1531 *   BSD network interface (e.g. "en0", "en1").
1532 *
1533 *   If you pass NULL for the "profile" argument, the System profile
1534 *   list is cleared.
1535 */
1536Boolean
1537EAPOLClientConfigurationSetSystemProfile(EAPOLClientConfigurationRef cfg,
1538					 CFStringRef if_name,
1539					 EAPOLClientProfileRef profile)
1540{
1541    CFDictionaryRef		dict;
1542    CFStringRef			existing_profileID = NULL;
1543    CFMutableDictionaryRef	new_dict = NULL;
1544    SCNetworkInterfaceRef	net_if = NULL;
1545    CFStringRef			profileID = NULL;
1546    Boolean			ret = FALSE;
1547
1548    if (profile != NULL) {
1549	profileID = EAPOLClientProfileGetID(profile);
1550    }
1551    dict = get_eapol_configuration(get_sc_prefs(cfg), if_name, &net_if);
1552    if (net_if == NULL) {
1553	goto done;
1554    }
1555    if (CFEqual(SCNetworkInterfaceGetInterfaceType(net_if),
1556		kSCNetworkInterfaceTypeIEEE80211)) {
1557	/* disallow setting static System Mode on AirPort interfaces */
1558	goto done;
1559    }
1560
1561    if (dict != NULL) {
1562	existing_profileID
1563	    = CFDictionaryGetValue(dict, kSystemProfileID);
1564    }
1565    if (my_CFEqual(existing_profileID, profileID)) {
1566	/* nothing to do */
1567	ret = TRUE;
1568	goto done;
1569    }
1570    if (dict != NULL) {
1571	/*
1572	 * remove the AcceptEAPTypes array to give EAPOLController a way to
1573	 * know whether we're using new configuration or the old
1574	 */
1575	new_dict = CFDictionaryCreateMutableCopy(NULL, 0, dict);
1576	CFDictionaryRemoveValue(new_dict, kEAPClientPropAcceptEAPTypes);
1577	CFDictionaryRemoveValue(new_dict, kSCResvInactive);
1578    }
1579    else {
1580	new_dict = CFDictionaryCreateMutable(NULL, 0,
1581					     &kCFTypeDictionaryKeyCallBacks,
1582					     &kCFTypeDictionaryValueCallBacks);
1583    }
1584
1585    if (profileID == NULL) {
1586	CFDictionaryRemoveValue(new_dict, kSystemProfileID);
1587	if (CFDictionaryGetCount(new_dict) == 0) {
1588	    my_CFRelease(&new_dict);
1589	}
1590    }
1591    else {
1592	CFDictionarySetValue(new_dict, kSystemProfileID, profileID);
1593    }
1594    if (setInterfaceEAPOLConfiguration(cfg, net_if, new_dict)
1595	== FALSE) {
1596	goto done;
1597    }
1598    ret = TRUE;
1599
1600 done:
1601    my_CFRelease(&new_dict);
1602    my_CFRelease(&net_if);
1603    return (ret);
1604}
1605
1606/*
1607 * Function: EAPOLClientConfigurationCopyAllSystemProfiles
1608 *
1609 * Purpose:
1610 *   Determine which interfaces have System mode configured.
1611 *   Return the results in a dictionary of EAPOLClientProfile
1612 *   keyed by the interface name.
1613 *
1614 * Returns:
1615 *    NULL if no interfaces are configured for System mode,
1616 *    non-NULL CFDictionary of (CFString, EAPOLClientProfile) otherwise.
1617 */
1618CFDictionaryRef /* of (CFString, EAPOLClientProfile) */
1619EAPOLClientConfigurationCopyAllSystemProfiles(EAPOLClientConfigurationRef cfg)
1620{
1621    return (copy_profiles_for_mode(cfg, kEAPOLControlModeSystem));
1622}
1623
1624/*
1625 * Function: EAPOLClientConfigurationCopyAllLoginWindowProfiles
1626 *
1627 * Purpose:
1628 *   Determine which interfaces have LoginWindow mode configured.
1629 *   Return the results in a dictionary of arrays keyed by the interface name.
1630 *   Each array contains EAPOLClientProfileRefs.
1631 *
1632 * Returns:
1633 *    NULL if no interfaces are configured for LoginWindow mode,
1634 *    non-NULL CFDictionary of (CFString, CFArray[EAPOLClientProfile])
1635 *    otherwise.
1636 */
1637CFDictionaryRef /* of (CFString, CFArray[EAPOLClientProfile]) */
1638EAPOLClientConfigurationCopyAllLoginWindowProfiles(EAPOLClientConfigurationRef cfg)
1639{
1640    return (copy_profiles_for_mode(cfg, kEAPOLControlModeLoginWindow));
1641}
1642
1643
1644/**
1645 ** Private SPI
1646 **/
1647
1648/*
1649 * Function: EAPOLClientConfigurationGetDefaultAuthenticationProperties
1650 *
1651 * Purpose:
1652 *   Get the default authentication properties.   Used by the authentication
1653 *   client when there is no profile specified.
1654 *
1655 * Returns:
1656 *   CFDictionaryRef containing the default authentication properties.
1657 */
1658CFDictionaryRef
1659EAPOLClientConfigurationGetDefaultAuthenticationProperties(EAPOLClientConfigurationRef cfg)
1660{
1661    return (cfg->def_auth_props);
1662}
1663
1664/*
1665 * Function: EAPOLClientConfigurationSetDefaultAuthenticationProperties
1666 *
1667 * Purpose:
1668 *   Set the default authentication properties.
1669 *
1670 *   If 'auth_props' is NULL, resets the value to the default.
1671 *
1672 * Returns:
1673 *   FALSE if the auth_props dictionary is invalid, TRUE otherwise.
1674 *
1675 * Note:
1676 *   You must call EAPOLClientConfigurationSave() to make the change permanent.
1677 */
1678Boolean
1679EAPOLClientConfigurationSetDefaultAuthenticationProperties(EAPOLClientConfigurationRef cfg,
1680							   CFDictionaryRef auth_props)
1681{
1682    CFArrayRef		accept_types;
1683
1684    my_CFRelease(&cfg->def_auth_props);
1685    if (auth_props == NULL) {
1686	cfg->def_auth_props = copy_def_auth_props(cfg->eap_prefs);
1687	cfg->def_auth_props_changed = TRUE;
1688	return (TRUE);
1689    }
1690    accept_types = CFDictionaryGetValue(auth_props,
1691					kEAPClientPropAcceptEAPTypes);
1692    if (accept_types_valid(accept_types) == FALSE) {
1693	return (FALSE);
1694    }
1695    cfg->def_auth_props = CFRetain(auth_props);
1696    cfg->def_auth_props_changed = TRUE;
1697    return (TRUE);
1698}
1699
1700
1701/**
1702 ** Internal SPI
1703 **/
1704PRIVATE_EXTERN void
1705EAPOLClientConfigurationSetProfileForSSID(EAPOLClientConfigurationRef cfg,
1706					  CFDataRef ssid,
1707					  EAPOLClientProfileRef profile)
1708{
1709    if (profile == NULL) {
1710	CFDictionaryRemoveValue(cfg->ssids, ssid);
1711    }
1712    else {
1713	CFDictionarySetValue(cfg->ssids, ssid,
1714			     EAPOLClientProfileGetID(profile));
1715    }
1716    return;
1717}
1718
1719PRIVATE_EXTERN void
1720EAPOLClientConfigurationSetProfileForWLANDomain(EAPOLClientConfigurationRef cfg,
1721						CFStringRef domain,
1722						EAPOLClientProfileRef profile)
1723{
1724    if (profile == NULL) {
1725	CFDictionaryRemoveValue(cfg->domains, domain);
1726    }
1727    else {
1728	CFDictionarySetValue(cfg->domains, domain,
1729			     EAPOLClientProfileGetID(profile));
1730    }
1731    return;
1732}
1733
1734PRIVATE_EXTERN AuthorizationExternalForm *
1735EAPOLClientConfigurationGetAuthorizationExternalForm(EAPOLClientConfigurationRef cfg)
1736{
1737    return (cfg->auth_ext_p);
1738}
1739