eea929c6afce0e8d20fc25513cf033804b62cd54
[freeradius.git] / src / modules / rlm_eap / libeap / eapcommon.c
1 /*
2  * eapcommon.c    rfc2284 & rfc2869 implementation
3  *
4  * code common to clients and to servers.
5  *
6  * Version:     $Id$
7  *
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.
12  *
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.
17  *
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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
21  *
22  * Copyright 2000-2003  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>
26  */
27 /*
28  *  EAP PACKET FORMAT
29  *  --- ------ ------
30  *  0                   1                   2                   3
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  * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
35  * |    Data ...
36  * +-+-+-+-+
37  *
38  *
39  * EAP Request and Response Packet Format
40  * --- ------- --- -------- ------ ------
41  *  0                   1                   2                   3
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  * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-
48  *
49  *
50  * EAP Success and Failure Packet Format
51  * --- ------- --- ------- ------ ------
52  *  0                   1                   2                   3
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  * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
57  *
58  */
59
60 #include <freeradius-devel/autoconf.h>
61 #include <freeradius-devel/missing.h>
62 #include <freeradius-devel/libradius.h>
63 #include "eap_types.h"
64
65 static const char rcsid[] = "$Id$";
66
67 static const char *eap_types[] = {
68   "",
69   "identity",
70   "notification",
71   "nak",                        /* NAK */
72   "md5",
73   "otp",
74   "gtc",
75   "7",
76   "8",
77   "9",
78   "10",
79   "11",
80   "12",
81   "tls",                        /* 13 */
82   "14",
83   "15",
84   "16",
85   "leap",                       /* 17 */
86   "sim",                        /* 18 GSM-SIM authentication */
87   "19",
88   "20",
89   "ttls",                       /* 21 */
90   "22",
91   "23",
92   "24",
93   "peap",                       /* 25 */
94   "mschapv2",                   /* 26 */
95   "27",
96   "28",
97   "cisco_mschapv2"              /* 29 */
98 };
99 #define MAX_EAP_TYPE_NAME 29
100
101 /*
102  *      Return an EAP-Type for a particular name.
103  */
104 int eaptype_name2type(const char *name)
105 {
106         int i;
107
108         for (i = 0; i <= PW_EAP_MAX_TYPES; i++) {
109                 if (strcmp(name, eap_types[i]) == 0) {
110                         return i;
111                 }
112         }
113
114         return -1;
115 }
116
117 /*
118  *      Returns a text string containing the name of the EAP type.
119  */
120 const char *eaptype_type2name(unsigned int type, char *buffer, size_t buflen)
121 {
122         DICT_VALUE      *dval;
123
124         if (type > MAX_EAP_TYPE_NAME) {
125                 /*
126                  *      Prefer the dictionary name over a number,
127                  *      if it exists.
128                  */
129                 dval = dict_valbyattr(PW_EAP_TYPE, type);
130                 if (dval) {
131                         snprintf(buffer, buflen, "%s", dval->name);
132                 }
133
134                 snprintf(buffer, buflen, "%d", type);
135                 return buffer;
136         } else if ((*eap_types[type] >= '0') && (*eap_types[type] <= '9')) {
137                 /*
138                  *      Prefer the dictionary name, if it exists.
139                  */
140                 dval = dict_valbyattr(PW_EAP_TYPE, type);
141                 if (dval) {
142                         snprintf(buffer, buflen, "%s", dval->name);
143                         return buffer;
144                 } /* else it wasn't in the dictionary */
145         } /* else the name in the array was non-numeric */
146
147         /*
148          *      Return the name, whatever it is.
149          */
150         return eap_types[type];
151 }
152
153 /*
154  *      EAP packet format to be sent over the wire
155  *
156  *      i.e. code+id+length+data where data = null/type+typedata
157  *      based on code.
158  *
159  * INPUT to function is reply->code
160  *                      reply->id
161  *                      reply->type   - setup with data
162  *
163  * OUTPUT reply->packet is setup with wire format, and will
164  *                      be malloc()'ed to the right size.
165  *
166  */
167 static int eap_wireformat(EAP_PACKET *reply)
168 {
169
170         eap_packet_t    *hdr;
171         uint16_t total_length = 0;
172
173         if (reply == NULL) return EAP_INVALID;
174
175         /*
176          * if reply->packet is set, then the wire format
177          * has already been calculated, just succeed!
178          */
179         if(reply->packet != NULL)
180         {
181                 return EAP_VALID;
182         }
183
184         total_length = EAP_HEADER_LEN;
185         if (reply->code < 3) {
186                 total_length += 1/*EAPtype*/;
187                 if (reply->type.data && reply->type.length > 0) {
188                         total_length += reply->type.length;
189                 }
190         }
191
192         reply->packet = (unsigned char *)malloc(total_length);
193         hdr = (eap_packet_t *)reply->packet;
194         if (!hdr) {
195                 radlog(L_ERR, "rlm_eap: out of memory");
196                 return EAP_INVALID;
197         }
198
199         hdr->code = (reply->code & 0xFF);
200         hdr->id = (reply->id & 0xFF);
201         total_length = htons(total_length);
202         memcpy(hdr->length, &total_length, sizeof(uint16_t));
203
204         /*
205          *      Request and Response packets are special.
206          */
207         if ((reply->code == PW_EAP_REQUEST) ||
208             (reply->code == PW_EAP_RESPONSE)) {
209                 hdr->data[0] = (reply->type.type & 0xFF);
210
211                 /*
212                  * Here since we cannot know the typedata format and length
213                  *
214                  * Type_data is expected to be wired by each EAP-Type
215                  *
216                  * Zero length/No typedata is supported as long as
217                  * type is defined
218                  */
219                 if (reply->type.data && reply->type.length > 0) {
220                         memcpy(&hdr->data[1], reply->type.data, reply->type.length);
221                         free(reply->type.data);
222                         reply->type.data = reply->packet + EAP_HEADER_LEN + 1/*EAPtype*/;
223                 }
224         }
225
226         return EAP_VALID;
227 }
228
229 /*
230  *      compose EAP reply packet in EAP-Message attr of RADIUS.  If
231  *      EAP exceeds 253, frame it in multiple EAP-Message attrs.
232  */
233 int eap_basic_compose(RADIUS_PACKET *packet, EAP_PACKET *reply)
234 {
235         uint16_t eap_len, len;
236         VALUE_PAIR *eap_msg;
237         VALUE_PAIR *vp;
238         eap_packet_t *eap_packet;
239         unsigned char   *ptr;
240         int rcode;
241
242         if (eap_wireformat(reply) == EAP_INVALID) {
243                 return RLM_MODULE_INVALID;
244         }
245         eap_packet = (eap_packet_t *)reply->packet;
246
247         memcpy(&eap_len, &(eap_packet->length), sizeof(uint16_t));
248         len = eap_len = ntohs(eap_len);
249         ptr = (unsigned char *)eap_packet;
250
251         pairdelete(&(packet->vps), PW_EAP_MESSAGE);
252
253         do {
254                 if (eap_len > 253) {
255                         len = 253;
256                         eap_len -= 253;
257                 } else {
258                         len = eap_len;
259                         eap_len = 0;
260                 }
261
262                 /*
263                  * create a value pair & append it to the packet list
264                  * This memory gets freed up when packet is freed up
265                  */
266                 eap_msg = paircreate(PW_EAP_MESSAGE, PW_TYPE_OCTETS);
267                 memcpy(eap_msg->vp_strvalue, ptr, len);
268                 eap_msg->length = len;
269                 pairadd(&(packet->vps), eap_msg);
270                 ptr += len;
271                 eap_msg = NULL;
272         } while (eap_len);
273
274         /*
275          *      EAP-Message is always associated with
276          *      Message-Authenticator but not vice-versa.
277          *
278          *      Don't add a Message-Authenticator if it's already
279          *      there.
280          */
281         vp = pairfind(packet->vps, PW_MESSAGE_AUTHENTICATOR);
282         if (!vp) {
283                 vp = paircreate(PW_MESSAGE_AUTHENTICATOR, PW_TYPE_OCTETS);
284                 memset(vp->vp_strvalue, 0, AUTH_VECTOR_LEN);
285                 vp->length = AUTH_VECTOR_LEN;
286                 pairadd(&(packet->vps), vp);
287         }
288
289         /* Set request reply code, but only if it's not already set. */
290         rcode = RLM_MODULE_OK;
291         if (!packet->code) switch(reply->code) {
292         case PW_EAP_RESPONSE:
293         case PW_EAP_SUCCESS:
294                 packet->code = PW_AUTHENTICATION_ACK;
295                 rcode = RLM_MODULE_HANDLED;
296                 break;
297         case PW_EAP_FAILURE:
298                 packet->code = PW_AUTHENTICATION_REJECT;
299                 rcode = RLM_MODULE_REJECT;
300                 break;
301         case PW_EAP_REQUEST:
302                 packet->code = PW_ACCESS_CHALLENGE;
303                 rcode = RLM_MODULE_HANDLED;
304                 break;
305         default:
306                 /* Should never enter here */
307                 radlog(L_ERR, "rlm_eap: reply code %d is unknown, Rejecting the request.", reply->code);
308                 packet->code = PW_AUTHENTICATION_REJECT;
309                 break;
310         }
311
312         return rcode;
313 }
314
315 /*
316  * given a radius request with some attributes in the EAP range, build
317  * them all into a single EAP-Message body.
318  *
319  * Note that this function will build multiple EAP-Message bodies
320  * if there are multiple eligible EAP-types. This is incorrect, as the
321  * recipient will in fact concatenate them.
322  *
323  * XXX - we could break the loop once we process one type. Maybe this
324  *       just deserves an assert?
325  *
326  */
327 void map_eap_types(RADIUS_PACKET *req)
328 {
329         VALUE_PAIR *vp, *vpnext;
330         int id, eapcode;
331         EAP_PACKET ep;
332         int eap_type;
333
334         vp = pairfind(req->vps, ATTRIBUTE_EAP_ID);
335         if(vp == NULL) {
336                 id = ((int)getpid() & 0xff);
337         } else {
338                 id = vp->lvalue;
339         }
340
341         vp = pairfind(req->vps, ATTRIBUTE_EAP_CODE);
342         if(vp == NULL) {
343                 eapcode = PW_EAP_REQUEST;
344         } else {
345                 eapcode = vp->lvalue;
346         }
347
348
349         for(vp = req->vps; vp != NULL; vp = vpnext) {
350                 /* save it in case it changes! */
351                 vpnext = vp->next;
352
353                 if(vp->attribute >= ATTRIBUTE_EAP_BASE &&
354                    vp->attribute < ATTRIBUTE_EAP_BASE+256) {
355                         break;
356                 }
357         }
358
359         if(vp == NULL) {
360                 return;
361         }
362
363         eap_type = vp->attribute - ATTRIBUTE_EAP_BASE;
364
365         switch(eap_type) {
366         case PW_EAP_IDENTITY:
367         case PW_EAP_NOTIFICATION:
368         case PW_EAP_NAK:
369         case PW_EAP_MD5:
370         case PW_EAP_OTP:
371         case PW_EAP_GTC:
372         case PW_EAP_TLS:
373         case PW_EAP_LEAP:
374         case PW_EAP_TTLS:
375         case PW_EAP_PEAP:
376         default:
377                 /*
378                  * no known special handling, it is just encoded as an
379                  * EAP-message with the given type.
380                  */
381
382                 /* nuke any existing EAP-Messages */
383                 pairdelete(&req->vps, PW_EAP_MESSAGE);
384
385                 memset(&ep, 0, sizeof(ep));
386                 ep.code = eapcode;
387                 ep.id   = id;
388                 ep.type.type = eap_type;
389                 ep.type.length = vp->length;
390                 ep.type.data = vp->vp_strvalue;
391                 eap_basic_compose(req, &ep);
392         }
393 }
394
395 /*
396  * Handles multiple EAP-Message attrs
397  * ie concatenates all to get the complete EAP packet.
398  *
399  * NOTE: Sometimes Framed-MTU might contain the length of EAP-Message,
400  *      refer fragmentation in rfc2869.
401  */
402 eap_packet_t *eap_attribute(VALUE_PAIR *vps)
403 {
404         VALUE_PAIR *first, *vp;
405         eap_packet_t *eap_packet;
406         unsigned char *ptr;
407         uint16_t len;
408         int total_len;
409
410         /*
411          *      Get only EAP-Message attribute list
412          */
413         first = pairfind(vps, PW_EAP_MESSAGE);
414         if (first == NULL) {
415                 radlog(L_ERR, "rlm_eap: EAP-Message not found");
416                 return NULL;
417         }
418
419         /*
420          *      Sanity check the length before doing anything.
421          */
422         if (first->length < 4) {
423                 radlog(L_ERR, "rlm_eap: EAP packet is too short.");
424                 return NULL;
425         }
426
427         /*
428          *      Get the Actual length from the EAP packet
429          *      First EAP-Message contains the EAP packet header
430          */
431         memcpy(&len, first->vp_strvalue + 2, sizeof(len));
432         len = ntohs(len);
433
434         /*
435          *      Take out even more weird things.
436          */
437         if (len < 4) {
438                 radlog(L_ERR, "rlm_eap: EAP packet has invalid length.");
439                 return NULL;
440         }
441
442         /*
443          *      Sanity check the length, BEFORE malloc'ing memory.
444          */
445         total_len = 0;
446         for (vp = first; vp; vp = pairfind(vp->next, PW_EAP_MESSAGE)) {
447                 total_len += vp->length;
448
449                 if (total_len > len) {
450                         radlog(L_ERR, "rlm_eap: Malformed EAP packet.  Length in packet header does not match actual length");
451                         return NULL;
452                 }
453         }
454
455         /*
456          *      If the length is SMALLER, die, too.
457          */
458         if (total_len < len) {
459                 radlog(L_ERR, "rlm_eap: Malformed EAP packet.  Length in packet header does not match actual length");
460                 return NULL;
461         }
462
463         /*
464          *      Now that we know the lengths are OK, allocate memory.
465          */
466         eap_packet = (eap_packet_t *) malloc(len);
467         if (eap_packet == NULL) {
468                 radlog(L_ERR, "rlm_eap: out of memory");
469                 return NULL;
470         }
471
472         /*
473          *      Copy the data from EAP-Message's over to out EAP packet.
474          */
475         ptr = (unsigned char *)eap_packet;
476
477         /* RADIUS ensures order of attrs, so just concatenate all */
478         for (vp = first; vp; vp = pairfind(vp->next, PW_EAP_MESSAGE)) {
479                 memcpy(ptr, vp->vp_strvalue, vp->length);
480                 ptr += vp->length;
481         }
482
483         return eap_packet;
484 }
485
486 /*
487  * given a radius request with an EAP-Message body, decode it specific
488  * attributes.
489  */
490 void unmap_eap_types(RADIUS_PACKET *rep)
491 {
492         VALUE_PAIR *eap1;
493         eap_packet_t *e;
494         int len;
495         int type;
496
497         /* find eap message */
498         e = eap_attribute(rep->vps);
499
500         /* nothing to do! */
501         if(e == NULL) return;
502
503         /* create EAP-ID and EAP-CODE attributes to start */
504         eap1 = paircreate(ATTRIBUTE_EAP_ID, PW_TYPE_INTEGER);
505         eap1->lvalue = e->id;
506         pairadd(&(rep->vps), eap1);
507
508         eap1 = paircreate(ATTRIBUTE_EAP_CODE, PW_TYPE_INTEGER);
509         eap1->lvalue = e->code;
510         pairadd(&(rep->vps), eap1);
511
512         switch(e->code)
513         {
514         default:
515         case PW_EAP_SUCCESS:
516         case PW_EAP_FAILURE:
517                 /* no data */
518                 break;
519
520         case PW_EAP_REQUEST:
521         case PW_EAP_RESPONSE:
522                 /* there is a type field, which we use to create
523                  * a new attribute */
524
525                 /* the length was decode already into the attribute
526                  * length, and was checked already. Network byte
527                  * order, just pull it out using math.
528                  */
529                 len = e->length[0]*256 + e->length[1];
530
531                 /* verify the length is big enough to hold type */
532                 if(len < 5)
533                 {
534                         return;
535                 }
536
537                 type = e->data[0];
538
539                 type += ATTRIBUTE_EAP_BASE;
540                 len -= 5;
541
542                 if(len > MAX_STRING_LEN) {
543                         len = MAX_STRING_LEN;
544                 }
545
546                 eap1 = paircreate(type, PW_TYPE_OCTETS);
547                 memcpy(eap1->vp_strvalue, &e->data[1], len);
548                 eap1->length = len;
549                 pairadd(&(rep->vps), eap1);
550                 break;
551         }
552
553         return;
554 }
555