a952a3cdf435382fddb248655ac489b89e915bf8
[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/libradius.h>
64 #include "eap_types.h"
65
66 static const char *eap_types[] = {
67   "",
68   "identity",
69   "notification",
70   "nak",                        /* NAK */
71   "md5",
72   "otp",
73   "gtc",
74   "7",
75   "8",
76   "9",
77   "10",
78   "11",
79   "12",
80   "tls",                        /* 13 */
81   "14",
82   "15",
83   "16",
84   "leap",                       /* 17 */
85   "sim",                        /* 18 GSM-SIM authentication */
86   "19",
87   "20",
88   "ttls",                       /* 21 */
89   "22",
90   "23",
91   "24",
92   "peap",                       /* 25 */
93   "mschapv2",                   /* 26 */
94   "27",
95   "28",
96   "cisco_mschapv2",             /* 29 */
97   "30",
98   "31",
99   "32",
100   "33",
101   "34",
102   "35",
103   "36",
104   "37",
105   "tnc",                        /* 38 */
106   "39",
107   "40",
108   "41",
109   "42",
110   "fast",
111   "44",
112   "45",
113   "pax",
114   "psk",
115   "sake",
116   "ikev2",
117   "50",
118   "51",
119   "pwd"
120 };                              /* MUST have PW_EAP_MAX_TYPES */
121
122 /*
123  *      Return an EAP-Type for a particular name.
124  */
125 int eaptype_name2type(const char *name)
126 {
127         int i;
128
129         for (i = 0; i <= PW_EAP_MAX_TYPES; i++) {
130                 if (strcmp(name, eap_types[i]) == 0) {
131                         return i;
132                 }
133         }
134
135         return -1;
136 }
137
138 /*
139  *      Returns a text string containing the name of the EAP type.
140  */
141 const char *eaptype_type2name(unsigned int type, char *buffer, size_t buflen)
142 {
143         DICT_VALUE      *dval;
144
145         if (type > PW_EAP_MAX_TYPES) {
146                 /*
147                  *      Prefer the dictionary name over a number,
148                  *      if it exists.
149                  */
150                 dval = dict_valbyattr(PW_EAP_TYPE, 0, type);
151                 if (dval) {
152                         snprintf(buffer, buflen, "%s", dval->name);
153                 }
154
155                 snprintf(buffer, buflen, "%d", type);
156                 return buffer;
157         } else if ((*eap_types[type] >= '0') && (*eap_types[type] <= '9')) {
158                 /*
159                  *      Prefer the dictionary name, if it exists.
160                  */
161                 dval = dict_valbyattr(PW_EAP_TYPE, 0, type);
162                 if (dval) {
163                         snprintf(buffer, buflen, "%s", dval->name);
164                         return buffer;
165                 } /* else it wasn't in the dictionary */
166         } /* else the name in the array was non-numeric */
167
168         /*
169          *      Return the name, whatever it is.
170          */
171         return eap_types[type];
172 }
173
174 /*
175  *      EAP packet format to be sent over the wire
176  *
177  *      i.e. code+id+length+data where data = null/type+typedata
178  *      based on code.
179  *
180  * INPUT to function is reply->code
181  *                      reply->id
182  *                      reply->type   - setup with data
183  *
184  * OUTPUT reply->packet is setup with wire format, and will
185  *                      be malloc()'ed to the right size.
186  *
187  */
188 int eap_wireformat(EAP_PACKET *reply)
189 {
190         eap_packet_t    *hdr;
191         uint16_t total_length = 0;
192
193         if (reply == NULL) return EAP_INVALID;
194
195         /*
196          *      If reply->packet is set, then the wire format
197          *      has already been calculated, just succeed.
198          */
199         if(reply->packet != NULL) return EAP_VALID;
200
201         total_length = EAP_HEADER_LEN;
202         if (reply->code < 3) {
203                 total_length += 1/*EAPtype*/;
204                 if (reply->type.data && reply->type.length > 0) {
205                         total_length += reply->type.length;
206                 }
207         }
208
209         reply->packet = (unsigned char *)malloc(total_length);
210         hdr = (eap_packet_t *)reply->packet;
211         if (!hdr) {
212                 radlog(L_ERR, "rlm_eap: out of memory");
213                 return EAP_INVALID;
214         }
215
216         hdr->code = (reply->code & 0xFF);
217         hdr->id = (reply->id & 0xFF);
218         total_length = htons(total_length);
219         memcpy(hdr->length, &total_length, sizeof(total_length));
220
221         /*
222          *      Request and Response packets are special.
223          */
224         if ((reply->code == PW_EAP_REQUEST) ||
225             (reply->code == PW_EAP_RESPONSE)) {
226                 hdr->data[0] = (reply->type.type & 0xFF);
227
228                 /*
229                  * Here since we cannot know the typedata format and length
230                  *
231                  * Type_data is expected to be wired by each EAP-Type
232                  *
233                  * Zero length/No typedata is supported as long as
234                  * type is defined
235                  */
236                 if (reply->type.data && reply->type.length > 0) {
237                         memcpy(&hdr->data[1], reply->type.data, reply->type.length);
238                         free(reply->type.data);
239                         reply->type.data = reply->packet + EAP_HEADER_LEN + 1/*EAPtype*/;
240                 }
241         }
242
243         return EAP_VALID;
244 }
245
246
247 /*
248  *      compose EAP reply packet in EAP-Message attr of RADIUS.  If
249  *      EAP exceeds 253, frame it in multiple EAP-Message attrs.
250  */
251 int eap_basic_compose(RADIUS_PACKET *packet, EAP_PACKET *reply)
252 {
253         VALUE_PAIR *vp;
254         eap_packet_t *eap_packet;
255         int rcode;
256
257         if (eap_wireformat(reply) == EAP_INVALID) {
258                 return RLM_MODULE_INVALID;
259         }
260         eap_packet = (eap_packet_t *)reply->packet;
261
262         pairdelete(&(packet->vps), PW_EAP_MESSAGE, 0, TAG_ANY);
263
264         vp = eap_packet2vp(eap_packet);
265         if (!vp) return RLM_MODULE_INVALID;
266         pairadd(&(packet->vps), vp);
267
268         /*
269          *      EAP-Message is always associated with
270          *      Message-Authenticator but not vice-versa.
271          *
272          *      Don't add a Message-Authenticator if it's already
273          *      there.
274          */
275         vp = pairfind(packet->vps, PW_MESSAGE_AUTHENTICATOR, 0, TAG_ANY);
276         if (!vp) {
277                 vp = paircreate(PW_MESSAGE_AUTHENTICATOR, 0);
278                 memset(vp->vp_strvalue, 0, AUTH_VECTOR_LEN);
279                 vp->length = AUTH_VECTOR_LEN;
280                 pairadd(&(packet->vps), vp);
281         }
282
283         /* Set request reply code, but only if it's not already set. */
284         rcode = RLM_MODULE_OK;
285         if (!packet->code) switch(reply->code) {
286         case PW_EAP_RESPONSE:
287         case PW_EAP_SUCCESS:
288                 packet->code = PW_AUTHENTICATION_ACK;
289                 rcode = RLM_MODULE_HANDLED;
290                 break;
291         case PW_EAP_FAILURE:
292                 packet->code = PW_AUTHENTICATION_REJECT;
293                 rcode = RLM_MODULE_REJECT;
294                 break;
295         case PW_EAP_REQUEST:
296                 packet->code = PW_ACCESS_CHALLENGE;
297                 rcode = RLM_MODULE_HANDLED;
298                 break;
299         default:
300                 /* Should never enter here */
301                 radlog(L_ERR, "rlm_eap: reply code %d is unknown, Rejecting the request.", reply->code);
302                 packet->code = PW_AUTHENTICATION_REJECT;
303                 break;
304         }
305
306         return rcode;
307 }
308
309
310 VALUE_PAIR *eap_packet2vp(const eap_packet_t *packet)
311 {
312         int             total, size;
313         const uint8_t   *ptr;
314         VALUE_PAIR      *head = NULL;
315         VALUE_PAIR      **tail = &head;
316         VALUE_PAIR      *vp;
317
318         total = packet->length[0] * 256 + packet->length[1];
319
320         ptr = (const uint8_t *) packet;
321
322         do {
323                 size = total;
324                 if (size > 253) size = 253;
325
326                 vp = paircreate(PW_EAP_MESSAGE, 0);
327                 if (!vp) {
328                         pairfree(&head);
329                         return NULL;
330                 }
331                 memcpy(vp->vp_octets, ptr, size);
332                 vp->length = size;
333
334                 *tail = vp;
335                 tail = &(vp->next);
336
337                 ptr += size;
338                 total -= size;
339         } while (total > 0);
340
341         return head;
342 }
343
344
345 /*
346  * Handles multiple EAP-Message attrs
347  * ie concatenates all to get the complete EAP packet.
348  *
349  * NOTE: Sometimes Framed-MTU might contain the length of EAP-Message,
350  *      refer fragmentation in rfc2869.
351  */
352 eap_packet_t *eap_vp2packet(VALUE_PAIR *vps)
353 {
354         VALUE_PAIR *first, *vp;
355         eap_packet_t *eap_packet;
356         unsigned char *ptr;
357         uint16_t len;
358         int total_len;
359
360         /*
361          *      Get only EAP-Message attribute list
362          */
363         first = pairfind(vps, PW_EAP_MESSAGE, 0, TAG_ANY);
364         if (first == NULL) {
365                 DEBUG("rlm_eap: EAP-Message not found");
366                 return NULL;
367         }
368
369         /*
370          *      Sanity check the length before doing anything.
371          */
372         if (first->length < 4) {
373                 DEBUG("rlm_eap: EAP packet is too short.");
374                 return NULL;
375         }
376
377         /*
378          *      Get the Actual length from the EAP packet
379          *      First EAP-Message contains the EAP packet header
380          */
381         memcpy(&len, first->vp_strvalue + 2, sizeof(len));
382         len = ntohs(len);
383
384         /*
385          *      Take out even more weird things.
386          */
387         if (len < 4) {
388                 DEBUG("rlm_eap: EAP packet has invalid length.");
389                 return NULL;
390         }
391
392         /*
393          *      Sanity check the length, BEFORE malloc'ing memory.
394          */
395         total_len = 0;
396         for (vp = first; vp; vp = pairfind(vp->next, PW_EAP_MESSAGE, 0, TAG_ANY)) {
397                 total_len += vp->length;
398
399                 if (total_len > len) {
400                         DEBUG("rlm_eap: Malformed EAP packet.  Length in packet header does not match actual length");
401                         return NULL;
402                 }
403         }
404
405         /*
406          *      If the length is SMALLER, die, too.
407          */
408         if (total_len < len) {
409                 DEBUG("rlm_eap: Malformed EAP packet.  Length in packet header does not match actual length");
410                 return NULL;
411         }
412
413         /*
414          *      Now that we know the lengths are OK, allocate memory.
415          */
416         eap_packet = (eap_packet_t *) malloc(len);
417         if (eap_packet == NULL) {
418                 radlog(L_ERR, "rlm_eap: out of memory");
419                 return NULL;
420         }
421
422         /*
423          *      Copy the data from EAP-Message's over to our EAP packet.
424          */
425         ptr = (unsigned char *)eap_packet;
426
427         /* RADIUS ensures order of attrs, so just concatenate all */
428         for (vp = first; vp; vp = pairfind(vp->next, PW_EAP_MESSAGE, 0, TAG_ANY)) {
429                 memcpy(ptr, vp->vp_strvalue, vp->length);
430                 ptr += vp->length;
431         }
432
433         return eap_packet;
434 }
435
436 VALUE_PAIR *eap_chbind_packet2vp(const eap_chbind_packet_t *packet, size_t len)
437 {
438         size_t          size;
439         const uint8_t   *ptr;
440         VALUE_PAIR      *head = NULL;
441         VALUE_PAIR      **tail = &head;
442         VALUE_PAIR      *vp;
443
444         ptr = (const uint8_t *) packet;
445
446         do {
447                 size = len;
448                 if (size > 247) size = 247;
449
450                 vp = paircreate(PW_UKERNA_CHBIND, VENDORPEC_UKERNA,
451                                 PW_TYPE_OCTETS);
452                 if (!vp) {
453                         pairfree(&head);
454                         return NULL;
455                 }
456                 memcpy(vp->vp_octets, ptr, size);
457                 vp->length = size;
458
459                 *tail = vp;
460                 tail = &(vp->next);
461
462                 ptr += size;
463                 len -= size;
464         } while (len > 0);
465
466         return head;
467 }
468
469
470 /*
471  * Handles multiple EAP-channel-binding Message attrs
472  * ie concatenates all to get the complete EAP-channel-binding packet.
473  */
474 size_t eap_chbind_vp2packet(VALUE_PAIR *vps, eap_chbind_packet_t **result)
475 {
476         VALUE_PAIR *first, *vp;
477         eap_chbind_packet_t *eap_chbind_packet;
478         unsigned char *ptr;
479         size_t len;
480
481         first = pairfind(vps, PW_UKERNA_CHBIND, VENDORPEC_UKERNA);
482
483         /*
484          *      Compute total length
485          */
486         len = 0;
487         for (vp = first; vp; 
488              vp = pairfind(vps, PW_UKERNA_CHBIND, VENDORPEC_UKERNA)) {
489                 len += vp->length;
490         }
491
492         /*
493          *      Now that we know the length, allocate memory.
494          */
495         eap_chbind_packet = (eap_chbind_packet_t *) malloc(len);
496         if (eap_chbind_packet == NULL) {
497                 radlog(L_ERR, "rlm_eap: out of memory");
498                 return 0;
499         }
500
501         /*
502          *      Copy the data from EAP-Message's over to our EAP packet.
503          */
504         ptr = (unsigned char *)eap_chbind_packet;
505
506         /* RADIUS ensures order of attrs, so just concatenate all */
507         for (vp = first; vp; 
508              vp = pairfind(vps, PW_UKERNA_CHBIND, VENDORPEC_UKERNA)) {
509                 memcpy(ptr, vp->vp_octets, vp->length);
510                 ptr += vp->length;
511         }
512
513         *result = eap_chbind_packet;
514         return len;
515 }