Package files in 3.0.1 not in 3.0.3 and chbind site
[freeradius.git] / src / modules / rlm_eap / libeap / eap_chbind.c
1 /*
2  * eap_chbind.c
3  *
4  * Version:     $Id$
5  *
6  * Copyright (c) 2012, JANET(UK)
7  * All rights reserved.
8  *
9  * Redistribution and use in source and binary forms, with or without
10  * modification, are permitted provided that the following conditions
11  * are met:
12  *
13  * 1. Redistributions of source code must retain the above copyright
14  *    notice, this list of conditions and the following disclaimer.
15  *
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.
19  *
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.
23  *
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.
35  */
36
37
38 RCSID("$Id$")
39
40 #include "eap_chbind.h"
41
42 #define MAX_PACKET_LEN          4096
43
44 /*
45  * Process any channel bindings included in the request.
46  */
47 CHBIND_REQ *chbind_allocate(void)
48 {
49   CHBIND_REQ *ret;
50   ret = malloc(sizeof *ret);
51   if (0 != ret)
52     memset(ret, 0, sizeof *ret);
53   return ret;
54 }
55
56 void chbind_free(CHBIND_REQ *chbind)
57 {
58   /* free the chbind response, if allocated by chbind_process */
59   if (chbind->chbind_resp)
60     free(chbind->chbind_resp);
61
62   free(chbind);
63 }
64
65 int chbind_process(REQUEST *req, CHBIND_REQ *chbind_req)
66 {
67   int rcode = PW_AUTHENTICATION_REJECT;
68   REQUEST *fake = NULL;
69   VALUE_PAIR *vp = NULL;
70   uint8_t *attr_data;
71   size_t datalen = 0;
72
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? */
79
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;
83
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);
88   
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);
93     rad_assert(vp);
94     octets = talloc_array(vp, uint8_t, chbind_req->username_len+1);
95     rad_assert(octets);
96     memcpy(octets, chbind_req->username, chbind_req->username_len);
97     vp->vp_octets = octets;
98     vp->length = chbind_req->username_len;
99
100     pairadd(&fake->packet->vps, vp);
101     fake->username = pairfind(fake->packet->vps, PW_USER_NAME, 0, TAG_ANY);
102   }
103
104   /* Copy the request state into the fake request */
105   /*xxx vp = paircopy(req->state);
106   if (vp)
107   pairadd(&fake->packet->vps, vp);*/
108
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))) {
113           while(datalen > 0) {
114                   int mylen = rad_attr2vp(NULL, NULL, NULL, attr_data, datalen, &vp);
115                   if (mylen <= 0) {
116                           /* If radaddr2vp fails, return NULL string for 
117                              channel binding response */
118                           request_free(&fake);
119                           return PW_AUTHENTICATION_ACK;
120                   }
121                   /* TODO: need to account for the possibility of rad_attr2vp generating 
122                      multiple vps */
123                   if (vp)
124                           pairadd(&fake->packet->vps, vp);
125                   attr_data += mylen;
126                   datalen -= mylen;
127           }
128   }
129
130   /* Set virtual server based on configuration for channel bindings,
131      this is hard-coded to "chbind" for now */
132   fake->server = "chbind";
133
134   /* Call rad_authenticate */
135   if ((debug_flag > 0) && fr_log_fp) {
136           DEBUG("prcoessing chbind request");
137
138           debug_pair_list(fake->packet->vps);
139
140           fprintf(fr_log_fp, "server %s {\n",
141             (fake->server == NULL) ? "" : fake->server);
142   }
143   rcode = rad_authenticate(fake);
144
145   switch(rcode) {
146     /* If rad_authenticate succeeded, build a reply */
147   case RLM_MODULE_OK:
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;
151     else
152       rcode = PW_AUTHENTICATION_REJECT;
153     break;
154   
155   /* If we got any other response from rad_authenticate, it maps to a reject */
156   default:
157     rcode = PW_AUTHENTICATION_REJECT;
158     break;
159   }
160
161   request_free(&fake);
162
163   return rcode;
164 }
165
166 /*
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:
169  */ 
170
171 size_t chbind_get_data(CHBIND_PACKET_T *chbind_packet,
172                            size_t chbind_packet_len,
173                            int desired_nsid,
174                            uint8_t **radbuf_data)
175 {
176   size_t chbind_data_len = chbind_packet_len-1;
177   size_t pos=0;
178   if (chbind_packet->code != CHBIND_CODE_REQUEST)
179     return 0;
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 */
186       return 0;
187     }
188     if (nsid == desired_nsid) {
189       *radbuf_data = &chbind_packet->data[pos+3];
190       return len;
191     }
192     pos += 3 + len;
193   }
194   /* didn't find any data matching nsid */
195   if (pos != chbind_data_len) {
196     /* warn about malformed packet */
197   }
198
199   return 0;
200 }
201
202 uint8_t *chbind_build_response(REQUEST *req, size_t *resp_len)
203 {
204   uint8_t *resp;
205   uint16_t pos, len = 0;
206   VALUE_PAIR *vp = NULL;
207
208   *resp_len = 0;
209   resp = malloc(MAX_PACKET_LEN + 4);
210   rad_assert(resp);
211
212   /* Set-up the chbind header fields (except length, computed later) */
213   vp = pairfind(req->config_items, PW_CHBIND_RESPONSE_CODE, 0, TAG_ANY);
214   if (vp)
215     resp[0] = vp->vp_integer;
216   else resp[0] = 3; /*failure*/
217   
218
219   resp[3] = CHBIND_NSID_RADIUS;
220
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");
225   }
226   /* Encode the chbind attributes into the response */
227   for (vp = req->reply->vps, pos = 4; 
228        (vp != NULL) && (pos < MAX_PACKET_LEN + 4); 
229        pos += len) {
230     len = rad_vp2attr(NULL, NULL, NULL, (const VALUE_PAIR **) &vp, &resp[pos], (MAX_PACKET_LEN + 4) - pos);
231   }
232
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);
237   
238   /* Output the length of the entire response (attrs + header) */
239   /* If there are no attributes, only send the code*/
240   if (req->reply->vps)
241     *resp_len = len + 4;
242   else *resp_len = 1;
243   return resp;
244 }