1/*
2 * Copyright (c) 2009-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/*
25 * EAPOLClientProfile.c
26 * - implementation of the EAPOLClientProfile 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 <SystemConfiguration/SCPrivate.h>
43#include <SystemConfiguration/SCValidation.h>
44#include <pthread.h>
45#include "EAPClientProperties.h"
46#include "EAPOLClientConfigurationInternal.h"
47#include "symbol_scope.h"
48#include "myCFUtil.h"
49
50/**
51 ** Schema keys
52 **/
53#define kProfileKeyProfileID 		CFSTR("ProfileID")
54#define kProfileKeyUserDefinedName 	CFSTR("UserDefinedName")
55#define kProfileKeyAuthenticationProperties CFSTR("AuthenticationProperties")
56#define kProfileKeyInformation	 	CFSTR("Information")
57#define kProfileKeyWLAN 		CFSTR("WLAN")
58#define kProfileKeyWLANSSID 		CFSTR("SSID")
59#define kProfileKeyWLANSecurityType 	CFSTR("SecurityType")
60#define kProfileKeyWLANDomain 		CFSTR("Domain")
61
62/**
63 ** Utility Functions
64 **/
65
66PRIVATE_EXTERN Boolean
67accept_types_valid(CFArrayRef accept)
68{
69    int			count;
70    int			i;
71
72    if (isA_CFArray(accept) == NULL) {
73	return (FALSE);
74    }
75    count = CFArrayGetCount(accept);
76    if (count == 0) {
77	return (FALSE);
78    }
79    for (i = 0; i < count; i++) {
80	CFNumberRef		type = CFArrayGetValueAtIndex(accept, i);
81
82	if (isA_CFNumber(type) == NULL) {
83	    return (FALSE);
84	}
85    }
86    return (TRUE);
87}
88
89STATIC Boolean
90applicationID_is_valid(CFStringRef applicationID)
91{
92    CFRange	r;
93
94    if (CFStringGetLength(applicationID) < 3) {
95	return (FALSE);
96    }
97    r = CFStringFind(applicationID, CFSTR("."), 0);
98    if (r.location == kCFNotFound) {
99	return (FALSE);
100    }
101    return (TRUE);
102}
103
104/**
105 ** CF object glue code
106 **/
107STATIC CFStringRef	__EAPOLClientProfileCopyDebugDesc(CFTypeRef cf);
108STATIC void		__EAPOLClientProfileDeallocate(CFTypeRef cf);
109STATIC Boolean		__EAPOLClientProfileEqual(CFTypeRef cf1, CFTypeRef cf2);
110STATIC CFHashCode	__EAPOLClientProfileHash(CFTypeRef cf);
111
112STATIC CFTypeID __kEAPOLClientProfileTypeID = _kCFRuntimeNotATypeID;
113
114STATIC const CFRuntimeClass __EAPOLClientProfileClass = {
115    0,					/* version */
116    "EAPOLClientProfile",		/* className */
117    NULL,				/* init */
118    NULL,				/* copy */
119    __EAPOLClientProfileDeallocate,	/* deallocate */
120    __EAPOLClientProfileEqual,		/* equal */
121    __EAPOLClientProfileHash,		/* hash */
122    NULL,				/* copyFormattingDesc */
123    __EAPOLClientProfileCopyDebugDesc	/* copyDebugDesc */
124};
125
126STATIC CFStringRef
127__EAPOLClientProfileCopyDebugDesc(CFTypeRef cf)
128{
129    CFAllocatorRef		allocator = CFGetAllocator(cf);
130    EAPOLClientProfileRef	profile = (EAPOLClientProfileRef)cf;
131    CFMutableStringRef		result;
132
133    result = CFStringCreateMutable(allocator, 0);
134    CFStringAppendFormat(result, NULL,
135			 CFSTR("<EAPOLClientProfile %p [%p]> {"),
136			 cf, allocator);
137
138    CFStringAppendFormat(result, NULL,
139			 CFSTR("ProfileID = %@"),
140			 profile->uuid);
141    if (profile->user_defined_name != NULL) {
142	CFStringAppendFormat(result, NULL,
143			     CFSTR(" Name = '%@'"),
144			     profile->user_defined_name);
145    }
146    if (profile->WLAN.ssid != NULL) {
147	CFStringRef ssid_str = my_CFStringCreateWithData(profile->WLAN.ssid);
148
149	CFStringAppendFormat(result, NULL,
150			     CFSTR(", WLAN SSID %@ [%@]"),
151			     ssid_str, profile->WLAN.security_type);
152	CFRelease(ssid_str);
153    }
154    if (profile->auth_props != NULL) {
155	CFStringAppendFormat(result, NULL,
156			     CFSTR(", auth_props = %@"),
157			     profile->auth_props);
158    }
159    if (profile->information != NULL
160	&& CFDictionaryGetCount(profile->information) != 0) {
161	CFStringAppendFormat(result, NULL,
162			     CFSTR(", info = %@"),
163			     profile->information);
164    }
165    CFStringAppendFormat(result, NULL, CFSTR("}"));
166    return result;
167}
168
169
170STATIC void
171__EAPOLClientProfileDeallocate(CFTypeRef cf)
172{
173    EAPOLClientProfileRef profile = (EAPOLClientProfileRef)cf;
174
175    my_CFRelease(&profile->uuid);
176    my_CFRelease(&profile->auth_props);
177    my_CFRelease(&profile->user_defined_name);
178    my_CFRelease(&profile->WLAN.ssid);
179    my_CFRelease(&profile->WLAN.security_type);
180    my_CFRelease(&profile->information);
181    return;
182}
183
184
185STATIC Boolean
186__EAPOLClientProfileEqual(CFTypeRef cf1, CFTypeRef cf2)
187{
188    EAPOLClientProfileRef 	prof1 = (EAPOLClientProfileRef)cf1;
189    EAPOLClientProfileRef	prof2 = (EAPOLClientProfileRef)cf2;
190
191    if (CFEqual(prof1->uuid, prof2->uuid) == FALSE) {
192	return (FALSE);
193    }
194    if (my_CFEqual(prof1->auth_props, prof2->auth_props) == FALSE) {
195	return (FALSE);
196    }
197    if (my_CFEqual(prof1->user_defined_name,
198		   prof2->user_defined_name) == FALSE) {
199	return (FALSE);
200    }
201    if (my_CFEqual(prof1->WLAN.ssid, prof2->WLAN.ssid) == FALSE) {
202	return (FALSE);
203    }
204    if (my_CFEqual(prof1->WLAN.security_type, prof2->WLAN.security_type)
205	== FALSE) {
206	return (FALSE);
207    }
208    if (my_CFEqual(prof1->information, prof2->information) == FALSE) {
209	return (FALSE);
210    }
211    return (TRUE);
212}
213
214STATIC CFHashCode
215__EAPOLClientProfileHash(CFTypeRef cf)
216{
217    EAPOLClientProfileRef 	profile = (EAPOLClientProfileRef)cf;
218
219    return (CFHash(profile->uuid));
220}
221
222
223STATIC void
224__EAPOLClientProfileInitialize(void)
225{
226    /* initialize runtime */
227    __kEAPOLClientProfileTypeID
228	= _CFRuntimeRegisterClass(&__EAPOLClientProfileClass);
229    return;
230}
231
232STATIC void
233__EAPOLClientProfileRegisterClass(void)
234{
235    STATIC pthread_once_t	initialized = PTHREAD_ONCE_INIT;
236
237    pthread_once(&initialized, __EAPOLClientProfileInitialize);
238    return;
239}
240
241STATIC EAPOLClientProfileRef
242__EAPOLClientProfileAllocate(CFAllocatorRef allocator)
243{
244    EAPOLClientProfileRef	profile;
245    int				size;
246
247    __EAPOLClientProfileRegisterClass();
248
249    size = sizeof(*profile) - sizeof(CFRuntimeBase);
250    profile = (EAPOLClientProfileRef)
251	_CFRuntimeCreateInstance(allocator,
252				 __kEAPOLClientProfileTypeID, size, NULL);
253    bzero(((void *)profile) + sizeof(CFRuntimeBase), size);
254    return (profile);
255}
256
257/**
258 ** EAPOLClientProfile APIs
259 **/
260
261CFTypeID
262EAPOLClientProfileGetTypeID(void)
263{
264    __EAPOLClientProfileRegisterClass();
265    return (__kEAPOLClientProfileTypeID);
266}
267
268/*
269 * Function: EAPOLClientProfileCreate
270 *
271 * Purpose:
272 *   Instantiate a new profile to be filled in by calling the various
273 *   "setter" functions:
274 *	EAPOLClientProfileSetUserDefinedName
275 *	EAPOLClientProfileSetAuthenticationProperties
276 *	EAPOLClientProfileSetWLANSSIDAndSecurityType
277 *	EAPOLClientProfileSetWLANDomain
278 *	EAPOLClientProfileSetInformation
279 *
280 */
281EAPOLClientProfileRef
282EAPOLClientProfileCreate(EAPOLClientConfigurationRef cfg)
283{
284    CFAllocatorRef			alloc = CFGetAllocator(cfg);
285    EAPOLClientProfileRef		profile;
286
287    profile = __EAPOLClientProfileAllocate(alloc);
288    if (profile == NULL) {
289	return (NULL);
290    }
291    profile->uuid = my_CFUUIDStringCreate(alloc);
292    if (EAPOLClientConfigurationAddProfile(cfg, profile) == FALSE) {
293	/* this should not happen */
294	my_CFRelease(&profile);
295    }
296    return (profile);
297}
298
299/*
300 * Function: EAPOLClientProfileGetUserDefinedName
301 *
302 * Purpose:
303 *   Retrieve the user defined string associated with the profile.
304 *
305 * Returns:
306 *   NULL if no user defined name is set, non-NULL otherwise.
307 */
308CFStringRef
309EAPOLClientProfileGetUserDefinedName(EAPOLClientProfileRef profile)
310{
311    return (profile->user_defined_name);
312}
313
314
315/*
316 * Function: EAPOLClientProfileGetUserDefinedName
317 *
318 * Purpose:
319 *   Set the user defined string associated with the profile.
320 *
321 * Notes:
322 *   If user_defined_name is NULL, the user visible name is removed,
323 *   otherwise it is set to the value passed.
324 */
325void
326EAPOLClientProfileSetUserDefinedName(EAPOLClientProfileRef profile,
327				     CFStringRef user_defined_name)
328{
329    if (user_defined_name != NULL) {
330	CFRetain(user_defined_name);
331    }
332    if (profile->user_defined_name != NULL) {
333	CFRelease(profile->user_defined_name);
334    }
335    profile->user_defined_name = user_defined_name;
336    return;
337}
338
339/*
340 * Function: EAPOLClientProfileGetID
341 *
342 * Purpose:
343 *   Get the unique identifier for the profile.
344 */
345CFStringRef
346EAPOLClientProfileGetID(EAPOLClientProfileRef profile)
347{
348    return (profile->uuid);
349}
350
351/*
352 * Function: EAPOLClientProfileGetAuthenticationProperties
353 *
354 * Purpose:
355 *   Returns the EAP client authentication properties for the profile.
356 *   The individual keys in the dictionary are defined in
357 *   <EAP8021X/EAPClientProperties.h>.
358 */
359CFDictionaryRef
360EAPOLClientProfileGetAuthenticationProperties(EAPOLClientProfileRef profile)
361{
362    return (profile->auth_props);
363}
364
365/*
366 * Function: EAPOLClientProfileSetAuthenticationProperties
367 * Purpose:
368 *   Set the EAP client authentication properties for the profile.
369 *   The individual keys in the dictionary are defined in
370 *   <EAP8021X/EAPClientProperties.h>.
371 */
372void
373EAPOLClientProfileSetAuthenticationProperties(EAPOLClientProfileRef profile,
374					      CFDictionaryRef auth_props)
375{
376    if (auth_props != NULL) {
377	CFRetain(auth_props);
378    }
379    if (profile->auth_props != NULL) {
380	CFRelease(profile->auth_props);
381    }
382    profile->auth_props = auth_props;
383    return;
384}
385
386/*
387 * Function: EAPOLClientProfileGetWLANSSIDAndSecurityType
388 *
389 * Purpose:
390 *   Get the SSID and security type associated with the profile.
391 *
392 * Returns:
393 *   non-NULL SSID and security type if the profile is bound to a WLAN SSID,
394 *   NULL otherwise.
395 */
396CFDataRef
397EAPOLClientProfileGetWLANSSIDAndSecurityType(EAPOLClientProfileRef profile,
398					     CFStringRef * ret_security_type)
399{
400    if (profile->WLAN.ssid != NULL) {
401	if (ret_security_type != NULL) {
402	    *ret_security_type = profile->WLAN.security_type;
403	}
404	return (profile->WLAN.ssid);
405    }
406    if (ret_security_type != NULL) {
407	*ret_security_type = NULL;
408    }
409    return (NULL);
410}
411
412/*
413 * Function: EAPOLClientProfileSetWLANSSIDAndSecurityType
414 *
415 * Purpose:
416 *   Bind the profile to a particular SSID, and specify the expected
417 *   security type i.e. WEP, WPA, WPA2 or any.  Only a single profile can be
418 *   associated with a particular SSID.
419 *
420 *   To un-bind the profile from its SSID, set the ssid argument to NULL.
421 *   In that case, the security_type argument will be ignored.
422 *
423 * Returns:
424 *    FALSE if there's an existing profile with the same SSID, or the
425 *    security_type is not one of the defined strings above,
426 *    TRUE otherwise.
427 */
428Boolean
429EAPOLClientProfileSetWLANSSIDAndSecurityType(EAPOLClientProfileRef profile,
430					     CFDataRef ssid,
431					     CFStringRef security_type)
432{
433    EAPOLClientProfileRef	existing_profile;
434
435    if (ssid != NULL) {
436	if (profile->WLAN.domain != NULL) {
437	    /* can't specify an SSID when domain is already specified */
438	    return (FALSE);
439	}
440	if (security_type == NULL) {
441	    /* both SSID and security_type must be specified */
442	    return (FALSE);
443	}
444	if (profile->cfg != NULL) {
445	    existing_profile
446		= EAPOLClientConfigurationGetProfileWithWLANSSID(profile->cfg,
447								 ssid);
448	    if (existing_profile != NULL && existing_profile != profile) {
449		/* some other profile has this SSID already */
450		return (FALSE);
451	    }
452	}
453    }
454    else if (security_type != NULL) {
455	/* SSID is NULL, so security_type must also be NULL */
456	return (FALSE);
457    }
458
459    /* give up the existing SSID */
460    if (profile->WLAN.ssid != NULL && profile->cfg != NULL) {
461	/* clear the old binding */
462	EAPOLClientConfigurationSetProfileForSSID(profile->cfg,
463						  profile->WLAN.ssid,
464						  NULL);
465    }
466
467    /* claim the new SSID */
468    if (ssid != NULL) {
469	CFRetain(ssid);
470	if (profile->cfg != NULL) {
471	    EAPOLClientConfigurationSetProfileForSSID(profile->cfg,
472						      ssid,
473						      profile);
474	}
475    }
476    if (security_type != NULL) {
477	CFRetain(security_type);
478    }
479    if (profile->WLAN.ssid != NULL) {
480	CFRelease(profile->WLAN.ssid);
481    }
482    if (profile->WLAN.security_type != NULL) {
483	CFRelease(profile->WLAN.security_type);
484    }
485    profile->WLAN.ssid = ssid;
486    profile->WLAN.security_type = security_type;
487    return (TRUE);
488}
489
490/*
491 * Function: EAPOLClientProfileGetWLANDomain
492 *
493 * Purpose:
494 *   Get the WLAN Hotspot 2.0 domain name associated with the profile.
495 *
496 * Returns:
497 *   non-NULL domain name if the profile is bound to a Hotspot 2.0 WLAN
498 *   domain name, NULL otherwise.
499 */
500CFStringRef
501EAPOLClientProfileGetWLANDomain(EAPOLClientProfileRef profile)
502{
503    return (profile->WLAN.domain);
504}
505
506/*
507 * Function: EAPOLClientProfileSetWLANDomain
508 *
509 * Purpose:
510 *   Bind the profile to a Hotspot 2.0 domain name.
511 *
512 *   Only a single profile can be associated with a particular domain name.
513 *
514 *   To un-bind the profile from the domain name, set the domain
515 *   argument to NULL.
516 *
517 * Returns:
518 *    FALSE if there's an existing profile with the same domain name,
519 *    TRUE otherwise.
520 *
521 * Note:
522 *    EAPOLClientProfileSetWLANSSIDAndSecurityType() and
523 *    EAPOLClientProfileSetWLANDomain() are mutally exclusive.
524 *    A given profile can only be associated with a WLAN SSID *or* a
525 *    WLAN domain and not both.
526 */
527Boolean
528EAPOLClientProfileSetWLANDomain(EAPOLClientProfileRef profile,
529				CFStringRef domain)
530{
531    EAPOLClientProfileRef	existing_profile;
532
533    if (domain != NULL) {
534	if (profile->WLAN.ssid != NULL) {
535	    /* can't specify a domain when ssid is already specified */
536	    return (FALSE);
537	}
538	if (profile->cfg != NULL) {
539	    existing_profile
540		= EAPOLClientConfigurationGetProfileWithWLANDomain(profile->cfg,
541								   domain);
542	    if (existing_profile != NULL && existing_profile != profile) {
543		/* some other profile has this domain already */
544		return (FALSE);
545	    }
546	}
547    }
548
549    /* give up the existing domain */
550    if (profile->WLAN.domain != NULL && profile->cfg != NULL) {
551	/* clear the old binding */
552	EAPOLClientConfigurationSetProfileForWLANDomain(profile->cfg,
553							profile->WLAN.domain,
554							NULL);
555    }
556
557    /* claim the domain */
558    if (domain != NULL) {
559	CFRetain(domain);
560	if (profile->cfg != NULL) {
561	    EAPOLClientConfigurationSetProfileForWLANDomain(profile->cfg,
562							    domain,
563							    profile);
564	}
565    }
566    if (profile->WLAN.domain != NULL) {
567	CFRelease(profile->WLAN.domain);
568    }
569    profile->WLAN.domain = domain;
570    return (TRUE);
571}
572
573/*
574 * Function: EAPOLClientProfileSetInformation
575 *
576 * Purpose:
577 *   Associate additional information with the profile using the given
578 *   application identifier.
579 *
580 *   If info is NULL, the information for the particular application is cleared.
581 *
582 * Note:
583 *   applicationID must be an application identifier e.g. "com.mycompany.myapp".
584 */
585Boolean
586EAPOLClientProfileSetInformation(EAPOLClientProfileRef profile,
587				 CFStringRef applicationID,
588				 CFDictionaryRef information)
589{
590    if (applicationID_is_valid(applicationID) == FALSE) {
591	return (FALSE);
592    }
593    if (information == NULL) {
594	if (profile->information != NULL) {
595	    CFDictionaryRemoveValue(profile->information, applicationID);
596	}
597    }
598    else {
599	if (isA_CFDictionary(information) == NULL) {
600	    return (FALSE);
601	}
602	if (profile->information == NULL) {
603	    profile->information
604		= CFDictionaryCreateMutable(NULL, 0,
605					    &kCFTypeDictionaryKeyCallBacks,
606					    &kCFTypeDictionaryValueCallBacks);
607	}
608	CFDictionarySetValue(profile->information, applicationID, information);
609    }
610    return (TRUE);
611}
612
613/*
614 * Function: EAPOLClientProfileGetInformation
615 *
616 * Purpose:
617 *   Retrieve the additional information associated with the profile using
618 *   the given application identifier.
619 *
620 * Returns:
621 *   NULL if no such application identifier exists,
622 *   non-NULL dictionary otherwise.
623 *
624 * Note:
625 *   applicationID must be an application identifier e.g. "com.mycompany.myapp".
626 */
627CFDictionaryRef
628EAPOLClientProfileGetInformation(EAPOLClientProfileRef profile,
629				 CFStringRef applicationID)
630{
631    CFDictionaryRef	dict;
632
633    if (profile->information == NULL) {
634	return (NULL);
635    }
636    if (applicationID_is_valid(applicationID) == FALSE) {
637	return (NULL);
638    }
639    dict = CFDictionaryGetValue(profile->information, applicationID);
640    return (isA_CFDictionary(dict));
641}
642
643/*
644 * Function: EAPOLClientProfileCreatePropertyList
645 *
646 * Purpose:
647 *   Create an "external" format for a profile.
648 *
649 * Returns:
650 *   NULL if the profile could not be externalized, non-NULL otherwise.
651 */
652CFPropertyListRef
653EAPOLClientProfileCreatePropertyList(EAPOLClientProfileRef profile)
654{
655    CFMutableDictionaryRef	dict;
656    CFStringRef			profileID;
657
658    dict = EAPOLClientProfileCreateDictAndProfileID(profile, &profileID);
659    if (dict == NULL) {
660	return (NULL);
661    }
662    CFDictionarySetValue(dict, kProfileKeyProfileID, profileID);
663    return ((CFPropertyListRef)dict);
664}
665
666/*
667 * Function: EAPOLClientProfileCreateWithPropertyList
668 *
669 * Purpose:
670 *   Create a profile using the supplied "external_format".  The profile
671 *   is not tied to the configuration until the function
672 *   EAPOLClientConfigurationAddProfile() is invoked successfully.
673 *   Use EAPOLClientConfigurationGetMatchingProfiles() to check for conflicts
674 *   if calling that function fails.
675 *
676 * Returns:
677 *   NULL if the "external_format" property list was not a valid format,
678 *   non-NULL EAPOLClientProfileRef otherwise.
679 */
680EAPOLClientProfileRef
681EAPOLClientProfileCreateWithPropertyList(CFPropertyListRef external_format)
682{
683    CFDictionaryRef		dict = (CFDictionaryRef)external_format;
684    CFStringRef			profileID;
685
686    if (isA_CFDictionary(dict) == NULL) {
687	return (NULL);
688    }
689    profileID = CFDictionaryGetValue(dict, kProfileKeyProfileID);
690    if (isA_CFString(profileID) == NULL) {
691	return (NULL);
692    }
693    return (EAPOLClientProfileCreateWithDictAndProfileID(dict, profileID));
694}
695
696/**
697 ** Internal API
698 **/
699
700PRIVATE_EXTERN CFMutableDictionaryRef
701EAPOLClientProfileCreateDictAndProfileID(EAPOLClientProfileRef profile,
702					 CFStringRef * ret_profileID)
703{
704    CFArrayRef			accept_types = NULL;
705    CFMutableDictionaryRef	dict;
706
707    if (profile->auth_props != NULL) {
708	accept_types = CFDictionaryGetValue(profile->auth_props,
709					    kEAPClientPropAcceptEAPTypes);
710    }
711    if (accept_types_valid(accept_types) == FALSE) {
712	SCLog(TRUE, LOG_NOTICE,
713	      CFSTR("EAPOLClientConfiguration: profile %@"
714		    " missing/invalid AuthenticationProperties"),
715	      EAPOLClientProfileGetID(profile));
716	return (NULL);
717    }
718    dict = CFDictionaryCreateMutable(NULL, 0,
719				     &kCFTypeDictionaryKeyCallBacks,
720				     &kCFTypeDictionaryValueCallBacks);
721    if (ret_profileID != NULL) {
722	*ret_profileID = CFRetain(profile->uuid);
723    }
724    CFDictionarySetValue(dict,
725			 kProfileKeyAuthenticationProperties,
726			 profile->auth_props);
727    if (profile->user_defined_name != NULL) {
728	CFDictionarySetValue(dict,
729			     kProfileKeyUserDefinedName,
730			     profile->user_defined_name);
731    }
732    if (profile->information != NULL
733	&& CFDictionaryGetCount(profile->information) != 0) {
734	CFDictionarySetValue(dict,
735			     kProfileKeyInformation,
736			     profile->information);
737    }
738    if (profile->WLAN.ssid != NULL || profile->WLAN.domain != NULL) {
739	int			count;
740	const void *		keys[2];
741	CFDictionaryRef		WLAN;
742	const void *		values[2];
743
744	if (profile->WLAN.ssid != NULL) {
745	    keys[0] = kProfileKeyWLANSSID;
746	    values[0] = profile->WLAN.ssid;
747	    keys[1] = kProfileKeyWLANSecurityType;
748	    values[1] = profile->WLAN.security_type;
749	    count = 2;
750	}
751	else {
752	    keys[0] = kProfileKeyWLANDomain;
753	    values[0] = profile->WLAN.domain;
754	    count = 1;
755	}
756	WLAN = CFDictionaryCreate(NULL, keys, values, count,
757				  &kCFTypeDictionaryKeyCallBacks,
758				  &kCFTypeDictionaryValueCallBacks);
759	CFDictionarySetValue(dict, kProfileKeyWLAN, WLAN);
760	CFRelease(WLAN);
761    }
762    return (dict);
763}
764
765PRIVATE_EXTERN EAPOLClientProfileRef
766EAPOLClientProfileCreateWithDictAndProfileID(CFDictionaryRef dict,
767					     CFStringRef profileID)
768{
769    CFAllocatorRef		alloc = CFGetAllocator(dict);
770    CFArrayRef			accept_types;
771    CFDictionaryRef		information;
772    CFStringRef			domain = NULL;
773    CFDictionaryRef		eap_config;
774    EAPOLClientProfileRef	profile;
775    CFStringRef			security_type = NULL;
776    CFStringRef			user_defined_name;
777    CFDataRef			ssid = NULL;
778    CFDictionaryRef		wlan;
779
780    eap_config = CFDictionaryGetValue(dict,
781				      kProfileKeyAuthenticationProperties);
782    if (isA_CFDictionary(eap_config) == NULL
783	|| CFDictionaryGetCount(eap_config) == 0) {
784	SCLog(TRUE, LOG_NOTICE,
785	      CFSTR("EAPOLClientConfiguration: profile %@"
786		    " missing/invalid %@ property"),
787	      profileID, kProfileKeyAuthenticationProperties);
788	return (NULL);
789    }
790    accept_types = CFDictionaryGetValue(eap_config,
791					kEAPClientPropAcceptEAPTypes);
792    if (accept_types_valid(accept_types) == FALSE) {
793	SCLog(TRUE, LOG_NOTICE,
794	      CFSTR("EAPOLClientConfiguration: profile %@"
795		    " missing/invalid %@ property in %@"),
796	      profileID, kEAPClientPropAcceptEAPTypes,
797	      kProfileKeyAuthenticationProperties);
798	return (NULL);
799    }
800    wlan = CFDictionaryGetValue(dict, kProfileKeyWLAN);
801    if (wlan != NULL) {
802	if (isA_CFDictionary(wlan) == NULL) {
803	    SCLog(TRUE, LOG_NOTICE,
804		  CFSTR("EAPOLClientConfiguration: profile %@"
805			" invalid %@ property"),
806		  profileID, kProfileKeyWLAN);
807	    return (NULL);
808	}
809	ssid = CFDictionaryGetValue(wlan, kProfileKeyWLANSSID);
810	domain = CFDictionaryGetValue(wlan, kProfileKeyWLANDomain);
811	if (isA_CFData(ssid) == NULL && isA_CFString(domain) == NULL) {
812	    SCLog(TRUE, LOG_NOTICE,
813		  CFSTR("EAPOLClientConfiguration: profile %@"
814			" invalid/missing property (%@ or %@) in %@"),
815		  profileID, kProfileKeyWLANSSID, kProfileKeyWLANDomain,
816		  kProfileKeyWLAN);
817	    return (NULL);
818	}
819	if (ssid != NULL) {
820	    security_type = CFDictionaryGetValue(wlan,
821						 kProfileKeyWLANSecurityType);
822	    if (isA_CFString(security_type) == NULL) {
823		SCLog(TRUE, LOG_NOTICE,
824		      CFSTR("EAPOLClientConfiguration: profile %@"
825			    " invalid/missing %@ property in %@"),
826		      profileID, kProfileKeyWLANSecurityType, kProfileKeyWLAN);
827		return (NULL);
828	    }
829	}
830    }
831    user_defined_name = CFDictionaryGetValue(dict, kProfileKeyUserDefinedName);
832    if (user_defined_name != NULL && isA_CFString(user_defined_name) == NULL) {
833	SCLog(TRUE, LOG_NOTICE,
834	      CFSTR("EAPOLClientConfiguration: profile %@"
835		    " invalid %@ property"),
836	      profileID, kProfileKeyUserDefinedName);
837	return (NULL);
838    }
839    information = CFDictionaryGetValue(dict, kProfileKeyInformation);
840    if (information != NULL && isA_CFDictionary(information) == NULL) {
841	SCLog(TRUE, LOG_NOTICE,
842	      CFSTR("EAPOLClientConfiguration: profile %@"
843		    " invalid %@ property"),
844	      profileID, kProfileKeyInformation);
845	return (NULL);
846    }
847
848    /* allocate/set an EAPOLClientProfileRef */
849    profile = __EAPOLClientProfileAllocate(alloc);
850    if (profile == NULL) {
851	return (NULL);
852    }
853    profile->uuid = CFRetain(profileID);
854    EAPOLClientProfileSetUserDefinedName(profile, user_defined_name);
855    EAPOLClientProfileSetAuthenticationProperties(profile, eap_config);
856    if (ssid != NULL) {
857	EAPOLClientProfileSetWLANSSIDAndSecurityType(profile, ssid,
858						     security_type);
859    }
860    else if (domain != NULL) {
861	EAPOLClientProfileSetWLANDomain(profile, domain);
862    }
863    if (information != NULL) {
864	profile->information
865	    = CFDictionaryCreateMutableCopy(NULL, 0, information);
866    }
867    return (profile);
868}
869
870PRIVATE_EXTERN void
871EAPOLClientProfileSetConfiguration(EAPOLClientProfileRef profile,
872				   EAPOLClientConfigurationRef cfg)
873{
874    /* we can't retain the configuration, since it retains us */
875    profile->cfg = cfg;
876    return;
877}
878
879PRIVATE_EXTERN EAPOLClientConfigurationRef
880EAPOLClientProfileGetConfiguration(EAPOLClientProfileRef profile)
881{
882    return (profile->cfg);
883}
884