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