2 * eapcommon.c rfc2284 & rfc2869 implementation
4 * code common to clients and to servers.
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; either version 2 of the License, or
11 * (at your option) any later version.
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
18 * You should have received a copy of the GNU General Public License
19 * along with this program; if not, write to the Free Software
20 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
22 * Copyright 2000-2003,2006 The FreeRADIUS server project
23 * Copyright 2001 hereUare Communications, Inc. <raghud@hereuare.com>
24 * Copyright 2003 Alan DeKok <aland@freeradius.org>
25 * Copyright 2003 Michael Richardson <mcr@sandelman.ottawa.on.ca>
31 * 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
32 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
33 * | Code | Identifier | Length |
34 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
39 * EAP Request and Response Packet Format
40 * --- ------- --- -------- ------ ------
42 * 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
43 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
44 * | Code | Identifier | Length |
45 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
46 * | Type | Type-Data ...
47 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-
50 * EAP Success and Failure Packet Format
51 * --- ------- --- ------- ------ ------
53 * 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
54 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
55 * | Code | Identifier | Length |
56 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
60 #include <freeradius-devel/ident.h>
63 #include <freeradius-devel/autoconf.h>
64 #include <freeradius-devel/missing.h>
65 #include <freeradius-devel/libradius.h>
66 #include "eap_types.h"
68 static const char *eap_types[] = {
87 "sim", /* 18 GSM-SIM authentication */
98 "cisco_mschapv2" /* 29 */
100 #define MAX_EAP_TYPE_NAME 29
103 * Return an EAP-Type for a particular name.
105 int eaptype_name2type(const char *name)
109 for (i = 0; i <= PW_EAP_MAX_TYPES; i++) {
110 if (strcmp(name, eap_types[i]) == 0) {
119 * Returns a text string containing the name of the EAP type.
121 const char *eaptype_type2name(unsigned int type, char *buffer, size_t buflen)
125 if (type > MAX_EAP_TYPE_NAME) {
127 * Prefer the dictionary name over a number,
130 dval = dict_valbyattr(PW_EAP_TYPE, type);
132 snprintf(buffer, buflen, "%s", dval->name);
135 snprintf(buffer, buflen, "%d", type);
137 } else if ((*eap_types[type] >= '0') && (*eap_types[type] <= '9')) {
139 * Prefer the dictionary name, if it exists.
141 dval = dict_valbyattr(PW_EAP_TYPE, type);
143 snprintf(buffer, buflen, "%s", dval->name);
145 } /* else it wasn't in the dictionary */
146 } /* else the name in the array was non-numeric */
149 * Return the name, whatever it is.
151 return eap_types[type];
155 * EAP packet format to be sent over the wire
157 * i.e. code+id+length+data where data = null/type+typedata
160 * INPUT to function is reply->code
162 * reply->type - setup with data
164 * OUTPUT reply->packet is setup with wire format, and will
165 * be malloc()'ed to the right size.
168 static int eap_wireformat(EAP_PACKET *reply)
172 uint16_t total_length = 0;
174 if (reply == NULL) return EAP_INVALID;
177 * if reply->packet is set, then the wire format
178 * has already been calculated, just succeed!
180 if(reply->packet != NULL)
185 total_length = EAP_HEADER_LEN;
186 if (reply->code < 3) {
187 total_length += 1/*EAPtype*/;
188 if (reply->type.data && reply->type.length > 0) {
189 total_length += reply->type.length;
193 reply->packet = (unsigned char *)malloc(total_length);
194 hdr = (eap_packet_t *)reply->packet;
196 radlog(L_ERR, "rlm_eap: out of memory");
200 hdr->code = (reply->code & 0xFF);
201 hdr->id = (reply->id & 0xFF);
202 total_length = htons(total_length);
203 memcpy(hdr->length, &total_length, sizeof(uint16_t));
206 * Request and Response packets are special.
208 if ((reply->code == PW_EAP_REQUEST) ||
209 (reply->code == PW_EAP_RESPONSE)) {
210 hdr->data[0] = (reply->type.type & 0xFF);
213 * Here since we cannot know the typedata format and length
215 * Type_data is expected to be wired by each EAP-Type
217 * Zero length/No typedata is supported as long as
220 if (reply->type.data && reply->type.length > 0) {
221 memcpy(&hdr->data[1], reply->type.data, reply->type.length);
222 free(reply->type.data);
223 reply->type.data = reply->packet + EAP_HEADER_LEN + 1/*EAPtype*/;
231 * compose EAP reply packet in EAP-Message attr of RADIUS. If
232 * EAP exceeds 253, frame it in multiple EAP-Message attrs.
234 int eap_basic_compose(RADIUS_PACKET *packet, EAP_PACKET *reply)
236 uint16_t eap_len, len;
239 eap_packet_t *eap_packet;
243 if (eap_wireformat(reply) == EAP_INVALID) {
244 return RLM_MODULE_INVALID;
246 eap_packet = (eap_packet_t *)reply->packet;
248 memcpy(&eap_len, &(eap_packet->length), sizeof(uint16_t));
249 len = eap_len = ntohs(eap_len);
250 ptr = (unsigned char *)eap_packet;
252 pairdelete(&(packet->vps), PW_EAP_MESSAGE);
264 * create a value pair & append it to the packet list
265 * This memory gets freed up when packet is freed up
267 eap_msg = paircreate(PW_EAP_MESSAGE, PW_TYPE_OCTETS);
268 memcpy(eap_msg->vp_strvalue, ptr, len);
269 eap_msg->length = len;
270 pairadd(&(packet->vps), eap_msg);
276 * EAP-Message is always associated with
277 * Message-Authenticator but not vice-versa.
279 * Don't add a Message-Authenticator if it's already
282 vp = pairfind(packet->vps, PW_MESSAGE_AUTHENTICATOR);
284 vp = paircreate(PW_MESSAGE_AUTHENTICATOR, PW_TYPE_OCTETS);
285 memset(vp->vp_strvalue, 0, AUTH_VECTOR_LEN);
286 vp->length = AUTH_VECTOR_LEN;
287 pairadd(&(packet->vps), vp);
290 /* Set request reply code, but only if it's not already set. */
291 rcode = RLM_MODULE_OK;
292 if (!packet->code) switch(reply->code) {
293 case PW_EAP_RESPONSE:
295 packet->code = PW_AUTHENTICATION_ACK;
296 rcode = RLM_MODULE_HANDLED;
299 packet->code = PW_AUTHENTICATION_REJECT;
300 rcode = RLM_MODULE_REJECT;
303 packet->code = PW_ACCESS_CHALLENGE;
304 rcode = RLM_MODULE_HANDLED;
307 /* Should never enter here */
308 radlog(L_ERR, "rlm_eap: reply code %d is unknown, Rejecting the request.", reply->code);
309 packet->code = PW_AUTHENTICATION_REJECT;
317 * given a radius request with some attributes in the EAP range, build
318 * them all into a single EAP-Message body.
320 * Note that this function will build multiple EAP-Message bodies
321 * if there are multiple eligible EAP-types. This is incorrect, as the
322 * recipient will in fact concatenate them.
324 * XXX - we could break the loop once we process one type. Maybe this
325 * just deserves an assert?
328 void map_eap_types(RADIUS_PACKET *req)
330 VALUE_PAIR *vp, *vpnext;
335 vp = pairfind(req->vps, ATTRIBUTE_EAP_ID);
337 id = ((int)getpid() & 0xff);
342 vp = pairfind(req->vps, ATTRIBUTE_EAP_CODE);
344 eapcode = PW_EAP_REQUEST;
346 eapcode = vp->lvalue;
350 for(vp = req->vps; vp != NULL; vp = vpnext) {
351 /* save it in case it changes! */
354 if(vp->attribute >= ATTRIBUTE_EAP_BASE &&
355 vp->attribute < ATTRIBUTE_EAP_BASE+256) {
364 eap_type = vp->attribute - ATTRIBUTE_EAP_BASE;
367 case PW_EAP_IDENTITY:
368 case PW_EAP_NOTIFICATION:
379 * no known special handling, it is just encoded as an
380 * EAP-message with the given type.
383 /* nuke any existing EAP-Messages */
384 pairdelete(&req->vps, PW_EAP_MESSAGE);
386 memset(&ep, 0, sizeof(ep));
389 ep.type.type = eap_type;
390 ep.type.length = vp->length;
391 ep.type.data = vp->vp_octets;
392 eap_basic_compose(req, &ep);
397 * Handles multiple EAP-Message attrs
398 * ie concatenates all to get the complete EAP packet.
400 * NOTE: Sometimes Framed-MTU might contain the length of EAP-Message,
401 * refer fragmentation in rfc2869.
403 eap_packet_t *eap_attribute(VALUE_PAIR *vps)
405 VALUE_PAIR *first, *vp;
406 eap_packet_t *eap_packet;
412 * Get only EAP-Message attribute list
414 first = pairfind(vps, PW_EAP_MESSAGE);
416 radlog(L_ERR, "rlm_eap: EAP-Message not found");
421 * Sanity check the length before doing anything.
423 if (first->length < 4) {
424 radlog(L_ERR, "rlm_eap: EAP packet is too short.");
429 * Get the Actual length from the EAP packet
430 * First EAP-Message contains the EAP packet header
432 memcpy(&len, first->vp_strvalue + 2, sizeof(len));
436 * Take out even more weird things.
439 radlog(L_ERR, "rlm_eap: EAP packet has invalid length.");
444 * Sanity check the length, BEFORE malloc'ing memory.
447 for (vp = first; vp; vp = pairfind(vp->next, PW_EAP_MESSAGE)) {
448 total_len += vp->length;
450 if (total_len > len) {
451 radlog(L_ERR, "rlm_eap: Malformed EAP packet. Length in packet header does not match actual length");
457 * If the length is SMALLER, die, too.
459 if (total_len < len) {
460 radlog(L_ERR, "rlm_eap: Malformed EAP packet. Length in packet header does not match actual length");
465 * Now that we know the lengths are OK, allocate memory.
467 eap_packet = (eap_packet_t *) malloc(len);
468 if (eap_packet == NULL) {
469 radlog(L_ERR, "rlm_eap: out of memory");
474 * Copy the data from EAP-Message's over to out EAP packet.
476 ptr = (unsigned char *)eap_packet;
478 /* RADIUS ensures order of attrs, so just concatenate all */
479 for (vp = first; vp; vp = pairfind(vp->next, PW_EAP_MESSAGE)) {
480 memcpy(ptr, vp->vp_strvalue, vp->length);
488 * given a radius request with an EAP-Message body, decode it specific
491 void unmap_eap_types(RADIUS_PACKET *rep)
498 /* find eap message */
499 e = eap_attribute(rep->vps);
502 if(e == NULL) return;
504 /* create EAP-ID and EAP-CODE attributes to start */
505 eap1 = paircreate(ATTRIBUTE_EAP_ID, PW_TYPE_INTEGER);
506 eap1->lvalue = e->id;
507 pairadd(&(rep->vps), eap1);
509 eap1 = paircreate(ATTRIBUTE_EAP_CODE, PW_TYPE_INTEGER);
510 eap1->lvalue = e->code;
511 pairadd(&(rep->vps), eap1);
522 case PW_EAP_RESPONSE:
523 /* there is a type field, which we use to create
526 /* the length was decode already into the attribute
527 * length, and was checked already. Network byte
528 * order, just pull it out using math.
530 len = e->length[0]*256 + e->length[1];
532 /* verify the length is big enough to hold type */
540 type += ATTRIBUTE_EAP_BASE;
543 if(len > MAX_STRING_LEN) {
544 len = MAX_STRING_LEN;
547 eap1 = paircreate(type, PW_TYPE_OCTETS);
548 memcpy(eap1->vp_strvalue, &e->data[1], len);
550 pairadd(&(rep->vps), eap1);