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 #define MAX_PACKET_LEN 4096
31 static bool chbind_build_response(REQUEST *request, CHBIND_REQ *chbind)
39 for (vp = fr_cursor_init(&cursor, &request->reply->vps);
41 vp = fr_cursor_next(&cursor)) {
43 * Skip things which shouldn't be in channel bindings.
45 if (vp->da->flags.encrypt != FLAG_ENCRYPT_NONE) continue;
46 if (!vp->da->vendor && (vp->da->attr == PW_MESSAGE_AUTHENTICATOR)) continue;
48 total = 2 + vp->length;
52 * No attributes: just send a 1-byte response code.
55 ptr = talloc_zero_array(chbind, uint8_t, 1);
57 ptr = talloc_zero_array(chbind, uint8_t, total + 4);
59 if (!ptr) return false;
60 chbind->response = (chbind_packet_t *) ptr;
63 * Set the response code. Default to "fail" if none was
66 vp = pairfind(request->config_items, PW_CHBIND_RESPONSE_CODE, 0, TAG_ANY);
68 ptr[0] = vp->vp_integer;
70 ptr[0] = CHBIND_CODE_FAILURE;
73 if (!total) return true; /* nothing to encode */
75 /* Write the length field into the header */
76 ptr[1] = (total >> 8) & 0xff;
77 ptr[2] = total & 0xff;
78 ptr[3] = CHBIND_NSID_RADIUS;
80 if ((debug_flag > 0) && fr_log_fp) {
81 RDEBUG("Sending chbind response: code %i", (int )(ptr[0]));
82 debug_pair_list(request->reply->vps);
85 /* Encode the chbind attributes into the response */
88 for (vp = fr_cursor_init(&cursor, &request->reply->vps);
90 vp = fr_cursor_next(&cursor)) {
91 length = rad_vp2attr(NULL, NULL, NULL, &vp, ptr, end - ptr);
100 * Parse channel binding packet to obtain data for a specific
104 * http://tools.ietf.org/html/draft-ietf-emu-chbind-13#section-5.3.2
106 static size_t chbind_get_data(chbind_packet_t const *packet,
108 uint8_t const **data)
113 if (packet->code != CHBIND_CODE_REQUEST) {
117 ptr = (uint8_t const *) packet;
118 end = ptr + talloc_array_length(packet);
120 ptr++; /* skip the code at the start of the packet */
126 * Need room for length(2) + NSID + data.
128 if ((end - ptr) < 4) return 0;
130 length = (ptr[0] << 8) | ptr[1];
131 if (length == 0) return 0;
133 if ((ptr + length + 3) > end) return 0;
136 if (nsid == desired_nsid) {
149 PW_CODE chbind_process(REQUEST *request, CHBIND_REQ *chbind)
152 REQUEST *fake = NULL;
153 VALUE_PAIR *vp = NULL;
154 uint8_t const *attr_data;
157 /* check input parameters */
158 rad_assert((request != NULL) &&
160 (chbind->request != NULL) &&
161 (chbind->response == NULL));
163 /* Set-up the fake request */
164 fake = request_alloc_fake(request);
165 pairmake_packet("Freeradius-Proxied-To", "127.0.0.1", T_OP_EQ);
167 /* Add the username to the fake request */
168 if (chbind->username) {
169 vp = paircopyvp(fake->packet, chbind->username);
170 pairadd(&fake->packet->vps, vp);
175 * Maybe copy the State over, too?
178 /* Add the channel binding attributes to the fake packet */
179 data_len = chbind_get_data(chbind->request, CHBIND_NSID_RADIUS, &attr_data);
181 while (data_len > 0) {
182 int attr_len = rad_attr2vp(fake->packet, NULL, NULL, NULL, attr_data, data_len, &vp);
184 /* If radaddr2vp fails, return NULL string for
185 channel binding response */
187 return PW_CODE_ACCESS_ACCEPT;
190 pairadd(&fake->packet->vps, vp);
192 attr_data += attr_len;
193 data_len -= attr_len;
198 * Set virtual server based on configuration for channel
199 * bindings, this is hard-coded for now.
201 fake->server = "channel_bindings";
202 fake->packet->code = PW_CODE_ACCESS_REQUEST;
204 rcode = rad_virtual_server(fake);
207 /* If rad_authenticate succeeded, build a reply */
209 case RLM_MODULE_HANDLED:
210 if (chbind_build_response(fake, chbind)) {
211 rcode = PW_CODE_ACCESS_ACCEPT;
216 /* If we got any other response from rad_authenticate, it maps to a reject */
218 rcode = PW_CODE_ACCESS_REJECT;
228 * Handles multiple EAP-channel-binding Message attrs
229 * ie concatenates all to get the complete EAP-channel-binding packet.
231 chbind_packet_t *eap_chbind_vp2packet(TALLOC_CTX *ctx, VALUE_PAIR *vps)
235 VALUE_PAIR *first, *vp;
236 chbind_packet_t *packet;
239 first = pairfind(vps, PW_UKERNA_CHBIND, VENDORPEC_UKERNA, TAG_ANY);
240 if (!first) return NULL;
243 * Compute the total length of the channel binding data.
246 for (vp =fr_cursor_init(&cursor, &first);
248 vp = fr_cursor_next_by_num(&cursor, PW_UKERNA_CHBIND, VENDORPEC_UKERNA, TAG_ANY)) {
249 length += vp->length;
253 DEBUG("Invalid length %u for channel binding data", (unsigned int) length);
258 * Now that we know the length, allocate memory for the packet.
260 ptr = talloc_zero_array(ctx, uint8_t, length);
261 if (!ptr) return NULL;
264 * Copy the data over to our packet.
266 packet = (chbind_packet_t *) ptr;
267 for (vp = fr_cursor_init(&cursor, &first);
269 vp = fr_cursor_next_by_num(&cursor, PW_UKERNA_CHBIND, VENDORPEC_UKERNA, TAG_ANY)) {
270 memcpy(ptr, vp->vp_octets, vp->length);
277 VALUE_PAIR *eap_chbind_packet2vp(REQUEST *request, const chbind_packet_t *packet)
281 if (!packet) return NULL; /* don't produce garbage */
283 vp = paircreate(request->packet, PW_UKERNA_CHBIND, VENDORPEC_UKERNA);
284 if (!vp) return NULL;
285 pairmemcpy(vp, (const uint8_t *) packet, talloc_array_length(packet));