6 * Copyright (c) 2012, JANET(UK)
9 * Redistribution and use in source and binary forms, with or without
10 * modification, are permitted provided that the following conditions
13 * 1. Redistributions of source code must retain the above copyright
14 * notice, this list of conditions and the following disclaimer.
16 * 2. Redistributions in binary form must reproduce the above copyright
17 * notice, this list of conditions and the following disclaimer in the
18 * documentation and/or other materials provided with the distribution.
20 * 3. Neither the name of JANET(UK) nor the names of its contributors
21 * may be used to endorse or promote products derived from this software
22 * without specific prior written permission.
24 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
25 * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
26 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
27 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
28 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
29 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
30 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
31 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
32 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
33 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
34 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
40 #include "eap_chbind.h"
42 #define MAX_PACKET_LEN 4096
45 * Process any channel bindings included in the request.
47 CHBIND_REQ *chbind_allocate(void)
50 ret = malloc(sizeof *ret);
52 memset(ret, 0, sizeof *ret);
56 void chbind_free(CHBIND_REQ *chbind)
58 /* free the chbind response, if allocated by chbind_process */
59 if (chbind->chbind_resp)
60 free(chbind->chbind_resp);
65 int chbind_process(REQUEST *req, CHBIND_REQ *chbind_req)
67 int rcode = PW_AUTHENTICATION_REJECT;
69 VALUE_PAIR *vp = NULL;
73 /* check input parameters */
74 rad_assert((req != NULL) &&
75 (chbind_req != NULL) &&
76 (chbind_req->chbind_req_pkt != NULL));
77 if (chbind_req->chbind_req_len < 4)
78 return PW_AUTHENTICATION_REJECT; /* Is this the right response? */
80 /* Set-up NULL response for cases where channel bindings can't be processed */
81 chbind_req->chbind_resp = NULL;
82 chbind_req->chbind_resp_len = 0;
84 /* Set-up the fake request */
85 fake = request_alloc_fake(req);
86 rad_assert(fake->packet->vps == NULL);
87 pairmake(fake, &fake->packet->vps, "Freeradius-Proxied-To", "127.0.0.1", T_OP_EQ);
89 /* Add the username to the fake request */
90 if (chbind_req->username) {
91 uint8_t *octets = NULL;
92 vp = paircreate(fake, PW_USER_NAME, 0);
94 octets = talloc_array(vp, uint8_t, chbind_req->username_len+1);
96 memcpy(octets, chbind_req->username, chbind_req->username_len);
97 vp->vp_octets = octets;
98 vp->length = chbind_req->username_len;
100 pairadd(&fake->packet->vps, vp);
101 fake->username = pairfind(fake->packet->vps, PW_USER_NAME, 0, TAG_ANY);
104 /* Copy the request state into the fake request */
105 /*xxx vp = paircopy(req->state);
107 pairadd(&fake->packet->vps, vp);*/
109 /* Add the channel binding attributes to the fake packet */
110 if (0 != (datalen = chbind_get_data((CHBIND_PACKET_T *)chbind_req->chbind_req_pkt,
111 chbind_req->chbind_req_len,
112 CHBIND_NSID_RADIUS, &attr_data))) {
114 int mylen = rad_attr2vp(NULL, NULL, NULL, attr_data, datalen, &vp);
116 /* If radaddr2vp fails, return NULL string for
117 channel binding response */
119 return PW_AUTHENTICATION_ACK;
121 /* TODO: need to account for the possibility of rad_attr2vp generating
124 pairadd(&fake->packet->vps, vp);
130 /* Set virtual server based on configuration for channel bindings,
131 this is hard-coded to "chbind" for now */
132 fake->server = "chbind";
134 /* Call rad_authenticate */
135 if ((debug_flag > 0) && fr_log_fp) {
136 DEBUG("prcoessing chbind request");
138 debug_pair_list(fake->packet->vps);
140 fprintf(fr_log_fp, "server %s {\n",
141 (fake->server == NULL) ? "" : fake->server);
143 rcode = rad_authenticate(fake);
146 /* If rad_authenticate succeeded, build a reply */
148 case RLM_MODULE_HANDLED:
149 if ((chbind_req->chbind_resp = chbind_build_response(fake, &chbind_req->chbind_resp_len)) != NULL)
150 rcode = PW_AUTHENTICATION_ACK;
152 rcode = PW_AUTHENTICATION_REJECT;
155 /* If we got any other response from rad_authenticate, it maps to a reject */
157 rcode = PW_AUTHENTICATION_REJECT;
167 * Parse channel binding packet to obtain data for a specific NSID.
168 * See http://tools.ietf.org/html/draft-ietf-emu-chbind-13#section-5.3.2:
171 size_t chbind_get_data(CHBIND_PACKET_T *chbind_packet,
172 size_t chbind_packet_len,
174 uint8_t **radbuf_data)
176 size_t chbind_data_len = chbind_packet_len-1;
178 if (chbind_packet->code != CHBIND_CODE_REQUEST)
180 while (pos + 3 < chbind_data_len) {
181 size_t len = (chbind_packet->data[pos] << 8) +
182 chbind_packet->data[pos + 1];
183 uint8_t nsid = chbind_packet->data[pos + 2];
184 if (pos + 3 > chbind_data_len + len) {
185 /* malformed packet; warn here */
188 if (nsid == desired_nsid) {
189 *radbuf_data = &chbind_packet->data[pos+3];
194 /* didn't find any data matching nsid */
195 if (pos != chbind_data_len) {
196 /* warn about malformed packet */
202 uint8_t *chbind_build_response(REQUEST *req, size_t *resp_len)
205 uint16_t pos, len = 0;
206 VALUE_PAIR *vp = NULL;
209 resp = malloc(MAX_PACKET_LEN + 4);
212 /* Set-up the chbind header fields (except length, computed later) */
213 vp = pairfind(req->config_items, PW_CHBIND_RESPONSE_CODE, 0, TAG_ANY);
215 resp[0] = vp->vp_integer;
216 else resp[0] = 3; /*failure*/
219 resp[3] = CHBIND_NSID_RADIUS;
221 if ((debug_flag > 0) && fr_log_fp) {
222 DEBUG("Sending chbind response: code %i\n", (int )(resp[0]));
223 debug_pair_list(req->reply->vps);
224 DEBUG("end chbind response\n");
226 /* Encode the chbind attributes into the response */
227 for (vp = req->reply->vps, pos = 4;
228 (vp != NULL) && (pos < MAX_PACKET_LEN + 4);
230 len = rad_vp2attr(NULL, NULL, NULL, (const VALUE_PAIR **) &vp, &resp[pos], (MAX_PACKET_LEN + 4) - pos);
233 len = pos-4; /*length covers ns-specific only*/
234 /* Write the length field into the header */
235 resp[1] = (uint8_t)(len >> 8);
236 resp[2] = (uint8_t)(len & 0x00FF);
238 /* Output the length of the entire response (attrs + header) */
239 /* If there are no attributes, only send the code*/