More checks on handler_tree
[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 #ifdef HAVE_PTHREAD_H
31 #define PTHREAD_MUTEX_LOCK pthread_mutex_lock
32 #define PTHREAD_MUTEX_UNLOCK pthread_mutex_unlock
33 #else
34 #define PTHREAD_MUTEX_LOCK(_x)
35 #define PTHREAD_MUTEX_UNLOCK(_x)
36 #endif
37
38 /*
39  * Allocate a new EAP_PACKET
40  */
41 EAP_PACKET *eap_packet_alloc(void)
42 {
43         EAP_PACKET   *rp;
44
45         rp = rad_malloc(sizeof(EAP_PACKET));
46         memset(rp, 0, sizeof(EAP_PACKET));
47         return rp;
48 }
49
50 /*
51  * Free EAP_PACKET
52  */
53 void eap_packet_free(EAP_PACKET **eap_packet_ptr)
54 {
55         EAP_PACKET *eap_packet;
56
57         if (!eap_packet_ptr) return;
58         eap_packet = *eap_packet_ptr;
59         if (!eap_packet) return;
60
61         if (eap_packet->type.data) {
62                 /*
63                  *      There's no packet, OR the type data isn't
64                  *      pointing inside of the packet: free it.
65                  */
66                 if ((eap_packet->packet == NULL) ||
67                     (eap_packet->type.data != (eap_packet->packet + 5))) {
68                         free(eap_packet->type.data);
69                 }
70                 eap_packet->type.data = NULL;
71         }
72
73         if (eap_packet->packet) {
74                 free(eap_packet->packet);
75                 eap_packet->packet = NULL;
76         }
77
78         free(eap_packet);
79
80         *eap_packet_ptr = NULL;
81 }
82
83 /*
84  * Allocate a new EAP_PACKET
85  */
86 EAP_DS *eap_ds_alloc(void)
87 {
88         EAP_DS  *eap_ds;
89
90         eap_ds = rad_malloc(sizeof(EAP_DS));
91         memset(eap_ds, 0, sizeof(EAP_DS));
92         if ((eap_ds->response = eap_packet_alloc()) == NULL) {
93                 eap_ds_free(&eap_ds);
94                 return NULL;
95         }
96         if ((eap_ds->request = eap_packet_alloc()) == NULL) {
97                 eap_ds_free(&eap_ds);
98                 return NULL;
99         }
100
101         return eap_ds;
102 }
103
104 void eap_ds_free(EAP_DS **eap_ds_p)
105 {
106         EAP_DS *eap_ds;
107
108         if (!eap_ds_p) return;
109
110         eap_ds = *eap_ds_p;
111         if (!eap_ds) return;
112
113         if (eap_ds->response) eap_packet_free(&(eap_ds->response));
114         if (eap_ds->request) eap_packet_free(&(eap_ds->request));
115
116         free(eap_ds);
117         *eap_ds_p = NULL;
118 }
119
120 /*
121  * Allocate a new EAP_HANDLER
122  */
123 EAP_HANDLER *eap_handler_alloc(rlm_eap_t *inst)
124 {
125         EAP_HANDLER     *handler;
126
127         handler = rad_malloc(sizeof(EAP_HANDLER));
128         memset(handler, 0, sizeof(EAP_HANDLER));
129
130         if (inst->handler_tree) {
131                 PTHREAD_MUTEX_LOCK(&(inst->handler_mutex));
132                 rbtree_insert(inst->handler_tree, handler);
133                 PTHREAD_MUTEX_UNLOCK(&(inst->handler_mutex));
134         
135         }
136         return handler;
137 }
138
139 void eap_handler_free(rlm_eap_t *inst, EAP_HANDLER *handler)
140 {
141         if (!handler)
142                 return;
143
144         if (inst->handler_tree) {
145                 PTHREAD_MUTEX_LOCK(&(inst->handler_mutex));
146                 rbtree_deletebydata(inst->handler_tree, handler);
147                 PTHREAD_MUTEX_UNLOCK(&(inst->handler_mutex));
148         }
149
150         if (handler->identity) {
151                 free(handler->identity);
152                 handler->identity = NULL;
153         }
154
155         if (handler->prev_eapds) eap_ds_free(&(handler->prev_eapds));
156         if (handler->eap_ds) eap_ds_free(&(handler->eap_ds));
157
158         if ((handler->opaque) && (handler->free_opaque)) {
159                 handler->free_opaque(handler->opaque);
160                 handler->opaque = NULL;
161         }
162         else if ((handler->opaque) && (handler->free_opaque == NULL))
163                 radlog(L_ERR, "Possible memory leak ...");
164
165         handler->opaque = NULL;
166         handler->free_opaque = NULL;
167
168         if (handler->certs) pairfree(&handler->certs);
169
170         free(handler);
171 }
172
173
174 typedef struct check_handler_t {
175         rlm_eap_t       *inst;
176         EAP_HANDLER     *handler;
177         int             trips;
178 } check_handler_t;
179
180 static void check_handler(void *data)
181 {
182         int do_warning = FALSE;
183         uint8_t state[8];
184         check_handler_t *check = data;
185
186         if (!check) return;
187
188         if (!check->inst || !check->handler) {
189                 free(check);
190                 return;
191         }
192
193         if (!check->inst->handler_tree) goto done;
194
195         PTHREAD_MUTEX_LOCK(&(check->inst->handler_mutex));
196         if (!rbtree_finddata(check->inst->handler_tree, check->handler)) {
197                 goto done;
198         }
199
200         /*
201          *      The session has continued *after* this packet.
202          *      Don't do a warning.
203          */
204         if (check->handler->trips > check->trips) {
205                 goto done;
206         }
207
208         if (check->handler->tls && !check->handler->finished) {
209                 do_warning = TRUE;
210                 memcpy(state, check->handler->state, sizeof(state));
211         }
212
213 done:
214         PTHREAD_MUTEX_UNLOCK(&(check->inst->handler_mutex));
215         free(check);
216
217         if (do_warning) {
218                 DEBUG("WARNING: !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!");
219                 DEBUG("WARNING: !! EAP session for state 0x%02x%02x%02x%02x%02x%02x%02x%02x did not finish!",
220                       state[0], state[1],
221                       state[2], state[3],
222                       state[4], state[5],
223                       state[6], state[7]);
224
225                 DEBUG("WARNING: !! Please read http://wiki.freeradius.org/Certificate_Compatibility");
226                 DEBUG("WARNING: !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!");
227         }
228 }
229
230 void eaptype_free(EAP_TYPES *i)
231 {
232         if (i->type->detach) (i->type->detach)(i->type_data);
233         i->type_data = NULL;
234         if (i->handle) lt_dlclose(i->handle);
235         free(i);
236 }
237
238
239 void eaplist_free(rlm_eap_t *inst)
240 {
241         EAP_HANDLER *node, *next;
242
243         for (node = inst->session_head; node != NULL; node = next) {
244                 next = node->next;
245                 eap_handler_free(inst, node);
246         }
247
248         inst->session_head = inst->session_tail = NULL;
249 }
250
251 /*
252  *      Return a 32-bit random number.
253  */
254 static uint32_t eap_rand(fr_randctx *ctx)
255 {
256         uint32_t num;
257
258         num = ctx->randrsl[ctx->randcnt++];
259         if (ctx->randcnt >= 256) {
260                 ctx->randcnt = 0;
261                 fr_isaac(ctx);
262         }
263
264         return num;
265 }
266
267
268 static EAP_HANDLER *eaplist_delete(rlm_eap_t *inst, EAP_HANDLER *handler)
269 {
270         rbnode_t *node;
271
272         node = rbtree_find(inst->session_tree, handler);
273         if (!node) return NULL;
274
275         handler = rbtree_node2data(inst->session_tree, node);
276
277         /*
278          *      Delete old handler from the tree.
279          */
280         rbtree_delete(inst->session_tree, node);
281         
282         /*
283          *      And unsplice it from the linked list.
284          */
285         if (handler->prev) {
286                 handler->prev->next = handler->next;
287         } else {
288                 inst->session_head = handler->next;
289         }
290         if (handler->next) {
291                 handler->next->prev = handler->prev;
292         } else {
293                 inst->session_tail = handler->prev;
294         }
295         handler->prev = handler->next = NULL;
296
297         return handler;
298 }
299
300
301 static void eaplist_expire(rlm_eap_t *inst, time_t timestamp)
302 {
303         int i;
304         EAP_HANDLER *handler;
305
306         /*
307          *      Check the first few handlers in the list, and delete
308          *      them if they're too old.  We don't need to check them
309          *      all, as incoming requests will quickly cause older
310          *      handlers to be deleted.
311          *
312          */
313         for (i = 0; i < 3; i++) {
314                 handler = inst->session_head;
315                 if (!handler) break;
316
317                 /*
318                  *      Expire entries from the start of the list.
319                  *      They should be the oldest ones.
320                  */
321                 if ((timestamp - handler->timestamp) > inst->timer_limit) {
322                         rbnode_t *node;
323                         node = rbtree_find(inst->session_tree, handler);
324                         rad_assert(node != NULL);
325                         rbtree_delete(inst->session_tree, node);
326
327                         /*
328                          *      handler == inst->session_head
329                          */
330                         inst->session_head = handler->next;
331                         if (handler->next) {
332                                 handler->next->prev = NULL;
333                         } else {
334                                 inst->session_head = NULL;
335                                 inst->session_tail = NULL;
336                         }
337                         eap_handler_free(inst, handler);
338                 }
339         }
340 }
341
342 /*
343  *      Add a handler to the set of active sessions.
344  *
345  *      Since we're adding it to the list, we guess that this means
346  *      the packet needs a State attribute.  So add one.
347  */
348 int eaplist_add(rlm_eap_t *inst, EAP_HANDLER *handler)
349 {
350         int             status = 0;
351         VALUE_PAIR      *state;
352         REQUEST         *request = handler->request;
353
354         rad_assert(handler != NULL);
355         rad_assert(request != NULL);
356
357         /*
358          *      Generate State, since we've been asked to add it to
359          *      the list.
360          */
361         state = pairmake("State", "0x00", T_OP_EQ);
362         if (!state) return 0;
363
364         /*
365          *      The time at which this request was made was the time
366          *      at which it was received by the RADIUS server.
367          */
368         handler->timestamp = request->timestamp;
369         handler->status = 1;
370
371         handler->src_ipaddr = request->packet->src_ipaddr;
372         handler->eap_id = handler->eap_ds->request->id;
373
374         /*
375          *      Playing with a data structure shared among threads
376          *      means that we need a lock, to avoid conflict.
377          */
378         PTHREAD_MUTEX_LOCK(&(inst->session_mutex));
379
380         /*
381          *      If we have a DoS attack, discard new sessions.
382          */
383         if (rbtree_num_elements(inst->session_tree) >= inst->max_sessions) {
384                 status = -1;
385                 eaplist_expire(inst, handler->timestamp);
386                 goto done;
387         }
388
389         /*
390          *      Create a unique content for the State variable.
391          *      It will be modified slightly per round trip, but less so
392          *      than in 1.x.
393          */
394         if (handler->trips == 0) {
395                 int i;
396
397                 for (i = 0; i < 4; i++) {
398                         uint32_t lvalue;
399
400                         lvalue = eap_rand(&inst->rand_pool);
401
402                         memcpy(handler->state + i * 4, &lvalue,
403                                sizeof(lvalue));
404                 }               
405         }
406
407         memcpy(state->vp_octets, handler->state, sizeof(handler->state));
408         state->length = EAP_STATE_LEN;
409
410         /*
411          *      Add some more data to distinguish the sessions.
412          */
413         state->vp_octets[4] = handler->trips ^ handler->state[0];
414         state->vp_octets[5] = handler->eap_id ^ handler->state[1];
415         state->vp_octets[6] = handler->eap_type ^ handler->state[2];
416
417         /*
418          *      and copy the state back again.
419          */
420         memcpy(handler->state, state->vp_octets, sizeof(handler->state));
421
422         /*
423          *      Big-time failure.
424          */
425         status = rbtree_insert(inst->session_tree, handler);
426
427         /*
428          *      Catch Access-Challenge without response.
429          */
430         if (inst->handler_tree) {
431                 check_handler_t *check = rad_malloc(sizeof(*check));
432
433                 check->inst = inst;
434                 check->handler = handler;
435                 check->trips = handler->trips;
436                 request_data_add(request, inst, 0, check, check_handler);
437         }
438
439         if (status) {
440                 EAP_HANDLER *prev;
441
442                 prev = inst->session_tail;
443                 if (prev) {
444                         prev->next = handler;
445                         handler->prev = prev;
446                         handler->next = NULL;
447                         inst->session_tail = handler;
448                 } else {
449                         inst->session_head = inst->session_tail = handler;
450                         handler->next = handler->prev = NULL;
451                 }
452         }
453
454         /*
455          *      Now that we've finished mucking with the list,
456          *      unlock it.
457          */
458  done:
459
460         /*
461          *      We don't need this any more.
462          */
463         if (status > 0) handler->request = NULL;
464
465         PTHREAD_MUTEX_UNLOCK(&(inst->session_mutex));
466
467         if (status <= 0) {
468                 pairfree(&state);
469
470                 if (status < 0) {
471                         static time_t last_logged = 0;
472
473                         if (last_logged < handler->timestamp) {
474                                 last_logged = handler->timestamp;
475                                 radlog(L_ERR, "rlm_eap: Too many open sessions.  Try increasing \"max_sessions\" in the EAP module configuration");
476                         }                                      
477                 } else {
478                         radlog(L_ERR, "rlm_eap: Internal error: failed to store handler");
479                 }
480                 return 0;
481         }
482
483         pairadd(&(request->reply->vps), state);
484
485         return 1;
486 }
487
488 /*
489  *      Find a a previous EAP-Request sent by us, which matches
490  *      the current EAP-Response.
491  *
492  *      Then, release the handle from the list, and return it to
493  *      the caller.
494  *
495  *      Also since we fill the eap_ds with the present EAP-Response we
496  *      got to free the prev_eapds & move the eap_ds to prev_eapds
497  */
498 EAP_HANDLER *eaplist_find(rlm_eap_t *inst, REQUEST *request,
499                           eap_packet_t *eap_packet)
500 {
501         VALUE_PAIR      *state;
502         EAP_HANDLER     *handler, myHandler;
503
504         /*
505          *      We key the sessions off of the 'state' attribute, so it
506          *      must exist.
507          */
508         state = pairfind(request->packet->vps, PW_STATE, 0);
509         if (!state ||
510             (state->length != EAP_STATE_LEN)) {
511                 return NULL;
512         }
513
514         myHandler.src_ipaddr = request->packet->src_ipaddr;
515         myHandler.eap_id = eap_packet->id;
516         memcpy(myHandler.state, state->vp_strvalue, sizeof(myHandler.state));
517
518         /*
519          *      Playing with a data structure shared among threads
520          *      means that we need a lock, to avoid conflict.
521          */
522         PTHREAD_MUTEX_LOCK(&(inst->session_mutex));
523
524         eaplist_expire(inst, request->timestamp);
525
526         handler = eaplist_delete(inst, &myHandler);
527         PTHREAD_MUTEX_UNLOCK(&(inst->session_mutex));
528
529         /*
530          *      Might not have been there.
531          */
532         if (!handler) {
533                 radlog(L_ERR, "rlm_eap: No EAP session matching the State variable.");
534                 return NULL;
535         }
536
537         if (handler->trips >= 50) {
538                 RDEBUG2("More than 50 authentication packets for this EAP session.  Aborted.");
539                 eap_handler_free(inst, handler);
540                 return NULL;
541         }
542         handler->trips++;
543
544         RDEBUG2("Request found, released from the list");
545
546         /*
547          *      Remember what the previous request was.
548          */
549         eap_ds_free(&(handler->prev_eapds));
550         handler->prev_eapds = handler->eap_ds;
551         handler->eap_ds = NULL;
552
553         return handler;
554 }