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