1/*
2 * Copyright (c) 2002-2014 Apple Inc. All rights reserved.
3 *
4 * @APPLE_LICENSE_HEADER_START@
5 *
6 * This file contains Original Code and/or Modifications of Original Code
7 * as defined in and that are subject to the Apple Public Source License
8 * Version 2.0 (the 'License'). You may not use this file except in
9 * compliance with the License. Please obtain a copy of the License at
10 * http://www.opensource.apple.com/apsl/ and read it before using this
11 * file.
12 *
13 * The Original Code and all software distributed under the License are
14 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
15 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
16 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
18 * Please see the License for the specific language governing rights and
19 * limitations under the License.
20 *
21 * @APPLE_LICENSE_HEADER_END@
22 */
23
24/*
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/ioctl.h>
35#include <sys/sockio.h>
36#include <sys/socket.h>
37#include <string.h>
38#include <net/if.h>
39#include <net/if_media.h>
40#include <net/ethernet.h>
41#include <sys/errno.h>
42#include <sys/queue.h>
43#include <mach/mach.h>
44#include <mach/message.h>
45#include <mach/mach_error.h>
46#include <sys/param.h>
47#include <sys/types.h>
48#include <fcntl.h>
49#include <paths.h>
50#include <pwd.h>
51#include <pthread.h>
52#include <SystemConfiguration/SystemConfiguration.h>
53#include <SystemConfiguration/SCValidation.h>
54#include <SystemConfiguration/SCPrivate.h>
55#include <CoreFoundation/CFDictionary.h>
56#include <CoreFoundation/CFMachPort.h>
57#include <CoreFoundation/CFRunLoop.h>
58#include <SystemConfiguration/SCDPlugin.h>
59#include <TargetConditionals.h>
60#include <EAP8021X/myCFUtil.h>
61#include "controller.h"
62#include "server.h"
63#if ! TARGET_OS_EMBEDDED
64#include <CoreFoundation/CFSocket.h>
65#include "EAPOLClientConfiguration.h"
66#include "EAPOLClientConfigurationPrivate.h"
67#include "EAPOLControlPrivate.h"
68#include "EAPOLControlTypesPrivate.h"
69#endif /* ! TARGET_OS_EMBEDDED */
70#include "ClientControlInterface.h"
71#include "EAPOLControlTypes.h"
72#include "EAPClientProperties.h"
73#include "eapol_socket.h"
74#include "EAPLog.h"
75#include "EAPOLControlPrefs.h"
76
77#ifndef kSCEntNetRefreshConfiguration
78#define kSCEntNetRefreshConfiguration	CFSTR("RefreshConfiguration")
79#endif /* kSCEntNetRefreshConfiguration */
80
81#ifndef kSCEntNetEAPOL
82#define kSCEntNetEAPOL			CFSTR("EAPOL")
83#endif /* kSCEntNetEAPOL */
84
85#define kSystemModeManagedExternally	CFSTR("_SystemModeManagedExternally")
86
87static SCDynamicStoreRef	S_store;
88static char * 			S_eapolclient_path = NULL;
89
90struct eapolClient_s;
91#define LIST_HEAD_clientHead 	LIST_HEAD(clientHead, eapolClient_s)
92static LIST_HEAD_clientHead	S_head;
93static struct clientHead * S_clientHead_p = &S_head;
94
95#define LIST_ENTRY_eapolClient_s	LIST_ENTRY(eapolClient_s)
96
97typedef struct eapolClient_s {
98    LIST_ENTRY_eapolClient_s	link;
99    EAPOLControlState		state;
100
101    if_name_t			if_name;
102    CFStringRef 		if_name_cf;
103    CFStringRef			notification_key;
104    CFStringRef			force_renew_key;
105    struct {
106	uid_t			uid;
107	gid_t			gid;
108    } owner;
109
110    pid_t			pid;
111    mach_port_t			notify_port;
112    mach_port_t			bootstrap;
113    mach_port_t			au_session;
114    CFMachPortRef		session_cfport;
115    boolean_t			notification_sent;
116    boolean_t			retry;
117    boolean_t			user_input_provided;
118
119    boolean_t			console_user;	/* started by console user */
120    EAPOLControlMode		mode;
121    CFDictionaryRef		config_dict;
122    CFDictionaryRef		user_input_dict;
123
124    CFDictionaryRef		status_dict;
125#if ! TARGET_OS_EMBEDDED
126    CFDictionaryRef		loginwindow_config;
127    CFSocketRef			eapol_sock;
128    int				eapol_fd;
129    bool			packet_received;
130    struct ether_addr		authenticator_mac;
131
132    bool			user_cancelled;
133#endif /* ! TARGET_OS_EMBEDDED */
134} eapolClient, *eapolClientRef;
135
136#if TARGET_OS_EMBEDDED
137static __inline__ boolean_t
138is_console_user(uid_t check_uid)
139{
140    return (TRUE);
141}
142
143#else /* TARGET_OS_EMBEDDED */
144
145static void
146clear_loginwindow_config(eapolClientRef client)
147{
148    my_CFRelease(&client->loginwindow_config);
149    return;
150}
151
152static void
153set_loginwindow_config(eapolClientRef client)
154{
155    CFDictionaryRef	itemID_dict;
156
157    clear_loginwindow_config(client);
158    if (client->config_dict == NULL) {
159	return;
160    }
161    /*
162     * If there's an EAPOLClientItemID dictionary, save a dictionary containing
163     * just that property: don't save the whole dictionary because it contains
164     * the name/password.
165     */
166    itemID_dict = CFDictionaryGetValue(client->config_dict,
167				       kEAPOLControlClientItemID);
168    if (isA_CFDictionary(itemID_dict) != NULL) {
169	CFStringRef	key;
170
171	key = kEAPOLControlClientItemID;
172	client->loginwindow_config
173	    = CFDictionaryCreate(NULL,
174				 (const void * *)&key,
175				 (const void * *)&itemID_dict, 1,
176				 &kCFTypeDictionaryKeyCallBacks,
177				 &kCFTypeDictionaryValueCallBacks);
178    }
179    return;
180}
181
182static uid_t
183login_window_uid(void)
184{
185    static uid_t	login_window_uid = -1;
186
187    /* look up the _securityagent user */
188    if (login_window_uid == -1) {
189	struct passwd *	pwd = getpwnam("_securityagent");
190	if (pwd == NULL) {
191	    EAPLOG(LOG_NOTICE,
192		   "EAPOLController: getpwnam(_securityagent) failed");
193	    return (92);
194	}
195	login_window_uid = pwd->pw_uid;
196    }
197    return (login_window_uid);
198}
199
200static boolean_t
201is_console_user(uid_t check_uid)
202{
203    uid_t	uid;
204    CFStringRef user;
205
206    user = SCDynamicStoreCopyConsoleUser(S_store, &uid, NULL);
207    if (user == NULL) {
208	return (FALSE);
209    }
210    CFRelease(user);
211    return (check_uid == uid);
212}
213
214static CFDictionaryRef
215S_profile_copy_itemID_dict(EAPOLClientProfileRef profile)
216{
217    CFStringRef			key;
218    CFDictionaryRef		dict;
219    EAPOLClientItemIDRef	itemID;
220    CFDictionaryRef		itemID_dict;
221
222    itemID = EAPOLClientItemIDCreateWithProfile(profile);
223    itemID_dict = EAPOLClientItemIDCopyDictionary(itemID);
224    CFRelease(itemID);
225    key = kEAPOLControlClientItemID;
226    dict = CFDictionaryCreate(NULL,
227			      (const void * *)&key,
228			      (const void * *)&itemID_dict, 1,
229			      &kCFTypeDictionaryKeyCallBacks,
230			      &kCFTypeDictionaryValueCallBacks);
231    CFRelease(itemID_dict);
232    return (dict);
233}
234#endif /* TARGET_OS_EMBEDDED */
235
236static int
237get_ifm_type(const char * name)
238{
239    int			i;
240    struct ifmediareq	ifm;
241    int			media_static[20];
242    int			media_static_count = sizeof(media_static) / sizeof(media_static[0]);
243    int			s;
244    int			ifm_type = 0;
245    bool		supports_full_duplex = FALSE;
246
247    s = socket(AF_INET, SOCK_DGRAM, 0);
248    if (s < 0) {
249	perror("socket");
250	goto done;
251    }
252    bzero(&ifm, sizeof(ifm));
253    strlcpy(ifm.ifm_name, name, sizeof(ifm.ifm_name));
254    if (ioctl(s, SIOCGIFMEDIA, (caddr_t)&ifm) < 0) {
255	goto done;
256    }
257    ifm_type = IFM_TYPE(ifm.ifm_current);
258    if (ifm_type != IFM_ETHER) {
259	goto done;
260    }
261    if (ifm.ifm_count == 0) {
262	goto done;
263    }
264    if (ifm.ifm_count > media_static_count) {
265	ifm.ifm_ulist = (int *)malloc(ifm.ifm_count * sizeof(int));
266    }
267    else {
268	ifm.ifm_ulist = media_static;
269    }
270    if (ioctl(s, SIOCGIFMEDIA, (caddr_t)&ifm) == -1) {
271	goto done;
272    }
273    if (ifm.ifm_count == 1) {
274	/* only support one media type, not real ethernet */
275	goto done;
276    }
277    for (i = 0; i < ifm.ifm_count; i++) {
278	if ((ifm.ifm_ulist[i] & IFM_FDX) != 0) {
279	    supports_full_duplex = TRUE;
280	    break;
281	}
282    }
283
284 done:
285    if (s >= 0) {
286	close(s);
287    }
288    if (ifm_type == IFM_ETHER && supports_full_duplex == FALSE) {
289	/* not really ethernet */
290	ifm_type = 0;
291    }
292    return (ifm_type);
293}
294
295
296static int
297eapolClientStop(eapolClientRef client);
298
299static void
300eapolClientSetState(eapolClientRef client, EAPOLControlState state);
301
302
303static CFNumberRef
304make_number(int val)
305{
306    return (CFNumberCreate(NULL, kCFNumberIntType, &val));
307}
308
309eapolClientRef
310eapolClientLookupInterfaceCF(CFStringRef if_name_cf)
311{
312    eapolClientRef	scan;
313
314    LIST_FOREACH(scan, S_clientHead_p, link) {
315	if (CFEqual(if_name_cf, scan->if_name_cf)) {
316	    return (scan);
317	}
318    }
319    return (NULL);
320}
321eapolClientRef
322eapolClientLookupInterface(const char * if_name)
323{
324    eapolClientRef	scan;
325
326    LIST_FOREACH(scan, S_clientHead_p, link) {
327	if (strcmp(if_name, scan->if_name) == 0) {
328	    return (scan);
329	}
330    }
331    return (NULL);
332}
333
334eapolClientRef
335eapolClientLookupProcess(pid_t pid)
336{
337    eapolClientRef	scan;
338
339    LIST_FOREACH(scan, S_clientHead_p, link) {
340	if (pid == scan->pid) {
341	    return (scan);
342	}
343    }
344    return (NULL);
345}
346
347eapolClientRef
348eapolClientLookupSession(mach_port_t session_port)
349{
350    eapolClientRef	scan;
351
352    LIST_FOREACH(scan, S_clientHead_p, link) {
353	if (scan->session_cfport != NULL
354	    && session_port == CFMachPortGetPort(scan->session_cfport)) {
355	    return (scan);
356	}
357    }
358    return (NULL);
359}
360
361eapolClientRef
362eapolClientAdd(const char * if_name)
363{
364    eapolClientRef client;
365
366    client = malloc(sizeof(*client));
367    if (client == NULL) {
368	return (NULL);
369    }
370    bzero(client, sizeof(*client));
371    strlcpy(client->if_name, if_name, sizeof(client->if_name));
372    client->if_name_cf = CFStringCreateWithCString(NULL, client->if_name,
373						   kCFStringEncodingASCII);
374    client->pid = -1;
375#if ! TARGET_OS_EMBEDDED
376    client->eapol_fd = -1;
377#endif /* ! TARGET_OS_EMBEDDED */
378    LIST_INSERT_HEAD(S_clientHead_p, client, link);
379    return (client);
380}
381
382void
383eapolClientRemove(eapolClientRef client)
384{
385#if ! TARGET_OS_EMBEDDED
386    clear_loginwindow_config(client);
387#endif /* ! TARGET_OS_EMBEDDED */
388    my_CFRelease(&client->if_name_cf);
389    LIST_REMOVE(client, link);
390    free(client);
391    return;
392}
393
394static void
395eapolClientInvalidate(eapolClientRef client)
396{
397    eapolClientSetState(client, kEAPOLControlStateIdle);
398    client->pid = -1;
399    client->owner.uid = 0;
400    client->owner.gid = 0;
401    client->mode = kEAPOLControlModeNone;
402    client->retry = FALSE;
403    client->notification_sent = FALSE;
404    client->console_user = FALSE;
405    client->user_input_provided = FALSE;
406    my_CFRelease(&client->notification_key);
407    my_CFRelease(&client->force_renew_key);
408    if (client->notify_port != MACH_PORT_NULL) {
409	(void)mach_port_deallocate(mach_task_self(), client->notify_port);
410	client->notify_port = MACH_PORT_NULL;
411    }
412    if (client->bootstrap != MACH_PORT_NULL) {
413	(void)mach_port_deallocate(mach_task_self(), client->bootstrap);
414	client->bootstrap = MACH_PORT_NULL;
415    }
416    if (client->au_session != MACH_PORT_NULL) {
417	(void)mach_port_deallocate(mach_task_self(), client->au_session);
418	client->au_session = MACH_PORT_NULL;
419    }
420    if (client->session_cfport != NULL) {
421	CFMachPortInvalidate(client->session_cfport);
422	my_CFRelease(&client->session_cfport);
423    }
424    my_CFRelease(&client->config_dict);
425    my_CFRelease(&client->user_input_dict);
426    my_CFRelease(&client->status_dict);
427    return;
428}
429
430static void
431eapolClientNotify(eapolClientRef client)
432{
433    mach_msg_empty_send_t	msg;
434    kern_return_t		status;
435
436    if (client->notify_port == MACH_PORT_NULL) {
437	return;
438    }
439    if (client->notification_sent == TRUE) {
440	/* no need to send more than a single message */
441	return;
442    }
443    msg.header.msgh_bits = MACH_MSGH_BITS(MACH_MSG_TYPE_COPY_SEND, 0);
444    msg.header.msgh_size = sizeof(msg);
445    msg.header.msgh_remote_port = client->notify_port;
446    msg.header.msgh_local_port = MACH_PORT_NULL;
447    msg.header.msgh_id = 0;
448    status = mach_msg(&msg.header,			/* msg */
449		      MACH_SEND_MSG | MACH_SEND_TIMEOUT,/* options */
450		      msg.header.msgh_size,		/* send_size */
451		      0,				/* rcv_size */
452		      MACH_PORT_NULL,			/* rcv_name */
453		      0,				/* timeout */
454		      MACH_PORT_NULL);			/* notify */
455    if (status != KERN_SUCCESS) {
456	EAPLOG_FL(LOG_NOTICE,  "mach_msg(%s) failed: %s",
457		  client->if_name, mach_error_string(status));
458    }
459    client->notification_sent = TRUE;
460    return;
461}
462
463static CFStringRef
464eapolClientNotificationKey(eapolClientRef client)
465{
466    if (client->notification_key == NULL) {
467	client->notification_key
468	    = SCDynamicStoreKeyCreateNetworkInterfaceEntity(NULL,
469							    kSCDynamicStoreDomainState,
470							    client->if_name_cf,
471							    kSCEntNetEAPOL);
472    }
473    return (client->notification_key);
474}
475
476static void
477eapolClientSetState(eapolClientRef client, EAPOLControlState state)
478{
479    client->state = state;
480    if (S_store == NULL) {
481	return;
482    }
483    SCDynamicStoreNotifyValue(S_store, eapolClientNotificationKey(client));
484    return;
485}
486
487static void
488eapolClientPublishStatus(eapolClientRef client, CFDictionaryRef status_dict)
489
490{
491    CFRetain(status_dict);
492    my_CFRelease(&client->status_dict);
493    client->status_dict = status_dict;
494    if (S_store != NULL) {
495	SCDynamicStoreNotifyValue(S_store, eapolClientNotificationKey(client));
496    }
497    return;
498}
499
500static CFStringRef
501eapolClientForceRenewKey(eapolClientRef client)
502{
503    if (client->force_renew_key == NULL) {
504	client->force_renew_key
505	    = SCDynamicStoreKeyCreateNetworkInterfaceEntity(NULL,
506							    kSCDynamicStoreDomainState,
507							    client->if_name_cf,
508							    kSCEntNetRefreshConfiguration);
509    }
510    return (client->force_renew_key);
511}
512
513static void
514eapolClientForceRenew(eapolClientRef client)
515
516{
517    if (S_store == NULL) {
518	return;
519    }
520    SCDynamicStoreNotifyValue(S_store, eapolClientForceRenewKey(client));
521    return;
522}
523
524
525#if TARGET_OS_EMBEDDED
526static void
527eapolClientExited(eapolClientRef client, EAPOLControlMode mode)
528{
529    return;
530}
531#else /* TARGET_OS_EMBEDDED */
532
533/**
534 ** 802.1X socket monitoring routines
535 **/
536
537#include "EAP.h"
538#include "EAPOLUtil.h"
539
540#define ALIGNED_BUF(name, size, type)	type 	name[(size) / (sizeof(type))]
541
542static boolean_t
543S_if_get_link_active(const char * if_name)
544{
545    boolean_t	link_active = TRUE;
546    int		s;
547
548    s = socket(AF_INET, SOCK_DGRAM, 0);
549    if (s < 0) {
550	EAPLOG(LOG_NOTICE, "EAPOLController: get link status, socket failed %s",
551	       strerror(errno));
552    }
553    else {
554	struct ifmediareq	ifmr;
555
556	memset(&ifmr, 0, sizeof(ifmr));
557	strlcpy(ifmr.ifm_name, if_name, sizeof(ifmr.ifm_name));
558	if (ioctl(s, SIOCGIFMEDIA, (caddr_t)&ifmr) != -1
559	    && ifmr.ifm_count > 0
560	    && (ifmr.ifm_status & IFM_AVALID) != 0
561	    && (ifmr.ifm_status & IFM_ACTIVE) == 0) {
562	    link_active = FALSE;
563	}
564	close(s);
565    }
566    return (link_active);
567}
568
569static void
570handle_config_changed(boolean_t start_system_mode);
571
572#define RECV_SIZE	1600
573
574static void
575monitoring_callback(CFSocketRef s, CFSocketCallBackType type,
576		    CFDataRef address, const void * data, void * info)
577{
578    ALIGNED_BUF(buf, RECV_SIZE, uint32_t);
579    eapolClientRef		client = (eapolClientRef)info;
580    struct ether_header *	eh_p = (struct ether_header *)buf;
581    EAPOLPacketRef		eapol_p;
582    int				n;
583    EAPRequestPacketRef 	req_p;
584
585    n = recv(client->eapol_fd, buf, sizeof(buf), 0);
586    if (n < sizeof(*eh_p)) {
587	if (n < 0) {
588	    EAPLOG(LOG_NOTICE, "EAPOLController: monitor %s recv failed %s",
589		   client->if_name,
590		   strerror(errno));
591	}
592	return;
593    }
594    eapol_p = (EAPOLPacketRef)(eh_p + 1);
595    if (EAPOLPacketValid(eapol_p, n - sizeof(*eh_p), NULL) == FALSE) {
596	/* bad packet */
597	return;
598    }
599    req_p = (EAPRequestPacketRef)eapol_p->body;
600    if (eapol_p->packet_type != kEAPOLPacketTypeEAPPacket
601	|| req_p->code != kEAPCodeRequest
602	|| req_p->type != kEAPTypeIdentity) {
603	/* only EAP Request Identity packets can trigger */
604	return;
605    }
606
607    bcopy(eh_p->ether_shost, &client->authenticator_mac,
608	  sizeof(client->authenticator_mac));
609    client->packet_received = TRUE;
610    EAPLOG(LOG_DEBUG, "EAPOLController: %s requires 802.1X", client->if_name);
611    if (S_store != NULL) {
612	SCDynamicStoreNotifyValue(S_store,
613				  kEAPOLControlAutoDetectInformationNotifyKey);
614    }
615    return;
616}
617
618static void
619eapolClientStopMonitoring(eapolClientRef client)
620{
621    if (client->eapol_fd == -1) {
622	return;
623    }
624    if (client->eapol_sock != NULL) {
625	/* remove one socket reference, close the file descriptor */
626	CFSocketInvalidate(client->eapol_sock);
627
628	/* release the socket */
629	my_CFRelease(&client->eapol_sock);
630    }
631    else {
632	close(client->eapol_fd);
633    }
634    client->eapol_fd = -1;
635    client->packet_received = FALSE;
636    EAPLOG(LOG_DEBUG, "EAPOLController: no longer monitoring %s",
637	   client->if_name);
638    return;
639}
640
641static void
642eapolClientStartMonitoring(eapolClientRef client)
643{
644    CFSocketContext	context = { 0, NULL, NULL, NULL, NULL };
645    CFRunLoopSourceRef	rls;
646
647    if (client->eapol_fd != -1) {
648	EAPLOG(LOG_DEBUG, "EAPOLController: already monitoring %s",
649	       client->if_name);
650	return;
651    }
652    EAPLOG(LOG_DEBUG,
653	   "EAPOLController: starting monitoring on %s", client->if_name);
654    client->eapol_fd = eapol_socket(client->if_name, FALSE);
655    if (client->eapol_fd < 0) {
656	EAPLOG(LOG_NOTICE,
657	       "EAPOLController: failed to open EAPOL socket over %s",
658	       client->if_name);
659	return;
660    }
661
662    /* arrange to be called back when socket has data */
663    context.info = client;
664    client->eapol_sock
665	= CFSocketCreateWithNative(NULL, client->eapol_fd,
666				   kCFSocketReadCallBack,
667				   monitoring_callback, &context);
668    if (client->eapol_sock == NULL) {
669	EAPLOG(LOG_NOTICE,
670	       "EAPOLController: failed create CFSocket over %s",
671	       client->if_name);
672	goto failed;
673    }
674    rls = CFSocketCreateRunLoopSource(NULL, client->eapol_sock, 0);
675    if (rls == NULL) {
676	EAPLOG(LOG_NOTICE,
677	       "EAPOLController: failed create CFRunLoopSource for %s",
678	       client->if_name);
679	goto failed;
680    }
681    CFRunLoopAddSource(CFRunLoopGetCurrent(), rls, kCFRunLoopDefaultMode);
682    CFRelease(rls);
683    return;
684
685 failed:
686    eapolClientStopMonitoring(client);
687    return;
688}
689
690static void
691eapolClientExited(eapolClientRef client, EAPOLControlMode mode)
692{
693    boolean_t		start_system_mode = FALSE;
694    CFStringRef		user;
695
696    if (S_store == NULL) {
697	return;
698    }
699    user = SCDynamicStoreCopyConsoleUser(S_store, NULL, NULL);
700    if (user == NULL) {
701	if (mode != kEAPOLControlModeSystem) {
702	    start_system_mode = TRUE;
703	}
704    }
705    else {
706	CFRelease(user);
707    }
708    handle_config_changed(start_system_mode);
709    return;
710}
711
712#endif /* TARGET_OS_EMBEDDED */
713
714/**
715 ** fork/exec eapolclient routines
716 **/
717static void
718exec_callback(pid_t pid, int status, struct rusage * rusage, void * context)
719{
720    eapolClientRef	client;
721    EAPOLControlMode	mode;
722
723    client = eapolClientLookupProcess(pid);
724    if (client == NULL) {
725	return;
726    }
727    if (client->state != kEAPOLControlStateIdle) {
728	EAPLOG(LOG_NOTICE,
729	       "EAPOLController: eapolclient(%s) pid=%d exited with status %d",
730	       client->if_name,  pid, status);
731    }
732    mode = client->mode;
733    eapolClientInvalidate(client);
734    eapolClientExited(client, mode);
735    return;
736}
737
738typedef struct {
739    int			eapol_fd;
740    eapolClientRef	client;
741} exec_context_t;
742
743static void
744exec_setup(pid_t pid, void * context)
745{
746    int			fd;
747    int 		i;
748    exec_context_t *	ec_p = (exec_context_t *)context;
749
750    if (pid != 0) {
751	/* parent: clean up file descriptors */
752#if TARGET_OS_EMBEDDED
753	close(ec_p->eapol_fd);
754#else /* TARGET_OS_EMBEDDED */
755	if (ec_p->client->eapol_fd == ec_p->eapol_fd) {
756	    eapolClientStopMonitoring(ec_p->client);
757	}
758	else {
759	    close(ec_p->eapol_fd);
760	}
761#endif /* TARGET_OS_EMBEDDED */
762	return;
763    }
764
765    /* child: close all fds except the ones we inherit from parent */
766    for (i = (getdtablesize() - 1); i >= 0; i--) {
767	if (i != ec_p->eapol_fd) {
768	    close(i);
769	}
770    }
771
772    /* re-direct stdin to the inherited eapol_fd */
773    if (ec_p->eapol_fd != STDIN_FILENO) {
774	dup(ec_p->eapol_fd);			/* stdin */
775	close(ec_p->eapol_fd);
776    }
777
778    /* re-direct stdout/stderr to /dev/null */
779    fd = open(_PATH_DEVNULL, O_RDWR, 0);	/* stdout */
780    dup(fd);					/* stderr */
781    return;
782}
783
784static int
785open_eapol_socket(eapolClientRef client)
786{
787#if ! TARGET_OS_EMBEDDED
788    if (client->eapol_fd != -1) {
789	return (client->eapol_fd);
790    }
791#endif /* ! TARGET_OS_EMBEDDED */
792    return (eapol_socket(client->if_name,
793			 (get_ifm_type(client->if_name)
794			  == IFM_IEEE80211)));
795}
796
797static int
798eapolClientStart(eapolClientRef client, uid_t uid, gid_t gid,
799		 CFDictionaryRef config_dict, mach_port_t bootstrap,
800		 mach_port_t au_session)
801{
802    char * 			argv[] = { S_eapolclient_path, 	/* 0 */
803					   "-i",	       	/* 1 */
804					   client->if_name,	/* 2 */
805					   NULL,		/* 3 */
806					   NULL,		/* 4 */
807					   NULL,		/* 5 */
808					   NULL,		/* 6 */
809					   NULL,		/* 7 */
810					   NULL };
811    exec_context_t	ec;
812    char			gid_str[32];
813    int				status = 0;
814    char			uid_str[32];
815
816#if ! TARGET_OS_EMBEDDED
817    client->user_cancelled = FALSE;
818#endif /* ! TARGET_OS_EMBEDDED */
819
820    bzero(&ec, sizeof(ec));
821    ec.client = client;
822    ec.eapol_fd = open_eapol_socket(client);
823    if (ec.eapol_fd < 0) {
824	EAPLOG(LOG_NOTICE,
825	       "EAPOLController: failed to open EAPOL socket over %s",
826	       client->if_name);
827	return (errno);
828    }
829    if (bootstrap != MACH_PORT_NULL) {
830	snprintf(uid_str, sizeof(uid_str), "%u", uid);
831	snprintf(gid_str, sizeof(gid_str), "%u", gid);
832	argv[3] = "-u";
833	argv[4] = uid_str;
834	argv[5] = "-g";
835	argv[6] = gid_str;
836    }
837    client->pid = _SCDPluginExecCommand2(exec_callback, NULL, 0, 0,
838					 S_eapolclient_path, argv,
839					 exec_setup, &ec);
840    if (client->pid == -1) {
841	/* failure, clean-up too */
842	exec_setup(-1, &ec);
843	status = errno;
844    }
845    else {
846	boolean_t			on_console = FALSE;
847
848	if (bootstrap != MACH_PORT_NULL) {
849	    on_console = is_console_user(uid);
850	    client->owner.uid = uid;
851	    client->owner.gid = gid;
852	    client->console_user = on_console;
853#if TARGET_OS_EMBEDDED
854	    client->mode = kEAPOLControlModeUser;
855	    client->bootstrap = bootstrap;
856#else /* TARGET_OS_EMBEDDED */
857	    if (on_console == FALSE
858		&& uid == login_window_uid()) {
859		client->mode = kEAPOLControlModeLoginWindow;
860	    }
861	    else {
862		client->mode = kEAPOLControlModeUser;
863		client->bootstrap = bootstrap;
864		client->au_session = au_session;
865	    }
866#endif /* TARGET_OS_EMBEDDED */
867	}
868	else {
869	    client->mode = kEAPOLControlModeSystem;
870	}
871	if (config_dict != NULL) {
872	    client->config_dict = CFRetain(config_dict);
873	}
874	eapolClientSetState(client, kEAPOLControlStateStarting);
875    }
876    return (status);
877}
878
879static int
880eapolClientUpdate(eapolClientRef client, CFDictionaryRef config_dict)
881{
882    int			status = 0;
883
884    switch (client->state) {
885    case kEAPOLControlStateStarting:
886    case kEAPOLControlStateRunning:
887	break;
888    case kEAPOLControlStateIdle:
889	status = ENOENT;
890	goto done;
891    default:
892	status = EBUSY;
893	goto done;
894    }
895
896    if (client->state == kEAPOLControlStateRunning) {
897	/* tell the client to re-read */
898	eapolClientNotify(client);
899    }
900    my_CFRelease(&client->config_dict);
901    my_CFRelease(&client->user_input_dict);
902    client->config_dict = CFRetain(config_dict);
903    client->retry = FALSE;
904    client->user_input_provided = FALSE;
905
906 done:
907    return (status);
908}
909
910static int
911eapolClientProvideUserInput(eapolClientRef client, CFDictionaryRef user_input)
912{
913    int			status = 0;
914
915    switch (client->state) {
916    case kEAPOLControlStateRunning:
917	break;
918    case kEAPOLControlStateStarting:
919	status = EINVAL;
920	goto done;
921    case kEAPOLControlStateIdle:
922	status = ENOENT;
923	goto done;
924    default:
925	status = EBUSY;
926	goto done;
927    }
928
929    /* tell the client to re-read */
930    eapolClientNotify(client);
931    my_CFRelease(&client->user_input_dict);
932    if (user_input != NULL) {
933	client->user_input_dict = CFRetain(user_input);
934    }
935    client->retry = FALSE;
936    client->user_input_provided = TRUE;
937
938 done:
939    return (status);
940}
941
942int
943ControllerGetState(if_name_t if_name, int * state)
944{
945    eapolClientRef 	client;
946    int			status = 0;
947
948    *state = kEAPOLControlStateIdle;
949    client = eapolClientLookupInterface(if_name);
950    if (client == NULL) {
951	status = ENOENT;
952    }
953    else {
954	*state = client->state;
955    }
956    return (status);
957}
958
959int
960ControllerCopyStateAndStatus(if_name_t if_name,
961			     int * state,
962			     CFDictionaryRef * status_dict)
963{
964    eapolClientRef 	client;
965    int			status = 0;
966
967    *state = kEAPOLControlStateIdle;
968    *status_dict = NULL;
969    client = eapolClientLookupInterface(if_name);
970    if (client == NULL) {
971	status = ENOENT;
972    }
973    else {
974	if (client->status_dict != NULL) {
975	    *status_dict = CFRetain(client->status_dict);
976	}
977	*state = client->state;
978    }
979    return (status);
980}
981
982int
983ControllerStart(if_name_t if_name, uid_t uid, gid_t gid,
984		CFDictionaryRef config_dict, mach_port_t bootstrap,
985		mach_port_t au_session)
986{
987    eapolClientRef 	client;
988    int			status = 0;
989
990    client = eapolClientLookupInterface(if_name);
991    if (client != NULL) {
992	if (client->state != kEAPOLControlStateIdle) {
993	    if (client->state == kEAPOLControlStateRunning) {
994		status = EEXIST;
995	    }
996	    else {
997		status = EBUSY;
998	    }
999	    goto done;
1000	}
1001    }
1002    else {
1003	int	ifm_type;
1004
1005	/* make sure that the interface is one that we support */
1006	ifm_type = get_ifm_type(if_name);
1007	switch (ifm_type) {
1008	case IFM_ETHER:
1009	case IFM_IEEE80211:
1010	    break;
1011	default:
1012	    status = ENXIO;
1013	    goto done;
1014	}
1015	client = eapolClientAdd(if_name);
1016	if (client == NULL) {
1017	    status = ENOMEM;
1018	    goto done;
1019	}
1020    }
1021#if TARGET_OS_EMBEDDED
1022    /* automatically map all requests by root to the mobile user */
1023    if (uid == 0) {
1024	static gid_t	mobile_gid = -1;
1025	static uid_t	mobile_uid = -1;
1026
1027	if (mobile_uid == -1) {
1028	    struct passwd *	pwd;
1029
1030	    /* lookup the mobile user */
1031	    pwd = getpwnam("mobile");
1032	    if (pwd != NULL) {
1033		mobile_uid = pwd->pw_uid;
1034		mobile_gid = pwd->pw_gid;
1035	    }
1036	    else {
1037		EAPLOG(LOG_NOTICE,
1038		       "EAPOLController: getpwnam(mobile) failed");
1039	    }
1040	}
1041	if (mobile_uid != -1) {
1042	    uid = mobile_uid;
1043	    if (mobile_gid != -1) {
1044		gid = mobile_gid;
1045	    }
1046	}
1047    }
1048#endif /* TARGET_OS_EMBEDDED */
1049    status = eapolClientStart(client, uid, gid, config_dict, bootstrap,
1050			      au_session);
1051 done:
1052    if (status != 0) {
1053	if (bootstrap != MACH_PORT_NULL) {
1054	    (void)mach_port_deallocate(mach_task_self(), bootstrap);
1055	}
1056	if (au_session != MACH_PORT_NULL) {
1057	    (void)mach_port_deallocate(mach_task_self(), au_session);
1058	}
1059    }
1060    return (status);
1061}
1062
1063static int
1064eapolClientStop(eapolClientRef client)
1065{
1066    int			status = 0;
1067
1068    switch (client->state) {
1069    case kEAPOLControlStateRunning:
1070    case kEAPOLControlStateStarting:
1071	break;
1072    case kEAPOLControlStateIdle:
1073	status = ENOENT;
1074	goto done;
1075    default:
1076	status = EBUSY;
1077	goto done;
1078    }
1079    eapolClientSetState(client, kEAPOLControlStateStopping);
1080    my_CFRelease(&client->config_dict);
1081
1082    /* send a message to stop it */
1083    eapolClientNotify(client);
1084    /* should set a timeout so that if it doesn't detach, we kill it XXX */
1085#if 0
1086    if (client->pid != -1 && client->pid != 0) {
1087	kill(client->pid, SIGTERM);
1088    }
1089#endif /* 0 */
1090
1091 done:
1092    return (status);
1093
1094}
1095
1096#if ! TARGET_OS_EMBEDDED
1097static boolean_t
1098S_get_plist_boolean(CFDictionaryRef plist, CFStringRef key, boolean_t def)
1099{
1100    CFBooleanRef 	b;
1101    boolean_t		ret = def;
1102
1103    b = isA_CFBoolean(CFDictionaryGetValue(plist, key));
1104    if (b != NULL) {
1105	ret = CFBooleanGetValue(b);
1106    }
1107    return (ret);
1108}
1109#endif /* ! TARGET_OS_EMBEDDED */
1110
1111int
1112ControllerStop(if_name_t if_name, uid_t uid, gid_t gid)
1113{
1114    eapolClientRef 	client;
1115    int			status = 0;
1116
1117    client = eapolClientLookupInterface(if_name);
1118    if (client == NULL) {
1119	status = ENOENT;
1120	goto done;
1121    }
1122#if TARGET_OS_EMBEDDED
1123    if (uid != 0 && uid != client->owner.uid) {
1124	status = EPERM;
1125	goto done;
1126    }
1127#else /* TARGET_OS_EMBEDDED */
1128    if (uid != 0) {
1129	if (uid != client->owner.uid) {
1130	    if (client->mode == kEAPOLControlModeSystem
1131		&& (client->config_dict == NULL
1132		    || S_get_plist_boolean(client->config_dict,
1133					   CFSTR("AllowStop"), TRUE))
1134		&& (uid == login_window_uid() || is_console_user(uid))) {
1135		/* allow the change */
1136	    }
1137	    else {
1138		status = EPERM;
1139		goto done;
1140	    }
1141	}
1142	else if (client->mode == kEAPOLControlModeLoginWindow
1143		 && uid == login_window_uid()) {
1144	    /* LoginWindow mode is being turned off, clear the config */
1145	    clear_loginwindow_config(client);
1146	}
1147    }
1148
1149#endif /* TARGET_OS_EMBEDDED */
1150    status = eapolClientStop(client);
1151 done:
1152    return (status);
1153}
1154
1155
1156int
1157ControllerUpdate(if_name_t if_name, uid_t uid, gid_t gid,
1158		 CFDictionaryRef config_dict)
1159{
1160    eapolClientRef 	client;
1161    int			status = 0;
1162
1163    client = eapolClientLookupInterface(if_name);
1164    if (client == NULL) {
1165	status = ENOENT;
1166	goto done;
1167    }
1168    if (uid != 0 && uid != client->owner.uid) {
1169	status = EPERM;
1170	goto done;
1171    }
1172    status = eapolClientUpdate(client, config_dict);
1173 done:
1174    return (status);
1175}
1176
1177int
1178ControllerProvideUserInput(if_name_t if_name, uid_t uid, gid_t gid,
1179			   CFDictionaryRef user_input)
1180{
1181    eapolClientRef 	client;
1182    int			status = 0;
1183
1184    client = eapolClientLookupInterface(if_name);
1185    if (client == NULL) {
1186	status = ENOENT;
1187	goto done;
1188    }
1189    if (uid != 0 && uid != client->owner.uid) {
1190	status = EPERM;
1191	goto done;
1192    }
1193    status = eapolClientProvideUserInput(client, user_input);
1194 done:
1195    return (status);
1196}
1197
1198int
1199ControllerRetry(if_name_t if_name, uid_t uid, gid_t gid)
1200{
1201    eapolClientRef 	client;
1202    int			status = 0;
1203
1204    client = eapolClientLookupInterface(if_name);
1205    if (client == NULL) {
1206	status = ENOENT;
1207	goto done;
1208    }
1209    if (uid != 0 && uid != client->owner.uid) {
1210	status = EPERM;
1211	goto done;
1212    }
1213    switch (client->state) {
1214    case kEAPOLControlStateStarting:
1215	goto done;
1216    case kEAPOLControlStateRunning:
1217	break;
1218    case kEAPOLControlStateIdle:
1219	status = ENOENT;
1220	goto done;
1221    default:
1222	status = EBUSY;
1223	goto done;
1224    }
1225    /* tell the client to re-read */
1226    eapolClientNotify(client);
1227    client->retry = TRUE;
1228
1229 done:
1230    return (status);
1231
1232}
1233
1234#if ! TARGET_OS_EMBEDDED
1235
1236static CFDictionaryRef
1237system_eapol_copy(const char * if_name)
1238{
1239    CFDictionaryRef		dict = NULL;
1240    EAPOLClientConfigurationRef	cfg;
1241
1242    cfg = EAPOLClientConfigurationCreate(NULL);
1243    if (cfg != NULL) {
1244	CFStringRef		if_name_cf;
1245	EAPOLClientProfileRef	profile = NULL;
1246
1247	if_name_cf = CFStringCreateWithCString(NULL, if_name,
1248					       kCFStringEncodingASCII);
1249	profile = EAPOLClientConfigurationGetSystemProfile(cfg, if_name_cf);
1250	CFRelease(if_name_cf);
1251	if (profile != NULL) {
1252	    /* new, profileID-based configuration */
1253	    dict = S_profile_copy_itemID_dict(profile);
1254	}
1255	CFRelease(cfg);
1256    }
1257    return (dict);
1258}
1259
1260int
1261ControllerStartSystem(if_name_t if_name, uid_t uid, gid_t gid,
1262		      CFDictionaryRef options)
1263{
1264    eapolClientRef 	client;
1265    CFDictionaryRef	dict = NULL;
1266    CFDictionaryRef	itemID_dict = NULL;
1267    int			status = 0;
1268
1269    /* make sure that 802.1X isn't already running on the interface */
1270    client = eapolClientLookupInterface(if_name);
1271    if (client != NULL && client->state != kEAPOLControlStateIdle) {
1272	if (client->state == kEAPOLControlStateRunning) {
1273	    status = EEXIST;
1274	}
1275	else {
1276	    status = EBUSY;
1277	}
1278	goto done;
1279    }
1280
1281    /* verify that non-root caller is either loginwindow or a logged-in user */
1282    if (uid != 0
1283	&& uid != login_window_uid()
1284	&& is_console_user(uid) == FALSE) {
1285	status = EPERM;
1286	goto done;
1287    }
1288
1289    /* check whether the caller provided which profile to use */
1290    if (options != NULL) {
1291	itemID_dict
1292	    = CFDictionaryGetValue(options, kEAPOLControlClientItemID);
1293	if (isA_CFDictionary(itemID_dict) != NULL) {
1294	    CFMutableDictionaryRef	new_dict;
1295
1296	    new_dict = CFDictionaryCreateMutableCopy(NULL, 0, options);
1297	    CFDictionarySetValue(new_dict, kSystemModeManagedExternally,
1298				 kCFBooleanTrue);
1299	    dict = new_dict;
1300	}
1301    }
1302
1303    /* check whether system mode is configured in the preferences */
1304    if (dict == NULL) {
1305	dict = system_eapol_copy(if_name);
1306	if (dict == NULL) {
1307	    status = ESRCH;
1308	    goto done;
1309	}
1310    }
1311
1312    /* if there's no client entry yet, create it */
1313    if (client == NULL) {
1314	client = eapolClientAdd(if_name);
1315	if (client == NULL) {
1316	    status = ENOMEM;
1317	    goto done;
1318	}
1319    }
1320    /* start system mode over the specific interface */
1321    status = eapolClientStart(client, 0, 0, dict, MACH_PORT_NULL,
1322			      MACH_PORT_NULL);
1323
1324 done:
1325    my_CFRelease(&dict);
1326    return (status);
1327}
1328
1329int
1330ControllerCopyLoginWindowConfiguration(if_name_t if_name,
1331				       CFDictionaryRef * config_data_p)
1332{
1333    eapolClientRef 	client;
1334    int			status = 0;
1335
1336    *config_data_p = NULL;
1337    client = eapolClientLookupInterface(if_name);
1338    if (client == NULL
1339	|| client->loginwindow_config == NULL) {
1340	status = ENOENT;
1341    }
1342    else {
1343	*config_data_p = CFRetain(client->loginwindow_config);
1344    }
1345    return (status);
1346}
1347
1348int
1349ControllerCopyAutoDetectInformation(CFDictionaryRef * info_p)
1350{
1351    CFMutableDictionaryRef	all_dict = NULL;
1352    eapolClientRef		scan;
1353    int				status = 0;
1354
1355    LIST_FOREACH(scan, S_clientHead_p, link) {
1356	CFDataRef		data;
1357	CFMutableDictionaryRef	this_dict = NULL;
1358
1359	if (scan->state != kEAPOLControlStateIdle
1360	    || scan->eapol_fd == -1
1361	    || scan->packet_received == FALSE) {
1362	    continue;
1363	}
1364	this_dict = CFDictionaryCreateMutable(NULL, 0,
1365					      &kCFTypeDictionaryKeyCallBacks,
1366					      &kCFTypeDictionaryValueCallBacks);
1367	data = CFDataCreate(NULL, (const UInt8 *)&scan->authenticator_mac,
1368			    sizeof(scan->authenticator_mac));
1369	CFDictionarySetValue(this_dict, kEAPOLAutoDetectAuthenticatorMACAddress,
1370			     data);
1371	CFRelease(data);
1372	if (all_dict == NULL) {
1373	    all_dict = CFDictionaryCreateMutable(NULL, 0,
1374					     &kCFTypeDictionaryKeyCallBacks,
1375					     &kCFTypeDictionaryValueCallBacks);
1376	}
1377	CFDictionarySetValue(all_dict, scan->if_name_cf, this_dict);
1378	CFRelease(this_dict);
1379    }
1380    if (all_dict != NULL && CFDictionaryGetCount(all_dict) == 0) {
1381	status = ENOENT;
1382	my_CFRelease(&all_dict);
1383    }
1384    *info_p = all_dict;
1385    return (status);
1386}
1387
1388boolean_t
1389ControllerDidUserCancel(if_name_t if_name)
1390{
1391    boolean_t		cancelled = FALSE;
1392    eapolClientRef 	client;
1393
1394    client = eapolClientLookupInterface(if_name);
1395    if (client == NULL) {
1396	cancelled = FALSE;
1397    }
1398    else {
1399	cancelled = client->user_cancelled;
1400    }
1401    return (cancelled);
1402}
1403
1404#endif /* ! TARGET_OS_EMBEDDED */
1405
1406int
1407ControllerClientAttach(pid_t pid, if_name_t if_name,
1408		       mach_port_t notify_port,
1409		       mach_port_t * session_port,
1410		       CFDictionaryRef * control_dict,
1411		       mach_port_t * bootstrap,
1412		       mach_port_t * au_session)
1413{
1414    CFMutableDictionaryRef	dict;
1415    CFNumberRef			command_cf;
1416    eapolClientRef		client;
1417    int				result = 0;
1418    CFRunLoopSourceRef		rls;
1419
1420    *session_port = MACH_PORT_NULL;
1421    *control_dict = NULL;
1422    client = eapolClientLookupInterface(if_name);
1423    if (client == NULL) {
1424	result = ENOENT;
1425	goto failed;
1426    }
1427    if (pid != client->pid) {
1428	result = EPERM;
1429	goto failed;
1430    }
1431    if (client->session_cfport != NULL) {
1432	result = EEXIST;
1433	goto failed;
1434    }
1435    client->notify_port = notify_port;
1436    client->session_cfport
1437	= CFMachPortCreate(NULL, server_handle_request, NULL, NULL);
1438    *session_port = CFMachPortGetPort(client->session_cfport);
1439    rls = CFMachPortCreateRunLoopSource(NULL, client->session_cfport, 0);
1440    CFRunLoopAddSource(CFRunLoopGetCurrent(), rls, kCFRunLoopDefaultMode);
1441    CFRelease(rls);
1442    dict = CFDictionaryCreateMutable(NULL, 0,
1443				     &kCFTypeDictionaryKeyCallBacks,
1444				     &kCFTypeDictionaryValueCallBacks);
1445    if (client->state == kEAPOLControlStateStarting) {
1446	CFNumberRef			mode_cf;
1447
1448	command_cf = make_number(kEAPOLClientControlCommandRun);
1449	if (client->config_dict != NULL) {
1450	    CFDictionarySetValue(dict, kEAPOLClientControlConfiguration,
1451				 client->config_dict);
1452	}
1453	mode_cf = make_number(client->mode);
1454	CFDictionarySetValue(dict, kEAPOLClientControlMode,
1455			     mode_cf);
1456	CFRelease(mode_cf);
1457    }
1458    else {
1459	command_cf = make_number(kEAPOLClientControlCommandStop);
1460    }
1461    CFDictionarySetValue(dict, kEAPOLClientControlCommand, command_cf);
1462    CFRelease(command_cf);
1463    *control_dict = dict;
1464    eapolClientSetState(client, kEAPOLControlStateRunning);
1465    *bootstrap = client->bootstrap;
1466    *au_session = client->au_session;
1467#if ! TARGET_OS_EMBEDDED
1468    if (client->mode == kEAPOLControlModeLoginWindow) {
1469	set_loginwindow_config(client);
1470    }
1471#endif /* ! TARGET_OS_EMBEDDED */
1472    return (result);
1473 failed:
1474    (void)mach_port_deallocate(mach_task_self(), notify_port);
1475    return (result);
1476
1477}
1478
1479int
1480ControllerClientDetach(mach_port_t session_port)
1481{
1482    eapolClientRef	client;
1483    EAPOLControlMode	mode;
1484    int			result = 0;
1485
1486    client = eapolClientLookupSession(session_port);
1487    if (client == NULL) {
1488	result = EINVAL;
1489	goto failed;
1490    }
1491    mode = client->mode;
1492    eapolClientInvalidate(client);
1493    eapolClientExited(client, mode);
1494
1495 failed:
1496    return (result);
1497}
1498
1499int
1500ControllerClientGetConfig(mach_port_t session_port,
1501			  CFDictionaryRef * control_dict)
1502{
1503    eapolClientRef		client;
1504    CFNumberRef			command_cf = NULL;
1505    CFMutableDictionaryRef	dict = NULL;
1506    int				result = 0;
1507
1508    *control_dict = NULL;
1509    client = eapolClientLookupSession(session_port);
1510    if (client == NULL) {
1511	result = EINVAL;
1512	goto failed;
1513    }
1514    dict = CFDictionaryCreateMutable(NULL, 0,
1515				     &kCFTypeDictionaryKeyCallBacks,
1516				     &kCFTypeDictionaryValueCallBacks);
1517    if (client->state == kEAPOLControlStateRunning) {
1518	if (client->retry) {
1519	    command_cf = make_number(kEAPOLClientControlCommandRetry);
1520	    client->retry = FALSE;
1521	}
1522	else if (client->user_input_provided) {
1523	    command_cf
1524		= make_number(kEAPOLClientControlCommandTakeUserInput);
1525	    if (client->user_input_dict != NULL) {
1526		CFDictionarySetValue(dict,
1527				     kEAPOLClientControlUserInput,
1528				     client->user_input_dict);
1529	    }
1530	    client->user_input_provided = FALSE;
1531	    my_CFRelease(&client->user_input_dict);
1532	}
1533	else {
1534	    command_cf = make_number(kEAPOLClientControlCommandRun);
1535	    if (client->config_dict != NULL) {
1536		CFDictionarySetValue(dict, kEAPOLClientControlConfiguration,
1537				     client->config_dict);
1538	    }
1539	}
1540    }
1541    else {
1542	command_cf = make_number(kEAPOLClientControlCommandStop);
1543    }
1544    CFDictionarySetValue(dict, kEAPOLClientControlCommand, command_cf);
1545    *control_dict = dict;
1546    client->notification_sent = FALSE;
1547    my_CFRelease(&command_cf);
1548 failed:
1549    return (result);
1550}
1551
1552int
1553ControllerClientReportStatus(mach_port_t session_port,
1554			     CFDictionaryRef status_dict)
1555{
1556    eapolClientRef	client;
1557    int			result = 0;
1558
1559    client = eapolClientLookupSession(session_port);
1560    if (client == NULL) {
1561	result = EINVAL;
1562	goto failed;
1563    }
1564    eapolClientPublishStatus(client, status_dict);
1565 failed:
1566    return (result);
1567}
1568
1569int
1570ControllerClientForceRenew(mach_port_t session_port)
1571{
1572    eapolClientRef	client;
1573    int			result = 0;
1574
1575    client = eapolClientLookupSession(session_port);
1576    if (client == NULL) {
1577	result = EINVAL;
1578	goto failed;
1579    }
1580    (void)eapolClientForceRenew(client);
1581 failed:
1582    return (result);
1583}
1584
1585#if ! TARGET_OS_EMBEDDED
1586int
1587ControllerClientUserCancelled(mach_port_t session_port)
1588{
1589    eapolClientRef	client;
1590    int			result = 0;
1591
1592    client = eapolClientLookupSession(session_port);
1593    if (client == NULL) {
1594	result = EINVAL;
1595	goto failed;
1596    }
1597    client->user_cancelled = TRUE;
1598    result = eapolClientStop(client);
1599
1600 failed:
1601    return (result);
1602}
1603#endif /* ! TARGET_OS_EMBEDDED */
1604
1605
1606#if TARGET_OS_EMBEDDED
1607static SCDynamicStoreRef
1608dynamic_store_create(void)
1609{
1610    SCDynamicStoreRef		store;
1611
1612    store = SCDynamicStoreCreate(NULL, CFSTR("EAPOLController"), NULL, NULL);
1613    if (store == NULL) {
1614	EAPLOG(LOG_NOTICE, "EAPOLController: SCDynamicStoreCreate() failed, %s",
1615	       SCErrorString(SCError()));
1616    }
1617    return (store);
1618}
1619
1620static void *
1621ControllerThread(void * arg)
1622{
1623    server_start();
1624    CFRunLoopRun();
1625    return (arg);
1626}
1627
1628#else /* TARGET_OS_EMBEDDED */
1629
1630static void
1631console_user_changed()
1632{
1633    eapolClientRef	scan;
1634    uid_t		uid = 0;
1635    CFStringRef		user;
1636
1637    user = SCDynamicStoreCopyConsoleUser(S_store, &uid, NULL);
1638    LIST_FOREACH(scan, S_clientHead_p, link) {
1639	if (scan->console_user) {
1640	    if (user == NULL || scan->owner.uid != uid) {
1641		/* user logged out or fast-user switch */
1642		(void)eapolClientStop(scan);
1643		clear_loginwindow_config(scan);
1644	    }
1645	}
1646	else if (user == NULL) {
1647	    clear_loginwindow_config(scan);
1648	}
1649    }
1650    my_CFRelease(&user);
1651    return;
1652}
1653
1654static CFStringRef
1655mySCNetworkInterfacePathCopyInterfaceName(CFStringRef path)
1656{
1657    CFArrayRef          arr;
1658    CFStringRef         ifname = NULL;
1659
1660    arr = CFStringCreateArrayBySeparatingStrings(NULL, path, CFSTR("/"));
1661    if (arr == NULL) {
1662        goto done;
1663    }
1664    /* "domain:/Network/Interface/<ifname>[/<entity>]" =>
1665     * {"domain:","Network","Interface","<ifname>"[,"<entity>"]}
1666     */
1667    if (CFArrayGetCount(arr) < 4) {
1668        goto done;
1669    }
1670    ifname = CFRetain(CFArrayGetValueAtIndex(arr, 3));
1671 done:
1672    if (arr != NULL) {
1673        CFRelease(arr);
1674    }
1675    return (ifname);
1676}
1677
1678static CFArrayRef
1679copy_interface_list(void)
1680{
1681    CFDictionaryRef	dict;
1682    CFArrayRef		iflist = NULL;
1683    CFStringRef		key;
1684
1685    key = SCDynamicStoreKeyCreateNetworkInterface(NULL,
1686						  kSCDynamicStoreDomainState);
1687    dict = SCDynamicStoreCopyValue(S_store, key);
1688    my_CFRelease(&key);
1689
1690    if (isA_CFDictionary(dict) != NULL) {
1691	iflist = CFDictionaryGetValue(dict, kSCPropNetInterfaces);
1692	iflist = isA_CFArray(iflist);
1693    }
1694    if (iflist != NULL) {
1695	CFRetain(iflist);
1696    }
1697    else {
1698	iflist = CFArrayCreate(NULL, NULL, 0, &kCFTypeArrayCallBacks);
1699    }
1700
1701    my_CFRelease(&dict);
1702    return (iflist);
1703}
1704
1705typedef struct {
1706    EAPOLClientConfigurationRef cfg;
1707    CFMutableArrayRef		configured_iflist;
1708    CFRange			configured_iflist_range;
1709    CFMutableDictionaryRef	system_mode_configurations;
1710    CFMutableArrayRef		system_mode_iflist;
1711} EAPOLEthernetInfo, * EAPOLEthernetInfoRef;
1712
1713static void
1714EAPOLEthernetInfoProcess(const void * key, const void * value, void * context)
1715{
1716    EAPOLEthernetInfoRef	info_p = (EAPOLEthernetInfoRef)context;
1717
1718    if (isA_CFDictionary(value) == NULL) {
1719	return;
1720    }
1721    if (CFStringHasSuffix(key, kSCEntNetEAPOL)) {
1722	CFStringRef		name;
1723	EAPOLClientProfileRef	profile = NULL;
1724
1725	name = mySCNetworkInterfacePathCopyInterfaceName(key);
1726	if (info_p->cfg == NULL) {
1727	    info_p->cfg = EAPOLClientConfigurationCreate(NULL);
1728	}
1729	if (info_p->cfg != NULL) {
1730	    profile
1731		= EAPOLClientConfigurationGetSystemProfile(info_p->cfg, name);
1732	}
1733	if (profile != NULL) {
1734	    CFDictionaryRef		this_config = NULL;
1735
1736	    this_config = S_profile_copy_itemID_dict(profile);
1737	    if (this_config != NULL) {
1738		CFDictionarySetValue(info_p->system_mode_configurations,
1739				     name, this_config);
1740		CFRelease(this_config);
1741		CFArrayAppendValue(info_p->system_mode_iflist, name);
1742	    }
1743	}
1744	my_CFRelease(&name);
1745    }
1746    else {
1747	int			ifm_type = 0;
1748	char			ifname[IFNAMSIZ];
1749	CFStringRef		name;
1750	CFStringRef		type;
1751
1752	type = CFDictionaryGetValue(value, kSCPropNetInterfaceType);
1753	if (type == NULL
1754	    || CFEqual(type, kSCValNetInterfaceTypeEthernet) == FALSE) {
1755	    return;
1756	}
1757	name = CFDictionaryGetValue(value, kSCPropNetInterfaceDeviceName);
1758	if (isA_CFString(name) == NULL) {
1759	    return;
1760	}
1761	if (CFStringGetCString(name, ifname, sizeof(ifname),
1762			       kCFStringEncodingASCII) == FALSE) {
1763	    return;
1764	}
1765	if (CFArrayContainsValue(info_p->configured_iflist,
1766				 info_p->configured_iflist_range, name)) {
1767	    return;
1768	}
1769	ifm_type = get_ifm_type(ifname);
1770	if (ifm_type != IFM_ETHER) {
1771	    /* ignore non-ethernet */
1772	    return;
1773	}
1774	CFArrayAppendValue(info_p->configured_iflist, name);
1775	info_p->configured_iflist_range.length++;
1776    }
1777    return;
1778}
1779
1780static void
1781EAPOLEthernetInfoInit(EAPOLEthernetInfoRef info_p, boolean_t system_mode)
1782{
1783    int					count;
1784    int					i;
1785    CFStringRef				list[2];
1786    CFArrayRef				patterns;
1787    CFDictionaryRef			store_info = NULL;
1788
1789    bzero(info_p, sizeof(*info_p));
1790    count = 0;
1791    list[count++]
1792	= SCDynamicStoreKeyCreateNetworkServiceEntity(NULL,
1793						      kSCDynamicStoreDomainSetup,
1794						      kSCCompAnyRegex,
1795						      kSCEntNetInterface);
1796    if (system_mode) {
1797	list[count++] = SCDynamicStoreKeyCreateNetworkInterfaceEntity(NULL,
1798								      kSCDynamicStoreDomainSetup,
1799								      kSCCompAnyRegex,
1800								      kSCEntNetEAPOL);
1801    }
1802    patterns = CFArrayCreate(NULL, (const void * *)list, count,
1803			     &kCFTypeArrayCallBacks);
1804    for (i = 0; i < count; i++) {
1805	CFRelease(list[i]);
1806    }
1807    store_info = SCDynamicStoreCopyMultiple(S_store, NULL, patterns);
1808    my_CFRelease(&patterns);
1809    if (store_info == NULL) {
1810	return;
1811    }
1812
1813    /* build list of configured services and System mode configurations */
1814    info_p->configured_iflist
1815	= CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
1816    if (system_mode) {
1817	info_p->system_mode_configurations
1818	    = CFDictionaryCreateMutable(NULL, 0,
1819					&kCFTypeDictionaryKeyCallBacks,
1820					&kCFTypeDictionaryValueCallBacks);
1821	info_p->system_mode_iflist
1822	    = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
1823    }
1824    CFDictionaryApplyFunction(store_info, EAPOLEthernetInfoProcess,
1825			      (void *)info_p);
1826    CFRelease(store_info);
1827    return;
1828}
1829
1830static void
1831EAPOLEthernetInfoFree(EAPOLEthernetInfoRef info_p)
1832{
1833    my_CFRelease(&info_p->cfg);
1834    my_CFRelease(&info_p->configured_iflist);
1835    my_CFRelease(&info_p->system_mode_configurations);
1836    my_CFRelease(&info_p->system_mode_iflist);
1837    return;
1838}
1839
1840static void
1841update_system_mode_interfaces(CFDictionaryRef system_mode_configurations,
1842			      CFArrayRef system_mode_iflist)
1843{
1844    CFArrayRef				current_iflist = NULL;
1845    int					i;
1846    CFRange				range;
1847    eapolClientRef			scan;
1848    int					status;
1849
1850    /* get the current list of interfaces */
1851    current_iflist = copy_interface_list();
1852    range = CFRangeMake(0, CFArrayGetCount(current_iflist));
1853
1854    /* change existing interface configurations */
1855    LIST_FOREACH(scan, S_clientHead_p, link) {
1856	CFDictionaryRef		this_config = NULL;
1857
1858	if (CFArrayContainsValue(current_iflist,
1859				 range,
1860				 scan->if_name_cf) == FALSE) {
1861	    /* interface doesn't exist, stop it */
1862	    if (scan->state == kEAPOLControlStateIdle) {
1863		eapolClientStopMonitoring(scan);
1864	    }
1865	    else {
1866		(void)eapolClientStop(scan);
1867	    }
1868	    continue;
1869	}
1870	this_config
1871	    = CFDictionaryGetValue(system_mode_configurations,
1872				   scan->if_name_cf);
1873	if (scan->mode == kEAPOLControlModeSystem) {
1874	    /* interface is in System mode */
1875	    if (scan->config_dict == NULL) {
1876		/* we must be stopping, ignore it */
1877		continue;
1878	    }
1879	    if (CFDictionaryContainsKey(scan->config_dict,
1880					kSystemModeManagedExternally)) {
1881		/* this instance is managed externally, skip it */
1882		continue;
1883	    }
1884	    if (this_config == NULL) {
1885		/* interface is no longer in System mode */
1886		status = eapolClientStop(scan);
1887		if (status != 0) {
1888		    EAPLOG(LOG_NOTICE, "EAPOLController handle_config_changed:"
1889			   " eapolClientStop (%s) failed %d",
1890			   scan->if_name, status);
1891		}
1892	    }
1893	    else {
1894		if (CFEqual(this_config, scan->config_dict) == FALSE) {
1895		    status = eapolClientUpdate(scan, this_config);
1896		    if (status != 0) {
1897			EAPLOG(LOG_NOTICE,
1898			       "EAPOLController handle_config_changed: "
1899			       "eapolClientUpdate (%s) failed %d",
1900			       scan->if_name, status);
1901		    }
1902		}
1903	    }
1904	}
1905	else if (this_config != NULL) {
1906	    if (scan->state == kEAPOLControlStateIdle) {
1907		status = eapolClientStart(scan, 0, 0,
1908					  this_config, MACH_PORT_NULL,
1909					  MACH_PORT_NULL);
1910		if (status != 0) {
1911		    EAPLOG(LOG_NOTICE,
1912			   "EAPOLController handle_config_changed:"
1913			   " eapolClientStart (%s) failed %d",
1914			   scan->if_name, status);
1915		}
1916	    }
1917	}
1918    }
1919
1920    /* start any that are missing */
1921    range = CFRangeMake(0, CFArrayGetCount(system_mode_iflist));
1922    for (i = 0; i < range.length; i++) {
1923	eapolClientRef		client;
1924	CFStringRef		if_name_cf;
1925
1926	if_name_cf = CFArrayGetValueAtIndex(system_mode_iflist, i);
1927	client = eapolClientLookupInterfaceCF(if_name_cf);
1928	if (client == NULL) {
1929	    char *	if_name;
1930
1931	    if_name = my_CFStringToCString(if_name_cf, kCFStringEncodingASCII);
1932	    client = eapolClientAdd(if_name);
1933	    if (client == NULL) {
1934		EAPLOG(LOG_NOTICE,
1935		       "EAPOLController handle_config_changed:"
1936		       " eapolClientAdd (%s) failed", if_name);
1937	    }
1938	    else {
1939		CFDictionaryRef		this_config;
1940
1941		this_config
1942		    = CFDictionaryGetValue(system_mode_configurations,
1943					   if_name_cf);
1944		status = eapolClientStart(client, 0, 0,
1945					  this_config, MACH_PORT_NULL,
1946					  MACH_PORT_NULL);
1947		if (status != 0) {
1948		    EAPLOG(LOG_NOTICE,
1949			   "EAPOLController handle_config_changed:"
1950			   " eapolClientStart (%s) failed %d",
1951			   client->if_name, status);
1952		}
1953	    }
1954	    free(if_name);
1955	}
1956    }
1957    my_CFRelease(&current_iflist);
1958    return;
1959}
1960
1961static void
1962update_monitored_interfaces(CFArrayRef configured_iflist)
1963{
1964    int				i;
1965    CFRange			range;
1966    eapolClientRef		scan;
1967
1968    /* stop monitoring any interfaces that are no longer configured */
1969    range = CFRangeMake(0, CFArrayGetCount(configured_iflist));
1970    LIST_FOREACH(scan, S_clientHead_p, link) {
1971	if (CFArrayContainsValue(configured_iflist,
1972				 range,
1973				 scan->if_name_cf) == FALSE) {
1974	    if (scan->state == kEAPOLControlStateIdle) {
1975		eapolClientStopMonitoring(scan);
1976	    }
1977	}
1978    }
1979
1980    /* start monitoring any interfaces that are configured */
1981    for (i = 0; i < range.length; i++) {
1982	eapolClientRef		client;
1983	CFStringRef		if_name_cf;
1984
1985	if_name_cf
1986	    = (CFStringRef)CFArrayGetValueAtIndex(configured_iflist, i);
1987	client = eapolClientLookupInterfaceCF(if_name_cf);
1988	if (client == NULL
1989	    || (client->state == kEAPOLControlStateIdle
1990		&& client->eapol_fd == -1)) {
1991	    char *	if_name;
1992
1993	    if_name = my_CFStringToCString(if_name_cf, kCFStringEncodingASCII);
1994	    if (client == NULL) {
1995		client = eapolClientAdd(if_name);
1996		if (client == NULL) {
1997		    EAPLOG(LOG_NOTICE,
1998			   "EAPOLController: monitor "
1999			   "eapolClientAdd (%s) failed", if_name);
2000		}
2001	    }
2002	    if (client != NULL) {
2003		eapolClientStartMonitoring(client);
2004	    }
2005	    free(if_name);
2006	}
2007    }
2008    return;
2009}
2010
2011static void
2012handle_config_changed(boolean_t start_system_mode)
2013{
2014    EAPOLEthernetInfo			info;
2015
2016    if (S_store == NULL) {
2017	return;
2018    }
2019
2020    /* get a snapshot of the configuration information */
2021    EAPOLEthernetInfoInit(&info, start_system_mode);
2022
2023    if (start_system_mode) {
2024	update_system_mode_interfaces(info.system_mode_configurations,
2025				      info.system_mode_iflist);
2026    }
2027    update_monitored_interfaces(info.configured_iflist);
2028
2029    EAPOLEthernetInfoFree(&info);
2030    return;
2031}
2032
2033static void
2034handle_link_changed(CFStringRef if_name)
2035{
2036    eapolClientRef	client;
2037    boolean_t		link_active;
2038
2039    client = eapolClientLookupInterfaceCF(if_name);
2040    if (client == NULL) {
2041	return;
2042    }
2043    if (client->eapol_fd == -1) {
2044	/* we don't care about this interface */
2045	return;
2046    }
2047    link_active = S_if_get_link_active(client->if_name);
2048    EAPLOG(LOG_DEBUG, "EAPOLController: %s link %sactive",
2049	   client->if_name,
2050	   link_active ? "" : "in");
2051    if (link_active == FALSE) {
2052	client->packet_received = FALSE;
2053    }
2054    return;
2055}
2056
2057static void
2058eapol_handle_change(SCDynamicStoreRef store, CFArrayRef changes, void * arg)
2059{
2060    boolean_t		config_changed = FALSE;
2061    CFStringRef		console_user_key;
2062    CFIndex		count;
2063    CFIndex		i;
2064    boolean_t		iflist_changed = FALSE;
2065    CFMutableArrayRef	link_changes = NULL;
2066    boolean_t		user_changed = FALSE;
2067
2068    console_user_key = SCDynamicStoreKeyCreateConsoleUser(NULL);
2069    count = CFArrayGetCount(changes);
2070    if (count == 0) {
2071	goto done;
2072    }
2073    for (i = 0; i < count; i++) {
2074	CFStringRef	cache_key = CFArrayGetValueAtIndex(changes, i);
2075
2076	if (CFEqual(cache_key, console_user_key)) {
2077	    user_changed = TRUE;
2078	}
2079        else if (CFStringHasPrefix(cache_key, kSCDynamicStoreDomainSetup)) {
2080	    config_changed = TRUE;
2081	}
2082	else if (CFStringHasSuffix(cache_key, kSCCompInterface)) {
2083	    /* list of interfaces changed */
2084	    iflist_changed = TRUE;
2085	}
2086	else if (CFStringHasSuffix(cache_key, kSCEntNetLink)) {
2087	    /* link status changed */
2088	    CFStringRef		if_name;
2089
2090	    if_name = my_CFStringCopyComponent(cache_key, CFSTR("/"), 3);
2091	    if (if_name != NULL) {
2092		if (link_changes == NULL) {
2093		    link_changes
2094			= CFArrayCreateMutable(NULL,
2095					       count,
2096					       &kCFTypeArrayCallBacks);
2097		}
2098		CFArrayAppendValue(link_changes, if_name);
2099	    }
2100	}
2101    }
2102
2103    if (iflist_changed || config_changed) {
2104	handle_config_changed(TRUE);
2105    }
2106    if (user_changed) {
2107	console_user_changed();
2108    }
2109    if (link_changes != NULL) {
2110	count = CFArrayGetCount(link_changes);
2111	for (i = 0; i < count; i++) {
2112	    CFStringRef		if_name;
2113
2114	    if_name = CFArrayGetValueAtIndex(link_changes, i);
2115	    handle_link_changed(if_name);
2116	}
2117	CFRelease(link_changes);
2118    }
2119
2120 done:
2121    my_CFRelease(&console_user_key);
2122    return;
2123}
2124
2125static SCDynamicStoreRef
2126dynamic_store_create(void)
2127{
2128    CFArrayRef			keys = NULL;
2129    CFStringRef			list[3];
2130    CFArrayRef			patterns = NULL;
2131    SCDynamicStoreRef		store;
2132
2133    store = SCDynamicStoreCreate(NULL, CFSTR("EAPOLController"),
2134				 eapol_handle_change, NULL);
2135    if (store == NULL) {
2136	EAPLOG(LOG_NOTICE, "EAPOLController: SCDynamicStoreCreate() failed, %s",
2137	       SCErrorString(SCError()));
2138	return (NULL);
2139    }
2140
2141    /* console user */
2142    list[0]
2143	= SCDynamicStoreKeyCreateConsoleUser(NULL);
2144
2145    /* list of interfaces */
2146    list[1]
2147	= SCDynamicStoreKeyCreateNetworkInterface(NULL,
2148						  kSCDynamicStoreDomainState);
2149    keys = CFArrayCreate(NULL, (const void * *)list, 2, &kCFTypeArrayCallBacks);
2150    CFRelease(list[0]);
2151    CFRelease(list[1]);
2152
2153    /* requested EAPOL configurations */
2154    list[0]
2155	= SCDynamicStoreKeyCreateNetworkInterfaceEntity(NULL,
2156							kSCDynamicStoreDomainSetup,
2157							kSCCompAnyRegex,
2158							kSCEntNetEAPOL);
2159    /* configured services */
2160    list[1]
2161	= SCDynamicStoreKeyCreateNetworkServiceEntity(NULL,
2162						      kSCDynamicStoreDomainSetup,
2163						      kSCCompAnyRegex,
2164						      kSCEntNetInterface);
2165    list[2]
2166	= SCDynamicStoreKeyCreateNetworkInterfaceEntity(NULL,
2167							kSCDynamicStoreDomainState,
2168							kSCCompAnyRegex,
2169							kSCEntNetLink);
2170    patterns = CFArrayCreate(NULL, (const void * *)list, 3,
2171			     &kCFTypeArrayCallBacks);
2172    CFRelease(list[0]);
2173    CFRelease(list[1]);
2174    CFRelease(list[2]);
2175
2176    SCDynamicStoreSetNotificationKeys(store, keys, patterns);
2177    CFRelease(keys);
2178    CFRelease(patterns);
2179    return (store);
2180}
2181
2182static void
2183dynamic_store_schedule(SCDynamicStoreRef store)
2184{
2185    CFRunLoopSourceRef		rls;
2186
2187    rls = SCDynamicStoreCreateRunLoopSource(NULL, store, 0);
2188    CFRunLoopAddSource(CFRunLoopGetCurrent(), rls, kCFRunLoopDefaultMode);
2189    my_CFRelease(&rls);
2190    return;
2191}
2192
2193static void *
2194ControllerThread(void * arg)
2195{
2196    server_start();
2197    dynamic_store_schedule(S_store);
2198    handle_config_changed(TRUE);
2199    CFRunLoopRun();
2200    return (arg);
2201}
2202
2203#endif /* TARGET_OS_EMBEDDED */
2204
2205
2206static void
2207ControllerBegin(void)
2208{
2209    pthread_attr_t	attr;
2210    int			ret;
2211    pthread_t		thread;
2212
2213    ret = pthread_attr_init(&attr);
2214    if (ret != 0) {
2215	EAPLOG(LOG_NOTICE, "EAPOLController: pthread_attr_init failed %d", ret);
2216	return;
2217    }
2218    ret = pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
2219    if (ret != 0) {
2220	EAPLOG(LOG_NOTICE,
2221	       "EAPOLController: pthread_attr_setdetachstate failed %d", ret);
2222	goto done;
2223    }
2224    ret = pthread_create(&thread, &attr, ControllerThread, NULL);
2225    if (ret != 0) {
2226	EAPLOG(LOG_NOTICE, "EAPOLController: pthread_create failed %d", ret);
2227	goto done;
2228    }
2229
2230 done:
2231    (void)pthread_attr_destroy(&attr);
2232    return;
2233}
2234
2235
2236static void
2237check_prefs(SCPreferencesRef prefs)
2238{
2239    uint32_t	log_flags;
2240
2241    log_flags = EAPOLControlPrefsGetLogFlags();
2242    EAPLogSetVerbose(log_flags != 0);
2243    EAPOLControlPrefsSynchronize();
2244    return;
2245}
2246
2247/*
2248 * configd plugin-specific routines:
2249 */
2250void
2251load(CFBundleRef bundle, Boolean bundleVerbose)
2252{
2253    Boolean		ok;
2254    uint8_t		path[MAXPATHLEN];
2255    SCPreferencesRef	prefs;
2256    CFURLRef		url;
2257
2258    prefs = EAPOLControlPrefsInit(CFRunLoopGetCurrent(), check_prefs);
2259    check_prefs(prefs);
2260    /* get a path to eapolclient */
2261    url = CFBundleCopyResourceURL(bundle, CFSTR("eapolclient"), NULL, NULL);
2262    if (url == NULL) {
2263	EAPLOG(LOG_NOTICE,
2264	       "EAPOLController: failed to get URL for eapolclient");
2265	return;
2266    }
2267    ok = CFURLGetFileSystemRepresentation(url, TRUE, path, sizeof(path));
2268    CFRelease(url);
2269    if (ok == FALSE) {
2270	EAPLOG(LOG_NOTICE,
2271	       "EAPOLController: failed to get path for eapolclient");
2272	return;
2273    }
2274    S_eapolclient_path = strdup((const char *)path);
2275
2276    /* register the EAPOLController server port */
2277    server_register();
2278    return;
2279}
2280
2281void
2282start(const char *bundleName, const char *bundleDir)
2283{
2284    if (S_eapolclient_path == NULL) {
2285	return;
2286    }
2287    LIST_INIT(S_clientHead_p);
2288    S_store = dynamic_store_create();
2289    return;
2290}
2291
2292void
2293prime()
2294{
2295    if (S_eapolclient_path == NULL) {
2296	return;
2297    }
2298    ControllerBegin();
2299    return;
2300}
2301