All Data Structures Files Functions Variables Typedefs Enumerations Enumerator Macros Groups Pages
eapcommon.c
Go to the documentation of this file.
1 /*
2  * eapcommon.c rfc2284 & rfc2869 implementation
3  *
4  * code common to clients and to servers.
5  *
6  * Version: $Id: cb0a56053a538845a9d1735cb707637f597747e7 $
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: cb0a56053a538845a9d1735cb707637f597747e7 $")
61 
62 #include <freeradius-devel/libradius.h>
63 #include <freeradius-devel/rad_assert.h>
64 #include "eap_types.h"
65 #include "../eap.h"
66 
68  { "notfound", EAP_NOTFOUND },
69  { "found", EAP_OK },
70  { "ok", EAP_FAIL },
71  { "fail", EAP_NOOP },
72  { "noop", EAP_INVALID },
73  { "invalid", EAP_VALID },
74  { "valid", EAP_MAX_RCODES },
75 
76  { NULL , -1 }
77 };
78 
79 /** Return an EAP-Type for a particular name
80  *
81  * Converts a name into an IANA EAP type.
82  *
83  * @param name to convert.
84  * @return
85  * - IANA EAP type.
86  * - #PW_EAP_INVALID if the name doesn't match any known types.
87  */
89 {
90  fr_dict_enum_t *dv;
91 
92  dv = fr_dict_enum_by_name(NULL, fr_dict_attr_by_num(NULL, 0, PW_EAP_TYPE), name);
93  if (!dv) return PW_EAP_INVALID;
94 
95  if (dv->value >= PW_EAP_MAX_TYPES) return PW_EAP_INVALID;
96 
97  return dv->value;
98 }
99 
100 /** Return an EAP-name for a particular type
101  *
102  * Resolve
103  */
104 char const *eap_type2name(eap_type_t method)
105 {
106  fr_dict_enum_t *dv;
107 
108  dv = fr_dict_enum_by_da(NULL, fr_dict_attr_by_num(NULL, 0, PW_EAP_TYPE), method);
109  if (dv) return dv->name;
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  */
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  */
192 {
193  VALUE_PAIR *vp;
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  fr_pair_delete_by_num(&(packet->vps), 0, PW_EAP_MESSAGE, TAG_ANY);
203 
204  vp = eap_packet2vp(packet, eap_packet);
205  if (!vp) return RLM_MODULE_INVALID;
206  fr_pair_add(&(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 = fr_pair_find_by_num(packet->vps, 0, PW_MESSAGE_AUTHENTICATOR, TAG_ANY);
216  if (!vp) {
217  vp = fr_pair_afrom_num(packet, 0, PW_MESSAGE_AUTHENTICATOR);
218  vp->vp_length = AUTH_VECTOR_LEN;
219  vp->vp_octets = talloc_zero_array(vp, uint8_t, vp->vp_length);
220 
221  fr_pair_add(&(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 
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 = fr_pair_afrom_num(packet, 0, PW_EAP_MESSAGE);
274  if (!vp) {
275  fr_pair_list_free(&head);
276  return NULL;
277  }
278  fr_pair_value_memcpy(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  */
298 {
299  VALUE_PAIR *first, *i;
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 = fr_pair_find_by_num(vps, 0, PW_EAP_MESSAGE, TAG_ANY);
310  if (!first) {
311  fr_strerror_printf("EAP-Message not found");
312  return NULL;
313  }
314 
315  /*
316  * Sanity check the length before doing anything.
317  */
318  if (first->vp_length < 4) {
319  fr_strerror_printf("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  fr_strerror_printf("EAP packet has invalid length (less than 4 bytes)");
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, 0, PW_EAP_MESSAGE, TAG_ANY))) {
344  total_len += i->vp_length;
345 
346  if (total_len > len) {
347  fr_strerror_printf("Malformed EAP packet. Length in packet header %i, "
348  "does not match actual length %i", len, total_len);
349  return NULL;
350  }
351  }
352 
353  /*
354  * If the length is SMALLER, die, too.
355  */
356  if (total_len < len) {
357  fr_strerror_printf("Malformed EAP packet. Length in packet header does not "
358  "match actual length");
359  return NULL;
360  }
361 
362  /*
363  * Now that we know the lengths are OK, allocate memory.
364  */
365  eap_packet = (eap_packet_raw_t *) talloc_zero_array(ctx, uint8_t, len);
366  if (!eap_packet) {
367  return NULL;
368  }
369 
370  /*
371  * Copy the data from EAP-Message's over to our EAP packet.
372  */
373  ptr = (unsigned char *)eap_packet;
374 
375  /* RADIUS ensures order of attrs, so just concatenate all */
376  fr_cursor_first(&cursor);
377  while ((i = fr_cursor_next_by_num(&cursor, 0, PW_EAP_MESSAGE, TAG_ANY))) {
378  memcpy(ptr, i->vp_strvalue, i->vp_length);
379  ptr += i->vp_length;
380  }
381 
382  return eap_packet;
383 }
384 
385 /*
386  * Add raw hex data to the reply.
387  */
388 void eap_add_reply(REQUEST *request,
389  char const *name, uint8_t const *value, int len)
390 {
391  VALUE_PAIR *vp;
392 
393  vp = pair_make_reply(name, NULL, T_OP_EQ);
394  if (!vp) {
395  REDEBUG("Did not create attribute %s: %s\n",
396  name, fr_strerror());
397  return;
398  }
399 
400  fr_pair_value_memcpy(vp, value, len);
401 }
402 
403 /** Send a fake request to a virtual server, managing the eap_session_t of the child
404  *
405  * If eap_session_t has a child, inject that into the fake request.
406  *
407  * If after the request has run, the child eap_session_t is no longer present,
408  * we assume it has been freed, and fixup the parent eap_session_t.
409  *
410  * If the eap_session_t pointer changes, this is considered a fatal error.
411  *
412  * @param request the current (real) request.
413  * @param eap_session representing the outer eap method.
414  * @param fake request we're going to send.
415  * @param virtual_server The default virtual server to send the request to.
416  * @return the rcode of the last executed section in the virtual server.
417  */
419  eap_session_t *eap_session, char const *virtual_server)
420 {
421  eap_session_t *inner_eap;
422  rlm_rcode_t rcode;
423  VALUE_PAIR *vp;
424 
425  vp = fr_pair_find_by_num(request->config, 0, PW_VIRTUAL_SERVER, TAG_ANY);
426  fake->server = vp ? vp->vp_strvalue : virtual_server;
427 
428  if (fake->server) {
429  RDEBUG2("Sending tunneled request to %s", fake->server);
430  } else {
431  RDEBUG2("Sending tunnelled request");
432  }
433 
434  /*
435  * Add a previously recorded inner eap_session_t back
436  * to the request. This in theory allows infinite
437  * nesting, but this is probably limited somewhere.
438  */
439  if (eap_session->child) {
440  RDEBUG4("Adding eap_session_t %p to fake request", eap_session->child);
441  request_data_add(fake, NULL, REQUEST_DATA_EAP_SESSION, eap_session->child, false, false, false);
442  }
443  rcode = rad_virtual_server(fake);
444  inner_eap = request_data_get(fake, NULL, REQUEST_DATA_EAP_SESSION);
445  if (inner_eap) {
446  if (!eap_session->child || (eap_session->child != inner_eap)) {
447  RDEBUG4("Binding lifetime of child eap_session %p to parent eap_session %p",
448  inner_eap, eap_session);
449  fr_talloc_link_ctx(eap_session, inner_eap);
450  eap_session->child = inner_eap;
451  } else {
452  RDEBUG4("Got eap_session_t %p back unmolested", eap_session->child);
453  }
454  /*
455  * Assume the inner server freed the
456  * eap_session_t and remove our reference to it.
457  *
458  * If it didn't actually free the child (due to error)
459  * the call to talloc_link_ctx (above) ensures it will
460  * be freed when the parent is.
461  */
462  } else if (eap_session->child) {
463  RDEBUG4("Inner server freed eap_session %p", eap_session->child);
464  eap_session->child = NULL;
465  }
466 
467  return rcode;
468 }
469 
void fr_pair_list_free(VALUE_PAIR **)
Free memory used by a valuepair list.
Definition: pair.c:544
uint8_t id
Definition: eap_types.h:145
uint8_t * packet
Definition: eap_types.h:138
VALUE_PAIR * fr_cursor_first(vp_cursor_t *cursor)
Rewind cursor to the start of the list.
Definition: cursor.c:105
VALUE_PAIR * config
VALUE_PAIR (s) used to set per request parameters for modules and the server core at runtime...
Definition: radiusd.h:227
Ok, continue.
Definition: eap_types.h:109
RFC2865 - Access-Challenge.
Definition: radius.h:102
Invalid, don't reply.
Definition: eap_types.h:112
The module is OK, continue.
Definition: radiusd.h:91
VALUE_PAIR * fr_pair_afrom_num(TALLOC_CTX *ctx, unsigned int vendor, unsigned int attr)
Create a new valuepair.
Definition: pair.c:106
VALUE_PAIR * fr_cursor_next_by_num(vp_cursor_t *cursor, unsigned int vendor, unsigned int attr, int8_t tag)
Iterate over a collection of VALUE_PAIRs of a given type in the pairlist.
Definition: cursor.c:200
eap_type_t eap_name2type(char const *name)
Return an EAP-Type for a particular name.
Definition: eapcommon.c:88
static char const * name
VALUE_PAIR * vps
Result of decoding the packet into VALUE_PAIRs.
Definition: libradius.h:162
uint8_t code
Definition: eap_types.h:144
#define EAP_HEADER_LEN
Definition: eap_types.h:35
VALUE_PAIR * fr_cursor_init(vp_cursor_t *cursor, VALUE_PAIR *const *node)
Setup a cursor to iterate over attribute pairs.
Definition: cursor.c:60
Definition: token.h:46
The module considers the request invalid.
Definition: radiusd.h:93
eap_session_t * child
Session for tunnelled EAP method.
Definition: eap.h:63
VALUE_PAIR * eap_packet2vp(RADIUS_PACKET *packet, eap_packet_raw_t const *eap)
Definition: eapcommon.c:251
eap_type_data_t type
Definition: eap_types.h:136
fr_dict_enum_t * fr_dict_enum_by_name(fr_dict_t *dict, fr_dict_attr_t const *da, char const *val)
Definition: dict.c:3703
Abstraction to allow iterating over different configurations of VALUE_PAIRs.
Definition: pair.h:144
RFC2865 - Access-Reject.
Definition: radius.h:94
enum eap_method eap_type_t
eap_type_t num
Definition: eap_types.h:122
#define DEBUG(fmt,...)
Definition: log.h:175
#define AUTH_VECTOR_LEN
Definition: libradius.h:118
uint8_t id
Definition: eap_types.h:134
Structure to represent packet format of eap on wire
Definition: eap_types.h:143
void fr_cursor_insert(vp_cursor_t *cursor, VALUE_PAIR *vp)
Insert a single VALUE_PAIR at the end of the list.
Definition: cursor.c:321
void fr_pair_add(VALUE_PAIR **head, VALUE_PAIR *vp)
Add a VP to the end of the list.
Definition: pair.c:659
Tracks the progress of a single session of any EAP method.
Definition: eap.h:60
Immediately reject the request.
Definition: radiusd.h:89
void eap_add_reply(REQUEST *request, char const *name, uint8_t const *value, int len)
Definition: eapcommon.c:388
unsigned int code
Packet code (type).
Definition: libradius.h:155
void * request_data_get(REQUEST *request, void *unique_ptr, int unique_int)
Get opaque data from a request.
Definition: request.c:374
RFC2865 - Access-Accept.
Definition: radius.h:93
Failed, don't reply.
Definition: eap_types.h:110
Stores an attribute, a value and various bits of other data.
Definition: pair.h:112
size_t length
Definition: eap_types.h:123
uint8_t length[2]
Definition: eap_types.h:146
int eap_wireformat(eap_packet_t *reply)
Definition: eapcommon.c:128
uint8_t data[1]
Definition: eap_types.h:147
enum rlm_rcodes rlm_rcode_t
Return codes indicating the result of the module call.
char const * fr_strerror(void)
Get the last library error.
Definition: log.c:212
Valid, continue.
Definition: eap_types.h:113
char name[1]
Enum name.
Definition: dict.h:97
void fr_pair_delete_by_num(VALUE_PAIR **head, unsigned int vendor, unsigned int attr, int8_t tag)
Delete matching pairs.
Definition: pair.c:797
#define REQUEST_DATA_EAP_SESSION
Definition: eap.h:106
#define RDEBUG2(fmt,...)
Definition: log.h:244
char const * eap_type2name(eap_type_t method)
Return an EAP-name for a particular type.
Definition: eapcommon.c:104
#define TAG_ANY
Definition: pair.h:191
static const void * fake
Definition: rlm_sql_null.c:33
eap_packet_raw_t * eap_vp2packet(TALLOC_CTX *ctx, VALUE_PAIR *vps)
Definition: eapcommon.c:297
#define pair_make_reply(_a, _b, _c)
Definition: radiusd.h:546
int request_data_add(REQUEST *request, void *unique_ptr, int unique_int, void *opaque, bool free_on_replace, bool free_on_parent, bool persist)
Add opaque data to a REQUEST.
Definition: request.c:279
fr_dict_enum_t * fr_dict_enum_by_da(fr_dict_t *dict, fr_dict_attr_t const *da, int value)
Lookup the structure representing an enum value in a fr_dict_attr_t.
Definition: dict.c:3654
int value
Enum value.
Definition: dict.h:96
void fr_strerror_printf(char const *,...) CC_HINT(format(printf
#define REDEBUG(fmt,...)
Definition: log.h:254
int fr_talloc_link_ctx(TALLOC_CTX *parent, TALLOC_CTX *child)
Link a parent and a child context, so the child is freed before the parent.
Definition: misc.c:105
rlm_rcode_t eap_virtual_server(REQUEST *request, REQUEST *fake, eap_session_t *eap_session, char const *virtual_server)
Send a fake request to a virtual server, managing the eap_session_t of the child. ...
Definition: eapcommon.c:418
VALUE_PAIR * fr_pair_find_by_num(VALUE_PAIR *head, unsigned int vendor, unsigned int attr, int8_t tag)
Find the pair with the matching attribute.
Definition: pair.c:639
EAP eap_session data not found.
Definition: eap_types.h:107
int eap_basic_compose(RADIUS_PACKET *packet, eap_packet_t *reply)
Definition: eapcommon.c:191
Succeeded without doing anything.
Definition: eap_types.h:111
Structure to hold EAP data.
Definition: eap_types.h:132
const FR_NAME_NUMBER eap_rcode_table[]
Definition: eapcommon.c:67
#define RDEBUG4(fmt,...)
Definition: log.h:246
fr_dict_attr_t const * fr_dict_attr_by_num(fr_dict_t *dict, unsigned int vendor, unsigned int attr)
Lookup a fr_dict_attr_t by its vendor and attribute numbers.
Definition: dict.c:3519
#define RCSID(id)
Definition: build.h:135
int rad_virtual_server(REQUEST *)
Definition: auth.c:659
The module handled the request, so stop.
Definition: radiusd.h:92
#define ERROR(fmt,...)
Definition: log.h:145
eap_code_t code
Definition: eap_types.h:133
Value of an enumerated attribute.
Definition: dict.h:94
void fr_pair_value_memcpy(VALUE_PAIR *vp, uint8_t const *src, size_t len)
Copy data into an "octets" data type.
Definition: pair.c:1905
char const * server
Definition: radiusd.h:289
uint8_t * data
Definition: eap_types.h:124