#include <syslog.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <sys/errno.h>
#include <sys/queue.h>
#include <mach/mach.h>
#include <mach/message.h>
#include <mach/mach_error.h>
#include <SystemConfiguration/SystemConfiguration.h>
#include <SystemConfiguration/SCValidation.h>
#include <CoreFoundation/CFDictionary.h>
#include <CoreFoundation/CFMachPort.h>
#include <CoreFoundation/CFRunLoop.h>
#include <SystemConfiguration/SCDPlugin.h>
#include "mylog.h"
#include "myCFUtil.h"
#include "controller.h"
#include "server.h"
#include "ClientControlInterface.h"
#include <EAP8021X/EAPOLControlTypes.h>
#ifndef kSCEntNetRefreshConfiguration
#define kSCEntNetRefreshConfiguration CFSTR("RefreshConfiguration")
#endif kSCEntNetRefreshConfiguration
#ifndef kSCEntNetEAPOL
#define kSCEntNetEAPOL CFSTR("EAPOL")
#endif kSCEntNetEAPOL
static SCDynamicStoreRef S_store;
static boolean_t
is_console_user(uid_t check_uid);
typedef struct eapolClient_s {
LIST_ENTRY(eapolClient_s) link;
EAPOLControlState state;
if_name_t if_name;
CFStringRef notification_key;
CFStringRef force_renew_key;
struct {
uid_t uid;
gid_t gid;
} owner;
pid_t pid;
mach_port_t notify_port;
mach_port_t bootstrap;
CFMachPortRef session_cfport;
CFRunLoopSourceRef session_rls;
boolean_t notification_sent;
boolean_t retry;
boolean_t keep_it;
boolean_t console_user;
boolean_t system_mode;
CFDictionaryRef config_dict;
CFDictionaryRef status_dict;
} eapolClient, *eapolClientRef;
static LIST_HEAD(clientHead, eapolClient_s) S_head;
static struct clientHead * S_clientHead_p = &S_head;
static int
eapolClientStop(eapolClientRef client);
static void
eapolClientSetState(eapolClientRef client, EAPOLControlState state);
eapolClientRef
eapolClientLookupInterface(const char * if_name)
{
eapolClientRef scan;
LIST_FOREACH(scan, S_clientHead_p, link) {
if (strcmp(if_name, scan->if_name) == 0) {
return (scan);
}
}
return (NULL);
}
eapolClientRef
eapolClientLookupProcess(pid_t pid)
{
eapolClientRef scan;
LIST_FOREACH(scan, S_clientHead_p, link) {
if (pid == scan->pid) {
return (scan);
}
}
return (NULL);
}
eapolClientRef
eapolClientLookupSession(mach_port_t session_port)
{
eapolClientRef scan;
LIST_FOREACH(scan, S_clientHead_p, link) {
if (scan->session_cfport != NULL
&& session_port == CFMachPortGetPort(scan->session_cfport)) {
return (scan);
}
}
return (NULL);
}
eapolClientRef
eapolClientAdd(const char * if_name)
{
eapolClientRef client;
client = malloc(sizeof(*client));
if (client == NULL) {
return (NULL);
}
bzero(client, sizeof(*client));
strcpy(client->if_name, if_name);
client->pid = -1;
LIST_INSERT_HEAD(S_clientHead_p, client, link);
return (client);
}
void
eapolClientRemove(eapolClientRef client)
{
LIST_REMOVE(client, link);
free(client);
return;
}
static void
eapolClientInvalidate(eapolClientRef client)
{
eapolClientSetState(client, kEAPOLControlStateIdle);
client->pid = -1;
client->owner.uid = 0;
client->owner.gid = 0;
client->keep_it = FALSE;
client->system_mode = FALSE;
client->retry = FALSE;
client->notification_sent = FALSE;
client->console_user = FALSE;
my_CFRelease(&client->notification_key);
my_CFRelease(&client->force_renew_key);
if (client->notify_port != MACH_PORT_NULL) {
(void)mach_port_destroy(mach_task_self(), client->notify_port);
client->notify_port = MACH_PORT_NULL;
}
if (client->bootstrap != MACH_PORT_NULL) {
(void)mach_port_destroy(mach_task_self(), client->bootstrap);
client->bootstrap = MACH_PORT_NULL;
}
if (client->session_cfport != NULL) {
CFMachPortInvalidate(client->session_cfport);
my_CFRelease(&client->session_cfport);
}
if (client->session_rls != NULL) {
CFRunLoopRemoveSource(CFRunLoopGetCurrent(),
client->session_rls, kCFRunLoopDefaultMode);
my_CFRelease(&client->session_rls);
}
my_CFRelease(&client->config_dict);
my_CFRelease(&client->status_dict);
return;
}
static void
eapolClientNotify(eapolClientRef client)
{
mach_msg_empty_send_t msg;
mach_msg_option_t options;
kern_return_t status;
if (client->notify_port == MACH_PORT_NULL) {
return;
}
if (client->notification_sent == TRUE) {
return;
}
msg.header.msgh_bits = MACH_MSGH_BITS(MACH_MSG_TYPE_COPY_SEND, 0);
msg.header.msgh_size = sizeof(msg);
msg.header.msgh_remote_port = client->notify_port;
msg.header.msgh_local_port = MACH_PORT_NULL;
msg.header.msgh_id = 0;
options = MACH_SEND_TIMEOUT;
status = mach_msg(&msg.header,
MACH_SEND_MSG | MACH_SEND_TIMEOUT,
msg.header.msgh_size,
0,
MACH_PORT_NULL,
0,
MACH_PORT_NULL);
if (status != KERN_SUCCESS) {
my_log(LOG_NOTICE, "eapolClientNotify: mach_msg(%s) failed: %s",
client->if_name, mach_error_string(status));
}
client->notification_sent = TRUE;
return;
}
static CFStringRef
eapolClientNotificationKey(eapolClientRef client)
{
CFStringRef if_name_cf;
if (client->notification_key == NULL) {
if_name_cf = CFStringCreateWithCString(NULL, client->if_name,
kCFStringEncodingASCII);
client->notification_key
= SCDynamicStoreKeyCreateNetworkInterfaceEntity(NULL,
kSCDynamicStoreDomainState,
if_name_cf,
kSCEntNetEAPOL);
my_CFRelease(&if_name_cf);
}
return (client->notification_key);
}
static void
eapolClientSetState(eapolClientRef client, EAPOLControlState state)
{
client->state = state;
if (S_store == NULL) {
return;
}
SCDynamicStoreNotifyValue(S_store, eapolClientNotificationKey(client));
return;
}
static void
eapolClientPublishStatus(eapolClientRef client, CFDictionaryRef status_dict)
{
if (S_store == NULL) {
return;
}
CFRetain(status_dict);
my_CFRelease(&client->status_dict);
client->status_dict = status_dict;
SCDynamicStoreNotifyValue(S_store, eapolClientNotificationKey(client));
return;
}
static CFStringRef
eapolClientForceRenewKey(eapolClientRef client)
{
CFStringRef if_name_cf;
if (client->force_renew_key == NULL) {
if_name_cf = CFStringCreateWithCString(NULL, client->if_name,
kCFStringEncodingASCII);
client->force_renew_key
= SCDynamicStoreKeyCreateNetworkInterfaceEntity(NULL,
kSCDynamicStoreDomainState,
if_name_cf,
kSCEntNetRefreshConfiguration);
my_CFRelease(&if_name_cf);
}
return (client->force_renew_key);
}
static void
eapolClientForceRenew(eapolClientRef client)
{
if (S_store == NULL) {
return;
}
SCDynamicStoreNotifyValue(S_store, eapolClientForceRenewKey(client));
return;
}
static void
exec_callback(pid_t pid, int status, struct rusage * rusage, void * context)
{
eapolClientRef client;
boolean_t keep_it;
client = eapolClientLookupProcess(pid);
if (client == NULL) {
return;
}
if (client->state != kEAPOLControlStateIdle) {
my_log(LOG_NOTICE,
"EAPOLController: eapolclient(%s) pid=%d exited with status %d",
client->if_name, pid, status);
}
keep_it = client->keep_it;
eapolClientInvalidate(client);
if (keep_it == FALSE) {
eapolClientRemove(client);
}
return;
}
static int
eapolClientStart(eapolClientRef client, uid_t uid, gid_t gid,
CFDictionaryRef config_dict, mach_port_t bootstrap)
{
char * argv[] = { eapolclient_path,
"-i",
client->if_name,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL };
char gid_str[32];
boolean_t on_console = FALSE;
int status = 0;
char uid_str[32];
if (bootstrap == MACH_PORT_NULL) {
argv[3] = "-s";
}
else {
snprintf(uid_str, sizeof(uid_str), "%u", uid);
snprintf(gid_str, sizeof(gid_str), "%u", gid);
argv[3] = "-u";
argv[4] = uid_str;
argv[5] = "-g";
argv[6] = gid_str;
on_console = is_console_user(uid);
if (on_console == FALSE) {
argv[7] = "-n";
}
}
client->pid = _SCDPluginExecCommand(exec_callback, NULL, 0, 0,
eapolclient_path, argv);
if (client->pid == -1) {
status = errno;
}
else {
if (bootstrap != MACH_PORT_NULL) {
client->owner.uid = uid;
client->owner.gid = gid;
client->console_user = on_console;
client->bootstrap = bootstrap;
}
else {
client->system_mode = TRUE;
}
if (config_dict != NULL) {
client->config_dict = CFRetain(config_dict);
}
eapolClientSetState(client, kEAPOLControlStateStarting);
}
return (status);
}
static int
eapolClientUpdate(eapolClientRef client, CFDictionaryRef config_dict)
{
int status = 0;
switch (client->state) {
case kEAPOLControlStateStarting:
case kEAPOLControlStateRunning:
break;
case kEAPOLControlStateIdle:
status = ENOENT;
goto done;
default:
status = EBUSY;
goto done;
}
if (client->state == kEAPOLControlStateRunning) {
eapolClientNotify(client);
}
my_CFRelease(&client->config_dict);
client->config_dict = CFRetain(config_dict);
client->retry = FALSE;
done:
return (status);
}
static CFNumberRef
make_command_number(EAPOLClientControlCommand command)
{
return (CFNumberCreate(NULL, kCFNumberIntType, &command));
}
int
ControllerGetState(if_name_t if_name, int * state)
{
eapolClientRef client;
int status = 0;
*state = kEAPOLControlStateIdle;
client = eapolClientLookupInterface(if_name);
if (client == NULL) {
status = ENOENT;
}
else {
*state = client->state;
}
return (status);
}
int
ControllerCopyStateAndStatus(if_name_t if_name,
int * state,
CFDictionaryRef * status_dict)
{
eapolClientRef client;
int status = 0;
*state = kEAPOLControlStateIdle;
*status_dict = NULL;
client = eapolClientLookupInterface(if_name);
if (client == NULL) {
status = ENOENT;
}
else {
if (client->status_dict != NULL) {
*status_dict = CFRetain(client->status_dict);
}
*state = client->state;
}
return (status);
}
static boolean_t
is_console_user(uid_t check_uid)
{
uid_t uid;
CFStringRef user;
user = SCDynamicStoreCopyConsoleUser(S_store, &uid, NULL);
if (user == NULL) {
return (FALSE);
}
CFRelease(user);
return (check_uid == uid);
}
int
ControllerStart(if_name_t if_name, uid_t uid, gid_t gid,
CFDictionaryRef config_dict, mach_port_t bootstrap)
{
eapolClientRef client;
int status = 0;
client = eapolClientLookupInterface(if_name);
if (client != NULL) {
if (client->state != kEAPOLControlStateIdle) {
if (client->state == kEAPOLControlStateRunning) {
status = EEXIST;
}
else {
status = EBUSY;
}
goto done;
}
}
else {
client = eapolClientAdd(if_name);
if (client == NULL) {
status = ENOMEM;
goto done;
}
}
status = eapolClientStart(client, uid, gid, config_dict, bootstrap);
done:
if (status != 0) {
(void)mach_port_destroy(mach_task_self(), bootstrap);
}
return (status);
}
static int
eapolClientStop(eapolClientRef client)
{
int status = 0;
switch (client->state) {
case kEAPOLControlStateRunning:
case kEAPOLControlStateStarting:
break;
case kEAPOLControlStateIdle:
status = ENOENT;
goto done;
default:
status = EBUSY;
goto done;
}
eapolClientSetState(client, kEAPOLControlStateStopping);
my_CFRelease(&client->config_dict);
eapolClientNotify(client);
#if 0
if (client->pid != -1 && client->pid != 0) {
kill(client->pid, SIGTERM);
}
#endif 0
done:
return (status);
}
int
ControllerStop(if_name_t if_name, uid_t uid, gid_t gid)
{
eapolClientRef client;
int status = 0;
client = eapolClientLookupInterface(if_name);
if (client == NULL) {
status = ENOENT;
goto done;
}
if (uid != 0 && uid != client->owner.uid) {
status = EPERM;
goto done;
}
status = eapolClientStop(client);
done:
return (status);
}
int
ControllerUpdate(if_name_t if_name, uid_t uid, gid_t gid,
CFDictionaryRef config_dict)
{
eapolClientRef client;
int status = 0;
client = eapolClientLookupInterface(if_name);
if (client == NULL) {
status = ENOENT;
goto done;
}
if (uid != 0 && uid != client->owner.uid) {
status = EPERM;
goto done;
}
status = eapolClientUpdate(client, config_dict);
done:
return (status);
}
int
ControllerRetry(if_name_t if_name, uid_t uid, gid_t gid)
{
eapolClientRef client;
int status = 0;
client = eapolClientLookupInterface(if_name);
if (client == NULL) {
status = ENOENT;
goto done;
}
if (uid != 0 && uid != client->owner.uid) {
status = EPERM;
goto done;
}
switch (client->state) {
case kEAPOLControlStateStarting:
goto done;
case kEAPOLControlStateRunning:
break;
case kEAPOLControlStateIdle:
status = ENOENT;
goto done;
default:
status = EBUSY;
goto done;
}
eapolClientNotify(client);
client->retry = TRUE;
done:
return (status);
}
int
ControllerSetLogLevel(if_name_t if_name, uid_t uid, gid_t gid,
int32_t level)
{
eapolClientRef client;
int32_t cur_level = -1;
CFNumberRef log_prop;
int status = 0;
client = eapolClientLookupInterface(if_name);
if (client == NULL || client->config_dict == NULL) {
status = ENOENT;
goto done;
}
if (uid != 0 && uid != client->owner.uid) {
status = EPERM;
goto done;
}
switch (client->state) {
case kEAPOLControlStateStarting:
case kEAPOLControlStateRunning:
break;
case kEAPOLControlStateIdle:
status = ENOENT;
goto done;
default:
status = EBUSY;
goto done;
}
log_prop = CFDictionaryGetValue(client->config_dict,
kEAPOLControlLogLevel);
if (log_prop) {
(void)CFNumberGetValue(log_prop, kCFNumberSInt32Type, &cur_level);
}
if (level < 0) {
level = -1;
}
if (cur_level != level) {
int count;
CFMutableDictionaryRef dict = NULL;
count = CFDictionaryGetCount(client->config_dict);
if (level >= 0) {
count++;
}
dict = CFDictionaryCreateMutableCopy(NULL, count,
client->config_dict);
if (level >= 0) {
CFNumberRef level_prop;
level_prop = CFNumberCreate(NULL, kCFNumberSInt32Type,
&level);
CFDictionarySetValue(dict, kEAPOLControlLogLevel, level_prop);
my_CFRelease(&level_prop);
}
else {
CFDictionaryRemoveValue(dict, kEAPOLControlLogLevel);
}
my_CFRelease(&client->config_dict);
client->config_dict = dict;
if (client->state == kEAPOLControlStateRunning) {
eapolClientNotify(client);
}
}
done:
return (status);
}
int
ControllerClientAttach(pid_t pid, if_name_t if_name,
mach_port_t notify_port,
mach_port_t * session_port,
CFDictionaryRef * control_dict,
mach_port_t * bootstrap)
{
CFMutableDictionaryRef dict = NULL;
CFNumberRef command_cf = NULL;
eapolClientRef client;
int result = 0;
*session_port = MACH_PORT_NULL;
*control_dict = NULL;
client = eapolClientLookupInterface(if_name);
if (client == NULL) {
result = ENOENT;
goto failed;
}
if (pid != client->pid) {
result = EPERM;
goto failed;
}
if (client->session_cfport != NULL) {
result = EEXIST;
goto failed;
}
client->notify_port = notify_port;
client->session_cfport
= CFMachPortCreate(NULL, server_handle_request, NULL, NULL);
*session_port = CFMachPortGetPort(client->session_cfport);
client->session_rls
= CFMachPortCreateRunLoopSource(NULL, client->session_cfport, 0);
CFRunLoopAddSource(CFRunLoopGetCurrent(),
client->session_rls, kCFRunLoopDefaultMode);
dict = CFDictionaryCreateMutable(NULL, 0,
&kCFTypeDictionaryKeyCallBacks,
&kCFTypeDictionaryValueCallBacks);
if (client->state == kEAPOLControlStateStarting) {
command_cf = make_command_number(kEAPOLClientControlCommandRun);
if (client->config_dict != NULL) {
CFDictionarySetValue(dict, kEAPOLClientControlConfiguration,
client->config_dict);
}
}
else {
command_cf = make_command_number(kEAPOLClientControlCommandStop);
}
CFDictionarySetValue(dict, kEAPOLClientControlCommand, command_cf);
*control_dict = dict;
eapolClientSetState(client, kEAPOLControlStateRunning);
client->keep_it = TRUE;
*bootstrap = client->bootstrap;
return (result);
failed:
(void)mach_port_destroy(mach_task_self(), notify_port);
return (result);
}
int
ControllerClientDetach(mach_port_t session_port)
{
eapolClientRef client;
int result = 0;
client = eapolClientLookupSession(session_port);
if (client == NULL) {
result = EINVAL;
goto failed;
}
my_log(LOG_DEBUG, "EAPOLController: detached port 0x%x", session_port);
eapolClientInvalidate(client);
failed:
return (result);
}
int
ControllerClientGetConfig(mach_port_t session_port,
CFDictionaryRef * control_dict)
{
eapolClientRef client;
CFNumberRef command_cf = NULL;
CFMutableDictionaryRef dict = NULL;
int result = 0;
*control_dict = NULL;
client = eapolClientLookupSession(session_port);
if (client == NULL) {
result = EINVAL;
goto failed;
}
dict = CFDictionaryCreateMutable(NULL, 0,
&kCFTypeDictionaryKeyCallBacks,
&kCFTypeDictionaryValueCallBacks);
if (client->state == kEAPOLControlStateRunning) {
if (client->retry) {
command_cf = make_command_number(kEAPOLClientControlCommandRetry);
}
else {
command_cf = make_command_number(kEAPOLClientControlCommandRun);
if (client->config_dict != NULL) {
CFDictionarySetValue(dict, kEAPOLClientControlConfiguration,
client->config_dict);
}
}
}
else {
command_cf = make_command_number(kEAPOLClientControlCommandStop);
}
CFDictionarySetValue(dict, kEAPOLClientControlCommand, command_cf);
*control_dict = dict;
client->notification_sent = FALSE;
client->retry = FALSE;
my_CFRelease(&command_cf);
failed:
return (result);
}
int
ControllerClientReportStatus(mach_port_t session_port,
CFDictionaryRef status_dict)
{
eapolClientRef client;
int result = 0;
client = eapolClientLookupSession(session_port);
if (client == NULL) {
result = EINVAL;
goto failed;
}
(void)eapolClientPublishStatus(client, status_dict);
failed:
return (result);
}
int
ControllerClientForceRenew(mach_port_t session_port)
{
eapolClientRef client;
int result = 0;
client = eapolClientLookupSession(session_port);
if (client == NULL) {
result = EINVAL;
goto failed;
}
(void)eapolClientForceRenew(client);
failed:
return (result);
}
int
ControllerClientPortDead(mach_port_t session_port)
{
eapolClientRef client;
int result = 0;
my_log(LOG_DEBUG,
"EAPOLController: ControllerClientPortDead: port 0x%x died",
session_port);
client = eapolClientLookupSession(session_port);
if (client == NULL) {
result = ENOENT;
goto failed;
}
eapolClientInvalidate(client);
eapolClientRemove(client);
failed:
return (result);
}
static void
console_user_changed()
{
eapolClientRef scan;
uid_t uid = 0;
CFStringRef user;
user = SCDynamicStoreCopyConsoleUser(S_store, &uid, NULL);
LIST_FOREACH(scan, S_clientHead_p, link) {
if (scan->console_user) {
if (user == NULL || scan->owner.uid != uid) {
(void)eapolClientStop(scan);
}
}
}
my_CFRelease(&user);
return;
}
static Boolean
myCFStringArrayToCStringArray(CFArrayRef arr, char * buffer, int * buffer_size,
int * ret_count)
{
int count = CFArrayGetCount(arr);
int i;
char * offset = NULL;
int space;
char * * strlist = NULL;
if (ret_count != NULL) {
*ret_count = 0;
}
space = count * sizeof(char *);
if (buffer != NULL) {
if (*buffer_size < space) {
return (FALSE);
}
strlist = (char * *)buffer;
offset = buffer + space;
}
for (i = 0; i < count; i++) {
CFIndex len = 0;
CFStringRef str;
str = CFArrayGetValueAtIndex(arr, i);
if (buffer != NULL) {
len = *buffer_size - space;
if (len <= 0) {
return (FALSE);
}
}
CFStringGetBytes(str, CFRangeMake(0, CFStringGetLength(str)),
kCFStringEncodingASCII, '\0',
FALSE, (uint8_t *)offset, len - 1, &len);
if (buffer != NULL) {
strlist[i] = offset;
offset[len] = '\0';
offset += len + 1;
}
space += len + 1;
}
*buffer_size = space;
if (ret_count != NULL) {
*ret_count = count;
}
return (TRUE);
}
static CFStringRef
mySCNetworkInterfacePathCopyInterfaceName(CFStringRef path)
{
CFArrayRef arr;
CFStringRef ifname = NULL;
arr = CFStringCreateArrayBySeparatingStrings(NULL, path, CFSTR("/"));
if (arr == NULL) {
goto done;
}
if (CFArrayGetCount(arr) < 4) {
goto done;
}
ifname = CFRetain(CFArrayGetValueAtIndex(arr, 3));
done:
if (arr != NULL) {
CFRelease(arr);
}
return (ifname);
}
static const char * *
copy_interface_list(int * ret_iflist_count)
{
CFDictionaryRef dict;
CFArrayRef iflist_cf = NULL;
const char * * iflist = NULL;
int iflist_count = 0;
int iflist_size = 0;
CFStringRef key;
key = SCDynamicStoreKeyCreateNetworkInterface(NULL,
kSCDynamicStoreDomainState);
dict = SCDynamicStoreCopyValue(S_store, key);
my_CFRelease(&key);
if (isA_CFDictionary(dict) != NULL) {
iflist_cf = CFDictionaryGetValue(dict, kSCPropNetInterfaces);
iflist_cf = isA_CFArray(iflist_cf);
}
if (iflist_cf == NULL) {
goto done;
}
if (myCFStringArrayToCStringArray(iflist_cf, NULL, &iflist_size, NULL)
== FALSE) {
goto done;
}
iflist = malloc(iflist_size);
if (iflist == NULL) {
goto done;
}
if (myCFStringArrayToCStringArray(iflist_cf, (void *)iflist, &iflist_size,
&iflist_count) == FALSE) {
free(iflist);
iflist = NULL;
goto done;
}
done:
*ret_iflist_count = iflist_count;
my_CFRelease(&dict);
return (iflist);
}
#define INDEX_NONE (-1)
static int
strlist_item_index(const char * * strlist, int strlist_count, const char * item)
{
int i;
for (i = 0; i < strlist_count; i++) {
if (strcmp(strlist[i], item) == 0) {
return (i);
}
}
return (INDEX_NONE);
}
static void
handle_config_changed()
{
const void * * config_dicts = NULL;
const char * * config_iflist = NULL;
int count = 0;
CFDictionaryRef dict;
int i;
const char * * iflist = NULL;
int iflist_count = 0;
CFStringRef key;
CFArrayRef patterns;
eapolClientRef scan;
int status;
iflist = copy_interface_list(&iflist_count);
key = SCDynamicStoreKeyCreateNetworkInterfaceEntity(NULL,
kSCDynamicStoreDomainSetup,
kSCCompAnyRegex,
kSCEntNetEAPOL);
patterns = CFArrayCreate(NULL, (void *)&key, 1, &kCFTypeArrayCallBacks);
my_CFRelease(&key);
dict = SCDynamicStoreCopyMultiple(S_store, NULL, patterns);
my_CFRelease(&patterns);
if (dict != NULL) {
count = CFDictionaryGetCount(dict);
}
if (count != 0) {
CFMutableArrayRef iflist_cf = NULL;
const void * * keys;
int size;
keys = (const void * *)malloc(sizeof(*keys) * count);
config_dicts = (const void * *)malloc(sizeof(*config_dicts) * count);
CFDictionaryGetKeysAndValues(dict, keys, config_dicts);
iflist_cf = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
for (i = 0; i < count; i++) {
CFStringRef name;
name = mySCNetworkInterfacePathCopyInterfaceName(keys[i]);
CFArrayAppendValue(iflist_cf, name);
my_CFRelease(&name);
}
if (myCFStringArrayToCStringArray(iflist_cf, NULL, &size,
NULL) == FALSE) {
my_log(LOG_NOTICE,
"EAPOLController: config_iflist failed to calculate size");
count = 0;
}
else {
config_iflist = malloc(size);
if (myCFStringArrayToCStringArray(iflist_cf, (void *)config_iflist,
&size, NULL) == FALSE) {
free(config_iflist);
config_iflist = NULL;
count = 0;
}
}
free(keys);
my_CFRelease(&iflist_cf);
}
LIST_FOREACH(scan, S_clientHead_p, link) {
if (strlist_item_index(iflist, iflist_count, scan->if_name)
== INDEX_NONE) {
continue;
}
i = strlist_item_index(config_iflist, count, scan->if_name);
if (scan->system_mode) {
if (i == INDEX_NONE) {
status = eapolClientStop(scan);
if (status != 0) {
my_log(LOG_NOTICE, "EAPOLController handle_config_changed:"
" eapolClientStop (%s) failed %d",
scan->if_name, status);
}
}
else {
if (CFEqual(config_dicts[i], scan->config_dict) == FALSE) {
status = eapolClientUpdate(scan, config_dicts[i]);
if (status != 0) {
my_log(LOG_NOTICE,
"EAPOLController handle_config_changed: "
"eapolClientUpdate (%s) failed %d",
scan->if_name, status);
}
}
}
}
else if (i != INDEX_NONE) {
if (scan->state == kEAPOLControlStateIdle) {
status = eapolClientStart(scan, 0, 0,
config_dicts[i], MACH_PORT_NULL);
if (status != 0) {
my_log(LOG_NOTICE,
"EAPOLController handle_config_changed:"
" eapolClientStart (%s) failed %d",
scan->if_name, status);
}
}
}
}
for (i = 0; i < count; i++) {
eapolClientRef client;
client = eapolClientLookupInterface(config_iflist[i]);
if (client == NULL) {
client = eapolClientAdd(config_iflist[i]);
if (client == NULL) {
my_log(LOG_NOTICE,
"EAPOLController handle_config_changed:"
" eapolClientAdd (%s) failed", config_iflist[i]);
}
else {
status = eapolClientStart(client, 0, 0,
config_dicts[i], MACH_PORT_NULL);
if (status != 0) {
my_log(LOG_NOTICE,
"EAPOLController handle_config_changed:"
" eapolClientStart (%s) failed %d",
scan->if_name, status);
}
}
}
}
if (iflist != NULL) {
free(iflist);
}
if (config_iflist != NULL) {
free(config_iflist);
}
if (config_dicts != NULL) {
free(config_dicts);
}
my_CFRelease(&dict);
return;
}
static void
eapol_handle_change(SCDynamicStoreRef store, CFArrayRef changes, void * arg)
{
boolean_t config_changed = FALSE;
CFStringRef console_user_key;
CFIndex count;
CFIndex i;
boolean_t iflist_changed = FALSE;
boolean_t user_changed = FALSE;
console_user_key = SCDynamicStoreKeyCreateConsoleUser(NULL);
count = CFArrayGetCount(changes);
if (count == 0) {
goto done;
}
for (i = 0; i < count; i++) {
CFStringRef cache_key = CFArrayGetValueAtIndex(changes, i);
if (CFEqual(cache_key, console_user_key)) {
user_changed = TRUE;
}
else if (CFStringHasPrefix(cache_key, kSCDynamicStoreDomainSetup)) {
config_changed = TRUE;
}
else if (CFStringHasSuffix(cache_key, kSCCompInterface)) {
iflist_changed = TRUE;
}
}
if (iflist_changed || config_changed) {
handle_config_changed();
}
if (user_changed) {
console_user_changed();
}
done:
my_CFRelease(&console_user_key);
return;
}
static SCDynamicStoreRef
dynamic_store_create(SCDynamicStoreCallBack func, void * arg)
{
CFMutableArrayRef keys = NULL;
CFStringRef key;
CFRunLoopSourceRef rls;
CFArrayRef patterns = NULL;
SCDynamicStoreRef store;
SCDynamicStoreContext context;
bzero(&context, sizeof(context));
context.info = arg;
store = SCDynamicStoreCreate(NULL, CFSTR("EAPOLController"),
func, &context);
if (store == NULL) {
my_log(LOG_NOTICE, "EAPOLController: SCDynamicStoreCreate() failed, %s",
SCErrorString(SCError()));
return (NULL);
}
key = SCDynamicStoreKeyCreateConsoleUser(NULL);
keys = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
CFArrayAppendValue(keys, key);
my_CFRelease(&key);
key = SCDynamicStoreKeyCreateNetworkInterface(NULL,
kSCDynamicStoreDomainState);
CFArrayAppendValue(keys, key);
my_CFRelease(&key);
key = SCDynamicStoreKeyCreateNetworkInterfaceEntity(NULL,
kSCDynamicStoreDomainSetup,
kSCCompAnyRegex,
kSCEntNetEAPOL);
patterns = CFArrayCreate(NULL, (void *)&key, 1, &kCFTypeArrayCallBacks);
my_CFRelease(&key);
SCDynamicStoreSetNotificationKeys(store, keys, patterns);
my_CFRelease(&keys);
my_CFRelease(&patterns);
rls = SCDynamicStoreCreateRunLoopSource(NULL, store, 0);
CFRunLoopAddSource(CFRunLoopGetCurrent(), rls, kCFRunLoopDefaultMode);
my_CFRelease(&rls);
return (store);
}
void
_ControllerInitialize()
{
LIST_INIT(S_clientHead_p);
S_store = dynamic_store_create(eapol_handle_change, NULL);
return;
}
void
_ControllerBegin()
{
handle_config_changed();
return;
}