rlm_eap: RERROR type debugs so Module-Failure-Message gets set
[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         if (handler->identity) {
78                 talloc_free(handler->identity);
79                 handler->identity = NULL;
80         }
81
82         if (handler->prev_eapds) eap_ds_free(&(handler->prev_eapds));
83         if (handler->eap_ds) eap_ds_free(&(handler->eap_ds));
84
85         if ((handler->opaque) && (handler->free_opaque)) {
86                 handler->free_opaque(handler->opaque);
87                 handler->opaque = NULL;
88         }
89
90         handler->opaque = NULL;
91         handler->free_opaque = NULL;
92
93         if (handler->certs) fr_pair_list_free(&handler->certs);
94
95         /*
96          *      Give helpful debug messages if:
97          *
98          *      we're debugging TLS sessions, which don't finish,
99          *      and which aren't deleted early due to a likely RADIUS
100          *      retransmit which nukes our ID, and therefore our stare.
101          */
102         if (fr_debug_lvl && handler->tls && !handler->finished &&
103             (time(NULL) > (handler->timestamp + 3))) {
104                 WARN("!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!");
105                 WARN("!! EAP session with state 0x%02x%02x%02x%02x%02x%02x%02x%02x did not finish!                  !!",
106                      handler->state[0], handler->state[1],
107                      handler->state[2], handler->state[3],
108                      handler->state[4], handler->state[5],
109                      handler->state[6], handler->state[7]);
110
111                 WARN("!! Please read http://wiki.freeradius.org/guide/Certificate_Compatibility     !!");
112                 WARN("!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!");
113         }
114
115         talloc_free(handler);
116         
117         return 0;
118 }
119
120 /*
121  * Allocate a new eap_handler_t
122  */
123 eap_handler_t *eap_handler_alloc(rlm_eap_t *inst)
124 {
125         eap_handler_t   *handler;
126
127         handler = talloc_zero(NULL, eap_handler_t);
128         if (!handler) {
129                 ERROR("Failed allocating handler");
130                 return NULL;
131         }
132         handler->inst_holder = inst;
133
134         /* Doesn't need to be inside the critical region */
135         talloc_set_destructor(handler, _eap_handler_free);
136
137         return handler;
138 }
139
140
141 void eaplist_free(rlm_eap_t *inst)
142 {
143         eap_handler_t *node, *next;
144
145         for (node = inst->session_head; node != NULL; node = next) {
146                 next = node->next;
147                 talloc_free(node);
148         }
149
150         inst->session_head = inst->session_tail = NULL;
151 }
152
153 /*
154  *      Return a 32-bit random number.
155  */
156 static uint32_t eap_rand(fr_randctx *ctx)
157 {
158         uint32_t num;
159
160         num = ctx->randrsl[ctx->randcnt++];
161         if (ctx->randcnt >= 256) {
162                 ctx->randcnt = 0;
163                 fr_isaac(ctx);
164         }
165
166         return num;
167 }
168
169
170 static eap_handler_t *eaplist_delete(rlm_eap_t *inst, REQUEST *request,
171                                    eap_handler_t *handler)
172 {
173         rbnode_t *node;
174
175         node = rbtree_find(inst->session_tree, handler);
176         if (!node) return NULL;
177
178         handler = rbtree_node2data(inst->session_tree, node);
179
180         RDEBUG("Finished EAP session with state "
181                "0x%02x%02x%02x%02x%02x%02x%02x%02x",
182                handler->state[0], handler->state[1],
183                handler->state[2], handler->state[3],
184                handler->state[4], handler->state[5],
185                handler->state[6], handler->state[7]);
186         /*
187          *      Delete old handler from the tree.
188          */
189         rbtree_delete(inst->session_tree, node);
190
191         /*
192          *      And unsplice it from the linked list.
193          */
194         if (handler->prev) {
195                 handler->prev->next = handler->next;
196         } else {
197                 inst->session_head = handler->next;
198         }
199         if (handler->next) {
200                 handler->next->prev = handler->prev;
201         } else {
202                 inst->session_tail = handler->prev;
203         }
204         handler->prev = handler->next = NULL;
205
206         return handler;
207 }
208
209
210 static void eaplist_expire(rlm_eap_t *inst, REQUEST *request, time_t timestamp)
211 {
212         int i;
213         eap_handler_t *handler;
214
215         /*
216          *      Check the first few handlers in the list, and delete
217          *      them if they're too old.  We don't need to check them
218          *      all, as incoming requests will quickly cause older
219          *      handlers to be deleted.
220          *
221          */
222         for (i = 0; i < 3; i++) {
223                 handler = inst->session_head;
224                 if (!handler) break;
225
226                 RDEBUG("Expiring EAP session with state "
227                        "0x%02x%02x%02x%02x%02x%02x%02x%02x",
228                        handler->state[0], handler->state[1],
229                        handler->state[2], handler->state[3],
230                        handler->state[4], handler->state[5],
231                        handler->state[6], handler->state[7]);
232
233                 /*
234                  *      Expire entries from the start of the list.
235                  *      They should be the oldest ones.
236                  */
237                 if ((timestamp - handler->timestamp) > (int)inst->timer_limit) {
238                         rbnode_t *node;
239                         node = rbtree_find(inst->session_tree, handler);
240                         rad_assert(node != NULL);
241                         rbtree_delete(inst->session_tree, node);
242
243                         /*
244                          *      handler == inst->session_head
245                          */
246                         inst->session_head = handler->next;
247                         if (handler->next) {
248                                 handler->next->prev = NULL;
249                         } else {
250                                 inst->session_head = NULL;
251                                 inst->session_tail = NULL;
252                         }
253                         talloc_free(handler);
254                 } else {
255                         break;
256                 }
257         }
258 }
259
260 /*
261  *      Add a handler to the set of active sessions.
262  *
263  *      Since we're adding it to the list, we guess that this means
264  *      the packet needs a State attribute.  So add one.
265  */
266 int eaplist_add(rlm_eap_t *inst, eap_handler_t *handler)
267 {
268         int             status = 0;
269         VALUE_PAIR      *state;
270         REQUEST         *request = handler->request;
271
272         /*
273          *      Generate State, since we've been asked to add it to
274          *      the list.
275          */
276         state = pair_make_reply("State", NULL, T_OP_EQ);
277         if (!state) return 0;
278
279         /*
280          *      The time at which this request was made was the time
281          *      at which it was received by the RADIUS server.
282          */
283         handler->timestamp = request->timestamp;
284         handler->status = 1;
285
286         handler->src_ipaddr = request->packet->src_ipaddr;
287         handler->eap_id = handler->eap_ds->request->id;
288
289         /*
290          *      Playing with a data structure shared among threads
291          *      means that we need a lock, to avoid conflict.
292          */
293         PTHREAD_MUTEX_LOCK(&(inst->session_mutex));
294
295         /*
296          *      If we have a DoS attack, discard new sessions.
297          */
298         if (rbtree_num_elements(inst->session_tree) >= inst->max_sessions) {
299                 status = -1;
300                 eaplist_expire(inst, request, handler->timestamp);
301                 goto done;
302         }
303
304         /*
305          *      Create a unique content for the State variable.
306          *      It will be modified slightly per round trip, but less so
307          *      than in 1.x.
308          */
309         if (handler->trips == 0) {
310                 int i;
311
312                 for (i = 0; i < 4; i++) {
313                         uint32_t lvalue;
314
315                         lvalue = eap_rand(&inst->rand_pool);
316
317                         memcpy(handler->state + i * 4, &lvalue,
318                                sizeof(lvalue));
319                 }
320         }
321
322         /*
323          *      Add some more data to distinguish the sessions.
324          */
325         handler->state[4] = handler->trips ^ handler->state[0];
326         handler->state[5] = handler->eap_id ^ handler->state[1];
327         handler->state[6] = handler->type ^ handler->state[2];
328
329         fr_pair_value_memcpy(state, handler->state, sizeof(handler->state));
330
331         /*
332          *      Big-time failure.
333          */
334         status = rbtree_insert(inst->session_tree, handler);
335
336         if (status) {
337                 eap_handler_t *prev;
338
339                 prev = inst->session_tail;
340                 if (prev) {
341                         prev->next = handler;
342                         handler->prev = prev;
343                         handler->next = NULL;
344                         inst->session_tail = handler;
345                 } else {
346                         inst->session_head = inst->session_tail = handler;
347                         handler->next = handler->prev = NULL;
348                 }
349         }
350
351         /*
352          *      Now that we've finished mucking with the list,
353          *      unlock it.
354          */
355  done:
356
357         /*
358          *      We don't need this any more.
359          */
360         if (status > 0) handler->request = NULL;
361
362         PTHREAD_MUTEX_UNLOCK(&(inst->session_mutex));
363
364         if (status <= 0) {
365                 fr_pair_delete_by_num(&request->reply->vps, PW_STATE, 0, TAG_ANY);
366
367                 if (status < 0) {
368                         static time_t last_logged = 0;
369
370                         if (last_logged < handler->timestamp) {
371                                 last_logged = handler->timestamp;
372                                 ERROR("rlm_eap (%s): Too many open sessions. Try increasing \"max_sessions\" "
373                                       "in the EAP module configuration", inst->xlat_name);
374                         }
375                 } else {
376                         ERROR("rlm_eap (%s): Failed to store handler", inst->xlat_name);
377                 }
378                 return 0;
379         }
380
381         RDEBUG("EAP session adding &reply:State = 0x%02x%02x%02x%02x%02x%02x%02x%02x",
382                state->vp_octets[0], state->vp_octets[1], state->vp_octets[2], state->vp_octets[3],
383                state->vp_octets[4], state->vp_octets[5], state->vp_octets[6], state->vp_octets[7]);
384
385         return 1;
386 }
387
388 /*
389  *      Find a a previous EAP-Request sent by us, which matches
390  *      the current EAP-Response.
391  *
392  *      Then, release the handle from the list, and return it to
393  *      the caller.
394  *
395  *      Also since we fill the eap_ds with the present EAP-Response we
396  *      got to free the prev_eapds & move the eap_ds to prev_eapds
397  */
398 eap_handler_t *eaplist_find(rlm_eap_t *inst, REQUEST *request,
399                           eap_packet_raw_t *eap_packet)
400 {
401         VALUE_PAIR      *state;
402         eap_handler_t   *handler, myHandler;
403
404         /*
405          *      We key the sessions off of the 'state' attribute, so it
406          *      must exist.
407          */
408         state = fr_pair_find_by_num(request->packet->vps, PW_STATE, 0, TAG_ANY);
409         if (!state) {
410                 REDEBUG("EAP requires the State attribute to work, but no State exists in the Access-Request packet.");
411                 REDEBUG("The RADIUS client is broken.  No amount of changing FreeRADIUS will fix the RADIUS client.");
412                 return NULL;
413         }
414
415         if (state->vp_length != EAP_STATE_LEN) {
416                 REDEBUG("The RADIUS client has mangled the State attribute, OR you are forcing EAP in the wrong situation");
417                 return NULL;
418         }
419
420         myHandler.src_ipaddr = request->packet->src_ipaddr;
421         myHandler.eap_id = eap_packet->id;
422         memcpy(myHandler.state, state->vp_strvalue, sizeof(myHandler.state));
423
424         /*
425          *      Playing with a data structure shared among threads
426          *      means that we need a lock, to avoid conflict.
427          */
428         PTHREAD_MUTEX_LOCK(&(inst->session_mutex));
429
430         eaplist_expire(inst, request, request->timestamp);
431
432         handler = eaplist_delete(inst, request, &myHandler);
433         PTHREAD_MUTEX_UNLOCK(&(inst->session_mutex));
434
435         /*
436          *      Might not have been there.
437          */
438         if (!handler) {
439                 RERROR("rlm_eap (%s): No EAP session matching state "
440                        "0x%02x%02x%02x%02x%02x%02x%02x%02x",
441                        inst->xlat_name,
442                        state->vp_octets[0], state->vp_octets[1],
443                        state->vp_octets[2], state->vp_octets[3],
444                        state->vp_octets[4], state->vp_octets[5],
445                        state->vp_octets[6], state->vp_octets[7]);
446                 return NULL;
447         }
448
449         if (handler->trips >= 50) {
450                 RERROR("rlm_eap (%s): Aborting! More than 50 roundtrips "
451                        "made in session with state "
452                        "0x%02x%02x%02x%02x%02x%02x%02x%02x",
453                        inst->xlat_name,
454                        state->vp_octets[0], state->vp_octets[1],
455                        state->vp_octets[2], state->vp_octets[3],
456                        state->vp_octets[4], state->vp_octets[5],
457                        state->vp_octets[6], state->vp_octets[7]);
458
459
460                 talloc_free(handler);
461                 return NULL;
462         }
463         handler->trips++;
464
465         RDEBUG("Previous EAP request found for state "
466                "0x%02x%02x%02x%02x%02x%02x%02x%02x, released from the list",
467                 state->vp_octets[0], state->vp_octets[1],
468                 state->vp_octets[2], state->vp_octets[3],
469                 state->vp_octets[4], state->vp_octets[5],
470                 state->vp_octets[6], state->vp_octets[7]);
471
472         /*
473          *      Remember what the previous request was.
474          */
475         eap_ds_free(&(handler->prev_eapds));
476         handler->prev_eapds = handler->eap_ds;
477         handler->eap_ds = NULL;
478
479         return handler;
480 }