1/*
2 * Copyright (c) 2001-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 * Modification History
26 *
27 * November 8, 2001	Dieter Siegmund
28 * - created
29 */
30
31#include <stdlib.h>
32#include <unistd.h>
33#include <stdio.h>
34#include <sys/types.h>
35#include <sys/param.h>
36#include <fcntl.h>
37#include <sys/errno.h>
38#include <sys/socket.h>
39#include <net/if.h>
40#include <string.h>
41#include <paths.h>
42#include <CommonCrypto/CommonCryptor.h>
43#include <CommonCrypto/CommonHMAC.h>
44#include <EAP8021X/EAPUtil.h>
45#include <EAP8021X/EAPClientModule.h>
46#include <EAP8021X/EAPClientProperties.h>
47#include <EAP8021X/EAPOLControlTypes.h>
48#include <EAP8021X/EAPOLControlTypesPrivate.h>
49#include <EAP8021X/EAPOLControl.h>
50#include <EAP8021X/SupplicantTypes.h>
51#include <EAP8021X/EAPKeychainUtil.h>
52#include <TargetConditionals.h>
53#if ! TARGET_OS_EMBEDDED
54#include <EAP8021X/EAPOLClientConfiguration.h>
55#include <EAP8021X/EAPOLClientConfigurationPrivate.h>
56#include <notify.h>
57#endif /* ! TARGET_OS_EMBEDDED */
58#include <EAP8021X/EAPCertificateUtil.h>
59#include <CoreFoundation/CFString.h>
60#include <CoreFoundation/CFDictionary.h>
61#include <CoreFoundation/CFArray.h>
62#include <CoreFoundation/CFBundle.h>
63#include <SystemConfiguration/SystemConfiguration.h>
64#include <SystemConfiguration/SCValidation.h>
65#include <SystemConfiguration/SCPrivate.h>
66
67#if ! TARGET_OS_EMBEDDED
68#include <Security/SecKeychain.h>
69#include <Security/SecKeychainItem.h>
70#include "Dialogue.h"
71#include <OpenDirectory/OpenDirectoryPriv.h>
72#include <opendirectory/adsupport.h>
73#endif /* ! TARGET_OS_EMBEDDED */
74#include <Security/SecureTransport.h>
75#include "symbol_scope.h"
76#include "Supplicant.h"
77#include "Timer.h"
78#include "EAPOLSocket.h"
79#include "printdata.h"
80#include "mylog.h"
81#include "myCFUtil.h"
82#include "ClientControlInterface.h"
83
84#define START_PERIOD_SECS		5
85#define START_ATTEMPTS_MAX		3
86#define AUTH_PERIOD_SECS		5
87#define AUTH_ATTEMPTS_MAX		4
88#define HELD_PERIOD_SECS		60
89#define LINK_ACTIVE_PERIOD_SECS		5
90#define LINK_INACTIVE_PERIOD_SECS	1
91
92#define BAD_IDENTIFIER		(-1)
93
94#define kSupplicant		CFSTR("Supplicant")
95#define kStartPeriodSeconds	CFSTR("StartPeriodSeconds")
96#define kStartAttemptsMax	CFSTR("StartAttemptsMax")
97#define kAuthPeriodSeconds	CFSTR("AuthPeriodSeconds")
98#define kAuthAttemptsMax	CFSTR("AuthAttemptsMax")
99#define kHeldPeriodSeconds	CFSTR("HeldPeriodSeconds")
100
101static int	S_start_period_secs = START_PERIOD_SECS;
102static int	S_start_attempts_max = START_ATTEMPTS_MAX;
103static int	S_auth_period_secs = AUTH_PERIOD_SECS;
104static int	S_auth_attempts_max = AUTH_ATTEMPTS_MAX;
105static int	S_held_period_secs = HELD_PERIOD_SECS;
106
107static int	S_link_active_period_secs = LINK_ACTIVE_PERIOD_SECS;
108static int	S_link_inactive_period_secs = LINK_INACTIVE_PERIOD_SECS;
109
110struct eap_client {
111    EAPClientModuleRef		module;
112    EAPClientPluginData		plugin_data;
113    CFArrayRef			required_props;
114    CFDictionaryRef		published_props;
115    EAPType			last_type;
116    const char *		last_type_name;
117};
118
119typedef struct {
120    int	*		types;
121    int			count;
122    int			index;
123    bool		use_outer_identity;
124} EAPAcceptTypes, * EAPAcceptTypesRef;
125
126struct Supplicant_s {
127    SupplicantState		state;
128    TimerRef			timer;
129    EAPOLSocket *		sock;
130
131    uint32_t			generation;
132
133    CFDictionaryRef		orig_config_dict;
134    CFDictionaryRef		config_dict;
135    CFMutableDictionaryRef	ui_config_dict;
136    CFStringRef			config_id;
137
138    int				previous_identifier;
139    char *			outer_identity;
140    int				outer_identity_length;
141    char *			username;
142    int				username_length;
143    char *			password;
144    int				password_length;
145    bool			username_derived;
146    bool			one_time_password;
147    bool			ignore_password;
148    bool			ignore_username;
149    bool			ignore_sec_identity;
150    bool			remember_information;
151
152    SecIdentityRef		sec_identity;
153
154    EAPAcceptTypes		eap_accept;
155
156    CFArrayRef			identity_attributes;
157
158#if ! TARGET_OS_EMBEDDED
159    EAPOLClientItemIDRef	itemID;
160    EAPOLClientConfigurationRef	eapolcfg;
161    AlertDialogueRef		alert_prompt;
162    CredentialsDialogueRef	cred_prompt;
163    TrustDialogueRef		trust_prompt;
164    long			credentials_access_time;
165    CFStringRef			manager_name;
166    struct {
167	CFMachPortRef		mp;
168	int			token;
169    } config_change;
170    int				failure_count;
171#endif /* ! TARGET_OS_EMBEDDED */
172
173    int				start_count;
174    int				auth_attempts_count;
175
176    bool			no_authenticator;
177
178    struct eap_client		eap;
179
180    EAPOLSocketReceiveData	last_rx_packet;
181    EAPClientStatus		last_status;
182    EAPClientDomainSpecificError last_error;
183
184    bool			pmk_set;
185
186    bool			no_ui;
187};
188
189typedef enum {
190    kSupplicantEventStart,
191    kSupplicantEventData,
192    kSupplicantEventTimeout,
193    kSupplicantEventUserResponse,
194    kSupplicantEventMoreDataAvailable,
195} SupplicantEvent;
196
197#define INDEX_NONE		(-1)
198
199static Boolean
200myCFDictionaryGetBooleanValue(CFDictionaryRef properties, CFStringRef propname,
201			      Boolean def_value);
202static bool
203S_set_credentials(SupplicantRef supp);
204
205static void
206Supplicant_acquired(SupplicantRef supp, SupplicantEvent event,
207		    void * evdata);
208static void
209Supplicant_authenticated(SupplicantRef supp, SupplicantEvent event,
210			 void * evdata);
211static void
212Supplicant_authenticating(SupplicantRef supp, SupplicantEvent event,
213			  void * evdata);
214static void
215Supplicant_connecting(SupplicantRef supp, SupplicantEvent event,
216		      void * evdata);
217static void
218Supplicant_held(SupplicantRef supp, SupplicantEvent event,
219		void * evdata);
220
221static void
222Supplicant_logoff(SupplicantRef supp, SupplicantEvent event, void * evdata);
223
224static void
225Supplicant_inactive(SupplicantRef supp, SupplicantEvent event, void * evdata);
226
227static void
228Supplicant_report_status(SupplicantRef supp);
229
230static void
231respond_to_notification(SupplicantRef supp, int identifier);
232
233/**
234 ** Logging
235 **/
236static CFStringRef
237copy_cleaned_config_dict(CFDictionaryRef d);
238
239static void
240log_eap_notification(SupplicantState state, EAPRequestPacketRef req_p)
241{
242    int			len;
243    CFStringRef		str;
244
245    len = EAPPacketGetLength((EAPPacketRef)req_p) - sizeof(*req_p);
246    if (len > 0) {
247	str =  CFStringCreateWithBytes(NULL, req_p->type_data, len,
248				       kCFStringEncodingUTF8, FALSE);
249    }
250    else {
251	str = NULL;
252    }
253    EAPLOG(LOG_NOTICE, "%s: Notification '%@'",
254	   SupplicantStateString(state),
255	   (str != NULL) ? str : CFSTR(""));
256    my_CFRelease(&str);
257    return;
258}
259
260/**
261 ** Utility routines
262 **/
263static bool
264S_array_contains_int(CFArrayRef array, int val)
265{
266    CFIndex	count;
267    int		i;
268
269    if (array == NULL) {
270	goto done;
271    }
272
273    count = CFArrayGetCount(array);
274    for (i = 0; i < count; i++) {
275	CFNumberRef	num = CFArrayGetValueAtIndex(array, i);
276	int		num_val;
277
278	if (isA_CFNumber(num) == NULL) {
279	    continue;
280	}
281	if (CFNumberGetValue(num, kCFNumberIntType, &num_val)
282	    && num_val == val) {
283	    return (TRUE);
284	}
285    }
286
287 done:
288    return (FALSE);
289}
290
291static char *
292S_identity_copy_name(SecIdentityRef sec_identity)
293{
294    SecCertificateRef		cert;
295    char *			name = NULL;
296    OSStatus			status;
297
298    status = SecIdentityCopyCertificate(sec_identity, &cert);
299    if (status != noErr) {
300	EAPLOG_FL(LOG_NOTICE,
301		  "EAPSecIdentityHandleCreateSecIdentity failed, %d",
302		  status);
303    }
304    else {
305	CFStringRef		name_cf;
306
307	name_cf = EAPSecCertificateCopyUserNameString(cert);
308	CFRelease(cert);
309	if (name_cf != NULL) {
310	    name = my_CFStringToCString(name_cf, kCFStringEncodingUTF8);
311	    CFRelease(name_cf);
312	}
313    }
314    return (name);
315}
316
317/**
318 ** EAP client module access convenience routines
319 **/
320
321static void
322eap_client_free_properties(SupplicantRef supp)
323{
324    my_CFRelease((CFDictionaryRef *)&supp->eap.plugin_data.properties);
325}
326
327#if TARGET_OS_EMBEDDED
328
329static void
330eap_client_set_properties(SupplicantRef supp)
331{
332    CFStringRef		config_domain;
333    CFStringRef		config_ident;
334
335    eap_client_free_properties(supp);
336
337    config_domain
338	= CFDictionaryGetValue(supp->config_dict,
339			       kEAPClientPropTLSTrustExceptionsDomain);
340    config_ident
341	= CFDictionaryGetValue(supp->config_dict,
342			       kEAPClientPropTLSTrustExceptionsID);
343    if (config_domain != NULL && config_ident != NULL) {
344	/* configuration already specifies the trust domain/ID */
345	*((CFDictionaryRef *)&supp->eap.plugin_data.properties)
346	    = CFRetain(supp->config_dict);
347    }
348    else {
349	CFMutableDictionaryRef	dict;
350	CFStringRef		domain;
351	CFStringRef		ident;
352	CFStringRef		if_name_cf = NULL;
353
354	ident = EAPOLSocketGetSSID(supp->sock);
355	if (ident != NULL) {
356	    domain = kEAPTLSTrustExceptionsDomainWirelessSSID;
357	}
358	else if (supp->config_id != NULL) {
359	    domain = kEAPTLSTrustExceptionsDomainProfileID;
360	    ident = supp->config_id;
361	}
362	else {
363	    if_name_cf
364		= CFStringCreateWithCString(NULL,
365					    EAPOLSocketIfName(supp->sock, NULL),
366					    kCFStringEncodingASCII);
367	    domain = kEAPTLSTrustExceptionsDomainNetworkInterfaceName;
368	    ident = if_name_cf;
369	}
370	dict = CFDictionaryCreateMutableCopy(NULL, 0,
371					     supp->config_dict);
372	CFDictionarySetValue(dict,
373			     kEAPClientPropTLSTrustExceptionsDomain,
374			     domain);
375	CFDictionarySetValue(dict,
376			     kEAPClientPropTLSTrustExceptionsID,
377			     ident);
378	*((CFDictionaryRef *)&supp->eap.plugin_data.properties) = dict;
379	my_CFRelease(&if_name_cf);
380    }
381    return;
382}
383#else /* TARGET_OS_EMBEDDED */
384
385static void
386eap_client_set_properties(SupplicantRef supp)
387{
388    eap_client_free_properties(supp);
389    *((CFDictionaryRef *)&supp->eap.plugin_data.properties)
390	= CFRetain(supp->config_dict);
391    return;
392}
393#endif /* TARGET_OS_EMBEDDED */
394
395static void
396eap_client_free(SupplicantRef supp)
397{
398    if (supp->eap.module != NULL) {
399	EAPClientModulePluginFree(supp->eap.module, &supp->eap.plugin_data);
400	supp->eap.module = NULL;
401	eap_client_free_properties(supp);
402	bzero(&supp->eap.plugin_data, sizeof(supp->eap.plugin_data));
403    }
404    my_CFRelease(&supp->eap.required_props);
405    my_CFRelease(&supp->eap.published_props);
406    supp->eap.last_type = kEAPTypeInvalid;
407    supp->eap.last_type_name = NULL;
408    return;
409}
410
411static EAPType
412eap_client_type(SupplicantRef supp)
413{
414    if (supp->eap.module == NULL) {
415	return (kEAPTypeInvalid);
416    }
417    return (EAPClientModulePluginEAPType(supp->eap.module));
418}
419
420static __inline__ void
421S_set_uint32(const uint32_t * v_p, uint32_t value)
422{
423    *((uint32_t *)v_p) = value;
424    return;
425}
426
427static bool
428eap_client_init(SupplicantRef supp, EAPType type)
429{
430    EAPClientModule *		module;
431
432    supp->eap.last_type = kEAPTypeInvalid;
433    supp->eap.last_type_name = NULL;
434
435    if (supp->eap.module != NULL) {
436	EAPLOG_FL(LOG_NOTICE, "already initialized");
437	return (TRUE);
438    }
439    module = EAPClientModuleLookup(type);
440    if (module == NULL) {
441	return (FALSE);
442    }
443    my_CFRelease(&supp->eap.required_props);
444    my_CFRelease(&supp->eap.published_props);
445    bzero(&supp->eap.plugin_data, sizeof(supp->eap.plugin_data));
446    supp->eap.plugin_data.unique_id
447	= EAPOLSocketIfName(supp->sock, (uint32_t *)
448			    &supp->eap.plugin_data.unique_id_length);
449    S_set_uint32(&supp->eap.plugin_data.mtu,
450		 EAPOLSocketMTU(supp->sock) - sizeof(EAPOLPacket));
451
452    supp->eap.plugin_data.username = (uint8_t *)supp->username;
453    S_set_uint32(&supp->eap.plugin_data.username_length,
454		 supp->username_length);
455    supp->eap.plugin_data.password = (uint8_t *)supp->password;
456    S_set_uint32(&supp->eap.plugin_data.password_length,
457		 supp->password_length);
458    eap_client_set_properties(supp);
459    supp->eap.plugin_data.sec_identity = supp->sec_identity;
460    *((bool *)&supp->eap.plugin_data.log_enabled)
461	= ((eapolclient_log_flags() & kLogFlagDisableInnerDetails) == 0);
462    *((bool *)&supp->eap.plugin_data.system_mode)
463	= (EAPOLSocketGetMode(supp->sock) == kEAPOLControlModeSystem);
464    supp->last_status =
465	EAPClientModulePluginInit(module, &supp->eap.plugin_data,
466				  &supp->eap.required_props,
467				  &supp->last_error);
468    supp->eap.last_type_name = EAPClientModulePluginEAPName(module);
469    supp->eap.last_type = type;
470    if (supp->last_status != kEAPClientStatusOK) {
471	return (FALSE);
472    }
473    supp->eap.module = module;
474    return (TRUE);
475}
476
477static CFArrayRef
478eap_client_require_properties(SupplicantRef supp)
479{
480    return (EAPClientModulePluginRequireProperties(supp->eap.module,
481						   &supp->eap.plugin_data));
482}
483
484static CFDictionaryRef
485eap_client_publish_properties(SupplicantRef supp)
486{
487    return (EAPClientModulePluginPublishProperties(supp->eap.module,
488						   &supp->eap.plugin_data));
489}
490
491static EAPClientState
492eap_client_process(SupplicantRef supp, EAPPacketRef in_pkt_p,
493		   EAPPacketRef * out_pkt_p, EAPClientStatus * status,
494		   EAPClientDomainSpecificError * error)
495{
496    EAPClientState cstate;
497
498    supp->eap.plugin_data.username = (uint8_t *)supp->username;
499    S_set_uint32(&supp->eap.plugin_data.username_length,
500		 supp->username_length);
501    supp->eap.plugin_data.password = (uint8_t *)supp->password;
502    S_set_uint32(&supp->eap.plugin_data.password_length,
503		 supp->password_length);
504    S_set_uint32(&supp->eap.plugin_data.generation,
505		 supp->generation);
506    eap_client_set_properties(supp);
507    *((bool *)&supp->eap.plugin_data.log_enabled)
508	= ((eapolclient_log_flags() & kLogFlagDisableInnerDetails) == 0);
509    cstate = EAPClientModulePluginProcess(supp->eap.module,
510					  &supp->eap.plugin_data,
511					  in_pkt_p, out_pkt_p,
512					  status, error);
513    return (cstate);
514}
515
516static void
517eap_client_free_packet(SupplicantRef supp, EAPPacketRef out_pkt_p)
518{
519    EAPClientModulePluginFreePacket(supp->eap.module,
520				    &supp->eap.plugin_data,
521				    out_pkt_p);
522}
523
524static void
525eap_client_log_failure(SupplicantRef supp)
526{
527    const char * err;
528    err = EAPClientModulePluginFailureString(supp->eap.module,
529					     &supp->eap.plugin_data);
530    if (err) {
531	EAPLOG(LOG_NOTICE, "error string '%s'", err);
532    }
533    return;
534}
535
536static void *
537eap_client_session_key(SupplicantRef supp, int * key_length)
538{
539    return (EAPClientModulePluginSessionKey(supp->eap.module,
540					    &supp->eap.plugin_data,
541					    key_length));
542}
543
544static void *
545eap_client_server_key(SupplicantRef supp, int * key_length)
546{
547    return (EAPClientModulePluginServerKey(supp->eap.module,
548					   &supp->eap.plugin_data,
549					   key_length));
550}
551
552static int
553eap_client_master_session_key_copy_bytes(SupplicantRef supp,
554					 void * msk, int msk_length)
555{
556    return (EAPClientModulePluginMasterSessionKeyCopyBytes(supp->eap.module,
557							   &supp->eap.plugin_data,
558							   msk, msk_length));
559}
560
561/**
562 ** EAPAcceptTypes routines
563 **/
564
565static void
566EAPAcceptTypesCopy(EAPAcceptTypesRef dest, EAPAcceptTypesRef src)
567{
568    *dest = *src;
569    dest->types = (int *)malloc(src->count * sizeof(*dest->types));
570    bcopy(src->types, dest->types, src->count * sizeof(*dest->types));
571    return;
572}
573
574static void
575EAPAcceptTypesFree(EAPAcceptTypesRef accept)
576{
577    if (accept->types != NULL) {
578	free(accept->types);
579    }
580    bzero(accept, sizeof(*accept));
581    return;
582}
583
584static void
585EAPAcceptTypesInit(EAPAcceptTypesRef accept, CFArrayRef accept_types)
586{
587    int			i;
588    CFIndex		count;
589    int			tunneled_count;
590    CFNumberRef		type_cf;
591    int			type_i;
592    int *		types;
593    int			types_count;
594
595    EAPAcceptTypesFree(accept);
596    if (accept_types == NULL) {
597	return;
598    }
599    count = CFArrayGetCount(accept_types);
600    if (count == 0) {
601	return;
602    }
603    types = (int *)malloc(count * sizeof(*types));
604    tunneled_count = types_count = 0;
605    for (i = 0; i < count; i++) {
606	int		j;
607
608	type_cf = CFArrayGetValueAtIndex(accept_types, i);
609	if (isA_CFNumber(type_cf) == NULL) {
610	    EAPLOG(LOG_NOTICE,
611		   "AcceptEAPTypes[%d] contains invalid type, ignoring", i);
612	    continue;
613	}
614	if (CFNumberGetValue(type_cf, kCFNumberIntType, &type_i) == FALSE) {
615	    EAPLOG(LOG_NOTICE,
616		   "AcceptEAPTypes[%d] contains invalid number, ignoring", i);
617	    continue;
618	}
619	if (EAPClientModuleLookup(type_i) == NULL) {
620	    EAPLOG(LOG_NOTICE,
621		   "AcceptEAPTypes[%d] specifies unsupported type %d, ignoring",
622		   i, type_i);
623	    continue;
624	}
625	for (j = 0; j < types_count; j++) {
626	    if (types[j] == type_i) {
627		EAPLOG(LOG_NOTICE,
628		       "AcceptEAPTypes[%d] %s (%d) already specified at [%d], "
629		       "ignoring", i, EAPTypeStr(type_i), type_i, j);
630		continue;
631	    }
632	}
633	types[types_count++] = type_i;
634	if (type_i == kEAPTypePEAP
635	    || type_i == kEAPTypeTTLS
636	    || type_i == kEAPTypeEAPFAST) {
637	    tunneled_count++;
638	}
639    }
640    if (types_count == 0) {
641	free(types);
642    }
643    else {
644	accept->types = types;
645	accept->count = types_count;
646	if (tunneled_count == types_count) {
647	    accept->use_outer_identity = TRUE;
648	}
649    }
650    return;
651}
652
653static EAPType
654EAPAcceptTypesNextType(EAPAcceptTypesRef accept)
655{
656    if (accept->types == NULL
657	|| accept->index >= accept->count) {
658	return (kEAPTypeInvalid);
659    }
660    return (accept->types[accept->index++]);
661}
662
663static void
664EAPAcceptTypesReset(EAPAcceptTypesRef accept)
665{
666    accept->index = 0;
667    return;
668}
669
670static int
671EAPAcceptTypesIndexOfType(EAPAcceptTypesRef accept, EAPType type)
672{
673    int			i;
674
675    if (accept->types == NULL) {
676	return (INDEX_NONE);
677    }
678    for (i = 0; i < accept->count; i++) {
679	if (accept->types[i] == type) {
680	    return (i);
681	}
682    }
683    return (INDEX_NONE);
684}
685
686static bool
687EAPAcceptTypesIsSupportedType(EAPAcceptTypesRef accept, EAPType type)
688{
689    return (EAPAcceptTypesIndexOfType(accept, type) != INDEX_NONE);
690}
691
692static bool
693EAPAcceptTypesUseOuterIdentity(EAPAcceptTypesRef accept)
694{
695    return (accept->use_outer_identity);
696}
697
698static void
699EAPAcceptTypesRemoveTypeAtIndex(EAPAcceptTypesRef accept, int type_index)
700{
701    int		i;
702
703    if (type_index >= accept->count || type_index < 0) {
704	/* invalid arg */
705	return;
706    }
707    for (i = type_index; i < (accept->count - 1); i++) {
708	accept->types[i] = accept->types[i + 1];
709    }
710    accept->count--;
711    return;
712}
713
714#if ! TARGET_OS_EMBEDDED
715
716static boolean_t
717EAPAcceptTypesRequirePassword(EAPAcceptTypesRef accept)
718{
719    int			i;
720
721    if (accept->types == NULL) {
722	return (FALSE);
723    }
724    for (i = 0; i < accept->count; i++) {
725	switch (accept->types[i]) {
726	case kEAPTypeTLS:
727	case kEAPTypeEAPSIM:
728	case kEAPTypeEAPAKA:
729	    break;
730	default:
731	    return (TRUE);
732	}
733    }
734    return (FALSE);
735}
736
737#endif /* ! TARGET_OS_EMBEDDED */
738
739/**
740 ** Supplicant routines
741 **/
742
743#if ! TARGET_OS_EMBEDDED
744
745#define CREDENTIALS_ACCESS_DELAY_SECS		2
746
747static void
748S_set_credentials_access_time(SupplicantRef supp)
749{
750    supp->credentials_access_time = Timer_current_secs();
751    return;
752}
753
754static boolean_t
755S_check_for_updated_credentials(SupplicantRef supp)
756{
757    boolean_t	changed = FALSE;
758    long	current_time;
759    long	delta;
760
761    current_time = Timer_current_secs();
762    delta = current_time - supp->credentials_access_time;
763    if (delta < 0
764	|| delta >= CREDENTIALS_ACCESS_DELAY_SECS) {
765	changed = S_set_credentials(supp);
766	eapolclient_log(kLogFlagBasic,
767			"Re-read credentials (%schanged)",
768			changed ? "" : "un");
769    }
770    return (changed);
771}
772
773#endif /* ! TARGET_OS_EMBEDDED */
774
775static void
776clear_password(SupplicantRef supp)
777{
778    supp->ignore_password = TRUE;
779
780    /* clear the password */
781    if (supp->password != NULL) {
782	free(supp->password);
783	supp->password = NULL;
784    }
785    supp->password_length = 0;
786    return;
787}
788
789static void
790clear_username(SupplicantRef supp)
791{
792    supp->ignore_username = TRUE;
793
794    if (supp->username != NULL) {
795	free(supp->username);
796	supp->username = NULL;
797    }
798    supp->username_length = 0;
799    supp->username_derived = FALSE;
800    return;
801}
802
803static void
804clear_sec_identity(SupplicantRef supp)
805{
806    supp->ignore_sec_identity = TRUE;
807    my_CFRelease(&supp->sec_identity);
808    return;
809}
810
811static void
812free_last_packet(SupplicantRef supp)
813{
814    if (supp->last_rx_packet.eapol_p != NULL) {
815	free(supp->last_rx_packet.eapol_p);
816	bzero(&supp->last_rx_packet, sizeof(supp->last_rx_packet));
817    }
818    return;
819}
820
821static void
822save_last_packet(SupplicantRef supp, EAPOLSocketReceiveDataRef rx_p)
823{
824    EAPOLPacketRef	last_eapol_p;
825
826    last_eapol_p = supp->last_rx_packet.eapol_p;
827    if (last_eapol_p == rx_p->eapol_p) {
828	/* don't bother re-saving the same buffer */
829	return;
830    }
831    bzero(&supp->last_rx_packet, sizeof(supp->last_rx_packet));
832    supp->last_rx_packet.eapol_p = (EAPOLPacketRef)malloc(rx_p->length);
833    supp->last_rx_packet.length = rx_p->length;
834    bcopy(rx_p->eapol_p, supp->last_rx_packet.eapol_p, rx_p->length);
835    if (last_eapol_p != NULL) {
836	free(last_eapol_p);
837    }
838    return;
839}
840
841static void
842Supplicant_cancel_pending_events(SupplicantRef supp)
843{
844    EAPOLSocketDisableReceive(supp->sock);
845    Timer_cancel(supp->timer);
846    return;
847}
848
849static void
850S_update_identity_attributes(SupplicantRef supp, void * data, int length)
851{
852    int		props_length;
853    void * 	props_start;
854    CFStringRef	props_string;
855
856    my_CFRelease(&supp->identity_attributes);
857
858    props_start = memchr(data, '\0', length);
859    if (props_start == NULL) {
860	return;
861    }
862    props_start++;	/* skip '\0' */
863    props_length = length - (int)(props_start - data);
864    if (length <= 0) {
865	/* no props there */
866	return;
867    }
868    props_string = CFStringCreateWithBytes(NULL, props_start, props_length,
869					   kCFStringEncodingUTF8, FALSE);
870    if (props_string != NULL) {
871	supp->identity_attributes =
872	    CFStringCreateArrayBySeparatingStrings(NULL, props_string,
873						   CFSTR(","));
874	my_CFRelease(&props_string);
875    }
876    return;
877}
878
879/*
880 * Function: eapol_key_verify_signature
881 *
882 * Purpose:
883 *   Verify that the signature in the key packet is valid.
884 * Notes:
885 *   As documented in IEEE802.1X, section 7.6.8,
886 *   compute the HMAC-MD5 hash over the entire EAPOL key packet
887 *   with a zero signature using the server key as the key.
888 *   The resulting signature is compared with the signature in
889 *   the packet.  If they match, the packet is valid, if not
890 *   it is invalid.
891 * Returns:
892 *   TRUE if the signature is valid, FALSE otherwise.
893 */
894static bool
895eapol_key_verify_signature(EAPOLPacketRef packet, int packet_length,
896			   char * server_key, int server_key_length,
897			   CFMutableStringRef debug_str)
898{
899    EAPOLKeyDescriptorRef	descr_p;
900    EAPOLPacketRef		packet_copy;
901    u_char			signature[16];
902    bool			valid = FALSE;
903
904    /* make a copy of the entire packet */
905    packet_copy = (EAPOLPacketRef)malloc(packet_length);
906    bcopy(packet, packet_copy, packet_length);
907    descr_p = (EAPOLKeyDescriptorRef)packet_copy->body;
908    bzero(descr_p->key_signature, sizeof(descr_p->key_signature));
909    CCHmac(kCCHmacAlgMD5,
910	   server_key, server_key_length,
911	   packet_copy, packet_length,
912	   signature);
913    descr_p = (EAPOLKeyDescriptorRef)packet->body;
914    valid = (bcmp(descr_p->key_signature, signature, sizeof(signature)) == 0);
915    if (debug_str != NULL) {
916	STRING_APPEND(debug_str, "Signature: ");
917	print_bytes_cfstr(debug_str, signature, sizeof(signature));
918	STRING_APPEND(debug_str, " is %s", valid ? "valid" : "INVALID");
919    }
920    free(packet_copy);
921    return (valid);
922}
923
924static void
925process_key(SupplicantRef supp, EAPOLPacketRef eapol_p)
926{
927    int				body_length;
928    CFMutableStringRef		debug_str = NULL;
929    EAPOLKeyDescriptorRef	descr_p;
930    bool			is_valid;
931    int				key_length;
932    int				key_data_length;
933    int				packet_length;
934    char *			server_key;
935    int				server_key_length = 0;
936    uint8_t *			session_key;
937    int				session_key_length = 0;
938    wirelessKeyType		type;
939
940    descr_p = (EAPOLKeyDescriptorRef)eapol_p->body;
941    session_key = eap_client_session_key(supp, &session_key_length);
942    if (session_key == NULL) {
943	EAPLOG_FL(LOG_NOTICE, "session key is NULL");
944	return;
945    }
946    server_key = eap_client_server_key(supp, &server_key_length);
947    if (server_key == NULL) {
948	EAPLOG_FL(LOG_NOTICE, "server key is NULL");
949	return;
950    }
951    body_length = EAPOLPacketGetLength(eapol_p);
952    packet_length = sizeof(EAPOLPacket) + body_length;
953    if (eapolclient_should_log(kLogFlagPacketDetails)) {
954	debug_str = CFStringCreateMutable(NULL, 0);
955    }
956    is_valid = eapol_key_verify_signature(eapol_p, packet_length,
957					  server_key, server_key_length,
958					  debug_str);
959    if (debug_str != NULL) {
960	EAPLOG(-LOG_NOTICE, "%@", debug_str);
961	CFRelease(debug_str);
962    }
963    if (is_valid == FALSE) {
964	EAPLOG_FL(LOG_NOTICE,
965		  "key signature mismatch, ignoring");
966	return;
967    }
968    if (descr_p->key_index & kEAPOLKeyDescriptorIndexUnicastFlag) {
969	type = kKeyTypeIndexedTx;
970    }
971    else {
972	type = kKeyTypeMulticast;
973    }
974    key_length = EAPOLKeyDescriptorGetLength(descr_p);
975    key_data_length = body_length - sizeof(*descr_p);
976    if (key_data_length > 0) {
977	size_t 			bytes_processed;
978	CCCryptorStatus		c_status;
979	uint8_t *		enc_key;
980	uint8_t *		rc4_key;
981	int			rc4_key_length;
982
983	/* decrypt the key from the packet */
984	rc4_key_length = sizeof(descr_p->key_IV) + session_key_length;
985	rc4_key = malloc(rc4_key_length);
986	bcopy(descr_p->key_IV, rc4_key, sizeof(descr_p->key_IV));
987	bcopy(session_key, rc4_key + sizeof(descr_p->key_IV),
988	      session_key_length);
989	enc_key = malloc(key_data_length);
990	c_status = CCCrypt(kCCDecrypt, kCCAlgorithmRC4, 0,
991			   rc4_key, rc4_key_length,
992			   NULL,
993			   descr_p->key, key_data_length,
994			   enc_key, key_data_length,
995			   &bytes_processed);
996	if (c_status != kCCSuccess) {
997	    eapolclient_log(kLogFlagBasic,
998			    "key process: RC4 decrypt failed %d",
999			    c_status);
1000	}
1001	else {
1002	    eapolclient_log(kLogFlagBasic,
1003			    "set %s key length %d using descriptor",
1004			    (type == kKeyTypeIndexedTx)
1005			    ? "Unicast" : "Broadcast",
1006			    key_length);
1007	    EAPOLSocketSetKey(supp->sock, type,
1008			      (descr_p->key_index
1009			       & kEAPOLKeyDescriptorIndexMask),
1010			      enc_key, key_length);
1011	}
1012	free(enc_key);
1013	free(rc4_key);
1014    }
1015    else {
1016	eapolclient_log(kLogFlagBasic,
1017			"set %s key length %d using session key",
1018			(type == kKeyTypeIndexedTx)
1019			? "Unicast" : "Broadcast",
1020			key_length);
1021	EAPOLSocketSetKey(supp->sock, type,
1022			  descr_p->key_index & kEAPOLKeyDescriptorIndexMask,
1023			  session_key, key_length);
1024    }
1025    return;
1026}
1027
1028static void
1029clear_wpa_key_info(SupplicantRef supp)
1030{
1031    (void)EAPOLSocketSetWPAKey(supp->sock, NULL, 0);
1032    supp->pmk_set = FALSE;
1033    return;
1034}
1035
1036static void
1037set_wpa_key_info(SupplicantRef supp)
1038{
1039    uint8_t		msk[kEAPMasterSessionKeyMinimumSize];
1040    int			msk_length = sizeof(msk);
1041
1042    if (supp->pmk_set) {
1043	/* already set */
1044	return;
1045    }
1046    msk_length = eap_client_master_session_key_copy_bytes(supp,
1047							  msk,
1048							  msk_length);
1049    if (msk_length != 0
1050	&& EAPOLSocketSetWPAKey(supp->sock, msk, msk_length)) {
1051	supp->pmk_set = TRUE;
1052    }
1053    return;
1054}
1055
1056static void
1057Supplicant_authenticated(SupplicantRef supp, SupplicantEvent event,
1058			 void * evdata)
1059{
1060    EAPRequestPacket * 		req_p;
1061    EAPOLSocketReceiveDataRef	rx = evdata;
1062
1063    switch (event) {
1064    case kSupplicantEventStart:
1065	Supplicant_cancel_pending_events(supp);
1066	supp->auth_attempts_count = 0;
1067	supp->state = kSupplicantStateAuthenticated;
1068	free_last_packet(supp);
1069	if (supp->one_time_password == FALSE) {
1070	    CFStringRef	new_password;
1071
1072	    new_password
1073		= CFDictionaryGetValue(supp->config_dict,
1074				       kEAPClientPropNewPassword);
1075	    if (isA_CFString(new_password) != NULL) {
1076		if (supp->password != NULL) {
1077		    free(supp->password);
1078		}
1079		supp->password = my_CFStringToCString(new_password,
1080						      kCFStringEncodingUTF8);
1081		if (supp->password != NULL) {
1082		    supp->password_length = (int)strlen(supp->password);
1083		}
1084	    }
1085	}
1086#if ! TARGET_OS_EMBEDDED
1087	supp->failure_count = 0;
1088	AlertDialogue_free(&supp->alert_prompt);
1089	CredentialsDialogue_free(&supp->cred_prompt);
1090	TrustDialogue_free(&supp->trust_prompt);
1091	if (supp->remember_information && supp->itemID != NULL) {
1092	    CFDataRef	name_data = NULL;
1093	    CFDataRef	password_data = NULL;
1094
1095	    if (supp->username != NULL && supp->username_derived == FALSE) {
1096		name_data
1097		    = CFDataCreate(NULL, (const UInt8 *)supp->username,
1098				   supp->username_length);
1099	    }
1100	    if (supp->eap.last_type == kEAPTypeTLS
1101		&& supp->sec_identity != NULL) {
1102		if (EAPOLClientItemIDSetIdentity(supp->itemID,
1103						 kEAPOLClientDomainUser,
1104						 supp->sec_identity)
1105		    == FALSE) {
1106		    EAPLOG_FL(LOG_NOTICE, "Failed to save identity selection");
1107		}
1108		else {
1109		    OSStatus		status;
1110
1111		    eapolclient_log(kLogFlagBasic,
1112				    "Identity selection saved");
1113		    status = EAPOLClientSetACLForIdentity(supp->sec_identity);
1114		    if (status != noErr) {
1115			EAPLOG_FL(LOG_NOTICE,
1116				  "Failed to set ACL for identity, %d",
1117				  (int)status);
1118		    }
1119		}
1120	    }
1121	    else if (supp->password != NULL) {
1122		password_data
1123		    = CFDataCreate(NULL, (const UInt8 *)supp->password,
1124				   supp->password_length);
1125	    }
1126	    if (name_data != NULL || password_data != NULL) {
1127		if (EAPOLClientItemIDSetPasswordItem(supp->itemID,
1128						     kEAPOLClientDomainUser,
1129						     name_data, password_data)
1130		    == FALSE) {
1131		    EAPLOG_FL(LOG_NOTICE, "Failed to save password");
1132		}
1133		else {
1134		    eapolclient_log(kLogFlagBasic, "Password saved");
1135		}
1136	    }
1137	    my_CFRelease(&name_data);
1138	    my_CFRelease(&password_data);
1139	}
1140	supp->remember_information = FALSE;
1141#endif /* ! TARGET_OS_EMBEDDED */
1142	if (supp->one_time_password) {
1143	    clear_password(supp);
1144	}
1145	Supplicant_report_status(supp);
1146	EAPOLSocketEnableReceive(supp->sock,
1147				 (void *)Supplicant_authenticated,
1148				 (void *)supp,
1149				 (void *)kSupplicantEventData);
1150	break;
1151    case kSupplicantEventData:
1152	Timer_cancel(supp->timer);
1153	switch (rx->eapol_p->packet_type) {
1154	case kEAPOLPacketTypeEAPPacket:
1155	    req_p = (EAPRequestPacket *)rx->eapol_p->body;
1156	    switch (req_p->code) {
1157	    case kEAPCodeRequest:
1158		switch (req_p->type) {
1159		case kEAPTypeIdentity:
1160		    Supplicant_acquired(supp, kSupplicantEventStart, evdata);
1161		    break;
1162		case kEAPTypeNotification:
1163		    /* need to display information to the user XXX */
1164		    log_eap_notification(supp->state, req_p);
1165		    respond_to_notification(supp, req_p->identifier);
1166		    break;
1167		default:
1168		    Supplicant_authenticating(supp,
1169					      kSupplicantEventStart,
1170					      evdata);
1171		    break;
1172		}
1173	    }
1174	    break;
1175	case kEAPOLPacketTypeKey:
1176	    if (EAPOLSocketIsWireless(supp->sock)) {
1177		EAPOLKeyDescriptorRef	descr_p;
1178
1179		descr_p = (EAPOLKeyDescriptorRef)(rx->eapol_p->body);
1180		switch (descr_p->descriptor_type) {
1181		case kEAPOLKeyDescriptorTypeRC4:
1182		    process_key(supp, rx->eapol_p);
1183		    break;
1184		case kEAPOLKeyDescriptorTypeIEEE80211:
1185		    /* wireless family takes care of these */
1186		    break;
1187		default:
1188		    break;
1189		}
1190	    }
1191	    break;
1192	default:
1193	    break;
1194	}
1195	break;
1196    default:
1197	break;
1198    }
1199    return;
1200}
1201
1202static void
1203Supplicant_cleanup(SupplicantRef supp)
1204{
1205    supp->previous_identifier = BAD_IDENTIFIER;
1206    EAPAcceptTypesReset(&supp->eap_accept);
1207    supp->last_status = kEAPClientStatusOK;
1208    supp->last_error = 0;
1209    eap_client_free(supp);
1210    free_last_packet(supp);
1211    return;
1212}
1213
1214static void
1215Supplicant_disconnected(SupplicantRef supp, SupplicantEvent event,
1216			void * evdata)
1217{
1218    switch (event) {
1219    case kSupplicantEventStart:
1220	Supplicant_cancel_pending_events(supp);
1221	supp->state = kSupplicantStateDisconnected;
1222	Supplicant_report_status(supp);
1223	Supplicant_cleanup(supp);
1224	Supplicant_connecting(supp, kSupplicantEventStart, NULL);
1225	break;
1226    default:
1227	break;
1228    }
1229    return;
1230}
1231
1232static void
1233Supplicant_no_authenticator(SupplicantRef supp, SupplicantEvent event,
1234			    void * evdata)
1235{
1236    switch (event) {
1237    case kSupplicantEventStart:
1238	Supplicant_cancel_pending_events(supp);
1239	supp->state = kSupplicantStateNoAuthenticator;
1240	supp->no_authenticator = TRUE;
1241	Supplicant_report_status(supp);
1242	/* let Connecting state handle any packets that may arrive */
1243	EAPOLSocketEnableReceive(supp->sock,
1244				 (void *)Supplicant_connecting,
1245				 (void *)supp,
1246				 (void *)kSupplicantEventStart);
1247	break;
1248    default:
1249	break;
1250    }
1251    return;
1252}
1253
1254static void
1255Supplicant_connecting(SupplicantRef supp, SupplicantEvent event,
1256		      void * evdata)
1257{
1258    EAPOLSocketReceiveDataRef 	rx = evdata;
1259    struct timeval 		t = {S_start_period_secs, 0};
1260
1261    switch (event) {
1262    case kSupplicantEventStart:
1263	Supplicant_cancel_pending_events(supp);
1264	supp->state = kSupplicantStateConnecting;
1265	Supplicant_report_status(supp);
1266	supp->start_count = 0;
1267	EAPOLSocketEnableReceive(supp->sock,
1268				 (void *)Supplicant_connecting,
1269				 (void *)supp,
1270				 (void *)kSupplicantEventData);
1271	/* FALL THROUGH */
1272    case kSupplicantEventData:
1273	if (rx != NULL) {
1274	    EAPOLIEEE80211KeyDescriptorRef	ieee80211_descr_p;
1275	    EAPRequestPacketRef			req_p;
1276
1277	    switch (rx->eapol_p->packet_type) {
1278	    case kEAPOLPacketTypeEAPPacket:
1279		req_p = (void *)rx->eapol_p->body;
1280		if (req_p->code == kEAPCodeRequest) {
1281		    if (req_p->type == kEAPTypeIdentity) {
1282			Supplicant_acquired(supp,
1283					    kSupplicantEventStart,
1284					    evdata);
1285		    }
1286		    else {
1287			Supplicant_authenticating(supp,
1288						  kSupplicantEventStart,
1289						  evdata);
1290		    }
1291		}
1292		return;
1293 	    case kEAPOLPacketTypeKey:
1294		ieee80211_descr_p = (void *)rx->eapol_p->body;
1295		if (ieee80211_descr_p->descriptor_type
1296		    == kEAPOLKeyDescriptorTypeIEEE80211) {
1297		    break;
1298		}
1299		return;
1300	    default:
1301		return;
1302	    }
1303	}
1304	/* FALL THROUGH */
1305    case kSupplicantEventTimeout:
1306	if (rx == NULL) {
1307	    if (supp->start_count == S_start_attempts_max) {
1308		/* no response from Authenticator */
1309		Supplicant_no_authenticator(supp, kSupplicantEventStart, NULL);
1310		break;
1311	    }
1312	    supp->start_count++;
1313	}
1314	EAPOLSocketTransmit(supp->sock,
1315			    kEAPOLPacketTypeStart,
1316			    NULL, 0);
1317	Timer_set_relative(supp->timer, t,
1318			   (void *)Supplicant_connecting,
1319			   (void *)supp,
1320			   (void *)kSupplicantEventTimeout,
1321			   NULL);
1322	break;
1323
1324    default:
1325	break;
1326    }
1327}
1328
1329static bool
1330S_retrieve_identity(SupplicantRef supp)
1331{
1332    CFStringRef			identity_cf;
1333    char *			identity;
1334
1335    /* no module is active yet, use what we have */
1336    if (supp->eap.module == NULL) {
1337	goto use_default;
1338    }
1339    /* if we have an active module, ask it for the identity to use */
1340    identity_cf	= EAPClientModulePluginCopyIdentity(supp->eap.module,
1341						    &supp->eap.plugin_data);
1342    if (identity_cf == NULL) {
1343	goto use_default;
1344    }
1345    identity = my_CFStringToCString(identity_cf, kCFStringEncodingUTF8);
1346    my_CFRelease(&identity_cf);
1347    if (identity == NULL) {
1348	goto use_default;
1349    }
1350    if (supp->username != NULL) {
1351	free(supp->username);
1352    }
1353    supp->username = identity;
1354    supp->username_length = (int)strlen(identity);
1355    supp->username_derived = TRUE;
1356
1357 use_default:
1358    return (supp->username != NULL);
1359}
1360
1361static bool
1362respond_with_identity(SupplicantRef supp, int identifier)
1363{
1364    char			buf[256];
1365    char *			identity;
1366    int				length;
1367    EAPPacketRef		pkt_p;
1368    int				size;
1369
1370    if (S_retrieve_identity(supp) == FALSE) {
1371	return (FALSE);
1372    }
1373    if (supp->outer_identity != NULL) {
1374	identity = supp->outer_identity;
1375	length = supp->outer_identity_length;
1376    }
1377    else {
1378	identity = supp->username;
1379	length = supp->username_length;
1380    }
1381
1382    eapolclient_log(kLogFlagBasic,
1383		    "EAP Response Identity %.*s",
1384		    length, identity);
1385
1386    /* transmit a response/Identity */
1387    pkt_p = EAPPacketCreate(buf, sizeof(buf),
1388			    kEAPCodeResponse, identifier,
1389			    kEAPTypeIdentity,
1390			    identity, length,
1391			    &size);
1392    if (EAPOLSocketTransmit(supp->sock,
1393			    kEAPOLPacketTypeEAPPacket,
1394			    pkt_p, size) < 0) {
1395	EAPLOG_FL(LOG_NOTICE, "EAPOLSocketTransmit failed");
1396    }
1397    if ((char *)pkt_p != buf) {
1398	free(pkt_p);
1399    }
1400    return (TRUE);
1401}
1402
1403static void
1404Supplicant_acquired(SupplicantRef supp, SupplicantEvent event,
1405		    void * evdata)
1406{
1407    SupplicantState		prev_state = supp->state;
1408    EAPOLSocketReceiveDataRef 	rx;
1409    EAPRequestPacket *		req_p;
1410    struct timeval 		t = {S_auth_period_secs, 0};
1411
1412    switch (event) {
1413    case kSupplicantEventStart:
1414	supp->auth_attempts_count++;
1415	EAPAcceptTypesReset(&supp->eap_accept);
1416	Supplicant_cancel_pending_events(supp);
1417	supp->state = kSupplicantStateAcquired;
1418	EAPOLSocketEnableReceive(supp->sock,
1419				 (void *)Supplicant_acquired,
1420				 (void *)supp,
1421				 (void *)kSupplicantEventData);
1422	/* FALL THROUGH */
1423    case kSupplicantEventData:
1424	supp->no_authenticator = FALSE;
1425	rx = evdata;
1426	if (rx->eapol_p->packet_type != kEAPOLPacketTypeEAPPacket) {
1427	    break;
1428	}
1429	req_p = (EAPRequestPacket *)rx->eapol_p->body;
1430	if (req_p->code == kEAPCodeRequest
1431	    && req_p->type == kEAPTypeIdentity) {
1432	    int				len;
1433
1434	    len = EAPPacketGetLength((EAPPacketRef)req_p) - sizeof(*req_p);
1435	    S_update_identity_attributes(supp, req_p->type_data, len);
1436	    eapolclient_log(kLogFlagBasic,
1437			    "EAP Request Identity");
1438	    supp->previous_identifier = req_p->identifier;
1439#if ! TARGET_OS_EMBEDDED
1440	    if (EAPOLSocketGetMode(supp->sock) == kEAPOLControlModeSystem) {
1441#define MAX_AUTH_FAILURES	3
1442		if (S_check_for_updated_credentials(supp) == FALSE
1443		    && supp->failure_count >= MAX_AUTH_FAILURES) {
1444		    EAPLOG(LOG_NOTICE,
1445			   "maximum (%d) authentication failures reached",
1446			   MAX_AUTH_FAILURES);
1447		    supp->last_status = kEAPClientStatusAuthenticationStalled;
1448		    Supplicant_held(supp, kSupplicantEventStart, NULL);
1449		    break;
1450		}
1451	    }
1452#endif /* ! TARGET_OS_EMBEDDED */
1453
1454	    if (respond_with_identity(supp, req_p->identifier)) {
1455		supp->last_status = kEAPClientStatusOK;
1456		Supplicant_report_status(supp);
1457
1458		/* set a timeout */
1459		Timer_set_relative(supp->timer, t,
1460				   (void *)Supplicant_acquired,
1461				   (void *)supp,
1462				   (void *)kSupplicantEventTimeout,
1463				   NULL);
1464	    }
1465	    else if (supp->no_ui) {
1466		EAPLOG(LOG_NOTICE,
1467		       "Acquired: cannot prompt for missing user name");
1468		supp->last_status = kEAPClientStatusUserInputNotPossible;
1469		Supplicant_held(supp, kSupplicantEventStart, NULL);
1470	    }
1471	    else {
1472		supp->last_status = kEAPClientStatusUserInputRequired;
1473		Supplicant_report_status(supp);
1474	    }
1475	    break;
1476	}
1477	else {
1478	    if (event == kSupplicantEventStart) {
1479		/* this will not happen if we're bug free */
1480		EAPLOG_FL(LOG_NOTICE,
1481			  "internal error: recursion avoided from state %s",
1482			  SupplicantStateString(prev_state));
1483		break;
1484	    }
1485	    Supplicant_authenticating(supp,
1486				      kSupplicantEventStart,
1487				      evdata);
1488	}
1489	break;
1490    case kSupplicantEventMoreDataAvailable:
1491	if (respond_with_identity(supp, supp->previous_identifier)) {
1492	    supp->last_status = kEAPClientStatusOK;
1493	    Supplicant_report_status(supp);
1494	    /* set a timeout */
1495	    Timer_set_relative(supp->timer, t,
1496			       (void *)Supplicant_acquired,
1497			       (void *)supp,
1498			       (void *)kSupplicantEventTimeout,
1499			       NULL);
1500
1501	}
1502	else {
1503	    supp->last_status = kEAPClientStatusUserInputRequired;
1504	    Supplicant_report_status(supp);
1505	}
1506	break;
1507
1508    case kSupplicantEventTimeout:
1509	if (supp->auth_attempts_count >= S_auth_attempts_max) {
1510	    supp->auth_attempts_count = 0;
1511	    supp->last_status = kEAPClientStatusAuthenticationStalled;
1512	    Supplicant_held(supp, kSupplicantEventStart, NULL);
1513	}
1514	else {
1515	    Supplicant_connecting(supp, kSupplicantEventStart, NULL);
1516	}
1517	break;
1518    default:
1519	break;
1520    }
1521    return;
1522}
1523
1524static void
1525respond_to_notification(SupplicantRef supp, int identifier)
1526{
1527    EAPNotificationPacket	notif;
1528    int				size;
1529
1530    eapolclient_log(kLogFlagBasic,
1531		    "EAP Response Notification");
1532
1533    /* transmit a response/Notification */
1534    (void)EAPPacketCreate(&notif, sizeof(notif),
1535			  kEAPCodeResponse, identifier,
1536			  kEAPTypeNotification, NULL, 0, &size);
1537    if (EAPOLSocketTransmit(supp->sock,
1538			    kEAPOLPacketTypeEAPPacket,
1539			    &notif, sizeof(notif)) < 0) {
1540	EAPLOG_FL(LOG_NOTICE, "EAPOLSocketTransmit failed");
1541    }
1542    return;
1543}
1544
1545static void
1546respond_with_nak(SupplicantRef supp, int identifier, uint8_t desired_type)
1547{
1548    EAPNakPacket		nak;
1549    int				size;
1550
1551    /* transmit a response/Nak */
1552    (void)EAPPacketCreate(&nak, sizeof(nak),
1553			  kEAPCodeResponse, identifier,
1554			  kEAPTypeNak, NULL,
1555			  sizeof(nak) - sizeof(EAPRequestPacket), &size);
1556    nak.desired_type = desired_type;
1557    if (EAPOLSocketTransmit(supp->sock,
1558			    kEAPOLPacketTypeEAPPacket,
1559			    &nak, sizeof(nak)) < 0) {
1560	EAPLOG_FL(LOG_NOTICE, "EAPOLSocketTransmit failed");
1561    }
1562    return;
1563}
1564
1565static void
1566process_packet(SupplicantRef supp, EAPOLSocketReceiveDataRef rx)
1567{
1568    EAPPacketRef	in_pkt_p = (EAPPacketRef)(rx->eapol_p->body);
1569    EAPPacketRef	out_pkt_p = NULL;
1570    EAPRequestPacket *	req_p = (EAPRequestPacket *)in_pkt_p;
1571    EAPClientState	state;
1572    struct timeval 	t = {S_auth_period_secs, 0};
1573
1574    if (supp->username == NULL) {
1575	return;
1576    }
1577
1578    switch (in_pkt_p->code) {
1579    case kEAPCodeRequest:
1580	if (req_p->type == kEAPTypeInvalid) {
1581	    return;
1582	}
1583	if (req_p->type != eap_client_type(supp)) {
1584	    if (EAPAcceptTypesIsSupportedType(&supp->eap_accept,
1585					      req_p->type) == FALSE) {
1586		EAPType eap_type = EAPAcceptTypesNextType(&supp->eap_accept);
1587		if (eap_type == kEAPTypeInvalid) {
1588		    eapolclient_log(kLogFlagBasic,
1589				    "EAP Request: EAP type %d not enabled",
1590				    req_p->type);
1591		    supp->last_status = kEAPClientStatusProtocolNotSupported;
1592		    Supplicant_held(supp, kSupplicantEventStart, NULL);
1593		    return;
1594		}
1595		eapolclient_log(kLogFlagBasic,
1596				"EAP Request: NAK'ing EAP type %d with %d",
1597				req_p->type, eap_type);
1598		respond_with_nak(supp, in_pkt_p->identifier,
1599				 eap_type);
1600		/* set a timeout */
1601		Timer_set_relative(supp->timer, t,
1602				   (void *)Supplicant_authenticating,
1603				   (void *)supp,
1604				   (void *)kSupplicantEventTimeout,
1605				   NULL);
1606		return;
1607	    }
1608	    Timer_cancel(supp->timer);
1609	    eap_client_free(supp);
1610	    if (eap_client_init(supp, req_p->type) == FALSE) {
1611		if (supp->last_status
1612		    != kEAPClientStatusUserInputRequired) {
1613		    EAPLOG(LOG_NOTICE,
1614			   "EAP Request: EAP type %d"
1615			   " init failed, %d",
1616			   req_p->type, supp->last_status);
1617		    Supplicant_held(supp, kSupplicantEventStart, NULL);
1618		    return;
1619		}
1620		save_last_packet(supp, rx);
1621		Supplicant_report_status(supp);
1622		return;
1623	    }
1624	    eapolclient_log(kLogFlagBasic,
1625			    "EAP Request: EAP type %d accepted",
1626			    req_p->type);
1627	    Supplicant_report_status(supp);
1628	}
1629	else {
1630	    eapolclient_log(kLogFlagBasic,
1631			    "EAP Request: EAP type %d",
1632			    req_p->type);
1633	}
1634	break;
1635    case kEAPCodeResponse:
1636	if (req_p->type != eap_client_type(supp)) {
1637	    /* this should not happen, but if it does, ignore the packet */
1638	    return;
1639	}
1640	eapolclient_log(kLogFlagBasic,
1641			"EAP Response: EAP type %d", req_p->type);
1642	break;
1643    case kEAPCodeFailure:
1644	eapolclient_log(kLogFlagBasic, "EAP Failure");
1645	if (supp->eap.module == NULL) {
1646	    supp->last_status = kEAPClientStatusFailed;
1647	    Supplicant_held(supp, kSupplicantEventStart, NULL);
1648	    return;
1649	}
1650	break;
1651    case kEAPCodeSuccess:
1652	eapolclient_log(kLogFlagBasic, "EAP Success");
1653	if (supp->eap.module == NULL) {
1654	    Supplicant_authenticated(supp, kSupplicantEventStart, NULL);
1655	    return;
1656	}
1657	break;
1658    default:
1659	break;
1660    }
1661    if (supp->eap.module == NULL) {
1662	return;
1663    }
1664    /* invoke the authentication method "process" function */
1665    my_CFRelease(&supp->eap.required_props);
1666    my_CFRelease(&supp->eap.published_props);
1667    state = eap_client_process(supp, in_pkt_p, &out_pkt_p,
1668			       &supp->last_status, &supp->last_error);
1669    if (out_pkt_p != NULL) {
1670	/* send the output packet */
1671	if (EAPOLSocketTransmit(supp->sock,
1672				kEAPOLPacketTypeEAPPacket,
1673				out_pkt_p,
1674				EAPPacketGetLength(out_pkt_p)) < 0) {
1675	    EAPLOG_FL(LOG_NOTICE, "EAPOLSocketTransmit %d failed",
1676		      out_pkt_p->code);
1677	}
1678	/* and free the packet */
1679	eap_client_free_packet(supp, out_pkt_p);
1680    }
1681
1682    supp->eap.published_props = eap_client_publish_properties(supp);
1683
1684    switch (state) {
1685    case kEAPClientStateAuthenticating:
1686	if (supp->last_status == kEAPClientStatusUserInputRequired) {
1687	    save_last_packet(supp, rx);
1688	    supp->eap.required_props = eap_client_require_properties(supp);
1689	    if (supp->no_ui) {
1690		EAPLOG(LOG_NOTICE,
1691		       "Authenticating: can't prompt for missing properties %@",
1692		       supp->eap.required_props);
1693		supp->last_status = kEAPClientStatusUserInputNotPossible;
1694		Supplicant_held(supp, kSupplicantEventStart, NULL);
1695		return;
1696	    }
1697	    EAPLOG(LOG_DEBUG,
1698		   "Authenticating: user input required for properties %@",
1699		   supp->eap.required_props);
1700	}
1701	Supplicant_report_status(supp);
1702
1703	/* try to set the session key, if it is available */
1704	if (EAPOLSocketIsWireless(supp->sock)) {
1705	    set_wpa_key_info(supp);
1706	}
1707	break;
1708    case kEAPClientStateSuccess:
1709	/* authentication method succeeded */
1710	EAPLOG(LOG_NOTICE,
1711	       "%s %s: successfully authenticated",
1712	       EAPOLSocketIfName(supp->sock, NULL),
1713	       supp->eap.last_type_name);
1714	/* try to set the session key, if it is available */
1715	if (EAPOLSocketIsWireless(supp->sock)) {
1716	    set_wpa_key_info(supp);
1717	}
1718	Supplicant_authenticated(supp, kSupplicantEventStart, NULL);
1719	break;
1720    case kEAPClientStateFailure:
1721	/* authentication method failed */
1722	eap_client_log_failure(supp);
1723	EAPLOG(LOG_NOTICE,
1724	       "%s %s: authentication failed with status %d",
1725	       EAPOLSocketIfName(supp->sock, NULL),
1726	       supp->eap.last_type_name, supp->last_status);
1727	Supplicant_held(supp, kSupplicantEventStart, NULL);
1728	break;
1729    }
1730    return;
1731}
1732
1733static void
1734Supplicant_authenticating(SupplicantRef supp, SupplicantEvent event,
1735			  void * evdata)
1736{
1737    EAPRequestPacket *		req_p;
1738    SupplicantState		prev_state = supp->state;
1739    EAPOLSocketReceiveDataRef 	rx = evdata;
1740
1741    switch (event) {
1742    case kSupplicantEventStart:
1743	if (EAPOLSocketIsWireless(supp->sock)) {
1744	    clear_wpa_key_info(supp);
1745	}
1746	supp->state = kSupplicantStateAuthenticating;
1747	Supplicant_report_status(supp);
1748	EAPOLSocketEnableReceive(supp->sock,
1749				 (void *)Supplicant_authenticating,
1750				 (void *)supp,
1751				 (void *)kSupplicantEventData);
1752	/* FALL THROUGH */
1753    case kSupplicantEventData:
1754	Timer_cancel(supp->timer);
1755	supp->no_authenticator = FALSE;
1756	if (rx->eapol_p->packet_type != kEAPOLPacketTypeEAPPacket) {
1757	    break;
1758	}
1759	req_p = (EAPRequestPacket *)rx->eapol_p->body;
1760	switch (req_p->code) {
1761	case kEAPCodeSuccess:
1762	    process_packet(supp, rx);
1763	    break;
1764
1765	case kEAPCodeFailure:
1766	    process_packet(supp, rx);
1767	    break;
1768
1769	case kEAPCodeRequest:
1770	case kEAPCodeResponse:
1771	    switch (req_p->type) {
1772	    case kEAPTypeIdentity:
1773		if (req_p->code == kEAPCodeResponse) {
1774		    /* don't care about responses */
1775		    break;
1776		}
1777		if (event == kSupplicantEventStart) {
1778		    /* this will not happen if we're bug free */
1779		    EAPLOG_FL(LOG_NOTICE,
1780			      "internal error: recursion avoided from state %s",
1781			      SupplicantStateString(prev_state));
1782		    break;
1783		}
1784		Supplicant_acquired(supp, kSupplicantEventStart, evdata);
1785		break;
1786
1787	    case kEAPTypeNotification:
1788		if (req_p->code == kEAPCodeResponse) {
1789		    /* don't care about responses */
1790		    break;
1791		}
1792		/* need to display information to the user XXX */
1793		log_eap_notification(supp->state, req_p);
1794		respond_to_notification(supp, req_p->identifier);
1795		break;
1796	    default:
1797		process_packet(supp, rx);
1798		break;
1799	    } /* switch (req_p->type) */
1800	    break;
1801	default:
1802	    break;
1803	} /* switch (req_p->code) */
1804	break;
1805    case kSupplicantEventTimeout:
1806	Supplicant_connecting(supp, kSupplicantEventStart, NULL);
1807	break;
1808    default:
1809	break;
1810    }
1811    return;
1812}
1813
1814static void
1815dictInsertNumber(CFMutableDictionaryRef dict, CFStringRef prop, uint32_t num)
1816{
1817    CFNumberRef			num_cf;
1818
1819    num_cf = CFNumberCreate(NULL, kCFNumberSInt32Type, &num);
1820    CFDictionarySetValue(dict, prop, num_cf);
1821    my_CFRelease(&num_cf);
1822    return;
1823}
1824
1825static void
1826dictInsertEAPTypeInfo(CFMutableDictionaryRef dict, EAPType type,
1827		      const char * type_name)
1828{
1829    if (type == kEAPTypeInvalid) {
1830	return;
1831    }
1832
1833    /* EAPTypeName */
1834    if (type_name != NULL) {
1835	CFStringRef		eap_type_name_cf;
1836	eap_type_name_cf
1837	    = CFStringCreateWithCString(NULL, type_name,
1838					kCFStringEncodingASCII);
1839	CFDictionarySetValue(dict, kEAPOLControlEAPTypeName,
1840			     eap_type_name_cf);
1841	my_CFRelease(&eap_type_name_cf);
1842    }
1843    /* EAPType */
1844    dictInsertNumber(dict, kEAPOLControlEAPType, type);
1845    return;
1846}
1847
1848static void
1849dictInsertSupplicantState(CFMutableDictionaryRef dict, SupplicantState state)
1850{
1851    dictInsertNumber(dict, kEAPOLControlSupplicantState, state);
1852    return;
1853}
1854
1855static void
1856dictInsertClientStatus(CFMutableDictionaryRef dict,
1857		       EAPClientStatus status, int error)
1858{
1859    dictInsertNumber(dict, kEAPOLControlClientStatus, status);
1860    dictInsertNumber(dict, kEAPOLControlDomainSpecificError, error);
1861    return;
1862}
1863
1864static void
1865dictInsertGeneration(CFMutableDictionaryRef dict, uint32_t generation)
1866{
1867    dictInsertNumber(dict, kEAPOLControlConfigurationGeneration, generation);
1868}
1869
1870static void
1871dictInsertRequiredProperties(CFMutableDictionaryRef dict,
1872			     CFArrayRef required_props)
1873{
1874    if (required_props == NULL) {
1875	CFMutableArrayRef array;
1876
1877	/* to start, we at least need the user name */
1878	array = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
1879	CFArrayAppendValue(array, kEAPClientPropUserName);
1880	CFDictionarySetValue(dict, kEAPOLControlRequiredProperties,
1881			     array);
1882	my_CFRelease(&array);
1883    }
1884    else {
1885	CFDictionarySetValue(dict, kEAPOLControlRequiredProperties,
1886			     required_props);
1887    }
1888    return;
1889}
1890
1891static void
1892dictInsertPublishedProperties(CFMutableDictionaryRef dict,
1893			      CFDictionaryRef published_props)
1894{
1895    if (published_props == NULL) {
1896	return;
1897    }
1898    CFDictionarySetValue(dict, kEAPOLControlAdditionalProperties,
1899			 published_props);
1900}
1901
1902static void
1903dictInsertIdentityAttributes(CFMutableDictionaryRef dict,
1904			     CFArrayRef identity_attributes)
1905{
1906    if (identity_attributes == NULL) {
1907	return;
1908    }
1909    CFDictionarySetValue(dict, kEAPOLControlIdentityAttributes,
1910			 identity_attributes);
1911}
1912
1913static void
1914dictInsertMode(CFMutableDictionaryRef dict, EAPOLControlMode mode)
1915{
1916    if (mode == kEAPOLControlModeNone) {
1917	return;
1918    }
1919    dictInsertNumber(dict, kEAPOLControlMode, mode);
1920    if (mode == kEAPOLControlModeSystem) {
1921	CFDictionarySetValue(dict, kEAPOLControlSystemMode, kCFBooleanTrue);
1922    }
1923    return;
1924}
1925
1926PRIVATE_EXTERN void
1927Supplicant_stop(SupplicantRef supp)
1928{
1929    eap_client_free(supp);
1930    Supplicant_logoff(supp, kSupplicantEventStart, NULL);
1931    Supplicant_free(&supp);
1932    return;
1933}
1934
1935static void
1936user_supplied_data(SupplicantRef supp)
1937{
1938    eapolclient_log(kLogFlagBasic, "user_supplied_data");
1939    switch (supp->state) {
1940    case kSupplicantStateAcquired:
1941	Supplicant_acquired(supp,
1942			    kSupplicantEventMoreDataAvailable,
1943			    NULL);
1944	break;
1945    case kSupplicantStateAuthenticating:
1946	if (supp->last_rx_packet.eapol_p != NULL) {
1947	    process_packet(supp, &supp->last_rx_packet);
1948	}
1949	break;
1950    default:
1951	break;
1952    }
1953}
1954
1955static void
1956create_ui_config_dict(SupplicantRef supp)
1957{
1958    if (supp->ui_config_dict != NULL) {
1959	return;
1960    }
1961    supp->ui_config_dict
1962	= CFDictionaryCreateMutable(NULL, 0,
1963				    &kCFTypeDictionaryKeyCallBacks,
1964				    &kCFTypeDictionaryValueCallBacks);
1965
1966    return;
1967}
1968
1969#if ! TARGET_OS_EMBEDDED
1970static Boolean
1971dicts_compare_arrays(CFDictionaryRef dict1, CFDictionaryRef dict2,
1972		     CFStringRef propname)
1973{
1974    CFArrayRef	array1 = NULL;
1975    CFArrayRef	array2 = NULL;
1976
1977    if (dict1 != NULL && dict2 != NULL) {
1978	array1 = CFDictionaryGetValue(dict1, propname);
1979	array2 = CFDictionaryGetValue(dict2, propname);
1980    }
1981    if (array1 == NULL || array2 == NULL) {
1982	return (FALSE);
1983    }
1984    return (CFEqual(array1, array2));
1985}
1986
1987static void
1988trust_callback(const void * arg1, const void * arg2,
1989	       TrustDialogueResponseRef response)
1990{
1991    CFDictionaryRef		config_dict = NULL;
1992    SupplicantRef		supp = (SupplicantRef)arg1;
1993    CFDictionaryRef		trust_info;
1994    CFArrayRef			trust_proceed;
1995
1996    if (supp->trust_prompt == NULL) {
1997	return;
1998    }
1999    trust_info = TrustDialogue_trust_info(supp->trust_prompt);
2000    if (trust_info != NULL) {
2001	CFRetain(trust_info);
2002    }
2003    TrustDialogue_free(&supp->trust_prompt);
2004    if (trust_info == NULL) {
2005	return;
2006    }
2007    if (response->proceed == FALSE) {
2008	EAPLOG(LOG_NOTICE, "%s: user cancelled",
2009	       EAPOLSocketIfName(supp->sock, NULL));
2010	EAPOLSocketStopClient(supp->sock);
2011	goto done;
2012    }
2013    if (supp->last_status != kEAPClientStatusUserInputRequired
2014	|| supp->eap.published_props == NULL) {
2015	goto done;
2016    }
2017    create_ui_config_dict(supp);
2018    if (dicts_compare_arrays(trust_info, supp->eap.published_props,
2019			     kEAPClientPropTLSServerCertificateChain)
2020	== FALSE) {
2021	CFDictionaryRemoveValue(supp->ui_config_dict,
2022				kEAPClientPropTLSUserTrustProceedCertificateChain);
2023    }
2024    else {
2025	trust_proceed
2026	    = CFDictionaryGetValue(supp->eap.published_props,
2027				   kEAPClientPropTLSServerCertificateChain);
2028	if (trust_proceed != NULL) {
2029	    CFDictionarySetValue(supp->ui_config_dict,
2030				 kEAPClientPropTLSUserTrustProceedCertificateChain,
2031				 trust_proceed);
2032	}
2033    }
2034    config_dict = CFDictionaryCreateCopy(NULL, supp->orig_config_dict);
2035    Supplicant_update_configuration(supp, config_dict, NULL);
2036    my_CFRelease(&config_dict);
2037    user_supplied_data(supp);
2038
2039 done:
2040    my_CFRelease(&trust_info);
2041    return;
2042}
2043
2044static void
2045credentials_callback(const void * arg1, const void * arg2,
2046		     CredentialsDialogueResponseRef response)
2047{
2048    CFDictionaryRef		config_dict = NULL;
2049    SupplicantRef		supp = (SupplicantRef)arg1;
2050
2051    CredentialsDialogue_free(&supp->cred_prompt);
2052    if (response->user_cancelled) {
2053	EAPLOG(LOG_NOTICE, "%s: user cancelled",
2054	       EAPOLSocketIfName(supp->sock, NULL));
2055	EAPOLSocketStopClient(supp->sock);
2056	return;
2057    }
2058    if (supp->last_status != kEAPClientStatusUserInputRequired) {
2059	return;
2060    }
2061    create_ui_config_dict(supp);
2062    if (response->username != NULL) {
2063	CFDictionarySetValue(supp->ui_config_dict, kEAPClientPropUserName,
2064			     response->username);
2065	supp->ignore_username = FALSE;
2066    }
2067    if (response->password != NULL) {
2068	CFDictionarySetValue(supp->ui_config_dict,
2069			     kEAPClientPropUserPassword,
2070			     response->password);
2071	supp->ignore_password = FALSE;
2072    }
2073    if (response->new_password != NULL) {
2074	CFDictionarySetValue(supp->ui_config_dict,
2075			     kEAPClientPropNewPassword,
2076			     response->new_password);
2077	supp->ignore_password = FALSE;
2078    }
2079    if (response->chosen_identity != NULL) {
2080	EAPSecIdentityHandleRef	id_handle;
2081
2082	id_handle = EAPSecIdentityHandleCreate(response->chosen_identity);
2083	CFDictionarySetValue(supp->ui_config_dict,
2084			     kEAPClientPropTLSIdentityHandle,
2085			     id_handle);
2086	CFRelease(id_handle);
2087	supp->ignore_sec_identity = FALSE;
2088    }
2089    supp->remember_information = response->remember_information;
2090
2091    config_dict = CFDictionaryCreateCopy(NULL, supp->orig_config_dict);
2092    Supplicant_update_configuration(supp, config_dict, NULL);
2093    my_CFRelease(&config_dict);
2094    user_supplied_data(supp);
2095    return;
2096}
2097
2098static void
2099alert_callback(const void * arg1, const void * arg2)
2100{
2101    SupplicantRef 	supp = (SupplicantRef)arg1;
2102
2103    AlertDialogue_free(&supp->alert_prompt);
2104    EAPOLSocketStopClient(supp->sock);
2105    return;
2106}
2107
2108
2109static void
2110present_alert_dialogue(SupplicantRef supp)
2111{
2112    CFStringRef		message = NULL;
2113
2114    if (supp->no_ui) {
2115	return;
2116    }
2117    if (supp->alert_prompt != NULL) {
2118	AlertDialogue_free(&supp->alert_prompt);
2119    }
2120    switch (supp->last_status) {
2121    case kEAPClientStatusOK:
2122	break;
2123    case kEAPClientStatusFailed:
2124	/* assume it's a password error */
2125	break;
2126    case kEAPClientStatusSecurityError:
2127	switch (supp->last_error) {
2128	case errSSLCrypto:
2129	    break;
2130	default:
2131	    message = CFSTR("EAPOLCLIENT_FAILURE_MESSAGE_DEFAULT");
2132	    break;
2133	}
2134	break;
2135    case kEAPClientStatusProtocolNotSupported:
2136    case kEAPClientStatusInnerProtocolNotSupported:
2137	message = CFSTR("EAPOLCLIENT_FAILURE_MESSAGE_DEFAULT");
2138	break;
2139    case kEAPClientStatusServerCertificateNotTrusted:
2140	/* trust settings not correct */
2141	message = CFSTR("EAPOLCLIENT_FAILURE_MESSAGE_SERVER_NOT_TRUSTED");
2142	break;
2143    case kEAPClientStatusAuthenticationStalled:
2144	message = CFSTR("EAPOLCLIENT_FAILURE_MESSAGE_AUTHENTICATION_STALLED");
2145	break;
2146
2147    default:
2148	message = CFSTR("EAPOLCLIENT_FAILURE_MESSAGE_DEFAULT");
2149	break;
2150    }
2151    if (message != NULL) {
2152	supp->alert_prompt
2153	    = AlertDialogue_create(alert_callback, supp, NULL,
2154				   message, EAPOLSocketGetSSID(supp->sock));
2155
2156    }
2157    return;
2158}
2159
2160static Boolean
2161my_CFArrayContainsValue(CFArrayRef list, CFStringRef value)
2162{
2163    if (list == NULL) {
2164	return (FALSE);
2165    }
2166    return (CFArrayContainsValue(list, CFRangeMake(0, CFArrayGetCount(list)),
2167				 value));
2168}
2169
2170static void
2171dictInsertAuthenticatorMACAddress(CFMutableDictionaryRef dict,
2172				  EAPOLSocketRef sock)
2173{
2174    const struct ether_addr *	authenticator_mac;
2175    CFDataRef			data;
2176
2177    authenticator_mac = EAPOLSocketGetAuthenticatorMACAddress(sock);
2178    if (authenticator_mac == NULL) {
2179	return;
2180    }
2181    data = CFDataCreate(NULL, (const UInt8 *)authenticator_mac,
2182			sizeof(*authenticator_mac));
2183    CFDictionarySetValue(dict, kEAPOLControlAuthenticatorMACAddress, data);
2184    CFRelease(data);
2185    return;
2186}
2187
2188static void
2189S_config_changed(CFMachPortRef port, void * msg, CFIndex size, void * info)
2190{
2191    EAPOLClientConfigurationRef	cfg;
2192    CFStringRef			profileID;
2193    SupplicantRef		supp = (SupplicantRef)info;
2194
2195    if (supp->itemID == NULL) {
2196	return;
2197    }
2198    profileID = EAPOLClientItemIDGetProfileID(supp->itemID);
2199    if (profileID == NULL) {
2200	return;
2201    }
2202    cfg = EAPOLClientConfigurationCreate(NULL);
2203    if (cfg == NULL) {
2204	EAPLOG_FL(LOG_ERR, "EAPOLClientConfiguration() failed");
2205	return;
2206    }
2207    if (EAPOLClientConfigurationGetProfileWithID(cfg, profileID) == NULL) {
2208	EAPLOG(LOG_NOTICE, "%s: profile no longer exists, stopping",
2209	       EAPOLSocketIfName(supp->sock, NULL));
2210	EAPOLControlStop(EAPOLSocketIfName(supp->sock, NULL));
2211    }
2212    CFRelease(cfg);
2213    return;
2214}
2215
2216static void
2217S_add_config_notification(SupplicantRef supp)
2218{
2219    CFMachPortContext		context = {0, NULL, NULL, NULL, NULL};
2220    CFMachPortRef		notify_port_cf;
2221    mach_port_t			notify_port;
2222    int				notify_token;
2223    CFRunLoopSourceRef		rls;
2224    uint32_t			status;
2225
2226    if (supp->config_change.mp != NULL) {
2227	/* already registered, nothing to do */
2228	return;
2229    }
2230    notify_port = MACH_PORT_NULL;
2231    status
2232	= notify_register_mach_port(kEAPOLClientConfigurationChangedNotifyKey,
2233				    &notify_port, 0, &notify_token);
2234    if (status != NOTIFY_STATUS_OK) {
2235	EAPLOG_FL(LOG_ERR, "notify_register_mach_port() failed");
2236	return;
2237    }
2238    context.info = supp;
2239    notify_port_cf = CFMachPortCreateWithPort(NULL, notify_port,
2240					      S_config_changed,
2241					      &context,
2242					      NULL);
2243    if (notify_port_cf == NULL) {
2244	EAPLOG_FL(LOG_ERR, "CFMachPortCreateWithPort() failed");
2245	(void)notify_cancel(notify_token);
2246	return;
2247    }
2248    rls = CFMachPortCreateRunLoopSource(NULL, notify_port_cf, 0);
2249    if (rls == NULL) {
2250	EAPLOG_FL(LOG_ERR, "CFMachPortCreateRunLoopSource() failed");
2251	CFRelease(notify_port_cf);
2252	(void)notify_cancel(notify_token);
2253	return;
2254    }
2255    CFRunLoopAddSource(CFRunLoopGetCurrent(), rls, kCFRunLoopDefaultMode);
2256    CFRelease(rls);
2257    supp->config_change.mp = notify_port_cf;
2258    supp->config_change.token = notify_token;
2259    return;
2260}
2261
2262/*
2263 * Function: S_system_mode_use_od
2264 * Purpose:
2265 *   Check whether System Mode should use Open Directory machine
2266 *   credentials.
2267 */
2268static bool
2269S_system_mode_use_od(CFDictionaryRef dict, CFStringRef * ret_nodename)
2270{
2271    CFBooleanRef	use_od_cf;
2272    bool		use_od = FALSE;
2273
2274    *ret_nodename = NULL;
2275    if (dict == NULL) {
2276	return (FALSE);
2277    }
2278    use_od_cf
2279	= CFDictionaryGetValue(dict,
2280			       kEAPClientPropSystemModeUseOpenDirectoryCredentials);
2281    if (isA_CFBoolean(use_od_cf) != NULL) {
2282	if (CFBooleanGetValue(use_od_cf)) {
2283	    CFStringRef	nodename;
2284
2285	    use_od = TRUE;
2286	    nodename
2287		= CFDictionaryGetValue(dict,
2288				       kEAPClientPropSystemModeOpenDirectoryNodeName);
2289	    if (isA_CFString(nodename) != NULL) {
2290		*ret_nodename = nodename;
2291	    }
2292	}
2293    }
2294    else {
2295	CFStringRef	cred_source;
2296
2297	cred_source
2298	    = CFDictionaryGetValue(dict,
2299				   kEAPClientPropSystemModeCredentialsSource);
2300	if (isA_CFString(cred_source) != NULL) {
2301	    use_od = CFEqual(cred_source,
2302			     kEAPClientCredentialsSourceActiveDirectory);
2303	}
2304    }
2305    return (use_od);
2306}
2307
2308#endif /* ! TARGET_OS_EMBEDDED */
2309
2310static void
2311Supplicant_report_status(SupplicantRef supp)
2312{
2313    CFMutableDictionaryRef	dict;
2314    EAPOLControlMode		mode;
2315#if ! TARGET_OS_EMBEDDED
2316    Boolean			need_username = FALSE;
2317    Boolean			need_password = FALSE;
2318    Boolean			need_new_password = FALSE;
2319    Boolean			need_trust = FALSE;
2320#endif /* ! TARGET_OS_EMBEDDED */
2321    CFDateRef			timestamp = NULL;
2322
2323    dict = CFDictionaryCreateMutable(NULL, 0,
2324				     &kCFTypeDictionaryKeyCallBacks,
2325				     &kCFTypeDictionaryValueCallBacks);
2326    mode = EAPOLSocketGetMode(supp->sock);
2327    dictInsertMode(dict, mode);
2328    if (supp->config_id != NULL) {
2329	CFDictionarySetValue(dict, kEAPOLControlUniqueIdentifier,
2330			     supp->config_id);
2331    }
2332    dictInsertSupplicantState(dict, supp->state);
2333#if ! TARGET_OS_EMBEDDED
2334    dictInsertAuthenticatorMACAddress(dict, supp->sock);
2335    if (mode == kEAPOLControlModeUser) {
2336	dictInsertNumber(dict, kEAPOLControlUID, getuid());
2337    }
2338    if (supp->manager_name != NULL) {
2339	CFDictionarySetValue(dict, kEAPOLControlManagerName,
2340			     supp->manager_name);
2341    }
2342#endif /* ! TARGET_OS_EMBEDDED */
2343    if (supp->no_authenticator) {
2344	/* don't report EAP type information if no auth was present */
2345	dictInsertClientStatus(dict, kEAPClientStatusOK, 0);
2346    }
2347    else {
2348	dictInsertEAPTypeInfo(dict, supp->eap.last_type,
2349			      supp->eap.last_type_name);
2350	dictInsertClientStatus(dict, supp->last_status,
2351			       supp->last_error);
2352	if (supp->last_status == kEAPClientStatusUserInputRequired) {
2353	    if (supp->username == NULL) {
2354		dictInsertRequiredProperties(dict, NULL);
2355#if ! TARGET_OS_EMBEDDED
2356		need_username = TRUE;
2357#endif /* ! TARGET_OS_EMBEDDED */
2358	    }
2359	    else {
2360		dictInsertRequiredProperties(dict, supp->eap.required_props);
2361#if ! TARGET_OS_EMBEDDED
2362		need_password
2363		    = my_CFArrayContainsValue(supp->eap.required_props,
2364					      kEAPClientPropUserPassword);
2365		need_new_password
2366		    = my_CFArrayContainsValue(supp->eap.required_props,
2367					      kEAPClientPropNewPassword);
2368		need_trust
2369		    = my_CFArrayContainsValue(supp->eap.required_props,
2370					      kEAPClientPropTLSUserTrustProceedCertificateChain);
2371#endif /* ! TARGET_OS_EMBEDDED */
2372	    }
2373	}
2374	dictInsertPublishedProperties(dict, supp->eap.published_props);
2375	dictInsertIdentityAttributes(dict, supp->identity_attributes);
2376    }
2377    dictInsertGeneration(dict, supp->generation);
2378    timestamp = CFDateCreate(NULL, CFAbsoluteTimeGetCurrent());
2379    CFDictionarySetValue(dict, kEAPOLControlTimestamp, timestamp);
2380    my_CFRelease(&timestamp);
2381    if (eapolclient_should_log(kLogFlagBasic)) {
2382	CFStringRef	str = NULL;
2383
2384	if (dict != NULL) {
2385	    str = my_CFPropertyListCopyAsXMLString(dict);
2386	}
2387	EAPLOG(-LOG_DEBUG, "Supplicant %s status: state=%s\n%@",
2388	       EAPOLSocketName(supp->sock),
2389	       SupplicantStateString(supp->state),
2390	       str == NULL ? CFSTR("") : str);
2391	my_CFRelease(&str);
2392    }
2393    EAPOLSocketReportStatus(supp->sock, dict);
2394    my_CFRelease(&dict);
2395
2396#if ! TARGET_OS_EMBEDDED
2397    if (supp->no_ui) {
2398	goto no_ui;
2399    }
2400    if (need_username || need_password || need_new_password) {
2401	if (supp->cred_prompt == NULL) {
2402	    Boolean			ask_for_password = TRUE;
2403	    CFMutableDictionaryRef	details;
2404	    CFArrayRef			identities = NULL;
2405
2406	    if (need_new_password == FALSE && need_password == FALSE) {
2407		Boolean			cert_required;
2408		Boolean			tls_specified;
2409
2410		need_password
2411		    = EAPAcceptTypesRequirePassword(&supp->eap_accept);
2412		tls_specified
2413		    = EAPAcceptTypesIsSupportedType(&supp->eap_accept,
2414						    kEAPTypeTLS);
2415		cert_required
2416		    = myCFDictionaryGetBooleanValue(supp->config_dict,
2417						    kEAPClientPropTLSCertificateIsRequired,
2418						    FALSE);
2419		if (tls_specified || cert_required) {
2420		    Boolean		only_tls_specified;
2421
2422		    only_tls_specified
2423			= (tls_specified && need_password == FALSE);
2424		    (void)EAPSecIdentityListCreate(&identities);
2425		    if (identities == NULL) {
2426			if (only_tls_specified || cert_required) {
2427			    /* we need a cert to authenticate */
2428			    /* XXX tell the user there aren't any certs */
2429			    EAPLOG(LOG_NOTICE, "User has no certificates");
2430			    return;
2431			}
2432		    }
2433		    else if (only_tls_specified) {
2434			/* no need for a password */
2435			ask_for_password = FALSE;
2436		    }
2437		}
2438	    }
2439	    details
2440		= CFDictionaryCreateMutable(NULL, 0,
2441					    &kCFTypeDictionaryKeyCallBacks,
2442					    &kCFTypeDictionaryValueCallBacks);
2443	    if (EAPOLSocketIsWireless(supp->sock)) {
2444		CFStringRef	ssid;
2445
2446		ssid = EAPOLSocketGetSSID(supp->sock);
2447		if (ssid == NULL) {
2448		    ssid = CFSTR("");
2449		}
2450		CFDictionarySetValue(details, kCredentialsDialogueSSID, ssid);
2451	    }
2452	    if (supp->itemID != NULL && supp->one_time_password == FALSE) {
2453		CFDictionarySetValue(details,
2454				     kCredentialsDialogueRememberInformation,
2455				     kCFBooleanTrue);
2456	    }
2457	    if (supp->username != NULL) {
2458		CFStringRef	str;
2459
2460		str = my_CFStringCreateWithCString(supp->username);
2461		CFDictionarySetValue(details,
2462				     kCredentialsDialogueAccountName,
2463				     str);
2464		CFRelease(str);
2465	    }
2466	    if (need_new_password && supp->password != NULL) {
2467		CFStringRef	str;
2468
2469		/* password change dialogue */
2470		CFDictionarySetValue(details,
2471				     kCredentialsDialoguePasswordChangeRequired,
2472				     kCFBooleanTrue);
2473		str = my_CFStringCreateWithCString(supp->password);
2474		CFDictionarySetValue(details,
2475				     kCredentialsDialoguePassword,
2476				     str);
2477		CFRelease(str);
2478	    }
2479	    else if (ask_for_password == FALSE) {
2480		CFDictionarySetValue(details,
2481				     kCredentialsDialoguePassword,
2482				     kCFNull);
2483	    }
2484	    if (identities != NULL) {
2485		CFDictionarySetValue(details,
2486				     kCredentialsDialogueCertificates,
2487				     identities);
2488		CFRelease(identities);
2489	    }
2490	    supp->remember_information = FALSE;
2491	    supp->cred_prompt
2492		= CredentialsDialogue_create(credentials_callback, supp, NULL,
2493					     details);
2494	    my_CFRelease(&details);
2495	}
2496    }
2497    if (need_trust) {
2498	if (supp->trust_prompt == NULL) {
2499	    CFStringRef	ssid = NULL;
2500
2501	    if (EAPOLSocketIsWireless(supp->sock)) {
2502		ssid = EAPOLSocketGetSSID(supp->sock);
2503		if (ssid == NULL) {
2504		    ssid = CFSTR("");
2505		}
2506	    }
2507	    supp->trust_prompt
2508		= TrustDialogue_create(trust_callback, supp, NULL,
2509				       supp->eap.published_props,
2510				       ssid);
2511	}
2512    }
2513 no_ui:
2514#endif /* ! TARGET_OS_EMBEDDED */
2515
2516    return;
2517}
2518
2519static void
2520Supplicant_held(SupplicantRef supp, SupplicantEvent event,
2521		void * evdata)
2522{
2523    EAPRequestPacket *		req_p;
2524    EAPOLSocketReceiveDataRef 	rx = evdata;
2525    struct timeval 		t = {S_held_period_secs, 0};
2526
2527    switch (event) {
2528    case kSupplicantEventStart:
2529	if (EAPOLSocketIsWireless(supp->sock)) {
2530	    clear_wpa_key_info(supp);
2531	}
2532	Supplicant_cancel_pending_events(supp);
2533	supp->state = kSupplicantStateHeld;
2534	Supplicant_report_status(supp);
2535	supp->previous_identifier = BAD_IDENTIFIER;
2536	EAPAcceptTypesReset(&supp->eap_accept);
2537#if ! TARGET_OS_EMBEDDED
2538	present_alert_dialogue(supp);
2539	CredentialsDialogue_free(&supp->cred_prompt);
2540	TrustDialogue_free(&supp->trust_prompt);
2541#endif /* ! TARGET_OS_EMBEDDED */
2542	if (supp->eap.module != NULL
2543	    && (supp->last_status == kEAPClientStatusFailed
2544		|| (supp->last_status == kEAPClientStatusSecurityError
2545		    && supp->last_error == errSSLCrypto))) {
2546	    if (supp->no_ui == FALSE) {
2547		clear_sec_identity(supp);
2548		clear_username(supp);
2549		clear_password(supp);
2550#if ! TARGET_OS_EMBEDDED
2551		if (EAPOLSocketIsWireless(supp->sock)) {
2552		    /* force re-association to immediately prompt the user */
2553		    EAPOLSocketReassociate(supp->sock);
2554		}
2555#endif /* ! TARGET_OS_EMBEDDED */
2556	    }
2557#if ! TARGET_OS_EMBEDDED
2558	    else {
2559		supp->failure_count++;
2560	    }
2561#endif /* ! TARGET_OS_EMBEDDED */
2562	}
2563	supp->last_status = kEAPClientStatusOK;
2564	supp->last_error = 0;
2565	free_last_packet(supp);
2566	eap_client_free(supp);
2567	EAPOLSocketEnableReceive(supp->sock,
2568				 (void *)Supplicant_held,
2569				 (void *)supp,
2570				 (void *)kSupplicantEventData);
2571	/* set a timeout */
2572	Timer_set_relative(supp->timer, t,
2573			   (void *)Supplicant_held,
2574			   (void *)supp,
2575			   (void *)kSupplicantEventTimeout,
2576			   NULL);
2577	break;
2578    case kSupplicantEventTimeout:
2579	Supplicant_connecting(supp, kSupplicantEventStart, NULL);
2580	break;
2581    case kSupplicantEventData:
2582	if (rx->eapol_p->packet_type != kEAPOLPacketTypeEAPPacket) {
2583	    break;
2584	}
2585	req_p = (EAPRequestPacket *)rx->eapol_p->body;
2586	switch (req_p->code) {
2587	case kEAPCodeRequest:
2588	    switch (req_p->type) {
2589	    case kEAPTypeIdentity:
2590		Supplicant_acquired(supp, kSupplicantEventStart, evdata);
2591		break;
2592	    case kEAPTypeNotification:
2593		/* need to display information to the user XXX */
2594		log_eap_notification(supp->state, req_p);
2595		respond_to_notification(supp, req_p->identifier);
2596		break;
2597	    default:
2598		Supplicant_authenticating(supp, kSupplicantEventStart, evdata);
2599		break;
2600	    }
2601	    break;
2602	default:
2603	    break;
2604	}
2605	break;
2606
2607    default:
2608	break;
2609    }
2610}
2611
2612PRIVATE_EXTERN void
2613Supplicant_start(SupplicantRef supp)
2614{
2615    if (EAPOLSocketIsLinkActive(supp->sock)) {
2616	Supplicant_disconnected(supp, kSupplicantEventStart, NULL);
2617    }
2618    else {
2619	Supplicant_inactive(supp, kSupplicantEventStart, NULL);
2620    }
2621    return;
2622}
2623
2624static void
2625Supplicant_inactive(SupplicantRef supp, SupplicantEvent event, void * evdata)
2626{
2627    switch (event) {
2628    case kSupplicantEventStart:
2629	Supplicant_cancel_pending_events(supp);
2630	supp->state = kSupplicantStateInactive;
2631	supp->no_authenticator = TRUE;
2632	Supplicant_report_status(supp);
2633	EAPOLSocketEnableReceive(supp->sock,
2634				 (void *)Supplicant_connecting,
2635				 (void *)supp,
2636				 (void *)kSupplicantEventStart);
2637	break;
2638
2639    default:
2640	break;
2641    }
2642    return;
2643}
2644
2645static void
2646Supplicant_logoff(SupplicantRef supp, SupplicantEvent event, void * evdata)
2647{
2648    switch (event) {
2649    case kSupplicantEventStart:
2650	Supplicant_cancel_pending_events(supp);
2651	if (EAPOLSocketIsWireless(supp->sock)) {
2652	    EAPOLSocketClearPMKCache(supp->sock);
2653	}
2654	if (supp->state != kSupplicantStateAuthenticated) {
2655	    break;
2656	}
2657	supp->state = kSupplicantStateLogoff;
2658	supp->last_status = kEAPClientStatusOK;
2659	eap_client_free(supp);
2660	EAPOLSocketTransmit(supp->sock,
2661			    kEAPOLPacketTypeLogoff,
2662			    NULL, 0);
2663	Supplicant_report_status(supp);
2664	break;
2665    default:
2666	break;
2667    }
2668    return;
2669}
2670
2671static int
2672my_strcmp(char * s1, char * s2)
2673{
2674    if (s1 == NULL || s2 == NULL) {
2675	if (s1 == s2) {
2676	    return (0);
2677	}
2678	if (s1 == NULL) {
2679	    return (-1);
2680	}
2681	return (1);
2682    }
2683    return (strcmp(s1, s2));
2684}
2685
2686static char *
2687eap_method_user_name(EAPAcceptTypesRef accept, CFDictionaryRef config_dict)
2688{
2689    int 	i;
2690
2691    for (i = 0; i < accept->count; i++) {
2692	EAPClientModuleRef	module;
2693	CFStringRef		eap_user;
2694
2695	module = EAPClientModuleLookup(accept->types[i]);
2696	if (module == NULL) {
2697	    continue;
2698	}
2699	eap_user = EAPClientModulePluginUserName(module, config_dict);
2700	if (eap_user != NULL) {
2701	    char *	user;
2702
2703	    user = my_CFStringToCString(eap_user, kCFStringEncodingUTF8);
2704	    my_CFRelease(&eap_user);
2705	    return (user);
2706	}
2707    }
2708    return (NULL);
2709}
2710
2711/*
2712 * Function: S_string_copy_from_data
2713 * Purpose:
2714 *   Take the bytes from the specified CFDataRef and allocate a C-string
2715 *   large enough to hold the bytes plus the terminating nul char.
2716 *
2717 *   The assumption here is that the data represents a string but in data
2718 *   form.
2719 */
2720static char *
2721S_string_from_data(CFDataRef data)
2722{
2723    int		data_length;
2724    char *	str;
2725
2726    data_length = (int)CFDataGetLength(data);
2727    str = malloc(data_length + 1);
2728    bcopy(CFDataGetBytePtr(data), str, data_length);
2729    str[data_length] = '\0';
2730    return (str);
2731}
2732
2733static char *
2734S_copy_password_from_keychain(bool use_system_keychain,
2735			      CFStringRef unique_id_str)
2736{
2737    SecKeychainRef 	keychain = NULL;
2738    char *		password = NULL;
2739    CFDataRef		password_data = NULL;
2740    OSStatus		status;
2741
2742#if ! TARGET_OS_EMBEDDED
2743    if (use_system_keychain) {
2744	status =  SecKeychainCopyDomainDefault(kSecPreferencesDomainSystem,
2745					       &keychain);
2746	if (status != noErr) {
2747	    goto done;
2748	}
2749    }
2750#endif /* ! TARGET_OS_EMBEDDED */
2751
2752    status = EAPSecKeychainPasswordItemCopy(keychain, unique_id_str,
2753					    &password_data);
2754    if (status != noErr) {
2755	EAPLOG_FL(LOG_NOTICE, "SecKeychainFindGenericPassword failed, %d",
2756		  status);
2757	goto done;
2758    }
2759    password = S_string_from_data(password_data);
2760
2761 done:
2762    my_CFRelease(&password_data);
2763    my_CFRelease(&keychain);
2764    return ((char *)password);
2765}
2766
2767static Boolean
2768myCFDictionaryGetBooleanValue(CFDictionaryRef properties, CFStringRef propname,
2769			      Boolean def_value)
2770{
2771    bool		ret = def_value;
2772
2773    if (properties != NULL) {
2774	CFBooleanRef	val;
2775
2776	val = CFDictionaryGetValue(properties, propname);
2777	if (isA_CFBoolean(val)) {
2778	    ret = CFBooleanGetValue(val);
2779	}
2780    }
2781    return (ret);
2782}
2783
2784/*
2785 * Function: S_filter_eap_accept_types
2786 * Purpose:
2787 *   Filter out protocols we accept depending on which credential type(s)
2788 *   are specified.
2789 *
2790 *   If there's just a single protocol specified, no filtering is required.
2791 *   If both password and identity are specifed, or both password and identity
2792 *   are not specified, no filtering is possible.
2793 *   Otherwise, either make EAP-TLS the only choice if an identity is
2794 *   specified, or exclude EAP-TLS if a password is specified.
2795 */
2796static void
2797S_filter_eap_accept_types(SupplicantRef supp, CFArrayRef accept_types,
2798			  bool password_specified, bool identity_specified)
2799{
2800    EAPAcceptTypesRef		accept_p = &supp->eap_accept;
2801    int				tls_index = -1;
2802
2803    if (accept_p->count < 2 || (password_specified == identity_specified)) {
2804	return;
2805    }
2806    tls_index = EAPAcceptTypesIndexOfType(accept_p, kEAPTypeTLS);
2807    if (identity_specified) {
2808	if (tls_index == INDEX_NONE) {
2809	    if (myCFDictionaryGetBooleanValue(supp->config_dict,
2810					      kEAPClientPropTLSCertificateIsRequired,
2811					      FALSE) == FALSE) {
2812		/* this should not happen */
2813		EAPLOG(LOG_NOTICE,
2814		       "%s: identity specified but EAP-TLS isn't enabled",
2815		       EAPOLSocketIfName(supp->sock, NULL));
2816	    }
2817	}
2818	else {
2819	    /* only accept EAP-TLS */
2820	    accept_p->count = 1;
2821	    accept_p->types[0] = kEAPTypeTLS;
2822	    eapolclient_log(kLogFlagBasic,
2823			    "identity is specified, enabling EAP-TLS only");
2824	}
2825    }
2826    else if (tls_index != INDEX_NONE) {
2827	/* exclude EAP-TLS */
2828	eapolclient_log(kLogFlagBasic,
2829			"password is specified, disabling EAP-TLS");
2830	EAPAcceptTypesRemoveTypeAtIndex(accept_p, tls_index);
2831    }
2832    return;
2833}
2834
2835
2836static bool
2837S_set_credentials(SupplicantRef supp)
2838{
2839    CFArrayRef			accept_types = NULL;
2840    bool			cert_required = FALSE;
2841    bool			change = FALSE;
2842#if ! TARGET_OS_EMBEDDED
2843    EAPOLClientDomain		domain;
2844    CFStringRef			nodename = NULL;
2845#endif /* ! TARGET_OS_EMBEDDED */
2846    EAPSecIdentityHandleRef	id_handle = NULL;
2847    CFArrayRef			inner_accept_types;
2848    char *			name = NULL;
2849    CFStringRef			outer_identity_cf = NULL;
2850    char *			outer_identity = NULL;
2851    char *			password = NULL;
2852    bool			remember_information = FALSE;
2853    SecIdentityRef		sec_identity = NULL;
2854    OSStatus			status;
2855    bool			system_mode = FALSE;
2856    bool			username_derived = FALSE;
2857
2858    if (supp->config_dict != NULL) {
2859	accept_types
2860	    = CFDictionaryGetValue(supp->config_dict,
2861				   kEAPClientPropAcceptEAPTypes);
2862    }
2863    if (isA_CFArray(accept_types) == NULL) {
2864	EAPAcceptTypesFree(&supp->eap_accept);
2865	return (TRUE);
2866    }
2867    EAPAcceptTypesInit(&supp->eap_accept, accept_types);
2868
2869    inner_accept_types
2870	= CFDictionaryGetValue(supp->config_dict,
2871			       kEAPClientPropInnerAcceptEAPTypes);
2872    inner_accept_types = isA_CFArray(inner_accept_types);
2873
2874    switch (EAPOLSocketGetMode(supp->sock)) {
2875    case kEAPOLControlModeSystem:
2876	system_mode = TRUE;
2877#if ! TARGET_OS_EMBEDDED
2878	S_set_credentials_access_time(supp);
2879	domain = kEAPOLClientDomainSystem;
2880#endif /* ! TARGET_OS_EMBEDDED */
2881	break;
2882    case kEAPOLControlModeUser:
2883	/* check whether one-time use password */
2884	supp->one_time_password
2885	    = myCFDictionaryGetBooleanValue(supp->config_dict,
2886					    kEAPClientPropOneTimeUserPassword,
2887					    FALSE);
2888#if ! TARGET_OS_EMBEDDED
2889	domain = kEAPOLClientDomainUser;
2890	if (supp->one_time_password == FALSE) {
2891	    remember_information
2892		= myCFDictionaryGetBooleanValue(supp->config_dict,
2893						kEAPClientPropSaveCredentialsOnSuccessfulAuthentication,
2894						FALSE);
2895	}
2896#endif /* ! TARGET_OS_EMBEDDED */
2897	break;
2898    default:
2899#if ! TARGET_OS_EMBEDDED
2900	domain = 0;
2901#endif /* ! TARGET_OS_EMBEDDED */
2902	break;
2903    }
2904
2905#if ! TARGET_OS_EMBEDDED
2906    /* in system mode, check for OD password if so configured */
2907    if (system_mode
2908	&& S_system_mode_use_od(supp->config_dict, &nodename)) {
2909	CFStringRef	domain_cf = NULL;
2910	CFStringRef	password_cf = NULL;
2911	CFStringRef	username_cf = NULL;
2912
2913	if (ODTrustInfoCopy(nodename,
2914			    &domain_cf,
2915			    &username_cf,
2916			    &password_cf)
2917	    && username_cf != NULL
2918	    && password_cf != NULL) {
2919	    if (domain_cf != NULL) {
2920		/* Create the fully-qualified user name */
2921		CFMutableStringRef fqusername_cf =
2922		    CFStringCreateMutableCopy(NULL, 0, domain_cf);
2923		CFStringAppend(fqusername_cf, CFSTR("\\"));
2924		CFStringAppend(fqusername_cf, username_cf);
2925		CFRelease(username_cf);
2926		username_cf = fqusername_cf;
2927	    }
2928
2929	    name = my_CFStringToCString(username_cf, kCFStringEncodingUTF8);
2930	    password = my_CFStringToCString(password_cf, kCFStringEncodingUTF8);
2931	}
2932	my_CFRelease(&username_cf);
2933	my_CFRelease(&password_cf);
2934	my_CFRelease(&domain_cf);
2935	if (name != NULL && password != NULL) {
2936	    /* successfully retrieved OpenDirectory credentials */
2937	    EAPLOG(LOG_NOTICE,
2938		   "System Mode using OD account '%s'",
2939		   name);
2940	}
2941	else {
2942	    /* failed to get OpenDirectory credentials */
2943	    EAPLOG(LOG_NOTICE, "System Mode OD credentials unavailable");
2944	}
2945	goto filter_eap_types;
2946    }
2947#endif /* ! TARGET_OS_EMBEDDED */
2948
2949    /* extract the username */
2950    if (supp->ignore_username == FALSE) {
2951	CFStringRef		name_cf;
2952
2953	name_cf = CFDictionaryGetValue(supp->config_dict,
2954				       kEAPClientPropUserName);
2955	name_cf = isA_CFString(name_cf);
2956	if (name_cf != NULL) {
2957	    name = my_CFStringToCString(name_cf, kCFStringEncodingUTF8);
2958	    if (remember_information) {
2959		supp->remember_information = TRUE;
2960	    }
2961	}
2962    }
2963
2964    /* extract the password */
2965    if (supp->ignore_password == FALSE) {
2966	CFStringRef	item_cf;
2967	CFStringRef	password_cf;
2968
2969	password_cf = CFDictionaryGetValue(supp->config_dict,
2970					   kEAPClientPropUserPassword);
2971	item_cf
2972	    = CFDictionaryGetValue(supp->config_dict,
2973				   kEAPClientPropUserPasswordKeychainItemID);
2974	if (isA_CFString(password_cf) != NULL) {
2975	    password = my_CFStringToCString(password_cf,
2976					    kCFStringEncodingMacRoman);
2977	    if (remember_information) {
2978		supp->remember_information = TRUE;
2979	    }
2980	}
2981	else if (isA_CFString(item_cf) != NULL) {
2982	    password = S_copy_password_from_keychain(system_mode,
2983						     item_cf);
2984	    if (password == NULL) {
2985		EAPLOG_FL(LOG_NOTICE,
2986			  "%s: failed to retrieve password from keychain",
2987			  EAPOLSocketIfName(supp->sock, NULL));
2988	    }
2989	}
2990#if ! TARGET_OS_EMBEDDED
2991	else if (name == NULL && supp->itemID != NULL) {
2992	    CFDataRef		name_data = NULL;
2993	    CFDataRef		password_data = NULL;
2994
2995	    if (EAPOLClientItemIDCopyPasswordItem(supp->itemID,
2996						  domain,
2997						  &name_data,
2998						  &password_data)) {
2999		if (password_data != NULL) {
3000		    password = S_string_from_data(password_data);
3001		}
3002		if (name_data != NULL) {
3003		    name = S_string_from_data(name_data);
3004		}
3005		my_CFRelease(&name_data);
3006		my_CFRelease(&password_data);
3007	    }
3008	}
3009#endif /* ! TARGET_OS_EMBEDDED */
3010    }
3011
3012    /* check for a SecIdentity */
3013    if (supp->ignore_sec_identity == FALSE) {
3014	bool		tls_specified;
3015
3016	tls_specified
3017	    = (S_array_contains_int(accept_types, kEAPTypeTLS)
3018	       || S_array_contains_int(inner_accept_types, kEAPTypeTLS));
3019	cert_required
3020	    = myCFDictionaryGetBooleanValue(supp->config_dict,
3021					    kEAPClientPropTLSCertificateIsRequired,
3022					    tls_specified);
3023    }
3024    if (cert_required) {
3025	id_handle = CFDictionaryGetValue(supp->config_dict,
3026					 kEAPClientPropTLSIdentityHandle);
3027	if (id_handle != NULL) {
3028	    status = EAPSecIdentityHandleCreateSecIdentity(id_handle,
3029							   &sec_identity);
3030	    if (status != noErr) {
3031		EAPLOG_FL(LOG_NOTICE,
3032			  "EAPSecIdentityHandleCreateSecIdentity failed, %d",
3033			  status);
3034	    }
3035	    else if (remember_information) {
3036		supp->remember_information = TRUE;
3037	    }
3038	}
3039
3040#if ! TARGET_OS_EMBEDDED
3041	/* grab itemID-based identity */
3042	if (sec_identity == NULL && supp->itemID != NULL) {
3043	    sec_identity = EAPOLClientItemIDCopyIdentity(supp->itemID, domain);
3044	}
3045#endif /* ! TARGET_OS_EMBEDDED */
3046	my_CFRelease(&supp->sec_identity);
3047	supp->sec_identity = sec_identity;
3048
3049	if (name == NULL && sec_identity != NULL) {
3050	    name = S_identity_copy_name(sec_identity);
3051	    if (name != NULL) {
3052		username_derived = TRUE;
3053	    }
3054	}
3055    }
3056
3057#if ! TARGET_OS_EMBEDDED
3058 filter_eap_types:
3059#endif /* ! TARGET_OS_EMBEDDED */
3060
3061    /* update the list of protocols we accept */
3062    S_filter_eap_accept_types(supp, accept_types, (password != NULL),
3063			      (sec_identity != NULL));
3064
3065    /* name */
3066    if (cert_required == FALSE && name == NULL) {
3067	/* no username specified, ask EAP types if they can come up with one */
3068	name = eap_method_user_name(&supp->eap_accept, supp->config_dict);
3069	if (name != NULL) {
3070	    username_derived = TRUE;
3071	}
3072    }
3073    if (my_strcmp(supp->username, name) != 0) {
3074	change = TRUE;
3075    }
3076    if (supp->username != NULL) {
3077	free(supp->username);
3078    }
3079    supp->username = name;
3080    if (name != NULL) {
3081	supp->username_length = (int)strlen(name);
3082    }
3083    else {
3084	supp->username_length = 0;
3085    }
3086    supp->username_derived = username_derived;
3087
3088    /* password */
3089    if (my_strcmp(supp->password, password) != 0) {
3090	change = TRUE;
3091    }
3092    if (supp->password != NULL) {
3093	free(supp->password);
3094    }
3095    supp->password = password;
3096    if (password != NULL) {
3097	supp->password_length = (int)strlen(password);
3098    }
3099    else {
3100	supp->password_length = 0;
3101    }
3102
3103    /* extract the outer identity */
3104    if (EAPAcceptTypesUseOuterIdentity(&supp->eap_accept) == TRUE) {
3105	outer_identity_cf = CFDictionaryGetValue(supp->config_dict,
3106						 kEAPClientPropOuterIdentity);
3107	outer_identity_cf = isA_CFString(outer_identity_cf);
3108	if (outer_identity_cf != NULL) {
3109	    outer_identity = my_CFStringToCString(outer_identity_cf,
3110						  kCFStringEncodingUTF8);
3111	}
3112    }
3113    if (my_strcmp(supp->outer_identity, outer_identity) != 0) {
3114	change = TRUE;
3115    }
3116    if (supp->outer_identity != NULL) {
3117	free(supp->outer_identity);
3118    }
3119    supp->outer_identity = outer_identity;
3120    if (outer_identity != NULL) {
3121	supp->outer_identity_length = (int)strlen(outer_identity);
3122    }
3123    else {
3124	supp->outer_identity_length = 0;
3125    }
3126    return (change);
3127}
3128
3129static void
3130dict_set_key_value(const void * key, const void * value, void * context)
3131{
3132    CFMutableDictionaryRef	new_dict = (CFMutableDictionaryRef)context;
3133
3134    /* set the (key, value) */
3135    CFDictionarySetValue(new_dict, key, value);
3136    return;
3137}
3138
3139static bool
3140cfstring_is_empty(CFStringRef str)
3141{
3142    if (str == NULL) {
3143	return (FALSE);
3144    }
3145    return (isA_CFString(str) == NULL || CFStringGetLength(str) == 0);
3146}
3147
3148PRIVATE_EXTERN bool
3149Supplicant_update_configuration(SupplicantRef supp, CFDictionaryRef config_dict,
3150				bool * should_stop)
3151{
3152#if ! TARGET_OS_EMBEDDED
3153    EAPOLClientConfigurationRef	cfg = NULL;
3154    EAPOLClientItemIDRef	itemID = NULL;
3155    CFDictionaryRef		item_dict;
3156    CFStringRef			manager_name;
3157    EAPOLClientProfileRef	profile = NULL;
3158    CFDictionaryRef		password_info = NULL;
3159#endif /* ! TARGET_OS_EMBEDDED */
3160    bool			change = FALSE;
3161    CFStringRef			config_id = NULL;
3162    CFDictionaryRef		eap_config;
3163    bool			empty_password = FALSE;
3164    bool			empty_user = FALSE;
3165
3166    if (should_stop != NULL) {
3167	*should_stop = FALSE;
3168    }
3169
3170#if ! TARGET_OS_EMBEDDED
3171    /* check for a manager name */
3172    my_CFRelease(&supp->manager_name);
3173    manager_name = CFDictionaryGetValue(config_dict,
3174					kEAPOLControlManagerName);
3175    if (isA_CFString(manager_name) != NULL) {
3176	supp->manager_name = CFRetain(manager_name);
3177    }
3178
3179    /* check whether there is an itemID */
3180    item_dict = CFDictionaryGetValue(config_dict, kEAPOLControlClientItemID);
3181    if (item_dict != NULL) {
3182	if (isA_CFDictionary(item_dict) == NULL) {
3183	    EAPLOG_FL(LOG_NOTICE, "invalid item dict");
3184	    if (should_stop != NULL) {
3185		*should_stop = TRUE;
3186	    }
3187	    goto done;
3188	}
3189	my_CFRelease(&supp->itemID);
3190	my_CFRelease(&supp->eapolcfg);
3191	cfg = EAPOLClientConfigurationCreate(NULL);
3192	itemID = EAPOLClientItemIDCreateWithDictionary(cfg, item_dict);
3193	supp->itemID = itemID;
3194	supp->eapolcfg = cfg;
3195	if (itemID == NULL) {
3196	    EAPLOG_FL(LOG_NOTICE, "couldn't instantiate item");
3197	    if (should_stop != NULL) {
3198		*should_stop = TRUE;
3199	    }
3200	    goto done;
3201	}
3202	profile = EAPOLClientItemIDGetProfile(itemID);
3203	if (profile == NULL) {
3204	    eap_config =
3205		EAPOLClientConfigurationGetDefaultAuthenticationProperties(cfg);
3206	}
3207	else {
3208	    eap_config = EAPOLClientProfileGetAuthenticationProperties(profile);
3209	    if (eap_config == NULL) {
3210		EAPLOG_FL(LOG_NOTICE,
3211			  "profile has no authentication properties");
3212		if (should_stop != NULL) {
3213		    *should_stop = TRUE;
3214		}
3215		goto done;
3216	    }
3217	    config_id = EAPOLClientProfileGetID(profile);
3218	    /* we're using a profile, monitor whether it gets removed */
3219	    S_add_config_notification(supp);
3220	}
3221
3222	/* name/password may be passed on the side in this dictionary */
3223	password_info
3224	    = CFDictionaryGetValue(config_dict,
3225				   kEAPOLControlEAPClientConfiguration);
3226	password_info = isA_CFDictionary(password_info);
3227    }
3228    else {
3229	my_CFRelease(&supp->itemID);
3230	my_CFRelease(&supp->eapolcfg);
3231#endif /* ! TARGET_OS_EMBEDDED */
3232
3233	/* get the new configuration */
3234	eap_config = CFDictionaryGetValue(config_dict,
3235					  kEAPOLControlEAPClientConfiguration);
3236	if (isA_CFDictionary(eap_config) == NULL) {
3237	    eap_config = config_dict;
3238	}
3239	config_id = CFDictionaryGetValue(config_dict,
3240					 kEAPOLControlUniqueIdentifier);
3241
3242#if ! TARGET_OS_EMBEDDED
3243    }
3244#endif /* ! TARGET_OS_EMBEDDED */
3245
3246    /* keep a copy of the original around */
3247    my_CFRelease(&supp->orig_config_dict);
3248    supp->orig_config_dict = CFDictionaryCreateCopy(NULL, config_dict);
3249
3250    empty_user
3251	= cfstring_is_empty(CFDictionaryGetValue(eap_config,
3252						 kEAPClientPropUserName));
3253    empty_password
3254	= cfstring_is_empty(CFDictionaryGetValue(eap_config,
3255						 kEAPClientPropUserPassword));
3256
3257    my_CFRelease(&supp->config_dict);
3258
3259    /* clean up empty username/password, add UI properties */
3260    if (empty_user || empty_password
3261	|| supp->ui_config_dict != NULL
3262#if ! TARGET_OS_EMBEDDED
3263	|| password_info != NULL
3264	|| profile != NULL
3265#endif /* ! TARGET_OS_EMBEDDED */
3266	|| CFDictionaryContainsKey(eap_config,
3267				   kEAPClientPropProfileID)) {
3268	CFMutableDictionaryRef		new_eap_config = NULL;
3269
3270	new_eap_config = CFDictionaryCreateMutableCopy(NULL, 0, eap_config);
3271	if (empty_user) {
3272	    CFDictionaryRemoveValue(new_eap_config, kEAPClientPropUserName);
3273	}
3274	if (empty_password) {
3275	    CFDictionaryRemoveValue(new_eap_config, kEAPClientPropUserPassword);
3276	}
3277	if (supp->ui_config_dict != NULL) {
3278	    CFDictionaryApplyFunction(supp->ui_config_dict, dict_set_key_value,
3279				      new_eap_config);
3280	}
3281	CFDictionaryRemoveValue(new_eap_config,
3282				kEAPClientPropProfileID);
3283#if ! TARGET_OS_EMBEDDED
3284	if (password_info != NULL) {
3285	    CFDictionaryApplyFunction(password_info, dict_set_key_value,
3286				      new_eap_config);
3287	}
3288	if (profile != NULL) {
3289	    CFDictionarySetValue(new_eap_config,
3290				 kEAPClientPropProfileID,
3291				 EAPOLClientProfileGetID(profile));
3292	}
3293#endif /* TARGET_OS_EMBEDDED */
3294	supp->config_dict = new_eap_config;
3295    }
3296    else {
3297	supp->config_dict = CFRetain(eap_config);
3298    }
3299    if (eapolclient_should_log(kLogFlagBasic)) {
3300	CFStringRef	str;
3301
3302	str = copy_cleaned_config_dict(supp->config_dict);
3303	EAPLOG(-LOG_DEBUG, "update_configuration\n%@", str);
3304	CFRelease(str);
3305    }
3306
3307    /* bump the configuration generation */
3308    supp->generation++;
3309
3310    /* get the configuration identifier */
3311    my_CFRelease(&supp->config_id);
3312    if (config_id != NULL && isA_CFString(config_id) != NULL) {
3313	supp->config_id = CFRetain(config_id);
3314    }
3315
3316    /* update the name/password, identity, and list of EAP types we accept */
3317    if (S_set_credentials(supp)) {
3318	change = TRUE;
3319    }
3320    if (EAPAcceptTypesIsSupportedType(&supp->eap_accept,
3321				      eap_client_type(supp)) == FALSE) {
3322	/* negotiated EAP type is no longer valid, start over */
3323	eap_client_free(supp);
3324	change = TRUE;
3325    }
3326
3327#if ! TARGET_OS_EMBEDDED
3328 done:
3329#endif /* ! TARGET_OS_EMBEDDED */
3330    return (change);
3331}
3332
3333PRIVATE_EXTERN bool
3334Supplicant_control(SupplicantRef supp,
3335		   EAPOLClientControlCommand command,
3336		   CFDictionaryRef control_dict)
3337{
3338    bool			change;
3339    CFDictionaryRef		config_dict = NULL;
3340    bool			should_stop = FALSE;
3341    CFDictionaryRef		user_input_dict = NULL;
3342
3343    switch (command) {
3344    case kEAPOLClientControlCommandRetry:
3345	if (supp->state != kSupplicantStateInactive) {
3346	    Supplicant_connecting(supp, kSupplicantEventStart, NULL);
3347	}
3348	break;
3349    case kEAPOLClientControlCommandTakeUserInput:
3350	user_input_dict = CFDictionaryGetValue(control_dict,
3351					       kEAPOLClientControlUserInput);
3352	if (user_input_dict != NULL) {
3353	    /* add the user input to the ui_config_dict */
3354	    create_ui_config_dict(supp);
3355	    CFDictionaryApplyFunction(user_input_dict, dict_set_key_value,
3356				      supp->ui_config_dict);
3357	}
3358	config_dict = CFDictionaryCreateCopy(NULL, supp->orig_config_dict);
3359	Supplicant_update_configuration(supp, config_dict, NULL);
3360	my_CFRelease(&config_dict);
3361	user_supplied_data(supp);
3362	break;
3363    case kEAPOLClientControlCommandRun:
3364	config_dict = CFDictionaryGetValue(control_dict,
3365					   kEAPOLClientControlConfiguration);
3366	if (config_dict == NULL) {
3367	    should_stop = TRUE;
3368	    break;
3369	}
3370	change = Supplicant_update_configuration(supp, config_dict,
3371						 &should_stop);
3372	if (should_stop) {
3373	    break;
3374	}
3375	if (EAPOLSocketIsLinkActive(supp->sock) == FALSE) {
3376	    /* no point in doing anything if the link is down */
3377	    break;
3378	}
3379	if (supp->last_status == kEAPClientStatusUserInputRequired) {
3380	    switch (supp->state) {
3381	    case kSupplicantStateAcquired:
3382		change = FALSE;
3383		if (supp->username != NULL) {
3384		    Supplicant_acquired(supp,
3385					kSupplicantEventMoreDataAvailable,
3386					NULL);
3387		}
3388		break;
3389	    case kSupplicantStateAuthenticating:
3390		if (change == FALSE && supp->last_rx_packet.eapol_p != NULL) {
3391		    process_packet(supp, &supp->last_rx_packet);
3392		}
3393		break;
3394	    default:
3395		break;
3396	    }
3397	}
3398	if (change) {
3399	    Supplicant_disconnected(supp, kSupplicantEventStart, NULL);
3400	}
3401	break;
3402    case kEAPOLClientControlCommandStop:
3403	should_stop = TRUE;
3404	break;
3405    default:
3406	break;
3407
3408    }
3409    return (should_stop);
3410}
3411
3412PRIVATE_EXTERN void
3413Supplicant_link_status_changed(SupplicantRef supp, bool active)
3414{
3415    struct timeval	t = {0, 0};
3416
3417    supp->auth_attempts_count = 0;
3418    if (active) {
3419
3420	t.tv_sec = S_link_active_period_secs;
3421	switch (supp->state) {
3422	case kSupplicantStateInactive:
3423	    if (supp->eap.module != NULL
3424		&& EAPOLSocketHasPMK(supp->sock)) {
3425		/* no need to re-start authentication */
3426		eapolclient_log(kLogFlagBasic, "Valid PMK Exists");
3427		Supplicant_authenticated(supp, kSupplicantEventStart, NULL);
3428		break;
3429	    }
3430	    /* FALL THROUGH */
3431	case kSupplicantStateConnecting:
3432	    /* give the Authenticator a chance to initiate */
3433	    t.tv_sec = 0;
3434	    t.tv_usec = 500 * 1000; /* 1/2 second */
3435	    /* FALL THROUGH */
3436	default:
3437	    /*
3438	     * wait awhile before entering connecting state to avoid
3439	     * disrupting an existing conversation
3440	     */
3441	    Timer_set_relative(supp->timer, t,
3442			       (void *)Supplicant_connecting,
3443			       (void *)supp,
3444			       (void *)kSupplicantEventStart,
3445			       NULL);
3446	    break;
3447	}
3448    }
3449    else {
3450	/*
3451	 * wait awhile before entering the inactive state to avoid
3452	 * disrupting an existing conversation
3453	 */
3454	t.tv_sec = S_link_inactive_period_secs;
3455
3456	/* if link is down, enter wait for link state */
3457	Timer_set_relative(supp->timer, t,
3458			   (void *)Supplicant_inactive,
3459			   (void *)supp,
3460			   (void *)kSupplicantEventStart,
3461			   NULL);
3462    }
3463    return;
3464}
3465
3466PRIVATE_EXTERN SupplicantRef
3467Supplicant_create(EAPOLSocketRef sock)
3468{
3469    SupplicantRef		supp = NULL;
3470    TimerRef			timer = NULL;
3471
3472    timer = Timer_create();
3473    if (timer == NULL) {
3474	EAPLOG_FL(LOG_NOTICE, "Timer_create failed");
3475	goto failed;
3476    }
3477
3478    supp = malloc(sizeof(*supp));
3479    if (supp == NULL) {
3480	EAPLOG_FL(LOG_NOTICE, "malloc failed");
3481	goto failed;
3482    }
3483
3484    bzero(supp, sizeof(*supp));
3485    supp->timer = timer;
3486    supp->sock = sock;
3487    return (supp);
3488
3489 failed:
3490    if (supp != NULL) {
3491	free(supp);
3492    }
3493    Timer_free(&timer);
3494    return (NULL);
3495}
3496
3497PRIVATE_EXTERN SupplicantRef
3498Supplicant_create_with_supplicant(EAPOLSocketRef sock, SupplicantRef main_supp)
3499{
3500    SupplicantRef	supp;
3501
3502    supp = Supplicant_create(sock);
3503    if (supp == NULL) {
3504	return (NULL);
3505    }
3506    supp->generation = main_supp->generation;
3507#if ! TARGET_OS_EMBEDDED
3508    if (main_supp->itemID != NULL) {
3509	CFRetain(main_supp->itemID);
3510	supp->itemID = main_supp->itemID;
3511    }
3512#endif /* ! TARGET_OS_EMBEDDED */
3513    if (main_supp->sec_identity != NULL) {
3514	CFRetain(main_supp->sec_identity);
3515	supp->sec_identity = main_supp->sec_identity;
3516    }
3517    supp->config_dict = CFRetain(main_supp->config_dict);
3518    if (main_supp->ui_config_dict) {
3519	supp->ui_config_dict
3520	    = CFDictionaryCreateMutableCopy(NULL, 0, main_supp->ui_config_dict);
3521    }
3522    if (main_supp->outer_identity != NULL) {
3523	supp->outer_identity = strdup(main_supp->outer_identity);
3524	supp->outer_identity_length = main_supp->outer_identity_length;
3525    }
3526    if (main_supp->username != NULL) {
3527	supp->username = strdup(main_supp->username);
3528	supp->username_length = main_supp->username_length;
3529    }
3530    if (main_supp->password != NULL) {
3531	supp->password = strdup(main_supp->password);
3532	supp->password_length = main_supp->password_length;
3533    }
3534    EAPAcceptTypesCopy(&supp->eap_accept, &main_supp->eap_accept);
3535    supp->no_ui = TRUE;
3536
3537    return (supp);
3538}
3539
3540PRIVATE_EXTERN void
3541Supplicant_free(SupplicantRef * supp_p)
3542{
3543    SupplicantRef supp;
3544
3545    if (supp_p == NULL) {
3546	return;
3547    }
3548    supp = *supp_p;
3549    if (supp != NULL) {
3550#if ! TARGET_OS_EMBEDDED
3551	AlertDialogue_free(&supp->alert_prompt);
3552	CredentialsDialogue_free(&supp->cred_prompt);
3553	TrustDialogue_free(&supp->trust_prompt);
3554#endif /* ! TARGET_OS_EMBEDDED */
3555	Timer_free(&supp->timer);
3556	my_CFRelease(&supp->orig_config_dict);
3557	my_CFRelease(&supp->config_dict);
3558	my_CFRelease(&supp->ui_config_dict);
3559	my_CFRelease(&supp->config_id);
3560	my_CFRelease(&supp->identity_attributes);
3561#if ! TARGET_OS_EMBEDDED
3562	my_CFRelease(&supp->eapolcfg);
3563	my_CFRelease(&supp->itemID);
3564	my_CFRelease(&supp->manager_name);
3565	if (supp->config_change.mp != NULL) {
3566	    CFMachPortInvalidate(supp->config_change.mp);
3567	    my_CFRelease(&supp->config_change.mp);
3568	    (void)notify_cancel(supp->config_change.token);
3569	}
3570#endif /* ! TARGET_OS_EMBEDDED */
3571	my_CFRelease(&supp->sec_identity);
3572	if (supp->outer_identity != NULL) {
3573	    free(supp->outer_identity);
3574	}
3575	if (supp->username != NULL) {
3576	    free(supp->username);
3577	}
3578	if (supp->password != NULL) {
3579	    free(supp->password);
3580	}
3581	EAPAcceptTypesFree(&supp->eap_accept);
3582	free_last_packet(supp);
3583	eap_client_free(supp);
3584	free(supp);
3585    }
3586    *supp_p = NULL;
3587    return;
3588}
3589
3590PRIVATE_EXTERN SupplicantState
3591Supplicant_get_state(SupplicantRef supp, EAPClientStatus * last_status)
3592{
3593    *last_status = supp->last_status;
3594    return (supp->state);
3595}
3596
3597PRIVATE_EXTERN void
3598Supplicant_set_no_ui(SupplicantRef supp)
3599{
3600    supp->no_ui = TRUE;
3601    return;
3602}
3603
3604static CFStringRef
3605copy_cleaned_config_dict(CFDictionaryRef d)
3606{
3607    CFStringRef		password;
3608    CFStringRef		new_password;
3609    CFStringRef		str;
3610
3611    password = CFDictionaryGetValue(d, kEAPClientPropUserPassword);
3612    new_password = CFDictionaryGetValue(d, kEAPClientPropNewPassword);
3613    if (password != NULL || new_password != NULL) {
3614	CFMutableDictionaryRef	d_copy;
3615
3616	d_copy = CFDictionaryCreateMutableCopy(NULL, 0, d);
3617	if (password != NULL) {
3618	    CFDictionarySetValue(d_copy, kEAPClientPropUserPassword,
3619				 CFSTR("XXXXXXXX"));
3620	}
3621	if (new_password != NULL) {
3622	    CFDictionarySetValue(d_copy, kEAPClientPropNewPassword,
3623				 CFSTR("XXXXXXXX"));
3624	}
3625	str = my_CFPropertyListCopyAsXMLString(d_copy);
3626	CFRelease(d_copy);
3627    }
3628    else {
3629	str = my_CFPropertyListCopyAsXMLString(d);
3630    }
3631    return (str);
3632}
3633
3634#define SUCCESS_SIZE		(offsetof(EAPOLPacket, body)	\
3635				 + sizeof(EAPSuccessPacket))
3636PRIVATE_EXTERN void
3637Supplicant_simulate_success(SupplicantRef supp)
3638{
3639    uint32_t			buf[roundup(SUCCESS_SIZE, sizeof(uint32_t))];
3640    EAPOLPacketRef		eapol_p;
3641    EAPOLSocketReceiveData 	rx;
3642    EAPPacketRef		success_pkt;
3643
3644    if (supp->state != kSupplicantStateAuthenticating) {
3645	return;
3646    }
3647    eapolclient_log(kLogFlagBasic, "Simulating EAP Success packet");
3648    eapol_p = (EAPOLPacketRef)buf;
3649    eapol_p->protocol_version = EAPOL_802_1_X_PROTOCOL_VERSION;
3650    eapol_p->packet_type = kEAPOLPacketTypeEAPPacket;
3651    EAPOLPacketSetLength(eapol_p, sizeof(EAPSuccessPacket));
3652    success_pkt = (EAPPacketRef)eapol_p->body;
3653    success_pkt->code = kEAPCodeSuccess;
3654    success_pkt->identifier = 0;
3655    EAPPacketSetLength(success_pkt, sizeof(EAPSuccessPacket));
3656    rx.eapol_p = eapol_p;
3657    rx.length = SUCCESS_SIZE;
3658    Supplicant_authenticating(supp, kSupplicantEventData, &rx);
3659    return;
3660}
3661
3662PRIVATE_EXTERN void
3663Supplicant_set_globals(SCPreferencesRef prefs)
3664{
3665    CFDictionaryRef	plist;
3666
3667    if (prefs == NULL) {
3668	return;
3669    }
3670    plist = SCPreferencesGetValue(prefs, kSupplicant);
3671    if (isA_CFDictionary(plist) == NULL) {
3672	return;
3673    }
3674    S_start_period_secs
3675	= get_plist_int(plist, kStartPeriodSeconds, START_PERIOD_SECS);
3676    S_start_attempts_max
3677	= get_plist_int(plist, kStartAttemptsMax, START_ATTEMPTS_MAX);
3678    S_auth_period_secs
3679	= get_plist_int(plist, kAuthPeriodSeconds, AUTH_PERIOD_SECS);
3680    S_auth_attempts_max
3681	= get_plist_int(plist, kAuthAttemptsMax, AUTH_ATTEMPTS_MAX);
3682    S_held_period_secs
3683	= get_plist_int(plist, kHeldPeriodSeconds, HELD_PERIOD_SECS);
3684    return;
3685}
3686