Annotation of embedaddon/strongswan/src/libcharon/plugins/eap_radius/eap_radius_provider.c, revision 1.1.1.1
1.1 misho 1: /*
2: * Copyright (C) 2018 Tobias Brunner
3: * HSR Hochschule fuer Technik Rapperswil
4: *
5: * Copyright (C) 2013 Martin Willi
6: * Copyright (C) 2013 revosec AG
7: *
8: * This program is free software; you can redistribute it and/or modify it
9: * under the terms of the GNU General Public License as published by the
10: * Free Software Foundation; either version 2 of the License, or (at your
11: * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
12: *
13: * This program is distributed in the hope that it will be useful, but
14: * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
15: * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
16: * for more details.
17: */
18:
19: #include "eap_radius_provider.h"
20:
21: #include <daemon.h>
22: #include <collections/hashtable.h>
23: #include <threading/mutex.h>
24:
25: typedef struct private_eap_radius_provider_t private_eap_radius_provider_t;
26: typedef struct private_listener_t private_listener_t;
27:
28: /**
29: * Private data of registered listener
30: */
31: struct private_listener_t {
32:
33: /**
34: * Implements listener_t interface
35: */
36: listener_t public;
37:
38: /**
39: * Leases not acquired yet, identification_t => entry_t
40: */
41: hashtable_t *unclaimed;
42:
43: /**
44: * Leases acquired, identification_t => entry_t
45: */
46: hashtable_t *claimed;
47:
48: /**
49: * Mutex to lock leases
50: */
51: mutex_t *mutex;
52: };
53:
54: /**
55: * Private data of an eap_radius_provider_t object.
56: */
57: struct private_eap_radius_provider_t {
58:
59: /**
60: * Public eap_radius_provider_t interface.
61: */
62: eap_radius_provider_t public;
63:
64: /**
65: * Additionally implements the listener_t interface
66: */
67: private_listener_t listener;
68: };
69:
70: /**
71: * Singleton instance of provider
72: */
73: static eap_radius_provider_t *singleton = NULL;
74:
75: /**
76: * Configuration attribute in an entry
77: */
78: typedef struct {
79: /** type of attribute */
80: configuration_attribute_type_t type;
81: /** attribute data */
82: chunk_t data;
83: } attr_t;
84:
85: /**
86: * Destroy an attr_t
87: */
88: static void destroy_attr(attr_t *this)
89: {
90: free(this->data.ptr);
91: free(this);
92: }
93:
94: /**
95: * Hashtable entry with leases and attributes
96: */
97: typedef struct {
98: /** IKE_SA unique id we assign the IP lease */
99: uintptr_t id;
100: /** list of IP leases received from AAA, as host_t */
101: linked_list_t *addrs;
102: /** list of configuration attributes, as attr_t */
103: linked_list_t *attrs;
104: } entry_t;
105:
106: /**
107: * destroy an entry_t
108: */
109: static void destroy_entry(entry_t *this)
110: {
111: this->addrs->destroy_offset(this->addrs, offsetof(host_t, destroy));
112: this->attrs->destroy_function(this->attrs, (void*)destroy_attr);
113: free(this);
114: }
115:
116: /**
117: * Get or create an entry from a locked hashtable
118: */
119: static entry_t* get_or_create_entry(hashtable_t *hashtable, uintptr_t id)
120: {
121: entry_t *entry;
122:
123: entry = hashtable->get(hashtable, (void*)id);
124: if (!entry)
125: {
126: INIT(entry,
127: .id = id,
128: .addrs = linked_list_create(),
129: .attrs = linked_list_create(),
130: );
131: hashtable->put(hashtable, (void*)id, entry);
132: }
133: return entry;
134: }
135:
136: /**
137: * Put an entry to hashtable, or destroy it if empty
138: */
139: static void put_or_destroy_entry(hashtable_t *hashtable, entry_t *entry)
140: {
141: if (entry->addrs->get_count(entry->addrs) > 0 ||
142: entry->attrs->get_count(entry->attrs) > 0)
143: {
144: hashtable->put(hashtable, (void*)entry->id, entry);
145: }
146: else
147: {
148: destroy_entry(entry);
149: }
150: }
151:
152: /**
153: * Hashtable hash function
154: */
155: static u_int hash(uintptr_t id)
156: {
157: return id;
158: }
159:
160: /**
161: * Hashtable equals function
162: */
163: static bool equals(uintptr_t a, uintptr_t b)
164: {
165: return a == b;
166: }
167:
168: /**
169: * Insert an address entry to a locked claimed/unclaimed hashtable
170: */
171: static void add_addr(private_eap_radius_provider_t *this,
172: hashtable_t *hashtable, uintptr_t id, host_t *host)
173: {
174: entry_t *entry;
175:
176: entry = get_or_create_entry(hashtable, id);
177: entry->addrs->insert_last(entry->addrs, host);
178: }
179:
180: /**
181: * Remove the next address from the locked hashtable stored for given id
182: */
183: static host_t* remove_addr(private_eap_radius_provider_t *this,
184: hashtable_t *hashtable, uintptr_t id, host_t *addr)
185: {
186: enumerator_t *enumerator;
187: entry_t *entry;
188: host_t *found = NULL, *current;
189:
190: entry = hashtable->remove(hashtable, (void*)id);
191: if (entry)
192: {
193: enumerator = entry->addrs->create_enumerator(entry->addrs);
194: while (enumerator->enumerate(enumerator, ¤t))
195: {
196: if (addr->ip_equals(addr, current))
197: { /* prefer an exact match */
198: entry->addrs->remove_at(entry->addrs, enumerator);
199: enumerator->destroy(enumerator);
200: put_or_destroy_entry(hashtable, entry);
201: return current;
202: }
203: if (!found && addr->get_family(addr) == current->get_family(current))
204: { /* fallback to the first IP with a matching address family */
205: found = current;
206: }
207: }
208: enumerator->destroy(enumerator);
209: if (found)
210: {
211: entry->addrs->remove(entry->addrs, found, NULL);
212: }
213: put_or_destroy_entry(hashtable, entry);
214: }
215: return found;
216: }
217:
218: /**
219: * Insert an attribute entry to a locked claimed/unclaimed hashtable
220: */
221: static void add_attr(private_eap_radius_provider_t *this,
222: hashtable_t *hashtable, uintptr_t id, attr_t *attr)
223: {
224: entry_t *entry;
225:
226: entry = get_or_create_entry(hashtable, id);
227: entry->attrs->insert_last(entry->attrs, attr);
228: }
229:
230: /**
231: * Remove the next attribute from the locked hashtable stored for given id
232: */
233: static attr_t* remove_attr(private_eap_radius_provider_t *this,
234: hashtable_t *hashtable, uintptr_t id)
235: {
236: entry_t *entry;
237: attr_t *attr = NULL;
238:
239: entry = hashtable->remove(hashtable, (void*)id);
240: if (entry)
241: {
242: entry->attrs->remove_first(entry->attrs, (void**)&attr);
243: put_or_destroy_entry(hashtable, entry);
244: }
245: return attr;
246: }
247:
248: /**
249: * Clean up unclaimed leases assigned for an IKE_SA
250: */
251: static void release_unclaimed(private_listener_t *this, ike_sa_t *ike_sa)
252: {
253: uintptr_t id;
254: entry_t *entry;
255:
256: id = ike_sa->get_unique_id(ike_sa);
257: this->mutex->lock(this->mutex);
258: entry = this->unclaimed->remove(this->unclaimed, (void*)id);
259: this->mutex->unlock(this->mutex);
260: if (entry)
261: {
262: destroy_entry(entry);
263: }
264: }
265:
266: METHOD(listener_t, message_hook, bool,
267: private_listener_t *this, ike_sa_t *ike_sa,
268: message_t *message, bool incoming, bool plain)
269: {
270: if (plain && ike_sa->get_state(ike_sa) == IKE_ESTABLISHED &&
271: !incoming && !message->get_request(message))
272: {
273: if ((ike_sa->get_version(ike_sa) == IKEV1 &&
274: message->get_exchange_type(message) == TRANSACTION) ||
275: (ike_sa->get_version(ike_sa) == IKEV2 &&
276: message->get_exchange_type(message) == IKE_AUTH))
277: {
278: /* if the addresses have not been claimed yet, they won't. Release
279: * these resources. */
280: release_unclaimed(this, ike_sa);
281: }
282: }
283: return TRUE;
284: }
285:
286: METHOD(listener_t, ike_updown, bool,
287: private_listener_t *this, ike_sa_t *ike_sa, bool up)
288: {
289: if (!up)
290: {
291: /* if the message hook does not apply because of a failed exchange
292: * or something, make sure we release any resources now */
293: release_unclaimed(this, ike_sa);
294: }
295: return TRUE;
296: }
297:
298: /**
299: * Migrate an entry in hashtable from old to new id
300: */
301: static void migrate_entry(hashtable_t *table, uintptr_t old, uintptr_t new)
302: {
303: entry_t *entry;
304:
305: entry = table->remove(table, (void*)old);
306: if (entry)
307: {
308: entry->id = new;
309: entry = table->put(table, (void*)new, entry);
310: if (entry)
311: { /* shouldn't happen */
312: destroy_entry(entry);
313: }
314: }
315: }
316:
317: METHOD(listener_t, ike_rekey, bool,
318: private_listener_t *this, ike_sa_t *old, ike_sa_t *new)
319: {
320: uintptr_t old_id, new_id;
321:
322: old_id = old->get_unique_id(old);
323: new_id = new->get_unique_id(new);
324:
325: this->mutex->lock(this->mutex);
326:
327: migrate_entry(this->unclaimed, old_id, new_id);
328: migrate_entry(this->claimed, old_id, new_id);
329:
330: this->mutex->unlock(this->mutex);
331:
332: return TRUE;
333: }
334:
335: METHOD(attribute_provider_t, acquire_address, host_t*,
336: private_eap_radius_provider_t *this, linked_list_t *pools,
337: ike_sa_t *ike_sa, host_t *requested)
338: {
339: enumerator_t *enumerator;
340: host_t *addr = NULL;
341: uintptr_t sa;
342: char *name;
343:
344: sa = ike_sa->get_unique_id(ike_sa);
345:
346: enumerator = pools->create_enumerator(pools);
347: while (enumerator->enumerate(enumerator, &name))
348: {
349: if (streq(name, "radius"))
350: {
351: this->listener.mutex->lock(this->listener.mutex);
352: addr = remove_addr(this, this->listener.unclaimed, sa, requested);
353: if (addr)
354: {
355: add_addr(this, this->listener.claimed, sa, addr->clone(addr));
356: }
357: this->listener.mutex->unlock(this->listener.mutex);
358: break;
359: }
360: }
361: enumerator->destroy(enumerator);
362:
363: return addr;
364: }
365:
366: METHOD(attribute_provider_t, release_address, bool,
367: private_eap_radius_provider_t *this, linked_list_t *pools, host_t *address,
368: ike_sa_t *ike_sa)
369: {
370: enumerator_t *enumerator;
371: host_t *found = NULL;
372: uintptr_t sa;
373: char *name;
374:
375: sa = ike_sa->get_unique_id(ike_sa);
376:
377: enumerator = pools->create_enumerator(pools);
378: while (enumerator->enumerate(enumerator, &name))
379: {
380: if (streq(name, "radius"))
381: {
382: this->listener.mutex->lock(this->listener.mutex);
383: found = remove_addr(this, this->listener.claimed, sa, address);
384: this->listener.mutex->unlock(this->listener.mutex);
385: break;
386: }
387: }
388: enumerator->destroy(enumerator);
389:
390: if (found)
391: {
392: found->destroy(found);
393: return TRUE;
394: }
395: return FALSE;
396: }
397:
398: /**
399: * Enumerator implementation over attributes
400: */
401: typedef struct {
402: /** implements enumerator_t */
403: enumerator_t public;
404: /** list of attributes to enumerate */
405: linked_list_t *list;
406: /** currently enumerating attribute */
407: attr_t *current;
408: } attribute_enumerator_t;
409:
410: METHOD(enumerator_t, attribute_enumerate, bool,
411: attribute_enumerator_t *this, va_list args)
412: {
413: configuration_attribute_type_t *type;
414: chunk_t *data;
415:
416: VA_ARGS_VGET(args, type, data);
417: if (this->current)
418: {
419: destroy_attr(this->current);
420: this->current = NULL;
421: }
422: if (this->list->remove_first(this->list, (void**)&this->current) == SUCCESS)
423: {
424: *type = this->current->type;
425: *data = this->current->data;
426: return TRUE;
427: }
428: return FALSE;
429: }
430:
431: METHOD(enumerator_t, attribute_destroy, void,
432: attribute_enumerator_t *this)
433: {
434: if (this->current)
435: {
436: destroy_attr(this->current);
437: }
438: this->list->destroy_function(this->list, (void*)destroy_attr);
439: free(this);
440: }
441:
442: METHOD(attribute_provider_t, create_attribute_enumerator, enumerator_t*,
443: private_eap_radius_provider_t *this, linked_list_t *pools,
444: ike_sa_t *ike_sa, linked_list_t *vips)
445: {
446: attribute_enumerator_t *enumerator;
447: attr_t *attr;
448: uintptr_t sa;
449:
450: sa = ike_sa->get_unique_id(ike_sa);
451:
452: INIT(enumerator,
453: .public = {
454: .enumerate = enumerator_enumerate_default,
455: .venumerate = _attribute_enumerate,
456: .destroy = _attribute_destroy,
457: },
458: .list = linked_list_create(),
459: );
460:
461: /* we forward attributes regardless of pool configurations */
462: this->listener.mutex->lock(this->listener.mutex);
463: while (TRUE)
464: {
465: attr = remove_attr(this, this->listener.unclaimed, sa);
466: if (!attr)
467: {
468: break;
469: }
470: enumerator->list->insert_last(enumerator->list, attr);
471: }
472: this->listener.mutex->unlock(this->listener.mutex);
473:
474: return &enumerator->public;
475: }
476:
477: METHOD(eap_radius_provider_t, add_framed_ip, void,
478: private_eap_radius_provider_t *this, uint32_t id, host_t *ip)
479: {
480: this->listener.mutex->lock(this->listener.mutex);
481: add_addr(this, this->listener.unclaimed, id, ip);
482: this->listener.mutex->unlock(this->listener.mutex);
483: }
484:
485: METHOD(eap_radius_provider_t, add_attribute, void,
486: private_eap_radius_provider_t *this, uint32_t id,
487: configuration_attribute_type_t type, chunk_t data)
488: {
489: attr_t *attr;
490:
491: INIT(attr,
492: .type = type,
493: .data = chunk_clone(data),
494: );
495: this->listener.mutex->lock(this->listener.mutex);
496: add_attr(this, this->listener.unclaimed, id, attr);
497: this->listener.mutex->unlock(this->listener.mutex);
498: }
499:
500: METHOD(eap_radius_provider_t, clear_unclaimed, enumerator_t*,
501: private_eap_radius_provider_t *this, uint32_t id)
502: {
503: entry_t *entry;
504:
505: this->listener.mutex->lock(this->listener.mutex);
506: entry = this->listener.unclaimed->remove(this->listener.unclaimed,
507: (void*)(uintptr_t)id);
508: this->listener.mutex->unlock(this->listener.mutex);
509: if (!entry)
510: {
511: return enumerator_create_empty();
512: }
513: return enumerator_create_cleaner(
514: entry->addrs->create_enumerator(entry->addrs),
515: (void*)destroy_entry, entry);
516: }
517:
518: METHOD(eap_radius_provider_t, destroy, void,
519: private_eap_radius_provider_t *this)
520: {
521: singleton = NULL;
522: charon->bus->remove_listener(charon->bus, &this->listener.public);
523: this->listener.mutex->destroy(this->listener.mutex);
524: this->listener.claimed->destroy(this->listener.claimed);
525: this->listener.unclaimed->destroy(this->listener.unclaimed);
526: free(this);
527: }
528:
529: /**
530: * See header
531: */
532: eap_radius_provider_t *eap_radius_provider_create()
533: {
534: if (!singleton)
535: {
536: private_eap_radius_provider_t *this;
537:
538: INIT(this,
539: .public = {
540: .provider = {
541: .acquire_address = _acquire_address,
542: .release_address = _release_address,
543: .create_attribute_enumerator = _create_attribute_enumerator,
544: },
545: .add_framed_ip = _add_framed_ip,
546: .add_attribute = _add_attribute,
547: .clear_unclaimed = _clear_unclaimed,
548: .destroy = _destroy,
549: },
550: .listener = {
551: .public = {
552: .ike_updown = _ike_updown,
553: .ike_rekey = _ike_rekey,
554: .message = _message_hook,
555: },
556: .claimed = hashtable_create((hashtable_hash_t)hash,
557: (hashtable_equals_t)equals, 32),
558: .unclaimed = hashtable_create((hashtable_hash_t)hash,
559: (hashtable_equals_t)equals, 32),
560: .mutex = mutex_create(MUTEX_TYPE_DEFAULT),
561: },
562: );
563:
564: if (lib->settings->get_bool(lib->settings,
565: "%s.plugins.eap-radius.accounting", FALSE, lib->ns))
566: {
567: /* if RADIUS accounting is enabled, keep unclaimed IPs around until
568: * the Accounting-Stop message is sent */
569: this->listener.public.message = NULL;
570: }
571:
572: charon->bus->add_listener(charon->bus, &this->listener.public);
573:
574: singleton = &this->public;
575: }
576: return singleton;
577: }
578:
579: /**
580: * See header
581: */
582: eap_radius_provider_t *eap_radius_provider_get()
583: {
584: return singleton;
585: }
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>