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