Cleaned up EAP-SIM en/decoding, eliminated unaligned half-word reads

This commit is contained in:
Martin Willi
2009-10-05 13:32:41 +02:00
parent 587ebae722
commit 3b836fc759
+107 -98
View File
@@ -116,6 +116,8 @@ ENUM_END(sim_attribute_names, AT_RESULT_IND);
typedef struct private_eap_sim_t private_eap_sim_t; typedef struct private_eap_sim_t private_eap_sim_t;
typedef struct eap_sim_header_t eap_sim_header_t;
typedef struct sim_attribute_header_t sim_attribute_header_t;
/** /**
* Private data of an eap_sim_t object. * Private data of an eap_sim_t object.
@@ -198,6 +200,35 @@ struct private_eap_sim_t {
chunk_t emsk; chunk_t emsk;
}; };
/**
* packed eap SIM header struct
*/
struct eap_sim_header_t {
/** EAP code (REQUEST/RESPONSE) */
u_int8_t code;
/** unique message identifier */
u_int8_t identifier;
/** length of whole message */
u_int16_t length;
/** EAP type => EAP_SIM */
u_int8_t type;
/** SIM subtype */
u_int8_t subtype;
/** reserved bytes */
u_int16_t reserved;
} __attribute__((__packed__));
/**
* packed eap SIM attribute header struct
*/
struct sim_attribute_header_t {
/** attribute type */
u_int8_t type;
/** attibute length */
u_int8_t length;
} __attribute__((__packed__));
/** length of the AT_NONCE_MT nonce value */ /** length of the AT_NONCE_MT nonce value */
#define NONCE_LEN 16 #define NONCE_LEN 16
/** length of the AT_MAC value */ /** length of the AT_MAC value */
@@ -255,9 +286,9 @@ static sim_attribute_t read_attribute(chunk_t *message, chunk_t *data)
{ {
return AT_END; return AT_END;
} }
attribute = *message->ptr++; attribute = message->ptr[0];
length = *message->ptr++ * 4 - 2; length = message->ptr[1] * 4 - 2;
message->len -= 2; *message = chunk_skip(*message, 2);
DBG3(DBG_IKE, "found attribute %N with length %d", DBG3(DBG_IKE, "found attribute %N with length %d",
sim_attribute_names, attribute, length); sim_attribute_names, attribute, length);
@@ -278,44 +309,44 @@ static sim_attribute_t read_attribute(chunk_t *message, chunk_t *data)
static eap_payload_t *build_payload(private_eap_sim_t *this, u_int8_t identifier, static eap_payload_t *build_payload(private_eap_sim_t *this, u_int8_t identifier,
sim_subtype_t type, ...) sim_subtype_t type, ...)
{ {
chunk_t message = chunk_alloca(512); chunk_t pos, data, message, mac_data = chunk_empty;
chunk_t pos = message;
eap_payload_t *payload; eap_payload_t *payload;
va_list args; va_list args;
sim_attribute_t attr;
u_int8_t *mac_pos = NULL; u_int8_t *mac_pos = NULL;
chunk_t mac_data = chunk_empty; u_int16_t len;
eap_sim_header_t *hdr;
sim_attribute_t attr;
sim_attribute_header_t *ahdr;
/* write EAP header, skip length bytes */ pos = message = chunk_alloca(512);
*pos.ptr++ = this->type;
*pos.ptr++ = identifier; hdr = (eap_sim_header_t*)message.ptr;
pos.ptr += 2; hdr->code = this->type;
pos.len -= 4; hdr->identifier = identifier;
/* write SIM header with type and subtype, zero reserved bytes */ hdr->length = 0;
*pos.ptr++ = EAP_SIM; hdr->type = EAP_SIM;
*pos.ptr++ = type; hdr->subtype = type;
*pos.ptr++ = 0; hdr->reserved = 0;
*pos.ptr++ = 0;
pos.len -= 4; pos = chunk_skip(pos, sizeof(eap_sim_header_t));
va_start(args, type); va_start(args, type);
while ((attr = va_arg(args, sim_attribute_t)) != AT_END) while ((attr = va_arg(args, sim_attribute_t)) != AT_END)
{ {
chunk_t data = va_arg(args, chunk_t); data = va_arg(args, chunk_t);
DBG3(DBG_IKE, "building %N %B", sim_attribute_names, attr, &data); DBG3(DBG_IKE, "building %N %B", sim_attribute_names, attr, &data);
/* write attribute header */ ahdr = (sim_attribute_header_t*)pos.ptr;
*pos.ptr++ = attr; ahdr->type = attr;
pos.len--; pos = chunk_skip(pos, sizeof(sim_attribute_header_t));
switch (attr) switch (attr)
{ {
case AT_CLIENT_ERROR_CODE: case AT_CLIENT_ERROR_CODE:
case AT_SELECTED_VERSION: case AT_SELECTED_VERSION:
{ {
*pos.ptr = data.len/4 + 1; ahdr->length = data.len / 4 + 1;
pos = chunk_skip(pos, 1);
memcpy(pos.ptr, data.ptr, data.len); memcpy(pos.ptr, data.ptr, data.len);
pos = chunk_skip(pos, data.len); pos = chunk_skip(pos, data.len);
break; break;
@@ -323,8 +354,8 @@ static eap_payload_t *build_payload(private_eap_sim_t *this, u_int8_t identifier
case AT_IDENTITY: case AT_IDENTITY:
case AT_VERSION_LIST: case AT_VERSION_LIST:
{ {
u_int16_t act_len = data.len; len = data.len;
/* align up to four byte */ /* align up to four bytes */
if (data.len % 4) if (data.len % 4)
{ {
chunk_t tmp = chunk_alloca((data.len/4)*4 + 4); chunk_t tmp = chunk_alloca((data.len/4)*4 + 4);
@@ -332,19 +363,19 @@ static eap_payload_t *build_payload(private_eap_sim_t *this, u_int8_t identifier
memcpy(tmp.ptr, data.ptr, data.len); memcpy(tmp.ptr, data.ptr, data.len);
data = tmp; data = tmp;
} }
*pos.ptr = data.len/4 + 1; ahdr->length = data.len / 4 + 1;
pos = chunk_skip(pos, 1);
/* actual length in bytes */ /* actual length in bytes */
*(u_int16_t*)pos.ptr = htons(act_len); len = htons(len);
pos = chunk_skip(pos, sizeof(u_int16_t)); memcpy(pos.ptr, &len, sizeof(len));
pos = chunk_skip(pos, sizeof(len));
memcpy(pos.ptr, data.ptr, data.len); memcpy(pos.ptr, data.ptr, data.len);
pos = chunk_skip(pos, data.len); pos = chunk_skip(pos, data.len);
break; break;
} }
case AT_RAND:
case AT_NONCE_MT: case AT_NONCE_MT:
{ {
*pos.ptr = data.len/4 + 1; ahdr->length = data.len / 4 + 1;
pos = chunk_skip(pos, 1);
memset(pos.ptr, 0, 2); memset(pos.ptr, 0, 2);
pos = chunk_skip(pos, 2); pos = chunk_skip(pos, 2);
memcpy(pos.ptr, data.ptr, data.len); memcpy(pos.ptr, data.ptr, data.len);
@@ -353,24 +384,15 @@ static eap_payload_t *build_payload(private_eap_sim_t *this, u_int8_t identifier
} }
case AT_MAC: case AT_MAC:
{ {
*pos.ptr++ = 5; pos.len--; ahdr->length = 5;
*pos.ptr++ = 0; pos.len--; memset(pos.ptr, 0, 2);
*pos.ptr++ = 0; pos.len--; pos = chunk_skip(pos, 2);
mac_pos = pos.ptr; mac_pos = pos.ptr;
memset(mac_pos, 0, MAC_LEN); memset(mac_pos, 0, MAC_LEN);
pos = chunk_skip(pos, MAC_LEN); pos = chunk_skip(pos, MAC_LEN);
mac_data = data; mac_data = data;
break; break;
} }
case AT_RAND:
{
*pos.ptr++ = data.len/4 + 1; pos.len--;
*pos.ptr++ = 0; pos.len--;
*pos.ptr++ = 0; pos.len--;
memcpy(pos.ptr, data.ptr, data.len);
pos = chunk_skip(pos, data.len);
break;
}
default: default:
DBG1(DBG_IKE, "no rule to build EAP_SIM attribute %N, skipped", DBG1(DBG_IKE, "no rule to build EAP_SIM attribute %N, skipped",
sim_attribute_names, attr); sim_attribute_names, attr);
@@ -381,7 +403,8 @@ static eap_payload_t *build_payload(private_eap_sim_t *this, u_int8_t identifier
/* calculate message length, write into header */ /* calculate message length, write into header */
message.len = pos.ptr - message.ptr; message.len = pos.ptr - message.ptr;
*(u_int16_t*)(message.ptr + 2) = htons(message.len); len = htons(message.len);
memcpy(&hdr->length, &len, sizeof(len));
/* create MAC if AT_MAC attribte was included. Append supplied va_arg /* create MAC if AT_MAC attribte was included. Append supplied va_arg
* chunk mac_data to "to-sign" chunk */ * chunk mac_data to "to-sign" chunk */
@@ -400,6 +423,35 @@ static eap_payload_t *build_payload(private_eap_sim_t *this, u_int8_t identifier
return payload; return payload;
} }
/**
* Process the AT_NOTIFICATION attribute
*/
static bool process_notification(private_eap_sim_t *this, chunk_t data,
eap_payload_t *in, eap_payload_t **out)
{
u_int16_t code = 0;
if (data.len == 2)
{
memcpy(&code, data.ptr, 2);
code = ntohs(code);
}
if (code > 32767)
{ /* success bit set */
DBG1(DBG_IKE, "received %N code %d",
sim_attribute_names, AT_NOTIFICATION, code);
return FALSE;
}
DBG1(DBG_IKE, "received %N error %d",
sim_attribute_names, AT_NOTIFICATION, code);
*out = build_payload(this,
in->get_identifier(in), SIM_CLIENT_ERROR,
AT_CLIENT_ERROR_CODE, client_error_general,
AT_END);
return TRUE;
}
/** /**
* process an EAP-SIM/Request/Start message * process an EAP-SIM/Request/Start message
*/ */
@@ -409,6 +461,7 @@ static status_t peer_process_start(private_eap_sim_t *this, eap_payload_t *in,
chunk_t message, data; chunk_t message, data;
sim_attribute_t attribute, include_id = AT_END; sim_attribute_t attribute, include_id = AT_END;
u_int8_t identifier; u_int8_t identifier;
u_int16_t len;
identifier = in->get_identifier(in); identifier = in->get_identifier(in);
message = in->get_data(in); message = in->get_data(in);
@@ -425,10 +478,12 @@ static status_t peer_process_start(private_eap_sim_t *this, eap_payload_t *in,
if (data.len > 2) if (data.len > 2)
{ {
/* read actual length first */ /* read actual length first */
data.len = min(data.len, ntohs(*(u_int16_t*)data.ptr) + 2); memcpy(&len, data.ptr, 2);
data.len = min(data.len, ntohs(len) + 2);
data = chunk_skip(data, 2); data = chunk_skip(data, 2);
chunk_free(&this->version_list); chunk_free(&this->version_list);
this->version_list = chunk_clone(data); this->version_list = chunk_clone(data);
while (data.len >= version.len) while (data.len >= version.len)
{ {
if (memeq(data.ptr, version.ptr, version.len)) if (memeq(data.ptr, version.ptr, version.len))
@@ -458,26 +513,10 @@ static status_t peer_process_start(private_eap_sim_t *this, eap_payload_t *in,
break; break;
case AT_NOTIFICATION: case AT_NOTIFICATION:
{ {
u_int16_t code = 0; if (process_notification(this, data, in, out))
if (data.len == 2)
{ {
code = ntohs(*(u_int16_t*)data.ptr);
}
if (code <= 32767) /* no success bit */
{
DBG1(DBG_IKE, "received %N error %d",
sim_attribute_names, attribute, code);
*out = build_payload(this,
in->get_identifier(in), SIM_CLIENT_ERROR,
AT_CLIENT_ERROR_CODE, client_error_general,
AT_END);
return NEED_MORE; return NEED_MORE;
} }
else
{
DBG1(DBG_IKE, "received %N code %d",
sim_attribute_names, attribute, code);
}
break; break;
} }
default: default:
@@ -606,26 +645,10 @@ static status_t peer_process_challenge(private_eap_sim_t *this,
} }
case AT_NOTIFICATION: case AT_NOTIFICATION:
{ {
u_int16_t code = 0; if (process_notification(this, data, in, out))
if (data.len == 2)
{ {
code = ntohs(*(u_int16_t*)data.ptr);
}
if (code <= 32767) /* no success bit */
{
DBG1(DBG_IKE, "received %N error %d",
sim_attribute_names, attribute, code);
*out = build_payload(this,
in->get_identifier(in), SIM_CLIENT_ERROR,
AT_CLIENT_ERROR_CODE, client_error_general,
AT_END);
return NEED_MORE; return NEED_MORE;
} }
else
{
DBG1(DBG_IKE, "received %N code %d",
sim_attribute_names, attribute, code);
}
break; break;
} }
default: default:
@@ -860,26 +883,10 @@ static status_t peer_process_notification(private_eap_sim_t *this,
{ {
case AT_NOTIFICATION: case AT_NOTIFICATION:
{ {
u_int16_t code = 0; if (process_notification(this, data, in, out))
if (data.len == 2)
{ {
code = ntohs(*(u_int16_t*)data.ptr);
}
if (code <= 32767) /* no success bit */
{
DBG1(DBG_IKE, "received %N error %d",
sim_attribute_names, attribute, code);
*out = build_payload(this,
in->get_identifier(in), SIM_CLIENT_ERROR,
AT_CLIENT_ERROR_CODE, client_error_general,
AT_END);
return NEED_MORE; return NEED_MORE;
} }
else
{
DBG1(DBG_IKE, "received %N code %d",
sim_attribute_names, attribute, code);
}
break; break;
} }
default: default:
@@ -910,9 +917,11 @@ static status_t server_process_client_error(private_eap_sim_t *this,
if (attribute == AT_CLIENT_ERROR_CODE) if (attribute == AT_CLIENT_ERROR_CODE)
{ {
u_int16_t code = 0; u_int16_t code = 0;
if (data.len == 2) if (data.len == 2)
{ {
code = ntohs(*(u_int16_t*)data.ptr); memcpy(&code, data.ptr, 2);
code = ntohs(code);
} }
DBG1(DBG_IKE, "received %N error %d", DBG1(DBG_IKE, "received %N error %d",
sim_attribute_names, attribute, code); sim_attribute_names, attribute, code);