#include <freeradius-devel/ident.h>
RCSID("$Id$")
#include <freeradius-devel/autoconf.h>
#include <freeradius-devel/missing.h>
#include <freeradius-devel/libradius.h>
#include "eap_types.h"
#include "eap_sim.h"
#include <freeradius-devel/sha1.h>
int map_eapsim_basictypes(RADIUS_PACKET *r, EAP_PACKET *ep)
{
VALUE_PAIR *vp;
int encoded_size;
uint8_t *encodedmsg, *attr;
unsigned int id, eapcode;
unsigned char *macspace, *append;
int appendlen;
unsigned char subtype;
macspace = NULL;
append = NULL;
appendlen = 0;
vp = pairfind(r->vps, ATTRIBUTE_EAP_SIM_SUBTYPE);
if(vp == NULL)
{
subtype = eapsim_start;
}
else
{
subtype = vp->vp_integer;
}
vp = pairfind(r->vps, ATTRIBUTE_EAP_ID);
if(vp == NULL)
{
id = ((int)getpid() & 0xff);
}
else
{
id = vp->vp_integer;
}
vp = pairfind(r->vps, ATTRIBUTE_EAP_CODE);
if(vp == NULL)
{
eapcode = PW_EAP_REQUEST;
}
else
{
eapcode = vp->vp_integer;
}
encoded_size = 0;
for(vp = r->vps; vp != NULL; vp = vp->next)
{
int roundedlen;
int vplen;
if(vp->attribute < ATTRIBUTE_EAP_SIM_BASE ||
vp->attribute >= ATTRIBUTE_EAP_SIM_BASE+256)
{
continue;
}
vplen = vp->length;
if(vp->attribute == ATTRIBUTE_EAP_SIM_BASE+PW_EAP_SIM_MAC) {
vplen = 18;
}
roundedlen = (vplen + 2 + 3) & ~3;
encoded_size += roundedlen;
}
if (ep->code != PW_EAP_SUCCESS)
ep->code = eapcode;
ep->id = (id & 0xff);
ep->type.type = PW_EAP_SIM;
if(encoded_size == 0)
{
encodedmsg = malloc(3);
encodedmsg[0]=subtype;
encodedmsg[1]=0;
encodedmsg[2]=0;
ep->type.length = 3;
ep->type.data = encodedmsg;
return 0;
}
encoded_size += 3;
encodedmsg = malloc(encoded_size);
if (encodedmsg == NULL) {
radlog(L_ERR, "eapsim: out of memory allocating %d bytes", encoded_size+5);
return 0;
}
memset(encodedmsg, 0, encoded_size);
attr = encodedmsg+3;
for(vp = r->vps; vp != NULL; vp = vp->next)
{
int roundedlen;
if(vp->attribute < ATTRIBUTE_EAP_SIM_BASE ||
vp->attribute >= ATTRIBUTE_EAP_SIM_BASE+256)
{
continue;
}
if(vp->attribute == ATTRIBUTE_EAP_SIM_BASE+PW_EAP_SIM_MAC) {
roundedlen = 20;
memset(&attr[2], 0, 18);
macspace = &attr[4];
append = vp->vp_octets;
appendlen = vp->length;
}
else {
roundedlen = (vp->length + 2 + 3) & ~3;
memset(attr, 0, roundedlen);
memcpy(&attr[2], vp->vp_strvalue, vp->length);
}
attr[0] = vp->attribute - ATTRIBUTE_EAP_SIM_BASE;
attr[1] = roundedlen >> 2;
attr += roundedlen;
}
encodedmsg[0] = subtype;
ep->type.length = encoded_size;
ep->type.data = encodedmsg;
vp = pairfind(r->vps, ATTRIBUTE_EAP_SIM_KEY);
if(macspace != NULL && vp != NULL)
{
unsigned char *buffer;
eap_packet_t *hdr;
uint16_t hmaclen, total_length = 0;
unsigned char sha1digest[20];
total_length = EAP_HEADER_LEN + 1 + encoded_size;
hmaclen = total_length + appendlen;
buffer = (unsigned char *)malloc(hmaclen);
hdr = (eap_packet_t *)buffer;
if (!hdr) {
radlog(L_ERR, "rlm_eap: out of memory");
free(encodedmsg);
return 0;
}
hdr->code = eapcode & 0xFF;
hdr->id = (id & 0xFF);
total_length = htons(total_length);
memcpy(hdr->length, &total_length, sizeof(total_length));
hdr->data[0] = PW_EAP_SIM;
memcpy(&hdr->data[1], encodedmsg, encoded_size);
memcpy(&hdr->data[encoded_size+1], append, appendlen);
fr_hmac_sha1(buffer, hmaclen,
vp->vp_octets, vp->length,
sha1digest);
free(buffer);
memcpy(macspace, sha1digest, 16);
}
if(macspace != NULL && vp == NULL)
{
if(encodedmsg != NULL)
free(encodedmsg);
return 0;
}
return 1;
}
int unmap_eapsim_basictypes(RADIUS_PACKET *r,
uint8_t *attr, unsigned int attrlen)
{
VALUE_PAIR *newvp;
int eapsim_attribute;
unsigned int eapsim_len;
int es_attribute_count;
es_attribute_count=0;
if(attrlen < 5) {
radlog(L_ERR, "eap: EAP-Sim attribute too short: %d < 2", attrlen);
return 0;
}
newvp = paircreate(ATTRIBUTE_EAP_SIM_SUBTYPE, PW_TYPE_INTEGER);
if (!newvp) return 0;
newvp->vp_integer = attr[0];
newvp->length = 1;
pairadd(&(r->vps), newvp);
attr += 3;
attrlen -= 3;
while(attrlen > 0)
{
if(attrlen < 2) {
radlog(L_ERR, "eap: EAP-Sim attribute %d too short: %d < 2", es_attribute_count, attrlen);
return 0;
}
eapsim_attribute = attr[0];
eapsim_len = attr[1] * 4;
if(eapsim_len > attrlen) {
radlog(L_ERR, "eap: EAP-Sim attribute %d (no.%d) has length longer than data (%d > %d)"
, eapsim_attribute
, es_attribute_count, eapsim_len, attrlen);
return 0;
}
if(eapsim_len > MAX_STRING_LEN) {
eapsim_len = MAX_STRING_LEN;
}
if (eapsim_len < 2) {
radlog(L_ERR, "eap: EAP-Sim attribute %d (no.%d) has length too small",
eapsim_attribute, es_attribute_count);
return 0;
}
newvp = paircreate(eapsim_attribute+ATTRIBUTE_EAP_SIM_BASE, PW_TYPE_OCTETS);
memcpy(newvp->vp_strvalue, &attr[2], eapsim_len-2);
newvp->length = eapsim_len-2;
pairadd(&(r->vps), newvp);
newvp = NULL;
attr += eapsim_len;
attrlen -= eapsim_len;
es_attribute_count++;
}
return 1;
}
int
eapsim_checkmac(VALUE_PAIR *rvps,
uint8_t key[EAPSIM_AUTH_SIZE],
uint8_t *extra, int extralen,
uint8_t calcmac[20])
{
int ret;
eap_packet_t *e;
uint8_t *buffer;
int elen,len;
VALUE_PAIR *mac;
mac = pairfind(rvps, ATTRIBUTE_EAP_SIM_BASE+PW_EAP_SIM_MAC);
if(mac == NULL
|| mac->length != 18) {
return 0;
}
e = eap_vp2packet(rvps);
if(e == NULL)
{
return 0;
}
elen = e->length[0] * 256 + e->length[1];
len = elen + extralen;
buffer = malloc(len);
if(buffer == NULL)
{
free(e);
return 0;
}
memcpy(buffer, e, elen);
memcpy(buffer+elen, extra, extralen);
{
uint8_t *attr;
attr = buffer+8;
while(attr < (buffer+elen)) {
if(attr[0] == PW_EAP_SIM_MAC) {
if(attr[1] < 5) {
ret = 0;
goto done;
}
memset(&attr[4], 0, (attr[1]-1)*4);
}
attr += attr[1]*4;
}
}
fr_hmac_sha1(buffer, len,
key, 16,
calcmac);
if(memcmp(&mac->vp_strvalue[2], calcmac, 16) == 0) {
ret = 1;
} else {
ret = 0;
}
done:
free(e);
free(buffer);
return(ret);
}
const char *simstates[]={ "init", "start", NULL };
const char *sim_state2name(enum eapsim_clientstates state,
char *statenamebuf,
int statenamebuflen)
{
if(state >= eapsim_client_maxstates)
{
snprintf(statenamebuf, statenamebuflen,
"eapstate:%d", state);
return statenamebuf;
}
else
{
return simstates[state];
}
}
const char *subtypes[]={ "subtype0", "subtype1", "subtype2", "subtype3",
"subtype4", "subtype5", "subtype6", "subtype7",
"subtype8", "subtype9",
"start",
"challenge",
"notification",
"reauth",
"client-error",
NULL };
const char *sim_subtype2name(enum eapsim_subtype subtype,
char *subtypenamebuf,
int subtypenamebuflen)
{
if(subtype >= eapsim_max_subtype)
{
snprintf(subtypenamebuf, subtypenamebuflen,
"illegal-subtype:%d", subtype);
return subtypenamebuf;
}
else
{
return subtypes[subtype];
}
}