/*
* eap.c rfc2284 & rfc2869 implementation
*
* Version: $Id: eap.c,v 1.52.4.2 2007/04/07 22:18:34 aland Exp $
*
* 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.
*
* 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.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
* Copyright 2000-2003 The FreeRADIUS server project
* Copyright 2001 hereUare Communications, Inc. <raghud@hereuare.com>
* Copyright 2003 Alan DeKok <aland@freeradius.org>
*/
/*
* EAP PACKET FORMAT
* --- ------ ------
* 0 1 2 3
* 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
* | Code | Identifier | Length |
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
* | Data ...
* +-+-+-+-+
*
*
* EAP Request and Response Packet Format
* --- ------- --- -------- ------ ------
* 0 1 2 3
* 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
* | Code | Identifier | Length |
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
* | Type | Type-Data ...
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-
*
*
* EAP Success and Failure Packet Format
* --- ------- --- ------- ------ ------
* 0 1 2 3
* 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
* | Code | Identifier | Length |
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
*
*/
#include "rlm_eap.h"
static const char rcsid[] = "$Id: eap.c,v 1.52.4.2 2007/04/07 22:18:34 aland Exp $";
static const char *eap_codes[] = {
"", /* 0 is invalid */
"request",
"response",
"success",
"failure"
};
/*
* Load all the required eap authentication types.
* Get all the supported EAP-types from config file.
*/
int eaptype_load(EAP_TYPES **type, int eap_type, CONF_SECTION *cs)
{
char buffer[64];
char namebuf[64];
const char *eaptype_name;
lt_dlhandle handle;
EAP_TYPES *node;
eaptype_name = eaptype_type2name(eap_type, namebuf, sizeof(namebuf));
snprintf(buffer, sizeof(buffer), "rlm_eap_%s", eaptype_name);
/* Link the loaded EAP-Type */
handle = lt_dlopenext(buffer);
if (handle == NULL) {
radlog(L_ERR, "rlm_eap: Failed to link EAP-Type/%s: %s",
eaptype_name, lt_dlerror());
return -1;
}
/* Make room for the EAP-Type */
node = (EAP_TYPES *)malloc(sizeof(EAP_TYPES));
if (node == NULL) {
radlog(L_ERR, "rlm_eap: out of memory");
return -1;
}
memset(node, 0, sizeof(*node));
/* fill in the structure */
node->handle = handle;
node->cs = cs;
/*
* In general, this is a terrible idea. It works here
* solely because the eap_type2name function returns a
* 'static const char *' pointer sometimes, and we can
* ONLY link to module which are named in that static
* array.
*/
node->typename = eaptype_name;
node->type_data = NULL;
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",
buffer, eaptype_name, lt_dlerror());
lt_dlclose(node->handle); /* ignore any errors */
free(node);
return -1;
}
if ((node->type->attach) &&
((node->type->attach)(node->cs, &(node->type_data)) < 0)) {
radlog(L_ERR, "rlm_eap: Failed to initialize type %s",
eaptype_name);
lt_dlclose(node->handle);
free(node);
return -1;
}
DEBUG("rlm_eap: Loaded and initialized type %s", eaptype_name);
*type = node;
return 0;
}
/*
* Call the appropriate handle with the right eap_type.
*/
static int eaptype_call(EAP_TYPES *atype, EAP_HANDLER *handler)
{
int rcode = 1;
DEBUG2(" rlm_eap: processing type %s", atype->typename);
rad_assert(atype != NULL);
switch (handler->stage) {
case INITIATE:
if (!atype->type->initiate(atype->type_data, handler))
rcode = 0;
break;
case AUTHORIZE:
/*
* The called function updates the EAP reply packet.
*/
if (!atype->type->authorize ||
!atype->type->authorize(atype->type_data, handler))
rcode = 0;
break;
case AUTHENTICATE:
/*
* The called function updates the EAP reply packet.
*/
if (!atype->type->authenticate ||
!atype->type->authenticate(atype->type_data, handler))
rcode = 0;
break;
default:
/* Should never enter here */
radlog(L_DBG, "rlm_eap: Invalid operation on eap_type");
rcode = 0;
break;
}
return rcode;
}
/*
* Based on TYPE, call the appropriate EAP-type handler
* Default to the configured EAP-Type
* for all Unsupported EAP-Types
*/
int eaptype_select(rlm_eap_t *inst, EAP_HANDLER *handler)
{
unsigned int default_eap_type = inst->default_eap_type;
eaptype_t *eaptype;
VALUE_PAIR *vp;
char namebuf[64];
const char *eaptype_name;
eaptype = &handler->eap_ds->response->type;
/*
* Don't trust anyone.
*/
if ((eaptype->type == 0) ||
(eaptype->type > PW_EAP_MAX_TYPES)) {
DEBUG2(" rlm_eap: Asked to select bad type");
return EAP_INVALID;
}
/*
* Figure out what to do.
*/
switch(eaptype->type) {
case PW_EAP_IDENTITY:
DEBUG2(" rlm_eap: EAP Identity");
/*
* Allow per-user configuration of EAP types.
*/
vp = pairfind(handler->request->config_items,
PW_EAP_TYPE);
if (vp) default_eap_type = vp->lvalue;
do_initiate:
/*
* Ensure it's valid.
*/
if ((default_eap_type < PW_EAP_MD5) ||
(default_eap_type > PW_EAP_MAX_TYPES) ||
(inst->types[default_eap_type] == NULL)) {
DEBUG2(" rlm_eap: No such EAP type %s",
eaptype_type2name(default_eap_type,
namebuf, sizeof(namebuf)));
return EAP_INVALID;
}
handler->stage = INITIATE;
handler->eap_type = default_eap_type;
/*
* Wild & crazy stuff! For TTLS & PEAP, we
* initiate a TLS session, and then pass that
* session data to TTLS or PEAP for the
* authenticate stage.
*
* Handler->eap_type holds the TRUE type.
*/
if ((default_eap_type == PW_EAP_TTLS) ||
(default_eap_type == PW_EAP_PEAP)) {
default_eap_type = PW_EAP_TLS;
}
/*
* We don't do TLS inside of TLS, as it's a bad
* idea...
*/
if (((handler->request->options & RAD_REQUEST_OPTION_FAKE_REQUEST) != 0) &&
(default_eap_type == PW_EAP_TLS)) {
DEBUG2(" rlm_eap: Unable to tunnel TLS inside of TLS");
return EAP_INVALID;
}
if (eaptype_call(inst->types[default_eap_type],
handler) == 0) {
DEBUG2(" rlm_eap: Default EAP type %s failed in initiate",
eaptype_type2name(default_eap_type,
namebuf, sizeof(namebuf)));
return EAP_INVALID;
}
break;
case PW_EAP_NAK:
/*
* The NAK data is the preferred EAP type(s) of
* the client.
*
* RFC 3748 says to list one or more proposed
* alternative types, one per octet, or to use
* 0 for no alternative.
*/
DEBUG2(" rlm_eap: EAP NAK");
/*
* Delete old data, if necessary.
*/
if (handler->opaque && handler->free_opaque) {
handler->free_opaque(handler->opaque);
handler->free_opaque = NULL;
handler->opaque = NULL;
}
/*
* It is invalid to request identity,
* notification & nak in nak
*/
if (eaptype->data == NULL) {
DEBUG2(" rlm_eap: Empty NAK packet, cannot decide what EAP type the client wants.");
return EAP_INVALID;
}
/*
* FIXME: Pick one type out of the one they asked
* for, as they may have asked for many.
*/
if ((eaptype->data[0] < PW_EAP_MD5) ||
(eaptype->data[0] > PW_EAP_MAX_TYPES)) {
DEBUG2(" rlm_eap: NAK asked for bad type %d",
eaptype->data[0]);
return EAP_INVALID;
}
default_eap_type = eaptype->data[0];
eaptype_name = eaptype_type2name(default_eap_type,
namebuf, sizeof(namebuf));
DEBUG2(" rlm_eap: EAP-NAK asked for EAP-Type/%s",
eaptype_name);
/*
* Prevent a firestorm if the client is confused.
*/
if (handler->eap_type == default_eap_type) {
DEBUG2(" rlm_eap: ERROR! Our request for %s was NAK'd with a request for %s, what is the client thinking?",
eaptype_name, eaptype_name);
return EAP_INVALID;
}
/*
* Enforce per-user configuration of EAP types.
*/
vp = pairfind(handler->request->config_items,
PW_EAP_TYPE);
if (vp && (vp->lvalue != default_eap_type)) {
char mynamebuf[64];
DEBUG2(" rlm_eap: Client wants %s, while we require %s, rejecting the user.",
eaptype_name,
eaptype_type2name(vp->lvalue,
mynamebuf,
sizeof(mynamebuf)));
return EAP_INVALID;
}
goto do_initiate;
break;
/*
* Key off of the configured sub-modules.
*/
default:
eaptype_name = eaptype_type2name(eaptype->type,
namebuf,
sizeof(namebuf));
DEBUG2(" rlm_eap: EAP/%s", eaptype_name);
/*
* We haven't configured it, it doesn't exit.
*/
if (!inst->types[eaptype->type]) {
DEBUG2(" rlm_eap: EAP type %d is unsupported",
eaptype->type);
return EAP_INVALID;
}
rad_assert(handler->stage == AUTHENTICATE);
handler->eap_type = eaptype->type;
if (eaptype_call(inst->types[eaptype->type],
handler) == 0) {
DEBUG2(" rlm_eap: Handler failed in EAP/%s",
eaptype_name);
return EAP_INVALID;
}
break;
}
return EAP_OK;
}
/*
* EAP packet format to be sent over the wire
*
* i.e. code+id+length+data where data = null/type+typedata
* based on code.
*/
static int eap_wireformat(EAP_PACKET *reply)
{
eap_packet_t *hdr;
uint16_t total_length = 0;
if (reply == NULL) return EAP_INVALID;
total_length = EAP_HEADER_LEN;
if (reply->code < 3) {
total_length += 1/*EAPtype*/;
if (reply->type.data && reply->type.length > 0) {
total_length += reply->type.length;
}
}
reply->packet = (unsigned char *)malloc(total_length);
hdr = (eap_packet_t *)reply->packet;
if (!hdr) {
radlog(L_ERR, "rlm_eap: out of memory");
return EAP_INVALID;
}
hdr->code = (reply->code & 0xFF);
hdr->id = (reply->id & 0xFF);
total_length = htons(total_length);
memcpy(hdr->length, &total_length, sizeof(uint16_t));
/*
* Request and Response packets are special.
*/
if ((reply->code == PW_EAP_REQUEST) ||
(reply->code == PW_EAP_RESPONSE)) {
hdr->data[0] = (reply->type.type & 0xFF);
/*
* 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
*/
if (reply->type.data && reply->type.length > 0) {
memcpy(&hdr->data[1], reply->type.data, reply->type.length);
free(reply->type.data);
reply->type.data = reply->packet + EAP_HEADER_LEN + 1/*EAPtype*/;
}
}
return EAP_VALID;
}
/*
* 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(EAP_HANDLER *handler)
{
uint16_t eap_len, len;
VALUE_PAIR *eap_msg;
VALUE_PAIR *vp;
eap_packet_t *eap_packet;
unsigned char *ptr;
REQUEST *request = handler->request;
EAP_DS *eap_ds = handler->eap_ds;
EAP_PACKET *reply = eap_ds->request;
int rcode;
/*
* The Id for the EAP packet to the NAS wasn't set.
* Do so now.
*
* LEAP requires the Id to be incremented on EAP-Success
* in Stage 4, so that we can carry on the conversation
* where the client asks us to authenticate ourselves
* in stage 5.
*/
if (!eap_ds->set_request_id) {
/*
* 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.
*/
reply->id = handler->eap_ds->response->id;
switch (reply->code) {
/*
* The Id is a simple "ack" for success
* and failure.
*
* RFC 3748 section 4.2 says
*
* ... The Identifier field MUST match
* the Identifier field of the Response
* packet that it is sent in response
* to.
*/
case PW_EAP_SUCCESS:
case PW_EAP_FAILURE:
break;
/*
* We've sent a response to their
* request, the Id is incremented.
*/
default:
++reply->id;
}
} else {
DEBUG2(" rlm_eap: Underlying EAP-Type set EAP ID to %d",
reply->id);
}
/*
* For Request & Response packets, set the EAP sub-type,
* if the EAP sub-module didn't already set it.
*
* This allows the TLS module to be "morphic", and means
* that the TTLS and PEAP modules can call it to do most
* of their dirty work.
*/
if (((eap_ds->request->code == PW_EAP_REQUEST) ||
(eap_ds->request->code == PW_EAP_RESPONSE)) &&
(eap_ds->request->type.type == 0)) {
rad_assert(handler->eap_type >= PW_EAP_MD5);
rad_assert(handler->eap_type <= PW_EAP_MAX_TYPES);
eap_ds->request->type.type = handler->eap_type;
}
if (eap_wireformat(reply) == EAP_INVALID) {
return RLM_MODULE_INVALID;
}
eap_packet = (eap_packet_t *)reply->packet;
memcpy(&eap_len, &(eap_packet->length), sizeof(uint16_t));
len = eap_len = ntohs(eap_len);
ptr = (unsigned char *)eap_packet;
do {
if (eap_len > 253) {
len = 253;
eap_len -= 253;
} else {
len = eap_len;
eap_len = 0;
}
/*
* create a value pair & append it to the request reply list
* This memory gets freed up when request is freed up
*/
eap_msg = paircreate(PW_EAP_MESSAGE, PW_TYPE_OCTETS);
memcpy(eap_msg->strvalue, ptr, len);
eap_msg->length = len;
pairadd(&(request->reply->vps), eap_msg);
ptr += len;
eap_msg = NULL;
} while (eap_len);
/*
* EAP-Message is always associated with
* Message-Authenticator but not vice-versa.
*
* Don't add a Message-Authenticator if it's already
* there.
*/
vp = pairfind(request->reply->vps, PW_MESSAGE_AUTHENTICATOR);
if (!vp) {
vp = paircreate(PW_MESSAGE_AUTHENTICATOR, PW_TYPE_OCTETS);
memset(vp->strvalue, 0, AUTH_VECTOR_LEN);
vp->length = AUTH_VECTOR_LEN;
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:
request->reply->code = PW_AUTHENTICATION_ACK;
rcode = RLM_MODULE_HANDLED; /* leap weirdness */
break;
case PW_EAP_SUCCESS:
request->reply->code = PW_AUTHENTICATION_ACK;
rcode = RLM_MODULE_OK;
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:
/*
* When we're pulling MS-CHAPv2 out of EAP-MS-CHAPv2,
* we do so WITHOUT setting a reply code, as the
* request is being proxied.
*/
if (request->options & RAD_REQUEST_OPTION_PROXY_EAP) {
return RLM_MODULE_HANDLED;
}
/* Should never enter here */
radlog(L_ERR, "rlm_eap: reply code %d is unknown, Rejecting the request.", reply->code);
request->reply->code = PW_AUTHENTICATION_REJECT;
reply->code = PW_EAP_FAILURE;
rcode = RLM_MODULE_REJECT;
break;
}
return rcode;
}
/*
* Radius criteria, EAP-Message is invalid without Message-Authenticator
* For EAP_START, send Access-Challenge with EAP Identity request.
*/
int eap_start(rlm_eap_t *inst, REQUEST *request)
{
VALUE_PAIR *vp, *proxy;
VALUE_PAIR *eap_msg;
eap_msg = pairfind(request->packet->vps, PW_EAP_MESSAGE);
if (eap_msg == NULL) {
DEBUG2(" rlm_eap: No EAP-Message, not doing EAP");
return EAP_NOOP;
}
/*
* Look for EAP-Type = None (FreeRADIUS specific attribute)
* this allows you to NOT do EAP for some users.
*/
vp = pairfind(request->packet->vps, PW_EAP_TYPE);
if (vp && vp->lvalue == 0) {
DEBUG2(" rlm_eap: Found EAP-Message, but EAP-Type = None, so we're not doing EAP.");
return EAP_NOOP;
}
/*
* http://www.freeradius.org/rfc/rfc2869.html#EAP-Message
*
* Checks for Message-Authenticator are handled by rad_recv().
*/
/*
* Check for a Proxy-To-Realm. Don't get excited over LOCAL
* realms (sigh).
*/
proxy = pairfind(request->config_items, PW_PROXY_TO_REALM);
if (proxy) {
REALM *realm;
/*
* If it's a LOCAL realm, then we're not proxying
* to it.
*/
realm = realm_find(proxy->strvalue, 0);
if (realm && (realm->ipaddr == htonl(INADDR_NONE))) {
proxy = NULL;
}
}
/*
* Check the length before de-referencing the contents.
*
* Lengths of zero are required by the RFC for EAP-Start,
* but we've never seen them in practice.
*
* Lengths of two are what we see in practice as
* EAP-Starts.
*/
if ((eap_msg->length == 0) || (eap_msg->length == 2)) {
EAP_DS *eap_ds;
EAP_HANDLER handler;
/*
* It's a valid EAP-Start, but the request
* was marked as being proxied. So we don't
* do EAP, as the home server will do it.
*/
if (proxy) {
do_proxy:
DEBUG2(" rlm_eap: Request is supposed to be proxied to Realm %s. Not doing EAP.", proxy->strvalue);
return EAP_NOOP;
}
DEBUG2(" rlm_eap: Got EAP_START message");
if ((eap_ds = eap_ds_alloc()) == NULL) {
DEBUG2(" rlm_eap: EAP Start failed in allocation");
return EAP_FAIL;
}
/*
* It's an EAP-Start packet. Tell them to stop wasting
* our time, and give us an EAP-Identity packet.
*
* Hmm... we should probably check the contents of the
* EAP-Start packet for something...
*/
eap_ds->request->code = PW_EAP_REQUEST;
eap_ds->request->type.type = PW_EAP_IDENTITY;
/*
* We don't have a handler, but eap_compose needs one,
* (for various reasons), so we fake it out here.
*/
memset(&handler, 0, sizeof(handler));
handler.request = request;
handler.eap_ds = eap_ds;
eap_compose(&handler);
eap_ds_free(&eap_ds);
return EAP_FOUND;
} /* end of handling EAP-Start */
/*
* The EAP packet header is 4 bytes, plus one byte of
* EAP sub-type. Short packets are discarded, unless
* we're proxying.
*/
if (eap_msg->length < (EAP_HEADER_LEN + 1)) {
if (proxy) goto do_proxy;
DEBUG2(" rlm_eap: Ignoring EAP-Message which is too short to be meaningful.");
return EAP_FAIL;
}
/*
* Create an EAP-Type containing the EAP-type
* from the packet.
*/
vp = paircreate(PW_EAP_TYPE, PW_TYPE_INTEGER);
if (vp) {
vp->lvalue = eap_msg->strvalue[4];
pairadd(&(request->packet->vps), vp);
}
/*
* If the request was marked to be proxied, do it now.
* This is done after checking for a valid length
* (which may not be good), and after adding the EAP-Type
* attribute. This lets other modules selectively cancel
* proxying based on EAP-Type.
*/
if (proxy) goto do_proxy;
/*
* From now on, we're supposed to be handling the
* EAP packet. We better understand it...
*/
/*
* We're allowed only a few codes. Request, Response,
* Success, or Failure.
*/
if ((eap_msg->strvalue[0] == 0) ||
(eap_msg->strvalue[0] > PW_EAP_MAX_CODES)) {
DEBUG2(" rlm_eap: Unknown EAP packet");
} else {
DEBUG2(" rlm_eap: EAP packet type %s id %d length %d",
eap_codes[eap_msg->strvalue[0]],
eap_msg->strvalue[1],
eap_msg->length);
}
/*
* We handle request and responses. The only other defined
* codes are success and fail. The client SHOULD NOT be
* sending success/fail packets to us, as it doesn't make
* sense.
*/
if ((eap_msg->strvalue[0] != PW_EAP_REQUEST) &&
(eap_msg->strvalue[0] != PW_EAP_RESPONSE)) {
DEBUG2(" rlm_eap: Ignoring EAP packet which we don't know how to handle.");
return EAP_FAIL;
}
/*
* We've been told to ignore unknown EAP types, AND it's
* an unknown type. Return "NOOP", which will cause the
* eap_authorize() to return NOOP.
*
* EAP-Identity, Notification, and NAK are all handled
* internally, so they never have handlers.
*/
if ((eap_msg->strvalue[4] >= PW_EAP_MD5) &&
inst->ignore_unknown_eap_types &&
((eap_msg->strvalue[4] == 0) ||
(eap_msg->strvalue[4] > PW_EAP_MAX_TYPES) ||
(inst->types[eap_msg->strvalue[4]] == NULL))) {
DEBUG2(" rlm_eap: Ignoring Unknown EAP type");
return EAP_NOOP;
}
/*
* They're NAKing the EAP type we wanted to use, and
* asking for one which we don't support.
*
* NAK is code + id + length1 + length + NAK
* + requested EAP type(s).
*
* We know at this point that we can't handle the
* request. We could either return an EAP-Fail here, but
* it's not too critical.
*
* By returning "noop", we can ensure that authorize()
* returns NOOP, and another module may choose to proxy
* the request.
*/
if ((eap_msg->strvalue[4] == PW_EAP_NAK) &&
(eap_msg->length >= (EAP_HEADER_LEN + 2)) &&
inst->ignore_unknown_eap_types &&
((eap_msg->strvalue[5] == 0) ||
(eap_msg->strvalue[5] > PW_EAP_MAX_TYPES) ||
(inst->types[eap_msg->strvalue[5]] == NULL))) {
DEBUG2(" rlm_eap: Ignoring NAK with request for unknown EAP type");
return EAP_NOOP;
}
/*
* Later EAP messages are longer than the 'start'
* message, so if everything is OK, this function returns
* 'no start found', so that the rest of the EAP code can
* use the State attribute to match this EAP-Message to
* an ongoing conversation.
*/
DEBUG2(" rlm_eap: No EAP Start, assuming it's an on-going EAP conversation");
return EAP_NOTFOUND;
}
/*
* compose EAP FAILURE packet in EAP-Message
*/
void eap_fail(EAP_HANDLER *handler)
{
handler->eap_ds->request->code = PW_EAP_FAILURE;
eap_compose(handler);
}
/*
* compose EAP SUCCESS packet in EAP-Message
*/
void eap_success(EAP_HANDLER *handler)
{
handler->eap_ds->request->code = PW_EAP_SUCCESS;
eap_compose(handler);
}
/*
* Basic EAP packet verfications & validations
*/
static int eap_validation(eap_packet_t *eap_packet)
{
uint16_t len;
memcpy(&len, eap_packet->length, sizeof(uint16_t));
len = ntohs(len);
/*
* High level EAP packet checks
*/
if ((len <= EAP_HEADER_LEN) ||
((eap_packet->code != PW_EAP_RESPONSE) &&
(eap_packet->code != PW_EAP_REQUEST)) ||
(eap_packet->data[0] <= 0) ||
(eap_packet->data[0] > PW_EAP_MAX_TYPES)) {
radlog(L_AUTH, "rlm_eap: Incorrect EAP Message, "
"Ignoring the packet");
return EAP_INVALID;
}
/* we don't expect notification, but we send it */
if (eap_packet->data[0] == PW_EAP_NOTIFICATION) {
radlog(L_AUTH, "rlm_eap: Got NOTIFICATION, "
"Ignoring the packet");
return EAP_INVALID;
}
return EAP_VALID;
}
/*
* Get the user Identity only from EAP-Identity packets
*/
static char *eap_identity(eap_packet_t *eap_packet)
{
int size;
uint16_t len;
char *identity;
if ((eap_packet == NULL) ||
(eap_packet->code != PW_EAP_RESPONSE) ||
(eap_packet->data[0] != PW_EAP_IDENTITY)) {
return NULL;
}
memcpy(&len, eap_packet->length, sizeof(uint16_t));
len = ntohs(len);
if ((len <= 5) || (eap_packet->data[1] == 0x00)) {
radlog(L_ERR, "rlm_eap: UserIdentity Unknown ");
return NULL;
}
size = len - 5;
identity = (char *)malloc(size + 1);
if (identity == NULL) {
radlog(L_ERR, "rlm_eap: out of memory");
return NULL;
}
memcpy(identity, &eap_packet->data[1], size);
identity[size] = '\0';
return identity;
}
/*
* Create our Request-Response data structure with the eap packet
*/
static EAP_DS *eap_buildds(eap_packet_t **eap_packet_p)
{
EAP_DS *eap_ds = NULL;
eap_packet_t *eap_packet = *eap_packet_p;
int typelen;
uint16_t len;
if ((eap_ds = eap_ds_alloc()) == NULL) {
return NULL;
}
eap_ds->response->packet = (unsigned char *)eap_packet;
eap_ds->response->code = eap_packet->code;
eap_ds->response->id = eap_packet->id;
eap_ds->response->type.type = eap_packet->data[0];
memcpy(&len, eap_packet->length, sizeof(uint16_t));
len = ntohs(len);
eap_ds->response->length = len;
/*
* We've eaten the eap packet into the eap_ds.
*/
*eap_packet_p = NULL;
/*
* First 5 bytes in eap, are code + id + length(2) + type.
*
* The rest is type-specific data. We skip type while
* getting typedata from data.
*/
typelen = len - 5/*code + id + length + type */;
if (typelen > 0) {
/*
* Since the packet contains the complete
* eap_packet, typedata will be a ptr in packet
* to its typedata
*/
eap_ds->response->type.data = eap_ds->response->packet + 5/*code+id+length+type*/;
eap_ds->response->type.length = typelen;
} else {
eap_ds->response->type.length = 0;
eap_ds->response->type.data = NULL;
}
return eap_ds;
}
/*
* If identity response then create a fresh handler & fill the identity
* else handler MUST be in our list, get that.
* This handler creation cannot fail
*
* username contains REQUEST->username which might have been stripped.
* identity contains the one sent in EAP-Identity response
*/
EAP_HANDLER *eap_handler(rlm_eap_t *inst, eap_packet_t **eap_packet_p,
REQUEST *request)
{
EAP_HANDLER *handler = NULL;
eap_packet_t *eap_packet = *eap_packet_p;
VALUE_PAIR *vp;
/*
* Ensure it's a valid EAP-Request, or EAP-Response.
*/
if (eap_validation(eap_packet) == EAP_INVALID) {
free(*eap_packet_p);
*eap_packet_p = NULL;
return NULL;
}
/*
* EAP_HANDLER MUST be found in the list if it is not
* EAP-Identity response
*/
if (eap_packet->data[0] != PW_EAP_IDENTITY) {
handler = eaplist_find(inst, request, eap_packet);
if (handler == NULL) {
/* Either send EAP_Identity or EAP-Fail */
radlog(L_ERR, "rlm_eap: Either EAP-request timed out OR"
" EAP-response to an unknown EAP-request");
free(*eap_packet_p);
*eap_packet_p = NULL;
return NULL;
}
/*
* Even more paranoia. Without this, some weird
* clients could do crazy things.
*
* It's ok to send EAP sub-type NAK in response
* to a request for a particular type, but it's NOT
* OK to blindly return data for another type.
*/
if ((eap_packet->data[0] != PW_EAP_NAK) &&
(eap_packet->data[0] != handler->eap_type)) {
radlog(L_ERR, "rlm_eap: Response appears to match, but EAP type is wrong.");
free(*eap_packet_p);
*eap_packet_p = NULL;
return NULL;
}
vp = pairfind(request->packet->vps, PW_USER_NAME);
if (!vp) {
/*
* NAS did not set the User-Name
* attribute, so we set it here and
* prepend it to the beginning of the
* request vps so that autz's work
* correctly
*/
radlog(L_INFO, "rlm_eap: Broken NAS did not set User-Name, setting from EAP Identity");
vp = pairmake("User-Name", handler->identity, T_OP_EQ);
if (vp == NULL) {
radlog(L_ERR, "rlm_eap: out of memory");
free(*eap_packet_p);
*eap_packet_p = NULL;
return NULL;
}
vp->next = request->packet->vps;
request->packet->vps = vp;
} else {
/*
* A little more paranoia. If the NAS
* *did* set the User-Name, and it doesn't
* match the identity, (i.e. If they
* change their User-Name part way through
* the EAP transaction), then reject the
* request as the NAS is doing something
* funny.
*/
if (strncmp(handler->identity, vp->strvalue,
MAX_STRING_LEN) != 0) {
radlog(L_ERR, "rlm_eap: Identity does not match User-Name. Authentication failed.");
free(*eap_packet_p);
*eap_packet_p = NULL;
return NULL;
}
}
} else { /* packet was EAP identity */
handler = eap_handler_alloc();
if (handler == NULL) {
radlog(L_ERR, "rlm_eap: out of memory");
free(*eap_packet_p);
*eap_packet_p = NULL;
return NULL;
}
/*
* All fields in the handler are set to zero.
*/
handler->identity = eap_identity(eap_packet);
if (handler->identity == NULL) {
radlog(L_ERR, "rlm_eap: Identity Unknown, authentication failed");
free(*eap_packet_p);
*eap_packet_p = NULL;
eap_handler_free(handler);
return NULL;
}
vp = pairfind(request->packet->vps, PW_USER_NAME);
if (!vp) {
/*
* NAS did not set the User-Name
* attribute, so we set it here and
* prepend it to the beginning of the
* request vps so that autz's work
* correctly
*/
radlog(L_INFO, "rlm_eap: WARNING NAS did not set User-Name. Setting it locally from EAP Identity");
vp = pairmake("User-Name", handler->identity, T_OP_EQ);
if (vp == NULL) {
radlog(L_ERR, "rlm_eap: out of memory");
free(*eap_packet_p);
*eap_packet_p = NULL;
eap_handler_free(handler);
return NULL;
}
vp->next = request->packet->vps;
request->packet->vps = vp;
} else {
/*
* Paranoia. If the NAS *did* set the
* User-Name, and it doesn't match the
* identity, the NAS is doing something
* funny, so reject the request.
*/
if (strncmp(handler->identity, vp->strvalue,
MAX_STRING_LEN) != 0) {
radlog(L_ERR, "rlm_eap: Identity does not match User-Name, setting from EAP Identity.");
free(*eap_packet_p);
*eap_packet_p = NULL;
eap_handler_free(handler);
return NULL;
}
}
}
handler->eap_ds = eap_buildds(eap_packet_p);
if (handler->eap_ds == NULL) {
free(*eap_packet_p);
*eap_packet_p = NULL;
eap_handler_free(handler);
return NULL;
}
handler->timestamp = request->timestamp;
handler->request = request; /* LEAP needs this */
return handler;
}
syntax highlighted by Code2HTML, v. 0.9.1