Channel binding encode/decoder and process functions
[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 RCSID("$Id$")
61
62 #include <freeradius-devel/libradius.h>
63 #include <freeradius-devel/rad_assert.h>
64 #include "eap_types.h"
65
66 const FR_NAME_NUMBER eap_rcode_table[] = {
67         { "notfound",           EAP_NOTFOUND            },
68         { "found",              EAP_OK                  },
69         { "ok",                 EAP_FAIL                },
70         { "fail",               EAP_NOOP                },
71         { "noop",               EAP_INVALID             },
72         { "invalid",            EAP_VALID               },
73         { "valid",              EAP_MAX_RCODES          },
74
75         {  NULL , -1 }
76 };
77
78 /** Return an EAP-Type for a particular name
79  *
80  * Converts a name into an IANA EAP type.
81  *
82  * @param name to convert.
83  * @return The IANA EAP type or PW_EAP_INVALID if the name doesn't match any
84  * known types.
85  */
86 eap_type_t eap_name2type(char const *name)
87 {
88         DICT_VALUE      *dv;
89
90         dv = dict_valbyname(PW_EAP_TYPE, 0, name);
91         if (!dv) return PW_EAP_INVALID;
92
93         if (dv->value >= PW_EAP_MAX_TYPES) return PW_EAP_INVALID;
94
95         return dv->value;
96 }
97
98 /** Return an EAP-name for a particular type
99  *
100  * Resolve
101  */
102 char const *eap_type2name(eap_type_t method)
103 {
104         DICT_VALUE      *dv;
105
106         dv = dict_valbyattr(PW_EAP_TYPE, 0, method);
107         if (dv) {
108                 return dv->name;
109         }
110
111         return "unknown";
112 }
113
114 /*
115  *      EAP packet format to be sent over the wire
116  *
117  *      i.e. code+id+length+data where data = null/type+typedata
118  *      based on code.
119  *
120  * INPUT to function is reply->code
121  *                    reply->id
122  *                    reply->type   - setup with data
123  *
124  * OUTPUT reply->packet is setup with wire format, and will
125  *                    be allocated to the right size.
126  *
127  */
128 int eap_wireformat(eap_packet_t *reply)
129 {
130         eap_packet_raw_t        *header;
131         uint16_t total_length = 0;
132
133         if (!reply) return EAP_INVALID;
134
135         /*
136          *      If reply->packet is set, then the wire format
137          *      has already been calculated, just succeed.
138          */
139         if(reply->packet != NULL) return EAP_VALID;
140
141         total_length = EAP_HEADER_LEN;
142         if (reply->code < 3) {
143                 total_length += 1/* EAP Method */;
144                 if (reply->type.data && reply->type.length > 0) {
145                         total_length += reply->type.length;
146                 }
147         }
148
149         reply->packet = talloc_array(reply, uint8_t, total_length);
150         header = (eap_packet_raw_t *)reply->packet;
151         if (!header) {
152                 return EAP_INVALID;
153         }
154
155         header->code = (reply->code & 0xFF);
156         header->id = (reply->id & 0xFF);
157
158         total_length = htons(total_length);
159         memcpy(header->length, &total_length, sizeof(total_length));
160
161         /*
162          *      Request and Response packets are special.
163          */
164         if ((reply->code == PW_EAP_REQUEST) ||
165             (reply->code == PW_EAP_RESPONSE)) {
166                 header->data[0] = (reply->type.num & 0xFF);
167
168                 /*
169                  * Here since we cannot know the typedata format and length
170                  *
171                  * Type_data is expected to be wired by each EAP-Type
172                  *
173                  * Zero length/No typedata is supported as long as
174                  * type is defined
175                  */
176                 if (reply->type.data && reply->type.length > 0) {
177                         memcpy(&header->data[1], reply->type.data, reply->type.length);
178                         talloc_free(reply->type.data);
179                         reply->type.data = reply->packet + EAP_HEADER_LEN + 1/*EAPtype*/;
180                 }
181         }
182
183         return EAP_VALID;
184 }
185
186
187 /*
188  *      compose EAP reply packet in EAP-Message attr of RADIUS.  If
189  *      EAP exceeds 253, frame it in multiple EAP-Message attrs.
190  */
191 int eap_basic_compose(RADIUS_PACKET *packet, eap_packet_t *reply)
192 {
193         VALUE_PAIR *vp;
194         eap_packet_raw_t *eap_packet;
195         int rcode;
196
197         if (eap_wireformat(reply) == EAP_INVALID) {
198                 return RLM_MODULE_INVALID;
199         }
200         eap_packet = (eap_packet_raw_t *)reply->packet;
201
202         pairdelete(&(packet->vps), PW_EAP_MESSAGE, 0, TAG_ANY);
203
204         vp = eap_packet2vp(packet, eap_packet);
205         if (!vp) return RLM_MODULE_INVALID;
206         pairadd(&(packet->vps), vp);
207
208         /*
209          *      EAP-Message is always associated with
210          *      Message-Authenticator but not vice-versa.
211          *
212          *      Don't add a Message-Authenticator if it's already
213          *      there.
214          */
215         vp = pairfind(packet->vps, PW_MESSAGE_AUTHENTICATOR, 0, TAG_ANY);
216         if (!vp) {
217                 vp = paircreate(packet, PW_MESSAGE_AUTHENTICATOR, 0);
218                 vp->length = AUTH_VECTOR_LEN;
219                 vp->vp_octets = talloc_zero_array(vp, uint8_t, vp->length);
220
221                 pairadd(&(packet->vps), vp);
222         }
223
224         /* Set request reply code, but only if it's not already set. */
225         rcode = RLM_MODULE_OK;
226         if (!packet->code) switch(reply->code) {
227         case PW_EAP_RESPONSE:
228         case PW_EAP_SUCCESS:
229                 packet->code = PW_CODE_ACCESS_ACCEPT;
230                 rcode = RLM_MODULE_HANDLED;
231                 break;
232         case PW_EAP_FAILURE:
233                 packet->code = PW_CODE_ACCESS_REJECT;
234                 rcode = RLM_MODULE_REJECT;
235                 break;
236         case PW_EAP_REQUEST:
237                 packet->code = PW_CODE_ACCESS_CHALLENGE;
238                 rcode = RLM_MODULE_HANDLED;
239                 break;
240         default:
241                 /* Should never enter here */
242                 ERROR("rlm_eap: reply code %d is unknown, Rejecting the request.", reply->code);
243                 packet->code = PW_CODE_ACCESS_REJECT;
244                 break;
245         }
246
247         return rcode;
248 }
249
250
251 VALUE_PAIR *eap_packet2vp(RADIUS_PACKET *packet, eap_packet_raw_t const *eap)
252 {
253         int             total, size;
254         uint8_t const *ptr;
255         VALUE_PAIR      *head = NULL;
256         VALUE_PAIR      *vp;
257         vp_cursor_t     out;
258
259         total = eap->length[0] * 256 + eap->length[1];
260
261         if (total == 0) {
262                 DEBUG("Asked to encode empty EAP-Message!");
263                 return NULL;
264         }
265
266         ptr = (uint8_t const *) eap;
267
268         fr_cursor_init(&out, &head);
269         do {
270                 size = total;
271                 if (size > 253) size = 253;
272
273                 vp = paircreate(packet, PW_EAP_MESSAGE, 0);
274                 if (!vp) {
275                         pairfree(&head);
276                         return NULL;
277                 }
278                 pairmemcpy(vp, ptr, size);
279
280                 fr_cursor_insert(&out, vp);
281
282                 ptr += size;
283                 total -= size;
284         } while (total > 0);
285
286         return head;
287 }
288
289
290 /*
291  * Handles multiple EAP-Message attrs
292  * ie concatenates all to get the complete EAP packet.
293  *
294  * NOTE: Sometimes Framed-MTU might contain the length of EAP-Message,
295  *      refer fragmentation in rfc2869.
296  */
297 eap_packet_raw_t *eap_vp2packet(TALLOC_CTX *ctx, VALUE_PAIR *vps)
298 {
299         VALUE_PAIR *first, *i;
300         eap_packet_raw_t *eap_packet;
301         unsigned char *ptr;
302         uint16_t len;
303         int total_len;
304         vp_cursor_t cursor;
305
306         /*
307          *      Get only EAP-Message attribute list
308          */
309         first = pairfind(vps, PW_EAP_MESSAGE, 0, TAG_ANY);
310         if (!first) {
311                 DEBUG("rlm_eap: EAP-Message not found");
312                 return NULL;
313         }
314
315         /*
316          *      Sanity check the length before doing anything.
317          */
318         if (first->length < 4) {
319                 DEBUG("rlm_eap: EAP packet is too short");
320                 return NULL;
321         }
322
323         /*
324          *      Get the Actual length from the EAP packet
325          *      First EAP-Message contains the EAP packet header
326          */
327         memcpy(&len, first->vp_strvalue + 2, sizeof(len));
328         len = ntohs(len);
329
330         /*
331          *      Take out even more weird things.
332          */
333         if (len < 4) {
334                 DEBUG("rlm_eap: EAP packet has invalid length");
335                 return NULL;
336         }
337
338         /*
339          *      Sanity check the length, BEFORE allocating  memory.
340          */
341         total_len = 0;
342         fr_cursor_init(&cursor, &first);
343         while ((i = fr_cursor_next_by_num(&cursor, PW_EAP_MESSAGE, 0, TAG_ANY))) {
344                 total_len += i->length;
345
346                 if (total_len > len) {
347                         DEBUG("rlm_eap: Malformed EAP packet.  Length in packet header does not match actual length");
348                         return NULL;
349                 }
350         }
351
352         /*
353          *      If the length is SMALLER, die, too.
354          */
355         if (total_len < len) {
356                 DEBUG("rlm_eap: Malformed EAP packet.  Length in packet header does not match actual length");
357                 return NULL;
358         }
359
360         /*
361          *      Now that we know the lengths are OK, allocate memory.
362          */
363         eap_packet = (eap_packet_raw_t *) talloc_zero_array(ctx, uint8_t, len);
364         if (!eap_packet) {
365                 return NULL;
366         }
367
368         /*
369          *      Copy the data from EAP-Message's over to our EAP packet.
370          */
371         ptr = (unsigned char *)eap_packet;
372
373         /* RADIUS ensures order of attrs, so just concatenate all */
374         fr_cursor_first(&cursor);
375         while ((i = fr_cursor_next_by_num(&cursor, PW_EAP_MESSAGE, 0, TAG_ANY))) {
376                 memcpy(ptr, i->vp_strvalue, i->length);
377                 ptr += i->length;
378         }
379
380         return eap_packet;
381 }
382
383 /*
384  *      Add raw hex data to the reply.
385  */
386 void eap_add_reply(REQUEST *request,
387                    char const *name, uint8_t const *value, int len)
388 {
389         VALUE_PAIR *vp;
390
391         vp = pairmake_reply(name, NULL, T_OP_EQ);
392         if (!vp) {
393                 REDEBUG("Did not create attribute %s: %s\n",
394                         name, fr_strerror());
395                 return;
396         }
397
398         pairmemcpy(vp, value, len);
399 }