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