The FreeRADIUS server  $Id: 15bac2a4c627c01d1aa2047687b3418955ac7f00 $
chbind.c
Go to the documentation of this file.
1 /*
2  * This program is is free software; you can redistribute it and/or modify
3  * it under the terms of the GNU General Public License as published by
4  * the Free Software Foundation; either version 2 of the License, or (at
5  * your option) any later version.
6  *
7  * This program is distributed in the hope that it will be useful,
8  * but WITHOUT ANY WARRANTY; without even the implied warranty of
9  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10  * GNU General Public License for more details.
11  *
12  * You should have received a copy of the GNU General Public License
13  * along with this program; if not, write to the Free Software
14  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
15  */
16 
17 /**
18  * $Id: b9ebd81311e53e153d1447dfe3dee6a601c6d668 $
19  * @file lib/eap/chbind.c
20  * @brief Channel binding
21  *
22  * @copyright 2014 Network RADIUS SAS
23  * @copyright 2014 The FreeRADIUS server project
24  */
25 
26 RCSID("$Id: b9ebd81311e53e153d1447dfe3dee6a601c6d668 $")
27 
28 #include <freeradius-devel/radius/defs.h>
29 #include <freeradius-devel/radius/radius.h>
30 
31 #include <freeradius-devel/io/pair.h>
32 #include "chbind.h"
33 #include "attrs.h"
34 
35 static bool chbind_build_response(request_t *request, CHBIND_REQ *chbind)
36 {
37  ssize_t slen;
38  size_t total;
39  uint8_t *ptr, *end;
40  fr_pair_t const *vp;
41  fr_dcursor_t cursor;
42 
43  total = 0;
44  for (vp = fr_pair_list_head(&request->reply_pairs);
45  vp != NULL;
46  vp = fr_pair_list_next(&request->reply_pairs, vp)) {
47  /*
48  * Skip things which shouldn't be in channel bindings.
49  */
50  if (vp->da->flags.internal || (!vp->da->flags.extra && vp->da->flags.subtype)) continue;
51  if (vp->da == attr_message_authenticator) continue;
52 
53  total += 2 + vp->vp_length;
54  }
55 
56  /*
57  * No attributes: just send a 1-byte response code.
58  */
59  if (!total) {
60  ptr = talloc_zero_array(chbind, uint8_t, 1);
61  } else {
62  ptr = talloc_zero_array(chbind, uint8_t, total + 4);
63  }
64  if (!ptr) return false;
65  chbind->response = (chbind_packet_t *) ptr;
66 
67  /*
68  * Set the response code. Default to "fail" if none was
69  * specified.
70  */
71  vp = fr_pair_find_by_da(&request->control_pairs, NULL, attr_chbind_response_code);
72  if (vp) {
73  ptr[0] = vp->vp_uint32;
74  } else {
75  ptr[0] = CHBIND_CODE_FAILURE;
76  }
77 
78  if (!total) return true; /* nothing to encode */
79 
80  /* Write the length field into the header */
81  ptr[1] = (total >> 8) & 0xff;
82  ptr[2] = total & 0xff;
83  ptr[3] = CHBIND_NSID_RADIUS;
84 
85  RDEBUG2("Sending chbind response: code %i", (int )(ptr[0]));
86  log_request_pair_list(L_DBG_LVL_1, request, NULL, &request->reply_pairs, NULL);
87 
88  /* Encode the chbind attributes into the response */
89  ptr += 4;
90  end = ptr + total;
91 
92  fr_pair_dcursor_init(&cursor, &request->reply_pairs);
93  while ((vp = fr_dcursor_current(&cursor)) && (ptr < end)) {
94  /*
95  * Skip things which shouldn't be in channel bindings.
96  * i.e. tagged, encrypted, or extended attributes
97  */
98  if (vp->da->flags.subtype) {
99  next:
100  fr_dcursor_next(&cursor);
101  continue;
102  }
103  if (vp->da == attr_message_authenticator) goto next;
104 
105  slen = fr_radius_encode_pair(&FR_DBUFF_TMP(ptr, end), &cursor, NULL);
106  if (slen < 0) {
107  RPERROR("Failed encoding chbind response");
108 
109  talloc_free(ptr);
110  return false;
111  }
112  ptr += slen;
113  }
114 
115  return true;
116 }
117 
118 
119 /*
120  * Parse channel binding packet to obtain data for a specific
121  * NSID.
122  *
123  * See:
124  * http://tools.ietf.org/html/draft-ietf-emu-chbind-13#section-5.3.2
125  */
126 static size_t chbind_get_data(chbind_packet_t const *packet,
127  int desired_nsid,
128  uint8_t const **data)
129 {
130  uint8_t const *ptr;
131  uint8_t const *end;
132 
133  if (packet->code != CHBIND_CODE_REQUEST) {
134  return 0;
135  }
136 
137  ptr = (uint8_t const *) packet;
138  end = ptr + talloc_array_length((uint8_t const *) packet);
139 
140  ptr++; /* skip the code at the start of the packet */
141  while (ptr < end) {
142  uint8_t nsid;
143  size_t length;
144 
145  /*
146  * Need room for length(2) + NSID + data.
147  */
148  if ((end - ptr) < 4) return 0;
149 
150  length = fr_nbo_to_uint16(ptr);
151  if (length == 0) return 0;
152 
153  if ((ptr + length + 3) > end) return 0;
154 
155  nsid = ptr[2];
156  if (nsid == desired_nsid) {
157  ptr += 3;
158  *data = ptr;
159  return length;
160  }
161 
162  ptr += 3 + length;
163  }
164 
165  return 0;
166 }
167 
168 
170 {
172  rlm_rcode_t rcode;
173  request_t *fake = NULL;
174  uint8_t const *attr_data;
175  size_t data_len = 0;
176  fr_pair_t *vp;
177  fr_radius_ctx_t common_ctx = {};
178  fr_radius_decode_ctx_t packet_ctx = {};
179 
180  /* check input parameters */
181  fr_assert((request != NULL) &&
182  (chbind != NULL) &&
183  (chbind->request != NULL) &&
184  (chbind->response == NULL));
185 
186  /* Set-up the fake request */
187  fake = request_alloc_internal(request, &(request_init_args_t){ .parent = request });
188  MEM(fr_pair_prepend_by_da(fake->request_ctx, &vp, &fake->request_pairs, attr_freeradius_proxied_to) >= 0);
189  (void) fr_pair_value_from_str(vp, "127.0.0.1", sizeof("127.0.0.1") - 1, NULL, false);
190 
191  /* Add the username to the fake request */
192  if (chbind->username) {
193  vp = fr_pair_copy(fake->request_ctx, chbind->username);
194  fr_pair_append(&fake->request_pairs, vp);
195  }
196 
197  /*
198  * Maybe copy the State over, too?
199  */
200  packet_ctx.common = &common_ctx;
201  packet_ctx.tmp_ctx = talloc_init_const("tmp");
202 
203  /* Add the channel binding attributes to the fake packet */
204  data_len = chbind_get_data(chbind->request, CHBIND_NSID_RADIUS, &attr_data);
205  if (data_len) {
206  fr_assert(data_len <= talloc_array_length((uint8_t const *) chbind->request));
207 
208  packet_ctx.end = attr_data + data_len;
209 
210  while (data_len > 0) {
211  ssize_t attr_len;
212 
213  attr_len = fr_radius_decode_pair(fake->request_ctx, &fake->request_pairs,
214  attr_data, data_len, &packet_ctx);
215  if (attr_len <= 0) {
216  /*
217  * If fr_radius_decode_pair fails, return NULL string for
218  * channel binding response.
219  */
220  talloc_free(fake);
221  talloc_free(packet_ctx.tmp_ctx);
222  talloc_free(packet_ctx.tags);
223 
225  }
226  attr_data += attr_len;
227  data_len -= attr_len;
228  }
229 
230  talloc_free_children(packet_ctx.tmp_ctx);
231  }
232  talloc_free(packet_ctx.tmp_ctx);
233  talloc_free(packet_ctx.tags);
234 
235  /*
236  * Set virtual server based on configuration for channel
237  * bindings, this is hard-coded for now.
238  */
239 // fake->server_cs = virtual_server_find("channel_bindings");
240  fake->packet->code = FR_RADIUS_CODE_ACCESS_REQUEST;
241 
242  rad_virtual_server(&rcode, fake);
243  switch (rcode) {
244  /* If the virtual server succeeded, build a reply */
245  case RLM_MODULE_OK:
246  case RLM_MODULE_HANDLED:
247  if (chbind_build_response(fake, chbind)) {
249  break;
250  }
251  FALL_THROUGH;
252 
253  /* If we got any other response from the virtual server, it maps to a reject */
254  default:
256  break;
257  }
258 
259  talloc_free(fake);
260 
261  return code;
262 }
263 
264 /*
265  * Handles multiple EAP-channel-binding Message attrs
266  * ie concatenates all to get the complete EAP-channel-binding packet.
267  */
269 {
270  size_t length;
271  uint8_t *ptr;
272  fr_pair_t *vp;
273  chbind_packet_t *packet;
274  fr_dcursor_t cursor;
275 
276  if (!fr_pair_dcursor_by_da_init(&cursor, vps, attr_eap_channel_binding_message)) return NULL;
277 
278  /*
279  * Compute the total length of the channel binding data.
280  */
281  length = 0;
282  for (vp = fr_dcursor_current(&cursor);
283  vp;
284  vp = fr_dcursor_next(&cursor)) {
285  length += vp->vp_length;
286  }
287 
288  if (length < 4) {
289  DEBUG("Invalid length %u for channel binding data", (unsigned int) length);
290  return NULL;
291  }
292 
293  /*
294  * Now that we know the length, allocate memory for the packet.
295  */
296  ptr = talloc_zero_array(ctx, uint8_t, length);
297  if (!ptr) return NULL;
298 
299  /*
300  * Copy the data over to our packet.
301  */
302  packet = (chbind_packet_t *) ptr;
303  for (vp = fr_dcursor_head(&cursor);
304  vp != NULL;
305  vp = fr_dcursor_next(&cursor)) {
306  memcpy(ptr, vp->vp_octets, vp->vp_length);
307  ptr += vp->vp_length;
308  }
309 
310  return packet;
311 }
312 
314 {
315  fr_pair_t *vp;
316 
317  if (!chbind) return NULL; /* don't produce garbage */
318 
320  fr_pair_value_memdup(vp, (uint8_t *) chbind, talloc_array_length((uint8_t *)chbind), false);
321 
322  return vp;
323 }
unlang_action_t rad_virtual_server(rlm_rcode_t *p_result, request_t *request)
Definition: auth.c:48
#define RCSID(id)
Definition: build.h:444
#define FALL_THROUGH
clang 10 doesn't recognised the FALL-THROUGH comment anymore
Definition: build.h:320
static size_t chbind_get_data(chbind_packet_t const *packet, int desired_nsid, uint8_t const **data)
Definition: chbind.c:126
fr_radius_packet_code_t chbind_process(request_t *request, CHBIND_REQ *chbind)
Definition: chbind.c:169
static bool chbind_build_response(request_t *request, CHBIND_REQ *chbind)
Definition: chbind.c:35
chbind_packet_t * eap_chbind_vp2packet(TALLOC_CTX *ctx, fr_pair_list_t *vps)
Definition: chbind.c:268
fr_pair_t * eap_chbind_packet2vp(TALLOC_CTX *ctx, chbind_packet_t *chbind)
Definition: chbind.c:313
Channel binding.
chbind_packet_t * response
Definition: chbind.h:49
#define CHBIND_NSID_RADIUS
Definition: chbind.h:53
fr_pair_t * username
Definition: chbind.h:47
chbind_packet_t * request
Definition: chbind.h:48
uint8_t code
Definition: chbind.h:41
#define CHBIND_CODE_REQUEST
Definition: chbind.h:55
#define CHBIND_CODE_FAILURE
Definition: chbind.h:57
#define FR_DBUFF_TMP(_start, _len_or_end)
Creates a compound literal to pass into functions which accept a dbuff.
Definition: dbuff.h:509
static void * fr_dcursor_next(fr_dcursor_t *cursor)
Advanced the cursor to the next item.
Definition: dcursor.h:287
static void * fr_dcursor_head(fr_dcursor_t *cursor)
Rewind cursor to the start of the list.
Definition: dcursor.h:233
static void * fr_dcursor_current(fr_dcursor_t *cursor)
Return the item the cursor current points to.
Definition: dcursor.h:336
fr_radius_packet_code_t
RADIUS packet codes.
Definition: defs.h:31
@ FR_RADIUS_CODE_ACCESS_REQUEST
RFC2865 - Access-Request.
Definition: defs.h:33
@ FR_RADIUS_CODE_ACCESS_ACCEPT
RFC2865 - Access-Accept.
Definition: defs.h:34
@ FR_RADIUS_CODE_ACCESS_REJECT
RFC2865 - Access-Reject.
Definition: defs.h:35
#define DEBUG(fmt,...)
Definition: dhcpclient.c:39
HIDDEN fr_dict_attr_t const * attr_chbind_response_code
Definition: base.c:83
HIDDEN fr_dict_attr_t const * attr_freeradius_proxied_to
Definition: base.c:93
HIDDEN fr_dict_attr_t const * attr_eap_channel_binding_message
Definition: base.c:89
void log_request_pair_list(fr_log_lvl_t lvl, request_t *request, fr_pair_t const *parent, fr_pair_list_t const *vps, char const *prefix)
Print a fr_pair_list_t.
Definition: log.c:821
#define RPERROR(fmt,...)
Definition: log.h:302
talloc_free(reap)
@ L_DBG_LVL_1
Highest priority debug messages (-x).
Definition: log.h:70
long int ssize_t
Definition: merged_model.c:24
unsigned char uint8_t
Definition: merged_model.c:30
static uint16_t fr_nbo_to_uint16(uint8_t const data[static sizeof(uint16_t)])
Read an unsigned 16bit integer from wire format (big endian)
Definition: nbo.h:137
fr_pair_t * fr_pair_find_by_da(fr_pair_list_t const *list, fr_pair_t const *prev, fr_dict_attr_t const *da)
Find the first pair with a matching da.
Definition: pair.c:688
fr_pair_t * fr_pair_afrom_da(TALLOC_CTX *ctx, fr_dict_attr_t const *da)
Dynamically allocate a new attribute and assign a fr_dict_attr_t.
Definition: pair.c:278
int fr_pair_value_memdup(fr_pair_t *vp, uint8_t const *src, size_t len, bool tainted)
Copy data into an "octets" data type.
Definition: pair.c:2978
int fr_pair_append(fr_pair_list_t *list, fr_pair_t *to_add)
Add a VP to the end of the list.
Definition: pair.c:1340
fr_pair_t * fr_pair_copy(TALLOC_CTX *ctx, fr_pair_t const *vp)
Copy a single valuepair.
Definition: pair.c:484
int fr_pair_value_from_str(fr_pair_t *vp, char const *value, size_t inlen, fr_sbuff_unescape_rules_t const *uerules, bool tainted)
Convert string value to native attribute value.
Definition: pair.c:2586
int fr_pair_prepend_by_da(TALLOC_CTX *ctx, fr_pair_t **out, fr_pair_list_t *list, fr_dict_attr_t const *da)
Alloc a new fr_pair_t (and prepend)
Definition: pair.c:1488
ssize_t fr_radius_decode_pair(TALLOC_CTX *ctx, fr_pair_list_t *out, uint8_t const *data, size_t data_len, fr_radius_decode_ctx_t *packet_ctx)
Create a "normal" fr_pair_t from the given data.
Definition: decode.c:1982
ssize_t fr_radius_encode_pair(fr_dbuff_t *dbuff, fr_dcursor_t *cursor, void *encode_ctx)
Encode a data structure into a RADIUS attribute.
Definition: encode.c:1498
VQP attributes.
#define RDEBUG2(fmt,...)
Definition: radclient.h:54
fr_radius_tag_ctx_t ** tags
for decoding tagged attributes
Definition: radius.h:150
uint8_t const * end
end of the packet
Definition: radius.h:142
fr_radius_ctx_t * common
Definition: radius.h:137
TALLOC_CTX * tmp_ctx
for temporary things cleaned up during decoding
Definition: radius.h:141
static fr_dict_attr_t const * attr_message_authenticator
Definition: radsnmp.c:112
rlm_rcode_t
Return codes indicating the result of the module call.
Definition: rcode.h:40
@ RLM_MODULE_OK
The module is OK, continue.
Definition: rcode.h:43
@ RLM_MODULE_HANDLED
The module handled the request, so stop.
Definition: rcode.h:44
#define request_alloc_internal(_ctx, _args)
Allocate a new internal request.
Definition: request.h:304
Optional arguments for initialising requests.
Definition: request.h:253
static const void * fake
Definition: rlm_sql_null.c:30
fr_assert(0)
MEM(pair_append_request(&vp, attr_eap_aka_sim_identity) >=0)
fr_pair_t * vp
Stores an attribute, a value and various bits of other data.
Definition: pair.h:68
fr_dict_attr_t const *_CONST da
Dictionary attribute defines the attribute number, vendor and type of the pair.
Definition: pair.h:69
static TALLOC_CTX * talloc_init_const(char const *name)
Allocate a top level chunk with a constant name.
Definition: talloc.h:112
fr_pair_t * fr_pair_list_head(fr_pair_list_t const *list)
Get the head of a valuepair list.
Definition: pair_inline.c:43
#define fr_pair_dcursor_by_da_init(_cursor, _list, _da)
Initialise a cursor that will return only attributes matching the specified fr_dict_attr_t.
Definition: pair.h:627
fr_pair_t * fr_pair_list_next(fr_pair_list_t const *list, fr_pair_t const *item))
Get the next item in a valuepair list after a specific entry.
Definition: pair_inline.c:70
#define fr_pair_dcursor_init(_cursor, _list)
Initialises a special dcursor with callbacks that will maintain the attr sublists correctly.
Definition: pair.h:590
static fr_slen_t data
Definition: value.h:1259