Pull fix from branch_1_1, so proxied EAP replies work
[freeradius.git] / src / modules / rlm_eap / mem.c
1 /*
2  * mem.c  Memory allocation, deallocation stuff.
3  *
4  * Version:     $Id$
5  *
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.
10  *
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.
15  *
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
19  *
20  * Copyright 2000,2001,2006  The FreeRADIUS server project
21  * Copyright 2001  hereUare Communications, Inc. <raghud@hereuare.com>
22  */
23
24 #include <freeradius-devel/ident.h>
25 RCSID("$Id$")
26
27 #include <stdio.h>
28 #include "rlm_eap.h"
29
30 /*
31  * Allocate a new EAP_PACKET
32  */
33 EAP_PACKET *eap_packet_alloc(void)
34 {
35         EAP_PACKET   *rp;
36
37         rp = rad_malloc(sizeof(EAP_PACKET));
38         memset(rp, 0, sizeof(EAP_PACKET));
39         return rp;
40 }
41
42 /*
43  * Free EAP_PACKET
44  */
45 void eap_packet_free(EAP_PACKET **eap_packet_ptr)
46 {
47         EAP_PACKET *eap_packet;
48
49         if (!eap_packet_ptr) return;
50         eap_packet = *eap_packet_ptr;
51         if (!eap_packet) return;
52
53         if (eap_packet->type.data) {
54                 /*
55                  * This is just a pointer in the packet
56                  * so we do not free it but we NULL it
57                 free(eap_packet->type.data);
58                 */
59                 eap_packet->type.data = NULL;
60         }
61
62         if (eap_packet->packet) {
63                 free(eap_packet->packet);
64                 eap_packet->packet = NULL;
65         }
66
67         free(eap_packet);
68
69         *eap_packet_ptr = NULL;
70 }
71
72 /*
73  * Allocate a new EAP_PACKET
74  */
75 EAP_DS *eap_ds_alloc(void)
76 {
77         EAP_DS  *eap_ds;
78
79         eap_ds = rad_malloc(sizeof(EAP_DS));
80         memset(eap_ds, 0, sizeof(EAP_DS));
81         if ((eap_ds->response = eap_packet_alloc()) == NULL) {
82                 eap_ds_free(&eap_ds);
83                 return NULL;
84         }
85         if ((eap_ds->request = eap_packet_alloc()) == NULL) {
86                 eap_ds_free(&eap_ds);
87                 return NULL;
88         }
89
90         return eap_ds;
91 }
92
93 void eap_ds_free(EAP_DS **eap_ds_p)
94 {
95         EAP_DS *eap_ds;
96
97         if (!eap_ds_p) return;
98
99         eap_ds = *eap_ds_p;
100         if (!eap_ds) return;
101
102         if (eap_ds->response) eap_packet_free(&(eap_ds->response));
103         if (eap_ds->request) eap_packet_free(&(eap_ds->request));
104
105         free(eap_ds);
106         *eap_ds_p = NULL;
107 }
108
109 /*
110  * Allocate a new EAP_HANDLER
111  */
112 EAP_HANDLER *eap_handler_alloc(void)
113 {
114         EAP_HANDLER     *handler;
115
116         handler = rad_malloc(sizeof(EAP_HANDLER));
117         memset(handler, 0, sizeof(EAP_HANDLER));
118         return handler;
119 }
120
121 void eap_handler_free(EAP_HANDLER *handler)
122 {
123         if (!handler)
124                 return;
125
126         if (handler->identity) {
127                 free(handler->identity);
128                 handler->identity = NULL;
129         }
130
131         if (handler->prev_eapds) eap_ds_free(&(handler->prev_eapds));
132         if (handler->eap_ds) eap_ds_free(&(handler->eap_ds));
133
134         if ((handler->opaque) && (handler->free_opaque)) {
135                 handler->free_opaque(handler->opaque);
136                 handler->opaque = NULL;
137         }
138         else if ((handler->opaque) && (handler->free_opaque == NULL))
139                 radlog(L_ERR, "Possible memory leak ...");
140
141         handler->opaque = NULL;
142         handler->free_opaque = NULL;
143
144         free(handler);
145 }
146
147 void eaptype_free(EAP_TYPES *i)
148 {
149         if (i->type->detach) (i->type->detach)(i->type_data);
150         i->type_data = NULL;
151         if (i->handle) lt_dlclose(i->handle);
152 }
153
154
155 void eaplist_free(rlm_eap_t *inst)
156 {
157         EAP_HANDLER *node, *next;
158
159         for (node = inst->session_head; node != NULL; node = next) {
160                 next = node->next;
161                 eap_handler_free(node);
162         }
163
164         inst->session_head = inst->session_tail = NULL;
165 }
166
167 /*
168  *      Add a handler to the set of active sessions.
169  *
170  *      Since we're adding it to the list, we guess that this means
171  *      the packet needs a State attribute.  So add one.
172  */
173 int eaplist_add(rlm_eap_t *inst, EAP_HANDLER *handler)
174 {
175         int             status;
176         VALUE_PAIR      *state;
177         
178         rad_assert(handler != NULL);
179         rad_assert(handler->request != NULL);
180
181         /*
182          *      Generate State, since we've been asked to add it to
183          *      the list.
184          */
185         state = generate_state(handler->request->timestamp);
186         pairadd(&(handler->request->reply->vps), state);
187
188         /*
189          *      Create a unique 'key' for the handler, based
190          *      on State, Client-IP-Address, and EAP ID.
191          */
192         rad_assert(state->length == EAP_STATE_LEN);
193
194         /*
195          *      The time at which this request was made was the time
196          *      at which it was received by the RADIUS server.
197          */
198         handler->timestamp = handler->request->timestamp;
199         handler->status = 1;
200
201         memcpy(handler->state, state->vp_strvalue, sizeof(handler->state));
202         handler->src_ipaddr = handler->request->packet->src_ipaddr;
203         handler->eap_id = handler->eap_ds->request->id;
204
205         /*
206          *      We don't need this any more.
207          */
208         handler->request = NULL;
209
210         /*
211          *      Playing with a data structure shared among threads
212          *      means that we need a lock, to avoid conflict.
213          */
214         pthread_mutex_lock(&(inst->session_mutex));
215
216         /*
217          *      Big-time failure.
218          */
219         status = rbtree_insert(inst->session_tree, handler);
220
221         if (status) {
222                 EAP_HANDLER *prev;
223
224                 prev = inst->session_tail;
225                 if (prev) {
226                         prev->next = handler;
227                         handler->prev = prev;
228                         handler->next = NULL;
229                         inst->session_tail = handler;
230                 } else {
231                         inst->session_head = inst->session_tail = handler;
232                         handler->next = handler->prev = NULL;
233                 }
234         }
235
236         /*
237          *      Now that we've finished mucking with the list,
238          *      unlock it.
239          */
240         pthread_mutex_unlock(&(inst->session_mutex));
241
242         if (!status) {
243                 radlog(L_ERR, "rlm_eap: Failed to remember handler!");
244                 eap_handler_free(handler);
245                 return 0;
246         }
247
248         return 1;
249 }
250
251 /*
252  *      Find a a previous EAP-Request sent by us, which matches
253  *      the current EAP-Response.
254  *
255  *      Then, release the handle from the list, and return it to
256  *      the caller.
257  *
258  *      Also since we fill the eap_ds with the present EAP-Response we
259  *      got to free the prev_eapds & move the eap_ds to prev_eapds
260  */
261 EAP_HANDLER *eaplist_find(rlm_eap_t *inst, REQUEST *request,
262                           eap_packet_t *eap_packet)
263 {
264         int             i;
265         VALUE_PAIR      *state;
266         rbnode_t        *node;
267         EAP_HANDLER     *handler, myHandler;
268
269         /*
270          *      We key the sessions off of the 'state' attribute, so it
271          *      must exist.
272          */
273         state = pairfind(request->packet->vps, PW_STATE);
274         if (!state ||
275             (state->length != EAP_STATE_LEN)) {
276                 return NULL;
277         }
278
279         myHandler.src_ipaddr = request->packet->src_ipaddr;
280         myHandler.eap_id = eap_packet->id;
281         memcpy(myHandler.state, state->vp_strvalue, sizeof(myHandler.state));
282
283         /*
284          *      Playing with a data structure shared among threads
285          *      means that we need a lock, to avoid conflict.
286          */
287         pthread_mutex_lock(&(inst->session_mutex));
288
289         /*
290          *      Check the first few handlers in the list, and delete
291          *      them if they're too old.  We don't need to check them
292          *      all, as incoming requests will quickly cause older
293          *      handlers to be deleted.
294          *
295          */
296         for (i = 0; i < 2; i++) {
297                 handler = inst->session_head;
298                 if (handler &&
299                     ((request->timestamp - handler->timestamp) > inst->timer_limit)) {
300                         node = rbtree_find(inst->session_tree, handler);
301                         rad_assert(node != NULL);
302                         rbtree_delete(inst->session_tree, node);
303                         
304                         inst->session_head = handler->next;
305                         if (handler->next) handler->next->prev = NULL;
306                         eap_handler_free(handler);
307                 }
308         }
309
310         handler = NULL;
311         node = rbtree_find(inst->session_tree, &myHandler);
312         if (node) {
313                 handler = rbtree_node2data(inst->session_tree, node);
314
315                 /*
316                  *      Check against replays.  The client can re-play
317                  *      a State attribute verbatim, so we wish to
318                  *      ensure that the attribute falls within the
319                  *      valid time window, which is the second at
320                  *      which it was sent out.
321                  *
322                  *      Hmm... I'm not sure that this step is
323                  *      necessary, or even that it does anything.
324                  */
325                 if (verify_state(state, handler->timestamp) != 0) {
326                         handler = NULL;
327                 } else {
328                         /*
329                          *      It's OK, delete it from the tree.
330                          */
331                         rbtree_delete(inst->session_tree, node);
332
333                         /*
334                          *      And unsplice it from the linked list.
335                          */
336                         if (handler->prev) {
337                                 handler->prev->next = handler->next;
338                         } else {
339                                 inst->session_head = handler->next;
340                         }
341                         if (handler->next) {
342                                 handler->next->prev = handler->prev;
343                         } else {
344                                 inst->session_tail = handler->prev;
345                         }
346                         handler->prev = handler->next = NULL;
347                 }
348         }
349
350         pthread_mutex_unlock(&(inst->session_mutex));
351
352         /*
353          *      Not found.
354          */
355         if (!node) {
356                 DEBUG2("  rlm_eap: Request not found in the list");
357                 return NULL;
358         }
359
360         /*
361          *      Found, but state verification failed.
362          */
363         if (!handler) {
364                 radlog(L_ERR, "rlm_eap: State verification failed.");
365                 return NULL;
366         }
367
368         DEBUG2("  rlm_eap: Request found, released from the list");
369
370         /*
371          *      Remember what the previous request was.
372          */
373         eap_ds_free(&(handler->prev_eapds));
374         handler->prev_eapds = handler->eap_ds;
375         handler->eap_ds = NULL;
376         
377         return handler;
378 }