#include <freeradius-devel/ident.h>
RCSID("$Id$")
#include <freeradius-devel/autoconf.h>
#include <stdio.h>
#include <stdlib.h>
#include "../../eap.h"
#include "eap_types.h"
#include "eap_sim.h"
#include <freeradius-devel/rad_assert.h>
struct eap_sim_server_state {
enum eapsim_serverstates state;
struct eapsim_keys keys;
int sim_id;
};
static void add_reply(VALUE_PAIR** vp,
const char* name, const uint8_t *value, size_t len)
{
VALUE_PAIR *reply_attr;
reply_attr = pairmake(name, "", T_OP_EQ);
if (!reply_attr) {
DEBUG("rlm_eap_sim: "
"add_reply failed to create attribute %s: %s\n",
name, fr_strerror());
return;
}
memcpy(reply_attr->vp_strvalue, value, len);
reply_attr->length = len;
pairadd(vp, reply_attr);
}
static void eap_sim_state_free(void *opaque)
{
struct eap_sim_server_state *ess = (struct eap_sim_server_state *)opaque;
if (!ess) return;
free(ess);
}
static int eap_sim_compose(EAP_HANDLER *handler)
{
handler->eap_ds->set_request_id = 1;
return map_eapsim_basictypes(handler->request->reply,
handler->eap_ds->request);
}
static int eap_sim_sendstart(EAP_HANDLER *handler)
{
VALUE_PAIR **vps, *newvp;
uint16_t *words;
struct eap_sim_server_state *ess;
rad_assert(handler->request != NULL);
rad_assert(handler->request->reply);
ess = (struct eap_sim_server_state *)handler->opaque;
vps = &handler->request->reply->vps;
rad_assert(vps != NULL);
newvp = paircreate(ATTRIBUTE_EAP_SIM_BASE+PW_EAP_SIM_VERSION_LIST,
PW_TYPE_OCTETS);
words = (uint16_t *)newvp->vp_strvalue;
newvp->length = 3*sizeof(uint16_t);
words[0] = htons(1*sizeof(uint16_t));
words[1] = htons(EAP_SIM_VERSION);
words[2] = 0;
pairadd(vps, newvp);
newvp = paircreate(ATTRIBUTE_EAP_ID, PW_TYPE_INTEGER);
newvp->vp_integer = ess->sim_id++;
pairreplace(vps, newvp);
ess->keys.versionlistlen = 2;
memcpy(ess->keys.versionlist, words+1, ess->keys.versionlistlen);
newvp = paircreate(ATTRIBUTE_EAP_SIM_BASE+PW_EAP_SIM_FULLAUTH_ID_REQ,
PW_TYPE_OCTETS);
newvp->length = 2;
newvp->vp_strvalue[0]=0;
newvp->vp_strvalue[0]=1;
pairadd(vps, newvp);
newvp = paircreate(ATTRIBUTE_EAP_SIM_SUBTYPE, PW_TYPE_INTEGER);
newvp->vp_integer = eapsim_start;
pairreplace(vps, newvp);
return 1;
}
static int eap_sim_getchalans(VALUE_PAIR *vps, int chalno,
struct eap_sim_server_state *ess)
{
VALUE_PAIR *vp;
rad_assert(chalno >= 0 && chalno < 3);
vp = pairfind(vps, ATTRIBUTE_EAP_SIM_RAND1+chalno);
if(vp == NULL) {
DEBUG2(" eap-sim can not find sim-challenge%d",chalno+1);
return 0;
}
if(vp->length != EAPSIM_RAND_SIZE) {
DEBUG2(" eap-sim chal%d is not 8-bytes: %d", chalno+1,
vp->length);
return 0;
}
memcpy(ess->keys.rand[chalno], vp->vp_strvalue, EAPSIM_RAND_SIZE);
vp = pairfind(vps, ATTRIBUTE_EAP_SIM_SRES1+chalno);
if(vp == NULL) {
DEBUG2(" eap-sim can not find sim-sres%d",chalno+1);
return 0;
}
if(vp->length != EAPSIM_SRES_SIZE) {
DEBUG2(" eap-sim sres%d is not 16-bytes: %d", chalno+1,
vp->length);
return 0;
}
memcpy(ess->keys.sres[chalno], vp->vp_strvalue, EAPSIM_SRES_SIZE);
vp = pairfind(vps, ATTRIBUTE_EAP_SIM_KC1+chalno);
if(vp == NULL) {
DEBUG2(" eap-sim can not find sim-kc%d",chalno+1);
return 0;
}
if(vp->length != EAPSIM_Kc_SIZE) {
DEBUG2(" eap-sim kc%d is not 8-bytes: %d", chalno+1,
vp->length);
return 0;
}
memcpy(ess->keys.Kc[chalno], vp->vp_strvalue, EAPSIM_Kc_SIZE);
return 1;
}
static int eap_sim_sendchallenge(EAP_HANDLER *handler)
{
struct eap_sim_server_state *ess;
VALUE_PAIR **invps, **outvps, *newvp;
ess = (struct eap_sim_server_state *)handler->opaque;
rad_assert(handler->request != NULL);
rad_assert(handler->request->reply);
invps = &handler->request->packet->vps;
outvps= &handler->request->reply->vps;
if ((debug_flag > 0) && fr_log_fp) {
fprintf(fr_log_fp, "+++> EAP-sim decoded packet:\n");
debug_pair_list(*invps);
}
newvp = paircreate(ATTRIBUTE_EAP_SIM_BASE+PW_EAP_SIM_RAND,
PW_TYPE_OCTETS);
memset(newvp->vp_strvalue, 0, 2);
memcpy(newvp->vp_strvalue+2+EAPSIM_RAND_SIZE*0, ess->keys.rand[0], EAPSIM_RAND_SIZE);
memcpy(newvp->vp_strvalue+2+EAPSIM_RAND_SIZE*1, ess->keys.rand[1], EAPSIM_RAND_SIZE);
memcpy(newvp->vp_strvalue+2+EAPSIM_RAND_SIZE*2, ess->keys.rand[2], EAPSIM_RAND_SIZE);
newvp->length = 2+EAPSIM_RAND_SIZE*3;
pairadd(outvps, newvp);
newvp = paircreate(ATTRIBUTE_EAP_ID, PW_TYPE_INTEGER);
newvp->vp_integer = ess->sim_id++;
pairreplace(outvps, newvp);
ess->keys.identitylen = strlen(handler->identity);
memcpy(ess->keys.identity, handler->identity, ess->keys.identitylen);
eapsim_calculate_keys(&ess->keys);
#ifdef EAP_SIM_DEBUG_PRF
eapsim_dump_mk(&ess->keys);
#endif
newvp = paircreate(ATTRIBUTE_EAP_SIM_BASE+PW_EAP_SIM_MAC,
PW_TYPE_OCTETS);
memcpy(newvp->vp_strvalue, ess->keys.nonce_mt, 16);
newvp->length = 16;
pairreplace(outvps, newvp);
newvp = paircreate(ATTRIBUTE_EAP_SIM_KEY, PW_TYPE_OCTETS);
memcpy(newvp->vp_strvalue, ess->keys.K_aut, 16);
newvp->length = 16;
pairreplace(outvps, newvp);
newvp = paircreate(ATTRIBUTE_EAP_SIM_SUBTYPE, PW_TYPE_INTEGER);
newvp->vp_integer = eapsim_challenge;
pairreplace(outvps, newvp);
return 1;
}
#ifndef EAPTLS_MPPE_KEY_LEN
#define EAPTLS_MPPE_KEY_LEN 32
#endif
static int eap_sim_sendsuccess(EAP_HANDLER *handler)
{
unsigned char *p;
struct eap_sim_server_state *ess;
VALUE_PAIR **outvps;
VALUE_PAIR *newvp;
outvps= &handler->request->reply->vps;
ess = (struct eap_sim_server_state *)handler->opaque;
newvp = paircreate(ATTRIBUTE_EAP_ID, PW_TYPE_INTEGER);
newvp->vp_integer = ess->sim_id++;
pairreplace(outvps, newvp);
p = ess->keys.msk;
add_reply(outvps, "MS-MPPE-Recv-Key", p, EAPTLS_MPPE_KEY_LEN);
p += EAPTLS_MPPE_KEY_LEN;
add_reply(outvps, "MS-MPPE-Send-Key", p, EAPTLS_MPPE_KEY_LEN);
return 1;
}
static void eap_sim_stateenter(EAP_HANDLER *handler,
struct eap_sim_server_state *ess,
enum eapsim_serverstates newstate)
{
switch(newstate) {
case eapsim_server_start:
eap_sim_sendstart(handler);
break;
case eapsim_server_challenge:
eap_sim_sendchallenge(handler);
break;
case eapsim_server_success:
eap_sim_sendsuccess(handler);
handler->eap_ds->request->code = PW_EAP_SUCCESS;
break;
default:
break;
}
ess->state = newstate;
eap_sim_compose(handler);
}
static int eap_sim_initiate(void *type_data, EAP_HANDLER *handler)
{
struct eap_sim_server_state *ess;
VALUE_PAIR *vp;
VALUE_PAIR *outvps;
time_t n;
outvps = handler->request->reply->vps;
type_data = type_data;
vp = pairfind(outvps, ATTRIBUTE_EAP_SIM_RAND1);
if(vp == NULL) {
DEBUG2(" can not initiate sim, no RAND1 attribute");
return 0;
}
ess = malloc(sizeof(struct eap_sim_server_state));
if(ess == NULL) {
DEBUG2(" no space for eap sim state");
return 0;
}
handler->opaque = ((void *)ess);
handler->free_opaque = eap_sim_state_free;
handler->stage = AUTHENTICATE;
if((eap_sim_getchalans(outvps, 0, ess) +
eap_sim_getchalans(outvps, 1, ess) +
eap_sim_getchalans(outvps, 2, ess)) != 3)
{
DEBUG2(" can not initiate sim, missing attributes");
return 0;
}
time(&n);
ess->sim_id = (n & 0xff);
eap_sim_stateenter(handler, ess, eapsim_server_start);
return 1;
}
static int process_eap_sim_start(EAP_HANDLER *handler, VALUE_PAIR *vps)
{
VALUE_PAIR *nonce_vp, *selectedversion_vp;
struct eap_sim_server_state *ess;
uint16_t simversion;
ess = (struct eap_sim_server_state *)handler->opaque;
nonce_vp = pairfind(vps, ATTRIBUTE_EAP_SIM_BASE+PW_EAP_SIM_NONCE_MT);
selectedversion_vp = pairfind(vps, ATTRIBUTE_EAP_SIM_BASE+PW_EAP_SIM_SELECTED_VERSION);
if(nonce_vp == NULL ||
selectedversion_vp == NULL) {
DEBUG2(" client did not select a version and send a NONCE");
eap_sim_stateenter(handler, ess, eapsim_server_start);
return 1;
}
if(selectedversion_vp->length < 2) {
DEBUG2(" EAP-Sim version field is too short.");
return 0;
}
memcpy(&simversion, selectedversion_vp->vp_strvalue, sizeof(simversion));
simversion = ntohs(simversion);
if(simversion != EAP_SIM_VERSION) {
DEBUG2(" EAP-Sim version %d is unknown.", simversion);
return 0;
}
memcpy(ess->keys.versionselect, selectedversion_vp->vp_strvalue,
sizeof(ess->keys.versionselect));
if(nonce_vp->length != 18) {
DEBUG2(" EAP-Sim nonce_mt must be 16 bytes (+2 bytes padding), not %d", nonce_vp->length);
return 0;
}
memcpy(ess->keys.nonce_mt, nonce_vp->vp_strvalue+2, 16);
eap_sim_stateenter(handler, ess, eapsim_server_challenge);
return 1;
}
static int process_eap_sim_challenge(EAP_HANDLER *handler, VALUE_PAIR *vps)
{
struct eap_sim_server_state *ess;
uint8_t srescat[EAPSIM_SRES_SIZE*3];
uint8_t calcmac[EAPSIM_CALCMAC_SIZE];
ess = (struct eap_sim_server_state *)handler->opaque;
memcpy(srescat +(0*EAPSIM_SRES_SIZE), ess->keys.sres[0], EAPSIM_SRES_SIZE);
memcpy(srescat +(1*EAPSIM_SRES_SIZE), ess->keys.sres[1], EAPSIM_SRES_SIZE);
memcpy(srescat +(2*EAPSIM_SRES_SIZE), ess->keys.sres[2], EAPSIM_SRES_SIZE);
if(eapsim_checkmac(vps, ess->keys.K_aut,
srescat, sizeof(srescat),
calcmac)) {
DEBUG2("MAC check succeed\n");
} else {
int i, j;
char macline[20*3];
char *m = macline;
j=0;
for (i = 0; i < EAPSIM_CALCMAC_SIZE; i++) {
if(j==4) {
*m++ = '_';
j=0;
}
j++;
sprintf(m, "%02x", calcmac[i]);
m = m + strlen(m);
}
DEBUG2("calculated MAC (%s) did not match", macline);
return 0;
}
eap_sim_stateenter(handler, ess, eapsim_server_success);
return 1;
}
static int eap_sim_authenticate(void *arg, EAP_HANDLER *handler)
{
struct eap_sim_server_state *ess;
VALUE_PAIR *vp, *vps;
enum eapsim_subtype subtype;
int success;
arg = arg;
ess = (struct eap_sim_server_state *)handler->opaque;
vps = handler->request->packet->vps;
success= unmap_eapsim_basictypes(handler->request->packet,
handler->eap_ds->response->type.data,
handler->eap_ds->response->type.length);
if(!success) {
return 0;
}
if((vp = pairfind(vps, ATTRIBUTE_EAP_SIM_SUBTYPE)) == NULL)
{
DEBUG2(" no subtype attribute was created, message dropped");
return 0;
}
subtype = vp->vp_integer;
if (subtype == eapsim_client_error) {
return 0;
}
switch(ess->state) {
case eapsim_server_start:
switch(subtype) {
default:
eap_sim_stateenter(handler, ess, eapsim_server_start);
return 1;
case eapsim_start:
return process_eap_sim_start(handler, vps);
}
break;
case eapsim_server_challenge:
switch(subtype) {
default:
eap_sim_stateenter(handler, ess, eapsim_server_challenge);
return 1;
case eapsim_challenge:
return process_eap_sim_challenge(handler, vps);
}
break;
default:
DEBUG2(" illegal-unknown state reached in eap_sim_authenticate\n");
rad_assert(0 == 1);
}
return 0;
}
EAP_TYPE rlm_eap_sim = {
"eap_sim",
NULL,
eap_sim_initiate,
NULL,
eap_sim_authenticate,
NULL
};