*
* 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
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
*
- * Copyright 2000-2003 The FreeRADIUS server project
+ * Copyright 2000-2003,2006 The FreeRADIUS server project
* Copyright 2001 hereUare Communications, Inc. <raghud@hereuare.com>
* Copyright 2003 Alan DeKok <aland@freeradius.org>
*/
* +-+-+-+-+
*
*
- * EAP Request and Response Packet Format
+ * 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
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-
*
*
- * EAP Success and Failure Packet Format
+ * 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
*
*/
-#include "rlm_eap.h"
+#include <freeradius-devel/ident.h>
+#include <freeradius-devel/modpriv.h>
+RCSID("$Id$")
-static const char rcsid[] = "$Id$";
+#include "rlm_eap.h"
static const char *eap_codes[] = {
"", /* 0 is invalid */
"failure"
};
-static const char *eap_types[] = {
- "",
- "identity",
- "notification",
- "nak", /* NAK */
- "md5",
- "otp",
- "gtc",
- "7",
- "8",
- "9",
- "10",
- "11",
- "12",
- "tls", /* 13 */
- "14",
- "15",
- "16",
- "leap", /* 17 */
- "18",
- "19",
- "20",
- "ttls", /* 21 */
- "22",
- "23",
- "24",
- "peap", /* 25 */
- "26",
- "27",
- "28",
- "mschapv2"
-};
-
-/*
- * Return an EAP-Type for a particular name.
- */
-int eaptype_name2type(const char *name)
-{
- int i;
-
- 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.
+ * Get all the supported EAP-types from config file.
*/
int eaptype_load(EAP_TYPES **type, int eap_type, CONF_SECTION *cs)
{
char buffer[64];
- lt_dlhandle handle;
+ char namebuf[64];
+ const char *eaptype_name;
EAP_TYPES *node;
- snprintf(buffer, sizeof(buffer), "rlm_eap_%s", eap_types[eap_type]);
-
- /* Link the loaded EAP-Type */
- handle = lt_dlopenext(buffer);
- if (handle == NULL) {
- radlog(L_ERR, "rlm_eap: Failed to link EAP-Type/%s: %s",
- eap_types[eap_type], lt_dlerror());
- return -1;
- }
+ eaptype_name = eaptype_type2name(eap_type, namebuf, sizeof(namebuf));
+ snprintf(buffer, sizeof(buffer), "rlm_eap_%s", eaptype_name);
/* Make room for the EAP-Type */
node = (EAP_TYPES *)malloc(sizeof(EAP_TYPES));
memset(node, 0, sizeof(*node));
/* fill in the structure */
- node->handle = handle;
node->cs = cs;
- node->typename = eap_types[eap_type];
+
+ /*
+ * 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;
-
+
+#ifdef WITHOUT_LIBLTDL
+#ifdef WITH_DLOPEN
+#include <dlfcn.h>
+
+#ifdef RTLD_SELF
+ node->type = (EAP_TYPE *)lt_dlsym(RTLD_SELF, buffer);
+ if (node->type) goto open_self;
+#endif
+#endif
+#endif
+
+ /* Link the loaded EAP-Type */
+ node->handle = lt_dlopenext(buffer);
+ if (node->handle == NULL) {
+ free(node);
+ radlog(L_ERR, "rlm_eap: Failed to link EAP-Type/%s: %s",
+ eaptype_name, lt_dlerror());
+ return -1;
+ }
+
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, eap_types[eap_type], lt_dlerror());
+ 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)) {
+
+#if defined(WITHOUT_LIBLTDL) && defined (WITH_DLOPEN) && defined(RTLD_SELF)
+open_self:
+#endif
+ cf_log_module(cs, "Linked to sub-module %s", buffer);
+
+ cf_log_module(cs, "Instantiating eap-%s", eaptype_name);
+
+ if ((node->type->attach) &&
+ ((node->type->attach)(node->cs, &(node->type_data)) < 0)) {
radlog(L_ERR, "rlm_eap: Failed to initialize type %s",
- eap_types[eap_type]);
+ eaptype_name);
lt_dlclose(node->handle);
free(node);
return -1;
}
- DEBUG("rlm_eap: Loaded and initialized type %s", eap_types[eap_type]);
*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;
+ REQUEST *request = handler->request;
+ const char *module = request->module;
- DEBUG2(" rlm_eap: processing type %s", atype->typename);
+ RDEBUG2("processing type %s", atype->typename);
+ request->module = atype->typename;
rad_assert(atype != NULL);
default:
/* Should never enter here */
- radlog(L_DBG, "rlm_eap: Invalid operation on eap_type");
+ RDEBUG("Internal sanity check failed on eap_type");
rcode = 0;
break;
}
+ request->module = module;
return rcode;
}
/*
* Based on TYPE, call the appropriate EAP-type handler
- * Default to the configured EAP-Type
- * for all Unsupported EAP-Types
+ * Default to the configured EAP-Type
+ * for all Unsupported EAP-Types
*/
int eaptype_select(rlm_eap_t *inst, EAP_HANDLER *handler)
{
+ size_t i;
+ unsigned int default_eap_type = inst->default_eap_type;
eaptype_t *eaptype;
- int default_eap_type = inst->default_eap_type;
+ VALUE_PAIR *vp;
+ char namebuf[64];
+ const char *eaptype_name;
+ REQUEST *request = handler->request;
eaptype = &handler->eap_ds->response->type;
*/
if ((eaptype->type == 0) ||
(eaptype->type > PW_EAP_MAX_TYPES)) {
- DEBUG2(" rlm_eap: Asked to select bad type");
+ RDEBUG2("Asked to select bad type");
+ return EAP_INVALID;
+ }
+
+ /*
+ * Multiple levels of nesting are invalid.
+ */
+ if (handler->request->parent && handler->request->parent->parent) {
+ RDEBUG2("Multiple levels of TLS nesting is invalid.");
return EAP_INVALID;
}
*/
switch(eaptype->type) {
case PW_EAP_IDENTITY:
- {
- VALUE_PAIR *vp;
+ RDEBUG2("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;
+ /*
+ * Allow per-user configuration of EAP types.
+ */
+ vp = pairfind(handler->request->config_items,
+ PW_EAP_TYPE, 0);
+ if (vp) default_eap_type = vp->vp_integer;
- 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 %d",
- default_eap_type);
- return EAP_INVALID;
- }
-
- handler->stage = INITIATE;
- handler->eap_type = default_eap_type;
+ 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)) {
+ RDEBUG2("No such EAP type %s",
+ eaptype_type2name(default_eap_type,
+ namebuf, sizeof(namebuf)));
+ return EAP_INVALID;
+ }
- /*
- * 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;
- }
+ 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 ((default_eap_type == PW_EAP_TNC) &&
+ !handler->request->parent) {
+ RDEBUG2("ERROR: EAP-TNC must be run inside of a TLS method.");
+ return EAP_INVALID;
+ }
- if (eaptype_call(inst->types[default_eap_type],
- handler) == 0) {
- DEBUG2(" rlm_eap: Default EAP type %s failed in initiate", eap_types[default_eap_type]);
- return EAP_INVALID;
- }
+ if (eaptype_call(inst->types[default_eap_type],
+ handler) == 0) {
+ RDEBUG2("Default EAP type %s failed in initiate",
+ eaptype_type2name(default_eap_type,
+ namebuf, sizeof(namebuf)));
+ return EAP_INVALID;
}
break;
case PW_EAP_NAK:
/*
- * The one byte of NAK data is the preferred EAP type
- * of the client.
+ * 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");
+ RDEBUG2("EAP NAK");
/*
* Delete old data, if necessary.
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.");
+ RDEBUG2("Empty NAK packet, cannot decide what EAP type the client wants.");
return EAP_INVALID;
}
- 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;
+ /*
+ * Pick one type out of the one they asked for,
+ * as they may have asked for many.
+ */
+ default_eap_type = 0;
+ vp = pairfind(handler->request->config_items,
+ PW_EAP_TYPE, 0);
+ for (i = 0; i < eaptype->length; i++) {
+ /*
+ * It is invalid to request identity,
+ * notification & nak in nak.
+ *
+ * Type 0 is valid, and means there are no
+ * common choices.
+ */
+ if (eaptype->data[i] < PW_EAP_MD5) {
+ RDEBUG2("NAK asked for bad type %d",
+ eaptype->data[i]);
+ return EAP_INVALID;
+ }
+
+ if ((eaptype->data[i] > PW_EAP_MAX_TYPES) ||
+ !inst->types[eaptype->data[i]]) {
+ DICT_VALUE *dv;
+
+ dv = dict_valbyattr(PW_EAP_TYPE, 0, eaptype->data[i]);
+ if (dv) {
+ RDEBUG2("NAK asked for unsupported type %s",
+ dv->name);
+ } else {
+ RDEBUG2("NAK asked for unsupported type %d",
+ eaptype->data[i]);
+ }
+ continue;
+ }
+
+ eaptype_name = eaptype_type2name(eaptype->data[i],
+ namebuf,
+ sizeof(namebuf));
+
+ /*
+ * Prevent a firestorm if the client is confused.
+ */
+ if (handler->eap_type == eaptype->data[i]) {
+ RDEBUG2("ERROR! Our request for %s was NAK'd with a request for %s. Skipping the requested type.",
+ eaptype_name, eaptype_name);
+ continue;
+ }
+
+ /*
+ * Enforce per-user configuration of EAP
+ * types.
+ */
+ if (vp && (vp->vp_integer != eaptype->data[i])) {
+ char mynamebuf[64];
+ RDEBUG2("Client wants %s, while we require %s. Skipping the requested type.",
+ eaptype_name,
+ eaptype_type2name(vp->vp_integer,
+ mynamebuf,
+ sizeof(mynamebuf)));
+ continue;
+ }
+
+ default_eap_type = eaptype->data[i];
+ break;
}
- default_eap_type = eaptype->data[0];
- DEBUG2(" rlm_eap: EAP-NAK asked for EAP-Type/%s",
- eap_types[default_eap_type]);
+ /*
+ * We probably want to return 'fail' here...
+ */
+ if (!default_eap_type) {
+ RDEBUG2("No common EAP types found.");
+ return EAP_INVALID;
+ }
+ eaptype_name = eaptype_type2name(default_eap_type,
+ namebuf, sizeof(namebuf));
+ RDEBUG2("EAP-NAK asked for EAP-Type/%s",
+ eaptype_name);
goto do_initiate;
break;
* Key off of the configured sub-modules.
*/
default:
- DEBUG2(" rlm_eap: EAP_TYPE - %s",
- eap_types[eaptype->type]);
-
+ eaptype_name = eaptype_type2name(eaptype->type,
+ namebuf,
+ sizeof(namebuf));
+ RDEBUG2("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",
+ RDEBUG2("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 type %d",
- eaptype->type);
+ RDEBUG2("Handler failed in EAP/%s",
+ eaptype_name);
return EAP_INVALID;
}
break;
return EAP_OK;
}
-/*
- * Handles multiple EAP-Message attrs
- * ie concatenates all to get the complete EAP packet
- *
- * NOTE: Sometimes Framed-MTU might contain the length of EAP-Message,
- * refer fragmentation in rfc2869.
- */
-eap_packet_t *eap_attribute(VALUE_PAIR *vps)
-{
- VALUE_PAIR *first, *vp;
- eap_packet_t *eap_packet;
- unsigned char *ptr;
- uint16_t len;
- int total_len;
-
- /*
- * Get only EAP-Message attribute list
- */
- first = pairfind(vps, PW_EAP_MESSAGE);
- if (first == NULL) {
- radlog(L_ERR, "rlm_eap: EAP-Message not found");
- return NULL;
- }
-
- /*
- * Sanity check the length before doing anything.
- */
- if (first->length < 4) {
- radlog(L_ERR, "rlm_eap: EAP packet is too short.");
- return NULL;
- }
-
- /*
- * Get the Actual length from the EAP packet
- * First EAP-Message contains the EAP packet header
- */
- memcpy(&len, first->strvalue + 2, sizeof(len));
- len = ntohs(len);
-
- /*
- * Take out even more weird things.
- */
- if (len < 4) {
- radlog(L_ERR, "rlm_eap: EAP packet has invalid length.");
- return NULL;
- }
-
- /*
- * Sanity check the length, BEFORE malloc'ing memory.
- */
- total_len = 0;
- for (vp = first; vp; vp = pairfind(vp->next, PW_EAP_MESSAGE)) {
- total_len += vp->length;
-
- if (total_len > len) {
- radlog(L_ERR, "rlm_eap: Malformed EAP packet. Length in packet header does not match actual length");
- return NULL;
- }
- }
-
- /*
- * If the length is SMALLER, die, too.
- */
- if (total_len < len) {
- radlog(L_ERR, "rlm_eap: Malformed EAP packet. Length in packet header does not match actual length");
- return NULL;
- }
-
- /*
- * Now that we know the lengths are OK, allocate memory.
- */
- eap_packet = (eap_packet_t *) malloc(len);
- if (eap_packet == NULL) {
- radlog(L_ERR, "rlm_eap: out of memory");
- return NULL;
- }
-
- /*
- * Copy the data from EAP-Message's over to out EAP packet.
- */
- ptr = (unsigned char *)eap_packet;
-
- /* RADIUS ensures order of attrs, so just concatenate all */
- for (vp = first; vp; vp = pairfind(vp->next, PW_EAP_MESSAGE)) {
- memcpy(ptr, vp->strvalue, vp->length);
- ptr += vp->length;
- }
-
- return eap_packet;
-}
-
-/*
- * 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
*/
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;
* 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.
++reply->id;
}
} else {
- DEBUG2(" rlm_eap: Underlying EAP-Type set EAP ID to %d",
+ RDEBUG2("Underlying EAP-Type set EAP ID to %d",
reply->id);
}
eap_ds->request->type.type = handler->eap_type;
}
-
+ /*
+ * FIXME: We malloc memory for the eap packet, and then
+ * immediately copy that data into VALUE_PAIRs. This
+ * could be done more efficiently...
+ */
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);
+ vp = eap_packet2vp(eap_packet);
+ if (!vp) return RLM_MODULE_INVALID;
+ pairadd(&(request->reply->vps), vp);
/*
* EAP-Message is always associated with
* Don't add a Message-Authenticator if it's already
* there.
*/
- vp = pairfind(request->reply->vps, PW_MESSAGE_AUTHENTICATOR);
+ vp = pairfind(request->reply->vps, PW_MESSAGE_AUTHENTICATOR, 0);
if (!vp) {
- vp = paircreate(PW_MESSAGE_AUTHENTICATOR, PW_TYPE_OCTETS);
- memset(vp->strvalue, 0, AUTH_VECTOR_LEN);
+ vp = paircreate(PW_MESSAGE_AUTHENTICATOR, 0, PW_TYPE_OCTETS);
+ memset(vp->vp_octets, 0, AUTH_VECTOR_LEN);
vp->length = AUTH_VECTOR_LEN;
pairadd(&(request->reply->vps), vp);
}
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;
}
*/
int eap_start(rlm_eap_t *inst, REQUEST *request)
{
- VALUE_PAIR *vp;
+ VALUE_PAIR *vp, *proxy;
VALUE_PAIR *eap_msg;
- EAP_DS *eap_ds;
- EAP_HANDLER handler;
- eap_msg = pairfind(request->packet->vps, PW_EAP_MESSAGE);
+ eap_msg = pairfind(request->packet->vps, PW_EAP_MESSAGE, 0);
if (eap_msg == NULL) {
- DEBUG2(" rlm_eap: No EAP-Message, not doing EAP");
+ RDEBUG2("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.");
+ vp = pairfind(request->packet->vps, PW_EAP_TYPE, 0);
+ if (vp && vp->vp_integer == 0) {
+ RDEBUG2("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
*
- * This is handled by rad_recv().
+ * Checks for Message-Authenticator are handled by rad_recv().
*/
/*
- * We're allowed only a few codes. Request, Response,
- * Success, or Failure.
+ * Check for a Proxy-To-Realm. Don't get excited over LOCAL
+ * realms (sigh).
*/
- 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);
- }
+ proxy = pairfind(request->config_items, PW_PROXY_TO_REALM, 0);
+ if (proxy) {
+ REALM *realm;
- /*
- * If we've been configured to proxy, do nothing.
- *
- * Note that we don't check if the realm is local.
- * We figure that anyone bright enough to add
- * Proxy-To-Realm is bright enough to NOT do so
- * when it's a local realm.
- */
- if (pairfind(request->config_items, PW_PROXY_TO_REALM) != NULL) {
- return EAP_NOOP;
+ /*
+ * If it's a LOCAL realm, then we're not proxying
+ * to it.
+ */
+ realm = realm_find(proxy->vp_strvalue);
+ if (!realm || (realm && (realm->auth_pool == NULL))) {
+ proxy = NULL;
+ }
}
/*
- * Not a start message. Don't start anything.
+ * 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.
*
- * Later EAP messages are longer than the 'start' message,
- * so 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.
+ * Lengths of two are what we see in practice as
+ * EAP-Starts.
*/
- if (eap_msg->length != EAP_START) {
- DEBUG2(" rlm_eap: No EAP Start, assuming it's an on-going EAP conversation");
+ if ((eap_msg->length == 0) || (eap_msg->length == 2)) {
+ EAP_DS *eap_ds;
+ EAP_HANDLER handler;
/*
- * Add the 'EAP-Type' attribute to the request,
- * if it's part of an EAP conversation, and the
- * EAP sub-type is in the EAP packet.
+ * 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:
+ RDEBUG2("Request is supposed to be proxied to Realm %s. Not doing EAP.", proxy->vp_strvalue);
+ return EAP_NOOP;
+ }
+
+ RDEBUG2("Got EAP_START message");
+ if ((eap_ds = eap_ds_alloc()) == NULL) {
+ RDEBUG2("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.
*
- * Store the EAP type in the request, so modules
- * outside of EAP can check & use it.
+ * Hmm... we should probably check the contents of the
+ * EAP-Start packet for something...
*/
- if (((eap_msg->strvalue[0] == PW_EAP_REQUEST) ||
- (eap_msg->strvalue[0] == PW_EAP_RESPONSE)) &&
- (eap_msg->length >= (EAP_HEADER_LEN + 1))) {
- /*
- * Create an EAP-Type of the type which
- * was NAK'd, or of the type in the packet.
- */
- vp = paircreate(PW_EAP_TYPE, PW_TYPE_INTEGER);
- if (vp) {
- vp->lvalue = eap_msg->strvalue[4];
- pairadd(&(request->packet->vps), vp);
- }
-
- /*
- * 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))) {
- return EAP_NOOP;
- }
+ eap_ds->request->code = PW_EAP_REQUEST;
+ eap_ds->request->type.type = PW_EAP_IDENTITY;
- /*
- * 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.
- *
- * 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))) {
- return EAP_NOOP;
- }
- } /* else it's not an EAP-Request or EAP-Response */
-
/*
- * No EAP-Start found.
+ * We don't have a handler, but eap_compose needs one,
+ * (for various reasons), so we fake it out here.
*/
- return EAP_NOTFOUND;
+ 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;
+
+ RDEBUG2("Ignoring EAP-Message which is too short to be meaningful.");
+ return EAP_FAIL;
}
- DEBUG2(" rlm_eap: Got EAP_START message");
- if ((eap_ds = eap_ds_alloc()) == NULL) {
- DEBUG2(" rlm_eap: EAP Start failed in allocation");
+ /*
+ * Create an EAP-Type containing the EAP-type
+ * from the packet.
+ */
+ vp = paircreate(PW_EAP_TYPE, 0, PW_TYPE_INTEGER);
+ if (vp) {
+ vp->vp_integer = eap_msg->vp_octets[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->vp_octets[0] == 0) ||
+ (eap_msg->vp_octets[0] > PW_EAP_MAX_CODES)) {
+ RDEBUG2("Unknown EAP packet");
+ } else {
+ RDEBUG2("EAP packet type %s id %d length %d",
+ eap_codes[eap_msg->vp_octets[0]],
+ eap_msg->vp_octets[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->vp_octets[0] != PW_EAP_REQUEST) &&
+ (eap_msg->vp_octets[0] != PW_EAP_RESPONSE)) {
+ RDEBUG2("Ignoring EAP packet which we don't know how to handle.");
return EAP_FAIL;
}
/*
- * It's an EAP-Start packet. Tell them to stop wasting
- * our time, and give us an EAP-Identity packet.
+ * 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.
*
- * Hmm... we should probably check the contents of the
- * EAP-Start packet for something...
+ * EAP-Identity, Notification, and NAK are all handled
+ * internally, so they never have handlers.
*/
- eap_ds->request->code = PW_EAP_REQUEST;
- eap_ds->request->type.type = PW_EAP_IDENTITY;
+ if ((eap_msg->vp_octets[4] >= PW_EAP_MD5) &&
+ inst->ignore_unknown_eap_types &&
+ ((eap_msg->vp_octets[4] == 0) ||
+ (eap_msg->vp_octets[4] > PW_EAP_MAX_TYPES) ||
+ (inst->types[eap_msg->vp_octets[4]] == NULL))) {
+ RDEBUG2(" Ignoring Unknown EAP type");
+ return EAP_NOOP;
+ }
/*
- * We don't have a handler, but eap_compose needs one,
- * (for various reasons), so we fake it out here.
+ * 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.
*/
- memset(&handler, 0, sizeof(handler));
- handler.request = request;
- handler.eap_ds = eap_ds;
+ if ((eap_msg->vp_octets[4] == PW_EAP_NAK) &&
+ (eap_msg->length >= (EAP_HEADER_LEN + 2)) &&
+ inst->ignore_unknown_eap_types &&
+ ((eap_msg->vp_octets[5] == 0) ||
+ (eap_msg->vp_octets[5] > PW_EAP_MAX_TYPES) ||
+ (inst->types[eap_msg->vp_octets[5]] == NULL))) {
+ RDEBUG2("Ignoring NAK with request for unknown EAP type");
+ return EAP_NOOP;
+ }
- eap_compose(&handler);
+ if ((eap_msg->vp_octets[4] == PW_EAP_TTLS) ||
+ (eap_msg->vp_octets[4] == PW_EAP_PEAP)) {
+ RDEBUG2("Continuing tunnel setup.");
+ return EAP_OK;
+ }
- eap_ds_free(&eap_ds);
- return EAP_FOUND;
+ /*
+ * 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.
+ */
+ RDEBUG2("No EAP Start, assuming it's an on-going EAP conversation");
+
+ return EAP_NOTFOUND;
}
/*
*/
void eap_fail(EAP_HANDLER *handler)
{
+ /*
+ * Delete any previous replies.
+ */
+ pairdelete(&handler->request->reply->vps, PW_EAP_MESSAGE, 0);
+ pairdelete(&handler->request->reply->vps, PW_STATE, 0);
+
+ eap_packet_free(&handler->eap_ds->request);
+ handler->eap_ds->request = eap_packet_alloc();
+
handler->eap_ds->request->code = PW_EAP_FAILURE;
eap_compose(handler);
}
/*
* Basic EAP packet verfications & validations
*/
-static int eap_validation(eap_packet_t *eap_packet)
+static int eap_validation(REQUEST *request, eap_packet_t *eap_packet)
{
uint16_t len;
(eap_packet->data[0] <= 0) ||
(eap_packet->data[0] > PW_EAP_MAX_TYPES)) {
- radlog(L_AUTH, "rlm_eap: Incorrect EAP Message, "
- "Ignoring the packet");
+ radlog_request(L_AUTH, 0, request,
+ "Badly formatted 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");
+ radlog_request(L_AUTH, 0, request, "Got NOTIFICATION, "
+ "Ignoring the packet");
return EAP_INVALID;
}
/*
* Get the user Identity only from EAP-Identity packets
*/
-static char *eap_identity(eap_packet_t *eap_packet)
+static char *eap_identity(REQUEST *request, eap_packet_t *eap_packet)
{
int size;
uint16_t len;
len = ntohs(len);
if ((len <= 5) || (eap_packet->data[1] == 0x00)) {
- radlog(L_ERR, "rlm_eap: UserIdentity Unknown ");
+ RDEBUG("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;
- }
+ identity = rad_malloc(size + 1);
memcpy(identity, &eap_packet->data[1], size);
identity[size] = '\0';
{
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) {
+ if (eap_validation(request, eap_packet) == EAP_INVALID) {
free(*eap_packet_p);
*eap_packet_p = NULL;
return NULL;
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");
+ RDEBUG("Either EAP-request timed out OR"
+ " EAP-response to an unknown EAP-request");
free(*eap_packet_p);
*eap_packet_p = NULL;
return NULL;
*/
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.");
+ RDEBUG("Response appears to match, but EAP type is wrong.");
free(*eap_packet_p);
*eap_packet_p = NULL;
return NULL;
}
- } else {
- handler = eap_handler_alloc();
+
+ vp = pairfind(request->packet->vps, PW_USER_NAME, 0);
+ 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
+ */
+ RDEBUG2("Broken NAS did not set User-Name, setting from EAP Identity");
+ vp = pairmake("User-Name", handler->identity, T_OP_EQ);
+ if (vp == NULL) {
+ RDEBUG("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->vp_strvalue,
+ MAX_STRING_LEN) != 0) {
+ RDEBUG("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(inst);
if (handler == NULL) {
- radlog(L_ERR, "rlm_eap: out of memory");
+ RDEBUG("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);
+ handler->identity = eap_identity(request, eap_packet);
if (handler->identity == NULL) {
- radlog(L_ERR, "rlm_eap: Identity Unknown, authentication failed");
+ RDEBUG("Identity Unknown, authentication failed");
free(*eap_packet_p);
*eap_packet_p = NULL;
- eap_handler_free(&handler);
+ eap_handler_free(inst, handler);
return NULL;
}
+
+ vp = pairfind(request->packet->vps, PW_USER_NAME, 0);
+ 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
+ */
+ RDEBUG2("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) {
+ RDEBUG("Out of memory");
+ free(*eap_packet_p);
+ *eap_packet_p = NULL;
+ eap_handler_free(inst, 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->vp_strvalue,
+ MAX_STRING_LEN) != 0) {
+ RDEBUG("Identity does not match User-Name, setting from EAP Identity.");
+ free(*eap_packet_p);
+ *eap_packet_p = NULL;
+ eap_handler_free(inst, 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);
+ eap_handler_free(inst, handler);
return NULL;
}
- handler->timestamp = time(NULL);
+ handler->timestamp = request->timestamp;
handler->request = request; /* LEAP needs this */
return handler;
}