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                 } else {
227                         inst->session_head = inst->session_tail = handler;
228                 }
229         }
230
231         /*
232          *      Now that we've finished mucking with the list,
233          *      unlock it.
234          */
235         pthread_mutex_unlock(&(inst->session_mutex));
236
237         if (!status) {
238                 radlog(L_ERR, "rlm_eap: Failed to remember handler!");
239                 eap_handler_free(handler);
240                 return 0;
241         }
242
243         return 1;
244 }
245
246 /*
247  *      Find a a previous EAP-Request sent by us, which matches
248  *      the current EAP-Response.
249  *
250  *      Then, release the handle from the list, and return it to
251  *      the caller.
252  *
253  *      Also since we fill the eap_ds with the present EAP-Response we
254  *      got to free the prev_eapds & move the eap_ds to prev_eapds
255  */
256 EAP_HANDLER *eaplist_find(rlm_eap_t *inst, REQUEST *request,
257                           eap_packet_t *eap_packet)
258 {
259         int             i;
260         VALUE_PAIR      *state;
261         rbnode_t        *node;
262         EAP_HANDLER     *handler, myHandler;
263
264         /*
265          *      We key the sessions off of the 'state' attribute, so it
266          *      must exist.
267          */
268         state = pairfind(request->packet->vps, PW_STATE);
269         if (!state ||
270             (state->length != EAP_STATE_LEN)) {
271                 return NULL;
272         }
273
274         myHandler.src_ipaddr = request->packet->src_ipaddr;
275         myHandler.eap_id = eap_packet->id;
276         memcpy(myHandler.state, state->strvalue, sizeof(myHandler.state));
277
278         /*
279          *      Playing with a data structure shared among threads
280          *      means that we need a lock, to avoid conflict.
281          */
282         pthread_mutex_lock(&(inst->session_mutex));
283
284         /*
285          *      Check the first few handlers in the list, and delete
286          *      them if they're too old.  We don't need to check them
287          *      all, as incoming requests will quickly cause older
288          *      handlers to be deleted.
289          *
290          */
291         for (i = 0; i < 2; i++) {
292                 handler = inst->session_head;
293                 if (handler &&
294                     ((request->timestamp - handler->timestamp) > inst->timer_limit)) {
295                         node = rbtree_find(inst->session_tree, handler);
296                         rad_assert(node != NULL);
297                         rbtree_delete(inst->session_tree, node);
298                         
299                         inst->session_head = handler->next;
300                         if (handler->next) handler->next->prev = NULL;
301                         eap_handler_free(handler);
302                 }
303         }
304
305         handler = NULL;
306         node = rbtree_find(inst->session_tree, &myHandler);
307         if (node) {
308                 handler = rbtree_node2data(inst->session_tree, node);
309
310                 /*
311                  *      Check against replays.  The client can re-play
312                  *      a State attribute verbatim, so we wish to
313                  *      ensure that the attribute falls within the
314                  *      valid time window, which is the second at
315                  *      which it was sent out.
316                  *
317                  *      Hmm... I'm not sure that this step is
318                  *      necessary, or even that it does anything.
319                  */
320                 if (verify_state(state, handler->timestamp) != 0) {
321                         handler = NULL;
322                 } else {
323                         /*
324                          *      It's OK, delete it from the tree.
325                          */
326                         rbtree_delete(inst->session_tree, node);
327
328                         /*
329                          *      And unsplice it from the linked list.
330                          */
331                         if (handler->prev) {
332                                 handler->prev->next = handler->next;
333                         } else {
334                                 inst->session_head = NULL;
335                         }
336                         if (handler->next) {
337                                 handler->next->prev = handler->prev;
338                         } else {
339                                 inst->session_tail = NULL;
340                         }
341                         handler->prev = handler->next = NULL;
342                 }
343         }
344
345         pthread_mutex_unlock(&(inst->session_mutex));
346
347         /*
348          *      Not found.
349          */
350         if (!node) {
351                 DEBUG2("  rlm_eap: Request not found in the list");
352                 return NULL;
353         }
354
355         /*
356          *      Found, but state verification failed.
357          */
358         if (!handler) {
359                 radlog(L_ERR, "rlm_eap: State verification failed.");
360                 return NULL;
361         }
362
363         DEBUG2("  rlm_eap: Request found, released from the list");
364
365         /*
366          *      Remember what the previous request was.
367          */
368         eap_ds_free(&(handler->prev_eapds));
369         handler->prev_eapds = handler->eap_ds;
370         handler->eap_ds = NULL;
371         
372         return handler;
373 }