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