A SIM/AKA message can be parsed twice, without and with decryption

This commit is contained in:
Martin Willi
2009-11-12 10:34:00 +01:00
parent 0e20893d81
commit f81760dcd1
2 changed files with 115 additions and 61 deletions
+112 -60
View File
@@ -181,6 +181,16 @@ struct private_simaka_message_t {
* MAC value, pointing into message * MAC value, pointing into message
*/ */
chunk_t mac; chunk_t mac;
/**
* ENCR_DATA value, pointing into message
*/
chunk_t encr;
/**
* IV value, pointing into message
*/
chunk_t iv;
}; };
/** /**
@@ -255,15 +265,28 @@ static void add_attribute(private_simaka_message_t *this,
} }
/** /**
* Implementation of simaka_message_t.parse * Error handling for unencrypted attributes
*/ */
static bool parse(private_simaka_message_t *this, chunk_t sigdata) static bool not_encrypted(simaka_attribute_t type)
{ {
chunk_t in, iv = chunk_empty, encr = chunk_empty; DBG1(DBG_IKE, "received unencrypted %N", simaka_attribute_names, type);
return FALSE;
}
in = chunk_create((char*)(this->hdr + 1), /**
ntohs(this->hdr->length) - sizeof(hdr_t)); * Error handling for invalid length
*/
static bool invalid_length(simaka_attribute_t type)
{
DBG1(DBG_IKE, "invalid length of %N", simaka_attribute_names, type);
return FALSE;
}
/**
* Parse attributes from a chunk of data
*/
static bool parse_attributes(private_simaka_message_t *this, chunk_t in)
{
while (in.len) while (in.len)
{ {
attr_hdr_t *hdr; attr_hdr_t *hdr;
@@ -283,7 +306,7 @@ static bool parse(private_simaka_message_t *this, chunk_t sigdata)
case AT_COUNTER_TOO_SMALL: case AT_COUNTER_TOO_SMALL:
if (!this->encrypted) if (!this->encrypted)
{ {
return FALSE; return not_encrypted(hdr->type);
} }
/* FALL */ /* FALL */
case AT_ANY_ID_REQ: case AT_ANY_ID_REQ:
@@ -292,7 +315,7 @@ static bool parse(private_simaka_message_t *this, chunk_t sigdata)
{ {
if (hdr->length != 1 || in.len < 4) if (hdr->length != 1 || in.len < 4)
{ {
return FALSE; return invalid_length(hdr->type);
} }
data = chunk_empty; data = chunk_empty;
in = chunk_skip(in, 4); in = chunk_skip(in, 4);
@@ -302,7 +325,7 @@ static bool parse(private_simaka_message_t *this, chunk_t sigdata)
case AT_COUNTER: case AT_COUNTER:
if (!this->encrypted) if (!this->encrypted)
{ {
return FALSE; return not_encrypted(hdr->type);
} }
/* FALL */ /* FALL */
case AT_CLIENT_ERROR_CODE: case AT_CLIENT_ERROR_CODE:
@@ -311,7 +334,7 @@ static bool parse(private_simaka_message_t *this, chunk_t sigdata)
{ {
if (hdr->length != 1 || in.len < 4) if (hdr->length != 1 || in.len < 4)
{ {
return FALSE; return invalid_length(hdr->type);
} }
data = chunk_create(in.ptr + 2, 2); data = chunk_create(in.ptr + 2, 2);
in = chunk_skip(in, 4); in = chunk_skip(in, 4);
@@ -322,7 +345,7 @@ static bool parse(private_simaka_message_t *this, chunk_t sigdata)
case AT_NEXT_REAUTH_ID: case AT_NEXT_REAUTH_ID:
if (!this->encrypted) if (!this->encrypted)
{ {
return FALSE; return not_encrypted(hdr->type);
} }
/* FALL */ /* FALL */
case AT_RES: case AT_RES:
@@ -333,7 +356,7 @@ static bool parse(private_simaka_message_t *this, chunk_t sigdata)
if (hdr->length < 1 || in.len < 4) if (hdr->length < 1 || in.len < 4)
{ {
return FALSE; return invalid_length(hdr->type);
} }
memcpy(&len, in.ptr + 2, 2); memcpy(&len, in.ptr + 2, 2);
len = ntohs(len); len = ntohs(len);
@@ -343,7 +366,7 @@ static bool parse(private_simaka_message_t *this, chunk_t sigdata)
} }
if (len > hdr->length * 4 || len > in.len) if (len > hdr->length * 4 || len > in.len)
{ {
return FALSE; return invalid_length(hdr->type);
} }
data = chunk_create(in.ptr + 4, len); data = chunk_create(in.ptr + 4, len);
in = chunk_skip(in, hdr->length * 4); in = chunk_skip(in, hdr->length * 4);
@@ -353,7 +376,7 @@ static bool parse(private_simaka_message_t *this, chunk_t sigdata)
case AT_NONCE_S: case AT_NONCE_S:
if (!this->encrypted) if (!this->encrypted)
{ {
return FALSE; return not_encrypted(hdr->type);
} }
/* FALL */ /* FALL */
case AT_AUTN: case AT_AUTN:
@@ -363,7 +386,7 @@ static bool parse(private_simaka_message_t *this, chunk_t sigdata)
{ {
if (hdr->length != 5 || in.len < 20) if (hdr->length != 5 || in.len < 20)
{ {
return FALSE; return invalid_length(hdr->type);
} }
data = chunk_create(in.ptr + 4, 16); data = chunk_create(in.ptr + 4, 16);
in = chunk_skip(in, 20); in = chunk_skip(in, 20);
@@ -375,7 +398,7 @@ static bool parse(private_simaka_message_t *this, chunk_t sigdata)
{ {
if (hdr->length * 4 > in.len || in.len < 4) if (hdr->length * 4 > in.len || in.len < 4)
{ {
return FALSE; return invalid_length(hdr->type);
} }
data = chunk_create(in.ptr + 4, hdr->length * 4 - 4); data = chunk_create(in.ptr + 4, hdr->length * 4 - 4);
in = chunk_skip(in, hdr->length * 4); in = chunk_skip(in, hdr->length * 4);
@@ -386,7 +409,7 @@ static bool parse(private_simaka_message_t *this, chunk_t sigdata)
{ {
if (hdr->length != 4 || in.len < 16) if (hdr->length != 4 || in.len < 16)
{ {
return FALSE; return invalid_length(hdr->type);
} }
data = chunk_create(in.ptr + 2, 14); data = chunk_create(in.ptr + 2, 14);
in = chunk_skip(in, 16); in = chunk_skip(in, 16);
@@ -398,7 +421,7 @@ static bool parse(private_simaka_message_t *this, chunk_t sigdata)
{ {
if (hdr->length * 4 > in.len || in.len < 4) if (hdr->length * 4 > in.len || in.len < 4)
{ {
return FALSE; return invalid_length(hdr->type);
} }
data = chunk_create(in.ptr + 2, hdr->length * 4 - 2); data = chunk_create(in.ptr + 2, hdr->length * 4 - 2);
in = chunk_skip(in, hdr->length * 4); in = chunk_skip(in, hdr->length * 4);
@@ -413,10 +436,10 @@ static bool parse(private_simaka_message_t *this, chunk_t sigdata)
this->mac = data; this->mac = data;
break; break;
case AT_IV: case AT_IV:
iv = data; this->iv = data;
break; break;
case AT_ENCR_DATA: case AT_ENCR_DATA:
encr = data; this->encr = data;
break; break;
case AT_PADDING: case AT_PADDING:
break; break;
@@ -426,7 +449,8 @@ static bool parse(private_simaka_message_t *this, chunk_t sigdata)
this->p_bit = !!(data.ptr[0] & 0x40); this->p_bit = !!(data.ptr[0] & 0x40);
} }
else if (!this->encrypted) else if (!this->encrypted)
{ /* found a P bit notify in an unencrypted message */ {
DBG1(DBG_IKE, "found P-bit 0 notify in unencrypted message");
return FALSE; return FALSE;
} }
/* FALL */ /* FALL */
@@ -435,44 +459,59 @@ static bool parse(private_simaka_message_t *this, chunk_t sigdata)
break; break;
} }
} }
/* decrypt, invoke parser recursively */
if (iv.len && encr.len)
{
bool success;
crypter_t *crypter;
if (this->encrypted)
{
DBG1(DBG_IKE, "%N message is recursively encrypted",
eap_type_names, this->hdr->type);
return FALSE;
}
crypter = this->crypto->get_crypter(this->crypto);
if (!crypter)
{
DBG1(DBG_IKE, "%N message contains unexpected encrypted data",
eap_type_names, this->hdr->type);
return FALSE;
}
if (encr.len % crypter->get_block_size(crypter))
{
DBG1(DBG_IKE, "%N ENCR_DATA not a multiple of block size",
eap_type_names, this->hdr->type);
return FALSE;
}
/* decrypt inline */
crypter->decrypt(crypter, encr, iv, NULL);
this->encrypted = TRUE;
success = parse(this, chunk_empty);
this->encrypted = FALSE;
return success;
}
return TRUE; return TRUE;
} }
/**
* Decrypt a message and parse the decrypted attributes
*/
static bool decrypt(private_simaka_message_t *this)
{
bool success;
crypter_t *crypter;
crypter = this->crypto->get_crypter(this->crypto);
if (!crypter || !this->iv.len || !this->encr.len || this->encrypted)
{
return TRUE;
}
if (this->encr.len % crypter->get_block_size(crypter))
{
DBG1(DBG_IKE, "%N ENCR_DATA not a multiple of block size",
eap_type_names, this->hdr->type);
return FALSE;
}
/* decrypt inline */
crypter->decrypt(crypter, this->encr, this->iv, NULL);
this->encrypted = TRUE;
success = parse_attributes(this, this->encr);
this->encrypted = FALSE;
return success;
}
/**
* Implementation of simaka_message_t.parse
*/
static bool parse(private_simaka_message_t *this)
{
chunk_t in;
if (this->attributes->get_count(this->attributes))
{ /* Already parsed. Try to decrypt and parse AT_ENCR_DATA. */
return decrypt(this);
}
in = chunk_create((char*)this->hdr, ntohs(this->hdr->length));
if (!parse_attributes(this, chunk_skip(in, sizeof(hdr_t))))
{
return FALSE;
}
/* try to decrypt if we already have keys */
return decrypt(this);
}
/** /**
* Implementation of simaka_message_t.verify * Implementation of simaka_message_t.verify
*/ */
@@ -690,14 +729,25 @@ static eap_payload_t* generate(private_simaka_message_t *this, chunk_t sigdata)
if (encr.len < sizeof(encr_buf)) if (encr.len < sizeof(encr_buf))
{ {
chunk_t iv; chunk_t iv;
size_t bs; size_t bs, padding;
crypter_t *crypter; crypter_t *crypter;
rng_t *rng; rng_t *rng;
crypter = this->crypto->get_crypter(this->crypto); crypter = this->crypto->get_crypter(this->crypto);
encr = chunk_create(encr_buf, sizeof(encr_buf) - encr.len);
bs = crypter->get_block_size(crypter); bs = crypter->get_block_size(crypter);
/* add AT_PADDING attribute */
padding = bs - ((sizeof(encr_buf) - encr.len) % bs);
if (padding)
{
hdr = (attr_hdr_t*)encr.ptr;
hdr->type = AT_PADDING;
hdr->length = padding / 4;
memset(encr.ptr + 2, 0, padding - 2);
encr = chunk_skip(encr, padding);
}
encr = chunk_create(encr_buf, sizeof(encr_buf) - encr.len);
/* add IV attribute */ /* add IV attribute */
hdr = (attr_hdr_t*)out.ptr; hdr = (attr_hdr_t*)out.ptr;
hdr->type = AT_IV; hdr->type = AT_IV;
@@ -807,9 +857,9 @@ static simaka_message_t *simaka_message_create_data(chunk_t data,
this->public.get_subtype = (simaka_subtype_t(*)(simaka_message_t*))get_subtype; this->public.get_subtype = (simaka_subtype_t(*)(simaka_message_t*))get_subtype;
this->public.create_attribute_enumerator = (enumerator_t*(*)(simaka_message_t*))create_attribute_enumerator; this->public.create_attribute_enumerator = (enumerator_t*(*)(simaka_message_t*))create_attribute_enumerator;
this->public.add_attribute = (void(*)(simaka_message_t*, simaka_attribute_t type, chunk_t data))add_attribute; this->public.add_attribute = (void(*)(simaka_message_t*, simaka_attribute_t type, chunk_t data))add_attribute;
this->public.parse = (bool(*)(simaka_message_t*, simaka_crypto_t* crypto))parse; this->public.parse = (bool(*)(simaka_message_t*))parse;
this->public.verify = (bool(*)(simaka_message_t*, simaka_crypto_t* crypto, chunk_t sigdata))verify; this->public.verify = (bool(*)(simaka_message_t*, chunk_t sigdata))verify;
this->public.generate = (eap_payload_t*(*)(simaka_message_t*, simaka_crypto_t* crypto, chunk_t sigdata))generate; this->public.generate = (eap_payload_t*(*)(simaka_message_t*, chunk_t sigdata))generate;
this->public.destroy = (void(*)(simaka_message_t*))destroy; this->public.destroy = (void(*)(simaka_message_t*))destroy;
this->attributes = linked_list_create(); this->attributes = linked_list_create();
@@ -817,6 +867,8 @@ static simaka_message_t *simaka_message_create_data(chunk_t data,
this->crypto = crypto; this->crypto = crypto;
this->p_bit = TRUE; this->p_bit = TRUE;
this->mac = chunk_empty; this->mac = chunk_empty;
this->encr = chunk_empty;
this->iv = chunk_empty;
this->hdr = malloc(data.len); this->hdr = malloc(data.len);
memcpy(this->hdr, hdr, data.len); memcpy(this->hdr, hdr, data.len);
+3 -1
View File
@@ -214,7 +214,9 @@ struct simaka_message_t {
* Parse a message, with optional attribute decryption. * Parse a message, with optional attribute decryption.
* *
* This method does not verify message integrity, as the key is available * This method does not verify message integrity, as the key is available
* only after the payload has been parsed. * only after the payload has been parsed. It might be necessary to call
* parse twice, as key derivation data in EAP-SIM/AKA is in the same
* packet as encrypted data.
* *
* @param crypto EAP-SIM/AKA crypto helper * @param crypto EAP-SIM/AKA crypto helper
* @return TRUE if message parsed successfully * @return TRUE if message parsed successfully