All Data Structures Files Functions Variables Typedefs Enumerations Enumerator Macros Groups Pages
eap_chbind.c
Go to the documentation of this file.
1 /*
2  * eap_chbind.c
3  *
4  * Version: $Id: 67c2e0d67a8a23f032ffe67258f0a8f6641d2524 $
5  *
6  * This program is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation; either version 2 of the License, or
9  * (at your option) any later version.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program; if not, write to the Free Software
18  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
19  *
20  * Copyright 2014 Network RADIUS SARL
21  * Copyright 2014 The FreeRADIUS server project
22  */
23 
24 
25 RCSID("$Id: 67c2e0d67a8a23f032ffe67258f0a8f6641d2524 $")
26 
27 #include "eap_chbind.h"
28 
29 static bool chbind_build_response(REQUEST *request, CHBIND_REQ *chbind)
30 {
31  int length;
32  size_t total;
33  uint8_t *ptr, *end;
34  VALUE_PAIR const *vp;
35  vp_cursor_t cursor;
36 
37  total = 0;
38  for (vp = fr_cursor_init(&cursor, &request->reply->vps);
39  vp != NULL;
40  vp = fr_cursor_next(&cursor)) {
41  /*
42  * Skip things which shouldn't be in channel bindings.
43  */
44  if (vp->da->flags.encrypt != FLAG_ENCRYPT_NONE) continue;
45  if (!vp->da->vendor && (vp->da->attr == PW_MESSAGE_AUTHENTICATOR)) continue;
46 
47  total += 2 + vp->vp_length;
48  }
49 
50  /*
51  * No attributes: just send a 1-byte response code.
52  */
53  if (!total) {
54  ptr = talloc_zero_array(chbind, uint8_t, 1);
55  } else {
56  ptr = talloc_zero_array(chbind, uint8_t, total + 4);
57  }
58  if (!ptr) return false;
59  chbind->response = (chbind_packet_t *) ptr;
60 
61  /*
62  * Set the response code. Default to "fail" if none was
63  * specified.
64  */
65  vp = fr_pair_find_by_num(request->config, 0, PW_CHBIND_RESPONSE_CODE, TAG_ANY);
66  if (vp) {
67  ptr[0] = vp->vp_integer;
68  } else {
69  ptr[0] = CHBIND_CODE_FAILURE;
70  }
71 
72  if (!total) return true; /* nothing to encode */
73 
74  /* Write the length field into the header */
75  ptr[1] = (total >> 8) & 0xff;
76  ptr[2] = total & 0xff;
77  ptr[3] = CHBIND_NSID_RADIUS;
78 
79  RDEBUG("Sending chbind response: code %i", (int )(ptr[0]));
80  rdebug_pair_list(L_DBG_LVL_1, request, request->reply->vps, NULL);
81 
82  /* Encode the chbind attributes into the response */
83  ptr += 4;
84  end = ptr + total;
85 
86  fr_cursor_init(&cursor, &request->reply->vps);
87  while ((vp = fr_cursor_current(&cursor)) && (ptr < end)) {
88  /*
89  * Skip things which shouldn't be in channel bindings.
90  */
91  if (vp->da->flags.encrypt != FLAG_ENCRYPT_NONE) {
92  next:
93  fr_cursor_next(&cursor);
94  continue;
95  }
96  if (!vp->da->vendor && (vp->da->attr == PW_MESSAGE_AUTHENTICATOR)) goto next;
97 
98  length = fr_radius_encode_pair(ptr, end - ptr, &cursor, NULL);
99  ptr += length;
100  }
101 
102  return true;
103 }
104 
105 
106 /*
107  * Parse channel binding packet to obtain data for a specific
108  * NSID.
109  *
110  * See:
111  * http://tools.ietf.org/html/draft-ietf-emu-chbind-13#section-5.3.2
112  */
113 static size_t chbind_get_data(chbind_packet_t const *packet,
114  int desired_nsid,
115  uint8_t const **data)
116 {
117  uint8_t const *ptr;
118  uint8_t const *end;
119 
120  if (packet->code != CHBIND_CODE_REQUEST) {
121  return 0;
122  }
123 
124  ptr = (uint8_t const *) packet;
125  end = ptr + talloc_array_length(packet);
126 
127  ptr++; /* skip the code at the start of the packet */
128  while (ptr < end) {
129  uint8_t nsid;
130  size_t length;
131 
132  /*
133  * Need room for length(2) + NSID + data.
134  */
135  if ((end - ptr) < 4) return 0;
136 
137  length = (ptr[0] << 8) | ptr[1];
138  if (length == 0) return 0;
139 
140  if ((ptr + length + 3) > end) return 0;
141 
142  nsid = ptr[2];
143  if (nsid == desired_nsid) {
144  ptr += 3;
145  *data = ptr;
146  return length;
147  }
148 
149  ptr += 3 + length;
150  }
151 
152  return 0;
153 }
154 
155 
157 {
158  PW_CODE rcode;
159  REQUEST *fake = NULL;
160  VALUE_PAIR *vp = NULL;
161  uint8_t const *attr_data;
162  size_t data_len = 0;
163 
164  /* check input parameters */
165  rad_assert((request != NULL) &&
166  (chbind != NULL) &&
167  (chbind->request != NULL) &&
168  (chbind->response == NULL));
169 
170  /* Set-up the fake request */
171  fake = request_alloc_fake(request);
172  pair_make_request("Freeradius-Proxied-To", "127.0.0.1", T_OP_EQ);
173 
174  /* Add the username to the fake request */
175  if (chbind->username) {
176  vp = fr_pair_copy(fake->packet, chbind->username);
177  fr_pair_add(&fake->packet->vps, vp);
178  fake->username = vp;
179  }
180 
181  /*
182  * Maybe copy the State over, too?
183  */
184 
185  /* Add the channel binding attributes to the fake packet */
186  data_len = chbind_get_data(chbind->request, CHBIND_NSID_RADIUS, &attr_data);
187  if (data_len) {
188  vp_cursor_t cursor;
189  rad_assert(data_len <= talloc_array_length(chbind->request));
190 
191  fr_cursor_init(&cursor, &vp);
192  while (data_len > 0) {
193  ssize_t attr_len;
194 
195  attr_len = fr_radius_decode_pair(fake->packet, &cursor, fr_dict_root(fr_dict_internal),
196  attr_data, data_len, NULL);
197  if (attr_len <= 0) {
198  /* If radaddr2vp fails, return NULL string for
199  channel binding response */
200  talloc_free(fake);
201  return PW_CODE_ACCESS_ACCEPT;
202  }
203  if (vp) {
204  fr_pair_add(&fake->packet->vps, vp);
205  }
206  attr_data += attr_len;
207  data_len -= attr_len;
208  }
209  }
210 
211  /*
212  * Set virtual server based on configuration for channel
213  * bindings, this is hard-coded for now.
214  */
215  fake->server = "channel_bindings";
217 
218  rcode = rad_virtual_server(fake);
219 
220  switch (rcode) {
221  /* If rad_authenticate succeeded, build a reply */
222  case RLM_MODULE_OK:
223  case RLM_MODULE_HANDLED:
224  if (chbind_build_response(fake, chbind)) {
225  rcode = PW_CODE_ACCESS_ACCEPT;
226  break;
227  }
228  /* FALL-THROUGH */
229 
230  /* If we got any other response from rad_authenticate, it maps to a reject */
231  default:
232  rcode = PW_CODE_ACCESS_REJECT;
233  break;
234  }
235 
236  talloc_free(fake);
237 
238  return rcode;
239 }
240 
241 /*
242  * Handles multiple EAP-channel-binding Message attrs
243  * ie concatenates all to get the complete EAP-channel-binding packet.
244  */
246 {
247  size_t length;
248  uint8_t *ptr;
249  VALUE_PAIR *first, *vp;
250  chbind_packet_t *packet;
251  vp_cursor_t cursor;
252 
254  if (!first) return NULL;
255 
256  /*
257  * Compute the total length of the channel binding data.
258  */
259  length = 0;
260  for (vp =fr_cursor_init(&cursor, &first);
261  vp != NULL;
263  length += vp->vp_length;
264  }
265 
266  if (length < 4) {
267  DEBUG("Invalid length %u for channel binding data", (unsigned int) length);
268  return NULL;
269  }
270 
271  /*
272  * Now that we know the length, allocate memory for the packet.
273  */
274  ptr = talloc_zero_array(ctx, uint8_t, length);
275  if (!ptr) return NULL;
276 
277  /*
278  * Copy the data over to our packet.
279  */
280  packet = (chbind_packet_t *) ptr;
281  for (vp = fr_cursor_init(&cursor, &first);
282  vp != NULL;
284  memcpy(ptr, vp->vp_octets, vp->vp_length);
285  ptr += vp->vp_length;
286  }
287 
288  return packet;
289 }
290 
292 {
293  VALUE_PAIR *vp;
294 
295  if (!packet) return NULL; /* don't produce garbage */
296 
298  if (!vp) return NULL;
299  fr_pair_value_memcpy(vp, (uint8_t *) packet, talloc_array_length((uint8_t *)packet));
300 
301  return vp;
302 }
VALUE_PAIR * config
VALUE_PAIR (s) used to set per request parameters for modules and the server core at runtime...
Definition: radiusd.h:227
VALUE_PAIR * eap_chbind_packet2vp(REQUEST *request, chbind_packet_t *packet)
Definition: eap_chbind.c:291
int fr_radius_encode_pair(uint8_t *out, size_t outlen, vp_cursor_t *cursor, void *encoder_ctx)
Encode a data structure into a RADIUS attribute.
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
#define PW_UKERNA_CHBIND
Definition: radius.h:222
uint8_t code
Definition: eap_chbind.h:40
#define CHBIND_NSID_RADIUS
Definition: eap_chbind.h:52
VALUE_PAIR * username
Cached username VALUE_PAIR from request RADIUS_PACKET.
Definition: radiusd.h:222
VALUE_PAIR * vps
Result of decoding the packet into VALUE_PAIRs.
Definition: libradius.h:162
#define pair_make_request(_a, _b, _c)
Definition: radiusd.h:545
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
uint8_t length
Definition: proto_bfd.c:203
Definition: token.h:46
REQUEST * request_alloc_fake(REQUEST *oldreq)
Definition: request.c:124
Abstraction to allow iterating over different configurations of VALUE_PAIRs.
Definition: pair.h:144
fr_dict_attr_flags_t flags
Flags.
Definition: dict.h:88
RFC2865 - Access-Reject.
Definition: radius.h:94
#define rad_assert(expr)
Definition: rad_assert.h:38
RFC2865 - Access-Request.
Definition: radius.h:92
static size_t chbind_get_data(chbind_packet_t const *packet, int desired_nsid, uint8_t const **data)
Definition: eap_chbind.c:113
Highest priority debug messages (-x).
Definition: log.h:51
#define DEBUG(fmt,...)
Definition: log.h:175
PW_CODE chbind_process(REQUEST *request, CHBIND_REQ *chbind)
Definition: eap_chbind.c:156
void fr_pair_add(VALUE_PAIR **head, VALUE_PAIR *vp)
Add a VP to the end of the list.
Definition: pair.c:659
unsigned int attr
Attribute number.
Definition: dict.h:79
unsigned int code
Packet code (type).
Definition: libradius.h:155
RFC2865 - Access-Accept.
Definition: radius.h:93
unsigned int vendor
Vendor that defines this attribute.
Definition: dict.h:78
Stores an attribute, a value and various bits of other data.
Definition: pair.h:112
VALUE_PAIR * fr_cursor_current(vp_cursor_t *cursor)
Return the VALUE_PAIR the cursor current points to.
Definition: cursor.c:304
RADIUS_PACKET * reply
Outgoing response.
Definition: radiusd.h:225
enum attr_flags::@0 encrypt
#define VENDORPEC_UKERNA
Definition: radius.h:203
static bool chbind_build_response(REQUEST *request, CHBIND_REQ *chbind)
Definition: eap_chbind.c:29
chbind_packet_t * request
Definition: eap_chbind.h:47
void rdebug_pair_list(log_lvl_t level, REQUEST *, VALUE_PAIR *, char const *)
Print a list of VALUE_PAIRs.
Definition: pair.c:757
chbind_packet_t * response
Definition: eap_chbind.h:48
#define CHBIND_CODE_REQUEST
Definition: eap_chbind.h:54
uint8_t data[]
Definition: eap_pwd.h:625
chbind_packet_t * eap_chbind_vp2packet(TALLOC_CTX *ctx, VALUE_PAIR *vps)
Definition: eap_chbind.c:245
#define TAG_ANY
Definition: pair.h:191
static const void * fake
Definition: rlm_sql_null.c:33
ssize_t fr_radius_decode_pair(TALLOC_CTX *ctx, vp_cursor_t *cursor, fr_dict_attr_t const *parent, uint8_t const *data, size_t data_len, void *decoder_ctx)
Create a "normal" VALUE_PAIR from the given data.
VALUE_PAIR * fr_cursor_next(vp_cursor_t *cursor)
Advanced the cursor to the next VALUE_PAIR.
Definition: cursor.c:263
RADIUS_PACKET * packet
Incoming request.
Definition: radiusd.h:221
VALUE_PAIR * fr_pair_copy(TALLOC_CTX *ctx, VALUE_PAIR const *vp)
Copy a single valuepair.
Definition: pair.c:129
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
VALUE_PAIR * username
Definition: eap_chbind.h:46
PW_CODE
RADIUS packet codes.
Definition: radius.h:90
fr_dict_attr_t const * da
Dictionary attribute defines the attribute.
Definition: pair.h:113
#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
fr_dict_t * fr_dict_internal
Internal server dictionary.
Definition: dict.c:81
fr_dict_attr_t const * fr_dict_root(fr_dict_t const *dict)
Return the root attribute of a dictionary.
Definition: dict.c:2339
#define CHBIND_CODE_FAILURE
Definition: eap_chbind.h:56
#define RDEBUG(fmt,...)
Definition: log.h:243
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