perl -i -npe "s/[ \t]+$//g" `find src -name "*.[ch]" -print`
[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_p)
120 {
121         EAP_HANDLER *handler;
122
123         if ((handler_p == NULL) || (*handler_p == NULL))
124                 return;
125
126         handler = *handler_p;
127         if (handler->identity) {
128                 free(handler->identity);
129                 handler->identity = NULL;
130         }
131
132         if (handler->prev_eapds) eap_ds_free(&(handler->prev_eapds));
133         if (handler->eap_ds) eap_ds_free(&(handler->eap_ds));
134
135         if ((handler->opaque) && (handler->free_opaque)) {
136                 handler->free_opaque(handler->opaque);
137                 handler->opaque = NULL;
138         }
139         else if ((handler->opaque) && (handler->free_opaque == NULL))
140                 radlog(L_ERR, "Possible memory leak ...");
141
142         handler->opaque = NULL;
143         handler->free_opaque = NULL;
144         handler->next = NULL;
145
146         free(handler);
147         *handler_p = NULL;
148 }
149
150 void eaptype_free(EAP_TYPES *i)
151 {
152         if (i->type->detach) (i->type->detach)(i->type_data);
153         i->type_data = NULL;
154         if (i->handle) lt_dlclose(i->handle);
155 }
156
157 void eaplist_free(rlm_eap_t *inst)
158 {
159         int i;
160
161         /*
162          *      The sessions are split out into an array, which makes
163          *      looking them up a bit faster.
164          */
165         for (i = 0; i < 256; i++) {
166                 EAP_HANDLER *node, *next;
167
168                 if (inst->sessions[i]) continue;
169
170                 node = inst->sessions[i];
171                 while (node) {
172                         next = node->next;
173                         eap_handler_free(&node);
174                         node = next;
175                 }
176
177                 inst->sessions[i] = NULL;
178         }
179 }
180
181 /*
182  *      Add a handler to the set of active sessions.
183  *
184  *      Since we're adding it to the list, we guess that this means
185  *      the packet needs a State attribute.  So add one.
186  */
187 int eaplist_add(rlm_eap_t *inst, EAP_HANDLER *handler)
188 {
189         EAP_HANDLER     **last;
190         VALUE_PAIR      *state;
191
192         rad_assert(handler != NULL);
193         rad_assert(handler->request != NULL);
194
195         /*
196          *      Generate State, since we've been asked to add it to
197          *      the list.
198          */
199         state = generate_state(handler->request->timestamp);
200         pairadd(&(handler->request->reply->vps), state);
201
202         /*
203          *      Create a unique 'key' for the handler, based
204          *      on State, Client-IP-Address, and EAP ID.
205          */
206         rad_assert(state->length == EAP_STATE_LEN);
207
208         memcpy(handler->state, state->strvalue, sizeof(handler->state));
209         handler->src_ipaddr = handler->request->packet->src_ipaddr;
210         handler->eap_id = handler->eap_ds->request->id;
211
212 #ifdef HAVE_PTHREAD_H
213         /*
214          *      Playing with a data structure shared among threads
215          *      means that we need a lock, to avoid conflict.
216          */
217         pthread_mutex_lock(&(inst->session_mutex));
218 #endif
219
220         /*
221          *      We key the array based on the challenge, which is
222          *      a random number.  This "fans out" the sessions, and
223          *      helps to minimize the amount of work we've got to do
224          *      under heavy load.
225          */
226         last = &(inst->sessions[state->strvalue[0]]);
227
228         while (*last) last = &((*last)->next);
229
230         *last = handler;
231
232         /*
233          *      The time at which this request was made was the time
234          *      at which it was received by the RADIUS server.
235          */
236         handler->timestamp = handler->request->timestamp;
237         handler->status = 1;
238         handler->next = NULL;
239
240 #ifdef HAVE_PTHREAD_H
241         /*
242          *      Now that we've finished mucking with the list,
243          *      unlock it.
244          */
245         pthread_mutex_unlock(&(inst->session_mutex));
246 #endif
247
248         /*
249          *      We don't need this any more.
250          */
251         handler->request = NULL;
252
253         return 1;
254 }
255
256 /*
257  *      Find a a previous EAP-Request sent by us, which matches
258  *      the current EAP-Response.
259  *
260  *      Then, release the handle from the list, and return it to
261  *      the caller.
262  *
263  *      Also since we fill the eap_ds with the present EAP-Response we
264  *      got to free the prev_eapds & move the eap_ds to prev_eapds
265  */
266 EAP_HANDLER *eaplist_find(rlm_eap_t *inst, REQUEST *request,
267                           eap_packet_t *eap_packet)
268 {
269         EAP_HANDLER     *node, *next;
270         VALUE_PAIR      *state;
271         EAP_HANDLER     **first,  **last;
272
273         /*
274          *      We key the sessions off of the 'state' attribute, so it
275          *      must exist.
276          */
277         state = pairfind(request->packet->vps, PW_STATE);
278         if (!state ||
279             (state->length != EAP_STATE_LEN)) {
280                 return NULL;
281         }
282
283 #ifdef HAVE_PTHREAD_H
284         /*
285          *      Playing with a data structure shared among threads
286          *      means that we need a lock, to avoid conflict.
287          */
288         pthread_mutex_lock(&(inst->session_mutex));
289 #endif
290
291         last = first = &(inst->sessions[state->strvalue[0]]);
292
293         for (node = *first; node; node = next) {
294                 next = node->next;
295
296                 /*
297                  *      If the time on this entry has expired,
298                  *      delete it.  We do this while walking the list,
299                  *      in order to spread out the work of deleting old
300                  *      sessions.
301                  */
302                 if ((request->timestamp - node->timestamp) > inst->timer_limit) {
303                         *last = next;
304                         eap_handler_free(&node);
305                         continue;
306                 }
307
308                 /*
309                  *      Find the previous part of the same conversation,
310                  *      keying off of the EAP ID, the client IP, and
311                  *      the State attribute.
312                  *
313                  *      If we've found a conversation, then we don't
314                  *      have to check entries later in the list for
315                  *      timeout, as they're guaranteed to be newer than
316                  *      the one we found.
317                  */
318                 if ((node->eap_id == eap_packet->id) &&
319                     (node->src_ipaddr == request->packet->src_ipaddr) &&
320                     (memcmp(node->state, state->strvalue, state->length) == 0)) {
321                         /*
322                          *      Check against replays.  The client can
323                          *      re-play a State attribute verbatim, so
324                          *      we wish to ensure that the attribute falls
325                          *      within the valid time window, which is
326                          *      the second at which it was sent out.
327                          */
328                         if (verify_state(state, node->timestamp) != 0) {
329                                 radlog(L_ERR, "rlm_eap: State verification failed.");
330                                 node = NULL;
331                                 break;
332                         }
333
334                         DEBUG2("  rlm_eap: Request found, released from the list");
335                         /*
336                          *      detach the node from the list
337                          */
338                         *last = next;
339                         node->next = NULL;
340
341                         /*
342                          *      Don't bother updating handler->request, etc.
343                          *      eap_handler() will do that for us.
344                          */
345
346                         /*
347                          *      Remember what the previous request was.
348                          */
349                         eap_ds_free(&(node->prev_eapds));
350                         node->prev_eapds = node->eap_ds;
351                         node->eap_ds = NULL;
352
353                         /*
354                          *      Stop here.
355                          */
356                         break;
357                 } else  {
358                         last = &(node->next);
359                 }
360         }
361
362 #ifdef HAVE_PTHREAD_H
363         pthread_mutex_unlock(&(inst->session_mutex));
364 #endif
365
366         if (!node) {
367                 DEBUG2("  rlm_eap: Request not found in the list");
368         }
369         return node;
370 }