The FreeRADIUS server $Id: 15bac2a4c627c01d1aa2047687b3418955ac7f00 $
Loading...
Searching...
No Matches
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: fb2881fd45834f51bea41c3655c3c0f028c539ff $
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
26RCSID("$Id: fb2881fd45834f51bea41c3655c3c0f028c539ff $")
27
28#include <freeradius-devel/radius/defs.h>
29#include <freeradius-devel/radius/radius.h>
30#include <freeradius-devel/server/auth.h>
31
32#include <freeradius-devel/io/pair.h>
33#include "chbind.h"
34#include "attrs.h"
35
36static bool chbind_build_response(request_t *request, CHBIND_REQ *chbind)
37{
38 ssize_t slen;
39 size_t total;
40 uint8_t *ptr, *start, *end;
41 fr_pair_t const *vp;
42 fr_dcursor_t cursor;
43
44 total = 0;
45 for (vp = fr_pair_list_head(&request->reply_pairs);
46 vp != NULL;
47 vp = fr_pair_list_next(&request->reply_pairs, vp)) {
48 /*
49 * Skip things which shouldn't be in channel bindings.
50 */
51 if (vp->da->flags.internal || (!vp->da->flags.extra && vp->da->flags.subtype)) continue;
52 if (vp->da == attr_message_authenticator) continue;
53
54 total += 2 + vp->vp_length;
55 }
56
57 /*
58 * No attributes: just send a 1-byte response code.
59 */
60 if (!total) {
61 ptr = start = talloc_zero_array(chbind, uint8_t, 1);
62 } else {
63 ptr = start = talloc_zero_array(chbind, uint8_t, total + 4);
64 }
65 if (!ptr) return false;
66 chbind->response = (chbind_packet_t *) ptr;
67
68 /*
69 * Set the response code. Default to "fail" if none was
70 * specified.
71 */
72 vp = fr_pair_find_by_da(&request->control_pairs, NULL, attr_chbind_response_code);
73 if (vp) {
74 ptr[0] = vp->vp_uint32;
75 } else {
76 ptr[0] = CHBIND_CODE_FAILURE;
77 }
78
79 if (!total) return true; /* nothing to encode */
80
81 /* Write the length field into the header */
82 ptr[1] = (total >> 8) & 0xff;
83 ptr[2] = total & 0xff;
84 ptr[3] = CHBIND_NSID_RADIUS;
85
86 RDEBUG2("Sending chbind response: code %i", (int )(ptr[0]));
87 log_request_pair_list(L_DBG_LVL_1, request, NULL, &request->reply_pairs, NULL);
88
89 /* Encode the chbind attributes into the response */
90 ptr += 4;
91 end = ptr + total;
92
93 fr_pair_dcursor_init(&cursor, &request->reply_pairs);
94 while ((vp = fr_dcursor_current(&cursor)) && (ptr < end)) {
95 /*
96 * Skip things which shouldn't be in channel bindings.
97 */
98 if (vp->da->flags.internal || (!vp->da->flags.extra && vp->da->flags.subtype) ||
100 next:
101 fr_dcursor_next(&cursor);
102 continue;
103 }
104
105 if (vp->da == attr_message_authenticator) goto next;
106
107 slen = fr_radius_encode_pair(&FR_DBUFF_TMP(ptr, end), &cursor, NULL);
108 if (slen < 0) {
109 RPERROR("Failed encoding chbind response");
110 chbind->response = NULL;
111 talloc_free(start);
112 return false;
113 }
114
115 ptr += slen;
116 }
117
118 return true;
119}
120
121
122/*
123 * Parse channel binding packet to obtain data for a specific
124 * NSID.
125 *
126 * See:
127 * http://tools.ietf.org/html/draft-ietf-emu-chbind-13#section-5.3.2
128 */
129static size_t chbind_get_data(chbind_packet_t const *packet,
130 int desired_nsid,
131 uint8_t const **data)
132{
133 uint8_t const *ptr;
134 uint8_t const *end;
135
136 if (packet->code != CHBIND_CODE_REQUEST) {
137 return 0;
138 }
139
140 ptr = (uint8_t const *) packet;
141 end = ptr + talloc_array_length((uint8_t const *) packet);
142
143 ptr++; /* skip the code at the start of the packet */
144 while (ptr < end) {
145 uint8_t nsid;
146 size_t length;
147
148 /*
149 * Need room for length(2) + NSID + data.
150 */
151 if ((end - ptr) < 4) return 0;
152
153 length = fr_nbo_to_uint16(ptr);
154 if (length == 0) return 0;
155
156 if ((ptr + length + 3) > end) return 0;
157
158 nsid = ptr[2];
159 if (nsid == desired_nsid) {
160 ptr += 3;
161 *data = ptr;
162 return length;
163 }
164
165 ptr += 3 + length;
166 }
167
168 return 0;
169}
170
171
173{
175 unlang_result_t result;
176 request_t *fake = NULL;
177 uint8_t const *attr_data;
178 size_t data_len = 0;
179 fr_pair_t *vp;
180 fr_radius_ctx_t common_ctx = {};
181 fr_radius_decode_ctx_t packet_ctx = {};
182
183 /* check input parameters */
184 fr_assert((request != NULL) &&
185 (chbind != NULL) &&
186 (chbind->request != NULL) &&
187 (chbind->response == NULL));
188
189 /* Set-up the fake request */
190 fake = request_local_alloc_internal(request, &(request_init_args_t){ .parent = request });
191 MEM(fr_pair_prepend_by_da(fake->request_ctx, &vp, &fake->request_pairs, attr_freeradius_proxied_to) >= 0);
192 (void) fr_pair_value_from_str(vp, "127.0.0.1", sizeof("127.0.0.1") - 1, NULL, false);
193
194 /* Add the username to the fake request */
195 if (chbind->username) {
196 MEM(vp = fr_pair_copy(fake->request_ctx, chbind->username));
197 fr_pair_append(&fake->request_pairs, vp);
198 }
199
200 /*
201 * Maybe copy the State over, too?
202 */
203 packet_ctx.common = &common_ctx;
204 packet_ctx.tmp_ctx = talloc_init_const("tmp");
205
206 /* Add the channel binding attributes to the fake packet */
207 data_len = chbind_get_data(chbind->request, CHBIND_NSID_RADIUS, &attr_data);
208 if (data_len) {
209 fr_assert(data_len <= talloc_array_length((uint8_t const *) chbind->request));
210
211 packet_ctx.end = attr_data + data_len;
212
213 while (data_len > 0) {
214 ssize_t attr_len;
215
216 attr_len = fr_radius_decode_pair(fake->request_ctx, &fake->request_pairs,
217 attr_data, data_len, &packet_ctx);
218 if (attr_len <= 0) {
219 /*
220 * If fr_radius_decode_pair fails, return NULL string for
221 * channel binding response.
222 */
224 talloc_free(packet_ctx.tmp_ctx);
225 talloc_free(packet_ctx.tags);
226
228 }
229 attr_data += attr_len;
230 data_len -= attr_len;
231 }
232
233 talloc_free_children(packet_ctx.tmp_ctx);
234 }
235 talloc_free(packet_ctx.tmp_ctx);
236 talloc_free(packet_ctx.tags);
237
238 /*
239 * Set virtual server based on configuration for channel
240 * bindings, this is hard-coded for now.
241 */
242// fake->server_cs = virtual_server_find("channel_bindings");
243 fake->packet->code = FR_RADIUS_CODE_ACCESS_REQUEST;
244
245 rad_virtual_server(&result, fake);
246 switch (result.rcode) {
247 /* If the virtual server succeeded, build a reply */
248 case RLM_MODULE_OK:
250 if (chbind_build_response(fake, chbind)) {
252 break;
253 }
255
256 /* If we got any other response from the virtual server, it maps to a reject */
257 default:
259 break;
260 }
261
263
264 return code;
265}
266
267/*
268 * Handles multiple EAP-channel-binding Message attrs
269 * ie concatenates all to get the complete EAP-channel-binding packet.
270 */
272{
273 size_t length;
274 uint8_t *ptr;
275 fr_pair_t *vp;
276 chbind_packet_t *packet;
277 fr_dcursor_t cursor;
278
279 if (!fr_pair_dcursor_by_da_init(&cursor, vps, attr_eap_channel_binding_message)) return NULL;
280
281 /*
282 * Compute the total length of the channel binding data.
283 */
284 length = 0;
285 for (vp = fr_dcursor_current(&cursor);
286 vp;
287 vp = fr_dcursor_next(&cursor)) {
288 length += vp->vp_length;
289 }
290
291 if (length < 4) {
292 DEBUG("Invalid length %u for channel binding data", (unsigned int) length);
293 return NULL;
294 }
295
296 /*
297 * Now that we know the length, allocate memory for the packet.
298 */
299 ptr = talloc_zero_array(ctx, uint8_t, length);
300 if (!ptr) return NULL;
301
302 /*
303 * Copy the data over to our packet.
304 */
305 packet = (chbind_packet_t *) ptr;
306 for (vp = fr_dcursor_head(&cursor);
307 vp != NULL;
308 vp = fr_dcursor_next(&cursor)) {
309 memcpy(ptr, vp->vp_octets, vp->vp_length);
310 ptr += vp->vp_length;
311 }
312
313 return packet;
314}
315
317{
318 fr_pair_t *vp;
319
320 if (!chbind) return NULL; /* don't produce garbage */
321
323 fr_pair_value_memdup(vp, (uint8_t *) chbind, talloc_array_length((uint8_t *)chbind), false);
324
325 return vp;
326}
unlang_action_t rad_virtual_server(unlang_result_t *p_result, request_t *request)
Definition auth.c:44
#define RCSID(id)
Definition build.h:487
#define FALL_THROUGH
clang 10 doesn't recognised the FALL-THROUGH comment anymore
Definition build.h:324
static size_t chbind_get_data(chbind_packet_t const *packet, int desired_nsid, uint8_t const **data)
Definition chbind.c:129
fr_pair_t * eap_chbind_packet2vp(TALLOC_CTX *ctx, chbind_packet_t *chbind)
Definition chbind.c:316
fr_radius_packet_code_t chbind_process(request_t *request, CHBIND_REQ *chbind)
Definition chbind.c:172
static bool chbind_build_response(request_t *request, CHBIND_REQ *chbind)
Definition chbind.c:36
chbind_packet_t * eap_chbind_vp2packet(TALLOC_CTX *ctx, fr_pair_list_t *vps)
Definition chbind.c:271
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:524
static void * fr_dcursor_next(fr_dcursor_t *cursor)
Advanced the cursor to the next item.
Definition dcursor.h:290
static void * fr_dcursor_current(fr_dcursor_t *cursor)
Return the item the cursor current points to.
Definition dcursor.h:339
static void * fr_dcursor_head(fr_dcursor_t *cursor)
Rewind cursor to the start of the list.
Definition dcursor.h:234
#define MEM(x)
Definition debug.h:36
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
talloc_free(hp)
rlm_rcode_t rcode
The current rcode, from executing the instruction or merging the result from a frame.
Definition interpret.h:134
HIDDEN fr_dict_attr_t const * attr_chbind_response_code
Definition base.c:89
HIDDEN fr_dict_attr_t const * attr_freeradius_proxied_to
Definition base.c:100
HIDDEN fr_dict_attr_t const * attr_eap_channel_binding_message
Definition base.c:95
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:820
#define RPERROR(fmt,...)
Definition log.h:314
@ L_DBG_LVL_1
Highest priority debug messages (-x).
Definition log.h:70
long int ssize_t
unsigned char uint8_t
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:146
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:2945
int fr_pair_value_from_str(fr_pair_t *vp, char const *value, size_t inlen, fr_sbuff_unescape_rules_t const *uerules, UNUSED bool tainted)
Convert string value to native attribute value.
Definition pair.c:2600
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:704
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:1349
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:290
fr_pair_t * fr_pair_copy(TALLOC_CTX *ctx, fr_pair_t const *vp)
Copy a single valuepair.
Definition pair.c:498
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:1495
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:1972
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:1537
VQP attributes.
#define fr_assert(_expr)
Definition rad_assert.h:38
#define RDEBUG2(fmt,...)
Definition radclient.h:54
fr_radius_tag_ctx_t ** tags
for decoding tagged attributes
Definition radius.h:176
uint8_t const * end
end of the packet
Definition radius.h:165
fr_radius_ctx_t const * common
Definition radius.h:160
TALLOC_CTX * tmp_ctx
for temporary things cleaned up during decoding
Definition radius.h:164
static fr_dict_attr_t const * attr_message_authenticator
Definition radsnmp.c:114
@ RLM_MODULE_OK
The module is OK, continue.
Definition rcode.h:49
@ RLM_MODULE_HANDLED
The module handled the request, so stop.
Definition rcode.h:50
#define request_local_alloc_internal(_ctx, _args)
Allocate a new internal request outside of the request pool.
Definition request.h:343
Optional arguments for initialising requests.
Definition request.h:287
static const void * fake
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
#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:639
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:69
#define fr_pair_dcursor_init(_cursor, _list)
Initialises a special dcursor with callbacks that will maintain the attr sublists correctly.
Definition pair.h:604
fr_pair_t * fr_pair_list_head(fr_pair_list_t const *list)
Get the head of a valuepair list.
Definition pair_inline.c:42
static fr_slen_t data
Definition value.h:1334