Massive changes to clean up the EAP module, in preparation for
[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 /*
27  * Allocate a new EAP_PACKET
28  */
29 EAP_PACKET *eap_packet_alloc(void)
30 {
31         EAP_PACKET   *rp;
32
33         rp = rad_malloc(sizeof(EAP_PACKET));
34         memset(rp, 0, sizeof(EAP_PACKET));
35         return rp;
36 }
37
38 /*
39  * Free EAP_PACKET
40  */
41 void eap_packet_free(EAP_PACKET **eap_packet_ptr)
42 {
43         EAP_PACKET *eap_packet;
44
45         if (!eap_packet_ptr) return;
46         eap_packet = *eap_packet_ptr;
47         if (!eap_packet) return;
48
49         if (eap_packet->type.data) {
50                 /*
51                  * This is just a pointer in the packet
52                  * so we do not free it but we NULL it
53                 free(eap_packet->type.data);
54                 */
55                 eap_packet->type.data = NULL;
56         }
57
58         if (eap_packet->packet) {
59                 free(eap_packet->packet);
60                 eap_packet->packet = NULL;
61         }
62
63         free(eap_packet);
64
65         *eap_packet_ptr = NULL;
66 }
67
68 /*
69  * Allocate a new EAP_PACKET
70  */
71 EAP_DS *eap_ds_alloc(void)
72 {
73         EAP_DS  *eap_ds;
74         
75         eap_ds = rad_malloc(sizeof(EAP_DS));
76         memset(eap_ds, 0, sizeof(EAP_DS));
77         if ((eap_ds->response = eap_packet_alloc()) == NULL) {
78                 eap_ds_free(&eap_ds);
79                 return NULL;
80         }
81         if ((eap_ds->request = eap_packet_alloc()) == NULL) {
82                 eap_ds_free(&eap_ds);
83                 return NULL;
84         }
85
86         return eap_ds;
87 }
88
89 void eap_ds_free(EAP_DS **eap_ds_p)
90 {
91         EAP_DS *eap_ds;
92
93         if (!eap_ds_p) return;
94
95         eap_ds = *eap_ds_p;
96         if (!eap_ds) return;
97
98         if (eap_ds->response) eap_packet_free(&(eap_ds->response));
99         if (eap_ds->request) eap_packet_free(&(eap_ds->request));
100
101         free(eap_ds);
102         *eap_ds_p = NULL;
103 }
104
105 /*
106  * Allocate a new EAP_HANDLER
107  */
108 EAP_HANDLER *eap_handler_alloc(void)
109 {
110         EAP_HANDLER     *handler;
111         
112         handler = rad_malloc(sizeof(EAP_HANDLER));
113         memset(handler, 0, sizeof(EAP_HANDLER));
114         return handler;
115 }
116
117 void eap_handler_free(EAP_HANDLER **handler_p)
118 {
119         EAP_HANDLER *handler;
120
121         if ((handler_p == NULL) || (*handler_p == NULL))
122                 return;
123
124         handler = *handler_p;
125         if (handler->identity) {
126                 free(handler->identity);
127                 handler->identity = NULL;
128         }
129
130         if (handler->username) pairfree(&(handler->username));
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         /*
213          *      We key the array based on the challenge, which is
214          *      a random number.  This "fans out" the sessions, and
215          *      helps to minimize the amount of work we've got to do
216          *      under heavy load.
217          */
218         last = &(inst->sessions[state->strvalue[0]]);
219         while (*last) last = &((*last)->next);
220         
221         /*
222          *      The time at which this request was made was the time
223          *      at which it was received by the RADIUS server.
224          */
225         handler->timestamp = handler->request->timestamp;
226         handler->status = 1;
227         handler->next = NULL;
228
229         /*
230          *      We don't need this any more.
231          */
232         handler->request = NULL;
233
234         *last = handler;
235         return 1;
236 }
237
238 /*
239  *      Find a a previous EAP-Request sent by us, which matches
240  *      the current EAP-Response.
241  *
242  *      Then, release the handle from the list, and return it to
243  *      the caller.
244  *
245  *      Also since we fill the eap_ds with the present EAP-Response we
246  *      got to free the prev_eapds & move the eap_ds to prev_eapds
247  */
248 EAP_HANDLER *eaplist_find(rlm_eap_t *inst, REQUEST *request, int eap_id)
249 {
250         EAP_HANDLER     *node, *next, *ret = NULL;
251         VALUE_PAIR      *state;
252         EAP_HANDLER     **first,  **last;
253
254         /*
255          *      We key the sessions off of the 'state' attribute, so it
256          *      must exist.
257          */
258         state = pairfind(request->packet->vps, PW_STATE);
259         if (!state ||
260             (state->length != EAP_STATE_LEN)) {
261                 return NULL;
262         }
263
264         last = first = &(inst->sessions[state->strvalue[0]]);
265
266         for (node = *first; node; node = next) {
267                 next = node->next;
268
269                 /*
270                  *      If the time on this entry has expired, 
271                  *      delete it.  We do this while walking the list,
272                  *      in order to spread out the work of deleting old
273                  *      sessions.
274                  */
275                 if ((request->timestamp - node->timestamp) > inst->timer_limit) {
276                         *last = next;
277                         eap_handler_free(&node);
278                         continue;
279                 }
280
281                 /*
282                  *      Find the previous part of the same conversation,
283                  *      keying off of the EAP ID, the client IP, and
284                  *      the State attribute.
285                  *
286                  *      If we've found a conversation, then we don't
287                  *      have to check entries later in the list for
288                  *      timeout, as they're guaranteed to be newer than
289                  *      the one we found.
290                  */
291                 if ((node->eap_id == eap_id) &&
292                     (node->src_ipaddr == request->packet->src_ipaddr) &&
293                     (memcmp(node->state, state->strvalue, state->length) == 0)) {
294                         /*
295                          *      Check against replays.  The client can
296                          *      re-play a State attribute verbatim, so
297                          *      we wish to ensure that the attribute falls
298                          *      within the valid time window, which is
299                          *      the second at which it was sent out.
300                          */
301                         if (verify_state(state, node->timestamp) != 0) {
302                                 radlog(L_ERR, "rlm_eap: State verification failed.");
303                                 return NULL;
304                         }
305                         
306                         DEBUG2("  rlm_eap: Request found, released from the list");
307                         /*
308                          *      detach the node from the list
309                          */
310                         *last = next;
311                         node->next = NULL;
312
313                         /*
314                          *      Don't bother updating handler->request, etc.
315                          *      eap_handler() will do that for us.
316                          */
317
318                         /*
319                          *      Remember what the previous request was.
320                          */
321                         eap_ds_free(&(node->prev_eapds));
322                         node->prev_eapds = node->eap_ds;
323                         node->eap_ds = NULL;
324
325                         /*
326                          *      And return it to the caller.
327                          */
328                         return node;
329                 } else  {
330                         last = &(node->next);
331                 }
332         }
333
334         if (!ret) {
335                 DEBUG2("  rlm_eap: Request not found in the list");
336         }
337         return ret;
338 }