📄 eap.c
字号:
/* * eap.c rfc2284 & rfc2869 implementation * * Version: $Id$ * * 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA * * Copyright 2000-2003,2006 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 <freeradius-devel/ident.h>RCSID("$Id$")#include "rlm_eap.h"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; } 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", eaptype_name); lt_dlclose(node->handle); free(node); return -1; } *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; RDEBUG2("processing type %s", atype->typename); request->module = 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 */ 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 */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; VALUE_PAIR *vp; char namebuf[64]; const char *eaptype_name; REQUEST *request = handler->request; eaptype = &handler->eap_ds->response->type; /* * Don't trust anyone. */ if ((eaptype->type == 0) || (eaptype->type > PW_EAP_MAX_TYPES)) { 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; } /* * Figure out what to do. */ switch(eaptype->type) { case PW_EAP_IDENTITY: RDEBUG2("EAP Identity"); /* * Allow per-user configuration of EAP types. */ vp = pairfind(handler->request->config_items, PW_EAP_TYPE); 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)) { RDEBUG2("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; } 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) { 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 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. */ RDEBUG2("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; } if (eaptype->data == NULL) { RDEBUG2("Empty NAK packet, cannot decide what EAP type the client wants."); 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); 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]]) { 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; } /* * 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: 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]) { 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) { RDEBUG2("Handler failed in EAP/%s", eaptype_name); return EAP_INVALID; } break; } return EAP_OK;}/* * 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){ VALUE_PAIR *vp; eap_packet_t *eap_packet; 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 { RDEBUG2("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; } /* * 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; vp = eap_packet2vp(eap_packet); if (!vp) return RLM_MODULE_INVALID; pairadd(&(request->reply->vps), vp); /* * 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->vp_octets, 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) {
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -