#include <syslog.h>
#include <stdlib.h>
#include <unistd.h>
#include <stdio.h>
#include <sys/ioctl.h>
#include <sys/sockio.h>
#include <sys/socket.h>
#include <string.h>
#include <net/if.h>
#include <net/if_media.h>
#include <net/ethernet.h>
#include <sys/errno.h>
#include <sys/queue.h>
#include <mach/mach.h>
#include <mach/message.h>
#include <mach/mach_error.h>
#include <sys/param.h>
#include <sys/types.h>
#include <fcntl.h>
#include <paths.h>
#include <pwd.h>
#include <pthread.h>
#include <SystemConfiguration/SystemConfiguration.h>
#include <SystemConfiguration/SCValidation.h>
#include <SystemConfiguration/SCPrivate.h>
#include <CoreFoundation/CFDictionary.h>
#include <CoreFoundation/CFMachPort.h>
#include <CoreFoundation/CFRunLoop.h>
#include <SystemConfiguration/SCDPlugin.h>
#include <TargetConditionals.h>
#include "mylog.h"
#include <EAP8021X/myCFUtil.h>
#include "controller.h"
#include "server.h"
#if ! TARGET_OS_EMBEDDED
#include <CoreFoundation/CFSocket.h>
#include "EAPOLClientConfiguration.h"
#include "EAPOLClientConfigurationPrivate.h"
#include "EAPOLControlPrivate.h"
#include "EAPOLControlTypesPrivate.h"
#endif
#include "ClientControlInterface.h"
#include "EAPOLControlTypes.h"
#include "EAPClientProperties.h"
#include "eapol_socket.h"
#ifndef kSCEntNetRefreshConfiguration
#define kSCEntNetRefreshConfiguration CFSTR("RefreshConfiguration")
#endif
#ifndef kSCEntNetEAPOL
#define kSCEntNetEAPOL CFSTR("EAPOL")
#endif
#define kSystemModeManagedExternally CFSTR("_SystemModeManagedExternally")
static SCDynamicStoreRef S_store;
static char * S_eapolclient_path = NULL;
struct eapolClient_s;
#define LIST_HEAD_clientHead LIST_HEAD(clientHead, eapolClient_s)
static LIST_HEAD_clientHead S_head;
static struct clientHead * S_clientHead_p = &S_head;
#define LIST_ENTRY_eapolClient_s LIST_ENTRY(eapolClient_s)
typedef struct eapolClient_s {
LIST_ENTRY_eapolClient_s link;
EAPOLControlState state;
if_name_t if_name;
CFStringRef if_name_cf;
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;
mach_port_t au_session;
CFMachPortRef session_cfport;
boolean_t notification_sent;
boolean_t retry;
boolean_t user_input_provided;
boolean_t console_user;
EAPOLControlMode mode;
CFDictionaryRef config_dict;
CFDictionaryRef user_input_dict;
CFDictionaryRef status_dict;
#if ! TARGET_OS_EMBEDDED
CFDictionaryRef loginwindow_config;
CFSocketRef eapol_sock;
int eapol_fd;
bool packet_received;
uint64_t packet_received_time;
struct ether_addr authenticator_mac;
bool user_cancelled;
#endif
} eapolClient, *eapolClientRef;
#if TARGET_OS_EMBEDDED
static __inline__ boolean_t
is_console_user(uid_t check_uid)
{
return (TRUE);
}
#else
static void
clear_loginwindow_config(eapolClientRef client)
{
my_CFRelease(&client->loginwindow_config);
return;
}
static void
set_loginwindow_config(eapolClientRef client)
{
CFDictionaryRef itemID_dict;
clear_loginwindow_config(client);
if (client->config_dict == NULL) {
return;
}
itemID_dict = CFDictionaryGetValue(client->config_dict,
kEAPOLControlClientItemID);
if (isA_CFDictionary(itemID_dict) != NULL) {
CFStringRef key;
key = kEAPOLControlClientItemID;
client->loginwindow_config
= CFDictionaryCreate(NULL,
(const void * *)&key,
(const void * *)&itemID_dict, 1,
&kCFTypeDictionaryKeyCallBacks,
&kCFTypeDictionaryValueCallBacks);
}
return;
}
static uid_t
login_window_uid(void)
{
static uid_t login_window_uid = -1;
if (login_window_uid == -1) {
struct passwd * pwd = getpwnam("_securityagent");
if (pwd == NULL) {
my_log(LOG_NOTICE,
"EAPOLController: getpwnam(_securityagent) failed");
return (92);
}
login_window_uid = pwd->pw_uid;
}
return (login_window_uid);
}
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);
}
static CFDictionaryRef
S_profile_copy_itemID_dict(EAPOLClientProfileRef profile)
{
CFStringRef key;
CFDictionaryRef dict;
EAPOLClientItemIDRef itemID;
CFDictionaryRef itemID_dict;
itemID = EAPOLClientItemIDCreateWithProfile(profile);
itemID_dict = EAPOLClientItemIDCopyDictionary(itemID);
CFRelease(itemID);
key = kEAPOLControlClientItemID;
dict = CFDictionaryCreate(NULL,
(const void * *)&key,
(const void * *)&itemID_dict, 1,
&kCFTypeDictionaryKeyCallBacks,
&kCFTypeDictionaryValueCallBacks);
CFRelease(itemID_dict);
return (dict);
}
#endif
#if 0
#endif 0
static int
get_ifm_type(const char * name)
{
int i;
struct ifmediareq ifm;
int media_static[20];
int media_static_count = sizeof(media_static) / sizeof(media_static[0]);
int s;
int ifm_type = 0;
bool supports_full_duplex = FALSE;
s = socket(AF_INET, SOCK_DGRAM, 0);
if (s < 0) {
perror("socket");
goto done;
}
bzero(&ifm, sizeof(ifm));
strlcpy(ifm.ifm_name, name, sizeof(ifm.ifm_name));
if (ioctl(s, SIOCGIFMEDIA, (caddr_t)&ifm) < 0) {
goto done;
}
ifm_type = IFM_TYPE(ifm.ifm_current);
if (ifm_type != IFM_ETHER) {
goto done;
}
if (ifm.ifm_count == 0) {
goto done;
}
if (ifm.ifm_count > media_static_count) {
ifm.ifm_ulist = (int *)malloc(ifm.ifm_count * sizeof(int));
}
else {
ifm.ifm_ulist = media_static;
}
if (ioctl(s, SIOCGIFMEDIA, (caddr_t)&ifm) == -1) {
goto done;
}
if (ifm.ifm_count == 1
&& IFM_SUBTYPE(ifm.ifm_ulist[0]) == IFM_AUTO) {
goto done;
}
for (i = 0; i < ifm.ifm_count; i++) {
if ((ifm.ifm_ulist[i] & IFM_FDX) != 0) {
supports_full_duplex = TRUE;
break;
}
}
done:
if (s >= 0) {
close(s);
}
if (ifm_type == IFM_ETHER && supports_full_duplex == FALSE) {
ifm_type = 0;
}
return (ifm_type);
}
static int
eapolClientStop(eapolClientRef client);
static void
eapolClientSetState(eapolClientRef client, EAPOLControlState state);
static CFNumberRef
make_number(int val)
{
return (CFNumberCreate(NULL, kCFNumberIntType, &val));
}
eapolClientRef
eapolClientLookupInterfaceCF(CFStringRef if_name_cf)
{
eapolClientRef scan;
LIST_FOREACH(scan, S_clientHead_p, link) {
if (CFEqual(if_name_cf, scan->if_name_cf)) {
return (scan);
}
}
return (NULL);
}
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));
strlcpy(client->if_name, if_name, sizeof(client->if_name));
client->if_name_cf = CFStringCreateWithCString(NULL, client->if_name,
kCFStringEncodingASCII);
client->pid = -1;
#if ! TARGET_OS_EMBEDDED
client->eapol_fd = -1;
#endif
LIST_INSERT_HEAD(S_clientHead_p, client, link);
return (client);
}
void
eapolClientRemove(eapolClientRef client)
{
#if ! TARGET_OS_EMBEDDED
clear_loginwindow_config(client);
#endif
my_CFRelease(&client->if_name_cf);
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->mode = kEAPOLControlModeNone;
client->retry = FALSE;
client->notification_sent = FALSE;
client->console_user = FALSE;
client->user_input_provided = FALSE;
my_CFRelease(&client->notification_key);
my_CFRelease(&client->force_renew_key);
if (client->notify_port != MACH_PORT_NULL) {
(void)mach_port_deallocate(mach_task_self(), client->notify_port);
client->notify_port = MACH_PORT_NULL;
}
if (client->bootstrap != MACH_PORT_NULL) {
(void)mach_port_deallocate(mach_task_self(), client->bootstrap);
client->bootstrap = MACH_PORT_NULL;
}
if (client->au_session != MACH_PORT_NULL) {
(void)mach_port_deallocate(mach_task_self(), client->au_session);
client->au_session = MACH_PORT_NULL;
}
if (client->session_cfport != NULL) {
CFMachPortInvalidate(client->session_cfport);
my_CFRelease(&client->session_cfport);
}
my_CFRelease(&client->config_dict);
my_CFRelease(&client->user_input_dict);
my_CFRelease(&client->status_dict);
return;
}
static void
eapolClientNotify(eapolClientRef client)
{
mach_msg_empty_send_t msg;
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;
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)
{
if (client->notification_key == NULL) {
client->notification_key
= SCDynamicStoreKeyCreateNetworkInterfaceEntity(NULL,
kSCDynamicStoreDomainState,
client->if_name_cf,
kSCEntNetEAPOL);
}
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)
{
CFRetain(status_dict);
my_CFRelease(&client->status_dict);
client->status_dict = status_dict;
if (S_store != NULL) {
SCDynamicStoreNotifyValue(S_store, eapolClientNotificationKey(client));
}
return;
}
static CFStringRef
eapolClientForceRenewKey(eapolClientRef client)
{
if (client->force_renew_key == NULL) {
client->force_renew_key
= SCDynamicStoreKeyCreateNetworkInterfaceEntity(NULL,
kSCDynamicStoreDomainState,
client->if_name_cf,
kSCEntNetRefreshConfiguration);
}
return (client->force_renew_key);
}
static void
eapolClientForceRenew(eapolClientRef client)
{
if (S_store == NULL) {
return;
}
SCDynamicStoreNotifyValue(S_store, eapolClientForceRenewKey(client));
return;
}
#if TARGET_OS_EMBEDDED
static void
eapolClientExited(eapolClientRef client)
{
return;
}
#else
#include "EAP.h"
#include "EAPOLUtil.h"
static void
handle_config_changed(boolean_t check_system_mode);
#define RECV_SIZE 1600
static void
monitoring_callback(CFSocketRef s, CFSocketCallBackType type,
CFDataRef address, const void * data, void * info)
{
uint32_t buf[RECV_SIZE / sizeof(uint32_t)];
eapolClientRef client = (eapolClientRef)info;
uint64_t current_time;
struct ether_header * eh_p = (struct ether_header *)buf;
EAPOLPacketRef eapol_p;
int n;
EAPRequestPacketRef req_p;
n = recv(client->eapol_fd, buf, sizeof(buf), 0);
if (n < sizeof(*eh_p)) {
if (n < 0) {
my_log(LOG_NOTICE, "EAPOLController: monitor %s recv failed %s",
client->if_name,
strerror(errno));
}
return;
}
eapol_p = (EAPOLPacketRef)(eh_p + 1);
if (EAPOLPacketValid(eapol_p, n - sizeof(*eh_p), NULL) == FALSE) {
return;
}
req_p = (EAPRequestPacketRef)eapol_p->body;
if (eapol_p->packet_type != kEAPOLPacketTypeEAPPacket
|| req_p->code != kEAPCodeRequest
|| req_p->type != kEAPTypeIdentity) {
return;
}
current_time = (uint64_t)CFAbsoluteTimeGetCurrent();
if (client->packet_received == FALSE
|| client->packet_received_time != current_time) {
bcopy(eh_p->ether_shost, &client->authenticator_mac,
sizeof(client->authenticator_mac));
client->packet_received = TRUE;
client->packet_received_time = current_time;
syslog(LOG_DEBUG, "EAPOLController: %s requires 802.1X",
client->if_name);
if (S_store != NULL) {
SCDynamicStoreNotifyValue(S_store,
kEAPOLControlAutoDetectInformationNotifyKey);
}
}
return;
}
static void
eapolClientStopMonitoring(eapolClientRef client)
{
if (client->eapol_fd == -1) {
return;
}
if (client->eapol_sock != NULL) {
CFSocketInvalidate(client->eapol_sock);
my_CFRelease(&client->eapol_sock);
}
else {
close(client->eapol_fd);
}
client->eapol_fd = -1;
client->packet_received = FALSE;
syslog(LOG_DEBUG, "EAPOLController: no longer monitoring %s",
client->if_name);
return;
}
static void
eapolClientStartMonitoring(eapolClientRef client)
{
CFSocketContext context = { 0, NULL, NULL, NULL, NULL };
CFRunLoopSourceRef rls;
if (client->eapol_fd != -1) {
syslog(LOG_DEBUG, "EAPOLController: already monitoring %s",
client->if_name);
return;
}
my_log(LOG_DEBUG,
"EAPOLController: starting monitoring on %s", client->if_name);
client->eapol_fd = eapol_socket(client->if_name, FALSE);
if (client->eapol_fd < 0) {
syslog(LOG_NOTICE,
"EAPOLController: failed to open EAPOL socket over %s",
client->if_name);
return;
}
context.info = client;
client->eapol_sock
= CFSocketCreateWithNative(NULL, client->eapol_fd,
kCFSocketReadCallBack,
monitoring_callback, &context);
if (client->eapol_sock == NULL) {
syslog(LOG_NOTICE,
"EAPOLController: failed create CFSocket over %s",
client->if_name);
goto failed;
}
rls = CFSocketCreateRunLoopSource(NULL, client->eapol_sock, 0);
if (rls == NULL) {
syslog(LOG_NOTICE,
"EAPOLController: failed create CFRunLoopSource for %s",
client->if_name);
goto failed;
}
CFRunLoopAddSource(CFRunLoopGetCurrent(), rls, kCFRunLoopDefaultMode);
CFRelease(rls);
return;
failed:
eapolClientStopMonitoring(client);
return;
}
static void
eapolClientExited(eapolClientRef client)
{
boolean_t no_one_logged_in;
CFStringRef user;
if (S_store == NULL) {
return;
}
user = SCDynamicStoreCopyConsoleUser(S_store, NULL, NULL);
if (user == NULL) {
no_one_logged_in = TRUE;
}
else {
CFRelease(user);
no_one_logged_in = FALSE;
}
handle_config_changed(no_one_logged_in);
return;
}
#endif
static void
exec_callback(pid_t pid, int status, struct rusage * rusage, void * context)
{
eapolClientRef client;
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);
}
eapolClientInvalidate(client);
eapolClientExited(client);
return;
}
typedef struct {
int eapol_fd;
eapolClientRef client;
} exec_context_t;
static void
exec_setup(pid_t pid, void * context)
{
int fd;
int i;
exec_context_t * ec_p = (exec_context_t *)context;
if (pid != 0) {
#if TARGET_OS_EMBEDDED
close(ec_p->eapol_fd);
#else
if (ec_p->client->eapol_fd == ec_p->eapol_fd) {
eapolClientStopMonitoring(ec_p->client);
}
else {
close(ec_p->eapol_fd);
}
#endif
return;
}
for (i = (getdtablesize() - 1); i >= 0; i--) {
if (i != ec_p->eapol_fd) {
close(i);
}
}
if (ec_p->eapol_fd != STDIN_FILENO) {
dup(ec_p->eapol_fd);
close(ec_p->eapol_fd);
}
fd = open(_PATH_DEVNULL, O_RDWR, 0);
dup(fd);
return;
}
static int
open_eapol_socket(eapolClientRef client)
{
#if ! TARGET_OS_EMBEDDED
if (client->eapol_fd != -1) {
return (client->eapol_fd);
}
#endif
return (eapol_socket(client->if_name,
(get_ifm_type(client->if_name)
== IFM_IEEE80211)));
}
static int
eapolClientStart(eapolClientRef client, uid_t uid, gid_t gid,
CFDictionaryRef config_dict, mach_port_t bootstrap,
mach_port_t au_session)
{
char * argv[] = { S_eapolclient_path,
"-i",
client->if_name,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL };
exec_context_t ec;
char gid_str[32];
int status = 0;
char uid_str[32];
#if ! TARGET_OS_EMBEDDED
client->user_cancelled = FALSE;
#endif
bzero(&ec, sizeof(ec));
ec.client = client;
ec.eapol_fd = open_eapol_socket(client);
if (ec.eapol_fd < 0) {
syslog(LOG_NOTICE,
"EAPOLController: failed to open EAPOL socket over %s",
client->if_name);
return (errno);
}
if (bootstrap != MACH_PORT_NULL) {
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;
}
client->pid = _SCDPluginExecCommand2(exec_callback, NULL, 0, 0,
S_eapolclient_path, argv,
exec_setup, &ec);
if (client->pid == -1) {
exec_setup(-1, &ec);
status = errno;
}
else {
boolean_t on_console = FALSE;
if (bootstrap != MACH_PORT_NULL) {
on_console = is_console_user(uid);
client->owner.uid = uid;
client->owner.gid = gid;
client->console_user = on_console;
#if TARGET_OS_EMBEDDED
client->mode = kEAPOLControlModeUser;
client->bootstrap = bootstrap;
#else
if (on_console == FALSE
&& uid == login_window_uid()) {
client->mode = kEAPOLControlModeLoginWindow;
}
else {
client->mode = kEAPOLControlModeUser;
client->bootstrap = bootstrap;
client->au_session = au_session;
}
#endif
}
else {
client->mode = kEAPOLControlModeSystem;
}
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);
my_CFRelease(&client->user_input_dict);
client->config_dict = CFRetain(config_dict);
client->retry = FALSE;
client->user_input_provided = FALSE;
done:
return (status);
}
static int
eapolClientProvideUserInput(eapolClientRef client, CFDictionaryRef user_input)
{
int status = 0;
switch (client->state) {
case kEAPOLControlStateRunning:
break;
case kEAPOLControlStateStarting:
status = EINVAL;
goto done;
case kEAPOLControlStateIdle:
status = ENOENT;
goto done;
default:
status = EBUSY;
goto done;
}
eapolClientNotify(client);
my_CFRelease(&client->user_input_dict);
if (user_input != NULL) {
client->user_input_dict = CFRetain(user_input);
}
client->retry = FALSE;
client->user_input_provided = TRUE;
done:
return (status);
}
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);
}
int
ControllerStart(if_name_t if_name, uid_t uid, gid_t gid,
CFDictionaryRef config_dict, mach_port_t bootstrap,
mach_port_t au_session)
{
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 {
int ifm_type;
ifm_type = get_ifm_type(if_name);
switch (ifm_type) {
case IFM_ETHER:
case IFM_IEEE80211:
break;
default:
status = ENXIO;
goto done;
}
client = eapolClientAdd(if_name);
if (client == NULL) {
status = ENOMEM;
goto done;
}
}
#if TARGET_OS_EMBEDDED
if (uid == 0) {
static gid_t mobile_gid = -1;
static uid_t mobile_uid = -1;
if (mobile_uid == -1) {
struct passwd * pwd;
pwd = getpwnam("mobile");
if (pwd != NULL) {
mobile_uid = pwd->pw_uid;
mobile_gid = pwd->pw_gid;
}
else {
my_log(LOG_NOTICE,
"EAPOLController: getpwnam(mobile) failed");
}
}
if (mobile_uid != -1) {
uid = mobile_uid;
if (mobile_gid != -1) {
gid = mobile_gid;
}
}
}
#endif
status = eapolClientStart(client, uid, gid, config_dict, bootstrap,
au_session);
done:
if (status != 0) {
if (bootstrap != MACH_PORT_NULL) {
(void)mach_port_deallocate(mach_task_self(), bootstrap);
}
if (au_session != MACH_PORT_NULL) {
(void)mach_port_deallocate(mach_task_self(), au_session);
}
}
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
done:
return (status);
}
#if ! TARGET_OS_EMBEDDED
static boolean_t
S_get_plist_boolean(CFDictionaryRef plist, CFStringRef key, boolean_t def)
{
CFBooleanRef b;
boolean_t ret = def;
b = isA_CFBoolean(CFDictionaryGetValue(plist, key));
if (b != NULL) {
ret = CFBooleanGetValue(b);
}
return (ret);
}
#endif
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 TARGET_OS_EMBEDDED
if (uid != 0 && uid != client->owner.uid) {
status = EPERM;
goto done;
}
#else
if (uid != 0) {
if (uid != client->owner.uid) {
if (client->mode == kEAPOLControlModeSystem
&& (client->config_dict == NULL
|| S_get_plist_boolean(client->config_dict,
CFSTR("AllowStop"), TRUE))
&& (uid == login_window_uid() || is_console_user(uid))) {
}
else {
status = EPERM;
goto done;
}
}
else if (client->mode == kEAPOLControlModeLoginWindow
&& uid == login_window_uid()) {
clear_loginwindow_config(client);
}
}
#endif
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
ControllerProvideUserInput(if_name_t if_name, uid_t uid, gid_t gid,
CFDictionaryRef user_input)
{
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 = eapolClientProvideUserInput(client, user_input);
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 = make_number(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);
}
#if ! TARGET_OS_EMBEDDED
static CFDictionaryRef
system_eapol_copy(const char * if_name)
{
CFDictionaryRef dict = NULL;
EAPOLClientConfigurationRef cfg;
cfg = EAPOLClientConfigurationCreate(NULL);
if (cfg != NULL) {
CFStringRef if_name_cf;
EAPOLClientProfileRef profile = NULL;
if_name_cf = CFStringCreateWithCString(NULL, if_name,
kCFStringEncodingASCII);
profile = EAPOLClientConfigurationGetSystemProfile(cfg, if_name_cf);
CFRelease(if_name_cf);
if (profile != NULL) {
dict = S_profile_copy_itemID_dict(profile);
}
CFRelease(cfg);
}
return (dict);
}
int
ControllerStartSystem(if_name_t if_name, uid_t uid, gid_t gid,
CFDictionaryRef options)
{
eapolClientRef client;
CFDictionaryRef dict = NULL;
CFDictionaryRef itemID_dict = NULL;
int status = 0;
client = eapolClientLookupInterface(if_name);
if (client != NULL && client->state != kEAPOLControlStateIdle) {
if (client->state == kEAPOLControlStateRunning) {
status = EEXIST;
}
else {
status = EBUSY;
}
goto done;
}
if (uid != 0
&& uid != login_window_uid()
&& is_console_user(uid) == FALSE) {
status = EPERM;
goto done;
}
if (options != NULL) {
itemID_dict
= CFDictionaryGetValue(options, kEAPOLControlClientItemID);
if (isA_CFDictionary(itemID_dict) != NULL) {
CFMutableDictionaryRef new_dict;
new_dict = CFDictionaryCreateMutableCopy(NULL, 0, options);
CFDictionarySetValue(new_dict, kSystemModeManagedExternally,
kCFBooleanTrue);
dict = new_dict;
}
}
if (dict == NULL) {
dict = system_eapol_copy(if_name);
if (dict == NULL) {
status = ESRCH;
goto done;
}
}
if (client == NULL) {
client = eapolClientAdd(if_name);
if (client == NULL) {
status = ENOMEM;
goto done;
}
}
status = eapolClientStart(client, 0, 0, dict, MACH_PORT_NULL,
MACH_PORT_NULL);
done:
my_CFRelease(&dict);
return (status);
}
int
ControllerCopyLoginWindowConfiguration(if_name_t if_name,
CFDictionaryRef * config_data_p)
{
eapolClientRef client;
int status = 0;
*config_data_p = NULL;
client = eapolClientLookupInterface(if_name);
if (client == NULL
|| client->loginwindow_config == NULL) {
status = ENOENT;
}
else {
*config_data_p = CFRetain(client->loginwindow_config);
}
return (status);
}
int
ControllerCopyAutoDetectInformation(CFDictionaryRef * info_p)
{
CFMutableDictionaryRef all_dict = NULL;
eapolClientRef scan;
int status = 0;
LIST_FOREACH(scan, S_clientHead_p, link) {
CFDataRef data;
int elapsed_time;
CFNumberRef num;
CFMutableDictionaryRef this_dict = NULL;
if (scan->state != kEAPOLControlStateIdle
|| scan->eapol_fd == -1
|| scan->packet_received == FALSE) {
continue;
}
elapsed_time = ((uint64_t)CFAbsoluteTimeGetCurrent())
- scan->packet_received_time;
if (elapsed_time < 0) {
elapsed_time = 0;
}
else if (elapsed_time > 60) {
continue;
}
this_dict = CFDictionaryCreateMutable(NULL, 0,
&kCFTypeDictionaryKeyCallBacks,
&kCFTypeDictionaryValueCallBacks);
num = make_number(elapsed_time);
CFDictionarySetValue(this_dict, kEAPOLAutoDetectSecondsSinceLastPacket,
num);
CFRelease(num);
data = CFDataCreate(NULL, (const UInt8 *)&scan->authenticator_mac,
sizeof(scan->authenticator_mac));
CFDictionarySetValue(this_dict, kEAPOLAutoDetectAuthenticatorMACAddress,
data);
CFRelease(data);
if (all_dict == NULL) {
all_dict = CFDictionaryCreateMutable(NULL, 0,
&kCFTypeDictionaryKeyCallBacks,
&kCFTypeDictionaryValueCallBacks);
}
CFDictionarySetValue(all_dict, scan->if_name_cf, this_dict);
CFRelease(this_dict);
}
if (all_dict != NULL && CFDictionaryGetCount(all_dict) == 0) {
status = ENOENT;
my_CFRelease(&all_dict);
}
*info_p = all_dict;
return (status);
}
boolean_t
ControllerDidUserCancel(if_name_t if_name)
{
boolean_t cancelled = FALSE;
eapolClientRef client;
client = eapolClientLookupInterface(if_name);
if (client == NULL) {
cancelled = FALSE;
}
else {
cancelled = client->user_cancelled;
}
return (cancelled);
}
#endif
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,
mach_port_t * au_session)
{
CFMutableDictionaryRef dict;
CFNumberRef command_cf;
eapolClientRef client;
int result = 0;
CFRunLoopSourceRef rls;
*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);
rls = CFMachPortCreateRunLoopSource(NULL, client->session_cfport, 0);
CFRunLoopAddSource(CFRunLoopGetCurrent(), rls, kCFRunLoopDefaultMode);
CFRelease(rls);
dict = CFDictionaryCreateMutable(NULL, 0,
&kCFTypeDictionaryKeyCallBacks,
&kCFTypeDictionaryValueCallBacks);
if (client->state == kEAPOLControlStateStarting) {
CFNumberRef mode_cf;
command_cf = make_number(kEAPOLClientControlCommandRun);
if (client->config_dict != NULL) {
CFDictionarySetValue(dict, kEAPOLClientControlConfiguration,
client->config_dict);
}
mode_cf = make_number(client->mode);
CFDictionarySetValue(dict, kEAPOLClientControlMode,
mode_cf);
CFRelease(mode_cf);
}
else {
command_cf = make_number(kEAPOLClientControlCommandStop);
}
CFDictionarySetValue(dict, kEAPOLClientControlCommand, command_cf);
CFRelease(command_cf);
*control_dict = dict;
eapolClientSetState(client, kEAPOLControlStateRunning);
*bootstrap = client->bootstrap;
*au_session = client->au_session;
#if ! TARGET_OS_EMBEDDED
if (client->mode == kEAPOLControlModeLoginWindow) {
set_loginwindow_config(client);
}
#endif
return (result);
failed:
(void)mach_port_deallocate(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;
}
eapolClientInvalidate(client);
eapolClientExited(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_number(kEAPOLClientControlCommandRetry);
client->retry = FALSE;
}
else if (client->user_input_provided) {
command_cf
= make_number(kEAPOLClientControlCommandTakeUserInput);
if (client->user_input_dict != NULL) {
CFDictionarySetValue(dict,
kEAPOLClientControlUserInput,
client->user_input_dict);
}
client->user_input_provided = FALSE;
my_CFRelease(&client->user_input_dict);
}
else {
command_cf = make_number(kEAPOLClientControlCommandRun);
if (client->config_dict != NULL) {
CFDictionarySetValue(dict, kEAPOLClientControlConfiguration,
client->config_dict);
}
}
}
else {
command_cf = make_number(kEAPOLClientControlCommandStop);
}
CFDictionarySetValue(dict, kEAPOLClientControlCommand, command_cf);
*control_dict = dict;
client->notification_sent = 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;
}
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);
}
#if ! TARGET_OS_EMBEDDED
int
ControllerClientUserCancelled(mach_port_t session_port)
{
eapolClientRef client;
int result = 0;
client = eapolClientLookupSession(session_port);
if (client == NULL) {
result = EINVAL;
goto failed;
}
client->user_cancelled = TRUE;
result = eapolClientStop(client);
failed:
return (result);
}
#endif
#if TARGET_OS_EMBEDDED
static SCDynamicStoreRef
dynamic_store_create(void)
{
SCDynamicStoreRef store;
store = SCDynamicStoreCreate(NULL, CFSTR("EAPOLController"), NULL, NULL);
if (store == NULL) {
my_log(LOG_NOTICE, "EAPOLController: SCDynamicStoreCreate() failed, %s",
SCErrorString(SCError()));
}
return (store);
}
static void *
ControllerThread(void * arg)
{
server_start();
CFRunLoopRun();
return (arg);
}
static void
ControllerBegin(void)
{
pthread_attr_t attr;
int ret;
pthread_t thread;
ret = pthread_attr_init(&attr);
if (ret != 0) {
my_log(LOG_NOTICE, "EAPOLController: pthread_attr_init failed %d", ret);
return;
}
ret = pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
if (ret != 0) {
my_log(LOG_NOTICE,
"EAPOLController: pthread_attr_setdetachstate failed %d", ret);
goto done;
}
ret = pthread_create(&thread, &attr, ControllerThread, NULL);
if (ret != 0) {
my_log(LOG_NOTICE, "EAPOLController: pthread_create failed %d", ret);
goto done;
}
done:
(void)pthread_attr_destroy(&attr);
return;
}
#else
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);
clear_loginwindow_config(scan);
}
}
else if (user == NULL) {
clear_loginwindow_config(scan);
}
}
my_CFRelease(&user);
return;
}
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 CFArrayRef
copy_interface_list(void)
{
CFDictionaryRef dict;
CFArrayRef iflist = NULL;
CFStringRef key;
key = SCDynamicStoreKeyCreateNetworkInterface(NULL,
kSCDynamicStoreDomainState);
dict = SCDynamicStoreCopyValue(S_store, key);
my_CFRelease(&key);
if (isA_CFDictionary(dict) != NULL) {
iflist = CFDictionaryGetValue(dict, kSCPropNetInterfaces);
iflist = isA_CFArray(iflist);
}
if (iflist != NULL) {
CFRetain(iflist);
}
else {
iflist = CFArrayCreate(NULL, NULL, 0, &kCFTypeArrayCallBacks);
}
my_CFRelease(&dict);
return (iflist);
}
typedef struct {
EAPOLClientConfigurationRef cfg;
CFMutableArrayRef configured_iflist;
CFRange configured_iflist_range;
CFMutableDictionaryRef system_mode_configurations;
CFMutableArrayRef system_mode_iflist;
} EAPOLEthernetInfo, * EAPOLEthernetInfoRef;
static void
EAPOLEthernetInfoProcess(const void * key, const void * value, void * context)
{
EAPOLEthernetInfoRef info_p = (EAPOLEthernetInfoRef)context;
if (isA_CFDictionary(value) == NULL) {
return;
}
if (CFStringHasSuffix(key, kSCEntNetEAPOL)) {
CFStringRef name;
EAPOLClientProfileRef profile = NULL;
name = mySCNetworkInterfacePathCopyInterfaceName(key);
if (info_p->cfg == NULL) {
info_p->cfg = EAPOLClientConfigurationCreate(NULL);
}
if (info_p->cfg != NULL) {
profile
= EAPOLClientConfigurationGetSystemProfile(info_p->cfg, name);
}
if (profile != NULL) {
CFDictionaryRef this_config = NULL;
this_config = S_profile_copy_itemID_dict(profile);
if (this_config != NULL) {
CFDictionarySetValue(info_p->system_mode_configurations,
name, this_config);
CFRelease(this_config);
CFArrayAppendValue(info_p->system_mode_iflist, name);
}
}
my_CFRelease(&name);
}
else {
int ifm_type = 0;
char ifname[IFNAMSIZ];
CFStringRef name;
CFStringRef type;
type = CFDictionaryGetValue(value, kSCPropNetInterfaceType);
if (type == NULL
|| CFEqual(type, kSCValNetInterfaceTypeEthernet) == FALSE) {
return;
}
name = CFDictionaryGetValue(value, kSCPropNetInterfaceDeviceName);
if (isA_CFString(name) == NULL) {
return;
}
if (CFStringGetCString(name, ifname, sizeof(ifname),
kCFStringEncodingASCII) == FALSE) {
return;
}
if (CFArrayContainsValue(info_p->configured_iflist,
info_p->configured_iflist_range, name)) {
return;
}
ifm_type = get_ifm_type(ifname);
if (ifm_type != IFM_ETHER) {
return;
}
CFArrayAppendValue(info_p->configured_iflist, name);
info_p->configured_iflist_range.length++;
}
return;
}
static void
EAPOLEthernetInfoInit(EAPOLEthernetInfoRef info_p, boolean_t system_mode)
{
int count;
int i;
CFStringRef list[2];
CFArrayRef patterns;
CFDictionaryRef store_info = NULL;
bzero(info_p, sizeof(*info_p));
count = 0;
list[count++]
= SCDynamicStoreKeyCreateNetworkServiceEntity(NULL,
kSCDynamicStoreDomainSetup,
kSCCompAnyRegex,
kSCEntNetInterface);
if (system_mode) {
list[count++] = SCDynamicStoreKeyCreateNetworkInterfaceEntity(NULL,
kSCDynamicStoreDomainSetup,
kSCCompAnyRegex,
kSCEntNetEAPOL);
}
patterns = CFArrayCreate(NULL, (const void * *)list, count,
&kCFTypeArrayCallBacks);
for (i = 0; i < count; i++) {
CFRelease(list[i]);
}
store_info = SCDynamicStoreCopyMultiple(S_store, NULL, patterns);
my_CFRelease(&patterns);
if (store_info == NULL) {
return;
}
info_p->configured_iflist
= CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
if (system_mode) {
info_p->system_mode_configurations
= CFDictionaryCreateMutable(NULL, 0,
&kCFTypeDictionaryKeyCallBacks,
&kCFTypeDictionaryValueCallBacks);
info_p->system_mode_iflist
= CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
}
CFDictionaryApplyFunction(store_info, EAPOLEthernetInfoProcess,
(void *)info_p);
CFRelease(store_info);
return;
}
static void
EAPOLEthernetInfoFree(EAPOLEthernetInfoRef info_p)
{
my_CFRelease(&info_p->cfg);
my_CFRelease(&info_p->configured_iflist);
my_CFRelease(&info_p->system_mode_configurations);
my_CFRelease(&info_p->system_mode_iflist);
return;
}
static void
update_system_mode_interfaces(CFDictionaryRef system_mode_configurations,
CFArrayRef system_mode_iflist)
{
CFArrayRef current_iflist = NULL;
int i;
CFRange range;
eapolClientRef scan;
int status;
current_iflist = copy_interface_list();
range = CFRangeMake(0, CFArrayGetCount(current_iflist));
LIST_FOREACH(scan, S_clientHead_p, link) {
CFDictionaryRef this_config = NULL;
if (CFArrayContainsValue(current_iflist,
range,
scan->if_name_cf) == FALSE) {
if (scan->state == kEAPOLControlStateIdle) {
eapolClientStopMonitoring(scan);
}
else {
(void)eapolClientStop(scan);
}
continue;
}
this_config
= CFDictionaryGetValue(system_mode_configurations,
scan->if_name_cf);
if (scan->mode == kEAPOLControlModeSystem) {
if (scan->config_dict == NULL) {
continue;
}
if (CFDictionaryContainsKey(scan->config_dict,
kSystemModeManagedExternally)) {
continue;
}
if (this_config == NULL) {
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(this_config, scan->config_dict) == FALSE) {
status = eapolClientUpdate(scan, this_config);
if (status != 0) {
my_log(LOG_NOTICE,
"EAPOLController handle_config_changed: "
"eapolClientUpdate (%s) failed %d",
scan->if_name, status);
}
}
}
}
else if (this_config != NULL) {
if (scan->state == kEAPOLControlStateIdle) {
status = eapolClientStart(scan, 0, 0,
this_config, MACH_PORT_NULL,
MACH_PORT_NULL);
if (status != 0) {
my_log(LOG_NOTICE,
"EAPOLController handle_config_changed:"
" eapolClientStart (%s) failed %d",
scan->if_name, status);
}
}
}
}
range = CFRangeMake(0, CFArrayGetCount(system_mode_iflist));
for (i = 0; i < range.length; i++) {
eapolClientRef client;
CFStringRef if_name_cf;
if_name_cf = CFArrayGetValueAtIndex(system_mode_iflist, i);
client = eapolClientLookupInterfaceCF(if_name_cf);
if (client == NULL) {
char * if_name;
if_name = my_CFStringToCString(if_name_cf, kCFStringEncodingASCII);
client = eapolClientAdd(if_name);
if (client == NULL) {
my_log(LOG_NOTICE,
"EAPOLController handle_config_changed:"
" eapolClientAdd (%s) failed", if_name);
}
else {
CFDictionaryRef this_config;
this_config
= CFDictionaryGetValue(system_mode_configurations,
if_name_cf);
status = eapolClientStart(client, 0, 0,
this_config, MACH_PORT_NULL,
MACH_PORT_NULL);
if (status != 0) {
my_log(LOG_NOTICE,
"EAPOLController handle_config_changed:"
" eapolClientStart (%s) failed %d",
client->if_name, status);
}
}
free(if_name);
}
}
my_CFRelease(¤t_iflist);
return;
}
static void
update_monitored_interfaces(CFArrayRef configured_iflist)
{
int i;
CFRange range;
eapolClientRef scan;
range = CFRangeMake(0, CFArrayGetCount(configured_iflist));
LIST_FOREACH(scan, S_clientHead_p, link) {
if (CFArrayContainsValue(configured_iflist,
range,
scan->if_name_cf) == FALSE) {
if (scan->state == kEAPOLControlStateIdle) {
eapolClientStopMonitoring(scan);
}
}
}
for (i = 0; i < range.length; i++) {
eapolClientRef client;
CFStringRef if_name_cf;
if_name_cf
= (CFStringRef)CFArrayGetValueAtIndex(configured_iflist, i);
client = eapolClientLookupInterfaceCF(if_name_cf);
if (client == NULL
|| (client->state == kEAPOLControlStateIdle
&& client->eapol_fd == -1)) {
char * if_name;
if_name = my_CFStringToCString(if_name_cf, kCFStringEncodingASCII);
if (client == NULL) {
client = eapolClientAdd(if_name);
if (client == NULL) {
my_log(LOG_NOTICE,
"EAPOLController: monitor "
"eapolClientAdd (%s) failed", if_name);
}
}
if (client != NULL) {
eapolClientStartMonitoring(client);
}
free(if_name);
}
}
return;
}
static void
handle_config_changed(boolean_t check_system_mode)
{
EAPOLEthernetInfo info;
if (S_store == NULL) {
return;
}
EAPOLEthernetInfoInit(&info, check_system_mode);
if (check_system_mode) {
update_system_mode_interfaces(info.system_mode_configurations,
info.system_mode_iflist);
}
update_monitored_interfaces(info.configured_iflist);
EAPOLEthernetInfoFree(&info);
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(TRUE);
}
if (user_changed) {
console_user_changed();
}
done:
my_CFRelease(&console_user_key);
return;
}
static SCDynamicStoreRef
dynamic_store_create(void)
{
SCDynamicStoreContext context;
CFArrayRef keys = NULL;
CFStringRef list[2];
CFArrayRef patterns = NULL;
CFRunLoopSourceRef rls;
SCDynamicStoreRef store;
bzero(&context, sizeof(context));
store = SCDynamicStoreCreate(NULL, CFSTR("EAPOLController"),
eapol_handle_change, &context);
if (store == NULL) {
my_log(LOG_NOTICE, "EAPOLController: SCDynamicStoreCreate() failed, %s",
SCErrorString(SCError()));
return (NULL);
}
list[0]
= SCDynamicStoreKeyCreateConsoleUser(NULL);
list[1]
= SCDynamicStoreKeyCreateNetworkInterface(NULL,
kSCDynamicStoreDomainState);
keys = CFArrayCreate(NULL, (const void * *)list, 2, &kCFTypeArrayCallBacks);
CFRelease(list[0]);
CFRelease(list[1]);
list[0]
= SCDynamicStoreKeyCreateNetworkInterfaceEntity(NULL,
kSCDynamicStoreDomainSetup,
kSCCompAnyRegex,
kSCEntNetEAPOL);
list[1]
= SCDynamicStoreKeyCreateNetworkServiceEntity(NULL,
kSCDynamicStoreDomainSetup,
kSCCompAnyRegex,
kSCEntNetInterface);
patterns = CFArrayCreate(NULL, (const void * *)list, 2,
&kCFTypeArrayCallBacks);
CFRelease(list[0]);
CFRelease(list[1]);
SCDynamicStoreSetNotificationKeys(store, keys, patterns);
my_CFRelease(&keys);
my_CFRelease(&patterns);
rls = SCDynamicStoreCreateRunLoopSource(NULL, store, 0);
CFRunLoopAddSource(CFRunLoopGetCurrent(), rls, kCFRunLoopDefaultMode);
my_CFRelease(&rls);
return (store);
}
static void
ControllerBegin(void)
{
server_start();
handle_config_changed(TRUE);
return;
}
#endif
void
load(CFBundleRef bundle, Boolean bundleVerbose)
{
Boolean ok;
uint8_t path[MAXPATHLEN];
CFURLRef url;
if (server_active()) {
my_log(LOG_NOTICE, "ipconfig server already active");
return;
}
url = CFBundleCopyResourceURL(bundle, CFSTR("eapolclient"), NULL, NULL);
if (url == NULL) {
my_log(LOG_NOTICE,
"EAPOLController: failed to get URL for eapolclient");
return;
}
ok = CFURLGetFileSystemRepresentation(url, TRUE, path, sizeof(path));
CFRelease(url);
if (ok == FALSE) {
my_log(LOG_NOTICE,
"EAPOLController: failed to get path for eapolclient");
return;
}
S_eapolclient_path = strdup((const char *)path);
server_register();
return;
}
void
start(const char *bundleName, const char *bundleDir)
{
if (S_eapolclient_path == NULL) {
return;
}
LIST_INIT(S_clientHead_p);
S_store = dynamic_store_create();
return;
}
void
prime()
{
if (S_eapolclient_path == NULL) {
return;
}
ControllerBegin();
return;
}