#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"
static const char *eap_types[] = {
"",
"identity",
"notification",
"nak",
"md5",
"otp",
"gtc",
"7",
"8",
"9",
"10",
"11",
"12",
"tls",
"14",
"15",
"16",
"leap",
"sim",
"19",
"20",
"ttls",
"22",
"23",
"24",
"peap",
"mschapv2",
"27",
"28",
"cisco_mschapv2",
"30",
"31",
"32",
"33",
"34",
"35",
"36",
"37",
"tnc",
"39",
"40",
"41",
"42",
"fast",
"44",
"45",
"pax",
"psk",
"sake",
"ikev2"
};
int eaptype_name2type(const char *name)
{
int i;
for (i = 0; i <= PW_EAP_MAX_TYPES; i++) {
if (strcmp(name, eap_types[i]) == 0) {
return i;
}
}
return -1;
}
const char *eaptype_type2name(unsigned int type, char *buffer, size_t buflen)
{
DICT_VALUE *dval;
if (type > PW_EAP_MAX_TYPES) {
dval = dict_valbyattr(PW_EAP_TYPE, type);
if (dval) {
snprintf(buffer, buflen, "%s", dval->name);
}
snprintf(buffer, buflen, "%d", type);
return buffer;
} else if ((*eap_types[type] >= '0') && (*eap_types[type] <= '9')) {
dval = dict_valbyattr(PW_EAP_TYPE, type);
if (dval) {
snprintf(buffer, buflen, "%s", dval->name);
return buffer;
}
}
return eap_types[type];
}
int eap_wireformat(EAP_PACKET *reply)
{
eap_packet_t *hdr;
uint16_t total_length = 0;
if (reply == NULL) return EAP_INVALID;
if(reply->packet != NULL) return EAP_VALID;
total_length = EAP_HEADER_LEN;
if (reply->code < 3) {
total_length += 1;
if (reply->type.data && reply->type.length > 0) {
total_length += reply->type.length;
}
}
reply->packet = (unsigned char *)malloc(total_length);
hdr = (eap_packet_t *)reply->packet;
if (!hdr) {
radlog(L_ERR, "rlm_eap: out of memory");
return EAP_INVALID;
}
hdr->code = (reply->code & 0xFF);
hdr->id = (reply->id & 0xFF);
total_length = htons(total_length);
memcpy(hdr->length, &total_length, sizeof(total_length));
if ((reply->code == PW_EAP_REQUEST) ||
(reply->code == PW_EAP_RESPONSE)) {
hdr->data[0] = (reply->type.type & 0xFF);
if (reply->type.data && reply->type.length > 0) {
memcpy(&hdr->data[1], reply->type.data, reply->type.length);
free(reply->type.data);
reply->type.data = reply->packet + EAP_HEADER_LEN + 1;
}
}
return EAP_VALID;
}
int eap_basic_compose(RADIUS_PACKET *packet, EAP_PACKET *reply)
{
VALUE_PAIR *vp;
eap_packet_t *eap_packet;
int rcode;
if (eap_wireformat(reply) == EAP_INVALID) {
return RLM_MODULE_INVALID;
}
eap_packet = (eap_packet_t *)reply->packet;
pairdelete(&(packet->vps), PW_EAP_MESSAGE);
vp = eap_packet2vp(eap_packet);
if (!vp) return RLM_MODULE_INVALID;
pairadd(&(packet->vps), vp);
vp = pairfind(packet->vps, PW_MESSAGE_AUTHENTICATOR);
if (!vp) {
vp = paircreate(PW_MESSAGE_AUTHENTICATOR, PW_TYPE_OCTETS);
memset(vp->vp_strvalue, 0, AUTH_VECTOR_LEN);
vp->length = AUTH_VECTOR_LEN;
pairadd(&(packet->vps), vp);
}
rcode = RLM_MODULE_OK;
if (!packet->code) switch(reply->code) {
case PW_EAP_RESPONSE:
case PW_EAP_SUCCESS:
packet->code = PW_AUTHENTICATION_ACK;
rcode = RLM_MODULE_HANDLED;
break;
case PW_EAP_FAILURE:
packet->code = PW_AUTHENTICATION_REJECT;
rcode = RLM_MODULE_REJECT;
break;
case PW_EAP_REQUEST:
packet->code = PW_ACCESS_CHALLENGE;
rcode = RLM_MODULE_HANDLED;
break;
default:
radlog(L_ERR, "rlm_eap: reply code %d is unknown, Rejecting the request.", reply->code);
packet->code = PW_AUTHENTICATION_REJECT;
break;
}
return rcode;
}
VALUE_PAIR *eap_packet2vp(const eap_packet_t *packet)
{
int total, size;
const uint8_t *ptr;
VALUE_PAIR *head = NULL;
VALUE_PAIR **tail = &head;
VALUE_PAIR *vp;
total = packet->length[0] * 256 + packet->length[1];
ptr = (const uint8_t *) packet;
do {
size = total;
if (size > 253) size = 253;
vp = paircreate(PW_EAP_MESSAGE, PW_TYPE_OCTETS);
if (!vp) {
pairfree(&head);
return NULL;
}
memcpy(vp->vp_octets, ptr, size);
vp->length = size;
*tail = vp;
tail = &(vp->next);
ptr += size;
total -= size;
} while (total > 0);
return head;
}
eap_packet_t *eap_vp2packet(VALUE_PAIR *vps)
{
VALUE_PAIR *first, *vp;
eap_packet_t *eap_packet;
unsigned char *ptr;
uint16_t len;
int total_len;
first = pairfind(vps, PW_EAP_MESSAGE);
if (first == NULL) {
DEBUG("rlm_eap: EAP-Message not found");
return NULL;
}
if (first->length < 4) {
DEBUG("rlm_eap: EAP packet is too short.");
return NULL;
}
memcpy(&len, first->vp_strvalue + 2, sizeof(len));
len = ntohs(len);
if (len < 4) {
DEBUG("rlm_eap: EAP packet has invalid length.");
return NULL;
}
total_len = 0;
for (vp = first; vp; vp = pairfind(vp->next, PW_EAP_MESSAGE)) {
total_len += vp->length;
if (total_len > len) {
DEBUG("rlm_eap: Malformed EAP packet. Length in packet header does not match actual length");
return NULL;
}
}
if (total_len < len) {
DEBUG("rlm_eap: Malformed EAP packet. Length in packet header does not match actual length");
return NULL;
}
eap_packet = (eap_packet_t *) malloc(len);
if (eap_packet == NULL) {
radlog(L_ERR, "rlm_eap: out of memory");
return NULL;
}
ptr = (unsigned char *)eap_packet;
for (vp = first; vp; vp = pairfind(vp->next, PW_EAP_MESSAGE)) {
memcpy(ptr, vp->vp_strvalue, vp->length);
ptr += vp->length;
}
return eap_packet;
}