};
/*
- * Load all the required eap authentication types.
- * Get all the supported EAP-types from config file.
+ * Return an EAP-Type for a particular name.
*/
-int eaptype_load(EAP_TYPES **type_list, const char *type_name,
- CONF_SECTION *cs)
+int eaptype_name2id(const char *name)
{
- EAP_TYPES **last, *node;
- lt_dlhandle handle;
- char auth_type_name[NAME_LEN];
int i;
- snprintf(auth_type_name, sizeof(auth_type_name), "rlm_eap_%s", type_name);
-
- last = type_list;
- /* Go to the end of the EAP-Type list, if it is not already loaded */
- for (node = *type_list; node != NULL; node = node->next) {
- if (strcmp(node->typename, auth_type_name) == 0)
- return 0;
- last = &node->next;
+ for (i = 0; i <= PW_EAP_MAX_TYPES; i++) {
+ if (strcmp(name, eap_types[i]) == 0) {
+ return i;
+ }
}
+
+ return -1;
+}
+
+/*
+ * Load all the required eap authentication types.
+ * Get all the supported EAP-types from config file.
+ */
+int eaptype_load(EAP_TYPES **type, int id, CONF_SECTION *cs)
+{
+ char buffer[64];
+ lt_dlhandle handle;
+ EAP_TYPES *node;
+
+ snprintf(buffer, sizeof(buffer), "rlm_eap_%s", eap_types[id]);
/* Link the loaded EAP-Type */
- handle = lt_dlopenext(auth_type_name);
+ handle = lt_dlopenext(buffer);
if (handle == NULL) {
radlog(L_ERR, "rlm_eap: Failed to link EAP-Type/%s: %s",
- type_name, lt_dlerror());
+ eap_types[id], lt_dlerror());
return -1;
}
radlog(L_ERR, "rlm_eap: out of memory");
return -1;
}
+ memset(node, 0, sizeof(*node));
/* fill in the structure */
- node->next = NULL;
node->handle = handle;
node->cs = cs;
- node->typeid = 0;
- memset(node->typename, 0, NAME_LEN);
- node->type_stuff = NULL;
- strNcpy(node->typename, type_name, sizeof(node->typename));
- for (i = PW_EAP_MAX_TYPES; i > 0; i--) {
- if (!strcmp(type_name, eap_types[i])) {
- node->typeid = i;
- break;
- }
- }
-
- if (node->typeid == 0) {
- radlog(L_ERR, "rlm_eap: Invalid type name %s cannot be linked", type_name);
- free(node);
- return -1;
- }
+ node->typeid = id;
+ node->typename = eap_types[id];
+ node->type_data = NULL;
- node->type = (EAP_TYPE *)lt_dlsym(node->handle, auth_type_name);
+ node->type = (EAP_TYPE *)lt_dlsym(node->handle, buffer);
if (!node->type) {
radlog(L_ERR, "rlm_eap: Failed linking to %s structure in %s: %s",
- auth_type_name, type_name, lt_dlerror());
+ buffer, eap_types[id], lt_dlerror());
lt_dlclose(node->handle); /* ignore any errors */
free(node);
return -1;
}
if ((node->type->attach) &&
- ((node->type->attach)(node->cs, &(node->type_stuff)) < 0)) {
+ ((node->type->attach)(node->cs, &(node->type_data)) < 0)) {
- radlog(L_ERR, "rlm_eap: Failed to initialize the type %s", type_name);
+ radlog(L_ERR, "rlm_eap: Failed to initialize the type %s",
+ eap_types[id]);
lt_dlclose(node->handle);
free(node);
return -1;
}
- DEBUG("rlm_eap: Loaded and initialized the type %s", type_name);
- *last = node;
+ DEBUG("rlm_eap: Loaded and initialized the type %s", eap_types[id]);
+ *type = node;
return 0;
}
-/*
- * Get the handle for the requested authentication type,
- * if supported.
- */
-EAP_TYPES *eaptype_byid(EAP_TYPES **list, int type)
-{
- EAP_TYPES *node;
- for(node = *list; node != NULL; node = node->next) {
- if (node->typeid == type)
- return node;
- }
- return NULL;
-}
-
-EAP_TYPES *eaptype_byname(EAP_TYPES **list, const char *name)
-{
- EAP_TYPES *node;
- for(node = *list; node != NULL; node = node->next) {
- if (strcmp(node->typename, name) == 0)
- return node;
- }
- return NULL;
-}
/*
* Call the appropriate handle with the right eap_type.
*/
-int eaptype_call(int eap_type, operation_t action,
- EAP_TYPES *type_list, EAP_HANDLER *handler)
+static int eaptype_call(EAP_TYPES *atype, EAP_HANDLER *handler)
{
- EAP_TYPES *atype;
-
- atype = eaptype_byid(&type_list, eap_type);
- if (!atype) {
- radlog(L_ERR, "rlm_eap: Unsupported EAP_TYPE %d",
- handler->eap_ds->response->type.type);
- return 0;
- }
-
DEBUG2(" rlm_eap: processing type %s", atype->typename);
- switch (action) {
+ switch (handler->stage) {
case INITIATE:
- if (!atype->type->initiate(atype->type_stuff, handler))
+ if (!atype->type->initiate(atype->type_data, handler))
return 0;
break;
+
+ case AUTHORIZE:
+ /*
+ * The called function updates the EAP reply packet.
+ */
+ if (!atype->type->authorize ||
+ !atype->type->authorize(atype->type_data, handler))
+ return 0;
+ break;
+
case AUTHENTICATE:
/*
- * when it returns, eap_ds->reply is expected to have complete info
+ * The called function updates the EAP reply packet.
*/
- if (!atype->type->authenticate(atype->type_stuff, handler))
+ if (!atype->type->authenticate ||
+ !atype->type->authenticate(atype->type_data, handler))
return 0;
break;
+
default:
/* Should never enter here */
radlog(L_DBG, "rlm_eap: Invalid operation on eap_type");
* Default to the configured EAP-Type
* for all Unsupported EAP-Types
*/
-int eaptype_select(EAP_TYPES *type_list, EAP_HANDLER *handler, char *conftype)
+int eaptype_select(rlm_eap_t *inst, EAP_HANDLER *handler)
{
- int type = 0, i;
eaptype_t *eaptype;
- for (i = PW_EAP_MAX_TYPES; i > 0; i--) {
- if (strcmp(conftype, eap_types[i]) == 0) {
- type = i;
- break;
- }
- }
+ eaptype = &handler->eap_ds->response->type;
- if (type == 0) {
- radlog(L_ERR, "rlm_eap: Configured EAP_TYPE is not supported");
+ /*
+ * Don't trust anyone.
+ */
+ if ((eaptype->type == 0) ||
+ (eaptype->type > PW_EAP_MAX_TYPES)) {
+ return EAP_INVALID;
}
- eaptype = &handler->eap_ds->response->type;
switch(eaptype->type) {
case PW_EAP_IDENTITY:
DEBUG2(" rlm_eap: EAP Identity");
- if (eaptype_call(type, INITIATE, type_list, handler) == 0)
+ do_default_type:
+ handler->stage = INITIATE;
+ if (eaptype_call(inst->types[inst->default_eap_id],
+ handler) == 0)
return EAP_INVALID;
break;
* It is invalid to request identity, notification & nak in nak
*/
if ((eaptype->data != NULL) &&
- (eaptype->data[0] < PW_EAP_MD5)) {
+ (eaptype->data[0] < PW_EAP_MD5)) {
return EAP_INVALID;
}
/*
+ * Delete old data, if necessary.
+ */
+ if (handler->opaque && handler->free_opaque) {
+ handler->free_opaque(handler->opaque);
+ handler->free_opaque = NULL;
+ handler->opaque = NULL;
+ }
+ handler->stage = INITIATE;
+
+ /*
* The one byte of NAK data is the preferred EAP type
* of the client.
*/
case PW_EAP_MD5:
case PW_EAP_TLS:
case PW_EAP_LEAP:
+ DEBUG2(" rlm_eap: EAP_TYPE - %s",
+ eap_types[eaptype->data[0]]);
+
/*
- * eap-type specified in typdata is supported
+ * Call the type.
*/
- if (eaptype_call(eaptype->data[0],
- INITIATE, type_list, handler) == 0)
+ if (eaptype_call(inst->types[eaptype->data[0]],
+ handler) == 0)
return EAP_INVALID;
+
break;
default :
- DEBUG2(" rlm_eap: Unknown EAP type %d, reverting to default_eap_type",
- eaptype->data[0]);
+ DEBUG2(" rlm_eap: Unknown EAP type %d, reverting to default_eap_type %s",
+ eaptype->data[0], inst->default_eap_type);
+
/*
* Unsupported type, default to configured one.
* or rather reject it
*/
/* handler->eap_ds->request->code = PW_EAP_FAILURE; */
- if (eaptype_call(type, INITIATE, type_list, handler) == 0)
- return EAP_INVALID;
+ goto do_default_type;
break;
}
break;
default:
DEBUG2(" rlm_eap: EAP_TYPE - %s",
eap_types[eaptype->type]);
- if (eaptype_call(eaptype->type, AUTHENTICATE,
- type_list, handler) == 0) {
+
+ rad_assert(handler->stage == AUTHENTICATE);
+ if (eaptype_call(inst->types[eaptype->type],
+ handler) == 0)
return EAP_INVALID;
- }
break;
}
return EAP_OK;
}
/*
- * EAP packet format to be sent over the wire
- * ie code+id+length+data where data = null/type+typedata
- * based on code.
+ * EAP packet format to be sent over the wire
+ *
+ * i.e. code+id+length+data where data = null/type+typedata
+ * based on code.
*/
int eap_wireformat(EAP_PACKET *reply)
{
/*
* Here since we cannot know the typedata format and length
+ *
* Type_data is expected to be wired by each EAP-Type
- * Zero length/No typedata is supported as long as type is defined
+ *
+ * Zero length/No typedata is supported as long as
+ * type is defined
*/
if (reply->type.data && reply->type.length > 0) {
memcpy(&hdr->data[1], reply->type.data, reply->type.length);
}
/*
- * compose EAP reply packet in EAP-Message attr of RADIUS.
- * If EAP exceeds 253, frame it in multiple EAP-Message attrs.
- * Set the RADIUS reply codes based on EAP request codes
- * Append any additonal VPs to RADIUS reply
+ * compose EAP reply packet in EAP-Message attr of RADIUS. If
+ * EAP exceeds 253, frame it in multiple EAP-Message attrs.
+ *
+ * Set the RADIUS reply codes based on EAP request codes. Append
+ * any additonal VPs to RADIUS reply
*/
int eap_compose(REQUEST *request, EAP_DS *eap_ds)
{
eap_packet_t *eap_packet;
unsigned char *ptr;
EAP_PACKET *reply = eap_ds->request;
+ int rcode;
/*
- * ID serves to suppport request/response
- * retransmission in the EAP layer and as
- * such must be different for 'adjacent'
- * packets except in case of success/failure-replies.
+ * ID serves to suppport request/response
+ * retransmission in the EAP layer and as such must be
+ * different for 'adjacent' packets except in case of
+ * success/failure-replies.
*
- * RFC2716 (EAP_TLS) requires this to be
- * incremented, RFC2284 only makes the above-
- * mentioned restriction.
+ * RFC2716 (EAP_TLS) requires this to be incremented,
+ * RFC2284 only makes the above- mentioned restriction.
*/
eap_msg = pairfind(request->packet->vps, PW_EAP_MESSAGE);
rq = (EAP_PACKET *)eap_msg->strvalue;
}
if (eap_wireformat(reply) == EAP_INVALID) {
- return EAP_INVALID;
+ return RLM_MODULE_INVALID;
}
eap_packet = (eap_packet_t *)reply->packet;
pairadd(&(request->reply->vps), vp);
}
- /*
- * Generate State, only if it not Identity request
- */
- if ((eap_packet->code == PW_EAP_REQUEST) &&
- (eap_packet->data[0] >= PW_EAP_MD5)) {
- vp = generate_state();
- pairadd(&(request->reply->vps), vp);
- }
-
/* Set request reply code, but only if it's not already set. */
+ rcode = RLM_MODULE_OK;
if (!request->reply->code) switch(reply->code) {
case PW_EAP_RESPONSE:
case PW_EAP_SUCCESS:
request->reply->code = PW_AUTHENTICATION_ACK;
+ rcode = RLM_MODULE_HANDLED;
break;
case PW_EAP_FAILURE:
request->reply->code = PW_AUTHENTICATION_REJECT;
+ rcode = RLM_MODULE_REJECT;
break;
case PW_EAP_REQUEST:
request->reply->code = PW_ACCESS_CHALLENGE;
+ rcode = RLM_MODULE_HANDLED;
break;
default:
/* Should never enter here */
request->reply->code = PW_AUTHENTICATION_REJECT;
break;
}
- return 0;
+
+ return rcode;
}
/*
/*
- * Get the user Identity if at all it is available with us.
- */
-VALUE_PAIR *eap_useridentity(EAP_HANDLER *list, eap_packet_t *eap_packet, unsigned char id[])
-{
- char *un;
- VALUE_PAIR *username;
- EAP_HANDLER *handler;
-
- if ((un = eap_identity(eap_packet)) != NULL) {
- username = pairmake("User-Name", un, T_OP_EQ);
- free(un);
- return username;
- }
-
- /* Get the handler from the list, if present */
- handler = eaplist_findhandler(list, id);
- if (handler)
- return pairmake("User-Name", handler->identity, T_OP_EQ);
- return NULL;
-}
-
-
-/*
* Get the user Identity only from EAP-Identity packets
*/
char *eap_identity(eap_packet_t *eap_packet)
char *identity;
if ((eap_packet == NULL) ||
- (eap_packet->code != PW_EAP_RESPONSE) ||
- (eap_packet->data[0] != PW_EAP_IDENTITY)) {
+ (eap_packet->code != PW_EAP_RESPONSE) ||
+ (eap_packet->data[0] != PW_EAP_IDENTITY)) {
return NULL;
}
* username contains REQUEST->username which might have been stripped.
* identity contains the one sent in EAP-Identity response
*/
-EAP_HANDLER *eap_handler(EAP_HANDLER **list, eap_packet_t **eap_packet_p, REQUEST *request)
+EAP_HANDLER *eap_handler(rlm_eap_t *inst, eap_packet_t **eap_packet_p,
+ REQUEST *request)
{
EAP_HANDLER *handler = NULL;
- unsigned char *unique;
eap_packet_t *eap_packet = NULL;
eap_packet = *eap_packet_p;
* EAP_HANDLER MUST be found in the list if it is not EAP-Identity response
*/
if (eap_packet->data[0] != PW_EAP_IDENTITY) {
- unique = eap_regenerateid(request, eap_packet->id);
- if (unique == NULL) {
- return NULL;
- }
-
- handler = eaplist_isreply(list, unique);
- free(unique);
- unique = NULL;
+ handler = eaplist_find(inst, request, eap_packet->id);
if (handler == NULL) {
/* Either send EAP_Identity or EAP-Fail */
radlog(L_ERR, "rlm_eap: Either EAP-request timed out OR"
return NULL;
}
- handler->id = NULL;
- handler->prev_eapds = NULL;
- handler->eap_ds = NULL;
- handler->configured = NULL;
- handler->opaque = NULL;
- handler->free_opaque = NULL;
- handler->next = NULL;
+ /*
+ * all fields are set to zero.
+ */
handler->identity = eap_identity(eap_packet);
if (handler->identity == NULL) {
}
*/
- /*
- * Always get the configured values, for each user.
- * to pass it to the specific EAP-Type
- *
- * No Configured information found for a user, means
- * there is no such user in the database.
- *
- * Every user should have, atleast, one item configured
- * This is required for Authentication purpose.
- */
- handler->configured = paircopy(request->config_items);
- if (handler->configured == NULL) {
- DEBUG2(" rlm_eap: No configured information for this user");
-
- /*
- * FIXME: If there is no config info then
- * config_items should provide the same username
- * if the user is present in the database.
- */
- /*
- eap_handler_free(&handler);
- return NULL;
- */
- }
}
handler->eap_ds = eap_buildds(eap_packet_p);
}
handler->timestamp = time(NULL);
- handler->reply_vps = &(request->reply->vps);
handler->request = request; /* LEAP needs this */
return handler;
}
-
-
-/*
- * Regenerate the ID to match the ID stored in the list.
- * This ID is created based on the NAS, State & EAP-Response
- */
-unsigned char *eap_regenerateid(REQUEST *request, unsigned char response_id)
-{
- VALUE_PAIR *state = NULL;
- unsigned char *id = NULL;
-
- state = pairfind(request->packet->vps, PW_STATE);
- if (state == NULL) {
- DEBUG2(" rlm_eap: NO State Attribute found: Cannot match EAP packet to any existing conversation.");
- return NULL;
- }
- if (verify_state(state) != 0) {
- radlog(L_ERR, "rlm_eap: State verification failed.");
- return NULL;
- }
-
- id = (unsigned char *)malloc(1/*Length*/ + 1/*Id*/ + state->length + sizeof(request->packet->src_ipaddr));
- if (id == NULL) {
- radlog(L_ERR, "rlm_eap: out of memory");
- return NULL;
- }
-
- /*
- * Generate unique-id to check for the reply
- * id = Length + ID + State + Client IP Address
- *
- * Note that we do NOT use NAS-IP-Address, or NAS-Identifier,
- * as they may lie to us!
- */
- id[0] = (1 + 1 + state->length + sizeof(request->packet->src_ipaddr)) & 0xFF;
- memcpy(id+1, &response_id, sizeof(unsigned char));
- memcpy(id+2, state->strvalue, state->length);
- memcpy(id+2+state->length, &request->packet->src_ipaddr,
- sizeof(request->packet->src_ipaddr));
-
- return id;
-}
-
-/*
- * Generate the ID that is used as the search criteria in the list.
- * This ID is created based on the NAS, State & EAP-Request
- */
-unsigned char *eap_generateid(REQUEST *request, unsigned char response_id)
-{
- VALUE_PAIR *state = NULL;
- unsigned char *id = NULL;
-
- state = pairfind(request->reply->vps, PW_STATE);
- if (state == NULL) {
- DEBUG2(" rlm_eap: NO State Attribute found. Cannot match the EAP packet to any existing conversation.");
- return NULL;
- }
-
- id = (unsigned char *)malloc(1/*Length*/ + 1/*Id*/ + state->length + sizeof(request->packet->src_ipaddr));
- if (id == NULL) {
- radlog(L_ERR, "rlm_eap: out of memory");
- return NULL;
- }
-
- /*
- * Generate unique-id to check for the reply
- * id = Length + ID + State + Client IP Address
- *
- * Note that we do NOT use NAS-IP-Address, or NAS-Identifier,
- * as they may lie to us!
- */
- id[0] = (1 + 1 + state->length + sizeof(request->packet->src_ipaddr)) & 0xFF;
- memcpy(id+1, &response_id, sizeof(unsigned char));
- memcpy(id+2, state->strvalue, state->length);
- memcpy(id+2+state->length, &request->packet->src_ipaddr,
- sizeof(request->packet->src_ipaddr));
-
- return id;
-}
#include "radiusd.h"
#include "modules.h"
+#include "rad_assert.h"
#define PW_EAP_REQUEST 1
#define PW_EAP_RESPONSE 2
} EAP_DS;
/*
+ * Currently there are only 2 types
+ * of operations defined,
+ * apart from attach & detach for each EAP-Type.
+ */
+typedef enum operation_t {
+ INITIATE = 0,
+ AUTHORIZE,
+ AUTHENTICATE
+} operation_t;
+
+
+/*
* EAP_HANDLER is the interface for any EAP-Type.
* Each handler contains information for one specific EAP-Type.
* This way we don't need to change any interfaces in future.
* It is also a list of EAP-request handlers waiting for EAP-response
+ * eap_id = copy of the eap packet we sent to the
*
- * id = Length + Request-ID + State + (NAS-IP-Address|NAS-Identifier)
+ * next = pointer to next
+ * state = state attribute from the reply we sent
+ * state_len = length of data in the state attribute.
+ * src_ipaddr = client which sent us the RADIUS request containing
+ * this EAP conversation.
+ * eap_id = copy of EAP id we sent to the client.
+ * timestamp = timestamp when this handler was last used.
* identity = Identity, as obtained, from EAP-Identity response.
* username = as obtained in Radius request, It might differ from identity.
- * configured = List of configured values for this user.
+ * request = RADIUS request data structure
* prev_eapds = Previous EAP request, for which eap_ds contains the response.
* eap_ds = Current EAP response.
* opaque = EAP-Type holds some data that corresponds to the current
* to avoid any memory leaks in opaque
* Hence this pointer should be provided by the EAP-Type
* if opaque is not NULL
- * timestamp = timestamp when this handler is created.
* status = finished/onhold/..
*/
+#define EAP_STATE_LEN (AUTH_VECTOR_LEN)
typedef struct _eap_handler {
- unsigned char *id;
+ struct _eap_handler *next;
+
+ uint8_t state[EAP_STATE_LEN];
+ uint32_t src_ipaddr;
+ int eap_id;
- VALUE_PAIR *username;
- VALUE_PAIR *configured;
+ time_t timestamp;
+
+ VALUE_PAIR *username; /* SHOULD get rid of this! */
REQUEST *request;
- VALUE_PAIR **reply_vps;
- char *identity;
+ char *identity; /* user identity? Huh? */
- EAP_DS *prev_eapds;
- EAP_DS *eap_ds;
+ EAP_DS *prev_eapds;
+ EAP_DS *eap_ds;
- void *opaque;
- void (*free_opaque)(void **opaque);
+ void *opaque;
+ void (*free_opaque)(void *opaque);
- time_t timestamp;
- int status;
+ int status;
- struct _eap_handler *next;
+ int stage;
} EAP_HANDLER;
/*
*/
typedef struct eap_type_t {
const char *name;
- int (*attach)(CONF_SECTION *conf, void **type_arg);
- int (*initiate)(void *type_arg, EAP_HANDLER *handler);
- int (*authenticate)(void *type_arg, EAP_HANDLER *handler);
- int (*detach)(void **type_arg);
+ int (*attach)(CONF_SECTION *conf, void **type_data);
+ int (*initiate)(void *type_data, EAP_HANDLER *handler);
+ int (*authorize)(void *type_data, EAP_HANDLER *handler);
+ int (*authenticate)(void *type_data, EAP_HANDLER *handler);
+ int (*detach)(void *type_data);
} EAP_TYPE;
#endif /*_EAP_H*/
return;
handler = *handler_p;
- if (handler->id) {
- free(handler->id);
- handler->id = NULL;
- }
-
if (handler->identity) {
free(handler->identity);
handler->identity = NULL;
}
if (handler->username) pairfree(&(handler->username));
- if (handler->configured) pairfree(&(handler->configured));
if (handler->prev_eapds) eap_ds_free(&(handler->prev_eapds));
if (handler->eap_ds) eap_ds_free(&(handler->eap_ds));
- if ((handler->opaque) && (handler->free_opaque))
- handler->free_opaque(&(handler->opaque));
+ if ((handler->opaque) && (handler->free_opaque)) {
+ handler->free_opaque(handler->opaque);
+ handler->opaque = NULL;
+ }
else if ((handler->opaque) && (handler->free_opaque == NULL))
radlog(L_ERR, "Possible memory leak ...");
*handler_p = NULL;
}
-void eaptype_freelist(EAP_TYPES **i)
+void eaptype_free(EAP_TYPES *i)
{
- EAP_TYPES *c, *next;
-
- c = *i;
- while (c) {
- next = c->next;
- if(c->type->detach) (c->type->detach)(&(c->type_stuff));
- if (c->handle) lt_dlclose(c->handle);
- free(c);
- c = next;
- }
- *i = NULL;
+ if (i->type->detach) (i->type->detach)(i->type_data);
+ i->type_data = NULL;
+ if (i->handle) lt_dlclose(i->handle);
}
-void eaplist_free(EAP_HANDLER **list)
+void eaplist_free(rlm_eap_t *inst)
{
- EAP_HANDLER *node, *next;
+ int i;
- if (!list) return;
+ /*
+ * The sessions are split out into an array, which makes
+ * looking them up a bit faster.
+ */
+ for (i = 0; i < 256; i++) {
+ EAP_HANDLER *node, *next;
- node = *list;
- while (node) {
- next = node->next;
- eap_handler_free(&node);
- node = next;
- }
+ if (inst->sessions[i]) continue;
- *list = NULL;
+ node = inst->sessions[i];
+ while (node) {
+ next = node->next;
+ eap_handler_free(&node);
+ node = next;
+ }
+
+ inst->sessions[i] = NULL;
+ }
}
/*
- * TODO: For now this is a plain list.
- * It can be hashed on EAP-Type, EAP-Id
+ * Add a handler to the set of active sessions.
+ *
+ * Since we're adding it to the list, we guess that this means
+ * the packet needs a State attribute. So add one.
*/
-int eaplist_add(EAP_HANDLER **list, EAP_HANDLER *node)
+int eaplist_add(rlm_eap_t *inst, EAP_HANDLER *handler)
{
EAP_HANDLER **last;
-
- if (node == NULL) return 0;
-
- last = list;
+ VALUE_PAIR *state;
+
+ rad_assert(handler != NULL);
+ rad_assert(handler->request != NULL);
+
+ /*
+ * Generate State, since we've been asked to add it to
+ * the list.
+ */
+ state = generate_state(handler->request->timestamp);
+ pairadd(&(handler->request->reply->vps), state);
+
+ /*
+ * Create a unique 'key' for the handler, based
+ * on State, Client-IP-Address, and EAP ID.
+ */
+ rad_assert(state->length == EAP_STATE_LEN);
+
+ memcpy(handler->state, state->strvalue, sizeof(handler->state));
+ handler->src_ipaddr = handler->request->packet->src_ipaddr;
+ handler->eap_id = handler->eap_ds->request->id;
+
+ /*
+ * We key the array based on the challenge, which is
+ * a random number. This "fans out" the sessions, and
+ * helps to minimize the amount of work we've got to do
+ * under heavy load.
+ */
+ last = &(inst->sessions[state->strvalue[0]]);
while (*last) last = &((*last)->next);
- node->timestamp = time(NULL);
- node->status = 1;
- node->next = NULL;
+ /*
+ * The time at which this request was made was the time
+ * at which it was received by the RADIUS server.
+ */
+ handler->timestamp = handler->request->timestamp;
+ handler->status = 1;
+ handler->next = NULL;
+
+ /*
+ * We don't need this any more.
+ */
+ handler->request = NULL;
- *last = node;
+ *last = handler;
return 1;
}
/*
- * List should contain only recent packets with life < x seconds.
+ * Find a a previous EAP-Request sent by us, which matches
+ * the current EAP-Response.
+ *
+ * Then, release the handle from the list, and return it to
+ * the caller.
+ *
+ * Also since we fill the eap_ds with the present EAP-Response we
+ * got to free the prev_eapds & move the eap_ds to prev_eapds
*/
-void eaplist_clean(EAP_HANDLER **first, time_t limit)
+EAP_HANDLER *eaplist_find(rlm_eap_t *inst, REQUEST *request, int eap_id)
{
- time_t now;
- EAP_HANDLER *node, *next;
- EAP_HANDLER **last = first;
+ EAP_HANDLER *node, *next, *ret = NULL;
+ VALUE_PAIR *state;
+ EAP_HANDLER **first, **last;
+
+ /*
+ * We key the sessions off of the 'state' attribute, so it
+ * must exist.
+ */
+ state = pairfind(request->packet->vps, PW_STATE);
+ if (!state ||
+ (state->length != EAP_STATE_LEN)) {
+ return NULL;
+ }
- now = time(NULL);
+ last = first = &(inst->sessions[state->strvalue[0]]);
for (node = *first; node; node = next) {
next = node->next;
- if ((now - node->timestamp) > limit) {
- DEBUG2(" rlm_eap: list_clean deleted one item");
+
+ /*
+ * If the time on this entry has expired,
+ * delete it. We do this while walking the list,
+ * in order to spread out the work of deleting old
+ * sessions.
+ */
+ if ((request->timestamp - node->timestamp) > inst->timer_limit) {
*last = next;
eap_handler_free(&node);
- } else {
- last = &(node->next);
+ continue;
}
- }
-}
-/*
- * If the present EAP-Response is a reply to the previous
- * EAP-Request sent by us, then return the EAP_HANDLER
- * only after releasing from the eaplist
- * Also since we fill the eap_ds with the present EAP-Response
- * we got to free the prev_eapds & move the eap_ds to prev_eapds
- */
-EAP_HANDLER *eaplist_isreply(EAP_HANDLER **first, unsigned char id[])
-{
- EAP_HANDLER *node, *next, *ret = NULL;
- EAP_HANDLER **last = first;
-
- for (node = *first; node; node = next) {
- next = node->next;
- if (memcmp(node->id, id, id[0]) == 0) {
+ /*
+ * Find the previous part of the same conversation,
+ * keying off of the EAP ID, the client IP, and
+ * the State attribute.
+ *
+ * If we've found a conversation, then we don't
+ * have to check entries later in the list for
+ * timeout, as they're guaranteed to be newer than
+ * the one we found.
+ */
+ if ((node->eap_id == eap_id) &&
+ (node->src_ipaddr == request->packet->src_ipaddr) &&
+ (memcmp(node->state, state->strvalue, state->length) == 0)) {
+ /*
+ * Check against replays. The client can
+ * re-play a State attribute verbatim, so
+ * we wish to ensure that the attribute falls
+ * within the valid time window, which is
+ * the second at which it was sent out.
+ */
+ if (verify_state(state, node->timestamp) != 0) {
+ radlog(L_ERR, "rlm_eap: State verification failed.");
+ return NULL;
+ }
+
DEBUG2(" rlm_eap: Request found, released from the list");
- /* detach the node from the list */
+ /*
+ * detach the node from the list
+ */
*last = next;
node->next = NULL;
- /* clean up the unwanted stuff before returning */
+ /*
+ * Don't bother updating handler->request, etc.
+ * eap_handler() will do that for us.
+ */
- /* Clear the handler Id */
- free(node->id);
- node->id = NULL;
-
- /* Move the current EAP to prev EAP after clearing prev_eap */
+ /*
+ * Remember what the previous request was.
+ */
eap_ds_free(&(node->prev_eapds));
node->prev_eapds = node->eap_ds;
node->eap_ds = NULL;
- ret = node;
- break;
+ /*
+ * And return it to the caller.
+ */
+ return node;
} else {
last = &(node->next);
}
}
return ret;
}
-
-EAP_HANDLER *eaplist_findhandler(EAP_HANDLER *list, unsigned char id[])
-{
- EAP_HANDLER *node;
- node = list;
-
- while (node) {
- /*
- * Match is identified by the same IDs
- */
- if (memcmp(node->id, id, id[0]) == 0) {
- DEBUG2(" rlm_eap: EAP Handler found in the list ");
- return node;
- }
- node = node->next;
- }
- return NULL;
-}
#include "modules.h"
static CONF_PARSER module_config[] = {
- { "default_eap_type", PW_TYPE_STRING_PTR, offsetof(EAP_CONF, default_eap_type), NULL, "md5" },
- { "timer_expire", PW_TYPE_INTEGER, offsetof(EAP_CONF, timer_limit), NULL, "60"},
-
+ { "default_eap_type", PW_TYPE_STRING_PTR,
+ offsetof(rlm_eap_t, default_eap_type), NULL, "md5" },
+ { "timer_expire", PW_TYPE_INTEGER,
+ offsetof(rlm_eap_t, timer_limit), NULL, "60"},
+
{ NULL, -1, 0, NULL, NULL } /* end the list */
};
/*
+ * delete all the allocated space by eap module
+ */
+static int eap_detach(void *instance)
+{
+ rlm_eap_t *inst;
+ int i;
+
+ inst = (rlm_eap_t *)instance;
+
+ eaplist_free(inst);
+
+ for (i = 0; i < PW_EAP_MAX_TYPES; i++) {
+ if (inst->types[i]) eaptype_free(inst->types[i]);
+ inst->types[i] = NULL;
+ }
+
+ if (inst->default_eap_type) free(inst->default_eap_type);
+ free(inst);
+
+ return 0;
+}
+
+
+/*
* read the config section and load all the eap authentication types present.
*/
static int eap_instantiate(CONF_SECTION *cs, void **instance)
{
- char *auth_type;
+ int id;
+ int num_types;
CONF_SECTION *scs;
- EAP_TYPES *types;
- EAP_CONF *conf;
- rlm_eap_t **eap_stuff;
+ rlm_eap_t *inst;
- eap_stuff = (rlm_eap_t **)instance;
- types = NULL;
- conf = NULL;
- auth_type = NULL;
-
- conf = (EAP_CONF *)malloc(sizeof(*conf));
- if (conf == NULL) {
- radlog(L_ERR, "rlm_eap: out of memory");
+ inst = (rlm_eap_t *) malloc(sizeof(*inst));
+ if (!inst) {
return -1;
}
- memset(conf, 0, sizeof(*conf));
- if (cf_section_parse(cs, conf, module_config) < 0) {
- free(conf);
+ memset(inst, 0, sizeof(*inst));
+ if (cf_section_parse(cs, inst, module_config) < 0) {
+ eap_detach(inst);
return -1;
}
/* Load all the configured EAP-Types */
+ num_types = 0;
for(scs=cf_subsection_find_next(cs, NULL, NULL);
scs != NULL;
scs=cf_subsection_find_next(cs, scs, NULL)) {
+ char *auth_type;
+
auth_type = cf_section_name1(scs);
if (!auth_type) continue;
- if (eaptype_load(&types, auth_type, scs) < 0) {
- free(conf);
+ id = eaptype_name2id(auth_type);
+ if (id < 0) {
+ radlog(L_ERR|L_CONS, "rlm_eap: Unknown EAP type %s",
+ auth_type);
+ eap_detach(inst);
+ return -1;
+ }
+
+ if (eaptype_load(&inst->types[id], id, scs) < 0) {
+ eap_detach(inst);
return -1;
}
+
+ num_types++; /* successfully loaded one more types */
}
- if (!types) {
- free(conf->default_eap_type);
- conf->default_eap_type = NULL;
- free(conf);
- conf = NULL;
+ if (num_types == 0) {
+ radlog(L_ERR|L_CONS, "rlm_eap: No EAP type configured, module cannot do anything.");
+ eap_detach(inst);
return -1;
}
- *eap_stuff = (rlm_eap_t *)malloc(sizeof(rlm_eap_t));
- if (*eap_stuff) {
- (*eap_stuff)->typelist = types;
- (*eap_stuff)->echolist = NULL;
- (*eap_stuff)->conf = conf;
- } else {
- radlog(L_ERR, "rlm_eap: out of memory");
- eaptype_freelist(&types);
- free(conf->default_eap_type);
- conf->default_eap_type = NULL;
- free(conf);
- conf = NULL;
+ /*
+ * Ensure that the default EAP type is loaded.
+ */
+ id = eaptype_name2id(inst->default_eap_type);
+ if (id < 0) {
+ radlog(L_ERR|L_CONS, "rlm_eap: Unknown default EAP type %s",
+ inst->default_eap_type);
+ eap_detach(inst);
return -1;
}
- /* Generate a state key, specific to eap */
- generate_key();
- return 0;
-}
-
-/*
- * delete all the allocated space by eap module
- */
-static int eap_detach(void *instance)
-{
- rlm_eap_t *t;
- t = (rlm_eap_t *)instance;
-
- eaplist_free(&(t->echolist));
- eaptype_freelist(&(t->typelist));
-
- free(t->conf->default_eap_type);
- free(t->conf);
+ if (inst->types[id] == NULL) {
+ radlog(L_ERR|L_CONS, "rlm_eap: No such sub-type for default EAP type %s",
+ inst->default_eap_type);
+ eap_detach(inst);
+ return -1;
+ }
+ inst->default_eap_id = id;
- free(t);
- t = NULL;
+ /*
+ * List of sessions are set to NULL by the memset
+ * of 'inst', above.
+ */
+ /* Generate a state key, specific to eap */
+ generate_key();
+
+ *instance = inst;
return 0;
}
/*
- * Assumption: Any one of the Authorization module should
- * get the configured password for any valid user.
- * If not, Authentication fails to validate.
- *
- * All EAP types will be handled in their respective sub modules.
- *
- * To Handle EAP-response, we keep track of the EAP-request we send.
- * When Success or Failure or when timed out, we delete them.
+ * For backwards compatibility.
*/
static int eap_authenticate(void *instance, REQUEST *request)
{
+ rlm_eap_t *inst;
EAP_HANDLER *handler;
- rlm_eap_t *eap_stuff;
eap_packet_t *eap_packet;
- int status;
-
- eap_stuff = (rlm_eap_t *)instance;
+ int rcode;
- /*
- * Always, clean the list first as it is not timer based
- * FIXME: Appropriate cleaning mechanism.
- */
- eaplist_clean(&(eap_stuff->echolist), (time_t)eap_stuff->conf->timer_limit);
-
- /*
- * Incase if EAP is not configured in autz block
- * or eap_authorize is not invoked
- */
- status = eap_start(request);
- switch(status) {
- case EAP_NOOP:
- return RLM_MODULE_NOOP;
- case EAP_FAIL:
- return RLM_MODULE_FAIL;
- case EAP_FOUND:
- return RLM_MODULE_OK;
- case EAP_NOTFOUND:
- default:
- break;
- }
+ inst = (rlm_eap_t *) instance;
/* get the eap packet to start with */
eap_packet = eap_attribute(request->packet->vps);
}
/*
- * create the eap handler
+ * Create the eap handler
*/
- handler = eap_handler(&(eap_stuff->echolist), &eap_packet, request);
+ handler = eap_handler(inst, &eap_packet, request);
if (handler == NULL) {
return RLM_MODULE_INVALID;
}
/*
- * No User-Name, No authentication
+ * No User-Name, No authentication
*/
if (handler->username == NULL) {
radlog(L_ERR, "rlm_eap: Unknown User, authentication failed");
}
/*
- * Select the appropriate eap_type or default to the configured one
+ * Select the appropriate eap_type or default to the
+ * configured one
*/
- if (eaptype_select(eap_stuff->typelist, handler,
- eap_stuff->conf->default_eap_type) == EAP_INVALID) {
+ if (eaptype_select(inst, handler) == EAP_INVALID) {
eap_fail(request, handler->eap_ds);
eap_handler_free(&handler);
}
/*
- * We are done, wrap the EAP-request in RADIUS to send
- * with all other required radius attributes
+ * We are done, wrap the EAP-request in RADIUS to send
+ * with all other required radius attributes
*/
- eap_compose(request, handler->eap_ds);
+ rcode = eap_compose(request, handler->eap_ds);
/*
- * Add to the list only if it is EAP-Request,
- * OR if it's LEAP, and a response.
+ * Add to the list only if it is EAP-Request, OR if
+ * it's LEAP, and a response.
*/
if ((handler->eap_ds->request->code == PW_EAP_REQUEST) &&
(handler->eap_ds->request->type.type >= PW_EAP_MD5)) {
- handler->id = eap_generateid(request, (u_char)handler->eap_ds->request->id);
- if (handler->id == NULL) {
- radlog(L_ERR, "rlm_eap: problem in generating ID, Present EAP is not valid");
- eap_handler_free(&handler);
- } else {
- eaplist_add(&(eap_stuff->echolist), handler);
- }
+ eaplist_add(inst, handler);
/*
* LEAP is a little different. At Stage 4,
(handler->eap_ds->response->type.type == PW_EAP_LEAP) &&
(handler->eap_ds->request->code == PW_EAP_SUCCESS) &&
(handler->eap_ds->request->type.type == 0)) {
- VALUE_PAIR *state;
-
- DEBUG2(" rlm_eap: Saving LEAP state");
- handler->id = eap_regenerateid(request, (u_char)handler->eap_ds->request->id);
- if (handler->id == NULL) {
- radlog(L_ERR, "rlm_eap: problem in generating ID, Present EAP is not valid");
- eap_handler_free(&handler);
- } else {
- eaplist_add(&(eap_stuff->echolist), handler);
- }
-
- /*
- * And copy the State attribute from the request
- */
- state = paircopy2(request->packet->vps, PW_STATE);
- /*
- * FIXME: Assert there's only 1 state?
- */
- pairadd(&request->reply->vps, state);
+ eaplist_add(inst, handler);
} else {
DEBUG2(" rlm_eap: Freeing handler");
/* handler is no more required, free it now */
eap_handler_free(&handler);
}
- return RLM_MODULE_OK;
+
+ /*
+ * If it's an Access-Accept, RFC 2869, Section 2.3.1
+ * says that we MUST include a User-Name attribute in the
+ * Access-Accept.
+ */
+ if ((request->reply->code == PW_AUTHENTICATION_ACK) &&
+ request->username) {
+ VALUE_PAIR *vp;
+
+ /*
+ * Doesn't exist, add it in.
+ */
+ vp = pairfind(request->reply->vps, PW_USER_NAME);
+ if (!vp) {
+ vp = pairmake("User-Name", request->username->strvalue,
+ T_OP_EQ);
+ rad_assert(vp != NULL);
+ pairadd(&(request->reply->vps), vp);
+ }
+ }
+
+ return rcode;
}
/*
*/
static int eap_authorize(void *instance, REQUEST *request)
{
- VALUE_PAIR *atype, *vp;
- rlm_eap_t *eap_stuff;
- eap_packet_t *eap_packet;
+ rlm_eap_t *inst;
int status;
- unsigned char *id;
+ VALUE_PAIR *vp;
- eap_stuff = (rlm_eap_t *)instance;
+ inst = (rlm_eap_t *)instance;
- /* Authorization not valid for proxies */
+ /*
+ * We don't do authorization again, once we've seen the
+ * proxy reply (or the proxied packet)
+ */
if (request->proxy != NULL)
return RLM_MODULE_NOOP;
/*
- * For EAP_START, send Access-Challenge with EAP Identity request.
- * even when we have to proxy this request
+ * For EAP_START, send Access-Challenge with EAP Identity
+ * request. even when we have to proxy this request
+ *
+ * RFC 2869, Section 2.3.1 notes that the "domain" of the
+ * user, (i.e. where to proxy him) comes from the EAP-Identity,
+ * so we CANNOT proxy the user, until we know his identity.
+ *
+ * We therefore send an EAP Identity request.
*/
status = eap_start(request);
switch(status) {
* We should have User-Name to proceed further
*/
if (request->username == NULL) {
-
- /* get the eap packet */
- eap_packet = eap_attribute(request->packet->vps);
- if (eap_packet == NULL) {
- radlog(L_ERR, "rlm_eap: Malformed EAP Message");
- return RLM_MODULE_FAIL;
- }
-
- id = eap_regenerateid(request, eap_packet->id);
- if (id == NULL) {
- radlog(L_ERR, "rlm_eap: User-Name cannot be obtained");
- free(eap_packet);
- return RLM_MODULE_FAIL;
- }
-
- request->username = eap_useridentity(eap_stuff->echolist, eap_packet, id);
- if (request->username == NULL) {
- radlog(L_ERR, "rlm_eap: Unknown User, authorization failed");
- free(eap_packet);
- free(id);
- return RLM_MODULE_FAIL;
- }
- free(eap_packet);
- free(id);
+ radlog(L_ERR, "rlm_eap: User-Name is required for EAP authentication");
+ return RLM_MODULE_FAIL;
}
- /*
- * Enforce EAP authentication
-
- * Auth-type(s) already set? overide it with EAP
- * If EAP-Message is present in RADIUS, then EAP authentication is MUST.
-
- * TODO: When Multiple authentications are supported in RADIUS,
- * then prioritize EAP by prepending it before all Auth-Types
- */
-
- atype = pairfind(request->config_items, PW_AUTHTYPE);
- if ((atype == NULL) ||
- ((atype->lvalue != PW_AUTHTYPE_EAP) &&
- (atype->lvalue != PW_AUTHTYPE_ACCEPT) &&
- (atype->lvalue != PW_AUTHTYPE_REJECT))) {
-
+ vp = pairfind(request->config_items, PW_AUTH_TYPE);
+ if ((!vp) ||
+ (vp->lvalue != PW_AUTHTYPE_REJECT)) {
vp = pairmake("Auth-Type", "EAP", T_OP_EQ);
- if (vp == NULL) {
+ if (!vp) {
return RLM_MODULE_FAIL;
}
- /* to overide */
- pairdelete(&request->config_items, PW_AUTHTYPE);
pairadd(&request->config_items, vp);
-
- /* To prioritize
- vp->next = request->config_items;
- request->config_items = vp;
- */
}
return RLM_MODULE_UPDATED;
unsigned char data[1];
} eap_packet_t;
-/*
- * Config stuff that rlm_eap depends on.
- */
-typedef struct eap_conf {
- char *default_eap_type;
- int timer_limit;
-} EAP_CONF;
-
-/*
- * Currently there are only 2 types
- * of operations defined,
- * apart from attach & detach for each EAP-Type.
- */
-typedef enum operation_t {
- INITIATE = 0,
- AUTHENTICATE
-} operation_t;
/*
* Keep track of which sub modules we've loaded.
*/
typedef struct eap_types_t {
- struct eap_types_t *next;
+ const char *typename;
int typeid;
- char typename[NAME_LEN];
EAP_TYPE *type;
lt_dlhandle handle;
CONF_SECTION *cs;
- void *type_stuff;
+ void *type_data;
} EAP_TYPES;
/*
* This structure contains eap's persistent data.
- * echolist = EAP_HANDLERs
+ * sessions[] = EAP_HANDLERS, keyed by the first octet of the State
+ * attribute, and composed of a linked list, ordered from
+ * oldest to newest.
* typelist = All supported EAP-Types
* conf = configured values for rlm_eap only.
*/
typedef struct rlm_eap_t {
- EAP_HANDLER *echolist;
- EAP_TYPES *typelist;
- EAP_CONF *conf;
+ EAP_HANDLER *sessions[256];
+ EAP_TYPES *types[PW_EAP_MAX_TYPES + 1];
+
+ /*
+ * Configuration items.
+ */
+ char *default_eap_type;
+ int timer_limit;
+ int default_eap_id;
} rlm_eap_t;
/* function definitions */
/* EAP-Type */
+int eaptype_name2id(const char *name);
EAP_TYPES *eaptype_byid(EAP_TYPES **list, int type);
EAP_TYPES *eaptype_byname(EAP_TYPES **list, const char *name);
-int eaptype_load(EAP_TYPES **tl, const char *tname, CONF_SECTION *cs);
-int eaptype_select(EAP_TYPES *tl, EAP_HANDLER *h, char *eaptype);
-int eaptype_call(int type, operation_t act, EAP_TYPES *tl, EAP_HANDLER *h);
-void eaptype_freelist(EAP_TYPES **tl);
+int eaptype_load(EAP_TYPES **type, int id, CONF_SECTION *cs);
+int eaptype_select(rlm_eap_t *inst, EAP_HANDLER *h);
+void eaptype_free(EAP_TYPES *tl);
/* EAP */
int eap_start(REQUEST *request);
int eap_wireformat(EAP_PACKET *packet);
int eap_compose(REQUEST *request, EAP_DS *eap_ds);
eap_packet_t *eap_attribute(VALUE_PAIR *vps);
-EAP_HANDLER *eap_handler(EAP_HANDLER **list, eap_packet_t **eap_msg, REQUEST *request);
+EAP_HANDLER *eap_handler(rlm_eap_t *inst, eap_packet_t **eap_msg, REQUEST *request);
char *eap_identity(eap_packet_t *eap_packet);
-VALUE_PAIR *eap_useridentity(EAP_HANDLER *list, eap_packet_t *eap_packet, unsigned char id[]);
-unsigned char *eap_generateid(REQUEST *request, unsigned char response_id);
-unsigned char *eap_regenerateid(REQUEST *request, unsigned char response_id);
/* Memory Management */
EAP_PACKET *eap_packet_alloc(void);
void eap_ds_free(EAP_DS **eap_ds);
void eap_handler_free(EAP_HANDLER **handler);
-int eaplist_add(EAP_HANDLER **list, EAP_HANDLER *handler);
-void eaplist_clean(EAP_HANDLER **list, time_t limit);
-void eaplist_free(EAP_HANDLER **list);
-EAP_HANDLER *eaplist_isreply(EAP_HANDLER **list, unsigned char id[]);
-EAP_HANDLER *eaplist_findhandler(EAP_HANDLER *list, unsigned char id[]);
+int eaplist_add(rlm_eap_t *inst, EAP_HANDLER *handler);
+void eaplist_free(rlm_eap_t *inst);
+EAP_HANDLER *eaplist_find(rlm_eap_t *inst, REQUEST *request, int id);
/* State */
void generate_key(void);
-VALUE_PAIR *generate_state(void);
-int verify_state(VALUE_PAIR *state);
+VALUE_PAIR *generate_state(time_t timestamp);
+int verify_state(VALUE_PAIR *state, time_t timestamp);
#endif /*_RLM_EAP_H*/
/*
* Global key to generate & verify State
*
- * FIXME: this means that we can't have multiple instances
- * of the EAP module.
+ * This is needed only once per instance of the server,
+ * and putting it in the rlm_eap_t is just too much effort.
+ *
+ * Putting it here is ugly, but it works.
*/
+static int key_initialized = 0;
static unsigned char state_key[AUTH_VECTOR_LEN];
/*
- * Generate & Verify the State attribute
- *
- * In the simplest implementation, we would just use the challenge as state.
- * Unfortunately, the RADIUS secret protects only the User-Password
- * attribute; an attacker that can remove packets from the wire and insert
- * new ones can simply insert a replayed state without having to know
- * the secret. If not for an attacker that can remove packets from the
- * network, I believe trivial state to be secure.
- *
- * So, we have to make up for that deficiency by signing our state with
- * data unique to this specific request. A NAS would use the Request
- * Authenticator, we don't know what that will be when the State is
- * returned to us, so we'll use the time. So our replay prevention
- * is limited to a time interval (inst->maxdelay). We could keep
- * track of all challenges issued over that time interval for
- * better protection.
- *
- * Our state, then, is (challenge + time + hmac(challenge + time, key)),
- * where '+' denotes concatentation, 'challenge' is the ASCII octets of
- * the challenge, 'time' is the 32-bit time (LSB if time_t is 64 bits)
- * in network byte order, and 'key' is a random key, generated once in
- * eap_init(). This means that only the server which generates
- * a challenge can verify it; this should be OK if your NAS's load balance
- * across RADIUS servers by a "first available" algorithm. If your
- * NAS's round-robin (ugh), you could use the RADIUS secret instead, but
- * read RFC 2104 first, and make very sure you really want to do this.
+ * Generate & Verify the State attribute
+ *
+ * In the simplest implementation, we would just use the
+ * challenge as state. Unfortunately, the RADIUS secret protects
+ * only the User-Password attribute; an attacker that can remove
+ * packets from the wire and insert new ones can simply insert a
+ * replayed state without having to know the secret.
+ *
+ * However, RADIUS packets containing EAP conversations MUST be
+ * signed with Message-Authenticator, at which point, they MUST
+ * know the secret, in order to get to the EAP module. And if
+ * they know the secret, they can do many worse things than
+ * re-playing a State attribute. Their only alternative is to
+ * re-play entire packets, which is caught by the server core.
+ *
+ * In any case, we sign our state with data unique to this
+ * specific request. A NAS would use the Request Authenticator,
+ * we don't know what that will be when the State is returned to
+ * us, so we'll use a time stamp.
+ *
+ * Our replay prevention is limited to a time interval
+ * (inst->maxdelay). We could keep track of all challenges
+ * issued over that time interval, to ensure that the challenges
+ * were unique. However, they're 8-bytes of data from a good
+ * PRNG, which means that it's pretty damn unlikely that they'll
+ * be re-used.
*
+ * Our state, then, is (challenge + hmac(challenge + time, key)),
+ * where '+' denotes concatentation, 'challenge' is the octets
+ * of the challenge, 'time' is the 'time_t' in host byte order,
+ * and 'key' is a random key, generated once in eap_init().
+ *
+ * This means that only the server which generates a challenge
+ * can verify it; this should be OK if your NAS's load balance
+ * across RADIUS servers by a "first available" algorithm. If
+ * your NAS's round-robin (ugh), you could use the RADIUS
+ * secret instead, but read RFC 2104 first, and make very sure
+ * you really want to do this.
*/
-
void generate_key(void)
{
int i;
+ if (key_initialized) return;
+
/*
* Use a cryptographically strong method to generate
* pseudo-random numbers.
for (i = 0; i < sizeof(state_key); i++) {
state_key[i] = lrad_rand();
}
+
+ key_initialized = 1;
}
/*
+ * For clarity. Also, to avoid giving away
+ * too much information, we only put 8 octets of the HMAC
+ * into the State attribute, instead of all 16.
+ *
+ * As a security feature, it's a little hokey, but WTF.
+ *
+ * Also, ensure that EAP_CHALLENGE_LEN + EAP_USE_OF_HMAC = EAP_STATE_LEN
+ */
+#define EAP_CHALLENGE_LEN (8)
+#define EAP_HMAC_SIZE (16)
+#define EAP_USE_OF_HMAC (8)
+
+/*
* Our state, is (challenge + time + hmac(challenge + time, key))
+ *
+ * If it's too long, then some clients chop it (sigh)
*/
-VALUE_PAIR *generate_state(void)
+VALUE_PAIR *generate_state(time_t timestamp)
{
int i;
- unsigned char challenge[AUTH_VECTOR_LEN];
- unsigned char hmac[AUTH_VECTOR_LEN];
- unsigned char value[AUTH_VECTOR_LEN+sizeof(time_t)];
+ unsigned char challenge[EAP_CHALLENGE_LEN];
+ unsigned char hmac[EAP_HMAC_SIZE];
+ unsigned char value[EAP_CHALLENGE_LEN + sizeof(timestamp)];
VALUE_PAIR *state;
- time_t now;
/* Generate challenge (a random value). */
for (i = 0; i < sizeof(challenge); i++) {
challenge[i] = lrad_rand();
}
- now = time(NULL);
- memcpy(value, challenge, AUTH_VECTOR_LEN);
- memcpy(value + AUTH_VECTOR_LEN, &now, sizeof(time_t));
-
- /* Generate hmac. */
- lrad_hmac_md5(value, AUTH_VECTOR_LEN + sizeof(time_t),
- state_key, AUTH_VECTOR_LEN, hmac);
+ memcpy(value, challenge, sizeof(challenge));
+ memcpy(value + sizeof(challenge), ×tamp, sizeof(timestamp));
+ /*
+ * hmac(challenge + timestamp)
+ */
+ lrad_hmac_md5(value, sizeof(value),
+ state_key, sizeof(state_key), hmac);
- /* Create state attribute. */
+ /*
+ * Create the state attribute.
+ *
+ * Note that the timestamp is used internally, but is NOT
+ * sent to the client!
+ */
state = paircreate(PW_STATE, PW_TYPE_OCTETS);
if (state == NULL) {
radlog(L_ERR, "rlm_eap: out of memory");
return NULL;
}
- memcpy(state->strvalue, value, AUTH_VECTOR_LEN+sizeof(time_t));
- memcpy(state->strvalue+AUTH_VECTOR_LEN+sizeof(time_t), hmac, AUTH_VECTOR_LEN);
- state->length = AUTH_VECTOR_LEN + sizeof(time_t) + AUTH_VECTOR_LEN;
+ memcpy(state->strvalue, challenge, sizeof(challenge));
+ memcpy(state->strvalue + sizeof(challenge), hmac,
+ EAP_USE_OF_HMAC);
+
+ state->length = sizeof(challenge) + EAP_USE_OF_HMAC;
return state;
}
/*
* Returns 0 on success, non-zero otherwise.
*/
-int verify_state(VALUE_PAIR *state)
+int verify_state(VALUE_PAIR *state, time_t timestamp)
{
- unsigned char prev_hmac[AUTH_VECTOR_LEN];
- unsigned char hmac[AUTH_VECTOR_LEN];
- unsigned char value[AUTH_VECTOR_LEN+sizeof(time_t)];
-
- /* Get the challenge value & hmac from the State */
- memcpy(value, state->strvalue, AUTH_VECTOR_LEN+sizeof(time_t));
- memcpy(prev_hmac, state->strvalue+AUTH_VECTOR_LEN+sizeof(time_t), AUTH_VECTOR_LEN);
+ unsigned char hmac[EAP_HMAC_SIZE];
+ unsigned char value[EAP_CHALLENGE_LEN + sizeof(timestamp)];
+ /*
+ * The length is wrong. Don't do anything.
+ */
+ if (state->length != EAP_STATE_LEN) {
+ return -1;
+ }
+
+ /*
+ * The first 16 octets of the State attribute constains
+ * the random challenge.
+ */
+ memcpy(value, state->strvalue, EAP_CHALLENGE_LEN);
+ memcpy(value + EAP_CHALLENGE_LEN, ×tamp, sizeof(timestamp));
+
/* Generate hmac. */
- lrad_hmac_md5(value, AUTH_VECTOR_LEN + sizeof(time_t),
- state_key, AUTH_VECTOR_LEN, hmac);
+ lrad_hmac_md5(value, sizeof(value),
+ state_key, sizeof(state_key), hmac);
- /* verify both the hmacs */
- return memcmp(hmac, prev_hmac, AUTH_VECTOR_LEN);
+ /*
+ * Compare the hmac we calculated to the one in the
+ * packet.
+ */
+ return memcmp(hmac, state->strvalue + EAP_CHALLENGE_LEN,
+ EAP_USE_OF_HMAC);
}
+
TARGET = rlm_eap_leap
SRCS = rlm_eap_leap.c eap_leap.c
RLM_CFLAGS = $(INCLTDL) -I../..
-HEADERS = des.h eap_leap.h
+HEADERS = eap_leap.h ../../eap.h ../../rlm_eap.h
RLM_INSTALL =
$(STATIC_OBJS): $(HEADERS)
/*
* The User-Name comes after the challenge.
+ *
+ * Length of the EAP-LEAP portion of the packet, minus
+ * 3 octets for data, minus the challenge size, is the
+ * length of the user name.
*/
- name_len = eap_ds->response->length - 3 - packet->count;
+ name_len = packet->length - 3 - packet->count;
if (name_len > 0) {
packet->name = malloc(name_len + 1);
if (!packet->name) {
#include "eap_leap.h"
-static void leap_session_free(void **opaque)
-{
- leap_session_t *session;
-
- if (!opaque) return;
-
- session = *(leap_session_t **) opaque;
-
- free(session);
- *opaque = NULL;
-}
-
/*
* send an initial eap-leap request
* ie access challenge to the user/peer.
* of filling in the peer response.
*/
session = (leap_session_t *) handler->opaque;
- handler->free_opaque = leap_session_free;
+ handler->free_opaque = free; /* just malloc'd memory */
session->stage = 4; /* the next stage we're in */
memcpy(session->peer_challenge, reply->challenge, reply->count);
DEBUG2(" rlm_eap_leap: Successfully initiated");
+ /*
+ * The next stage to process the packet.
+ */
+ handler->stage = AUTHENTICATE;
+
eapleap_free(&reply);
return 1;
}
* The password is never sent over the wire.
* Always get the configured password, for each user.
*/
- password = pairfind(handler->configured, PW_PASSWORD);
- if (!password) password = pairfind(handler->configured, PW_NT_PASSWORD);
+ password = pairfind(handler->request->config_items, PW_PASSWORD);
+ if (!password) password = pairfind(handler->request->config_items, PW_NT_PASSWORD);
if (!password) {
radlog(L_INFO, "rlm_eap_leap: No User-Password or NT-Password configured for this user");
eapleap_free(&packet);
DEBUG2(" rlm_eap_leap: Stage 6");
reply = eapleap_stage6(packet, handler->request,
handler->username, password,
- session, handler->reply_vps);
+ session, &handler->request->reply->vps);
break;
/*
"eap_leap",
NULL, /* attach */
leap_initiate, /* Start the initial request, after Identity */
+ NULL, /* authorization */
leap_authenticate, /* authentication */
NULL, /* detach */
};
}
/*
- * We expect only RESPONSE for which CHALLENGE, SUCCESS or FAILURE is sent back
+ * We expect only RESPONSE for which SUCCESS or FAILURE is sent back
*/
MD5_PACKET *eapmd5_extract(EAP_DS *eap_ds)
{
MD5_PACKET *packet;
unsigned short name_len;
- if (!eap_ds ||
- !eap_ds->response ||
- (eap_ds->response->code != PW_MD5_RESPONSE) ||
- eap_ds->response->type.type != PW_EAP_MD5 ||
- !eap_ds->response->type.data ||
- (eap_ds->response->length < MD5_HEADER_LEN) ||
- (eap_ds->response->type.data[0] <= 0) ) {
-
+ /*
+ * We need a response, of type EAP-MD5, with at least
+ * one byte of type data (EAP-MD5) following the 4-byte
+ * EAP-Packet header.
+ */
+ if (!eap_ds ||
+ !eap_ds->response ||
+ (eap_ds->response->code != PW_MD5_RESPONSE) ||
+ eap_ds->response->type.type != PW_EAP_MD5 ||
+ !eap_ds->response->type.data ||
+ (eap_ds->response->length <= MD5_HEADER_LEN) ||
+ (eap_ds->response->type.data[0] <= 0)) {
radlog(L_ERR, "rlm_eap_md5: corrupted data");
return NULL;
}
if (!packet) return NULL;
/*
- * Code, id & length for MD5 & EAP are same
- * but md5_length = eap_length - 1(Type = 1 octet)
+ * Code & id for MD5 & EAP are same
+ *
+ * but md5_length = length of the EAP-MD5 data, which
+ * doesn't include the EAP header, or the octet saying
+ * EAP-MD5.
*/
packet->code = eap_ds->response->code;
packet->id = eap_ds->response->id;
- packet->length = eap_ds->response->length - 1;
- packet->value_size = 0;
- packet->value = NULL;
- packet->name = NULL;
+ packet->length = eap_ds->response->length - (MD5_HEADER_LEN + 1);
+ /*
+ * Sanity check the EAP-MD5 packet sent to us
+ * by the client.
+ */
data = (md5_packet_t *)eap_ds->response->type.data;
+ /*
+ * Already checked the size above.
+ */
packet->value_size = data->value_size;
- if (packet->value_size < 1) {
- radlog(L_ERR, "rlm_eap_md5: Value size is too small");
- eapmd5_free(&packet);
- return NULL;
- }
+ /*
+ * Allocate room for the data, and copy over the data.
+ */
packet->value = malloc(packet->value_size);
if (packet->value == NULL) {
radlog(L_ERR, "rlm_eap_md5: out of memory");
memcpy(packet->value, data->value_name, packet->value_size);
/*
- * Name is optional and is present after Value, but we need to check for it
+ * Name is optional and is present after Value, but we
+ * need to check for it, as eapmd5_compose()
*/
- name_len = packet->length - (packet->value_size + 5);
+ name_len = packet->length - (packet->value_size + 1);
if (name_len) {
- packet->name = malloc(name_len+1);
+ packet->name = malloc(name_len + 1);
if (!packet->name) {
radlog(L_ERR, "rlm_eap_md5: out of memory");
eapmd5_free(&packet);
return NULL;
}
- memset(packet->name, 0, name_len+1);
- memcpy(packet->name, data->value_name+packet->value_size, name_len);
+ memcpy(packet->name, data->value_name + packet->value_size,
+ name_len);
+ packet->name[name_len] = 0;
}
return packet;
}
-/*
- * Generate a random value
- * challenge = MD5(random)
- */
-int eapmd5_challenge(unsigned char *value, int len)
-{
- int i;
-
- /*
- * Get real pseudo-random numbers.
- */
- for (i = 0; i < len; i++) {
- value[i] = lrad_rand();
- }
- radlog(L_INFO, "rlm_eap_md5: Issuing Challenge");
-
- return 1;
-}
/*
* verify = MD5(id+password+challenge_sent)
*/
int eapmd5_verify(MD5_PACKET *packet, VALUE_PAIR* password,
- md5_packet_t *challenge)
+ uint8_t *challenge)
{
char *ptr;
- char string[MAX_STRING_LEN*2];
+ char string[1 + MAX_STRING_LEN*2];
unsigned char output[MAX_STRING_LEN];
unsigned short len;
- if ((password == NULL) || (challenge == NULL)) {
- return 0;
- }
-
/*
* Sanity check it.
*/
len = 0;
ptr = string;
+ /*
+ * This is really rad_chap_pwencode()...
+ */
*ptr++ = packet->id;
len++;
memcpy(ptr, password->strvalue, password->length);
ptr += password->length;
len += password->length;
- memcpy(ptr, challenge->value_name, challenge->value_size);
- len += challenge->value_size;
+ /*
+ * The challenge size is hard-coded.
+ */
+ memcpy(ptr, challenge, MD5_CHALLENGE_LEN);
+ len += MD5_CHALLENGE_LEN;
librad_md5_calc((u_char *)output, (u_char *)string, len);
return 1;
}
-/*
- * Identify whether the response that you got is either the
- * response to the challenge that we sent or a new one.
- * If it is a response to the request then issue success/failure
- * else issue a challenge
- */
-MD5_PACKET *eapmd5_process(MD5_PACKET *packet, int id,
- VALUE_PAIR *username, VALUE_PAIR* password, md5_packet_t *request)
-{
- unsigned char output[MAX_STRING_LEN];
- MD5_PACKET *reply;
-
- if (!username || !password || !packet)
- return NULL;
-
- reply = eapmd5_alloc();
- if (!reply) return NULL;
- memset(output, 0, MAX_STRING_LEN);
- reply->id = id;
-
- if (request) {
- /* verify and issue Success/failure */
- if (eapmd5_verify(packet, password, request) == 0) {
- radlog(L_INFO, "rlm_eap_md5: Challenge failed");
- reply->code = PW_MD5_FAILURE;
- }
- else {
- reply->code = PW_MD5_SUCCESS;
- }
-
- } else {
- /*
- * Previous request not found.
- * Probably it is timed out.
- * So send another challenge.
- * TODO: Later Send these challenges for the configurable
- * number of times for each user & stop.
- */
-
- /*
- * Ensure that the challenge is always of the correct
- * length. i.e. Don't take value size from data
- * supplied by the client.
- */
- if (reply->value_size != MD5_LEN) {
- free(reply->value);
- reply->value_size = MD5_LEN;
- reply->value = malloc(reply->value_size);
- }
-
- eapmd5_challenge(reply->value, reply->value_size);
- reply->code = PW_MD5_CHALLENGE;
- radlog(L_INFO, "rlm_eap_md5: Previous request not found");
- radlog(L_INFO, "rlm_eap_md5: Issuing Challenge to the user - %s",
- (char *)username->strvalue);
- }
-
- /* fill reply packet */
- if (reply->code == PW_MD5_CHALLENGE) {
- reply->value_size = packet->value_size;
- reply->value = malloc(reply->value_size);
- if (reply->value == NULL) {
- radlog(L_ERR, "rlm_eap_md5: out of memory");
- eapmd5_free(&reply);
- return NULL;
- }
- memcpy(reply->value, output, reply->value_size);
- reply->length = packet->length;
- } else {
- reply->length = MD5_HEADER_LEN;
- }
-
- return reply;
-}
-
-/*
- * If an EAP MD5 request needs to be initiated then
- * create such a packet.
- */
-MD5_PACKET *eapmd5_initiate(EAP_DS *eap_ds)
-{
- MD5_PACKET *reply;
-
- reply = eapmd5_alloc();
- if (reply == NULL) {
- radlog(L_ERR, "rlm_eap_md5: out of memory");
- return NULL;
- }
-
- reply->code = PW_MD5_CHALLENGE;
- reply->length = MD5_HEADER_LEN + 1/*value_size*/ + MD5_LEN;
- reply->value_size = MD5_LEN;
-
- reply->value = malloc(reply->value_size);
- if (reply->value == NULL) {
- radlog(L_ERR, "rlm_eap_md5: out of memory");
- eapmd5_free(&reply);
- return NULL;
- }
-
- eapmd5_challenge(reply->value, reply->value_size);
-
- return reply;
-}
-
/*
- * compose the MD5 reply packet in the EAP reply typedata
+ * Compose the portions of the reply packet specific to the
+ * EAP-MD5 protocol, in the EAP reply typedata
*/
int eapmd5_compose(EAP_DS *eap_ds, MD5_PACKET *reply)
{
uint8_t *ptr;
unsigned short name_len;
+ /*
+ * We really only send Challenge (EAP-Identity),
+ * and EAP-Success, and EAP-Failure.
+ */
if (reply->code < 3) {
-
eap_ds->request->type.type = PW_EAP_MD5;
- eap_ds->request->type.data = malloc(reply->length - MD5_HEADER_LEN);
+ rad_assert(reply->length > 0);
+ rad_assert(reply->value_size < 256);
+
+ eap_ds->request->type.data = malloc(reply->length);
if (eap_ds->request->type.data == NULL) {
radlog(L_ERR, "rlm_eap_md5: out of memory");
return 0;
/* Just the Challenge length */
eap_ds->request->type.length = reply->value_size + 1;
- name_len = reply->length - (reply->value_size + 1 + MD5_HEADER_LEN);
+ /*
+ * Return the name, if necessary.
+ *
+ * Don't see why this is *ever* necessary...
+ */
+ name_len = reply->length - (reply->value_size + 1);
if (name_len && reply->name) {
ptr += reply->value_size;
memcpy(ptr, reply->name, name_len);
}
eap_ds->request->code = reply->code;
+ eapmd5_free(&reply);
+
return 1;
}
#define PW_MD5_MAX_CODES 4
#define MD5_HEADER_LEN 4
-#define MD5_LEN 16
+#define MD5_CHALLENGE_LEN 16
/*
****
* for generalization purpose, complete header should be sent
* and not just value_size, value and name.
* future implementation.
+ *
+ * Huh? What does that mean?
*/
/* eap packet structure */
unsigned char value_size;
unsigned char *value;
char *name;
-/* char *message; */
} MD5_PACKET;
-typedef struct md5_list {
- struct md5_list *next;
- MD5_PACKET *packet;
- char username[MAX_STRING_LEN];
- int processed;
- time_t time;
-} MD5_LIST;
-
/* function declarations here */
MD5_PACKET *eapmd5_alloc(void);
int eapmd5_compose(EAP_DS *auth, MD5_PACKET *reply);
MD5_PACKET *eapmd5_extract(EAP_DS *auth);
-MD5_PACKET *eapmd5_initiate(EAP_DS *eap_ds);
-MD5_PACKET *eapmd5_process(MD5_PACKET *packet, int id,
- VALUE_PAIR *username, VALUE_PAIR* password,
- md5_packet_t *req);
-int eapmd5_challenge(unsigned char *value, int len);
-int eapmd5_verify(MD5_PACKET *pkt, VALUE_PAIR* pwd, md5_packet_t *ch);
+int eapmd5_verify(MD5_PACKET *pkt, VALUE_PAIR* pwd, uint8_t *ch);
#endif /*_EAP_MD5_H*/
#include "eap_md5.h"
-
-static int md5_attach(CONF_SECTION *conf, void **arg)
-{
- return 0;
-}
+#include <rad_assert.h>
/*
- * send an initial eap-md5 request
- * ie access challenge to the user/peer.
-
- * Frame eap reply packet.
- * len = header + type + md5_typedata
- * md5_typedata = value_size + value
+ * Initiate the EAP-MD5 session by sending a challenge to the peer.
*/
-static int md5_initiate(void *type_arg, EAP_HANDLER *handler)
+static int md5_initiate(void *type_data, EAP_HANDLER *handler)
{
+ int i;
MD5_PACKET *reply;
- reply = eapmd5_initiate(handler->eap_ds);
- if (reply == NULL)
+ type_data = type_data; /* -Wunused */
+
+ /*
+ * Allocate an EAP-MD5 packet.
+ */
+ reply = eapmd5_alloc();
+ if (reply == NULL) {
+ radlog(L_ERR, "rlm_eap_md5: out of memory");
+ return 0;
+ }
+
+ /*
+ * Fill it with data.
+ */
+ reply->code = PW_MD5_CHALLENGE;
+ reply->length = 1 + MD5_CHALLENGE_LEN; /* one byte of value size */
+ reply->value_size = MD5_CHALLENGE_LEN;
+
+ /*
+ * Allocate user data.
+ */
+ reply->value = malloc(reply->value_size);
+ if (reply->value == NULL) {
+ radlog(L_ERR, "rlm_eap_md5: out of memory");
+ eapmd5_free(&reply);
return 0;
+ }
+ /*
+ * Get a random challenge.
+ */
+ for (i = 0; i < reply->value_size; i++) {
+ reply->value[i] = lrad_rand();
+ }
+ radlog(L_INFO, "rlm_eap_md5: Issuing Challenge");
+
+ /*
+ * Keep track of the challenge.
+ */
+ handler->opaque = malloc(reply->value_size);
+ rad_assert(handler->opaque != NULL);
+ memcpy(handler->opaque, reply->value, reply->value_size);
+ handler->free_opaque = free;
+
+ /*
+ * Compose the EAP-MD5 packet out of the data structure,
+ * and free it.
+ */
eapmd5_compose(handler->eap_ds, reply);
- eapmd5_free(&reply);
+ /*
+ * We don't need to authorize the user at this point.
+ *
+ * We also don't need to keep the challenge, as it's
+ * stored in 'handler->eap_ds', which will be given back
+ * to us...
+ */
+ handler->stage = AUTHENTICATE;
+
return 1;
}
+
+/*
+ * Authenticate a previously sent challenge.
+ */
static int md5_authenticate(void *arg, EAP_HANDLER *handler)
{
MD5_PACKET *packet;
MD5_PACKET *reply;
- md5_packet_t *request;
- char* username;
VALUE_PAIR *password;
- EAP_DS *temp;
/*
- * Password is never sent over the wire.
- * Always get the configured password, for each user.
+ * Get the User-Password for this user.
*/
- password = pairfind(handler->configured, PW_PASSWORD);
+ rad_assert(handler->request != NULL);
+ rad_assert(handler->stage == AUTHENTICATE);
+
+ password = pairfind(handler->request->config_items, PW_PASSWORD);
if (password == NULL) {
- radlog(L_INFO, "rlm_eap_md5: No password configured for this user");
+ radlog(L_INFO, "rlm_eap_md5: User-Password is required for EAP-MD5 authentication");
return 0;
}
/*
- * Allocate memory AFTER doing sanity checks.
+ * Extract the EAP-MD5 packet.
*/
if (!(packet = eapmd5_extract(handler->eap_ds)))
return 0;
- username = (char *)handler->username->strvalue;
-
- temp = (EAP_DS *)handler->prev_eapds;
- request = temp?(md5_packet_t *)(temp->request->type.data):NULL;
- reply = eapmd5_process(packet, handler->eap_ds->request->id,
- handler->username, password, request);
+ /*
+ * Create a reply, and initialize it.
+ */
+ reply = eapmd5_alloc();
if (!reply) {
- eapmd5_free(&packet);
return 0;
}
+ reply->id = handler->eap_ds->request->id;
+ reply->length = 0;
+
+ /*
+ * Verify the received packet against the previous packet
+ * (i.e. challenge) which we sent out.
+ */
+ if (eapmd5_verify(packet, password, handler->opaque)) {
+ reply->code = PW_MD5_SUCCESS;
+ } else {
+ reply->code = PW_MD5_FAILURE;
+ }
+ /*
+ * Compose the EAP-MD5 packet out of the data structure,
+ * and free it.
+ */
eapmd5_compose(handler->eap_ds, reply);
- eapmd5_free(&reply);
eapmd5_free(&packet);
return 1;
}
-static int md5_detach(void **arg)
-{
- return 0;
-}
-
/*
* The module name should be the only globally exported symbol.
* That is, everything else should be 'static'.
*/
EAP_TYPE rlm_eap_md5 = {
"eap_md5",
- md5_attach, /* attach */
- md5_initiate, /* Start the initial request, after Identity */
+ NULL, /* attach */
+ md5_initiate, /* Start the initial request */
+ NULL, /* authorization */
md5_authenticate, /* authentication */
- md5_detach /* detach */
+ NULL /* detach */
};
case alert:
eaptls_fail(handler->eap_ds);
- session_free(&handler->opaque);
+ session_free(handler->opaque);
+ handler->opaque = NULL;
return EAPTLS_FAIL;
case handshake:
if (tls_session->info.handshake_type == finished) {
eaptls_success(handler->eap_ds);
- eaptls_gen_mppe_keys(handler->reply_vps,
+ eaptls_gen_mppe_keys(&handler->request->reply->vps,
tls_session->ssl);
- session_free(&handler->opaque);
+ session_free(handler->opaque);
+ handler->opaque = NULL;
return EAPTLS_SUCCESS;
} else if (tls_session->fragment > 0) {
/* Fragmentation handler, send next fragment */
default:
radlog(L_ERR, "rlm_eap_tls: Invalid ACK received");
- session_free(&handler->opaque);
+ session_free(handler->opaque);
+ handler->opaque = NULL;
return EAPTLS_NOOP;
}
}
/* We donot receive a TLS_START but we send it. */
if (TLS_START(eaptls_packet->flags) == 1) {
radlog(L_ERR, "rlm_eap_tls: Received EAP-TLS Start message");
- //return EAPTLS_START;
return EAPTLS_INVALID;
}
} else {
eaptls_fail(handler->eap_ds);
}
- //record_init(&tls_session->dirty_in);
}
return;
}
void tls_session_information(tls_session_t *tls_session);
/* Session */
-void session_free(void **ssn);
+void session_free(void *ssn);
void session_close(tls_session_t *ssn);
void session_init(tls_session_t *ssn);
#include "eap_tls.h"
static CONF_PARSER module_config[] = {
- { "rsa_key_exchange", PW_TYPE_BOOLEAN, offsetof(EAP_TLS_CONF, rsa_key), NULL, "no" },
- { "dh_key_exchange", PW_TYPE_BOOLEAN, offsetof(EAP_TLS_CONF, dh_key), NULL, "yes" },
- { "rsa_key_length", PW_TYPE_INTEGER, offsetof(EAP_TLS_CONF, rsa_key_length), NULL, "512" },
- { "dh_key_length", PW_TYPE_INTEGER, offsetof(EAP_TLS_CONF, dh_key_length), NULL, "512" },
- { "verify_depth", PW_TYPE_INTEGER, offsetof(EAP_TLS_CONF, verify_depth), NULL, "0" },
- { "CA_path", PW_TYPE_STRING_PTR, offsetof(EAP_TLS_CONF, ca_path), NULL, NULL },
- { "pem_file_type", PW_TYPE_BOOLEAN, offsetof(EAP_TLS_CONF, file_type), NULL, "yes" },
- { "private_key_file", PW_TYPE_STRING_PTR, offsetof(EAP_TLS_CONF, private_key_file), NULL, NULL },
- { "certificate_file", PW_TYPE_STRING_PTR, offsetof(EAP_TLS_CONF, certificate_file), NULL, NULL },
- { "CA_file", PW_TYPE_STRING_PTR, offsetof(EAP_TLS_CONF, ca_file), NULL, NULL },
- { "private_key_password", PW_TYPE_STRING_PTR, offsetof(EAP_TLS_CONF, private_key_password), NULL, NULL },
- { "dh_file", PW_TYPE_STRING_PTR, offsetof(EAP_TLS_CONF, dh_file), NULL, NULL },
- { "random_file", PW_TYPE_STRING_PTR, offsetof(EAP_TLS_CONF, random_file), NULL, NULL },
- { "fragment_size", PW_TYPE_INTEGER, offsetof(EAP_TLS_CONF, fragment_size), NULL, "1024" },
- { "include_length", PW_TYPE_BOOLEAN, offsetof(EAP_TLS_CONF, include_length), NULL, "yes" },
+ { "rsa_key_exchange", PW_TYPE_BOOLEAN,
+ offsetof(EAP_TLS_CONF, rsa_key), NULL, "no" },
+ { "dh_key_exchange", PW_TYPE_BOOLEAN,
+ offsetof(EAP_TLS_CONF, dh_key), NULL, "yes" },
+ { "rsa_key_length", PW_TYPE_INTEGER,
+ offsetof(EAP_TLS_CONF, rsa_key_length), NULL, "512" },
+ { "dh_key_length", PW_TYPE_INTEGER,
+ offsetof(EAP_TLS_CONF, dh_key_length), NULL, "512" },
+ { "verify_depth", PW_TYPE_INTEGER,
+ offsetof(EAP_TLS_CONF, verify_depth), NULL, "0" },
+ { "CA_path", PW_TYPE_STRING_PTR,
+ offsetof(EAP_TLS_CONF, ca_path), NULL, NULL },
+ { "pem_file_type", PW_TYPE_BOOLEAN,
+ offsetof(EAP_TLS_CONF, file_type), NULL, "yes" },
+ { "private_key_file", PW_TYPE_STRING_PTR,
+ offsetof(EAP_TLS_CONF, private_key_file), NULL, NULL },
+ { "certificate_file", PW_TYPE_STRING_PTR,
+ offsetof(EAP_TLS_CONF, certificate_file), NULL, NULL },
+ { "CA_file", PW_TYPE_STRING_PTR,
+ offsetof(EAP_TLS_CONF, ca_file), NULL, NULL },
+ { "private_key_password", PW_TYPE_STRING_PTR,
+ offsetof(EAP_TLS_CONF, private_key_password), NULL, NULL },
+ { "dh_file", PW_TYPE_STRING_PTR,
+ offsetof(EAP_TLS_CONF, dh_file), NULL, NULL },
+ { "random_file", PW_TYPE_STRING_PTR,
+ offsetof(EAP_TLS_CONF, random_file), NULL, NULL },
+ { "fragment_size", PW_TYPE_INTEGER,
+ offsetof(EAP_TLS_CONF, fragment_size), NULL, "1024" },
+ { "include_length", PW_TYPE_BOOLEAN,
+ offsetof(EAP_TLS_CONF, include_length), NULL, "yes" },
{ NULL, -1, 0, NULL, NULL } /* end the list */
};
-static int eaptls_attach(CONF_SECTION *cs, void **arg)
+static int eaptls_detach(void *arg)
+{
+ EAP_TLS_CONF *conf;
+ eap_tls_t *inst;
+
+ inst = (eap_tls_t *) arg;
+ conf = inst->conf;
+
+ if (conf) {
+ if (conf->dh_file) free(conf->dh_file);
+ conf->dh_file = NULL;
+ if (conf->certificate_file) free(conf->certificate_file);
+ conf->certificate_file = NULL;
+ if (conf->private_key_file) free(conf->private_key_file);
+ conf->private_key_file = NULL;
+ if (conf->private_key_password) free(conf->private_key_password);
+ conf->private_key_password = NULL;
+ if (conf->random_file) free(conf->random_file);
+ conf->random_file = NULL;
+
+ free(inst->conf);
+ inst->conf = NULL;
+ }
+
+ if (inst->ctx) SSL_CTX_free(inst->ctx);
+ inst->ctx = NULL;
+
+ free(inst);
+
+ return 0;
+}
+
+static int eaptls_attach(CONF_SECTION *cs, void **instance)
{
SSL_CTX *ctx;
EAP_TLS_CONF *conf;
- eap_tls_t **eaptls;
+ eap_tls_t *inst;
- eaptls = (eap_tls_t **)arg;
+ /* Store all these values in the data structure for later references */
+ inst = (eap_tls_t *)malloc(sizeof(*inst));
+ if (!inst) {
+ radlog(L_ERR, "rlm_eap_tls: out of memory");
+ return -1;
+ }
+ memset(inst, 0, sizeof(*inst));
/* Parse the config file & get all the configured values */
conf = (EAP_TLS_CONF *)malloc(sizeof(*conf));
return -1;
}
memset(conf, 0, sizeof(*conf));
+
+ inst->conf = conf;
if (cf_section_parse(cs, conf, module_config) < 0) {
- free(conf);
+ eaptls_detach(inst);
return -1;
}
/* Initialize TLS */
ctx = init_tls_ctx(conf);
- if (ctx == NULL) return -1;
-
- if (load_dh_params(ctx, conf->dh_file) < 0) return -1;
- if (generate_eph_rsa_key(ctx) < 0) return -1;
-
- /* Store all these values in the data structure for later references */
- *eaptls = (eap_tls_t *)malloc(sizeof(eap_tls_t));
- if (*eaptls == NULL) {
- radlog(L_ERR, "rlm_eap_tls: out of memory");
+ if (ctx == NULL) {
+ eaptls_detach(inst);
+ return -1;
+ }
+ inst->ctx = ctx;
- free(conf->dh_file);
- free(conf->certificate_file);
- free(conf->private_key_file);
- free(conf->private_key_password);
- free(conf);
+ if (load_dh_params(ctx, conf->dh_file) < 0) {
+ eaptls_detach(inst);
+ return -1;
+ }
+ if (generate_eph_rsa_key(ctx) < 0) {
+ eaptls_detach(inst);
return -1;
}
- radlog(L_ERR, "rlm_eap_tls: conf N ctx stored ");
- (*eaptls)->conf = conf;
- (*eaptls)->ctx = ctx;
+ *instance = inst;
return 0;
}
if (status == 0)
return 0;
+ /*
+ * The next stage to process the packet.
+ */
+ handler->stage = AUTHENTICATE;
+
return 1;
}
return 1;
}
-static int eaptls_detach(void **arg)
-{
- EAP_TLS_CONF *conf;
- eap_tls_t **eaptls;
-
- eaptls = (eap_tls_t **)arg;
- conf = (*eaptls)->conf;
-
- free(conf->dh_file);
- conf->dh_file = NULL;
- free(conf->certificate_file);
- conf->certificate_file = NULL;
- free(conf->private_key_file);
- conf->private_key_file = NULL;
- free(conf->private_key_password);
- conf->private_key_password = NULL;
- free(conf->random_file);
- conf->random_file = NULL;
-
- free((*eaptls)->conf);
- (*eaptls)->conf = NULL;
-
- SSL_CTX_free((*eaptls)->ctx);
- (*eaptls)->ctx = NULL;
-
- free(*eaptls);
- *eaptls = NULL;
-
- return 0;
-}
-
/*
* The module name should be the only globally exported symbol.
* That is, everything else should be 'static'.
EAP_TYPE rlm_eap_tls = {
"eap_tls",
eaptls_attach, /* attach */
- eaptls_initiate, /* Start the initial request, after Identity */
+ eaptls_initiate, /* Start the initial request */
+ NULL, /* authorization */
eaptls_authenticate, /* authentication */
eaptls_detach /* detach */
};
session_init(ssn);
}
-void session_free(void **ssn)
+void session_free(void *ssn)
{
- tls_session_t **sess = (tls_session_t **)ssn;
- if ((sess == NULL) || (*sess == NULL))
- return;
+ tls_session_t *sess = (tls_session_t *)ssn;
+
+ if (!ssn) return;
- session_close(*sess);
- free(*sess);
- *sess = NULL;
+ session_close(sess);
+ free(sess);
}
void record_init(record_t *rec)