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.
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.
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
20 * Copyright 2014 Network RADIUS SARL
21 * Copyright 2014 The FreeRADIUS server project
27 #include "eap_chbind.h"
29 static bool chbind_build_response(REQUEST *request, CHBIND_REQ *chbind)
38 for (vp = fr_cursor_init(&cursor, &request->reply->vps);
40 vp = fr_cursor_next(&cursor)) {
42 * Skip things which shouldn't be in channel bindings.
44 if (vp->da->flags.encrypt != FLAG_ENCRYPT_NONE) continue;
45 if (!vp->da->vendor && (vp->da->attr == PW_MESSAGE_AUTHENTICATOR)) continue;
47 total += 2 + vp->vp_length;
51 * No attributes: just send a 1-byte response code.
54 ptr = talloc_zero_array(chbind, uint8_t, 1);
56 ptr = talloc_zero_array(chbind, uint8_t, total + 4);
58 if (!ptr) return false;
59 chbind->response = (chbind_packet_t *) ptr;
62 * Set the response code. Default to "fail" if none was
65 vp = fr_pair_find_by_num(request->config, PW_CHBIND_RESPONSE_CODE, 0, TAG_ANY);
67 ptr[0] = vp->vp_integer;
69 ptr[0] = CHBIND_CODE_FAILURE;
72 if (!total) return true; /* nothing to encode */
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;
79 RDEBUG("Sending chbind response: code %i", (int )(ptr[0]));
80 rdebug_pair_list(L_DBG_LVL_1, request, request->reply->vps, NULL);
82 /* Encode the chbind attributes into the response */
85 for (vp = fr_cursor_init(&cursor, &request->reply->vps);
87 vp = fr_cursor_next(&cursor)) {
89 * Skip things which shouldn't be in channel bindings.
91 if (vp->da->flags.encrypt != FLAG_ENCRYPT_NONE) continue;
92 if (!vp->da->vendor && (vp->da->attr == PW_MESSAGE_AUTHENTICATOR)) continue;
94 length = rad_vp2attr(NULL, NULL, NULL, &vp, ptr, end - ptr);
95 if (length < 0) continue;
104 * Parse channel binding packet to obtain data for a specific
108 * http://tools.ietf.org/html/draft-ietf-emu-chbind-13#section-5.3.2
110 static size_t chbind_get_data(chbind_packet_t const *packet,
112 uint8_t const **data)
117 if (packet->code != CHBIND_CODE_REQUEST) {
121 ptr = (uint8_t const *) packet;
122 end = ptr + talloc_array_length(packet);
124 ptr++; /* skip the code at the start of the packet */
130 * Need room for length(2) + NSID + data.
132 if ((end - ptr) < 4) return 0;
134 length = (ptr[0] << 8) | ptr[1];
135 if (length == 0) return 0;
137 if ((ptr + length + 3) > end) return 0;
140 if (nsid == desired_nsid) {
153 PW_CODE chbind_process(REQUEST *request, CHBIND_REQ *chbind)
156 REQUEST *fake = NULL;
157 VALUE_PAIR *vp = NULL;
158 uint8_t const *attr_data;
161 /* check input parameters */
162 rad_assert((request != NULL) &&
164 (chbind->request != NULL) &&
165 (chbind->response == NULL));
167 /* Set-up the fake request */
168 fake = request_alloc_fake(request);
169 pair_make_request("Freeradius-Proxied-To", "127.0.0.1", T_OP_EQ);
171 /* Add the username to the fake request */
172 if (chbind->username) {
173 vp = fr_pair_copy(fake->packet, chbind->username);
174 fr_pair_add(&fake->packet->vps, vp);
179 * Maybe copy the State over, too?
182 /* Add the channel binding attributes to the fake packet */
183 data_len = chbind_get_data(chbind->request, CHBIND_NSID_RADIUS, &attr_data);
185 rad_assert(data_len <= talloc_array_length(chbind->request));
187 while (data_len > 0) {
188 int attr_len = rad_attr2vp(fake->packet, NULL, NULL, NULL, attr_data, data_len, &vp);
190 /* If radaddr2vp fails, return NULL string for
191 channel binding response */
193 return PW_CODE_ACCESS_ACCEPT;
196 fr_pair_add(&fake->packet->vps, vp);
198 attr_data += attr_len;
199 data_len -= attr_len;
204 * Set virtual server based on configuration for channel
205 * bindings, this is hard-coded for now.
207 fake->server = "channel_bindings";
208 fake->packet->code = PW_CODE_ACCESS_REQUEST;
210 rcode = rad_virtual_server(fake);
213 /* If rad_authenticate succeeded, build a reply */
215 case RLM_MODULE_HANDLED:
216 if (chbind_build_response(fake, chbind)) {
217 rcode = PW_CODE_ACCESS_ACCEPT;
222 /* If we got any other response from rad_authenticate, it maps to a reject */
224 rcode = PW_CODE_ACCESS_REJECT;
234 * Handles multiple EAP-channel-binding Message attrs
235 * ie concatenates all to get the complete EAP-channel-binding packet.
237 chbind_packet_t *eap_chbind_vp2packet(TALLOC_CTX *ctx, VALUE_PAIR *vps)
241 VALUE_PAIR *first, *vp;
242 chbind_packet_t *packet;
245 first = fr_pair_find_by_num(vps, PW_UKERNA_CHBIND, VENDORPEC_UKERNA, TAG_ANY);
246 if (!first) return NULL;
249 * Compute the total length of the channel binding data.
252 for (vp =fr_cursor_init(&cursor, &first);
254 vp = fr_cursor_next_by_num(&cursor, PW_UKERNA_CHBIND, VENDORPEC_UKERNA, TAG_ANY)) {
255 length += vp->vp_length;
259 DEBUG("Invalid length %u for channel binding data", (unsigned int) length);
264 * Now that we know the length, allocate memory for the packet.
266 ptr = talloc_zero_array(ctx, uint8_t, length);
267 if (!ptr) return NULL;
270 * Copy the data over to our packet.
272 packet = (chbind_packet_t *) ptr;
273 for (vp = fr_cursor_init(&cursor, &first);
275 vp = fr_cursor_next_by_num(&cursor, PW_UKERNA_CHBIND, VENDORPEC_UKERNA, TAG_ANY)) {
276 memcpy(ptr, vp->vp_octets, vp->vp_length);
277 ptr += vp->vp_length;
283 VALUE_PAIR *eap_chbind_packet2vp(REQUEST *request, chbind_packet_t *packet)
287 if (!packet) return NULL; /* don't produce garbage */
289 vp = fr_pair_afrom_num(request->packet, PW_UKERNA_CHBIND, VENDORPEC_UKERNA);
290 if (!vp) return NULL;
291 fr_pair_value_memcpy(vp, (uint8_t *) packet, talloc_array_length((uint8_t *)packet));