These absolutely don't need to be at the default debug log level
[freeradius.git] / src / main / state.c
1 /*
2  *   This program is free software; you can redistribute it and/or modify
3  *   it under the terms of the GNU General Public License as published by
4  *   the Free Software Foundation; either version 2 of the License, or
5  *   (at your option) any later version.
6  *
7  *   This program is distributed in the hope that it will be useful,
8  *   but WITHOUT ANY WARRANTY; without even the implied warranty of
9  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
10  *   GNU General Public License for more details.
11  *
12  *   You should have received a copy of the GNU General Public License
13  *   along with this program; if not, write to the Free Software
14  *   Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
15  */
16
17 /**
18  * $Id$
19  *
20  * @brief Multi-packet state handling
21  * @file main/state.c
22  *
23  * @ingroup AVP
24  *
25  * @copyright 2014 The FreeRADIUS server project
26  */
27 RCSID("$Id$")
28
29 #include <freeradius-devel/radiusd.h>
30 #include <freeradius-devel/state.h>
31 #include <freeradius-devel/rad_assert.h>
32
33 static rbtree_t *state_tree;
34
35 #ifdef HAVE_PTHREAD_H
36 static pthread_mutex_t state_mutex;
37
38 #define PTHREAD_MUTEX_LOCK pthread_mutex_lock
39 #define PTHREAD_MUTEX_UNLOCK pthread_mutex_unlock
40
41 #else
42 /*
43  *      This is easier than ifdef's throughout the code.
44  */
45 #define PTHREAD_MUTEX_LOCK(_x)
46 #define PTHREAD_MUTEX_UNLOCK(_x)
47
48 #endif
49
50 typedef struct state_entry_t {
51         uint8_t         state[AUTH_VECTOR_LEN];
52
53         time_t          cleanup;
54         struct state_entry_t *prev;
55         struct state_entry_t *next;
56
57         int             tries;
58
59         VALUE_PAIR      *vps;
60
61         void            *opaque;
62         void            (*free_opaque)(void *opaque);
63 } state_entry_t;
64
65 static state_entry_t *state_head = NULL;
66 static state_entry_t *state_tail = NULL;
67
68 /*
69  *      rbtree callback.
70  */
71 static int state_entry_cmp(void const *one, void const *two)
72 {
73         state_entry_t const *a = one;
74         state_entry_t const *b = two;
75
76         return memcmp(a->state, b->state, sizeof(a->state));
77 }
78
79 /*
80  *      When an entry is free'd, it's removed from the linked list of
81  *      cleanup times.
82  *
83  *      Note that
84  */
85 static void state_entry_free(state_entry_t *entry)
86 {
87         state_entry_t *prev, *next;
88
89         /*
90          *      If we're deleting the whole tree, don't bother doing
91          *      all of the fixups.
92          */
93         if (!state_tree) return;
94
95         prev = entry->prev;
96         next = entry->next;
97
98         if (prev) {
99                 rad_assert(state_tail != entry);
100                 prev->next = next;
101         } else if (state_head) {
102                 rad_assert(state_head == entry);
103                 state_head = next;
104         }
105
106         if (next) {
107                 rad_assert(state_tail != entry);
108                 next->prev = prev;
109         } else if (state_tail) {
110                 rad_assert(state_tail == entry);
111                 state_tail = prev;
112         }
113
114         if (entry->opaque) {
115                 entry->free_opaque(entry->opaque);
116         }
117
118 #ifdef WITH_VERIFY_PTR
119         (void) talloc_get_type_abort(entry, state_entry_t);
120 #endif
121         rbtree_deletebydata(state_tree, entry);
122         talloc_free(entry);
123 }
124
125 bool fr_state_init(void)
126 {
127 #ifdef HAVE_PTHREAD_H
128         if (pthread_mutex_init(&state_mutex, NULL) != 0) {
129                 return false;
130         }
131 #endif
132
133         state_tree = rbtree_create(NULL, state_entry_cmp, NULL, 0);
134         if (!state_tree) {
135                 return false;
136         }
137
138         return true;
139 }
140
141 void fr_state_delete(void)
142 {
143         rbtree_t *my_tree;
144
145         PTHREAD_MUTEX_LOCK(&state_mutex);
146
147         /*
148          *      Tell the talloc callback to NOT delete the entry from
149          *      the tree.  We're deleting the entire tree.
150          */
151         my_tree = state_tree;
152         state_tree = NULL;
153
154         rbtree_free(my_tree);
155         PTHREAD_MUTEX_UNLOCK(&state_mutex);
156 }
157
158 /*
159  *      Create a new entry.  Called with the mutex held.
160  */
161 static state_entry_t *fr_state_create(RADIUS_PACKET *packet, state_entry_t *old)
162 {
163         size_t i;
164         uint32_t x;
165         time_t now = time(NULL);
166         VALUE_PAIR *vp;
167         state_entry_t *entry, *next;
168
169         /*
170          *      Clean up old entries.
171          */
172         for (entry = state_head; entry != NULL; entry = next) {
173                 next = entry->next;
174
175                 if (entry == old) continue;
176
177                 /*
178                  *      Too old, we can delete it.
179                  */
180                 if (entry->cleanup < now) {
181                         state_entry_free(entry);
182                         continue;
183                 }
184
185                 /*
186                  *      Unused.  We can delete it, even if now isn't
187                  *      the time to clean it up.
188                  */
189                 if (!entry->vps && !entry->opaque) {
190                         state_entry_free(entry);
191                         continue;
192                 }
193
194                 break;
195         }
196
197         /*
198          *      Limit the size of the cache based on how many requests
199          *      we can handle at the same time.
200          */
201         if (rbtree_num_elements(state_tree) >= main_config.max_requests * 2) {
202                 return NULL;
203         }
204
205         /*
206          *      Allocate a new one.
207          */
208         entry = talloc_zero(state_tree, state_entry_t);
209         if (!entry) return NULL;
210
211         /*
212          *      Limit the lifetime of this entry based on how long the
213          *      server takes to process a request.  Doing it this way
214          *      isn't perfect, but it's reasonable, and it's one less
215          *      thing for an administrator to configure.
216          */
217         entry->cleanup = now + main_config.max_request_time * 10;
218
219         /*
220          *      Hacks for EAP, until we convert EAP to using the state API.
221          *
222          *      The EAP module creates it's own State attribute, so we
223          *      want to use that one in preference to one we create.
224          */
225         vp = pairfind(packet->vps, PW_STATE, 0, TAG_ANY);
226
227         /*
228          *      If possible, base the new one off of the old one.
229          */
230         if (old) {
231                 entry->tries = old->tries + 1;
232
233                 rad_assert(old->vps == NULL);
234
235                 /*
236                  *      Track State
237                  */
238                 if (!vp) {
239                         memcpy(entry->state, old->state, sizeof(entry->state));
240
241                         entry->state[1] = entry->state[0] ^ entry->tries;
242                         entry->state[3] = entry->state[2] ^ (RADIUSD_VERSION / 10000);
243                 }
244
245                 /*
246                  *      The old one isn't used any more, so we can free it.
247                  */
248                 if (!old->opaque) state_entry_free(old);
249
250         } else if (!vp) {
251                 /*
252                  *      16 octets of randomness should be enough to
253                  *      have a globally unique state.
254                  */
255                 for (i = 0; i < sizeof(entry->state) / sizeof(x); i++) {
256                         x = fr_rand();
257                         memcpy(entry->state + (i * 4), &x, sizeof(x));
258                 }
259         }
260
261         /*
262          *      If EAP created a State, use that.  Otherwise, use the
263          *      one we created above.
264          */
265         if (vp) {
266                 rad_assert(vp->length == sizeof(entry->state));
267                 memcpy(entry->state, vp->vp_octets, sizeof(entry->state));
268
269         } else {
270                 vp = paircreate(packet, PW_STATE, 0);
271                 pairmemcpy(vp, entry->state, sizeof(entry->state));
272                 pairadd(&packet->vps, vp);
273         }
274
275         if (!rbtree_insert(state_tree, entry)) {
276                 talloc_free(entry);
277                 return NULL;
278         }
279
280         /*
281          *      Link it to the end of the list, which is implicitely
282          *      ordered by cleanup time.
283          */
284         if (!state_head) {
285                 entry->prev = entry->next = NULL;
286                 state_head = state_tail = entry;
287         } else {
288                 rad_assert(state_tail != NULL);
289
290                 entry->prev = state_tail;
291                 state_tail->next = entry;
292
293                 entry->next = NULL;
294                 state_tail = entry;
295         }
296
297         return entry;
298 }
299
300
301 /*
302  *      Find the entry, based on the State attribute.
303  */
304 static state_entry_t *fr_state_find(RADIUS_PACKET *packet)
305 {
306         VALUE_PAIR *vp;
307         state_entry_t *entry, my_entry;
308
309         vp = pairfind(packet->vps, PW_STATE, 0, TAG_ANY);
310         if (!vp) return NULL;
311
312         if (vp->length != sizeof(my_entry.state)) return NULL;
313
314         memcpy(my_entry.state, vp->vp_octets, sizeof(my_entry.state));
315
316         entry = rbtree_finddata(state_tree, &my_entry);
317
318 #ifdef WITH_VERIFY_PTR
319         if (entry)  (void) talloc_get_type_abort(entry, state_entry_t);
320 #endif
321
322         return entry;
323 }
324
325 /*
326  *      Called when sending Access-Reject, so that all State is
327  *      discarded.
328  */
329 void fr_state_discard(REQUEST *request, RADIUS_PACKET *original)
330 {
331         state_entry_t *entry;
332
333         pairfree(&request->state);
334         request->state = NULL;
335
336         PTHREAD_MUTEX_LOCK(&state_mutex);
337         entry = fr_state_find(original);
338         if (!entry) {
339                 PTHREAD_MUTEX_UNLOCK(&state_mutex);
340                 return;
341         }
342
343         state_entry_free(entry);
344         PTHREAD_MUTEX_UNLOCK(&state_mutex);
345         return;
346 }
347
348 /*
349  *      Get the VPS from the state.
350  */
351 void fr_state_get_vps(REQUEST *request, RADIUS_PACKET *packet)
352 {
353         state_entry_t *entry;
354
355         rad_assert(request->state == NULL);
356
357         /*
358          *      No State, don't do anything.
359          */
360         if (!pairfind(request->packet->vps, PW_STATE, 0, TAG_ANY)) {
361                 RDEBUG3("session-state: No State attribute");
362                 return;
363         }
364
365         PTHREAD_MUTEX_LOCK(&state_mutex);
366         entry = fr_state_find(packet);
367
368         /*
369          *      This has to be done in a mutex lock, because talloc
370          *      isn't thread-safe.
371          */
372         if (entry) {
373                 pairfilter(request, &request->state, &entry->vps, 0, 0, TAG_ANY);
374                 RDEBUG2("session-state: Found cached attributes");
375                 rdebug_pair_list(L_DBG_LVL_1, request, request->state, NULL);
376
377         } else {
378                 RDEBUG2("session-state: No cached attributes");
379         }
380
381         PTHREAD_MUTEX_UNLOCK(&state_mutex);
382
383         VERIFY_REQUEST(request);
384         return;
385 }
386
387
388 /*
389  *      Put request->state into the State attribute.  Put the State
390  *      attribute into the vps list.  Delete the original entry, if it
391  *      exists.
392  */
393 bool fr_state_put_vps(REQUEST *request, RADIUS_PACKET *original, RADIUS_PACKET *packet)
394 {
395         state_entry_t *entry, *old;
396
397         if (!request->state) {
398                 RDEBUG3("session-state: Nothing to cache");
399                 return true;
400         }
401
402         RDEBUG2("session-state: Saving cached attributes");
403         rdebug_pair_list(L_DBG_LVL_1, request, request->state, NULL);
404
405         PTHREAD_MUTEX_LOCK(&state_mutex);
406
407         if (original) {
408                 old = fr_state_find(original);
409         } else {
410                 old = NULL;
411         }
412
413         entry = fr_state_create(packet, old);
414         if (!entry) {
415                 PTHREAD_MUTEX_UNLOCK(&state_mutex);
416                 return false;
417         }
418
419         /*
420          *      This has to be done in a mutex lock, because talloc
421          *      isn't thread-safe.
422          */
423         pairfilter(entry, &entry->vps, &request->state, 0, 0, TAG_ANY);
424         PTHREAD_MUTEX_UNLOCK(&state_mutex);
425
426         rad_assert(request->state == NULL);
427         VERIFY_REQUEST(request);
428         return true;
429 }
430
431 /*
432  *      Find the opaque data associated with a State attribute.
433  *      Leave the data in the entry.
434  */
435 void *fr_state_find_data(UNUSED REQUEST *request, RADIUS_PACKET *packet)
436 {
437         void *data;
438         state_entry_t *entry;
439
440         PTHREAD_MUTEX_LOCK(&state_mutex);
441         entry = fr_state_find(packet);
442         if (!entry) {
443                 PTHREAD_MUTEX_UNLOCK(&state_mutex);
444                 return NULL;
445         }
446
447         data = entry->opaque;
448         PTHREAD_MUTEX_UNLOCK(&state_mutex);
449
450         return data;
451 }
452
453
454 /*
455  *      Get the opaque data associated with a State attribute.
456  *      and remove the data from the entry.
457  */
458 void *fr_state_get_data(UNUSED REQUEST *request, RADIUS_PACKET *packet)
459 {
460         void *data;
461         state_entry_t *entry;
462
463         PTHREAD_MUTEX_LOCK(&state_mutex);
464         entry = fr_state_find(packet);
465         if (!entry) {
466                 PTHREAD_MUTEX_UNLOCK(&state_mutex);
467                 return NULL;
468         }
469
470         data = entry->opaque;
471         entry->opaque = NULL;
472         PTHREAD_MUTEX_UNLOCK(&state_mutex);
473
474         return data;
475 }
476
477
478 /*
479  *      Get the opaque data associated with a State attribute.
480  *      and remove the data from the entry.
481  */
482 bool fr_state_put_data(UNUSED REQUEST *request, RADIUS_PACKET *original, RADIUS_PACKET *packet,
483                        void *data, void (*free_data)(void *))
484 {
485         state_entry_t *entry, *old;
486
487         PTHREAD_MUTEX_LOCK(&state_mutex);
488
489         if (original) {
490                 old = fr_state_find(original);
491         } else {
492                 old = NULL;
493         }
494
495         entry = fr_state_create(packet, old);
496         if (!entry) {
497                 PTHREAD_MUTEX_UNLOCK(&state_mutex);
498                 return false;
499         }
500
501         /*
502          *      If we're moving the data, ensure that we delete it
503          *      from the old state.
504          */
505         if (old && (old->opaque == data)) {
506                 old->opaque = NULL;
507         }
508
509         entry->opaque = data;
510         entry->free_opaque = free_data;
511
512         PTHREAD_MUTEX_UNLOCK(&state_mutex);
513         return true;
514 }