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         free(i);
151 }
152
153
154 void eaplist_free(rlm_eap_t *inst)
155 {
156         EAP_HANDLER *node, *next;
157
158         for (node = inst->session_head; node != NULL; node = next) {
159                 next = node->next;
160                 eap_handler_free(node);
161         }
162
163         inst->session_head = inst->session_tail = NULL;
164 }
165
166 /*
167  *      Add a handler to the set of active sessions.
168  *
169  *      Since we're adding it to the list, we guess that this means
170  *      the packet needs a State attribute.  So add one.
171  */
172 int eaplist_add(rlm_eap_t *inst, EAP_HANDLER *handler)
173 {
174         int             status;
175         VALUE_PAIR      *state;
176         
177         rad_assert(handler != NULL);
178         rad_assert(handler->request != NULL);
179
180         /*
181          *      Generate State, since we've been asked to add it to
182          *      the list.
183          */
184         state = generate_state(handler->request->timestamp);
185         pairadd(&(handler->request->reply->vps), state);
186
187         /*
188          *      Create a unique 'key' for the handler, based
189          *      on State, Client-IP-Address, and EAP ID.
190          */
191         rad_assert(state->length == EAP_STATE_LEN);
192
193         /*
194          *      The time at which this request was made was the time
195          *      at which it was received by the RADIUS server.
196          */
197         handler->timestamp = handler->request->timestamp;
198         handler->status = 1;
199
200         memcpy(handler->state, state->strvalue, sizeof(handler->state));
201         handler->src_ipaddr = handler->request->packet->src_ipaddr;
202         handler->eap_id = handler->eap_ds->request->id;
203
204         /*
205          *      We don't need this any more.
206          */
207         handler->request = NULL;
208
209         /*
210          *      Playing with a data structure shared among threads
211          *      means that we need a lock, to avoid conflict.
212          */
213         pthread_mutex_lock(&(inst->session_mutex));
214
215         /*
216          *      Big-time failure.
217          */
218         status = rbtree_insert(inst->session_tree, handler);
219
220         if (status) {
221                 EAP_HANDLER *prev;
222
223                 prev = inst->session_tail;
224                 if (prev) {
225                         prev->next = handler;
226                         handler->prev = prev;
227                         handler->next = NULL;
228                         inst->session_tail = handler;
229                 } else {
230                         inst->session_head = inst->session_tail = handler;
231                         handler->next = handler->prev = NULL;
232                 }
233         }
234
235         /*
236          *      Now that we've finished mucking with the list,
237          *      unlock it.
238          */
239         pthread_mutex_unlock(&(inst->session_mutex));
240
241         if (!status) {
242                 radlog(L_ERR, "rlm_eap: Failed to remember handler!");
243                 eap_handler_free(handler);
244                 return 0;
245         }
246
247         return 1;
248 }
249
250 /*
251  *      Find a a previous EAP-Request sent by us, which matches
252  *      the current EAP-Response.
253  *
254  *      Then, release the handle from the list, and return it to
255  *      the caller.
256  *
257  *      Also since we fill the eap_ds with the present EAP-Response we
258  *      got to free the prev_eapds & move the eap_ds to prev_eapds
259  */
260 EAP_HANDLER *eaplist_find(rlm_eap_t *inst, REQUEST *request,
261                           eap_packet_t *eap_packet)
262 {
263         int             i;
264         VALUE_PAIR      *state;
265         rbnode_t        *node;
266         EAP_HANDLER     *handler, myHandler;
267
268         /*
269          *      We key the sessions off of the 'state' attribute, so it
270          *      must exist.
271          */
272         state = pairfind(request->packet->vps, PW_STATE);
273         if (!state ||
274             (state->length != EAP_STATE_LEN)) {
275                 return NULL;
276         }
277
278         myHandler.src_ipaddr = request->packet->src_ipaddr;
279         myHandler.eap_id = eap_packet->id;
280         memcpy(myHandler.state, state->strvalue, sizeof(myHandler.state));
281
282         /*
283          *      Playing with a data structure shared among threads
284          *      means that we need a lock, to avoid conflict.
285          */
286         pthread_mutex_lock(&(inst->session_mutex));
287
288         /*
289          *      Check the first few handlers in the list, and delete
290          *      them if they're too old.  We don't need to check them
291          *      all, as incoming requests will quickly cause older
292          *      handlers to be deleted.
293          *
294          */
295         for (i = 0; i < 2; i++) {
296                 handler = inst->session_head;
297                 if (handler &&
298                     ((request->timestamp - handler->timestamp) > inst->timer_limit)) {
299                         node = rbtree_find(inst->session_tree, handler);
300                         rad_assert(node != NULL);
301                         rbtree_delete(inst->session_tree, node);
302                         
303                         inst->session_head = handler->next;
304                         if (handler->next) handler->next->prev = NULL;
305                         eap_handler_free(handler);
306                 }
307         }
308
309         handler = NULL;
310         node = rbtree_find(inst->session_tree, &myHandler);
311         if (node) {
312                 handler = rbtree_node2data(inst->session_tree, node);
313
314                 /*
315                  *      Check against replays.  The client can re-play
316                  *      a State attribute verbatim, so we wish to
317                  *      ensure that the attribute falls within the
318                  *      valid time window, which is the second at
319                  *      which it was sent out.
320                  *
321                  *      Hmm... I'm not sure that this step is
322                  *      necessary, or even that it does anything.
323                  */
324                 if (verify_state(state, handler->timestamp) != 0) {
325                         handler = NULL;
326                 } else {
327                         /*
328                          *      It's OK, delete it from the tree.
329                          */
330                         rbtree_delete(inst->session_tree, node);
331
332                         /*
333                          *      And unsplice it from the linked list.
334                          */
335                         if (handler->prev) {
336                                 handler->prev->next = handler->next;
337                         } else {
338                                 inst->session_head = handler->next;
339                         }
340                         if (handler->next) {
341                                 handler->next->prev = handler->prev;
342                         } else {
343                                 inst->session_tail = handler->prev;
344                         }
345                         handler->prev = handler->next = NULL;
346                 }
347         }
348
349         pthread_mutex_unlock(&(inst->session_mutex));
350
351         /*
352          *      Not found.
353          */
354         if (!node) {
355                 DEBUG2("  rlm_eap: Request not found in the list");
356                 return NULL;
357         }
358
359         /*
360          *      Found, but state verification failed.
361          */
362         if (!handler) {
363                 radlog(L_ERR, "rlm_eap: State verification failed.");
364                 return NULL;
365         }
366
367         DEBUG2("  rlm_eap: Request found, released from the list");
368
369         /*
370          *      Remember what the previous request was.
371          */
372         eap_ds_free(&(handler->prev_eapds));
373         handler->prev_eapds = handler->eap_ds;
374         handler->eap_ds = NULL;
375         
376         return handler;
377 }