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