Use new RDEBUG macro in rlm_eap
[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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
19  *
20  * Copyright 2000,2001,2006  The FreeRADIUS server project
21  * Copyright 2001  hereUare Communications, Inc. <raghud@hereuare.com>
22  */
23
24 #include <freeradius-devel/ident.h>
25 RCSID("$Id$")
26
27 #include <stdio.h>
28 #include "rlm_eap.h"
29
30 /*
31  * Allocate a new EAP_PACKET
32  */
33 EAP_PACKET *eap_packet_alloc(void)
34 {
35         EAP_PACKET   *rp;
36
37         rp = rad_malloc(sizeof(EAP_PACKET));
38         memset(rp, 0, sizeof(EAP_PACKET));
39         return rp;
40 }
41
42 /*
43  * Free EAP_PACKET
44  */
45 void eap_packet_free(EAP_PACKET **eap_packet_ptr)
46 {
47         EAP_PACKET *eap_packet;
48
49         if (!eap_packet_ptr) return;
50         eap_packet = *eap_packet_ptr;
51         if (!eap_packet) return;
52
53         if (eap_packet->type.data) {
54                 /*
55                  *      There's no packet, OR the type data isn't
56                  *      pointing inside of the packet: free it.
57                  */
58                 if ((eap_packet->packet == NULL) ||
59                     (eap_packet->type.data != (eap_packet->packet + 5))) {
60                         free(eap_packet->type.data);
61                 }
62                 eap_packet->type.data = NULL;
63         }
64
65         if (eap_packet->packet) {
66                 free(eap_packet->packet);
67                 eap_packet->packet = NULL;
68         }
69
70         free(eap_packet);
71
72         *eap_packet_ptr = NULL;
73 }
74
75 /*
76  * Allocate a new EAP_PACKET
77  */
78 EAP_DS *eap_ds_alloc(void)
79 {
80         EAP_DS  *eap_ds;
81
82         eap_ds = rad_malloc(sizeof(EAP_DS));
83         memset(eap_ds, 0, sizeof(EAP_DS));
84         if ((eap_ds->response = eap_packet_alloc()) == NULL) {
85                 eap_ds_free(&eap_ds);
86                 return NULL;
87         }
88         if ((eap_ds->request = eap_packet_alloc()) == NULL) {
89                 eap_ds_free(&eap_ds);
90                 return NULL;
91         }
92
93         return eap_ds;
94 }
95
96 void eap_ds_free(EAP_DS **eap_ds_p)
97 {
98         EAP_DS *eap_ds;
99
100         if (!eap_ds_p) return;
101
102         eap_ds = *eap_ds_p;
103         if (!eap_ds) return;
104
105         if (eap_ds->response) eap_packet_free(&(eap_ds->response));
106         if (eap_ds->request) eap_packet_free(&(eap_ds->request));
107
108         free(eap_ds);
109         *eap_ds_p = NULL;
110 }
111
112 /*
113  * Allocate a new EAP_HANDLER
114  */
115 EAP_HANDLER *eap_handler_alloc(void)
116 {
117         EAP_HANDLER     *handler;
118
119         handler = rad_malloc(sizeof(EAP_HANDLER));
120         memset(handler, 0, sizeof(EAP_HANDLER));
121         return handler;
122 }
123
124 void eap_handler_free(EAP_HANDLER *handler)
125 {
126         if (!handler)
127                 return;
128
129         if (handler->identity) {
130                 free(handler->identity);
131                 handler->identity = NULL;
132         }
133
134         if (handler->prev_eapds) eap_ds_free(&(handler->prev_eapds));
135         if (handler->eap_ds) eap_ds_free(&(handler->eap_ds));
136
137         if ((handler->opaque) && (handler->free_opaque)) {
138                 handler->free_opaque(handler->opaque);
139                 handler->opaque = NULL;
140         }
141         else if ((handler->opaque) && (handler->free_opaque == NULL))
142                 radlog(L_ERR, "Possible memory leak ...");
143
144         handler->opaque = NULL;
145         handler->free_opaque = NULL;
146
147         free(handler);
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         free(i);
156 }
157
158
159 void eaplist_free(rlm_eap_t *inst)
160 {
161         EAP_HANDLER *node, *next;
162
163         for (node = inst->session_head; node != NULL; node = next) {
164                 next = node->next;
165                 eap_handler_free(node);
166         }
167
168         inst->session_head = inst->session_tail = NULL;
169 }
170
171 /*
172  *      Return a 32-bit random number.
173  */
174 static uint32_t eap_rand(fr_randctx *ctx)
175 {
176         uint32_t num;
177
178         num = ctx->randrsl[ctx->randcnt++];
179         if (ctx->randcnt >= 256) {
180                 ctx->randcnt = 0;
181                 fr_isaac(ctx);
182         }
183
184         return num;
185 }
186
187 static EAP_HANDLER *eaplist_delete(rlm_eap_t *inst, EAP_HANDLER *handler)
188 {
189         rbnode_t *node;
190
191         node = rbtree_find(inst->session_tree, handler);
192         if (!node) return NULL;
193
194         handler = rbtree_node2data(inst->session_tree, node);
195
196         /*
197          *      Delete old handler from the tree.
198          */
199         rbtree_delete(inst->session_tree, node);
200         
201         /*
202          *      And unsplice it from the linked list.
203          */
204         if (handler->prev) {
205                 handler->prev->next = handler->next;
206         } else {
207                 inst->session_head = handler->next;
208         }
209         if (handler->next) {
210                 handler->next->prev = handler->prev;
211         } else {
212                 inst->session_tail = handler->prev;
213         }
214         handler->prev = handler->next = NULL;
215
216         return handler;
217 }
218
219
220 static void eaplist_expire(rlm_eap_t *inst, time_t timestamp)
221 {
222         int i;
223         EAP_HANDLER *handler;
224
225         /*
226          *      Check the first few handlers in the list, and delete
227          *      them if they're too old.  We don't need to check them
228          *      all, as incoming requests will quickly cause older
229          *      handlers to be deleted.
230          *
231          */
232         for (i = 0; i < 2; i++) {
233                 handler = inst->session_head;
234                 if (handler &&
235                     ((timestamp - handler->timestamp) > inst->timer_limit)) {
236                         rbnode_t *node;
237                         node = rbtree_find(inst->session_tree, handler);
238                         rad_assert(node != NULL);
239                         rbtree_delete(inst->session_tree, node);
240
241                         /*
242                          *      handler == inst->session_head
243                          */
244                         inst->session_head = handler->next;
245                         if (handler->next) {
246                                 handler->next->prev = NULL;
247                         } else {
248                                 inst->session_head = NULL;
249                         }
250                         eap_handler_free(handler);
251                 }
252         }
253 }
254
255 /*
256  *      Add a handler to the set of active sessions.
257  *
258  *      Since we're adding it to the list, we guess that this means
259  *      the packet needs a State attribute.  So add one.
260  */
261 int eaplist_add(rlm_eap_t *inst, EAP_HANDLER *handler)
262 {
263         int             status = 0;
264         VALUE_PAIR      *state;
265         REQUEST         *request = handler->request;
266
267         rad_assert(handler != NULL);
268         rad_assert(request != NULL);
269
270         /*
271          *      Generate State, since we've been asked to add it to
272          *      the list.
273          */
274         state = pairmake("State", "0x00", T_OP_EQ);
275         if (!state) return 0;
276
277         /*
278          *      The time at which this request was made was the time
279          *      at which it was received by the RADIUS server.
280          */
281         handler->timestamp = request->timestamp;
282         handler->status = 1;
283
284         handler->src_ipaddr = request->packet->src_ipaddr;
285         handler->eap_id = handler->eap_ds->request->id;
286
287         /*
288          *      Playing with a data structure shared among threads
289          *      means that we need a lock, to avoid conflict.
290          */
291         pthread_mutex_lock(&(inst->session_mutex));
292
293         /*
294          *      If we have a DoS attack, discard new sessions.
295          */
296         if (rbtree_num_elements(inst->session_tree) >= inst->max_sessions) {
297                 eaplist_expire(inst, handler->timestamp);
298                 goto done;
299         }
300
301         /*
302          *      Create a unique content for the State variable.
303          *      It will be modified slightly per round trip, but less so
304          *      than in 1.x.
305          */
306         if (handler->trips == 0) {
307                 int i;
308
309                 for (i = 0; i < 4; i++) {
310                         uint32_t lvalue;
311
312                         lvalue = eap_rand(&inst->rand_pool);
313
314                         memcpy(handler->state + i * 4, &lvalue,
315                                sizeof(lvalue));
316                 }               
317         }
318
319         memcpy(state->vp_octets, handler->state, sizeof(handler->state));
320         state->length = EAP_STATE_LEN;
321
322         /*
323          *      Add some more data to distinguish the sessions.
324          */
325         state->vp_octets[4] = handler->trips ^ handler->state[0];
326         state->vp_octets[5] = handler->eap_id ^ handler->state[1];
327         state->vp_octets[6] = handler->eap_type ^ handler->state[2];
328
329         /*
330          *      and copy the state back again.
331          */
332         memcpy(handler->state, state->vp_octets, sizeof(handler->state));
333
334         /*
335          *      Big-time failure.
336          */
337         status = rbtree_insert(inst->session_tree, handler);
338
339         if (status) {
340                 EAP_HANDLER *prev;
341
342                 prev = inst->session_tail;
343                 if (prev) {
344                         prev->next = handler;
345                         handler->prev = prev;
346                         handler->next = NULL;
347                         inst->session_tail = handler;
348                 } else {
349                         inst->session_head = inst->session_tail = handler;
350                         handler->next = handler->prev = NULL;
351                 }
352         }
353
354         /*
355          *      Now that we've finished mucking with the list,
356          *      unlock it.
357          */
358  done:
359
360         /*
361          *      We don't need this any more.
362          */
363         if (status) handler->request = NULL;
364
365         pthread_mutex_unlock(&(inst->session_mutex));
366
367         if (!status) {
368                 pairfree(&state);
369                 radlog(L_ERR, "rlm_eap: Failed to store handler");
370                 return 0;
371         }
372
373         pairadd(&(request->reply->vps), state);
374
375         return 1;
376 }
377
378 /*
379  *      Find a a previous EAP-Request sent by us, which matches
380  *      the current EAP-Response.
381  *
382  *      Then, release the handle from the list, and return it to
383  *      the caller.
384  *
385  *      Also since we fill the eap_ds with the present EAP-Response we
386  *      got to free the prev_eapds & move the eap_ds to prev_eapds
387  */
388 EAP_HANDLER *eaplist_find(rlm_eap_t *inst, REQUEST *request,
389                           eap_packet_t *eap_packet)
390 {
391         VALUE_PAIR      *state;
392         EAP_HANDLER     *handler, myHandler;
393
394         /*
395          *      We key the sessions off of the 'state' attribute, so it
396          *      must exist.
397          */
398         state = pairfind(request->packet->vps, PW_STATE);
399         if (!state ||
400             (state->length != EAP_STATE_LEN)) {
401                 return NULL;
402         }
403
404         myHandler.src_ipaddr = request->packet->src_ipaddr;
405         myHandler.eap_id = eap_packet->id;
406         memcpy(myHandler.state, state->vp_strvalue, sizeof(myHandler.state));
407
408         /*
409          *      Playing with a data structure shared among threads
410          *      means that we need a lock, to avoid conflict.
411          */
412         pthread_mutex_lock(&(inst->session_mutex));
413
414         eaplist_expire(inst, request->timestamp);
415
416         handler = eaplist_delete(inst, &myHandler);
417         pthread_mutex_unlock(&(inst->session_mutex));
418
419         /*
420          *      Might not have been there.
421          */
422         if (!handler) {
423                 radlog(L_ERR, "rlm_eap: No EAP session matching the State variable.");
424                 return NULL;
425         }
426
427         if (handler->trips >= 50) {
428                 RDEBUG2("More than 50 authentication packets for this EAP session.  Aborted.");
429                 eap_handler_free(handler);
430                 return NULL;
431         }
432         handler->trips++;
433
434         RDEBUG2("Request found, released from the list");
435
436         /*
437          *      Remember what the previous request was.
438          */
439         eap_ds_free(&(handler->prev_eapds));
440         handler->prev_eapds = handler->eap_ds;
441         handler->eap_ds = NULL;
442
443         return handler;
444 }