static int eap_sim_sendstart(eap_handler_t *handler)
{
	VALUE_PAIR **vps, *newvp;
	uint16_t words[3];
	eap_sim_state_t *ess;
	RADIUS_PACKET *packet;
	uint8_t *p;

	rad_assert(handler->request != NULL);
	rad_assert(handler->request->reply);

	ess = (eap_sim_state_t *)handler->opaque;

	/* these are the outgoing attributes */
	packet = handler->request->reply;
	vps = &packet->vps;
	rad_assert(vps != NULL);


	/*
	 *	Add appropriate TLVs for the EAP things we wish to send.
	 */

	/* the version list. We support only version 1. */
	words[0] = htons(sizeof(words[1]));
	words[1] = htons(EAP_SIM_VERSION);
	words[2] = 0;

	newvp = paircreate(packet, PW_EAP_SIM_VERSION_LIST, 0);
	pairmemcpy(newvp, (uint8_t const *) words, sizeof(words));

	pairadd(vps, newvp);

	/* set the EAP_ID - new value */
	newvp = paircreate(packet, PW_EAP_ID, 0);
	newvp->vp_integer = ess->sim_id++;
	pairreplace(vps, newvp);

	/* record it in the ess */
	ess->keys.versionlistlen = 2;
	memcpy(ess->keys.versionlist, words + 1, ess->keys.versionlistlen);

	/* the ANY_ID attribute. We do not support re-auth or pseudonym */
	newvp = paircreate(packet, PW_EAP_SIM_FULLAUTH_ID_REQ, 0);
	newvp->length = 2;
	newvp->vp_octets = p = talloc_array(newvp, uint8_t, 2);

	p[0] = 0;
	p[0] = 1;
	pairadd(vps, newvp);

	/* the SUBTYPE, set to start. */
	newvp = paircreate(packet, PW_EAP_SIM_SUBTYPE, 0);
	newvp->vp_integer = EAPSIM_START;
	pairreplace(vps, newvp);

	return 1;
}
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;

	/* these are the outgoing attributes */
	vps = &handler->request->reply->vps;

	rad_assert(vps != NULL);

	/*
	 * add appropriate TLVs for the EAP things we wish to send.
	 */

	/* the version list. We support only version 1. */
	newvp = paircreate(ATTRIBUTE_EAP_SIM_BASE+PW_EAP_SIM_VERSION_LIST, 0,
			   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);

	/* set the EAP_ID - new value */
	newvp = paircreate(ATTRIBUTE_EAP_ID, 0, PW_TYPE_INTEGER);
	newvp->vp_integer = ess->sim_id++;
	pairreplace(vps, newvp);

	/* record it in the ess */
	ess->keys.versionlistlen = 2;
	memcpy(ess->keys.versionlist, words+1, ess->keys.versionlistlen);

	/* the ANY_ID attribute. We do not support re-auth or pseudonym */
	newvp = paircreate(ATTRIBUTE_EAP_SIM_BASE+PW_EAP_SIM_FULLAUTH_ID_REQ,
			   0, PW_TYPE_OCTETS);
	newvp->length = 2;
	newvp->vp_strvalue[0]=0;
	newvp->vp_strvalue[0]=1;
	pairadd(vps, newvp);

	/* the SUBTYPE, set to start. */
	newvp = paircreate(ATTRIBUTE_EAP_SIM_SUBTYPE, 0, PW_TYPE_INTEGER);
	newvp->vp_integer = eapsim_start;
	pairreplace(vps, newvp);

	return 1;
}
/*
 * this code sends the success message.
 *
 * the only work to be done is the add the appropriate SEND/RECV
 * radius attributes derived from the MSK.
 *
 */
static int eap_sim_sendsuccess(eap_handler_t *handler)
{
	unsigned char *p;
	struct eap_sim_server_state *ess;
	VALUE_PAIR *vp;
	RADIUS_PACKET *packet;

	/* outvps is the data to the client. */
	packet = handler->request->reply;
	ess = (struct eap_sim_server_state *)handler->opaque;

	/* set the EAP_ID - new value */
	vp = paircreate(packet, ATTRIBUTE_EAP_ID, 0);
	vp->vp_integer = ess->sim_id++;
	pairreplace(&handler->request->reply->vps, vp);

	p = ess->keys.msk;
	eap_add_reply(handler->request, "MS-MPPE-Recv-Key", p, EAPTLS_MPPE_KEY_LEN);
	p += EAPTLS_MPPE_KEY_LEN;
	eap_add_reply(handler->request, "MS-MPPE-Send-Key", p, EAPTLS_MPPE_KEY_LEN);
	return 1;
}
/*
 * this code sends the success message.
 *
 * the only work to be done is the add the appropriate SEND/RECV
 * radius attributes derived from the MSK.
 *
 */
static int eap_sim_sendsuccess(EAP_HANDLER *handler)
{
        unsigned char *p;
	struct eap_sim_server_state *ess;
	VALUE_PAIR **outvps;
	VALUE_PAIR *newvp;

	/* outvps is the data to the client. */
	outvps= &handler->request->reply->vps;
	ess = (struct eap_sim_server_state *)handler->opaque;

	/* set the EAP_ID - new value */
	newvp = paircreate(ATTRIBUTE_EAP_ID, 0, 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;
}
/** Send the challenge itself
 *
 * Challenges will come from one of three places eventually:
 *
 * 1  from attributes like PW_EAP_SIM_RANDx
 *	    (these might be retrived from a database)
 *
 * 2  from internally implemented SIM authenticators
 *	    (a simple one based upon XOR will be provided)
 *
 * 3  from some kind of SS7 interface.
 *
 * For now, they only come from attributes.
 * It might be that the best way to do 2/3 will be with a different
 * module to generate/calculate things.
 *
 */
static int eap_sim_sendchallenge(eap_handler_t *handler)
{
	REQUEST *request = handler->request;
	eap_sim_state_t *ess;
	VALUE_PAIR **invps, **outvps, *newvp;
	RADIUS_PACKET *packet;
	uint8_t *p;

	ess = (eap_sim_state_t *)handler->opaque;
	rad_assert(handler->request != NULL);
	rad_assert(handler->request->reply);

	/*
	 *	Invps is the data from the client but this is for non-protocol data here.
	 *	We should already have consumed any client originated data.
	 */
	invps = &handler->request->packet->vps;

	/*
	 *	Outvps is the data to the client
	 */
	packet = handler->request->reply;
	outvps = &packet->vps;

	if (RDEBUG_ENABLED2) {
		RDEBUG2("EAP-SIM decoded packet:");
		debug_pair_list(*invps);
	}

	/*
	 *	Okay, we got the challenges! Put them into an attribute.
	 */
	newvp = paircreate(packet, PW_EAP_SIM_RAND, 0);
	newvp->length = 2 + (EAPSIM_RAND_SIZE * 3);
	newvp->vp_octets = p = talloc_array(newvp, uint8_t, newvp->length);

	memset(p, 0, 2); /* clear reserved bytes */
	p += 2;
	memcpy(p, ess->keys.rand[0], EAPSIM_RAND_SIZE);
	p += EAPSIM_RAND_SIZE;
	memcpy(p, ess->keys.rand[1], EAPSIM_RAND_SIZE);
	p += EAPSIM_RAND_SIZE;
	memcpy(p, ess->keys.rand[2], EAPSIM_RAND_SIZE);
	pairadd(outvps, newvp);

	/*
	 *	Set the EAP_ID - new value
	 */
	newvp = paircreate(packet, PW_EAP_ID, 0);
	newvp->vp_integer = ess->sim_id++;
	pairreplace(outvps, newvp);

	/*
	 *	Make a copy of the identity
	 */
	ess->keys.identitylen = strlen(handler->identity);
	memcpy(ess->keys.identity, handler->identity, ess->keys.identitylen);

	/*
	 *	Use the SIM identity, if available
	 */
	newvp = pairfind(*invps, PW_EAP_SIM_IDENTITY, 0, TAG_ANY);
	if (newvp && newvp->length > 2) {
		uint16_t len;

		memcpy(&len, newvp->vp_octets, sizeof(uint16_t));
		len = ntohs(len);
		if (len <= newvp->length - 2 && len <= MAX_STRING_LEN) {
			ess->keys.identitylen = len;
			memcpy(ess->keys.identity, newvp->vp_octets + 2, ess->keys.identitylen);
		}
	}

	/*
	 *	All set, calculate keys!
	 */
	eapsim_calculate_keys(&ess->keys);

#ifdef EAP_SIM_DEBUG_PRF
	eapsim_dump_mk(&ess->keys);
#endif

	/*
	 *	Need to include an AT_MAC attribute so that it will get
	 *	calculated. The NONCE_MT and the MAC are both 16 bytes, so
	 *	We store the NONCE_MT in the MAC for the encoder, which
	 *	will pull it out before it does the operation.
	 */
	newvp = paircreate(packet, PW_EAP_SIM_MAC, 0);
	pairmemcpy(newvp, ess->keys.nonce_mt, 16);
	pairreplace(outvps, newvp);

	newvp = paircreate(packet, PW_EAP_SIM_KEY, 0);
	pairmemcpy(newvp, ess->keys.K_aut, 16);
	pairreplace(outvps, newvp);

	/* the SUBTYPE, set to challenge. */
	newvp = paircreate(packet, PW_EAP_SIM_SUBTYPE, 0);
	newvp->vp_integer = EAPSIM_CHALLENGE;
	pairreplace(outvps, newvp);

	return 1;
}
/*
 * this code sends the challenge itself.
 *
 * Challenges will come from one of three places eventually:
 *
 * 1  from attributes like ATTRIBUTE_EAP_SIM_RANDx
 *            (these might be retrived from a database)
 *
 * 2  from internally implemented SIM authenticators
 *            (a simple one based upon XOR will be provided)
 *
 * 3  from some kind of SS7 interface.
 *
 * For now, they only come from attributes.
 * It might be that the best way to do 2/3 will be with a different
 * module to generate/calculate things.
 *
 */
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 is the data from the client.
	 * but, this is for non-protocol data here. We should
	 * already have consumed any client originated data.
	 */
	invps = &handler->request->packet->vps;

	/* outvps is the data to the client. */
	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);
	}

	/* okay, we got the challenges! Put them into an attribute */
	newvp = paircreate(ATTRIBUTE_EAP_SIM_BASE+PW_EAP_SIM_RAND,
			   0, PW_TYPE_OCTETS);
	memset(newvp->vp_strvalue,    0, 2); /* clear reserved bytes */
	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);

	/* set the EAP_ID - new value */
	newvp = paircreate(ATTRIBUTE_EAP_ID, 0, PW_TYPE_INTEGER);
	newvp->vp_integer = ess->sim_id++;
	pairreplace(outvps, newvp);

	/* make a copy of the identity */
	ess->keys.identitylen = strlen(handler->identity);
	memcpy(ess->keys.identity, handler->identity, ess->keys.identitylen);

	/* use the SIM identity, if available */
	newvp = pairfind(*invps, ATTRIBUTE_EAP_SIM_BASE + PW_EAP_SIM_IDENTITY, 0, TAG_ANY);
	if (newvp && newvp->length > 2) {
		uint16_t len;

		memcpy(&len, newvp->vp_octets, sizeof(uint16_t));
		len = ntohs(len);
		if (len <= newvp->length - 2 && len <= MAX_STRING_LEN) {
			ess->keys.identitylen = len;
			memcpy(ess->keys.identity, newvp->vp_octets + 2,
			       ess->keys.identitylen);
		}
	}

	/* all set, calculate keys! */
	eapsim_calculate_keys(&ess->keys);

#ifdef EAP_SIM_DEBUG_PRF
	eapsim_dump_mk(&ess->keys);
#endif

	/*
	 * need to include an AT_MAC attribute so that it will get
	 * calculated. The NONCE_MT and the MAC are both 16 bytes, so
	 * we store the NONCE_MT in the MAC for the encoder, which
	 * will pull it out before it does the operation.
	 */

	newvp = paircreate(ATTRIBUTE_EAP_SIM_BASE+PW_EAP_SIM_MAC,
			   0, PW_TYPE_OCTETS);
	memcpy(newvp->vp_strvalue, ess->keys.nonce_mt, 16);
	newvp->length = 16;
	pairreplace(outvps, newvp);

	newvp = paircreate(ATTRIBUTE_EAP_SIM_KEY, 0, PW_TYPE_OCTETS);
	memcpy(newvp->vp_strvalue, ess->keys.K_aut, 16);
	newvp->length = 16;
	pairreplace(outvps, newvp);

	/* the SUBTYPE, set to challenge. */
	newvp = paircreate(ATTRIBUTE_EAP_SIM_SUBTYPE, 0, PW_TYPE_INTEGER);
	newvp->vp_integer = eapsim_challenge;
	pairreplace(outvps, newvp);

	return 1;
}
static int respond_eap_md5(RADIUS_PACKET *req,
			   RADIUS_PACKET *rep)
{
	VALUE_PAIR *vp, *id, *state;
	size_t valuesize;
	uint8_t identifier;
	uint8_t const *value;
	FR_MD5_CTX	context;
	uint8_t    response[16];

	cleanresp(rep);

	if ((state = paircopy2(NULL, req->vps, PW_STATE, 0, TAG_ANY)) == NULL)
	{
		fprintf(stderr, "radeapclient: no state attribute found\n");
		return 0;
	}

	if ((id = paircopy2(NULL, req->vps, ATTRIBUTE_EAP_ID, 0, TAG_ANY)) == NULL)
	{
		fprintf(stderr, "radeapclient: no EAP-ID attribute found\n");
		return 0;
	}
	identifier = id->vp_integer;

	if ((vp = pairfind(req->vps, ATTRIBUTE_EAP_BASE+PW_EAP_MD5, 0, TAG_ANY)) == NULL)
	{
		fprintf(stderr, "radeapclient: no EAP-MD5 attribute found\n");
		return 0;
	}

	/* got the details of the MD5 challenge */
	valuesize = vp->vp_octets[0];
	value = &vp->vp_octets[1];

	/* sanitize items */
	if(valuesize > vp->length)
	{
		fprintf(stderr, "radeapclient: md5 valuesize if too big (%u > %u)\n",
			(unsigned int) valuesize, (unsigned int) vp->length);
		return 0;
	}

	/* now do the CHAP operation ourself, rather than build the
	 * buffer. We could also call rad_chap_encode, but it wants
	 * a CHAP-Challenge, which we don't want to bother with.
	 */
	fr_md5_init(&context);
	fr_md5_update(&context, &identifier, 1);
	fr_md5_update(&context, (uint8_t *) password, strlen(password));
	fr_md5_update(&context, value, valuesize);
	fr_md5_final(response, &context);

	{
		uint8_t *p;
		uint8_t lg_response;

		vp = paircreate(rep, ATTRIBUTE_EAP_BASE+PW_EAP_MD5, 0);
		vp->length = 17;

		p = talloc_zero_array(vp, uint8_t, 17);
		lg_response = 16;
		memcpy(p, &lg_response, 1);
		memcpy(p + 1, response, 16);
		pairmemsteal(vp, p);
	}
	pairreplace(&(rep->vps), vp);

	pairreplace(&(rep->vps), id);

	/* copy the state object in */
	pairreplace(&(rep->vps), state);

	return 1;
}
/*
 * this code runs the EAP-SIM client state machine.
 * the *request* is from the server.
 * the *reponse* is to the server.
 *
 */
static int respond_eap_sim(RADIUS_PACKET *req,
			   RADIUS_PACKET *resp)
{
	enum eapsim_clientstates state, newstate;
	enum eapsim_subtype subtype;
	VALUE_PAIR *vp, *statevp, *radstate, *eapid;
	char statenamebuf[32], subtypenamebuf[32];

	if ((radstate = paircopy2(NULL, req->vps, PW_STATE, 0, TAG_ANY)) == NULL)
	{
		return 0;
	}

	if ((eapid = paircopy2(NULL, req->vps, ATTRIBUTE_EAP_ID, 0, TAG_ANY)) == NULL)
	{
		return 0;
	}

	/* first, dig up the state from the request packet, setting
	 * outselves to be in EAP-SIM-Start state if there is none.
	 */

	if((statevp = pairfind(resp->vps, ATTRIBUTE_EAP_SIM_STATE, 0, TAG_ANY)) == NULL)
	{
		/* must be initial request */
		statevp = paircreate(resp, ATTRIBUTE_EAP_SIM_STATE, 0);
		statevp->vp_integer = eapsim_client_init;
		pairreplace(&(resp->vps), statevp);
	}
	state = statevp->vp_integer;

	/*
	 * map the attributes, and authenticate them.
	 */
	unmap_eapsim_types(req);

	if((vp = pairfind(req->vps, ATTRIBUTE_EAP_SIM_SUBTYPE, 0, TAG_ANY)) == NULL)
	{
		return 0;
	}
	subtype = vp->vp_integer;

	/*
	 * look for the appropriate state, and process incoming message
	 */
	switch(state) {
	case eapsim_client_init:
		switch(subtype) {
		case eapsim_start:
			newstate = process_eap_start(req, resp);
			break;

		case eapsim_challenge:
		case eapsim_notification:
		case eapsim_reauth:
		default:
			fprintf(stderr, "radeapclient: sim in state %s message %s is illegal. Reply dropped.\n",
				sim_state2name(state, statenamebuf, sizeof(statenamebuf)),
				sim_subtype2name(subtype, subtypenamebuf, sizeof(subtypenamebuf)));
			/* invalid state, drop message */
			return 0;
		}
		break;

	case eapsim_client_start:
		switch(subtype) {
		case eapsim_start:
			/* NOT SURE ABOUT THIS ONE, retransmit, I guess */
			newstate = process_eap_start(req, resp);
			break;

		case eapsim_challenge:
			newstate = process_eap_challenge(req, resp);
			break;

		default:
			fprintf(stderr, "radeapclient: sim in state %s message %s is illegal. Reply dropped.\n",
				sim_state2name(state, statenamebuf, sizeof(statenamebuf)),
				sim_subtype2name(subtype, subtypenamebuf, sizeof(subtypenamebuf)));
			/* invalid state, drop message */
			return 0;
		}
		break;


	default:
		fprintf(stderr, "radeapclient: sim in illegal state %s\n",
			sim_state2name(state, statenamebuf, sizeof(statenamebuf)));
		return 0;
	}

	/* copy the eap state object in */
	pairreplace(&(resp->vps), eapid);

	/* update stete info, and send new packet */
	map_eapsim_types(resp);

	/* copy the radius state object in */
	pairreplace(&(resp->vps), radstate);

	statevp->vp_integer = newstate;
	return 1;
}
/*
 * we got an EAP-Request/Sim/Challenge message in a legal state.
 *
 * use the RAND challenge to produce the SRES result, and then
 * use that to generate a new MAC.
 *
 * for the moment, we ignore the RANDs, then just plug in the SRES
 * values.
 *
 */
static int process_eap_challenge(RADIUS_PACKET *req,
				 RADIUS_PACKET *rep)
{
	VALUE_PAIR *newvp;
	VALUE_PAIR *mac, *randvp;
	VALUE_PAIR *sres1,*sres2,*sres3;
	VALUE_PAIR *Kc1, *Kc2, *Kc3;
	uint8_t calcmac[20];

	/* look for the AT_MAC and the challenge data */
	mac   = pairfind(req->vps, ATTRIBUTE_EAP_SIM_BASE+PW_EAP_SIM_MAC, 0, TAG_ANY);
	randvp= pairfind(req->vps, ATTRIBUTE_EAP_SIM_BASE+PW_EAP_SIM_RAND, 0, TAG_ANY);
	if(!mac || !randvp) {
		fprintf(stderr, "radeapclient: challenge message needs to contain RAND and MAC\n");
		return 0;
	}

	/*
	 * compare RAND with randX, to verify this is the right response
	 * to this challenge.
	 */
	{
	  VALUE_PAIR *randcfgvp[3];
	  uint8_t const *randcfg[3];

	  randcfg[0] = &randvp->vp_octets[2];
	  randcfg[1] = &randvp->vp_octets[2+EAPSIM_RAND_SIZE];
	  randcfg[2] = &randvp->vp_octets[2+EAPSIM_RAND_SIZE*2];

	  randcfgvp[0] = pairfind(rep->vps, ATTRIBUTE_EAP_SIM_RAND1, 0, TAG_ANY);
	  randcfgvp[1] = pairfind(rep->vps, ATTRIBUTE_EAP_SIM_RAND2, 0, TAG_ANY);
	  randcfgvp[2] = pairfind(rep->vps, ATTRIBUTE_EAP_SIM_RAND3, 0, TAG_ANY);

	  if(!randcfgvp[0] ||
	     !randcfgvp[1] ||
	     !randcfgvp[2]) {
	    fprintf(stderr, "radeapclient: needs to have rand1, 2 and 3 set.\n");
	    return 0;
	  }

	  if(memcmp(randcfg[0], randcfgvp[0]->vp_octets, EAPSIM_RAND_SIZE)!=0 ||
	     memcmp(randcfg[1], randcfgvp[1]->vp_octets, EAPSIM_RAND_SIZE)!=0 ||
	     memcmp(randcfg[2], randcfgvp[2]->vp_octets, EAPSIM_RAND_SIZE)!=0) {
	    int rnum,i,j;

	    fprintf(stderr, "radeapclient: one of rand 1,2,3 didn't match\n");
	    for(rnum = 0; rnum < 3; rnum++) {
	      fprintf(stderr, "received   rand %d: ", rnum);
	      j=0;
	      for (i = 0; i < EAPSIM_RAND_SIZE; i++) {
		if(j==4) {
		  printf("_");
		  j=0;
		}
		j++;

		fprintf(stderr, "%02x", randcfg[rnum][i]);
	      }
	      fprintf(stderr, "\nconfigured rand %d: ", rnum);
	      j=0;
	      for (i = 0; i < EAPSIM_RAND_SIZE; i++) {
		if(j==4) {
		  printf("_");
		  j=0;
		}
		j++;

		fprintf(stderr, "%02x", randcfgvp[rnum]->vp_octets[i]);
	      }
	      fprintf(stderr, "\n");
	    }
	    return 0;
	  }
	}

	/*
	 * now dig up the sres values from the response packet,
	 * which were put there when we read things in.
	 *
	 * Really, they should be calculated from the RAND!
	 *
	 */
	sres1 = pairfind(rep->vps, ATTRIBUTE_EAP_SIM_SRES1, 0, TAG_ANY);
	sres2 = pairfind(rep->vps, ATTRIBUTE_EAP_SIM_SRES2, 0, TAG_ANY);
	sres3 = pairfind(rep->vps, ATTRIBUTE_EAP_SIM_SRES3, 0, TAG_ANY);

	if(!sres1 ||
	   !sres2 ||
	   !sres3) {
		fprintf(stderr, "radeapclient: needs to have sres1, 2 and 3 set.\n");
		return 0;
	}
	memcpy(eapsim_mk.sres[0], sres1->vp_strvalue, sizeof(eapsim_mk.sres[0]));
	memcpy(eapsim_mk.sres[1], sres2->vp_strvalue, sizeof(eapsim_mk.sres[1]));
	memcpy(eapsim_mk.sres[2], sres3->vp_strvalue, sizeof(eapsim_mk.sres[2]));

	Kc1 = pairfind(rep->vps, ATTRIBUTE_EAP_SIM_KC1, 0, TAG_ANY);
	Kc2 = pairfind(rep->vps, ATTRIBUTE_EAP_SIM_KC2, 0, TAG_ANY);
	Kc3 = pairfind(rep->vps, ATTRIBUTE_EAP_SIM_KC3, 0, TAG_ANY);

	if(!Kc1 ||
	   !Kc2 ||
	   !Kc3) {
		fprintf(stderr, "radeapclient: needs to have Kc1, 2 and 3 set.\n");
		return 0;
	}
	memcpy(eapsim_mk.Kc[0], Kc1->vp_strvalue, sizeof(eapsim_mk.Kc[0]));
	memcpy(eapsim_mk.Kc[1], Kc2->vp_strvalue, sizeof(eapsim_mk.Kc[1]));
	memcpy(eapsim_mk.Kc[2], Kc3->vp_strvalue, sizeof(eapsim_mk.Kc[2]));

	/* all set, calculate keys */
	eapsim_calculate_keys(&eapsim_mk);

	if(debug_flag) {
	  eapsim_dump_mk(&eapsim_mk);
	}

	/* verify the MAC, now that we have all the keys. */
	if(eapsim_checkmac(NULL, req->vps, eapsim_mk.K_aut,
			   eapsim_mk.nonce_mt, sizeof(eapsim_mk.nonce_mt),
			   calcmac)) {
		printf("MAC check succeed\n");
	} else {
		int i, j;
		j=0;
		printf("calculated MAC (");
		for (i = 0; i < 20; i++) {
			if(j==4) {
				printf("_");
				j=0;
			}
			j++;

			printf("%02x", calcmac[i]);
		}
		printf(" did not match\n");
		return 0;
	}

	/* form new response clear of any EAP stuff */
	cleanresp(rep);

	/* mark the subtype as being EAP-SIM/Response/Start */
	newvp = paircreate(rep, ATTRIBUTE_EAP_SIM_SUBTYPE, 0);
	newvp->vp_integer = eapsim_challenge;
	pairreplace(&(rep->vps), newvp);

	{
		uint8_t *p;
		/*
		 * fill the SIM_MAC with a field that will in fact get appended
		 * to the packet before the MAC is calculated
		 */
		newvp = paircreate(rep, ATTRIBUTE_EAP_SIM_BASE+PW_EAP_SIM_MAC, 0);

		p = talloc_zero_array(newvp, uint8_t, EAPSIM_SRES_SIZE*3);
		memcpy(p+EAPSIM_SRES_SIZE * 0, sres1->vp_strvalue, EAPSIM_SRES_SIZE);
		memcpy(p+EAPSIM_SRES_SIZE * 1, sres2->vp_strvalue, EAPSIM_SRES_SIZE);
		memcpy(p+EAPSIM_SRES_SIZE * 2, sres3->vp_strvalue, EAPSIM_SRES_SIZE);
		pairmemsteal(newvp, p);

		pairreplace(&(rep->vps), newvp);
	}

	newvp = paircreate(rep, ATTRIBUTE_EAP_SIM_KEY, 0);
	pairmemcpy(newvp, eapsim_mk.K_aut, EAPSIM_AUTH_SIZE);

	pairreplace(&(rep->vps), newvp);

	return 1;
}
/*
 * we got an EAP-Request/Sim/Start message in a legal state.
 *
 * pick a supported version, put it into the reply, and insert a nonce.
 */
static int process_eap_start(RADIUS_PACKET *req,
			     RADIUS_PACKET *rep)
{
	VALUE_PAIR *vp, *newvp;
	VALUE_PAIR *anyidreq_vp, *fullauthidreq_vp, *permanentidreq_vp;
	uint16_t const *versions;
	uint16_t selectedversion;
	unsigned int i,versioncount;

	/* form new response clear of any EAP stuff */
	cleanresp(rep);

	if((vp = pairfind(req->vps, ATTRIBUTE_EAP_SIM_BASE+PW_EAP_SIM_VERSION_LIST, 0, TAG_ANY)) == NULL) {
		fprintf(stderr, "illegal start message has no VERSION_LIST\n");
		return 0;
	}

	versions = (uint16_t const *) vp->vp_strvalue;

	/* verify that the attribute length is big enough for a length field */
	if(vp->length < 4)
	{
		fprintf(stderr, "start message has illegal VERSION_LIST. Too short: %u\n", (unsigned int) vp->length);
		return 0;
	}

	versioncount = ntohs(versions[0])/2;
	/* verify that the attribute length is big enough for the given number
	 * of versions present.
	 */
	if((unsigned)vp->length <= (versioncount*2 + 2))
	{
		fprintf(stderr, "start message is too short. Claimed %d versions does not fit in %u bytes\n", versioncount, (unsigned int) vp->length);
		return 0;
	}

	/*
	 * record the versionlist for the MK calculation.
	 */
	eapsim_mk.versionlistlen = versioncount*2;
	memcpy(eapsim_mk.versionlist, (unsigned char const *)(versions+1),
	       eapsim_mk.versionlistlen);

	/* walk the version list, and pick the one we support, which
	 * at present, is 1, EAP_SIM_VERSION.
	 */
	selectedversion=0;
	for(i=0; i < versioncount; i++)
	{
		if(ntohs(versions[i+1]) == EAP_SIM_VERSION)
		{
			selectedversion=EAP_SIM_VERSION;
			break;
		}
	}
	if(selectedversion == 0)
	{
		fprintf(stderr, "eap-sim start message. No compatible version found. We need %d\n", EAP_SIM_VERSION);
		for(i=0; i < versioncount; i++)
		{
			fprintf(stderr, "\tfound version %d\n",
				ntohs(versions[i+1]));
		}
	}

	/*
	 * now make sure that we have only FULLAUTH_ID_REQ.
	 * I think that it actually might not matter - we can answer in
	 * anyway we like, but it is illegal to have more than one
	 * present.
	 */
	anyidreq_vp = pairfind(req->vps, ATTRIBUTE_EAP_SIM_BASE+PW_EAP_SIM_ANY_ID_REQ, 0, TAG_ANY);
	fullauthidreq_vp = pairfind(req->vps, ATTRIBUTE_EAP_SIM_BASE+PW_EAP_SIM_FULLAUTH_ID_REQ, 0, TAG_ANY);
	permanentidreq_vp = pairfind(req->vps, ATTRIBUTE_EAP_SIM_BASE+PW_EAP_SIM_PERMANENT_ID_REQ, 0, TAG_ANY);

	if(!fullauthidreq_vp ||
	   anyidreq_vp != NULL ||
	   permanentidreq_vp != NULL) {
		fprintf(stderr, "start message has %sanyidreq, %sfullauthid and %spermanentid. Illegal combination.\n",
			(anyidreq_vp != NULL ? "a " : "no "),
			(fullauthidreq_vp != NULL ? "a " : "no "),
			(permanentidreq_vp != NULL ? "a " : "no "));
		return 0;
	}

	/* okay, we have just any_id_req there, so fill in response */

	/* mark the subtype as being EAP-SIM/Response/Start */
	newvp = paircreate(rep, ATTRIBUTE_EAP_SIM_SUBTYPE, 0);
	newvp->vp_integer = eapsim_start;
	pairreplace(&(rep->vps), newvp);

	/* insert selected version into response. */
	{
		uint16_t no_versions;

		no_versions = htons(selectedversion);

		newvp = paircreate(rep, ATTRIBUTE_EAP_SIM_BASE + PW_EAP_SIM_SELECTED_VERSION, 0);
		pairmemcpy(newvp, (uint8_t *) &no_versions, 2);
		pairreplace(&(rep->vps), newvp);

		/* record the selected version */
		memcpy(eapsim_mk.versionselect, &no_versions, 2);
	}

	vp = newvp = NULL;

	{
		uint32_t nonce[4];
		uint8_t *p;
		/*
		 * insert a nonce_mt that we make up.
		 */
		nonce[0]=fr_rand();
		nonce[1]=fr_rand();
		nonce[2]=fr_rand();
		nonce[3]=fr_rand();

		newvp = paircreate(rep, ATTRIBUTE_EAP_SIM_BASE+PW_EAP_SIM_NONCE_MT, 0);

		p = talloc_zero_array(newvp, uint8_t, 18); /* 18 = 16 bytes of nonce + padding */
		memcpy(&p[2], nonce, 16);
		pairmemsteal(newvp, p);

		pairreplace(&(rep->vps), newvp);

		/* also keep a copy of the nonce! */
		memcpy(eapsim_mk.nonce_mt, nonce, 16);
	}

	{
		uint16_t idlen;
		uint8_t *p;
		uint16_t no_idlen;

		/*
		 * insert the identity here.
		 */
		vp = pairfind(rep->vps, PW_USER_NAME, 0, TAG_ANY);
		if(!vp)
		{
			fprintf(stderr, "eap-sim: We need to have a User-Name attribute!\n");
			return 0;
		}
		newvp = paircreate(rep, ATTRIBUTE_EAP_SIM_BASE+PW_EAP_SIM_IDENTITY, 0);

		idlen = strlen(vp->vp_strvalue);
		p = talloc_zero_array(newvp, uint8_t, idlen + 2);
		no_idlen = htons(idlen);
		memcpy(p, &no_idlen, 2);
		memcpy(p + 2, vp->vp_strvalue, idlen);
		pairmemsteal(newvp, p);

		pairreplace(&(rep->vps), newvp);

		/* record it */
		memcpy(eapsim_mk.identity, vp->vp_strvalue, idlen);
		eapsim_mk.identitylen = idlen;
	}

	return 1;
}
Exemple #11
0
static int 
respond_eap_md5(RADIUS_PACKET *req,
			   RADIUS_PACKET *rep,const char* pwd)
{
	VALUE_PAIR *vp, *id, *state;
	size_t valuesize;
	uint8_t identifier;
	uint8_t *value;
	FR_MD5_CTX	context;
	uint8_t    response[16];

	cleanresp(rep);

	if ((state = paircopy2(req->vps, PW_STATE)) == NULL)
	{
		fr_strerror_printf("radeapclient: no state attribute found");
		return 0;
	}

	if ((id = paircopy2(req->vps, ATTRIBUTE_EAP_ID)) == NULL)
	{
		fr_strerror_printf("radeapclient: no EAP-ID attribute found");
		return 0;
	}
	identifier = id->vp_integer;

	if ((vp = pairfind(req->vps, ATTRIBUTE_EAP_BASE+PW_EAP_MD5)) == NULL)
	{
		fr_strerror_printf("radeapclient: no EAP-MD5 attribute found");
		return 0;
	}

	/* got the details of the MD5 challenge */
	valuesize = vp->vp_octets[0];
	value = &vp->vp_octets[1];

	/* sanitize items */
	if(valuesize > vp->length)
	{
		fr_strerror_printf("radeapclient: md5 valuesize if too big (%u > %u)\n",
			(unsigned int) valuesize, (unsigned int) vp->length);
		return 0;
	}

	/* now do the CHAP operation ourself, rather than build the
	 * buffer. We could also call rad_chap_encode, but it wants
	 * a CHAP-Challenge, which we don't want to bother with.
	 */
	fr_MD5Init(&context);
	fr_MD5Update(&context, &identifier, 1);
	fr_MD5Update(&context, (uint8_t *) pwd, strlen(pwd));
	fr_MD5Update(&context, value, valuesize);
	fr_MD5Final(response, &context);

	vp = paircreate(ATTRIBUTE_EAP_BASE+PW_EAP_MD5, PW_TYPE_OCTETS);
	vp->vp_octets[0]=16;
	memcpy(&vp->vp_strvalue[1], response, 16);
	vp->length = 17;

	pairreplace(&(rep->vps), vp);

	pairreplace(&(rep->vps), id);

	/* copy the state object in */
	pairreplace(&(rep->vps), state);

	return 1;
}
Exemple #12
0
static int respond_eap_md5(RADIUS_PACKET *req,
			   RADIUS_PACKET *rep)
{
	VALUE_PAIR *vp, *id, *state;
	int valuesize, namesize;
	unsigned char identifier;
	unsigned char *value;
	unsigned char *name;
	MD5_CTX	context;
	char    response[16];

	cleanresp(rep);

	if ((state = paircopy2(req->vps, PW_STATE)) == NULL)
	{
		fprintf(stderr, "radeapclient: no state attribute found\n");
		return 0;
	}

	if ((id = paircopy2(req->vps, ATTRIBUTE_EAP_ID)) == NULL)
	{
		fprintf(stderr, "radeapclient: no EAP-ID attribute found\n");
		return 0;
	}
	identifier = id->lvalue;

	if ((vp = pairfind(req->vps, ATTRIBUTE_EAP_BASE+PW_EAP_MD5)) == NULL)
	{
		fprintf(stderr, "radeapclient: no EAP-MD5 attribute found\n");
		return 0;
	}

	/* got the details of the MD5 challenge */
	valuesize = vp->strvalue[0];
	value = &vp->strvalue[1];
	name  = &vp->strvalue[valuesize+1];
	namesize = vp->length - (valuesize + 1);

	/* sanitize items */
	if(valuesize > vp->length)
	{
		fprintf(stderr, "radeapclient: md5 valuesize if too big (%d > %d)\n",
			valuesize, vp->length);
		return 0;
	}

	/* now do the CHAP operation ourself, rather than build the
	 * buffer. We could also call rad_chap_encode, but it wants
	 * a CHAP-Challenge, which we don't want to bother with.
	 */
	librad_MD5Init(&context);
	librad_MD5Update(&context, &identifier, 1);
	librad_MD5Update(&context, password, strlen(password));
	librad_MD5Update(&context, value, valuesize);
	librad_MD5Final(response, &context);

	vp = paircreate(ATTRIBUTE_EAP_BASE+PW_EAP_MD5, PW_TYPE_OCTETS);
	vp->strvalue[0]=16;
	memcpy(&vp->strvalue[1], response, 16);
	vp->length = 17;

	pairreplace(&(rep->vps), vp);

	pairreplace(&(rep->vps), id);

	/* copy the state object in */
	pairreplace(&(rep->vps), state);

	return 1;
}