2009-10-19 13:37:36 +00:00
|
|
|
/*
|
|
|
|
* Copyright (C) 2009 Martin Willi
|
|
|
|
* Hochschule fuer Technik Rapperswil
|
|
|
|
*
|
|
|
|
* This program is free software; you can redistribute it and/or modify it
|
|
|
|
* under the terms of the GNU General Public License as published by the
|
|
|
|
* Free Software Foundation; either version 2 of the License, or (at your
|
|
|
|
* option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
|
|
|
|
*
|
|
|
|
* This program is distributed in the hope that it will be useful, but
|
|
|
|
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
|
|
|
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
|
|
|
* for more details.
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include "simaka_message.h"
|
|
|
|
|
2011-07-07 10:31:45 +00:00
|
|
|
#include "simaka_manager.h"
|
|
|
|
|
|
|
|
#include <debug.h>
|
|
|
|
#include <utils/linked_list.h>
|
|
|
|
|
2009-10-19 13:37:36 +00:00
|
|
|
typedef struct private_simaka_message_t private_simaka_message_t;
|
|
|
|
typedef struct hdr_t hdr_t;
|
|
|
|
typedef struct attr_hdr_t attr_hdr_t;
|
|
|
|
typedef struct attr_t attr_t;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* packed EAP-SIM/AKA header struct
|
|
|
|
*/
|
|
|
|
struct hdr_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/EAP_AKA */
|
|
|
|
u_int8_t type;
|
|
|
|
/** SIM subtype */
|
|
|
|
u_int8_t subtype;
|
|
|
|
/** reserved bytes */
|
|
|
|
u_int16_t reserved;
|
|
|
|
} __attribute__((__packed__));
|
|
|
|
|
|
|
|
/**
|
|
|
|
* packed EAP-SIM/AKA attribute header struct
|
|
|
|
*/
|
|
|
|
struct attr_hdr_t {
|
|
|
|
/** attribute type */
|
|
|
|
u_int8_t type;
|
|
|
|
/** attibute length */
|
|
|
|
u_int8_t length;
|
|
|
|
} __attribute__((__packed__));
|
|
|
|
|
|
|
|
/**
|
|
|
|
* SIM/AKA attribute, parsed
|
|
|
|
*/
|
|
|
|
struct attr_t {
|
|
|
|
/** type of attribute */
|
|
|
|
simaka_attribute_t type;
|
|
|
|
/** length of data */
|
|
|
|
size_t len;
|
|
|
|
/** start of data, variable length */
|
|
|
|
char data[];
|
|
|
|
};
|
|
|
|
|
|
|
|
ENUM_BEGIN(simaka_subtype_names, AKA_CHALLENGE, AKA_IDENTITY,
|
|
|
|
"AKA_CHALLENGE",
|
|
|
|
"AKA_AUTHENTICATION_REJECT",
|
|
|
|
"AKA_3",
|
|
|
|
"AKA_SYNCHRONIZATION_FAILURE",
|
|
|
|
"AKA_IDENTITY");
|
|
|
|
ENUM_NEXT(simaka_subtype_names, SIM_START, AKA_CLIENT_ERROR, AKA_IDENTITY,
|
|
|
|
"SIM_START",
|
|
|
|
"SIM_CHALLENGE",
|
|
|
|
"SIM/AKA_NOTIFICATION",
|
|
|
|
"SIM/AKA_REAUTHENTICATION",
|
|
|
|
"SIM/AKA_CLIENT_ERROR");
|
|
|
|
ENUM_END(simaka_subtype_names, AKA_CLIENT_ERROR);
|
|
|
|
|
|
|
|
|
|
|
|
ENUM_BEGIN(simaka_attribute_names, AT_RAND, AT_CLIENT_ERROR_CODE,
|
|
|
|
"AT_RAND",
|
|
|
|
"AT_AUTN",
|
|
|
|
"AT_RES",
|
|
|
|
"AT_AUTS",
|
|
|
|
"AT_5",
|
|
|
|
"AT_PADDING",
|
|
|
|
"AT_NONCE_MT",
|
|
|
|
"AT_8",
|
|
|
|
"AT_9",
|
|
|
|
"AT_PERMANENT_ID_REQ",
|
|
|
|
"AT_MAC",
|
|
|
|
"AT_NOTIFICATION",
|
|
|
|
"AT_ANY_ID_REQ",
|
|
|
|
"AT_IDENTITY",
|
|
|
|
"AT_VERSION_LIST",
|
|
|
|
"AT_SELECTED_VERSION",
|
|
|
|
"AT_FULLAUTH_ID_REQ",
|
|
|
|
"AT_18",
|
|
|
|
"AT_COUNTER",
|
|
|
|
"AT_COUNTER_TOO_SMALL",
|
|
|
|
"AT_NONCE_S",
|
|
|
|
"AT_CLIENT_ERROR_CODE");
|
|
|
|
ENUM_NEXT(simaka_attribute_names, AT_IV, AT_RESULT_IND, AT_CLIENT_ERROR_CODE,
|
|
|
|
"AT_IV",
|
|
|
|
"AT_ENCR_DATA",
|
|
|
|
"AT_131",
|
|
|
|
"AT_NEXT_PSEUDONYM",
|
|
|
|
"AT_NEXT_REAUTH_ID",
|
|
|
|
"AT_CHECKCODE",
|
|
|
|
"AT_RESULT_IND");
|
|
|
|
ENUM_END(simaka_attribute_names, AT_RESULT_IND);
|
|
|
|
|
2009-10-22 11:02:32 +00:00
|
|
|
|
|
|
|
ENUM_BEGIN(simaka_notification_names, SIM_GENERAL_FAILURE_AA, SIM_GENERAL_FAILURE_AA,
|
|
|
|
"General failure after authentication");
|
|
|
|
ENUM_NEXT(simaka_notification_names, SIM_TEMP_DENIED, SIM_TEMP_DENIED, SIM_GENERAL_FAILURE_AA,
|
|
|
|
"User has been temporarily denied access");
|
|
|
|
ENUM_NEXT(simaka_notification_names, SIM_NOT_SUBSCRIBED, SIM_NOT_SUBSCRIBED, SIM_TEMP_DENIED,
|
|
|
|
"User has not subscribed to the requested service");
|
|
|
|
ENUM_NEXT(simaka_notification_names, SIM_GENERAL_FAILURE, SIM_GENERAL_FAILURE, SIM_NOT_SUBSCRIBED,
|
|
|
|
"General failure");
|
|
|
|
ENUM_NEXT(simaka_notification_names, SIM_SUCCESS, SIM_SUCCESS, SIM_GENERAL_FAILURE,
|
|
|
|
"User has been successfully authenticated");
|
|
|
|
ENUM_END(simaka_notification_names, SIM_SUCCESS);
|
|
|
|
|
|
|
|
|
|
|
|
ENUM(simaka_client_error_names, SIM_UNABLE_TO_PROCESS, SIM_RANDS_NOT_FRESH,
|
|
|
|
"unable to process packet",
|
|
|
|
"unsupported version",
|
|
|
|
"insufficient number of challenges",
|
|
|
|
"RANDs are not fresh",
|
|
|
|
);
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Check if an EAP-SIM/AKA attribute is skippable
|
|
|
|
*/
|
|
|
|
bool simaka_attribute_skippable(simaka_attribute_t attribute)
|
|
|
|
{
|
2011-11-25 08:40:30 +00:00
|
|
|
bool skippable = !((int)attribute >= 0 && attribute <= 127);
|
2009-10-22 11:02:32 +00:00
|
|
|
|
2011-07-07 10:31:45 +00:00
|
|
|
DBG1(DBG_LIB, "%sskippable EAP-SIM/AKA attribute %N",
|
2009-10-22 11:02:32 +00:00
|
|
|
skippable ? "ignoring " : "found non-",
|
|
|
|
simaka_attribute_names, attribute);
|
|
|
|
return skippable;
|
|
|
|
}
|
|
|
|
|
2009-10-19 13:37:36 +00:00
|
|
|
/**
|
|
|
|
* Private data of an simaka_message_t object.
|
|
|
|
*/
|
|
|
|
struct private_simaka_message_t {
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Public simaka_message_t interface.
|
|
|
|
*/
|
|
|
|
simaka_message_t public;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* EAP message, starting with EAP header
|
|
|
|
*/
|
|
|
|
hdr_t *hdr;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* List of parsed attributes, attr_t
|
|
|
|
*/
|
|
|
|
linked_list_t *attributes;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Currently parsing AT_ENCR_DATA wrapped attributes?
|
|
|
|
*/
|
|
|
|
bool encrypted;
|
|
|
|
|
|
|
|
/**
|
2009-10-23 11:57:13 +00:00
|
|
|
* crypto helper
|
|
|
|
*/
|
|
|
|
simaka_crypto_t *crypto;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Phase a NOTIFICATION is sent within
|
|
|
|
*/
|
2009-10-19 13:37:36 +00:00
|
|
|
bool p_bit;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* MAC value, pointing into message
|
|
|
|
*/
|
|
|
|
chunk_t mac;
|
2009-10-23 14:26:26 +00:00
|
|
|
|
|
|
|
/**
|
|
|
|
* ENCR_DATA value, pointing into message
|
|
|
|
*/
|
|
|
|
chunk_t encr;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* IV value, pointing into message
|
|
|
|
*/
|
|
|
|
chunk_t iv;
|
2009-10-19 13:37:36 +00:00
|
|
|
};
|
|
|
|
|
2011-07-07 10:42:15 +00:00
|
|
|
METHOD(simaka_message_t, is_request, bool,
|
|
|
|
private_simaka_message_t *this)
|
2009-10-19 13:37:36 +00:00
|
|
|
{
|
|
|
|
return this->hdr->code == EAP_REQUEST;
|
|
|
|
}
|
|
|
|
|
2011-07-07 10:42:15 +00:00
|
|
|
METHOD(simaka_message_t, get_identifier, u_int8_t,
|
|
|
|
private_simaka_message_t *this)
|
2009-10-19 13:37:36 +00:00
|
|
|
{
|
|
|
|
return this->hdr->identifier;
|
|
|
|
}
|
|
|
|
|
2011-07-07 10:42:15 +00:00
|
|
|
METHOD(simaka_message_t, get_subtype, simaka_subtype_t,
|
|
|
|
private_simaka_message_t *this)
|
2009-10-19 13:37:36 +00:00
|
|
|
{
|
|
|
|
return this->hdr->subtype;
|
|
|
|
}
|
|
|
|
|
2011-07-07 10:42:15 +00:00
|
|
|
METHOD(simaka_message_t, get_type, eap_type_t,
|
|
|
|
private_simaka_message_t *this)
|
2009-10-19 13:37:36 +00:00
|
|
|
{
|
|
|
|
return this->hdr->type;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* convert attr_t to type and data enumeration
|
|
|
|
*/
|
|
|
|
static bool attr_enum_filter(void *null, attr_t **in, simaka_attribute_t *type,
|
|
|
|
void *dummy, chunk_t *data)
|
|
|
|
{
|
|
|
|
attr_t *attr = *in;
|
|
|
|
|
|
|
|
*type = attr->type;
|
|
|
|
*data = chunk_create(attr->data, attr->len);
|
|
|
|
return TRUE;
|
|
|
|
}
|
|
|
|
|
2011-07-07 10:42:15 +00:00
|
|
|
METHOD(simaka_message_t, create_attribute_enumerator, enumerator_t*,
|
|
|
|
private_simaka_message_t *this)
|
2009-10-19 13:37:36 +00:00
|
|
|
{
|
|
|
|
return enumerator_create_filter(
|
|
|
|
this->attributes->create_enumerator(this->attributes),
|
|
|
|
(void*)attr_enum_filter, NULL, NULL);
|
|
|
|
}
|
|
|
|
|
2011-07-07 10:42:15 +00:00
|
|
|
METHOD(simaka_message_t, add_attribute, void,
|
|
|
|
private_simaka_message_t *this, simaka_attribute_t type, chunk_t data)
|
2009-10-19 13:37:36 +00:00
|
|
|
{
|
|
|
|
attr_t *attr;
|
|
|
|
|
2009-11-27 10:14:40 +00:00
|
|
|
attr = malloc(sizeof(attr_t) + data.len);
|
|
|
|
attr->len = data.len;
|
|
|
|
attr->type = type;
|
|
|
|
memcpy(attr->data, data.ptr, data.len);
|
2009-10-19 13:37:36 +00:00
|
|
|
|
2009-11-27 10:14:40 +00:00
|
|
|
this->attributes->insert_last(this->attributes, attr);
|
2009-10-19 13:37:36 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2009-10-23 14:26:26 +00:00
|
|
|
* Error handling for unencrypted attributes
|
2009-10-19 13:37:36 +00:00
|
|
|
*/
|
2009-10-23 14:26:26 +00:00
|
|
|
static bool not_encrypted(simaka_attribute_t type)
|
2009-10-19 13:37:36 +00:00
|
|
|
{
|
2011-07-07 10:31:45 +00:00
|
|
|
DBG1(DBG_LIB, "received unencrypted %N", simaka_attribute_names, type);
|
2009-10-23 14:26:26 +00:00
|
|
|
return FALSE;
|
|
|
|
}
|
2009-10-19 13:37:36 +00:00
|
|
|
|
2009-10-23 14:26:26 +00:00
|
|
|
/**
|
|
|
|
* Error handling for invalid length
|
|
|
|
*/
|
|
|
|
static bool invalid_length(simaka_attribute_t type)
|
|
|
|
{
|
2011-07-07 10:31:45 +00:00
|
|
|
DBG1(DBG_LIB, "invalid length of %N", simaka_attribute_names, type);
|
2009-10-23 14:26:26 +00:00
|
|
|
return FALSE;
|
|
|
|
}
|
2009-10-19 13:37:36 +00:00
|
|
|
|
2011-07-07 10:31:45 +00:00
|
|
|
/**
|
|
|
|
* Call SIM/AKA message hooks
|
|
|
|
*/
|
|
|
|
static void call_hook(private_simaka_message_t *this,
|
|
|
|
bool inbound, bool decrypted)
|
|
|
|
{
|
|
|
|
simaka_manager_t *mgr;
|
|
|
|
|
|
|
|
switch (this->hdr->type)
|
|
|
|
{
|
|
|
|
case EAP_SIM:
|
|
|
|
mgr = lib->get(lib, "sim-manager");
|
|
|
|
break;
|
|
|
|
case EAP_AKA:
|
|
|
|
mgr = lib->get(lib, "aka-manager");
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
mgr->message_hook(mgr, &this->public, inbound, decrypted);
|
|
|
|
}
|
|
|
|
|
2009-10-23 14:26:26 +00:00
|
|
|
/**
|
|
|
|
* Parse attributes from a chunk of data
|
|
|
|
*/
|
|
|
|
static bool parse_attributes(private_simaka_message_t *this, chunk_t in)
|
|
|
|
{
|
2009-10-19 13:37:36 +00:00
|
|
|
while (in.len)
|
|
|
|
{
|
|
|
|
attr_hdr_t *hdr;
|
|
|
|
chunk_t data;
|
|
|
|
|
|
|
|
if (in.len < sizeof(attr_hdr_t))
|
|
|
|
{
|
2011-07-07 10:31:45 +00:00
|
|
|
DBG1(DBG_LIB, "found short %N attribute header",
|
2009-10-19 13:37:36 +00:00
|
|
|
eap_type_names, this->hdr->type);
|
|
|
|
return FALSE;
|
|
|
|
}
|
|
|
|
hdr = (attr_hdr_t*)in.ptr;
|
|
|
|
|
|
|
|
switch (hdr->type)
|
|
|
|
{
|
|
|
|
/* attributes without data */
|
|
|
|
case AT_COUNTER_TOO_SMALL:
|
|
|
|
if (!this->encrypted)
|
|
|
|
{
|
2009-10-23 14:26:26 +00:00
|
|
|
return not_encrypted(hdr->type);
|
2009-10-19 13:37:36 +00:00
|
|
|
}
|
|
|
|
/* FALL */
|
|
|
|
case AT_ANY_ID_REQ:
|
|
|
|
case AT_PERMANENT_ID_REQ:
|
|
|
|
case AT_FULLAUTH_ID_REQ:
|
|
|
|
{
|
|
|
|
if (hdr->length != 1 || in.len < 4)
|
|
|
|
{
|
2009-10-23 14:26:26 +00:00
|
|
|
return invalid_length(hdr->type);
|
2009-10-19 13:37:36 +00:00
|
|
|
}
|
|
|
|
data = chunk_empty;
|
|
|
|
in = chunk_skip(in, 4);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
/* attributes with two bytes data */
|
|
|
|
case AT_COUNTER:
|
|
|
|
if (!this->encrypted)
|
|
|
|
{
|
2009-10-23 14:26:26 +00:00
|
|
|
return not_encrypted(hdr->type);
|
2009-10-19 13:37:36 +00:00
|
|
|
}
|
|
|
|
/* FALL */
|
|
|
|
case AT_CLIENT_ERROR_CODE:
|
|
|
|
case AT_SELECTED_VERSION:
|
|
|
|
case AT_NOTIFICATION:
|
|
|
|
{
|
|
|
|
if (hdr->length != 1 || in.len < 4)
|
|
|
|
{
|
2009-10-23 14:26:26 +00:00
|
|
|
return invalid_length(hdr->type);
|
2009-10-19 13:37:36 +00:00
|
|
|
}
|
|
|
|
data = chunk_create(in.ptr + 2, 2);
|
|
|
|
in = chunk_skip(in, 4);
|
|
|
|
break;
|
|
|
|
}
|
2009-10-22 11:02:32 +00:00
|
|
|
/* attributes with an additional actual-length in bits or bytes */
|
2009-10-19 13:37:36 +00:00
|
|
|
case AT_NEXT_PSEUDONYM:
|
|
|
|
case AT_NEXT_REAUTH_ID:
|
|
|
|
if (!this->encrypted)
|
|
|
|
{
|
2009-10-23 14:26:26 +00:00
|
|
|
return not_encrypted(hdr->type);
|
2009-10-19 13:37:36 +00:00
|
|
|
}
|
|
|
|
/* FALL */
|
2009-10-22 11:02:32 +00:00
|
|
|
case AT_RES:
|
2009-10-19 13:37:36 +00:00
|
|
|
case AT_IDENTITY:
|
|
|
|
case AT_VERSION_LIST:
|
|
|
|
{
|
|
|
|
u_int16_t len;
|
|
|
|
|
|
|
|
if (hdr->length < 1 || in.len < 4)
|
|
|
|
{
|
2009-10-23 14:26:26 +00:00
|
|
|
return invalid_length(hdr->type);
|
2009-10-19 13:37:36 +00:00
|
|
|
}
|
|
|
|
memcpy(&len, in.ptr + 2, 2);
|
|
|
|
len = ntohs(len);
|
2009-10-22 11:02:32 +00:00
|
|
|
if (hdr->type == AT_RES)
|
|
|
|
{ /* AT_RES uses length encoding in bits */
|
|
|
|
len /= 8;
|
|
|
|
}
|
2009-10-19 13:37:36 +00:00
|
|
|
if (len > hdr->length * 4 || len > in.len)
|
|
|
|
{
|
2009-10-23 14:26:26 +00:00
|
|
|
return invalid_length(hdr->type);
|
2009-10-19 13:37:36 +00:00
|
|
|
}
|
|
|
|
data = chunk_create(in.ptr + 4, len);
|
|
|
|
in = chunk_skip(in, hdr->length * 4);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
/* attributes with two reserved bytes, 16 bytes length */
|
|
|
|
case AT_NONCE_S:
|
|
|
|
if (!this->encrypted)
|
|
|
|
{
|
2009-10-23 14:26:26 +00:00
|
|
|
return not_encrypted(hdr->type);
|
2009-10-19 13:37:36 +00:00
|
|
|
}
|
|
|
|
/* FALL */
|
|
|
|
case AT_AUTN:
|
|
|
|
case AT_NONCE_MT:
|
|
|
|
case AT_IV:
|
|
|
|
case AT_MAC:
|
|
|
|
{
|
|
|
|
if (hdr->length != 5 || in.len < 20)
|
|
|
|
{
|
2009-10-23 14:26:26 +00:00
|
|
|
return invalid_length(hdr->type);
|
2009-10-19 13:37:36 +00:00
|
|
|
}
|
|
|
|
data = chunk_create(in.ptr + 4, 16);
|
|
|
|
in = chunk_skip(in, 20);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
/* attributes with two reserved bytes, variable length */
|
|
|
|
case AT_ENCR_DATA:
|
|
|
|
case AT_RAND:
|
|
|
|
{
|
|
|
|
if (hdr->length * 4 > in.len || in.len < 4)
|
|
|
|
{
|
2009-10-23 14:26:26 +00:00
|
|
|
return invalid_length(hdr->type);
|
2009-10-19 13:37:36 +00:00
|
|
|
}
|
|
|
|
data = chunk_create(in.ptr + 4, hdr->length * 4 - 4);
|
|
|
|
in = chunk_skip(in, hdr->length * 4);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
/* attributes with no reserved bytes, 14 bytes length */
|
|
|
|
case AT_AUTS:
|
|
|
|
{
|
|
|
|
if (hdr->length != 4 || in.len < 16)
|
|
|
|
{
|
2009-10-23 14:26:26 +00:00
|
|
|
return invalid_length(hdr->type);
|
2009-10-19 13:37:36 +00:00
|
|
|
}
|
|
|
|
data = chunk_create(in.ptr + 2, 14);
|
|
|
|
in = chunk_skip(in, 16);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
/* other attributes (with 4n + 2 length) */
|
|
|
|
case AT_PADDING:
|
|
|
|
default:
|
|
|
|
{
|
|
|
|
if (hdr->length * 4 > in.len || in.len < 4)
|
|
|
|
{
|
2009-10-23 14:26:26 +00:00
|
|
|
return invalid_length(hdr->type);
|
2009-10-19 13:37:36 +00:00
|
|
|
}
|
|
|
|
data = chunk_create(in.ptr + 2, hdr->length * 4 - 2);
|
|
|
|
in = chunk_skip(in, hdr->length * 4);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* handle special attributes */
|
|
|
|
switch (hdr->type)
|
|
|
|
{
|
|
|
|
case AT_MAC:
|
|
|
|
this->mac = data;
|
|
|
|
break;
|
|
|
|
case AT_IV:
|
2009-10-23 14:26:26 +00:00
|
|
|
this->iv = data;
|
2009-10-19 13:37:36 +00:00
|
|
|
break;
|
|
|
|
case AT_ENCR_DATA:
|
2009-10-23 14:26:26 +00:00
|
|
|
this->encr = data;
|
2009-10-19 13:37:36 +00:00
|
|
|
break;
|
|
|
|
case AT_PADDING:
|
|
|
|
break;
|
|
|
|
case AT_NOTIFICATION:
|
|
|
|
if (this->p_bit)
|
|
|
|
{ /* remember P bit for MAC verification */
|
|
|
|
this->p_bit = !!(data.ptr[0] & 0x40);
|
|
|
|
}
|
|
|
|
else if (!this->encrypted)
|
2009-10-23 14:26:26 +00:00
|
|
|
{
|
2011-07-07 10:31:45 +00:00
|
|
|
DBG1(DBG_LIB, "found P-bit 0 notify in unencrypted message");
|
2009-10-19 13:37:36 +00:00
|
|
|
return FALSE;
|
|
|
|
}
|
|
|
|
/* FALL */
|
|
|
|
default:
|
|
|
|
add_attribute(this, hdr->type, data);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
2009-11-27 10:14:40 +00:00
|
|
|
|
2011-07-07 10:31:45 +00:00
|
|
|
call_hook(this, TRUE, this->encrypted);
|
2009-11-27 10:14:40 +00:00
|
|
|
|
2009-10-23 14:26:26 +00:00
|
|
|
return TRUE;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Decrypt a message and parse the decrypted attributes
|
|
|
|
*/
|
|
|
|
static bool decrypt(private_simaka_message_t *this)
|
|
|
|
{
|
|
|
|
bool success;
|
|
|
|
crypter_t *crypter;
|
2009-10-28 13:15:24 +00:00
|
|
|
chunk_t plain;
|
2009-10-19 13:37:36 +00:00
|
|
|
|
2009-10-23 14:26:26 +00:00
|
|
|
crypter = this->crypto->get_crypter(this->crypto);
|
|
|
|
if (!crypter || !this->iv.len || !this->encr.len || this->encrypted)
|
2009-10-19 13:37:36 +00:00
|
|
|
{
|
2009-10-23 14:26:26 +00:00
|
|
|
return TRUE;
|
|
|
|
}
|
|
|
|
if (this->encr.len % crypter->get_block_size(crypter))
|
|
|
|
{
|
2011-07-07 10:31:45 +00:00
|
|
|
DBG1(DBG_LIB, "%N ENCR_DATA not a multiple of block size",
|
2009-10-23 14:26:26 +00:00
|
|
|
eap_type_names, this->hdr->type);
|
|
|
|
return FALSE;
|
|
|
|
}
|
2009-10-19 13:37:36 +00:00
|
|
|
|
2009-10-28 13:15:24 +00:00
|
|
|
crypter->decrypt(crypter, this->encr, this->iv, &plain);
|
2009-10-19 13:37:36 +00:00
|
|
|
|
2009-10-23 14:26:26 +00:00
|
|
|
this->encrypted = TRUE;
|
2009-10-28 13:15:24 +00:00
|
|
|
success = parse_attributes(this, plain);
|
2009-10-23 14:26:26 +00:00
|
|
|
this->encrypted = FALSE;
|
2009-10-28 13:15:24 +00:00
|
|
|
free(plain.ptr);
|
2009-10-23 14:26:26 +00:00
|
|
|
return success;
|
|
|
|
}
|
2009-10-19 13:37:36 +00:00
|
|
|
|
2011-07-07 10:42:15 +00:00
|
|
|
METHOD(simaka_message_t, parse, bool,
|
|
|
|
private_simaka_message_t *this)
|
2009-10-23 14:26:26 +00:00
|
|
|
{
|
|
|
|
chunk_t in;
|
|
|
|
|
|
|
|
if (this->attributes->get_count(this->attributes))
|
|
|
|
{ /* Already parsed. Try to decrypt and parse AT_ENCR_DATA. */
|
|
|
|
return decrypt(this);
|
2009-10-19 13:37:36 +00:00
|
|
|
}
|
2009-10-23 14:26:26 +00:00
|
|
|
|
|
|
|
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);
|
2009-10-19 13:37:36 +00:00
|
|
|
}
|
|
|
|
|
2011-07-07 10:42:15 +00:00
|
|
|
METHOD(simaka_message_t, verify, bool,
|
|
|
|
private_simaka_message_t *this, chunk_t sigdata)
|
2009-10-19 13:37:36 +00:00
|
|
|
{
|
|
|
|
chunk_t data, backup;
|
2009-10-21 12:21:00 +00:00
|
|
|
signer_t *signer;
|
|
|
|
|
2009-10-23 11:57:13 +00:00
|
|
|
signer = this->crypto->get_signer(this->crypto);
|
2009-10-19 13:37:36 +00:00
|
|
|
|
|
|
|
switch (this->hdr->subtype)
|
|
|
|
{
|
|
|
|
case SIM_START:
|
|
|
|
case SIM_CLIENT_ERROR:
|
|
|
|
/* AKA_CLIENT_ERROR: */
|
|
|
|
case AKA_AUTHENTICATION_REJECT:
|
|
|
|
case AKA_SYNCHRONIZATION_FAILURE:
|
|
|
|
case AKA_IDENTITY:
|
2009-10-22 11:02:32 +00:00
|
|
|
/* skip MAC if available */
|
2009-10-19 13:37:36 +00:00
|
|
|
return TRUE;
|
|
|
|
case SIM_CHALLENGE:
|
2009-10-22 11:02:32 +00:00
|
|
|
case AKA_CHALLENGE:
|
2009-10-19 13:37:36 +00:00
|
|
|
case SIM_REAUTHENTICATION:
|
|
|
|
/* AKA_REAUTHENTICATION: */
|
|
|
|
{
|
|
|
|
if (!this->mac.ptr || !signer)
|
|
|
|
{ /* require MAC, but not found */
|
2011-07-07 10:31:45 +00:00
|
|
|
DBG1(DBG_LIB, "%N message requires a MAC, but none found",
|
2009-10-22 11:02:32 +00:00
|
|
|
simaka_subtype_names, this->hdr->subtype);
|
2009-10-19 13:37:36 +00:00
|
|
|
return FALSE;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case SIM_NOTIFICATION:
|
|
|
|
/* AKA_NOTIFICATION: */
|
|
|
|
{
|
|
|
|
if (this->p_bit)
|
|
|
|
{ /* MAC not verified if in Phase 1 */
|
|
|
|
return TRUE;
|
|
|
|
}
|
|
|
|
if (!this->mac.ptr || !signer)
|
2009-10-22 11:02:32 +00:00
|
|
|
{
|
2011-07-07 10:31:45 +00:00
|
|
|
DBG1(DBG_LIB, "%N message has a phase 0 notify, but "
|
2009-10-22 11:02:32 +00:00
|
|
|
"no MAC found", simaka_subtype_names, this->hdr->subtype);
|
2009-10-19 13:37:36 +00:00
|
|
|
return FALSE;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
default:
|
|
|
|
/* unknown message? */
|
2011-07-07 10:31:45 +00:00
|
|
|
DBG1(DBG_LIB, "signature rule for %N messages missing",
|
2009-10-22 11:02:32 +00:00
|
|
|
simaka_subtype_names, this->hdr->subtype);
|
2009-10-19 13:37:36 +00:00
|
|
|
return FALSE;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* zero MAC for verification */
|
|
|
|
backup = chunk_clonea(this->mac);
|
|
|
|
memset(this->mac.ptr, 0, this->mac.len);
|
|
|
|
|
|
|
|
data = chunk_create((char*)this->hdr, ntohs(this->hdr->length));
|
|
|
|
if (sigdata.len)
|
|
|
|
{
|
|
|
|
data = chunk_cata("cc", data, sigdata);
|
|
|
|
}
|
|
|
|
if (!signer->verify_signature(signer, data, backup))
|
|
|
|
{
|
2011-07-07 10:31:45 +00:00
|
|
|
DBG1(DBG_LIB, "%N MAC verification failed",
|
2009-10-19 13:37:36 +00:00
|
|
|
eap_type_names, this->hdr->type);
|
|
|
|
return FALSE;
|
|
|
|
}
|
|
|
|
return TRUE;
|
|
|
|
}
|
|
|
|
|
2012-07-05 16:04:41 +00:00
|
|
|
METHOD(simaka_message_t, generate, bool,
|
|
|
|
private_simaka_message_t *this, chunk_t sigdata, chunk_t *gen)
|
2009-10-19 13:37:36 +00:00
|
|
|
{
|
|
|
|
/* buffers large enough for messages we generate */
|
|
|
|
char out_buf[1024], encr_buf[512];
|
|
|
|
enumerator_t *enumerator;
|
|
|
|
chunk_t out, encr, data, *target, mac = chunk_empty;
|
|
|
|
simaka_attribute_t type;
|
|
|
|
attr_hdr_t *hdr;
|
|
|
|
u_int16_t len;
|
2009-10-21 12:21:00 +00:00
|
|
|
signer_t *signer;
|
2009-10-19 13:37:36 +00:00
|
|
|
|
2011-07-07 10:31:45 +00:00
|
|
|
call_hook(this, FALSE, TRUE);
|
2009-11-27 10:14:40 +00:00
|
|
|
|
2009-10-19 13:37:36 +00:00
|
|
|
out = chunk_create(out_buf, sizeof(out_buf));
|
|
|
|
encr = chunk_create(encr_buf, sizeof(encr_buf));
|
|
|
|
|
|
|
|
/* copy header */
|
|
|
|
memcpy(out.ptr, this->hdr, sizeof(hdr_t));
|
|
|
|
out = chunk_skip(out, sizeof(hdr_t));
|
|
|
|
|
|
|
|
/* encode attributes */
|
|
|
|
enumerator = create_attribute_enumerator(this);
|
|
|
|
while (enumerator->enumerate(enumerator, &type, &data))
|
|
|
|
{
|
|
|
|
/* encrypt this attribute? */
|
|
|
|
switch (type)
|
|
|
|
{
|
|
|
|
case AT_NONCE_S:
|
|
|
|
case AT_NEXT_PSEUDONYM:
|
|
|
|
case AT_NEXT_REAUTH_ID:
|
|
|
|
case AT_COUNTER:
|
|
|
|
case AT_COUNTER_TOO_SMALL:
|
|
|
|
target = &encr;
|
|
|
|
break;
|
|
|
|
case AT_NOTIFICATION:
|
|
|
|
/* P bit not set, encrypt */
|
|
|
|
if (!(data.ptr[0] & 0x40))
|
|
|
|
{
|
|
|
|
target = &encr;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
/* FALL */
|
|
|
|
default:
|
|
|
|
target = &out;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
hdr = (attr_hdr_t*)target->ptr;
|
|
|
|
hdr->type = type;
|
|
|
|
|
|
|
|
/* encode type specific */
|
|
|
|
switch (type)
|
|
|
|
{
|
|
|
|
/* attributes without data */
|
|
|
|
case AT_COUNTER_TOO_SMALL:
|
|
|
|
case AT_ANY_ID_REQ:
|
|
|
|
case AT_PERMANENT_ID_REQ:
|
|
|
|
case AT_FULLAUTH_ID_REQ:
|
|
|
|
{
|
|
|
|
hdr->length = 1;
|
|
|
|
memset(target->ptr + 2, 0, 2);
|
|
|
|
*target = chunk_skip(*target, 4);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
/* attributes with two bytes data */
|
|
|
|
case AT_COUNTER:
|
|
|
|
case AT_CLIENT_ERROR_CODE:
|
|
|
|
case AT_SELECTED_VERSION:
|
|
|
|
case AT_NOTIFICATION:
|
|
|
|
{
|
|
|
|
hdr->length = 1;
|
|
|
|
memcpy(target->ptr + 2, data.ptr, 2);
|
|
|
|
*target = chunk_skip(*target, 4);
|
|
|
|
break;
|
|
|
|
}
|
2009-10-22 11:02:32 +00:00
|
|
|
/* attributes with an additional actual-length in bits or bytes */
|
2009-10-19 13:37:36 +00:00
|
|
|
case AT_NEXT_PSEUDONYM:
|
|
|
|
case AT_NEXT_REAUTH_ID:
|
|
|
|
case AT_IDENTITY:
|
|
|
|
case AT_VERSION_LIST:
|
2009-10-22 11:02:32 +00:00
|
|
|
case AT_RES:
|
2009-10-19 13:37:36 +00:00
|
|
|
{
|
|
|
|
u_int16_t len, padding;
|
|
|
|
|
|
|
|
len = htons(data.len);
|
2009-10-22 11:02:32 +00:00
|
|
|
if (type == AT_RES)
|
|
|
|
{ /* AT_RES uses length encoding in bits */
|
|
|
|
len *= 8;
|
|
|
|
}
|
2009-10-19 13:37:36 +00:00
|
|
|
memcpy(target->ptr + 2, &len, sizeof(len));
|
|
|
|
memcpy(target->ptr + 4, data.ptr, data.len);
|
|
|
|
hdr->length = data.len / 4 + 1;
|
|
|
|
padding = (4 - (data.len % 4)) % 4;
|
|
|
|
if (padding)
|
|
|
|
{
|
|
|
|
hdr->length++;
|
|
|
|
memset(target->ptr + 4 + data.len, 0, padding);
|
|
|
|
}
|
|
|
|
*target = chunk_skip(*target, hdr->length * 4);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
/* attributes with two reserved bytes, 16 bytes length */
|
|
|
|
case AT_NONCE_S:
|
|
|
|
case AT_NONCE_MT:
|
|
|
|
case AT_AUTN:
|
|
|
|
{
|
|
|
|
hdr->length = 5;
|
|
|
|
memset(target->ptr + 2, 0, 2);
|
|
|
|
memcpy(target->ptr + 4, data.ptr, data.len);
|
|
|
|
*target = chunk_skip(*target, 20);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
/* attributes with two reserved bytes, variable length */
|
|
|
|
case AT_RAND:
|
|
|
|
{
|
|
|
|
hdr->length = 1 + data.len / 4;
|
|
|
|
memset(target->ptr + 2, 0, 2);
|
|
|
|
memcpy(target->ptr + 4, data.ptr, data.len);
|
|
|
|
*target = chunk_skip(*target, data.len + 4);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
/* attributes with no reserved bytes, 14 bytes length */
|
|
|
|
case AT_AUTS:
|
|
|
|
{
|
|
|
|
hdr->length = 4;
|
|
|
|
memcpy(target->ptr + 2, data.ptr, data.len);
|
|
|
|
*target = chunk_skip(*target, 16);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
default:
|
|
|
|
{
|
2011-07-07 10:31:45 +00:00
|
|
|
DBG1(DBG_LIB, "no rule to encode %N, skipped",
|
2009-10-19 13:37:36 +00:00
|
|
|
simaka_attribute_names, type);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
enumerator->destroy(enumerator);
|
|
|
|
|
|
|
|
/* encrypt attributes, if any */
|
|
|
|
if (encr.len < sizeof(encr_buf))
|
|
|
|
{
|
|
|
|
chunk_t iv;
|
2009-10-23 14:26:26 +00:00
|
|
|
size_t bs, padding;
|
2009-10-21 12:21:00 +00:00
|
|
|
crypter_t *crypter;
|
|
|
|
rng_t *rng;
|
2009-10-19 13:37:36 +00:00
|
|
|
|
2009-10-23 11:57:13 +00:00
|
|
|
crypter = this->crypto->get_crypter(this->crypto);
|
2009-10-19 13:37:36 +00:00
|
|
|
bs = crypter->get_block_size(crypter);
|
2010-08-13 10:20:25 +00:00
|
|
|
iv.len = crypter->get_iv_size(crypter);
|
2009-10-19 13:37:36 +00:00
|
|
|
|
2009-10-23 14:26:26 +00:00
|
|
|
/* 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);
|
|
|
|
|
2009-10-19 13:37:36 +00:00
|
|
|
/* add IV attribute */
|
|
|
|
hdr = (attr_hdr_t*)out.ptr;
|
|
|
|
hdr->type = AT_IV;
|
2010-08-13 10:20:25 +00:00
|
|
|
hdr->length = iv.len / 4 + 1;
|
2009-10-19 13:37:36 +00:00
|
|
|
memset(out.ptr + 2, 0, 2);
|
|
|
|
out = chunk_skip(out, 4);
|
|
|
|
|
2009-10-23 11:57:13 +00:00
|
|
|
rng = this->crypto->get_rng(this->crypto);
|
2010-08-13 10:20:25 +00:00
|
|
|
rng->get_bytes(rng, iv.len, out.ptr);
|
2009-10-19 13:37:36 +00:00
|
|
|
|
2010-08-13 10:20:25 +00:00
|
|
|
iv = chunk_clonea(chunk_create(out.ptr, iv.len));
|
|
|
|
out = chunk_skip(out, iv.len);
|
2009-10-19 13:37:36 +00:00
|
|
|
|
|
|
|
/* inline encryption */
|
|
|
|
crypter->encrypt(crypter, encr, iv, NULL);
|
|
|
|
|
|
|
|
/* add ENCR_DATA attribute */
|
|
|
|
hdr = (attr_hdr_t*)out.ptr;
|
|
|
|
hdr->type = AT_ENCR_DATA;
|
|
|
|
hdr->length = encr.len / 4 + 1;
|
|
|
|
memset(out.ptr + 2, 0, 2);
|
|
|
|
memcpy(out.ptr + 4, encr.ptr, encr.len);
|
|
|
|
out = chunk_skip(out, encr.len + 4);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* include MAC ? */
|
2009-10-23 11:57:13 +00:00
|
|
|
signer = this->crypto->get_signer(this->crypto);
|
2009-10-19 13:37:36 +00:00
|
|
|
switch (this->hdr->subtype)
|
|
|
|
{
|
|
|
|
case SIM_CHALLENGE:
|
2009-10-22 11:02:32 +00:00
|
|
|
case AKA_CHALLENGE:
|
2009-10-19 13:37:36 +00:00
|
|
|
case SIM_REAUTHENTICATION:
|
|
|
|
/* AKA_REAUTHENTICATION: */
|
|
|
|
/* TODO: Notifications without P bit */
|
|
|
|
{
|
|
|
|
size_t bs;
|
|
|
|
|
|
|
|
bs = signer->get_block_size(signer);
|
|
|
|
hdr = (attr_hdr_t*)out.ptr;
|
|
|
|
hdr->type = AT_MAC;
|
|
|
|
hdr->length = bs / 4 + 1;
|
|
|
|
memset(out.ptr + 2, 0, 2 + bs);
|
|
|
|
mac = chunk_create(out.ptr + 4, bs);
|
|
|
|
out = chunk_skip(out, bs + 4);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* calculate message length */
|
|
|
|
out = chunk_create(out_buf, sizeof(out_buf) - out.len);
|
|
|
|
len = htons(out.len);
|
|
|
|
memcpy(out.ptr + 2, &len, sizeof(len));
|
|
|
|
|
|
|
|
/* generate MAC */
|
|
|
|
if (mac.len)
|
|
|
|
{
|
|
|
|
data = chunk_cata("cc", out, sigdata);
|
|
|
|
signer->get_signature(signer, data, mac.ptr);
|
|
|
|
}
|
2009-11-27 10:14:40 +00:00
|
|
|
|
2011-07-07 10:31:45 +00:00
|
|
|
call_hook(this, FALSE, FALSE);
|
2009-11-27 10:14:40 +00:00
|
|
|
|
2012-07-05 16:04:41 +00:00
|
|
|
*gen = chunk_clone(out);
|
|
|
|
return TRUE;
|
2009-10-19 13:37:36 +00:00
|
|
|
}
|
|
|
|
|
2011-07-07 10:42:15 +00:00
|
|
|
METHOD(simaka_message_t, destroy, void,
|
|
|
|
private_simaka_message_t *this)
|
2009-10-19 13:37:36 +00:00
|
|
|
{
|
|
|
|
this->attributes->destroy_function(this->attributes, free);
|
|
|
|
free(this->hdr);
|
|
|
|
free(this);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Generic constructor.
|
|
|
|
*/
|
2009-10-23 11:57:13 +00:00
|
|
|
static simaka_message_t *simaka_message_create_data(chunk_t data,
|
|
|
|
simaka_crypto_t *crypto)
|
2009-10-19 13:37:36 +00:00
|
|
|
{
|
|
|
|
private_simaka_message_t *this;
|
|
|
|
hdr_t *hdr = (hdr_t*)data.ptr;
|
|
|
|
|
|
|
|
if (data.len < sizeof(hdr_t) || hdr->length != htons(data.len))
|
|
|
|
{
|
2011-07-07 10:31:45 +00:00
|
|
|
DBG1(DBG_LIB, "EAP-SIM/AKA header has invalid length");
|
2009-10-19 13:37:36 +00:00
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
if (hdr->code != EAP_REQUEST && hdr->code != EAP_RESPONSE)
|
|
|
|
{
|
2011-07-07 10:31:45 +00:00
|
|
|
DBG1(DBG_LIB, "invalid EAP code in EAP-SIM/AKA message",
|
2009-10-19 13:37:36 +00:00
|
|
|
eap_type_names, hdr->type);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
if (hdr->type != EAP_SIM && hdr->type != EAP_AKA)
|
|
|
|
{
|
2011-07-07 10:31:45 +00:00
|
|
|
DBG1(DBG_LIB, "invalid EAP type in EAP-SIM/AKA message",
|
2009-10-19 13:37:36 +00:00
|
|
|
eap_type_names, hdr->type);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2011-07-07 10:42:15 +00:00
|
|
|
INIT(this,
|
|
|
|
.public = {
|
|
|
|
.is_request = _is_request,
|
|
|
|
.get_identifier = _get_identifier,
|
|
|
|
.get_type = _get_type,
|
|
|
|
.get_subtype = _get_subtype,
|
|
|
|
.create_attribute_enumerator = _create_attribute_enumerator,
|
|
|
|
.add_attribute = _add_attribute,
|
|
|
|
.parse = _parse,
|
|
|
|
.verify = _verify,
|
|
|
|
.generate = _generate,
|
|
|
|
.destroy = _destroy,
|
|
|
|
},
|
|
|
|
.attributes = linked_list_create(),
|
|
|
|
.crypto = crypto,
|
|
|
|
.p_bit = TRUE,
|
|
|
|
.hdr = malloc(data.len),
|
|
|
|
);
|
2009-10-19 13:37:36 +00:00
|
|
|
memcpy(this->hdr, hdr, data.len);
|
|
|
|
|
|
|
|
return &this->public;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* See header.
|
|
|
|
*/
|
2011-07-07 10:31:45 +00:00
|
|
|
simaka_message_t *simaka_message_create_from_payload(chunk_t data,
|
2009-10-23 11:57:13 +00:00
|
|
|
simaka_crypto_t *crypto)
|
2009-10-19 13:37:36 +00:00
|
|
|
{
|
2011-07-07 10:31:45 +00:00
|
|
|
return simaka_message_create_data(data, crypto);
|
2009-10-19 13:37:36 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* See header.
|
|
|
|
*/
|
|
|
|
simaka_message_t *simaka_message_create(bool request, u_int8_t identifier,
|
2009-10-23 11:57:13 +00:00
|
|
|
eap_type_t type, simaka_subtype_t subtype,
|
|
|
|
simaka_crypto_t *crypto)
|
2009-10-19 13:37:36 +00:00
|
|
|
{
|
|
|
|
hdr_t hdr = {
|
|
|
|
.code = request ? EAP_REQUEST : EAP_RESPONSE,
|
|
|
|
.identifier = identifier,
|
|
|
|
.length = htons(sizeof(hdr_t)),
|
|
|
|
.type = type,
|
|
|
|
.subtype = subtype,
|
|
|
|
};
|
2009-10-23 11:57:13 +00:00
|
|
|
return simaka_message_create_data(chunk_create((char*)&hdr, sizeof(hdr)),
|
|
|
|
crypto);
|
2009-10-19 13:37:36 +00:00
|
|
|
}
|
|
|
|
|