#include <stdio.h>
#include <sys/types.h>
#include <sys/uio.h>
#include <unistd.h>
#include <syslog.h>
#include <sys/fcntl.h>
#include <CoreFoundation/CoreFoundation.h>
#include <CoreFoundation/CFBundle.h>
#include <mach/mach.h>
#include <EAP8021X/EAP.h>
#include <EAP8021X/EAPClientModule.h>
#include <EAP8021X/EAPClientProperties.h>
#if !TARGET_OS_EMBEDDED // This file is not built for Embedded
#include <Security/SecKeychain.h>
#include <Security/SecKeychainSearch.h>
#include <Security/SecKeychainItem.h>
#include <Security/SecIdentity.h>
#endif
#include <SystemConfiguration/SCNetworkConnection.h>
#include "plog.h"
#include "eap.h"
#include "eap_sim.h"
static CFBundleRef bundle = 0;
static char eapaka_unique[17];
static EAPClientModuleRef eapRef = NULL;
static EAPClientPluginData eapData;
static CFMutableDictionaryRef eapProperties = NULL;
static CFDictionaryRef eapOptions = NULL;
static struct EAP_Packet *eapSavePacket = NULL;
extern EAPClientPluginFuncRef
eapaka_introspect(EAPClientPluginFuncName name);
static void
EAPAKAGetOptions (void)
{
if (eapOptions)
return;
if (!eapOptions)
eapOptions = CFDictionaryCreate(0, 0, 0, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
}
static int
EAPAKALoad (void)
{
EAPClientModuleStatus status;
if (eapRef)
return EAP_NO_ERROR;
status = EAPClientModuleAddBuiltinModule(eapaka_introspect);
if (status != kEAPClientModuleStatusOK) {
plog(ASL_LEVEL_INFO, "EAP-AKA: EAPClientAddBuiltinModule(eapaka) failed %d\n", status);
return EAP_ERROR_GENERIC;
}
eapRef = EAPClientModuleLookup(kEAPTypeEAPAKA);
if (eapRef == NULL) {
plog(ASL_LEVEL_INFO, "EAP-AKA: EAPClientModuleLookup(eapaka) failed\n");
return EAP_ERROR_GENERIC;
}
return EAP_NO_ERROR;
}
int EAPAKAIdentity (char *identity, int maxlen)
{
CFStringRef identRef = NULL;
int error;
int ret = EAP_ERROR_GENERIC;
error = EAPAKALoad();
if (error)
return error;
EAPAKAGetOptions();
if (eapOptions == NULL)
return ret;
identRef = EAPClientModulePluginUserName(eapRef, eapOptions);
if (identRef) {
if (CFStringGetCString(identRef, identity, maxlen, kCFStringEncodingUTF8))
ret = EAP_NO_ERROR;
CFRelease(identRef);
}
return ret;
}
int
EAPAKAInit (EAP_Input_t *eap_in, void **context, CFDictionaryRef eapOptions)
{
int error;
EAPClientModuleStatus status;
int ret = EAP_ERROR_GENERIC;
error = EAPAKALoad();
if (error)
return error;
bundle = (CFBundleRef)eap_in->data;
if (bundle)
CFRetain(bundle);
EAPAKAGetOptions();
bzero(&eapData, sizeof(eapData));
uint32_t username_len = strlen(eap_in->username);
eapData.username = (uint8_t *)strndup(eap_in->username, username_len);
memcpy((void*)&eapData.username_length, &username_len, sizeof(uint32_t));
*((bool *)&eapData.log_enabled) = 1;
*((uint32_t *)&eapData.log_level) = LOG_NOTICE;
*((uint32_t *)&eapData.mtu) = eap_in->mtu;
*((uint32_t *)&eapData.generation) = 0;
arc4random_buf(eapaka_unique, sizeof(eapaka_unique) - 1);
eapaka_unique[sizeof(eapaka_unique)-1] = 0;
eapData.unique_id = eapaka_unique;
*((uint32_t *)&eapData.unique_id_length) = strlen(eapData.unique_id);
if (eapOptions) {
CFTypeRef value = CFDictionaryGetValue(eapOptions, kEAPPropertiesTypeEAPAKA);
if (value && CFGetTypeID(value) == CFDictionaryGetTypeID()) {
eapProperties = CFDictionaryCreateMutableCopy(kCFAllocatorDefault, 0, (CFDictionaryRef)value);
} else {
eapProperties = CFDictionaryCreateMutableCopy(kCFAllocatorDefault, 0, eapOptions);
}
} else
eapProperties = CFDictionaryCreateMutable(0, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
if (eapProperties == NULL) {
plog(ASL_LEVEL_ERR, "EAP-AKA: Cannot allocate memory\n");
goto failed;
}
*((CFDictionaryRef *)&eapData.properties) = (CFDictionaryRef)eapProperties;
status = EAPClientModulePluginInit(eapRef, &eapData, NULL, &error);
if (status != kEAPClientStatusOK) {
plog(ASL_LEVEL_ERR, "EAP-AKA: EAPClientPluginInit(eapaka) failed, error %d\n", status);
goto failed;
}
eapSavePacket = NULL;
return EAP_NO_ERROR;
failed:
return ret;
}
int EAPAKADispose (void *context)
{
EAPClientModulePluginFree(eapRef, &eapData);
eapRef = 0;
if (bundle) {
CFRelease(bundle);
bundle = 0;
}
if (eapOptions) {
CFRelease(eapOptions);
eapOptions = 0;
}
if (eapProperties) {
CFRelease(eapProperties);
eapProperties = 0;
}
if (eapSavePacket) {
free(eapSavePacket);
eapSavePacket = 0;
}
return EAP_NO_ERROR;
}
int
EAPAKAProcess (void *context, EAP_Input_t *eap_in, EAP_Output_t *eap_out)
{
struct EAP_Packet *pkt_in = NULL;
struct EAP_Packet *pkt_out = NULL;
EAPClientStatus status;
EAPClientState state;
EAPClientDomainSpecificError error;
int do_process = 0;
eap_out->action = EAP_ACTION_NONE;
eap_out->data = 0;
eap_out->data_len = 0;
switch (eap_in->notification) {
case EAP_NOTIFICATION_DATA_FROM_UI:
plog(ASL_LEVEL_ERR, "unexpected EAP UI event");
break;
case EAP_NOTIFICATION_PACKET:
pkt_in = (struct EAP_Packet *)eap_in->data;
do_process = 1;
break;
}
if (do_process) {
state = EAPClientModulePluginProcess(eapRef, &eapData, (EAPPacketRef)pkt_in, (EAPPacketRef*)&pkt_out, &status, &error);
switch(state) {
case kEAPClientStateAuthenticating:
switch (status) {
case kEAPClientStatusOK:
eap_out->data = pkt_out;
eap_out->data_len = ntohs(pkt_out->len);
eap_out->action = EAP_ACTION_SEND;
break;
case kEAPClientStatusUserInputRequired:
plog(ASL_LEVEL_ERR, "unsupported EAP UI input");
default:
eap_out->action = EAP_ACTION_ACCESS_DENIED;
}
break;
case kEAPClientStateSuccess:
eap_out->action = EAP_ACTION_ACCESS_GRANTED;
break;
default:
case kEAPClientStateFailure:
eap_out->action = EAP_ACTION_ACCESS_DENIED;
break;
}
}
if (eapSavePacket) {
free(eapSavePacket);
eapSavePacket = 0;
}
return 0;
}
int
EAPAKAFree (void *context, EAP_Output_t *eap_out)
{
EAPClientModulePluginFreePacket(eapRef, &eapData, eap_out->data);
return EAP_NO_ERROR;
}
int
EAPAKAGetAttribute (void *context, EAP_Attribute_t *eap_attr)
{
void *data = NULL;
int len = 0;
eap_attr->data = 0;
switch (eap_attr->type) {
case EAP_ATTRIBUTE_MPPE_SEND_KEY:
data = EAPClientModulePluginSessionKey(eapRef, &eapData, &len);
break;
case EAP_ATTRIBUTE_MPPE_RECV_KEY:
data = EAPClientModulePluginServerKey(eapRef, &eapData, &len);
break;
}
if (data == NULL)
return -1;
eap_attr->data = data;
if (len == 32)
eap_attr->data_len = 64;
else
eap_attr->data_len = len;
return 0;
}