#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/errno.h>
#include <signal.h>
#include <mach/mach.h>
#include <mach/message.h>
#include <mach/mach_error.h>
#include <servers/bootstrap.h>
#include <syslog.h>
#include <SystemConfiguration/SCPrivate.h>
#include <SystemConfiguration/SCValidation.h>
#include <SystemConfiguration/SCDPlugin.h>
#include <CoreFoundation/CFMachPort.h>
#include "controller.h"
#include "eapolcontrollerServer.h"
#include "eapolcontroller_types.h"
#include "eapolcontroller_ext.h"
#include "server.h"
#include "myCFUtil.h"
#include "EAPLog.h"
extern boolean_t eapolcontroller_server(mach_msg_header_t *,
mach_msg_header_t *);
static uid_t S_uid = -1;
static gid_t S_gid = -1;
static CFMachPortRef server_cfport;
static vm_address_t
my_CFPropertyListCreateVMData(CFPropertyListRef plist,
mach_msg_type_number_t * ret_data_len)
{
vm_address_t data;
int data_len;
kern_return_t status;
CFDataRef xml_data;
data = 0;
*ret_data_len = 0;
xml_data = CFPropertyListCreateXMLData(NULL, plist);
if (xml_data == NULL) {
goto done;
}
data_len = CFDataGetLength(xml_data);
status = vm_allocate(mach_task_self(), &data, data_len, TRUE);
if (status != KERN_SUCCESS) {
goto done;
}
bcopy((char *)CFDataGetBytePtr(xml_data), (char *)data, data_len);
*ret_data_len = data_len;
done:
my_CFRelease(&xml_data);
return (data);
}
static __inline__ void
read_trailer(mach_msg_header_t * request)
{
mach_msg_format_0_trailer_t *trailer;
trailer = (mach_msg_security_trailer_t *)((vm_offset_t)request +
round_msg(request->msgh_size));
if ((trailer->msgh_trailer_type == MACH_MSG_TRAILER_FORMAT_0) &&
(trailer->msgh_trailer_size >= MACH_MSG_TRAILER_FORMAT_0_SIZE)) {
S_uid = trailer->msgh_sender.val[0];
S_gid = trailer->msgh_sender.val[1];
}
else {
S_uid = -1;
S_gid = -1;
}
}
kern_return_t
eapolcontroller_get_state(mach_port_t server,
if_name_t if_name,
int * state,
int * result)
{
*result = ControllerGetState(if_name, state);
return (KERN_SUCCESS);
}
kern_return_t
eapolcontroller_copy_status(mach_port_t server,
if_name_t if_name,
xmlDataOut_t * status,
mach_msg_type_number_t * status_len,
int * state,
int * result)
{
CFDictionaryRef dict = NULL;
*status = NULL;
*status_len = 0;
*result = ControllerCopyStateAndStatus(if_name, state, &dict);
if (dict != NULL) {
*status = (xmlDataOut_t)my_CFPropertyListCreateVMData(dict, status_len);
if (*status == NULL) {
EAPLOG_FL(LOG_NOTICE, "failed to serialize data");
}
}
my_CFRelease(&dict);
return (KERN_SUCCESS);
}
#if ! TARGET_OS_EMBEDDED
kern_return_t
eapolcontroller_copy_loginwindow_config(mach_port_t server,
if_name_t if_name,
xmlDataOut_t * config,
mach_msg_type_number_t * config_len,
int * result)
{
CFDictionaryRef dict = NULL;
*config = NULL;
*config_len = 0;
*result = ControllerCopyLoginWindowConfiguration(if_name, &dict);
if (dict != NULL) {
*config = (xmlDataOut_t)my_CFPropertyListCreateVMData(dict, config_len);
if (*config == NULL) {
EAPLOG_FL(LOG_NOTICE, "failed to serialize data");
}
}
my_CFRelease(&dict);
return (KERN_SUCCESS);
}
kern_return_t
eapolcontroller_copy_autodetect_info(mach_port_t server,
xmlDataOut_t * info,
mach_msg_type_number_t * info_len,
int * result)
{
CFDictionaryRef dict = NULL;
*info = NULL;
*info_len = 0;
*result = ControllerCopyAutoDetectInformation(&dict);
if (dict != NULL) {
*info = (xmlDataOut_t)my_CFPropertyListCreateVMData(dict, info_len);
if (*info == NULL) {
EAPLOG_FL(LOG_NOTICE, "failed to serialize data");
}
}
my_CFRelease(&dict);
return (KERN_SUCCESS);
}
kern_return_t
eapolcontroller_did_user_cancel(mach_port_t server,
if_name_t if_name,
boolean_t * user_cancelled)
{
*user_cancelled = ControllerDidUserCancel(if_name);
return (KERN_SUCCESS);
}
#endif
kern_return_t
eapolcontroller_start(mach_port_t server,
if_name_t if_name, xmlData_t config,
mach_msg_type_number_t config_len,
mach_port_t bootstrap,
mach_port_t au_session,
int * ret_result)
{
CFDictionaryRef dict = NULL;
int result = EINVAL;
if (config == NULL) {
goto done;
}
dict = my_CFPropertyListCreateWithBytePtrAndLength(config, config_len);
(void)vm_deallocate(mach_task_self(), (vm_address_t)config, config_len);
if (isA_CFDictionary(dict) == NULL) {
goto done;
}
if (S_uid == -1) {
result = EPERM;
goto done;
}
result = ControllerStart(if_name, S_uid, S_gid, dict, bootstrap,
au_session);
done:
*ret_result = result;
my_CFRelease(&dict);
return (KERN_SUCCESS);
}
#if ! TARGET_OS_EMBEDDED
kern_return_t
eapolcontroller_start_system(mach_port_t server,
if_name_t if_name,
xmlData_t options,
mach_msg_type_number_t options_len,
int * ret_result)
{
CFDictionaryRef dict = NULL;
int result = EINVAL;
if (options != NULL) {
dict = my_CFPropertyListCreateWithBytePtrAndLength(options,
options_len);
(void)vm_deallocate(mach_task_self(), (vm_address_t)options,
options_len);
if (isA_CFDictionary(dict) == NULL) {
goto done;
}
}
if (S_uid == -1) {
result = EPERM;
goto done;
}
result = ControllerStartSystem(if_name, S_uid, S_gid, dict);
done:
my_CFRelease(&dict);
*ret_result = result;
return (KERN_SUCCESS);
}
kern_return_t
eapolcontroller_client_user_cancelled(mach_port_t server,
int * result)
{
*result = ControllerClientUserCancelled(server);
return (KERN_SUCCESS);
};
#endif
kern_return_t
eapolcontroller_stop(mach_port_t server,
if_name_t if_name, int * result)
{
*result = ControllerStop(if_name, S_uid, S_gid);
return (KERN_SUCCESS);
}
kern_return_t
eapolcontroller_update(mach_port_t server,
if_name_t if_name, xmlData_t config,
mach_msg_type_number_t config_len,
int * ret_result)
{
CFDictionaryRef dict = NULL;
int result = EINVAL;
if (config == NULL) {
goto done;
}
dict = my_CFPropertyListCreateWithBytePtrAndLength(config, config_len);
(void)vm_deallocate(mach_task_self(), (vm_address_t)config, config_len);
if (isA_CFDictionary(dict) == NULL) {
goto done;
}
result = ControllerUpdate(if_name, S_uid, S_gid, dict);
done:
my_CFRelease(&dict);
*ret_result = result;
return (KERN_SUCCESS);
}
kern_return_t
eapolcontroller_provide_user_input(mach_port_t server,
if_name_t if_name, xmlData_t user_input,
mach_msg_type_number_t user_input_len,
int * ret_result)
{
CFDictionaryRef dict = NULL;
int result = EINVAL;
if (user_input == NULL) {
goto done;
}
dict = my_CFPropertyListCreateWithBytePtrAndLength(user_input,
user_input_len);
(void)vm_deallocate(mach_task_self(), (vm_address_t)user_input,
user_input_len);
if (isA_CFDictionary(dict) == NULL) {
goto done;
}
result = ControllerProvideUserInput(if_name, S_uid, S_gid, dict);
done:
my_CFRelease(&dict);
*ret_result = result;
return (KERN_SUCCESS);
}
kern_return_t
eapolcontroller_retry(mach_port_t server,
if_name_t if_name, int * result)
{
*result = ControllerRetry(if_name, S_uid, S_gid);
return (KERN_SUCCESS);
}
kern_return_t
eapolcontroller_client_attach(mach_port_t server, task_t task,
if_name_t if_name,
mach_port_t notify_port,
mach_port_t * session,
xmlDataOut_t * control,
mach_msg_type_number_t * control_len,
mach_port_t * bootstrap,
mach_port_t * au_session,
int * ret_result)
{
int pid;
CFDictionaryRef dict = NULL;
int result = EINVAL;
kern_return_t status;
*control = NULL;
*control_len = 0;
status = pid_for_task(task, &pid);
if (status != KERN_SUCCESS) {
(void)mach_port_deallocate(mach_task_self(), notify_port);
goto done;
}
result = ControllerClientAttach(pid, if_name, notify_port, session,
&dict, bootstrap, au_session);
if (dict != NULL) {
*control = (xmlDataOut_t)my_CFPropertyListCreateVMData(dict,
control_len);
if (*control == 0) {
EAPLOG_FL(LOG_NOTICE, "failed to serialize data");
}
}
my_CFRelease(&dict);
done:
if (task != TASK_NULL) {
(void)mach_port_deallocate(mach_task_self(), task);
}
*ret_result = result;
return (KERN_SUCCESS);
}
kern_return_t
eapolcontroller_client_detach(mach_port_t server, int * result)
{
*result = ControllerClientDetach(server);
return (KERN_SUCCESS);
}
kern_return_t
eapolcontroller_client_getconfig(mach_port_t server,
xmlDataOut_t * control,
mach_msg_type_number_t * control_len,
int * result)
{
CFDictionaryRef dict = NULL;
*control = NULL;
*control_len = 0;
*result = ControllerClientGetConfig(server, &dict);
if (dict != NULL) {
*control = (xmlDataOut_t)my_CFPropertyListCreateVMData(dict,
control_len);
if (*control == NULL) {
EAPLOG_FL(LOG_NOTICE, "failed to serialize data");
}
}
my_CFRelease(&dict);
return (KERN_SUCCESS);
}
kern_return_t
eapolcontroller_client_report_status(mach_port_t server,
xmlData_t status_data,
mach_msg_type_number_t status_data_len,
int * ret_result)
{
CFDictionaryRef dict = NULL;
int result = EINVAL;
if (status_data == NULL) {
goto done;
}
dict = my_CFPropertyListCreateWithBytePtrAndLength(status_data,
status_data_len);
(void)vm_deallocate(mach_task_self(), (vm_address_t)status_data,
status_data_len);
if (isA_CFDictionary(dict) == NULL) {
goto done;
}
result = ControllerClientReportStatus(server, dict);
done:
my_CFRelease(&dict);
*ret_result = result;
return (KERN_SUCCESS);
};
kern_return_t
eapolcontroller_client_force_renew(mach_port_t server,
int * result)
{
*result = ControllerClientForceRenew(server);
return (KERN_SUCCESS);
};
boolean_t
server_active(void)
{
mach_port_t server;
kern_return_t result;
result = eapolcontroller_server_port(&server);
if (result == BOOTSTRAP_SUCCESS) {
return (TRUE);
}
return (FALSE);
}
static boolean_t
process_notification(mach_msg_header_t * request)
{
mach_no_senders_notification_t * notify;
notify = (mach_no_senders_notification_t *)request;
if (notify->not_header.msgh_id > MACH_NOTIFY_LAST
|| notify->not_header.msgh_id < MACH_NOTIFY_FIRST) {
return FALSE;
}
return (TRUE);
}
void
server_handle_request(CFMachPortRef port, void *msg, CFIndex size, void *info)
{
mach_msg_return_t r;
mach_msg_header_t * request = (mach_msg_header_t *)msg;
mach_msg_header_t * reply;
#define REPLY_SIZE_AS_UINT64 \
((eapolcontroller_subsystem.maxsize + sizeof(uint64_t) - 1) / sizeof(uint64_t))
uint64_t reply_s[REPLY_SIZE_AS_UINT64];
if (process_notification(request) == FALSE) {
read_trailer(request);
reply = (mach_msg_header_t *)(void *)reply_s;
if (eapolcontroller_server(request, reply) == FALSE) {
EAPLOG(LOG_NOTICE, "EAPOLController: unknown message ID (%d)",
request->msgh_id);
mach_msg_destroy(request);
}
else {
int options;
options = MACH_SEND_MSG;
if (MACH_MSGH_BITS_REMOTE(reply->msgh_bits)
!= MACH_MSG_TYPE_MOVE_SEND_ONCE) {
options |= MACH_SEND_TIMEOUT;
}
r = mach_msg(reply,
options,
reply->msgh_size,
0,
MACH_PORT_NULL,
MACH_MSG_TIMEOUT_NONE,
MACH_PORT_NULL);
if (r != MACH_MSG_SUCCESS) {
EAPLOG(LOG_NOTICE, "EAPOLController: mach_msg(send): %s",
mach_error_string(r));
mach_msg_destroy(reply);
}
}
}
return;
}
void
server_register(void)
{
mach_port_t server_port;
kern_return_t status;
status = bootstrap_check_in(bootstrap_port, EAPOLCONTROLLER_SERVER,
&server_port);
if (status != BOOTSTRAP_SUCCESS) {
EAPLOG(LOG_NOTICE, "EAPOLController: bootstrap_check_in failed, %s",
mach_error_string(status));
return;
}
server_cfport = _SC_CFMachPortCreateWithPort(NULL, server_port,
server_handle_request, NULL);
return;
}
void
server_start(void)
{
CFRunLoopSourceRef rls;
if (server_cfport != NULL) {
rls = CFMachPortCreateRunLoopSource(NULL, server_cfport, 0);
CFRunLoopAddSource(CFRunLoopGetCurrent(), rls, kCFRunLoopDefaultMode);
CFRelease(rls);
}
}