#include "EAPClientProperties.h"
#include <EAP8021X/EAPClientPlugin.h>
#include <EAP8021X/EAPClientProperties.h>
#include <CoreFoundation/CFData.h>
#include <CoreFoundation/CFArray.h>
#include <CoreFoundation/CFDictionary.h>
#include <SystemConfiguration/SCValidation.h>
#include <stdbool.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <syslog.h>
#include <sys/param.h>
#include <CommonCrypto/CommonDigest.h>
#include <CommonCrypto/CommonCryptor.h>
#include <sys/param.h>
#include <EAP8021X/EAP.h>
#include <EAP8021X/EAPUtil.h>
#include <EAP8021X/EAPClientModule.h>
#include <TargetConditionals.h>
#include "myCFUtil.h"
#include "printdata.h"
#include "fips186prf.h"
#include "SIMAccess.h"
#include "nbo.h"
#include "EAPSIMAKAPersistentState.h"
#include "EAPSIMAKA.h"
#include "EAPSIMAKAUtil.h"
#include "EAPLog.h"
#define EAP_SIM_NAME "EAP-SIM"
#define kEAPClientPropEAPSIMIMSI \
CFSTR("EAPSIMIMSI")
#define kEAPClientPropEAPSIMRealm \
CFSTR("EAPSIMRealm")
#define kEAPClientPropEAPSIMNumberOfRANDs \
CFSTR("EAPSIMNumberOfRANDs")
#define kEAPClientPropEAPSIMKcList CFSTR("EAPSIMKcList")
#define kEAPClientPropEAPSIMSRESList CFSTR("EAPSIMSRESList")
#define kEAPClientPropEAPSIMRANDList CFSTR("EAPSIMSRANDList")
EAPClientPluginFuncIntrospect eapsim_introspect;
STATIC EAPClientPluginFuncVersion eapsim_version;
STATIC EAPClientPluginFuncEAPType eapsim_type;
STATIC EAPClientPluginFuncEAPName eapsim_name;
STATIC EAPClientPluginFuncInit eapsim_init;
STATIC EAPClientPluginFuncFree eapsim_free;
STATIC EAPClientPluginFuncProcess eapsim_process;
STATIC EAPClientPluginFuncFreePacket eapsim_free_packet;
STATIC EAPClientPluginFuncSessionKey eapsim_session_key;
STATIC EAPClientPluginFuncServerKey eapsim_server_key;
STATIC EAPClientPluginFuncPublishProperties eapsim_publish_props;
STATIC EAPClientPluginFuncUserName eapsim_user_name_copy;
STATIC EAPClientPluginFuncCopyIdentity eapsim_copy_identity;
STATIC EAPClientPluginFuncCopyPacketDescription eapsim_copy_packet_description;
enum {
kEAPSIMClientStateNone = 0,
kEAPSIMClientStateStart = 1,
kEAPSIMClientStateChallenge = 2,
kEAPSIMClientStateReauthentication = 3,
kEAPSIMClientStateSuccess = 4,
kEAPSIMClientStateFailure = 5
};
typedef int EAPSIMClientState;
typedef struct {
CFArrayRef kc;
CFArrayRef sres;
CFArrayRef rand;
} SIMStaticTriplets, * SIMStaticTripletsRef;
typedef struct {
EAPClientPluginDataRef plugin;
EAPClientState plugin_state;
EAPSIMClientState state;
int previous_identifier;
int start_count;
int n_required_rands;
EAPSIMAKAAttributeType last_identity_type;
CFDataRef last_identity;
SIMStaticTriplets sim_static;
EAPSIMAKAKeyInfo key_info;
bool key_info_valid;
uint8_t nonce_mt[NONCE_MT_SIZE];
EAPSIMAKAPersistentStateRef persist;
bool reauth_success;
uint16_t * version_list;
int version_list_count;
uint8_t pkt[1500];
} EAPSIMContext, *EAPSIMContextRef;
STATIC CFStringRef
copy_imsi_identity(CFStringRef imsi, CFStringRef realm)
{
if (realm != NULL) {
return (CFStringCreateWithFormat(NULL, NULL,
CFSTR("1" "%@" "@" "%@"),
imsi, realm));
}
return (CFStringCreateWithFormat(NULL, NULL, CFSTR("1" "%@"),
imsi));
}
STATIC CFStringRef
copy_static_realm(CFDictionaryRef properties)
{
CFStringRef realm = NULL;
if (properties == NULL) {
return (NULL);
}
realm = isA_CFString(CFDictionaryGetValue(properties,
kEAPClientPropEAPSIMRealm));
if (realm == NULL) {
realm
= isA_CFString(CFDictionaryGetValue(properties,
kEAPClientPropEAPSIMAKARealm));
if (realm == NULL) {
return (NULL);
}
}
return (CFRetain(realm));
}
STATIC CFStringRef
copy_static_imsi(CFDictionaryRef properties)
{
CFStringRef imsi;
if (properties == NULL) {
return (NULL);
}
imsi = isA_CFString(CFDictionaryGetValue(properties,
kEAPClientPropEAPSIMIMSI));
if (imsi == NULL) {
imsi = isA_CFString(CFDictionaryGetValue(properties,
kEAPClientPropEAPSIMAKAIMSI));
if (imsi == NULL) {
return (NULL);
}
}
return (CFRetain(imsi));
}
STATIC CFStringRef
copy_static_identity(CFDictionaryRef properties)
{
CFStringRef imsi;
CFStringRef realm;
CFStringRef ret_identity;
imsi = copy_static_imsi(properties);
if (imsi == NULL) {
return (NULL);
}
realm = copy_static_realm(properties);
ret_identity = copy_imsi_identity(imsi, realm);
my_CFRelease(&imsi);
my_CFRelease(&realm);
return (ret_identity);
}
#if TARGET_OS_EMBEDDED
STATIC CFStringRef
copy_pseudonym_identity(CFStringRef pseudonym, CFStringRef realm)
{
if (realm != NULL) {
return (CFStringCreateWithFormat(NULL, NULL,
CFSTR("%@" "@" "%@"),
pseudonym, realm));
}
return (CFRetain(pseudonym));
}
STATIC CFStringRef
create_identity(EAPSIMAKAPersistentStateRef persist,
EAPSIMAKAAttributeType requested_type,
CFStringRef realm,
Boolean * is_reauth_id_p)
{
CFStringRef ret_identity = NULL;
if (is_reauth_id_p != NULL) {
*is_reauth_id_p = FALSE;
}
if (persist == NULL) {
return (NULL);
}
if (requested_type == kAT_ANY_ID_REQ
|| requested_type == kAT_FULLAUTH_ID_REQ) {
CFStringRef reauth_id;
CFStringRef pseudonym;
reauth_id = EAPSIMAKAPersistentStateGetReauthID(persist);
pseudonym = EAPSIMAKAPersistentStateGetPseudonym(persist);
if (requested_type == kAT_ANY_ID_REQ && reauth_id != NULL) {
if (is_reauth_id_p != NULL) {
*is_reauth_id_p = TRUE;
}
ret_identity = CFRetain(reauth_id);
}
else if (pseudonym != NULL) {
ret_identity = copy_pseudonym_identity(pseudonym, realm);
}
}
if (ret_identity == NULL) {
ret_identity
= copy_imsi_identity(EAPSIMAKAPersistentStateGetIMSI(persist),
realm);
}
return (ret_identity);
}
STATIC CFStringRef
sim_identity_create(EAPSIMAKAPersistentStateRef persist,
CFDictionaryRef properties,
EAPSIMAKAAttributeType identity_type,
Boolean * is_reauth_id_p)
{
CFStringRef realm = NULL;
CFStringRef ret_identity = NULL;
if (is_reauth_id_p != NULL) {
*is_reauth_id_p = FALSE;
}
realm = copy_static_realm(properties);
if (realm == NULL) {
realm = SIMCopyRealm();
}
ret_identity = create_identity(persist, identity_type, realm,
is_reauth_id_p);
my_CFRelease(&realm);
return (ret_identity);
}
#else
STATIC CFStringRef
sim_identity_create(EAPSIMAKAPersistentStateRef persist,
CFDictionaryRef properties,
EAPSIMAKAAttributeType identity_type,
Boolean * is_reauth_id_p)
{
if (is_reauth_id_p != NULL) {
*is_reauth_id_p = FALSE;
}
return (NULL);
}
#endif
static int
S_get_plist_int(CFDictionaryRef plist, CFStringRef key, int def)
{
CFNumberRef n;
int ret = def;
n = isA_CFNumber(CFDictionaryGetValue(plist, key));
if (n != NULL) {
if (CFNumberGetValue(n, kCFNumberIntType, &ret) == FALSE) {
ret = def;
}
}
return (ret);
}
STATIC bool
blocks_are_duplicated(const uint8_t * blocks, int n_blocks, int block_size)
{
int i;
int j;
const uint8_t * scan;
for (i = 0, scan = blocks; i < (n_blocks - 1); i++, scan += block_size) {
const uint8_t * scan_j = scan + block_size;
for (j = i + 1; j < n_blocks; j++, scan_j += block_size) {
if (bcmp(scan, scan_j, block_size) == 0) {
return (TRUE);
}
}
}
return (FALSE);
}
STATIC void
fill_with_random(uint8_t * buf, int len)
{
int i;
int n;
void * p;
uint32_t random;
n = len / sizeof(random);
for (i = 0, p = buf; i < n; i++, p += sizeof(random)) {
random = arc4random();
bcopy(&random, p, sizeof(random));
}
return;
}
STATIC EAPSIMAKAAttributeType
S_get_identity_type(CFDictionaryRef dict)
{
CFStringRef identity_type_str;
if (dict == NULL) {
identity_type_str = NULL;
}
else {
identity_type_str
= CFDictionaryGetValue(dict, kEAPClientPropEAPSIMAKAIdentityType);
identity_type_str = isA_CFString(identity_type_str);
}
return (EAPSIMAKAIdentityTypeGetAttributeType(identity_type_str));
}
STATIC void
EAPSIMContextSetLastIdentity(EAPSIMContextRef context, CFDataRef identity_data)
{
if (identity_data != NULL) {
CFRetain(identity_data);
}
if (context->last_identity != NULL) {
CFRelease(context->last_identity);
}
context->last_identity = identity_data;
return;
}
STATIC void
EAPSIMContextSetVersionList(EAPSIMContextRef context,
const void * list, int count)
{
if (list != NULL) {
int size = count * sizeof(uint16_t);
if (context->version_list != NULL
&& count == context->version_list_count
&& bcmp(context->version_list, list, size) == 0) {
}
else {
if (context->version_list != NULL) {
free(context->version_list);
}
context->version_list = (uint16_t *)malloc(size);
bcopy(list, context->version_list, size);
context->version_list_count = count;
}
}
else if (context->version_list != NULL) {
free(context->version_list);
context->version_list = NULL;
context->version_list_count = 0;
}
return;
}
STATIC void
EAPSIMContextClear(EAPSIMContextRef context)
{
bzero(context, sizeof(*context));
context->plugin_state = kEAPClientStateAuthenticating;
context->state = kEAPSIMClientStateNone;
context->previous_identifier = -1;
return;
}
STATIC CFArrayRef
copy_data_array(CFDictionaryRef properties, CFStringRef prop_name,
int data_size)
{
int count;
int i;
CFArrayRef list;
if (properties == NULL) {
return (NULL);
}
list = CFDictionaryGetValue(properties, prop_name);
if (isA_CFArray(list) == NULL) {
return (NULL);
}
count = CFArrayGetCount(list);
if (count == 0) {
return (NULL);
}
for (i = 0; i < count; i++) {
CFDataRef data = CFArrayGetValueAtIndex(list, i);
if (isA_CFData(data) == NULL
|| CFDataGetLength(data) != data_size) {
return (NULL);
}
}
return (CFRetain(list));
}
STATIC bool
SIMStaticTripletsInitFromProperties(SIMStaticTripletsRef sim_static_p,
CFDictionaryRef properties)
{
int count;
CFArrayRef kc;
CFArrayRef rand;
CFArrayRef sres;
my_CFRelease(&sim_static_p->kc);
my_CFRelease(&sim_static_p->sres);
my_CFRelease(&sim_static_p->rand);
if (properties == NULL) {
return (FALSE);
}
kc = copy_data_array(properties,
kEAPClientPropEAPSIMKcList,
SIM_KC_SIZE);
sres = copy_data_array(properties,
kEAPClientPropEAPSIMSRESList,
SIM_SRES_SIZE);
rand = copy_data_array(properties,
kEAPClientPropEAPSIMRANDList,
SIM_RAND_SIZE);
if (kc == NULL || sres == NULL) {
goto failed;
}
count = CFArrayGetCount(kc);
if (count != CFArrayGetCount(sres)
|| (rand != NULL && count != CFArrayGetCount(rand))) {
goto failed;
}
sim_static_p->kc = kc;
sim_static_p->sres = sres;
sim_static_p->rand = rand;
return (TRUE);
failed:
my_CFRelease(&kc);
my_CFRelease(&sres);
my_CFRelease(&rand);
return (FALSE);
}
STATIC void
EAPSIMContextFree(EAPSIMContextRef context)
{
EAPSIMContextSetVersionList(context, NULL, 0);
SIMStaticTripletsInitFromProperties(&context->sim_static, NULL);
EAPSIMAKAPersistentStateRelease(context->persist);
EAPSIMContextSetLastIdentity(context, NULL);
EAPSIMContextClear(context);
free(context);
return;
}
STATIC int
EAPSIMContextLookupStaticRAND(EAPSIMContextRef context,
const uint8_t rand[SIM_RAND_SIZE])
{
int count;
int i;
if (context->sim_static.rand == NULL) {
return (-1);
}
count = CFArrayGetCount(context->sim_static.rand);
for (i = 0; i < count; i++) {
CFDataRef data;
data = CFArrayGetValueAtIndex(context->sim_static.rand, i);
if (bcmp(rand, CFDataGetBytePtr(data), SIM_RAND_SIZE) == 0) {
return (i);
}
}
return (-1);
}
STATIC bool
EAPSIMContextSIMProcessRAND(EAPSIMContextRef context,
const uint8_t * rand_p, int count,
uint8_t * kc_p, uint8_t * sres_p)
{
int i;
uint8_t * kc_scan;
const uint8_t * rand_scan;
uint8_t * sres_scan;
if (context->sim_static.kc != NULL) {
rand_scan = rand_p;
kc_scan = kc_p;
sres_scan = sres_p;
for (i = 0; i < count; i++) {
CFDataRef kc_data;
CFDataRef sres_data;
int where;
if (context->sim_static.rand != NULL) {
where = EAPSIMContextLookupStaticRAND(context, rand_scan);
if (where == -1) {
EAPLOG(LOG_NOTICE, "eapsim: can't find static RAND value");
return (FALSE);
}
}
else {
where = 0;
}
kc_data = CFArrayGetValueAtIndex(context->sim_static.kc, where);
bcopy(CFDataGetBytePtr(kc_data), kc_scan, SIM_KC_SIZE);
sres_data = CFArrayGetValueAtIndex(context->sim_static.sres, where);
bcopy(CFDataGetBytePtr(sres_data), sres_scan, SIM_SRES_SIZE);
rand_scan += SIM_RAND_SIZE;
kc_scan += SIM_KC_SIZE;
sres_scan += SIM_SRES_SIZE;
}
}
else {
if (SIMAuthenticateGSM(rand_p, count, kc_p, sres_p) == FALSE) {
EAPLOG(LOG_NOTICE, "SIMAuthenticateGSM failed");
return (FALSE);
}
}
return (TRUE);
}
STATIC EAPClientStatus
eapsim_init(EAPClientPluginDataRef plugin, CFArrayRef * require_props,
EAPClientDomainSpecificError * error)
{
EAPSIMContextRef context = NULL;
EAPSIMAKAAttributeType identity_type;
CFStringRef imsi = NULL;
SIMStaticTriplets triplets;
bzero(&triplets, sizeof(triplets));
if (SIMStaticTripletsInitFromProperties(&triplets,
plugin->properties)) {
imsi = copy_static_imsi(plugin->properties);
if (imsi == NULL) {
EAPLOG(LOG_NOTICE,
"eapsim: static triplets specified but IMSI missing");
SIMStaticTripletsInitFromProperties(&triplets, NULL);
return (kEAPClientStatusConfigurationInvalid);
}
}
else {
imsi = SIMCopyIMSI();
if (imsi == NULL) {
EAPLOG(LOG_NOTICE, "EAP-SIM: no SIM available");
return (kEAPClientStatusResourceUnavailable);
}
EAPLOG(LOG_NOTICE, "EAP-SIM: SIM found");
}
context = (EAPSIMContextRef)malloc(sizeof(*context));
if (context == NULL) {
CFRelease(imsi);
(void)SIMStaticTripletsInitFromProperties(&triplets, NULL);
return (kEAPClientStatusAllocationFailed);
}
EAPSIMContextClear(context);
identity_type = S_get_identity_type(plugin->properties);
context->persist
= EAPSIMAKAPersistentStateCreate(kEAPTypeEAPSIM,
CC_SHA1_DIGEST_LENGTH,
imsi, identity_type);
CFRelease(imsi);
context->sim_static = triplets;
context->n_required_rands
= S_get_plist_int(plugin->properties,
kEAPClientPropEAPSIMNumberOfRANDs,
EAPSIM_MAX_RANDS);
if (context->n_required_rands != 2
&& context->n_required_rands != 3) {
EAPLOG(LOG_NOTICE,
"eapsim: EAPSIMNumberOfRands %d is invalid, using 3 instead",
context->n_required_rands);
context->n_required_rands = EAPSIM_MAX_RANDS;
}
if (EAPSIMAKAPersistentStateGetReauthID(context->persist) != NULL) {
fips186_2prf(EAPSIMAKAPersistentStateGetMasterKey(context->persist),
context->key_info.key);
context->key_info_valid = TRUE;
}
context->plugin = plugin;
plugin->private = context;
return (kEAPClientStatusOK);
}
STATIC void
eapsim_free(EAPClientPluginDataRef plugin)
{
EAPSIMContextFree(plugin->private);
plugin->private = NULL;
return;
}
STATIC void
eapsim_free_packet(EAPClientPluginDataRef plugin, EAPPacketRef arg)
{
return;
}
STATIC EAPPacketRef
eapsim_make_response(EAPSIMContextRef context,
EAPPacketRef in_pkt, EAPSIMAKAPacketSubtype subtype,
TLVBufferRef tb_p)
{
EAPSIMPacketRef pkt;
pkt = (EAPSIMPacketRef)context->pkt;
TLVBufferInit(tb_p, pkt->attrs,
sizeof(context->pkt) - offsetof(EAPSIMPacket, attrs));
pkt->code = kEAPCodeResponse;
pkt->identifier = in_pkt->identifier;
pkt->type = kEAPTypeEAPSIM;
pkt->subtype = subtype;
net_uint16_set(pkt->reserved, 0);
return ((EAPPacketRef)pkt);
}
STATIC EAPPacketRef
eapsim_make_client_error(EAPSIMContextRef context,
EAPPacketRef in_pkt, ClientErrorCode code)
{
AttrUnion attr;
EAPPacketRef pkt;
TLVBufferDeclare( tb_p);
pkt = eapsim_make_response(context, in_pkt,
kEAPSIMAKAPacketSubtypeClientError, tb_p);
attr.tlv_p = TLVBufferAllocateTLV(tb_p, kAT_CLIENT_ERROR_CODE,
sizeof(AT_CLIENT_ERROR_CODE));
if (attr.tlv_p == NULL) {
EAPLOG(LOG_NOTICE, "eapsim: failed allocating AT_CLIENT_ERROR_CODE, %s",
TLVBufferErrorString(tb_p));
return (NULL);
}
net_uint16_set(attr.at_client_error_code->ce_client_error_code, code);
EAPPacketSetLength(pkt,
offsetof(EAPSIMPacket, attrs) + TLVBufferUsed(tb_p));
return (pkt);
}
STATIC void
save_persistent_state(EAPSIMContextRef context)
{
CFStringRef ssid = NULL;
#if TARGET_OS_EMBEDDED
CFStringRef trust_domain;
trust_domain = CFDictionaryGetValue(context->plugin->properties,
kEAPClientPropTLSTrustExceptionsDomain);
if (my_CFEqual(trust_domain, kEAPTLSTrustExceptionsDomainWirelessSSID)) {
ssid = CFDictionaryGetValue(context->plugin->properties,
kEAPClientPropTLSTrustExceptionsID);
}
#endif
EAPSIMAKAPersistentStateSave(context->persist, context->key_info_valid,
ssid);
return;
}
STATIC EAPPacketRef
eapsim_start(EAPSIMContextRef context,
const EAPPacketRef in_pkt,
TLVListRef tlvs_p,
EAPClientStatus * client_status)
{
AttrUnion attr;
int count;
int i;
CFStringRef identity = NULL;
EAPSIMAKAAttributeType identity_req_type;
bool good_version = FALSE;
EAPPacketRef pkt = NULL;
const uint8_t * scan;
bool skip_identity = FALSE;
TLVBufferDeclare( tb_p);
AT_VERSION_LIST * version_list_p;
version_list_p = (AT_VERSION_LIST *)
TLVListLookupAttribute(tlvs_p, kAT_VERSION_LIST);
if (version_list_p == NULL) {
EAPLOG(LOG_NOTICE, "eapsim: Start is missing AT_VERSION_LIST");
*client_status = kEAPClientStatusProtocolError;
goto done;
}
count = net_uint16_get(version_list_p->vl_actual_length) / sizeof(uint16_t);
for (i = 0, scan = version_list_p->vl_version_list;
i < count; i++, scan += sizeof(uint16_t)) {
uint16_t this_vers = net_uint16_get(scan);
if (this_vers == kEAPSIMVersion1) {
good_version = TRUE;
break;
}
}
if (good_version == FALSE) {
pkt = eapsim_make_client_error(context, in_pkt,
kClientErrorCodeUnsupportedVersion);
*client_status = kEAPClientStatusProtocolError;
goto done;
}
if (count > 1) {
EAPSIMContextSetVersionList(context,
version_list_p->vl_version_list, count);
}
else {
EAPSIMContextSetVersionList(context, NULL, 0);
}
if (context->state != kEAPSIMClientStateStart) {
context->plugin_state = kEAPClientStateAuthenticating;
context->start_count = 0;
context->last_identity_type = 0;
context->state = kEAPSIMClientStateStart;
}
if (context->start_count == 0) {
fill_with_random(context->nonce_mt, sizeof(context->nonce_mt));
}
context->start_count++;
if (context->start_count > kEAPSIMAKAIdentityAttributesCount) {
EAPLOG(LOG_NOTICE, "eapsim: too many Start packets (%d > %d)",
context->start_count, kEAPSIMAKAIdentityAttributesCount);
*client_status = kEAPClientStatusProtocolError;
goto done;
}
identity_req_type = TLVListLookupIdentityAttribute(tlvs_p);
switch (identity_req_type) {
case kAT_ANY_ID_REQ:
if (context->start_count > 1) {
EAPLOG(LOG_NOTICE,
"eapsim: AT_ANY_ID_REQ at Start #%d",
context->start_count);
*client_status = kEAPClientStatusProtocolError;
goto done;
}
break;
case kAT_FULLAUTH_ID_REQ:
if (context->start_count > 1
&& context->last_identity_type != kAT_ANY_ID_REQ) {
EAPLOG(LOG_NOTICE,
"eapsim: AT_FULLAUTH_ID_REQ follows %s at Start #%d",
EAPSIMAKAAttributeTypeGetString(context->last_identity_type),
context->start_count);
*client_status = kEAPClientStatusProtocolError;
goto done;
}
break;
case kAT_PERMANENT_ID_REQ:
if (context->start_count > 1
&& context->last_identity_type != kAT_ANY_ID_REQ
&& context->last_identity_type != kAT_FULLAUTH_ID_REQ) {
EAPLOG(LOG_NOTICE,
"eapsim: AT_PERMANENT_ID_REQ follows %s at Start #%d",
EAPSIMAKAAttributeTypeGetString(context->last_identity_type),
context->start_count);
*client_status = kEAPClientStatusProtocolError;
goto done;
}
break;
default:
if (context->start_count > 1) {
EAPLOG(LOG_NOTICE,
"eapsim: no *ID_REQ follows %s at Start #%d",
EAPSIMAKAAttributeTypeGetString(context->last_identity_type),
context->start_count);
*client_status = kEAPClientStatusProtocolError;
goto done;
}
skip_identity = TRUE;
break;
}
context->last_identity_type = identity_req_type;
pkt = eapsim_make_response(context, in_pkt, kEAPSIMAKAPacketSubtypeSIMStart,
tb_p);
if (!skip_identity) {
CFDataRef identity_data = NULL;
Boolean reauth_id_used = FALSE;
if (context->sim_static.kc != NULL) {
identity = copy_static_identity(context->plugin->properties);
}
else {
identity = sim_identity_create(context->persist,
context->plugin->properties,
identity_req_type,
&reauth_id_used);
}
if (identity == NULL) {
EAPLOG(LOG_NOTICE, "eapsim: can't find SIM identity");
*client_status = kEAPClientStatusResourceUnavailable;
pkt = NULL;
goto done;
}
if (!TLVBufferAddIdentityString(tb_p, identity, &identity_data)) {
EAPLOG(LOG_NOTICE, "eapsim: can't add AT_IDENTITY, %s",
TLVBufferErrorString(tb_p));
*client_status = kEAPClientStatusInternalError;
pkt = NULL;
goto done;
}
EAPSIMContextSetLastIdentity(context, identity_data);
my_CFRelease(&identity_data);
if (reauth_id_used) {
goto packet_complete;
}
}
context->key_info_valid = FALSE;
attr.tlv_p = TLVBufferAllocateTLV(tb_p, kAT_SELECTED_VERSION,
sizeof(AT_SELECTED_VERSION));
if (attr.tlv_p == NULL) {
EAPLOG(LOG_NOTICE, "eapsim: failed allocating AT_SELECTED_VERSION, %s",
TLVBufferErrorString(tb_p));
*client_status = kEAPClientStatusInternalError;
pkt = NULL;
goto done;
}
net_uint16_set(attr.at_selected_version->sv_selected_version,
kEAPSIMVersion1);
attr.tlv_p = TLVBufferAllocateTLV(tb_p, kAT_NONCE_MT,
sizeof(AT_NONCE_MT));
if (attr.tlv_p == NULL) {
EAPLOG(LOG_NOTICE, "eapsim: failed allocating AT_NONCE_MT, %s",
TLVBufferErrorString(tb_p));
pkt = NULL;
goto done;
}
net_uint16_set(attr.at_nonce_mt->nm_reserved, 0);
bcopy(context->nonce_mt, attr.at_nonce_mt->nm_nonce_mt,
sizeof(context->nonce_mt));
packet_complete:
EAPPacketSetLength(pkt,
offsetof(EAPSIMPacket, attrs) + TLVBufferUsed(tb_p));
done:
my_CFRelease(&identity);
return (pkt);
}
STATIC bool
eapsim_challenge_process_encr_data(EAPSIMContextRef context, TLVListRef tlvs_p)
{
uint8_t * decrypted_buffer = NULL;
TLVListDeclare( decrypted_tlvs_p);
AT_ENCR_DATA * encr_data_p;
AT_IV * iv_p;
CFStringRef next_reauth_id;
CFStringRef next_pseudonym;
TLVListInit(decrypted_tlvs_p);
encr_data_p = (AT_ENCR_DATA *)TLVListLookupAttribute(tlvs_p, kAT_ENCR_DATA);
if (encr_data_p == NULL) {
return (TRUE);
}
iv_p = (AT_IV *)TLVListLookupAttribute(tlvs_p, kAT_IV);
if (iv_p == NULL) {
EAPLOG(LOG_NOTICE,
"eapsim: Challenge missing AT_IV");
return (FALSE);
}
decrypted_buffer
= EAPSIMAKAKeyInfoDecryptTLVList(&context->key_info, encr_data_p, iv_p,
decrypted_tlvs_p);
if (decrypted_buffer == NULL) {
EAPLOG(LOG_NOTICE, "eapsim: Challenge decrypt AT_ENCR_DATA failed");
return (FALSE);
}
{
CFStringRef str;
str = TLVListCopyDescription(decrypted_tlvs_p);
EAPLOG(-LOG_DEBUG, "Decrypted TLVs:\n%@", str);
CFRelease(str);
}
next_reauth_id = TLVListCreateStringFromAttribute(decrypted_tlvs_p,
kAT_NEXT_REAUTH_ID);
if (next_reauth_id != NULL) {
EAPSIMAKAPersistentStateSetReauthID(context->persist,
next_reauth_id);
CFRelease(next_reauth_id);
}
next_pseudonym = TLVListCreateStringFromAttribute(decrypted_tlvs_p,
kAT_NEXT_PSEUDONYM);
if (next_pseudonym != NULL) {
EAPSIMAKAPersistentStateSetPseudonym(context->persist,
next_pseudonym);
CFRelease(next_pseudonym);
}
if (decrypted_buffer != NULL) {
free(decrypted_buffer);
}
TLVListFree(decrypted_tlvs_p);
return (TRUE);
}
STATIC EAPPacketRef
eapsim_challenge(EAPSIMContextRef context,
const EAPPacketRef in_pkt,
TLVListRef tlvs_p,
EAPClientStatus * client_status)
{
int count;
uint8_t kc[SIM_KC_SIZE * EAPSIM_MAX_RANDS];
AT_MAC * mac_p;
EAPPacketRef pkt = NULL;
AT_RAND * rand_p;
uint16_t selected_version = htons(kEAPSIMVersion1);
CC_SHA1_CTX sha1_context;
uint8_t sres[SIM_SRES_SIZE * EAPSIM_MAX_RANDS];
TLVBufferDeclare( tb_p);
if (context->state != kEAPSIMClientStateStart) {
EAPLOG(LOG_NOTICE, "eapsim: Challenge sent without Start");
*client_status = kEAPClientStatusProtocolError;
goto done;
}
context->state = kEAPSIMClientStateChallenge;
EAPSIMAKAPersistentStateSetCounter(context->persist, 1);
context->reauth_success = FALSE;
rand_p = (AT_RAND *)TLVListLookupAttribute(tlvs_p, kAT_RAND);
if (rand_p == NULL) {
EAPLOG(LOG_NOTICE, "eapsim: Challenge is missing AT_RAND");
*client_status = kEAPClientStatusProtocolError;
goto done;
}
count = ((rand_p->ra_length * TLV_ALIGNMENT)
- offsetof(AT_RAND, ra_rand)) / SIM_RAND_SIZE;
if (count < context->n_required_rands) {
EAPLOG(LOG_NOTICE,
"eapsim: Challenge AT_RAND has %d RANDs, policy requires %d",
count, context->n_required_rands);
pkt = eapsim_make_client_error(context, in_pkt,
kClientErrorCodeInsufficientNumberOfChallenges);
*client_status = kEAPClientStatusProtocolError;
goto done;
}
if (count > EAPSIM_MAX_RANDS) {
EAPLOG(LOG_NOTICE,
"eapsim: Challenge AT_RAND has %d RANDs > %d",
count, EAPSIM_MAX_RANDS);
*client_status = kEAPClientStatusProtocolError;
goto done;
}
if (blocks_are_duplicated(rand_p->ra_rand, count, SIM_RAND_SIZE)) {
EAPLOG(LOG_NOTICE,
"eapsim: Challenge AT_RAND has duplicate RANDs");
*client_status = kEAPClientStatusProtocolError;
goto done;
}
if (EAPSIMContextSIMProcessRAND(context, rand_p->ra_rand, count, kc, sres)
== FALSE) {
*client_status = kEAPClientStatusInternalError;
goto done;
}
CC_SHA1_Init(&sha1_context);
if (context->last_identity != NULL) {
CC_SHA1_Update(&sha1_context, CFDataGetBytePtr(context->last_identity),
CFDataGetLength(context->last_identity));
}
else {
CC_SHA1_Update(&sha1_context, context->plugin->username,
context->plugin->username_length);
}
CC_SHA1_Update(&sha1_context, kc, SIM_KC_SIZE * count);
CC_SHA1_Update(&sha1_context, context->nonce_mt, sizeof(context->nonce_mt));
if (context->version_list != NULL) {
CC_SHA1_Update(&sha1_context, context->version_list,
(sizeof(*context->version_list)
* context->version_list_count));
}
else {
CC_SHA1_Update(&sha1_context, &selected_version,
sizeof(selected_version));
}
CC_SHA1_Update(&sha1_context, &selected_version, sizeof(selected_version));
CC_SHA1_Final(EAPSIMAKAPersistentStateGetMasterKey(context->persist),
&sha1_context);
fips186_2prf(EAPSIMAKAPersistentStateGetMasterKey(context->persist),
context->key_info.key);
mac_p = (AT_MAC *)TLVListLookupAttribute(tlvs_p, kAT_MAC);
if (mac_p == NULL) {
EAPLOG(LOG_NOTICE,
"eapsim: Challenge is missing AT_MAC");
*client_status = kEAPClientStatusProtocolError;
goto done;
}
if (!EAPSIMAKAKeyInfoVerifyMAC(&context->key_info, in_pkt,
mac_p->ma_mac,
context->nonce_mt,
sizeof(context->nonce_mt))) {
EAPLOG(LOG_NOTICE,
"eapsim: Challenge AT_MAC not valid");
*client_status = kEAPClientStatusProtocolError;
goto done;
}
if (eapsim_challenge_process_encr_data(context, tlvs_p) == FALSE) {
*client_status = kEAPClientStatusProtocolError;
goto done;
}
pkt = eapsim_make_response(context, in_pkt,
kEAPSIMAKAPacketSubtypeSIMChallenge, tb_p);
mac_p = (AT_MAC *)TLVBufferAllocateTLV(tb_p, kAT_MAC,
sizeof(AT_MAC));
if (mac_p == NULL) {
EAPLOG(LOG_NOTICE, "eapsim: failed allocating AT_MAC, %s",
TLVBufferErrorString(tb_p));
*client_status = kEAPClientStatusInternalError;
pkt = NULL;
goto done;
}
net_uint16_set(mac_p->ma_reserved, 0);
EAPPacketSetLength(pkt,
offsetof(EAPSIMPacket, attrs) + TLVBufferUsed(tb_p));
EAPSIMAKAKeyInfoSetMAC(&context->key_info, pkt, mac_p->ma_mac,
sres, SIM_SRES_SIZE * count);
context->state = kEAPSIMClientStateSuccess;
context->key_info_valid = TRUE;
save_persistent_state(context);
done:
return (pkt);
}
STATIC void
eapsim_compute_reauth_key(EAPSIMContextRef context,
AT_COUNTER * counter_p,
AT_NONCE_S * nonce_s_p)
{
const void * identity;
int identity_length;
if (context->last_identity != NULL) {
identity = CFDataGetBytePtr(context->last_identity);
identity_length = CFDataGetLength(context->last_identity);
}
else {
identity = context->plugin->username;
identity_length = context->plugin->username_length;
}
EAPSIMAKAKeyInfoComputeReauthKey(&context->key_info,
context->persist,
identity, identity_length,
counter_p, nonce_s_p);
return;
}
#define ENCR_BUFSIZE (sizeof(AT_COUNTER) + sizeof(AT_COUNTER_TOO_SMALL))
#define ENCR_BUFSIZE_R AT_ENCR_DATA_ROUNDUP(ENCR_BUFSIZE)
STATIC EAPPacketRef
eapsim_reauthentication(EAPSIMContextRef context,
const EAPPacketRef in_pkt,
TLVListRef tlvs_p,
EAPClientStatus * client_status)
{
uint16_t at_counter;
AT_COUNTER * counter_p;
bool force_fullauth = FALSE;
uint8_t encr_buffer[ENCR_BUFSIZE_R];
TLVBufferDeclare( encr_tb_p);
uint8_t * decrypted_buffer = NULL;
TLVListDeclare( decrypted_tlvs_p);
AT_ENCR_DATA * encr_data_p;
AT_IV * iv_p;
AT_MAC * mac_p;
CFStringRef next_reauth_id;
AT_NONCE_S * nonce_s_p;
EAPPacketRef pkt = NULL;
CFStringRef reauth_id = NULL;
TLVBufferDeclare( tb_p);
TLVListInit(decrypted_tlvs_p);
if (context->key_info_valid == FALSE) {
EAPLOG(LOG_NOTICE,
"eapsim: Reauthentication but no key info available");
*client_status = kEAPClientStatusProtocolError;
goto done;
}
reauth_id = EAPSIMAKAPersistentStateGetReauthID(context->persist);
if (reauth_id == NULL) {
EAPLOG(LOG_NOTICE,
"eapsim: received Reauthentication but don't have reauth id");
*client_status = kEAPClientStatusProtocolError;
goto done;
}
context->state = kEAPSIMClientStateReauthentication;
context->plugin_state = kEAPClientStateAuthenticating;
mac_p = (AT_MAC *)TLVListLookupAttribute(tlvs_p, kAT_MAC);
if (mac_p == NULL) {
EAPLOG(LOG_NOTICE,
"eapsim: Reauthentication is missing AT_MAC");
*client_status = kEAPClientStatusProtocolError;
goto done;
}
if (!EAPSIMAKAKeyInfoVerifyMAC(&context->key_info, in_pkt, mac_p->ma_mac,
NULL, 0)) {
EAPLOG(LOG_NOTICE,
"eapsim: Reauthentication AT_MAC not valid");
*client_status = kEAPClientStatusProtocolError;
goto done;
}
encr_data_p = (AT_ENCR_DATA *)TLVListLookupAttribute(tlvs_p, kAT_ENCR_DATA);
iv_p = (AT_IV *)TLVListLookupAttribute(tlvs_p, kAT_IV);
if (encr_data_p == NULL || iv_p == NULL) {
if (encr_data_p == NULL) {
EAPLOG(LOG_NOTICE,
"eapsim: Reauthentication missing AT_ENCR_DATA");
}
if (iv_p == NULL) {
EAPLOG(LOG_NOTICE,
"eapsim: Reauthentication missing AT_IV");
}
*client_status = kEAPClientStatusProtocolError;
goto done;
}
decrypted_buffer
= EAPSIMAKAKeyInfoDecryptTLVList(&context->key_info, encr_data_p, iv_p,
decrypted_tlvs_p);
if (decrypted_buffer == NULL) {
EAPLOG(LOG_NOTICE,
"eapsim: failed to decrypt Reauthentication AT_ENCR_DATA");
*client_status = kEAPClientStatusProtocolError;
goto done;
}
{
CFStringRef str;
str = TLVListCopyDescription(decrypted_tlvs_p);
EAPLOG(-LOG_DEBUG, "Decrypted TLVs:\n%@", str);
CFRelease(str);
}
nonce_s_p
= (AT_NONCE_S *)TLVListLookupAttribute(decrypted_tlvs_p, kAT_NONCE_S);
counter_p
= (AT_COUNTER *)TLVListLookupAttribute(decrypted_tlvs_p, kAT_COUNTER);
if (nonce_s_p == NULL || counter_p == NULL) {
if (nonce_s_p == NULL) {
EAPLOG(LOG_NOTICE,
"eapsim: Reauthentication AT_ENCR_DATA missing AT_NONCE_S");
}
if (counter_p == NULL) {
EAPLOG(LOG_NOTICE,
"eapsim: Reauthentication AT_ENCR_DATA missing AT_COUNTER");
}
*client_status = kEAPClientStatusProtocolError;
goto done;
}
at_counter = net_uint16_get(counter_p->co_counter);
if (at_counter < EAPSIMAKAPersistentStateGetCounter(context->persist)) {
force_fullauth = TRUE;
}
else {
next_reauth_id = TLVListCreateStringFromAttribute(decrypted_tlvs_p,
kAT_NEXT_REAUTH_ID);
if (next_reauth_id != NULL) {
EAPSIMAKAPersistentStateSetReauthID(context->persist,
next_reauth_id);
CFRelease(next_reauth_id);
}
EAPSIMAKAPersistentStateSetCounter(context->persist, at_counter);
}
pkt = eapsim_make_response(context, in_pkt,
kEAPSIMAKAPacketSubtypeReauthentication, tb_p);
TLVBufferInit(encr_tb_p, encr_buffer, sizeof(encr_buffer));
if (TLVBufferAddCounter(encr_tb_p, at_counter) == FALSE) {
EAPLOG(LOG_NOTICE, "eapsim: failed allocating AT_COUNTER, %s",
TLVBufferErrorString(tb_p));
*client_status = kEAPClientStatusInternalError;
pkt = NULL;
goto done;
}
if (force_fullauth
&& TLVBufferAddCounterTooSmall(encr_tb_p) == FALSE) {
EAPLOG(LOG_NOTICE,
"eapsim: failed allocating AT_COUNTER_TOO_SMALL, %s",
TLVBufferErrorString(tb_p));
*client_status = kEAPClientStatusInternalError;
pkt = NULL;
goto done;
}
if (!EAPSIMAKAKeyInfoEncryptTLVs(&context->key_info, tb_p, encr_tb_p)) {
*client_status = kEAPClientStatusInternalError;
pkt = NULL;
goto done;
}
mac_p = (AT_MAC *)TLVBufferAllocateTLV(tb_p, kAT_MAC,
sizeof(AT_MAC));
if (mac_p == NULL) {
EAPLOG(LOG_NOTICE, "eapsim: failed allocating AT_MAC, %s",
TLVBufferErrorString(tb_p));
*client_status = kEAPClientStatusInternalError;
pkt = NULL;
goto done;
}
net_uint16_set(mac_p->ma_reserved, 0);
EAPPacketSetLength(pkt,
offsetof(EAPSIMPacket, attrs) + TLVBufferUsed(tb_p));
EAPSIMAKAKeyInfoSetMAC(&context->key_info, pkt,
mac_p->ma_mac, nonce_s_p->nc_nonce_s,
sizeof(nonce_s_p->nc_nonce_s));
if (force_fullauth == FALSE) {
context->state = kEAPSIMClientStateSuccess;
eapsim_compute_reauth_key(context, counter_p, nonce_s_p);
context->key_info_valid = TRUE;
context->reauth_success = TRUE;
}
else {
context->key_info_valid = FALSE;
}
save_persistent_state(context);
done:
if (decrypted_buffer != NULL) {
free(decrypted_buffer);
}
TLVListFree(decrypted_tlvs_p);
return (pkt);
}
#define ENCR_BUFSIZE_NOTIF (sizeof(AT_COUNTER))
#define ENCR_BUFSIZE_NOTIF_R AT_ENCR_DATA_ROUNDUP(ENCR_BUFSIZE_NOTIF)
STATIC EAPPacketRef
eapsim_notification(EAPSIMContextRef context,
const EAPPacketRef in_pkt,
TLVListRef tlvs_p,
EAPClientStatus * client_status,
EAPClientDomainSpecificError * error)
{
bool after_auth;
uint16_t current_at_counter;
bool do_replay_protection = FALSE;
AT_NOTIFICATION * notification_p;
AT_MAC * mac_p;
uint16_t notification_code = 0;
EAPPacketRef pkt = NULL;
TLVBufferDeclare( tb_p);
*client_status = kEAPClientStatusOK;
*error = 0;
notification_p =
(AT_NOTIFICATION *)TLVListLookupAttribute(tlvs_p, kAT_NOTIFICATION);
if (notification_p == NULL) {
EAPLOG(LOG_NOTICE, "eapsim: Notification does not contain "
"AT_NOTIFICATION attribute");
*client_status = kEAPClientStatusProtocolError;
goto done;
}
notification_code = net_uint16_get(notification_p->nt_notification);
after_auth = ATNotificationPhaseIsAfterAuthentication(notification_code);
if (ATNotificationCodeIsSuccess(notification_code) && after_auth == FALSE) {
EAPLOG(LOG_NOTICE,
"eapsim: Notification code '%d' indicates "
"success before authentication", notification_code);
*client_status = kEAPClientStatusProtocolError;
goto done;
}
mac_p = (AT_MAC *)TLVListLookupAttribute(tlvs_p, kAT_MAC);
if (mac_p == NULL) {
if (after_auth) {
EAPLOG(LOG_NOTICE, "eapsim: Notification is missing AT_MAC");
*client_status = kEAPClientStatusProtocolError;
goto done;
}
}
else {
if (after_auth == FALSE) {
EAPLOG(LOG_NOTICE,
"eapsim: Notification incorrectly contains AT_MAC");
*client_status = kEAPClientStatusProtocolError;
goto done;
}
if (!EAPSIMAKAKeyInfoVerifyMAC(&context->key_info, in_pkt,
mac_p->ma_mac, NULL, 0)) {
EAPLOG(LOG_NOTICE, "eapsim: Notification AT_MAC not valid");
*client_status = kEAPClientStatusProtocolError;
goto done;
}
}
current_at_counter = EAPSIMAKAPersistentStateGetCounter(context->persist);
do_replay_protection = context->reauth_success && after_auth;
if (do_replay_protection) {
uint16_t at_counter;
uint8_t * decrypted_buffer;
AT_ENCR_DATA * encr_data_p;
TLVListDeclare( decrypted_tlvs_p);
bool has_counter = FALSE;
AT_IV * iv_p;
encr_data_p
= (AT_ENCR_DATA *)TLVListLookupAttribute(tlvs_p, kAT_ENCR_DATA);
iv_p = (AT_IV *)TLVListLookupAttribute(tlvs_p, kAT_IV);
if (encr_data_p == NULL || iv_p == NULL) {
EAPLOG(LOG_NOTICE,
"eapsim: Notification after re-auth missing "
"AT_ENCR_DATA (%p) or AT_IV (%p)", encr_data_p, iv_p);
*client_status = kEAPClientStatusProtocolError;
goto done;
}
decrypted_buffer
= EAPSIMAKAKeyInfoDecryptTLVList(&context->key_info,
encr_data_p, iv_p,
decrypted_tlvs_p);
if (decrypted_buffer != NULL) {
AT_COUNTER * counter_p;
CFStringRef str;
str = TLVListCopyDescription(decrypted_tlvs_p);
EAPLOG(-LOG_DEBUG, "Decrypted TLVs:\n%@", str);
CFRelease(str);
counter_p = (AT_COUNTER *)TLVListLookupAttribute(decrypted_tlvs_p,
kAT_COUNTER);
if (counter_p != NULL) {
at_counter = net_uint16_get(counter_p->co_counter);
has_counter = TRUE;
}
free(decrypted_buffer);
TLVListFree(decrypted_tlvs_p);
}
else {
EAPLOG(LOG_NOTICE,
"eapsim: failed to decrypt Notification AT_ENCR_DATA");
*client_status = kEAPClientStatusInternalError;
goto done;
}
if (!has_counter) {
EAPLOG(LOG_NOTICE,
"eapsim: Notification AT_ENCR_DATA missing AT_COUNTER");
*client_status = kEAPClientStatusProtocolError;
goto done;
}
if (at_counter != current_at_counter) {
EAPLOG(LOG_NOTICE, "eapsim: Notification AT_COUNTER (%d) does not "
"match current counter (%d)", at_counter,
current_at_counter);
*client_status = kEAPClientStatusProtocolError;
goto done;
}
}
pkt = eapsim_make_response(context, in_pkt,
kEAPSIMAKAPacketSubtypeNotification,
tb_p);
if (do_replay_protection) {
uint8_t encr_buffer[ENCR_BUFSIZE_NOTIF_R];
TLVBufferDeclare( encr_tb_p);
TLVBufferInit(encr_tb_p, encr_buffer, sizeof(encr_buffer));
if (TLVBufferAddCounter(encr_tb_p, current_at_counter) == FALSE) {
EAPLOG(LOG_NOTICE, "eapsim: failed to allocate AT_COUNTER, %s",
TLVBufferErrorString(encr_tb_p));
*client_status = kEAPClientStatusAllocationFailed;
goto done;
}
if (!EAPSIMAKAKeyInfoEncryptTLVs(&context->key_info, tb_p, encr_tb_p)) {
*client_status = kEAPClientStatusInternalError;
pkt = NULL;
goto done;
}
}
if (mac_p != NULL) {
mac_p = (AT_MAC *)TLVBufferAllocateTLV(tb_p, kAT_MAC, sizeof(AT_MAC));
if (mac_p == NULL) {
EAPLOG(LOG_NOTICE, "eapsim: failed allocating AT_MAC, %s",
TLVBufferErrorString(tb_p));
*client_status = kEAPClientStatusAllocationFailed;
pkt = NULL;
goto done;
}
net_uint16_set(mac_p->ma_reserved, 0);
}
EAPPacketSetLength(pkt,
offsetof(EAPSIMPacket, attrs) + TLVBufferUsed(tb_p));
if (mac_p != NULL) {
EAPSIMAKAKeyInfoSetMAC(&context->key_info, pkt, mac_p->ma_mac, NULL, 0);
}
if (ATNotificationCodeIsSuccess(notification_code)) {
context->state = kEAPSIMClientStateSuccess;
}
else {
const char * str;
context->state = kEAPSIMClientStateFailure;
*client_status = kEAPClientStatusPluginSpecificError;
*error = EAPSIMAKAStatusForATNotificationCode(notification_code);
str = ATNotificationCodeGetString(notification_code);
if (str == NULL) {
EAPLOG(LOG_NOTICE,
"eapsim: Notification code '%d' unrecognized failure",
notification_code);
}
else {
EAPLOG(LOG_NOTICE, "eapsim: Notification: %s", str);
}
}
done:
return (pkt);
}
STATIC EAPPacketRef
eapsim_request(EAPSIMContextRef context,
const EAPPacketRef in_pkt,
EAPClientStatus * client_status,
EAPClientDomainSpecificError * error)
{
EAPSIMPacketRef eapsim_in = (EAPSIMPacketRef)in_pkt;
EAPPacketRef eapsim_out = NULL;
uint16_t in_length = EAPPacketGetLength(in_pkt);
uint8_t subtype;
TLVListDeclare( tlvs_p);
TLVListInit(tlvs_p);
if (in_length <= kEAPSIMAKAPacketHeaderLength) {
EAPLOG_FL(LOG_NOTICE, "length %d <= %ld",
in_length, kEAPSIMAKAPacketHeaderLength);
*client_status = kEAPClientStatusProtocolError;
goto done;
}
if (TLVListParse(tlvs_p, eapsim_in->attrs,
in_length - kEAPSIMAKAPacketHeaderLength) == FALSE) {
EAPLOG_FL(LOG_NOTICE, "parse failed: %s",
TLVListErrorString(tlvs_p));
*client_status = kEAPClientStatusProtocolError;
goto done;
}
if (context->state != kEAPSIMClientStateNone
&& context->previous_identifier == in_pkt->identifier) {
return ((EAPPacketRef)context->pkt);
}
subtype = eapsim_in->subtype;
switch (subtype) {
case kEAPSIMAKAPacketSubtypeSIMStart:
eapsim_out = eapsim_start(context, in_pkt, tlvs_p, client_status);
break;
case kEAPSIMAKAPacketSubtypeSIMChallenge:
eapsim_out = eapsim_challenge(context, in_pkt, tlvs_p, client_status);
break;
case kEAPSIMAKAPacketSubtypeNotification:
eapsim_out = eapsim_notification(context, in_pkt, tlvs_p,
client_status, error);
break;
case kEAPSIMAKAPacketSubtypeReauthentication:
eapsim_out
= eapsim_reauthentication(context, in_pkt, tlvs_p, client_status);
break;
default:
*client_status = kEAPClientStatusProtocolError;
EAPLOG_FL(LOG_NOTICE, "unexpected Subtype %s",
EAPSIMAKAPacketSubtypeGetString(subtype));
*client_status = kEAPClientStatusProtocolError;
goto done;
}
done:
TLVListFree(tlvs_p);
if (*client_status != kEAPClientStatusOK) {
context->plugin_state = kEAPClientStateFailure;
context->state = kEAPSIMClientStateFailure;
}
if (eapsim_out == NULL
&& *client_status == kEAPClientStatusProtocolError) {
eapsim_out
= eapsim_make_client_error(context, in_pkt,
kClientErrorCodeUnableToProcessPacket);
}
if (eapsim_out != NULL) {
context->previous_identifier = in_pkt->identifier;
}
return (eapsim_out);
}
STATIC EAPClientState
eapsim_process(EAPClientPluginDataRef plugin,
const EAPPacketRef in_pkt,
EAPPacketRef * out_pkt_p,
EAPClientStatus * client_status,
EAPClientDomainSpecificError * error)
{
EAPSIMContextRef context = (EAPSIMContextRef)plugin->private;
*client_status = kEAPClientStatusOK;
*error = 0;
switch (in_pkt->code) {
case kEAPCodeRequest:
*out_pkt_p = eapsim_request(context, in_pkt, client_status, error);
break;
case kEAPCodeSuccess:
context->previous_identifier = -1;
if (context->state == kEAPSIMClientStateSuccess) {
context->plugin_state = kEAPClientStateSuccess;
}
break;
case kEAPCodeFailure:
context->previous_identifier = -1;
context->plugin_state = kEAPClientStateFailure;
break;
default:
break;
}
return (context->plugin_state);
}
STATIC const char *
eapsim_failure_string(EAPClientPluginDataRef plugin)
{
return (NULL);
}
STATIC void *
eapsim_session_key(EAPClientPluginDataRef plugin, int * key_length)
{
EAPSIMContextRef context = (EAPSIMContextRef)plugin->private;
if (context->state == kEAPSIMClientStateSuccess
&& context->key_info_valid) {
*key_length = 32;
return (context->key_info.s.msk);
}
return (NULL);
}
STATIC void *
eapsim_server_key(EAPClientPluginDataRef plugin, int * key_length)
{
EAPSIMContextRef context = (EAPSIMContextRef)plugin->private;
if (context->state == kEAPSIMClientStateSuccess
&& context->key_info_valid) {
*key_length = 32;
return (context->key_info.s.msk + 32);
}
return (NULL);
}
STATIC CFDictionaryRef
eapsim_publish_props(EAPClientPluginDataRef plugin)
{
return (NULL);
}
STATIC CFStringRef
eapsim_user_name_copy(CFDictionaryRef properties)
{
EAPSIMAKAAttributeType identity_type;
CFStringRef imsi;
EAPSIMAKAPersistentStateRef persist;
CFStringRef ret_identity;
ret_identity = copy_static_identity(properties);
if (ret_identity != NULL) {
return (ret_identity);
}
imsi = SIMCopyIMSI();
if (imsi == NULL) {
return (NULL);
}
identity_type = S_get_identity_type(properties);
persist = EAPSIMAKAPersistentStateCreate(kEAPTypeEAPSIM,
CC_SHA1_DIGEST_LENGTH,
imsi, identity_type);
CFRelease(imsi);
if (persist != NULL) {
ret_identity = sim_identity_create(persist, properties,
identity_type, NULL);
EAPSIMAKAPersistentStateRelease(persist);
}
return (ret_identity);
}
STATIC CFStringRef
eapsim_copy_identity(EAPClientPluginDataRef plugin)
{
EAPSIMContextRef context = (EAPSIMContextRef)plugin->private;
EAPSIMContextSetLastIdentity(context, NULL);
context->state = kEAPSIMClientStateNone;
context->previous_identifier = -1;
if (context->sim_static.rand != NULL) {
return (copy_static_identity(plugin->properties));
}
return (sim_identity_create(context->persist, plugin->properties,
kAT_ANY_ID_REQ, NULL));
}
STATIC CFStringRef
eapsim_copy_packet_description(const EAPPacketRef pkt, bool * packet_is_valid)
{
return (EAPSIMAKAPacketCopyDescription(pkt, packet_is_valid));
}
STATIC EAPType
eapsim_type(void)
{
return (kEAPTypeEAPSIM);
}
STATIC const char *
eapsim_name(void)
{
return (EAP_SIM_NAME);
}
STATIC EAPClientPluginVersion
eapsim_version(void)
{
return (kEAPClientPluginVersion);
}
STATIC struct func_table_ent {
const char * name;
void * func;
} func_table[] = {
#if 0
{ kEAPClientPluginFuncNameIntrospect, eapsim_introspect },
#endif
{ kEAPClientPluginFuncNameVersion, eapsim_version },
{ kEAPClientPluginFuncNameEAPType, eapsim_type },
{ kEAPClientPluginFuncNameEAPName, eapsim_name },
{ kEAPClientPluginFuncNameInit, eapsim_init },
{ kEAPClientPluginFuncNameFree, eapsim_free },
{ kEAPClientPluginFuncNameProcess, eapsim_process },
{ kEAPClientPluginFuncNameFreePacket, eapsim_free_packet },
{ kEAPClientPluginFuncNameFailureString, eapsim_failure_string },
{ kEAPClientPluginFuncNameSessionKey, eapsim_session_key },
{ kEAPClientPluginFuncNameServerKey, eapsim_server_key },
{ kEAPClientPluginFuncNamePublishProperties, eapsim_publish_props },
{ kEAPClientPluginFuncNameUserName, eapsim_user_name_copy },
{ kEAPClientPluginFuncNameCopyIdentity, eapsim_copy_identity },
{ kEAPClientPluginFuncNameCopyPacketDescription,
eapsim_copy_packet_description },
{ NULL, NULL},
};
EAPClientPluginFuncRef
eapsim_introspect(EAPClientPluginFuncName name)
{
struct func_table_ent * scan;
for (scan = func_table; scan->name != NULL; scan++) {
if (strcmp(name, scan->name) == 0) {
return (scan->func);
}
}
return (NULL);
}
#ifdef TEST_RAND_DUPS
const uint8_t randval1[3 * SIM_RAND_SIZE] = {
0x0, 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8, 0x9, 0xa, 0xb, 0xc, 0xd, 0xe, 0xf,
0x0, 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8, 0x9, 0xa, 0xb, 0xc, 0xd, 0xe, 0xf,
0x0, 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8, 0x9, 0xa, 0xb, 0xc, 0xd, 0xe, 0xf
};
const uint8_t randval2[3 * SIM_RAND_SIZE] = {
0x0, 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8, 0x9, 0xa, 0xb, 0xc, 0xd, 0xe, 0xf,
0x10, 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8, 0x9, 0xa, 0xb, 0xc, 0xd, 0xe, 0xf,
0x0, 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8, 0x9, 0xa, 0xb, 0xc, 0xd, 0xe, 0xf
};
const uint8_t randval3[3 * SIM_RAND_SIZE] = {
0x10, 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8, 0x9, 0xa, 0xb, 0xc, 0xd, 0xe, 0xf,
0x0, 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8, 0x9, 0xa, 0xb, 0xc, 0xd, 0xe, 0xf,
0x0, 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8, 0x9, 0xa, 0xb, 0xc, 0xd, 0xe, 0xf
};
const uint8_t randval4[3 * SIM_RAND_SIZE] = {
0x0, 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8, 0x9, 0xa, 0xb, 0xc, 0xd, 0xe, 0xf,
0x10, 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8, 0x9, 0xa, 0xb, 0xc, 0xd, 0xe, 0xf,
0x20, 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8, 0x9, 0xa, 0xb, 0xc, 0xd, 0xe, 0xf
};
struct {
const uint8_t * block;
int size;
bool duplicated;
} rands[] = {
{ randval1, sizeof(randval1), TRUE },
{ randval2, sizeof(randval2), TRUE },
{ randval3, sizeof(randval3), TRUE },
{ randval4, sizeof(randval4), FALSE },
{ NULL, 0 }
};
int
main()
{
int i;
for (i = 0; rands[i].block != NULL; i++) {
bool duplicated;
duplicated = blocks_are_duplicated(rands[i].block,
rands[i].size / SIM_RAND_SIZE,
SIM_RAND_SIZE);
if (duplicated == rands[i].duplicated) {
printf("Test %d passed (found%sduplicate)\n", i,
duplicated ? " " : " no ");
}
else {
printf("Test %d failed\n", i);
}
}
exit(0);
return (0);
}
#endif
#ifdef TEST_SET_VERSION_LIST
int
main(int argc, char * argv[])
{
EAPSIMContext context;
uint16_t list1[2] = { 0x1234, 0x5678 };
uint16_t list2[3] = { 0x1, 0x2, 0x3 };
uint16_t list3[2] = { 0x4, 0x5 };
EAPSIMContextClear(&context);
EAPSIMContextSetVersionList(&context,
list1, sizeof(list1) / sizeof(list1[0]));
EAPSIMContextSetVersionList(&context,
list1, sizeof(list1) / sizeof(list1[0]));
EAPSIMContextSetVersionList(&context,
list2, sizeof(list2) / sizeof(list2[0]));
EAPSIMContextSetVersionList(&context,
list2, sizeof(list2) / sizeof(list2[0]));
EAPSIMContextSetVersionList(&context,
list3, sizeof(list3) / sizeof(list3[0]));
EAPSIMContextSetVersionList(&context,
list3, sizeof(list3) / sizeof(list3[0]));
EAPSIMContextSetVersionList(&context, NULL, 0);
exit(0);
return (0);
}
#endif
#ifdef TEST_SIM_INFO
#if TARGET_OS_EMBEDDED
int
main()
{
CFStringRef identity;
identity = eapsim_user_name_copy(NULL);
if (identity != NULL) {
CFShow(identity);
CFRelease(identity);
}
exit(0);
return (0);
}
#endif
#endif