e0fdc373ea5254261a6bd7a5b95660addc94e0db
[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_opaque_free(EAP_HANDLER *handler)
140 {
141         if (!handler)
142                 return;
143
144         eap_handler_free(handler->inst_holder, handler);
145 }
146
147 void eap_handler_free(rlm_eap_t *inst, EAP_HANDLER *handler)
148 {
149         if (!handler)
150                 return;
151
152         if (inst->handler_tree) {
153                 PTHREAD_MUTEX_LOCK(&(inst->handler_mutex));
154                 rbtree_deletebydata(inst->handler_tree, handler);
155                 PTHREAD_MUTEX_UNLOCK(&(inst->handler_mutex));
156         }
157
158         if (handler->identity) {
159                 free(handler->identity);
160                 handler->identity = NULL;
161         }
162
163         if (handler->prev_eapds) eap_ds_free(&(handler->prev_eapds));
164         if (handler->eap_ds) eap_ds_free(&(handler->eap_ds));
165
166         if ((handler->opaque) && (handler->free_opaque)) {
167                 handler->free_opaque(handler->opaque);
168                 handler->opaque = NULL;
169         }
170         else if ((handler->opaque) && (handler->free_opaque == NULL))
171                 radlog(L_ERR, "rlm_eap (%s): Possible memory leak ...", 
172                        inst->xlat_name);
173
174         handler->opaque = NULL;
175         handler->free_opaque = NULL;
176
177         if (handler->certs) pairfree(&handler->certs);
178
179         free(handler);
180 }
181
182
183 typedef struct check_handler_t {
184         rlm_eap_t       *inst;
185         EAP_HANDLER     *handler;
186         int             trips;
187 } check_handler_t;
188
189 static void check_handler(void *data)
190 {
191         int do_warning = FALSE;
192         uint8_t state[8];
193         check_handler_t *check = data;
194
195         if (!check) return;
196
197         if (!check->inst || !check->handler) {
198                 free(check);
199                 return;
200         }
201
202         if (!check->inst->handler_tree) goto done;
203
204         PTHREAD_MUTEX_LOCK(&(check->inst->handler_mutex));
205         if (!rbtree_finddata(check->inst->handler_tree, check->handler)) {
206                 goto done;
207         }
208
209         /*
210          *      The session has continued *after* this packet.
211          *      Don't do a warning.
212          */
213         if (check->handler->trips > check->trips) {
214                 goto done;
215         }
216
217         /*
218          *      No TLS means no warnings.
219          */
220         if (!check->handler->tls) goto done;
221
222         /*
223          *      If we're being deleted early, it's likely because we
224          *      received a transmit from the client that re-uses the
225          *      same RADIUS Id, which forces the current packet to be
226          *      deleted.  In that case, ignore the error.
227          */
228         if (time(NULL) < (check->handler->timestamp + 3)) goto done;
229
230         if (!check->handler->finished) {
231                 do_warning = TRUE;
232                 memcpy(state, check->handler->state, sizeof(state));
233         }
234
235 done:
236         PTHREAD_MUTEX_UNLOCK(&(check->inst->handler_mutex));
237         free(check);
238
239         if (do_warning) {
240                 DEBUGW("!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!");
241                 DEBUGW("!! EAP session with state 0x%02x%02x%02x%02x%02x%02x%02x%02x did not finish!  !!",
242                       state[0], state[1],
243                       state[2], state[3],
244                       state[4], state[5],
245                       state[6], state[7]);
246
247                 DEBUGW("!! Please read http://wiki.freeradius.org/guide/Certificate_Compatibility     !!");
248                 DEBUGW("!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!");
249         }
250 }
251
252 void eaptype_free(EAP_TYPES *i)
253 {
254         cf_section_parse_free(i->cs, i->type_data);
255
256         if (i->type->detach) (i->type->detach)(i->type_data);
257         i->type_data = NULL;
258         if (i->handle) lt_dlclose(i->handle);
259         free(i);
260 }
261
262
263 void eaplist_free(rlm_eap_t *inst)
264 {
265         EAP_HANDLER *node, *next;
266
267         for (node = inst->session_head; node != NULL; node = next) {
268                 next = node->next;
269                 eap_handler_free(inst, node);
270         }
271
272         inst->session_head = inst->session_tail = NULL;
273 }
274
275 /*
276  *      Return a 32-bit random number.
277  */
278 static uint32_t eap_rand(fr_randctx *ctx)
279 {
280         uint32_t num;
281
282         num = ctx->randrsl[ctx->randcnt++];
283         if (ctx->randcnt >= 256) {
284                 ctx->randcnt = 0;
285                 fr_isaac(ctx);
286         }
287
288         return num;
289 }
290
291
292 static EAP_HANDLER *eaplist_delete(rlm_eap_t *inst, REQUEST *request,
293                                    EAP_HANDLER *handler)
294 {
295         rbnode_t *node;
296
297         node = rbtree_find(inst->session_tree, handler);
298         if (!node) return NULL;
299
300         handler = rbtree_node2data(inst->session_tree, node);
301
302         RDEBUG("Finished EAP session with state "
303                "0x%02x%02x%02x%02x%02x%02x%02x%02x",
304                handler->state[0], handler->state[1],
305                handler->state[2], handler->state[3],
306                handler->state[4], handler->state[5],
307                handler->state[6], handler->state[7]);
308         /*
309          *      Delete old handler from the tree.
310          */
311         rbtree_delete(inst->session_tree, node);
312         
313         /*
314          *      And unsplice it from the linked list.
315          */
316         if (handler->prev) {
317                 handler->prev->next = handler->next;
318         } else {
319                 inst->session_head = handler->next;
320         }
321         if (handler->next) {
322                 handler->next->prev = handler->prev;
323         } else {
324                 inst->session_tail = handler->prev;
325         }
326         handler->prev = handler->next = NULL;
327
328         return handler;
329 }
330
331
332 static void eaplist_expire(rlm_eap_t *inst, REQUEST *request, time_t timestamp)
333 {
334         int i;
335         EAP_HANDLER *handler;
336
337         /*
338          *      Check the first few handlers in the list, and delete
339          *      them if they're too old.  We don't need to check them
340          *      all, as incoming requests will quickly cause older
341          *      handlers to be deleted.
342          *
343          */
344         for (i = 0; i < 3; i++) {
345                 handler = inst->session_head;
346                 if (!handler) break;
347                 
348                 RDEBUG("Expiring EAP session with state "
349                        "0x%02x%02x%02x%02x%02x%02x%02x%02x",
350                        handler->state[0], handler->state[1],
351                        handler->state[2], handler->state[3],
352                        handler->state[4], handler->state[5],
353                        handler->state[6], handler->state[7]);
354
355                 /*
356                  *      Expire entries from the start of the list.
357                  *      They should be the oldest ones.
358                  */
359                 if ((timestamp - handler->timestamp) > inst->timer_limit) {
360                         rbnode_t *node;
361                         node = rbtree_find(inst->session_tree, handler);
362                         rad_assert(node != NULL);
363                         rbtree_delete(inst->session_tree, node);
364
365                         /*
366                          *      handler == inst->session_head
367                          */
368                         inst->session_head = handler->next;
369                         if (handler->next) {
370                                 handler->next->prev = NULL;
371                         } else {
372                                 inst->session_head = NULL;
373                                 inst->session_tail = NULL;
374                         }
375                         eap_handler_free(inst, handler);
376                 }
377         }
378 }
379
380 /*
381  *      Add a handler to the set of active sessions.
382  *
383  *      Since we're adding it to the list, we guess that this means
384  *      the packet needs a State attribute.  So add one.
385  */
386 int eaplist_add(rlm_eap_t *inst, EAP_HANDLER *handler)
387 {
388         int             status = 0;
389         VALUE_PAIR      *state;
390         REQUEST         *request = handler->request;
391
392         rad_assert(handler != NULL);
393         rad_assert(request != NULL);
394
395         /*
396          *      Generate State, since we've been asked to add it to
397          *      the list.
398          */
399         state = pairmake("State", "0x00", T_OP_EQ);
400         if (!state) return 0;
401
402         /*
403          *      The time at which this request was made was the time
404          *      at which it was received by the RADIUS server.
405          */
406         handler->timestamp = request->timestamp;
407         handler->status = 1;
408
409         handler->src_ipaddr = request->packet->src_ipaddr;
410         handler->eap_id = handler->eap_ds->request->id;
411
412         /*
413          *      Playing with a data structure shared among threads
414          *      means that we need a lock, to avoid conflict.
415          */
416         PTHREAD_MUTEX_LOCK(&(inst->session_mutex));
417
418         /*
419          *      If we have a DoS attack, discard new sessions.
420          */
421         if (rbtree_num_elements(inst->session_tree) >= inst->max_sessions) {
422                 status = -1;
423                 eaplist_expire(inst, request, handler->timestamp);
424                 goto done;
425         }
426
427         /*
428          *      Create a unique content for the State variable.
429          *      It will be modified slightly per round trip, but less so
430          *      than in 1.x.
431          */
432         if (handler->trips == 0) {
433                 int i;
434
435                 for (i = 0; i < 4; i++) {
436                         uint32_t lvalue;
437
438                         lvalue = eap_rand(&inst->rand_pool);
439
440                         memcpy(handler->state + i * 4, &lvalue,
441                                sizeof(lvalue));
442                 }               
443         }
444
445         memcpy(state->vp_octets, handler->state, sizeof(handler->state));
446         state->length = EAP_STATE_LEN;
447
448         /*
449          *      Add some more data to distinguish the sessions.
450          */
451         state->vp_octets[4] = handler->trips ^ handler->state[0];
452         state->vp_octets[5] = handler->eap_id ^ handler->state[1];
453         state->vp_octets[6] = handler->eap_type ^ handler->state[2];
454
455         /*
456          *      and copy the state back again.
457          */
458         memcpy(handler->state, state->vp_octets, sizeof(handler->state));
459
460         /*
461          *      Big-time failure.
462          */
463         status = rbtree_insert(inst->session_tree, handler);
464
465         /*
466          *      Catch Access-Challenge without response.
467          */
468         if (inst->handler_tree) {
469                 check_handler_t *check = rad_malloc(sizeof(*check));
470
471                 check->inst = inst;
472                 check->handler = handler;
473                 check->trips = handler->trips;
474                 request_data_add(request, inst, 0, check, check_handler);
475         }
476
477         if (status) {
478                 EAP_HANDLER *prev;
479
480                 prev = inst->session_tail;
481                 if (prev) {
482                         prev->next = handler;
483                         handler->prev = prev;
484                         handler->next = NULL;
485                         inst->session_tail = handler;
486                 } else {
487                         inst->session_head = inst->session_tail = handler;
488                         handler->next = handler->prev = NULL;
489                 }
490         }
491
492         /*
493          *      Now that we've finished mucking with the list,
494          *      unlock it.
495          */
496  done:
497
498         /*
499          *      We don't need this any more.
500          */
501         if (status > 0) handler->request = NULL;
502
503         PTHREAD_MUTEX_UNLOCK(&(inst->session_mutex));
504
505         if (status <= 0) {
506                 pairfree(&state);
507
508                 if (status < 0) {
509                         static time_t last_logged = 0;
510
511                         if (last_logged < handler->timestamp) {
512                                 last_logged = handler->timestamp;
513                                 radlog(L_ERR, "rlm_eap (%s): Too many open "
514                                        "sessions.  Try increasing "
515                                        "\"max_sessions\" in the EAP module "
516                                        "configuration", inst->xlat_name);
517                         }                                      
518                 } else {
519                         radlog(L_ERR, "rlm_eap (%s): Internal error: "
520                                "failed to store handler", inst->xlat_name);
521                 }
522                 return 0;
523         }
524
525         RDEBUG("New EAP session, adding 'State' attribute to reply "
526                "0x%02x%02x%02x%02x%02x%02x%02x%02x",
527                state->vp_octets[0], state->vp_octets[1],
528                state->vp_octets[2], state->vp_octets[3],
529                state->vp_octets[4], state->vp_octets[5],
530                state->vp_octets[6], state->vp_octets[7]);
531                
532         pairadd(&(request->reply->vps), state);
533
534         return 1;
535 }
536
537 /*
538  *      Find a a previous EAP-Request sent by us, which matches
539  *      the current EAP-Response.
540  *
541  *      Then, release the handle from the list, and return it to
542  *      the caller.
543  *
544  *      Also since we fill the eap_ds with the present EAP-Response we
545  *      got to free the prev_eapds & move the eap_ds to prev_eapds
546  */
547 EAP_HANDLER *eaplist_find(rlm_eap_t *inst, REQUEST *request,
548                           eap_packet_t *eap_packet)
549 {
550         VALUE_PAIR      *state;
551         EAP_HANDLER     *handler, myHandler;
552
553         /*
554          *      We key the sessions off of the 'state' attribute, so it
555          *      must exist.
556          */
557         state = pairfind(request->packet->vps, PW_STATE, 0, TAG_ANY);
558         if (!state ||
559             (state->length != EAP_STATE_LEN)) {
560                 return NULL;
561         }
562
563         myHandler.src_ipaddr = request->packet->src_ipaddr;
564         myHandler.eap_id = eap_packet->id;
565         memcpy(myHandler.state, state->vp_strvalue, sizeof(myHandler.state));
566
567         /*
568          *      Playing with a data structure shared among threads
569          *      means that we need a lock, to avoid conflict.
570          */
571         PTHREAD_MUTEX_LOCK(&(inst->session_mutex));
572
573         eaplist_expire(inst, request, request->timestamp);
574
575         handler = eaplist_delete(inst, request, &myHandler);
576         PTHREAD_MUTEX_UNLOCK(&(inst->session_mutex));
577
578         /*
579          *      Might not have been there.
580          */
581         if (!handler) {
582                 radlog(L_ERR, "rlm_eap (%s): No EAP session matching state "
583                        "0x%02x%02x%02x%02x%02x%02x%02x%02x",
584                        inst->xlat_name,
585                        state->vp_octets[0], state->vp_octets[1],
586                        state->vp_octets[2], state->vp_octets[3],
587                        state->vp_octets[4], state->vp_octets[5],
588                        state->vp_octets[6], state->vp_octets[7]);
589                 return NULL;
590         }
591
592         if (handler->trips >= 50) {
593                 radlog(L_ERR, "rlm_eap (%s): Aborting! More than 50 roundtrips "
594                        "made in session with state "
595                        "0x%02x%02x%02x%02x%02x%02x%02x%02x",
596                        inst->xlat_name,
597                        state->vp_octets[0], state->vp_octets[1],
598                        state->vp_octets[2], state->vp_octets[3],
599                        state->vp_octets[4], state->vp_octets[5],
600                        state->vp_octets[6], state->vp_octets[7]);
601                        
602                 
603                 eap_handler_free(inst, handler);
604                 return NULL;
605         }
606         handler->trips++;
607
608         RDEBUG("Previous EAP request found for state "
609                "0x%02x%02x%02x%02x%02x%02x%02x%02x, released from the list",
610                 state->vp_octets[0], state->vp_octets[1],
611                 state->vp_octets[2], state->vp_octets[3],
612                 state->vp_octets[4], state->vp_octets[5],
613                 state->vp_octets[6], state->vp_octets[7]);
614
615         /*
616          *      Remember what the previous request was.
617          */
618         eap_ds_free(&(handler->prev_eapds));
619         handler->prev_eapds = handler->eap_ds;
620         handler->eap_ds = NULL;
621
622         return handler;
623 }