#include <launch.h>
#include <stdlib.h>
#include <unistd.h>
#include <bsm/libbsm.h>
#include <sys/types.h>
#include <sysexits.h>
#include <CoreFoundation/CFRunLoop.h>
#include <CoreFoundation/CFRuntime.h>
#include <SystemConfiguration/SystemConfiguration.h>
#include <SystemConfiguration/SCPrivate.h>
#include <SystemConfiguration/SCValidation.h>
#include "EAPOLClientConfiguration.h"
#include "EAPOLClientConfigurationPrivate.h"
#include "symbol_scope.h"
#include "eapolcfg_authServer.h"
#include "eapolcfg_auth_types.h"
STATIC Boolean S_handled_request;
INLINE void
my_vm_deallocate(vm_address_t data, int data_length)
{
if (data != 0) {
(void)vm_deallocate(mach_task_self(), data, data_length);
}
return;
}
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 CFPropertyListRef
my_CFPropertyListCreateWithBytePtrAndLength(const void * data, int data_len)
{
CFPropertyListRef plist;
CFDataRef xml_data;
xml_data = CFDataCreateWithBytesNoCopy(NULL,
(const UInt8 *)data, data_len,
kCFAllocatorNull);
if (xml_data == NULL) {
return (NULL);
}
plist = CFPropertyListCreateFromXMLData(NULL,
xml_data,
kCFPropertyListImmutable,
NULL);
CFRelease(xml_data);
return (plist);
}
STATIC Boolean
authorization_is_valid(const void * auth_data, int auth_data_length)
{
AuthorizationExternalForm * auth_ext_p;
AuthorizationRef authorization;
AuthorizationFlags flags;
AuthorizationItem item;
AuthorizationRights rights;
OSStatus status;
if (auth_data == NULL
|| auth_data_length != sizeof(auth_ext_p->bytes)) {
syslog(LOG_ERR, "eapolcfg_auth: authorization NULL/invalid size");
return (FALSE);
}
auth_ext_p = (AuthorizationExternalForm *)auth_data;
status = AuthorizationCreateFromExternalForm(auth_ext_p, &authorization);
if (status != errAuthorizationSuccess) {
syslog(LOG_ERR, "eapolcfg_auth: authorization is invalid (%d)",
(int)status);
return (FALSE);
}
rights.count = 1;
rights.items = &item;
item.name = "system.preferences";
item.value = NULL;
item.valueLength = 0;
item.flags = 0;
flags = kAuthorizationFlagDefaults;
flags |= kAuthorizationFlagExtendRights;
flags |= kAuthorizationFlagInteractionAllowed;
status = AuthorizationCopyRights(authorization,
&rights,
kAuthorizationEmptyEnvironment,
flags,
NULL);
AuthorizationFree(authorization, kAuthorizationFlagDefaults);
return (status == errAuthorizationSuccess);
}
STATIC int
init_itemID_and_cfg(xmlData_t itemID_data,
mach_msg_type_number_t itemID_data_length,
EAPOLClientItemIDRef * itemID_p,
EAPOLClientConfigurationRef * cfg_p)
{
EAPOLClientConfigurationRef cfg = NULL;
EAPOLClientItemIDRef itemID = NULL;
CFDictionaryRef itemID_dict = NULL;
int ret = 0;
itemID_dict
= my_CFPropertyListCreateWithBytePtrAndLength(itemID_data,
itemID_data_length);
if (isA_CFDictionary(itemID_dict) == NULL) {
ret = EINVAL;
goto done;
}
cfg = EAPOLClientConfigurationCreate(NULL);
if (cfg == NULL) {
ret = ENOMEM;
goto done;
}
itemID = EAPOLClientItemIDCreateWithDictionary(cfg, itemID_dict);
if (itemID == NULL) {
ret = EINVAL;
goto done;
}
done:
my_CFRelease(&itemID_dict);
if (ret == 0) {
*cfg_p = cfg;
*itemID_p = itemID;
}
else {
my_CFRelease(&itemID);
my_CFRelease(&cfg);
}
return (ret);
}
PRIVATE_EXTERN kern_return_t
eapolclientitemid_set_identity(mach_port_t server,
OOBData_t auth_data,
mach_msg_type_number_t auth_data_length,
xmlData_t itemID_data,
mach_msg_type_number_t itemID_data_length,
OOBData_t id_handle,
mach_msg_type_number_t id_handle_length,
int * result)
{
EAPOLClientConfigurationRef cfg = NULL;
SecIdentityRef identity = NULL;
EAPOLClientItemIDRef itemID = NULL;
int ret = 0;
if (authorization_is_valid(auth_data, auth_data_length) == FALSE) {
ret = EPERM;
goto done;
}
ret = init_itemID_and_cfg(itemID_data, itemID_data_length, &itemID, &cfg);
if (ret != 0) {
goto done;
}
if (id_handle != NULL) {
CFDataRef data;
OSStatus status;
data = CFDataCreateWithBytesNoCopy(NULL,
(const UInt8 *)id_handle,
id_handle_length,
kCFAllocatorNull);
if (data == NULL) {
ret = EINVAL;
goto done;
}
status = EAPSecIdentityHandleCreateSecIdentity(data, &identity);
CFRelease(data);
if (status != noErr) {
ret = ENOENT;
goto done;
}
}
if (EAPOLClientItemIDSetIdentity(itemID,
kEAPOLClientDomainSystem,
identity) == FALSE) {
ret = ENXIO;
}
done:
my_vm_deallocate((vm_address_t)auth_data, auth_data_length);
my_vm_deallocate((vm_address_t)itemID_data, itemID_data_length);
my_vm_deallocate((vm_address_t)id_handle, id_handle_length);
my_CFRelease(&cfg);
my_CFRelease(&itemID);
my_CFRelease(&identity);
*result = ret;
return (KERN_SUCCESS);
}
kern_return_t
eapolclientitemid_set_password(mach_port_t server,
OOBData_t auth_data,
mach_msg_type_number_t auth_data_length,
xmlData_t itemID_data,
mach_msg_type_number_t itemID_data_length,
uint32_t flags,
OOBData_t name,
mach_msg_type_number_t name_length,
OOBData_t password,
mach_msg_type_number_t password_length,
int * result)
{
EAPOLClientConfigurationRef cfg = NULL;
EAPOLClientItemIDRef itemID = NULL;
CFDataRef name_data = NULL;
CFDataRef password_data = NULL;
int ret = 0;
if (authorization_is_valid(auth_data, auth_data_length) == FALSE) {
ret = EPERM;
goto done;
}
ret = init_itemID_and_cfg(itemID_data, itemID_data_length, &itemID, &cfg);
if (ret != 0) {
goto done;
}
if ((flags & keapolcfg_auth_set_name) != 0) {
name_data = CFDataCreate(NULL, (const UInt8 *)name,
name_length);
}
else {
syslog(LOG_NOTICE, "not setting name");
}
if ((flags & keapolcfg_auth_set_password) != 0) {
password_data = CFDataCreate(NULL, (const UInt8 *)password,
password_length);
}
else {
syslog(LOG_NOTICE, "not setting password");
}
if (EAPOLClientItemIDSetPasswordItem(itemID,
kEAPOLClientDomainSystem,
name_data, password_data) == FALSE) {
ret = ENXIO;
}
done:
my_vm_deallocate((vm_address_t)auth_data, auth_data_length);
my_vm_deallocate((vm_address_t)itemID_data, itemID_data_length);
my_vm_deallocate((vm_address_t)name, name_length);
my_vm_deallocate((vm_address_t)password, password_length);
my_CFRelease(&name_data);
my_CFRelease(&password_data);
my_CFRelease(&cfg);
my_CFRelease(&itemID);
*result = ret;
return (KERN_SUCCESS);
}
PRIVATE_EXTERN kern_return_t
eapolclientitemid_remove_password(mach_port_t server,
OOBData_t auth_data,
mach_msg_type_number_t auth_data_length,
xmlData_t itemID_data,
mach_msg_type_number_t itemID_data_length,
int * result)
{
EAPOLClientConfigurationRef cfg = NULL;
SecIdentityRef identity = NULL;
EAPOLClientItemIDRef itemID = NULL;
CFDataRef name_data = NULL;
CFDataRef password_data = NULL;
int ret = 0;
if (authorization_is_valid(auth_data, auth_data_length) == FALSE) {
ret = EPERM;
goto done;
}
ret = init_itemID_and_cfg(itemID_data, itemID_data_length, &itemID, &cfg);
if (ret != 0) {
goto done;
}
if (EAPOLClientItemIDRemovePasswordItem(itemID, kEAPOLClientDomainSystem)
== FALSE) {
ret = ENXIO;
}
done:
my_vm_deallocate((vm_address_t)auth_data, auth_data_length);
my_vm_deallocate((vm_address_t)itemID_data, itemID_data_length);
my_CFRelease(&cfg);
my_CFRelease(&itemID);
*result = ret;
return (KERN_SUCCESS);
}
PRIVATE_EXTERN kern_return_t
eapolclientitemid_check_password(mach_port_t server,
OOBData_t auth_data,
mach_msg_type_number_t auth_data_length,
xmlData_t itemID_data,
mach_msg_type_number_t itemID_data_length,
OOBDataOut_t * name,
mach_msg_type_number_t * name_length,
boolean_t * password_set,
int * result)
{
EAPOLClientConfigurationRef cfg = NULL;
SecIdentityRef identity = NULL;
EAPOLClientItemIDRef itemID = NULL;
CFDataRef name_data = NULL;
vm_address_t name_vm_data = 0;
mach_msg_type_number_t name_vm_length = 0;
CFDataRef password_data = NULL;
vm_address_t password_vm_data = 0;
mach_msg_type_number_t password_vm_length = 0;
int ret = 0;
*password_set = FALSE;
if (authorization_is_valid(auth_data, auth_data_length) == FALSE) {
ret = EPERM;
goto done;
}
ret = init_itemID_and_cfg(itemID_data, itemID_data_length, &itemID, &cfg);
if (ret != 0) {
goto done;
}
if (EAPOLClientItemIDCopyPasswordItem(itemID, kEAPOLClientDomainSystem,
&name_data, &password_data)
== FALSE) {
ret = ENOENT;
}
else {
kern_return_t kret;
if (name_data != NULL) {
name_vm_length = CFDataGetLength(name_data);
kret = vm_allocate(mach_task_self(), &name_vm_data,
name_vm_length, TRUE);
if (kret != KERN_SUCCESS) {
ret = ENOMEM;
goto done;
}
bcopy((char *)CFDataGetBytePtr(name_data),
(char *)(name_vm_data), name_vm_length);
CFRelease(name_data);
}
if (password_data != NULL) {
*password_set = TRUE;
CFRelease(password_data);
}
}
done:
if (ret != 0) {
if (name_vm_data != 0) {
(void)vm_deallocate(mach_task_self(), name_vm_data,
name_vm_length);
name_vm_data = 0;
name_vm_length = 0;
}
}
*name = (char *)name_vm_data;
*name_length = name_vm_length;
my_vm_deallocate((vm_address_t)auth_data, auth_data_length);
my_vm_deallocate((vm_address_t)itemID_data, itemID_data_length);
my_CFRelease(&cfg);
my_CFRelease(&itemID);
*result = ret;
return (KERN_SUCCESS);
}
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);
}
STATIC 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;
char reply_s[eapolcfg_auth_subsystem.maxsize];
if (process_notification(request) == FALSE) {
reply = (mach_msg_header_t *)reply_s;
if (eapolcfg_auth_server(request, reply) == FALSE) {
syslog(LOG_NOTICE,
"eapolcfg_auth: unknown message ID (%d)",
request->msgh_id);
mach_msg_destroy(request);
}
else {
int options;
S_handled_request = TRUE;
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) {
syslog(LOG_NOTICE, "eapolcfg_auth: mach_msg(send): %s",
mach_error_string(r));
mach_msg_destroy(reply);
}
}
}
return;
}
static void
process_launchd_machport(const launch_data_t obj, const char *name, void *info)
{
int * count_p = (int *)info;
CFMachPortRef mp;
CFRunLoopSourceRef rls;
mach_port_t service_port;
if (obj == NULL
|| launch_data_get_type(obj) != LAUNCH_DATA_MACHPORT) {
syslog(LOG_ERR, "eapolcfg_auth: bad type in MachServices entry");
return;
}
service_port = launch_data_get_machport(obj);
mp = CFMachPortCreateWithPort(NULL, service_port, server_handle_request,
NULL, NULL);
rls = CFMachPortCreateRunLoopSource(NULL, mp, 0);
CFRunLoopAddSource(CFRunLoopGetCurrent(), rls, kCFRunLoopDefaultMode);
CFRelease(mp);
CFRelease(rls);
(*count_p)++;
return;
}
int
main(int argc, char **argv)
{
int count = 0;
launch_data_t msg;
launch_data_t machservices;
launch_data_t response;
launch_data_type_t type;
openlog("eapolcfg_auth", LOG_CONS|LOG_PID, LOG_DAEMON);
if (geteuid() != 0) {
syslog(LOG_ERR, "eapolcfg_auth must be run as root - exiting");
exit(EX_CONFIG);
}
msg = launch_data_new_string(LAUNCH_KEY_CHECKIN);
response = launch_msg(msg);
launch_data_free(msg);
if (response == NULL) {
syslog(LOG_ERR, "eapolcf_auth: NULL response from launchd");
exit(EX_CONFIG);
}
if (launch_data_get_type(response) != LAUNCH_DATA_DICTIONARY) {
syslog(LOG_ERR, "eapolcfg_auth: response type not a dictionary");
exit(EX_CONFIG);
}
machservices = launch_data_dict_lookup(response,
LAUNCH_JOBKEY_MACHSERVICES);
if (machservices == NULL
|| launch_data_get_type(machservices) != LAUNCH_DATA_DICTIONARY) {
syslog(LOG_ERR, "eapolcfg_auth: MachServices is NULL/invalid");
exit(EX_CONFIG);
}
launch_data_dict_iterate(machservices, process_launchd_machport,
(void *)&count);
if (count == 0) {
syslog(LOG_ERR, "eapolcfg_auth: no port to listen on");
exit(EX_OK);
}
while (1) {
SInt32 rlStatus;
rlStatus = CFRunLoopRunInMode(kCFRunLoopDefaultMode, 15.0, TRUE);
if (rlStatus == kCFRunLoopRunTimedOut) {
if (S_handled_request == FALSE) {
break;
}
S_handled_request = FALSE;
}
}
exit(EX_OK);
return (0);
}